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