Author: sephe
Date: Mon Jun 13 05:06:07 2016
New Revision: 301854
URL: https://svnweb.freebsd.org/changeset/base/301854

Log:
  MFC 294886
  
      hyperv/vmbus: Event handling code refactor.
  
      - Use taskqueue instead of swi for event handling.
      - Scan the interrupt flags in filter
      - Disable ringbuffer interrupt mask in filter to ensure no unnecessary
        interrupts.
  
      Submitted by:               Jun Su <junsu microsoft com>
      Reviewed by:                adrian, sephe, Dexuan <decui microsoft com>
      Approved by:                adrian (mentor)
      MFC after:          2 weeks
      Sponsored by:               Microsoft OSTC
      Differential Revision:      https://reviews.freebsd.org/D4920

Modified:
  stable/10/sys/dev/hyperv/include/hyperv.h
  stable/10/sys/dev/hyperv/vmbus/hv_channel.c
  stable/10/sys/dev/hyperv/vmbus/hv_connection.c
  stable/10/sys/dev/hyperv/vmbus/hv_vmbus_drv_freebsd.c
  stable/10/sys/dev/hyperv/vmbus/hv_vmbus_priv.h
Directory Properties:
  stable/10/   (props changed)

Modified: stable/10/sys/dev/hyperv/include/hyperv.h
==============================================================================
--- stable/10/sys/dev/hyperv/include/hyperv.h   Mon Jun 13 03:45:08 2016        
(r301853)
+++ stable/10/sys/dev/hyperv/include/hyperv.h   Mon Jun 13 05:06:07 2016        
(r301854)
@@ -755,6 +755,8 @@ typedef struct hv_vmbus_channel {
 
        struct mtx                      inbound_lock;
 
+       struct taskqueue *              rxq;
+       struct task                     channel_task;
        hv_vmbus_pfn_channel_callback   on_channel_callback;
        void*                           channel_callback_context;
 

Modified: stable/10/sys/dev/hyperv/vmbus/hv_channel.c
==============================================================================
--- stable/10/sys/dev/hyperv/vmbus/hv_channel.c Mon Jun 13 03:45:08 2016        
(r301853)
+++ stable/10/sys/dev/hyperv/vmbus/hv_channel.c Mon Jun 13 05:06:07 2016        
(r301854)
@@ -52,6 +52,7 @@ static int    vmbus_channel_create_gpadl_h
                        uint32_t*                       message_count);
 
 static void    vmbus_channel_set_event(hv_vmbus_channel* channel);
+static void    VmbusProcessChannelEvent(void* channel, int pending);
 
 /**
  *  @brief Trigger an event notification on the specified channel
@@ -115,6 +116,9 @@ hv_vmbus_channel_open(
        new_channel->on_channel_callback = pfn_on_channel_callback;
        new_channel->channel_callback_context = context;
 
+       new_channel->rxq = 
hv_vmbus_g_context.hv_event_queue[new_channel->target_cpu];
+       TASK_INIT(&new_channel->channel_task, 0, VmbusProcessChannelEvent, 
new_channel);
+
        /* Allocate the ring buffer */
        out = contigmalloc((send_ring_buffer_size + recv_ring_buffer_size),
            M_DEVBUF, M_ZERO, 0UL, BUS_SPACE_MAXADDR, PAGE_SIZE, 0);
