18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * SLOB Allocator: Simple List Of Blocks 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Matt Mackall <mpm@selenic.com> 12/30/03 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * NUMA support by Paul Mundt, 2007. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * How SLOB works: 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * The core of SLOB is a traditional K&R style heap allocator, with 128c2ecf20Sopenharmony_ci * support for returning aligned objects. The granularity of this 138c2ecf20Sopenharmony_ci * allocator is as little as 2 bytes, however typically most architectures 148c2ecf20Sopenharmony_ci * will require 4 bytes on 32-bit and 8 bytes on 64-bit. 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * The slob heap is a set of linked list of pages from alloc_pages(), 178c2ecf20Sopenharmony_ci * and within each page, there is a singly-linked list of free blocks 188c2ecf20Sopenharmony_ci * (slob_t). The heap is grown on demand. To reduce fragmentation, 198c2ecf20Sopenharmony_ci * heap pages are segregated into three lists, with objects less than 208c2ecf20Sopenharmony_ci * 256 bytes, objects less than 1024 bytes, and all other objects. 218c2ecf20Sopenharmony_ci * 228c2ecf20Sopenharmony_ci * Allocation from heap involves first searching for a page with 238c2ecf20Sopenharmony_ci * sufficient free blocks (using a next-fit-like approach) followed by 248c2ecf20Sopenharmony_ci * a first-fit scan of the page. Deallocation inserts objects back 258c2ecf20Sopenharmony_ci * into the free list in address order, so this is effectively an 268c2ecf20Sopenharmony_ci * address-ordered first fit. 278c2ecf20Sopenharmony_ci * 288c2ecf20Sopenharmony_ci * Above this is an implementation of kmalloc/kfree. Blocks returned 298c2ecf20Sopenharmony_ci * from kmalloc are prepended with a 4-byte header with the kmalloc size. 308c2ecf20Sopenharmony_ci * If kmalloc is asked for objects of PAGE_SIZE or larger, it calls 318c2ecf20Sopenharmony_ci * alloc_pages() directly, allocating compound pages so the page order 328c2ecf20Sopenharmony_ci * does not have to be separately tracked. 338c2ecf20Sopenharmony_ci * These objects are detected in kfree() because PageSlab() 348c2ecf20Sopenharmony_ci * is false for them. 358c2ecf20Sopenharmony_ci * 368c2ecf20Sopenharmony_ci * SLAB is emulated on top of SLOB by simply calling constructors and 378c2ecf20Sopenharmony_ci * destructors for every SLAB allocation. Objects are returned with the 388c2ecf20Sopenharmony_ci * 4-byte alignment unless the SLAB_HWCACHE_ALIGN flag is set, in which 398c2ecf20Sopenharmony_ci * case the low-level allocator will fragment blocks to create the proper 408c2ecf20Sopenharmony_ci * alignment. Again, objects of page-size or greater are allocated by 418c2ecf20Sopenharmony_ci * calling alloc_pages(). As SLAB objects know their size, no separate 428c2ecf20Sopenharmony_ci * size bookkeeping is necessary and there is essentially no allocation 438c2ecf20Sopenharmony_ci * space overhead, and compound pages aren't needed for multi-page 448c2ecf20Sopenharmony_ci * allocations. 458c2ecf20Sopenharmony_ci * 468c2ecf20Sopenharmony_ci * NUMA support in SLOB is fairly simplistic, pushing most of the real 478c2ecf20Sopenharmony_ci * logic down to the page allocator, and simply doing the node accounting 488c2ecf20Sopenharmony_ci * on the upper levels. In the event that a node id is explicitly 498c2ecf20Sopenharmony_ci * provided, __alloc_pages_node() with the specified node id is used 508c2ecf20Sopenharmony_ci * instead. The common case (or when the node id isn't explicitly provided) 518c2ecf20Sopenharmony_ci * will default to the current node, as per numa_node_id(). 528c2ecf20Sopenharmony_ci * 538c2ecf20Sopenharmony_ci * Node aware pages are still inserted in to the global freelist, and 548c2ecf20Sopenharmony_ci * these are scanned for by matching against the node id encoded in the 558c2ecf20Sopenharmony_ci * page flags. As a result, block allocations that can be satisfied from 568c2ecf20Sopenharmony_ci * the freelist will only be done so on pages residing on the same node, 578c2ecf20Sopenharmony_ci * in order to prevent random node placement. 588c2ecf20Sopenharmony_ci */ 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci#include <linux/kernel.h> 618c2ecf20Sopenharmony_ci#include <linux/slab.h> 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci#include <linux/mm.h> 648c2ecf20Sopenharmony_ci#include <linux/swap.h> /* struct reclaim_state */ 658c2ecf20Sopenharmony_ci#include <linux/cache.h> 668c2ecf20Sopenharmony_ci#include <linux/init.h> 678c2ecf20Sopenharmony_ci#include <linux/export.h> 688c2ecf20Sopenharmony_ci#include <linux/rcupdate.h> 698c2ecf20Sopenharmony_ci#include <linux/list.h> 708c2ecf20Sopenharmony_ci#include <linux/kmemleak.h> 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci#include <trace/events/kmem.h> 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci#include <linux/atomic.h> 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci#include "slab.h" 778c2ecf20Sopenharmony_ci/* 788c2ecf20Sopenharmony_ci * slob_block has a field 'units', which indicates size of block if +ve, 798c2ecf20Sopenharmony_ci * or offset of next block if -ve (in SLOB_UNITs). 808c2ecf20Sopenharmony_ci * 818c2ecf20Sopenharmony_ci * Free blocks of size 1 unit simply contain the offset of the next block. 828c2ecf20Sopenharmony_ci * Those with larger size contain their size in the first SLOB_UNIT of 838c2ecf20Sopenharmony_ci * memory, and the offset of the next free block in the second SLOB_UNIT. 848c2ecf20Sopenharmony_ci */ 858c2ecf20Sopenharmony_ci#if PAGE_SIZE <= (32767 * 2) 868c2ecf20Sopenharmony_citypedef s16 slobidx_t; 878c2ecf20Sopenharmony_ci#else 888c2ecf20Sopenharmony_citypedef s32 slobidx_t; 898c2ecf20Sopenharmony_ci#endif 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistruct slob_block { 928c2ecf20Sopenharmony_ci slobidx_t units; 938c2ecf20Sopenharmony_ci}; 948c2ecf20Sopenharmony_citypedef struct slob_block slob_t; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci/* 978c2ecf20Sopenharmony_ci * All partially free slob pages go on these lists. 988c2ecf20Sopenharmony_ci */ 998c2ecf20Sopenharmony_ci#define SLOB_BREAK1 256 1008c2ecf20Sopenharmony_ci#define SLOB_BREAK2 1024 1018c2ecf20Sopenharmony_cistatic LIST_HEAD(free_slob_small); 1028c2ecf20Sopenharmony_cistatic LIST_HEAD(free_slob_medium); 1038c2ecf20Sopenharmony_cistatic LIST_HEAD(free_slob_large); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci/* 1068c2ecf20Sopenharmony_ci * slob_page_free: true for pages on free_slob_pages list. 1078c2ecf20Sopenharmony_ci */ 1088c2ecf20Sopenharmony_cistatic inline int slob_page_free(struct page *sp) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci return PageSlobFree(sp); 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic void set_slob_page_free(struct page *sp, struct list_head *list) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci list_add(&sp->slab_list, list); 1168c2ecf20Sopenharmony_ci __SetPageSlobFree(sp); 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic inline void clear_slob_page_free(struct page *sp) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci list_del(&sp->slab_list); 1228c2ecf20Sopenharmony_ci __ClearPageSlobFree(sp); 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci#define SLOB_UNIT sizeof(slob_t) 1268c2ecf20Sopenharmony_ci#define SLOB_UNITS(size) DIV_ROUND_UP(size, SLOB_UNIT) 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci/* 1298c2ecf20Sopenharmony_ci * struct slob_rcu is inserted at the tail of allocated slob blocks, which 1308c2ecf20Sopenharmony_ci * were created with a SLAB_TYPESAFE_BY_RCU slab. slob_rcu is used to free 1318c2ecf20Sopenharmony_ci * the block using call_rcu. 1328c2ecf20Sopenharmony_ci */ 1338c2ecf20Sopenharmony_cistruct slob_rcu { 1348c2ecf20Sopenharmony_ci struct rcu_head head; 1358c2ecf20Sopenharmony_ci int size; 1368c2ecf20Sopenharmony_ci}; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci/* 1398c2ecf20Sopenharmony_ci * slob_lock protects all slob allocator structures. 1408c2ecf20Sopenharmony_ci */ 1418c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(slob_lock); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci/* 1448c2ecf20Sopenharmony_ci * Encode the given size and next info into a free slob block s. 1458c2ecf20Sopenharmony_ci */ 1468c2ecf20Sopenharmony_cistatic void set_slob(slob_t *s, slobidx_t size, slob_t *next) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci slob_t *base = (slob_t *)((unsigned long)s & PAGE_MASK); 1498c2ecf20Sopenharmony_ci slobidx_t offset = next - base; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci if (size > 1) { 1528c2ecf20Sopenharmony_ci s[0].units = size; 1538c2ecf20Sopenharmony_ci s[1].units = offset; 1548c2ecf20Sopenharmony_ci } else 1558c2ecf20Sopenharmony_ci s[0].units = -offset; 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci/* 1598c2ecf20Sopenharmony_ci * Return the size of a slob block. 1608c2ecf20Sopenharmony_ci */ 1618c2ecf20Sopenharmony_cistatic slobidx_t slob_units(slob_t *s) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci if (s->units > 0) 1648c2ecf20Sopenharmony_ci return s->units; 1658c2ecf20Sopenharmony_ci return 1; 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci/* 1698c2ecf20Sopenharmony_ci * Return the next free slob block pointer after this one. 1708c2ecf20Sopenharmony_ci */ 1718c2ecf20Sopenharmony_cistatic slob_t *slob_next(slob_t *s) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci slob_t *base = (slob_t *)((unsigned long)s & PAGE_MASK); 1748c2ecf20Sopenharmony_ci slobidx_t next; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci if (s[0].units < 0) 1778c2ecf20Sopenharmony_ci next = -s[0].units; 1788c2ecf20Sopenharmony_ci else 1798c2ecf20Sopenharmony_ci next = s[1].units; 1808c2ecf20Sopenharmony_ci return base+next; 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci/* 1848c2ecf20Sopenharmony_ci * Returns true if s is the last free block in its page. 1858c2ecf20Sopenharmony_ci */ 1868c2ecf20Sopenharmony_cistatic int slob_last(slob_t *s) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci return !((unsigned long)slob_next(s) & ~PAGE_MASK); 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic void *slob_new_pages(gfp_t gfp, int order, int node) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci struct page *page; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci#ifdef CONFIG_NUMA 1968c2ecf20Sopenharmony_ci if (node != NUMA_NO_NODE) 1978c2ecf20Sopenharmony_ci page = __alloc_pages_node(node, gfp, order); 1988c2ecf20Sopenharmony_ci else 1998c2ecf20Sopenharmony_ci#endif 2008c2ecf20Sopenharmony_ci page = alloc_pages(gfp, order); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci if (!page) 2038c2ecf20Sopenharmony_ci return NULL; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci mod_node_page_state(page_pgdat(page), NR_SLAB_UNRECLAIMABLE_B, 2068c2ecf20Sopenharmony_ci PAGE_SIZE << order); 2078c2ecf20Sopenharmony_ci return page_address(page); 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistatic void slob_free_pages(void *b, int order) 2118c2ecf20Sopenharmony_ci{ 2128c2ecf20Sopenharmony_ci struct page *sp = virt_to_page(b); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci if (current->reclaim_state) 2158c2ecf20Sopenharmony_ci current->reclaim_state->reclaimed_slab += 1 << order; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci mod_node_page_state(page_pgdat(sp), NR_SLAB_UNRECLAIMABLE_B, 2188c2ecf20Sopenharmony_ci -(PAGE_SIZE << order)); 2198c2ecf20Sopenharmony_ci __free_pages(sp, order); 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci/* 2238c2ecf20Sopenharmony_ci * slob_page_alloc() - Allocate a slob block within a given slob_page sp. 2248c2ecf20Sopenharmony_ci * @sp: Page to look in. 2258c2ecf20Sopenharmony_ci * @size: Size of the allocation. 2268c2ecf20Sopenharmony_ci * @align: Allocation alignment. 2278c2ecf20Sopenharmony_ci * @align_offset: Offset in the allocated block that will be aligned. 2288c2ecf20Sopenharmony_ci * @page_removed_from_list: Return parameter. 2298c2ecf20Sopenharmony_ci * 2308c2ecf20Sopenharmony_ci * Tries to find a chunk of memory at least @size bytes big within @page. 2318c2ecf20Sopenharmony_ci * 2328c2ecf20Sopenharmony_ci * Return: Pointer to memory if allocated, %NULL otherwise. If the 2338c2ecf20Sopenharmony_ci * allocation fills up @page then the page is removed from the 2348c2ecf20Sopenharmony_ci * freelist, in this case @page_removed_from_list will be set to 2358c2ecf20Sopenharmony_ci * true (set to false otherwise). 2368c2ecf20Sopenharmony_ci */ 2378c2ecf20Sopenharmony_cistatic void *slob_page_alloc(struct page *sp, size_t size, int align, 2388c2ecf20Sopenharmony_ci int align_offset, bool *page_removed_from_list) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci slob_t *prev, *cur, *aligned = NULL; 2418c2ecf20Sopenharmony_ci int delta = 0, units = SLOB_UNITS(size); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci *page_removed_from_list = false; 2448c2ecf20Sopenharmony_ci for (prev = NULL, cur = sp->freelist; ; prev = cur, cur = slob_next(cur)) { 2458c2ecf20Sopenharmony_ci slobidx_t avail = slob_units(cur); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci /* 2488c2ecf20Sopenharmony_ci * 'aligned' will hold the address of the slob block so that the 2498c2ecf20Sopenharmony_ci * address 'aligned'+'align_offset' is aligned according to the 2508c2ecf20Sopenharmony_ci * 'align' parameter. This is for kmalloc() which prepends the 2518c2ecf20Sopenharmony_ci * allocated block with its size, so that the block itself is 2528c2ecf20Sopenharmony_ci * aligned when needed. 2538c2ecf20Sopenharmony_ci */ 2548c2ecf20Sopenharmony_ci if (align) { 2558c2ecf20Sopenharmony_ci aligned = (slob_t *) 2568c2ecf20Sopenharmony_ci (ALIGN((unsigned long)cur + align_offset, align) 2578c2ecf20Sopenharmony_ci - align_offset); 2588c2ecf20Sopenharmony_ci delta = aligned - cur; 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci if (avail >= units + delta) { /* room enough? */ 2618c2ecf20Sopenharmony_ci slob_t *next; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci if (delta) { /* need to fragment head to align? */ 2648c2ecf20Sopenharmony_ci next = slob_next(cur); 2658c2ecf20Sopenharmony_ci set_slob(aligned, avail - delta, next); 2668c2ecf20Sopenharmony_ci set_slob(cur, delta, aligned); 2678c2ecf20Sopenharmony_ci prev = cur; 2688c2ecf20Sopenharmony_ci cur = aligned; 2698c2ecf20Sopenharmony_ci avail = slob_units(cur); 2708c2ecf20Sopenharmony_ci } 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci next = slob_next(cur); 2738c2ecf20Sopenharmony_ci if (avail == units) { /* exact fit? unlink. */ 2748c2ecf20Sopenharmony_ci if (prev) 2758c2ecf20Sopenharmony_ci set_slob(prev, slob_units(prev), next); 2768c2ecf20Sopenharmony_ci else 2778c2ecf20Sopenharmony_ci sp->freelist = next; 2788c2ecf20Sopenharmony_ci } else { /* fragment */ 2798c2ecf20Sopenharmony_ci if (prev) 2808c2ecf20Sopenharmony_ci set_slob(prev, slob_units(prev), cur + units); 2818c2ecf20Sopenharmony_ci else 2828c2ecf20Sopenharmony_ci sp->freelist = cur + units; 2838c2ecf20Sopenharmony_ci set_slob(cur + units, avail - units, next); 2848c2ecf20Sopenharmony_ci } 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci sp->units -= units; 2878c2ecf20Sopenharmony_ci if (!sp->units) { 2888c2ecf20Sopenharmony_ci clear_slob_page_free(sp); 2898c2ecf20Sopenharmony_ci *page_removed_from_list = true; 2908c2ecf20Sopenharmony_ci } 2918c2ecf20Sopenharmony_ci return cur; 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci if (slob_last(cur)) 2948c2ecf20Sopenharmony_ci return NULL; 2958c2ecf20Sopenharmony_ci } 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci/* 2998c2ecf20Sopenharmony_ci * slob_alloc: entry point into the slob allocator. 3008c2ecf20Sopenharmony_ci */ 3018c2ecf20Sopenharmony_cistatic void *slob_alloc(size_t size, gfp_t gfp, int align, int node, 3028c2ecf20Sopenharmony_ci int align_offset) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci struct page *sp; 3058c2ecf20Sopenharmony_ci struct list_head *slob_list; 3068c2ecf20Sopenharmony_ci slob_t *b = NULL; 3078c2ecf20Sopenharmony_ci unsigned long flags; 3088c2ecf20Sopenharmony_ci bool _unused; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci if (size < SLOB_BREAK1) 3118c2ecf20Sopenharmony_ci slob_list = &free_slob_small; 3128c2ecf20Sopenharmony_ci else if (size < SLOB_BREAK2) 3138c2ecf20Sopenharmony_ci slob_list = &free_slob_medium; 3148c2ecf20Sopenharmony_ci else 3158c2ecf20Sopenharmony_ci slob_list = &free_slob_large; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci spin_lock_irqsave(&slob_lock, flags); 3188c2ecf20Sopenharmony_ci /* Iterate through each partially free page, try to find room */ 3198c2ecf20Sopenharmony_ci list_for_each_entry(sp, slob_list, slab_list) { 3208c2ecf20Sopenharmony_ci bool page_removed_from_list = false; 3218c2ecf20Sopenharmony_ci#ifdef CONFIG_NUMA 3228c2ecf20Sopenharmony_ci /* 3238c2ecf20Sopenharmony_ci * If there's a node specification, search for a partial 3248c2ecf20Sopenharmony_ci * page with a matching node id in the freelist. 3258c2ecf20Sopenharmony_ci */ 3268c2ecf20Sopenharmony_ci if (node != NUMA_NO_NODE && page_to_nid(sp) != node) 3278c2ecf20Sopenharmony_ci continue; 3288c2ecf20Sopenharmony_ci#endif 3298c2ecf20Sopenharmony_ci /* Enough room on this page? */ 3308c2ecf20Sopenharmony_ci if (sp->units < SLOB_UNITS(size)) 3318c2ecf20Sopenharmony_ci continue; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci b = slob_page_alloc(sp, size, align, align_offset, &page_removed_from_list); 3348c2ecf20Sopenharmony_ci if (!b) 3358c2ecf20Sopenharmony_ci continue; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci /* 3388c2ecf20Sopenharmony_ci * If slob_page_alloc() removed sp from the list then we 3398c2ecf20Sopenharmony_ci * cannot call list functions on sp. If so allocation 3408c2ecf20Sopenharmony_ci * did not fragment the page anyway so optimisation is 3418c2ecf20Sopenharmony_ci * unnecessary. 3428c2ecf20Sopenharmony_ci */ 3438c2ecf20Sopenharmony_ci if (!page_removed_from_list) { 3448c2ecf20Sopenharmony_ci /* 3458c2ecf20Sopenharmony_ci * Improve fragment distribution and reduce our average 3468c2ecf20Sopenharmony_ci * search time by starting our next search here. (see 3478c2ecf20Sopenharmony_ci * Knuth vol 1, sec 2.5, pg 449) 3488c2ecf20Sopenharmony_ci */ 3498c2ecf20Sopenharmony_ci if (!list_is_first(&sp->slab_list, slob_list)) 3508c2ecf20Sopenharmony_ci list_rotate_to_front(&sp->slab_list, slob_list); 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci break; 3538c2ecf20Sopenharmony_ci } 3548c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&slob_lock, flags); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci /* Not enough space: must allocate a new page */ 3578c2ecf20Sopenharmony_ci if (!b) { 3588c2ecf20Sopenharmony_ci b = slob_new_pages(gfp & ~__GFP_ZERO, 0, node); 3598c2ecf20Sopenharmony_ci if (!b) 3608c2ecf20Sopenharmony_ci return NULL; 3618c2ecf20Sopenharmony_ci sp = virt_to_page(b); 3628c2ecf20Sopenharmony_ci __SetPageSlab(sp); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci spin_lock_irqsave(&slob_lock, flags); 3658c2ecf20Sopenharmony_ci sp->units = SLOB_UNITS(PAGE_SIZE); 3668c2ecf20Sopenharmony_ci sp->freelist = b; 3678c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&sp->slab_list); 3688c2ecf20Sopenharmony_ci set_slob(b, SLOB_UNITS(PAGE_SIZE), b + SLOB_UNITS(PAGE_SIZE)); 3698c2ecf20Sopenharmony_ci set_slob_page_free(sp, slob_list); 3708c2ecf20Sopenharmony_ci b = slob_page_alloc(sp, size, align, align_offset, &_unused); 3718c2ecf20Sopenharmony_ci BUG_ON(!b); 3728c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&slob_lock, flags); 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci if (unlikely(gfp & __GFP_ZERO)) 3758c2ecf20Sopenharmony_ci memset(b, 0, size); 3768c2ecf20Sopenharmony_ci return b; 3778c2ecf20Sopenharmony_ci} 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci/* 3808c2ecf20Sopenharmony_ci * slob_free: entry point into the slob allocator. 3818c2ecf20Sopenharmony_ci */ 3828c2ecf20Sopenharmony_cistatic void slob_free(void *block, int size) 3838c2ecf20Sopenharmony_ci{ 3848c2ecf20Sopenharmony_ci struct page *sp; 3858c2ecf20Sopenharmony_ci slob_t *prev, *next, *b = (slob_t *)block; 3868c2ecf20Sopenharmony_ci slobidx_t units; 3878c2ecf20Sopenharmony_ci unsigned long flags; 3888c2ecf20Sopenharmony_ci struct list_head *slob_list; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci if (unlikely(ZERO_OR_NULL_PTR(block))) 3918c2ecf20Sopenharmony_ci return; 3928c2ecf20Sopenharmony_ci BUG_ON(!size); 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci sp = virt_to_page(block); 3958c2ecf20Sopenharmony_ci units = SLOB_UNITS(size); 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci spin_lock_irqsave(&slob_lock, flags); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci if (sp->units + units == SLOB_UNITS(PAGE_SIZE)) { 4008c2ecf20Sopenharmony_ci /* Go directly to page allocator. Do not pass slob allocator */ 4018c2ecf20Sopenharmony_ci if (slob_page_free(sp)) 4028c2ecf20Sopenharmony_ci clear_slob_page_free(sp); 4038c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&slob_lock, flags); 4048c2ecf20Sopenharmony_ci __ClearPageSlab(sp); 4058c2ecf20Sopenharmony_ci page_mapcount_reset(sp); 4068c2ecf20Sopenharmony_ci slob_free_pages(b, 0); 4078c2ecf20Sopenharmony_ci return; 4088c2ecf20Sopenharmony_ci } 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci if (!slob_page_free(sp)) { 4118c2ecf20Sopenharmony_ci /* This slob page is about to become partially free. Easy! */ 4128c2ecf20Sopenharmony_ci sp->units = units; 4138c2ecf20Sopenharmony_ci sp->freelist = b; 4148c2ecf20Sopenharmony_ci set_slob(b, units, 4158c2ecf20Sopenharmony_ci (void *)((unsigned long)(b + 4168c2ecf20Sopenharmony_ci SLOB_UNITS(PAGE_SIZE)) & PAGE_MASK)); 4178c2ecf20Sopenharmony_ci if (size < SLOB_BREAK1) 4188c2ecf20Sopenharmony_ci slob_list = &free_slob_small; 4198c2ecf20Sopenharmony_ci else if (size < SLOB_BREAK2) 4208c2ecf20Sopenharmony_ci slob_list = &free_slob_medium; 4218c2ecf20Sopenharmony_ci else 4228c2ecf20Sopenharmony_ci slob_list = &free_slob_large; 4238c2ecf20Sopenharmony_ci set_slob_page_free(sp, slob_list); 4248c2ecf20Sopenharmony_ci goto out; 4258c2ecf20Sopenharmony_ci } 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci /* 4288c2ecf20Sopenharmony_ci * Otherwise the page is already partially free, so find reinsertion 4298c2ecf20Sopenharmony_ci * point. 4308c2ecf20Sopenharmony_ci */ 4318c2ecf20Sopenharmony_ci sp->units += units; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci if (b < (slob_t *)sp->freelist) { 4348c2ecf20Sopenharmony_ci if (b + units == sp->freelist) { 4358c2ecf20Sopenharmony_ci units += slob_units(sp->freelist); 4368c2ecf20Sopenharmony_ci sp->freelist = slob_next(sp->freelist); 4378c2ecf20Sopenharmony_ci } 4388c2ecf20Sopenharmony_ci set_slob(b, units, sp->freelist); 4398c2ecf20Sopenharmony_ci sp->freelist = b; 4408c2ecf20Sopenharmony_ci } else { 4418c2ecf20Sopenharmony_ci prev = sp->freelist; 4428c2ecf20Sopenharmony_ci next = slob_next(prev); 4438c2ecf20Sopenharmony_ci while (b > next) { 4448c2ecf20Sopenharmony_ci prev = next; 4458c2ecf20Sopenharmony_ci next = slob_next(prev); 4468c2ecf20Sopenharmony_ci } 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci if (!slob_last(prev) && b + units == next) { 4498c2ecf20Sopenharmony_ci units += slob_units(next); 4508c2ecf20Sopenharmony_ci set_slob(b, units, slob_next(next)); 4518c2ecf20Sopenharmony_ci } else 4528c2ecf20Sopenharmony_ci set_slob(b, units, next); 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci if (prev + slob_units(prev) == b) { 4558c2ecf20Sopenharmony_ci units = slob_units(b) + slob_units(prev); 4568c2ecf20Sopenharmony_ci set_slob(prev, units, slob_next(b)); 4578c2ecf20Sopenharmony_ci } else 4588c2ecf20Sopenharmony_ci set_slob(prev, slob_units(prev), b); 4598c2ecf20Sopenharmony_ci } 4608c2ecf20Sopenharmony_ciout: 4618c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&slob_lock, flags); 4628c2ecf20Sopenharmony_ci} 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci/* 4658c2ecf20Sopenharmony_ci * End of slob allocator proper. Begin kmem_cache_alloc and kmalloc frontend. 4668c2ecf20Sopenharmony_ci */ 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_cistatic __always_inline void * 4698c2ecf20Sopenharmony_ci__do_kmalloc_node(size_t size, gfp_t gfp, int node, unsigned long caller) 4708c2ecf20Sopenharmony_ci{ 4718c2ecf20Sopenharmony_ci unsigned int *m; 4728c2ecf20Sopenharmony_ci int minalign = max_t(size_t, ARCH_KMALLOC_MINALIGN, ARCH_SLAB_MINALIGN); 4738c2ecf20Sopenharmony_ci void *ret; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci gfp &= gfp_allowed_mask; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci fs_reclaim_acquire(gfp); 4788c2ecf20Sopenharmony_ci fs_reclaim_release(gfp); 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci if (size < PAGE_SIZE - minalign) { 4818c2ecf20Sopenharmony_ci int align = minalign; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci /* 4848c2ecf20Sopenharmony_ci * For power of two sizes, guarantee natural alignment for 4858c2ecf20Sopenharmony_ci * kmalloc()'d objects. 4868c2ecf20Sopenharmony_ci */ 4878c2ecf20Sopenharmony_ci if (is_power_of_2(size)) 4888c2ecf20Sopenharmony_ci align = max(minalign, (int) size); 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci if (!size) 4918c2ecf20Sopenharmony_ci return ZERO_SIZE_PTR; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci m = slob_alloc(size + minalign, gfp, align, node, minalign); 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci if (!m) 4968c2ecf20Sopenharmony_ci return NULL; 4978c2ecf20Sopenharmony_ci *m = size; 4988c2ecf20Sopenharmony_ci ret = (void *)m + minalign; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci trace_kmalloc_node(caller, ret, 5018c2ecf20Sopenharmony_ci size, size + minalign, gfp, node); 5028c2ecf20Sopenharmony_ci } else { 5038c2ecf20Sopenharmony_ci unsigned int order = get_order(size); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci if (likely(order)) 5068c2ecf20Sopenharmony_ci gfp |= __GFP_COMP; 5078c2ecf20Sopenharmony_ci ret = slob_new_pages(gfp, order, node); 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci trace_kmalloc_node(caller, ret, 5108c2ecf20Sopenharmony_ci size, PAGE_SIZE << order, gfp, node); 5118c2ecf20Sopenharmony_ci } 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci kmemleak_alloc(ret, size, 1, gfp); 5148c2ecf20Sopenharmony_ci return ret; 5158c2ecf20Sopenharmony_ci} 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_civoid *__kmalloc(size_t size, gfp_t gfp) 5188c2ecf20Sopenharmony_ci{ 5198c2ecf20Sopenharmony_ci return __do_kmalloc_node(size, gfp, NUMA_NO_NODE, _RET_IP_); 5208c2ecf20Sopenharmony_ci} 5218c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__kmalloc); 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_civoid *__kmalloc_track_caller(size_t size, gfp_t gfp, unsigned long caller) 5248c2ecf20Sopenharmony_ci{ 5258c2ecf20Sopenharmony_ci return __do_kmalloc_node(size, gfp, NUMA_NO_NODE, caller); 5268c2ecf20Sopenharmony_ci} 5278c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__kmalloc_track_caller); 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci#ifdef CONFIG_NUMA 5308c2ecf20Sopenharmony_civoid *__kmalloc_node_track_caller(size_t size, gfp_t gfp, 5318c2ecf20Sopenharmony_ci int node, unsigned long caller) 5328c2ecf20Sopenharmony_ci{ 5338c2ecf20Sopenharmony_ci return __do_kmalloc_node(size, gfp, node, caller); 5348c2ecf20Sopenharmony_ci} 5358c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__kmalloc_node_track_caller); 5368c2ecf20Sopenharmony_ci#endif 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_civoid kfree(const void *block) 5398c2ecf20Sopenharmony_ci{ 5408c2ecf20Sopenharmony_ci struct page *sp; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci trace_kfree(_RET_IP_, block); 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci if (unlikely(ZERO_OR_NULL_PTR(block))) 5458c2ecf20Sopenharmony_ci return; 5468c2ecf20Sopenharmony_ci kmemleak_free(block); 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci sp = virt_to_page(block); 5498c2ecf20Sopenharmony_ci if (PageSlab(sp)) { 5508c2ecf20Sopenharmony_ci int align = max_t(size_t, ARCH_KMALLOC_MINALIGN, ARCH_SLAB_MINALIGN); 5518c2ecf20Sopenharmony_ci unsigned int *m = (unsigned int *)(block - align); 5528c2ecf20Sopenharmony_ci slob_free(m, *m + align); 5538c2ecf20Sopenharmony_ci } else { 5548c2ecf20Sopenharmony_ci unsigned int order = compound_order(sp); 5558c2ecf20Sopenharmony_ci mod_node_page_state(page_pgdat(sp), NR_SLAB_UNRECLAIMABLE_B, 5568c2ecf20Sopenharmony_ci -(PAGE_SIZE << order)); 5578c2ecf20Sopenharmony_ci __free_pages(sp, order); 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci } 5608c2ecf20Sopenharmony_ci} 5618c2ecf20Sopenharmony_ciEXPORT_SYMBOL(kfree); 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci/* can't use ksize for kmem_cache_alloc memory, only kmalloc */ 5648c2ecf20Sopenharmony_cisize_t __ksize(const void *block) 5658c2ecf20Sopenharmony_ci{ 5668c2ecf20Sopenharmony_ci struct page *sp; 5678c2ecf20Sopenharmony_ci int align; 5688c2ecf20Sopenharmony_ci unsigned int *m; 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci BUG_ON(!block); 5718c2ecf20Sopenharmony_ci if (unlikely(block == ZERO_SIZE_PTR)) 5728c2ecf20Sopenharmony_ci return 0; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci sp = virt_to_page(block); 5758c2ecf20Sopenharmony_ci if (unlikely(!PageSlab(sp))) 5768c2ecf20Sopenharmony_ci return page_size(sp); 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci align = max_t(size_t, ARCH_KMALLOC_MINALIGN, ARCH_SLAB_MINALIGN); 5798c2ecf20Sopenharmony_ci m = (unsigned int *)(block - align); 5808c2ecf20Sopenharmony_ci return SLOB_UNITS(*m) * SLOB_UNIT; 5818c2ecf20Sopenharmony_ci} 5828c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__ksize); 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ciint __kmem_cache_create(struct kmem_cache *c, slab_flags_t flags) 5858c2ecf20Sopenharmony_ci{ 5868c2ecf20Sopenharmony_ci if (flags & SLAB_TYPESAFE_BY_RCU) { 5878c2ecf20Sopenharmony_ci /* leave room for rcu footer at the end of object */ 5888c2ecf20Sopenharmony_ci c->size += sizeof(struct slob_rcu); 5898c2ecf20Sopenharmony_ci } 5908c2ecf20Sopenharmony_ci c->flags = flags; 5918c2ecf20Sopenharmony_ci return 0; 5928c2ecf20Sopenharmony_ci} 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_cistatic void *slob_alloc_node(struct kmem_cache *c, gfp_t flags, int node) 5958c2ecf20Sopenharmony_ci{ 5968c2ecf20Sopenharmony_ci void *b; 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci flags &= gfp_allowed_mask; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci fs_reclaim_acquire(flags); 6018c2ecf20Sopenharmony_ci fs_reclaim_release(flags); 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci if (c->size < PAGE_SIZE) { 6048c2ecf20Sopenharmony_ci b = slob_alloc(c->size, flags, c->align, node, 0); 6058c2ecf20Sopenharmony_ci trace_kmem_cache_alloc_node(_RET_IP_, b, c->object_size, 6068c2ecf20Sopenharmony_ci SLOB_UNITS(c->size) * SLOB_UNIT, 6078c2ecf20Sopenharmony_ci flags, node); 6088c2ecf20Sopenharmony_ci } else { 6098c2ecf20Sopenharmony_ci b = slob_new_pages(flags, get_order(c->size), node); 6108c2ecf20Sopenharmony_ci trace_kmem_cache_alloc_node(_RET_IP_, b, c->object_size, 6118c2ecf20Sopenharmony_ci PAGE_SIZE << get_order(c->size), 6128c2ecf20Sopenharmony_ci flags, node); 6138c2ecf20Sopenharmony_ci } 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci if (b && c->ctor) { 6168c2ecf20Sopenharmony_ci WARN_ON_ONCE(flags & __GFP_ZERO); 6178c2ecf20Sopenharmony_ci c->ctor(b); 6188c2ecf20Sopenharmony_ci } 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci kmemleak_alloc_recursive(b, c->size, 1, c->flags, flags); 6218c2ecf20Sopenharmony_ci return b; 6228c2ecf20Sopenharmony_ci} 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_civoid *kmem_cache_alloc(struct kmem_cache *cachep, gfp_t flags) 6258c2ecf20Sopenharmony_ci{ 6268c2ecf20Sopenharmony_ci return slob_alloc_node(cachep, flags, NUMA_NO_NODE); 6278c2ecf20Sopenharmony_ci} 6288c2ecf20Sopenharmony_ciEXPORT_SYMBOL(kmem_cache_alloc); 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci#ifdef CONFIG_NUMA 6318c2ecf20Sopenharmony_civoid *__kmalloc_node(size_t size, gfp_t gfp, int node) 6328c2ecf20Sopenharmony_ci{ 6338c2ecf20Sopenharmony_ci return __do_kmalloc_node(size, gfp, node, _RET_IP_); 6348c2ecf20Sopenharmony_ci} 6358c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__kmalloc_node); 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_civoid *kmem_cache_alloc_node(struct kmem_cache *cachep, gfp_t gfp, int node) 6388c2ecf20Sopenharmony_ci{ 6398c2ecf20Sopenharmony_ci return slob_alloc_node(cachep, gfp, node); 6408c2ecf20Sopenharmony_ci} 6418c2ecf20Sopenharmony_ciEXPORT_SYMBOL(kmem_cache_alloc_node); 6428c2ecf20Sopenharmony_ci#endif 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_cistatic void __kmem_cache_free(void *b, int size) 6458c2ecf20Sopenharmony_ci{ 6468c2ecf20Sopenharmony_ci if (size < PAGE_SIZE) 6478c2ecf20Sopenharmony_ci slob_free(b, size); 6488c2ecf20Sopenharmony_ci else 6498c2ecf20Sopenharmony_ci slob_free_pages(b, get_order(size)); 6508c2ecf20Sopenharmony_ci} 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_cistatic void kmem_rcu_free(struct rcu_head *head) 6538c2ecf20Sopenharmony_ci{ 6548c2ecf20Sopenharmony_ci struct slob_rcu *slob_rcu = (struct slob_rcu *)head; 6558c2ecf20Sopenharmony_ci void *b = (void *)slob_rcu - (slob_rcu->size - sizeof(struct slob_rcu)); 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci __kmem_cache_free(b, slob_rcu->size); 6588c2ecf20Sopenharmony_ci} 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_civoid kmem_cache_free(struct kmem_cache *c, void *b) 6618c2ecf20Sopenharmony_ci{ 6628c2ecf20Sopenharmony_ci kmemleak_free_recursive(b, c->flags); 6638c2ecf20Sopenharmony_ci if (unlikely(c->flags & SLAB_TYPESAFE_BY_RCU)) { 6648c2ecf20Sopenharmony_ci struct slob_rcu *slob_rcu; 6658c2ecf20Sopenharmony_ci slob_rcu = b + (c->size - sizeof(struct slob_rcu)); 6668c2ecf20Sopenharmony_ci slob_rcu->size = c->size; 6678c2ecf20Sopenharmony_ci call_rcu(&slob_rcu->head, kmem_rcu_free); 6688c2ecf20Sopenharmony_ci } else { 6698c2ecf20Sopenharmony_ci __kmem_cache_free(b, c->size); 6708c2ecf20Sopenharmony_ci } 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci trace_kmem_cache_free(_RET_IP_, b); 6738c2ecf20Sopenharmony_ci} 6748c2ecf20Sopenharmony_ciEXPORT_SYMBOL(kmem_cache_free); 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_civoid kmem_cache_free_bulk(struct kmem_cache *s, size_t size, void **p) 6778c2ecf20Sopenharmony_ci{ 6788c2ecf20Sopenharmony_ci __kmem_cache_free_bulk(s, size, p); 6798c2ecf20Sopenharmony_ci} 6808c2ecf20Sopenharmony_ciEXPORT_SYMBOL(kmem_cache_free_bulk); 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ciint kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size, 6838c2ecf20Sopenharmony_ci void **p) 6848c2ecf20Sopenharmony_ci{ 6858c2ecf20Sopenharmony_ci return __kmem_cache_alloc_bulk(s, flags, size, p); 6868c2ecf20Sopenharmony_ci} 6878c2ecf20Sopenharmony_ciEXPORT_SYMBOL(kmem_cache_alloc_bulk); 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ciint __kmem_cache_shutdown(struct kmem_cache *c) 6908c2ecf20Sopenharmony_ci{ 6918c2ecf20Sopenharmony_ci /* No way to check for remaining objects */ 6928c2ecf20Sopenharmony_ci return 0; 6938c2ecf20Sopenharmony_ci} 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_civoid __kmem_cache_release(struct kmem_cache *c) 6968c2ecf20Sopenharmony_ci{ 6978c2ecf20Sopenharmony_ci} 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ciint __kmem_cache_shrink(struct kmem_cache *d) 7008c2ecf20Sopenharmony_ci{ 7018c2ecf20Sopenharmony_ci return 0; 7028c2ecf20Sopenharmony_ci} 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_cistruct kmem_cache kmem_cache_boot = { 7058c2ecf20Sopenharmony_ci .name = "kmem_cache", 7068c2ecf20Sopenharmony_ci .size = sizeof(struct kmem_cache), 7078c2ecf20Sopenharmony_ci .flags = SLAB_PANIC, 7088c2ecf20Sopenharmony_ci .align = ARCH_KMALLOC_MINALIGN, 7098c2ecf20Sopenharmony_ci}; 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_civoid __init kmem_cache_init(void) 7128c2ecf20Sopenharmony_ci{ 7138c2ecf20Sopenharmony_ci kmem_cache = &kmem_cache_boot; 7148c2ecf20Sopenharmony_ci slab_state = UP; 7158c2ecf20Sopenharmony_ci} 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_civoid __init kmem_cache_init_late(void) 7188c2ecf20Sopenharmony_ci{ 7198c2ecf20Sopenharmony_ci slab_state = FULL; 7208c2ecf20Sopenharmony_ci} 721