git: ff119f7c4a38 - stable/13 - LinuxKPI: implement devres() framework parts and two examples

Bjoern A. Zeeb bz at FreeBSD.org
Tue Feb 2 12:02:05 UTC 2021


The branch stable/13 has been updated by bz:

URL: https://cgit.FreeBSD.org/src/commit/?id=ff119f7c4a3894e20190cca37ca00c9fef88a6df

commit ff119f7c4a3894e20190cca37ca00c9fef88a6df
Author:     Bjoern A. Zeeb <bz at FreeBSD.org>
AuthorDate: 2021-01-28 16:32:43 +0000
Commit:     Bjoern A. Zeeb <bz at FreeBSD.org>
CommitDate: 2021-02-02 11:51:26 +0000

    LinuxKPI: implement devres() framework parts and two examples
    
    This code implements a version of the devres framework found
    working for various iwlwifi use cases and also providing functions
    for ttm_page_alloc_dma.c from DRM.
    
    Part of the framework replicates the consumed KPI, while others
    are internal helper functions.
    
    In addition the simple devm_k*malloc() consumers were implemented
    and kvasprintf() was enhanced to also work for the devm_kasprintf()
    case.
    Addmittingly lkpi_devm_kmalloc_release() could be avoided but for
    the overall understanding of the code and possible memory tracing
    it may still be helpful.
    
    Further devsres consumer are implemented for iwlwifi but will follow
    later as the main reason for this change is to sort out overlap with
    DRM.
    
    Sponsored-by:   The FreeBSD Foundation
    Obtained-from:  bz_iwlwifi
    MFC After:      3 days
    Reviewed-by:    hselasky, manu
    Differential Revision:  https://reviews.freebsd.org/D28189
    
    (cherry picked from commit fa765ca73e553399ffbad382e579e4c2b4d0fc12)
---
 sys/compat/linuxkpi/common/include/linux/device.h |  42 ++++
 sys/compat/linuxkpi/common/src/linux_compat.c     |  29 ++-
 sys/compat/linuxkpi/common/src/linux_devres.c     | 226 ++++++++++++++++++++++
 sys/conf/files                                    |   2 +
 sys/modules/linuxkpi/Makefile                     |   1 +
 5 files changed, 297 insertions(+), 3 deletions(-)

diff --git a/sys/compat/linuxkpi/common/include/linux/device.h b/sys/compat/linuxkpi/common/include/linux/device.h
index a55f6b0215ce..2ffe70f45c6e 100644
--- a/sys/compat/linuxkpi/common/include/linux/device.h
+++ b/sys/compat/linuxkpi/common/include/linux/device.h
@@ -562,5 +562,47 @@ dev_to_node(struct device *dev)
 
 char *kvasprintf(gfp_t, const char *, va_list);
 char *kasprintf(gfp_t, const char *, ...);
+char *lkpi_devm_kasprintf(struct device *, gfp_t, const char *, ...);
+
+#define	devm_kasprintf(_dev, _gfp, _fmt, ...)			\
+    lkpi_devm_kasprintf(_dev, _gfp, _fmt, ##__VA_ARGS__)
+
+void *lkpi_devres_alloc(void(*release)(struct device *, void *), size_t, gfp_t);
+void lkpi_devres_add(struct device *, void *);
+void lkpi_devres_free(void *);
+void *lkpi_devres_find(struct device *, void(*release)(struct device *, void *),
+    int (*match)(struct device *, void *, void *), void *);
+int lkpi_devres_destroy(struct device *, void(*release)(struct device *, void *),
+    int (*match)(struct device *, void *, void *), void *);
+#define	devres_alloc(_r, _s, _g)	lkpi_devres_alloc(_r, _s, _g)
+#define	devres_add(_d, _p)		lkpi_devres_add(_d, _p)
+#define	devres_free(_p)			lkpi_devres_free(_p)
+#define	devres_find(_d, _rfn, _mfn, _mp) \
+					lkpi_devres_find(_d, _rfn, _mfn, _mp)
+#define	devres_destroy(_d, _rfn, _mfn, _mp) \
+					lkpi_devres_destroy(_d, _rfn, _mfn, _mp)
+
+/* LinuxKPI internal functions. */
+void lkpi_devres_release_free_list(struct device *);
+void lkpi_devres_unlink(struct device *, void *);
+void lkpi_devm_kmalloc_release(struct device *, void *);
+
+static __inline void *
+devm_kmalloc(struct device *dev, size_t size, gfp_t gfp)
+{
+	void *p;
+
+	p = lkpi_devres_alloc(lkpi_devm_kmalloc_release, size, gfp);
+	if (p != NULL)
+		lkpi_devres_add(dev, p);
+
+	return (p);
+}
+
+#define	devm_kzalloc(_dev, _size, _gfp)				\
+    devm_kmalloc((_dev), (_size), (_gfp) | __GFP_ZERO)
+
+#define	devm_kcalloc(_dev, _sizen, _size, _gfp)			\
+    devm_kmalloc((_dev), ((_sizen) * (_size)), (_gfp) | __GFP_ZERO)
 
 #endif	/* _LINUX_DEVICE_H_ */
diff --git a/sys/compat/linuxkpi/common/src/linux_compat.c b/sys/compat/linuxkpi/common/src/linux_compat.c
index b73f6fed03af..2ad936311204 100644
--- a/sys/compat/linuxkpi/common/src/linux_compat.c
+++ b/sys/compat/linuxkpi/common/src/linux_compat.c
@@ -1854,8 +1854,8 @@ vunmap(void *addr)
 	kfree(vmmap);
 }
 
