]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blobdiff - drivers/hv/ring_buffer.c
Merge tag 'armsoc-tee' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
[mirror_ubuntu-artful-kernel.git] / drivers / hv / ring_buffer.c
index 87799e81af97697cb4879acb227a30ea0bf792bd..1f450c39a9b0830f68611c0a11ee90c9f77ab2a3 100644 (file)
@@ -32,6 +32,8 @@
 
 #include "hyperv_vmbus.h"
 
+#define VMBUS_PKT_TRAILER      8
+
 /*
  * When we write to the ring buffer, check if the host needs to
  * be signaled. Here is the details of this protocol:
@@ -73,8 +75,6 @@ static void hv_signal_on_write(u32 old_write, struct vmbus_channel *channel)
         */
        if (old_write == READ_ONCE(rbi->ring_buffer->read_index))
                vmbus_setevent(channel);
-
-       return;
 }
 
 /* Get the next write location for the specified ring buffer. */
@@ -208,6 +208,7 @@ void hv_ringbuffer_get_debuginfo(const struct hv_ring_buffer_info *ring_info,
                        ring_info->ring_buffer->interrupt_mask;
        }
 }
+EXPORT_SYMBOL_GPL(hv_ringbuffer_get_debuginfo);
 
 /* Initialize the ring buffer. */
 int hv_ringbuffer_init(struct hv_ring_buffer_info *ring_info,
@@ -267,14 +268,13 @@ void hv_ringbuffer_cleanup(struct hv_ring_buffer_info *ring_info)
 int hv_ringbuffer_write(struct vmbus_channel *channel,
                        const struct kvec *kv_list, u32 kv_count)
 {
-       int i = 0;
+       int i;
        u32 bytes_avail_towrite;
-       u32 totalbytes_towrite = 0;
-
+       u32 totalbytes_towrite = sizeof(u64);
        u32 next_write_location;
        u32 old_write;
-       u64 prev_indices = 0;
-       unsigned long flags = 0;
+       u64 prev_indices;
+       unsigned long flags;
        struct hv_ring_buffer_info *outring_info = &channel->outbound;
 
        if (channel->rescind)
@@ -283,8 +283,6 @@ int hv_ringbuffer_write(struct vmbus_channel *channel,
        for (i = 0; i < kv_count; i++)
                totalbytes_towrite += kv_list[i].iov_len;
 
-       totalbytes_towrite += sizeof(u64);
-
        spin_lock_irqsave(&outring_info->ring_lock, flags);
 
        bytes_avail_towrite = hv_get_bytes_to_write(outring_info);
@@ -336,23 +334,27 @@ int hv_ringbuffer_write(struct vmbus_channel *channel,
        return 0;
 }
 
+static inline void
+init_cached_read_index(struct hv_ring_buffer_info *rbi)
+{
+       rbi->cached_read_index = rbi->ring_buffer->read_index;
+}
+
 int hv_ringbuffer_read(struct vmbus_channel *channel,
                       void *buffer, u32 buflen, u32 *buffer_actual_len,
                       u64 *requestid, bool raw)
 {
        u32 bytes_avail_toread;
-       u32 next_read_location = 0;
+       u32 next_read_location;
        u64 prev_indices = 0;
        struct vmpacket_descriptor desc;
        u32 offset;
        u32 packetlen;
-       int ret = 0;
        struct hv_ring_buffer_info *inring_info = &channel->inbound;
 
        if (buflen <= 0)
                return -EINVAL;
 
-
        *buffer_actual_len = 0;
        *requestid = 0;
 
@@ -363,10 +365,11 @@ int hv_ringbuffer_read(struct vmbus_channel *channel,
                 * No error is set when there is even no header, drivers are
                 * supposed to analyze buffer_actual_len.
                 */
-               return ret;
+               return 0;
        }
 
-       init_cached_read_index(channel);
+       init_cached_read_index(inring_info);
+
        next_read_location = hv_get_next_read_location(inring_info);
        next_read_location = hv_copyfrom_ringbuffer(inring_info, &desc,
                                                    sizeof(desc),
@@ -408,5 +411,88 @@ int hv_ringbuffer_read(struct vmbus_channel *channel,
 
        hv_signal_on_read(channel);
 
-       return ret;
+       return 0;
+}
+
+/*
+ * Determine number of bytes available in ring buffer after
+ * the current iterator (priv_read_index) location.
+ *
+ * This is similar to hv_get_bytes_to_read but with private
+ * read index instead.
+ */
+static u32 hv_pkt_iter_avail(const struct hv_ring_buffer_info *rbi)
+{
+       u32 priv_read_loc = rbi->priv_read_index;
+       u32 write_loc = READ_ONCE(rbi->ring_buffer->write_index);
+
+       if (write_loc >= priv_read_loc)
+               return write_loc - priv_read_loc;
+       else
+               return (rbi->ring_datasize - priv_read_loc) + write_loc;
+}
+
+/*
+ * Get first vmbus packet from ring buffer after read_index
+ *
+ * If ring buffer is empty, returns NULL and no other action needed.
+ */
+struct vmpacket_descriptor *hv_pkt_iter_first(struct vmbus_channel *channel)
+{
+       struct hv_ring_buffer_info *rbi = &channel->inbound;
+
+       /* set state for later hv_signal_on_read() */
+       init_cached_read_index(rbi);
+
+       if (hv_pkt_iter_avail(rbi) < sizeof(struct vmpacket_descriptor))
+               return NULL;
+
+       return hv_get_ring_buffer(rbi) + rbi->priv_read_index;
+}
+EXPORT_SYMBOL_GPL(hv_pkt_iter_first);
+
+/*
+ * Get next vmbus packet from ring buffer.
+ *
+ * Advances the current location (priv_read_index) and checks for more
+ * data. If the end of the ring buffer is reached, then return NULL.
+ */
+struct vmpacket_descriptor *
+__hv_pkt_iter_next(struct vmbus_channel *channel,
+                  const struct vmpacket_descriptor *desc)
+{
+       struct hv_ring_buffer_info *rbi = &channel->inbound;
+       u32 packetlen = desc->len8 << 3;
+       u32 dsize = rbi->ring_datasize;
+
+       /* bump offset to next potential packet */
+       rbi->priv_read_index += packetlen + VMBUS_PKT_TRAILER;
+       if (rbi->priv_read_index >= dsize)
+               rbi->priv_read_index -= dsize;
+
+       /* more data? */
+       if (hv_pkt_iter_avail(rbi) < sizeof(struct vmpacket_descriptor))
+               return NULL;
+       else
+               return hv_get_ring_buffer(rbi) + rbi->priv_read_index;
+}
+EXPORT_SYMBOL_GPL(__hv_pkt_iter_next);
+
+/*
+ * Update host ring buffer after iterating over packets.
+ */
+void hv_pkt_iter_close(struct vmbus_channel *channel)
+{
+       struct hv_ring_buffer_info *rbi = &channel->inbound;
+
+       /*
+        * Make sure all reads are done before we update the read index since
+        * the writer may start writing to the read area once the read index
+        * is updated.
+        */
+       virt_rmb();
+       rbi->ring_buffer->read_index = rbi->priv_read_index;
+
+       hv_signal_on_read(channel);
 }
+EXPORT_SYMBOL_GPL(hv_pkt_iter_close);