Skip to content

Commit

Permalink
veth: implement support for set_channel ethtool op
Browse files Browse the repository at this point in the history
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 <pabeni@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Paolo Abeni authored and David S. Miller committed Jul 20, 2021
1 parent dedd53c commit 4752eeb
Showing 1 changed file with 123 additions and 2 deletions.
125 changes: 123 additions & 2 deletions drivers/net/veth.c
Original file line number Diff line number Diff line change
Expand Up @@ -224,10 +224,13 @@ static void veth_get_channels(struct net_device *dev,
{
channels->tx_count = dev->real_num_tx_queues;
channels->rx_count = dev->real_num_rx_queues;
channels->max_tx = dev->real_num_tx_queues;
channels->max_rx = dev->real_num_rx_queues;
channels->max_tx = dev->num_tx_queues;
channels->max_rx = dev->num_rx_queues;
}

static int veth_set_channels(struct net_device *dev,
struct ethtool_channels *ch);

static const struct ethtool_ops veth_ethtool_ops = {
.get_drvinfo = veth_get_drvinfo,
.get_link = ethtool_op_get_link,
Expand All @@ -237,6 +240,7 @@ static const struct ethtool_ops veth_ethtool_ops = {
.get_link_ksettings = veth_get_link_ksettings,
.get_ts_info = ethtool_op_get_ts_info,
.get_channels = veth_get_channels,
.set_channels = veth_set_channels,
};

/* general routines */
Expand Down Expand Up @@ -1136,6 +1140,123 @@ static int veth_napi_enable(struct net_device *dev)
return veth_napi_enable_range(dev, 0, dev->real_num_rx_queues);
}

static void veth_disable_range_safe(struct net_device *dev, int start, int end)
{
struct veth_priv *priv = netdev_priv(dev);

if (start >= end)
return;

if (priv->_xdp_prog) {
veth_napi_del_range(dev, start, end);
veth_disable_xdp_range(dev, start, end, false);
} else if (veth_gro_requested(dev)) {
veth_napi_del_range(dev, start, end);
}
}

static int veth_enable_range_safe(struct net_device *dev, int start, int end)
{
struct veth_priv *priv = netdev_priv(dev);
int err;

if (start >= end)
return 0;

if (priv->_xdp_prog) {
/* these channels are freshly initialized, napi is not on there even
* when GRO is requeste
*/
err = veth_enable_xdp_range(dev, start, end, false);
if (err)
return err;

err = __veth_napi_enable_range(dev, start, end);
if (err) {
/* on error always delete the newly added napis */
veth_disable_xdp_range(dev, start, end, true);
return err;
}
} else if (veth_gro_requested(dev)) {
return veth_napi_enable_range(dev, start, end);
}
return 0;
}

static int veth_set_channels(struct net_device *dev,
struct ethtool_channels *ch)
{
struct veth_priv *priv = netdev_priv(dev);
unsigned int old_rx_count, new_rx_count;
struct veth_priv *peer_priv;
struct net_device *peer;
int err;

/* sanity check. Upper bounds are already enforced by the caller */
if (!ch->rx_count || !ch->tx_count)
return -EINVAL;

/* avoid braking XDP, if that is enabled */
peer = rtnl_dereference(priv->peer);
peer_priv = peer ? netdev_priv(peer) : NULL;
if (priv->_xdp_prog && peer && ch->rx_count < peer->real_num_tx_queues)
return -EINVAL;

if (peer && peer_priv && peer_priv->_xdp_prog && ch->tx_count > peer->real_num_rx_queues)
return -EINVAL;

old_rx_count = dev->real_num_rx_queues;
new_rx_count = ch->rx_count;
if (netif_running(dev)) {
/* turn device off */
netif_carrier_off(dev);
if (peer)
netif_carrier_off(peer);

/* try to allocate new resurces, as needed*/
err = veth_enable_range_safe(dev, old_rx_count, new_rx_count);
if (err)
goto out;
}

err = netif_set_real_num_rx_queues(dev, ch->rx_count);
if (err)
goto revert;

err = netif_set_real_num_tx_queues(dev, ch->tx_count);
if (err) {
int err2 = netif_set_real_num_rx_queues(dev, old_rx_count);

/* this error condition could happen only if rx and tx change
* in opposite directions (e.g. tx nr raises, rx nr decreases)
* and we can't do anything to fully restore the original
* status
*/
if (err2)
pr_warn("Can't restore rx queues config %d -> %d %d",
new_rx_count, old_rx_count, err2);
else
goto revert;
}

out:
if (netif_running(dev)) {
/* note that we need to swap the arguments WRT the enable part
* to identify the range we have to disable
*/
veth_disable_range_safe(dev, new_rx_count, old_rx_count);
netif_carrier_on(dev);
if (peer)
netif_carrier_on(peer);
}
return err;

revert:
new_rx_count = old_rx_count;
old_rx_count = ch->rx_count;
goto out;
}

static int veth_open(struct net_device *dev)
{
struct veth_priv *priv = netdev_priv(dev);
Expand Down

0 comments on commit 4752eeb

Please sign in to comment.