]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - drivers/hv/connection.c
Merge tag 'stable/for-linus-3.16-rc5-tag' of git://git.kernel.org/pub/scm/linux/kerne...
[karo-tx-linux.git] / drivers / hv / connection.c
index 2e7801af466e586fe34bd7c1400723540e0d2440..ae22e3c1fc4c189f675b1ccd2c18cf90d5d4d8fc 100644 (file)
@@ -224,8 +224,8 @@ cleanup:
                vmbus_connection.int_page = NULL;
        }
 
-       free_pages((unsigned long)vmbus_connection.monitor_pages[0], 1);
-       free_pages((unsigned long)vmbus_connection.monitor_pages[1], 1);
+       free_pages((unsigned long)vmbus_connection.monitor_pages[0], 0);
+       free_pages((unsigned long)vmbus_connection.monitor_pages[1], 0);
        vmbus_connection.monitor_pages[0] = NULL;
        vmbus_connection.monitor_pages[1] = NULL;
 
@@ -234,6 +234,28 @@ cleanup:
        return ret;
 }
 
+/*
+ * 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 vmbus_channel *channel;
+       struct vmbus_channel *found_channel  = NULL;
+       int cpu = smp_processor_id();
+       struct list_head *pcpu_head = &hv_context.percpu_list[cpu];
+
+       list_for_each_entry(channel, pcpu_head, percpu_list) {
+               if (channel->offermsg.child_relid == relid) {
+                       found_channel = channel;
+                       break;
+               }
+       }
+
+       return found_channel;
+}
 
 /*
  * relid2channel - Get the channel object given its
@@ -277,7 +299,6 @@ struct vmbus_channel *relid2channel(u32 relid)
 static void process_chn_event(u32 relid)
 {
        struct vmbus_channel *channel;
-       unsigned long flags;
        void *arg;
        bool read_state;
        u32 bytes_to_read;
@@ -286,7 +307,7 @@ static void process_chn_event(u32 relid)
         * Find the channel based on this relid and invokes the
         * channel callback to process the event
         */
-       channel = relid2channel(relid);
+       channel = pcpu_relid2channel(relid);
 
        if (!channel) {
                pr_err("channel not found for relid - %u\n", relid);
@@ -296,13 +317,12 @@ static void process_chn_event(u32 relid)
        /*
         * A channel once created is persistent even when there
         * is no driver handling the device. An unloading driver
-        * sets the onchannel_callback to NULL under the
-        * protection of the channel inbound_lock. Thus, checking
-        * and invoking the driver specific callback takes care of
-        * orderly unloading of the driver.
+        * sets the onchannel_callback to NULL on the same CPU
+        * as where this interrupt is handled (in an interrupt context).
+        * Thus, checking and invoking the driver specific callback takes
+        * care of orderly unloading of the driver.
         */
 
-       spin_lock_irqsave(&channel->inbound_lock, flags);
        if (channel->onchannel_callback != NULL) {
                arg = channel->channel_callback_context;
                read_state = channel->batched_reading;
@@ -319,15 +339,18 @@ static void process_chn_event(u32 relid)
                 */
 
                do {
-                       hv_begin_read(&channel->inbound);
+                       if (read_state)
+                               hv_begin_read(&channel->inbound);
                        channel->onchannel_callback(arg);
-                       bytes_to_read = hv_end_read(&channel->inbound);
+                       if (read_state)
+                               bytes_to_read = hv_end_read(&channel->inbound);
+                       else
+                               bytes_to_read = 0;
                } while (read_state && (bytes_to_read != 0));
        } else {
                pr_err("no channel callback for relid - %u\n", relid);
        }
 
-       spin_unlock_irqrestore(&channel->inbound_lock, flags);
 }
 
 /*