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 <m...@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} > -- _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev