diff options
Diffstat (limited to 'src/interface/efi/efi_snp.c')
-rw-r--r-- | src/interface/efi/efi_snp.c | 54 |
1 files changed, 31 insertions, 23 deletions
diff --git a/src/interface/efi/efi_snp.c b/src/interface/efi/efi_snp.c index 67fba342..c21af33d 100644 --- a/src/interface/efi/efi_snp.c +++ b/src/interface/efi/efi_snp.c @@ -68,6 +68,14 @@ static void efi_snp_set_state ( struct efi_snp_device *snpdev ) { */ mode->State = EfiSimpleNetworkInitialized; } + + if (mode->State != EfiSimpleNetworkInitialized) { + /* Zero the number of recycled buffers when moving to any other + * state than Initialized. Transmit() and GetStatus() are only + * valid in Initialized. + */ + snpdev->tx_count_txbufs = 0; + } } /** @@ -446,12 +454,12 @@ efi_snp_nvdata ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN read, * * @v snp SNP interface * @v interrupts Interrupt status, or NULL - * @v txbufs Recycled transmit buffer address, or NULL + * @v txbuf Recycled transmit buffer address, or NULL * @ret efirc EFI status code */ static EFI_STATUS EFIAPI efi_snp_get_status ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, - UINT32 *interrupts, VOID **txbufs ) { + UINT32 *interrupts, VOID **txbuf ) { struct efi_snp_device *snpdev = container_of ( snp, struct efi_snp_device, snp ); @@ -485,30 +493,22 @@ efi_snp_get_status ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, DBGC2 ( snpdev, " INTS:%02x", *interrupts ); } - /* TX completions. It would be possible to design a more - * idiotic scheme for this, but it would be a challenge. - * According to the UEFI header file, txbufs will be filled in - * with a list of "recycled transmit buffers" (i.e. completed - * TX buffers). Observant readers may care to note that - * *txbufs is a void pointer. Precisely how a list of - * completed transmit buffers is meant to be represented as an - * array of voids is left as an exercise for the reader. - * - * The only users of this interface (MnpDxe/MnpIo.c and - * PxeBcDxe/Bc.c within the EFI dev kit) both just poll until - * seeing a non-NULL result return in txbufs. This is valid - * provided that they do not ever attempt to transmit more - * than one packet concurrently (and that TX never times out). + /* In efi_snp_transmit() we enqueue packets by copying them (not by + * linking them), hence we can recycle them immediately to the SNP + * client. */ - if ( txbufs ) { - if ( snpdev->tx_count_txbufs && - list_empty ( &snpdev->netdev->tx_queue ) ) { - *txbufs = "Which idiot designed this API?"; + if ( txbuf ) { + if ( snpdev->tx_count_txbufs ) { + unsigned first; + + first = snpdev->tx_first_txbuf++; + snpdev->tx_first_txbuf %= MAX_RECYCLED_TXBUFS; + *txbuf = snpdev->tx_recycled_txbufs[first]; snpdev->tx_count_txbufs--; } else { - *txbufs = NULL; + *txbuf = NULL; } - DBGC2 ( snpdev, " TX:%s", ( *txbufs ? "some" : "none" ) ); + DBGC2 ( snpdev, " TX:%p", *txbuf ); } DBGC2 ( snpdev, "\n" ); @@ -560,6 +560,12 @@ efi_snp_transmit ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, if ( efi_snp_claimed ) return EFI_NOT_READY; + assert ( snpdev->tx_count_txbufs <= MAX_RECYCLED_TXBUFS ); + if ( snpdev->tx_count_txbufs == MAX_RECYCLED_TXBUFS ) { + /* No room to recycle another buffer. */ + return EFI_NOT_READY; + } + /* Sanity checks */ if ( ll_header_len ) { if ( ll_header_len != ll_protocol->ll_header_len ) { @@ -626,7 +632,9 @@ efi_snp_transmit ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, /* Record transmission as outstanding */ snpdev->tx_count_interrupts++; - snpdev->tx_count_txbufs++; + snpdev->tx_recycled_txbufs[(snpdev->tx_first_txbuf + + snpdev->tx_count_txbufs++ + ) % MAX_RECYCLED_TXBUFS] = data; return 0; |