Path: blob/main/sys/compat/linuxkpi/common/src/linux_slab.c
39586 views
/*-1* Copyright (c) 2017 Mellanox Technologies, Ltd.2* All rights reserved.3* Copyright (c) 2024-2025 The FreeBSD Foundation4*5* Portions of this software were developed by Björn Zeeb6* under sponsorship from the FreeBSD Foundation.7*8* Redistribution and use in source and binary forms, with or without9* modification, are permitted provided that the following conditions10* are met:11* 1. Redistributions of source code must retain the above copyright12* notice unmodified, this list of conditions, and the following13* disclaimer.14* 2. Redistributions in binary form must reproduce the above copyright15* notice, this list of conditions and the following disclaimer in the16* documentation and/or other materials provided with the distribution.17*18* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR19* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES20* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.21* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,22* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT23* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,24* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY25* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT26* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF27* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.28*/2930#include <sys/cdefs.h>31#include <linux/compat.h>32#include <linux/slab.h>33#include <linux/rcupdate.h>34#include <linux/kernel.h>35#include <linux/irq_work.h>36#include <linux/llist.h>3738#include <sys/param.h>39#include <sys/taskqueue.h>40#include <vm/uma.h>4142struct linux_kmem_rcu {43struct rcu_head rcu_head;44struct linux_kmem_cache *cache;45};4647struct linux_kmem_cache {48uma_zone_t cache_zone;49linux_kmem_ctor_t *cache_ctor;50unsigned cache_flags;51unsigned cache_size;52struct llist_head cache_items;53struct task cache_task;54};5556#define LINUX_KMEM_TO_RCU(c, m) \57((struct linux_kmem_rcu *)((char *)(m) + \58(c)->cache_size - sizeof(struct linux_kmem_rcu)))5960#define LINUX_RCU_TO_KMEM(r) \61((void *)((char *)(r) + sizeof(struct linux_kmem_rcu) - \62(r)->cache->cache_size))6364static LLIST_HEAD(linux_kfree_async_list);6566static void lkpi_kmem_cache_free_async_fn(void *, int);6768void *69lkpi_kmem_cache_alloc(struct linux_kmem_cache *c, gfp_t flags)70{71return (uma_zalloc_arg(c->cache_zone, c,72linux_check_m_flags(flags)));73}7475void *76lkpi_kmem_cache_zalloc(struct linux_kmem_cache *c, gfp_t flags)77{78return (uma_zalloc_arg(c->cache_zone, c,79linux_check_m_flags(flags | M_ZERO)));80}8182static int83linux_kmem_ctor(void *mem, int size, void *arg, int flags)84{85struct linux_kmem_cache *c = arg;8687if (unlikely(c->cache_flags & SLAB_TYPESAFE_BY_RCU)) {88struct linux_kmem_rcu *rcu = LINUX_KMEM_TO_RCU(c, mem);8990/* duplicate cache pointer */91rcu->cache = c;92}9394/* check for constructor */95if (likely(c->cache_ctor != NULL))96c->cache_ctor(mem);9798return (0);99}100101static void102linux_kmem_cache_free_rcu_callback(struct rcu_head *head)103{104struct linux_kmem_rcu *rcu =105container_of(head, struct linux_kmem_rcu, rcu_head);106107uma_zfree(rcu->cache->cache_zone, LINUX_RCU_TO_KMEM(rcu));108}109110struct linux_kmem_cache *111linux_kmem_cache_create(const char *name, size_t size, size_t align,112unsigned flags, linux_kmem_ctor_t *ctor)113{114struct linux_kmem_cache *c;115116c = malloc(sizeof(*c), M_KMALLOC, M_WAITOK);117118if (flags & SLAB_HWCACHE_ALIGN)119align = UMA_ALIGN_CACHE;120else if (align != 0)121align--;122123if (flags & SLAB_TYPESAFE_BY_RCU) {124/* make room for RCU structure */125size = ALIGN(size, sizeof(void *));126size += sizeof(struct linux_kmem_rcu);127128/* create cache_zone */129c->cache_zone = uma_zcreate(name, size,130linux_kmem_ctor, NULL, NULL, NULL,131align, UMA_ZONE_ZINIT);132} else {133/* make room for async task list items */134size = MAX(size, sizeof(struct llist_node));135136/* create cache_zone */137c->cache_zone = uma_zcreate(name, size,138ctor ? linux_kmem_ctor : NULL, NULL,139NULL, NULL, align, 0);140}141142c->cache_flags = flags;143c->cache_ctor = ctor;144c->cache_size = size;145init_llist_head(&c->cache_items);146TASK_INIT(&c->cache_task, 0, lkpi_kmem_cache_free_async_fn, c);147return (c);148}149150static inline void151lkpi_kmem_cache_free_rcu(struct linux_kmem_cache *c, void *m)152{153struct linux_kmem_rcu *rcu = LINUX_KMEM_TO_RCU(c, m);154155call_rcu(&rcu->rcu_head, linux_kmem_cache_free_rcu_callback);156}157158static inline void159lkpi_kmem_cache_free_sync(struct linux_kmem_cache *c, void *m)160{161uma_zfree(c->cache_zone, m);162}163164static void165lkpi_kmem_cache_free_async_fn(void *context, int pending)166{167struct linux_kmem_cache *c = context;168struct llist_node *freed, *next;169170llist_for_each_safe(freed, next, llist_del_all(&c->cache_items))171lkpi_kmem_cache_free_sync(c, freed);172}173174static inline void175lkpi_kmem_cache_free_async(struct linux_kmem_cache *c, void *m)176{177if (m == NULL)178return;179180llist_add(m, &c->cache_items);181taskqueue_enqueue(linux_irq_work_tq, &c->cache_task);182}183184void185lkpi_kmem_cache_free(struct linux_kmem_cache *c, void *m)186{187if (unlikely(c->cache_flags & SLAB_TYPESAFE_BY_RCU))188lkpi_kmem_cache_free_rcu(c, m);189else if (unlikely(curthread->td_critnest != 0))190lkpi_kmem_cache_free_async(c, m);191else192lkpi_kmem_cache_free_sync(c, m);193}194195void196linux_kmem_cache_destroy(struct linux_kmem_cache *c)197{198if (c == NULL)199return;200201if (unlikely(c->cache_flags & SLAB_TYPESAFE_BY_RCU)) {202/* make sure all free callbacks have been called */203rcu_barrier();204}205206if (!llist_empty(&c->cache_items))207taskqueue_enqueue(linux_irq_work_tq, &c->cache_task);208taskqueue_drain(linux_irq_work_tq, &c->cache_task);209uma_zdestroy(c->cache_zone);210free(c, M_KMALLOC);211}212213void *214lkpi___kmalloc_node(size_t size, gfp_t flags, int node)215{216if (size <= PAGE_SIZE)217return (malloc_domainset(size, M_KMALLOC,218linux_get_vm_domain_set(node), linux_check_m_flags(flags)));219else220return (contigmalloc_domainset(size, M_KMALLOC,221linux_get_vm_domain_set(node), linux_check_m_flags(flags),2220, -1UL, PAGE_SIZE, 0));223}224225void *226lkpi___kmalloc(size_t size, gfp_t flags)227{228size_t _s;229230/* sizeof(struct llist_node) is used for kfree_async(). */231_s = MAX(size, sizeof(struct llist_node));232233if (_s <= PAGE_SIZE)234return (malloc(_s, M_KMALLOC, linux_check_m_flags(flags)));235else236return (contigmalloc(_s, M_KMALLOC, linux_check_m_flags(flags),2370, -1UL, PAGE_SIZE, 0));238}239240void *241lkpi_krealloc(void *ptr, size_t size, gfp_t flags)242{243void *nptr;244size_t osize;245246/*247* First handle invariants based on function arguments.248*/249if (ptr == NULL)250return (kmalloc(size, flags));251252osize = ksize(ptr);253if (size <= osize)254return (ptr);255256/*257* We know the new size > original size. realloc(9) does not (and cannot)258* know about our requirements for physically contiguous memory, so we can259* only call it for sizes up to and including PAGE_SIZE, and otherwise have260* to replicate its functionality using kmalloc to get the contigmalloc(9)261* backing.262*/263if (size <= PAGE_SIZE)264return (realloc(ptr, size, M_KMALLOC, linux_check_m_flags(flags)));265266nptr = kmalloc(size, flags);267if (nptr == NULL)268return (NULL);269270memcpy(nptr, ptr, osize);271kfree(ptr);272return (nptr);273}274275struct lkpi_kmalloc_ctx {276size_t size;277gfp_t flags;278void *addr;279};280281static void282lkpi_kmalloc_cb(void *ctx)283{284struct lkpi_kmalloc_ctx *lmc = ctx;285286lmc->addr = __kmalloc(lmc->size, lmc->flags);287}288289void *290lkpi_kmalloc(size_t size, gfp_t flags)291{292struct lkpi_kmalloc_ctx lmc = { .size = size, .flags = flags };293294lkpi_fpu_safe_exec(&lkpi_kmalloc_cb, &lmc);295return(lmc.addr);296}297298static void299lkpi_kvmalloc_cb(void *ctx)300{301struct lkpi_kmalloc_ctx *lmc = ctx;302303lmc->addr = malloc(lmc->size, M_KMALLOC, linux_check_m_flags(lmc->flags));304}305306void *307lkpi_kvmalloc(size_t size, gfp_t flags)308{309struct lkpi_kmalloc_ctx lmc = { .size = size, .flags = flags };310311lkpi_fpu_safe_exec(&lkpi_kvmalloc_cb, &lmc);312return(lmc.addr);313}314315static void316linux_kfree_async_fn(void *context, int pending)317{318struct llist_node *freed;319320while((freed = llist_del_first(&linux_kfree_async_list)) != NULL)321kfree(freed);322}323static struct task linux_kfree_async_task =324TASK_INITIALIZER(0, linux_kfree_async_fn, &linux_kfree_async_task);325326static void327linux_kfree_async(void *addr)328{329if (addr == NULL)330return;331llist_add(addr, &linux_kfree_async_list);332taskqueue_enqueue(linux_irq_work_tq, &linux_kfree_async_task);333}334335void336lkpi_kfree(const void *ptr)337{338if (ZERO_OR_NULL_PTR(ptr))339return;340341if (curthread->td_critnest != 0)342linux_kfree_async(__DECONST(void *, ptr));343else344free(__DECONST(void *, ptr), M_KMALLOC);345}346347348349