diff options
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/bpf/btf.c | 108 | ||||
-rw-r--r-- | kernel/bpf/syscall.c | 24 |
2 files changed, 121 insertions, 11 deletions
diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index fa0dce0452e7..40950b6bf395 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c | |||
@@ -11,6 +11,7 @@ | |||
11 | #include <linux/file.h> | 11 | #include <linux/file.h> |
12 | #include <linux/uaccess.h> | 12 | #include <linux/uaccess.h> |
13 | #include <linux/kernel.h> | 13 | #include <linux/kernel.h> |
14 | #include <linux/idr.h> | ||
14 | #include <linux/bpf_verifier.h> | 15 | #include <linux/bpf_verifier.h> |
15 | #include <linux/btf.h> | 16 | #include <linux/btf.h> |
16 | 17 | ||
@@ -179,6 +180,9 @@ | |||
179 | i < btf_type_vlen(struct_type); \ | 180 | i < btf_type_vlen(struct_type); \ |
180 | i++, member++) | 181 | i++, member++) |
181 | 182 | ||
183 | static DEFINE_IDR(btf_idr); | ||
184 | static DEFINE_SPINLOCK(btf_idr_lock); | ||
185 | |||
182 | struct btf { | 186 | struct btf { |
183 | union { | 187 | union { |
184 | struct btf_header *hdr; | 188 | struct btf_header *hdr; |
@@ -193,6 +197,8 @@ struct btf { | |||
193 | u32 types_size; | 197 | u32 types_size; |
194 | u32 data_size; | 198 | u32 data_size; |
195 | refcount_t refcnt; | 199 | refcount_t refcnt; |
200 | u32 id; | ||
201 | struct rcu_head rcu; | ||
196 | }; | 202 | }; |
197 | 203 | ||
198 | enum verifier_phase { | 204 | enum verifier_phase { |
@@ -598,6 +604,42 @@ static int btf_add_type(struct btf_verifier_env *env, struct btf_type *t) | |||
598 | return 0; | 604 | return 0; |
599 | } | 605 | } |
600 | 606 | ||
607 | static int btf_alloc_id(struct btf *btf) | ||
608 | { | ||
609 | int id; | ||
610 | |||
611 | idr_preload(GFP_KERNEL); | ||
612 | spin_lock_bh(&btf_idr_lock); | ||
613 | id = idr_alloc_cyclic(&btf_idr, btf, 1, INT_MAX, GFP_ATOMIC); | ||
614 | if (id > 0) | ||
615 | btf->id = id; | ||
616 | spin_unlock_bh(&btf_idr_lock); | ||
617 | idr_preload_end(); | ||
618 | |||
619 | if (WARN_ON_ONCE(!id)) | ||
620 | return -ENOSPC; | ||
621 | |||
622 | return id > 0 ? 0 : id; | ||
623 | } | ||
624 | |||
625 | static void btf_free_id(struct btf *btf) | ||
626 | { | ||
627 | unsigned long flags; | ||
628 | |||
629 | /* | ||
630 | * In map-in-map, calling map_delete_elem() on outer | ||
631 | * map will call bpf_map_put on the inner map. | ||
632 | * It will then eventually call btf_free_id() | ||
633 | * on the inner map. Some of the map_delete_elem() | ||
634 | * implementation may have irq disabled, so | ||
635 | * we need to use the _irqsave() version instead | ||
636 | * of the _bh() version. | ||
637 | */ | ||
638 | spin_lock_irqsave(&btf_idr_lock, flags); | ||
639 | idr_remove(&btf_idr, btf->id); | ||
640 | spin_unlock_irqrestore(&btf_idr_lock, flags); | ||
641 | } | ||
642 | |||
601 | static void btf_free(struct btf *btf) | 643 | static void btf_free(struct btf *btf) |
602 | { | 644 | { |
603 | kvfree(btf->types); | 645 | kvfree(btf->types); |
@@ -607,15 +649,19 @@ static void btf_free(struct btf *btf) | |||
607 | kfree(btf); | 649 | kfree(btf); |
608 | } | 650 | } |
609 | 651 | ||
610 | static void btf_get(struct btf *btf) | 652 | static void btf_free_rcu(struct rcu_head *rcu) |
611 | { | 653 | { |
612 | refcount_inc(&btf->refcnt); | 654 | struct btf *btf = container_of(rcu, struct btf, rcu); |
655 | |||
656 | btf_free(btf); | ||
613 | } | 657 | } |
614 | 658 | ||
615 | void btf_put(struct btf *btf) | 659 | void btf_put(struct btf *btf) |
616 | { | 660 | { |
617 | if (btf && refcount_dec_and_test(&btf->refcnt)) | 661 | if (btf && refcount_dec_and_test(&btf->refcnt)) { |
618 | btf_free(btf); | 662 | btf_free_id(btf); |
663 | call_rcu(&btf->rcu, btf_free_rcu); | ||
664 | } | ||
619 | } | 665 | } |
620 | 666 | ||
621 | static int env_resolve_init(struct btf_verifier_env *env) | 667 | static int env_resolve_init(struct btf_verifier_env *env) |
@@ -2006,10 +2052,15 @@ const struct file_operations btf_fops = { | |||
2006 | .release = btf_release, | 2052 | .release = btf_release, |
2007 | }; | 2053 | }; |
2008 | 2054 | ||
2055 | static int __btf_new_fd(struct btf *btf) | ||
2056 | { | ||
2057 | return anon_inode_getfd("btf", &btf_fops, btf, O_RDONLY | O_CLOEXEC); | ||
2058 | } | ||
2059 | |||
2009 | int btf_new_fd(const union bpf_attr *attr) | 2060 | int btf_new_fd(const union bpf_attr *attr) |
2010 | { | 2061 | { |
2011 | struct btf *btf; | 2062 | struct btf *btf; |
2012 | int fd; | 2063 | int ret; |
2013 | 2064 | ||
2014 | btf = btf_parse(u64_to_user_ptr(attr->btf), | 2065 | btf = btf_parse(u64_to_user_ptr(attr->btf), |
2015 | attr->btf_size, attr->btf_log_level, | 2066 | attr->btf_size, attr->btf_log_level, |
@@ -2018,12 +2069,23 @@ int btf_new_fd(const union bpf_attr *attr) | |||
2018 | if (IS_ERR(btf)) | 2069 | if (IS_ERR(btf)) |
2019 | return PTR_ERR(btf); | 2070 | return PTR_ERR(btf); |
2020 | 2071 | ||
2021 | fd = anon_inode_getfd("btf", &btf_fops, btf, | 2072 | ret = btf_alloc_id(btf); |
2022 | O_RDONLY | O_CLOEXEC); | 2073 | if (ret) { |
2023 | if (fd < 0) | 2074 | btf_free(btf); |
2075 | return ret; | ||
2076 | } | ||
2077 | |||
2078 | /* | ||
2079 | * The BTF ID is published to the userspace. | ||
2080 | * All BTF free must go through call_rcu() from | ||
2081 | * now on (i.e. free by calling btf_put()). | ||
2082 | */ | ||
2083 | |||
2084 | ret = __btf_new_fd(btf); | ||
2085 | if (ret < 0) | ||
2024 | btf_put(btf); | 2086 | btf_put(btf); |
2025 | 2087 | ||
2026 | return fd; | 2088 | return ret; |
2027 | } | 2089 | } |
2028 | 2090 | ||
2029 | struct btf *btf_get_by_fd(int fd) | 2091 | struct btf *btf_get_by_fd(int fd) |
@@ -2042,7 +2104,7 @@ struct btf *btf_get_by_fd(int fd) | |||
2042 | } | 2104 | } |
2043 | 2105 | ||
2044 | btf = f.file->private_data; | 2106 | btf = f.file->private_data; |
2045 | btf_get(btf); | 2107 | refcount_inc(&btf->refcnt); |
2046 | fdput(f); | 2108 | fdput(f); |
2047 | 2109 | ||
2048 | return btf; | 2110 | return btf; |
@@ -2062,3 +2124,29 @@ int btf_get_info_by_fd(const struct btf *btf, | |||
2062 | 2124 | ||
2063 | return 0; | 2125 | return 0; |
2064 | } | 2126 | } |
2127 | |||
2128 | int btf_get_fd_by_id(u32 id) | ||
2129 | { | ||
2130 | struct btf *btf; | ||
2131 | int fd; | ||
2132 | |||
2133 | rcu_read_lock(); | ||
2134 | btf = idr_find(&btf_idr, id); | ||
2135 | if (!btf || !refcount_inc_not_zero(&btf->refcnt)) | ||
2136 | btf = ERR_PTR(-ENOENT); | ||
2137 | rcu_read_unlock(); | ||
2138 | |||
2139 | if (IS_ERR(btf)) | ||
2140 | return PTR_ERR(btf); | ||
2141 | |||
2142 | fd = __btf_new_fd(btf); | ||
2143 | if (fd < 0) | ||
2144 | btf_put(btf); | ||
2145 | |||
2146 | return fd; | ||
2147 | } | ||
2148 | |||
2149 | u32 btf_id(const struct btf *btf) | ||
2150 | { | ||
2151 | return btf->id; | ||
2152 | } | ||
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 9b87198deea2..31c4092da277 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c | |||
@@ -252,7 +252,6 @@ static void bpf_map_free_deferred(struct work_struct *work) | |||
252 | 252 | ||
253 | bpf_map_uncharge_memlock(map); | 253 | bpf_map_uncharge_memlock(map); |
254 | security_bpf_map_free(map); | 254 | security_bpf_map_free(map); |
255 | btf_put(map->btf); | ||
256 | /* implementation dependent freeing */ | 255 | /* implementation dependent freeing */ |
257 | map->ops->map_free(map); | 256 | map->ops->map_free(map); |
258 | } | 257 | } |
@@ -273,6 +272,7 @@ static void __bpf_map_put(struct bpf_map *map, bool do_idr_lock) | |||
273 | if (atomic_dec_and_test(&map->refcnt)) { | 272 | if (atomic_dec_and_test(&map->refcnt)) { |
274 | /* bpf_map_free_id() must be called first */ | 273 | /* bpf_map_free_id() must be called first */ |
275 | bpf_map_free_id(map, do_idr_lock); | 274 | bpf_map_free_id(map, do_idr_lock); |
275 | btf_put(map->btf); | ||
276 | INIT_WORK(&map->work, bpf_map_free_deferred); | 276 | INIT_WORK(&map->work, bpf_map_free_deferred); |
277 | schedule_work(&map->work); | 277 | schedule_work(&map->work); |
278 | } | 278 | } |
@@ -2002,6 +2002,12 @@ static int bpf_map_get_info_by_fd(struct bpf_map *map, | |||
2002 | info.map_flags = map->map_flags; | 2002 | info.map_flags = map->map_flags; |
2003 | memcpy(info.name, map->name, sizeof(map->name)); | 2003 | memcpy(info.name, map->name, sizeof(map->name)); |
2004 | 2004 | ||
2005 | if (map->btf) { | ||
2006 | info.btf_id = btf_id(map->btf); | ||
2007 | info.btf_key_id = map->btf_key_id; | ||
2008 | info.btf_value_id = map->btf_value_id; | ||
2009 | } | ||
2010 | |||
2005 | if (bpf_map_is_dev_bound(map)) { | 2011 | if (bpf_map_is_dev_bound(map)) { |
2006 | err = bpf_map_offload_info_fill(&info, map); | 2012 | err = bpf_map_offload_info_fill(&info, map); |
2007 | if (err) | 2013 | if (err) |
@@ -2059,6 +2065,19 @@ static int bpf_btf_load(const union bpf_attr *attr) | |||
2059 | return btf_new_fd(attr); | 2065 | return btf_new_fd(attr); |
2060 | } | 2066 | } |
2061 | 2067 | ||
2068 | #define BPF_BTF_GET_FD_BY_ID_LAST_FIELD btf_id | ||
2069 | |||
2070 | static int bpf_btf_get_fd_by_id(const union bpf_attr *attr) | ||
2071 | { | ||
2072 | if (CHECK_ATTR(BPF_BTF_GET_FD_BY_ID)) | ||
2073 | return -EINVAL; | ||
2074 | |||
2075 | if (!capable(CAP_SYS_ADMIN)) | ||
2076 | return -EPERM; | ||
2077 | |||
2078 | return btf_get_fd_by_id(attr->btf_id); | ||
2079 | } | ||
2080 | |||
2062 | SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, size) | 2081 | SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, size) |
2063 | { | 2082 | { |
2064 | union bpf_attr attr = {}; | 2083 | union bpf_attr attr = {}; |
@@ -2142,6 +2161,9 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz | |||
2142 | case BPF_BTF_LOAD: | 2161 | case BPF_BTF_LOAD: |
2143 | err = bpf_btf_load(&attr); | 2162 | err = bpf_btf_load(&attr); |
2144 | break; | 2163 | break; |
2164 | case BPF_BTF_GET_FD_BY_ID: | ||
2165 | err = bpf_btf_get_fd_by_id(&attr); | ||
2166 | break; | ||
2145 | default: | 2167 | default: |
2146 | err = -EINVAL; | 2168 | err = -EINVAL; |
2147 | break; | 2169 | break; |