Skip to content

Commit

Permalink
Drivers: hv: ring_buffer: eliminate hv_ringbuffer_peek()
Browse files Browse the repository at this point in the history
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 <[email protected]>
Signed-off-by: K. Y. Srinivasan <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
  • Loading branch information
vittyvk authored and gregkh committed Dec 15, 2015
1 parent 667d374 commit 940b68e
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 64 deletions.
30 changes: 2 additions & 28 deletions drivers/hv/channel.c
Original file line number Diff line number Diff line change
Expand Up @@ -927,37 +927,11 @@ __vmbus_recvpacket(struct vmbus_channel *channel, void *buffer,
u32 bufferlen, u32 *buffer_actual_len, u64 *requestid,
bool raw)
{
struct vmpacket_descriptor desc;
u32 packetlen;
u32 userlen;
int ret;
bool signal = false;

*buffer_actual_len = 0;
*requestid = 0;


ret = hv_ringbuffer_peek(&channel->inbound, &desc,
sizeof(struct vmpacket_descriptor));
if (ret != 0)
return 0;

packetlen = desc.len8 << 3;
if (!raw)
userlen = packetlen - (desc.offset8 << 3);
else
userlen = packetlen;

*buffer_actual_len = userlen;

if (userlen > bufferlen)
return -ENOBUFS;

*requestid = desc.trans_id;

/* Copy over the packet to the user buffer */
ret = hv_ringbuffer_read(&channel->inbound, buffer, userlen,
raw ? 0 : desc.offset8 << 3, &signal);
ret = hv_ringbuffer_read(&channel->inbound, buffer, bufferlen,
buffer_actual_len, requestid, &signal, raw);

if (signal)
vmbus_setevent(channel);
Expand Down
11 changes: 3 additions & 8 deletions drivers/hv/hyperv_vmbus.h
Original file line number Diff line number Diff line change
Expand Up @@ -619,14 +619,9 @@ int hv_ringbuffer_write(struct hv_ring_buffer_info *ring_info,
struct kvec *kv_list,
u32 kv_count, bool *signal);

int hv_ringbuffer_peek(struct hv_ring_buffer_info *ring_info, void *buffer,
u32 buflen);

int hv_ringbuffer_read(struct hv_ring_buffer_info *ring_info,
void *buffer,
u32 buflen,
u32 offset, bool *signal);

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);

void hv_ringbuffer_get_debuginfo(struct hv_ring_buffer_info *ring_info,
struct hv_ring_buffer_debug_info *debug_info);
Expand Down
65 changes: 37 additions & 28 deletions drivers/hv/ring_buffer.c
Original file line number Diff line number Diff line change
Expand Up @@ -380,43 +380,69 @@ 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 =
hv_get_next_readlocation_withoffset(inring_info, offset);

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),
Expand All @@ -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;
}

0 comments on commit 940b68e

Please sign in to comment.