@@ -518,6 +522,7 @@ static void
 hv_vmbus_channel_close_internal(hv_vmbus_channel *channel)
 {
        int ret = 0;
+       struct taskqueue *rxq = channel->rxq;
        hv_vmbus_channel_close_channel* msg;
        hv_vmbus_channel_msg_info* info;
 
@@ -525,6 +530,11 @@ hv_vmbus_channel_close_internal(hv_vmbus
        channel->sc_creation_callback = NULL;
 
        /*
+        * set rxq to NULL to avoid more requests be scheduled
+        */
+       channel->rxq = NULL;
+       taskqueue_drain(rxq, &channel->channel_task);
+       /*
         * Grab the lock to prevent race condition when a packet received
         * and unloading driver is in the process.
         */
@@ -877,3 +887,67 @@ hv_vmbus_channel_recv_packet_raw(
 
        return (0);
 }
+
+
+/**
+ * Process a channel event notification
+ */
+static void
+VmbusProcessChannelEvent(void* context, int pending)
+{
+       void* arg;
+       uint32_t bytes_to_read;
+       hv_vmbus_channel* channel = (hv_vmbus_channel*)context;
+       boolean_t is_batched_reading;
+
+       /**
+        * Find the channel based on this relid and invokes
+        * the channel callback to process the event
+        */
+
+       if (channel == NULL) {
+               return;
+       }
+       /**
+        * To deal with the race condition where we might
+        * receive a packet while the relevant driver is
+        * being unloaded, dispatch the callback while
+        * holding the channel lock. The unloading driver
+        * will acquire the same channel lock to set the
+        * callback to NULL. This closes the window.
+        */
+
+       /*
+        * Disable the lock due to newly added WITNESS check in r277723.
+        * Will seek other way to avoid race condition.
+        * -- whu
+        */
+       // mtx_lock(&channel->inbound_lock);
+       if (channel->on_channel_callback != NULL) {
+               arg = channel->channel_callback_context;
+               is_batched_reading = channel->batched_reading;
+               /*
+                * Optimize host to guest signaling by ensuring:
+                * 1. While reading the channel, we disable interrupts from
+                *    host.
+                * 2. Ensure that we process all posted messages from the host
+                *    before returning from this callback.
+                * 3. Once we return, enable signaling from the host. Once this
+                *    state is set we check to see if additional packets are
+                *    available to read. In this case we repeat the process.
+                */
+               do {
+                       if (is_batched_reading)
+                               hv_ring_buffer_read_begin(&channel->inbound);
+
+                       channel->on_channel_callback(arg);
+
+                       if (is_batched_reading)
+                               bytes_to_read =
+                                   hv_ring_buffer_read_end(&channel->inbound);
+                       else
+                               bytes_to_read = 0;
+               } while (is_batched_reading && (bytes_to_read != 0));
+       }
+       // mtx_unlock(&channel->inbound_lock);
+}

Modified: stable/10/sys/dev/hyperv/vmbus/hv_connection.c
==============================================================================
--- stable/10/sys/dev/hyperv/vmbus/hv_connection.c      Mon Jun 13 03:45:08 
2016        (r301853)
+++ stable/10/sys/dev/hyperv/vmbus/hv_connection.c      Mon Jun 13 05:06:07 
2016        (r301854)
@@ -339,78 +339,12 @@ hv_vmbus_disconnect(void) {
 }
 
 /**
- * Process a channel event notification
- */
-static void
-VmbusProcessChannelEvent(uint32_t relid) 
-{
-       void* arg;
-       uint32_t bytes_to_read;
-       hv_vmbus_channel* channel;
-       boolean_t is_batched_reading;
-
-       /**
-        * Find the channel based on this relid and invokes
-        * the channel callback to process the event
-        */
-
-       channel = hv_vmbus_g_connection.channels[relid];
-
-       if (channel == NULL) {
-               return;
-       }
-       /**
-        * To deal with the race condition where we might
-        * receive a packet while the relevant driver is 
-        * being unloaded, dispatch the callback while 
-        * holding the channel lock. The unloading driver
-        * will acquire the same channel lock to set the
-        * callback to NULL. This closes the window.
-        */
-
-       /*
-        * Disable the lock due to newly added WITNESS check in r277723.
-        * Will seek other way to avoid race condition.
-        * -- whu
-        */
-       // mtx_lock(&channel->inbound_lock);
-       if (channel->on_channel_callback != NULL) {
-               arg = channel->channel_callback_context;
-               is_batched_reading = channel->batched_reading;
-               /*
-                * Optimize host to guest signaling by ensuring:
-                * 1. While reading the channel, we disable interrupts from
-                *    host.
-                * 2. Ensure that we process all posted messages from the host
-                *    before returning from this callback.
-                * 3. Once we return, enable signaling from the host. Once this
-                *    state is set we check to see if additional packets are
-                *    available to read. In this case we repeat the process.
-                */
-               do {
-                       if (is_batched_reading)
-                               hv_ring_buffer_read_begin(&channel->inbound);
-
-                       channel->on_channel_callback(arg);
-
-                       if (is_batched_reading)
-                               bytes_to_read =
-                                   hv_ring_buffer_read_end(&channel->inbound);
-                       else
-                               bytes_to_read = 0;
-               } while (is_batched_reading && (bytes_to_read != 0));
-       }
-       // mtx_unlock(&channel->inbound_lock);
-}
-
-/**
  * Handler for events
  */
 void
-hv_vmbus_on_events(void *arg) 
+hv_vmbus_on_events(int cpu)
 {
        int bit;
-       int cpu;
        int dword;
        void *page_addr;
        uint32_t* recv_interrupt_page = NULL;
@@ -419,7 +353,6 @@ hv_vmbus_on_events(void *arg) 
        hv_vmbus_synic_event_flags *event;
        /* int maxdword = PAGE_SIZE >> 3; */
 
-       cpu = (int)(long)arg;
        KASSERT(cpu <= mp_maxid, ("VMBUS: hv_vmbus_on_events: "
            "cpu out of range!"));
 
@@ -461,8 +394,14 @@ hv_vmbus_on_events(void *arg) 
                                 */
                                continue;
                            } else {
-                               VmbusProcessChannelEvent(rel_id);
-
+                               hv_vmbus_channel * channel = 
hv_vmbus_g_connection.channels[rel_id];
+                               /* if channel is closed or closing */
+                               if (channel == NULL || channel->rxq == NULL)
+                                       continue;
+
+                               if (channel->batched_reading)
+                                       
hv_ring_buffer_read_begin(&channel->inbound);
+                               taskqueue_enqueue_fast(channel->rxq, 
&channel->channel_task);
                            }
                        }
                    }

