public inbox for rust-for-linux@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/2] rust: pci: add capability lookup helpers
@ 2026-01-31  4:01 Zijing Zhang
  2026-01-31  4:01 ` [PATCH 1/2] " Zijing Zhang
                   ` (3 more replies)
  0 siblings, 4 replies; 13+ messages in thread
From: Zijing Zhang @ 2026-01-31  4:01 UTC (permalink / raw)
  To: dakr, linux-pci, rust-for-linux
  Cc: bhelgaas, kwilczynski, ojeda, gary, Zijing Zhang

This series adds small helpers to locate PCI capabilities from Rust.

It introduces `pci::Device::{find_capability, find_ext_capability}`, thin
wrappers around the PCI core helpers (`pci_find_capability()` and
`pci_find_ext_capability()`), returning the config-space offset when
present.

An in-tree user is added to the Rust PCI sample driver to exercise the
new API.
The sample attempts to locate a few common capabilities (standard and
extended) and prints whether they are present. It also includes best-effort
self-checks to
try to hit the `Some(offset)` path when the device advertises a standard
capability list or has PCIe extended configuration space.

This patchset intentionally stays policy-free: no Rust-only
range/alignment checks, and no capability parsing.

Testing

Build:
  - x86_64 defconfig-based kernel with Rust enabled
    (out-of-tree build: `make O=...`).
  - `CONFIG_SAMPLES_RUST=y`
  - `CONFIG_SAMPLE_RUST_DRIVER_PCI=y`

Runtime:
  - QEMU x86_64 with `-device pci-testdev`
    (capability lists are not advertised in this configuration).
  - QEMU x86_64 (q35) with an NVMe device
    (exercised the `Some(offset)` path via
     `find_capability(PCI_CAP_ID_EXP)` returning `Some(0x80)`).


Zijing Zhang (2):
  rust: pci: add capability lookup helpers
  samples: rust: pci: exercise capability lookup

 rust/kernel/pci.rs              | 36 +++++++++++++
 samples/rust/rust_driver_pci.rs | 90 +++++++++++++++++++++++++++++++++
 2 files changed, 126 insertions(+)

-- 
2.52.0


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 1/2] rust: pci: add capability lookup helpers
  2026-01-31  4:01 [PATCH 0/2] rust: pci: add capability lookup helpers Zijing Zhang
@ 2026-01-31  4:01 ` Zijing Zhang
  2026-01-31  4:02 ` [PATCH 2/2] samples: rust: pci: exercise capability lookup Zijing Zhang
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 13+ messages in thread
From: Zijing Zhang @ 2026-01-31  4:01 UTC (permalink / raw)
  To: dakr, linux-pci, rust-for-linux
  Cc: bhelgaas, kwilczynski, ojeda, gary, Zijing Zhang

Add thin wrappers around pci_find_capability() and
pci_find_ext_capability().

The helpers return the capability offset in config space, or None if the
capability is not present.

Signed-off-by: Zijing Zhang <zijing.zhang@ry.rs>
---
 rust/kernel/pci.rs | 36 ++++++++++++++++++++++++++++++++++++
 1 file changed, 36 insertions(+)

diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs
index af74ddff6114..2bec354cf620 100644
--- a/rust/kernel/pci.rs
+++ b/rust/kernel/pci.rs
@@ -443,6 +443,42 @@ pub fn pci_class(&self) -> Class {
         // SAFETY: `self.as_raw` is a valid pointer to a `struct pci_dev`.
         Class::from_raw(unsafe { (*self.as_raw()).class })
     }
+
+    /// Finds a PCI capability by ID and returns its config-space offset.
+    ///
+    /// Returns `None` if the capability is not present.
+    ///
+    /// This is a thin wrapper around `pci_find_capability()`.
+    #[inline]
+    pub fn find_capability(&self, id: u8) -> Option<u8> {
+        // SAFETY: By its type invariant `self.as_raw` is always a valid pointer to a
+        // `struct pci_dev`.
+        let offset = unsafe { bindings::pci_find_capability(self.as_raw(), id.into()) };
+
+        if offset == 0 {
+            None
+        } else {
+            Some(offset)
+        }
+    }
+
+    /// Finds an extended capability by ID and returns its config-space offset.
+    ///
+    /// Returns `None` if the capability is not present.
+    ///
+    /// This is a thin wrapper around `pci_find_ext_capability()`.
+    #[inline]
+    pub fn find_ext_capability(&self, id: u16) -> Option<u16> {
+        // SAFETY: By its type invariant `self.as_raw` is always a valid pointer to a
+        // `struct pci_dev`.
+        let offset = unsafe { bindings::pci_find_ext_capability(self.as_raw(), id.into()) };
+
+        if offset == 0 {
+            None
+        } else {
+            Some(offset)
+        }
+    }
 }
 
 impl Device<device::Core> {
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 2/2] samples: rust: pci: exercise capability lookup
  2026-01-31  4:01 [PATCH 0/2] rust: pci: add capability lookup helpers Zijing Zhang
  2026-01-31  4:01 ` [PATCH 1/2] " Zijing Zhang
@ 2026-01-31  4:02 ` Zijing Zhang
  2026-01-31  9:17   ` kernel test robot
  2026-01-31 13:34   ` Dirk Behme
  2026-01-31  9:51 ` [PATCH v2 0/2] rust: pci: add capability lookup helpers Zijing Zhang
  2026-01-31 15:17 ` [PATCH v3 0/2] rust: pci: add capability lookup helpers Zijing Zhang
  3 siblings, 2 replies; 13+ messages in thread
From: Zijing Zhang @ 2026-01-31  4:02 UTC (permalink / raw)
  To: dakr, linux-pci, rust-for-linux
  Cc: bhelgaas, kwilczynski, ojeda, gary, Zijing Zhang

Use the new pci::Device capability helpers to locate a few common
capabilities (standard and extended).

Also try a best-effort self-check to exercise the `Some(offset)` path
when the device advertises a standard capability list or an ext-cap header.

Signed-off-by: Zijing Zhang <zijing.zhang@ry.rs>
---
 samples/rust/rust_driver_pci.rs | 90 +++++++++++++++++++++++++++++++++
 1 file changed, 90 insertions(+)

