aboutsummaryrefslogtreecommitdiff
path: root/kernel/dev.c
diff options
context:
space:
mode:
authorGravatar Miklos Szeredi <miklos@szeredi.hu>2005-01-04 12:45:54 +0000
committerGravatar Miklos Szeredi <miklos@szeredi.hu>2005-01-04 12:45:54 +0000
commit0f62d7265ebab7807978ae8a206213d968bea9e4 (patch)
tree5039e673540eb681b78849f665beb5efed2bcc54 /kernel/dev.c
parent0fcfa039c1dfb7cf9d9da132972334e33f320dd1 (diff)
interrupted request improvements
Diffstat (limited to 'kernel/dev.c')
-rw-r--r--kernel/dev.c149
1 files changed, 96 insertions, 53 deletions
diff --git a/kernel/dev.c b/kernel/dev.c
index 3f2d85a..bfd2bd7 100644
--- a/kernel/dev.c
+++ b/kernel/dev.c
@@ -55,15 +55,6 @@ void fuse_request_free(struct fuse_req *req)
kmem_cache_free(fuse_req_cachep, req);
}
-static int get_unique(struct fuse_conn *fc)
-{
- fc->reqctr++;
- /* zero is special */
- if (fc->reqctr == 0)
- fc->reqctr = 1;
- return fc->reqctr;
-}
-
#ifdef KERNEL_2_6
static inline void block_sigs(sigset_t *oldset)
{
@@ -112,6 +103,7 @@ static void __fuse_get_request(struct fuse_req *req)
/* Must be called with > 1 refcount */
static void __fuse_put_request(struct fuse_req *req)
{
+ BUG_ON(atomic_read(&req->count) < 2);
atomic_dec(&req->count);
}
@@ -134,7 +126,7 @@ static struct fuse_req *do_get_request(struct fuse_conn *fc)
struct fuse_req *fuse_get_request(struct fuse_conn *fc)
{
- if (down_interruptible(&fc->unused_sem))
+ if (down_interruptible(&fc->outstanding_sem))
return NULL;
return do_get_request(fc);
}
@@ -145,7 +137,7 @@ struct fuse_req *fuse_get_request_nonint(struct fuse_conn *fc)
sigset_t oldset;
block_sigs(&oldset);
- intr = down_interruptible(&fc->unused_sem);
+ intr = down_interruptible(&fc->outstanding_sem);
restore_sigs(&oldset);
return intr ? NULL : do_get_request(fc);
}
@@ -154,12 +146,16 @@ void fuse_putback_request(struct fuse_conn *fc, struct fuse_req *req)
{
if (!req->preallocated)
fuse_request_free(req);
- else {
- spin_lock(&fuse_lock);
+
+ spin_lock(&fuse_lock);
+ if (req->preallocated)
list_add(&req->list, &fc->unused_list);
- spin_unlock(&fuse_lock);
- up(&fc->unused_sem);
- }
+
+ if (fc->outstanding_debt)
+ fc->outstanding_debt--;
+ else
+ up(&fc->outstanding_sem);
+ spin_unlock(&fuse_lock);
}
void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req)
@@ -175,6 +171,14 @@ static void request_end(struct fuse_conn *fc, struct fuse_req *req)
req->finished = 1;
putback = atomic_dec_and_test(&req->count);
spin_unlock(&fuse_lock);
+ if (req->background) {
+ if (req->inode)
+ iput(req->inode);
+ if (req->inode2)
+ iput(req->inode2);
+ if (req->file)
+ fput(req->file);
+ }
wake_up(&req->waitq);
if (putback)
fuse_putback_request(fc, req);
@@ -190,9 +194,21 @@ static int request_wait_answer_nonint(struct fuse_req *req)
return err;
}
-/* Called with fuse_lock held. Releases, and then reaquires it. */
-static void request_wait_answer(struct fuse_req *req, int interruptible,
- int background)
+static void background_request(struct fuse_req *req)
+{
+ /* Need to get hold of the inode(s) and/or file used in the
+ request, so FORGET and RELEASE are not sent too early */
+ req->background = 1;
+ if (req->inode)
+ req->inode = igrab(req->inode);
+ if (req->inode2)
+ req->inode2 = igrab(req->inode2);
+ if (req->file)
+ get_file(req->file);
+}
+
+/* Called with fuse_lock held. Releases, and then reacquires it. */
+static void request_wait_answer(struct fuse_req *req, int interruptible)
{
int intr;
@@ -212,10 +228,6 @@ static void request_wait_answer(struct fuse_req *req, int interruptible,
if (!intr)
return;
- if (background && !req->sent) {
- req->isreply = 0;
- return;
- }
if (!interruptible || req->sent)
req->out.h.error = -EINTR;
else
@@ -232,50 +244,74 @@ static void request_wait_answer(struct fuse_req *req, int interruptible,
wait_event(req->waitq, !req->locked);
spin_lock(&fuse_lock);
}
- if (!list_empty(&req->list)) {
- /* request is still on one of the lists */
+ if (!req->sent && !list_empty(&req->list)) {
list_del(&req->list);
__fuse_put_request(req);
+ } else if (req->sent)
+ background_request(req);
+}
+
+static unsigned len_args(unsigned numargs, struct fuse_arg *args)
+{
+ unsigned nbytes = 0;
+ unsigned i;
+
+ for (i = 0; i < numargs; i++)
+ nbytes += args[i].size;
+
+ return nbytes;
+}
+
+static void queue_request(struct fuse_conn *fc, struct fuse_req *req)
+{
+ fc->reqctr++;
+ /* zero is special */
+ if (fc->reqctr == 0)
+ fc->reqctr = 1;
+ req->in.h.unique = fc->reqctr;
+ req->in.h.len = sizeof(struct fuse_in_header) +
+ len_args(req->in.numargs, (struct fuse_arg *) req->in.args);
+ if (!req->preallocated) {
+ /* decrease outstanding_sem, but without blocking... */
+ if (down_trylock(&fc->outstanding_sem))
+ fc->outstanding_debt++;
}
+ list_add_tail(&req->list, &fc->pending);
+ wake_up(&fc->waitq);
}
static void request_send_wait(struct fuse_conn *fc, struct fuse_req *req,
- int interruptible, int background)
+ int interruptible)
{
req->isreply = 1;
spin_lock(&fuse_lock);
req->out.h.error = -ENOTCONN;
if (fc->file) {
- req->in.h.unique = get_unique(fc);
- list_add_tail(&req->list, &fc->pending);
- wake_up(&fc->waitq);
+ queue_request(fc, req);
/* acquire extra reference, since request is still needed
after request_end() */
__fuse_get_request(req);
- request_wait_answer(req, interruptible, background);
+ request_wait_answer(req, interruptible);
}
spin_unlock(&fuse_lock);
}
void request_send(struct fuse_conn *fc, struct fuse_req *req)
{
- request_send_wait(fc, req, 1, 0);
+ request_send_wait(fc, req, 1);
}
-void request_send_nonint(struct fuse_conn *fc, struct fuse_req *req,
- int background)
+void request_send_nonint(struct fuse_conn *fc, struct fuse_req *req)
{
- request_send_wait(fc, req, 0, background);
+ request_send_wait(fc, req, 0);
}
-void request_send_noreply(struct fuse_conn *fc, struct fuse_req *req)
+void request_send_nowait(struct fuse_conn *fc, struct fuse_req *req)
{
- req->isreply = 0;
spin_lock(&fuse_lock);
if (fc->file) {
- list_add_tail(&req->list, &fc->pending);
- wake_up(&fc->waitq);
+ queue_request(fc, req);
spin_unlock(&fuse_lock);
} else {
req->out.h.error = -ENOTCONN;
@@ -283,6 +319,19 @@ void request_send_noreply(struct fuse_conn *fc, struct fuse_req *req)
}
}
+void request_send_noreply(struct fuse_conn *fc, struct fuse_req *req)
+{
+ req->isreply = 0;
+ request_send_nowait(fc, req);
+}
+
+void request_send_background(struct fuse_conn *fc, struct fuse_req *req)
+{
+ req->isreply = 1;
+ background_request(req);
+ request_send_nowait(fc, req);
+}
+
static inline int lock_request(struct fuse_req *req)
{
int err = 0;
@@ -486,17 +535,6 @@ static int fuse_copy_args(struct fuse_copy_state *cs, unsigned numargs,
return err;
}
-static unsigned len_args(unsigned numargs, struct fuse_arg *args)
-{
- unsigned nbytes = 0;
- unsigned i;
-
- for (i = 0; i < numargs; i++)
- nbytes += args[i].size;
-
- return nbytes;
-}
-
static void request_wait(struct fuse_conn *fc)
{
DECLARE_WAITQUEUE(wait, current);
@@ -544,8 +582,7 @@ static ssize_t fuse_dev_readv(struct file *file, const struct iovec *iov,
spin_unlock(&fuse_lock);
in = &req->in;
- reqsize = sizeof(struct fuse_in_header);
- reqsize += len_args(in->numargs, (struct fuse_arg *) in->args);
+ reqsize = req->in.h.len;
nbytes = fuse_copy_init(&cs, 1, req, iov, nr_segs);
err = -EINVAL;
if (nbytes >= reqsize) {
@@ -652,16 +689,22 @@ static ssize_t fuse_dev_writev(struct file *file, const struct iovec *iov,
if (err)
goto err_finish;
err = -EINVAL;
- if (!oh.unique || oh.error <= -1000 || oh.error > 0)
+ if (!oh.unique || oh.error <= -1000 || oh.error > 0 ||
+ oh.len != nbytes)
goto err_finish;
spin_lock(&fuse_lock);
req = request_find(fc, oh.unique);
- err = -ENOENT;
+ err = -EINVAL;
if (!req)
goto err_unlock;
list_del_init(&req->list);
+ if (req->interrupted) {
+ request_end(fc, req);
+ fuse_copy_finish(&cs);
+ return -ENOENT;
+ }
req->out.h = oh;
req->locked = 1;
cs.req = req;