| HISTORY: | |
| February 16/2002 -- revision 0.2.1: | |
| COR typo corrected | |
| February 10/2002 -- revision 0.2: | |
| some spell checking ;-> | |
| January 12/2002 -- revision 0.1 | |
| This is still work in progress so may change. | |
| To keep up to date please watch this space. | |
| Introduction to NAPI | |
| ==================== | |
| NAPI is a proven (www.cyberus.ca/~hadi/usenix-paper.tgz) technique | |
| to improve network performance on Linux. For more details please | |
| read that paper. | |
| NAPI provides a "inherent mitigation" which is bound by system capacity | |
| as can be seen from the following data collected by Robert on Gigabit | |
| ethernet (e1000): | |
| Psize Ipps Tput Rxint Txint Done Ndone | |
| --------------------------------------------------------------- | |
| 60 890000 409362 17 27622 7 6823 | |
| 128 758150 464364 21 9301 10 7738 | |
| 256 445632 774646 42 15507 21 12906 | |
| 512 232666 994445 241292 19147 241192 1062 | |
| 1024 119061 1000003 872519 19258 872511 0 | |
| 1440 85193 1000003 946576 19505 946569 0 | |
| Legend: | |
| "Ipps" stands for input packets per second. | |
| "Tput" == packets out of total 1M that made it out. | |
| "txint" == transmit completion interrupts seen | |
| "Done" == The number of times that the poll() managed to pull all | |
| packets out of the rx ring. Note from this that the lower the | |
| load the more we could clean up the rxring | |
| "Ndone" == is the converse of "Done". Note again, that the higher | |
| the load the more times we couldn't clean up the rxring. | |
| Observe that: | |
| when the NIC receives 890Kpackets/sec only 17 rx interrupts are generated. | |
| The system cant handle the processing at 1 interrupt/packet at that load level. | |
| At lower rates on the other hand, rx interrupts go up and therefore the | |
| interrupt/packet ratio goes up (as observable from that table). So there is | |
| possibility that under low enough input, you get one poll call for each | |
| input packet caused by a single interrupt each time. And if the system | |
| cant handle interrupt per packet ratio of 1, then it will just have to | |
| chug along .... | |
| 0) Prerequisites: | |
| ================== | |
| A driver MAY continue using the old 2.4 technique for interfacing | |
| to the network stack and not benefit from the NAPI changes. | |
| NAPI additions to the kernel do not break backward compatibility. | |
| NAPI, however, requires the following features to be available: | |
| A) DMA ring or enough RAM to store packets in software devices. | |
| B) Ability to turn off interrupts or maybe events that send packets up | |
| the stack. | |
| NAPI processes packet events in what is known as dev->poll() method. | |
| Typically, only packet receive events are processed in dev->poll(). | |
| The rest of the events MAY be processed by the regular interrupt handler | |
| to reduce processing latency (justified also because there are not that | |
| many of them). | |
| Note, however, NAPI does not enforce that dev->poll() only processes | |
| receive events. | |
| Tests with the tulip driver indicated slightly increased latency if | |
| all of the interrupt handler is moved to dev->poll(). Also MII handling | |
| gets a little trickier. | |
| The example used in this document is to move the receive processing only | |
| to dev->poll(); this is shown with the patch for the tulip driver. | |
| For an example of code that moves all the interrupt driver to | |
| dev->poll() look at the ported e1000 code. | |
| There are caveats that might force you to go with moving everything to | |
| dev->poll(). Different NICs work differently depending on their status/event | |
| acknowledgement setup. | |
| There are two types of event register ACK mechanisms. | |
| I) what is known as Clear-on-read (COR). | |
| when you read the status/event register, it clears everything! | |
| The natsemi and sunbmac NICs are known to do this. | |
| In this case your only choice is to move all to dev->poll() | |
| II) Clear-on-write (COW) | |
| i) you clear the status by writing a 1 in the bit-location you want. | |
| These are the majority of the NICs and work the best with NAPI. | |
| Put only receive events in dev->poll(); leave the rest in | |
| the old interrupt handler. | |
| ii) whatever you write in the status register clears every thing ;-> | |
| Cant seem to find any supported by Linux which do this. If | |
| someone knows such a chip email us please. | |
| Move all to dev->poll() | |
| C) Ability to detect new work correctly. | |
| NAPI works by shutting down event interrupts when there's work and | |
| turning them on when there's none. | |
| New packets might show up in the small window while interrupts were being | |
| re-enabled (refer to appendix 2). A packet might sneak in during the period | |
| we are enabling interrupts. We only get to know about such a packet when the | |
| next new packet arrives and generates an interrupt. | |
| Essentially, there is a small window of opportunity for a race condition | |
| which for clarity we'll refer to as the "rotting packet". | |
| This is a very important topic and appendix 2 is dedicated for more | |
| discussion. | |
| Locking rules and environmental guarantees | |
| ========================================== | |
| -Guarantee: Only one CPU at any time can call dev->poll(); this is because | |
| only one CPU can pick the initial interrupt and hence the initial | |
| netif_rx_schedule(dev); | |
| - The core layer invokes devices to send packets in a round robin format. | |
| This implies receive is totally lockless because of the guarantee that only | |
| one CPU is executing it. | |
| - contention can only be the result of some other CPU accessing the rx | |
| ring. This happens only in close() and suspend() (when these methods | |
| try to clean the rx ring); | |
| ****guarantee: driver authors need not worry about this; synchronization | |
| is taken care for them by the top net layer. | |
| -local interrupts are enabled (if you dont move all to dev->poll()). For | |
| example link/MII and txcomplete continue functioning just same old way. | |
| This improves the latency of processing these events. It is also assumed that | |
| the receive interrupt is the largest cause of noise. Note this might not | |
| always be true. | |
| [according to Manfred Spraul, the winbond insists on sending one | |
| txmitcomplete interrupt for each packet (although this can be mitigated)]. | |
| For these broken drivers, move all to dev->poll(). | |
| For the rest of this text, we'll assume that dev->poll() only | |
| processes receive events. | |
| new methods introduce by NAPI | |
| ============================= | |
| a) netif_rx_schedule(dev) | |
| Called by an IRQ handler to schedule a poll for device | |
| b) netif_rx_schedule_prep(dev) | |
| puts the device in a state which allows for it to be added to the | |
| CPU polling list if it is up and running. You can look at this as | |
| the first half of netif_rx_schedule(dev) above; the second half | |
| being c) below. | |
| c) __netif_rx_schedule(dev) | |
| Add device to the poll list for this CPU; assuming that _prep above | |
| has already been called and returned 1. | |
| d) netif_rx_reschedule(dev, undo) | |
| Called to reschedule polling for device specifically for some | |
| deficient hardware. Read Appendix 2 for more details. | |
| e) netif_rx_complete(dev) | |
| Remove interface from the CPU poll list: it must be in the poll list | |
| on current cpu. This primitive is called by dev->poll(), when | |
| it completes its work. The device cannot be out of poll list at this | |
| call, if it is then clearly it is a BUG(). You'll know ;-> | |
| All of the above methods are used below, so keep reading for clarity. | |
| Device driver changes to be made when porting NAPI | |
| ================================================== | |
| Below we describe what kind of changes are required for NAPI to work. | |
| 1) introduction of dev->poll() method | |
| ===================================== | |
| This is the method that is invoked by the network core when it requests | |
| for new packets from the driver. A driver is allowed to send upto | |
| dev->quota packets by the current CPU before yielding to the network | |
| subsystem (so other devices can also get opportunity to send to the stack). | |
| dev->poll() prototype looks as follows: | |
| int my_poll(struct net_device *dev, int *budget) | |
| budget is the remaining number of packets the network subsystem on the | |
| current CPU can send up the stack before yielding to other system tasks. | |
| *Each driver is responsible for decrementing budget by the total number of | |
| packets sent. | |
| Total number of packets cannot exceed dev->quota. | |
| dev->poll() method is invoked by the top layer, the driver just sends if it | |
| can to the stack the packet quantity requested. | |
| more on dev->poll() below after the interrupt changes are explained. | |
| 2) registering dev->poll() method | |
| =================================== | |
| dev->poll should be set in the dev->probe() method. | |
| e.g: | |
| dev->open = my_open; | |
| . | |
| . | |
| /* two new additions */ | |
| /* first register my poll method */ | |
| dev->poll = my_poll; | |
| /* next register my weight/quanta; can be overridden in /proc */ | |
| dev->weight = 16; | |
| . | |
| . | |
| dev->stop = my_close; | |
| 3) scheduling dev->poll() | |
| ============================= | |
| This involves modifying the interrupt handler and the code | |
| path which takes the packet off the NIC and sends them to the | |
| stack. | |
| it's important at this point to introduce the classical D Becker | |
| interrupt processor: | |
| ------------------ | |
| static irqreturn_t | |
| netdevice_interrupt(int irq, void *dev_id, struct pt_regs *regs) | |
| { | |
| struct net_device *dev = (struct net_device *)dev_instance; | |
| struct my_private *tp = (struct my_private *)dev->priv; | |
| int work_count = my_work_count; | |
| status = read_interrupt_status_reg(); | |
| if (status == 0) | |
| return IRQ_NONE; /* Shared IRQ: not us */ | |
| if (status == 0xffff) | |
| return IRQ_HANDLED; /* Hot unplug */ | |
| if (status & error) | |
| do_some_error_handling() | |
| do { | |
| acknowledge_ints_ASAP(); | |
| if (status & link_interrupt) { | |
| spin_lock(&tp->link_lock); | |
| do_some_link_stat_stuff(); | |
| spin_lock(&tp->link_lock); | |
| } | |
| if (status & rx_interrupt) { | |
| receive_packets(dev); | |
| } | |
| if (status & rx_nobufs) { | |
| make_rx_buffs_avail(); | |
| } | |
| if (status & tx_related) { | |
| spin_lock(&tp->lock); | |
| tx_ring_free(dev); | |
| if (tx_died) | |
| restart_tx(); | |
| spin_unlock(&tp->lock); | |
| } | |
| status = read_interrupt_status_reg(); | |
| } while (!(status & error) || more_work_to_be_done); | |
| return IRQ_HANDLED; | |
| } | |
| ---------------------------------------------------------------------- | |
| We now change this to what is shown below to NAPI-enable it: | |
| ---------------------------------------------------------------------- | |
| static irqreturn_t | |
| netdevice_interrupt(int irq, void *dev_id, struct pt_regs *regs) | |
| { | |
| struct net_device *dev = (struct net_device *)dev_instance; | |
| struct my_private *tp = (struct my_private *)dev->priv; | |
| status = read_interrupt_status_reg(); | |
| if (status == 0) | |
| return IRQ_NONE; /* Shared IRQ: not us */ | |
| if (status == 0xffff) | |
| return IRQ_HANDLED; /* Hot unplug */ | |
| if (status & error) | |
| do_some_error_handling(); | |
| do { | |
| /************************ start note *********************************/ | |
| acknowledge_ints_ASAP(); // dont ack rx and rxnobuff here | |
| /************************ end note *********************************/ | |
| if (status & link_interrupt) { | |
| spin_lock(&tp->link_lock); | |
| do_some_link_stat_stuff(); | |
| spin_unlock(&tp->link_lock); | |
| } | |
| /************************ start note *********************************/ | |
| if (status & rx_interrupt || (status & rx_nobuffs)) { | |
| if (netif_rx_schedule_prep(dev)) { | |
| /* disable interrupts caused | |
| * by arriving packets */ | |
| disable_rx_and_rxnobuff_ints(); | |
| /* tell system we have work to be done. */ | |
| __netif_rx_schedule(dev); | |
| } else { | |
| printk("driver bug! interrupt while in poll\n"); | |
| /* FIX by disabling interrupts */ | |
| disable_rx_and_rxnobuff_ints(); | |
| } | |
| } | |
| /************************ end note note *********************************/ | |
| if (status & tx_related) { | |
| spin_lock(&tp->lock); | |
| tx_ring_free(dev); | |
| if (tx_died) | |
| restart_tx(); | |
| spin_unlock(&tp->lock); | |
| } | |
| status = read_interrupt_status_reg(); | |
| /************************ start note *********************************/ | |
| } while (!(status & error) || more_work_to_be_done(status)); | |
| /************************ end note note *********************************/ | |
| return IRQ_HANDLED; | |
| } | |
| --------------------------------------------------------------------- | |
| We note several things from above: | |
| I) Any interrupt source which is caused by arriving packets is now | |
| turned off when it occurs. Depending on the hardware, there could be | |
| several reasons that arriving packets would cause interrupts; these are the | |
| interrupt sources we wish to avoid. The two common ones are a) a packet | |
| arriving (rxint) b) a packet arriving and finding no DMA buffers available | |
| (rxnobuff) . | |
| This means also acknowledge_ints_ASAP() will not clear the status | |
| register for those two items above; clearing is done in the place where | |
| proper work is done within NAPI; at the poll() and refill_rx_ring() | |
| discussed further below. | |
| netif_rx_schedule_prep() returns 1 if device is in running state and | |
| gets successfully added to the core poll list. If we get a zero value | |
| we can _almost_ assume are already added to the list (instead of not running. | |
| Logic based on the fact that you shouldn't get interrupt if not running) | |
| We rectify this by disabling rx and rxnobuf interrupts. | |
| II) that receive_packets(dev) and make_rx_buffs_avail() may have disappeared. | |
| These functionalities are still around actually...... | |
| infact, receive_packets(dev) is very close to my_poll() and | |
| make_rx_buffs_avail() is invoked from my_poll() | |
| 4) converting receive_packets() to dev->poll() | |
| =============================================== | |
| We need to convert the classical D Becker receive_packets(dev) to my_poll() | |
| First the typical receive_packets() below: | |
| ------------------------------------------------------------------- | |
| /* this is called by interrupt handler */ | |
| static void receive_packets (struct net_device *dev) | |
| { | |
| struct my_private *tp = (struct my_private *)dev->priv; | |
| rx_ring = tp->rx_ring; | |
| cur_rx = tp->cur_rx; | |
| int entry = cur_rx % RX_RING_SIZE; | |
| int received = 0; | |
| int rx_work_limit = tp->dirty_rx + RX_RING_SIZE - tp->cur_rx; | |
| while (rx_ring_not_empty) { | |
| u32 rx_status; | |
| unsigned int rx_size; | |
| unsigned int pkt_size; | |
| struct sk_buff *skb; | |
| /* read size+status of next frame from DMA ring buffer */ | |
| /* the number 16 and 4 are just examples */ | |
| rx_status = le32_to_cpu (*(u32 *) (rx_ring + ring_offset)); | |
| rx_size = rx_status >> 16; | |
| pkt_size = rx_size - 4; | |
| /* process errors */ | |
| if ((rx_size > (MAX_ETH_FRAME_SIZE+4)) || | |
| (!(rx_status & RxStatusOK))) { | |
| netdrv_rx_err (rx_status, dev, tp, ioaddr); | |
| return; | |
| } | |
| if (--rx_work_limit < 0) | |
| break; | |
| /* grab a skb */ | |
| skb = dev_alloc_skb (pkt_size + 2); | |
| if (skb) { | |
| . | |
| . | |
| netif_rx (skb); | |
| . | |
| . | |
| } else { /* OOM */ | |
| /*seems very driver specific ... some just pass | |
| whatever is on the ring already. */ | |
| } | |
| /* move to the next skb on the ring */ | |
| entry = (++tp->cur_rx) % RX_RING_SIZE; | |
| received++ ; | |
| } | |
| /* store current ring pointer state */ | |
| tp->cur_rx = cur_rx; | |
| /* Refill the Rx ring buffers if they are needed */ | |
| refill_rx_ring(); | |
| . | |
| . | |
| } | |
| ------------------------------------------------------------------- | |
| We change it to a new one below; note the additional parameter in | |
| the call. | |
| ------------------------------------------------------------------- | |
| /* this is called by the network core */ | |
| static int my_poll (struct net_device *dev, int *budget) | |
| { | |
| struct my_private *tp = (struct my_private *)dev->priv; | |
| rx_ring = tp->rx_ring; | |
| cur_rx = tp->cur_rx; | |
| int entry = cur_rx % RX_BUF_LEN; | |
| /* maximum packets to send to the stack */ | |
| /************************ note note *********************************/ | |
| int rx_work_limit = dev->quota; | |
| /************************ end note note *********************************/ | |
| do { // outer beginning loop starts here | |
| clear_rx_status_register_bit(); | |
| while (rx_ring_not_empty) { | |
| u32 rx_status; | |
| unsigned int rx_size; | |
| unsigned int pkt_size; | |
| struct sk_buff *skb; | |
| /* read size+status of next frame from DMA ring buffer */ | |
| /* the number 16 and 4 are just examples */ | |
| rx_status = le32_to_cpu (*(u32 *) (rx_ring + ring_offset)); | |
| rx_size = rx_status >> 16; | |
| pkt_size = rx_size - 4; | |
| /* process errors */ | |
| if ((rx_size > (MAX_ETH_FRAME_SIZE+4)) || | |
| (!(rx_status & RxStatusOK))) { | |
| netdrv_rx_err (rx_status, dev, tp, ioaddr); | |
| return 1; | |
| } | |
| /************************ note note *********************************/ | |
| if (--rx_work_limit < 0) { /* we got packets, but no quota */ | |
| /* store current ring pointer state */ | |
| tp->cur_rx = cur_rx; | |
| /* Refill the Rx ring buffers if they are needed */ | |
| refill_rx_ring(dev); | |
| goto not_done; | |
| } | |
| /********************** end note **********************************/ | |
| /* grab a skb */ | |
| skb = dev_alloc_skb (pkt_size + 2); | |
| if (skb) { | |
| . | |
| . | |
| /************************ note note *********************************/ | |
| netif_receive_skb (skb); | |
| /********************** end note **********************************/ | |
| . | |
| . | |
| } else { /* OOM */ | |
| /*seems very driver specific ... common is just pass | |
| whatever is on the ring already. */ | |
| } | |
| /* move to the next skb on the ring */ | |
| entry = (++tp->cur_rx) % RX_RING_SIZE; | |
| received++ ; | |
| } | |
| /* store current ring pointer state */ | |
| tp->cur_rx = cur_rx; | |
| /* Refill the Rx ring buffers if they are needed */ | |
| refill_rx_ring(dev); | |
| /* no packets on ring; but new ones can arrive since we last | |
| checked */ | |
| status = read_interrupt_status_reg(); | |
| if (rx status is not set) { | |
| /* If something arrives in this narrow window, | |
| an interrupt will be generated */ | |
| goto done; | |
| } | |
| /* done! at least that's what it looks like ;-> | |
| if new packets came in after our last check on status bits | |
| they'll be caught by the while check and we go back and clear them | |
| since we havent exceeded our quota */ | |
| } while (rx_status_is_set); | |
| done: | |
| /************************ note note *********************************/ | |
| dev->quota -= received; | |
| *budget -= received; | |
| /* If RX ring is not full we are out of memory. */ | |
| if (tp->rx_buffers[tp->dirty_rx % RX_RING_SIZE].skb == NULL) | |
| goto oom; | |
| /* we are happy/done, no more packets on ring; put us back | |
| to where we can start processing interrupts again */ | |
| netif_rx_complete(dev); | |
| enable_rx_and_rxnobuf_ints(); | |
| /* The last op happens after poll completion. Which means the following: | |
| * 1. it can race with disabling irqs in irq handler (which are done to | |
| * schedule polls) | |
| * 2. it can race with dis/enabling irqs in other poll threads | |
| * 3. if an irq raised after the beginning of the outer beginning | |
| * loop (marked in the code above), it will be immediately | |
| * triggered here. | |
| * | |
| * Summarizing: the logic may result in some redundant irqs both | |
| * due to races in masking and due to too late acking of already | |
| * processed irqs. The good news: no events are ever lost. | |
| */ | |
| return 0; /* done */ | |
| not_done: | |
| if (tp->cur_rx - tp->dirty_rx > RX_RING_SIZE/2 || | |
| tp->rx_buffers[tp->dirty_rx % RX_RING_SIZE].skb == NULL) | |
| refill_rx_ring(dev); | |
| if (!received) { | |
| printk("received==0\n"); | |
| received = 1; | |
| } | |
| dev->quota -= received; | |
| *budget -= received; | |
| return 1; /* not_done */ | |
| oom: | |
| /* Start timer, stop polling, but do not enable rx interrupts. */ | |
| start_poll_timer(dev); | |
| return 0; /* we'll take it from here so tell core "done"*/ | |
| /************************ End note note *********************************/ | |
| } | |
| ------------------------------------------------------------------- | |
| From above we note that: | |
| 0) rx_work_limit = dev->quota | |
| 1) refill_rx_ring() is in charge of clearing the bit for rxnobuff when | |
| it does the work. | |
| 2) We have a done and not_done state. | |
| 3) instead of netif_rx() we call netif_receive_skb() to pass the skb. | |
| 4) we have a new way of handling oom condition | |
| 5) A new outer for (;;) loop has been added. This serves the purpose of | |
| ensuring that if a new packet has come in, after we are all set and done, | |
| and we have not exceeded our quota that we continue sending packets up. | |
| ----------------------------------------------------------- | |
| Poll timer code will need to do the following: | |
| a) | |
| if (tp->cur_rx - tp->dirty_rx > RX_RING_SIZE/2 || | |
| tp->rx_buffers[tp->dirty_rx % RX_RING_SIZE].skb == NULL) | |
| refill_rx_ring(dev); | |
| /* If RX ring is not full we are still out of memory. | |
| Restart the timer again. Else we re-add ourselves | |
| to the master poll list. | |
| */ | |
| if (tp->rx_buffers[tp->dirty_rx % RX_RING_SIZE].skb == NULL) | |
| restart_timer(); | |
| else netif_rx_schedule(dev); /* we are back on the poll list */ | |
| 5) dev->close() and dev->suspend() issues | |
| ========================================== | |
| The driver writer needn't worry about this; the top net layer takes | |
| care of it. | |
| 6) Adding new Stats to /proc | |
| ============================= | |
| In order to debug some of the new features, we introduce new stats | |
| that need to be collected. | |
| TODO: Fill this later. | |
| APPENDIX 1: discussion on using ethernet HW FC | |
| ============================================== | |
| Most chips with FC only send a pause packet when they run out of Rx buffers. | |
| Since packets are pulled off the DMA ring by a softirq in NAPI, | |
| if the system is slow in grabbing them and we have a high input | |
| rate (faster than the system's capacity to remove packets), then theoretically | |
| there will only be one rx interrupt for all packets during a given packetstorm. | |
| Under low load, we might have a single interrupt per packet. | |
| FC should be programmed to apply in the case when the system cant pull out | |
| packets fast enough i.e send a pause only when you run out of rx buffers. | |
| Note FC in itself is a good solution but we have found it to not be | |
| much of a commodity feature (both in NICs and switches) and hence falls | |
| under the same category as using NIC based mitigation. Also, experiments | |
| indicate that it's much harder to resolve the resource allocation | |
| issue (aka lazy receiving that NAPI offers) and hence quantify its usefulness | |
| proved harder. In any case, FC works even better with NAPI but is not | |
| necessary. | |
| APPENDIX 2: the "rotting packet" race-window avoidance scheme | |
| ============================================================= | |
| There are two types of associations seen here | |
| 1) status/int which honors level triggered IRQ | |
| If a status bit for receive or rxnobuff is set and the corresponding | |
| interrupt-enable bit is not on, then no interrupts will be generated. However, | |
| as soon as the "interrupt-enable" bit is unmasked, an immediate interrupt is | |
| generated. [assuming the status bit was not turned off]. | |
| Generally the concept of level triggered IRQs in association with a status and | |
| interrupt-enable CSR register set is used to avoid the race. | |
| If we take the example of the tulip: | |
| "pending work" is indicated by the status bit(CSR5 in tulip). | |
| the corresponding interrupt bit (CSR7 in tulip) might be turned off (but | |
| the CSR5 will continue to be turned on with new packet arrivals even if | |
| we clear it the first time) | |
| Very important is the fact that if we turn on the interrupt bit on when | |
| status is set that an immediate irq is triggered. | |
| If we cleared the rx ring and proclaimed there was "no more work | |
| to be done" and then went on to do a few other things; then when we enable | |
| interrupts, there is a possibility that a new packet might sneak in during | |
| this phase. It helps to look at the pseudo code for the tulip poll | |
| routine: | |
| -------------------------- | |
| do { | |
| ACK; | |
| while (ring_is_not_empty()) { | |
| work-work-work | |
| if quota is exceeded: exit, no touching irq status/mask | |
| } | |
| /* No packets, but new can arrive while we are doing this*/ | |
| CSR5 := read | |
| if (CSR5 is not set) { | |
| /* If something arrives in this narrow window here, | |
| * where the comments are ;-> irq will be generated */ | |
| unmask irqs; | |
| exit poll; | |
| } | |
| } while (rx_status_is_set); | |
| ------------------------ | |
| CSR5 bit of interest is only the rx status. | |
| If you look at the last if statement: | |
| you just finished grabbing all the packets from the rx ring .. you check if | |
| status bit says there are more packets just in ... it says none; you then | |
| enable rx interrupts again; if a new packet just came in during this check, | |
| we are counting that CSR5 will be set in that small window of opportunity | |
| and that by re-enabling interrupts, we would actually trigger an interrupt | |
| to register the new packet for processing. | |
| [The above description nay be very verbose, if you have better wording | |
| that will make this more understandable, please suggest it.] | |
| 2) non-capable hardware | |
| These do not generally respect level triggered IRQs. Normally, | |
| irqs may be lost while being masked and the only way to leave poll is to do | |
| a double check for new input after netif_rx_complete() is invoked | |
| and re-enable polling (after seeing this new input). | |
| Sample code: | |
| --------- | |
| . | |
| . | |
| restart_poll: | |
| while (ring_is_not_empty()) { | |
| work-work-work | |
| if quota is exceeded: exit, not touching irq status/mask | |
| } | |
| . | |
| . | |
| . | |
| enable_rx_interrupts() | |
| netif_rx_complete(dev); | |
| if (ring_has_new_packet() && netif_rx_reschedule(dev, received)) { | |
| disable_rx_and_rxnobufs() | |
| goto restart_poll | |
| } while (rx_status_is_set); | |
| --------- | |
| Basically netif_rx_complete() removes us from the poll list, but because a | |
| new packet which will never be caught due to the possibility of a race | |
| might come in, we attempt to re-add ourselves to the poll list. | |
| APPENDIX 3: Scheduling issues. | |
| ============================== | |
| As seen NAPI moves processing to softirq level. Linux uses the ksoftirqd as the | |
| general solution to schedule softirq's to run before next interrupt and by putting | |
| them under scheduler control. Also this prevents consecutive softirq's from | |
| monopolize the CPU. This also have the effect that the priority of ksoftirq needs | |
| to be considered when running very CPU-intensive applications and networking to | |
| get the proper balance of softirq/user balance. Increasing ksoftirq priority to 0 | |
| (eventually more) is reported cure problems with low network performance at high | |
| CPU load. | |
| Most used processes in a GIGE router: | |
| USER PID %CPU %MEM SIZE RSS TTY STAT START TIME COMMAND | |
| root 3 0.2 0.0 0 0 ? RWN Aug 15 602:00 (ksoftirqd_CPU0) | |
| root 232 0.0 7.9 41400 40884 ? S Aug 15 74:12 gated | |
| -------------------------------------------------------------------- | |
| relevant sites: | |
| ================== | |
| ftp://robur.slu.se/pub/Linux/net-development/NAPI/ | |
| -------------------------------------------------------------------- | |
| TODO: Write net-skeleton.c driver. | |
| ------------------------------------------------------------- | |
| Authors: | |
| ======== | |
| Alexey Kuznetsov <kuznet@ms2.inr.ac.ru> | |
| Jamal Hadi Salim <hadi@cyberus.ca> | |
| Robert Olsson <Robert.Olsson@data.slu.se> | |
| Acknowledgements: | |
| ================ | |
| People who made this document better: | |
| Lennert Buytenhek <buytenh@gnu.org> | |
| Andrew Morton <akpm@zip.com.au> | |
| Manfred Spraul <manfred@colorfullife.com> | |
| Donald Becker <becker@scyld.com> | |
| Jeff Garzik <jgarzik@pobox.com> |