From 5eef715f2ecdd4d78240c7c484a057b2a7a7799a Mon Sep 17 00:00:00 2001
From: Corey Hickey <bugfood-ml@fatooh.org>
Date: Tue, 15 Jan 2008 19:53:48 -0800
Subject: [PATCH 09/10] Support non-conntrack hash types.

Signed-off-by: Corey Hickey <bugfood-ml@fatooh.org>
---
 include/linux/pkt_sched.h |    9 ++++++
 net/sched/sch_sfq.c       |   68 +++++++++++++++++++++++++++++++++++----------
 2 files changed, 62 insertions(+), 15 deletions(-)

diff --git a/include/linux/pkt_sched.h b/include/linux/pkt_sched.h
index b1a1a52..f75907f 100644
--- a/include/linux/pkt_sched.h
+++ b/include/linux/pkt_sched.h
@@ -157,11 +157,20 @@ enum
 	TCA_SFQ_LIMIT,
 	TCA_SFQ_DIVISOR,
 	TCA_SFQ_FLOWS,
+	TCA_SFQ_HASH,
 	__TCA_SFQ_MAX,
 };
 
 #define TCA_SFQ_MAX (__TCA_SFQ_MAX - 1)
 
+enum
+{
+        TCA_SFQ_HASH_CLASSIC,
+        TCA_SFQ_HASH_DST,
+        TCA_SFQ_HASH_SRC,
+        TCA_SFQ_HASH_FWMARK,
+};
+
 /* RED section */
 
 enum
diff --git a/net/sched/sch_sfq.c b/net/sched/sch_sfq.c
index 51789bc..9fb6d4b 100644
--- a/net/sched/sch_sfq.c
+++ b/net/sched/sch_sfq.c
@@ -23,6 +23,7 @@
 #include <net/ip.h>
 #include <net/netlink.h>
 #include <net/pkt_sched.h>
+#include <linux/jhash.h>
 
 
 /*	Stochastic Fairness Queuing algorithm.
@@ -95,6 +96,7 @@ struct sfq_sched_data
 	int		limit;
 	unsigned	depth;
 	unsigned	hash_divisor;
+	unsigned	hash_kind;
 
 /* Variables */
 	struct timer_list perturb_timer;
@@ -110,23 +112,28 @@ struct sfq_sched_data
 	struct sfq_head	*dep;			/* Linked list of slots, indexed by depth */
 };
 
-static __inline__ unsigned sfq_fold_hash(struct sfq_sched_data *q, u32 h, u32 h1)
+/* This contains the info we will hash. */
+struct sfq_packet_info
 {
-	unsigned mask = (1<<q->hash_divisor) - 1;
+	u32     proto;          /* protocol or port */
+	u32     src;            /* source from packet header */
+	u32     dst;            /* destination from packet header */
+	u32     mark;           /* netfilter mark (fwmark) */
+};
 
-	return jhash_2words(h, h1, q->perturbation) & mask;
-}
 
 static unsigned sfq_hash(struct sfq_sched_data *q, struct sk_buff *skb)
 {
-	u32 h, h2;
+	struct sfq_packet_info info;
+	u32 pert = q->perturbation;
+	unsigned mask = (1<<q->hash_divisor) - 1;
 
 	switch (skb->protocol) {
 	case __constant_htons(ETH_P_IP):
 	{
 		const struct iphdr *iph = ip_hdr(skb);
-		h = iph->daddr;
-		h2 = iph->saddr^iph->protocol;
+		info.dst = iph->daddr;
+		info.src = iph->saddr;
 		if (!(iph->frag_off&htons(IP_MF|IP_OFFSET)) &&
 		    (iph->protocol == IPPROTO_TCP ||
 		     iph->protocol == IPPROTO_UDP ||
@@ -134,28 +141,54 @@ static unsigned sfq_hash(struct sfq_sched_data *q, struct sk_buff *skb)
 		     iph->protocol == IPPROTO_SCTP ||
 		     iph->protocol == IPPROTO_DCCP ||
 		     iph->protocol == IPPROTO_ESP))
-			h2 ^= *(((u32*)iph) + iph->ihl);
+			info.proto = *(((u32*)iph) + iph->ihl);
+		else
+			info.proto = iph->protocol;
 		break;
 	}
 	case __constant_htons(ETH_P_IPV6):
 	{
 		struct ipv6hdr *iph = ipv6_hdr(skb);
-		h = iph->daddr.s6_addr32[3];
-		h2 = iph->saddr.s6_addr32[3]^iph->nexthdr;
+		/* Hash ipv6 addresses into a u32. This isn't ideal,
+		* but the code is simple. */
+		info.dst = jhash2(iph->daddr.s6_addr32, 4, q->perturbation);
+		info.src = jhash2(iph->saddr.s6_addr32, 4, q->perturbation);
 		if (iph->nexthdr == IPPROTO_TCP ||
 		    iph->nexthdr == IPPROTO_UDP ||
 		    iph->nexthdr == IPPROTO_UDPLITE ||
 		    iph->nexthdr == IPPROTO_SCTP ||
 		    iph->nexthdr == IPPROTO_DCCP ||
 		    iph->nexthdr == IPPROTO_ESP)
-			h2 ^= *(u32*)&iph[1];
+		    info.proto = *(u32*)&iph[1];
+		else
+			info.proto = iph->nexthdr;
 		break;
 	}
 	default:
-		h = (u32)(unsigned long)skb->dst^skb->protocol;
-		h2 = (u32)(unsigned long)skb->sk;
+		info.dst   = (u32)(unsigned long)skb->dst;
+		info.src   = (u32)(unsigned long)skb->sk;
+		info.proto = skb->protocol;
+	}
+
+	info.mark = skb->mark;
+
+	switch (q->hash_kind) {
+	case TCA_SFQ_HASH_CLASSIC:
+		return jhash_3words(info.dst, info.src, info.proto, pert) & mask;
+	case TCA_SFQ_HASH_DST:
+		return jhash_1word(info.dst, pert) & mask;
+	case TCA_SFQ_HASH_SRC:
+		return jhash_1word(info.src, pert) & mask;
+	case TCA_SFQ_HASH_FWMARK:
+		return jhash_1word(info.mark, pert) & mask;
 	}
-	return sfq_fold_hash(q, h, h2);
+	/* sfq_q_init makes sure the hash is known,
+	 * so this should never happen */
+	if (net_ratelimit())
+		printk(KERN_WARNING "SFQ: Unknown hash method. "
+		                    "Falling back to classic.\n");
+	q->hash_kind = TCA_SFQ_HASH_CLASSIC;
+	return jhash_3words(info.dst, info.src, info.proto, pert) & mask;
 }
 
 static inline void sfq_link(struct sfq_sched_data *q, sfq_index x)
@@ -443,6 +476,7 @@ sfq_default_parameters(struct Qdisc *sch)
 	q->hash_divisor   = SFQ_DIVISOR_DEFAULT;
 	q->depth          = SFQ_DEPTH_DEFAULT;
 	q->limit          = SFQ_DEPTH_DEFAULT - 1;
+	q->hash_kind      = TCA_SFQ_HASH_CLASSIC;
 }
 
 static void
@@ -454,6 +488,7 @@ sfq_copy_parameters(struct sfq_sched_data *dst, struct sfq_sched_data *src)
 	dst->hash_divisor   = src->hash_divisor;
 	dst->limit          = src->limit;
 	dst->depth          = src->depth;
+	dst->hash_kind      = src->hash_kind;
 }
 
 /* SFQ parameters exist as individual rtattr attributes, with a nested
@@ -505,7 +540,9 @@ sfq_q_init(struct sfq_sched_data *q, struct rtattr *opt)
 		    sfq_get_parameter(&(q->depth),          tb, TCA_SFQ_FLOWS,
 				ctl->flows)          ||
 		    sfq_get_parameter(&(q->limit),          tb, TCA_SFQ_LIMIT,
-				ctl->limit))
+				ctl->limit)          ||
+		    sfq_get_parameter(&(q->hash_kind),      tb, TCA_SFQ_HASH,
+		    		0))
 			goto rtattr_failure;
 
 		if (q->depth        > SFQ_MAX_DEPTH ||
@@ -647,6 +684,7 @@ static int sfq_dump(struct Qdisc *sch, struct sk_buff *skb)
 	RTA_PUT_U32(skb, TCA_SFQ_LIMIT,   q->limit);
 	RTA_PUT_U32(skb, TCA_SFQ_DIVISOR, q->hash_divisor);
 	RTA_PUT_U32(skb, TCA_SFQ_FLOWS,   q->depth);
+	RTA_PUT_U32(skb, TCA_SFQ_HASH,    q->hash_kind);
 	RTA_PUT(skb, TCA_OPTIONS, sizeof(opt), &opt);
 
 	RTA_NEST_COMPAT_END(skb, nest);
-- 
1.5.3.8