Modified: stable/10/sys/dev/hyperv/vmbus/hv_vmbus_drv_freebsd.c
==============================================================================
--- stable/10/sys/dev/hyperv/vmbus/hv_vmbus_drv_freebsd.c       Mon Jun 13 
03:45:08 2016        (r301853)
+++ stable/10/sys/dev/hyperv/vmbus/hv_vmbus_drv_freebsd.c       Mon Jun 13 
05:06:07 2016        (r301854)
@@ -177,7 +177,7 @@ hv_vmbus_isr(struct trapframe *frame)
            (hv_vmbus_protocal_version == HV_VMBUS_VERSION_WIN7)) {
                /* Since we are a child, we only need to check bit 0 */
                if (synch_test_and_clear_bit(0, &event->flags32[0])) {
-                       swi_sched(hv_vmbus_g_context.event_swintr[cpu], 0);
+                       hv_vmbus_on_events(cpu);
                }
        } else {
                /*
@@ -187,7 +187,7 @@ hv_vmbus_isr(struct trapframe *frame)
                 * Directly schedule the event software interrupt on
                 * current cpu.
                 */
-               swi_sched(hv_vmbus_g_context.event_swintr[cpu], 0);
+               hv_vmbus_on_events(cpu);
        }
 
        /* Check if there are actual msgs to be process */
@@ -225,7 +225,6 @@ hv_vmbus_isr(struct trapframe *frame)
        return (FILTER_HANDLED);
 }
 
-uint32_t hv_vmbus_swintr_event_cpu[MAXCPU];
 u_long *hv_vmbus_intr_cpu[MAXCPU];
 
 void
@@ -455,6 +454,19 @@ vmbus_vector_free(int vector)
 
 #endif /* HYPERV */
 
