in vmbus_drv.c [2458:2537]
static int vmbus_bus_suspend(struct device *dev)
{
struct vmbus_channel *channel, *sc;
while (atomic_read(&vmbus_connection.offer_in_progress) != 0) {
/*
* We wait here until the completion of any channel
* offers that are currently in progress.
*/
usleep_range(1000, 2000);
}
mutex_lock(&vmbus_connection.channel_mutex);
list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) {
if (!is_hvsock_channel(channel))
continue;
vmbus_force_channel_rescinded(channel);
}
mutex_unlock(&vmbus_connection.channel_mutex);
/*
* Wait until all the sub-channels and hv_sock channels have been
* cleaned up. Sub-channels should be destroyed upon suspend, otherwise
* they would conflict with the new sub-channels that will be created
* in the resume path. hv_sock channels should also be destroyed, but
* a hv_sock channel of an established hv_sock connection can not be
* really destroyed since it may still be referenced by the userspace
* application, so we just force the hv_sock channel to be rescinded
* by vmbus_force_channel_rescinded(), and the userspace application
* will thoroughly destroy the channel after hibernation.
*
* Note: the counter nr_chan_close_on_suspend may never go above 0 if
* the VM has no sub-channel and hv_sock channel, e.g. a 1-vCPU VM.
*/
if (atomic_read(&vmbus_connection.nr_chan_close_on_suspend) > 0)
wait_for_completion(&vmbus_connection.ready_for_suspend_event);
if (atomic_read(&vmbus_connection.nr_chan_fixup_on_resume) != 0) {
pr_err("Can not suspend due to a previous failed resuming\n");
return -EBUSY;
}
mutex_lock(&vmbus_connection.channel_mutex);
list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) {
/*
* Remove the channel from the array of channels and invalidate
* the channel's relid. Upon resume, vmbus_onoffer() will fix
* up the relid (and other fields, if necessary) and add the
* channel back to the array.
*/
vmbus_channel_unmap_relid(channel);
channel->offermsg.child_relid = INVALID_RELID;
if (is_hvsock_channel(channel)) {
if (!channel->rescind) {
pr_err("hv_sock channel not rescinded!\n");
WARN_ON_ONCE(1);
}
continue;
}
list_for_each_entry(sc, &channel->sc_list, sc_list) {
pr_err("Sub-channel not deleted!\n");
WARN_ON_ONCE(1);
}
atomic_inc(&vmbus_connection.nr_chan_fixup_on_resume);
}
mutex_unlock(&vmbus_connection.channel_mutex);
vmbus_initiate_unload(false);
/* Reset the event for the next resume. */
reinit_completion(&vmbus_connection.ready_for_resume_event);
return 0;
}