if (charge_mem && mem_charge(smap, owner, smap->elem_size)) return NULL;
if (smap->bpf_ma) {
selem = bpf_mem_cache_alloc_flags(&smap->selem_ma, gfp_flags); if (selem) /* Keep the original bpf_map_kzalloc behavior *beforestartedusingthebpf_mem_cache_alloc. * *Noneedtousezero_map_value.Thebpf_selem_free() *onlydoesbpf_mem_cache_freewhenthereis *nootherbpfprogisusingtheselem.
*/
memset(SDATA(selem)->data, 0, smap->map.value_size);
} else {
selem = bpf_map_kzalloc(&smap->map, smap->elem_size,
gfp_flags | __GFP_NOWARN);
}
if (selem) { if (value) { /* No need to call check_and_init_map_value as memory is zero init */
copy_map_value(&smap->map, SDATA(selem)->data, value); if (swap_uptrs)
bpf_obj_swap_uptrs(smap->map.record, SDATA(selem)->data, value);
} return selem;
}
if (charge_mem)
mem_uncharge(smap, owner, smap->elem_size);
if (!bpf_ma) {
__bpf_local_storage_free(local_storage, reuse_now); return;
}
if (!reuse_now) {
call_rcu_tasks_trace(&local_storage->rcu,
bpf_local_storage_free_trace_rcu); return;
}
if (smap)
bpf_mem_cache_free(&smap->storage_ma, local_storage); else /* smap could be NULL if the selem that triggered *this'local_storage'creationhadbeenlonggone. *Inthiscase,directlydocall_rcu().
*/
call_rcu(&local_storage->rcu, bpf_local_storage_free_rcu);
}
void bpf_selem_free(struct bpf_local_storage_elem *selem, struct bpf_local_storage_map *smap, bool reuse_now)
{ if (!smap->bpf_ma) { /* Only task storage has uptrs and task storage *hasmovedtobpf_mem_alloc.Meaningsmap->bpf_ma==true *fortaskstorage,sothisbpf_obj_free_fields()won'tunpin *anyuptr.
*/
bpf_obj_free_fields(smap->map.record, SDATA(selem)->data);
__bpf_selem_free(selem, reuse_now); return;
}
if (reuse_now) { /* reuse_now == true only happens when the storage owner *(e.g.task_struct)isbeingdestructedorthemapitself *isbeingdestructed(iemap_free).Inbothcases, *nobpfprogcanhaveaholdontheselem.Itis *safetounpintheuptrsandfreetheselemnow.
*/
bpf_obj_free_fields(smap->map.record, SDATA(selem)->data); /* Instead of using the vanilla call_rcu(), *bpf_mem_cache_freewillbeabletoreuseselem *immediately.
*/
bpf_mem_cache_free(&smap->selem_ma, selem); return;
}
/* All uncharging on the owner must be done first. *Theownermaybefreedoncethelastselemisunlinked *fromlocal_storage.
*/ if (uncharge_mem)
mem_uncharge(smap, owner, smap->elem_size);
/* local_storage->smap may be NULL. If it is, get the bpf_ma *fromanyseleminthelocal_storage->list.Thebpf_maofall *local_storageandselemshouldhavethesamevalue *forthesamemaptype. * *Ifthelocal_storage->listisalreadyempty,thecallerwillnot *careaboutthebpf_mavaluealsobecausethecallerisnot *responsibletofreethelocal_storage.
*/
if (storage_smap) return storage_smap->bpf_ma;
if (!selem) { struct hlist_node *n;
n = rcu_dereference_check(hlist_first_rcu(&local_storage->list),
bpf_rcu_lock_held()); if (!n) returnfalse;
/* spinlock is needed to avoid racing with the *paralleldelete.Otherwise,publishinganalready *deletedsdatatothecachewillbecomeause-after-free *probleminthenextbpf_local_storage_lookup().
*/
raw_spin_lock_irqsave(&local_storage->lock, flags); if (selem_linked_to_storage(selem))
rcu_assign_pointer(local_storage->cache[smap->cache_idx], SDATA(selem));
raw_spin_unlock_irqrestore(&local_storage->lock, flags);
}
/* Note that even first_selem was linked to smap's *bucket->list,first_selemcanbefreedimmediately *(insteadofkfree_rcu)because *bpf_local_storage_map_free()doesa *synchronize_rcu_mult(waitingforbothsleepableand *normalprograms)beforewalkingthebucket->list. *Hence,nooneisaccessingselemfromthe *bucket->listunderrcu_read_lock().
*/
}
/* sk cannot be going away because it is linking new elem *tosk->sk_bpf_storage.(i.e.sk->sk_refcntcannotbe0). *Otherwise,itwillbecomealeak(andothermemoryissues *duringmapdestruction).
*/ struct bpf_local_storage_data *
bpf_local_storage_update(void *owner, struct bpf_local_storage_map *smap, void *value, u64 map_flags, bool swap_uptrs, gfp_t gfp_flags)
{ struct bpf_local_storage_data *old_sdata = NULL; struct bpf_local_storage_elem *alloc_selem, *selem = NULL; struct bpf_local_storage *local_storage;
HLIST_HEAD(old_selem_free_list); unsignedlong flags; int err;
/* BPF_EXIST and BPF_NOEXIST cannot be both set */ if (unlikely((map_flags & ~BPF_F_LOCK) > BPF_EXIST) || /* BPF_F_LOCK can only be used in a value with spin_lock */
unlikely((map_flags & BPF_F_LOCK) &&
!btf_record_has_field(smap->map.record, BPF_SPIN_LOCK))) return ERR_PTR(-EINVAL);
local_storage = rcu_dereference_check(*owner_storage(smap, owner),
bpf_rcu_lock_held()); if (!local_storage || hlist_empty(&local_storage->list)) { /* Very first elem for the owner */
err = check_flags(NULL, map_flags); if (err) return ERR_PTR(err);
if ((map_flags & BPF_F_LOCK) && !(map_flags & BPF_NOEXIST)) { /* Hoping to find an old_sdata to do inline update *suchthatitcanavoidtakingthelocal_storage->lock *andchangingthelists.
*/
old_sdata =
bpf_local_storage_lookup(local_storage, smap, false);
err = check_flags(old_sdata, map_flags); if (err) return ERR_PTR(err); if (old_sdata && selem_linked_to_storage_lockless(SELEM(old_sdata))) {
copy_map_value_locked(&smap->map, old_sdata->data,
value, false); return old_sdata;
}
}
/* A lookup has just been done before and concluded a new selem is *needed.Thechanceofanunnecessaryallocisunlikely.
*/
alloc_selem = selem = bpf_selem_alloc(smap, owner, value, true, swap_uptrs, gfp_flags); if (!alloc_selem) return ERR_PTR(-ENOMEM);
/* Recheck local_storage->list under local_storage->lock */ if (unlikely(hlist_empty(&local_storage->list))) { /* A parallel del is happening and local_storage is going *away.Ithasjustbeencheckedbefore,sovery *unlikely.Returninsteadofretrytokeepthings *simple.
*/
err = -EAGAIN; goto unlock;
}
/* Neither the bpf_prog nor the bpf_map's syscall *couldbemodifyingthelocal_storage->listnow. *Thus,noelemcanbeaddedtoordeletedfromthe *local_storage->listbythebpf_progorbythebpf_map'ssyscall. * *Itisracingwithbpf_local_storage_map_free()alone *whenunlinkingelemfromthelocal_storage->listand *themap'sbucket->list.
*/
raw_spin_lock_irqsave(&local_storage->lock, flags);
hlist_for_each_entry_safe(selem, n, &local_storage->list, snode) { /* Always unlink from map before unlinking from *local_storage.
*/
bpf_selem_unlink_map(selem); /* If local_storage list has only one element, the *bpf_selem_unlink_storage_nolock()willreturntrue. *Otherwise,itwillreturnfalse.Thecurrentloopiteration *intendstoremovealllocalstorage.Sothelastiteration *oftheloopwillsetthefree_cgroup_storagetotrue.
*/
free_storage = bpf_selem_unlink_storage_nolock(
local_storage, selem, true, &free_selem_list);
}
raw_spin_unlock_irqrestore(&local_storage->lock, flags);
bpf_selem_free_list(&free_selem_list, true);
if (free_storage)
bpf_local_storage_free(local_storage, storage_smap, bpf_ma, true);
}
/* The dynamically callocated selems are not counted currently. */
usage += sizeof(*smap->buckets) * (1ULL << smap->bucket_log); return usage;
}
/* When bpf_ma == true, the bpf_mem_alloc is used to allocate and free memory. *Adeadlockfreeallocatorisusefulforstoragethatthebpfprogcaneasily *getaholdoftheownerPTR_TO_BTF_IDinanycontext.eg.bpf_get_current_task_btf. *Thetaskandcgroupstoragefallintothiscase.Thebpf_mem_allocreuses *memoryimmediately.Tobereuse-immediatesafe,theownerdestruction *codepathneedstogothrougharcugraceperiodbeforecalling *bpf_local_storage_destroy(). * *Whenbpf_ma==false,thekmallocandkfreeareused.
*/ struct bpf_map *
bpf_local_storage_map_alloc(union bpf_attr *attr, struct bpf_local_storage_cache *cache, bool bpf_ma)
{ struct bpf_local_storage_map *smap; unsignedint i;
u32 nbuckets; int err;
smap = bpf_map_area_alloc(sizeof(*smap), NUMA_NO_NODE); if (!smap) return ERR_PTR(-ENOMEM);
bpf_map_init_from_attr(&smap->map, attr);
nbuckets = roundup_pow_of_two(num_possible_cpus()); /* Use at least 2 buckets, select_bucket() is undefined behavior with 1 bucket */
nbuckets = max_t(u32, 2, nbuckets);
smap->bucket_log = ilog2(nbuckets);
/* In PREEMPT_RT, kmalloc(GFP_ATOMIC) is still not safe in non *preemptiblecontext.Thus,enforceallstoragestouse *bpf_mem_allocwhenCONFIG_PREEMPT_RTisenabled.
*/
smap->bpf_ma = IS_ENABLED(CONFIG_PREEMPT_RT) ? true : bpf_ma; if (smap->bpf_ma) {
err = bpf_mem_alloc_init(&smap->selem_ma, smap->elem_size, false); if (err) goto free_smap;
/* Note that this map might be concurrently cloned from *bpf_sk_storage_clone.Waitforanyexistingbpf_sk_storage_clone *RCUreadsectiontofinishbeforeproceeding.NewRCU *readsectionsshouldbepreventedviabpf_map_inc_not_zero.
*/
synchronize_rcu();
/* bpf prog and the userspace can no longer access this map *now.Nonewselem(ofthismap)canbeadded *totheowner->storageortothemapbucket'slist. * *Theelemofthismapcanbecleaneduphere *orwhenthestorageisfreede.g. *bybpf_sk_storage_free()during__sk_destruct().
*/ for (i = 0; i < (1U << smap->bucket_log); i++) {
b = &smap->buckets[i];
rcu_read_lock(); /* No one is adding to b->list now */ while ((selem = hlist_entry_safe(
rcu_dereference_raw(hlist_first_rcu(&b->list)), struct bpf_local_storage_elem, map_node))) { if (busy_counter)
this_cpu_inc(*busy_counter);
bpf_selem_unlink(selem, true); if (busy_counter)
this_cpu_dec(*busy_counter);
cond_resched_rcu();
}
rcu_read_unlock();
}
/* While freeing the storage we may still need to access the map. * *e.g.whenbpf_sk_storage_free()hasunlinkedselemfromthemap *whichthenmadetheabovewhile((selem=...))loop *exitimmediately. * *However,whilefreeingthestorageonestillneedstoaccessthe *smap->elem_sizetodotheunchargingin *bpf_selem_unlink_storage_nolock(). * *Hence,waitanotherrcugraceperiodforthestoragetobefreed.
*/
synchronize_rcu();
if (smap->bpf_ma) {
rcu_barrier_tasks_trace(); if (!rcu_trace_implies_rcu_gp())
rcu_barrier();
bpf_mem_alloc_destroy(&smap->selem_ma);
bpf_mem_alloc_destroy(&smap->storage_ma);
}
kvfree(smap->buckets);
bpf_map_area_free(smap);
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.16 Sekunden
(vorverarbeitet am 2026-09-27)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.