Skip to content

Commit 6c212e6

Browse files
david-marchandigsilya
authored andcommitted
netdev: Fix full duplex capability for 25G ports.
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 Acked-by: Eelco Chaudron <[email protected]> Acked-by: Mike Pattrick <[email protected]> Signed-off-by: David Marchand <[email protected]> Signed-off-by: Ilya Maximets <[email protected]>
1 parent a940357 commit 6c212e6

File tree

10 files changed

+129
-10
lines changed

10 files changed

+129
-10
lines changed

include/openvswitch/netdev.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ int netdev_get_speed(const struct netdev *, uint32_t *current, uint32_t *max);
136136
uint64_t netdev_features_to_bps(enum netdev_features features,
137137
uint64_t default_bps);
138138
bool netdev_features_is_full_duplex(enum netdev_features features);
139+
int netdev_get_duplex(const struct netdev *, bool *full_duplex);
139140
int netdev_set_advertisements(struct netdev *, enum netdev_features advertise);
140141
void netdev_features_format(struct ds *, enum netdev_features);
141142

lib/netdev-bsd.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ struct netdev_bsd {
9797
enum netdev_features current;
9898
enum netdev_features advertised;
9999
enum netdev_features supported;
100+
bool full_duplex;
100101

101102
int tap_fd; /* TAP character device, if any, otherwise -1. */
102103

@@ -1155,6 +1156,7 @@ netdev_bsd_read_features(struct netdev_bsd *netdev)
11551156
if (!netdev->current) {
11561157
netdev->current = NETDEV_F_OTHER;
11571158
}
1159+
netdev->full_duplex = ifmr.ifm_active & IFM_FDX;
11581160

11591161
/* Advertised features. */
11601162
netdev->advertised = netdev_bsd_parse_media(ifmr.ifm_current);
@@ -1225,6 +1227,23 @@ netdev_bsd_get_speed(const struct netdev *netdev_, uint32_t *current,
12251227
return error;
12261228
}
12271229

1230+
static int
1231+
netdev_bsd_get_duplex(const struct netdev *netdev_, bool *full_duplex)
1232+
{
1233+
struct netdev_bsd *netdev = netdev_bsd_cast(netdev_);
1234+
int error;
1235+
1236+
ovs_mutex_lock(&netdev->mutex);
1237+
netdev_bsd_read_features(netdev);
1238+
if (!netdev->get_features_error) {
1239+
*full_duplex = netdev->full_duplex;
1240+
}
1241+
error = netdev->get_features_error;
1242+
ovs_mutex_unlock(&netdev->mutex);
1243+
1244+
return error;
1245+
}
1246+
12281247
/*
12291248
* Assigns 'addr' as 'netdev''s IPv4 address and 'mask' as its netmask. If
12301249
* 'addr' is INADDR_ANY, 'netdev''s IPv4 address is cleared. Returns a
@@ -1553,6 +1572,7 @@ netdev_bsd_update_flags(struct netdev *netdev_, enum netdev_flags off,
15531572
.get_stats = netdev_bsd_get_stats, \
15541573
.get_features = netdev_bsd_get_features, \
15551574
.get_speed = netdev_bsd_get_speed, \
1575+
.get_duplex = netdev_bsd_get_duplex, \
15561576
.set_in4 = netdev_bsd_set_in4, \
15571577
.get_addr_list = netdev_bsd_get_addr_list, \
15581578
.get_next_hop = netdev_bsd_get_next_hop, \

lib/netdev-dpdk.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4067,6 +4067,23 @@ netdev_dpdk_get_speed(const struct netdev *netdev, uint32_t *current,
40674067
return 0;
40684068
}
40694069

4070+
static int
4071+
netdev_dpdk_get_duplex(const struct netdev *netdev, bool *full_duplex)
4072+
{
4073+
struct netdev_dpdk *dev = netdev_dpdk_cast(netdev);
4074+
int err = 0;
4075+
4076+
ovs_mutex_lock(&dev->mutex);
4077+
if (dev->link.link_speed != RTE_ETH_SPEED_NUM_UNKNOWN) {
4078+
*full_duplex = dev->link.link_duplex == RTE_ETH_LINK_FULL_DUPLEX;
4079+
} else {
4080+
err = EOPNOTSUPP;
4081+
}
4082+
ovs_mutex_unlock(&dev->mutex);
4083+
4084+
return err;
4085+
}
4086+
40704087
static struct ingress_policer *
40714088
netdev_dpdk_policer_construct(uint32_t rate, uint32_t burst)
40724089
{
@@ -6749,6 +6766,7 @@ parse_vhost_config(const struct smap *ovs_other_config)
67496766
.get_custom_stats = netdev_dpdk_get_custom_stats, \
67506767
.get_features = netdev_dpdk_get_features, \
67516768
.get_speed = netdev_dpdk_get_speed, \
6769+
.get_duplex = netdev_dpdk_get_duplex, \
67526770
.get_status = netdev_dpdk_get_status, \
67536771
.reconfigure = netdev_dpdk_reconfigure, \
67546772
.rxq_recv = netdev_dpdk_rxq_recv

lib/netdev-linux-private.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ struct netdev_linux {
9494
enum netdev_features advertised; /* Cached from ETHTOOL_GSET. */
9595
enum netdev_features supported; /* Cached from ETHTOOL_GSET. */
9696
uint32_t current_speed; /* Cached from ETHTOOL_GSET. */
97+
uint8_t current_duplex; /* Cached from ETHTOOL_GSET. */
9798

9899
struct ethtool_drvinfo drvinfo; /* Cached from ETHTOOL_GDRVINFO. */
99100
struct tc *tc;

lib/netdev-linux.c

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,10 +174,13 @@ static inline uint32_t rpl_ethtool_cmd_speed(const struct ethtool_cmd *ep)
174174
#define ADVERTISED_10000baseR_FEC (1 << 20)
175175
#endif
176176

177-
/* Linux 3.2 introduced "unknown" speed. */
177+
/* Linux 3.2 introduced "unknown" speed and duplex. */
178178
#ifndef SPEED_UNKNOWN
179179
#define SPEED_UNKNOWN -1
180180
#endif
181+
#ifndef DUPLEX_UNKNOWN
182+
#define DUPLEX_UNKNOWN 0xff
183+
#endif
181184

182185
/* Linux 3.5 introduced supported and advertised flags for
183186
* 40G base KR4, CR4, SR4 and LR4. */
@@ -2699,6 +2702,7 @@ netdev_linux_read_features(struct netdev_linux *netdev)
26992702
} else {
27002703
netdev->current = 0;
27012704
}
2705+
netdev->current_duplex = ecmd.duplex;
27022706

