mirror of
https://xff.cz/git/u-boot/
synced 2025-09-26 04:51:17 +02:00
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
This commit is contained in:
@@ -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
|
||||
|
@@ -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))
|
||||
|
@@ -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;
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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)
|
||||
{
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user