> bpftool: Use batch lookups for bounded hash map dumps
>
> Use BPF_MAP_LOOKUP_BATCH when dumping hash maps to reduce the number
> of BPF syscalls. Share element formatting with individual lookups to
> preserve plain, JSON and BTF output.
>
> Start with up to 256 entries and grow on ENOSPC without advancing the
> input cursor. Restrict the optimization to maps whose maximum key and
> value storage fits in 4 MiB, so even a worst-case bucket can fit without
> restarting a partially printed dump. Preserve aligned element buffers
> for formatting keys and values with odd sizes.
>
> Fall back to individual lookups only if the initial batch operation is
> unsupported. Process the final partial batch on ENOENT, but never use
> count or output buffers after other errors. Report errors after batch
> traversal starts without restarting and duplicating output.
>
> Send batch failure diagnostics to stderr so JSON element arrays contain
> only map entries.
This isn't a bug, but would it help to include performance numbers in the
commit message? The stated motivation is reducing the number of BPF
syscalls, but the last three paragraphs describe what each part of
dump_map_batch() does rather than showing how much the change gains. For
a patch whose purpose is syscall reduction, showing before and after
syscall counts or wall time for a representative dump would clarify the
benefit.
> diff --git a/tools/bpf/bpftool/map.c b/tools/bpf/bpftool/map.c
> index 684a8fb72414..ea6f1389245f 100644
> --- a/tools/bpf/bpftool/map.c
> +++ b/tools/bpf/bpftool/map.c
[ ... ]
> +#define MAP_DUMP_BATCH_SIZE 256U
> +#define MAP_DUMP_BATCH_MAX_BYTES (4 * 1024 * 1024)
> +
> +/* Return 1 to use individual lookups, but only before batch traversal
> starts. */
> +static int dump_map_batch(int fd, void *key, void *value,
> + struct bpf_map_info *info, struct btf *btf,
> + json_writer_t *wtr, unsigned int *num_elems)
> +{
> + __u32 capacity, count, batch = 0, next_batch = 0, i;
> + void *keys = NULL, *values = NULL, *buf;
> + bool first = true, can_fallback = true;
> + int err;
> +
> + /*
> + * Hash lookup batches must accommodate a whole bucket. Restrict the
> + * optimization to maps whose worst-case bucket fits the memory budget,
> + * so a later ENOSPC never forces a restart after printing some entries.
> + * Division also bounds the allocation multiplications on 32-bit hosts.
> + */
> + if (info->type != BPF_MAP_TYPE_HASH || !info->max_entries ||
> + (__u64)info->key_size + info->value_size >
> + MAP_DUMP_BATCH_MAX_BYTES / info->max_entries)
> + return 1;
This isn't a bug, but does the eligibility test have the intended effect
for large maps? The check disables batching when (key_size + value_size)
exceeds MAP_DUMP_BATCH_MAX_BYTES / max_entries. For a hash map with 1
million entries and key_size = value_size = 4, that division yields 4, so
the test rejects it and forces individual lookups. The batching code
allocates starting at MAP_DUMP_BATCH_SIZE (256) entries and grows on
ENOSPC, so the 4 MiB budget could instead cap how far capacity is allowed
to grow rather than disqualifying the map entirely. That way large hash
maps would still benefit from batching, and only a genuinely oversized
bucket would hit the fallback path.
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/34074207177