+2006-04-11 Csaba Henk <csaba.henk@creo.hu>
+
+ * kernel: remove request pool, instead allocate requests on
+ demand. Account the number of background requests, and if they go
+ over a limit, block the allocation of new requests.
+
+ * kernel: fix deadlock if backgrounded request holds the last
+ reference to the super block
+
+ * kernel: don't use fuse_reset_request() during direct I/O
+
2006-04-06 Csaba Henk <csaba.henk@creo.hu>
* lib: Let FreeBSD mount option parsing routine recognize "no"
*/
void fuse_reset_request(struct fuse_req *req)
{
- int preallocated = req->preallocated;
BUG_ON(atomic_read(&req->count) != 1);
fuse_request_init(req);
- req->preallocated = preallocated;
}
static void __fuse_get_request(struct fuse_req *req)
atomic_dec(&req->count);
}
-static struct fuse_req *do_get_request(struct fuse_conn *fc)
+struct fuse_req *fuse_get_req(struct fuse_conn *fc)
{
struct fuse_req *req;
-
- spin_lock(&fc->lock);
- BUG_ON(list_empty(&fc->unused_list));
- req = list_entry(fc->unused_list.next, struct fuse_req, list);
- list_del_init(&req->list);
- spin_unlock(&fc->lock);
- fuse_request_init(req);
- req->preallocated = 1;
- req->in.h.uid = current->fsuid;
- req->in.h.gid = current->fsgid;
- req->in.h.pid = current->pid;
- return req;
-}
-
-/* This can return NULL, but only in case it's interrupted by a SIGKILL */
-struct fuse_req *fuse_get_request(struct fuse_conn *fc)
-{
- int intr;
sigset_t oldset;
+ int intr;
+ int err;
atomic_inc(&fc->num_waiting);
block_sigs(&oldset);
- intr = down_interruptible(&fc->outstanding_sem);
+ intr = wait_event_interruptible(fc->blocked_waitq, !fc->blocked);
restore_sigs(&oldset);
- if (intr) {
- atomic_dec(&fc->num_waiting);
- return NULL;
- }
- return do_get_request(fc);
-}
+ err = -EINTR;
+ if (intr)
+ goto out;
-/* Must be called with fc->lock held */
-static void fuse_putback_request(struct fuse_conn *fc, struct fuse_req *req)
-{
- if (req->preallocated) {
- atomic_dec(&fc->num_waiting);
- list_add(&req->list, &fc->unused_list);
- } else
- fuse_request_free(req);
+ req = fuse_request_alloc();
+ err = -ENOMEM;
+ if (!req)
+ goto out;
- /* If we are in debt decrease that first */
- if (fc->outstanding_debt)
- fc->outstanding_debt--;
- else
- up(&fc->outstanding_sem);
+ req->in.h.uid = current->fsuid;
+ req->in.h.gid = current->fsgid;
+ req->in.h.pid = current->pid;
+ req->waiting = 1;
+ return req;
+
+ out:
+ atomic_dec(&fc->num_waiting);
+ return ERR_PTR(err);
}
void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req)
{
if (atomic_dec_and_test(&req->count)) {
- spin_lock(&fc->lock);
- fuse_putback_request(fc, req);
- spin_unlock(&fc->lock);
+ if (req->waiting)
+ atomic_dec(&fc->num_waiting);
+ fuse_request_free(req);
}
}
-static void fuse_put_request_locked(struct fuse_conn *fc, struct fuse_req *req)
+void fuse_remove_background(struct fuse_conn *fc, struct fuse_req *req)
{
- if (atomic_dec_and_test(&req->count))
- fuse_putback_request(fc, req);
-}
-
-void fuse_release_background(struct fuse_conn *fc, struct fuse_req *req)
-{
- iput(req->inode);
- iput(req->inode2);
- if (req->file)
- fput(req->file);
- spin_lock(&fc->lock);
- list_del(&req->bg_entry);
- spin_unlock(&fc->lock);
+ list_del_init(&req->bg_entry);
+ if (fc->num_background == FUSE_MAX_BACKGROUND) {
+ fc->blocked = 0;
+ wake_up_all(&fc->blocked_waitq);
+ }
+ fc->num_background--;
}
/*
list_del(&req->list);
req->state = FUSE_REQ_FINISHED;
if (!req->background) {
- wake_up(&req->waitq);
- fuse_put_request_locked(fc, req);
spin_unlock(&fc->lock);
+ wake_up(&req->waitq);
+ fuse_put_request(fc, req);
} 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;
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);
- down_read(&fc->sbput_sem);
- if (fc->mounted)
- fuse_release_background(fc, req);
- up_read(&fc->sbput_sem);
+
if (end)
end(fc, req);
else
fuse_put_request(fc, req);
+
+ if (file)
+ fput(file);
+ iput(inode);
+ iput(inode2);
}
}
{
req->background = 1;
list_add(&req->bg_entry, &fc->background);
+ fc->num_background++;
+ if (fc->num_background == FUSE_MAX_BACKGROUND)
+ fc->blocked = 1;
if (req->inode)
req->inode = igrab(req->inode);
if (req->inode2)
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) {
- /* If request is not preallocated (either FORGET or
- RELEASE), then still decrease outstanding_sem, so
- user can't open infinite number of files while not
- processing the RELEASE requests. However for
- efficiency do it without blocking, so if down()
- would block, just increase the debt instead */
- if (down_trylock(&fc->outstanding_sem))
- fc->outstanding_debt++;
- }
list_add_tail(&req->list, &fc->pending);
req->state = FUSE_REQ_PENDING;
+ if (!req->waiting) {
+ req->waiting = 1;
+ atomic_inc(&fc->num_waiting);
+ }
wake_up(&fc->waitq);
kill_fasync(&fc->fasync, SIGIO, POLL_IN);
}
static void request_send_nowait(struct fuse_conn *fc, struct fuse_req *req)
{
spin_lock(&fc->lock);
+ background_request(fc, req);
if (fc->connected) {
queue_request(fc, req);
spin_unlock(&fc->lock);
void request_send_background(struct fuse_conn *fc, struct fuse_req *req)
{
req->isreply = 1;
- spin_lock(&fc->lock);
- background_request(fc, req);
- spin_unlock(&fc->lock);
request_send_nowait(fc, req);
}
restart:
spin_lock(&fc->lock);
err = -EAGAIN;
- if((file->f_flags & O_NONBLOCK) && fc->connected &&
- list_empty(&fc->pending))
+ if ((file->f_flags & O_NONBLOCK) && fc->connected &&
+ list_empty(&fc->pending))
goto err_unlock;
request_wait(fc);
return 0;
fc = get_fuse_conn(inode);
- req = fuse_get_request(fc);
- if (!req)
+ req = fuse_get_req(fc);
+ if (IS_ERR(req))
return 0;
fuse_lookup_init(req, entry->d_parent->d_inode, entry, &outarg);
if (entry->d_name.len > FUSE_NAME_MAX)
return ERR_PTR(-ENAMETOOLONG);
- req = fuse_get_request(fc);
- if (!req)
- return ERR_PTR(-EINTR);
+ req = fuse_get_req(fc);
+ if (IS_ERR(req))
+ return ERR_PTR(PTR_ERR(req));
fuse_lookup_init(req, dir, entry, &outarg);
request_send(fc, req);
struct file *file;
int flags = nd->intent.open.flags - 1;
- err = -ENOSYS;
if (fc->no_create)
- goto out;
+ return -ENOSYS;
- err = -EINTR;
- req = fuse_get_request(fc);
- if (!req)
- goto out;
+ req = fuse_get_req(fc);
+ if (IS_ERR(req))
+ return PTR_ERR(req);
+ err = -ENOMEM;
ff = fuse_file_alloc();
if (!ff)
goto out_put_request;
fuse_file_free(ff);
out_put_request:
fuse_put_request(fc, req);
- out:
return err;
}
#endif
{
struct fuse_mknod_in inarg;
struct fuse_conn *fc = get_fuse_conn(dir);
- struct fuse_req *req = fuse_get_request(fc);
- if (!req)
- return -EINTR;
+ struct fuse_req *req = fuse_get_req(fc);
+ if (IS_ERR(req))
+ return PTR_ERR(req);
memset(&inarg, 0, sizeof(inarg));
inarg.mode = mode;
{
struct fuse_mkdir_in inarg;
struct fuse_conn *fc = get_fuse_conn(dir);
- struct fuse_req *req = fuse_get_request(fc);
- if (!req)
- return -EINTR;
+ struct fuse_req *req = fuse_get_req(fc);
+ if (IS_ERR(req))
+ return PTR_ERR(req);
memset(&inarg, 0, sizeof(inarg));
inarg.mode = mode;
{
struct fuse_conn *fc = get_fuse_conn(dir);
unsigned len = strlen(link) + 1;
- struct fuse_req *req = fuse_get_request(fc);
- if (!req)
- return -EINTR;
+ struct fuse_req *req = fuse_get_req(fc);
+ if (IS_ERR(req))
+ return PTR_ERR(req);
req->in.h.opcode = FUSE_SYMLINK;
req->in.numargs = 2;
{
int err;
struct fuse_conn *fc = get_fuse_conn(dir);
- struct fuse_req *req = fuse_get_request(fc);
- if (!req)
- return -EINTR;
+ struct fuse_req *req = fuse_get_req(fc);
+ if (IS_ERR(req))
+ return PTR_ERR(req);
req->in.h.opcode = FUSE_UNLINK;
req->in.h.nodeid = get_node_id(dir);
{
int err;
struct fuse_conn *fc = get_fuse_conn(dir);
- struct fuse_req *req = fuse_get_request(fc);
- if (!req)
- return -EINTR;
+ struct fuse_req *req = fuse_get_req(fc);
+ if (IS_ERR(req))
+ return PTR_ERR(req);
req->in.h.opcode = FUSE_RMDIR;
req->in.h.nodeid = get_node_id(dir);
int err;
struct fuse_rename_in inarg;
struct fuse_conn *fc = get_fuse_conn(olddir);
- struct fuse_req *req = fuse_get_request(fc);
- if (!req)
- return -EINTR;
+ struct fuse_req *req = fuse_get_req(fc);
+ if (IS_ERR(req))
+ return PTR_ERR(req);
memset(&inarg, 0, sizeof(inarg));
inarg.newdir = get_node_id(newdir);
struct fuse_link_in inarg;
struct inode *inode = entry->d_inode;
struct fuse_conn *fc = get_fuse_conn(inode);
- struct fuse_req *req = fuse_get_request(fc);
- if (!req)
- return -EINTR;
+ struct fuse_req *req = fuse_get_req(fc);
+ if (IS_ERR(req))
+ return PTR_ERR(req);
memset(&inarg, 0, sizeof(inarg));
inarg.oldnodeid = get_node_id(inode);
int err;
struct fuse_attr_out arg;
struct fuse_conn *fc = get_fuse_conn(inode);
- struct fuse_req *req = fuse_get_request(fc);
- if (!req)
- return -EINTR;
+ struct fuse_req *req = fuse_get_req(fc);
+ if (IS_ERR(req))
+ return PTR_ERR(req);
req->in.h.opcode = FUSE_GETATTR;
req->in.h.nodeid = get_node_id(inode);
if (fc->no_access)
return 0;
- req = fuse_get_request(fc);
- if (!req)
- return -EINTR;
+ req = fuse_get_req(fc);
+ if (IS_ERR(req))
+ return PTR_ERR(req);
memset(&inarg, 0, sizeof(inarg));
inarg.mask = mask;
if (is_bad_inode(inode))
return -EIO;
- req = fuse_get_request(fc);
- if (!req)
- return -EINTR;
+ req = fuse_get_req(fc);
+ if (IS_ERR(req))
+ return PTR_ERR(req);
page = alloc_page(GFP_KERNEL);
if (!page) {
{
struct inode *inode = dentry->d_inode;
struct fuse_conn *fc = get_fuse_conn(inode);
- struct fuse_req *req = fuse_get_request(fc);
+ struct fuse_req *req = fuse_get_req(fc);
char *link;
- if (!req)
- return ERR_PTR(-EINTR);
+ if (IS_ERR(req))
+ return ERR_PTR(PTR_ERR(req));
link = (char *) __get_free_page(GFP_KERNEL);
if (!link) {
}
}
- req = fuse_get_request(fc);
- if (!req)
- return -EINTR;
+ req = fuse_get_req(fc);
+ if (IS_ERR(req))
+ return PTR_ERR(req);
memset(&inarg, 0, sizeof(inarg));
iattr_to_fattr(attr, &inarg);
if (fc->no_setxattr)
return -EOPNOTSUPP;
- req = fuse_get_request(fc);
- if (!req)
- return -EINTR;
+ req = fuse_get_req(fc);
+ if (IS_ERR(req))
+ return PTR_ERR(req);
memset(&inarg, 0, sizeof(inarg));
inarg.size = size;
if (fc->no_getxattr)
return -EOPNOTSUPP;
- req = fuse_get_request(fc);
- if (!req)
- return -EINTR;
+ req = fuse_get_req(fc);
+ if (IS_ERR(req))
+ return PTR_ERR(req);
memset(&inarg, 0, sizeof(inarg));
inarg.size = size;
if (fc->no_listxattr)
return -EOPNOTSUPP;
- req = fuse_get_request(fc);
- if (!req)
- return -EINTR;
+ req = fuse_get_req(fc);
+ if (IS_ERR(req))
+ return PTR_ERR(req);
memset(&inarg, 0, sizeof(inarg));
inarg.size = size;
if (fc->no_removexattr)
return -EOPNOTSUPP;
- req = fuse_get_request(fc);
- if (!req)
- return -EINTR;
+ req = fuse_get_req(fc);
+ if (IS_ERR(req))
+ return PTR_ERR(req);
req->in.h.opcode = FUSE_REMOVEXATTR;
req->in.h.nodeid = get_node_id(inode);
struct fuse_req *req;
int err;
- req = fuse_get_request(fc);
- if (!req)
- return -EINTR;
+ req = fuse_get_req(fc);
+ if (IS_ERR(req))
+ return PTR_ERR(req);
memset(&inarg, 0, sizeof(inarg));
inarg.flags = file->f_flags & ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);
if (fc->no_flush)
return 0;
- req = fuse_get_request(fc);
- if (!req)
- return -EINTR;
+ req = fuse_get_req(fc);
+ if (IS_ERR(req))
+ return PTR_ERR(req);
memset(&inarg, 0, sizeof(inarg));
inarg.fh = ff->fh;
if ((!isdir && fc->no_fsync) || (isdir && fc->no_fsyncdir))
return 0;
- req = fuse_get_request(fc);
- if (!req)
- return -EINTR;
+ req = fuse_get_req(fc);
+ if (IS_ERR(req))
+ return PTR_ERR(req);
memset(&inarg, 0, sizeof(inarg));
inarg.fh = ff->fh;
if (is_bad_inode(inode))
goto out;
- err = -EINTR;
- req = fuse_get_request(fc);
- if (!req)
+ req = fuse_get_req(fc);
+ err = PTR_ERR(req);
+ if (IS_ERR(req))
goto out;
req->out.page_zeroing = 1;
(req->num_pages + 1) * PAGE_CACHE_SIZE > fc->max_read ||
req->pages[req->num_pages - 1]->index + 1 != page->index)) {
fuse_send_readpages(req, data->file, inode);
- data->req = req = fuse_get_request(fc);
- if (!req) {
+ data->req = req = fuse_get_req(fc);
+ if (IS_ERR(req)) {
unlock_page(page);
- return -EINTR;
+ return PTR_ERR(req);
}
}
req->pages[req->num_pages] = page;
data.file = file;
data.inode = inode;
- data.req = fuse_get_request(fc);
- if (!data.req)
- return -EINTR;
+ data.req = fuse_get_req(fc);
+ if (IS_ERR(data.req))
+ return PTR_ERR(data.req);
err = read_cache_pages(mapping, pages, fuse_readpages_fill, &data);
- if (!err)
- fuse_send_readpages(data.req, file, inode);
+ if (!err) {
+ if (data.req->num_pages)
+ fuse_send_readpages(data.req, file, inode);
+ else
+ fuse_put_request(fc, data.req);
+ }
return err;
}
#else /* KERNEL_2_6 */
starti = (pos & FUSE_BLOCK_MASK) >> PAGE_CACHE_SHIFT;
endi = (end + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
- req = fuse_get_request(fc);
- if (!req)
- return -EINTR;
+ req = fuse_get_req(fc);
+ if (IS_ERR(req))
+ return PTR_ERR(req);
for (; starti < endi; starti = nexti) {
nexti = starti + (FUSE_BLOCK_SIZE >> PAGE_CACHE_SHIFT);
if (is_bad_inode(inode))
return -EIO;
- req = fuse_get_request(fc);
- if (!req)
- return -EINTR;
+ req = fuse_get_req(fc);
+ if (IS_ERR(req))
+ return PTR_ERR(req);
req->num_pages = 1;
req->pages[0] = page;
if (is_bad_inode(inode))
return -EIO;
- req = fuse_get_request(fc);
- if (!req)
- return -EINTR;
+ req = fuse_get_req(fc);
+ if (IS_ERR(req))
+ return PTR_ERR(req);
while (count) {
size_t nres;
buf += nres;
if (nres != nbytes)
break;
- if (count)
- fuse_reset_request(req);
+ if (count) {
+ fuse_put_request(fc, req);
+ req = fuse_get_req(fc);
+ if (IS_ERR(req))
+ break;
+ }
}
fuse_put_request(fc, req);
if (res > 0) {
/** Max number of pages that can be used in a single read request */
#define FUSE_MAX_PAGES_PER_REQ 32
-/** If more requests are outstanding, then the operation will block */
-#define FUSE_MAX_OUTSTANDING 10
+/** Maximum number of outstanding background requests */
+#define FUSE_MAX_BACKGROUND 10
/** It could be as large as PATH_MAX, but would that have any uses? */
#define FUSE_NAME_MAX 1024
* A request to the client
*/
struct fuse_req {
- /** This can be on either unused_list, pending processing or
- io lists in fuse_conn */
+ /** This can be on either pending processing or io lists in
+ fuse_conn */
struct list_head list;
/** Entry on the background list */
/** True if the request has reply */
unsigned isreply:1;
- /** The request is preallocated */
- unsigned preallocated:1;
-
/** The request was interrupted */
unsigned interrupted:1;
/** Data is being copied to/from the request */
unsigned locked:1;
+ /** Request is counted as "waiting" */
+ unsigned waiting:1;
+
/** State of the request */
enum fuse_req_state state;
interrupted request) */
struct list_head background;
- /** Controls the maximum number of outstanding requests */
- struct semaphore outstanding_sem;
+ /** Number of requests currently in the background */
+ unsigned num_background;
- /** This counts the number of outstanding requests if
- outstanding_sem would go negative */
- unsigned outstanding_debt;
+ /** Flag indicating if connection is blocked. This will be
+ the case before the INIT reply is received, and if there
+ are too many outstading backgrounds requests */
+ int blocked;
- /** RW semaphore for exclusion with fuse_put_super() */
- struct rw_semaphore sbput_sem;
-
- /** The list of unused requests */
- struct list_head unused_list;
+ /** waitq for blocked connection */
+ wait_queue_head_t blocked_waitq;
/** The next unique request id */
u64 reqctr;
- /** Mount is active */
- unsigned mounted;
-
/** Connection established, cleared on umount, connection
abort and device release */
unsigned connected;
/**
* Reserve a preallocated request
*/
-struct fuse_req *fuse_get_request(struct fuse_conn *fc);
+struct fuse_req *fuse_get_req(struct fuse_conn *fc);
/**
- * Decrement reference count of a request. If count goes to zero put
- * on unused list (preallocated) or free request (not preallocated).
+ * Decrement reference count of a request. If count goes to zero free
+ * the request.
*/
void fuse_put_request(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);
/**
{
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);
- fc->mounted = 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);
- up_write(&fc->sbput_sem);
/* Flush all readers on this fs */
kill_fasync(&fc->fasync, SIGIO, POLL_IN);
wake_up_all(&fc->waitq);
struct fuse_statfs_out outarg;
int err;
- req = fuse_get_request(fc);
- if (!req)
- return -EINTR;
+ req = fuse_get_req(fc);
+ if (IS_ERR(req))
+ return PTR_ERR(req);
memset(&outarg, 0, sizeof(outarg));
req->in.numargs = 0;
static void fuse_conn_release(struct kobject *kobj)
{
- struct fuse_conn *fc = get_fuse_conn_kobj(kobj);
-
- while (!list_empty(&fc->unused_list)) {
- struct fuse_req *req;
- req = list_entry(fc->unused_list.next, struct fuse_req, list);
- list_del(&req->list);
- fuse_request_free(req);
- }
- kfree(fc);
+ kfree(get_fuse_conn_kobj(kobj));
}
#ifndef HAVE_KZALLOC
fc = kzalloc(sizeof(*fc), GFP_KERNEL);
if (fc) {
- int i;
spin_lock_init(&fc->lock);
init_waitqueue_head(&fc->waitq);
+ init_waitqueue_head(&fc->blocked_waitq);
INIT_LIST_HEAD(&fc->pending);
INIT_LIST_HEAD(&fc->processing);
INIT_LIST_HEAD(&fc->io);
- INIT_LIST_HEAD(&fc->unused_list);
INIT_LIST_HEAD(&fc->background);
- sema_init(&fc->outstanding_sem, 1); /* One for INIT */
- init_rwsem(&fc->sbput_sem);
#ifdef KERNEL_2_6
kobj_set_kset_s(fc, connections_subsys);
kobject_init(&fc->kobj);
fc->kobj.release = fuse_conn_release;
#endif
atomic_set(&fc->num_waiting, 0);
- for (i = 0; i < FUSE_MAX_OUTSTANDING; i++) {
- struct fuse_req *req = fuse_request_alloc();
- if (!req) {
- kobject_put(&fc->kobj);
- return NULL;
- }
- list_add(&req->list, &fc->unused_list);
- }
#ifdef KERNEL_2_6_6_PLUS
fc->bdi.ra_pages = (VM_MAX_READAHEAD * 1024) / PAGE_CACHE_SIZE;
fc->bdi.unplug_io_fn = default_unplug_io_fn;
#endif
fc->reqctr = 0;
+ fc->blocked = 1;
}
return fc;
}
static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req)
{
- int i;
struct fuse_init_out *arg = &req->misc.init_out;
if (req->out.h.error || arg->major != FUSE_KERNEL_VERSION)
fc->minor = arg->minor;
fc->max_write = arg->minor < 5 ? 4096 : arg->max_write;
}
-
- /* After INIT reply is received other requests can go
- out. So do (FUSE_MAX_OUTSTANDING - 1) number of
- up()s on outstanding_sem. The last up() is done in
- fuse_putback_request() */
- for (i = 1; i < FUSE_MAX_OUTSTANDING; i++)
- up(&fc->outstanding_sem);
-
fuse_put_request(fc, req);
+ fc->blocked = 0;
+ wake_up_all(&fc->blocked_waitq);
}
-static void fuse_send_init(struct fuse_conn *fc)
+static void fuse_send_init(struct fuse_conn *fc, struct fuse_req *req)
{
- /* This is called from fuse_read_super() so there's guaranteed
- to be exactly one request available */
- struct fuse_req *req = fuse_get_request(fc);
struct fuse_init_in *arg = &req->misc.init_in;
arg->major = FUSE_KERNEL_VERSION;
struct fuse_mount_data d;
struct file *file;
struct dentry *root_dentry;
+ struct fuse_req *init_req;
int err;
if (!parse_fuse_opt((char *) data, &d))
goto err;
}
+ init_req = fuse_request_alloc();
+ if (!init_req)
+ goto err_put_root;
+
#ifdef KERNEL_2_6
err = kobject_set_name(&fc->kobj, "%llu", conn_id());
if (err)
- goto err_put_root;
+ goto err_free_req;
err = kobject_add(&fc->kobj);
if (err)
- goto err_put_root;
+ goto err_free_req;
#endif
sb->s_root = root_dentry;
- fc->mounted = 1;
fc->connected = 1;
kobject_get(&fc->kobj);
file->private_data = fc;
*/
fput(file);
- fuse_send_init(fc);
+ fuse_send_init(fc, init_req);
return 0;
#ifdef KERNEL_2_6
+ err_free_req:
+ fuse_request_free(init_req);
+#endif
err_put_root:
dput(root_dentry);
-#endif
err:
fput(file);
kobject_put(&fc->kobj);