static void vmbus_onoffer()

in channel_mgmt.c [997:1098]


static void vmbus_onoffer(struct vmbus_channel_message_header *hdr)
{
	struct vmbus_channel_offer_channel *offer;
	struct vmbus_channel *oldchannel, *newchannel;
	size_t offer_sz;

	offer = (struct vmbus_channel_offer_channel *)hdr;

	trace_vmbus_onoffer(offer);

	if (!vmbus_is_valid_device(&offer->offer.if_type)) {
		pr_err_ratelimited("Invalid offer %d from the host supporting isolation\n",
				   offer->child_relid);
		atomic_dec(&vmbus_connection.offer_in_progress);
		return;
	}

	oldchannel = find_primary_channel_by_offer(offer);

	if (oldchannel != NULL) {
		/*
		 * We're resuming from hibernation: all the sub-channel and
		 * hv_sock channels we had before the hibernation should have
		 * been cleaned up, and now we must be seeing a re-offered
		 * primary channel that we had before the hibernation.
		 */

		/*
		 * { Initially: channel relid = INVALID_RELID,
		 *		channels[valid_relid] = NULL }
		 *
		 * CPU1					CPU2
		 *
		 * [vmbus_onoffer()]			[vmbus_device_release()]
		 *
		 * LOCK channel_mutex			LOCK channel_mutex
		 * STORE channel relid = valid_relid	LOAD r1 = channel relid
		 * MAP_RELID channel			if (r1 != INVALID_RELID)
		 * UNLOCK channel_mutex			  UNMAP_RELID channel
		 *					UNLOCK channel_mutex
		 *
		 * Forbids: r1 == valid_relid &&
		 *              channels[valid_relid] == channel
		 *
		 * Note.  r1 can be INVALID_RELID only for an hv_sock channel.
		 * None of the hv_sock channels which were present before the
		 * suspend are re-offered upon the resume.  See the WARN_ON()
		 * in hv_process_channel_removal().
		 */
		mutex_lock(&vmbus_connection.channel_mutex);

		atomic_dec(&vmbus_connection.offer_in_progress);

		WARN_ON(oldchannel->offermsg.child_relid != INVALID_RELID);
		/* Fix up the relid. */
		oldchannel->offermsg.child_relid = offer->child_relid;

		offer_sz = sizeof(*offer);
		if (memcmp(offer, &oldchannel->offermsg, offer_sz) != 0) {
			/*
			 * This is not an error, since the host can also change
			 * the other field(s) of the offer, e.g. on WS RS5
			 * (Build 17763), the offer->connection_id of the
			 * Mellanox VF vmbus device can change when the host
			 * reoffers the device upon resume.
			 */
			pr_debug("vmbus offer changed: relid=%d\n",
				 offer->child_relid);

			print_hex_dump_debug("Old vmbus offer: ",
					     DUMP_PREFIX_OFFSET, 16, 4,
					     &oldchannel->offermsg, offer_sz,
					     false);
			print_hex_dump_debug("New vmbus offer: ",
					     DUMP_PREFIX_OFFSET, 16, 4,
					     offer, offer_sz, false);

			/* Fix up the old channel. */
			vmbus_setup_channel_state(oldchannel, offer);
		}

		/* Add the channel back to the array of channels. */
		vmbus_channel_map_relid(oldchannel);
		check_ready_for_resume_event();

		mutex_unlock(&vmbus_connection.channel_mutex);
		return;
	}

	/* Allocate the channel object and save this offer. */
	newchannel = alloc_channel();
	if (!newchannel) {
		vmbus_release_relid(offer->child_relid);
		atomic_dec(&vmbus_connection.offer_in_progress);
		pr_err("Unable to allocate channel object\n");
		return;
	}

	vmbus_setup_channel_state(newchannel, offer);

	vmbus_process_offer(newchannel);
}