aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorMiklos Szeredi <miklos@szeredi.hu>2006-04-11 15:14:26 -0400
committerMiklos Szeredi <miklos@szeredi.hu>2006-04-11 15:14:26 -0400
commit73ce8355c243a434524a34c05cc417dd0467996e (patch)
treea5bc5bfb31c41a5806caf763533943e7411e6543 /fs
parent2514395ef88b46e895726a8d40966cb83de7940c (diff)
[fuse] fix deadlock between fuse_put_super() and request_end()
A deadlock was possible, when the last reference to the superblock was held due to a background request containing a file reference. Releasing the file would release the vfsmount which in turn would release the superblock. Since sbput_sem is held during the fput() and fuse_put_super() tries to acquire this same semaphore, a deadlock results. The chosen soltuion is to get rid of sbput_sem, and instead use the spinlock to ensure the referenced inodes/file are released only once. Since the actual release may sleep, defer these outside the locked region, but using local variables instead of the structure members. This is a much more rubust solution. Signed-off-by: Miklos Szeredi <miklos@szeredi.hu>
Diffstat (limited to 'fs')
-rw-r--r--fs/fuse/dev.c28
-rw-r--r--fs/fuse/fuse_i.h12
-rw-r--r--fs/fuse/inode.c27
3 files changed, 36 insertions, 31 deletions
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index 6c740f860665..d4efb6223e2c 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -120,20 +120,14 @@ void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req)
120 } 120 }
121} 121}
122 122
123void fuse_release_background(struct fuse_conn *fc, struct fuse_req *req) 123void fuse_remove_background(struct fuse_conn *fc, struct fuse_req *req)
124{ 124{
125 iput(req->inode); 125 list_del_init(&req->bg_entry);
126 iput(req->inode2);
127 if (req->file)
128 fput(req->file);
129 spin_lock(&fc->lock);
130 list_del(&req->bg_entry);
131 if (fc->num_background == FUSE_MAX_BACKGROUND) { 126 if (fc->num_background == FUSE_MAX_BACKGROUND) {
132 fc->blocked = 0; 127 fc->blocked = 0;
133 wake_up_all(&fc->blocked_waitq); 128 wake_up_all(&fc->blocked_waitq);
134 } 129 }
135 fc->num_background--; 130 fc->num_background--;
136 spin_unlock(&fc->lock);
137} 131}
138 132
139/* 133/*
@@ -163,17 +157,27 @@ static void request_end(struct fuse_conn *fc, struct fuse_req *req)
163 wake_up(&req->waitq); 157 wake_up(&req->waitq);
164 fuse_put_request(fc, req); 158 fuse_put_request(fc, req);
165 } else { 159 } else {
160 struct inode *inode = req->inode;
161 struct inode *inode2 = req->inode2;
162 struct file *file = req->file;
166 void (*end) (struct fuse_conn *, struct fuse_req *) = req->end; 163 void (*end) (struct fuse_conn *, struct fuse_req *) = req->end;
167 req->end = NULL; 164 req->end = NULL;
165 req->inode = NULL;
166 req->inode2 = NULL;
167 req->file = NULL;
168 if (!list_empty(&req->bg_entry))
169 fuse_remove_background(fc, req);
168 spin_unlock(&fc->lock); 170 spin_unlock(&fc->lock);
169 down_read(&fc->sbput_sem); 171
170 if (fc->mounted)
171 fuse_release_background(fc, req);
172 up_read(&fc->sbput_sem);
173 if (end) 172 if (end)
174 end(fc, req); 173 end(fc, req);
175 else 174 else
176 fuse_put_request(fc, req); 175 fuse_put_request(fc, req);
176
177 if (file)
178 fput(file);
179 iput(inode);
180 iput(inode2);
177 } 181 }
178} 182}
179 183
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 19c7185a7546..ee9b83042510 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -255,15 +255,9 @@ struct fuse_conn {
255 /** waitq for blocked connection */ 255 /** waitq for blocked connection */
256 wait_queue_head_t blocked_waitq; 256 wait_queue_head_t blocked_waitq;
257 257
258 /** RW semaphore for exclusion with fuse_put_super() */
259 struct rw_semaphore sbput_sem;
260
261 /** The next unique request id */ 258 /** The next unique request id */
262 u64 reqctr; 259 u64 reqctr;
263 260
264 /** Mount is active */
265 unsigned mounted;
266
267 /** Connection established, cleared on umount, connection 261 /** Connection established, cleared on umount, connection
268 abort and device release */ 262 abort and device release */
269 unsigned connected; 263 unsigned connected;
@@ -474,11 +468,11 @@ void request_send_noreply(struct fuse_conn *fc, struct fuse_req *req);
474void request_send_background(struct fuse_conn *fc, struct fuse_req *req); 468void request_send_background(struct fuse_conn *fc, struct fuse_req *req);
475 469
476/** 470/**
477 * Release inodes and file associated with background request 471 * Remove request from the the background list
478 */ 472 */
479void fuse_release_background(struct fuse_conn *fc, struct fuse_req *req); 473void fuse_remove_background(struct fuse_conn *fc, struct fuse_req *req);
480 474
481/* Abort all requests */ 475/** Abort all requests */
482void fuse_abort_conn(struct fuse_conn *fc); 476void fuse_abort_conn(struct fuse_conn *fc);
483 477
484/** 478/**
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index fd34037b0588..43a6fc0db8a7 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -204,17 +204,26 @@ static void fuse_put_super(struct super_block *sb)
204{ 204{
205 struct fuse_conn *fc = get_fuse_conn_super(sb); 205 struct fuse_conn *fc = get_fuse_conn_super(sb);
206 206
207 down_write(&fc->sbput_sem);
208 while (!list_empty(&fc->background))
209 fuse_release_background(fc,
210 list_entry(fc->background.next,
211 struct fuse_req, bg_entry));
212
213 spin_lock(&fc->lock); 207 spin_lock(&fc->lock);
214 fc->mounted = 0;
215 fc->connected = 0; 208 fc->connected = 0;
209 while (!list_empty(&fc->background)) {
210 struct fuse_req *req = list_entry(fc->background.next,
211 struct fuse_req, bg_entry);
212 struct inode *inode = req->inode;
213 struct inode *inode2 = req->inode2;
214
215 /* File would hold a reference to vfsmount */
216 BUG_ON(req->file);
217 req->inode = NULL;
218 req->inode2 = NULL;
219 fuse_remove_background(fc, req);
220
221 spin_unlock(&fc->lock);
222 iput(inode);
223 iput(inode2);
224 spin_lock(&fc->lock);
225 }
216 spin_unlock(&fc->lock); 226 spin_unlock(&fc->lock);
217 up_write(&fc->sbput_sem);
218 /* Flush all readers on this fs */ 227 /* Flush all readers on this fs */
219 kill_fasync(&fc->fasync, SIGIO, POLL_IN); 228 kill_fasync(&fc->fasync, SIGIO, POLL_IN);
220 wake_up_all(&fc->waitq); 229 wake_up_all(&fc->waitq);
@@ -386,7 +395,6 @@ static struct fuse_conn *new_conn(void)
386 INIT_LIST_HEAD(&fc->processing); 395 INIT_LIST_HEAD(&fc->processing);
387 INIT_LIST_HEAD(&fc->io); 396 INIT_LIST_HEAD(&fc->io);
388 INIT_LIST_HEAD(&fc->background); 397 INIT_LIST_HEAD(&fc->background);
389 init_rwsem(&fc->sbput_sem);
390 kobj_set_kset_s(fc, connections_subsys); 398 kobj_set_kset_s(fc, connections_subsys);
391 kobject_init(&fc->kobj); 399 kobject_init(&fc->kobj);
392 atomic_set(&fc->num_waiting, 0); 400 atomic_set(&fc->num_waiting, 0);
@@ -541,7 +549,6 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
541 goto err_free_req; 549 goto err_free_req;
542 550
543 sb->s_root = root_dentry; 551 sb->s_root = root_dentry;
544 fc->mounted = 1;
545 fc->connected = 1; 552 fc->connected = 1;
546 kobject_get(&fc->kobj); 553 kobject_get(&fc->kobj);
547 file->private_data = fc; 554 file->private_data = fc;