static int vmbus_bus_suspend()

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;
}