Merge branch 'for-linus' of master.kernel.org:/pub/scm/linux/kernel/git/mszeredi/fuse

* 'for-linus' of master.kernel.org:/pub/scm/linux/kernel/git/mszeredi/fuse:
  [fuse] Direct I/O  should not use fuse_reset_request
  [fuse] Don't init request twice
  [fuse] Fix accounting the number of waiting requests
  [fuse] fix deadlock between fuse_put_super() and request_end()
This commit is contained in:
Linus Torvalds 2006-04-14 09:11:34 -07:00
commit 9a7e9f1c60
4 changed files with 65 additions and 41 deletions

View File

@ -92,48 +92,50 @@ struct fuse_req *fuse_get_req(struct fuse_conn *fc)
{ {
struct fuse_req *req; struct fuse_req *req;
sigset_t oldset; sigset_t oldset;
int intr;
int err; int err;
atomic_inc(&fc->num_waiting);
block_sigs(&oldset); block_sigs(&oldset);
err = wait_event_interruptible(fc->blocked_waitq, !fc->blocked); intr = wait_event_interruptible(fc->blocked_waitq, !fc->blocked);
restore_sigs(&oldset); restore_sigs(&oldset);
if (err) err = -EINTR;
return ERR_PTR(-EINTR); if (intr)
goto out;
req = fuse_request_alloc(); req = fuse_request_alloc();
err = -ENOMEM;
if (!req) if (!req)
return ERR_PTR(-ENOMEM); goto out;
atomic_inc(&fc->num_waiting);
fuse_request_init(req);
req->in.h.uid = current->fsuid; req->in.h.uid = current->fsuid;
req->in.h.gid = current->fsgid; req->in.h.gid = current->fsgid;
req->in.h.pid = current->pid; req->in.h.pid = current->pid;
req->waiting = 1;
return req; return req;
out:
atomic_dec(&fc->num_waiting);
return ERR_PTR(err);
} }
void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req) void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req)
{ {
if (atomic_dec_and_test(&req->count)) { if (atomic_dec_and_test(&req->count)) {
atomic_dec(&fc->num_waiting); if (req->waiting)
atomic_dec(&fc->num_waiting);
fuse_request_free(req); fuse_request_free(req);
} }
} }
void fuse_release_background(struct fuse_conn *fc, struct fuse_req *req) void fuse_remove_background(struct fuse_conn *fc, struct fuse_req *req)
{ {
iput(req->inode); list_del_init(&req->bg_entry);
iput(req->inode2);
if (req->file)
fput(req->file);
spin_lock(&fc->lock);
list_del(&req->bg_entry);
if (fc->num_background == FUSE_MAX_BACKGROUND) { if (fc->num_background == FUSE_MAX_BACKGROUND) {
fc->blocked = 0; fc->blocked = 0;
wake_up_all(&fc->blocked_waitq); wake_up_all(&fc->blocked_waitq);
} }
fc->num_background--; fc->num_background--;
spin_unlock(&fc->lock);
} }
/* /*
@ -163,17 +165,27 @@ static void request_end(struct fuse_conn *fc, struct fuse_req *req)
wake_up(&req->waitq); wake_up(&req->waitq);
fuse_put_request(fc, req); fuse_put_request(fc, req);
} else { } else {
struct inode *inode = req->inode;
struct inode *inode2 = req->inode2;
struct file *file = req->file;
void (*end) (struct fuse_conn *, struct fuse_req *) = req->end; void (*end) (struct fuse_conn *, struct fuse_req *) = req->end;
req->end = NULL; req->end = NULL;
req->inode = NULL;
req->inode2 = NULL;
req->file = NULL;
if (!list_empty(&req->bg_entry))
fuse_remove_background(fc, req);
spin_unlock(&fc->lock); spin_unlock(&fc->lock);
down_read(&fc->sbput_sem);
if (fc->mounted)
fuse_release_background(fc, req);
up_read(&fc->sbput_sem);
if (end) if (end)
end(fc, req); end(fc, req);
else else
fuse_put_request(fc, req); fuse_put_request(fc, req);
if (file)
fput(file);
iput(inode);
iput(inode2);
} }
} }
@ -277,6 +289,10 @@ static void queue_request(struct fuse_conn *fc, struct fuse_req *req)
len_args(req->in.numargs, (struct fuse_arg *) req->in.args); len_args(req->in.numargs, (struct fuse_arg *) req->in.args);
list_add_tail(&req->list, &fc->pending); list_add_tail(&req->list, &fc->pending);
req->state = FUSE_REQ_PENDING; req->state = FUSE_REQ_PENDING;
if (!req->waiting) {
req->waiting = 1;
atomic_inc(&fc->num_waiting);
}
wake_up(&fc->waitq); wake_up(&fc->waitq);
kill_fasync(&fc->fasync, SIGIO, POLL_IN); kill_fasync(&fc->fasync, SIGIO, POLL_IN);
} }

View File

@ -1,6 +1,6 @@
/* /*
FUSE: Filesystem in Userspace FUSE: Filesystem in Userspace
Copyright (C) 2001-2005 Miklos Szeredi <miklos@szeredi.hu> Copyright (C) 2001-2006 Miklos Szeredi <miklos@szeredi.hu>
This program can be distributed under the terms of the GNU GPL. This program can be distributed under the terms of the GNU GPL.
See the file COPYING. See the file COPYING.
@ -565,8 +565,12 @@ static ssize_t fuse_direct_io(struct file *file, const char __user *buf,
buf += nres; buf += nres;
if (nres != nbytes) if (nres != nbytes)
break; break;
if (count) if (count) {
fuse_reset_request(req); fuse_put_request(fc, req);
req = fuse_get_req(fc);
if (IS_ERR(req))
break;
}
} }
fuse_put_request(fc, req); fuse_put_request(fc, req);
if (res > 0) { if (res > 0) {

View File

@ -159,6 +159,9 @@ struct fuse_req {
/** Data is being copied to/from the request */ /** Data is being copied to/from the request */
unsigned locked:1; unsigned locked:1;
/** Request is counted as "waiting" */
unsigned waiting:1;
/** State of the request */ /** State of the request */
enum fuse_req_state state; enum fuse_req_state state;
@ -255,15 +258,9 @@ struct fuse_conn {
/** waitq for blocked connection */ /** waitq for blocked connection */
wait_queue_head_t blocked_waitq; wait_queue_head_t blocked_waitq;
/** RW semaphore for exclusion with fuse_put_super() */
struct rw_semaphore sbput_sem;
/** The next unique request id */ /** The next unique request id */
u64 reqctr; u64 reqctr;
/** Mount is active */
unsigned mounted;
/** Connection established, cleared on umount, connection /** Connection established, cleared on umount, connection
abort and device release */ abort and device release */
unsigned connected; unsigned connected;
@ -474,11 +471,11 @@ void request_send_noreply(struct fuse_conn *fc, struct fuse_req *req);
void request_send_background(struct fuse_conn *fc, struct fuse_req *req); void request_send_background(struct fuse_conn *fc, struct fuse_req *req);
/** /**
* Release inodes and file associated with background request * Remove request from the the background list
*/ */
void fuse_release_background(struct fuse_conn *fc, struct fuse_req *req); void fuse_remove_background(struct fuse_conn *fc, struct fuse_req *req);
/* Abort all requests */ /** Abort all requests */
void fuse_abort_conn(struct fuse_conn *fc); void fuse_abort_conn(struct fuse_conn *fc);
/** /**

View File

@ -204,17 +204,26 @@ static void fuse_put_super(struct super_block *sb)
{ {
struct fuse_conn *fc = get_fuse_conn_super(sb); struct fuse_conn *fc = get_fuse_conn_super(sb);
down_write(&fc->sbput_sem);
while (!list_empty(&fc->background))
fuse_release_background(fc,
list_entry(fc->background.next,
struct fuse_req, bg_entry));
spin_lock(&fc->lock); spin_lock(&fc->lock);
fc->mounted = 0;
fc->connected = 0; fc->connected = 0;
while (!list_empty(&fc->background)) {
struct fuse_req *req = list_entry(fc->background.next,
struct fuse_req, bg_entry);
struct inode *inode = req->inode;
struct inode *inode2 = req->inode2;
/* File would hold a reference to vfsmount */
BUG_ON(req->file);
req->inode = NULL;
req->inode2 = NULL;
fuse_remove_background(fc, req);
spin_unlock(&fc->lock);
iput(inode);
iput(inode2);
spin_lock(&fc->lock);
}
spin_unlock(&fc->lock); spin_unlock(&fc->lock);
up_write(&fc->sbput_sem);
/* Flush all readers on this fs */ /* Flush all readers on this fs */
kill_fasync(&fc->fasync, SIGIO, POLL_IN); kill_fasync(&fc->fasync, SIGIO, POLL_IN);
wake_up_all(&fc->waitq); wake_up_all(&fc->waitq);
@ -386,7 +395,6 @@ static struct fuse_conn *new_conn(void)
INIT_LIST_HEAD(&fc->processing); INIT_LIST_HEAD(&fc->processing);
INIT_LIST_HEAD(&fc->io); INIT_LIST_HEAD(&fc->io);
INIT_LIST_HEAD(&fc->background); INIT_LIST_HEAD(&fc->background);
init_rwsem(&fc->sbput_sem);
kobj_set_kset_s(fc, connections_subsys); kobj_set_kset_s(fc, connections_subsys);
kobject_init(&fc->kobj); kobject_init(&fc->kobj);
atomic_set(&fc->num_waiting, 0); atomic_set(&fc->num_waiting, 0);
@ -541,7 +549,6 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
goto err_free_req; goto err_free_req;
sb->s_root = root_dentry; sb->s_root = root_dentry;
fc->mounted = 1;
fc->connected = 1; fc->connected = 1;
kobject_get(&fc->kobj); kobject_get(&fc->kobj);
file->private_data = fc; file->private_data = fc;