Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6529eaba authored by Jiri Pirko's avatar Jiri Pirko Committed by David S. Miller
Browse files

net: sched: introduce tcf block infractructure



Currently, the filter chains are direcly put into the private structures
of qdiscs. In order to be able to have multiple chains per qdisc and to
allow filter chains sharing among qdiscs, there is a need for common
object that would hold the chains. This introduces such object and calls
it "tcf_block".

Helpers to get and put the blocks are provided to be called from
individual qdisc code. Also, the original filter_list pointers are left
in qdisc privs to allow the entry into tcf_block processing without any
added overhead of possible multiple pointer dereference on fast path.

Signed-off-by: default avatarJiri Pirko <jiri@mellanox.com>
Acked-by: default avatarJamal Hadi Salim <jhs@mojatatu.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 87d83093
Loading
Loading
Loading
Loading
+11 −2
Original line number Diff line number Diff line
@@ -18,12 +18,21 @@ int register_tcf_proto_ops(struct tcf_proto_ops *ops);
int unregister_tcf_proto_ops(struct tcf_proto_ops *ops);

#ifdef CONFIG_NET_CLS
void tcf_destroy_chain(struct tcf_proto __rcu **fl);
int tcf_block_get(struct tcf_block **p_block,
		  struct tcf_proto __rcu **p_filter_chain);
void tcf_block_put(struct tcf_block *block);
int tcf_classify(struct sk_buff *skb, const struct tcf_proto *tp,
		 struct tcf_result *res, bool compat_mode);

#else
static inline void tcf_destroy_chain(struct tcf_proto __rcu **fl)
static inline
int tcf_block_get(struct tcf_block **p_block,
		  struct tcf_proto __rcu **p_filter_chain)
{
	return 0;
}

static inline void tcf_block_put(struct tcf_block *block)
{
}

+6 −1
Original line number Diff line number Diff line
@@ -153,7 +153,7 @@ struct Qdisc_class_ops {
	void			(*walk)(struct Qdisc *, struct qdisc_walker * arg);

	/* Filter manipulation */
	struct tcf_proto __rcu ** (*tcf_chain)(struct Qdisc *, unsigned long);
	struct tcf_block *	(*tcf_block)(struct Qdisc *, unsigned long);
	bool			(*tcf_cl_offload)(u32 classid);
	unsigned long		(*bind_tcf)(struct Qdisc *, unsigned long,
					u32 classid);
@@ -236,6 +236,7 @@ struct tcf_proto {
	struct Qdisc		*q;
	void			*data;
	const struct tcf_proto_ops	*ops;
	struct tcf_block	*block;
	struct rcu_head		rcu;
};

@@ -247,6 +248,10 @@ struct qdisc_skb_cb {
	unsigned char		data[QDISC_CB_PRIV_LEN];
};

struct tcf_block {
	struct tcf_proto __rcu **p_filter_chain;
};

static inline void qdisc_cb_private_validate(const struct sk_buff *skb, int sz)
{
	struct qdisc_skb_cb *qcb;
+38 −10
Original line number Diff line number Diff line
@@ -129,7 +129,8 @@ static inline u32 tcf_auto_prio(struct tcf_proto *tp)
}

static struct tcf_proto *tcf_proto_create(const char *kind, u32 protocol,
					  u32 prio, u32 parent, struct Qdisc *q)
					  u32 prio, u32 parent, struct Qdisc *q,
					  struct tcf_block *block)
{
	struct tcf_proto *tp;
	int err;
@@ -165,6 +166,7 @@ static struct tcf_proto *tcf_proto_create(const char *kind, u32 protocol,
	tp->prio = prio;
	tp->classid = parent;
	tp->q = q;
	tp->block = block;

	err = tp->ops->init(tp);
	if (err) {
@@ -185,7 +187,7 @@ static void tcf_proto_destroy(struct tcf_proto *tp)
	kfree_rcu(tp, rcu);
}

void tcf_destroy_chain(struct tcf_proto __rcu **fl)
static void tcf_destroy_chain(struct tcf_proto __rcu **fl)
{
	struct tcf_proto *tp;

@@ -194,7 +196,28 @@ void tcf_destroy_chain(struct tcf_proto __rcu **fl)
		tcf_proto_destroy(tp);
	}
}
EXPORT_SYMBOL(tcf_destroy_chain);

int tcf_block_get(struct tcf_block **p_block,
		  struct tcf_proto __rcu **p_filter_chain)
{
	struct tcf_block *block = kzalloc(sizeof(*block), GFP_KERNEL);

