sbitmap optimizations
Subsystems (3)
Jens Axboe
maintainerPATCH v3 1/1 sbitmap optimizations
The v2 posting got screwed up somehow, sending a v3 just to make sure things are sane.
Changes:
- Dropped the alignment patch, it should not be needed unless we have debugging enabled of some sort.
- Fumbled the optimized wakeup, it's important we match prep + finish with that interface.
Signature · 1 line
Jens Axboe
Jens Axboe
maintainerPATCH 1/2 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.
--- include/linux/sbitmap.h | 26 +++++++++++++++--- lib/sbitmap.c | 60 ++++++++++++++++++++++++++++++++++++++--- 2 files changed, 78 insertions(+), 8 deletions(-)
include/linux/sbitmap.h +22 -4
@@ -30,14 +30,19 @@ struct seq_file;*/ struct sbitmap_word {/** -* @word: The bitmap word itself. +* @depth: Number of bits being used in @word/@cleared */ -unsigned long word; +unsigned long depth;/** -* @depth: Number of bits being used in @word. +* @word: word holding free bits */ -unsigned long depth; +unsigned long word____cacheline_aligned_in_smp ; + +/** +* @cleared: word holding cleared bits +*/ +unsigned long cleared____cacheline_aligned_in_smp ; }____cacheline_aligned_in_smp ;/**
@@ -310,6 +315,19 @@ static inline void sbitmap_clear_bit(struct sbitmap *sb, unsigned int bitnr)clear_bit (SB_NR_TO_BIT (sb, bitnr),__sbitmap_word (sb, bitnr)); } +/* +* This one is special, since it doesn't actually clear the bit, rather it +* sets the corresponding bit in the ->cleared mask instead. Paired with +* the caller doing sbitmap_batch_clear() if a given index is full, which +* will clear the previously freed entries in the corresponding ->word. +*/ +static inline void sbitmap_deferred_clear_bit (struct sbitmap *sb,unsigned int bitnr) +{ +unsigned long *addr = &sb->map[SB_NR_TO_INDEX (sb, bitnr)].cleared; + +set_bit (SB_NR_TO_BIT (sb, bitnr), addr); +} +static inline void sbitmap_clear_bit_unlock (struct sbitmap *sb,unsigned int bitnr) {
lib/sbitmap.c +56 -4
78 lines not on this page · raw message
Signature · 1 line
2.17.1
Jens Axboe
maintainerPATCH 2/2 sbitmap: optimize wakeup check
In reply to Jens AxboeEven if we have no waiters on any of the sbitmap_queue wait states, we still have to loop every entry to check. We do this for every IO, so the cost adds up.
Shift a bit of the cost to the slow path, when we actually have waiters. Wrap prepare_to_wait_exclusive() and finish_wait(), so we can maintain an internal count of how many are currently active. Then we can simply check this count in sbq_wake_ptr() and not have to loop if we don't have any sleepers.
Convert the two users of sbitmap with waiting, blk-mq-tag and iSCSI.
--- block/blk-mq-tag.c | 13 ++++++------- drivers/target/iscsi/iscsi_target_util.c | 9 ++++++--- include/linux/sbitmap.h | 19 +++++++++++++++++++ lib/sbitmap.c | 21 +++++++++++++++++++++ 4 files changed, 52 insertions(+), 10 deletions(-)
block/blk-mq-tag.c +6 -7
@@ -154,12 +154,13 @@ unsigned int blk_mq_get_tag(struct blk_mq_alloc_data *data)if (tag !=-1 )break ; -prepare_to_wait_exclusive (&ws->wait, &wait, -TASK_UNINTERRUPTIBLE ); +sbitmap_prepare_to_wait (bt, ws, &wait,TASK_UNINTERRUPTIBLE ); tag =__blk_mq_get_tag (data, bt); -if (tag !=-1 ) +if (tag !=-1 ) { +sbitmap_finish_wait (bt, ws, &wait);break ; + }if (data->ctx)blk_mq_put_ctx (data->ctx);
@@ -167,6 +168,8 @@ unsigned int blk_mq_get_tag(struct blk_mq_alloc_data *data) bt_prev = bt;io_schedule (); +sbitmap_finish_wait (bt, ws, &wait); + data->ctx =blk_mq_get_ctx (data->q); data->hctx =blk_mq_map_queue (data->q, data->cmd_flags, data->ctx->cpu);
@@ -176,8 +179,6 @@ unsigned int blk_mq_get_tag(struct blk_mq_alloc_data *data)else bt = &tags->bitmap_tags; -finish_wait (&ws->wait, &wait); -/* * If destination hw queue is changed, fake wake up on * previous queue for compensating the wake up miss, so
@@ -192,8 +193,6 @@ unsigned int blk_mq_get_tag(struct blk_mq_alloc_data *data)if (drop_ctx && data->ctx)blk_mq_put_ctx (data->ctx); -finish_wait (&ws->wait, &wait); - found_tag:return tag + tag_offset; }
drivers/target/iscsi/iscsi_target_util.c +6 -3
@@ -152,22 +152,25 @@ static int iscsit_wait_for_tag(struct se_session *se_sess, int state, int *cpup)int tag =-1 ;DEFINE_WAIT (wait);struct sbq_wait_state *ws; +struct sbitmap_queue *sbq;if (state ==TASK_RUNNING )return tag; - ws = &se_sess->sess_tag_pool.ws[0 ]; + sbq = &se_sess->sess_tag_pool; + ws = &sbq->ws[0 ];for (;;) { -prepare_to_wait_exclusive (&ws->wait, &wait, state); +sbitmap_prepare_to_wait (sbq, ws, &wait, state);if (signal_pending_state (state, current))break ; tag =sbitmap_queue_get (&se_sess->sess_tag_pool, cpup);if (tag >=0 )break ;schedule (); +sbitmap_finish_wait (sbq, ws, &wait); } +sbitmap_finish_wait (sbq, ws, &wait); -finish_wait (&ws->wait, &wait);return tag; }
include/linux/sbitmap.h +19 -0
@@ -130,6 +130,11 @@ struct sbitmap_queue {*/ struct sbq_wait_state *ws; +/* +* @ws_active: count of currently active ws waitqueues +*/ +atomic_t ws_active; +/** * @round_robin: Allocate bits in strict round-robin order. */
@@ -549,4 +554,18 @@ void sbitmap_queue_wake_up(struct sbitmap_queue *sbq);*/ void sbitmap_queue_show (struct sbitmap_queue *sbq,struct seq_file *m); +/* +* Wrapper around prepare_to_wait_exclusive(), which maintains some extra +* internal state. +*/ +void sbitmap_prepare_to_wait (struct sbitmap_queue *sbq, +struct sbq_wait_state *ws, +struct wait_queue_entry *wait,int state); + +/* +* Must be paired with sbitmap_prepare_to_wait(). +*/ +void sbitmap_finish_wait (struct sbitmap_queue *sbq,struct sbq_wait_state *ws, +struct wait_queue_entry *wait); +#endif /* __LINUX_SCALE_BITMAP_H */
lib/sbitmap.c +21 -0
@@ -381,6 +381,7 @@ int sbitmap_queue_init_node(struct sbitmap_queue *sbq, unsigned int depth, sbq->min_shallow_depth =UINT_MAX ; sbq->wake_batch =sbq_calc_wake_batch (sbq, depth);atomic_set (&sbq->wake_index,0 ); +atomic_set (&sbq->ws_active,0 ); sbq->ws =kzalloc_node (SBQ_WAIT_QUEUES *sizeof (*sbq->ws), flags, node);if (!sbq->ws) {
@@ -496,6 +497,9 @@ static struct sbq_wait_state *sbq_wake_ptr(struct sbitmap_queue *sbq) {int i, wake_index; +if (!atomic_read (&sbq->ws_active)) +return NULL ; + wake_index =atomic_read (&sbq->wake_index);for (i =0 ; i <SBQ_WAIT_QUEUES ; i++) {struct sbq_wait_state *ws = &sbq->ws[wake_index];
@@ -636,3 +640,20 @@ void sbitmap_queue_show(struct sbitmap_queue *sbq, struct seq_file *m)seq_printf (m,"min_shallow_depth=%u\n" , sbq->min_shallow_depth); }EXPORT_SYMBOL_GPL (sbitmap_queue_show); + +void sbitmap_prepare_to_wait (struct sbitmap_queue *sbq, +struct sbq_wait_state *ws, +struct wait_queue_entry *wait,int state) +{ +atomic_inc (&sbq->ws_active); +prepare_to_wait_exclusive (&ws->wait, wait, state); +} +EXPORT_SYMBOL_GPL (sbitmap_prepare_to_wait); + +void sbitmap_finish_wait (struct sbitmap_queue *sbq,struct sbq_wait_state *ws, +struct wait_queue_entry *wait) +{ +finish_wait (&ws->wait, wait); +atomic_dec (&sbq->ws_active); +} +EXPORT_SYMBOL_GPL (sbitmap_finish_wait);
Signature · 1 line
2.17.1