On 7/3/25 9:45 AM, David Marchand wrote:
> As OVS does not know of the 25G speed, matching on a list of known speed
> for deducing full duplex capability is broken.
>
> Add a new netdev op to retrieve the duplex status for the existing netdev
> implementations.
>
> Reported-at: https://issues.redhat.com/browse/FDP-883
> Signed-off-by: David Marchand <[email protected]>
> ---
> include/openvswitch/netdev.h | 2 +-
> lib/netdev-bsd.c | 31 ++++++++++++++++++++++++++-----
> lib/netdev-dpdk.c | 20 ++++++++++++++++++++
> lib/netdev-linux-private.h | 1 +
> lib/netdev-linux.c | 35 +++++++++++++++++++++++++++++++++++
> lib/netdev-provider.h | 7 +++++++
> lib/netdev.c | 30 +++++++++++++++++++++++-------
> ofproto/ofproto-dpif-sflow.c | 7 +++----
> tests/system-interface.at | 12 ++++++++++++
> vswitchd/bridge.c | 8 +++-----
> 10 files changed, 131 insertions(+), 22 deletions(-)
>
> diff --git a/include/openvswitch/netdev.h b/include/openvswitch/netdev.h
> index 83e8633dda..f79bfd1cdd 100644
> --- a/include/openvswitch/netdev.h
> +++ b/include/openvswitch/netdev.h
> @@ -135,7 +135,7 @@ int netdev_get_features(const struct netdev *,
> int netdev_get_speed(const struct netdev *, uint32_t *current, uint32_t
> *max);
> uint64_t netdev_features_to_bps(enum netdev_features features,
> uint64_t default_bps);
> -bool netdev_features_is_full_duplex(enum netdev_features features);
> +int netdev_get_duplex(const struct netdev *, bool *);
May want to give a name to the boolean, it's not clear what it means exactly.
> int netdev_set_advertisements(struct netdev *, enum netdev_features
> advertise);
> void netdev_features_format(struct ds *, enum netdev_features);
>
> diff --git a/lib/netdev-bsd.c b/lib/netdev-bsd.c
> index c7cd59376a..26e1286ac0 100644
> --- a/lib/netdev-bsd.c
> +++ b/lib/netdev-bsd.c
> @@ -92,6 +92,7 @@ struct netdev_bsd {
> struct eth_addr etheraddr;
> int mtu;
> int carrier;
> + bool full_duplex; /* Cached value, see netdev_bsd_get_features(). */
It's not really cached, we're calling the netdev_bsd_get_features()
every time.
>
> int tap_fd; /* TAP character device, if any, otherwise -1. */
>
> @@ -1107,10 +1108,11 @@ netdev_bsd_parse_media(int media)
> * passed-in values are set to 0.
> */
> static int
> -netdev_bsd_get_features(const struct netdev *netdev,
> +netdev_bsd_get_features(const struct netdev *netdev_,
> enum netdev_features *current, uint32_t *advertised,
> enum netdev_features *supported, uint32_t *peer)
> {
> + struct netdev_bsd *netdev = netdev_bsd_cast(netdev_);
> struct ifmediareq ifmr;
> int *media_list;
> int i;
> @@ -1120,7 +1122,7 @@ netdev_bsd_get_features(const struct netdev *netdev,
> /* XXX Look into SIOCGIFCAP instead of SIOCGIFMEDIA */
>
> memset(&ifmr, 0, sizeof(ifmr));
> - ovs_strlcpy(ifmr.ifm_name, netdev_get_name(netdev), sizeof
> ifmr.ifm_name);
> + ovs_strlcpy(ifmr.ifm_name, netdev_get_name(netdev_), sizeof
> ifmr.ifm_name);
>
> /* We make two SIOCGIFMEDIA ioctl calls. The first to determine the
> * number of supported modes, and a second with a buffer to retrieve
> @@ -1128,7 +1130,7 @@ netdev_bsd_get_features(const struct netdev *netdev,
> error = af_inet_ioctl(SIOCGIFMEDIA, &ifmr);
> if (error) {
> VLOG_DBG_RL(&rl, "%s: ioctl(SIOCGIFMEDIA) failed: %s",
> - netdev_get_name(netdev), ovs_strerror(error));
> + netdev_get_name(netdev_), ovs_strerror(error));
> return error;
> }
>
> @@ -1137,7 +1139,7 @@ netdev_bsd_get_features(const struct netdev *netdev,
>
> if (IFM_TYPE(ifmr.ifm_current) != IFM_ETHER) {
> VLOG_DBG_RL(&rl, "%s: doesn't appear to be ethernet",
> - netdev_get_name(netdev));
> + netdev_get_name(netdev_));
> error = EINVAL;
> goto cleanup;
> }
> @@ -1145,7 +1147,7 @@ netdev_bsd_get_features(const struct netdev *netdev,
> error = af_inet_ioctl(SIOCGIFMEDIA, &ifmr);
> if (error) {
> VLOG_DBG_RL(&rl, "%s: ioctl(SIOCGIFMEDIA) failed: %s",
> - netdev_get_name(netdev), ovs_strerror(error));
> + netdev_get_name(netdev_), ovs_strerror(error));
> goto cleanup;
> }
>
> @@ -1154,6 +1156,7 @@ netdev_bsd_get_features(const struct netdev *netdev,
> if (!*current) {
> *current = NETDEV_F_OTHER;
> }
> + netdev->full_duplex = ifmr.ifm_active & IFM_FDX;
Technically, we should be holding a lock for this and for the read.
It may be simpler to convert this into internal function with one more
return value and create a netdev_bsd_get_features() wrapper along the
existing netdev_bsd_get_speed() and the new netdev_bsd_get_duplex().
>
> /* Advertised features. */
> *advertised = netdev_bsd_parse_media(ifmr.ifm_current);
> @@ -1194,6 +1197,23 @@ netdev_bsd_get_speed(const struct netdev *netdev,
> uint32_t *current,
> return 0;
> }
>
> +static int
> +netdev_bsd_get_duplex(const struct netdev *netdev_, bool *full_duplex)
> +{
> + enum netdev_features f_current, f_supported, f_advertised, f_peer;
> + struct netdev_bsd *netdev = netdev_bsd_cast(netdev_);
> + int error;
> +
> + error = netdev_bsd_get_features(netdev_, &f_current, &f_advertised,
> + &f_supported, &f_peer);
> + if (error) {
> + return error;
> + }
> +
> + *full_duplex = netdev->full_duplex;
> + return 0;
> +}
> +
> /*
> * Assigns 'addr' as 'netdev''s IPv4 address and 'mask' as its netmask. If
> * 'addr' is INADDR_ANY, 'netdev''s IPv4 address is cleared. Returns a
> @@ -1522,6 +1542,7 @@ netdev_bsd_update_flags(struct netdev *netdev_, enum
> netdev_flags off,
> .get_stats = netdev_bsd_get_stats, \
> .get_features = netdev_bsd_get_features, \
> .get_speed = netdev_bsd_get_speed, \
> + .get_duplex = netdev_bsd_get_duplex, \
> .set_in4 = netdev_bsd_set_in4, \
> .get_addr_list = netdev_bsd_get_addr_list, \
> .get_next_hop = netdev_bsd_get_next_hop, \
> diff --git a/lib/netdev-dpdk.c b/lib/netdev-dpdk.c
> index 17b4d66779..5692111b32 100644
> --- a/lib/netdev-dpdk.c
> +++ b/lib/netdev-dpdk.c
> @@ -4177,6 +4177,25 @@ out:
> return 0;
> }
>
> +static int
> +netdev_dpdk_get_duplex(const struct netdev *netdev, bool *full_duplex)
> +{
> + struct netdev_dpdk *dev = netdev_dpdk_cast(netdev);
> + int err;
> +
> + ovs_mutex_lock(&dev->mutex);
> + if (dev->link.link_speed != RTE_ETH_SPEED_NUM_UNKNOWN) {
> + *full_duplex = dev->link.link_duplex == RTE_ETH_LINK_FULL_DUPLEX;
> + err = 0;
nit: May be better to just set the err to zero at the declaration point.
> + } else {
> + err = EOPNOTSUPP;
> + }
> + ovs_mutex_unlock(&dev->mutex);
> +
> +
One too many empty lines.
> + return err;
> +}
> +
> static struct ingress_policer *
> netdev_dpdk_policer_construct(uint32_t rate, uint32_t burst)
> {
> @@ -6889,6 +6908,7 @@ parse_vhost_config(const struct smap *ovs_other_config)
> .get_custom_stats = netdev_dpdk_get_custom_stats, \
> .get_features = netdev_dpdk_get_features, \
> .get_speed = netdev_dpdk_get_speed, \
> + .get_duplex = netdev_dpdk_get_duplex, \
> .get_status = netdev_dpdk_get_status, \
> .reconfigure = netdev_dpdk_reconfigure, \
> .rxq_recv = netdev_dpdk_rxq_recv
> diff --git a/lib/netdev-linux-private.h b/lib/netdev-linux-private.h
> index 8e572e3b3b..4b5b606755 100644
> --- a/lib/netdev-linux-private.h
> +++ b/lib/netdev-linux-private.h
> @@ -94,6 +94,7 @@ struct netdev_linux {
> enum netdev_features advertised; /* Cached from ETHTOOL_GSET. */
> enum netdev_features supported; /* Cached from ETHTOOL_GSET. */
> uint32_t current_speed; /* Cached from ETHTOOL_GSET. */
> + uint8_t current_duplex; /* Cached from ETHTOOL_GSET. */
>
> struct ethtool_drvinfo drvinfo; /* Cached from ETHTOOL_GDRVINFO. */
> struct tc *tc;
> diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c
> index 5dad2e7ed7..fb3cbf0597 100644
> --- a/lib/netdev-linux.c
> +++ b/lib/netdev-linux.c
> @@ -2682,6 +2682,7 @@ netdev_linux_read_features(struct netdev_linux *netdev)
> } else {
> netdev->current = NETDEV_F_OTHER;
> }
> + netdev->current_duplex = ecmd.duplex;
>
> if (ecmd.port == PORT_TP) {
> netdev->current |= NETDEV_F_COPPER;
> @@ -2765,6 +2766,38 @@ netdev_linux_get_speed(const struct netdev *netdev_,
> uint32_t *current,
> return error;
> }
>
> +static int
> +netdev_linux_get_duplex_locked(struct netdev_linux *netdev, bool
> *full_duplex)
> +{
> + int err;
> +
> + if (netdev_linux_netnsid_is_remote(netdev)) {
> + return EOPNOTSUPP;
> + }
> +
> + netdev_linux_read_features(netdev);
> + err = netdev->get_features_error;
> + if (!err && netdev->current_duplex == DUPLEX_UNKNOWN) {
> + err = EOPNOTSUPP;
> + }
> + if (!err) {
> + *full_duplex = netdev->current_duplex == DUPLEX_FULL;
> + }
> + return err;
> +}
> +
> +static int
> +netdev_linux_get_duplex(const struct netdev *netdev_, bool *full_duplex)
> +{
> + struct netdev_linux *netdev = netdev_linux_cast(netdev_);
> + int error;
> +
> + ovs_mutex_lock(&netdev->mutex);
> + error = netdev_linux_get_duplex_locked(netdev, full_duplex);
> + ovs_mutex_unlock(&netdev->mutex);
> + return error;
> +}
> +
> /* Set the features advertised by 'netdev' to 'advertise'. */
> static int
> netdev_linux_set_advertisements(struct netdev *netdev_,
> @@ -3889,6 +3922,7 @@ const struct netdev_class netdev_linux_class = {
> .get_stats = netdev_linux_get_stats,
> .get_features = netdev_linux_get_features,
> .get_speed = netdev_linux_get_speed,
> + .get_duplex = netdev_linux_get_duplex,
> .get_status = netdev_linux_get_status,
> .get_block_id = netdev_linux_get_block_id,
> .send = netdev_linux_send,
> @@ -3906,6 +3940,7 @@ const struct netdev_class netdev_tap_class = {
> .get_stats = netdev_tap_get_stats,
> .get_features = netdev_linux_get_features,
> .get_speed = netdev_linux_get_speed,
> + .get_duplex = netdev_linux_get_duplex,
> .get_status = netdev_linux_get_status,
> .send = netdev_linux_send,
> .rxq_construct = netdev_linux_rxq_construct,
> diff --git a/lib/netdev-provider.h b/lib/netdev-provider.h
> index 5ae3794699..33a68c49c2 100644
> --- a/lib/netdev-provider.h
> +++ b/lib/netdev-provider.h
> @@ -514,6 +514,13 @@ struct netdev_class {
> int (*get_speed)(const struct netdev *netdev, uint32_t *current,
> uint32_t *max);
>
> + /* Stores the current duplex status of 'netdev' into '*full_duplex'.
> + * 'true' means full duplex, 'false' means half duplex.
> + *
> + * This function may be set to null if it would always return EOPNOTSUPP.
> + */
> + int (*get_duplex)(const struct netdev *netdev, bool *full_duplex);
> +
> /* Set the features advertised by 'netdev' to 'advertise', which is a
> * set of NETDEV_F_* bits.
> *
> diff --git a/lib/netdev.c b/lib/netdev.c
> index df5b35232d..f32003a95c 100644
> --- a/lib/netdev.c
> +++ b/lib/netdev.c
> @@ -1288,14 +1288,30 @@ netdev_features_to_bps(enum netdev_features features,
> : default_bps);
> }
>
> -/* Returns true if any of the NETDEV_F_* bits that indicate a full-duplex
> link
> - * are set in 'features', otherwise false. */
I see that netdev_get_speed() is missing a description comment, but we shouldn't
follow a bad example. Should add a short description for the
netdev_get_duplex().
Also, do we need to make it always initialize the full_duplex argument? Other
functions do that. Might make sense to follow the same logic.
> -bool
> -netdev_features_is_full_duplex(enum netdev_features features)
> +int
> +netdev_get_duplex(const struct netdev *netdev, bool *full_duplex)
> {
> - return (features & (NETDEV_F_10MB_FD | NETDEV_F_100MB_FD |
> NETDEV_F_1GB_FD
> - | NETDEV_F_10GB_FD | NETDEV_F_40GB_FD
> - | NETDEV_F_100GB_FD | NETDEV_F_1TB_FD)) != 0;
> + int error;
> +
> + error = netdev->netdev_class->get_duplex
> + ? netdev->netdev_class->get_duplex(netdev, full_duplex)
> + : EOPNOTSUPP;
> +
> + if (error == EOPNOTSUPP) {
> + enum netdev_features current;
> +
> + error = netdev_get_features(netdev, ¤t, NULL, NULL, NULL);
> + if (!error && (current & NETDEV_F_OTHER)) {
> + error = EOPNOTSUPP;
> + }
> + if (!error) {
> + *full_duplex = (current & (NETDEV_F_10MB_FD | NETDEV_F_100MB_FD
> + | NETDEV_F_1GB_FD | NETDEV_F_10GB_FD
> + | NETDEV_F_40GB_FD |
> NETDEV_F_100GB_FD
> + | NETDEV_F_1TB_FD)) != 0;
> + }
> + }
> + return error;
> }
>
> /* Set the features advertised by 'netdev' to 'advertise'. Returns 0 if
> diff --git a/ofproto/ofproto-dpif-sflow.c b/ofproto/ofproto-dpif-sflow.c
> index c5403e27aa..e043d7cbc8 100644
> --- a/ofproto/ofproto-dpif-sflow.c
> +++ b/ofproto/ofproto-dpif-sflow.c
> @@ -298,7 +298,6 @@ sflow_agent_get_counters(void *ds_, SFLPoller *poller,
> struct dpif_sflow *ds = ds_;
> SFLCounters_sample_element elem, lacp_elem, of_elem, name_elem;
> SFLCounters_sample_element eth_elem;
> - enum netdev_features current;
> struct dpif_sflow_port *dsp;
> SFLIf_counters *counters;
> SFLEthernet_counters* eth_counters;
> @@ -307,6 +306,7 @@ sflow_agent_get_counters(void *ds_, SFLPoller *poller,
> struct lacp_member_stats lacp_stats;
> uint32_t curr_speed;
> const char *ifName;
> + bool full_duplex;
>
> dsp = dpif_sflow_find_port(ds, u32_to_odp(poller->bridgePort));
> if (!dsp) {
> @@ -317,11 +317,10 @@ sflow_agent_get_counters(void *ds_, SFLPoller *poller,
> counters = &elem.counterBlock.generic;
> counters->ifIndex = SFL_DS_INDEX(poller->dsi);
> counters->ifType = 6;
> - if (!netdev_get_features(dsp->ofport->netdev, ¤t, NULL, NULL,
> NULL)) {
> + if (!netdev_get_duplex(dsp->ofport->netdev, &full_duplex)) {
> /* The values of ifDirection come from MAU MIB (RFC 2668): 0 =
> unknown,
> 1 = full-duplex, 2 = half-duplex, 3 = in, 4=out */
> - counters->ifDirection = (netdev_features_is_full_duplex(current)
> - ? 1 : 2);
> + counters->ifDirection = full_duplex ? 1 : 2;
> } else {
> counters->ifDirection = 0;
> }
> diff --git a/tests/system-interface.at b/tests/system-interface.at
> index cb0835ad6b..15c4a0e2e1 100644
> --- a/tests/system-interface.at
> +++ b/tests/system-interface.at
> @@ -207,5 +207,17 @@ AT_CHECK([ovs-vsctl get interface tap0 link_speed], [0],
> [dnl
> 50000000000
> ])
>
> +AT_CHECK([ovs-vsctl get interface tap0 duplex], [0], [dnl
> +full
> +])
May be worth setting the duplex to full before checking. In case the
default value ever changes.
> +
> +AT_CHECK([ip link set dev tap0 down])
> +AT_CHECK([ethtool -s tap0 duplex half])
> +AT_CHECK([ip link set dev tap0 up])
> +
> +AT_CHECK([ovs-vsctl get interface tap0 duplex], [0], [dnl
> +half
> +])
> +
> OVS_TRAFFIC_VSWITCHD_STOP
> AT_CLEANUP
> diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c
> index 456b784c01..7b49d1b037 100644
> --- a/vswitchd/bridge.c
> +++ b/vswitchd/bridge.c
> @@ -2532,9 +2532,9 @@ iface_refresh_netdev_status(struct iface *iface)
> {
> struct smap smap;
>
> - enum netdev_features current;
> enum netdev_flags flags;
> const char *link_state;
> + bool full_duplex;
nit: The one line below breaks the reverse x-mass tree, but it's better
to at least keep the other ones in a relative order.
> struct eth_addr mac;
> int64_t bps, mtu_64, ifindex64, link_resets;
> int mtu, error;
> @@ -2576,11 +2576,9 @@ iface_refresh_netdev_status(struct iface *iface)
> link_resets = netdev_get_carrier_resets(iface->netdev);
> ovsrec_interface_set_link_resets(iface->cfg, &link_resets, 1);
>
> - error = netdev_get_features(iface->netdev, ¤t, NULL, NULL, NULL);
> + error = netdev_get_duplex(iface->netdev, &full_duplex);
> if (!error) {
> - ovsrec_interface_set_duplex(iface->cfg,
> - netdev_features_is_full_duplex(current)
> - ? "full" : "half");
> + ovsrec_interface_set_duplex(iface->cfg, full_duplex ? "full" :
> "half");
> } else {
> ovsrec_interface_set_duplex(iface->cfg, NULL);
> }
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev