Message ID | 20230119235833.2948341-5-void@manifault.com (mailing list archive) |
---|---|
State | Superseded |
Delegated to: | BPF |
Headers | show |
Series | Enable cpumasks to be used as kptrs | expand |
Hi David, Thank you for the patch! Perhaps something to improve: [auto build test WARNING on bpf-next/master] url: https://github.com/intel-lab-lkp/linux/commits/David-Vernet/bpf-Enable-annotating-trusted-nested-pointers/20230120-080139 base: https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git master patch link: https://lore.kernel.org/r/20230119235833.2948341-5-void%40manifault.com patch subject: [PATCH bpf-next 4/8] bpf: Enable cpumasks to be queried and used as kptrs config: sparc-allyesconfig (https://download.01.org/0day-ci/archive/20230120/202301201053.OKCBdOsh-lkp@intel.com/config) compiler: sparc64-linux-gcc (GCC) 12.1.0 reproduce (this is a W=1 build): wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # https://github.com/intel-lab-lkp/linux/commit/11541205c58f2226e5ffbc5967317469d65efac6 git remote add linux-review https://github.com/intel-lab-lkp/linux git fetch --no-tags linux-review David-Vernet/bpf-Enable-annotating-trusted-nested-pointers/20230120-080139 git checkout 11541205c58f2226e5ffbc5967317469d65efac6 # save the config file mkdir build_dir && cp config build_dir/.config COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=sparc olddefconfig COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=sparc SHELL=/bin/bash kernel/bpf/ If you fix the issue, kindly add following tag where applicable | Reported-by: kernel test robot <lkp@intel.com> All warnings (new ones prefixed by >>): >> kernel/bpf/cpumask.c:38:21: warning: no previous prototype for 'bpf_cpumask_create' [-Wmissing-prototypes] 38 | struct bpf_cpumask *bpf_cpumask_create(void) | ^~~~~~~~~~~~~~~~~~ >> kernel/bpf/cpumask.c:52:21: warning: no previous prototype for 'bpf_cpumask_acquire' [-Wmissing-prototypes] 52 | struct bpf_cpumask *bpf_cpumask_acquire(struct bpf_cpumask *cpumask) | ^~~~~~~~~~~~~~~~~~~ >> kernel/bpf/cpumask.c:58:21: warning: no previous prototype for 'bpf_cpumask_kptr_get' [-Wmissing-prototypes] 58 | struct bpf_cpumask *bpf_cpumask_kptr_get(struct bpf_cpumask **cpumaskp) | ^~~~~~~~~~~~~~~~~~~~ >> kernel/bpf/cpumask.c:76:6: warning: no previous prototype for 'bpf_cpumask_release' [-Wmissing-prototypes] 76 | void bpf_cpumask_release(struct bpf_cpumask *cpumask) | ^~~~~~~~~~~~~~~~~~~ >> kernel/bpf/cpumask.c:88:5: warning: no previous prototype for 'bpf_cpumask_first' [-Wmissing-prototypes] 88 | u32 bpf_cpumask_first(const struct cpumask *cpumask) | ^~~~~~~~~~~~~~~~~ >> kernel/bpf/cpumask.c:93:5: warning: no previous prototype for 'bpf_cpumask_first_zero' [-Wmissing-prototypes] 93 | u32 bpf_cpumask_first_zero(const struct cpumask *cpumask) | ^~~~~~~~~~~~~~~~~~~~~~ >> kernel/bpf/cpumask.c:98:6: warning: no previous prototype for 'bpf_cpumask_set_cpu' [-Wmissing-prototypes] 98 | void bpf_cpumask_set_cpu(u32 cpu, struct bpf_cpumask *cpumask) | ^~~~~~~~~~~~~~~~~~~ >> kernel/bpf/cpumask.c:106:6: warning: no previous prototype for 'bpf_cpumask_clear_cpu' [-Wmissing-prototypes] 106 | void bpf_cpumask_clear_cpu(u32 cpu, struct bpf_cpumask *cpumask) | ^~~~~~~~~~~~~~~~~~~~~ >> kernel/bpf/cpumask.c:114:6: warning: no previous prototype for 'bpf_cpumask_test_cpu' [-Wmissing-prototypes] 114 | bool bpf_cpumask_test_cpu(u32 cpu, const struct cpumask *cpumask) | ^~~~~~~~~~~~~~~~~~~~ >> kernel/bpf/cpumask.c:122:6: warning: no previous prototype for 'bpf_cpumask_test_and_set_cpu' [-Wmissing-prototypes] 122 | bool bpf_cpumask_test_and_set_cpu(u32 cpu, struct bpf_cpumask *cpumask) | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ >> kernel/bpf/cpumask.c:130:6: warning: no previous prototype for 'bpf_cpumask_test_and_clear_cpu' [-Wmissing-prototypes] 130 | bool bpf_cpumask_test_and_clear_cpu(u32 cpu, struct bpf_cpumask *cpumask) | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ >> kernel/bpf/cpumask.c:138:6: warning: no previous prototype for 'bpf_cpumask_setall' [-Wmissing-prototypes] 138 | void bpf_cpumask_setall(struct bpf_cpumask *cpumask) | ^~~~~~~~~~~~~~~~~~ >> kernel/bpf/cpumask.c:143:6: warning: no previous prototype for 'bpf_cpumask_clear' [-Wmissing-prototypes] 143 | void bpf_cpumask_clear(struct bpf_cpumask *cpumask) | ^~~~~~~~~~~~~~~~~ >> kernel/bpf/cpumask.c:148:6: warning: no previous prototype for 'bpf_cpumask_and' [-Wmissing-prototypes] 148 | bool bpf_cpumask_and(struct bpf_cpumask *dst, | ^~~~~~~~~~~~~~~ >> kernel/bpf/cpumask.c:155:6: warning: no previous prototype for 'bpf_cpumask_or' [-Wmissing-prototypes] 155 | void bpf_cpumask_or(struct bpf_cpumask *dst, | ^~~~~~~~~~~~~~ >> kernel/bpf/cpumask.c:162:6: warning: no previous prototype for 'bpf_cpumask_xor' [-Wmissing-prototypes] 162 | void bpf_cpumask_xor(struct bpf_cpumask *dst, | ^~~~~~~~~~~~~~~ >> kernel/bpf/cpumask.c:169:6: warning: no previous prototype for 'bpf_cpumask_equal' [-Wmissing-prototypes] 169 | bool bpf_cpumask_equal(const struct cpumask *src1, const struct cpumask *src2) | ^~~~~~~~~~~~~~~~~ >> kernel/bpf/cpumask.c:174:6: warning: no previous prototype for 'bpf_cpumask_intersects' [-Wmissing-prototypes] 174 | bool bpf_cpumask_intersects(const struct cpumask *src1, const struct cpumask *src2) | ^~~~~~~~~~~~~~~~~~~~~~ >> kernel/bpf/cpumask.c:179:6: warning: no previous prototype for 'bpf_cpumask_subset' [-Wmissing-prototypes] 179 | bool bpf_cpumask_subset(const struct cpumask *src1, const struct cpumask *src2) | ^~~~~~~~~~~~~~~~~~ >> kernel/bpf/cpumask.c:184:6: warning: no previous prototype for 'bpf_cpumask_empty' [-Wmissing-prototypes] 184 | bool bpf_cpumask_empty(const struct cpumask *cpumask) | ^~~~~~~~~~~~~~~~~ kernel/bpf/cpumask.c:189:6: warning: no previous prototype for 'bpf_cpumask_full' [-Wmissing-prototypes] 189 | bool bpf_cpumask_full(const struct cpumask *cpumask) | ^~~~~~~~~~~~~~~~ kernel/bpf/cpumask.c:194:6: warning: no previous prototype for 'bpf_cpumask_copy' [-Wmissing-prototypes] 194 | void bpf_cpumask_copy(struct bpf_cpumask *dst, const struct cpumask *src) | ^~~~~~~~~~~~~~~~ kernel/bpf/cpumask.c:199:5: warning: no previous prototype for 'bpf_cpumask_any' [-Wmissing-prototypes] 199 | u32 bpf_cpumask_any(const struct cpumask *cpumask) | ^~~~~~~~~~~~~~~ kernel/bpf/cpumask.c:204:5: warning: no previous prototype for 'bpf_cpumask_any_and' [-Wmissing-prototypes] 204 | u32 bpf_cpumask_any_and(const struct cpumask *src1, const struct cpumask *src2) | ^~~~~~~~~~~~~~~~~~~ vim +/bpf_cpumask_create +38 kernel/bpf/cpumask.c 37 > 38 struct bpf_cpumask *bpf_cpumask_create(void) 39 { 40 struct bpf_cpumask *cpumask; 41 42 cpumask = bpf_mem_alloc(&bpf_cpumask_ma, sizeof(*cpumask)); 43 if (!cpumask) 44 return NULL; 45 46 memset(cpumask, 0, sizeof(*cpumask)); 47 refcount_set(&cpumask->usage, 1); 48 49 return cpumask; 50 } 51 > 52 struct bpf_cpumask *bpf_cpumask_acquire(struct bpf_cpumask *cpumask) 53 { 54 refcount_inc(&cpumask->usage); 55 return cpumask; 56 } 57 > 58 struct bpf_cpumask *bpf_cpumask_kptr_get(struct bpf_cpumask **cpumaskp) 59 { 60 struct bpf_cpumask *cpumask; 61 62 /* The BPF memory allocator frees memory backing its caches in an RCU 63 * callback. Thus, we can safely use RCU to ensure that the cpumask is 64 * safe to read. 65 */ 66 rcu_read_lock(); 67 68 cpumask = READ_ONCE(*cpumaskp); 69 if (cpumask && !refcount_inc_not_zero(&cpumask->usage)) 70 cpumask = NULL; 71 72 rcu_read_unlock(); 73 return cpumask; 74 } 75 > 76 void bpf_cpumask_release(struct bpf_cpumask *cpumask) 77 { 78 if (!cpumask) 79 return; 80 81 if (refcount_dec_and_test(&cpumask->usage)) { 82 migrate_disable(); 83 bpf_mem_free(&bpf_cpumask_ma, cpumask); 84 migrate_enable(); 85 } 86 } 87 > 88 u32 bpf_cpumask_first(const struct cpumask *cpumask) 89 { 90 return cpumask_first(cpumask); 91 } 92 > 93 u32 bpf_cpumask_first_zero(const struct cpumask *cpumask) 94 { 95 return cpumask_first_zero(cpumask); 96 } 97 > 98 void bpf_cpumask_set_cpu(u32 cpu, struct bpf_cpumask *cpumask) 99 { 100 if (!cpu_valid(cpu)) 101 return; 102 103 cpumask_set_cpu(cpu, (struct cpumask *)cpumask); 104 } 105 > 106 void bpf_cpumask_clear_cpu(u32 cpu, struct bpf_cpumask *cpumask) 107 { 108 if (!cpu_valid(cpu)) 109 return; 110 111 cpumask_clear_cpu(cpu, (struct cpumask *)cpumask); 112 } 113 > 114 bool bpf_cpumask_test_cpu(u32 cpu, const struct cpumask *cpumask) 115 { 116 if (!cpu_valid(cpu)) 117 return false; 118 119 return cpumask_test_cpu(cpu, (struct cpumask *)cpumask); 120 } 121 > 122 bool bpf_cpumask_test_and_set_cpu(u32 cpu, struct bpf_cpumask *cpumask) 123 { 124 if (!cpu_valid(cpu)) 125 return false; 126 127 return cpumask_test_and_set_cpu(cpu, (struct cpumask *)cpumask); 128 } 129 > 130 bool bpf_cpumask_test_and_clear_cpu(u32 cpu, struct bpf_cpumask *cpumask) 131 { 132 if (!cpu_valid(cpu)) 133 return false; 134 135 return cpumask_test_and_clear_cpu(cpu, (struct cpumask *)cpumask); 136 } 137 > 138 void bpf_cpumask_setall(struct bpf_cpumask *cpumask) 139 { 140 cpumask_setall((struct cpumask *)cpumask); 141 } 142 > 143 void bpf_cpumask_clear(struct bpf_cpumask *cpumask) 144 { 145 cpumask_clear((struct cpumask *)cpumask); 146 } 147 > 148 bool bpf_cpumask_and(struct bpf_cpumask *dst, 149 const struct cpumask *src1, 150 const struct cpumask *src2) 151 { 152 return cpumask_and((struct cpumask *)dst, src1, src2); 153 } 154 > 155 void bpf_cpumask_or(struct bpf_cpumask *dst, 156 const struct cpumask *src1, 157 const struct cpumask *src2) 158 { 159 cpumask_or((struct cpumask *)dst, src1, src2); 160 } 161 > 162 void bpf_cpumask_xor(struct bpf_cpumask *dst, 163 const struct cpumask *src1, 164 const struct cpumask *src2) 165 { 166 cpumask_xor((struct cpumask *)dst, src1, src2); 167 } 168 > 169 bool bpf_cpumask_equal(const struct cpumask *src1, const struct cpumask *src2) 170 { 171 return cpumask_equal(src1, src2); 172 } 173 > 174 bool bpf_cpumask_intersects(const struct cpumask *src1, const struct cpumask *src2) 175 { 176 return cpumask_intersects(src1, src2); 177 } 178 > 179 bool bpf_cpumask_subset(const struct cpumask *src1, const struct cpumask *src2) 180 { 181 return cpumask_subset(src1, src2); 182 } 183 > 184 bool bpf_cpumask_empty(const struct cpumask *cpumask) 185 { 186 return cpumask_empty(cpumask); 187 } 188 > 189 bool bpf_cpumask_full(const struct cpumask *cpumask) 190 { 191 return cpumask_full(cpumask); 192 } 193 > 194 void bpf_cpumask_copy(struct bpf_cpumask *dst, const struct cpumask *src) 195 { 196 cpumask_copy((struct cpumask *)dst, src); 197 } 198 > 199 u32 bpf_cpumask_any(const struct cpumask *cpumask) 200 { 201 return cpumask_any(cpumask); 202 } 203 > 204 u32 bpf_cpumask_any_and(const struct cpumask *src1, const struct cpumask *src2) 205 { 206 return cpumask_any_and(src1, src2); 207 } 208
On Fri, Jan 20, 2023 at 10:36:24AM +0800, kernel test robot wrote: > Hi David, > > Thank you for the patch! Perhaps something to improve: > > [auto build test WARNING on bpf-next/master] > > url: https://github.com/intel-lab-lkp/linux/commits/David-Vernet/bpf-Enable-annotating-trusted-nested-pointers/20230120-080139 > base: https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git master > patch link: https://lore.kernel.org/r/20230119235833.2948341-5-void%40manifault.com > patch subject: [PATCH bpf-next 4/8] bpf: Enable cpumasks to be queried and used as kptrs > config: sparc-allyesconfig (https://download.01.org/0day-ci/archive/20230120/202301201053.OKCBdOsh-lkp@intel.com/config) > compiler: sparc64-linux-gcc (GCC) 12.1.0 > reproduce (this is a W=1 build): > wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross > chmod +x ~/bin/make.cross > # https://github.com/intel-lab-lkp/linux/commit/11541205c58f2226e5ffbc5967317469d65efac6 > git remote add linux-review https://github.com/intel-lab-lkp/linux > git fetch --no-tags linux-review David-Vernet/bpf-Enable-annotating-trusted-nested-pointers/20230120-080139 > git checkout 11541205c58f2226e5ffbc5967317469d65efac6 > # save the config file > mkdir build_dir && cp config build_dir/.config > COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=sparc olddefconfig > COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=sparc SHELL=/bin/bash kernel/bpf/ > > If you fix the issue, kindly add following tag where applicable > | Reported-by: kernel test robot <lkp@intel.com> > > All warnings (new ones prefixed by >>): > > >> kernel/bpf/cpumask.c:38:21: warning: no previous prototype for 'bpf_cpumask_create' [-Wmissing-prototypes] > 38 | struct bpf_cpumask *bpf_cpumask_create(void) Sorry, also forgot to do the __diag() dance described in [0]. I'll include that fix in v2. [0]: https://docs.kernel.org/bpf/kfuncs.html#creating-a-wrapper-kfunc This is also reminding me to send out the v2 for [1]. I'll do that after I take care of another few small things I've been putting off. [1]: https://lore.kernel.org/lkml/20230106195130.1216841-3-void@manifault.com/T/ Thanks, David
On Thu, Jan 19, 2023 at 05:58:29PM -0600, David Vernet wrote: > silently check for and ignore these cases at runtime. When we have e.g. > per-argument kfunc flags, it might be helpful to add another KF_CPU-type > flag that specifies that the verifier should validate that it's a valid > CPU. ... > +void bpf_cpumask_set_cpu(u32 cpu, struct bpf_cpumask *cpumask) > +{ > + if (!cpu_valid(cpu)) > + return; > + > + cpumask_set_cpu(cpu, (struct cpumask *)cpumask); > +} ... > +void bpf_cpumask_clear_cpu(u32 cpu, struct bpf_cpumask *cpumask) > +{ > + if (!cpu_valid(cpu)) > + return; I don't think we'll be able to get rid of this with KF_CPU or special suffix. The argument might be a variable and not a constant at the verification time. We would have to allow passing unknown vars otherwise the UX will be too restrictive, so this run-time check would have to stay.
On Thu, Jan 19, 2023 at 09:48:23PM -0800, Alexei Starovoitov wrote: > On Thu, Jan 19, 2023 at 05:58:29PM -0600, David Vernet wrote: > > silently check for and ignore these cases at runtime. When we have e.g. > > per-argument kfunc flags, it might be helpful to add another KF_CPU-type > > flag that specifies that the verifier should validate that it's a valid > > CPU. > > ... > > > +void bpf_cpumask_set_cpu(u32 cpu, struct bpf_cpumask *cpumask) > > +{ > > + if (!cpu_valid(cpu)) > > + return; > > + > > + cpumask_set_cpu(cpu, (struct cpumask *)cpumask); > > +} > > ... > > > +void bpf_cpumask_clear_cpu(u32 cpu, struct bpf_cpumask *cpumask) > > +{ > > + if (!cpu_valid(cpu)) > > + return; > > I don't think we'll be able to get rid of this with KF_CPU or special suffix. > The argument might be a variable and not a constant at the verification time. > We would have to allow passing unknown vars otherwise the UX will be too restrictive, > so this run-time check would have to stay. Makes sense. We'll just leave it as is then and document that passing in cpu >= nr_cpus is silently ignored for any kfunc taking a cpu argument.
On Thu, Jan 19, 2023 at 11:50:47PM -0600, David Vernet wrote: > On Thu, Jan 19, 2023 at 09:48:23PM -0800, Alexei Starovoitov wrote: > > On Thu, Jan 19, 2023 at 05:58:29PM -0600, David Vernet wrote: > > > silently check for and ignore these cases at runtime. When we have e.g. > > > per-argument kfunc flags, it might be helpful to add another KF_CPU-type > > > flag that specifies that the verifier should validate that it's a valid > > > CPU. > > > > ... > > > > > +void bpf_cpumask_set_cpu(u32 cpu, struct bpf_cpumask *cpumask) > > > +{ > > > + if (!cpu_valid(cpu)) > > > + return; > > > + > > > + cpumask_set_cpu(cpu, (struct cpumask *)cpumask); > > > +} > > > > ... > > > > > +void bpf_cpumask_clear_cpu(u32 cpu, struct bpf_cpumask *cpumask) > > > +{ > > > + if (!cpu_valid(cpu)) > > > + return; > > > > I don't think we'll be able to get rid of this with KF_CPU or special suffix. > > The argument might be a variable and not a constant at the verification time. > > We would have to allow passing unknown vars otherwise the UX will be too restrictive, > > so this run-time check would have to stay. > > Makes sense. We'll just leave it as is then and document that passing in > cpu >= nr_cpus is silently ignored for any kfunc taking a cpu argument. Eventually we can clean it up with bpf_assert infra.
Hi David, Thank you for the patch! Perhaps something to improve: [auto build test WARNING on bpf-next/master] url: https://github.com/intel-lab-lkp/linux/commits/David-Vernet/bpf-Enable-annotating-trusted-nested-pointers/20230120-080139 base: https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git master patch link: https://lore.kernel.org/r/20230119235833.2948341-5-void%40manifault.com patch subject: [PATCH bpf-next 4/8] bpf: Enable cpumasks to be queried and used as kptrs config: i386-randconfig-a015 (https://download.01.org/0day-ci/archive/20230120/202301201454.hTFU2jPK-lkp@intel.com/config) compiler: clang version 14.0.6 (https://github.com/llvm/llvm-project f28c006a5895fc0e329fe15fead81e37457cb1d1) reproduce (this is a W=1 build): wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # https://github.com/intel-lab-lkp/linux/commit/11541205c58f2226e5ffbc5967317469d65efac6 git remote add linux-review https://github.com/intel-lab-lkp/linux git fetch --no-tags linux-review David-Vernet/bpf-Enable-annotating-trusted-nested-pointers/20230120-080139 git checkout 11541205c58f2226e5ffbc5967317469d65efac6 # save the config file mkdir build_dir && cp config build_dir/.config COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=i386 olddefconfig COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=i386 SHELL=/bin/bash If you fix the issue, kindly add following tag where applicable | Reported-by: kernel test robot <lkp@intel.com> All warnings (new ones prefixed by >>): >> kernel/bpf/cpumask.c:38:21: warning: no previous prototype for function 'bpf_cpumask_create' [-Wmissing-prototypes] struct bpf_cpumask *bpf_cpumask_create(void) ^ kernel/bpf/cpumask.c:38:1: note: declare 'static' if the function is not intended to be used outside of this translation unit struct bpf_cpumask *bpf_cpumask_create(void) ^ static >> kernel/bpf/cpumask.c:52:21: warning: no previous prototype for function 'bpf_cpumask_acquire' [-Wmissing-prototypes] struct bpf_cpumask *bpf_cpumask_acquire(struct bpf_cpumask *cpumask) ^ kernel/bpf/cpumask.c:52:1: note: declare 'static' if the function is not intended to be used outside of this translation unit struct bpf_cpumask *bpf_cpumask_acquire(struct bpf_cpumask *cpumask) ^ static >> kernel/bpf/cpumask.c:58:21: warning: no previous prototype for function 'bpf_cpumask_kptr_get' [-Wmissing-prototypes] struct bpf_cpumask *bpf_cpumask_kptr_get(struct bpf_cpumask **cpumaskp) ^ kernel/bpf/cpumask.c:58:1: note: declare 'static' if the function is not intended to be used outside of this translation unit struct bpf_cpumask *bpf_cpumask_kptr_get(struct bpf_cpumask **cpumaskp) ^ static >> kernel/bpf/cpumask.c:76:6: warning: no previous prototype for function 'bpf_cpumask_release' [-Wmissing-prototypes] void bpf_cpumask_release(struct bpf_cpumask *cpumask) ^ kernel/bpf/cpumask.c:76:1: note: declare 'static' if the function is not intended to be used outside of this translation unit void bpf_cpumask_release(struct bpf_cpumask *cpumask) ^ static >> kernel/bpf/cpumask.c:88:5: warning: no previous prototype for function 'bpf_cpumask_first' [-Wmissing-prototypes] u32 bpf_cpumask_first(const struct cpumask *cpumask) ^ kernel/bpf/cpumask.c:88:1: note: declare 'static' if the function is not intended to be used outside of this translation unit u32 bpf_cpumask_first(const struct cpumask *cpumask) ^ static >> kernel/bpf/cpumask.c:93:5: warning: no previous prototype for function 'bpf_cpumask_first_zero' [-Wmissing-prototypes] u32 bpf_cpumask_first_zero(const struct cpumask *cpumask) ^ kernel/bpf/cpumask.c:93:1: note: declare 'static' if the function is not intended to be used outside of this translation unit u32 bpf_cpumask_first_zero(const struct cpumask *cpumask) ^ static >> kernel/bpf/cpumask.c:98:6: warning: no previous prototype for function 'bpf_cpumask_set_cpu' [-Wmissing-prototypes] void bpf_cpumask_set_cpu(u32 cpu, struct bpf_cpumask *cpumask) ^ kernel/bpf/cpumask.c:98:1: note: declare 'static' if the function is not intended to be used outside of this translation unit void bpf_cpumask_set_cpu(u32 cpu, struct bpf_cpumask *cpumask) ^ static >> kernel/bpf/cpumask.c:106:6: warning: no previous prototype for function 'bpf_cpumask_clear_cpu' [-Wmissing-prototypes] void bpf_cpumask_clear_cpu(u32 cpu, struct bpf_cpumask *cpumask) ^ kernel/bpf/cpumask.c:106:1: note: declare 'static' if the function is not intended to be used outside of this translation unit void bpf_cpumask_clear_cpu(u32 cpu, struct bpf_cpumask *cpumask) ^ static >> kernel/bpf/cpumask.c:114:6: warning: no previous prototype for function 'bpf_cpumask_test_cpu' [-Wmissing-prototypes] bool bpf_cpumask_test_cpu(u32 cpu, const struct cpumask *cpumask) ^ kernel/bpf/cpumask.c:114:1: note: declare 'static' if the function is not intended to be used outside of this translation unit bool bpf_cpumask_test_cpu(u32 cpu, const struct cpumask *cpumask) ^ static >> kernel/bpf/cpumask.c:122:6: warning: no previous prototype for function 'bpf_cpumask_test_and_set_cpu' [-Wmissing-prototypes] bool bpf_cpumask_test_and_set_cpu(u32 cpu, struct bpf_cpumask *cpumask) ^ kernel/bpf/cpumask.c:122:1: note: declare 'static' if the function is not intended to be used outside of this translation unit bool bpf_cpumask_test_and_set_cpu(u32 cpu, struct bpf_cpumask *cpumask) ^ static >> kernel/bpf/cpumask.c:130:6: warning: no previous prototype for function 'bpf_cpumask_test_and_clear_cpu' [-Wmissing-prototypes] bool bpf_cpumask_test_and_clear_cpu(u32 cpu, struct bpf_cpumask *cpumask) ^ kernel/bpf/cpumask.c:130:1: note: declare 'static' if the function is not intended to be used outside of this translation unit bool bpf_cpumask_test_and_clear_cpu(u32 cpu, struct bpf_cpumask *cpumask) ^ static >> kernel/bpf/cpumask.c:138:6: warning: no previous prototype for function 'bpf_cpumask_setall' [-Wmissing-prototypes] void bpf_cpumask_setall(struct bpf_cpumask *cpumask) ^ kernel/bpf/cpumask.c:138:1: note: declare 'static' if the function is not intended to be used outside of this translation unit void bpf_cpumask_setall(struct bpf_cpumask *cpumask) ^ static >> kernel/bpf/cpumask.c:143:6: warning: no previous prototype for function 'bpf_cpumask_clear' [-Wmissing-prototypes] void bpf_cpumask_clear(struct bpf_cpumask *cpumask) ^ kernel/bpf/cpumask.c:143:1: note: declare 'static' if the function is not intended to be used outside of this translation unit void bpf_cpumask_clear(struct bpf_cpumask *cpumask) ^ static >> kernel/bpf/cpumask.c:148:6: warning: no previous prototype for function 'bpf_cpumask_and' [-Wmissing-prototypes] bool bpf_cpumask_and(struct bpf_cpumask *dst, ^ kernel/bpf/cpumask.c:148:1: note: declare 'static' if the function is not intended to be used outside of this translation unit bool bpf_cpumask_and(struct bpf_cpumask *dst, ^ static >> kernel/bpf/cpumask.c:155:6: warning: no previous prototype for function 'bpf_cpumask_or' [-Wmissing-prototypes] void bpf_cpumask_or(struct bpf_cpumask *dst, ^ kernel/bpf/cpumask.c:155:1: note: declare 'static' if the function is not intended to be used outside of this translation unit void bpf_cpumask_or(struct bpf_cpumask *dst, ^ static >> kernel/bpf/cpumask.c:162:6: warning: no previous prototype for function 'bpf_cpumask_xor' [-Wmissing-prototypes] void bpf_cpumask_xor(struct bpf_cpumask *dst, ^ kernel/bpf/cpumask.c:162:1: note: declare 'static' if the function is not intended to be used outside of this translation unit void bpf_cpumask_xor(struct bpf_cpumask *dst, ^ static >> kernel/bpf/cpumask.c:169:6: warning: no previous prototype for function 'bpf_cpumask_equal' [-Wmissing-prototypes] bool bpf_cpumask_equal(const struct cpumask *src1, const struct cpumask *src2) ^ kernel/bpf/cpumask.c:169:1: note: declare 'static' if the function is not intended to be used outside of this translation unit bool bpf_cpumask_equal(const struct cpumask *src1, const struct cpumask *src2) ^ static >> kernel/bpf/cpumask.c:174:6: warning: no previous prototype for function 'bpf_cpumask_intersects' [-Wmissing-prototypes] bool bpf_cpumask_intersects(const struct cpumask *src1, const struct cpumask *src2) ^ kernel/bpf/cpumask.c:174:1: note: declare 'static' if the function is not intended to be used outside of this translation unit bool bpf_cpumask_intersects(const struct cpumask *src1, const struct cpumask *src2) ^ static >> kernel/bpf/cpumask.c:179:6: warning: no previous prototype for function 'bpf_cpumask_subset' [-Wmissing-prototypes] bool bpf_cpumask_subset(const struct cpumask *src1, const struct cpumask *src2) ^ kernel/bpf/cpumask.c:179:1: note: declare 'static' if the function is not intended to be used outside of this translation unit bool bpf_cpumask_subset(const struct cpumask *src1, const struct cpumask *src2) ^ static >> kernel/bpf/cpumask.c:184:6: warning: no previous prototype for function 'bpf_cpumask_empty' [-Wmissing-prototypes] bool bpf_cpumask_empty(const struct cpumask *cpumask) ^ kernel/bpf/cpumask.c:184:1: note: declare 'static' if the function is not intended to be used outside of this translation unit bool bpf_cpumask_empty(const struct cpumask *cpumask) ^ static kernel/bpf/cpumask.c:189:6: warning: no previous prototype for function 'bpf_cpumask_full' [-Wmissing-prototypes] bool bpf_cpumask_full(const struct cpumask *cpumask) ^ kernel/bpf/cpumask.c:189:1: note: declare 'static' if the function is not intended to be used outside of this translation unit bool bpf_cpumask_full(const struct cpumask *cpumask) ^ static kernel/bpf/cpumask.c:194:6: warning: no previous prototype for function 'bpf_cpumask_copy' [-Wmissing-prototypes] void bpf_cpumask_copy(struct bpf_cpumask *dst, const struct cpumask *src) ^ kernel/bpf/cpumask.c:194:1: note: declare 'static' if the function is not intended to be used outside of this translation unit void bpf_cpumask_copy(struct bpf_cpumask *dst, const struct cpumask *src) ^ static kernel/bpf/cpumask.c:199:5: warning: no previous prototype for function 'bpf_cpumask_any' [-Wmissing-prototypes] u32 bpf_cpumask_any(const struct cpumask *cpumask) ^ kernel/bpf/cpumask.c:199:1: note: declare 'static' if the function is not intended to be used outside of this translation unit u32 bpf_cpumask_any(const struct cpumask *cpumask) ^ static kernel/bpf/cpumask.c:204:5: warning: no previous prototype for function 'bpf_cpumask_any_and' [-Wmissing-prototypes] u32 bpf_cpumask_any_and(const struct cpumask *src1, const struct cpumask *src2) ^ kernel/bpf/cpumask.c:204:1: note: declare 'static' if the function is not intended to be used outside of this translation unit u32 bpf_cpumask_any_and(const struct cpumask *src1, const struct cpumask *src2) ^ static 24 warnings generated. vim +/bpf_cpumask_create +38 kernel/bpf/cpumask.c 37 > 38 struct bpf_cpumask *bpf_cpumask_create(void) 39 { 40 struct bpf_cpumask *cpumask; 41 42 cpumask = bpf_mem_alloc(&bpf_cpumask_ma, sizeof(*cpumask)); 43 if (!cpumask) 44 return NULL; 45 46 memset(cpumask, 0, sizeof(*cpumask)); 47 refcount_set(&cpumask->usage, 1); 48 49 return cpumask; 50 } 51 > 52 struct bpf_cpumask *bpf_cpumask_acquire(struct bpf_cpumask *cpumask) 53 { 54 refcount_inc(&cpumask->usage); 55 return cpumask; 56 } 57 > 58 struct bpf_cpumask *bpf_cpumask_kptr_get(struct bpf_cpumask **cpumaskp) 59 { 60 struct bpf_cpumask *cpumask; 61 62 /* The BPF memory allocator frees memory backing its caches in an RCU 63 * callback. Thus, we can safely use RCU to ensure that the cpumask is 64 * safe to read. 65 */ 66 rcu_read_lock(); 67 68 cpumask = READ_ONCE(*cpumaskp); 69 if (cpumask && !refcount_inc_not_zero(&cpumask->usage)) 70 cpumask = NULL; 71 72 rcu_read_unlock(); 73 return cpumask; 74 } 75 > 76 void bpf_cpumask_release(struct bpf_cpumask *cpumask) 77 { 78 if (!cpumask) 79 return; 80 81 if (refcount_dec_and_test(&cpumask->usage)) { 82 migrate_disable(); 83 bpf_mem_free(&bpf_cpumask_ma, cpumask); 84 migrate_enable(); 85 } 86 } 87 > 88 u32 bpf_cpumask_first(const struct cpumask *cpumask) 89 { 90 return cpumask_first(cpumask); 91 } 92 > 93 u32 bpf_cpumask_first_zero(const struct cpumask *cpumask) 94 { 95 return cpumask_first_zero(cpumask); 96 } 97 > 98 void bpf_cpumask_set_cpu(u32 cpu, struct bpf_cpumask *cpumask) 99 { 100 if (!cpu_valid(cpu)) 101 return; 102 103 cpumask_set_cpu(cpu, (struct cpumask *)cpumask); 104 } 105 > 106 void bpf_cpumask_clear_cpu(u32 cpu, struct bpf_cpumask *cpumask) 107 { 108 if (!cpu_valid(cpu)) 109 return; 110 111 cpumask_clear_cpu(cpu, (struct cpumask *)cpumask); 112 } 113 > 114 bool bpf_cpumask_test_cpu(u32 cpu, const struct cpumask *cpumask) 115 { 116 if (!cpu_valid(cpu)) 117 return false; 118 119 return cpumask_test_cpu(cpu, (struct cpumask *)cpumask); 120 } 121 > 122 bool bpf_cpumask_test_and_set_cpu(u32 cpu, struct bpf_cpumask *cpumask) 123 { 124 if (!cpu_valid(cpu)) 125 return false; 126 127 return cpumask_test_and_set_cpu(cpu, (struct cpumask *)cpumask); 128 } 129 > 130 bool bpf_cpumask_test_and_clear_cpu(u32 cpu, struct bpf_cpumask *cpumask) 131 { 132 if (!cpu_valid(cpu)) 133 return false; 134 135 return cpumask_test_and_clear_cpu(cpu, (struct cpumask *)cpumask); 136 } 137 > 138 void bpf_cpumask_setall(struct bpf_cpumask *cpumask) 139 { 140 cpumask_setall((struct cpumask *)cpumask); 141 } 142 > 143 void bpf_cpumask_clear(struct bpf_cpumask *cpumask) 144 { 145 cpumask_clear((struct cpumask *)cpumask); 146 } 147 > 148 bool bpf_cpumask_and(struct bpf_cpumask *dst, 149 const struct cpumask *src1, 150 const struct cpumask *src2) 151 { 152 return cpumask_and((struct cpumask *)dst, src1, src2); 153 } 154 > 155 void bpf_cpumask_or(struct bpf_cpumask *dst, 156 const struct cpumask *src1, 157 const struct cpumask *src2) 158 { 159 cpumask_or((struct cpumask *)dst, src1, src2); 160 } 161 > 162 void bpf_cpumask_xor(struct bpf_cpumask *dst, 163 const struct cpumask *src1, 164 const struct cpumask *src2) 165 { 166 cpumask_xor((struct cpumask *)dst, src1, src2); 167 } 168 > 169 bool bpf_cpumask_equal(const struct cpumask *src1, const struct cpumask *src2) 170 { 171 return cpumask_equal(src1, src2); 172 } 173 > 174 bool bpf_cpumask_intersects(const struct cpumask *src1, const struct cpumask *src2) 175 { 176 return cpumask_intersects(src1, src2); 177 } 178 > 179 bool bpf_cpumask_subset(const struct cpumask *src1, const struct cpumask *src2) 180 { 181 return cpumask_subset(src1, src2); 182 } 183 > 184 bool bpf_cpumask_empty(const struct cpumask *cpumask) 185 { 186 return cpumask_empty(cpumask); 187 } 188 > 189 bool bpf_cpumask_full(const struct cpumask *cpumask) 190 { 191 return cpumask_full(cpumask); 192 } 193 > 194 void bpf_cpumask_copy(struct bpf_cpumask *dst, const struct cpumask *src) 195 { 196 cpumask_copy((struct cpumask *)dst, src); 197 } 198 > 199 u32 bpf_cpumask_any(const struct cpumask *cpumask) 200 { 201 return cpumask_any(cpumask); 202 } 203 > 204 u32 bpf_cpumask_any_and(const struct cpumask *src1, const struct cpumask *src2) 205 { 206 return cpumask_any_and(src1, src2); 207 } 208
diff --git a/kernel/bpf/Makefile b/kernel/bpf/Makefile index 3a12e6b400a2..02242614dcc7 100644 --- a/kernel/bpf/Makefile +++ b/kernel/bpf/Makefile @@ -36,6 +36,7 @@ obj-$(CONFIG_DEBUG_INFO_BTF) += sysfs_btf.o endif ifeq ($(CONFIG_BPF_JIT),y) obj-$(CONFIG_BPF_SYSCALL) += bpf_struct_ops.o +obj-$(CONFIG_BPF_SYSCALL) += cpumask.o obj-${CONFIG_BPF_LSM} += bpf_lsm.o endif obj-$(CONFIG_BPF_PRELOAD) += preload/ diff --git a/kernel/bpf/cpumask.c b/kernel/bpf/cpumask.c new file mode 100644 index 000000000000..e1fa15a7e079 --- /dev/null +++ b/kernel/bpf/cpumask.c @@ -0,0 +1,263 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2023 Meta, Inc + */ +#include <linux/bpf.h> +#include <linux/bpf_mem_alloc.h> +#include <linux/btf.h> +#include <linux/btf_ids.h> +#include <linux/cpumask.h> + +/** + * struct bpf_cpumask - refcounted BPF cpumask wrapper structure + * @cpumask: The actual cpumask embedded in the struct. + * @usage: Object reference counter. When the refcount goes to 0, the + * memory is released back to the BPF allocator, which provides + * RCU safety. + * + * Note that we explicitly embed a cpumask_t rather than a cpumask_var_t. This + * is done to avoid confusing the verifier due to the typedef of cpumask_var_t + * changing depending on whether CONFIG_CPUMASK_OFFSTACK is defined or not. See + * the details in <linux/cpumask.h>. The consequence is that this structure is + * likely a bit larger than it needs to be when CONFIG_CPUMASK_OFFSTACK is + * defined due to embedding the whole NR_CPUS-size bitmap, but the extra memory + * overhead it's minimal. For the more typical case of CONFIG_CPUMASK_OFFSTACK + * not being defined, the structure is the same size regardless. + */ +struct bpf_cpumask { + cpumask_t cpumask; + refcount_t usage; +}; + +static struct bpf_mem_alloc bpf_cpumask_ma; + +static bool cpu_valid(u32 cpu) +{ + return cpu < nr_cpu_ids; +} + +struct bpf_cpumask *bpf_cpumask_create(void) +{ + struct bpf_cpumask *cpumask; + + cpumask = bpf_mem_alloc(&bpf_cpumask_ma, sizeof(*cpumask)); + if (!cpumask) + return NULL; + + memset(cpumask, 0, sizeof(*cpumask)); + refcount_set(&cpumask->usage, 1); + + return cpumask; +} + +struct bpf_cpumask *bpf_cpumask_acquire(struct bpf_cpumask *cpumask) +{ + refcount_inc(&cpumask->usage); + return cpumask; +} + +struct bpf_cpumask *bpf_cpumask_kptr_get(struct bpf_cpumask **cpumaskp) +{ + struct bpf_cpumask *cpumask; + + /* The BPF memory allocator frees memory backing its caches in an RCU + * callback. Thus, we can safely use RCU to ensure that the cpumask is + * safe to read. + */ + rcu_read_lock(); + + cpumask = READ_ONCE(*cpumaskp); + if (cpumask && !refcount_inc_not_zero(&cpumask->usage)) + cpumask = NULL; + + rcu_read_unlock(); + return cpumask; +} + +void bpf_cpumask_release(struct bpf_cpumask *cpumask) +{ + if (!cpumask) + return; + + if (refcount_dec_and_test(&cpumask->usage)) { + migrate_disable(); + bpf_mem_free(&bpf_cpumask_ma, cpumask); + migrate_enable(); + } +} + +u32 bpf_cpumask_first(const struct cpumask *cpumask) +{ + return cpumask_first(cpumask); +} + +u32 bpf_cpumask_first_zero(const struct cpumask *cpumask) +{ + return cpumask_first_zero(cpumask); +} + +void bpf_cpumask_set_cpu(u32 cpu, struct bpf_cpumask *cpumask) +{ + if (!cpu_valid(cpu)) + return; + + cpumask_set_cpu(cpu, (struct cpumask *)cpumask); +} + +void bpf_cpumask_clear_cpu(u32 cpu, struct bpf_cpumask *cpumask) +{ + if (!cpu_valid(cpu)) + return; + + cpumask_clear_cpu(cpu, (struct cpumask *)cpumask); +} + +bool bpf_cpumask_test_cpu(u32 cpu, const struct cpumask *cpumask) +{ + if (!cpu_valid(cpu)) + return false; + + return cpumask_test_cpu(cpu, (struct cpumask *)cpumask); +} + +bool bpf_cpumask_test_and_set_cpu(u32 cpu, struct bpf_cpumask *cpumask) +{ + if (!cpu_valid(cpu)) + return false; + + return cpumask_test_and_set_cpu(cpu, (struct cpumask *)cpumask); +} + +bool bpf_cpumask_test_and_clear_cpu(u32 cpu, struct bpf_cpumask *cpumask) +{ + if (!cpu_valid(cpu)) + return false; + + return cpumask_test_and_clear_cpu(cpu, (struct cpumask *)cpumask); +} + +void bpf_cpumask_setall(struct bpf_cpumask *cpumask) +{ + cpumask_setall((struct cpumask *)cpumask); +} + +void bpf_cpumask_clear(struct bpf_cpumask *cpumask) +{ + cpumask_clear((struct cpumask *)cpumask); +} + +bool bpf_cpumask_and(struct bpf_cpumask *dst, + const struct cpumask *src1, + const struct cpumask *src2) +{ + return cpumask_and((struct cpumask *)dst, src1, src2); +} + +void bpf_cpumask_or(struct bpf_cpumask *dst, + const struct cpumask *src1, + const struct cpumask *src2) +{ + cpumask_or((struct cpumask *)dst, src1, src2); +} + +void bpf_cpumask_xor(struct bpf_cpumask *dst, + const struct cpumask *src1, + const struct cpumask *src2) +{ + cpumask_xor((struct cpumask *)dst, src1, src2); +} + +bool bpf_cpumask_equal(const struct cpumask *src1, const struct cpumask *src2) +{ + return cpumask_equal(src1, src2); +} + +bool bpf_cpumask_intersects(const struct cpumask *src1, const struct cpumask *src2) +{ + return cpumask_intersects(src1, src2); +} + +bool bpf_cpumask_subset(const struct cpumask *src1, const struct cpumask *src2) +{ + return cpumask_subset(src1, src2); +} + +bool bpf_cpumask_empty(const struct cpumask *cpumask) +{ + return cpumask_empty(cpumask); +} + +bool bpf_cpumask_full(const struct cpumask *cpumask) +{ + return cpumask_full(cpumask); +} + +void bpf_cpumask_copy(struct bpf_cpumask *dst, const struct cpumask *src) +{ + cpumask_copy((struct cpumask *)dst, src); +} + +u32 bpf_cpumask_any(const struct cpumask *cpumask) +{ + return cpumask_any(cpumask); +} + +u32 bpf_cpumask_any_and(const struct cpumask *src1, const struct cpumask *src2) +{ + return cpumask_any_and(src1, src2); +} + +BTF_SET8_START(cpumask_kfunc_btf_ids) +BTF_ID_FLAGS(func, bpf_cpumask_create, KF_ACQUIRE | KF_RET_NULL) +BTF_ID_FLAGS(func, bpf_cpumask_release, KF_RELEASE | KF_TRUSTED_ARGS) +BTF_ID_FLAGS(func, bpf_cpumask_acquire, KF_ACQUIRE | KF_TRUSTED_ARGS) +BTF_ID_FLAGS(func, bpf_cpumask_kptr_get, KF_ACQUIRE | KF_KPTR_GET | KF_RET_NULL) +BTF_ID_FLAGS(func, bpf_cpumask_first, KF_TRUSTED_ARGS) +BTF_ID_FLAGS(func, bpf_cpumask_first_zero, KF_TRUSTED_ARGS) +BTF_ID_FLAGS(func, bpf_cpumask_set_cpu, KF_TRUSTED_ARGS) +BTF_ID_FLAGS(func, bpf_cpumask_clear_cpu, KF_TRUSTED_ARGS) +BTF_ID_FLAGS(func, bpf_cpumask_test_cpu, KF_TRUSTED_ARGS) +BTF_ID_FLAGS(func, bpf_cpumask_test_and_set_cpu, KF_TRUSTED_ARGS) +BTF_ID_FLAGS(func, bpf_cpumask_test_and_clear_cpu, KF_TRUSTED_ARGS) +BTF_ID_FLAGS(func, bpf_cpumask_setall, KF_TRUSTED_ARGS) +BTF_ID_FLAGS(func, bpf_cpumask_clear, KF_TRUSTED_ARGS) +BTF_ID_FLAGS(func, bpf_cpumask_and, KF_TRUSTED_ARGS) +BTF_ID_FLAGS(func, bpf_cpumask_or, KF_TRUSTED_ARGS) +BTF_ID_FLAGS(func, bpf_cpumask_xor, KF_TRUSTED_ARGS) +BTF_ID_FLAGS(func, bpf_cpumask_equal, KF_TRUSTED_ARGS) +BTF_ID_FLAGS(func, bpf_cpumask_intersects, KF_TRUSTED_ARGS) +BTF_ID_FLAGS(func, bpf_cpumask_subset, KF_TRUSTED_ARGS) +BTF_ID_FLAGS(func, bpf_cpumask_empty, KF_TRUSTED_ARGS) +BTF_ID_FLAGS(func, bpf_cpumask_full, KF_TRUSTED_ARGS) +BTF_ID_FLAGS(func, bpf_cpumask_copy, KF_TRUSTED_ARGS) +BTF_ID_FLAGS(func, bpf_cpumask_any, KF_TRUSTED_ARGS) +BTF_ID_FLAGS(func, bpf_cpumask_any_and, KF_TRUSTED_ARGS) +BTF_SET8_END(cpumask_kfunc_btf_ids) + +static const struct btf_kfunc_id_set cpumask_kfunc_set = { + .owner = THIS_MODULE, + .set = &cpumask_kfunc_btf_ids, +}; + +BTF_ID_LIST(cpumask_dtor_ids) +BTF_ID(struct, bpf_cpumask) +BTF_ID(func, bpf_cpumask_release) + +static int __init cpumask_kfunc_init(void) +{ + int ret; + const struct btf_id_dtor_kfunc cpumask_dtors[] = { + { + .btf_id = cpumask_dtor_ids[0], + .kfunc_btf_id = cpumask_dtor_ids[1] + }, + }; + + ret = bpf_mem_alloc_init(&bpf_cpumask_ma, 0, false); + ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_TRACING, &cpumask_kfunc_set); + ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_STRUCT_OPS, &cpumask_kfunc_set); + return ret ?: register_btf_id_dtor_kfuncs(cpumask_dtors, + ARRAY_SIZE(cpumask_dtors), + THIS_MODULE); +} + +late_initcall(cpumask_kfunc_init);
Certain programs may wish to be able to query cpumasks. For example, if a program that is tracing percpu operations may wish to track which tasks end up running on which CPUs, and it could be useful to associate that with the tasks' cpumasks. Similarly, a program tracking NUMA allocations, CPU scheduling domains, etc, would potentially benefit from being able to see which CPUs a task could be migrated to, etc. This patch enables these such cases by introducing a series of bpf_cpumask_* kfuncs. Amongst these kfuncs, there are two separate "classes" of operations: 1. kfuncs which allow the caller to allocate and mutate their own cpumasks in the form of a struct bpf_cpumask * object. Such kfuncs include e.g. bpf_cpumask_create() to allocate the cpumask, and bpf_cpumask_or() to mutate it. "Regular" cpumasks such as p->cpus_ptr may not be passed to these kfuncs, and the verifier will ensure this is the case by comparing BTF IDs. 2. Read-only operations which operate on const struct cpumask * arguments. For example, bpf_cpumask_test_cpu(), which tests whether a CPU is set in the cpumask. Any trusted struct cpumask * or struct bpf_cpumask * may be passed to these kfuncs. The verifier allows struct bpf_cpumask * even though the kfunc is defined with struct cpumask * because the first element of a struct bpf_cpumask is a cpumask_t, so it is safe to cast. A follow-on patch will add selftests which validate these kfuncs, and another will document them. Note that some of the kfuncs that were added would benefit from additional verification logic. For example, any kfunc taking a CPU argument that exceeds the number of CPUs on the system, etc. For now, we silently check for and ignore these cases at runtime. When we have e.g. per-argument kfunc flags, it might be helpful to add another KF_CPU-type flag that specifies that the verifier should validate that it's a valid CPU. Signed-off-by: David Vernet <void@manifault.com> --- kernel/bpf/Makefile | 1 + kernel/bpf/cpumask.c | 263 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 264 insertions(+) create mode 100644 kernel/bpf/cpumask.c