sbitmap: introduce __sbitmap_for_each_set()
authorMing Lei <ming.lei@redhat.com>
Sat, 14 Oct 2017 09:22:27 +0000 (17:22 +0800)
committerJens Axboe <axboe@kernel.dk>
Wed, 1 Nov 2017 14:20:02 +0000 (08:20 -0600)
For blk-mq, we need to be able to iterate software queues starting
from any queue in a round robin fashion, so introduce this helper.

Reviewed-by: Omar Sandoval <osandov@fb.com>
Cc: Omar Sandoval <osandov@fb.com>
Signed-off-by: Ming Lei <ming.lei@redhat.com>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
include/linux/sbitmap.h

index a1904aadbc45004ba18c8db06f184a164908cd46..0dcc60e820de71cb21199bbbcf515aed2c1d7645 100644 (file)
@@ -211,10 +211,14 @@ bool sbitmap_any_bit_set(const struct sbitmap *sb);
  */
 bool sbitmap_any_bit_clear(const struct sbitmap *sb);
 
+#define SB_NR_TO_INDEX(sb, bitnr) ((bitnr) >> (sb)->shift)
+#define SB_NR_TO_BIT(sb, bitnr) ((bitnr) & ((1U << (sb)->shift) - 1U))
+
 typedef bool (*sb_for_each_fn)(struct sbitmap *, unsigned int, void *);
 
 /**
- * sbitmap_for_each_set() - Iterate over each set bit in a &struct sbitmap.
+ * __sbitmap_for_each_set() - Iterate over each set bit in a &struct sbitmap.
+ * @start: Where to start the iteration.
  * @sb: Bitmap to iterate over.
  * @fn: Callback. Should return true to continue or false to break early.
  * @data: Pointer to pass to callback.
@@ -222,35 +226,61 @@ typedef bool (*sb_for_each_fn)(struct sbitmap *, unsigned int, void *);
  * This is inline even though it's non-trivial so that the function calls to the
  * callback will hopefully get optimized away.
  */
-static inline void sbitmap_for_each_set(struct sbitmap *sb, sb_for_each_fn fn,
-                                       void *data)
+static inline void __sbitmap_for_each_set(struct sbitmap *sb,
+                                         unsigned int start,
+                                         sb_for_each_fn fn, void *data)
 {
-       unsigned int i;
+       unsigned int index;
+       unsigned int nr;
+       unsigned int scanned = 0;
 
-       for (i = 0; i < sb->map_nr; i++) {
-               struct sbitmap_word *word = &sb->map[i];
-               unsigned int off, nr;
+       if (start >= sb->depth)
+               start = 0;
+       index = SB_NR_TO_INDEX(sb, start);
+       nr = SB_NR_TO_BIT(sb, start);
 
-               if (!word->word)
-                       continue;
+       while (scanned < sb->depth) {
+               struct sbitmap_word *word = &sb->map[index];
+               unsigned int depth = min_t(unsigned int, word->depth - nr,
+                                          sb->depth - scanned);
 
-               nr = 0;
-               off = i << sb->shift;
+               scanned += depth;
+               if (!word->word)
+                       goto next;
+
+               /*
+                * On the first iteration of the outer loop, we need to add the
+                * bit offset back to the size of the word for find_next_bit().
+                * On all other iterations, nr is zero, so this is a noop.
+                */
+               depth += nr;
                while (1) {
-                       nr = find_next_bit(&word->word, word->depth, nr);
-                       if (nr >= word->depth)
+                       nr = find_next_bit(&word->word, depth, nr);
+                       if (nr >= depth)
                                break;
-
-                       if (!fn(sb, off + nr, data))
+                       if (!fn(sb, (index << sb->shift) + nr, data))
                                return;
 
                        nr++;
                }
+next:
+               nr = 0;
+               if (++index >= sb->map_nr)
+                       index = 0;
        }
 }
 
-#define SB_NR_TO_INDEX(sb, bitnr) ((bitnr) >> (sb)->shift)
-#define SB_NR_TO_BIT(sb, bitnr) ((bitnr) & ((1U << (sb)->shift) - 1U))
+/**
+ * sbitmap_for_each_set() - Iterate over each set bit in a &struct sbitmap.
+ * @sb: Bitmap to iterate over.
+ * @fn: Callback. Should return true to continue or false to break early.
+ * @data: Pointer to pass to callback.
+ */
+static inline void sbitmap_for_each_set(struct sbitmap *sb, sb_for_each_fn fn,
+                                       void *data)
+{
+       __sbitmap_for_each_set(sb, 0, fn, data);
+}
 
 static inline unsigned long *__sbitmap_word(struct sbitmap *sb,
                                            unsigned int bitnr)