fuse: add locking to max_background and congestion_threshold changes
authorKirill Tkhai <ktkhai@virtuozzo.com>
Mon, 27 Aug 2018 15:29:37 +0000 (18:29 +0300)
committerMiklos Szeredi <mszeredi@redhat.com>
Fri, 28 Sep 2018 14:43:22 +0000 (16:43 +0200)
Functions sequences like request_end()->flush_bg_queue() require that
max_background and congestion_threshold are constant during their
execution. Otherwise, checks like

if (fc->num_background == fc->max_background)

made in different time may behave not like expected.

Signed-off-by: Kirill Tkhai <ktkhai@virtuozzo.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
fs/fuse/control.c

index acc3581..eaa0e2b 100644 (file)
@@ -125,7 +125,12 @@ static ssize_t fuse_conn_max_background_write(struct file *file,
        if (ret > 0) {
                struct fuse_conn *fc = fuse_ctl_file_conn_get(file);
                if (fc) {
+                       spin_lock(&fc->lock);
                        fc->max_background = val;
+                       fc->blocked = fc->num_background >= fc->max_background;
+                       if (!fc->blocked)
+                               wake_up(&fc->blocked_waitq);
+                       spin_unlock(&fc->lock);
                        fuse_conn_put(fc);
                }
        }
@@ -155,18 +160,31 @@ static ssize_t fuse_conn_congestion_threshold_write(struct file *file,
                                                    size_t count, loff_t *ppos)
 {
        unsigned uninitialized_var(val);
+       struct fuse_conn *fc;
        ssize_t ret;
 
        ret = fuse_conn_limit_write(file, buf, count, ppos, &val,
                                    max_user_congthresh);
-       if (ret > 0) {
-               struct fuse_conn *fc = fuse_ctl_file_conn_get(file);
-               if (fc) {
-                       fc->congestion_threshold = val;
-                       fuse_conn_put(fc);
+       if (ret <= 0)
+               goto out;
+       fc = fuse_ctl_file_conn_get(file);
+       if (!fc)
+               goto out;
+
+       spin_lock(&fc->lock);
+       fc->congestion_threshold = val;
+       if (fc->sb) {
+               if (fc->num_background < fc->congestion_threshold) {
+                       clear_bdi_congested(fc->sb->s_bdi, BLK_RW_SYNC);
+                       clear_bdi_congested(fc->sb->s_bdi, BLK_RW_ASYNC);
+               } else {
+                       set_bdi_congested(fc->sb->s_bdi, BLK_RW_SYNC);
+                       set_bdi_congested(fc->sb->s_bdi, BLK_RW_ASYNC);
                }
        }
-
+       spin_unlock(&fc->lock);
+       fuse_conn_put(fc);
+out:
        return ret;
 }