If an error case needs to leak the buffer memory allocated by vmbus_alloc_buffer(), doing so requires knowledge of how vmbus_free_buffer() works. In a CoCo VM buffers are allocated differently from a normal VM, and vmbus_free_buffer() handles the difference.
Encapsulate this knowledge in a new function, vmbus_leak_buffer(), that error paths can call. After calling vmbus_leak_buffer(), a subsequent call to vmbus_free_buffer() frees the additional resources used in the CoCo VM case but does not free the actual buffer memory. As such, vmbus_leak_buffer() is callable in a context where accesses to the buffer memory may be in flight. Signed-off-by: Michael Kelley <[email protected]> --- drivers/hv/channel.c | 26 ++++++++++++++++++++++++++ include/linux/hyperv.h | 4 ++++ 2 files changed, 30 insertions(+) diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index f4370617deac..d5d20e322831 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -648,6 +648,32 @@ void vmbus_free_buffer(void *addr, struct page **chunks, u32 chunk_cnt) } EXPORT_SYMBOL_GPL(vmbus_free_buffer); +/** + * vmbus_leak_buffer - set up a buffer to be leaked by vmbus_free_buffer(). + * + * @addr: buffer address + * @chunks: chunks array from vmbus_alloc_buffer() + * @chunk_cnt: number of entries in @chunks + * + * When @chunks is NULL the buffer is a plain vzalloc() allocation and + * the buffer is leaked by setting @addr to NULL. Otherwise set + * @chunk_cnt to 0 so that vmbus_free_buffer() does not try to re-encrypt + * or free the buffer memory, but still releases the vmap address and + * the chunks memory. + * + * This function may be called in a context where the buffer is still + * being accessed. It must not remove any kernel virtual addresses of + * the buffer or change its encryption status. + */ +void vmbus_leak_buffer(void **addr, struct page ***chunks, u32 *chunk_cnt) +{ + if (*chunks) + *chunk_cnt = 0; + else + *addr = NULL; +} +EXPORT_SYMBOL_GPL(vmbus_leak_buffer); + /** * vmbus_alloc_buffer - allocate a host-visible, virtually-contiguous buffer. * diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index e61f9a4cb7c3..c55de4d01cbb 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -1220,6 +1220,10 @@ extern void *vmbus_alloc_buffer(struct vmbus_channel *channel, extern void vmbus_free_buffer(void *addr, struct page **chunks, u32 chunk_cnt); +extern void vmbus_leak_buffer(void **addr, + struct page ***chunks, + u32 *chunk_cnt); + void vmbus_reset_channel_cb(struct vmbus_channel *channel); extern int vmbus_recvpacket(struct vmbus_channel *channel, -- 2.25.1

