[U-Boot] [RFC PATCH 06/12] devres: introduce Devres (Managed Device Resource) framework

Heiko Schocher hs at denx.de
Wed Jul 8 07:07:45 CEST 2015


Hello Masahiro,

Am 08.07.2015 um 06:29 schrieb Masahiro Yamada:
> In U-Boot's driver model, memory is basically allocated and freed
> in the core framework.  So, low level drivers generally only have
> to specify the size of needed memory with .priv_auto_alloc_size,
> .platdata_auto_alloc_size, etc.  Nevertheless, some drivers still
> need to allocate memory on their own in case they cannot statically
> know how much memory is needed.  Moreover, I am afraid the failure
> paths of driver model core parts are getting messier as more and
> more memory size members are supported, .per_child_auto_alloc_size,
> .per_child_platdata_auto_alloc_size...  So, I believe it is
> reasonable enough to port Devres into U-boot.
>
> As you know, Devres, which originates in Linux, manages device
> resources for each device and automatically releases them on driver
> detach.  With devres, device resources are guaranteed to be freed
> whether initialization fails half-way or the device gets detached.
>
> The basic idea is totally the same to that of Linux, but I tweaked
> it a bit so that it fits in U-Boot's driver model.
>
> In U-Boot, drivers are activated in two steps: binding and probing.
> Binding puts a driver and a device together.  It is just data
> manipulation on the system memory, so nothing has happened on the
> hardware device at this moment.  When the device is really used, it
> is probed.  Probing initializes the real hardware device to make it
> really ready for use.
>
> So, the resources acquired during the probing process must be freed
> when the device is removed.  Likewise, what has been allocated in
> binding should be released when the device is unbound.  The struct
> devres has a member "probe" to remember when the resource was
> allocated.
>
> CONFIG_DEBUG_DEVRES is also supported for easier debugging.
> If enabled, debug messages are printed each time a resource is
> allocated/freed.
>
> Signed-off-by: Masahiro Yamada <yamada.masahiro at socionext.com>
> ---
>
>   drivers/core/Kconfig         |  10 ++++
>   drivers/core/Makefile        |   2 +-
>   drivers/core/device-remove.c |   5 ++
>   drivers/core/device.c        |   3 +
>   drivers/core/devres.c        | 137 +++++++++++++++++++++++++++++++++++++++++++
>   include/dm/device.h          |  17 ++++++
>   6 files changed, 173 insertions(+), 1 deletion(-)
>   create mode 100644 drivers/core/devres.c

sounds good, nice work. Do you have a statistic how the codesize
footprint is? In the long term it should save codesize I think.

