1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2020-2021 The FreeBSD Foundation 5 * 6 * This software was developed by Bj\xc3\xb6rn Zeeb under sponsorship from 7 * the FreeBSD Foundation. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31 #include <sys/cdefs.h> 32 __FBSDID("$FreeBSD$"); 33 34 #include <linux/kernel.h> 35 #include <linux/device.h> 36 #include <linux/slab.h> 37 #include <linux/list.h> 38 39 /* 40 * Linux devres KPI implementation. 41 */ 42 43 struct devres { 44 struct list_head entry; 45 void (*release)(struct device *, void *); 46 47 /* Must come last. */ 48 uint8_t __drdata[0] __aligned(CACHE_LINE_SIZE); 49 }; 50 51 void * 52 lkpi_devres_alloc(void(*release)(struct device *, void *), 53 size_t size, gfp_t gfp) 54 { 55 void *p; 56 struct devres *dr; 57 size_t total; 58 59 if (size == 0) 60 return (NULL); 61 62 total = sizeof(*dr) + size; 63 dr = kmalloc(total, gfp); 64 if (dr == NULL) 65 return (NULL); 66 67 INIT_LIST_HEAD(&dr->entry); 68 dr->release = release; 69 p = (void *)(dr+1); 70 71 return (p); 72 } 73 74 static void 75 lkpi_devres_free_dr(struct devres *dr) 76 { 77 78 /* 79 * We have no dev, so cannot lock. This means someone else has 80 * to do this prior to us if devres_add() had been called. 81 */ 82 KASSERT(list_empty_careful(&dr->entry), 83 ("%s: dr %p still on devres_head\n", __func__, dr)); 84 kfree(dr); 85 } 86 87 void 88 lkpi_devres_free(void *p) 89 { 90 struct devres *dr; 91 92 if (p == NULL) 93 return; 94 95 dr = container_of(p, struct devres, __drdata); 96 lkpi_devres_free_dr(dr); 97 } 98 99 void 100 lkpi_devres_add(struct device *dev, void *p) 101 { 102 struct devres *dr; 103 104 KASSERT(dev != NULL && p != NULL, ("%s: dev %p p %p\n", 105 __func__, dev, p)); 106 107 dr = container_of(p, struct devres, __drdata); 108 spin_lock(&dev->devres_lock); 109 list_add(&dr->entry, &dev->devres_head); 110 spin_unlock(&dev->devres_lock); 111 } 112 113 static struct devres * 114 lkpi_devres_find_dr(struct device *dev, void(*release)(struct device *, void *), 115 int (*match)(struct device *, void *, void *), void *mp) 116 { 117 struct devres *dr, *next; 118 void *p; 119 120 KASSERT(dev != NULL, ("%s: dev %p\n", __func__, dev)); 121 assert_spin_locked(&dev->devres_lock); 122 123 list_for_each_entry_safe(dr, next, &dev->devres_head, entry) { 124 if (dr->release != release) 125 continue; 126 p = (void *)(dr+1); 127 if (match != NULL && match(dev, p, mp) == false) 128 continue; 129 return (dr); 130 } 131 132 return (NULL); 133 } 134 135 void * 136 lkpi_devres_find(struct device *dev, void(*release)(struct device *, void *), 137 int (*match)(struct device *, void *, void *), void *mp) 138 { 139 struct devres *dr; 140 141 KASSERT(dev != NULL, ("%s: dev %p\n", __func__, dev)); 142 143 spin_lock(&dev->devres_lock); 144 dr = lkpi_devres_find_dr(dev, release, match, mp); 145 spin_unlock(&dev->devres_lock); 146 147 if (dr == NULL) 148 return (NULL); 149 150 return ((void *)(dr + 1)); 151 } 152 153 static void 154 lkpi_devres_unlink_locked(struct device *dev, struct devres *dr) 155 { 156 KASSERT(dev != NULL, ("%s: dev %p\n", __func__, dev)); 157 KASSERT(dr != NULL, ("%s: dr %p\n", __func__, dr)); 158 assert_spin_locked(&dev->devres_lock); 159 160 list_del_init(&dr->entry); 161 } 162 163 void 164 lkpi_devres_unlink(struct device *dev, void *p) 165 { 166 struct devres *dr; 167 168 KASSERT(dev != NULL && p != NULL, ("%s: dev %p p %p\n", 169 __func__, dev, p)); 170 171 dr = container_of(p, struct devres, __drdata); 172 spin_lock(&dev->devres_lock); 173 lkpi_devres_unlink_locked(dev, dr); 174 spin_unlock(&dev->devres_lock); 175 } 176 177 /* This is called on device free. */ 178 void 179 lkpi_devres_release_free_list(struct device *dev) 180 { 181 struct devres *dr, *next; 182 void *p; 183 184 /* Free any resources allocated on the device. */ 185 /* No need to lock anymore. */ 186 list_for_each_entry_safe(dr, next, &dev->devres_head, entry) { 187 p = (void *)(dr+1); 188 if (dr->release != NULL) 189 dr->release(dev, p); 190 /* This should probably be a function of some kind. */ 191 list_del_init(&dr->entry); 192 lkpi_devres_free(p); 193 } 194 } 195 196 int 197 lkpi_devres_destroy(struct device *dev, void(*release)(struct device *, void *), 198 int (*match)(struct device *, void *, void *), void *mp) 199 { 200 struct devres *dr; 201 202 spin_lock(&dev->devres_lock); 203 dr = lkpi_devres_find_dr(dev, release, match, mp); 204 if (dr != NULL) 205 lkpi_devres_unlink_locked(dev, dr); 206 spin_unlock(&dev->devres_lock); 207 208 if (dr == NULL) 209 return (-ENOENT); 210 lkpi_devres_free_dr(dr); 211 212 return (0); 213 } 214 215 /* 216 * Devres release function for k*malloc(). 217 * While there is nothing to do here adding, e.g., tracing would be 218 * possible so we leave the empty function here. 219 * Also good for documentation as it is the simplest example. 220 */ 221 void 222 lkpi_devm_kmalloc_release(struct device *dev __unused, void *p __unused) 223 { 224 225 /* Nothing to do. Freed with the devres. */ 226 } 227