diff --git a/samples/rust/rust_driver_pci.rs b/samples/rust/rust_driver_pci.rs
index 4dfb8a6a4707..195d37edff2c 100644
--- a/samples/rust/rust_driver_pci.rs
+++ b/samples/rust/rust_driver_pci.rs
@@ -5,6 +5,7 @@
 //! To make this driver probe, QEMU must be run with `-device pci-testdev`.
 
 use kernel::{
+    bindings,
     device::Bound,
     device::Core,
     devres::Devres,
@@ -89,6 +90,95 @@ fn config_space(pdev: &pci::Device<Bound>) {
             "pci-testdev config space read32 BAR 0: {:x}\n",
             config.read32(0x10)
         );
+
+        for (name, id) in [
+            ("PM", bindings::PCI_CAP_ID_PM as u8),
+            ("MSI", bindings::PCI_CAP_ID_MSI as u8),
+            ("PCIe", bindings::PCI_CAP_ID_EXP as u8),
+        ] {
+            if let Some(pos) = pdev.find_capability(id) {
+                dev_info!(pdev.as_ref(), "pci-testdev {name} cap @ 0x{:02x}\n", pos);
+            } else {
+                dev_info!(pdev.as_ref(), "pci-testdev has no {name} capability\n");
+            }
+        }
+
+        // Best-effort self-check to exercise the `Some(offset)` path:
+        // If the device advertises a standard capability list, read the first capability ID
+        // directly from config space and verify that `find_capability()` returns an offset.
+        let status = config.read16(bindings::PCI_STATUS as usize);
+        if (status & bindings::PCI_STATUS_CAP_LIST as u16) != 0 {
+            let pos = config.read8(bindings::PCI_CAPABILITY_LIST as usize);
+            if pos != 0 {
+                let id = config.read8(pos as usize + bindings::PCI_CAP_LIST_ID as usize);
+                match pdev.find_capability(id) {
+                    Some(found) => dev_info!(
+                        pdev.as_ref(),
+                        "pci-testdev selfcheck: cap id 0x{:02x} @ 0x{:02x} (find -> 0x{:02x})\n",
+                        id,
+                        pos,
+                        found
+                    ),
+                    None => dev_info!(
+                        pdev.as_ref(),
+                        "pci-testdev selfcheck: cap id 0x{:02x} @ 0x{:02x} (find -> none)\n",
+                        id,
+                        pos
+                    ),
+                }
+            } else {
+                dev_info!(pdev.as_ref(), "pci-testdev selfcheck: empty cap list\n");
+            }
+        } else {
+            dev_info!(pdev.as_ref(), "pci-testdev selfcheck: no cap list\n");
+        }
+
+        for (name, id) in [
+            ("DSN", bindings::PCI_EXT_CAP_ID_DSN as u16),
+            ("SR-IOV", bindings::PCI_EXT_CAP_ID_SRIOV as u16),
+        ] {
+            if let Some(pos) = pdev.find_ext_capability(id) {
+                dev_info!(pdev.as_ref(), "pci-testdev {name} ext cap @ 0x{:04x}\n", pos);
+            } else {
+                dev_info!(pdev.as_ref(), "pci-testdev has no {name} ext capability\n");
+            }
+        }
+
+        // Best-effort self-check for extended capabilities.
+        //
+        // If the device has PCIe extended configuration space, verify that
+        // `find_ext_capability()` can find the ID from the first extended
+        // capability header (which is located right after the 256-byte legacy
+        // configuration space).
+        if let Ok(config_ext) = pdev.config_space_extended() {
+            let hdr = config_ext.read32(bindings::PCI_CFG_SPACE_SIZE as usize);
+            if hdr != 0 && hdr != u32::MAX {
+                let id = (hdr & 0xffff) as u16;
+                match pdev.find_ext_capability(id) {
+                    Some(found) => dev_info!(
+                        pdev.as_ref(),
+                        "pci-testdev selfcheck: ext cap id 0x{:04x} @ 0x{:04x} (find -> 0x{:04x})\n",
+                        id,
+                        bindings::PCI_CFG_SPACE_SIZE,
+                        found
+                    ),
+                    None => dev_info!(
+                        pdev.as_ref(),
+                        "pci-testdev selfcheck: ext cap id 0x{:04x} @ 0x{:04x} (find -> none)\n",
+                        id,
+                        bindings::PCI_CFG_SPACE_SIZE
+                    ),
+                }
+            } else {
+                dev_info!(
+                    pdev.as_ref(),
+                    "pci-testdev selfcheck: no ext cap header @ 0x{:04x}\n",
+                    bindings::PCI_CFG_SPACE_SIZE
+                );
+            }
+        } else {
+            dev_info!(pdev.as_ref(), "pci-testdev selfcheck: no ext config space\n");
+        }
     }
 }
 
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH 2/2] samples: rust: pci: exercise capability lookup
  2026-01-31  4:02 ` [PATCH 2/2] samples: rust: pci: exercise capability lookup Zijing Zhang
@ 2026-01-31  9:17   ` kernel test robot
  2026-01-31 13:34   ` Dirk Behme
  1 sibling, 0 replies; 13+ messages in thread
From: kernel test robot @ 2026-01-31  9:17 UTC (permalink / raw)
  To: Zijing Zhang, dakr, linux-pci, rust-for-linux
  Cc: oe-kbuild-all, bhelgaas, kwilczynski, ojeda, gary, Zijing Zhang

Hi Zijing,

kernel test robot noticed the following build errors:

[auto build test ERROR on next-20260130]
[cannot apply to pci/next pci/for-linus v6.19-rc7 v6.19-rc6 v6.19-rc5 linus/master v6.16-rc1]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Zijing-Zhang/rust-pci-add-capability-lookup-helpers/20260131-120935
base:   next-20260130
patch link:    https://lore.kernel.org/r/20260131040200.1242566-3-zijing.zhang%40ry.rs
patch subject: [PATCH 2/2] samples: rust: pci: exercise capability lookup
config: x86_64-rhel-9.4-rust (https://download.01.org/0day-ci/archive/20260131/202601311056.PVHRBER5-lkp@intel.com/config)
compiler: clang version 20.1.8 (https://github.com/llvm/llvm-project 87f0227cb60147a26a1eeb4fb06e3b505e9c7261)
rustc: rustc 1.88.0 (6b00bc388 2025-06-23)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260131/202601311056.PVHRBER5-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202601311056.PVHRBER5-lkp@intel.com/

All errors (new ones prefixed by >>):

   PATH=/opt/cross/clang-20/bin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
   INFO PATH=/opt/cross/rustc-1.88.0-bindgen-0.72.1/cargo/bin:/opt/cross/clang-20/bin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
   /usr/bin/timeout -k 100 12h /usr/bin/make KCFLAGS= -fno-crash-diagnostics -Wno-error=return-type -Wreturn-type -funsigned-char -Wundef -falign-functions=64 W=1 --keep-going LLVM=1 -j32 -C source O=/kbuild/obj/consumer/x86_64-rhel-9.4-rust ARCH=x86_64 SHELL=/bin/bash rustfmtcheck
   make: Entering directory '/kbuild/src/consumer'
   make[1]: Entering directory '/kbuild/obj/consumer/x86_64-rhel-9.4-rust'
>> Diff in samples/rust/rust_driver_pci.rs:138:
                ("SR-IOV", bindings::PCI_EXT_CAP_ID_SRIOV as u16),
            ] {
                if let Some(pos) = pdev.find_ext_capability(id) {
   -                dev_info!(pdev.as_ref(), "pci-testdev {name} ext cap @ 0x{:04x}\n", pos);
   +                dev_info!(
   +                    pdev.as_ref(),
   +                    "pci-testdev {name} ext cap @ 0x{:04x}\n",
   +                    pos
   +                );
                } else {
                    dev_info!(pdev.as_ref(), "pci-testdev has no {name} ext capability\n");
                }
   Diff in samples/rust/rust_driver_pci.rs:177:
                    );
                }
            } else {
   -            dev_info!(pdev.as_ref(), "pci-testdev selfcheck: no ext config space\n");
   +            dev_info!(
   +                pdev.as_ref(),
   +                "pci-testdev selfcheck: no ext config space\n"
   +            );
            }
        }
    }
   make[2]: *** [Makefile:1902: rustfmt] Error 123
   make[2]: Target 'rustfmtcheck' not remade because of errors.
   make[1]: Leaving directory '/kbuild/obj/consumer/x86_64-rhel-9.4-rust'
   make[1]: *** [Makefile:248: __sub-make] Error 2
   make[1]: Target 'rustfmtcheck' not remade because of errors.
   make: *** [Makefile:248: __sub-make] Error 2
   make: Target 'rustfmtcheck' not remade because of errors.
   make: Leaving directory '/kbuild/src/consumer'

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v2 0/2] rust: pci: add capability lookup helpers
  2026-01-31  4:01 [PATCH 0/2] rust: pci: add capability lookup helpers Zijing Zhang
  2026-01-31  4:01 ` [PATCH 1/2] " Zijing Zhang
  2026-01-31  4:02 ` [PATCH 2/2] samples: rust: pci: exercise capability lookup Zijing Zhang
@ 2026-01-31  9:51 ` Zijing Zhang
  2026-01-31  9:51   ` [PATCH v2 1/2] " Zijing Zhang
  2026-01-31  9:51   ` [PATCH v2 2/2] samples: rust: pci: exercise capability lookup Zijing Zhang
  2026-01-31 15:17 ` [PATCH v3 0/2] rust: pci: add capability lookup helpers Zijing Zhang
  3 siblings, 2 replies; 13+ messages in thread
From: Zijing Zhang @ 2026-01-31  9:51 UTC (permalink / raw)
  To: dakr, linux-pci, rust-for-linux
  Cc: bhelgaas, kwilczynski, ojeda, gary, Zijing Zhang

Changes in v2:
  - Run rustfmt on samples/rust/rust_driver_pci.rs to fix rustfmtcheck.

This series adds small helpers to locate PCI capabilities from Rust.

It introduces `pci::Device::{find_capability, find_ext_capability}`, thin
wrappers around the PCI core helpers (`pci_find_capability()` and
`pci_find_ext_capability()`), returning the config-space offset when
present.

An in-tree user is added to the Rust PCI sample driver to exercise the
new API.
The sample attempts to locate a few common capabilities (standard and
extended) and prints whether they are present. It also includes best-effort
self-checks to try to hit the `Some(offset)` path when the device
advertises a standard capability list or has PCIe extended config space.

This patchset intentionally stays policy-free: no Rust-only
range/alignment checks, and no capability parsing.

Testing

Build:
  - x86_64 defconfig-based kernel with Rust enabled
    (out-of-tree build: `make O=...`).
  - `CONFIG_SAMPLES_RUST=y`
  - `CONFIG_SAMPLE_RUST_DRIVER_PCI=y`

Runtime:
  - QEMU x86_64 with `-device pci-testdev`
    (capability lists are not advertised in this configuration).
  - QEMU x86_64 (q35) with an NVMe device
    (exercised the `Some(offset)` path via
     `find_capability(PCI_CAP_ID_EXP)` returning `Some(0x80)`).


Zijing Zhang (2):
  rust: pci: add capability lookup helpers
  samples: rust: pci: exercise capability lookup

 rust/kernel/pci.rs              | 36 ++++++++++++
 samples/rust/rust_driver_pci.rs | 97 +++++++++++++++++++++++++++++++++
 2 files changed, 133 insertions(+)

-- 
2.52.0


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v2 1/2] rust: pci: add capability lookup helpers
  2026-01-31  9:51 ` [PATCH v2 0/2] rust: pci: add capability lookup helpers Zijing Zhang
@ 2026-01-31  9:51   ` Zijing Zhang
  2026-01-31  9:51   ` [PATCH v2 2/2] samples: rust: pci: exercise capability lookup Zijing Zhang
  1 sibling, 0 replies; 13+ messages in thread
From: Zijing Zhang @ 2026-01-31  9:51 UTC (permalink / raw)
  To: dakr, linux-pci, rust-for-linux
  Cc: bhelgaas, kwilczynski, ojeda, gary, Zijing Zhang

Add thin wrappers around pci_find_capability() and
pci_find_ext_capability().

The helpers return the capability offset in config space, or None if the
capability is not present.

Signed-off-by: Zijing Zhang <zijing.zhang@ry.rs>
---
 rust/kernel/pci.rs | 36 ++++++++++++++++++++++++++++++++++++
 1 file changed, 36 insertions(+)

diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs
index af74ddff6114..2bec354cf620 100644
--- a/rust/kernel/pci.rs
+++ b/rust/kernel/pci.rs
@@ -443,6 +443,42 @@ pub fn pci_class(&self) -> Class {
         // SAFETY: `self.as_raw` is a valid pointer to a `struct pci_dev`.
         Class::from_raw(unsafe { (*self.as_raw()).class })
     }
+
+    /// Finds a PCI capability by ID and returns its config-space offset.
+    ///
+    /// Returns `None` if the capability is not present.
+    ///
+    /// This is a thin wrapper around `pci_find_capability()`.
+    #[inline]
+    pub fn find_capability(&self, id: u8) -> Option<u8> {
+        // SAFETY: By its type invariant `self.as_raw` is always a valid pointer to a
+        // `struct pci_dev`.
+        let offset = unsafe { bindings::pci_find_capability(self.as_raw(), id.into()) };
+
+        if offset == 0 {
+            None
+        } else {
+            Some(offset)
+        }
+    }
+
+    /// Finds an extended capability by ID and returns its config-space offset.
+    ///
+    /// Returns `None` if the capability is not present.
+    ///
+    /// This is a thin wrapper around `pci_find_ext_capability()`.
+    #[inline]
+    pub fn find_ext_capability(&self, id: u16) -> Option<u16> {
+        // SAFETY: By its type invariant `self.as_raw` is always a valid pointer to a
+        // `struct pci_dev`.
+        let offset = unsafe { bindings::pci_find_ext_capability(self.as_raw(), id.into()) };
+
+        if offset == 0 {
+            None
+        } else {
+            Some(offset)
+        }
+    }
 }
 
 impl Device<device::Core> {
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v2 2/2] samples: rust: pci: exercise capability lookup
  2026-01-31  9:51 ` [PATCH v2 0/2] rust: pci: add capability lookup helpers Zijing Zhang
  2026-01-31  9:51   ` [PATCH v2 1/2] " Zijing Zhang
