From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-pg1-f173.google.com (mail-pg1-f173.google.com [209.85.215.173]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CFBE4339858 for ; Fri, 17 Jul 2026 21:52:18 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.173 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784325140; cv=none; b=ssUrLoT3r1PiYGRzDaQK4YadZ3BhOCrDgAIZryekFYstKbwPnFuOy8foCjl8HS4nX9gwtP2KfTJ5pwW/rGo/nKfeV98s6wV590pvNCs6M1oCFsz4izrb8M0uTcqi67RT6TfmXKOvhjIZTg5dVtAIyrg24n4ZP4Xto96h94cF8rk= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784325140; c=relaxed/simple; bh=55EzlMufrn+hckL4EKFgbR5tUr6d3Envr0gIRu0iIkY=; h=Mime-Version:Content-Type:Date:Message-Id:Cc:Subject:From:To: References:In-Reply-To; b=nMxp7x8QaGr0VyUqOzqWC/EaNfJGWh4NtUzxFhvTZhttJpDeff89m+wKOWjQV0SWdnlZD6gxBwRossbwkyVO97IMHJqaoixmKGBOAAoy3e4MSQgvZsG2XdBg//qVYzDltwiCCag3K4hOWf6GjJyO7jyrkQdX9N3SSPGPCa9ja8s= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=etsalapatis.com; spf=pass smtp.mailfrom=etsalapatis.com; dkim=pass (2048-bit key) header.d=etsalapatis-com.20251104.gappssmtp.com header.i=@etsalapatis-com.20251104.gappssmtp.com header.b=e5cCPdyh; arc=none smtp.client-ip=209.85.215.173 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=etsalapatis.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=etsalapatis.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=etsalapatis-com.20251104.gappssmtp.com header.i=@etsalapatis-com.20251104.gappssmtp.com header.b="e5cCPdyh" Received: by mail-pg1-f173.google.com with SMTP id 41be03b00d2f7-ca766c1c9ccso4936915a12.0 for ; Fri, 17 Jul 2026 14:52:18 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=etsalapatis-com.20251104.gappssmtp.com; s=20251104; t=1784325138; x=1784929938; darn=vger.kernel.org; h=in-reply-to:references:to:from:subject:cc:message-id:date :content-type:content-transfer-encoding:mime-version:from:to:cc :subject:date:message-id:reply-to:content-type; bh=8xICSg/3uLQX5g2+LM6seI/bgJKFaOXV9vjEkcMgNPU=; b=e5cCPdyhQv13naxSZRKizKQe0oEO6Qqdgs/XUhtIhiKSlQUKN7HmfF0etVzoPQwaxi L7jCaDOLNntvkFZJzubi4KjJvueIKbJCEpXNNqy1GEeom6pOe4tCuAorVPZqStXn2zjM tP4w5DxsVqEJsJgMkZJ7lFnGu1gg3Dntg2YrbzjHMUFd/CHF8c4N04YbfyjcCRyIChPV F57458IO05z05o3yaTdTIUKhFuB9+2rLUIdvxIWvZOQXbusmjAEHMDyA401tNmIgIbPg utA9dlJw8deH9QP2tmbYg/Gqv/cpJsPrwoUKxIW7Ow7qN42H4VGYagkr+4o97WyV718H Vo4w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784325138; x=1784929938; h=in-reply-to:references:to:from:subject:cc:message-id:date :content-type:content-transfer-encoding:mime-version:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=8xICSg/3uLQX5g2+LM6seI/bgJKFaOXV9vjEkcMgNPU=; b=PuX2FBuH3beZwzqY7mtJdZbUM3vuZuNf5Ol7Ty9+1gDWhE2OJJcphgFB3JvnB++LZR aPZCdbCrV/xOKj3fz8RXPZS7rqAjLoFA7oxPWj26yf3fQP11H7iMbQJvTglbypgi5mII cO3aEH01qDjwwNMJ5OP4SrmHjI+7cEifWDnI0xqRp6ts+M2BH3Z3TFvLBpxLPrM1I4Hx tzDg7DuK0h7P2dMfDFPm6in/wshnbp9CG5WvpZPZjPN3KkiaGbqMPL+NyLZ5l4zbgRG+ cFY9gwo9SYwC69o8VUHK/e2KhJrIg2Hb8moyeliRsDSnWLBTtMrODC1ixMpZL+leGlmN H0uw== X-Forwarded-Encrypted: i=1; AHgh+RqgdTyWbDyNTtEXQC7xxmrxrByJuz/YipuX3AXdpnIwpWt2KiObOKkUfttJN23MuUIqslRjJVl0Gq6+DLw=@vger.kernel.org X-Gm-Message-State: AOJu0YwaiitFVVD/93g0Rx0hQxI0zqCrMgzzh4MLc6xuX7+lft9ON/gQ tMX9TJWqb7iXOsIW1uMul/6ym6ViIIvHx4GJeommkB8WFwyXMHwR4MjpV6+MP/KxIDc= X-Gm-Gg: AfdE7ckZB8+msFRGaCugqkMUi4fIxIMW9XzKRkjugDPcgxpbS53twEwnkwUCM7EX8Wj yAsTveWMn8gobwmC8XgvQWQTT8iNby7NrXMMdGnJpOIQUIWW7wRz8PP6Dx9VL6Dt0pSjVpET7hS sw+18c9BeOmy0TS9m4ZbRXV59V8WX5AZzCfm6DB3EspuYLggsXl5Lh9zWBtLjFKoGXjHjrywQpz cvupzAwimk8J+F9VCzITompaHRkv1s6vdSdKy07SONmnIv2zNmeAazR1IifTibYB5o5gp0r2bM2 ijasqYKr9z0mGSM/uRKz2YazFRA/bgGHxVFaIj9pJNX1TLMxgfKrVxmft5WAsSCvvxAyRCjK0NP XLt1wSTJGAHQ1RiM+e24zDAQHC/sl9Wlv2cltF6Soo6w4bfSACCQ4CotuAxk= X-Received: by 2002:a05:6a20:9630:b0:3bf:8de9:c647 with SMTP id adf61e73a8af0-3c3ad355d6dmr5251707637.0.1784325138039; Fri, 17 Jul 2026 14:52:18 -0700 (PDT) Received: from localhost ([2620:10d:c090:600::f5f9]) by smtp.gmail.com with ESMTPSA id 5a478bee46e88-3142a1ddeb6sm12396518eec.20.2026.07.17.14.52.16 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Fri, 17 Jul 2026 14:52:17 -0700 (PDT) Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=UTF-8 Date: Fri, 17 Jul 2026 17:52:15 -0400 Message-Id: Cc: "Alexei Starovoitov" , "Daniel Borkmann" , "Andrii Nakryiko" , "Martin KaFai Lau" , "Eduard Zingerman" , "Kumar Kartikeya Dwivedi" , "Song Liu" , "Yonghong Song" , "Jiri Olsa" , "John Fastabend" , "Quentin Monnet" , "Shuah Khan" , , , Subject: Re: [PATCH bpf-next v10 5/9] bpftool: Generate skeleton for global percpu data From: "Emil Tsalapatis" To: "Leon Hwang" , X-Mailer: aerc 0.21.0-0-g5549850facc2 References: <20260715153254.92010-1-leon.hwang@linux.dev> <20260715153254.92010-6-leon.hwang@linux.dev> In-Reply-To: <20260715153254.92010-6-leon.hwang@linux.dev> On Wed Jul 15, 2026 at 11:32 AM EDT, Leon Hwang wrote: > Enhance bpftool to generate skeletons that properly handle global percpu > variables. The generated skeleton now includes a dedicated structure for > percpu data, allowing users to initialize and access percpu variables mor= e > efficiently. > > For global percpu variables, the skeleton now includes a nested > structure, e.g.: > > struct test_global_percpu_data { > struct bpf_object_skeleton *skeleton; > struct bpf_object *obj; > struct { > struct bpf_map *percpu; > } maps; > // ... > struct test_global_percpu_data__percpu { > int data; > char run; > struct { > char set; > int i; > int nums[7]; > } struct_data; > int nums[7]; > } *percpu; > > // ... > }; > > * The "struct test_global_percpu_data__percpu *percpu" points to > initialized data, which is actually "maps.percpu->mmaped". > * Before loading the skeleton, updating the > "struct test_global_percpu_data__percpu *percpu" modifies the initial > value of the corresponding global percpu variables. > * After loading the skeleton, "maps.percpu->mmaped" has been marked as > read-only in libbpf. If users want to update the global percpu > variables, they have to update the "maps.percpu" map instead. > * For lightweight skeleton, "lskel->percpu" will be protected by > "mprotect(p, sz, PROT_READ)". > * For subskeleton, those variables of global percpu data will be > skipped. > > Acked-by: Quentin Monnet > Assisted-by: Codex:gpt-5.5-xhigh > Signed-off-by: Leon Hwang > --- > tools/bpf/bpftool/gen.c | 76 +++++++++++++++++++++++++++-------- > tools/lib/bpf/skel_internal.h | 24 ++++++++++- > 2 files changed, 81 insertions(+), 19 deletions(-) > > diff --git a/tools/bpf/bpftool/gen.c b/tools/bpf/bpftool/gen.c > index a01d06d22d1a..b60db1108149 100644 > --- a/tools/bpf/bpftool/gen.c > +++ b/tools/bpf/bpftool/gen.c > @@ -101,6 +101,12 @@ static bool get_map_ident(const struct bpf_map *map,= char *buf, size_t buf_sz) > return true; > } > =20 > + if (bpf_map__type(map) =3D=3D BPF_MAP_TYPE_PERCPU_ARRAY) { > + snprintf(buf, buf_sz, "%s", name + 1); > + sanitize_identifier(buf); > + return true; > + } > + > for (i =3D 0, n =3D ARRAY_SIZE(sfxs); i < n; i++) { > const char *sfx =3D sfxs[i], *p; > =20 > @@ -117,7 +123,7 @@ static bool get_map_ident(const struct bpf_map *map, = char *buf, size_t buf_sz) > =20 > static bool get_datasec_ident(const char *sec_name, char *buf, size_t bu= f_sz) > { > - static const char *pfxs[] =3D { ".data", ".rodata", ".bss", ".kconfig" = }; > + static const char *pfxs[] =3D { ".data", ".rodata", ".bss", ".percpu", = ".kconfig" }; > int i, n; > =20 > /* recognize hard coded LLVM section name */ > @@ -254,7 +260,7 @@ static const struct btf_type *find_type_for_map(struc= t btf *btf, const char *map > return NULL; > } > =20 > -static bool is_mmapable_map(const struct bpf_map *map, char *buf, size_t= sz) > +static bool is_skel_data(const struct bpf_map *map, char *buf, size_t sz= ) Here we change the function to take PERCPU_ARRAY into account, but then=20 immediately turn around and add checks of the form is_skel_data && type !=3D BPF_MAP_TYPE_PERCPU_ARRAY. If we keep both the existing mmapable_map and add a separate is_skel_data map (possibly using is_mappable_map), we can remove those additional checks. > { > size_t tmp_sz; > =20 > @@ -263,13 +269,19 @@ static bool is_mmapable_map(const struct bpf_map *m= ap, char *buf, size_t sz) > return true; > } > =20 > - if (!bpf_map__is_internal(map) || !(bpf_map__map_flags(map) & BPF_F_MMA= PABLE)) > + if (!bpf_map__is_internal(map)) > return false; > =20 > if (!get_map_ident(map, buf, sz)) > return false; > =20 > - return true; > + if (bpf_map__map_flags(map) & BPF_F_MMAPABLE) > + return true; > + > + if (bpf_map__type(map) =3D=3D BPF_MAP_TYPE_PERCPU_ARRAY) > + return true; > + > + return false; > } > =20 > static int codegen_datasecs(struct bpf_object *obj, const char *obj_name= ) > @@ -286,8 +298,11 @@ static int codegen_datasecs(struct bpf_object *obj, = const char *obj_name) > return -errno; > =20 > bpf_object__for_each_map(map, obj) { > - /* only generate definitions for memory-mapped internal maps */ > - if (!is_mmapable_map(map, map_ident, sizeof(map_ident))) > + /* > + * Only generate definitions for internal maps that have > + * mmapped data. > + */ > + if (!is_skel_data(map, map_ident, sizeof(map_ident))) > continue; > =20 > sec =3D find_type_for_map(btf, map_ident); > @@ -339,8 +354,14 @@ static int codegen_subskel_datasecs(struct bpf_objec= t *obj, const char *obj_name > return -errno; > =20 > bpf_object__for_each_map(map, obj) { > - /* only generate definitions for memory-mapped internal maps */ > - if (!is_mmapable_map(map, map_ident, sizeof(map_ident))) > + /* > + * Only generate definitions for internal maps that have > + * mmapped data. > + */ > + if (!is_skel_data(map, map_ident, sizeof(map_ident))) > + continue; > + > + if (bpf_map__type(map) =3D=3D BPF_MAP_TYPE_PERCPU_ARRAY) > continue; Case in point, the old is_mmapable_map would work fine here. > =20 > sec =3D find_type_for_map(btf, map_ident); > @@ -493,7 +514,10 @@ static size_t bpf_map_mmap_sz(const struct bpf_map *= map) > return map_sz; > } > =20 > -/* Emit type size asserts for all top-level fields in memory-mapped inte= rnal maps. */ > +/* > + * Emit type size asserts for all top-level fields in internal maps that > + * have mmaped data. I do not think the new comment makes it any clearer, the difference is "memory-mapped internal maps" and "maps that are memory-mapped initially but actually inaccessible after loading because there's no way to=20 represent them as a mapping of the skeleton" AFAICT which is not easy to infer currently. > + */ > static void codegen_asserts(struct bpf_object *obj, const char *obj_name= ) > { > struct btf *btf =3D bpf_object__btf(obj); > @@ -517,7 +541,7 @@ static void codegen_asserts(struct bpf_object *obj, c= onst char *obj_name) > ", obj_name); > =20 > bpf_object__for_each_map(map, obj) { > - if (!is_mmapable_map(map, map_ident, sizeof(map_ident))) > + if (!is_skel_data(map, map_ident, sizeof(map_ident))) > continue; > =20 > sec =3D find_type_for_map(btf, map_ident); > @@ -669,7 +693,8 @@ static void codegen_destroy(struct bpf_object *obj, c= onst char *obj_name) > if (!get_map_ident(map, ident, sizeof(ident))) > continue; > if (bpf_map__is_internal(map) && > - (bpf_map__map_flags(map) & BPF_F_MMAPABLE)) > + ((bpf_map__map_flags(map) & BPF_F_MMAPABLE) || > + bpf_map__type(map) =3D=3D BPF_MAP_TYPE_PERCPU_ARRAY)) I think this gives us a hint on how we could structure is_skel_data above, we now have a special calss that is "mappable maps + PERCPU_ARRAY whose initial representation in userspace is mappable, but the loaded map isn't". > printf("\tskel_free_map_data(skel->%1$s, skel->maps.%1$s.initial_valu= e, %2$zu);\n", > ident, bpf_map_mmap_sz(map)); > codegen("\ > @@ -741,7 +766,7 @@ static int gen_trace(struct bpf_object *obj, const ch= ar *obj_name, const char *h > const void *mmap_data =3D NULL; > size_t mmap_size =3D 0; > =20 > - if (!is_mmapable_map(map, ident, sizeof(ident))) > + if (!is_skel_data(map, ident, sizeof(ident))) > continue; > =20 > codegen("\ > @@ -849,8 +874,22 @@ static int gen_trace(struct bpf_object *obj, const c= har *obj_name, const char *h > bpf_object__for_each_map(map, obj) { > const char *mmap_flags; > =20 > - if (!is_mmapable_map(map, ident, sizeof(ident))) > + if (!is_skel_data(map, ident, sizeof(ident))) > + continue; > + > + if (bpf_map__type(map) =3D=3D BPF_MAP_TYPE_PERCPU_ARRAY) { > + codegen("\ > + \n\ > + err =3D skel_protect_map_data(skel->%1$s, &skel->maps.%1$s.initial_va= lue, %2$zd);\n\ > + if (err) \n\ > + return err; \n\ > + #ifdef __KERNEL__ \n\ > + skel->%1$s =3D NULL; \n\ > + #endif \n\ > + ", > + ident, bpf_map_mmap_sz(map)); > continue; > + } > =20 > if (bpf_map__map_flags(map) & BPF_F_RDONLY_PROG) > mmap_flags =3D "PROT_READ"; > @@ -955,8 +994,7 @@ codegen_maps_skeleton(struct bpf_object *obj, size_t = map_cnt, bool mmaped, bool > map->map =3D &obj->maps.%s; \n\ > ", > i, bpf_map__name(map), ident); > - /* memory-mapped internal maps */ > - if (mmaped && is_mmapable_map(map, ident, sizeof(ident))) { > + if (mmaped && is_skel_data(map, ident, sizeof(ident))) { > printf("\tmap->mmaped =3D (void **)&obj->%s;\n", ident); > } > =20 > @@ -1740,7 +1778,9 @@ static int do_subskeleton(int argc, char **argv) > /* Also count all maps that have a name */ > map_cnt++; > =20 > - if (!is_mmapable_map(map, ident, sizeof(ident))) > + if (!is_skel_data(map, ident, sizeof(ident))) > + continue; > + if (bpf_map__type(map) =3D=3D BPF_MAP_TYPE_PERCPU_ARRAY) > continue; Another instance of what I was talking about. > =20 > map_type_id =3D bpf_map__btf_value_type_id(map); > @@ -1863,7 +1903,9 @@ static int do_subskeleton(int argc, char **argv) > =20 > /* walk through each symbol and emit the runtime representation */ > bpf_object__for_each_map(map, obj) { > - if (!is_mmapable_map(map, ident, sizeof(ident))) > + if (!is_skel_data(map, ident, sizeof(ident))) > + continue; > + if (bpf_map__type(map) =3D=3D BPF_MAP_TYPE_PERCPU_ARRAY) > continue; > =20 > map_type_id =3D bpf_map__btf_value_type_id(map); > diff --git a/tools/lib/bpf/skel_internal.h b/tools/lib/bpf/skel_internal.= h > index 53fee53d36d5..1f3f332dffbe 100644 > --- a/tools/lib/bpf/skel_internal.h > +++ b/tools/lib/bpf/skel_internal.h > @@ -131,8 +131,10 @@ static inline void skel_free_map_data(void *p, __u64= addr, size_t sz) > { > if (addr !=3D ~0ULL) > kvfree(p); > - /* When addr =3D=3D ~0ULL the 'p' points to > - * ((struct bpf_array *)map)->value. See skel_finalize_map_data. > + /* > + * When addr =3D=3D ~0ULL the init buffer has already been released. > + * For skel_finalize_map_data(), 'p' points to > + * ((struct bpf_array *)map)->value. > */ > } > =20 > @@ -170,6 +172,15 @@ static inline void *skel_finalize_map_data(__u64 *in= it_val, size_t mmap_sz, int > return addr; > } > =20 > +static inline int skel_protect_map_data(void *p, __u64 *init_val, size_t= sz) > +{ > + (void)sz; > + > + kvfree(p); > + *init_val =3D ~0ULL; > + return 0; > +} > + > #else > =20 > static inline void *skel_alloc(size_t size) > @@ -208,6 +219,15 @@ static inline void *skel_finalize_map_data(__u64 *in= it_val, size_t mmap_sz, int > return NULL; > return addr; > } > + > +static inline int skel_protect_map_data(void *p, __u64 *init_val, size_t= sz) > +{ > + (void)init_val; > + > + if (mprotect(p, sz, PROT_READ)) > + return -errno; > + return 0; > +} > #endif > =20 > static inline int skel_closenz(int fd)