Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: hv_netvsc: fix loss of early receive events from host during channel open.

The hv_netvsc driver currently enables NAPI after opening the primary and
subchannels. This ordering creates a race: if the Hyper-V host places data
in the host -> guest ring buffer and signals the channel before
napi_enable() has been called, the channel callback will run but
napi_schedule_prep() will return false. As a result, the NAPI poller never
gets scheduled, the data in the ring buffer is not consumed, and the
receive queue may remain permanently stuck until another interrupt happens
to arrive.

Fix this by enabling NAPI and registering it with the RX/TX queues before
vmbus channel is opened. This guarantees that any early host signal after
open will correctly trigger NAPI scheduling and the ring buffer will be
drained.

Fixes: 76bb5db5c749d ("netvsc: fix use after free on module removal")
Signed-off-by: Dipayaan Roy <dipayanroy@linux.microsoft.com>
Link: https://patch.msgid.link/20250825115627.GA32189@linuxonhyperv3.guj3yctzbm1etfxqx2vob5hsef.xx.internal.cloudapp.net
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Dipayaan Roy and committed by
Jakub Kicinski
9448ccd8 4b4a8ec2

+24 -16
+8 -9
drivers/net/hyperv/netvsc.c
··· 1812 1812 1813 1813 /* Enable NAPI handler before init callbacks */ 1814 1814 netif_napi_add(ndev, &net_device->chan_table[0].napi, netvsc_poll); 1815 + napi_enable(&net_device->chan_table[0].napi); 1816 + netif_queue_set_napi(ndev, 0, NETDEV_QUEUE_TYPE_RX, 1817 + &net_device->chan_table[0].napi); 1818 + netif_queue_set_napi(ndev, 0, NETDEV_QUEUE_TYPE_TX, 1819 + &net_device->chan_table[0].napi); 1815 1820 1816 1821 /* Open the channel */ 1817 1822 device->channel->next_request_id_callback = vmbus_next_request_id; ··· 1836 1831 /* Channel is opened */ 1837 1832 netdev_dbg(ndev, "hv_netvsc channel opened successfully\n"); 1838 1833 1839 - napi_enable(&net_device->chan_table[0].napi); 1840 - netif_queue_set_napi(ndev, 0, NETDEV_QUEUE_TYPE_RX, 1841 - &net_device->chan_table[0].napi); 1842 - netif_queue_set_napi(ndev, 0, NETDEV_QUEUE_TYPE_TX, 1843 - &net_device->chan_table[0].napi); 1844 - 1845 1834 /* Connect with the NetVsp */ 1846 1835 ret = netvsc_connect_vsp(device, net_device, device_info); 1847 1836 if (ret != 0) { ··· 1853 1854 1854 1855 close: 1855 1856 RCU_INIT_POINTER(net_device_ctx->nvdev, NULL); 1856 - netif_queue_set_napi(ndev, 0, NETDEV_QUEUE_TYPE_TX, NULL); 1857 - netif_queue_set_napi(ndev, 0, NETDEV_QUEUE_TYPE_RX, NULL); 1858 - napi_disable(&net_device->chan_table[0].napi); 1859 1857 1860 1858 /* Now, we can close the channel safely */ 1861 1859 vmbus_close(device->channel); 1862 1860 1863 1861 cleanup: 1862 + netif_queue_set_napi(ndev, 0, NETDEV_QUEUE_TYPE_TX, NULL); 1863 + netif_queue_set_napi(ndev, 0, NETDEV_QUEUE_TYPE_RX, NULL); 1864 + napi_disable(&net_device->chan_table[0].napi); 1864 1865 netif_napi_del(&net_device->chan_table[0].napi); 1865 1866 1866 1867 cleanup2:
+16 -7
drivers/net/hyperv/rndis_filter.c
··· 1252 1252 new_sc->rqstor_size = netvsc_rqstor_size(netvsc_ring_bytes); 1253 1253 new_sc->max_pkt_size = NETVSC_MAX_PKT_SIZE; 1254 1254 1255 + /* Enable napi before opening the vmbus channel to avoid races 1256 + * as the host placing data on the host->guest ring may be left 1257 + * out if napi was not enabled. 1258 + */ 1259 + napi_enable(&nvchan->napi); 1260 + netif_queue_set_napi(ndev, chn_index, NETDEV_QUEUE_TYPE_RX, 1261 + &nvchan->napi); 1262 + netif_queue_set_napi(ndev, chn_index, NETDEV_QUEUE_TYPE_TX, 1263 + &nvchan->napi); 1264 + 1255 1265 ret = vmbus_open(new_sc, netvsc_ring_bytes, 1256 1266 netvsc_ring_bytes, NULL, 0, 1257 1267 netvsc_channel_cb, nvchan); 1258 - if (ret == 0) { 1259 - napi_enable(&nvchan->napi); 1260 - netif_queue_set_napi(ndev, chn_index, NETDEV_QUEUE_TYPE_RX, 1261 - &nvchan->napi); 1262 - netif_queue_set_napi(ndev, chn_index, NETDEV_QUEUE_TYPE_TX, 1263 - &nvchan->napi); 1264 - } else { 1268 + if (ret != 0) { 1265 1269 netdev_notice(ndev, "sub channel open failed: %d\n", ret); 1270 + netif_queue_set_napi(ndev, chn_index, NETDEV_QUEUE_TYPE_TX, 1271 + NULL); 1272 + netif_queue_set_napi(ndev, chn_index, NETDEV_QUEUE_TYPE_RX, 1273 + NULL); 1274 + napi_disable(&nvchan->napi); 1266 1275 } 1267 1276 1268 1277 if (atomic_inc_return(&nvscdev->open_chn) == nvscdev->num_chn)