seq/proc: modify seq_put_decimal_[u]ll to take a const char *, not char
[cascardo/linux.git] / net / sched / act_vlan.c
index 691409d..b57fcbc 100644 (file)
@@ -30,12 +30,19 @@ static int tcf_vlan(struct sk_buff *skb, const struct tc_action *a,
        struct tcf_vlan *v = to_vlan(a);
        int action;
        int err;
+       u16 tci;
 
        spin_lock(&v->tcf_lock);
        tcf_lastuse_update(&v->tcf_tm);
        bstats_update(&v->tcf_bstats, skb);
        action = v->tcf_action;
 
+       /* Ensure 'data' points at mac_header prior calling vlan manipulating
+        * functions.
+        */
+       if (skb_at_tc_ingress(skb))
+               skb_push_rcsum(skb, skb->mac_len);
+
        switch (v->tcfv_action) {
        case TCA_VLAN_ACT_POP:
                err = skb_vlan_pop(skb);
@@ -43,10 +50,35 @@ static int tcf_vlan(struct sk_buff *skb, const struct tc_action *a,
                        goto drop;
                break;
        case TCA_VLAN_ACT_PUSH:
-               err = skb_vlan_push(skb, v->tcfv_push_proto, v->tcfv_push_vid);
+               err = skb_vlan_push(skb, v->tcfv_push_proto, v->tcfv_push_vid |
+                                   (v->tcfv_push_prio << VLAN_PRIO_SHIFT));
                if (err)
                        goto drop;
                break;
+       case TCA_VLAN_ACT_MODIFY:
+               /* No-op if no vlan tag (either hw-accel or in-payload) */
+               if (!skb_vlan_tagged(skb))
+                       goto unlock;
+               /* extract existing tag (and guarantee no hw-accel tag) */
+               if (skb_vlan_tag_present(skb)) {
+                       tci = skb_vlan_tag_get(skb);
+                       skb->vlan_tci = 0;
+               } else {
+                       /* in-payload vlan tag, pop it */
+                       err = __skb_vlan_pop(skb, &tci);
+                       if (err)
+                               goto drop;
+               }
+               /* replace the vid */
+               tci = (tci & ~VLAN_VID_MASK) | v->tcfv_push_vid;
+               /* replace prio bits, if tcfv_push_prio specified */
+               if (v->tcfv_push_prio) {
+                       tci &= ~VLAN_PRIO_MASK;
+                       tci |= v->tcfv_push_prio << VLAN_PRIO_SHIFT;
+               }
+               /* put updated tci as hwaccel tag */
+               __vlan_hwaccel_put_tag(skb, v->tcfv_push_proto, tci);
+               break;
        default:
                BUG();
        }
@@ -57,6 +89,9 @@ drop:
        action = TC_ACT_SHOT;
        v->tcf_qstats.drops++;
 unlock:
+       if (skb_at_tc_ingress(skb))
+               skb_pull_rcsum(skb, skb->mac_len);
+
        spin_unlock(&v->tcf_lock);
        return action;
 }
@@ -65,6 +100,7 @@ static const struct nla_policy vlan_policy[TCA_VLAN_MAX + 1] = {
        [TCA_VLAN_PARMS]                = { .len = sizeof(struct tc_vlan) },
        [TCA_VLAN_PUSH_VLAN_ID]         = { .type = NLA_U16 },
        [TCA_VLAN_PUSH_VLAN_PROTOCOL]   = { .type = NLA_U16 },
+       [TCA_VLAN_PUSH_VLAN_PRIORITY]   = { .type = NLA_U8 },
 };
 
 static int tcf_vlan_init(struct net *net, struct nlattr *nla,
@@ -78,6 +114,7 @@ static int tcf_vlan_init(struct net *net, struct nlattr *nla,
        int action;
        __be16 push_vid = 0;
        __be16 push_proto = 0;
+       u8 push_prio = 0;
        bool exists = false;
        int ret = 0, err;
 
@@ -99,6 +136,7 @@ static int tcf_vlan_init(struct net *net, struct nlattr *nla,
        case TCA_VLAN_ACT_POP:
                break;
        case TCA_VLAN_ACT_PUSH:
+       case TCA_VLAN_ACT_MODIFY:
                if (!tb[TCA_VLAN_PUSH_VLAN_ID]) {
                        if (exists)
                                tcf_hash_release(*a, bind);
@@ -123,6 +161,9 @@ static int tcf_vlan_init(struct net *net, struct nlattr *nla,
                } else {
                        push_proto = htons(ETH_P_8021Q);
                }
+
+               if (tb[TCA_VLAN_PUSH_VLAN_PRIORITY])
+                       push_prio = nla_get_u8(tb[TCA_VLAN_PUSH_VLAN_PRIORITY]);
                break;
        default:
                if (exists)
@@ -150,6 +191,7 @@ static int tcf_vlan_init(struct net *net, struct nlattr *nla,
 
        v->tcfv_action = action;
        v->tcfv_push_vid = push_vid;
+       v->tcfv_push_prio = push_prio;
        v->tcfv_push_proto = push_proto;
 
        v->tcf_action = parm->action;
@@ -178,10 +220,13 @@ static int tcf_vlan_dump(struct sk_buff *skb, struct tc_action *a,
        if (nla_put(skb, TCA_VLAN_PARMS, sizeof(opt), &opt))
                goto nla_put_failure;
 
-       if (v->tcfv_action == TCA_VLAN_ACT_PUSH &&
+       if ((v->tcfv_action == TCA_VLAN_ACT_PUSH ||
+            v->tcfv_action == TCA_VLAN_ACT_MODIFY) &&
            (nla_put_u16(skb, TCA_VLAN_PUSH_VLAN_ID, v->tcfv_push_vid) ||
             nla_put_be16(skb, TCA_VLAN_PUSH_VLAN_PROTOCOL,
-                         v->tcfv_push_proto)))
+                         v->tcfv_push_proto) ||
+            (nla_put_u8(skb, TCA_VLAN_PUSH_VLAN_PRIORITY,
+                                             v->tcfv_push_prio))))
                goto nla_put_failure;
 
        tcf_tm_dump(&t, &v->tcf_tm);