Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ida: Add ida_find_first_range()

There is no helpers for user to check if a given ID is allocated or not,
neither a helper to loop all the allocated IDs in an IDA and do something
for cleanup. With the two needs, a helper to get the lowest allocated ID
of a range and two variants based on it.

Caller can check if a given ID is allocated or not by:

bool ida_exists(struct ida *ida, unsigned int id)

Caller can iterate all allocated IDs by:

int id;
while ((id = ida_find_first(&pasid_ida)) >= 0) {
//anything to do with the allocated ID
ida_free(pasid_ida, pasid);
}

Link: https://patch.msgid.link/r/20250321180143.8468-2-yi.l.liu@intel.com
Cc: Matthew Wilcox (Oracle) <willy@infradead.org>
Suggested-by: Jason Gunthorpe <jgg@nvidia.com>
Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
Reviewed-by: Kevin Tian <kevin.tian@intel.com>
Acked-by: Matthew Wilcox (Oracle) <willy@infradead.org>
Tested-by: Nicolin Chen <nicolinc@nvidia.com>
Signed-off-by: Yi Liu <yi.l.liu@intel.com>
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>

authored by

Yi Liu and committed by
Jason Gunthorpe
7fe6b987 d57a1fb3

+148
+11
include/linux/idr.h
··· 257 257 int ida_alloc_range(struct ida *, unsigned int min, unsigned int max, gfp_t); 258 258 void ida_free(struct ida *, unsigned int id); 259 259 void ida_destroy(struct ida *ida); 260 + int ida_find_first_range(struct ida *ida, unsigned int min, unsigned int max); 260 261 261 262 /** 262 263 * ida_alloc() - Allocate an unused ID. ··· 328 327 static inline bool ida_is_empty(const struct ida *ida) 329 328 { 330 329 return xa_empty(&ida->xa); 330 + } 331 + 332 + static inline bool ida_exists(struct ida *ida, unsigned int id) 333 + { 334 + return ida_find_first_range(ida, id, id) == id; 335 + } 336 + 337 + static inline int ida_find_first(struct ida *ida) 338 + { 339 + return ida_find_first_range(ida, 0, ~0); 331 340 } 332 341 #endif /* __IDR_H__ */
+67
lib/idr.c
··· 477 477 EXPORT_SYMBOL(ida_alloc_range); 478 478 479 479 /** 480 + * ida_find_first_range - Get the lowest used ID. 481 + * @ida: IDA handle. 482 + * @min: Lowest ID to get. 483 + * @max: Highest ID to get. 484 + * 485 + * Get the lowest used ID between @min and @max, inclusive. The returned 486 + * ID will not exceed %INT_MAX, even if @max is larger. 487 + * 488 + * Context: Any context. Takes and releases the xa_lock. 489 + * Return: The lowest used ID, or errno if no used ID is found. 490 + */ 491 + int ida_find_first_range(struct ida *ida, unsigned int min, unsigned int max) 492 + { 493 + unsigned long index = min / IDA_BITMAP_BITS; 494 + unsigned int offset = min % IDA_BITMAP_BITS; 495 + unsigned long *addr, size, bit; 496 + unsigned long tmp = 0; 497 + unsigned long flags; 498 + void *entry; 499 + int ret; 500 + 501 + if ((int)min < 0) 502 + return -EINVAL; 503 + if ((int)max < 0) 504 + max = INT_MAX; 505 + 506 + xa_lock_irqsave(&ida->xa, flags); 507 + 508 + entry = xa_find(&ida->xa, &index, max / IDA_BITMAP_BITS, XA_PRESENT); 509 + if (!entry) { 510 + ret = -ENOENT; 511 + goto err_unlock; 512 + } 513 + 514 + if (index > min / IDA_BITMAP_BITS) 515 + offset = 0; 516 + if (index * IDA_BITMAP_BITS + offset > max) { 517 + ret = -ENOENT; 518 + goto err_unlock; 519 + } 520 + 521 + if (xa_is_value(entry)) { 522 + tmp = xa_to_value(entry); 523 + addr = &tmp; 524 + size = BITS_PER_XA_VALUE; 525 + } else { 526 + addr = ((struct ida_bitmap *)entry)->bitmap; 527 + size = IDA_BITMAP_BITS; 528 + } 529 + 530 + bit = find_next_bit(addr, size, offset); 531 + 532 + xa_unlock_irqrestore(&ida->xa, flags); 533 + 534 + if (bit == size || 535 + index * IDA_BITMAP_BITS + bit > max) 536 + return -ENOENT; 537 + 538 + return index * IDA_BITMAP_BITS + bit; 539 + 540 + err_unlock: 541 + xa_unlock_irqrestore(&ida->xa, flags); 542 + return ret; 543 + } 544 + EXPORT_SYMBOL(ida_find_first_range); 545 + 546 + /** 480 547 * ida_free() - Release an allocated ID. 481 548 * @ida: IDA handle. 482 549 * @id: Previously allocated ID.
+70
lib/test_ida.c
··· 189 189 IDA_BUG_ON(ida, !ida_is_empty(ida)); 190 190 } 191 191 192 + /* 193 + * Check ida_find_first_range() and varriants. 194 + */ 195 + static void ida_check_find_first(struct ida *ida) 196 + { 197 + /* IDA is empty; all of the below should be not exist */ 198 + IDA_BUG_ON(ida, ida_exists(ida, 0)); 199 + IDA_BUG_ON(ida, ida_exists(ida, 3)); 200 + IDA_BUG_ON(ida, ida_exists(ida, 63)); 201 + IDA_BUG_ON(ida, ida_exists(ida, 1023)); 202 + IDA_BUG_ON(ida, ida_exists(ida, (1 << 20) - 1)); 203 + 204 + /* IDA contains a single value entry */ 205 + IDA_BUG_ON(ida, ida_alloc_min(ida, 3, GFP_KERNEL) != 3); 206 + IDA_BUG_ON(ida, ida_exists(ida, 0)); 207 + IDA_BUG_ON(ida, !ida_exists(ida, 3)); 208 + IDA_BUG_ON(ida, ida_exists(ida, 63)); 209 + IDA_BUG_ON(ida, ida_exists(ida, 1023)); 210 + IDA_BUG_ON(ida, ida_exists(ida, (1 << 20) - 1)); 211 + 212 + IDA_BUG_ON(ida, ida_alloc_min(ida, 63, GFP_KERNEL) != 63); 213 + IDA_BUG_ON(ida, ida_exists(ida, 0)); 214 + IDA_BUG_ON(ida, !ida_exists(ida, 3)); 215 + IDA_BUG_ON(ida, !ida_exists(ida, 63)); 216 + IDA_BUG_ON(ida, ida_exists(ida, 1023)); 217 + IDA_BUG_ON(ida, ida_exists(ida, (1 << 20) - 1)); 218 + 219 + /* IDA contains a single bitmap */ 220 + IDA_BUG_ON(ida, ida_alloc_min(ida, 1023, GFP_KERNEL) != 1023); 221 + IDA_BUG_ON(ida, ida_exists(ida, 0)); 222 + IDA_BUG_ON(ida, !ida_exists(ida, 3)); 223 + IDA_BUG_ON(ida, !ida_exists(ida, 63)); 224 + IDA_BUG_ON(ida, !ida_exists(ida, 1023)); 225 + IDA_BUG_ON(ida, ida_exists(ida, (1 << 20) - 1)); 226 + 227 + /* IDA contains a tree */ 228 + IDA_BUG_ON(ida, ida_alloc_min(ida, (1 << 20) - 1, GFP_KERNEL) != (1 << 20) - 1); 229 + IDA_BUG_ON(ida, ida_exists(ida, 0)); 230 + IDA_BUG_ON(ida, !ida_exists(ida, 3)); 231 + IDA_BUG_ON(ida, !ida_exists(ida, 63)); 232 + IDA_BUG_ON(ida, !ida_exists(ida, 1023)); 233 + IDA_BUG_ON(ida, !ida_exists(ida, (1 << 20) - 1)); 234 + 235 + /* Now try to find first */ 236 + IDA_BUG_ON(ida, ida_find_first(ida) != 3); 237 + IDA_BUG_ON(ida, ida_find_first_range(ida, -1, 2) != -EINVAL); 238 + IDA_BUG_ON(ida, ida_find_first_range(ida, 0, 2) != -ENOENT); // no used ID 239 + IDA_BUG_ON(ida, ida_find_first_range(ida, 0, 3) != 3); 240 + IDA_BUG_ON(ida, ida_find_first_range(ida, 1, 3) != 3); 241 + IDA_BUG_ON(ida, ida_find_first_range(ida, 3, 3) != 3); 242 + IDA_BUG_ON(ida, ida_find_first_range(ida, 2, 4) != 3); 243 + IDA_BUG_ON(ida, ida_find_first_range(ida, 4, 3) != -ENOENT); // min > max, fail 244 + IDA_BUG_ON(ida, ida_find_first_range(ida, 4, 60) != -ENOENT); // no used ID 245 + IDA_BUG_ON(ida, ida_find_first_range(ida, 4, 64) != 63); 246 + IDA_BUG_ON(ida, ida_find_first_range(ida, 63, 63) != 63); 247 + IDA_BUG_ON(ida, ida_find_first_range(ida, 64, 1026) != 1023); 248 + IDA_BUG_ON(ida, ida_find_first_range(ida, 1023, 1023) != 1023); 249 + IDA_BUG_ON(ida, ida_find_first_range(ida, 1023, (1 << 20) - 1) != 1023); 250 + IDA_BUG_ON(ida, ida_find_first_range(ida, 1024, (1 << 20) - 1) != (1 << 20) - 1); 251 + IDA_BUG_ON(ida, ida_find_first_range(ida, (1 << 20), INT_MAX) != -ENOENT); 252 + 253 + ida_free(ida, 3); 254 + ida_free(ida, 63); 255 + ida_free(ida, 1023); 256 + ida_free(ida, (1 << 20) - 1); 257 + 258 + IDA_BUG_ON(ida, !ida_is_empty(ida)); 259 + } 260 + 192 261 static DEFINE_IDA(ida); 193 262 194 263 static int ida_checks(void) ··· 271 202 ida_check_max(&ida); 272 203 ida_check_conv(&ida); 273 204 ida_check_bad_free(&ida); 205 + ida_check_find_first(&ida); 274 206 275 207 printk("IDA: %u of %u tests passed\n", tests_passed, tests_run); 276 208 return (tests_run != tests_passed) ? 0 : -EINVAL;