Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 14 Jul 2016 07:59:01 +0000 (UTC)
From:      Sepherosa Ziehau <sephe@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r302818 - head/sys/dev/hyperv/vmbus
Message-ID:  <201607140759.u6E7x1GW094415@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: sephe
Date: Thu Jul 14 07:59:01 2016
New Revision: 302818
URL: https://svnweb.freebsd.org/changeset/base/302818

Log:
  hyperv/vmbus: Fix the racy channel close.
  
  It is not safe to iterate the sub-channel list w/o lock on the
  close path, while it's even more difficult to hold the lock
  and iterate the sub-channel list.  We leverage the
  vmbua_{get,rel}_subchan() functions to solve this dilemma.
  
  MFC after:	1 week
  Sponsored by:	Microsoft OSTC
  Differential Revision:	https://reviews.freebsd.org/D7112

Modified:
  head/sys/dev/hyperv/vmbus/hv_channel.c

Modified: head/sys/dev/hyperv/vmbus/hv_channel.c
==============================================================================
--- head/sys/dev/hyperv/vmbus/hv_channel.c	Thu Jul 14 07:48:26 2016	(r302817)
+++ head/sys/dev/hyperv/vmbus/hv_channel.c	Thu Jul 14 07:59:01 2016	(r302818)
@@ -542,35 +542,40 @@ hv_vmbus_channel_close_internal(hv_vmbus
 	    M_DEVBUF);
 }
 
-/**
- * @brief Close the specified channel
+/*
+ * Caller should make sure that all sub-channels have
+ * been added to 'chan' and all to-be-closed channels
+ * are not being opened.
  */
 void
-hv_vmbus_channel_close(hv_vmbus_channel *channel)
+hv_vmbus_channel_close(struct hv_vmbus_channel *chan)
 {
-	hv_vmbus_channel*	sub_channel;
+	int subchan_cnt;
 
-	if (channel->primary_channel != NULL) {
+	if (!VMBUS_CHAN_ISPRIMARY(chan)) {
 		/*
-		 * We only close multi-channels when the primary is
-		 * closed.
+		 * Sub-channel is closed when its primary channel
+		 * is closed; done.
 		 */
 		return;
 	}
 
 	/*
-	 * Close all multi-channels first.
+	 * Close all sub-channels, if any.
 	 */
-	TAILQ_FOREACH(sub_channel, &channel->sc_list_anchor,
-	    sc_list_entry) {
-		if ((sub_channel->ch_stflags & VMBUS_CHAN_ST_OPENED) == 0)
-			continue;
-		hv_vmbus_channel_close_internal(sub_channel);
+	subchan_cnt = chan->subchan_cnt;
+	if (subchan_cnt > 0) {
+		struct hv_vmbus_channel **subchan;
+		int i;
+
+		subchan = vmbus_get_subchan(chan, subchan_cnt);
+		for (i = 0; i < subchan_cnt; ++i)
+			hv_vmbus_channel_close_internal(subchan[i]);
+		vmbus_rel_subchan(subchan, subchan_cnt);
 	}
-	/*
-	 * Then close the primary channel.
-	 */
-	hv_vmbus_channel_close_internal(channel);
+
+	/* Then close the primary channel. */
+	hv_vmbus_channel_close_internal(chan);
 }
 
 /**



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201607140759.u6E7x1GW094415>