kunit: improve KTAP compliance of KUnit test output
[platform/kernel/linux-starfive.git] / lib / sbitmap.c
index 7280ae8..c515072 100644 (file)
@@ -434,6 +434,8 @@ int sbitmap_queue_init_node(struct sbitmap_queue *sbq, unsigned int depth,
        sbq->wake_batch = sbq_calc_wake_batch(sbq, depth);
        atomic_set(&sbq->wake_index, 0);
        atomic_set(&sbq->ws_active, 0);
+       atomic_set(&sbq->completion_cnt, 0);
+       atomic_set(&sbq->wakeup_cnt, 0);
 
        sbq->ws = kzalloc_node(SBQ_WAIT_QUEUES * sizeof(*sbq->ws), flags, node);
        if (!sbq->ws) {
@@ -441,54 +443,33 @@ int sbitmap_queue_init_node(struct sbitmap_queue *sbq, unsigned int depth,
                return -ENOMEM;
        }
 
-       for (i = 0; i < SBQ_WAIT_QUEUES; i++) {
+       for (i = 0; i < SBQ_WAIT_QUEUES; i++)
                init_waitqueue_head(&sbq->ws[i].wait);
-               atomic_set(&sbq->ws[i].wait_cnt, sbq->wake_batch);
-       }
 
        return 0;
 }
 EXPORT_SYMBOL_GPL(sbitmap_queue_init_node);
 
-static inline void __sbitmap_queue_update_wake_batch(struct sbitmap_queue *sbq,
-                                           unsigned int wake_batch)
-{
-       int i;
-
-       if (sbq->wake_batch != wake_batch) {
-               WRITE_ONCE(sbq->wake_batch, wake_batch);
-               /*
-                * Pairs with the memory barrier in sbitmap_queue_wake_up()
-                * to ensure that the batch size is updated before the wait
-                * counts.
-                */
-               smp_mb();
-               for (i = 0; i < SBQ_WAIT_QUEUES; i++)
-                       atomic_set(&sbq->ws[i].wait_cnt, 1);
-       }
-}
-
 static void sbitmap_queue_update_wake_batch(struct sbitmap_queue *sbq,
                                            unsigned int depth)
 {
        unsigned int wake_batch;
 
        wake_batch = sbq_calc_wake_batch(sbq, depth);
-       __sbitmap_queue_update_wake_batch(sbq, wake_batch);
+       if (sbq->wake_batch != wake_batch)
+               WRITE_ONCE(sbq->wake_batch, wake_batch);
 }
 
 void sbitmap_queue_recalculate_wake_batch(struct sbitmap_queue *sbq,
                                            unsigned int users)
 {
        unsigned int wake_batch;
-       unsigned int min_batch;
        unsigned int depth = (sbq->sb.depth + users - 1) / users;
 
-       min_batch = sbq->sb.depth >= (4 * SBQ_WAIT_QUEUES) ? 4 : 1;
-
        wake_batch = clamp_val(depth / SBQ_WAIT_QUEUES,
-                       min_batch, SBQ_WAKE_BATCH);
-       __sbitmap_queue_update_wake_batch(sbq, wake_batch);
+                       1, SBQ_WAKE_BATCH);
+
+       WRITE_ONCE(sbq->wake_batch, wake_batch);
 }
 EXPORT_SYMBOL_GPL(sbitmap_queue_recalculate_wake_batch);
 
