diff options
-rw-r--r-- | Makefile.am | 54 | ||||
-rw-r--r-- | mem.c | 1816 | ||||
-rw-r--r-- | mem.h | 120 | ||||
-rw-r--r-- | mem_malloc.c | 153 | ||||
-rw-r--r-- | mem_malloc.h | 42 | ||||
-rw-r--r-- | phys.c | 777 | ||||
-rw-r--r-- | phys.h | 66 | ||||
-rw-r--r-- | test/test_mem.c | 57 | ||||
-rw-r--r-- | test/test_mem_cache.c | 145 | ||||
-rw-r--r-- | test/test_mem_cache_double_free.c | 75 | ||||
-rw-r--r-- | test/test_mem_cache_invalid_free.c | 74 | ||||
-rw-r--r-- | test/test_mem_cache_write_beyond.c | 90 | ||||
-rw-r--r-- | test/test_mem_cache_write_buftag.c | 92 | ||||
-rw-r--r-- | test/test_mem_cache_write_free.c | 106 | ||||
-rw-r--r-- | test/test_mem_offbyone.c | 58 | ||||
-rw-r--r-- | test/test_phys.c | 65 | ||||
-rw-r--r-- | test/test_xprintf.c | 298 | ||||
-rw-r--r-- | xprintf.c | 591 | ||||
-rw-r--r-- | xprintf.h | 58 |
19 files changed, 3 insertions, 4734 deletions
diff --git a/Makefile.am b/Makefile.am index 55da9a2..d685fce 100644 --- a/Makefile.am +++ b/Makefile.am @@ -25,72 +25,24 @@ librbraun_la_SOURCES = \ list.c \ list.h \ macros.h \ - mem.c \ - mem.h \ - mem_malloc.c \ - mem_malloc.h \ - phys.c \ - phys.h \ rbtree.c \ rbtree.h \ rbtree_i.h \ rdxtree.c \ - rdxtree.h \ - xprintf.c \ - xprintf.h + rdxtree.h + librbraun_la_LIBADD = -lrt -lpthread bin_PROGRAMS = \ test_avltree \ - test_mem \ - test_mem_cache \ - test_mem_cache_invalid_free \ - test_mem_cache_double_free \ - test_mem_cache_write_free \ - test_mem_cache_write_beyond \ - test_mem_cache_write_buftag \ - test_mem_offbyone \ - test_phys \ test_rbtree \ - test_rdxtree \ - test_xprintf + test_rdxtree test_avltree_SOURCES = test/test_avltree.c test_avltree_LDADD = librbraun.la -test_mem_SOURCES = test/test_mem.c -test_mem_LDADD = librbraun.la - -test_mem_cache_SOURCES = test/test_mem_cache.c -test_mem_cache_LDADD = -lrt -lpthread - -test_mem_cache_invalid_free_SOURCES = test/test_mem_cache_invalid_free.c -test_mem_cache_invalid_free_LDADD = librbraun.la - -test_mem_cache_double_free_SOURCES = test/test_mem_cache_double_free.c -test_mem_cache_double_free_LDADD = librbraun.la - -test_mem_cache_write_free_SOURCES = test/test_mem_cache_write_free.c -test_mem_cache_write_free_LDADD = librbraun.la - -test_mem_cache_write_beyond_SOURCES = test/test_mem_cache_write_beyond.c -test_mem_cache_write_beyond_LDADD = librbraun.la - -test_mem_cache_write_buftag_SOURCES = test/test_mem_cache_write_buftag.c -test_mem_cache_write_buftag_LDADD = librbraun.la - -test_mem_offbyone_SOURCES = test/test_mem_offbyone.c -test_mem_offbyone_LDADD = librbraun.la - -test_phys_SOURCES = test/test_phys.c -test_phys_LDADD = -lrt -lpthread - test_rbtree_SOURCES = test/test_rbtree.c test_rbtree_LDADD = librbraun.la test_rdxtree_SOURCES = test/test_rdxtree.c test_rdxtree_LDADD = -lrt -lpthread - -test_xprintf_SOURCES = test/test_xprintf.c -test_xprintf_CFLAGS = $(AM_CFLAGS) -Wno-format -test_xprintf_LDADD = librbraun.la @@ -1,1816 +0,0 @@ -/* - * Copyright (c) 2010, 2011 Richard Braun. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * - * Object caching and general purpose memory allocator. - * - * This allocator is based on the following works : - * - "The Slab Allocator: An Object-Caching Kernel Memory Allocator", - * by Jeff Bonwick. - * - * It allows the allocation of objects (i.e. fixed-size typed buffers) from - * caches and is efficient in both space and time. This implementation follows - * many of the indications from the paper mentioned. The most notable - * differences are outlined below. - * - * The per-cache self-scaling hash table for buffer-to-bufctl conversion, - * described in 3.2.3 "Slab Layout for Large Objects", has been replaced by - * an AVL tree storing slabs, sorted by address. The use of a self-balancing - * tree for buffer-to-slab conversions provides a few advantages over a hash - * table. Unlike a hash table, a BST provides a "lookup nearest" operation, - * so obtaining the slab data (whether it is embedded in the slab or off - * slab) from a buffer address simply consists of a "lookup nearest towards - * 0" tree search. Storing slabs instead of buffers also considerably reduces - * the number of elements to retain. Finally, a self-balancing tree is a true - * self-scaling data structure, whereas a hash table requires periodic - * maintenance and complete resizing, which is expensive. The only drawback is - * that releasing a buffer to the slab layer takes logarithmic time instead of - * constant time. But as the data set size is kept reasonable (because slabs - * are stored instead of buffers) and because the CPU pool layer services most - * requests, avoiding many accesses to the slab layer, it is considered an - * acceptable tradeoff. - * - * This implementation uses per-cpu pools of objects, which service most - * allocation requests. These pools act as caches (but are named differently - * to avoid confusion with CPU caches) that reduce contention on multiprocessor - * systems. When a pool is empty and cannot provide an object, it is filled by - * transferring multiple objects from the slab layer. The symmetric case is - * handled likewise. - */ - -#include <time.h> -#include <errno.h> -#include <sched.h> -#include <stdio.h> -#include <assert.h> -#include <limits.h> -#include <stddef.h> -#include <stdint.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <pthread.h> -#include <sys/mman.h> - -#include "cpu.h" -#include "mem.h" -#include "list.h" -#include "error.h" -#include "macros.h" -#include "avltree.h" - -/* - * The system page size. - * - * This macro actually expands to a global variable that is set on - * initialization. - */ -#define PAGE_SIZE ((unsigned long)_pagesize) - -/* - * Minimum required alignment. - */ -#define MEM_ALIGN_MIN 8 - -/* - * Minimum number of buffers per slab. - * - * This value is ignored when the slab size exceeds a threshold. - */ -#define MEM_MIN_BUFS_PER_SLAB 8 - -/* - * Special slab size beyond which the minimum number of buffers per slab is - * ignored when computing the slab size of a cache. - */ -#define MEM_SLAB_SIZE_THRESHOLD (8 * PAGE_SIZE) - -/* - * Special buffer size under which slab data is unconditionnally allocated - * from its associated slab. - */ -#define MEM_BUF_SIZE_THRESHOLD (PAGE_SIZE / 8) - -/* - * Time (in seconds) between two garbage collection operations. - */ -#define MEM_GC_INTERVAL 15 - -/* - * The transfer size of a CPU pool is computed by dividing the pool size by - * this value. - */ -#define MEM_CPU_POOL_TRANSFER_RATIO 2 - -/* - * Shift for the first general cache size. - */ -#define MEM_CACHES_FIRST_SHIFT 5 - -/* - * Number of caches backing general purpose allocations. - */ -#define MEM_NR_MEM_CACHES 13 - -/* - * Per-processor cache of pre-constructed objects. - * - * The flags member is a read-only CPU-local copy of the parent cache flags. - */ -struct mem_cpu_pool { - pthread_mutex_t lock; - int flags; - int size; - int transfer_size; - int nr_objs; - void **array; -} __aligned(CPU_L1_SIZE); - -/* - * When a cache is created, its CPU pool type is determined from the buffer - * size. For small buffer sizes, many objects can be cached in a CPU pool. - * Conversely, for large buffer sizes, this would incur much overhead, so only - * a few objects are stored in a CPU pool. - */ -struct mem_cpu_pool_type { - size_t buf_size; - int array_size; - size_t array_align; - struct mem_cache *array_cache; -}; - -/* - * Buffer descriptor. - * - * For normal caches (i.e. without MEM_CF_VERIFY), bufctls are located at the - * end of (but inside) each buffer. If MEM_CF_VERIFY is set, bufctls are located - * after each buffer. - * - * When an object is allocated to a client, its bufctl isn't used. This memory - * is instead used for redzoning if cache debugging is in effect. - */ -union mem_bufctl { - union mem_bufctl *next; - unsigned long redzone; -}; - -/* - * Redzone guard word. - */ -#ifdef __LP64__ -#if _HOST_BIG_ENDIAN -#define MEM_REDZONE_WORD 0xfeedfacefeedfaceUL -#else /* _HOST_BIG_ENDIAN */ -#define MEM_REDZONE_WORD 0xcefaedfecefaedfeUL -#endif /* _HOST_BIG_ENDIAN */ -#else /* __LP64__ */ -#if _HOST_BIG_ENDIAN -#define MEM_REDZONE_WORD 0xfeedfaceUL -#else /* _HOST_BIG_ENDIAN */ -#define MEM_REDZONE_WORD 0xcefaedfeUL -#endif /* _HOST_BIG_ENDIAN */ -#endif /* __LP64__ */ - -/* - * Redzone byte for padding. - */ -#define MEM_REDZONE_BYTE 0xbb - -/* - * Buffer tag. - * - * This structure is only used for MEM_CF_VERIFY caches. It is located after - * the bufctl and includes information about the state of the buffer it - * describes (allocated or not). It should be thought of as a debugging - * extension of the bufctl. - */ -struct mem_buftag { - unsigned long state; -}; - -/* - * Values the buftag state member can take. - */ -#ifdef __LP64__ -#if _HOST_BIG_ENDIAN -#define MEM_BUFTAG_ALLOC 0xa110c8eda110c8edUL -#define MEM_BUFTAG_FREE 0xf4eeb10cf4eeb10cUL -#else /* _HOST_BIG_ENDIAN */ -#define MEM_BUFTAG_ALLOC 0xedc810a1edc810a1UL -#define MEM_BUFTAG_FREE 0x0cb1eef40cb1eef4UL -#endif /* _HOST_BIG_ENDIAN */ -#else /* __LP64__ */ -#if _HOST_BIG_ENDIAN -#define MEM_BUFTAG_ALLOC 0xa110c8edUL -#define MEM_BUFTAG_FREE 0xf4eeb10cUL -#else /* _HOST_BIG_ENDIAN */ -#define MEM_BUFTAG_ALLOC 0xedc810a1UL -#define MEM_BUFTAG_FREE 0x0cb1eef4UL -#endif /* _HOST_BIG_ENDIAN */ -#endif /* __LP64__ */ - -/* - * Free and uninitialized patterns. - * - * These values are unconditionnally 64-bit wide since buffers are at least - * 8-byte aligned. - */ -#if _HOST_BIG_ENDIAN -#define MEM_FREE_PATTERN 0xdeadbeefdeadbeefULL -#define MEM_UNINIT_PATTERN 0xbaddcafebaddcafeULL -#else /* _HOST_BIG_ENDIAN */ -#define MEM_FREE_PATTERN 0xefbeaddeefbeaddeULL -#define MEM_UNINIT_PATTERN 0xfecaddbafecaddbaULL -#endif /* _HOST_BIG_ENDIAN */ - -/* - * Page-aligned collection of unconstructed buffers. - */ -struct mem_slab { - struct list list_node; - struct avltree_node tree_node; - unsigned long nr_refs; - union mem_bufctl *first_free; - void *addr; -}; - -/* - * Private cache creation flags. - */ -#define MEM_CREATE_INTERNAL 0x0100 /* Prevent off slab data */ - -/* - * Cache name buffer size. - */ -#define MEM_NAME_SIZE 32 - -/* - * Cache flags. - * - * The flags don't change once set and can be tested without locking. - */ -#define MEM_CF_DIRECT 0x0001 /* No buf-to-slab tree lookup */ -#define MEM_CF_SLAB_EXTERNAL 0x0002 /* Slab data is off slab */ - -/* - * Debugging flags - */ -#define MEM_CF_VERIFY 0x0100 /* Use debugging facilities */ - -/* - * Cache of objects. - * - * Locking order : cpu_pool -> cache. CPU pools locking is ordered by CPU ID. - * - * The partial slabs list is sorted by slab references. Slabs with a high - * number of references are placed first on the list to reduce fragmentation. - * Sorting occurs at insertion/removal of buffers in a slab. As the list - * is maintained sorted, and the number of references only changes by one, - * this is a very cheap operation in the average case and the worst (linear) - * case is very unlikely. - */ -struct mem_cache { - /* CPU pool layer */ - struct mem_cpu_pool cpu_pools[NR_CPUS]; - struct mem_cpu_pool_type *cpu_pool_type; - - /* Slab layer */ - pthread_mutex_t lock; - struct list node; /* Cache list linkage */ - struct list partial_slabs; - struct list free_slabs; - struct avltree active_slabs; - int flags; - size_t obj_size; /* User-provided size */ - size_t align; - size_t buf_size; /* Aligned object size */ - size_t bufctl_dist; /* Distance from buffer to bufctl */ - size_t slab_size; - size_t color; - size_t color_max; - unsigned long bufs_per_slab; - unsigned long nr_objs; /* Number of allocated objects */ - unsigned long nr_bufs; /* Total number of buffers */ - unsigned long nr_slabs; - unsigned long nr_free_slabs; - mem_cache_ctor_t ctor; - struct mem_source source; - char name[MEM_NAME_SIZE]; - size_t buftag_dist; /* Distance from buffer to buftag */ - size_t redzone_pad; /* Bytes from end of object to redzone word */ -}; - -/* - * Options for mem_cache_alloc_verify(). - */ -#define MEM_AV_NOCONSTRUCT 0 -#define MEM_AV_CONSTRUCT 1 - -/* - * Error codes for mem_cache_error(). - */ -#define MEM_ERR_INVALID 0 /* Invalid address being freed */ -#define MEM_ERR_DOUBLEFREE 1 /* Freeing already free address */ -#define MEM_ERR_BUFTAG 2 /* Invalid buftag content */ -#define MEM_ERR_MODIFIED 3 /* Buffer modified while free */ -#define MEM_ERR_REDZONE 4 /* Redzone violation */ - -/* - * See PAGE_SIZE. - */ -static long _pagesize; - -/* - * Available CPU pool types. - * - * For each entry, the CPU pool size applies from the entry buf_size - * (excluded) up to (and including) the buf_size of the preceding entry. - * - * See struct cpu_pool_type for a description of the values. - */ -static struct mem_cpu_pool_type mem_cpu_pool_types[] = { - { 32768, 1, 0, NULL }, - { 4096, 8, CPU_L1_SIZE, NULL }, - { 256, 64, CPU_L1_SIZE, NULL }, - { 0, 128, CPU_L1_SIZE, NULL } -}; - -/* - * Caches where CPU pool arrays are allocated from. - */ -static struct mem_cache mem_cpu_array_caches[ARRAY_SIZE(mem_cpu_pool_types)]; - -/* - * Cache for off slab data. - */ -static struct mem_cache mem_slab_cache; - -/* - * Cache for dynamically created caches. - */ -static struct mem_cache mem_cache_cache; - -/* - * General caches array. - */ -static struct mem_cache mem_caches[MEM_NR_MEM_CACHES]; - -/* - * List of all caches managed by the allocator. - */ -static struct list mem_cache_list; -static pthread_mutex_t mem_cache_list_lock; - -/* - * Default backend functions. - */ -static void * mem_default_alloc(size_t size); -static void mem_default_free(void *ptr, size_t size); - -/* - * Default source of memory. - */ -static struct mem_source mem_default_source = { - mem_default_alloc, - mem_default_free -}; - -#define mem_error(format, ...) \ - fprintf(stderr, "mem: error: %s(): " format "\n", __func__, \ - ## __VA_ARGS__) - -#define mem_warn(format, ...) \ - fprintf(stderr, "mem: warning: %s(): " format "\n", __func__, \ - ## __VA_ARGS__) - -#define mem_print(format, ...) \ - fprintf(stderr, format "\n", ## __VA_ARGS__) - -static void mem_cache_error(struct mem_cache *cache, void *buf, int error, - void *arg); -static void * mem_cache_alloc_from_slab(struct mem_cache *cache); -static void mem_cache_free_to_slab(struct mem_cache *cache, void *buf); - -#if CONFIG_MEM_USE_PHYS -#include "phys.h" - -static void * -mem_default_alloc(size_t size) -{ - return (void *)phys_alloc(size); -} - -static void -mem_default_free(void *ptr, size_t size) -{ - phys_free((phys_paddr_t)ptr, size); -} -#else /* CONFIG_MEM_USE_PHYS */ -static void * -mem_default_alloc(size_t size) -{ - void *addr; - - addr = mmap(NULL, size, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - - if (addr == MAP_FAILED) - return NULL; - - return addr; -} - -static void -mem_default_free(void *ptr, size_t size) -{ - munmap(ptr, size); -} -#endif /* CONFIG_MEM_USE_PHYS */ - -static void * -mem_buf_verify_bytes(void *buf, void *pattern, size_t size) -{ - char *ptr, *pattern_ptr, *end; - - end = buf + size; - - for (ptr = buf, pattern_ptr = pattern; ptr < end; ptr++, pattern_ptr++) - if (*ptr != *pattern_ptr) - return ptr; - - return NULL; -} - -static void * -mem_buf_verify(void *buf, uint64_t pattern, size_t size) -{ - uint64_t *ptr, *end; - - assert(P2ALIGNED((unsigned long)buf, sizeof(uint64_t))); - assert(P2ALIGNED(size, sizeof(uint64_t))); - - end = buf + size; - - for (ptr = buf; ptr < end; ptr++) - if (*ptr != pattern) - return mem_buf_verify_bytes(ptr, &pattern, sizeof(pattern)); - - return NULL; -} - -static void -mem_buf_fill(void *buf, uint64_t pattern, size_t size) -{ - uint64_t *ptr, *end; - - assert(P2ALIGNED((unsigned long)buf, sizeof(uint64_t))); - assert(P2ALIGNED(size, sizeof(uint64_t))); - - end = buf + size; - - for (ptr = buf; ptr < end; ptr++) - *ptr = pattern; -} - -static void * -mem_buf_verify_fill(void *buf, uint64_t old, uint64_t new, size_t size) -{ - uint64_t *ptr, *end; - - assert(P2ALIGNED((unsigned long)buf, sizeof(uint64_t))); - assert(P2ALIGNED(size, sizeof(uint64_t))); - - end = buf + size; - - for (ptr = buf; ptr < end; ptr++) { - if (*ptr != old) - return mem_buf_verify_bytes(ptr, &old, sizeof(old)); - - *ptr = new; - } - - return NULL; -} - -static inline union mem_bufctl * -mem_buf_to_bufctl(void *buf, struct mem_cache *cache) -{ - return (union mem_bufctl *)(buf + cache->bufctl_dist); -} - -static inline struct mem_buftag * -mem_buf_to_buftag(void *buf, struct mem_cache *cache) -{ - return (struct mem_buftag *)(buf + cache->buftag_dist); -} - -static inline void * -mem_bufctl_to_buf(union mem_bufctl *bufctl, struct mem_cache *cache) -{ - return (void *)bufctl - cache->bufctl_dist; -} - -static void -mem_slab_create_verify(struct mem_slab *slab, struct mem_cache *cache) -{ - struct mem_buftag *buftag; - size_t buf_size; - unsigned long buffers; - void *buf; - - buf_size = cache->buf_size; - buf = slab->addr; - buftag = mem_buf_to_buftag(buf, cache); - - for (buffers = cache->bufs_per_slab; buffers != 0; buffers--) { - mem_buf_fill(buf, MEM_FREE_PATTERN, cache->bufctl_dist); - buftag->state = MEM_BUFTAG_FREE; - buf += buf_size; - buftag = mem_buf_to_buftag(buf, cache); - } -} - -/* - * Create an empty slab for a cache. - * - * The caller must drop all locks before calling this function. - */ -static struct mem_slab * -mem_slab_create(struct mem_cache *cache, size_t color) -{ - struct mem_slab *slab; - union mem_bufctl *bufctl; - size_t buf_size; - unsigned long buffers; - void *slab_buf; - - slab_buf = cache->source.alloc_fn(cache->slab_size); - - if (slab_buf == NULL) - return NULL; - - if (cache->flags & MEM_CF_SLAB_EXTERNAL) { - slab = mem_cache_alloc(&mem_slab_cache); - - if (slab == NULL) { - cache->source.free_fn(slab_buf, cache->slab_size); - return NULL; - } - } else { - slab = (struct mem_slab *)(slab_buf + cache->slab_size) - 1; - } - - list_node_init(&slab->list_node); - avltree_node_init(&slab->tree_node); - slab->nr_refs = 0; - slab->first_free = NULL; - slab->addr = slab_buf + color; - - buf_size = cache->buf_size; - bufctl = mem_buf_to_bufctl(slab->addr, cache); - - for (buffers = cache->bufs_per_slab; buffers != 0; buffers--) { - bufctl->next = slab->first_free; - slab->first_free = bufctl; - bufctl = (union mem_bufctl *)((void *)bufctl + buf_size); - } - - if (cache->flags & MEM_CF_VERIFY) - mem_slab_create_verify(slab, cache); - - return slab; -} - -static void -mem_slab_destroy_verify(struct mem_slab *slab, struct mem_cache *cache) -{ - struct mem_buftag *buftag; - size_t buf_size; - unsigned long buffers; - void *buf, *addr; - - buf_size = cache->buf_size; - buf = slab->addr; - buftag = mem_buf_to_buftag(buf, cache); - - for (buffers = cache->bufs_per_slab; buffers != 0; buffers--) { - if (buftag->state != MEM_BUFTAG_FREE) - mem_cache_error(cache, buf, MEM_ERR_BUFTAG, buftag); - - addr = mem_buf_verify(buf, MEM_FREE_PATTERN, cache->bufctl_dist); - - if (addr != NULL) - mem_cache_error(cache, buf, MEM_ERR_MODIFIED, addr); - - buf += buf_size; - buftag = mem_buf_to_buftag(buf, cache); - } -} - -/* - * Destroy a slab. - * - * The caller must drop all locks before calling this function. - */ -static void -mem_slab_destroy(struct mem_slab *slab, struct mem_cache *cache) -{ - void *slab_buf; - - assert(slab->nr_refs == 0); - assert(slab->first_free != NULL); - - if (cache->flags & MEM_CF_VERIFY) - mem_slab_destroy_verify(slab, cache); - - slab_buf = (void *)P2ALIGN((unsigned long)slab->addr, PAGE_SIZE); - cache->source.free_fn(slab_buf, cache->slab_size); - - if (cache->flags & MEM_CF_SLAB_EXTERNAL) - mem_cache_free(&mem_slab_cache, slab); -} - -static inline int -mem_slab_use_tree(int flags) -{ - return !(flags & MEM_CF_DIRECT) || (flags & MEM_CF_VERIFY); -} - -static inline int -mem_slab_cmp_lookup(const void *addr, const struct avltree_node *node) -{ - struct mem_slab *slab; - - slab = avltree_entry(node, struct mem_slab, tree_node); - - if (addr == slab->addr) - return 0; - else if (addr < slab->addr) - return -1; - else - return 1; -} - -static inline int -mem_slab_cmp_insert(const struct avltree_node *a, const struct avltree_node *b) -{ - struct mem_slab *slab; - - slab = avltree_entry(a, struct mem_slab, tree_node); - return mem_slab_cmp_lookup(slab->addr, b); -} - -static void -mem_cpu_pool_init(struct mem_cpu_pool *cpu_pool, struct mem_cache *cache) -{ - pthread_mutex_init(&cpu_pool->lock, NULL); - cpu_pool->flags = cache->flags; - cpu_pool->size = 0; - cpu_pool->transfer_size = 0; - cpu_pool->nr_objs = 0; - cpu_pool->array = NULL; -} - -/* - * Return a CPU pool. - * - * This function will generally return the pool matching the CPU running the - * calling thread. Because of context switches and thread migration, the - * caller might be running on another processor after this function returns. - * Although not optimal, this should rarely happen, and it doesn't affect the - * allocator operations in any other way, as CPU pools are always valid, and - * their access is serialized by a lock. - */ -static inline struct mem_cpu_pool * -mem_cpu_pool_get(struct mem_cache *cache) -{ - return &cache->cpu_pools[cpu_id()]; -} - -static inline void -mem_cpu_pool_build(struct mem_cpu_pool *cpu_pool, struct mem_cache *cache, - void **array) -{ - cpu_pool->size = cache->cpu_pool_type->array_size; - cpu_pool->transfer_size = (cpu_pool->size + MEM_CPU_POOL_TRANSFER_RATIO - 1) - / MEM_CPU_POOL_TRANSFER_RATIO; - cpu_pool->array = array; -} - -static inline void * -mem_cpu_pool_pop(struct mem_cpu_pool *cpu_pool) -{ - cpu_pool->nr_objs--; - return cpu_pool->array[cpu_pool->nr_objs]; -} - -static inline void -mem_cpu_pool_push(struct mem_cpu_pool *cpu_pool, void *obj) -{ - cpu_pool->array[cpu_pool->nr_objs] = obj; - cpu_pool->nr_objs++; -} - -static int -mem_cpu_pool_fill(struct mem_cpu_pool *cpu_pool, struct mem_cache *cache) -{ - void *obj; - int i; - - pthread_mutex_lock(&cache->lock); - - for (i = 0; i < cpu_pool->transfer_size; i++) { - obj = mem_cache_alloc_from_slab(cache); - - if (obj == NULL) - break; - - mem_cpu_pool_push(cpu_pool, obj); - } - - pthread_mutex_unlock(&cache->lock); - - return i; -} - -static void -mem_cpu_pool_drain(struct mem_cpu_pool *cpu_pool, struct mem_cache *cache) -{ - void *obj; - int i; - - pthread_mutex_lock(&cache->lock); - - for (i = cpu_pool->transfer_size; i > 0; i--) { - obj = mem_cpu_pool_pop(cpu_pool); - mem_cache_free_to_slab(cache, obj); - } - - pthread_mutex_unlock(&cache->lock); -} - -static void -mem_cache_error(struct mem_cache *cache, void *buf, int error, void *arg) -{ - struct mem_buftag *buftag; - - mem_error("cache: %s, buffer: %p", cache->name, buf); - - switch(error) { - case MEM_ERR_INVALID: - mem_error("freeing invalid address"); - break; - case MEM_ERR_DOUBLEFREE: - mem_error("attempting to free the same address twice"); - break; - case MEM_ERR_BUFTAG: - mem_error("invalid buftag content"); - buftag = arg; - mem_error("buftag state: %p", (void *)buftag->state); - break; - case MEM_ERR_MODIFIED: - mem_error("free buffer modified"); - mem_error("fault address: %p, offset in buffer: %td", arg, arg - buf); - break; - case MEM_ERR_REDZONE: - mem_error("write beyond end of buffer"); - mem_error("fault address: %p, offset in buffer: %td", arg, arg - buf); - break; - default: - mem_error("unknown error"); - } - - error_die(ERR_MEM_CACHE); - - /* - * Never reached. - */ -} - -/* - * Compute an appropriate slab size for the given cache. - * - * Once the slab size is known, this function sets the related properties - * (buffers per slab and maximum color). It can also set the MEM_CF_DIRECT - * and/or MEM_CF_SLAB_EXTERNAL flags depending on the resulting layout. - */ -static void -mem_cache_compute_sizes(struct mem_cache *cache, int flags) -{ - size_t i, buffers, buf_size, slab_size, free_slab_size, optimal_size; - size_t waste, waste_min; - int embed, optimal_embed; - - buf_size = cache->buf_size; - - if (buf_size < MEM_BUF_SIZE_THRESHOLD) - flags |= MEM_CREATE_INTERNAL; - - i = 0; - waste_min = (size_t)-1; - - do { - i++; - slab_size = P2ROUND(i * buf_size, PAGE_SIZE); - free_slab_size = slab_size; - - if (flags & MEM_CREATE_INTERNAL) - free_slab_size -= sizeof(struct mem_slab); - - buffers = free_slab_size / buf_size; - waste = free_slab_size % buf_size; - - if (buffers > i) - i = buffers; - - if (flags & MEM_CREATE_INTERNAL) - embed = 1; - else if (sizeof(struct mem_slab) <= waste) { - embed = 1; - waste -= sizeof(struct mem_slab); - } else { - embed = 0; - } - - if (waste <= waste_min) { - waste_min = waste; - optimal_size = slab_size; - optimal_embed = embed; - } - } while ((buffers < MEM_MIN_BUFS_PER_SLAB) - && (slab_size < MEM_SLAB_SIZE_THRESHOLD)); - - assert(!(flags & MEM_CREATE_INTERNAL) || optimal_embed); - - cache->slab_size = optimal_size; - slab_size = cache->slab_size - (optimal_embed - ? sizeof(struct mem_slab) - : 0); - cache->bufs_per_slab = slab_size / buf_size; - cache->color_max = slab_size % buf_size; - - if (cache->color_max >= PAGE_SIZE) - cache->color_max = PAGE_SIZE - 1; - - if (optimal_embed) { - if (cache->slab_size == PAGE_SIZE) - cache->flags |= MEM_CF_DIRECT; - } else { - cache->flags |= MEM_CF_SLAB_EXTERNAL; - } -} - -static void -mem_cache_init(struct mem_cache *cache, const char *name, - size_t obj_size, size_t align, mem_cache_ctor_t ctor, - const struct mem_source *source, int flags) -{ - struct mem_cpu_pool_type *cpu_pool_type; - size_t i, buf_size; - -#if CONFIG_MEM_VERIFY - cache->flags = MEM_CF_VERIFY; -#else - cache->flags = 0; -#endif - - if (flags & MEM_CACHE_VERIFY) - cache->flags |= MEM_CF_VERIFY; - - if (align < MEM_ALIGN_MIN) - align = MEM_ALIGN_MIN; - - assert(obj_size > 0); - assert(ISP2(align)); - assert(align < PAGE_SIZE); - - buf_size = P2ROUND(obj_size, align); - - if (source == NULL) - source = &mem_default_source; - - pthread_mutex_init(&cache->lock, NULL); - list_node_init(&cache->node); - list_init(&cache->partial_slabs); - list_init(&cache->free_slabs); - avltree_init(&cache->active_slabs); - cache->obj_size = obj_size; - cache->align = align; - cache->buf_size = buf_size; - cache->bufctl_dist = buf_size - sizeof(union mem_bufctl); - cache->color = 0; - cache->nr_objs = 0; - cache->nr_bufs = 0; - cache->nr_slabs = 0; - cache->nr_free_slabs = 0; - cache->ctor = ctor; - cache->source = *source; - strncpy(cache->name, name, MEM_NAME_SIZE); - cache->name[MEM_NAME_SIZE - 1] = '\0'; - cache->buftag_dist = 0; - cache->redzone_pad = 0; - - if (cache->flags & MEM_CF_VERIFY) { - cache->bufctl_dist = buf_size; - cache->buftag_dist = cache->bufctl_dist + sizeof(union mem_bufctl); - cache->redzone_pad = cache->bufctl_dist - cache->obj_size; - buf_size += sizeof(union mem_bufctl) + sizeof(struct mem_buftag); - buf_size = P2ROUND(buf_size, align); - cache->buf_size = buf_size; - } - - mem_cache_compute_sizes(cache, flags); - - for (cpu_pool_type = mem_cpu_pool_types; - buf_size <= cpu_pool_type->buf_size; - cpu_pool_type++); - - cache->cpu_pool_type = cpu_pool_type; - - for (i = 0; i < ARRAY_SIZE(cache->cpu_pools); i++) - mem_cpu_pool_init(&cache->cpu_pools[i], cache); - - pthread_mutex_lock(&mem_cache_list_lock); - list_insert_tail(&mem_cache_list, &cache->node); - pthread_mutex_unlock(&mem_cache_list_lock); -} - -struct mem_cache * -mem_cache_create(const char *name, size_t obj_size, size_t align, - mem_cache_ctor_t ctor, const struct mem_source *source, - int flags) -{ - struct mem_cache *cache; - - cache = mem_cache_alloc(&mem_cache_cache); - - if (cache == NULL) - return NULL; - - mem_cache_init(cache, name, obj_size, align, ctor, source, flags); - - return cache; -} - -static inline int -mem_cache_empty(struct mem_cache *cache) -{ - return cache->nr_objs == cache->nr_bufs; -} - -static int -mem_cache_grow(struct mem_cache *cache) -{ - struct mem_slab *slab; - size_t color; - int empty; - - pthread_mutex_lock(&cache->lock); - - if (!mem_cache_empty(cache)) { - pthread_mutex_unlock(&cache->lock); - return 1; - } - - color = cache->color; - cache->color += cache->align; - - if (cache->color > cache->color_max) - cache->color = 0; - - pthread_mutex_unlock(&cache->lock); - - slab = mem_slab_create(cache, color); - - pthread_mutex_lock(&cache->lock); - - if (slab != NULL) { - list_insert_tail(&cache->free_slabs, &slab->list_node); - cache->nr_bufs += cache->bufs_per_slab; - cache->nr_slabs++; - cache->nr_free_slabs++; - } - - /* - * Even if our slab creation failed, another thread might have succeeded - * in growing the cache. - */ - empty = mem_cache_empty(cache); - - pthread_mutex_unlock(&cache->lock); - - return !empty; -} - -static void -mem_cache_reap(struct mem_cache *cache) -{ - struct mem_slab *slab; - struct list dead_slabs; - - list_init(&dead_slabs); - - pthread_mutex_lock(&cache->lock); - - while (!list_empty(&cache->free_slabs)) { - slab = list_first_entry(&cache->free_slabs, struct mem_slab, list_node); - list_remove(&slab->list_node); - list_insert(&dead_slabs, &slab->list_node); - cache->nr_bufs -= cache->bufs_per_slab; - cache->nr_slabs--; - cache->nr_free_slabs--; - } - - pthread_mutex_unlock(&cache->lock); - - while (!list_empty(&dead_slabs)) { - slab = list_first_entry(&dead_slabs, struct mem_slab, list_node); - list_remove(&slab->list_node); - mem_slab_destroy(slab, cache); - } -} - -void -mem_cache_destroy(struct mem_cache *cache) -{ - struct mem_cpu_pool *cpu_pool; - void **ptr; - size_t i; - - pthread_mutex_lock(&mem_cache_list_lock); - list_remove(&cache->node); - pthread_mutex_unlock(&mem_cache_list_lock); - - for (i = 0; i < ARRAY_SIZE(cache->cpu_pools); i++) { - cpu_pool = &cache->cpu_pools[i]; - - pthread_mutex_lock(&cpu_pool->lock); - - if (cpu_pool->array == NULL) { - pthread_mutex_unlock(&cpu_pool->lock); - continue; - } - - pthread_mutex_lock(&cache->lock); - - for (ptr = cpu_pool->array + cpu_pool->nr_objs - 1; - ptr >= cpu_pool->array; - ptr--) - mem_cache_free_to_slab(cache, *ptr); - - pthread_mutex_unlock(&cache->lock); - - ptr = cpu_pool->array; - cpu_pool->size = 0; - cpu_pool->nr_objs = 0; - cpu_pool->array = NULL; - pthread_mutex_unlock(&cpu_pool->lock); - - mem_cache_free(cache->cpu_pool_type->array_cache, ptr); - } - - mem_cache_reap(cache); - -#ifndef NDEBUG - if (cache->nr_objs != 0) - mem_warn("'%s' not empty", cache->name); - else { - assert(list_empty(&cache->partial_slabs)); - assert(list_empty(&cache->free_slabs)); - assert(avltree_empty(&cache->active_slabs)); - assert(cache->nr_bufs == 0); - assert(cache->nr_slabs == 0); - } -#endif /* NDEBUG */ - - pthread_mutex_destroy(&cache->lock); - - for (i = 0; i < ARRAY_SIZE(cache->cpu_pools); i++) - pthread_mutex_destroy(&cache->cpu_pools[i].lock); - - mem_cache_free(&mem_cache_cache, cache); -} - -/* - * Allocate a raw (unconstructed) buffer from the slab layer of a cache. - * - * The cache must be locked before calling this function. - */ -static void * -mem_cache_alloc_from_slab(struct mem_cache *cache) -{ - struct mem_slab *slab; - union mem_bufctl *bufctl; - - if (!list_empty(&cache->partial_slabs)) - slab = list_first_entry(&cache->partial_slabs, struct mem_slab, - list_node); - else if (!list_empty(&cache->free_slabs)) - slab = list_first_entry(&cache->free_slabs, struct mem_slab, list_node); - else - return NULL; - - bufctl = slab->first_free; - assert(bufctl != NULL); - slab->first_free = bufctl->next; - slab->nr_refs++; - cache->nr_objs++; - - /* - * The slab has become complete. - */ - if (slab->nr_refs == cache->bufs_per_slab) { - list_remove(&slab->list_node); - - if (slab->nr_refs == 1) - cache->nr_free_slabs--; - } else if (slab->nr_refs == 1) { - /* - * The slab has become partial. - */ - list_remove(&slab->list_node); - list_insert_tail(&cache->partial_slabs, &slab->list_node); - cache->nr_free_slabs--; - } else if (!list_singular(&cache->partial_slabs)) { - struct list *node; - struct mem_slab *tmp; - - /* - * The slab remains partial. If there are more than one partial slabs, - * maintain the list sorted. - */ - - assert(slab->nr_refs > 1); - - for (node = list_prev(&slab->list_node); - !list_end(&cache->partial_slabs, node); - node = list_prev(node)) { - tmp = list_entry(node, struct mem_slab, list_node); - - if (tmp->nr_refs >= slab->nr_refs) - break; - } - - /* - * If the direct neighbor was found, the list is already sorted. - * If no slab was found, the slab is inserted at the head of the list. - */ - if (node != list_prev(&slab->list_node)) { - list_remove(&slab->list_node); - list_insert_after(node, &slab->list_node); - } - } - - if ((slab->nr_refs == 1) && mem_slab_use_tree(cache->flags)) - avltree_insert(&cache->active_slabs, &slab->tree_node, - mem_slab_cmp_insert); - - return mem_bufctl_to_buf(bufctl, cache); -} - -/* - * Release a buffer to the slab layer of a cache. - * - * The cache must be locked before calling this function. - */ -static void -mem_cache_free_to_slab(struct mem_cache *cache, void *buf) -{ - struct mem_slab *slab; - union mem_bufctl *bufctl; - - if (cache->flags & MEM_CF_DIRECT) { - assert(cache->slab_size == PAGE_SIZE); - slab = (struct mem_slab *)P2END((unsigned long)buf, cache->slab_size) - - 1; - } else { - struct avltree_node *node; - - node = avltree_lookup_nearest(&cache->active_slabs, buf, - mem_slab_cmp_lookup, AVLTREE_LEFT); - assert(node != NULL); - slab = avltree_entry(node, struct mem_slab, tree_node); - assert((unsigned long)buf < (P2ALIGN((unsigned long)slab->addr - + cache->slab_size, PAGE_SIZE))); - } - - assert(slab->nr_refs >= 1); - assert(slab->nr_refs <= cache->bufs_per_slab); - bufctl = mem_buf_to_bufctl(buf, cache); - bufctl->next = slab->first_free; - slab->first_free = bufctl; - slab->nr_refs--; - cache->nr_objs--; - - /* - * The slab has become free. - */ - if (slab->nr_refs == 0) { - if (mem_slab_use_tree(cache->flags)) - avltree_remove(&cache->active_slabs, &slab->tree_node); - - /* - * The slab was partial. - */ - if (cache->bufs_per_slab > 1) - list_remove(&slab->list_node); - - list_insert_tail(&cache->free_slabs, &slab->list_node); - cache->nr_free_slabs++; - } else if (slab->nr_refs == (cache->bufs_per_slab - 1)) { - /* - * The slab has become partial. - */ - list_insert(&cache->partial_slabs, &slab->list_node); - } else if (!list_singular(&cache->partial_slabs)) { - struct list *node; - struct mem_slab *tmp; - - /* - * The slab remains partial. If there are more than one partial slabs, - * maintain the list sorted. - */ - - assert(slab->nr_refs > 0); - - for (node = list_next(&slab->list_node); - !list_end(&cache->partial_slabs, node); - node = list_next(node)) { - tmp = list_entry(node, struct mem_slab, list_node); - - if (tmp->nr_refs <= slab->nr_refs) - break; - } - - /* - * If the direct neighbor was found, the list is already sorted. - * If no slab was found, the slab is inserted at the tail of the list. - */ - if (node != list_next(&slab->list_node)) { - list_remove(&slab->list_node); - list_insert_before(node, &slab->list_node); - } - } -} - -static void -mem_cache_alloc_verify(struct mem_cache *cache, void *buf, int construct) -{ - struct mem_buftag *buftag; - union mem_bufctl *bufctl; - void *addr; - - buftag = mem_buf_to_buftag(buf, cache); - - if (buftag->state != MEM_BUFTAG_FREE) - mem_cache_error(cache, buf, MEM_ERR_BUFTAG, buftag); - - addr = mem_buf_verify_fill(buf, MEM_FREE_PATTERN, MEM_UNINIT_PATTERN, - cache->bufctl_dist); - - if (addr != NULL) - mem_cache_error(cache, buf, MEM_ERR_MODIFIED, addr); - - addr = buf + cache->obj_size; - memset(addr, MEM_REDZONE_BYTE, cache->redzone_pad); - - bufctl = mem_buf_to_bufctl(buf, cache); - bufctl->redzone = MEM_REDZONE_WORD; - buftag->state = MEM_BUFTAG_ALLOC; - - if (construct && (cache->ctor != NULL)) - cache->ctor(buf); -} - -void * -mem_cache_alloc(struct mem_cache *cache) -{ - struct mem_cpu_pool *cpu_pool; - int filled; - void *buf; - - cpu_pool = mem_cpu_pool_get(cache); - - pthread_mutex_lock(&cpu_pool->lock); - -fast_alloc_retry: - if (likely(cpu_pool->nr_objs > 0)) { - buf = mem_cpu_pool_pop(cpu_pool); - pthread_mutex_unlock(&cpu_pool->lock); - - if (cpu_pool->flags & MEM_CF_VERIFY) - mem_cache_alloc_verify(cache, buf, MEM_AV_CONSTRUCT); - - return buf; - } - - if (cpu_pool->array != NULL) { - filled = mem_cpu_pool_fill(cpu_pool, cache); - - if (!filled) { - pthread_mutex_unlock(&cpu_pool->lock); - - filled = mem_cache_grow(cache); - - if (!filled) - return NULL; - - pthread_mutex_lock(&cpu_pool->lock); - } - - goto fast_alloc_retry; - } - - pthread_mutex_unlock(&cpu_pool->lock); - -slow_alloc_retry: - pthread_mutex_lock(&cache->lock); - buf = mem_cache_alloc_from_slab(cache); - pthread_mutex_unlock(&cache->lock); - - if (buf == NULL) { - filled = mem_cache_grow(cache); - - if (!filled) - return NULL; - - goto slow_alloc_retry; - } - - if (cache->flags & MEM_CF_VERIFY) - mem_cache_alloc_verify(cache, buf, MEM_AV_NOCONSTRUCT); - - if (cache->ctor != NULL) - cache->ctor(buf); - - return buf; -} - -static void -mem_cache_free_verify(struct mem_cache *cache, void *buf) -{ - struct avltree_node *node; - struct mem_buftag *buftag; - struct mem_slab *slab; - union mem_bufctl *bufctl; - unsigned char *redzone_byte; - unsigned long slabend; - - pthread_mutex_lock(&cache->lock); - node = avltree_lookup_nearest(&cache->active_slabs, buf, - mem_slab_cmp_lookup, AVLTREE_LEFT); - pthread_mutex_unlock(&cache->lock); - - if (node == NULL) - mem_cache_error(cache, buf, MEM_ERR_INVALID, NULL); - - slab = avltree_entry(node, struct mem_slab, tree_node); - slabend = P2ALIGN((unsigned long)slab->addr + cache->slab_size, PAGE_SIZE); - - if ((unsigned long)buf >= slabend) - mem_cache_error(cache, buf, MEM_ERR_INVALID, NULL); - - if ((((unsigned long)buf - (unsigned long)slab->addr) % cache->buf_size) - != 0) - mem_cache_error(cache, buf, MEM_ERR_INVALID, NULL); - - /* - * As the buffer address is valid, accessing its buftag is safe. - */ - buftag = mem_buf_to_buftag(buf, cache); - - if (buftag->state != MEM_BUFTAG_ALLOC) { - if (buftag->state == MEM_BUFTAG_FREE) - mem_cache_error(cache, buf, MEM_ERR_DOUBLEFREE, NULL); - else - mem_cache_error(cache, buf, MEM_ERR_BUFTAG, buftag); - } - - redzone_byte = buf + cache->obj_size; - bufctl = mem_buf_to_bufctl(buf, cache); - - while (redzone_byte < (unsigned char *)bufctl) { - if (*redzone_byte != MEM_REDZONE_BYTE) - mem_cache_error(cache, buf, MEM_ERR_REDZONE, redzone_byte); - - redzone_byte++; - } - - if (bufctl->redzone != MEM_REDZONE_WORD) { - unsigned long word; - - word = MEM_REDZONE_WORD; - redzone_byte = mem_buf_verify_bytes(&bufctl->redzone, &word, - sizeof(bufctl->redzone)); - mem_cache_error(cache, buf, MEM_ERR_REDZONE, redzone_byte); - } - - mem_buf_fill(buf, MEM_FREE_PATTERN, cache->bufctl_dist); - buftag->state = MEM_BUFTAG_FREE; -} - -void -mem_cache_free(struct mem_cache *cache, void *obj) -{ - struct mem_cpu_pool *cpu_pool; - void **array; - - cpu_pool = mem_cpu_pool_get(cache); - - if (cpu_pool->flags & MEM_CF_VERIFY) - mem_cache_free_verify(cache, obj); - - pthread_mutex_lock(&cpu_pool->lock); - -fast_free_retry: - if (likely(cpu_pool->nr_objs < cpu_pool->size)) { - mem_cpu_pool_push(cpu_pool, obj); - pthread_mutex_unlock(&cpu_pool->lock); - return; - } - - if (cpu_pool->array != NULL) { - mem_cpu_pool_drain(cpu_pool, cache); - goto fast_free_retry; - } - - pthread_mutex_unlock(&cpu_pool->lock); - - array = mem_cache_alloc(cache->cpu_pool_type->array_cache); - - if (array != NULL) { - pthread_mutex_lock(&cpu_pool->lock); - - /* - * Another thread may have built the CPU pool while the lock was - * dropped. - */ - if (cpu_pool->array != NULL) { - pthread_mutex_unlock(&cpu_pool->lock); - mem_cache_free(cache->cpu_pool_type->array_cache, array); - pthread_mutex_lock(&cpu_pool->lock); - goto fast_free_retry; - } - - mem_cpu_pool_build(cpu_pool, cache, array); - goto fast_free_retry; - } - - pthread_mutex_lock(&cache->lock); - mem_cache_free_to_slab(cache, obj); - pthread_mutex_unlock(&cache->lock); -} - -void -mem_cache_info(struct mem_cache *cache) -{ - struct mem_cache *cache_stats; - char flags_str[64]; - - if (cache == NULL) { - pthread_mutex_lock(&mem_cache_list_lock); - - list_for_each_entry(&mem_cache_list, cache, node) - mem_cache_info(cache); - - pthread_mutex_unlock(&mem_cache_list_lock); - - return; - } - - cache_stats = mem_alloc(sizeof(*cache_stats)); - - if (cache_stats == NULL) { - mem_warn("unable to allocate memory for cache stats"); - return; - } - - pthread_mutex_lock(&cache->lock); - cache_stats->flags = cache->flags; - cache_stats->obj_size = cache->obj_size; - cache_stats->align = cache->align; - cache_stats->buf_size = cache->buf_size; - cache_stats->bufctl_dist = cache->bufctl_dist; - cache_stats->slab_size = cache->slab_size; - cache_stats->color_max = cache->color_max; - cache_stats->bufs_per_slab = cache->bufs_per_slab; - cache_stats->nr_objs = cache->nr_objs; - cache_stats->nr_bufs = cache->nr_bufs; - cache_stats->nr_slabs = cache->nr_slabs; - cache_stats->nr_free_slabs = cache->nr_free_slabs; - strcpy(cache_stats->name, cache->name); - cache_stats->buftag_dist = cache->buftag_dist; - cache_stats->redzone_pad = cache->redzone_pad; - cache_stats->cpu_pool_type = cache->cpu_pool_type; - pthread_mutex_unlock(&cache->lock); - - snprintf(flags_str, sizeof(flags_str), "%s%s%s", - (cache_stats->flags & MEM_CF_DIRECT) ? " DIRECT" : "", - (cache_stats->flags & MEM_CF_SLAB_EXTERNAL) ? " SLAB_EXTERNAL" : "", - (cache_stats->flags & MEM_CF_VERIFY) ? " VERIFY" : ""); - - mem_print("name: %s", cache_stats->name); - mem_print("flags: 0x%x%s", cache_stats->flags, flags_str); - mem_print("obj_size: %zu", cache_stats->obj_size); - mem_print("align: %zu", cache_stats->align); - mem_print("buf_size: %zu", cache_stats->buf_size); - mem_print("bufctl_dist: %zu", cache_stats->bufctl_dist); - mem_print("slab_size: %zu", cache_stats->slab_size); - mem_print("color_max: %zu", cache_stats->color_max); - mem_print("bufs_per_slab: %lu", cache_stats->bufs_per_slab); - mem_print("nr_objs: %lu", cache_stats->nr_objs); - mem_print("nr_bufs: %lu", cache_stats->nr_bufs); - mem_print("nr_slabs: %lu", cache_stats->nr_slabs); - mem_print("nr_free_slabs: %lu", cache_stats->nr_free_slabs); - mem_print("buftag_dist: %zu", cache_stats->buftag_dist); - mem_print("redzone_pad: %zu", cache_stats->redzone_pad); - mem_print("cpu_pool_size: %d", cache_stats->cpu_pool_type->array_size); - mem_print("--"); - - mem_free(cache_stats, sizeof(*cache_stats)); -} - -static void * -mem_gc(void *arg) -{ - struct mem_cache *cache; - struct timespec ts; - int error; - - (void)arg; - - clock_gettime(CLOCK_MONOTONIC, &ts); - - for (;;) { - ts.tv_sec += MEM_GC_INTERVAL; - - do - error = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &ts, NULL); - while (error == EINTR); - - /* - * EINTR is the only expected error. - */ - assert(error == 0); - -#if 0 - mem_info(); - -#if CONFIG_MEM_USE_PHYS - phys_info(); -#endif /* CONFIG_MEM_USE_PHYS */ -#endif - - pthread_mutex_lock(&mem_cache_list_lock); - - list_for_each_entry(&mem_cache_list, cache, node) - mem_cache_reap(cache); - - pthread_mutex_unlock(&mem_cache_list_lock); - } - - return NULL; -} - -void -mem_setup(void) -{ - static int mem_initialized = 0; - struct mem_cpu_pool_type *cpu_pool_type; - char name[MEM_NAME_SIZE]; - pthread_t thread; - size_t i, size; - int error; - - if (mem_initialized) - return; - - mem_initialized = 1; - - _pagesize = sysconf(_SC_PAGESIZE); - assert(ISP2(_pagesize)); - - /* - * Make sure a bufctl can always be stored in a buffer. - */ - assert(sizeof(union mem_bufctl) <= MEM_ALIGN_MIN); - -#if CONFIG_MEM_USE_PHYS - phys_setup(); -#endif /* CONFIG_MEM_USE_PHYS */ - - list_init(&mem_cache_list); - pthread_mutex_init(&mem_cache_list_lock, NULL); - - for (i = 0; i < ARRAY_SIZE(mem_cpu_pool_types); i++) { - cpu_pool_type = &mem_cpu_pool_types[i]; - cpu_pool_type->array_cache = &mem_cpu_array_caches[i]; - sprintf(name, "mem_cpu_array_%d", cpu_pool_type->array_size); - size = sizeof(void *) * cpu_pool_type->array_size; - mem_cache_init(cpu_pool_type->array_cache, name, size, - cpu_pool_type->array_align, NULL, NULL, 0); - } - - /* - * Prevent off slab data for the slab cache to avoid infinite recursion. - */ - mem_cache_init(&mem_slab_cache, "mem_slab", sizeof(struct mem_slab), - 0, NULL, NULL, MEM_CREATE_INTERNAL); - mem_cache_init(&mem_cache_cache, "mem_cache", sizeof(struct mem_cache), - CPU_L1_SIZE, NULL, NULL, 0); - - size = 1 << MEM_CACHES_FIRST_SHIFT; - - for (i = 0; i < ARRAY_SIZE(mem_caches); i++) { - sprintf(name, "mem_%zu", size); - mem_cache_init(&mem_caches[i], name, size, 0, NULL, NULL, 0); - size <<= 1; - } - - error = pthread_create(&thread, NULL, mem_gc, NULL); - - if (error) - mem_error("unable to create garbage collection thread: %s", - strerror(error)); -} - -/* - * Return the mem cache index matching the given allocation size, which - * must be strictly greater than 0. - */ -static inline size_t -mem_get_index(size_t size) -{ - assert(size != 0); - - size = (size - 1) >> MEM_CACHES_FIRST_SHIFT; - - if (size == 0) - return 0; - else - return (sizeof(long) * CHAR_BIT) - __builtin_clzl(size); -} - -static void -mem_alloc_verify(struct mem_cache *cache, void *buf, size_t size) -{ - size_t redzone_size; - void *redzone; - - assert(size <= cache->obj_size); - - redzone = buf + size; - redzone_size = cache->obj_size - size; - memset(redzone, MEM_REDZONE_BYTE, redzone_size); -} - -void * -mem_alloc(size_t size) -{ - size_t index; - void *buf; - - if (size == 0) - return NULL; - - index = mem_get_index(size); - - if (index < ARRAY_SIZE(mem_caches)) { - struct mem_cache *cache; - - cache = &mem_caches[index]; - buf = mem_cache_alloc(cache); - - if ((buf != NULL) && (cache->flags & MEM_CF_VERIFY)) - mem_alloc_verify(cache, buf, size); - } else { - buf = mem_default_alloc(size); - } - - return buf; -} - -void * -mem_zalloc(size_t size) -{ - void *ptr; - - ptr = mem_alloc(size); - - if (ptr == NULL) - return NULL; - - memset(ptr, 0, size); - return ptr; -} - -static void -mem_free_verify(struct mem_cache *cache, void *buf, size_t size) -{ - unsigned char *redzone_byte, *redzone_end; - - assert(size <= cache->obj_size); - - redzone_byte = buf + size; - redzone_end = buf + cache->obj_size; - - while (redzone_byte < redzone_end) { - if (*redzone_byte != MEM_REDZONE_BYTE) - mem_cache_error(cache, buf, MEM_ERR_REDZONE, redzone_byte); - - redzone_byte++; - } -} - -void -mem_free(void *ptr, size_t size) -{ - size_t index; - - if ((ptr == NULL) || (size == 0)) - return; - - index = mem_get_index(size); - - if (index < ARRAY_SIZE(mem_caches)) { - struct mem_cache *cache; - - cache = &mem_caches[index]; - - if (cache->flags & MEM_CF_VERIFY) - mem_free_verify(cache, ptr, size); - - mem_cache_free(cache, ptr); - } else { - mem_default_free(ptr, size); - } -} - -void -mem_info(void) -{ - struct mem_cache *cache, *cache_stats; - size_t mem_usage, mem_reclaimable; - - cache_stats = mem_alloc(sizeof(*cache_stats)); - - if (cache_stats == NULL) { - mem_warn("unable to allocate memory for cache stats"); - return; - } - - mem_print("-- cache obj slab bufs objs bufs " - " total reclaimable"); - mem_print("-- name size size /slab usage count " - " memory memory"); - - pthread_mutex_lock(&mem_cache_list_lock); - - list_for_each_entry(&mem_cache_list, cache, node) { - pthread_mutex_lock(&cache->lock); - cache_stats->obj_size = cache->obj_size; - cache_stats->slab_size = cache->slab_size; - cache_stats->bufs_per_slab = cache->bufs_per_slab; - cache_stats->nr_objs = cache->nr_objs; - cache_stats->nr_bufs = cache->nr_bufs; - cache_stats->nr_slabs = cache->nr_slabs; - cache_stats->nr_free_slabs = cache->nr_free_slabs; - strcpy(cache_stats->name, cache->name); - pthread_mutex_unlock(&cache->lock); - - mem_usage = (cache_stats->nr_slabs * cache_stats->slab_size) >> 10; - mem_reclaimable = - (cache_stats->nr_free_slabs * cache_stats->slab_size) >> 10; - - mem_print("%-27s %6zu %3zuk %4lu %6lu %6lu %7zuk %10zuk", - cache_stats->name, cache_stats->obj_size, - cache_stats->slab_size >> 10, cache_stats->bufs_per_slab, - cache_stats->nr_objs, cache_stats->nr_bufs, mem_usage, - mem_reclaimable); - } - - pthread_mutex_unlock(&mem_cache_list_lock); - - mem_free(cache_stats, sizeof(*cache_stats)); -} @@ -1,120 +0,0 @@ -/* - * Copyright (c) 2010, 2011 Richard Braun. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * - * Object caching and general purpose memory allocator. - */ - -#ifndef _MEM_H -#define _MEM_H - -#include <stddef.h> - -/* - * Backend source of memory for a cache. - */ -struct mem_source { - void * (*alloc_fn)(size_t); - void (*free_fn)(void *, size_t); -}; - -/* - * Object cache opaque declaration. - */ -struct mem_cache; - -/* - * Type for constructor functions. - * - * The pre-constructed state of an object is supposed to include only - * elements such as e.g. linked lists, locks, reference counters. Therefore - * constructors are expected to 1) never fail and 2) not need any - * user-provided data. The first constraint implies that object construction - * never performs dynamic resource allocation, which also means there is no - * need for destructors. - */ -typedef void (*mem_cache_ctor_t)(void *); - -/* - * Cache creation flags. - */ -#define MEM_CACHE_VERIFY 0x1 /* Use debugging facilities */ - -/* - * Create a cache. - */ -struct mem_cache * mem_cache_create(const char *name, size_t obj_size, - size_t align, mem_cache_ctor_t ctor, - const struct mem_source *source, int flags); - -/* - * Destroy a cache. - */ -void mem_cache_destroy(struct mem_cache *cache); - -/* - * Allocate an object from a cache. - */ -void * mem_cache_alloc(struct mem_cache *cache); - -/* - * Release an object to its cache. - */ -void mem_cache_free(struct mem_cache *cache, void *obj); - -/* - * Display internal cache stats on stderr. - * - * If cache is NULL, this function displays all managed caches. - */ -void mem_cache_info(struct mem_cache *cache); - -/* - * Set up the memory allocator module. - */ -void mem_setup(void); - -/* - * Allocate size bytes of uninitialized memory. - */ -void * mem_alloc(size_t size); - -/* - * Allocate size bytes of zeroed memory. - */ -void * mem_zalloc(size_t size); - -/* - * Release memory obtained with mem_alloc() or mem_zalloc(). - * - * The size argument must strictly match the value given at allocation time. - */ -void mem_free(void *ptr, size_t size); - -/* - * Display global memory information on stderr. - */ -void mem_info(void); - -#endif /* _MEM_H */ diff --git a/mem_malloc.c b/mem_malloc.c deleted file mode 100644 index 2f25e0b..0000000 --- a/mem_malloc.c +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright (c) 2010 Richard Braun. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * - * Libc-compatible malloc() functions implementation. - * - * Keep in mind this code is mainly used for tests. Also, initialization is - * not thread-safe. - */ - -#include <errno.h> -#include <stddef.h> -#include <string.h> - -#include "mem.h" -#include "macros.h" -#include "mem_malloc.h" - -struct btag { - void *addr; - size_t size; -} __aligned(8); - -void * -malloc(size_t size) -{ - struct btag *btag; - - mem_setup(); - - size += sizeof(*btag); - btag = mem_alloc(size); - - if (btag == NULL) { - errno = ENOMEM; - return NULL; - } - - btag->addr = btag; - btag->size = size; - - return btag + 1; -} - -void * -calloc(size_t nmemb, size_t size) -{ - size_t bytes; - void *buf; - - mem_setup(); - - bytes = nmemb * size; - buf = malloc(bytes); - - if (buf == NULL) - return NULL; - - memset(buf, 0, bytes); - - return buf; -} - -void * -realloc(void *ptr, size_t size) -{ - struct btag *btag; - size_t old_size; - char *buf; - - mem_setup(); - - if (ptr == NULL) - return malloc(size); - else if (size == 0) { - free(ptr); - return NULL; - } - - buf = malloc(size); - - if (buf == NULL) - return NULL; - - btag = (struct btag *)ptr - 1; - old_size = btag->size - sizeof(*btag); - memcpy(buf, ptr, MIN(old_size, size)); - mem_free(btag->addr, btag->size); - - return buf; -} - -int -posix_memalign(void **ptr, size_t align, size_t size) -{ - struct btag *btag; - char *buf; - - mem_setup(); - - if (!ISP2(align)) - return EINVAL; - - size += sizeof(*btag); - size = P2ROUND(size, align * 2); - buf = mem_alloc(size); - - if (buf == NULL) - return ENOMEM; - - btag = (struct btag *)P2ROUND((unsigned long)buf + sizeof(*btag), align) - - 1; - btag->addr = buf; - btag->size = size; - - *ptr = btag + 1; - return 0; -} - -void -free(void *ptr) -{ - struct btag *btag; - - mem_setup(); - - if (ptr == NULL) - return; - - btag = (struct btag *)ptr - 1; - mem_free(btag->addr, btag->size); -} diff --git a/mem_malloc.h b/mem_malloc.h deleted file mode 100644 index 6921c32..0000000 --- a/mem_malloc.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2010 Richard Braun. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * - * Libc-compatible malloc() functions. - * - * This code is messy, for testing purposes only, and disabled by default. - */ - -#ifndef _MEM_MALLOC_H -#define _MEM_MALLOC_H - -#include <stddef.h> - -void * malloc(size_t size); -void * calloc(size_t nmemb, size_t size); -void * realloc(void *ptr, size_t size); -int posix_memalign(void **ptr, size_t align, size_t size); -void free(void *ptr); - -#endif /* _MEM_MALLOC_H */ @@ -1,777 +0,0 @@ -/* - * Copyright (c) 2011 Richard Braun. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * - * Page allocator. - * - * This implementation uses the binary buddy system to manage its heap. - * Descriptions of the buddy system can be found in the following works : - * - "UNIX Internals: The New Frontiers", by Uresh Vahalia. - * - "Dynamic Storage Allocation: A Survey and Critical Review", - * by Paul R. Wilson, Mark S. Johnstone, Michael Neely, and David Boles. - * - * In addition, this allocator uses per-cpu pools of pages for level 0 - * (i.e. single page) allocations. These pools act as caches (but are named - * differently to avoid confusion with CPU caches) that reduce contention on - * multiprocessor systems. When a pool is empty and cannot provide a page, - * it is filled by transferring multiple pages from the backend buddy system. - * The symmetric case is handled likewise. - */ - -#include <sched.h> -#include <stdio.h> -#include <assert.h> -#include <limits.h> -#include <stddef.h> -#include <string.h> -#include <unistd.h> -#include <pthread.h> -#include <sys/mman.h> - -#include "cpu.h" -#include "list.h" -#include "phys.h" -#include "error.h" -#include "macros.h" - -/* - * The system page size. - * - * This macro actually expands to a global variable that is set on - * initialization. - */ -#define PAGE_SIZE ((unsigned long)_pagesize) - -/* - * Maximum number of segments. - */ -#define PHYS_MAX_SEGMENTS 2 - -/* - * Segment boundaries. - */ -#define PHYS_ISADMA_LIMIT 0x1000000 -#define PHYS_NORMAL_LIMIT 0x30000000 - -/* - * Number of segment lists. - */ -#define PHYS_NR_SEG_LISTS 2 - -/* - * Segment list priorities. - * - * Higher priorities have lower numerical values. - */ -#define PHYS_SEGLIST_NORMAL 1 -#define PHYS_SEGLIST_ISADMA 0 - -/* - * Number of free block lists per segment. - */ -#define PHYS_NR_FREE_LISTS 11 - -/* - * The size of a CPU pool is computed by dividing the number of pages in its - * containing segment by this value. - */ -#define PHYS_CPU_POOL_RATIO 1024 - -/* - * Maximum number of pages in a CPU pool. - */ -#define PHYS_CPU_POOL_MAX_SIZE 128 - -/* - * The transfer size of a CPU pool is computed by dividing the pool size by - * this value. - */ -#define PHYS_CPU_POOL_TRANSFER_RATIO 2 - -/* - * Per-processor cache of pages. - */ -struct phys_cpu_pool { - pthread_mutex_t lock; - int size; - int transfer_size; - int nr_pages; - struct list pages; -} __aligned(CPU_L1_SIZE); - -/* - * Special level value. - * - * When a page is free, its level is the index of its free list. - */ -#define PHYS_LEVEL_ALLOCATED PHYS_NR_FREE_LISTS - -/* - * Doubly-linked list of free blocks. - */ -struct phys_free_list { - unsigned long size; - struct list blocks; -}; - -/* - * Segment name buffer size. - */ -#define PHYS_NAME_SIZE 16 - -/* - * Segment of contiguous memory. - */ -struct phys_seg { - struct phys_cpu_pool cpu_pools[NR_CPUS]; - - struct list node; - phys_paddr_t start; - phys_paddr_t end; - struct phys_page *pages; - struct phys_page *pages_end; - pthread_mutex_t lock; - struct phys_free_list free_lists[PHYS_NR_FREE_LISTS]; - unsigned long nr_free_pages; - char name[PHYS_NAME_SIZE]; -}; - -/* - * See PAGE_SIZE. - */ -static long _pagesize; - -/* - * Segment lists, ordered by priority (higher priority lists have lower - * numerical priorities). - */ -static struct list phys_seg_lists[PHYS_NR_SEG_LISTS]; - -/* - * Segment table. - */ -static struct phys_seg phys_segs[PHYS_MAX_SEGMENTS]; - -/* - * Number of loaded segments. - */ -static unsigned int phys_segs_size; - -/* - * Page/address conversion macros. - */ -#define phys_atop(addr) ((addr) / PAGE_SIZE) -#define phys_ptoa(pfn) ((pfn) * PAGE_SIZE) - -static void -phys_page_init(struct phys_page *page, struct phys_seg *seg, phys_paddr_t pa) -{ - page->seg = seg; - page->phys_addr = pa; - page->level = PHYS_LEVEL_ALLOCATED; -} - -static inline struct phys_page * -phys_page_lookup(phys_paddr_t pa) -{ - struct phys_seg *seg; - unsigned int i; - - for (i = 0; i < phys_segs_size; i++) { - seg = &phys_segs[i]; - - if ((pa >= seg->start) && (pa < seg->end)) - return &seg->pages[phys_atop(pa - seg->start)]; - } - - return NULL; -} - -static void -phys_free_list_init(struct phys_free_list *free_list) -{ - free_list->size = 0; - list_init(&free_list->blocks); -} - -static inline void -phys_free_list_insert(struct phys_free_list *free_list, struct phys_page *page) -{ - assert(page->level == PHYS_LEVEL_ALLOCATED); - - free_list->size++; - list_insert(&free_list->blocks, &page->node); -} - -static inline void -phys_free_list_remove(struct phys_free_list *free_list, struct phys_page *page) -{ - assert(free_list->size != 0); - assert(!list_empty(&free_list->blocks)); - assert(page->level < PHYS_NR_FREE_LISTS); - - free_list->size--; - list_remove(&page->node); -} - -static struct phys_page * -phys_seg_alloc_from_buddy(struct phys_seg *seg, unsigned int level) -{ - struct phys_free_list *free_list; - struct phys_page *page, *buddy; - unsigned int i; - - assert(level < PHYS_NR_FREE_LISTS); - - for (i = level; i < PHYS_NR_FREE_LISTS; i++) { - free_list = &seg->free_lists[i]; - - if (free_list->size != 0) - break; - } - - if (i == PHYS_NR_FREE_LISTS) - return NULL; - - page = list_first_entry(&free_list->blocks, struct phys_page, node); - phys_free_list_remove(free_list, page); - page->level = PHYS_LEVEL_ALLOCATED; - - while (i > level) { - i--; - buddy = &page[1 << i]; - phys_free_list_insert(&seg->free_lists[i], buddy); - buddy->level = i; - } - - seg->nr_free_pages -= (1 << level); - return page; -} - -static void -phys_seg_free_to_buddy(struct phys_seg *seg, struct phys_page *page, - unsigned int level) -{ - struct phys_page *buddy; - phys_paddr_t pa, buddy_pa; - unsigned int nr_pages; - - assert(page >= seg->pages); - assert(page < seg->pages_end); - assert(page->level == PHYS_LEVEL_ALLOCATED); - assert(level < PHYS_NR_FREE_LISTS); - - nr_pages = (1 << level); - pa = page->phys_addr; - - while (level < (PHYS_NR_FREE_LISTS - 1)) { - buddy_pa = pa ^ phys_ptoa(1 << level); - - if ((buddy_pa < seg->start) || (buddy_pa >= seg->end)) - break; - - buddy = &seg->pages[phys_atop(buddy_pa - seg->start)]; - - if (buddy->level != level) - break; - - phys_free_list_remove(&seg->free_lists[level], buddy); - buddy->level = PHYS_LEVEL_ALLOCATED; - level++; - pa &= -phys_ptoa(1 << level); - page = &seg->pages[phys_atop(pa - seg->start)]; - } - - phys_free_list_insert(&seg->free_lists[level], page); - page->level = level; - seg->nr_free_pages += nr_pages; -} - -static void -phys_cpu_pool_init(struct phys_cpu_pool *cpu_pool, int size) -{ - pthread_mutex_init(&cpu_pool->lock, NULL); - cpu_pool->size = size; - cpu_pool->transfer_size = (size + PHYS_CPU_POOL_TRANSFER_RATIO - 1) - / PHYS_CPU_POOL_TRANSFER_RATIO; - cpu_pool->nr_pages = 0; - list_init(&cpu_pool->pages); -} - -/* - * Return a CPU pool. - * - * This function will generally return the pool matching the CPU running the - * calling thread. Because of context switches and thread migration, the - * caller might be running on another processor after this function returns. - * Although not optimal, this should rarely happen, and it doesn't affect the - * allocator operations in any other way, as CPU pools are always valid, and - * their access is serialized by a lock. - */ -static inline struct phys_cpu_pool * -phys_cpu_pool_get(struct phys_seg *seg) -{ - return &seg->cpu_pools[cpu_id()]; -} - -static inline struct phys_page * -phys_cpu_pool_pop(struct phys_cpu_pool *cpu_pool) -{ - struct phys_page *page; - - assert(cpu_pool->nr_pages != 0); - cpu_pool->nr_pages--; - page = list_first_entry(&cpu_pool->pages, struct phys_page, node); - list_remove(&page->node); - return page; -} - -static inline void -phys_cpu_pool_push(struct phys_cpu_pool *cpu_pool, struct phys_page *page) -{ - assert(cpu_pool->nr_pages < cpu_pool->size); - cpu_pool->nr_pages++; - list_insert(&cpu_pool->pages, &page->node); -} - -static int -phys_cpu_pool_fill(struct phys_cpu_pool *cpu_pool, struct phys_seg *seg) -{ - struct phys_page *page; - int i; - - assert(cpu_pool->nr_pages == 0); - - pthread_mutex_lock(&seg->lock); - - for (i = 0; i < cpu_pool->transfer_size; i++) { - page = phys_seg_alloc_from_buddy(seg, 0); - - if (page == NULL) - break; - - phys_cpu_pool_push(cpu_pool, page); - } - - pthread_mutex_unlock(&seg->lock); - - return i; -} - -static void -phys_cpu_pool_drain(struct phys_cpu_pool *cpu_pool, struct phys_seg *seg) -{ - struct phys_page *page; - int i; - - assert(cpu_pool->nr_pages == cpu_pool->size); - - pthread_mutex_lock(&seg->lock); - - for (i = cpu_pool->transfer_size; i > 0; i--) { - page = phys_cpu_pool_pop(cpu_pool); - phys_seg_free_to_buddy(seg, page, 0); - } - - pthread_mutex_unlock(&seg->lock); -} - -static inline phys_paddr_t -phys_seg_start(struct phys_seg *seg) -{ - return seg->start; -} - -static inline phys_paddr_t -phys_seg_end(struct phys_seg *seg) -{ - return seg->end; -} - -static inline phys_paddr_t -phys_seg_size(struct phys_seg *seg) -{ - return phys_seg_end(seg) - phys_seg_start(seg); -} - -static int -phys_seg_compute_pool_size(struct phys_seg *seg) -{ - phys_paddr_t size; - - size = phys_atop(phys_seg_size(seg)) / PHYS_CPU_POOL_RATIO; - - if (size == 0) - size = 1; - else if (size > PHYS_CPU_POOL_MAX_SIZE) - size = PHYS_CPU_POOL_MAX_SIZE; - - return size; -} - -static void -phys_seg_init(struct phys_seg *seg, struct phys_page *pages) -{ - phys_paddr_t pa; - int pool_size; - unsigned int i; - - pool_size = phys_seg_compute_pool_size(seg); - - for (i = 0; i < ARRAY_SIZE(seg->cpu_pools); i++) - phys_cpu_pool_init(&seg->cpu_pools[i], pool_size); - - seg->pages = pages; - seg->pages_end = pages + phys_atop(phys_seg_size(seg)); - pthread_mutex_init(&seg->lock, NULL); - - for (i = 0; i < ARRAY_SIZE(seg->free_lists); i++) - phys_free_list_init(&seg->free_lists[i]); - - seg->nr_free_pages = 0; - - for (pa = phys_seg_start(seg); pa < phys_seg_end(seg); pa += PAGE_SIZE) - phys_page_init(&pages[phys_atop(pa - phys_seg_start(seg))], seg, pa); -} - -/* - * Return the level (i.e. the index in the free lists array) matching the - * given size. - */ -static inline unsigned int -phys_get_level(phys_size_t size) -{ - size = P2ROUND(size, PAGE_SIZE) / PAGE_SIZE; - assert(size != 0); - size--; - - if (size == 0) - return 0; - else - return (sizeof(size) * CHAR_BIT) - __builtin_clzl(size); -} - -static struct phys_page * -phys_seg_alloc(struct phys_seg *seg, phys_size_t size) -{ - struct phys_cpu_pool *cpu_pool; - struct phys_page *page; - unsigned int level; - int filled; - - level = phys_get_level(size); - - if (level == 0) { - cpu_pool = phys_cpu_pool_get(seg); - - pthread_mutex_lock(&cpu_pool->lock); - - if (cpu_pool->nr_pages == 0) { - filled = phys_cpu_pool_fill(cpu_pool, seg); - - if (!filled) { - pthread_mutex_unlock(&cpu_pool->lock); - return NULL; - } - } - - page = phys_cpu_pool_pop(cpu_pool); - pthread_mutex_unlock(&cpu_pool->lock); - } else { - pthread_mutex_lock(&seg->lock); - page = phys_seg_alloc_from_buddy(seg, level); - pthread_mutex_unlock(&seg->lock); - } - - return page; -} - -static void -phys_seg_free(struct phys_seg *seg, struct phys_page *page, phys_size_t size) -{ - struct phys_cpu_pool *cpu_pool; - unsigned int level; - - level = phys_get_level(size); - - if (level == 0) { - cpu_pool = phys_cpu_pool_get(seg); - - pthread_mutex_lock(&cpu_pool->lock); - - if (cpu_pool->nr_pages == cpu_pool->size) - phys_cpu_pool_drain(cpu_pool, seg); - - phys_cpu_pool_push(cpu_pool, page); - pthread_mutex_unlock(&cpu_pool->lock); - } else { - pthread_mutex_lock(&seg->lock); - phys_seg_free_to_buddy(seg, page, level); - pthread_mutex_unlock(&seg->lock); - } -} - -/* - * Load memory during initialization. - * - * This function partially initializes a segment. - */ -static void -phys_load_segment(const char *name, phys_paddr_t start, phys_paddr_t end, - unsigned int seg_list_prio) -{ - static int initialized = 0; - struct phys_seg *seg; - struct list *seg_list; - unsigned int i; - - assert(name != NULL); - assert(start < end); - assert(seg_list_prio < ARRAY_SIZE(phys_seg_lists)); - - if (!initialized) { - for (i = 0; i < ARRAY_SIZE(phys_seg_lists); i++) - list_init(&phys_seg_lists[i]); - - phys_segs_size = 0; - initialized = 1; - } - - if (phys_segs_size >= ARRAY_SIZE(phys_segs)) - error_die(ERR_NORES); - - seg_list = &phys_seg_lists[seg_list_prio]; - seg = &phys_segs[phys_segs_size]; - - list_insert_tail(seg_list, &seg->node); - seg->start = start; - seg->end = end; - strncpy(seg->name, name, PHYS_NAME_SIZE); - seg->name[sizeof(seg->name) - 1] = '\0'; - - phys_segs_size++; -} - -/* - * Loading segments is normally done by architecture-specific code. In - * this implementation, an Intel machine with two segments of RAM is - * virtualized. - */ -static void -phys_load_segments(void) -{ - phys_paddr_t start, end; - size_t size; - void *addr; - - /* - * Load the ISADMA segment. - */ - size = PHYS_ISADMA_LIMIT; - addr = mmap(NULL, size, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - - if (addr == MAP_FAILED) - error_die(ERR_NOMEM); - - start = (phys_paddr_t)addr; - end = start + size; - phys_load_segment("isadma", start, end, PHYS_SEGLIST_ISADMA); - - /* - * Load the normal segment. - */ - size = PHYS_NORMAL_LIMIT - PHYS_ISADMA_LIMIT; - addr = mmap(NULL, size, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - - if (addr == MAP_FAILED) - error_die(ERR_NOMEM); - - start = (phys_paddr_t)addr; - end = start + size; - phys_load_segment("normal", start, end, PHYS_SEGLIST_NORMAL); -} - -void -phys_setup(void) -{ - struct phys_seg *seg, *map_seg; - struct phys_page *page, *map; - struct list *seg_list; - phys_paddr_t map_size; - unsigned int i; - - _pagesize = sysconf(_SC_PAGESIZE); - assert(ISP2(_pagesize)); - - phys_load_segments(); - - /* - * Compute the memory map size. - */ - map_size = 0; - - for (i = 0; i < phys_segs_size; i++) - map_size += phys_atop(phys_seg_size(&phys_segs[i])); - - map_size = P2ROUND(map_size * sizeof(struct phys_page), PAGE_SIZE); - - /* - * Find a segment from which to allocate the memory map. - */ - for (seg_list = &phys_seg_lists[ARRAY_SIZE(phys_seg_lists) - 1]; - seg_list >= phys_seg_lists; - seg_list--) - list_for_each_entry(seg_list, map_seg, node) - if (map_size <= phys_seg_size(map_seg)) - goto found; - - error_die(ERR_NOMEM); - -found: - /* - * Allocate the memory map. - */ - map = (struct phys_page *)phys_seg_start(map_seg); - - /* - * Initialize the segments, associating them to the memory map. When - * the segments are initialized, all their pages are set allocated, - * with a block size of one (level 0). They are then released, which - * populates the free lists. - */ - for (i = 0; i < phys_segs_size; i++) { - seg = &phys_segs[i]; - phys_seg_init(seg, map); - - /* - * Don't release the memory map pages. - * - * XXX The memory map pages normally don't need descriptors, as they - * are never released. This implementation however can be used in - * cases where some memory is reserved at the start of all segments. - * In order not to require descriptors for the memory map, the segment - * where the map resides should be split. As it is quite cumbersome, - * no effort is made here to avoid wasting descriptors and the pages - * containing them. - */ - if (seg == map_seg) - page = seg->pages + phys_atop(map_size); - else - page = seg->pages; - - while (page < seg->pages_end) { - phys_seg_free_to_buddy(seg, page, 0); - page++; - } - - map += phys_atop(phys_seg_size(seg)); - } -} - -struct phys_page * -phys_alloc_pages(phys_size_t size) -{ - struct list *seg_list; - struct phys_seg *seg; - struct phys_page *page; - - for (seg_list = &phys_seg_lists[ARRAY_SIZE(phys_seg_lists) - 1]; - seg_list >= phys_seg_lists; - seg_list--) - list_for_each_entry(seg_list, seg, node) { - page = phys_seg_alloc(seg, size); - - if (page != NULL) - return page; - } - - return NULL; -} - -void -phys_free_pages(struct phys_page *page, phys_size_t size) -{ - phys_seg_free(page->seg, page, size); -} - -phys_paddr_t -phys_alloc(phys_size_t size) -{ - struct phys_page *page; - - page = phys_alloc_pages(size); - - /* - * XXX Rely on the system to never provide a virtual memory area - * starting at 0. - */ - if (page == NULL) - return 0; - - return page->phys_addr; -} - -void -phys_free(phys_paddr_t pa, phys_size_t size) -{ - struct phys_page *page; - - page = phys_page_lookup(pa); - assert(page != NULL); - phys_free_pages(page, size); -} - -void -phys_info(void) -{ - struct phys_seg *seg; - unsigned int i, j; - char name[16]; - - printf(" name"); - - for (i = 0; i < PHYS_NR_FREE_LISTS; i++) { - snprintf(name, sizeof(name), "#%u", (1 << i)); - printf(" %5s", name); - } - - printf("\n"); - - for (i = 0; i < phys_segs_size; i++) { - seg = &phys_segs[i]; - - printf("%8s", seg->name); - - pthread_mutex_lock(&seg->lock); - - for (j = 0; j < ARRAY_SIZE(seg->free_lists); j++) - printf(" %5lu", seg->free_lists[j].size); - - pthread_mutex_unlock(&seg->lock); - - printf("\n"); - } -} @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2011 Richard Braun. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * - * Page allocator. - */ - -#ifndef _PHYS_H -#define _PHYS_H - -#include "list.h" - -/* - * Physical address. - */ -typedef unsigned long phys_paddr_t; - -/* - * Memory range size. - */ -typedef unsigned long phys_size_t; - -/* - * Page descriptor. - */ -struct phys_page { - struct list node; - struct phys_seg *seg; - phys_paddr_t phys_addr; - unsigned int level; -}; - -void phys_setup(void); - -struct phys_page * phys_alloc_pages(phys_size_t size); - -void phys_free_pages(struct phys_page *page, phys_size_t size); - -phys_paddr_t phys_alloc(phys_size_t size); - -void phys_free(phys_paddr_t pa, phys_size_t size); - -void phys_info(void); - -#endif /* _PHYS_H */ diff --git a/test/test_mem.c b/test/test_mem.c deleted file mode 100644 index eb7a56c..0000000 --- a/test/test_mem.c +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2010 Richard Braun. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <stdio.h> -#include <string.h> - -#include "../mem.h" -#include "../macros.h" - -#define STRING "This is a test string." -#define STRING_SIZE (STRLEN(STRING) + 1) - -int -main(int argc, char *argv[]) -{ - char *s; - - (void)argc; - (void)argv; - - mem_setup(); - - s = mem_alloc(STRING_SIZE); - - if (s == NULL) { - fprintf(stderr, "unable to allocate memory\n"); - return 1; - } - - strcpy(s, STRING); - printf("string: '%s'\n", s); - mem_free(s, STRING_SIZE); - - return 0; -} diff --git a/test/test_mem_cache.c b/test/test_mem_cache.c deleted file mode 100644 index eaa043a..0000000 --- a/test/test_mem_cache.c +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright (c) 2010, 2011 Richard Braun. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <assert.h> -#include <stddef.h> -#include <pthread.h> - -#include <stdio.h> - -#include "../cpu.h" -#include "../mem.c" -#include "../error.c" -#include "../avltree.c" - -#if CONFIG_MEM_USE_PHYS -#include "../phys.c" -#endif /* CONFIG_MEM_USE_PHYS */ - -#define NTHREADS 4 -#define TEST_TIME 60 -#define OBJSPERLOOP 100 - -struct result { - unsigned long allocs; - unsigned long frees; -} __aligned(CPU_L1_SIZE); - -struct obj { - unsigned long nr_refs; - char name[16]; -}; - -static void -obj_ctor(void *ptr) -{ - struct obj *obj; - - obj = ptr; - obj->nr_refs = 0; -} - -static struct mem_cache *obj_cache; -static volatile int work; -static struct result results[NTHREADS]; - -static void * -run(void *arg) -{ - struct obj *objs[OBJSPERLOOP]; - struct result *result; - int i, id; - - result = arg; - id = result - results; - - mem_print("started"); - - while (work) { - for (i = 0; i < OBJSPERLOOP; i++) { - objs[i] = mem_cache_alloc(obj_cache); - result->allocs++; - } - - for (i = 0; i < OBJSPERLOOP; i++) { - mem_cache_free(obj_cache, objs[i]); - result->frees++; - } - } - - return NULL; -} - -int -main(int argc, char *argv[]) -{ - pthread_t threads[NTHREADS]; - unsigned long ops; - int i; - - (void)argc; - (void)argv; - - mem_setup(); - - mem_info(); - - mem_print("Selected cache line size: %u", CPU_L1_SIZE); - mem_print("sizeof(pthread_mutex_t): %zu", sizeof(pthread_mutex_t)); - mem_print("sizeof(struct mem_cpu_pool): %zu", sizeof(struct mem_cpu_pool)); - mem_print("sizeof(union mem_bufctl): %zu", sizeof(union mem_bufctl)); - mem_print("sizeof(struct mem_buftag): %zu", sizeof(struct mem_buftag)); - mem_print("sizeof(struct mem_slab): %zu", sizeof(struct mem_slab)); - mem_print("sizeof(struct mem_cache): %zu", sizeof(struct mem_cache)); - mem_print("sizeof(struct obj): %zu", sizeof(struct obj)); - - obj_cache = mem_cache_create("obj", sizeof(struct obj), 0, - obj_ctor, NULL, 0); - - memset(results, 0, sizeof(results)); - work = 1; - - for (i = 0; i < NTHREADS; i++) - pthread_create(&threads[i], NULL, run, &results[i]); - - sleep(TEST_TIME); - work = 0; - - for (i = 0; i < NTHREADS; i++) - pthread_join(threads[i], NULL); - - ops = 0; - - for (i = 0; i < NTHREADS; i++) - ops += results[i].allocs + results[i].frees; - - mem_info(); - mem_cache_info(obj_cache); - mem_print("total: %lu ops in %d secs", ops, TEST_TIME); - - mem_cache_destroy(obj_cache); - - return 0; -} diff --git a/test/test_mem_cache_double_free.c b/test/test_mem_cache_double_free.c deleted file mode 100644 index de356a3..0000000 --- a/test/test_mem_cache_double_free.c +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (c) 2010 Richard Braun. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <stdio.h> -#include <assert.h> -#include <string.h> - -#include "../mem.h" -#include "../macros.h" - -struct obj { - unsigned long nr_refs; - char name[16]; -}; - -static void -obj_ctor(void *ptr) -{ - struct obj *obj; - - obj = ptr; - obj->nr_refs = 0; -} - -int -main(int argc, char *argv[]) -{ - struct mem_cache *obj_cache; - struct obj *obj; - - (void)argc; - (void)argv; - - mem_setup(); - - obj_cache = mem_cache_create("obj", sizeof(struct obj), 0, - obj_ctor, NULL, MEM_CACHE_VERIFY); - - printf("trying normal alloc+free:\n"); - obj = mem_cache_alloc(obj_cache); - mem_cache_free(obj_cache, obj); - - mem_cache_info(obj_cache); - - printf("trying alloc+double free:\n"); - obj = mem_cache_alloc(obj_cache); - mem_cache_free(obj_cache, obj); - mem_cache_free(obj_cache, obj); - - printf("done\n"); - - return 0; -} diff --git a/test/test_mem_cache_invalid_free.c b/test/test_mem_cache_invalid_free.c deleted file mode 100644 index 767ee6b..0000000 --- a/test/test_mem_cache_invalid_free.c +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c) 2010 Richard Braun. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <stdio.h> -#include <assert.h> -#include <string.h> - -#include "../mem.h" -#include "../macros.h" - -struct obj { - unsigned long nr_refs; - char name[16]; -}; - -static void -obj_ctor(void *ptr) -{ - struct obj *obj; - - obj = ptr; - obj->nr_refs = 0; -} - -int -main(int argc, char *argv[]) -{ - struct mem_cache *obj_cache; - struct obj *obj; - - (void)argc; - (void)argv; - - mem_setup(); - - obj_cache = mem_cache_create("obj", sizeof(struct obj), 0, - obj_ctor, NULL, MEM_CACHE_VERIFY); - - printf("trying normal alloc+free:\n"); - obj = mem_cache_alloc(obj_cache); - mem_cache_free(obj_cache, obj); - - mem_cache_info(obj_cache); - - printf("trying alloc+invalid free:\n"); - obj = mem_cache_alloc(obj_cache); - mem_cache_free(obj_cache, (char *)obj + 1); - - printf("done\n"); - - return 0; -} diff --git a/test/test_mem_cache_write_beyond.c b/test/test_mem_cache_write_beyond.c deleted file mode 100644 index 05bea2e..0000000 --- a/test/test_mem_cache_write_beyond.c +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (c) 2010 Richard Braun. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <stdio.h> -#include <assert.h> -#include <string.h> - -#include "../mem.h" -#include "../macros.h" - -struct obj { - unsigned long nr_refs; - char name[16]; -}; - -static void -obj_ctor(void *ptr) -{ - struct obj *obj; - - obj = ptr; - obj->nr_refs = 0; -} - -static void -obj_print(struct obj *obj, size_t size) -{ - unsigned char *ptr, *end; - - printf("buffer content: "); - - for (ptr = (unsigned char *)obj, end = ptr + size; ptr < end; ptr++) - printf("%02x", *ptr); - - printf("\n"); -} - -int -main(int argc, char *argv[]) -{ - struct mem_cache *obj_cache; - struct obj *obj; - size_t buf_size; - - (void)argc; - (void)argv; - - mem_setup(); - - obj_cache = mem_cache_create("obj", sizeof(struct obj), 0, - obj_ctor, NULL, MEM_CACHE_VERIFY); - mem_cache_info(obj_cache); - - printf("doing normal alloc:\n"); - obj = mem_cache_alloc(obj_cache); - - printf("writing beyond object boundary:\n"); - strcpy(obj->name, "invalid write 000000"); - buf_size = P2ROUND(sizeof(*obj), 8); - obj_print(obj, buf_size + (sizeof(unsigned long) * 2)); - - printf("doing normal free:\n"); - mem_cache_free(obj_cache, obj); - - printf("done\n"); - - return 0; -} diff --git a/test/test_mem_cache_write_buftag.c b/test/test_mem_cache_write_buftag.c deleted file mode 100644 index 539d399..0000000 --- a/test/test_mem_cache_write_buftag.c +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (c) 2010 Richard Braun. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <stdio.h> -#include <assert.h> -#include <string.h> - -#include "../mem.h" -#include "../macros.h" - -struct obj { - unsigned long nr_refs; - char name[16]; -}; - -static void -obj_ctor(void *ptr) -{ - struct obj *obj; - - obj = ptr; - obj->nr_refs = 0; -} - -static void -obj_print(struct obj *obj, size_t size) -{ - unsigned char *ptr, *end; - - printf("buffer content: "); - - for (ptr = (unsigned char *)obj, end = ptr + size; ptr < end; ptr++) - printf("%02x", *ptr); - - printf("\n"); -} - -int -main(int argc, char *argv[]) -{ - struct mem_cache *obj_cache; - struct obj *obj; - unsigned long *ptr; - size_t buf_size; - - (void)argc; - (void)argv; - - mem_setup(); - - obj_cache = mem_cache_create("obj", sizeof(struct obj), 0, - obj_ctor, NULL, MEM_CACHE_VERIFY); - mem_cache_info(obj_cache); - - printf("doing normal alloc:\n"); - obj = mem_cache_alloc(obj_cache); - - printf("writing garbage in buftag:\n"); - buf_size = P2ROUND(sizeof(*obj), 8); - ptr = (void *)obj + buf_size + sizeof(unsigned long); - *ptr = 0xed0000a1; - obj_print(obj, buf_size + (sizeof(unsigned long) * 2)); - - printf("doing normal free:\n"); - mem_cache_free(obj_cache, obj); - - printf("done\n"); - - return 0; -} diff --git a/test/test_mem_cache_write_free.c b/test/test_mem_cache_write_free.c deleted file mode 100644 index fd1c9db..0000000 --- a/test/test_mem_cache_write_free.c +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (c) 2010 Richard Braun. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#define _GNU_SOURCE -#include <sched.h> - -#include <stdio.h> -#include <assert.h> -#include <string.h> - -#include "../mem.h" -#include "../macros.h" - -struct obj { - unsigned long nr_refs; - char name[16]; -}; - -static void -obj_ctor(void *ptr) -{ - struct obj *obj; - - obj = ptr; - obj->nr_refs = 0; -} - -static void -obj_print(struct obj *obj) -{ - unsigned char *ptr, *end; - - printf("buffer content: "); - - for (ptr = (unsigned char *)obj, end = ptr + sizeof(*obj); - ptr < end; - ptr++) - printf("%02x", *ptr); - - printf("\n"); -} - -int -main(int argc, char *argv[]) -{ - struct mem_cache *obj_cache; - struct obj *obj; - cpu_set_t cpu_set; - - (void)argc; - (void)argv; - - printf("binding to CPU 0\n"); - CPU_ZERO(&cpu_set); - CPU_SET(0, &cpu_set); - sched_setaffinity(0, sizeof(cpu_set), &cpu_set); - - mem_setup(); - - obj_cache = mem_cache_create("obj", sizeof(struct obj), 0, - obj_ctor, NULL, MEM_CACHE_VERIFY); - - printf("doing normal alloc+free:\n"); - obj = mem_cache_alloc(obj_cache); - mem_cache_free(obj_cache, obj); - - mem_cache_info(obj_cache); - - obj_print(obj); - - printf("doing write on free object:\n"); - memset((void *)obj + 3, 0x89, 5); - - obj_print(obj); - - printf("trying normal alloc+free on same CPU (should fail):\n"); - - obj = mem_cache_alloc(obj_cache); - mem_cache_free(obj_cache, obj); - - printf("done\n"); - - return 0; -} diff --git a/test/test_mem_offbyone.c b/test/test_mem_offbyone.c deleted file mode 100644 index b9e7d75..0000000 --- a/test/test_mem_offbyone.c +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2010 Richard Braun. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <stdio.h> -#include <string.h> - -#include "../mem.h" - -#define BUFSIZE 200 - -int -main(int argc, char *argv[]) -{ - char *s; - - (void)argc; - (void)argv; - - mem_setup(); - - printf("allocating memory:\n"); - s = mem_alloc(BUFSIZE); - - if (s == NULL) { - fprintf(stderr, "unable to allocate memory\n"); - return 1; - } - - printf("writing beyond end of buffer:\n"); - memset(s, 'a', BUFSIZE + 1); - - printf("releasing buffer, should fail if CONFIG_MEM_VERIFY defined\n"); - mem_free(s, BUFSIZE); - - return 0; -} diff --git a/test/test_phys.c b/test/test_phys.c deleted file mode 100644 index 5ded8d5..0000000 --- a/test/test_phys.c +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2011 Richard Braun. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <stdio.h> -#include <stddef.h> -#include <string.h> - -#include "../error.c" -#include "../phys.c" - -int -main(int argc, char *argv[]) -{ - struct phys_page *page; - - (void)argc; - (void)argv; - - phys_setup(); - - phys_info(); - printf("sizeof(struct phys_cpu_pool) = %zu\n", - sizeof(struct phys_cpu_pool)); - printf("sizeof(struct phys_free_list) = %zu\n", - sizeof(struct phys_free_list)); - printf("sizeof(struct phys_page): %zu\n", sizeof(struct phys_page)); - printf("sizeof(struct phys_seg): %zu\n", sizeof(struct phys_seg)); - printf("allocating two pages\n"); - page = phys_alloc_pages(PAGE_SIZE * 2); - - if (page == NULL) { - fprintf(stderr, "unable to allocate memory\n"); - return 1; - } - - phys_info(); - - printf("freeing the allocated pages\n"); - phys_free_pages(page, PAGE_SIZE * 2); - phys_info(); - - return 0; -} diff --git a/test/test_xprintf.c b/test/test_xprintf.c deleted file mode 100644 index ff157b8..0000000 --- a/test/test_xprintf.c +++ /dev/null @@ -1,298 +0,0 @@ -/* - * Copyright (c) 2010 Richard Braun. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <stdio.h> -#include <assert.h> -#include <stdarg.h> -#include <string.h> - -#include "../macros.h" -#include "../xprintf.h" - -#define TEST_PRINTF(format, ...) \ -MACRO_BEGIN \ - char stra[256], strb[256]; \ - int la, lb; \ - la = snprintf(stra, 256, format, ## __VA_ARGS__); \ - printf(" printf: %s", stra); \ - lb = xsnprintf(strb, 256, format, ## __VA_ARGS__); \ - xprintf("xprintf: %s", stra); \ - assert(la == lb); \ - assert(strcmp(stra, strb) == 0); \ -MACRO_END - -int -main(int argc, char *argv[]) -{ - (void)argc; - (void)argv; - -#define FORMAT "%c" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 'a'); -#undef FORMAT - -#define FORMAT "%8c" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 'a'); -#undef FORMAT - -#define FORMAT "%-8c" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 'a'); -#undef FORMAT - -#define FORMAT "%.s" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, "12345"); -#undef FORMAT - -#define FORMAT "%.3s" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, "12345"); -#undef FORMAT - -#define FORMAT "%4.3s" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, "12345"); -#undef FORMAT - -#define FORMAT "%-4.3s" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, "12345"); -#undef FORMAT - -#define FORMAT "%3.4s" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, "12345"); -#undef FORMAT - -#define FORMAT "%-3.4s" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, "12345"); -#undef FORMAT - -#define FORMAT "%#o" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 123); -#undef FORMAT - -#define FORMAT "%#x" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 123); -#undef FORMAT - -#define FORMAT "%#X" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 123); -#undef FORMAT - -#define FORMAT "%08d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 123); -#undef FORMAT - -#define FORMAT "%-8d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 123); -#undef FORMAT - -#define FORMAT "%-08d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 123); -#undef FORMAT - -#define FORMAT "%0-8d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 123); -#undef FORMAT - -#define FORMAT "% d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 123); -#undef FORMAT - -#define FORMAT "%+d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 123); -#undef FORMAT - -#define FORMAT "%+ d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 123); -#undef FORMAT - -#define FORMAT "%12d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 123); -#undef FORMAT - -#define FORMAT "%*d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 12, 123); -#undef FORMAT - -#define FORMAT "%.12d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 123); -#undef FORMAT - -#define FORMAT "%.012d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 123); -#undef FORMAT - -#define FORMAT "%.*d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 12, 123); -#undef FORMAT - -#define FORMAT "%.d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 123); -#undef FORMAT - -#define FORMAT "%.*d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, -12, 123); -#undef FORMAT - -#define FORMAT "%.4d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 123); -#undef FORMAT - -#define FORMAT "%5.4d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 123); -#undef FORMAT - -#define FORMAT "%4.5d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 123); -#undef FORMAT - -#define FORMAT "%d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 0); -#undef FORMAT - -#define FORMAT "%.0d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 0); -#undef FORMAT - -#define FORMAT "%.0o" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 0); -#undef FORMAT - -#define FORMAT "%.0x" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 0); -#undef FORMAT - -#define FORMAT "%1.0d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 0); -#undef FORMAT - -#define FORMAT "%08.0d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, -123); -#undef FORMAT - -#define FORMAT "%08d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, -123); -#undef FORMAT - -#define FORMAT "%08d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 123); -#undef FORMAT - -#define FORMAT "%8d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 123); -#undef FORMAT - -#define FORMAT "%8d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, -123); -#undef FORMAT - -#define FORMAT "%.8d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, -123); -#undef FORMAT - -#define FORMAT "%.80d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, -123); -#undef FORMAT - -#define FORMAT "%-80d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, -123); -#undef FORMAT - -#define FORMAT "%80d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, -123); -#undef FORMAT - -#define FORMAT "%80.40d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, -123); -#undef FORMAT - -#define FORMAT "%-+80.40d" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 123); -#undef FORMAT - -#define FORMAT "%+x" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, -123); -#undef FORMAT - -#define FORMAT "%#x" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 123); -#undef FORMAT - -#define FORMAT "%#o" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 123); -#undef FORMAT - -#define FORMAT "%p" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, "123"); -#undef FORMAT - -#define FORMAT "%#lx" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 0xdeadbeefL); -#undef FORMAT - -#define FORMAT "%zd" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, (size_t)-123); -#undef FORMAT - -#define FORMAT "%#llx" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 0xdeadbeefbadcafeLL); -#undef FORMAT - -#define FORMAT "%llo" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT, 0xffffffffffffffffLL); -#undef FORMAT - -#define FORMAT "%%" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT); -#undef FORMAT - -#define FORMAT "%y" - TEST_PRINTF("%s: '" FORMAT "'\n", FORMAT); -#undef FORMAT - - char stra[10], strb[10]; - int la, lb; - la = snprintf(stra, sizeof(stra), "%s", "123456789a"); - printf(" printf: %s\n", stra); - lb = xsnprintf(strb, sizeof(strb), "%s", "123456789a"); - xprintf("xprintf: %s\n", stra); - assert(la == lb); - assert(strncmp(stra, strb, 10) == 0); - -#define FORMAT "12%n3%#08x4%n5" - int lc, ld; - sprintf(stra, FORMAT, &la, 123, &lb); - printf(" printf: la: %d, lb: %d\n", la, lb); - xsprintf(strb, FORMAT, &lc, 123, &ld); - xprintf("xprintf: lc: %d, ld: %d\n", lc, ld); - assert(la == lc); - assert(lb == ld); -#undef FORMAT - - la = snprintf(NULL, 0, "%s", "123"); - printf(" printf: %d\n", la); - lb = xsnprintf(NULL, 0, "%s", "123"); - xprintf("xprintf: %d\n", lb); - assert(la == lb); - - return 0; -} diff --git a/xprintf.c b/xprintf.c deleted file mode 100644 index 7efb786..0000000 --- a/xprintf.c +++ /dev/null @@ -1,591 +0,0 @@ -/* - * Copyright (c) 2010 Richard Braun. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <stdio.h> -#include <limits.h> -#include <stdarg.h> -#include <stddef.h> -#include <unistd.h> -#include <pthread.h> - -#include "xprintf.h" - -/* - * Formatting flags. - * - * FORMAT_LOWER must be 0x20 as it is OR'd with digits, eg. - * '0': 0x30 | 0x20 => 0x30 ('0') - * 'A': 0x41 | 0x20 => 0x61 ('a') - */ -#define FORMAT_ALT_FORM 0x01 /* "Alternate form" */ -#define FORMAT_ZERO_PAD 0x02 /* Zero padding on the left */ -#define FORMAT_LEFT_JUSTIFY 0x04 /* Align text on the left */ -#define FORMAT_BLANK 0x08 /* Blank space before positive number */ -#define FORMAT_SIGN 0x10 /* Always place a sign (either + or -) */ -#define FORMAT_LOWER 0x20 /* To lowercase (for %x) */ -#define FORMAT_CONV_SIGNED 0x40 /* Format specifies signed conversion */ - -enum { - MODIFIER_NONE, - MODIFIER_CHAR, - MODIFIER_SHORT, - MODIFIER_LONG, - MODIFIER_LONGLONG, - MODIFIER_PTR, /* Used only for %p */ - MODIFIER_SIZE, - MODIFIER_PTRDIFF -}; - -enum { - SPECIFIER_INVALID, - SPECIFIER_INT, - SPECIFIER_CHAR, - SPECIFIER_STR, - SPECIFIER_NRCHARS, - SPECIFIER_PERCENT -}; - -/* - * Size for the temporary number buffer. The minimum base is 8 so 3 bits - * are consumed per digit. Add one to round up. The conversion algorithm - * doesn't use the null byte. - */ -#define MAX_NUM_SIZE (((sizeof(unsigned long long) * CHAR_BIT) / 3) + 1) - -/* - * Special size for xvsnprintf(), used by xsprintf()/xvsprintf() when the - * buffer size is unknown. - */ -#define XPRINT_NOLIMIT ((size_t)-1) - -/* - * Size of the static buffer used by xprintf()/xvprintf(). - */ -#define XPRINT_BUFSIZE 1024 - -static char xprint_buffer[XPRINT_BUFSIZE]; -static pthread_mutex_t xprint_mutex = PTHREAD_MUTEX_INITIALIZER; - -static const char digits[] = "0123456789ABCDEF"; - -static inline char * -xputchar(char *str, char *end, char c) -{ - if (str < end) - *str = c; - - str++; - - return str; -} - -static inline int -xisdigit(char c) -{ - return (c >= '0') && (c <= '9'); -} - -int -xprintf(const char *format, ...) -{ - va_list ap; - int length; - - va_start(ap, format); - length = xvprintf(format, ap); - va_end(ap); - - return length; -} - -int -xvprintf(const char *format, va_list ap) -{ - size_t size; - int length; - - pthread_mutex_lock(&xprint_mutex); - length = xvsnprintf(xprint_buffer, sizeof(xprint_buffer), format, ap); - size = ((unsigned int)length >= sizeof(xprint_buffer)) - ? sizeof(xprint_buffer) - 1 - : (unsigned int)length; - fwrite(xprint_buffer, 1, size, stdout); - pthread_mutex_unlock(&xprint_mutex); - - return length; -} - -int -xsprintf(char *str, const char *format, ...) -{ - va_list ap; - int length; - - va_start(ap, format); - length = xvsprintf(str, format, ap); - va_end(ap); - - return length; -} - -int -xvsprintf(char *str, const char *format, va_list ap) -{ - return xvsnprintf(str, XPRINT_NOLIMIT, format, ap); -} - -int -xsnprintf(char *str, size_t size, const char *format, ...) -{ - va_list ap; - int length; - - va_start(ap, format); - length = xvsnprintf(str, size, format, ap); - va_end(ap); - - return length; -} - -int -xvsnprintf(char *str, size_t size, const char *format, va_list ap) -{ - unsigned long long n; - int i, len, found, flags, width, precision, modifier, specifier, shift; - unsigned char r, base, mask; - char c, *s, *start, *end, sign, tmp[MAX_NUM_SIZE]; - - start = str; - - if (size == 0) - end = NULL; - else if (size == XPRINT_NOLIMIT) - end = (char *)-1; - else - end = start + size - 1; - - while ((c = *format) != '\0') { - if (c != '%') { - str = xputchar(str, end, c); - format++; - continue; - } - - /* Flags */ - - found = 1; - flags = 0; - - do { - format++; - c = *format; - - switch (c) { - case '#': - flags |= FORMAT_ALT_FORM; - break; - case '0': - flags |= FORMAT_ZERO_PAD; - break; - case '-': - flags |= FORMAT_LEFT_JUSTIFY; - break; - case ' ': - flags |= FORMAT_BLANK; - break; - case '+': - flags |= FORMAT_SIGN; - break; - default: - found = 0; - break; - } - } while (found); - - /* Width */ - - if (xisdigit(c)) { - width = 0; - - while (xisdigit(c)) { - width = width * 10 + (c - '0'); - format++; - c = *format; - } - } else if (c == '*') { - width = va_arg(ap, int); - - if (width < 0) { - flags |= FORMAT_LEFT_JUSTIFY; - width = -width; - } - - format++; - c = *format; - } else { - width = 0; - } - - /* Precision */ - - if (c == '.') { - format++; - c = *format; - - if (xisdigit(c)) { - precision = 0; - - while (xisdigit(c)) { - precision = precision * 10 + (c - '0'); - format++; - c = *format; - } - } else if (c == '*') { - precision = va_arg(ap, int); - - if (precision < 0) - precision = 0; - - format++; - c = *format; - } else { - precision = 0; - } - } else { - /* precision is >= 0 only if explicit */ - precision = -1; - } - - /* Length modifier */ - - switch (c) { - case 'h': - case 'l': - format++; - - if (c == *format) { - modifier = (c == 'h') ? MODIFIER_CHAR : MODIFIER_LONGLONG; - goto skip_modifier; - } else { - modifier = (c == 'h') ? MODIFIER_SHORT : MODIFIER_LONG; - c = *format; - } - - break; - case 'z': - modifier = MODIFIER_SIZE; - goto skip_modifier; - case 't': - modifier = MODIFIER_PTRDIFF; -skip_modifier: - format++; - c = *format; - break; - default: - modifier = MODIFIER_NONE; - break; - } - - /* Specifier */ - - switch (c) { - case 'd': - case 'i': - flags |= FORMAT_CONV_SIGNED; - case 'u': - base = 10; - goto integer; - case 'o': - base = 8; - goto integer; - case 'p': - flags |= FORMAT_ALT_FORM; - modifier = MODIFIER_PTR; - case 'x': - flags |= FORMAT_LOWER; - case 'X': - base = 16; -integer: - specifier = SPECIFIER_INT; - break; - case 'c': - specifier = SPECIFIER_CHAR; - break; - case 's': - specifier = SPECIFIER_STR; - break; - case 'n': - specifier = SPECIFIER_NRCHARS; - break; - case '%': - specifier = SPECIFIER_PERCENT; - break; - default: - specifier = SPECIFIER_INVALID; - break; - } - - /* Output */ - - switch (specifier) { - case SPECIFIER_INT: - switch (modifier) { - case MODIFIER_CHAR: - if (flags & FORMAT_CONV_SIGNED) - n = (signed char)va_arg(ap, int); - else - n = (unsigned char)va_arg(ap, int); - break; - case MODIFIER_SHORT: - if (flags & FORMAT_CONV_SIGNED) - n = (short)va_arg(ap, int); - else - n = (unsigned short)va_arg(ap, int); - break; - case MODIFIER_LONG: - if (flags & FORMAT_CONV_SIGNED) - n = va_arg(ap, long); - else - n = va_arg(ap, unsigned long); - break; - case MODIFIER_LONGLONG: - if (flags & FORMAT_CONV_SIGNED) - n = va_arg(ap, long long); - else - n = va_arg(ap, unsigned long long); - break; - case MODIFIER_PTR: - n = (unsigned long)va_arg(ap, void *); - break; - case MODIFIER_SIZE: - if (flags & FORMAT_CONV_SIGNED) - n = va_arg(ap, ssize_t); - else - n = va_arg(ap, size_t); - break; - case MODIFIER_PTRDIFF: - n = va_arg(ap, ptrdiff_t); - break; - default: - if (flags & FORMAT_CONV_SIGNED) - n = va_arg(ap, int); - else - n = va_arg(ap, unsigned int); - break; - } - - if ((flags & FORMAT_LEFT_JUSTIFY) || (precision >= 0)) - flags &= ~FORMAT_ZERO_PAD; - - sign = 0; - - if (flags & FORMAT_ALT_FORM) { - /* '0' for octal */ - width--; - - /* '0x' or '0X' for hexadecimal */ - if (base == 16) - width--; - } else if (flags & FORMAT_CONV_SIGNED) { - if ((long long)n < 0) { - sign = '-'; - width--; - n = -(long long)n; - } else if (flags & FORMAT_SIGN) { - /* FORMAT_SIGN must precede FORMAT_BLANK. */ - sign = '+'; - width--; - } else if (flags & FORMAT_BLANK) { - sign = ' '; - width--; - } - } - - /* Conversion, in reverse order */ - - i = 0; - - if (n == 0) { - if (precision != 0) - tmp[i++] = '0'; - } else if (base == 10) { - /* - * Try to avoid 64 bits operations if the processor doesn't - * support them. Note that even when using modulus and - * division operators close to each other, the compiler may - * forge two calls to __udivdi3() and __umoddi3() instead of - * one to __udivmoddi3(), whereas processor instructions are - * generally correctly used once, giving both the remainder - * and the quotient, through plain or reciprocal division. - */ -#ifndef __LP64__ - if (modifier == MODIFIER_LONGLONG) { -#endif /* __LP64__ */ - do { - r = n % 10; - n /= 10; - tmp[i++] = digits[r]; - } while (n != 0); -#ifndef __LP64__ - } else { - unsigned long m; - - m = (unsigned long)n; - - do { - r = m % 10; - m /= 10; - tmp[i++] = digits[r]; - } while (m != 0); - } -#endif /* __LP64__ */ - } else { - mask = base - 1; - shift = (base == 8) ? 3 : 4; - - do { - r = (unsigned char)n & mask; - n >>= shift; - tmp[i++] = digits[r] | (flags & FORMAT_LOWER); - } while (n != 0); - } - - if (i > precision) - precision = i; - - width -= precision; - - if (!(flags & (FORMAT_LEFT_JUSTIFY | FORMAT_ZERO_PAD))) - while (width-- > 0) - str = xputchar(str, end, ' '); - - if (flags & FORMAT_ALT_FORM) { - str = xputchar(str, end, '0'); - - if (base == 16) - str = xputchar(str, end, 'X' | (flags & FORMAT_LOWER)); - } else if (sign) { - str = xputchar(str, end, sign); - } - - if (!(flags & FORMAT_LEFT_JUSTIFY)) { - c = (flags & FORMAT_ZERO_PAD) ? '0' : ' '; - - while (width-- > 0) - str = xputchar(str, end, c); - } - - while (i < precision--) - str = xputchar(str, end, '0'); - - while (i-- > 0) - str = xputchar(str, end, tmp[i]); - - while (width-- > 0) - str = xputchar(str, end, ' '); - - break; - case SPECIFIER_CHAR: - c = (unsigned char)va_arg(ap, int); - - if (!(flags & FORMAT_LEFT_JUSTIFY)) - while (--width > 0) - str = xputchar(str, end, ' '); - - str = xputchar(str, end, c); - - while (--width > 0) - str = xputchar(str, end, ' '); - - break; - case SPECIFIER_STR: - s = va_arg(ap, char *); - - if (s == NULL) - s = "(null)"; - - len = 0; - - for (len = 0; s[len] != '\0'; len++) - if (len == precision) - break; - - if (!(flags & FORMAT_LEFT_JUSTIFY)) - while (len < width--) - str = xputchar(str, end, ' '); - - for (i = 0; i < len; i++) { - str = xputchar(str, end, *s); - s++; - } - - while (len < width--) - str = xputchar(str, end, ' '); - - break; - case SPECIFIER_NRCHARS: - if (modifier == MODIFIER_CHAR) { - signed char *ptr = va_arg(ap, signed char *); - *ptr = str - start; - } else if (modifier == MODIFIER_SHORT) { - short *ptr = va_arg(ap, short *); - *ptr = str - start; - } else if (modifier == MODIFIER_LONG) { - long *ptr = va_arg(ap, long *); - *ptr = str - start; - } else if (modifier == MODIFIER_LONGLONG) { - long long *ptr = va_arg(ap, long long *); - *ptr = str - start; - } else if (modifier == MODIFIER_SIZE) { - ssize_t *ptr = va_arg(ap, ssize_t *); - *ptr = str - start; - } else if (modifier == MODIFIER_PTRDIFF) { - ptrdiff_t *ptr = va_arg(ap, ptrdiff_t *); - *ptr = str - start; - } else { - int *ptr = va_arg(ap, int *); - *ptr = str - start; - } - - break; - case SPECIFIER_PERCENT: - case SPECIFIER_INVALID: - str = xputchar(str, end, '%'); - break; - default: - break; - } - - if (specifier != SPECIFIER_INVALID) - format++; - } - - if (str < end) - *str = '\0'; - else if (end != NULL) - *end = '\0'; - - return str - start; -} diff --git a/xprintf.h b/xprintf.h deleted file mode 100644 index 7634a9f..0000000 --- a/xprintf.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2010 Richard Braun. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * - * Formatted output functions. - * - * The functions provided by this module implement a subset of the C99 - * printf() like functions, mostly centered around character, string, and - * integer conversions. - * - * The supported specifiers are: d i o u x X c s p n % - * The supported length modifiers are: hh h l ll z t - * - * The xprintf() and xvprintf() functions internally use a statically - * allocated buffer. Although they are thread-safe, they won't produce - * output larger than 1 KiB. - */ - -#ifndef _XPRINTF_H -#define _XPRINTF_H - -#include <stdarg.h> - -#include "macros.h" - -int xprintf(const char *format, ...) __format_printf(1, 2); -int xvprintf(const char *format, va_list ap) __format_printf(1, 0); - -int xsprintf(char *str, const char *format, ...) __format_printf(2, 3); -int xvsprintf(char *str, const char *format, va_list ap) __format_printf(2, 0); - -int xsnprintf(char *str, size_t size, const char *format, ...) - __format_printf(3, 4); -int xvsnprintf(char *str, size_t size, const char *format, va_list ap) - __format_printf(3, 0); - -#endif /* _XPRINTF_H */ |