mirror of
				https://github.com/torvalds/linux.git
				synced 2025-10-31 16:48:26 +02:00 
			
		
		
		
	bpf: pass attached BTF to the bpf_struct_ops subsystem
Pass the fd of a btf from the userspace to the bpf() syscall, and then convert the fd into a btf. The btf is generated from the module that defines the target BPF struct_ops type. In order to inform the kernel about the module that defines the target struct_ops type, the userspace program needs to provide a btf fd for the respective module's btf. This btf contains essential information on the types defined within the module, including the target struct_ops type. A btf fd must be provided to the kernel for struct_ops maps and for the bpf programs attached to those maps. In the case of the bpf programs, the attach_btf_obj_fd parameter is passed as part of the bpf_attr and is converted into a btf. This btf is then stored in the prog->aux->attach_btf field. Here, it just let the verifier access attach_btf directly. In the case of struct_ops maps, a btf fd is passed as value_type_btf_obj_fd of bpf_attr. The bpf_struct_ops_map_alloc() function converts the fd to a btf and stores it as st_map->btf. A flag BPF_F_VTYPE_BTF_OBJ_FD is added for map_flags to indicate that the value of value_type_btf_obj_fd is set. Signed-off-by: Kui-Feng Lee <thinker.li@gmail.com> Link: https://lore.kernel.org/r/20240119225005.668602-9-thinker.li@gmail.com Signed-off-by: Martin KaFai Lau <martin.lau@kernel.org>
This commit is contained in:
		
							parent
							
								
									689423db3b
								
							
						
					
					
						commit
						fcc2c1fb06
					
				
					 5 changed files with 70 additions and 22 deletions
				
			
		|  | @ -1330,6 +1330,9 @@ enum { | |||
| 
 | ||||
| /* Get path from provided FD in BPF_OBJ_PIN/BPF_OBJ_GET commands */ | ||||
| 	BPF_F_PATH_FD		= (1U << 14), | ||||
| 
 | ||||
| /* Flag for value_type_btf_obj_fd, the fd is available */ | ||||
| 	BPF_F_VTYPE_BTF_OBJ_FD	= (1U << 15), | ||||
| }; | ||||
| 
 | ||||
| /* Flags for BPF_PROG_QUERY. */ | ||||
|  | @ -1403,6 +1406,11 @@ union bpf_attr { | |||
| 		 * to using 5 hash functions). | ||||
| 		 */ | ||||
| 		__u64	map_extra; | ||||
| 
 | ||||
| 		__s32   value_type_btf_obj_fd;	/* fd pointing to a BTF
 | ||||
| 						 * type data for | ||||
| 						 * btf_vmlinux_value_type_id. | ||||
| 						 */ | ||||
| 	}; | ||||
| 
 | ||||
| 	struct { /* anonymous struct used by BPF_MAP_*_ELEM commands */ | ||||
|  |  | |||
|  | @ -641,6 +641,7 @@ static void __bpf_struct_ops_map_free(struct bpf_map *map) | |||
| 		bpf_jit_uncharge_modmem(PAGE_SIZE); | ||||
| 	} | ||||
| 	bpf_map_area_free(st_map->uvalue); | ||||
| 	btf_put(st_map->btf); | ||||
| 	bpf_map_area_free(st_map); | ||||
| } | ||||
| 
 | ||||
