diff options
Diffstat (limited to 'kernel/bpf/map_in_map.c')
| -rw-r--r-- | kernel/bpf/map_in_map.c | 61 | 
1 files changed, 44 insertions, 17 deletions
| diff --git a/kernel/bpf/map_in_map.c b/kernel/bpf/map_in_map.c index 135205d0d560..38136ec4e095 100644 --- a/kernel/bpf/map_in_map.c +++ b/kernel/bpf/map_in_map.c @@ -12,6 +12,7 @@ struct bpf_map *bpf_map_meta_alloc(int inner_map_ufd)  	struct bpf_map *inner_map, *inner_map_meta;  	u32 inner_map_meta_size;  	struct fd f; +	int ret;  	f = fdget(inner_map_ufd);  	inner_map = __bpf_map_get(f); @@ -20,18 +21,13 @@ struct bpf_map *bpf_map_meta_alloc(int inner_map_ufd)  	/* Does not support >1 level map-in-map */  	if (inner_map->inner_map_meta) { -		fdput(f); -		return ERR_PTR(-EINVAL); +		ret = -EINVAL; +		goto put;  	}  	if (!inner_map->ops->map_meta_equal) { -		fdput(f); -		return ERR_PTR(-ENOTSUPP); -	} - -	if (map_value_has_spin_lock(inner_map)) { -		fdput(f); -		return ERR_PTR(-ENOTSUPP); +		ret = -ENOTSUPP; +		goto put;  	}  	inner_map_meta_size = sizeof(*inner_map_meta); @@ -41,8 +37,8 @@ struct bpf_map *bpf_map_meta_alloc(int inner_map_ufd)  	inner_map_meta = kzalloc(inner_map_meta_size, GFP_USER);  	if (!inner_map_meta) { -		fdput(f); -		return ERR_PTR(-ENOMEM); +		ret = -ENOMEM; +		goto put;  	}  	inner_map_meta->map_type = inner_map->map_type; @@ -50,9 +46,33 @@ struct bpf_map *bpf_map_meta_alloc(int inner_map_ufd)  	inner_map_meta->value_size = inner_map->value_size;  	inner_map_meta->map_flags = inner_map->map_flags;  	inner_map_meta->max_entries = inner_map->max_entries; -	inner_map_meta->spin_lock_off = inner_map->spin_lock_off; -	inner_map_meta->timer_off = inner_map->timer_off; -	inner_map_meta->kptr_off_tab = bpf_map_copy_kptr_off_tab(inner_map); + +	inner_map_meta->record = btf_record_dup(inner_map->record); +	if (IS_ERR(inner_map_meta->record)) { +		/* btf_record_dup returns NULL or valid pointer in case of +		 * invalid/empty/valid, but ERR_PTR in case of errors. During +		 * equality NULL or IS_ERR is equivalent. +		 */ +		ret = PTR_ERR(inner_map_meta->record); +		goto free; +	} +	if (inner_map_meta->record) { +		struct btf_field_offs *field_offs; +		/* If btf_record is !IS_ERR_OR_NULL, then field_offs is always +		 * valid. +		 */ +		field_offs = kmemdup(inner_map->field_offs, sizeof(*inner_map->field_offs), GFP_KERNEL | __GFP_NOWARN); +		if (!field_offs) { +			ret = -ENOMEM; +			goto free_rec; +		} +		inner_map_meta->field_offs = field_offs; +	} +	/* Note: We must use the same BTF, as we also used btf_record_dup above +	 * which relies on BTF being same for both maps, as some members like +	 * record->fields.list_head have pointers like value_rec pointing into +	 * inner_map->btf. +	 */  	if (inner_map->btf) {  		btf_get(inner_map->btf);  		inner_map_meta->btf = inner_map->btf; @@ -68,11 +88,19 @@ struct bpf_map *bpf_map_meta_alloc(int inner_map_ufd)  	fdput(f);  	return inner_map_meta; +free_rec: +	btf_record_free(inner_map_meta->record); +free: +	kfree(inner_map_meta); +put: +	fdput(f); +	return ERR_PTR(ret);  }  void bpf_map_meta_free(struct bpf_map *map_meta)  { -	bpf_map_free_kptr_off_tab(map_meta); +	kfree(map_meta->field_offs); +	bpf_map_free_record(map_meta);  	btf_put(map_meta->btf);  	kfree(map_meta);  } @@ -84,9 +112,8 @@ bool bpf_map_meta_equal(const struct bpf_map *meta0,  	return meta0->map_type == meta1->map_type &&  		meta0->key_size == meta1->key_size &&  		meta0->value_size == meta1->value_size && -		meta0->timer_off == meta1->timer_off &&  		meta0->map_flags == meta1->map_flags && -		bpf_map_equal_kptr_off_tab(meta0, meta1); +		btf_record_equal(meta0->record, meta1->record);  }  void *bpf_map_fd_get_ptr(struct bpf_map *map, |