Selaa lähdekoodia

[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 vuotta sitten
vanhempi
commit
639632b059
4 muutettua tiedostoa jossa 72 lisäystä ja 94 poistoa
  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 Näytä tiedosto

298
  * @v xid		Transaction ID
298
  * @v xid		Transaction ID
299
  * @v data		Data
299
  * @v data		Data
300
  * @v len		Length of data
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
  * @ret rc		Return status code
302
  * @ret rc		Return status code
303
  */
303
  */
304
 static int netvsc_recv_data ( struct vmbus_device *vmdev, uint64_t xid,
304
 static int netvsc_recv_data ( struct vmbus_device *vmdev, uint64_t xid,
305
 			      const void *data, size_t len,
305
 			      const void *data, size_t len,
306
-			      struct io_buffer *iobuf ) {
306
+			      struct list_head *list ) {
307
 	struct rndis_device *rndis = vmbus_get_drvdata ( vmdev );
307
 	struct rndis_device *rndis = vmbus_get_drvdata ( vmdev );
308
 	struct netvsc_device *netvsc = rndis->priv;
308
 	struct netvsc_device *netvsc = rndis->priv;
309
 	const struct netvsc_rndis_message *msg = data;
309
 	const struct netvsc_rndis_message *msg = data;
310
+	struct io_buffer *iobuf;
311
+	struct io_buffer *tmp;
310
 	int rc;
312
 	int rc;
311
 
313
 
312
 	/* Sanity check */
314
 	/* Sanity check */
324
 		goto err_sanity;
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
 	if ( ( rc = vmbus_send_completion ( vmdev, xid, NULL, 0 ) ) != 0 ) {
330
 	if ( ( rc = vmbus_send_completion ( vmdev, xid, NULL, 0 ) ) != 0 ) {
329
 		DBGC ( netvsc, "NETVSC %s could not send completion: %s\n",
331
 		DBGC ( netvsc, "NETVSC %s could not send completion: %s\n",
330
 		       netvsc->name, strerror ( rc ) );
332
 		       netvsc->name, strerror ( rc ) );
331
 		goto err_completion;
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
 	return 0;
342
 	return 0;
338
 
343
 
339
  err_completion:
344
  err_completion:
340
  err_sanity:
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
 	return rc;
350
 	return rc;
343
 }
351
 }
344
 
352
 

+ 2
- 7
src/include/ipxe/vmbus.h Näytä tiedosto

403
 	 * @v xid		Transaction ID
403
 	 * @v xid		Transaction ID
404
 	 * @v data		Data
404
 	 * @v data		Data
405
 	 * @v len		Length of data
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
 	 * @ret rc		Return status code
407
 	 * @ret rc		Return status code
408
 	 *
408
 	 *
409
 	 * This function takes ownership of the I/O buffer.  It should
409
 	 * This function takes ownership of the I/O buffer.  It should
410
 	 * eventually call vmbus_send_completion() to indicate to the
410
 	 * eventually call vmbus_send_completion() to indicate to the
411
 	 * host that the buffer can be reused.
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
 	int ( * recv_data ) ( struct vmbus_device *vmdev, uint64_t xid,
413
 	int ( * recv_data ) ( struct vmbus_device *vmdev, uint64_t xid,
419
 			      const void *data, size_t len,
414
 			      const void *data, size_t len,
420
-			      struct io_buffer *iobuf );
415
+			      struct list_head *list );
421
 	/**
416
 	/**
422
 	 * Handle received completion packet
417
 	 * Handle received completion packet
423
 	 *
418
 	 *

+ 43
- 31
src/interface/hyperv/vmbus.c Näytä tiedosto

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
  * @v vmdev		VMBus device
812
  * @v vmdev		VMBus device
813
  * @v header		Transfer page header
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
 	struct vmbus_xfer_page_header *page_header =
820
 	struct vmbus_xfer_page_header *page_header =
820
 		container_of ( header, struct vmbus_xfer_page_header, header );
821
 		container_of ( header, struct vmbus_xfer_page_header, header );
821
 	struct vmbus_xfer_pages *pages;
822
 	struct vmbus_xfer_pages *pages;
822
 	struct io_buffer *iobuf;
823
 	struct io_buffer *iobuf;
823
-	size_t total_len;
824
+	struct io_buffer *tmp;
824
 	size_t len;
825
 	size_t len;
825
 	size_t offset;
826
 	size_t offset;
826
 	unsigned int range_count;
827
 	unsigned int range_count;
832
 
833
 
833
 	/* Locate page set */
834
 	/* Locate page set */
834
 	pages = vmbus_xfer_pages ( vmdev, page_header->pageset );
835
 	pages = vmbus_xfer_pages ( vmdev, page_header->pageset );
835
-	if ( ! pages )
836
+	if ( ! pages ) {
837
+		rc = -ENOENT;
836
 		goto err_pages;
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
 	for ( i = 0 ; i < range_count ; i++ ) {
843
 	for ( i = 0 ; i < range_count ; i++ ) {
844
+
845
+		/* Parse header */
855
 		len = le32_to_cpu ( page_header->range[i].len );
846
 		len = le32_to_cpu ( page_header->range[i].len );
856
 		offset = le32_to_cpu ( page_header->range[i].offset );
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
 		if ( ( rc = pages->op->copy ( pages, iob_put ( iobuf, len ),
862
 		if ( ( rc = pages->op->copy ( pages, iob_put ( iobuf, len ),
858
 					      offset, len ) ) != 0 ) {
863
 					      offset, len ) ) != 0 ) {
859
 			DBGC ( vmdev, "VMBUS %s could not populate I/O buffer "
864
 			DBGC ( vmdev, "VMBUS %s could not populate I/O buffer "
863
 		}
868
 		}
864
 	}
869
 	}
865
 
870
 
866
-	return iobuf;
871
+	return 0;
867
 
872
 
868
  err_copy:
873
  err_copy:
869
-	free_iob ( iobuf );
870
  err_alloc:
874
  err_alloc:
875
+	list_for_each_entry_safe ( iobuf, tmp, list, list ) {
876
+		list_del ( &iobuf->list );
877
+		free_iob ( iobuf );
878
+	}
871
  err_pages:
879
  err_pages:
872
-	return NULL;
880
+	return rc;
873
 }
881
 }
874
 
882
 
875
 /**
883
 /**
880
  */
888
  */
881
 int vmbus_poll ( struct vmbus_device *vmdev ) {
889
 int vmbus_poll ( struct vmbus_device *vmdev ) {
882
 	struct vmbus_packet_header *header = vmdev->packet;
890
 	struct vmbus_packet_header *header = vmdev->packet;
883
-	struct io_buffer *iobuf;
891
+	struct list_head list;
884
 	void *data;
892
 	void *data;
885
 	size_t header_len;
893
 	size_t header_len;
886
 	size_t len;
894
 	size_t len;
929
 	DBGC2_HDA ( vmdev, old_cons, header, ring_len );
937
 	DBGC2_HDA ( vmdev, old_cons, header, ring_len );
930
 	assert ( ( ( cons - old_cons ) & ( vmdev->in_len - 1 ) ) == ring_len );
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
 	/* Update producer index */
948
 	/* Update producer index */
933
 	rmb();
949
 	rmb();
934
 	vmdev->in->cons = cpu_to_le32 ( cons );
950
 	vmdev->in->cons = cpu_to_le32 ( cons );
948
 		break;
964
 		break;
949
 
965
 
950
 	case cpu_to_le16 ( VMBUS_DATA_XFER_PAGES ) :
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
 		if ( ( rc = vmdev->op->recv_data ( vmdev, xid, data, len,
967
 		if ( ( rc = vmdev->op->recv_data ( vmdev, xid, data, len,
956
-						   iob_disown ( iobuf ) ) )!=0){
968
+						   &list ) ) != 0 ) {
957
 			DBGC ( vmdev, "VMBUS %s could not handle data packet: "
969
 			DBGC ( vmdev, "VMBUS %s could not handle data packet: "
958
 			       "%s\n", vmdev->dev.name, strerror ( rc ) );
970
 			       "%s\n", vmdev->dev.name, strerror ( rc ) );
959
 			return rc;
971
 			return rc;

+ 13
- 50
src/net/rndis.c Näytä tiedosto

759
  * Receive packet from underlying transport layer
759
  * Receive packet from underlying transport layer
760
  *
760
  *
761
  * @v rndis		RNDIS device
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
 void rndis_rx ( struct rndis_device *rndis, struct io_buffer *iobuf ) {
764
 void rndis_rx ( struct rndis_device *rndis, struct io_buffer *iobuf ) {
765
 	struct net_device *netdev = rndis->netdev;
765
 	struct net_device *netdev = rndis->netdev;
766
 	struct rndis_header *header;
766
 	struct rndis_header *header;
767
-	struct io_buffer *msg;
768
-	size_t len;
769
 	unsigned int type;
767
 	unsigned int type;
770
 	int rc;
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
 		goto drop;
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
 	return;
787
 	return;
825
 
788
 

Loading…
Peruuta
Tallenna