]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - block/blk-wbt.c
block: blk_init_allocated_queue() set q->fq as NULL in the fail case
[mirror_ubuntu-bionic-kernel.git] / block / blk-wbt.c
index d891b4f80c2f66a5b45e6337cb73dbf5a8ff5ad4..742f0cb4b1cf6bda9f056ea3a68eb146ee29b3b1 100644 (file)
@@ -157,7 +157,7 @@ static void wbt_rqw_done(struct rq_wb *rwb, struct rq_wait *rqw,
                int diff = limit - inflight;
 
                if (!inflight || diff >= rwb->wb_background / 2)
-                       wake_up(&rqw->wait);
+                       wake_up_all(&rqw->wait);
        }
 }
 
@@ -519,6 +519,34 @@ static inline unsigned int get_limit(struct rq_wb *rwb, unsigned long rw)
        return limit;
 }
 
+struct wbt_wait_data {
+       struct wait_queue_entry wq;
+       struct task_struct *task;
+       struct rq_wb *rwb;
+       struct rq_wait *rqw;
+       unsigned long rw;
+       bool got_token;
+};
+
+static int wbt_wake_function(struct wait_queue_entry *curr, unsigned int mode,
+                            int wake_flags, void *key)
+{
+       struct wbt_wait_data *data = container_of(curr, struct wbt_wait_data,
+                                                       wq);
+
+       /*
+        * If we fail to get a budget, return -1 to interrupt the wake up
+        * loop in __wake_up_common.
+        */
+       if (!atomic_inc_below(&data->rqw->inflight, get_limit(data->rwb, data->rw)))
+               return -1;
+
+       data->got_token = true;
+       list_del_init(&curr->entry);
+       wake_up_process(data->task);
+       return 1;
+}
+
 /*
  * Block if we will exceed our limit, or if we are currently waiting for
  * the timer to kick off queuing again.
@@ -529,19 +557,40 @@ static void __wbt_wait(struct rq_wb *rwb, enum wbt_flags wb_acct,
        __acquires(lock)
 {
        struct rq_wait *rqw = get_rq_wait(rwb, wb_acct);
-       DECLARE_WAITQUEUE(wait, current);
+       struct wbt_wait_data data = {
+               .wq = {
+                       .func   = wbt_wake_function,
+                       .entry  = LIST_HEAD_INIT(data.wq.entry),
+               },
+               .task = current,
+               .rwb = rwb,
+               .rqw = rqw,
+               .rw = rw,
+       };
        bool has_sleeper;
 
        has_sleeper = wq_has_sleeper(&rqw->wait);
        if (!has_sleeper && atomic_inc_below(&rqw->inflight, get_limit(rwb, rw)))
                return;
 
-       add_wait_queue_exclusive(&rqw->wait, &wait);
+       prepare_to_wait_exclusive(&rqw->wait, &data.wq, TASK_UNINTERRUPTIBLE);
        do {
-               set_current_state(TASK_UNINTERRUPTIBLE);
+               if (data.got_token)
+                       break;
 
-               if (!has_sleeper && atomic_inc_below(&rqw->inflight, get_limit(rwb, rw)))
+               if (!has_sleeper &&
+                   atomic_inc_below(&rqw->inflight, get_limit(rwb, rw))) {
+                       finish_wait(&rqw->wait, &data.wq);
+
+                       /*
+                        * We raced with wbt_wake_function() getting a token,
+                        * which means we now have two. Put our local token
+                        * and wake anyone else potentially waiting for one.
+                        */
+                       if (data.got_token)
+                               wbt_rqw_done(rwb, rqw, wb_acct);
                        break;
+               }
 
                if (lock) {
                        spin_unlock_irq(lock);
@@ -549,11 +598,11 @@ static void __wbt_wait(struct rq_wb *rwb, enum wbt_flags wb_acct,
                        spin_lock_irq(lock);
                } else
                        io_schedule();
+
                has_sleeper = false;
        } while (1);
 
-       __set_current_state(TASK_RUNNING);
-       remove_wait_queue(&rqw->wait, &wait);
+       finish_wait(&rqw->wait, &data.wq);
 }
 
 static inline bool wbt_should_throttle(struct rq_wb *rwb, struct bio *bio)