diff options
author | Pravin B Shelar <pshelar@nicira.com> | 2013-10-04 03:14:23 -0400 |
---|---|---|
committer | Jesse Gross <jesse@nicira.com> | 2013-10-04 03:18:26 -0400 |
commit | b637e4988c2d689bb43f943a5af0e684a4981159 (patch) | |
tree | 36fbf2ee83323b2441861de650c6dabe6d5cc663 /net/openvswitch | |
parent | e64457191a259537bbbfaebeba9a8043786af96f (diff) |
openvswitch: Move mega-flow list out of rehashing struct.
ovs-flow rehash does not touch mega flow list. Following patch
moves it dp struct datapath. Avoid one extra indirection for
accessing mega-flow list head on every packet receive.
Signed-off-by: Pravin B Shelar <pshelar@nicira.com>
Signed-off-by: Jesse Gross <jesse@nicira.com>
Diffstat (limited to 'net/openvswitch')
-rw-r--r-- | net/openvswitch/datapath.c | 77 | ||||
-rw-r--r-- | net/openvswitch/datapath.h | 6 | ||||
-rw-r--r-- | net/openvswitch/flow_table.c | 205 | ||||
-rw-r--r-- | net/openvswitch/flow_table.h | 32 |
4 files changed, 155 insertions, 165 deletions
diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index 72e68743c643..60b9be3b9477 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c | |||
@@ -59,8 +59,6 @@ | |||
59 | #include "vport-internal_dev.h" | 59 | #include "vport-internal_dev.h" |
60 | #include "vport-netdev.h" | 60 | #include "vport-netdev.h" |
61 | 61 | ||
62 | #define REHASH_FLOW_INTERVAL (10 * 60 * HZ) | ||
63 | |||
64 | int ovs_net_id __read_mostly; | 62 | int ovs_net_id __read_mostly; |
65 | 63 | ||
66 | static void ovs_notify(struct sk_buff *skb, struct genl_info *info, | 64 | static void ovs_notify(struct sk_buff *skb, struct genl_info *info, |
@@ -163,7 +161,7 @@ static void destroy_dp_rcu(struct rcu_head *rcu) | |||
163 | { | 161 | { |
164 | struct datapath *dp = container_of(rcu, struct datapath, rcu); | 162 | struct datapath *dp = container_of(rcu, struct datapath, rcu); |
165 | 163 | ||
166 | ovs_flow_tbl_destroy((__force struct flow_table *)dp->table, false); | 164 | ovs_flow_tbl_destroy(&dp->table, false); |
167 | free_percpu(dp->stats_percpu); | 165 | free_percpu(dp->stats_percpu); |
168 | release_net(ovs_dp_get_net(dp)); | 166 | release_net(ovs_dp_get_net(dp)); |
169 | kfree(dp->ports); | 167 | kfree(dp->ports); |
@@ -235,7 +233,7 @@ void ovs_dp_process_received_packet(struct vport *p, struct sk_buff *skb) | |||
235 | } | 233 | } |
236 | 234 | ||
237 | /* Look up flow. */ | 235 | /* Look up flow. */ |
238 | flow = ovs_flow_tbl_lookup(rcu_dereference(dp->table), &key); | 236 | flow = ovs_flow_tbl_lookup(&dp->table, &key); |
239 | if (unlikely(!flow)) { | 237 | if (unlikely(!flow)) { |
240 | struct dp_upcall_info upcall; | 238 | struct dp_upcall_info upcall; |
241 | 239 | ||
@@ -453,23 +451,6 @@ out: | |||
453 | return err; | 451 | return err; |
454 | } | 452 | } |
455 | 453 | ||
456 | /* Called with ovs_mutex. */ | ||
457 | static int flush_flows(struct datapath *dp) | ||
458 | { | ||
459 | struct flow_table *old_table; | ||
460 | struct flow_table *new_table; | ||
461 | |||
462 | old_table = ovsl_dereference(dp->table); | ||
463 | new_table = ovs_flow_tbl_alloc(TBL_MIN_BUCKETS); | ||
464 | if (!new_table) | ||
465 | return -ENOMEM; | ||
466 | |||
467 | rcu_assign_pointer(dp->table, new_table); | ||
468 | |||
469 | ovs_flow_tbl_destroy(old_table, true); | ||
470 | return 0; | ||
471 | } | ||
472 | |||
473 | static void clear_stats(struct sw_flow *flow) | 454 | static void clear_stats(struct sw_flow *flow) |
474 | { | 455 | { |
475 | flow->used = 0; | 456 | flow->used = 0; |
@@ -584,11 +565,9 @@ static struct genl_ops dp_packet_genl_ops[] = { | |||
584 | 565 | ||
585 | static void get_dp_stats(struct datapath *dp, struct ovs_dp_stats *stats) | 566 | static void get_dp_stats(struct datapath *dp, struct ovs_dp_stats *stats) |
586 | { | 567 | { |
587 | struct flow_table *table; | ||
588 | int i; | 568 | int i; |
589 | 569 | ||
590 | table = rcu_dereference_check(dp->table, lockdep_ovsl_is_held()); | 570 | stats->n_flows = ovs_flow_tbl_count(&dp->table); |
591 | stats->n_flows = ovs_flow_tbl_count(table); | ||
592 | 571 | ||
593 | stats->n_hit = stats->n_missed = stats->n_lost = 0; | 572 | stats->n_hit = stats->n_missed = stats->n_lost = 0; |
594 | for_each_possible_cpu(i) { | 573 | for_each_possible_cpu(i) { |
@@ -773,7 +752,6 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info) | |||
773 | struct sw_flow_mask mask; | 752 | struct sw_flow_mask mask; |
774 | struct sk_buff *reply; | 753 | struct sk_buff *reply; |
775 | struct datapath *dp; | 754 | struct datapath *dp; |
776 | struct flow_table *table; | ||
777 | struct sw_flow_actions *acts = NULL; | 755 | struct sw_flow_actions *acts = NULL; |
778 | struct sw_flow_match match; | 756 | struct sw_flow_match match; |
779 | int error; | 757 | int error; |
@@ -814,12 +792,9 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info) | |||
814 | if (!dp) | 792 | if (!dp) |
815 | goto err_unlock_ovs; | 793 | goto err_unlock_ovs; |
816 | 794 | ||
817 | table = ovsl_dereference(dp->table); | ||
818 | |||
819 | /* Check if this is a duplicate flow */ | 795 | /* Check if this is a duplicate flow */ |
820 | flow = ovs_flow_tbl_lookup(table, &key); | 796 | flow = ovs_flow_tbl_lookup(&dp->table, &key); |
821 | if (!flow) { | 797 | if (!flow) { |
822 | struct flow_table *new_table = NULL; | ||
823 | struct sw_flow_mask *mask_p; | 798 | struct sw_flow_mask *mask_p; |
824 | 799 | ||
825 | /* Bail out if we're not allowed to create a new flow. */ | 800 | /* Bail out if we're not allowed to create a new flow. */ |
@@ -827,19 +802,6 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info) | |||
827 | if (info->genlhdr->cmd == OVS_FLOW_CMD_SET) | 802 | if (info->genlhdr->cmd == OVS_FLOW_CMD_SET) |
828 | goto err_unlock_ovs; | 803 | goto err_unlock_ovs; |
829 | 804 | ||
830 | /* Expand table, if necessary, to make room. */ | ||
831 | if (ovs_flow_tbl_need_to_expand(table)) | ||
832 | new_table = ovs_flow_tbl_expand(table); | ||
833 | else if (time_after(jiffies, dp->last_rehash + REHASH_FLOW_INTERVAL)) | ||
834 | new_table = ovs_flow_tbl_rehash(table); | ||
835 | |||
836 | if (new_table && !IS_ERR(new_table)) { | ||
837 | rcu_assign_pointer(dp->table, new_table); | ||
838 | ovs_flow_tbl_destroy(table, true); | ||
839 | table = ovsl_dereference(dp->table); | ||
840 | dp->last_rehash = jiffies; | ||
841 | } | ||
842 | |||
843 | /* Allocate flow. */ | 805 | /* Allocate flow. */ |
844 | flow = ovs_flow_alloc(); | 806 | flow = ovs_flow_alloc(); |
845 | if (IS_ERR(flow)) { | 807 | if (IS_ERR(flow)) { |
@@ -852,7 +814,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info) | |||
852 | flow->unmasked_key = key; | 814 | flow->unmasked_key = key; |
853 | 815 | ||
854 | /* Make sure mask is unique in the system */ | 816 | /* Make sure mask is unique in the system */ |
855 | mask_p = ovs_sw_flow_mask_find(table, &mask); | 817 | mask_p = ovs_sw_flow_mask_find(&dp->table, &mask); |
856 | if (!mask_p) { | 818 | if (!mask_p) { |
857 | /* Allocate a new mask if none exsits. */ | 819 | /* Allocate a new mask if none exsits. */ |
858 | mask_p = ovs_sw_flow_mask_alloc(); | 820 | mask_p = ovs_sw_flow_mask_alloc(); |
@@ -860,7 +822,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info) | |||
860 | goto err_flow_free; | 822 | goto err_flow_free; |
861 | mask_p->key = mask.key; | 823 | mask_p->key = mask.key; |
862 | mask_p->range = mask.range; | 824 | mask_p->range = mask.range; |
863 | ovs_sw_flow_mask_insert(table, mask_p); | 825 | ovs_sw_flow_mask_insert(&dp->table, mask_p); |
864 | } | 826 | } |
865 | 827 | ||
866 | ovs_sw_flow_mask_add_ref(mask_p); | 828 | ovs_sw_flow_mask_add_ref(mask_p); |
@@ -868,7 +830,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info) | |||
868 | rcu_assign_pointer(flow->sf_acts, acts); | 830 | rcu_assign_pointer(flow->sf_acts, acts); |
869 | 831 | ||
870 | /* Put flow in bucket. */ | 832 | /* Put flow in bucket. */ |
871 | ovs_flow_tbl_insert(table, flow); | 833 | ovs_flow_tbl_insert(&dp->table, flow); |
872 | 834 | ||
873 | reply = ovs_flow_cmd_build_info(flow, dp, info->snd_portid, | 835 | reply = ovs_flow_cmd_build_info(flow, dp, info->snd_portid, |
874 | info->snd_seq, OVS_FLOW_CMD_NEW); | 836 | info->snd_seq, OVS_FLOW_CMD_NEW); |
@@ -936,7 +898,6 @@ static int ovs_flow_cmd_get(struct sk_buff *skb, struct genl_info *info) | |||
936 | struct sk_buff *reply; | 898 | struct sk_buff *reply; |
937 | struct sw_flow *flow; | 899 | struct sw_flow *flow; |
938 | struct datapath *dp; | 900 | struct datapath *dp; |
939 | struct flow_table *table; | ||
940 | struct sw_flow_match match; | 901 | struct sw_flow_match match; |
941 | int err; | 902 | int err; |
942 | 903 | ||
@@ -957,8 +918,7 @@ static int ovs_flow_cmd_get(struct sk_buff *skb, struct genl_info *info) | |||
957 | goto unlock; | 918 | goto unlock; |
958 | } | 919 | } |
959 | 920 | ||
960 | table = ovsl_dereference(dp->table); | 921 | flow = ovs_flow_tbl_lookup(&dp->table, &key); |
961 | flow = ovs_flow_tbl_lookup(table, &key); | ||
962 | if (!flow || !ovs_flow_cmp_unmasked_key(flow, &match)) { | 922 | if (!flow || !ovs_flow_cmp_unmasked_key(flow, &match)) { |
963 | err = -ENOENT; | 923 | err = -ENOENT; |
964 | goto unlock; | 924 | goto unlock; |
@@ -986,7 +946,6 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info) | |||
986 | struct sk_buff *reply; | 946 | struct sk_buff *reply; |
987 | struct sw_flow *flow; | 947 | struct sw_flow *flow; |
988 | struct datapath *dp; | 948 | struct datapath *dp; |
989 | struct flow_table *table; | ||
990 | struct sw_flow_match match; | 949 | struct sw_flow_match match; |
991 | int err; | 950 | int err; |
992 | 951 | ||
@@ -998,7 +957,7 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info) | |||
998 | } | 957 | } |
999 | 958 | ||
1000 | if (!a[OVS_FLOW_ATTR_KEY]) { | 959 | if (!a[OVS_FLOW_ATTR_KEY]) { |
1001 | err = flush_flows(dp); | 960 | err = ovs_flow_tbl_flush(&dp->table); |
1002 | goto unlock; | 961 | goto unlock; |
1003 | } | 962 | } |
1004 | 963 | ||
@@ -1007,8 +966,7 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info) | |||
1007 | if (err) | 966 | if (err) |
1008 | goto unlock; | 967 | goto unlock; |
1009 | 968 | ||
1010 | table = ovsl_dereference(dp->table); | 969 | flow = ovs_flow_tbl_lookup(&dp->table, &key); |
1011 | flow = ovs_flow_tbl_lookup(table, &key); | ||
1012 | if (!flow || !ovs_flow_cmp_unmasked_key(flow, &match)) { | 970 | if (!flow || !ovs_flow_cmp_unmasked_key(flow, &match)) { |
1013 | err = -ENOENT; | 971 | err = -ENOENT; |
1014 | goto unlock; | 972 | goto unlock; |
@@ -1020,7 +978,7 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info) | |||
1020 | goto unlock; | 978 | goto unlock; |
1021 | } | 979 | } |
1022 | 980 | ||
1023 | ovs_flow_tbl_remove(table, flow); | 981 | ovs_flow_tbl_remove(&dp->table, flow); |
1024 | 982 | ||
1025 | err = ovs_flow_cmd_fill_info(flow, dp, reply, info->snd_portid, | 983 | err = ovs_flow_cmd_fill_info(flow, dp, reply, info->snd_portid, |
1026 | info->snd_seq, 0, OVS_FLOW_CMD_DEL); | 984 | info->snd_seq, 0, OVS_FLOW_CMD_DEL); |
@@ -1039,8 +997,8 @@ unlock: | |||
1039 | static int ovs_flow_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb) | 997 | static int ovs_flow_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb) |
1040 | { | 998 | { |
1041 | struct ovs_header *ovs_header = genlmsg_data(nlmsg_data(cb->nlh)); | 999 | struct ovs_header *ovs_header = genlmsg_data(nlmsg_data(cb->nlh)); |
1000 | struct table_instance *ti; | ||
1042 | struct datapath *dp; | 1001 | struct datapath *dp; |
1043 | struct flow_table *table; | ||
1044 | 1002 | ||
1045 | rcu_read_lock(); | 1003 | rcu_read_lock(); |
1046 | dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex); | 1004 | dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex); |
@@ -1049,14 +1007,14 @@ static int ovs_flow_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb) | |||
1049 | return -ENODEV; | 1007 | return -ENODEV; |
1050 | } | 1008 | } |
1051 | 1009 | ||
1052 | table = rcu_dereference(dp->table); | 1010 | ti = rcu_dereference(dp->table.ti); |
1053 | for (;;) { | 1011 | for (;;) { |
1054 | struct sw_flow *flow; | 1012 | struct sw_flow *flow; |
1055 | u32 bucket, obj; | 1013 | u32 bucket, obj; |
1056 | 1014 | ||
1057 | bucket = cb->args[0]; | 1015 | bucket = cb->args[0]; |
1058 | obj = cb->args[1]; | 1016 | obj = cb->args[1]; |
1059 | flow = ovs_flow_tbl_dump_next(table, &bucket, &obj); | 1017 | flow = ovs_flow_tbl_dump_next(ti, &bucket, &obj); |
1060 | if (!flow) | 1018 | if (!flow) |
1061 | break; | 1019 | break; |
1062 | 1020 | ||
@@ -1220,9 +1178,8 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info) | |||
1220 | ovs_dp_set_net(dp, hold_net(sock_net(skb->sk))); | 1178 | ovs_dp_set_net(dp, hold_net(sock_net(skb->sk))); |
1221 | 1179 | ||
1222 | /* Allocate table. */ | 1180 | /* Allocate table. */ |
1223 | err = -ENOMEM; | 1181 | err = ovs_flow_tbl_init(&dp->table); |
1224 | rcu_assign_pointer(dp->table, ovs_flow_tbl_alloc(TBL_MIN_BUCKETS)); | 1182 | if (err) |
1225 | if (!dp->table) | ||
1226 | goto err_free_dp; | 1183 | goto err_free_dp; |
1227 | 1184 | ||
1228 | dp->stats_percpu = alloc_percpu(struct dp_stats_percpu); | 1185 | dp->stats_percpu = alloc_percpu(struct dp_stats_percpu); |
@@ -1279,7 +1236,7 @@ err_destroy_ports_array: | |||
1279 | err_destroy_percpu: | 1236 | err_destroy_percpu: |
1280 | free_percpu(dp->stats_percpu); | 1237 | free_percpu(dp->stats_percpu); |
1281 | err_destroy_table: | 1238 | err_destroy_table: |
1282 | ovs_flow_tbl_destroy(ovsl_dereference(dp->table), false); | 1239 | ovs_flow_tbl_destroy(&dp->table, false); |
1283 | err_free_dp: | 1240 | err_free_dp: |
1284 | release_net(ovs_dp_get_net(dp)); | 1241 | release_net(ovs_dp_get_net(dp)); |
1285 | kfree(dp); | 1242 | kfree(dp); |
diff --git a/net/openvswitch/datapath.h b/net/openvswitch/datapath.h index a6982ef84f20..acfd4af8ca3a 100644 --- a/net/openvswitch/datapath.h +++ b/net/openvswitch/datapath.h | |||
@@ -58,12 +58,11 @@ struct dp_stats_percpu { | |||
58 | * struct datapath - datapath for flow-based packet switching | 58 | * struct datapath - datapath for flow-based packet switching |
59 | * @rcu: RCU callback head for deferred destruction. | 59 | * @rcu: RCU callback head for deferred destruction. |
60 | * @list_node: Element in global 'dps' list. | 60 | * @list_node: Element in global 'dps' list. |
61 | * @table: Current flow table. Protected by ovs_mutex and RCU. | 61 | * @table: flow table. |
62 | * @ports: Hash table for ports. %OVSP_LOCAL port always exists. Protected by | 62 | * @ports: Hash table for ports. %OVSP_LOCAL port always exists. Protected by |
63 | * ovs_mutex and RCU. | 63 | * ovs_mutex and RCU. |
64 | * @stats_percpu: Per-CPU datapath statistics. | 64 | * @stats_percpu: Per-CPU datapath statistics. |
65 | * @net: Reference to net namespace. | 65 | * @net: Reference to net namespace. |
66 | * @last_rehash: Timestamp of last rehash. | ||
67 | * | 66 | * |
68 | * Context: See the comment on locking at the top of datapath.c for additional | 67 | * Context: See the comment on locking at the top of datapath.c for additional |
69 | * locking information. | 68 | * locking information. |
@@ -73,7 +72,7 @@ struct datapath { | |||
73 | struct list_head list_node; | 72 | struct list_head list_node; |
74 | 73 | ||
75 | /* Flow table. */ | 74 | /* Flow table. */ |
76 | struct flow_table __rcu *table; | 75 | struct flow_table table; |
77 | 76 | ||
78 | /* Switch ports. */ | 77 | /* Switch ports. */ |
79 | struct hlist_head *ports; | 78 | struct hlist_head *ports; |
@@ -85,7 +84,6 @@ struct datapath { | |||
85 | /* Network namespace ref. */ | 84 | /* Network namespace ref. */ |
86 | struct net *net; | 85 | struct net *net; |
87 | #endif | 86 | #endif |
88 | unsigned long last_rehash; | ||
89 | }; | 87 | }; |
90 | 88 | ||
91 | /** | 89 | /** |
diff --git a/net/openvswitch/flow_table.c b/net/openvswitch/flow_table.c index dcadb75bb173..1c7e7732ed4c 100644 --- a/net/openvswitch/flow_table.c +++ b/net/openvswitch/flow_table.c | |||
@@ -44,6 +44,11 @@ | |||
44 | #include <net/ipv6.h> | 44 | #include <net/ipv6.h> |
45 | #include <net/ndisc.h> | 45 | #include <net/ndisc.h> |
46 | 46 | ||
47 | #include "datapath.h" | ||
48 | |||
49 | #define TBL_MIN_BUCKETS 1024 | ||
50 | #define REHASH_INTERVAL (10 * 60 * HZ) | ||
51 | |||
47 | static struct kmem_cache *flow_cache; | 52 | static struct kmem_cache *flow_cache; |
48 | 53 | ||
49 | static u16 range_n_bytes(const struct sw_flow_key_range *range) | 54 | static u16 range_n_bytes(const struct sw_flow_key_range *range) |
@@ -82,6 +87,11 @@ struct sw_flow *ovs_flow_alloc(void) | |||
82 | return flow; | 87 | return flow; |
83 | } | 88 | } |
84 | 89 | ||
90 | int ovs_flow_tbl_count(struct flow_table *table) | ||
91 | { | ||
92 | return table->count; | ||
93 | } | ||
94 | |||
85 | static struct flex_array *alloc_buckets(unsigned int n_buckets) | 95 | static struct flex_array *alloc_buckets(unsigned int n_buckets) |
86 | { | 96 | { |
87 | struct flex_array *buckets; | 97 | struct flex_array *buckets; |
@@ -136,18 +146,18 @@ static void free_buckets(struct flex_array *buckets) | |||
136 | flex_array_free(buckets); | 146 | flex_array_free(buckets); |
137 | } | 147 | } |
138 | 148 | ||
139 | static void __flow_tbl_destroy(struct flow_table *table) | 149 | static void __table_instance_destroy(struct table_instance *ti) |
140 | { | 150 | { |
141 | int i; | 151 | int i; |
142 | 152 | ||
143 | if (table->keep_flows) | 153 | if (ti->keep_flows) |
144 | goto skip_flows; | 154 | goto skip_flows; |
145 | 155 | ||
146 | for (i = 0; i < table->n_buckets; i++) { | 156 | for (i = 0; i < ti->n_buckets; i++) { |
147 | struct sw_flow *flow; | 157 | struct sw_flow *flow; |
148 | struct hlist_head *head = flex_array_get(table->buckets, i); | 158 | struct hlist_head *head = flex_array_get(ti->buckets, i); |
149 | struct hlist_node *n; | 159 | struct hlist_node *n; |
150 | int ver = table->node_ver; | 160 | int ver = ti->node_ver; |
151 | 161 | ||
152 | hlist_for_each_entry_safe(flow, n, head, hash_node[ver]) { | 162 | hlist_for_each_entry_safe(flow, n, head, hash_node[ver]) { |
153 | hlist_del(&flow->hash_node[ver]); | 163 | hlist_del(&flow->hash_node[ver]); |
@@ -155,74 +165,74 @@ static void __flow_tbl_destroy(struct flow_table *table) | |||
155 | } | 165 | } |
156 | } | 166 | } |
157 | 167 | ||
158 | BUG_ON(!list_empty(table->mask_list)); | ||
159 | kfree(table->mask_list); | ||
160 | |||
161 | skip_flows: | 168 | skip_flows: |
162 | free_buckets(table->buckets); | 169 | free_buckets(ti->buckets); |
163 | kfree(table); | 170 | kfree(ti); |
164 | } | 171 | } |
165 | 172 | ||
166 | static struct flow_table *__flow_tbl_alloc(int new_size) | 173 | static struct table_instance *table_instance_alloc(int new_size) |
167 | { | 174 | { |
168 | struct flow_table *table = kmalloc(sizeof(*table), GFP_KERNEL); | 175 | struct table_instance *ti = kmalloc(sizeof(*ti), GFP_KERNEL); |
169 | 176 | ||
170 | if (!table) | 177 | if (!ti) |
171 | return NULL; | 178 | return NULL; |
172 | 179 | ||
173 | table->buckets = alloc_buckets(new_size); | 180 | ti->buckets = alloc_buckets(new_size); |
174 | 181 | ||
175 | if (!table->buckets) { | 182 | if (!ti->buckets) { |
176 | kfree(table); | 183 | kfree(ti); |
177 | return NULL; | 184 | return NULL; |
178 | } | 185 | } |
179 | table->n_buckets = new_size; | 186 | ti->n_buckets = new_size; |
180 | table->count = 0; | 187 | ti->node_ver = 0; |
181 | table->node_ver = 0; | 188 | ti->keep_flows = false; |
182 | table->keep_flows = false; | 189 | get_random_bytes(&ti->hash_seed, sizeof(u32)); |
183 | get_random_bytes(&table->hash_seed, sizeof(u32)); | ||
184 | table->mask_list = NULL; | ||
185 | 190 | ||
186 | return table; | 191 | return ti; |
187 | } | 192 | } |
188 | 193 | ||
189 | struct flow_table *ovs_flow_tbl_alloc(int new_size) | 194 | int ovs_flow_tbl_init(struct flow_table *table) |
190 | { | 195 | { |
191 | struct flow_table *table = __flow_tbl_alloc(new_size); | 196 | struct table_instance *ti; |
192 | 197 | ||
193 | if (!table) | 198 | ti = table_instance_alloc(TBL_MIN_BUCKETS); |
194 | return NULL; | ||
195 | 199 | ||
196 | table->mask_list = kmalloc(sizeof(struct list_head), GFP_KERNEL); | 200 | if (!ti) |
197 | if (!table->mask_list) { | 201 | return -ENOMEM; |
198 | table->keep_flows = true; | ||
199 | __flow_tbl_destroy(table); | ||
200 | return NULL; | ||
201 | } | ||
202 | INIT_LIST_HEAD(table->mask_list); | ||
203 | 202 | ||
204 | return table; | 203 | rcu_assign_pointer(table->ti, ti); |
204 | INIT_LIST_HEAD(&table->mask_list); | ||
205 | table->last_rehash = jiffies; | ||
206 | table->count = 0; | ||
207 | return 0; | ||
205 | } | 208 | } |
206 | 209 | ||
207 | static void flow_tbl_destroy_rcu_cb(struct rcu_head *rcu) | 210 | static void flow_tbl_destroy_rcu_cb(struct rcu_head *rcu) |
208 | { | 211 | { |
209 | struct flow_table *table = container_of(rcu, struct flow_table, rcu); | 212 | struct table_instance *ti = container_of(rcu, struct table_instance, rcu); |
210 | 213 | ||
211 | __flow_tbl_destroy(table); | 214 | __table_instance_destroy(ti); |
212 | } | 215 | } |
213 | 216 | ||
214 | void ovs_flow_tbl_destroy(struct flow_table *table, bool deferred) | 217 | static void table_instance_destroy(struct table_instance *ti, bool deferred) |
215 | { | 218 | { |
216 | if (!table) | 219 | if (!ti) |
217 | return; | 220 | return; |
218 | 221 | ||
219 | if (deferred) | 222 | if (deferred) |
220 | call_rcu(&table->rcu, flow_tbl_destroy_rcu_cb); | 223 | call_rcu(&ti->rcu, flow_tbl_destroy_rcu_cb); |
221 | else | 224 | else |
222 | __flow_tbl_destroy(table); | 225 | __table_instance_destroy(ti); |
226 | } | ||
227 | |||
228 | void ovs_flow_tbl_destroy(struct flow_table *table, bool deferred) | ||
229 | { | ||
230 | struct table_instance *ti = ovsl_dereference(table->ti); | ||
231 | |||
232 | table_instance_destroy(ti, deferred); | ||
223 | } | 233 | } |
224 | 234 | ||
225 | struct sw_flow *ovs_flow_tbl_dump_next(struct flow_table *table, | 235 | struct sw_flow *ovs_flow_tbl_dump_next(struct table_instance *ti, |
226 | u32 *bucket, u32 *last) | 236 | u32 *bucket, u32 *last) |
227 | { | 237 | { |
228 | struct sw_flow *flow; | 238 | struct sw_flow *flow; |
@@ -230,10 +240,10 @@ struct sw_flow *ovs_flow_tbl_dump_next(struct flow_table *table, | |||
230 | int ver; | 240 | int ver; |
231 | int i; | 241 | int i; |
232 | 242 | ||
233 | ver = table->node_ver; | 243 | ver = ti->node_ver; |
234 | while (*bucket < table->n_buckets) { | 244 | while (*bucket < ti->n_buckets) { |
235 | i = 0; | 245 | i = 0; |
236 | head = flex_array_get(table->buckets, *bucket); | 246 | head = flex_array_get(ti->buckets, *bucket); |
237 | hlist_for_each_entry_rcu(flow, head, hash_node[ver]) { | 247 | hlist_for_each_entry_rcu(flow, head, hash_node[ver]) { |
238 | if (i < *last) { | 248 | if (i < *last) { |
239 | i++; | 249 | i++; |
@@ -249,25 +259,23 @@ struct sw_flow *ovs_flow_tbl_dump_next(struct flow_table *table, | |||
249 | return NULL; | 259 | return NULL; |
250 | } | 260 | } |
251 | 261 | ||
252 | static struct hlist_head *find_bucket(struct flow_table *table, u32 hash) | 262 | static struct hlist_head *find_bucket(struct table_instance *ti, u32 hash) |
253 | { | 263 | { |
254 | hash = jhash_1word(hash, table->hash_seed); | 264 | hash = jhash_1word(hash, ti->hash_seed); |
255 | return flex_array_get(table->buckets, | 265 | return flex_array_get(ti->buckets, |
256 | (hash & (table->n_buckets - 1))); | 266 | (hash & (ti->n_buckets - 1))); |
257 | } | 267 | } |
258 | 268 | ||
259 | static void __tbl_insert(struct flow_table *table, struct sw_flow *flow) | 269 | static void table_instance_insert(struct table_instance *ti, struct sw_flow *flow) |
260 | { | 270 | { |
261 | struct hlist_head *head; | 271 | struct hlist_head *head; |
262 | 272 | ||
263 | head = find_bucket(table, flow->hash); | 273 | head = find_bucket(ti, flow->hash); |
264 | hlist_add_head_rcu(&flow->hash_node[table->node_ver], head); | 274 | hlist_add_head_rcu(&flow->hash_node[ti->node_ver], head); |
265 | |||
266 | table->count++; | ||
267 | } | 275 | } |
268 | 276 | ||
269 | static void flow_table_copy_flows(struct flow_table *old, | 277 | static void flow_table_copy_flows(struct table_instance *old, |
270 | struct flow_table *new) | 278 | struct table_instance *new) |
271 | { | 279 | { |
272 | int old_ver; | 280 | int old_ver; |
273 | int i; | 281 | int i; |
@@ -283,35 +291,42 @@ static void flow_table_copy_flows(struct flow_table *old, | |||
283 | head = flex_array_get(old->buckets, i); | 291 | head = flex_array_get(old->buckets, i); |
284 | 292 | ||
285 | hlist_for_each_entry(flow, head, hash_node[old_ver]) | 293 | hlist_for_each_entry(flow, head, hash_node[old_ver]) |
286 | __tbl_insert(new, flow); | 294 | table_instance_insert(new, flow); |
287 | } | 295 | } |
288 | 296 | ||
289 | new->mask_list = old->mask_list; | ||
290 | old->keep_flows = true; | 297 | old->keep_flows = true; |
291 | } | 298 | } |
292 | 299 | ||
293 | static struct flow_table *__flow_tbl_rehash(struct flow_table *table, | 300 | static struct table_instance *table_instance_rehash(struct table_instance *ti, |
294 | int n_buckets) | 301 | int n_buckets) |
295 | { | 302 | { |
296 | struct flow_table *new_table; | 303 | struct table_instance *new_ti; |
297 | 304 | ||
298 | new_table = __flow_tbl_alloc(n_buckets); | 305 | new_ti = table_instance_alloc(n_buckets); |
299 | if (!new_table) | 306 | if (!new_ti) |
300 | return ERR_PTR(-ENOMEM); | 307 | return ERR_PTR(-ENOMEM); |
301 | 308 | ||
302 | flow_table_copy_flows(table, new_table); | 309 | flow_table_copy_flows(ti, new_ti); |
303 | 310 | ||
304 | return new_table; | 311 | return new_ti; |
305 | } | 312 | } |
306 | 313 | ||
307 | struct flow_table *ovs_flow_tbl_rehash(struct flow_table *table) | 314 | int ovs_flow_tbl_flush(struct flow_table *flow_table) |
308 | { | 315 | { |
309 | return __flow_tbl_rehash(table, table->n_buckets); | 316 | struct table_instance *old_ti; |
310 | } | 317 | struct table_instance *new_ti; |
311 | 318 | ||
312 | struct flow_table *ovs_flow_tbl_expand(struct flow_table *table) | 319 | old_ti = ovsl_dereference(flow_table->ti); |
313 | { | 320 | new_ti = table_instance_alloc(TBL_MIN_BUCKETS); |
314 | return __flow_tbl_rehash(table, table->n_buckets * 2); | 321 | if (!new_ti) |
322 | return -ENOMEM; | ||
323 | |||
324 | rcu_assign_pointer(flow_table->ti, new_ti); | ||
325 | flow_table->last_rehash = jiffies; | ||
326 | flow_table->count = 0; | ||
327 | |||
328 | table_instance_destroy(old_ti, true); | ||
329 | return 0; | ||
315 | } | 330 | } |
316 | 331 | ||
317 | static u32 flow_hash(const struct sw_flow_key *key, int key_start, | 332 | static u32 flow_hash(const struct sw_flow_key *key, int key_start, |
@@ -367,7 +382,7 @@ bool ovs_flow_cmp_unmasked_key(const struct sw_flow *flow, | |||
367 | return cmp_key(&flow->unmasked_key, key, key_start, key_end); | 382 | return cmp_key(&flow->unmasked_key, key, key_start, key_end); |
368 | } | 383 | } |
369 | 384 | ||
370 | static struct sw_flow *masked_flow_lookup(struct flow_table *table, | 385 | static struct sw_flow *masked_flow_lookup(struct table_instance *ti, |
371 | const struct sw_flow_key *unmasked, | 386 | const struct sw_flow_key *unmasked, |
372 | struct sw_flow_mask *mask) | 387 | struct sw_flow_mask *mask) |
373 | { | 388 | { |
@@ -380,8 +395,8 @@ static struct sw_flow *masked_flow_lookup(struct flow_table *table, | |||
380 | 395 | ||
381 | ovs_flow_mask_key(&masked_key, unmasked, mask); | 396 | ovs_flow_mask_key(&masked_key, unmasked, mask); |
382 | hash = flow_hash(&masked_key, key_start, key_end); | 397 | hash = flow_hash(&masked_key, key_start, key_end); |
383 | head = find_bucket(table, hash); | 398 | head = find_bucket(ti, hash); |
384 | hlist_for_each_entry_rcu(flow, head, hash_node[table->node_ver]) { | 399 | hlist_for_each_entry_rcu(flow, head, hash_node[ti->node_ver]) { |
385 | if (flow->mask == mask && | 400 | if (flow->mask == mask && |
386 | flow_cmp_masked_key(flow, &masked_key, | 401 | flow_cmp_masked_key(flow, &masked_key, |
387 | key_start, key_end)) | 402 | key_start, key_end)) |
@@ -393,29 +408,55 @@ static struct sw_flow *masked_flow_lookup(struct flow_table *table, | |||
393 | struct sw_flow *ovs_flow_tbl_lookup(struct flow_table *tbl, | 408 | struct sw_flow *ovs_flow_tbl_lookup(struct flow_table *tbl, |
394 | const struct sw_flow_key *key) | 409 | const struct sw_flow_key *key) |
395 | { | 410 | { |
396 | struct sw_flow *flow = NULL; | 411 | struct table_instance *ti = rcu_dereference(tbl->ti); |
397 | struct sw_flow_mask *mask; | 412 | struct sw_flow_mask *mask; |
413 | struct sw_flow *flow; | ||
398 | 414 | ||
399 | list_for_each_entry_rcu(mask, tbl->mask_list, list) { | 415 | list_for_each_entry_rcu(mask, &tbl->mask_list, list) { |
400 | flow = masked_flow_lookup(tbl, key, mask); | 416 | flow = masked_flow_lookup(ti, key, mask); |
401 | if (flow) /* Found */ | 417 | if (flow) /* Found */ |
402 | break; | 418 | return flow; |
403 | } | 419 | } |
420 | return NULL; | ||
421 | } | ||
404 | 422 | ||
405 | return flow; | 423 | static struct table_instance *table_instance_expand(struct table_instance *ti) |
424 | { | ||
425 | return table_instance_rehash(ti, ti->n_buckets * 2); | ||
406 | } | 426 | } |
407 | 427 | ||
408 | void ovs_flow_tbl_insert(struct flow_table *table, struct sw_flow *flow) | 428 | void ovs_flow_tbl_insert(struct flow_table *table, struct sw_flow *flow) |
409 | { | 429 | { |
430 | struct table_instance *ti = NULL; | ||
431 | struct table_instance *new_ti = NULL; | ||
432 | |||
433 | ti = ovsl_dereference(table->ti); | ||
434 | |||
435 | /* Expand table, if necessary, to make room. */ | ||
436 | if (table->count > ti->n_buckets) | ||
437 | new_ti = table_instance_expand(ti); | ||
438 | else if (time_after(jiffies, table->last_rehash + REHASH_INTERVAL)) | ||
439 | new_ti = table_instance_rehash(ti, ti->n_buckets); | ||
440 | |||
441 | if (new_ti && !IS_ERR(new_ti)) { | ||
442 | rcu_assign_pointer(table->ti, new_ti); | ||
443 | ovs_flow_tbl_destroy(table, true); | ||
444 | ti = ovsl_dereference(table->ti); | ||
445 | table->last_rehash = jiffies; | ||
446 | } | ||
447 | |||
410 | flow->hash = flow_hash(&flow->key, flow->mask->range.start, | 448 | flow->hash = flow_hash(&flow->key, flow->mask->range.start, |
411 | flow->mask->range.end); | 449 | flow->mask->range.end); |
412 | __tbl_insert(table, flow); | 450 | table_instance_insert(ti, flow); |
451 | table->count++; | ||
413 | } | 452 | } |
414 | 453 | ||
415 | void ovs_flow_tbl_remove(struct flow_table *table, struct sw_flow *flow) | 454 | void ovs_flow_tbl_remove(struct flow_table *table, struct sw_flow *flow) |
416 | { | 455 | { |
456 | struct table_instance *ti = ovsl_dereference(table->ti); | ||
457 | |||
417 | BUG_ON(table->count == 0); | 458 | BUG_ON(table->count == 0); |
418 | hlist_del_rcu(&flow->hash_node[table->node_ver]); | 459 | hlist_del_rcu(&flow->hash_node[ti->node_ver]); |
419 | table->count--; | 460 | table->count--; |
420 | } | 461 | } |
421 | 462 | ||
@@ -475,7 +516,7 @@ struct sw_flow_mask *ovs_sw_flow_mask_find(const struct flow_table *tbl, | |||
475 | { | 516 | { |
476 | struct list_head *ml; | 517 | struct list_head *ml; |
477 | 518 | ||
478 | list_for_each(ml, tbl->mask_list) { | 519 | list_for_each(ml, &tbl->mask_list) { |
479 | struct sw_flow_mask *m; | 520 | struct sw_flow_mask *m; |
480 | m = container_of(ml, struct sw_flow_mask, list); | 521 | m = container_of(ml, struct sw_flow_mask, list); |
481 | if (mask_equal(mask, m)) | 522 | if (mask_equal(mask, m)) |
@@ -492,7 +533,7 @@ struct sw_flow_mask *ovs_sw_flow_mask_find(const struct flow_table *tbl, | |||
492 | */ | 533 | */ |
493 | void ovs_sw_flow_mask_insert(struct flow_table *tbl, struct sw_flow_mask *mask) | 534 | void ovs_sw_flow_mask_insert(struct flow_table *tbl, struct sw_flow_mask *mask) |
494 | { | 535 | { |
495 | list_add_rcu(&mask->list, tbl->mask_list); | 536 | list_add_rcu(&mask->list, &tbl->mask_list); |
496 | } | 537 | } |
497 | 538 | ||
498 | /* Initializes the flow module. | 539 | /* Initializes the flow module. |
diff --git a/net/openvswitch/flow_table.h b/net/openvswitch/flow_table.h index d7a114457cde..5d1abe566c46 100644 --- a/net/openvswitch/flow_table.h +++ b/net/openvswitch/flow_table.h | |||
@@ -36,42 +36,36 @@ | |||
36 | 36 | ||
37 | #include "flow.h" | 37 | #include "flow.h" |
38 | 38 | ||
39 | #define TBL_MIN_BUCKETS 1024 | 39 | struct table_instance { |
40 | |||
41 | struct flow_table { | ||
42 | struct flex_array *buckets; | 40 | struct flex_array *buckets; |
43 | unsigned int count, n_buckets; | 41 | unsigned int n_buckets; |
44 | struct rcu_head rcu; | 42 | struct rcu_head rcu; |
45 | struct list_head *mask_list; | ||
46 | int node_ver; | 43 | int node_ver; |
47 | u32 hash_seed; | 44 | u32 hash_seed; |
48 | bool keep_flows; | 45 | bool keep_flows; |
49 | }; | 46 | }; |
50 | 47 | ||
48 | struct flow_table { | ||
49 | struct table_instance __rcu *ti; | ||
50 | struct list_head mask_list; | ||
51 | unsigned long last_rehash; | ||
52 | unsigned int count; | ||
53 | }; | ||
54 | |||
51 | int ovs_flow_init(void); | 55 | int ovs_flow_init(void); |
52 | void ovs_flow_exit(void); | 56 | void ovs_flow_exit(void); |
53 | 57 | ||
54 | struct sw_flow *ovs_flow_alloc(void); | 58 | struct sw_flow *ovs_flow_alloc(void); |
55 | void ovs_flow_free(struct sw_flow *, bool deferred); | 59 | void ovs_flow_free(struct sw_flow *, bool deferred); |
56 | 60 | ||
57 | static inline int ovs_flow_tbl_count(struct flow_table *table) | 61 | int ovs_flow_tbl_init(struct flow_table *); |
58 | { | 62 | int ovs_flow_tbl_count(struct flow_table *table); |
59 | return table->count; | ||
60 | } | ||
61 | |||
62 | static inline int ovs_flow_tbl_need_to_expand(struct flow_table *table) | ||
63 | { | ||
64 | return (table->count > table->n_buckets); | ||
65 | } | ||
66 | |||
67 | struct flow_table *ovs_flow_tbl_alloc(int new_size); | ||
68 | struct flow_table *ovs_flow_tbl_expand(struct flow_table *table); | ||
69 | struct flow_table *ovs_flow_tbl_rehash(struct flow_table *table); | ||
70 | void ovs_flow_tbl_destroy(struct flow_table *table, bool deferred); | 63 | void ovs_flow_tbl_destroy(struct flow_table *table, bool deferred); |
64 | int ovs_flow_tbl_flush(struct flow_table *flow_table); | ||
71 | 65 | ||
72 | void ovs_flow_tbl_insert(struct flow_table *table, struct sw_flow *flow); | 66 | void ovs_flow_tbl_insert(struct flow_table *table, struct sw_flow *flow); |
73 | void ovs_flow_tbl_remove(struct flow_table *table, struct sw_flow *flow); | 67 | void ovs_flow_tbl_remove(struct flow_table *table, struct sw_flow *flow); |
74 | struct sw_flow *ovs_flow_tbl_dump_next(struct flow_table *table, | 68 | struct sw_flow *ovs_flow_tbl_dump_next(struct table_instance *table, |
75 | u32 *bucket, u32 *idx); | 69 | u32 *bucket, u32 *idx); |
76 | struct sw_flow *ovs_flow_tbl_lookup(struct flow_table *, | 70 | struct sw_flow *ovs_flow_tbl_lookup(struct flow_table *, |
77 | const struct sw_flow_key *); | 71 | const struct sw_flow_key *); |