|  | @ -669,7 +670,8 @@ static void bpf_struct_ops_map_free(struct bpf_map *map) | |||
| static int bpf_struct_ops_map_alloc_check(union bpf_attr *attr) | ||||
| { | ||||
| 	if (attr->key_size != sizeof(unsigned int) || attr->max_entries != 1 || | ||||
| 	    (attr->map_flags & ~BPF_F_LINK) || !attr->btf_vmlinux_value_type_id) | ||||
| 	    (attr->map_flags & ~(BPF_F_LINK | BPF_F_VTYPE_BTF_OBJ_FD)) || | ||||
| 	    !attr->btf_vmlinux_value_type_id) | ||||
| 		return -EINVAL; | ||||
| 	return 0; | ||||
| } | ||||
|  | @ -681,15 +683,36 @@ static struct bpf_map *bpf_struct_ops_map_alloc(union bpf_attr *attr) | |||
| 	struct bpf_struct_ops_map *st_map; | ||||
| 	const struct btf_type *t, *vt; | ||||
| 	struct bpf_map *map; | ||||
| 	struct btf *btf; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	st_ops_desc = bpf_struct_ops_find_value(btf_vmlinux, attr->btf_vmlinux_value_type_id); | ||||
| 	if (!st_ops_desc) | ||||
| 		return ERR_PTR(-ENOTSUPP); | ||||
| 	if (attr->map_flags & BPF_F_VTYPE_BTF_OBJ_FD) { | ||||
| 		/* The map holds btf for its whole life time. */ | ||||
| 		btf = btf_get_by_fd(attr->value_type_btf_obj_fd); | ||||
| 		if (IS_ERR(btf)) | ||||
| 			return ERR_CAST(btf); | ||||
| 		if (!btf_is_module(btf)) { | ||||
| 			btf_put(btf); | ||||
| 			return ERR_PTR(-EINVAL); | ||||
| 		} | ||||
| 	} else { | ||||
| 		btf = bpf_get_btf_vmlinux(); | ||||
| 		if (IS_ERR(btf)) | ||||
| 			return ERR_CAST(btf); | ||||
| 		btf_get(btf); | ||||
| 	} | ||||
| 
 | ||||
| 	st_ops_desc = bpf_struct_ops_find_value(btf, attr->btf_vmlinux_value_type_id); | ||||
| 	if (!st_ops_desc) { | ||||
| 		ret = -ENOTSUPP; | ||||
| 		goto errout; | ||||
| 	} | ||||
| 
 | ||||
| 	vt = st_ops_desc->value_type; | ||||
| 	if (attr->value_size != vt->size) | ||||
| 		return ERR_PTR(-EINVAL); | ||||
| 	if (attr->value_size != vt->size) { | ||||
| 		ret = -EINVAL; | ||||
| 		goto errout; | ||||
| 	} | ||||
| 
 | ||||
| 	t = st_ops_desc->type; | ||||
| 
 | ||||
|  | @ -700,17 +723,17 @@ static struct bpf_map *bpf_struct_ops_map_alloc(union bpf_attr *attr) | |||
| 		(vt->size - sizeof(struct bpf_struct_ops_value)); | ||||
| 
 | ||||
| 	st_map = bpf_map_area_alloc(st_map_size, NUMA_NO_NODE); | ||||
| 	if (!st_map) | ||||
| 		return ERR_PTR(-ENOMEM); | ||||
| 	if (!st_map) { | ||||
| 		ret = -ENOMEM; | ||||
| 		goto errout; | ||||
| 	} | ||||
| 
 | ||||
| 	st_map->st_ops_desc = st_ops_desc; | ||||
| 	map = &st_map->map; | ||||
| 
 | ||||
| 	ret = bpf_jit_charge_modmem(PAGE_SIZE); | ||||
| 	if (ret) { | ||||
| 		__bpf_struct_ops_map_free(map); | ||||
| 		return ERR_PTR(ret); | ||||
| 	} | ||||
| 	if (ret) | ||||
| 		goto errout_free; | ||||
| 
 | ||||
| 	st_map->image = arch_alloc_bpf_trampoline(PAGE_SIZE); | ||||
| 	if (!st_map->image) { | ||||
|  | @ -719,24 +742,30 @@ static struct bpf_map *bpf_struct_ops_map_alloc(union bpf_attr *attr) | |||
| 		 * here. | ||||
| 		 */ | ||||
| 		bpf_jit_uncharge_modmem(PAGE_SIZE); | ||||
| 		__bpf_struct_ops_map_free(map); | ||||
| 		return ERR_PTR(-ENOMEM); | ||||
| 		ret = -ENOMEM; | ||||
| 		goto errout_free; | ||||
| 	} | ||||
| 	st_map->uvalue = bpf_map_area_alloc(vt->size, NUMA_NO_NODE); | ||||
| 	st_map->links = | ||||
| 		bpf_map_area_alloc(btf_type_vlen(t) * sizeof(struct bpf_links *), | ||||
| 				   NUMA_NO_NODE); | ||||
| 	if (!st_map->uvalue || !st_map->links) { | ||||
| 		__bpf_struct_ops_map_free(map); | ||||
| 		return ERR_PTR(-ENOMEM); | ||||
| 		ret = -ENOMEM; | ||||
| 		goto errout_free; | ||||
| 	} | ||||
| 
 | ||||
