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