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

vmbus: remove hv_event_tasklet_disable/enable

With the recent introduction of per-channel tasklet, we need to update
the way we handle the 3 concurrency issues:

1. hv_process_channel_removal -> percpu_channel_deq vs.
vmbus_chan_sched -> list_for_each_entry(..., percpu_list);

2. vmbus_process_offer -> percpu_channel_enq/deq vs. vmbus_chan_sched.

3. vmbus_close_internal vs. the per-channel tasklet vmbus_on_event;

The first 2 issues can be handled by Stephen's recent patch
"vmbus: use rcu for per-cpu channel list", and the third issue
can be handled by calling tasklet_disable in vmbus_close_internal here.

We don't need the original hv_event_tasklet_disable/enable since we
now use per-channel tasklet instead of the previous per-CPU tasklet,
and actually we must remove them due to the side effect now:
vmbus_process_offer -> hv_event_tasklet_enable -> tasklet_schedule will
start the per-channel callback prematurely, cauing NULL dereferencing
(the channel may haven't been properly configured to run the callback yet).

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

Signed-off-by: Dexuan Cui <decui@microsoft.com>
Cc: "K. Y. Srinivasan" <kys@microsoft.com>
Cc: Haiyang Zhang <haiyangz@microsoft.com>
Cc: Stephen Hemminger <sthemmin@microsoft.com>
Tested-by: Vitaly Kuznetsov <vkuznets@redhat.com>
Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Dexuan Cui and committed by
Greg Kroah-Hartman
dad72a1d 8200f208

+4 -30
+4 -8
drivers/hv/channel.c
··· 530 530 int ret; 531 531 532 532 /* 533 - * vmbus_on_event(), running in the tasklet, can race 533 + * vmbus_on_event(), running in the per-channel tasklet, can race 534 534 * with vmbus_close_internal() in the case of SMP guest, e.g., when 535 535 * the former is accessing channel->inbound.ring_buffer, the latter 536 - * could be freeing the ring_buffer pages. 537 - * 538 - * To resolve the race, we can serialize them by disabling the 539 - * tasklet when the latter is running here. 536 + * could be freeing the ring_buffer pages, so here we must stop it 537 + * first. 540 538 */ 541 - hv_event_tasklet_disable(channel); 539 + tasklet_disable(&channel->callback_event); 542 540 543 541 /* 544 542 * In case a device driver's probe() fails (e.g., ··· 603 605 get_order(channel->ringbuffer_pagecount * PAGE_SIZE)); 604 606 605 607 out: 606 - hv_event_tasklet_enable(channel); 607 - 608 608 return ret; 609 609 } 610 610
-19
drivers/hv/channel_mgmt.c
··· 382 382 true); 383 383 } 384 384 385 - void hv_event_tasklet_disable(struct vmbus_channel *channel) 386 - { 387 - tasklet_disable(&channel->callback_event); 388 - } 389 - 390 - void hv_event_tasklet_enable(struct vmbus_channel *channel) 391 - { 392 - tasklet_enable(&channel->callback_event); 393 - 394 - /* In case there is any pending event */ 395 - tasklet_schedule(&channel->callback_event); 396 - } 397 - 398 385 void hv_process_channel_removal(struct vmbus_channel *channel, u32 relid) 399 386 { 400 387 unsigned long flags; ··· 390 403 BUG_ON(!channel->rescind); 391 404 BUG_ON(!mutex_is_locked(&vmbus_connection.channel_mutex)); 392 405 393 - hv_event_tasklet_disable(channel); 394 406 if (channel->target_cpu != get_cpu()) { 395 407 put_cpu(); 396 408 smp_call_function_single(channel->target_cpu, ··· 398 412 percpu_channel_deq(channel); 399 413 put_cpu(); 400 414 } 401 - hv_event_tasklet_enable(channel); 402 415 403 416 if (channel->primary_channel == NULL) { 404 417 list_del(&channel->listentry); ··· 491 506 492 507 init_vp_index(newchannel, dev_type); 493 508 494 - hv_event_tasklet_disable(newchannel); 495 509 if (newchannel->target_cpu != get_cpu()) { 496 510 put_cpu(); 497 511 smp_call_function_single(newchannel->target_cpu, ··· 500 516 percpu_channel_enq(newchannel); 501 517 put_cpu(); 502 518 } 503 - hv_event_tasklet_enable(newchannel); 504 519 505 520 /* 506 521 * This state is used to indicate a successful open ··· 549 566 list_del(&newchannel->listentry); 550 567 mutex_unlock(&vmbus_connection.channel_mutex); 551 568 552 - hv_event_tasklet_disable(newchannel); 553 569 if (newchannel->target_cpu != get_cpu()) { 554 570 put_cpu(); 555 571 smp_call_function_single(newchannel->target_cpu, ··· 557 575 percpu_channel_deq(newchannel); 558 576 put_cpu(); 559 577 } 560 - hv_event_tasklet_enable(newchannel); 561 578 562 579 vmbus_release_relid(newchannel->offermsg.child_relid); 563 580
-3
include/linux/hyperv.h
··· 1437 1437 const int *srv_version, int srv_vercnt, 1438 1438 int *nego_fw_version, int *nego_srv_version); 1439 1439 1440 - void hv_event_tasklet_disable(struct vmbus_channel *channel); 1441 - void hv_event_tasklet_enable(struct vmbus_channel *channel); 1442 - 1443 1440 void hv_process_channel_removal(struct vmbus_channel *channel, u32 relid); 1444 1441 1445 1442 void vmbus_setevent(struct vmbus_channel *channel);