aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorMiklos Szeredi <mszeredi@suse.cz>2014-12-12 03:49:04 -0500
committerMiklos Szeredi <mszeredi@suse.cz>2014-12-12 03:49:04 -0500
commitbaebccbe997d5023289e0fc9b4d0d71c6fc17a79 (patch)
tree19cb0ed831569c140cd82c8cf5bac413e4c92803 /fs
parent580640ba5d331eb5631a5de46941c98f5ed90886 (diff)
fuse: hold inode instead of path after release
path_put() in release could trigger a DESTROY request in fuseblk. The possible deadlock was worked around by doing the path_put() with schedule_work(). This complexity isn't needed if we just hold the inode instead of the path. Since we now flush all requests before destroying the super block we can be sure that all held inodes will be dropped. Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
Diffstat (limited to 'fs')
-rw-r--r--fs/fuse/file.c39
-rw-r--r--fs/fuse/fuse_i.h7
2 files changed, 7 insertions, 39 deletions
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index caa8d95b24e8..2d4ae68943bb 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -89,37 +89,9 @@ struct fuse_file *fuse_file_get(struct fuse_file *ff)
89 return ff; 89 return ff;
90} 90}
91 91
92static void fuse_release_async(struct work_struct *work)
93{
94 struct fuse_req *req;
95 struct fuse_conn *fc;
96 struct path path;
97
98 req = container_of(work, struct fuse_req, misc.release.work);
99 path = req->misc.release.path;
100 fc = get_fuse_conn(path.dentry->d_inode);
101
102 fuse_put_request(fc, req);
103 path_put(&path);
104}
105
106static void fuse_release_end(struct fuse_conn *fc, struct fuse_req *req) 92static void fuse_release_end(struct fuse_conn *fc, struct fuse_req *req)
107{ 93{
108 if (fc->destroy_req) { 94 iput(req->misc.release.inode);
109 /*
110 * If this is a fuseblk mount, then it's possible that
111 * releasing the path will result in releasing the
112 * super block and sending the DESTROY request. If
113 * the server is single threaded, this would hang.
114 * For this reason do the path_put() in a separate
115 * thread.
116 */
117 atomic_inc(&req->count);
118 INIT_WORK(&req->misc.release.work, fuse_release_async);
119 schedule_work(&req->misc.release.work);
120 } else {
121 path_put(&req->misc.release.path);
122 }
123} 95}
124 96
125static void fuse_file_put(struct fuse_file *ff, bool sync) 97static void fuse_file_put(struct fuse_file *ff, bool sync)
@@ -133,12 +105,12 @@ static void fuse_file_put(struct fuse_file *ff, bool sync)
133 * implement 'open' 105 * implement 'open'
134 */ 106 */
135 req->background = 0; 107 req->background = 0;
136 path_put(&req->misc.release.path); 108 iput(req->misc.release.inode);
137 fuse_put_request(ff->fc, req); 109 fuse_put_request(ff->fc, req);
138 } else if (sync) { 110 } else if (sync) {
139 req->background = 0; 111 req->background = 0;
140 fuse_request_send(ff->fc, req); 112 fuse_request_send(ff->fc, req);
141 path_put(&req->misc.release.path); 113 iput(req->misc.release.inode);
142 fuse_put_request(ff->fc, req); 114 fuse_put_request(ff->fc, req);
143 } else { 115 } else {
144 req->end = fuse_release_end; 116 req->end = fuse_release_end;
@@ -297,9 +269,8 @@ void fuse_release_common(struct file *file, int opcode)
297 inarg->lock_owner = fuse_lock_owner_id(ff->fc, 269 inarg->lock_owner = fuse_lock_owner_id(ff->fc,
298 (fl_owner_t) file); 270 (fl_owner_t) file);
299 } 271 }
300 /* Hold vfsmount and dentry until release is finished */ 272 /* Hold inode until release is finished */
301 path_get(&file->f_path); 273 req->misc.release.inode = igrab(file_inode(file));
302 req->misc.release.path = file->f_path;
303 274
304 /* 275 /*
305 * Normally this will send the RELEASE request, however if 276 * Normally this will send the RELEASE request, however if
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index fbc63ab149b6..416fee2ee1bc 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -305,11 +305,8 @@ struct fuse_req {
305 /** Data for asynchronous requests */ 305 /** Data for asynchronous requests */
306 union { 306 union {
307 struct { 307 struct {
308 union { 308 struct fuse_release_in in;
309 struct fuse_release_in in; 309 struct inode *inode;
310 struct work_struct work;
311 };
312 struct path path;
313 } release; 310 } release;
314 struct fuse_init_in init_in; 311 struct fuse_init_in init_in;
315 struct fuse_init_out init_out; 312 struct fuse_init_out init_out;