diff mbox series

[ovs-dev,v6,1/3] netdev-linux: Use ethtool to detect offload support.

Message ID 20231031195138.2178690-1-mkp@redhat.com
State Changes Requested
Delegated to: Ilya Maximets
Headers show
Series [ovs-dev,v6,1/3] netdev-linux: Use ethtool to detect offload support. | expand

Checks

Context Check Description
ovsrobot/apply-robot success apply and check: success
ovsrobot/github-robot-_Build_and_Test success github build: passed
ovsrobot/intel-ovs-compilation success test: success

Commit Message

Mike Pattrick Oct. 31, 2023, 7:51 p.m. UTC
Currently when userspace-tso is enabled, netdev-linux interfaces will
indicate support for all offload flags regardless of interface
configuration. This patch checks for which offload features are enabled
during netdev construction.

Signed-off-by: Mike Pattrick <mkp@redhat.com>

--

v6:
 - Removed legacy comment
 - Reverse xmas
---
 lib/netdev-linux.c      | 146 ++++++++++++++++++++++++++++++++++++++--
 tests/ofproto-macros.at |   1 +
 2 files changed, 140 insertions(+), 7 deletions(-)

--
2.39.3

Comments

Simon Horman Nov. 14, 2023, 1:14 p.m. UTC | #1
On Tue, Oct 31, 2023 at 03:51:36PM -0400, Mike Pattrick wrote:
> Currently when userspace-tso is enabled, netdev-linux interfaces will
> indicate support for all offload flags regardless of interface
> configuration. This patch checks for which offload features are enabled
> during netdev construction.
> 
> Signed-off-by: Mike Pattrick <mkp@redhat.com>
> 
> --
> 
> v6:
>  - Removed legacy comment
>  - Reverse xmas

Thanks for the updates.
This patch looks good to me.

Acked-by: Simon Horman <horms@ovn.org>

For the record, this entire series seems ready to me.
But I think it is appropriate for Ilya to have the option to review the
series.
Ilya Maximets Nov. 16, 2023, 10:42 p.m. UTC | #2
On 10/31/23 20:51, Mike Pattrick wrote:
> Currently when userspace-tso is enabled, netdev-linux interfaces will
> indicate support for all offload flags regardless of interface
> configuration. This patch checks for which offload features are enabled
> during netdev construction.
> 
> Signed-off-by: Mike Pattrick <mkp@redhat.com>
> 
> --
> 
> v6:
>  - Removed legacy comment
>  - Reverse xmas
> ---
>  lib/netdev-linux.c      | 146 ++++++++++++++++++++++++++++++++++++++--
>  tests/ofproto-macros.at |   1 +
>  2 files changed, 140 insertions(+), 7 deletions(-)
> 
> diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c
> index cca340879..a46f5127f 100644
> --- a/lib/netdev-linux.c
> +++ b/lib/netdev-linux.c
> @@ -558,6 +558,7 @@ static bool netdev_linux_miimon_enabled(void);
>  static void netdev_linux_miimon_run(void);
>  static void netdev_linux_miimon_wait(void);
>  static int netdev_linux_get_mtu__(struct netdev_linux *netdev, int *mtup);
> +static void netdev_linux_set_ol(struct netdev *netdev);
> 
>  static bool
>  is_tap_netdev(const struct netdev *netdev)
> @@ -959,14 +960,8 @@ netdev_linux_construct(struct netdev *netdev_)
>          return error;
>      }
> 
> -    /* The socket interface doesn't offer the option to enable only
> -     * csum offloading without TSO. */

This comment seems to be still sort of valid.  i.e. we're only calling the
netdev_linux_set_ol() when TSO is enabled, because if we call it just for
the checksum, we'll get TSO flags as well.  Maybe just re-phrase it instead
of removing?  Or maybe even keep as is?

