Commit a2d2cadc authored by David S. Miller's avatar David S. Miller

Merge branch 'net-dunamic-dummy-device'

Breno Leitao says:

====================
allocate dummy device dynamically

struct net_device shouldn't be embedded into any structure, instead,
the owner should use the private space to embed their state into
net_device.

But, in some cases the net_device is embedded inside the private
structure, which blocks the usage of zero-length arrays inside
net_device.

Create a helper to allocate a dummy device at dynamically runtime, and
move the Ethernet devices to use it, instead of embedding the dummy
device inside the private structure.

This fixes all the network cases plus some wireless drivers.

PS: Due to lack of hardware, unfortunately most these patches are
compiled tested only, except ath11k that was kindly tested by Kalle Valo.

---
Changelog:

v7:
	* Document the return value of alloc_netdev_dummy()
v6:
	* No code change. Just added Reviewed-by: and fix a commit message
v5:
	* Added a new patch to fix some typos in the previous code
	* Rebased to net-net/main
v4:
	* Added a new patch to add dummy device at free_netdev(), as suggested
	  by Jakub.
	* Added support for some wireless driver.
	* Added some Acked-by and Reviewed-by.
v3:
	* Use free_netdev() instead of kfree() as suggested by Jakub.
	* Change the free_netdev() place in ipa driver, as suggested by
	  Alex Elder.
	* Set err in the error path in the Marvell driver, as suggested
	  by Simon Horman.
v2:
	* Patch 1: Use a pre-defined name ("dummy#") for the dummy
	  net_devices.
	* Patch 2-5: Added users for the new helper.
