fuse: req use bitops
Finer grained locking will mean there's no single lock to protect modification of bitfileds in fuse_req. So move to using bitops. Can use the non-atomic variants for those which happen while the request definitely has only one reference. Signed-off-by: Miklos Szeredi <mszeredi@suse.cz> Reviewed-by: Ashish Samant <ashish.samant@oracle.com>
This commit is contained in:
parent
0d8e84b043
commit
825d6d3395
4 changed files with 71 additions and 72 deletions
|
@ -181,8 +181,10 @@ static struct fuse_req *__fuse_get_req(struct fuse_conn *fc, unsigned npages,
|
||||||
}
|
}
|
||||||
|
|
||||||
fuse_req_init_context(req);
|
fuse_req_init_context(req);
|
||||||
req->waiting = 1;
|
__set_bit(FR_WAITING, &req->flags);
|
||||||
req->background = for_background;
|
if (for_background)
|
||||||
|
__set_bit(FR_BACKGROUND, &req->flags);
|
||||||
|
|
||||||
return req;
|
return req;
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
@ -272,15 +274,15 @@ struct fuse_req *fuse_get_req_nofail_nopages(struct fuse_conn *fc,
|
||||||
req = get_reserved_req(fc, file);
|
req = get_reserved_req(fc, file);
|
||||||
|
|
||||||
fuse_req_init_context(req);
|
fuse_req_init_context(req);
|
||||||
req->waiting = 1;
|
__set_bit(FR_WAITING, &req->flags);
|
||||||
req->background = 0;
|
__clear_bit(FR_BACKGROUND, &req->flags);
|
||||||
return req;
|
return req;
|
||||||
}
|
}
|
||||||
|
|
||||||
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)) {
|
||||||
if (unlikely(req->background)) {
|
if (test_bit(FR_BACKGROUND, &req->flags)) {
|
||||||
/*
|
/*
|
||||||
* We get here in the unlikely case that a background
|
* We get here in the unlikely case that a background
|
||||||
* request was allocated but not sent
|
* request was allocated but not sent
|
||||||
|
@ -291,9 +293,9 @@ void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req)
|
||||||
spin_unlock(&fc->lock);
|
spin_unlock(&fc->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (req->waiting) {
|
if (test_bit(FR_WAITING, &req->flags)) {
|
||||||
|
__clear_bit(FR_WAITING, &req->flags);
|
||||||
atomic_dec(&fc->num_waiting);
|
atomic_dec(&fc->num_waiting);
|
||||||
req->waiting = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (req->stolen_file)
|
if (req->stolen_file)
|
||||||
|
@ -385,9 +387,8 @@ __releases(fc->lock)
|
||||||
list_del_init(&req->list);
|
list_del_init(&req->list);
|
||||||
list_del_init(&req->intr_entry);
|
list_del_init(&req->intr_entry);
|
||||||
req->state = FUSE_REQ_FINISHED;
|
req->state = FUSE_REQ_FINISHED;
|
||||||
if (req->background) {
|
if (test_bit(FR_BACKGROUND, &req->flags)) {
|
||||||
req->background = 0;
|
clear_bit(FR_BACKGROUND, &req->flags);
|
||||||
|
|
||||||
if (fc->num_background == fc->max_background)
|
if (fc->num_background == fc->max_background)
|
||||||
fc->blocked = 0;
|
fc->blocked = 0;
|
||||||
|
|
||||||
|
@ -442,12 +443,12 @@ __acquires(fc->lock)
|
||||||
if (req->state == FUSE_REQ_FINISHED)
|
if (req->state == FUSE_REQ_FINISHED)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
req->interrupted = 1;
|
set_bit(FR_INTERRUPTED, &req->flags);
|
||||||
if (req->state == FUSE_REQ_SENT)
|
if (req->state == FUSE_REQ_SENT)
|
||||||
queue_interrupt(fc, req);
|
queue_interrupt(fc, req);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!req->force) {
|
if (!test_bit(FR_FORCE, &req->flags)) {
|
||||||
sigset_t oldset;
|
sigset_t oldset;
|
||||||
|
|
||||||
/* Only fatal signals may interrupt this */
|
/* Only fatal signals may interrupt this */
|
||||||
|
@ -478,7 +479,7 @@ __acquires(fc->lock)
|
||||||
|
|
||||||
static void __fuse_request_send(struct fuse_conn *fc, struct fuse_req *req)
|
static void __fuse_request_send(struct fuse_conn *fc, struct fuse_req *req)
|
||||||
{
|
{
|
||||||
BUG_ON(req->background);
|
BUG_ON(test_bit(FR_BACKGROUND, &req->flags));
|
||||||
spin_lock(&fc->lock);
|
spin_lock(&fc->lock);
|
||||||
if (!fc->connected)
|
if (!fc->connected)
|
||||||
req->out.h.error = -ENOTCONN;
|
req->out.h.error = -ENOTCONN;
|
||||||
|
@ -496,9 +497,9 @@ static void __fuse_request_send(struct fuse_conn *fc, struct fuse_req *req)
|
||||||
|
|
||||||
void fuse_request_send(struct fuse_conn *fc, struct fuse_req *req)
|
void fuse_request_send(struct fuse_conn *fc, struct fuse_req *req)
|
||||||
{
|
{
|
||||||
req->isreply = 1;
|
__set_bit(FR_ISREPLY, &req->flags);
|
||||||
if (!req->waiting) {
|
if (!test_bit(FR_WAITING, &req->flags)) {
|
||||||
req->waiting = 1;
|
__set_bit(FR_WAITING, &req->flags);
|
||||||
atomic_inc(&fc->num_waiting);
|
atomic_inc(&fc->num_waiting);
|
||||||
}
|
}
|
||||||
__fuse_request_send(fc, req);
|
__fuse_request_send(fc, req);
|
||||||
|
@ -578,12 +579,12 @@ ssize_t fuse_simple_request(struct fuse_conn *fc, struct fuse_args *args)
|
||||||
void fuse_request_send_background_locked(struct fuse_conn *fc,
|
void fuse_request_send_background_locked(struct fuse_conn *fc,
|
||||||
struct fuse_req *req)
|
struct fuse_req *req)
|
||||||
{
|
{
|
||||||
BUG_ON(!req->background);
|
BUG_ON(!test_bit(FR_BACKGROUND, &req->flags));
|
||||||
if (!req->waiting) {
|
if (!test_bit(FR_WAITING, &req->flags)) {
|
||||||
req->waiting = 1;
|
__set_bit(FR_WAITING, &req->flags);
|
||||||
atomic_inc(&fc->num_waiting);
|
atomic_inc(&fc->num_waiting);
|
||||||
}
|
}
|
||||||
req->isreply = 1;
|
__set_bit(FR_ISREPLY, &req->flags);
|
||||||
fc->num_background++;
|
fc->num_background++;
|
||||||
if (fc->num_background == fc->max_background)
|
if (fc->num_background == fc->max_background)
|
||||||
fc->blocked = 1;
|
fc->blocked = 1;
|
||||||
|
@ -617,7 +618,7 @@ static int fuse_request_send_notify_reply(struct fuse_conn *fc,
|
||||||
{
|
{
|
||||||
int err = -ENODEV;
|
int err = -ENODEV;
|
||||||
|
|
||||||
req->isreply = 0;
|
__clear_bit(FR_ISREPLY, &req->flags);
|
||||||
req->in.h.unique = unique;
|
req->in.h.unique = unique;
|
||||||
spin_lock(&fc->lock);
|
spin_lock(&fc->lock);
|
||||||
if (fc->connected) {
|
if (fc->connected) {
|
||||||
|
@ -644,7 +645,7 @@ void fuse_force_forget(struct file *file, u64 nodeid)
|
||||||
req->in.numargs = 1;
|
req->in.numargs = 1;
|
||||||
req->in.args[0].size = sizeof(inarg);
|
req->in.args[0].size = sizeof(inarg);
|
||||||
req->in.args[0].value = &inarg;
|
req->in.args[0].value = &inarg;
|
||||||
req->isreply = 0;
|
__clear_bit(FR_ISREPLY, &req->flags);
|
||||||
__fuse_request_send(fc, req);
|
__fuse_request_send(fc, req);
|
||||||
/* ignore errors */
|
/* ignore errors */
|
||||||
fuse_put_request(fc, req);
|
fuse_put_request(fc, req);
|
||||||
|
@ -660,10 +661,10 @@ static int lock_request(struct fuse_conn *fc, struct fuse_req *req)
|
||||||
int err = 0;
|
int err = 0;
|
||||||
if (req) {
|
if (req) {
|
||||||
spin_lock(&fc->lock);
|
spin_lock(&fc->lock);
|
||||||
if (req->aborted)
|
if (test_bit(FR_ABORTED, &req->flags))
|
||||||
err = -ENOENT;
|
err = -ENOENT;
|
||||||
else
|
else
|
||||||
req->locked = 1;
|
set_bit(FR_LOCKED, &req->flags);
|
||||||
spin_unlock(&fc->lock);
|
spin_unlock(&fc->lock);
|
||||||
}
|
}
|
||||||
return err;
|
return err;
|
||||||
|
@ -678,10 +679,10 @@ static int unlock_request(struct fuse_conn *fc, struct fuse_req *req)
|
||||||
int err = 0;
|
int err = 0;
|
||||||
if (req) {
|
if (req) {
|
||||||
spin_lock(&fc->lock);
|
spin_lock(&fc->lock);
|
||||||
if (req->aborted)
|
if (test_bit(FR_ABORTED, &req->flags))
|
||||||
err = -ENOENT;
|
err = -ENOENT;
|
||||||
else
|
else
|
||||||
req->locked = 0;
|
clear_bit(FR_LOCKED, &req->flags);
|
||||||
spin_unlock(&fc->lock);
|
spin_unlock(&fc->lock);
|
||||||
}
|
}
|
||||||
return err;
|
return err;
|
||||||
|
@ -902,7 +903,7 @@ static int fuse_try_move_page(struct fuse_copy_state *cs, struct page **pagep)
|
||||||
|
|
||||||
err = 0;
|
err = 0;
|
||||||
spin_lock(&cs->fc->lock);
|
spin_lock(&cs->fc->lock);
|
||||||
if (cs->req->aborted)
|
if (test_bit(FR_ABORTED, &cs->req->flags))
|
||||||
err = -ENOENT;
|
err = -ENOENT;
|
||||||
else
|
else
|
||||||
*pagep = newpage;
|
*pagep = newpage;
|
||||||
|
@ -1309,7 +1310,7 @@ static ssize_t fuse_dev_do_read(struct fuse_conn *fc, struct file *file,
|
||||||
(struct fuse_arg *) in->args, 0);
|
(struct fuse_arg *) in->args, 0);
|
||||||
fuse_copy_finish(cs);
|
fuse_copy_finish(cs);
|
||||||
spin_lock(&fc->lock);
|
spin_lock(&fc->lock);
|
||||||
req->locked = 0;
|
clear_bit(FR_LOCKED, &req->flags);
|
||||||
if (!fc->connected) {
|
if (!fc->connected) {
|
||||||
request_end(fc, req);
|
request_end(fc, req);
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
@ -1319,12 +1320,12 @@ static ssize_t fuse_dev_do_read(struct fuse_conn *fc, struct file *file,
|
||||||
request_end(fc, req);
|
request_end(fc, req);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
if (!req->isreply)
|
if (!test_bit(FR_ISREPLY, &req->flags)) {
|
||||||
request_end(fc, req);
|
request_end(fc, req);
|
||||||
else {
|
} else {
|
||||||
req->state = FUSE_REQ_SENT;
|
req->state = FUSE_REQ_SENT;
|
||||||
list_move_tail(&req->list, &fc->processing);
|
list_move_tail(&req->list, &fc->processing);
|
||||||
if (req->interrupted)
|
if (test_bit(FR_INTERRUPTED, &req->flags))
|
||||||
queue_interrupt(fc, req);
|
queue_interrupt(fc, req);
|
||||||
spin_unlock(&fc->lock);
|
spin_unlock(&fc->lock);
|
||||||
}
|
}
|
||||||
|
@ -1921,7 +1922,7 @@ static ssize_t fuse_dev_do_write(struct fuse_conn *fc,
|
||||||
req->state = FUSE_REQ_WRITING;
|
req->state = FUSE_REQ_WRITING;
|
||||||
list_move(&req->list, &fc->io);
|
list_move(&req->list, &fc->io);
|
||||||
req->out.h = oh;
|
req->out.h = oh;
|
||||||
req->locked = 1;
|
set_bit(FR_LOCKED, &req->flags);
|
||||||
cs->req = req;
|
cs->req = req;
|
||||||
if (!req->out.page_replace)
|
if (!req->out.page_replace)
|
||||||
cs->move_pages = 0;
|
cs->move_pages = 0;
|
||||||
|
@ -1931,7 +1932,7 @@ static ssize_t fuse_dev_do_write(struct fuse_conn *fc,
|
||||||
fuse_copy_finish(cs);
|
fuse_copy_finish(cs);
|
||||||
|
|
||||||
spin_lock(&fc->lock);
|
spin_lock(&fc->lock);
|
||||||
req->locked = 0;
|
clear_bit(FR_LOCKED, &req->flags);
|
||||||
if (!fc->connected)
|
if (!fc->connected)
|
||||||
err = -ENOENT;
|
err = -ENOENT;
|
||||||
else if (err)
|
else if (err)
|
||||||
|
@ -2097,8 +2098,8 @@ __acquires(fc->lock)
|
||||||
|
|
||||||
list_for_each_entry_safe(req, next, &fc->io, list) {
|
list_for_each_entry_safe(req, next, &fc->io, list) {
|
||||||
req->out.h.error = -ECONNABORTED;
|
req->out.h.error = -ECONNABORTED;
|
||||||
req->aborted = 1;
|
set_bit(FR_ABORTED, &req->flags);
|
||||||
if (!req->locked)
|
if (!test_bit(FR_LOCKED, &req->flags))
|
||||||
list_move(&req->list, &to_end);
|
list_move(&req->list, &to_end);
|
||||||
}
|
}
|
||||||
while (!list_empty(&to_end)) {
|
while (!list_empty(&to_end)) {
|
||||||
|
|
|
@ -96,17 +96,17 @@ static void fuse_file_put(struct fuse_file *ff, bool sync)
|
||||||
* Drop the release request when client does not
|
* Drop the release request when client does not
|
||||||
* implement 'open'
|
* implement 'open'
|
||||||
*/
|
*/
|
||||||
req->background = 0;
|
__clear_bit(FR_BACKGROUND, &req->flags);
|
||||||
iput(req->misc.release.inode);
|
iput(req->misc.release.inode);
|
||||||
fuse_put_request(ff->fc, req);
|
fuse_put_request(ff->fc, req);
|
||||||
} else if (sync) {
|
} else if (sync) {
|
||||||
req->background = 0;
|
__clear_bit(FR_BACKGROUND, &req->flags);
|
||||||
fuse_request_send(ff->fc, req);
|
fuse_request_send(ff->fc, req);
|
||||||
iput(req->misc.release.inode);
|
iput(req->misc.release.inode);
|
||||||
fuse_put_request(ff->fc, req);
|
fuse_put_request(ff->fc, req);
|
||||||
} else {
|
} else {
|
||||||
req->end = fuse_release_end;
|
req->end = fuse_release_end;
|
||||||
req->background = 1;
|
__set_bit(FR_BACKGROUND, &req->flags);
|
||||||
fuse_request_send_background(ff->fc, req);
|
fuse_request_send_background(ff->fc, req);
|
||||||
}
|
}
|
||||||
kfree(ff);
|
kfree(ff);
|
||||||
|
@ -299,8 +299,8 @@ void fuse_sync_release(struct fuse_file *ff, int flags)
|
||||||
{
|
{
|
||||||
WARN_ON(atomic_read(&ff->count) > 1);
|
WARN_ON(atomic_read(&ff->count) > 1);
|
||||||
fuse_prepare_release(ff, flags, FUSE_RELEASE);
|
fuse_prepare_release(ff, flags, FUSE_RELEASE);
|
||||||
ff->reserved_req->force = 1;
|
__set_bit(FR_FORCE, &ff->reserved_req->flags);
|
||||||
ff->reserved_req->background = 0;
|
__clear_bit(FR_BACKGROUND, &ff->reserved_req->flags);
|
||||||
fuse_request_send(ff->fc, ff->reserved_req);
|
fuse_request_send(ff->fc, ff->reserved_req);
|
||||||
fuse_put_request(ff->fc, ff->reserved_req);
|
fuse_put_request(ff->fc, ff->reserved_req);
|
||||||
kfree(ff);
|
kfree(ff);
|
||||||
|
@ -426,7 +426,7 @@ static int fuse_flush(struct file *file, fl_owner_t id)
|
||||||
req->in.numargs = 1;
|
req->in.numargs = 1;
|
||||||
req->in.args[0].size = sizeof(inarg);
|
req->in.args[0].size = sizeof(inarg);
|
||||||
req->in.args[0].value = &inarg;
|
req->in.args[0].value = &inarg;
|
||||||
req->force = 1;
|
__set_bit(FR_FORCE, &req->flags);
|
||||||
fuse_request_send(fc, req);
|
fuse_request_send(fc, req);
|
||||||
err = req->out.h.error;
|
err = req->out.h.error;
|
||||||
fuse_put_request(fc, req);
|
fuse_put_request(fc, req);
|
||||||
|
@ -1611,7 +1611,8 @@ static int fuse_writepage_locked(struct page *page)
|
||||||
if (!req)
|
if (!req)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
req->background = 1; /* writeback always goes to bg_queue */
|
/* writeback always goes to bg_queue */
|
||||||
|
__set_bit(FR_BACKGROUND, &req->flags);
|
||||||
tmp_page = alloc_page(GFP_NOFS | __GFP_HIGHMEM);
|
tmp_page = alloc_page(GFP_NOFS | __GFP_HIGHMEM);
|
||||||
if (!tmp_page)
|
if (!tmp_page)
|
||||||
goto err_free;
|
goto err_free;
|
||||||
|
@ -1830,7 +1831,7 @@ static int fuse_writepages_fill(struct page *page,
|
||||||
req->misc.write.in.write_flags |= FUSE_WRITE_CACHE;
|
req->misc.write.in.write_flags |= FUSE_WRITE_CACHE;
|
||||||
req->misc.write.next = NULL;
|
req->misc.write.next = NULL;
|
||||||
req->in.argpages = 1;
|
req->in.argpages = 1;
|
||||||
req->background = 1;
|
__set_bit(FR_BACKGROUND, &req->flags);
|
||||||
req->num_pages = 0;
|
req->num_pages = 0;
|
||||||
req->end = fuse_writepage_end;
|
req->end = fuse_writepage_end;
|
||||||
req->inode = inode;
|
req->inode = inode;
|
||||||
|
|
|
@ -266,6 +266,27 @@ struct fuse_io_priv {
|
||||||
struct completion *done;
|
struct completion *done;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request flags
|
||||||
|
*
|
||||||
|
* FR_ISREPLY: set if the request has reply
|
||||||
|
* FR_FORCE: force sending of the request even if interrupted
|
||||||
|
* FR_BACKGROUND: request is sent in the background
|
||||||
|
* FR_WAITING: request is counted as "waiting"
|
||||||
|
* FR_ABORTED: the request was aborted
|
||||||
|
* FR_INTERRUPTED: the request has been interrupted
|
||||||
|
* FR_LOCKED: data is being copied to/from the request
|
||||||
|
*/
|
||||||
|
enum fuse_req_flag {
|
||||||
|
FR_ISREPLY,
|
||||||
|
FR_FORCE,
|
||||||
|
FR_BACKGROUND,
|
||||||
|
FR_WAITING,
|
||||||
|
FR_ABORTED,
|
||||||
|
FR_INTERRUPTED,
|
||||||
|
FR_LOCKED,
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A request to the client
|
* A request to the client
|
||||||
*/
|
*/
|
||||||
|
@ -283,32 +304,8 @@ struct fuse_req {
|
||||||
/** Unique ID for the interrupt request */
|
/** Unique ID for the interrupt request */
|
||||||
u64 intr_unique;
|
u64 intr_unique;
|
||||||
|
|
||||||
/*
|
/* Request flags, updated with test/set/clear_bit() */
|
||||||
* The following bitfields are either set once before the
|
unsigned long flags;
|
||||||
* request is queued or setting/clearing them is protected by
|
|
||||||
* fuse_conn->lock
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** True if the request has reply */
|
|
||||||
unsigned isreply:1;
|
|
||||||
|
|
||||||
/** Force sending of the request even if interrupted */
|
|
||||||
unsigned force:1;
|
|
||||||
|
|
||||||
/** The request was aborted */
|
|
||||||
unsigned aborted:1;
|
|
||||||
|
|
||||||
/** Request is sent in the background */
|
|
||||||
unsigned background:1;
|
|
||||||
|
|
||||||
/** The request has been 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 */
|
/** State of the request */
|
||||||
enum fuse_req_state state;
|
enum fuse_req_state state;
|
||||||
|
|
|
@ -362,8 +362,8 @@ static void fuse_send_destroy(struct fuse_conn *fc)
|
||||||
if (req && fc->conn_init) {
|
if (req && fc->conn_init) {
|
||||||
fc->destroy_req = NULL;
|
fc->destroy_req = NULL;
|
||||||
req->in.h.opcode = FUSE_DESTROY;
|
req->in.h.opcode = FUSE_DESTROY;
|
||||||
req->force = 1;
|
__set_bit(FR_FORCE, &req->flags);
|
||||||
req->background = 0;
|
__clear_bit(FR_BACKGROUND, &req->flags);
|
||||||
fuse_request_send(fc, req);
|
fuse_request_send(fc, req);
|
||||||
fuse_put_request(fc, req);
|
fuse_put_request(fc, req);
|
||||||
}
|
}
|
||||||
|
@ -1060,7 +1060,7 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
|
||||||
init_req = fuse_request_alloc(0);
|
init_req = fuse_request_alloc(0);
|
||||||
if (!init_req)
|
if (!init_req)
|
||||||
goto err_put_root;
|
goto err_put_root;
|
||||||
init_req->background = 1;
|
__set_bit(FR_BACKGROUND, &init_req->flags);
|
||||||
|
|
||||||
if (is_bdev) {
|
if (is_bdev) {
|
||||||
fc->destroy_req = fuse_request_alloc(0);
|
fc->destroy_req = fuse_request_alloc(0);
|
||||||
|
|
Loading…
Reference in a new issue