Skip to content

Commit 4752eeb

Browse files
Paolo Abenidavem330
authored andcommitted
veth: implement support for set_channel ethtool op
This change implements the set_channel() ethtool operation, preserving the current defaults values and allowing up set the number of queues in the range set ad device creation time. The update operation tries hard to leave the device in a consistent status in case of errors. RFC v1 -> RFC v2: - don't flip device status on set_channel() - roll-back the changes if possible on error - Jackub Signed-off-by: Paolo Abeni <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent dedd53c commit 4752eeb

File tree

1 file changed

+123
-2
lines changed

1 file changed

+123
-2
lines changed

drivers/net/veth.c

Lines changed: 123 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -224,10 +224,13 @@ static void veth_get_channels(struct net_device *dev,
224224
{
225225
channels->tx_count = dev->real_num_tx_queues;
226226
channels->rx_count = dev->real_num_rx_queues;
227-
channels->max_tx = dev->real_num_tx_queues;
228-
channels->max_rx = dev->real_num_rx_queues;
227+
channels->max_tx = dev->num_tx_queues;
228+
channels->max_rx = dev->num_rx_queues;
229229
}
230230

231+
static int veth_set_channels(struct net_device *dev,
232+
struct ethtool_channels *ch);
233+
231234
static const struct ethtool_ops veth_ethtool_ops = {
232235
.get_drvinfo = veth_get_drvinfo,
233236
.get_link = ethtool_op_get_link,
@@ -237,6 +240,7 @@ static const struct ethtool_ops veth_ethtool_ops = {
237240
.get_link_ksettings = veth_get_link_ksettings,
238241
.get_ts_info = ethtool_op_get_ts_info,
239242
.get_channels = veth_get_channels,
243+
.set_channels = veth_set_channels,
240244
};
241245

242246
/* general routines */
@@ -1136,6 +1140,123 @@ static int veth_napi_enable(struct net_device *dev)
11361140
return veth_napi_enable_range(dev, 0, dev->real_num_rx_queues);
11371141
}
11381142

1143+
static void veth_disable_range_safe(struct net_device *dev, int start, int end)
1144+
{
1145+
struct veth_priv *priv = netdev_priv(dev);
1146+
1147+
if (start >= end)
1148+
return;
1149+
1150+
if (priv->_xdp_prog) {
1151+
veth_napi_del_range(dev, start, end);
1152+
veth_disable_xdp_range(dev, start, end, false);
1153+
} else if (veth_gro_requested(dev)) {
1154+
veth_napi_del_range(dev, start, end);
1155+
}
1156+
}
1157+
1158+
static int veth_enable_range_safe(struct net_device *dev, int start, int end)
1159+
{
1160+
struct veth_priv *priv = netdev_priv(dev);
1161+
int err;
1162+
1163+
if (start >= end)
1164+
return 0;
1165+
1166+
if (priv->_xdp_prog) {
1167+
/* these channels are freshly initialized, napi is not on there even
1168+
* when GRO is requeste
1169+
*/
1170+
err = veth_enable_xdp_range(dev, start, end, false);
1171+
if (err)
1172+
return err;
1173+
1174+
err = __veth_napi_enable_range(dev, start, end);
1175+
if (err) {
1176+
/* on error always delete the newly added napis */
1177+
veth_disable_xdp_range(dev, start, end, true);
1178+
return err;
1179+
}
1180+
} else if (veth_gro_requested(dev)) {
1181+
return veth_napi_enable_range(dev, start, end);
1182+
}
1183+
return 0;
1184+
}
1185+
1186+
static int veth_set_channels(struct net_device *dev,
1187+
struct ethtool_channels *ch)
1188+
{
1189+
struct veth_priv *priv = netdev_priv(dev);
1190+
unsigned int old_rx_count, new_rx_count;
1191+
struct veth_priv *peer_priv;
1192+
struct net_device *peer;
1193+
int err;
1194+
1195+
/* sanity check. Upper bounds are already enforced by the caller */
1196+
if (!ch->rx_count || !ch->tx_count)
1197+
return -EINVAL;
1198+
1199+
/* avoid braking XDP, if that is enabled */
1200+
peer = rtnl_dereference(priv->peer);
1201+
peer_priv = peer ? netdev_priv(peer) : NULL;
1202+
if (priv->_xdp_prog && peer && ch->rx_count < peer->real_num_tx_queues)
1203+
return -EINVAL;
1204+
1205+
if (peer && peer_priv && peer_priv->_xdp_prog && ch->tx_count > peer->real_num_rx_queues)
1206+
return -EINVAL;
1207+
1208+
old_rx_count = dev->real_num_rx_queues;
1209+
new_rx_count = ch->rx_count;
1210+
if (netif_running(dev)) {
1211+
/* turn device off */
1212+
netif_carrier_off(dev);
1213+
if (peer)
1214+
netif_carrier_off(peer);
1215+
1216+
/* try to allocate new resurces, as needed*/
1217+
err = veth_enable_range_safe(dev, old_rx_count, new_rx_count);
1218+
if (err)
1219+
goto out;
1220+
}
1221+
1222+
err = netif_set_real_num_rx_queues(dev, ch->rx_count);
1223+
if (err)
1224+
goto revert;
1225+
1226+
err = netif_set_real_num_tx_queues(dev, ch->tx_count);
1227+
if (err) {
1228+
int err2 = netif_set_real_num_rx_queues(dev, old_rx_count);
1229+
1230+
/* this error condition could happen only if rx and tx change
1231+
* in opposite directions (e.g. tx nr raises, rx nr decreases)
1232+
* and we can't do anything to fully restore the original
1233+
* status
1234+
*/
1235+
if (err2)
1236+
pr_warn("Can't restore rx queues config %d -> %d %d",
1237+
new_rx_count, old_rx_count, err2);
1238+
else
1239+
goto revert;
1240+
}
1241+
1242+
out:
1243+
if (netif_running(dev)) {
1244+
/* note that we need to swap the arguments WRT the enable part
1245+
* to identify the range we have to disable
1246+
*/
1247+
veth_disable_range_safe(dev, new_rx_count, old_rx_count);
1248+
netif_carrier_on(dev);
1249+
if (peer)
1250+
netif_carrier_on(peer);
1251+
}
1252+
return err;
1253+
1254+
revert:
1255+
new_rx_count = old_rx_count;
1256+
old_rx_count = ch->rx_count;
1257+
goto out;
1258+
}
1259+
11391260
static int veth_open(struct net_device *dev)
11401261
{
11411262
struct veth_priv *priv = netdev_priv(dev);

0 commit comments

Comments
 (0)