@@ -537,11 +518,9 @@ unsigned long __sbitmap_queue_get_batch(struct sbitmap_queue *sbq, int nr_tags,
 
                        get_mask = ((1UL << nr_tags) - 1) << nr;
                        val = READ_ONCE(map->word);
-                       do {
-                               if ((val & ~get_mask) != val)
-                                       goto next;
-                       } while (!atomic_long_try_cmpxchg(ptr, &val,
-                                                         get_mask | val));
+                       while (!atomic_long_try_cmpxchg(ptr, &val,
+                                                         get_mask | val))
+                               ;
                        get_mask = (get_mask & ~val) >> nr;
                        if (get_mask) {
                                *offset = nr + (index << sb->shift);
@@ -576,106 +555,56 @@ void sbitmap_queue_min_shallow_depth(struct sbitmap_queue *sbq,
 }
 EXPORT_SYMBOL_GPL(sbitmap_queue_min_shallow_depth);
 
-static struct sbq_wait_state *sbq_wake_ptr(struct sbitmap_queue *sbq)
+static void __sbitmap_queue_wake_up(struct sbitmap_queue *sbq, int nr)
 {
        int i, wake_index;
 
        if (!atomic_read(&sbq->ws_active))
-               return NULL;
+               return;
 
        wake_index = atomic_read(&sbq->wake_index);
        for (i = 0; i < SBQ_WAIT_QUEUES; i++) {
                struct sbq_wait_state *ws = &sbq->ws[wake_index];
 
-               if (waitqueue_active(&ws->wait) && atomic_read(&ws->wait_cnt)) {
-                       if (wake_index != atomic_read(&sbq->wake_index))
-                               atomic_set(&sbq->wake_index, wake_index);
-                       return ws;
-               }
-
+               /*
+                * Advance the index before checking the current queue.
+                * It improves fairness, by ensuring the queue doesn't
+                * need to be fully emptied before trying to wake up
+                * from the next one.
+                */
                wake_index = sbq_index_inc(wake_index);
+
+               /*
+                * It is sufficient to wake up at least one waiter to
+                * guarantee forward progress.
+                */
+               if (waitqueue_active(&ws->wait) &&
+                   wake_up_nr(&ws->wait, nr))
+                       break;
        }
 
-       return NULL;
+       if (wake_index != atomic_read(&sbq->wake_index))
+               atomic_set(&sbq->wake_index, wake_index);
 }
 
-static bool __sbq_wake_up(struct sbitmap_queue *sbq, int *nr)
+void sbitmap_queue_wake_up(struct sbitmap_queue *sbq, int nr)
 {
-       struct sbq_wait_state *ws;
-       unsigned int wake_batch;
-       int wait_cnt, cur, sub;
-       bool ret;
+       unsigned int wake_batch = READ_ONCE(sbq->wake_batch);
+       unsigned int wakeups;
 
-       if (*nr <= 0)
-               return false;
+       if (!atomic_read(&sbq->ws_active))
+               return;
 
-       ws = sbq_wake_ptr(sbq);
-       if (!ws)
-               return false;
+       atomic_add(nr, &sbq->completion_cnt);
+       wakeups = atomic_read(&sbq->wakeup_cnt);
 
-       cur = atomic_read(&ws->wait_cnt);
        do {
-               /*
-                * For concurrent callers of this, callers should call this
-                * function again to wakeup a new batch on a different 'ws'.
-                */
-               if (cur == 0)
-                       return true;
-               sub = min(*nr, cur);
-               wait_cnt = cur - sub;
-       } while (!atomic_try_cmpxchg(&ws->wait_cnt, &cur, wait_cnt));
-
-       /*
-        * If we decremented queue without waiters, retry to avoid lost
-        * wakeups.
-        */
-       if (wait_cnt > 0)
-               return !waitqueue_active(&ws->wait);
-
-       *nr -= sub;
-
-       /*
-        * When wait_cnt == 0, we have to be particularly careful as we are
-        * responsible to reset wait_cnt regardless whether we've actually
-        * woken up anybody. But in case we didn't wakeup anybody, we still
-        * need to retry.
-        */
-       ret = !waitqueue_active(&ws->wait);
-       wake_batch = READ_ONCE(sbq->wake_batch);
-
-       /*
-        * Wake up first in case that concurrent callers decrease wait_cnt
-        * while waitqueue is empty.
-        */
-       wake_up_nr(&ws->wait, wake_batch);
+               if (atomic_read(&sbq->completion_cnt) - wakeups < wake_batch)
+                       return;
+       } while (!atomic_try_cmpxchg(&sbq->wakeup_cnt,
+                                    &wakeups, wakeups + wake_batch));
 
-       /*
-        * Pairs with the memory barrier in sbitmap_queue_resize() to
-        * ensure that we see the batch size update before the wait
-        * count is reset.
-        *
-        * Also pairs with the implicit barrier between decrementing wait_cnt
-        * and checking for waitqueue_active() to make sure waitqueue_active()
-        * sees result of the wakeup if atomic_dec_return() has seen the result
-        * of atomic_set().
-        */
-       smp_mb__before_atomic();
-
-       /*
-        * Increase wake_index before updating wait_cnt, otherwise concurrent
-        * callers can see valid wait_cnt in old waitqueue, which can cause
-        * invalid wakeup on the old waitqueue.
-        */
-       sbq_index_atomic_inc(&sbq->wake_index);
-       atomic_set(&ws->wait_cnt, wake_batch);
-
-       return ret || *nr;
-}
-
-void sbitmap_queue_wake_up(struct sbitmap_queue *sbq, int nr)
-{
-       while (__sbq_wake_up(sbq, &nr))
-               ;
+       __sbitmap_queue_wake_up(sbq, wake_batch);
 }
 EXPORT_SYMBOL_GPL(sbitmap_queue_wake_up);
 
@@ -792,9 +721,7 @@ void sbitmap_queue_show(struct sbitmap_queue *sbq, struct seq_file *m)
        seq_puts(m, "ws={\n");
        for (i = 0; i < SBQ_WAIT_QUEUES; i++) {
                struct sbq_wait_state *ws = &sbq->ws[i];
-
-               seq_printf(m, "\t{.wait_cnt=%d, .wait=%s},\n",
-                          atomic_read(&ws->wait_cnt),
+               seq_printf(m, "\t{.wait=%s},\n",
                           waitqueue_active(&ws->wait) ? "active" : "inactive");
        }
        seq_puts(m, "}\n");