mirror of https://github.com/ipxe/ipxe.git
[netdevice] Ensure driver transmit() and poll() will not be re-entered
When CONSOLE_SYSLOG is used, a DBG() from within a network device driver may cause its transmit() or poll() methods to be unexpectedly re-entered. Since these methods are not intended to be re-entrant, this can lead to undefined behaviour. Add an explicit re-entrancy guard to both methods. Note that this must operate at a per-netdevice level, since there are legitimate circumstances under which the netdev_tx() or netdev_poll() functions may be re-entered (e.g. when using VLAN devices). Signed-off-by: Michael Brown <mcb30@ipxe.org>pull/329/head
parent
0be8491b71
commit
78749542fc
|
@ -451,6 +451,12 @@ struct net_device {
|
|||
*/
|
||||
#define NETDEV_IRQ_UNSUPPORTED 0x0008
|
||||
|
||||
/** Network device transmission is in progress */
|
||||
#define NETDEV_TX_IN_PROGRESS 0x0010
|
||||
|
||||
/** Network device poll is in progress */
|
||||
#define NETDEV_POLL_IN_PROGRESS 0x0020
|
||||
|
||||
/** Link-layer protocol table */
|
||||
#define LL_PROTOCOLS __table ( struct ll_protocol, "ll_protocols" )
|
||||
|
||||
|
|
|
@ -297,30 +297,45 @@ int netdev_tx ( struct net_device *netdev, struct io_buffer *iobuf ) {
|
|||
/* Enqueue packet */
|
||||
list_add_tail ( &iobuf->list, &netdev->tx_queue );
|
||||
|
||||
/* Guard against re-entry */
|
||||
if ( netdev->state & NETDEV_TX_IN_PROGRESS ) {
|
||||
rc = -EBUSY;
|
||||
goto err_busy;
|
||||
}
|
||||
netdev->state |= NETDEV_TX_IN_PROGRESS;
|
||||
|
||||
/* Avoid calling transmit() on unopened network devices */
|
||||
if ( ! netdev_is_open ( netdev ) ) {
|
||||
rc = -ENETUNREACH;
|
||||
goto err;
|
||||
goto err_closed;
|
||||
}
|
||||
|
||||
/* Discard packet (for test purposes) if applicable */
|
||||
if ( ( rc = inject_fault ( NETDEV_DISCARD_RATE ) ) != 0 )
|
||||
goto err;
|
||||
goto err_fault;
|
||||
|
||||
/* Map for DMA, if required */
|
||||
if ( netdev->dma && ( ! dma_mapped ( &iobuf->map ) ) ) {
|
||||
if ( ( rc = iob_map_tx ( iobuf, netdev->dma ) ) != 0 )
|
||||
goto err;
|
||||
goto err_map;
|
||||
}
|
||||
|
||||
/* Transmit packet */
|
||||
if ( ( rc = netdev->op->transmit ( netdev, iobuf ) ) != 0 )
|
||||
goto err;
|
||||
goto err_transmit;
|
||||
|
||||
/* Clear in-progress flag */
|
||||
netdev->state &= ~NETDEV_TX_IN_PROGRESS;
|
||||
|
||||
profile_stop ( &net_tx_profiler );
|
||||
return 0;
|
||||
|
||||
err:
|
||||
err_transmit:
|
||||
err_map:
|
||||
err_fault:
|
||||
err_closed:
|
||||
netdev->state &= ~NETDEV_TX_IN_PROGRESS;
|
||||
err_busy:
|
||||
netdev_tx_complete_err ( netdev, iobuf, rc );
|
||||
return rc;
|
||||
}
|
||||
|
@ -552,8 +567,18 @@ void netdev_rx_err ( struct net_device *netdev,
|
|||
*/
|
||||
void netdev_poll ( struct net_device *netdev ) {
|
||||
|
||||
if ( netdev_is_open ( netdev ) )
|
||||
/* Avoid calling poll() on unopened network devices */
|
||||
if ( ! netdev_is_open ( netdev ) )
|
||||
return;
|
||||
|
||||
/* Guard against re-entry */
|
||||
if ( netdev->state & NETDEV_POLL_IN_PROGRESS )
|
||||
return;
|
||||
|
||||
/* Poll device */
|
||||
netdev->state |= NETDEV_POLL_IN_PROGRESS;
|
||||
netdev->op->poll ( netdev );
|
||||
netdev->state &= ~NETDEV_POLL_IN_PROGRESS;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue