summaryrefslogtreecommitdiffstats
path: root/src/arch/i386/drivers/net/undinet.c
diff options
context:
space:
mode:
authorMichael Brown2007-01-09 18:04:10 +0100
committerMichael Brown2007-01-09 18:04:10 +0100
commit3c2cc59d25e15dff61e5939046816d4a2fdc1043 (patch)
tree09aa9f53f0880d0c0457a0be7ba2e7ea67929e47 /src/arch/i386/drivers/net/undinet.c
parentSend EOI after enabling interrupt, in case the device had asserted IRQ (diff)
downloadipxe-3c2cc59d25e15dff61e5939046816d4a2fdc1043.tar.gz
ipxe-3c2cc59d25e15dff61e5939046816d4a2fdc1043.tar.xz
ipxe-3c2cc59d25e15dff61e5939046816d4a2fdc1043.zip
Added ability to break ISR processing over several calls to poll().
This will allow us to implement RX quotas.
Diffstat (limited to 'src/arch/i386/drivers/net/undinet.c')
-rw-r--r--src/arch/i386/drivers/net/undinet.c51
1 files changed, 34 insertions, 17 deletions
diff --git a/src/arch/i386/drivers/net/undinet.c b/src/arch/i386/drivers/net/undinet.c
index dc48b0eb..dc48c975 100644
--- a/src/arch/i386/drivers/net/undinet.c
+++ b/src/arch/i386/drivers/net/undinet.c
@@ -42,6 +42,8 @@ struct undi_nic {
SEGOFF16_t entry;
/** Assigned IRQ number */
unsigned int irq;
+ /** Currently processing ISR */
+ int isr_processing;
};
static void undinet_close ( struct net_device *netdev );
@@ -384,24 +386,32 @@ static void undinet_poll ( struct net_device *netdev ) {
size_t frag_len;
int rc;
- /* Do nothing unless ISR has been triggered */
- if ( ! undinet_isr_triggered() )
- return;
-
- /* See if this was our interrupt */
- memset ( &undi_isr, 0, sizeof ( undi_isr ) );
- undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_START;
- if ( ( rc = undinet_call ( undinic, PXENV_UNDI_ISR, &undi_isr,
- sizeof ( undi_isr ) ) ) != 0 )
- return;
- if ( undi_isr.FuncFlag != PXENV_UNDI_ISR_OUT_OURS )
- return;
-
- /* Send EOI */
- send_eoi ( undinic->irq );
+ if ( ! undinic->isr_processing ) {
+ /* Do nothing unless ISR has been triggered */
+ if ( ! undinet_isr_triggered() )
+ return;
+
+ /* See if this was our interrupt */
+ memset ( &undi_isr, 0, sizeof ( undi_isr ) );
+ undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_START;
+ if ( ( rc = undinet_call ( undinic, PXENV_UNDI_ISR, &undi_isr,
+ sizeof ( undi_isr ) ) ) != 0 )
+ return;
+ if ( undi_isr.FuncFlag != PXENV_UNDI_ISR_OUT_OURS )
+ return;
+
+ /* Send EOI */
+ send_eoi ( undinic->irq );
+
+ /* Start ISR processing */
+ undinic->isr_processing = 1;
+ undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_PROCESS;
+ } else {
+ /* Continue ISR processing */
+ undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
+ }
/* Run through the ISR loop */
- undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_PROCESS;
while ( 1 ) {
if ( ( rc = undinet_call ( undinic, PXENV_UNDI_ISR, &undi_isr,
sizeof ( undi_isr ) ) ) != 0 )
@@ -420,7 +430,8 @@ static void undinet_poll ( struct net_device *netdev ) {
DBGC ( undinic, "UNDINIC %p could not "
"allocate %zd bytes for RX buffer\n",
undinic, len );
- break;
+ /* Fragment will be dropped */
+ goto done;
}
if ( frag_len > pkb_available ( pkb ) ) {
DBGC ( undinic, "UNDINIC %p fragment too "
@@ -437,11 +448,13 @@ static void undinet_poll ( struct net_device *netdev ) {
break;
case PXENV_UNDI_ISR_OUT_DONE:
/* Processing complete */
+ undinic->isr_processing = 0;
goto done;
default:
/* Should never happen */
DBGC ( undinic, "UNDINIC %p ISR returned invalid "
"FuncFlag %04x\n", undinic, undi_isr.FuncFlag );
+ undinic->isr_processing = 0;
goto done;
}
undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
@@ -505,6 +518,10 @@ static void undinet_close ( struct net_device *netdev ) {
struct undi_nic *undinic = netdev->priv;
struct s_PXENV_UNDI_CLOSE close;
+ /* Ensure ISR has exited cleanly */
+ while ( undinic->isr_processing )
+ undinet_poll ( netdev );
+
/* Close NIC */
undinet_call ( undinic, PXENV_UNDI_CLOSE, &close, sizeof ( close ) );