@ 2026-01-31  9:51   ` Zijing Zhang
  1 sibling, 0 replies; 13+ messages in thread
From: Zijing Zhang @ 2026-01-31  9:51 UTC (permalink / raw)
  To: dakr, linux-pci, rust-for-linux
  Cc: bhelgaas, kwilczynski, ojeda, gary, Zijing Zhang

Use the new pci::Device capability helpers to locate a few common
capabilities (standard and extended).

Also try a best-effort self-check to exercise the `Some(offset)` path
when the device advertises a standard capability list or an ext-cap header.

Signed-off-by: Zijing Zhang <zijing.zhang@ry.rs>
---
 samples/rust/rust_driver_pci.rs | 97 +++++++++++++++++++++++++++++++++
 1 file changed, 97 insertions(+)

diff --git a/samples/rust/rust_driver_pci.rs b/samples/rust/rust_driver_pci.rs
index 4dfb8a6a4707..7e7a3b82b858 100644
--- a/samples/rust/rust_driver_pci.rs
+++ b/samples/rust/rust_driver_pci.rs
@@ -5,6 +5,7 @@
 //! To make this driver probe, QEMU must be run with `-device pci-testdev`.
 
 use kernel::{
+    bindings,
     device::Bound,
     device::Core,
     devres::Devres,
@@ -89,6 +90,102 @@ fn config_space(pdev: &pci::Device<Bound>) {
             "pci-testdev config space read32 BAR 0: {:x}\n",
             config.read32(0x10)
         );
+
+        for (name, id) in [
+            ("PM", bindings::PCI_CAP_ID_PM as u8),
+            ("MSI", bindings::PCI_CAP_ID_MSI as u8),
+            ("PCIe", bindings::PCI_CAP_ID_EXP as u8),
+        ] {
+            if let Some(pos) = pdev.find_capability(id) {
+                dev_info!(pdev.as_ref(), "pci-testdev {name} cap @ 0x{:02x}\n", pos);
+            } else {
+                dev_info!(pdev.as_ref(), "pci-testdev has no {name} capability\n");
+            }
+        }
+
+        // Best-effort self-check to exercise the `Some(offset)` path:
+        // If the device advertises a standard capability list, read the first capability ID
+        // directly from config space and verify that `find_capability()` returns an offset.
+        let status = config.read16(bindings::PCI_STATUS as usize);
+        if (status & bindings::PCI_STATUS_CAP_LIST as u16) != 0 {
+            let pos = config.read8(bindings::PCI_CAPABILITY_LIST as usize);
+            if pos != 0 {
+                let id = config.read8(pos as usize + bindings::PCI_CAP_LIST_ID as usize);
+                match pdev.find_capability(id) {
+                    Some(found) => dev_info!(
+                        pdev.as_ref(),
+                        "pci-testdev selfcheck: cap id 0x{:02x} @ 0x{:02x} (find -> 0x{:02x})\n",
+                        id,
+                        pos,
+                        found
+                    ),
+                    None => dev_info!(
+                        pdev.as_ref(),
+                        "pci-testdev selfcheck: cap id 0x{:02x} @ 0x{:02x} (find -> none)\n",
+                        id,
+                        pos
+                    ),
+                }
+            } else {
+                dev_info!(pdev.as_ref(), "pci-testdev selfcheck: empty cap list\n");
+            }
+        } else {
+            dev_info!(pdev.as_ref(), "pci-testdev selfcheck: no cap list\n");
+        }
+
+        for (name, id) in [
+            ("DSN", bindings::PCI_EXT_CAP_ID_DSN as u16),
+            ("SR-IOV", bindings::PCI_EXT_CAP_ID_SRIOV as u16),
+        ] {
+            if let Some(pos) = pdev.find_ext_capability(id) {
+                dev_info!(
+                    pdev.as_ref(),
+                    "pci-testdev {name} ext cap @ 0x{:04x}\n",
+                    pos
+                );
+            } else {
+                dev_info!(pdev.as_ref(), "pci-testdev has no {name} ext capability\n");
+            }
+        }
+
+        // Best-effort self-check for extended capabilities.
+        //
+        // If the device has PCIe extended configuration space, verify that
+        // `find_ext_capability()` can find the ID from the first extended
+        // capability header (which is located right after the 256-byte legacy
+        // configuration space).
+        if let Ok(config_ext) = pdev.config_space_extended() {
+            let hdr = config_ext.read32(bindings::PCI_CFG_SPACE_SIZE as usize);
+            if hdr != 0 && hdr != u32::MAX {
+                let id = (hdr & 0xffff) as u16;
+                match pdev.find_ext_capability(id) {
+                    Some(found) => dev_info!(
+                        pdev.as_ref(),
+                        "pci-testdev selfcheck: ext cap id 0x{:04x} @ 0x{:04x} (find -> 0x{:04x})\n",
+                        id,
+                        bindings::PCI_CFG_SPACE_SIZE,
+                        found
+                    ),
+                    None => dev_info!(
+                        pdev.as_ref(),
+                        "pci-testdev selfcheck: ext cap id 0x{:04x} @ 0x{:04x} (find -> none)\n",
+                        id,
+                        bindings::PCI_CFG_SPACE_SIZE
+                    ),
+                }
+            } else {
+                dev_info!(
+                    pdev.as_ref(),
+                    "pci-testdev selfcheck: no ext cap header @ 0x{:04x}\n",
+                    bindings::PCI_CFG_SPACE_SIZE
+                );
+            }
+        } else {
+            dev_info!(
+                pdev.as_ref(),
+                "pci-testdev selfcheck: no ext config space\n"
+            );
+        }
     }
 }
 
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH 2/2] samples: rust: pci: exercise capability lookup
  2026-01-31  4:02 ` [PATCH 2/2] samples: rust: pci: exercise capability lookup Zijing Zhang
  2026-01-31  9:17   ` kernel test robot
@ 2026-01-31 13:34   ` Dirk Behme
  2026-01-31 14:22     ` Zijing Zhang
  1 sibling, 1 reply; 13+ messages in thread
From: Dirk Behme @ 2026-01-31 13:34 UTC (permalink / raw)
  To: Zijing Zhang, dakr, linux-pci, rust-for-linux
  Cc: bhelgaas, kwilczynski, ojeda, gary

On 31.01.26 05:02, Zijing Zhang wrote:
> Use the new pci::Device capability helpers to locate a few common
> capabilities (standard and extended).
> 
> Also try a best-effort self-check to exercise the `Some(offset)` path
> when the device advertises a standard capability list or an ext-cap header.
> 
> Signed-off-by: Zijing Zhang <zijing.zhang@ry.rs>
> ---
>  samples/rust/rust_driver_pci.rs | 90 +++++++++++++++++++++++++++++++++
>  1 file changed, 90 insertions(+)
> 
> diff --git a/samples/rust/rust_driver_pci.rs b/samples/rust/rust_driver_pci.rs
> index 4dfb8a6a4707..195d37edff2c 100644
> --- a/samples/rust/rust_driver_pci.rs
> +++ b/samples/rust/rust_driver_pci.rs
> @@ -5,6 +5,7 @@
>  //! To make this driver probe, QEMU must be run with `-device pci-testdev`.
>  
>  use kernel::{
> +    bindings,
>      device::Bound,
>      device::Core,
>      devres::Devres,
> @@ -89,6 +90,95 @@ fn config_space(pdev: &pci::Device<Bound>) {
>              "pci-testdev config space read32 BAR 0: {:x}\n",
>              config.read32(0x10)
>          );
> +
> +        for (name, id) in [
> +            ("PM", bindings::PCI_CAP_ID_PM as u8),
> +            ("MSI", bindings::PCI_CAP_ID_MSI as u8),
> +            ("PCIe", bindings::PCI_CAP_ID_EXP as u8),


Would it make sense to drop the direct usage of `bindings` by the
user? And add an abstraction for this in pci.rs? E.g. an enum?


> +        ] {
> +            if let Some(pos) = pdev.find_capability(id) {
> +                dev_info!(pdev.as_ref(), "pci-testdev {name} cap @ 0x{:02x}\n", pos);


Just as a heads up, using `as_ref()`in the `dev_*` prints should be
obsolete soon:

https://lore.kernel.org/rust-for-linux/20260120181152.3640314-2-gary@kernel.org/


> +            } else {
> +                dev_info!(pdev.as_ref(), "pci-testdev has no {name} capability\n");
> +            }
> +        }
> +
> +        // Best-effort self-check to exercise the `Some(offset)` path:
> +        // If the device advertises a standard capability list, read the first capability ID
> +        // directly from config space and verify that `find_capability()` returns an offset.
> +        let status = config.read16(bindings::PCI_STATUS as usize);
> +        if (status & bindings::PCI_STATUS_CAP_LIST as u16) != 0 {
> +            let pos = config.read8(bindings::PCI_CAPABILITY_LIST as usize);


An other heads up: This should use the `register!()` macro soon:

https://lore.kernel.org/rust-for-linux/20260128-register-v4-6-aee3a33d9649@nvidia.com/


> +            if pos != 0 {
> +                let id = config.read8(pos as usize + bindings::PCI_CAP_LIST_ID as usize);
> +                match pdev.find_capability(id) {
> +                    Some(found) => dev_info!(
> +                        pdev.as_ref(),
> +                        "pci-testdev selfcheck: cap id 0x{:02x} @ 0x{:02x} (find -> 0x{:02x})\n",
> +                        id,
> +                        pos,
> +                        found
> +                    ),
> +                    None => dev_info!(
> +                        pdev.as_ref(),
> +                        "pci-testdev selfcheck: cap id 0x{:02x} @ 0x{:02x} (find -> none)\n",
> +                        id,
> +                        pos
> +                    ),
> +                }
> +            } else {
> +                dev_info!(pdev.as_ref(), "pci-testdev selfcheck: empty cap list\n");
> +            }
> +        } else {
> +            dev_info!(pdev.as_ref(), "pci-testdev selfcheck: no cap list\n");
> +        }


Could the indentation and nesting level of this block be reduced by
moving this to a helper function, e.g. `check_standard_capability()`,
and in that use early returns?

Same for the extended capabilities below (e.g.
`check_extended_capability()`).

And, going even one step further: I don't know much about PCI, but is
this a common pattern several users / drivers might need? If so it
might make sense to move some of this `check_*_capability()` to pci.rs?

Best regards

Dirk


> +        for (name, id) in [
> +            ("DSN", bindings::PCI_EXT_CAP_ID_DSN as u16),
> +            ("SR-IOV", bindings::PCI_EXT_CAP_ID_SRIOV as u16),
> +        ] {
> +            if let Some(pos) = pdev.find_ext_capability(id) {
> +                dev_info!(pdev.as_ref(), "pci-testdev {name} ext cap @ 0x{:04x}\n", pos);
> +            } else {
> +                dev_info!(pdev.as_ref(), "pci-testdev has no {name} ext capability\n");
> +            }
> +        }
> +
> +        // Best-effort self-check for extended capabilities.
> +        //
> +        // If the device has PCIe extended configuration space, verify that
> +        // `find_ext_capability()` can find the ID from the first extended
> +        // capability header (which is located right after the 256-byte legacy
> +        // configuration space).
> +        if let Ok(config_ext) = pdev.config_space_extended() {
> +            let hdr = config_ext.read32(bindings::PCI_CFG_SPACE_SIZE as usize);
> +            if hdr != 0 && hdr != u32::MAX {
> +                let id = (hdr & 0xffff) as u16;
> +                match pdev.find_ext_capability(id) {
> +                    Some(found) => dev_info!(
> +                        pdev.as_ref(),
> +                        "pci-testdev selfcheck: ext cap id 0x{:04x} @ 0x{:04x} (find -> 0x{:04x})\n",
> +                        id,
> +                        bindings::PCI_CFG_SPACE_SIZE,
> +                        found
> +                    ),
> +                    None => dev_info!(
> +                        pdev.as_ref(),
> +                        "pci-testdev selfcheck: ext cap id 0x{:04x} @ 0x{:04x} (find -> none)\n",
> +                        id,
> +                        bindings::PCI_CFG_SPACE_SIZE
> +                    ),
> +                }
> +            } else {
> +                dev_info!(
> +                    pdev.as_ref(),
> +                    "pci-testdev selfcheck: no ext cap header @ 0x{:04x}\n",
> +                    bindings::PCI_CFG_SPACE_SIZE
> +                );
> +            }
> +        } else {
> +            dev_info!(pdev.as_ref(), "pci-testdev selfcheck: no ext config space\n");
> +        }
>      }
>  }
>  


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 2/2] samples: rust: pci: exercise capability lookup
  2026-01-31 13:34   ` Dirk Behme
