diff options
| author | Tejun Heo <tj@kernel.org> | 2013-02-27 20:05:08 -0500 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-02-27 22:10:21 -0500 |
| commit | 0ffc2a9c8072969253a20821c2c733a2cbb4c7c7 (patch) | |
| tree | ff6a9d270d4f0fa5c770837c6d9e5d5aac2637a7 /lib | |
| parent | 54616283c2948812a44240858ced610e7cacbde1 (diff) | |
idr: implement lookup hint
While idr lookup isn't a particularly heavy operation, it still is too
substantial to use in hot paths without worrying about the performance
implications. With recent changes, each idr_layer covers 256 slots
which should be enough to cover most use cases with single idr_layer
making lookup hint very attractive.
This patch adds idr->hint which points to the idr_layer which
allocated an ID most recently and the fast path lookup becomes
if (look up target's prefix matches that of the hinted layer)
return hint->ary[ID's offset in the leaf layer];
which can be inlined.
idr->hint is set to the leaf node on idr_fill_slot() and cleared from
free_layer().
[andriy.shevchenko@linux.intel.com: always do slow path when hint is uninitialized]
Signed-off-by: Tejun Heo <tj@kernel.org>
Cc: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Cc: Sasha Levin <sasha.levin@oracle.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/idr.c | 38 |
1 files changed, 16 insertions, 22 deletions
| @@ -137,8 +137,10 @@ static void idr_layer_rcu_free(struct rcu_head *head) | |||
| 137 | kmem_cache_free(idr_layer_cache, layer); | 137 | kmem_cache_free(idr_layer_cache, layer); |
| 138 | } | 138 | } |
| 139 | 139 | ||
| 140 | static inline void free_layer(struct idr_layer *p) | 140 | static inline void free_layer(struct idr *idr, struct idr_layer *p) |
| 141 | { | 141 | { |
| 142 | if (idr->hint && idr->hint == p) | ||
| 143 | RCU_INIT_POINTER(idr->hint, NULL); | ||
| 142 | call_rcu(&p->rcu_head, idr_layer_rcu_free); | 144 | call_rcu(&p->rcu_head, idr_layer_rcu_free); |
| 143 | } | 145 | } |
| 144 | 146 | ||
| @@ -363,8 +365,12 @@ build_up: | |||
| 363 | * @id and @pa are from a successful allocation from idr_get_empty_slot(). | 365 | * @id and @pa are from a successful allocation from idr_get_empty_slot(). |
| 364 | * Install the user pointer @ptr and mark the slot full. | 366 | * Install the user pointer @ptr and mark the slot full. |
| 365 | */ | 367 | */ |
| 366 | static void idr_fill_slot(void *ptr, int id, struct idr_layer **pa) | 368 | static void idr_fill_slot(struct idr *idr, void *ptr, int id, |
| 369 | struct idr_layer **pa) | ||
| 367 | { | 370 | { |
| 371 | /* update hint used for lookup, cleared from free_layer() */ | ||
| 372 | rcu_assign_pointer(idr->hint, pa[0]); | ||
| 373 | |||
| 368 | rcu_assign_pointer(pa[0]->ary[id & IDR_MASK], (struct idr_layer *)ptr); | 374 | rcu_assign_pointer(pa[0]->ary[id & IDR_MASK], (struct idr_layer *)ptr); |
| 369 | pa[0]->count++; | 375 | pa[0]->count++; |
| 370 | idr_mark_full(pa, id); | 376 | idr_mark_full(pa, id); |
| @@ -397,7 +403,7 @@ int idr_get_new_above(struct idr *idp, void *ptr, int starting_id, int *id) | |||
| 397 | if (rv < 0) | 403 | if (rv < 0) |
| 398 | return rv == -ENOMEM ? -EAGAIN : rv; | 404 | return rv == -ENOMEM ? -EAGAIN : rv; |
| 399 | 405 | ||
| 400 | idr_fill_slot(ptr, rv, pa); | 406 | idr_fill_slot(idp, ptr, rv, pa); |
| 401 | *id = rv; | 407 | *id = rv; |
| 402 | return 0; | 408 | return 0; |
| 403 | } | 409 | } |
| @@ -504,7 +510,7 @@ int idr_alloc(struct idr *idr, void *ptr, int start, int end, gfp_t gfp_mask) | |||
| 504 | if (unlikely(id > max)) | 510 | if (unlikely(id > max)) |
| 505 | return -ENOSPC; | 511 | return -ENOSPC; |
| 506 | 512 | ||
| 507 | idr_fill_slot(ptr, id, pa); | 513 | idr_fill_slot(idr, ptr, id, pa); |
| 508 | return id; | 514 | return id; |
| 509 | } | 515 | } |
| 510 | EXPORT_SYMBOL_GPL(idr_alloc); | 516 | EXPORT_SYMBOL_GPL(idr_alloc); |
| @@ -541,14 +547,14 @@ static void sub_remove(struct idr *idp, int shift, int id) | |||
| 541 | to_free = NULL; | 547 | to_free = NULL; |
| 542 | while(*paa && ! --((**paa)->count)){ | 548 | while(*paa && ! --((**paa)->count)){ |
| 543 | if (to_free) | 549 | if (to_free) |
| 544 | free_layer(to_free); | 550 | free_layer(idp, to_free); |
| 545 | to_free = **paa; | 551 | to_free = **paa; |
| 546 | **paa-- = NULL; | 552 | **paa-- = NULL; |
| 547 | } | 553 | } |
| 548 | if (!*paa) | 554 | if (!*paa) |
| 549 | idp->layers = 0; | 555 | idp->layers = 0; |
| 550 | if (to_free) | 556 | if (to_free) |
| 551 | free_layer(to_free); | 557 | free_layer(idp, to_free); |
| 552 | } else | 558 | } else |
| 553 | idr_remove_warning(id); | 559 | idr_remove_warning(id); |
| 554 | } | 560 | } |
| @@ -581,7 +587,7 @@ void idr_remove(struct idr *idp, int id) | |||
| 581 | --idp->layers; | 587 | --idp->layers; |
| 582 | to_free->count = 0; | 588 | to_free->count = 0; |
| 583 | bitmap_clear(to_free->bitmap, 0, IDR_SIZE); | 589 | bitmap_clear(to_free->bitmap, 0, IDR_SIZE); |
| 584 | free_layer(to_free); | 590 | free_layer(idp, to_free); |
| 585 | } | 591 | } |
| 586 | while (idp->id_free_cnt >= MAX_IDR_FREE) { | 592 | while (idp->id_free_cnt >= MAX_IDR_FREE) { |
| 587 | p = get_from_free_list(idp); | 593 | p = get_from_free_list(idp); |
| @@ -622,7 +628,7 @@ void __idr_remove_all(struct idr *idp) | |||
| 622 | /* Get the highest bit that the above add changed from 0->1. */ | 628 | /* Get the highest bit that the above add changed from 0->1. */ |
| 623 | while (n < fls(id ^ bt_mask)) { | 629 | while (n < fls(id ^ bt_mask)) { |
| 624 | if (p) | 630 | if (p) |
| 625 | free_layer(p); | 631 | free_layer(idp, p); |
| 626 | n += IDR_BITS; | 632 | n += IDR_BITS; |
| 627 | p = *--paa; | 633 | p = *--paa; |
| 628 | } | 634 | } |
| @@ -655,19 +661,7 @@ void idr_destroy(struct idr *idp) | |||
| 655 | } | 661 | } |
| 656 | EXPORT_SYMBOL(idr_destroy); | 662 | EXPORT_SYMBOL(idr_destroy); |
| 657 | 663 | ||
| 658 | /** | 664 | void *idr_find_slowpath(struct idr *idp, int id) |
| 659 | * idr_find - return pointer for given id | ||
| 660 | * @idp: idr handle | ||
| 661 | * @id: lookup key | ||
| 662 | * | ||
| 663 | * Return the pointer given the id it has been registered with. A %NULL | ||
| 664 | * return indicates that @id is not valid or you passed %NULL in | ||
| 665 | * idr_get_new(). | ||
| 666 | * | ||
| 667 | * This function can be called under rcu_read_lock(), given that the leaf | ||
| 668 | * pointers lifetimes are correctly managed. | ||
| 669 | */ | ||
| 670 | void *idr_find(struct idr *idp, int id) | ||
| 671 | { | 665 | { |
| 672 | int n; | 666 | int n; |
| 673 | struct idr_layer *p; | 667 | struct idr_layer *p; |
| @@ -691,7 +685,7 @@ void *idr_find(struct idr *idp, int id) | |||
| 691 | } | 685 | } |
| 692 | return((void *)p); | 686 | return((void *)p); |
| 693 | } | 687 | } |
| 694 | EXPORT_SYMBOL(idr_find); | 688 | EXPORT_SYMBOL(idr_find_slowpath); |
| 695 | 689 | ||
| 696 | /** | 690 | /** |
| 697 | * idr_for_each - iterate through all stored pointers | 691 | * idr_for_each - iterate through all stored pointers |
