From 501a9a7ed803ac948adb392e541f9da9bafad60e Mon Sep 17 00:00:00 2001 From: Sean Anderson Date: Wed, 30 Mar 2022 12:26:40 -0400 Subject: dm: core: Use device_foreach_child where possible We have some nice macros for iterating over devices in device.h, but they are not used by the driver core. Convert all the users I could find. Signed-off-by: Sean Anderson --- drivers/core/device-remove.c | 4 ++-- drivers/core/device.c | 21 ++++++++++----------- drivers/core/devres.c | 2 +- drivers/core/dump.c | 2 +- 4 files changed, 14 insertions(+), 15 deletions(-) (limited to 'drivers/core') 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..7f71570a94 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; @@ -724,7 +723,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 +736,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 +747,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 +760,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 +789,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 +818,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 +896,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 +914,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 +931,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..fe97dca954 100644 --- a/drivers/core/dump.c +++ b/drivers/core/dump.c @@ -39,7 +39,7 @@ 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); } -- cgit v1.2.3 From d3eb1bf7cf242440c966ff20114abbe7f94c4a46 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Tue, 19 Apr 2022 18:46:36 +0200 Subject: dm: fix formatting of uclass dump Insert an empty line after each uclass independent of whether it has devices or not. Signed-off-by: Heinrich Schuchardt --- drivers/core/dump.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/core') diff --git a/drivers/core/dump.c b/drivers/core/dump.c index fe97dca954..21d9e7a91f 100644 --- a/drivers/core/dump.c +++ b/drivers/core/dump.c @@ -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++; -- cgit v1.2.3 From 1452870404804210db1d797ec046e24a99c101bf Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 8 May 2022 04:39:19 -0600 Subject: dm: core: Rename dm_dump_all() This is not a good name anymore as it does not dump everything. Rename it to dm_dump_tree() to avoid confusion. Signed-off-by: Simon Glass --- cmd/dm.c | 8 ++++---- drivers/core/dump.c | 2 +- include/dm/util.h | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/core') diff --git a/cmd/dm.c b/cmd/dm.c index ca609224f5..03674cd086 100644 --- a/cmd/dm.c +++ b/cmd/dm.c @@ -10,10 +10,10 @@ #include #include -static int do_dm_dump_all(struct cmd_tbl *cmdtp, int flag, int argc, - char *const argv[]) +static int do_dm_dump_tree(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) { - dm_dump_all(); + dm_dump_tree(); return 0; } @@ -70,7 +70,7 @@ static char dm_help_text[] = #endif U_BOOT_CMD_WITH_SUBCMDS(dm, "Driver model low level access", dm_help_text, - U_BOOT_SUBCMD_MKENT(tree, 1, 1, do_dm_dump_all), + U_BOOT_SUBCMD_MKENT(tree, 1, 1, do_dm_dump_tree), U_BOOT_SUBCMD_MKENT(uclass, 1, 1, do_dm_dump_uclass), U_BOOT_SUBCMD_MKENT(devres, 1, 1, do_dm_dump_devres), U_BOOT_SUBCMD_MKENT(drivers, 1, 1, do_dm_dump_drivers), diff --git a/drivers/core/dump.c b/drivers/core/dump.c index 21d9e7a91f..994e308f05 100644 --- a/drivers/core/dump.c +++ b/drivers/core/dump.c @@ -45,7 +45,7 @@ static void show_devices(struct udevice *dev, int depth, int last_flag) } } -void dm_dump_all(void) +void dm_dump_tree(void) { struct udevice *root; diff --git a/include/dm/util.h b/include/dm/util.h index 4428f045b7..c52daa87ef 100644 --- a/include/dm/util.h +++ b/include/dm/util.h @@ -25,7 +25,7 @@ struct list_head; int list_count_items(struct list_head *head); /* Dump out a tree of all devices */ -void dm_dump_all(void); +void dm_dump_tree(void); /* Dump out a list of uclasses and their devices */ void dm_dump_uclass(void); -- cgit v1.2.3 From c625666ea10d88141546807f1b720703ba710eea Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 8 May 2022 04:39:21 -0600 Subject: dm: core: Fix addresses in the dm static command This command converts pointers to addresses, but the pointers being converted are in the image's rodata region. For sandbox this means it is not in DRAM so it does not make sense to do this conversion. Fix this by showing a simple pointer instead. Drop the unnecessary @ and hex prefixes. Signed-off-by: Simon Glass --- drivers/core/dump.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/core') diff --git a/drivers/core/dump.c b/drivers/core/dump.c index 994e308f05..e434fe0472 100644 --- a/drivers/core/dump.c +++ b/drivers/core/dump.c @@ -169,8 +169,6 @@ 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); } -- cgit v1.2.3 From 930a3ddadebf3660cc3163081671de189300afdd Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 8 May 2022 04:39:24 -0600 Subject: dm: core: Support accessing core tags At present tag numbers are only allocated for non-core data, meaning that the 'core' data, like priv and plat, are accessed through dedicated functions. For debugging and consistency it is convenient to use tags for this 'core' data too. Add support for this, with new tag numbers and functions to access the pointer and size for each. Update one of the test drivers so that the uclass-private data can be tested here. There is some code duplication with functions like device_alloc_priv() but this is not addressed for now. At some point, some rationalisation may help to reduce code size, but more thought it needed on that. Signed-off-by: Simon Glass --- drivers/core/device.c | 65 ++++++++++++++++++++++++++++++++++++++++ drivers/misc/test_drv.c | 4 ++- include/dm/device.h | 25 ++++++++++++++++ include/dm/tag.h | 13 +++++++- test/dm/core.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++++ tools/dtoc/test_dtoc.py | 4 +++ 6 files changed, 189 insertions(+), 2 deletions(-) (limited to 'drivers/core') diff --git a/drivers/core/device.c b/drivers/core/device.c index 7f71570a94..d9ce546c0c 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -674,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) { diff --git a/drivers/misc/test_drv.c b/drivers/misc/test_drv.c index b6df118903..927618256f 100644 --- a/drivers/misc/test_drv.c +++ b/drivers/misc/test_drv.c @@ -108,7 +108,9 @@ UCLASS_DRIVER(testbus) = { .child_pre_probe = testbus_child_pre_probe_uclass, .child_post_probe = testbus_child_post_probe_uclass, - /* This is for dtoc testing only */ + .per_device_auto = sizeof(struct dm_test_uclass_priv), + + /* Note: this is for dtoc testing as well as tags*/ .per_device_plat_auto = sizeof(struct dm_test_uclass_plat), }; diff --git a/include/dm/device.h b/include/dm/device.h index 5bdb10653f..12c6ba37ff 100644 --- a/include/dm/device.h +++ b/include/dm/device.h @@ -11,6 +11,7 @@ #define _DM_DEVICE_H #include +#include #include #include #include @@ -546,6 +547,30 @@ void *dev_get_parent_priv(const struct udevice *dev); */ void *dev_get_uclass_priv(const struct udevice *dev); +/** + * dev_get_attach_ptr() - Get the value of an attached pointed tag + * + * The tag is assumed to hold a pointer, if it exists + * + * @dev: Device to look at + * @tag: Tag to access + * @return value of tag, or NULL if there is no tag of this type + */ +void *dev_get_attach_ptr(const struct udevice *dev, enum dm_tag_t tag); + +/** + * dev_get_attach_size() - Get the size of an attached tag + * + * Core tags have an automatic-allocation mechanism where the allocated size is + * defined by the device, parent or uclass. This returns the size associated + * with a particular tag + * + * @dev: Device to look at + * @tag: Tag to access + * @return size of auto-allocated data, 0 if none + */ +int dev_get_attach_size(const struct udevice *dev, enum dm_tag_t tag); + /** * dev_get_parent() - Get the parent of a device * diff --git a/include/dm/tag.h b/include/dm/tag.h index 54fc31eb15..9cb5d68f0a 100644 --- a/include/dm/tag.h +++ b/include/dm/tag.h @@ -13,8 +13,19 @@ struct udevice; enum dm_tag_t { + /* Types of core tags that can be attached to devices */ + DM_TAG_PLAT, + DM_TAG_PARENT_PLAT, + DM_TAG_UC_PLAT, + + DM_TAG_PRIV, + DM_TAG_PARENT_PRIV, + DM_TAG_UC_PRIV, + DM_TAG_DRIVER_DATA, + DM_TAG_ATTACH_COUNT, + /* EFI_LOADER */ - DM_TAG_EFI = 0, + DM_TAG_EFI = DM_TAG_ATTACH_COUNT, DM_TAG_COUNT, }; diff --git a/test/dm/core.c b/test/dm/core.c index ebd504427d..26e2fd5661 100644 --- a/test/dm/core.c +++ b/test/dm/core.c @@ -1275,3 +1275,83 @@ static int dm_test_uclass_find_device(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_uclass_find_device, UT_TESTF_SCAN_FDT); + +/* Test getting information about tags attached to devices */ +static int dm_test_dev_get_attach(struct unit_test_state *uts) +{ + struct udevice *dev; + + ut_assertok(uclass_first_device_err(UCLASS_TEST_FDT, &dev)); + ut_asserteq_str("a-test", dev->name); + + ut_assertnonnull(dev_get_attach_ptr(dev, DM_TAG_PLAT)); + ut_assertnonnull(dev_get_attach_ptr(dev, DM_TAG_PRIV)); + ut_assertnull(dev_get_attach_ptr(dev, DM_TAG_UC_PRIV)); + ut_assertnull(dev_get_attach_ptr(dev, DM_TAG_UC_PLAT)); + ut_assertnull(dev_get_attach_ptr(dev, DM_TAG_PARENT_PLAT)); + ut_assertnull(dev_get_attach_ptr(dev, DM_TAG_PARENT_PRIV)); + + ut_asserteq(sizeof(struct dm_test_pdata), + dev_get_attach_size(dev, DM_TAG_PLAT)); + ut_asserteq(sizeof(struct dm_test_priv), + dev_get_attach_size(dev, DM_TAG_PRIV)); + ut_asserteq(0, dev_get_attach_size(dev, DM_TAG_UC_PRIV)); + ut_asserteq(0, dev_get_attach_size(dev, DM_TAG_UC_PLAT)); + ut_asserteq(0, dev_get_attach_size(dev, DM_TAG_PARENT_PLAT)); + ut_asserteq(0, dev_get_attach_size(dev, DM_TAG_PARENT_PRIV)); + + return 0; +} +DM_TEST(dm_test_dev_get_attach, UT_TESTF_SCAN_FDT); + +/* Test getting information about tags attached to bus devices */ +static int dm_test_dev_get_attach_bus(struct unit_test_state *uts) +{ + struct udevice *dev, *child; + + ut_assertok(uclass_first_device_err(UCLASS_TEST_BUS, &dev)); + ut_asserteq_str("some-bus", dev->name); + + ut_assertnonnull(dev_get_attach_ptr(dev, DM_TAG_PLAT)); + ut_assertnonnull(dev_get_attach_ptr(dev, DM_TAG_PRIV)); + ut_assertnonnull(dev_get_attach_ptr(dev, DM_TAG_UC_PRIV)); + ut_assertnonnull(dev_get_attach_ptr(dev, DM_TAG_UC_PLAT)); + ut_assertnull(dev_get_attach_ptr(dev, DM_TAG_PARENT_PLAT)); + ut_assertnull(dev_get_attach_ptr(dev, DM_TAG_PARENT_PRIV)); + + ut_asserteq(sizeof(struct dm_test_pdata), + dev_get_attach_size(dev, DM_TAG_PLAT)); + ut_asserteq(sizeof(struct dm_test_priv), + dev_get_attach_size(dev, DM_TAG_PRIV)); + ut_asserteq(sizeof(struct dm_test_uclass_priv), + dev_get_attach_size(dev, DM_TAG_UC_PRIV)); + ut_asserteq(sizeof(struct dm_test_uclass_plat), + dev_get_attach_size(dev, DM_TAG_UC_PLAT)); + ut_asserteq(0, dev_get_attach_size(dev, DM_TAG_PARENT_PLAT)); + ut_asserteq(0, dev_get_attach_size(dev, DM_TAG_PARENT_PRIV)); + + /* Now try the child of the bus */ + ut_assertok(device_first_child_err(dev, &child)); + ut_asserteq_str("c-test@5", child->name); + + ut_assertnonnull(dev_get_attach_ptr(child, DM_TAG_PLAT)); + ut_assertnonnull(dev_get_attach_ptr(child, DM_TAG_PRIV)); + ut_assertnull(dev_get_attach_ptr(child, DM_TAG_UC_PRIV)); + ut_assertnull(dev_get_attach_ptr(child, DM_TAG_UC_PLAT)); + ut_assertnonnull(dev_get_attach_ptr(child, DM_TAG_PARENT_PLAT)); + ut_assertnonnull(dev_get_attach_ptr(child, DM_TAG_PARENT_PRIV)); + + ut_asserteq(sizeof(struct dm_test_pdata), + dev_get_attach_size(child, DM_TAG_PLAT)); + ut_asserteq(sizeof(struct dm_test_priv), + dev_get_attach_size(child, DM_TAG_PRIV)); + ut_asserteq(0, dev_get_attach_size(child, DM_TAG_UC_PRIV)); + ut_asserteq(0, dev_get_attach_size(child, DM_TAG_UC_PLAT)); + ut_asserteq(sizeof(struct dm_test_parent_plat), + dev_get_attach_size(child, DM_TAG_PARENT_PLAT)); + ut_asserteq(sizeof(struct dm_test_parent_data), + dev_get_attach_size(child, DM_TAG_PARENT_PRIV)); + + return 0; +} +DM_TEST(dm_test_dev_get_attach_bus, UT_TESTF_SCAN_FDT); diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py index 8bac207621..879ca2ab2b 100755 --- a/tools/dtoc/test_dtoc.py +++ b/tools/dtoc/test_dtoc.py @@ -618,6 +618,9 @@ u8 _denx_u_boot_test_bus_priv_some_bus[sizeof(struct dm_test_priv)] #include u8 _denx_u_boot_test_bus_ucplat_some_bus[sizeof(struct dm_test_uclass_plat)] \t__attribute__ ((section (".priv_data"))); +#include +u8 _denx_u_boot_test_bus_uc_priv_some_bus[sizeof(struct dm_test_uclass_priv)] + __attribute__ ((section (".priv_data"))); #include DM_DEVICE_INST(some_bus) = { @@ -628,6 +631,7 @@ DM_DEVICE_INST(some_bus) = { \t.driver_data\t= DM_TEST_TYPE_FIRST, \t.priv_\t\t= _denx_u_boot_test_bus_priv_some_bus, \t.uclass\t\t= DM_UCLASS_REF(testbus), +\t.uclass_priv_ = _denx_u_boot_test_bus_uc_priv_some_bus, \t.uclass_node\t= { \t\t.prev = &DM_UCLASS_REF(testbus)->dev_head, \t\t.next = &DM_UCLASS_REF(testbus)->dev_head, -- cgit v1.2.3 From 0dfda34ca594c701955cfcb71711a7599f97bae3 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 8 May 2022 04:39:25 -0600 Subject: dm: core: Add a way to collect memory usage Add a function for collecting the amount of memory used by driver model, including devices, uclasses and attached data and tags. This information can provide insights into how to reduce the memory required by driver model. Future work may look at execution speed also. Signed-off-by: Simon Glass --- drivers/core/root.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ drivers/core/tag.c | 11 +++++++++++ include/dm/root.h | 45 +++++++++++++++++++++++++++++++++++++++++++++ include/dm/tag.h | 11 +++++++++++ test/dm/core.c | 11 +++++++++++ 5 files changed, 131 insertions(+) (limited to 'drivers/core') 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..2961725b65 100644 --- a/drivers/core/tag.c +++ b/drivers/core/tag.c @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -137,3 +138,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); + } +} diff --git a/include/dm/root.h b/include/dm/root.h index e888fb993c..382f83c7f5 100644 --- a/include/dm/root.h +++ b/include/dm/root.h @@ -9,11 +9,49 @@ #ifndef _DM_ROOT_H_ #define _DM_ROOT_H_ +#include + struct udevice; /* Head of the uclass list if CONFIG_OF_PLATDATA_INST is enabled */ extern struct list_head uclass_head; +/** + * struct dm_stats - Information about driver model memory usage + * + * @total_size: All data + * @dev_count: Number of devices + * @dev_size: Size of all devices (just the struct udevice) + * @dev_name_size: Bytes used by device names + * @uc_count: Number of uclasses + * @uc_size: Size of all uclasses (just the struct uclass) + * @tag_count: Number of tags + * @tag_size: Bytes used by all tags + * @uc_attach_count: Number of uclasses with attached data (priv) + * @uc_attach_size: Total size of that attached data + * @attach_count_total: Total number of attached data items for all udevices and + * uclasses + * @attach_size_total: Total number of bytes of attached data + * @attach_count: Number of devices with attached, for each type + * @attach_size: Total number of bytes of attached data, for each type + */ +struct dm_stats { + int total_size; + int dev_count; + int dev_size; + int dev_name_size; + int uc_count; + int uc_size; + int tag_count; + int tag_size; + int uc_attach_count; + int uc_attach_size; + int attach_count_total; + int attach_size_total; + int attach_count[DM_TAG_ATTACH_COUNT]; + int attach_size[DM_TAG_ATTACH_COUNT]; +}; + /** * dm_root() - Return pointer to the top of the driver tree * @@ -141,4 +179,11 @@ static inline int dm_remove_devices_flags(uint flags) { return 0; } */ void dm_get_stats(int *device_countp, int *uclass_countp); +/** + * dm_get_mem() - Get stats on memory usage in driver model + * + * @mem: Place to put the information + */ +void dm_get_mem(struct dm_stats *stats); + #endif diff --git a/include/dm/tag.h b/include/dm/tag.h index 9cb5d68f0a..1ea3c9f7af 100644 --- a/include/dm/tag.h +++ b/include/dm/tag.h @@ -10,6 +10,7 @@ #include #include +struct dm_stats; struct udevice; enum dm_tag_t { @@ -118,4 +119,14 @@ int dev_tag_del(struct udevice *dev, enum dm_tag_t tag); */ int dev_tag_del_all(struct udevice *dev); +/** + * dev_tag_collect_stats() - Collect information on driver model performance + * + * This collects information on how driver model is performing. For now it only + * includes memory usage + * + * @stats: Place to put the collected information + */ +void dev_tag_collect_stats(struct dm_stats *stats); + #endif /* _DM_TAG_H */ diff --git a/test/dm/core.c b/test/dm/core.c index 26e2fd5661..fd4d756972 100644 --- a/test/dm/core.c +++ b/test/dm/core.c @@ -1355,3 +1355,14 @@ static int dm_test_dev_get_attach_bus(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_dev_get_attach_bus, UT_TESTF_SCAN_FDT); + +/* Test getting information about tags attached to bus devices */ +static int dm_test_dev_get_mem(struct unit_test_state *uts) +{ + struct dm_stats stats; + + dm_get_mem(&stats); + + return 0; +} +DM_TEST(dm_test_dev_get_mem, UT_TESTF_SCAN_FDT); -- cgit v1.2.3 From 2cb4ddb91ec9fcb77c895e4a1192a15aece700c6 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 8 May 2022 04:39:26 -0600 Subject: dm: core: Add a command to show driver model statistics This command shows the memory used by driver model along with various hints as to what it might be if some 'core' tags were moved to use the tag list instead of a core (i.e. always-there) pointer. This may help with future work to reduce memory usage. Signed-off-by: Simon Glass --- cmd/dm.c | 24 +++++++++++++++++ drivers/core/Kconfig | 11 ++++++++ drivers/core/dump.c | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++ drivers/core/tag.c | 18 +++++++++++++ include/dm/root.h | 2 +- include/dm/tag.h | 8 ++++++ include/dm/util.h | 9 +++++++ 7 files changed, 144 insertions(+), 1 deletion(-) (limited to 'drivers/core') diff --git a/cmd/dm.c b/cmd/dm.c index 0c7554a1b1..eb40f0865f 100644 --- a/cmd/dm.c +++ b/cmd/dm.c @@ -8,6 +8,7 @@ #include #include +#include #include static int do_dm_dump_driver_compat(struct cmd_tbl *cmdtp, int flag, int argc, @@ -34,6 +35,19 @@ static int do_dm_dump_drivers(struct cmd_tbl *cmdtp, int flag, int argc, return 0; } +#if CONFIG_IS_ENABLED(DM_STATS) +static int do_dm_dump_mem(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct dm_stats mem; + + dm_get_mem(&mem); + dm_dump_mem(&mem); + + return 0; +} +#endif /* DM_STATS */ + static int do_dm_dump_static_driver_info(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[]) { @@ -58,11 +72,20 @@ static int do_dm_dump_uclass(struct cmd_tbl *cmdtp, int flag, int argc, return 0; } +#if CONFIG_IS_ENABLED(DM_STATS) +#define DM_MEM_HELP "dm mem Provide a summary of memory usage\n" +#define DM_MEM U_BOOT_SUBCMD_MKENT(mem, 1, 1, do_dm_dump_mem), +#else +#define DM_MEM_HELP +#define DM_MEM +#endif + #if CONFIG_IS_ENABLED(SYS_LONGHELP) static char dm_help_text[] = "compat Dump list of drivers with compatibility strings\n" "dm devres Dump list of device resources for each device\n" "dm drivers Dump list of drivers with uclass and instances\n" + DM_MEM_HELP "dm static Dump list of drivers with static platform data\n" "dn tree Dump tree of driver model devices ('*' = activated)\n" "dm uclass Dump list of instances for each uclass" @@ -73,6 +96,7 @@ U_BOOT_CMD_WITH_SUBCMDS(dm, "Driver model low level access", dm_help_text, U_BOOT_SUBCMD_MKENT(compat, 1, 1, do_dm_dump_driver_compat), U_BOOT_SUBCMD_MKENT(devres, 1, 1, do_dm_dump_devres), U_BOOT_SUBCMD_MKENT(drivers, 1, 1, do_dm_dump_drivers), + DM_MEM U_BOOT_SUBCMD_MKENT(static, 1, 1, do_dm_dump_static_driver_info), U_BOOT_SUBCMD_MKENT(tree, 1, 1, do_dm_dump_tree), U_BOOT_SUBCMD_MKENT(uclass, 1, 1, do_dm_dump_uclass)); diff --git a/drivers/core/Kconfig b/drivers/core/Kconfig index 9b9a7148a1..97dc699e96 100644 --- a/drivers/core/Kconfig +++ b/drivers/core/Kconfig @@ -75,6 +75,17 @@ 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 DM_DEVICE_REMOVE bool "Support device removal" depends on DM diff --git a/drivers/core/dump.c b/drivers/core/dump.c index e434fe0472..1c1f7e4d30 100644 --- a/drivers/core/dump.c +++ b/drivers/core/dump.c @@ -172,3 +172,76 @@ void dm_dump_static_driver_info(void) 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/tag.c b/drivers/core/tag.c index 2961725b65..a3c5cb7e57 100644 --- a/drivers/core/tag.c +++ b/drivers/core/tag.c @@ -16,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; diff --git a/include/dm/root.h b/include/dm/root.h index 382f83c7f5..b2f30a842f 100644 --- a/include/dm/root.h +++ b/include/dm/root.h @@ -182,7 +182,7 @@ void dm_get_stats(int *device_countp, int *uclass_countp); /** * dm_get_mem() - Get stats on memory usage in driver model * - * @mem: Place to put the information + * @stats: Place to put the information */ void dm_get_mem(struct dm_stats *stats); diff --git a/include/dm/tag.h b/include/dm/tag.h index 1ea3c9f7af..745088ffcf 100644 --- a/include/dm/tag.h +++ b/include/dm/tag.h @@ -129,4 +129,12 @@ int dev_tag_del_all(struct udevice *dev); */ void dev_tag_collect_stats(struct dm_stats *stats); +/** + * tag_get_name() - Get the name of a tag + * + * @tag: Tag to look up, which must be valid + * Returns: Name of tag + */ +const char *tag_get_name(enum dm_tag_t tag); + #endif /* _DM_TAG_H */ diff --git a/include/dm/util.h b/include/dm/util.h index c52daa87ef..e10c6060ce 100644 --- a/include/dm/util.h +++ b/include/dm/util.h @@ -6,6 +6,8 @@ #ifndef __DM_UTIL_H #define __DM_UTIL_H +struct dm_stats; + #if CONFIG_IS_ENABLED(DM_WARN) #define dm_warn(fmt...) log(LOGC_DM, LOGL_WARNING, ##fmt) #else @@ -48,6 +50,13 @@ void dm_dump_driver_compat(void); /* Dump out a list of drivers with static platform data */ void dm_dump_static_driver_info(void); +/** + * dm_dump_mem() - Dump stats on memory usage in driver model + * + * @mem: Stats to dump + */ +void dm_dump_mem(struct dm_stats *stats); + #if CONFIG_IS_ENABLED(OF_PLATDATA_INST) && CONFIG_IS_ENABLED(READ_ONLY) void *dm_priv_to_rw(void *priv); #else -- cgit v1.2.3 From 4f6500aa1a62a80e8df2ffdf16fe4c3eabd99f1c Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 8 May 2022 04:39:27 -0600 Subject: dm: spl: Allow SPL to show memory usage Add an option to tell SPL to show memory usage for driver model just before it boots into the next phase. Signed-off-by: Simon Glass --- common/spl/spl.c | 9 +++++++++ drivers/core/Kconfig | 10 ++++++++++ 2 files changed, 19 insertions(+) (limited to 'drivers/core') diff --git a/common/spl/spl.c b/common/spl/spl.c index 2a69a7c932..dff4eef707 100644 --- a/common/spl/spl.c +++ b/common/spl/spl.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -780,6 +781,14 @@ void board_init_r(gd_t *dummy1, ulong dummy2) bootcount_inc(); + /* Dump driver model states to aid analysis */ + if (CONFIG_IS_ENABLED(DM_STATS)) { + struct dm_stats mem; + + dm_get_mem(&mem); + dm_dump_mem(&mem); + } + memset(&spl_image, '\0', sizeof(spl_image)); #ifdef CONFIG_SYS_SPL_ARGS_ADDR spl_image.arg = (void *)CONFIG_SYS_SPL_ARGS_ADDR; diff --git a/drivers/core/Kconfig b/drivers/core/Kconfig index 97dc699e96..99e28713f9 100644 --- a/drivers/core/Kconfig +++ b/drivers/core/Kconfig @@ -86,6 +86,16 @@ config DM_STATS 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 -- cgit v1.2.3