mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	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