ソースを参照

[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年前
コミット
88a5f56dc7
2個のファイルの変更79行の追加80行の削除
  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 ファイルの表示

18
 #include <ipxe/efi/Protocol/HiiDatabase.h>
18
 #include <ipxe/efi/Protocol/HiiDatabase.h>
19
 #include <ipxe/efi/Protocol/LoadFile.h>
19
 #include <ipxe/efi/Protocol/LoadFile.h>
20
 
20
 
21
+/** SNP transmit completion ring size */
22
+#define EFI_SNP_NUM_TX 32
23
+
21
 /** An SNP device */
24
 /** An SNP device */
22
 struct efi_snp_device {
25
 struct efi_snp_device {
23
 	/** List of SNP devices */
26
 	/** List of SNP devices */
34
 	EFI_SIMPLE_NETWORK_MODE mode;
37
 	EFI_SIMPLE_NETWORK_MODE mode;
35
 	/** Started flag */
38
 	/** Started flag */
36
 	int started;
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
 	/** The network interface identifier */
50
 	/** The network interface identifier */
52
 	EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL nii;
51
 	EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL nii;
53
 	/** Component name protocol */
52
 	/** Component name protocol */

+ 66
- 66
src/interface/efi/efi_snp.c ファイルの表示

97
 	mode->MediaPresent = ( netdev_link_ok ( netdev ) ? TRUE : FALSE );
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
  * Poll net device and count received packets
121
  * Poll net device and count received packets
102
  *
122
  *
103
  * @v snpdev		SNP device
123
  * @v snpdev		SNP device
104
  */
124
  */
105
 static void efi_snp_poll ( struct efi_snp_device *snpdev ) {
125
 static void efi_snp_poll ( struct efi_snp_device *snpdev ) {
126
+	EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
106
 	struct io_buffer *iobuf;
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
 	netdev_poll ( snpdev->netdev );
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
 
236
 
222
 	netdev_close ( snpdev->netdev );
237
 	netdev_close ( snpdev->netdev );
223
 	efi_snp_set_state ( snpdev );
238
 	efi_snp_set_state ( snpdev );
239
+	efi_snp_flush ( snpdev );
224
 
240
 
225
 	if ( ( rc = netdev_open ( snpdev->netdev ) ) != 0 ) {
241
 	if ( ( rc = netdev_open ( snpdev->netdev ) ) != 0 ) {
226
 		DBGC ( snpdev, "SNPDEV %p could not reopen %s: %s\n",
242
 		DBGC ( snpdev, "SNPDEV %p could not reopen %s: %s\n",
251
 
267
 
252
 	netdev_close ( snpdev->netdev );
268
 	netdev_close ( snpdev->netdev );
253
 	efi_snp_set_state ( snpdev );
269
 	efi_snp_set_state ( snpdev );
270
+	efi_snp_flush ( snpdev );
254
 
271
 
255
 	return 0;
272
 	return 0;
256
 }
273
 }
446
  *
463
  *
447
  * @v snp		SNP interface
464
  * @v snp		SNP interface
448
  * @v interrupts	Interrupt status, or NULL
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
  * @ret efirc		EFI status code
467
  * @ret efirc		EFI status code
451
  */
468
  */
452
 static EFI_STATUS EFIAPI
469
 static EFI_STATUS EFIAPI
453
 efi_snp_get_status ( EFI_SIMPLE_NETWORK_PROTOCOL *snp,
470
 efi_snp_get_status ( EFI_SIMPLE_NETWORK_PROTOCOL *snp,
454
-		     UINT32 *interrupts, VOID **txbufs ) {
471
+		     UINT32 *interrupts, VOID **txbuf ) {
455
 	struct efi_snp_device *snpdev =
472
 	struct efi_snp_device *snpdev =
456
 		container_of ( snp, struct efi_snp_device, snp );
473
 		container_of ( snp, struct efi_snp_device, snp );
457
 
474
 
458
 	DBGC2 ( snpdev, "SNPDEV %p GET_STATUS", snpdev );
475
 	DBGC2 ( snpdev, "SNPDEV %p GET_STATUS", snpdev );
459
 
476
 
460
 	/* Fail if net device is currently claimed for use by iPXE */
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
 		return EFI_NOT_READY;
480
 		return EFI_NOT_READY;
481
+	}
463
 
482
 
464
 	/* Poll the network device */
483
 	/* Poll the network device */
465
 	efi_snp_poll ( snpdev );
484
 	efi_snp_poll ( snpdev );
468
 	 * to detect TX completions.
487
 	 * to detect TX completions.
469
 	 */
488
 	 */
470
 	if ( interrupts ) {
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
 		DBGC2 ( snpdev, " INTS:%02x", *interrupts );
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
 		} else {
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
 	DBGC2 ( snpdev, "\n" );
505
 	DBGC2 ( snpdev, "\n" );
537
 	struct ll_protocol *ll_protocol = snpdev->netdev->ll_protocol;
528
 	struct ll_protocol *ll_protocol = snpdev->netdev->ll_protocol;
538
 	struct io_buffer *iobuf;
529
 	struct io_buffer *iobuf;
539
 	size_t payload_len;
530
 	size_t payload_len;
531
+	unsigned int tx_fill;
540
 	int rc;
532
 	int rc;
541
 
533
 
542
 	DBGC2 ( snpdev, "SNPDEV %p TRANSMIT %p+%lx", snpdev, data,
534
 	DBGC2 ( snpdev, "SNPDEV %p TRANSMIT %p+%lx", snpdev, data,
624
 		goto err_tx;
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
 	return 0;
637
 	return 0;
632
 
638
 
639
+ err_ring_full:
633
  err_tx:
640
  err_tx:
634
  err_ll_push:
641
  err_ll_push:
635
 	free_iob ( iobuf );
642
 	free_iob ( iobuf );
676
 	efi_snp_poll ( snpdev );
683
 	efi_snp_poll ( snpdev );
677
 
684
 
678
 	/* Dequeue a packet, if one is available */
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
 	if ( ! iobuf ) {
687
 	if ( ! iobuf ) {
681
 		DBGC2 ( snpdev, "\n" );
688
 		DBGC2 ( snpdev, "\n" );
682
 		rc = -EAGAIN;
689
 		rc = -EAGAIN;
683
 		goto out_no_packet;
690
 		goto out_no_packet;
684
 	}
691
 	}
692
+	list_del ( &iobuf->list );
685
 	DBGC2 ( snpdev, "+%zx\n", iob_len ( iobuf ) );
693
 	DBGC2 ( snpdev, "+%zx\n", iob_len ( iobuf ) );
686
 
694
 
687
 	/* Return packet to caller */
695
 	/* Return packet to caller */
721
  * @v event		Event
729
  * @v event		Event
722
  * @v context		Event context
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
 					     VOID *context ) {
733
 					     VOID *context ) {
726
-	EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
727
 	struct efi_snp_device *snpdev = context;
734
 	struct efi_snp_device *snpdev = context;
728
 
735
 
729
 	DBGCP ( snpdev, "SNPDEV %p WAIT_FOR_PACKET\n", snpdev );
736
 	DBGCP ( snpdev, "SNPDEV %p WAIT_FOR_PACKET\n", snpdev );
738
 
745
 
739
 	/* Poll the network device */
746
 	/* Poll the network device */
740
 	efi_snp_poll ( snpdev );
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
 /** SNP interface */
750
 /** SNP interface */
922
 	}
921
 	}
923
 	snpdev->netdev = netdev_get ( netdev );
922
 	snpdev->netdev = netdev_get ( netdev );
924
 	snpdev->efidev = efidev;
923
 	snpdev->efidev = efidev;
924
+	INIT_LIST_HEAD ( &snpdev->rx );
925
 
925
 
926
 	/* Sanity check */
926
 	/* Sanity check */
927
 	if ( netdev->ll_protocol->ll_addr_len > sizeof ( EFI_MAC_ADDRESS ) ) {
927
 	if ( netdev->ll_protocol->ll_addr_len > sizeof ( EFI_MAC_ADDRESS ) ) {

読み込み中…
キャンセル
保存