From 0213de764a40ab482ea3cff023aba83a000eaa38 Mon Sep 17 00:00:00 2001 From: Vivek Goyal Date: Wed, 6 Mar 2019 16:51:40 -0500 Subject: [PATCH 1839/2944] fuse: Separate fuse device allocation and installation in fuse_conn task #28910367 commit 0cd1eb9a4160a96e0ec9b93b2e7b489f449bf22d upstream As of now fuse_dev_alloc() both allocates a fuse device and installs it in fuse_conn list. fuse_dev_alloc() can fail if fuse_device allocation fails. virtio-fs needs to initialize multiple fuse devices (one per virtio queue). It initializes one fuse device as part of call to fuse_fill_super_common() and rest of the devices are allocated and installed after that. But, we can't affort to fail after calling fuse_fill_super_common() as we don't have a way to undo all the actions done by fuse_fill_super_common(). So to avoid failures after the call to fuse_fill_super_common(), pre-allocate all fuse devices early and install them into fuse connection later. This patch provides two separate helpers for fuse device allocation and fuse device installation in fuse_conn. Signed-off-by: Vivek Goyal Signed-off-by: Liu Bo Reviewed-by: Joseph Qi --- fs/fuse/cuse.c | 2 +- fs/fuse/dev.c | 2 +- fs/fuse/fuse_i.h | 4 +++- fs/fuse/inode.c | 34 +++++++++++++++++++++++++--------- 4 files changed, 30 insertions(+), 12 deletions(-) diff --git a/fs/fuse/cuse.c b/fs/fuse/cuse.c index fb8f124..1fa9c4c 100644 --- a/fs/fuse/cuse.c +++ b/fs/fuse/cuse.c @@ -505,7 +505,7 @@ static int cuse_channel_open(struct inode *inode, struct file *file) */ fuse_conn_init(&cc->fc, file->f_cred->user_ns, &fuse_dev_fiq_ops, NULL); - fud = fuse_dev_alloc(&cc->fc); + fud = fuse_dev_alloc_install(&cc->fc); if (!fud) { kfree(cc); return -ENOMEM; diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index adf7b65..a8d8524 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -2283,7 +2283,7 @@ static int fuse_device_clone(struct fuse_conn *fc, struct file *new) if (new->private_data) return -EINVAL; - fud = fuse_dev_alloc(fc); + fud = fuse_dev_alloc_install(fc); if (!fud) return -ENOMEM; diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index c6ca244..c4f900a 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -955,7 +955,9 @@ void fuse_conn_init(struct fuse_conn *fc, struct user_namespace *user_ns, */ void fuse_conn_put(struct fuse_conn *fc); -struct fuse_dev *fuse_dev_alloc(struct fuse_conn *fc); +struct fuse_dev *fuse_dev_alloc_install(struct fuse_conn *fc); +struct fuse_dev *fuse_dev_alloc(void); +void fuse_dev_install(struct fuse_dev *fud, struct fuse_conn *fc); void fuse_dev_free(struct fuse_dev *fud); /** diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 31e0d38..bf2a355 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -1012,24 +1012,40 @@ static int fuse_bdi_init(struct fuse_conn *fc, struct super_block *sb) return 0; } -struct fuse_dev *fuse_dev_alloc(struct fuse_conn *fc) +struct fuse_dev *fuse_dev_alloc(void) { struct fuse_dev *fud; fud = kzalloc(sizeof(struct fuse_dev), GFP_KERNEL); - if (fud) { - fud->fc = fuse_conn_get(fc); + if (fud) fuse_pqueue_init(&fud->pq); - spin_lock(&fc->lock); - list_add_tail(&fud->entry, &fc->devices); - spin_unlock(&fc->lock); - } - return fud; } EXPORT_SYMBOL_GPL(fuse_dev_alloc); +void fuse_dev_install(struct fuse_dev *fud, struct fuse_conn *fc) +{ + fud->fc = fuse_conn_get(fc); + spin_lock(&fc->lock); + list_add_tail(&fud->entry, &fc->devices); + spin_unlock(&fc->lock); +} +EXPORT_SYMBOL_GPL(fuse_dev_install); + +struct fuse_dev *fuse_dev_alloc_install(struct fuse_conn *fc) +{ + struct fuse_dev *fud; + + fud = fuse_dev_alloc(); + if (!fud) + return NULL; + + fuse_dev_install(fud, fc); + return fud; +} +EXPORT_SYMBOL_GPL(fuse_dev_alloc_install); + void fuse_dev_free(struct fuse_dev *fud) { struct fuse_conn *fc = fud->fc; @@ -1099,7 +1115,7 @@ int fuse_fill_super_common(struct super_block *sb, fuse_conn_init(fc, sb->s_user_ns, fiq_ops, fiq_priv); fc->release = fuse_free_conn; - fud = fuse_dev_alloc(fc); + fud = fuse_dev_alloc_install(fc); if (!fud) goto err_put_conn; -- 1.8.3.1