diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c index afb414b26d01d..312abb6644c54 100644 --- a/kernel/bpf/cgroup.c +++ b/kernel/bpf/cgroup.c @@ -720,6 +720,57 @@ static struct bpf_prog_list *find_detach_entry(struct list_head *progs, return ERR_PTR(-ENOENT); } +/** + * purge_effective_progs() - After compute_effective_progs fails to alloc new + * cgrp->bpf.inactive table we can recover by + * recomputing the array in place. + * + * @cgrp: The cgroup which descendants to traverse + * @link: A link to detach + * @atype: Type of detach operation + */ +static void purge_effective_progs(struct cgroup *cgrp, struct bpf_prog *prog, + enum cgroup_bpf_attach_type atype) +{ + struct cgroup_subsys_state *css; + struct bpf_prog_array_item *item; + struct bpf_prog *tmp; + struct bpf_prog_array *array; + int index = 0, index_purge = -1; + + if (!prog) + return; + + /* recompute effective prog array in place */ + css_for_each_descendant_pre(css, &cgrp->self) { + struct cgroup *desc = container_of(css, struct cgroup, self); + + array = desc->bpf.effective[atype]; + item = &array->items[0]; + + /* Find the index of the prog to purge */ + while ((tmp = READ_ONCE(item->prog))) { + if (tmp == prog) { + index_purge = index; + break; + } + item++; + index++; + } + + /* Check if we found what's needed for removing the prog */ + if (index_purge == -1 || index_purge == index - 1) + continue; + + /* Remove the program from the array */ + WARN_ONCE(bpf_prog_array_delete_safe_at(array, index_purge), + "Failed to purge a prog from array at index %d", index_purge); + + index = 0; + index_purge = -1; + } +} + /** * __cgroup_bpf_detach() - Detach the program or link from a cgroup, and * propagate the change to descendants @@ -762,8 +813,11 @@ static int __cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog, pl->link = NULL; err = update_effective_progs(cgrp, atype); - if (err) - goto cleanup; + if (err) { + struct bpf_prog *prog_purge = prog ? prog : link->link.prog; + + purge_effective_progs(cgrp, prog_purge, atype); + } /* now can actually delete it from this cgroup list */ list_del(&pl->node); @@ -775,12 +829,6 @@ static int __cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog, bpf_prog_put(old_prog); static_branch_dec(&cgroup_bpf_enabled_key[atype]); return 0; - -cleanup: - /* restore back prog or link */ - pl->prog = old_prog; - pl->link = link; - return err; } static int cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog,