static int tcf_bpf_init_from_efd(struct nlattr **tb, struct tcf_bpf_cfg *cfg) { struct bpf_prog *fp; char *name = NULL; u32 bpf_fd; bpf_fd = nla_get_u32(tb[TCA_ACT_BPF_FD]); fp = bpf_prog_get(bpf_fd); if (IS_ERR(fp)) return PTR_ERR(fp); if (fp->type != BPF_PROG_TYPE_SCHED_ACT) { bpf_prog_put(fp); return -EINVAL; } if (tb[TCA_ACT_BPF_NAME]) { name = kmemdup(nla_data(tb[TCA_ACT_BPF_NAME]), nla_len(tb[TCA_ACT_BPF_NAME]), GFP_KERNEL); if (!name) { bpf_prog_put(fp); return -ENOMEM; } } cfg->bpf_fd = bpf_fd; cfg->bpf_name = name; cfg->filter = fp; return 0; }
static int tcf_bpf_init_from_efd(struct nlattr **tb, struct tcf_bpf_cfg *cfg) { struct bpf_prog *fp; char *name = NULL; u32 bpf_fd; bpf_fd = nla_get_u32(tb[TCA_ACT_BPF_FD]); fp = bpf_prog_get_type(bpf_fd, BPF_PROG_TYPE_SCHED_ACT); if (IS_ERR(fp)) return PTR_ERR(fp); if (tb[TCA_ACT_BPF_NAME]) { name = nla_memdup(tb[TCA_ACT_BPF_NAME], GFP_KERNEL); if (!name) { bpf_prog_put(fp); return -ENOMEM; } } cfg->bpf_name = name; cfg->filter = fp; cfg->is_ebpf = true; return 0; }
/* only called from syscall */ static int prog_array_map_update_elem(struct bpf_map *map, void *key, void *value, u64 map_flags) { struct bpf_array *array = container_of(map, struct bpf_array, map); struct bpf_prog *prog, *old_prog; u32 index = *(u32 *)key, ufd; if (map_flags != BPF_ANY) return -EINVAL; if (index >= array->map.max_entries) return -E2BIG; ufd = *(u32 *)value; prog = bpf_prog_get(ufd); if (IS_ERR(prog)) return PTR_ERR(prog); if (!bpf_prog_array_compatible(array, prog)) { bpf_prog_put(prog); return -EINVAL; } old_prog = xchg(array->prog + index, prog); if (old_prog) bpf_prog_put_rcu(old_prog); return 0; }
static void tcf_bpf_cleanup(struct tc_action *act, int bind) { const struct tcf_bpf *prog = act->priv; if (tcf_bpf_is_ebpf(prog)) bpf_prog_put(prog->filter); else bpf_prog_destroy(prog->filter); }
static void tcf_bpf_cfg_cleanup(const struct tcf_bpf_cfg *cfg) { if (cfg->is_ebpf) bpf_prog_put(cfg->filter); else bpf_prog_destroy(cfg->filter); kfree(cfg->bpf_ops); kfree(cfg->bpf_name); }
static void *prog_fd_array_get_ptr(struct bpf_map *map, int fd) { struct bpf_array *array = container_of(map, struct bpf_array, map); struct bpf_prog *prog = bpf_prog_get(fd); if (IS_ERR(prog)) return prog; if (!bpf_prog_array_compatible(array, prog)) { bpf_prog_put(prog); return ERR_PTR(-EINVAL); } return prog; }
static void bpf_any_put(void *raw, enum bpf_type type) { switch (type) { case BPF_TYPE_PROG: bpf_prog_put(raw); break; case BPF_TYPE_MAP: bpf_map_put_with_uref(raw); break; default: WARN_ON_ONCE(1); break; } }
/** * cgroup_bpf_put() - put references of all bpf programs * @cgrp: the cgroup to modify */ void cgroup_bpf_put(struct cgroup *cgrp) { unsigned int type; for (type = 0; type < ARRAY_SIZE(cgrp->bpf.progs); type++) { struct list_head *progs = &cgrp->bpf.progs[type]; struct bpf_prog_list *pl, *tmp; list_for_each_entry_safe(pl, tmp, progs, node) { list_del(&pl->node); bpf_prog_put(pl->prog); kfree(pl); static_branch_dec(&cgroup_bpf_enabled_key); } bpf_prog_array_free(cgrp->bpf.effective[type]); }
static int tcf_bpf_init(struct net *net, struct nlattr *nla, struct nlattr *est, struct tc_action *act, int replace, int bind) { struct nlattr *tb[TCA_ACT_BPF_MAX + 1]; struct tc_act_bpf *parm; struct tcf_bpf *prog; struct tcf_bpf_cfg cfg; bool is_bpf, is_ebpf; int ret; if (!nla) return -EINVAL; ret = nla_parse_nested(tb, TCA_ACT_BPF_MAX, nla, act_bpf_policy); if (ret < 0) return ret; is_bpf = tb[TCA_ACT_BPF_OPS_LEN] && tb[TCA_ACT_BPF_OPS]; is_ebpf = tb[TCA_ACT_BPF_FD]; if ((!is_bpf && !is_ebpf) || (is_bpf && is_ebpf) || !tb[TCA_ACT_BPF_PARMS]) return -EINVAL; parm = nla_data(tb[TCA_ACT_BPF_PARMS]); memset(&cfg, 0, sizeof(cfg)); ret = is_bpf ? tcf_bpf_init_from_ops(tb, &cfg) : tcf_bpf_init_from_efd(tb, &cfg); if (ret < 0) return ret; if (!tcf_hash_check(parm->index, act, bind)) { ret = tcf_hash_create(parm->index, est, act, sizeof(*prog), bind); if (ret < 0) goto destroy_fp; ret = ACT_P_CREATED; } else { /* Don't override defaults. */ if (bind) goto destroy_fp; tcf_hash_release(act, bind); if (!replace) { ret = -EEXIST; goto destroy_fp; } } prog = to_bpf(act); spin_lock_bh(&prog->tcf_lock); prog->bpf_ops = cfg.bpf_ops; prog->bpf_name = cfg.bpf_name; if (cfg.bpf_num_ops) prog->bpf_num_ops = cfg.bpf_num_ops; if (cfg.bpf_fd) prog->bpf_fd = cfg.bpf_fd; prog->tcf_action = parm->action; prog->filter = cfg.filter; spin_unlock_bh(&prog->tcf_lock); if (ret == ACT_P_CREATED) tcf_hash_insert(act); return ret; destroy_fp: if (is_ebpf) bpf_prog_put(cfg.filter); else bpf_prog_destroy(cfg.filter); kfree(cfg.bpf_ops); kfree(cfg.bpf_name); return ret; }
static void prog_fd_array_put_ptr(void *ptr) { bpf_prog_put(ptr); }