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