Drivers: hv: kvp: switch to using the hvutil_device_state state machine
[linux-2.6-block.git] / drivers / hv / channel.c
index 2978f5ee8d2a0abdeacdc8f7fc2121d35c0260a2..54da66dc7d16c033db21664606a0fe7efc576535 100644 (file)
@@ -71,7 +71,8 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
        struct vmbus_channel_msginfo *open_info = NULL;
        void *in, *out;
        unsigned long flags;
-       int ret, t, err = 0;
+       int ret, err = 0;
+       unsigned long t;
 
        spin_lock_irqsave(&newchannel->lock, flags);
        if (newchannel->state == CHANNEL_OPEN_STATE) {
@@ -89,9 +90,10 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
        out = (void *)__get_free_pages(GFP_KERNEL|__GFP_ZERO,
                get_order(send_ringbuffer_size + recv_ringbuffer_size));
 
-       if (!out)
-               return -ENOMEM;
-
+       if (!out) {
+               err = -ENOMEM;
+               goto error0;
+       }
 
        in = (void *)((unsigned long)out + send_ringbuffer_size);
 
@@ -135,7 +137,7 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
                           GFP_KERNEL);
        if (!open_info) {
                err = -ENOMEM;
-               goto error0;
+               goto error_gpadl;
        }
 
        init_completion(&open_info->waitevent);
@@ -151,7 +153,7 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
 
        if (userdatalen > MAX_USER_DEFINED_BYTES) {
                err = -EINVAL;
-               goto error0;
+               goto error_gpadl;
        }
 
        if (userdatalen)
@@ -195,10 +197,14 @@ error1:
        list_del(&open_info->msglistentry);
        spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
 
+error_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);
+       newchannel->state = CHANNEL_OPEN_STATE;
        return err;
 }
 EXPORT_SYMBOL_GPL(vmbus_open);
@@ -534,6 +540,12 @@ static int vmbus_close_internal(struct vmbus_channel *channel)
        free_pages((unsigned long)channel->ringbuffer_pages,
                get_order(channel->ringbuffer_pagecount * PAGE_SIZE));
 
+       /*
+        * If the channel has been rescinded; process device removal.
+        */
+       if (channel->rescind)
+               hv_process_channel_removal(channel,
+                                          channel->offermsg.child_relid);
        return ret;
 }
 
@@ -569,23 +581,9 @@ void vmbus_close(struct vmbus_channel *channel)
 }
 EXPORT_SYMBOL_GPL(vmbus_close);
 
-/**
- * vmbus_sendpacket() - Send the specified buffer on the given channel
- * @channel: Pointer to vmbus_channel structure.
- * @buffer: Pointer to the buffer you want to receive the data into.
- * @bufferlen: Maximum size of what the the buffer will hold
- * @requestid: Identifier of the request
- * @type: Type of packet that is being send e.g. negotiate, time
- * packet etc.
- *
- * Sends data in @buffer directly to hyper-v via the vmbus
- * This will send the data unparsed to hyper-v.
- *
- * Mainly used by Hyper-V drivers.
- */
-int vmbus_sendpacket(struct vmbus_channel *channel, void *buffer,
+int vmbus_sendpacket_ctl(struct vmbus_channel *channel, void *buffer,
                           u32 bufferlen, u64 requestid,
-                          enum vmbus_packet_type type, u32 flags)
+                          enum vmbus_packet_type type, u32 flags, bool kick_q)
 {
        struct vmpacket_descriptor desc;
        u32 packetlen = sizeof(struct vmpacket_descriptor) + bufferlen;
@@ -613,21 +611,61 @@ int vmbus_sendpacket(struct vmbus_channel *channel, void *buffer,
 
        ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3, &signal);
 
-       if (ret == 0 && signal)
+       /*
+        * Signalling the host is conditional on many factors:
+        * 1. The ring state changed from being empty to non-empty.
+        *    This is tracked by the variable "signal".
+        * 2. The variable kick_q tracks if more data will be placed
+        *    on the ring. We will not signal if more data is
+        *    to be placed.
+        *
+        * If we cannot write to the ring-buffer; signal the host
+        * even if we may not have written anything. This is a rare
+        * enough condition that it should not matter.
+        */
+       if (((ret == 0) && kick_q && signal) || (ret))
                vmbus_setevent(channel);
 
        return ret;
 }
