| /* Copyright (c) 2016 Google Inc | 
 |  * Barret Rhoden <brho@cs.berkeley.edu> | 
 |  * See LICENSE for details. | 
 |  * | 
 |  * Arena resource allocator, based on Bonwick and Adams's "Magazines and Vmem: | 
 |  * Extending the Slab Allocator to Many CPUs and Arbitrary Resources". */ | 
 |  | 
 | #pragma once | 
 |  | 
 | #include <sys/queue.h> | 
 | #include <atomic.h> | 
 | #include <rbtree.h> | 
 | #include <hash_helper.h> | 
 | #include <kthread.h> | 
 |  | 
 | /* Boundary tags track segments.  All segments, regardless of allocation status, | 
 |  * are on the all_segs list.  BTs are on other lists, depending on their status. | 
 |  * There is a list of unused BTs (those not in use by the arena), lists of free | 
 |  * segments (the power-of-two lists in the array), and lists of allocated BTs in | 
 |  * the hash table. | 
 |  * | 
 |  * BTs also track 'spans', which are contig segments that were allocated from a | 
 |  * source arena.  SPANS are never merged with adjacent BTs, and they come before | 
 |  * the ALLOC BTs that track the segments inside the span.  An entire span is | 
 |  * returned to its source when all of its entries are freed (policy, up for | 
 |  * debate/modification).  Spans are not on a free, unused, or hash list. */ | 
 | typedef enum { | 
 | 	BTAG_FREE, | 
 | 	BTAG_ALLOC, | 
 | 	BTAG_SPAN, | 
 | } btag_status_t; | 
 |  | 
 | struct btag { | 
 | 	struct rb_node			all_link; /* all non-free BTs */ | 
 | 	BSD_LIST_ENTRY(btag)		misc_link; /* freelist unused or hash */ | 
 | 	uintptr_t			start; | 
 | 	size_t				size; | 
 | 	btag_status_t			status; | 
 | }; | 
 | BSD_LIST_HEAD(btag_list, btag); | 
 |  | 
 | /* 64 is the most powers of two we can express with 64 bits. */ | 
 | #define ARENA_NR_FREE_LISTS	64 | 
 | #define ARENA_NAME_SZ		32 | 
 |  | 
 | /* Forward declarations of import lists */ | 
 | struct arena; | 
 | TAILQ_HEAD(arena_tailq, arena); | 
 | struct kmem_cache; | 
 | TAILQ_HEAD(kmem_cache_tailq, kmem_cache); | 
 |  | 
 | /* The arena maintains an in-order list of all segments, allocated or otherwise. | 
 |  * All free segments are on one of the free_segs[] lists.  There is one list for | 
 |  * each power-of-two we can allocate. */ | 
 | struct arena { | 
 | 	spinlock_t			lock; | 
 | 	uint8_t				import_scale; | 
 | 	bool				is_base; | 
 | 	size_t				quantum; | 
 | 	size_t				qcache_max; | 
 | 	struct kmem_cache		*qcaches; | 
 | 	struct rb_root			all_segs;    /* BTs, using all_link */ | 
 | 	struct btag_list		unused_btags;/* BTs, using misc_link */ | 
 | 	struct btag_list		*alloc_hash; /* BTs, using misc_link */ | 
 | 	struct hash_helper		hh; | 
 | 	void *(*afunc)(struct arena *, size_t, int); | 
 | 	void (*ffunc)(struct arena *, void *, size_t); | 
 | 	struct arena			*source; | 
 | 	size_t				amt_total_segs;	/* not include qcache */ | 
 | 	size_t				amt_alloc_segs; | 
 | 	size_t				nr_allocs_ever; | 
 | 	uintptr_t			last_nextfit_alloc; | 
 | 	struct btag_list		free_segs[ARENA_NR_FREE_LISTS]; | 
 | 	struct btag_list		static_hash[HASH_INIT_SZ]; | 
 |  | 
 | 	/* Accounting */ | 
 | 	char				name[ARENA_NAME_SZ]; | 
 | 	TAILQ_ENTRY(arena)		next; | 
 | 	/* These lists are protected by the global arena_and_slab qlock */ | 
 | 	TAILQ_ENTRY(arena)		import_link; | 
 | 	struct arena_tailq		__importing_arenas; | 
 | 	struct kmem_cache_tailq		__importing_slabs; | 
 | }; | 
 |  | 
 | extern struct arena_tailq all_arenas; | 
 |  | 
 | /* Arena allocation styles, or'd with MEM_FLAGS */ | 
 | #define ARENA_BESTFIT		0x100 | 
 | #define ARENA_INSTANTFIT	0x200 | 
 | #define ARENA_NEXTFIT		0x400 | 
 | #define ARENA_ALLOC_STYLES (ARENA_BESTFIT | ARENA_INSTANTFIT | ARENA_NEXTFIT) | 
 |  | 
 | /* Magic source, for arenas that dynamically generate their contents. */ | 
 | #define ARENA_SELF_SOURCE (void*)(-2) | 
 | /* Creates an area, with initial segment [@base, @base + @size).  Allocs are in | 
 |  * units of @quantum.  If @source is provided, the arena will alloc new segments | 
 |  * from @source, calling @afunc to alloc and @ffunc to free.  Uses a slab | 
 |  * allocator for allocations up to @qcache_max (0 = no caching). */ | 
 | struct arena *arena_create(const char *name, void *base, size_t size, | 
 | 			   size_t quantum, | 
 |                            void *(*afunc)(struct arena *, size_t, int), | 
 |                            void (*ffunc)(struct arena *, void *, size_t), | 
 |                            struct arena *source, size_t qcache_max, int flags); | 
 | /* Adds segment [@base, @base + @size) to @arena. */ | 
 | void *arena_add(struct arena *arena, void *base, size_t size, int flags); | 
 | void arena_destroy(struct arena *arena); | 
 | /* Lower-level create/destroy interface; caller manages the memory */ | 
 | void __arena_create(struct arena *arena, const char *name, size_t quantum, | 
 | 		    void *(*afunc)(struct arena *, size_t, int), | 
 | 		    void (*ffunc)(struct arena *, void *, size_t), | 
 | 		    struct arena *source, size_t qcache_max); | 
 | void __arena_destroy(struct arena *arena); | 
 |  | 
 | void *arena_alloc(struct arena *arena, size_t size, int flags); | 
 | void arena_free(struct arena *arena, void *addr, size_t size); | 
 | void *arena_xalloc(struct arena *arena, size_t size, size_t align, size_t phase, | 
 |                    size_t nocross, void *minaddr, void *maxaddr, int flags); | 
 | void arena_xfree(struct arena *arena, void *addr, size_t size); | 
 |  | 
 | size_t arena_amt_free(struct arena *arena); | 
 | size_t arena_amt_total(struct arena *arena); | 
 | void kmemstat(void); | 
 |  | 
 | /* All lists that track the existence of arenas, slabs, and the connections | 
 |  * between them are tracked by a global qlock.  For the most part, slabs/arenas | 
 |  * are created rarely, and the main lockers will be a reclaim ktask and #mem | 
 |  * accessors. */ | 
 | extern qlock_t arenas_and_slabs_lock; | 
 | void add_importing_arena(struct arena *source, struct arena *importer); | 
 | void del_importing_arena(struct arena *source, struct arena *importer); | 
 | void add_importing_slab(struct arena *source, struct kmem_cache *importer); | 
 | void del_importing_slab(struct arena *source, struct kmem_cache *importer); | 
 |  | 
 | /* Low-level memory allocator intefaces */ | 
 | extern struct arena *base_arena; | 
 | extern struct arena *kpages_arena; | 
 | struct arena *arena_builder(void *pgaddr, const char *name, size_t quantum, | 
 |                             void *(*afunc)(struct arena *, size_t, int), | 
 |                             void (*ffunc)(struct arena *, void *, size_t), | 
 |                             struct arena *source, size_t qcache_max); | 
 | /* Allocate directly from the nearest base allocator.  Used by other mm code. | 
 |  * Pass in your closest arena (such as your source) to help us find a base. */ | 
 | void *base_alloc(struct arena *guess, size_t size, int flags); | 
 | void *base_zalloc(struct arena *guess, size_t size, int flags); | 
 | void base_free(struct arena *guess, void *addr, size_t size); |