|
@@ -457,7 +457,7 @@ static struct sbq_wait_state *sbq_wake_ptr(struct sbitmap_queue *sbq)
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
-static void sbq_wake_up(struct sbitmap_queue *sbq)
|
|
|
+static bool __sbq_wake_up(struct sbitmap_queue *sbq)
|
|
|
{
|
|
|
struct sbq_wait_state *ws;
|
|
|
unsigned int wake_batch;
|
|
@@ -474,28 +474,43 @@ static void sbq_wake_up(struct sbitmap_queue *sbq)
|
|
|
|
|
|
ws = sbq_wake_ptr(sbq);
|
|
|
if (!ws)
|
|
|
- return;
|
|
|
+ return false;
|
|
|
|
|
|
wait_cnt = atomic_dec_return(&ws->wait_cnt);
|
|
|
if (wait_cnt <= 0) {
|
|
|
+ int ret;
|
|
|
+
|
|
|
wake_batch = READ_ONCE(sbq->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.
|
|
|
*/
|
|
|
smp_mb__before_atomic();
|
|
|
+
|
|
|
/*
|
|
|
- * If there are concurrent callers to sbq_wake_up(), the last
|
|
|
- * one to decrement the wait count below zero will bump it back
|
|
|
- * up. If there is a concurrent resize, the count reset will
|
|
|
- * either cause the cmpxchg to fail or overwrite after the
|
|
|
- * cmpxchg.
|
|
|
+ * For concurrent callers of this, the one that failed the
|
|
|
+ * atomic_cmpxhcg() race should call this function again
|
|
|
+ * to wakeup a new batch on a different 'ws'.
|
|
|
*/
|
|
|
- atomic_cmpxchg(&ws->wait_cnt, wait_cnt, wait_cnt + wake_batch);
|
|
|
- sbq_index_atomic_inc(&sbq->wake_index);
|
|
|
- wake_up_nr(&ws->wait, wake_batch);
|
|
|
+ ret = atomic_cmpxchg(&ws->wait_cnt, wait_cnt, wake_batch);
|
|
|
+ if (ret == wait_cnt) {
|
|
|
+ sbq_index_atomic_inc(&sbq->wake_index);
|
|
|
+ wake_up_nr(&ws->wait, wake_batch);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
}
|
|
|
+
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+static void sbq_wake_up(struct sbitmap_queue *sbq)
|
|
|
+{
|
|
|
+ while (__sbq_wake_up(sbq))
|
|
|
+ ;
|
|
|
}
|
|
|
|
|
|
void sbitmap_queue_clear(struct sbitmap_queue *sbq, unsigned int nr,
|