diff --git a/fs/fuse/control.c b/fs/fuse/control.c index acc35819aae64b5c98de795ca1827a05a69c7a0e..ffe238d97d52e8a00fd7170b46562bd3c218429b 100644 --- a/fs/fuse/control.c +++ b/fs/fuse/control.c @@ -41,6 +41,18 @@ static ssize_t fuse_conn_abort_write(struct file *file, const char __user *buf, return count; } +static ssize_t fuse_conn_flush_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct fuse_conn *fc = fuse_ctl_file_conn_get(file); + + if (fc) { + fuse_flush_pq(fc); + fuse_conn_put(fc); + } + return count; +} + static ssize_t fuse_conn_waiting_read(struct file *file, char __user *buf, size_t len, loff_t *ppos) { @@ -176,6 +188,12 @@ static const struct file_operations fuse_ctl_abort_ops = { .llseek = no_llseek, }; +static const struct file_operations fuse_ctl_flush_ops = { + .open = nonseekable_open, + .write = fuse_conn_flush_write, + .llseek = no_llseek, +}; + static const struct file_operations fuse_ctl_waiting_ops = { .open = nonseekable_open, .read = fuse_conn_waiting_read, @@ -260,6 +278,8 @@ int fuse_ctl_add_conn(struct fuse_conn *fc) NULL, &fuse_ctl_waiting_ops) || !fuse_ctl_add_dentry(parent, fc, "abort", S_IFREG | 0200, 1, NULL, &fuse_ctl_abort_ops) || + !fuse_ctl_add_dentry(parent, fc, "flush", S_IFREG | 0200, 1, + NULL, &fuse_ctl_flush_ops) || !fuse_ctl_add_dentry(parent, fc, "max_background", S_IFREG | 0600, 1, NULL, &fuse_conn_max_background_ops) || !fuse_ctl_add_dentry(parent, fc, "congestion_threshold", diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 6d39143cfa09460c9a3ca18c1036dc022225d7d7..89619cb586b8fbd4e3b4cdaa6c72efd2a123509d 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -2208,6 +2208,41 @@ int fuse_dev_release(struct inode *inode, struct file *file) } EXPORT_SYMBOL_GPL(fuse_dev_release); +/* + * Flush all pending processing requests. + * + * The failover procedure will reuse the fuse_conn after usersapce crash and + * recovery. But the reqs in the processing queue will never get the reply, + * making the application stuck forever. + * + * So we need to flush these reqs by sysfs api. We only flush the reqs in + * the processing queue, because these reqs have been sent to userspace. + * Firstly we need to dequeue the req from the processing queue, and secondly + * we need to call request_end to finish it. + */ +void fuse_flush_pq(struct fuse_conn *fc) +{ + struct fuse_dev *fud; + LIST_HEAD(to_end); + + spin_lock(&fc->lock); + if (!fc->connected) { + spin_unlock(&fc->lock); + return; + } + list_for_each_entry(fud, &fc->devices, entry) { + struct fuse_pqueue *fpq = &fud->pq; + + spin_lock(&fpq->lock); + WARN_ON(!list_empty(&fpq->io)); + list_splice_init(&fpq->processing, &to_end); + spin_unlock(&fpq->lock); + } + spin_unlock(&fc->lock); + + end_requests(fc, &to_end); +} + static int fuse_dev_fasync(int fd, struct file *file, int on) { struct fuse_dev *fud = fuse_get_dev(file); diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index dbfc35efbefb64a0234ae73b6859302766f2aac7..b4878607264865fd588bf5b7d5f9119c1a117b78 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -38,7 +38,7 @@ #define FUSE_NAME_MAX 1024 /** Number of dentries for each connection in the control filesystem */ -#define FUSE_CTL_NUM_DENTRIES 5 +#define FUSE_CTL_NUM_DENTRIES 6 /** Number of page pointers embedded in fuse_req */ #define FUSE_REQ_INLINE_PAGES 1 @@ -867,6 +867,9 @@ void fuse_request_send_background_locked(struct fuse_conn *fc, void fuse_abort_conn(struct fuse_conn *fc, bool is_abort); void fuse_wait_aborted(struct fuse_conn *fc); +/* Flush all requests in processing queue */ +void fuse_flush_pq(struct fuse_conn *fc); + /** * Invalidate inode attributes */