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> 33ec25b6faSVladimir Kondratyev #include <linux/irq_work.h> 34ec25b6faSVladimir Kondratyev #include <linux/llist.h> 35ec25b6faSVladimir Kondratyev 36ec25b6faSVladimir Kondratyev #include <sys/param.h> 37ec25b6faSVladimir Kondratyev #include <sys/taskqueue.h> 38a2b83b59SVladimir Kondratyev #include <vm/uma.h> 3914c5024dSHans Petter Selasky 4014c5024dSHans Petter Selasky struct linux_kmem_rcu { 4114c5024dSHans Petter Selasky struct rcu_head rcu_head; 4214c5024dSHans Petter Selasky struct linux_kmem_cache *cache; 4314c5024dSHans Petter Selasky }; 4414c5024dSHans Petter Selasky 45a2b83b59SVladimir Kondratyev struct linux_kmem_cache { 46a2b83b59SVladimir Kondratyev uma_zone_t cache_zone; 47a2b83b59SVladimir Kondratyev linux_kmem_ctor_t *cache_ctor; 48a2b83b59SVladimir Kondratyev unsigned cache_flags; 49a2b83b59SVladimir Kondratyev unsigned cache_size; 50a2b83b59SVladimir Kondratyev struct llist_head cache_items; 51a2b83b59SVladimir Kondratyev struct task cache_task; 52a2b83b59SVladimir Kondratyev }; 53a2b83b59SVladimir Kondratyev 5414c5024dSHans Petter Selasky #define LINUX_KMEM_TO_RCU(c, m) \ 5514c5024dSHans Petter Selasky ((struct linux_kmem_rcu *)((char *)(m) + \ 5614c5024dSHans Petter Selasky (c)->cache_size - sizeof(struct linux_kmem_rcu))) 5714c5024dSHans Petter Selasky 5814c5024dSHans Petter Selasky #define LINUX_RCU_TO_KMEM(r) \ 5914c5024dSHans Petter Selasky ((void *)((char *)(r) + sizeof(struct linux_kmem_rcu) - \ 6014c5024dSHans Petter Selasky (r)->cache->cache_size)) 6114c5024dSHans Petter Selasky 62ec25b6faSVladimir Kondratyev static LLIST_HEAD(linux_kfree_async_list); 63ec25b6faSVladimir Kondratyev 64a2b83b59SVladimir Kondratyev static void lkpi_kmem_cache_free_async_fn(void *, int); 65a2b83b59SVladimir Kondratyev 66a2b83b59SVladimir Kondratyev void * 67a2b83b59SVladimir Kondratyev lkpi_kmem_cache_alloc(struct linux_kmem_cache *c, gfp_t flags) 68a2b83b59SVladimir Kondratyev { 69a2b83b59SVladimir Kondratyev return (uma_zalloc_arg(c->cache_zone, c, 70a2b83b59SVladimir Kondratyev linux_check_m_flags(flags))); 71a2b83b59SVladimir Kondratyev } 72a2b83b59SVladimir Kondratyev 73a2b83b59SVladimir Kondratyev void * 74a2b83b59SVladimir Kondratyev lkpi_kmem_cache_zalloc(struct linux_kmem_cache *c, gfp_t flags) 75a2b83b59SVladimir Kondratyev { 76a2b83b59SVladimir Kondratyev return (uma_zalloc_arg(c->cache_zone, c, 77a2b83b59SVladimir Kondratyev linux_check_m_flags(flags | M_ZERO))); 78a2b83b59SVladimir Kondratyev } 79a2b83b59SVladimir Kondratyev 8014c5024dSHans Petter Selasky static int 8114c5024dSHans Petter Selasky linux_kmem_ctor(void *mem, int size, void *arg, int flags) 8214c5024dSHans Petter Selasky { 8314c5024dSHans Petter Selasky struct linux_kmem_cache *c = arg; 8414c5024dSHans Petter Selasky 85782a90d1SHans Petter Selasky if (unlikely(c->cache_flags & SLAB_TYPESAFE_BY_RCU)) { 8614c5024dSHans Petter Selasky struct linux_kmem_rcu *rcu = LINUX_KMEM_TO_RCU(c, mem); 8714c5024dSHans Petter Selasky 8814c5024dSHans Petter Selasky /* duplicate cache pointer */ 8914c5024dSHans Petter Selasky rcu->cache = c; 9014c5024dSHans Petter Selasky } 9114c5024dSHans Petter Selasky 9214c5024dSHans Petter Selasky /* check for constructor */ 9314c5024dSHans Petter Selasky if (likely(c->cache_ctor != NULL)) 9414c5024dSHans Petter Selasky c->cache_ctor(mem); 9514c5024dSHans Petter Selasky 9614c5024dSHans Petter Selasky return (0); 9714c5024dSHans Petter Selasky } 9814c5024dSHans Petter Selasky 9914c5024dSHans Petter Selasky static void 10014c5024dSHans Petter Selasky linux_kmem_cache_free_rcu_callback(struct rcu_head *head) 10114c5024dSHans Petter Selasky { 10214c5024dSHans Petter Selasky struct linux_kmem_rcu *rcu = 10314c5024dSHans Petter Selasky container_of(head, struct linux_kmem_rcu, rcu_head); 10414c5024dSHans Petter Selasky 10514c5024dSHans Petter Selasky uma_zfree(rcu->cache->cache_zone, LINUX_RCU_TO_KMEM(rcu)); 10614c5024dSHans Petter Selasky } 10714c5024dSHans Petter Selasky 10814c5024dSHans Petter Selasky struct linux_kmem_cache * 10914c5024dSHans Petter Selasky linux_kmem_cache_create(const char *name, size_t size, size_t align, 11014c5024dSHans Petter Selasky unsigned flags, linux_kmem_ctor_t *ctor) 11114c5024dSHans Petter Selasky { 11214c5024dSHans Petter Selasky struct linux_kmem_cache *c; 11314c5024dSHans Petter Selasky 11414c5024dSHans Petter Selasky c = malloc(sizeof(*c), M_KMALLOC, M_WAITOK); 11514c5024dSHans Petter Selasky 11614c5024dSHans Petter Selasky if (flags & SLAB_HWCACHE_ALIGN) 11714c5024dSHans Petter Selasky align = UMA_ALIGN_CACHE; 11814c5024dSHans Petter Selasky else if (align != 0) 11914c5024dSHans Petter Selasky align--; 12014c5024dSHans Petter Selasky 121782a90d1SHans Petter Selasky if (flags & SLAB_TYPESAFE_BY_RCU) { 12214c5024dSHans Petter Selasky /* make room for RCU structure */ 12314c5024dSHans Petter Selasky size = ALIGN(size, sizeof(void *)); 12414c5024dSHans Petter Selasky size += sizeof(struct linux_kmem_rcu); 12514c5024dSHans Petter Selasky 12614c5024dSHans Petter Selasky /* create cache_zone */ 12714c5024dSHans Petter Selasky c->cache_zone = uma_zcreate(name, size, 12814c5024dSHans Petter Selasky linux_kmem_ctor, NULL, NULL, NULL, 12914c5024dSHans Petter Selasky align, UMA_ZONE_ZINIT); 13014c5024dSHans Petter Selasky } else { 131a2b83b59SVladimir Kondratyev /* make room for async task list items */ 132a2b83b59SVladimir Kondratyev size = MAX(size, sizeof(struct llist_node)); 133a2b83b59SVladimir Kondratyev 13414c5024dSHans Petter Selasky /* create cache_zone */ 13514c5024dSHans Petter Selasky c->cache_zone = uma_zcreate(name, size, 13614c5024dSHans Petter Selasky ctor ? linux_kmem_ctor : NULL, NULL, 13714c5024dSHans Petter Selasky NULL, NULL, align, 0); 13814c5024dSHans Petter Selasky } 13914c5024dSHans Petter Selasky 14014c5024dSHans Petter Selasky c->cache_flags = flags; 14114c5024dSHans Petter Selasky c->cache_ctor = ctor; 14214c5024dSHans Petter Selasky c->cache_size = size; 143a2b83b59SVladimir Kondratyev init_llist_head(&c->cache_items); 144a2b83b59SVladimir Kondratyev TASK_INIT(&c->cache_task, 0, lkpi_kmem_cache_free_async_fn, c); 14514c5024dSHans Petter Selasky return (c); 14614c5024dSHans Petter Selasky } 14714c5024dSHans Petter Selasky 148a2b83b59SVladimir Kondratyev static inline void 149a2b83b59SVladimir Kondratyev lkpi_kmem_cache_free_rcu(struct linux_kmem_cache *c, void *m) 15014c5024dSHans Petter Selasky { 15114c5024dSHans Petter Selasky struct linux_kmem_rcu *rcu = LINUX_KMEM_TO_RCU(c, m); 15214c5024dSHans Petter Selasky 15314c5024dSHans Petter Selasky call_rcu(&rcu->rcu_head, linux_kmem_cache_free_rcu_callback); 15414c5024dSHans Petter Selasky } 15514c5024dSHans Petter Selasky 156a2b83b59SVladimir Kondratyev static inline void 157a2b83b59SVladimir Kondratyev lkpi_kmem_cache_free_sync(struct linux_kmem_cache *c, void *m) 158a2b83b59SVladimir Kondratyev { 159a2b83b59SVladimir Kondratyev uma_zfree(c->cache_zone, m); 160a2b83b59SVladimir Kondratyev } 161a2b83b59SVladimir Kondratyev 162a2b83b59SVladimir Kondratyev static void 163a2b83b59SVladimir Kondratyev lkpi_kmem_cache_free_async_fn(void *context, int pending) 164a2b83b59SVladimir Kondratyev { 165a2b83b59SVladimir Kondratyev struct linux_kmem_cache *c = context; 166a2b83b59SVladimir Kondratyev struct llist_node *freed, *next; 167a2b83b59SVladimir Kondratyev 168a2b83b59SVladimir Kondratyev llist_for_each_safe(freed, next, llist_del_all(&c->cache_items)) 169a2b83b59SVladimir Kondratyev lkpi_kmem_cache_free_sync(c, freed); 170a2b83b59SVladimir Kondratyev } 171a2b83b59SVladimir Kondratyev 172a2b83b59SVladimir Kondratyev static inline void 173a2b83b59SVladimir Kondratyev lkpi_kmem_cache_free_async(struct linux_kmem_cache *c, void *m) 174a2b83b59SVladimir Kondratyev { 175a2b83b59SVladimir Kondratyev if (m == NULL) 176a2b83b59SVladimir Kondratyev return; 177a2b83b59SVladimir Kondratyev 178a2b83b59SVladimir Kondratyev llist_add(m, &c->cache_items); 179a2b83b59SVladimir Kondratyev taskqueue_enqueue(linux_irq_work_tq, &c->cache_task); 180a2b83b59SVladimir Kondratyev } 181a2b83b59SVladimir Kondratyev 182a2b83b59SVladimir Kondratyev void 183a2b83b59SVladimir Kondratyev lkpi_kmem_cache_free(struct linux_kmem_cache *c, void *m) 184a2b83b59SVladimir Kondratyev { 185a2b83b59SVladimir Kondratyev if (unlikely(c->cache_flags & SLAB_TYPESAFE_BY_RCU)) 186a2b83b59SVladimir Kondratyev lkpi_kmem_cache_free_rcu(c, m); 187a2b83b59SVladimir Kondratyev else if (unlikely(curthread->td_critnest != 0)) 188a2b83b59SVladimir Kondratyev lkpi_kmem_cache_free_async(c, m); 189a2b83b59SVladimir Kondratyev else 190a2b83b59SVladimir Kondratyev lkpi_kmem_cache_free_sync(c, m); 191a2b83b59SVladimir Kondratyev } 192a2b83b59SVladimir Kondratyev 19314c5024dSHans Petter Selasky void 19414c5024dSHans Petter Selasky linux_kmem_cache_destroy(struct linux_kmem_cache *c) 19514c5024dSHans Petter Selasky { 196*a76de177SMark Johnston if (c == NULL) 197*a76de177SMark Johnston return; 198*a76de177SMark Johnston 199782a90d1SHans Petter Selasky if (unlikely(c->cache_flags & SLAB_TYPESAFE_BY_RCU)) { 20014c5024dSHans Petter Selasky /* make sure all free callbacks have been called */ 20114c5024dSHans Petter Selasky rcu_barrier(); 20214c5024dSHans Petter Selasky } 20314c5024dSHans Petter Selasky 204a2b83b59SVladimir Kondratyev if (!llist_empty(&c->cache_items)) 205a2b83b59SVladimir Kondratyev taskqueue_enqueue(linux_irq_work_tq, &c->cache_task); 206a2b83b59SVladimir Kondratyev taskqueue_drain(linux_irq_work_tq, &c->cache_task); 20714c5024dSHans Petter Selasky uma_zdestroy(c->cache_zone); 20814c5024dSHans Petter Selasky free(c, M_KMALLOC); 20914c5024dSHans Petter Selasky } 210ec25b6faSVladimir Kondratyev 211ec25b6faSVladimir Kondratyev static void 212ec25b6faSVladimir Kondratyev linux_kfree_async_fn(void *context, int pending) 213ec25b6faSVladimir Kondratyev { 214ec25b6faSVladimir Kondratyev struct llist_node *freed; 215ec25b6faSVladimir Kondratyev 216ec25b6faSVladimir Kondratyev while((freed = llist_del_first(&linux_kfree_async_list)) != NULL) 217ec25b6faSVladimir Kondratyev kfree(freed); 218ec25b6faSVladimir Kondratyev } 219ec25b6faSVladimir Kondratyev static struct task linux_kfree_async_task = 220ec25b6faSVladimir Kondratyev TASK_INITIALIZER(0, linux_kfree_async_fn, &linux_kfree_async_task); 221ec25b6faSVladimir Kondratyev 222ec25b6faSVladimir Kondratyev void 223ec25b6faSVladimir Kondratyev linux_kfree_async(void *addr) 224ec25b6faSVladimir Kondratyev { 225ec25b6faSVladimir Kondratyev if (addr == NULL) 226ec25b6faSVladimir Kondratyev return; 227ec25b6faSVladimir Kondratyev llist_add(addr, &linux_kfree_async_list); 228ec25b6faSVladimir Kondratyev taskqueue_enqueue(linux_irq_work_tq, &linux_kfree_async_task); 229ec25b6faSVladimir Kondratyev } 230