@ 2026-01-31 14:22     ` Zijing Zhang
  0 siblings, 0 replies; 13+ messages in thread
From: Zijing Zhang @ 2026-01-31 14:22 UTC (permalink / raw)
  To: dirk.behme; +Cc: linux-pci, rust-for-linux, Zijing Zhang

Hi Dirk,

Thanks a lot for the careful review and the pointers!

I agree on the `bindings` usage in the sample. Exposing raw bindgen constants
to users is not ideal.

For v3, I plan to introduce small typed wrappers in `kernel::pci`:

- `CapabilityId` (u8 newtype) for standard capabilities.
- `ExtendedCapabilityId` (u16 newtype) for PCIe extended capabilities.

Both will provide common `const` IDs matching the existing `PCI_CAP_ID_*` /
`PCI_EXT_CAP_ID_*` definitions, plus `from_raw()` / `as_raw()` so drivers can
still use less common IDs without falling back to `bindings`.

I will then update `find_capability()` and `find_ext_capability()` to accept
`CapabilityId` and `ExtendedCapabilityId` respectively.Since the newtypes
provide `from_raw()`.

Real drivers typically know what they need and do not need to validate the PCI
core helpers, thus the intent of the self-check here is mainly to validate PCI
abstraction correctly behaves. This logic would be kept as sample-only private
helpers and refactored.

Also noted on `pdev.as_ref()` becoming obsolete soon, and the upcoming
`register!()` macro; Sample will be updated once those are available in tree.

Best regards,
Zijing

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v3 0/2] rust: pci: add capability lookup helpers
  2026-01-31  4:01 [PATCH 0/2] rust: pci: add capability lookup helpers Zijing Zhang
                   ` (2 preceding siblings ...)
  2026-01-31  9:51 ` [PATCH v2 0/2] rust: pci: add capability lookup helpers Zijing Zhang
@ 2026-01-31 15:17 ` Zijing Zhang
  2026-01-31 15:17   ` [PATCH v3 1/2] " Zijing Zhang
  2026-01-31 15:17   ` [PATCH v3 2/2] samples: rust: pci: exercise capability lookup Zijing Zhang
  3 siblings, 2 replies; 13+ messages in thread
From: Zijing Zhang @ 2026-01-31 15:17 UTC (permalink / raw)
  To: dakr, linux-pci, rust-for-linux
  Cc: bhelgaas, kwilczynski, ojeda, gary, Zijing Zhang

This series adds small helpers to locate PCI capabilities from Rust.

It introduces `pci::Device::{find_capability, find_ext_capability}`, thin wrappers
around the PCI core helpers (`pci_find_capability()` and `pci_find_ext_capability()`),
returning the config-space offset when present.

Capability IDs are wrapped in `pci::CapabilityId` and `pci::ExtendedCapabilityId`
(newtypes with `from_raw()` plus a set of common constants), so users do not need
to import `bindings` in order to use the helpers.

An in-tree user is added to the Rust PCI driver sample to exercise the new API.

Testing

Build:
  - x86_64 defconfig-based kernel with Rust enabled
    (out-of-tree build: `make O=...`).
  - `CONFIG_SAMPLES_RUST=y`
  - `CONFIG_SAMPLE_RUST_DRIVER_PCI=y`

Runtime:
  - QEMU x86_64 with `-device pci-testdev`

v2:
- Run rustfmt on samples/rust/rust_driver_pci.rs to fix rustfmtcheck.

v3:
- Base on pci/next.
- Add `CapabilityId`/`ExtendedCapabilityId` and switch `find_*capability()` to use them.
- Document the common ID constants.
- Update the sample to use typed IDs and exercise the new helpers.


Zijing Zhang (2):
  rust: pci: add capability lookup helpers
  samples: rust: pci: exercise capability lookup

 rust/kernel/pci.rs              | 149 ++++++++++++++++++++++++++++++++
 samples/rust/rust_driver_pci.rs |  31 +++++++
 2 files changed, 180 insertions(+)


base-commit: ff0e2f679ab0de50a2e9e88fabc1026bc3be04ba
-- 
2.52.0


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v3 1/2] rust: pci: add capability lookup helpers
  2026-01-31 15:17 ` [PATCH v3 0/2] rust: pci: add capability lookup helpers Zijing Zhang
@ 2026-01-31 15:17   ` Zijing Zhang
  2026-02-01  6:24     ` Dirk Behme
  2026-01-31 15:17   ` [PATCH v3 2/2] samples: rust: pci: exercise capability lookup Zijing Zhang
  1 sibling, 1 reply; 13+ messages in thread
