To get the payload of hvsock, we need raw=0 to skip the level-1 header (i.e., struct vmpacket_descriptor desc) and we also need to skip the level-2 header (i.e., struct vmpipe_proto_header pipe_hdr). NB: if the length of the hvsock payload is not aligned with the 8-byte boundeary, at most 7 padding bytes are appended, so the real hvsock payload's length must be retrieved by the pipe_hdr.data_size field. I 'upgrade' the 'raw' parameter of hv_ringbuffer_read() to a 'read_flags', trying to share the logic of the function. This patch is required by the next patch, which will introduce the hvsock send/recv APIs. Signed-off-by: Dexuan Cui <decui@xxxxxxxxxxxxx> Cc: Vitaly Kuznetsov <vkuznets@xxxxxxxxxx> --- drivers/hv/channel.c | 10 +++++---- drivers/hv/hyperv_vmbus.h | 13 +++++++++++- drivers/hv/ring_buffer.c | 54 ++++++++++++++++++++++++++++++++++++----------- include/linux/hyperv.h | 12 +++++++++++ 4 files changed, 72 insertions(+), 17 deletions(-) diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index eaaa066..cc49966 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -940,13 +940,14 @@ EXPORT_SYMBOL_GPL(vmbus_sendpacket_multipagebuffer); static inline int __vmbus_recvpacket(struct vmbus_channel *channel, void *buffer, u32 bufferlen, u32 *buffer_actual_len, u64 *requestid, - bool raw) + u32 read_flags) { int ret; bool signal = false; ret = hv_ringbuffer_read(&channel->inbound, buffer, bufferlen, - buffer_actual_len, requestid, &signal, raw); + buffer_actual_len, requestid, &signal, + read_flags); if (signal) vmbus_setevent(channel); @@ -959,7 +960,7 @@ int vmbus_recvpacket(struct vmbus_channel *channel, void *buffer, u64 *requestid) { return __vmbus_recvpacket(channel, buffer, bufferlen, - buffer_actual_len, requestid, false); + buffer_actual_len, requestid, 0); } EXPORT_SYMBOL(vmbus_recvpacket); @@ -971,6 +972,7 @@ int vmbus_recvpacket_raw(struct vmbus_channel *channel, void *buffer, u64 *requestid) { return __vmbus_recvpacket(channel, buffer, bufferlen, - buffer_actual_len, requestid, true); + buffer_actual_len, requestid, + HV_RINGBUFFER_READ_FLAG_RAW); } EXPORT_SYMBOL_GPL(vmbus_recvpacket_raw); diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index 0411b7b..46206b6 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -619,9 +619,20 @@ int hv_ringbuffer_write(struct hv_ring_buffer_info *ring_info, struct kvec *kv_list, u32 kv_count, bool *signal); +/* + * By default, a read_flags of 0 means: the payload offset is + * sizeof(struct vmpacket_descriptor). + * + * If HV_RINGBUFFER_READ_FLAG_RAW is used, the payload offset is 0. + * + * If HV_RINGBUFFER_READ_FLAG_HVSOCK is used, the payload offset is + * sizeof(struct vmpacket_descriptor) + sizeof(struct vmpipe_proto_header). + */ +#define HV_RINGBUFFER_READ_FLAG_RAW (1 << 0) +#define HV_RINGBUFFER_READ_FLAG_HVSOCK (1 << 1) 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); + u64 *requestid, bool *signal, u32 read_flags); void hv_ringbuffer_get_debuginfo(struct hv_ring_buffer_info *ring_info, struct hv_ring_buffer_debug_info *debug_info); diff --git a/drivers/hv/ring_buffer.c b/drivers/hv/ring_buffer.c index b53702c..03a509c 100644 --- a/drivers/hv/ring_buffer.c +++ b/drivers/hv/ring_buffer.c @@ -382,32 +382,43 @@ int hv_ringbuffer_write(struct hv_ring_buffer_info *outring_info, 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) + u64 *requestid, bool *signal, u32 read_flags) { + bool raw = !!(read_flags & HV_RINGBUFFER_READ_FLAG_RAW); + bool hvsock = !!(read_flags & HV_RINGBUFFER_READ_FLAG_HVSOCK); + u32 bytes_avail_towrite; u32 bytes_avail_toread; u32 next_read_location = 0; u64 prev_indices = 0; unsigned long flags; - struct vmpacket_descriptor desc; + struct vmpipe_proto_header *pipe_hdr; + struct vmpacket_descriptor *desc; u32 offset; - u32 packetlen; + u32 packetlen, tot_hdrlen; int ret = 0; if (buflen <= 0) return -EINVAL; + tot_hdrlen = sizeof(*desc); + if (hvsock) + tot_hdrlen += sizeof(*pipe_hdr); + spin_lock_irqsave(&inring_info->ring_lock, flags); *buffer_actual_len = 0; - *requestid = 0; + + /* If some driver doesn't need the info, a NULL is passed in. */ + if (requestid) + *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 < sizeof(desc)) { + if (bytes_avail_toread < tot_hdrlen) { /* * No error is set when there is even no header, drivers are * supposed to analyze buffer_actual_len. @@ -415,17 +426,26 @@ int hv_ringbuffer_read(struct hv_ring_buffer_info *inring_info, goto out_unlock; } + if (tot_hdrlen > buflen) { + ret = -ENOBUFS; + goto out_unlock; + } + + desc = (struct vmpacket_descriptor *)buffer; + next_read_location = hv_get_next_read_location(inring_info); - next_read_location = hv_copyfrom_ringbuffer(inring_info, &desc, - sizeof(desc), + next_read_location = hv_copyfrom_ringbuffer(inring_info, desc, + tot_hdrlen, next_read_location); + offset = 0; + if (!raw) + offset += (desc->offset8 << 3); + if (hvsock) + offset += sizeof(*pipe_hdr); - offset = raw ? 0 : (desc.offset8 << 3); - packetlen = (desc.len8 << 3) - offset; - *buffer_actual_len = packetlen; - *requestid = desc.trans_id; + packetlen = (desc->len8 << 3) - offset; - if (bytes_avail_toread < packetlen + offset) { + if (bytes_avail_toread < (desc->len8 << 3)) { ret = -EAGAIN; goto out_unlock; } @@ -435,6 +455,16 @@ int hv_ringbuffer_read(struct hv_ring_buffer_info *inring_info, goto out_unlock; } + if (requestid) + *requestid = desc->trans_id; + + if (!hvsock) + *buffer_actual_len = packetlen; + else { + pipe_hdr = (struct vmpipe_proto_header *)(desc + 1); + *buffer_actual_len = pipe_hdr->data_size; + } + next_read_location = hv_get_next_readlocation_withoffset(inring_info, offset); diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index b835d80..e005223 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -1268,4 +1268,16 @@ extern __u32 vmbus_proto_version; int vmbus_send_tl_connect_request(const uuid_le *shv_guest_servie_id, const uuid_le *shv_host_servie_id); +struct vmpipe_proto_header { + u32 pkt_type; + + union { + u32 data_size; + struct { + u16 data_size; + u16 offset; + } partial; + }; +} __packed; + #endif /* _HYPERV_H */ -- 2.1.4 _______________________________________________ devel mailing list devel@xxxxxxxxxxxxxxxxxxxxxx http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel