@@ -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,44 @@ 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 != htons(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];
+ }
+ 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 +2166,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();
}