Merge tag 'uninit-macro-v5.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6-microblaze.git] / fs / fuse / file.c
index 32301fe..6611ef3 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/swap.h>
 #include <linux/falloc.h>
 #include <linux/uio.h>
+#include <linux/fs.h>
 
 static struct page **fuse_pages_alloc(unsigned int npages, gfp_t flags,
                                      struct fuse_page_desc **desc)
@@ -1586,7 +1587,6 @@ static void fuse_writepage_finish(struct fuse_conn *fc,
        struct backing_dev_info *bdi = inode_to_bdi(inode);
        int i;
 
-       rb_erase(&wpa->writepages_entry, &fi->writepages);
        for (i = 0; i < ap->num_pages; i++) {
                dec_wb_stat(&bdi->wb, WB_WRITEBACK);
                dec_node_page_state(ap->pages[i], NR_WRITEBACK_TEMP);
@@ -1637,6 +1637,7 @@ __acquires(fi->lock)
 
  out_free:
        fi->writectr--;
+       rb_erase(&wpa->writepages_entry, &fi->writepages);
        fuse_writepage_finish(fc, wpa);
        spin_unlock(&fi->lock);
 
@@ -1674,7 +1675,8 @@ __acquires(fi->lock)
        }
 }
 
-static void tree_insert(struct rb_root *root, struct fuse_writepage_args *wpa)
+static struct fuse_writepage_args *fuse_insert_writeback(struct rb_root *root,
+                                               struct fuse_writepage_args *wpa)
 {
        pgoff_t idx_from = wpa->ia.write.in.offset >> PAGE_SHIFT;
        pgoff_t idx_to = idx_from + wpa->ia.ap.num_pages - 1;
@@ -1697,11 +1699,17 @@ static void tree_insert(struct rb_root *root, struct fuse_writepage_args *wpa)
                else if (idx_to < curr_index)
                        p = &(*p)->rb_left;
                else
-                       return (void) WARN_ON(true);
+                       return curr;
        }
 
        rb_link_node(&wpa->writepages_entry, parent, p);
        rb_insert_color(&wpa->writepages_entry, root);
