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
This commit is contained in:
parent
1fac2cb4d6
commit
fa765ca73e
@ -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_ */
|
||||
|
@ -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, ...)
|
||||
{
|
||||
|
226
sys/compat/linuxkpi/common/src/linux_devres.c
Normal file
226
sys/compat/linuxkpi/common/src/linux_devres.c
Normal file
@ -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. */
|
||||
}
|
@ -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 \
|
||||
|
@ -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 \
|
||||
|
Loading…
Reference in New Issue
Block a user