bpf.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Kui-Feng Lee <sinquersw@gmail.com>
To: Quentin Monnet <quentin@isovalent.com>,
	thinker.li@gmail.com, bpf@vger.kernel.org, ast@kernel.org,
	martin.lau@linux.dev, song@kernel.org, kernel-team@meta.com,
	andrii@kernel.org
Cc: kuifeng@meta.com
Subject: Re: [PATCH bpf-next v3 4/5] bpftool: generated shadow variables for struct_ops maps.
Date: Wed, 21 Feb 2024 09:47:47 -0800	[thread overview]
Message-ID: <f5338514-793a-4d73-9b8f-1381e985c943@gmail.com> (raw)
In-Reply-To: <70fe67f3-4ae2-4866-95d6-e41c908ca300@isovalent.com>



On 2/21/24 03:49, Quentin Monnet wrote:
> 2024-02-21 01:23 UTC+0000 ~ thinker.li@gmail.com
>> From: Kui-Feng Lee <thinker.li@gmail.com>
>>
>> Declares and defines a pointer of the shadow type for each struct_ops map.
>>
>> The code generator will create an anonymous struct type as the shadow type
>> for each struct_ops map. The shadow type is translated from the original
>> struct type of the map. The user of the skeleton use pointers of them to
>> access the values of struct_ops maps.
>>
>> However, shadow types only supports certain types of fields, such as scalar
> 
> Nit: "such as" implies the list may not be exhaustive.
> 
>> types and function pointers. Any fields of unsupported types are translated
>> into an array of characters to occupy the space of the original
>> field. Function pointers are translated into pointers of the struct
>> bpf_program. Additionally, padding fields are generated to occupy the space
>> between two consecutive fields.
>>
>> The pointers of shadow types of struct_osp maps are initialized when
>> *__open_opts() in skeletons are called. For a map called FOO, the user can
>> access it through the pointer at skel->struct_ops.FOO.
>>
>> Signed-off-by: Kui-Feng Lee <thinker.li@gmail.com>
>> ---
>>   tools/bpf/bpftool/gen.c | 229 +++++++++++++++++++++++++++++++++++++++-
>>   1 file changed, 228 insertions(+), 1 deletion(-)
>>
>> diff --git a/tools/bpf/bpftool/gen.c b/tools/bpf/bpftool/gen.c
>> index a9334c57e859..20c5d5912df7 100644
>> --- a/tools/bpf/bpftool/gen.c
>> +++ b/tools/bpf/bpftool/gen.c
>> @@ -909,6 +909,201 @@ codegen_progs_skeleton(struct bpf_object *obj, size_t prog_cnt, bool populate_li
>>   	}
>>   }
>>   
>> +static int walk_st_ops_shadow_vars(struct btf *btf,
>> +				   const char *ident,
>> +				   const struct bpf_map *map)
>> +{
>> +	DECLARE_LIBBPF_OPTS(btf_dump_emit_type_decl_opts, opts,
>> +			    .indent_level = 3,
>> +			    );
>> +	const struct btf_type *map_type, *member_type;
>> +	__u32 map_type_id, member_type_id;
>> +	__u32 offset, next_offset = 0;
>> +	const struct btf_member *m;
>> +	const char *member_name;
>> +	struct btf_dump *d = NULL;
>> +	int i, err = 0;
>> +	int size, map_size;
>> +
>> +	map_type_id = bpf_map__btf_value_type_id(map);
>> +	if (map_type_id == 0)
>> +		return -EINVAL;
>> +	map_type = btf__type_by_id(btf, map_type_id);
>> +	if (!map_type)
>> +		return -EINVAL;
>> +
>> +	d = btf_dump__new(btf, codegen_btf_dump_printf, NULL, NULL);
>> +	if (!d)
>> +		return -errno;
>> +
>> +	for (i = 0, m = btf_members(map_type);
>> +	     i < btf_vlen(map_type);
>> +	     i++, m++) {
>> +		member_type = skip_mods_and_typedefs(btf, m->type,
>> +						     &member_type_id);
>> +		if (!member_type) {
>> +			err = -EINVAL;
>> +			goto out;
>> +		}
>> +
>> +		member_name = btf__name_by_offset(btf, m->name_off);
>> +		if (!member_name) {
>> +			err = -EINVAL;
>> +			goto out;
>> +		}
>> +
>> +		offset = m->offset / 8;
>> +		if (next_offset != offset) {
>> +			printf("\t\t\tchar __padding_%d[%d];\n",
>> +			       i - 1, offset - next_offset);
>> +		}
>> +
>> +		switch (btf_kind(member_type)) {
>> +		case BTF_KIND_INT:
>> +		case BTF_KIND_FLOAT:
>> +		case BTF_KIND_ENUM:
>> +		case BTF_KIND_ENUM64:
>> +			/* scalar type */
>> +			printf("\t\t\t");
>> +			opts.field_name = member_name;
>> +			err = btf_dump__emit_type_decl(d, member_type_id,
>> +						       &opts);
>> +			if (err)
>> +				goto out;
>> +			printf(";\n");
>> +
>> +			size = btf__resolve_size(btf, member_type_id);
>> +			if (size < 0) {
>> +				err = size;
>> +				goto out;
>> +			}
>> +
>> +			next_offset = offset + size;
>> +			break;
>> +
>> +		case BTF_KIND_PTR:
>> +			if (resolve_func_ptr(btf, m->type, NULL)) {
>> +				/* Function pointer */
>> +				printf("\t\t\tconst struct bpf_program *%s;\n",
>> +				       member_name);
>> +
>> +				next_offset = offset + sizeof(void *);
>> +				break;
>> +			}
>> +			fallthrough;
> 
> I wouldn't mind a comment about the "fallthrough;" to state explicitly
> that only function pointers are supported for now.


Sure

> 
>> +
>> +		default:
>> +			/* Unsupported types
>> +			 *
>> +			 * For unsupported types, we have to generate
>> +			 * definitions for them in order to support
>> +			 * them. For example, we need to generate a
>> +			 * definition for a struct type or a union type. It
>> +			 * may cause type conflicts without renaming since
>> +			 * the same type may be defined for several
>> +			 * skeletons, and the user may include these
>> +			 * skeletons in the same compile unit.
>> +			 */
> 
> This comment could be clearer. "For unsupported types, we have to
> generate definitions for them in order to support them". So do we, or do
> we not support them? "It may cause type conflicts [...]" -> do we
> address these?
> 
> My understanding is that this note describes the work to do if we want
> to add support in the future, and this could perhaps be more explicit:
> "We do not support other types yet. The reason is that ... But when we
> generate definitions, we will have to take care of type conflicts
> because ...". What do you think?


Agree! I will rephrase this comment.

> 
>> +			if (i == btf_vlen(map_type) - 1) {
>> +				map_size = btf__resolve_size(btf, map_type_id);
>> +				if (map_size < 0)
>> +					return -EINVAL;
>> +				size = map_size - offset;
>> +			} else {
>> +				size = (m[1].offset - m->offset) / 8;
>> +			}
>> +
>> +			printf("\t\t\tchar __padding_%d[%d];\n", i, size);
>> +
>> +			next_offset = offset + size;
>> +			break;
>> +		}
>> +	}
>> +
>> +out:
>> +	btf_dump__free(d);
>> +
>> +	return err;
>> +}
>> +
>> +/* Generate the pointer of the shadow type for a struct_ops map.
>> + *
>> + * This function adds a pointer of the shadow type for a struct_ops map.
>> + * The members of a struct_ops map can be exported through a pointer to a
>> + * shadow type. The user can access these members through the pointer.
>> + *
>> + * A shadow type includes not all members, only members of some types.
>> + * They are scalar types and function pointers. The function pointers are
>> + * translated to the pointer of the struct bpf_program. The scalar types
>> + * are translated to the original type without any modifiers.
>> + *
>> + * Unsupported types will be translated to a char array to take the same
>> + * space of the original field. However, due to handling padding and
>> + * alignments, the user should not access them directly.
> 
> What's the risk, and how should users know?

The names of unsupported fields are replaced by "__padding_*", and their
types are "char []".
Changing names and types of fields in a struct can lead to accessing
issues, where users may inadvertently corrupt data due to padding and
field reordering in different versions.

I will include the above explanation in the next version.

> 
>> + */
> 
> [...]
> 
> Thanks for this work! The bpftool changes look good. I've got these few
> observations above, but notwithstanding:
> 
> Reviewed-by: Quentin Monnet <quentin@isovalent.com>
> 
> I wonder, did you think of adding a paragraph or an example to the man
> page for "bpftool gen"? Your change is for a specific use case, but
> otherwise I'm not sure how users will ever know that these shadow types
> are available (other than discovering them by luck in a skeleton) or how
> to use them if they need to.

