On Fri, Sep 4, 2026 at 6:37 AM Paul Greenwalt <[email protected]> wrote:
>
> Add a devlink instance for the idpf adapter and implement the .info_get
> callback, reporting:
> - serial_number: PCI Device Serial Number, for device identification
> - fw.mgmt.api: running version of the driver-device communication
> channel (virtchnl)
>
> This enables NIPA CI and other tools to uniquely identify devices under
> test and track device capabilities. This phased implementation initially
> focuses on stable, readily available information. Firmware version
> information will be added in future patches as support is exposed
> through virtchnl and the driver architecture allows reliable retrieval.
>
> 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 in
> idpf_probe(), before the reset worker that performs the virtchnl
> handshake is queued, and unregistered in idpf_remove(), so it stays
> available for the whole time the driver is bound.
>
> The serial number is omitted on devices that do not implement the PCI
> Device Serial Number extended capability, where pci_get_dsn() returns 0,
> rather than reporting an all-zero serial number. The virtchnl version is
> omitted until it has been negotiated, as the instance is registered
> before the handshake runs.
>
> $ devlink dev show
> pci/0000:85:00.0
>
> $ devlink dev info pci/0000:85:00.0
> pci/0000:85:00.0:
> driver idpf
> serial_number 00-a0-c9-ff-ff-23-45-67
> versions:
> running:
> fw.mgmt.api 2.0
>
> Link:
> https://github.com/linux-netdev/nipa/wiki/Netdev-CI-system#device-information
> Signed-off-by: Paul Greenwalt <[email protected]>
> ---
Verified the devlink info output:
$ devlink -j dev info pci/0000:05:00.0
{"info":{"pci/0000:05:00.0":{"driver":"idpf","versions":{"running":{"fw.mgmt.api":"2.0"}}}}}
Tested-by: Danny Gonzalez <[email protected]>
> Documentation/networking/devlink/idpf.rst | 30 ++++++
> Documentation/networking/devlink/index.rst | 1 +
> drivers/net/ethernet/intel/idpf/Kconfig | 1 +
> drivers/net/ethernet/intel/idpf/Makefile | 1 +
> .../net/ethernet/intel/idpf/idpf_devlink.c | 98 +++++++++++++++++++
> .../net/ethernet/intel/idpf/idpf_devlink.h | 45 +++++++++
> drivers/net/ethernet/intel/idpf/idpf_main.c | 13 ++-
> 7 files changed, 186 insertions(+), 3 deletions(-)
> create mode 100644 Documentation/networking/devlink/idpf.rst
> 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/Documentation/networking/devlink/idpf.rst
> b/Documentation/networking/devlink/idpf.rst
> new file mode 100644
> index 0000000000000..5121cffd8f476
> --- /dev/null
> +++ b/Documentation/networking/devlink/idpf.rst
> @@ -0,0 +1,30 @@
> +.. SPDX-License-Identifier: GPL-2.0
> +
> +====================
> +idpf devlink support
> +====================
> +
> +This document describes the devlink features implemented by the ``idpf``
> +device driver.
> +
> +Info versions
> +=============
> +
> +The ``idpf`` driver reports the following versions
> +
> +.. list-table:: devlink info versions implemented
> + :widths: 5 5 5 90
> +
> + * - Name
> + - Type
> + - Example
> + - Description
> + * - ``fw.mgmt.api``
> + - running
> + - 2.0
> + - 2-digit version number (major.minor) of the communication channel
> + (virtchnl) used by the device.
> +
> +The driver also reports the PCI Device Serial Number through the
> +``serial_number`` attribute, on devices that implement the PCI Device Serial
> +Number extended capability.
> diff --git a/Documentation/networking/devlink/index.rst
> b/Documentation/networking/devlink/index.rst
> index d4a83fdcff7fe..538494b1051bd 100644
> --- a/Documentation/networking/devlink/index.rst
> +++ b/Documentation/networking/devlink/index.rst
> @@ -85,6 +85,7 @@ parameters, info versions, and other features it supports.
> hns3
> i40e
> ice
> + idpf
> ionic
> iosm
> ixd
> diff --git a/drivers/net/ethernet/intel/idpf/Kconfig
> b/drivers/net/ethernet/intel/idpf/Kconfig
> index 586df3a4afe90..4ee8bbc401fe6 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 4aaafa175ec37..81b27a4067478 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_devlink.c
> b/drivers/net/ethernet/intel/idpf/idpf_devlink.c
> new file mode 100644
> index 0000000000000..afdb5bc2e002c
> --- /dev/null
> +++ b/drivers/net/ethernet/intel/idpf/idpf_devlink.c
> @@ -0,0 +1,98 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/* Copyright (C) 2026 Intel Corporation */
> +
> +#include <linux/pci.h>
> +#include <linux/unaligned.h>
> +
> +#include "idpf.h"
> +#include "idpf_devlink.h"
> +
> +#define IDPF_DEVLINK_INFO_LEN 128
> +
> +/**
> + * idpf_info_get_dsn - format the PCI DSN as the device serial number
> + * @adapter: the idpf adapter structure
> + * @buf: buffer to store the formatted serial number
> + * @buf_size: size of @buf
> + *
> + * Return: true if the device reports a DSN, false otherwise.
> + */
> +static bool idpf_info_get_dsn(struct idpf_adapter *adapter, char *buf,
> + size_t buf_size)
> +{
> + u64 dsn = pci_get_dsn(adapter->pdev);
> + u8 dsn_be[sizeof(dsn)];
> +
> + if (!dsn)
> + return false;
> +
> + /* Copy the DSN into an array in Big Endian format */
> + put_unaligned_be64(dsn, dsn_be);
> + snprintf(buf, buf_size, "%8phD", dsn_be);
> +
> + return true;
> +}
> +
> +/**
> + * idpf_devlink_info_get - .info_get devlink handler
> + * @devlink: devlink instance structure
> + * @req: the devlink info request
> + * @extack: extended netlink ack structure
> + *
> + * Callback for the devlink .info_get operation. Reports information about
> the
> + * device. The virtchnl version is only reported once it has been negotiated,
> + * as the instance is registered before the handshake runs.
> + *
> + * Return: zero on success or a negative error code on failure.
> + */
> +static int idpf_devlink_info_get(struct devlink *devlink,
> + struct devlink_info_req *req,
> + struct netlink_ext_ack *extack)
> +{
> + struct idpf_adapter *adapter = devlink_priv(devlink);
> + char buf[IDPF_DEVLINK_INFO_LEN];
> + u32 maj, min;
> + int err;
> +
> + if (idpf_info_get_dsn(adapter, buf, sizeof(buf))) {
> + err = devlink_info_serial_number_put(req, buf);
> + if (err)
> + return err;
> + }
> +
> + maj = READ_ONCE(adapter->virt_ver_maj);
> + min = READ_ONCE(adapter->virt_ver_min);
> + if (!maj && !min)
> + return 0;
> +
> + snprintf(buf, sizeof(buf), "%u.%u", maj, min);
> +
> + return devlink_info_version_running_put(req,
> +
> DEVLINK_INFO_VERSION_GENERIC_FW_MGMT_API,
> + buf);
> +}
> +
> +static const struct devlink_ops idpf_devlink_ops = {
> + .info_get = idpf_devlink_info_get,
> +};
> +
> +/**
> + * 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 0000000000000..3ace09be8dddf
> --- /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_main.c
> b/drivers/net/ethernet/intel/idpf/idpf_main.c
> index 3dbbe21459990..34a69bb2e36bc 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"
>
> @@ -127,12 +128,15 @@ static void idpf_remove(struct pci_dev *pdev)
>
> set_bit(IDPF_REMOVE_IN_PROG, adapter->flags);
>
> + idpf_devlink_unregister(adapter);
> +
> /* Wait until vc_event_task is done to consider if any hard reset is
> * in progress else we may go ahead and release the resources but the
> * thread doing the hard reset might continue the init path and
> * end up in bad state.
> */
> cancel_delayed_work_sync(&adapter->vc_event_task);
> +
> if (adapter->num_vfs)
> idpf_sriov_configure(pdev, 0);
>
> @@ -184,7 +188,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);
> }
>
> /**
> @@ -263,7 +268,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;
>
> @@ -349,6 +354,8 @@ static int idpf_probe(struct pci_dev *pdev, const struct
> pci_device_id *ent)
> INIT_DELAYED_WORK(&adapter->stats_task, idpf_statistics_task);
> INIT_DELAYED_WORK(&adapter->vc_event_task, idpf_vc_event_task);
>
> + idpf_devlink_register(adapter);
> +
> adapter->dev_ops.reg_ops.reset_reg_init(adapter);
> set_bit(IDPF_HR_DRV_LOAD, adapter->flags);
> queue_delayed_work(adapter->vc_event_wq, &adapter->vc_event_task,
> @@ -367,7 +374,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