diff options
author | Vitaly Kuznetsov <vkuznets@redhat.com> | 2015-12-14 19:02:01 -0800 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2015-12-14 19:27:30 -0800 |
commit | 940b68e2c3e4ebf032885203c3970e9649f814af (patch) | |
tree | 3f6da80644a206f45d67c2ef8bc892a5c5351d1f /drivers/hv/ring_buffer.c | |
parent | 667d374064b0cc48b6122101b287908d1b392bdb (diff) |
Drivers: hv: ring_buffer: eliminate hv_ringbuffer_peek()
Currently, there is only one user for hv_ringbuffer_read()/
hv_ringbuffer_peak() functions and the usage of these functions is:
- insecure as we drop ring_lock between them, someone else (in theory
only) can acquire it in between;
- non-optimal as we do a number of things (acquire/release the above
mentioned lock, calculate available space on the ring, ...) twice and
this path is performance-critical.
Remove hv_ringbuffer_peek() moving the logic from __vmbus_recvpacket() to
hv_ringbuffer_read().
Signed-off-by: Vitaly Kuznetsov <vkuznets@redhat.com>
Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/hv/ring_buffer.c')
-rw-r--r-- | drivers/hv/ring_buffer.c | 65 |
1 files changed, 37 insertions, 28 deletions
diff --git a/drivers/hv/ring_buffer.c b/drivers/hv/ring_buffer.c index 07f9408fc59e..b53702ce692f 100644 --- a/drivers/hv/ring_buffer.c +++ b/drivers/hv/ring_buffer.c @@ -380,30 +380,59 @@ int hv_ringbuffer_write(struct hv_ring_buffer_info *outring_info, return 0; } -static inline int __hv_ringbuffer_read(struct hv_ring_buffer_info *inring_info, - void *buffer, u32 buflen, u32 offset, - bool *signal, bool advance) +int hv_ringbuffer_read(struct hv_ring_buffer_info *inring_info, + void *buffer, u32 buflen, u32 *buffer_actual_len, + u64 *requestid, bool *signal, bool raw) { u32 bytes_avail_towrite; u32 bytes_avail_toread; u32 next_read_location = 0; u64 prev_indices = 0; unsigned long flags; + struct vmpacket_descriptor desc; + u32 offset; + u32 packetlen; + int ret = 0; if (buflen <= 0) return -EINVAL; spin_lock_irqsave(&inring_info->ring_lock, flags); + *buffer_actual_len = 0; + *requestid = 0; + hv_get_ringbuffer_availbytes(inring_info, &bytes_avail_toread, &bytes_avail_towrite); /* Make sure there is something to read */ - if (bytes_avail_toread < buflen) { - spin_unlock_irqrestore(&inring_info->ring_lock, flags); + if (bytes_avail_toread < sizeof(desc)) { + /* + * No error is set when there is even no header, drivers are + * supposed to analyze buffer_actual_len. + */ + goto out_unlock; + } - return -EAGAIN; + next_read_location = hv_get_next_read_location(inring_info); + next_read_location = hv_copyfrom_ringbuffer(inring_info, &desc, + sizeof(desc), + next_read_location); + + offset = raw ? 0 : (desc.offset8 << 3); + packetlen = (desc.len8 << 3) - offset; + *buffer_actual_len = packetlen; + *requestid = desc.trans_id; + + if (bytes_avail_toread < packetlen + offset) { + ret = -EAGAIN; + goto out_unlock; + } + + if (packetlen > buflen) { + ret = -ENOBUFS; + goto out_unlock; } next_read_location = @@ -411,12 +440,9 @@ static inline int __hv_ringbuffer_read(struct hv_ring_buffer_info *inring_info, next_read_location = hv_copyfrom_ringbuffer(inring_info, buffer, - buflen, + packetlen, next_read_location); - if (!advance) - goto out_unlock; - next_read_location = hv_copyfrom_ringbuffer(inring_info, &prev_indices, sizeof(u64), @@ -436,22 +462,5 @@ static inline int __hv_ringbuffer_read(struct hv_ring_buffer_info *inring_info, out_unlock: spin_unlock_irqrestore(&inring_info->ring_lock, flags); - return 0; -} - -/* Read from ring buffer without advancing the read index. */ -int hv_ringbuffer_peek(struct hv_ring_buffer_info *inring_info, - void *buffer, u32 buflen) -{ - return __hv_ringbuffer_read(inring_info, buffer, buflen, - 0, NULL, false); -} - -/* Read from ring buffer and advance the read index. */ -int hv_ringbuffer_read(struct hv_ring_buffer_info *inring_info, - void *buffer, u32 buflen, u32 offset, - bool *signal) -{ - return __hv_ringbuffer_read(inring_info, buffer, buflen, - offset, signal, true); + return ret; } |