aboutsummaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
Diffstat (limited to 'sys')
-rw-r--r--sys/compat/linuxkpi/common/include/linux/device.h42
-rw-r--r--sys/compat/linuxkpi/common/src/linux_compat.c29
-rw-r--r--sys/compat/linuxkpi/common/src/linux_devres.c226
-rw-r--r--sys/conf/files2
-rw-r--r--sys/modules/linuxkpi/Makefile1
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,7 +1865,10 @@ 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);
@@ -1873,6 +1876,26 @@ kvasprintf(gfp_t gfp, const char *fmt, va_list ap)
}
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, ...)
{
va_list ap;
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 bb6d622b1e9a..edca1003e904 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -4557,6 +4557,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 \