Skip to content

Commit c967590

Browse files
dcuimartinkpetersen
authored andcommitted
scsi: storvsc: Fix a race in sub-channel creation that can cause panic
We can concurrently try to open the same sub-channel from 2 paths: path #1: vmbus_onoffer() -> vmbus_process_offer() -> handle_sc_creation(). path #2: storvsc_probe() -> storvsc_connect_to_vsp() -> -> storvsc_channel_init() -> handle_multichannel_storage() -> -> vmbus_are_subchannels_present() -> handle_sc_creation(). They conflict with each other, but it was not an issue before the recent commit ae6935e ("vmbus: split ring buffer allocation from open"), because at the beginning of vmbus_open() we checked newchannel->state so only one path could succeed, and the other would return with -EINVAL. After ae6935e, the failing path frees the channel's ringbuffer by vmbus_free_ring(), and this causes a panic later. Commit ae6935e itself is good, and it just reveals the longstanding race. We can resolve the issue by removing path #2, i.e. removing the second vmbus_are_subchannels_present() in handle_multichannel_storage(). BTW, the comment "Check to see if sub-channels have already been created" in handle_multichannel_storage() is incorrect: when we unload the driver, we first close the sub-channel(s) and then close the primary channel, next the host sends rescind-offer message(s) so primary->sc_list will become empty. This means the first vmbus_are_subchannels_present() in handle_multichannel_storage() is never useful. Fixes: ae6935e ("vmbus: split ring buffer allocation from open") Cc: [email protected] Cc: Long Li <[email protected]> Cc: Stephen Hemminger <[email protected]> Cc: K. Y. Srinivasan <[email protected]> Cc: Haiyang Zhang <[email protected]> Signed-off-by: Dexuan Cui <[email protected]> Signed-off-by: K. Y. Srinivasan <[email protected]> Signed-off-by: Martin K. Petersen <[email protected]>
1 parent 02f425f commit c967590

File tree

1 file changed

+30
-31
lines changed

1 file changed

+30
-31
lines changed

drivers/scsi/storvsc_drv.c

+30-31
Original file line numberDiff line numberDiff line change
@@ -446,7 +446,6 @@ struct storvsc_device {
446446

447447
bool destroy;
448448
bool drain_notify;
449-
bool open_sub_channel;
450449
atomic_t num_outstanding_req;
451450
struct Scsi_Host *host;
452451

@@ -636,33 +635,38 @@ static inline struct storvsc_device *get_in_stor_device(
636635
static void handle_sc_creation(struct vmbus_channel *new_sc)
637636
{
638637
struct hv_device *device = new_sc->primary_channel->device_obj;
638+
struct device *dev = &device->device;
639639
struct storvsc_device *stor_device;
640640
struct vmstorage_channel_properties props;
641+
int ret;
641642

642643
stor_device = get_out_stor_device(device);
643644
if (!stor_device)
644645
return;
645646

646-
if (stor_device->open_sub_channel == false)
647-
return;
648-
649647
memset(&props, 0, sizeof(struct vmstorage_channel_properties));
650648

651-
vmbus_open(new_sc,
652-
storvsc_ringbuffer_size,
653-
storvsc_ringbuffer_size,
654-
(void *)&props,
655-
sizeof(struct vmstorage_channel_properties),
656-
storvsc_on_channel_callback, new_sc);
649+
ret = vmbus_open(new_sc,
650+
storvsc_ringbuffer_size,
651+
storvsc_ringbuffer_size,
652+
(void *)&props,
653+
sizeof(struct vmstorage_channel_properties),
654+
storvsc_on_channel_callback, new_sc);
657655

658-
if (new_sc->state == CHANNEL_OPENED_STATE) {
659-
stor_device->stor_chns[new_sc->target_cpu] = new_sc;
660-
cpumask_set_cpu(new_sc->target_cpu, &stor_device->alloced_cpus);
656+
/* In case vmbus_open() fails, we don't use the sub-channel. */
657+
if (ret != 0) {
658+
dev_err(dev, "Failed to open sub-channel: err=%d\n", ret);
659+
return;
661660
}
661+
662+
/* Add the sub-channel to the array of available channels. */
663+
stor_device->stor_chns[new_sc->target_cpu] = new_sc;
664+
cpumask_set_cpu(new_sc->target_cpu, &stor_device->alloced_cpus);
662665
}
663666

664667
static void handle_multichannel_storage(struct hv_device *device, int max_chns)
665668
{
669+
struct device *dev = &device->device;
666670
struct storvsc_device *stor_device;
667671
int num_cpus = num_online_cpus();
668672
int num_sc;
@@ -679,21 +683,11 @@ static void handle_multichannel_storage(struct hv_device *device, int max_chns)
679683
request = &stor_device->init_request;
680684
vstor_packet = &request->vstor_packet;
681685

682-
stor_device->open_sub_channel = true;
683686
/*
684687
* Establish a handler for dealing with subchannels.
685688
*/
686689
vmbus_set_sc_create_callback(device->channel, handle_sc_creation);
687690

688-
/*
689-
* Check to see if sub-channels have already been created. This
690-
* can happen when this driver is re-loaded after unloading.
691-
*/
692-
693-
if (vmbus_are_subchannels_present(device->channel))
694-
return;
695-
696-
stor_device->open_sub_channel = false;
697691
/*
698692
* Request the host to create sub-channels.
699693
*/
@@ -710,23 +704,29 @@ static void handle_multichannel_storage(struct hv_device *device, int max_chns)
710704
VM_PKT_DATA_INBAND,
711705
VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
712706

713-
if (ret != 0)
707+
if (ret != 0) {
708+
dev_err(dev, "Failed to create sub-channel: err=%d\n", ret);
714709
return;
710+
}
715711

716712
t = wait_for_completion_timeout(&request->wait_event, 10*HZ);
717-
if (t == 0)
713+
if (t == 0) {
714+
dev_err(dev, "Failed to create sub-channel: timed out\n");
718715
return;
716+
}
719717

720718
if (vstor_packet->operation != VSTOR_OPERATION_COMPLETE_IO ||
721-
vstor_packet->status != 0)
719+
vstor_packet->status != 0) {
720+
dev_err(dev, "Failed to create sub-channel: op=%d, sts=%d\n",
721+
vstor_packet->operation, vstor_packet->status);
722722
return;
723+
}
723724

724725
/*
725-
* Now that we created the sub-channels, invoke the check; this
726-
* may trigger the callback.
726+
* We need to do nothing here, because vmbus_process_offer()
727+
* invokes channel->sc_creation_callback, which will open and use
728+
* the sub-channel(s).
727729
*/
728-
stor_device->open_sub_channel = true;
729-
vmbus_are_subchannels_present(device->channel);
730730
}
731731

732732
static void cache_wwn(struct storvsc_device *stor_device,
@@ -1794,7 +1794,6 @@ static int storvsc_probe(struct hv_device *device,
17941794
}
17951795

17961796
stor_device->destroy = false;
1797-
stor_device->open_sub_channel = false;
17981797
init_waitqueue_head(&stor_device->waiting_to_drain);
17991798
stor_device->device = device;
18001799
stor_device->host = host;

0 commit comments

Comments
 (0)