From: Zijing Zhang @ 2026-01-31 15:17 UTC (permalink / raw)
  To: dakr, linux-pci, rust-for-linux
  Cc: bhelgaas, kwilczynski, ojeda, gary, Zijing Zhang

Add thin wrappers around pci_find_capability() and
pci_find_ext_capability().

The helpers return the capability offset in config space, or None if the
capability is not present.

Introduce CapabilityId and ExtendedCapabilityId newtypes so drivers can use
named constants without importing raw bindgen constants, while still
allowing uncommon IDs via from_raw().

Signed-off-by: Zijing Zhang <zijing.zhang@ry.rs>
---
 rust/kernel/pci.rs | 149 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 149 insertions(+)

diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs
index 82e128431f08..213ff856f538 100644
--- a/rust/kernel/pci.rs
+++ b/rust/kernel/pci.rs
@@ -41,6 +41,118 @@
     Vendor, //
 };
 pub use self::io::Bar;
+
+/// A standard PCI capability ID.
+///
+/// This is a thin wrapper around the underlying numeric capability ID.
+#[repr(transparent)]
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub struct CapabilityId(u8);
+
+impl CapabilityId {
+    /// Creates a [`CapabilityId`] from a raw value.
+    #[inline]
+    pub const fn from_raw(id: u8) -> Self {
+        Self(id)
+    }
+
+    /// Returns the raw value.
+    #[inline]
+    pub const fn as_raw(self) -> u8 {
+        self.0
+    }
+
+    /// Power Management.
+    pub const PM: Self = Self(bindings::PCI_CAP_ID_PM as u8);
+    /// Accelerated Graphics Port.
+    pub const AGP: Self = Self(bindings::PCI_CAP_ID_AGP as u8);
+    /// Vital Product Data.
+    pub const VPD: Self = Self(bindings::PCI_CAP_ID_VPD as u8);
+    /// Slot Identification.
+    pub const SLOTID: Self = Self(bindings::PCI_CAP_ID_SLOTID as u8);
+    /// Message Signalled Interrupts.
+    pub const MSI: Self = Self(bindings::PCI_CAP_ID_MSI as u8);
+    /// CompactPCI HotSwap.
+    pub const CHSWP: Self = Self(bindings::PCI_CAP_ID_CHSWP as u8);
+    /// PCI-X.
+    pub const PCIX: Self = Self(bindings::PCI_CAP_ID_PCIX as u8);
+    /// HyperTransport.
+    pub const HT: Self = Self(bindings::PCI_CAP_ID_HT as u8);
+    /// Vendor-Specific.
+    pub const VNDR: Self = Self(bindings::PCI_CAP_ID_VNDR as u8);
+    /// Debug port.
+    pub const DBG: Self = Self(bindings::PCI_CAP_ID_DBG as u8);
+    /// CompactPCI Central Resource Control.
+    pub const CCRC: Self = Self(bindings::PCI_CAP_ID_CCRC as u8);
+    /// PCI Standard Hot-Plug Controller.
+    pub const SHPC: Self = Self(bindings::PCI_CAP_ID_SHPC as u8);
+    /// Bridge subsystem vendor/device ID.
+    pub const SSVID: Self = Self(bindings::PCI_CAP_ID_SSVID as u8);
+    /// AGP 8x.
+    pub const AGP3: Self = Self(bindings::PCI_CAP_ID_AGP3 as u8);
+    /// Secure Device.
+    pub const SECDEV: Self = Self(bindings::PCI_CAP_ID_SECDEV as u8);
+    /// PCI Express.
+    pub const EXP: Self = Self(bindings::PCI_CAP_ID_EXP as u8);
+    /// MSI-X.
+    pub const MSIX: Self = Self(bindings::PCI_CAP_ID_MSIX as u8);
+    /// Serial ATA Data/Index Configuration.
+    pub const SATA: Self = Self(bindings::PCI_CAP_ID_SATA as u8);
+    /// PCI Advanced Features.
+    pub const AF: Self = Self(bindings::PCI_CAP_ID_AF as u8);
+    /// PCI Enhanced Allocation.
+    pub const EA: Self = Self(bindings::PCI_CAP_ID_EA as u8);
+}
+
+/// A PCIe extended capability ID.
+///
+/// This is a thin wrapper around the underlying numeric capability ID.
+#[repr(transparent)]
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub struct ExtendedCapabilityId(u16);
+
+impl ExtendedCapabilityId {
+    /// Creates an [`ExtendedCapabilityId`] from a raw value.
+    #[inline]
+    pub const fn from_raw(id: u16) -> Self {
+        Self(id)
+    }
+
+    /// Returns the raw value.
+    #[inline]
+    pub const fn as_raw(self) -> u16 {
+        self.0
+    }
+
+    /// Advanced Error Reporting.
+    pub const ERR: Self = Self(bindings::PCI_EXT_CAP_ID_ERR as u16);
+    /// Virtual Channel.
+    pub const VC: Self = Self(bindings::PCI_EXT_CAP_ID_VC as u16);
+    /// Device Serial Number.
+    pub const DSN: Self = Self(bindings::PCI_EXT_CAP_ID_DSN as u16);
+    /// Vendor-Specific Extended Capability.
+    pub const VNDR: Self = Self(bindings::PCI_EXT_CAP_ID_VNDR as u16);
+    /// Access Control Services.
+    pub const ACS: Self = Self(bindings::PCI_EXT_CAP_ID_ACS as u16);
+    /// Alternate Routing-ID Interpretation.
+    pub const ARI: Self = Self(bindings::PCI_EXT_CAP_ID_ARI as u16);
+    /// Address Translation Services.
+    pub const ATS: Self = Self(bindings::PCI_EXT_CAP_ID_ATS as u16);
+    /// Single Root I/O Virtualization.
+    pub const SRIOV: Self = Self(bindings::PCI_EXT_CAP_ID_SRIOV as u16);
+    /// Resizable BAR.
+    pub const REBAR: Self = Self(bindings::PCI_EXT_CAP_ID_REBAR as u16);
+    /// Latency Tolerance Reporting.
+    pub const LTR: Self = Self(bindings::PCI_EXT_CAP_ID_LTR as u16);
+    /// Downstream Port Containment.
+    pub const DPC: Self = Self(bindings::PCI_EXT_CAP_ID_DPC as u16);
+    /// L1 PM Substates.
+    pub const L1SS: Self = Self(bindings::PCI_EXT_CAP_ID_L1SS as u16);
+    /// Precision Time Measurement.
+    pub const PTM: Self = Self(bindings::PCI_EXT_CAP_ID_PTM as u16);
+    /// Designated Vendor-Specific Extended Capability.
+    pub const DVSEC: Self = Self(bindings::PCI_EXT_CAP_ID_DVSEC as u16);
+}
 pub use self::irq::{
     IrqType,
     IrqTypes,
@@ -427,6 +539,43 @@ pub fn pci_class(&self) -> Class {
         // SAFETY: `self.as_raw` is a valid pointer to a `struct pci_dev`.
         Class::from_raw(unsafe { (*self.as_raw()).class })
     }
+
+    /// Finds a PCI capability by ID and returns its config-space offset.
+    ///
+    /// Returns `None` if the capability is not present.
+    ///
+    /// This is a thin wrapper around `pci_find_capability()`.
+    #[inline]
+    pub fn find_capability(&self, id: CapabilityId) -> Option<u8> {
+        // SAFETY: By its type invariant `self.as_raw` is always a valid pointer to a
+        // `struct pci_dev`.
+        let offset = unsafe { bindings::pci_find_capability(self.as_raw(), id.as_raw().into()) };
+
+        if offset == 0 {
+            None
+        } else {
+            Some(offset)
+        }
+    }
+
+    /// Finds an extended capability by ID and returns its config-space offset.
+    ///
+    /// Returns `None` if the capability is not present.
+    ///
+    /// This is a thin wrapper around `pci_find_ext_capability()`.
+    #[inline]
+    pub fn find_ext_capability(&self, id: ExtendedCapabilityId) -> Option<u16> {
+        // SAFETY: By its type invariant `self.as_raw` is always a valid pointer to a
+        // `struct pci_dev`.
+        let offset =
+            unsafe { bindings::pci_find_ext_capability(self.as_raw(), id.as_raw().into()) };
+
+        if offset == 0 {
+            None
+        } else {
+            Some(offset)
+        }
+    }
 }
 
 impl Device<device::Core> {
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v3 2/2] samples: rust: pci: exercise capability lookup
  2026-01-31 15:17 ` [PATCH v3 0/2] rust: pci: add capability lookup helpers Zijing Zhang
  2026-01-31 15:17   ` [PATCH v3 1/2] " Zijing Zhang
