Commit 0dd68a34 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'fuse-fixes-5.8-rc6' of...

Merge tag 'fuse-fixes-5.8-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse into master

Pull fuse fixes from Miklos Szeredi:

 - two regressions in this cycle caused by the conversion of writepage
   list to an rb_tree

 - two regressions in v5.4 cause by the conversion to the new mount API

 - saner behavior of fsconfig(2) for the reconfigure case

 - an ancient issue with FS_IOC_{GET,SET}FLAGS ioctls

* tag 'fuse-fixes-5.8-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse:
  fuse: Fix parameter for FS_IOC_{GET,SET}FLAGS
  fuse: don't ignore errors from fuse_writepages_fill()
  fuse: clean up condition for writepage sending
  fuse: reject options on reconfigure via fsconfig(2)
  fuse: ignore 'data' argument of mount(..., MS_REMOUNT)
  fuse: use ->reconfigure() instead of ->remount_fs()
  fuse: fix warning in tree_insert() and clean up writepage insertion
  fuse: move rb_erase() before tree_insert()
parents 44fea373 31070f6c
Loading
Loading
Loading
Loading
+78 −54
Original line number Diff line number Diff line
@@ -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,13 +1961,13 @@ 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,
static bool fuse_writepage_add(struct fuse_writepage_args *new_wpa,
			       struct page *page)
{
	struct fuse_inode *fi = get_fuse_inode(new_wpa->inode);
@@ -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 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,26 +2069,10 @@ 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)) {
		fuse_writepages_send(data);
		data->wpa = NULL;
	} else if (wpa && ap->num_pages == data->max_pages) {
		if (!fuse_pages_realloc(data)) {
	if (wpa && fuse_writepage_need_send(fc, page, ap, data)) {
		fuse_writepages_send(data);
		data->wpa = NULL;
	}
	}

	err = -ENOMEM;
	tmp_page = alloc_page(GFP_NOFS | __GFP_HIGHMEM);
@@ -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,18 +2116,13 @@ 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)) {
		end_page_writeback(page);
		data->wpa = NULL;
		goto out_unlock;
	}
	data->orig_pages[ap->num_pages] = page;

	if (data->wpa) {
		/*
		 * Protected by fi->lock against concurrent access by
		 * fuse_page_is_writeback().
@@ -2117,7 +2130,11 @@ static int fuse_writepages_fill(struct page *page,
		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);
	}
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;

		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;
+16 −3
Original line number Diff line number Diff line
@@ -121,10 +121,12 @@ static void fuse_evict_inode(struct inode *inode)
	}
}

static int fuse_remount_fs(struct super_block *sb, int *flags, char *data)
static int fuse_reconfigure(struct fs_context *fc)
{
	struct super_block *sb = fc->root->d_sb;

	sync_filesystem(sb);
	if (*flags & SB_MANDLOCK)
	if (fc->sb_flags & SB_MANDLOCK)
		return -EINVAL;

	return 0;
@@ -475,6 +477,17 @@ static int fuse_parse_param(struct fs_context *fc, struct fs_parameter *param)
	struct fuse_fs_context *ctx = fc->fs_private;
	int opt;

	if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE) {
		/*
		 * Ignore options coming from mount(MS_REMOUNT) for backward
		 * compatibility.
		 */
		if (fc->oldapi)
			return 0;

		return invalfc(fc, "No changes allowed in reconfigure");
	}

	opt = fs_parse(fc, fuse_fs_parameters, param, &result);
	if (opt < 0)
		return opt;
@@ -817,7 +830,6 @@ static const struct super_operations fuse_super_operations = {
	.evict_inode	= fuse_evict_inode,
	.write_inode	= fuse_write_inode,
	.drop_inode	= generic_delete_inode,
	.remount_fs	= fuse_remount_fs,
	.put_super	= fuse_put_super,
	.umount_begin	= fuse_umount_begin,
	.statfs		= fuse_statfs,
@@ -1296,6 +1308,7 @@ static int fuse_get_tree(struct fs_context *fc)
static const struct fs_context_operations fuse_context_ops = {
	.free		= fuse_free_fc,
	.parse_param	= fuse_parse_param,
	.reconfigure	= fuse_reconfigure,
	.get_tree	= fuse_get_tree,
};

+1 −0
Original line number Diff line number Diff line
@@ -2603,6 +2603,7 @@ static int do_remount(struct path *path, int ms_flags, int sb_flags,
	if (IS_ERR(fc))
		return PTR_ERR(fc);

	fc->oldapi = true;
	err = parse_monolithic_mount_data(fc, data);
	if (!err) {
		down_write(&sb->s_umount);
+1 −0
Original line number Diff line number Diff line
@@ -109,6 +109,7 @@ struct fs_context {
	enum fs_context_phase	phase:8;	/* The phase the context is in */
	bool			need_free:1;	/* Need to call ops->free() */
	bool			global:1;	/* Goes into &init_user_ns */
	bool			oldapi:1;	/* Coming from mount(2) */
};

struct fs_context_operations {