From 6df2159dd33976623785ef7b0e08bc23fa6b5845 Mon Sep 17 00:00:00 2001 From: claude-noether Date: Wed, 13 May 2026 11:00:20 +0000 Subject: [PATCH] fresnel-fourier iter7 Phase 7 fix-forward: data links connect pads not entities directly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Empirical Phase 7 verification revealed the algorithm bug: data links in MEDIA_IOC_G_TOPOLOGY connect PAD IDs, not entity IDs directly. My iter7 Phase 6 commit compared link source_id/sink_id against the proc entity_id, never matched → io_entity_ids stayed empty → interface lookup never fired → returns -1 → falls back to legacy hardcoded path. Topology dump on fresnel /dev/media0 (rkvdec) confirmed: - Entity 3 (rkvdec-proc) has function=0x4008 (DECODER) ✓ - Data link src=16777218 sink=16777220 — these are PAD ids (0x01000002, 0x01000004), NOT entity 3. - Interface link src=50331660 (interface) sink=1 (entity) — for interface links source/sink ARE entity IDs. Fix: resolve pads → entities via the topo.pads[] array. 1. Collect pads belonging to proc entity (via pads[].entity_id). 2. For each data link touching those pads, the OTHER pad's entity_id is an IO neighbor. 3. Find interface link to those IO entities (unchanged from prev). Also allocate topo.pads[] in the 2-call ioctl pattern. Signed-off-by: claude-noether --- src/request.c | 59 ++++++++++++++++++++++++++-------- tests/run_perf_binding_cell.sh | 20 ++++++------ 2 files changed, 57 insertions(+), 22 deletions(-) diff --git a/src/request.c b/src/request.c index f7b5154..d8f26d5 100644 --- a/src/request.c +++ b/src/request.c @@ -140,6 +140,7 @@ static int find_decoder_video_node_via_topology(int media_fd, struct media_v2_entity *entities = NULL; struct media_v2_interface *interfaces = NULL; struct media_v2_link *links = NULL; + struct media_v2_pad *pads = NULL; int ret = -1; unsigned int i, j; @@ -147,50 +148,81 @@ static int find_decoder_video_node_via_topology(int media_fd, if (ioctl(media_fd, MEDIA_IOC_G_TOPOLOGY, &topo) < 0) return -1; if (topo.num_entities == 0 || topo.num_interfaces == 0 || - topo.num_links == 0) + topo.num_links == 0 || topo.num_pads == 0) return -1; entities = calloc(topo.num_entities, sizeof *entities); interfaces = calloc(topo.num_interfaces, sizeof *interfaces); links = calloc(topo.num_links, sizeof *links); - if (!entities || !interfaces || !links) + pads = calloc(topo.num_pads, sizeof *pads); + if (!entities || !interfaces || !links || !pads) goto out; topo.ptr_entities = (uintptr_t)entities; topo.ptr_interfaces = (uintptr_t)interfaces; topo.ptr_links = (uintptr_t)links; + topo.ptr_pads = (uintptr_t)pads; if (ioctl(media_fd, MEDIA_IOC_G_TOPOLOGY, &topo) < 0) goto out; for (i = 0; i < topo.num_entities; i++) { uint32_t proc_id; + uint32_t proc_pad_ids[16]; uint32_t io_entity_ids[16]; + unsigned int proc_pad_count = 0; unsigned int io_count = 0; if (entities[i].function != MEDIA_ENT_F_PROC_VIDEO_DECODER) continue; proc_id = entities[i].id; - /* Step 2: collect data-link neighbors of the proc entity. */ + /* Step 2a: collect pads belonging to the proc entity. Data + * links connect PADs, not entities directly. */ + for (j = 0; j < topo.num_pads; j++) { + if (pads[j].entity_id != proc_id) + continue; + if (proc_pad_count < (sizeof proc_pad_ids / + sizeof proc_pad_ids[0])) + proc_pad_ids[proc_pad_count++] = pads[j].id; + } + + /* Step 2b: walk data links. For each link with either endpoint + * in proc_pad_ids[], the other endpoint is a pad belonging to + * an IO neighbor. Resolve that pad's entity_id via pads[]. */ for (j = 0; j < topo.num_links; j++) { - uint32_t other; + uint32_t other_pad = 0; + unsigned int k; if (links[j].flags & MEDIA_LNK_FL_INTERFACE_LINK) continue; - if (links[j].source_id == proc_id) - other = links[j].sink_id; - else if (links[j].sink_id == proc_id) - other = links[j].source_id; - else + for (k = 0; k < proc_pad_count; k++) { + if (links[j].source_id == proc_pad_ids[k]) + other_pad = links[j].sink_id; + else if (links[j].sink_id == proc_pad_ids[k]) + other_pad = links[j].source_id; + if (other_pad != 0) + break; + } + if (other_pad == 0) continue; - if (io_count < (sizeof io_entity_ids / - sizeof io_entity_ids[0])) - io_entity_ids[io_count++] = other; + /* Resolve other_pad to its entity_id. */ + for (k = 0; k < topo.num_pads; k++) { + if (pads[k].id != other_pad) + continue; + if (io_count < (sizeof io_entity_ids / + sizeof io_entity_ids[0])) + io_entity_ids[io_count++] = + pads[k].entity_id; + break; + } } /* Step 3-4: find an interface link from any IO entity neighbor; - * resolve devnode for the linked V4L_VIDEO interface. */ + * resolve devnode for the linked V4L_VIDEO interface. + * Interface links connect interfaces↔entities directly (not + * via pads), so source_id/sink_id is an entity ID on one side + * and an interface ID on the other. */ for (j = 0; j < topo.num_links; j++) { uint32_t intf_id = 0; unsigned int k; @@ -230,6 +262,7 @@ out: free(entities); free(interfaces); free(links); + free(pads); return ret; } diff --git a/tests/run_perf_binding_cell.sh b/tests/run_perf_binding_cell.sh index 683233a..d618311 100755 --- a/tests/run_perf_binding_cell.sh +++ b/tests/run_perf_binding_cell.sh @@ -171,17 +171,19 @@ run_consumer() { sleep 1 # Parse pidstat by header: locate the %CPU column index from the - # column-name row, then apply it to data rows. Robust across - # sysstat 12.x point releases (where column positions shift). + # column-name row (where any field equals "%CPU"), then apply it + # to data rows. Robust across sysstat 12.x point releases. + # pidstat default output has no '#' header marker — the header is + # the first row containing "%CPU" as a field. awk ' - # Header row: find which field is %CPU. - $1 == "#" { - for (i = 1; i <= NF; i++) if ($i == "%CPU") col = i - next + # Header row: any line where some field equals "%CPU". + !col { + for (i = 1; i <= NF; i++) if ($i == "%CPU") { col = i; next } } - # Data row: skip the average summary at end + blank lines. - col && NF >= col && $1 ~ /^[0-9]/ { - if ($col ~ /^[0-9]+(\.[0-9]+)?$/) print $col + # Data row: lines whose value at $col is numeric. Skip the + # trailing "Average" summary by requiring $col to parse cleanly. + col && NF >= col && $col ~ /^[0-9]+(\.[0-9]+)?$/ { + print $col } ' "$logdir/pidstat.log" >"$logdir/cpu_pct.log" || true