{
        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,
        .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 */
        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);