Refactor vmbus_establish_gpadl() to separate the encryption lifecycle from the rest of the GPADL establishment logic.
Add a new vmbus_establish_gpadl_caller_decrypted() for callers that want to decrypt their own buffers. No functional change for existing callers. Signed-off-by: Kameron Carr <[email protected]> --- drivers/hv/channel.c | 123 +++++++++++++++++++++++++++-------------- include/linux/hyperv.h | 11 ++++ 2 files changed, 93 insertions(+), 41 deletions(-) diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index 6821f225248b1..0166367a4df37 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -426,7 +426,13 @@ static void vmbus_free_channel_msginfo(struct vmbus_channel_msginfo *msginfo) } /* - * __vmbus_establish_gpadl - Establish a GPADL for a buffer or ringbuffer + * __vmbus_establish_gpadl - Establish a GPADL for a buffer or ringbuffer. + * + * This function only handles the GPADL handshake with the host. The caller + * is responsible for ensuring that @kbuffer is in the encryption state the + * host expects (host-visible / "decrypted" for confidential VMs); this + * function never calls set_memory_decrypted() or set_memory_encrypted(). + * It also leaves gpadl->decrypted untouched, so the caller must set it. * * @channel: a channel * @type: the type of the corresponding GPADL, only meaningful for the guest. @@ -434,7 +440,7 @@ static void vmbus_free_channel_msginfo(struct vmbus_channel_msginfo *msginfo) * @size: page-size multiple * @send_offset: the offset (in bytes) where the send ring buffer starts, * should be 0 for BUFFER type gpadl - * @gpadl_handle: some funky thing + * @gpadl: out parameter receiving the established GPADL handle/buffer/size */ static int __vmbus_establish_gpadl(struct vmbus_channel *channel, enum hv_gpadl_type type, void *kbuffer, @@ -454,30 +460,8 @@ static int __vmbus_establish_gpadl(struct vmbus_channel *channel, (atomic_inc_return(&vmbus_connection.next_gpadl_handle) - 1); ret = create_gpadl_header(type, kbuffer, size, send_offset, &msginfo); - if (ret) { - gpadl->decrypted = false; + if (ret) return ret; - } - - gpadl->decrypted = !((channel->co_external_memory && type == HV_GPADL_BUFFER) || - (channel->co_ring_buffer && type == HV_GPADL_RING)); - if (gpadl->decrypted) { - /* - * The "decrypted" flag being true assumes that set_memory_decrypted() succeeds. - * But if it fails, the encryption state of the memory is unknown. In that case, - * leave "decrypted" as true to ensure the memory is leaked instead of going back - * on the free list. - */ - ret = set_memory_decrypted((unsigned long)kbuffer, - PFN_UP(size)); - if (ret) { - dev_warn(&channel->device_obj->device, - "Failed to set host visibility for new GPADL %d.\n", - ret); - vmbus_free_channel_msginfo(msginfo); - return ret; - } - } init_completion(&msginfo->waitevent); msginfo->waiting_channel = channel; @@ -553,19 +537,47 @@ static int __vmbus_establish_gpadl(struct vmbus_channel *channel, spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); vmbus_free_channel_msginfo(msginfo); + return ret; +} - if (ret) { - /* - * If set_memory_encrypted() fails, the decrypted flag is - * left as true so the memory is leaked instead of being - * put back on the free list. - */ - if (gpadl->decrypted) { - if (!set_memory_encrypted((unsigned long)kbuffer, PFN_UP(size))) - gpadl->decrypted = false; +/* + * vmbus_establish_gpadl_and_decrypt - Manage the encryption lifecycle of + * @kbuffer around a __vmbus_establish_gpadl() call. + * + * Decrypts @kbuffer (making it host-visible) unless the channel was created + * with confidential memory for @type, then establishes the GPADL. On + * establish failure the buffer is re-encrypted so the caller can free it. + * + * Sets gpadl->decrypted; see the comment on struct vmbus_gpadl::decrypted. + */ +static int vmbus_establish_gpadl_and_decrypt(struct vmbus_channel *channel, + enum hv_gpadl_type type, + void *kbuffer, u32 size, + u32 send_offset, + struct vmbus_gpadl *gpadl) +{ + bool decrypt = !((channel->co_external_memory && type == HV_GPADL_BUFFER) || + (channel->co_ring_buffer && type == HV_GPADL_RING)); + int ret; + + if (decrypt) { + ret = set_memory_decrypted((unsigned long)kbuffer, PFN_UP(size)); + if (ret) { + dev_warn(&channel->device_obj->device, + "Failed to set host visibility for new GPADL %d.\n", + ret); + /* Encryption state unknown; signal caller to leak. */ + gpadl->decrypted = true; + return ret; } } + gpadl->decrypted = decrypt; + ret = __vmbus_establish_gpadl(channel, type, kbuffer, size, send_offset, + gpadl); + if (ret && decrypt && + !set_memory_encrypted((unsigned long)kbuffer, PFN_UP(size))) + gpadl->decrypted = false; return ret; } @@ -580,11 +592,40 @@ static int __vmbus_establish_gpadl(struct vmbus_channel *channel, int vmbus_establish_gpadl(struct vmbus_channel *channel, void *kbuffer, u32 size, struct vmbus_gpadl *gpadl) { - return __vmbus_establish_gpadl(channel, HV_GPADL_BUFFER, kbuffer, size, - 0U, gpadl); + return vmbus_establish_gpadl_and_decrypt(channel, HV_GPADL_BUFFER, + kbuffer, size, 0U, gpadl); } EXPORT_SYMBOL_GPL(vmbus_establish_gpadl); +/* + * vmbus_establish_gpadl_caller_decrypted - Establish a GPADL for a buffer + * whose encryption state is managed by the caller. + * + * @channel: a channel + * @kbuffer: a buffer that the caller has already transitioned to host-visible + * (decrypted) via set_memory_decrypted() or an equivalent mechanism. + * @size: page-size multiple + * @gpadl: out parameter receiving the established GPADL + * + * Unlike vmbus_establish_gpadl(), this function does not call + * set_memory_decrypted() on @kbuffer, and the matching vmbus_teardown_gpadl() + * call will not call set_memory_encrypted() on it. The caller is responsible + * for the full encryption lifecycle of @kbuffer; on return gpadl->decrypted + * is set to false to record that vmbus does not own the encryption state. + */ +int vmbus_establish_gpadl_caller_decrypted(struct vmbus_channel *channel, + void *kbuffer, u32 size, + struct vmbus_gpadl *gpadl) +{ + int ret = __vmbus_establish_gpadl(channel, HV_GPADL_BUFFER, kbuffer, + size, 0U, gpadl); + + /* Caller owns @kbuffer's encryption; teardown must not touch it. */ + gpadl->decrypted = false; + return ret; +} +EXPORT_SYMBOL_GPL(vmbus_establish_gpadl_caller_decrypted); + /** * request_arr_init - Allocates memory for the requestor array. Each slot * keeps track of the next available slot in the array. Initially, each @@ -685,11 +726,11 @@ static int __vmbus_open(struct vmbus_channel *newchannel, /* Establish the gpadl for the ring buffer */ newchannel->ringbuffer_gpadlhandle.gpadl_handle = 0; - err = __vmbus_establish_gpadl(newchannel, HV_GPADL_RING, - page_address(newchannel->ringbuffer_page), - (send_pages + recv_pages) << PAGE_SHIFT, - newchannel->ringbuffer_send_offset << PAGE_SHIFT, - &newchannel->ringbuffer_gpadlhandle); + err = vmbus_establish_gpadl_and_decrypt(newchannel, HV_GPADL_RING, + page_address(newchannel->ringbuffer_page), + (send_pages + recv_pages) << PAGE_SHIFT, + newchannel->ringbuffer_send_offset << PAGE_SHIFT, + &newchannel->ringbuffer_gpadlhandle); if (err) goto error_clean_ring; diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 964f1be8150c5..9c6b1b7794b48 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -781,6 +781,12 @@ struct vmbus_gpadl { u32 gpadl_handle; u32 size; void *buffer; + /* + * Only used if the vmbus layer owns the encryption lifecycle of + * @buffer. + * Indicates @buffer must be re-encrypted at teardown or that it + * must be leaked because re-encryption failed. + */ bool decrypted; }; @@ -1205,6 +1211,11 @@ extern int vmbus_establish_gpadl(struct vmbus_channel *channel, u32 size, struct vmbus_gpadl *gpadl); +extern int vmbus_establish_gpadl_caller_decrypted(struct vmbus_channel *channel, + void *kbuffer, + u32 size, + struct vmbus_gpadl *gpadl); + extern int vmbus_teardown_gpadl(struct vmbus_channel *channel, struct vmbus_gpadl *gpadl); -- 2.45.4

