xen-netback: fix race between napi_complete() and interrupt handler
When the NAPI budget was not all used, xenvif_poll() would call napi_complete() /after/ enabling the interrupt. This resulted in a race between the napi_complete() and the napi_schedule() in the interrupt handler. The use of local_irq_save/restore() avoided by race iff the handler is running on the same CPU but not if it was running on a different CPU. Fix this properly by calling napi_complete() before reenabling interrupts (in the xenvif_napi_schedule_or_enable_irq() call). Signed-off-by: David Vrabel <david.vrabel@citrix.com> Acked-by: Wei Liu <wei.liu2@citrix.com> Acked-by: Ian Campbell <ian.campbell@citrix.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									202630b445
								
							
						
					
					
						commit
						0d08fceb2e
					
				| @ -226,7 +226,7 @@ int xenvif_map_frontend_rings(struct xenvif *vif, | ||||
| 			      grant_ref_t rx_ring_ref); | ||||
| 
 | ||||
| /* Check for SKBs from frontend and schedule backend processing */ | ||||
| void xenvif_check_rx_xenvif(struct xenvif *vif); | ||||
| void xenvif_napi_schedule_or_enable_events(struct xenvif *vif); | ||||
| 
 | ||||
| /* Prevent the device from generating any further traffic. */ | ||||
| void xenvif_carrier_off(struct xenvif *vif); | ||||
|  | ||||
| @ -75,32 +75,8 @@ static int xenvif_poll(struct napi_struct *napi, int budget) | ||||
| 	work_done = xenvif_tx_action(vif, budget); | ||||
| 
 | ||||
| 	if (work_done < budget) { | ||||
| 		int more_to_do = 0; | ||||
| 		unsigned long flags; | ||||
| 
 | ||||
| 		/* It is necessary to disable IRQ before calling
 | ||||
| 		 * RING_HAS_UNCONSUMED_REQUESTS. Otherwise we might | ||||
| 		 * lose event from the frontend. | ||||
| 		 * | ||||
| 		 * Consider: | ||||
| 		 *   RING_HAS_UNCONSUMED_REQUESTS | ||||
| 		 *   <frontend generates event to trigger napi_schedule> | ||||
| 		 *   __napi_complete | ||||
| 		 * | ||||
| 		 * This handler is still in scheduled state so the | ||||
| 		 * event has no effect at all. After __napi_complete | ||||
| 		 * this handler is descheduled and cannot get | ||||
| 		 * scheduled again. We lose event in this case and the ring | ||||
| 		 * will be completely stalled. | ||||
| 		 */ | ||||
| 
 | ||||
| 		local_irq_save(flags); | ||||
| 
 | ||||
| 		RING_FINAL_CHECK_FOR_REQUESTS(&vif->tx, more_to_do); | ||||
| 		if (!more_to_do) | ||||
| 			__napi_complete(napi); | ||||
| 
 | ||||
| 		local_irq_restore(flags); | ||||
| 		napi_complete(napi); | ||||
| 		xenvif_napi_schedule_or_enable_events(vif); | ||||
| 	} | ||||
| 
 | ||||
| 	return work_done; | ||||
| @ -194,7 +170,7 @@ static void xenvif_up(struct xenvif *vif) | ||||
| 	enable_irq(vif->tx_irq); | ||||
| 	if (vif->tx_irq != vif->rx_irq) | ||||
| 		enable_irq(vif->rx_irq); | ||||
| 	xenvif_check_rx_xenvif(vif); | ||||
| 	xenvif_napi_schedule_or_enable_events(vif); | ||||
| } | ||||
| 
 | ||||
| static void xenvif_down(struct xenvif *vif) | ||||
|  | ||||
| @ -716,7 +716,7 @@ done: | ||||
| 		notify_remote_via_irq(vif->rx_irq); | ||||
| } | ||||
| 
 | ||||
| void xenvif_check_rx_xenvif(struct xenvif *vif) | ||||
| void xenvif_napi_schedule_or_enable_events(struct xenvif *vif) | ||||
| { | ||||
| 	int more_to_do; | ||||
| 
 | ||||
| @ -750,7 +750,7 @@ static void tx_credit_callback(unsigned long data) | ||||
| { | ||||
| 	struct xenvif *vif = (struct xenvif *)data; | ||||
| 	tx_add_credit(vif); | ||||
| 	xenvif_check_rx_xenvif(vif); | ||||
| 	xenvif_napi_schedule_or_enable_events(vif); | ||||
| } | ||||
| 
 | ||||
| static void xenvif_tx_err(struct xenvif *vif, | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user