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

Reply via email to