mirror of https://github.com/ipxe/ipxe.git
Add RX quotas to the net device poll() method. This avoids the problem
of alloc_pkb() exhaustion when e.g. an iSCSI-booted DOS session is left idle for a long time at the C:\ prompt and builds up a huge packet backlog.pull/1/head
parent
e822bc2a90
commit
c65fae2475
|
@ -364,7 +364,8 @@ static int undinet_transmit ( struct net_device *netdev,
|
||||||
/**
|
/**
|
||||||
* Poll for received packets
|
* Poll for received packets
|
||||||
*
|
*
|
||||||
* @v netdev Network device
|
* @v netdev Network device
|
||||||
|
* @v rx_quota Maximum number of packets to receive
|
||||||
*
|
*
|
||||||
* Fun, fun, fun. UNDI drivers don't use polling; they use
|
* Fun, fun, fun. UNDI drivers don't use polling; they use
|
||||||
* interrupts. We therefore cheat and pretend that an interrupt has
|
* interrupts. We therefore cheat and pretend that an interrupt has
|
||||||
|
@ -382,7 +383,7 @@ static int undinet_transmit ( struct net_device *netdev,
|
||||||
* of installing a genuine interrupt service routine and dealing with
|
* of installing a genuine interrupt service routine and dealing with
|
||||||
* the wonderful 8259 Programmable Interrupt Controller. Joy.
|
* the wonderful 8259 Programmable Interrupt Controller. Joy.
|
||||||
*/
|
*/
|
||||||
static void undinet_poll ( struct net_device *netdev ) {
|
static void undinet_poll ( struct net_device *netdev, unsigned int rx_quota ) {
|
||||||
struct undi_nic *undinic = netdev->priv;
|
struct undi_nic *undinic = netdev->priv;
|
||||||
struct s_PXENV_UNDI_ISR undi_isr;
|
struct s_PXENV_UNDI_ISR undi_isr;
|
||||||
struct pk_buff *pkb = NULL;
|
struct pk_buff *pkb = NULL;
|
||||||
|
@ -416,7 +417,7 @@ static void undinet_poll ( struct net_device *netdev ) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Run through the ISR loop */
|
/* Run through the ISR loop */
|
||||||
while ( 1 ) {
|
while ( rx_quota ) {
|
||||||
if ( ( rc = undinet_call ( undinic, PXENV_UNDI_ISR, &undi_isr,
|
if ( ( rc = undinet_call ( undinic, PXENV_UNDI_ISR, &undi_isr,
|
||||||
sizeof ( undi_isr ) ) ) != 0 )
|
sizeof ( undi_isr ) ) ) != 0 )
|
||||||
break;
|
break;
|
||||||
|
@ -448,6 +449,7 @@ static void undinet_poll ( struct net_device *netdev ) {
|
||||||
if ( pkb_len ( pkb ) == len ) {
|
if ( pkb_len ( pkb ) == len ) {
|
||||||
netdev_rx ( netdev, pkb );
|
netdev_rx ( netdev, pkb );
|
||||||
pkb = NULL;
|
pkb = NULL;
|
||||||
|
--rx_quota;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case PXENV_UNDI_ISR_OUT_DONE:
|
case PXENV_UNDI_ISR_OUT_DONE:
|
||||||
|
@ -480,8 +482,8 @@ static void undinet_poll ( struct net_device *netdev ) {
|
||||||
*/
|
*/
|
||||||
static int undinet_open ( struct net_device *netdev ) {
|
static int undinet_open ( struct net_device *netdev ) {
|
||||||
struct undi_nic *undinic = netdev->priv;
|
struct undi_nic *undinic = netdev->priv;
|
||||||
struct s_PXENV_UNDI_SET_STATION_ADDRESS set_address;
|
struct s_PXENV_UNDI_SET_STATION_ADDRESS undi_set_address;
|
||||||
struct s_PXENV_UNDI_OPEN open;
|
struct s_PXENV_UNDI_OPEN undi_open;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
/* Hook interrupt service routine and enable interrupt */
|
/* Hook interrupt service routine and enable interrupt */
|
||||||
|
@ -494,16 +496,16 @@ static int undinet_open ( struct net_device *netdev ) {
|
||||||
* use it to set the MAC address to the card's permanent value
|
* use it to set the MAC address to the card's permanent value
|
||||||
* anyway.
|
* anyway.
|
||||||
*/
|
*/
|
||||||
memcpy ( set_address.StationAddress, netdev->ll_addr,
|
memcpy ( undi_set_address.StationAddress, netdev->ll_addr,
|
||||||
sizeof ( set_address.StationAddress ) );
|
sizeof ( undi_set_address.StationAddress ) );
|
||||||
undinet_call ( undinic, PXENV_UNDI_SET_STATION_ADDRESS,
|
undinet_call ( undinic, PXENV_UNDI_SET_STATION_ADDRESS,
|
||||||
&set_address, sizeof ( set_address ) );
|
&undi_set_address, sizeof ( undi_set_address ) );
|
||||||
|
|
||||||
/* Open NIC */
|
/* Open NIC */
|
||||||
memset ( &open, 0, sizeof ( open ) );
|
memset ( &undi_open, 0, sizeof ( undi_open ) );
|
||||||
open.PktFilter = ( FLTR_DIRECTED | FLTR_BRDCST );
|
undi_open.PktFilter = ( FLTR_DIRECTED | FLTR_BRDCST );
|
||||||
if ( ( rc = undinet_call ( undinic, PXENV_UNDI_OPEN, &open,
|
if ( ( rc = undinet_call ( undinic, PXENV_UNDI_OPEN, &undi_open,
|
||||||
sizeof ( open ) ) ) != 0 )
|
sizeof ( undi_open ) ) ) != 0 )
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -520,14 +522,31 @@ static int undinet_open ( struct net_device *netdev ) {
|
||||||
*/
|
*/
|
||||||
static void undinet_close ( struct net_device *netdev ) {
|
static void undinet_close ( struct net_device *netdev ) {
|
||||||
struct undi_nic *undinic = netdev->priv;
|
struct undi_nic *undinic = netdev->priv;
|
||||||
struct s_PXENV_UNDI_CLOSE close;
|
struct s_PXENV_UNDI_ISR undi_isr;
|
||||||
|
struct s_PXENV_UNDI_CLOSE undi_close;
|
||||||
|
int rc;
|
||||||
|
|
||||||
/* Ensure ISR has exited cleanly */
|
/* Ensure ISR has exited cleanly */
|
||||||
while ( undinic->isr_processing )
|
while ( undinic->isr_processing ) {
|
||||||
undinet_poll ( netdev );
|
undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
|
||||||
|
if ( ( rc = undinet_call ( undinic, PXENV_UNDI_ISR, &undi_isr,
|
||||||
|
sizeof ( undi_isr ) ) ) != 0 )
|
||||||
|
break;
|
||||||
|
switch ( undi_isr.FuncFlag ) {
|
||||||
|
case PXENV_UNDI_ISR_OUT_TRANSMIT:
|
||||||
|
case PXENV_UNDI_ISR_OUT_RECEIVE:
|
||||||
|
/* Continue draining */
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* Stop processing */
|
||||||
|
undinic->isr_processing = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Close NIC */
|
/* Close NIC */
|
||||||
undinet_call ( undinic, PXENV_UNDI_CLOSE, &close, sizeof ( close ) );
|
undinet_call ( undinic, PXENV_UNDI_CLOSE, &undi_close,
|
||||||
|
sizeof ( undi_close ) );
|
||||||
|
|
||||||
/* Disable interrupt and unhook ISR */
|
/* Disable interrupt and unhook ISR */
|
||||||
disable_irq ( undinic->irq );
|
disable_irq ( undinic->irq );
|
||||||
|
|
|
@ -38,10 +38,13 @@ static int legacy_transmit ( struct net_device *netdev, struct pk_buff *pkb ) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void legacy_poll ( struct net_device *netdev ) {
|
static void legacy_poll ( struct net_device *netdev, unsigned int rx_quota ) {
|
||||||
struct nic *nic = netdev->priv;
|
struct nic *nic = netdev->priv;
|
||||||
struct pk_buff *pkb;
|
struct pk_buff *pkb;
|
||||||
|
|
||||||
|
if ( ! rx_quota )
|
||||||
|
return;
|
||||||
|
|
||||||
pkb = alloc_pkb ( ETH_FRAME_LEN );
|
pkb = alloc_pkb ( ETH_FRAME_LEN );
|
||||||
if ( ! pkb )
|
if ( ! pkb )
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -112,14 +112,14 @@ static int pnic_api_check ( uint16_t api_version ) {
|
||||||
/**************************************************************************
|
/**************************************************************************
|
||||||
POLL - Wait for a frame
|
POLL - Wait for a frame
|
||||||
***************************************************************************/
|
***************************************************************************/
|
||||||
static void pnic_poll ( struct net_device *netdev ) {
|
static void pnic_poll ( struct net_device *netdev, unsigned int rx_quota ) {
|
||||||
struct pnic *pnic = netdev->priv;
|
struct pnic *pnic = netdev->priv;
|
||||||
struct pk_buff *pkb;
|
struct pk_buff *pkb;
|
||||||
uint16_t length;
|
uint16_t length;
|
||||||
uint16_t qlen;
|
uint16_t qlen;
|
||||||
|
|
||||||
/* Fetch all available packets */
|
/* Fetch all available packets */
|
||||||
while ( 1 ) {
|
while ( rx_quota ) {
|
||||||
if ( pnic_command ( pnic, PNIC_CMD_RECV_QLEN, NULL, 0,
|
if ( pnic_command ( pnic, PNIC_CMD_RECV_QLEN, NULL, 0,
|
||||||
&qlen, sizeof ( qlen ), NULL )
|
&qlen, sizeof ( qlen ), NULL )
|
||||||
!= PNIC_STATUS_OK )
|
!= PNIC_STATUS_OK )
|
||||||
|
@ -139,6 +139,7 @@ static void pnic_poll ( struct net_device *netdev ) {
|
||||||
}
|
}
|
||||||
pkb_put ( pkb, length );
|
pkb_put ( pkb, length );
|
||||||
netdev_rx ( netdev, pkb );
|
netdev_rx ( netdev, pkb );
|
||||||
|
--rx_quota;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -413,8 +413,9 @@ static int rtl_transmit ( struct net_device *netdev, struct pk_buff *pkb ) {
|
||||||
* Poll for received packets
|
* Poll for received packets
|
||||||
*
|
*
|
||||||
* @v netdev Network device
|
* @v netdev Network device
|
||||||
|
* @v rx_quota Maximum number of packets to receive
|
||||||
*/
|
*/
|
||||||
static void rtl_poll ( struct net_device *netdev ) {
|
static void rtl_poll ( struct net_device *netdev, unsigned int rx_quota ) {
|
||||||
struct rtl8139_nic *rtl = netdev->priv;
|
struct rtl8139_nic *rtl = netdev->priv;
|
||||||
unsigned int status;
|
unsigned int status;
|
||||||
unsigned int tsad;
|
unsigned int tsad;
|
||||||
|
@ -441,7 +442,7 @@ static void rtl_poll ( struct net_device *netdev ) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handle received packets */
|
/* Handle received packets */
|
||||||
while ( ! ( inw ( rtl->ioaddr + ChipCmd ) & RxBufEmpty ) ) {
|
while ( rx_quota && ! ( inw ( rtl->ioaddr + ChipCmd ) & RxBufEmpty ) ){
|
||||||
rx_status = * ( ( uint16_t * )
|
rx_status = * ( ( uint16_t * )
|
||||||
( rtl->rx.ring + rtl->rx.offset ) );
|
( rtl->rx.ring + rtl->rx.offset ) );
|
||||||
rx_len = * ( ( uint16_t * )
|
rx_len = * ( ( uint16_t * )
|
||||||
|
|
|
@ -181,15 +181,16 @@ struct net_device {
|
||||||
/** Poll for received packet
|
/** Poll for received packet
|
||||||
*
|
*
|
||||||
* @v netdev Network device
|
* @v netdev Network device
|
||||||
|
* @v rx_quota Maximum number of packets to receive
|
||||||
*
|
*
|
||||||
* This method should cause the hardware to check for received
|
* This method should cause the hardware to check for received
|
||||||
* packets. Any received packets should be delivered via
|
* packets. Any received packets should be delivered via
|
||||||
* netdev_rx().
|
* netdev_rx(), up to a maximum of @c rx_quota packets.
|
||||||
*
|
*
|
||||||
* This method is guaranteed to be called only when the device
|
* This method is guaranteed to be called only when the device
|
||||||
* is open.
|
* is open.
|
||||||
*/
|
*/
|
||||||
void ( * poll ) ( struct net_device *netdev );
|
void ( * poll ) ( struct net_device *netdev, unsigned int rx_quota );
|
||||||
|
|
||||||
/** Link-layer protocol */
|
/** Link-layer protocol */
|
||||||
struct ll_protocol *ll_protocol;
|
struct ll_protocol *ll_protocol;
|
||||||
|
@ -238,7 +239,7 @@ extern int netdev_tx ( struct net_device *netdev, struct pk_buff *pkb );
|
||||||
void netdev_tx_complete ( struct net_device *netdev, struct pk_buff *pkb );
|
void netdev_tx_complete ( struct net_device *netdev, struct pk_buff *pkb );
|
||||||
void netdev_tx_complete_next ( struct net_device *netdev );
|
void netdev_tx_complete_next ( struct net_device *netdev );
|
||||||
extern void netdev_rx ( struct net_device *netdev, struct pk_buff *pkb );
|
extern void netdev_rx ( struct net_device *netdev, struct pk_buff *pkb );
|
||||||
extern int netdev_poll ( struct net_device *netdev );
|
extern int netdev_poll ( struct net_device *netdev, unsigned int rx_quota );
|
||||||
extern struct pk_buff * netdev_rx_dequeue ( struct net_device *netdev );
|
extern struct pk_buff * netdev_rx_dequeue ( struct net_device *netdev );
|
||||||
extern struct net_device * alloc_netdev ( size_t priv_size );
|
extern struct net_device * alloc_netdev ( size_t priv_size );
|
||||||
extern int register_netdev ( struct net_device *netdev );
|
extern int register_netdev ( struct net_device *netdev );
|
||||||
|
|
|
@ -126,16 +126,17 @@ void netdev_rx ( struct net_device *netdev, struct pk_buff *pkb ) {
|
||||||
* Poll for packet on network device
|
* Poll for packet on network device
|
||||||
*
|
*
|
||||||
* @v netdev Network device
|
* @v netdev Network device
|
||||||
|
* @v rx_quota Maximum number of packets to receive
|
||||||
* @ret True There are packets present in the receive queue
|
* @ret True There are packets present in the receive queue
|
||||||
* @ret False There are no packets present in the receive queue
|
* @ret False There are no packets present in the receive queue
|
||||||
*
|
*
|
||||||
* Polls the network device for received packets. Any received
|
* Polls the network device for received packets. Any received
|
||||||
* packets will be added to the RX packet queue via netdev_rx().
|
* packets will be added to the RX packet queue via netdev_rx().
|
||||||
*/
|
*/
|
||||||
int netdev_poll ( struct net_device *netdev ) {
|
int netdev_poll ( struct net_device *netdev, unsigned int rx_quota ) {
|
||||||
|
|
||||||
if ( netdev->state & NETDEV_OPEN )
|
if ( netdev->state & NETDEV_OPEN )
|
||||||
netdev->poll ( netdev );
|
netdev->poll ( netdev, rx_quota );
|
||||||
|
|
||||||
return ( ! list_empty ( &netdev->rx_queue ) );
|
return ( ! list_empty ( &netdev->rx_queue ) );
|
||||||
}
|
}
|
||||||
|
@ -351,14 +352,9 @@ int net_rx ( struct pk_buff *pkb, struct net_device *netdev,
|
||||||
*
|
*
|
||||||
* @v process Network stack process
|
* @v process Network stack process
|
||||||
*
|
*
|
||||||
* This polls all interfaces for any received packets, and processes
|
* This polls all interfaces for received packets, and processes
|
||||||
* at most one packet from the RX queue.
|
* packets from the RX queue.
|
||||||
*
|
*
|
||||||
* We avoid processing all received packets, because processing the
|
|
||||||
* received packet can trigger transmission of a new packet (e.g. an
|
|
||||||
* ARP response). Since TX completions will be processed as part of
|
|
||||||
* the poll operation, it is easy to overflow small TX queues if
|
|
||||||
* multiple packets are processed per poll.
|
|
||||||
*/
|
*/
|
||||||
static void net_step ( struct process *process ) {
|
static void net_step ( struct process *process ) {
|
||||||
struct net_device *netdev;
|
struct net_device *netdev;
|
||||||
|
@ -367,10 +363,28 @@ static void net_step ( struct process *process ) {
|
||||||
/* Poll and process each network device */
|
/* Poll and process each network device */
|
||||||
list_for_each_entry ( netdev, &net_devices, list ) {
|
list_for_each_entry ( netdev, &net_devices, list ) {
|
||||||
|
|
||||||
/* Poll for new packets */
|
/* Poll for new packets. Limit RX queue size to a
|
||||||
netdev_poll ( netdev );
|
* single packet, because otherwise most drivers are
|
||||||
|
* in serious danger of running out of memory and
|
||||||
|
* having to drop packets.
|
||||||
|
*
|
||||||
|
* This limitation isn't relevant to devices that
|
||||||
|
* preallocate packet buffers (i.e. devices with
|
||||||
|
* descriptor-based RX datapaths). We might at some
|
||||||
|
* point want to relax the quota for such devices.
|
||||||
|
*/
|
||||||
|
netdev_poll ( netdev,
|
||||||
|
( list_empty ( &netdev->rx_queue ) ? 1 : 0 ) );
|
||||||
|
|
||||||
/* Handle at most one received packet per poll */
|
/* Handle at most one received packet per poll. We
|
||||||
|
* avoid processing more than one packet per call to
|
||||||
|
* netdev_poll(), because processing the received
|
||||||
|
* packet can trigger transmission of a new packet
|
||||||
|
* (e.g. an ARP response). Since TX completions will
|
||||||
|
* be processed as part of the poll operation, it is
|
||||||
|
* easy to overflow small TX queues if multiple
|
||||||
|
* packets are processed per poll.
|
||||||
|
*/
|
||||||
if ( ( pkb = netdev_rx_dequeue ( netdev ) ) ) {
|
if ( ( pkb = netdev_rx_dequeue ( netdev ) ) ) {
|
||||||
DBGC ( netdev, "NETDEV %p processing %p\n",
|
DBGC ( netdev, "NETDEV %p processing %p\n",
|
||||||
netdev, pkb );
|
netdev, pkb );
|
||||||
|
|
Loading…
Reference in New Issue