diff mbox series

[2/3] bpf: do not walk twice the hash map on free

Message ID 20240425-bpf-next-v1-2-1d8330e6c643@kernel.org (mailing list archive)
State Superseded
Delegated to: BPF
Headers show
Series bpf_wq followup series | expand

Checks

Context Check Description
netdev/tree_selection success Guessing tree name failed - patch did not apply
bpf/vmtest-bpf-next-PR success PR summary
bpf/vmtest-bpf-next-VM_Test-32 success Logs for x86_64-llvm-17 / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-7 success Logs for aarch64-gcc / test (test_progs, false, 360) / test_progs on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-8 success Logs for aarch64-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-23 success Logs for x86_64-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-14 success Logs for s390x-gcc / test (test_progs, false, 360) / test_progs on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-22 success Logs for x86_64-gcc / test (test_progs, false, 360) / test_progs on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-38 success Logs for x86_64-llvm-18 / test (test_progs, false, 360) / test_progs on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-31 success Logs for x86_64-llvm-17 / test (test_progs, false, 360) / test_progs on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-40 success Logs for x86_64-llvm-18 / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-39 success Logs for x86_64-llvm-18 / test (test_progs_cpuv4, false, 360) / test_progs_cpuv4 on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-0 success Logs for Lint
bpf/vmtest-bpf-next-VM_Test-1 success Logs for ShellCheck
bpf/vmtest-bpf-next-VM_Test-2 success Logs for Unittests
bpf/vmtest-bpf-next-VM_Test-3 success Logs for Validate matrix.py
bpf/vmtest-bpf-next-VM_Test-6 success Logs for aarch64-gcc / test (test_maps, false, 360) / test_maps on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-4 success Logs for aarch64-gcc / build / build for aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-5 success Logs for aarch64-gcc / build-release
bpf/vmtest-bpf-next-VM_Test-9 success Logs for aarch64-gcc / test (test_verifier, false, 360) / test_verifier on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-34 success Logs for x86_64-llvm-17 / veristat
bpf/vmtest-bpf-next-VM_Test-36 success Logs for x86_64-llvm-18 / build-release / build for x86_64 with llvm-18 and -O2 optimization
bpf/vmtest-bpf-next-VM_Test-37 success Logs for x86_64-llvm-18 / test (test_maps, false, 360) / test_maps on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-41 success Logs for x86_64-llvm-18 / test (test_verifier, false, 360) / test_verifier on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-42 success Logs for x86_64-llvm-18 / veristat
bpf/vmtest-bpf-next-VM_Test-33 success Logs for x86_64-llvm-17 / test (test_verifier, false, 360) / test_verifier on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-35 success Logs for x86_64-llvm-18 / build / build for x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-12 success Logs for s390x-gcc / build-release
bpf/vmtest-bpf-next-VM_Test-13 success Logs for s390x-gcc / test (test_maps, false, 360) / test_maps on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-16 success Logs for s390x-gcc / test (test_verifier, false, 360) / test_verifier on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-18 success Logs for set-matrix
bpf/vmtest-bpf-next-VM_Test-19 success Logs for x86_64-gcc / build / build for x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-11 success Logs for s390x-gcc / build / build for s390x with gcc
bpf/vmtest-bpf-next-VM_Test-24 success Logs for x86_64-gcc / test (test_progs_no_alu32_parallel, true, 30) / test_progs_no_alu32_parallel on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-15 success Logs for s390x-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-20 success Logs for x86_64-gcc / build-release
bpf/vmtest-bpf-next-VM_Test-27 success Logs for x86_64-gcc / veristat / veristat on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-28 success Logs for x86_64-llvm-17 / build / build for x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-29 success Logs for x86_64-llvm-17 / build-release / build for x86_64 with llvm-17 and -O2 optimization
bpf/vmtest-bpf-next-VM_Test-30 success Logs for x86_64-llvm-17 / test (test_maps, false, 360) / test_maps on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-10 success Logs for aarch64-gcc / veristat
bpf/vmtest-bpf-next-VM_Test-17 success Logs for s390x-gcc / veristat
bpf/vmtest-bpf-next-VM_Test-21 success Logs for x86_64-gcc / test (test_maps, false, 360) / test_maps on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-26 success Logs for x86_64-gcc / test (test_verifier, false, 360) / test_verifier on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-25 success Logs for x86_64-gcc / test (test_progs_parallel, true, 30) / test_progs_parallel on x86_64 with gcc

Commit Message

Benjamin Tissoires April 25, 2024, 1:59 p.m. UTC
If someone stores both a timer and a workqueue in a hash map, on free, we
would walk it twice.
Add a check in htab_free_malloced_timers_or_wq and free the timers
and workqueues if they are present.

