Skip to content

Commit ea86ea2

Browse files
committed
sbitmap: ammortize cost of clearing bits
sbitmap maintains a set of words that we use to set and clear bits, with each bit representing a tag for blk-mq. Even though we spread the bits out and maintain a hint cache, one particular bit allocated will end up being cleared in the exact same spot. This introduces batched clearing of bits. Instead of clearing a given bit, the same bit is set in a cleared/free mask instead. If we fail allocating a bit from a given word, then we check the free mask, and batch move those cleared bits at that time. This trades 64 atomic bitops for 2 cmpxchg(). In a threaded poll test case, half the overhead of getting and clearing tags is removed with this change. On another poll test case with a single thread, performance is unchanged. Reviewed-by: Omar Sandoval <[email protected]> Signed-off-by: Jens Axboe <[email protected]>
1 parent 531724a commit ea86ea2

File tree

2 files changed

+100
-14
lines changed

2 files changed

+100
-14
lines changed

include/linux/sbitmap.h

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,24 @@ struct seq_file;
3030
*/
3131
struct sbitmap_word {
3232
/**
33-
* @word: The bitmap word itself.
33+
* @depth: Number of bits being used in @word/@cleared
3434
*/
35-
unsigned long word;
35+
unsigned long depth;
3636

3737
/**
38-
* @depth: Number of bits being used in @word.
38+
* @word: word holding free bits
3939
*/
40-
unsigned long depth;
40+
unsigned long word ____cacheline_aligned_in_smp;
41+
42+
/**
43+
* @cleared: word holding cleared bits
44+
*/
45+
unsigned long cleared ____cacheline_aligned_in_smp;
46+
47+
/**
48+
* @swap_lock: Held while swapping word <-> cleared
49+
*/
50+
spinlock_t swap_lock;
4151
} ____cacheline_aligned_in_smp;
4252

4353
/**
@@ -310,6 +320,19 @@ static inline void sbitmap_clear_bit(struct sbitmap *sb, unsigned int bitnr)
310320
clear_bit(SB_NR_TO_BIT(sb, bitnr), __sbitmap_word(sb, bitnr));
311321
}
312322

323+
/*
324+
* This one is special, since it doesn't actually clear the bit, rather it
325+
* sets the corresponding bit in the ->cleared mask instead. Paired with
326+
* the caller doing sbitmap_batch_clear() if a given index is full, which
327+
* will clear the previously freed entries in the corresponding ->word.
328+
*/
329+
static inline void sbitmap_deferred_clear_bit(struct sbitmap *sb, unsigned int bitnr)
330+
{
331+
unsigned long *addr = &sb->map[SB_NR_TO_INDEX(sb, bitnr)].cleared;
332+
333+
set_bit(SB_NR_TO_BIT(sb, bitnr), addr);
334+
}
335+
313336
static inline void sbitmap_clear_bit_unlock(struct sbitmap *sb,
314337
unsigned int bitnr)
315338
{
@@ -321,8 +344,6 @@ static inline int sbitmap_test_bit(struct sbitmap *sb, unsigned int bitnr)
321344
return test_bit(SB_NR_TO_BIT(sb, bitnr), __sbitmap_word(sb, bitnr));
322345
}
323346

