| @@ -292,7 +292,12 @@ static inline bool kmem_cache_has_cpu_partial(struct kmem_cache *s) · 291 unmodified lines |
| 292 | 292 | /* Poison object */ |
| 293 | 293 | #define __OBJECT_POISON ((slab_flags_t __force)0x80000000U) |
| 294 | 294 | /* Use cmpxchg_double */ |
| 295 | + |
| 296 | +#ifdef system_has_freelist_aba |
| 295 | 297 | #define __CMPXCHG_DOUBLE ((slab_flags_t __force)0x40000000U) |
| 298 | +#else |
| 299 | +#define __CMPXCHG_DOUBLE ((slab_flags_t __force)0U) |
| 300 | +#endif |
| 296 | 301 | |
| 297 | 302 | /* |
| 298 | 303 | * Tracking user of a slab. |
| @@ -512,6 +517,40 @@ static __always_inline void slab_unlock(struct slab *slab) · 213 unmodified lines |
| 512 | 517 | __bit_spin_unlock(PG_locked, &page->flags); |
| 513 | 518 | } |
| 514 | 519 | |
| 520 | +static inline bool |
| 521 | +__update_freelist_fast(struct slab *slab, |
| 522 | + void *freelist_old, unsigned long counters_old, |
| 523 | + void *freelist_new, unsigned long counters_new) |
| 524 | +{ |
| 525 | +#ifdef system_has_freelist_aba |
| 526 | + freelist_aba_t old = { .freelist = freelist_old, .counter = counters_old }; |
| 527 | + freelist_aba_t new = { .freelist = freelist_new, .counter = counters_new }; |
| 528 | + |
| 529 | + return try_cmpxchg_freelist(&slab->freelist_counter.full, &old.full, new.full); |
| 530 | +#else |
| 531 | + return false; |
| 532 | +#endif |
| 533 | +} |
| 534 | + |
| 535 | +static inline bool |
| 536 | +__update_freelist_slow(struct slab *slab, |
| 537 | + void *freelist_old, unsigned long counters_old, |
| 538 | + void *freelist_new, unsigned long counters_new) |
| 539 | +{ |
| 540 | + bool ret = false; |
| 541 | + |
| 542 | + slab_lock(slab); |
| 543 | + if (slab->freelist == freelist_old && |
| 544 | + slab->counters == counters_old) { |
| 545 | + slab->freelist = freelist_new; |
| 546 | + slab->counters = counters_new; |
| 547 | + ret = true; |
| 548 | + } |
| 549 | + slab_unlock(slab); |
| 550 | + |
| 551 | + return ret; |
| 552 | +} |
| 553 | + |
| 515 | 554 | /* |
| 516 | 555 | * Interrupts must be disabled (for the fallback code to work right), typically |
| 517 | 556 | * by an _irqsave() lock variant. On PREEMPT_RT the preempt_disable(), which is |
| @@ -519,33 +558,25 @@ static __always_inline void slab_unlock(struct slab *slab) · 1 unmodified line |
| 519 | 558 | * allocation/ free operation in hardirq context. Therefore nothing can |
| 520 | 559 | * interrupt the operation. |
| 521 | 560 | */ |
| 522 | | −static inline bool __cmpxchg_double_slab(struct kmem_cache *s, struct slab *slab, |
| 561 | +static inline bool __slab_update_freelist(struct kmem_cache *s, struct slab *slab, |
| 523 | 562 | void *freelist_old, unsigned long counters_old, |
| 524 | 563 | void *freelist_new, unsigned long counters_new, |
| 525 | 564 | const char *n) |
| 526 | 565 | { |
| 566 | + bool ret; |
| 567 | + |
| 527 | 568 | if (USE_LOCKLESS_FAST_PATH()) |
| 528 | 569 | lockdep_assert_irqs_disabled(); |
| 529 | | −#if defined(CONFIG_HAVE_CMPXCHG_DOUBLE) && \ |
| 530 | | − defined(CONFIG_HAVE_ALIGNED_STRUCT_PAGE) |
| 570 | + |
| 531 | 571 | if (s->flags & __CMPXCHG_DOUBLE) { |
| 532 | | − if (cmpxchg_double(&slab->freelist, &slab->counters, |
| 533 | | − freelist_old, counters_old, |
| 534 | | − freelist_new, counters_new)) |
| 535 | | − return true; |
| 536 | | − } else |
| 537 | | −#endif |
| 538 | | − { |
| 539 | | − slab_lock(slab); |
| 540 | | − if (slab->freelist == freelist_old && |
| 541 | | − slab->counters == counters_old) { |
| 542 | | − slab->freelist = freelist_new; |
| 543 | | − slab->counters = counters_new; |
| 544 | | − slab_unlock(slab); |
| 545 | | − return true; |
| 546 | | − } |
| 547 | | − slab_unlock(slab); |
| 572 | + ret = __update_freelist_fast(slab, freelist_old, counters_old, |
| 573 | + freelist_new, counters_new); |
| 574 | + } else { |
| 575 | + ret = __update_freelist_slow(slab, freelist_old, counters_old, |
| 576 | + freelist_new, counters_new); |
| 548 | 577 | } |
| 578 | + if (likely(ret)) |
| 579 | + return true; |
| 549 | 580 | |
| 550 | 581 | cpu_relax(); |
| 551 | 582 | stat(s, CMPXCHG_DOUBLE_FAIL); |
| @@ -557,36 +588,26 @@ static inline bool __cmpxchg_double_slab(struct kmem_cache *s, struct slab *slab · 5 unmodified lines |
| 557 | 588 | return false; |
| 558 | 589 | } |
| 559 | 590 | |
| 560 | | −static inline bool cmpxchg_double_slab(struct kmem_cache *s, struct slab *slab, |
| 591 | +static inline bool slab_update_freelist(struct kmem_cache *s, struct slab *slab, |
| 561 | 592 | void *freelist_old, unsigned long counters_old, |
| 562 | 593 | void *freelist_new, unsigned long counters_new, |
| 563 | 594 | const char *n) |
| 564 | 595 | { |
| 565 | | −#if defined(CONFIG_HAVE_CMPXCHG_DOUBLE) && \ |
| 566 | | − defined(CONFIG_HAVE_ALIGNED_STRUCT_PAGE) |
| 596 | + bool ret; |
| 597 | + |
| 567 | 598 | if (s->flags & __CMPXCHG_DOUBLE) { |
| 568 | | − if (cmpxchg_double(&slab->freelist, &slab->counters, |
| 569 | | − freelist_old, counters_old, |
| 570 | | − freelist_new, counters_new)) |
| 571 | | − return true; |
| 572 | | − } else |
| 573 | | −#endif |
| 574 | | − { |
| 599 | + ret = __update_freelist_fast(slab, freelist_old, counters_old, |
| 600 | + freelist_new, counters_new); |
| 601 | + } else { |
| 575 | 602 | unsigned long flags; |
| 576 | 603 | |
| 577 | 604 | local_irq_save(flags); |
| 578 | | − slab_lock(slab); |
| 579 | | − if (slab->freelist == freelist_old && |
| 580 | | − slab->counters == counters_old) { |
| 581 | | − slab->freelist = freelist_new; |
| 582 | | − slab->counters = counters_new; |
| 583 | | − slab_unlock(slab); |
| 584 | | − local_irq_restore(flags); |
| 585 | | − return true; |
| 586 | | − } |
| 587 | | − slab_unlock(slab); |
| 605 | + ret = __update_freelist_slow(slab, freelist_old, counters_old, |
| 606 | + freelist_new, counters_new); |
| 588 | 607 | local_irq_restore(flags); |
| 589 | 608 | } |
| 609 | + if (likely(ret)) |
| 610 | + return true; |
| 590 | 611 | |
| 591 | 612 | cpu_relax(); |
| 592 | 613 | stat(s, CMPXCHG_DOUBLE_FAIL); |
| @@ -2228,7 +2249,7 @@ static inline void *acquire_slab(struct kmem_cache *s, · 1635 unmodified lines |
| 2228 | 2249 | VM_BUG_ON(new.frozen); |
| 2229 | 2250 | new.frozen = 1; |
| 2230 | 2251 | |
| 2231 | | − if (!__cmpxchg_double_slab(s, slab, |
| 2252 | + if (!__slab_update_freelist(s, slab, |
| 2232 | 2253 | freelist, counters, |
| 2233 | 2254 | new.freelist, new.counters, |
| 2234 | 2255 | "acquire_slab")) |
| @@ -2554,7 +2575,7 @@ redo: · 319 unmodified lines |
| 2554 | 2575 | } |
| 2555 | 2576 | |
| 2556 | 2577 | |
| 2557 | | − if (!cmpxchg_double_slab(s, slab, |
| 2578 | + if (!slab_update_freelist(s, slab, |
| 2558 | 2579 | old.freelist, old.counters, |
| 2559 | 2580 | new.freelist, new.counters, |
| 2560 | 2581 | "unfreezing slab")) { |
| @@ -2611,7 +2632,7 @@ static void __unfreeze_partials(struct kmem_cache *s, struct slab *partial_slab) · 50 unmodified lines |
| 2611 | 2632 | |
| 2612 | 2633 | new.frozen = 0; |
| 2613 | 2634 | |
| 2614 | | − } while (!__cmpxchg_double_slab(s, slab, |
| 2635 | + } while (!__slab_update_freelist(s, slab, |
| 2615 | 2636 | old.freelist, old.counters, |
| 2616 | 2637 | new.freelist, new.counters, |
| 2617 | 2638 | "unfreezing slab")); |
| @@ -3008,6 +3029,18 @@ static inline bool pfmemalloc_match(struct slab *slab, gfp_t gfpflags) · 390 unmodified lines |
| 3008 | 3029 | } |
| 3009 | 3030 | |
| 3010 | 3031 | #ifndef CONFIG_SLUB_TINY |
| 3032 | +static inline bool |
| 3033 | +__update_cpu_freelist_fast(struct kmem_cache *s, |
| 3034 | + void *freelist_old, void *freelist_new, |
| 3035 | + unsigned long tid) |
| 3036 | +{ |
| 3037 | + freelist_aba_t old = { .freelist = freelist_old, .counter = tid }; |
| 3038 | + freelist_aba_t new = { .freelist = freelist_new, .counter = next_tid(tid) }; |
| 3039 | + |
| 3040 | + return this_cpu_try_cmpxchg_freelist(s->cpu_slab->freelist_tid.full, |
| 3041 | + &old.full, new.full); |
| 3042 | +} |
| 3043 | + |
| 3011 | 3044 | /* |
| 3012 | 3045 | * Check the slab->freelist and either transfer the freelist to the |
| 3013 | 3046 | * per cpu freelist or deactivate the slab. |
| @@ -3034,7 +3067,7 @@ static inline void *get_freelist(struct kmem_cache *s, struct slab *slab) · 20 unmodified lines |
| 3034 | 3067 | new.inuse = slab->objects; |
| 3035 | 3068 | new.frozen = freelist != NULL; |
| 3036 | 3069 | |
| 3037 | | − } while (!__cmpxchg_double_slab(s, slab, |
| 3070 | + } while (!__slab_update_freelist(s, slab, |
| 3038 | 3071 | freelist, counters, |
| 3039 | 3072 | NULL, new.counters, |
| 3040 | 3073 | "get_freelist")); |
| @@ -3359,11 +3392,7 @@ redo: · 318 unmodified lines |
| 3359 | 3392 | * against code executing on this cpu *not* from access by |
| 3360 | 3393 | * other cpus. |
| 3361 | 3394 | */ |
| 3362 | | − if (unlikely(!this_cpu_cmpxchg_double( |
| 3363 | | − s->cpu_slab->freelist, s->cpu_slab->tid, |
| 3364 | | − object, tid, |
| 3365 | | − next_object, next_tid(tid)))) { |
| 3366 | | − |
| 3395 | + if (unlikely(!__update_cpu_freelist_fast(s, object, next_object, tid))) { |
| 3367 | 3396 | note_cmpxchg_failure("slab_alloc", s, tid); |
| 3368 | 3397 | goto redo; |
| 3369 | 3398 | } |
| @@ -3631,7 +3660,7 @@ static void __slab_free(struct kmem_cache *s, struct slab *slab, · 261 unmodified lines |
| 3631 | 3660 | } |
| 3632 | 3661 | } |
| 3633 | 3662 | |
| 3634 | | − } while (!cmpxchg_double_slab(s, slab, |
| 3663 | + } while (!slab_update_freelist(s, slab, |
| 3635 | 3664 | prior, counters, |
| 3636 | 3665 | head, new.counters, |
| 3637 | 3666 | "__slab_free")); |
| @@ -3736,11 +3765,7 @@ redo: · 98 unmodified lines |
| 3736 | 3765 | |
| 3737 | 3766 | set_freepointer(s, tail_obj, freelist); |
| 3738 | 3767 | |
| 3739 | | − if (unlikely(!this_cpu_cmpxchg_double( |
| 3740 | | − s->cpu_slab->freelist, s->cpu_slab->tid, |
| 3741 | | − freelist, tid, |
| 3742 | | − head, next_tid(tid)))) { |
| 3743 | | − |
| 3768 | + if (unlikely(!__update_cpu_freelist_fast(s, freelist, head, tid))) { |
| 3744 | 3769 | note_cmpxchg_failure("slab_free", s, tid); |
| 3745 | 3770 | goto redo; |
| 3746 | 3771 | } |
| @@ -4505,11 +4530,11 @@ static int kmem_cache_open(struct kmem_cache *s, slab_flags_t flags) · 758 unmodified lines |
| 4505 | 4530 | } |
| 4506 | 4531 | } |
| 4507 | 4532 | |
| 4508 | | −#if defined(CONFIG_HAVE_CMPXCHG_DOUBLE) && \ |
| 4509 | | − defined(CONFIG_HAVE_ALIGNED_STRUCT_PAGE) |
| 4510 | | − if (system_has_cmpxchg_double() && (s->flags & SLAB_NO_CMPXCHG) == 0) |
| 4533 | +#ifdef system_has_freelist_aba |
| 4534 | + if (system_has_freelist_aba() && !(s->flags & SLAB_NO_CMPXCHG)) { |
| 4511 | 4535 | /* Enable fast mode */ |
| 4512 | 4536 | s->flags |= __CMPXCHG_DOUBLE; |
| 4537 | + } |
| 4513 | 4538 | #endif |
| 4514 | 4539 | |
| 4515 | 4540 | /* |