@ 2026-01-31 15:17   ` Zijing Zhang
  1 sibling, 0 replies; 13+ messages in thread
From: Zijing Zhang @ 2026-01-31 15:17 UTC (permalink / raw)
  To: dakr, linux-pci, rust-for-linux
  Cc: bhelgaas, kwilczynski, ojeda, gary, Zijing Zhang

Use the new pci::Device capability helpers to locate a few common
capabilities (standard and extended).

Also try a best-effort self-check to exercise the `Some(offset)` path
when the device advertises a standard capability list or an ext-cap header.

Signed-off-by: Zijing Zhang <zijing.zhang@ry.rs>
---
 samples/rust/rust_driver_pci.rs | 31 +++++++++++++++++++++++++++++++
 1 file changed, 31 insertions(+)

diff --git a/samples/rust/rust_driver_pci.rs b/samples/rust/rust_driver_pci.rs
index 5823787bea8e..aea7d5cbe421 100644
--- a/samples/rust/rust_driver_pci.rs
+++ b/samples/rust/rust_driver_pci.rs
@@ -58,6 +58,35 @@ fn testdev(index: &TestIndex, bar: &Bar0) -> Result<u32> {
 
         Ok(bar.read32(Regs::COUNT))
     }
+
+    fn log_capabilities(pdev: &pci::Device<Core>) {
+        for (name, id) in [
+            ("PM", pci::CapabilityId::PM),
+            ("MSI", pci::CapabilityId::MSI),
+            ("PCIe", pci::CapabilityId::EXP),
+        ] {
+            if let Some(pos) = pdev.find_capability(id) {
+                dev_info!(pdev.as_ref(), "pci-testdev {name} cap @ 0x{:02x}\n", pos);
+            } else {
+                dev_info!(pdev.as_ref(), "pci-testdev has no {name} capability\n");
+            }
+        }
+
+        for (name, id) in [
+            ("DSN", pci::ExtendedCapabilityId::DSN),
+            ("SR-IOV", pci::ExtendedCapabilityId::SRIOV),
+        ] {
+            if let Some(pos) = pdev.find_ext_capability(id) {
+                dev_info!(
+                    pdev.as_ref(),
+                    "pci-testdev {name} ext cap @ 0x{:04x}\n",
+                    pos
+                );
+            } else {
+                dev_info!(pdev.as_ref(), "pci-testdev has no {name} ext capability\n");
+            }
+        }
+    }
 }
 
 impl pci::Driver for SampleDriver {
@@ -89,6 +118,8 @@ fn probe(pdev: &pci::Device<Core>, info: &Self::IdInfo) -> impl PinInit<Self, Er
                         "pci-testdev data-match count: {}\n",
                         Self::testdev(info, bar)?
                     );
+
+                    Self::log_capabilities(pdev);
                 },
                 pdev: pdev.into(),
             }))
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH v3 1/2] rust: pci: add capability lookup helpers
  2026-01-31 15:17   ` [PATCH v3 1/2] " Zijing Zhang
