Path: blob/main/sys/compat/linuxkpi/common/src/linux_devres.c
39586 views
/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 2020-2025 The FreeBSD Foundation4*5* This software was developed by Bj\xc3\xb6rn Zeeb under sponsorship from6* 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, this list of conditions and the following disclaimer.13* 2. Redistributions in binary form must reproduce the above copyright14* notice, this list of conditions and the following disclaimer in the15* documentation and/or other materials provided with the distribution.16*17* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND18* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE19* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE20* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE21* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL22* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS23* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)24* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT25* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY26* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF27* SUCH DAMAGE.28*/2930#include <sys/cdefs.h>31#include <linux/kernel.h>32#include <linux/device.h>33#include <linux/slab.h>34#include <linux/list.h>3536/*37* Linux devres KPI implementation.38*/3940struct devres {41struct list_head entry;42void (*release)(struct device *, void *);4344/* Must come last. */45uint8_t __drdata[0] __aligned(CACHE_LINE_SIZE);46};4748void *49lkpi_devres_alloc(void(*release)(struct device *, void *),50size_t size, gfp_t gfp)51{52void *p;53struct devres *dr;54size_t total;5556if (size == 0)57return (NULL);5859total = sizeof(*dr) + size;60dr = kmalloc(total, gfp);61if (dr == NULL)62return (NULL);6364INIT_LIST_HEAD(&dr->entry);65dr->release = release;66p = (void *)(dr+1);6768return (p);69}7071static void72lkpi_devres_free_dr(struct devres *dr)73{7475/*76* We have no dev, so cannot lock. This means someone else has77* to do this prior to us if devres_add() had been called.78*/79KASSERT(list_empty_careful(&dr->entry),80("%s: dr %p still on devres_head\n", __func__, dr));81kfree(dr);82}8384void85lkpi_devres_free(void *p)86{87struct devres *dr;8889if (p == NULL)90return;9192dr = container_of(p, struct devres, __drdata);93lkpi_devres_free_dr(dr);94}9596void97lkpi_devres_add(struct device *dev, void *p)98{99struct devres *dr;100101KASSERT(dev != NULL && p != NULL, ("%s: dev %p p %p\n",102__func__, dev, p));103104dr = container_of(p, struct devres, __drdata);105spin_lock(&dev->devres_lock);106list_add(&dr->entry, &dev->devres_head);107spin_unlock(&dev->devres_lock);108}109110static struct devres *111lkpi_devres_find_dr(struct device *dev, void(*release)(struct device *, void *),112int (*match)(struct device *, void *, void *), void *mp)113{114struct devres *dr, *next;115void *p;116117KASSERT(dev != NULL, ("%s: dev %p\n", __func__, dev));118assert_spin_locked(&dev->devres_lock);119120list_for_each_entry_safe(dr, next, &dev->devres_head, entry) {121if (dr->release != release)122continue;123p = (void *)(dr+1);124if (match != NULL && match(dev, p, mp) == false)125continue;126return (dr);127}128129return (NULL);130}131132void *133lkpi_devres_find(struct device *dev, void(*release)(struct device *, void *),134int (*match)(struct device *, void *, void *), void *mp)135{136struct devres *dr;137138KASSERT(dev != NULL, ("%s: dev %p\n", __func__, dev));139140spin_lock(&dev->devres_lock);141dr = lkpi_devres_find_dr(dev, release, match, mp);142spin_unlock(&dev->devres_lock);143144if (dr == NULL)145return (NULL);146147return ((void *)(dr + 1));148}149150static void151lkpi_devres_unlink_locked(struct device *dev, struct devres *dr)152{153KASSERT(dev != NULL, ("%s: dev %p\n", __func__, dev));154KASSERT(dr != NULL, ("%s: dr %p\n", __func__, dr));155assert_spin_locked(&dev->devres_lock);156157list_del_init(&dr->entry);158}159160void161lkpi_devres_unlink(struct device *dev, void *p)162{163struct devres *dr;164165KASSERT(dev != NULL && p != NULL, ("%s: dev %p p %p\n",166__func__, dev, p));167168dr = container_of(p, struct devres, __drdata);169spin_lock(&dev->devres_lock);170lkpi_devres_unlink_locked(dev, dr);171spin_unlock(&dev->devres_lock);172}173174/* This is called on device free. */175void176lkpi_devres_release_free_list(struct device *dev)177{178struct devres *dr, *next;179void *p;180181/* Free any resources allocated on the device. */182/* No need to lock anymore. */183list_for_each_entry_safe(dr, next, &dev->devres_head, entry) {184p = (void *)(dr+1);185if (dr->release != NULL)186dr->release(dev, p);187/* This should probably be a function of some kind. */188list_del_init(&dr->entry);189lkpi_devres_free(p);190}191}192193int194lkpi_devres_destroy(struct device *dev, void(*release)(struct device *, void *),195int (*match)(struct device *, void *, void *), void *mp)196{197struct devres *dr;198199spin_lock(&dev->devres_lock);200dr = lkpi_devres_find_dr(dev, release, match, mp);201if (dr != NULL)202lkpi_devres_unlink_locked(dev, dr);203spin_unlock(&dev->devres_lock);204205if (dr == NULL)206return (-ENOENT);207lkpi_devres_free_dr(dr);208209return (0);210}211212/*213* Devres release function for k*malloc().214* While there is nothing to do here adding, e.g., tracing would be215* possible so we leave the empty function here.216* Also good for documentation as it is the simplest example.217*/218void219lkpi_devm_kmalloc_release(struct device *dev __unused, void *p __unused)220{221222/* Nothing to do. Freed with the devres. */223}224225static int226lkpi_devm_kmalloc_match(struct device *dev __unused, void *p, void *mp)227{228return (p == mp);229}230231void232lkpi_devm_kfree(struct device *dev, const void *p)233{234void *mp;235int error;236237if (p == NULL)238return;239240/* I assume Linux simply casts the const away... */241mp = __DECONST(void *, p);242error = lkpi_devres_destroy(dev, lkpi_devm_kmalloc_release,243lkpi_devm_kmalloc_match, mp);244if (error != 0)245dev_warn(dev, "%s: lkpi_devres_destroy failed with %d\n",246__func__, error);247}248249struct devres_action {250void *data;251void (*action)(void *);252};253254static void255lkpi_devm_action_release(struct device *dev, void *res)256{257struct devres_action *devres;258259devres = (struct devres_action *)res;260devres->action(devres->data);261}262263int264lkpi_devm_add_action(struct device *dev, void (*action)(void *), void *data)265{266struct devres_action *devres;267268KASSERT(action != NULL, ("%s: action is NULL\n", __func__));269devres = lkpi_devres_alloc(lkpi_devm_action_release,270sizeof(struct devres_action), GFP_KERNEL);271if (devres == NULL)272return (-ENOMEM);273devres->data = data;274devres->action = action;275devres_add(dev, devres);276277return (0);278}279280int281lkpi_devm_add_action_or_reset(struct device *dev, void (*action)(void *), void *data)282{283int rv;284285rv = lkpi_devm_add_action(dev, action, data);286if (rv != 0)287action(data);288289return (rv);290}291292293