mirror of
				https://github.com/linuxkit/linuxkit.git
				synced 2025-11-04 07:38:28 +00:00 
			
		
		
		
	The patches from 4.12 applied cleanly, except for 81304747d9
("Drivers: hv: vmbus: Fix rescind handling"), which was already
in upstream so has been dropped from the patch series.
The kernel config is from 4.12 run through defconfig/oldconfig to
pick up any new defaults.
Signed-off-by: Rolf Neugebauer <rolf.neugebauer@docker.com>
		
	
		
			
				
	
	
		
			190 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			190 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
From 038dde9e9da90707062ebf934ee837b450454f2c Mon Sep 17 00:00:00 2001
 | 
						||
From: Dexuan Cui <decui@microsoft.com>
 | 
						||
Date: Fri, 5 May 2017 16:57:23 -0600
 | 
						||
Subject: [PATCH 03/17] vmbus: dynamically enqueue/dequeue a channel on
 | 
						||
 vmbus_open/close
 | 
						||
MIME-Version: 1.0
 | 
						||
Content-Type: text/plain; charset=UTF-8
 | 
						||
Content-Transfer-Encoding: 8bit
 | 
						||
 | 
						||
A just-closed channel may have a pending interrupt, and later when a new
 | 
						||
channel with the same channel ID is not being fully initialized, the
 | 
						||
pending interrupt of the previous channel with the same channel ID can run
 | 
						||
the channel callback on the new channel data structure, causing a crash
 | 
						||
of NULL pointer dereferencing.
 | 
						||
 | 
						||
Normally it’s pretty hard to reproduce the race condition, but it can
 | 
						||
indeed happen with specially-designed hv_sock stress test cases.
 | 
						||
 | 
						||
Signed-off-by: Dexuan Cui <decui@microsoft.com>
 | 
						||
Reported-by: Rolf Neugebauer <rolf.neugebauer@docker.com>
 | 
						||
Tested-by: Rolf Neugebauer <rolf.neugebauer@docker.com>
 | 
						||
Cc: K. Y. Srinivasan <kys@microsoft.com>
 | 
						||
Cc: Haiyang Zhang <haiyangz@microsoft.com>
 | 
						||
Cc: Stephen Hemminger <sthemmin@microsoft.com>
 | 
						||
Origin: git@github.com:dcui/linux.git
 | 
						||
(cherry picked from commit fdd8e16c855a6c7238c654d7217dcf51c5533307)
 | 
						||
---
 | 
						||
 drivers/hv/channel.c      | 12 +++++++++---
 | 
						||
 drivers/hv/channel_mgmt.c | 50 +++++++++++++++++++++--------------------------
 | 
						||
 include/linux/hyperv.h    |  3 +++
 | 
						||
 3 files changed, 34 insertions(+), 31 deletions(-)
 | 
						||
 | 
						||
diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c
 | 
						||
index 42498ecd0f02..d4243b5c39b7 100644
 | 
						||
--- a/drivers/hv/channel.c
 | 
						||
+++ b/drivers/hv/channel.c
 | 
						||
@@ -177,6 +177,8 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
 | 
						||
 		      &vmbus_connection.chn_msg_list);
 | 
						||
 	spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
 | 
						||
 
 | 
						||
+	hv_percpu_channel_enq(newchannel);
 | 
						||
+
 | 
						||
 	ret = vmbus_post_msg(open_msg,
 | 
						||
 			     sizeof(struct vmbus_channel_open_channel), true);
 | 
						||
 
 | 
						||
@@ -189,23 +191,25 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
 | 
						||
 
 | 
						||
 	if (ret != 0) {
 | 
						||
 		err = ret;
 | 
						||
-		goto error_free_gpadl;
 | 
						||
+		goto error_deq_channel;
 | 
						||
 	}
 | 
						||
 
 | 
						||
 	if (newchannel->rescind) {
 | 
						||
 		err = -ENODEV;
 | 
						||
-		goto error_free_gpadl;
 | 
						||
+		goto error_deq_channel;
 | 
						||
 	}
 | 
						||
 
 | 
						||
 	if (open_info->response.open_result.status) {
 | 
						||
 		err = -EAGAIN;
 | 
						||
-		goto error_free_gpadl;
 | 
						||
+		goto error_deq_channel;
 | 
						||
 	}
 | 
						||
 
 | 
						||
 	newchannel->state = CHANNEL_OPENED_STATE;
 | 
						||
 	kfree(open_info);
 | 
						||
 	return 0;
 | 
						||
 
 | 
						||
+error_deq_channel:
 | 
						||
+	hv_percpu_channel_deq(newchannel);
 | 
						||
 error_free_gpadl:
 | 
						||
 	vmbus_teardown_gpadl(newchannel, newchannel->ringbuffer_gpadlhandle);
 | 
						||
 	kfree(open_info);
 | 
						||
@@ -551,6 +555,8 @@ static int vmbus_close_internal(struct vmbus_channel *channel)
 | 
						||
 		goto out;
 | 
						||
 	}
 | 
						||
 
 | 
						||
+	hv_percpu_channel_deq(channel);
 | 
						||
+
 | 
						||
 	channel->state = CHANNEL_OPEN_STATE;
 | 
						||
 	channel->sc_creation_callback = NULL;
 | 
						||
 	/* Stop callback and cancel the timer asap */
 | 
						||
diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c
 | 
						||
index 4bbb8dea4727..1afa7a699dd2 100644
 | 
						||
