summaryrefslogtreecommitdiff
path: root/drivers/core
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2022-07-08 21:39:07 +0300
committerTom Rini <trini@konsulko.com>2022-07-08 21:39:07 +0300
commit9ff4ce8abc627b8696c9bd6fd726dd1dbf4b9a5c (patch)
treed28c5d99d4996b080ec0c38f24a0232d611ea847 /drivers/core
parent7bc0be96f79344d7b103dd64c31be0574f7b39e9 (diff)
parente87da5704ffa6fc782d93d137fa30a37a5df3566 (diff)
downloadu-boot-9ff4ce8abc627b8696c9bd6fd726dd1dbf4b9a5c.tar.xz
Merge tag 'dm-pull-28jun22' of https://source.denx.de/u-boot/custodians/u-boot-dm into next
nman external-symbol improvements Driver model memory-usage reporting patman test-reporting improvements Add bloblist design goals
Diffstat (limited to 'drivers/core')
-rw-r--r--drivers/core/Kconfig21
-rw-r--r--drivers/core/device-remove.c4
-rw-r--r--drivers/core/device.c86
-rw-r--r--drivers/core/devres.c2
-rw-r--r--drivers/core/dump.c83
-rw-r--r--drivers/core/root.c53
-rw-r--r--drivers/core/tag.c29
7 files changed, 257 insertions, 21 deletions
diff --git a/drivers/core/Kconfig b/drivers/core/Kconfig
index d3fe1d4093..8eb0070d22 100644
--- a/drivers/core/Kconfig
+++ b/drivers/core/Kconfig
@@ -75,6 +75,27 @@ config DM_DEBUG
help
Say Y here if you want to compile in debug messages in DM core.
+config DM_STATS
+ bool "Collect and show driver model stats"
+ depends on DM
+ default y if SANDBOX
+ help
+ Enable this to collect and display memory statistics about driver
+ model. This can help to figure out where all the memory is going and
+ to find optimisations.
+
+ To display the memory stats, use the 'dm mem' command.
+
+config SPL_DM_STATS
+ bool "Collect and show driver model stats in SPL"
+ depends on DM_SPL
+ help
+ Enable this to collect and display memory statistics about driver
+ model. This can help to figure out where all the memory is going and
+ to find optimisations.
+
+ The stats are displayed just before SPL boots to the next phase.
+
config DM_DEVICE_REMOVE
bool "Support device removal"
depends on DM
diff --git a/drivers/core/device-remove.c b/drivers/core/device-remove.c
index 73d2e9e420..a86b9325dd 100644
--- a/drivers/core/device-remove.c
+++ b/drivers/core/device-remove.c
@@ -29,7 +29,7 @@ int device_chld_unbind(struct udevice *dev, struct driver *drv)
assert(dev);
- list_for_each_entry_safe(pos, n, &dev->child_head, sibling_node) {
+ device_foreach_child_safe(pos, n, dev) {
if (drv && (pos->driver != drv))
continue;
@@ -52,7 +52,7 @@ int device_chld_remove(struct udevice *dev, struct driver *drv,
assert(dev);
- list_for_each_entry_safe(pos, n, &dev->child_head, sibling_node) {
+ device_foreach_child_safe(pos, n, dev) {
int ret;
if (drv && (pos->driver != drv))
diff --git a/drivers/core/device.c b/drivers/core/device.c
index 3d7fbfe073..d9ce546c0c 100644
--- a/drivers/core/device.c
+++ b/drivers/core/device.c
@@ -284,8 +284,7 @@ int device_reparent(struct udevice *dev, struct udevice *new_parent)
assert(dev);
assert(new_parent);
- list_for_each_entry_safe(pos, n, &dev->parent->child_head,
- sibling_node) {
+ device_foreach_child_safe(pos, n, dev->parent) {
if (pos->driver != dev->driver)
continue;
@@ -675,6 +674,71 @@ void *dev_get_parent_priv(const struct udevice *dev)
return dm_priv_to_rw(dev->parent_priv_);
}
+void *dev_get_attach_ptr(const struct udevice *dev, enum dm_tag_t tag)
+{
+ switch (tag) {
+ case DM_TAG_PLAT:
+ return dev_get_plat(dev);
+ case DM_TAG_PARENT_PLAT:
+ return dev_get_parent_plat(dev);
+ case DM_TAG_UC_PLAT:
+ return dev_get_uclass_plat(dev);
+ case DM_TAG_PRIV:
+ return dev_get_priv(dev);
+ case DM_TAG_PARENT_PRIV:
+ return dev_get_parent_priv(dev);
+ case DM_TAG_UC_PRIV:
+ return dev_get_uclass_priv(dev);
+ default:
+ return NULL;
+ }
+}
+
+int dev_get_attach_size(const struct udevice *dev, enum dm_tag_t tag)
+{
+ const struct udevice *parent = dev_get_parent(dev);
+ const struct uclass *uc = dev->uclass;
+ const struct uclass_driver *uc_drv = uc->uc_drv;
+ const struct driver *parent_drv = NULL;
+ int size = 0;
+
+ if (parent)
+ parent_drv = parent->driver;
+
+ switch (tag) {
+ case DM_TAG_PLAT:
+ size = dev->driver->plat_auto;
+ break;
+ case DM_TAG_PARENT_PLAT:
+ if (parent) {
+ size = parent_drv->per_child_plat_auto;
+ if (!size)
+ size = parent->uclass->uc_drv->per_child_plat_auto;
+ }
+ break;
+ case DM_TAG_UC_PLAT:
+ size = uc_drv->per_device_plat_auto;
+ break;
+ case DM_TAG_PRIV:
+ size = dev->driver->priv_auto;
+ break;
+ case DM_TAG_PARENT_PRIV:
+ if (parent) {
+ size = parent_drv->per_child_auto;
+ if (!size)
+ size = parent->uclass->uc_drv->per_child_auto;
+ }
+ break;
+ case DM_TAG_UC_PRIV:
+ size = uc_drv->per_device_auto;
+ break;
+ default:
+ break;
+ }
+
+ return size;
+}
+
static int device_get_device_tail(struct udevice *dev, int ret,
struct udevice **devp)
{
@@ -724,7 +788,7 @@ int device_get_child(const struct udevice *parent, int index,
{
struct udevice *dev;
- list_for_each_entry(dev, &parent->child_head, sibling_node) {
+ device_foreach_child(dev, parent) {
if (!index--)
return device_get_device_tail(dev, 0, devp);
}
@@ -737,7 +801,7 @@ int device_get_child_count(const struct udevice *parent)
struct udevice *dev;
int count = 0;
- list_for_each_entry(dev, &parent->child_head, sibling_node)
+ device_foreach_child(dev, parent)
count++;
return count;
@@ -748,7 +812,7 @@ int device_get_decendent_count(const struct udevice *parent)
const struct udevice *dev;
int count = 1;
- list_for_each_entry(dev, &parent->child_head, sibling_node)
+ device_foreach_child(dev, parent)
count += device_get_decendent_count(dev);
return count;
@@ -761,7 +825,7 @@ int device_find_child_by_seq(const struct udevice *parent, int seq,
*devp = NULL;
- list_for_each_entry(dev, &parent->child_head, sibling_node) {
+ device_foreach_child(dev, parent) {
if (dev->seq_ == seq) {
*devp = dev;
return 0;
@@ -790,7 +854,7 @@ int device_find_child_by_of_offset(const struct udevice *parent, int of_offset,
*devp = NULL;
- list_for_each_entry(dev, &parent->child_head, sibling_node) {
+ device_foreach_child(dev, parent) {
if (dev_of_offset(dev) == of_offset) {
*devp = dev;
return 0;
@@ -819,7 +883,7 @@ static struct udevice *_device_find_global_by_ofnode(struct udevice *parent,
if (ofnode_equal(dev_ofnode(parent), ofnode))
return parent;
- list_for_each_entry(dev, &parent->child_head, sibling_node) {
+ device_foreach_child(dev, parent) {
found = _device_find_global_by_ofnode(dev, ofnode);
if (found)
return found;
@@ -897,7 +961,7 @@ int device_find_first_inactive_child(const struct udevice *parent,
struct udevice *dev;
*devp = NULL;
- list_for_each_entry(dev, &parent->child_head, sibling_node) {
+ device_foreach_child(dev, parent) {
if (!device_active(dev) &&
device_get_uclass_id(dev) == uclass_id) {
*devp = dev;
@@ -915,7 +979,7 @@ int device_find_first_child_by_uclass(const struct udevice *parent,
struct udevice *dev;
*devp = NULL;
- list_for_each_entry(dev, &parent->child_head, sibling_node) {
+ device_foreach_child(dev, parent) {
if (device_get_uclass_id(dev) == uclass_id) {
*devp = dev;
return 0;
@@ -932,7 +996,7 @@ int device_find_child_by_namelen(const struct udevice *parent, const char *name,
*devp = NULL;
- list_for_each_entry(dev, &parent->child_head, sibling_node) {
+ device_foreach_child(dev, parent) {
if (!strncmp(dev->name, name, len) &&
strlen(dev->name) == len) {
*devp = dev;
diff --git a/drivers/core/devres.c b/drivers/core/devres.c
index 313ddc7089..78914bdf7f 100644
--- a/drivers/core/devres.c
+++ b/drivers/core/devres.c
@@ -232,7 +232,7 @@ static void dump_resources(struct udevice *dev, int depth)
(unsigned long)dr->size, dr->name,
devres_phase_name[dr->phase]);
- list_for_each_entry(child, &dev->child_head, sibling_node)
+ device_foreach_child(child, dev)
dump_resources(child, depth + 1);
}
diff --git a/drivers/core/dump.c b/drivers/core/dump.c
index f2f9cacc56..1c1f7e4d30 100644
--- a/drivers/core/dump.c
+++ b/drivers/core/dump.c
@@ -39,13 +39,13 @@ static void show_devices(struct udevice *dev, int depth, int last_flag)
printf("%s\n", dev->name);
- list_for_each_entry(child, &dev->child_head, sibling_node) {
+ device_foreach_child(child, dev) {
is_last = list_is_last(&child->sibling_node, &dev->child_head);
show_devices(child, depth + 1, (last_flag << 1) | is_last);
}
}
-void dm_dump_all(void)
+void dm_dump_tree(void)
{
struct udevice *root;
@@ -89,8 +89,6 @@ void dm_dump_uclass(void)
continue;
printf("uclass %d: %s\n", id, uc->uc_drv->name);
- if (list_empty(&uc->dev_head))
- continue;
uclass_foreach_dev(dev, uc) {
dm_display_line(dev, i);
i++;
@@ -171,8 +169,79 @@ void dm_dump_static_driver_info(void)
puts("Driver Address\n");
puts("---------------------------------\n");
- for (entry = drv; entry != drv + n_ents; entry++) {
- printf("%-25.25s @%08lx\n", entry->name,
- (ulong)map_to_sysmem(entry->plat));
+ for (entry = drv; entry != drv + n_ents; entry++)
+ printf("%-25.25s %p\n", entry->name, entry->plat);
+}
+
+void dm_dump_mem(struct dm_stats *stats)
+{
+ int total, total_delta;
+ int i;
+
+ /* Support SPL printf() */
+ printf("Struct sizes: udevice %x, driver %x, uclass %x, uc_driver %x\n",
+ (int)sizeof(struct udevice), (int)sizeof(struct driver),
+ (int)sizeof(struct uclass), (int)sizeof(struct uclass_driver));
+ printf("Memory: device %x:%x, device names %x, uclass %x:%x\n",
+ stats->dev_count, stats->dev_size, stats->dev_name_size,
+ stats->uc_count, stats->uc_size);
+ printf("\n");
+ printf("%-15s %5s %5s %5s %5s %5s\n", "Attached type", "Count",
+ "Size", "Cur", "Tags", "Save");
+ printf("%-15s %5s %5s %5s %5s %5s\n", "---------------", "-----",
+ "-----", "-----", "-----", "-----");
+ total_delta = 0;
+ for (i = 0; i < DM_TAG_ATTACH_COUNT; i++) {
+ int cur_size, new_size, delta;
+
+ cur_size = stats->dev_count * sizeof(struct udevice);
+ new_size = stats->dev_count * (sizeof(struct udevice) -
+ sizeof(void *));
+ /*
+ * Let's assume we can fit each dmtag_node into 32 bits. We can
+ * limit the 'tiny tags' feature to SPL with
+ * CONFIG_SPL_SYS_MALLOC_F_LEN <= 64KB, so needing 14 bits to
+ * point to anything in that region (with 4-byte alignment).
+ * So:
+ * 4 bits for tag
+ * 14 bits for offset of dev
+ * 14 bits for offset of data
+ */
+ new_size += stats->attach_count[i] * sizeof(u32);
+ delta = cur_size - new_size;
+ total_delta += delta;
+ printf("%-16s %5x %6x %6x %6x %6x (%d)\n", tag_get_name(i),
+ stats->attach_count[i], stats->attach_size[i],
+ cur_size, new_size, delta > 0 ? delta : 0, delta);
}
+ printf("%-16s %5x %6x\n", "uclass", stats->uc_attach_count,
+ stats->uc_attach_size);
+ printf("%-16s %5x %6x %5s %5s %6x (%d)\n", "Attached total",
+ stats->attach_count_total + stats->uc_attach_count,
+ stats->attach_size_total + stats->uc_attach_size, "", "",
+ total_delta > 0 ? total_delta : 0, total_delta);
+ printf("%-16s %5x %6x\n", "tags", stats->tag_count, stats->tag_size);
+ printf("\n");
+ printf("Total size: %x (%d)\n", stats->total_size, stats->total_size);
+ printf("\n");
+
+ total = stats->total_size;
+ total -= total_delta;
+ printf("With tags: %x (%d)\n", total, total);
+
+ /* Use singly linked lists in struct udevice (3 nodes in each) */
+ total -= sizeof(void *) * 3 * stats->dev_count;
+ printf("- singly-linked: %x (%d)\n", total, total);
+
+ /* Use an index into the struct_driver list instead of a pointer */
+ total = total + stats->dev_count * (1 - sizeof(void *));
+ printf("- driver index: %x (%d)\n", total, total);
+
+ /* Same with the uclass */
+ total = total + stats->dev_count * (1 - sizeof(void *));
+ printf("- uclass index: %x (%d)\n", total, total);
+
+ /* Drop the device name */
+ printf("Drop device name (not SRAM): %x (%d)\n", stats->dev_name_size,
+ stats->dev_name_size);
}
diff --git a/drivers/core/root.c b/drivers/core/root.c
index 17dd1205a3..f24ddfa521 100644
--- a/drivers/core/root.c
+++ b/drivers/core/root.c
@@ -449,6 +449,59 @@ void dm_get_stats(int *device_countp, int *uclass_countp)
*uclass_countp = uclass_get_count();
}
+void dev_collect_stats(struct dm_stats *stats, const struct udevice *parent)
+{
+ const struct udevice *dev;
+ int i;
+
+ stats->dev_count++;
+ stats->dev_size += sizeof(struct udevice);
+ stats->dev_name_size += strlen(parent->name) + 1;
+ for (i = 0; i < DM_TAG_ATTACH_COUNT; i++) {
+ int size = dev_get_attach_size(parent, i);
+
+ if (size ||
+ (i == DM_TAG_DRIVER_DATA && parent->driver_data)) {
+ stats->attach_count[i]++;
+ stats->attach_size[i] += size;
+ stats->attach_count_total++;
+ stats->attach_size_total += size;
+ }
+ }
+
+ list_for_each_entry(dev, &parent->child_head, sibling_node)
+ dev_collect_stats(stats, dev);
+}
+
+void uclass_collect_stats(struct dm_stats *stats)
+{
+ struct uclass *uc;
+
+ list_for_each_entry(uc, gd->uclass_root, sibling_node) {
+ int size;
+
+ stats->uc_count++;
+ stats->uc_size += sizeof(struct uclass);
+ size = uc->uc_drv->priv_auto;
+ if (size) {
+ stats->uc_attach_count++;
+ stats->uc_attach_size += size;
+ }
+ }
+}
+
+void dm_get_mem(struct dm_stats *stats)
+{
+ memset(stats, '\0', sizeof(*stats));
+ dev_collect_stats(stats, gd->dm_root);
+ uclass_collect_stats(stats);
+ dev_tag_collect_stats(stats);
+
+ stats->total_size = stats->dev_size + stats->uc_size +
+ stats->attach_size_total + stats->uc_attach_size +
+ stats->tag_size;
+}
+
#ifdef CONFIG_ACPIGEN
static int root_acpi_get_name(const struct udevice *dev, char *out_name)
{
diff --git a/drivers/core/tag.c b/drivers/core/tag.c
index 22999193a5..a3c5cb7e57 100644
--- a/drivers/core/tag.c
+++ b/drivers/core/tag.c
@@ -6,6 +6,7 @@
#include <malloc.h>
#include <asm/global_data.h>
+#include <dm/root.h>
#include <dm/tag.h>
#include <linux/err.h>
#include <linux/list.h>
@@ -15,6 +16,24 @@ struct udevice;
DECLARE_GLOBAL_DATA_PTR;
+static const char *const tag_name[] = {
+ [DM_TAG_PLAT] = "plat",
+ [DM_TAG_PARENT_PLAT] = "parent_plat",
+ [DM_TAG_UC_PLAT] = "uclass_plat",
+
+ [DM_TAG_PRIV] = "priv",
+ [DM_TAG_PARENT_PRIV] = "parent_priv",
+ [DM_TAG_UC_PRIV] = "uclass_priv",
+ [DM_TAG_DRIVER_DATA] = "driver_data",
+
+ [DM_TAG_EFI] = "efi",
+};
+
+const char *tag_get_name(enum dm_tag_t tag)
+{
+ return tag_name[tag];
+}
+
int dev_tag_set_ptr(struct udevice *dev, enum dm_tag_t tag, void *ptr)
{
struct dmtag_node *node;
@@ -137,3 +156,13 @@ int dev_tag_del_all(struct udevice *dev)
return -ENOENT;
}
+
+void dev_tag_collect_stats(struct dm_stats *stats)
+{
+ struct dmtag_node *node;
+
+ list_for_each_entry(node, &gd->dmtag_list, sibling) {
+ stats->tag_count++;
+ stats->tag_size += sizeof(struct dmtag_node);
+ }
+}