diff options
| author | Miklos Szeredi <mszeredi@suse.cz> | 2011-02-25 08:44:58 -0500 |
|---|---|---|
| committer | Miklos Szeredi <mszeredi@suse.cz> | 2011-02-25 08:44:58 -0500 |
| commit | 5a18ec176c934ca1bc9dc61580a5e0e90a9b5733 (patch) | |
| tree | 6995509f59166fff90ce240ce72e3858f61ac101 | |
| parent | 4662db446190ddef8fbab024f72dee77dd04b8f2 (diff) | |
fuse: fix hang of single threaded fuseblk filesystem
Single threaded NTFS-3G could get stuck if a delayed RELEASE reply
triggered a DESTROY request via path_put().
Fix this by
a) making RELEASE requests synchronous, whenever possible, on fuseblk
filesystems
b) if not possible (triggered by an asynchronous read/write) then do
the path_put() in a separate thread with schedule_work().
Reported-by: Oliver Neukum <oneukum@suse.de>
Cc: stable@kernel.org
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
| -rw-r--r-- | fs/fuse/file.c | 52 | ||||
| -rw-r--r-- | fs/fuse/fuse_i.h | 6 |
2 files changed, 50 insertions, 8 deletions
diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 95da1bc1c826..9e0832dbb1e3 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c | |||
| @@ -86,18 +86,52 @@ struct fuse_file *fuse_file_get(struct fuse_file *ff) | |||
| 86 | return ff; | 86 | return ff; |
| 87 | } | 87 | } |
| 88 | 88 | ||
| 89 | static void fuse_release_async(struct work_struct *work) | ||
| 90 | { | ||
| 91 | struct fuse_req *req; | ||
| 92 | struct fuse_conn *fc; | ||
| 93 | struct path path; | ||
| 94 | |||
| 95 | req = container_of(work, struct fuse_req, misc.release.work); | ||
| 96 | path = req->misc.release.path; | ||
| 97 | fc = get_fuse_conn(path.dentry->d_inode); | ||
| 98 | |||
| 99 | fuse_put_request(fc, req); | ||
| 100 | path_put(&path); | ||
| 101 | } | ||
| 102 | |||
| 89 | static void fuse_release_end(struct fuse_conn *fc, struct fuse_req *req) | 103 | static void fuse_release_end(struct fuse_conn *fc, struct fuse_req *req) |
| 90 | { | 104 | { |
| 91 | path_put(&req->misc.release.path); | 105 | if (fc->destroy_req) { |
| 106 | /* | ||
| 107 | * If this is a fuseblk mount, then it's possible that | ||
| 108 | * releasing the path will result in releasing the | ||
| 109 | * super block and sending the DESTROY request. If | ||
| 110 | * the server is single threaded, this would hang. | ||
| 111 | * For this reason do the path_put() in a separate | ||
| 112 | * thread. | ||
| 113 | */ | ||
| 114 | atomic_inc(&req->count); | ||
| 115 | INIT_WORK(&req->misc.release.work, fuse_release_async); | ||
| 116 | schedule_work(&req->misc.release.work); | ||
| 117 | } else { | ||
| 118 | path_put(&req->misc.release.path); | ||
| 119 | } | ||
| 92 | } | 120 | } |
| 93 | 121 | ||
| 94 | static void fuse_file_put(struct fuse_file *ff) | 122 | static void fuse_file_put(struct fuse_file *ff, bool sync) |
| 95 | { | 123 | { |
| 96 | if (atomic_dec_and_test(&ff->count)) { | 124 | if (atomic_dec_and_test(&ff->count)) { |
| 97 | struct fuse_req *req = ff->reserved_req; | 125 | struct fuse_req *req = ff->reserved_req; |
| 98 | 126 | ||
| 99 | req->end = fuse_release_end; | 127 | if (sync) { |
| 100 | fuse_request_send_background(ff->fc, req); | 128 | fuse_request_send(ff->fc, req); |
| 129 | path_put(&req->misc.release.path); | ||
| 130 | fuse_put_request(ff->fc, req); | ||
| 131 | } else { | ||
| 132 | req->end = fuse_release_end; | ||
| 133 | fuse_request_send_background(ff->fc, req); | ||
| 134 | } | ||
| 101 | kfree(ff); | 135 | kfree(ff); |
| 102 | } | 136 | } |
| 103 | } | 137 | } |
| @@ -219,8 +253,12 @@ void fuse_release_common(struct file *file, int opcode) | |||
| 219 | * Normally this will send the RELEASE request, however if | 253 | * Normally this will send the RELEASE request, however if |
| 220 | * some asynchronous READ or WRITE requests are outstanding, | 254 | * some asynchronous READ or WRITE requests are outstanding, |
| 221 | * the sending will be delayed. | 255 | * the sending will be delayed. |
| 256 | * | ||
| 257 | * Make the release synchronous if this is a fuseblk mount, | ||
| 258 | * synchronous RELEASE is allowed (and desirable) in this case | ||
| 259 | * because the server can be trusted not to screw up. | ||
| 222 | */ | 260 | */ |
| 223 | fuse_file_put(ff); | 261 | fuse_file_put(ff, ff->fc->destroy_req != NULL); |
| 224 | } | 262 | } |
| 225 | 263 | ||
| 226 | static int fuse_open(struct inode *inode, struct file *file) | 264 | static int fuse_open(struct inode *inode, struct file *file) |
| @@ -558,7 +596,7 @@ static void fuse_readpages_end(struct fuse_conn *fc, struct fuse_req *req) | |||
| 558 | page_cache_release(page); | 596 | page_cache_release(page); |
| 559 | } | 597 | } |
| 560 | if (req->ff) | 598 | if (req->ff) |
| 561 | fuse_file_put(req->ff); | 599 | fuse_file_put(req->ff, false); |
| 562 | } | 600 | } |
| 563 | 601 | ||
| 564 | static void fuse_send_readpages(struct fuse_req *req, struct file *file) | 602 | static void fuse_send_readpages(struct fuse_req *req, struct file *file) |
| @@ -1137,7 +1175,7 @@ static ssize_t fuse_direct_write(struct file *file, const char __user *buf, | |||
| 1137 | static void fuse_writepage_free(struct fuse_conn *fc, struct fuse_req *req) | 1175 | static void fuse_writepage_free(struct fuse_conn *fc, struct fuse_req *req) |
| 1138 | { | 1176 | { |
| 1139 | __free_page(req->pages[0]); | 1177 | __free_page(req->pages[0]); |
| 1140 | fuse_file_put(req->ff); | 1178 | fuse_file_put(req->ff, false); |
| 1141 | } | 1179 | } |
| 1142 | 1180 | ||
| 1143 | static void fuse_writepage_finish(struct fuse_conn *fc, struct fuse_req *req) | 1181 | static void fuse_writepage_finish(struct fuse_conn *fc, struct fuse_req *req) |
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index ae5744a2f9e9..d4286947bc2c 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h | |||
| @@ -21,6 +21,7 @@ | |||
| 21 | #include <linux/rwsem.h> | 21 | #include <linux/rwsem.h> |
| 22 | #include <linux/rbtree.h> | 22 | #include <linux/rbtree.h> |
| 23 | #include <linux/poll.h> | 23 | #include <linux/poll.h> |
| 24 | #include <linux/workqueue.h> | ||
| 24 | 25 | ||
| 25 | /** Max number of pages that can be used in a single read request */ | 26 | /** Max number of pages that can be used in a single read request */ |
| 26 | #define FUSE_MAX_PAGES_PER_REQ 32 | 27 | #define FUSE_MAX_PAGES_PER_REQ 32 |
| @@ -262,7 +263,10 @@ struct fuse_req { | |||
| 262 | /** Data for asynchronous requests */ | 263 | /** Data for asynchronous requests */ |
| 263 | union { | 264 | union { |
| 264 | struct { | 265 | struct { |
| 265 | struct fuse_release_in in; | 266 | union { |
| 267 | struct fuse_release_in in; | ||
| 268 | struct work_struct work; | ||
| 269 | }; | ||
| 266 | struct path path; | 270 | struct path path; |
| 267 | } release; | 271 | } release; |
| 268 | struct fuse_init_in init_in; | 272 | struct fuse_init_in init_in; |