324-
unsigned int sbitmap_weight(const struct sbitmap *sb);
325-
326347
/**
327348
* sbitmap_show() - Dump &struct sbitmap information to a &struct seq_file.
328349
* @sb: Bitmap to show.

lib/sbitmap.c

Lines changed: 73 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ int sbitmap_init_node(struct sbitmap *sb, unsigned int depth, int shift,
5959
for (i = 0; i < sb->map_nr; i++) {
6060
sb->map[i].depth = min(depth, bits_per_word);
6161
depth -= sb->map[i].depth;
62+
spin_lock_init(&sb->map[i].swap_lock);
6263
}
6364
return 0;
6465
}
@@ -111,6 +112,57 @@ static int __sbitmap_get_word(unsigned long *word, unsigned long depth,
111112
return nr;
112113
}
113114

115+
/*
116+
* See if we have deferred clears that we can batch move
117+
*/
118+
static inline bool sbitmap_deferred_clear(struct sbitmap *sb, int index)
119+
{
120+
unsigned long mask, val;
121+
bool ret = false;
122+
123+
spin_lock(&sb->map[index].swap_lock);
124+
125+
if (!sb->map[index].cleared)
126+
goto out_unlock;
127+
128+
/*
129+
* First get a stable cleared mask, setting the old mask to 0.
130+
*/
131+
do {
132+
mask = sb->map[index].cleared;
133+
} while (cmpxchg(&sb->map[index].cleared, mask, 0) != mask);
134+
135+
/*
136+
* Now clear the masked bits in our free word
137+
*/
138+
do {
139+
val = sb->map[index].word;
140+
} while (cmpxchg(&sb->map[index].word, val, val & ~mask) != val);
141+
142+
ret = true;
143+
out_unlock:
144+
spin_unlock(&sb->map[index].swap_lock);
145+
return ret;
146+
}
147+
148+
static int sbitmap_find_bit_in_index(struct sbitmap *sb, int index,
149+
unsigned int alloc_hint, bool round_robin)
150+
{
151+
int nr;
152+
153+
do {
154+
nr = __sbitmap_get_word(&sb->map[index].word,
155+
sb->map[index].depth, alloc_hint,
156+
!round_robin);
157+
if (nr != -1)
158+
break;
159+
if (!sbitmap_deferred_clear(sb, index))
160+
break;
161+
} while (1);
162+
163+
return nr;
164+
}
165+
114166
int sbitmap_get(struct sbitmap *sb, unsigned int alloc_hint, bool round_robin)
115167
{
116168
unsigned int i, index;
@@ -129,9 +181,8 @@ int sbitmap_get(struct sbitmap *sb, unsigned int alloc_hint, bool round_robin)
129181
alloc_hint = 0;
130182

131183
for (i = 0; i < sb->map_nr; i++) {
132-
nr = __sbitmap_get_word(&sb->map[index].word,
133-
sb->map[index].depth, alloc_hint,
134-
!round_robin);
184+
nr = sbitmap_find_bit_in_index(sb, index, alloc_hint,
185+
round_robin);
135186
if (nr != -1) {
136187
nr += index << sb->shift;
137188
break;
@@ -206,23 +257,36 @@ bool sbitmap_any_bit_clear(const struct sbitmap *sb)
206257
}
207258
EXPORT_SYMBOL_GPL(sbitmap_any_bit_clear);
208259

209-
unsigned int sbitmap_weight(const struct sbitmap *sb)
260+
static unsigned int __sbitmap_weight(const struct sbitmap *sb, bool set)
210261
{
211262
unsigned int i, weight = 0;
212263

213264
for (i = 0; i < sb->map_nr; i++) {
214265
const struct sbitmap_word *word = &sb->map[i];
215266

216-
weight += bitmap_weight(&word->word, word->depth);
267+
if (set)
268+
weight += bitmap_weight(&word->word, word->depth);
269+
else
270+
weight += bitmap_weight(&word->cleared, word->depth);
217271
}
218272
return weight;
219273
}
220-
EXPORT_SYMBOL_GPL(sbitmap_weight);
274+
275+
static unsigned int sbitmap_weight(const struct sbitmap *sb)
276+
{
277+
return __sbitmap_weight(sb, true);
278+
}
279+
280+
static unsigned int sbitmap_cleared(const struct sbitmap *sb)
281+
{
282+
return __sbitmap_weight(sb, false);
283+
}
221284

222285
void sbitmap_show(struct sbitmap *sb, struct seq_file *m)
223286
{
224287
seq_printf(m, "depth=%u\n", sb->depth);
225-
seq_printf(m, "busy=%u\n", sbitmap_weight(sb));
288+
seq_printf(m, "busy=%u\n", sbitmap_weight(sb) - sbitmap_cleared(sb));
289+
seq_printf(m, "cleared=%u\n", sbitmap_cleared(sb));
226290
seq_printf(m, "bits_per_word=%u\n", 1U << sb->shift);
227291
seq_printf(m, "map_nr=%u\n", sb->map_nr);
228292
}
@@ -514,7 +578,8 @@ EXPORT_SYMBOL_GPL(sbitmap_queue_wake_up);
514578
void sbitmap_queue_clear(struct sbitmap_queue *sbq, unsigned int nr,
515579
unsigned int cpu)
516580
{
517-
sbitmap_clear_bit_unlock(&sbq->sb, nr);
581+
sbitmap_deferred_clear_bit(&sbq->sb, nr);
582+
518583
/*
519584
* Pairs with the memory barrier in set_current_state() to ensure the
520585
* proper ordering of clear_bit_unlock()/waitqueue_active() in the waker

0 commit comments

Comments
 (0)