+EXPORT_SYMBOL(vmbus_sendpacket_ctl);
+
+/**
+ * vmbus_sendpacket() - Send the specified buffer on the given channel
+ * @channel: Pointer to vmbus_channel structure.
+ * @buffer: Pointer to the buffer you want to receive the data into.
+ * @bufferlen: Maximum size of what the the buffer will hold
+ * @requestid: Identifier of the request
+ * @type: Type of packet that is being send e.g. negotiate, time
+ * packet etc.
+ *
+ * Sends data in @buffer directly to hyper-v via the vmbus
+ * This will send the data unparsed to hyper-v.
+ *
+ * Mainly used by Hyper-V drivers.
+ */
+int vmbus_sendpacket(struct vmbus_channel *channel, void *buffer,
+                          u32 bufferlen, u64 requestid,
+                          enum vmbus_packet_type type, u32 flags)
+{
+       return vmbus_sendpacket_ctl(channel, buffer, bufferlen, requestid,
+                                   type, flags, true);
+}
 EXPORT_SYMBOL(vmbus_sendpacket);
 
 /*
- * vmbus_sendpacket_pagebuffer - Send a range of single-page buffer
- * packets using a GPADL Direct packet type.
+ * vmbus_sendpacket_pagebuffer_ctl - Send a range of single-page buffer
+ * packets using a GPADL Direct packet type. This interface allows you
+ * to control notifying the host. This will be useful for sending
+ * batched data. Also the sender can control the send flags
+ * explicitly.
  */
-int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel,
+int vmbus_sendpacket_pagebuffer_ctl(struct vmbus_channel *channel,
                                     struct hv_page_buffer pagebuffers[],
                                     u32 pagecount, void *buffer, u32 bufferlen,
-                                    u64 requestid)
+                                    u64 requestid,
+                                    u32 flags,
+                                    bool kick_q)
 {
        int ret;
        int i;
@@ -655,7 +693,7 @@ int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel,
 
        /* Setup the descriptor */
        desc.type = VM_PKT_DATA_USING_GPA_DIRECT;
-       desc.flags = VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED;
+       desc.flags = flags;
        desc.dataoffset8 = descsize >> 3; /* in 8-bytes grandularity */
        desc.length8 = (u16)(packetlen_aligned >> 3);
        desc.transactionid = requestid;
@@ -676,11 +714,40 @@ int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel,
 
        ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3, &signal);
 
-       if (ret == 0 && signal)
+       /*
+        * Signalling the host is conditional on many factors:
+        * 1. The ring state changed from being empty to non-empty.
+        *    This is tracked by the variable "signal".
+        * 2. The variable kick_q tracks if more data will be placed
+        *    on the ring. We will not signal if more data is
+        *    to be placed.
+        *
+        * If we cannot write to the ring-buffer; signal the host
+        * even if we may not have written anything. This is a rare
+        * enough condition that it should not matter.
+        */
+       if (((ret == 0) && kick_q && signal) || (ret))
                vmbus_setevent(channel);
 
        return ret;
 }
+EXPORT_SYMBOL_GPL(vmbus_sendpacket_pagebuffer_ctl);
+
+/*
+ * vmbus_sendpacket_pagebuffer - Send a range of single-page buffer
+ * packets using a GPADL Direct packet type.
+ */
+int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel,
+                                    struct hv_page_buffer pagebuffers[],
+                                    u32 pagecount, void *buffer, u32 bufferlen,
+                                    u64 requestid)
+{
+       u32 flags = VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED;
+       return vmbus_sendpacket_pagebuffer_ctl(channel, pagebuffers, pagecount,
+                                              buffer, bufferlen, requestid,
+                                              flags, true);
+
+}
 EXPORT_SYMBOL_GPL(vmbus_sendpacket_pagebuffer);
 
 /*