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);
 }
 
 /**
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to