[RESEND PATCH 02/13] idr: Add idr_for_each_entry_tagged()

From: Matthew Wilcox
Date: Tue Jul 11 2017 - 08:59:55 EST


Add the ability to iterate over tagged entries in the IDR with
idr_get_next_tag() and idr_for_each_entry_tagged().

Signed-off-by: Matthew Wilcox <mawilcox@xxxxxxxxxxxxx>
---
include/linux/idr.h | 15 ++++++++++++++-
lib/idr.c | 30 +++++++++++++++++++++++++++++-
tools/testing/radix-tree/idr-test.c | 18 ++++++++++--------
tools/testing/radix-tree/test.c | 9 +++++++--
tools/testing/radix-tree/test.h | 1 +
5 files changed, 61 insertions(+), 12 deletions(-)

diff --git a/include/linux/idr.h b/include/linux/idr.h
index 7eb4432..9f71e63 100644
--- a/include/linux/idr.h
+++ b/include/linux/idr.h
@@ -84,7 +84,8 @@ static inline void idr_set_cursor(struct idr *idr, unsigned int val)
int idr_alloc_cyclic(struct idr *, void *entry, int start, int end, gfp_t);
int idr_for_each(const struct idr *,
int (*fn)(int id, void *p, void *data), void *data);
-void *idr_get_next(struct idr *, int *nextid);
+void *idr_get_next(const struct idr *, int *nextid);
+void *idr_get_next_tag(const struct idr *, int *nextid, unsigned int tag);
void *idr_replace(struct idr *, void *, int id);
void idr_destroy(struct idr *);

@@ -213,6 +214,18 @@ static inline void *idr_find(const struct idr *idr, int id)
entry; \
++id, (entry) = idr_get_next((idr), &(id)))

+/**
+ * idr_for_each_entry_tagged - iterate over IDs with a set tag
+ * @idr: IDR handle
+ * @entry: The pointer stored in @idr
+ * @id: The index of @entry in @idr
+ * @tag: tag to search for
+ */
+#define idr_for_each_entry_tagged(idr, entry, id, tag) \
+ for (id = 0; \
+ ((entry) = idr_get_next_tag(idr, &(id), (tag))) != NULL; \
+ ++id)
+
/*
* IDA - IDR based id allocator, use when translation from id to
* pointer isn't necessary.
diff --git a/lib/idr.c b/lib/idr.c
index b13682b..68e39c3 100644
--- a/lib/idr.c
+++ b/lib/idr.c
@@ -120,7 +120,7 @@ int idr_for_each(const struct idr *idr,
* to the ID of the found value. To use in a loop, the value pointed to by
* nextid must be incremented by the user.
*/
-void *idr_get_next(struct idr *idr, int *nextid)
+void *idr_get_next(const struct idr *idr, int *nextid)
{
struct radix_tree_iter iter;
void __rcu **slot;
@@ -135,6 +135,34 @@ void *idr_get_next(struct idr *idr, int *nextid)
EXPORT_SYMBOL(idr_get_next);

/**
+ * idr_get_next_tag - Find next tagged entry
+ * @idr: idr handle
+ * @nextid: Pointer to lowest possible ID to return
+ * @tag: tag to search for
+ *
+ * Returns the next tagged entry in the tree with an ID greater than
+ * or equal to the value pointed to by @nextid. On exit, @nextid is updated
+ * to the ID of the found value. To use in a loop, the value pointed to by
+ * nextid must be incremented by the user. If a NULL entry is tagged, it
+ * will be returned.
+ */
+void *idr_get_next_tag(const struct idr *idr, int *nextid, unsigned int tag)
+{
+ struct radix_tree_iter iter;
+ void __rcu **slot;
+
+ radix_tree_iter_init(&iter, *nextid);
+ slot = radix_tree_next_chunk(&idr->idr_rt, &iter,
+ RADIX_TREE_ITER_TAGGED | tag);
+ if (!slot)
+ return NULL;
+
+ *nextid = iter.index;
+ return rcu_dereference_raw(*slot);
+}
+EXPORT_UNUSED_SYMBOL(idr_get_next_tag);
+
+/**
* idr_replace - replace pointer for given id
* @idr: idr handle
* @ptr: New pointer to associate with the ID
diff --git a/tools/testing/radix-tree/idr-test.c b/tools/testing/radix-tree/idr-test.c
index fd94bee..334ce1c 100644
--- a/tools/testing/radix-tree/idr-test.c
+++ b/tools/testing/radix-tree/idr-test.c
@@ -23,19 +23,15 @@

int item_idr_free(int id, void *p, void *data)
{
- struct item *item = p;
- assert(item->index == id);
- free(p);
-
+ item_free(p, id);
return 0;
}

void item_idr_remove(struct idr *idr, int id)
{
struct item *item = idr_find(idr, id);
- assert(item->index == id);
idr_remove(idr, id);
- free(item);
+ item_free(item, id);
}

void idr_alloc_test(void)
@@ -139,11 +135,13 @@ void idr_null_test(void)

void idr_tag_test(void)
{
- unsigned int i;
+ int i;
DEFINE_IDR(idr);
+ struct item *item;

for (i = 0; i < 100; i++) {
- assert(idr_alloc(&idr, NULL, 0, 0, GFP_KERNEL) == i);
+ item = item_create(i, 0);
+ assert(idr_alloc(&idr, item, 0, 0, GFP_KERNEL) == i);
if (i % 7 == 0)
idr_tag_set(&idr, i, IDR_TEST);
}
@@ -157,6 +155,10 @@ void idr_tag_test(void)
assert(idr_tag_get(&idr, i, IDR_TEST) == (i % 14 == 7));
}

+ idr_for_each_entry_tagged(&idr, item, i, IDR_TEST) {
+ assert(item->index % 14 == 7);
+ }
+
idr_for_each(&idr, item_idr_free, &idr);
idr_destroy(&idr);
}
diff --git a/tools/testing/radix-tree/test.c b/tools/testing/radix-tree/test.c
index 1a257d7..74f8e5c 100644
--- a/tools/testing/radix-tree/test.c
+++ b/tools/testing/radix-tree/test.c
@@ -62,13 +62,18 @@ void item_sanity(struct item *item, unsigned long index)
assert((item->index | mask) == (index | mask));
}

+void item_free(struct item *item, unsigned long index)
+{
+ item_sanity(item, index);
+ free(item);
+}
+
int item_delete(struct radix_tree_root *root, unsigned long index)
{
struct item *item = radix_tree_delete(root, index);

if (item) {
- item_sanity(item, index);
- free(item);
+ item_free(item, index);
return 1;
}
return 0;
diff --git a/tools/testing/radix-tree/test.h b/tools/testing/radix-tree/test.h
index 0f8220c..cbabea1 100644
--- a/tools/testing/radix-tree/test.h
+++ b/tools/testing/radix-tree/test.h
@@ -13,6 +13,7 @@ struct item {
int item_insert(struct radix_tree_root *root, unsigned long index);
int item_insert_order(struct radix_tree_root *root, unsigned long index,
unsigned order);
+void item_free(struct item *item, unsigned long index);
int item_delete(struct radix_tree_root *root, unsigned long index);
struct item *item_lookup(struct radix_tree_root *root, unsigned long index);

--
1.8.3.1