Sfoglia il codice sorgente

[hyperv] Assume that VMBus xfer page ranges correspond to RNDIS messages

The (undocumented) VMBus protocol seems to allow for transfer
page-based packets where the data payload is split into an arbitrary
set of ranges within the transfer page set.

The RNDIS protocol includes a length field within the header of each
message, and it is known from observation that multiple RNDIS messages
can be concatenated into a single VMBus message.

iPXE currently assumes that the transfer page range boundaries are
entirely arbitrary, and uses the RNDIS header length to determine the
RNDIS message boundaries.

Windows Server 2012 R2 generates an RNDIS_INDICATE_STATUS_MSG for an
undocumented and unknown status code (0x40020006) with a malformed
RNDIS header length: the length does not cover the StatusBuffer
portion of the message.  This causes iPXE to report a malformed RNDIS
message and to discard any further RNDIS messages within the same
VMBus message.

The Linux Hyper-V driver assumes that the transfer page range
boundaries correspond to RNDIS message boundaries, and so does not
notice the malformed length field in the RNDIS header.

Match the behaviour of the Linux Hyper-V driver: assume that the
transfer page range boundaries correspond to the RNDIS message
boundaries and ignore the RNDIS header length.  This avoids triggering
the "malformed packet" error and also avoids unnecessary data copying:
since we now have one I/O buffer per RNDIS message, there is no longer
any need to use iob_split().

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 9 anni fa
parent
commit
639632b059
4 ha cambiato i file con 72 aggiunte e 94 eliminazioni
  1. 14
    6
      src/drivers/net/netvsc.c
  2. 2
    7
      src/include/ipxe/vmbus.h
  3. 43
    31
      src/interface/hyperv/vmbus.c
  4. 13
    50
      src/net/rndis.c

+ 14
- 6
src/drivers/net/netvsc.c Vedi File

