Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

fuse: do not take fc->lock in fuse_request_send_background()

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@virtuozzo.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>

authored by

Kirill Tkhai and committed by
Miklos Szeredi
63825b4e ae2dffa3

+27 -27
+23 -23
fs/fuse/dev.c
··· 581 581 return ret; 582 582 } 583 583 584 - /* 585 - * Called under fc->lock 586 - * 587 - * fc->connected must have been checked previously 588 - */ 589 - void fuse_request_send_background_nocheck(struct fuse_conn *fc, 590 - struct fuse_req *req) 584 + bool fuse_request_queue_background(struct fuse_conn *fc, struct fuse_req *req) 591 585 { 592 - BUG_ON(!test_bit(FR_BACKGROUND, &req->flags)); 586 + bool queued = false; 587 + 588 + WARN_ON(!test_bit(FR_BACKGROUND, &req->flags)); 593 589 if (!test_bit(FR_WAITING, &req->flags)) { 594 590 __set_bit(FR_WAITING, &req->flags); 595 591 atomic_inc(&fc->num_waiting); 596 592 } 597 593 __set_bit(FR_ISREPLY, &req->flags); 598 594 spin_lock(&fc->bg_lock); 599 - fc->num_background++; 600 - if (fc->num_background == fc->max_background) 601 - fc->blocked = 1; 602 - if (fc->num_background == fc->congestion_threshold && fc->sb) { 603 - set_bdi_congested(fc->sb->s_bdi, BLK_RW_SYNC); 604 - set_bdi_congested(fc->sb->s_bdi, BLK_RW_ASYNC); 595 + if (likely(fc->connected)) { 596 + fc->num_background++; 597 + if (fc->num_background == fc->max_background) 598 + fc->blocked = 1; 599 + if (fc->num_background == fc->congestion_threshold && fc->sb) { 600 + set_bdi_congested(fc->sb->s_bdi, BLK_RW_SYNC); 601 + set_bdi_congested(fc->sb->s_bdi, BLK_RW_ASYNC); 602 + } 603 + list_add_tail(&req->list, &fc->bg_queue); 604 + flush_bg_queue(fc); 605 + queued = true; 605 606 } 606 - list_add_tail(&req->list, &fc->bg_queue); 607 - flush_bg_queue(fc); 608 607 spin_unlock(&fc->bg_lock); 608 + 609 + return queued; 609 610 } 610 611 611 612 void fuse_request_send_background(struct fuse_conn *fc, struct fuse_req *req) 612 613 { 613 - BUG_ON(!req->end); 614 - spin_lock(&fc->lock); 615 - if (fc->connected) { 616 - fuse_request_send_background_nocheck(fc, req); 617 - spin_unlock(&fc->lock); 618 - } else { 619 - spin_unlock(&fc->lock); 614 + WARN_ON(!req->end); 615 + if (!fuse_request_queue_background(fc, req)) { 620 616 req->out.h.error = -ENOTCONN; 621 617 req->end(fc, req); 622 618 fuse_put_request(fc, req); ··· 2115 2119 struct fuse_req *req, *next; 2116 2120 LIST_HEAD(to_end); 2117 2121 2122 + /* Background queuing checks fc->connected under bg_lock */ 2123 + spin_lock(&fc->bg_lock); 2118 2124 fc->connected = 0; 2125 + spin_unlock(&fc->bg_lock); 2126 + 2119 2127 fc->aborted = is_abort; 2120 2128 fuse_set_initialized(fc); 2121 2129 list_for_each_entry(fud, &fc->devices, entry) {
+3 -1
fs/fuse/file.c
··· 1487 1487 struct fuse_inode *fi = get_fuse_inode(req->inode); 1488 1488 struct fuse_write_in *inarg = &req->misc.write.in; 1489 1489 __u64 data_size = req->num_pages * PAGE_SIZE; 1490 + bool queued; 1490 1491 1491 1492 if (!fc->connected) 1492 1493 goto out_free; ··· 1503 1502 1504 1503 req->in.args[1].size = inarg->size; 1505 1504 fi->writectr++; 1506 - fuse_request_send_background_nocheck(fc, req); 1505 + queued = fuse_request_queue_background(fc, req); 1506 + WARN_ON(!queued); 1507 1507 return; 1508 1508 1509 1509 out_free:
+1 -3
fs/fuse/fuse_i.h
··· 863 863 * Send a request in the background 864 864 */ 865 865 void fuse_request_send_background(struct fuse_conn *fc, struct fuse_req *req); 866 - 867 - void fuse_request_send_background_nocheck(struct fuse_conn *fc, 868 - struct fuse_req *req); 866 + bool fuse_request_queue_background(struct fuse_conn *fc, struct fuse_req *req); 869 867 870 868 /* Abort all requests */ 871 869 void fuse_abort_conn(struct fuse_conn *fc, bool is_abort);