@@ -181,7 +181,7 @@ void hv_ringbuffer_pre_init(struct vmbus_channel *channel)
181181
182182/* Initialize the ring buffer. */
183183int hv_ringbuffer_init (struct hv_ring_buffer_info * ring_info ,
184- struct page * pages , u32 page_cnt )
184+ struct page * pages , u32 page_cnt , u32 max_pkt_size )
185185{
186186 int i ;
187187 struct page * * pages_wraparound ;
@@ -223,6 +223,14 @@ int hv_ringbuffer_init(struct hv_ring_buffer_info *ring_info,
223223 sizeof (struct hv_ring_buffer );
224224 ring_info -> priv_read_index = 0 ;
225225
226+ /* Initialize buffer that holds copies of incoming packets */
227+ if (max_pkt_size ) {
228+ ring_info -> pkt_buffer = kzalloc (max_pkt_size , GFP_KERNEL );
229+ if (!ring_info -> pkt_buffer )
230+ return - ENOMEM ;
231+ ring_info -> pkt_buffer_size = max_pkt_size ;
232+ }
233+
226234 spin_lock_init (& ring_info -> ring_lock );
227235
228236 return 0 ;
@@ -235,6 +243,9 @@ void hv_ringbuffer_cleanup(struct hv_ring_buffer_info *ring_info)
235243 vunmap (ring_info -> ring_buffer );
236244 ring_info -> ring_buffer = NULL ;
237245 mutex_unlock (& ring_info -> ring_buffer_mutex );
246+
247+ kfree (ring_info -> pkt_buffer );
248+ ring_info -> pkt_buffer_size = 0 ;
238249}
239250
240251/* Write to the ring buffer. */
@@ -375,7 +386,7 @@ int hv_ringbuffer_read(struct vmbus_channel *channel,
375386 memcpy (buffer , (const char * )desc + offset , packetlen );
376387
377388 /* Advance ring index to next packet descriptor */
378- __hv_pkt_iter_next (channel , desc );
389+ __hv_pkt_iter_next (channel , desc , true );
379390
380391 /* Notify host of update */
381392 hv_pkt_iter_close (channel );
@@ -401,6 +412,22 @@ static u32 hv_pkt_iter_avail(const struct hv_ring_buffer_info *rbi)
401412 return (rbi -> ring_datasize - priv_read_loc ) + write_loc ;
402413}
403414
415+ /*
416+ * Get first vmbus packet without copying it out of the ring buffer
417+ */
418+ struct vmpacket_descriptor * hv_pkt_iter_first_raw (struct vmbus_channel * channel )
419+ {
420+ struct hv_ring_buffer_info * rbi = & channel -> inbound ;
421+
422+ hv_debug_delay_test (channel , MESSAGE_DELAY );
423+
424+ if (hv_pkt_iter_avail (rbi ) < sizeof (struct vmpacket_descriptor ))
425+ return NULL ;
426+
427+ return (struct vmpacket_descriptor * )(hv_get_ring_buffer (rbi ) + rbi -> priv_read_index );
428+ }
429+ EXPORT_SYMBOL_GPL (hv_pkt_iter_first_raw );
430+
404431/*
405432 * Get first vmbus packet from ring buffer after read_index
406433 *
@@ -409,17 +436,49 @@ static u32 hv_pkt_iter_avail(const struct hv_ring_buffer_info *rbi)
409436struct vmpacket_descriptor * hv_pkt_iter_first (struct vmbus_channel * channel )
410437{
411438 struct hv_ring_buffer_info * rbi = & channel -> inbound ;
412- struct vmpacket_descriptor * desc ;
439+ struct vmpacket_descriptor * desc , * desc_copy ;
440+ u32 bytes_avail , pkt_len , pkt_offset ;
413441
414- hv_debug_delay_test (channel , MESSAGE_DELAY );
415- if (hv_pkt_iter_avail ( rbi ) < sizeof ( struct vmpacket_descriptor ) )
442+ desc = hv_pkt_iter_first_raw (channel );
443+ if (! desc )
416444 return NULL ;
417445
418- desc = hv_get_ring_buffer (rbi ) + rbi -> priv_read_index ;
419- if (desc )
420- prefetch ((char * )desc + (desc -> len8 << 3 ));
446+ bytes_avail = min (rbi -> pkt_buffer_size , hv_pkt_iter_avail (rbi ));
447+
448+ /*
449+ * Ensure the compiler does not use references to incoming Hyper-V values (which
450+ * could change at any moment) when reading local variables later in the code
451+ */
452+ pkt_len = READ_ONCE (desc -> len8 ) << 3 ;
453+ pkt_offset = READ_ONCE (desc -> offset8 ) << 3 ;
454+
455+ /*
456+ * If pkt_len is invalid, set it to the smaller of hv_pkt_iter_avail() and
457+ * rbi->pkt_buffer_size
458+ */
459+ if (pkt_len < sizeof (struct vmpacket_descriptor ) || pkt_len > bytes_avail )
460+ pkt_len = bytes_avail ;
461+
462+ /*
463+ * If pkt_offset is invalid, arbitrarily set it to
464+ * the size of vmpacket_descriptor
465+ */
466+ if (pkt_offset < sizeof (struct vmpacket_descriptor ) || pkt_offset > pkt_len )
467+ pkt_offset = sizeof (struct vmpacket_descriptor );
468+
469+ /* Copy the Hyper-V packet out of the ring buffer */
470+ desc_copy = (struct vmpacket_descriptor * )rbi -> pkt_buffer ;
471+ memcpy (desc_copy , desc , pkt_len );
472+
473+ /*
474+ * Hyper-V could still change len8 and offset8 after the earlier read.
475+ * Ensure that desc_copy has legal values for len8 and offset8 that
476+ * are consistent with the copy we just made
477+ */
478+ desc_copy -> len8 = pkt_len >> 3 ;
479+ desc_copy -> offset8 = pkt_offset >> 3 ;
421480
422- return desc ;
481+ return desc_copy ;
423482}
424483EXPORT_SYMBOL_GPL (hv_pkt_iter_first );
425484
@@ -431,7 +490,8 @@ EXPORT_SYMBOL_GPL(hv_pkt_iter_first);
431490 */
432491struct vmpacket_descriptor *
433492__hv_pkt_iter_next (struct vmbus_channel * channel ,
434- const struct vmpacket_descriptor * desc )
493+ const struct vmpacket_descriptor * desc ,
494+ bool copy )
435495{
436496 struct hv_ring_buffer_info * rbi = & channel -> inbound ;
437497 u32 packetlen = desc -> len8 << 3 ;
@@ -444,7 +504,7 @@ __hv_pkt_iter_next(struct vmbus_channel *channel,
444504 rbi -> priv_read_index -= dsize ;
445505
446506 /* more data? */
447- return hv_pkt_iter_first (channel );
507+ return copy ? hv_pkt_iter_first ( channel ) : hv_pkt_iter_first_raw (channel );
448508}
449509EXPORT_SYMBOL_GPL (__hv_pkt_iter_next );
450510
0 commit comments