@@ -720,6 +720,10 @@ struct ovs_action_push_vlan {
*/
enum ovs_hash_alg {
OVS_HASH_ALG_L4,
+#ifndef __KERNEL__
+ OVS_HASH_ALG_SYM_L4,
+#endif
+ __OVS_HASH_MAX
};
/*
@@ -2108,6 +2108,45 @@ flow_hash_symmetric_l4(const struct flow *flow, uint32_t basis)
return jhash_bytes(&fields, sizeof fields, basis);
}
+/* Symmetrically Hashes non-IP 'flow' based on its L2 headers. */
+uint32_t
+flow_hash_symmetric_l2(const struct flow *flow, uint32_t basis)
+{
+ union {
+ struct {
+ ovs_be16 eth_type;
+ ovs_be16 vlan_tci;
+ struct eth_addr eth_addr;
+ ovs_be16 pad;
+ };
+ uint32_t word[3];
+ } fields;
+
+ uint32_t hash = basis;
+ int i;
+
+ if (flow->packet_type != htonl(PT_ETH)) {
+ /* Cannot hash non-Ethernet flows */
+ return 0;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(fields.eth_addr.be16); i++) {
+ fields.eth_addr.be16[i] =
+ flow->dl_src.be16[i] ^ flow->dl_dst.be16[i];
+ }
+ fields.vlan_tci = 0;
+ for (i = 0; i < FLOW_MAX_VLAN_HEADERS; i++) {
+ fields.vlan_tci ^= flow->vlans[i].tci & htons(VLAN_VID_MASK);
+ }
+ fields.eth_type = flow->dl_type;
+ fields.pad = 0;
+
+ hash = hash_add(hash, fields.word[0]);
+ hash = hash_add(hash, fields.word[1]);
+ hash = hash_add(hash, fields.word[2]);
+ return hash_finish(hash, basis);
+}
+
/* Hashes 'flow' based on its L3 through L4 protocol information */
uint32_t
flow_hash_symmetric_l3l4(const struct flow *flow, uint32_t basis,
@@ -2128,8 +2167,8 @@ flow_hash_symmetric_l3l4(const struct flow *flow, uint32_t basis,
hash = hash_add64(hash, a[i] ^ b[i]);
}
} else {
- /* Cannot hash non-IP flows */
- return 0;
+ /* Revert to hashing L2 headers */
+ return flow_hash_symmetric_l2(flow, basis);
}
hash = hash_add(hash, flow->nw_proto);
@@ -236,6 +236,7 @@ hash_odp_port(odp_port_t odp_port)
uint32_t flow_hash_5tuple(const struct flow *flow, uint32_t basis);
uint32_t flow_hash_symmetric_l4(const struct flow *flow, uint32_t basis);
+uint32_t flow_hash_symmetric_l2(const struct flow *flow, uint32_t basis);
uint32_t flow_hash_symmetric_l3l4(const struct flow *flow, uint32_t basis,
bool inc_udp_ports );
@@ -726,14 +726,16 @@ odp_execute_actions(void *dp, struct dp_packet_batch *batch, bool steal,
}
switch ((enum ovs_action_attr) type) {
+
case OVS_ACTION_ATTR_HASH: {
const struct ovs_action_hash *hash_act = nl_attr_get(a);
- /* Calculate a hash value directly. This might not match the
+ /* Calculate a hash value directly. This might not match the
* value computed by the datapath, but it is much less expensive,
* and the current use case (bonding) does not require a strict
* match to work properly. */
- if (hash_act->hash_alg == OVS_HASH_ALG_L4) {
+ switch (hash_act->hash_alg) {
+ case OVS_HASH_ALG_L4: {
struct flow flow;
uint32_t hash;
@@ -749,7 +751,22 @@ odp_execute_actions(void *dp, struct dp_packet_batch *batch, bool steal,
}
packet->md.dp_hash = hash;
}
- } else {
+ break;
+ }
+ case OVS_HASH_ALG_SYM_L4: {
+ struct flow flow;
+ uint32_t hash;
+
+ DP_PACKET_BATCH_FOR_EACH (i, packet, batch) {
+ flow_extract(packet, &flow);
+ hash = flow_hash_symmetric_l3l4(&flow,
+ hash_act->hash_basis,
+ false);
+ packet->md.dp_hash = hash;
+ }
+ break;
+ }
+ default:
/* Assert on unknown hash algorithm. */
OVS_NOT_REACHED();
}
@@ -3968,10 +3968,15 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
struct ovs_action_hash *act_hash;
/* Hash action. */
+ enum ovs_hash_alg hash_alg = xr->hash_alg;
+ if (hash_alg > ctx->xbridge->support.max_hash_alg) {
+ /* Algorithm supported by all datapaths. */
+ hash_alg = OVS_HASH_ALG_L4;
+ }
act_hash = nl_msg_put_unspec_uninit(ctx->odp_actions,
OVS_ACTION_ATTR_HASH,
sizeof *act_hash);
- act_hash->hash_alg = xr->hash_alg;
+ act_hash->hash_alg = hash_alg;
act_hash->hash_basis = xr->hash_basis;
/* Recirc action. */
@@ -1291,6 +1291,50 @@ check_ct_clear(struct dpif_backer *backer)
return supported;
}
+/* Probe the highest dp_hash algorithm supported by the datapath. */
+static size_t
+check_max_dp_hash_alg(struct dpif_backer *backer)
+{
+ struct odputil_keybuf keybuf;
+ struct ofpbuf key;
+ struct flow flow;
+ struct ovs_action_hash *hash;
+ int max_alg = 0;
+
+ struct odp_flow_key_parms odp_parms = {
+ .flow = &flow,
+ .probe = true,
+ };
+
+ memset(&flow, 0, sizeof flow);
+ ofpbuf_use_stack(&key, &keybuf, sizeof keybuf);
+ odp_flow_key_from_flow(&odp_parms, &key);
+
+ /* All datapaths support algortithm 0 (OVS_HASH_ALG_L4). */
+ for (int alg = 1; alg < __OVS_HASH_MAX; alg++) {
+ struct ofpbuf actions;
+ bool ok;
+
+ ofpbuf_init(&actions, 300);
+ hash = nl_msg_put_unspec_uninit(&actions,
+ OVS_ACTION_ATTR_HASH, sizeof *hash);
+ hash->hash_basis = 0;
+ hash->hash_alg = alg;
+ ok = dpif_probe_feature(backer->dpif, "Max dp_hash algorithm", &key,
+ &actions, NULL);
+ ofpbuf_uninit(&actions);
+ if (ok) {
+ max_alg = alg;
+ } else {
+ break;
+ }
+ }
+
+ VLOG_INFO("%s: Max dp_hash algorithm probed to be %d",
+ dpif_name(backer->dpif), max_alg);
+ return max_alg;
+}
+
#define CHECK_FEATURE__(NAME, SUPPORT, FIELD, VALUE, ETHTYPE) \
static bool \
check_##NAME(struct dpif_backer *backer) \
@@ -1353,6 +1397,7 @@ check_support(struct dpif_backer *backer)
backer->rt_support.sample_nesting = check_max_sample_nesting(backer);
backer->rt_support.ct_eventmask = check_ct_eventmask(backer);
backer->rt_support.ct_clear = check_ct_clear(backer);
+ backer->rt_support.max_hash_alg = check_max_dp_hash_alg(backer);
/* Flow fields. */
backer->rt_support.odp.ct_state = check_ct_state(backer);
@@ -175,7 +175,10 @@ struct group_dpif *group_dpif_lookup(struct ofproto_dpif *,
DPIF_SUPPORT_FIELD(bool, ct_eventmask, "Conntrack eventmask") \
\
/* True if the datapath supports OVS_ACTION_ATTR_CT_CLEAR action. */ \
- DPIF_SUPPORT_FIELD(bool, ct_clear, "Conntrack clear")
+ DPIF_SUPPORT_FIELD(bool, ct_clear, "Conntrack clear") \
+ \
+ /* Highest supported dp_hash algorithm. */ \
+ DPIF_SUPPORT_FIELD(size_t, max_hash_alg, "Max dp_hash algorithm")
/* Stores the various features which the corresponding backer supports. */
struct dpif_backer_support {