xref: /freebsd/sys/compat/linuxkpi/common/src/linux_slab.c (revision 14c5024db875e0a4104b0203f3d202eb3042230c)
1*14c5024dSHans Petter Selasky /*-
2*14c5024dSHans Petter Selasky  * Copyright (c) 2017 Mellanox Technologies, Ltd.
3*14c5024dSHans Petter Selasky  * All rights reserved.
4*14c5024dSHans Petter Selasky  *
5*14c5024dSHans Petter Selasky  * Redistribution and use in source and binary forms, with or without
6*14c5024dSHans Petter Selasky  * modification, are permitted provided that the following conditions
7*14c5024dSHans Petter Selasky  * are met:
8*14c5024dSHans Petter Selasky  * 1. Redistributions of source code must retain the above copyright
9*14c5024dSHans Petter Selasky  *    notice unmodified, this list of conditions, and the following
10*14c5024dSHans Petter Selasky  *    disclaimer.
11*14c5024dSHans Petter Selasky  * 2. Redistributions in binary form must reproduce the above copyright
12*14c5024dSHans Petter Selasky  *    notice, this list of conditions and the following disclaimer in the
13*14c5024dSHans Petter Selasky  *    documentation and/or other materials provided with the distribution.
14*14c5024dSHans Petter Selasky  *
15*14c5024dSHans Petter Selasky  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16*14c5024dSHans Petter Selasky  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17*14c5024dSHans Petter Selasky  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18*14c5024dSHans Petter Selasky  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19*14c5024dSHans Petter Selasky  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20*14c5024dSHans Petter Selasky  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21*14c5024dSHans Petter Selasky  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22*14c5024dSHans Petter Selasky  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23*14c5024dSHans Petter Selasky  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24*14c5024dSHans Petter Selasky  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25*14c5024dSHans Petter Selasky  */
26*14c5024dSHans Petter Selasky 
27*14c5024dSHans Petter Selasky #include <sys/cdefs.h>
28*14c5024dSHans Petter Selasky __FBSDID("$FreeBSD$");
29*14c5024dSHans Petter Selasky 
30*14c5024dSHans Petter Selasky #include <linux/slab.h>
31*14c5024dSHans Petter Selasky #include <linux/rcupdate.h>
32*14c5024dSHans Petter Selasky #include <linux/kernel.h>
33*14c5024dSHans Petter Selasky 
34*14c5024dSHans Petter Selasky struct linux_kmem_rcu {
35*14c5024dSHans Petter Selasky 	struct rcu_head rcu_head;
36*14c5024dSHans Petter Selasky 	struct linux_kmem_cache *cache;
37*14c5024dSHans Petter Selasky };
38*14c5024dSHans Petter Selasky 
39*14c5024dSHans Petter Selasky #define	LINUX_KMEM_TO_RCU(c, m)					\
40*14c5024dSHans Petter Selasky 	((struct linux_kmem_rcu *)((char *)(m) +		\
41*14c5024dSHans Petter Selasky 	(c)->cache_size - sizeof(struct linux_kmem_rcu)))
42*14c5024dSHans Petter Selasky 
43*14c5024dSHans Petter Selasky #define	LINUX_RCU_TO_KMEM(r)					\
44*14c5024dSHans Petter Selasky 	((void *)((char *)(r) + sizeof(struct linux_kmem_rcu) - \
45*14c5024dSHans Petter Selasky 	(r)->cache->cache_size))
46*14c5024dSHans Petter Selasky 
47*14c5024dSHans Petter Selasky static int
48*14c5024dSHans Petter Selasky linux_kmem_ctor(void *mem, int size, void *arg, int flags)
49*14c5024dSHans Petter Selasky {
50*14c5024dSHans Petter Selasky 	struct linux_kmem_cache *c = arg;
51*14c5024dSHans Petter Selasky 
52*14c5024dSHans Petter Selasky 	if (unlikely(c->cache_flags & SLAB_DESTROY_BY_RCU)) {
53*14c5024dSHans Petter Selasky 		struct linux_kmem_rcu *rcu = LINUX_KMEM_TO_RCU(c, mem);
54*14c5024dSHans Petter Selasky 
55*14c5024dSHans Petter Selasky 		/* duplicate cache pointer */
56*14c5024dSHans Petter Selasky 		rcu->cache = c;
57*14c5024dSHans Petter Selasky 	}
58*14c5024dSHans Petter Selasky 
59*14c5024dSHans Petter Selasky 	/* check for constructor */
60*14c5024dSHans Petter Selasky 	if (likely(c->cache_ctor != NULL))
61*14c5024dSHans Petter Selasky 		c->cache_ctor(mem);
62*14c5024dSHans Petter Selasky 
63*14c5024dSHans Petter Selasky 	return (0);
64*14c5024dSHans Petter Selasky }
65*14c5024dSHans Petter Selasky 
66*14c5024dSHans Petter Selasky static void
67*14c5024dSHans Petter Selasky linux_kmem_cache_free_rcu_callback(struct rcu_head *head)
68*14c5024dSHans Petter Selasky {
69*14c5024dSHans Petter Selasky 	struct linux_kmem_rcu *rcu =
70*14c5024dSHans Petter Selasky 	    container_of(head, struct linux_kmem_rcu, rcu_head);
71*14c5024dSHans Petter Selasky 
72*14c5024dSHans Petter Selasky 	uma_zfree(rcu->cache->cache_zone, LINUX_RCU_TO_KMEM(rcu));
73*14c5024dSHans Petter Selasky }
74*14c5024dSHans Petter Selasky 
75*14c5024dSHans Petter Selasky struct linux_kmem_cache *
76*14c5024dSHans Petter Selasky linux_kmem_cache_create(const char *name, size_t size, size_t align,
77*14c5024dSHans Petter Selasky     unsigned flags, linux_kmem_ctor_t *ctor)
78*14c5024dSHans Petter Selasky {
79*14c5024dSHans Petter Selasky 	struct linux_kmem_cache *c;
80*14c5024dSHans Petter Selasky 
81*14c5024dSHans Petter Selasky 	c = malloc(sizeof(*c), M_KMALLOC, M_WAITOK);
82*14c5024dSHans Petter Selasky 
83*14c5024dSHans Petter Selasky 	if (flags & SLAB_HWCACHE_ALIGN)
84*14c5024dSHans Petter Selasky 		align = UMA_ALIGN_CACHE;
85*14c5024dSHans Petter Selasky 	else if (align != 0)
86*14c5024dSHans Petter Selasky 		align--;
87*14c5024dSHans Petter Selasky 
88*14c5024dSHans Petter Selasky 	if (flags & SLAB_DESTROY_BY_RCU) {
89*14c5024dSHans Petter Selasky 		/* make room for RCU structure */
90*14c5024dSHans Petter Selasky 		size = ALIGN(size, sizeof(void *));
91*14c5024dSHans Petter Selasky 		size += sizeof(struct linux_kmem_rcu);
92*14c5024dSHans Petter Selasky 
93*14c5024dSHans Petter Selasky 		/* create cache_zone */
94*14c5024dSHans Petter Selasky 		c->cache_zone = uma_zcreate(name, size,
95*14c5024dSHans Petter Selasky 		    linux_kmem_ctor, NULL, NULL, NULL,
96*14c5024dSHans Petter Selasky 		    align, UMA_ZONE_ZINIT);
97*14c5024dSHans Petter Selasky 	} else {
98*14c5024dSHans Petter Selasky 		/* create cache_zone */
99*14c5024dSHans Petter Selasky 		c->cache_zone = uma_zcreate(name, size,
100*14c5024dSHans Petter Selasky 		    ctor ? linux_kmem_ctor : NULL, NULL,
101*14c5024dSHans Petter Selasky 		    NULL, NULL, align, 0);
102*14c5024dSHans Petter Selasky 	}
103*14c5024dSHans Petter Selasky 
104*14c5024dSHans Petter Selasky 	c->cache_flags = flags;
105*14c5024dSHans Petter Selasky 	c->cache_ctor = ctor;
106*14c5024dSHans Petter Selasky 	c->cache_size = size;
107*14c5024dSHans Petter Selasky 	return (c);
108*14c5024dSHans Petter Selasky }
109*14c5024dSHans Petter Selasky 
110*14c5024dSHans Petter Selasky void
111*14c5024dSHans Petter Selasky linux_kmem_cache_free_rcu(struct linux_kmem_cache *c, void *m)
112*14c5024dSHans Petter Selasky {
113*14c5024dSHans Petter Selasky 	struct linux_kmem_rcu *rcu = LINUX_KMEM_TO_RCU(c, m);
114*14c5024dSHans Petter Selasky 
115*14c5024dSHans Petter Selasky 	call_rcu(&rcu->rcu_head, linux_kmem_cache_free_rcu_callback);
116*14c5024dSHans Petter Selasky }
117*14c5024dSHans Petter Selasky 
118*14c5024dSHans Petter Selasky void
119*14c5024dSHans Petter Selasky linux_kmem_cache_destroy(struct linux_kmem_cache *c)
120*14c5024dSHans Petter Selasky {
121*14c5024dSHans Petter Selasky 	if (unlikely(c->cache_flags & SLAB_DESTROY_BY_RCU)) {
122*14c5024dSHans Petter Selasky 		/* make sure all free callbacks have been called */
123*14c5024dSHans Petter Selasky 		rcu_barrier();
124*14c5024dSHans Petter Selasky 	}
125*14c5024dSHans Petter Selasky 
126*14c5024dSHans Petter Selasky 	uma_zdestroy(c->cache_zone);
127*14c5024dSHans Petter Selasky 	free(c, M_KMALLOC);
128*14c5024dSHans Petter Selasky }
129