Commit 1f84260c8ce3b1ce26d4c1d6dedc2f33a3a29c0c
Committed by
Christoph Lameter
1 parent
683d0baad3
Exists in
master
and in
20 other branches
SLUB: Alternate fast paths using cmpxchg_local
Provide an alternate implementation of the SLUB fast paths for alloc and free using cmpxchg_local. The cmpxchg_local fast path is selected for arches that have CONFIG_FAST_CMPXCHG_LOCAL set. An arch should only set CONFIG_FAST_CMPXCHG_LOCAL if the cmpxchg_local is faster than an interrupt enable/disable sequence. This is known to be true for both x86 platforms so set FAST_CMPXCHG_LOCAL for both arches. Currently another requirement for the fastpath is that the kernel is compiled without preemption. The restriction will go away with the introduction of a new per cpu allocator and new per cpu operations. The advantages of a cmpxchg_local based fast path are: 1. Potentially lower cycle count (30%-60% faster) 2. There is no need to disable and enable interrupts on the fast path. Currently interrupts have to be disabled and enabled on every slab operation. This is likely avoiding a significant percentage of interrupt off / on sequences in the kernel. 3. The disposal of freed slabs can occur with interrupts enabled. The alternate path is realized using #ifdef's. Several attempts to do the same with macros and inline functions resulted in a mess (in particular due to the strange way that local_interrupt_save() handles its argument and due to the need to define macros/functions that sometimes disable interrupts and sometimes do something else). [clameter: Stripped preempt bits and disabled fastpath if preempt is enabled] Signed-off-by: Christoph Lameter <clameter@sgi.com> Reviewed-by: Pekka Enberg <penberg@cs.helsinki.fi> Cc: <linux-arch@vger.kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Showing 2 changed files with 92 additions and 5 deletions Side-by-side Diff
arch/x86/Kconfig
mm/slub.c
... | ... | @@ -149,6 +149,13 @@ |
149 | 149 | /* Enable to test recovery from slab corruption on boot */ |
150 | 150 | #undef SLUB_RESILIENCY_TEST |
151 | 151 | |
152 | +/* | |
153 | + * Currently fastpath is not supported if preemption is enabled. | |
154 | + */ | |
155 | +#if defined(CONFIG_FAST_CMPXCHG_LOCAL) && !defined(CONFIG_PREEMPT) | |
156 | +#define SLUB_FASTPATH | |
157 | +#endif | |
158 | + | |
152 | 159 | #if PAGE_SHIFT <= 12 |
153 | 160 | |
154 | 161 | /* |
155 | 162 | |
... | ... | @@ -1493,7 +1500,11 @@ |
1493 | 1500 | { |
1494 | 1501 | void **object; |
1495 | 1502 | struct page *new; |
1503 | +#ifdef SLUB_FASTPATH | |
1504 | + unsigned long flags; | |
1496 | 1505 | |
1506 | + local_irq_save(flags); | |
1507 | +#endif | |
1497 | 1508 | if (!c->page) |
1498 | 1509 | goto new_slab; |
1499 | 1510 | |
1500 | 1511 | |
... | ... | @@ -1512,7 +1523,12 @@ |
1512 | 1523 | c->page->inuse = s->objects; |
1513 | 1524 | c->page->freelist = c->page->end; |
1514 | 1525 | c->node = page_to_nid(c->page); |
1526 | +unlock_out: | |
1515 | 1527 | slab_unlock(c->page); |
1528 | +out: | |
1529 | +#ifdef SLUB_FASTPATH | |
1530 | + local_irq_restore(flags); | |
1531 | +#endif | |
1516 | 1532 | return object; |
1517 | 1533 | |
1518 | 1534 | another_slab: |
... | ... | @@ -1542,7 +1558,8 @@ |
1542 | 1558 | c->page = new; |
1543 | 1559 | goto load_freelist; |
1544 | 1560 | } |
1545 | - return NULL; | |
1561 | + object = NULL; | |
1562 | + goto out; | |
1546 | 1563 | debug: |
1547 | 1564 | object = c->page->freelist; |
1548 | 1565 | if (!alloc_debug_processing(s, c->page, object, addr)) |
... | ... | @@ -1551,8 +1568,7 @@ |
1551 | 1568 | c->page->inuse++; |
1552 | 1569 | c->page->freelist = object[c->offset]; |
1553 | 1570 | c->node = -1; |
1554 | - slab_unlock(c->page); | |
1555 | - return object; | |
1571 | + goto unlock_out; | |
1556 | 1572 | } |
1557 | 1573 | |
1558 | 1574 | /* |
1559 | 1575 | |
... | ... | @@ -1569,9 +1585,36 @@ |
1569 | 1585 | gfp_t gfpflags, int node, void *addr) |
1570 | 1586 | { |
1571 | 1587 | void **object; |
1572 | - unsigned long flags; | |
1573 | 1588 | struct kmem_cache_cpu *c; |
1574 | 1589 | |
1590 | +/* | |
1591 | + * The SLUB_FASTPATH path is provisional and is currently disabled if the | |
1592 | + * kernel is compiled with preemption or if the arch does not support | |
1593 | + * fast cmpxchg operations. There are a couple of coming changes that will | |
1594 | + * simplify matters and allow preemption. Ultimately we may end up making | |
1595 | + * SLUB_FASTPATH the default. | |
1596 | + * | |
1597 | + * 1. The introduction of the per cpu allocator will avoid array lookups | |
1598 | + * through get_cpu_slab(). A special register can be used instead. | |
1599 | + * | |
1600 | + * 2. The introduction of per cpu atomic operations (cpu_ops) means that | |
1601 | + * we can realize the logic here entirely with per cpu atomics. The | |
1602 | + * per cpu atomic ops will take care of the preemption issues. | |
1603 | + */ | |
1604 | + | |
1605 | +#ifdef SLUB_FASTPATH | |
1606 | + c = get_cpu_slab(s, raw_smp_processor_id()); | |
1607 | + do { | |
1608 | + object = c->freelist; | |
1609 | + if (unlikely(is_end(object) || !node_match(c, node))) { | |
1610 | + object = __slab_alloc(s, gfpflags, node, addr, c); | |
1611 | + break; | |
1612 | + } | |
1613 | + } while (cmpxchg_local(&c->freelist, object, object[c->offset]) | |
1614 | + != object); | |
1615 | +#else | |
1616 | + unsigned long flags; | |
1617 | + | |
1575 | 1618 | local_irq_save(flags); |
1576 | 1619 | c = get_cpu_slab(s, smp_processor_id()); |
1577 | 1620 | if (unlikely(is_end(c->freelist) || !node_match(c, node))) |
... | ... | @@ -1583,6 +1626,7 @@ |
1583 | 1626 | c->freelist = object[c->offset]; |
1584 | 1627 | } |
1585 | 1628 | local_irq_restore(flags); |
1629 | +#endif | |
1586 | 1630 | |
1587 | 1631 | if (unlikely((gfpflags & __GFP_ZERO) && object)) |
1588 | 1632 | memset(object, 0, c->objsize); |
... | ... | @@ -1618,6 +1662,11 @@ |
1618 | 1662 | void *prior; |
1619 | 1663 | void **object = (void *)x; |
1620 | 1664 | |
1665 | +#ifdef SLUB_FASTPATH | |
1666 | + unsigned long flags; | |
1667 | + | |
1668 | + local_irq_save(flags); | |
1669 | +#endif | |
1621 | 1670 | slab_lock(page); |
1622 | 1671 | |
1623 | 1672 | if (unlikely(SlabDebug(page))) |
... | ... | @@ -1643,6 +1692,9 @@ |
1643 | 1692 | |
1644 | 1693 | out_unlock: |
1645 | 1694 | slab_unlock(page); |
1695 | +#ifdef SLUB_FASTPATH | |
1696 | + local_irq_restore(flags); | |
1697 | +#endif | |
1646 | 1698 | return; |
1647 | 1699 | |
1648 | 1700 | slab_empty: |
... | ... | @@ -1653,6 +1705,9 @@ |
1653 | 1705 | remove_partial(s, page); |
1654 | 1706 | |
1655 | 1707 | slab_unlock(page); |
1708 | +#ifdef SLUB_FASTPATH | |
1709 | + local_irq_restore(flags); | |
1710 | +#endif | |
1656 | 1711 | discard_slab(s, page); |
1657 | 1712 | return; |
1658 | 1713 | |
1659 | 1714 | |
... | ... | @@ -1677,9 +1732,36 @@ |
1677 | 1732 | struct page *page, void *x, void *addr) |
1678 | 1733 | { |
1679 | 1734 | void **object = (void *)x; |
1680 | - unsigned long flags; | |
1681 | 1735 | struct kmem_cache_cpu *c; |
1682 | 1736 | |
1737 | +#ifdef SLUB_FASTPATH | |
1738 | + void **freelist; | |
1739 | + | |
1740 | + c = get_cpu_slab(s, raw_smp_processor_id()); | |
1741 | + debug_check_no_locks_freed(object, s->objsize); | |
1742 | + do { | |
1743 | + freelist = c->freelist; | |
1744 | + barrier(); | |
1745 | + /* | |
1746 | + * If the compiler would reorder the retrieval of c->page to | |
1747 | + * come before c->freelist then an interrupt could | |
1748 | + * change the cpu slab before we retrieve c->freelist. We | |
1749 | + * could be matching on a page no longer active and put the | |
1750 | + * object onto the freelist of the wrong slab. | |
1751 | + * | |
1752 | + * On the other hand: If we already have the freelist pointer | |
1753 | + * then any change of cpu_slab will cause the cmpxchg to fail | |
1754 | + * since the freelist pointers are unique per slab. | |
1755 | + */ | |
1756 | + if (unlikely(page != c->page || c->node < 0)) { | |
1757 | + __slab_free(s, page, x, addr, c->offset); | |
1758 | + break; | |
1759 | + } | |
1760 | + object[c->offset] = freelist; | |
1761 | + } while (cmpxchg_local(&c->freelist, freelist, object) != freelist); | |
1762 | +#else | |
1763 | + unsigned long flags; | |
1764 | + | |
1683 | 1765 | local_irq_save(flags); |
1684 | 1766 | debug_check_no_locks_freed(object, s->objsize); |
1685 | 1767 | c = get_cpu_slab(s, smp_processor_id()); |
... | ... | @@ -1690,6 +1772,7 @@ |
1690 | 1772 | __slab_free(s, page, x, addr, c->offset); |
1691 | 1773 | |
1692 | 1774 | local_irq_restore(flags); |
1775 | +#endif | |
1693 | 1776 | } |
1694 | 1777 | |
1695 | 1778 | void kmem_cache_free(struct kmem_cache *s, void *x) |