Commit baebccbe997d5023289e0fc9b4d0d71c6fc17a79

Authored by Miklos Szeredi
1 parent 580640ba5d

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>

Showing 2 changed files with 7 additions and 39 deletions Side-by-side Diff

... ... @@ -89,37 +89,9 @@
89 89 return ff;
90 90 }
91 91  
92   -static 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   -
106 92 static void fuse_release_end(struct fuse_conn *fc, struct fuse_req *req)
107 93 {
108   - if (fc->destroy_req) {
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   - }
  94 + iput(req->misc.release.inode);
123 95 }
124 96  
125 97 static void fuse_file_put(struct fuse_file *ff, bool sync)
126 98  
... ... @@ -133,12 +105,12 @@
133 105 * implement 'open'
134 106 */
135 107 req->background = 0;
136   - path_put(&req->misc.release.path);
  108 + iput(req->misc.release.inode);
137 109 fuse_put_request(ff->fc, req);
138 110 } else if (sync) {
139 111 req->background = 0;
140 112 fuse_request_send(ff->fc, req);
141   - path_put(&req->misc.release.path);
  113 + iput(req->misc.release.inode);
142 114 fuse_put_request(ff->fc, req);
143 115 } else {
144 116 req->end = fuse_release_end;
... ... @@ -297,9 +269,8 @@
297 269 inarg->lock_owner = fuse_lock_owner_id(ff->fc,
298 270 (fl_owner_t) file);
299 271 }
300   - /* Hold vfsmount and dentry until release is finished */
301   - path_get(&file->f_path);
302   - req->misc.release.path = file->f_path;
  272 + /* Hold inode until release is finished */
  273 + req->misc.release.inode = igrab(file_inode(file));
303 274  
304 275 /*
305 276 * Normally this will send the RELEASE request, however if
... ... @@ -305,11 +305,8 @@
305 305 /** Data for asynchronous requests */
306 306 union {
307 307 struct {
308   - union {
309   - struct fuse_release_in in;
310   - struct work_struct work;
311   - };
312   - struct path path;
  308 + struct fuse_release_in in;
  309 + struct inode *inode;
313 310 } release;
314 311 struct fuse_init_in init_in;
315 312 struct fuse_init_out init_out;