diff options
author | Santiago Leon <santil@us.ibm.com> | 2005-10-26 12:47:08 -0400 |
---|---|---|
committer | Jeff Garzik <jgarzik@pobox.com> | 2005-10-28 16:07:30 -0400 |
commit | e2adbcb480992de8a01acf9218e8bbd9b507fc6f (patch) | |
tree | 92465e753d0221c0c54862994735a7fe078200fa | |
parent | b6d35182fe62e57d368062adcc880ca35119d88e (diff) |
[PATCH] ibmveth fix buffer replenishing
This patch removes the allocation of RX skb's buffers from a workqueue
to be called directly at RX processing time. This change was suggested
by Dave Miller when the driver was starving the RX buffers and
deadlocking under heavy traffic:
> Allocating RX SKBs via tasklet is, IMHO, the worst way to
> do it. It is no surprise that there are starvation cases.
>
> If tasklets or work queues get delayed in any way, you lose,
> and it's very easy for a card to catch up with the driver RX'ing
> packets very fast, no matter how aggressive you make the
> replenishing. By the time you detect that you need to be
> "more aggressive" it is already too late.
> The only pseudo-reliable way is to allocate at RX processing time.
>
Signed-off-by: Santiago Leon <santil@us.ibm.com>
Signed-off-by: Jeff Garzik <jgarzik@pobox.com>
-rw-r--r-- | drivers/net/ibmveth.c | 48 | ||||
-rw-r--r-- | drivers/net/ibmveth.h | 4 |
2 files changed, 8 insertions, 44 deletions
diff --git a/drivers/net/ibmveth.c b/drivers/net/ibmveth.c index d985b804a762..aea1598b2253 100644 --- a/drivers/net/ibmveth.c +++ b/drivers/net/ibmveth.c | |||
@@ -96,7 +96,6 @@ static void ibmveth_proc_unregister_driver(void); | |||
96 | static void ibmveth_proc_register_adapter(struct ibmveth_adapter *adapter); | 96 | static void ibmveth_proc_register_adapter(struct ibmveth_adapter *adapter); |
97 | static void ibmveth_proc_unregister_adapter(struct ibmveth_adapter *adapter); | 97 | static void ibmveth_proc_unregister_adapter(struct ibmveth_adapter *adapter); |
98 | static irqreturn_t ibmveth_interrupt(int irq, void *dev_instance, struct pt_regs *regs); | 98 | static irqreturn_t ibmveth_interrupt(int irq, void *dev_instance, struct pt_regs *regs); |
99 | static inline void ibmveth_schedule_replenishing(struct ibmveth_adapter*); | ||
100 | static inline void ibmveth_rxq_harvest_buffer(struct ibmveth_adapter *adapter); | 99 | static inline void ibmveth_rxq_harvest_buffer(struct ibmveth_adapter *adapter); |
101 | 100 | ||
102 | #ifdef CONFIG_PROC_FS | 101 | #ifdef CONFIG_PROC_FS |
@@ -257,29 +256,7 @@ static void ibmveth_replenish_buffer_pool(struct ibmveth_adapter *adapter, struc | |||
257 | atomic_add(buffers_added, &(pool->available)); | 256 | atomic_add(buffers_added, &(pool->available)); |
258 | } | 257 | } |
259 | 258 | ||
260 | /* check if replenishing is needed. */ | 259 | /* replenish routine */ |
261 | static inline int ibmveth_is_replenishing_needed(struct ibmveth_adapter *adapter) | ||
262 | { | ||
263 | int i; | ||
264 | |||
265 | for(i = 0; i < IbmVethNumBufferPools; i++) | ||
266 | if(adapter->rx_buff_pool[i].active && | ||
267 | (atomic_read(&adapter->rx_buff_pool[i].available) < | ||
268 | adapter->rx_buff_pool[i].threshold)) | ||
269 | return 1; | ||
270 | return 0; | ||
271 | } | ||
272 | |||
273 | /* kick the replenish tasklet if we need replenishing and it isn't already running */ | ||
274 | static inline void ibmveth_schedule_replenishing(struct ibmveth_adapter *adapter) | ||
275 | { | ||
276 | if(ibmveth_is_replenishing_needed(adapter) && | ||
277 | (atomic_dec_if_positive(&adapter->not_replenishing) == 0)) { | ||
278 | schedule_work(&adapter->replenish_task); | ||
279 | } | ||
280 | } | ||
281 | |||
282 | /* replenish tasklet routine */ | ||
283 | static void ibmveth_replenish_task(struct ibmveth_adapter *adapter) | 260 | static void ibmveth_replenish_task(struct ibmveth_adapter *adapter) |
284 | { | 261 | { |
285 | int i; | 262 | int i; |
@@ -292,10 +269,6 @@ static void ibmveth_replenish_task(struct ibmveth_adapter *adapter) | |||
292 | &adapter->rx_buff_pool[i]); | 269 | &adapter->rx_buff_pool[i]); |
293 | 270 | ||
294 | adapter->rx_no_buffer = *(u64*)(((char*)adapter->buffer_list_addr) + 4096 - 8); | 271 | adapter->rx_no_buffer = *(u64*)(((char*)adapter->buffer_list_addr) + 4096 - 8); |
295 | |||
296 | atomic_inc(&adapter->not_replenishing); | ||
297 | |||
298 | ibmveth_schedule_replenishing(adapter); | ||
299 | } | 272 | } |
300 | 273 | ||
301 | /* empty and free ana buffer pool - also used to do cleanup in error paths */ | 274 | /* empty and free ana buffer pool - also used to do cleanup in error paths */ |
@@ -563,10 +536,10 @@ static int ibmveth_open(struct net_device *netdev) | |||
563 | return rc; | 536 | return rc; |
564 | } | 537 | } |
565 | 538 | ||
566 | netif_start_queue(netdev); | 539 | ibmveth_debug_printk("initial replenish cycle\n"); |
540 | ibmveth_replenish_task(adapter); | ||
567 | 541 | ||
568 | ibmveth_debug_printk("scheduling initial replenish cycle\n"); | 542 | netif_start_queue(netdev); |
569 | ibmveth_schedule_replenishing(adapter); | ||
570 | 543 | ||
571 | ibmveth_debug_printk("open complete\n"); | 544 | ibmveth_debug_printk("open complete\n"); |
572 | 545 | ||
@@ -584,9 +557,6 @@ static int ibmveth_close(struct net_device *netdev) | |||
584 | 557 | ||
585 | free_irq(netdev->irq, netdev); | 558 | free_irq(netdev->irq, netdev); |
586 | 559 | ||
587 | cancel_delayed_work(&adapter->replenish_task); | ||
588 | flush_scheduled_work(); | ||
589 | |||
590 | do { | 560 | do { |
591 | lpar_rc = h_free_logical_lan(adapter->vdev->unit_address); | 561 | lpar_rc = h_free_logical_lan(adapter->vdev->unit_address); |
592 | } while (H_isLongBusy(lpar_rc) || (lpar_rc == H_Busy)); | 562 | } while (H_isLongBusy(lpar_rc) || (lpar_rc == H_Busy)); |
@@ -795,7 +765,7 @@ static int ibmveth_poll(struct net_device *netdev, int *budget) | |||
795 | } | 765 | } |
796 | } while(more_work && (frames_processed < max_frames_to_process)); | 766 | } while(more_work && (frames_processed < max_frames_to_process)); |
797 | 767 | ||
798 | ibmveth_schedule_replenishing(adapter); | 768 | ibmveth_replenish_task(adapter); |
799 | 769 | ||
800 | if(more_work) { | 770 | if(more_work) { |
801 | /* more work to do - return that we are not done yet */ | 771 | /* more work to do - return that we are not done yet */ |
@@ -931,8 +901,10 @@ static int ibmveth_change_mtu(struct net_device *dev, int new_mtu) | |||
931 | 901 | ||
932 | } | 902 | } |
933 | 903 | ||
904 | /* kick the interrupt handler so that the new buffer pools get | ||
905 | replenished or deallocated */ | ||
906 | ibmveth_interrupt(dev->irq, dev, NULL); | ||
934 | 907 | ||
935 | ibmveth_schedule_replenishing(adapter); | ||
936 | dev->mtu = new_mtu; | 908 | dev->mtu = new_mtu; |
937 | return 0; | 909 | return 0; |
938 | } | 910 | } |
@@ -1017,14 +989,10 @@ static int __devinit ibmveth_probe(struct vio_dev *dev, const struct vio_device_ | |||
1017 | 989 | ||
1018 | ibmveth_debug_printk("adapter @ 0x%p\n", adapter); | 990 | ibmveth_debug_printk("adapter @ 0x%p\n", adapter); |
1019 | 991 | ||
1020 | INIT_WORK(&adapter->replenish_task, (void*)ibmveth_replenish_task, (void*)adapter); | ||
1021 | |||
1022 | adapter->buffer_list_dma = DMA_ERROR_CODE; | 992 | adapter->buffer_list_dma = DMA_ERROR_CODE; |
1023 | adapter->filter_list_dma = DMA_ERROR_CODE; | 993 | adapter->filter_list_dma = DMA_ERROR_CODE; |
1024 | adapter->rx_queue.queue_dma = DMA_ERROR_CODE; | 994 | adapter->rx_queue.queue_dma = DMA_ERROR_CODE; |
1025 | 995 | ||
1026 | atomic_set(&adapter->not_replenishing, 1); | ||
1027 | |||
1028 | ibmveth_debug_printk("registering netdev...\n"); | 996 | ibmveth_debug_printk("registering netdev...\n"); |
1029 | 997 | ||
1030 | rc = register_netdev(netdev); | 998 | rc = register_netdev(netdev); |
diff --git a/drivers/net/ibmveth.h b/drivers/net/ibmveth.h index a5d27a9cdf1f..a3ea02937d63 100644 --- a/drivers/net/ibmveth.h +++ b/drivers/net/ibmveth.h | |||
@@ -118,10 +118,6 @@ struct ibmveth_adapter { | |||
118 | dma_addr_t filter_list_dma; | 118 | dma_addr_t filter_list_dma; |
119 | struct ibmveth_buff_pool rx_buff_pool[IbmVethNumBufferPools]; | 119 | struct ibmveth_buff_pool rx_buff_pool[IbmVethNumBufferPools]; |
120 | struct ibmveth_rx_q rx_queue; | 120 | struct ibmveth_rx_q rx_queue; |
121 | atomic_t not_replenishing; | ||
122 | |||
123 | /* helper tasks */ | ||
124 | struct work_struct replenish_task; | ||
125 | 121 | ||
126 | /* adapter specific stats */ | 122 | /* adapter specific stats */ |
127 | u64 replenish_task_cycles; | 123 | u64 replenish_task_cycles; |