return container_of(r, struct rdt_hw_resource, r_resctrl);
  }
  
 -int parse_cbm(struct rdt_parse_data *data, struct resctrl_schema *s,
 -            struct rdt_ctrl_domain *d);
 -int parse_bw(struct rdt_parse_data *data, struct resctrl_schema *s,
 -           struct rdt_ctrl_domain *d);
 -
  extern struct mutex rdtgroup_mutex;
  
+ static inline const char *rdt_kn_name(const struct kernfs_node *kn)
+ {
+       return rcu_dereference_check(kn->name, lockdep_is_held(&rdtgroup_mutex));
+ }
+ 
  extern struct rdt_hw_resource rdt_resources_all[];
  extern struct rdtgroup rdtgroup_default;
  extern struct dentry *debugfs_resctrl;
 
  static int rdt_default_ctrl_show(struct kernfs_open_file *of,
                             struct seq_file *seq, void *v)
  {
-       struct resctrl_schema *s = of->kn->parent->priv;
+       struct resctrl_schema *s = rdt_kn_parent_priv(of->kn);
        struct rdt_resource *r = s->res;
  
 -      seq_printf(seq, "%x\n", r->default_ctrl);
 +      seq_printf(seq, "%x\n", resctrl_get_default_ctrl(r));
        return 0;
  }
  
  static int rdt_thread_throttle_mode_show(struct kernfs_open_file *of,
                                         struct seq_file *seq, void *v)
  {
-       struct resctrl_schema *s = of->kn->parent->priv;
+       struct resctrl_schema *s = rdt_kn_parent_priv(of->kn);
        struct rdt_resource *r = s->res;
  
 -      if (r->membw.throttle_mode == THREAD_THROTTLE_PER_THREAD)
 +      switch (r->membw.throttle_mode) {
 +      case THREAD_THROTTLE_PER_THREAD:
                seq_puts(seq, "per-thread\n");
 -      else
 +              return 0;
 +      case THREAD_THROTTLE_MAX:
                seq_puts(seq, "max\n");
 +              return 0;
 +      case THREAD_THROTTLE_UNDEFINED:
 +              seq_puts(seq, "undefined\n");
 +              return 0;
 +      }
 +
 +      WARN_ON_ONCE(1);
  
        return 0;
  }
 
  static DEVICE_ATTR(pcie_replay_count, 0444,
                amdgpu_device_get_pcie_replay_count, NULL);
  
 +static int amdgpu_device_attr_sysfs_init(struct amdgpu_device *adev)
 +{
 +      int ret = 0;
 +
 +      if (!amdgpu_sriov_vf(adev))
 +              ret = sysfs_create_file(&adev->dev->kobj,
 +                                      &dev_attr_pcie_replay_count.attr);
 +
 +      return ret;
 +}
 +
 +static void amdgpu_device_attr_sysfs_fini(struct amdgpu_device *adev)
 +{
 +      if (!amdgpu_sriov_vf(adev))
 +              sysfs_remove_file(&adev->dev->kobj,
 +                                &dev_attr_pcie_replay_count.attr);
 +}
 +
  static ssize_t amdgpu_sysfs_reg_state_get(struct file *f, struct kobject *kobj,
-                                         struct bin_attribute *attr, char *buf,
+                                         const struct bin_attribute *attr, char *buf,
                                          loff_t ppos, size_t count)
  {
        struct device *dev = kobj_to_dev(kobj);
 
--- /dev/null
- use kernel::{bindings, c_str, pci, prelude::*};
 +// SPDX-License-Identifier: GPL-2.0
 +
-     fn probe(pdev: &mut pci::Device, _info: &Self::IdInfo) -> Result<Pin<KBox<Self>>> {
++use kernel::{bindings, c_str, device::Core, pci, prelude::*};
 +
 +use crate::gpu::Gpu;
 +
 +#[pin_data]
 +pub(crate) struct NovaCore {
 +    #[pin]
 +    pub(crate) gpu: Gpu,
 +}
 +
 +const BAR0_SIZE: usize = 8;
 +pub(crate) type Bar0 = pci::Bar<BAR0_SIZE>;
 +
 +kernel::pci_device_table!(
 +    PCI_TABLE,
 +    MODULE_PCI_TABLE,
 +    <NovaCore as pci::Driver>::IdInfo,
 +    [(
 +        pci::DeviceId::from_id(bindings::PCI_VENDOR_ID_NVIDIA, bindings::PCI_ANY_ID as _),
 +        ()
 +    )]
 +);
 +
 +impl pci::Driver for NovaCore {
 +    type IdInfo = ();
 +    const ID_TABLE: pci::IdTable<Self::IdInfo> = &PCI_TABLE;
 +
++    fn probe(pdev: &pci::Device<Core>, _info: &Self::IdInfo) -> Result<Pin<KBox<Self>>> {
 +        dev_dbg!(pdev.as_ref(), "Probe Nova Core GPU driver.\n");
 +
 +        pdev.enable_device_mem()?;
 +        pdev.set_master();
 +
 +        let bar = pdev.iomap_region_sized::<BAR0_SIZE>(0, c_str!("nova-core/bar0"))?;
 +
 +        let this = KBox::pin_init(
 +            try_pin_init!(Self {
 +                gpu <- Gpu::new(pdev, bar)?,
 +            }),
 +            GFP_KERNEL,
 +        )?;
 +
 +        Ok(this)
 +    }
 +}
 
--- /dev/null
-         Self(bar.readl(BOOT0_OFFSET))
 +// SPDX-License-Identifier: GPL-2.0
 +
 +use crate::driver::Bar0;
 +
 +// TODO
 +//
 +// Create register definitions via generic macros. See task "Generic register
 +// abstraction" in Documentation/gpu/nova/core/todo.rst.
 +
 +const BOOT0_OFFSET: usize = 0x00000000;
 +
 +// 3:0 - chipset minor revision
 +const BOOT0_MINOR_REV_SHIFT: u8 = 0;
 +const BOOT0_MINOR_REV_MASK: u32 = 0x0000000f;
 +
 +// 7:4 - chipset major revision
 +const BOOT0_MAJOR_REV_SHIFT: u8 = 4;
 +const BOOT0_MAJOR_REV_MASK: u32 = 0x000000f0;
 +
 +// 23:20 - chipset implementation Identifier (depends on architecture)
 +const BOOT0_IMPL_SHIFT: u8 = 20;
 +const BOOT0_IMPL_MASK: u32 = 0x00f00000;
 +
 +// 28:24 - chipset architecture identifier
 +const BOOT0_ARCH_MASK: u32 = 0x1f000000;
 +
 +// 28:20 - chipset identifier (virtual register field combining BOOT0_IMPL and
 +//         BOOT0_ARCH)
 +const BOOT0_CHIPSET_SHIFT: u8 = BOOT0_IMPL_SHIFT;
 +const BOOT0_CHIPSET_MASK: u32 = BOOT0_IMPL_MASK | BOOT0_ARCH_MASK;
 +
 +#[derive(Copy, Clone)]
 +pub(crate) struct Boot0(u32);
 +
 +impl Boot0 {
 +    #[inline]
 +    pub(crate) fn read(bar: &Bar0) -> Self {
++        Self(bar.read32(BOOT0_OFFSET))
 +    }
 +
 +    #[inline]
 +    pub(crate) fn chipset(&self) -> u32 {
 +        (self.0 & BOOT0_CHIPSET_MASK) >> BOOT0_CHIPSET_SHIFT
 +    }
 +
 +    #[inline]
 +    pub(crate) fn minor_rev(&self) -> u8 {
 +        ((self.0 & BOOT0_MINOR_REV_MASK) >> BOOT0_MINOR_REV_SHIFT) as u8
 +    }
 +
 +    #[inline]
 +    pub(crate) fn major_rev(&self) -> u8 {
 +        ((self.0 & BOOT0_MAJOR_REV_MASK) >> BOOT0_MAJOR_REV_SHIFT) as u8
 +    }
 +}
 
--- /dev/null
- use kernel::{bindings, dma::CoherentAllocation, pci, prelude::*};
 +// SPDX-License-Identifier: GPL-2.0
 +
 +//! Rust DMA api test (based on QEMU's `pci-testdev`).
 +//!
 +//! To make this driver probe, QEMU must be run with `-device pci-testdev`.
 +
-     pdev: pci::Device,
++use kernel::{bindings, device::Core, dma::CoherentAllocation, pci, prelude::*, types::ARef};
 +
 +struct DmaSampleDriver {
-     fn probe(pdev: &mut pci::Device, _info: &Self::IdInfo) -> Result<Pin<KBox<Self>>> {
++    pdev: ARef<pci::Device>,
 +    ca: CoherentAllocation<MyStruct>,
 +}
 +
 +const TEST_VALUES: [(u32, u32); 5] = [
 +    (0xa, 0xb),
 +    (0xc, 0xd),
 +    (0xe, 0xf),
 +    (0xab, 0xba),
 +    (0xcd, 0xef),
 +];
 +
 +struct MyStruct {
 +    h: u32,
 +    b: u32,
 +}
 +
 +impl MyStruct {
 +    fn new(h: u32, b: u32) -> Self {
 +        Self { h, b }
 +    }
 +}
 +// SAFETY: All bit patterns are acceptable values for `MyStruct`.
 +unsafe impl kernel::transmute::AsBytes for MyStruct {}
 +// SAFETY: Instances of `MyStruct` have no uninitialized portions.
 +unsafe impl kernel::transmute::FromBytes for MyStruct {}
 +
 +kernel::pci_device_table!(
 +    PCI_TABLE,
 +    MODULE_PCI_TABLE,
 +    <DmaSampleDriver as pci::Driver>::IdInfo,
 +    [(
 +        pci::DeviceId::from_id(bindings::PCI_VENDOR_ID_REDHAT, 0x5),
 +        ()
 +    )]
 +);
 +
 +impl pci::Driver for DmaSampleDriver {
 +    type IdInfo = ();
 +    const ID_TABLE: pci::IdTable<Self::IdInfo> = &PCI_TABLE;
 +
-                 pdev: pdev.clone(),
++    fn probe(pdev: &pci::Device<Core>, _info: &Self::IdInfo) -> Result<Pin<KBox<Self>>> {
 +        dev_info!(pdev.as_ref(), "Probe DMA test driver.\n");
 +
 +        let ca: CoherentAllocation<MyStruct> =
 +            CoherentAllocation::alloc_coherent(pdev.as_ref(), TEST_VALUES.len(), GFP_KERNEL)?;
 +
 +        || -> Result {
 +            for (i, value) in TEST_VALUES.into_iter().enumerate() {
 +                kernel::dma_write!(ca[i] = MyStruct::new(value.0, value.1));
 +            }
 +
 +            Ok(())
 +        }()?;
 +
 +        let drvdata = KBox::new(
 +            Self {
++                pdev: pdev.into(),
 +                ca,
 +            },
 +            GFP_KERNEL,
 +        )?;
 +
 +        Ok(drvdata.into())
 +    }
 +}
 +
 +impl Drop for DmaSampleDriver {
 +    fn drop(&mut self) {
 +        dev_info!(self.pdev.as_ref(), "Unload DMA test driver.\n");
 +
 +        let _ = || -> Result {
 +            for (i, value) in TEST_VALUES.into_iter().enumerate() {
 +                assert_eq!(kernel::dma_read!(self.ca[i].h), value.0);
 +                assert_eq!(kernel::dma_read!(self.ca[i].b), value.1);
 +            }
 +            Ok(())
 +        }();
 +    }
 +}
 +
 +kernel::module_pci_driver! {
 +    type: DmaSampleDriver,
 +    name: "rust_dma",
 +    authors: ["Abdiel Janulgue"],
 +    description: "Rust DMA test",
 +    license: "GPL v2",
 +}