Commit 75b96f0e authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'fuse-update-5.15' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse

Pull fuse updates from Miklos Szeredi:

 - Allow mounting an active fuse device. Previously the fuse device
   would always be mounted during initialization, and sharing a fuse
   superblock was only possible through mount or namespace cloning

 - Fix data flushing in syncfs (virtiofs only)

 - Fix data flushing in copy_file_range()

 - Fix a possible deadlock in atomic O_TRUNC

 - Misc fixes and cleanups

* tag 'fuse-update-5.15' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse:
  fuse: remove unused arg in fuse_write_file_get()
  fuse: wait for writepages in syncfs
  fuse: flush extending writes
  fuse: truncate pagecache on atomic_o_trunc
  fuse: allow sharing existing sb
  fuse: move fget() to fuse_get_tree()
  fuse: move option checking into fuse_fill_super()
  fuse: name fs_context consistently
  fuse: fix use after free in fuse_read_interrupt()
parents 996fe061 a9667ac8
...@@ -328,7 +328,7 @@ void fuse_ctl_remove_conn(struct fuse_conn *fc) ...@@ -328,7 +328,7 @@ void fuse_ctl_remove_conn(struct fuse_conn *fc)
drop_nlink(d_inode(fuse_control_sb->s_root)); drop_nlink(d_inode(fuse_control_sb->s_root));
} }
static int fuse_ctl_fill_super(struct super_block *sb, struct fs_context *fctx) static int fuse_ctl_fill_super(struct super_block *sb, struct fs_context *fsc)
{ {
static const struct tree_descr empty_descr = {""}; static const struct tree_descr empty_descr = {""};
struct fuse_conn *fc; struct fuse_conn *fc;
...@@ -354,18 +354,18 @@ static int fuse_ctl_fill_super(struct super_block *sb, struct fs_context *fctx) ...@@ -354,18 +354,18 @@ static int fuse_ctl_fill_super(struct super_block *sb, struct fs_context *fctx)
return 0; return 0;
} }
static int fuse_ctl_get_tree(struct fs_context *fc) static int fuse_ctl_get_tree(struct fs_context *fsc)
{ {
return get_tree_single(fc, fuse_ctl_fill_super); return get_tree_single(fsc, fuse_ctl_fill_super);
} }
static const struct fs_context_operations fuse_ctl_context_ops = { static const struct fs_context_operations fuse_ctl_context_ops = {
.get_tree = fuse_ctl_get_tree, .get_tree = fuse_ctl_get_tree,
}; };
static int fuse_ctl_init_fs_context(struct fs_context *fc) static int fuse_ctl_init_fs_context(struct fs_context *fsc)
{ {
fc->ops = &fuse_ctl_context_ops; fsc->ops = &fuse_ctl_context_ops;
return 0; return 0;
} }
......
...@@ -288,10 +288,10 @@ void fuse_request_end(struct fuse_req *req) ...@@ -288,10 +288,10 @@ void fuse_request_end(struct fuse_req *req)
/* /*
* test_and_set_bit() implies smp_mb() between bit * test_and_set_bit() implies smp_mb() between bit
* changing and below intr_entry check. Pairs with * changing and below FR_INTERRUPTED check. Pairs with
* smp_mb() from queue_interrupt(). * smp_mb() from queue_interrupt().
*/ */
if (!list_empty(&req->intr_entry)) { if (test_bit(FR_INTERRUPTED, &req->flags)) {
spin_lock(&fiq->lock); spin_lock(&fiq->lock);
list_del_init(&req->intr_entry); list_del_init(&req->intr_entry);
spin_unlock(&fiq->lock); spin_unlock(&fiq->lock);
......
...@@ -198,12 +198,11 @@ void fuse_finish_open(struct inode *inode, struct file *file) ...@@ -198,12 +198,11 @@ void fuse_finish_open(struct inode *inode, struct file *file)
struct fuse_file *ff = file->private_data; struct fuse_file *ff = file->private_data;
struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_conn *fc = get_fuse_conn(inode);
if (!(ff->open_flags & FOPEN_KEEP_CACHE))
invalidate_inode_pages2(inode->i_mapping);
if (ff->open_flags & FOPEN_STREAM) if (ff->open_flags & FOPEN_STREAM)
stream_open(inode, file); stream_open(inode, file);
else if (ff->open_flags & FOPEN_NONSEEKABLE) else if (ff->open_flags & FOPEN_NONSEEKABLE)
nonseekable_open(inode, file); nonseekable_open(inode, file);
if (fc->atomic_o_trunc && (file->f_flags & O_TRUNC)) { if (fc->atomic_o_trunc && (file->f_flags & O_TRUNC)) {
struct fuse_inode *fi = get_fuse_inode(inode); struct fuse_inode *fi = get_fuse_inode(inode);
...@@ -211,10 +210,14 @@ void fuse_finish_open(struct inode *inode, struct file *file) ...@@ -211,10 +210,14 @@ void fuse_finish_open(struct inode *inode, struct file *file)
fi->attr_version = atomic64_inc_return(&fc->attr_version); fi->attr_version = atomic64_inc_return(&fc->attr_version);
i_size_write(inode, 0); i_size_write(inode, 0);
spin_unlock(&fi->lock); spin_unlock(&fi->lock);
truncate_pagecache(inode, 0);
fuse_invalidate_attr(inode); fuse_invalidate_attr(inode);
if (fc->writeback_cache) if (fc->writeback_cache)
file_update_time(file); file_update_time(file);
} else if (!(ff->open_flags & FOPEN_KEEP_CACHE)) {
invalidate_inode_pages2(inode->i_mapping);
} }
if ((file->f_mode & FMODE_WRITE) && fc->writeback_cache) if ((file->f_mode & FMODE_WRITE) && fc->writeback_cache)
fuse_link_write_file(file); fuse_link_write_file(file);
} }
...@@ -389,6 +392,7 @@ struct fuse_writepage_args { ...@@ -389,6 +392,7 @@ struct fuse_writepage_args {
struct list_head queue_entry; struct list_head queue_entry;
struct fuse_writepage_args *next; struct fuse_writepage_args *next;
struct inode *inode; struct inode *inode;
struct fuse_sync_bucket *bucket;
}; };
static struct fuse_writepage_args *fuse_find_writeback(struct fuse_inode *fi, static struct fuse_writepage_args *fuse_find_writeback(struct fuse_inode *fi,
...@@ -1608,6 +1612,9 @@ static void fuse_writepage_free(struct fuse_writepage_args *wpa) ...@@ -1608,6 +1612,9 @@ static void fuse_writepage_free(struct fuse_writepage_args *wpa)
struct fuse_args_pages *ap = &wpa->ia.ap; struct fuse_args_pages *ap = &wpa->ia.ap;
int i; int i;
if (wpa->bucket)
fuse_sync_bucket_dec(wpa->bucket);
for (i = 0; i < ap->num_pages; i++) for (i = 0; i < ap->num_pages; i++)
__free_page(ap->pages[i]); __free_page(ap->pages[i]);
...@@ -1813,8 +1820,7 @@ static void fuse_writepage_end(struct fuse_mount *fm, struct fuse_args *args, ...@@ -1813,8 +1820,7 @@ static void fuse_writepage_end(struct fuse_mount *fm, struct fuse_args *args,
fuse_writepage_free(wpa); fuse_writepage_free(wpa);
} }
static struct fuse_file *__fuse_write_file_get(struct fuse_conn *fc, static struct fuse_file *__fuse_write_file_get(struct fuse_inode *fi)
struct fuse_inode *fi)
{ {
struct fuse_file *ff = NULL; struct fuse_file *ff = NULL;
...@@ -1829,22 +1835,20 @@ static struct fuse_file *__fuse_write_file_get(struct fuse_conn *fc, ...@@ -1829,22 +1835,20 @@ static struct fuse_file *__fuse_write_file_get(struct fuse_conn *fc,
return ff; return ff;
} }
static struct fuse_file *fuse_write_file_get(struct fuse_conn *fc, static struct fuse_file *fuse_write_file_get(struct fuse_inode *fi)
struct fuse_inode *fi)
{ {
struct fuse_file *ff = __fuse_write_file_get(fc, fi); struct fuse_file *ff = __fuse_write_file_get(fi);
WARN_ON(!ff); WARN_ON(!ff);
return ff; return ff;
} }
int fuse_write_inode(struct inode *inode, struct writeback_control *wbc) int fuse_write_inode(struct inode *inode, struct writeback_control *wbc)
{ {
struct fuse_conn *fc = get_fuse_conn(inode);
struct fuse_inode *fi = get_fuse_inode(inode); struct fuse_inode *fi = get_fuse_inode(inode);
struct fuse_file *ff; struct fuse_file *ff;
int err; int err;
ff = __fuse_write_file_get(fc, fi); ff = __fuse_write_file_get(fi);
err = fuse_flush_times(inode, ff); err = fuse_flush_times(inode, ff);
if (ff) if (ff)
fuse_file_put(ff, false, false); fuse_file_put(ff, false, false);
...@@ -1871,6 +1875,20 @@ static struct fuse_writepage_args *fuse_writepage_args_alloc(void) ...@@ -1871,6 +1875,20 @@ static struct fuse_writepage_args *fuse_writepage_args_alloc(void)
} }
static void fuse_writepage_add_to_bucket(struct fuse_conn *fc,
struct fuse_writepage_args *wpa)
{
if (!fc->sync_fs)
return;
rcu_read_lock();
/* Prevent resurrection of dead bucket in unlikely race with syncfs */
do {
wpa->bucket = rcu_dereference(fc->curr_bucket);
} while (unlikely(!atomic_inc_not_zero(&wpa->bucket->count)));
rcu_read_unlock();
}
static int fuse_writepage_locked(struct page *page) static int fuse_writepage_locked(struct page *page)
{ {
struct address_space *mapping = page->mapping; struct address_space *mapping = page->mapping;
...@@ -1894,10 +1912,11 @@ static int fuse_writepage_locked(struct page *page) ...@@ -1894,10 +1912,11 @@ static int fuse_writepage_locked(struct page *page)
goto err_free; goto err_free;
error = -EIO; error = -EIO;
wpa->ia.ff = fuse_write_file_get(fc, fi); wpa->ia.ff = fuse_write_file_get(fi);
if (!wpa->ia.ff) if (!wpa->ia.ff)
goto err_nofile; goto err_nofile;
fuse_writepage_add_to_bucket(fc, wpa);
fuse_write_args_fill(&wpa->ia, wpa->ia.ff, page_offset(page), 0); fuse_write_args_fill(&wpa->ia, wpa->ia.ff, page_offset(page), 0);
copy_highpage(tmp_page, page); copy_highpage(tmp_page, page);
...@@ -2113,7 +2132,7 @@ static int fuse_writepages_fill(struct page *page, ...@@ -2113,7 +2132,7 @@ static int fuse_writepages_fill(struct page *page,
if (!data->ff) { if (!data->ff) {
err = -EIO; err = -EIO;
data->ff = fuse_write_file_get(fc, fi); data->ff = fuse_write_file_get(fi);
if (!data->ff) if (!data->ff)
goto out_unlock; goto out_unlock;
} }
...@@ -2148,6 +2167,8 @@ static int fuse_writepages_fill(struct page *page, ...@@ -2148,6 +2167,8 @@ static int fuse_writepages_fill(struct page *page,
__free_page(tmp_page); __free_page(tmp_page);
goto out_unlock; goto out_unlock;
} }
fuse_writepage_add_to_bucket(fc, wpa);
data->max_pages = 1; data->max_pages = 1;
ap = &wpa->ia.ap; ap = &wpa->ia.ap;
...@@ -2881,7 +2902,7 @@ fuse_direct_IO(struct kiocb *iocb, struct iov_iter *iter) ...@@ -2881,7 +2902,7 @@ fuse_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
static int fuse_writeback_range(struct inode *inode, loff_t start, loff_t end) static int fuse_writeback_range(struct inode *inode, loff_t start, loff_t end)
{ {
int err = filemap_write_and_wait_range(inode->i_mapping, start, end); int err = filemap_write_and_wait_range(inode->i_mapping, start, -1);
if (!err) if (!err)
fuse_sync_writes(inode); fuse_sync_writes(inode);
......
...@@ -482,6 +482,7 @@ struct fuse_dev { ...@@ -482,6 +482,7 @@ struct fuse_dev {
struct fuse_fs_context { struct fuse_fs_context {
int fd; int fd;
struct file *file;
unsigned int rootmode; unsigned int rootmode;
kuid_t user_id; kuid_t user_id;
kgid_t group_id; kgid_t group_id;
...@@ -508,6 +509,13 @@ struct fuse_fs_context { ...@@ -508,6 +509,13 @@ struct fuse_fs_context {
void **fudptr; void **fudptr;
}; };
struct fuse_sync_bucket {
/* count is a possible scalability bottleneck */
atomic_t count;
wait_queue_head_t waitq;
struct rcu_head rcu;
};
/** /**
* A Fuse connection. * A Fuse connection.
* *
...@@ -800,6 +808,9 @@ struct fuse_conn { ...@@ -800,6 +808,9 @@ struct fuse_conn {
/** List of filesystems using this connection */ /** List of filesystems using this connection */
struct list_head mounts; struct list_head mounts;
/* New writepages go into this bucket */
struct fuse_sync_bucket __rcu *curr_bucket;
}; };
/* /*
...@@ -903,6 +914,15 @@ static inline void fuse_page_descs_length_init(struct fuse_page_desc *descs, ...@@ -903,6 +914,15 @@ static inline void fuse_page_descs_length_init(struct fuse_page_desc *descs,
descs[i].length = PAGE_SIZE - descs[i].offset; descs[i].length = PAGE_SIZE - descs[i].offset;
} }
static inline void fuse_sync_bucket_dec(struct fuse_sync_bucket *bucket)
{
/* Need RCU protection to prevent use after free after the decrement */
rcu_read_lock();
if (atomic_dec_and_test(&bucket->count))
wake_up(&bucket->waitq);
rcu_read_unlock();
}
/** Device operations */ /** Device operations */
extern const struct file_operations fuse_dev_operations; extern const struct file_operations fuse_dev_operations;
......
This diff is collapsed.
...@@ -97,14 +97,14 @@ static const struct fs_parameter_spec virtio_fs_parameters[] = { ...@@ -97,14 +97,14 @@ static const struct fs_parameter_spec virtio_fs_parameters[] = {
{} {}
}; };
static int virtio_fs_parse_param(struct fs_context *fc, static int virtio_fs_parse_param(struct fs_context *fsc,
struct fs_parameter *param) struct fs_parameter *param)
{ {
struct fs_parse_result result; struct fs_parse_result result;
struct fuse_fs_context *ctx = fc->fs_private; struct fuse_fs_context *ctx = fsc->fs_private;
int opt; int opt;
opt = fs_parse(fc, virtio_fs_parameters, param, &result); opt = fs_parse(fsc, virtio_fs_parameters, param, &result);
if (opt < 0) if (opt < 0)
return opt; return opt;
...@@ -119,9 +119,9 @@ static int virtio_fs_parse_param(struct fs_context *fc, ...@@ -119,9 +119,9 @@ static int virtio_fs_parse_param(struct fs_context *fc,
return 0; return 0;
} }
static void virtio_fs_free_fc(struct fs_context *fc) static void virtio_fs_free_fsc(struct fs_context *fsc)
{ {
struct fuse_fs_context *ctx = fc->fs_private; struct fuse_fs_context *ctx = fsc->fs_private;
kfree(ctx); kfree(ctx);
} }
...@@ -1488,7 +1488,7 @@ static int virtio_fs_get_tree(struct fs_context *fsc) ...@@ -1488,7 +1488,7 @@ static int virtio_fs_get_tree(struct fs_context *fsc)
} }
static const struct fs_context_operations virtio_fs_context_ops = { static const struct fs_context_operations virtio_fs_context_ops = {
.free = virtio_fs_free_fc, .free = virtio_fs_free_fsc,
.parse_param = virtio_fs_parse_param, .parse_param = virtio_fs_parse_param,
.get_tree = virtio_fs_get_tree, .get_tree = virtio_fs_get_tree,
}; };
......
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