@@ -298,15 +298,17 @@ static int netvsc_recv_control ( struct vmbus_device *vmdev, uint64_t xid,
298 298
  * @v xid		Transaction ID
299 299
  * @v data		Data
300 300
  * @v len		Length of data
301
- * @v iobuf		I/O buffer, or NULL if allocation failed
301
+ * @v list		List of I/O buffers
302 302
  * @ret rc		Return status code
303 303
  */
304 304
 static int netvsc_recv_data ( struct vmbus_device *vmdev, uint64_t xid,
305 305
 			      const void *data, size_t len,
306
-			      struct io_buffer *iobuf ) {
306
+			      struct list_head *list ) {
307 307
 	struct rndis_device *rndis = vmbus_get_drvdata ( vmdev );
308 308
 	struct netvsc_device *netvsc = rndis->priv;
309 309
 	const struct netvsc_rndis_message *msg = data;
310
+	struct io_buffer *iobuf;
311
+	struct io_buffer *tmp;
310 312
 	int rc;
311 313
 
312 314
 	/* Sanity check */
@@ -324,21 +326,27 @@ static int netvsc_recv_data ( struct vmbus_device *vmdev, uint64_t xid,
324 326
 		goto err_sanity;
325 327
 	}
326 328
 
327
-	/* Send completion back to host (even if I/O buffer was missing) */
329
+	/* Send completion back to host */
328 330
 	if ( ( rc = vmbus_send_completion ( vmdev, xid, NULL, 0 ) ) != 0 ) {
329 331
 		DBGC ( netvsc, "NETVSC %s could not send completion: %s\n",
330 332
 		       netvsc->name, strerror ( rc ) );
331 333
 		goto err_completion;
332 334
 	}
333 335
 
334
-	/* Hand off to RNDIS (even if I/O buffer was missing) */
335
-	rndis_rx ( rndis, iob_disown ( iobuf ) );
336
+	/* Hand off to RNDIS */
337
+	list_for_each_entry_safe ( iobuf, tmp, list, list ) {
338
+		list_del ( &iobuf->list );
339
+		rndis_rx ( rndis, iob_disown ( iobuf ) );
340
+	}
336 341
 
337 342
 	return 0;
338 343
 
339 344
  err_completion:
340 345
  err_sanity:
341
-	free_iob ( iobuf );
346
+	list_for_each_entry_safe ( iobuf, tmp, list, list ) {
347
+		list_del ( &iobuf->list );
348
+		free_iob ( iobuf );
349
+	}
342 350
 	return rc;
343 351
 }
344 352
 

+ 2
- 7
src/include/ipxe/vmbus.h Vedi File

@@ -403,21 +403,16 @@ struct vmbus_channel_operations {
403 403
 	 * @v xid		Transaction ID
404 404
 	 * @v data		Data
405 405
 	 * @v len		Length of data
406
-	 * @v iobuf		I/O buffer, or NULL if allocation failed
406
+	 * @v list		List of I/O buffers
407 407
 	 * @ret rc		Return status code
408 408
 	 *
409 409
 	 * This function takes ownership of the I/O buffer.  It should
410 410
 	 * eventually call vmbus_send_completion() to indicate to the
411 411
 	 * host that the buffer can be reused.
412
-	 *
413
-	 * Note that this function will be called even if we failed to
414
-	 * allocate or populate the I/O buffer; this is to allow for a
415
-	 * completion to be sent even in the event of a transient
416
-	 * memory shortage failure.
417 412
 	 */
418 413
 	int ( * recv_data ) ( struct vmbus_device *vmdev, uint64_t xid,
419 414
 			      const void *data, size_t len,
420
-			      struct io_buffer *iobuf );
415
+			      struct list_head *list );
421 416
 	/**
422 417
 	 * Handle received completion packet
423 418
 	 *

+ 43
- 31
src/interface/hyperv/vmbus.c Vedi File

@@ -807,20 +807,21 @@ static struct vmbus_xfer_pages * vmbus_xfer_pages ( struct vmbus_device *vmdev,
807 807
 }
808 808
 
809 809
 /**
810
- * Construct I/O buffer from transfer pages
810
+ * Construct I/O buffer list from transfer pages
811 811
  *
812 812
  * @v vmdev		VMBus device
813 813
  * @v header		Transfer page header
814
- * @ret iobuf		I/O buffer, or NULL on error
814
+ * @v list		I/O buffer list to populate
815
+ * @ret rc		Return status code
815 816
  */
816
-static struct io_buffer *
817
-vmbus_xfer_page_iobuf ( struct vmbus_device *vmdev,
818
-			struct vmbus_packet_header *header ) {
817
+static int vmbus_xfer_page_iobufs ( struct vmbus_device *vmdev,
818
+				    struct vmbus_packet_header *header,
819
+				    struct list_head *list ) {
819 820
 	struct vmbus_xfer_page_header *page_header =
820 821
 		container_of ( header, struct vmbus_xfer_page_header, header );
821 822
 	struct vmbus_xfer_pages *pages;
822 823
 	struct io_buffer *iobuf;
823
-	size_t total_len;
824
+	struct io_buffer *tmp;
824 825
 	size_t len;
825 826
 	size_t offset;
826 827
 	unsigned int range_count;
@@ -832,28 +833,32 @@ vmbus_xfer_page_iobuf ( struct vmbus_device *vmdev,
832 833
 
833 834
 	/* Locate page set */
834 835
 	pages = vmbus_xfer_pages ( vmdev, page_header->pageset );
835
-	if ( ! pages )
836
+	if ( ! pages ) {
837
+		rc = -ENOENT;
836 838
 		goto err_pages;
837
-
838
-	/* Determine total length */
839
-	range_count = le32_to_cpu ( page_header->range_count );
840
-	for ( total_len = 0, i = 0 ; i < range_count ; i++ ) {
841
-		len = le32_to_cpu ( page_header->range[i].len );
842
-		total_len += len;
843 839
 	}
844 840
 
845
-	/* Allocate I/O buffer */
846
-	iobuf = alloc_iob ( total_len );
847
-	if ( ! iobuf ) {
848
-		DBGC ( vmdev, "VMBUS %s could not allocate %zd-byte I/O "
849
-		       "buffer\n", vmdev->dev.name, total_len );
850
-		goto err_alloc;
851
-	}
852
-
853
-	/* Populate I/O buffer */
841
+	/* Allocate and populate I/O buffers */
842
+	range_count = le32_to_cpu ( page_header->range_count );
854 843
 	for ( i = 0 ; i < range_count ; i++ ) {
844
+
845
+		/* Parse header */
855 846
 		len = le32_to_cpu ( page_header->range[i].len );
856 847
 		offset = le32_to_cpu ( page_header->range[i].offset );
848
+
849
+		/* Allocate I/O buffer */
850
+		iobuf = alloc_iob ( len );
851
+		if ( ! iobuf ) {
852
+			DBGC ( vmdev, "VMBUS %s could not allocate %zd-byte "
853
+			       "I/O buffer\n", vmdev->dev.name, len );
854
+			rc = -ENOMEM;
855
+			goto err_alloc;
856
+		}
857
+
858
+		/* Add I/O buffer to list */
859
+		list_add ( &iobuf->list, list );
860
+
861
+		/* Populate I/O buffer */
857 862
 		if ( ( rc = pages->op->copy ( pages, iob_put ( iobuf, len ),
858 863
 					      offset, len ) ) != 0 ) {
859 864
 			DBGC ( vmdev, "VMBUS %s could not populate I/O buffer "
@@ -863,13 +868,16 @@ vmbus_xfer_page_iobuf ( struct vmbus_device *vmdev,
863 868
 		}
864 869
 	}
865 870
 
866
-	return iobuf;
871
+	return 0;
867 872
 
868 873
  err_copy:
869
-	free_iob ( iobuf );
870 874
  err_alloc:
875
+	list_for_each_entry_safe ( iobuf, tmp, list, list ) {
876
+		list_del ( &iobuf->list );
877
+		free_iob ( iobuf );
878
+	}
871 879
  err_pages:
872
-	return NULL;
880
+	return rc;
873 881
 }
874 882
 
875 883
 /**
@@ -880,7 +888,7 @@ vmbus_xfer_page_iobuf ( struct vmbus_device *vmdev,
880 888
  */
881 889
 int vmbus_poll ( struct vmbus_device *vmdev ) {
882 890
 	struct vmbus_packet_header *header = vmdev->packet;
883
-	struct io_buffer *iobuf;
891
+	struct list_head list;
884 892
 	void *data;
885 893
 	size_t header_len;
886 894
 	size_t len;
@@ -929,6 +937,14 @@ int vmbus_poll ( struct vmbus_device *vmdev ) {
929 937
 	DBGC2_HDA ( vmdev, old_cons, header, ring_len );
930 938
 	assert ( ( ( cons - old_cons ) & ( vmdev->in_len - 1 ) ) == ring_len );
931 939
 
940
+	/* Allocate I/O buffers, if applicable */
941
+	INIT_LIST_HEAD ( &list );
942
+	if ( header->type == cpu_to_le16 ( VMBUS_DATA_XFER_PAGES ) ) {
943
+		if ( ( rc = vmbus_xfer_page_iobufs ( vmdev, header,
944
+						     &list ) ) != 0 )
945
+			return rc;
946
+	}
947
+
932 948
 	/* Update producer index */
933 949
 	rmb();
934 950
 	vmdev->in->cons = cpu_to_le32 ( cons );
@@ -948,12 +964,8 @@ int vmbus_poll ( struct vmbus_device *vmdev ) {
948 964
 		break;
949 965
 
950 966
 	case cpu_to_le16 ( VMBUS_DATA_XFER_PAGES ) :
951
-		iobuf = vmbus_xfer_page_iobuf ( vmdev, header );
952
-		/* Call recv_data() even if I/O buffer allocation
953
-		 * failed, to allow for completions to be sent.
954
-		 */
955 967
 		if ( ( rc = vmdev->op->recv_data ( vmdev, xid, data, len,
956
-						   iob_disown ( iobuf ) ) )!=0){
968
+						   &list ) ) != 0 ) {
957 969
 			DBGC ( vmdev, "VMBUS %s could not handle data packet: "
958 970
 			       "%s\n", vmdev->dev.name, strerror ( rc ) );
959 971
 			return rc;

+ 13
- 50
src/net/rndis.c Vedi File

@@ -759,67 +759,30 @@ static void rndis_rx_message ( struct rndis_device *rndis,
759 759
  * Receive packet from underlying transport layer
760 760
  *
761 761
  * @v rndis		RNDIS device
762
- * @v iobuf		I/O buffer, or NULL if allocation failed
762
+ * @v iobuf		I/O buffer
763 763
  */
764 764
 void rndis_rx ( struct rndis_device *rndis, struct io_buffer *iobuf ) {
765 765
 	struct net_device *netdev = rndis->netdev;
766 766
 	struct rndis_header *header;
767
-	struct io_buffer *msg;
768
-	size_t len;
769 767
 	unsigned int type;
770 768
 	int rc;
771 769
 
772
-	/* Record dropped packet if I/O buffer is missing */
773
-	if ( ! iobuf ) {
774
-		DBGC2 ( rndis, "RNDIS %s received dropped packet\n",
775
-			rndis->name );
776
-		rc = -ENOBUFS;
770
+	/* Sanity check */
771
+	if ( iob_len ( iobuf ) < sizeof ( *header ) ) {
772
+		DBGC ( rndis, "RNDIS %s received underlength packet:\n",
773
+		       rndis->name );
774
+		DBGC_HDA ( rndis, 0, iobuf->data, iob_len ( iobuf ) );
775
+		rc = -EINVAL;
777 776
 		goto drop;
778 777
 	}
778
+	header = iobuf->data;
779 779
 
780
-	/* Split packet into messages */
781
-	while ( iobuf ) {
782
-
783
-		/* Sanity check */
784
-		if ( iob_len ( iobuf ) < sizeof ( *header ) ) {
785
-			DBGC ( rndis, "RNDIS %s received underlength packet:\n",
786
-			       rndis->name );
787
-			DBGC_HDA ( rndis, 0, iobuf->data, iob_len ( iobuf ) );
788
-			rc = -EINVAL;
789
-			goto drop;
790
-		}
791
-		header = iobuf->data;
792
-
793
-		/* Parse and check header */
794
-		type = le32_to_cpu ( header->type );
795
-		len = le32_to_cpu ( header->len );
796
-		if ( ( len < sizeof ( *header ) ) ||
797
-		     ( len > iob_len ( iobuf ) ) ) {
798
-			DBGC ( rndis, "RNDIS %s received malformed packet:\n",
799
-			       rndis->name );
800
-			DBGC_HDA ( rndis, 0, iobuf->data, iob_len ( iobuf ) );
801
-			rc = -EINVAL;
802
-			goto drop;
803
-		}
804
-
805
-		/* Split buffer if required */
806
-		if ( len < iob_len ( iobuf ) ) {
807
-			msg = iob_split ( iobuf, len );
808
-			if ( ! msg ) {
809
-				rc = -ENOMEM;
810
-				goto drop;
811
-			}
812
-		} else {
813
-			msg = iobuf;
814
-			iobuf = NULL;
815
-		}
816
-
817
-		/* Strip header */
818
-		iob_pull ( msg, sizeof ( *header ) );
780
+	/* Parse and strip header */
781
+	type = le32_to_cpu ( header->type );
782
+	iob_pull ( iobuf, sizeof ( *header ) );
819 783
 
820
-		/* Handle message */
821
-		rndis_rx_message ( rndis, iob_disown ( msg ), type );
822
-	}
784
+	/* Handle message */
785
+	rndis_rx_message ( rndis, iob_disown ( iobuf ), type );
823 786
 
824 787
 	return;
825 788
 

Loading…
Annulla
Salva