Thanks for the review. I have expanded the local commit message to explain the FD tradeoff:
Holding every discovered inner-map FD open would make descriptor use grow with the number of maps and could exhaust RLIMIT_NOFILE. Keeping the selected map FDs open and queuing distinct inner IDs requires only one additional map FD while traversing them. Deferred ID resolution means a concurrently removed inner map can disappear before it is opened; the dump is explicitly not atomic. For hashmap__add() failure, I am keeping the specific negative errno. It is already reported with strerror(-err), and the command still returns failure. I agree that this differs from the nearby -1 paths, but it does not turn allocation failure into success. I prefer to retain the specific internal error unless a uniform command exit code is a maintainer requirement. This is not a claim that a shell preserves the negative errno: its exit status is limited to eight bits. For the repeated info lookup, an IDs-only array would not replace the second query: map_dump() needs the full bpf_map_info, including type, key/value sizes and BTF information. Caching full records is possible, but adds allocation and cleanup state. The extra query is once per selected root, not for every discovered inner map. I am retaining the simpler traversal state here; this can be optimized separately if that cost is shown to matter.
