@@ -262,3 +262,8 @@ PMDs in the case where no value is specified. By default "scalar" is used.
\fIstudy_cnt\fR defaults to 128 and indicates the number of packets that the
"study" miniflow implementation must parse before choosing an optimal
implementation.
+.IP "\fBdpif-netdev/offload-show\fR [\fIdp\fR] [\fInetdev\fR]"
+Prints the hardware offloading features enabled in netdev \fInetdev\fR
+attached to datapath \fIdp\fR. The datapath \fIdp\fR parameter can be
+omitted if there is only one. All netdev ports are printed if the
+parameter \fInetdev\fR is omitted.
@@ -1568,6 +1568,61 @@ dpif_netdev_bond_show(struct unixctl_conn *conn, int argc,
ds_destroy(&reply);
}
+static void
+dpif_netdev_offload_show(struct unixctl_conn *conn, int argc,
+ const char *argv[], void *aux OVS_UNUSED)
+{
+ struct ds reply = DS_EMPTY_INITIALIZER;
+ const char *netdev_name = NULL;
+ struct dp_netdev *dp = NULL;
+ struct dp_netdev_port *port;
+
+ ovs_mutex_lock(&dp_netdev_mutex);
+ if (argc == 3) {
+ dp = shash_find_data(&dp_netdevs, argv[1]);
+ netdev_name = argv[2];
+ } else if (argc == 2) {
+ dp = shash_find_data(&dp_netdevs, argv[1]);
+ if (!dp && shash_count(&dp_netdevs) == 1) {
+ /* There's only one datapath. */
+ dp = shash_first(&dp_netdevs)->data;
+ netdev_name = argv[1];
+ }
+ } else if (shash_count(&dp_netdevs) == 1) {
+ /* There's only one datapath. */
+ dp = shash_first(&dp_netdevs)->data;
+ }
+
+ if (!dp) {
+ ovs_mutex_unlock(&dp_netdev_mutex);
+ unixctl_command_reply_error(conn,
+ "please specify an existing datapath");
+ return;
+ }
+
+ ovs_rwlock_rdlock(&dp->port_rwlock);
+ HMAP_FOR_EACH (port, node, &dp->ports) {
+ if (netdev_name) {
+ /* find the port and dump the info */
+ if (!strcmp(netdev_get_name(port->netdev), netdev_name)) {
+ ds_put_format(&reply, "%s: ", netdev_get_name(port->netdev));
+ netdev_ol_flags_to_string(&reply, port->netdev);
+ ds_put_format(&reply, "\n");
+ break;
+ }
+ } else {
+ ds_put_format(&reply, "%s: ", netdev_get_name(port->netdev));
+ netdev_ol_flags_to_string(&reply, port->netdev);
+ ds_put_format(&reply, "\n");
+ }
+ }
+
+ ovs_rwlock_unlock(&dp->port_rwlock);
+ ovs_mutex_unlock(&dp_netdev_mutex);
+ unixctl_command_reply(conn, ds_cstr(&reply));
+ ds_destroy(&reply);
+}
+
static int
dpif_netdev_init(void)
@@ -1624,6 +1679,9 @@ dpif_netdev_init(void)
unixctl_command_register("dpif-netdev/miniflow-parser-get", "",
0, 0, dpif_miniflow_extract_impl_get,
NULL);
+ unixctl_command_register("dpif-netdev/offload-show", "[dp] [netdev]",
+ 0, 2, dpif_netdev_offload_show,
+ NULL);
return 0;
}
@@ -37,6 +37,7 @@ extern "C" {
struct netdev_tnl_build_header_params;
#define NETDEV_NUMA_UNSPEC OVS_NUMA_UNSPEC
+/* Keep this enum updated with translation to string below. */
enum netdev_ol_flags {
NETDEV_OFFLOAD_TX_IPV4_CSUM = 1 << 0,
NETDEV_OFFLOAD_TX_TCP_CSUM = 1 << 1,
@@ -45,6 +46,8 @@ enum netdev_ol_flags {
NETDEV_OFFLOAD_TX_TCP_TSO = 1 << 4,
};
+void netdev_ol_flags_to_string(struct ds *, const struct netdev *);
+
/* A network device (e.g. an Ethernet device).
*
* Network device implementations may read these members but should not modify
@@ -2298,3 +2298,38 @@ netdev_free_custom_stats_counters(struct netdev_custom_stats *custom_stats)
}
}
}
+
+void
+netdev_ol_flags_to_string(struct ds *string, const struct netdev *netdev)
+{
+ /* Sort by dependency, if any. */
+ if (netdev->ol_flags & NETDEV_OFFLOAD_TX_IPV4_CSUM) {
+ ds_put_format(string, "ip_csum: on, ");
+ } else {
+ ds_put_format(string, "ip_csum: off, ");
+ }
+
+ if (netdev->ol_flags & NETDEV_OFFLOAD_TX_TCP_CSUM) {
+ ds_put_format(string, "tcp_csum: on, ");
+ } else {
+ ds_put_format(string, "tcp_csum: off, ");
+ }
+
+ if (netdev->ol_flags & NETDEV_OFFLOAD_TX_UDP_CSUM) {
+ ds_put_format(string, "udp_csum: on, ");
+ } else {
+ ds_put_format(string, "udp_csum: off, ");
+ }
+
+ if (netdev->ol_flags & NETDEV_OFFLOAD_TX_SCTP_CSUM) {
+ ds_put_format(string, "sctp_csum: on, ");
+ } else {
+ ds_put_format(string, "sctp_csum: off, ");
+ }
+
+ if (netdev->ol_flags & NETDEV_OFFLOAD_TX_TCP_TSO) {
+ ds_put_format(string, "tso: on");
+ } else {
+ ds_put_format(string, "tso: off");
+ }
+}
@@ -636,6 +636,27 @@ OVS_VSWITCHD_STOP(["/flow: in_port is not an exact match/d
/failed to put/d"])
AT_CLEANUP
+AT_SETUP([dpif-netdev - check dpif-netdev/offload-show])
+OVS_VSWITCHD_START(
+ [add-port br0 p1 \
+ -- set interface p1 type=dummy options:pstream=punix:$OVS_RUNDIR/p0.sock \
+ -- set bridge br0 datapath-type=dummy \
+ other-config:datapath-id=1234 fail-mode=secure])
+
+AT_CHECK([ovs-appctl dpif-netdev/offload-show | sort], [0], [dnl
+br0: ip_csum: off, tcp_csum: off, udp_csum: off, sctp_csum: off, tso: off
+ovs-dummy: ip_csum: off, tcp_csum: off, udp_csum: off, sctp_csum: off, tso: off
+p1: ip_csum: off, tcp_csum: off, udp_csum: off, sctp_csum: off, tso: off
+], [])
+AT_CHECK([ovs-appctl dpif-netdev/offload-show p1], [0], [dnl
+p1: ip_csum: off, tcp_csum: off, udp_csum: off, sctp_csum: off, tso: off
+], [])
+AT_CHECK([ovs-appctl dpif-netdev/offload-show ovs-dummy p1], [0], [dnl
+p1: ip_csum: off, tcp_csum: off, udp_csum: off, sctp_csum: off, tso: off
+], [])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
# SEND_UDP_PKTS([p_name], [p_ofport])
#
# Sends 128 packets to port 'p_name' with different UDP destination ports.