bye,
Heiko
>
> diff --git a/drivers/core/Kconfig b/drivers/core/Kconfig
> index 2861b43..5966801 100644
> --- a/drivers/core/Kconfig
> +++ b/drivers/core/Kconfig
> @@ -55,3 +55,13 @@ config DM_SEQ_ALIAS
>   	  Most boards will have a '/aliases' node containing the path to
>   	  numbered devices (e.g. serial0 = &serial0). This feature can be
>   	  disabled if it is not required, to save code space in SPL.
> +
> +config DEBUG_DEVRES
> +	bool "Managed device resources verbose debug messages"
> +	depends on DM
> +	help
> +	  If this option is enabled, devres debug messages are printed.
> +	  Select this if you are having a problem with devres or want to
> +	  debug resource management for a managed device.
> +
> +	  If you are unsure about this, Say N here.
> diff --git a/drivers/core/Makefile b/drivers/core/Makefile
> index a3fec38..cd8c104 100644
> --- a/drivers/core/Makefile
> +++ b/drivers/core/Makefile
> @@ -4,7 +4,7 @@
>   # SPDX-License-Identifier:	GPL-2.0+
>   #
>
> -obj-$(CONFIG_DM)	+= device.o lists.o root.o uclass.o util.o
> +obj-$(CONFIG_DM)	+= device.o lists.o root.o uclass.o util.o devres.o
>   ifndef CONFIG_SPL_BUILD
>   obj-$(CONFIG_OF_CONTROL) += simple-bus.o
>   endif
> diff --git a/drivers/core/device-remove.c b/drivers/core/device-remove.c
> index 20b56f9..e1714b2 100644
> --- a/drivers/core/device-remove.c
> +++ b/drivers/core/device-remove.c
> @@ -109,6 +109,9 @@ int device_unbind(struct udevice *dev)
>
>   	if (dev->parent)
>   		list_del(&dev->sibling_node);
> +
> +	devres_release_all(dev);
> +
>   	free(dev);
>
>   	return 0;
> @@ -142,6 +145,8 @@ void device_free(struct udevice *dev)
>   			dev->parent_priv = NULL;
>   		}
>   	}
> +
> +	devres_release_probe(dev);
>   }
>
>   int device_remove(struct udevice *dev)
> diff --git a/drivers/core/device.c b/drivers/core/device.c
> index b954974..ac2c4f8 100644
> --- a/drivers/core/device.c
> +++ b/drivers/core/device.c
> @@ -47,6 +47,7 @@ int device_bind(struct udevice *parent, const struct driver *drv,
>   	INIT_LIST_HEAD(&dev->sibling_node);
>   	INIT_LIST_HEAD(&dev->child_head);
>   	INIT_LIST_HEAD(&dev->uclass_node);
> +	INIT_LIST_HEAD(&dev->devres_head);
>   	dev->platdata = platdata;
>   	dev->name = name;
>   	dev->of_offset = of_offset;
> @@ -170,6 +171,8 @@ fail_alloc2:
>   		dev->platdata = NULL;
>   	}
>   fail_alloc1:
> +	devres_release_all(dev);
> +
>   	free(dev);
>
>   	return ret;
> diff --git a/drivers/core/devres.c b/drivers/core/devres.c
> new file mode 100644
> index 0000000..2e967bf
> --- /dev/null
> +++ b/drivers/core/devres.c
> @@ -0,0 +1,137 @@
> +/*
> + * Copyright (C) 2015  Masahiro Yamada <yamada.masahiro at socionext.com>
> + *
> + * Based on the original work in Linux by
> + * Copyright (c) 2006  SUSE Linux Products GmbH
> + * Copyright (c) 2006  Tejun Heo <teheo at suse.de>
> + *
> + * SPDX-License-Identifier:	GPL-2.0+
> + */
> +
> +#include <common.h>
> +#include <linux/compat.h>
> +#include <linux/kernel.h>
> +#include <linux/list.h>
> +#include <dm/device.h>
> +
> +struct devres {
> +	struct list_head		entry;
> +	dr_release_t			release;
> +	bool				probe;
> +#ifdef CONFIG_DEBUG_DEVRES
> +	const char			*name;
> +	size_t				size;
> +#endif
> +	unsigned long long		data[];
> +};
> +
> +#ifdef CONFIG_DEBUG_DEVRES
> +
> +static void set_node_dbginfo(struct devres *dr, const char *name, size_t size)
> +{
> +	dr->name = name;
> +	dr->size = size;
> +}
> +
> +static void devres_log(struct udevice *dev, struct devres *dr,
> +		       const char *op)
> +{
> +	printf("%s: DEVRES %3s %p %s (%lu bytes)\n",
> +	       dev->name, op, dr, dr->name, (unsigned long)dr->size);
> +}
> +#else /* CONFIG_DEBUG_DEVRES */
> +#define set_node_dbginfo(dr, n, s)	do {} while (0)
> +#define devres_log(dev, dr, op)		do {} while (0)
> +#endif
> +
> +/**
> + * devres_alloc - Allocate device resource data
> + * @release: Release function devres will be associated with
> + * @size: Allocation size
> + * @gfp: Allocation flags
> + *
> + * Allocate devres of @size bytes.  The allocated area is zeroed, then
> + * associated with @release.  The returned pointer can be passed to
> + * other devres_*() functions.
> + *
> + * RETURNS:
> + * Pointer to allocated devres on success, NULL on failure.
> + */
> +#if CONFIG_DEBUG_DEVRES
> +void *__devres_alloc(dr_release_t release, size_t size, gfp_t gfp,
> +		     const char *name)
> +#else
> +void *devres_alloc(dr_release_t release, size_t size, gfp_t gfp)
> +#endif
> +{
> +	size_t tot_size = sizeof(struct devres) + size;
> +	struct devres *dr;
> +
> +	dr = kmalloc(tot_size, gfp);
> +	if (unlikely(!dr))
> +		return NULL;
> +
> +	INIT_LIST_HEAD(&dr->entry);
> +	dr->release = release;
> +	set_node_dbginfo(dr, name, size);
> +
> +	return dr->data;
> +}
> +
> +/**
> + * devres_add - Register device resource
> + * @dev: Device to add resource to
> + * @res: Resource to register
> + *
> + * Register devres @res to @dev.  @res should have been allocated
> + * using devres_alloc().  On driver detach, the associated release
> + * function will be invoked and devres will be freed automatically.
> + */
> +void devres_add(struct udevice *dev, void *res)
> +{
> +	struct devres *dr = container_of(res, struct devres, data);
> +
> +	devres_log(dev, dr, "ADD");
> +	BUG_ON(!list_empty(&dr->entry));
> +	dr->probe = dev->flags & DM_FLAG_BOUND ? true : false;
> +	list_add_tail(&dr->entry, &dev->devres_head);
> +}
> +
> +static void release_nodes(struct udevice *dev, struct list_head *head,
> +			  bool probe_only)
> +{
> +	struct devres *dr, *tmp;
> +
> +	list_for_each_entry_safe_reverse(dr, tmp, head, entry)  {
> +		if (probe_only && !dr->probe)
> +			break;
> +		devres_log(dev, dr, "REL");
> +		dr->release(dev, dr->data);
> +		list_del(&dr->entry);
> +		kfree(dr);
> +	}
> +}
> +
> +/**
> + * devres_release_probe - Release managed resources allocated after probing
> + * @dev: Device to release resources for
> + *
> + * Release all resources allocated for @dev when it was probed or later.
> + * This function is called on driver removal.
> + */
> +void devres_release_probe(struct udevice *dev)
> +{
> +	release_nodes(dev, &dev->devres_head, true);
> +}
> +
> +/**
> + * devres_release_all - Release all managed resources
> + * @dev: Device to release resources for
> + *
> + * Release all resources associated with @dev.  This function is
> + * called on driver unbinding.
> + */
> +void devres_release_all(struct udevice *dev)
> +{
> +	release_nodes(dev, &dev->devres_head, false);
> +}
> diff --git a/include/dm/device.h b/include/dm/device.h
> index 3674d19..7b39659 100644
> --- a/include/dm/device.h
> +++ b/include/dm/device.h
> @@ -96,6 +96,7 @@ struct udevice {
>   	uint32_t flags;
>   	int req_seq;
>   	int seq;
> +	struct list_head devres_head;
>   };
>
>   /* Maximum sequence number supported */
> @@ -449,4 +450,20 @@ bool device_has_active_children(struct udevice *dev);
>    */
>   bool device_is_last_sibling(struct udevice *dev);
>
> +/* device resource management */
> +typedef void (*dr_release_t)(struct udevice *dev, void *res);
> +
> +#ifdef CONFIG_DEBUG_DEVRES
> +void *__devres_alloc(dr_release_t release, size_t size, gfp_t gfp,
> +		     const char *name);
> +#define devres_alloc(release, size, gfp) \
> +	__devres_alloc(release, size, gfp, #release)
> +#else
> +void *devres_alloc(dr_release_t release, size_t size, gfp_t gfp);
> +#endif
> +void devres_free(void *res);
> +void devres_add(struct udevice *dev, void *res);
> +void devres_release_probe(struct udevice *dev);
> +void devres_release_all(struct udevice *dev);
> +
>   #endif
>

-- 
DENX Software Engineering GmbH,      Managing Director: Wolfgang Denk
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany


More information about the U-Boot mailing list