Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 631e63a9 authored by Stephen Hemminger's avatar Stephen Hemminger Committed by Greg Kroah-Hartman
Browse files

vmbus: change to per channel tasklet



Make the event handling tasklet per channel rather than per-cpu.
This allows for better fairness when getting lots of data on the same
cpu.

Signed-off-by: default avatarStephen Hemminger <sthemmin@microsoft.com>
Signed-off-by: default avatarK. Y. Srinivasan <kys@microsoft.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 37cdd991
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -530,7 +530,7 @@ static int vmbus_close_internal(struct vmbus_channel *channel)
	int ret;

	/*
	 * process_chn_event(), running in the tasklet, can race
	 * vmbus_on_event(), running in the tasklet, can race
	 * with vmbus_close_internal() in the case of SMP guest, e.g., when
	 * the former is accessing channel->inbound.ring_buffer, the latter
	 * could be freeing the ring_buffer pages.
+7 −9
Original line number Diff line number Diff line
@@ -339,6 +339,9 @@ static struct vmbus_channel *alloc_channel(void)
	INIT_LIST_HEAD(&channel->sc_list);
	INIT_LIST_HEAD(&channel->percpu_list);

	tasklet_init(&channel->callback_event,
		     vmbus_on_event, (unsigned long)channel);

	return channel;
}

@@ -347,6 +350,7 @@ static struct vmbus_channel *alloc_channel(void)
 */
static void free_channel(struct vmbus_channel *channel)
{
	tasklet_kill(&channel->callback_event);
	kfree(channel);
}

@@ -380,21 +384,15 @@ static void vmbus_release_relid(u32 relid)

void hv_event_tasklet_disable(struct vmbus_channel *channel)
{
	struct hv_per_cpu_context *hv_cpu;

	hv_cpu = per_cpu_ptr(hv_context.cpu_context, channel->target_cpu);
	tasklet_disable(&hv_cpu->event_dpc);
	tasklet_disable(&channel->callback_event);
}

void hv_event_tasklet_enable(struct vmbus_channel *channel)
{
	struct hv_per_cpu_context *hv_cpu;

	hv_cpu = per_cpu_ptr(hv_context.cpu_context, channel->target_cpu);
	tasklet_enable(&hv_cpu->event_dpc);
	tasklet_enable(&channel->callback_event);

	/* In case there is any pending event */
	tasklet_schedule(&hv_cpu->event_dpc);
	tasklet_schedule(&channel->callback_event);
}

void hv_process_channel_removal(struct vmbus_channel *channel, u32 relid)
+3 −75
Original line number Diff line number Diff line
@@ -259,29 +259,6 @@ void vmbus_disconnect(void)
	vmbus_connection.monitor_pages[1] = NULL;
}

/*
 * Map the given relid to the corresponding channel based on the
 * per-cpu list of channels that have been affinitized to this CPU.
 * This will be used in the channel callback path as we can do this
 * mapping in a lock-free fashion.
 */
static struct vmbus_channel *pcpu_relid2channel(u32 relid)
{
	struct hv_per_cpu_context *hv_cpu
		= this_cpu_ptr(hv_context.cpu_context);
	struct vmbus_channel *found_channel = NULL;
	struct vmbus_channel *channel;

	list_for_each_entry(channel, &hv_cpu->chan_list, percpu_list) {
		if (channel->offermsg.child_relid == relid) {
			found_channel = channel;
			break;
		}
	}

	return found_channel;
}

/*
 * relid2channel - Get the channel object given its
 * child relative id (ie channel id)
@@ -318,24 +295,15 @@ struct vmbus_channel *relid2channel(u32 relid)
}

/*
 * process_chn_event - Process a channel event notification
 * vmbus_on_event - Process a channel event notification
 */
static void process_chn_event(u32 relid)
void vmbus_on_event(unsigned long data)
{
	struct vmbus_channel *channel;
	struct vmbus_channel *channel = (void *) data;
	void *arg;
	bool read_state;
	u32 bytes_to_read;

	/*
	 * Find the channel based on this relid and invokes the
	 * channel callback to process the event
	 */
	channel = pcpu_relid2channel(relid);

	if (!channel)
		return;

	/*
	 * A channel once created is persistent even when there
	 * is no driver handling the device. An unloading driver
@@ -344,7 +312,6 @@ static void process_chn_event(u32 relid)
	 * Thus, checking and invoking the driver specific callback takes
	 * care of orderly unloading of the driver.
	 */

	if (channel->onchannel_callback != NULL) {
		arg = channel->channel_callback_context;
		read_state = channel->batched_reading;
@@ -372,45 +339,6 @@ static void process_chn_event(u32 relid)
	}
}

/*
 * vmbus_on_event - Handler for events
 */
void vmbus_on_event(unsigned long data)
{
	struct hv_per_cpu_context *hv_cpu = (void *)data;
	unsigned long *recv_int_page;
	u32 maxbits, relid;

	if (vmbus_proto_version < VERSION_WIN8) {
		maxbits = MAX_NUM_CHANNELS_SUPPORTED;
		recv_int_page = vmbus_connection.recv_int_page;
	} else {
		/*
		 * When the host is win8 and beyond, the event page
		 * can be directly checked to get the id of the channel
		 * that has the interrupt pending.
		 */
		void *page_addr = hv_cpu->synic_event_page;
		union hv_synic_event_flags *event
			= (union hv_synic_event_flags *)page_addr +
						 VMBUS_MESSAGE_SINT;

		maxbits = HV_EVENT_FLAGS_COUNT;
		recv_int_page = event->flags;
	}

	if (unlikely(!recv_int_page))
		return;

	for_each_set_bit(relid, recv_int_page, maxbits) {
		if (sync_test_and_clear_bit(relid, recv_int_page)) {
			/* Special case - vmbus channel protocol msg */
			if (relid != 0)
				process_chn_event(relid);
		}
	}
}

/*
 * vmbus_post_msg - Send a msg on the vmbus's message connection
 */
+0 −2
Original line number Diff line number Diff line
@@ -156,8 +156,6 @@ int hv_synic_alloc(void)
			= per_cpu_ptr(hv_context.cpu_context, cpu);

		memset(hv_cpu, 0, sizeof(*hv_cpu));
		tasklet_init(&hv_cpu->event_dpc,
			     vmbus_on_event, (unsigned long) hv_cpu);
		tasklet_init(&hv_cpu->msg_dpc,
			     vmbus_on_msg_dpc, (unsigned long) hv_cpu);

+0 −1
Original line number Diff line number Diff line
@@ -206,7 +206,6 @@ struct hv_per_cpu_context {
	 * we will manage the tasklet that handles events messages on a per CPU
	 * basis.
	 */
	struct tasklet_struct event_dpc;
	struct tasklet_struct msg_dpc;

	/*
Loading