xref: /freebsd/sys/compat/linuxkpi/common/src/linux_slab.c (revision 782a90d16af6b235ea51160defcf489fcb5f92a1)
114c5024dSHans Petter Selasky /*-
214c5024dSHans Petter Selasky  * Copyright (c) 2017 Mellanox Technologies, Ltd.
314c5024dSHans Petter Selasky  * All rights reserved.
414c5024dSHans Petter Selasky  *
514c5024dSHans Petter Selasky  * Redistribution and use in source and binary forms, with or without
614c5024dSHans Petter Selasky  * modification, are permitted provided that the following conditions
714c5024dSHans Petter Selasky  * are met:
814c5024dSHans Petter Selasky  * 1. Redistributions of source code must retain the above copyright
914c5024dSHans Petter Selasky  *    notice unmodified, this list of conditions, and the following
1014c5024dSHans Petter Selasky  *    disclaimer.
1114c5024dSHans Petter Selasky  * 2. Redistributions in binary form must reproduce the above copyright
1214c5024dSHans Petter Selasky  *    notice, this list of conditions and the following disclaimer in the
1314c5024dSHans Petter Selasky  *    documentation and/or other materials provided with the distribution.
1414c5024dSHans Petter Selasky  *
1514c5024dSHans Petter Selasky  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1614c5024dSHans Petter Selasky  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1714c5024dSHans Petter Selasky  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1814c5024dSHans Petter Selasky  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1914c5024dSHans Petter Selasky  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2014c5024dSHans Petter Selasky  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2114c5024dSHans Petter Selasky  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2214c5024dSHans Petter Selasky  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2314c5024dSHans Petter Selasky  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2414c5024dSHans Petter Selasky  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2514c5024dSHans Petter Selasky  */
2614c5024dSHans Petter Selasky 
2714c5024dSHans Petter Selasky #include <sys/cdefs.h>
2814c5024dSHans Petter Selasky __FBSDID("$FreeBSD$");
2914c5024dSHans Petter Selasky 
3014c5024dSHans Petter Selasky #include <linux/slab.h>
3114c5024dSHans Petter Selasky #include <linux/rcupdate.h>
3214c5024dSHans Petter Selasky #include <linux/kernel.h>
3314c5024dSHans Petter Selasky 
3414c5024dSHans Petter Selasky struct linux_kmem_rcu {
3514c5024dSHans Petter Selasky 	struct rcu_head rcu_head;
3614c5024dSHans Petter Selasky 	struct linux_kmem_cache *cache;
3714c5024dSHans Petter Selasky };
3814c5024dSHans Petter Selasky 
3914c5024dSHans Petter Selasky #define	LINUX_KMEM_TO_RCU(c, m)					\
4014c5024dSHans Petter Selasky 	((struct linux_kmem_rcu *)((char *)(m) +		\
4114c5024dSHans Petter Selasky 	(c)->cache_size - sizeof(struct linux_kmem_rcu)))
4214c5024dSHans Petter Selasky 
4314c5024dSHans Petter Selasky #define	LINUX_RCU_TO_KMEM(r)					\
4414c5024dSHans Petter Selasky 	((void *)((char *)(r) + sizeof(struct linux_kmem_rcu) - \
4514c5024dSHans Petter Selasky 	(r)->cache->cache_size))
4614c5024dSHans Petter Selasky 
4714c5024dSHans Petter Selasky static int
4814c5024dSHans Petter Selasky linux_kmem_ctor(void *mem, int size, void *arg, int flags)
4914c5024dSHans Petter Selasky {
5014c5024dSHans Petter Selasky 	struct linux_kmem_cache *c = arg;
5114c5024dSHans Petter Selasky 
52*782a90d1SHans Petter Selasky 	if (unlikely(c->cache_flags & SLAB_TYPESAFE_BY_RCU)) {
5314c5024dSHans Petter Selasky 		struct linux_kmem_rcu *rcu = LINUX_KMEM_TO_RCU(c, mem);
5414c5024dSHans Petter Selasky 
5514c5024dSHans Petter Selasky 		/* duplicate cache pointer */
5614c5024dSHans Petter Selasky 		rcu->cache = c;
5714c5024dSHans Petter Selasky 	}
5814c5024dSHans Petter Selasky 
5914c5024dSHans Petter Selasky 	/* check for constructor */
6014c5024dSHans Petter Selasky 	if (likely(c->cache_ctor != NULL))
6114c5024dSHans Petter Selasky 		c->cache_ctor(mem);
6214c5024dSHans Petter Selasky 
6314c5024dSHans Petter Selasky 	return (0);
6414c5024dSHans Petter Selasky }
6514c5024dSHans Petter Selasky 
6614c5024dSHans Petter Selasky static void
6714c5024dSHans Petter Selasky linux_kmem_cache_free_rcu_callback(struct rcu_head *head)
6814c5024dSHans Petter Selasky {
6914c5024dSHans Petter Selasky 	struct linux_kmem_rcu *rcu =
7014c5024dSHans Petter Selasky 	    container_of(head, struct linux_kmem_rcu, rcu_head);
7114c5024dSHans Petter Selasky 
7214c5024dSHans Petter Selasky 	uma_zfree(rcu->cache->cache_zone, LINUX_RCU_TO_KMEM(rcu));
7314c5024dSHans Petter Selasky }
7414c5024dSHans Petter Selasky 
7514c5024dSHans Petter Selasky struct linux_kmem_cache *
7614c5024dSHans Petter Selasky linux_kmem_cache_create(const char *name, size_t size, size_t align,
7714c5024dSHans Petter Selasky     unsigned flags, linux_kmem_ctor_t *ctor)
7814c5024dSHans Petter Selasky {
7914c5024dSHans Petter Selasky 	struct linux_kmem_cache *c;
8014c5024dSHans Petter Selasky 
8114c5024dSHans Petter Selasky 	c = malloc(sizeof(*c), M_KMALLOC, M_WAITOK);
8214c5024dSHans Petter Selasky 
8314c5024dSHans Petter Selasky 	if (flags & SLAB_HWCACHE_ALIGN)
8414c5024dSHans Petter Selasky 		align = UMA_ALIGN_CACHE;
8514c5024dSHans Petter Selasky 	else if (align != 0)
8614c5024dSHans Petter Selasky 		align--;
8714c5024dSHans Petter Selasky 
88*782a90d1SHans Petter Selasky 	if (flags & SLAB_TYPESAFE_BY_RCU) {
8914c5024dSHans Petter Selasky 		/* make room for RCU structure */
9014c5024dSHans Petter Selasky 		size = ALIGN(size, sizeof(void *));
9114c5024dSHans Petter Selasky 		size += sizeof(struct linux_kmem_rcu);
9214c5024dSHans Petter Selasky 
9314c5024dSHans Petter Selasky 		/* create cache_zone */
9414c5024dSHans Petter Selasky 		c->cache_zone = uma_zcreate(name, size,
9514c5024dSHans Petter Selasky 		    linux_kmem_ctor, NULL, NULL, NULL,
9614c5024dSHans Petter Selasky 		    align, UMA_ZONE_ZINIT);
9714c5024dSHans Petter Selasky 	} else {
9814c5024dSHans Petter Selasky 		/* create cache_zone */
9914c5024dSHans Petter Selasky 		c->cache_zone = uma_zcreate(name, size,
10014c5024dSHans Petter Selasky 		    ctor ? linux_kmem_ctor : NULL, NULL,
10114c5024dSHans Petter Selasky 		    NULL, NULL, align, 0);
10214c5024dSHans Petter Selasky 	}
10314c5024dSHans Petter Selasky 
10414c5024dSHans Petter Selasky 	c->cache_flags = flags;
10514c5024dSHans Petter Selasky 	c->cache_ctor = ctor;
10614c5024dSHans Petter Selasky 	c->cache_size = size;
10714c5024dSHans Petter Selasky 	return (c);
10814c5024dSHans Petter Selasky }
10914c5024dSHans Petter Selasky 
11014c5024dSHans Petter Selasky void
11114c5024dSHans Petter Selasky linux_kmem_cache_free_rcu(struct linux_kmem_cache *c, void *m)
11214c5024dSHans Petter Selasky {
11314c5024dSHans Petter Selasky 	struct linux_kmem_rcu *rcu = LINUX_KMEM_TO_RCU(c, m);
11414c5024dSHans Petter Selasky 
11514c5024dSHans Petter Selasky 	call_rcu(&rcu->rcu_head, linux_kmem_cache_free_rcu_callback);
11614c5024dSHans Petter Selasky }
11714c5024dSHans Petter Selasky 
11814c5024dSHans Petter Selasky void
11914c5024dSHans Petter Selasky linux_kmem_cache_destroy(struct linux_kmem_cache *c)
12014c5024dSHans Petter Selasky {
121*782a90d1SHans Petter Selasky 	if (unlikely(c->cache_flags & SLAB_TYPESAFE_BY_RCU)) {
12214c5024dSHans Petter Selasky 		/* make sure all free callbacks have been called */
12314c5024dSHans Petter Selasky 		rcu_barrier();
12414c5024dSHans Petter Selasky 	}
12514c5024dSHans Petter Selasky 
12614c5024dSHans Petter Selasky 	uma_zdestroy(c->cache_zone);
12714c5024dSHans Petter Selasky 	free(c, M_KMALLOC);
12814c5024dSHans Petter Selasky }
129