-char *
-kvasprintf(gfp_t gfp, const char *fmt, va_list ap)
+static char *
+devm_kvasprintf(struct device *dev, gfp_t gfp, const char *fmt, va_list ap)
 {
 	unsigned int len;
 	char *p;
@@ -1865,13 +1865,36 @@ kvasprintf(gfp_t gfp, const char *fmt, va_list ap)
 	len = vsnprintf(NULL, 0, fmt, aq);
 	va_end(aq);
 
-	p = kmalloc(len + 1, gfp);
+	if (dev != NULL)
+		p = devm_kmalloc(dev, len + 1, gfp);
+	else
+		p = kmalloc(len + 1, gfp);
 	if (p != NULL)
 		vsnprintf(p, len + 1, fmt, ap);
 
 	return (p);
 }
 
+char *
+kvasprintf(gfp_t gfp, const char *fmt, va_list ap)
+{
+
+	return (devm_kvasprintf(NULL, gfp, fmt, ap));
+}
+
+char *
+lkpi_devm_kasprintf(struct device *dev, gfp_t gfp, const char *fmt, ...)
+{
+	va_list ap;
+	char *p;
+
+	va_start(ap, fmt);
+	p = devm_kvasprintf(dev, gfp, fmt, ap);
+	va_end(ap);
+
+	return (p);
+}
+
 char *
 kasprintf(gfp_t gfp, const char *fmt, ...)
 {
diff --git a/sys/compat/linuxkpi/common/src/linux_devres.c b/sys/compat/linuxkpi/common/src/linux_devres.c
new file mode 100644
index 000000000000..96ff3e486d1d
--- /dev/null
+++ b/sys/compat/linuxkpi/common/src/linux_devres.c
@@ -0,0 +1,226 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2020-2021 The FreeBSD Foundation
+ *
+ * This software was developed by Bj\xc3\xb6rn Zeeb under sponsorship from
+ * the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+
+/*
+ * Linux devres KPI implementation.
+ */
+
+struct devres {
+	struct list_head	entry;
+	void			(*release)(struct device *, void *);
+
+	/* Must come last. */
+	uint8_t			__drdata[0] __aligned(CACHE_LINE_SIZE);
+};
+
+void *
+lkpi_devres_alloc(void(*release)(struct device *, void *),
+    size_t size, gfp_t gfp)
+{
+	void *p;
+	struct devres *dr;
+	size_t total;
+
+	if (size == 0)
+		return (NULL);
+
+	total = sizeof(*dr) + size;
+	dr = kmalloc(total, gfp);
+	if (dr == NULL)
+		return (NULL);
+
+	INIT_LIST_HEAD(&dr->entry);
+	dr->release = release;
+	p = (void *)(dr+1);
+
+	return (p);
+}
+
+static void
+lkpi_devres_free_dr(struct devres *dr)
+{
+
+	/*
+	 * We have no dev, so cannot lock.  This means someone else has
+	 * to do this prior to us if devres_add() had been called.
+	 */
+	KASSERT(list_empty_careful(&dr->entry),
+	    ("%s: dr %p still on devres_head\n", __func__, dr));
+	kfree(dr);
+}
+
+void
+lkpi_devres_free(void *p)
+{
+	struct devres *dr;
+
+	if (p == NULL)
+		return;
+
+	dr = container_of(p, struct devres, __drdata);
+	lkpi_devres_free_dr(dr);
+}
+
+void
+lkpi_devres_add(struct device *dev, void *p)
+{
+	struct devres *dr;
+
+	KASSERT(dev != NULL && p != NULL, ("%s: dev %p p %p\n",
+	    __func__, dev, p));
+
+	dr = container_of(p, struct devres, __drdata);
+	spin_lock(&dev->devres_lock);
+	list_add(&dr->entry, &dev->devres_head);
+	spin_unlock(&dev->devres_lock);
+}
+
+static struct devres *
+lkpi_devres_find_dr(struct device *dev, void(*release)(struct device *, void *),
+    int (*match)(struct device *, void *, void *), void *mp)
+{
+	struct devres *dr, *next;
+	void *p;
+
+	KASSERT(dev != NULL, ("%s: dev %p\n", __func__, dev));
+	assert_spin_locked(&dev->devres_lock);
+
+	list_for_each_entry_safe(dr, next, &dev->devres_head, entry) {
+		if (dr->release != release)
+			continue;
+		p = (void *)(dr+1);
+		if (match != NULL && match(dev, p, mp) == false)
+			continue;
+		return (dr);
+	}
+
+	return (NULL);
+}
+
+void *
+lkpi_devres_find(struct device *dev, void(*release)(struct device *, void *),
+    int (*match)(struct device *, void *, void *), void *mp)
+{
+	struct devres *dr;
+
+	KASSERT(dev != NULL, ("%s: dev %p\n", __func__, dev));
+
+	spin_lock(&dev->devres_lock);
+	dr = lkpi_devres_find_dr(dev, release, match, mp);
+	spin_unlock(&dev->devres_lock);
+
+	if (dr == NULL)
+		return (NULL);
+
+	return ((void *)(dr + 1));
+}
+
+static void
+lkpi_devres_unlink_locked(struct device *dev, struct devres *dr)
+{
+	KASSERT(dev != NULL, ("%s: dev %p\n", __func__, dev));
+	KASSERT(dr != NULL, ("%s: dr %p\n", __func__, dr));
+	assert_spin_locked(&dev->devres_lock);
+
+	list_del_init(&dr->entry);
+}
+
+void
+lkpi_devres_unlink(struct device *dev, void *p)
+{
+	struct devres *dr;
+
+	KASSERT(dev != NULL && p != NULL, ("%s: dev %p p %p\n",
+	    __func__, dev, p));
+
+	dr = container_of(p, struct devres, __drdata);
+	spin_lock(&dev->devres_lock);
+	lkpi_devres_unlink_locked(dev, dr);
+	spin_unlock(&dev->devres_lock);
+}
+
+/* This is called on device free. */
+void
+lkpi_devres_release_free_list(struct device *dev)
+{
+	struct devres *dr, *next;
+	void *p;
+
+	/* Free any resources allocated on the device. */
+	/* No need to lock anymore. */
+	list_for_each_entry_safe(dr, next, &dev->devres_head, entry) {
+		p = (void *)(dr+1);
+		if (dr->release != NULL)
+			dr->release(dev, p);
+		/* This should probably be a function of some kind. */
+		list_del_init(&dr->entry);
+		lkpi_devres_free(p);
+	}
+}
+
+int
+lkpi_devres_destroy(struct device *dev, void(*release)(struct device *, void *),
+    int (*match)(struct device *, void *, void *), void *mp)
+{
+	struct devres *dr;
+
+	spin_lock(&dev->devres_lock);
+	dr = lkpi_devres_find_dr(dev, release, match, mp);
+	if (dr != NULL)
+		lkpi_devres_unlink_locked(dev, dr);
+	spin_unlock(&dev->devres_lock);
+
+	if (dr == NULL)
+		return (-ENOENT);
+	lkpi_devres_free_dr(dr);
+
+	return (0);
+}
+
+/*
+ * Devres release function for k*malloc().
+ * While there is nothing to do here adding, e.g., tracing would be
+ * possible so we leave the empty function here.
+ * Also good for documentation as it is the simplest example.
+ */
+void
+lkpi_devm_kmalloc_release(struct device *dev __unused, void *p __unused)
+{
+
+	/* Nothing to do.  Freed with the devres. */
+}
diff --git a/sys/conf/files b/sys/conf/files
index c7f6e97a6172..f0bf574b3236 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -4556,6 +4556,8 @@ compat/linuxkpi/common/src/linux_compat.c	optional compat_linuxkpi \
 	compile-with "${LINUXKPI_C}"
 compat/linuxkpi/common/src/linux_current.c	optional compat_linuxkpi \
 	compile-with "${LINUXKPI_C}"
+compat/linuxkpi/common/src/linux_devres.c	optional compat_linuxkpi \
+	compile-with "${LINUXKPI_C}"
 compat/linuxkpi/common/src/linux_dmi.c		optional compat_linuxkpi \
 	compile-with "${LINUXKPI_C}"
 compat/linuxkpi/common/src/linux_firmware.c	optional compat_linuxkpi \
diff --git a/sys/modules/linuxkpi/Makefile b/sys/modules/linuxkpi/Makefile
index 7bb4080fa81d..4274d211b10e 100644
--- a/sys/modules/linuxkpi/Makefile
+++ b/sys/modules/linuxkpi/Makefile
@@ -4,6 +4,7 @@
 KMOD=	linuxkpi
 SRCS=	linux_compat.c \
 	linux_current.c \
+	linux_devres.c \
 	linux_dmi.c \
 	linux_firmware.c \
 	linux_hrtimer.c \


More information about the dev-commits-src-all mailing list