| 	st_map->btf = btf_vmlinux; | ||||
| 	st_map->btf = btf; | ||||
| 
 | ||||
| 	mutex_init(&st_map->lock); | ||||
| 	bpf_map_init_from_attr(map, attr); | ||||
| 
 | ||||
| 	return map; | ||||
| 
 | ||||
| errout_free: | ||||
| 	__bpf_struct_ops_map_free(map); | ||||
| errout: | ||||
| 	btf_put(btf); | ||||
| 
 | ||||
| 	return ERR_PTR(ret); | ||||
| } | ||||
| 
 | ||||
| static u64 bpf_struct_ops_map_mem_usage(const struct bpf_map *map) | ||||
|  |  | |||
|  | @ -1123,7 +1123,7 @@ static int map_check_btf(struct bpf_map *map, const struct btf *btf, | |||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| #define BPF_MAP_CREATE_LAST_FIELD map_extra | ||||
| #define BPF_MAP_CREATE_LAST_FIELD value_type_btf_obj_fd | ||||
| /* called via syscall */ | ||||
| static int map_create(union bpf_attr *attr) | ||||
| { | ||||
|  |  | |||
|  | @ -20290,6 +20290,7 @@ static int check_struct_ops_btf_id(struct bpf_verifier_env *env) | |||
| 	const struct btf_member *member; | ||||
| 	struct bpf_prog *prog = env->prog; | ||||
| 	u32 btf_id, member_idx; | ||||
| 	struct btf *btf; | ||||
| 	const char *mname; | ||||
| 
 | ||||
| 	if (!prog->gpl_compatible) { | ||||
|  | @ -20297,8 +20298,10 @@ static int check_struct_ops_btf_id(struct bpf_verifier_env *env) | |||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	btf = prog->aux->attach_btf ?: bpf_get_btf_vmlinux(); | ||||
| 
 | ||||
| 	btf_id = prog->aux->attach_btf_id; | ||||
| 	st_ops_desc = bpf_struct_ops_find(btf_vmlinux, btf_id); | ||||
| 	st_ops_desc = bpf_struct_ops_find(btf, btf_id); | ||||
| 	if (!st_ops_desc) { | ||||
| 		verbose(env, "attach_btf_id %u is not a supported struct\n", | ||||
| 			btf_id); | ||||
|  | @ -20315,8 +20318,8 @@ static int check_struct_ops_btf_id(struct bpf_verifier_env *env) | |||
| 	} | ||||
| 
 | ||||
| 	member = &btf_type_member(t)[member_idx]; | ||||
| 	mname = btf_name_by_offset(btf_vmlinux, member->name_off); | ||||
| 	func_proto = btf_type_resolve_func_ptr(btf_vmlinux, member->type, | ||||
| 	mname = btf_name_by_offset(btf, member->name_off); | ||||
| 	func_proto = btf_type_resolve_func_ptr(btf, member->type, | ||||
| 					       NULL); | ||||
| 	if (!func_proto) { | ||||
| 		verbose(env, "attach to invalid member %s(@idx %u) of struct %s\n", | ||||
|  |  | |||
|  | @ -1330,6 +1330,9 @@ enum { | |||
| 
 | ||||
| /* Get path from provided FD in BPF_OBJ_PIN/BPF_OBJ_GET commands */ | ||||
| 	BPF_F_PATH_FD		= (1U << 14), | ||||
| 
 | ||||
| /* Flag for value_type_btf_obj_fd, the fd is available */ | ||||
| 	BPF_F_VTYPE_BTF_OBJ_FD	= (1U << 15), | ||||
| }; | ||||
| 
 | ||||
| /* Flags for BPF_PROG_QUERY. */ | ||||
|  | @ -1403,6 +1406,11 @@ union bpf_attr { | |||
| 		 * to using 5 hash functions). | ||||
| 		 */ | ||||
| 		__u64	map_extra; | ||||
| 
 | ||||
| 		__s32   value_type_btf_obj_fd;	/* fd pointing to a BTF
 | ||||
| 						 * type data for | ||||
| 						 * btf_vmlinux_value_type_id. | ||||
| 						 */ | ||||
| 	}; | ||||
| 
 | ||||
| 	struct { /* anonymous struct used by BPF_MAP_*_ELEM commands */ | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Kui-Feng Lee
						Kui-Feng Lee