forked from mirrors/linux
		
	XArray: Redesign xa_alloc API
It was too easy to forget to initialise the start index. Add an xa_limit data structure which can be used to pass min & max, and define a couple of special values for common cases. Also add some more tests cribbed from the IDR test suite. Change the return value from -ENOSPC to -EBUSY to match xa_insert(). Signed-off-by: Matthew Wilcox <willy@infradead.org>
This commit is contained in:
		
							parent
							
								
									3ccaf57a6a
								
							
						
					
					
						commit
						a3e4d3f97e
					
				
					 3 changed files with 135 additions and 60 deletions
				
			
		|  | @ -200,6 +200,27 @@ static inline int xa_err(void *entry) | |||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * struct xa_limit - Represents a range of IDs. | ||||
|  * @min: The lowest ID to allocate (inclusive). | ||||
|  * @max: The maximum ID to allocate (inclusive). | ||||
|  * | ||||
|  * This structure is used either directly or via the XA_LIMIT() macro | ||||
|  * to communicate the range of IDs that are valid for allocation. | ||||
|  * Two common ranges are predefined for you: | ||||
|  *  * xa_limit_32b	- [0 - UINT_MAX] | ||||
|  *  * xa_limit_31b	- [0 - INT_MAX] | ||||
|  */ | ||||
| struct xa_limit { | ||||
| 	u32 max; | ||||
| 	u32 min; | ||||
| }; | ||||
| 
 | ||||
| #define XA_LIMIT(_min, _max) (struct xa_limit) { .min = _min, .max = _max } | ||||
| 
 | ||||
| #define xa_limit_32b	XA_LIMIT(0, UINT_MAX) | ||||
| #define xa_limit_31b	XA_LIMIT(0, INT_MAX) | ||||
| 
 | ||||
