diff options
author | Miklos Szeredi <miklos@szeredi.hu> | 2005-09-09 16:10:39 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2005-09-09 17:03:48 -0400 |
commit | 7c352bdf048811b8128019ffc1e886161e09c11c (patch) | |
tree | 74c48298b67d37295433d9b2bb08d590a5f97a78 /fs/fuse/dev.c | |
parent | 8254798199332966e2ab647380c990193af7e854 (diff) |
[PATCH] FUSE: don't allow restarting of system calls
This patch removes ability to interrupt and restart operations while there
hasn't been any side-effect.
The reason: applications. There are some apps it seems that generate
signals at a fast rate. This means, that if the operation cannot make
enough progress between two signals, it will be restarted for ever. This
bug actually manifested itself with 'krusader' trying to open a file for
writing under sshfs. Thanks to Eduard Czimbalmos for the report.
The problem can be solved just by making open() uninterruptible, because in
this case it was the truncate operation that slowed down the progress. But
it's better to solve this by simply not allowing interrupts at all (except
SIGKILL), because applications don't expect file operations to be
interruptible anyway. As an added bonus the code is simplified somewhat.
Signed-off-by: Miklos Szeredi <miklos@szeredi.hu>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'fs/fuse/dev.c')
-rw-r--r-- | fs/fuse/dev.c | 73 |
1 files changed, 13 insertions, 60 deletions
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index e4ada021d087..d4c869c6d01b 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c | |||
@@ -103,20 +103,9 @@ static struct fuse_req *do_get_request(struct fuse_conn *fc) | |||
103 | return req; | 103 | return req; |
104 | } | 104 | } |
105 | 105 | ||
106 | /* This can return NULL, but only in case it's interrupted by a SIGKILL */ | ||
106 | struct fuse_req *fuse_get_request(struct fuse_conn *fc) | 107 | struct fuse_req *fuse_get_request(struct fuse_conn *fc) |
107 | { | 108 | { |
108 | if (down_interruptible(&fc->outstanding_sem)) | ||
109 | return NULL; | ||
110 | return do_get_request(fc); | ||
111 | } | ||
112 | |||
113 | /* | ||
114 | * Non-interruptible version of the above function is for operations | ||
115 | * which can't legally return -ERESTART{SYS,NOINTR}. This can still | ||
116 | * return NULL, but only in case the signal is SIGKILL. | ||
117 | */ | ||
118 | struct fuse_req *fuse_get_request_nonint(struct fuse_conn *fc) | ||
119 | { | ||
120 | int intr; | 109 | int intr; |
121 | sigset_t oldset; | 110 | sigset_t oldset; |
122 | 111 | ||
@@ -241,43 +230,20 @@ static void background_request(struct fuse_conn *fc, struct fuse_req *req) | |||
241 | get_file(req->file); | 230 | get_file(req->file); |
242 | } | 231 | } |
243 | 232 | ||
244 | static int request_wait_answer_nonint(struct fuse_req *req) | ||
245 | { | ||
246 | int err; | ||
247 | sigset_t oldset; | ||
248 | block_sigs(&oldset); | ||
249 | err = wait_event_interruptible(req->waitq, req->finished); | ||
250 | restore_sigs(&oldset); | ||
251 | return err; | ||
252 | } | ||
253 | |||
254 | /* Called with fuse_lock held. Releases, and then reacquires it. */ | 233 | /* Called with fuse_lock held. Releases, and then reacquires it. */ |
255 | static void request_wait_answer(struct fuse_conn *fc, struct fuse_req *req, | 234 | static void request_wait_answer(struct fuse_conn *fc, struct fuse_req *req) |
256 | int interruptible) | ||
257 | { | 235 | { |
258 | int intr; | 236 | sigset_t oldset; |
259 | 237 | ||
260 | spin_unlock(&fuse_lock); | 238 | spin_unlock(&fuse_lock); |
261 | if (interruptible) | 239 | block_sigs(&oldset); |
262 | intr = wait_event_interruptible(req->waitq, req->finished); | 240 | wait_event_interruptible(req->waitq, req->finished); |
263 | else | 241 | restore_sigs(&oldset); |
264 | intr = request_wait_answer_nonint(req); | ||
265 | spin_lock(&fuse_lock); | 242 | spin_lock(&fuse_lock); |
266 | if (intr && interruptible && req->sent) { | 243 | if (req->finished) |
267 | /* If request is already in userspace, only allow KILL | ||
268 | signal to interrupt */ | ||
269 | spin_unlock(&fuse_lock); | ||
270 | intr = request_wait_answer_nonint(req); | ||
271 | spin_lock(&fuse_lock); | ||
272 | } | ||
273 | if (!intr) | ||
274 | return; | 244 | return; |
275 | 245 | ||
276 | if (!interruptible || req->sent) | 246 | req->out.h.error = -EINTR; |
277 | req->out.h.error = -EINTR; | ||
278 | else | ||
279 | req->out.h.error = -ERESTARTNOINTR; | ||
280 | |||
281 | req->interrupted = 1; | 247 | req->interrupted = 1; |
282 | if (req->locked) { | 248 | if (req->locked) { |
283 | /* This is uninterruptible sleep, because data is | 249 | /* This is uninterruptible sleep, because data is |
@@ -330,8 +296,10 @@ static void queue_request(struct fuse_conn *fc, struct fuse_req *req) | |||
330 | wake_up(&fc->waitq); | 296 | wake_up(&fc->waitq); |
331 | } | 297 | } |
332 | 298 | ||
333 | static void request_send_wait(struct fuse_conn *fc, struct fuse_req *req, | 299 | /* |
334 | int interruptible) | 300 | * This can only be interrupted by a SIGKILL |
301 | */ | ||
302 | void request_send(struct fuse_conn *fc, struct fuse_req *req) | ||
335 | { | 303 | { |
336 | req->isreply = 1; | 304 | req->isreply = 1; |
337 | spin_lock(&fuse_lock); | 305 | spin_lock(&fuse_lock); |
@@ -345,26 +313,11 @@ static void request_send_wait(struct fuse_conn *fc, struct fuse_req *req, | |||
345 | after request_end() */ | 313 | after request_end() */ |
346 | __fuse_get_request(req); | 314 | __fuse_get_request(req); |
347 | 315 | ||
348 | request_wait_answer(fc, req, interruptible); | 316 | request_wait_answer(fc, req); |
349 | } | 317 | } |
350 | spin_unlock(&fuse_lock); | 318 | spin_unlock(&fuse_lock); |
351 | } | 319 | } |
352 | 320 | ||
353 | void request_send(struct fuse_conn *fc, struct fuse_req *req) | ||
354 | { | ||
355 | request_send_wait(fc, req, 1); | ||
356 | } | ||
357 | |||
358 | /* | ||
359 | * Non-interruptible version of the above function is for operations | ||
360 | * which can't legally return -ERESTART{SYS,NOINTR}. This can still | ||
361 | * be interrupted but only with SIGKILL. | ||
362 | */ | ||
363 | void request_send_nonint(struct fuse_conn *fc, struct fuse_req *req) | ||
364 | { | ||
365 | request_send_wait(fc, req, 0); | ||
366 | } | ||
367 | |||
368 | static void request_send_nowait(struct fuse_conn *fc, struct fuse_req *req) | 321 | static void request_send_nowait(struct fuse_conn *fc, struct fuse_req *req) |
369 | { | 322 | { |
370 | spin_lock(&fuse_lock); | 323 | spin_lock(&fuse_lock); |