[T106][ZXW-22]7520V3SCV2.01.01.02P42U09_VEC_V0.8_AP_VEC origin source commit
Change-Id: Ic6e05d89ecd62fc34f82b23dcf306c93764aec4b
diff --git a/ap/os/linux/linux-3.4.x/mm/slob.c b/ap/os/linux/linux-3.4.x/mm/slob.c
new file mode 100755
index 0000000..5e6b4d7
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/mm/slob.c
@@ -0,0 +1,1138 @@
+/*
+ * SLOB Allocator: Simple List Of Blocks
+ *
+ * Matt Mackall <mpm@selenic.com> 12/30/03
+ *
+ * NUMA support by Paul Mundt, 2007.
+ *
+ * How SLOB works:
+ *
+ * The core of SLOB is a traditional K&R style heap allocator, with
+ * support for returning aligned objects. The granularity of this
+ * allocator is as little as 2 bytes, however typically most architectures
+ * will require 4 bytes on 32-bit and 8 bytes on 64-bit.
+ *
+ * The slob heap is a set of linked list of pages from alloc_pages(),
+ * and within each page, there is a singly-linked list of free blocks
+ * (slob_t). The heap is grown on demand. To reduce fragmentation,
+ * heap pages are segregated into three lists, with objects less than
+ * 256 bytes, objects less than 1024 bytes, and all other objects.
+ *
+ * Allocation from heap involves first searching for a page with
+ * sufficient free blocks (using a next-fit-like approach) followed by
+ * a first-fit scan of the page. Deallocation inserts objects back
+ * into the free list in address order, so this is effectively an
+ * address-ordered first fit.
+ *
+ * Above this is an implementation of kmalloc/kfree. Blocks returned
+ * from kmalloc are prepended with a 4-byte header with the kmalloc size.
+ * If kmalloc is asked for objects of PAGE_SIZE or larger, it calls
+ * alloc_pages() directly, allocating compound pages so the page order
+ * does not have to be separately tracked, and also stores the exact
+ * allocation size in page->private so that it can be used to accurately
+ * provide ksize(). These objects are detected in kfree() because slob_page()
+ * is false for them.
+ *
+ * SLAB is emulated on top of SLOB by simply calling constructors and
+ * destructors for every SLAB allocation. Objects are returned with the
+ * 4-byte alignment unless the SLAB_HWCACHE_ALIGN flag is set, in which
+ * case the low-level allocator will fragment blocks to create the proper
+ * alignment. Again, objects of page-size or greater are allocated by
+ * calling alloc_pages(). As SLAB objects know their size, no separate
+ * size bookkeeping is necessary and there is essentially no allocation
+ * space overhead, and compound pages aren't needed for multi-page
+ * allocations.
+ *
+ * NUMA support in SLOB is fairly simplistic, pushing most of the real
+ * logic down to the page allocator, and simply doing the node accounting
+ * on the upper levels. In the event that a node id is explicitly
+ * provided, alloc_pages_exact_node() with the specified node id is used
+ * instead. The common case (or when the node id isn't explicitly provided)
+ * will default to the current node, as per numa_node_id().
+ *
+ * Node aware pages are still inserted in to the global freelist, and
+ * these are scanned for by matching against the node id encoded in the
+ * page flags. As a result, block allocations that can be satisfied from
+ * the freelist will only be done so on pages residing on the same node,
+ * in order to prevent random node placement.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/swap.h> /* struct reclaim_state */
+#include <linux/cache.h>
+#include <linux/init.h>
+#include <linux/export.h>
+#include <linux/rcupdate.h>
+#include <linux/list.h>
+#include <linux/kmemleak.h>
+
+#include <trace/events/kmem.h>
+
+#include <linux/atomic.h>
+
+#ifdef CONFIG_KMALLOC_TRACKER
+#include <linux/mem_tracker_def.h>
+#endif
+
+#ifdef CONFIG_DEBUG_SLOB_MARK_HEAD
+
+#define BYTES_PER_RECORD (sizeof(void *))
+#define RECORD_MAGIC 0xdeaddead
+#define RECORD_COUNT 4
+
+static void **dbg_userrecord(void *mem,int index)
+{
+ return (void **)(mem + index * BYTES_PER_RECORD);
+}
+
+static void **dbg_userhead(void *mem)
+{
+ return (void **)(mem - RECORD_COUNT * BYTES_PER_RECORD);
+}
+#endif
+
+/*
+ * slob_block has a field 'units', which indicates size of block if +ve,
+ * or offset of next block if -ve (in SLOB_UNITs).
+ *
+ * Free blocks of size 1 unit simply contain the offset of the next block.
+ * Those with larger size contain their size in the first SLOB_UNIT of
+ * memory, and the offset of the next free block in the second SLOB_UNIT.
+ */
+#if PAGE_SIZE <= (32767 * 2)
+typedef s16 slobidx_t;
+#else
+typedef s32 slobidx_t;
+#endif
+
+struct slob_block {
+ slobidx_t units;
+};
+typedef struct slob_block slob_t;
+
+/*
+ * We use struct page fields to manage some slob allocation aspects,
+ * however to avoid the horrible mess in include/linux/mm_types.h, we'll
+ * just define our own struct page type variant here.
+ */
+struct slob_page {
+ union {
+ struct {
+ unsigned long flags; /* mandatory */
+ unsigned long index; /* mandatory */
+ slobidx_t units; /* free units left in page */
+ unsigned long pad[2];
+ slob_t *free; /* first free slob_t in page */
+ struct list_head list; /* linked list of free pages */
+ };
+ struct page page;
+ };
+};
+
+unsigned int g_slob_kmalloc_pages;
+unsigned int g_slob_kmem_cache_pages;
+raw_spinlock_t g_slob_kmalloc_spin_lock = (raw_spinlock_t) __RAW_SPIN_LOCK_INITIALIZER (g_slob_kmalloc_spin_lock);
+raw_spinlock_t g_slob_kmem_cache_spin_lock = (raw_spinlock_t) __RAW_SPIN_LOCK_INITIALIZER (g_slob_kmem_cache_spin_lock);
+
+static inline void struct_slob_page_wrong_size(void)
+{ BUILD_BUG_ON(sizeof(struct slob_page) != sizeof(struct page)); }
+
+/*
+ * free_slob_page: call before a slob_page is returned to the page allocator.
+ */
+static inline void free_slob_page(struct slob_page *sp)
+{
+ reset_page_mapcount(&sp->page);
+ sp->page.mapping = NULL;
+}
+
+/*
+ * All partially free slob pages go on these lists.
+ */
+#define SLOB_BREAK1 256
+#define SLOB_BREAK2 1024
+static LIST_HEAD(free_slob_small);
+static LIST_HEAD(free_slob_medium);
+static LIST_HEAD(free_slob_large);
+
+/*
+ * is_slob_page: True for all slob pages (false for bigblock pages)
+ */
+static inline int is_slob_page(struct slob_page *sp)
+{
+ return PageSlab((struct page *)sp);
+}
+
+static inline void set_slob_page(struct slob_page *sp)
+{
+ __SetPageSlab((struct page *)sp);
+}
+
+static inline void clear_slob_page(struct slob_page *sp)
+{
+ __ClearPageSlab((struct page *)sp);
+}
+
+static inline struct slob_page *slob_page(const void *addr)
+{
+ return (struct slob_page *)virt_to_page(addr);
+}
+
+/*
+ * slob_page_free: true for pages on free_slob_pages list.
+ */
+static inline int slob_page_free(struct slob_page *sp)
+{
+ return PageSlobFree((struct page *)sp);
+}
+
+static void set_slob_page_free(struct slob_page *sp, struct list_head *list)
+{
+ list_add(&sp->list, list);
+ __SetPageSlobFree((struct page *)sp);
+}
+
+static inline void clear_slob_page_free(struct slob_page *sp)
+{
+ list_del(&sp->list);
+ __ClearPageSlobFree((struct page *)sp);
+}
+
+#ifdef CONFIG_SLOB_OPT
+#define SLOB_CHUNK_SHIFT (L1_CACHE_SHIFT)
+#define SLOB_CHUNK_SIZE (1 << (SLOB_CHUNK_SHIFT))
+#define SLOB_LIST_NUM (PAGE_SIZE >> (SLOB_CHUNK_SHIFT))
+#define SLOB_LIST_LIMIT (0)
+
+
+struct slob_item_info
+{
+ struct list_head slob_list;
+ int count;
+};
+struct slob_item_info slob_sizes[SLOB_LIST_NUM];
+#else
+#define SLOB_CHUNK_SHIFT (L1_CACHE_SHIFT)
+#define SLOB_CHUNK_SIZE (1 << (SLOB_CHUNK_SHIFT))
+#define SLOB_LIST_NUM ((PAGE_SIZE >> (SLOB_CHUNK_SHIFT)))
+struct list_head slob_sizes[SLOB_LIST_NUM];
+#endif
+static inline void set_slob_page_list_index(struct slob_page *sp, unsigned long index)
+{
+ sp->index = index;
+}
+#ifdef CONFIG_SLOB_OPT
+static inline struct slob_item_info *get_slob_page_list_head(struct slob_page *sp)
+{
+ return &slob_sizes[sp->index];
+}
+#else
+static inline struct list_head *get_slob_page_list_head(struct slob_page *sp)
+{
+ return &slob_sizes[sp->index];
+}
+#endif
+static inline void clear_slob_page_list_index(struct slob_page *sp)
+{
+ sp->index = 0;
+}
+
+#define SLOB_UNIT sizeof(slob_t)
+#define SLOB_UNITS(size) (((size) + SLOB_UNIT - 1)/SLOB_UNIT)
+#define SLOB_ALIGN L1_CACHE_BYTES
+
+/*
+ * struct slob_rcu is inserted at the tail of allocated slob blocks, which
+ * were created with a SLAB_DESTROY_BY_RCU slab. slob_rcu is used to free
+ * the block using call_rcu.
+ */
+struct slob_rcu {
+ struct rcu_head head;
+ int size;
+};
+
+/*
+ * slob_lock protects all slob allocator structures.
+ */
+static DEFINE_SPINLOCK(slob_lock);
+
+/*
+ * Encode the given size and next info into a free slob block s.
+ */
+static void set_slob(slob_t *s, slobidx_t size, slob_t *next)
+{
+ slob_t *base = (slob_t *)((unsigned long)s & PAGE_MASK);
+ slobidx_t offset = next - base;
+
+ if (size > 1) {
+ s[0].units = size;
+ s[1].units = offset;
+ } else
+ s[0].units = -offset;
+}
+
+/*
+ * Return the size of a slob block.
+ */
+static slobidx_t slob_units(slob_t *s)
+{
+ if (s->units > 0)
+ return s->units;
+ return 1;
+}
+
+/*
+ * Return the next free slob block pointer after this one.
+ */
+static slob_t *slob_next(slob_t *s)
+{
+ slob_t *base = (slob_t *)((unsigned long)s & PAGE_MASK);
+ slobidx_t next;
+
+ if (s[0].units < 0)
+ next = -s[0].units;
+ else
+ next = s[1].units;
+ return base+next;
+}
+
+/*
+ * Returns true if s is the last free block in its page.
+ */
+static int slob_last(slob_t *s)
+{
+ return !((unsigned long)slob_next(s) & ~PAGE_MASK);
+}
+
+static void *slob_new_pages(gfp_t gfp, int order, int node)
+{
+ void *page;
+
+#ifdef CONFIG_NUMA
+ if (node != -1)
+ page = alloc_pages_exact_node(node, gfp, order);
+ else
+#endif
+ page = alloc_pages(gfp, order);
+
+ if (!page)
+ return NULL;
+
+ return page_address(page);
+}
+
+static void slob_free_pages(void *b, int order)
+{
+ if (current->reclaim_state)
+ current->reclaim_state->reclaimed_slab += 1 << order;
+ free_pages((unsigned long)b, order);
+}
+
+/*
+ * Allocate a slob block within a given slob_page sp.
+ */
+static void *slob_page_alloc(struct slob_page *sp, size_t size, int align)
+{
+ slob_t *prev, *cur, *aligned = NULL;
+ int delta = 0, units = SLOB_UNITS(size);
+
+ for (prev = NULL, cur = sp->free; ; prev = cur, cur = slob_next(cur)) {
+ slobidx_t avail = slob_units(cur);
+
+ if (align) {
+ aligned = (slob_t *)ALIGN((unsigned long)cur, align);
+ delta = aligned - cur;
+ }
+ if (avail >= units + delta) { /* room enough? */
+ slob_t *next;
+
+ if (delta) { /* need to fragment head to align? */
+ next = slob_next(cur);
+ set_slob(aligned, avail - delta, next);
+ set_slob(cur, delta, aligned);
+ prev = cur;
+ cur = aligned;
+ avail = slob_units(cur);
+ }
+
+ next = slob_next(cur);
+ if (avail == units) { /* exact fit? unlink. */
+ if (prev)
+ set_slob(prev, slob_units(prev), next);
+ else
+ sp->free = next;
+ } else { /* fragment */
+ if (prev)
+ set_slob(prev, slob_units(prev), cur + units);
+ else
+ sp->free = cur + units;
+ set_slob(cur + units, avail - units, next);
+ }
+
+ sp->units -= units;
+ if (!sp->units
+#ifdef CONFIG_SLOB_OPT
+ && size != PAGE_SIZE
+#endif
+ )
+ clear_slob_page_free(sp);
+ return cur;
+ }
+ if (slob_last(cur))
+ return NULL;
+ }
+}
+
+void slob_sizes_init(void)
+{
+ int i;
+
+ for (i = 0; i < SLOB_LIST_NUM; i++)
+ {
+#ifdef CONFIG_SLOB_OPT
+ INIT_LIST_HEAD(&slob_sizes[i].slob_list);
+#else
+ INIT_LIST_HEAD(&slob_sizes[i]);
+#endif
+ }
+
+}
+
+static struct list_head *find_general_slob_list(size_t size, size_t *general_size, unsigned long *index)
+{
+ int slob_index = 0;
+
+ if (size & (SLOB_CHUNK_SIZE - 1))
+ slob_index = size >> SLOB_CHUNK_SHIFT;
+ else
+ slob_index = (size >> SLOB_CHUNK_SHIFT) - 1;
+
+ *general_size = (slob_index + 1) << SLOB_CHUNK_SHIFT;
+ *index = slob_index;
+ return &(slob_sizes[slob_index]);
+}
+
+/*
+ * slob_alloc: entry point into the slob allocator.
+ */
+static void *slob_alloc_general(size_t size, gfp_t gfp, int align, int node)
+{
+ unsigned long index;
+ size_t general_size;
+ struct slob_page *sp;
+ struct list_head *prev;
+#ifdef CONFIG_SLOB_OPT
+ struct slob_item_info *slob_list;
+#else
+ struct list_head *slob_list;
+#endif
+ slob_t *b = NULL;
+ unsigned long flags;
+
+ if (size < PAGE_SIZE)
+ slob_list = find_general_slob_list(size, &general_size, &index);
+ else
+ panic("slob alloc error");
+
+ size = general_size;
+ spin_lock_irqsave(&slob_lock, flags);
+ /* Iterate through each partially free page, try to find room */
+#ifdef CONFIG_SLOB_OPT
+ if (size ==PAGE_SIZE && slob_list->count ==0 )
+ goto alloc_out;
+
+ list_for_each_entry(sp, &(slob_list->slob_list), list) {
+#else
+ list_for_each_entry(sp, slob_list, list) {
+#endif
+#ifdef CONFIG_NUMA
+ /*
+ * If there's a node specification, search for a partial
+ * page with a matching node id in the freelist.
+ */
+ if (node != -1 && page_to_nid(&sp->page) != node)
+ continue;
+#endif
+ /* Enough room on this page? */
+ if (sp->units < SLOB_UNITS(size))
+ continue;
+
+#ifdef CONFIG_SLOB_OPT
+ /*if it is a free block,count--*/
+ if (sp->units == SLOB_UNITS(PAGE_SIZE))
+ slob_list->count--;
+#endif
+ /* Attempt to alloc */
+ prev = sp->list.prev;
+ b = slob_page_alloc(sp, size, align);
+ if (!b)
+ continue;
+
+ /* Improve fragment distribution and reduce our average
+ * search time by starting our next search here. (see
+ * Knuth vol 1, sec 2.5, pg 449) */
+#ifdef CONFIG_SLOB_OPT
+ if (prev != slob_list->slob_list.prev &&
+ slob_list->slob_list.next != prev->next)
+ list_move_tail(&(slob_list->slob_list), prev->next);
+#else
+ if (prev != slob_list->prev &&
+ slob_list->next != prev->next)
+ list_move_tail(slob_list, prev->next);
+#endif
+ break;
+ }
+alloc_out:
+ spin_unlock_irqrestore(&slob_lock, flags);
+
+ /* Not enough space: must allocate a new page */
+ if (!b) {
+ b = slob_new_pages(gfp & ~__GFP_ZERO, 0, node);
+ if (!b)
+ return NULL;
+ raw_spin_lock_irqsave(&g_slob_kmalloc_spin_lock, flags);
+ g_slob_kmalloc_pages++;
+ raw_spin_unlock_irqrestore(&g_slob_kmalloc_spin_lock, flags);
+ sp = slob_page(b);
+ set_slob_page(sp);
+
+ spin_lock_irqsave(&slob_lock, flags);
+ sp->units = SLOB_UNITS(PAGE_SIZE);
+ sp->free = b;
+ INIT_LIST_HEAD(&sp->list);
+ set_slob(b, SLOB_UNITS(PAGE_SIZE), b + SLOB_UNITS(PAGE_SIZE));
+#ifdef CONFIG_SLOB_OPT
+ set_slob_page_free(sp, &(slob_list->slob_list));
+#else
+ set_slob_page_free(sp, slob_list);
+#endif
+ b = slob_page_alloc(sp, size, align);
+ BUG_ON(!b);
+ set_slob_page_list_index(sp, index);
+ spin_unlock_irqrestore(&slob_lock, flags);
+ }
+ if (unlikely((gfp & __GFP_ZERO) && b))
+ memset(b, 0, size);
+ return b;
+}
+
+static size_t slob_find_general_size(struct slob_page *sp)
+{
+ return (sp->index + 1) << SLOB_CHUNK_SHIFT;
+}
+
+/*
+ * slob_free: entry point into the slob allocator.
+ */
+static void slob_free_general(const void *block, struct slob_page *sp)
+{
+ size_t size;
+ slob_t *prev, *next, *b = (slob_t *)block;
+ slobidx_t units;
+ unsigned long flags;
+#ifdef CONFIG_SLOB_OPT
+ struct slob_item_info *slob_list;
+#else
+ struct list_head *slob_list;
+#endif
+ if (unlikely(ZERO_OR_NULL_PTR(block)))
+ return;
+
+ slob_list = get_slob_page_list_head(sp);
+ size = slob_find_general_size(sp);
+
+ units = SLOB_UNITS(size);
+
+ spin_lock_irqsave(&slob_lock, flags);
+
+ if (sp->units + units == SLOB_UNITS(PAGE_SIZE)) {
+#ifdef CONFIG_SLOB_OPT
+ if (slob_list->count < SLOB_LIST_LIMIT && slob_page_free(sp)){
+ sp->units += units;
+ sp->free = page_address((struct page *)sp);
+ set_slob(sp->free, SLOB_UNITS(PAGE_SIZE), sp->free + SLOB_UNITS(PAGE_SIZE));
+ slob_list->count++;
+ // if (slob_list->slob_list.next != &sp->list)
+ // list_move(&sp->list, &(slob_list->slob_list));
+
+ spin_unlock_irqrestore(&slob_lock, flags);
+ return;
+ }
+#endif
+ /* Go directly to page allocator. Do not pass slob allocator */
+ if (slob_page_free(sp))
+ clear_slob_page_free(sp);
+ spin_unlock_irqrestore(&slob_lock, flags);
+ clear_slob_page(sp);
+ clear_slob_page_list_index(sp);
+ free_slob_page(sp);
+ slob_free_pages(b, 0);
+ raw_spin_lock_irqsave(&g_slob_kmalloc_spin_lock, flags);
+ g_slob_kmalloc_pages--;
+ raw_spin_unlock_irqrestore(&g_slob_kmalloc_spin_lock, flags);
+ return;
+ }
+
+ if (!slob_page_free(sp)) {
+ /* This slob page is about to become partially free. Easy! */
+ sp->units = units;
+ sp->free = b;
+ set_slob(b, units,
+ (void *)((unsigned long)(b +
+ SLOB_UNITS(PAGE_SIZE)) & PAGE_MASK));
+#ifdef CONFIG_SLOB_OPT
+ set_slob_page_free(sp, &(slob_list->slob_list));
+#else
+ set_slob_page_free(sp, slob_list);
+#endif
+ goto out;
+ }
+
+ /*
+ * Otherwise the page is already partially free, so find reinsertion
+ * point.
+ */
+ sp->units += units;
+
+ if (b < sp->free) {
+ if (b + units == sp->free) {
+ units += slob_units(sp->free);
+ sp->free = slob_next(sp->free);
+ }
+ set_slob(b, units, sp->free);
+ sp->free = b;
+ } else {
+ prev = sp->free;
+ next = slob_next(prev);
+ while (b > next) {
+ prev = next;
+ next = slob_next(prev);
+ }
+
+ if (unlikely((b == prev) || (b == next)))
+ panic("slob: double free error!\n");
+
+ if (!slob_last(prev) && b + units == next) {
+ units += slob_units(next);
+ set_slob(b, units, slob_next(next));
+ } else
+ set_slob(b, units, next);
+
+ if (prev + slob_units(prev) == b) {
+ units = slob_units(b) + slob_units(prev);
+ set_slob(prev, units, slob_next(b));
+ } else
+ set_slob(prev, slob_units(prev), b);
+ }
+#ifdef CONFIG_SLOB_OPT
+ if (slob_list->slob_list.next != &sp->list)
+ list_move(&sp->list, &(slob_list->slob_list));
+#else
+ if (slob_list->next != &sp->list)
+ list_move(&sp->list, slob_list);
+#endif
+out:
+ spin_unlock_irqrestore(&slob_lock, flags);
+}
+
+static void *slob_alloc(size_t size, gfp_t gfp, int align, int node)
+{
+ struct slob_page *sp;
+ struct list_head *prev;
+ struct list_head *slob_list;
+ slob_t *b = NULL;
+ unsigned long flags;
+
+ if (size < SLOB_BREAK1)
+ slob_list = &free_slob_small;
+ else if (size < SLOB_BREAK2)
+ slob_list = &free_slob_medium;
+ else
+ slob_list = &free_slob_large;
+
+ spin_lock_irqsave(&slob_lock, flags);
+ /* Iterate through each partially free page, try to find room */
+ list_for_each_entry(sp, slob_list, list) {
+#ifdef CONFIG_NUMA
+ /*
+ * If there's a node specification, search for a partial
+ * page with a matching node id in the freelist.
+ */
+ if (node != -1 && page_to_nid(&sp->page) != node)
+ continue;
+#endif
+ /* Enough room on this page? */
+ if (sp->units < SLOB_UNITS(size))
+ continue;
+
+ /* Attempt to alloc */
+ prev = sp->list.prev;
+ b = slob_page_alloc(sp, size, align);
+ if (!b)
+ continue;
+
+ /* Improve fragment distribution and reduce our average
+ * search time by starting our next search here. (see
+ * Knuth vol 1, sec 2.5, pg 449) */
+ if (prev != slob_list->prev &&
+ slob_list->next != prev->next)
+ list_move_tail(slob_list, prev->next);
+ break;
+ }
+ spin_unlock_irqrestore(&slob_lock, flags);
+
+ /* Not enough space: must allocate a new page */
+ if (!b) {
+ b = slob_new_pages(gfp & ~__GFP_ZERO, 0, node);
+ if (!b)
+ return NULL;
+ raw_spin_lock_irqsave(&g_slob_kmem_cache_spin_lock, flags);
+ g_slob_kmem_cache_pages++;
+ raw_spin_unlock_irqrestore(&g_slob_kmem_cache_spin_lock, flags);
+ sp = slob_page(b);
+ set_slob_page(sp);
+
+ spin_lock_irqsave(&slob_lock, flags);
+ sp->units = SLOB_UNITS(PAGE_SIZE);
+ sp->free = b;
+ INIT_LIST_HEAD(&sp->list);
+ set_slob(b, SLOB_UNITS(PAGE_SIZE), b + SLOB_UNITS(PAGE_SIZE));
+ set_slob_page_free(sp, slob_list);
+ b = slob_page_alloc(sp, size, align);
+ BUG_ON(!b);
+ spin_unlock_irqrestore(&slob_lock, flags);
+ }
+ if (unlikely((gfp & __GFP_ZERO) && b))
+ memset(b, 0, size);
+ return b;
+}
+
+/*
+ * slob_free: entry point into the slob allocator.
+ */
+static void slob_free(void *block, int size)
+{
+ struct slob_page *sp;
+ slob_t *prev, *next, *b = (slob_t *)block;
+ slobidx_t units;
+ unsigned long flags;
+ struct list_head *slob_list;
+
+ if (unlikely(ZERO_OR_NULL_PTR(block)))
+ return;
+ BUG_ON(!size);
+
+ sp = slob_page(block);
+ units = SLOB_UNITS(size);
+
+ spin_lock_irqsave(&slob_lock, flags);
+
+ if (sp->units + units == SLOB_UNITS(PAGE_SIZE)) {
+ /* Go directly to page allocator. Do not pass slob allocator */
+ if (slob_page_free(sp))
+ clear_slob_page_free(sp);
+ spin_unlock_irqrestore(&slob_lock, flags);
+ clear_slob_page(sp);
+ free_slob_page(sp);
+ slob_free_pages(b, 0);
+ raw_spin_lock_irqsave(&g_slob_kmem_cache_spin_lock, flags);
+ g_slob_kmem_cache_pages--;
+ raw_spin_unlock_irqrestore(&g_slob_kmem_cache_spin_lock, flags);
+ return;
+ }
+
+ if (!slob_page_free(sp)) {
+ /* This slob page is about to become partially free. Easy! */
+ sp->units = units;
+ sp->free = b;
+ set_slob(b, units,
+ (void *)((unsigned long)(b +
+ SLOB_UNITS(PAGE_SIZE)) & PAGE_MASK));
+ if (size < SLOB_BREAK1)
+ slob_list = &free_slob_small;
+ else if (size < SLOB_BREAK2)
+ slob_list = &free_slob_medium;
+ else
+ slob_list = &free_slob_large;
+ set_slob_page_free(sp, slob_list);
+ goto out;
+ }
+
+ /*
+ * Otherwise the page is already partially free, so find reinsertion
+ * point.
+ */
+ sp->units += units;
+
+ if (b < sp->free) {
+ if (b + units == sp->free) {
+ units += slob_units(sp->free);
+ sp->free = slob_next(sp->free);
+ }
+ set_slob(b, units, sp->free);
+ sp->free = b;
+ } else {
+ prev = sp->free;
+ next = slob_next(prev);
+ while (b > next) {
+ prev = next;
+ next = slob_next(prev);
+ }
+
+ if (!slob_last(prev) && b + units == next) {
+ units += slob_units(next);
+ set_slob(b, units, slob_next(next));
+ } else
+ set_slob(b, units, next);
+
+ if (prev + slob_units(prev) == b) {
+ units = slob_units(b) + slob_units(prev);
+ set_slob(prev, units, slob_next(b));
+ } else
+ set_slob(prev, slob_units(prev), b);
+ }
+out:
+ spin_unlock_irqrestore(&slob_lock, flags);
+}
+
+/*
+ * End of slob allocator proper. Begin kmem_cache_alloc and kmalloc frontend.
+ */
+
+void *__kmalloc_node(size_t size, gfp_t gfp, int node)
+{
+ int align = max(ARCH_KMALLOC_MINALIGN, ARCH_SLAB_MINALIGN);
+ void *ret;
+ unsigned long flags;
+
+ if (!size)
+ return ZERO_SIZE_PTR;
+
+ gfp &= gfp_allowed_mask;
+
+ lockdep_trace_alloc(gfp);
+
+#ifdef CONFIG_DEBUG_SLOB_MARK_HEAD
+ if (size < PAGE_SIZE)
+ size += RECORD_COUNT * BYTES_PER_RECORD;
+#endif
+
+#ifdef CONFIG_KMALLOC_TRACKER
+ size += HEAP_SUFFIX_SIZE;
+#endif
+
+ if (size < PAGE_SIZE) {
+ if (!size)
+ return ZERO_SIZE_PTR;
+
+ ret = slob_alloc_general(size, gfp, align, node);
+
+ trace_kmalloc_node(_RET_IP_, ret,
+ size, size, gfp, node);
+
+#ifdef CONFIG_DEBUG_SLOB_MARK_HEAD
+ if (ret) {
+ *dbg_userrecord(ret,0) = (void *)RECORD_MAGIC;
+ *dbg_userrecord(ret,1) = __builtin_return_address(0);
+ *dbg_userrecord(ret,2) = current;
+ ret = (void *)dbg_userrecord(ret,RECORD_COUNT);
+ }
+
+#endif
+
+#ifdef CONFIG_KMALLOC_TRACKER
+ if (ret) {
+ kmalloc_alloc_tracker(ret, KMALLOC_ORIGINAL_SIZE(size));
+ return KMALLOC_SETUP(ret);
+ }
+#endif
+ } else {
+ unsigned int order = get_order(size);
+
+ if (likely(order))
+ gfp |= __GFP_COMP;
+ ret = slob_new_pages(gfp, order, node);
+ if (ret) {
+ struct page *page;
+ page = virt_to_page(ret);
+ page->private = size;
+ raw_spin_lock_irqsave(&g_slob_kmalloc_spin_lock, flags);
+ g_slob_kmalloc_pages += (1 << order);
+ raw_spin_unlock_irqrestore(&g_slob_kmalloc_spin_lock, flags);
+ }
+
+ trace_kmalloc_node(_RET_IP_, ret,
+ size, PAGE_SIZE << order, gfp, node);
+ }
+
+ kmemleak_alloc(ret, size, 1, gfp);
+ return ret;
+}
+EXPORT_SYMBOL(__kmalloc_node);
+
+void kfree(const void *block)
+{
+ struct slob_page *sp;
+ unsigned long flags;
+
+ trace_kfree(_RET_IP_, block);
+
+ if (unlikely(ZERO_OR_NULL_PTR(block)))
+ return;
+ kmemleak_free(block);
+
+#ifdef CONFIG_KMALLOC_TRACKER
+ int entry = 0;
+ void *mem = NULL;
+
+ sp = slob_page(block);
+ if (is_slob_page(sp)) {
+
+ mem = KMALLOC_BASE(block);
+ entry = *(size_t *)(mem);
+
+ if ((entry != 0) && (MEM_TRUE == check_node_entry(entry))) {
+ mem_free_tracker((void *)entry, MEM_TRACKER_TYPE_KMALLOC);
+ }else
+ panic("mem out!!");
+ slob_free_general(mem, sp);
+ } else
+ put_page(&sp->page);
+#else
+ sp = slob_page(block);
+ if (is_slob_page(sp)) {
+#ifdef CONFIG_DEBUG_SLOB_MARK_HEAD
+ if (*dbg_userhead(block) == (void *)RECORD_MAGIC) {
+ block = (void *)dbg_userhead(block);
+ } else
+ panic("memmory corruption!!");
+#endif
+ slob_free_general(block, sp);
+ } else {
+ struct page *page;
+ unsigned int order;
+ page = &sp->page;
+ order = get_order(page->private);
+ raw_spin_lock_irqsave(&g_slob_kmalloc_spin_lock, flags);
+ g_slob_kmalloc_pages -= (1 << order);
+ raw_spin_unlock_irqrestore(&g_slob_kmalloc_spin_lock, flags);
+ put_page(&sp->page);
+ }
+#endif
+
+}
+EXPORT_SYMBOL(kfree);
+
+/* can't use ksize for kmem_cache_alloc memory, only kmalloc */
+size_t ksize(const void *block)
+{
+ struct slob_page *sp;
+
+ BUG_ON(!block);
+ if (unlikely(block == ZERO_SIZE_PTR))
+ return 0;
+
+ sp = slob_page(block);
+ if (is_slob_page(sp)) {
+#ifdef CONFIG_SLOB_OPT
+ struct slob_item_info *slob_list;
+#else
+ struct list_head *slob_list;
+#endif
+ slob_list = get_slob_page_list_head(sp);
+
+#ifdef CONFIG_KMALLOC_TRACKER
+ return (slob_find_general_size(sp) - 2 * sizeof(size_t));
+#endif
+
+#ifdef CONFIG_DEBUG_SLOB_MARK_HEAD
+ return (slob_find_general_size(sp) - RECORD_COUNT * BYTES_PER_RECORD);
+#else
+ return slob_find_general_size(sp);
+#endif
+
+ } else
+ return sp->page.private;
+}
+EXPORT_SYMBOL(ksize);
+
+struct kmem_cache {
+ unsigned int size, align;
+ unsigned long flags;
+ const char *name;
+ void (*ctor)(void *);
+};
+
+struct kmem_cache *kmem_cache_create(const char *name, size_t size,
+ size_t align, unsigned long flags, void (*ctor)(void *))
+{
+ struct kmem_cache *c;
+
+ c = slob_alloc(sizeof(struct kmem_cache),
+ GFP_KERNEL, ARCH_KMALLOC_MINALIGN, -1);
+
+ if (c) {
+ c->name = name;
+ c->size = size;
+ if (flags & SLAB_DESTROY_BY_RCU) {
+ /* leave room for rcu footer at the end of object */
+ c->size += sizeof(struct slob_rcu);
+ }
+ c->flags = flags;
+ c->ctor = ctor;
+ /* ignore alignment unless it's forced */
+ c->align = (flags & SLAB_HWCACHE_ALIGN) ? SLOB_ALIGN : 0;
+ if (c->align < ARCH_SLAB_MINALIGN)
+ c->align = ARCH_SLAB_MINALIGN;
+ if (c->align < align)
+ c->align = align;
+ } else if (flags & SLAB_PANIC)
+ panic("Cannot create slab cache %s\n", name);
+
+ kmemleak_alloc(c, sizeof(struct kmem_cache), 1, GFP_KERNEL);
+ return c;
+}
+EXPORT_SYMBOL(kmem_cache_create);
+
+void kmem_cache_destroy(struct kmem_cache *c)
+{
+ kmemleak_free(c);
+ if (c->flags & SLAB_DESTROY_BY_RCU)
+ rcu_barrier();
+ slob_free(c, sizeof(struct kmem_cache));
+}
+EXPORT_SYMBOL(kmem_cache_destroy);
+
+static unsigned int align(unsigned int i)
+{
+ return (i + 7) & ~7u;
+}
+void *kmem_cache_alloc_node(struct kmem_cache *c, gfp_t flags, int node)
+{
+ void *b;
+
+ flags &= gfp_allowed_mask;
+ unsigned long spin_flags;
+
+ lockdep_trace_alloc(flags);
+
+ if (c->size < PAGE_SIZE) {
+ //b = slob_alloc(c->size, flags, c->align, node);
+ c->size = align(c->size);/*align 8*/
+ c->align = 0;
+ b = slob_alloc(c->size, flags, 0, node);/*already align 8*/
+ trace_kmem_cache_alloc_node(_RET_IP_, b, c->size,
+ SLOB_UNITS(c->size) * SLOB_UNIT,
+ flags, node);
+ } else {
+ b = slob_new_pages(flags, get_order(c->size), node);
+ if(b) {
+ raw_spin_lock_irqsave(&g_slob_kmem_cache_spin_lock, spin_flags);
+ g_slob_kmem_cache_pages += (1 << get_order(c->size));
+ raw_spin_unlock_irqrestore(&g_slob_kmem_cache_spin_lock, spin_flags);
+ }
+ trace_kmem_cache_alloc_node(_RET_IP_, b, c->size,
+ PAGE_SIZE << get_order(c->size),
+ flags, node);
+ }
+ if (c->ctor)
+ c->ctor(b);
+
+ kmemleak_alloc_recursive(b, c->size, 1, c->flags, flags);
+ return b;
+}
+
+EXPORT_SYMBOL(kmem_cache_alloc_node);
+static void __kmem_cache_free(void *b, int size)
+{
+ unsigned long flags;
+ if (size < PAGE_SIZE)
+ slob_free(b, size);
+ else {
+ slob_free_pages(b, get_order(size));
+ raw_spin_lock_irqsave(&g_slob_kmem_cache_spin_lock, flags);
+ g_slob_kmem_cache_pages -= (1 << get_order(size));
+ raw_spin_unlock_irqrestore(&g_slob_kmem_cache_spin_lock, flags);
+ }
+}
+
+static void kmem_rcu_free(struct rcu_head *head)
+{
+ struct slob_rcu *slob_rcu = (struct slob_rcu *)head;
+ void *b = (void *)slob_rcu - (slob_rcu->size - sizeof(struct slob_rcu));
+
+ __kmem_cache_free(b, slob_rcu->size);
+}
+
+void kmem_cache_free(struct kmem_cache *c, void *b)
+{
+ kmemleak_free_recursive(b, c->flags);
+
+ if (unlikely(c->flags & SLAB_DESTROY_BY_RCU)) {
+ struct slob_rcu *slob_rcu;
+ slob_rcu = b + (c->size - sizeof(struct slob_rcu));
+ slob_rcu->size = c->size;
+ call_rcu(&slob_rcu->head, kmem_rcu_free);
+ } else {
+ __kmem_cache_free(b, c->size);
+ }
+
+ trace_kmem_cache_free(_RET_IP_, b);
+}
+EXPORT_SYMBOL(kmem_cache_free);
+
+unsigned int kmem_cache_size(struct kmem_cache *c)
+{
+ return c->size;
+}
+EXPORT_SYMBOL(kmem_cache_size);
+
+int kmem_cache_shrink(struct kmem_cache *d)
+{
+#ifdef CONFIG_SLOB_OPT
+ int i;
+ unsigned long flags;
+ struct slob_page *sp,*n;
+
+ for (i = 0; i < SLOB_LIST_NUM; i++){
+ if (slob_sizes[i].count == 0)
+ continue;
+ spin_lock_irqsave(&slob_lock, flags);
+
+ list_for_each_entry_safe(sp, n, &(slob_sizes[i].slob_list), list) {
+ if (sp->units == SLOB_UNITS(PAGE_SIZE)){
+ if (slob_page_free(sp))
+ clear_slob_page_free(sp);
+ clear_slob_page(sp);
+ clear_slob_page_list_index(sp);
+ free_slob_page(sp);
+ slob_free_pages(page_address((struct page*)sp), 0);
+ }
+ }
+ slob_sizes[i].count = 0;
+ spin_unlock_irqrestore(&slob_lock, flags);
+
+ }
+#endif
+ return 0;
+}
+EXPORT_SYMBOL(kmem_cache_shrink);
+
+static unsigned int slob_ready __read_mostly;
+
+int slab_is_available(void)
+{
+ return slob_ready;
+}
+
+void __init kmem_cache_init(void)
+{
+ slob_ready = 1;
+}
+
+void __init kmem_cache_init_late(void)
+{
+ /* Nothing to do */
+}