• Martin KaFai Lau's avatar
    bpf: Use bpf_mem_cache_alloc/free in bpf_local_storage_elem · 08a7ce38
    Martin KaFai Lau authored
    This patch uses bpf_mem_alloc for the task and cgroup local storage that
    the bpf prog can easily get a hold of the storage owner's PTR_TO_BTF_ID.
    eg. bpf_get_current_task_btf() can be used in some of the kmalloc code
    path which will cause deadlock/recursion. bpf_mem_cache_alloc is
    deadlock free and will solve a legit use case in [1].
    
    For sk storage, its batch creation benchmark shows a few percent
    regression when the sk create/destroy batch size is larger than 32.
    The sk creation/destruction happens much more often and
    depends on external traffic. Considering it is hypothetical
    to be able to cause deadlock with sk storage, it can cross
    the bridge to use bpf_mem_alloc till a legit (ie. useful)
    use case comes up.
    
    For inode storage, bpf_local_storage_destroy() is called before
    waiting for a rcu gp and its memory cannot be reused immediately.
    inode stays with kmalloc/kfree after the rcu [or tasks_trace] gp.
    
    A 'bool bpf_ma' argument is added to bpf_local_storage_map_alloc().
    Only task and cgroup storage have 'bpf_ma == true' which
    means to use bpf_mem_cache_alloc/free(). This patch only changes
    selem to use bpf_mem_alloc for task and cgroup. The next patch
    will change the local_storage to use bpf_mem_alloc also for
    task and cgroup.
    
    Here is some more details on the changes:
    
    * memory allocation:
    After bpf_mem_cache_alloc(), the SDATA(selem)->data is zero-ed because
    bpf_mem_cache_alloc() could return a reused selem. It is to keep
    the existing bpf_map_kzalloc() behavior. Only SDATA(selem)->data
    is zero-ed. SDATA(selem)->data is the visible part to the bpf prog.
    No need to use zero_map_value() to do the zeroing because
    bpf_selem_free(..., reuse_now = true) ensures no bpf prog is using
    the selem before returning the selem through bpf_mem_cache_free().
    For the internal fields of selem, they will be initialized when
    linking to the new smap and the new local_storage.
    
    When 'bpf_ma == false', nothing changes in this patch. It will
    stay with the bpf_map_kzalloc().
    
    * memory free:
    The bpf_selem_free() and bpf_selem_free_rcu() are modified to handle
    the bpf_ma == true case.
    
    For the common selem free path where its owner is also being destroyed,
    the mem is freed in bpf_local_storage_destroy(), the owner (task
    and cgroup) has gone through a rcu gp. The memory can be reused
    immediately, so bpf_local_storage_destroy() will call
    bpf_selem_free(..., reuse_now = true) which will do
    bpf_mem_cache_free() for immediate reuse consideration.
    
    An exception is the delete elem code path. The delete elem code path
    is called from the helper bpf_*_storage_delete() and the syscall
    bpf_map_delete_elem(). This path is an unusual case for local
    storage because the common use case is to have the local storage
    staying with its owner life time so that the bpf prog and the user
    space does not have to monitor the owner's destruction. For the delete
    elem path, the selem cannot be reused immediately because there could
    be bpf prog using it. It will call bpf_selem_free(..., reuse_now = false)
    and it will wait for a rcu tasks trace gp before freeing the elem. The
    rcu callback is changed to do bpf_mem_cache_raw_free() instead of kfree().
    
    When 'bpf_ma == false', it should be the same as before.
    __bpf_selem_free() is added to do the kfree_rcu and call_tasks_trace_rcu().
    A few words on the 'reuse_now == true'. When 'reuse_now == true',
    it is still racing with bpf_local_storage_map_free which is under rcu
    protection, so it still needs to wait for a rcu gp instead of kfree().
    Otherwise, the selem may be reused by slab for a totally different struct
    while the bpf_local_storage_map_free() is still using it (as a
    rcu reader). For the inode case, there may be other rcu readers also.
    In short, when bpf_ma == false and reuse_now == true => vanilla rcu.
    
    [1]: https://lore.kernel.org/bpf/20221118190109.1512674-1-namhyung@kernel.org/
    
    Cc: Namhyung Kim <namhyung@kernel.org>
    Signed-off-by: default avatarMartin KaFai Lau <martin.lau@kernel.org>
    Link: https://lore.kernel.org/r/20230322215246.1675516-3-martin.lau@linux.devSigned-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
    08a7ce38
bpf_task_storage.c 9.24 KB