[PATCH 10/10] WIP: dm: core: Add a command to calculate memory usage

Simon Glass sjg at chromium.org
Sun Mar 27 22:26:22 CEST 2022


Driver model can use a lot of memory, as it is the core of all drivers
and devices in U-Boot. Add a command to show how much is in use, along
with the sizes of various data structures.

This patch can be used to analyse the impact of various potential changes
to driver model for SPL, none of which has been implemented. Most involve
shrinking the size of struct udevice, which is a particular problem on
64-bit machines since their pointers are so unnecessarily large in SPL.

To try this out, build and run on your board. You should see output for
SPL and U-Boot proper, like this:

   Struct sizes: udevice 90, driver 78, uclass 30, uc_driver 78
   Memory: device 11:990, device names 111, uclass a:1e0

   Attached type    Count   Size    Cur   Tags   Save
   ---------------  -----  -----  -----  -----  -----
   plat                 3     e0    990    914     7c (124)
   parent_plat          2     40    990    910     80 (128)
   uclass_plat          1     10    990    90c     84 (132)
   priv                 6    13d    990    920     70 (112)
   parent_priv          0      0    990    908     88 (136)
   uclass_priv          3     38    990    914     7c (124)
   driver_data          0      0    990    908     88 (136)
   uclass               0      0
   Attached total       f    2a5                   37c (892)
   tags                 0      0

   Total size: e15 (3605)

   With tags:       a99 (2713)
   - singly-linked: 901 (2305)
   - driver index:  88a (2186)
   - uclass index:  813 (2067)
   Drop device name (not SRAM): 111 (273)

Signed-off-by: Simon Glass <sjg at chromium.org>
---

 cmd/dm.c              | 15 ++++++++-
 common/spl/spl.c      |  9 ++++++
 drivers/core/device.c | 65 ++++++++++++++++++++++++++++++++++++++
 drivers/core/dump.c   | 73 +++++++++++++++++++++++++++++++++++++++++++
 drivers/core/root.c   | 53 +++++++++++++++++++++++++++++++
 drivers/core/tag.c    | 29 +++++++++++++++++
 include/dm/device.h   |  6 ++++
 include/dm/root.h     | 45 ++++++++++++++++++++++++++
 include/dm/tag.h      | 18 ++++++++++-
 include/dm/util.h     |  9 ++++++
 10 files changed, 320 insertions(+), 2 deletions(-)

diff --git a/cmd/dm.c b/cmd/dm.c
index 1dd19fe45b5..ec32a13b177 100644
--- a/cmd/dm.c
+++ b/cmd/dm.c
@@ -64,6 +64,17 @@ static int do_dm_dump_static_driver_info(struct cmd_tbl *cmdtp, int flag, int ar
 	return 0;
 }
 
+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;
+}
+
 static struct cmd_tbl test_commands[] = {
 	U_BOOT_CMD_MKENT(tree, 0, 1, do_dm_dump_all, "", ""),
 	U_BOOT_CMD_MKENT(uclass, 1, 1, do_dm_dump_uclass, "", ""),
@@ -71,6 +82,7 @@ static struct cmd_tbl test_commands[] = {
 	U_BOOT_CMD_MKENT(drivers, 1, 1, do_dm_dump_drivers, "", ""),
 	U_BOOT_CMD_MKENT(compat, 1, 1, do_dm_dump_driver_compat, "", ""),
 	U_BOOT_CMD_MKENT(static, 1, 1, do_dm_dump_static_driver_info, "", ""),
+	U_BOOT_CMD_MKENT(mem, 1, 1, do_dm_dump_mem, "", ""),
 };
 
 static __maybe_unused void dm_reloc(void)
@@ -114,5 +126,6 @@ U_BOOT_CMD(
 	"dm devres        Dump list of device resources for each device\n"
 	"dm drivers       Dump list of drivers with uclass and instances\n"
 	"dm compat        Dump list of drivers with compatibility strings\n"
-	"dm static        Dump list of drivers with static platform data"
+	"dm static        Dump list of drivers with static platform data\n"
+	"dm mem           Provide a summary of memory usage"
 );
diff --git a/common/spl/spl.c b/common/spl/spl.c
index b452d4feeb2..3182f774f13 100644
--- a/common/spl/spl.c
+++ b/common/spl/spl.c
@@ -32,6 +32,7 @@
 #include <malloc.h>
 #include <mapmem.h>
 #include <dm/root.h>
+#include <dm/util.h>
 #include <linux/compiler.h>
 #include <fdt_support.h>
 #include <bootcount.h>
@@ -743,6 +744,14 @@ void board_init_r(gd_t *dummy1, ulong dummy2)
 		}
 	}
 
+	/* Dump drive model states to aid analysis */
+	if (1) {
+		struct dm_stats mem;
+
+		dm_get_mem(&mem);
+		dm_dump_mem(&mem);
+	}
+
 #if CONFIG_IS_ENABLED(BOARD_INIT)
 	spl_board_init();
 #endif
diff --git a/drivers/core/device.c b/drivers/core/device.c
index 3ab2583df38..5d9dca8a81f 100644
--- a/drivers/core/device.c
+++ b/drivers/core/device.c
@@ -680,6 +680,71 @@ void *dev_get_parent_priv(const struct udevice *dev)
 	return dm_priv_to_rw(dev->parent_priv_);
 }
 
+void *dev_get_attach(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/core/dump.c b/drivers/core/dump.c
index f2f9cacc56c..5e51d06787a 100644
--- a/drivers/core/dump.c
+++ b/drivers/core/dump.c
@@ -176,3 +176,76 @@ void dm_dump_static_driver_info(void)
 		       (ulong)map_to_sysmem(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 e09c12f4d6e..4f83cb2b557 100644
--- a/drivers/core/root.c
+++ b/drivers/core/root.c
@@ -427,6 +427,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 6829bcd8806..d8042fa2c5b 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);
+	}
+}
diff --git a/include/dm/device.h b/include/dm/device.h
index e0f86f5df9f..d8193900b12 100644
--- a/include/dm/device.h
+++ b/include/dm/device.h
@@ -11,6 +11,7 @@
 #define _DM_DEVICE_H
 
 #include <dm/ofnode.h>
+#include <dm/tag.h>
 #include <dm/uclass-id.h>
 #include <fdtdec.h>
 #include <linker_lists.h>
@@ -18,6 +19,7 @@
 #include <linux/list.h>
 #include <linux/printk.h>
 
+struct dm_stats;
 struct driver_info;
 
 /* Driver is active (probed). Cleared when it is removed */
@@ -543,6 +545,10 @@ void *dev_get_parent_priv(const struct udevice *dev);
  */
 void *dev_get_uclass_priv(const struct udevice *dev);
 
+void *dev_get_attach(const struct udevice *dev, enum dm_tag_t tag);
+
+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/root.h b/include/dm/root.h
index e888fb993c0..382f83c7f5b 100644
--- a/include/dm/root.h
+++ b/include/dm/root.h
@@ -9,11 +9,49 @@
 #ifndef _DM_ROOT_H_
 #define _DM_ROOT_H_
 
+#include <dm/tag.h>
+
 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 54fc31eb153..3b1ea7576ee 100644
--- a/include/dm/tag.h
+++ b/include/dm/tag.h
@@ -10,11 +10,23 @@
 #include <linux/list.h>
 #include <linux/types.h>
 
+struct dm_stats;
 struct udevice;
 
 enum dm_tag_t {
+	/* Types of info 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,
 };
@@ -107,4 +119,8 @@ int dev_tag_del(struct udevice *dev, enum dm_tag_t tag);
  */
 int dev_tag_del_all(struct udevice *dev);
 
+const char *tag_get_name(enum dm_tag_t tag);
+
+void dev_tag_collect_stats(struct dm_stats *stats);
+
 #endif /* _DM_TAG_H */
diff --git a/include/dm/util.h b/include/dm/util.h
index 4428f045b72..b346ecfe042 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
-- 
2.35.1.1021.g381101b075-goog



More information about the U-Boot mailing list