From: Kameron Carr <[email protected]> Sent: Thursday, August 6, 2026 2:33 PM >
Nit on the patch Subject: I'd quibble a bit about calling these "GPADL buffers". The netvsc driver calls them send/receive buffers. A GPADL is the mechanism by which the Hyper-V host is told where they live in the guest physical address space. I think a more precise Subject would be: hv_netvsc: Allocate send/receive buffers using vmbus_alloc_buffer() Your commit description describes them correctly. > On CoCo VMs without confidential VMBus, the netvsc send and receive buffers > must be made host-visible by decrypting them. These buffers are vmalloc'ed, > but set_memory_decrypted()/encrypted() do not work on vmalloc'ed memory. > This use case is (so far) unique to netvsc, so solve it locally rather than > changing the set_memory() or allocation APIs. > > Use vmbus_alloc_buffer() to allocate the send and receive buffers, which > will make them host-visible. Store the list of memory chunks in the > netvsc_device struct so they can be individually freed later. Use > vmbus_establish_gpadl_caller_decrypted() so there is no attempt to decrypt > the virtual address. > > Appropriately free the buffers with vmbus_free_buffer(). Because vunmap() > and set_memory_encrypted() must run in process context, replace the > rcu_head/call_rcu() pair used to defer free_netvsc_device() with > rcu_work/queue_rcu_work(). This also fixes a small race condition where the > buffers may be accessed while being re-encrypted by moving the > re-encryption after the RCU grace period. > > Signed-off-by: Kameron Carr <[email protected]> Modulo the nit, looks good me to. Reviewed-by: Michael Kelley <[email protected]> > --- > drivers/net/hyperv/hyperv_net.h | 8 ++- > drivers/net/hyperv/netvsc.c | 103 ++++++++++++++++++++++---------- > drivers/net/hyperv/netvsc_drv.c | 6 ++ > 3 files changed, 83 insertions(+), 34 deletions(-) > > diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h > index 7397c693f984a..4841367fdab2f 100644 > --- a/drivers/net/hyperv/hyperv_net.h > +++ b/drivers/net/hyperv/hyperv_net.h > @@ -220,6 +220,8 @@ struct net_device_context; > > extern u32 netvsc_ring_bytes; > > +int netvsc_workqueue_init(void); > +void netvsc_workqueue_destroy(void); > struct netvsc_device *netvsc_device_add(struct hv_device *device, > const struct netvsc_device_info *info); > int netvsc_alloc_recv_comp_ring(struct netvsc_device *net_device, u32 q_idx); > @@ -1158,6 +1160,8 @@ struct netvsc_device { > /* Receive buffer allocated by us but manages by NetVSP */ > void *recv_buf; > u32 recv_buf_size; /* allocated bytes */ > + struct page **recv_buf_chunks; > + u32 recv_buf_chunk_cnt; > struct vmbus_gpadl recv_buf_gpadl_handle; > u32 recv_section_cnt; > u32 recv_section_size; > @@ -1166,6 +1170,8 @@ struct netvsc_device { > /* Send buffer allocated by us */ > void *send_buf; > u32 send_buf_size; > + struct page **send_buf_chunks; > + u32 send_buf_chunk_cnt; > struct vmbus_gpadl send_buf_gpadl_handle; > u32 send_section_cnt; > u32 send_section_size; > @@ -1193,7 +1199,7 @@ struct netvsc_device { > > struct netvsc_channel chan_table[VRSS_CHANNEL_MAX]; > > - struct rcu_head rcu; > + struct rcu_work rwork; > }; > > /* NdisInitialize message */ > diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c > index 59e95341f9b1e..c59f2a44badf0 100644 > --- a/drivers/net/hyperv/netvsc.c > +++ b/drivers/net/hyperv/netvsc.c > @@ -28,6 +28,8 @@ > #include "hyperv_net.h" > #include "netvsc_trace.h" > > +static struct workqueue_struct *netvsc_wq; > + > /* > * Switch the data path from the synthetic interface to the VF > * interface. > @@ -125,6 +127,47 @@ static void netvsc_subchan_work(struct work_struct *w) > rtnl_unlock(); > } > > +static void __free_netvsc_device(struct netvsc_device *nvdev) > +{ > + int i; > + > + kfree(nvdev->extension); > + > + vmbus_free_buffer(nvdev->recv_buf, nvdev->recv_buf_chunks, > + nvdev->recv_buf_chunk_cnt); > + vmbus_free_buffer(nvdev->send_buf, nvdev->send_buf_chunks, > + nvdev->send_buf_chunk_cnt); > + bitmap_free(nvdev->send_section_map); > + > + for (i = 0; i < VRSS_CHANNEL_MAX; i++) { > + xdp_rxq_info_unreg(&nvdev->chan_table[i].xdp_rxq); > + kfree(nvdev->chan_table[i].recv_buf); > + vfree(nvdev->chan_table[i].mrc.slots); > + } > + > + kfree(nvdev); > +} > + > +static void free_netvsc_device(struct work_struct *w) > +{ > + struct rcu_work *rwork = to_rcu_work(w); > + > + __free_netvsc_device(container_of(rwork, struct netvsc_device, rwork)); > +} > + > +int netvsc_workqueue_init(void) > +{ > + netvsc_wq = alloc_workqueue("hv_netvsc", WQ_UNBOUND, 0); > + > + return netvsc_wq ? 0 : -ENOMEM; > +} > + > +void netvsc_workqueue_destroy(void) > +{ > + rcu_barrier(); > + destroy_workqueue(netvsc_wq); > +} > + > static struct netvsc_device *alloc_net_device(void) > { > struct netvsc_device *net_device; > @@ -143,36 +186,18 @@ static struct netvsc_device *alloc_net_device(void) > init_completion(&net_device->channel_init_wait); > init_waitqueue_head(&net_device->subchan_open); > INIT_WORK(&net_device->subchan_work, netvsc_subchan_work); > + INIT_RCU_WORK(&net_device->rwork, free_netvsc_device); > > return net_device; > } > > -static void free_netvsc_device(struct rcu_head *head) > -{ > - struct netvsc_device *nvdev > - = container_of(head, struct netvsc_device, rcu); > - int i; > - > - kfree(nvdev->extension); > - > - if (!nvdev->recv_buf_gpadl_handle.decrypted) > - vfree(nvdev->recv_buf); > - if (!nvdev->send_buf_gpadl_handle.decrypted) > - vfree(nvdev->send_buf); > - bitmap_free(nvdev->send_section_map); > - > - for (i = 0; i < VRSS_CHANNEL_MAX; i++) { > - xdp_rxq_info_unreg(&nvdev->chan_table[i].xdp_rxq); > - kfree(nvdev->chan_table[i].recv_buf); > - vfree(nvdev->chan_table[i].mrc.slots); > - } > - > - kfree(nvdev); > -} > - > static void free_netvsc_device_rcu(struct netvsc_device *nvdev) > { > - call_rcu(&nvdev->rcu, free_netvsc_device); > + /* > + * Defer the actual free to process context: vunmap() and > + * set_memory_encrypted() cannot run from RCU softirq context. > + */ > + queue_rcu_work(netvsc_wq, &nvdev->rwork); > } > > static void netvsc_revoke_recv_buf(struct hv_device *device, > @@ -351,7 +376,10 @@ static int netvsc_init_buf(struct hv_device *device, > buf_size = min_t(unsigned int, buf_size, > NETVSC_RECEIVE_BUFFER_SIZE_LEGACY); > > - net_device->recv_buf = vzalloc(buf_size); > + net_device->recv_buf = > + vmbus_alloc_buffer(device->channel, buf_size, > + &net_device->recv_buf_chunks, > + &net_device->recv_buf_chunk_cnt); > if (!net_device->recv_buf) { > netdev_err(ndev, > "unable to allocate receive buffer of size %u\n", > @@ -367,9 +395,10 @@ static int netvsc_init_buf(struct hv_device *device, > * channel. Note: This call uses the vmbus connection rather > * than the channel to establish the gpadl handle. > */ > - ret = vmbus_establish_gpadl(device->channel, net_device->recv_buf, > - buf_size, > - &net_device->recv_buf_gpadl_handle); > + ret = vmbus_establish_gpadl_caller_decrypted(device->channel, > + net_device->recv_buf, > + buf_size, > + &net_device- > >recv_buf_gpadl_handle); > if (ret != 0) { > netdev_err(ndev, > "unable to establish receive buffer's gpadl\n"); > @@ -457,7 +486,10 @@ static int netvsc_init_buf(struct hv_device *device, > buf_size = device_info->send_sections * device_info->send_section_size; > buf_size = round_up(buf_size, PAGE_SIZE); > > - net_device->send_buf = vzalloc(buf_size); > + net_device->send_buf = > + vmbus_alloc_buffer(device->channel, buf_size, > + &net_device->send_buf_chunks, > + &net_device->send_buf_chunk_cnt); > if (!net_device->send_buf) { > netdev_err(ndev, "unable to allocate send buffer of size %u\n", > buf_size); > @@ -470,9 +502,10 @@ static int netvsc_init_buf(struct hv_device *device, > * channel. Note: This call uses the vmbus connection rather > * than the channel to establish the gpadl handle. > */ > - ret = vmbus_establish_gpadl(device->channel, net_device->send_buf, > - buf_size, > - &net_device->send_buf_gpadl_handle); > + ret = vmbus_establish_gpadl_caller_decrypted(device->channel, > + net_device->send_buf, > + buf_size, > + &net_device- > >send_buf_gpadl_handle); > if (ret != 0) { > netdev_err(ndev, > "unable to establish send buffer's gpadl\n"); > @@ -1863,7 +1896,11 @@ struct netvsc_device *netvsc_device_add(struct > hv_device > *device, > netif_napi_del(&net_device->chan_table[0].napi); > > cleanup2: > - free_netvsc_device(&net_device->rcu); > + /* > + * net_device was never published, so we don't need to wait for an > + * RCU grace period -- call the free routine synchronously. > + */ > + __free_netvsc_device(net_device); > > return ERR_PTR(ret); > } > diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c > index ee5ab5ceb2be2..1d43c73fd73f1 100644 > --- a/drivers/net/hyperv/netvsc_drv.c > +++ b/drivers/net/hyperv/netvsc_drv.c > @@ -2867,12 +2867,17 @@ static void __exit netvsc_drv_exit(void) > { > unregister_netdevice_notifier(&netvsc_netdev_notifier); > vmbus_driver_unregister(&netvsc_drv); > + netvsc_workqueue_destroy(); > } > > static int __init netvsc_drv_init(void) > { > int ret; > > + ret = netvsc_workqueue_init(); > + if (ret) > + return ret; > + > if (ring_size < RING_SIZE_MIN) { > ring_size = RING_SIZE_MIN; > pr_info("Increased ring_size to %u (min allowed)\n", > @@ -2890,6 +2895,7 @@ static int __init netvsc_drv_init(void) > > err_vmbus_reg: > unregister_netdevice_notifier(&netvsc_netdev_notifier); > + netvsc_workqueue_destroy(); > return ret; > } > > -- > 2.45.4 >