	if (!block)
		return -ENOMEM;
	block->p_filter_chain = p_filter_chain;
	*p_block = block;
	return 0;
}
EXPORT_SYMBOL(tcf_block_get);

void tcf_block_put(struct tcf_block *block)
{
	if (!block)
		return;
	tcf_destroy_chain(block->p_filter_chain);
	kfree(block);
}
EXPORT_SYMBOL(tcf_block_put);

/* Main classifier routine: scans classifier chain attached
 * to this qdisc, (optionally) tests for protocol and asks
@@ -260,6 +283,7 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
	struct Qdisc  *q;
	struct tcf_proto __rcu **back;
	struct tcf_proto __rcu **chain;
	struct tcf_block *block;
	struct tcf_proto *next;
	struct tcf_proto *tp;
	const struct Qdisc_class_ops *cops;
@@ -328,7 +352,7 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
	if (!cops)
		return -EINVAL;

	if (cops->tcf_chain == NULL)
	if (!cops->tcf_block)
		return -EOPNOTSUPP;

	/* Do we search for filter, attached to class? */
@@ -339,11 +363,13 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
	}

	/* And the last stroke */
	chain = cops->tcf_chain(q, cl);
	if (chain == NULL) {
	block = cops->tcf_block(q, cl);
	if (!block) {
		err = -EINVAL;
		goto errout;
	}
	chain = block->p_filter_chain;

	if (n->nlmsg_type == RTM_DELTFILTER && prio == 0) {
		tfilter_notify_chain(net, skb, n, chain, RTM_DELTFILTER);
		tcf_destroy_chain(chain);
@@ -387,7 +413,7 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
			nprio = TC_H_MAJ(tcf_auto_prio(rtnl_dereference(*back)));

		tp = tcf_proto_create(nla_data(tca[TCA_KIND]),
				      protocol, nprio, parent, q);
				      protocol, nprio, parent, q, block);
		if (IS_ERR(tp)) {
			err = PTR_ERR(tp);
			goto errout;
@@ -556,6 +582,7 @@ static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb)
	int s_t;
	struct net_device *dev;
	struct Qdisc *q;
	struct tcf_block *block;
	struct tcf_proto *tp, __rcu **chain;
	struct tcmsg *tcm = nlmsg_data(cb->nlh);
	unsigned long cl = 0;
@@ -577,16 +604,17 @@ static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb)
	cops = q->ops->cl_ops;
	if (!cops)
		goto errout;
	if (cops->tcf_chain == NULL)
	if (!cops->tcf_block)
		goto errout;
	if (TC_H_MIN(tcm->tcm_parent)) {
		cl = cops->get(q, tcm->tcm_parent);
		if (cl == 0)
			goto errout;
	}
	chain = cops->tcf_chain(q, cl);
	if (chain == NULL)
	block = cops->tcf_block(q, cl);
	if (!block)
		goto errout;
	chain = block->p_filter_chain;

	s_t = cb->args[0];

+1 −1
Original line number Diff line number Diff line
@@ -163,7 +163,7 @@ int register_qdisc(struct Qdisc_ops *qops)
		if (!(cops->get && cops->put && cops->walk && cops->leaf))
			goto out_einval;

		if (cops->tcf_chain && !(cops->bind_tcf && cops->unbind_tcf))
		if (cops->tcf_block && !(cops->bind_tcf && cops->unbind_tcf))
			goto out_einval;
	}

+19 −8
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@
struct atm_flow_data {
	struct Qdisc		*q;	/* FIFO, TBF, etc. */
	struct tcf_proto __rcu	*filter_list;
	struct tcf_block	*block;
	struct atm_vcc		*vcc;	/* VCC; NULL if VCC is closed */
	void			(*old_pop)(struct atm_vcc *vcc,
					   struct sk_buff *skb); /* chaining */
@@ -143,7 +144,7 @@ static void atm_tc_put(struct Qdisc *sch, unsigned long cl)
	list_del_init(&flow->list);
	pr_debug("atm_tc_put: qdisc %p\n", flow->q);
	qdisc_destroy(flow->q);
	tcf_destroy_chain(&flow->filter_list);
	tcf_block_put(flow->block);
	if (flow->sock) {
		pr_debug("atm_tc_put: f_count %ld\n",
			file_count(flow->sock->file));
@@ -274,7 +275,13 @@ static int atm_tc_change(struct Qdisc *sch, u32 classid, u32 parent,
		error = -ENOBUFS;
		goto err_out;
	}
	RCU_INIT_POINTER(flow->filter_list, NULL);

	error = tcf_block_get(&flow->block, &flow->filter_list);
	if (error) {
		kfree(flow);
		goto err_out;
	}

	flow->q = qdisc_create_dflt(sch->dev_queue, &pfifo_qdisc_ops, classid);
	if (!flow->q)
		flow->q = &noop_qdisc;
@@ -346,14 +353,13 @@ static void atm_tc_walk(struct Qdisc *sch, struct qdisc_walker *walker)
	}
}

static struct tcf_proto __rcu **atm_tc_find_tcf(struct Qdisc *sch,
						unsigned long cl)
static struct tcf_block *atm_tc_tcf_block(struct Qdisc *sch, unsigned long cl)
{
	struct atm_qdisc_data *p = qdisc_priv(sch);
	struct atm_flow_data *flow = (struct atm_flow_data *)cl;

	pr_debug("atm_tc_find_tcf(sch %p,[qdisc %p],flow %p)\n", sch, p, flow);
	return flow ? &flow->filter_list : &p->link.filter_list;
	return flow ? flow->block : p->link.block;
}

/* --------------------------- Qdisc operations ---------------------------- */
@@ -524,6 +530,7 @@ static struct sk_buff *atm_tc_peek(struct Qdisc *sch)
static int atm_tc_init(struct Qdisc *sch, struct nlattr *opt)
{
	struct atm_qdisc_data *p = qdisc_priv(sch);
	int err;

	pr_debug("atm_tc_init(sch %p,[qdisc %p],opt %p)\n", sch, p, opt);
	INIT_LIST_HEAD(&p->flows);
@@ -534,7 +541,11 @@ static int atm_tc_init(struct Qdisc *sch, struct nlattr *opt)
	if (!p->link.q)
		p->link.q = &noop_qdisc;
	pr_debug("atm_tc_init: link (%p) qdisc %p\n", &p->link, p->link.q);
	RCU_INIT_POINTER(p->link.filter_list, NULL);

	err = tcf_block_get(&p->link.block, &p->link.filter_list);
	if (err)
		return err;

	p->link.vcc = NULL;
	p->link.sock = NULL;
	p->link.classid = sch->handle;
@@ -561,7 +572,7 @@ static void atm_tc_destroy(struct Qdisc *sch)

	pr_debug("atm_tc_destroy(sch %p,[qdisc %p])\n", sch, p);
	list_for_each_entry(flow, &p->flows, list)
		tcf_destroy_chain(&flow->filter_list);
		tcf_block_put(flow->block);

	list_for_each_entry_safe(flow, tmp, &p->flows, list) {
		if (flow->ref > 1)
@@ -646,7 +657,7 @@ static const struct Qdisc_class_ops atm_class_ops = {
	.change		= atm_tc_change,
	.delete		= atm_tc_delete,
	.walk		= atm_tc_walk,
	.tcf_chain	= atm_tc_find_tcf,
	.tcf_block	= atm_tc_tcf_block,
	.bind_tcf	= atm_tc_bind_filter,
	.unbind_tcf	= atm_tc_put,
	.dump		= atm_tc_dump_class,
Loading