Bläddra i källkod

[efi] Fix receive and transmit completion reporting

Fix the TxBuf value filled in by GetStatus() to report the transmit
buffer address as required by the (now clarified) specification.

Simplify "interrupt" handling in GetStatus() to report only that one
or more packets have been transmitted or received; there is no need to
report one GetStatus() "interrupt" per packet.

Simplify receive handling to dequeue received packets immediately from
the network device into an internal list (thereby avoiding the hacks
previously used to determine when to report new packet arrivals).

Originally-fixed-by: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 9 år sedan
förälder
incheckning
88a5f56dc7
2 ändrade filer med 79 tillägg och 80 borttagningar
  1. 13
    14
      src/include/ipxe/efi/efi_snp.h
  2. 66
    66
      src/interface/efi/efi_snp.c

+ 13
- 14
src/include/ipxe/efi/efi_snp.h Visa fil

@@ -18,6 +18,9 @@
18 18
 #include <ipxe/efi/Protocol/HiiDatabase.h>
19 19
 #include <ipxe/efi/Protocol/LoadFile.h>
20 20
 
21
+/** SNP transmit completion ring size */
22
+#define EFI_SNP_NUM_TX 32
23
+
21 24
 /** An SNP device */
22 25
 struct efi_snp_device {
23 26
 	/** List of SNP devices */
@@ -34,20 +37,16 @@ struct efi_snp_device {
34 37
 	EFI_SIMPLE_NETWORK_MODE mode;
35 38
 	/** Started flag */
36 39
 	int started;
37
-	/** Outstanding TX packet count (via "interrupt status")
38
-	 *
39
-	 * Used in order to generate TX completions.
40
-	 */
41
-	unsigned int tx_count_interrupts;
42
-	/** Outstanding TX packet count (via "recycled tx buffers")
43
-	 *
44
-	 * Used in order to generate TX completions.
45
-	 */
46
-	unsigned int tx_count_txbufs;
47
-	/** Outstanding RX packet count (via "interrupt status") */
48
-	unsigned int rx_count_interrupts;
49
-	/** Outstanding RX packet count (via WaitForPacket event) */
50
-	unsigned int rx_count_events;
40
+	/** Pending interrupt status */
41
+	unsigned int interrupts;
42
+	/** Transmit completion ring */
43
+	VOID *tx[EFI_SNP_NUM_TX];
44
+	/** Transmit completion ring producer counter */
45
+	unsigned int tx_prod;
46
+	/** Transmit completion ring consumer counter */
47
+	unsigned int tx_cons;
48
+	/** Receive queue */
49
+	struct list_head rx;
51 50
 	/** The network interface identifier */
52 51
 	EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL nii;
53 52
 	/** Component name protocol */

+ 66
- 66
src/interface/efi/efi_snp.c Visa fil

@@ -97,29 +97,44 @@ static void efi_snp_set_mode ( struct efi_snp_device *snpdev ) {
97 97
 	mode->MediaPresent = ( netdev_link_ok ( netdev ) ? TRUE : FALSE );
98 98
 }
99 99
 
100
+/**
101
+ * Flush transmit ring and receive queue
102
+ *
103
+ * @v snpdev		SNP device
104
+ */
105
+static void efi_snp_flush ( struct efi_snp_device *snpdev ) {
106
+	struct io_buffer *iobuf;
107
+	struct io_buffer *tmp;
108
+
109
+	/* Reset transmit completion ring */
110
+	snpdev->tx_prod = 0;
111
+	snpdev->tx_cons = 0;
112
+
113
+	/* Discard any queued receive buffers */
114
+	list_for_each_entry_safe ( iobuf, tmp, &snpdev->rx, list ) {
115
+		list_del ( &iobuf->list );
116
+		free_iob ( iobuf );
117
+	}
118
+}
119
+
100 120
 /**
101 121
  * Poll net device and count received packets
102 122
  *
103 123
  * @v snpdev		SNP device
104 124
  */
105 125
 static void efi_snp_poll ( struct efi_snp_device *snpdev ) {
126
+	EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
106 127
 	struct io_buffer *iobuf;
107
-	unsigned int before = 0;
108
-	unsigned int after = 0;
109
-	unsigned int arrived;
110 128
 
111
-	/* We have to report packet arrivals, and this is the easiest
112
-	 * way to fake it.
113
-	 */
114
-	list_for_each_entry ( iobuf, &snpdev->netdev->rx_queue, list )
115
-		before++;
129
+	/* Poll network device */
116 130
 	netdev_poll ( snpdev->netdev );
117
-	list_for_each_entry ( iobuf, &snpdev->netdev->rx_queue, list )
118
-		after++;
119
-	arrived = ( after - before );
120 131
 
121
-	snpdev->rx_count_interrupts += arrived;
122
-	snpdev->rx_count_events += arrived;
132
+	/* Retrieve any received packets */
133
+	while ( ( iobuf = netdev_rx_dequeue ( snpdev->netdev ) ) ) {
134
+		list_add_tail ( &iobuf->list, &snpdev->rx );
135
+		snpdev->interrupts |= EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT;
136
+		bs->SignalEvent ( &snpdev->snp.WaitForPacket );
137
+	}
123 138
 }
124 139
 
125 140
 /**
@@ -221,6 +236,7 @@ efi_snp_reset ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN ext_verify ) {
221 236
 
222 237
 	netdev_close ( snpdev->netdev );
223 238
 	efi_snp_set_state ( snpdev );
239
+	efi_snp_flush ( snpdev );
224 240
 
225 241
 	if ( ( rc = netdev_open ( snpdev->netdev ) ) != 0 ) {
226 242
 		DBGC ( snpdev, "SNPDEV %p could not reopen %s: %s\n",
@@ -251,6 +267,7 @@ efi_snp_shutdown ( EFI_SIMPLE_NETWORK_PROTOCOL *snp ) {
251 267
 
252 268
 	netdev_close ( snpdev->netdev );
253 269
 	efi_snp_set_state ( snpdev );
270
+	efi_snp_flush ( snpdev );
254 271
 
255 272
 	return 0;
256 273
 }
@@ -446,20 +463,22 @@ efi_snp_nvdata ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN read,
446 463
  *
447 464
  * @v snp		SNP interface
448 465
  * @v interrupts	Interrupt status, or NULL
449
- * @v txbufs		Recycled transmit buffer address, or NULL
466
+ * @v txbuf		Recycled transmit buffer address, or NULL
450 467
  * @ret efirc		EFI status code
451 468
  */
452 469
 static EFI_STATUS EFIAPI
453 470
 efi_snp_get_status ( EFI_SIMPLE_NETWORK_PROTOCOL *snp,
454
-		     UINT32 *interrupts, VOID **txbufs ) {
471
+		     UINT32 *interrupts, VOID **txbuf ) {
455 472
 	struct efi_snp_device *snpdev =
456 473
 		container_of ( snp, struct efi_snp_device, snp );
457 474
 
458 475
 	DBGC2 ( snpdev, "SNPDEV %p GET_STATUS", snpdev );
459 476
 
460 477
 	/* Fail if net device is currently claimed for use by iPXE */
461
-	if ( efi_snp_claimed )
478
+	if ( efi_snp_claimed ) {
479
+		DBGC2 ( snpdev, "\n" );
462 480
 		return EFI_NOT_READY;
481
+	}
463 482
 
464 483
 	/* Poll the network device */
465 484
 	efi_snp_poll ( snpdev );
@@ -468,47 +487,19 @@ efi_snp_get_status ( EFI_SIMPLE_NETWORK_PROTOCOL *snp,
468 487
 	 * to detect TX completions.
469 488
 	 */
470 489
 	if ( interrupts ) {
471
-		*interrupts = 0;
472
-		/* Report TX completions once queue is empty; this
473
-		 * avoids having to add hooks in the net device layer.
474
-		 */
475
-		if ( snpdev->tx_count_interrupts &&
476
-		     list_empty ( &snpdev->netdev->tx_queue ) ) {
477
-			*interrupts |= EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT;
478
-			snpdev->tx_count_interrupts--;
479
-		}
480
-		/* Report RX */
481
-		if ( snpdev->rx_count_interrupts ) {
482
-			*interrupts |= EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT;
483
-			snpdev->rx_count_interrupts--;
484
-		}
490
+		*interrupts = snpdev->interrupts;
485 491
 		DBGC2 ( snpdev, " INTS:%02x", *interrupts );
492
+		snpdev->interrupts = 0;
486 493
 	}
487 494
 
488
-	/* TX completions.  It would be possible to design a more
489
-	 * idiotic scheme for this, but it would be a challenge.
490
-	 * According to the UEFI header file, txbufs will be filled in
491
-	 * with a list of "recycled transmit buffers" (i.e. completed
492
-	 * TX buffers).  Observant readers may care to note that
493
-	 * *txbufs is a void pointer.  Precisely how a list of
494
-	 * completed transmit buffers is meant to be represented as an
495
-	 * array of voids is left as an exercise for the reader.
496
-	 *
497
-	 * The only users of this interface (MnpDxe/MnpIo.c and
498
-	 * PxeBcDxe/Bc.c within the EFI dev kit) both just poll until
499
-	 * seeing a non-NULL result return in txbufs.  This is valid
500
-	 * provided that they do not ever attempt to transmit more
501
-	 * than one packet concurrently (and that TX never times out).
502
-	 */
503
-	if ( txbufs ) {
504
-		if ( snpdev->tx_count_txbufs &&
505
-		     list_empty ( &snpdev->netdev->tx_queue ) ) {
506
-			*txbufs = "Which idiot designed this API?";
507
-			snpdev->tx_count_txbufs--;
495
+	/* TX completions */
496
+	if ( txbuf ) {
497
+		if ( snpdev->tx_prod != snpdev->tx_cons ) {
498
+			*txbuf = snpdev->tx[snpdev->tx_cons++ % EFI_SNP_NUM_TX];
508 499
 		} else {
509
-			*txbufs = NULL;
500
+			*txbuf = NULL;
510 501
 		}
511
-		DBGC2 ( snpdev, " TX:%s", ( *txbufs ? "some" : "none" ) );
502
+		DBGC2 ( snpdev, " TX:%p", *txbuf );
512 503
 	}
513 504
 
514 505
 	DBGC2 ( snpdev, "\n" );
@@ -537,6 +528,7 @@ efi_snp_transmit ( EFI_SIMPLE_NETWORK_PROTOCOL *snp,
537 528
 	struct ll_protocol *ll_protocol = snpdev->netdev->ll_protocol;
538 529
 	struct io_buffer *iobuf;
539 530
 	size_t payload_len;
531
+	unsigned int tx_fill;
540 532
 	int rc;
541 533
 
542 534
 	DBGC2 ( snpdev, "SNPDEV %p TRANSMIT %p+%lx", snpdev, data,
@@ -624,12 +616,27 @@ efi_snp_transmit ( EFI_SIMPLE_NETWORK_PROTOCOL *snp,
624 616
 		goto err_tx;
625 617
 	}
626 618
 
627
-	/* Record transmission as outstanding */
628
-	snpdev->tx_count_interrupts++;
629
-	snpdev->tx_count_txbufs++;
619
+	/* Record in transmit completion ring.  If we run out of
620
+	 * space, report the failure even though we have already
621
+	 * transmitted the packet.
622
+	 *
623
+	 * This allows us to report completions only for packets for
624
+	 * which we had reported successfully initiating transmission,
625
+	 * while continuing to support clients that never poll for
626
+	 * transmit completions.
627
+	 */
628
+	tx_fill = ( snpdev->tx_prod - snpdev->tx_cons );
629
+	if ( tx_fill >= EFI_SNP_NUM_TX ) {
630
+		DBGC ( snpdev, "SNPDEV %p TX completion ring full\n", snpdev );
631
+		rc = -ENOBUFS;
632
+		goto err_ring_full;
633
+	}
634
+	snpdev->tx[ snpdev->tx_prod++ % EFI_SNP_NUM_TX ] = data;
635
+	snpdev->interrupts |= EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT;
630 636
 
631 637
 	return 0;
632 638
 
639
+ err_ring_full:
633 640
  err_tx:
634 641
  err_ll_push:
635 642
 	free_iob ( iobuf );
@@ -676,12 +683,13 @@ efi_snp_receive ( EFI_SIMPLE_NETWORK_PROTOCOL *snp,
676 683
 	efi_snp_poll ( snpdev );
677 684
 
678 685
 	/* Dequeue a packet, if one is available */
679
-	iobuf = netdev_rx_dequeue ( snpdev->netdev );
686
+	iobuf = list_first_entry ( &snpdev->rx, struct io_buffer, list );
680 687
 	if ( ! iobuf ) {
681 688
 		DBGC2 ( snpdev, "\n" );
682 689
 		rc = -EAGAIN;
683 690
 		goto out_no_packet;
684 691
 	}
692
+	list_del ( &iobuf->list );
685 693
 	DBGC2 ( snpdev, "+%zx\n", iob_len ( iobuf ) );
686 694
 
687 695
 	/* Return packet to caller */
@@ -721,9 +729,8 @@ efi_snp_receive ( EFI_SIMPLE_NETWORK_PROTOCOL *snp,
721 729
  * @v event		Event
722 730
  * @v context		Event context
723 731
  */
724
-static VOID EFIAPI efi_snp_wait_for_packet ( EFI_EVENT event,
732
+static VOID EFIAPI efi_snp_wait_for_packet ( EFI_EVENT event __unused,
725 733
 					     VOID *context ) {
726
-	EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
727 734
 	struct efi_snp_device *snpdev = context;
728 735
 
729 736
 	DBGCP ( snpdev, "SNPDEV %p WAIT_FOR_PACKET\n", snpdev );
@@ -738,14 +745,6 @@ static VOID EFIAPI efi_snp_wait_for_packet ( EFI_EVENT event,
738 745
 
739 746
 	/* Poll the network device */
740 747
 	efi_snp_poll ( snpdev );
741
-
742
-	/* Fire event if packets have been received */
743
-	if ( snpdev->rx_count_events != 0 ) {
744
-		DBGC2 ( snpdev, "SNPDEV %p firing WaitForPacket event\n",
745
-			snpdev );
746
-		bs->SignalEvent ( event );
747
-		snpdev->rx_count_events--;
748
-	}
749 748
 }
750 749
 
751 750
 /** SNP interface */
@@ -922,6 +921,7 @@ static int efi_snp_probe ( struct net_device *netdev ) {
922 921
 	}
923 922
 	snpdev->netdev = netdev_get ( netdev );
924 923
 	snpdev->efidev = efidev;
924
+	INIT_LIST_HEAD ( &snpdev->rx );
925 925
 
926 926
 	/* Sanity check */
927 927
 	if ( netdev->ll_protocol->ll_addr_len > sizeof ( EFI_MAC_ADDRESS ) ) {

Laddar…
Avbryt
Spara