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

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
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include <linux/swap.h> #include <linux/swap.h>
#include <linux/falloc.h> #include <linux/falloc.h>
#include <linux/uio.h> #include <linux/uio.h>
#include <linux/fs.h>
static struct page **fuse_pages_alloc(unsigned int npages, gfp_t flags, static struct page **fuse_pages_alloc(unsigned int npages, gfp_t flags,
struct fuse_page_desc **desc) struct fuse_page_desc **desc)
...@@ -1586,7 +1587,6 @@ static void fuse_writepage_finish(struct fuse_conn *fc, ...@@ -1586,7 +1587,6 @@ static void fuse_writepage_finish(struct fuse_conn *fc,
struct backing_dev_info *bdi = inode_to_bdi(inode); struct backing_dev_info *bdi = inode_to_bdi(inode);
int i; int i;
rb_erase(&wpa->writepages_entry, &fi->writepages);
for (i = 0; i < ap->num_pages; i++) { for (i = 0; i < ap->num_pages; i++) {
dec_wb_stat(&bdi->wb, WB_WRITEBACK); dec_wb_stat(&bdi->wb, WB_WRITEBACK);
dec_node_page_state(ap->pages[i], NR_WRITEBACK_TEMP); dec_node_page_state(ap->pages[i], NR_WRITEBACK_TEMP);
...@@ -1637,6 +1637,7 @@ __acquires(fi->lock) ...@@ -1637,6 +1637,7 @@ __acquires(fi->lock)
out_free: out_free:
fi->writectr--; fi->writectr--;
rb_erase(&wpa->writepages_entry, &fi->writepages);
fuse_writepage_finish(fc, wpa); fuse_writepage_finish(fc, wpa);
spin_unlock(&fi->lock); spin_unlock(&fi->lock);
...@@ -1674,7 +1675,8 @@ __acquires(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_from = wpa->ia.write.in.offset >> PAGE_SHIFT;
pgoff_t idx_to = idx_from + wpa->ia.ap.num_pages - 1; 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) ...@@ -1697,11 +1699,17 @@ static void tree_insert(struct rb_root *root, struct fuse_writepage_args *wpa)
else if (idx_to < curr_index) else if (idx_to < curr_index)
p = &(*p)->rb_left; p = &(*p)->rb_left;
else else
return (void) WARN_ON(true); return curr;
} }
rb_link_node(&wpa->writepages_entry, parent, p); rb_link_node(&wpa->writepages_entry, parent, p);
rb_insert_color(&wpa->writepages_entry, root); 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, 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, ...@@ -1714,6 +1722,7 @@ static void fuse_writepage_end(struct fuse_conn *fc, struct fuse_args *args,
mapping_set_error(inode->i_mapping, error); mapping_set_error(inode->i_mapping, error);
spin_lock(&fi->lock); spin_lock(&fi->lock);
rb_erase(&wpa->writepages_entry, &fi->writepages);
while (wpa->next) { while (wpa->next) {
struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_conn *fc = get_fuse_conn(inode);
struct fuse_write_in *inarg = &wpa->ia.write.in; struct fuse_write_in *inarg = &wpa->ia.write.in;
...@@ -1952,14 +1961,14 @@ static void fuse_writepages_send(struct fuse_fill_wb_data *data) ...@@ -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 * Check under fi->lock if the page is under writeback, and insert it onto the
* writeback. If yes, then iterate auxiliary write requests, to see if there's * 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 * 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 * 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 page *page)
{ {
struct fuse_inode *fi = get_fuse_inode(new_wpa->inode); struct fuse_inode *fi = get_fuse_inode(new_wpa->inode);
struct fuse_writepage_args *tmp; struct fuse_writepage_args *tmp;
...@@ -1967,17 +1976,15 @@ static bool fuse_writepage_in_flight(struct fuse_writepage_args *new_wpa, ...@@ -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; struct fuse_args_pages *new_ap = &new_wpa->ia.ap;
WARN_ON(new_ap->num_pages != 0); WARN_ON(new_ap->num_pages != 0);
new_ap->num_pages = 1;
spin_lock(&fi->lock); spin_lock(&fi->lock);
rb_erase(&new_wpa->writepages_entry, &fi->writepages); old_wpa = fuse_insert_writeback(&fi->writepages, new_wpa);
old_wpa = fuse_find_writeback(fi, page->index, page->index);
if (!old_wpa) { if (!old_wpa) {
tree_insert(&fi->writepages, new_wpa);
spin_unlock(&fi->lock); spin_unlock(&fi->lock);
return false; return true;
} }
new_ap->num_pages = 1;
for (tmp = old_wpa->next; tmp; tmp = tmp->next) { for (tmp = old_wpa->next; tmp; tmp = tmp->next) {
pgoff_t curr_index; pgoff_t curr_index;
...@@ -2006,7 +2013,41 @@ static bool fuse_writepage_in_flight(struct fuse_writepage_args *new_wpa, ...@@ -2006,7 +2013,41 @@ static bool fuse_writepage_in_flight(struct fuse_writepage_args *new_wpa,
fuse_writepage_free(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, static int fuse_writepages_fill(struct page *page,
...@@ -2019,7 +2060,6 @@ 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_inode *fi = get_fuse_inode(inode);
struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_conn *fc = get_fuse_conn(inode);
struct page *tmp_page; struct page *tmp_page;
bool is_writeback;
int err; int err;
if (!data->ff) { if (!data->ff) {
...@@ -2029,25 +2069,9 @@ static int fuse_writepages_fill(struct page *page, ...@@ -2029,25 +2069,9 @@ static int fuse_writepages_fill(struct page *page,
goto out_unlock; goto out_unlock;
} }
/* if (wpa && fuse_writepage_need_send(fc, page, ap, data)) {
* 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); fuse_writepages_send(data);
data->wpa = NULL; 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; err = -ENOMEM;
...@@ -2085,12 +2109,6 @@ static int fuse_writepages_fill(struct page *page, ...@@ -2085,12 +2109,6 @@ static int fuse_writepages_fill(struct page *page,
ap->args.end = fuse_writepage_end; ap->args.end = fuse_writepage_end;
ap->num_pages = 0; ap->num_pages = 0;
wpa->inode = inode; wpa->inode = inode;
spin_lock(&fi->lock);
tree_insert(&fi->writepages, wpa);
spin_unlock(&fi->lock);
data->wpa = wpa;
} }
set_page_writeback(page); set_page_writeback(page);
...@@ -2098,26 +2116,25 @@ static int fuse_writepages_fill(struct page *page, ...@@ -2098,26 +2116,25 @@ static int fuse_writepages_fill(struct page *page,
ap->pages[ap->num_pages] = tmp_page; ap->pages[ap->num_pages] = tmp_page;
ap->descs[ap->num_pages].offset = 0; ap->descs[ap->num_pages].offset = 0;
ap->descs[ap->num_pages].length = PAGE_SIZE; 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_wb_stat(&inode_to_bdi(inode)->wb, WB_WRITEBACK);
inc_node_page_state(tmp_page, NR_WRITEBACK_TEMP); inc_node_page_state(tmp_page, NR_WRITEBACK_TEMP);
err = 0; 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); 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: out_unlock:
unlock_page(page); unlock_page(page);
...@@ -2149,10 +2166,8 @@ static int fuse_writepages(struct address_space *mapping, ...@@ -2149,10 +2166,8 @@ static int fuse_writepages(struct address_space *mapping,
err = write_cache_pages(mapping, wbc, fuse_writepages_fill, &data); err = write_cache_pages(mapping, wbc, fuse_writepages_fill, &data);
if (data.wpa) { if (data.wpa) {
/* Ignore errors if we can write at least one page */
WARN_ON(!data.wpa->ia.ap.num_pages); WARN_ON(!data.wpa->ia.ap.num_pages);
fuse_writepages_send(&data); fuse_writepages_send(&data);
err = 0;
} }
if (data.ff) if (data.ff)
fuse_file_put(data.ff, false, false); fuse_file_put(data.ff, false, false);
...@@ -2761,7 +2776,16 @@ long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg, ...@@ -2761,7 +2776,16 @@ long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg,
struct iovec *iov = iov_page; struct iovec *iov = iov_page;
iov->iov_base = (void __user *)arg; 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) { if (_IOC_DIR(cmd) & _IOC_WRITE) {
in_iov = iov; in_iov = iov;
......
...@@ -121,10 +121,12 @@ static void fuse_evict_inode(struct inode *inode) ...@@ -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); sync_filesystem(sb);
if (*flags & SB_MANDLOCK) if (fc->sb_flags & SB_MANDLOCK)
return -EINVAL; return -EINVAL;
return 0; return 0;
...@@ -475,6 +477,17 @@ static int fuse_parse_param(struct fs_context *fc, struct fs_parameter *param) ...@@ -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; struct fuse_fs_context *ctx = fc->fs_private;
int opt; 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); opt = fs_parse(fc, fuse_fs_parameters, param, &result);
if (opt < 0) if (opt < 0)
return opt; return opt;
...@@ -817,7 +830,6 @@ static const struct super_operations fuse_super_operations = { ...@@ -817,7 +830,6 @@ static const struct super_operations fuse_super_operations = {
.evict_inode = fuse_evict_inode, .evict_inode = fuse_evict_inode,
.write_inode = fuse_write_inode, .write_inode = fuse_write_inode,
.drop_inode = generic_delete_inode, .drop_inode = generic_delete_inode,
.remount_fs = fuse_remount_fs,
.put_super = fuse_put_super, .put_super = fuse_put_super,
.umount_begin = fuse_umount_begin, .umount_begin = fuse_umount_begin,
.statfs = fuse_statfs, .statfs = fuse_statfs,
...@@ -1296,6 +1308,7 @@ static int fuse_get_tree(struct fs_context *fc) ...@@ -1296,6 +1308,7 @@ static int fuse_get_tree(struct fs_context *fc)
static const struct fs_context_operations fuse_context_ops = { static const struct fs_context_operations fuse_context_ops = {
.free = fuse_free_fc, .free = fuse_free_fc,
.parse_param = fuse_parse_param, .parse_param = fuse_parse_param,
.reconfigure = fuse_reconfigure,
.get_tree = fuse_get_tree, .get_tree = fuse_get_tree,
}; };
......
...@@ -2603,6 +2603,7 @@ static int do_remount(struct path *path, int ms_flags, int sb_flags, ...@@ -2603,6 +2603,7 @@ static int do_remount(struct path *path, int ms_flags, int sb_flags,
if (IS_ERR(fc)) if (IS_ERR(fc))
return PTR_ERR(fc); return PTR_ERR(fc);
fc->oldapi = true;
err = parse_monolithic_mount_data(fc, data); err = parse_monolithic_mount_data(fc, data);
if (!err) { if (!err) {
down_write(&sb->s_umount); down_write(&sb->s_umount);
......
...@@ -109,6 +109,7 @@ struct fs_context { ...@@ -109,6 +109,7 @@ struct fs_context {
enum fs_context_phase phase:8; /* The phase the context is in */ enum fs_context_phase phase:8; /* The phase the context is in */
bool need_free:1; /* Need to call ops->free() */ bool need_free:1; /* Need to call ops->free() */
bool global:1; /* Goes into &init_user_ns */ bool global:1; /* Goes into &init_user_ns */
bool oldapi:1; /* Coming from mount(2) */
}; };
struct fs_context_operations { struct fs_context_operations {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment