[ovs-dev] [PATCH 2/2] datapath: Hash only the part of struct sw_flow_key populated by flow_extract().

Andrew Evans aevans at nicira.com
Tue Apr 26 01:11:19 UTC 2011


Now that struct sw_flow_key's fields have been reordered to put optional
information last, it's possible to limit hashing of flow keys to only the
relevant portion for this flow as a performance optimization. This commit adds
code to compute the length of the flow key when populating it from an skb or
userspace, and flow_hash() uses this to hash only the portion that contains
data.

Suggested-by: Jesse Gross <jesse at nicira.com>
Signed-off-by: Andrew Evans <aevans at nicira.com>
---
 datapath/datapath.c |   25 +++++++++++------
 datapath/flow.c     |   73 +++++++++++++++++++++++++++++++++++++++-----------
 datapath/flow.h     |    8 +++--
 datapath/tunnel.c   |    7 +++-
 4 files changed, 83 insertions(+), 30 deletions(-)

diff --git a/datapath/datapath.c b/datapath/datapath.c
index 5ce77cd..9727f87 100644
--- a/datapath/datapath.c
+++ b/datapath/datapath.c
@@ -276,10 +276,11 @@ void dp_process_received_packet(struct vport *p, struct sk_buff *skb)
 	if (!OVS_CB(skb)->flow) {
 		struct sw_flow_key key;
 		struct tbl_node *flow_node;
+		int key_len;
 		bool is_frag;
 
 		/* Extract flow from 'skb' into 'key'. */
-		error = flow_extract(skb, p->port_no, &key, &is_frag);
+		error = flow_extract(skb, p->port_no, &key, &key_len, &is_frag);
 		if (unlikely(error)) {
 			kfree_skb(skb);
 			return;
@@ -293,7 +294,7 @@ void dp_process_received_packet(struct vport *p, struct sk_buff *skb)
 
 		/* Look up flow. */
 		flow_node = tbl_lookup(rcu_dereference(dp->table), &key,
-					flow_hash(&key), flow_cmp);
+				       flow_hash(&key, key_len), flow_cmp);
 		if (unlikely(!flow_node)) {
 			struct dp_upcall_info upcall;
 
@@ -684,6 +685,7 @@ static int odp_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info)
 	bool is_frag;
 	int len;
 	int err;
+	int key_len;
 
 	err = -EINVAL;
 	if (!a[ODP_PACKET_ATTR_PACKET] || !a[ODP_PACKET_ATTR_ACTIONS] ||
@@ -717,7 +719,7 @@ static int odp_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info)
 	/* Initialize OVS_CB (it came from Netlink so might not be zeroed). */
 	memset(OVS_CB(packet), 0, sizeof(struct ovs_skb_cb));
 
-	err = flow_extract(packet, -1, &key, &is_frag);
+	err = flow_extract(packet, -1, &key, &key_len, &is_frag);
 	if (err)
 		goto err_kfree_skb;
 
@@ -954,12 +956,13 @@ static int odp_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
 	struct tbl *table;
 	u32 hash;
 	int error;
+	int key_len;
 
 	/* Extract key. */
 	error = -EINVAL;
 	if (!a[ODP_FLOW_ATTR_KEY])
 		goto error;
-	error = flow_from_nlattrs(&key, a[ODP_FLOW_ATTR_KEY]);
+	error = flow_from_nlattrs(&key, &key_len, a[ODP_FLOW_ATTR_KEY]);
 	if (error)
 		goto error;
 
@@ -978,7 +981,7 @@ static int odp_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
 	if (!dp)
 		goto error;
 
-	hash = flow_hash(&key);
+	hash = flow_hash(&key, key_len);
 	table = get_table_protected(dp);
 	flow_node = tbl_lookup(table, &key, hash, flow_cmp);
 	if (!flow_node) {
@@ -1090,10 +1093,11 @@ static int odp_flow_cmd_get(struct sk_buff *skb, struct genl_info *info)
 	struct datapath *dp;
 	struct tbl *table;
 	int err;
+	int key_len;
 
 	if (!a[ODP_FLOW_ATTR_KEY])
 		return -EINVAL;
-	err = flow_from_nlattrs(&key, a[ODP_FLOW_ATTR_KEY]);
+	err = flow_from_nlattrs(&key, &key_len, a[ODP_FLOW_ATTR_KEY]);
 	if (err)
 		return err;
 
@@ -1102,7 +1106,8 @@ static int odp_flow_cmd_get(struct sk_buff *skb, struct genl_info *info)
 		return -ENODEV;
 
 	table = get_table_protected(dp);
-	flow_node = tbl_lookup(table, &key, flow_hash(&key), flow_cmp);
+	flow_node = tbl_lookup(table, &key, flow_hash(&key, key_len),
+			       flow_cmp);
 	if (!flow_node)
 		return -ENOENT;
 
@@ -1125,10 +1130,11 @@ static int odp_flow_cmd_del(struct sk_buff *skb, struct genl_info *info)
 	struct datapath *dp;
 	struct tbl *table;
 	int err;
+	int key_len;
 
 	if (!a[ODP_FLOW_ATTR_KEY])
 		return flush_flows(odp_header->dp_ifindex);
-	err = flow_from_nlattrs(&key, a[ODP_FLOW_ATTR_KEY]);
+	err = flow_from_nlattrs(&key, &key_len, a[ODP_FLOW_ATTR_KEY]);
 	if (err)
 		return err;
 
@@ -1137,7 +1143,8 @@ static int odp_flow_cmd_del(struct sk_buff *skb, struct genl_info *info)
  		return -ENODEV;
 
 	table = get_table_protected(dp);
-	flow_node = tbl_lookup(table, &key, flow_hash(&key), flow_cmp);
+	flow_node = tbl_lookup(table, &key, flow_hash(&key, key_len),
+			       flow_cmp);
 	if (!flow_node)
 		return -ENOENT;
 	flow = flow_cast(flow_node);
diff --git a/datapath/flow.c b/datapath/flow.c
index 558ed19..aaf9d99 100644
--- a/datapath/flow.c
+++ b/datapath/flow.c
@@ -101,7 +101,12 @@ u64 flow_used_time(unsigned long flow_jiffies)
 	return cur_ms - idle_ms;
 }
 
-static int parse_ipv6hdr(struct sk_buff *skb, struct sw_flow_key *key)
+#define SW_FLOW_KEY_OFFSET(field)			\
+	offsetof(struct sw_flow_key, field) +		\
+	sizeof(((struct sw_flow_key *)0)->field)
+
+static int parse_ipv6hdr(struct sk_buff *skb, struct sw_flow_key *key,
+			 int *key_len)
 {
 	unsigned int nh_ofs = skb_network_offset(skb);
 	unsigned int nh_len;
@@ -116,10 +121,11 @@ static int parse_ipv6hdr(struct sk_buff *skb, struct sw_flow_key *key)
 	nexthdr = nh->nexthdr;
 	payload_ofs = (u8 *)(nh + 1) - skb->data;
 
+	key->nw_proto = NEXTHDR_NONE;
+	key->nw_tos = ipv6_get_dsfield(nh) & ~INET_ECN_MASK;
 	ipv6_addr_copy(&key->ipv6.src, &nh->saddr);
 	ipv6_addr_copy(&key->ipv6.dst, &nh->daddr);
-	key->nw_tos = ipv6_get_dsfield(nh) & ~INET_ECN_MASK;
-	key->nw_proto = NEXTHDR_NONE;
+	*key_len = SW_FLOW_KEY_OFFSET(ipv6.dst);
 
 	payload_ofs = ipv6_skip_exthdr(skb, payload_ofs, &nexthdr);
 	if (unlikely(payload_ofs < 0))
@@ -304,15 +310,17 @@ static __be16 parse_ethertype(struct sk_buff *skb)
 }
 
 static int parse_icmpv6(struct sk_buff *skb, struct sw_flow_key *key,
-			int nh_len)
+			int *key_len, int nh_len)
 {
 	struct icmp6hdr *icmp = icmp6_hdr(skb);
+	int saved_key_len = *key_len;
 
 	/* The ICMPv6 type and code fields use the 16-bit transport port
 	 * fields, so we need to store them in 16-bit network byte order.
 	 */
 	key->ipv6.tp.src = htons(icmp->icmp6_type);
 	key->ipv6.tp.dst = htons(icmp->icmp6_code);
+	*key_len = SW_FLOW_KEY_OFFSET(ipv6.tp.dst);
 
 	if (icmp->icmp6_code == 0 &&
 	    (icmp->icmp6_type == NDISC_NEIGHBOUR_SOLICITATION ||
@@ -331,6 +339,7 @@ static int parse_icmpv6(struct sk_buff *skb, struct sw_flow_key *key,
 
 		nd = (struct nd_msg *)skb_transport_header(skb);
 		ipv6_addr_copy(&key->ipv6.nd_target, &nd->target);
+		*key_len = SW_FLOW_KEY_OFFSET(ipv6.nd_target);
 
 		icmp_len -= sizeof(*nd);
 		offset = 0;
@@ -351,12 +360,14 @@ static int parse_icmpv6(struct sk_buff *skb, struct sw_flow_key *key,
 					goto invalid;
 				memcpy(key->ipv6.nd_sha,
 				    &nd->opt[offset+sizeof(*nd_opt)], ETH_ALEN);
+				*key_len = SW_FLOW_KEY_OFFSET(ipv6.nd_sha);
 			} else if (nd_opt->nd_opt_type == ND_OPT_TARGET_LL_ADDR
 				   && opt_len == 8) {
 				if (unlikely(!is_zero_ether_addr(key->ipv6.nd_tha)))
 					goto invalid;
 				memcpy(key->ipv6.nd_tha,
 				    &nd->opt[offset+sizeof(*nd_opt)], ETH_ALEN);
+				*key_len = SW_FLOW_KEY_OFFSET(ipv6.nd_tha);
 			}
 
 			icmp_len -= opt_len;
@@ -370,6 +381,7 @@ invalid:
 	memset(&key->ipv6.nd_target, 0, sizeof(key->ipv6.nd_target));
 	memset(key->ipv6.nd_sha, 0, sizeof(key->ipv6.nd_sha));
 	memset(key->ipv6.nd_tha, 0, sizeof(key->ipv6.nd_tha));
+	*key_len = saved_key_len;
 
 	return 0;
 }
@@ -380,6 +392,7 @@ invalid:
  * Ethernet header
  * @in_port: port number on which @skb was received.
  * @key: output flow key
+ * @key_len: length of output flow key
  * @is_frag: set to 1 if @skb contains an IPv4 fragment, or to 0 if @skb does
  * not contain an IPv4 packet or if it is not a fragment.
  *
@@ -400,13 +413,14 @@ invalid:
  *      For other key->dl_type values it is left untouched.
  */
 int flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key,
-		 bool *is_frag)
+		int *key_len, bool *is_frag)
 {
 	struct ethhdr *eth;
 
 	memset(key, 0, sizeof(*key));
 	key->tun_id = OVS_CB(skb)->tun_id;
 	key->in_port = in_port;
+	*key_len = SW_FLOW_KEY_OFFSET(in_port);
 	*is_frag = false;
 
 	/*
@@ -446,6 +460,7 @@ int flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key,
 		parse_vlan(skb, key);
 
 	key->dl_type = parse_ethertype(skb);
+	*key_len = SW_FLOW_KEY_OFFSET(dl_type);
 	skb_reset_network_header(skb);
 	__skb_push(skb, skb->data - (unsigned char *)eth);
 
@@ -468,6 +483,7 @@ int flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key,
 		key->ipv4.dst = nh->daddr;
 		key->nw_tos = nh->tos & ~INET_ECN_MASK;
 		key->nw_proto = nh->protocol;
+		*key_len = SW_FLOW_KEY_OFFSET(ipv4.dst);
 
 		/* Transport layer. */
 		if (!(nh->frag_off & htons(IP_MF | IP_OFFSET)) &&
@@ -477,12 +493,14 @@ int flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key,
 					struct tcphdr *tcp = tcp_hdr(skb);
 					key->ipv4.tp.src = tcp->source;
 					key->ipv4.tp.dst = tcp->dest;
+					*key_len = SW_FLOW_KEY_OFFSET(ipv4.tp.dst);
 				}
 			} else if (key->nw_proto == IPPROTO_UDP) {
 				if (udphdr_ok(skb)) {
 					struct udphdr *udp = udp_hdr(skb);
 					key->ipv4.tp.src = udp->source;
 					key->ipv4.tp.dst = udp->dest;
+					*key_len = SW_FLOW_KEY_OFFSET(ipv4.tp.dst);
 				}
 			} else if (key->nw_proto == IPPROTO_ICMP) {
 				if (icmphdr_ok(skb)) {
@@ -492,6 +510,7 @@ int flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key,
 					 * in 16-bit network byte order. */
 					key->ipv4.tp.src = htons(icmp->type);
 					key->ipv4.tp.dst = htons(icmp->code);
+					*key_len = SW_FLOW_KEY_OFFSET(ipv4.tp.dst);
 				}
 			}
 		} else
@@ -508,8 +527,10 @@ int flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key,
 				&& arp->ar_pln == 4) {
 
 			/* We only match on the lower 8 bits of the opcode. */
-			if (ntohs(arp->ar_op) <= 0xff)
+			if (ntohs(arp->ar_op) <= 0xff) {
 				key->nw_proto = ntohs(arp->ar_op);
+				*key_len = SW_FLOW_KEY_OFFSET(nw_proto);
+			}
 
 			if (key->nw_proto == ARPOP_REQUEST
 					|| key->nw_proto == ARPOP_REPLY) {
@@ -517,12 +538,13 @@ int flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key,
 				memcpy(&key->ipv4.dst, arp->ar_tip, sizeof(key->ipv4.dst));
 				memcpy(key->ipv4.arp.sha, arp->ar_sha, ETH_ALEN);
 				memcpy(key->ipv4.arp.tha, arp->ar_tha, ETH_ALEN);
+				*key_len = SW_FLOW_KEY_OFFSET(ipv4.arp.tha);
 			}
 		}
 	} else if (key->dl_type == htons(ETH_P_IPV6)) {
 		int nh_len;             /* IPv6 Header + Extensions */
 
-		nh_len = parse_ipv6hdr(skb, key);
+		nh_len = parse_ipv6hdr(skb, key, key_len);
 		if (unlikely(nh_len < 0)) {
 			if (nh_len == -EINVAL) {
 				skb->transport_header = skb->network_header;
@@ -537,16 +559,19 @@ int flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key,
 				struct tcphdr *tcp = tcp_hdr(skb);
 				key->ipv6.tp.src = tcp->source;
 				key->ipv6.tp.dst = tcp->dest;
+				*key_len = SW_FLOW_KEY_OFFSET(ipv6.tp.dst);
 			}
 		} else if (key->nw_proto == NEXTHDR_UDP) {
 			if (udphdr_ok(skb)) {
 				struct udphdr *udp = udp_hdr(skb);
 				key->ipv6.tp.src = udp->source;
 				key->ipv6.tp.dst = udp->dest;
+				*key_len = SW_FLOW_KEY_OFFSET(ipv6.tp.dst);
 			}
 		} else if (key->nw_proto == NEXTHDR_ICMP) {
 			if (icmp6hdr_ok(skb)) {
-				int error = parse_icmpv6(skb, key, nh_len);
+				int error = parse_icmpv6(skb, key, key_len,
+							 nh_len);
 				if (error < 0)
 					return error;
 			}
@@ -555,9 +580,9 @@ int flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key,
 	return 0;
 }
 
-u32 flow_hash(const struct sw_flow_key *key)
+u32 flow_hash(const struct sw_flow_key *key, int key_len)
 {
-	return jhash2((u32*)key, sizeof(*key) / sizeof(u32), hash_seed);
+	return jhash2((u32*)key, (key_len + sizeof(u32) - 1) / sizeof(u32), hash_seed);
 }
 
 int flow_cmp(const struct tbl_node *node, void *key2_)
@@ -571,7 +596,8 @@ int flow_cmp(const struct tbl_node *node, void *key2_)
 /**
  * flow_from_nlattrs - parses Netlink attributes into a flow key.
  * @swkey: receives the extracted flow key.
- * @key: Netlink attribute holding nested %ODP_KEY_ATTR_* Netlink attribute
+ * @key_len: number of bytes used in @swkey.
+ * @attr: Netlink attribute holding nested %ODP_KEY_ATTR_* Netlink attribute
  * sequence.
  *
  * This state machine accepts the following forms, with [] for optional
@@ -580,13 +606,15 @@ int flow_cmp(const struct tbl_node *node, void *key2_)
  * [tun_id] in_port ethernet [8021q] [ethertype \
  *              [IPv4 [TCP|UDP|ICMP] | IPv6 [TCP|UDP|ICMPv6 [ND]] | ARP]]
  */
-int flow_from_nlattrs(struct sw_flow_key *swkey, const struct nlattr *attr)
+int flow_from_nlattrs(struct sw_flow_key *swkey, int *key_len,
+		      const struct nlattr *attr)
 {
 	const struct nlattr *nla;
 	u16 prev_type;
 	int rem;
 
 	memset(swkey, 0, sizeof(*swkey));
+	*key_len = 0;
 	swkey->dl_type = htons(ETH_P_802_2);
 
 	prev_type = ODP_KEY_ATTR_UNSPEC;
@@ -663,10 +691,10 @@ int flow_from_nlattrs(struct sw_flow_key *swkey, const struct nlattr *attr)
 			if (swkey->dl_type != htons(ETH_P_IP))
 				return -EINVAL;
 			ipv4_key = nla_data(nla);
-			swkey->ipv4.src = ipv4_key->ipv4_src;
-			swkey->ipv4.dst = ipv4_key->ipv4_dst;
 			swkey->nw_proto = ipv4_key->ipv4_proto;
 			swkey->nw_tos = ipv4_key->ipv4_tos;
+			swkey->ipv4.src = ipv4_key->ipv4_src;
+			swkey->ipv4.dst = ipv4_key->ipv4_dst;
 			if (swkey->nw_tos & INET_ECN_MASK)
 				return -EINVAL;
 			break;
@@ -675,12 +703,12 @@ int flow_from_nlattrs(struct sw_flow_key *swkey, const struct nlattr *attr)
 			if (swkey->dl_type != htons(ETH_P_IPV6))
 				return -EINVAL;
 			ipv6_key = nla_data(nla);
+			swkey->nw_proto = ipv6_key->ipv6_proto;
+			swkey->nw_tos = ipv6_key->ipv6_tos;
 			memcpy(&swkey->ipv6.src, ipv6_key->ipv6_src,
 					sizeof(swkey->ipv6.src));
 			memcpy(&swkey->ipv6.dst, ipv6_key->ipv6_dst,
 					sizeof(swkey->ipv6.dst));
-			swkey->nw_proto = ipv6_key->ipv6_proto;
-			swkey->nw_tos = ipv6_key->ipv6_tos;
 			if (swkey->nw_tos & INET_ECN_MASK)
 				return -EINVAL;
 			break;
@@ -691,6 +719,7 @@ int flow_from_nlattrs(struct sw_flow_key *swkey, const struct nlattr *attr)
 			tcp_key = nla_data(nla);
 			swkey->ipv4.tp.src = tcp_key->tcp_src;
 			swkey->ipv4.tp.dst = tcp_key->tcp_dst;
+			*key_len = SW_FLOW_KEY_OFFSET(ipv4.tp.dst);
 			break;
 
 		case TRANSITION(ODP_KEY_ATTR_IPV6, ODP_KEY_ATTR_TCP):
@@ -699,6 +728,7 @@ int flow_from_nlattrs(struct sw_flow_key *swkey, const struct nlattr *attr)
 			tcp_key = nla_data(nla);
 			swkey->ipv6.tp.src = tcp_key->tcp_src;
 			swkey->ipv6.tp.dst = tcp_key->tcp_dst;
+			*key_len = SW_FLOW_KEY_OFFSET(ipv6.tp.dst);
 			break;
 
 		case TRANSITION(ODP_KEY_ATTR_IPV4, ODP_KEY_ATTR_UDP):
@@ -707,6 +737,7 @@ int flow_from_nlattrs(struct sw_flow_key *swkey, const struct nlattr *attr)
 			udp_key = nla_data(nla);
 			swkey->ipv4.tp.src = udp_key->udp_src;
 			swkey->ipv4.tp.dst = udp_key->udp_dst;
+			*key_len = SW_FLOW_KEY_OFFSET(ipv4.tp.dst);
 			break;
 
 		case TRANSITION(ODP_KEY_ATTR_IPV6, ODP_KEY_ATTR_UDP):
@@ -715,6 +746,7 @@ int flow_from_nlattrs(struct sw_flow_key *swkey, const struct nlattr *attr)
 			udp_key = nla_data(nla);
 			swkey->ipv6.tp.src = udp_key->udp_src;
 			swkey->ipv6.tp.dst = udp_key->udp_dst;
+			*key_len = SW_FLOW_KEY_OFFSET(ipv6.tp.dst);
 			break;
 
 		case TRANSITION(ODP_KEY_ATTR_IPV4, ODP_KEY_ATTR_ICMP):
@@ -723,6 +755,7 @@ int flow_from_nlattrs(struct sw_flow_key *swkey, const struct nlattr *attr)
 			icmp_key = nla_data(nla);
 			swkey->ipv4.tp.src = htons(icmp_key->icmp_type);
 			swkey->ipv4.tp.dst = htons(icmp_key->icmp_code);
+			*key_len = SW_FLOW_KEY_OFFSET(ipv4.tp.dst);
 			break;
 
 		case TRANSITION(ODP_KEY_ATTR_IPV6, ODP_KEY_ATTR_ICMPV6):
@@ -744,6 +777,7 @@ int flow_from_nlattrs(struct sw_flow_key *swkey, const struct nlattr *attr)
 			swkey->nw_proto = ntohs(arp_key->arp_op);
 			memcpy(swkey->ipv4.arp.sha, arp_key->arp_sha, ETH_ALEN);
 			memcpy(swkey->ipv4.arp.tha, arp_key->arp_tha, ETH_ALEN);
+			*key_len = SW_FLOW_KEY_OFFSET(ipv4.arp.tha);
 			break;
 
 		case TRANSITION(ODP_KEY_ATTR_ICMPV6, ODP_KEY_ATTR_ND):
@@ -755,6 +789,7 @@ int flow_from_nlattrs(struct sw_flow_key *swkey, const struct nlattr *attr)
 					sizeof(swkey->ipv6.nd_target));
 			memcpy(swkey->ipv6.nd_sha, nd_key->nd_sll, ETH_ALEN);
 			memcpy(swkey->ipv6.nd_tha, nd_key->nd_tll, ETH_ALEN);
+			*key_len = SW_FLOW_KEY_OFFSET(ipv6.nd_tha);
 			break;
 
 		default:
@@ -776,12 +811,14 @@ int flow_from_nlattrs(struct sw_flow_key *swkey, const struct nlattr *attr)
 
 	case ODP_KEY_ATTR_ETHERNET:
 	case ODP_KEY_ATTR_8021Q:
+		*key_len = SW_FLOW_KEY_OFFSET(dl_type);
 		return 0;
 
 	case ODP_KEY_ATTR_ETHERTYPE:
 		if (swkey->dl_type == htons(ETH_P_IP) ||
 		    swkey->dl_type == htons(ETH_P_ARP))
 			return -EINVAL;
+		*key_len = SW_FLOW_KEY_OFFSET(dl_type);
 		return 0;
 
 	case ODP_KEY_ATTR_IPV4:
@@ -789,6 +826,7 @@ int flow_from_nlattrs(struct sw_flow_key *swkey, const struct nlattr *attr)
 		    swkey->nw_proto == IPPROTO_UDP ||
 		    swkey->nw_proto == IPPROTO_ICMP)
 			return -EINVAL;
+		*key_len = SW_FLOW_KEY_OFFSET(ipv4.dst);
 		return 0;
 
 	case ODP_KEY_ATTR_IPV6:
@@ -796,12 +834,14 @@ int flow_from_nlattrs(struct sw_flow_key *swkey, const struct nlattr *attr)
 		    swkey->nw_proto == IPPROTO_UDP ||
 		    swkey->nw_proto == IPPROTO_ICMPV6)
 			return -EINVAL;
+		*key_len = SW_FLOW_KEY_OFFSET(ipv6.dst);
 		return 0;
 
 	case ODP_KEY_ATTR_ICMPV6:
 		if (swkey->ipv6.tp.src == htons(NDISC_NEIGHBOUR_SOLICITATION) ||
 		    swkey->ipv6.tp.src == htons(NDISC_NEIGHBOUR_ADVERTISEMENT))
 			return -EINVAL;
+		*key_len = SW_FLOW_KEY_OFFSET(ipv6.tp.dst);
 		return 0;
 
 	case ODP_KEY_ATTR_TCP:
@@ -809,6 +849,7 @@ int flow_from_nlattrs(struct sw_flow_key *swkey, const struct nlattr *attr)
 	case ODP_KEY_ATTR_ICMP:
 	case ODP_KEY_ATTR_ARP:
 	case ODP_KEY_ATTR_ND:
+		/* *key_len already set above */
 		return 0;
 	}
 
diff --git a/datapath/flow.h b/datapath/flow.h
index a7c1f69..dc4e8da 100644
--- a/datapath/flow.h
+++ b/datapath/flow.h
@@ -122,11 +122,12 @@ void flow_deferred_free_acts(struct sw_flow_actions *);
 void flow_hold(struct sw_flow *);
 void flow_put(struct sw_flow *);
 
-int flow_extract(struct sk_buff *, u16 in_port, struct sw_flow_key *, bool *is_frag);
+int flow_extract(struct sk_buff *, u16 in_port, struct sw_flow_key *,
+		 int *key_len, bool *is_frag);
 void flow_used(struct sw_flow *, struct sk_buff *);
 u64 flow_used_time(unsigned long flow_jiffies);
 
-u32 flow_hash(const struct sw_flow_key *);
+u32 flow_hash(const struct sw_flow_key *, int key_len);
 int flow_cmp(const struct tbl_node *, void *target);
 
 /* Upper bound on the length of a nlattr-formatted flow key.  The longest
@@ -148,7 +149,8 @@ int flow_cmp(const struct tbl_node *, void *target);
 #define FLOW_BUFSIZE 132
 
 int flow_to_nlattrs(const struct sw_flow_key *, struct sk_buff *);
-int flow_from_nlattrs(struct sw_flow_key *swkey, const struct nlattr *);
+int flow_from_nlattrs(struct sw_flow_key *swkey, int *key_len,
+		      const struct nlattr *);
 
 static inline struct sw_flow *flow_cast(const struct tbl_node *node)
 {
diff --git a/datapath/tunnel.c b/datapath/tunnel.c
index 899d1cd..5754d92 100644
--- a/datapath/tunnel.c
+++ b/datapath/tunnel.c
@@ -938,6 +938,7 @@ static struct tnl_cache *build_cache(struct vport *vport,
 		struct sk_buff *skb;
 		bool is_frag;
 		int err;
+		int flow_key_len;
 
 		dst_vport = internal_dev_get_vport(rt_dst(rt).dev);
 		if (!dst_vport)
@@ -950,14 +951,16 @@ static struct tnl_cache *build_cache(struct vport *vport,
 		__skb_put(skb, cache->len);
 		memcpy(skb->data, get_cached_header(cache), cache->len);
 
-		err = flow_extract(skb, dst_vport->port_no, &flow_key, &is_frag);
+		err = flow_extract(skb, dst_vport->port_no, &flow_key,
+				   &flow_key_len, &is_frag);
 
 		kfree_skb(skb);
 		if (err || is_frag)
 			goto done;
 
 		flow_node = tbl_lookup(rcu_dereference(dst_vport->dp->table),
-				       &flow_key, flow_hash(&flow_key),
+				       &flow_key,
+				       flow_hash(&flow_key, flow_key_len),
 				       flow_cmp);
 		if (flow_node) {
 			struct sw_flow *flow = flow_cast(flow_node);
-- 
1.7.4.1




More information about the dev mailing list