+       return NULL;
+}
+
+static void tree_insert(struct rb_root *root, struct fuse_writepage_args *wpa)
+{
+       WARN_ON(fuse_insert_writeback(root, wpa));
 }
 
 static void fuse_writepage_end(struct fuse_conn *fc, struct fuse_args *args,
@@ -1714,6 +1722,7 @@ static void fuse_writepage_end(struct fuse_conn *fc, struct fuse_args *args,
 
        mapping_set_error(inode->i_mapping, error);
        spin_lock(&fi->lock);
+       rb_erase(&wpa->writepages_entry, &fi->writepages);
        while (wpa->next) {
                struct fuse_conn *fc = get_fuse_conn(inode);
                struct fuse_write_in *inarg = &wpa->ia.write.in;
@@ -1952,14 +1961,14 @@ static void fuse_writepages_send(struct fuse_fill_wb_data *data)
 }
 
 /*
- * First recheck under fi->lock if the offending offset is still under
- * writeback.  If yes, then iterate auxiliary write requests, to see if there's
+ * Check under fi->lock if the page is under writeback, and insert it onto the
+ * rb_tree if not. Otherwise iterate auxiliary write requests, to see if there's
  * one already added for a page at this offset.  If there's none, then insert
  * this new request onto the auxiliary list, otherwise reuse the existing one by
- * copying the new page contents over to the old temporary page.
+ * swapping the new temp page with the old one.
  */
-static bool fuse_writepage_in_flight(struct fuse_writepage_args *new_wpa,
-                                    struct page *page)
+static bool fuse_writepage_add(struct fuse_writepage_args *new_wpa,
+                              struct page *page)
 {
        struct fuse_inode *fi = get_fuse_inode(new_wpa->inode);
        struct fuse_writepage_args *tmp;
@@ -1967,17 +1976,15 @@ static bool fuse_writepage_in_flight(struct fuse_writepage_args *new_wpa,
        struct fuse_args_pages *new_ap = &new_wpa->ia.ap;
 
        WARN_ON(new_ap->num_pages != 0);
+       new_ap->num_pages = 1;
 
        spin_lock(&fi->lock);
-       rb_erase(&new_wpa->writepages_entry, &fi->writepages);
-       old_wpa = fuse_find_writeback(fi, page->index, page->index);
+       old_wpa = fuse_insert_writeback(&fi->writepages, new_wpa);
        if (!old_wpa) {
-               tree_insert(&fi->writepages, new_wpa);
                spin_unlock(&fi->lock);
-               return false;
+               return true;
        }
 
-       new_ap->num_pages = 1;
        for (tmp = old_wpa->next; tmp; tmp = tmp->next) {
                pgoff_t curr_index;
 
@@ -2006,7 +2013,41 @@ static bool fuse_writepage_in_flight(struct fuse_writepage_args *new_wpa,
                fuse_writepage_free(new_wpa);
        }
 
-       return true;
+       return false;
+}
+
+static bool fuse_writepage_need_send(struct fuse_conn *fc, struct page *page,
+                                    struct fuse_args_pages *ap,
+                                    struct fuse_fill_wb_data *data)
+{
+       WARN_ON(!ap->num_pages);
+
+       /*
+        * Being under writeback is unlikely but possible.  For example direct
+        * read to an mmaped fuse file will set the page dirty twice; once when
+        * the pages are faulted with get_user_pages(), and then after the read
+        * completed.
+        */
+       if (fuse_page_is_writeback(data->inode, page->index))
+               return true;
+
+       /* Reached max pages */
+       if (ap->num_pages == fc->max_pages)
+               return true;
+
+       /* Reached max write bytes */
+       if ((ap->num_pages + 1) * PAGE_SIZE > fc->max_write)
+               return true;
+
+       /* Discontinuity */
+       if (data->orig_pages[ap->num_pages - 1]->index + 1 != page->index)
+               return true;
+
+       /* Need to grow the pages array?  If so, did the expansion fail? */
+       if (ap->num_pages == data->max_pages && !fuse_pages_realloc(data))
+               return true;
+
+       return false;
 }
 
 static int fuse_writepages_fill(struct page *page,
@@ -2019,7 +2060,6 @@ static int fuse_writepages_fill(struct page *page,
        struct fuse_inode *fi = get_fuse_inode(inode);
        struct fuse_conn *fc = get_fuse_conn(inode);
        struct page *tmp_page;
-       bool is_writeback;
        int err;
 
        if (!data->ff) {
@@ -2029,25 +2069,9 @@ static int fuse_writepages_fill(struct page *page,
                        goto out_unlock;
        }
 
-       /*
-        * Being under writeback is unlikely but possible.  For example direct
-        * read to an mmaped fuse file will set the page dirty twice; once when
-        * the pages are faulted with get_user_pages(), and then after the read
-        * completed.
-        */
-       is_writeback = fuse_page_is_writeback(inode, page->index);
-
-       if (wpa && ap->num_pages &&
-           (is_writeback || ap->num_pages == fc->max_pages ||
-            (ap->num_pages + 1) * PAGE_SIZE > fc->max_write ||
-            data->orig_pages[ap->num_pages - 1]->index + 1 != page->index)) {
+       if (wpa && fuse_writepage_need_send(fc, page, ap, data)) {
                fuse_writepages_send(data);
                data->wpa = NULL;
-       } else if (wpa && ap->num_pages == data->max_pages) {
-               if (!fuse_pages_realloc(data)) {
-                       fuse_writepages_send(data);
-                       data->wpa = NULL;
-               }
        }
 
        err = -ENOMEM;
@@ -2085,12 +2109,6 @@ static int fuse_writepages_fill(struct page *page,
                ap->args.end = fuse_writepage_end;
                ap->num_pages = 0;
                wpa->inode = inode;
-
-               spin_lock(&fi->lock);
-               tree_insert(&fi->writepages, wpa);
-               spin_unlock(&fi->lock);
-
-               data->wpa = wpa;
        }
        set_page_writeback(page);
 
@@ -2098,26 +2116,25 @@ static int fuse_writepages_fill(struct page *page,
        ap->pages[ap->num_pages] = tmp_page;
        ap->descs[ap->num_pages].offset = 0;
        ap->descs[ap->num_pages].length = PAGE_SIZE;
+       data->orig_pages[ap->num_pages] = page;
 
        inc_wb_stat(&inode_to_bdi(inode)->wb, WB_WRITEBACK);
        inc_node_page_state(tmp_page, NR_WRITEBACK_TEMP);
 
        err = 0;
-       if (is_writeback && fuse_writepage_in_flight(wpa, page)) {
+       if (data->wpa) {
+               /*
+                * Protected by fi->lock against concurrent access by
+                * fuse_page_is_writeback().
+                */
+               spin_lock(&fi->lock);
+               ap->num_pages++;
+               spin_unlock(&fi->lock);
+       } else if (fuse_writepage_add(wpa, page)) {
+               data->wpa = wpa;
+       } else {
                end_page_writeback(page);
-               data->wpa = NULL;
-               goto out_unlock;
        }
-       data->orig_pages[ap->num_pages] = page;
-
-       /*
-        * Protected by fi->lock against concurrent access by
-        * fuse_page_is_writeback().
-        */
-       spin_lock(&fi->lock);
-       ap->num_pages++;
-       spin_unlock(&fi->lock);
-
 out_unlock:
        unlock_page(page);
 
@@ -2149,10 +2166,8 @@ static int fuse_writepages(struct address_space *mapping,
 
        err = write_cache_pages(mapping, wbc, fuse_writepages_fill, &data);
        if (data.wpa) {
-               /* Ignore errors if we can write at least one page */
                WARN_ON(!data.wpa->ia.ap.num_pages);
                fuse_writepages_send(&data);
-               err = 0;
        }
        if (data.ff)
                fuse_file_put(data.ff, false, false);
@@ -2761,7 +2776,16 @@ long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg,
                struct iovec *iov = iov_page;
 
                iov->iov_base = (void __user *)arg;
-               iov->iov_len = _IOC_SIZE(cmd);
+
+               switch (cmd) {
+               case FS_IOC_GETFLAGS:
+               case FS_IOC_SETFLAGS:
+                       iov->iov_len = sizeof(int);
+                       break;
+               default:
+                       iov->iov_len = _IOC_SIZE(cmd);
+                       break;
+               }
 
                if (_IOC_DIR(cmd) & _IOC_WRITE) {
                        in_iov = iov;