Drivers: hv: vmbus: Miscellaneous fixes
Subsystems (2)
cover Drivers: hv: vmbus: Miscellaneous fixes
From: "K. Y. Srinivasan" <[email protected]>
Miscellaneous fixes.
Dexuan Cui (1):
Drivers: hv: vmbus: Reset the channel callback in
vmbus_onoffer_rescind()
Michael Kelley (2):
Drivers: hv: vmbus: Remove use of slow_virt_to_phys()
Drivers: hv: vmbus: Cleanup synic memory free path
drivers/hv/channel.c | 67 +++++++++++++++++++++++++-------------- drivers/hv/channel_mgmt.c | 6 ++++ drivers/hv/hv.c | 14 ++++---- include/linux/hyperv.h | 2 ++ 4 files changed, 60 insertions(+), 29 deletions(-)
Signature · 1 line
2.17.1
PATCH 1/3 Drivers: hv: vmbus: Reset the channel callback in vmbus_onoffer_rescind()
From: Dexuan Cui <[email protected]>
Before setting channel->rescind in vmbus_rescind_cleanup(), we should make sure the channel callback won't run any more, otherwise a high-level driver like pci_hyperv, which may be infinitely waiting for the host VSP's response and notices the channel has been rescinded, can't safely give
up: e.g., in hv_pci_protocol_negotiation() -> wait_for_response(), it's unsafe to exit from wait_for_response() and proceed with the on-stack variable "comp_pkt" popped. The issue was originally spotted by Michael Kelley <[email protected]>.
In vmbus_close_internal(), the patch also minimizes the range protected by disabling/enabling channel->callback_event: we don't really need that for the whole function.
--- drivers/hv/channel.c | 40 +++++++++++++++++++++++---------------- drivers/hv/channel_mgmt.c | 6 ++++++ include/linux/hyperv.h | 2 ++ 3 files changed, 32 insertions(+), 16 deletions(-)
drivers/hv/channel.c +24 -16
@@ -558,11 +558,8 @@ static void reset_channel_cb(void *arg) channel->onchannel_callback =NULL ; } -static int vmbus_close_internal (struct vmbus_channel *channel) +void vmbus_reset_channel_cb (struct vmbus_channel *channel) { -struct vmbus_channel_close_channel *msg; -int ret; -/* * vmbus_on_event(), running in the per-channel tasklet, can race * with vmbus_close_internal() in the case of SMP guest, e.g., when
@@ -572,6 +569,29 @@ static int vmbus_close_internal(struct vmbus_channel *channel)*/ tasklet_disable (&channel->callback_event); + channel->sc_creation_callback =NULL ; + +/* Stop the callback asap */ +if (channel->target_cpu !=get_cpu ()) { +put_cpu (); +smp_call_function_single (channel->target_cpu, reset_channel_cb, + channel, true); + }else { +reset_channel_cb (channel); +put_cpu (); + } + +/* Re-enable tasklet for use on re-open */ +tasklet_enable (&channel->callback_event); +} + +static int vmbus_close_internal (struct vmbus_channel *channel) +{ +struct vmbus_channel_close_channel *msg; +int ret; + +vmbus_reset_channel_cb (channel); +/* * In case a device driver's probe() fails (e.g., * util_probe() -> vmbus_open() returns -ENOMEM) and the device is
@@ -585,16 +605,6 @@ static int vmbus_close_internal(struct vmbus_channel *channel) } channel->state =CHANNEL_OPEN_STATE ; - channel->sc_creation_callback =NULL ; -/* Stop callback and cancel the timer asap */ -if (channel->target_cpu !=get_cpu ()) { -put_cpu (); -smp_call_function_single (channel->target_cpu, reset_channel_cb, - channel, true); - }else { -reset_channel_cb (channel); -put_cpu (); - }/* Send a closing message */
@@ -639,8 +649,6 @@ static int vmbus_close_internal(struct vmbus_channel *channel) get_order(channel->ringbuffer_pagecount * PAGE_SIZE)); out: -/* re-enable tasklet for use on re-open */ -tasklet_enable (&channel->callback_event);return ret; }
drivers/hv/channel_mgmt.c +6 -0
@@ -892,6 +892,12 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr)return ; } +/* +* Before setting channel->rescind in vmbus_rescind_cleanup(), we +* should make sure the channel callback is not running any more. +*/ +vmbus_reset_channel_cb (channel); +/* * Now wait for offer handling to complete. */
include/linux/hyperv.h +2 -0
@@ -1061,6 +1061,8 @@ extern int vmbus_establish_gpadl(struct vmbus_channel *channel,extern int vmbus_teardown_gpadl (struct vmbus_channel *channel,u32 gpadl_handle); +void vmbus_reset_channel_cb (struct vmbus_channel *channel); + extern int vmbus_recvpacket(struct vmbus_channel *channel, void *buffer, u32 bufferlen,
Signature · 1 line
2.17.1
PATCH 2/3 Drivers: hv: vmbus: Remove use of slow_virt_to_phys()
From: Michael Kelley <[email protected]>
slow_virt_to_phys() is only implemented for arch/x86. Remove its use in arch independent Hyper-V drivers, and replace with test for vmalloc() address followed by appropriate v-to-p function. This follows the typical pattern of other drivers and avoids the need to implement slow_virt_to_phys() for Hyper-V on ARM64.
--- drivers/hv/channel.c | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-)
drivers/hv/channel.c +20 -7
@@ -29,12 +29,26 @@#include <linux/hyperv.h> #include <linux/uio.h> #include <linux/interrupt.h> +#include <asm/page.h> #include "hyperv_vmbus.h" #define NUM_PAGES_SPANNED (addr, len) \ ((PAGE_ALIGN(addr + len) >> PAGE_SHIFT) - (addr >> PAGE_SHIFT)) +static unsigned long virt_to_hvpfn (void *addr) +{ +unsigned long paddr; + +if (is_vmalloc_addr (addr)) + paddr =page_to_phys (vmalloc_to_page (addr)) + +offset_in_page (addr); +else + paddr =__pa (addr); + +return paddr >>PAGE_SHIFT ; +} +/* * vmbus_setevent- Trigger an event notification on the specified * channel.
@@ -298,8 +312,8 @@ static int create_gpadl_header(void *kbuffer, u32 size, gpadl_header->range[0 ].byte_offset =0 ; gpadl_header->range[0 ].byte_count = size;for (i =0 ; i < pfncount; i++) - gpadl_header->range[0 ].pfn_array[i] =slow_virt_to_phys ( - kbuffer +PAGE_SIZE * i) >>PAGE_SHIFT ; + gpadl_header->range[0 ].pfn_array[i] =virt_to_hvpfn ( + kbuffer +PAGE_SIZE * i); *msginfo = msgheader; pfnsum = pfncount;
@@ -350,9 +364,8 @@ static int create_gpadl_header(void *kbuffer, u32 size,* so the hypervisor guarantees that this is ok. */ for (i =0 ; i < pfncurr; i++) - gpadl_body->pfn[i] =slow_virt_to_phys ( - kbuffer +PAGE_SIZE * (pfnsum + i)) >> -PAGE_SHIFT ; + gpadl_body->pfn[i] =virt_to_hvpfn ( + kbuffer +PAGE_SIZE * (pfnsum + i));/* add to msg header */ list_add_tail(&msgbody->msglistentry,
@@ -380,8 +393,8 @@ static int create_gpadl_header(void *kbuffer, u32 size, gpadl_header->range[0 ].byte_offset =0 ; gpadl_header->range[0 ].byte_count = size;for (i =0 ; i < pagecount; i++) - gpadl_header->range[0 ].pfn_array[i] =slow_virt_to_phys ( - kbuffer +PAGE_SIZE * i) >>PAGE_SHIFT ; + gpadl_header->range[0 ].pfn_array[i] =virt_to_hvpfn ( + kbuffer +PAGE_SIZE * i); *msginfo = msgheader; }
Signature · 1 line
2.17.1
PATCH 3/3 Drivers: hv: vmbus: Cleanup synic memory free path
In reply to [email protected]From: Michael Kelley <[email protected]>
clk_evt memory is not being freed when the synic is shutdown or when there is an allocation error. Add the appropriate kfree() call, along with a comment to clarify how the memory gets freed after an allocation error. Make the free path consistent by removing checks for NULL since kfree() and free_page() already do the check.
--- drivers/hv/hv.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-)
drivers/hv/hv.c +8 -6
@@ -242,6 +242,10 @@ int hv_synic_alloc(void)return 0 ; err: +/* +* Any memory allocations that succeeded will be freed when +* the caller cleans up by calling hv_synic_free() +*/ return -ENOMEM ; }
@@ -254,12 +258,10 @@ void hv_synic_free(void)struct hv_per_cpu_context *hv_cpu =per_cpu_ptr (hv_context.cpu_context, cpu); -if (hv_cpu->synic_event_page) -free_page ((unsigned long )hv_cpu->synic_event_page); -if (hv_cpu->synic_message_page) -free_page ((unsigned long )hv_cpu->synic_message_page); -if (hv_cpu->post_msg_page) -free_page ((unsigned long )hv_cpu->post_msg_page); +kfree (hv_cpu->clk_evt); +free_page ((unsigned long )hv_cpu->synic_event_page); +free_page ((unsigned long )hv_cpu->synic_message_page); +free_page ((unsigned long )hv_cpu->post_msg_page); }kfree (hv_context.hv_numa_map);
Signature · 1 line
2.17.1