| typedef unsigned __bitwise xa_mark_t; | ||||
| #define XA_MARK_0		((__force xa_mark_t)0U) | ||||
| #define XA_MARK_1		((__force xa_mark_t)1U) | ||||
|  | @ -476,7 +497,8 @@ void *__xa_store(struct xarray *, unsigned long index, void *entry, gfp_t); | |||
| void *__xa_cmpxchg(struct xarray *, unsigned long index, void *old, | ||||
| 		void *entry, gfp_t); | ||||
| int __xa_insert(struct xarray *, unsigned long index, void *entry, gfp_t); | ||||
| int __xa_alloc(struct xarray *, u32 *id, u32 max, void *entry, gfp_t); | ||||
| int __must_check __xa_alloc(struct xarray *, u32 *id, void *entry, | ||||
| 		struct xa_limit, gfp_t); | ||||
| int __xa_reserve(struct xarray *, unsigned long index, gfp_t); | ||||
| void __xa_set_mark(struct xarray *, unsigned long index, xa_mark_t); | ||||
| void __xa_clear_mark(struct xarray *, unsigned long index, xa_mark_t); | ||||
|  | @ -753,26 +775,26 @@ static inline int xa_insert_irq(struct xarray *xa, unsigned long index, | |||
|  * xa_alloc() - Find somewhere to store this entry in the XArray. | ||||
|  * @xa: XArray. | ||||
|  * @id: Pointer to ID. | ||||
|  * @max: Maximum ID to allocate (inclusive). | ||||
|  * @entry: New entry. | ||||
|  * @limit: Range of ID to allocate. | ||||
|  * @gfp: Memory allocation flags. | ||||
|  * | ||||
|  * Allocates an unused ID in the range specified by @id and @max. | ||||
|  * Updates the @id pointer with the index, then stores the entry at that | ||||
|  * index.  A concurrent lookup will not see an uninitialised @id. | ||||
|  * Finds an empty entry in @xa between @limit.min and @limit.max, | ||||
|  * stores the index into the @id pointer, then stores the entry at | ||||
|  * that index.  A concurrent lookup will not see an uninitialised @id. | ||||
|  * | ||||
|  * Context: Process context.  Takes and releases the xa_lock.  May sleep if | ||||
|  * Context: Any context.  Takes and releases the xa_lock.  May sleep if | ||||
|  * the @gfp flags permit. | ||||
|  * Return: 0 on success, -ENOMEM if memory allocation fails or -ENOSPC if | ||||
|  * there is no more space in the XArray. | ||||
|  * Return: 0 on success, -ENOMEM if memory could not be allocated or | ||||
|  * -EBUSY if there are no free entries in @limit. | ||||
|  */ | ||||
| static inline int xa_alloc(struct xarray *xa, u32 *id, u32 max, void *entry, | ||||
| 		gfp_t gfp) | ||||
| static inline __must_check int xa_alloc(struct xarray *xa, u32 *id, | ||||
| 		void *entry, struct xa_limit limit, gfp_t gfp) | ||||
| { | ||||
| 	int err; | ||||
| 
 | ||||
| 	xa_lock(xa); | ||||
| 	err = __xa_alloc(xa, id, max, entry, gfp); | ||||
| 	err = __xa_alloc(xa, id, entry, limit, gfp); | ||||
| 	xa_unlock(xa); | ||||
| 
 | ||||
| 	return err; | ||||
|  | @ -782,26 +804,26 @@ static inline int xa_alloc(struct xarray *xa, u32 *id, u32 max, void *entry, | |||
|  * xa_alloc_bh() - Find somewhere to store this entry in the XArray. | ||||
|  * @xa: XArray. | ||||
|  * @id: Pointer to ID. | ||||
|  * @max: Maximum ID to allocate (inclusive). | ||||
|  * @entry: New entry. | ||||
|  * @limit: Range of ID to allocate. | ||||
|  * @gfp: Memory allocation flags. | ||||
|  * | ||||
|  * Allocates an unused ID in the range specified by @id and @max. | ||||
|  * Updates the @id pointer with the index, then stores the entry at that | ||||
|  * index.  A concurrent lookup will not see an uninitialised @id. | ||||
|  * Finds an empty entry in @xa between @limit.min and @limit.max, | ||||
|  * stores the index into the @id pointer, then stores the entry at | ||||
|  * that index.  A concurrent lookup will not see an uninitialised @id. | ||||
|  * | ||||
|  * Context: Any context.  Takes and releases the xa_lock while | ||||
|  * disabling softirqs.  May sleep if the @gfp flags permit. | ||||
|  * Return: 0 on success, -ENOMEM if memory allocation fails or -ENOSPC if | ||||
|  * there is no more space in the XArray. | ||||
|  * Return: 0 on success, -ENOMEM if memory could not be allocated or | ||||
|  * -EBUSY if there are no free entries in @limit. | ||||
|  */ | ||||
| static inline int xa_alloc_bh(struct xarray *xa, u32 *id, u32 max, void *entry, | ||||
| 		gfp_t gfp) | ||||
| static inline int __must_check xa_alloc_bh(struct xarray *xa, u32 *id, | ||||
| 		void *entry, struct xa_limit limit, gfp_t gfp) | ||||
| { | ||||
| 	int err; | ||||
| 
 | ||||
| 	xa_lock_bh(xa); | ||||
| 	err = __xa_alloc(xa, id, max, entry, gfp); | ||||
| 	err = __xa_alloc(xa, id, entry, limit, gfp); | ||||
| 	xa_unlock_bh(xa); | ||||
| 
 | ||||
| 	return err; | ||||
|  | @ -811,26 +833,26 @@ static inline int xa_alloc_bh(struct xarray *xa, u32 *id, u32 max, void *entry, | |||
|  * xa_alloc_irq() - Find somewhere to store this entry in the XArray. | ||||
|  * @xa: XArray. | ||||
|  * @id: Pointer to ID. | ||||
|  * @max: Maximum ID to allocate (inclusive). | ||||
|  * @entry: New entry. | ||||
|  * @limit: Range of ID to allocate. | ||||
|  * @gfp: Memory allocation flags. | ||||
|  * | ||||
|  * Allocates an unused ID in the range specified by @id and @max. | ||||
|  * Updates the @id pointer with the index, then stores the entry at that | ||||
|  * index.  A concurrent lookup will not see an uninitialised @id. | ||||
|  * Finds an empty entry in @xa between @limit.min and @limit.max, | ||||
|  * stores the index into the @id pointer, then stores the entry at | ||||
|  * that index.  A concurrent lookup will not see an uninitialised @id. | ||||
|  * | ||||
|  * Context: Process context.  Takes and releases the xa_lock while | ||||
|  * disabling interrupts.  May sleep if the @gfp flags permit. | ||||
|  * Return: 0 on success, -ENOMEM if memory allocation fails or -ENOSPC if | ||||
|  * there is no more space in the XArray. | ||||
|  * Return: 0 on success, -ENOMEM if memory could not be allocated or | ||||
|  * -EBUSY if there are no free entries in @limit. | ||||
|  */ | ||||
| static inline int xa_alloc_irq(struct xarray *xa, u32 *id, u32 max, void *entry, | ||||
| 		gfp_t gfp) | ||||
| static inline int __must_check xa_alloc_irq(struct xarray *xa, u32 *id, | ||||
| 		void *entry, struct xa_limit limit, gfp_t gfp) | ||||
| { | ||||
| 	int err; | ||||
| 
 | ||||
| 	xa_lock_irq(xa); | ||||
| 	err = __xa_alloc(xa, id, max, entry, gfp); | ||||
| 	err = __xa_alloc(xa, id, entry, limit, gfp); | ||||
| 	xa_unlock_irq(xa); | ||||
| 
 | ||||
| 	return err; | ||||
|  |  | |||
|  | @ -40,9 +40,9 @@ static void *xa_store_index(struct xarray *xa, unsigned long index, gfp_t gfp) | |||
| 
 | ||||
| static void xa_alloc_index(struct xarray *xa, unsigned long index, gfp_t gfp) | ||||
| { | ||||
| 	u32 id = 0; | ||||
| 	u32 id; | ||||
| 
 | ||||
| 	XA_BUG_ON(xa, xa_alloc(xa, &id, UINT_MAX, xa_mk_index(index), | ||||
| 	XA_BUG_ON(xa, xa_alloc(xa, &id, xa_mk_index(index), xa_limit_32b, | ||||
| 				gfp) != 0); | ||||
| 	XA_BUG_ON(xa, id != index); | ||||
| } | ||||
|  | @ -640,28 +640,81 @@ static noinline void check_xa_alloc_1(struct xarray *xa, unsigned int base) | |||
| 	xa_destroy(xa); | ||||
| 
 | ||||
| 	/* Check that we fail properly at the limit of allocation */ | ||||
| 	id = 0xfffffffeU; | ||||
| 	XA_BUG_ON(xa, xa_alloc(xa, &id, UINT_MAX, xa_mk_index(id), | ||||
| 	XA_BUG_ON(xa, xa_alloc(xa, &id, xa_mk_index(UINT_MAX - 1), | ||||
| 				XA_LIMIT(UINT_MAX - 1, UINT_MAX), | ||||
| 				GFP_KERNEL) != 0); | ||||
| 	XA_BUG_ON(xa, id != 0xfffffffeU); | ||||
| 	XA_BUG_ON(xa, xa_alloc(xa, &id, UINT_MAX, xa_mk_index(id), | ||||
| 	XA_BUG_ON(xa, xa_alloc(xa, &id, xa_mk_index(UINT_MAX), | ||||
| 				XA_LIMIT(UINT_MAX - 1, UINT_MAX), | ||||
| 				GFP_KERNEL) != 0); | ||||
| 	XA_BUG_ON(xa, id != 0xffffffffU); | ||||
| 	XA_BUG_ON(xa, xa_alloc(xa, &id, UINT_MAX, xa_mk_index(id), | ||||
| 				GFP_KERNEL) != -ENOSPC); | ||||
| 	XA_BUG_ON(xa, id != 0xffffffffU); | ||||
| 	id = 3; | ||||
| 	XA_BUG_ON(xa, xa_alloc(xa, &id, xa_mk_index(0), | ||||
| 				XA_LIMIT(UINT_MAX - 1, UINT_MAX), | ||||
| 				GFP_KERNEL) != -EBUSY); | ||||
| 	XA_BUG_ON(xa, id != 3); | ||||
| 	xa_destroy(xa); | ||||
| 
 | ||||
| 	id = 10; | ||||
| 	XA_BUG_ON(xa, xa_alloc(xa, &id, 5, xa_mk_index(id), | ||||
| 				GFP_KERNEL) != -ENOSPC); | ||||
| 	XA_BUG_ON(xa, xa_alloc(xa, &id, xa_mk_index(10), XA_LIMIT(10, 5), | ||||
| 				GFP_KERNEL) != -EBUSY); | ||||
| 	XA_BUG_ON(xa, xa_store_index(xa, 3, GFP_KERNEL) != 0); | ||||
| 	XA_BUG_ON(xa, xa_alloc(xa, &id, 5, xa_mk_index(id), | ||||
| 				GFP_KERNEL) != -ENOSPC); | ||||
| 	XA_BUG_ON(xa, xa_alloc(xa, &id, xa_mk_index(10), XA_LIMIT(10, 5), | ||||
| 				GFP_KERNEL) != -EBUSY); | ||||
| 	xa_erase_index(xa, 3); | ||||
| 	XA_BUG_ON(xa, !xa_empty(xa)); | ||||
| } | ||||
| 
 | ||||
| static noinline void check_xa_alloc_2(struct xarray *xa, unsigned int base) | ||||
| { | ||||
| 	unsigned int i, id; | ||||
| 	unsigned long index; | ||||
| 	void *entry; | ||||
| 
 | ||||
| 	/* Allocate and free a NULL and check xa_empty() behaves */ | ||||
| 	XA_BUG_ON(xa, !xa_empty(xa)); | ||||
| 	XA_BUG_ON(xa, xa_alloc(xa, &id, NULL, xa_limit_32b, GFP_KERNEL) != 0); | ||||
| 	XA_BUG_ON(xa, id != base); | ||||
| 	XA_BUG_ON(xa, xa_empty(xa)); | ||||
| 	XA_BUG_ON(xa, xa_erase(xa, id) != NULL); | ||||
| 	XA_BUG_ON(xa, !xa_empty(xa)); | ||||
| 
 | ||||
| 	/* Ditto, but check destroy instead of erase */ | ||||
| 	XA_BUG_ON(xa, !xa_empty(xa)); | ||||
| 	XA_BUG_ON(xa, xa_alloc(xa, &id, NULL, xa_limit_32b, GFP_KERNEL) != 0); | ||||
| 	XA_BUG_ON(xa, id != base); | ||||
| 	XA_BUG_ON(xa, xa_empty(xa)); | ||||
| 	xa_destroy(xa); | ||||
| 	XA_BUG_ON(xa, !xa_empty(xa)); | ||||
| 
 | ||||
| 	for (i = base; i < base + 10; i++) { | ||||
| 		XA_BUG_ON(xa, xa_alloc(xa, &id, NULL, xa_limit_32b, | ||||
| 					GFP_KERNEL) != 0); | ||||
| 		XA_BUG_ON(xa, id != i); | ||||
| 	} | ||||
| 
 | ||||
| 	XA_BUG_ON(xa, xa_store(xa, 3, xa_mk_index(3), GFP_KERNEL) != NULL); | ||||
| 	XA_BUG_ON(xa, xa_store(xa, 4, xa_mk_index(4), GFP_KERNEL) != NULL); | ||||
| 	XA_BUG_ON(xa, xa_store(xa, 4, NULL, GFP_KERNEL) != xa_mk_index(4)); | ||||
| 	XA_BUG_ON(xa, xa_erase(xa, 5) != NULL); | ||||
| 	XA_BUG_ON(xa, xa_alloc(xa, &id, NULL, xa_limit_32b, GFP_KERNEL) != 0); | ||||
| 	XA_BUG_ON(xa, id != 5); | ||||
| 
 | ||||
| 	xa_for_each(xa, index, entry) { | ||||
| 		xa_erase_index(xa, index); | ||||
| 	} | ||||
| 
 | ||||
| 	for (i = base; i < base + 9; i++) { | ||||
| 		XA_BUG_ON(xa, xa_erase(xa, i) != NULL); | ||||
| 		XA_BUG_ON(xa, xa_empty(xa)); | ||||
| 	} | ||||
| 	XA_BUG_ON(xa, xa_erase(xa, 8) != NULL); | ||||
| 	XA_BUG_ON(xa, xa_empty(xa)); | ||||
| 	XA_BUG_ON(xa, xa_erase(xa, base + 9) != NULL); | ||||
| 	XA_BUG_ON(xa, !xa_empty(xa)); | ||||
| 
 | ||||
| 	xa_destroy(xa); | ||||
| } | ||||
| 
 | ||||
| static DEFINE_XARRAY_ALLOC(xa0); | ||||
| static DEFINE_XARRAY_ALLOC1(xa1); | ||||
| 
 | ||||
|  | @ -669,6 +722,8 @@ static noinline void check_xa_alloc(void) | |||
| { | ||||
| 	check_xa_alloc_1(&xa0, 0); | ||||
| 	check_xa_alloc_1(&xa1, 1); | ||||
| 	check_xa_alloc_2(&xa0, 0); | ||||
| 	check_xa_alloc_2(&xa1, 1); | ||||
| } | ||||
| 
 | ||||
| static noinline void __check_store_iter(struct xarray *xa, unsigned long start, | ||||
|  | @ -1219,9 +1274,8 @@ static void check_align_1(struct xarray *xa, char *name) | |||
| 	void *entry; | ||||
| 
 | ||||
| 	for (i = 0; i < 8; i++) { | ||||
| 		id = 0; | ||||
| 		XA_BUG_ON(xa, xa_alloc(xa, &id, UINT_MAX, name + i, GFP_KERNEL) | ||||
| 				!= 0); | ||||
| 		XA_BUG_ON(xa, xa_alloc(xa, &id, name + i, xa_limit_32b, | ||||
| 					GFP_KERNEL) != 0); | ||||
| 		XA_BUG_ON(xa, id != i); | ||||
| 	} | ||||
| 	xa_for_each(xa, index, entry) | ||||
|  |  | |||
							
								
								
									
										29
									
								
								lib/xarray.c
									
									
									
									
									
								
							
							
						
						
									
										29
									
								
								lib/xarray.c
									
									
									
									
									
								
							|  | @ -1615,23 +1615,23 @@ EXPORT_SYMBOL(xa_store_range); | |||
|  * __xa_alloc() - Find somewhere to store this entry in the XArray. | ||||
|  * @xa: XArray. | ||||
|  * @id: Pointer to ID. | ||||
|  * @max: Maximum ID to allocate (inclusive). | ||||
|  * @limit: Range for allocated ID. | ||||
|  * @entry: New entry. | ||||
|  * @gfp: Memory allocation flags. | ||||
|  * | ||||
|  * Allocates an unused ID in the range specified by @id and @max. | ||||
|  * Updates the @id pointer with the index, then stores the entry at that | ||||
|  * index.  A concurrent lookup will not see an uninitialised @id. | ||||
|  * Finds an empty entry in @xa between @limit.min and @limit.max, | ||||
|  * stores the index into the @id pointer, then stores the entry at | ||||
|  * that index.  A concurrent lookup will not see an uninitialised @id. | ||||
|  * | ||||
|  * Context: Any context.  Expects xa_lock to be held on entry.  May | ||||
|  * release and reacquire xa_lock if @gfp flags permit. | ||||
|  * Return: 0 on success, -ENOMEM if memory allocation fails or -ENOSPC if | ||||
|  * there is no more space in the XArray. | ||||
|  * Return: 0 on success, -ENOMEM if memory could not be allocated or | ||||
|  * -EBUSY if there are no free entries in @limit. | ||||
|  */ | ||||
| int __xa_alloc(struct xarray *xa, u32 *id, u32 max, void *entry, gfp_t gfp) | ||||
| int __xa_alloc(struct xarray *xa, u32 *id, void *entry, | ||||
| 		struct xa_limit limit, gfp_t gfp) | ||||
| { | ||||
| 	XA_STATE(xas, xa, 0); | ||||
| 	int err; | ||||
| 
 | ||||
| 	if (WARN_ON_ONCE(xa_is_advanced(entry))) | ||||
| 		return -EINVAL; | ||||
|  | @ -1642,18 +1642,17 @@ int __xa_alloc(struct xarray *xa, u32 *id, u32 max, void *entry, gfp_t gfp) | |||
| 		entry = XA_ZERO_ENTRY; | ||||
| 
 | ||||
| 	do { | ||||
| 		xas.xa_index = *id; | ||||
| 		xas_find_marked(&xas, max, XA_FREE_MARK); | ||||
| 		xas.xa_index = limit.min; | ||||
| 		xas_find_marked(&xas, limit.max, XA_FREE_MARK); | ||||
| 		if (xas.xa_node == XAS_RESTART) | ||||
| 			xas_set_err(&xas, -ENOSPC); | ||||
| 			xas_set_err(&xas, -EBUSY); | ||||
| 		else | ||||
| 			*id = xas.xa_index; | ||||
| 		xas_store(&xas, entry); | ||||
| 		xas_clear_mark(&xas, XA_FREE_MARK); | ||||
| 	} while (__xas_nomem(&xas, gfp)); | ||||
| 
 | ||||
| 	err = xas_error(&xas); | ||||
| 	if (!err) | ||||
| 		*id = xas.xa_index; | ||||
| 	return err; | ||||
| 	return xas_error(&xas); | ||||
| } | ||||
| EXPORT_SYMBOL(__xa_alloc); | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Matthew Wilcox
						Matthew Wilcox