2
0
mirror of https://github.com/openvswitch/ovs synced 2025-08-22 01:51:26 +00:00

netdev: Add netdev_get_speed() to netdev API.

Currently, the netdev's speed is being calculated by taking the link's
feature bits (using netdev_get_features()) and transforming them into
bps.

This mechanism can be both inaccurate and difficult to maintain, mainly
because we currently use the feature bits supported by OpenFlow which
would have to be extended to support all new feature bits of all netdev
implementations while keeping the OpenFlow API intact.

In order to expose the link speed accurately for all current and future
hardware, add a new netdev API call that allows the implementations to
provide the current and maximum link speeds in Mbps.

Internally, the logic to get the maximum supported speed still relies on
feature bits so it might still get out of sync in the future. However,
the maximum configurable speed is not used as much as the current speed
and these feature bits are not exposed through the netdev interface so
it should be easier to add more.

Use this new function instead of netdev_get_features() where the link
speed is needed.

As a consequence of this patch, link speeds of cards is properly
reported (internally in OVSDB) even if not supported by OpenFlow.
A test verifies this behavior using a tap device.

Also, in order to avoid using the old, this patch adds a checkpatch.py
warning if the old API is used.

Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=2137567
Acked-by: Eelco Chaudron <echaudro@redhat.com>
Signed-off-by: Adrian Moreno <amorenoz@redhat.com>
Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
This commit is contained in:
Adrian Moreno 2023-07-17 10:08:11 +02:00 committed by Ilya Maximets
parent 1ef3f4f78a
commit 6240c0b4c8
14 changed files with 228 additions and 28 deletions

View File

@ -132,6 +132,7 @@ int netdev_get_features(const struct netdev *,
enum netdev_features *advertised,
enum netdev_features *supported,
enum netdev_features *peer);
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);

View File

@ -91,7 +91,9 @@
*
* - Carrier status (netdev_get_carrier()).
*
* - Speed (netdev_get_features()).
* - Link features (netdev_get_features()).
*
* - Speed (netdev_get_speed()).
*
* - QoS queue configuration (netdev_get_queue(), netdev_set_queue() and
* related functions.)

View File

