forked from mirrors/linux
		
	net: protect NAPI enablement with netdev_lock()
Wrap napi_enable() / napi_disable() with netdev_lock(). Provide the "already locked" flavor of the API. iavf needs the usual adjustment. A number of drivers call napi_enable() under a spin lock, so they have to be modified to take netdev_lock() first, then spin lock then call napi_enable_locked(). Protecting napi_enable() implies that napi->napi_id is protected by netdev_lock(). Acked-by: Francois Romieu <romieu@fr.zoreil.com> # via-velocity Reviewed-by: Eric Dumazet <edumazet@google.com> Reviewed-by: Kuniyuki Iwashima <kuniyu@amazon.com> Link: https://patch.msgid.link/20250115035319.559603-7-kuba@kernel.org Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
		
							parent
							
								
									1b23cdbd2b
								
							
						
					
					
						commit
						413f0271f3
					
				
					 6 changed files with 56 additions and 22 deletions
				
			
		|  | @ -462,7 +462,7 @@ static void pcnet32_netif_start(struct net_device *dev) | ||||||
| 	val = lp->a->read_csr(ioaddr, CSR3); | 	val = lp->a->read_csr(ioaddr, CSR3); | ||||||
| 	val &= 0x00ff; | 	val &= 0x00ff; | ||||||
| 	lp->a->write_csr(ioaddr, CSR3, val); | 	lp->a->write_csr(ioaddr, CSR3, val); | ||||||
| 	napi_enable(&lp->napi); | 	napi_enable_locked(&lp->napi); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /*
 | /*
 | ||||||
|  | @ -889,6 +889,7 @@ static int pcnet32_set_ringparam(struct net_device *dev, | ||||||
| 	if (netif_running(dev)) | 	if (netif_running(dev)) | ||||||
| 		pcnet32_netif_stop(dev); | 		pcnet32_netif_stop(dev); | ||||||
| 
 | 
 | ||||||
|  | 	netdev_lock(dev); | ||||||
| 	spin_lock_irqsave(&lp->lock, flags); | 	spin_lock_irqsave(&lp->lock, flags); | ||||||
| 	lp->a->write_csr(ioaddr, CSR0, CSR0_STOP);	/* stop the chip */ | 	lp->a->write_csr(ioaddr, CSR0, CSR0_STOP);	/* stop the chip */ | ||||||
| 
 | 
 | ||||||
|  | @ -920,6 +921,7 @@ static int pcnet32_set_ringparam(struct net_device *dev, | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	spin_unlock_irqrestore(&lp->lock, flags); | 	spin_unlock_irqrestore(&lp->lock, flags); | ||||||
|  | 	netdev_unlock(dev); | ||||||
| 
 | 
 | ||||||
| 	netif_info(lp, drv, dev, "Ring Param Settings: RX: %d, TX: %d\n", | 	netif_info(lp, drv, dev, "Ring Param Settings: RX: %d, TX: %d\n", | ||||||
| 		   lp->rx_ring_size, lp->tx_ring_size); | 		   lp->rx_ring_size, lp->tx_ring_size); | ||||||
|  | @ -985,6 +987,7 @@ static int pcnet32_loopback_test(struct net_device *dev, uint64_t * data1) | ||||||
| 	if (netif_running(dev)) | 	if (netif_running(dev)) | ||||||
| 		pcnet32_netif_stop(dev); | 		pcnet32_netif_stop(dev); | ||||||
| 
 | 
 | ||||||
|  | 	netdev_lock(dev); | ||||||
| 	spin_lock_irqsave(&lp->lock, flags); | 	spin_lock_irqsave(&lp->lock, flags); | ||||||
| 	lp->a->write_csr(ioaddr, CSR0, CSR0_STOP);	/* stop the chip */ | 	lp->a->write_csr(ioaddr, CSR0, CSR0_STOP);	/* stop the chip */ | ||||||
| 
 | 
 | ||||||
|  | @ -1122,6 +1125,7 @@ static int pcnet32_loopback_test(struct net_device *dev, uint64_t * data1) | ||||||
| 		lp->a->write_bcr(ioaddr, 20, 4);	/* return to 16bit mode */ | 		lp->a->write_bcr(ioaddr, 20, 4);	/* return to 16bit mode */ | ||||||
| 	} | 	} | ||||||
| 	spin_unlock_irqrestore(&lp->lock, flags); | 	spin_unlock_irqrestore(&lp->lock, flags); | ||||||
|  | 	netdev_unlock(dev); | ||||||
| 
 | 
 | ||||||
| 	return rc; | 	return rc; | ||||||
| }				/* end pcnet32_loopback_test  */ | }				/* end pcnet32_loopback_test  */ | ||||||
|  | @ -2101,6 +2105,7 @@ static int pcnet32_open(struct net_device *dev) | ||||||
| 		return -EAGAIN; | 		return -EAGAIN; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	netdev_lock(dev); | ||||||
| 	spin_lock_irqsave(&lp->lock, flags); | 	spin_lock_irqsave(&lp->lock, flags); | ||||||
| 	/* Check for a valid station address */ | 	/* Check for a valid station address */ | ||||||
| 	if (!is_valid_ether_addr(dev->dev_addr)) { | 	if (!is_valid_ether_addr(dev->dev_addr)) { | ||||||
|  | @ -2266,7 +2271,7 @@ static int pcnet32_open(struct net_device *dev) | ||||||
| 		goto err_free_ring; | 		goto err_free_ring; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	napi_enable(&lp->napi); | 	napi_enable_locked(&lp->napi); | ||||||
| 
 | 
 | ||||||
| 	/* Re-initialize the PCNET32, and start it when done. */ | 	/* Re-initialize the PCNET32, and start it when done. */ | ||||||
| 	lp->a->write_csr(ioaddr, 1, (lp->init_dma_addr & 0xffff)); | 	lp->a->write_csr(ioaddr, 1, (lp->init_dma_addr & 0xffff)); | ||||||
|  | @ -2300,6 +2305,7 @@ static int pcnet32_open(struct net_device *dev) | ||||||
| 		     lp->a->read_csr(ioaddr, CSR0)); | 		     lp->a->read_csr(ioaddr, CSR0)); | ||||||
| 
 | 
 | ||||||
| 	spin_unlock_irqrestore(&lp->lock, flags); | 	spin_unlock_irqrestore(&lp->lock, flags); | ||||||
|  | 	netdev_unlock(dev); | ||||||
| 
 | 
 | ||||||
| 	return 0;		/* Always succeed */ | 	return 0;		/* Always succeed */ | ||||||
| 
 | 
 | ||||||
|  | @ -2315,6 +2321,7 @@ static int pcnet32_open(struct net_device *dev) | ||||||
| 
 | 
 | ||||||
| err_free_irq: | err_free_irq: | ||||||
| 	spin_unlock_irqrestore(&lp->lock, flags); | 	spin_unlock_irqrestore(&lp->lock, flags); | ||||||
|  | 	netdev_unlock(dev); | ||||||
| 	free_irq(dev->irq, dev); | 	free_irq(dev->irq, dev); | ||||||
| 	return rc; | 	return rc; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1180,7 +1180,7 @@ static void iavf_napi_enable_all(struct iavf_adapter *adapter) | ||||||
| 
 | 
 | ||||||
| 		q_vector = &adapter->q_vectors[q_idx]; | 		q_vector = &adapter->q_vectors[q_idx]; | ||||||
| 		napi = &q_vector->napi; | 		napi = &q_vector->napi; | ||||||
| 		napi_enable(napi); | 		napi_enable_locked(napi); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -1196,7 +1196,7 @@ static void iavf_napi_disable_all(struct iavf_adapter *adapter) | ||||||
| 
 | 
 | ||||||
| 	for (q_idx = 0; q_idx < q_vectors; q_idx++) { | 	for (q_idx = 0; q_idx < q_vectors; q_idx++) { | ||||||
| 		q_vector = &adapter->q_vectors[q_idx]; | 		q_vector = &adapter->q_vectors[q_idx]; | ||||||
| 		napi_disable(&q_vector->napi); | 		napi_disable_locked(&q_vector->napi); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -4392,6 +4392,7 @@ static int mvneta_cpu_online(unsigned int cpu, struct hlist_node *node) | ||||||
| 	if (pp->neta_armada3700) | 	if (pp->neta_armada3700) | ||||||
| 		return 0; | 		return 0; | ||||||
| 
 | 
 | ||||||
|  | 	netdev_lock(port->napi.dev); | ||||||
| 	spin_lock(&pp->lock); | 	spin_lock(&pp->lock); | ||||||
| 	/*
 | 	/*
 | ||||||
| 	 * Configuring the driver for a new CPU while the driver is | 	 * Configuring the driver for a new CPU while the driver is | ||||||
|  | @ -4418,7 +4419,7 @@ static int mvneta_cpu_online(unsigned int cpu, struct hlist_node *node) | ||||||
| 
 | 
 | ||||||
| 	/* Mask all ethernet port interrupts */ | 	/* Mask all ethernet port interrupts */ | ||||||
| 	on_each_cpu(mvneta_percpu_mask_interrupt, pp, true); | 	on_each_cpu(mvneta_percpu_mask_interrupt, pp, true); | ||||||
| 	napi_enable(&port->napi); | 	napi_enable_locked(&port->napi); | ||||||
| 
 | 
 | ||||||
| 	/*
 | 	/*
 | ||||||
| 	 * Enable per-CPU interrupts on the CPU that is | 	 * Enable per-CPU interrupts on the CPU that is | ||||||
|  | @ -4439,6 +4440,8 @@ static int mvneta_cpu_online(unsigned int cpu, struct hlist_node *node) | ||||||
| 		    MVNETA_CAUSE_LINK_CHANGE); | 		    MVNETA_CAUSE_LINK_CHANGE); | ||||||
| 	netif_tx_start_all_queues(pp->dev); | 	netif_tx_start_all_queues(pp->dev); | ||||||
| 	spin_unlock(&pp->lock); | 	spin_unlock(&pp->lock); | ||||||
|  | 	netdev_unlock(port->napi.dev); | ||||||
|  | 
 | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -2320,7 +2320,8 @@ static int velocity_change_mtu(struct net_device *dev, int new_mtu) | ||||||
| 		if (ret < 0) | 		if (ret < 0) | ||||||
| 			goto out_free_tmp_vptr_1; | 			goto out_free_tmp_vptr_1; | ||||||
| 
 | 
 | ||||||
| 		napi_disable(&vptr->napi); | 		netdev_lock(dev); | ||||||
|  | 		napi_disable_locked(&vptr->napi); | ||||||
| 
 | 
 | ||||||
| 		spin_lock_irqsave(&vptr->lock, flags); | 		spin_lock_irqsave(&vptr->lock, flags); | ||||||
| 
 | 
 | ||||||
|  | @ -2342,12 +2343,13 @@ static int velocity_change_mtu(struct net_device *dev, int new_mtu) | ||||||
| 
 | 
 | ||||||
| 		velocity_give_many_rx_descs(vptr); | 		velocity_give_many_rx_descs(vptr); | ||||||
| 
 | 
 | ||||||
| 		napi_enable(&vptr->napi); | 		napi_enable_locked(&vptr->napi); | ||||||
| 
 | 
 | ||||||
| 		mac_enable_int(vptr->mac_regs); | 		mac_enable_int(vptr->mac_regs); | ||||||
| 		netif_start_queue(dev); | 		netif_start_queue(dev); | ||||||
| 
 | 
 | ||||||
| 		spin_unlock_irqrestore(&vptr->lock, flags); | 		spin_unlock_irqrestore(&vptr->lock, flags); | ||||||
|  | 		netdev_unlock(dev); | ||||||
| 
 | 
 | ||||||
| 		velocity_free_rings(tmp_vptr); | 		velocity_free_rings(tmp_vptr); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -382,7 +382,7 @@ struct napi_struct { | ||||||
| 	struct sk_buff		*skb; | 	struct sk_buff		*skb; | ||||||
| 	struct list_head	rx_list; /* Pending GRO_NORMAL skbs */ | 	struct list_head	rx_list; /* Pending GRO_NORMAL skbs */ | ||||||
| 	int			rx_count; /* length of rx_list */ | 	int			rx_count; /* length of rx_list */ | ||||||
| 	unsigned int		napi_id; | 	unsigned int		napi_id; /* protected by netdev_lock */ | ||||||
| 	struct hrtimer		timer; | 	struct hrtimer		timer; | ||||||
| 	struct task_struct	*thread; | 	struct task_struct	*thread; | ||||||
| 	unsigned long		gro_flush_timeout; | 	unsigned long		gro_flush_timeout; | ||||||
|  | @ -570,16 +570,11 @@ static inline bool napi_complete(struct napi_struct *n) | ||||||
| 
 | 
 | ||||||
| int dev_set_threaded(struct net_device *dev, bool threaded); | int dev_set_threaded(struct net_device *dev, bool threaded); | ||||||
| 
 | 
 | ||||||
| /**
 |  | ||||||
|  *	napi_disable - prevent NAPI from scheduling |  | ||||||
|  *	@n: NAPI context |  | ||||||
|  * |  | ||||||
|  * Stop NAPI from being scheduled on this context. |  | ||||||
|  * Waits till any outstanding processing completes. |  | ||||||
|  */ |  | ||||||
| void napi_disable(struct napi_struct *n); | void napi_disable(struct napi_struct *n); | ||||||
|  | void napi_disable_locked(struct napi_struct *n); | ||||||
| 
 | 
 | ||||||
| void napi_enable(struct napi_struct *n); | void napi_enable(struct napi_struct *n); | ||||||
|  | void napi_enable_locked(struct napi_struct *n); | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  *	napi_synchronize - wait until NAPI is not running |  *	napi_synchronize - wait until NAPI is not running | ||||||
|  |  | ||||||
|  | @ -6958,11 +6958,13 @@ void netif_napi_add_weight_locked(struct net_device *dev, | ||||||
| } | } | ||||||
| EXPORT_SYMBOL(netif_napi_add_weight_locked); | EXPORT_SYMBOL(netif_napi_add_weight_locked); | ||||||
| 
 | 
 | ||||||
| void napi_disable(struct napi_struct *n) | void napi_disable_locked(struct napi_struct *n) | ||||||
| { | { | ||||||
| 	unsigned long val, new; | 	unsigned long val, new; | ||||||
| 
 | 
 | ||||||
| 	might_sleep(); | 	might_sleep(); | ||||||
|  | 	netdev_assert_locked(n->dev); | ||||||
|  | 
 | ||||||
| 	set_bit(NAPI_STATE_DISABLE, &n->state); | 	set_bit(NAPI_STATE_DISABLE, &n->state); | ||||||
| 
 | 
 | ||||||
| 	val = READ_ONCE(n->state); | 	val = READ_ONCE(n->state); | ||||||
|  | @ -6985,16 +6987,25 @@ void napi_disable(struct napi_struct *n) | ||||||
| 
 | 
 | ||||||
| 	clear_bit(NAPI_STATE_DISABLE, &n->state); | 	clear_bit(NAPI_STATE_DISABLE, &n->state); | ||||||
| } | } | ||||||
| EXPORT_SYMBOL(napi_disable); | EXPORT_SYMBOL(napi_disable_locked); | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  *	napi_enable - enable NAPI scheduling |  * napi_disable() - prevent NAPI from scheduling | ||||||
|  *	@n: NAPI context |  * @n: NAPI context | ||||||
|  * |  * | ||||||
|  * Resume NAPI from being scheduled on this context. |  * Stop NAPI from being scheduled on this context. | ||||||
|  * Must be paired with napi_disable. |  * Waits till any outstanding processing completes. | ||||||
|  |  * Takes netdev_lock() for associated net_device. | ||||||
|  */ |  */ | ||||||
| void napi_enable(struct napi_struct *n) | void napi_disable(struct napi_struct *n) | ||||||
|  | { | ||||||
|  | 	netdev_lock(n->dev); | ||||||
|  | 	napi_disable_locked(n); | ||||||
|  | 	netdev_unlock(n->dev); | ||||||
|  | } | ||||||
|  | EXPORT_SYMBOL(napi_disable); | ||||||
|  | 
 | ||||||
|  | void napi_enable_locked(struct napi_struct *n) | ||||||
| { | { | ||||||
| 	unsigned long new, val = READ_ONCE(n->state); | 	unsigned long new, val = READ_ONCE(n->state); | ||||||
| 
 | 
 | ||||||
|  | @ -7011,6 +7022,22 @@ void napi_enable(struct napi_struct *n) | ||||||
| 			new |= NAPIF_STATE_THREADED; | 			new |= NAPIF_STATE_THREADED; | ||||||
| 	} while (!try_cmpxchg(&n->state, &val, new)); | 	} while (!try_cmpxchg(&n->state, &val, new)); | ||||||
| } | } | ||||||
|  | EXPORT_SYMBOL(napi_enable_locked); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * napi_enable() - enable NAPI scheduling | ||||||
|  |  * @n: NAPI context | ||||||
|  |  * | ||||||
|  |  * Enable scheduling of a NAPI instance. | ||||||
|  |  * Must be paired with napi_disable(). | ||||||
|  |  * Takes netdev_lock() for associated net_device. | ||||||
|  |  */ | ||||||
|  | void napi_enable(struct napi_struct *n) | ||||||
|  | { | ||||||
|  | 	netdev_lock(n->dev); | ||||||
|  | 	napi_enable_locked(n); | ||||||
|  | 	netdev_unlock(n->dev); | ||||||
|  | } | ||||||
| EXPORT_SYMBOL(napi_enable); | EXPORT_SYMBOL(napi_enable); | ||||||
| 
 | 
 | ||||||
| static void flush_gro_hash(struct napi_struct *napi) | static void flush_gro_hash(struct napi_struct *napi) | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Jakub Kicinski
						Jakub Kicinski