+
+out:
+ ofpbuf_uninit(&buf);
+ return alt_actions;
}
static int
@@ -864,12 +934,24 @@ dummy_flow_put(const struct dpif_offload *offload_,
struct netdev *netdev,
}
memcpy(&off_flow->match, put->match, sizeof *put->match);
free(CONST_CAST(struct nlattr *, off_flow->actions));
+ ovsrcu_postpone(free, off_flow->alt_actions);
+ off_flow->alt_actions = NULL;
if (full_offload) {
off_flow->actions = xmemdup(put->actions, put->actions_len);
off_flow->actions_len = put->actions_len;
} else {
- off_flow->actions = NULL;
- off_flow->actions_len = 0;
+ off_flow->alt_actions = dummy_build_alt_actions(
+ offload_, put->match->flow.in_port.odp_port,
+ put->actions, put->actions_len);
+ if (off_flow->alt_actions) {
+ /* Partial action offload to test the alt_actions API. */
+ off_flow->actions = xmemdup(put->actions, put->actions_len);
+ off_flow->actions_len = put->actions_len;
+ } else {
+ /* Classic partial offload, i.e., only flow matching. */
+ off_flow->actions = NULL;
+ off_flow->actions_len = 0;
+ }
}
/* As we have per-netdev 'offloaded_flows', we don't need to match
@@ -1028,6 +1110,7 @@ dummy_netdev_simulate_offload(struct netdev *netdev,
struct dp_packet *packet,
struct dummy_offload_port *port;
bool packet_stolen = false;
struct flow packet_flow;
+ uint32_t existing_mark;
bool offloaded = false;
if (!dpif_offload_enabled() || !offload
@@ -1040,6 +1123,12 @@ dummy_netdev_simulate_offload(struct netdev *netdev,
struct dp_packet *packet,
return false;
}
+ /* If the packet already has a flow mark, it was re-injected, so let it
+ * continue to the CPU so hw_post_process can return alt_actions. */
+ if (dp_packet_has_flow_mark(packet, &existing_mark)) {
+ return false;
+ }
+
if (!flow) {
flow = &packet_flow;
flow_extract(packet, flow);
@@ -1072,9 +1161,6 @@ dummy_netdev_simulate_offload(struct netdev *netdev,
struct dp_packet *packet,
}
if (data->actions) {
- /* Perform hardware offload simulation. The packet is stolen
- * here and handed off to the PMD thread callback for
- * processing. */
struct hw_pkt_node *pkt_node = xmalloc(sizeof *pkt_node);
pkt_node->pkt = packet;
@@ -1124,7 +1210,7 @@ dummy_netdev_hw_offload_run(struct netdev *netdev)
LIST_FOR_EACH_POP (pkt_node, list_node, &port->hw_recv_queue) {
struct dummy_offloaded_flow *offloaded_flow;
struct dp_packet *pkt = pkt_node->pkt;
- bool processed = false;
+ int err = -ENOENT;
struct flow flow;
flow_extract(pkt, &flow);
@@ -1132,13 +1218,18 @@ dummy_netdev_hw_offload_run(struct netdev *netdev)
if (flow_equal_except(&flow, &offloaded_flow->match.flow,
&offloaded_flow->match.wc)) {
- processed = dummy_offload_hw_process_pkt(
- offload, offloaded_flow, pkt);
+ err = dummy_offload_hw_process_pkt(offload, offloaded_flow,
+ pkt);
break;
}
}
- if (!processed) {
+ if (err == -EAGAIN) {
+ /* We failed full processing as we have alt_actions. */
+ port->rx_offload_alt_actions++;
+ netdev_dummy_queue_simulate_offload_packet(
+ port->pm_port.netdev, pkt, pkt_node->queue_id);
+ } else if (err) {
VLOG_DBG("Failed HW pipeline, sent to sw!");
port->rx_offload_pipe_abort++;
netdev_dummy_queue_simulate_offload_packet(
diff --git a/tests/dpif-netdev.at b/tests/dpif-netdev.at
index 14f238e62..b14cc6d27 100644
--- a/tests/dpif-netdev.at
+++ b/tests/dpif-netdev.at
@@ -728,7 +728,7 @@ p1: flow del: mark: 1
AT_CHECK([ovs-appctl dpif-netdev/pmd-perf-show \
| grep -q "PHWOL hits: 0 "])
-# Verify two packets where received.
+# Verify two packets were received.
AT_CHECK([[[ $(ovs-pcap p2.pcap | grep -c "$packet_hex") -eq 2 ]]])
# Verify that we observe one miss, one packet processed by hardware,
@@ -737,6 +737,7 @@ AT_CHECK(
[ovs-appctl --format json dpif/offload/show \
| sed 's/.*"p1":{\([[^}]]*\)}.*/\1/; s/,/\n/g; s/"//g' \
| sed -n '/^rx_offload_/p' | sort], [0], [dnl
+rx_offload_alt_actions:0
rx_offload_full:1
rx_offload_miss:1
rx_offload_partial:0
@@ -759,6 +760,7 @@ AT_CHECK(
[ovs-appctl --format json dpif/offload/show \
| sed 's/.*"p1":{\([[^}]]*\)}.*/\1/; s/,/\n/g; s/"//g' \
| sed -n '/^rx_offload_/p' | sort], [0], [dnl
+rx_offload_alt_actions:0
rx_offload_full:2
rx_offload_miss:2
rx_offload_partial:0
@@ -768,6 +770,104 @@ rx_offload_pipe_abort:1
OVS_VSWITCHD_STOP
AT_CLEANUP
+AT_SETUP([dpif-netdev - full hw offload - alt actions - dummy-pmd])
+OVS_VSWITCHD_START(
+ [add-port br0 p1 -- \
+ add-port br0 p2 -- \
+ set interface br0 options:tx_pcap=br0.pcap -- \
+ set interface p1 type=dummy-pmd ofport_request=1 options:ifindex=1100 -- \
+ set interface p2 type=dummy-pmd ofport_request=2 options:ifindex=1200 \
+ options:tx_pcap=p2.pcap -- \
+ set bridge br0 datapath-type=dummy other-config:datapath-id=1234 \
+ fail-mode=secure], [], [], [--dummy-numa="0,0,0,0,1,1,1,1"])
+AT_CHECK([ovs-appctl vlog/set dpif:file:dbg dpif_netdev:file:dbg \
+ dpif_offload_dummy:file:dbg])
+
+AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:hw-offload=true])
+OVS_WAIT_UNTIL([grep "Flow HW offload is enabled" ovs-vswitchd.log])
+
+# Install a flow that outputs to both a dummy-pmd port (p2, offloadable
+# in hardware) and the bridge's internal LOCAL port (br0, dummy-internal,
+# not offloadable). Hardware handles the match; the dummy-pmd output is
+# treated as the HW-side action, while the LOCAL output is returned as
+# alt_actions for the CPU to execute.
+AT_CHECK([ovs-ofctl del-flows br0])
+AT_CHECK([ovs-ofctl add-flow br0 in_port=1,actions=p2,LOCAL])
+
+packet="\
+ eth_src=00:06:07:08:09:0a,eth_dst=00:01:02:03:04:05,\
+ udp,ip_src=127.0.0.1,ip_dst=127.0.0.1,nw_ttl=64,\
+ udp_src=54392,udp_dst=5201"
+packet_hex=$(ovs-ofctl compose-packet --bare "${packet}")
+
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 $packet_hex], [0])
+
+OVS_WAIT_UNTIL([grep "miss upcall" ovs-vswitchd.log])
+AT_CHECK([grep -A 1 'miss upcall' ovs-vswitchd.log | tail -n 1], [0], [dnl
+recirc_id(0),dp_hash(0),skb_priority(0),in_port(1),skb_mark(0),ct_state(0),ct_zone(0),ct_mark(0),ct_label(0),packet_type(ns=0,id=0),eth(src=00:06:07:08:09:0a,dst=00:01:02:03:04:05),eth_type(0x0800),ipv4(src=127.0.0.1,dst=127.0.0.1,proto=17,tos=0,ttl=64,frag=no),udp(src=54392,dst=5201)
+])
+
+# Check that flow successfully offloaded.
+OVS_WAIT_UNTIL([grep "succeed to add netdev flow" ovs-vswitchd.log])
+AT_CHECK([filter_hw_flow_install < ovs-vswitchd.log | strip_xout], [0], [dnl
+p1: flow put[[create]]: flow match:
recirc_id=0,eth,ip,in_port=1,vlan_tci=0x0000/0x1fff,nw_frag=no, mark: 1
+])
+
+# Check that datapath flow installed successfully.
+AT_CHECK([filter_flow_install < ovs-vswitchd.log | strip_xout], [0], [dnl
+recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no),
actions: <del>
+])
+
+# Inject the same packet again.
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 $packet_hex], [0])
+
+# Check for successful packet matching with installed offloaded flow.
+AT_CHECK([filter_hw_packet_netdev_dummy < ovs-vswitchd.log | strip_xout], [0],
[dnl
+p1: packet:
udp,vlan_tci=0x0000,dl_src=00:06:07:08:09:0a,dl_dst=00:01:02:03:04:05,nw_src=127.0.0.1,nw_dst=127.0.0.1,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=54392,tp_dst=5201
matches with flow: recirc_id=0,eth,ip,vlan_tci=0x0000/0x1fff,nw_frag=no with
mark: 1
+])
+
+# Dump the datapath flow to see that actions was executed for a packet.
+AT_CHECK([ovs-appctl dpctl/dump-flows -m | strip_hw_offload], [0], [dnl
+recirc_id(0),in_port(p1),packet_type(ns=0,id=0),eth(),eth_type(0x0800),ipv4(),udp(),
packets:2, bytes:212, used:0.0s, offloaded:yes, dp:dummy, actions:p2,br0
+])
+
+# Wait for datapath flow expiration.
+ovs-appctl time/stop
+ovs-appctl time/warp 15000
+ovs-appctl revalidator/wait
+
+# Check that flow successfully deleted from HW.
+OVS_WAIT_UNTIL([grep "succeed to delete netdev flow" ovs-vswitchd.log])
+AT_CHECK([filter_hw_flow_del < ovs-vswitchd.log | strip_xout], [0], [dnl
+p1: flow del: mark: 1
+])
+
+# Check if we hit partial hw offload.
+AT_CHECK([ovs-appctl dpif-netdev/pmd-perf-show \
+ | grep -q "PHWOL hits: 1 "])
+
+# Verify two packets were received.
+AT_CHECK([[[ $(ovs-pcap p2.pcap | grep -c "$packet_hex") -eq 2 ]]])
+
+# Verify we received the packet on br0.
+AT_CHECK([[[ $(ovs-pcap br0.pcap | grep -c "$packet_hex") -eq 2 ]]])
+
+# Verify offload statistics: one miss (first packet), one full (second
+# packet matched in HW), and one alt_actions return from hw_post_process.
+AT_CHECK(
+ [ovs-appctl --format json dpif/offload/show \
+ | sed 's/.*"p1":{\([[^}]]*\)}.*/\1/; s/,/\n/g; s/"//g' \
+ | sed -n '/^rx_offload_/p' | sort], [0], [dnl
+rx_offload_alt_actions:1
+rx_offload_full:1
+rx_offload_miss:1
+rx_offload_partial:0
+rx_offload_pipe_abort:0
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
AT_SETUP([dpif-netdev - full hw offload - tunnel entropy - dummy-pmd])
OVS_VSWITCHD_START(
[add-br br1 -- set bridge br1 datapath-type=dummy -- \
@@ -838,6 +938,7 @@ AT_CHECK(
[ovs-appctl --format json dpif/offload/show \
| sed 's/.*"p1":{\([[^}]]*\)}.*/\1/; s/,/\n/g; s/"//g' \
| sed -n '/^rx_offload_/p' | sort], [0], [dnl
+rx_offload_alt_actions:0
rx_offload_full:1
rx_offload_miss:1
rx_offload_partial:0
diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
index efb36e058..2cba6c81d 100644
--- a/tests/ofproto-dpif.at
+++ b/tests/ofproto-dpif.at
@@ -10485,18 +10485,21 @@ Globally enabled: false
Datapaths:
dummy@ovs-dummy:
dummy
- rx_offload_partial : 0
- rx_offload_full : 0
- rx_offload_miss : 0
- rx_offload_pipe_abort: 0
- rx_offload_partial : 0
- rx_offload_full : 0
- rx_offload_miss : 0
- rx_offload_pipe_abort: 0
- rx_offload_partial : 0
- rx_offload_full : 0
- rx_offload_miss : 0
- rx_offload_pipe_abort: 0
+ rx_offload_partial : 0
+ rx_offload_full : 0
+ rx_offload_miss : 0
+ rx_offload_pipe_abort : 0
+ rx_offload_alt_actions: 0
+ rx_offload_partial : 0
+ rx_offload_full : 0
+ rx_offload_miss : 0
+ rx_offload_pipe_abort : 0
+ rx_offload_alt_actions: 0
+ rx_offload_partial : 0
+ rx_offload_full : 0
+ rx_offload_miss : 0
+ rx_offload_pipe_abort : 0
+ rx_offload_alt_actions: 0
dummy_x
- br0: port_no: 100
- br1: port_no: 101
@@ -10514,18 +10517,21 @@ AT_CHECK([ovs-appctl --format json --pretty
dpif/offload/show], [0], [dnl
"ports": {
"br0": {
"port_no": 100,
+ "rx_offload_alt_actions": 0,
"rx_offload_full": 0,
"rx_offload_miss": 0,
"rx_offload_partial": 0,
"rx_offload_pipe_abort": 0},
"br1": {
"port_no": 101,
+ "rx_offload_alt_actions": 0,
"rx_offload_full": 0,
"rx_offload_miss": 0,
"rx_offload_partial": 0,
"rx_offload_pipe_abort": 0},
"ovs-dummy": {
"port_no": 0,
+ "rx_offload_alt_actions": 0,
"rx_offload_full": 0,
"rx_offload_miss": 0,
"rx_offload_partial": 0,
@@ -10561,18 +10567,21 @@ Globally enabled: false
Datapaths:
dummy@ovs-dummy:
dummy_x
- rx_offload_partial : 0
- rx_offload_full : 0
- rx_offload_miss : 0
- rx_offload_pipe_abort: 0
- rx_offload_partial : 0
- rx_offload_full : 0
- rx_offload_miss : 0
- rx_offload_pipe_abort: 0
- rx_offload_partial : 0
- rx_offload_full : 0
- rx_offload_miss : 0
- rx_offload_pipe_abort: 0
+ rx_offload_partial : 0
+ rx_offload_full : 0
+ rx_offload_miss : 0
+ rx_offload_pipe_abort : 0
+ rx_offload_alt_actions: 0
+ rx_offload_partial : 0
+ rx_offload_full : 0
+ rx_offload_miss : 0
+ rx_offload_pipe_abort : 0
+ rx_offload_alt_actions: 0
+ rx_offload_partial : 0
+ rx_offload_full : 0
+ rx_offload_miss : 0
+ rx_offload_pipe_abort : 0
+ rx_offload_alt_actions: 0
dummy
- br0: port_no: 100
- br1: port_no: 101
@@ -10592,18 +10601,21 @@ AT_CHECK([ovs-appctl --format json --pretty
dpif/offload/show], [0], [dnl
"ports": {
"br0": {
"port_no": 100,
+ "rx_offload_alt_actions": 0,
"rx_offload_full": 0,
"rx_offload_miss": 0,
"rx_offload_partial": 0,
"rx_offload_pipe_abort": 0},
"br1": {
"port_no": 101,
+ "rx_offload_alt_actions": 0,
"rx_offload_full": 0,
"rx_offload_miss": 0,
"rx_offload_partial": 0,
"rx_offload_pipe_abort": 0},
"ovs-dummy": {
"port_no": 0,
+ "rx_offload_alt_actions": 0,
"rx_offload_full": 0,
"rx_offload_miss": 0,
"rx_offload_partial": 0,
@@ -10640,18 +10652,21 @@ AT_CHECK([ovs-appctl --format json --pretty
dpif/offload/show], [0], [dnl
"ports": {
"br0": {
"port_no": 100,
+ "rx_offload_alt_actions": 0,
"rx_offload_full": 0,
"rx_offload_miss": 0,
"rx_offload_partial": 0,
"rx_offload_pipe_abort": 0},
"ovs-dummy": {
"port_no": 0,
+ "rx_offload_alt_actions": 0,
"rx_offload_full": 0,
"rx_offload_miss": 0,
"rx_offload_partial": 0,
"rx_offload_pipe_abort": 0},
"p4": {
"port_no": 4,
+ "rx_offload_alt_actions": 0,
"rx_offload_full": 0,
"rx_offload_miss": 0,
"rx_offload_partial": 0,
@@ -10660,12 +10675,14 @@ AT_CHECK([ovs-appctl --format json --pretty
dpif/offload/show], [0], [dnl
"ports": {
"p1": {
"port_no": 1,
+ "rx_offload_alt_actions": 0,
"rx_offload_full": 0,
"rx_offload_miss": 0,
"rx_offload_partial": 0,
"rx_offload_pipe_abort": 0},
"p3": {
"port_no": 3,
+ "rx_offload_alt_actions": 0,
"rx_offload_full": 0,
"rx_offload_miss": 0,
"rx_offload_partial": 0,
--
2.55.0