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
|
#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 */
|
/** Link-layer protocol table */
|
||||||
#define LL_PROTOCOLS __table ( struct ll_protocol, "ll_protocols" )
|
#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 */
|
/* Enqueue packet */
|
||||||
list_add_tail ( &iobuf->list, &netdev->tx_queue );
|
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 */
|
/* Avoid calling transmit() on unopened network devices */
|
||||||
if ( ! netdev_is_open ( netdev ) ) {
|
if ( ! netdev_is_open ( netdev ) ) {
|
||||||
rc = -ENETUNREACH;
|
rc = -ENETUNREACH;
|
||||||
goto err;
|
goto err_closed;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Discard packet (for test purposes) if applicable */
|
/* Discard packet (for test purposes) if applicable */
|
||||||
if ( ( rc = inject_fault ( NETDEV_DISCARD_RATE ) ) != 0 )
|
if ( ( rc = inject_fault ( NETDEV_DISCARD_RATE ) ) != 0 )
|
||||||
goto err;
|
goto err_fault;
|
||||||
|
|
||||||
/* Map for DMA, if required */
|
/* Map for DMA, if required */
|
||||||
if ( netdev->dma && ( ! dma_mapped ( &iobuf->map ) ) ) {
|
if ( netdev->dma && ( ! dma_mapped ( &iobuf->map ) ) ) {
|
||||||
if ( ( rc = iob_map_tx ( iobuf, netdev->dma ) ) != 0 )
|
if ( ( rc = iob_map_tx ( iobuf, netdev->dma ) ) != 0 )
|
||||||
goto err;
|
goto err_map;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Transmit packet */
|
/* Transmit packet */
|
||||||
if ( ( rc = netdev->op->transmit ( netdev, iobuf ) ) != 0 )
|
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 );
|
profile_stop ( &net_tx_profiler );
|
||||||
return 0;
|
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 );
|
netdev_tx_complete_err ( netdev, iobuf, rc );
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
@ -552,8 +567,18 @@ void netdev_rx_err ( struct net_device *netdev,
|
||||||
*/
|
*/
|
||||||
void netdev_poll ( struct net_device *netdev ) {
|
void netdev_poll ( struct net_device *netdev ) {
|
||||||
|
|
||||||
if ( netdev_is_open ( netdev ) )
|
/* Avoid calling poll() on unopened network devices */
|
||||||
netdev->op->poll ( netdev );
|
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