Sure! I will add it in the following version.

> 
> Thanks,
> Quentin

  reply	other threads:[~2024-02-21 17:47 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-02-21  1:23 [PATCH bpf-next v3 0/5] Create shadow types for struct_ops maps in skeletons thinker.li
2024-02-21  1:23 ` [PATCH bpf-next v3 1/5] libbpf: expose resolve_func_ptr() through libbpf_internal.h thinker.li
2024-02-21 11:49   ` Quentin Monnet
2024-02-21 16:32     ` Kui-Feng Lee
2024-02-21  1:23 ` [PATCH bpf-next v3 2/5] libbpf: set btf_value_type_id of struct bpf_map for struct_ops thinker.li
2024-02-21  1:23 ` [PATCH bpf-next v3 3/5] libbpf: Convert st_ops->data to shadow type thinker.li
2024-02-21  1:23 ` [PATCH bpf-next v3 4/5] bpftool: generated shadow variables for struct_ops maps thinker.li
2024-02-21 11:49   ` Quentin Monnet
2024-02-21 17:47     ` Kui-Feng Lee [this message]
2024-02-21  1:23 ` [PATCH bpf-next v3 5/5] selftests/bpf: Test if shadow types work correctly thinker.li

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=f5338514-793a-4d73-9b8f-1381e985c943@gmail.com \
    --to=sinquersw@gmail.com \
    --cc=andrii@kernel.org \
    --cc=ast@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=kernel-team@meta.com \
    --cc=kuifeng@meta.com \
    --cc=martin.lau@linux.dev \
    --cc=quentin@isovalent.com \
    --cc=song@kernel.org \
    --cc=thinker.li@gmail.com \
    /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;
as well as URLs for NNTP newsgroup(s).