From: Alan Maguire <alan.maguire@oracle.com>
To: ast@kernel.org, andrii@kernel.org
Cc: daniel@iogearbox.net, eddyz87@gmail.com, jolsa@kernel.org,
ihor.solodrai@linux.dev, yonghong.song@linux.dev,
song@kernel.org, qmo@kernel.org, martin.lau@linux.dev,
memxor@gmail.com, emil@etsalapatis.com, mcgrof@kernel.org,
petr.pavlu@suse.com, tj@kernel.org, kees@kernel.org,
bpf@vger.kernel.org, nathan@kernel.org, nsc@kernel.org,
arnd@arndb.de, puranjay@kernel.org, yatsenko@meta.com,
atenart@kernel.org, ojeda@kernel.org,
linux-modules@vger.kernel.org,
Alan Maguire <alan.maguire@oracle.com>
Subject: [PATCH v2 bpf-next 12/18] resolve_btfids: Extract inline BTF
Date: Tue, 1 Sep 2026 17:57:51 +0100 [thread overview]
Message-ID: <20260901165757.801449-13-alan.maguire@oracle.com> (raw)
In-Reply-To: <20260901165757.801449-1-alan.maguire@oracle.com>
Add an --inline option to extract LOC_PARAM, LOC_PROTO and LOCSEC
types to a new inline split .BTF.inline while sorting BTF.
Move LOC_PARAM, LOC_PROTO, and LOCSEC types to the split BTF. Also move
a FUNC when it is referenced only by LOCSEC, has no defined STT_FUNC
symbol, and is not used by .BTF_ids. Move its FUNC_PROTO when no
retained BTF type references that prototype.
Collect defined ELF function symbols to distinguish fully inlined
functions from functions that still have an out-of-line implementation.
The resulting .BTF.inline therefore contains only fully inlined FUNCs
(or any which have name collisions with other uninlined functions).
Sort types so transferred FUNCs, eligible FUNC_PROTOs, and LOC* types
form a self-contained split BTF. Sort LOCSEC records by projected
function BTF ID, instruction offset, and location-prototype ID before
permuting, preserving lookup order after IDs are remapped.
The end state is
- strings are moved to split BTF along with inline-related types
when they do not have another consumer in vmlinux BTF; the goal
is to minimum cost for vmlinux of inline info
- partially-inlined functions referred to in LOCSEC are in vmlinux BTF
- fully inlined functions are in vmlinux inline BTF
- LOCSEC data is sorted by FUNC type id as the pattern for inline
tracers will be to look up the function by name; once that is
done a binary search based on FUNC type id can quickly identify
relevant entries in LOCSEC
Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
---
tools/bpf/resolve_btfids/main.c | 336 +++++++++++++++++++++++++++++++-
1 file changed, 330 insertions(+), 6 deletions(-)
diff --git a/tools/bpf/resolve_btfids/main.c b/tools/bpf/resolve_btfids/main.c
index 37d7e7224207..20b228c4d678 100644
--- a/tools/bpf/resolve_btfids/main.c
+++ b/tools/bpf/resolve_btfids/main.c
@@ -142,7 +142,9 @@ struct object {
struct btf *btf;
struct btf *base_btf;
+ struct btf *inline_btf;
bool distill_base;
+ bool extract_inline;
struct {
int fd;
@@ -154,6 +156,10 @@ struct object {
size_t strtabidx;
unsigned long idlist_addr;
int encoding;
+ const char **func_symbols;
+ u32 func_symbols_cnt;
+ u32 func_symbols_cap;
+ bool func_symbols_collected;
} efile;
struct rb_root sets;
@@ -572,6 +578,58 @@ static const char *find_name_by_addr(struct object *obj, Elf64_Addr addr)
return res ? res->name : NULL;
}
+static int cmp_func_symbol(const void *a, const void *b)
+{
+ const char * const *name = a;
+ const char * const *other = b;
+
+ return strcmp(*name, *other);
+}
+
+static int collect_func_symbols(struct object *obj)
+{
+ Elf_Scn *scn;
+ GElf_Shdr sh;
+ int n, i;
+
+ if (obj->efile.symbols_shndx == -1)
+ return 0;
+
+ scn = elf_getscn(obj->efile.elf, obj->efile.symbols_shndx);
+ if (!scn || gelf_getshdr(scn, &sh) != &sh)
+ return -EINVAL;
+ n = sh.sh_size / sh.sh_entsize;
+
+ for (i = 0; i < n; i++) {
+ GElf_Sym sym;
+ const char *name;
+
+ if (!gelf_getsym(obj->efile.symbols, i, &sym))
+ return -EINVAL;
+ if (GELF_ST_TYPE(sym.st_info) != STT_FUNC ||
+ sym.st_shndx == SHN_UNDEF || !sym.st_name)
+ continue;
+ name = elf_strptr(obj->efile.elf, obj->efile.strtabidx, sym.st_name);
+ if (!name)
+ return -EINVAL;
+ if (ensure_mem(&obj->efile.func_symbols, &obj->efile.func_symbols_cap,
+ obj->efile.func_symbols_cnt + 1))
+ return -ENOMEM;
+ obj->efile.func_symbols[obj->efile.func_symbols_cnt++] = name;
+ }
+
+ qsort(obj->efile.func_symbols, obj->efile.func_symbols_cnt,
+ sizeof(*obj->efile.func_symbols), cmp_func_symbol);
+ obj->efile.func_symbols_collected = true;
+ return 0;
+}
+
+static bool has_func_symbol(const struct object *obj, const char *name)
+{
+ return bsearch(&name, obj->efile.func_symbols, obj->efile.func_symbols_cnt,
+ sizeof(*obj->efile.func_symbols), cmp_func_symbol) != NULL;
+}
+
static int symbols_collect(struct object *obj)
{
Elf_Scn *scn = NULL;
@@ -1514,14 +1572,193 @@ static int btf2btf(struct object *obj)
* Sort types by name in ascending order resulting in all
* anonymous types being placed before named types.
*/
+struct btf_name_sort {
+ struct btf *btf;
+ bool extract_inline;
+ bool *transfer_funcs;
+ bool *transfer_protos;
+};
+
+static int btf_inline_kind_order(const struct btf_type *t, bool transfer_func,
+ bool transfer_proto)
+{
+ if (transfer_func)
+ return 1;
+ if (transfer_proto)
+ return 2;
+ if (btf_is_loc_param(t))
+ return 3;
+ if (btf_is_loc_proto(t))
+ return 4;
+ if (btf_is_locsec(t))
+ return 5;
+ return 0;
+}
+
+enum inline_ref_kind {
+ INLINE_REF_FUNC,
+ INLINE_REF_FUNC_PROTO,
+};
+
+static void mark_inline_ref(struct btf *btf, bool *refs, int start_id,
+ int type_cnt, __u32 type_id, enum inline_ref_kind ref_kind)
+{
+ const struct btf_type *t;
+
+ if (type_id < start_id || type_id >= type_cnt)
+ return;
+ t = btf__type_by_id(btf, type_id);
+ if ((ref_kind == INLINE_REF_FUNC && btf_is_func(t)) ||
+ (ref_kind == INLINE_REF_FUNC_PROTO && btf_is_func_proto(t)))
+ refs[type_id] = true;
+}
+
+static void mark_inline_type_refs(struct btf *btf, bool *refs, int start_id,
+ int type_cnt, const struct btf_type *t,
+ enum inline_ref_kind ref_kind)
+{
+ /*
+ * These are the only BTF kinds that can directly reference a FUNC or
+ * FUNC_PROTO (aside from LOCSEC).
+ */
+ switch (btf_kind(t)) {
+ case BTF_KIND_CONST:
+ case BTF_KIND_VOLATILE:
+ case BTF_KIND_RESTRICT:
+ case BTF_KIND_PTR:
+ case BTF_KIND_TYPEDEF:
+ case BTF_KIND_FUNC:
+ case BTF_KIND_VAR:
+ case BTF_KIND_DECL_TAG:
+ case BTF_KIND_TYPE_TAG:
+ mark_inline_ref(btf, refs, start_id, type_cnt, t->type, ref_kind);
+ break;
+ default:
+ return;
+ }
+}
+
+/* A FUNC can move to inline BTF only when LOCSEC is its sole BTF user. */
+static int find_inline_funcs(struct object *obj, bool **transfer_funcsp,
+ bool **transfer_protosp)
+{
+ bool *locsec_refs, *other_refs, *proto_candidates, *proto_base_refs;
+ int i, type_cnt, start_id = 0;
+ struct btf *btf = obj->btf;
+
+ type_cnt = btf__type_cnt(btf);
+ *transfer_funcsp = NULL;
+ *transfer_protosp = NULL;
+ if (btf__base_btf(btf))
+ start_id = btf__type_cnt(btf__base_btf(btf));
+
+ locsec_refs = calloc(type_cnt, sizeof(*locsec_refs));
+ other_refs = calloc(type_cnt, sizeof(*other_refs));
+ proto_candidates = calloc(type_cnt, sizeof(*proto_candidates));
+ proto_base_refs = calloc(type_cnt, sizeof(*proto_base_refs));
+ if (!locsec_refs || !other_refs || !proto_candidates || !proto_base_refs)
+ goto err;
+
+ /* First identify locsec FUNC references */
+ for (i = start_id; i < type_cnt; i++) {
+ const struct btf_type *t = btf__type_by_id(btf, i);
+ const struct btf_loc *loc;
+ int j;
+
+ if (!btf_is_locsec(t))
+ continue;
+ loc = btf_locsec_locs(t);
+ for (j = 0; j < btf_vlen(t); j++, loc++) {
+ if (loc->func >= start_id && loc->func < type_cnt &&
+ btf_is_func(btf__type_by_id(btf, loc->func)))
+ locsec_refs[loc->func] = true;
+ }
+ }
+
+ /*
+ * Then mark any additional references to them; references outside
+ * of LOCSEC make the FUNC ineligible for transfer.
+ */
+ for (i = start_id; i < type_cnt; i++) {
+ const struct btf_type *t = btf__type_by_id(btf, i);
+
+ if (btf_is_locsec(t))
+ continue;
+ mark_inline_type_refs(btf, other_refs, start_id, type_cnt, t,
+ INLINE_REF_FUNC);
+ }
+
+ for (i = start_id; i < type_cnt; i++) {
+ const struct btf_type *t = btf__type_by_id(btf, i);
+ const char *name;
+
+ /* If there are no locsec references, or we find additional
+ * non-locsec references, skip as ineligible.
+ */
+ if (!locsec_refs[i] || other_refs[i])
+ continue;
+ name = btf__name_by_offset(btf, t->name_off);
+ /* Keep out-of-line functions and BTF ID users in the main BTF. */
+ if (!obj->efile.func_symbols_collected || has_func_symbol(obj, name) ||
+ btf_id__find(&obj->funcs, name))
+ locsec_refs[i] = false;
+ }
+
+ for (i = start_id; i < type_cnt; i++) {
+ const struct btf_type *t;
+
+ if (!locsec_refs[i])
+ continue;
+ t = btf__type_by_id(btf, i);
+ proto_candidates[t->type] = true;
+ }
+
+ /* Mark every FUNC_PROTO directly needed by retained base BTF types. */
+ for (i = start_id; i < type_cnt; i++) {
+ const struct btf_type *t = btf__type_by_id(btf, i);
+
+ if (locsec_refs[i] || proto_candidates[i] ||
+ btf_is_loc_param(t) || btf_is_loc_proto(t) ||
+ btf_is_locsec(t))
+ continue;
+ mark_inline_type_refs(btf, proto_base_refs, start_id, type_cnt, t,
+ INLINE_REF_FUNC_PROTO);
+ }
+ for (i = start_id; i < type_cnt; i++)
+ proto_candidates[i] &= !proto_base_refs[i];
+
+ free(other_refs);
+ *transfer_funcsp = locsec_refs;
+ *transfer_protosp = proto_candidates;
+ free(proto_base_refs);
+ return 0;
+err:
+ free(locsec_refs);
+ free(other_refs);
+ free(proto_candidates);
+ free(proto_base_refs);
+ return -ENOMEM;
+}
+
static int cmp_type_names(const void *a, const void *b, void *priv)
{
- struct btf *btf = (struct btf *)priv;
+ struct btf_name_sort *sort = priv;
+ struct btf *btf = sort->btf;
const struct btf_type *ta = btf__type_by_id(btf, *(__u32 *)a);
const struct btf_type *tb = btf__type_by_id(btf, *(__u32 *)b);
const char *na, *nb;
+ int inline_order;
int r;
+ if (sort->extract_inline) {
+ inline_order = btf_inline_kind_order(ta, sort->transfer_funcs[*(__u32 *)a],
+ sort->transfer_protos[*(__u32 *)a]) -
+ btf_inline_kind_order(tb, sort->transfer_funcs[*(__u32 *)b],
+ sort->transfer_protos[*(__u32 *)b]);
+ if (inline_order)
+ return inline_order;
+ }
+
na = btf__str_by_offset(btf, ta->name_off);
nb = btf__str_by_offset(btf, tb->name_off);
r = strcmp(na, nb);
@@ -1532,8 +1769,60 @@ static int cmp_type_names(const void *a, const void *b, void *priv)
return *(__u32 *)a < *(__u32 *)b ? -1 : 1;
}
-static int sort_btf_by_name(struct btf *btf)
+struct loc_sort {
+ const __u32 *id_map;
+ __u32 start_id;
+};
+
+static __u32 projected_type_id(const struct loc_sort *sort, __u32 id)
+{
+ if (id < sort->start_id)
+ return id;
+ return sort->id_map[id - sort->start_id] & ~BTF_PERMUTE_ID_TRANSFER;
+}
+
+static int cmp_loc(const void *a, const void *b, void *priv)
{
+ const struct loc_sort *sort = priv;
+ const struct btf_loc *la = a, *lb = b;
+ __u32 afunc = projected_type_id(sort, la->func);
+ __u32 bfunc = projected_type_id(sort, lb->func);
+ __u32 aproto = projected_type_id(sort, la->loc_proto);
+ __u32 bproto = projected_type_id(sort, lb->loc_proto);
+
+ if (afunc != bfunc)
+ return afunc < bfunc ? -1 : 1;
+ if (la->offset != lb->offset)
+ return la->offset < lb->offset ? -1 : 1;
+ if (aproto != bproto)
+ return aproto < bproto ? -1 : 1;
+ return 0;
+}
+
+static void sort_locsecs(struct btf *btf, const __u32 *id_map, __u32 start_id,
+ int nr_types)
+{
+ struct loc_sort sort = { .id_map = id_map, .start_id = start_id };
+ int i;
+
+ for (i = 0; i < nr_types; i++) {
+ struct btf_type *t = (struct btf_type *)btf__type_by_id(btf, start_id + i);
+
+ if (btf_is_locsec(t))
+ qsort_r(btf_locsec_locs(t), btf_vlen(t), sizeof(struct btf_loc),
+ cmp_loc, &sort);
+ }
+}
+
+static int sort_btf_by_name(struct object *obj)
+{
+ struct btf *btf = obj->btf;
+ bool *transfer_funcs = NULL, *transfer_protos = NULL;
+ struct btf_name_sort sort = {
+ .btf = btf,
+ .extract_inline = obj->extract_inline,
+ };
+ LIBBPF_OPTS(btf_permute_opts, opts);
__u32 *permute_ids = NULL, *id_map = NULL;
int nr_types, i, err = 0;
__u32 start_id = 0, id;
@@ -1541,6 +1830,13 @@ static int sort_btf_by_name(struct btf *btf)
if (btf__base_btf(btf))
start_id = btf__type_cnt(btf__base_btf(btf));
nr_types = btf__type_cnt(btf) - start_id;
+ if (obj->extract_inline) {
+ err = find_inline_funcs(obj, &transfer_funcs, &transfer_protos);
+ if (err)
+ goto out;
+ sort.transfer_funcs = transfer_funcs;
+ sort.transfer_protos = transfer_protos;
+ }
permute_ids = calloc(nr_types, sizeof(*permute_ids));
if (!permute_ids) {
@@ -1557,21 +1853,37 @@ static int sort_btf_by_name(struct btf *btf)
for (i = 0, id = start_id; i < nr_types; i++, id++)
permute_ids[i] = id;
- qsort_r(permute_ids, nr_types, sizeof(*permute_ids), cmp_type_names,
- btf);
+ qsort_r(permute_ids, nr_types, sizeof(*permute_ids), cmp_type_names, &sort);
for (i = 0; i < nr_types; i++) {
id = permute_ids[i] - start_id;
id_map[id] = i + start_id;
+ if (obj->extract_inline &&
+ btf_inline_kind_order(btf__type_by_id(btf, permute_ids[i]),
+ transfer_funcs[permute_ids[i]],
+ transfer_protos[permute_ids[i]]))
+ id_map[id] |= BTF_PERMUTE_ID_TRANSFER;
}
- err = btf__permute(btf, id_map, nr_types, NULL);
+ /*
+ * Key LOCSEC records by their post-permutation function ID and offset
+ * since the tracer pattern will be to look up FUNC by name, then
+ * find LOCSEC records for that function.
+ */
+ sort_locsecs(btf, id_map, start_id, nr_types);
+
+ if (obj->extract_inline)
+ opts.transfer_btf = &obj->inline_btf;
+ err = btf__permute(btf, id_map, nr_types,
+ obj->extract_inline ? &opts : NULL);
if (err)
pr_err("FAILED: btf permute: %s\n", strerror(-err));
out:
free(permute_ids);
free(id_map);
+ free(transfer_funcs);
+ free(transfer_protos);
return err;
}
@@ -1599,7 +1911,7 @@ static int finalize_btf(struct object *obj)
obj->btf = btf;
}
- err = sort_btf_by_name(obj->btf);
+ err = sort_btf_by_name(obj);
if (err) {
pr_err("FAILED to sort BTF: %s\n", strerror(errno));
goto out_err;
@@ -1773,6 +2085,8 @@ int main(int argc, const char **argv)
"turn warnings into errors"),
OPT_BOOLEAN(0, "distill_base", &obj.distill_base,
"distill --btf_base and emit .BTF.base section data"),
+ OPT_BOOLEAN(0, "inline", &obj.extract_inline,
+ "extract location BTF into a .BTF.inline file"),
OPT_STRING(0, "patch_btfids", &btfids_path, "file",
"path to .BTF_ids section data blob to patch into ELF file"),
OPT_END()
@@ -1791,6 +2105,8 @@ int main(int argc, const char **argv)
if (elf_collect(&obj))
goto out;
+ if (obj.extract_inline && collect_func_symbols(&obj))
+ goto out;
/*
* We did not find .BTF_ids section or symbols section,
@@ -1841,11 +2157,18 @@ int main(int argc, const char **argv)
if (err)
goto out;
}
+ if (obj.inline_btf) {
+ err = make_out_path(out_path, sizeof(out_path), obj.path, BTF_ELF_SEC ".inline");
+ err = err ?: dump_raw_btf(obj.inline_btf, out_path);
+ if (err)
+ goto out;
+ }
if (!(fatal_warnings && warnings))
err = 0;
out:
btf__free(obj.base_btf);
+ btf__free(obj.inline_btf);
btf__free(obj.btf);
btf_id__free_all(&obj.structs);
btf_id__free_all(&obj.unions);
@@ -1853,6 +2176,7 @@ int main(int argc, const char **argv)
btf_id__free_all(&obj.funcs);
btf_id__free_all(&obj.sets);
free(obj.addr_syms);
+ free(obj.efile.func_symbols);
if (obj.efile.elf) {
elf_end(obj.efile.elf);
close(obj.efile.fd);
--
2.43.5
next prev parent reply other threads:[~2026-09-01 16:59 UTC|newest]
Thread overview: 42+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-01 16:57 [PATCH v2 bpf-next 00/18] Support inline functions in BTF Alan Maguire
2026-09-01 16:57 ` [PATCH v2 bpf-next 01/18] btf: Extend UAPI to support BTF location (inline site) info Alan Maguire
2026-09-01 17:17 ` sashiko-bot
2026-09-01 17:55 ` bot+bpf-ci
2026-09-01 16:57 ` [PATCH v2 bpf-next 02/18] libbpf: Add support for BTF kinds LOC[_PARAM|_PROTO|SEC] Alan Maguire
2026-09-01 17:11 ` sashiko-bot
2026-09-01 16:57 ` [PATCH v2 bpf-next 03/18] libbpf: Support moving permuted BTF types into split BTF Alan Maguire
2026-09-01 17:15 ` sashiko-bot
2026-09-01 18:14 ` bot+bpf-ci
2026-09-01 16:57 ` [PATCH v2 bpf-next 04/18] selftests/bpf: Test helper support for BTF_KIND_LOC[_PARAM|_PROTO|SEC] Alan Maguire
2026-09-01 17:06 ` sashiko-bot
2026-09-01 16:57 ` [PATCH v2 bpf-next 05/18] selftests/bpf: Add LOC_PARAM, LOC_PROTO, LOCSEC to field iter tests Alan Maguire
2026-09-01 16:57 ` [PATCH v2 bpf-next 06/18] selftests/bpf: Add LOC_PARAM, LOC_PROTO, LOCSEC to dedup split tests Alan Maguire
2026-09-01 17:55 ` bot+bpf-ci
2026-09-01 16:57 ` [PATCH v2 bpf-next 07/18] selftests/bpf: BTF distill tests to ensure LOC[_PARAM|_PROTO] add to split BTF Alan Maguire
2026-09-01 17:55 ` bot+bpf-ci
2026-09-01 16:57 ` [PATCH v2 bpf-next 08/18] selftests/bpf: Validate that btf__permute transfer works Alan Maguire
2026-09-01 17:16 ` sashiko-bot
2026-09-01 17:55 ` bot+bpf-ci
2026-09-01 16:57 ` [PATCH v2 bpf-next 09/18] bpftool: Handle multi-split BTF by supporting multiple base BTFs Alan Maguire
2026-09-01 17:13 ` sashiko-bot
2026-09-01 16:57 ` [PATCH v2 bpf-next 10/18] bpftool: Document support for multi-split BTF Alan Maguire
2026-09-01 17:12 ` sashiko-bot
2026-09-01 16:57 ` [PATCH v2 bpf-next 11/18] bpftool: Add ability to dump LOC_PARAM, LOC_PROTO and LOCSEC Alan Maguire
2026-09-01 17:16 ` sashiko-bot
2026-09-01 17:55 ` bot+bpf-ci
2026-09-01 16:57 ` Alan Maguire [this message]
2026-09-01 17:23 ` [PATCH v2 bpf-next 12/18] resolve_btfids: Extract inline BTF sashiko-bot
2026-09-01 16:57 ` [PATCH v2 bpf-next 13/18] kbuild: Add support for BTF inline information Alan Maguire
2026-09-01 17:55 ` bot+bpf-ci
2026-09-01 16:57 ` [PATCH v2 bpf-next 14/18] btf: Make vmlinux, module inline info available in /sys/kernel/btf Alan Maguire
2026-09-01 16:57 ` [PATCH v2 bpf-next 15/18] btf: Support CONFIG_DEBUG_INFO_BTF_INLINE=m Alan Maguire
2026-09-01 17:24 ` sashiko-bot
2026-09-01 16:57 ` [PATCH v2 bpf-next 16/18] btf: Relocate inline BTF for modules with distilled base BTF Alan Maguire
2026-09-01 17:29 ` sashiko-bot
2026-09-01 17:55 ` bot+bpf-ci
2026-09-01 16:57 ` [PATCH v2 bpf-next 17/18] selftests/bpf: Test BTF sysfs inline representations Alan Maguire
2026-09-01 17:22 ` sashiko-bot
2026-09-01 17:55 ` bot+bpf-ci
2026-09-01 16:57 ` [PATCH v2 bpf-next 18/18] selftests/bpf: Add a test verifying inline information Alan Maguire
2026-09-01 17:28 ` sashiko-bot
2026-09-01 17:55 ` bot+bpf-ci
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260901165757.801449-13-alan.maguire@oracle.com \
--to=alan.maguire@oracle.com \
--cc=andrii@kernel.org \
--cc=arnd@arndb.de \
--cc=ast@kernel.org \
--cc=atenart@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=eddyz87@gmail.com \
--cc=emil@etsalapatis.com \
--cc=ihor.solodrai@linux.dev \
--cc=jolsa@kernel.org \
--cc=kees@kernel.org \
--cc=linux-modules@vger.kernel.org \
--cc=martin.lau@linux.dev \
--cc=mcgrof@kernel.org \
--cc=memxor@gmail.com \
--cc=nathan@kernel.org \
--cc=nsc@kernel.org \
--cc=ojeda@kernel.org \
--cc=petr.pavlu@suse.com \
--cc=puranjay@kernel.org \
--cc=qmo@kernel.org \
--cc=song@kernel.org \
--cc=tj@kernel.org \
--cc=yatsenko@meta.com \
--cc=yonghong.song@linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox