From: Matthew Wilcox <mawil...@microsoft.com> idr_alloc_ul fits better with other parts of the Linux kernel where we need to name a function based on the types it operates on.
It uses a 'nextid' pointer argument instead of separate minimum ID and output assigned ID, (like idr_get_next), reducing the number of arguments by one. It also takes a 'max' argument rather than an 'end' argument (unlike idr_alloc, but the semantics of 'end' don't work for unsigned long arguments). And its return value is an errno, so mark it as __must_check. Includes kernel-doc for idr_alloc_ul, which idr_alloc_ext didn't have, and I realised we were missing a test-case where idr_alloc_cyclic wraps around INT_MAX. Chris Mi <chr...@mellanox.com> has promised to contribute test-cases for idr_alloc_ul. Signed-off-by: Matthew Wilcox <mawil...@microsoft.com> --- include/linux/idr.h | 55 ++------------------- include/linux/radix-tree.h | 17 +------ lib/idr.c | 99 +++++++++++++++++++++++++++++-------- lib/radix-tree.c | 3 +- net/sched/cls_u32.c | 20 ++++---- tools/testing/radix-tree/idr-test.c | 17 +++++++ 6 files changed, 111 insertions(+), 100 deletions(-) diff --git a/include/linux/idr.h b/include/linux/idr.h index 9b2fd6f408b2..344380fd0887 100644 --- a/include/linux/idr.h +++ b/include/linux/idr.h @@ -13,7 +13,6 @@ #define __IDR_H__ #include <linux/radix-tree.h> -#include <linux/bug.h> #include <linux/gfp.h> #include <linux/percpu.h> @@ -82,55 +81,9 @@ static inline void idr_set_cursor(struct idr *idr, unsigned int val) void idr_preload(gfp_t gfp_mask); -int idr_alloc_cmn(struct idr *idr, void *ptr, unsigned long *index, - unsigned long start, unsigned long end, gfp_t gfp, - bool ext); - -/** - * idr_alloc - allocate an id - * @idr: idr handle - * @ptr: pointer to be associated with the new id - * @start: the minimum id (inclusive) - * @end: the maximum id (exclusive) - * @gfp: memory allocation flags - * - * Allocates an unused ID in the range [start, end). Returns -ENOSPC - * if there are no unused IDs in that range. - * - * Note that @end is treated as max when <= 0. This is to always allow - * using @start + N as @end as long as N is inside integer range. - * - * Simultaneous modifications to the @idr are not allowed and should be - * prevented by the user, usually with a lock. idr_alloc() may be called - * concurrently with read-only accesses to the @idr, such as idr_find() and - * idr_for_each_entry(). - */ -static inline int idr_alloc(struct idr *idr, void *ptr, - int start, int end, gfp_t gfp) -{ - unsigned long id; - int ret; - - if (WARN_ON_ONCE(start < 0)) - return -EINVAL; - - ret = idr_alloc_cmn(idr, ptr, &id, start, end, gfp, false); - - if (ret) - return ret; - - return id; -} - -static inline int idr_alloc_ext(struct idr *idr, void *ptr, - unsigned long *index, - unsigned long start, - unsigned long end, - gfp_t gfp) -{ - return idr_alloc_cmn(idr, ptr, index, start, end, gfp, true); -} - +int idr_alloc(struct idr *, void *, int start, int end, gfp_t); +int __must_check idr_alloc_ul(struct idr *, void *, unsigned long *nextid, + unsigned long max, gfp_t); 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); @@ -163,7 +116,7 @@ static inline int __must_check idr_alloc_u32(struct idr *idr, void *ptr, u32 *nextid, unsigned long max, gfp_t gfp) { unsigned long tmp = *nextid; - int ret = idr_alloc_ext(idr, ptr, &tmp, tmp, max + 1, gfp); + int ret = idr_alloc_ul(idr, ptr, &tmp, max, gfp); *nextid = tmp; return ret; } diff --git a/include/linux/radix-tree.h b/include/linux/radix-tree.h index 23a9c89c7ad9..fc55ff31eca7 100644 --- a/include/linux/radix-tree.h +++ b/include/linux/radix-tree.h @@ -356,24 +356,9 @@ int radix_tree_split(struct radix_tree_root *, unsigned long index, int radix_tree_join(struct radix_tree_root *, unsigned long index, unsigned new_order, void *); -void __rcu **idr_get_free_cmn(struct radix_tree_root *root, +void __rcu **idr_get_free(struct radix_tree_root *root, struct radix_tree_iter *iter, gfp_t gfp, unsigned long max); -static inline void __rcu **idr_get_free(struct radix_tree_root *root, - struct radix_tree_iter *iter, - gfp_t gfp, - int end) -{ - return idr_get_free_cmn(root, iter, gfp, end > 0 ? end - 1 : INT_MAX); -} - -static inline void __rcu **idr_get_free_ext(struct radix_tree_root *root, - struct radix_tree_iter *iter, - gfp_t gfp, - unsigned long end) -{ - return idr_get_free_cmn(root, iter, gfp, end - 1); -} enum { RADIX_TREE_ITER_TAG_MASK = 0x0f, /* tag index in lower nybble */ diff --git a/lib/idr.c b/lib/idr.c index 577bfd4fe5c2..103afb97b4bd 100644 --- a/lib/idr.c +++ b/lib/idr.c @@ -1,4 +1,5 @@ #include <linux/bitmap.h> +#include <linux/bug.h> #include <linux/export.h> #include <linux/idr.h> #include <linux/slab.h> @@ -7,32 +8,85 @@ DEFINE_PER_CPU(struct ida_bitmap *, ida_bitmap); static DEFINE_SPINLOCK(simple_ida_lock); -int idr_alloc_cmn(struct idr *idr, void *ptr, unsigned long *index, - unsigned long start, unsigned long end, gfp_t gfp, - bool ext) +/** + * idr_alloc_ul() - Allocate a large ID. + * @idr: IDR handle. + * @ptr: Pointer to be associated with the new ID. + * @nextid: Pointer to minimum and new ID. + * @max: The maximum ID to allocate (inclusive). + * @gfp: Memory allocation flags. + * + * Allocates an unused ID in the range [*nextid, max] and updates the @nextid + * pointer with the newly assigned ID. Note that @max differs by 1 from the + * @end parameter to idr_alloc(). + * + * The caller should provide their own locking to ensure that two concurrent + * modifications to the IDR are not possible. Read-only accesses to the + * IDR may be done under the RCU read lock or may exclude simultaneous + * writers. + * + * Return: 0 on success, -ENOMEM for memory allocation errors, -ENOSPC if + * there are no free IDs in the range. + */ +int idr_alloc_ul(struct idr *idr, void *ptr, unsigned long *nextid, + unsigned long max, gfp_t gfp) { struct radix_tree_iter iter; void __rcu **slot; if (WARN_ON_ONCE(radix_tree_is_internal_node(ptr))) return -EINVAL; + if (WARN_ON_ONCE(!(idr->idr_rt.gfp_mask & ROOT_IS_IDR))) + idr->idr_rt.gfp_mask |= IDR_RT_MARKER; - radix_tree_iter_init(&iter, start); - if (ext) - slot = idr_get_free_ext(&idr->idr_rt, &iter, gfp, end); - else - slot = idr_get_free(&idr->idr_rt, &iter, gfp, end); + radix_tree_iter_init(&iter, *nextid); + slot = idr_get_free(&idr->idr_rt, &iter, gfp, max); if (IS_ERR(slot)) return PTR_ERR(slot); radix_tree_iter_replace(&idr->idr_rt, &iter, slot, ptr); radix_tree_iter_tag_clear(&idr->idr_rt, &iter, IDR_FREE); - if (index) - *index = iter.index; + *nextid = iter.index; return 0; } -EXPORT_SYMBOL_GPL(idr_alloc_cmn); +EXPORT_SYMBOL_GPL(idr_alloc_ul); + +/** + * idr_alloc - allocate an id + * @idr: idr handle + * @ptr: pointer to be associated with the new id + * @start: the minimum id (inclusive) + * @end: the maximum id (exclusive) + * @gfp: memory allocation flags + * + * Allocates an unused ID in the range [start, end). Returns -ENOSPC + * if there are no unused IDs in that range. + * + * Note that @end is treated as max when <= 0. This is to always allow + * using @start + N as @end as long as N is inside integer range. + * + * Simultaneous modifications to the @idr are not allowed and should be + * prevented by the user, usually with a lock. idr_alloc() may be called + * concurrently with read-only accesses to the @idr, such as idr_find() and + * idr_for_each_entry(). + */ +int idr_alloc(struct idr *idr, void *ptr, int start, int end, gfp_t gfp) +{ + unsigned long id = start; + int ret; + + if (WARN_ON_ONCE(start < 0)) + return -EINVAL; + + ret = idr_alloc_ul(idr, ptr, &id, end > 0 ? end - 1 : INT_MAX, gfp); + + if (ret) + return ret; + + return id; +} +EXPORT_SYMBOL_GPL(idr_alloc); /** * idr_alloc_cyclic - allocate new idr entry in a cyclical fashion @@ -48,18 +102,21 @@ EXPORT_SYMBOL_GPL(idr_alloc_cmn); */ int idr_alloc_cyclic(struct idr *idr, void *ptr, int start, int end, gfp_t gfp) { - int id, curr = idr->idr_next; - - if (curr < start) - curr = start; + unsigned long id = idr->idr_next; + int err, max = end > 0 ? end - 1 : INT_MAX; - id = idr_alloc(idr, ptr, curr, end, gfp); - if ((id == -ENOSPC) && (curr > start)) - id = idr_alloc(idr, ptr, start, curr, gfp); + if ((int)id < start) + id = start; - if (id >= 0) - idr->idr_next = id + 1U; + err = idr_alloc_ul(idr, ptr, &id, max, gfp); + if ((err == -ENOSPC) && (id > start)) { + id = start; + err = idr_alloc_ul(idr, ptr, &id, max, gfp); + } + if (err) + return err; + idr->idr_next = id + 1; return id; } EXPORT_SYMBOL(idr_alloc_cyclic); @@ -226,7 +283,7 @@ EXPORT_SYMBOL(idr_replace); * bitmap, which is excessive. */ -#define IDA_MAX (0x80000000U / IDA_BITMAP_BITS) +#define IDA_MAX (0x80000000U / IDA_BITMAP_BITS - 1) /** * ida_get_new_above - allocate new ID above or equal to a start id diff --git a/lib/radix-tree.c b/lib/radix-tree.c index c8d55565fafa..0a7ae3288a24 100644 --- a/lib/radix-tree.c +++ b/lib/radix-tree.c @@ -24,6 +24,7 @@ #include <linux/bitmap.h> #include <linux/bitops.h> +#include <linux/bug.h> #include <linux/cpu.h> #include <linux/errno.h> #include <linux/export.h> @@ -2135,7 +2136,7 @@ int ida_pre_get(struct ida *ida, gfp_t gfp) } EXPORT_SYMBOL(ida_pre_get); -void __rcu **idr_get_free_cmn(struct radix_tree_root *root, +void __rcu **idr_get_free(struct radix_tree_root *root, struct radix_tree_iter *iter, gfp_t gfp, unsigned long max) { diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c index 2fede6a55d04..5c36e9c0df9a 100644 --- a/net/sched/cls_u32.c +++ b/net/sched/cls_u32.c @@ -730,19 +730,17 @@ static int u32_delete(struct tcf_proto *tp, void *arg, bool *last) static u32 gen_new_kid(struct tc_u_hnode *ht, u32 htid) { - unsigned long idr_index; - u32 start = htid | 0x800; - u32 max = htid | 0xFFF; - u32 min = htid; - - if (idr_alloc_ext(&ht->handle_idr, NULL, &idr_index, - start, max + 1, GFP_KERNEL)) { - if (idr_alloc_ext(&ht->handle_idr, NULL, &idr_index, - min + 1, max + 1, GFP_KERNEL)) - return max; + unsigned long index = htid | 0x800; + unsigned long max = htid | 0xFFF; + + if (idr_alloc_ul(&ht->handle_idr, NULL, &index, max, GFP_KERNEL)) { + index = htid + 1; + if (idr_alloc_ul(&ht->handle_idr, NULL, &index, max, + GFP_KERNEL)) + index = max; } - return (u32)idr_index; + return index; } static const struct nla_policy u32_policy[TCA_U32_MAX + 1] = { diff --git a/tools/testing/radix-tree/idr-test.c b/tools/testing/radix-tree/idr-test.c index 892ef8855b02..36437ade429c 100644 --- a/tools/testing/radix-tree/idr-test.c +++ b/tools/testing/radix-tree/idr-test.c @@ -215,6 +215,23 @@ void idr_checks(void) assert(idr_is_empty(&idr)); + idr_set_cursor(&idr, INT_MAX - 3UL); + for (i = INT_MAX - 3UL; i < INT_MAX + 3UL; i++) { + struct item *item; + unsigned int id; + if (i <= INT_MAX) + item = item_create(i, 0); + else + item = item_create(i - INT_MAX - 1, 0); + + id = idr_alloc_cyclic(&idr, item, 0, 0, GFP_KERNEL); + assert(id == item->index); + } + + idr_for_each(&idr, item_idr_free, &idr); + idr_destroy(&idr); + assert(idr_is_empty(&idr)); + for (i = 1; i < 10000; i++) { struct item *item = item_create(i, 0); assert(idr_alloc(&idr, item, 1, 20000, GFP_KERNEL) == i); -- 2.15.0