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