>      if (userspace_tso_enabled()) {
> -        netdev_->ol_flags |= NETDEV_TX_OFFLOAD_TCP_TSO;
> -        netdev_->ol_flags |= NETDEV_TX_OFFLOAD_TCP_CKSUM;
> -        netdev_->ol_flags |= NETDEV_TX_OFFLOAD_UDP_CKSUM;
> -        netdev_->ol_flags |= NETDEV_TX_OFFLOAD_SCTP_CKSUM;
> -        netdev_->ol_flags |= NETDEV_TX_OFFLOAD_IPV4_CKSUM;
> +        netdev_linux_set_ol(netdev_);
>      }
> 
>      error = get_flags(&netdev->up, &netdev->ifi_flags);
> @@ -2381,6 +2376,143 @@ netdev_internal_get_stats(const struct netdev *netdev_,
>      return error;
>  }
> 
> +static int
> +netdev_linux_read_stringset_info(struct netdev_linux *netdev, uint32_t *len)
> +{
> +    union {
> +        struct ethtool_sset_info hdr;
> +        struct {
> +            uint64_t pad[2];
> +            uint32_t sset_len[1];
> +        };
> +    } sset_info;
> +    int error;
> +
> +    sset_info.hdr.cmd = ETHTOOL_GSSET_INFO;
> +    sset_info.hdr.reserved = 0;
> +    sset_info.hdr.sset_mask = 1ULL << ETH_SS_FEATURES;
> +
> +    error = netdev_linux_do_ethtool(netdev->up.name,

netdev_get_name(&netdev->up)

> +            (struct ethtool_cmd *)&sset_info,

'''
Put a space between the ``()`` used in a cast and the expression
whose type is cast: ``(void *) 0``.
'''

> +            ETHTOOL_GSSET_INFO, "ETHTOOL_GSSET_INFO");

Please, indent to the level of '('.

> +    if (error) {
> +        return error;
> +    }
> +    *len = sset_info.sset_len[0];

According to the ethtool API, the sset_mask is also an output
parameter.  We should probably check that our ETH_SS_FEATURES
flag is set there.  If not, the access above may be invalid.

> +    return 0;
> +}
> +
> +
> +static int
> +netdev_linux_read_definitions(struct netdev_linux *netdev,
> +                              struct ethtool_gstrings **pstrings)
> +{
> +    struct ethtool_gstrings *strings = NULL;
> +    uint32_t len = 0;
> +    int error = 0;
> +
> +    error = netdev_linux_read_stringset_info(netdev, &len);
> +    if (error || !len) {
> +        return error;
> +    }
> +    strings = xcalloc(1, sizeof(*strings) + len * ETH_GSTRING_LEN);

This should be just xzalloc.
And don't parenthesize the argument of sizeof.

> +    if (!strings) {
> +        return ENOMEM;

xcalloc can't fail, hence the 'x'.

> +    }
> +
> +    strings->cmd = ETHTOOL_GSTRINGS;
> +    strings->string_set = ETH_SS_FEATURES;
> +    strings->len = len;
> +    error = netdev_linux_do_ethtool(netdev->up.name,
> +            (struct ethtool_cmd *) strings,
> +            ETHTOOL_GSTRINGS, "ETHTOOL_GSTRINGS");

Indentation.

> +    if (error) {
> +        goto out;
> +    }
> +
> +    for (int i = 0; i < len; i++) {
> +        strings->data[(i + 1) * ETH_GSTRING_LEN - 1] = 0;
> +    }
> +
> +    *pstrings = strings;
> +
> +    return 0;
> +out:
> +    *pstrings = NULL;
> +    free(strings);
> +    return error;
> +}
> +
> +static void
> +netdev_linux_set_ol(struct netdev *netdev_)
> +{
> +    struct netdev_linux *netdev = netdev_linux_cast(netdev_);
> +    struct ethtool_gfeatures *features = NULL;
> +    struct ethtool_gstrings *names = NULL;
> +    int error;
> +
> +    COVERAGE_INC(netdev_get_ethtool);
> +
> +    error = netdev_linux_read_definitions(netdev, &names);
> +    if (error) {
> +        return;
> +    }
> +
> +    features = xmalloc(sizeof *features +
> +                       DIV_ROUND_UP(names->len, 32) *
> +                       sizeof features->features[0]);
> +    if (!features) {
> +        goto out;

xmalloc can't fail.  Should it be xzalloc just to be sure?

> +    }
> +
> +    features->cmd = ETHTOOL_GFEATURES;
> +    features->size = DIV_ROUND_UP(names->len, 32);
> +    error = netdev_linux_do_ethtool(netdev_get_name(netdev_),
> +            (struct ethtool_cmd *) features,
> +            ETHTOOL_GFEATURES, "ETHTOOL_GFEATURES");

Indentation.

> +
> +    if (error) {
> +        goto out;
> +    }
> +
> +#define FEATURE_WORD(blocks, index, field)  ((blocks)[(index) / 32U].field)
> +#define FEATURE_FIELD_FLAG(index)       (1U << (index) % 32U)
> +#define FEATURE_BIT_IS_SET(blocks, index, field)        \
> +    (FEATURE_WORD(blocks, index, field) & FEATURE_FIELD_FLAG(index))
> +
> +    netdev->up.ol_flags = 0;
> +    static const struct {
> +        char * string;

Extra space between '*' and the 'string'.

> +        int value;

Should value be uint32_t instead?  ol_flags is uint32_t.

> +    } t_list[] = {
> +        {"tx-checksum-ipv4", NETDEV_TX_OFFLOAD_IPV4_CKSUM |
> +                             NETDEV_TX_OFFLOAD_TCP_CKSUM |
> +                             NETDEV_TX_OFFLOAD_UDP_CKSUM},
> +        {"tx-checksum-ipv6", NETDEV_TX_OFFLOAD_TCP_CKSUM |
> +                             NETDEV_TX_OFFLOAD_UDP_CKSUM},
> +        {"tx-checksum-ip-generic", NETDEV_TX_OFFLOAD_IPV4_CKSUM |
> +                                   NETDEV_TX_OFFLOAD_TCP_CKSUM |
> +                                   NETDEV_TX_OFFLOAD_UDP_CKSUM},
> +        {"tx-checksum-sctp", NETDEV_TX_OFFLOAD_SCTP_CKSUM},
> +        {"tx-tcp-segmentation", NETDEV_TX_OFFLOAD_TCP_TSO},
> +    };
> +
> +    for (int i = 0; i < names->len; i++) {
> +        char * name = (char *) names->data + i * ETH_GSTRING_LEN;

Extra space  ----^

> +        for (int j = 0; j < sizeof t_list / sizeof t_list[0]; j++) {

ARRAY_SIZE(t_list)

> +            if (strcmp(t_list[j].string, name) == 0) {
> +                if (FEATURE_BIT_IS_SET(features->features, i, active)) {
> +                    netdev_->ol_flags |= t_list[j].value;

Above if statements could be combined with &&.
Also, shouldn't we break here?

If we break, we may also consider changing the order of nested loops,
i.e. iterate over t_list and look for a value in the names->data.
There is no need to serach for values we're not insterested in.

> +                }
> +            }
> +        }
> +    }
> +
> +out:
> +    free(names);
> +    free(features);
> +}
> +
>  static void
>  netdev_linux_read_features(struct netdev_linux *netdev)
>  {
> diff --git a/tests/ofproto-macros.at b/tests/ofproto-macros.at
> index d2e6ac768..5a7b7a6e7 100644
> --- a/tests/ofproto-macros.at
> +++ b/tests/ofproto-macros.at
> @@ -260,6 +260,7 @@ check_logs () {
>  /ovs_rcu.*blocked [[0-9]]* ms waiting for .* to quiesce/d
>  /Dropped [[0-9]]* log messages/d
>  /setting extended ack support failed/d
> +/ETHTOOL_GSSET_INFO/d
>  /|WARN|/p
>  /|ERR|/p
>  /|EMER|/p" ${logs}
> --
diff mbox series

Patch

diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c
index cca340879..a46f5127f 100644
--- a/lib/netdev-linux.c
+++ b/lib/netdev-linux.c
@@ -558,6 +558,7 @@  static bool netdev_linux_miimon_enabled(void);
 static void netdev_linux_miimon_run(void);
 static void netdev_linux_miimon_wait(void);
 static int netdev_linux_get_mtu__(struct netdev_linux *netdev, int *mtup);
+static void netdev_linux_set_ol(struct netdev *netdev);

 static bool
 is_tap_netdev(const struct netdev *netdev)
@@ -959,14 +960,8 @@  netdev_linux_construct(struct netdev *netdev_)
         return error;
     }

-    /* The socket interface doesn't offer the option to enable only
-     * csum offloading without TSO. */
     if (userspace_tso_enabled()) {
-        netdev_->ol_flags |= NETDEV_TX_OFFLOAD_TCP_TSO;
-        netdev_->ol_flags |= NETDEV_TX_OFFLOAD_TCP_CKSUM;
-        netdev_->ol_flags |= NETDEV_TX_OFFLOAD_UDP_CKSUM;
-        netdev_->ol_flags |= NETDEV_TX_OFFLOAD_SCTP_CKSUM;
-        netdev_->ol_flags |= NETDEV_TX_OFFLOAD_IPV4_CKSUM;
+        netdev_linux_set_ol(netdev_);
     }

     error = get_flags(&netdev->up, &netdev->ifi_flags);
@@ -2381,6 +2376,143 @@  netdev_internal_get_stats(const struct netdev *netdev_,
     return error;
 }

+static int
+netdev_linux_read_stringset_info(struct netdev_linux *netdev, uint32_t *len)
+{
+    union {
+        struct ethtool_sset_info hdr;
+        struct {
+            uint64_t pad[2];
+            uint32_t sset_len[1];
+        };
+    } sset_info;
+    int error;
+
+    sset_info.hdr.cmd = ETHTOOL_GSSET_INFO;
+    sset_info.hdr.reserved = 0;
+    sset_info.hdr.sset_mask = 1ULL << ETH_SS_FEATURES;
+
+    error = netdev_linux_do_ethtool(netdev->up.name,
+            (struct ethtool_cmd *)&sset_info,
+            ETHTOOL_GSSET_INFO, "ETHTOOL_GSSET_INFO");
+    if (error) {
+        return error;
+    }
+    *len = sset_info.sset_len[0];
+    return 0;
+}
+
+
+static int
+netdev_linux_read_definitions(struct netdev_linux *netdev,
+                              struct ethtool_gstrings **pstrings)
+{
+    struct ethtool_gstrings *strings = NULL;
+    uint32_t len = 0;
+    int error = 0;
+
+    error = netdev_linux_read_stringset_info(netdev, &len);
+    if (error || !len) {
+        return error;
+    }
+    strings = xcalloc(1, sizeof(*strings) + len * ETH_GSTRING_LEN);
+    if (!strings) {
+        return ENOMEM;
+    }
+
+    strings->cmd = ETHTOOL_GSTRINGS;
+    strings->string_set = ETH_SS_FEATURES;
+    strings->len = len;
+    error = netdev_linux_do_ethtool(netdev->up.name,
+            (struct ethtool_cmd *) strings,
+            ETHTOOL_GSTRINGS, "ETHTOOL_GSTRINGS");
+    if (error) {
+        goto out;
+    }
+
+    for (int i = 0; i < len; i++) {
+        strings->data[(i + 1) * ETH_GSTRING_LEN - 1] = 0;
+    }
+
+    *pstrings = strings;
+
+    return 0;
+out:
+    *pstrings = NULL;
+    free(strings);
+    return error;
+}
+
+static void
+netdev_linux_set_ol(struct netdev *netdev_)
+{
+    struct netdev_linux *netdev = netdev_linux_cast(netdev_);
+    struct ethtool_gfeatures *features = NULL;
+    struct ethtool_gstrings *names = NULL;
+    int error;
+
+    COVERAGE_INC(netdev_get_ethtool);
+
+    error = netdev_linux_read_definitions(netdev, &names);
+    if (error) {
+        return;
+    }
+
+    features = xmalloc(sizeof *features +
+                       DIV_ROUND_UP(names->len, 32) *
+                       sizeof features->features[0]);
+    if (!features) {
+        goto out;
+    }
+
+    features->cmd = ETHTOOL_GFEATURES;
+    features->size = DIV_ROUND_UP(names->len, 32);
+    error = netdev_linux_do_ethtool(netdev_get_name(netdev_),
+            (struct ethtool_cmd *) features,
+            ETHTOOL_GFEATURES, "ETHTOOL_GFEATURES");
+
+    if (error) {
+        goto out;
+    }
+
+#define FEATURE_WORD(blocks, index, field)  ((blocks)[(index) / 32U].field)
+#define FEATURE_FIELD_FLAG(index)       (1U << (index) % 32U)
+#define FEATURE_BIT_IS_SET(blocks, index, field)        \
+    (FEATURE_WORD(blocks, index, field) & FEATURE_FIELD_FLAG(index))
+
+    netdev->up.ol_flags = 0;
+    static const struct {
+        char * string;
+        int value;
+    } t_list[] = {
+        {"tx-checksum-ipv4", NETDEV_TX_OFFLOAD_IPV4_CKSUM |
+                             NETDEV_TX_OFFLOAD_TCP_CKSUM |
+                             NETDEV_TX_OFFLOAD_UDP_CKSUM},
+        {"tx-checksum-ipv6", NETDEV_TX_OFFLOAD_TCP_CKSUM |
+                             NETDEV_TX_OFFLOAD_UDP_CKSUM},
+        {"tx-checksum-ip-generic", NETDEV_TX_OFFLOAD_IPV4_CKSUM |
+                                   NETDEV_TX_OFFLOAD_TCP_CKSUM |
+                                   NETDEV_TX_OFFLOAD_UDP_CKSUM},
+        {"tx-checksum-sctp", NETDEV_TX_OFFLOAD_SCTP_CKSUM},
+        {"tx-tcp-segmentation", NETDEV_TX_OFFLOAD_TCP_TSO},
+    };
+
+    for (int i = 0; i < names->len; i++) {
+        char * name = (char *) names->data + i * ETH_GSTRING_LEN;
+        for (int j = 0; j < sizeof t_list / sizeof t_list[0]; j++) {
+            if (strcmp(t_list[j].string, name) == 0) {
+                if (FEATURE_BIT_IS_SET(features->features, i, active)) {
+                    netdev_->ol_flags |= t_list[j].value;
+                }
+            }
+        }
+    }
+
+out:
+    free(names);
+    free(features);
+}
+
 static void
 netdev_linux_read_features(struct netdev_linux *netdev)
 {
diff --git a/tests/ofproto-macros.at b/tests/ofproto-macros.at
index d2e6ac768..5a7b7a6e7 100644
--- a/tests/ofproto-macros.at
+++ b/tests/ofproto-macros.at
@@ -260,6 +260,7 @@  check_logs () {
 /ovs_rcu.*blocked [[0-9]]* ms waiting for .* to quiesce/d
 /Dropped [[0-9]]* log messages/d
 /setting extended ack support failed/d
+/ETHTOOL_GSSET_INFO/d
 /|WARN|/p
 /|ERR|/p
 /|EMER|/p" ${logs}