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

vmbus: use rcu for per-cpu channel list

The per-cpu channel list is now referred to in the interrupt
routine. This is mostly safe since the host will not normally generate
an interrupt when channel is being deleted but if it did then there
would be a use after free problem.

To solve, this use RCU protection on ther per-cpu list.

Fixes: 631e63a9f346 ("vmbus: change to per channel tasklet")

Signed-off-by: Stephen Hemminger <sthemmin@microsoft.com>
Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Stephen Hemminger and committed by
Greg Kroah-Hartman
8200f208 c6240cac

+16 -4
+4 -3
drivers/hv/channel_mgmt.c
··· 350 350 static void free_channel(struct vmbus_channel *channel) 351 351 { 352 352 tasklet_kill(&channel->callback_event); 353 - kfree(channel); 353 + 354 + kfree_rcu(channel, rcu); 354 355 } 355 356 356 357 static void percpu_channel_enq(void *arg) ··· 360 359 struct hv_per_cpu_context *hv_cpu 361 360 = this_cpu_ptr(hv_context.cpu_context); 362 361 363 - list_add_tail(&channel->percpu_list, &hv_cpu->chan_list); 362 + list_add_tail_rcu(&channel->percpu_list, &hv_cpu->chan_list); 364 363 } 365 364 366 365 static void percpu_channel_deq(void *arg) 367 366 { 368 367 struct vmbus_channel *channel = arg; 369 368 370 - list_del(&channel->percpu_list); 369 + list_del_rcu(&channel->percpu_list); 371 370 } 372 371 373 372
+5 -1
drivers/hv/vmbus_drv.c
··· 939 939 if (relid == 0) 940 940 continue; 941 941 942 + rcu_read_lock(); 943 + 942 944 /* Find channel based on relid */ 943 - list_for_each_entry(channel, &hv_cpu->chan_list, percpu_list) { 945 + list_for_each_entry_rcu(channel, &hv_cpu->chan_list, percpu_list) { 944 946 if (channel->offermsg.child_relid != relid) 945 947 continue; 946 948 ··· 958 956 tasklet_schedule(&channel->callback_event); 959 957 } 960 958 } 959 + 960 + rcu_read_unlock(); 961 961 } 962 962 } 963 963
+7
include/linux/hyperv.h
··· 845 845 * link up channels based on their CPU affinity. 846 846 */ 847 847 struct list_head percpu_list; 848 + 849 + /* 850 + * Defer freeing channel until after all cpu's have 851 + * gone through grace period. 852 + */ 853 + struct rcu_head rcu; 854 + 848 855 /* 849 856 * For performance critical channels (storage, networking 850 857 * etc,), Hyper-V has a mechanism to enhance the throughput