27032707
if (ecmd.port == PORT_TP) {
27042708
netdev->current |= NETDEV_F_COPPER;
@@ -2782,6 +2786,32 @@ netdev_linux_get_speed(const struct netdev *netdev_, uint32_t *current,
27822786
return error;
27832787
}
27842788

2789+
static int
2790+
netdev_linux_get_duplex(const struct netdev *netdev_, bool *full_duplex)
2791+
{
2792+
struct netdev_linux *netdev = netdev_linux_cast(netdev_);
2793+
int err;
2794+
2795+
ovs_mutex_lock(&netdev->mutex);
2796+
2797+
if (netdev_linux_netnsid_is_remote(netdev)) {
2798+
err = EOPNOTSUPP;
2799+
goto exit;
2800+
}
2801+
2802+
netdev_linux_read_features(netdev);
2803+
err = netdev->get_features_error;
2804+
if (!err && netdev->current_duplex == DUPLEX_UNKNOWN) {
2805+
err = EOPNOTSUPP;
2806+
goto exit;
2807+
}
2808+
*full_duplex = netdev->current_duplex == DUPLEX_FULL;
2809+
2810+
exit:
2811+
ovs_mutex_unlock(&netdev->mutex);
2812+
return err;
2813+
}
2814+
27852815
/* Set the features advertised by 'netdev' to 'advertise'. */
27862816
static int
27872817
netdev_linux_set_advertisements(struct netdev *netdev_,
@@ -3905,6 +3935,7 @@ const struct netdev_class netdev_linux_class = {
39053935
.get_stats = netdev_linux_get_stats,
39063936
.get_features = netdev_linux_get_features,
39073937
.get_speed = netdev_linux_get_speed,
3938+
.get_duplex = netdev_linux_get_duplex,
39083939
.get_status = netdev_linux_get_status,
39093940
.get_block_id = netdev_linux_get_block_id,
39103941
.send = netdev_linux_send,
@@ -3922,6 +3953,7 @@ const struct netdev_class netdev_tap_class = {
39223953
.get_stats = netdev_tap_get_stats,
39233954
.get_features = netdev_linux_get_features,
39243955
.get_speed = netdev_linux_get_speed,
3956+
.get_duplex = netdev_linux_get_duplex,
39253957
.get_status = netdev_linux_get_status,
39263958
.send = netdev_linux_send,
39273959
.rxq_construct = netdev_linux_rxq_construct,

lib/netdev-provider.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,13 @@ struct netdev_class {
513513
int (*get_speed)(const struct netdev *netdev, uint32_t *current,
514514
uint32_t *max);
515515

516+
/* Stores the current duplex status of 'netdev' into '*full_duplex'.
517+
* 'true' means full duplex, 'false' means half duplex.
518+
*
519+
* This function may be set to null if it would always return EOPNOTSUPP.
520+
*/
521+
int (*get_duplex)(const struct netdev *netdev, bool *full_duplex);
522+
516523
/* Set the features advertised by 'netdev' to 'advertise', which is a
517524
* set of NETDEV_F_* bits.
518525
*

lib/netdev.c

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1295,6 +1295,37 @@ netdev_features_is_full_duplex(enum netdev_features features)
12951295
| NETDEV_F_100GB_FD | NETDEV_F_1TB_FD)) != 0;
12961296
}
12971297

1298+
/* Stores the duplex capability of 'netdev' into 'full_duplex'.
1299+
*
1300+
* Some network devices may not implement support for this function.
1301+
* In such cases this function will always return EOPNOTSUPP. */
1302+
int
1303+
netdev_get_duplex(const struct netdev *netdev, bool *full_duplex)
1304+
{
1305+
int error;
1306+
1307+
*full_duplex = false;
1308+
error = netdev->netdev_class->get_duplex
1309+
? netdev->netdev_class->get_duplex(netdev, full_duplex)
1310+
: EOPNOTSUPP;
1311+
1312+
if (error == EOPNOTSUPP) {
1313+
enum netdev_features current;
1314+
1315+
error = netdev_get_features(netdev, &current, NULL, NULL, NULL);
1316+
if (!error && (current & NETDEV_F_OTHER)) {
1317+
error = EOPNOTSUPP;
1318+
}
1319+
if (!error) {
1320+
*full_duplex = (current & (NETDEV_F_10MB_FD | NETDEV_F_100MB_FD
1321+
| NETDEV_F_1GB_FD | NETDEV_F_10GB_FD
1322+
| NETDEV_F_40GB_FD | NETDEV_F_100GB_FD
1323+
| NETDEV_F_1TB_FD)) != 0;
1324+
}
1325+
}
1326+
return error;
1327+
}
1328+
12981329
/* Set the features advertised by 'netdev' to 'advertise'. Returns 0 if
12991330
* successful, otherwise a positive errno value. */
13001331
int

ofproto/ofproto-dpif-sflow.c

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,6 @@ sflow_agent_get_counters(void *ds_, SFLPoller *poller,
299299
struct dpif_sflow *ds = ds_;
300300
SFLCounters_sample_element elem, lacp_elem, of_elem, name_elem;
301301
SFLCounters_sample_element eth_elem;
302-
enum netdev_features current;
303302
struct dpif_sflow_port *dsp;
304303
SFLIf_counters *counters;
305304
SFLEthernet_counters* eth_counters;
@@ -308,6 +307,7 @@ sflow_agent_get_counters(void *ds_, SFLPoller *poller,
308307
struct lacp_member_stats lacp_stats;
309308
uint32_t curr_speed;
310309
const char *ifName;
310+
bool full_duplex;
311311

312312
dsp = dpif_sflow_find_port(ds, u32_to_odp(poller->bridgePort));
313313
if (!dsp) {
@@ -318,11 +318,10 @@ sflow_agent_get_counters(void *ds_, SFLPoller *poller,
318318
counters = &elem.counterBlock.generic;
319319
counters->ifIndex = SFL_DS_INDEX(poller->dsi);
320320
counters->ifType = 6;
321-
if (!netdev_get_features(dsp->ofport->netdev, &current, NULL, NULL, NULL)) {
321+
if (!netdev_get_duplex(dsp->ofport->netdev, &full_duplex)) {
322322
/* The values of ifDirection come from MAU MIB (RFC 2668): 0 = unknown,
323323
1 = full-duplex, 2 = half-duplex, 3 = in, 4=out */
324-
counters->ifDirection = (netdev_features_is_full_duplex(current)
325-
? 1 : 2);
324+
counters->ifDirection = full_duplex ? 1 : 2;
326325
} else {
327326
counters->ifDirection = 0;
328327
}

tests/system-interface.at

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,5 +207,17 @@ AT_CHECK([ovs-vsctl get interface tap0 link_speed], [0], [dnl
207207
50000000000
208208
])
209209

210+
AT_CHECK([ovs-vsctl get interface tap0 duplex], [0], [dnl
211+
full
212+
])
213+
214+
AT_CHECK([ip link set dev tap0 down])
215+
AT_CHECK([ethtool -s tap0 duplex half])
216+
AT_CHECK([ip link set dev tap0 up])
217+
218+
AT_CHECK([ovs-vsctl get interface tap0 duplex], [0], [dnl
219+
half
220+
])
221+
210222
OVS_TRAFFIC_VSWITCHD_STOP
211223
AT_CLEANUP

vswitchd/bridge.c

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2460,11 +2460,11 @@ iface_refresh_netdev_status(struct iface *iface)
24602460
{
24612461
struct smap smap;
24622462

2463-
enum netdev_features current;
24642463
enum netdev_flags flags;
24652464
const char *link_state;
24662465
struct eth_addr mac;
24672466
int64_t bps, mtu_64, ifindex64, link_resets;
2467+
bool full_duplex;
24682468
int mtu, error;
24692469
uint32_t mbps;
24702470

@@ -2504,11 +2504,9 @@ iface_refresh_netdev_status(struct iface *iface)
25042504
link_resets = netdev_get_carrier_resets(iface->netdev);
25052505
ovsrec_interface_set_link_resets(iface->cfg, &link_resets, 1);
25062506

2507-
error = netdev_get_features(iface->netdev, &current, NULL, NULL, NULL);
2507+
error = netdev_get_duplex(iface->netdev, &full_duplex);
25082508
if (!error) {
2509-
ovsrec_interface_set_duplex(iface->cfg,
2510-
netdev_features_is_full_duplex(current)
2511-
? "full" : "half");
2509+
ovsrec_interface_set_duplex(iface->cfg, full_duplex ? "full" : "half");
25122510
} else {
25132511
ovsrec_interface_set_duplex(iface->cfg, NULL);
25142512
}

0 commit comments

Comments
 (0)