[Devel] [PATCH 3/8] fuse: do not take fc->lock in fuse_request_send_background()
Kirill Tkhai
ktkhai at virtuozzo.com
Tue Apr 23 10:48:40 MSK 2019
On 23.04.2019 09:56, Pavel Butsykin wrote:
>
>
> On 03.04.2019 18:37, Kirill Tkhai wrote:
>> ms commit 63825b4e1da5
>>
>> Currently, we take fc->lock there only to check for fc->connected.
>> But this flag is changed only on connection abort, which is very
>> rare operation.
>>
>> So allow checking fc->connected under just fc->bg_lock and use this lock
>> (as well as fc->lock) when resetting fc->connected.
>>
>> Signed-off-by: Kirill Tkhai <ktkhai at virtuozzo.com>
>> Signed-off-by: Miklos Szeredi <mszeredi at redhat.com>
>> ---
>> fs/fuse/dev.c | 73 +++++++++++++++++++++++++++---------------------------
>> fs/fuse/file.c | 4 ++-
>> fs/fuse/fuse_i.h | 3 +-
>> 3 files changed, 41 insertions(+), 39 deletions(-)
>>
>> diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
>> index 1ffc10ff18ba..1355f4a0a8e4 100644
>> --- a/fs/fuse/dev.c
>> +++ b/fs/fuse/dev.c
>> @@ -598,69 +598,70 @@ void fuse_request_send(struct fuse_conn *fc, struct fuse_req *req)
>> }
>> EXPORT_SYMBOL_GPL(fuse_request_send);
>>
>> -/*
>> - * Called under fc->lock
>> - *
>> - * fc->connected must have been checked previously
>> - */
>> -void fuse_request_send_background_nocheck(struct fuse_conn *fc,
>> - struct fuse_req *req)
>> +bool fuse_request_queue_background(struct fuse_conn *fc, struct fuse_req *req)
>> {
>> struct fuse_iqueue *fiq = req->fiq;
>> + bool queued = false;
>>
>> - BUG_ON(!test_bit(FR_BACKGROUND, &req->flags));
>> -
>> + WARN_ON(!test_bit(FR_BACKGROUND, &req->flags));
>> if (!test_bit(FR_WAITING, &req->flags)) {
>> __set_bit(FR_WAITING, &req->flags);
>> atomic_inc(&fc->num_waiting);
>> }
>> __set_bit(FR_ISREPLY, &req->flags);
>> spin_lock(&fc->bg_lock);
>> - fc->num_background++;
>> - if (fc->num_background == fc->max_background)
>> - fc->blocked = 1;
>> - if (fc->num_background == fc->congestion_threshold &&
>> - fc->bdi_initialized) {
>> - set_bdi_congested(&fc->bdi, BLK_RW_SYNC);
>> - set_bdi_congested(&fc->bdi, BLK_RW_ASYNC);
>> - }
>> + if (likely(fc->connected)) {
>> + fc->num_background++;
>> + if (fc->num_background == fc->max_background)
>> + fc->blocked = 1;
>> + if (fc->num_background == fc->congestion_threshold && fc->sb) {
>> + set_bdi_congested(fc->sb->s_bdi, BLK_RW_SYNC);
>> + set_bdi_congested(fc->sb->s_bdi, BLK_RW_ASYNC);
>> + }
>>
>> - if (test_bit(FR_NONBLOCKING, &req->flags)) {
>> - fc->active_background++;
>> - spin_lock(&fiq->waitq.lock);
>> - req->in.h.unique = fuse_get_unique(fiq);
>> - queue_request(fiq, req);
>> - spin_unlock(&fiq->waitq.lock);
>> - goto unlock;
>> - }
>> + if (test_bit(FR_NONBLOCKING, &req->flags)) {
>> + fc->active_background++;
>> + spin_lock(&fiq->waitq.lock);
>> + req->in.h.unique = fuse_get_unique(fiq);
>> + queue_request(fiq, req);
>> + spin_unlock(&fiq->waitq.lock);
>> + queued = true;
>> + goto unlock;
>> + }
>>
>> - list_add_tail(&req->list, &fc->bg_queue);
>> - flush_bg_queue(fc, fiq);
>> + list_add_tail(&req->list, &fc->bg_queue);
>> + flush_bg_queue(fc, fiq);
>> + queued = true;
>> + }
>> unlock:
>> spin_unlock(&fc->bg_lock);
>> + return queued;
>> }
>>
>> void fuse_request_send_background(struct fuse_conn *fc, struct fuse_req *req)
>> {
>> - BUG_ON(!req->end);
>> + bool fail;
>> + WARN_ON(!req->end);
>>
>> if (fc->kio.op && !fc->kio.op->req_send(fc, req, true, false))
>> return;
>>
>> spin_lock(&fc->lock);
>> - if (req->page_cache && req->ff &&
>> - test_bit(FUSE_S_FAIL_IMMEDIATELY, &req->ff->ff_state)) {
>> + fail = (req->page_cache && req->ff &&
>> + test_bit(FUSE_S_FAIL_IMMEDIATELY, &req->ff->ff_state));
>> + spin_unlock(&fc->lock);
>> +
>> + if (fail) {
>> + /* FIXME */
>
> What needs to be fixed here?
Commit aims to remove fc->lock from this function (and it's called "fuse: do not
take fc->lock in fuse_request_send_background()). But FUSE_S_FAIL_IMMEDIATELY
vstorage crutch is made under fc->lock, so the lock remains, and "FIXME" is there.
I think the lock is not needed, since the crutch's bit FUSE_S_FAIL_IMMEDIATELY
is set under lock, but we do not need for all queued requests in fuse_invalidate_files()
after the bit is set. So, I remain this logic, and you may decide whether you
need the lock or not.
>> BUG_ON(req->in.h.opcode != FUSE_READ);
>> req->out.h.error = -EIO;
>> __clear_bit(FR_BACKGROUND, &req->flags);
>> __clear_bit(FR_PENDING, &req->flags);
>> - spin_unlock(&fc->lock);
>> request_end(fc, req);
>> - } else if (fc->connected) {
>> - fuse_request_send_background_nocheck(fc, req);
>> - spin_unlock(&fc->lock);
>> - } else {
>> - spin_unlock(&fc->lock);
>> + return;
>> + }
>> +
>> + if (!fuse_request_queue_background(fc, req)) {
>> req->out.h.error = -ENOTCONN;
>> req->end(fc, req);
>> fuse_put_request(fc, req);
>> diff --git a/fs/fuse/file.c b/fs/fuse/file.c
>> index edc314bd7156..6bffa1eef4dd 100644
>> --- a/fs/fuse/file.c
>> +++ b/fs/fuse/file.c
>> @@ -1941,6 +1941,7 @@ __acquires(fc->lock)
>> loff_t size = i_size_read(req->inode);
>> struct fuse_write_in *inarg = &req->misc.write.in;
>> __u64 data_size = req->num_pages * PAGE_CACHE_SIZE;
>> + bool queued;
>>
>> if (!fc->connected ||
>> test_bit(FUSE_S_FAIL_IMMEDIATELY, &req->ff->ff_state))
>> @@ -1957,7 +1958,8 @@ __acquires(fc->lock)
>>
>> req->in.args[1].size = inarg->size;
>> fi->writectr++;
>> - fuse_request_send_background_nocheck(fc, req);
>> + queued = fuse_request_queue_background(fc, req);
>> + WARN_ON(!queued);
>> return;
>>
>> out_free:
>> diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
>> index a6eb566f8789..ee8b367f2956 100644
>> --- a/fs/fuse/fuse_i.h
>> +++ b/fs/fuse/fuse_i.h
>> @@ -979,8 +979,7 @@ void fuse_request_check_and_send(struct fuse_conn *fc, struct fuse_req *req,
>> */
>> void fuse_request_send_background(struct fuse_conn *fc, struct fuse_req *req);
>>
>> -void fuse_request_send_background_nocheck(struct fuse_conn *fc,
>> - struct fuse_req *req);
>> +bool fuse_request_queue_background(struct fuse_conn *fc, struct fuse_req *req);
>>
>> /* Abort all requests */
>> void fuse_abort_conn(struct fuse_conn *fc);
>>
More information about the Devel
mailing list