aboutsummaryrefslogtreecommitdiff
path: root/kernel/dev.c
diff options
context:
space:
mode:
authorGravatar Miklos Szeredi <miklos@szeredi.hu>2004-12-13 15:22:28 +0000
committerGravatar Miklos Szeredi <miklos@szeredi.hu>2004-12-13 15:22:28 +0000
commit0fcfa039c1dfb7cf9d9da132972334e33f320dd1 (patch)
treea825d1176d07fe68074cbd835c4934ba67cb72ea /kernel/dev.c
parente56818b231a231a48e2616ec083999a14516a4ae (diff)
fix
Diffstat (limited to 'kernel/dev.c')
-rw-r--r--kernel/dev.c18
1 files changed, 16 insertions, 2 deletions
diff --git a/kernel/dev.c b/kernel/dev.c
index b2a1b90..3f2d85a 100644
--- a/kernel/dev.c
+++ b/kernel/dev.c
@@ -216,7 +216,11 @@ static void request_wait_answer(struct fuse_req *req, int interruptible,
req->isreply = 0;
return;
}
- req->out.h.error = -ERESTARTNOINTR;
+ if (!interruptible || req->sent)
+ req->out.h.error = -EINTR;
+ else
+ req->out.h.error = -ERESTARTNOINTR;
+
req->interrupted = 1;
if (req->locked) {
/* This is uninterruptible sleep, because data is
@@ -304,6 +308,16 @@ static inline void unlock_request(struct fuse_req *req)
}
}
+
+/* Why all this complex one-page-at-a-time copying needed instead of
+ just copy_to/from_user()? The reason is that blocking on a page
+ fault must be avoided while the request is locked. This is because
+ if servicing that pagefault happens to be done by this filesystem,
+ an unbreakable deadlock can occur. So the code is careful to allow
+ request interruption during get_user_pages(), and only lock the
+ request while doing kmapped copying, which cannot block.
+ */
+
struct fuse_copy_state {
int write;
struct fuse_req *req;
@@ -521,7 +535,7 @@ static ssize_t fuse_dev_readv(struct file *file, const struct iovec *iov,
err = -ENODEV;
if (!fc->sb)
goto err_unlock;
- err = -EINTR;
+ err = -ERESTARTSYS;
if (list_empty(&fc->pending))
goto err_unlock;