Merge tag 'for-linus-2' of git://git.kernel.org/pub/scm/linux/kernel/git/dledford...
[cascardo/linux.git] / drivers / hv / channel.c
index 56dd261..16f91c8 100644 (file)
@@ -43,7 +43,12 @@ static void vmbus_setevent(struct vmbus_channel *channel)
 {
        struct hv_monitor_page *monitorpage;
 
-       if (channel->offermsg.monitor_allocated) {
+       /*
+        * For channels marked as in "low latency" mode
+        * bypass the monitor page mechanism.
+        */
+       if ((channel->offermsg.monitor_allocated) &&
+           (!channel->low_latency)) {
                /* Each u32 represents 32 channels */
                sync_set_bit(channel->offermsg.child_relid & 31,
                        (unsigned long *) vmbus_connection.send_int_page +
@@ -70,12 +75,14 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
 {
        struct vmbus_channel_open_channel *open_msg;
        struct vmbus_channel_msginfo *open_info = NULL;
-       void *in, *out;
        unsigned long flags;
        int ret, err = 0;
-       unsigned long t;
        struct page *page;
 
+       if (send_ringbuffer_size % PAGE_SIZE ||
+           recv_ringbuffer_size % PAGE_SIZE)
+               return -EINVAL;
+
        spin_lock_irqsave(&newchannel->lock, flags);
        if (newchannel->state == CHANNEL_OPEN_STATE) {
                newchannel->state = CHANNEL_OPENING_STATE;
@@ -95,36 +102,33 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
                                recv_ringbuffer_size));
 
        if (!page)
-               out = (void *)__get_free_pages(GFP_KERNEL|__GFP_ZERO,
-                                              get_order(send_ringbuffer_size +
-                                              recv_ringbuffer_size));
-       else
-               out = (void *)page_address(page);
+               page = alloc_pages(GFP_KERNEL|__GFP_ZERO,
+                                  get_order(send_ringbuffer_size +
+                                            recv_ringbuffer_size));
 
-       if (!out) {
+       if (!page) {
                err = -ENOMEM;
-               goto error0;
+               goto error_set_chnstate;
        }
 
-       in = (void *)((unsigned long)out + send_ringbuffer_size);
-
-       newchannel->ringbuffer_pages = out;
+       newchannel->ringbuffer_pages = page_address(page);
        newchannel->ringbuffer_pagecount = (send_ringbuffer_size +
                                           recv_ringbuffer_size) >> PAGE_SHIFT;
 
-       ret = hv_ringbuffer_init(
-               &newchannel->outbound, out, send_ringbuffer_size);
+       ret = hv_ringbuffer_init(&newchannel->outbound, page,
+                                send_ringbuffer_size >> PAGE_SHIFT);
 
        if (ret != 0) {
                err = ret;
-               goto error0;
+               goto error_free_pages;
        }
 
-       ret = hv_ringbuffer_init(
-               &newchannel->inbound, in, recv_ringbuffer_size);
+       ret = hv_ringbuffer_init(&newchannel->inbound,
+                                &page[send_ringbuffer_size >> PAGE_SHIFT],
+                                recv_ringbuffer_size >> PAGE_SHIFT);
        if (ret != 0) {
                err = ret;
-               goto error0;
+               goto error_free_pages;
        }
 
 
@@ -132,14 +136,14 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
        newchannel->ringbuffer_gpadlhandle = 0;
 
        ret = vmbus_establish_gpadl(newchannel,
-                                        newchannel->outbound.ring_buffer,
-                                        send_ringbuffer_size +
-                                        recv_ringbuffer_size,
-                                        &newchannel->ringbuffer_gpadlhandle);
+                                   page_address(page),
+                                   send_ringbuffer_size +
+                                   recv_ringbuffer_size,
+                                   &newchannel->ringbuffer_gpadlhandle);
 
        if (ret != 0) {
                err = ret;
-               goto error0;
+               goto error_free_pages;
        }
 
        /* Create and init the channel open message */
@@ -148,7 +152,7 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
                           GFP_KERNEL);
        if (!open_info) {
                err = -ENOMEM;
-               goto error_gpadl;
+               goto error_free_gpadl;
        }
 
        init_completion(&open_info->waitevent);
@@ -164,7 +168,7 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
 
        if (userdatalen > MAX_USER_DEFINED_BYTES) {
                err = -EINVAL;
-               goto error_gpadl;
+               goto error_free_gpadl;
        }
 
        if (userdatalen)
@@ -180,14 +184,10 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
 
        if (ret != 0) {
                err = ret;
-               goto error1;
+               goto error_clean_msglist;
        }
 
-       t = wait_for_completion_timeout(&open_info->waitevent, 5*HZ);
-       if (t == 0) {
-               err = -ETIMEDOUT;
-               goto error1;
-       }
+       wait_for_completion(&open_info->waitevent);
 
        spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
        list_del(&open_info->msglistentry);
@@ -195,25 +195,27 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
 
        if (open_info->response.open_result.status) {
                err = -EAGAIN;
-               goto error_gpadl;
+               goto error_free_gpadl;
        }
 
        newchannel->state = CHANNEL_OPENED_STATE;
        kfree(open_info);
        return 0;
 
-error1:
+error_clean_msglist:
        spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
        list_del(&open_info->msglistentry);
        spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
 
-error_gpadl:
+error_free_gpadl:
        vmbus_teardown_gpadl(newchannel, newchannel->ringbuffer_gpadlhandle);
-
-error0:
-       free_pages((unsigned long)out,
-               get_order(send_ringbuffer_size + recv_ringbuffer_size));
        kfree(open_info);
+error_free_pages:
+       hv_ringbuffer_cleanup(&newchannel->outbound);
+       hv_ringbuffer_cleanup(&newchannel->inbound);
+       __free_pages(page,
+                    get_order(send_ringbuffer_size + recv_ringbuffer_size));
+error_set_chnstate:
        newchannel->state = CHANNEL_OPEN_STATE;
        return err;
 }
@@ -238,8 +240,7 @@ EXPORT_SYMBOL_GPL(vmbus_send_tl_connect_request);
  * create_gpadl_header - Creates a gpadl for the specified buffer
  */
 static int create_gpadl_header(void *kbuffer, u32 size,
-                                        struct vmbus_channel_msginfo **msginfo,
-                                        u32 *messagecount)
+                              struct vmbus_channel_msginfo **msginfo)
 {
        int i;
        int pagecount;
@@ -283,7 +284,6 @@ static int create_gpadl_header(void *kbuffer, u32 size,
                        gpadl_header->range[0].pfn_array[i] = slow_virt_to_phys(
                                kbuffer + PAGE_SIZE * i) >> PAGE_SHIFT;
                *msginfo = msgheader;
-               *messagecount = 1;
 
                pfnsum = pfncount;
                pfnleft = pagecount - pfncount;
@@ -323,7 +323,6 @@ static int create_gpadl_header(void *kbuffer, u32 size,
                        }
 
                        msgbody->msgsize = msgsize;
-                       (*messagecount)++;
                        gpadl_body =
                                (struct vmbus_channel_gpadl_body *)msgbody->msg;
 
@@ -352,6 +351,8 @@ static int create_gpadl_header(void *kbuffer, u32 size,
                msgheader = kzalloc(msgsize, GFP_KERNEL);
                if (msgheader == NULL)
                        goto nomem;
+
+               INIT_LIST_HEAD(&msgheader->submsglist);
                msgheader->msgsize = msgsize;
 
                gpadl_header = (struct vmbus_channel_gpadl_header *)
@@ -366,7 +367,6 @@ static int create_gpadl_header(void *kbuffer, u32 size,
                                kbuffer + PAGE_SIZE * i) >> PAGE_SHIFT;
 
                *msginfo = msgheader;
-               *messagecount = 1;
        }
 
        return 0;
@@ -390,8 +390,7 @@ int vmbus_establish_gpadl(struct vmbus_channel *channel, void *kbuffer,
        struct vmbus_channel_gpadl_header *gpadlmsg;
        struct vmbus_channel_gpadl_body *gpadl_body;
        struct vmbus_channel_msginfo *msginfo = NULL;
-       struct vmbus_channel_msginfo *submsginfo;
-       u32 msgcount;
+       struct vmbus_channel_msginfo *submsginfo, *tmp;
        struct list_head *curr;
        u32 next_gpadl_handle;
        unsigned long flags;
@@ -400,7 +399,7 @@ int vmbus_establish_gpadl(struct vmbus_channel *channel, void *kbuffer,
        next_gpadl_handle =
                (atomic_inc_return(&vmbus_connection.next_gpadl_handle) - 1);
 
-       ret = create_gpadl_header(kbuffer, size, &msginfo, &msgcount);
+       ret = create_gpadl_header(kbuffer, size, &msginfo);
        if (ret)
                return ret;
 
@@ -423,24 +422,21 @@ int vmbus_establish_gpadl(struct vmbus_channel *channel, void *kbuffer,
        if (ret != 0)
                goto cleanup;
 
-       if (msgcount > 1) {
-               list_for_each(curr, &msginfo->submsglist) {
-
-                       submsginfo = (struct vmbus_channel_msginfo *)curr;
-                       gpadl_body =
-                            (struct vmbus_channel_gpadl_body *)submsginfo->msg;
+       list_for_each(curr, &msginfo->submsglist) {
+               submsginfo = (struct vmbus_channel_msginfo *)curr;
+               gpadl_body =
+                       (struct vmbus_channel_gpadl_body *)submsginfo->msg;
 
-                       gpadl_body->header.msgtype =
-                               CHANNELMSG_GPADL_BODY;
-                       gpadl_body->gpadl = next_gpadl_handle;
+               gpadl_body->header.msgtype =
+                       CHANNELMSG_GPADL_BODY;
+               gpadl_body->gpadl = next_gpadl_handle;
 
-                       ret = vmbus_post_msg(gpadl_body,
-                                              submsginfo->msgsize -
-                                              sizeof(*submsginfo));
-                       if (ret != 0)
-                               goto cleanup;
+               ret = vmbus_post_msg(gpadl_body,
+                                    submsginfo->msgsize -
+                                    sizeof(*submsginfo));
+               if (ret != 0)
+                       goto cleanup;
 
-               }
        }
        wait_for_completion(&msginfo->waitevent);
 
@@ -451,6 +447,10 @@ cleanup:
        spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
        list_del(&msginfo->msglistentry);
        spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
+       list_for_each_entry_safe(submsginfo, tmp, &msginfo->submsglist,
+                                msglistentry) {
+               kfree(submsginfo);
+       }
 
        kfree(msginfo);
        return ret;
@@ -512,7 +512,6 @@ static void reset_channel_cb(void *arg)
 static int vmbus_close_internal(struct vmbus_channel *channel)
 {
        struct vmbus_channel_close_channel *msg;
-       struct tasklet_struct *tasklet;
        int ret;
 
        /*
@@ -524,8 +523,7 @@ static int vmbus_close_internal(struct vmbus_channel *channel)
         * To resolve the race, we can serialize them by disabling the
         * tasklet when the latter is running here.
         */
-       tasklet = hv_context.event_dpc[channel->target_cpu];
-       tasklet_disable(tasklet);
+       hv_event_tasklet_disable(channel);
 
        /*
         * In case a device driver's probe() fails (e.g.,
@@ -591,7 +589,7 @@ static int vmbus_close_internal(struct vmbus_channel *channel)
                get_order(channel->ringbuffer_pagecount * PAGE_SIZE));
 
 out:
-       tasklet_enable(tasklet);
+       hv_event_tasklet_enable(channel);
 
        return ret;
 }
@@ -659,7 +657,7 @@ int vmbus_sendpacket_ctl(struct vmbus_channel *channel, void *buffer,
        bufferlist[2].iov_len = (packetlen_aligned - packetlen);
 
        ret = hv_ringbuffer_write(&channel->outbound, bufferlist, num_vecs,
-                                 &signal, lock);
+                                 &signal, lock, channel->signal_policy);
 
        /*
         * Signalling the host is conditional on many factors:
@@ -680,11 +678,6 @@ int vmbus_sendpacket_ctl(struct vmbus_channel *channel, void *buffer,
         * mechanism which can hurt the performance otherwise.
         */
 
-       if (channel->signal_policy)
-               signal = true;
-       else
-               kick_q = true;
-
        if (((ret == 0) && kick_q && signal) ||
            (ret && !is_hvsock_channel(channel)))
                vmbus_setevent(channel);
@@ -777,7 +770,7 @@ int vmbus_sendpacket_pagebuffer_ctl(struct vmbus_channel *channel,
        bufferlist[2].iov_len = (packetlen_aligned - packetlen);
 
        ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3,
-                                 &signal, lock);
+                                 &signal, lock, channel->signal_policy);
 
        /*
         * Signalling the host is conditional on many factors:
@@ -795,11 +788,6 @@ int vmbus_sendpacket_pagebuffer_ctl(struct vmbus_channel *channel,
         * enough condition that it should not matter.
         */
 
-       if (channel->signal_policy)
-               signal = true;
-       else
-               kick_q = true;
-
        if (((ret == 0) && kick_q && signal) || (ret))
                vmbus_setevent(channel);
 
@@ -861,7 +849,7 @@ int vmbus_sendpacket_mpb_desc(struct vmbus_channel *channel,
        bufferlist[2].iov_len = (packetlen_aligned - packetlen);
 
        ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3,
-                                 &signal, lock);
+                                 &signal, lock, channel->signal_policy);
 
        if (ret == 0 && signal)
                vmbus_setevent(channel);
@@ -926,7 +914,7 @@ int vmbus_sendpacket_multipagebuffer(struct vmbus_channel *channel,
        bufferlist[2].iov_len = (packetlen_aligned - packetlen);
 
        ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3,
-                                 &signal, lock);
+                                 &signal, lock, channel->signal_policy);
 
        if (ret == 0 && signal)
                vmbus_setevent(channel);