18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci#include <stdlib.h>
38c2ecf20Sopenharmony_ci#include <string.h>
48c2ecf20Sopenharmony_ci#include <malloc.h>
58c2ecf20Sopenharmony_ci#include <pthread.h>
68c2ecf20Sopenharmony_ci#include <unistd.h>
78c2ecf20Sopenharmony_ci#include <assert.h>
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/gfp.h>
108c2ecf20Sopenharmony_ci#include <linux/poison.h>
118c2ecf20Sopenharmony_ci#include <linux/slab.h>
128c2ecf20Sopenharmony_ci#include <linux/radix-tree.h>
138c2ecf20Sopenharmony_ci#include <urcu/uatomic.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ciint nr_allocated;
168c2ecf20Sopenharmony_ciint preempt_count;
178c2ecf20Sopenharmony_ciint kmalloc_verbose;
188c2ecf20Sopenharmony_ciint test_verbose;
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cistruct kmem_cache {
218c2ecf20Sopenharmony_ci	pthread_mutex_t lock;
228c2ecf20Sopenharmony_ci	unsigned int size;
238c2ecf20Sopenharmony_ci	unsigned int align;
248c2ecf20Sopenharmony_ci	int nr_objs;
258c2ecf20Sopenharmony_ci	void *objs;
268c2ecf20Sopenharmony_ci	void (*ctor)(void *);
278c2ecf20Sopenharmony_ci};
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_civoid *kmem_cache_alloc(struct kmem_cache *cachep, int gfp)
308c2ecf20Sopenharmony_ci{
318c2ecf20Sopenharmony_ci	void *p;
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	if (!(gfp & __GFP_DIRECT_RECLAIM))
348c2ecf20Sopenharmony_ci		return NULL;
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	pthread_mutex_lock(&cachep->lock);
378c2ecf20Sopenharmony_ci	if (cachep->nr_objs) {
388c2ecf20Sopenharmony_ci		struct radix_tree_node *node = cachep->objs;
398c2ecf20Sopenharmony_ci		cachep->nr_objs--;
408c2ecf20Sopenharmony_ci		cachep->objs = node->parent;
418c2ecf20Sopenharmony_ci		pthread_mutex_unlock(&cachep->lock);
428c2ecf20Sopenharmony_ci		node->parent = NULL;
438c2ecf20Sopenharmony_ci		p = node;
448c2ecf20Sopenharmony_ci	} else {
458c2ecf20Sopenharmony_ci		pthread_mutex_unlock(&cachep->lock);
468c2ecf20Sopenharmony_ci		if (cachep->align)
478c2ecf20Sopenharmony_ci			posix_memalign(&p, cachep->align, cachep->size);
488c2ecf20Sopenharmony_ci		else
498c2ecf20Sopenharmony_ci			p = malloc(cachep->size);
508c2ecf20Sopenharmony_ci		if (cachep->ctor)
518c2ecf20Sopenharmony_ci			cachep->ctor(p);
528c2ecf20Sopenharmony_ci		else if (gfp & __GFP_ZERO)
538c2ecf20Sopenharmony_ci			memset(p, 0, cachep->size);
548c2ecf20Sopenharmony_ci	}
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	uatomic_inc(&nr_allocated);
578c2ecf20Sopenharmony_ci	if (kmalloc_verbose)
588c2ecf20Sopenharmony_ci		printf("Allocating %p from slab\n", p);
598c2ecf20Sopenharmony_ci	return p;
608c2ecf20Sopenharmony_ci}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_civoid kmem_cache_free(struct kmem_cache *cachep, void *objp)
638c2ecf20Sopenharmony_ci{
648c2ecf20Sopenharmony_ci	assert(objp);
658c2ecf20Sopenharmony_ci	uatomic_dec(&nr_allocated);
668c2ecf20Sopenharmony_ci	if (kmalloc_verbose)
678c2ecf20Sopenharmony_ci		printf("Freeing %p to slab\n", objp);
688c2ecf20Sopenharmony_ci	pthread_mutex_lock(&cachep->lock);
698c2ecf20Sopenharmony_ci	if (cachep->nr_objs > 10 || cachep->align) {
708c2ecf20Sopenharmony_ci		memset(objp, POISON_FREE, cachep->size);
718c2ecf20Sopenharmony_ci		free(objp);
728c2ecf20Sopenharmony_ci	} else {
738c2ecf20Sopenharmony_ci		struct radix_tree_node *node = objp;
748c2ecf20Sopenharmony_ci		cachep->nr_objs++;
758c2ecf20Sopenharmony_ci		node->parent = cachep->objs;
768c2ecf20Sopenharmony_ci		cachep->objs = node;
778c2ecf20Sopenharmony_ci	}
788c2ecf20Sopenharmony_ci	pthread_mutex_unlock(&cachep->lock);
798c2ecf20Sopenharmony_ci}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_civoid *kmalloc(size_t size, gfp_t gfp)
828c2ecf20Sopenharmony_ci{
838c2ecf20Sopenharmony_ci	void *ret;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	if (!(gfp & __GFP_DIRECT_RECLAIM))
868c2ecf20Sopenharmony_ci		return NULL;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	ret = malloc(size);
898c2ecf20Sopenharmony_ci	uatomic_inc(&nr_allocated);
908c2ecf20Sopenharmony_ci	if (kmalloc_verbose)
918c2ecf20Sopenharmony_ci		printf("Allocating %p from malloc\n", ret);
928c2ecf20Sopenharmony_ci	if (gfp & __GFP_ZERO)
938c2ecf20Sopenharmony_ci		memset(ret, 0, size);
948c2ecf20Sopenharmony_ci	return ret;
958c2ecf20Sopenharmony_ci}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_civoid kfree(void *p)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	if (!p)
1008c2ecf20Sopenharmony_ci		return;
1018c2ecf20Sopenharmony_ci	uatomic_dec(&nr_allocated);
1028c2ecf20Sopenharmony_ci	if (kmalloc_verbose)
1038c2ecf20Sopenharmony_ci		printf("Freeing %p to malloc\n", p);
1048c2ecf20Sopenharmony_ci	free(p);
1058c2ecf20Sopenharmony_ci}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_cistruct kmem_cache *
1088c2ecf20Sopenharmony_cikmem_cache_create(const char *name, unsigned int size, unsigned int align,
1098c2ecf20Sopenharmony_ci		unsigned int flags, void (*ctor)(void *))
1108c2ecf20Sopenharmony_ci{
1118c2ecf20Sopenharmony_ci	struct kmem_cache *ret = malloc(sizeof(*ret));
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	pthread_mutex_init(&ret->lock, NULL);
1148c2ecf20Sopenharmony_ci	ret->size = size;
1158c2ecf20Sopenharmony_ci	ret->align = align;
1168c2ecf20Sopenharmony_ci	ret->nr_objs = 0;
1178c2ecf20Sopenharmony_ci	ret->objs = NULL;
1188c2ecf20Sopenharmony_ci	ret->ctor = ctor;
1198c2ecf20Sopenharmony_ci	return ret;
1208c2ecf20Sopenharmony_ci}
121