aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPravin B Shelar <pshelar@nicira.com>2013-10-29 20:22:21 -0400
committerJesse Gross <jesse@nicira.com>2014-01-06 18:52:24 -0500
commite298e505700604c97e6a9edb21cebb080bdb91f6 (patch)
treed2f4452ed7a4ec42a212690ac2633b006c1e10c0
parent795449d8b846a42d11d47d6ff2f51ab2967411c3 (diff)
openvswitch: Per cpu flow stats.
With mega flow implementation ovs flow can be shared between multiple CPUs which makes stats updates highly contended operation. This patch uses per-CPU stats in cases where a flow is likely to be shared (if there is a wildcard in the 5-tuple and therefore likely to be spread by RSS). In other situations, it uses the current strategy, saving memory and allocation time. Signed-off-by: Pravin B Shelar <pshelar@nicira.com> Signed-off-by: Jesse Gross <jesse@nicira.com>
-rw-r--r--net/openvswitch/datapath.c50
-rw-r--r--net/openvswitch/flow.c96
-rw-r--r--net/openvswitch/flow.h29
-rw-r--r--net/openvswitch/flow_netlink.c56
-rw-r--r--net/openvswitch/flow_netlink.h1
-rw-r--r--net/openvswitch/flow_table.c31
-rw-r--r--net/openvswitch/flow_table.h2
7 files changed, 210 insertions, 55 deletions
diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c
index 5da2534b140a..50d7782d8d7c 100644
--- a/net/openvswitch/datapath.c
+++ b/net/openvswitch/datapath.c
@@ -251,9 +251,9 @@ void ovs_dp_process_received_packet(struct vport *p, struct sk_buff *skb)
251 OVS_CB(skb)->flow = flow; 251 OVS_CB(skb)->flow = flow;
252 OVS_CB(skb)->pkt_key = &key; 252 OVS_CB(skb)->pkt_key = &key;
253 253
254 stats_counter = &stats->n_hit; 254 ovs_flow_stats_update(OVS_CB(skb)->flow, skb);
255 ovs_flow_used(OVS_CB(skb)->flow, skb);
256 ovs_execute_actions(dp, skb); 255 ovs_execute_actions(dp, skb);
256 stats_counter = &stats->n_hit;
257 257
258out: 258out:
259 /* Update datapath statistics. */ 259 /* Update datapath statistics. */
@@ -459,14 +459,6 @@ out:
459 return err; 459 return err;
460} 460}
461 461
462static void clear_stats(struct sw_flow *flow)
463{
464 flow->used = 0;
465 flow->tcp_flags = 0;
466 flow->packet_count = 0;
467 flow->byte_count = 0;
468}
469
470static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info) 462static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info)
471{ 463{
472 struct ovs_header *ovs_header = info->userhdr; 464 struct ovs_header *ovs_header = info->userhdr;
@@ -505,7 +497,7 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info)
505 packet->protocol = htons(ETH_P_802_2); 497 packet->protocol = htons(ETH_P_802_2);
506 498
507 /* Build an sw_flow for sending this packet. */ 499 /* Build an sw_flow for sending this packet. */
508 flow = ovs_flow_alloc(); 500 flow = ovs_flow_alloc(false);
509 err = PTR_ERR(flow); 501 err = PTR_ERR(flow);
510 if (IS_ERR(flow)) 502 if (IS_ERR(flow))
511 goto err_kfree_skb; 503 goto err_kfree_skb;
@@ -641,10 +633,10 @@ static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp,
641 const int skb_orig_len = skb->len; 633 const int skb_orig_len = skb->len;
642 struct nlattr *start; 634 struct nlattr *start;
643 struct ovs_flow_stats stats; 635 struct ovs_flow_stats stats;
636 __be16 tcp_flags;
637 unsigned long used;
644 struct ovs_header *ovs_header; 638 struct ovs_header *ovs_header;
645 struct nlattr *nla; 639 struct nlattr *nla;
646 unsigned long used;
647 u8 tcp_flags;
648 int err; 640 int err;
649 641
650 ovs_header = genlmsg_put(skb, portid, seq, &dp_flow_genl_family, flags, cmd); 642 ovs_header = genlmsg_put(skb, portid, seq, &dp_flow_genl_family, flags, cmd);
@@ -673,24 +665,17 @@ static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp,
673 665
674 nla_nest_end(skb, nla); 666 nla_nest_end(skb, nla);
675 667
676 spin_lock_bh(&flow->lock); 668 ovs_flow_stats_get(flow, &stats, &used, &tcp_flags);
677 used = flow->used;
678 stats.n_packets = flow->packet_count;
679 stats.n_bytes = flow->byte_count;
680 tcp_flags = (u8)ntohs(flow->tcp_flags);
681 spin_unlock_bh(&flow->lock);
682
683 if (used && 669 if (used &&
684 nla_put_u64(skb, OVS_FLOW_ATTR_USED, ovs_flow_used_time(used))) 670 nla_put_u64(skb, OVS_FLOW_ATTR_USED, ovs_flow_used_time(used)))
685 goto nla_put_failure; 671 goto nla_put_failure;
686 672
687 if (stats.n_packets && 673 if (stats.n_packets &&
688 nla_put(skb, OVS_FLOW_ATTR_STATS, 674 nla_put(skb, OVS_FLOW_ATTR_STATS, sizeof(struct ovs_flow_stats), &stats))
689 sizeof(struct ovs_flow_stats), &stats))
690 goto nla_put_failure; 675 goto nla_put_failure;
691 676
692 if (tcp_flags && 677 if ((u8)ntohs(tcp_flags) &&
693 nla_put_u8(skb, OVS_FLOW_ATTR_TCP_FLAGS, tcp_flags)) 678 nla_put_u8(skb, OVS_FLOW_ATTR_TCP_FLAGS, (u8)ntohs(tcp_flags)))
694 goto nla_put_failure; 679 goto nla_put_failure;
695 680
696 /* If OVS_FLOW_ATTR_ACTIONS doesn't fit, skip dumping the actions if 681 /* If OVS_FLOW_ATTR_ACTIONS doesn't fit, skip dumping the actions if
@@ -770,6 +755,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
770 struct datapath *dp; 755 struct datapath *dp;
771 struct sw_flow_actions *acts = NULL; 756 struct sw_flow_actions *acts = NULL;
772 struct sw_flow_match match; 757 struct sw_flow_match match;
758 bool exact_5tuple;
773 int error; 759 int error;
774 760
775 /* Extract key. */ 761 /* Extract key. */
@@ -778,7 +764,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
778 goto error; 764 goto error;
779 765
780 ovs_match_init(&match, &key, &mask); 766 ovs_match_init(&match, &key, &mask);
781 error = ovs_nla_get_match(&match, 767 error = ovs_nla_get_match(&match, &exact_5tuple,
782 a[OVS_FLOW_ATTR_KEY], a[OVS_FLOW_ATTR_MASK]); 768 a[OVS_FLOW_ATTR_KEY], a[OVS_FLOW_ATTR_MASK]);
783 if (error) 769 if (error)
784 goto error; 770 goto error;
@@ -817,12 +803,11 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
817 goto err_unlock_ovs; 803 goto err_unlock_ovs;
818 804
819 /* Allocate flow. */ 805 /* Allocate flow. */
820 flow = ovs_flow_alloc(); 806 flow = ovs_flow_alloc(!exact_5tuple);
821 if (IS_ERR(flow)) { 807 if (IS_ERR(flow)) {
822 error = PTR_ERR(flow); 808 error = PTR_ERR(flow);
823 goto err_unlock_ovs; 809 goto err_unlock_ovs;
824 } 810 }
825 clear_stats(flow);
826 811
827 flow->key = masked_key; 812 flow->key = masked_key;
828 flow->unmasked_key = key; 813 flow->unmasked_key = key;
@@ -866,11 +851,8 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
866 reply = ovs_flow_cmd_build_info(flow, dp, info, OVS_FLOW_CMD_NEW); 851 reply = ovs_flow_cmd_build_info(flow, dp, info, OVS_FLOW_CMD_NEW);
867 852
868 /* Clear stats. */ 853 /* Clear stats. */
869 if (a[OVS_FLOW_ATTR_CLEAR]) { 854 if (a[OVS_FLOW_ATTR_CLEAR])
870 spin_lock_bh(&flow->lock); 855 ovs_flow_stats_clear(flow);
871 clear_stats(flow);
872 spin_unlock_bh(&flow->lock);
873 }
874 } 856 }
875 ovs_unlock(); 857 ovs_unlock();
876 858
@@ -908,7 +890,7 @@ static int ovs_flow_cmd_get(struct sk_buff *skb, struct genl_info *info)
908 } 890 }
909 891
910 ovs_match_init(&match, &key, NULL); 892 ovs_match_init(&match, &key, NULL);
911 err = ovs_nla_get_match(&match, a[OVS_FLOW_ATTR_KEY], NULL); 893 err = ovs_nla_get_match(&match, NULL, a[OVS_FLOW_ATTR_KEY], NULL);
912 if (err) 894 if (err)
913 return err; 895 return err;
914 896
@@ -962,7 +944,7 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info)
962 } 944 }
963 945
964 ovs_match_init(&match, &key, NULL); 946 ovs_match_init(&match, &key, NULL);
965 err = ovs_nla_get_match(&match, a[OVS_FLOW_ATTR_KEY], NULL); 947 err = ovs_nla_get_match(&match, NULL, a[OVS_FLOW_ATTR_KEY], NULL);
966 if (err) 948 if (err)
967 goto unlock; 949 goto unlock;
968 950
diff --git a/net/openvswitch/flow.c b/net/openvswitch/flow.c
index b409f5279601..16f4b46161d4 100644
--- a/net/openvswitch/flow.c
+++ b/net/openvswitch/flow.c
@@ -35,6 +35,7 @@
35#include <linux/ip.h> 35#include <linux/ip.h>
36#include <linux/ipv6.h> 36#include <linux/ipv6.h>
37#include <linux/sctp.h> 37#include <linux/sctp.h>
38#include <linux/smp.h>
38#include <linux/tcp.h> 39#include <linux/tcp.h>
39#include <linux/udp.h> 40#include <linux/udp.h>
40#include <linux/icmp.h> 41#include <linux/icmp.h>
@@ -60,10 +61,16 @@ u64 ovs_flow_used_time(unsigned long flow_jiffies)
60 61
61#define TCP_FLAGS_BE16(tp) (*(__be16 *)&tcp_flag_word(tp) & htons(0x0FFF)) 62#define TCP_FLAGS_BE16(tp) (*(__be16 *)&tcp_flag_word(tp) & htons(0x0FFF))
62 63
63void ovs_flow_used(struct sw_flow *flow, struct sk_buff *skb) 64void ovs_flow_stats_update(struct sw_flow *flow, struct sk_buff *skb)
64{ 65{
66 struct flow_stats *stats;
65 __be16 tcp_flags = 0; 67 __be16 tcp_flags = 0;
66 68
69 if (!flow->stats.is_percpu)
70 stats = flow->stats.stat;
71 else
72 stats = this_cpu_ptr(flow->stats.cpu_stats);
73
67 if ((flow->key.eth.type == htons(ETH_P_IP) || 74 if ((flow->key.eth.type == htons(ETH_P_IP) ||
68 flow->key.eth.type == htons(ETH_P_IPV6)) && 75 flow->key.eth.type == htons(ETH_P_IPV6)) &&
69 flow->key.ip.proto == IPPROTO_TCP && 76 flow->key.ip.proto == IPPROTO_TCP &&
@@ -71,12 +78,87 @@ void ovs_flow_used(struct sw_flow *flow, struct sk_buff *skb)
71 tcp_flags = TCP_FLAGS_BE16(tcp_hdr(skb)); 78 tcp_flags = TCP_FLAGS_BE16(tcp_hdr(skb));
72 } 79 }
73 80
74 spin_lock(&flow->lock); 81 spin_lock(&stats->lock);
75 flow->used = jiffies; 82 stats->used = jiffies;
76 flow->packet_count++; 83 stats->packet_count++;
77 flow->byte_count += skb->len; 84 stats->byte_count += skb->len;
78 flow->tcp_flags |= tcp_flags; 85 stats->tcp_flags |= tcp_flags;
79 spin_unlock(&flow->lock); 86 spin_unlock(&stats->lock);
87}
88
89static void stats_read(struct flow_stats *stats,
90 struct ovs_flow_stats *ovs_stats,
91 unsigned long *used, __be16 *tcp_flags)
92{
93 spin_lock(&stats->lock);
94 if (time_after(stats->used, *used))
95 *used = stats->used;
96 *tcp_flags |= stats->tcp_flags;
97 ovs_stats->n_packets += stats->packet_count;
98 ovs_stats->n_bytes += stats->byte_count;
99 spin_unlock(&stats->lock);
100}
101
102void ovs_flow_stats_get(struct sw_flow *flow, struct ovs_flow_stats *ovs_stats,
103 unsigned long *used, __be16 *tcp_flags)
104{
105 int cpu, cur_cpu;
106
107 *used = 0;
108 *tcp_flags = 0;
109 memset(ovs_stats, 0, sizeof(*ovs_stats));
110
111 if (!flow->stats.is_percpu) {
112 stats_read(flow->stats.stat, ovs_stats, used, tcp_flags);
113 } else {
114 cur_cpu = get_cpu();
115 for_each_possible_cpu(cpu) {
116 struct flow_stats *stats;
117
118 if (cpu == cur_cpu)
119 local_bh_disable();
120
121 stats = per_cpu_ptr(flow->stats.cpu_stats, cpu);
122 stats_read(stats, ovs_stats, used, tcp_flags);
123
124 if (cpu == cur_cpu)
125 local_bh_enable();
126 }
127 put_cpu();
128 }
129}
130
131static void stats_reset(struct flow_stats *stats)
132{
133 spin_lock(&stats->lock);
134 stats->used = 0;
135 stats->packet_count = 0;
136 stats->byte_count = 0;
137 stats->tcp_flags = 0;
138 spin_unlock(&stats->lock);
139}
140
141void ovs_flow_stats_clear(struct sw_flow *flow)
142{
143 int cpu, cur_cpu;
144
145 if (!flow->stats.is_percpu) {
146 stats_reset(flow->stats.stat);
147 } else {
148 cur_cpu = get_cpu();
149
150 for_each_possible_cpu(cpu) {
151
152 if (cpu == cur_cpu)
153 local_bh_disable();
154
155 stats_reset(per_cpu_ptr(flow->stats.cpu_stats, cpu));
156
157 if (cpu == cur_cpu)
158 local_bh_enable();
159 }
160 put_cpu();
161 }
80} 162}
81 163
82static int check_header(struct sk_buff *skb, int len) 164static int check_header(struct sk_buff *skb, int len)
diff --git a/net/openvswitch/flow.h b/net/openvswitch/flow.h
index 176406d9b779..2d770e28a3a3 100644
--- a/net/openvswitch/flow.h
+++ b/net/openvswitch/flow.h
@@ -19,6 +19,7 @@
19#ifndef FLOW_H 19#ifndef FLOW_H
20#define FLOW_H 1 20#define FLOW_H 1
21 21
22#include <linux/cache.h>
22#include <linux/kernel.h> 23#include <linux/kernel.h>
23#include <linux/netlink.h> 24#include <linux/netlink.h>
24#include <linux/openvswitch.h> 25#include <linux/openvswitch.h>
@@ -146,6 +147,22 @@ struct sw_flow_actions {
146 struct nlattr actions[]; 147 struct nlattr actions[];
147}; 148};
148 149
150struct flow_stats {
151 u64 packet_count; /* Number of packets matched. */
152 u64 byte_count; /* Number of bytes matched. */
153 unsigned long used; /* Last used time (in jiffies). */
154 spinlock_t lock; /* Lock for atomic stats update. */
155 __be16 tcp_flags; /* Union of seen TCP flags. */
156};
157
158struct sw_flow_stats {
159 bool is_percpu;
160 union {
161 struct flow_stats *stat;
162 struct flow_stats __percpu *cpu_stats;
163 };
164};
165
149struct sw_flow { 166struct sw_flow {
150 struct rcu_head rcu; 167 struct rcu_head rcu;
151 struct hlist_node hash_node[2]; 168 struct hlist_node hash_node[2];
@@ -155,12 +172,7 @@ struct sw_flow {
155 struct sw_flow_key unmasked_key; 172 struct sw_flow_key unmasked_key;
156 struct sw_flow_mask *mask; 173 struct sw_flow_mask *mask;
157 struct sw_flow_actions __rcu *sf_acts; 174 struct sw_flow_actions __rcu *sf_acts;
158 175 struct sw_flow_stats stats;
159 spinlock_t lock; /* Lock for values below. */
160 unsigned long used; /* Last used time (in jiffies). */
161 u64 packet_count; /* Number of packets matched. */
162 u64 byte_count; /* Number of bytes matched. */
163 __be16 tcp_flags; /* Union of seen TCP flags. */
164}; 176};
165 177
166struct arp_eth_header { 178struct arp_eth_header {
@@ -177,7 +189,10 @@ struct arp_eth_header {
177 unsigned char ar_tip[4]; /* target IP address */ 189 unsigned char ar_tip[4]; /* target IP address */
178} __packed; 190} __packed;
179 191
180void ovs_flow_used(struct sw_flow *, struct sk_buff *); 192void ovs_flow_stats_update(struct sw_flow *flow, struct sk_buff *skb);
193void ovs_flow_stats_get(struct sw_flow *flow, struct ovs_flow_stats *stats,
194 unsigned long *used, __be16 *tcp_flags);
195void ovs_flow_stats_clear(struct sw_flow *flow);
181u64 ovs_flow_used_time(unsigned long flow_jiffies); 196u64 ovs_flow_used_time(unsigned long flow_jiffies);
182 197
183int ovs_flow_extract(struct sk_buff *, u16 in_port, struct sw_flow_key *); 198int ovs_flow_extract(struct sk_buff *, u16 in_port, struct sw_flow_key *);
diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c
index 2bc1bc1aca3b..3ccb92f48502 100644
--- a/net/openvswitch/flow_netlink.c
+++ b/net/openvswitch/flow_netlink.c
@@ -266,6 +266,20 @@ static bool is_all_zero(const u8 *fp, size_t size)
266 return true; 266 return true;
267} 267}
268 268
269static bool is_all_set(const u8 *fp, size_t size)
270{
271 int i;
272
273 if (!fp)
274 return false;
275
276 for (i = 0; i < size; i++)
277 if (fp[i] != 0xff)
278 return false;
279
280 return true;
281}
282
269static int __parse_flow_nlattrs(const struct nlattr *attr, 283static int __parse_flow_nlattrs(const struct nlattr *attr,
270 const struct nlattr *a[], 284 const struct nlattr *a[],
271 u64 *attrsp, bool nz) 285 u64 *attrsp, bool nz)
@@ -487,8 +501,9 @@ static int metadata_from_nlattrs(struct sw_flow_match *match, u64 *attrs,
487 return 0; 501 return 0;
488} 502}
489 503
490static int ovs_key_from_nlattrs(struct sw_flow_match *match, u64 attrs, 504static int ovs_key_from_nlattrs(struct sw_flow_match *match, bool *exact_5tuple,
491 const struct nlattr **a, bool is_mask) 505 u64 attrs, const struct nlattr **a,
506 bool is_mask)
492{ 507{
493 int err; 508 int err;
494 u64 orig_attrs = attrs; 509 u64 orig_attrs = attrs;
@@ -545,6 +560,11 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match, u64 attrs,
545 SW_FLOW_KEY_PUT(match, eth.type, htons(ETH_P_802_2), is_mask); 560 SW_FLOW_KEY_PUT(match, eth.type, htons(ETH_P_802_2), is_mask);
546 } 561 }
547 562
563 if (is_mask && exact_5tuple) {
564 if (match->mask->key.eth.type != htons(0xffff))
565 *exact_5tuple = false;
566 }
567
548 if (attrs & (1 << OVS_KEY_ATTR_IPV4)) { 568 if (attrs & (1 << OVS_KEY_ATTR_IPV4)) {
549 const struct ovs_key_ipv4 *ipv4_key; 569 const struct ovs_key_ipv4 *ipv4_key;
550 570
@@ -567,6 +587,13 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match, u64 attrs,
567 SW_FLOW_KEY_PUT(match, ipv4.addr.dst, 587 SW_FLOW_KEY_PUT(match, ipv4.addr.dst,
568 ipv4_key->ipv4_dst, is_mask); 588 ipv4_key->ipv4_dst, is_mask);
569 attrs &= ~(1 << OVS_KEY_ATTR_IPV4); 589 attrs &= ~(1 << OVS_KEY_ATTR_IPV4);
590
591 if (is_mask && exact_5tuple && *exact_5tuple) {
592 if (ipv4_key->ipv4_proto != 0xff ||
593 ipv4_key->ipv4_src != htonl(0xffffffff) ||
594 ipv4_key->ipv4_dst != htonl(0xffffffff))
595 *exact_5tuple = false;
596 }
570 } 597 }
571 598
572 if (attrs & (1 << OVS_KEY_ATTR_IPV6)) { 599 if (attrs & (1 << OVS_KEY_ATTR_IPV6)) {
@@ -598,6 +625,13 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match, u64 attrs,
598 is_mask); 625 is_mask);
599 626
600 attrs &= ~(1 << OVS_KEY_ATTR_IPV6); 627 attrs &= ~(1 << OVS_KEY_ATTR_IPV6);
628
629 if (is_mask && exact_5tuple && *exact_5tuple) {
630 if (ipv6_key->ipv6_proto != 0xff ||
631 !is_all_set((u8 *)ipv6_key->ipv6_src, sizeof(match->key->ipv6.addr.src)) ||
632 !is_all_set((u8 *)ipv6_key->ipv6_dst, sizeof(match->key->ipv6.addr.dst)))
633 *exact_5tuple = false;
634 }
601 } 635 }
602 636
603 if (attrs & (1 << OVS_KEY_ATTR_ARP)) { 637 if (attrs & (1 << OVS_KEY_ATTR_ARP)) {
@@ -640,6 +674,11 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match, u64 attrs,
640 tcp_key->tcp_dst, is_mask); 674 tcp_key->tcp_dst, is_mask);
641 } 675 }
642 attrs &= ~(1 << OVS_KEY_ATTR_TCP); 676 attrs &= ~(1 << OVS_KEY_ATTR_TCP);
677
678 if (is_mask && exact_5tuple && *exact_5tuple &&
679 (tcp_key->tcp_src != htons(0xffff) ||
680 tcp_key->tcp_dst != htons(0xffff)))
681 *exact_5tuple = false;
643 } 682 }
644 683
645 if (attrs & (1 << OVS_KEY_ATTR_TCP_FLAGS)) { 684 if (attrs & (1 << OVS_KEY_ATTR_TCP_FLAGS)) {
@@ -671,6 +710,11 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match, u64 attrs,
671 udp_key->udp_dst, is_mask); 710 udp_key->udp_dst, is_mask);
672 } 711 }
673 attrs &= ~(1 << OVS_KEY_ATTR_UDP); 712 attrs &= ~(1 << OVS_KEY_ATTR_UDP);
713
714 if (is_mask && exact_5tuple && *exact_5tuple &&
715 (udp_key->udp_src != htons(0xffff) ||
716 udp_key->udp_dst != htons(0xffff)))
717 *exact_5tuple = false;
674 } 718 }
675 719
676 if (attrs & (1 << OVS_KEY_ATTR_SCTP)) { 720 if (attrs & (1 << OVS_KEY_ATTR_SCTP)) {
@@ -756,6 +800,7 @@ static void sw_flow_mask_set(struct sw_flow_mask *mask,
756 * attribute specifies the mask field of the wildcarded flow. 800 * attribute specifies the mask field of the wildcarded flow.
757 */ 801 */
758int ovs_nla_get_match(struct sw_flow_match *match, 802int ovs_nla_get_match(struct sw_flow_match *match,
803 bool *exact_5tuple,
759 const struct nlattr *key, 804 const struct nlattr *key,
760 const struct nlattr *mask) 805 const struct nlattr *mask)
761{ 806{
@@ -803,10 +848,13 @@ int ovs_nla_get_match(struct sw_flow_match *match,
803 } 848 }
804 } 849 }
805 850
806 err = ovs_key_from_nlattrs(match, key_attrs, a, false); 851 err = ovs_key_from_nlattrs(match, NULL, key_attrs, a, false);
807 if (err) 852 if (err)
808 return err; 853 return err;
809 854
855 if (exact_5tuple)
856 *exact_5tuple = true;
857
810 if (mask) { 858 if (mask) {
811 err = parse_flow_mask_nlattrs(mask, a, &mask_attrs); 859 err = parse_flow_mask_nlattrs(mask, a, &mask_attrs);
812 if (err) 860 if (err)
@@ -844,7 +892,7 @@ int ovs_nla_get_match(struct sw_flow_match *match,
844 } 892 }
845 } 893 }
846 894
847 err = ovs_key_from_nlattrs(match, mask_attrs, a, true); 895 err = ovs_key_from_nlattrs(match, exact_5tuple, mask_attrs, a, true);
848 if (err) 896 if (err)
849 return err; 897 return err;
850 } else { 898 } else {
diff --git a/net/openvswitch/flow_netlink.h b/net/openvswitch/flow_netlink.h
index 440151045d39..b31fbe28bc7a 100644
--- a/net/openvswitch/flow_netlink.h
+++ b/net/openvswitch/flow_netlink.h
@@ -45,6 +45,7 @@ int ovs_nla_put_flow(const struct sw_flow_key *,
45int ovs_nla_get_flow_metadata(struct sw_flow *flow, 45int ovs_nla_get_flow_metadata(struct sw_flow *flow,
46 const struct nlattr *attr); 46 const struct nlattr *attr);
47int ovs_nla_get_match(struct sw_flow_match *match, 47int ovs_nla_get_match(struct sw_flow_match *match,
48 bool *exact_5tuple,
48 const struct nlattr *, 49 const struct nlattr *,
49 const struct nlattr *); 50 const struct nlattr *);
50 51
diff --git a/net/openvswitch/flow_table.c b/net/openvswitch/flow_table.c
index 7b9cf2c43813..299ea8bb400c 100644
--- a/net/openvswitch/flow_table.c
+++ b/net/openvswitch/flow_table.c
@@ -72,19 +72,42 @@ void ovs_flow_mask_key(struct sw_flow_key *dst, const struct sw_flow_key *src,
72 *d++ = *s++ & *m++; 72 *d++ = *s++ & *m++;
73} 73}
74 74
75struct sw_flow *ovs_flow_alloc(void) 75struct sw_flow *ovs_flow_alloc(bool percpu_stats)
76{ 76{
77 struct sw_flow *flow; 77 struct sw_flow *flow;
78 int cpu;
78 79
79 flow = kmem_cache_alloc(flow_cache, GFP_KERNEL); 80 flow = kmem_cache_alloc(flow_cache, GFP_KERNEL);
80 if (!flow) 81 if (!flow)
81 return ERR_PTR(-ENOMEM); 82 return ERR_PTR(-ENOMEM);
82 83
83 spin_lock_init(&flow->lock);
84 flow->sf_acts = NULL; 84 flow->sf_acts = NULL;
85 flow->mask = NULL; 85 flow->mask = NULL;
86 86
87 flow->stats.is_percpu = percpu_stats;
88
89 if (!percpu_stats) {
90 flow->stats.stat = kzalloc(sizeof(*flow->stats.stat), GFP_KERNEL);
91 if (!flow->stats.stat)
92 goto err;
93
94 spin_lock_init(&flow->stats.stat->lock);
95 } else {
96 flow->stats.cpu_stats = alloc_percpu(struct flow_stats);
97 if (!flow->stats.cpu_stats)
98 goto err;
99
100 for_each_possible_cpu(cpu) {
101 struct flow_stats *cpu_stats;
102
103 cpu_stats = per_cpu_ptr(flow->stats.cpu_stats, cpu);
104 spin_lock_init(&cpu_stats->lock);
105 }
106 }
87 return flow; 107 return flow;
108err:
109 kfree(flow);
110 return ERR_PTR(-ENOMEM);
88} 111}
89 112
90int ovs_flow_tbl_count(struct flow_table *table) 113int ovs_flow_tbl_count(struct flow_table *table)
@@ -118,6 +141,10 @@ static struct flex_array *alloc_buckets(unsigned int n_buckets)
118static void flow_free(struct sw_flow *flow) 141static void flow_free(struct sw_flow *flow)
119{ 142{
120 kfree((struct sf_flow_acts __force *)flow->sf_acts); 143 kfree((struct sf_flow_acts __force *)flow->sf_acts);
144 if (flow->stats.is_percpu)
145 free_percpu(flow->stats.cpu_stats);
146 else
147 kfree(flow->stats.stat);
121 kmem_cache_free(flow_cache, flow); 148 kmem_cache_free(flow_cache, flow);
122} 149}
123 150
diff --git a/net/openvswitch/flow_table.h b/net/openvswitch/flow_table.h
index f54aa82cf81e..1996e34c0fd8 100644
--- a/net/openvswitch/flow_table.h
+++ b/net/openvswitch/flow_table.h
@@ -55,7 +55,7 @@ struct flow_table {
55int ovs_flow_init(void); 55int ovs_flow_init(void);
56void ovs_flow_exit(void); 56void ovs_flow_exit(void);
57 57
58struct sw_flow *ovs_flow_alloc(void); 58struct sw_flow *ovs_flow_alloc(bool percpu_stats);
59void ovs_flow_free(struct sw_flow *, bool deferred); 59void ovs_flow_free(struct sw_flow *, bool deferred);
60 60
61int ovs_flow_tbl_init(struct flow_table *); 61int ovs_flow_tbl_init(struct flow_table *);