@@ -17,6 +17,7 @@ struct tcf_skbedit_params {
u32 mark;
u32 mask;
u16 queue_mapping;
+ u16 mapping_mod;
u16 ptype;
struct rcu_head rcu;
};
@@ -29,6 +29,11 @@
#define SKBEDIT_F_PTYPE 0x8
#define SKBEDIT_F_MASK 0x10
#define SKBEDIT_F_INHERITDSFIELD 0x20
+#define SKBEDIT_F_QUEUE_MAPPING_HASH 0x40
+#define SKBEDIT_F_QUEUE_MAPPING_CLASSID 0x80
+
+#define SKBEDIT_F_QUEUE_MAPPING_HASH_MASK (SKBEDIT_F_QUEUE_MAPPING_HASH | \
+ SKBEDIT_F_QUEUE_MAPPING_CLASSID)
struct tc_skbedit {
tc_gen;
@@ -10,6 +10,7 @@
#include <linux/kernel.h>
#include <linux/skbuff.h>
#include <linux/rtnetlink.h>
+#include <net/cls_cgroup.h>
#include <net/netlink.h>
#include <net/pkt_sched.h>
#include <net/ip.h>
@@ -23,6 +24,25 @@
static unsigned int skbedit_net_id;
static struct tc_action_ops act_skbedit_ops;
+static u16 tcf_skbedit_hash(struct tcf_skbedit_params *params,
+ struct sk_buff *skb)
+{
+ u16 queue_mapping = params->queue_mapping;
+ u16 mapping_mod = params->mapping_mod;
+ u32 hash = 0;
+
+ if (!(params->flags & SKBEDIT_F_QUEUE_MAPPING_HASH_MASK))
+ return netdev_cap_txqueue(skb->dev, queue_mapping);
+
+ if (params->flags & SKBEDIT_F_QUEUE_MAPPING_CLASSID)
+ hash = jhash_1word(task_get_classid(skb), 0);
+ else if (params->flags & SKBEDIT_F_QUEUE_MAPPING_HASH)
+ hash = skb_get_hash(skb);
+
+ queue_mapping= (queue_mapping & 0xff) + hash % mapping_mod;
+ return netdev_cap_txqueue(skb->dev, queue_mapping);
+}
+
static int tcf_skbedit_act(struct sk_buff *skb, const struct tc_action *a,
struct tcf_result *res)
{
@@ -57,10 +77,9 @@ static int tcf_skbedit_act(struct sk_buff *skb, const struct tc_action *a,
break;
}
}
- if (params->flags & SKBEDIT_F_QUEUE_MAPPING &&
- skb->dev->real_num_tx_queues > params->queue_mapping) {
+ if (params->flags & SKBEDIT_F_QUEUE_MAPPING) {
skb->tc_skip_txqueue = 1;
- skb_set_queue_mapping(skb, params->queue_mapping);
+ skb_set_queue_mapping(skb, tcf_skbedit_hash(params, skb));
}
if (params->flags & SKBEDIT_F_MARK) {
skb->mark &= ~params->mask;
@@ -110,6 +129,7 @@ static int tcf_skbedit_init(struct net *net, struct nlattr *nla,
struct tcf_skbedit *d;
u32 flags = 0, *priority = NULL, *mark = NULL, *mask = NULL;
u16 *queue_mapping = NULL, *ptype = NULL;
+ u16 mapping_mod = 0;
bool exists = false;
int ret = 0, err;
u32 index;
@@ -157,6 +177,21 @@ static int tcf_skbedit_init(struct net *net, struct nlattr *nla,
if (*pure_flags & SKBEDIT_F_INHERITDSFIELD)
flags |= SKBEDIT_F_INHERITDSFIELD;
+ if (*pure_flags & SKBEDIT_F_QUEUE_MAPPING_HASH_MASK) {
+ u16 max, min;
+
+ if (!queue_mapping)
+ return -EINVAL;
+
+ max = *queue_mapping >> 8;
+ min = *queue_mapping & 0xff;
+ if (max < min)
+ return -EINVAL;
+
+ mapping_mod = max - min + 1;
+ flags |= *pure_flags &
+ SKBEDIT_F_QUEUE_MAPPING_HASH_MASK;
+ }
}
parm = nla_data(tb[TCA_SKBEDIT_PARMS]);
@@ -206,8 +241,10 @@ static int tcf_skbedit_init(struct net *net, struct nlattr *nla,
params_new->flags = flags;
if (flags & SKBEDIT_F_PRIORITY)
params_new->priority = *priority;
- if (flags & SKBEDIT_F_QUEUE_MAPPING)
+ if (flags & SKBEDIT_F_QUEUE_MAPPING) {
params_new->queue_mapping = *queue_mapping;
+ params_new->mapping_mod = mapping_mod;
+ }
if (flags & SKBEDIT_F_MARK)
params_new->mark = *mark;
if (flags & SKBEDIT_F_PTYPE)
@@ -274,6 +311,9 @@ static int tcf_skbedit_dump(struct sk_buff *skb, struct tc_action *a,
goto nla_put_failure;
if (params->flags & SKBEDIT_F_INHERITDSFIELD)
pure_flags |= SKBEDIT_F_INHERITDSFIELD;
+ if (params->flags & SKBEDIT_F_QUEUE_MAPPING_HASH_MASK)
+ pure_flags |= params->flags &
+ SKBEDIT_F_QUEUE_MAPPING_HASH_MASK;
if (pure_flags != 0 &&
nla_put(skb, TCA_SKBEDIT_FLAGS, sizeof(pure_flags), &pure_flags))
goto nla_put_failure;