--- a/drivers/hv/channel_mgmt.c
 | 
						||
+++ b/drivers/hv/channel_mgmt.c
 | 
						||
@@ -362,6 +362,17 @@ static void percpu_channel_enq(void *arg)
 | 
						||
 	list_add_tail_rcu(&channel->percpu_list, &hv_cpu->chan_list);
 | 
						||
 }
 | 
						||
 
 | 
						||
+void hv_percpu_channel_enq(struct vmbus_channel *channel)
 | 
						||
+{
 | 
						||
+	if (channel->target_cpu != get_cpu())
 | 
						||
+		smp_call_function_single(channel->target_cpu,
 | 
						||
+					 percpu_channel_enq, channel, true);
 | 
						||
+	else
 | 
						||
+		percpu_channel_enq(channel);
 | 
						||
+
 | 
						||
+	put_cpu();
 | 
						||
+}
 | 
						||
+
 | 
						||
 static void percpu_channel_deq(void *arg)
 | 
						||
 {
 | 
						||
 	struct vmbus_channel *channel = arg;
 | 
						||
@@ -369,6 +380,17 @@ static void percpu_channel_deq(void *arg)
 | 
						||
 	list_del_rcu(&channel->percpu_list);
 | 
						||
 }
 | 
						||
 
 | 
						||
+void hv_percpu_channel_deq(struct vmbus_channel *channel)
 | 
						||
+{
 | 
						||
+	if (channel->target_cpu != get_cpu())
 | 
						||
+		smp_call_function_single(channel->target_cpu,
 | 
						||
+					 percpu_channel_deq, channel, true);
 | 
						||
+	else
 | 
						||
+		percpu_channel_deq(channel);
 | 
						||
+
 | 
						||
+	put_cpu();
 | 
						||
+}
 | 
						||
+
 | 
						||
 
 | 
						||
 static void vmbus_release_relid(u32 relid)
 | 
						||
 {
 | 
						||
@@ -389,15 +411,6 @@ void hv_process_channel_removal(struct vmbus_channel *channel, u32 relid)
 | 
						||
 	BUG_ON(!channel->rescind);
 | 
						||
 	BUG_ON(!mutex_is_locked(&vmbus_connection.channel_mutex));
 | 
						||
 
 | 
						||
-	if (channel->target_cpu != get_cpu()) {
 | 
						||
-		put_cpu();
 | 
						||
-		smp_call_function_single(channel->target_cpu,
 | 
						||
-					 percpu_channel_deq, channel, true);
 | 
						||
-	} else {
 | 
						||
-		percpu_channel_deq(channel);
 | 
						||
-		put_cpu();
 | 
						||
-	}
 | 
						||
-
 | 
						||
 	if (channel->primary_channel == NULL) {
 | 
						||
 		list_del(&channel->listentry);
 | 
						||
 
 | 
						||
@@ -490,16 +503,6 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel)
 | 
						||
 
 | 
						||
 	init_vp_index(newchannel, dev_type);
 | 
						||
 
 | 
						||
-	if (newchannel->target_cpu != get_cpu()) {
 | 
						||
-		put_cpu();
 | 
						||
-		smp_call_function_single(newchannel->target_cpu,
 | 
						||
-					 percpu_channel_enq,
 | 
						||
-					 newchannel, true);
 | 
						||
-	} else {
 | 
						||
-		percpu_channel_enq(newchannel);
 | 
						||
-		put_cpu();
 | 
						||
-	}
 | 
						||
-
 | 
						||
 	/*
 | 
						||
 	 * This state is used to indicate a successful open
 | 
						||
 	 * so that when we do close the channel normally, we
 | 
						||
@@ -549,15 +552,6 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel)
 | 
						||
 	list_del(&newchannel->listentry);
 | 
						||
 	mutex_unlock(&vmbus_connection.channel_mutex);
 | 
						||
 
 | 
						||
-	if (newchannel->target_cpu != get_cpu()) {
 | 
						||
-		put_cpu();
 | 
						||
-		smp_call_function_single(newchannel->target_cpu,
 | 
						||
-					 percpu_channel_deq, newchannel, true);
 | 
						||
-	} else {
 | 
						||
-		percpu_channel_deq(newchannel);
 | 
						||
-		put_cpu();
 | 
						||
-	}
 | 
						||
-
 | 
						||
 	vmbus_release_relid(newchannel->offermsg.child_relid);
 | 
						||
 
 | 
						||
 err_free_chan:
 | 
						||
diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h
 | 
						||
index b7d7bbec74e0..f5d3e8c01401 100644
 | 
						||
--- a/include/linux/hyperv.h
 | 
						||
+++ b/include/linux/hyperv.h
 | 
						||
@@ -1453,6 +1453,9 @@ extern bool vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, u8 *buf,
 | 
						||
 				const int *srv_version, int srv_vercnt,
 | 
						||
 				int *nego_fw_version, int *nego_srv_version);
 | 
						||
 
 | 
						||
+void hv_percpu_channel_enq(struct vmbus_channel *channel);
 | 
						||
+void hv_percpu_channel_deq(struct vmbus_channel *channel);
 | 
						||
+
 | 
						||
 void hv_process_channel_removal(struct vmbus_channel *channel, u32 relid);
 | 
						||
 
 | 
						||
 void vmbus_setevent(struct vmbus_channel *channel);
 | 
						||
-- 
 | 
						||
2.14.1
 | 
						||
 |