summaryrefslogtreecommitdiffstats
path: root/src/arch/i386/drivers/net
diff options
context:
space:
mode:
authorMichael Brown2007-01-09 22:47:01 +0100
committerMichael Brown2007-01-09 22:47:01 +0100
commitc65fae2475ca652ef7948f286881b0c06bce861b (patch)
tree5588ec4b947ecc79201ee613d1cd0a0a7ca6d1d8 /src/arch/i386/drivers/net
parentAutopadding was sometimes overwriting the struct list_head at the end (diff)
downloadipxe-c65fae2475ca652ef7948f286881b0c06bce861b.tar.gz
ipxe-c65fae2475ca652ef7948f286881b0c06bce861b.tar.xz
ipxe-c65fae2475ca652ef7948f286881b0c06bce861b.zip
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.
Diffstat (limited to 'src/arch/i386/drivers/net')
-rw-r--r--src/arch/i386/drivers/net/undinet.c51
1 files changed, 35 insertions, 16 deletions
diff --git a/src/arch/i386/drivers/net/undinet.c b/src/arch/i386/drivers/net/undinet.c
index c53ff53cf..5ff9bd428 100644
--- a/src/arch/i386/drivers/net/undinet.c
+++ b/src/arch/i386/drivers/net/undinet.c
@@ -364,7 +364,8 @@ static int undinet_transmit ( struct net_device *netdev,
/**
* 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
* 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
* 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 s_PXENV_UNDI_ISR undi_isr;
struct pk_buff *pkb = NULL;
@@ -416,7 +417,7 @@ static void undinet_poll ( struct net_device *netdev ) {
}
/* Run through the ISR loop */
- while ( 1 ) {
+ while ( rx_quota ) {
if ( ( rc = undinet_call ( undinic, PXENV_UNDI_ISR, &undi_isr,
sizeof ( undi_isr ) ) ) != 0 )
break;
@@ -448,6 +449,7 @@ static void undinet_poll ( struct net_device *netdev ) {
if ( pkb_len ( pkb ) == len ) {
netdev_rx ( netdev, pkb );
pkb = NULL;
+ --rx_quota;
}
break;
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 ) {
struct undi_nic *undinic = netdev->priv;
- struct s_PXENV_UNDI_SET_STATION_ADDRESS set_address;
- struct s_PXENV_UNDI_OPEN open;
+ struct s_PXENV_UNDI_SET_STATION_ADDRESS undi_set_address;
+ struct s_PXENV_UNDI_OPEN undi_open;
int rc;
/* 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
* anyway.
*/
- memcpy ( set_address.StationAddress, netdev->ll_addr,
- sizeof ( set_address.StationAddress ) );
+ memcpy ( undi_set_address.StationAddress, netdev->ll_addr,
+ sizeof ( undi_set_address.StationAddress ) );
undinet_call ( undinic, PXENV_UNDI_SET_STATION_ADDRESS,
- &set_address, sizeof ( set_address ) );
+ &undi_set_address, sizeof ( undi_set_address ) );
/* Open NIC */
- memset ( &open, 0, sizeof ( open ) );
- open.PktFilter = ( FLTR_DIRECTED | FLTR_BRDCST );
- if ( ( rc = undinet_call ( undinic, PXENV_UNDI_OPEN, &open,
- sizeof ( open ) ) ) != 0 )
+ memset ( &undi_open, 0, sizeof ( undi_open ) );
+ undi_open.PktFilter = ( FLTR_DIRECTED | FLTR_BRDCST );
+ if ( ( rc = undinet_call ( undinic, PXENV_UNDI_OPEN, &undi_open,
+ sizeof ( undi_open ) ) ) != 0 )
goto err;
return 0;
@@ -520,14 +522,31 @@ static int undinet_open ( struct net_device *netdev ) {
*/
static void undinet_close ( struct net_device *netdev ) {
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 */
- while ( undinic->isr_processing )
- undinet_poll ( netdev );
+ while ( undinic->isr_processing ) {
+ 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 */
- 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_irq ( undinic->irq );