Skip to content

Commit

Permalink
netdev-linux: Use ethtool to detect offload support.
Browse files Browse the repository at this point in the history
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 <[email protected]>
Acked-by: Simon Horman <[email protected]>
Signed-off-by: Ilya Maximets <[email protected]>
  • Loading branch information
mkp-rh authored and igsilya committed Dec 1, 2023
1 parent c8d4946 commit 6c59c19
Show file tree
Hide file tree
Showing 2 changed files with 144 additions and 7 deletions.
150 changes: 143 additions & 7 deletions lib/netdev-linux.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -959,14 +960,12 @@ 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;
/* The AF_PACKET socket interface uses the same option to facilitate
* both csum and segmentation offloading. However, these features can
* be toggled off or on individually at the interface level. The netdev
* flags are set based on the features indicated by ethtool. */
netdev_linux_set_ol(netdev_);
}

error = get_flags(&netdev->up, &netdev->ifi_flags);
Expand Down Expand Up @@ -2381,6 +2380,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_get_name(&netdev->up),
(struct ethtool_cmd *) &sset_info,
ETHTOOL_GSSET_INFO, "ETHTOOL_GSSET_INFO");
if (error) {
return error;
}
if (sset_info.hdr.sset_mask & (1ULL << ETH_SS_FEATURES)) {
*len = sset_info.sset_len[0];
return 0;
} else {
/* ETH_SS_FEATURES is not supported. */
return -EOPNOTSUPP;
}
}


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 = xzalloc(sizeof *strings + len * ETH_GSTRING_LEN);

strings->cmd = ETHTOOL_GSTRINGS;
strings->string_set = ETH_SS_FEATURES;
strings->len = len;
error = netdev_linux_do_ethtool(netdev_get_name(&netdev->up),
(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 = xzalloc(sizeof *features +
DIV_ROUND_UP(names->len, 32) *
sizeof features->features[0]);

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;
uint32_t 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 j = 0; j < ARRAY_SIZE(t_list); j++) {
for (int i = 0; i < names->len; i++) {
char *name = (char *) names->data + i * ETH_GSTRING_LEN;
if (strcmp(t_list[j].string, name) == 0) {
if (FEATURE_BIT_IS_SET(features->features, i, active)) {
netdev_->ol_flags |= t_list[j].value;
}
break;
}
}
}

out:
free(names);
free(features);
}

static void
netdev_linux_read_features(struct netdev_linux *netdev)
{
Expand Down
1 change: 1 addition & 0 deletions tests/ofproto-macros.at
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down

0 comments on commit 6c59c19

Please sign in to comment.