@ -1168,6 +1168,27 @@ cleanup:
return error;
}
static int
netdev_bsd_get_speed(const struct netdev *netdev, uint32_t *current,
uint32_t *max)
{
enum netdev_features f_current, f_supported, f_advertised, f_peer;
int error;
error = netdev_bsd_get_features(netdev, &f_current, &f_advertised,
&f_supported, &f_peer);
if (error) {
return error;
}
*current = MIN(UINT32_MAX,
netdev_features_to_bps(f_current, 0) / 1000000ULL);
*max = MIN(UINT32_MAX,
netdev_features_to_bps(f_supported, 0) / 1000000ULL);
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
@ -1493,6 +1514,7 @@ netdev_bsd_update_flags(struct netdev *netdev_, enum netdev_flags off,
.get_carrier = netdev_bsd_get_carrier, \
.get_stats = netdev_bsd_get_stats, \
.get_features = netdev_bsd_get_features, \
.get_speed = netdev_bsd_get_speed, \
.set_in4 = netdev_bsd_set_in4, \
.get_addr_list = netdev_bsd_get_addr_list, \
.get_next_hop = netdev_bsd_get_next_hop, \

View File

@ -3686,6 +3686,57 @@ netdev_dpdk_get_features(const struct netdev *netdev,
return 0;
}
static int
netdev_dpdk_get_speed(const struct netdev *netdev, uint32_t *current,
uint32_t *max)
{
struct netdev_dpdk *dev = netdev_dpdk_cast(netdev);
struct rte_eth_dev_info dev_info;
struct rte_eth_link link;
ovs_mutex_lock(&dev->mutex);
link = dev->link;
rte_eth_dev_info_get(dev->port_id, &dev_info);
ovs_mutex_unlock(&dev->mutex);
*current = link.link_speed != RTE_ETH_SPEED_NUM_UNKNOWN
? link.link_speed : 0;
if (dev_info.speed_capa & RTE_ETH_LINK_SPEED_200G) {
*max = RTE_ETH_SPEED_NUM_200G;
} else if (dev_info.speed_capa & RTE_ETH_LINK_SPEED_100G) {
*max = RTE_ETH_SPEED_NUM_100G;
} else if (dev_info.speed_capa & RTE_ETH_LINK_SPEED_56G) {
*max = RTE_ETH_SPEED_NUM_56G;
} else if (dev_info.speed_capa & RTE_ETH_LINK_SPEED_50G) {
*max = RTE_ETH_SPEED_NUM_50G;
} else if (dev_info.speed_capa & RTE_ETH_LINK_SPEED_40G) {
*max = RTE_ETH_SPEED_NUM_40G;
} else if (dev_info.speed_capa & RTE_ETH_LINK_SPEED_25G) {
*max = RTE_ETH_SPEED_NUM_25G;
} else if (dev_info.speed_capa & RTE_ETH_LINK_SPEED_20G) {
*max = RTE_ETH_SPEED_NUM_20G;
} else if (dev_info.speed_capa & RTE_ETH_LINK_SPEED_10G) {
*max = RTE_ETH_SPEED_NUM_10G;
} else if (dev_info.speed_capa & RTE_ETH_LINK_SPEED_5G) {
*max = RTE_ETH_SPEED_NUM_5G;
} else if (dev_info.speed_capa & RTE_ETH_LINK_SPEED_2_5G) {
*max = RTE_ETH_SPEED_NUM_2_5G;
} else if (dev_info.speed_capa & RTE_ETH_LINK_SPEED_1G) {
*max = RTE_ETH_SPEED_NUM_1G;
} else if (dev_info.speed_capa & RTE_ETH_LINK_SPEED_100M ||
dev_info.speed_capa & RTE_ETH_LINK_SPEED_100M_HD) {
*max = RTE_ETH_SPEED_NUM_100M;
} else if (dev_info.speed_capa & RTE_ETH_LINK_SPEED_10M ||
dev_info.speed_capa & RTE_ETH_LINK_SPEED_10M_HD) {
*max = RTE_ETH_SPEED_NUM_10M;
} else {
*max = 0;
}
return 0;
}
static struct ingress_policer *
netdev_dpdk_policer_construct(uint32_t rate, uint32_t burst)
{
@ -6332,6 +6383,7 @@ parse_vhost_config(const struct smap *ovs_other_config)
.get_stats = netdev_dpdk_get_stats, \
.get_custom_stats = netdev_dpdk_get_custom_stats, \
.get_features = netdev_dpdk_get_features, \
.get_speed = netdev_dpdk_get_speed, \
.get_status = netdev_dpdk_get_status, \
.reconfigure = netdev_dpdk_reconfigure, \
.rxq_recv = netdev_dpdk_rxq_recv

View File

@ -92,6 +92,7 @@ struct netdev_linux {
enum netdev_features current; /* Cached from ETHTOOL_GSET. */
enum netdev_features advertised; /* Cached from ETHTOOL_GSET. */
enum netdev_features supported; /* Cached from ETHTOOL_GSET. */
uint32_t current_speed; /* Cached from ETHTOOL_GSET. */
struct ethtool_drvinfo drvinfo; /* Cached from ETHTOOL_GDRVINFO. */
struct tc *tc;

View File

@ -2382,7 +2382,6 @@ static void
netdev_linux_read_features(struct netdev_linux *netdev)
{
struct ethtool_cmd ecmd;
uint32_t speed;
int error;
if (netdev->cache_valid & VALID_FEATURES) {
@ -2496,20 +2495,20 @@ netdev_linux_read_features(struct netdev_linux *netdev)
}
/* Current settings. */
speed = ethtool_cmd_speed(&ecmd);
if (speed == SPEED_10) {
netdev->current_speed = ethtool_cmd_speed(&ecmd);
if (netdev->current_speed == SPEED_10) {
netdev->current = ecmd.duplex ? NETDEV_F_10MB_FD : NETDEV_F_10MB_HD;
} else if (speed == SPEED_100) {
} else if (netdev->current_speed == SPEED_100) {
netdev->current = ecmd.duplex ? NETDEV_F_100MB_FD : NETDEV_F_100MB_HD;
} else if (speed == SPEED_1000) {
} else if (netdev->current_speed == SPEED_1000) {
netdev->current = ecmd.duplex ? NETDEV_F_1GB_FD : NETDEV_F_1GB_HD;
} else if (speed == SPEED_10000) {
} else if (netdev->current_speed == SPEED_10000) {
netdev->current = NETDEV_F_10GB_FD;
} else if (speed == 40000) {
} else if (netdev->current_speed == 40000) {
netdev->current = NETDEV_F_40GB_FD;
} else if (speed == 100000) {
} else if (netdev->current_speed == 100000) {
netdev->current = NETDEV_F_100GB_FD;
} else if (speed == 1000000) {
} else if (netdev->current_speed == 1000000) {
netdev->current = NETDEV_F_1TB_FD;
} else {
netdev->current = 0;
@ -2563,6 +2562,33 @@ exit:
return error;
}
static int
netdev_linux_get_speed(const struct netdev *netdev_, uint32_t *current,
uint32_t *max)
{
struct netdev_linux *netdev = netdev_linux_cast(netdev_);
int error;
ovs_mutex_lock(&netdev->mutex);
if (netdev_linux_netnsid_is_remote(netdev)) {
error = EOPNOTSUPP;
goto exit;
}
netdev_linux_read_features(netdev);
if (!netdev->get_features_error) {
*current = netdev->current_speed == SPEED_UNKNOWN
? 0 : netdev->current_speed;
*max = MIN(UINT32_MAX,
netdev_features_to_bps(netdev->supported, 0) / 1000000ULL);
}
error = netdev->get_features_error;
exit:
ovs_mutex_unlock(&netdev->mutex);
return error;
}
/* Set the features advertised by 'netdev' to 'advertise'. */
static int
netdev_linux_set_advertisements(struct netdev *netdev_,
@ -3697,6 +3723,7 @@ const struct netdev_class netdev_linux_class = {
.destruct = netdev_linux_destruct,
.get_stats = netdev_linux_get_stats,
.get_features = netdev_linux_get_features,
.get_speed = netdev_linux_get_speed,
.get_status = netdev_linux_get_status,
.get_block_id = netdev_linux_get_block_id,
.send = netdev_linux_send,
@ -3713,6 +3740,7 @@ const struct netdev_class netdev_tap_class = {
.destruct = netdev_linux_destruct,
.get_stats = netdev_tap_get_stats,
.get_features = netdev_linux_get_features,
.get_speed = netdev_linux_get_speed,
.get_status = netdev_linux_get_status,
.send = netdev_linux_send,
.rxq_construct = netdev_linux_rxq_construct,

View File

@ -500,6 +500,15 @@ struct netdev_class {
enum netdev_features *supported,
enum netdev_features *peer);
/* Stores the current and maximum supported link speed by 'netdev' into
* each of '*current' and '*max'. Each value represents the speed in Mbps.
* If any of the speeds is unknown, a zero value must be stored.
*
* This function may be set to null if it would always return EOPNOTSUPP.
*/
int (*get_speed)(const struct netdev *netdev, uint32_t *current,
uint32_t *max);
/* Set the features advertised by 'netdev' to 'advertise', which is a
* set of NETDEV_F_* bits.
*

View File

@ -1158,6 +1158,36 @@ netdev_get_features(const struct netdev *netdev,
return error;
}
int
netdev_get_speed(const struct netdev *netdev, uint32_t *current, uint32_t *max)
{
uint32_t current_dummy, max_dummy;
int error;
if (!current) {
current = &current_dummy;
}
if (!max) {
max = &max_dummy;
}
error = netdev->netdev_class->get_speed
? netdev->netdev_class->get_speed(netdev, current, max)
: EOPNOTSUPP;
if (error == EOPNOTSUPP) {
enum netdev_features current_f, supported_f;
error = netdev_get_features(netdev, &current_f, NULL,
&supported_f, NULL);
*current = netdev_features_to_bps(current_f, 0) / 1000000;
*max = netdev_features_to_bps(supported_f, 0) / 1000000;
} else if (error) {
*current = *max = 0;
}
return error;
}
/* Returns the maximum speed of a network connection that has the NETDEV_F_*
* bits in 'features', in bits per second. If no bits that indicate a speed
* are set in 'features', returns 'default_bps'. */

View File

@ -306,6 +306,7 @@ sflow_agent_get_counters(void *ds_, SFLPoller *poller,
struct netdev_stats stats;
enum netdev_flags flags;
struct lacp_member_stats lacp_stats;
uint32_t curr_speed;
const char *ifName;
dsp = dpif_sflow_find_port(ds, u32_to_odp(poller->bridgePort));
@ -320,13 +321,19 @@ sflow_agent_get_counters(void *ds_, SFLPoller *poller,
if (!netdev_get_features(dsp->ofport->netdev, &current, NULL, NULL, NULL)) {
/* The values of ifDirection come from MAU MIB (RFC 2668): 0 = unknown,
1 = full-duplex, 2 = half-duplex, 3 = in, 4=out */
counters->ifSpeed = netdev_features_to_bps(current, 0);
counters->ifDirection = (netdev_features_is_full_duplex(current)
? 1 : 2);
} else {
counters->ifSpeed = 100000000;
counters->ifDirection = 0;
}
netdev_get_speed(dsp->ofport->netdev, &curr_speed, NULL);
if (curr_speed) {
counters->ifSpeed = curr_speed * 1000000;
} else {
counters->ifSpeed = 100000000;
}
if (!netdev_get_flags(dsp->ofport->netdev, &flags) && flags & NETDEV_UP) {
counters->ifStatus = 1; /* ifAdminStatus up. */
if (netdev_get_carrier(dsp->ofport->netdev)) {

View File

@ -2476,6 +2476,7 @@ ofport_open(struct ofproto *ofproto,
struct ofputil_phy_port *pp,
struct netdev **p_netdev)
{
uint32_t curr_speed, max_speed;
enum netdev_flags flags;
struct netdev *netdev;
int error;
@ -2514,8 +2515,9 @@ ofport_open(struct ofproto *ofproto,
pp->state = netdev_get_carrier(netdev) ? 0 : OFPUTIL_PS_LINK_DOWN;
netdev_get_features(netdev, &pp->curr, &pp->advertised,
&pp->supported, &pp->peer);
pp->curr_speed = netdev_features_to_bps(pp->curr, 0) / 1000;
pp->max_speed = netdev_features_to_bps(pp->supported, 0) / 1000;
netdev_get_speed(netdev, &curr_speed, &max_speed);
pp->curr_speed = curr_speed * 1000;
pp->max_speed = max_speed * 1000;
*p_netdev = netdev;
return 0;

View File

@ -180,6 +180,9 @@ find_command tcpdump
# Set HAVE_LFTP
find_command lftp
# Set HAVE_ETHTOOL
find_command ethtool
CURL_OPT="-g -v --max-time 1 --retry 2 --retry-delay 1 --connect-timeout 1"
# Determine whether "diff" supports "normal" diffs. (busybox diff does not.)

View File

@ -122,3 +122,33 @@ AT_CHECK([ip link show | grep " genev_sys_[[0-9]]*: .* ovs-system " | diff -u -
OVS_TRAFFIC_VSWITCHD_STOP
AT_CLEANUP
AT_SETUP([interface - current speed])
AT_SKIP_IF([test $HAVE_ETHTOOL = "no"])
OVS_TRAFFIC_VSWITCHD_START()
AT_CHECK([ip tuntap add tap0 mode tap])
on_exit 'ip tuntap del tap0 mode tap'
AT_CHECK([ip link set dev tap0 address aa:55:aa:55:00:01])
AT_CHECK([ethtool -s tap0 speed 50000 duplex full])
AT_CHECK([ip link set dev tap0 up])
AT_CHECK([ovs-vsctl add-port br0 tap0 -- set int tap0 type=tap])
AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn dump-ports-desc br0 tap0], [0], [stdout])
AT_CHECK([strip_xids < stdout], [0], [dnl
OFPST_PORT_DESC reply (OF1.5):
1(tap0): addr:aa:55:aa:55:00:01
config: 0
state: LIVE
current: COPPER
speed: 50000 Mbps now, 0 Mbps max
])
AT_CHECK([ovs-vsctl get interface tap0 link_speed], [0], [dnl
50000000000
])
OVS_TRAFFIC_VSWITCHD_STOP
AT_CLEANUP

View File

@ -671,18 +671,23 @@ checks += [
easy_to_misuse_api = [
('ovsrcu_barrier',
'lib/ovs-rcu.c',
['lib/ovs-rcu.c'],
'Are you sure you need to use ovsrcu_barrier(), '
'in most cases ovsrcu_synchronize() will be fine?'),
('netdev_features_to_bps',
['lib/netdev.c', 'lib/netdev-bsd.c', 'lib/netdev-linux.c'],
'Are you sure you need to use netdev_features_to_bps()? '
'If you want to retrieve the current and/or maximum link speed, '
'consider using netdev_get_speed() instead.'),
]
checks += [
{'regex': r'(\.c)(\.in)?$',
'match_name': lambda x: x != location,
'match_name': lambda x, loc=locations: x not in loc,
'prereq': lambda x: not is_comment_line(x),
'check': regex_function_factory(function_name),
'print': regex_warn_factory(description)}
for (function_name, location, description) in easy_to_misuse_api]
for (function_name, locations, description) in easy_to_misuse_api]
def regex_operator_factory(operator):

View File

@ -1694,11 +1694,12 @@ port_configure_stp(const struct ofproto *ofproto, struct port *port,
if (config_str) {
port_s->path_cost = strtoul(config_str, NULL, 10);
} else {
enum netdev_features current;
unsigned int mbps;
uint32_t mbps;
netdev_get_features(iface->netdev, &current, NULL, NULL, NULL);
mbps = netdev_features_to_bps(current, NETDEV_DEFAULT_BPS) / 1000000;
netdev_get_speed(iface->netdev, &mbps, NULL);
if (!mbps) {
mbps = NETDEV_DEFAULT_BPS / 1000000;
}
port_s->path_cost = stp_convert_speed_to_cost(mbps);
}
@ -1777,11 +1778,12 @@ port_configure_rstp(const struct ofproto *ofproto, struct port *port,
if (config_str) {
port_s->path_cost = strtoul(config_str, NULL, 10);
} else {
enum netdev_features current;
unsigned int mbps;
uint32_t mbps;
netdev_get_features(iface->netdev, &current, NULL, NULL, NULL);
mbps = netdev_features_to_bps(current, NETDEV_DEFAULT_BPS) / 1000000;
netdev_get_speed(iface->netdev, &mbps, NULL);
if (!mbps) {
mbps = NETDEV_DEFAULT_BPS / 1000000;
}
port_s->path_cost = rstp_convert_speed_to_cost(mbps);
}
@ -2418,6 +2420,7 @@ iface_refresh_netdev_status(struct iface *iface)
struct eth_addr mac;
int64_t bps, mtu_64, ifindex64, link_resets;
int mtu, error;
uint32_t mbps;
if (iface_is_synthetic(iface)) {
return;
@ -2456,14 +2459,19 @@ iface_refresh_netdev_status(struct iface *iface)
ovsrec_interface_set_link_resets(iface->cfg, &link_resets, 1);
error = netdev_get_features(iface->netdev, &current, NULL, NULL, NULL);
bps = !error ? netdev_features_to_bps(current, 0) : 0;
if (bps) {
if (!error) {
ovsrec_interface_set_duplex(iface->cfg,
netdev_features_is_full_duplex(current)
? "full" : "half");
ovsrec_interface_set_link_speed(iface->cfg, &bps, 1);
} else {
ovsrec_interface_set_duplex(iface->cfg, NULL);
}
netdev_get_speed(iface->netdev, &mbps, NULL);
if (mbps) {
bps = mbps * 1000000ULL;
ovsrec_interface_set_link_speed(iface->cfg, &bps, 1);
} else {
ovsrec_interface_set_link_speed(iface->cfg, NULL, 0);
}