aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristoph Lameter <cl@linux.com>2011-08-09 17:12:27 -0400
committerPekka Enberg <penberg@kernel.org>2011-08-19 12:34:27 -0400
commit49e2258586b423684f03c278149ab46d8f8b6700 (patch)
treed4404d1b09d6fe505da29a32602d193c4ef56ac9
parent497b66f2ecc97844493e6a147fd5a7e73f73f408 (diff)
slub: per cpu cache for partial pages
Allow filling out the rest of the kmem_cache_cpu cacheline with pointers to partial pages. The partial page list is used in slab_free() to avoid per node lock taking. In __slab_alloc() we can then take multiple partial pages off the per node partial list in one go reducing node lock pressure. We can also use the per cpu partial list in slab_alloc() to avoid scanning partial lists for pages with free objects. The main effect of a per cpu partial list is that the per node list_lock is taken for batches of partial pages instead of individual ones. Potential future enhancements: 1. The pickup from the partial list could be perhaps be done without disabling interrupts with some work. The free path already puts the page into the per cpu partial list without disabling interrupts. 2. __slab_free() may have some code paths that could use optimization. Performance: Before After ./hackbench 100 process 200000 Time: 1953.047 1564.614 ./hackbench 100 process 20000 Time: 207.176 156.940 ./hackbench 100 process 20000 Time: 204.468 156.940 ./hackbench 100 process 20000 Time: 204.879 158.772 ./hackbench 10 process 20000 Time: 20.153 15.853 ./hackbench 10 process 20000 Time: 20.153 15.986 ./hackbench 10 process 20000 Time: 19.363 16.111 ./hackbench 1 process 20000 Time: 2.518 2.307 ./hackbench 1 process 20000 Time: 2.258 2.339 ./hackbench 1 process 20000 Time: 2.864 2.163 Signed-off-by: Christoph Lameter <cl@linux.com> Signed-off-by: Pekka Enberg <penberg@kernel.org>
-rw-r--r--include/linux/mm_types.h14
-rw-r--r--include/linux/slub_def.h4
-rw-r--r--mm/slub.c339
3 files changed, 309 insertions, 48 deletions
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index 774b8952deb4..7870e473033c 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -79,9 +79,21 @@ struct page {
79 }; 79 };
80 80
81 /* Third double word block */ 81 /* Third double word block */
82 struct list_head lru; /* Pageout list, eg. active_list 82 union {
83 struct list_head lru; /* Pageout list, eg. active_list
83 * protected by zone->lru_lock ! 84 * protected by zone->lru_lock !
84 */ 85 */
86 struct { /* slub per cpu partial pages */
87 struct page *next; /* Next partial slab */
88#ifdef CONFIG_64BIT
89 int pages; /* Nr of partial slabs left */
90 int pobjects; /* Approximate # of objects */
91#else
92 short int pages;
93 short int pobjects;
94#endif
95 };
96 };
85 97
86 /* Remainder is not double word aligned */ 98 /* Remainder is not double word aligned */
87 union { 99 union {
diff --git a/include/linux/slub_def.h b/include/linux/slub_def.h
index f58d6413d230..4890ef79d752 100644
--- a/include/linux/slub_def.h
+++ b/include/linux/slub_def.h
@@ -36,12 +36,15 @@ enum stat_item {
36 ORDER_FALLBACK, /* Number of times fallback was necessary */ 36 ORDER_FALLBACK, /* Number of times fallback was necessary */
37 CMPXCHG_DOUBLE_CPU_FAIL,/* Failure of this_cpu_cmpxchg_double */ 37 CMPXCHG_DOUBLE_CPU_FAIL,/* Failure of this_cpu_cmpxchg_double */
38 CMPXCHG_DOUBLE_FAIL, /* Number of times that cmpxchg double did not match */ 38 CMPXCHG_DOUBLE_FAIL, /* Number of times that cmpxchg double did not match */
39 CPU_PARTIAL_ALLOC, /* Used cpu partial on alloc */
40 CPU_PARTIAL_FREE, /* USed cpu partial on free */
39 NR_SLUB_STAT_ITEMS }; 41 NR_SLUB_STAT_ITEMS };
40 42
41struct kmem_cache_cpu { 43struct kmem_cache_cpu {
42 void **freelist; /* Pointer to next available object */ 44 void **freelist; /* Pointer to next available object */
43 unsigned long tid; /* Globally unique transaction id */ 45 unsigned long tid; /* Globally unique transaction id */
44 struct page *page; /* The slab from which we are allocating */ 46 struct page *page; /* The slab from which we are allocating */
47 struct page *partial; /* Partially allocated frozen slabs */
45 int node; /* The node of the page (or -1 for debug) */ 48 int node; /* The node of the page (or -1 for debug) */
46#ifdef CONFIG_SLUB_STATS 49#ifdef CONFIG_SLUB_STATS
47 unsigned stat[NR_SLUB_STAT_ITEMS]; 50 unsigned stat[NR_SLUB_STAT_ITEMS];
@@ -79,6 +82,7 @@ struct kmem_cache {
79 int size; /* The size of an object including meta data */ 82 int size; /* The size of an object including meta data */
80 int objsize; /* The size of an object without meta data */ 83 int objsize; /* The size of an object without meta data */
81 int offset; /* Free pointer offset. */ 84 int offset; /* Free pointer offset. */
85 int cpu_partial; /* Number of per cpu partial pages to keep around */
82 struct kmem_cache_order_objects oo; 86 struct kmem_cache_order_objects oo;
83 87
84 /* Allocation and freeing of slabs */ 88 /* Allocation and freeing of slabs */
diff --git a/mm/slub.c b/mm/slub.c
index df381af963b7..0e286acef62a 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -1560,7 +1560,7 @@ static inline void remove_partial(struct kmem_cache_node *n,
1560 */ 1560 */
1561static inline void *acquire_slab(struct kmem_cache *s, 1561static inline void *acquire_slab(struct kmem_cache *s,
1562 struct kmem_cache_node *n, struct page *page, 1562 struct kmem_cache_node *n, struct page *page,
1563 struct kmem_cache_cpu *c) 1563 int mode)
1564{ 1564{
1565 void *freelist; 1565 void *freelist;
1566 unsigned long counters; 1566 unsigned long counters;
@@ -1575,7 +1575,8 @@ static inline void *acquire_slab(struct kmem_cache *s,
1575 freelist = page->freelist; 1575 freelist = page->freelist;
1576 counters = page->counters; 1576 counters = page->counters;
1577 new.counters = counters; 1577 new.counters = counters;
1578 new.inuse = page->objects; 1578 if (mode)
1579 new.inuse = page->objects;
1579 1580
1580 VM_BUG_ON(new.frozen); 1581 VM_BUG_ON(new.frozen);
1581 new.frozen = 1; 1582 new.frozen = 1;
@@ -1586,34 +1587,20 @@ static inline void *acquire_slab(struct kmem_cache *s,
1586 "lock and freeze")); 1587 "lock and freeze"));
1587 1588
1588 remove_partial(n, page); 1589 remove_partial(n, page);
1589 1590 return freelist;
1590 if (freelist) {
1591 /* Populate the per cpu freelist */
1592 c->page = page;
1593 c->node = page_to_nid(page);
1594 stat(s, ALLOC_FROM_PARTIAL);
1595
1596 return freelist;
1597 } else {
1598 /*
1599 * Slab page came from the wrong list. No object to allocate
1600 * from. Put it onto the correct list and continue partial
1601 * scan.
1602 */
1603 printk(KERN_ERR "SLUB: %s : Page without available objects on"
1604 " partial list\n", s->name);
1605 return NULL;
1606 }
1607} 1591}
1608 1592
1593static int put_cpu_partial(struct kmem_cache *s, struct page *page, int drain);
1594
1609/* 1595/*
1610 * Try to allocate a partial slab from a specific node. 1596 * Try to allocate a partial slab from a specific node.
1611 */ 1597 */
1612static void *get_partial_node(struct kmem_cache *s, 1598static void *get_partial_node(struct kmem_cache *s,
1613 struct kmem_cache_node *n, struct kmem_cache_cpu *c) 1599 struct kmem_cache_node *n, struct kmem_cache_cpu *c)
1614{ 1600{
1615 struct page *page; 1601 struct page *page, *page2;
1616 void *object; 1602 void *object = NULL;
1603 int count = 0;
1617 1604
1618 /* 1605 /*
1619 * Racy check. If we mistakenly see no partial slabs then we 1606 * Racy check. If we mistakenly see no partial slabs then we
@@ -1625,13 +1612,28 @@ static void *get_partial_node(struct kmem_cache *s,
1625 return NULL; 1612 return NULL;
1626 1613
1627 spin_lock(&n->list_lock); 1614 spin_lock(&n->list_lock);
1628 list_for_each_entry(page, &n->partial, lru) { 1615 list_for_each_entry_safe(page, page2, &n->partial, lru) {
1629 object = acquire_slab(s, n, page, c); 1616 void *t = acquire_slab(s, n, page, count == 0);
1630 if (object) 1617 int available;
1631 goto out; 1618
1619 if (!t)
1620 break;
1621
1622 if (!count) {
1623 c->page = page;
1624 c->node = page_to_nid(page);
1625 stat(s, ALLOC_FROM_PARTIAL);
1626 count++;
1627 object = t;
1628 available = page->objects - page->inuse;
1629 } else {
1630 page->freelist = t;
1631 available = put_cpu_partial(s, page, 0);
1632 }
1633 if (kmem_cache_debug(s) || available > s->cpu_partial / 2)
1634 break;
1635
1632 } 1636 }
1633 object = NULL;
1634out:
1635 spin_unlock(&n->list_lock); 1637 spin_unlock(&n->list_lock);
1636 return object; 1638 return object;
1637} 1639}
@@ -1926,6 +1928,123 @@ redo:
1926 } 1928 }
1927} 1929}
1928 1930
1931/* Unfreeze all the cpu partial slabs */
1932static void unfreeze_partials(struct kmem_cache *s)
1933{
1934 struct kmem_cache_node *n = NULL;
1935 struct kmem_cache_cpu *c = this_cpu_ptr(s->cpu_slab);
1936 struct page *page;
1937
1938 while ((page = c->partial)) {
1939 enum slab_modes { M_PARTIAL, M_FREE };
1940 enum slab_modes l, m;
1941 struct page new;
1942 struct page old;
1943
1944 c->partial = page->next;
1945 l = M_FREE;
1946
1947 do {
1948
1949 old.freelist = page->freelist;