v1:
	* https://lore.kernel.org/all/20240327200809.512867-1-leitao@debian.org/
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 55972ce6 bca592ea
......@@ -605,9 +605,13 @@ static int mal_probe(struct platform_device *ofdev)
INIT_LIST_HEAD(&mal->list);
spin_lock_init(&mal->lock);
init_dummy_netdev(&mal->dummy_dev);
mal->dummy_dev = alloc_netdev_dummy(0);
if (!mal->dummy_dev) {
err = -ENOMEM;
goto fail_unmap;
}
netif_napi_add_weight(&mal->dummy_dev, &mal->napi, mal_poll,
netif_napi_add_weight(mal->dummy_dev, &mal->napi, mal_poll,
CONFIG_IBM_EMAC_POLL_WEIGHT);
/* Load power-on reset defaults */
......@@ -637,7 +641,7 @@ static int mal_probe(struct platform_device *ofdev)
GFP_KERNEL);
if (mal->bd_virt == NULL) {
err = -ENOMEM;
goto fail_unmap;
goto fail_dummy;
}
for (i = 0; i < mal->num_tx_chans; ++i)
......@@ -703,6 +707,8 @@ static int mal_probe(struct platform_device *ofdev)
free_irq(mal->serr_irq, mal);
fail2:
dma_free_coherent(&ofdev->dev, bd_size, mal->bd_virt, mal->bd_dma);
fail_dummy:
free_netdev(mal->dummy_dev);
fail_unmap:
dcr_unmap(mal->dcr_host, 0x100);
fail:
......@@ -734,6 +740,8 @@ static void mal_remove(struct platform_device *ofdev)
mal_reset(mal);
free_netdev(mal->dummy_dev);
dma_free_coherent(&ofdev->dev,
sizeof(struct mal_descriptor) *
(NUM_TX_BUFF * mal->num_tx_chans +
......
......@@ -205,7 +205,7 @@ struct mal_instance {
int index;
spinlock_t lock;
struct net_device dummy_dev;
struct net_device *dummy_dev;
unsigned int features;
};
......
......@@ -96,7 +96,7 @@ struct prestera_sdma {
struct dma_pool *desc_pool;
struct work_struct tx_work;
struct napi_struct rx_napi;
struct net_device napi_dev;
struct net_device *napi_dev;
u32 map_addr;
u64 dma_mask;
/* protect SDMA with concurrent access from multiple CPUs */
......@@ -654,13 +654,21 @@ static int prestera_sdma_switch_init(struct prestera_switch *sw)
if (err)
goto err_evt_register;
init_dummy_netdev(&sdma->napi_dev);
sdma->napi_dev = alloc_netdev_dummy(0);
if (!sdma->napi_dev) {
dev_err(dev, "not able to initialize dummy device\n");
err = -ENOMEM;
goto err_alloc_dummy;
}
netif_napi_add(&sdma->napi_dev, &sdma->rx_napi, prestera_sdma_rx_poll);
netif_napi_add(sdma->napi_dev, &sdma->rx_napi, prestera_sdma_rx_poll);
napi_enable(&sdma->rx_napi);
return 0;
err_alloc_dummy:
prestera_hw_event_handler_unregister(sw, PRESTERA_EVENT_TYPE_RXTX,
prestera_rxtx_handle_event);
err_evt_register:
err_tx_init:
prestera_sdma_tx_fini(sdma);
......@@ -677,6 +685,7 @@ static void prestera_sdma_switch_fini(struct prestera_switch *sw)
napi_disable(&sdma->rx_napi);
netif_napi_del(&sdma->rx_napi);
free_netdev(sdma->napi_dev);
prestera_hw_event_handler_unregister(sw, PRESTERA_EVENT_TYPE_RXTX,
prestera_rxtx_handle_event);
prestera_sdma_tx_fini(sdma);
......
......@@ -1710,7 +1710,7 @@ static struct page_pool *mtk_create_page_pool(struct mtk_eth *eth,
if (IS_ERR(pp))
return pp;
err = __xdp_rxq_info_reg(xdp_q, &eth->dummy_dev, id,
err = __xdp_rxq_info_reg(xdp_q, eth->dummy_dev, id,
eth->rx_napi.napi_id, PAGE_SIZE);
if (err < 0)
goto err_free_pp;
......@@ -4188,6 +4188,8 @@ static int mtk_free_dev(struct mtk_eth *eth)
metadata_dst_free(eth->dsa_meta[i]);
}
free_netdev(eth->dummy_dev);
return 0;
}
......@@ -4983,9 +4985,14 @@ static int mtk_probe(struct platform_device *pdev)
/* we run 2 devices on the same DMA ring so we need a dummy device
* for NAPI to work
*/
init_dummy_netdev(&eth->dummy_dev);
netif_napi_add(&eth->dummy_dev, &eth->tx_napi, mtk_napi_tx);
netif_napi_add(&eth->dummy_dev, &eth->rx_napi, mtk_napi_rx);
eth->dummy_dev = alloc_netdev_dummy(0);
if (!eth->dummy_dev) {
err = -ENOMEM;
dev_err(eth->dev, "failed to allocated dummy device\n");
goto err_unreg_netdev;
}
netif_napi_add(eth->dummy_dev, &eth->tx_napi, mtk_napi_tx);
netif_napi_add(eth->dummy_dev, &eth->rx_napi, mtk_napi_rx);
platform_set_drvdata(pdev, eth);
schedule_delayed_work(&eth->reset.monitor_work,
......@@ -4993,6 +5000,8 @@ static int mtk_probe(struct platform_device *pdev)
return 0;
err_unreg_netdev:
mtk_unreg_dev(eth);
err_deinit_ppe:
mtk_ppe_deinit(eth);
mtk_mdio_cleanup(eth);
......
......@@ -1242,7 +1242,7 @@ struct mtk_eth {
spinlock_t page_lock;
spinlock_t tx_irq_lock;
spinlock_t rx_irq_lock;
struct net_device dummy_dev;
struct net_device *dummy_dev;
struct net_device *netdev[MTK_MAX_DEVS];
struct mtk_mac *mac[MTK_MAX_DEVS];
int irq[3];
......
......@@ -1728,10 +1728,10 @@ static int gsi_channel_setup_one(struct gsi *gsi, u32 channel_id)
gsi_channel_program(channel, true);
if (channel->toward_ipa)
netif_napi_add_tx(&gsi->dummy_dev, &channel->napi,
netif_napi_add_tx(gsi->dummy_dev, &channel->napi,
gsi_channel_poll);
else
netif_napi_add(&gsi->dummy_dev, &channel->napi,
netif_napi_add(gsi->dummy_dev, &channel->napi,
gsi_channel_poll);
return 0;
......@@ -2367,12 +2367,14 @@ int gsi_init(struct gsi *gsi, struct platform_device *pdev,
/* GSI uses NAPI on all channels. Create a dummy network device
* for the channel NAPI contexts to be associated with.
*/
init_dummy_netdev(&gsi->dummy_dev);
gsi->dummy_dev = alloc_netdev_dummy(0);
if (!gsi->dummy_dev)
return -ENOMEM;
init_completion(&gsi->completion);
ret = gsi_reg_init(gsi, pdev);
if (ret)
return ret;
goto err_reg_exit;
ret = gsi_irq_init(gsi, pdev); /* No matching exit required */
if (ret)
......@@ -2387,6 +2389,7 @@ int gsi_init(struct gsi *gsi, struct platform_device *pdev,
return 0;
err_reg_exit:
free_netdev(gsi->dummy_dev);
gsi_reg_exit(gsi);
return ret;
......@@ -2397,6 +2400,7 @@ void gsi_exit(struct gsi *gsi)
{
mutex_destroy(&gsi->mutex);
gsi_channel_exit(gsi);
free_netdev(gsi->dummy_dev);
gsi_reg_exit(gsi);
}
......
......@@ -151,7 +151,7 @@ struct gsi {
struct mutex mutex; /* protects commands, programming */
struct gsi_channel channel[GSI_CHANNEL_COUNT_MAX];
struct gsi_evt_ring evt_ring[GSI_EVT_RING_COUNT_MAX];
struct net_device dummy_dev; /* needed for NAPI */
struct net_device *dummy_dev; /* needed for NAPI */
};
/**
......
......@@ -3673,11 +3673,13 @@ struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev,
INIT_WORK(&ar->set_coverage_class_work,
ath10k_core_set_coverage_class_work);
init_dummy_netdev(&ar->napi_dev);
ar->napi_dev = alloc_netdev_dummy(0);
if (!ar->napi_dev)
goto err_free_tx_complete;
ret = ath10k_coredump_create(ar);
if (ret)
goto err_free_tx_complete;
goto err_free_netdev;
ret = ath10k_debug_create(ar);
if (ret)
......@@ -3687,6 +3689,8 @@ struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev,
err_free_coredump:
ath10k_coredump_destroy(ar);
err_free_netdev:
free_netdev(ar->napi_dev);
err_free_tx_complete:
destroy_workqueue(ar->workqueue_tx_complete);
err_free_aux_wq:
......@@ -3708,6 +3712,7 @@ void ath10k_core_destroy(struct ath10k *ar)
destroy_workqueue(ar->workqueue_tx_complete);
free_netdev(ar->napi_dev);
ath10k_debug_destroy(ar);
ath10k_coredump_destroy(ar);
ath10k_htt_tx_destroy(&ar->htt);
......
......@@ -1269,7 +1269,7 @@ struct ath10k {
struct ath10k_per_peer_tx_stats peer_tx_stats;
/* NAPI */
struct net_device napi_dev;
struct net_device *napi_dev;
struct napi_struct napi;
struct work_struct set_coverage_class_work;
......
......@@ -3217,7 +3217,7 @@ static void ath10k_pci_free_irq(struct ath10k *ar)
void ath10k_pci_init_napi(struct ath10k *ar)
{
netif_napi_add(&ar->napi_dev, &ar->napi, ath10k_pci_napi_poll);
netif_napi_add(ar->napi_dev, &ar->napi, ath10k_pci_napi_poll);
}
static int ath10k_pci_init_irq(struct ath10k *ar)
......
......@@ -2532,7 +2532,7 @@ static int ath10k_sdio_probe(struct sdio_func *func,
return -ENOMEM;
}
netif_napi_add(&ar->napi_dev, &ar->napi, ath10k_sdio_napi_poll);
netif_napi_add(ar->napi_dev, &ar->napi, ath10k_sdio_napi_poll);
ath10k_dbg(ar, ATH10K_DBG_BOOT,
"sdio new func %d vendor 0x%x device 0x%x block 0x%x/0x%x\n",
......
......@@ -935,7 +935,7 @@ static int ath10k_snoc_hif_start(struct ath10k *ar)
bitmap_clear(ar_snoc->pending_ce_irqs, 0, CE_COUNT_MAX);
dev_set_threaded(&ar->napi_dev, true);
dev_set_threaded(ar->napi_dev, true);
ath10k_core_napi_enable(ar);
ath10k_snoc_irq_enable(ar);
ath10k_snoc_rx_post(ar);
......@@ -1253,7 +1253,7 @@ static int ath10k_snoc_napi_poll(struct napi_struct *ctx, int budget)
static void ath10k_snoc_init_napi(struct ath10k *ar)
{
netif_napi_add(&ar->napi_dev, &ar->napi, ath10k_snoc_napi_poll);
netif_napi_add(ar->napi_dev, &ar->napi, ath10k_snoc_napi_poll);
}
static int ath10k_snoc_request_irq(struct ath10k *ar)
......
......@@ -1014,7 +1014,7 @@ static int ath10k_usb_probe(struct usb_interface *interface,
return -ENOMEM;
}
netif_napi_add(&ar->napi_dev, &ar->napi, ath10k_usb_napi_poll);
netif_napi_add(ar->napi_dev, &ar->napi, ath10k_usb_napi_poll);
usb_get_dev(dev);
vendor_id = le16_to_cpu(dev->descriptor.idVendor);
......
......@@ -442,6 +442,7 @@ static void ath11k_ahb_free_ext_irq(struct ath11k_base *ab)
free_irq(ab->irq_num[irq_grp->irqs[j]], irq_grp);
netif_napi_del(&irq_grp->napi);
free_netdev(irq_grp->napi_ndev);
}
}
......@@ -533,8 +534,12 @@ static int ath11k_ahb_config_ext_irq(struct ath11k_base *ab)
irq_grp->ab = ab;
irq_grp->grp_id = i;
init_dummy_netdev(&irq_grp->napi_ndev);
netif_napi_add(&irq_grp->napi_ndev, &irq_grp->napi,
irq_grp->napi_ndev = alloc_netdev_dummy(0);
if (!irq_grp->napi_ndev)
return -ENOMEM;
netif_napi_add(irq_grp->napi_ndev, &irq_grp->napi,
ath11k_ahb_ext_grp_napi_poll);
for (j = 0; j < ATH11K_EXT_IRQ_NUM_MAX; j++) {
......
......@@ -174,7 +174,7 @@ struct ath11k_ext_irq_grp {
u64 timestamp;
bool napi_enabled;
struct napi_struct napi;
struct net_device napi_ndev;
struct net_device *napi_ndev;
};
enum ath11k_smbios_cc_type {
......
......@@ -316,6 +316,7 @@ static void ath11k_pcic_free_ext_irq(struct ath11k_base *ab)
free_irq(ab->irq_num[irq_grp->irqs[j]], irq_grp);
netif_napi_del(&irq_grp->napi);
free_netdev(irq_grp->napi_ndev);
}
}
......@@ -558,7 +559,7 @@ ath11k_pcic_get_msi_irq(struct ath11k_base *ab, unsigned int vector)
static int ath11k_pcic_ext_irq_config(struct ath11k_base *ab)
{
int i, j, ret, num_vectors = 0;
int i, j, n, ret, num_vectors = 0;
u32 user_base_data = 0, base_vector = 0;
unsigned long irq_flags;
......@@ -578,8 +579,11 @@ static int ath11k_pcic_ext_irq_config(struct ath11k_base *ab)
irq_grp->ab = ab;
irq_grp->grp_id = i;
init_dummy_netdev(&irq_grp->napi_ndev);
netif_napi_add(&irq_grp->napi_ndev, &irq_grp->napi,
irq_grp->napi_ndev = alloc_netdev_dummy(0);
if (!irq_grp->napi_ndev)
return -ENOMEM;
netif_napi_add(irq_grp->napi_ndev, &irq_grp->napi,
ath11k_pcic_ext_grp_napi_poll);
if (ab->hw_params.ring_mask->tx[i] ||
......@@ -601,8 +605,13 @@ static int ath11k_pcic_ext_irq_config(struct ath11k_base *ab)
int vector = (i % num_vectors) + base_vector;
int irq = ath11k_pcic_get_msi_irq(ab, vector);
if (irq < 0)
if (irq < 0) {
for (n = 0; n <= i; n++) {
irq_grp = &ab->ext_irq_grp[n];
free_netdev(irq_grp->napi_ndev);
}
return irq;
}
ab->irq_num[irq_idx] = irq;
......@@ -615,6 +624,10 @@ static int ath11k_pcic_ext_irq_config(struct ath11k_base *ab)
if (ret) {
ath11k_err(ab, "failed request irq %d: %d\n",
vector, ret);
for (n = 0; n <= i; n++) {
irq_grp = &ab->ext_irq_grp[n];
free_netdev(irq_grp->napi_ndev);
}
return ret;
}
}
......
......@@ -372,8 +372,7 @@ static int qtnf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
goto error;
}
bus->mux_dev = alloc_netdev(0, "dummy", NET_NAME_UNKNOWN,
init_dummy_netdev);
bus->mux_dev = alloc_netdev_dummy(0);
if (!bus->mux_dev) {
ret = -ENOMEM;
goto error;
......
......@@ -4519,6 +4519,9 @@ static inline void netif_addr_unlock_bh(struct net_device *dev)
void ether_setup(struct net_device *dev);
/* Allocate dummy net_device */
struct net_device *alloc_netdev_dummy(int sizeof_priv);
/* Support for loadable net-drivers */
struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
unsigned char name_assign_type,
......
......@@ -10420,25 +10420,12 @@ int register_netdevice(struct net_device *dev)
}
EXPORT_SYMBOL(register_netdevice);
/**
* init_dummy_netdev - init a dummy network device for NAPI
* @dev: device to init
*
* This takes a network device structure and initialize the minimum
* amount of fields so it can be used to schedule NAPI polls without
* registering a full blown interface. This is to be used by drivers
* that need to tie several hardware interfaces to a single NAPI
* poll scheduler due to HW limitations.
/* Initialize the core of a dummy net device.
* This is useful if you are calling this function after alloc_netdev(),
* since it does not memset the net_device fields.
*/
void init_dummy_netdev(struct net_device *dev)
static void init_dummy_netdev_core(struct net_device *dev)
{
/* Clear everything. Note we don't initialize spinlocks
* are they aren't supposed to be taken by any of the
* NAPI code and this dummy netdev is supposed to be
* only ever used for NAPI polls
*/
memset(dev, 0, sizeof(struct net_device));
/* make sure we BUG if trying to hit standard
* register/unregister code path
*/
......@@ -10459,8 +10446,28 @@ void init_dummy_netdev(struct net_device *dev)
* its refcount.
*/
}
EXPORT_SYMBOL_GPL(init_dummy_netdev);
/**
* init_dummy_netdev - init a dummy network device for NAPI
* @dev: device to init
*
* This takes a network device structure and initializes the minimum
* amount of fields so it can be used to schedule NAPI polls without
* registering a full blown interface. This is to be used by drivers
* that need to tie several hardware interfaces to a single NAPI
* poll scheduler due to HW limitations.
*/
void init_dummy_netdev(struct net_device *dev)
{
/* Clear everything. Note we don't initialize spinlocks
* as they aren't supposed to be taken by any of the
* NAPI code and this dummy netdev is supposed to be
* only ever used for NAPI polls
*/
memset(dev, 0, sizeof(struct net_device));
init_dummy_netdev_core(dev);
}
EXPORT_SYMBOL_GPL(init_dummy_netdev);
/**
* register_netdev - register a network device
......@@ -11066,7 +11073,8 @@ void free_netdev(struct net_device *dev)
phy_link_topo_destroy(dev->link_topo);
/* Compatibility with error handling in drivers */
if (dev->reg_state == NETREG_UNINITIALIZED) {
if (dev->reg_state == NETREG_UNINITIALIZED ||
dev->reg_state == NETREG_DUMMY) {
netdev_freemem(dev);
return;
}
......@@ -11079,6 +11087,19 @@ void free_netdev(struct net_device *dev)
}
EXPORT_SYMBOL(free_netdev);
/**
* alloc_netdev_dummy - Allocate and initialize a dummy net device.
* @sizeof_priv: size of private data to allocate space for
*
* Return: the allocated net_device on success, NULL otherwise
*/
struct net_device *alloc_netdev_dummy(int sizeof_priv)
{
return alloc_netdev(sizeof_priv, "dummy#", NET_NAME_UNKNOWN,
init_dummy_netdev_core);
}
EXPORT_SYMBOL_GPL(alloc_netdev_dummy);
/**
* synchronize_net - Synchronize with packet receive processing
*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment