Add a devlink instance for the idpf adapter. The adapter structure is carved out of the devlink private area, so devlink_alloc() replaces the plain kzalloc_obj() in idpf_probe() and devlink_free() replaces the corresponding kfree() on both the probe error path and in idpf_remove().
The instance is registered from idpf_init_hard_reset(), once idpf_vc_core_init() has succeeded, rather than from idpf_probe(). Device information is only meaningful after the virtchnl handshake has completed, and registering in idpf_probe() would expose the instance to userspace while the adapter is still being brought up. Since idpf_init_hard_reset() also runs for every function and PCI reset, registration is guarded by IDPF_DEVLINK_REGISTERED so that it happens at most once; devlink_register() warns on an already registered instance. idpf_remove() unregisters only after cancel_delayed_work_sync() has quiesced the init/reset worker, so the worker cannot register the instance again after teardown has started, and only if registration actually happened - a probe that never completed the handshake leaves the instance unregistered, and devlink_unregister() warns in that case. Signed-off-by: Paul Greenwalt <[email protected]> --- drivers/net/ethernet/intel/idpf/Kconfig | 1 + drivers/net/ethernet/intel/idpf/Makefile | 1 + drivers/net/ethernet/intel/idpf/idpf.h | 4 +- .../net/ethernet/intel/idpf/idpf_devlink.c | 29 ++++++++++++ .../net/ethernet/intel/idpf/idpf_devlink.h | 45 +++++++++++++++++++ drivers/net/ethernet/intel/idpf/idpf_lib.c | 8 ++++ drivers/net/ethernet/intel/idpf/idpf_main.c | 15 +++++-- 7 files changed, 99 insertions(+), 4 deletions(-) create mode 100644 drivers/net/ethernet/intel/idpf/idpf_devlink.c create mode 100644 drivers/net/ethernet/intel/idpf/idpf_devlink.h diff --git a/drivers/net/ethernet/intel/idpf/Kconfig b/drivers/net/ethernet/intel/idpf/Kconfig index 586df3a4afe9..4ee8bbc401fe 100644 --- a/drivers/net/ethernet/intel/idpf/Kconfig +++ b/drivers/net/ethernet/intel/idpf/Kconfig @@ -6,6 +6,7 @@ config IDPF depends on PCI_MSI depends on PTP_1588_CLOCK_OPTIONAL select DIMLIB + select NET_DEVLINK select LIBIE_CP select LIBETH_XDP help diff --git a/drivers/net/ethernet/intel/idpf/Makefile b/drivers/net/ethernet/intel/idpf/Makefile index 4aaafa175ec3..81b27a406747 100644 --- a/drivers/net/ethernet/intel/idpf/Makefile +++ b/drivers/net/ethernet/intel/idpf/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_IDPF) += idpf.o idpf-y := \ idpf_dev.o \ + idpf_devlink.o \ idpf_ethtool.o \ idpf_idc.o \ idpf_lib.o \ diff --git a/drivers/net/ethernet/intel/idpf/idpf.h b/drivers/net/ethernet/intel/idpf/idpf.h index df9e42bfeaa2..b72f31560fd2 100644 --- a/drivers/net/ethernet/intel/idpf/idpf.h +++ b/drivers/net/ethernet/intel/idpf/idpf.h @@ -81,7 +81,7 @@ enum idpf_state { }; /** - * enum idpf_flags - Hard reset causes. + * enum idpf_flags - Adapter state flags * @IDPF_HR_FUNC_RESET: Hard reset when TxRx timeout * @IDPF_HR_DRV_LOAD: Set on driver load for a clean HW * @IDPF_HR_RESET_IN_PROG: Reset in progress @@ -89,6 +89,7 @@ enum idpf_state { * @IDPF_MB_INTR_MODE: Mailbox in interrupt mode * @IDPF_VC_CORE_INIT: virtchnl core has been init * @IDPF_PCI_CB_RESET: Reset via the PCI callbacks + * @IDPF_DEVLINK_REGISTERED: devlink instance is registered with userspace * @IDPF_FLAGS_NBITS: Must be last */ enum idpf_flags { @@ -99,6 +100,7 @@ enum idpf_flags { IDPF_MB_INTR_MODE, IDPF_VC_CORE_INIT, IDPF_PCI_CB_RESET, + IDPF_DEVLINK_REGISTERED, IDPF_FLAGS_NBITS, }; diff --git a/drivers/net/ethernet/intel/idpf/idpf_devlink.c b/drivers/net/ethernet/intel/idpf/idpf_devlink.c new file mode 100644 index 000000000000..1669bdfc950b --- /dev/null +++ b/drivers/net/ethernet/intel/idpf/idpf_devlink.c @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (C) 2026 Intel Corporation */ + +#include "idpf.h" +#include "idpf_devlink.h" + +static const struct devlink_ops idpf_devlink_ops = { +}; + +/** + * idpf_adapter_alloc - allocate devlink and return adapter + * @dev: IDPF device to allocate for + * + * Allocate a devlink instance for this device and return the private area as + * the adapter structure. + * + * Return: adapter structure on success, NULL on failure + */ +struct idpf_adapter *idpf_adapter_alloc(struct device *dev) +{ + struct devlink *devlink; + + devlink = devlink_alloc(&idpf_devlink_ops, sizeof(struct idpf_adapter), + dev); + if (!devlink) + return NULL; + + return devlink_priv(devlink); +} diff --git a/drivers/net/ethernet/intel/idpf/idpf_devlink.h b/drivers/net/ethernet/intel/idpf/idpf_devlink.h new file mode 100644 index 000000000000..3ace09be8ddd --- /dev/null +++ b/drivers/net/ethernet/intel/idpf/idpf_devlink.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (C) 2026 Intel Corporation */ + +#ifndef _IDPF_DEVLINK_H_ +#define _IDPF_DEVLINK_H_ +#include <net/devlink.h> + +struct idpf_adapter; + +struct idpf_adapter *idpf_adapter_alloc(struct device *dev); + +/** + * idpf_devlink_free - teardown the devlink + * @adapter: IDPF adapter structure to free + */ +static inline void idpf_devlink_free(struct idpf_adapter *adapter) +{ + struct devlink *devlink = priv_to_devlink(adapter); + + devlink_free(devlink); +} + +/** + * idpf_devlink_register - register the devlink + * @adapter: IDPF adapter structure + */ +static inline void idpf_devlink_register(struct idpf_adapter *adapter) +{ + struct devlink *devlink = priv_to_devlink(adapter); + + devlink_register(devlink); +} + +/** + * idpf_devlink_unregister - unregister the devlink + * @adapter: IDPF adapter structure + */ +static inline void idpf_devlink_unregister(struct idpf_adapter *adapter) +{ + struct devlink *devlink = priv_to_devlink(adapter); + + devlink_unregister(devlink); +} + +#endif /* _IDPF_DEVLINK_H_ */ diff --git a/drivers/net/ethernet/intel/idpf/idpf_lib.c b/drivers/net/ethernet/intel/idpf/idpf_lib.c index 5d61ecb73a40..a375028c7080 100644 --- a/drivers/net/ethernet/intel/idpf/idpf_lib.c +++ b/drivers/net/ethernet/intel/idpf/idpf_lib.c @@ -2,6 +2,7 @@ /* Copyright (C) 2023 Intel Corporation */ #include "idpf.h" +#include "idpf_devlink.h" #include "idpf_virtchnl.h" #include "idpf_ptp.h" #include "xdp.h" @@ -1968,6 +1969,13 @@ static void idpf_init_hard_reset(struct idpf_adapter *adapter) * vports are not allocated at this point if the init task failed. */ if (!err) { + /* Expose the instance only once the device has been brought up + * far enough to answer info requests. This runs on every hard + * reset, so registration must happen at most once. + */ + if (!test_and_set_bit(IDPF_DEVLINK_REGISTERED, adapter->flags)) + idpf_devlink_register(adapter); + idpf_attach_and_open(adapter); idpf_idc_init(adapter); } diff --git a/drivers/net/ethernet/intel/idpf/idpf_main.c b/drivers/net/ethernet/intel/idpf/idpf_main.c index fc67d8f02569..ba09a130f200 100644 --- a/drivers/net/ethernet/intel/idpf/idpf_main.c +++ b/drivers/net/ethernet/intel/idpf/idpf_main.c @@ -3,6 +3,7 @@ #include "idpf.h" #include "idpf_devids.h" +#include "idpf_devlink.h" #include "idpf_lan_vf_regs.h" #include "idpf_virtchnl.h" @@ -134,6 +135,13 @@ static void idpf_remove(struct pci_dev *pdev) * end up in bad state. */ cancel_delayed_work_sync(&adapter->vc_event_task); + + /* IDPF_REMOVE_IN_PROG, set above, makes idpf_vc_event_task() return + * early, so the instance cannot be registered again after this point. + */ + if (test_and_clear_bit(IDPF_DEVLINK_REGISTERED, adapter->flags)) + idpf_devlink_unregister(adapter); + if (adapter->num_vfs) idpf_sriov_configure(pdev, 0); @@ -185,7 +193,8 @@ static void idpf_remove(struct pci_dev *pdev) mutex_destroy(&adapter->vc_buf_lock); idpf_decfg_device(adapter); - kfree(adapter); + + idpf_devlink_free(adapter); } /** @@ -264,7 +273,7 @@ static int idpf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) struct idpf_adapter *adapter; int err; - adapter = kzalloc_obj(*adapter); + adapter = idpf_adapter_alloc(dev); if (!adapter) return -ENOMEM; @@ -368,7 +377,7 @@ static int idpf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) err_init_wq: idpf_decfg_device(adapter); err_free: - kfree(adapter); + idpf_devlink_free(adapter); return err; } -- 2.52.0
