@@ -17,6 +17,8 @@ Post-v2.9.0
* OFPT_ROLE_STATUS is now available in OpenFlow 1.3.
* OpenFlow 1.5 extensible statistics (OXS) now implemented.
* New OpenFlow 1.0 extensions for group support.
+ * Default selection method for select groups is now dp_hash with improved
+ accuracy.
- Linux kernel 4.14
* Add support for compiling OVS with the latest Linux 4.14 kernel
- ovn:
@@ -1600,12 +1600,17 @@ parse_group_prop_ntr_selection_method(struct ofpbuf *payload,
return OFPERR_OFPBPC_BAD_VALUE;
}
- error = oxm_pull_field_array(payload->data, fields_len,
- &gp->fields);
- if (error) {
- OFPPROP_LOG(&rl, false,
+ if (fields_len > 0) {
+ error = oxm_pull_field_array(payload->data, fields_len,
+ &gp->fields);
+ if (error) {
+ OFPPROP_LOG(&rl, false,
"ntr selection method fields are invalid");
- return error;
+ return error;
+ }
+ } else {
+ /* Selection_method "hash: w/o fields means default hash method. */
+ gp->fields.values_size = 0;
}
return 0;
@@ -1,5 +1,4 @@
/*
- * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -4787,7 +4786,7 @@ group_setup_dp_hash_table(struct group_dpif *group, size_t max_hash)
} *webster;
if (n_buckets == 0) {
- VLOG_DBG(" Don't apply dp_hash method without buckets");
+ VLOG_DBG(" Don't apply dp_hash method without buckets.");
return false;
}
@@ -4862,9 +4861,24 @@ group_set_selection_method(struct group_dpif *group)
const struct ofputil_group_props *props = &group->up.props;
const char *selection_method = props->selection_method;
+ VLOG_DBG("Constructing select group %"PRIu32, group->up.group_id);
if (selection_method[0] == '\0') {
- VLOG_DBG("No selection method specified.");
- group->selection_method = SEL_METHOD_DEFAULT;
+ VLOG_DBG("No selection method specified. Trying dp_hash.");
+ /* If the controller has not specified a selection method, check if
+ * the dp_hash selection method with max 64 hash values is appropriate
+ * for the given bucket configuration. */
+ if (group_setup_dp_hash_table(group, 64)) {
+ /* Use dp_hash selection method with symmetric L4 hash. */
+ group->selection_method = SEL_METHOD_DP_HASH;
+ group->hash_alg = OVS_HASH_ALG_SYM_L4;
+ group->hash_basis = 0;
+ VLOG_DBG("Use dp_hash with %d hash values using algorithm %d.",
+ group->hash_mask + 1, group->hash_alg);
+ } else {
+ /* Fall back to original default hashing in slow path. */
+ VLOG_DBG("Falling back to default hash method.");
+ group->selection_method = SEL_METHOD_DEFAULT;
+ }
} else if (!strcmp(selection_method, "dp_hash")) {
VLOG_DBG("Selection method specified: dp_hash.");
/* Try to use dp_hash if possible at all. */
@@ -4872,7 +4886,7 @@ group_set_selection_method(struct group_dpif *group)
group->selection_method = SEL_METHOD_DP_HASH;
group->hash_alg = props->selection_method_param >> 32;
if (group->hash_alg >= __OVS_HASH_MAX) {
- VLOG_DBG(" Invalid dp_hash algorithm %d. "
+ VLOG_DBG("Invalid dp_hash algorithm %d. "
"Defaulting to OVS_HASH_ALG_L4", group->hash_alg);
group->hash_alg = OVS_HASH_ALG_L4;
}
@@ -4881,7 +4895,7 @@ group_set_selection_method(struct group_dpif *group)
group->hash_mask + 1, group->hash_alg);
} else {
/* Fall back to original default hashing in slow path. */
- VLOG_DBG(" Falling back to default hash method.");
+ VLOG_DBG("Falling back to default hash method.");
group->selection_method = SEL_METHOD_DEFAULT;
}
} else if (!strcmp(selection_method, "hash")) {
@@ -4890,12 +4904,12 @@ group_set_selection_method(struct group_dpif *group)
/* Controller has specified hash fields. */
struct ds s = DS_EMPTY_INITIALIZER;
oxm_format_field_array(&s, &props->fields);
- VLOG_DBG(" Hash fields: %s", ds_cstr(&s));
+ VLOG_DBG("Hash fields: %s", ds_cstr(&s));
ds_destroy(&s);
group->selection_method = SEL_METHOD_HASH;
} else {
/* No hash fields. Fall back to original default hashing. */
- VLOG_DBG(" No hash fields. Falling back to default hash method.");
+ VLOG_DBG("No hash fields. Falling back to default hash method.");
group->selection_method = SEL_METHOD_DEFAULT;
}
} else {
@@ -61,6 +61,7 @@ struct ofproto_async_msg;
struct ofproto_dpif;
struct uuid;
struct xlate_cache;
+struct xlate_ctx;
/* Number of implemented OpenFlow tables. */
enum { N_TABLES = 255 };
@@ -572,7 +572,7 @@ struct ofgroup {
const struct ovs_list buckets; /* Contains "struct ofputil_bucket"s. */
const uint32_t n_buckets;
- const struct ofputil_group_props props;
+ struct ofputil_group_props props;
struct rule_collection rules OVS_GUARDED; /* Referring rules. */
};
@@ -25,7 +25,7 @@ dummy@ovs-dummy: hit:0 missed:0
])
dnl Setup single MPLS tags.
-AT_CHECK([ovs-ofctl -O OpenFlow13 add-group br0 group_id=1232,type=select,bucket=output:LOCAL])
+AT_CHECK([ovs-ofctl -O OpenFlow15 add-group br0 group_id=1232,type=select,selection_method=hash,bucket=output:LOCAL])
AT_CHECK([ovs-ofctl -O OpenFlow13 add-group br0 group_id=1233,type=all,bucket=output:LOCAL])
AT_CHECK([ovs-ofctl -O OpenFlow13 add-group br0 group_id=1234,type=all,bucket=dec_ttl,output:LOCAL])
AT_CHECK([ovs-ofctl -O OpenFlow13 add-flow br0 in_port=local,dl_type=0x0800,action=push_mpls:0x8847,set_field:10-\>mpls_label,output:1])
@@ -71,9 +71,15 @@ AT_CHECK([tail -1 stdout], [0],
[Datapath actions: pop_mpls(eth_type=0x800),recirc(0x2)
])
-AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'recirc_id(2),in_port(1),eth(src=f8:bc:12:44:34:b6,dst=f8:bc:12:46:58:e0),eth_type(0x0800),ipv4(src=1.1.2.92,dst=1.1.2.88,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout])
-AT_CHECK([tail -1 stdout], [0],
- [Datapath actions: 100
+for d in 0 1 2 3; do
+ pkt="in_port(1),eth(src=f8:bc:12:44:34:b6,dst=f8:bc:12:46:58:e0),eth_type(0x8847),mpls(label=22,tc=0,ttl=64,bos=1)"
+ AT_CHECK([ovs-appctl netdev-dummy/receive p0 $pkt])
+done
+
+AT_CHECK([ovs-appctl dpctl/dump-flows | sed 's/packets.*actions:1/actions:1/' | strip_used | strip_ufid | sort], [0], [dnl
+flow-dump from non-dpdk interfaces:
+recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(src=f8:bc:12:44:34:b6,dst=f8:bc:12:46:58:e0),eth_type(0x8847),mpls(label=22/0xfffff,tc=0/0,ttl=64/0x0,bos=1/1), packets:3, bytes:54, used:0.0s, actions:pop_mpls(eth_type=0x800),recirc(0x3)
+recirc_id(0x3),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), actions:100
])
dnl Test MPLS pop then all group output (bucket actions do not trigger recirculation)
@@ -85,10 +91,10 @@ AT_CHECK([tail -1 stdout], [0],
dnl Test MPLS pop then all group output (bucket actions trigger recirculation)
AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=f8:bc:12:46:58:e0),eth_type(0x8847),mpls(label=24,tc=0,ttl=64,bos=1)'], [0], [stdout])
AT_CHECK([tail -1 stdout], [0],
- [Datapath actions: pop_mpls(eth_type=0x800),recirc(0x3)
+ [Datapath actions: pop_mpls(eth_type=0x800),recirc(0x4)
])
-AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'recirc_id(3),in_port(1),eth(src=f8:bc:12:44:34:b6,dst=f8:bc:12:46:58:e0),eth_type(0x0800),ipv4(src=1.1.2.92,dst=1.1.2.88,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'recirc_id(4),in_port(1),eth(src=f8:bc:12:44:34:b6,dst=f8:bc:12:46:58:e0),eth_type(0x0800),ipv4(src=1.1.2.92,dst=1.1.2.88,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout])
AT_CHECK([tail -1 stdout], [0],
[Datapath actions: set(ipv4(ttl=63)),100
])
@@ -96,10 +102,10 @@ AT_CHECK([tail -1 stdout], [0],
dnl Test MPLS pop then all output to patch port
AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=f8:bc:12:46:58:e0),eth_type(0x8847),mpls(label=25,tc=0,ttl=64,bos=1)'], [0], [stdout])
AT_CHECK([tail -1 stdout], [0],
- [Datapath actions: pop_mpls(eth_type=0x800),recirc(0x4)
+ [Datapath actions: pop_mpls(eth_type=0x800),recirc(0x5)
])
-AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'recirc_id(4),in_port(1),eth(src=f8:bc:12:44:34:b6,dst=f8:bc:12:46:58:e0),eth_type(0x0800),ipv4(src=1.1.2.92,dst=1.1.2.88,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'recirc_id(5),in_port(1),eth(src=f8:bc:12:44:34:b6,dst=f8:bc:12:46:58:e0),eth_type(0x0800),ipv4(src=1.1.2.92,dst=1.1.2.88,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout])
AT_CHECK([tail -1 stdout], [0],
[Datapath actions: 101
])
@@ -124,10 +130,10 @@ AT_CHECK([tail -1 stdout], [0],
dnl Double MPLS pop
AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=f8:bc:12:46:58:e0),eth_type(0x8847),mpls(label=60,tc=0,ttl=64,bos=0,label=50,tc=0,ttl=64,bos=1)'], [0], [stdout])
AT_CHECK([tail -1 stdout], [0],
- [Datapath actions: pop_mpls(eth_type=0x8847),pop_mpls(eth_type=0x800),recirc(0x5)
+ [Datapath actions: pop_mpls(eth_type=0x8847),pop_mpls(eth_type=0x800),recirc(0x7)
])
-AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'recirc_id(5),in_port(1),eth(src=f8:bc:12:44:34:b6,dst=f8:bc:12:46:58:e0),eth_type(0x0800),ipv4(src=1.1.2.92,dst=1.1.2.88,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'recirc_id(7),in_port(1),eth(src=f8:bc:12:44:34:b6,dst=f8:bc:12:46:58:e0),eth_type(0x0800),ipv4(src=1.1.2.92,dst=1.1.2.88,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout])
AT_CHECK([tail -1 stdout], [0],
[Datapath actions: set(ipv4(ttl=10)),100
])
@@ -337,10 +337,18 @@ OVS_VSWITCHD_START
add_of_ports br0 1 10
AT_CHECK([ovs-ofctl -O OpenFlow12 add-group br0 'group_id=1234,type=select,bucket=set_field:192.168.3.90->ip_src,output:10'])
AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 'ip actions=group:1234,output:10'])
-AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=1,nw_tos=0,nw_ttl=128,icmp_type=8,icmp_code=0'], [0], [stdout])
-AT_CHECK([tail -1 stdout], [0],
- [Datapath actions: set(ipv4(src=192.168.3.90,dst=192.168.0.2)),10,set(ipv4(src=192.168.0.1,dst=192.168.0.2)),10
+
+for d in 0 1 2 3; do
+ pkt="in_port(1),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:1),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.1.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)"
+ AT_CHECK([ovs-appctl netdev-dummy/receive p1 $pkt])
+done
+
+AT_CHECK([ovs-appctl dpctl/dump-flows | sed 's/dp_hash(.*\/0xf)/dp_hash(0xXXXX\/0xf)/' | sed 's/packets.*actions:/actions:/' | strip_ufid | strip_used | sort], [0], [dnl
+flow-dump from non-dpdk interfaces:
+recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), actions:hash(sym_l4(0)),recirc(0x1)
+recirc_id(0x1),dp_hash(0xXXXX/0xf),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(src=192.168.0.1,frag=no), actions:set(ipv4(src=192.168.3.90)),10,set(ipv4(src=192.168.0.1)),10
])
+
OVS_VSWITCHD_STOP
AT_CLEANUP
@@ -397,81 +405,265 @@ AT_CLEANUP
AT_SETUP([ofproto-dpif - select group])
+
+# Helper function to check the spread of dp_hash flows over buckets in the datapath
+check_dpflow_stats () {
+ min_flows=$1
+ min_buckets=$2
+ read -d '' dpflows
+ hash_flow=`echo "$dpflows" | grep "actions:hash"`
+ n_flows=`echo "$dpflows" | grep -c dp_hash`
+ n_buckets=`echo "$dpflows" | grep dp_hash | grep -o "actions:[[0-9]]*" | sort | uniq -c | wc -l`
+ if [[ $n_flows -ge $min_flows ]]; then flows=ok; else flows=nok; fi
+ if [[ $n_buckets -ge $min_buckets ]]; then buckets=ok; else buckets=nok; fi
+ echo $hash_flow
+ echo "n_flows=$flows n_buckets=$buckets"
+}
+
OVS_VSWITCHD_START
add_of_ports br0 1 10 11
+
+ovs-appctl vlog/set ofproto_dpif:file:dbg
AT_CHECK([ovs-ofctl -O OpenFlow12 add-group br0 'group_id=1234,type=select,bucket=output:10,bucket=output:11'])
+AT_CHECK([grep -A6 "Constructing select group 1234" ovs-vswitchd.log | sed 's/^.*ofproto_dpif/ofproto_dpif/'], [0], [dnl
+ofproto_dpif|DBG|Constructing select group 1234
+ofproto_dpif|DBG|No selection method specified. Trying dp_hash.
+ofproto_dpif|DBG| Minimum weight: 1, total weight: 2
+ofproto_dpif|DBG| Using 16 hash values:
+ofproto_dpif|DBG| Bucket 0: weight=1, target=8.00 hits=8
+ofproto_dpif|DBG| Bucket 1: weight=1, target=8.00 hits=8
+ofproto_dpif|DBG|Use dp_hash with 16 hash values using algorithm 1.
+])
AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 'ip actions=write_actions(group:1234)'])
# Try a bunch of different flows and make sure that they get distributed
-# at least somewhat.
-for d in 0 1 2 3 4 5 6 7 8 9 a b c d e f; do
- AT_CHECK([ovs-appctl ofproto/trace br0 "in_port=1,dl_src=50:54:00:00:00:07,dl_dst=50:54:00:00:00:0$d,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=1,nw_tos=0,nw_ttl=128,icmp_type=8,icmp_code=0"], [0], [stdout])
- tail -1 stdout >> results
+# # at least somewhat.
+for d in 0 1 2 3; do
+ for s in 1 2 3 4 ; do
+ pkt="in_port(1),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:1),eth_type(0x0800),ipv4(src=192.168.0.$s,dst=192.168.1.$d,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)"
+ AT_CHECK([ovs-appctl netdev-dummy/receive p1 $pkt])
+ done
done
-sort results | uniq -c
-AT_CHECK([sort results | uniq], [0],
- [Datapath actions: 10
-Datapath actions: 11
+
+AT_CHECK([ovs-appctl dpctl/dump-flows | sort | strip_ufid | strip_used | check_dpflow_stats 5 2], [0], [dnl
+recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:15, bytes:1590, used:0.0s, actions:hash(sym_l4(0)),recirc(0x1)
+n_flows=ok n_buckets=ok
])
+
OVS_VSWITCHD_STOP
AT_CLEANUP
AT_SETUP([ofproto-dpif - select group with watch port])
+
OVS_VSWITCHD_START
add_of_ports br0 1 10 11
AT_CHECK([ovs-ofctl -O OpenFlow12 add-group br0 'group_id=1234,type=select,bucket=watch_port:10,output:10,bucket=output:11'])
AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 'ip actions=write_actions(group:1234)'])
-AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_src=50:54:00:00:00:07,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=1,nw_tos=0,nw_ttl=128,icmp_type=8,icmp_code=0'], [0], [stdout])
-AT_CHECK([tail -1 stdout], [0],
- [Datapath actions: 11
+
+for d in 0 1 2 3; do
+ pkt="in_port(1),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:1),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)"
+ AT_CHECK([ovs-appctl netdev-dummy/receive p1 $pkt])
+done
+
+AT_CHECK([ovs-appctl dpctl/dump-flows | sort| sed 's/dp_hash(.*\/0xf)/dp_hash(0xXXXX\/0xf)/' | strip_ufid | strip_used], [0], [dnl
+flow-dump from non-dpdk interfaces:
+recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:3, bytes:318, used:0.0s, actions:hash(sym_l4(0)),recirc(0x1)
+recirc_id(0x1),dp_hash(0xXXXX/0xf),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:3, bytes:318, used:0.0s, actions:11
])
+
OVS_VSWITCHD_STOP
AT_CLEANUP
-AT_SETUP([ofproto-dpif - select group with weight])
+AT_SETUP([ofproto-dpif - select group with weights])
+
+# Helper function to check the spread of dp_hash flows over buckets in the datapath
+check_dpflow_stats () {
+ min_flows=$1
+ min_buckets=$2
+ read -d '' dpflows
+ hash_flow=`echo "$dpflows" | grep "actions:hash"`
+ n_flows=`echo "$dpflows" | grep -c dp_hash`
+ n_buckets=`echo "$dpflows" | grep dp_hash | grep -o "actions:[[0-9]]*" | sort | uniq -c | wc -l`
+ if [[ $n_flows -ge $min_flows ]]; then flows=ok; else flows=nok; fi
+ if [[ $n_buckets -ge $min_buckets ]]; then buckets=ok; else buckets=nok; fi
+ echo $hash_flow
+ echo "n_flows=$flows n_buckets=$buckets"
+}
+
+# Helper function to check the accuracy of distribution of packets over buckets
+check_group_stats () {
+ min=($1 $2 $3 $4)
+ buckets=`grep -o 'packet_count=[[0-9]]*' | cut -d'=' -f2 | tail -n +2`
+ i=0
+ for bucket in $buckets; do
+ if [[ $bucket -ge ${min[i]} ]]; then
+ echo "bucket$i >= ${min[[$i]]}"
+ else
+ echo "bucket$i < ${min[[$i]]}"
+ fi
+ (( i++ ))
+ if [[ $i -ge 4 ]]; then break; fi
+ done
+}
+
OVS_VSWITCHD_START
-add_of_ports br0 1 10 11 12
-AT_CHECK([ovs-ofctl -O OpenFlow12 add-group br0 'group_id=1234,type=select,bucket=output:10,bucket=output:11,weight=2000,bucket=output:12,weight=0'])
+add_of_ports br0 1 10 11 12 13 14
+
+ovs-appctl vlog/set ofproto_dpif:file:dbg
+AT_CHECK([ovs-ofctl -O OpenFlow13 add-group br0 'group_id=1234,type=select,bucket=weight:5,output:10,bucket=weight:10,output:11,bucket=weight:25,output:12,bucket=weight:60,output:13,bucket=weight:0,output:14'])
+AT_CHECK([grep -A9 "Constructing select group 1234" ovs-vswitchd.log | sed 's/^.*ofproto_dpif/ofproto_dpif/'], [0], [dnl
+ofproto_dpif|DBG|Constructing select group 1234
+ofproto_dpif|DBG|No selection method specified. Trying dp_hash.
+ofproto_dpif|DBG| Minimum weight: 5, total weight: 100
+ofproto_dpif|DBG| Using 32 hash values:
+ofproto_dpif|DBG| Bucket 0: weight=5, target=1.60 hits=2
+ofproto_dpif|DBG| Bucket 1: weight=10, target=3.20 hits=3
+ofproto_dpif|DBG| Bucket 2: weight=25, target=8.00 hits=8
+ofproto_dpif|DBG| Bucket 3: weight=60, target=19.20 hits=19
+ofproto_dpif|DBG| Bucket 4: weight=0, target=0.00 hits=0
+ofproto_dpif|DBG|Use dp_hash with 32 hash values using algorithm 1.
+])
AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 'ip actions=write_actions(group:1234)'])
-AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_src=50:54:00:00:00:07,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=1,nw_tos=0,nw_ttl=128,icmp_type=8,icmp_code=0'], [0], [stdout])
-AT_CHECK([tail -1 stdout], [0],
- [Datapath actions: 11
+
+# Try 1000 different flows and make sure that they get distributed according to weights
+for d1 in 0 1 2 3 4 5 6 7 8 9 ; do
+ for d2 in 0 1 2 3 4 5 6 7 8 9 ; do
+ for s in 0 1 2 3 4 5 6 7 8 9 ; do
+ pkt="in_port(1),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:1),eth_type(0x0800),ipv4(src=192.168.1.$s,dst=192.168.$d1.$d2,proto=6,tos=0,ttl=128,frag=no),tcp(src=1000$s,dst=1000)"
+ AT_CHECK([ovs-appctl netdev-dummy/receive p1 $pkt])
+ done
+ done
+done
+
+# Check balanced distribution over 32 dp_hash values
+AT_CHECK([ovs-appctl dpctl/dump-flows | sort | strip_ufid | strip_used | check_dpflow_stats 32 4 ], [0], [dnl
+recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:999, bytes:117882, used:0.0s, actions:hash(sym_l4(0)),recirc(0x1)
+n_flows=ok n_buckets=ok
+])
+
+# Check that actual distribution over the buckets is reasonably accurate:
+ ideal weights dp_hash values
+# bucket0: 5%*1000 = 50 2/32*1000 = 63
+# bucket1: 10%*1000 = 100 3/32*1000 = 94
+# bucket2: 25%*1000 = 250 8/32*1000 = 250
+# bucket3: 60%*1000 = 600 19/32*1000 = 594
+# bucket4: 0 0
+
+ovs-appctl time/warp 1000
+AT_CHECK([ovs-ofctl -O OpenFlow13 dump-group-stats br0 | sed 's/duration=[[0-9]]\.[[0-9]]*s,//' | check_group_stats 40 80 200 500],
+[0], [dnl
+bucket0 >= 40
+bucket1 >= 80
+bucket2 >= 200
+bucket3 >= 500
])
+
OVS_VSWITCHD_STOP
AT_CLEANUP
-AT_SETUP([ofproto-dpif - select group with hash selection method])
+AT_SETUP([ofproto-dpif - select group with explicit dp_hash selection method])
+
OVS_VSWITCHD_START
add_of_ports br0 1 10 11
-# Check that parse failures after 'fields' parsing work
-AT_CHECK([ovs-ofctl -O OpenFlow10 add-group br0 'group_id=1,type=select,fields(eth_dst),bukket=output:10'], [1], ,[dnl
-ovs-ofctl: unknown keyword bukket
+
+ovs-appctl vlog/set ofproto_dpif:file:dbg
+AT_CHECK([ovs-ofctl -O OpenFlow15 add-group br0 'group_id=1234,type=select,selection_method=dp_hash,bucket=output:10,bucket=output:11'])
+AT_CHECK([grep -A6 "Constructing select group 1234" ovs-vswitchd.log | sed 's/^.*ofproto_dpif/ofproto_dpif/'], [0], [dnl
+ofproto_dpif|DBG|Constructing select group 1234
+ofproto_dpif|DBG|Selection method specified: dp_hash.
+ofproto_dpif|DBG| Minimum weight: 1, total weight: 2
+ofproto_dpif|DBG| Using 16 hash values:
+ofproto_dpif|DBG| Bucket 0: weight=1, target=8.00 hits=8
+ofproto_dpif|DBG| Bucket 1: weight=1, target=8.00 hits=8
+ofproto_dpif|DBG|Use dp_hash with 16 hash values using algorithm 0.
+])
+
+# Fall back to legacy hash with zero buckets
+AT_CHECK([ovs-ofctl -O OpenFlow15 add-group br0 'group_id=1235,type=select,selection_method=dp_hash'])
+AT_CHECK([grep -A3 "Constructing select group 1235" ovs-vswitchd.log | sed 's/^.*ofproto_dpif/ofproto_dpif/'], [0], [dnl
+ofproto_dpif|DBG|Constructing select group 1235
+ofproto_dpif|DBG|Selection method specified: dp_hash.
+ofproto_dpif|DBG| Don't apply dp_hash method without buckets.
+ofproto_dpif|DBG|Falling back to default hash method.
+])
+
+# Fall back to legacy hash with zero buckets
+AT_CHECK([ovs-ofctl -O OpenFlow15 add-group br0 'group_id=1236,type=select,selection_method=dp_hash,bucket=weight=1,output:10,bucket=weight=1000,output:11'])
+AT_CHECK([grep -A4 "Constructing select group 1236" ovs-vswitchd.log | sed 's/^.*ofproto_dpif/ofproto_dpif/'], [0], [dnl
+ofproto_dpif|DBG|Constructing select group 1236
+ofproto_dpif|DBG|Selection method specified: dp_hash.
+ofproto_dpif|DBG| Minimum weight: 1, total weight: 1001
+ofproto_dpif|DBG| Too many hash values required: 1024
+ofproto_dpif|DBG|Falling back to default hash method.
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto-dpif - select group with legacy hash selection method])
+
+# Helper function to check the spread of dp_hash flows over buckets in the datapath
+check_dpflow_stats () {
+ min_flows=$1
+ min_buckets=$2
+ read -d '' dpflows
+ n_flows=`echo "$dpflows" | wc -l`
+ n_buckets=`echo "$dpflows" | grep -o "actions:[[0-9]]*" | sort | uniq -c | wc -l`
+ if [[ $n_flows -ge $min_flows ]]; then flows=ok; else flows=nok; fi
+ if [[ $n_buckets -ge $min_buckets ]]; then buckets=ok; else buckets=nok; fi
+ echo "n_flows=$flows n_buckets=$buckets"
+}
+
+OVS_VSWITCHD_START
+add_of_ports br0 1 10 11
+
+ovs-appctl vlog/set ofproto_dpif:file:dbg
+AT_CHECK([ovs-ofctl -O OpenFlow15 add-group br0 'group_id=1234,type=select,selection_method=hash,bucket=output:10,bucket=output:11'])
+AT_CHECK([grep -A2 "Constructing select group 1234" ovs-vswitchd.log | sed 's/^.*ofproto_dpif/ofproto_dpif/'], [0], [dnl
+ofproto_dpif|DBG|Constructing select group 1234
+ofproto_dpif|DBG|Selection method specified: hash.
+ofproto_dpif|DBG|No hash fields. Falling back to default hash method.
])
-AT_CHECK([ovs-ofctl -O OpenFlow15 add-group br0 'group_id=1234,type=select,selection_method=hash,fields(eth_dst,ip_dst,tcp_dst),bucket=output:10,bucket=output:11'])
+
AT_CHECK([ovs-ofctl -O OpenFlow15 add-flow br0 'ip actions=write_actions(group:1234)'])
-# Try a bunch of different flows and make sure that they get distributed
-# at least somewhat.
-for d in 0 1 2 3 4 5 6 7 8 9 a b c d e f; do
- AT_CHECK([ovs-appctl ofproto/trace br0 "in_port=1,dl_src=50:54:00:00:00:07,dl_dst=50:54:00:00:00:0$d,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=1,nw_tos=0,nw_ttl=128,icmp_type=8,icmp_code=0"], [0], [stdout])
- tail -1 stdout >> results
+# Try 16 flows with differing default hash values.
+for d in 0 1 2 3; do
+ for s in 1 2 3 4 ; do
+ pkt="in_port(1),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:1),eth_type(0x0800),ipv4(src=192.168.0.$s,dst=192.168.1.$d,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)"
+ AT_CHECK([ovs-appctl netdev-dummy/receive p1 $pkt])
+ done
done
-sort results | uniq -c
-AT_CHECK([sort results | uniq], [0],
- [Datapath actions: 10
-Datapath actions: 11
+
+# Check that the packets installed 16 data path flows and each of the two
+# buckets is hit at least once.
+AT_CHECK([ovs-appctl dpctl/dump-flows | strip_ufid | strip_used | sort | check_dpflow_stats 16 2], [0], [dnl
+n_flows=ok n_buckets=ok
])
-> results
-# Try a bunch of different flows and make sure that they are not distributed
-# as they only vary a field that is not hashed
-for d in 0 1 2 3 4 5 6 7 8 9 a b c d e f; do
- AT_CHECK([ovs-appctl ofproto/trace br0 "in_port=1,dl_src=50:54:00:00:00:0$d,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=1,nw_tos=0,nw_ttl=128,icmp_type=8,icmp_code=0"], [0], [stdout])
- tail -1 stdout >> results
-done
-sort results | uniq -c
-AT_CHECK([sort results | uniq | sed 's/1[[01]]/1?/'], [0],
- [Datapath actions: 1?
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto-dpif - select group with custom hash selection method])
+
+# Helper function to check the spread of dp_hash flows over buckets in the datapath
+check_dpflow_stats () {
+ min_flows=$1
+ min_buckets=$2
+ read -d '' dpflows
+ n_flows=`echo "$dpflows" | wc -l`
+ n_buckets=`echo "$dpflows" | grep -o "actions:[[0-9]]*" | sort | uniq -c | wc -l`
+ if [[ $n_flows -ge $min_flows ]]; then flows=ok; else flows=nok; fi
+ if [[ $n_buckets -ge $min_buckets ]]; then buckets=ok; else buckets=nok; fi
+ echo "n_flows=$flows n_buckets=$buckets"
+}
+
+OVS_VSWITCHD_START
+add_of_ports br0 1 10 11
+
+# Check that parse failures after 'fields' parsing work
+AT_CHECK([ovs-ofctl -O OpenFlow10 add-group br0 'group_id=1,type=select,fields(eth_dst),bukket=output:10'], [1], ,[dnl
+ovs-ofctl: unknown keyword bukket
])
# Check that fields are rejected without "selection_method=hash".
@@ -484,43 +676,31 @@ AT_CHECK([ovs-ofctl -O OpenFlow15 add-group br0 'group_id=1235,type=select,selec
ovs-ofctl: selection_method_param is only allowed with "selection_method"
])
-OVS_VSWITCHD_STOP
-AT_CLEANUP
-
-AT_SETUP([ofproto-dpif - select group with dp_hash selection method])
-OVS_VSWITCHD_START
-add_of_ports br0 1 10 11
-AT_CHECK([ovs-ofctl -O OpenFlow15 add-group br0 'group_id=1234,type=select,selection_method=dp_hash,bucket=output:10,bucket=output:11'])
-AT_CHECK([ovs-ofctl -O OpenFlow15 add-flow br0 'ip,nw_src=192.168.0.1 actions=group:1234'])
+AT_CHECK([ovs-ofctl -O OpenFlow15 add-group br0 'group_id=1234,type=select,selection_method=hash,fields(eth_dst,ip_dst,tcp_dst),bucket=output:10,bucket=output:11'])
+AT_CHECK([ovs-ofctl -O OpenFlow15 add-flow br0 'ip actions=write_actions(group:1234)'])
-# Try a bunch of different flows and make sure that they get distributed
-# at least somewhat.
-for d in 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15; do
- pkt="in_port(1),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:01),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.1.$d,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)"
+# Try 16 flows with differing custom hash and check that they give rise to
+# 16 data path flows and each of the two buckets is hit at least once
+for d in 0 1 2 3 4 5 6 7 8 9 a b c d e f; do
+ pkt="in_port(1),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:$d),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)"
AT_CHECK([ovs-appctl netdev-dummy/receive p1 $pkt])
done
-AT_CHECK([ovs-appctl dpctl/dump-flows | sed 's/dp_hash(.*\/0xf)/dp_hash(0xXXXX\/0xf)/' | sed 's/packets.*actions:1/actions:1/' | \
- strip_ufid | strip_used | sort | uniq], [0], [dnl
-flow-dump from non-dpdk interfaces:
-recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(src=192.168.0.1,frag=no), packets:15, bytes:1590, used:0.0s, actions:hash(l4(0)),recirc(0x1)
-recirc_id(0x1),dp_hash(0xXXXX/0xf),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), actions:10
-recirc_id(0x1),dp_hash(0xXXXX/0xf),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), actions:11
+AT_CHECK([ovs-appctl dpctl/dump-flows | strip_ufid | strip_used | sort | check_dpflow_stats 16 2], [0], [dnl
+n_flows=ok n_buckets=ok
])
AT_CHECK([ovs-appctl revalidator/purge], [0])
-# Try a bunch of different flows and make sure that they are not distributed
-# as they only vary a field that is not hashed
+# Try 16 flows that differ only in fields that are not part of the custom
+# hash and check that there is only a single datapath flow
for d in 0 1 2 3 4 5 6 7 8 9 a b c d e f; do
pkt="in_port(1),eth(src=50:54:00:00:00:$d,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)"
AT_CHECK([ovs-appctl netdev-dummy/receive p1 $pkt])
done
-AT_CHECK([ovs-appctl dpctl/dump-flows | sed 's/dp_hash(.*\/0xf)/dp_hash(0xXXXX\/0xf)/' | sed 's/\(actions:1\)[[01]]/\1X/' | strip_ufid | strip_used | sort], [0], [dnl
-flow-dump from non-dpdk interfaces:
-recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(src=192.168.0.1,frag=no), packets:15, bytes:1590, used:0.0s, actions:hash(l4(0)),recirc(0x2)
-recirc_id(0x2),dp_hash(0xXXXX/0xf),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:15, bytes:1590, used:0.0s, actions:1X
+AT_CHECK([ovs-appctl dpctl/dump-flows | grep -c recirc_id], [0], [dnl
+1
])
OVS_VSWITCHD_STOP
@@ -300,6 +300,11 @@ strip_used () {
sed 's/used:[[0-9]]\.[[0-9]]*/used:0.0/'
}
+# Removes all 'duration=...' to make output easier to compare.
+strip_duration () {
+ sed 's/duration=[[0-9]]*\.[[0-9]]*s,//'
+}
+
# Strips 'ufid:...' from output, to make it easier to compare.
# (ufids are random.)
strip_ufid () {
@@ -318,7 +323,7 @@ m4_define([_OVS_VSWITCHD_START],
[dnl Create database.
touch .conf.db.~lock~
AT_CHECK([ovsdb-tool create conf.db $abs_top_srcdir/vswitchd/vswitch.ovsschema])
-
+q
dnl Start ovsdb-server.
AT_CHECK([ovsdb-server --detach --no-chdir --pidfile --log-file --remote=punix:$OVS_RUNDIR/db.sock], [0], [], [stderr])
on_exit "kill `cat ovsdb-server.pid`"
@@ -2120,28 +2120,23 @@ The selection method used to select a bucket for a select group.
This is a string of 1 to 15 bytes in length known to lower layers.
This field is optional for \fBadd\-group\fR, \fBadd\-groups\fR and
\fBmod\-group\fR commands on groups of type \fBselect\fR. Prohibited
-otherwise. The default value is the empty string.
+otherwise. If no selection method is specified, Open vSwitch up to
+release 2.9 applies the \fBhash\fR method with default fields. From
+2.10 onwards Open vSwitch defaults to the \fBdp_hash\fR method with symmetric
+L3/L4 hash algorithm, unless the weighted group buckets cannot be mapped to
+a maximum of 64 dp_hash values with sufficient accuracy.
+In those rare cases Open vSwitch 2.10 and later fall back to the \fBhash\fR
+method with the default set of hash fields.
.RS
-.IP \fBhash\fR
-Use a hash computed over the fields specified with the \fBfields\fR
-option, see below. \fBhash\fR uses the \fBselection_method_param\fR
-as the hash basis.
-.IP
-Note that the hashed fields become exact matched by the datapath
-flows. For example, if the TCP source port is hashed, the created
-datapath flows will match the specific TCP source port value present
-in the packet received. Since each TCP connection generally has a
-different source port value, a separate datapath flow will be need to
-be inserted for each TCP connection thus hashed to a select group
-bucket.
.IP \fBdp_hash\fR
Use a datapath computed hash value. The hash algorithm varies accross
different datapath implementations. \fBdp_hash\fR uses the upper 32
bits of the \fBselection_method_param\fR as the datapath hash
-algorithm selector, which currently must always be 0, corresponding to
-hash computation over the IP 5-tuple (selecting specific fields with
-the \fBfields\fR option is not allowed with \fBdp_hash\fR). The lower
-32 bits are used as the hash basis.
+algorithm selector. The supported values are \fB0\fR (corresponding to
+hash computation over the IP 5-tuple) and \fB1\fR (corresponding to a
+\fIsymmetric\fR hash computation over the IP 5-tuple). Selecting specific
+fields with the \fBfields\fR option is not supported with \fBdp_hash\fR).
+The lower 32 bits are used as the hash basis.
.IP
Using \fBdp_hash\fR has the advantage that it does not require the
generated datapath flows to exact match any additional packet header
@@ -2155,9 +2150,23 @@ when needed, and a second match is required to match some bits of its
value. This double-matching incurs a small additional latency cost
for each packet, but this latency is orders of magnitude less than the
latency of creating new datapath flows for new TCP connections.
+.IP \fBhash\fR
+Use a hash computed over the fields specified with the \fBfields\fR
+option, see below. If no hash fields are specified, \fBhash\fR defaults
+to a symmetric hash over the combination of MAC addresses, VLAN tags,
+Ether type, IP addresses and L4 port numbers. \fBhash\fR uses the
+\fBselection_method_param\fR as the hash basis.
+.IP
+Note that the hashed fields become exact matched by the datapath
+flows. For example, if the TCP source port is hashed, the created
+datapath flows will match the specific TCP source port value present
+in the packet received. Since each TCP connection generally has a
+different source port value, a separate datapath flow will be need to
+be inserted for each TCP connection thus hashed to a select group
+bucket.
.RE
.IP
-This option will use a Netronome OpenFlow extension which is only supported
+This option uses a Netronome OpenFlow extension which is only supported
when using Open vSwitch 2.4 and later with OpenFlow 1.5 and later.
.IP \fBselection_method_param\fR=\fIparam\fR
@@ -2167,7 +2176,7 @@ lower-layer that implements the \fBselection_method\fR. It is optional if
the \fBselection_method\fR field is specified as a non-empty string.
Prohibited otherwise. The default value is zero.
.IP
-This option will use a Netronome OpenFlow extension which is only supported
+This option uses a Netronome OpenFlow extension which is only supported
when using Open vSwitch 2.4 and later with OpenFlow 1.5 and later.
.IP \fBfields\fR=\fIfield\fR