aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/dm.c24
-rw-r--r--drivers/core/Kconfig11
-rw-r--r--drivers/core/dump.c73
-rw-r--r--drivers/core/tag.c18
-rw-r--r--include/dm/root.h2
-rw-r--r--include/dm/tag.h8
-rw-r--r--include/dm/util.h9
7 files changed, 144 insertions, 1 deletions
diff --git a/cmd/dm.c b/cmd/dm.c
index 0c7554a..eb40f08 100644
--- a/cmd/dm.c
+++ b/cmd/dm.c
@@ -8,6 +8,7 @@
#include <common.h>
#include <command.h>
+#include <dm/root.h>
#include <dm/util.h>
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 9b9a714..97dc699 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 e434fe0..1c1f7e4 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 2961725..a3c5cb7 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 382f83c..b2f30a8 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 1ea3c9f..745088f 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 c52daa8..e10c606 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