Fixes: 246331e3f1ea ("bpf: allow struct bpf_wq to be embedded in arraymaps and hashmaps")
Signed-off-by: Benjamin Tissoires <bentiss@kernel.org>
---
 kernel/bpf/hashtab.c | 16 +++++-----------
 1 file changed, 5 insertions(+), 11 deletions(-)

Comments

Alexei Starovoitov April 25, 2024, 7:48 p.m. UTC | #1
On Thu, Apr 25, 2024 at 6:59 AM Benjamin Tissoires <bentiss@kernel.org> wrote:
>
> If someone stores both a timer and a workqueue in a hash map, on free, we
> would walk it twice.
> Add a check in htab_free_malloced_timers_or_wq and free the timers
> and workqueues if they are present.
>
> Fixes: 246331e3f1ea ("bpf: allow struct bpf_wq to be embedded in arraymaps and hashmaps")
> Signed-off-by: Benjamin Tissoires <bentiss@kernel.org>
> ---
>  kernel/bpf/hashtab.c | 16 +++++-----------
>  1 file changed, 5 insertions(+), 11 deletions(-)
>
> diff --git a/kernel/bpf/hashtab.c b/kernel/bpf/hashtab.c
> index 0179183c543a..20162ae741e9 100644
> --- a/kernel/bpf/hashtab.c
> +++ b/kernel/bpf/hashtab.c
> @@ -1515,7 +1515,7 @@ static void delete_all_elements(struct bpf_htab *htab)
>         migrate_enable();
>  }
>
> -static void htab_free_malloced_timers_or_wq(struct bpf_htab *htab, bool is_timer)
> +static void htab_free_malloced_timers_or_wq(struct bpf_htab *htab)
>  {
>         int i;
>
> @@ -1527,10 +1527,10 @@ static void htab_free_malloced_timers_or_wq(struct bpf_htab *htab, bool is_timer
>
>                 hlist_nulls_for_each_entry(l, n, head, hash_node) {
>                         /* We only free timer on uref dropping to zero */
> -                       if (is_timer)
> +                       if (btf_record_has_field(htab->map.record, BPF_TIMER))
>                                 bpf_obj_free_timer(htab->map.record,
>                                                    l->key + round_up(htab->map.key_size, 8));
> -                       else
> +                       if (btf_record_has_field(htab->map.record, BPF_WORKQUEUE))
>                                 bpf_obj_free_workqueue(htab->map.record,
>                                                        l->key + round_up(htab->map.key_size, 8));
>                 }
> @@ -1544,18 +1544,12 @@ static void htab_map_free_timers_and_wq(struct bpf_map *map)
>         struct bpf_htab *htab = container_of(map, struct bpf_htab, map);
>
>         /* We only free timer and workqueue on uref dropping to zero */
> -       if (btf_record_has_field(htab->map.record, BPF_TIMER)) {
> +       if (btf_record_has_field(htab->map.record, BPF_TIMER | BPF_WORKQUEUE)) {
>                 if (!htab_is_prealloc(htab))
> -                       htab_free_malloced_timers_or_wq(htab, true);
> +                       htab_free_malloced_timers_or_wq(htab);
>                 else
>                         htab_free_prealloced_timers(htab);
>         }
> -       if (btf_record_has_field(htab->map.record, BPF_WORKQUEUE)) {
> -               if (!htab_is_prealloc(htab))
> -                       htab_free_malloced_timers_or_wq(htab, false);
> -               else
> -                       htab_free_prealloced_wq(htab);

This looks wrong.
htab_free_prealloced_wq() is now unused as compiler says:
../kernel/bpf/hashtab.c:243:13: warning: ‘htab_free_prealloced_wq’
defined but not used [-Wunused-function]
  243 | static void htab_free_prealloced_wq(struct bpf_htab *htab)
      |             ^~~~~~~~~~~~~~~~~~~~~~~

and prealloced maps with wq leak wq-s.

pw-bot: cr
kernel test robot April 25, 2024, 11:09 p.m. UTC | #2
Hi Benjamin,

kernel test robot noticed the following build warnings:

[auto build test WARNING on 52578f7f53ff8fe3a8f6f3bc8b5956615c07a16e]

url:    https://github.com/intel-lab-lkp/linux/commits/Benjamin-Tissoires/bpf-do-not-walk-twice-the-map-on-free/20240425-220322
base:   52578f7f53ff8fe3a8f6f3bc8b5956615c07a16e
patch link:    https://lore.kernel.org/r/20240425-bpf-next-v1-2-1d8330e6c643%40kernel.org
patch subject: [PATCH 2/3] bpf: do not walk twice the hash map on free
config: arc-randconfig-002-20240426 (https://download.01.org/0day-ci/archive/20240426/202404260653.ULrCGrp2-lkp@intel.com/config)
compiler: arc-elf-gcc (GCC) 13.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20240426/202404260653.ULrCGrp2-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202404260653.ULrCGrp2-lkp@intel.com/

All warnings (new ones prefixed by >>):

>> kernel/bpf/hashtab.c:243:13: warning: 'htab_free_prealloced_wq' defined but not used [-Wunused-function]
     243 | static void htab_free_prealloced_wq(struct bpf_htab *htab)
         |             ^~~~~~~~~~~~~~~~~~~~~~~


vim +/htab_free_prealloced_wq +243 kernel/bpf/hashtab.c

68134668c17f31 Alexei Starovoitov 2021-07-14  242  
246331e3f1eac9 Benjamin Tissoires 2024-04-20 @243  static void htab_free_prealloced_wq(struct bpf_htab *htab)
246331e3f1eac9 Benjamin Tissoires 2024-04-20  244  {
246331e3f1eac9 Benjamin Tissoires 2024-04-20  245  	u32 num_entries = htab->map.max_entries;
246331e3f1eac9 Benjamin Tissoires 2024-04-20  246  	int i;
246331e3f1eac9 Benjamin Tissoires 2024-04-20  247  
246331e3f1eac9 Benjamin Tissoires 2024-04-20  248  	if (!btf_record_has_field(htab->map.record, BPF_WORKQUEUE))
246331e3f1eac9 Benjamin Tissoires 2024-04-20  249  		return;
246331e3f1eac9 Benjamin Tissoires 2024-04-20  250  	if (htab_has_extra_elems(htab))
246331e3f1eac9 Benjamin Tissoires 2024-04-20  251  		num_entries += num_possible_cpus();
246331e3f1eac9 Benjamin Tissoires 2024-04-20  252  
246331e3f1eac9 Benjamin Tissoires 2024-04-20  253  	for (i = 0; i < num_entries; i++) {
246331e3f1eac9 Benjamin Tissoires 2024-04-20  254  		struct htab_elem *elem;
246331e3f1eac9 Benjamin Tissoires 2024-04-20  255  
246331e3f1eac9 Benjamin Tissoires 2024-04-20  256  		elem = get_htab_elem(htab, i);
246331e3f1eac9 Benjamin Tissoires 2024-04-20  257  		bpf_obj_free_workqueue(htab->map.record,
246331e3f1eac9 Benjamin Tissoires 2024-04-20  258  				       elem->key + round_up(htab->map.key_size, 8));
246331e3f1eac9 Benjamin Tissoires 2024-04-20  259  		cond_resched();
246331e3f1eac9 Benjamin Tissoires 2024-04-20  260  	}
246331e3f1eac9 Benjamin Tissoires 2024-04-20  261  }
246331e3f1eac9 Benjamin Tissoires 2024-04-20  262
Benjamin Tissoires April 30, 2024, 9:20 a.m. UTC | #3
On Apr 25 2024, Alexei Starovoitov wrote:
> On Thu, Apr 25, 2024 at 6:59 AM Benjamin Tissoires <bentiss@kernel.org> wrote:
> >
> > If someone stores both a timer and a workqueue in a hash map, on free, we
> > would walk it twice.
> > Add a check in htab_free_malloced_timers_or_wq and free the timers
> > and workqueues if they are present.
> >
> > Fixes: 246331e3f1ea ("bpf: allow struct bpf_wq to be embedded in arraymaps and hashmaps")
> > Signed-off-by: Benjamin Tissoires <bentiss@kernel.org>
> > ---
> >  kernel/bpf/hashtab.c | 16 +++++-----------
> >  1 file changed, 5 insertions(+), 11 deletions(-)
> >
> > diff --git a/kernel/bpf/hashtab.c b/kernel/bpf/hashtab.c
> > index 0179183c543a..20162ae741e9 100644
> > --- a/kernel/bpf/hashtab.c
> > +++ b/kernel/bpf/hashtab.c
> > @@ -1515,7 +1515,7 @@ static void delete_all_elements(struct bpf_htab *htab)
> >         migrate_enable();
> >  }
> >
> > -static void htab_free_malloced_timers_or_wq(struct bpf_htab *htab, bool is_timer)
> > +static void htab_free_malloced_timers_or_wq(struct bpf_htab *htab)
> >  {
> >         int i;
> >
> > @@ -1527,10 +1527,10 @@ static void htab_free_malloced_timers_or_wq(struct bpf_htab *htab, bool is_timer
> >
> >                 hlist_nulls_for_each_entry(l, n, head, hash_node) {
> >                         /* We only free timer on uref dropping to zero */
> > -                       if (is_timer)
> > +                       if (btf_record_has_field(htab->map.record, BPF_TIMER))
> >                                 bpf_obj_free_timer(htab->map.record,
> >                                                    l->key + round_up(htab->map.key_size, 8));
> > -                       else
> > +                       if (btf_record_has_field(htab->map.record, BPF_WORKQUEUE))
> >                                 bpf_obj_free_workqueue(htab->map.record,
> >                                                        l->key + round_up(htab->map.key_size, 8));
> >                 }
> > @@ -1544,18 +1544,12 @@ static void htab_map_free_timers_and_wq(struct bpf_map *map)
> >         struct bpf_htab *htab = container_of(map, struct bpf_htab, map);
> >
> >         /* We only free timer and workqueue on uref dropping to zero */
> > -       if (btf_record_has_field(htab->map.record, BPF_TIMER)) {
> > +       if (btf_record_has_field(htab->map.record, BPF_TIMER | BPF_WORKQUEUE)) {
> >                 if (!htab_is_prealloc(htab))
> > -                       htab_free_malloced_timers_or_wq(htab, true);
> > +                       htab_free_malloced_timers_or_wq(htab);
> >                 else
> >                         htab_free_prealloced_timers(htab);
> >         }
> > -       if (btf_record_has_field(htab->map.record, BPF_WORKQUEUE)) {
> > -               if (!htab_is_prealloc(htab))
> > -                       htab_free_malloced_timers_or_wq(htab, false);
> > -               else
> > -                       htab_free_prealloced_wq(htab);
> 
> This looks wrong.
> htab_free_prealloced_wq() is now unused as compiler says:
> ../kernel/bpf/hashtab.c:243:13: warning: ‘htab_free_prealloced_wq’
> defined but not used [-Wunused-function]
>   243 | static void htab_free_prealloced_wq(struct bpf_htab *htab)
>       |             ^~~~~~~~~~~~~~~~~~~~~~~
> 
> and prealloced maps with wq leak wq-s.

oops, you are right. Sending a v2 right away (sorry for the delay).

Cheers,
Benjamin
diff mbox series

Patch

diff --git a/kernel/bpf/hashtab.c b/kernel/bpf/hashtab.c
index 0179183c543a..20162ae741e9 100644
--- a/kernel/bpf/hashtab.c
+++ b/kernel/bpf/hashtab.c
@@ -1515,7 +1515,7 @@  static void delete_all_elements(struct bpf_htab *htab)
 	migrate_enable();
 }
 
-static void htab_free_malloced_timers_or_wq(struct bpf_htab *htab, bool is_timer)
+static void htab_free_malloced_timers_or_wq(struct bpf_htab *htab)
 {
 	int i;
 
@@ -1527,10 +1527,10 @@  static void htab_free_malloced_timers_or_wq(struct bpf_htab *htab, bool is_timer
 
 		hlist_nulls_for_each_entry(l, n, head, hash_node) {
 			/* We only free timer on uref dropping to zero */
-			if (is_timer)
+			if (btf_record_has_field(htab->map.record, BPF_TIMER))
 				bpf_obj_free_timer(htab->map.record,
 						   l->key + round_up(htab->map.key_size, 8));
-			else
+			if (btf_record_has_field(htab->map.record, BPF_WORKQUEUE))
 				bpf_obj_free_workqueue(htab->map.record,
 						       l->key + round_up(htab->map.key_size, 8));
 		}
@@ -1544,18 +1544,12 @@  static void htab_map_free_timers_and_wq(struct bpf_map *map)
 	struct bpf_htab *htab = container_of(map, struct bpf_htab, map);
 
 	/* We only free timer and workqueue on uref dropping to zero */
-	if (btf_record_has_field(htab->map.record, BPF_TIMER)) {
+	if (btf_record_has_field(htab->map.record, BPF_TIMER | BPF_WORKQUEUE)) {
 		if (!htab_is_prealloc(htab))
-			htab_free_malloced_timers_or_wq(htab, true);
+			htab_free_malloced_timers_or_wq(htab);
 		else
 			htab_free_prealloced_timers(htab);
 	}
-	if (btf_record_has_field(htab->map.record, BPF_WORKQUEUE)) {
-		if (!htab_is_prealloc(htab))
-			htab_free_malloced_timers_or_wq(htab, false);
-		else
-			htab_free_prealloced_wq(htab);
-	}
 }
 
 /* Called when map->refcnt goes to zero, either from workqueue or from syscall */