summaryrefslogtreecommitdiffstats
path: root/src/net
diff options
context:
space:
mode:
authorMichael Brown2017-09-05 13:21:11 +0200
committerMichael Brown2017-09-05 13:30:04 +0200
commit97f0f56a34e32e705d3eee18526222f43fc88e6e (patch)
tree4760c8f6b536135bccdfdffb78887d96bc072891 /src/net
parent[efi] Raise TPL when calling UNDI entry point (diff)
downloadipxe-97f0f56a34e32e705d3eee18526222f43fc88e6e.tar.gz
ipxe-97f0f56a34e32e705d3eee18526222f43fc88e6e.tar.xz
ipxe-97f0f56a34e32e705d3eee18526222f43fc88e6e.zip
[netdevice] Cancel all pending transmissions on any transmit error
Some external code (such as the UEFI UNDI driver for the Realtek USB NIC on a Microsoft Surface Book) will block during transmission attempts and can take several seconds to report a transmit error. If there is a large queue of pending transmissions, then the accumulated time from a series of such failures can easily exceed the EFI watchdog timeout, resulting in what appears to be a system lockup followed by a reboot. Work around this problem by immediately cancelling any pending transmissions as soon as any transmit error occurs. The only expected transmit error under normal operation is ENOBUFS arising when the hardware transmit queue is full. By definition, this can happen only for drivers that do not utilise deferred transmissions, and so this new behaviour will not affect these drivers. Signed-off-by: Michael Brown <mcb30@ipxe.org>
Diffstat (limited to 'src/net')
-rw-r--r--src/net/netdevice.c19
1 files changed, 16 insertions, 3 deletions
diff --git a/src/net/netdevice.c b/src/net/netdevice.c
index 41ece77f..4c211d70 100644
--- a/src/net/netdevice.c
+++ b/src/net/netdevice.c
@@ -402,11 +402,24 @@ void netdev_tx_complete_err ( struct net_device *netdev,
list_del ( &iobuf->list );
netdev_tx_err ( netdev, iobuf, rc );
- /* Transmit first pending packet, if any */
- if ( ( iobuf = list_first_entry ( &netdev->tx_deferred,
- struct io_buffer, list ) ) != NULL ) {
+ /* Handle pending transmit queue */
+ while ( ( iobuf = list_first_entry ( &netdev->tx_deferred,
+ struct io_buffer, list ) ) ) {
+
+ /* Remove from pending transmit queue */
list_del ( &iobuf->list );
+
+ /* When any transmit completion fails, cancel all
+ * pending transmissions.
+ */
+ if ( rc != 0 ) {
+ netdev_tx_err ( netdev, iobuf, -ECANCELED );
+ continue;
+ }
+
+ /* Otherwise, attempt to transmit the first pending packet */
netdev_tx ( netdev, iobuf );
+ break;
}
}