1999 lines · 50.5 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* * Functions to manage eBPF programs attached to cgroups * * Copyright (c) 2016 Daniel Mack */ #include <linux/kernel.h>#include <linux/atomic.h>#include <linux/cgroup.h>#include <linux/filter.h>#include <linux/slab.h>#include <linux/sysctl.h>#include <linux/string.h>#include <linux/bpf.h>#include <linux/bpf-cgroup.h>#include <net/sock.h>#include <net/bpf_sk_storage.h> #include "../cgroup/cgroup-internal.h" DEFINE_STATIC_KEY_ARRAY_FALSE(cgroup_bpf_enabled_key, MAX_BPF_ATTACH_TYPE);EXPORT_SYMBOL(cgroup_bpf_enabled_key); void cgroup_bpf_offline(struct cgroup *cgrp){	cgroup_get(cgrp);	percpu_ref_kill(&cgrp->bpf.refcnt);} static void bpf_cgroup_storages_free(struct bpf_cgroup_storage *storages[]){	enum bpf_cgroup_storage_type stype; 	for_each_cgroup_storage_type(stype)		bpf_cgroup_storage_free(storages[stype]);} static int bpf_cgroup_storages_alloc(struct bpf_cgroup_storage *storages[],				     struct bpf_cgroup_storage *new_storages[],				     enum bpf_attach_type type,				     struct bpf_prog *prog,				     struct cgroup *cgrp){	enum bpf_cgroup_storage_type stype;	struct bpf_cgroup_storage_key key;	struct bpf_map *map; 	key.cgroup_inode_id = cgroup_id(cgrp);	key.attach_type = type; 	for_each_cgroup_storage_type(stype) {		map = prog->aux->cgroup_storage[stype];		if (!map)			continue; 		storages[stype] = cgroup_storage_lookup((void *)map, &key, false);		if (storages[stype])			continue; 		storages[stype] = bpf_cgroup_storage_alloc(prog, stype);		if (IS_ERR(storages[stype])) {			bpf_cgroup_storages_free(new_storages);			return -ENOMEM;		} 		new_storages[stype] = storages[stype];	} 	return 0;} static void bpf_cgroup_storages_assign(struct bpf_cgroup_storage *dst[],				       struct bpf_cgroup_storage *src[]){	enum bpf_cgroup_storage_type stype; 	for_each_cgroup_storage_type(stype)		dst[stype] = src[stype];} static void bpf_cgroup_storages_link(struct bpf_cgroup_storage *storages[],				     struct cgroup *cgrp,				     enum bpf_attach_type attach_type){	enum bpf_cgroup_storage_type stype; 	for_each_cgroup_storage_type(stype)		bpf_cgroup_storage_link(storages[stype], cgrp, attach_type);} /* Called when bpf_cgroup_link is auto-detached from dying cgroup. * It drops cgroup and bpf_prog refcounts, and marks bpf_link as defunct. It * doesn't free link memory, which will eventually be done by bpf_link's * release() callback, when its last FD is closed. */static void bpf_cgroup_link_auto_detach(struct bpf_cgroup_link *link){	cgroup_put(link->cgroup);	link->cgroup = NULL;} /** * cgroup_bpf_release() - put references of all bpf programs and *                        release all cgroup bpf data * @work: work structure embedded into the cgroup to modify */static void cgroup_bpf_release(struct work_struct *work){	struct cgroup *p, *cgrp = container_of(work, struct cgroup,					       bpf.release_work);	struct bpf_prog_array *old_array;	struct list_head *storages = &cgrp->bpf.storages;	struct bpf_cgroup_storage *storage, *stmp; 	unsigned int type; 	mutex_lock(&cgroup_mutex); 	for (type = 0; type < ARRAY_SIZE(cgrp->bpf.progs); type++) {		struct list_head *progs = &cgrp->bpf.progs[type];		struct bpf_prog_list *pl, *pltmp; 		list_for_each_entry_safe(pl, pltmp, progs, node) {			list_del(&pl->node);			if (pl->prog)				bpf_prog_put(pl->prog);			if (pl->link)				bpf_cgroup_link_auto_detach(pl->link);			kfree(pl);			static_branch_dec(&cgroup_bpf_enabled_key[type]);		}		old_array = rcu_dereference_protected(				cgrp->bpf.effective[type],				lockdep_is_held(&cgroup_mutex));		bpf_prog_array_free(old_array);	} 	list_for_each_entry_safe(storage, stmp, storages, list_cg) {		bpf_cgroup_storage_unlink(storage);		bpf_cgroup_storage_free(storage);	} 	mutex_unlock(&cgroup_mutex); 	for (p = cgroup_parent(cgrp); p; p = cgroup_parent(p))		cgroup_bpf_put(p); 	percpu_ref_exit(&cgrp->bpf.refcnt);	cgroup_put(cgrp);} /** * cgroup_bpf_release_fn() - callback used to schedule releasing *                           of bpf cgroup data * @ref: percpu ref counter structure */static void cgroup_bpf_release_fn(struct percpu_ref *ref){	struct cgroup *cgrp = container_of(ref, struct cgroup, bpf.refcnt); 	INIT_WORK(&cgrp->bpf.release_work, cgroup_bpf_release);	queue_work(system_wq, &cgrp->bpf.release_work);} /* Get underlying bpf_prog of bpf_prog_list entry, regardless if it's through * link or direct prog. */static struct bpf_prog *prog_list_prog(struct bpf_prog_list *pl){	if (pl->prog)		return pl->prog;	if (pl->link)		return pl->link->link.prog;	return NULL;} /* count number of elements in the list. * it's slow but the list cannot be long */static u32 prog_list_length(struct list_head *head){	struct bpf_prog_list *pl;	u32 cnt = 0; 	list_for_each_entry(pl, head, node) {		if (!prog_list_prog(pl))			continue;		cnt++;	}	return cnt;} /* if parent has non-overridable prog attached, * disallow attaching new programs to the descendent cgroup. * if parent has overridable or multi-prog, allow attaching */static bool hierarchy_allows_attach(struct cgroup *cgrp,				    enum bpf_attach_type type){	struct cgroup *p; 	p = cgroup_parent(cgrp);	if (!p)		return true;	do {		u32 flags = p->bpf.flags[type];		u32 cnt; 		if (flags & BPF_F_ALLOW_MULTI)			return true;		cnt = prog_list_length(&p->bpf.progs[type]);		WARN_ON_ONCE(cnt > 1);		if (cnt == 1)			return !!(flags & BPF_F_ALLOW_OVERRIDE);		p = cgroup_parent(p);	} while (p);	return true;} /* compute a chain of effective programs for a given cgroup: * start from the list of programs in this cgroup and add * all parent programs. * Note that parent's F_ALLOW_OVERRIDE-type program is yielding * to programs in this cgroup */static int compute_effective_progs(struct cgroup *cgrp,				   enum bpf_attach_type type,				   struct bpf_prog_array **array){	struct bpf_prog_array_item *item;	struct bpf_prog_array *progs;	struct bpf_prog_list *pl;	struct cgroup *p = cgrp;	int cnt = 0; 	/* count number of effective programs by walking parents */	do {		if (cnt == 0 || (p->bpf.flags[type] & BPF_F_ALLOW_MULTI))			cnt += prog_list_length(&p->bpf.progs[type]);		p = cgroup_parent(p);	} while (p); 	progs = bpf_prog_array_alloc(cnt, GFP_KERNEL);	if (!progs)		return -ENOMEM; 	/* populate the array with effective progs */	cnt = 0;	p = cgrp;	do {		if (cnt > 0 && !(p->bpf.flags[type] & BPF_F_ALLOW_MULTI))			continue; 		list_for_each_entry(pl, &p->bpf.progs[type], node) {			if (!prog_list_prog(pl))				continue; 			item = &progs->items[cnt];			item->prog = prog_list_prog(pl);			bpf_cgroup_storages_assign(item->cgroup_storage,						   pl->storage);			cnt++;		}	} while ((p = cgroup_parent(p))); 	*array = progs;	return 0;} static void activate_effective_progs(struct cgroup *cgrp,				     enum bpf_attach_type type,				     struct bpf_prog_array *old_array){	old_array = rcu_replace_pointer(cgrp->bpf.effective[type], old_array,					lockdep_is_held(&cgroup_mutex));	/* free prog array after grace period, since __cgroup_bpf_run_*()	 * might be still walking the array	 */	bpf_prog_array_free(old_array);} /** * cgroup_bpf_inherit() - inherit effective programs from parent * @cgrp: the cgroup to modify */int cgroup_bpf_inherit(struct cgroup *cgrp){/* has to use marco instead of const int, since compiler thinks * that array below is variable length */#define	NR ARRAY_SIZE(cgrp->bpf.effective)	struct bpf_prog_array *arrays[NR] = {};	struct cgroup *p;	int ret, i; 	ret = percpu_ref_init(&cgrp->bpf.refcnt, cgroup_bpf_release_fn, 0,			      GFP_KERNEL);	if (ret)		return ret; 	for (p = cgroup_parent(cgrp); p; p = cgroup_parent(p))		cgroup_bpf_get(p); 	for (i = 0; i < NR; i++)		INIT_LIST_HEAD(&cgrp->bpf.progs[i]); 	INIT_LIST_HEAD(&cgrp->bpf.storages); 	for (i = 0; i < NR; i++)		if (compute_effective_progs(cgrp, i, &arrays[i]))			goto cleanup; 	for (i = 0; i < NR; i++)		activate_effective_progs(cgrp, i, arrays[i]); 	return 0;cleanup:	for (i = 0; i < NR; i++)		bpf_prog_array_free(arrays[i]); 	for (p = cgroup_parent(cgrp); p; p = cgroup_parent(p))		cgroup_bpf_put(p); 	percpu_ref_exit(&cgrp->bpf.refcnt); 	return -ENOMEM;} static int update_effective_progs(struct cgroup *cgrp,				  enum bpf_attach_type type){	struct cgroup_subsys_state *css;	int err; 	/* allocate and recompute effective prog arrays */	css_for_each_descendant_pre(css, &cgrp->self) {		struct cgroup *desc = container_of(css, struct cgroup, self); 		if (percpu_ref_is_zero(&desc->bpf.refcnt))			continue; 		err = compute_effective_progs(desc, type, &desc->bpf.inactive);		if (err)			goto cleanup;	} 	/* all allocations were successful. Activate all prog arrays */	css_for_each_descendant_pre(css, &cgrp->self) {		struct cgroup *desc = container_of(css, struct cgroup, self); 		if (percpu_ref_is_zero(&desc->bpf.refcnt)) {			if (unlikely(desc->bpf.inactive)) {				bpf_prog_array_free(desc->bpf.inactive);				desc->bpf.inactive = NULL;			}			continue;		} 		activate_effective_progs(desc, type, desc->bpf.inactive);		desc->bpf.inactive = NULL;	} 	return 0; cleanup:	/* oom while computing effective. Free all computed effective arrays	 * since they were not activated	 */	css_for_each_descendant_pre(css, &cgrp->self) {		struct cgroup *desc = container_of(css, struct cgroup, self); 		bpf_prog_array_free(desc->bpf.inactive);		desc->bpf.inactive = NULL;	} 	return err;} #define BPF_CGROUP_MAX_PROGS 64 static struct bpf_prog_list *find_attach_entry(struct list_head *progs,					       struct bpf_prog *prog,					       struct bpf_cgroup_link *link,					       struct bpf_prog *replace_prog,					       bool allow_multi){	struct bpf_prog_list *pl; 	/* single-attach case */	if (!allow_multi) {		if (list_empty(progs))			return NULL;		return list_first_entry(progs, typeof(*pl), node);	} 	list_for_each_entry(pl, progs, node) {		if (prog && pl->prog == prog && prog != replace_prog)			/* disallow attaching the same prog twice */			return ERR_PTR(-EINVAL);		if (link && pl->link == link)			/* disallow attaching the same link twice */			return ERR_PTR(-EINVAL);	} 	/* direct prog multi-attach w/ replacement case */	if (replace_prog) {		list_for_each_entry(pl, progs, node) {			if (pl->prog == replace_prog)				/* a match found */				return pl;		}		/* prog to replace not found for cgroup */		return ERR_PTR(-ENOENT);	} 	return NULL;} /** * __cgroup_bpf_attach() - Attach the program or the link to a cgroup, and *                         propagate the change to descendants * @cgrp: The cgroup which descendants to traverse * @prog: A program to attach * @link: A link to attach * @replace_prog: Previously attached program to replace if BPF_F_REPLACE is set * @type: Type of attach operation * @flags: Option flags * * Exactly one of @prog or @link can be non-null. * Must be called with cgroup_mutex held. */int __cgroup_bpf_attach(struct cgroup *cgrp,			struct bpf_prog *prog, struct bpf_prog *replace_prog,			struct bpf_cgroup_link *link,			enum bpf_attach_type type, u32 flags){	u32 saved_flags = (flags & (BPF_F_ALLOW_OVERRIDE | BPF_F_ALLOW_MULTI));	struct list_head *progs = &cgrp->bpf.progs[type];	struct bpf_prog *old_prog = NULL;	struct bpf_cgroup_storage *storage[MAX_BPF_CGROUP_STORAGE_TYPE] = {};	struct bpf_cgroup_storage *new_storage[MAX_BPF_CGROUP_STORAGE_TYPE] = {};	struct bpf_prog_list *pl;	int err; 	if (((flags & BPF_F_ALLOW_OVERRIDE) && (flags & BPF_F_ALLOW_MULTI)) ||	    ((flags & BPF_F_REPLACE) && !(flags & BPF_F_ALLOW_MULTI)))		/* invalid combination */		return -EINVAL;	if (link && (prog || replace_prog))		/* only either link or prog/replace_prog can be specified */		return -EINVAL;	if (!!replace_prog != !!(flags & BPF_F_REPLACE))		/* replace_prog implies BPF_F_REPLACE, and vice versa */		return -EINVAL; 	if (!hierarchy_allows_attach(cgrp, type))		return -EPERM; 	if (!list_empty(progs) && cgrp->bpf.flags[type] != saved_flags)		/* Disallow attaching non-overridable on top		 * of existing overridable in this cgroup.		 * Disallow attaching multi-prog if overridable or none		 */		return -EPERM; 	if (prog_list_length(progs) >= BPF_CGROUP_MAX_PROGS)		return -E2BIG; 	pl = find_attach_entry(progs, prog, link, replace_prog,			       flags & BPF_F_ALLOW_MULTI);	if (IS_ERR(pl))		return PTR_ERR(pl); 	if (bpf_cgroup_storages_alloc(storage, new_storage, type,				      prog ? : link->link.prog, cgrp))		return -ENOMEM; 	if (pl) {		old_prog = pl->prog;	} else {		pl = kmalloc(sizeof(*pl), GFP_KERNEL);		if (!pl) {			bpf_cgroup_storages_free(new_storage);			return -ENOMEM;		}		list_add_tail(&pl->node, progs);	} 	pl->prog = prog;	pl->link = link;	bpf_cgroup_storages_assign(pl->storage, storage);	cgrp->bpf.flags[type] = saved_flags; 	err = update_effective_progs(cgrp, type);	if (err)		goto cleanup; 	if (old_prog)		bpf_prog_put(old_prog);

Lines 1–500 of 1999.