Commit 3b3ae880 authored by Daniel Borkmann's avatar Daniel Borkmann Committed by David S. Miller

net: sched: consolidate tc_classify{,_compat}

For classifiers getting invoked via tc_classify(), we always need an
extra function call into tc_classify_compat(), as both are being
exported as symbols and tc_classify() itself doesn't do much except
handling of reclassifications when tp->classify() returned with
TC_ACT_RECLASSIFY.

CBQ and ATM are the only qdiscs that directly call into tc_classify_compat(),
all others use tc_classify(). When tc actions are being configured
out in the kernel, tc_classify() effectively does nothing besides
delegating.

We could spare this layer and consolidate both functions. pktgen on
single CPU constantly pushing skbs directly into the netif_receive_skb()
path with a dummy classifier on ingress qdisc attached, improves
slightly from 22.3Mpps to 23.1Mpps.
Signed-off-by: default avatarDaniel Borkmann <daniel@iogearbox.net>
Acked-by: default avatarAlexei Starovoitov <ast@plumgrid.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent fe218823
...@@ -110,10 +110,8 @@ static inline void qdisc_run(struct Qdisc *q) ...@@ -110,10 +110,8 @@ static inline void qdisc_run(struct Qdisc *q)
__qdisc_run(q); __qdisc_run(q);
} }
int tc_classify_compat(struct sk_buff *skb, const struct tcf_proto *tp,
struct tcf_result *res);
int tc_classify(struct sk_buff *skb, const struct tcf_proto *tp, int tc_classify(struct sk_buff *skb, const struct tcf_proto *tp,
struct tcf_result *res); struct tcf_result *res, bool compat_mode);
static inline __be16 tc_skb_protocol(const struct sk_buff *skb) static inline __be16 tc_skb_protocol(const struct sk_buff *skb)
{ {
......
...@@ -3657,7 +3657,7 @@ static inline struct sk_buff *handle_ing(struct sk_buff *skb, ...@@ -3657,7 +3657,7 @@ static inline struct sk_buff *handle_ing(struct sk_buff *skb,
skb->tc_verd = SET_TC_AT(skb->tc_verd, AT_INGRESS); skb->tc_verd = SET_TC_AT(skb->tc_verd, AT_INGRESS);
qdisc_bstats_cpu_update(cl->q, skb); qdisc_bstats_cpu_update(cl->q, skb);
switch (tc_classify(skb, cl, &cl_res)) { switch (tc_classify(skb, cl, &cl_res, false)) {
case TC_ACT_OK: case TC_ACT_OK:
case TC_ACT_RECLASSIFY: case TC_ACT_RECLASSIFY:
skb->tc_index = TC_H_MIN(cl_res.classid); skb->tc_index = TC_H_MIN(cl_res.classid);
......
...@@ -1806,51 +1806,46 @@ static int tc_dump_tclass(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -1806,51 +1806,46 @@ static int tc_dump_tclass(struct sk_buff *skb, struct netlink_callback *cb)
* to this qdisc, (optionally) tests for protocol and asks * to this qdisc, (optionally) tests for protocol and asks
* specific classifiers. * specific classifiers.
*/ */
int tc_classify_compat(struct sk_buff *skb, const struct tcf_proto *tp, int tc_classify(struct sk_buff *skb, const struct tcf_proto *tp,
struct tcf_result *res) struct tcf_result *res, bool compat_mode)
{ {
__be16 protocol = tc_skb_protocol(skb); __be16 protocol = tc_skb_protocol(skb);
int err; #ifdef CONFIG_NET_CLS_ACT
const struct tcf_proto *old_tp = tp;
int limit = 0;
reclassify:
#endif
for (; tp; tp = rcu_dereference_bh(tp->next)) { for (; tp; tp = rcu_dereference_bh(tp->next)) {
int err;
if (tp->protocol != protocol && if (tp->protocol != protocol &&
tp->protocol != htons(ETH_P_ALL)) tp->protocol != htons(ETH_P_ALL))
continue; continue;
err = tp->classify(skb, tp, res);
err = tp->classify(skb, tp, res);
#ifdef CONFIG_NET_CLS_ACT
if (unlikely(err == TC_ACT_RECLASSIFY &&
!compat_mode))
goto reset;
#endif
if (err >= 0) if (err >= 0)
return err; return err;
} }
return -1;
}
EXPORT_SYMBOL(tc_classify_compat);
int tc_classify(struct sk_buff *skb, const struct tcf_proto *tp, return -1;
struct tcf_result *res)
{
int err = 0;
#ifdef CONFIG_NET_CLS_ACT
const struct tcf_proto *otp = tp;
int limit = 0;
reclassify:
#endif
err = tc_classify_compat(skb, tp, res);
#ifdef CONFIG_NET_CLS_ACT #ifdef CONFIG_NET_CLS_ACT
if (err == TC_ACT_RECLASSIFY) { reset:
tp = otp; if (unlikely(limit++ >= MAX_REC_LOOP)) {
net_notice_ratelimited("%s: reclassify loop, rule prio %u, "
if (unlikely(limit++ >= MAX_REC_LOOP)) { "protocol %02x\n", tp->q->ops->id,
net_notice_ratelimited("%s: packet reclassify loop rule prio %u protocol %02x\n", tp->prio & 0xffff, ntohs(tp->protocol));
tp->q->ops->id, return TC_ACT_SHOT;
tp->prio & 0xffff,
ntohs(tp->protocol));
return TC_ACT_SHOT;
}
goto reclassify;
} }
tp = old_tp;
goto reclassify;
#endif #endif
return err;
} }
EXPORT_SYMBOL(tc_classify); EXPORT_SYMBOL(tc_classify);
......
...@@ -375,7 +375,7 @@ static int atm_tc_enqueue(struct sk_buff *skb, struct Qdisc *sch) ...@@ -375,7 +375,7 @@ static int atm_tc_enqueue(struct sk_buff *skb, struct Qdisc *sch)
list_for_each_entry(flow, &p->flows, list) { list_for_each_entry(flow, &p->flows, list) {
fl = rcu_dereference_bh(flow->filter_list); fl = rcu_dereference_bh(flow->filter_list);
if (fl) { if (fl) {
result = tc_classify_compat(skb, fl, &res); result = tc_classify(skb, fl, &res, true);
if (result < 0) if (result < 0)
continue; continue;
flow = (struct atm_flow_data *)res.class; flow = (struct atm_flow_data *)res.class;
......
...@@ -240,7 +240,7 @@ cbq_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr) ...@@ -240,7 +240,7 @@ cbq_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr)
/* /*
* Step 2+n. Apply classifier. * Step 2+n. Apply classifier.
*/ */
result = tc_classify_compat(skb, fl, &res); result = tc_classify(skb, fl, &res, true);
if (!fl || result < 0) if (!fl || result < 0)
goto fallback; goto fallback;
......
...@@ -201,7 +201,7 @@ static bool choke_classify(struct sk_buff *skb, ...@@ -201,7 +201,7 @@ static bool choke_classify(struct sk_buff *skb,
int result; int result;
fl = rcu_dereference_bh(q->filter_list); fl = rcu_dereference_bh(q->filter_list);
result = tc_classify(skb, fl, &res); result = tc_classify(skb, fl, &res, false);
if (result >= 0) { if (result >= 0) {
#ifdef CONFIG_NET_CLS_ACT #ifdef CONFIG_NET_CLS_ACT
switch (result) { switch (result) {
......
...@@ -331,7 +331,7 @@ static struct drr_class *drr_classify(struct sk_buff *skb, struct Qdisc *sch, ...@@ -331,7 +331,7 @@ static struct drr_class *drr_classify(struct sk_buff *skb, struct Qdisc *sch,
*qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS; *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
fl = rcu_dereference_bh(q->filter_list); fl = rcu_dereference_bh(q->filter_list);
result = tc_classify(skb, fl, &res); result = tc_classify(skb, fl, &res, false);
if (result >= 0) { if (result >= 0) {
#ifdef CONFIG_NET_CLS_ACT #ifdef CONFIG_NET_CLS_ACT
switch (result) { switch (result) {
......
...@@ -230,7 +230,7 @@ static int dsmark_enqueue(struct sk_buff *skb, struct Qdisc *sch) ...@@ -230,7 +230,7 @@ static int dsmark_enqueue(struct sk_buff *skb, struct Qdisc *sch)
else { else {
struct tcf_result res; struct tcf_result res;
struct tcf_proto *fl = rcu_dereference_bh(p->filter_list); struct tcf_proto *fl = rcu_dereference_bh(p->filter_list);
int result = tc_classify(skb, fl, &res); int result = tc_classify(skb, fl, &res, false);
pr_debug("result %d class 0x%04x\n", result, res.classid); pr_debug("result %d class 0x%04x\n", result, res.classid);
......
...@@ -92,7 +92,7 @@ static unsigned int fq_codel_classify(struct sk_buff *skb, struct Qdisc *sch, ...@@ -92,7 +92,7 @@ static unsigned int fq_codel_classify(struct sk_buff *skb, struct Qdisc *sch,
return fq_codel_hash(q, skb) + 1; return fq_codel_hash(q, skb) + 1;
*qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS; *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
result = tc_classify(skb, filter, &res); result = tc_classify(skb, filter, &res, false);
if (result >= 0) { if (result >= 0) {
#ifdef CONFIG_NET_CLS_ACT #ifdef CONFIG_NET_CLS_ACT
switch (result) { switch (result) {
......
...@@ -1165,7 +1165,7 @@ hfsc_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr) ...@@ -1165,7 +1165,7 @@ hfsc_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr)
*qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS; *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
head = &q->root; head = &q->root;
tcf = rcu_dereference_bh(q->root.filter_list); tcf = rcu_dereference_bh(q->root.filter_list);
while (tcf && (result = tc_classify(skb, tcf, &res)) >= 0) { while (tcf && (result = tc_classify(skb, tcf, &res, false)) >= 0) {
#ifdef CONFIG_NET_CLS_ACT #ifdef CONFIG_NET_CLS_ACT
switch (result) { switch (result) {
case TC_ACT_QUEUED: case TC_ACT_QUEUED:
......
...@@ -229,7 +229,7 @@ static struct htb_class *htb_classify(struct sk_buff *skb, struct Qdisc *sch, ...@@ -229,7 +229,7 @@ static struct htb_class *htb_classify(struct sk_buff *skb, struct Qdisc *sch,
} }
*qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS; *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
while (tcf && (result = tc_classify(skb, tcf, &res)) >= 0) { while (tcf && (result = tc_classify(skb, tcf, &res, false)) >= 0) {
#ifdef CONFIG_NET_CLS_ACT #ifdef CONFIG_NET_CLS_ACT
switch (result) { switch (result) {
case TC_ACT_QUEUED: case TC_ACT_QUEUED:
......
...@@ -46,7 +46,7 @@ multiq_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr) ...@@ -46,7 +46,7 @@ multiq_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr)
int err; int err;
*qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS; *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
err = tc_classify(skb, fl, &res); err = tc_classify(skb, fl, &res, false);
#ifdef CONFIG_NET_CLS_ACT #ifdef CONFIG_NET_CLS_ACT
switch (err) { switch (err) {
case TC_ACT_STOLEN: case TC_ACT_STOLEN:
......
...@@ -42,7 +42,7 @@ prio_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr) ...@@ -42,7 +42,7 @@ prio_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr)
*qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS; *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
if (TC_H_MAJ(skb->priority) != sch->handle) { if (TC_H_MAJ(skb->priority) != sch->handle) {
fl = rcu_dereference_bh(q->filter_list); fl = rcu_dereference_bh(q->filter_list);
err = tc_classify(skb, fl, &res); err = tc_classify(skb, fl, &res, false);
#ifdef CONFIG_NET_CLS_ACT #ifdef CONFIG_NET_CLS_ACT
switch (err) { switch (err) {
case TC_ACT_STOLEN: case TC_ACT_STOLEN:
......
...@@ -717,7 +717,7 @@ static struct qfq_class *qfq_classify(struct sk_buff *skb, struct Qdisc *sch, ...@@ -717,7 +717,7 @@ static struct qfq_class *qfq_classify(struct sk_buff *skb, struct Qdisc *sch,
*qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS; *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
fl = rcu_dereference_bh(q->filter_list); fl = rcu_dereference_bh(q->filter_list);
result = tc_classify(skb, fl, &res); result = tc_classify(skb, fl, &res, false);
if (result >= 0) { if (result >= 0) {
#ifdef CONFIG_NET_CLS_ACT #ifdef CONFIG_NET_CLS_ACT
switch (result) { switch (result) {
......
...@@ -258,7 +258,7 @@ static bool sfb_classify(struct sk_buff *skb, struct tcf_proto *fl, ...@@ -258,7 +258,7 @@ static bool sfb_classify(struct sk_buff *skb, struct tcf_proto *fl,
struct tcf_result res; struct tcf_result res;
int result; int result;
result = tc_classify(skb, fl, &res); result = tc_classify(skb, fl, &res, false);
if (result >= 0) { if (result >= 0) {
#ifdef CONFIG_NET_CLS_ACT #ifdef CONFIG_NET_CLS_ACT
switch (result) { switch (result) {
......
...@@ -179,7 +179,7 @@ static unsigned int sfq_classify(struct sk_buff *skb, struct Qdisc *sch, ...@@ -179,7 +179,7 @@ static unsigned int sfq_classify(struct sk_buff *skb, struct Qdisc *sch,
return sfq_hash(q, skb) + 1; return sfq_hash(q, skb) + 1;
*qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS; *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
result = tc_classify(skb, fl, &res); result = tc_classify(skb, fl, &res, false);
if (result >= 0) { if (result >= 0) {
#ifdef CONFIG_NET_CLS_ACT #ifdef CONFIG_NET_CLS_ACT
switch (result) { switch (result) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment