Re: [PATCH bpf-next v3 15/16] selftests/bpf: Add test cases for hash map with dynptr key

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Wed, Apr 9, 2025 at 6:09 PM Hou Tao <houtao@xxxxxxxxxxxxxxx> wrote:
>
> Hi,
>
> On 4/10/2025 7:40 AM, Andrii Nakryiko wrote:
> > On Mon, Apr 7, 2025 at 7:24 PM Hou Tao <houtao@xxxxxxxxxxxxxxx> wrote:
> >> Hi,
> >>
> >> On 4/8/2025 12:05 AM, Andrii Nakryiko wrote:
> >>> On Sun, Apr 6, 2025 at 7:47 PM Hou Tao <houtao@xxxxxxxxxxxxxxx> wrote:
> >>>> Hi,
> >>>>
> >>>> On 4/5/2025 1:58 AM, Andrii Nakryiko wrote:
> >>>>> On Thu, Mar 27, 2025 at 1:23 AM Hou Tao <houtao@xxxxxxxxxxxxxxx> wrote:
> >>>>>> From: Hou Tao <houtao1@xxxxxxxxxx>
> >>>>>>
> >>>>>> Add three positive test cases to test the basic operations on the
> >>>>>> dynptr-keyed hash map. The basic operations include lookup, update,
> >>>>>> delete and get_next_key. These operations are exercised both through
> >>>>>> bpf syscall and bpf program. These three test cases use different map
> >>>>>> keys. The first test case uses both bpf_dynptr and a struct with only
> >>>>>> bpf_dynptr as map key, the second one uses a struct with an integer and
> >>>>>> a bpf_dynptr as map key, and the last one use a struct with two
> >>>>>> bpf_dynptr as map key: one in the struct itself and another is nested in
> >>>>>> another struct.
> >>>>>>
> >>>>>> Also add multiple negative test cases for dynptr-keyed hash map. These
> >>>>>> test cases mainly check whether the layout of dynptr and non-dynptr in
> >>>>>> the stack is matched with the definition of map->key_record.
> >>>>>>
> >>>>>> Signed-off-by: Hou Tao <houtao1@xxxxxxxxxx>
> >>>>>> ---
> >>>>>>  .../bpf/prog_tests/htab_dynkey_test.c         | 446 ++++++++++++++++++
> >>>>>>  .../bpf/progs/htab_dynkey_test_failure.c      | 266 +++++++++++
> >>>>>>  .../bpf/progs/htab_dynkey_test_success.c      | 382 +++++++++++++++
> >>>>>>  3 files changed, 1094 insertions(+)
> >>>>>>  create mode 100644 tools/testing/selftests/bpf/prog_tests/htab_dynkey_test.c
> >>>>>>  create mode 100644 tools/testing/selftests/bpf/progs/htab_dynkey_test_failure.c
> >>>>>>  create mode 100644 tools/testing/selftests/bpf/progs/htab_dynkey_test_success.c
> >>>>>>

[...]

