fuse: do not take fc->lock in fuse_request_send_background()
authorKirill Tkhai <ktkhai@virtuozzo.com>
Mon, 27 Aug 2018 15:29:56 +0000 (18:29 +0300)
committerMiklos Szeredi <mszeredi@redhat.com>
Fri, 28 Sep 2018 14:43:23 +0000 (16:43 +0200)
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>
fs/fuse/dev.c
fs/fuse/file.c
fs/fuse/fuse_i.h

index d4b9ffc..071feb8 100644 (file)
@@ -581,42 +581,38 @@ ssize_t fuse_simple_request(struct fuse_conn *fc, struct fuse_args *args)
        return ret;
 }
 
-/*
- * 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)
 {
-       BUG_ON(!test_bit(FR_BACKGROUND, &req->flags));
+       bool queued = false;
+
+       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->sb) {
-               set_bdi_congested(fc->sb->s_bdi, BLK_RW_SYNC);
-               set_bdi_congested(fc->sb->s_bdi, BLK_RW_ASYNC);
-       }
-       list_add_tail(&req->list, &fc->bg_queue);
-       flush_bg_queue(fc);
+       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);
+               }
+               list_add_tail(&req->list, &fc->bg_queue);
+               flush_bg_queue(fc);
+               queued = true;
+       }
        spin_unlock(&fc->bg_lock);
+
+       return queued;
 }
 
 void fuse_request_send_background(struct fuse_conn *fc, struct fuse_req *req)
 {
-       BUG_ON(!req->end);
-       spin_lock(&fc->lock);
-       if (fc->connected) {
-               fuse_request_send_background_nocheck(fc, req);
-               spin_unlock(&fc->lock);
-       } else {
-               spin_unlock(&fc->lock);
+       WARN_ON(!req->end);
+       if (!fuse_request_queue_background(fc, req)) {
                req->out.h.error = -ENOTCONN;
                req->end(fc, req);
                fuse_put_request(fc, req);
@@ -2119,7 +2115,11 @@ void fuse_abort_conn(struct fuse_conn *fc, bool is_abort)
                struct fuse_req *req, *next;
                LIST_HEAD(to_end);
 
+               /* Background queuing checks fc->connected under bg_lock */
+               spin_lock(&fc->bg_lock);
                fc->connected = 0;
+               spin_unlock(&fc->bg_lock);
+
                fc->aborted = is_abort;
                fuse_set_initialized(fc);
                list_for_each_entry(fud, &fc->devices, entry) {
index 65351d4..d15c149 100644 (file)
@@ -1487,6 +1487,7 @@ __acquires(fc->lock)
        struct fuse_inode *fi = get_fuse_inode(req->inode);
        struct fuse_write_in *inarg = &req->misc.write.in;
        __u64 data_size = req->num_pages * PAGE_SIZE;
+       bool queued;
 
        if (!fc->connected)
                goto out_free;
@@ -1502,7 +1503,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:
index d6d5564..6e6eab8 100644 (file)
@@ -863,9 +863,7 @@ ssize_t fuse_simple_request(struct fuse_conn *fc, struct fuse_args *args);
  * Send a request in the background
  */
 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, bool is_abort);