+static void
+vmbus_cpuset_setthread_task(void *xmask, int pending __unused)
+{
+       cpuset_t *mask = xmask;
+       int error;
+
+       error = cpuset_setthread(curthread->td_tid, mask);
+       if (error) {
+               panic("curthread=%ju: can't pin; error=%d",
+                   (uintmax_t)curthread->td_tid, error);
+       }
+}
+
 /**
  * @brief Main vmbus driver initialization routine.
  *
@@ -472,6 +484,7 @@ vmbus_bus_init(void)
 {
        int i, j, n, ret;
        char buf[MAXCOMLEN + 1];
+       cpuset_t cpu_mask;
 
        if (vmbus_inited)
                return (0);
@@ -508,10 +521,7 @@ vmbus_bus_init(void)
        setup_args.vector = hv_vmbus_g_context.hv_cb_vector;
 
        CPU_FOREACH(j) {
-               hv_vmbus_swintr_event_cpu[j] = 0;
-               hv_vmbus_g_context.hv_event_intr_event[j] = NULL;
                hv_vmbus_g_context.hv_msg_intr_event[j] = NULL;
-               hv_vmbus_g_context.event_swintr[j] = NULL;
                hv_vmbus_g_context.msg_swintr[j] = NULL;
 
                snprintf(buf, sizeof(buf), "cpu%d:hyperv", j);
@@ -525,6 +535,26 @@ vmbus_bus_init(void)
         * Per cpu setup.
         */
        CPU_FOREACH(j) {
+               struct task cpuset_task;
+
+               /*
+                * Setup taskqueue to handle events
+                */
+               hv_vmbus_g_context.hv_event_queue[j] = 
taskqueue_create_fast("hyperv event", M_WAITOK,
+                       taskqueue_thread_enqueue, 
&hv_vmbus_g_context.hv_event_queue[j]);
+               if (hv_vmbus_g_context.hv_event_queue[j] == NULL) {
+                       if (bootverbose)
+                               printf("VMBUS: failed to setup taskqueue\n");
+                       goto cleanup1;
+               }
+               taskqueue_start_threads(&hv_vmbus_g_context.hv_event_queue[j], 
1, PI_NET,
+                       "hvevent%d", j);
+
+               CPU_SETOF(j, &cpu_mask);
+               TASK_INIT(&cpuset_task, 0, vmbus_cpuset_setthread_task, 
&cpu_mask);
+               taskqueue_enqueue(hv_vmbus_g_context.hv_event_queue[j], 
&cpuset_task);
+               taskqueue_drain(hv_vmbus_g_context.hv_event_queue[j], 
&cpuset_task);
+
                /*
                 * Setup software interrupt thread and handler for msg handling.
                 */
@@ -543,7 +573,7 @@ vmbus_bus_init(void)
                 */
                ret = intr_event_bind(hv_vmbus_g_context.hv_msg_intr_event[j],
                    j);
-               if (ret) {
+               if (ret) {
                        if(bootverbose)
                                printf("VMBUS: failed to bind msg swi thread "
                                    "to cpu %d\n", j);
@@ -551,20 +581,6 @@ vmbus_bus_init(void)
                }
 
                /*
-                * Setup software interrupt thread and handler for
-                * event handling.
-                */
-               ret = swi_add(&hv_vmbus_g_context.hv_event_intr_event[j],
-                   "hv_event", hv_vmbus_on_events, (void *)(long)j,
-                   SWI_CLOCK, 0, &hv_vmbus_g_context.event_swintr[j]);
-               if (ret) {
-                       if(bootverbose)
-                               printf("VMBUS: failed to setup event swi for "
-                                   "cpu %d\n", j);
-                       goto cleanup1;
-               }
-
-               /*
                 * Prepare the per cpu msg and event pages to be called on each 
cpu.
                 */
                for(i = 0; i < 2; i++) {
@@ -607,12 +623,11 @@ vmbus_bus_init(void)
         * remove swi and vmbus callback vector;
         */
        CPU_FOREACH(j) {
+               if (hv_vmbus_g_context.hv_event_queue[j] != NULL)
+                       taskqueue_free(hv_vmbus_g_context.hv_event_queue[j]);
                if (hv_vmbus_g_context.msg_swintr[j] != NULL)
                        swi_remove(hv_vmbus_g_context.msg_swintr[j]);
-               if (hv_vmbus_g_context.event_swintr[j] != NULL)
-                       swi_remove(hv_vmbus_g_context.event_swintr[j]);
                hv_vmbus_g_context.hv_msg_intr_event[j] = NULL; 
-               hv_vmbus_g_context.hv_event_intr_event[j] = NULL;       
        }
 
        vmbus_vector_free(hv_vmbus_g_context.hv_cb_vector);
@@ -677,12 +692,11 @@ vmbus_bus_exit(void)
 
        /* remove swi */
        CPU_FOREACH(i) {
+               if (hv_vmbus_g_context.hv_event_queue[i] != NULL)
+                       taskqueue_free(hv_vmbus_g_context.hv_event_queue[i]);
                if (hv_vmbus_g_context.msg_swintr[i] != NULL)
                        swi_remove(hv_vmbus_g_context.msg_swintr[i]);
-               if (hv_vmbus_g_context.event_swintr[i] != NULL)
-                       swi_remove(hv_vmbus_g_context.event_swintr[i]);
                hv_vmbus_g_context.hv_msg_intr_event[i] = NULL; 
-               hv_vmbus_g_context.hv_event_intr_event[i] = NULL;       
        }
 
        vmbus_vector_free(hv_vmbus_g_context.hv_cb_vector);

Modified: stable/10/sys/dev/hyperv/vmbus/hv_vmbus_priv.h
==============================================================================
--- stable/10/sys/dev/hyperv/vmbus/hv_vmbus_priv.h      Mon Jun 13 03:45:08 
2016        (r301853)
+++ stable/10/sys/dev/hyperv/vmbus/hv_vmbus_priv.h      Mon Jun 13 05:06:07 
2016        (r301854)
@@ -202,9 +202,8 @@ typedef struct {
         * Each cpu has its own software interrupt handler for channel
         * event and msg handling.
         */
-       struct intr_event               *hv_event_intr_event[MAXCPU];
+       struct taskqueue                *hv_event_queue[MAXCPU];
        struct intr_event               *hv_msg_intr_event[MAXCPU];
-       void                            *event_swintr[MAXCPU];
        void                            *msg_swintr[MAXCPU];
        /*
         * Host use this vector to intrrupt guest for vmbus channel
@@ -717,7 +716,7 @@ int                 hv_vmbus_connect(void);
 int                    hv_vmbus_disconnect(void);
 int                    hv_vmbus_post_message(void *buffer, size_t buf_size);
 int                    hv_vmbus_set_event(hv_vmbus_channel *channel);
-void                   hv_vmbus_on_events(void *);
+void                   hv_vmbus_on_events(int cpu);
 
 /**
  * Event Timer interfaces
_______________________________________________
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