@@ -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 ) ) {
0 commit comments