@ 2026-02-01  6:24     ` Dirk Behme
  0 siblings, 0 replies; 13+ messages in thread
From: Dirk Behme @ 2026-02-01  6:24 UTC (permalink / raw)
  To: Zijing Zhang, dakr, linux-pci, rust-for-linux
  Cc: bhelgaas, kwilczynski, ojeda, gary

Hi Zijing,

some quite minor stylistic nits below:

On 31.01.26 16:17, Zijing Zhang wrote:
> Add thin wrappers around pci_find_capability() and
> pci_find_ext_capability().

pci_find_capability() -> `pci_find_capability()`

Same for the other cases and the 2nd commit message.

> The helpers return the capability offset in config space, or None if the
> capability is not present.
> 
> Introduce CapabilityId and ExtendedCapabilityId newtypes so drivers can use
> named constants without importing raw bindgen constants, while still
> allowing uncommon IDs via from_raw().
> 
> Signed-off-by: Zijing Zhang <zijing.zhang@ry.rs>
> ---
>  rust/kernel/pci.rs | 149 +++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 149 insertions(+)
> 
> diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs
> index 82e128431f08..213ff856f538 100644
> --- a/rust/kernel/pci.rs
> +++ b/rust/kernel/pci.rs
> @@ -41,6 +41,118 @@
>      Vendor, //
>  };
>  pub use self::io::Bar;
> +
> +/// A standard PCI capability ID.
> +///
> +/// This is a thin wrapper around the underlying numeric capability ID.
> +#[repr(transparent)]
> +#[derive(Clone, Copy, Debug, Eq, PartialEq)]
> +pub struct CapabilityId(u8);
> +
> +impl CapabilityId {
> +    /// Creates a [`CapabilityId`] from a raw value.
> +    #[inline]
> +    pub const fn from_raw(id: u8) -> Self {
> +        Self(id)
> +    }
> +
> +    /// Returns the raw value.
> +    #[inline]
> +    pub const fn as_raw(self) -> u8 {
> +        self.0
> +    }
> +
> +    /// Power Management.
> +    pub const PM: Self = Self(bindings::PCI_CAP_ID_PM as u8);
> +    /// Accelerated Graphics Port.
> +    pub const AGP: Self = Self(bindings::PCI_CAP_ID_AGP as u8);
> +    /// Vital Product Data.
> +    pub const VPD: Self = Self(bindings::PCI_CAP_ID_VPD as u8);
> +    /// Slot Identification.
> +    pub const SLOTID: Self = Self(bindings::PCI_CAP_ID_SLOTID as u8);
> +    /// Message Signalled Interrupts.
> +    pub const MSI: Self = Self(bindings::PCI_CAP_ID_MSI as u8);
> +    /// CompactPCI HotSwap.
> +    pub const CHSWP: Self = Self(bindings::PCI_CAP_ID_CHSWP as u8);
> +    /// PCI-X.
> +    pub const PCIX: Self = Self(bindings::PCI_CAP_ID_PCIX as u8);
> +    /// HyperTransport.
> +    pub const HT: Self = Self(bindings::PCI_CAP_ID_HT as u8);
> +    /// Vendor-Specific.
> +    pub const VNDR: Self = Self(bindings::PCI_CAP_ID_VNDR as u8);
> +    /// Debug port.
> +    pub const DBG: Self = Self(bindings::PCI_CAP_ID_DBG as u8);
> +    /// CompactPCI Central Resource Control.
> +    pub const CCRC: Self = Self(bindings::PCI_CAP_ID_CCRC as u8);
> +    /// PCI Standard Hot-Plug Controller.
> +    pub const SHPC: Self = Self(bindings::PCI_CAP_ID_SHPC as u8);
> +    /// Bridge subsystem vendor/device ID.
> +    pub const SSVID: Self = Self(bindings::PCI_CAP_ID_SSVID as u8);
> +    /// AGP 8x.
> +    pub const AGP3: Self = Self(bindings::PCI_CAP_ID_AGP3 as u8);
> +    /// Secure Device.
> +    pub const SECDEV: Self = Self(bindings::PCI_CAP_ID_SECDEV as u8);
> +    /// PCI Express.
> +    pub const EXP: Self = Self(bindings::PCI_CAP_ID_EXP as u8);
> +    /// MSI-X.
> +    pub const MSIX: Self = Self(bindings::PCI_CAP_ID_MSIX as u8);
> +    /// Serial ATA Data/Index Configuration.
> +    pub const SATA: Self = Self(bindings::PCI_CAP_ID_SATA as u8);
> +    /// PCI Advanced Features.
> +    pub const AF: Self = Self(bindings::PCI_CAP_ID_AF as u8);
> +    /// PCI Enhanced Allocation.
> +    pub const EA: Self = Self(bindings::PCI_CAP_ID_EA as u8);
> +}
> +
> +/// A PCIe extended capability ID.
> +///
> +/// This is a thin wrapper around the underlying numeric capability ID.
> +#[repr(transparent)]
> +#[derive(Clone, Copy, Debug, Eq, PartialEq)]
> +pub struct ExtendedCapabilityId(u16);
> +
> +impl ExtendedCapabilityId {
> +    /// Creates an [`ExtendedCapabilityId`] from a raw value.
> +    #[inline]
> +    pub const fn from_raw(id: u16) -> Self {
> +        Self(id)
> +    }
> +
> +    /// Returns the raw value.
> +    #[inline]
> +    pub const fn as_raw(self) -> u16 {
> +        self.0
> +    }
> +
> +    /// Advanced Error Reporting.
> +    pub const ERR: Self = Self(bindings::PCI_EXT_CAP_ID_ERR as u16);
> +    /// Virtual Channel.
> +    pub const VC: Self = Self(bindings::PCI_EXT_CAP_ID_VC as u16);
> +    /// Device Serial Number.
> +    pub const DSN: Self = Self(bindings::PCI_EXT_CAP_ID_DSN as u16);
> +    /// Vendor-Specific Extended Capability.
> +    pub const VNDR: Self = Self(bindings::PCI_EXT_CAP_ID_VNDR as u16);
> +    /// Access Control Services.
> +    pub const ACS: Self = Self(bindings::PCI_EXT_CAP_ID_ACS as u16);
> +    /// Alternate Routing-ID Interpretation.
> +    pub const ARI: Self = Self(bindings::PCI_EXT_CAP_ID_ARI as u16);
> +    /// Address Translation Services.
> +    pub const ATS: Self = Self(bindings::PCI_EXT_CAP_ID_ATS as u16);
> +    /// Single Root I/O Virtualization.
> +    pub const SRIOV: Self = Self(bindings::PCI_EXT_CAP_ID_SRIOV as u16);
> +    /// Resizable BAR.
> +    pub const REBAR: Self = Self(bindings::PCI_EXT_CAP_ID_REBAR as u16);
> +    /// Latency Tolerance Reporting.
> +    pub const LTR: Self = Self(bindings::PCI_EXT_CAP_ID_LTR as u16);
> +    /// Downstream Port Containment.
> +    pub const DPC: Self = Self(bindings::PCI_EXT_CAP_ID_DPC as u16);
> +    /// L1 PM Substates.
> +    pub const L1SS: Self = Self(bindings::PCI_EXT_CAP_ID_L1SS as u16);
> +    /// Precision Time Measurement.
> +    pub const PTM: Self = Self(bindings::PCI_EXT_CAP_ID_PTM as u16);
> +    /// Designated Vendor-Specific Extended Capability.
> +    pub const DVSEC: Self = Self(bindings::PCI_EXT_CAP_ID_DVSEC as u16);
> +}

Missing empty line before `pub use self::irq` below?

>  pub use self::irq::{
>      IrqType,
>      IrqTypes,
> @@ -427,6 +539,43 @@ pub fn pci_class(&self) -> Class {
>          // SAFETY: `self.as_raw` is a valid pointer to a `struct pci_dev`.
>          Class::from_raw(unsafe { (*self.as_raw()).class })
>      }
> +
> +    /// Finds a PCI capability by ID and returns its config-space offset.
> +    ///
> +    /// Returns `None` if the capability is not present.
> +    ///
> +    /// This is a thin wrapper around `pci_find_capability()`.
> +    #[inline]
> +    pub fn find_capability(&self, id: CapabilityId) -> Option<u8> {

Depending on the future use of this function (and
`find_ext_capability()` below) would it be an option to introduce a
type for the returned u8/u16 offset?

> +        // SAFETY: By its type invariant `self.as_raw` is always a valid pointer to a
> +        // `struct pci_dev`.
> +        let offset = unsafe { bindings::pci_find_capability(self.as_raw(), id.as_raw().into()) };
> +
> +        if offset == 0 {
> +            None
> +        } else {
> +            Some(offset)
> +        }

In

https://lore.kernel.org/rust-for-linux/CANiq72kiscT5euAUjcSzvxMzM9Hdj8aQGeUN_pVF-vHf3DhBuQ@mail.gmail.com/

it was proposed to use the style

if offset == 0 {
    return None;
}

Some(offset)


Best regards

Dirk


> +    }
> +
> +    /// Finds an extended capability by ID and returns its config-space offset.
> +    ///
> +    /// Returns `None` if the capability is not present.
> +    ///
> +    /// This is a thin wrapper around `pci_find_ext_capability()`.
> +    #[inline]
> +    pub fn find_ext_capability(&self, id: ExtendedCapabilityId) -> Option<u16> {
> +        // SAFETY: By its type invariant `self.as_raw` is always a valid pointer to a
> +        // `struct pci_dev`.
> +        let offset =
> +            unsafe { bindings::pci_find_ext_capability(self.as_raw(), id.as_raw().into()) };
> +
> +        if offset == 0 {
> +            None
> +        } else {
> +            Some(offset)
> +        }
> +    }
>  }
>  
>  impl Device<device::Core> {


^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2026-02-01  6:24 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-01-31  4:01 [PATCH 0/2] rust: pci: add capability lookup helpers Zijing Zhang
2026-01-31  4:01 ` [PATCH 1/2] " Zijing Zhang
2026-01-31  4:02 ` [PATCH 2/2] samples: rust: pci: exercise capability lookup Zijing Zhang
2026-01-31  9:17   ` kernel test robot
2026-01-31 13:34   ` Dirk Behme
2026-01-31 14:22     ` Zijing Zhang
2026-01-31  9:51 ` [PATCH v2 0/2] rust: pci: add capability lookup helpers Zijing Zhang
2026-01-31  9:51   ` [PATCH v2 1/2] " Zijing Zhang
2026-01-31  9:51   ` [PATCH v2 2/2] samples: rust: pci: exercise capability lookup Zijing Zhang
2026-01-31 15:17 ` [PATCH v3 0/2] rust: pci: add capability lookup helpers Zijing Zhang
2026-01-31 15:17   ` [PATCH v3 1/2] " Zijing Zhang
2026-02-01  6:24     ` Dirk Behme
2026-01-31 15:17   ` [PATCH v3 2/2] samples: rust: pci: exercise capability lookup Zijing Zhang

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox