Index: linux-2.6.18-rc4/mm/sslab.c =================================================================== --- linux-2.6.18-rc4.orig/mm/sslab.c 2006-08-09 15:16:27.701147548 -0700 +++ linux-2.6.18-rc4/mm/sslab.c 2006-08-09 16:53:49.998289897 -0700 @@ -13,10 +13,10 @@ * * Overloaded fields in struct page: * - * lru -> used to a slab on the lists - * mapping -> pointer to struct sslab - * private -> pointer to next free object - * index -> count number of elements in use + * lru -> used to a slab on the lists + * mapping -> pointer to struct sslab + * index -> pointer to next free object + * _mapcount -> count number of elements in use * * Lock order: * 1. slab_lock(page) @@ -43,12 +43,12 @@ /* Some definitions to overload certain fields in struct page */ static void *get_object_pointer(struct page *page) { - return (void *)page->private; + return (void *)page->index; } static void set_object_pointer(struct page *page, void *object) { - page->private = (unsigned long)object; + page->index = (unsigned long)object; } static struct sslab *get_sslab(struct page *page) @@ -61,6 +61,31 @@ static void set_sslab(struct page *page, page->mapping = (void *)s; } +int *object_counter(struct page *page) +{ + return (int *)&page->_mapcount; +} + +static void inc_object_counter(struct page *page) +{ + (*object_counter(page))++; +} + +static void dec_object_counter(struct page *page) +{ + (*object_counter(page))--; +} + +static void set_object_counter(struct page *page, int counter) +{ + (*object_counter(page))= counter; +} + +static int get_object_counter(struct page *page) +{ + return (*object_counter(page)); +} + /* * Locking for each individual slab using the pagelock */ @@ -80,16 +105,46 @@ static void slab_unlock(struct page *pag /* * Discard an unused slab page + * List lock is held. */ static void discard_slab(struct sslab *s, struct page *page) { + list_del(&page->lru); + s->slabs--; set_sslab(page, NULL); set_object_pointer(page, NULL); + set_object_counter(page, -1); /* -1 is convention for mapcount */ __ClearPageSlab(page); __free_pages(page, s->order); sub_zone_page_state(page_zone(page), NR_SLAB, 1 << s->order); } +static struct page *new_slab(struct sslab *s) +{ + void **p, **last; + int i; + struct page *page; + + page = alloc_pages(s->gfpflags, s->order); + if (!page) + return NULL; + + set_sslab(page, s); + last = page_address(page); + set_object_pointer(page, last); + + for (i = 0; i < s->objects - 1; i++) { + p = last + s->size / sizeof(void *); + *last = p; + last = p; + } + *last = NULL; + + __SetPageSlab(page); + set_object_counter(page, 0); + add_zone_page_state(page_zone(page), NR_SLAB, 1 << s->order); + return page; +} /* * Return a per cpu slab by taking it off the active list. */ @@ -115,19 +170,17 @@ redo: page = s->cpuslab[cpu]; s->cpuslab[cpu] = NULL; ClearPageActive(page); - if (page->index) { - if (page->index < s->objects) + if (get_object_counter(page)) { + if (get_object_counter(page) < s->objects) list_move(&page->lru, &s->partial); else list_move(&page->lru, &s->full); spin_unlock(&s->list_lock); slab_unlock(page); } else { - /* The slab is empty. Discard it. */ - list_del(&page->lru); - spin_unlock(&s->list_lock); slab_unlock(page); discard_slab(s, page); + spin_unlock(&s->list_lock); } } @@ -166,37 +219,23 @@ static int reload(struct sslab *s, int c list_move(&page->lru, &s->active); } else { - void **p, **last; - int i; /* No slabs with free objets */ spin_unlock(&s->list_lock); - page = alloc_pages(s->gfpflags, s->order); + page = new_slab(s); if (!page) return 0; - set_sslab(page, s); - last = page_address(page); - set_object_pointer(page, last); - - for (i = 0; i < s->objects - 1; i++) { - p = last + s->size / sizeof(void *); - *last = p; - last = p; - } - *last = NULL; - spin_lock(&s->list_lock); - __SetPageSlab(page); s->slabs++; - add_zone_page_state(page_zone(page), NR_SLAB, 1 << s->order); list_add(&page->lru, &s->active); } SetPageActive(page); ClearPageReferenced(page); s->cpuslab[cpu] = page; spin_unlock(&s->list_lock); - schedule_delayed_work(&s->flush[cpu], 10 * HZ); + if (keventd_up()) + schedule_delayed_work_on(cpu, &s->flush[cpu], 10 * HZ); return 1; } @@ -235,9 +274,11 @@ int sslab_create(struct sslab *s, size_t { int cpu; - s->size = size; + s->size = ALIGN(size, sizeof(void *)); s->order = order; - s->gfpflags = flags | __GFP_COMP; + s->gfpflags = flags; + if (order > 1) + s->gfpflags |= __GFP_COMP; s->objects = (PAGE_SIZE << order) / size; if (!s->objects) return -EINVAL; @@ -249,7 +290,7 @@ int sslab_create(struct sslab *s, size_t s->slabs = 0; for_each_possible_cpu(cpu) { s->cpuslab[cpu] = NULL; - INIT_WORK(&s->flush[cpu], flusher, s); + INIT_WORK(&s->flush[cpu], &flusher, s); } return 0; } @@ -274,7 +315,7 @@ void *sslab_alloc(struct sslab *s, gfp_t x = get_object_pointer(page); if (likely(x)) { set_object_pointer(page, *x); - page->index++; + inc_object_counter(page); SetPageReferenced(page); slab_unlock(page); check_free_chain(s, page); @@ -322,9 +363,11 @@ void sslab_free(struct sslab *s, void *o check_free_chain(s, page); check_valid_pointer(s, page, object); slab_lock(page); - * (void **) object = prior = get_object_pointer(page); + prior = get_object_pointer(page); + * (void **) object = prior; set_object_pointer(page, object); - leftover = --page->index; + dec_object_counter(page); + leftover = get_object_counter(page); slab_unlock(page); if (unlikely(PageActive(page))) @@ -333,14 +376,12 @@ void sslab_free(struct sslab *s, void *o if (unlikely(leftover == 0)) { spin_lock(&s->list_lock); if (PageActive(page)) { - /* No discarding of pages in active allocation */ + /* No discarding of slabs under active allocation */ spin_unlock(&s->list_lock); return; } - list_del(&page->lru); - s->slabs--; - spin_unlock(&s->list_lock); discard_slab(s, page); + spin_unlock(&s->list_lock); } if (unlikely(!prior)) { @@ -378,16 +419,60 @@ int sslab_pointer_valid(struct sslab *s, if ((object - slab_addr) & s->size) return 0; - return 0; + return 1; +} + +/* + * Preload the cache with enough slabs so that we we not need to + * allocate for the specified number of objects. + */ +void sslab_preload(struct sslab *s, unsigned long nr) +{ + int nr_local = 0; + int cpu = smp_processor_id(); + struct page * page = s->cpuslab[cpu]; + + if (page) + nr_local = get_object_counter(page); + + nr_local += sslab_objects(s); + + while (nr_local < nr) { + struct page *page = new_slab(s); + + spin_lock(&s->list_lock); + list_add_tail(&page->lru, &s->partial); + s->slabs++; + spin_unlock(&s->list_lock); + nr_local += s->objects; + } } + static void drain_cpu(void *v) { struct sslab *s = v; + flush_cpuslab(s, smp_processor_id()); } +/* + * Try to remove as many slabs as possible. In particular try to undo the + * effect of sslab_preload which may have added empty pages to the + * partial list. + */ void sslab_shrink(struct sslab *s) { + struct list_head *h1,*h2; + + spin_lock(&s->list_lock); + list_for_each_safe(h1, h2, &s->partial) { + struct page *page = lru_to_page(h1); + + if (get_object_counter(page) == 0) + discard_slab(s, page); + } + spin_unlock(&s->list_lock); + /* * Free each of the per cpu slabs */ @@ -396,17 +481,14 @@ void sslab_shrink(struct sslab *s) { static void free_list(struct sslab *s, struct list_head *list) { - while (!list_empty(list)) { - struct page *page = lru_to_page(list); + while (!list_empty(list)) + discard_slab(s, lru_to_page(list)); - list_del(&page->lru); - s->slabs--; - discard_slab(s, page); - } } /* - * Release all leftover slabs + * Release all leftover slabs. If there are any leftover pointers dangling + * to these objects then we will get into a lot of trouble later. */ void sslab_destroy(struct sslab *s) { @@ -416,34 +498,31 @@ void sslab_destroy(struct sslab *s) free_list(s, &s->full); free_list(s, &s->partial); spin_unlock(&s->list_lock); + /* Just to make sure that no one uses this again */ s->size = 0; BUG_ON(s->slabs); } -#if 0 -void sslab_preload(struct sslab *s, unsigned long nr) +static unsigned long count_objects(struct sslab *s, struct list_head *list) { - nr_local = 0; - - if (s->meta[cpu]) - nr_local = s->objects - bit_weight(s->meta[cpu]->bitmap, s->objects); + int count = 0; + struct list_head *h1; - /* Count objects on the partial list */ - - for_each(....) { - nr_local += s->objects - bit_weight( xx->bitmap, s->objects); - } + spin_lock(&s->list_lock); + list_for_each(h1, list) { + struct page *page = lru_to_page(h1); - while (nr_local < nr) { - /* alloc new slab */ - nr_local += s->objects; + count += get_object_counter(page); } - /* - * Add additional blocks to the page list until we have enough - * elements in there. - */ + spin_unlock(&s->list_lock); + return count; } -#endif +unsigned long sslab_objects(struct sslab *s) +{ + return count_objects(s, &s->partial) + + count_objects(s, &s->full) + + count_objects(s, &s->active); +} Index: linux-2.6.18-rc4/include/linux/sslab.h =================================================================== --- linux-2.6.18-rc4.orig/include/linux/sslab.h 2006-08-09 16:33:38.799711183 -0700 +++ linux-2.6.18-rc4/include/linux/sslab.h 2006-08-09 16:53:10.289804906 -0700 @@ -74,3 +74,6 @@ extern void sslab_shrink(struct sslab *) extern int sslab_pointer_valid(struct sslab *, void *); +extern unsigned long sslab_objects(struct sslab *); + +