static int netvsc_set_channels(struct net_device *net, struct ethtool_channels *channels) { struct net_device_context *net_device_ctx = netdev_priv(net); struct hv_device *dev = net_device_ctx->device_ctx; struct netvsc_device *nvdev = rtnl_dereference(net_device_ctx->nvdev); unsigned int count = channels->combined_count; bool was_running; int ret; /* We do not support separate count for rx, tx, or other */ if (count == 0 || channels->rx_count || channels->tx_count || channels->other_count) return -EINVAL; if (count > net->num_tx_queues || count > VRSS_CHANNEL_MAX) return -EINVAL; if (!nvdev || nvdev->destroy) return -ENODEV; if (nvdev->nvsp_version < NVSP_PROTOCOL_VERSION_5) return -EINVAL; if (count > nvdev->max_chn) return -EINVAL; was_running = netif_running(net); if (was_running) { ret = netvsc_close(net); if (ret) return ret; } rndis_filter_device_remove(dev, nvdev); ret = netvsc_set_queues(net, dev, count); if (ret == 0) nvdev->num_chn = count; else netvsc_set_queues(net, dev, nvdev->num_chn); if (was_running) ret = netvsc_open(net); /* We may have missed link change notifications */ schedule_delayed_work(&net_device_ctx->dwork, 0); return ret; }
static int netvsc_change_mtu(struct net_device *ndev, int mtu) { struct net_device_context *ndevctx = netdev_priv(ndev); struct netvsc_device *nvdev = ndevctx->nvdev; struct hv_device *hdev = ndevctx->device_ctx; struct netvsc_device_info device_info; int limit = ETH_DATA_LEN; u32 num_chn; int ret = 0; if (ndevctx->start_remove || !nvdev || nvdev->destroy) return -ENODEV; if (nvdev->nvsp_version >= NVSP_PROTOCOL_VERSION_2) limit = NETVSC_MTU - ETH_HLEN; if (mtu < NETVSC_MTU_MIN || mtu > limit) return -EINVAL; ret = netvsc_close(ndev); if (ret) goto out; num_chn = nvdev->num_chn; ndevctx->start_remove = true; rndis_filter_device_remove(hdev); ndev->mtu = mtu; memset(&device_info, 0, sizeof(device_info)); device_info.ring_size = ring_size; device_info.num_chn = num_chn; device_info.max_num_vrss_chns = max_num_vrss_chns; rndis_filter_device_add(hdev, &device_info); out: netvsc_open(ndev); ndevctx->start_remove = false; /* We may have missed link change notifications */ schedule_delayed_work(&ndevctx->dwork, 0); return ret; }
static int netvsc_change_mtu(struct net_device *ndev, int mtu) { struct net_device_context *ndevctx = netdev_priv(ndev); struct netvsc_device *nvdev = rtnl_dereference(ndevctx->nvdev); struct hv_device *hdev = ndevctx->device_ctx; struct netvsc_device_info device_info; bool was_running; int ret = 0; if (!nvdev || nvdev->destroy) return -ENODEV; was_running = netif_running(ndev); if (was_running) { ret = netvsc_close(ndev); if (ret) return ret; } memset(&device_info, 0, sizeof(device_info)); device_info.ring_size = ring_size; device_info.num_chn = nvdev->num_chn; device_info.max_num_vrss_chns = nvdev->num_chn; rndis_filter_device_remove(hdev, nvdev); /* 'nvdev' has been freed in rndis_filter_device_remove() -> * netvsc_device_remove () -> free_netvsc_device(). * We mustn't access it before it's re-created in * rndis_filter_device_add() -> netvsc_device_add(). */ ndev->mtu = mtu; rndis_filter_device_add(hdev, &device_info); if (was_running) ret = netvsc_open(ndev); /* We may have missed link change notifications */ schedule_delayed_work(&ndevctx->dwork, 0); return ret; }
static int netvsc_change_mtu(struct net_device *ndev, int mtu) { struct net_device_context *ndevctx = netdev_priv(ndev); struct hv_device *hdev = ndevctx->device_ctx; struct netvsc_device *nvdev = hv_get_drvdata(hdev); struct netvsc_device_info device_info; int limit = ETH_DATA_LEN; int ret = 0; if (nvdev == NULL || nvdev->destroy) return -ENODEV; if (nvdev->nvsp_version >= NVSP_PROTOCOL_VERSION_2) limit = NETVSC_MTU - ETH_HLEN; if (mtu < NETVSC_MTU_MIN || mtu > limit) return -EINVAL; ret = netvsc_close(ndev); if (ret) goto out; nvdev->start_remove = true; rndis_filter_device_remove(hdev); ndev->mtu = mtu; ndevctx->device_ctx = hdev; hv_set_drvdata(hdev, ndev); memset(&device_info, 0, sizeof(device_info)); device_info.ring_size = ring_size; device_info.num_chn = nvdev->num_chn; device_info.max_num_vrss_chns = max_num_vrss_chns; rndis_filter_device_add(hdev, &device_info); out: netvsc_open(ndev); return ret; }
static int netvsc_set_channels(struct net_device *net, struct ethtool_channels *channels) { struct net_device_context *net_device_ctx = netdev_priv(net); struct hv_device *dev = net_device_ctx->device_ctx; struct netvsc_device *nvdev = net_device_ctx->nvdev; struct netvsc_device_info device_info; u32 num_chn; u32 max_chn; int ret = 0; bool recovering = false; if (net_device_ctx->start_remove || !nvdev || nvdev->destroy) return -ENODEV; num_chn = nvdev->num_chn; max_chn = min_t(u32, nvdev->max_chn, num_online_cpus()); if (nvdev->nvsp_version < NVSP_PROTOCOL_VERSION_5) { pr_info("vRSS unsupported before NVSP Version 5\n"); return -EINVAL; } /* We do not support rx, tx, or other */ if (!channels || channels->rx_count || channels->tx_count || channels->other_count || (channels->combined_count < 1)) return -EINVAL; if (channels->combined_count > max_chn) { pr_info("combined channels too high, using %d\n", max_chn); channels->combined_count = max_chn; } ret = netvsc_close(net); if (ret) goto out; do_set: net_device_ctx->start_remove = true; rndis_filter_device_remove(dev); nvdev->num_chn = channels->combined_count; memset(&device_info, 0, sizeof(device_info)); device_info.num_chn = nvdev->num_chn; /* passed to RNDIS */ device_info.ring_size = ring_size; device_info.max_num_vrss_chns = max_num_vrss_chns; ret = rndis_filter_device_add(dev, &device_info); if (ret) { if (recovering) { netdev_err(net, "unable to add netvsc device (ret %d)\n", ret); return ret; } goto recover; } nvdev = net_device_ctx->nvdev; ret = netif_set_real_num_tx_queues(net, nvdev->num_chn); if (ret) { if (recovering) { netdev_err(net, "could not set tx queue count (ret %d)\n", ret); return ret; } goto recover; } ret = netif_set_real_num_rx_queues(net, nvdev->num_chn); if (ret) { if (recovering) { netdev_err(net, "could not set rx queue count (ret %d)\n", ret); return ret; } goto recover; } out: netvsc_open(net); net_device_ctx->start_remove = false; /* We may have missed link change notifications */ schedule_delayed_work(&net_device_ctx->dwork, 0); return ret; recover: /* If the above failed, we attempt to recover through the same * process but with the original number of channels. */ netdev_err(net, "could not set channels, recovering\n"); recovering = true; channels->combined_count = num_chn; goto do_set; }