> >>>>>> +       /* Lookup it again */
> >>>>>> +       value = bpf_map_lookup_elem(htab, &key);
> >>>>>> +       bpf_ringbuf_discard_dynptr(&key.f_3.name, 0);
> >>>>>> +       if (value) {
> >>>>>> +               err = 12;
> >>>>>> +               goto out;
> >>>>>> +       }
> >>>>>> +out:
> >>>>>> +       return err;
> >>>>>> +}
> >>>>> So, I'm not a big fan of this approach of literally embedding struct
> >>>>> bpf_dynptr into map key type and actually initializing and working
> >>>>> with it directly, like you do here with
> >>>>> bpf_ringbuf_reserve_dynptr(..., &key.f_3.name).
> >>>>>
> >>>>> Here's why. This approach only works for *map keys* (not map values)
> >>>>> and only when **the copy of the key** is on the stack (i.e., for map
> >>>>> lookups/updates/deletes). This approach won't work for having dynptrs
> >>>>> inside map value (for variable sized map values), nor does it really
> >>>>> work when you get a direct pointer to map key in
> >>>>> bpf_for_each_map_elem().
> >>>> Yes. The reason why the key should be on the stack is due to the
> >>>> limitation (or the design) of bpf_dynptr. However I didn't understand
> >>>> why it doesn't work for map value just like other special field in the
> >>>> map value (e.g., bpf_timer) ?
> >>> bpf_timer and other special things that go into map_value have to
> >>> painfully and carefully handle simultaneous access and modification of
> >>> map value. So they either do locking (and thus are not compatible or
> >>> reliable under NMI), or would need to be implemented locklessly.
> >>>
> >>> Dynptr is by design assumed to not be dealing with concurrent
> >>> modifications, so bpf_dynptr_adjust(), for instance, can just update
> >>> offset in place without any locking. Reliably and quickly.
> >> Thanks for the explanation here and below. I got it now: multiple bpf
> >> program could get the same map value through lookup and modify it
> >> concurrently through helpers or kfuncs. A bit of slow for me to figure
> >> out by myself. However, I think there is a big difference between
> >> bpf_dynptr and bpf_timer or other special fields. For bpf_timer, we
> >> could not update it through bpf_map_update_elem, so extra helpers or
> >> kfuncs are needed. However, for bpf_dynptr in map key/value, it could be
> >> updated through bpf_map_{update|delete}_elem(). Therefore, for dynptr in
> >> map key or map value, does it really need to allow update through
> >> non-map-update helpers and kfuncs ? Will it be enough to make the dynptr
> >> in map key/value be read-only ? If the dynptr in map key could be
> >> modified by bpf_dyptr_adjust(),  the lookup procedure may fail to find
> >> the target map element.
> > So you are saying that we would allow to update dynptr using
> > bpf_map_update_elem() (from BPF side), but allow to use bpf_dynptr
> > read-only APIs directly on dynptr inside the map_value, is that right?
>
> Yes. In your original stashed dynptr proposal, updating the dynptr part
> in the map key is done through bpf_map_update_elem(). For map value, do
> you think it is OK that we disable the update of dynptr in map value
> through bpf_map_update_elem() and only allow the update through extra
> kfuncs just like the existing special field in map value ? It will make
> a difference between the update of map key and map value. Will try to
> check whether is feasible to support the update of dynptr in map value
> through both bpf_map_update_elem() and extra kfunc.

I don't really see a problem supporting both. Under the cover it will
be the same logic, shared internal helper function, probably.

But if for whatever reason that's impossible, we can restrict setting
a new bpf_dynptr through bpf_map_update_elem(), yes.

[...]

> >
> >> 2) need to support concurrent update through non-bpf_map_update_elem helpers
> >> However, if the dynptr in map key and value is read-only, there will be
> >> no concurrent update. The update could be done through
> >> bpf_map_update_elem helper.
> >>
> >> 3) need to support in-place update through bpf_map_update_elem helper
> >> (e.g., for per-CPU map)
> >> However, if we need to support dynptr in map value, maybe we should
> >> change the in-place update to out-of-place update.
> >>
> >> Hope I didn't miss any point.
> > So, basically, if we unnecessarily restrict usability of dynptr, we
> > might carve out some way to work with dynptrs inside map key (but not
> > really map value, as I explained).
> >
> > On the other hand, we can have "stashed dynptr" and make it work the
> > same way in all situations.
> >
> > Tough choice, isn't it? ;)
>
> I see. Thanks for all these explanations. Will switch to the "stashed
> dynptr" in v4.

great, let's give it a try and see if we missed any gotchas, thanks!

> >
> >>>>> struct id_dname_key {
> >>>>>        int id;
> >>>>>        struct bpf_stashed_dynptr name;
> >>>>> };
> >>>>>
> >>> [...]
> >>>

[...]





[Index of Archives]     [Linux Samsung SoC]     [Linux Rockchip SoC]     [Linux Actions SoC]     [Linux for Synopsys ARC Processors]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]


  Powered by Linux