mirror of https://gitee.com/openkylin/linux.git
fuse fixes for 5.8-rc6
-----BEGIN PGP SIGNATURE----- iHUEABYIAB0WIQSQHSd0lITzzeNWNm3h3BK/laaZPAUCXxGFQwAKCRDh3BK/laaZ PDYzAP9bXxHQaRdetnj6lOGNWjmVmiHfntxHqkl6QjZf6e1WlwD+NRXayVTc+Lzw M1pBK6kqovMQVWkyFfA3dTq/BZMzfAc= =9GPn -----END PGP SIGNATURE----- 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()
This commit is contained in:
commit
0dd68a34ec
132
fs/fuse/file.c
132
fs/fuse/file.c
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue