From f2648bb3150a71241a2254aa4ac10680d7f9fb16 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 6 Apr 2026 16:22:55 -0700 Subject: driver core: Replace dev->can_match with dev_can_match() In C, bitfields are not necessarily safe to modify from multiple threads without locking. Switch "can_match" over to the "flags" field so modifications are safe. Cc: Saravana Kannan Reviewed-by: Rafael J. Wysocki (Intel) Reviewed-by: Danilo Krummrich Acked-by: Greg Kroah-Hartman Acked-by: Marek Szyprowski Signed-off-by: Douglas Anderson Link: https://patch.msgid.link/20260406162231.v5.2.I54b3ae6311ff34ad30227659d91bb109911a4aea@changeid Signed-off-by: Danilo Krummrich --- drivers/base/core.c | 10 +++++----- drivers/base/dd.c | 10 +++++----- include/linux/device.h | 9 +++++---- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/drivers/base/core.c b/drivers/base/core.c index bd2ddf2aab50..43bbd1716f37 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -1011,7 +1011,7 @@ static void device_links_missing_supplier(struct device *dev) static bool dev_is_best_effort(struct device *dev) { - return (fw_devlink_best_effort && dev->can_match) || + return (fw_devlink_best_effort && dev_can_match(dev)) || (dev->fwnode && fwnode_test_flag(dev->fwnode, FWNODE_FLAG_BEST_EFFORT)); } @@ -1079,7 +1079,7 @@ int device_links_check_suppliers(struct device *dev) if (dev_is_best_effort(dev) && device_link_test(link, DL_FLAG_INFERRED) && - !link->supplier->can_match) { + !dev_can_match(link->supplier)) { ret = -EAGAIN; continue; } @@ -1370,7 +1370,7 @@ void device_links_driver_bound(struct device *dev) } else if (dev_is_best_effort(dev) && device_link_test(link, DL_FLAG_INFERRED) && link->status != DL_STATE_CONSUMER_PROBE && - !link->supplier->can_match) { + !dev_can_match(link->supplier)) { /* * When dev_is_best_effort() is true, we ignore device * links to suppliers that don't have a driver. If the @@ -1758,7 +1758,7 @@ static int fw_devlink_no_driver(struct device *dev, void *data) { struct device_link *link = to_devlink(dev); - if (!link->supplier->can_match) + if (!dev_can_match(link->supplier)) fw_devlink_relax_link(link); return 0; @@ -3710,7 +3710,7 @@ int device_add(struct device *dev) * match with any driver, don't block its consumers from probing in * case the consumer device is able to operate without this supplier. */ - if (dev->fwnode && fw_devlink_drv_reg_done && !dev->can_match) + if (dev->fwnode && fw_devlink_drv_reg_done && !dev_can_match(dev)) fw_devlink_unblock_consumers(dev); if (parent) diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 1dc1e3528043..bce1e63b4230 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -132,7 +132,7 @@ static DECLARE_WORK(deferred_probe_work, deferred_probe_work_func); void driver_deferred_probe_add(struct device *dev) { - if (!dev->can_match) + if (!dev_can_match(dev)) return; mutex_lock(&deferred_probe_mutex); @@ -849,14 +849,14 @@ static int __driver_probe_device(const struct device_driver *drv, struct device return dev_err_probe(dev, -EPROBE_DEFER, "Device not ready to probe\n"); /* - * Set can_match = true after calling dev_ready_to_probe(), so + * Call dev_set_can_match() after calling dev_ready_to_probe(), so * driver_deferred_probe_add() won't actually add the device to the * deferred probe list when dev_ready_to_probe() returns false. * * When dev_ready_to_probe() returns false, it means that device_add() * will do another probe() attempt for us. */ - dev->can_match = true; + dev_set_can_match(dev); dev_dbg(dev, "bus: '%s': %s: matched device with driver %s\n", drv->bus->name, __func__, drv->name); @@ -1002,7 +1002,7 @@ static int __device_attach_driver(struct device_driver *drv, void *_data) return 0; } else if (ret == -EPROBE_DEFER) { dev_dbg(dev, "Device match requests probe deferral\n"); - dev->can_match = true; + dev_set_can_match(dev); driver_deferred_probe_add(dev); /* * Device can't match with a driver right now, so don't attempt @@ -1254,7 +1254,7 @@ static int __driver_attach(struct device *dev, void *data) return 0; } else if (ret == -EPROBE_DEFER) { dev_dbg(dev, "Device match requests probe deferral\n"); - dev->can_match = true; + dev_set_can_match(dev); driver_deferred_probe_add(dev); /* * Driver could not match with device, but may match with diff --git a/include/linux/device.h b/include/linux/device.h index 9c8fde6a3d86..00113821ebe9 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -512,10 +512,14 @@ struct device_physical_location { * * @DEV_FLAG_READY_TO_PROBE: If set then device_add() has finished enough * initialization that probe could be called. + * @DEV_FLAG_CAN_MATCH: The device has matched with a driver at least once or it + * is in a bus (like AMBA) which can't check for matching drivers + * until other devices probe successfully. * @DEV_FLAG_COUNT: Number of defined struct_device_flags. */ enum struct_device_flags { DEV_FLAG_READY_TO_PROBE = 0, + DEV_FLAG_CAN_MATCH = 1, DEV_FLAG_COUNT }; @@ -602,9 +606,6 @@ enum struct_device_flags { * @state_synced: The hardware state of this device has been synced to match * the software state of this device by calling the driver/bus * sync_state() callback. - * @can_match: The device has matched with a driver at least once or it is in - * a bus (like AMBA) which can't check for matching drivers until - * other devices probe successfully. * @dma_coherent: this particular device is dma coherent, even if the * architecture supports non-coherent devices. * @dma_ops_bypass: If set to %true then the dma_ops are bypassed for the @@ -723,7 +724,6 @@ struct device { bool offline:1; bool of_node_reused:1; bool state_synced:1; - bool can_match:1; #if defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE) || \ defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU) || \ defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU_ALL) @@ -765,6 +765,7 @@ static inline bool dev_test_and_set_##accessor_name(struct device *dev) \ } __create_dev_flag_accessors(ready_to_probe, DEV_FLAG_READY_TO_PROBE); +__create_dev_flag_accessors(can_match, DEV_FLAG_CAN_MATCH); #undef __create_dev_flag_accessors -- cgit v1.2.3 From 7fa1e85cfe6844cd7b09cb8288e5fb68952c88f7 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 6 Apr 2026 16:22:56 -0700 Subject: driver core: Replace dev->dma_iommu with dev_dma_iommu() In C, bitfields are not necessarily safe to modify from multiple threads without locking. Switch "dma_iommu" over to the "flags" field so modifications are safe. Cc: Leon Romanovsky Cc: Robin Murphy Cc: Christoph Hellwig Reviewed-by: Rafael J. Wysocki (Intel) Reviewed-by: Danilo Krummrich Acked-by: Greg Kroah-Hartman Acked-by: Marek Szyprowski Signed-off-by: Douglas Anderson Link: https://patch.msgid.link/20260406162231.v5.3.Id20d5973cbff542fea290e13177e9423f5d81342@changeid Signed-off-by: Danilo Krummrich --- drivers/iommu/dma-iommu.c | 9 ++++++--- drivers/iommu/iommu.c | 5 ++--- include/linux/device.h | 9 ++++----- include/linux/iommu-dma.h | 3 ++- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c index 54d96e847f16..3fdcbbf273fa 100644 --- a/drivers/iommu/dma-iommu.c +++ b/drivers/iommu/dma-iommu.c @@ -2142,18 +2142,21 @@ EXPORT_SYMBOL_GPL(dma_iova_destroy); void iommu_setup_dma_ops(struct device *dev, struct iommu_domain *domain) { + bool dma_iommu; + if (dev_is_pci(dev)) dev->iommu->pci_32bit_workaround = !iommu_dma_forcedac; - dev->dma_iommu = iommu_is_dma_domain(domain); - if (dev->dma_iommu && iommu_dma_init_domain(domain, dev)) + dma_iommu = iommu_is_dma_domain(domain); + dev_assign_dma_iommu(dev, dma_iommu); + if (dma_iommu && iommu_dma_init_domain(domain, dev)) goto out_err; return; out_err: pr_warn("Failed to set up IOMMU for device %s; retaining platform DMA ops\n", dev_name(dev)); - dev->dma_iommu = false; + dev_clear_dma_iommu(dev); } static bool has_msi_cookie(const struct iommu_domain *domain) diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index 61c12ba78206..fccdbaf6dbd5 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -590,9 +590,8 @@ static void iommu_deinit_device(struct device *dev) dev->iommu_group = NULL; module_put(ops->owner); dev_iommu_free(dev); -#ifdef CONFIG_IOMMU_DMA - dev->dma_iommu = false; -#endif + if (IS_ENABLED(CONFIG_IOMMU_DMA)) + dev_clear_dma_iommu(dev); } static struct iommu_domain *pasid_array_entry_to_domain(void *entry) diff --git a/include/linux/device.h b/include/linux/device.h index 00113821ebe9..bb02afb00f05 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -515,11 +515,14 @@ struct device_physical_location { * @DEV_FLAG_CAN_MATCH: The device has matched with a driver at least once or it * is in a bus (like AMBA) which can't check for matching drivers * until other devices probe successfully. + * @DEV_FLAG_DMA_IOMMU: Device is using default IOMMU implementation for DMA and + * doesn't rely on dma_ops structure. * @DEV_FLAG_COUNT: Number of defined struct_device_flags. */ enum struct_device_flags { DEV_FLAG_READY_TO_PROBE = 0, DEV_FLAG_CAN_MATCH = 1, + DEV_FLAG_DMA_IOMMU = 2, DEV_FLAG_COUNT }; @@ -614,8 +617,6 @@ enum struct_device_flags { * for dma allocations. This flag is managed by the dma ops * instance from ->dma_supported. * @dma_skip_sync: DMA sync operations can be skipped for coherent buffers. - * @dma_iommu: Device is using default IOMMU implementation for DMA and - * doesn't rely on dma_ops structure. * @flags: DEV_FLAG_XXX flags. Use atomic bitfield operations to modify. * * At the lowest level, every device in a Linux system is represented by an @@ -735,9 +736,6 @@ struct device { #ifdef CONFIG_DMA_NEED_SYNC bool dma_skip_sync:1; #endif -#ifdef CONFIG_IOMMU_DMA - bool dma_iommu:1; -#endif DECLARE_BITMAP(flags, DEV_FLAG_COUNT); }; @@ -766,6 +764,7 @@ static inline bool dev_test_and_set_##accessor_name(struct device *dev) \ __create_dev_flag_accessors(ready_to_probe, DEV_FLAG_READY_TO_PROBE); __create_dev_flag_accessors(can_match, DEV_FLAG_CAN_MATCH); +__create_dev_flag_accessors(dma_iommu, DEV_FLAG_DMA_IOMMU); #undef __create_dev_flag_accessors diff --git a/include/linux/iommu-dma.h b/include/linux/iommu-dma.h index a92b3ff9b934..060f6e23ab3c 100644 --- a/include/linux/iommu-dma.h +++ b/include/linux/iommu-dma.h @@ -7,12 +7,13 @@ #ifndef _LINUX_IOMMU_DMA_H #define _LINUX_IOMMU_DMA_H +#include #include #ifdef CONFIG_IOMMU_DMA static inline bool use_dma_iommu(struct device *dev) { - return dev->dma_iommu; + return dev_dma_iommu(dev); } #else static inline bool use_dma_iommu(struct device *dev) -- cgit v1.2.3 From d99167df047a12cac636188f994a9e8f1f9779ab Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 6 Apr 2026 16:22:57 -0700 Subject: driver core: Replace dev->dma_skip_sync with dev_dma_skip_sync() In C, bitfields are not necessarily safe to modify from multiple threads without locking. Switch "dma_skip_sync" over to the "flags" field so modifications are safe. Cc: Alexander Lobakin Cc: Eric Dumazet Cc: Christoph Hellwig Reviewed-by: Rafael J. Wysocki (Intel) Reviewed-by: Danilo Krummrich Acked-by: Greg Kroah-Hartman Acked-by: Marek Szyprowski Signed-off-by: Douglas Anderson Link: https://patch.msgid.link/20260406162231.v5.4.Icf072aa4184dd86a88fa8ca195b09d1651984000@changeid Signed-off-by: Danilo Krummrich --- include/linux/device.h | 8 ++++---- include/linux/dma-map-ops.h | 4 ++-- include/linux/dma-mapping.h | 2 +- kernel/dma/mapping.c | 8 ++++---- mm/hmm.c | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/include/linux/device.h b/include/linux/device.h index bb02afb00f05..cc93a5e1e5d2 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -517,12 +517,15 @@ struct device_physical_location { * until other devices probe successfully. * @DEV_FLAG_DMA_IOMMU: Device is using default IOMMU implementation for DMA and * doesn't rely on dma_ops structure. + * @DEV_FLAG_DMA_SKIP_SYNC: DMA sync operations can be skipped for coherent + * buffers. * @DEV_FLAG_COUNT: Number of defined struct_device_flags. */ enum struct_device_flags { DEV_FLAG_READY_TO_PROBE = 0, DEV_FLAG_CAN_MATCH = 1, DEV_FLAG_DMA_IOMMU = 2, + DEV_FLAG_DMA_SKIP_SYNC = 3, DEV_FLAG_COUNT }; @@ -616,7 +619,6 @@ enum struct_device_flags { * and optionall (if the coherent mask is large enough) also * for dma allocations. This flag is managed by the dma ops * instance from ->dma_supported. - * @dma_skip_sync: DMA sync operations can be skipped for coherent buffers. * @flags: DEV_FLAG_XXX flags. Use atomic bitfield operations to modify. * * At the lowest level, every device in a Linux system is represented by an @@ -733,9 +735,6 @@ struct device { #ifdef CONFIG_DMA_OPS_BYPASS bool dma_ops_bypass : 1; #endif -#ifdef CONFIG_DMA_NEED_SYNC - bool dma_skip_sync:1; -#endif DECLARE_BITMAP(flags, DEV_FLAG_COUNT); }; @@ -765,6 +764,7 @@ static inline bool dev_test_and_set_##accessor_name(struct device *dev) \ __create_dev_flag_accessors(ready_to_probe, DEV_FLAG_READY_TO_PROBE); __create_dev_flag_accessors(can_match, DEV_FLAG_CAN_MATCH); __create_dev_flag_accessors(dma_iommu, DEV_FLAG_DMA_IOMMU); +__create_dev_flag_accessors(dma_skip_sync, DEV_FLAG_DMA_SKIP_SYNC); #undef __create_dev_flag_accessors diff --git a/include/linux/dma-map-ops.h b/include/linux/dma-map-ops.h index 6a1832a73cad..9e677a79f3a8 100644 --- a/include/linux/dma-map-ops.h +++ b/include/linux/dma-map-ops.h @@ -240,8 +240,8 @@ static inline void dma_reset_need_sync(struct device *dev) { #ifdef CONFIG_DMA_NEED_SYNC /* Reset it only once so that the function can be called on hotpath */ - if (unlikely(dev->dma_skip_sync)) - dev->dma_skip_sync = false; + if (unlikely(dev_dma_skip_sync(dev))) + dev_clear_dma_skip_sync(dev); #endif } diff --git a/include/linux/dma-mapping.h b/include/linux/dma-mapping.h index db8ab24a54f4..cc0823a99cfd 100644 --- a/include/linux/dma-mapping.h +++ b/include/linux/dma-mapping.h @@ -429,7 +429,7 @@ bool __dma_need_sync(struct device *dev, dma_addr_t dma_addr); static inline bool dma_dev_need_sync(const struct device *dev) { /* Always call DMA sync operations when debugging is enabled */ - return !dev->dma_skip_sync || IS_ENABLED(CONFIG_DMA_API_DEBUG); + return !dev_dma_skip_sync(dev) || IS_ENABLED(CONFIG_DMA_API_DEBUG); } static inline void dma_sync_single_for_cpu(struct device *dev, dma_addr_t addr, diff --git a/kernel/dma/mapping.c b/kernel/dma/mapping.c index 23ed8eb9233e..a81a316971ff 100644 --- a/kernel/dma/mapping.c +++ b/kernel/dma/mapping.c @@ -476,7 +476,7 @@ bool dma_need_unmap(struct device *dev) { if (!dma_map_direct(dev, get_dma_ops(dev))) return true; - if (!dev->dma_skip_sync) + if (!dev_dma_skip_sync(dev)) return true; return IS_ENABLED(CONFIG_DMA_API_DEBUG); } @@ -492,16 +492,16 @@ static void dma_setup_need_sync(struct device *dev) * mapping, if any. During the device initialization, it's * enough to check only for the DMA coherence. */ - dev->dma_skip_sync = dev_is_dma_coherent(dev); + dev_assign_dma_skip_sync(dev, dev_is_dma_coherent(dev)); else if (!ops->sync_single_for_device && !ops->sync_single_for_cpu && !ops->sync_sg_for_device && !ops->sync_sg_for_cpu) /* * Synchronization is not possible when none of DMA sync ops * is set. */ - dev->dma_skip_sync = true; + dev_set_dma_skip_sync(dev); else - dev->dma_skip_sync = false; + dev_clear_dma_skip_sync(dev); } #else /* !CONFIG_DMA_NEED_SYNC */ static inline void dma_setup_need_sync(struct device *dev) { } diff --git a/mm/hmm.c b/mm/hmm.c index 5955f2f0c83d..c72c9ddfdb2f 100644 --- a/mm/hmm.c +++ b/mm/hmm.c @@ -709,7 +709,7 @@ int hmm_dma_map_alloc(struct device *dev, struct hmm_dma_map *map, * best approximation to ensure no swiotlb buffering happens. */ #ifdef CONFIG_DMA_NEED_SYNC - dma_need_sync = !dev->dma_skip_sync; + dma_need_sync = !dev_dma_skip_sync(dev); #endif /* CONFIG_DMA_NEED_SYNC */ if (dma_need_sync || dma_addressing_limited(dev)) return -EOPNOTSUPP; -- cgit v1.2.3 From 9ad67f494ba6bdb4e7bb612640158f645932dca3 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 6 Apr 2026 16:22:58 -0700 Subject: driver core: Replace dev->dma_ops_bypass with dev_dma_ops_bypass() In C, bitfields are not necessarily safe to modify from multiple threads without locking. Switch "dma_ops_bypass" over to the "flags" field so modifications are safe. Cc: Christoph Hellwig Cc: Alexey Kardashevskiy Reviewed-by: Rafael J. Wysocki (Intel) Reviewed-by: Danilo Krummrich Acked-by: Greg Kroah-Hartman Acked-by: Marek Szyprowski Signed-off-by: Douglas Anderson Link: https://patch.msgid.link/20260406162231.v5.5.If62b84471ef2c85e7ad250f0468867d6dba965ab@changeid Signed-off-by: Danilo Krummrich --- arch/powerpc/kernel/dma-iommu.c | 8 ++++---- include/linux/device.h | 15 +++++++-------- kernel/dma/mapping.c | 4 +--- 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/arch/powerpc/kernel/dma-iommu.c b/arch/powerpc/kernel/dma-iommu.c index 8b4de508d2eb..1f8b5a0f69be 100644 --- a/arch/powerpc/kernel/dma-iommu.c +++ b/arch/powerpc/kernel/dma-iommu.c @@ -67,7 +67,7 @@ bool arch_dma_unmap_sg_direct(struct device *dev, struct scatterlist *sg, } bool arch_dma_alloc_direct(struct device *dev) { - if (dev->dma_ops_bypass && dev->bus_dma_limit) + if (dev_dma_ops_bypass(dev) && dev->bus_dma_limit) return true; return false; @@ -75,7 +75,7 @@ bool arch_dma_alloc_direct(struct device *dev) bool arch_dma_free_direct(struct device *dev, dma_addr_t dma_handle) { - if (!dev->dma_ops_bypass || !dev->bus_dma_limit) + if (!dev_dma_ops_bypass(dev) || !dev->bus_dma_limit) return false; return is_direct_handle(dev, dma_handle); @@ -164,7 +164,7 @@ int dma_iommu_dma_supported(struct device *dev, u64 mask) * fixed ops will be used for RAM. This is limited by * bus_dma_limit which is set when RAM is pre-mapped. */ - dev->dma_ops_bypass = true; + dev_set_dma_ops_bypass(dev); dev_info(dev, "iommu: 64-bit OK but direct DMA is limited by %llx\n", dev->bus_dma_limit); return 1; @@ -185,7 +185,7 @@ int dma_iommu_dma_supported(struct device *dev, u64 mask) } dev_dbg(dev, "iommu: not 64-bit, using default ops\n"); - dev->dma_ops_bypass = false; + dev_clear_dma_ops_bypass(dev); return 1; } diff --git a/include/linux/device.h b/include/linux/device.h index cc93a5e1e5d2..099533cbdd2f 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -519,6 +519,11 @@ struct device_physical_location { * doesn't rely on dma_ops structure. * @DEV_FLAG_DMA_SKIP_SYNC: DMA sync operations can be skipped for coherent * buffers. + * @DEV_FLAG_DMA_OPS_BYPASS: If set then the dma_ops are bypassed for the + * streaming DMA operations (->map_* / ->unmap_* / ->sync_*), and + * optional (if the coherent mask is large enough) also for dma + * allocations. This flag is managed by the dma ops instance from + * ->dma_supported. * @DEV_FLAG_COUNT: Number of defined struct_device_flags. */ enum struct_device_flags { @@ -526,6 +531,7 @@ enum struct_device_flags { DEV_FLAG_CAN_MATCH = 1, DEV_FLAG_DMA_IOMMU = 2, DEV_FLAG_DMA_SKIP_SYNC = 3, + DEV_FLAG_DMA_OPS_BYPASS = 4, DEV_FLAG_COUNT }; @@ -614,11 +620,6 @@ enum struct_device_flags { * sync_state() callback. * @dma_coherent: this particular device is dma coherent, even if the * architecture supports non-coherent devices. - * @dma_ops_bypass: If set to %true then the dma_ops are bypassed for the - * streaming DMA operations (->map_* / ->unmap_* / ->sync_*), - * and optionall (if the coherent mask is large enough) also - * for dma allocations. This flag is managed by the dma ops - * instance from ->dma_supported. * @flags: DEV_FLAG_XXX flags. Use atomic bitfield operations to modify. * * At the lowest level, every device in a Linux system is represented by an @@ -732,9 +733,6 @@ struct device { defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU_ALL) bool dma_coherent:1; #endif -#ifdef CONFIG_DMA_OPS_BYPASS - bool dma_ops_bypass : 1; -#endif DECLARE_BITMAP(flags, DEV_FLAG_COUNT); }; @@ -765,6 +763,7 @@ __create_dev_flag_accessors(ready_to_probe, DEV_FLAG_READY_TO_PROBE); __create_dev_flag_accessors(can_match, DEV_FLAG_CAN_MATCH); __create_dev_flag_accessors(dma_iommu, DEV_FLAG_DMA_IOMMU); __create_dev_flag_accessors(dma_skip_sync, DEV_FLAG_DMA_SKIP_SYNC); +__create_dev_flag_accessors(dma_ops_bypass, DEV_FLAG_DMA_OPS_BYPASS); #undef __create_dev_flag_accessors diff --git a/kernel/dma/mapping.c b/kernel/dma/mapping.c index a81a316971ff..fe3ab94f3d83 100644 --- a/kernel/dma/mapping.c +++ b/kernel/dma/mapping.c @@ -126,11 +126,9 @@ static bool dma_go_direct(struct device *dev, dma_addr_t mask, if (likely(!ops)) return true; -#ifdef CONFIG_DMA_OPS_BYPASS - if (dev->dma_ops_bypass) + if (IS_ENABLED(CONFIG_DMA_OPS_BYPASS) && dev_dma_ops_bypass(dev)) return min_not_zero(mask, dev->bus_dma_limit) >= dma_direct_get_required_mask(dev); -#endif return false; } -- cgit v1.2.3 From 7befbf1281290876734046996ee861d7539532c1 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 6 Apr 2026 16:22:59 -0700 Subject: driver core: Replace dev->state_synced with dev_state_synced() In C, bitfields are not necessarily safe to modify from multiple threads without locking. Switch "state_synced" over to the "flags" field so modifications are safe. Cc: Saravana Kannan Reviewed-by: Rafael J. Wysocki (Intel) Reviewed-by: Danilo Krummrich Acked-by: Greg Kroah-Hartman Acked-by: Marek Szyprowski Signed-off-by: Douglas Anderson Link: https://patch.msgid.link/20260406162231.v5.6.Idb4818e1159fef104c7756bfd6e7ba8f374bebcd@changeid Signed-off-by: Danilo Krummrich --- drivers/base/core.c | 8 ++++---- drivers/base/dd.c | 8 +++----- include/linux/device.h | 9 +++++---- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/drivers/base/core.c b/drivers/base/core.c index 43bbd1716f37..7517738dfcd2 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -1123,7 +1123,7 @@ static void __device_links_queue_sync_state(struct device *dev, if (!dev_has_sync_state(dev)) return; - if (dev->state_synced) + if (dev_state_synced(dev)) return; list_for_each_entry(link, &dev->links.consumers, s_node) { @@ -1138,7 +1138,7 @@ static void __device_links_queue_sync_state(struct device *dev, * than once. This can happen if new consumers get added to the device * and probed before the list is flushed. */ - dev->state_synced = true; + dev_set_state_synced(dev); if (WARN_ON(!list_empty(&dev->links.defer_sync))) return; @@ -1779,7 +1779,7 @@ static int fw_devlink_dev_sync_state(struct device *dev, void *data) struct device *sup = link->supplier; if (!device_link_test(link, DL_FLAG_MANAGED) || - link->status == DL_STATE_ACTIVE || sup->state_synced || + link->status == DL_STATE_ACTIVE || dev_state_synced(sup) || !dev_has_sync_state(sup)) return 0; @@ -1793,7 +1793,7 @@ static int fw_devlink_dev_sync_state(struct device *dev, void *data) return 0; dev_warn(sup, "Timed out. Forcing sync_state()\n"); - sup->state_synced = true; + dev_set_state_synced(sup); get_device(sup); list_add_tail(&sup->links.defer_sync, data); diff --git a/drivers/base/dd.c b/drivers/base/dd.c index bce1e63b4230..5799a60fd058 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -569,12 +569,10 @@ static ssize_t state_synced_store(struct device *dev, return -EINVAL; device_lock(dev); - if (!dev->state_synced) { - dev->state_synced = true; + if (!dev_test_and_set_state_synced(dev)) dev_sync_state(dev); - } else { + else ret = -EINVAL; - } device_unlock(dev); return ret ? ret : count; @@ -586,7 +584,7 @@ static ssize_t state_synced_show(struct device *dev, bool val; device_lock(dev); - val = dev->state_synced; + val = dev_state_synced(dev); device_unlock(dev); return sysfs_emit(buf, "%u\n", val); diff --git a/include/linux/device.h b/include/linux/device.h index 099533cbdd2f..fc4334ff3351 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -524,6 +524,9 @@ struct device_physical_location { * optional (if the coherent mask is large enough) also for dma * allocations. This flag is managed by the dma ops instance from * ->dma_supported. + * @DEV_FLAG_STATE_SYNCED: The hardware state of this device has been synced to + * match the software state of this device by calling the + * driver/bus sync_state() callback. * @DEV_FLAG_COUNT: Number of defined struct_device_flags. */ enum struct_device_flags { @@ -532,6 +535,7 @@ enum struct_device_flags { DEV_FLAG_DMA_IOMMU = 2, DEV_FLAG_DMA_SKIP_SYNC = 3, DEV_FLAG_DMA_OPS_BYPASS = 4, + DEV_FLAG_STATE_SYNCED = 5, DEV_FLAG_COUNT }; @@ -615,9 +619,6 @@ enum struct_device_flags { * @offline: Set after successful invocation of bus type's .offline(). * @of_node_reused: Set if the device-tree node is shared with an ancestor * device. - * @state_synced: The hardware state of this device has been synced to match - * the software state of this device by calling the driver/bus - * sync_state() callback. * @dma_coherent: this particular device is dma coherent, even if the * architecture supports non-coherent devices. * @flags: DEV_FLAG_XXX flags. Use atomic bitfield operations to modify. @@ -727,7 +728,6 @@ struct device { bool offline_disabled:1; bool offline:1; bool of_node_reused:1; - bool state_synced:1; #if defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE) || \ defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU) || \ defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU_ALL) @@ -764,6 +764,7 @@ __create_dev_flag_accessors(can_match, DEV_FLAG_CAN_MATCH); __create_dev_flag_accessors(dma_iommu, DEV_FLAG_DMA_IOMMU); __create_dev_flag_accessors(dma_skip_sync, DEV_FLAG_DMA_SKIP_SYNC); __create_dev_flag_accessors(dma_ops_bypass, DEV_FLAG_DMA_OPS_BYPASS); +__create_dev_flag_accessors(state_synced, DEV_FLAG_STATE_SYNCED); #undef __create_dev_flag_accessors -- cgit v1.2.3 From 3e2c1e213ac2bfc9068a2686ef380ee0d8bef949 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 6 Apr 2026 16:23:00 -0700 Subject: driver core: Replace dev->dma_coherent with dev_dma_coherent() In C, bitfields are not necessarily safe to modify from multiple threads without locking. Switch "dma_coherent" over to the "flags" field so modifications are safe. Cc: Christoph Hellwig Reviewed-by: Rafael J. Wysocki (Intel) Reviewed-by: Danilo Krummrich Acked-by: Vinod Koul Acked-by: Greg Kroah-Hartman Acked-by: Marek Szyprowski Signed-off-by: Douglas Anderson Link: https://patch.msgid.link/20260406162231.v5.7.If839f6dde98979fce177f70c6c74689a1904ee76@changeid [ Since all DEV_FLAG_DMA_COHERENT accessors are exposed unconditionally, also drop the CONFIG guards around dev_assign_dma_coherent() in device_initialize() to ensure a correct default value. - Danilo ] Signed-off-by: Danilo Krummrich --- arch/arc/mm/dma.c | 4 ++-- arch/arm/mach-highbank/highbank.c | 2 +- arch/arm/mach-mvebu/coherency.c | 2 +- arch/arm/mm/dma-mapping-nommu.c | 4 ++-- arch/arm/mm/dma-mapping.c | 28 ++++++++++++++-------------- arch/arm64/mm/dma-mapping.c | 2 +- arch/mips/mm/dma-noncoherent.c | 2 +- arch/riscv/mm/dma-noncoherent.c | 2 +- drivers/base/core.c | 6 +----- drivers/dma/ti/k3-udma-glue.c | 6 +++--- drivers/dma/ti/k3-udma.c | 6 +++--- include/linux/device.h | 11 ++++------- include/linux/dma-map-ops.h | 2 +- 13 files changed, 35 insertions(+), 42 deletions(-) diff --git a/arch/arc/mm/dma.c b/arch/arc/mm/dma.c index 6b85e94f3275..9b9adb02b4c5 100644 --- a/arch/arc/mm/dma.c +++ b/arch/arc/mm/dma.c @@ -98,8 +98,8 @@ void arch_setup_dma_ops(struct device *dev, bool coherent) * DMA buffers. */ if (is_isa_arcv2() && ioc_enable && coherent) - dev->dma_coherent = true; + dev_set_dma_coherent(dev); dev_info(dev, "use %scoherent DMA ops\n", - dev->dma_coherent ? "" : "non"); + dev_dma_coherent(dev) ? "" : "non"); } diff --git a/arch/arm/mach-highbank/highbank.c b/arch/arm/mach-highbank/highbank.c index 47335c7dadf8..8b7d0929dac4 100644 --- a/arch/arm/mach-highbank/highbank.c +++ b/arch/arm/mach-highbank/highbank.c @@ -98,7 +98,7 @@ static int highbank_platform_notifier(struct notifier_block *nb, if (of_property_read_bool(dev->of_node, "dma-coherent")) { val = readl(sregs_base + reg); writel(val | 0xff01, sregs_base + reg); - dev->dma_coherent = true; + dev_set_dma_coherent(dev); } return NOTIFY_OK; diff --git a/arch/arm/mach-mvebu/coherency.c b/arch/arm/mach-mvebu/coherency.c index fa2c1e1aeb96..7234d487ff39 100644 --- a/arch/arm/mach-mvebu/coherency.c +++ b/arch/arm/mach-mvebu/coherency.c @@ -95,7 +95,7 @@ static int mvebu_hwcc_notifier(struct notifier_block *nb, if (event != BUS_NOTIFY_ADD_DEVICE) return NOTIFY_DONE; - dev->dma_coherent = true; + dev_set_dma_coherent(dev); return NOTIFY_OK; } diff --git a/arch/arm/mm/dma-mapping-nommu.c b/arch/arm/mm/dma-mapping-nommu.c index fecac107fd0d..c6a70686507b 100644 --- a/arch/arm/mm/dma-mapping-nommu.c +++ b/arch/arm/mm/dma-mapping-nommu.c @@ -42,11 +42,11 @@ void arch_setup_dma_ops(struct device *dev, bool coherent) * enough to check if MPU is in use or not since in absence of * MPU system memory map is used. */ - dev->dma_coherent = cacheid ? coherent : true; + dev_assign_dma_coherent(dev, cacheid ? coherent : true); } else { /* * Assume coherent DMA in case MMU/MPU has not been set up. */ - dev->dma_coherent = (get_cr() & CR_M) ? coherent : true; + dev_assign_dma_coherent(dev, (get_cr() & CR_M) ? coherent : true); } } diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index f304037d1c34..f9bc53b60f99 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -1076,7 +1076,7 @@ static void *arm_iommu_alloc_attrs(struct device *dev, size_t size, pgprot_t prot = __get_dma_pgprot(attrs, PAGE_KERNEL); struct page **pages; void *addr = NULL; - int coherent_flag = dev->dma_coherent ? COHERENT : NORMAL; + int coherent_flag = dev_dma_coherent(dev) ? COHERENT : NORMAL; *handle = DMA_MAPPING_ERROR; size = PAGE_ALIGN(size); @@ -1124,7 +1124,7 @@ static int arm_iommu_mmap_attrs(struct device *dev, struct vm_area_struct *vma, if (vma->vm_pgoff >= nr_pages) return -ENXIO; - if (!dev->dma_coherent) + if (!dev_dma_coherent(dev)) vma->vm_page_prot = __get_dma_pgprot(attrs, vma->vm_page_prot); err = vm_map_pages(vma, pages, nr_pages); @@ -1141,7 +1141,7 @@ static int arm_iommu_mmap_attrs(struct device *dev, struct vm_area_struct *vma, static void arm_iommu_free_attrs(struct device *dev, size_t size, void *cpu_addr, dma_addr_t handle, unsigned long attrs) { - int coherent_flag = dev->dma_coherent ? COHERENT : NORMAL; + int coherent_flag = dev_dma_coherent(dev) ? COHERENT : NORMAL; struct page **pages; size = PAGE_ALIGN(size); @@ -1202,7 +1202,7 @@ static int __map_sg_chunk(struct device *dev, struct scatterlist *sg, phys_addr_t phys = page_to_phys(sg_page(s)); unsigned int len = PAGE_ALIGN(s->offset + s->length); - if (!dev->dma_coherent && !(attrs & DMA_ATTR_SKIP_CPU_SYNC)) + if (!dev_dma_coherent(dev) && !(attrs & DMA_ATTR_SKIP_CPU_SYNC)) arch_sync_dma_for_device(sg_phys(s), s->length, dir); prot = __dma_info_to_prot(dir, attrs); @@ -1304,7 +1304,7 @@ static void arm_iommu_unmap_sg(struct device *dev, if (sg_dma_len(s)) __iommu_remove_mapping(dev, sg_dma_address(s), sg_dma_len(s)); - if (!dev->dma_coherent && !(attrs & DMA_ATTR_SKIP_CPU_SYNC)) + if (!dev_dma_coherent(dev) && !(attrs & DMA_ATTR_SKIP_CPU_SYNC)) arch_sync_dma_for_cpu(sg_phys(s), s->length, dir); } } @@ -1323,7 +1323,7 @@ static void arm_iommu_sync_sg_for_cpu(struct device *dev, struct scatterlist *s; int i; - if (dev->dma_coherent) + if (dev_dma_coherent(dev)) return; for_each_sg(sg, s, nents, i) @@ -1345,7 +1345,7 @@ static void arm_iommu_sync_sg_for_device(struct device *dev, struct scatterlist *s; int i; - if (dev->dma_coherent) + if (dev_dma_coherent(dev)) return; for_each_sg(sg, s, nents, i) @@ -1371,7 +1371,7 @@ static dma_addr_t arm_iommu_map_phys(struct device *dev, phys_addr_t phys, dma_addr_t dma_addr; int ret, prot; - if (!dev->dma_coherent && + if (!dev_dma_coherent(dev) && !(attrs & (DMA_ATTR_SKIP_CPU_SYNC | DMA_ATTR_MMIO))) arch_sync_dma_for_device(phys, size, dir); @@ -1412,7 +1412,7 @@ static void arm_iommu_unmap_phys(struct device *dev, dma_addr_t handle, if (!iova) return; - if (!dev->dma_coherent && + if (!dev_dma_coherent(dev) && !(attrs & (DMA_ATTR_SKIP_CPU_SYNC | DMA_ATTR_MMIO))) { phys_addr_t phys = iommu_iova_to_phys(mapping->domain, iova); @@ -1431,7 +1431,7 @@ static void arm_iommu_sync_single_for_cpu(struct device *dev, unsigned int offset = handle & ~PAGE_MASK; phys_addr_t phys; - if (dev->dma_coherent || !iova) + if (dev_dma_coherent(dev) || !iova) return; phys = iommu_iova_to_phys(mapping->domain, iova); @@ -1446,7 +1446,7 @@ static void arm_iommu_sync_single_for_device(struct device *dev, unsigned int offset = handle & ~PAGE_MASK; phys_addr_t phys; - if (dev->dma_coherent || !iova) + if (dev_dma_coherent(dev) || !iova) return; phys = iommu_iova_to_phys(mapping->domain, iova); @@ -1701,13 +1701,13 @@ static void arm_teardown_iommu_dma_ops(struct device *dev) { } void arch_setup_dma_ops(struct device *dev, bool coherent) { /* - * Due to legacy code that sets the ->dma_coherent flag from a bus - * notifier we can't just assign coherent to the ->dma_coherent flag + * Due to legacy code that sets the dma_coherent flag from a bus + * notifier we can't just assign coherent to the dma_coherent flag * here, but instead have to make sure we only set but never clear it * for now. */ if (coherent) - dev->dma_coherent = true; + dev_set_dma_coherent(dev); /* * Don't override the dma_ops if they have already been set. Ideally diff --git a/arch/arm64/mm/dma-mapping.c b/arch/arm64/mm/dma-mapping.c index ae1ae0280eef..994b7b36e2b9 100644 --- a/arch/arm64/mm/dma-mapping.c +++ b/arch/arm64/mm/dma-mapping.c @@ -48,7 +48,7 @@ void arch_setup_dma_ops(struct device *dev, bool coherent) dev_driver_string(dev), dev_name(dev), ARCH_DMA_MINALIGN, cls); - dev->dma_coherent = coherent; + dev_assign_dma_coherent(dev, coherent); xen_setup_dma_ops(dev); } diff --git a/arch/mips/mm/dma-noncoherent.c b/arch/mips/mm/dma-noncoherent.c index ab4f2a75a7d0..30ef3e247eb7 100644 --- a/arch/mips/mm/dma-noncoherent.c +++ b/arch/mips/mm/dma-noncoherent.c @@ -139,6 +139,6 @@ void arch_sync_dma_for_cpu(phys_addr_t paddr, size_t size, #ifdef CONFIG_ARCH_HAS_SETUP_DMA_OPS void arch_setup_dma_ops(struct device *dev, bool coherent) { - dev->dma_coherent = coherent; + dev_assign_dma_coherent(dev, coherent); } #endif diff --git a/arch/riscv/mm/dma-noncoherent.c b/arch/riscv/mm/dma-noncoherent.c index cb89d7e0ba88..a1ec2d71d1c9 100644 --- a/arch/riscv/mm/dma-noncoherent.c +++ b/arch/riscv/mm/dma-noncoherent.c @@ -140,7 +140,7 @@ void arch_setup_dma_ops(struct device *dev, bool coherent) "%s %s: device non-coherent but no non-coherent operations supported", dev_driver_string(dev), dev_name(dev)); - dev->dma_coherent = coherent; + dev_assign_dma_coherent(dev, coherent); } void riscv_noncoherent_supported(void) diff --git a/drivers/base/core.c b/drivers/base/core.c index 7517738dfcd2..ed5ec21edbea 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -3170,11 +3170,7 @@ void device_initialize(struct device *dev) INIT_LIST_HEAD(&dev->links.suppliers); INIT_LIST_HEAD(&dev->links.defer_sync); dev->links.status = DL_DEV_NO_DRIVER; -#if defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE) || \ - defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU) || \ - defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU_ALL) - dev->dma_coherent = dma_default_coherent; -#endif + dev_assign_dma_coherent(dev, dma_default_coherent); swiotlb_dev_init(dev); } EXPORT_SYMBOL_GPL(device_initialize); diff --git a/drivers/dma/ti/k3-udma-glue.c b/drivers/dma/ti/k3-udma-glue.c index f87d244cc2d6..686dc140293e 100644 --- a/drivers/dma/ti/k3-udma-glue.c +++ b/drivers/dma/ti/k3-udma-glue.c @@ -312,7 +312,7 @@ k3_udma_glue_request_tx_chn_common(struct device *dev, if (xudma_is_pktdma(tx_chn->common.udmax)) { /* prepare the channel device as coherent */ - tx_chn->common.chan_dev.dma_coherent = true; + dev_set_dma_coherent(&tx_chn->common.chan_dev); dma_coerce_mask_and_coherent(&tx_chn->common.chan_dev, DMA_BIT_MASK(48)); } @@ -1003,7 +1003,7 @@ k3_udma_glue_request_rx_chn_priv(struct device *dev, const char *name, if (xudma_is_pktdma(rx_chn->common.udmax)) { /* prepare the channel device as coherent */ - rx_chn->common.chan_dev.dma_coherent = true; + dev_set_dma_coherent(&rx_chn->common.chan_dev); dma_coerce_mask_and_coherent(&rx_chn->common.chan_dev, DMA_BIT_MASK(48)); } @@ -1104,7 +1104,7 @@ k3_udma_glue_request_remote_rx_chn_common(struct k3_udma_glue_rx_channel *rx_chn if (xudma_is_pktdma(rx_chn->common.udmax)) { /* prepare the channel device as coherent */ - rx_chn->common.chan_dev.dma_coherent = true; + dev_set_dma_coherent(&rx_chn->common.chan_dev); dma_coerce_mask_and_coherent(&rx_chn->common.chan_dev, DMA_BIT_MASK(48)); rx_chn->single_fdq = false; diff --git a/drivers/dma/ti/k3-udma.c b/drivers/dma/ti/k3-udma.c index c964ebfcf3b6..1cf158eb7bdb 100644 --- a/drivers/dma/ti/k3-udma.c +++ b/drivers/dma/ti/k3-udma.c @@ -428,18 +428,18 @@ static void k3_configure_chan_coherency(struct dma_chan *chan, u32 asel) /* No special handling for the channel */ chan->dev->chan_dma_dev = false; - chan_dev->dma_coherent = false; + dev_clear_dma_coherent(chan_dev); chan_dev->dma_parms = NULL; } else if (asel == 14 || asel == 15) { chan->dev->chan_dma_dev = true; - chan_dev->dma_coherent = true; + dev_set_dma_coherent(chan_dev); dma_coerce_mask_and_coherent(chan_dev, DMA_BIT_MASK(48)); chan_dev->dma_parms = chan_dev->parent->dma_parms; } else { dev_warn(chan->device->dev, "Invalid ASEL value: %u\n", asel); - chan_dev->dma_coherent = false; + dev_clear_dma_coherent(chan_dev); chan_dev->dma_parms = NULL; } } diff --git a/include/linux/device.h b/include/linux/device.h index fc4334ff3351..bab4315f4f61 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -527,6 +527,8 @@ struct device_physical_location { * @DEV_FLAG_STATE_SYNCED: The hardware state of this device has been synced to * match the software state of this device by calling the * driver/bus sync_state() callback. + * @DEV_FLAG_DMA_COHERENT: This particular device is dma coherent, even if the + * architecture supports non-coherent devices. * @DEV_FLAG_COUNT: Number of defined struct_device_flags. */ enum struct_device_flags { @@ -536,6 +538,7 @@ enum struct_device_flags { DEV_FLAG_DMA_SKIP_SYNC = 3, DEV_FLAG_DMA_OPS_BYPASS = 4, DEV_FLAG_STATE_SYNCED = 5, + DEV_FLAG_DMA_COHERENT = 6, DEV_FLAG_COUNT }; @@ -619,8 +622,6 @@ enum struct_device_flags { * @offline: Set after successful invocation of bus type's .offline(). * @of_node_reused: Set if the device-tree node is shared with an ancestor * device. - * @dma_coherent: this particular device is dma coherent, even if the - * architecture supports non-coherent devices. * @flags: DEV_FLAG_XXX flags. Use atomic bitfield operations to modify. * * At the lowest level, every device in a Linux system is represented by an @@ -728,11 +729,6 @@ struct device { bool offline_disabled:1; bool offline:1; bool of_node_reused:1; -#if defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE) || \ - defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU) || \ - defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU_ALL) - bool dma_coherent:1; -#endif DECLARE_BITMAP(flags, DEV_FLAG_COUNT); }; @@ -765,6 +761,7 @@ __create_dev_flag_accessors(dma_iommu, DEV_FLAG_DMA_IOMMU); __create_dev_flag_accessors(dma_skip_sync, DEV_FLAG_DMA_SKIP_SYNC); __create_dev_flag_accessors(dma_ops_bypass, DEV_FLAG_DMA_OPS_BYPASS); __create_dev_flag_accessors(state_synced, DEV_FLAG_STATE_SYNCED); +__create_dev_flag_accessors(dma_coherent, DEV_FLAG_DMA_COHERENT); #undef __create_dev_flag_accessors diff --git a/include/linux/dma-map-ops.h b/include/linux/dma-map-ops.h index 9e677a79f3a8..bcb5b5428aea 100644 --- a/include/linux/dma-map-ops.h +++ b/include/linux/dma-map-ops.h @@ -225,7 +225,7 @@ int dma_direct_set_offset(struct device *dev, phys_addr_t cpu_start, extern bool dma_default_coherent; static inline bool dev_is_dma_coherent(struct device *dev) { - return dev->dma_coherent; + return dev_dma_coherent(dev); } #else #define dma_default_coherent true -- cgit v1.2.3 From 4aca5e62f37dd10cc771d5489900f927d133a9f1 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 6 Apr 2026 16:23:01 -0700 Subject: driver core: Replace dev->of_node_reused with dev_of_node_reused() In C, bitfields are not necessarily safe to modify from multiple threads without locking. Switch "of_node_reused" over to the "flags" field so modifications are safe. Cc: Johan Hovold Acked-by: Mark Brown Reviewed-by: Rafael J. Wysocki (Intel) Reviewed-by: Danilo Krummrich Signed-off-by: Douglas Anderson Reviewed-by: Johan Hovold Acked-by: Manivannan Sadhasivam # PCI_PWRCTRL Acked-by: Greg Kroah-Hartman Acked-by: Marek Szyprowski Link: https://patch.msgid.link/20260406162231.v5.8.I806b8636cd3724f6cd1f5e199318ab8694472d90@changeid Signed-off-by: Danilo Krummrich --- drivers/base/core.c | 2 +- drivers/base/pinctrl.c | 2 +- drivers/base/platform.c | 2 +- drivers/net/pcs/pcs-xpcs-plat.c | 2 +- drivers/of/device.c | 6 +++--- drivers/pci/of.c | 2 +- drivers/pci/pwrctrl/core.c | 2 +- drivers/tty/serial/serial_base_bus.c | 2 +- drivers/usb/gadget/udc/aspeed-vhub/dev.c | 2 +- include/linux/device.h | 7 ++++--- 10 files changed, 15 insertions(+), 14 deletions(-) diff --git a/drivers/base/core.c b/drivers/base/core.c index ed5ec21edbea..62140ec6c49e 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -5279,7 +5279,7 @@ void device_set_of_node_from_dev(struct device *dev, const struct device *dev2) { of_node_put(dev->of_node); dev->of_node = of_node_get(dev2->of_node); - dev->of_node_reused = true; + dev_set_of_node_reused(dev); } EXPORT_SYMBOL_GPL(device_set_of_node_from_dev); diff --git a/drivers/base/pinctrl.c b/drivers/base/pinctrl.c index 6e250272c843..0bbc83231234 100644 --- a/drivers/base/pinctrl.c +++ b/drivers/base/pinctrl.c @@ -24,7 +24,7 @@ int pinctrl_bind_pins(struct device *dev) { int ret; - if (dev->of_node_reused) + if (dev_of_node_reused(dev)) return 0; dev->pins = devm_kzalloc(dev, sizeof(*(dev->pins)), GFP_KERNEL); diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 75b4698d0e58..c04641501ab4 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -858,7 +858,7 @@ struct platform_device *platform_device_register_full(const struct platform_devi pdev->dev.parent = pdevinfo->parent; pdev->dev.fwnode = pdevinfo->fwnode; pdev->dev.of_node = of_node_get(to_of_node(pdev->dev.fwnode)); - pdev->dev.of_node_reused = pdevinfo->of_node_reused; + dev_assign_of_node_reused(&pdev->dev, pdevinfo->of_node_reused); if (pdevinfo->dma_mask) { pdev->platform_dma_mask = pdevinfo->dma_mask; diff --git a/drivers/net/pcs/pcs-xpcs-plat.c b/drivers/net/pcs/pcs-xpcs-plat.c index b8c48f9effbf..f4b1b8246ce9 100644 --- a/drivers/net/pcs/pcs-xpcs-plat.c +++ b/drivers/net/pcs/pcs-xpcs-plat.c @@ -349,7 +349,7 @@ static int xpcs_plat_init_dev(struct dw_xpcs_plat *pxpcs) * up later. Make sure DD-core is aware of the OF-node being re-used. */ device_set_node(&mdiodev->dev, fwnode_handle_get(dev_fwnode(dev))); - mdiodev->dev.of_node_reused = true; + dev_set_of_node_reused(&mdiodev->dev); /* Pass the data further so the DW XPCS driver core could use it */ mdiodev->dev.platform_data = (void *)device_get_match_data(dev); diff --git a/drivers/of/device.c b/drivers/of/device.c index f7e75e527667..be4e1584e0af 100644 --- a/drivers/of/device.c +++ b/drivers/of/device.c @@ -26,7 +26,7 @@ const struct of_device_id *of_match_device(const struct of_device_id *matches, const struct device *dev) { - if (!matches || !dev->of_node || dev->of_node_reused) + if (!matches || !dev->of_node || dev_of_node_reused(dev)) return NULL; return of_match_node(matches, dev->of_node); } @@ -192,7 +192,7 @@ ssize_t of_device_modalias(struct device *dev, char *str, ssize_t len) { ssize_t sl; - if (!dev || !dev->of_node || dev->of_node_reused) + if (!dev || !dev->of_node || dev_of_node_reused(dev)) return -ENODEV; sl = of_modalias(dev->of_node, str, len - 2); @@ -254,7 +254,7 @@ int of_device_uevent_modalias(const struct device *dev, struct kobj_uevent_env * { int sl; - if ((!dev) || (!dev->of_node) || dev->of_node_reused) + if ((!dev) || (!dev->of_node) || dev_of_node_reused(dev)) return -ENODEV; /* Devicetree modalias is tricky, we add it in 2 steps */ diff --git a/drivers/pci/of.c b/drivers/pci/of.c index 6da569fd3b8f..8b18c4ba845c 100644 --- a/drivers/pci/of.c +++ b/drivers/pci/of.c @@ -38,7 +38,7 @@ int pci_set_of_node(struct pci_dev *dev) struct device *pdev __free(put_device) = bus_find_device_by_of_node(&platform_bus_type, node); if (pdev) - dev->bus->dev.of_node_reused = true; + dev_set_of_node_reused(&dev->bus->dev); device_set_node(&dev->dev, of_fwnode_handle(no_free_ptr(node))); return 0; diff --git a/drivers/pci/pwrctrl/core.c b/drivers/pci/pwrctrl/core.c index 97cff5b8ca88..31246bac84f1 100644 --- a/drivers/pci/pwrctrl/core.c +++ b/drivers/pci/pwrctrl/core.c @@ -39,7 +39,7 @@ static int pci_pwrctrl_notify(struct notifier_block *nb, unsigned long action, * If we got here then the PCI device is the second after the * power control platform device. Mark its OF node as reused. */ - dev->of_node_reused = true; + dev_set_of_node_reused(dev); break; } diff --git a/drivers/tty/serial/serial_base_bus.c b/drivers/tty/serial/serial_base_bus.c index a12935f6b992..5f23284a8778 100644 --- a/drivers/tty/serial/serial_base_bus.c +++ b/drivers/tty/serial/serial_base_bus.c @@ -74,7 +74,7 @@ static int serial_base_device_init(struct uart_port *port, dev->parent = parent_dev; dev->bus = &serial_base_bus_type; dev->release = release; - dev->of_node_reused = true; + dev_set_of_node_reused(dev); device_set_node(dev, fwnode_handle_get(dev_fwnode(parent_dev))); diff --git a/drivers/usb/gadget/udc/aspeed-vhub/dev.c b/drivers/usb/gadget/udc/aspeed-vhub/dev.c index 2ecd049dacc2..8b9449d16324 100644 --- a/drivers/usb/gadget/udc/aspeed-vhub/dev.c +++ b/drivers/usb/gadget/udc/aspeed-vhub/dev.c @@ -593,7 +593,7 @@ int ast_vhub_init_dev(struct ast_vhub *vhub, unsigned int idx) d->gadget.max_speed = USB_SPEED_HIGH; d->gadget.speed = USB_SPEED_UNKNOWN; d->gadget.dev.of_node = vhub->pdev->dev.of_node; - d->gadget.dev.of_node_reused = true; + dev_set_of_node_reused(&d->gadget.dev); rc = usb_add_gadget_udc(d->port_dev, &d->gadget); if (rc != 0) diff --git a/include/linux/device.h b/include/linux/device.h index bab4315f4f61..cc6ef0ca0d25 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -529,6 +529,8 @@ struct device_physical_location { * driver/bus sync_state() callback. * @DEV_FLAG_DMA_COHERENT: This particular device is dma coherent, even if the * architecture supports non-coherent devices. + * @DEV_FLAG_OF_NODE_REUSED: Set if the device-tree node is shared with an + * ancestor device. * @DEV_FLAG_COUNT: Number of defined struct_device_flags. */ enum struct_device_flags { @@ -539,6 +541,7 @@ enum struct_device_flags { DEV_FLAG_DMA_OPS_BYPASS = 4, DEV_FLAG_STATE_SYNCED = 5, DEV_FLAG_DMA_COHERENT = 6, + DEV_FLAG_OF_NODE_REUSED = 7, DEV_FLAG_COUNT }; @@ -620,8 +623,6 @@ enum struct_device_flags { * * @offline_disabled: If set, the device is permanently online. * @offline: Set after successful invocation of bus type's .offline(). - * @of_node_reused: Set if the device-tree node is shared with an ancestor - * device. * @flags: DEV_FLAG_XXX flags. Use atomic bitfield operations to modify. * * At the lowest level, every device in a Linux system is represented by an @@ -728,7 +729,6 @@ struct device { bool offline_disabled:1; bool offline:1; - bool of_node_reused:1; DECLARE_BITMAP(flags, DEV_FLAG_COUNT); }; @@ -762,6 +762,7 @@ __create_dev_flag_accessors(dma_skip_sync, DEV_FLAG_DMA_SKIP_SYNC); __create_dev_flag_accessors(dma_ops_bypass, DEV_FLAG_DMA_OPS_BYPASS); __create_dev_flag_accessors(state_synced, DEV_FLAG_STATE_SYNCED); __create_dev_flag_accessors(dma_coherent, DEV_FLAG_DMA_COHERENT); +__create_dev_flag_accessors(of_node_reused, DEV_FLAG_OF_NODE_REUSED); #undef __create_dev_flag_accessors -- cgit v1.2.3 From a7cc262a11354ab104b8e55c21200d099d141bc7 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 6 Apr 2026 16:23:02 -0700 Subject: driver core: Replace dev->offline + ->offline_disabled with accessors In C, bitfields are not necessarily safe to modify from multiple threads without locking. Switch "offline" and "offline_disabled" over to the "flags" field so modifications are safe. Cc: Rafael J. Wysocki Acked-by: Mark Brown Reviewed-by: Rafael J. Wysocki (Intel) Reviewed-by: Danilo Krummrich Acked-by: Greg Kroah-Hartman Acked-by: Marek Szyprowski Signed-off-by: Douglas Anderson Link: https://patch.msgid.link/20260406162231.v5.9.I897d478b4a9361d79cd5073207c1062fd4d0d0e4@changeid Signed-off-by: Danilo Krummrich --- arch/arm64/kernel/cpufeature.c | 2 +- arch/powerpc/platforms/pseries/hotplug-memory.c | 4 ++-- drivers/acpi/scan.c | 2 +- drivers/base/core.c | 18 +++++++++--------- drivers/base/cpu.c | 4 ++-- drivers/base/memory.c | 2 +- include/linux/device.h | 12 ++++++------ kernel/cpu.c | 4 ++-- 8 files changed, 24 insertions(+), 24 deletions(-) diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c index 6d53bb15cf7b..e592520e13b7 100644 --- a/arch/arm64/kernel/cpufeature.c +++ b/arch/arm64/kernel/cpufeature.c @@ -4052,7 +4052,7 @@ static int enable_mismatched_32bit_el0(unsigned int cpu) */ lucky_winner = cpu_32bit ? cpu : cpumask_any_and(cpu_32bit_el0_mask, cpu_active_mask); - get_cpu_device(lucky_winner)->offline_disabled = true; + dev_set_offline_disabled(get_cpu_device(lucky_winner)); setup_elf_hwcaps(compat_elf_hwcaps); elf_hwcap_fixup(); pr_info("Asymmetric 32-bit EL0 support detected on CPU %u; CPU hot-unplug disabled on CPU %u\n", diff --git a/arch/powerpc/platforms/pseries/hotplug-memory.c b/arch/powerpc/platforms/pseries/hotplug-memory.c index b2f14db59034..75f85a5da981 100644 --- a/arch/powerpc/platforms/pseries/hotplug-memory.c +++ b/arch/powerpc/platforms/pseries/hotplug-memory.c @@ -213,9 +213,9 @@ static int dlpar_change_lmb_state(struct drmem_lmb *lmb, bool online) return -EINVAL; } - if (online && mem_block->dev.offline) + if (online && dev_offline(&mem_block->dev)) rc = device_online(&mem_block->dev); - else if (!online && !mem_block->dev.offline) + else if (!online && !dev_offline(&mem_block->dev)) rc = device_offline(&mem_block->dev); else rc = 0; diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 530547cda8b2..a0c36bd968d3 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -122,7 +122,7 @@ bool acpi_scan_is_offline(struct acpi_device *adev, bool uevent) mutex_lock_nested(&adev->physical_node_lock, SINGLE_DEPTH_NESTING); list_for_each_entry(pn, &adev->physical_node_list, node) - if (device_supports_offline(pn->dev) && !pn->dev->offline) { + if (device_supports_offline(pn->dev) && !dev_offline(pn->dev)) { if (uevent) kobject_uevent_env(&pn->dev->kobj, KOBJ_CHANGE, envp); diff --git a/drivers/base/core.c b/drivers/base/core.c index 62140ec6c49e..d49420e066de 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -2787,7 +2787,7 @@ static ssize_t online_show(struct device *dev, struct device_attribute *attr, bool val; device_lock(dev); - val = !dev->offline; + val = !dev_offline(dev); device_unlock(dev); return sysfs_emit(buf, "%u\n", val); } @@ -2913,7 +2913,7 @@ static int device_add_attrs(struct device *dev) if (error) goto err_remove_type_groups; - if (device_supports_offline(dev) && !dev->offline_disabled) { + if (device_supports_offline(dev) && !dev_offline_disabled(dev)) { error = device_create_file(dev, &dev_attr_online); if (error) goto err_remove_dev_groups; @@ -4176,7 +4176,7 @@ static int device_check_offline(struct device *dev, void *not_used) if (ret) return ret; - return device_supports_offline(dev) && !dev->offline ? -EBUSY : 0; + return device_supports_offline(dev) && !dev_offline(dev) ? -EBUSY : 0; } /** @@ -4194,7 +4194,7 @@ int device_offline(struct device *dev) { int ret; - if (dev->offline_disabled) + if (dev_offline_disabled(dev)) return -EPERM; ret = device_for_each_child(dev, NULL, device_check_offline); @@ -4203,13 +4203,13 @@ int device_offline(struct device *dev) device_lock(dev); if (device_supports_offline(dev)) { - if (dev->offline) { + if (dev_offline(dev)) { ret = 1; } else { ret = dev->bus->offline(dev); if (!ret) { kobject_uevent(&dev->kobj, KOBJ_OFFLINE); - dev->offline = true; + dev_set_offline(dev); } } } @@ -4234,11 +4234,11 @@ int device_online(struct device *dev) device_lock(dev); if (device_supports_offline(dev)) { - if (dev->offline) { + if (dev_offline(dev)) { ret = dev->bus->online(dev); if (!ret) { kobject_uevent(&dev->kobj, KOBJ_ONLINE); - dev->offline = false; + dev_clear_offline(dev); } } else { ret = 1; @@ -4712,7 +4712,7 @@ static int device_attrs_change_owner(struct device *dev, kuid_t kuid, if (error) return error; - if (device_supports_offline(dev) && !dev->offline_disabled) { + if (device_supports_offline(dev) && !dev_offline_disabled(dev)) { /* Change online device attributes of @dev to @kuid/@kgid. */ error = sysfs_file_change_owner(kobj, dev_attr_online.attr.name, kuid, kgid); diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index 875abdc9942e..19d288a3c80c 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -422,8 +422,8 @@ int register_cpu(struct cpu *cpu, int num) cpu->dev.id = num; cpu->dev.bus = &cpu_subsys; cpu->dev.release = cpu_device_release; - cpu->dev.offline_disabled = !cpu->hotpluggable; - cpu->dev.offline = !cpu_online(num); + dev_assign_offline_disabled(&cpu->dev, !cpu->hotpluggable); + dev_assign_offline(&cpu->dev, !cpu_online(num)); cpu->dev.of_node = of_get_cpu_node(num, NULL); cpu->dev.groups = common_cpu_attr_groups; if (cpu->hotpluggable) diff --git a/drivers/base/memory.c b/drivers/base/memory.c index f806a683b767..37c7f773aa5a 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -697,7 +697,7 @@ static int __add_memory_block(struct memory_block *memory) memory->dev.id = memory->start_section_nr / sections_per_block; memory->dev.release = memory_block_release; memory->dev.groups = memory_memblk_attr_groups; - memory->dev.offline = memory->state == MEM_OFFLINE; + dev_assign_offline(&memory->dev, memory->state == MEM_OFFLINE); ret = device_register(&memory->dev); if (ret) { diff --git a/include/linux/device.h b/include/linux/device.h index cc6ef0ca0d25..3bf86d2f9544 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -531,6 +531,8 @@ struct device_physical_location { * architecture supports non-coherent devices. * @DEV_FLAG_OF_NODE_REUSED: Set if the device-tree node is shared with an * ancestor device. + * @DEV_FLAG_OFFLINE_DISABLED: If set, the device is permanently online. + * @DEV_FLAG_OFFLINE: Set after successful invocation of bus type's .offline(). * @DEV_FLAG_COUNT: Number of defined struct_device_flags. */ enum struct_device_flags { @@ -542,6 +544,8 @@ enum struct_device_flags { DEV_FLAG_STATE_SYNCED = 5, DEV_FLAG_DMA_COHERENT = 6, DEV_FLAG_OF_NODE_REUSED = 7, + DEV_FLAG_OFFLINE_DISABLED = 8, + DEV_FLAG_OFFLINE = 9, DEV_FLAG_COUNT }; @@ -620,9 +624,6 @@ enum struct_device_flags { * @removable: Whether the device can be removed from the system. This * should be set by the subsystem / bus driver that discovered * the device. - * - * @offline_disabled: If set, the device is permanently online. - * @offline: Set after successful invocation of bus type's .offline(). * @flags: DEV_FLAG_XXX flags. Use atomic bitfield operations to modify. * * At the lowest level, every device in a Linux system is represented by an @@ -727,9 +728,6 @@ struct device { enum device_removable removable; - bool offline_disabled:1; - bool offline:1; - DECLARE_BITMAP(flags, DEV_FLAG_COUNT); }; @@ -763,6 +761,8 @@ __create_dev_flag_accessors(dma_ops_bypass, DEV_FLAG_DMA_OPS_BYPASS); __create_dev_flag_accessors(state_synced, DEV_FLAG_STATE_SYNCED); __create_dev_flag_accessors(dma_coherent, DEV_FLAG_DMA_COHERENT); __create_dev_flag_accessors(of_node_reused, DEV_FLAG_OF_NODE_REUSED); +__create_dev_flag_accessors(offline_disabled, DEV_FLAG_OFFLINE_DISABLED); +__create_dev_flag_accessors(offline, DEV_FLAG_OFFLINE); #undef __create_dev_flag_accessors diff --git a/kernel/cpu.c b/kernel/cpu.c index bc4f7a9ba64e..f975bb34915b 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -2639,7 +2639,7 @@ static void cpuhp_offline_cpu_device(unsigned int cpu) { struct device *dev = get_cpu_device(cpu); - dev->offline = true; + dev_set_offline(dev); /* Tell user space about the state change */ kobject_uevent(&dev->kobj, KOBJ_OFFLINE); } @@ -2648,7 +2648,7 @@ static void cpuhp_online_cpu_device(unsigned int cpu) { struct device *dev = get_cpu_device(cpu); - dev->offline = false; + dev_clear_offline(dev); /* Tell user space about the state change */ kobject_uevent(&dev->kobj, KOBJ_ONLINE); } -- cgit v1.2.3 From 2690d071584ed8f488f2336f93272817b6999484 Mon Sep 17 00:00:00 2001 From: Markus Probst Date: Mon, 27 Apr 2026 17:55:57 +0000 Subject: rust: ACPI: fix missing match data for PRP0001 Export `acpi_of_match_device` function and use it to match the of device table against ACPI PRP0001 in Rust. This fixes id_info being None on ACPI PRP0001 devices. Using `device_get_match_data` is not possible, because Rust stores an index in the of device id instead of a data pointer. This was done this way to provide a convenient and obvious API for drivers, which can be evaluated in const context without the use of any unstable language features. Fixes: 7a718a1f26d1 ("rust: driver: implement `Adapter`") Signed-off-by: Markus Probst Acked-by: Rafael J. Wysocki (Intel) # ACPI Link: https://patch.msgid.link/20260427-rust_acpi_prp0001-v6-1-6119b2a66183@posteo.de Signed-off-by: Danilo Krummrich --- MAINTAINERS | 1 + drivers/acpi/bus.c | 6 ++-- include/acpi/acpi_bus.h | 11 ++++++++ rust/helpers/acpi.c | 16 +++++++++++ rust/helpers/helpers.c | 1 + rust/kernel/driver.rs | 74 ++++++++++++++++++++++++++++++++++++++++--------- 6 files changed, 93 insertions(+), 16 deletions(-) create mode 100644 rust/helpers/acpi.c diff --git a/MAINTAINERS b/MAINTAINERS index 2fb1c75afd16..4367a303e90e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -291,6 +291,7 @@ F: include/linux/acpi.h F: include/linux/fwnode.h F: include/linux/fw_table.h F: lib/fw_table.c +F: rust/helpers/acpi.c F: rust/kernel/acpi.rs F: tools/power/acpi/ diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 2ec095e2009e..554dbddb1c8c 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -831,9 +831,9 @@ const struct acpi_device *acpi_companion_match(const struct device *dev) * identifiers and a _DSD object with the "compatible" property, use that * property to match against the given list of identifiers. */ -static bool acpi_of_match_device(const struct acpi_device *adev, - const struct of_device_id *of_match_table, - const struct of_device_id **of_id) +bool acpi_of_match_device(const struct acpi_device *adev, + const struct of_device_id *of_match_table, + const struct of_device_id **of_id) { const union acpi_object *of_compatible, *obj; int i, nval; diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index b701b5f972cb..c85f7d8f1632 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -187,6 +187,10 @@ struct acpi_driver { * ----------- */ +bool acpi_of_match_device(const struct acpi_device *adev, + const struct of_device_id *of_match_table, + const struct of_device_id **of_id); + /* Status (_STA) */ struct acpi_device_status { @@ -992,6 +996,13 @@ int acpi_scan_add_dep(acpi_handle handle, struct acpi_handle_list *dep_devices); u32 arch_acpi_add_auto_dep(acpi_handle handle); #else /* CONFIG_ACPI */ +static inline bool acpi_of_match_device(const struct acpi_device *adev, + const struct of_device_id *of_match_table, + const struct of_device_id **of_id) +{ + return false; +} + static inline int register_acpi_bus_type(void *bus) { return 0; } static inline int unregister_acpi_bus_type(void *bus) { return 0; } diff --git a/rust/helpers/acpi.c b/rust/helpers/acpi.c new file mode 100644 index 000000000000..e75c9807bbad --- /dev/null +++ b/rust/helpers/acpi.c @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include + +__rust_helper bool rust_helper_acpi_of_match_device(const struct acpi_device *adev, + const struct of_device_id *of_match_table, + const struct of_device_id **of_id) +{ + return acpi_of_match_device(adev, of_match_table, of_id); +} + +__rust_helper struct acpi_device *rust_helper_to_acpi_device_node(struct fwnode_handle *fwnode) +{ + return to_acpi_device_node(fwnode); +} diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c index 625921e27dfb..38b34518eff1 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -38,6 +38,7 @@ #define __rust_helper __always_inline #endif +#include "acpi.c" #include "atomic.c" #include "atomic_ext.c" #include "auxiliary.c" diff --git a/rust/kernel/driver.rs b/rust/kernel/driver.rs index 36de8098754d..93e5dd6ae371 100644 --- a/rust/kernel/driver.rs +++ b/rust/kernel/driver.rs @@ -278,6 +278,26 @@ macro_rules! module_driver { } } +// Calling the FFI function directly from the `Adapter` impl may result in it being called +// directly from driver modules. This happens since the Rust compiler will use monomorphisation, so +// it might happen that functions are instantiated within the calling driver module. For now, work +// around this with `#[inline(never)]` helpers. +// +// TODO: Remove once a more generic solution has been implemented. For instance, we may be able to +// leverage `bindgen` to take care of this depending on whether a symbol is (already) exported. +#[inline(never)] +#[allow(clippy::missing_safety_doc)] +#[allow(dead_code)] +#[must_use] +unsafe fn acpi_of_match_device( + adev: *const bindings::acpi_device, + of_match_table: *const bindings::of_device_id, + of_id: *mut *const bindings::of_device_id, +) -> bool { + // SAFETY: Safety requirements are the same as `bindings::acpi_of_match_device`. + unsafe { bindings::acpi_of_match_device(adev, of_match_table, of_id) } +} + /// The bus independent adapter to match a drivers and a devices. /// /// This trait should be implemented by the bus specific adapter, which represents the connection @@ -329,35 +349,63 @@ pub trait Adapter { /// /// If this returns `None`, it means there is no match with an entry in the [`of::IdTable`]. fn of_id_info(dev: &device::Device) -> Option<&'static Self::IdInfo> { - #[cfg(not(CONFIG_OF))] + let table = Self::of_id_table()?; + + #[cfg(not(any(CONFIG_OF, CONFIG_ACPI)))] { - let _ = dev; - None + let _ = (dev, table); } #[cfg(CONFIG_OF)] { - let table = Self::of_id_table()?; - // SAFETY: // - `table` has static lifetime, hence it's valid for read, // - `dev` is guaranteed to be valid while it's alive, and so is `dev.as_raw()`. let raw_id = unsafe { bindings::of_match_device(table.as_ptr(), dev.as_raw()) }; - if raw_id.is_null() { - None - } else { + if !raw_id.is_null() { // SAFETY: `DeviceId` is a `#[repr(transparent)]` wrapper of `struct of_device_id` // and does not add additional invariants, so it's safe to transmute. let id = unsafe { &*raw_id.cast::() }; - Some( - table.info(::index( - id, - )), - ) + return Some(table.info( + ::index(id), + )); } } + + #[cfg(CONFIG_ACPI)] + { + use core::ptr; + use device::property::FwNode; + + let mut raw_id = ptr::null(); + + let fwnode = dev.fwnode().map_or(ptr::null_mut(), FwNode::as_raw); + + // SAFETY: `fwnode` is a pointer to a valid `fwnode_handle`. A null pointer will be + // passed through the function. + let adev = unsafe { bindings::to_acpi_device_node(fwnode) }; + + // SAFETY: + // - `adev` is a valid pointer to `acpi_device` or is null. It is guaranteed to be + // valid as long as `dev` is alive. + // - `table` has static lifetime, hence it's valid for read. + if unsafe { acpi_of_match_device(adev, table.as_ptr(), &raw mut raw_id) } { + // SAFETY: + // - the function returns true, therefore `raw_id` has been set to a pointer to a + // valid `of_device_id`. + // - `DeviceId` is a `#[repr(transparent)]` wrapper of `struct of_device_id` + // and does not add additional invariants, so it's safe to transmute. + let id = unsafe { &*raw_id.cast::() }; + + return Some(table.info( + ::index(id), + )); + } + } + + None } /// Returns the driver's private data from the matching entry of any of the ID tables, if any. -- cgit v1.2.3 From c9447f101315bcec9b6603de0be59ca0e6b8b505 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 16 Apr 2026 16:41:44 +0200 Subject: Documentation: update deferred_probe_timeout cmdline parameter documentation Extend the deferred_probe_timeout cmdline parameter documentation, documenting that negative values are treated as an infinite timeout value. Suggested-by: Danilo Krummrich Signed-off-by: Hans de Goede Link: https://patch.msgid.link/20260416144144.54547-1-johannes.goede@oss.qualcomm.com Signed-off-by: Danilo Krummrich --- Documentation/admin-guide/kernel-parameters.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 4d0f545fb3ec..b5a51a36a048 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -1246,8 +1246,9 @@ Kernel parameters deferred probe to give up waiting on dependencies to probe. Only specific dependencies (subsystems or drivers) that have opted in will be ignored. A timeout - of 0 will timeout at the end of initcalls. If the time - out hasn't expired, it'll be restarted by each + of 0 will timeout at the end of initcalls; a negative + value is treated as an infinite timeout value. If the + timeout hasn't expired, it'll be restarted by each successful driver registration. This option will also dump out devices still on the deferred probe list after retrying. -- cgit v1.2.3 From e9506871a8ea304cde48ff4a57226df2aadddae3 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Sat, 18 Apr 2026 18:22:18 +0200 Subject: driver core: use READ_ONCE() for dev->driver in dev_has_sync_state() dev_has_sync_state() reads dev->driver twice without holding device_lock() -- once for the NULL check and once to dereference ->sync_state. Some callers only hold device_links_write_lock, which doesn't prevent a concurrent unbind from clearing dev->driver via device_unbind_cleanup(). Fix it by reading dev->driver exactly once with READ_ONCE(), pairing with the WRITE_ONCE() in device_set_driver(). Link: https://lore.kernel.org/driver-core/DHW8QPU1VU1F.3P6PH69HLFBYC@kernel.org/ Fixes: ac338acf514e ("driver core: Add dev_has_sync_state()") Reviewed-by: Rafael J. Wysocki (Intel) Acked-by: Greg Kroah-Hartman Reviewed-by: Saravana Kannan Link: https://patch.msgid.link/20260418162221.1121873-1-dakr@kernel.org Signed-off-by: Danilo Krummrich --- include/linux/device.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/linux/device.h b/include/linux/device.h index 3bf86d2f9544..56a96e41d2c9 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -1063,9 +1063,12 @@ static inline void device_lock_assert(struct device *dev) static inline bool dev_has_sync_state(struct device *dev) { + struct device_driver *drv; + if (!dev) return false; - if (dev->driver && dev->driver->sync_state) + drv = READ_ONCE(dev->driver); + if (drv && drv->sync_state) return true; if (dev->bus && dev->bus->sync_state) return true; -- cgit v1.2.3 From 9db268212e0d7c7e3c4aef3494e55afbc1695b1f Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Tue, 21 Apr 2026 01:40:44 +0200 Subject: driver core: move dev_has_sync_state() to drivers/base/base.h All callers of dev_has_sync_state() are in drivers/base/ and any attempt to use it outside of driver-core should require good justification, so there is no need to have it defined in include/linux/device.h. Thus, move it to drivers/base/base.h. Suggested-by: Rafael J. Wysocki (Intel) Link: https://lore.kernel.org/driver-core/CAJZ5v0jkm9K9=-U_51FMsyxN2msdouRnz4sEjmxG0Btd6Hmw0w@mail.gmail.com/ Reviewed-by: Rafael J. Wysocki (Intel) Reviewed-by: Greg Kroah-Hartman Link: https://patch.msgid.link/20260420234153.2898532-1-dakr@kernel.org Signed-off-by: Danilo Krummrich --- drivers/base/base.h | 14 ++++++++++++++ include/linux/device.h | 14 -------------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/base/base.h b/drivers/base/base.h index 30b416588617..0ed1e278b957 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -188,6 +188,20 @@ static inline int driver_match_device(const struct device_driver *drv, return drv->bus->match ? drv->bus->match(dev, drv) : 1; } +static inline bool dev_has_sync_state(struct device *dev) +{ + struct device_driver *drv; + + if (!dev) + return false; + drv = READ_ONCE(dev->driver); + if (drv && drv->sync_state) + return true; + if (dev->bus && dev->bus->sync_state) + return true; + return false; +} + static inline void dev_sync_state(struct device *dev) { if (dev->bus->sync_state) diff --git a/include/linux/device.h b/include/linux/device.h index 56a96e41d2c9..d54c86d77764 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -1061,20 +1061,6 @@ static inline void device_lock_assert(struct device *dev) lockdep_assert_held(&dev->mutex); } -static inline bool dev_has_sync_state(struct device *dev) -{ - struct device_driver *drv; - - if (!dev) - return false; - drv = READ_ONCE(dev->driver); - if (drv && drv->sync_state) - return true; - if (dev->bus && dev->bus->sync_state) - return true; - return false; -} - static inline int dev_set_drv_sync_state(struct device *dev, void (*fn)(struct device *dev)) { -- cgit v1.2.3 From 580a795105dae2ef1622df72a27a8fb0605e2f6b Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 24 Apr 2026 17:31:26 +0200 Subject: driver core: faux: fix root device registration A recent change made the faux bus root device be allocated dynamically but failed to provide a release function to free the memory when the last reference is dropped (on theoretical failure to register the device or bus). Fix this by using root_device_register() instead of open coding. Also add the missing sanity check when registering faux devices to avoid use-after-free if the bus failed to register (which would previously have triggered a bunch of use-after-free warnings). Fixes: 61b76d07d2b4 ("driver core: faux: stop using static struct device") Cc: stable@vger.kernel.org # 7.0 Cc: Greg Kroah-Hartman Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260424153127.2647405-2-johan@kernel.org Signed-off-by: Danilo Krummrich --- drivers/base/faux.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/drivers/base/faux.c b/drivers/base/faux.c index fb3e42f21362..3d1d1eafb473 100644 --- a/drivers/base/faux.c +++ b/drivers/base/faux.c @@ -133,6 +133,9 @@ struct faux_device *faux_device_create_with_groups(const char *name, struct device *dev; int ret; + if (!faux_bus_root) + return NULL; + faux_obj = kzalloc_obj(*faux_obj); if (!faux_obj) return NULL; @@ -232,19 +235,12 @@ EXPORT_SYMBOL_GPL(faux_device_destroy); int __init faux_bus_init(void) { + struct device *root; int ret; - faux_bus_root = kzalloc_obj(*faux_bus_root); - if (!faux_bus_root) - return -ENOMEM; - - dev_set_name(faux_bus_root, "faux"); - - ret = device_register(faux_bus_root); - if (ret) { - put_device(faux_bus_root); - return ret; - } + root = root_device_register("faux"); + if (IS_ERR(root)) + return PTR_ERR(root); ret = bus_register(&faux_bus_type); if (ret) @@ -254,12 +250,14 @@ int __init faux_bus_init(void) if (ret) goto error_driver; + faux_bus_root = root; + return ret; error_driver: bus_unregister(&faux_bus_type); error_bus: - device_unregister(faux_bus_root); + root_device_unregister(root); return ret; } -- cgit v1.2.3 From 1a262c768f5b5a1ebbdec8cfa588f75d3a825a8d Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 24 Apr 2026 17:31:27 +0200 Subject: driver core: faux: clean up init error handling Clean up the faux bus init error handling by naming the labels after what they do (rather than from where they are jumped to) and separating the success path more clearly by returning explicit zero. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260424153127.2647405-3-johan@kernel.org Signed-off-by: Danilo Krummrich --- drivers/base/faux.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/base/faux.c b/drivers/base/faux.c index 3d1d1eafb473..a8329f88222e 100644 --- a/drivers/base/faux.c +++ b/drivers/base/faux.c @@ -244,20 +244,20 @@ int __init faux_bus_init(void) ret = bus_register(&faux_bus_type); if (ret) - goto error_bus; + goto err_deregister_root; ret = driver_register(&faux_driver); if (ret) - goto error_driver; + goto err_deregister_bus; faux_bus_root = root; - return ret; + return 0; -error_driver: +err_deregister_bus: bus_unregister(&faux_bus_type); - -error_bus: +err_deregister_root: root_device_unregister(root); + return ret; } -- cgit v1.2.3 From 36f35b8df6972167102a1c3d4361e0afb6a84534 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 30 Apr 2026 11:17:18 +0200 Subject: driver core: reject devices with unregistered buses Trying to register a device on a bus which has not yet been registered used to trigger a NULL-pointer dereference, but since the const bus structure rework registration instead succeeds without the device being added to the bus. This specifically means that the device will never bind to a driver and that the bus sysfs attributes are not created (i.e. as if the device had no bus). Reject devices with unregistered buses to catch any callers that get the ordering wrong and to handle bus registration failures more gracefully. Fixes: 5221b82d46f2 ("driver core: bus: bus_add/probe/remove_device() cleanups") Cc: stable@vger.kernel.org # 6.3 Cc: Greg Kroah-Hartman Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260430091718.230228-1-johan@kernel.org Signed-off-by: Danilo Krummrich --- drivers/base/bus.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 8b6722ff8590..d17bd91490ee 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -544,10 +544,10 @@ static const struct attribute_group driver_override_dev_group = { */ int bus_add_device(struct device *dev) { - struct subsys_private *sp = bus_to_subsys(dev->bus); + struct subsys_private *sp; int error; - if (!sp) { + if (!dev->bus) { /* * This is a normal operation for many devices that do not * have a bus assigned to them, just say that all went @@ -556,6 +556,13 @@ int bus_add_device(struct device *dev) return 0; } + sp = bus_to_subsys(dev->bus); + if (!sp) { + pr_err("%s: cannot add device '%s' to unregistered bus '%s'\n", + __func__, dev_name(dev), dev->bus->name); + return -EINVAL; + } + /* * Reference in sp is now incremented and will be dropped when * the device is removed from the bus -- cgit v1.2.3 From 627e28455578d2faef6552cd15d241b20e27e423 Mon Sep 17 00:00:00 2001 From: Prabhudasu Vatala Date: Sun, 3 May 2026 19:48:26 +0530 Subject: driver core: class: fix typo in struct class documentation Fix a spelling error in the comment for the ns_type member of struct class. Change "detemine" to "determine". Signed-off-by: Prabhudasu Vatala Link: https://patch.msgid.link/20260503141826.27462-1-prabhudasuvatala@gmail.com Signed-off-by: Danilo Krummrich --- include/linux/device/class.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/device/class.h b/include/linux/device/class.h index 78ab8d2b3e30..9db1d61ba743 100644 --- a/include/linux/device/class.h +++ b/include/linux/device/class.h @@ -34,7 +34,7 @@ struct fwnode_handle; * @class_release: Called to release this class. * @dev_release: Called to release the device. * @shutdown_pre: Called at shut-down time before driver shutdown. - * @ns_type: Callbacks so sysfs can detemine namespaces. + * @ns_type: Callbacks so sysfs can determine namespaces. * @namespace: Namespace of the device belongs to this class. * @get_ownership: Allows class to specify uid/gid of the sysfs directories * for the devices belonging to the class. Usually tied to -- cgit v1.2.3 From 30c878ed169983190f77940594f8ba8948debe6b Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 24 Apr 2026 12:24:00 +0200 Subject: isa: switch to dynamic root device Driver core expects devices to be dynamically allocated and will, for example, complain loudly if a device that lacks a release function is ever freed. Use root_device_register() to allocate and register the root device instead of open coding using a static device. Note that this also fixes a reference leak in case device_register() fails which may be flagged by static checkers. Signed-off-by: Johan Hovold Acked-by: William Breathitt Gray Link: https://patch.msgid.link/20260424102400.2615677-1-johan@kernel.org Signed-off-by: Danilo Krummrich --- drivers/base/isa.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/base/isa.c b/drivers/base/isa.c index fd076cc63cb6..5887e4211f80 100644 --- a/drivers/base/isa.c +++ b/drivers/base/isa.c @@ -11,9 +11,7 @@ #include #include -static struct device isa_bus = { - .init_name = "isa" -}; +static struct device *isa_bus; struct isa_dev { struct device dev; @@ -131,7 +129,7 @@ int isa_register_driver(struct isa_driver *isa_driver, unsigned int ndev) break; } - isa_dev->dev.parent = &isa_bus; + isa_dev->dev.parent = isa_bus; isa_dev->dev.bus = &isa_bus_type; dev_set_name(&isa_dev->dev, "%s.%u", @@ -169,9 +167,11 @@ static int __init isa_bus_init(void) error = bus_register(&isa_bus_type); if (!error) { - error = device_register(&isa_bus); - if (error) + isa_bus = root_device_register("isa"); + if (IS_ERR(isa_bus)) { + error = PTR_ERR(isa_bus); bus_unregister(&isa_bus_type); + } } return error; } -- cgit v1.2.3 From 896df22ee57648b0c505bd76ddbc6b2341834696 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Tue, 5 May 2026 17:12:31 +0800 Subject: firmware_loader: fix device reference leak in firmware_upload_register() firmware_upload_register() -> fw_create_instance() -> device_initialize() After fw_create_instance() succeeds, the lifetime of the embedded struct device is expected to be managed through the device core reference counting, since fw_create_instance() has already called device_initialize(). In firmware_upload_register(), if alloc_lookup_fw_priv() fails after fw_create_instance() succeeds, the code reaches free_fw_sysfs and frees fw_sysfs directly instead of releasing the device reference with put_device(). This may leave the reference count of the embedded struct device unbalanced, resulting in a refcount leak. The issue was identified by a static analysis tool I developed and confirmed by manual review. Fix this by using put_device(fw_dev) in the failure path and letting fw_dev_release() handle the final cleanup, instead of freeing the instance directly from the error path. Fixes: 97730bbb242c ("firmware_loader: Add firmware-upload support") Cc: stable@vger.kernel.org Signed-off-by: Guangshuo Li Link: https://patch.msgid.link/20260505091231.607089-1-lgs201920130244@gmail.com Signed-off-by: Danilo Krummrich --- drivers/base/firmware_loader/sysfs_upload.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/base/firmware_loader/sysfs_upload.c b/drivers/base/firmware_loader/sysfs_upload.c index f59a7856934c..efc33294212f 100644 --- a/drivers/base/firmware_loader/sysfs_upload.c +++ b/drivers/base/firmware_loader/sysfs_upload.c @@ -343,7 +343,6 @@ firmware_upload_register(struct module *module, struct device *parent, goto free_fw_upload_priv; } fw_upload->priv = fw_sysfs; - fw_sysfs->fw_upload_priv = fw_upload_priv; fw_dev = &fw_sysfs->dev; ret = alloc_lookup_fw_priv(name, &fw_cache, &fw_priv, NULL, 0, 0, @@ -351,10 +350,12 @@ firmware_upload_register(struct module *module, struct device *parent, if (ret != 0) { if (ret > 0) ret = -EINVAL; - goto free_fw_sysfs; + put_device(fw_dev); + goto free_fw_upload_priv; } fw_priv->is_paged_buf = true; fw_sysfs->fw_priv = fw_priv; + fw_sysfs->fw_upload_priv = fw_upload_priv; ret = device_add(fw_dev); if (ret) { @@ -365,9 +366,6 @@ firmware_upload_register(struct module *module, struct device *parent, return fw_upload; -free_fw_sysfs: - kfree(fw_sysfs); - free_fw_upload_priv: kfree(fw_upload_priv); -- cgit v1.2.3 From f71cc65c01bdac1bc7705b1aad34f0023b41ec4d Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Thu, 7 May 2026 12:01:43 +0300 Subject: sysfs: upgrade OOB write by buggy .show hook into WARNing Buggy .show hook will get just 1 line of dmesg: fill_read_buffer: ext4_attr_show+0x0/0x600 returned bad count It may or may not oops later in some unrelated process. But buggy .show hook most likely is corrupting random memory past sysfs buffer therefore deserving more. WARN, make it more visible and let QA machines panic earlier. Also, delete useless cast -- "count" is >=0 at this point. Signed-off-by: Alexey Dobriyan Reviewed-by: Danilo Krummrich Link: https://patch.msgid.link/3cc3e8c6-c6e8-4625-a88f-f5708b935dab@p183 Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/file.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index 5709cede1d75..6b3a8f62fa89 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -70,9 +70,8 @@ static int sysfs_kf_seq_show(struct seq_file *sf, void *v) * The code works fine with PAGE_SIZE return but it's likely to * indicate truncated result or overflow in normal use cases. */ - if (count >= (ssize_t)PAGE_SIZE) { - printk("fill_read_buffer: %pS returned bad count\n", - ops->show); + if (count >= PAGE_SIZE) { + WARN(1, "OOB write or bad count %zd at %pS\n", count, ops->show); /* Try to struggle along */ count = PAGE_SIZE - 1; } -- cgit v1.2.3 From c393ef0741d4bb8848e3e1f77a66404eaf174298 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 27 Apr 2026 15:18:25 +0200 Subject: software node: provide wrappers around kobject_get/put() Make the code more readable by avoid constant dereferencing of the swnode's kobject when managing references. Provide wrappers that take struct swnode * as argument and make them hide that logic. Signed-off-by: Bartosz Golaszewski Reviewed-by: Rafael J. Wysocki (Intel) Link: https://patch.msgid.link/20260427131825.15793-1-bartosz.golaszewski@oss.qualcomm.com Signed-off-by: Danilo Krummrich --- drivers/base/swnode.c | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c index a19f8f722bc8..869228a65cb3 100644 --- a/drivers/base/swnode.c +++ b/drivers/base/swnode.c @@ -374,20 +374,28 @@ EXPORT_SYMBOL_GPL(property_entries_free); /* -------------------------------------------------------------------------- */ /* fwnode operations */ -static struct fwnode_handle *software_node_get(struct fwnode_handle *fwnode) +static struct swnode *swnode_get(struct swnode *swnode) { - struct swnode *swnode = to_swnode(fwnode); - kobject_get(&swnode->kobj); + return swnode; +} + +static void swnode_put(struct swnode *swnode) +{ + kobject_put(&swnode->kobj); +} + +static struct fwnode_handle *software_node_get(struct fwnode_handle *fwnode) +{ + struct swnode *swnode = swnode_get(to_swnode(fwnode)); + return &swnode->fwnode; } static void software_node_put(struct fwnode_handle *fwnode) { - struct swnode *swnode = to_swnode(fwnode); - - kobject_put(&swnode->kobj); + swnode_put(to_swnode(fwnode)); } static bool software_node_property_present(const struct fwnode_handle *fwnode, @@ -493,7 +501,7 @@ software_node_get_named_child_node(const struct fwnode_handle *fwnode, list_for_each_entry(child, &swnode->children, entry) { if (!strcmp(childname, kobject_name(&child->kobj))) { - kobject_get(&child->kobj); + swnode_get(child); return &child->fwnode; } } @@ -737,7 +745,7 @@ software_node_find_by_name(const struct software_node *parent, const char *name) swnode = kobj_to_swnode(k); if (parent == swnode->node->parent && swnode->node->name && !strcmp(name, swnode->node->name)) { - kobject_get(&swnode->kobj); + swnode_get(swnode); break; } swnode = NULL; @@ -835,13 +843,13 @@ swnode_register(const struct software_node *node, struct swnode *parent, parent ? &parent->kobj : NULL, "node%d", swnode->id); if (ret) { - kobject_put(&swnode->kobj); + swnode_put(swnode); return ERR_PTR(ret); } /* * Assign the flag only in the successful case, so - * the above kobject_put() won't mess up with properties. + * the above swnode_put() won't mess up with properties. */ swnode->allocated = allocated; @@ -978,7 +986,7 @@ void fwnode_remove_software_node(struct fwnode_handle *fwnode) if (!swnode) return; - kobject_put(&swnode->kobj); + swnode_put(swnode); } EXPORT_SYMBOL_GPL(fwnode_remove_software_node); @@ -1002,7 +1010,7 @@ int device_add_software_node(struct device *dev, const struct software_node *nod swnode = software_node_to_swnode(node); if (swnode) { - kobject_get(&swnode->kobj); + swnode_get(swnode); } else { ret = software_node_register(node); if (ret) @@ -1044,7 +1052,7 @@ void device_remove_software_node(struct device *dev) software_node_notify_remove(dev); set_secondary_fwnode(dev, NULL); - kobject_put(&swnode->kobj); + swnode_put(swnode); } EXPORT_SYMBOL_GPL(device_remove_software_node); @@ -1097,7 +1105,7 @@ void software_node_notify(struct device *dev) if (!swnode) return; - kobject_get(&swnode->kobj); + swnode_get(swnode); ret = sysfs_create_link(&dev->kobj, &swnode->kobj, "software_node"); if (ret) return; @@ -1119,11 +1127,11 @@ void software_node_notify_remove(struct device *dev) sysfs_remove_link(&swnode->kobj, dev_name(dev)); sysfs_remove_link(&dev->kobj, "software_node"); - kobject_put(&swnode->kobj); + swnode_put(swnode); if (swnode->managed) { set_secondary_fwnode(dev, NULL); - kobject_put(&swnode->kobj); + swnode_put(swnode); } } -- cgit v1.2.3 From abb21500e7e5dcf2d1b1a4a02b2ee77b3d5061b6 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Tue, 5 May 2026 17:23:07 +0200 Subject: rust: alloc: add Box::zeroed() Add Box::zeroed() for T: Zeroable types. This allocates with __GFP_ZERO directly, letting the underlying allocator deal with zeroing out the memory compared to Box::new(T::zeroed(), flags). Reviewed-by: Alice Ryhl Link: https://patch.msgid.link/20260505152400.3905096-2-dakr@kernel.org Signed-off-by: Danilo Krummrich --- rust/kernel/alloc/kbox.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/rust/kernel/alloc/kbox.rs b/rust/kernel/alloc/kbox.rs index bd6da02c7ab8..c824ed6e1523 100644 --- a/rust/kernel/alloc/kbox.rs +++ b/rust/kernel/alloc/kbox.rs @@ -19,6 +19,7 @@ use crate::ffi::c_void; use crate::fmt; use crate::init::InPlaceInit; use crate::page::AsPageIter; +use crate::prelude::*; use crate::types::ForeignOwnable; use pin_init::{InPlaceWrite, Init, PinInit, ZeroableOption}; @@ -256,6 +257,27 @@ where Ok(Box(ptr.cast(), PhantomData)) } + /// Creates a new zero-initialized `Box`. + /// + /// New memory is allocated with `A` and the [`__GFP_ZERO`] flag. The allocation may fail, in + /// which case an error is returned. For ZSTs no memory is allocated. + /// + /// # Examples + /// + /// ``` + /// let b = KBox::<[u8; 128]>::zeroed(GFP_KERNEL)?; + /// assert_eq!(*b, [0; 128]); + /// # Ok::<(), Error>(()) + /// ``` + pub fn zeroed(flags: Flags) -> Result + where + T: Zeroable, + { + // SAFETY: `__GFP_ZERO` guarantees the memory is zeroed; `T: Zeroable` guarantees that + // all-zeroes is a valid bit pattern for `T`. + Ok(unsafe { Self::new_uninit(flags | __GFP_ZERO)?.assume_init() }) + } + /// Constructs a new `Pin>`. If `T` does not implement [`Unpin`], then `x` will be /// pinned in memory and can't be moved. #[inline] -- cgit v1.2.3 From fd3b87ff0232f46e1ad53a48609a3853c8757c6c Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Tue, 5 May 2026 17:23:08 +0200 Subject: rust: auxiliary: add registration data to auxiliary devices Add a registration_data pointer to struct auxiliary_device, allowing the registering (parent) driver to attach private data to the device at registration time and retrieve it later when called back by the auxiliary (child) driver. By tying the data to the device's registration, Rust drivers can bind the lifetime of device resources to it, since the auxiliary bus guarantees that the parent driver remains bound while the auxiliary device is bound. On the Rust side, Registration takes ownership of the data via ForeignOwnable. A TypeId is stored alongside the data for runtime type checking, making Device::registration_data() a safe method. Reviewed-by: Alice Ryhl Link: https://patch.msgid.link/20260505152400.3905096-3-dakr@kernel.org Signed-off-by: Danilo Krummrich --- drivers/gpu/nova-core/driver.rs | 10 +- include/linux/auxiliary_bus.h | 4 + rust/kernel/auxiliary.rs | 208 ++++++++++++++++++++++++---------- samples/rust/rust_driver_auxiliary.rs | 40 ++++--- 4 files changed, 180 insertions(+), 82 deletions(-) diff --git a/drivers/gpu/nova-core/driver.rs b/drivers/gpu/nova-core/driver.rs index 84b0e1703150..8fe484d357f6 100644 --- a/drivers/gpu/nova-core/driver.rs +++ b/drivers/gpu/nova-core/driver.rs @@ -32,8 +32,7 @@ static AUXILIARY_ID_COUNTER: Atomic = Atomic::new(0); pub(crate) struct NovaCore { #[pin] pub(crate) gpu: Gpu, - #[pin] - _reg: Devres, + _reg: Devres>, } const BAR0_SIZE: usize = SZ_16M; @@ -96,14 +95,15 @@ impl pci::Driver for NovaCore { Ok(try_pin_init!(Self { gpu <- Gpu::new(pdev, bar.clone(), bar.access(pdev.as_ref())?), - _reg <- auxiliary::Registration::new( + _reg: auxiliary::Registration::new( pdev.as_ref(), c"nova-drm", // TODO[XARR]: Use XArray or perhaps IDA for proper ID allocation/recycling. For // now, use a simple atomic counter that never recycles IDs. AUXILIARY_ID_COUNTER.fetch_add(1, Relaxed), - crate::MODULE_NAME - ), + crate::MODULE_NAME, + (), + )?, })) }) } diff --git a/include/linux/auxiliary_bus.h b/include/linux/auxiliary_bus.h index bc09b55e3682..4e1ad8ccbcdd 100644 --- a/include/linux/auxiliary_bus.h +++ b/include/linux/auxiliary_bus.h @@ -62,6 +62,9 @@ * @sysfs.irqs: irqs xarray contains irq indices which are used by the device, * @sysfs.lock: Synchronize irq sysfs creation, * @sysfs.irq_dir_exists: whether "irqs" directory exists, + * @registration_data_rust: private data owned by the registering (parent) + * driver; valid for as long as the device is + * registered with the driver core, * * An auxiliary_device represents a part of its parent device's functionality. * It is given a name that, combined with the registering drivers @@ -148,6 +151,7 @@ struct auxiliary_device { struct mutex lock; /* Synchronize irq sysfs creation */ bool irq_dir_exists; } sysfs; + void *registration_data_rust; }; /** diff --git a/rust/kernel/auxiliary.rs b/rust/kernel/auxiliary.rs index 93c0db1f6655..19aec94aa95b 100644 --- a/rust/kernel/auxiliary.rs +++ b/rust/kernel/auxiliary.rs @@ -19,12 +19,17 @@ use crate::{ to_result, // }, prelude::*, - types::Opaque, + types::{ + ForeignOwnable, + Opaque, // + }, ThisModule, // }; use core::{ + any::TypeId, marker::PhantomData, mem::offset_of, + pin::Pin, ptr::{ addr_of_mut, NonNull, // @@ -257,6 +262,40 @@ impl Device { // SAFETY: A bound auxiliary device always has a bound parent device. unsafe { parent.as_bound() } } + + /// Returns a pinned reference to the registration data set by the registering (parent) driver. + /// + /// Returns [`EINVAL`] if `T` does not match the type used by the parent driver when calling + /// [`Registration::new()`]. + /// + /// Returns [`ENOENT`] if no registration data has been set, e.g. when the device was + /// registered by a C driver. + pub fn registration_data(&self) -> Result> { + // SAFETY: By the type invariant, `self.as_raw()` is a valid `struct auxiliary_device`. + let ptr = unsafe { (*self.as_raw()).registration_data_rust }; + if ptr.is_null() { + dev_warn!( + self.as_ref(), + "No registration data set; parent is not a Rust driver.\n" + ); + return Err(ENOENT); + } + + // SAFETY: `ptr` is non-null and was set via `into_foreign()` in `Registration::new()`; + // `RegistrationData` is `#[repr(C)]` with `type_id` at offset 0, so reading a `TypeId` + // at the start of the allocation is valid regardless of `T`. + let type_id = unsafe { ptr.cast::().read() }; + if type_id != TypeId::of::() { + return Err(EINVAL); + } + + // SAFETY: The `TypeId` check above confirms that the stored type is `T`; `ptr` remains + // valid until `Registration::drop()` calls `from_foreign()`. + let wrapper = unsafe { Pin::>>::borrow(ptr) }; + + // SAFETY: `data` is a structurally pinned field of `RegistrationData`. + Ok(unsafe { wrapper.map_unchecked(|w| &w.data) }) + } } impl Device { @@ -326,87 +365,132 @@ unsafe impl Send for Device {} // (i.e. `Device) are thread safe. unsafe impl Sync for Device {} +/// Wrapper that stores a [`TypeId`] alongside the registration data for runtime type checking. +#[repr(C)] +#[pin_data] +struct RegistrationData { + type_id: TypeId, + #[pin] + data: T, +} + /// The registration of an auxiliary device. /// /// This type represents the registration of a [`struct auxiliary_device`]. When its parent device /// is unbound, the corresponding auxiliary device will be unregistered from the system. /// +/// The type parameter `T` is the type of the registration data owned by the registering (parent) +/// driver. It can be accessed by the auxiliary driver through +/// [`Device::registration_data()`]. +/// /// # Invariants /// -/// `self.0` always holds a valid pointer to an initialized and registered -/// [`struct auxiliary_device`]. -pub struct Registration(NonNull); - -impl Registration { - /// Create and register a new auxiliary device. - pub fn new<'a>( - parent: &'a device::Device, - name: &'a CStr, +/// `self.adev` always holds a valid pointer to an initialized and registered +/// [`struct auxiliary_device`] whose `registration_data_rust` field points to a +/// valid `Pin>>`. +pub struct Registration { + adev: NonNull, + _data: PhantomData, +} + +impl Registration { + /// Create and register a new auxiliary device with the given registration data. + /// + /// The `data` is owned by the registration and can be accessed through the auxiliary device + /// via [`Device::registration_data()`]. + pub fn new( + parent: &device::Device, + name: &CStr, id: u32, - modname: &'a CStr, - ) -> impl PinInit, Error> + 'a { - pin_init::pin_init_scope(move || { - let boxed = KBox::new(Opaque::::zeroed(), GFP_KERNEL)?; - let adev = boxed.get(); - - // SAFETY: It's safe to set the fields of `struct auxiliary_device` on initialization. - unsafe { - (*adev).dev.parent = parent.as_raw(); - (*adev).dev.release = Some(Device::release); - (*adev).name = name.as_char_ptr(); - (*adev).id = id; - } - - // SAFETY: `adev` is guaranteed to be a valid pointer to a `struct auxiliary_device`, - // which has not been initialized yet. - unsafe { bindings::auxiliary_device_init(adev) }; - - // Now that `adev` is initialized, leak the `Box`; the corresponding memory will be - // freed by `Device::release` when the last reference to the `struct auxiliary_device` - // is dropped. - let _ = KBox::into_raw(boxed); - - // SAFETY: - // - `adev` is guaranteed to be a valid pointer to a `struct auxiliary_device`, which - // has been initialized, - // - `modname.as_char_ptr()` is a NULL terminated string. - let ret = unsafe { bindings::__auxiliary_device_add(adev, modname.as_char_ptr()) }; - if ret != 0 { - // SAFETY: `adev` is guaranteed to be a valid pointer to a - // `struct auxiliary_device`, which has been initialized. - unsafe { bindings::auxiliary_device_uninit(adev) }; - - return Err(Error::from_errno(ret)); - } - - // INVARIANT: The device will remain registered until `auxiliary_device_delete()` is - // called, which happens in `Self::drop()`. - Ok(Devres::new( - parent, - // SAFETY: `adev` is guaranteed to be non-null, since the `KBox` was allocated - // successfully. - Self(unsafe { NonNull::new_unchecked(adev) }), - )) - }) + modname: &CStr, + data: impl PinInit, + ) -> Result> + where + Error: From, + { + let data = KBox::pin_init::( + try_pin_init!(RegistrationData { + type_id: TypeId::of::(), + data <- data, + }), + GFP_KERNEL, + )?; + + let boxed: KBox> = KBox::zeroed(GFP_KERNEL)?; + let adev = boxed.get(); + + // SAFETY: It's safe to set the fields of `struct auxiliary_device` on initialization. + unsafe { + (*adev).dev.parent = parent.as_raw(); + (*adev).dev.release = Some(Device::release); + (*adev).name = name.as_char_ptr(); + (*adev).id = id; + (*adev).registration_data_rust = data.into_foreign(); + } + + // SAFETY: `adev` is guaranteed to be a valid pointer to a `struct auxiliary_device`, + // which has not been initialized yet. + unsafe { bindings::auxiliary_device_init(adev) }; + + // Now that `adev` is initialized, leak the `Box`; the corresponding memory will be + // freed by `Device::release` when the last reference to the `struct auxiliary_device` + // is dropped. + let _ = KBox::into_raw(boxed); + + // SAFETY: + // - `adev` is guaranteed to be a valid pointer to a `struct auxiliary_device`, which + // has been initialized, + // - `modname.as_char_ptr()` is a NULL terminated string. + let ret = unsafe { bindings::__auxiliary_device_add(adev, modname.as_char_ptr()) }; + if ret != 0 { + // SAFETY: `registration_data` was set above via `into_foreign()`. + drop(unsafe { + Pin::>>::from_foreign((*adev).registration_data_rust) + }); + + // SAFETY: `adev` is guaranteed to be a valid pointer to a + // `struct auxiliary_device`, which has been initialized. + unsafe { bindings::auxiliary_device_uninit(adev) }; + + return Err(Error::from_errno(ret)); + } + + // INVARIANT: The device will remain registered until `auxiliary_device_delete()` is + // called, which happens in `Self::drop()`. + let reg = Self { + // SAFETY: `adev` is guaranteed to be non-null, since the `KBox` was allocated + // successfully. + adev: unsafe { NonNull::new_unchecked(adev) }, + _data: PhantomData, + }; + + Devres::new::(parent, reg) } } -impl Drop for Registration { +impl Drop for Registration { fn drop(&mut self) { - // SAFETY: By the type invariant of `Self`, `self.0.as_ptr()` is a valid registered + // SAFETY: By the type invariant of `Self`, `self.adev.as_ptr()` is a valid registered // `struct auxiliary_device`. - unsafe { bindings::auxiliary_device_delete(self.0.as_ptr()) }; + unsafe { bindings::auxiliary_device_delete(self.adev.as_ptr()) }; + + // SAFETY: `registration_data` was set in `new()` via `into_foreign()`. + drop(unsafe { + Pin::>>::from_foreign( + (*self.adev.as_ptr()).registration_data_rust, + ) + }); // This drops the reference we acquired through `auxiliary_device_init()`. // - // SAFETY: By the type invariant of `Self`, `self.0.as_ptr()` is a valid registered + // SAFETY: By the type invariant of `Self`, `self.adev.as_ptr()` is a valid registered // `struct auxiliary_device`. - unsafe { bindings::auxiliary_device_uninit(self.0.as_ptr()) }; + unsafe { bindings::auxiliary_device_uninit(self.adev.as_ptr()) }; } } // SAFETY: A `Registration` of a `struct auxiliary_device` can be released from any thread. -unsafe impl Send for Registration {} +unsafe impl Send for Registration {} // SAFETY: `Registration` does not expose any methods or fields that need synchronization. -unsafe impl Sync for Registration {} +unsafe impl Sync for Registration {} diff --git a/samples/rust/rust_driver_auxiliary.rs b/samples/rust/rust_driver_auxiliary.rs index 5c5a5105a3ff..319ef734c02b 100644 --- a/samples/rust/rust_driver_auxiliary.rs +++ b/samples/rust/rust_driver_auxiliary.rs @@ -17,8 +17,6 @@ use kernel::{ InPlaceModule, // }; -use core::any::TypeId; - const MODULE_NAME: &CStr = ::NAME; const AUXILIARY_NAME: &CStr = c"auxiliary"; @@ -49,13 +47,13 @@ impl auxiliary::Driver for AuxiliaryDriver { } } -#[pin_data] +struct Data { + index: u32, +} + struct ParentDriver { - private: TypeId, - #[pin] - _reg0: Devres, - #[pin] - _reg1: Devres, + _reg0: Devres>, + _reg1: Devres>, } kernel::pci_device_table!( @@ -71,10 +69,21 @@ impl pci::Driver for ParentDriver { const ID_TABLE: pci::IdTable = &PCI_TABLE; fn probe(pdev: &pci::Device, _info: &Self::IdInfo) -> impl PinInit { - try_pin_init!(Self { - private: TypeId::of::(), - _reg0 <- auxiliary::Registration::new(pdev.as_ref(), AUXILIARY_NAME, 0, MODULE_NAME), - _reg1 <- auxiliary::Registration::new(pdev.as_ref(), AUXILIARY_NAME, 1, MODULE_NAME), + Ok(Self { + _reg0: auxiliary::Registration::new( + pdev.as_ref(), + AUXILIARY_NAME, + 0, + MODULE_NAME, + Data { index: 0 }, + )?, + _reg1: auxiliary::Registration::new( + pdev.as_ref(), + AUXILIARY_NAME, + 1, + MODULE_NAME, + Data { index: 1 }, + )?, }) } } @@ -83,7 +92,8 @@ impl ParentDriver { fn connect(adev: &auxiliary::Device) -> Result { let dev = adev.parent(); let pdev: &pci::Device = dev.try_into()?; - let drvdata = dev.drvdata::()?; + + let data = adev.registration_data::()?; dev_info!( dev, @@ -95,8 +105,8 @@ impl ParentDriver { dev_info!( dev, - "We have access to the private data of {:?}.\n", - drvdata.private + "Connected to auxiliary device with index {}.\n", + data.index ); Ok(()) -- cgit v1.2.3 From 95ade775c4ab9b9b3d7cfa2d45283e93fbfa4e7a Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Tue, 5 May 2026 17:23:09 +0200 Subject: rust: driver core: remove drvdata() and driver_type When drvdata() was introduced in commit 6f61a2637abe ("rust: device: introduce Device::drvdata()"), its commit message already noted that a direct accessor to the driver's bus device private data is not commonly required -- bus callbacks provide access through &self, and other entry points (IRQs, workqueues, IOCTLs, etc.) carry their own private data. The sole motivation for drvdata() was inter-driver interaction -- an auxiliary driver deriving the parent's bus device private data from the parent device. However, drvdata() exposes the driver's bus device private data beyond the driver's own scope. This creates ordering constraints; for instance drvdata may not be set yet when the first caller of drvdata() can appear. It also forces the driver's bus device private data to outlive all registrations that access it, which causes unnecessary complications. Private data should be private to the entity that issues it, i.e. bus device private data belongs to bus callbacks, class device private data to class callbacks, IRQ private data to the IRQ handler, etc. With registration-private data now available through the auxiliary bus, there is no remaining user of drvdata(), thus remove it. Reviewed-by: Alice Ryhl Link: https://patch.msgid.link/20260505152400.3905096-4-dakr@kernel.org Signed-off-by: Danilo Krummrich --- drivers/base/base.h | 16 -------------- rust/kernel/device.rs | 60 --------------------------------------------------- 2 files changed, 76 deletions(-) diff --git a/drivers/base/base.h b/drivers/base/base.h index 30b416588617..a19f4cda2c83 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -86,18 +86,6 @@ struct driver_private { }; #define to_driver(obj) container_of(obj, struct driver_private, kobj) -#ifdef CONFIG_RUST -/** - * struct driver_type - Representation of a Rust driver type. - */ -struct driver_type { - /** - * @id: Representation of core::any::TypeId. - */ - u8 id[16]; -} __packed; -#endif - /** * struct device_private - structure to hold the private to the driver core * portions of the device structure. @@ -115,7 +103,6 @@ struct driver_type { * dev_err_probe() for later retrieval via debugfs * @device: pointer back to the struct device that this structure is * associated with. - * @driver_type: The type of the bound Rust driver. * @dead: This device is currently either in the process of or has been * removed from the system. Any asynchronous events scheduled for this * device should exit without taking any action. @@ -132,9 +119,6 @@ struct device_private { const struct device_driver *async_driver; char *deferred_probe_reason; struct device *device; -#ifdef CONFIG_RUST - struct driver_type driver_type; -#endif u8 dead:1; }; #define to_device_private_parent(obj) \ diff --git a/rust/kernel/device.rs b/rust/kernel/device.rs index 6d5396a43ebe..fd50399aadea 100644 --- a/rust/kernel/device.rs +++ b/rust/kernel/device.rs @@ -15,16 +15,12 @@ use crate::{ }, // }; use core::{ - any::TypeId, marker::PhantomData, ptr, // }; pub mod property; -// Assert that we can `read()` / `write()` a `TypeId` instance from / into `struct driver_type`. -static_assert!(core::mem::size_of::() >= core::mem::size_of::()); - /// The core representation of a device in the kernel's driver model. /// /// This structure represents the Rust abstraction for a C `struct device`. A [`Device`] can either @@ -206,29 +202,12 @@ impl Device { } impl Device { - fn set_type_id(&self) { - // SAFETY: By the type invariants, `self.as_raw()` is a valid pointer to a `struct device`. - let private = unsafe { (*self.as_raw()).p }; - - // SAFETY: For a bound device (implied by the `CoreInternal` device context), `private` is - // guaranteed to be a valid pointer to a `struct device_private`. - let driver_type = unsafe { &raw mut (*private).driver_type }; - - // SAFETY: `driver_type` is valid for (unaligned) writes of a `TypeId`. - unsafe { - driver_type - .cast::() - .write_unaligned(TypeId::of::()) - }; - } - /// Store a pointer to the bound driver's private data. pub fn set_drvdata(&self, data: impl PinInit) -> Result { let data = KBox::pin_init(data, GFP_KERNEL)?; // SAFETY: By the type invariants, `self.as_raw()` is a valid pointer to a `struct device`. unsafe { bindings::dev_set_drvdata(self.as_raw(), data.into_foreign().cast()) }; - self.set_type_id::(); Ok(()) } @@ -292,45 +271,6 @@ impl Device { // in `into_foreign()`. unsafe { Pin::>::borrow(ptr.cast()) } } - - fn match_type_id(&self) -> Result { - // SAFETY: By the type invariants, `self.as_raw()` is a valid pointer to a `struct device`. - let private = unsafe { (*self.as_raw()).p }; - - // SAFETY: For a bound device, `private` is guaranteed to be a valid pointer to a - // `struct device_private`. - let driver_type = unsafe { &raw mut (*private).driver_type }; - - // SAFETY: - // - `driver_type` is valid for (unaligned) reads of a `TypeId`. - // - A bound device guarantees that `driver_type` contains a valid `TypeId` value. - let type_id = unsafe { driver_type.cast::().read_unaligned() }; - - if type_id != TypeId::of::() { - return Err(EINVAL); - } - - Ok(()) - } - - /// Access a driver's private data. - /// - /// Returns a pinned reference to the driver's private data or [`EINVAL`] if it doesn't match - /// the asserted type `T`. - pub fn drvdata(&self) -> Result> { - // SAFETY: By the type invariants, `self.as_raw()` is a valid pointer to a `struct device`. - if unsafe { bindings::dev_get_drvdata(self.as_raw()) }.is_null() { - return Err(ENOENT); - } - - self.match_type_id::()?; - - // SAFETY: - // - The above check of `dev_get_drvdata()` guarantees that we are called after - // `set_drvdata()`. - // - We've just checked that the type of the driver's private data is in fact `T`. - Ok(unsafe { self.drvdata_unchecked() }) - } } impl Device { -- cgit v1.2.3 From 7eba000621fff223dd7bab484d48918c7c77a307 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 11 May 2026 09:49:26 +0200 Subject: device property: initialize the remaining fields of fwnode_handle in fwnode_init() If a firmware node is allocated on the stack (for instance: temporary software node whose life-time we control) or on the heap - but using a non-zeroing allocation function - and initialized using fwnode_init(), its secondary pointer will contain uninitialized memory which likely will be neither NULL nor IS_ERR() and so may end up being dereferenced (for example: in dev_to_swnode()). Set fwnode->secondary to NULL on initialization. While at it: initialize the remaining fields of struct fwnode_handle too just to be sure. Cc: stable@vger.kernel.org Fixes: 01bb86b380a3 ("driver core: Add fwnode_init()") Reviewed-by: Sakari Ailus Reviewed-by: Rafael J. Wysocki (Intel) Reviewed-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski Link: https://patch.msgid.link/20260511074927.9473-1-bartosz.golaszewski@oss.qualcomm.com [ Fix typo in commit message. - Danilo ] Signed-off-by: Danilo Krummrich --- include/linux/fwnode.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/linux/fwnode.h b/include/linux/fwnode.h index 80b38fbf2121..c30a9baafc0d 100644 --- a/include/linux/fwnode.h +++ b/include/linux/fwnode.h @@ -208,9 +208,12 @@ struct fwnode_operations { static inline void fwnode_init(struct fwnode_handle *fwnode, const struct fwnode_operations *ops) { + fwnode->secondary = NULL; fwnode->ops = ops; + fwnode->dev = NULL; INIT_LIST_HEAD(&fwnode->consumers); INIT_LIST_HEAD(&fwnode->suppliers); + fwnode->flags = 0; } static inline void fwnode_set_flag(struct fwnode_handle *fwnode, -- cgit v1.2.3 From 454257f6d124a92342dcbb7710c03dd6ef96c731 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 20 May 2026 15:07:01 +0200 Subject: sysfs: clamp show() return value in sysfs_kf_read() sysfs_kf_seq_show() defends against buggy show() callbacks that return larger than PAGE_SIZE by clamping the value and printing a warning. sysfs_kf_read(), the prealloc variant, has no such defense. The only current in-tree user of __ATTR_PREALLOC is drivers/md/md.c, whose show() callbacks are well-behaved, so this is hardening against future drivers doing foolish things and out-of-tree code doing even more foolish things. Cc: NeilBrown Cc: Tejun Heo Fixes: 2b75869bba67 ("sysfs/kernfs: allow attributes to request write buffer be pre-allocated.") Assisted-by: gregkh_clanker_t1000 Reviewed-by: Rafael J. Wysocki (Intel) Reviewed-by: Danilo Krummrich Link: https://patch.msgid.link/2026052000-drove-unicycle-d61b@gregkh Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/file.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index 6b3a8f62fa89..cd5bb0f9fee6 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -119,6 +119,10 @@ static ssize_t sysfs_kf_read(struct kernfs_open_file *of, char *buf, len = ops->show(kobj, of->kn->priv, buf); if (len < 0) return len; + if (len >= (ssize_t)PAGE_SIZE) { + printk("fill_read_buffer: %pS returned bad count\n", ops->show); + len = PAGE_SIZE - 1; + } if (pos) { if (len <= pos) return 0; -- cgit v1.2.3 From 0fdde3f2aeadcf8d090ee3edee0aad73fb91f690 Mon Sep 17 00:00:00 2001 From: Conor Kotwasinski Date: Thu, 16 Apr 2026 09:43:15 -0400 Subject: kernfs: fix suspicious RCU usage in kernfs_put() Commit 741c10b096bc ("kernfs: Use RCU to access kernfs_node::name.") converted the WARN_ONCE() in kernfs_put() to read kn->name and parent->name via rcu_dereference(), but kernfs_put() has callers that hold neither kernfs_rwsem nor the RCU read lock. The inode eviction path driven by memory reclaim is one such case: kernfs_put+0x53/0x60 fs/kernfs/dir.c:602 evict+0x3c2/0xad0 fs/inode.c:846 iput_final fs/inode.c:1966 [inline] iput.part.0+0x605/0xf50 fs/inode.c:2015 iput+0x35/0x40 fs/inode.c:1981 dentry_unlink_inode+0x2a1/0x490 fs/dcache.c:467 __dentry_kill+0x1d0/0x600 fs/dcache.c:670 shrink_dentry_list+0x180/0x5e0 fs/dcache.c:1174 prune_dcache_sb+0xea/0x150 fs/dcache.c:1256 super_cache_scan+0x328/0x550 fs/super.c:223 ... kswapd+0x556/0xba0 mm/vmscan.c:7343 lockdep complains with "suspicious RCU usage" whenever the WARN fires from such a context. Wrap the rcu_dereference() calls in an RCU read-side critical section. Gate on the active-ref check so the lock is only taken when the WARN is about to fire. Note that this does not address the underlying imbalance in kn->active that triggers the WARN. Fixes: 741c10b096bc ("kernfs: Use RCU to access kernfs_node::name.") Reported-by: syzbot+0dfe499ea713e0a15bec@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=0dfe499ea713e0a15bec Signed-off-by: Conor Kotwasinski Acked-by: Tejun Heo Link: https://patch.msgid.link/20260416134315.1474726-1-conorkotwasinski2024@u.northwestern.edu Signed-off-by: Greg Kroah-Hartman --- fs/kernfs/dir.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index 4f9ade82b08a..e88b71607f1e 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -597,10 +597,13 @@ void kernfs_put(struct kernfs_node *kn) */ parent = kernfs_parent(kn); - WARN_ONCE(atomic_read(&kn->active) != KN_DEACTIVATED_BIAS, - "kernfs_put: %s/%s: released with incorrect active_ref %d\n", - parent ? rcu_dereference(parent->name) : "", - rcu_dereference(kn->name), atomic_read(&kn->active)); + if (atomic_read(&kn->active) != KN_DEACTIVATED_BIAS) { + guard(rcu)(); + WARN_ONCE(1, + "kernfs_put: %s/%s: released with incorrect active_ref %d\n", + parent ? rcu_dereference(parent->name) : "", + rcu_dereference(kn->name), atomic_read(&kn->active)); + } if (kernfs_type(kn) == KERNFS_LINK) kernfs_put(kn->symlink.target_kn); -- cgit v1.2.3 From b2c885b362719cb114c6b2d46a295116abeb4848 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Thu, 2 Apr 2026 23:17:13 +0200 Subject: devcoredump: Remove exit call Kconfig symbol DEV_COREDUMP is of type bool, therefore devcoredump can't be built as a module and the exit code is a no-op. Signed-off-by: Heiner Kallweit Link: https://patch.msgid.link/39a3821b-03d6-4ff0-97b7-82411a76d39a@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/devcoredump.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/base/devcoredump.c b/drivers/base/devcoredump.c index 7e4a491bf15e..8bb1763083dd 100644 --- a/drivers/base/devcoredump.c +++ b/drivers/base/devcoredump.c @@ -471,10 +471,3 @@ static int __init devcoredump_init(void) return class_register(&devcd_class); } __initcall(devcoredump_init); - -static void __exit devcoredump_exit(void) -{ - class_for_each_device(&devcd_class, NULL, NULL, devcd_free); - class_unregister(&devcd_class); -} -__exitcall(devcoredump_exit); -- cgit v1.2.3 From bea3649af1293144172a20e0b33a319e5221db28 Mon Sep 17 00:00:00 2001 From: Jori Koolstra Date: Tue, 3 Mar 2026 17:25:04 +0100 Subject: devfreq: change devfreq_event_class to a const struct The class_create() call has been deprecated in favor of class_register() as the driver core now allows for a struct class to be in read-only memory. Change devfreq_event_class to be a const struct class and drop the class_create() call. Compile tested. Link: https://lore.kernel.org/all/2023040244-duffel-pushpin-f738@gregkh/ Suggested-by: Greg Kroah-Hartman Signed-off-by: Jori Koolstra Link: https://patch.msgid.link/20260303162505.3748001-1-jkoolstra@xs4all.nl Signed-off-by: Greg Kroah-Hartman --- drivers/devfreq/devfreq-event.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/drivers/devfreq/devfreq-event.c b/drivers/devfreq/devfreq-event.c index 179de3cf6d6c..9e183cd8818c 100644 --- a/drivers/devfreq/devfreq-event.c +++ b/drivers/devfreq/devfreq-event.c @@ -17,7 +17,13 @@ #include #include -static struct class *devfreq_event_class; +static struct attribute *devfreq_event_attrs[]; +ATTRIBUTE_GROUPS(devfreq_event); + +static const struct class devfreq_event_class = { + .name = "devfreq-event", + .dev_groups = devfreq_event_groups +}; /* The list of all devfreq event list */ static LIST_HEAD(devfreq_event_list); @@ -321,7 +327,7 @@ struct devfreq_event_dev *devfreq_event_add_edev(struct device *dev, edev->desc = desc; edev->enable_count = 0; edev->dev.parent = dev; - edev->dev.class = devfreq_event_class; + edev->dev.class = &devfreq_event_class; edev->dev.release = devfreq_event_release_edev; dev_set_name(&edev->dev, "event%d", atomic_inc_return(&event_no)); @@ -461,18 +467,15 @@ static struct attribute *devfreq_event_attrs[] = { &dev_attr_enable_count.attr, NULL, }; -ATTRIBUTE_GROUPS(devfreq_event); static int __init devfreq_event_init(void) { - devfreq_event_class = class_create("devfreq-event"); - if (IS_ERR(devfreq_event_class)) { - pr_err("%s: couldn't create class\n", __FILE__); - return PTR_ERR(devfreq_event_class); - } + int err; - devfreq_event_class->dev_groups = devfreq_event_groups; + err = class_register(&devfreq_event_class); + if (err) + pr_err("%s: couldn't create class\n", __FILE__); - return 0; + return err; } subsys_initcall(devfreq_event_init); -- cgit v1.2.3 From b382754793b1af27899164a5b412ff2f49eb24d5 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Sun, 17 May 2026 19:17:47 +0300 Subject: driver core: delete useless forward declaration of "struct class" "struct class" is defined earlier on both cases. Signed-off-by: Alexey Dobriyan Link: https://patch.msgid.link/6d5937c5-9d41-4cfe-9e42-0946e12dc72d@p183 Signed-off-by: Greg Kroah-Hartman --- arch/mips/include/asm/mips_mt.h | 1 - include/linux/device.h | 1 - 2 files changed, 2 deletions(-) diff --git a/arch/mips/include/asm/mips_mt.h b/arch/mips/include/asm/mips_mt.h index 6ea02af29876..0358489e7d13 100644 --- a/arch/mips/include/asm/mips_mt.h +++ b/arch/mips/include/asm/mips_mt.h @@ -23,7 +23,6 @@ extern void mips_mt_set_cpuoptions(void); static inline void mips_mt_set_cpuoptions(void) { } #endif -struct class; extern const struct class mt_class; #endif /* __ASM_MIPS_MT_H */ diff --git a/include/linux/device.h b/include/linux/device.h index d54c86d77764..ac05c6e0af1b 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -38,7 +38,6 @@ struct device_private; struct device_driver; struct driver_private; struct module; -struct class; struct subsys_private; struct device_node; struct fwnode_handle; -- cgit v1.2.3 From 9ecab063e9821b23c82907d7889302c22f197e1e Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Mon, 16 Mar 2026 23:09:37 +0100 Subject: driver core: constify group arrays arguments in driver_add_groups and driver_remove_groups Constify the groups array argument in driver_add_groups and driver_remove_groups. This allows to pass constant arrays as arguments. Signed-off-by: Heiner Kallweit Link: https://patch.msgid.link/21a1e5f1-c6a0-4f6f-aa86-1e6abd25f9c6@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/base.h | 6 ++++-- drivers/base/driver.c | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/base/base.h b/drivers/base/base.h index 483b99b4fa3d..a5b7abc10ff0 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -194,8 +194,10 @@ static inline void dev_sync_state(struct device *dev) dev->driver->sync_state(dev); } -int driver_add_groups(const struct device_driver *drv, const struct attribute_group **groups); -void driver_remove_groups(const struct device_driver *drv, const struct attribute_group **groups); +int driver_add_groups(const struct device_driver *drv, + const struct attribute_group *const *groups); +void driver_remove_groups(const struct device_driver *drv, + const struct attribute_group *const *groups); void device_driver_detach(struct device *dev); static inline void device_set_driver(struct device *dev, const struct device_driver *drv) diff --git a/drivers/base/driver.c b/drivers/base/driver.c index 8ab010ddf709..c5ebf1fdad75 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -203,13 +203,13 @@ void driver_remove_file(const struct device_driver *drv, EXPORT_SYMBOL_GPL(driver_remove_file); int driver_add_groups(const struct device_driver *drv, - const struct attribute_group **groups) + const struct attribute_group *const *groups) { return sysfs_create_groups(&drv->p->kobj, groups); } void driver_remove_groups(const struct device_driver *drv, - const struct attribute_group **groups) + const struct attribute_group *const *groups) { sysfs_remove_groups(&drv->p->kobj, groups); } -- cgit v1.2.3 From f1462b97684b5d0cef0a4d026c7c9c9c42cd192c Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Mon, 16 Mar 2026 23:10:31 +0100 Subject: driver core: make struct bus_type groups members constant arrays Constify the groups arrays, allowing to assign constant arrays. Signed-off-by: Heiner Kallweit Link: https://patch.msgid.link/265f6584-8edd-48a0-9568-a9d584b9ec3a@gmail.com Signed-off-by: Greg Kroah-Hartman --- include/linux/device/bus.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/linux/device/bus.h b/include/linux/device/bus.h index c1b463cd6464..a38f7229b8f4 100644 --- a/include/linux/device/bus.h +++ b/include/linux/device/bus.h @@ -83,9 +83,9 @@ struct fwnode_handle; struct bus_type { const char *name; const char *dev_name; - const struct attribute_group **bus_groups; - const struct attribute_group **dev_groups; - const struct attribute_group **drv_groups; + const struct attribute_group *const *bus_groups; + const struct attribute_group *const *dev_groups; + const struct attribute_group *const *drv_groups; int (*match)(struct device *dev, const struct device_driver *drv); int (*uevent)(const struct device *dev, struct kobj_uevent_env *env); -- cgit v1.2.3 From a9c12b783cc711de3ac7f188bed07d529bb818af Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Mon, 16 Mar 2026 23:11:12 +0100 Subject: device core: make struct device_driver groups members constant arrays Constify the groups arrays, allowing to assign constant arrays. Signed-off-by: Heiner Kallweit Link: https://patch.msgid.link/42624513-923c-4970-834d-036282e24e24@gmail.com Signed-off-by: Greg Kroah-Hartman --- include/linux/device/driver.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/device/driver.h b/include/linux/device/driver.h index bbc67ec513ed..c882daaef012 100644 --- a/include/linux/device/driver.h +++ b/include/linux/device/driver.h @@ -114,8 +114,8 @@ struct device_driver { void (*shutdown) (struct device *dev); int (*suspend) (struct device *dev, pm_message_t state); int (*resume) (struct device *dev); - const struct attribute_group **groups; - const struct attribute_group **dev_groups; + const struct attribute_group *const *groups; + const struct attribute_group *const *dev_groups; const struct dev_pm_ops *pm; void (*coredump) (struct device *dev); -- cgit v1.2.3 From 9582485a65eacfd7245ec7f0a9d7e2c34749d669 Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Thu, 14 May 2026 22:14:55 +0500 Subject: device property: fix fwnode reference leak in fwnode_graph_get_endpoint_by_id() When called with FWNODE_GRAPH_ENDPOINT_NEXT, the function walks every endpoint under the requested port and, for any endpoint whose ID is greater than or equal to the requested one, may store a fwnode reference in best_ep via fwnode_handle_get(). If a later iteration finds an exact-ID match, the function returns that endpoint directly without dropping the reference held by best_ep, leaking it. Drop the saved candidate before returning the exact-match endpoint. This affects callers that use FWNODE_GRAPH_ENDPOINT_NEXT to ask for the next endpoint with ID >= the requested one (used by a number of media drivers, e.g. imx7/8, sun6i CSI, omap3isp, xilinx-csi2, stm32-csi). Each leak retains a fwnode reference until reboot/unbind. Fixes: 0fcc2bdc8aff ("device property: Add fwnode_graph_get_endpoint_by_id()") Signed-off-by: Stepan Ionichev Link: https://patch.msgid.link/20260514171455.27271-1-sozdayvek@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/property.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/base/property.c b/drivers/base/property.c index 8e0148a37fff..e08eadd66f4f 100644 --- a/drivers/base/property.c +++ b/drivers/base/property.c @@ -1277,8 +1277,10 @@ fwnode_graph_get_endpoint_by_id(const struct fwnode_handle *fwnode, if (fwnode_ep.port != port) continue; - if (fwnode_ep.id == endpoint) + if (fwnode_ep.id == endpoint) { + fwnode_handle_put(best_ep); return ep; + } if (!endpoint_next) continue; -- cgit v1.2.3 From 1137838865bfc9a7cd5869c1dc5c22aa45ec12c8 Mon Sep 17 00:00:00 2001 From: Zhang Yuwei Date: Fri, 10 Apr 2026 10:44:48 +0800 Subject: driver core: Use mod_delayed_work to prevent lost deferred probe work The deferred_probe_timeout_work may be permanently and unexpectedly canceled when deferred_probe_extend_timeout() executes concurrently. Starting with deferred_probe_timeout_work pending, the problem can occur after the following sequence: CPU0 CPU1 deferred_probe_extend_timeout -> cancel_delayed_work() => true deferred_probe_extend_timeout -> cancel_delayed_work() -> __cancel_work() -> try_grab_pending() -> schedule_delayed_work() -> queue_delayed_work_on() (Since the pending bit is grabbed, it just returns without queuing) -> set_work_pool_and_clear_pending() (This __cancel_work() returns false and the work will never be queued again) The root cause is that the WORK_STRUCT_PENDING_BIT of the work_struct is set temporarily in __cancel_work() (via try_grab_pending()). This transient state prevents the work_struct from being successfully queued by another CPU. To fix this, replace the original non-atomic cancel and schedule mechanism with mod_delayed_work(). This ensures the modification is handled atomically and guarantees that the work is not lost. Fixes: 2b28a1a84a0e ("driver core: Extend deferred probe timeout on driver registration") Signed-off-by: Zhang Yuwei Link: https://patch.msgid.link/20260410024448.387231-1-zhangyuwei20@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/dd.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 5799a60fd058..172a02a438a2 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -323,12 +323,10 @@ void deferred_probe_extend_timeout(void) * If the work hasn't been queued yet or if the work expired, don't * start a new one. */ - if (cancel_delayed_work(&deferred_probe_timeout_work)) { - schedule_delayed_work(&deferred_probe_timeout_work, - driver_deferred_probe_timeout * HZ); + if (mod_delayed_work(system_wq, &deferred_probe_timeout_work, + driver_deferred_probe_timeout)) pr_debug("Extended deferred probe timeout by %d secs\n", driver_deferred_probe_timeout); - } } /** -- cgit v1.2.3 From aaf08c52df9a19148731d4a3cfd85d98455db901 Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Mon, 11 May 2026 17:57:48 +0200 Subject: Revert "treewide: Fix probing of devices in DT overlays" This reverts commit 1a50d9403fb90cbe4dea0ec9fd0351d2ecbd8924. While the commit fixed fw_devlink overlay handling for one case, it broke it for another case. So revert it and redo the fix in a separate patch. Fixes: 1a50d9403fb9 ("treewide: Fix probing of devices in DT overlays") Reported-by: Herve Codina Closes: https://lore.kernel.org/lkml/CAMuHMdXEnSD4rRJ-o90x4OprUacN_rJgyo8x6=9F9rZ+-KzjOg@mail.gmail.com/ Closes: https://lore.kernel.org/all/20240221095137.616d2aaa@bootlin.com/ Closes: https://lore.kernel.org/lkml/20240312151835.29ef62a0@bootlin.com/ Signed-off-by: Saravana Kannan Link: https://lore.kernel.org/lkml/20240411235623.1260061-2-saravanak@google.com/ [Herve: Fix conflicts due to f72e77c33e4b ("device property: Make modifications of fwnode "flags" thread safe")] Signed-off-by: Herve Codina Acked-by: Mark Brown Acked-by: Rob Herring (Arm) Acked-by: Wolfram Sang # for I2C Link: https://patch.msgid.link/20260511155755.34428-2-herve.codina@bootlin.com Signed-off-by: Greg Kroah-Hartman --- drivers/bus/imx-weim.c | 6 ------ drivers/i2c/i2c-core-of.c | 5 ----- drivers/of/dynamic.c | 1 - drivers/of/platform.c | 5 ----- drivers/spi/spi.c | 5 ----- 5 files changed, 22 deletions(-) diff --git a/drivers/bus/imx-weim.c b/drivers/bus/imx-weim.c index f735e0462c55..87070155b057 100644 --- a/drivers/bus/imx-weim.c +++ b/drivers/bus/imx-weim.c @@ -327,12 +327,6 @@ static int of_weim_notify(struct notifier_block *nb, unsigned long action, "Failed to setup timing for '%pOF'\n", rd->dn); if (!of_node_check_flag(rd->dn, OF_POPULATED)) { - /* - * Clear the flag before adding the device so that - * fw_devlink doesn't skip adding consumers to this - * device. - */ - fwnode_clear_flag(&rd->dn->fwnode, FWNODE_FLAG_NOT_DEVICE); if (!of_platform_device_create(rd->dn, NULL, &pdev->dev)) { dev_err(&pdev->dev, "Failed to create child device '%pOF'\n", diff --git a/drivers/i2c/i2c-core-of.c b/drivers/i2c/i2c-core-of.c index 354a88d0599e..30b48a428c0b 100644 --- a/drivers/i2c/i2c-core-of.c +++ b/drivers/i2c/i2c-core-of.c @@ -176,11 +176,6 @@ static int of_i2c_notify(struct notifier_block *nb, unsigned long action, return NOTIFY_OK; } - /* - * Clear the flag before adding the device so that fw_devlink - * doesn't skip adding consumers to this device. - */ - fwnode_clear_flag(&rd->dn->fwnode, FWNODE_FLAG_NOT_DEVICE); client = of_i2c_register_device(adap, rd->dn); if (IS_ERR(client)) { dev_err(&adap->dev, "failed to create client for '%pOF'\n", diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c index ade288372101..aa450425ec1e 100644 --- a/drivers/of/dynamic.c +++ b/drivers/of/dynamic.c @@ -225,7 +225,6 @@ static void __of_attach_node(struct device_node *np) np->sibling = np->parent->child; np->parent->child = np; of_node_clear_flag(np, OF_DETACHED); - fwnode_set_flag(&np->fwnode, FWNODE_FLAG_NOT_DEVICE); raw_spin_unlock_irqrestore(&devtree_lock, flags); diff --git a/drivers/of/platform.c b/drivers/of/platform.c index a42224f9d1a8..53bca8c6f781 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -744,11 +744,6 @@ static int of_platform_notify(struct notifier_block *nb, if (of_node_check_flag(rd->dn, OF_POPULATED)) return NOTIFY_OK; - /* - * Clear the flag before adding the device so that fw_devlink - * doesn't skip adding consumers to this device. - */ - fwnode_clear_flag(&rd->dn->fwnode, FWNODE_FLAG_NOT_DEVICE); /* pdev_parent may be NULL when no bus platform device */ pdev_parent = of_find_device_by_node(parent); pdev = of_platform_device_create(rd->dn, NULL, diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 104279858f56..889e1eecc757 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -5003,11 +5003,6 @@ static int of_spi_notify(struct notifier_block *nb, unsigned long action, return NOTIFY_OK; } - /* - * Clear the flag before adding the device so that fw_devlink - * doesn't skip adding consumers to this device. - */ - fwnode_clear_flag(&rd->dn->fwnode, FWNODE_FLAG_NOT_DEVICE); spi = of_register_spi_device(ctlr, rd->dn); put_device(&ctlr->dev); -- cgit v1.2.3 From 81e7c6befa36cecdcbf7244393bd67e8f8c59bf5 Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Mon, 11 May 2026 17:57:49 +0200 Subject: of: dynamic: Fix overlayed devices not probing because of fw_devlink When an overlay is applied, if the target device has already probed successfully and bound to a device, then some of the fw_devlink logic that ran when the device was probed needs to be rerun. This allows newly created dangling consumers of the overlayed device tree nodes to be moved to become consumers of the target device. [Herve: Add the call to driver_deferred_probe_trigger()] [Herve: Use fwnode_test_flag() to test fwnode flags value] Fixes: 1a50d9403fb9 ("treewide: Fix probing of devices in DT overlays") Reported-by: Herve Codina Closes: https://lore.kernel.org/lkml/CAMuHMdXEnSD4rRJ-o90x4OprUacN_rJgyo8x6=9F9rZ+-KzjOg@mail.gmail.com/ Closes: https://lore.kernel.org/all/20240221095137.616d2aaa@bootlin.com/ Closes: https://lore.kernel.org/lkml/20240312151835.29ef62a0@bootlin.com/ Signed-off-by: Saravana Kannan Link: https://lore.kernel.org/lkml/20240411235623.1260061-3-saravanak@google.com/ [Herve: Rebase on top of recent kernel] Signed-off-by: Herve Codina Tested-by: Kalle Niemi Tested-by: Geert Uytterhoeven Acked-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260511155755.34428-3-herve.codina@bootlin.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 83 ++++++++++++++++++++++++++++++++++++++++++++------ drivers/of/overlay.c | 15 +++++++++ include/linux/fwnode.h | 1 + 3 files changed, 90 insertions(+), 9 deletions(-) diff --git a/drivers/base/core.c b/drivers/base/core.c index d49420e066de..3cb736c7fb31 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -235,6 +235,79 @@ static void __fw_devlink_pickup_dangling_consumers(struct fwnode_handle *fwnode, __fw_devlink_pickup_dangling_consumers(child, new_sup); } +static void fw_devlink_pickup_dangling_consumers(struct device *dev) +{ + struct fwnode_handle *child; + + guard(mutex)(&fwnode_link_lock); + + fwnode_for_each_available_child_node(dev->fwnode, child) + __fw_devlink_pickup_dangling_consumers(child, dev->fwnode); + __fw_devlink_link_to_consumers(dev); +} + +/** + * fw_devlink_refresh_fwnode - Recheck the tree under this firmware node + * @fwnode: The fwnode under which the fwnode tree has changed + * + * This function is mainly meant to adjust the supplier/consumer dependencies + * after a fwnode tree overlay has occurred. + */ +void fw_devlink_refresh_fwnode(struct fwnode_handle *fwnode) +{ + struct device *dev; + + /* + * Find the closest ancestor fwnode that has been converted to a device + * that can bind to a driver (bus device). + */ + fwnode_handle_get(fwnode); + do { + if (fwnode_test_flag(fwnode, FWNODE_FLAG_NOT_DEVICE)) + continue; + + dev = get_dev_from_fwnode(fwnode); + if (!dev) + continue; + + if (dev->bus) + break; + + put_device(dev); + } while ((fwnode = fwnode_get_next_parent(fwnode))); + + /* + * If none of the ancestor fwnodes have (yet) been converted to a device + * that can bind to a driver, there's nothing to fix up. + */ + if (!fwnode) + return; + + WARN(device_is_bound(dev) && dev->links.status != DL_DEV_DRIVER_BOUND, + "Don't multithread overlaying and probing the same device!\n"); + + /* + * If the device has already bound to a driver, then we need to redo + * some of the work that was done after the device was bound to a + * driver. If the device hasn't bound to a driver, running things too + * soon would incorrectly pick up consumers that it shouldn't. + */ + if (dev->links.status == DL_DEV_DRIVER_BOUND) { + fw_devlink_pickup_dangling_consumers(dev); + /* + * Some of dangling consumers could have been put previously in + * the deferred probe list due to the unavailability of their + * suppliers. Those consumers have been picked up and some of + * their suppliers links have been updated. Time to re-try their + * probe sequence. + */ + driver_deferred_probe_trigger(); + } + + put_device(dev); + fwnode_handle_put(fwnode); +} + static DEFINE_MUTEX(device_links_lock); DEFINE_STATIC_SRCU(device_links_srcu); @@ -1312,16 +1385,8 @@ void device_links_driver_bound(struct device *dev) * child firmware node. */ if (dev->fwnode && dev->fwnode->dev == dev) { - struct fwnode_handle *child; - fwnode_links_purge_suppliers(dev->fwnode); - - guard(mutex)(&fwnode_link_lock); - - fwnode_for_each_available_child_node(dev->fwnode, child) - __fw_devlink_pickup_dangling_consumers(child, - dev->fwnode); - __fw_devlink_link_to_consumers(dev); + fw_devlink_pickup_dangling_consumers(dev); } device_remove_file(dev, &dev_attr_waiting_for_supplier); diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c index c1c5686fc7b1..4e45f3414c2c 100644 --- a/drivers/of/overlay.c +++ b/drivers/of/overlay.c @@ -185,6 +185,15 @@ static int overlay_notify(struct overlay_changeset *ovcs, return 0; } +static void overlay_fw_devlink_refresh(struct overlay_changeset *ovcs) +{ + for (int i = 0; i < ovcs->count; i++) { + struct device_node *np = ovcs->fragments[i].target; + + fw_devlink_refresh_fwnode(of_fwnode_handle(np)); + } +} + /* * The values of properties in the "/__symbols__" node are paths in * the ovcs->overlay_root. When duplicating the properties, the paths @@ -951,6 +960,12 @@ static int of_overlay_apply(struct overlay_changeset *ovcs, pr_err("overlay apply changeset entry notify error %d\n", ret); /* notify failure is not fatal, continue */ + /* + * Needs to happen after changeset notify to give the listeners a chance + * to finish creating all the devices they need to create. + */ + overlay_fw_devlink_refresh(ovcs); + ret_tmp = overlay_notify(ovcs, OF_OVERLAY_POST_APPLY); if (ret_tmp) if (!ret) diff --git a/include/linux/fwnode.h b/include/linux/fwnode.h index c30a9baafc0d..4e86e6990d28 100644 --- a/include/linux/fwnode.h +++ b/include/linux/fwnode.h @@ -253,6 +253,7 @@ int fwnode_link_add(struct fwnode_handle *con, struct fwnode_handle *sup, u8 flags); void fwnode_links_purge(struct fwnode_handle *fwnode); void fw_devlink_purge_absent_suppliers(struct fwnode_handle *fwnode); +void fw_devlink_refresh_fwnode(struct fwnode_handle *fwnode); bool fw_devlink_is_strict(void); #endif -- cgit v1.2.3 From 36d74f17e03f7e60e1b08fbe16cfad6e69cc3aa9 Mon Sep 17 00:00:00 2001 From: Herve Codina Date: Mon, 11 May 2026 17:57:50 +0200 Subject: driver core: Avoid warning when removing a device while its supplier is unbinding During driver removal, the following warning can appear: WARNING: CPU: 1 PID: 139 at drivers/base/core.c:1497 __device_links_no_driver+0xcc/0xfc ... Call trace: __device_links_no_driver+0xcc/0xfc (P) device_links_driver_cleanup+0xa8/0xf0 device_release_driver_internal+0x208/0x23c device_links_unbind_consumers+0xe0/0x108 device_release_driver_internal+0xec/0x23c device_links_unbind_consumers+0xe0/0x108 device_release_driver_internal+0xec/0x23c device_links_unbind_consumers+0xe0/0x108 device_release_driver_internal+0xec/0x23c driver_detach+0xa0/0x12c bus_remove_driver+0x6c/0xbc driver_unregister+0x30/0x60 pci_unregister_driver+0x20/0x9c lan966x_pci_driver_exit+0x18/0xa90 [lan966x_pci] This warning is triggered when a consumer is removed because the links status of its supplier is not DL_DEV_DRIVER_BOUND and the link flag DL_FLAG_SYNC_STATE_ONLY is not set. The topology in terms of consumers/suppliers used was the following (consumer ---> supplier): i2c -----------> OIC ----> PCI device | ^ | | +---> pinctrl ---+ When the PCI device is removed, the OIC (interrupt controller) has to be removed. In order to remove the OIC, pinctrl and i2c need to be removed and to remove pinctrl, i2c need to be removed. The removal order is: 1) i2c 2) pinctrl 3) OIC 4) PCI device In details, the removal sequence is the following (with 0000:01:00.0 the PCI device): driver_detach: call device_release_driver_internal(0000:01:00.0)... device_links_busy(0000:01:00.0): links->status = DL_DEV_UNBINDING device_links_unbind_consumers(0000:01:00.0): 0000:01:00.0--oic link->status = DL_STATE_SUPPLIER_UNBIND call device_release_driver_internal(oic)... device_links_busy(oic): links->status = DL_DEV_UNBINDING device_links_unbind_consumers(oic): oic--pinctrl link->status = DL_STATE_SUPPLIER_UNBIND call device_release_driver_internal(pinctrl)... device_links_busy(pinctrl): links->status = DL_DEV_UNBINDING device_links_unbind_consumers(pinctrl): pinctrl--i2c link->status = DL_STATE_SUPPLIER_UNBIND call device_release_driver_internal(i2c)... device_links_busy(i2c): links->status = DL_DEV_UNBINDING __device_links_no_driver(i2c)... pinctrl--i2c link->status is DL_STATE_SUPPLIER_UNBIND oic--i2c link->status is DL_STATE_ACTIVE oic--i2c link->supplier->links.status is DL_DEV_UNBINDING The warning is triggered by the i2c removal because the OIC (supplier) links status is not DL_DEV_DRIVER_BOUND. Its links status is indeed set to DL_DEV_UNBINDING. It is perfectly legit to have the links status set to DL_DEV_UNBINDING in that case. Indeed we had started to unbind the OIC which triggered the consumer unbinding and didn't finish yet when the i2c is unbound. Avoid the warning when the supplier links status is set to DL_DEV_UNBINDING and thus support this removal sequence without any warnings. Signed-off-by: Herve Codina Reviewed-by: Rafael J. Wysocki Reviewed-by: Saravana Kannan Link: https://patch.msgid.link/20260511155755.34428-4-herve.codina@bootlin.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/base/core.c b/drivers/base/core.c index 3cb736c7fb31..cc0d6984f1d4 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -1500,7 +1500,8 @@ static void __device_links_no_driver(struct device *dev) if (link->supplier->links.status == DL_DEV_DRIVER_BOUND) { WRITE_ONCE(link->status, DL_STATE_AVAILABLE); } else { - WARN_ON(!device_link_test(link, DL_FLAG_SYNC_STATE_ONLY)); + WARN_ON(link->supplier->links.status != DL_DEV_UNBINDING && + !device_link_test(link, DL_FLAG_SYNC_STATE_ONLY)); WRITE_ONCE(link->status, DL_STATE_DORMANT); } } -- cgit v1.2.3 From cdd4c98d5a0e320b18fcdbd37a30a732aec624da Mon Sep 17 00:00:00 2001 From: Thomas Weißschuh Date: Tue, 12 May 2026 18:39:11 +0200 Subject: driver core: Delete DEVICE_ATTR_PREALLOC() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This macro is unused and would create extra work during the upcoming constification of device attributes. Remove it. Signed-off-by: Thomas Weißschuh Link: https://patch.msgid.link/20260512-sysfs-const-attr-device_attr-prep-v3-1-cb7c17b34d52@weissschuh.net Signed-off-by: Greg Kroah-Hartman --- include/linux/device.h | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/include/linux/device.h b/include/linux/device.h index ac05c6e0af1b..5daed9c92979 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -156,19 +156,6 @@ ssize_t device_show_string(struct device *dev, struct device_attribute *attr, #define DEVICE_ATTR(_name, _mode, _show, _store) \ struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store) -/** - * DEVICE_ATTR_PREALLOC - Define a preallocated device attribute. - * @_name: Attribute name. - * @_mode: File mode. - * @_show: Show handler. Optional, but mandatory if attribute is readable. - * @_store: Store handler. Optional, but mandatory if attribute is writable. - * - * Like DEVICE_ATTR(), but ``SYSFS_PREALLOC`` is set on @_mode. - */ -#define DEVICE_ATTR_PREALLOC(_name, _mode, _show, _store) \ - struct device_attribute dev_attr_##_name = \ - __ATTR_PREALLOC(_name, _mode, _show, _store) - /** * DEVICE_ATTR_RW - Define a read-write device attribute. * @_name: Attribute name. -- cgit v1.2.3 From 56e733f78c223032368e500af58402840a56e243 Mon Sep 17 00:00:00 2001 From: Thomas Weißschuh Date: Tue, 12 May 2026 18:39:12 +0200 Subject: driver core: Add low-level macros for device attributes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For the upcoming constification of device attributes the generic __ATTR() macros are insufficient. Prepare for a split by introducing new low-level macros specific to device attributes. Signed-off-by: Thomas Weißschuh Link: https://patch.msgid.link/20260512-sysfs-const-attr-device_attr-prep-v3-2-cb7c17b34d52@weissschuh.net Signed-off-by: Greg Kroah-Hartman --- include/linux/device.h | 43 ++++++++++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/include/linux/device.h b/include/linux/device.h index 5daed9c92979..7d6796532313 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -134,6 +134,27 @@ ssize_t device_store_bool(struct device *dev, struct device_attribute *attr, ssize_t device_show_string(struct device *dev, struct device_attribute *attr, char *buf); +#define __DEVICE_ATTR(_name, _mode, _show, _store) \ + __ATTR(_name, _mode, _show, _store) + +#define __DEVICE_ATTR_RO_MODE(_name, _mode) \ + __ATTR_RO_MODE(_name, _mode) + +#define __DEVICE_ATTR_RO(_name) \ + __ATTR_RO(_name) + +#define __DEVICE_ATTR_WO(_name) \ + __ATTR_WO(_name) + +#define __DEVICE_ATTR_RW_MODE(_name, _mode) \ + __ATTR_RW_MODE(_name, _mode) + +#define __DEVICE_ATTR_RW(_name) \ + __ATTR_RW(_name) + +#define __DEVICE_ATTR_IGNORE_LOCKDEP(_name, _mode, _show, _store) \ + __ATTR_IGNORE_LOCKDEP(_name, _mode, _show, _store) + /** * DEVICE_ATTR - Define a device attribute. * @_name: Attribute name. @@ -154,7 +175,7 @@ ssize_t device_show_string(struct device *dev, struct device_attribute *attr, * }; */ #define DEVICE_ATTR(_name, _mode, _show, _store) \ - struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store) + struct device_attribute dev_attr_##_name = __DEVICE_ATTR(_name, _mode, _show, _store) /** * DEVICE_ATTR_RW - Define a read-write device attribute. @@ -164,7 +185,7 @@ ssize_t device_show_string(struct device *dev, struct device_attribute *attr, * and @_store is <_name>_store. */ #define DEVICE_ATTR_RW(_name) \ - struct device_attribute dev_attr_##_name = __ATTR_RW(_name) + struct device_attribute dev_attr_##_name = __DEVICE_ATTR_RW(_name) /** * DEVICE_ATTR_ADMIN_RW - Define an admin-only read-write device attribute. @@ -173,7 +194,7 @@ ssize_t device_show_string(struct device *dev, struct device_attribute *attr, * Like DEVICE_ATTR_RW(), but @_mode is 0600. */ #define DEVICE_ATTR_ADMIN_RW(_name) \ - struct device_attribute dev_attr_##_name = __ATTR_RW_MODE(_name, 0600) + struct device_attribute dev_attr_##_name = __DEVICE_ATTR_RW_MODE(_name, 0600) /** * DEVICE_ATTR_RW_NAMED - Define a read-write device attribute with a sysfs name @@ -198,7 +219,7 @@ ssize_t device_show_string(struct device *dev, struct device_attribute *attr, * Like DEVICE_ATTR(), but @_mode is 0444 and @_show is <_name>_show. */ #define DEVICE_ATTR_RO(_name) \ - struct device_attribute dev_attr_##_name = __ATTR_RO(_name) + struct device_attribute dev_attr_##_name = __DEVICE_ATTR_RO(_name) /** * DEVICE_ATTR_ADMIN_RO - Define an admin-only readable device attribute. @@ -207,7 +228,7 @@ ssize_t device_show_string(struct device *dev, struct device_attribute *attr, * Like DEVICE_ATTR_RO(), but @_mode is 0400. */ #define DEVICE_ATTR_ADMIN_RO(_name) \ - struct device_attribute dev_attr_##_name = __ATTR_RO_MODE(_name, 0400) + struct device_attribute dev_attr_##_name = __DEVICE_ATTR_RO_MODE(_name, 0400) /** * DEVICE_ATTR_RO_NAMED - Define a read-only device attribute with a sysfs name @@ -231,7 +252,7 @@ ssize_t device_show_string(struct device *dev, struct device_attribute *attr, * Like DEVICE_ATTR(), but @_mode is 0200 and @_store is <_name>_store. */ #define DEVICE_ATTR_WO(_name) \ - struct device_attribute dev_attr_##_name = __ATTR_WO(_name) + struct device_attribute dev_attr_##_name = __DEVICE_ATTR_WO(_name) /** * DEVICE_ATTR_WO_NAMED - Define a read-only device attribute with a sysfs name @@ -259,7 +280,7 @@ ssize_t device_show_string(struct device *dev, struct device_attribute *attr, */ #define DEVICE_ULONG_ATTR(_name, _mode, _var) \ struct dev_ext_attribute dev_attr_##_name = \ - { __ATTR(_name, _mode, device_show_ulong, device_store_ulong), &(_var) } + { __DEVICE_ATTR(_name, _mode, device_show_ulong, device_store_ulong), &(_var) } /** * DEVICE_INT_ATTR - Define a device attribute backed by an int. @@ -271,7 +292,7 @@ ssize_t device_show_string(struct device *dev, struct device_attribute *attr, */ #define DEVICE_INT_ATTR(_name, _mode, _var) \ struct dev_ext_attribute dev_attr_##_name = \ - { __ATTR(_name, _mode, device_show_int, device_store_int), &(_var) } + { __DEVICE_ATTR(_name, _mode, device_show_int, device_store_int), &(_var) } /** * DEVICE_BOOL_ATTR - Define a device attribute backed by a bool. @@ -283,7 +304,7 @@ ssize_t device_show_string(struct device *dev, struct device_attribute *attr, */ #define DEVICE_BOOL_ATTR(_name, _mode, _var) \ struct dev_ext_attribute dev_attr_##_name = \ - { __ATTR(_name, _mode, device_show_bool, device_store_bool), &(_var) } + { __DEVICE_ATTR(_name, _mode, device_show_bool, device_store_bool), &(_var) } /** * DEVICE_STRING_ATTR_RO - Define a device attribute backed by a r/o string. @@ -296,11 +317,11 @@ ssize_t device_show_string(struct device *dev, struct device_attribute *attr, */ #define DEVICE_STRING_ATTR_RO(_name, _mode, _var) \ struct dev_ext_attribute dev_attr_##_name = \ - { __ATTR(_name, (_mode) & ~0222, device_show_string, NULL), (_var) } + { __DEVICE_ATTR(_name, (_mode) & ~0222, device_show_string, NULL), (_var) } #define DEVICE_ATTR_IGNORE_LOCKDEP(_name, _mode, _show, _store) \ struct device_attribute dev_attr_##_name = \ - __ATTR_IGNORE_LOCKDEP(_name, _mode, _show, _store) + __DEVICE_ATTR_IGNORE_LOCKDEP(_name, _mode, _show, _store) int device_create_file(struct device *device, const struct device_attribute *entry); -- cgit v1.2.3 From 13443fbf62414e42afb1675ab2d3b767c6466915 Mon Sep 17 00:00:00 2001 From: Thomas Weißschuh Date: Tue, 12 May 2026 18:39:13 +0200 Subject: driver core: Stop using generic sysfs macros for device attributes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The constification of device attributes will require a transition phase, where 'struct device_attribute' contains a classic non-const and a new const variant of the 'show' and 'store' callbacks. As __ATTR() and friends can not handle this duplication stop using them. Signed-off-by: Thomas Weißschuh Link: https://patch.msgid.link/20260512-sysfs-const-attr-device_attr-prep-v3-3-cb7c17b34d52@weissschuh.net Signed-off-by: Greg Kroah-Hartman --- include/linux/device.h | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/include/linux/device.h b/include/linux/device.h index 7d6796532313..62985a8a78d3 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -134,26 +134,38 @@ ssize_t device_store_bool(struct device *dev, struct device_attribute *attr, ssize_t device_show_string(struct device *dev, struct device_attribute *attr, char *buf); -#define __DEVICE_ATTR(_name, _mode, _show, _store) \ - __ATTR(_name, _mode, _show, _store) +#define __DEVICE_ATTR(_name, _mode, _show, _store) { \ + .attr = {.name = __stringify(_name), \ + .mode = VERIFY_OCTAL_PERMISSIONS(_mode) }, \ + .show = _show, \ + .store = _store, \ +} #define __DEVICE_ATTR_RO_MODE(_name, _mode) \ - __ATTR_RO_MODE(_name, _mode) + __DEVICE_ATTR(_name, _mode, _name##_show, NULL) #define __DEVICE_ATTR_RO(_name) \ - __ATTR_RO(_name) + __DEVICE_ATTR_RO_MODE(_name, 0444) #define __DEVICE_ATTR_WO(_name) \ - __ATTR_WO(_name) + __DEVICE_ATTR(_name, 0200, NULL, _name##_store) #define __DEVICE_ATTR_RW_MODE(_name, _mode) \ - __ATTR_RW_MODE(_name, _mode) + __DEVICE_ATTR(_name, _mode, _name##_show, _name##_store) #define __DEVICE_ATTR_RW(_name) \ - __ATTR_RW(_name) - -#define __DEVICE_ATTR_IGNORE_LOCKDEP(_name, _mode, _show, _store) \ - __ATTR_IGNORE_LOCKDEP(_name, _mode, _show, _store) + __DEVICE_ATTR_RW_MODE(_name, 0644) + +#ifdef CONFIG_DEBUG_LOCK_ALLOC +#define __DEVICE_ATTR_IGNORE_LOCKDEP(_name, _mode, _show, _store) { \ + .attr = {.name = __stringify(_name), .mode = _mode, \ + .ignore_lockdep = true }, \ + .show = _show, \ + .store = _store, \ +} +#else +#define __DEVICE_ATTR_IGNORE_LOCKDEP __DEVICE_ATTR +#endif /** * DEVICE_ATTR - Define a device attribute. -- cgit v1.2.3 From 434506b86a6cde84a0ef19daa9e3b1926e2f96a9 Mon Sep 17 00:00:00 2001 From: Thomas Weißschuh Date: Tue, 12 May 2026 18:39:14 +0200 Subject: driver core: Allow the constification of device attributes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow device attribute to reside in read-only memory. Both const and non-const attributes are handled by the utility macros and attributes can be migrated one-by-one. Signed-off-by: Thomas Weißschuh Link: https://patch.msgid.link/20260512-sysfs-const-attr-device_attr-prep-v3-4-cb7c17b34d52@weissschuh.net Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 12 ++++++--- include/linux/device.h | 69 +++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 65 insertions(+), 16 deletions(-) diff --git a/drivers/base/core.c b/drivers/base/core.c index cc0d6984f1d4..433fbb863fc0 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -2485,9 +2485,11 @@ static ssize_t dev_attr_show(struct kobject *kobj, struct attribute *attr, if (dev_attr->show) ret = dev_attr->show(dev, dev_attr, buf); + else if (dev_attr->show_const) + ret = dev_attr->show_const(dev, dev_attr, buf); if (ret >= (ssize_t)PAGE_SIZE) { - printk("dev_attr_show: %pS returned bad count\n", - dev_attr->show); + printk("dev_attr_show: %pS/%pS returned bad count\n", + dev_attr->show, dev_attr->show_const); } return ret; } @@ -2501,6 +2503,8 @@ static ssize_t dev_attr_store(struct kobject *kobj, struct attribute *attr, if (dev_attr->store) ret = dev_attr->store(dev, dev_attr, buf, count); + else if (dev_attr->store_const) + ret = dev_attr->store_const(dev, dev_attr, buf, count); return ret; } @@ -3113,10 +3117,10 @@ int device_create_file(struct device *dev, int error = 0; if (dev) { - WARN(((attr->attr.mode & S_IWUGO) && !attr->store), + WARN(((attr->attr.mode & S_IWUGO) && !(attr->store || attr->store_const)), "Attribute %s: write permission without 'store'\n", attr->attr.name); - WARN(((attr->attr.mode & S_IRUGO) && !attr->show), + WARN(((attr->attr.mode & S_IRUGO) && !(attr->show || attr->show_const)), "Attribute %s: read permission without 'show'\n", attr->attr.name); error = sysfs_create_file(&dev->kobj, &attr->attr); diff --git a/include/linux/device.h b/include/linux/device.h index 62985a8a78d3..7b2baffdd2f5 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -103,10 +103,18 @@ struct device_type { */ struct device_attribute { struct attribute attr; - ssize_t (*show)(struct device *dev, struct device_attribute *attr, - char *buf); - ssize_t (*store)(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count); + __SYSFS_FUNCTION_ALTERNATIVE( + ssize_t (*show)(struct device *dev, struct device_attribute *attr, + char *buf); + ssize_t (*show_const)(struct device *dev, const struct device_attribute *attr, + char *buf); + ); + __SYSFS_FUNCTION_ALTERNATIVE( + ssize_t (*store)(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count); + ssize_t (*store_const)(struct device *dev, const struct device_attribute *attr, + const char *buf, size_t count); + ); }; /** @@ -134,11 +142,50 @@ ssize_t device_store_bool(struct device *dev, struct device_attribute *attr, ssize_t device_show_string(struct device *dev, struct device_attribute *attr, char *buf); +typedef ssize_t __device_show_handler_const(struct device *dev, const struct device_attribute *attr, + char *buf); +typedef ssize_t __device_store_handler_const(struct device *dev, const struct device_attribute *attr, + const char *buf, size_t count); + +#ifdef CONFIG_CFI + +#define __DEVICE_ATTR_SHOW_STORE(_show, _store) \ + .show = _Generic(_show, \ + __device_show_handler_const * : NULL, \ + default : _show \ + ), \ + .show_const = _Generic(_show, \ + __device_show_handler_const * : _show, \ + default : NULL \ + ), \ + .store = _Generic(_store, \ + __device_store_handler_const * : NULL, \ + default : _store \ + ), \ + .store_const = _Generic(_store, \ + __device_store_handler_const * : _store, \ + default : NULL \ + ), + +#else + +#define __DEVICE_ATTR_SHOW_STORE(_show, _store) \ + .show = _Generic(_show, \ + __device_show_handler_const * : (void *)_show, \ + default : _show \ + ), \ + .store = _Generic(_store, \ + __device_store_handler_const * : (void *)_store, \ + default : _store \ + ), \ + +#endif + + #define __DEVICE_ATTR(_name, _mode, _show, _store) { \ .attr = {.name = __stringify(_name), \ .mode = VERIFY_OCTAL_PERMISSIONS(_mode) }, \ - .show = _show, \ - .store = _store, \ + __DEVICE_ATTR_SHOW_STORE(_show, _store) \ } #define __DEVICE_ATTR_RO_MODE(_name, _mode) \ @@ -160,8 +207,7 @@ ssize_t device_show_string(struct device *dev, struct device_attribute *attr, #define __DEVICE_ATTR_IGNORE_LOCKDEP(_name, _mode, _show, _store) { \ .attr = {.name = __stringify(_name), .mode = _mode, \ .ignore_lockdep = true }, \ - .show = _show, \ - .store = _store, \ + __DEVICE_ATTR_SHOW_STORE(_show, _store) \ } #else #define __DEVICE_ATTR_IGNORE_LOCKDEP __DEVICE_ATTR @@ -220,8 +266,7 @@ ssize_t device_show_string(struct device *dev, struct device_attribute *attr, #define DEVICE_ATTR_RW_NAMED(_name, _attrname) \ struct device_attribute dev_attr_##_name = { \ .attr = { .name = _attrname, .mode = 0644 }, \ - .show = _name##_show, \ - .store = _name##_store, \ + __DEVICE_ATTR_SHOW_STORE(_name##_show, _name##_store) \ } /** @@ -254,7 +299,7 @@ ssize_t device_show_string(struct device *dev, struct device_attribute *attr, #define DEVICE_ATTR_RO_NAMED(_name, _attrname) \ struct device_attribute dev_attr_##_name = { \ .attr = { .name = _attrname, .mode = 0444 }, \ - .show = _name##_show, \ + __DEVICE_ATTR_SHOW_STORE(_name##_show, NULL) \ } /** @@ -278,7 +323,7 @@ ssize_t device_show_string(struct device *dev, struct device_attribute *attr, #define DEVICE_ATTR_WO_NAMED(_name, _attrname) \ struct device_attribute dev_attr_##_name = { \ .attr = { .name = _attrname, .mode = 0200 }, \ - .store = _name##_store, \ + __DEVICE_ATTR_SHOW_STORE(NULL, _name##_store) \ } /** -- cgit v1.2.3 From 024480bf8d75bd16894c5b0eb6082b6e6dae4970 Mon Sep 17 00:00:00 2001 From: Thomas Weißschuh Date: Tue, 12 May 2026 18:39:15 +0200 Subject: driver core: Constify core device attributes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To make sure these attributes are not modified by accident or by an attacker, move them to read-only memory. Signed-off-by: Thomas Weißschuh Link: https://patch.msgid.link/20260512-sysfs-const-attr-device_attr-prep-v3-5-cb7c17b34d52@weissschuh.net Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/drivers/base/core.c b/drivers/base/core.c index 433fbb863fc0..4d026682944f 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -495,7 +495,7 @@ void device_pm_move_to_tail(struct device *dev) #define to_devlink(dev) container_of((dev), struct device_link, link_dev) static ssize_t status_show(struct device *dev, - struct device_attribute *attr, char *buf) + const struct device_attribute *attr, char *buf) { const char *output; @@ -525,10 +525,10 @@ static ssize_t status_show(struct device *dev, return sysfs_emit(buf, "%s\n", output); } -static DEVICE_ATTR_RO(status); +static const DEVICE_ATTR_RO(status); static ssize_t auto_remove_on_show(struct device *dev, - struct device_attribute *attr, char *buf) + const struct device_attribute *attr, char *buf) { struct device_link *link = to_devlink(dev); const char *output; @@ -542,27 +542,27 @@ static ssize_t auto_remove_on_show(struct device *dev, return sysfs_emit(buf, "%s\n", output); } -static DEVICE_ATTR_RO(auto_remove_on); +static const DEVICE_ATTR_RO(auto_remove_on); static ssize_t runtime_pm_show(struct device *dev, - struct device_attribute *attr, char *buf) + const struct device_attribute *attr, char *buf) { struct device_link *link = to_devlink(dev); return sysfs_emit(buf, "%d\n", device_link_test(link, DL_FLAG_PM_RUNTIME)); } -static DEVICE_ATTR_RO(runtime_pm); +static const DEVICE_ATTR_RO(runtime_pm); static ssize_t sync_state_only_show(struct device *dev, - struct device_attribute *attr, char *buf) + const struct device_attribute *attr, char *buf) { struct device_link *link = to_devlink(dev); return sysfs_emit(buf, "%d\n", device_link_test(link, DL_FLAG_SYNC_STATE_ONLY)); } -static DEVICE_ATTR_RO(sync_state_only); +static const DEVICE_ATTR_RO(sync_state_only); -static struct attribute *devlink_attrs[] = { +static const struct attribute *const devlink_attrs[] = { &dev_attr_status.attr, &dev_attr_auto_remove_on.attr, &dev_attr_runtime_pm.attr, @@ -1306,7 +1306,7 @@ static void device_link_drop_managed(struct device_link *link) } static ssize_t waiting_for_supplier_show(struct device *dev, - struct device_attribute *attr, + const struct device_attribute *attr, char *buf) { bool val; @@ -1317,7 +1317,7 @@ static ssize_t waiting_for_supplier_show(struct device *dev, device_unlock(dev); return sysfs_emit(buf, "%u\n", val); } -static DEVICE_ATTR_RO(waiting_for_supplier); +static const DEVICE_ATTR_RO(waiting_for_supplier); /** * device_links_force_bind - Prepares device to be force bound @@ -2792,7 +2792,7 @@ static const struct kset_uevent_ops device_uevent_ops = { .uevent = dev_uevent, }; -static ssize_t uevent_show(struct device *dev, struct device_attribute *attr, +static ssize_t uevent_show(struct device *dev, const struct device_attribute *attr, char *buf) { struct kobject *top_kobj; @@ -2835,7 +2835,7 @@ out: return len; } -static ssize_t uevent_store(struct device *dev, struct device_attribute *attr, +static ssize_t uevent_store(struct device *dev, const struct device_attribute *attr, const char *buf, size_t count) { int rc; @@ -2849,9 +2849,9 @@ static ssize_t uevent_store(struct device *dev, struct device_attribute *attr, return count; } -static DEVICE_ATTR_RW(uevent); +static const DEVICE_ATTR_RW(uevent); -static ssize_t online_show(struct device *dev, struct device_attribute *attr, +static ssize_t online_show(struct device *dev, const struct device_attribute *attr, char *buf) { bool val; @@ -2862,7 +2862,7 @@ static ssize_t online_show(struct device *dev, struct device_attribute *attr, return sysfs_emit(buf, "%u\n", val); } -static ssize_t online_store(struct device *dev, struct device_attribute *attr, +static ssize_t online_store(struct device *dev, const struct device_attribute *attr, const char *buf, size_t count) { bool val; @@ -2880,9 +2880,9 @@ static ssize_t online_store(struct device *dev, struct device_attribute *attr, unlock_device_hotplug(); return ret < 0 ? ret : count; } -static DEVICE_ATTR_RW(online); +static const DEVICE_ATTR_RW(online); -static ssize_t removable_show(struct device *dev, struct device_attribute *attr, +static ssize_t removable_show(struct device *dev, const struct device_attribute *attr, char *buf) { const char *loc; @@ -2899,7 +2899,7 @@ static ssize_t removable_show(struct device *dev, struct device_attribute *attr, } return sysfs_emit(buf, "%s\n", loc); } -static DEVICE_ATTR_RO(removable); +static const DEVICE_ATTR_RO(removable); int device_add_groups(struct device *dev, const struct attribute_group *const *groups) @@ -3050,12 +3050,12 @@ static void device_remove_attrs(struct device *dev) device_remove_groups(dev, class->dev_groups); } -static ssize_t dev_show(struct device *dev, struct device_attribute *attr, +static ssize_t dev_show(struct device *dev, const struct device_attribute *attr, char *buf) { return print_dev_t(buf, dev->devt); } -static DEVICE_ATTR_RO(dev); +static const DEVICE_ATTR_RO(dev); /* /sys/devices/ */ struct kset *devices_kset; -- cgit v1.2.3 From f9e6da99fe49277979798a1c3b9790ae10aaa18a Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Mon, 25 May 2026 03:23:21 +0200 Subject: driver core: Fix missing jiffies conversion in deferred_probe_extend_timeout() mod_delayed_work() takes jiffies, not seconds. Thus, restore the dropped conversion. While at it, fix incorrect indentation. Fixes: 1137838865bf ("driver core: Use mod_delayed_work to prevent lost deferred probe work") Tested-by: Biju Das Tested-by: Geert Uytterhoeven Reviewed-by: Greg Kroah-Hartman Link: https://patch.msgid.link/20260525012340.3860581-1-dakr@kernel.org Signed-off-by: Danilo Krummrich --- drivers/base/dd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 172a02a438a2..bebb43acc132 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -324,7 +324,7 @@ void deferred_probe_extend_timeout(void) * start a new one. */ if (mod_delayed_work(system_wq, &deferred_probe_timeout_work, - driver_deferred_probe_timeout)) + secs_to_jiffies(driver_deferred_probe_timeout))) pr_debug("Extended deferred probe timeout by %d secs\n", driver_deferred_probe_timeout); } -- cgit v1.2.3 From 557495bc879013c3d5e21d667e987e7ce3a514de Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Mon, 25 May 2026 03:23:22 +0200 Subject: driver core: Guard deferred probe timeout extension with delayed_work_pending() mod_delayed_work() unconditionally queues the work even when it wasn't previously pending, which can fire the timeout prematurely or restart it after it already fired. Add a delayed_work_pending() guard to restore the originally intended semantics. Premature firing calls fw_devlink_drivers_done() before all built-in drivers have registered, causing fw_devlink to prematurely relax device links for suppliers whose drivers haven't loaded yet. Fixes: 1137838865bf ("driver core: Use mod_delayed_work to prevent lost deferred probe work") Tested-by: Geert Uytterhoeven Reviewed-by: Greg Kroah-Hartman Link: https://patch.msgid.link/20260525012340.3860581-2-dakr@kernel.org Signed-off-by: Danilo Krummrich --- drivers/base/dd.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/base/dd.c b/drivers/base/dd.c index bebb43acc132..905269ecef9b 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -323,7 +323,8 @@ void deferred_probe_extend_timeout(void) * If the work hasn't been queued yet or if the work expired, don't * start a new one. */ - if (mod_delayed_work(system_wq, &deferred_probe_timeout_work, + if (delayed_work_pending(&deferred_probe_timeout_work) && + mod_delayed_work(system_wq, &deferred_probe_timeout_work, secs_to_jiffies(driver_deferred_probe_timeout))) pr_debug("Extended deferred probe timeout by %d secs\n", driver_deferred_probe_timeout); -- cgit v1.2.3 From e566a9e17f3774c962b6d2522750f227f027edc6 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Mon, 25 May 2026 22:20:48 +0200 Subject: rust: pci: use 'static lifetime for PCI BAR resource names pci_request_region() stores the name pointer directly in struct resource; use &'static CStr to ensure the pointer remains valid even if the Bar is leaked. Cc: stable@vger.kernel.org Reported-by: Sashiko Closes: https://lore.kernel.org/all/20260522004943.CDA7C1F000E9@smtp.kernel.org/ Fixes: 3c2e31d717ac ("rust: pci: move I/O infrastructure to separate file") Reviewed-by: Alexandre Courbot Reviewed-by: Eliot Courtney Reviewed-by: Gary Guo Link: https://patch.msgid.link/20260525202921.124698-2-dakr@kernel.org Signed-off-by: Danilo Krummrich --- rust/kernel/pci/io.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rust/kernel/pci/io.rs b/rust/kernel/pci/io.rs index ae78676c927f..3ce21482b079 100644 --- a/rust/kernel/pci/io.rs +++ b/rust/kernel/pci/io.rs @@ -153,7 +153,7 @@ pub struct Bar { } impl Bar { - pub(super) fn new(pdev: &Device, num: u32, name: &CStr) -> Result { + pub(super) fn new(pdev: &Device, num: u32, name: &'static CStr) -> Result { let len = pdev.resource_len(num)?; if len == 0 { return Err(ENOMEM); @@ -252,7 +252,7 @@ impl Device { pub fn iomap_region_sized<'a, const SIZE: usize>( &'a self, bar: u32, - name: &'a CStr, + name: &'static CStr, ) -> impl PinInit>, Error> + 'a { Devres::new(self.as_ref(), Bar::::new(self, bar, name)) } @@ -261,7 +261,7 @@ impl Device { pub fn iomap_region<'a>( &'a self, bar: u32, - name: &'a CStr, + name: &'static CStr, ) -> impl PinInit, Error> + 'a { self.iomap_region_sized::<0>(bar, name) } -- cgit v1.2.3 From e9df918d61e08f4281c3bcd42486f1505f396b1d Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Mon, 25 May 2026 22:20:49 +0200 Subject: rust: alloc: remove `'static` bound on `ForeignOwnable` The `'static` bound is currently necessary because there's no restriction on the lifetime of the GAT. Add a `Self: 'a` bound to restrict possible lifetimes on `Borrowed` and `BorrowedMut`, and lift the `'static` requirement. Reviewed-by: Alexandre Courbot Reviewed-by: Greg Kroah-Hartman Signed-off-by: Gary Guo Acked-by: Miguel Ojeda Link: https://patch.msgid.link/20260525202921.124698-3-dakr@kernel.org Signed-off-by: Danilo Krummrich --- rust/kernel/alloc/kbox.rs | 24 ++++++++++++++++++------ rust/kernel/types.rs | 8 ++++++-- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/rust/kernel/alloc/kbox.rs b/rust/kernel/alloc/kbox.rs index c824ed6e1523..2f8c16473c2c 100644 --- a/rust/kernel/alloc/kbox.rs +++ b/rust/kernel/alloc/kbox.rs @@ -477,7 +477,7 @@ where // SAFETY: The pointer returned by `into_foreign` comes from a well aligned // pointer to `T` allocated by `A`. -unsafe impl ForeignOwnable for Box +unsafe impl ForeignOwnable for Box where A: Allocator, { @@ -487,8 +487,14 @@ where core::mem::align_of::() }; - type Borrowed<'a> = &'a T; - type BorrowedMut<'a> = &'a mut T; + type Borrowed<'a> + = &'a T + where + Self: 'a; + type BorrowedMut<'a> + = &'a mut T + where + Self: 'a; fn into_foreign(self) -> *mut c_void { Box::into_raw(self).cast() @@ -516,13 +522,19 @@ where // SAFETY: The pointer returned by `into_foreign` comes from a well aligned // pointer to `T` allocated by `A`. -unsafe impl ForeignOwnable for Pin> +unsafe impl ForeignOwnable for Pin> where A: Allocator, { const FOREIGN_ALIGN: usize = as ForeignOwnable>::FOREIGN_ALIGN; - type Borrowed<'a> = Pin<&'a T>; - type BorrowedMut<'a> = Pin<&'a mut T>; + type Borrowed<'a> + = Pin<&'a T> + where + Self: 'a; + type BorrowedMut<'a> + = Pin<&'a mut T> + where + Self: 'a; fn into_foreign(self) -> *mut c_void { // SAFETY: We are still treating the box as pinned. diff --git a/rust/kernel/types.rs b/rust/kernel/types.rs index 4329d3c2c2e5..9cf9f869d195 100644 --- a/rust/kernel/types.rs +++ b/rust/kernel/types.rs @@ -27,10 +27,14 @@ pub unsafe trait ForeignOwnable: Sized { const FOREIGN_ALIGN: usize; /// Type used to immutably borrow a value that is currently foreign-owned. - type Borrowed<'a>; + type Borrowed<'a> + where + Self: 'a; /// Type used to mutably borrow a value that is currently foreign-owned. - type BorrowedMut<'a>; + type BorrowedMut<'a> + where + Self: 'a; /// Converts a Rust-owned object to a foreign-owned one. /// -- cgit v1.2.3 From c8a43666bade4683640dc835f92cd456d29cee55 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Mon, 25 May 2026 22:20:50 +0200 Subject: rust: driver: move 'static bounds to constructor With the ForeignOwnable lifetime change, the 'static bound is no longer necessary on the drvdata methods or bus adapter impls. Move it to the Registration constructor instead. Reviewed-by: Alexandre Courbot Reviewed-by: Greg Kroah-Hartman Signed-off-by: Gary Guo Link: https://patch.msgid.link/20260525202921.124698-4-dakr@kernel.org Co-developed-by: Danilo Krummrich Signed-off-by: Danilo Krummrich --- rust/kernel/auxiliary.rs | 6 +++--- rust/kernel/device.rs | 8 ++++---- rust/kernel/driver.rs | 7 +++++-- rust/kernel/i2c.rs | 8 ++++---- rust/kernel/pci.rs | 6 +++--- rust/kernel/platform.rs | 8 ++++---- rust/kernel/usb.rs | 6 +++--- 7 files changed, 26 insertions(+), 23 deletions(-) diff --git a/rust/kernel/auxiliary.rs b/rust/kernel/auxiliary.rs index 19aec94aa95b..35b44d194f67 100644 --- a/rust/kernel/auxiliary.rs +++ b/rust/kernel/auxiliary.rs @@ -44,7 +44,7 @@ pub struct Adapter(T); // - `T` is the type of the driver's device private data. // - `struct auxiliary_driver` embeds a `struct device_driver`. // - `DEVICE_DRIVER_OFFSET` is the correct byte offset to the embedded `struct device_driver`. -unsafe impl driver::DriverLayout for Adapter { +unsafe impl driver::DriverLayout for Adapter { type DriverType = bindings::auxiliary_driver; type DriverData = T; const DEVICE_DRIVER_OFFSET: usize = core::mem::offset_of!(Self::DriverType, driver); @@ -52,7 +52,7 @@ unsafe impl driver::DriverLayout for Adapter { // SAFETY: A call to `unregister` for a given instance of `DriverType` is guaranteed to be valid if // a preceding call to `register` has been successful. -unsafe impl driver::RegistrationOps for Adapter { +unsafe impl driver::RegistrationOps for Adapter { unsafe fn register( adrv: &Opaque, name: &'static CStr, @@ -78,7 +78,7 @@ unsafe impl driver::RegistrationOps for Adapter { } } -impl Adapter { +impl Adapter { extern "C" fn probe_callback( adev: *mut bindings::auxiliary_device, id: *const bindings::auxiliary_device_id, diff --git a/rust/kernel/device.rs b/rust/kernel/device.rs index fd50399aadea..5df8fa108a52 100644 --- a/rust/kernel/device.rs +++ b/rust/kernel/device.rs @@ -203,7 +203,7 @@ impl Device { impl Device { /// Store a pointer to the bound driver's private data. - pub fn set_drvdata(&self, data: impl PinInit) -> Result { + pub fn set_drvdata(&self, data: impl PinInit) -> Result { let data = KBox::pin_init(data, GFP_KERNEL)?; // SAFETY: By the type invariants, `self.as_raw()` is a valid pointer to a `struct device`. @@ -218,7 +218,7 @@ impl Device { /// /// - The type `T` must match the type of the `ForeignOwnable` previously stored by /// [`Device::set_drvdata`]. - pub(crate) unsafe fn drvdata_obtain(&self) -> Option>> { + pub(crate) unsafe fn drvdata_obtain(&self) -> Option>> { // SAFETY: By the type invariants, `self.as_raw()` is a valid pointer to a `struct device`. let ptr = unsafe { bindings::dev_get_drvdata(self.as_raw()) }; @@ -244,7 +244,7 @@ impl Device { /// device is fully unbound. /// - The type `T` must match the type of the `ForeignOwnable` previously stored by /// [`Device::set_drvdata`]. - pub unsafe fn drvdata_borrow(&self) -> Pin<&T> { + pub unsafe fn drvdata_borrow(&self) -> Pin<&T> { // SAFETY: `drvdata_unchecked()` has the exact same safety requirements as the ones // required by this method. unsafe { self.drvdata_unchecked() } @@ -260,7 +260,7 @@ impl Device { /// the device is fully unbound. /// - The type `T` must match the type of the `ForeignOwnable` previously stored by /// [`Device::set_drvdata`]. - unsafe fn drvdata_unchecked(&self) -> Pin<&T> { + unsafe fn drvdata_unchecked(&self) -> Pin<&T> { // SAFETY: By the type invariants, `self.as_raw()` is a valid pointer to a `struct device`. let ptr = unsafe { bindings::dev_get_drvdata(self.as_raw()) }; diff --git a/rust/kernel/driver.rs b/rust/kernel/driver.rs index 36de8098754d..c8406dc4da60 100644 --- a/rust/kernel/driver.rs +++ b/rust/kernel/driver.rs @@ -181,7 +181,7 @@ unsafe impl Sync for Registration {} // any thread, so `Registration` is `Send`. unsafe impl Send for Registration {} -impl Registration { +impl Registration { extern "C" fn post_unbind_callback(dev: *mut bindings::device) { // SAFETY: The driver core only ever calls the post unbind callback with a valid pointer to // a `struct device`. @@ -215,7 +215,10 @@ impl Registration { } /// Creates a new instance of the registration object. - pub fn new(name: &'static CStr, module: &'static ThisModule) -> impl PinInit { + pub fn new(name: &'static CStr, module: &'static ThisModule) -> impl PinInit + where + T: 'static, + { try_pin_init!(Self { reg <- Opaque::try_ffi_init(|ptr: *mut T::DriverType| { // SAFETY: `try_ffi_init` guarantees that `ptr` is valid for write. diff --git a/rust/kernel/i2c.rs b/rust/kernel/i2c.rs index 7b908f0c5a58..4ccee4ba4f23 100644 --- a/rust/kernel/i2c.rs +++ b/rust/kernel/i2c.rs @@ -96,7 +96,7 @@ pub struct Adapter(T); // - `T` is the type of the driver's device private data. // - `struct i2c_driver` embeds a `struct device_driver`. // - `DEVICE_DRIVER_OFFSET` is the correct byte offset to the embedded `struct device_driver`. -unsafe impl driver::DriverLayout for Adapter { +unsafe impl driver::DriverLayout for Adapter { type DriverType = bindings::i2c_driver; type DriverData = T; const DEVICE_DRIVER_OFFSET: usize = core::mem::offset_of!(Self::DriverType, driver); @@ -104,7 +104,7 @@ unsafe impl driver::DriverLayout for Adapter { // SAFETY: A call to `unregister` for a given instance of `DriverType` is guaranteed to be valid if // a preceding call to `register` has been successful. -unsafe impl driver::RegistrationOps for Adapter { +unsafe impl driver::RegistrationOps for Adapter { unsafe fn register( idrv: &Opaque, name: &'static CStr, @@ -151,7 +151,7 @@ unsafe impl driver::RegistrationOps for Adapter { } } -impl Adapter { +impl Adapter { extern "C" fn probe_callback(idev: *mut bindings::i2c_client) -> kernel::ffi::c_int { // SAFETY: The I2C bus only ever calls the probe callback with a valid pointer to a // `struct i2c_client`. @@ -222,7 +222,7 @@ impl Adapter { } } -impl driver::Adapter for Adapter { +impl driver::Adapter for Adapter { type IdInfo = T::IdInfo; fn of_id_table() -> Option> { diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs index af74ddff6114..17a33819dc0a 100644 --- a/rust/kernel/pci.rs +++ b/rust/kernel/pci.rs @@ -62,7 +62,7 @@ pub struct Adapter(T); // - `T` is the type of the driver's device private data. // - `struct pci_driver` embeds a `struct device_driver`. // - `DEVICE_DRIVER_OFFSET` is the correct byte offset to the embedded `struct device_driver`. -unsafe impl driver::DriverLayout for Adapter { +unsafe impl driver::DriverLayout for Adapter { type DriverType = bindings::pci_driver; type DriverData = T; const DEVICE_DRIVER_OFFSET: usize = core::mem::offset_of!(Self::DriverType, driver); @@ -70,7 +70,7 @@ unsafe impl driver::DriverLayout for Adapter { // SAFETY: A call to `unregister` for a given instance of `DriverType` is guaranteed to be valid if // a preceding call to `register` has been successful. -unsafe impl driver::RegistrationOps for Adapter { +unsafe impl driver::RegistrationOps for Adapter { unsafe fn register( pdrv: &Opaque, name: &'static CStr, @@ -96,7 +96,7 @@ unsafe impl driver::RegistrationOps for Adapter { } } -impl Adapter { +impl Adapter { extern "C" fn probe_callback( pdev: *mut bindings::pci_dev, id: *const bindings::pci_device_id, diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs index 8917d4ee499f..c7a3dcdde3b1 100644 --- a/rust/kernel/platform.rs +++ b/rust/kernel/platform.rs @@ -48,7 +48,7 @@ pub struct Adapter(T); // - `T` is the type of the driver's device private data. // - `struct platform_driver` embeds a `struct device_driver`. // - `DEVICE_DRIVER_OFFSET` is the correct byte offset to the embedded `struct device_driver`. -unsafe impl driver::DriverLayout for Adapter { +unsafe impl driver::DriverLayout for Adapter { type DriverType = bindings::platform_driver; type DriverData = T; const DEVICE_DRIVER_OFFSET: usize = core::mem::offset_of!(Self::DriverType, driver); @@ -56,7 +56,7 @@ unsafe impl driver::DriverLayout for Adapter { // SAFETY: A call to `unregister` for a given instance of `DriverType` is guaranteed to be valid if // a preceding call to `register` has been successful. -unsafe impl driver::RegistrationOps for Adapter { +unsafe impl driver::RegistrationOps for Adapter { unsafe fn register( pdrv: &Opaque, name: &'static CStr, @@ -91,7 +91,7 @@ unsafe impl driver::RegistrationOps for Adapter { } } -impl Adapter { +impl Adapter { extern "C" fn probe_callback(pdev: *mut bindings::platform_device) -> kernel::ffi::c_int { // SAFETY: The platform bus only ever calls the probe callback with a valid pointer to a // `struct platform_device`. @@ -124,7 +124,7 @@ impl Adapter { } } -impl driver::Adapter for Adapter { +impl driver::Adapter for Adapter { type IdInfo = T::IdInfo; fn of_id_table() -> Option> { diff --git a/rust/kernel/usb.rs b/rust/kernel/usb.rs index 9c17a672cd27..3f62da585281 100644 --- a/rust/kernel/usb.rs +++ b/rust/kernel/usb.rs @@ -39,7 +39,7 @@ pub struct Adapter(T); // - `T` is the type of the driver's device private data. // - `struct usb_driver` embeds a `struct device_driver`. // - `DEVICE_DRIVER_OFFSET` is the correct byte offset to the embedded `struct device_driver`. -unsafe impl driver::DriverLayout for Adapter { +unsafe impl driver::DriverLayout for Adapter { type DriverType = bindings::usb_driver; type DriverData = T; const DEVICE_DRIVER_OFFSET: usize = core::mem::offset_of!(Self::DriverType, driver); @@ -47,7 +47,7 @@ unsafe impl driver::DriverLayout for Adapter { // SAFETY: A call to `unregister` for a given instance of `DriverType` is guaranteed to be valid if // a preceding call to `register` has been successful. -unsafe impl driver::RegistrationOps for Adapter { +unsafe impl driver::RegistrationOps for Adapter { unsafe fn register( udrv: &Opaque, name: &'static CStr, @@ -73,7 +73,7 @@ unsafe impl driver::RegistrationOps for Adapter { } } -impl Adapter { +impl Adapter { extern "C" fn probe_callback( intf: *mut bindings::usb_interface, id: *const bindings::usb_device_id, -- cgit v1.2.3 From 7fdffdda630ee61ae0e09ef8f1ace52bbf70e2b0 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Mon, 25 May 2026 22:20:51 +0200 Subject: rust: driver: decouple driver private data from driver type Add a type Data<'bound> associated type to all bus driver traits, decoupling the driver's bus device private data type from the driver struct itself. In the context of adding a 'bound lifetime, making this an associated type has the advantage that it allows us to avoid a driver trait global lifetime and it avoids the need for ForLt for bus device private data; both of which make the subsequent implementation by buses much simpler. All existing drivers and doc examples set type Data = Self to preserve the current behavior. Reviewed-by: Alexandre Courbot Reviewed-by: Gary Guo Reviewed-by: Greg Kroah-Hartman Link: https://patch.msgid.link/20260525202921.124698-5-dakr@kernel.org Signed-off-by: Danilo Krummrich --- drivers/cpufreq/rcpufreq_dt.rs | 1 + drivers/gpu/drm/nova/driver.rs | 1 + drivers/gpu/drm/tyr/driver.rs | 1 + drivers/gpu/nova-core/driver.rs | 1 + drivers/pwm/pwm_th1520.rs | 1 + rust/kernel/auxiliary.rs | 18 +++++++++++------- rust/kernel/cpufreq.rs | 1 + rust/kernel/driver.rs | 24 ++++++++++++++---------- rust/kernel/i2c.rs | 32 ++++++++++++++++++-------------- rust/kernel/io/mem.rs | 2 ++ rust/kernel/pci.rs | 21 +++++++++++++-------- rust/kernel/platform.rs | 20 ++++++++++++-------- rust/kernel/usb.rs | 20 ++++++++++++-------- samples/rust/rust_debugfs.rs | 1 + samples/rust/rust_dma.rs | 1 + samples/rust/rust_driver_auxiliary.rs | 2 ++ samples/rust/rust_driver_i2c.rs | 1 + samples/rust/rust_driver_pci.rs | 1 + samples/rust/rust_driver_platform.rs | 1 + samples/rust/rust_driver_usb.rs | 1 + samples/rust/rust_i2c_client.rs | 1 + samples/rust/rust_soc.rs | 1 + 22 files changed, 98 insertions(+), 55 deletions(-) diff --git a/drivers/cpufreq/rcpufreq_dt.rs b/drivers/cpufreq/rcpufreq_dt.rs index f17bf64c22e2..b7eeb2730eb0 100644 --- a/drivers/cpufreq/rcpufreq_dt.rs +++ b/drivers/cpufreq/rcpufreq_dt.rs @@ -201,6 +201,7 @@ kernel::of_device_table!( impl platform::Driver for CPUFreqDTDriver { type IdInfo = (); + type Data = Self; const OF_ID_TABLE: Option> = Some(&OF_TABLE); fn probe( diff --git a/drivers/gpu/drm/nova/driver.rs b/drivers/gpu/drm/nova/driver.rs index b1af0a099551..08136ec0bccb 100644 --- a/drivers/gpu/drm/nova/driver.rs +++ b/drivers/gpu/drm/nova/driver.rs @@ -51,6 +51,7 @@ kernel::auxiliary_device_table!( impl auxiliary::Driver for NovaDriver { type IdInfo = (); + type Data = Self; const ID_TABLE: auxiliary::IdTable = &AUX_TABLE; fn probe(adev: &auxiliary::Device, _info: &Self::IdInfo) -> impl PinInit { diff --git a/drivers/gpu/drm/tyr/driver.rs b/drivers/gpu/drm/tyr/driver.rs index 279710b36a10..c81bf217743d 100644 --- a/drivers/gpu/drm/tyr/driver.rs +++ b/drivers/gpu/drm/tyr/driver.rs @@ -91,6 +91,7 @@ kernel::of_device_table!( impl platform::Driver for TyrPlatformDriverData { type IdInfo = (); + type Data = Self; const OF_ID_TABLE: Option> = Some(&OF_TABLE); fn probe( diff --git a/drivers/gpu/nova-core/driver.rs b/drivers/gpu/nova-core/driver.rs index 8fe484d357f6..699e27046c93 100644 --- a/drivers/gpu/nova-core/driver.rs +++ b/drivers/gpu/nova-core/driver.rs @@ -74,6 +74,7 @@ kernel::pci_device_table!( impl pci::Driver for NovaCore { type IdInfo = (); + type Data = Self; const ID_TABLE: pci::IdTable = &PCI_TABLE; fn probe(pdev: &pci::Device, _info: &Self::IdInfo) -> impl PinInit { diff --git a/drivers/pwm/pwm_th1520.rs b/drivers/pwm/pwm_th1520.rs index ddd44a5ce497..07795910a0b5 100644 --- a/drivers/pwm/pwm_th1520.rs +++ b/drivers/pwm/pwm_th1520.rs @@ -316,6 +316,7 @@ kernel::of_device_table!( impl platform::Driver for Th1520PwmPlatformDriver { type IdInfo = (); + type Data = Self; const OF_ID_TABLE: Option> = Some(&OF_TABLE); fn probe( diff --git a/rust/kernel/auxiliary.rs b/rust/kernel/auxiliary.rs index 35b44d194f67..4e83f9e27d78 100644 --- a/rust/kernel/auxiliary.rs +++ b/rust/kernel/auxiliary.rs @@ -41,12 +41,12 @@ pub struct Adapter(T); // SAFETY: // - `bindings::auxiliary_driver` is a C type declared as `repr(C)`. -// - `T` is the type of the driver's device private data. +// - `T::Data` is the type of the driver's device private data. // - `struct auxiliary_driver` embeds a `struct device_driver`. // - `DEVICE_DRIVER_OFFSET` is the correct byte offset to the embedded `struct device_driver`. unsafe impl driver::DriverLayout for Adapter { type DriverType = bindings::auxiliary_driver; - type DriverData = T; + type DriverData<'bound> = T::Data; const DEVICE_DRIVER_OFFSET: usize = core::mem::offset_of!(Self::DriverType, driver); } @@ -111,8 +111,8 @@ impl Adapter { // SAFETY: `remove_callback` is only ever called after a successful call to // `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called - // and stored a `Pin>`. - let data = unsafe { adev.as_ref().drvdata_borrow::() }; + // and stored a `Pin>`. + let data = unsafe { adev.as_ref().drvdata_borrow::() }; T::unbind(adev, data); } @@ -202,13 +202,17 @@ pub trait Driver { /// type IdInfo: 'static = (); type IdInfo: 'static; + /// The type of the driver's bus device private data. + type Data: Send; + /// The table of device ids supported by the driver. const ID_TABLE: IdTable; /// Auxiliary driver probe. /// /// Called when an auxiliary device is matches a corresponding driver. - fn probe(dev: &Device, id_info: &Self::IdInfo) -> impl PinInit; + fn probe(dev: &Device, id_info: &Self::IdInfo) + -> impl PinInit; /// Auxiliary driver unbind. /// @@ -219,8 +223,8 @@ pub trait Driver { /// `&Device` or `&Device` reference. For instance, drivers may try to perform I/O /// operations to gracefully tear down the device. /// - /// Otherwise, release operations for driver resources should be performed in `Self::drop`. - fn unbind(dev: &Device, this: Pin<&Self>) { + /// Otherwise, release operations for driver resources should be performed in `Drop`. + fn unbind(dev: &Device, this: Pin<&Self::Data>) { let _ = (dev, this); } } diff --git a/rust/kernel/cpufreq.rs b/rust/kernel/cpufreq.rs index d8d26870bea2..50dd2a2c3e81 100644 --- a/rust/kernel/cpufreq.rs +++ b/rust/kernel/cpufreq.rs @@ -888,6 +888,7 @@ pub trait Driver { /// /// impl platform::Driver for SampleDriver { /// type IdInfo = (); +/// type Data = Self; /// const OF_ID_TABLE: Option> = None; /// /// fn probe( diff --git a/rust/kernel/driver.rs b/rust/kernel/driver.rs index c8406dc4da60..5fd1cfd64e93 100644 --- a/rust/kernel/driver.rs +++ b/rust/kernel/driver.rs @@ -13,10 +13,13 @@ //! The main driver interface is defined by a bus specific driver trait. For instance: //! //! ```ignore -//! pub trait Driver: Send { +//! pub trait Driver { //! /// The type holding information about each device ID supported by the driver. //! type IdInfo: 'static; //! +//! /// The type of the driver's bus device private data. +//! type Data: Send; +//! //! /// The table of OF device ids supported by the driver. //! const OF_ID_TABLE: Option> = None; //! @@ -24,10 +27,11 @@ //! const ACPI_ID_TABLE: Option> = None; //! //! /// Driver probe. -//! fn probe(dev: &Device, id_info: &Self::IdInfo) -> impl PinInit; +//! fn probe(dev: &Device, id_info: &Self::IdInfo) +//! -> impl PinInit; //! //! /// Driver unbind (optional). -//! fn unbind(dev: &Device, this: Pin<&Self>) { +//! fn unbind(dev: &Device, this: Pin<&Self::Data>) { //! let _ = (dev, this); //! } //! } @@ -42,9 +46,9 @@ )] #![cfg_attr(CONFIG_PCI, doc = "* [`pci::Driver`](kernel::pci::Driver)")] //! -//! The `probe()` callback should return a `impl PinInit`, i.e. the driver's private -//! data. The bus abstraction should store the pointer in the corresponding bus device. The generic -//! [`Device`] infrastructure provides common helpers for this purpose on its +//! The `probe()` callback should return a `impl PinInit`, i.e. the driver's +//! private data. The bus abstraction should store the pointer in the corresponding bus device. The +//! generic [`Device`] infrastructure provides common helpers for this purpose on its //! [`Device`] implementation. //! //! All driver callbacks should provide a reference to the driver's private data. Once the driver @@ -118,8 +122,8 @@ pub unsafe trait DriverLayout { /// The specific driver type embedding a `struct device_driver`. type DriverType: Default; - /// The type of the driver's device private data. - type DriverData; + /// The type of the driver's bus device private data. + type DriverData<'bound>; /// Byte offset of the embedded `struct device_driver` within `DriverType`. /// @@ -193,8 +197,8 @@ impl Registration { // driver's device private data. // // SAFETY: By the safety requirements of the `Driver` trait, `T::DriverData` is the - // driver's device private data type. - drop(unsafe { dev.drvdata_obtain::() }); + // driver's bus device private data type. + drop(unsafe { dev.drvdata_obtain::>() }); } /// Attach generic `struct device_driver` callbacks. diff --git a/rust/kernel/i2c.rs b/rust/kernel/i2c.rs index 4ccee4ba4f23..bfd081518615 100644 --- a/rust/kernel/i2c.rs +++ b/rust/kernel/i2c.rs @@ -93,12 +93,12 @@ pub struct Adapter(T); // SAFETY: // - `bindings::i2c_driver` is a C type declared as `repr(C)`. -// - `T` is the type of the driver's device private data. +// - `T::Data` is the type of the driver's device private data. // - `struct i2c_driver` embeds a `struct device_driver`. // - `DEVICE_DRIVER_OFFSET` is the correct byte offset to the embedded `struct device_driver`. unsafe impl driver::DriverLayout for Adapter { type DriverType = bindings::i2c_driver; - type DriverData = T; + type DriverData<'bound> = T::Data; const DEVICE_DRIVER_OFFSET: usize = core::mem::offset_of!(Self::DriverType, driver); } @@ -176,8 +176,8 @@ impl Adapter { // SAFETY: `remove_callback` is only ever called after a successful call to // `probe_callback`, hence it's guaranteed that `I2cClient::set_drvdata()` has been called - // and stored a `Pin>`. - let data = unsafe { idev.as_ref().drvdata_borrow::() }; + // and stored a `Pin>`. + let data = unsafe { idev.as_ref().drvdata_borrow::() }; T::unbind(idev, data); } @@ -188,8 +188,8 @@ impl Adapter { // SAFETY: `shutdown_callback` is only ever called after a successful call to // `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called - // and stored a `Pin>`. - let data = unsafe { idev.as_ref().drvdata_borrow::() }; + // and stored a `Pin>`. + let data = unsafe { idev.as_ref().drvdata_borrow::() }; T::shutdown(idev, data); } @@ -294,6 +294,7 @@ macro_rules! module_i2c_driver { /// /// impl i2c::Driver for MyDriver { /// type IdInfo = (); +/// type Data = Self; /// const I2C_ID_TABLE: Option> = Some(&I2C_TABLE); /// const OF_ID_TABLE: Option> = Some(&OF_TABLE); /// const ACPI_ID_TABLE: Option> = Some(&ACPI_TABLE); @@ -301,15 +302,15 @@ macro_rules! module_i2c_driver { /// fn probe( /// _idev: &i2c::I2cClient, /// _id_info: Option<&Self::IdInfo>, -/// ) -> impl PinInit { +/// ) -> impl PinInit { /// Err(ENODEV) /// } /// -/// fn shutdown(_idev: &i2c::I2cClient, this: Pin<&Self>) { +/// fn shutdown(_idev: &i2c::I2cClient, this: Pin<&Self::Data>) { /// } /// } ///``` -pub trait Driver: Send { +pub trait Driver { /// The type holding information about each device id supported by the driver. // TODO: Use `associated_type_defaults` once stabilized: // @@ -318,6 +319,9 @@ pub trait Driver: Send { // ``` type IdInfo: 'static; + /// The type of the driver's bus device private data. + type Data: Send; + /// The table of device ids supported by the driver. const I2C_ID_TABLE: Option> = None; @@ -334,7 +338,7 @@ pub trait Driver: Send { fn probe( dev: &I2cClient, id_info: Option<&Self::IdInfo>, - ) -> impl PinInit; + ) -> impl PinInit; /// I2C driver shutdown. /// @@ -346,8 +350,8 @@ pub trait Driver: Send { /// /// This callback is distinct from final resource cleanup, as the driver instance remains valid /// after it returns. Any deallocation or teardown of driver-owned resources should instead be - /// handled in `Self::drop`. - fn shutdown(dev: &I2cClient, this: Pin<&Self>) { + /// handled in `Drop`. + fn shutdown(dev: &I2cClient, this: Pin<&Self::Data>) { let _ = (dev, this); } @@ -360,8 +364,8 @@ pub trait Driver: Send { /// `&Device` or `&Device` reference. For instance, drivers may try to perform I/O /// operations to gracefully tear down the device. /// - /// Otherwise, release operations for driver resources should be performed in `Self::drop`. - fn unbind(dev: &I2cClient, this: Pin<&Self>) { + /// Otherwise, release operations for driver resources should be performed in `Drop`. + fn unbind(dev: &I2cClient, this: Pin<&Self::Data>) { let _ = (dev, this); } } diff --git a/rust/kernel/io/mem.rs b/rust/kernel/io/mem.rs index 7dc78d547f7a..e136b676d372 100644 --- a/rust/kernel/io/mem.rs +++ b/rust/kernel/io/mem.rs @@ -62,6 +62,7 @@ impl<'a> IoRequest<'a> { /// /// impl platform::Driver for SampleDriver { /// # type IdInfo = (); + /// # type Data = Self; /// /// fn probe( /// pdev: &platform::Device, @@ -126,6 +127,7 @@ impl<'a> IoRequest<'a> { /// /// impl platform::Driver for SampleDriver { /// # type IdInfo = (); + /// # type Data = Self; /// /// fn probe( /// pdev: &platform::Device, diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs index 17a33819dc0a..c743f2abb62f 100644 --- a/rust/kernel/pci.rs +++ b/rust/kernel/pci.rs @@ -59,12 +59,12 @@ pub struct Adapter(T); // SAFETY: // - `bindings::pci_driver` is a C type declared as `repr(C)`. -// - `T` is the type of the driver's device private data. +// - `T::Data` is the type of the driver's device private data. // - `struct pci_driver` embeds a `struct device_driver`. // - `DEVICE_DRIVER_OFFSET` is the correct byte offset to the embedded `struct device_driver`. unsafe impl driver::DriverLayout for Adapter { type DriverType = bindings::pci_driver; - type DriverData = T; + type DriverData<'bound> = T::Data; const DEVICE_DRIVER_OFFSET: usize = core::mem::offset_of!(Self::DriverType, driver); } @@ -129,8 +129,8 @@ impl Adapter { // SAFETY: `remove_callback` is only ever called after a successful call to // `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called - // and stored a `Pin>`. - let data = unsafe { pdev.as_ref().drvdata_borrow::() }; + // and stored a `Pin>`. + let data = unsafe { pdev.as_ref().drvdata_borrow::() }; T::unbind(pdev, data); } @@ -279,6 +279,7 @@ macro_rules! pci_device_table { /// /// impl pci::Driver for MyDriver { /// type IdInfo = (); +/// type Data = Self; /// const ID_TABLE: pci::IdTable = &PCI_TABLE; /// /// fn probe( @@ -291,7 +292,7 @@ macro_rules! pci_device_table { ///``` /// Drivers must implement this trait in order to get a PCI driver registered. Please refer to the /// `Adapter` documentation for an example. -pub trait Driver: Send { +pub trait Driver { /// The type holding information about each device id supported by the driver. // TODO: Use `associated_type_defaults` once stabilized: // @@ -300,6 +301,9 @@ pub trait Driver: Send { // ``` type IdInfo: 'static; + /// The type of the driver's bus device private data. + type Data: Send; + /// The table of device ids supported by the driver. const ID_TABLE: IdTable; @@ -307,7 +311,8 @@ pub trait Driver: Send { /// /// Called when a new pci device is added or discovered. Implementers should /// attempt to initialize the device here. - fn probe(dev: &Device, id_info: &Self::IdInfo) -> impl PinInit; + fn probe(dev: &Device, id_info: &Self::IdInfo) + -> impl PinInit; /// PCI driver unbind. /// @@ -318,8 +323,8 @@ pub trait Driver: Send { /// `&Device` or `&Device` reference. For instance, drivers may try to perform I/O /// operations to gracefully tear down the device. /// - /// Otherwise, release operations for driver resources should be performed in `Self::drop`. - fn unbind(dev: &Device, this: Pin<&Self>) { + /// Otherwise, release operations for driver resources should be performed in `Drop`. + fn unbind(dev: &Device, this: Pin<&Self::Data>) { let _ = (dev, this); } } diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs index c7a3dcdde3b1..975b22ffe5db 100644 --- a/rust/kernel/platform.rs +++ b/rust/kernel/platform.rs @@ -45,12 +45,12 @@ pub struct Adapter(T); // SAFETY: // - `bindings::platform_driver` is a C type declared as `repr(C)`. -// - `T` is the type of the driver's device private data. +// - `T::Data` is the type of the driver's device private data. // - `struct platform_driver` embeds a `struct device_driver`. // - `DEVICE_DRIVER_OFFSET` is the correct byte offset to the embedded `struct device_driver`. unsafe impl driver::DriverLayout for Adapter { type DriverType = bindings::platform_driver; - type DriverData = T; + type DriverData<'bound> = T::Data; const DEVICE_DRIVER_OFFSET: usize = core::mem::offset_of!(Self::DriverType, driver); } @@ -117,8 +117,8 @@ impl Adapter { // SAFETY: `remove_callback` is only ever called after a successful call to // `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called - // and stored a `Pin>`. - let data = unsafe { pdev.as_ref().drvdata_borrow::() }; + // and stored a `Pin>`. + let data = unsafe { pdev.as_ref().drvdata_borrow::() }; T::unbind(pdev, data); } @@ -192,6 +192,7 @@ macro_rules! module_platform_driver { /// /// impl platform::Driver for MyDriver { /// type IdInfo = (); +/// type Data = Self; /// const OF_ID_TABLE: Option> = Some(&OF_TABLE); /// const ACPI_ID_TABLE: Option> = Some(&ACPI_TABLE); /// @@ -203,7 +204,7 @@ macro_rules! module_platform_driver { /// } /// } ///``` -pub trait Driver: Send { +pub trait Driver { /// The type holding driver private data about each device id supported by the driver. // TODO: Use associated_type_defaults once stabilized: // @@ -212,6 +213,9 @@ pub trait Driver: Send { // ``` type IdInfo: 'static; + /// The type of the driver's bus device private data. + type Data: Send; + /// The table of OF device ids supported by the driver. const OF_ID_TABLE: Option> = None; @@ -225,7 +229,7 @@ pub trait Driver: Send { fn probe( dev: &Device, id_info: Option<&Self::IdInfo>, - ) -> impl PinInit; + ) -> impl PinInit; /// Platform driver unbind. /// @@ -236,8 +240,8 @@ pub trait Driver: Send { /// `&Device` or `&Device` reference. For instance, drivers may try to perform I/O /// operations to gracefully tear down the device. /// - /// Otherwise, release operations for driver resources should be performed in `Self::drop`. - fn unbind(dev: &Device, this: Pin<&Self>) { + /// Otherwise, release operations for driver resources should be performed in `Drop`. + fn unbind(dev: &Device, this: Pin<&Self::Data>) { let _ = (dev, this); } } diff --git a/rust/kernel/usb.rs b/rust/kernel/usb.rs index 3f62da585281..88721970afcb 100644 --- a/rust/kernel/usb.rs +++ b/rust/kernel/usb.rs @@ -36,12 +36,12 @@ pub struct Adapter(T); // SAFETY: // - `bindings::usb_driver` is a C type declared as `repr(C)`. -// - `T` is the type of the driver's device private data. +// - `T::Data` is the type of the driver's device private data. // - `struct usb_driver` embeds a `struct device_driver`. // - `DEVICE_DRIVER_OFFSET` is the correct byte offset to the embedded `struct device_driver`. unsafe impl driver::DriverLayout for Adapter { type DriverType = bindings::usb_driver; - type DriverData = T; + type DriverData<'bound> = T::Data; const DEVICE_DRIVER_OFFSET: usize = core::mem::offset_of!(Self::DriverType, driver); } @@ -109,8 +109,8 @@ impl Adapter { // SAFETY: `disconnect_callback` is only ever called after a successful call to // `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called - // and stored a `Pin>`. - let data = unsafe { dev.drvdata_borrow::() }; + // and stored a `Pin>`. + let data = unsafe { dev.drvdata_borrow::() }; T::disconnect(intf, data); } @@ -287,23 +287,27 @@ macro_rules! usb_device_table { /// /// impl usb::Driver for MyDriver { /// type IdInfo = (); +/// type Data = Self; /// const ID_TABLE: usb::IdTable = &USB_TABLE; /// /// fn probe( /// _interface: &usb::Interface, /// _id: &usb::DeviceId, /// _info: &Self::IdInfo, -/// ) -> impl PinInit { +/// ) -> impl PinInit { /// Err(ENODEV) /// } /// -/// fn disconnect(_interface: &usb::Interface, _data: Pin<&Self>) {} +/// fn disconnect(_interface: &usb::Interface, _data: Pin<&Self::Data>) {} /// } ///``` pub trait Driver { /// The type holding information about each one of the device ids supported by the driver. type IdInfo: 'static; + /// The type of the driver's bus device private data. + type Data: Send; + /// The table of device ids supported by the driver. const ID_TABLE: IdTable; @@ -315,12 +319,12 @@ pub trait Driver { interface: &Interface, id: &DeviceId, id_info: &Self::IdInfo, - ) -> impl PinInit; + ) -> impl PinInit; /// USB driver disconnect. /// /// Called when the USB interface is about to be unbound from this driver. - fn disconnect(interface: &Interface, data: Pin<&Self>); + fn disconnect(interface: &Interface, data: Pin<&Self::Data>); } /// A USB interface. diff --git a/samples/rust/rust_debugfs.rs b/samples/rust/rust_debugfs.rs index 0963efe19f93..478c4f693deb 100644 --- a/samples/rust/rust_debugfs.rs +++ b/samples/rust/rust_debugfs.rs @@ -117,6 +117,7 @@ kernel::acpi_device_table!( impl platform::Driver for RustDebugFs { type IdInfo = (); + type Data = Self; const OF_ID_TABLE: Option> = None; const ACPI_ID_TABLE: Option> = Some(&ACPI_TABLE); diff --git a/samples/rust/rust_dma.rs b/samples/rust/rust_dma.rs index 129bb4b39c04..e583c6b8390a 100644 --- a/samples/rust/rust_dma.rs +++ b/samples/rust/rust_dma.rs @@ -58,6 +58,7 @@ kernel::pci_device_table!( impl pci::Driver for DmaSampleDriver { type IdInfo = (); + type Data = Self; const ID_TABLE: pci::IdTable = &PCI_TABLE; fn probe(pdev: &pci::Device, _info: &Self::IdInfo) -> impl PinInit { diff --git a/samples/rust/rust_driver_auxiliary.rs b/samples/rust/rust_driver_auxiliary.rs index 319ef734c02b..61d5bf2e8c0d 100644 --- a/samples/rust/rust_driver_auxiliary.rs +++ b/samples/rust/rust_driver_auxiliary.rs @@ -31,6 +31,7 @@ kernel::auxiliary_device_table!( impl auxiliary::Driver for AuxiliaryDriver { type IdInfo = (); + type Data = Self; const ID_TABLE: auxiliary::IdTable = &AUX_TABLE; @@ -65,6 +66,7 @@ kernel::pci_device_table!( impl pci::Driver for ParentDriver { type IdInfo = (); + type Data = Self; const ID_TABLE: pci::IdTable = &PCI_TABLE; diff --git a/samples/rust/rust_driver_i2c.rs b/samples/rust/rust_driver_i2c.rs index 6be79f9e9fb5..8269f1798611 100644 --- a/samples/rust/rust_driver_i2c.rs +++ b/samples/rust/rust_driver_i2c.rs @@ -35,6 +35,7 @@ kernel::of_device_table! { impl i2c::Driver for SampleDriver { type IdInfo = u32; + type Data = Self; const ACPI_ID_TABLE: Option> = Some(&ACPI_TABLE); const I2C_ID_TABLE: Option> = Some(&I2C_TABLE); diff --git a/samples/rust/rust_driver_pci.rs b/samples/rust/rust_driver_pci.rs index 47d3e84fab63..f43c6a660b39 100644 --- a/samples/rust/rust_driver_pci.rs +++ b/samples/rust/rust_driver_pci.rs @@ -140,6 +140,7 @@ impl SampleDriver { impl pci::Driver for SampleDriver { type IdInfo = TestIndex; + type Data = Self; const ID_TABLE: pci::IdTable = &PCI_TABLE; diff --git a/samples/rust/rust_driver_platform.rs b/samples/rust/rust_driver_platform.rs index f2229d176fb9..6505902f8200 100644 --- a/samples/rust/rust_driver_platform.rs +++ b/samples/rust/rust_driver_platform.rs @@ -101,6 +101,7 @@ kernel::acpi_device_table!( impl platform::Driver for SampleDriver { type IdInfo = Info; + type Data = Self; const OF_ID_TABLE: Option> = Some(&OF_TABLE); const ACPI_ID_TABLE: Option> = Some(&ACPI_TABLE); diff --git a/samples/rust/rust_driver_usb.rs b/samples/rust/rust_driver_usb.rs index ab72e99e1274..5942e4b01fd8 100644 --- a/samples/rust/rust_driver_usb.rs +++ b/samples/rust/rust_driver_usb.rs @@ -26,6 +26,7 @@ kernel::usb_device_table!( impl usb::Driver for SampleDriver { type IdInfo = (); + type Data = Self; const ID_TABLE: usb::IdTable = &USB_TABLE; fn probe( diff --git a/samples/rust/rust_i2c_client.rs b/samples/rust/rust_i2c_client.rs index 8d2c12e535b0..5956b647294d 100644 --- a/samples/rust/rust_i2c_client.rs +++ b/samples/rust/rust_i2c_client.rs @@ -106,6 +106,7 @@ const BOARD_INFO: i2c::I2cBoardInfo = impl platform::Driver for SampleDriver { type IdInfo = (); + type Data = Self; const OF_ID_TABLE: Option> = Some(&OF_TABLE); const ACPI_ID_TABLE: Option> = Some(&ACPI_TABLE); diff --git a/samples/rust/rust_soc.rs b/samples/rust/rust_soc.rs index 8079c1c48416..a5e72582f4a2 100644 --- a/samples/rust/rust_soc.rs +++ b/samples/rust/rust_soc.rs @@ -37,6 +37,7 @@ kernel::acpi_device_table!( impl platform::Driver for SampleSocDriver { type IdInfo = (); + type Data = Self; const OF_ID_TABLE: Option> = Some(&OF_TABLE); const ACPI_ID_TABLE: Option> = Some(&ACPI_TABLE); -- cgit v1.2.3 From be31fcf5af751815457102575b816a2bd31b4562 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Mon, 25 May 2026 22:20:52 +0200 Subject: rust: driver core: drop drvdata before devres release Move the post_unbind_rust callback before devres_release_all() in device_unbind_cleanup(). With drvdata() removed, the driver's bus device private data is only accessible by the owning driver itself. It is hence safe to drop the driver's bus device private data before devres actions are released. This reordering is the key enabler for Higher-Ranked Lifetime Types (HRT) in Rust device drivers -- it allows driver structs to hold direct references to devres-managed resources, because the bus device private data (and with it all such references) is guaranteed to be dropped while the underlying devres resources are still alive. Without this change, devres resources would be freed first, leaving the driver's bus device private data with dangling references during its destructor. Reviewed-by: Alexandre Courbot Reviewed-by: Gary Guo Reviewed-by: Greg Kroah-Hartman Link: https://patch.msgid.link/20260525202921.124698-6-dakr@kernel.org Signed-off-by: Danilo Krummrich --- drivers/base/dd.c | 2 +- include/linux/device/driver.h | 4 ++-- rust/kernel/driver.rs | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 1dc1e3528043..73801b40a416 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -595,9 +595,9 @@ static DEVICE_ATTR_RW(state_synced); static void device_unbind_cleanup(struct device *dev) { - devres_release_all(dev); if (dev->driver->p_cb.post_unbind_rust) dev->driver->p_cb.post_unbind_rust(dev); + devres_release_all(dev); arch_teardown_dma_ops(dev); kfree(dev->dma_range_map); dev->dma_range_map = NULL; diff --git a/include/linux/device/driver.h b/include/linux/device/driver.h index bbc67ec513ed..38e9a4679447 100644 --- a/include/linux/device/driver.h +++ b/include/linux/device/driver.h @@ -123,8 +123,8 @@ struct device_driver { struct driver_private *p; struct { /* - * Called after remove() and after all devres entries have been - * processed. This is a Rust only callback. + * Called after remove() but before devres entries are released. + * This is a Rust only callback. */ void (*post_unbind_rust)(struct device *dev); } p_cb; diff --git a/rust/kernel/driver.rs b/rust/kernel/driver.rs index 5fd1cfd64e93..a95dafaa9d68 100644 --- a/rust/kernel/driver.rs +++ b/rust/kernel/driver.rs @@ -193,8 +193,8 @@ impl Registration { // INVARIANT: `dev` is valid for the duration of the `post_unbind_callback()`. let dev = unsafe { &*dev.cast::>() }; - // `remove()` and all devres callbacks have been completed at this point, hence drop the - // driver's device private data. + // `remove()` has been completed at this point; devres resources are still valid and will + // be released after the driver's bus device private data is dropped. // // SAFETY: By the safety requirements of the `Driver` trait, `T::DriverData` is the // driver's bus device private data type. -- cgit v1.2.3 From 0b9a29c3a4e2a1f0d0b73ad4c4a4175212bacf21 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Mon, 25 May 2026 22:20:53 +0200 Subject: rust: pci: implement Sync for Device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement Sync for Device in addition to Device. Device uses the same underlying struct pci_dev as Device; Bound is a zero-sized type-state marker that does not affect thread safety. This is needed for drivers to store &'bound pci::Device in their private data while remaining Send. Reviewed-by: Alexandre Courbot Reviewed-by: Gary Guo Reviewed-by: Greg Kroah-Hartman Acked-by: Uwe Kleine-König Tested-by: Dirk Behme Link: https://patch.msgid.link/20260525202921.124698-7-dakr@kernel.org Signed-off-by: Danilo Krummrich --- rust/kernel/pci.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs index c743f2abb62f..d214a861375d 100644 --- a/rust/kernel/pci.rs +++ b/rust/kernel/pci.rs @@ -528,3 +528,7 @@ unsafe impl Send for Device {} // SAFETY: `Device` can be shared among threads because all methods of `Device` // (i.e. `Device) are thread safe. unsafe impl Sync for Device {} + +// SAFETY: Same as `Device` -- the underlying `struct pci_dev` is the same; +// `Bound` is a zero-sized type-state marker that does not affect thread safety. +unsafe impl Sync for Device {} -- cgit v1.2.3 From a89111c00b68ff78cae7981a67601571e365d13b Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Mon, 25 May 2026 22:20:54 +0200 Subject: rust: platform: implement Sync for Device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement Sync for Device in addition to Device. Device uses the same underlying struct platform_device as Device; Bound is a zero-sized type-state marker that does not affect thread safety. This is needed for drivers to store &'bound platform::Device in their private data while remaining Send. Reviewed-by: Alexandre Courbot Reviewed-by: Gary Guo Reviewed-by: Greg Kroah-Hartman Acked-by: Uwe Kleine-König Tested-by: Dirk Behme Link: https://patch.msgid.link/20260525202921.124698-8-dakr@kernel.org Signed-off-by: Danilo Krummrich --- rust/kernel/platform.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs index 975b22ffe5db..106a5ed57ea6 100644 --- a/rust/kernel/platform.rs +++ b/rust/kernel/platform.rs @@ -565,3 +565,7 @@ unsafe impl Send for Device {} // SAFETY: `Device` can be shared among threads because all methods of `Device` // (i.e. `Device) are thread safe. unsafe impl Sync for Device {} + +// SAFETY: Same as `Device` -- the underlying `struct platform_device` is the same; +// `Bound` is a zero-sized type-state marker that does not affect thread safety. +unsafe impl Sync for Device {} -- cgit v1.2.3 From 5bbcefe8db74fe07681a9ded7c13362e3eaae54a Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Mon, 25 May 2026 22:20:55 +0200 Subject: rust: auxiliary: implement Sync for Device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement Sync for Device in addition to Device. Device uses the same underlying struct auxiliary_device as Device; Bound is a zero-sized type-state marker that does not affect thread safety. This is needed for drivers to store &'bound auxiliary::Device in their private data while remaining Send. Reviewed-by: Alexandre Courbot Reviewed-by: Gary Guo Reviewed-by: Greg Kroah-Hartman Acked-by: Uwe Kleine-König Tested-by: Dirk Behme Link: https://patch.msgid.link/20260525202921.124698-9-dakr@kernel.org Signed-off-by: Danilo Krummrich --- rust/kernel/auxiliary.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rust/kernel/auxiliary.rs b/rust/kernel/auxiliary.rs index 4e83f9e27d78..df2c97423dcc 100644 --- a/rust/kernel/auxiliary.rs +++ b/rust/kernel/auxiliary.rs @@ -369,6 +369,10 @@ unsafe impl Send for Device {} // (i.e. `Device) are thread safe. unsafe impl Sync for Device {} +// SAFETY: Same as `Device` -- the underlying `struct auxiliary_device` is the same; +// `Bound` is a zero-sized type-state marker that does not affect thread safety. +unsafe impl Sync for Device {} + /// Wrapper that stores a [`TypeId`] alongside the registration data for runtime type checking. #[repr(C)] #[pin_data] -- cgit v1.2.3 From 3bb1655192aeed68b761891eabdc97d9f2f7fc38 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Mon, 25 May 2026 22:20:56 +0200 Subject: rust: usb: implement Sync for Device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement Sync for Device in addition to Device. Device uses the same underlying struct usb_device as Device; Bound is a zero-sized type-state marker that does not affect thread safety. This is needed for drivers to store &'bound usb::Device in their private data while remaining Send. Reviewed-by: Alexandre Courbot Reviewed-by: Gary Guo Reviewed-by: Greg Kroah-Hartman Acked-by: Uwe Kleine-König Tested-by: Dirk Behme Link: https://patch.msgid.link/20260525202921.124698-10-dakr@kernel.org Signed-off-by: Danilo Krummrich --- rust/kernel/usb.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rust/kernel/usb.rs b/rust/kernel/usb.rs index 88721970afcb..6c917d8fa883 100644 --- a/rust/kernel/usb.rs +++ b/rust/kernel/usb.rs @@ -468,6 +468,10 @@ unsafe impl Send for Device {} // allow any mutation through a shared reference. unsafe impl Sync for Device {} +// SAFETY: Same as `Device` -- the underlying `struct usb_device` is the same; +// `Bound` is a zero-sized type-state marker that does not affect thread safety. +unsafe impl Sync for Device {} + /// Declares a kernel module that exposes a single USB driver. /// /// # Examples -- cgit v1.2.3 From de12e48a1be3e9edc0f8bc6e37bad8f7b6f32d54 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Mon, 25 May 2026 22:20:57 +0200 Subject: rust: device: implement Sync for Device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement Sync for Device in addition to Device. Device uses the same underlying struct device as Device; Bound is a zero-sized type-state marker that does not affect thread safety. This is needed for types that hold &'bound Device, such as io::mem::IoMem, to be Send. Reviewed-by: Alexandre Courbot Reviewed-by: Gary Guo Reviewed-by: Greg Kroah-Hartman Acked-by: Uwe Kleine-König Tested-by: Dirk Behme Link: https://patch.msgid.link/20260525202921.124698-11-dakr@kernel.org Signed-off-by: Danilo Krummrich --- rust/kernel/device.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rust/kernel/device.rs b/rust/kernel/device.rs index 5df8fa108a52..c4486f4b8c40 100644 --- a/rust/kernel/device.rs +++ b/rust/kernel/device.rs @@ -467,6 +467,10 @@ unsafe impl Send for Device {} // synchronization in `struct device`. unsafe impl Sync for Device {} +// SAFETY: Same as `Device` -- the underlying `struct device` is the same; `Bound` is a +// zero-sized type-state marker that does not affect thread safety. +unsafe impl Sync for Device {} + /// Marker trait for the context or scope of a bus specific device. /// /// [`DeviceContext`] is a marker trait for types representing the context of a bus specific -- cgit v1.2.3 From 24799831d631239ff21ea1bf7feee832df48b81f Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Mon, 25 May 2026 22:20:58 +0200 Subject: rust: device: make Core and CoreInternal lifetime-parameterized Device references in probe callbacks are scoped to the callback, not the full binding duration. Add a lifetime parameter to Core and CoreInternal to accurately represent this in the type system. Suggested-by: Gary Guo Reviewed-by: Greg Kroah-Hartman Reviewed-by: Alexandre Courbot Reviewed-by: Eliot Courtney Reviewed-by: Gary Guo Link: https://patch.msgid.link/20260525202921.124698-12-dakr@kernel.org Signed-off-by: Danilo Krummrich --- drivers/cpufreq/rcpufreq_dt.rs | 2 +- drivers/gpu/drm/nova/driver.rs | 5 +++- drivers/gpu/drm/tyr/driver.rs | 2 +- drivers/gpu/nova-core/driver.rs | 4 +-- drivers/gpu/nova-core/gpu.rs | 2 +- drivers/pwm/pwm_th1520.rs | 2 +- rust/kernel/auxiliary.rs | 12 +++++---- rust/kernel/cpufreq.rs | 2 +- rust/kernel/device.rs | 49 +++++++++++++++++++++++++++-------- rust/kernel/devres.rs | 2 +- rust/kernel/dma.rs | 2 +- rust/kernel/driver.rs | 6 ++--- rust/kernel/i2c.rs | 16 ++++++------ rust/kernel/io/mem.rs | 4 +-- rust/kernel/pci.rs | 20 +++++++------- rust/kernel/pci/id.rs | 2 +- rust/kernel/platform.rs | 12 ++++----- rust/kernel/usb.rs | 16 ++++++------ samples/rust/rust_debugfs.rs | 4 +-- samples/rust/rust_dma.rs | 2 +- samples/rust/rust_driver_auxiliary.rs | 7 +++-- samples/rust/rust_driver_i2c.rs | 6 ++--- samples/rust/rust_driver_pci.rs | 4 +-- samples/rust/rust_driver_platform.rs | 2 +- samples/rust/rust_driver_usb.rs | 8 +++--- samples/rust/rust_i2c_client.rs | 4 +-- samples/rust/rust_soc.rs | 2 +- 27 files changed, 118 insertions(+), 81 deletions(-) diff --git a/drivers/cpufreq/rcpufreq_dt.rs b/drivers/cpufreq/rcpufreq_dt.rs index b7eeb2730eb0..5e0b224f6699 100644 --- a/drivers/cpufreq/rcpufreq_dt.rs +++ b/drivers/cpufreq/rcpufreq_dt.rs @@ -205,7 +205,7 @@ impl platform::Driver for CPUFreqDTDriver { const OF_ID_TABLE: Option> = Some(&OF_TABLE); fn probe( - pdev: &platform::Device, + pdev: &platform::Device>, _id_info: Option<&Self::IdInfo>, ) -> impl PinInit { cpufreq::Registration::::new_foreign_owned(pdev.as_ref())?; diff --git a/drivers/gpu/drm/nova/driver.rs b/drivers/gpu/drm/nova/driver.rs index 08136ec0bccb..7605348af994 100644 --- a/drivers/gpu/drm/nova/driver.rs +++ b/drivers/gpu/drm/nova/driver.rs @@ -54,7 +54,10 @@ impl auxiliary::Driver for NovaDriver { type Data = Self; const ID_TABLE: auxiliary::IdTable = &AUX_TABLE; - fn probe(adev: &auxiliary::Device, _info: &Self::IdInfo) -> impl PinInit { + fn probe( + adev: &auxiliary::Device>, + _info: &Self::IdInfo, + ) -> impl PinInit { let data = try_pin_init!(NovaData { adev: adev.into() }); let drm = drm::Device::::new(adev.as_ref(), data)?; diff --git a/drivers/gpu/drm/tyr/driver.rs b/drivers/gpu/drm/tyr/driver.rs index c81bf217743d..001727f44fc8 100644 --- a/drivers/gpu/drm/tyr/driver.rs +++ b/drivers/gpu/drm/tyr/driver.rs @@ -95,7 +95,7 @@ impl platform::Driver for TyrPlatformDriverData { const OF_ID_TABLE: Option> = Some(&OF_TABLE); fn probe( - pdev: &platform::Device, + pdev: &platform::Device>, _info: Option<&Self::IdInfo>, ) -> impl PinInit { let core_clk = Clk::get(pdev.as_ref(), Some(c"core"))?; diff --git a/drivers/gpu/nova-core/driver.rs b/drivers/gpu/nova-core/driver.rs index 699e27046c93..13c5ff15e87f 100644 --- a/drivers/gpu/nova-core/driver.rs +++ b/drivers/gpu/nova-core/driver.rs @@ -77,7 +77,7 @@ impl pci::Driver for NovaCore { type Data = Self; const ID_TABLE: pci::IdTable = &PCI_TABLE; - fn probe(pdev: &pci::Device, _info: &Self::IdInfo) -> impl PinInit { + fn probe(pdev: &pci::Device>, _info: &Self::IdInfo) -> impl PinInit { pin_init::pin_init_scope(move || { dev_dbg!(pdev, "Probe Nova Core GPU driver.\n"); @@ -109,7 +109,7 @@ impl pci::Driver for NovaCore { }) } - fn unbind(pdev: &pci::Device, this: Pin<&Self>) { + fn unbind(pdev: &pci::Device>, this: Pin<&Self>) { this.gpu.unbind(pdev.as_ref()); } } diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs index 0f6fe9a1b955..4ffb506342a9 100644 --- a/drivers/gpu/nova-core/gpu.rs +++ b/drivers/gpu/nova-core/gpu.rs @@ -278,7 +278,7 @@ impl Gpu { /// Called when the corresponding [`Device`](device::Device) is unbound. /// /// Note: This method must only be called from `Driver::unbind`. - pub(crate) fn unbind(&self, dev: &device::Device) { + pub(crate) fn unbind(&self, dev: &device::Device>) { kernel::warn_on!(self .bar .access(dev) diff --git a/drivers/pwm/pwm_th1520.rs b/drivers/pwm/pwm_th1520.rs index 07795910a0b5..df83a4a9a507 100644 --- a/drivers/pwm/pwm_th1520.rs +++ b/drivers/pwm/pwm_th1520.rs @@ -320,7 +320,7 @@ impl platform::Driver for Th1520PwmPlatformDriver { const OF_ID_TABLE: Option> = Some(&OF_TABLE); fn probe( - pdev: &platform::Device, + pdev: &platform::Device>, _id_info: Option<&Self::IdInfo>, ) -> impl PinInit { let dev = pdev.as_ref(); diff --git a/rust/kernel/auxiliary.rs b/rust/kernel/auxiliary.rs index df2c97423dcc..6d504b0933d5 100644 --- a/rust/kernel/auxiliary.rs +++ b/rust/kernel/auxiliary.rs @@ -87,7 +87,7 @@ impl Adapter { // `struct auxiliary_device`. // // INVARIANT: `adev` is valid for the duration of `probe_callback()`. - let adev = unsafe { &*adev.cast::>() }; + let adev = unsafe { &*adev.cast::>>() }; // SAFETY: `DeviceId` is a `#[repr(transparent)`] wrapper of `struct auxiliary_device_id` // and does not add additional invariants, so it's safe to transmute. @@ -107,7 +107,7 @@ impl Adapter { // `struct auxiliary_device`. // // INVARIANT: `adev` is valid for the duration of `remove_callback()`. - let adev = unsafe { &*adev.cast::>() }; + let adev = unsafe { &*adev.cast::>>() }; // SAFETY: `remove_callback` is only ever called after a successful call to // `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called @@ -211,8 +211,10 @@ pub trait Driver { /// Auxiliary driver probe. /// /// Called when an auxiliary device is matches a corresponding driver. - fn probe(dev: &Device, id_info: &Self::IdInfo) - -> impl PinInit; + fn probe( + dev: &Device>, + id_info: &Self::IdInfo, + ) -> impl PinInit; /// Auxiliary driver unbind. /// @@ -224,7 +226,7 @@ pub trait Driver { /// operations to gracefully tear down the device. /// /// Otherwise, release operations for driver resources should be performed in `Drop`. - fn unbind(dev: &Device, this: Pin<&Self::Data>) { + fn unbind(dev: &Device>, this: Pin<&Self::Data>) { let _ = (dev, this); } } diff --git a/rust/kernel/cpufreq.rs b/rust/kernel/cpufreq.rs index 50dd2a2c3e81..0df518fa1d77 100644 --- a/rust/kernel/cpufreq.rs +++ b/rust/kernel/cpufreq.rs @@ -892,7 +892,7 @@ pub trait Driver { /// const OF_ID_TABLE: Option> = None; /// /// fn probe( -/// pdev: &platform::Device, +/// pdev: &platform::Device>, /// _id_info: Option<&Self::IdInfo>, /// ) -> impl PinInit { /// cpufreq::Registration::::new_foreign_owned(pdev.as_ref())?; diff --git a/rust/kernel/device.rs b/rust/kernel/device.rs index c4486f4b8c40..645afc49a27d 100644 --- a/rust/kernel/device.rs +++ b/rust/kernel/device.rs @@ -201,7 +201,7 @@ impl Device { } } -impl Device { +impl<'a> Device> { /// Store a pointer to the bound driver's private data. pub fn set_drvdata(&self, data: impl PinInit) -> Result { let data = KBox::pin_init(data, GFP_KERNEL)?; @@ -511,7 +511,7 @@ pub struct Normal; /// callback it appears in. It is intended to be used for synchronization purposes. Bus device /// implementations can implement methods for [`Device`], such that they can only be called /// from bus callbacks. -pub struct Core; +pub struct Core<'a>(PhantomData<&'a ()>); /// Semantically the same as [`Core`], but reserved for internal usage of the corresponding bus /// abstraction. @@ -522,7 +522,7 @@ pub struct Core; /// /// This context mainly exists to share generic [`Device`] infrastructure that should only be called /// from bus callbacks with bus abstractions, but without making them accessible for drivers. -pub struct CoreInternal; +pub struct CoreInternal<'a>(PhantomData<&'a ()>); /// The [`Bound`] context is the [`DeviceContext`] of a bus specific device when it is guaranteed to /// be bound to a driver. @@ -546,14 +546,14 @@ mod private { pub trait Sealed {} impl Sealed for super::Bound {} - impl Sealed for super::Core {} - impl Sealed for super::CoreInternal {} + impl<'a> Sealed for super::Core<'a> {} + impl<'a> Sealed for super::CoreInternal<'a> {} impl Sealed for super::Normal {} } impl DeviceContext for Bound {} -impl DeviceContext for Core {} -impl DeviceContext for CoreInternal {} +impl<'a> DeviceContext for Core<'a> {} +impl<'a> DeviceContext for CoreInternal<'a> {} impl DeviceContext for Normal {} impl AsRef> for Device { @@ -603,6 +603,22 @@ pub unsafe trait AsBusDevice: AsRef> { #[doc(hidden)] #[macro_export] macro_rules! __impl_device_context_deref { + (unsafe { $device:ident, <$lt:lifetime> $src:ty => $dst:ty }) => { + impl<$lt> ::core::ops::Deref for $device<$src> { + type Target = $device<$dst>; + + fn deref(&self) -> &Self::Target { + let ptr: *const Self = self; + + // CAST: `$device<$src>` and `$device<$dst>` transparently wrap the same type by the + // safety requirement of the macro. + let ptr = ptr.cast::(); + + // SAFETY: `ptr` was derived from `&self`. + unsafe { &*ptr } + } + } + }; (unsafe { $device:ident, $src:ty => $dst:ty }) => { impl ::core::ops::Deref for $device<$src> { type Target = $device<$dst>; @@ -635,14 +651,14 @@ macro_rules! impl_device_context_deref { // `__impl_device_context_deref!`. ::kernel::__impl_device_context_deref!(unsafe { $device, - $crate::device::CoreInternal => $crate::device::Core + <'a> $crate::device::CoreInternal<'a> => $crate::device::Core<'a> }); // SAFETY: This macro has the exact same safety requirement as // `__impl_device_context_deref!`. ::kernel::__impl_device_context_deref!(unsafe { $device, - $crate::device::Core => $crate::device::Bound + <'a> $crate::device::Core<'a> => $crate::device::Bound }); // SAFETY: This macro has the exact same safety requirement as @@ -657,6 +673,13 @@ macro_rules! impl_device_context_deref { #[doc(hidden)] #[macro_export] macro_rules! __impl_device_context_into_aref { + (<$lt:lifetime> $src:ty, $device:tt) => { + impl<$lt> ::core::convert::From<&$device<$src>> for $crate::sync::aref::ARef<$device> { + fn from(dev: &$device<$src>) -> Self { + (&**dev).into() + } + } + }; ($src:ty, $device:tt) => { impl ::core::convert::From<&$device<$src>> for $crate::sync::aref::ARef<$device> { fn from(dev: &$device<$src>) -> Self { @@ -671,8 +694,12 @@ macro_rules! __impl_device_context_into_aref { #[macro_export] macro_rules! impl_device_context_into_aref { ($device:tt) => { - ::kernel::__impl_device_context_into_aref!($crate::device::CoreInternal, $device); - ::kernel::__impl_device_context_into_aref!($crate::device::Core, $device); + ::kernel::__impl_device_context_into_aref!( + <'a> $crate::device::CoreInternal<'a>, $device + ); + ::kernel::__impl_device_context_into_aref!( + <'a> $crate::device::Core<'a>, $device + ); ::kernel::__impl_device_context_into_aref!($crate::device::Bound, $device); }; } diff --git a/rust/kernel/devres.rs b/rust/kernel/devres.rs index 9e5f93aed20c..fd4633f977f6 100644 --- a/rust/kernel/devres.rs +++ b/rust/kernel/devres.rs @@ -304,7 +304,7 @@ impl Devres { /// pci, // /// }; /// - /// fn from_core(dev: &pci::Device, devres: Devres>) -> Result { + /// fn from_core(dev: &pci::Device>, devres: Devres>) -> Result { /// let bar = devres.access(dev.as_ref())?; /// /// let _ = bar.read32(0x0); diff --git a/rust/kernel/dma.rs b/rust/kernel/dma.rs index 4995ee5dc689..8f97916e0688 100644 --- a/rust/kernel/dma.rs +++ b/rust/kernel/dma.rs @@ -47,7 +47,7 @@ pub type DmaAddress = bindings::dma_addr_t; /// where the underlying bus is DMA capable, such as: #[cfg_attr(CONFIG_PCI, doc = "* [`pci::Device`](kernel::pci::Device)")] /// * [`platform::Device`](::kernel::platform::Device) -pub trait Device: AsRef> { +pub trait Device<'a>: AsRef>> { /// Set up the device's DMA streaming addressing capabilities. /// /// This method is usually called once from `probe()` as soon as the device capabilities are diff --git a/rust/kernel/driver.rs b/rust/kernel/driver.rs index a95dafaa9d68..558fdef4a1c6 100644 --- a/rust/kernel/driver.rs +++ b/rust/kernel/driver.rs @@ -27,11 +27,11 @@ //! const ACPI_ID_TABLE: Option> = None; //! //! /// Driver probe. -//! fn probe(dev: &Device, id_info: &Self::IdInfo) +//! fn probe(dev: &Device>, id_info: &Self::IdInfo) //! -> impl PinInit; //! //! /// Driver unbind (optional). -//! fn unbind(dev: &Device, this: Pin<&Self::Data>) { +//! fn unbind(dev: &Device>, this: Pin<&Self::Data>) { //! let _ = (dev, this); //! } //! } @@ -191,7 +191,7 @@ impl Registration { // a `struct device`. // // INVARIANT: `dev` is valid for the duration of the `post_unbind_callback()`. - let dev = unsafe { &*dev.cast::>() }; + let dev = unsafe { &*dev.cast::>>() }; // `remove()` has been completed at this point; devres resources are still valid and will // be released after the driver's bus device private data is dropped. diff --git a/rust/kernel/i2c.rs b/rust/kernel/i2c.rs index bfd081518615..50feade0fb58 100644 --- a/rust/kernel/i2c.rs +++ b/rust/kernel/i2c.rs @@ -157,7 +157,7 @@ impl Adapter { // `struct i2c_client`. // // INVARIANT: `idev` is valid for the duration of `probe_callback()`. - let idev = unsafe { &*idev.cast::>() }; + let idev = unsafe { &*idev.cast::>>() }; let info = Self::i2c_id_info(idev).or_else(|| ::id_info(idev.as_ref())); @@ -172,7 +172,7 @@ impl Adapter { extern "C" fn remove_callback(idev: *mut bindings::i2c_client) { // SAFETY: `idev` is a valid pointer to a `struct i2c_client`. - let idev = unsafe { &*idev.cast::>() }; + let idev = unsafe { &*idev.cast::>>() }; // SAFETY: `remove_callback` is only ever called after a successful call to // `probe_callback`, hence it's guaranteed that `I2cClient::set_drvdata()` has been called @@ -184,7 +184,7 @@ impl Adapter { extern "C" fn shutdown_callback(idev: *mut bindings::i2c_client) { // SAFETY: `shutdown_callback` is only ever called for a valid `idev` - let idev = unsafe { &*idev.cast::>() }; + let idev = unsafe { &*idev.cast::>>() }; // SAFETY: `shutdown_callback` is only ever called after a successful call to // `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called @@ -300,13 +300,13 @@ macro_rules! module_i2c_driver { /// const ACPI_ID_TABLE: Option> = Some(&ACPI_TABLE); /// /// fn probe( -/// _idev: &i2c::I2cClient, +/// _idev: &i2c::I2cClient>, /// _id_info: Option<&Self::IdInfo>, /// ) -> impl PinInit { /// Err(ENODEV) /// } /// -/// fn shutdown(_idev: &i2c::I2cClient, this: Pin<&Self::Data>) { +/// fn shutdown(_idev: &i2c::I2cClient>, this: Pin<&Self::Data>) { /// } /// } ///``` @@ -336,7 +336,7 @@ pub trait Driver { /// Called when a new i2c client is added or discovered. /// Implementers should attempt to initialize the client here. fn probe( - dev: &I2cClient, + dev: &I2cClient>, id_info: Option<&Self::IdInfo>, ) -> impl PinInit; @@ -351,7 +351,7 @@ pub trait Driver { /// This callback is distinct from final resource cleanup, as the driver instance remains valid /// after it returns. Any deallocation or teardown of driver-owned resources should instead be /// handled in `Drop`. - fn shutdown(dev: &I2cClient, this: Pin<&Self::Data>) { + fn shutdown(dev: &I2cClient>, this: Pin<&Self::Data>) { let _ = (dev, this); } @@ -365,7 +365,7 @@ pub trait Driver { /// operations to gracefully tear down the device. /// /// Otherwise, release operations for driver resources should be performed in `Drop`. - fn unbind(dev: &I2cClient, this: Pin<&Self::Data>) { + fn unbind(dev: &I2cClient>, this: Pin<&Self::Data>) { let _ = (dev, this); } } diff --git a/rust/kernel/io/mem.rs b/rust/kernel/io/mem.rs index e136b676d372..03d8745b5e1d 100644 --- a/rust/kernel/io/mem.rs +++ b/rust/kernel/io/mem.rs @@ -65,7 +65,7 @@ impl<'a> IoRequest<'a> { /// # type Data = Self; /// /// fn probe( - /// pdev: &platform::Device, + /// pdev: &platform::Device>, /// info: Option<&Self::IdInfo>, /// ) -> impl PinInit { /// let offset = 0; // Some offset. @@ -130,7 +130,7 @@ impl<'a> IoRequest<'a> { /// # type Data = Self; /// /// fn probe( - /// pdev: &platform::Device, + /// pdev: &platform::Device>, /// info: Option<&Self::IdInfo>, /// ) -> impl PinInit { /// let offset = 0; // Some offset. diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs index d214a861375d..314ad9fefdb0 100644 --- a/rust/kernel/pci.rs +++ b/rust/kernel/pci.rs @@ -105,7 +105,7 @@ impl Adapter { // `struct pci_dev`. // // INVARIANT: `pdev` is valid for the duration of `probe_callback()`. - let pdev = unsafe { &*pdev.cast::>() }; + let pdev = unsafe { &*pdev.cast::>>() }; // SAFETY: `DeviceId` is a `#[repr(transparent)]` wrapper of `struct pci_device_id` and // does not add additional invariants, so it's safe to transmute. @@ -125,7 +125,7 @@ impl Adapter { // `struct pci_dev`. // // INVARIANT: `pdev` is valid for the duration of `remove_callback()`. - let pdev = unsafe { &*pdev.cast::>() }; + let pdev = unsafe { &*pdev.cast::>>() }; // SAFETY: `remove_callback` is only ever called after a successful call to // `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called @@ -283,7 +283,7 @@ macro_rules! pci_device_table { /// const ID_TABLE: pci::IdTable = &PCI_TABLE; /// /// fn probe( -/// _pdev: &pci::Device, +/// _pdev: &pci::Device>, /// _id_info: &Self::IdInfo, /// ) -> impl PinInit { /// Err(ENODEV) @@ -311,8 +311,10 @@ pub trait Driver { /// /// Called when a new pci device is added or discovered. Implementers should /// attempt to initialize the device here. - fn probe(dev: &Device, id_info: &Self::IdInfo) - -> impl PinInit; + fn probe( + dev: &Device>, + id_info: &Self::IdInfo, + ) -> impl PinInit; /// PCI driver unbind. /// @@ -324,7 +326,7 @@ pub trait Driver { /// operations to gracefully tear down the device. /// /// Otherwise, release operations for driver resources should be performed in `Drop`. - fn unbind(dev: &Device, this: Pin<&Self::Data>) { + fn unbind(dev: &Device>, this: Pin<&Self::Data>) { let _ = (dev, this); } } @@ -359,7 +361,7 @@ impl Device { /// /// ``` /// # use kernel::{device::Core, pci::{self, Vendor}, prelude::*}; - /// fn log_device_info(pdev: &pci::Device) -> Result { + /// fn log_device_info(pdev: &pci::Device>) -> Result { /// // Get an instance of `Vendor`. /// let vendor = pdev.vendor_id(); /// dev_info!( @@ -450,7 +452,7 @@ impl Device { } } -impl Device { +impl<'a> Device> { /// Enable memory resources for this device. pub fn enable_device_mem(&self) -> Result { // SAFETY: `self.as_raw` is guaranteed to be a pointer to a valid `struct pci_dev`. @@ -476,7 +478,7 @@ unsafe impl device::AsBusDevice for Device kernel::impl_device_context_deref!(unsafe { Device }); kernel::impl_device_context_into_aref!(Device); -impl crate::dma::Device for Device {} +impl<'a> crate::dma::Device<'a> for Device> {} // SAFETY: Instances of `Device` are always reference-counted. unsafe impl crate::sync::aref::AlwaysRefCounted for Device { diff --git a/rust/kernel/pci/id.rs b/rust/kernel/pci/id.rs index 50005d176561..dbaf301666e7 100644 --- a/rust/kernel/pci/id.rs +++ b/rust/kernel/pci/id.rs @@ -19,7 +19,7 @@ use crate::{ /// /// ``` /// # use kernel::{device::Core, pci::{self, Class}, prelude::*}; -/// fn probe_device(pdev: &pci::Device) -> Result { +/// fn probe_device(pdev: &pci::Device>) -> Result { /// let pci_class = pdev.pci_class(); /// dev_info!( /// pdev, diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs index 106a5ed57ea6..257b7084338c 100644 --- a/rust/kernel/platform.rs +++ b/rust/kernel/platform.rs @@ -97,7 +97,7 @@ impl Adapter { // `struct platform_device`. // // INVARIANT: `pdev` is valid for the duration of `probe_callback()`. - let pdev = unsafe { &*pdev.cast::>() }; + let pdev = unsafe { &*pdev.cast::>>() }; let info = ::id_info(pdev.as_ref()); from_result(|| { @@ -113,7 +113,7 @@ impl Adapter { // `struct platform_device`. // // INVARIANT: `pdev` is valid for the duration of `remove_callback()`. - let pdev = unsafe { &*pdev.cast::>() }; + let pdev = unsafe { &*pdev.cast::>>() }; // SAFETY: `remove_callback` is only ever called after a successful call to // `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called @@ -197,7 +197,7 @@ macro_rules! module_platform_driver { /// const ACPI_ID_TABLE: Option> = Some(&ACPI_TABLE); /// /// fn probe( -/// _pdev: &platform::Device, +/// _pdev: &platform::Device>, /// _id_info: Option<&Self::IdInfo>, /// ) -> impl PinInit { /// Err(ENODEV) @@ -227,7 +227,7 @@ pub trait Driver { /// Called when a new platform device is added or discovered. /// Implementers should attempt to initialize the device here. fn probe( - dev: &Device, + dev: &Device>, id_info: Option<&Self::IdInfo>, ) -> impl PinInit; @@ -241,7 +241,7 @@ pub trait Driver { /// operations to gracefully tear down the device. /// /// Otherwise, release operations for driver resources should be performed in `Drop`. - fn unbind(dev: &Device, this: Pin<&Self::Data>) { + fn unbind(dev: &Device>, this: Pin<&Self::Data>) { let _ = (dev, this); } } @@ -513,7 +513,7 @@ impl Device { kernel::impl_device_context_deref!(unsafe { Device }); kernel::impl_device_context_into_aref!(Device); -impl crate::dma::Device for Device {} +impl<'a> crate::dma::Device<'a> for Device> {} // SAFETY: Instances of `Device` are always reference-counted. unsafe impl crate::sync::aref::AlwaysRefCounted for Device { diff --git a/rust/kernel/usb.rs b/rust/kernel/usb.rs index 6c917d8fa883..1dbb8387b463 100644 --- a/rust/kernel/usb.rs +++ b/rust/kernel/usb.rs @@ -82,7 +82,7 @@ impl Adapter { // `struct usb_interface` and `struct usb_device_id`. // // INVARIANT: `intf` is valid for the duration of `probe_callback()`. - let intf = unsafe { &*intf.cast::>() }; + let intf = unsafe { &*intf.cast::>>() }; from_result(|| { // SAFETY: `DeviceId` is a `#[repr(transparent)]` wrapper of `struct usb_device_id` and @@ -92,7 +92,7 @@ impl Adapter { let info = T::ID_TABLE.info(id.index()); let data = T::probe(intf, id, info); - let dev: &device::Device = intf.as_ref(); + let dev: &device::Device> = intf.as_ref(); dev.set_drvdata(data)?; Ok(0) }) @@ -103,9 +103,9 @@ impl Adapter { // `struct usb_interface`. // // INVARIANT: `intf` is valid for the duration of `disconnect_callback()`. - let intf = unsafe { &*intf.cast::>() }; + let intf = unsafe { &*intf.cast::>>() }; - let dev: &device::Device = intf.as_ref(); + let dev: &device::Device> = intf.as_ref(); // SAFETY: `disconnect_callback` is only ever called after a successful call to // `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called @@ -291,14 +291,14 @@ macro_rules! usb_device_table { /// const ID_TABLE: usb::IdTable = &USB_TABLE; /// /// fn probe( -/// _interface: &usb::Interface, +/// _interface: &usb::Interface>, /// _id: &usb::DeviceId, /// _info: &Self::IdInfo, /// ) -> impl PinInit { /// Err(ENODEV) /// } /// -/// fn disconnect(_interface: &usb::Interface, _data: Pin<&Self::Data>) {} +/// fn disconnect(_interface: &usb::Interface>, _data: Pin<&Self::Data>) {} /// } ///``` pub trait Driver { @@ -316,7 +316,7 @@ pub trait Driver { /// Called when a new USB interface is bound to this driver. /// Implementers should attempt to initialize the interface here. fn probe( - interface: &Interface, + interface: &Interface>, id: &DeviceId, id_info: &Self::IdInfo, ) -> impl PinInit; @@ -324,7 +324,7 @@ pub trait Driver { /// USB driver disconnect. /// /// Called when the USB interface is about to be unbound from this driver. - fn disconnect(interface: &Interface, data: Pin<&Self::Data>); + fn disconnect(interface: &Interface>, data: Pin<&Self::Data>); } /// A USB interface. diff --git a/samples/rust/rust_debugfs.rs b/samples/rust/rust_debugfs.rs index 478c4f693deb..37640ed33642 100644 --- a/samples/rust/rust_debugfs.rs +++ b/samples/rust/rust_debugfs.rs @@ -122,7 +122,7 @@ impl platform::Driver for RustDebugFs { const ACPI_ID_TABLE: Option> = Some(&ACPI_TABLE); fn probe( - pdev: &platform::Device, + pdev: &platform::Device>, _info: Option<&Self::IdInfo>, ) -> impl PinInit { RustDebugFs::new(pdev).pin_chain(|this| { @@ -147,7 +147,7 @@ impl RustDebugFs { dir.read_write_file(c"pair", new_mutex!(Inner { x: 3, y: 10 })) } - fn new(pdev: &platform::Device) -> impl PinInit + '_ { + fn new<'a>(pdev: &'a platform::Device>) -> impl PinInit + 'a { let debugfs = Dir::new(c"sample_debugfs"); let dev = pdev.as_ref(); diff --git a/samples/rust/rust_dma.rs b/samples/rust/rust_dma.rs index e583c6b8390a..9a243e7c7298 100644 --- a/samples/rust/rust_dma.rs +++ b/samples/rust/rust_dma.rs @@ -61,7 +61,7 @@ impl pci::Driver for DmaSampleDriver { type Data = Self; const ID_TABLE: pci::IdTable = &PCI_TABLE; - fn probe(pdev: &pci::Device, _info: &Self::IdInfo) -> impl PinInit { + fn probe(pdev: &pci::Device>, _info: &Self::IdInfo) -> impl PinInit { pin_init::pin_init_scope(move || { dev_info!(pdev, "Probe DMA test driver.\n"); diff --git a/samples/rust/rust_driver_auxiliary.rs b/samples/rust/rust_driver_auxiliary.rs index 61d5bf2e8c0d..f0d419823f9a 100644 --- a/samples/rust/rust_driver_auxiliary.rs +++ b/samples/rust/rust_driver_auxiliary.rs @@ -35,7 +35,10 @@ impl auxiliary::Driver for AuxiliaryDriver { const ID_TABLE: auxiliary::IdTable = &AUX_TABLE; - fn probe(adev: &auxiliary::Device, _info: &Self::IdInfo) -> impl PinInit { + fn probe( + adev: &auxiliary::Device>, + _info: &Self::IdInfo, + ) -> impl PinInit { dev_info!( adev, "Probing auxiliary driver for auxiliary device with id={}\n", @@ -70,7 +73,7 @@ impl pci::Driver for ParentDriver { const ID_TABLE: pci::IdTable = &PCI_TABLE; - fn probe(pdev: &pci::Device, _info: &Self::IdInfo) -> impl PinInit { + fn probe(pdev: &pci::Device>, _info: &Self::IdInfo) -> impl PinInit { Ok(Self { _reg0: auxiliary::Registration::new( pdev.as_ref(), diff --git a/samples/rust/rust_driver_i2c.rs b/samples/rust/rust_driver_i2c.rs index 8269f1798611..171550ea0b6f 100644 --- a/samples/rust/rust_driver_i2c.rs +++ b/samples/rust/rust_driver_i2c.rs @@ -42,7 +42,7 @@ impl i2c::Driver for SampleDriver { const OF_ID_TABLE: Option> = Some(&OF_TABLE); fn probe( - idev: &i2c::I2cClient, + idev: &i2c::I2cClient>, info: Option<&Self::IdInfo>, ) -> impl PinInit { let dev = idev.as_ref(); @@ -56,11 +56,11 @@ impl i2c::Driver for SampleDriver { Ok(Self) } - fn shutdown(idev: &i2c::I2cClient, _this: Pin<&Self>) { + fn shutdown(idev: &i2c::I2cClient>, _this: Pin<&Self>) { dev_info!(idev.as_ref(), "Shutdown Rust I2C driver sample.\n"); } - fn unbind(idev: &i2c::I2cClient, _this: Pin<&Self>) { + fn unbind(idev: &i2c::I2cClient>, _this: Pin<&Self>) { dev_info!(idev.as_ref(), "Unbind Rust I2C driver sample.\n"); } } diff --git a/samples/rust/rust_driver_pci.rs b/samples/rust/rust_driver_pci.rs index f43c6a660b39..3106f766fd93 100644 --- a/samples/rust/rust_driver_pci.rs +++ b/samples/rust/rust_driver_pci.rs @@ -144,7 +144,7 @@ impl pci::Driver for SampleDriver { const ID_TABLE: pci::IdTable = &PCI_TABLE; - fn probe(pdev: &pci::Device, info: &Self::IdInfo) -> impl PinInit { + fn probe(pdev: &pci::Device>, info: &Self::IdInfo) -> impl PinInit { pin_init::pin_init_scope(move || { let vendor = pdev.vendor_id(); dev_dbg!( @@ -175,7 +175,7 @@ impl pci::Driver for SampleDriver { }) } - fn unbind(pdev: &pci::Device, this: Pin<&Self>) { + fn unbind(pdev: &pci::Device>, this: Pin<&Self>) { if let Ok(bar) = this.bar.access(pdev.as_ref()) { // Reset pci-testdev by writing a new test index. bar.write_reg(regs::TEST::zeroed().with_index(this.index)); diff --git a/samples/rust/rust_driver_platform.rs b/samples/rust/rust_driver_platform.rs index 6505902f8200..04d40f836275 100644 --- a/samples/rust/rust_driver_platform.rs +++ b/samples/rust/rust_driver_platform.rs @@ -106,7 +106,7 @@ impl platform::Driver for SampleDriver { const ACPI_ID_TABLE: Option> = Some(&ACPI_TABLE); fn probe( - pdev: &platform::Device, + pdev: &platform::Device>, info: Option<&Self::IdInfo>, ) -> impl PinInit { let dev = pdev.as_ref(); diff --git a/samples/rust/rust_driver_usb.rs b/samples/rust/rust_driver_usb.rs index 5942e4b01fd8..e900993335e9 100644 --- a/samples/rust/rust_driver_usb.rs +++ b/samples/rust/rust_driver_usb.rs @@ -30,18 +30,18 @@ impl usb::Driver for SampleDriver { const ID_TABLE: usb::IdTable = &USB_TABLE; fn probe( - intf: &usb::Interface, + intf: &usb::Interface>, _id: &usb::DeviceId, _info: &Self::IdInfo, ) -> impl PinInit { - let dev: &device::Device = intf.as_ref(); + let dev: &device::Device> = intf.as_ref(); dev_info!(dev, "Rust USB driver sample probed\n"); Ok(Self { _intf: intf.into() }) } - fn disconnect(intf: &usb::Interface, _data: Pin<&Self>) { - let dev: &device::Device = intf.as_ref(); + fn disconnect(intf: &usb::Interface>, _data: Pin<&Self>) { + let dev: &device::Device> = intf.as_ref(); dev_info!(dev, "Rust USB driver sample disconnected\n"); } } diff --git a/samples/rust/rust_i2c_client.rs b/samples/rust/rust_i2c_client.rs index 5956b647294d..3f273c754f86 100644 --- a/samples/rust/rust_i2c_client.rs +++ b/samples/rust/rust_i2c_client.rs @@ -111,7 +111,7 @@ impl platform::Driver for SampleDriver { const ACPI_ID_TABLE: Option> = Some(&ACPI_TABLE); fn probe( - pdev: &platform::Device, + pdev: &platform::Device>, _info: Option<&Self::IdInfo>, ) -> impl PinInit { dev_info!( @@ -130,7 +130,7 @@ impl platform::Driver for SampleDriver { }) } - fn unbind(pdev: &platform::Device, _this: Pin<&Self>) { + fn unbind(pdev: &platform::Device>, _this: Pin<&Self>) { dev_info!( pdev.as_ref(), "Unbind Rust I2C Client registration sample.\n" diff --git a/samples/rust/rust_soc.rs b/samples/rust/rust_soc.rs index a5e72582f4a2..c466653491d2 100644 --- a/samples/rust/rust_soc.rs +++ b/samples/rust/rust_soc.rs @@ -42,7 +42,7 @@ impl platform::Driver for SampleSocDriver { const ACPI_ID_TABLE: Option> = Some(&ACPI_TABLE); fn probe( - pdev: &platform::Device, + pdev: &platform::Device>, _info: Option<&Self::IdInfo>, ) -> impl PinInit { dev_dbg!(pdev, "Probe Rust SoC driver sample.\n"); -- cgit v1.2.3 From 16c2b8fdab7c0808ff36430b2f49569029a8f484 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Mon, 25 May 2026 22:20:59 +0200 Subject: rust: pci: make Driver trait lifetime-parameterized Add a 'bound lifetime to the associated Data, changing type Data to type Data<'bound>. This allows the driver's bus device private data to capture the device / driver bound lifetime; device resources can be stored directly by reference rather than requiring Devres. The probe() and unbind() callbacks thus gain a 'bound lifetime parameter on the methods themselves; avoiding a global lifetime on the trait impl. Existing drivers set type Data<'bound> = Self, preserving the current behavior. Reviewed-by: Alexandre Courbot Reviewed-by: Greg Kroah-Hartman Reviewed-by: Gary Guo Link: https://patch.msgid.link/20260525202921.124698-13-dakr@kernel.org Signed-off-by: Danilo Krummrich --- drivers/gpu/nova-core/driver.rs | 9 ++++++--- rust/kernel/pci.rs | 28 ++++++++++++++-------------- samples/rust/rust_dma.rs | 7 +++++-- samples/rust/rust_driver_auxiliary.rs | 7 +++++-- samples/rust/rust_driver_pci.rs | 7 +++++-- 5 files changed, 35 insertions(+), 23 deletions(-) diff --git a/drivers/gpu/nova-core/driver.rs b/drivers/gpu/nova-core/driver.rs index 13c5ff15e87f..6ad1a856694c 100644 --- a/drivers/gpu/nova-core/driver.rs +++ b/drivers/gpu/nova-core/driver.rs @@ -74,10 +74,13 @@ kernel::pci_device_table!( impl pci::Driver for NovaCore { type IdInfo = (); - type Data = Self; + type Data<'bound> = Self; const ID_TABLE: pci::IdTable = &PCI_TABLE; - fn probe(pdev: &pci::Device>, _info: &Self::IdInfo) -> impl PinInit { + fn probe<'bound>( + pdev: &'bound pci::Device>, + _info: &'bound Self::IdInfo, + ) -> impl PinInit + 'bound { pin_init::pin_init_scope(move || { dev_dbg!(pdev, "Probe Nova Core GPU driver.\n"); @@ -109,7 +112,7 @@ impl pci::Driver for NovaCore { }) } - fn unbind(pdev: &pci::Device>, this: Pin<&Self>) { + fn unbind<'bound>(pdev: &'bound pci::Device>, this: Pin<&Self>) { this.gpu.unbind(pdev.as_ref()); } } diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs index 314ad9fefdb0..5071cae6543f 100644 --- a/rust/kernel/pci.rs +++ b/rust/kernel/pci.rs @@ -64,7 +64,7 @@ pub struct Adapter(T); // - `DEVICE_DRIVER_OFFSET` is the correct byte offset to the embedded `struct device_driver`. unsafe impl driver::DriverLayout for Adapter { type DriverType = bindings::pci_driver; - type DriverData<'bound> = T::Data; + type DriverData<'bound> = T::Data<'bound>; const DEVICE_DRIVER_OFFSET: usize = core::mem::offset_of!(Self::DriverType, driver); } @@ -129,8 +129,8 @@ impl Adapter { // SAFETY: `remove_callback` is only ever called after a successful call to // `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called - // and stored a `Pin>`. - let data = unsafe { pdev.as_ref().drvdata_borrow::() }; + // and stored a `Pin>>`. + let data = unsafe { pdev.as_ref().drvdata_borrow::>() }; T::unbind(pdev, data); } @@ -279,13 +279,13 @@ macro_rules! pci_device_table { /// /// impl pci::Driver for MyDriver { /// type IdInfo = (); -/// type Data = Self; +/// type Data<'bound> = Self; /// const ID_TABLE: pci::IdTable = &PCI_TABLE; /// -/// fn probe( -/// _pdev: &pci::Device>, -/// _id_info: &Self::IdInfo, -/// ) -> impl PinInit { +/// fn probe<'bound>( +/// _pdev: &'bound pci::Device>, +/// _id_info: &'bound Self::IdInfo, +/// ) -> impl PinInit, Error> + 'bound { /// Err(ENODEV) /// } /// } @@ -302,7 +302,7 @@ pub trait Driver { type IdInfo: 'static; /// The type of the driver's bus device private data. - type Data: Send; + type Data<'bound>: Send + 'bound; /// The table of device ids supported by the driver. const ID_TABLE: IdTable; @@ -311,10 +311,10 @@ pub trait Driver { /// /// Called when a new pci device is added or discovered. Implementers should /// attempt to initialize the device here. - fn probe( - dev: &Device>, - id_info: &Self::IdInfo, - ) -> impl PinInit; + fn probe<'bound>( + dev: &'bound Device>, + id_info: &'bound Self::IdInfo, + ) -> impl PinInit, Error> + 'bound; /// PCI driver unbind. /// @@ -326,7 +326,7 @@ pub trait Driver { /// operations to gracefully tear down the device. /// /// Otherwise, release operations for driver resources should be performed in `Drop`. - fn unbind(dev: &Device>, this: Pin<&Self::Data>) { + fn unbind<'bound>(dev: &'bound Device>, this: Pin<&Self::Data<'bound>>) { let _ = (dev, this); } } diff --git a/samples/rust/rust_dma.rs b/samples/rust/rust_dma.rs index 9a243e7c7298..c4d2d36602af 100644 --- a/samples/rust/rust_dma.rs +++ b/samples/rust/rust_dma.rs @@ -58,10 +58,13 @@ kernel::pci_device_table!( impl pci::Driver for DmaSampleDriver { type IdInfo = (); - type Data = Self; + type Data<'bound> = Self; const ID_TABLE: pci::IdTable = &PCI_TABLE; - fn probe(pdev: &pci::Device>, _info: &Self::IdInfo) -> impl PinInit { + fn probe<'bound>( + pdev: &'bound pci::Device>, + _info: &'bound Self::IdInfo, + ) -> impl PinInit + 'bound { pin_init::pin_init_scope(move || { dev_info!(pdev, "Probe DMA test driver.\n"); diff --git a/samples/rust/rust_driver_auxiliary.rs b/samples/rust/rust_driver_auxiliary.rs index f0d419823f9a..0e979f45cd68 100644 --- a/samples/rust/rust_driver_auxiliary.rs +++ b/samples/rust/rust_driver_auxiliary.rs @@ -69,11 +69,14 @@ kernel::pci_device_table!( impl pci::Driver for ParentDriver { type IdInfo = (); - type Data = Self; + type Data<'bound> = Self; const ID_TABLE: pci::IdTable = &PCI_TABLE; - fn probe(pdev: &pci::Device>, _info: &Self::IdInfo) -> impl PinInit { + fn probe<'bound>( + pdev: &'bound pci::Device>, + _info: &'bound Self::IdInfo, + ) -> impl PinInit + 'bound { Ok(Self { _reg0: auxiliary::Registration::new( pdev.as_ref(), diff --git a/samples/rust/rust_driver_pci.rs b/samples/rust/rust_driver_pci.rs index 3106f766fd93..6791d98e1c79 100644 --- a/samples/rust/rust_driver_pci.rs +++ b/samples/rust/rust_driver_pci.rs @@ -140,11 +140,14 @@ impl SampleDriver { impl pci::Driver for SampleDriver { type IdInfo = TestIndex; - type Data = Self; + type Data<'bound> = Self; const ID_TABLE: pci::IdTable = &PCI_TABLE; - fn probe(pdev: &pci::Device>, info: &Self::IdInfo) -> impl PinInit { + fn probe<'bound>( + pdev: &'bound pci::Device>, + info: &'bound Self::IdInfo, + ) -> impl PinInit + 'bound { pin_init::pin_init_scope(move || { let vendor = pdev.vendor_id(); dev_dbg!( -- cgit v1.2.3 From 81fdc788144348f295cfaa4b1e1edf6c74441c15 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Mon, 25 May 2026 22:21:00 +0200 Subject: rust: platform: make Driver trait lifetime-parameterized MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a 'bound lifetime to the associated Data, changing type Data to type Data<'bound>. This allows the driver's bus device private data to capture the device / driver bound lifetime; device resources can be stored directly by reference rather than requiring Devres. The probe() and unbind() callbacks thus gain a 'bound lifetime parameter on the methods themselves; avoiding a global lifetime on the trait impl. Existing drivers set type Data<'bound> = Self, preserving the current behavior. Acked-by: Uwe Kleine-König Reviewed-by: Alexandre Courbot Reviewed-by: Greg Kroah-Hartman Reviewed-by: Gary Guo Link: https://patch.msgid.link/20260525202921.124698-14-dakr@kernel.org Signed-off-by: Danilo Krummrich --- drivers/cpufreq/rcpufreq_dt.rs | 10 +++++----- drivers/gpu/drm/tyr/driver.rs | 10 +++++----- drivers/pwm/pwm_th1520.rs | 10 +++++----- rust/kernel/cpufreq.rs | 10 +++++----- rust/kernel/io/mem.rs | 20 ++++++++++---------- rust/kernel/platform.rs | 28 ++++++++++++++-------------- samples/rust/rust_debugfs.rs | 10 +++++----- samples/rust/rust_driver_platform.rs | 10 +++++----- samples/rust/rust_i2c_client.rs | 15 +++++++++------ samples/rust/rust_soc.rs | 10 +++++----- 10 files changed, 68 insertions(+), 65 deletions(-) diff --git a/drivers/cpufreq/rcpufreq_dt.rs b/drivers/cpufreq/rcpufreq_dt.rs index 5e0b224f6699..10106fa13095 100644 --- a/drivers/cpufreq/rcpufreq_dt.rs +++ b/drivers/cpufreq/rcpufreq_dt.rs @@ -201,13 +201,13 @@ kernel::of_device_table!( impl platform::Driver for CPUFreqDTDriver { type IdInfo = (); - type Data = Self; + type Data<'bound> = Self; const OF_ID_TABLE: Option> = Some(&OF_TABLE); - fn probe( - pdev: &platform::Device>, - _id_info: Option<&Self::IdInfo>, - ) -> impl PinInit { + fn probe<'bound>( + pdev: &'bound platform::Device>, + _id_info: Option<&'bound Self::IdInfo>, + ) -> impl PinInit + 'bound { cpufreq::Registration::::new_foreign_owned(pdev.as_ref())?; Ok(Self {}) } diff --git a/drivers/gpu/drm/tyr/driver.rs b/drivers/gpu/drm/tyr/driver.rs index 001727f44fc8..797f09e23a4c 100644 --- a/drivers/gpu/drm/tyr/driver.rs +++ b/drivers/gpu/drm/tyr/driver.rs @@ -91,13 +91,13 @@ kernel::of_device_table!( impl platform::Driver for TyrPlatformDriverData { type IdInfo = (); - type Data = Self; + type Data<'bound> = Self; const OF_ID_TABLE: Option> = Some(&OF_TABLE); - fn probe( - pdev: &platform::Device>, - _info: Option<&Self::IdInfo>, - ) -> impl PinInit { + fn probe<'bound>( + pdev: &'bound platform::Device>, + _info: Option<&'bound Self::IdInfo>, + ) -> impl PinInit + 'bound { let core_clk = Clk::get(pdev.as_ref(), Some(c"core"))?; let stacks_clk = OptionalClk::get(pdev.as_ref(), Some(c"stacks"))?; let coregroup_clk = OptionalClk::get(pdev.as_ref(), Some(c"coregroup"))?; diff --git a/drivers/pwm/pwm_th1520.rs b/drivers/pwm/pwm_th1520.rs index df83a4a9a507..6c5b791f3153 100644 --- a/drivers/pwm/pwm_th1520.rs +++ b/drivers/pwm/pwm_th1520.rs @@ -316,13 +316,13 @@ kernel::of_device_table!( impl platform::Driver for Th1520PwmPlatformDriver { type IdInfo = (); - type Data = Self; + type Data<'bound> = Self; const OF_ID_TABLE: Option> = Some(&OF_TABLE); - fn probe( - pdev: &platform::Device>, - _id_info: Option<&Self::IdInfo>, - ) -> impl PinInit { + fn probe<'bound>( + pdev: &'bound platform::Device>, + _id_info: Option<&'bound Self::IdInfo>, + ) -> impl PinInit + 'bound { let dev = pdev.as_ref(); let request = pdev.io_request_by_index(0).ok_or(ENODEV)?; diff --git a/rust/kernel/cpufreq.rs b/rust/kernel/cpufreq.rs index 0df518fa1d77..d94c6cdbc45a 100644 --- a/rust/kernel/cpufreq.rs +++ b/rust/kernel/cpufreq.rs @@ -888,13 +888,13 @@ pub trait Driver { /// /// impl platform::Driver for SampleDriver { /// type IdInfo = (); -/// type Data = Self; +/// type Data<'bound> = Self; /// const OF_ID_TABLE: Option> = None; /// -/// fn probe( -/// pdev: &platform::Device>, -/// _id_info: Option<&Self::IdInfo>, -/// ) -> impl PinInit { +/// fn probe<'bound>( +/// pdev: &'bound platform::Device>, +/// _id_info: Option<&'bound Self::IdInfo>, +/// ) -> impl PinInit + 'bound { /// cpufreq::Registration::::new_foreign_owned(pdev.as_ref())?; /// Ok(Self {}) /// } diff --git a/rust/kernel/io/mem.rs b/rust/kernel/io/mem.rs index 03d8745b5e1d..51ba347220ee 100644 --- a/rust/kernel/io/mem.rs +++ b/rust/kernel/io/mem.rs @@ -62,12 +62,12 @@ impl<'a> IoRequest<'a> { /// /// impl platform::Driver for SampleDriver { /// # type IdInfo = (); - /// # type Data = Self; + /// # type Data<'bound> = Self; /// - /// fn probe( - /// pdev: &platform::Device>, - /// info: Option<&Self::IdInfo>, - /// ) -> impl PinInit { + /// fn probe<'bound>( + /// pdev: &'bound platform::Device>, + /// info: Option<&'bound Self::IdInfo>, + /// ) -> impl PinInit + 'bound { /// let offset = 0; // Some offset. /// /// // If the size is known at compile time, use [`Self::iomap_sized`]. @@ -127,12 +127,12 @@ impl<'a> IoRequest<'a> { /// /// impl platform::Driver for SampleDriver { /// # type IdInfo = (); - /// # type Data = Self; + /// # type Data<'bound> = Self; /// - /// fn probe( - /// pdev: &platform::Device>, - /// info: Option<&Self::IdInfo>, - /// ) -> impl PinInit { + /// fn probe<'bound>( + /// pdev: &'bound platform::Device>, + /// info: Option<&'bound Self::IdInfo>, + /// ) -> impl PinInit + 'bound { /// let offset = 0; // Some offset. /// /// // Unlike [`Self::iomap_sized`], here the size of the memory region diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs index 257b7084338c..d8d48f60b0b9 100644 --- a/rust/kernel/platform.rs +++ b/rust/kernel/platform.rs @@ -50,7 +50,7 @@ pub struct Adapter(T); // - `DEVICE_DRIVER_OFFSET` is the correct byte offset to the embedded `struct device_driver`. unsafe impl driver::DriverLayout for Adapter { type DriverType = bindings::platform_driver; - type DriverData<'bound> = T::Data; + type DriverData<'bound> = T::Data<'bound>; const DEVICE_DRIVER_OFFSET: usize = core::mem::offset_of!(Self::DriverType, driver); } @@ -117,8 +117,8 @@ impl Adapter { // SAFETY: `remove_callback` is only ever called after a successful call to // `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called - // and stored a `Pin>`. - let data = unsafe { pdev.as_ref().drvdata_borrow::() }; + // and stored a `Pin>>`. + let data = unsafe { pdev.as_ref().drvdata_borrow::>() }; T::unbind(pdev, data); } @@ -192,14 +192,14 @@ macro_rules! module_platform_driver { /// /// impl platform::Driver for MyDriver { /// type IdInfo = (); -/// type Data = Self; +/// type Data<'bound> = Self; /// const OF_ID_TABLE: Option> = Some(&OF_TABLE); /// const ACPI_ID_TABLE: Option> = Some(&ACPI_TABLE); /// -/// fn probe( -/// _pdev: &platform::Device>, -/// _id_info: Option<&Self::IdInfo>, -/// ) -> impl PinInit { +/// fn probe<'bound>( +/// _pdev: &'bound platform::Device>, +/// _id_info: Option<&'bound Self::IdInfo>, +/// ) -> impl PinInit, Error> + 'bound { /// Err(ENODEV) /// } /// } @@ -214,7 +214,7 @@ pub trait Driver { type IdInfo: 'static; /// The type of the driver's bus device private data. - type Data: Send; + type Data<'bound>: Send + 'bound; /// The table of OF device ids supported by the driver. const OF_ID_TABLE: Option> = None; @@ -226,10 +226,10 @@ pub trait Driver { /// /// Called when a new platform device is added or discovered. /// Implementers should attempt to initialize the device here. - fn probe( - dev: &Device>, - id_info: Option<&Self::IdInfo>, - ) -> impl PinInit; + fn probe<'bound>( + dev: &'bound Device>, + id_info: Option<&'bound Self::IdInfo>, + ) -> impl PinInit, Error> + 'bound; /// Platform driver unbind. /// @@ -241,7 +241,7 @@ pub trait Driver { /// operations to gracefully tear down the device. /// /// Otherwise, release operations for driver resources should be performed in `Drop`. - fn unbind(dev: &Device>, this: Pin<&Self::Data>) { + fn unbind<'bound>(dev: &'bound Device>, this: Pin<&Self::Data<'bound>>) { let _ = (dev, this); } } diff --git a/samples/rust/rust_debugfs.rs b/samples/rust/rust_debugfs.rs index 37640ed33642..1f59e08aaa4b 100644 --- a/samples/rust/rust_debugfs.rs +++ b/samples/rust/rust_debugfs.rs @@ -117,14 +117,14 @@ kernel::acpi_device_table!( impl platform::Driver for RustDebugFs { type IdInfo = (); - type Data = Self; + type Data<'bound> = Self; const OF_ID_TABLE: Option> = None; const ACPI_ID_TABLE: Option> = Some(&ACPI_TABLE); - fn probe( - pdev: &platform::Device>, - _info: Option<&Self::IdInfo>, - ) -> impl PinInit { + fn probe<'bound>( + pdev: &'bound platform::Device>, + _info: Option<&'bound Self::IdInfo>, + ) -> impl PinInit + 'bound { RustDebugFs::new(pdev).pin_chain(|this| { this.counter.store(91, Relaxed); { diff --git a/samples/rust/rust_driver_platform.rs b/samples/rust/rust_driver_platform.rs index 04d40f836275..ec0d6cac4f57 100644 --- a/samples/rust/rust_driver_platform.rs +++ b/samples/rust/rust_driver_platform.rs @@ -101,14 +101,14 @@ kernel::acpi_device_table!( impl platform::Driver for SampleDriver { type IdInfo = Info; - type Data = Self; + type Data<'bound> = Self; const OF_ID_TABLE: Option> = Some(&OF_TABLE); const ACPI_ID_TABLE: Option> = Some(&ACPI_TABLE); - fn probe( - pdev: &platform::Device>, - info: Option<&Self::IdInfo>, - ) -> impl PinInit { + fn probe<'bound>( + pdev: &'bound platform::Device>, + info: Option<&'bound Self::IdInfo>, + ) -> impl PinInit + 'bound { let dev = pdev.as_ref(); dev_dbg!(dev, "Probe Rust Platform driver sample.\n"); diff --git a/samples/rust/rust_i2c_client.rs b/samples/rust/rust_i2c_client.rs index 3f273c754f86..2d876f4e3ee0 100644 --- a/samples/rust/rust_i2c_client.rs +++ b/samples/rust/rust_i2c_client.rs @@ -106,14 +106,14 @@ const BOARD_INFO: i2c::I2cBoardInfo = impl platform::Driver for SampleDriver { type IdInfo = (); - type Data = Self; + type Data<'bound> = Self; const OF_ID_TABLE: Option> = Some(&OF_TABLE); const ACPI_ID_TABLE: Option> = Some(&ACPI_TABLE); - fn probe( - pdev: &platform::Device>, - _info: Option<&Self::IdInfo>, - ) -> impl PinInit { + fn probe<'bound>( + pdev: &'bound platform::Device>, + _info: Option<&'bound Self::IdInfo>, + ) -> impl PinInit + 'bound { dev_info!( pdev.as_ref(), "Probe Rust I2C Client registration sample.\n" @@ -130,7 +130,10 @@ impl platform::Driver for SampleDriver { }) } - fn unbind(pdev: &platform::Device>, _this: Pin<&Self>) { + fn unbind<'bound>( + pdev: &'bound platform::Device>, + _this: Pin<&Self::Data<'bound>>, + ) { dev_info!( pdev.as_ref(), "Unbind Rust I2C Client registration sample.\n" diff --git a/samples/rust/rust_soc.rs b/samples/rust/rust_soc.rs index c466653491d2..808d58200eb6 100644 --- a/samples/rust/rust_soc.rs +++ b/samples/rust/rust_soc.rs @@ -37,14 +37,14 @@ kernel::acpi_device_table!( impl platform::Driver for SampleSocDriver { type IdInfo = (); - type Data = Self; + type Data<'bound> = Self; const OF_ID_TABLE: Option> = Some(&OF_TABLE); const ACPI_ID_TABLE: Option> = Some(&ACPI_TABLE); - fn probe( - pdev: &platform::Device>, - _info: Option<&Self::IdInfo>, - ) -> impl PinInit { + fn probe<'bound>( + pdev: &'bound platform::Device>, + _info: Option<&'bound Self::IdInfo>, + ) -> impl PinInit + 'bound { dev_dbg!(pdev, "Probe Rust SoC driver sample.\n"); let pdev = pdev.into(); -- cgit v1.2.3 From 46f651d88662ef931555cd135f09382af206295a Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Mon, 25 May 2026 22:21:01 +0200 Subject: rust: auxiliary: make Driver trait lifetime-parameterized Add a 'bound lifetime to the associated Data, changing type Data to type Data<'bound>. This allows the driver's bus device private data to capture the device / driver bound lifetime; device resources can be stored directly by reference rather than requiring Devres. The probe() and unbind() callbacks thus gain a 'bound lifetime parameter on the methods themselves; avoiding a global lifetime on the trait impl. Existing drivers set type Data<'bound> = Self, preserving the current behavior. Reviewed-by: Alexandre Courbot Reviewed-by: Greg Kroah-Hartman Reviewed-by: Gary Guo Link: https://patch.msgid.link/20260525202921.124698-15-dakr@kernel.org Signed-off-by: Danilo Krummrich --- drivers/gpu/drm/nova/driver.rs | 10 +++++----- rust/kernel/auxiliary.rs | 18 +++++++++--------- samples/rust/rust_driver_auxiliary.rs | 10 +++++----- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/drivers/gpu/drm/nova/driver.rs b/drivers/gpu/drm/nova/driver.rs index 7605348af994..aa08644012f7 100644 --- a/drivers/gpu/drm/nova/driver.rs +++ b/drivers/gpu/drm/nova/driver.rs @@ -51,13 +51,13 @@ kernel::auxiliary_device_table!( impl auxiliary::Driver for NovaDriver { type IdInfo = (); - type Data = Self; + type Data<'bound> = Self; const ID_TABLE: auxiliary::IdTable = &AUX_TABLE; - fn probe( - adev: &auxiliary::Device>, - _info: &Self::IdInfo, - ) -> impl PinInit { + fn probe<'bound>( + adev: &'bound auxiliary::Device>, + _info: &'bound Self::IdInfo, + ) -> impl PinInit + 'bound { let data = try_pin_init!(NovaData { adev: adev.into() }); let drm = drm::Device::::new(adev.as_ref(), data)?; diff --git a/rust/kernel/auxiliary.rs b/rust/kernel/auxiliary.rs index 6d504b0933d5..7a1b1a7b7ca6 100644 --- a/rust/kernel/auxiliary.rs +++ b/rust/kernel/auxiliary.rs @@ -46,7 +46,7 @@ pub struct Adapter(T); // - `DEVICE_DRIVER_OFFSET` is the correct byte offset to the embedded `struct device_driver`. unsafe impl driver::DriverLayout for Adapter { type DriverType = bindings::auxiliary_driver; - type DriverData<'bound> = T::Data; + type DriverData<'bound> = T::Data<'bound>; const DEVICE_DRIVER_OFFSET: usize = core::mem::offset_of!(Self::DriverType, driver); } @@ -111,8 +111,8 @@ impl Adapter { // SAFETY: `remove_callback` is only ever called after a successful call to // `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called - // and stored a `Pin>`. - let data = unsafe { adev.as_ref().drvdata_borrow::() }; + // and stored a `Pin>>`. + let data = unsafe { adev.as_ref().drvdata_borrow::>() }; T::unbind(adev, data); } @@ -203,7 +203,7 @@ pub trait Driver { type IdInfo: 'static; /// The type of the driver's bus device private data. - type Data: Send; + type Data<'bound>: Send + 'bound; /// The table of device ids supported by the driver. const ID_TABLE: IdTable; @@ -211,10 +211,10 @@ pub trait Driver { /// Auxiliary driver probe. /// /// Called when an auxiliary device is matches a corresponding driver. - fn probe( - dev: &Device>, - id_info: &Self::IdInfo, - ) -> impl PinInit; + fn probe<'bound>( + dev: &'bound Device>, + id_info: &'bound Self::IdInfo, + ) -> impl PinInit, Error> + 'bound; /// Auxiliary driver unbind. /// @@ -226,7 +226,7 @@ pub trait Driver { /// operations to gracefully tear down the device. /// /// Otherwise, release operations for driver resources should be performed in `Drop`. - fn unbind(dev: &Device>, this: Pin<&Self::Data>) { + fn unbind<'bound>(dev: &'bound Device>, this: Pin<&Self::Data<'bound>>) { let _ = (dev, this); } } diff --git a/samples/rust/rust_driver_auxiliary.rs b/samples/rust/rust_driver_auxiliary.rs index 0e979f45cd68..b30a4d5cdf8a 100644 --- a/samples/rust/rust_driver_auxiliary.rs +++ b/samples/rust/rust_driver_auxiliary.rs @@ -31,14 +31,14 @@ kernel::auxiliary_device_table!( impl auxiliary::Driver for AuxiliaryDriver { type IdInfo = (); - type Data = Self; + type Data<'bound> = Self; const ID_TABLE: auxiliary::IdTable = &AUX_TABLE; - fn probe( - adev: &auxiliary::Device>, - _info: &Self::IdInfo, - ) -> impl PinInit { + fn probe<'bound>( + adev: &'bound auxiliary::Device>, + _info: &'bound Self::IdInfo, + ) -> impl PinInit + 'bound { dev_info!( adev, "Probing auxiliary driver for auxiliary device with id={}\n", -- cgit v1.2.3 From a3f09f8e47c4262510c979b384d6f85d376d91f5 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Mon, 25 May 2026 22:21:02 +0200 Subject: rust: usb: make Driver trait lifetime-parameterized Add a 'bound lifetime to the associated Data, changing type Data to type Data<'bound>. This allows the driver's bus device private data to capture the device / driver bound lifetime; device resources can be stored directly by reference rather than requiring Devres. The probe() and disconnect() callbacks thus gain a 'bound lifetime parameter on the methods themselves; avoiding a global lifetime on the trait impl. Existing drivers set type Data<'bound> = Self, preserving the current behavior. Reviewed-by: Alexandre Courbot Reviewed-by: Greg Kroah-Hartman Reviewed-by: Eliot Courtney Reviewed-by: Gary Guo Reviewed-by: Daniel Almeida Link: https://patch.msgid.link/20260525202921.124698-16-dakr@kernel.org Signed-off-by: Danilo Krummrich --- rust/kernel/usb.rs | 37 ++++++++++++++++++++++--------------- samples/rust/rust_driver_usb.rs | 12 ++++++------ 2 files changed, 28 insertions(+), 21 deletions(-) diff --git a/rust/kernel/usb.rs b/rust/kernel/usb.rs index 1dbb8387b463..7aff0c82d0af 100644 --- a/rust/kernel/usb.rs +++ b/rust/kernel/usb.rs @@ -41,7 +41,7 @@ pub struct Adapter(T); // - `DEVICE_DRIVER_OFFSET` is the correct byte offset to the embedded `struct device_driver`. unsafe impl driver::DriverLayout for Adapter { type DriverType = bindings::usb_driver; - type DriverData<'bound> = T::Data; + type DriverData<'bound> = T::Data<'bound>; const DEVICE_DRIVER_OFFSET: usize = core::mem::offset_of!(Self::DriverType, driver); } @@ -109,8 +109,8 @@ impl Adapter { // SAFETY: `disconnect_callback` is only ever called after a successful call to // `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called - // and stored a `Pin>`. - let data = unsafe { dev.drvdata_borrow::() }; + // and stored a `Pin>>`. + let data = unsafe { dev.drvdata_borrow::>() }; T::disconnect(intf, data); } @@ -287,18 +287,22 @@ macro_rules! usb_device_table { /// /// impl usb::Driver for MyDriver { /// type IdInfo = (); -/// type Data = Self; +/// type Data<'bound> = Self; /// const ID_TABLE: usb::IdTable = &USB_TABLE; /// -/// fn probe( -/// _interface: &usb::Interface>, +/// fn probe<'bound>( +/// _interface: &'bound usb::Interface>, /// _id: &usb::DeviceId, -/// _info: &Self::IdInfo, -/// ) -> impl PinInit { +/// _info: &'bound Self::IdInfo, +/// ) -> impl PinInit, Error> + 'bound { /// Err(ENODEV) /// } /// -/// fn disconnect(_interface: &usb::Interface>, _data: Pin<&Self::Data>) {} +/// fn disconnect<'bound>( +/// _interface: &'bound usb::Interface>, +/// _data: Pin<&Self::Data<'bound>>, +/// ) { +/// } /// } ///``` pub trait Driver { @@ -306,7 +310,7 @@ pub trait Driver { type IdInfo: 'static; /// The type of the driver's bus device private data. - type Data: Send; + type Data<'bound>: Send + 'bound; /// The table of device ids supported by the driver. const ID_TABLE: IdTable; @@ -315,16 +319,19 @@ pub trait Driver { /// /// Called when a new USB interface is bound to this driver. /// Implementers should attempt to initialize the interface here. - fn probe( - interface: &Interface>, + fn probe<'bound>( + interface: &'bound Interface>, id: &DeviceId, - id_info: &Self::IdInfo, - ) -> impl PinInit; + id_info: &'bound Self::IdInfo, + ) -> impl PinInit, Error> + 'bound; /// USB driver disconnect. /// /// Called when the USB interface is about to be unbound from this driver. - fn disconnect(interface: &Interface>, data: Pin<&Self::Data>); + fn disconnect<'bound>( + interface: &'bound Interface>, + data: Pin<&Self::Data<'bound>>, + ); } /// A USB interface. diff --git a/samples/rust/rust_driver_usb.rs b/samples/rust/rust_driver_usb.rs index e900993335e9..02bd5085f9bc 100644 --- a/samples/rust/rust_driver_usb.rs +++ b/samples/rust/rust_driver_usb.rs @@ -26,21 +26,21 @@ kernel::usb_device_table!( impl usb::Driver for SampleDriver { type IdInfo = (); - type Data = Self; + type Data<'bound> = Self; const ID_TABLE: usb::IdTable = &USB_TABLE; - fn probe( - intf: &usb::Interface>, + fn probe<'bound>( + intf: &'bound usb::Interface>, _id: &usb::DeviceId, - _info: &Self::IdInfo, - ) -> impl PinInit { + _info: &'bound Self::IdInfo, + ) -> impl PinInit + 'bound { let dev: &device::Device> = intf.as_ref(); dev_info!(dev, "Rust USB driver sample probed\n"); Ok(Self { _intf: intf.into() }) } - fn disconnect(intf: &usb::Interface>, _data: Pin<&Self>) { + fn disconnect<'bound>(intf: &'bound usb::Interface>, _data: Pin<&Self>) { let dev: &device::Device> = intf.as_ref(); dev_info!(dev, "Rust USB driver sample disconnected\n"); } -- cgit v1.2.3 From 71e6b6a80b5158323be56e0a776e9fa3cc77d061 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Mon, 25 May 2026 22:21:03 +0200 Subject: rust: i2c: make Driver trait lifetime-parameterized Add a 'bound lifetime to the associated Data, changing type Data to type Data<'bound>. This allows the driver's bus device private data to capture the device / driver bound lifetime; device resources can be stored directly by reference rather than requiring Devres. The probe() and unbind() callbacks thus gain a 'bound lifetime parameter on the methods themselves; avoiding a global lifetime on the trait impl. Existing drivers set type Data<'bound> = Self, preserving the current behavior. Acked-by: Igor Korotin Reviewed-by: Alexandre Courbot Reviewed-by: Greg Kroah-Hartman Reviewed-by: Gary Guo Link: https://patch.msgid.link/20260525202921.124698-17-dakr@kernel.org Signed-off-by: Danilo Krummrich --- rust/kernel/i2c.rs | 39 +++++++++++++++++++++------------------ samples/rust/rust_driver_i2c.rs | 14 +++++++------- 2 files changed, 28 insertions(+), 25 deletions(-) diff --git a/rust/kernel/i2c.rs b/rust/kernel/i2c.rs index 50feade0fb58..6094d32652e3 100644 --- a/rust/kernel/i2c.rs +++ b/rust/kernel/i2c.rs @@ -98,7 +98,7 @@ pub struct Adapter(T); // - `DEVICE_DRIVER_OFFSET` is the correct byte offset to the embedded `struct device_driver`. unsafe impl driver::DriverLayout for Adapter { type DriverType = bindings::i2c_driver; - type DriverData<'bound> = T::Data; + type DriverData<'bound> = T::Data<'bound>; const DEVICE_DRIVER_OFFSET: usize = core::mem::offset_of!(Self::DriverType, driver); } @@ -176,8 +176,8 @@ impl Adapter { // SAFETY: `remove_callback` is only ever called after a successful call to // `probe_callback`, hence it's guaranteed that `I2cClient::set_drvdata()` has been called - // and stored a `Pin>`. - let data = unsafe { idev.as_ref().drvdata_borrow::() }; + // and stored a `Pin>>`. + let data = unsafe { idev.as_ref().drvdata_borrow::>() }; T::unbind(idev, data); } @@ -188,8 +188,8 @@ impl Adapter { // SAFETY: `shutdown_callback` is only ever called after a successful call to // `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called - // and stored a `Pin>`. - let data = unsafe { idev.as_ref().drvdata_borrow::() }; + // and stored a `Pin>>`. + let data = unsafe { idev.as_ref().drvdata_borrow::>() }; T::shutdown(idev, data); } @@ -294,19 +294,22 @@ macro_rules! module_i2c_driver { /// /// impl i2c::Driver for MyDriver { /// type IdInfo = (); -/// type Data = Self; +/// type Data<'bound> = Self; /// const I2C_ID_TABLE: Option> = Some(&I2C_TABLE); /// const OF_ID_TABLE: Option> = Some(&OF_TABLE); /// const ACPI_ID_TABLE: Option> = Some(&ACPI_TABLE); /// -/// fn probe( -/// _idev: &i2c::I2cClient>, -/// _id_info: Option<&Self::IdInfo>, -/// ) -> impl PinInit { +/// fn probe<'bound>( +/// _idev: &'bound i2c::I2cClient>, +/// _id_info: Option<&'bound Self::IdInfo>, +/// ) -> impl PinInit, Error> + 'bound { /// Err(ENODEV) /// } /// -/// fn shutdown(_idev: &i2c::I2cClient>, this: Pin<&Self::Data>) { +/// fn shutdown<'bound>( +/// _idev: &'bound i2c::I2cClient>, +/// this: Pin<&Self::Data<'bound>>, +/// ) { /// } /// } ///``` @@ -320,7 +323,7 @@ pub trait Driver { type IdInfo: 'static; /// The type of the driver's bus device private data. - type Data: Send; + type Data<'bound>: Send + 'bound; /// The table of device ids supported by the driver. const I2C_ID_TABLE: Option> = None; @@ -335,10 +338,10 @@ pub trait Driver { /// /// Called when a new i2c client is added or discovered. /// Implementers should attempt to initialize the client here. - fn probe( - dev: &I2cClient>, - id_info: Option<&Self::IdInfo>, - ) -> impl PinInit; + fn probe<'bound>( + dev: &'bound I2cClient>, + id_info: Option<&'bound Self::IdInfo>, + ) -> impl PinInit, Error> + 'bound; /// I2C driver shutdown. /// @@ -351,7 +354,7 @@ pub trait Driver { /// This callback is distinct from final resource cleanup, as the driver instance remains valid /// after it returns. Any deallocation or teardown of driver-owned resources should instead be /// handled in `Drop`. - fn shutdown(dev: &I2cClient>, this: Pin<&Self::Data>) { + fn shutdown<'bound>(dev: &'bound I2cClient>, this: Pin<&Self::Data<'bound>>) { let _ = (dev, this); } @@ -365,7 +368,7 @@ pub trait Driver { /// operations to gracefully tear down the device. /// /// Otherwise, release operations for driver resources should be performed in `Drop`. - fn unbind(dev: &I2cClient>, this: Pin<&Self::Data>) { + fn unbind<'bound>(dev: &'bound I2cClient>, this: Pin<&Self::Data<'bound>>) { let _ = (dev, this); } } diff --git a/samples/rust/rust_driver_i2c.rs b/samples/rust/rust_driver_i2c.rs index 171550ea0b6f..ead8263a7d48 100644 --- a/samples/rust/rust_driver_i2c.rs +++ b/samples/rust/rust_driver_i2c.rs @@ -35,16 +35,16 @@ kernel::of_device_table! { impl i2c::Driver for SampleDriver { type IdInfo = u32; - type Data = Self; + type Data<'bound> = Self; const ACPI_ID_TABLE: Option> = Some(&ACPI_TABLE); const I2C_ID_TABLE: Option> = Some(&I2C_TABLE); const OF_ID_TABLE: Option> = Some(&OF_TABLE); - fn probe( - idev: &i2c::I2cClient>, - info: Option<&Self::IdInfo>, - ) -> impl PinInit { + fn probe<'bound>( + idev: &'bound i2c::I2cClient>, + info: Option<&'bound Self::IdInfo>, + ) -> impl PinInit + 'bound { let dev = idev.as_ref(); dev_info!(dev, "Probe Rust I2C driver sample.\n"); @@ -56,11 +56,11 @@ impl i2c::Driver for SampleDriver { Ok(Self) } - fn shutdown(idev: &i2c::I2cClient>, _this: Pin<&Self>) { + fn shutdown<'bound>(idev: &'bound i2c::I2cClient>, _this: Pin<&Self>) { dev_info!(idev.as_ref(), "Shutdown Rust I2C driver sample.\n"); } - fn unbind(idev: &i2c::I2cClient>, _this: Pin<&Self>) { + fn unbind<'bound>(idev: &'bound i2c::I2cClient>, _this: Pin<&Self>) { dev_info!(idev.as_ref(), "Unbind Rust I2C driver sample.\n"); } } -- cgit v1.2.3 From d31a349a7fd88c4cc7ba85bce6491c398408997a Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Mon, 25 May 2026 22:21:04 +0200 Subject: rust: driver: update module documentation for GAT-based Data type Now that all bus driver traits use type Data<'bound>: 'bound, update the illustrative driver trait in the module documentation to reflect the GAT pattern and lifetime-parameterized callbacks. Reviewed-by: Alexandre Courbot Reviewed-by: Greg Kroah-Hartman Reviewed-by: Gary Guo Link: https://patch.msgid.link/20260525202921.124698-18-dakr@kernel.org Signed-off-by: Danilo Krummrich --- rust/kernel/driver.rs | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/rust/kernel/driver.rs b/rust/kernel/driver.rs index 558fdef4a1c6..03c0dd713f4c 100644 --- a/rust/kernel/driver.rs +++ b/rust/kernel/driver.rs @@ -18,7 +18,7 @@ //! type IdInfo: 'static; //! //! /// The type of the driver's bus device private data. -//! type Data: Send; +//! type Data<'bound>: Send + 'bound; //! //! /// The table of OF device ids supported by the driver. //! const OF_ID_TABLE: Option> = None; @@ -27,11 +27,16 @@ //! const ACPI_ID_TABLE: Option> = None; //! //! /// Driver probe. -//! fn probe(dev: &Device>, id_info: &Self::IdInfo) -//! -> impl PinInit; +//! fn probe<'bound>( +//! dev: &'bound Device>, +//! id_info: &'bound Self::IdInfo, +//! ) -> impl PinInit, Error> + 'bound; //! //! /// Driver unbind (optional). -//! fn unbind(dev: &Device>, this: Pin<&Self::Data>) { +//! fn unbind<'bound>( +//! dev: &'bound Device>, +//! this: Pin<&Self::Data<'bound>>, +//! ) { //! let _ = (dev, this); //! } //! } @@ -46,9 +51,10 @@ )] #![cfg_attr(CONFIG_PCI, doc = "* [`pci::Driver`](kernel::pci::Driver)")] //! -//! The `probe()` callback should return a `impl PinInit`, i.e. the driver's -//! private data. The bus abstraction should store the pointer in the corresponding bus device. The -//! generic [`Device`] infrastructure provides common helpers for this purpose on its +//! The `probe()` callback should return a +//! `impl PinInit, Error>`, i.e. the driver's private data. The bus +//! abstraction should store the pointer in the corresponding bus device. The generic +//! [`Device`] infrastructure provides common helpers for this purpose on its //! [`Device`] implementation. //! //! All driver callbacks should provide a reference to the driver's private data. Once the driver -- cgit v1.2.3 From 8ea0b6d5bef5e4f4637964c3b2cf732d9bf4f408 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Mon, 25 May 2026 22:21:05 +0200 Subject: rust: pci: make Bar lifetime-parameterized Convert pci::Bar to pci::Bar<'a, SIZE>, storing &'a Device to tie the BAR mapping lifetime to the device. iomap_region_sized() now returns Result> directly instead of impl PinInit>, Error>. Since the lifetime ties the mapping to the device's bound state, callers no longer need Devres for the common case where the Bar lives in the driver's private data. Add Bar::into_devres() to consume the bar and register it as a device-managed resource, returning Devres>. The lifetime is erased to 'static because Devres guarantees the bar does not actually outlive the device -- access is revoked on unbind. Reviewed-by: Eliot Courtney Reviewed-by: Greg Kroah-Hartman Reviewed-by: Alexandre Courbot Reviewed-by: Gary Guo Link: https://patch.msgid.link/20260525202921.124698-19-dakr@kernel.org Signed-off-by: Danilo Krummrich --- drivers/gpu/nova-core/driver.rs | 7 +++--- rust/kernel/devres.rs | 2 +- rust/kernel/pci/io.rs | 52 ++++++++++++++++++++++++----------------- samples/rust/rust_driver_pci.rs | 5 ++-- 4 files changed, 38 insertions(+), 28 deletions(-) diff --git a/drivers/gpu/nova-core/driver.rs b/drivers/gpu/nova-core/driver.rs index 6ad1a856694c..7dbec0470c26 100644 --- a/drivers/gpu/nova-core/driver.rs +++ b/drivers/gpu/nova-core/driver.rs @@ -45,7 +45,7 @@ const BAR0_SIZE: usize = SZ_16M; // DMA addresses. These systems should be quite rare. const GPU_DMA_BITS: u32 = 47; -pub(crate) type Bar0 = pci::Bar; +pub(crate) type Bar0 = pci::Bar<'static, BAR0_SIZE>; kernel::pci_device_table!( PCI_TABLE, @@ -92,8 +92,9 @@ impl pci::Driver for NovaCore { // other threads of execution. unsafe { pdev.dma_set_mask_and_coherent(DmaMask::new::())? }; - let bar = Arc::pin_init( - pdev.iomap_region_sized::(0, c"nova-core/bar0"), + let bar = Arc::new( + pdev.iomap_region_sized::(0, c"nova-core/bar0")? + .into_devres()?, GFP_KERNEL, )?; diff --git a/rust/kernel/devres.rs b/rust/kernel/devres.rs index fd4633f977f6..82cbd8b969fb 100644 --- a/rust/kernel/devres.rs +++ b/rust/kernel/devres.rs @@ -304,7 +304,7 @@ impl Devres { /// pci, // /// }; /// - /// fn from_core(dev: &pci::Device>, devres: Devres>) -> Result { + /// fn from_core(dev: &pci::Device>, devres: Devres>) -> Result { /// let bar = devres.access(dev.as_ref())?; /// /// let _ = bar.read32(0x0); diff --git a/rust/kernel/pci/io.rs b/rust/kernel/pci/io.rs index 3ce21482b079..0461e01aaa20 100644 --- a/rust/kernel/pci/io.rs +++ b/rust/kernel/pci/io.rs @@ -14,8 +14,7 @@ use crate::{ Mmio, MmioRaw, // }, - prelude::*, - sync::aref::ARef, // + prelude::*, // }; use core::{ marker::PhantomData, @@ -146,14 +145,18 @@ impl<'a, S: ConfigSpaceKind> IoKnownSize for ConfigSpace<'a, S> { /// /// `Bar` always holds an `IoRaw` instance that holds a valid pointer to the start of the I/O /// memory mapped PCI BAR and its size. -pub struct Bar { - pdev: ARef, +pub struct Bar<'a, const SIZE: usize = 0> { + pdev: &'a Device, io: MmioRaw, num: i32, } -impl Bar { - pub(super) fn new(pdev: &Device, num: u32, name: &'static CStr) -> Result { +impl<'a, const SIZE: usize> Bar<'a, SIZE> { + pub(super) fn new( + pdev: &'a Device, + num: u32, + name: &'static CStr, + ) -> Result { let len = pdev.resource_len(num)?; if len == 0 { return Err(ENOMEM); @@ -196,11 +199,7 @@ impl Bar { } }; - Ok(Bar { - pdev: pdev.into(), - io, - num, - }) + Ok(Bar { pdev, io, num }) } /// # Safety @@ -219,11 +218,24 @@ impl Bar { fn release(&self) { // SAFETY: The safety requirements are guaranteed by the type invariant of `self.pdev`. - unsafe { Self::do_release(&self.pdev, self.io.addr(), self.num) }; + unsafe { Self::do_release(self.pdev, self.io.addr(), self.num) }; + } + + /// Consume the `Bar` and register it as a device-managed resource. + /// + /// The returned `Devres>` can outlive the original lifetime `'a`. Access + /// to the BAR is revoked when the device is unbound. + pub fn into_devres(self) -> Result>> { + // SAFETY: Casting to `'static` is sound because `Devres` guarantees the `Bar` does not + // actually outlive the device -- access is revoked and the resource is released when the + // device is unbound. + let bar: Bar<'static, SIZE> = unsafe { core::mem::transmute(self) }; + let pdev = bar.pdev; + Devres::new(pdev.as_ref(), bar) } } -impl Bar { +impl Bar<'_> { #[inline] pub(super) fn index_is_valid(index: u32) -> bool { // A `struct pci_dev` owns an array of resources with at most `PCI_NUM_RESOURCES` entries. @@ -231,13 +243,13 @@ impl Bar { } } -impl Drop for Bar { +impl Drop for Bar<'_, SIZE> { fn drop(&mut self) { self.release(); } } -impl Deref for Bar { +impl Deref for Bar<'_, SIZE> { type Target = Mmio; fn deref(&self) -> &Self::Target { @@ -253,16 +265,12 @@ impl Device { &'a self, bar: u32, name: &'static CStr, - ) -> impl PinInit>, Error> + 'a { - Devres::new(self.as_ref(), Bar::::new(self, bar, name)) + ) -> Result> { + Bar::new(self, bar, name) } /// Maps an entire PCI BAR after performing a region-request on it. - pub fn iomap_region<'a>( - &'a self, - bar: u32, - name: &'static CStr, - ) -> impl PinInit, Error> + 'a { + pub fn iomap_region<'a>(&'a self, bar: u32, name: &'static CStr) -> Result> { self.iomap_region_sized::<0>(bar, name) } diff --git a/samples/rust/rust_driver_pci.rs b/samples/rust/rust_driver_pci.rs index 6791d98e1c79..0353481b0690 100644 --- a/samples/rust/rust_driver_pci.rs +++ b/samples/rust/rust_driver_pci.rs @@ -45,7 +45,7 @@ mod regs { pub(super) const END: usize = 0x10; } -type Bar0 = pci::Bar<{ regs::END }>; +type Bar0 = pci::Bar<'static, { regs::END }>; #[derive(Copy, Clone, Debug)] struct TestIndex(u8); @@ -161,7 +161,8 @@ impl pci::Driver for SampleDriver { pdev.set_master(); Ok(try_pin_init!(Self { - bar <- pdev.iomap_region_sized::<{ regs::END }>(0, c"rust_driver_pci"), + bar: pdev.iomap_region_sized::<{ regs::END }>(0, c"rust_driver_pci")? + .into_devres()?, index: *info, _: { let bar = bar.access(pdev.as_ref())?; -- cgit v1.2.3 From 89f55d04c6028fa15800a4887faf51bdeebfa431 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Mon, 25 May 2026 22:21:06 +0200 Subject: rust: io: make IoMem and ExclusiveIoMem lifetime-parameterized MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a lifetime parameter to IoMem<'a, SIZE> and ExclusiveIoMem<'a, SIZE>, storing a &'a Device reference to tie the mapping to the device's lifetime. This mirrors the pci::Bar<'a, SIZE> design and enables drivers to hold I/O memory mappings directly in their HRT private data, tied to the device lifetime. IoRequest::iomap_* methods now return the mapping directly instead of wrapping it in Devres. Callers that need device-managed revocation can call the new into_devres() method. Acked-by: Uwe Kleine-König Reviewed-by: Eliot Courtney Reviewed-by: Greg Kroah-Hartman Reviewed-by: Alexandre Courbot Reviewed-by: Gary Guo Link: https://patch.msgid.link/20260525202921.124698-20-dakr@kernel.org Signed-off-by: Danilo Krummrich --- drivers/gpu/drm/tyr/driver.rs | 4 +- drivers/pwm/pwm_th1520.rs | 4 +- rust/kernel/io/mem.rs | 103 +++++++++++++++++++++--------------------- 3 files changed, 56 insertions(+), 55 deletions(-) diff --git a/drivers/gpu/drm/tyr/driver.rs b/drivers/gpu/drm/tyr/driver.rs index 797f09e23a4c..04f83fcf0937 100644 --- a/drivers/gpu/drm/tyr/driver.rs +++ b/drivers/gpu/drm/tyr/driver.rs @@ -37,7 +37,7 @@ use crate::{ regs, // }; -pub(crate) type IoMem = kernel::io::mem::IoMem; +pub(crate) type IoMem = kernel::io::mem::IoMem<'static, SZ_2M>; pub(crate) struct TyrDrmDriver; @@ -110,7 +110,7 @@ impl platform::Driver for TyrPlatformDriverData { let sram_regulator = Regulator::::get(pdev.as_ref(), c"sram")?; let request = pdev.io_request_by_index(0).ok_or(ENODEV)?; - let iomem = Arc::pin_init(request.iomap_sized::(), GFP_KERNEL)?; + let iomem = Arc::new(request.iomap_sized::()?.into_devres()?, GFP_KERNEL)?; issue_soft_reset(pdev.as_ref(), &iomem)?; gpu::l2_power_on(pdev.as_ref(), &iomem)?; diff --git a/drivers/pwm/pwm_th1520.rs b/drivers/pwm/pwm_th1520.rs index 6c5b791f3153..48808cd80737 100644 --- a/drivers/pwm/pwm_th1520.rs +++ b/drivers/pwm/pwm_th1520.rs @@ -92,7 +92,7 @@ struct Th1520WfHw { #[pin_data(PinnedDrop)] struct Th1520PwmDriverData { #[pin] - iomem: devres::Devres>, + iomem: devres::Devres>, clk: Clk, } @@ -352,7 +352,7 @@ impl platform::Driver for Th1520PwmPlatformDriver { dev, TH1520_MAX_PWM_NUM, try_pin_init!(Th1520PwmDriverData { - iomem <- request.iomap_sized::(), + iomem <- request.iomap_sized::()?.into_devres(), clk <- clk, }), )?; diff --git a/rust/kernel/io/mem.rs b/rust/kernel/io/mem.rs index 51ba347220ee..fc2a3e24f8d5 100644 --- a/rust/kernel/io/mem.rs +++ b/rust/kernel/io/mem.rs @@ -74,22 +74,19 @@ impl<'a> IoRequest<'a> { /// // /// // No runtime checks will apply when reading and writing. /// let request = pdev.io_request_by_index(0).ok_or(ENODEV)?; - /// let iomem = request.iomap_sized::<42>(); - /// let iomem = KBox::pin_init(iomem, GFP_KERNEL)?; - /// - /// let io = iomem.access(pdev.as_ref())?; + /// let iomem = request.iomap_sized::<42>()?; /// /// // Read and write a 32-bit value at `offset`. - /// let data = io.read32(offset); + /// let data = iomem.read32(offset); /// - /// io.write32(data, offset); + /// iomem.write32(data, offset); /// /// # Ok(SampleDriver) /// } /// } /// ``` - pub fn iomap_sized(self) -> impl PinInit>, Error> + 'a { - IoMem::new(self) + pub fn iomap_sized(self) -> Result> { + IoMem::ioremap(self.device, self.resource) } /// Same as [`Self::iomap_sized`] but with exclusive access to the @@ -98,10 +95,8 @@ impl<'a> IoRequest<'a> { /// This uses the [`ioremap()`] C API. /// /// [`ioremap()`]: https://docs.kernel.org/driver-api/device-io.html#getting-access-to-the-device - pub fn iomap_exclusive_sized( - self, - ) -> impl PinInit>, Error> + 'a { - ExclusiveIoMem::new(self) + pub fn iomap_exclusive_sized(self) -> Result> { + ExclusiveIoMem::ioremap(self.device, self.resource) } /// Maps an [`IoRequest`] where the size is not known at compile time, @@ -140,27 +135,24 @@ impl<'a> IoRequest<'a> { /// // family of functions should be used, leading to runtime checks on every /// // access. /// let request = pdev.io_request_by_index(0).ok_or(ENODEV)?; - /// let iomem = request.iomap(); - /// let iomem = KBox::pin_init(iomem, GFP_KERNEL)?; - /// - /// let io = iomem.access(pdev.as_ref())?; + /// let iomem = request.iomap()?; /// - /// let data = io.try_read32(offset)?; + /// let data = iomem.try_read32(offset)?; /// - /// io.try_write32(data, offset)?; + /// iomem.try_write32(data, offset)?; /// /// # Ok(SampleDriver) /// } /// } /// ``` - pub fn iomap(self) -> impl PinInit>, Error> + 'a { - Self::iomap_sized::<0>(self) + pub fn iomap(self) -> Result> { + self.iomap_sized::<0>() } /// Same as [`Self::iomap`] but with exclusive access to the underlying /// region. - pub fn iomap_exclusive(self) -> impl PinInit>, Error> + 'a { - Self::iomap_exclusive_sized::<0>(self) + pub fn iomap_exclusive(self) -> Result> { + self.iomap_exclusive_sized::<0>() } } @@ -169,9 +161,9 @@ impl<'a> IoRequest<'a> { /// # Invariants /// /// - [`ExclusiveIoMem`] has exclusive access to the underlying [`IoMem`]. -pub struct ExclusiveIoMem { +pub struct ExclusiveIoMem<'a, const SIZE: usize> { /// The underlying `IoMem` instance. - iomem: IoMem, + iomem: IoMem<'a, SIZE>, /// The region abstraction. This represents exclusive access to the /// range represented by the underlying `iomem`. @@ -180,9 +172,9 @@ pub struct ExclusiveIoMem { _region: Region, } -impl ExclusiveIoMem { +impl<'a, const SIZE: usize> ExclusiveIoMem<'a, SIZE> { /// Creates a new `ExclusiveIoMem` instance. - fn ioremap(resource: &Resource) -> Result { + fn ioremap(dev: &'a Device, resource: &Resource) -> Result { let start = resource.start(); let size = resource.size(); let name = resource.name().unwrap_or_default(); @@ -196,26 +188,29 @@ impl ExclusiveIoMem { ) .ok_or(EBUSY)?; - let iomem = IoMem::ioremap(resource)?; + let iomem = IoMem::ioremap(dev, resource)?; - let iomem = ExclusiveIoMem { + Ok(ExclusiveIoMem { iomem, _region: region, - }; - - Ok(iomem) + }) } - /// Creates a new `ExclusiveIoMem` instance from a previously acquired [`IoRequest`]. - pub fn new<'a>(io_request: IoRequest<'a>) -> impl PinInit, Error> + 'a { - let dev = io_request.device; - let res = io_request.resource; - - Devres::new(dev, Self::ioremap(res)) + /// Consume the `ExclusiveIoMem` and register it as a device-managed resource. + /// + /// The returned `Devres>` can outlive the original lifetime + /// `'a`. Access to the I/O memory is revoked when the device is unbound. + pub fn into_devres(self) -> Result>> { + // SAFETY: Casting to `'static` is sound because `Devres` guarantees the + // `ExclusiveIoMem` does not actually outlive the device -- access is revoked and the + // resource is released when the device is unbound. + let iomem: ExclusiveIoMem<'static, SIZE> = unsafe { core::mem::transmute(self) }; + let dev = iomem.iomem.dev; + Devres::new(dev, iomem) } } -impl Deref for ExclusiveIoMem { +impl Deref for ExclusiveIoMem<'_, SIZE> { type Target = Mmio; fn deref(&self) -> &Self::Target { @@ -232,12 +227,13 @@ impl Deref for ExclusiveIoMem { /// /// [`IoMem`] always holds an [`MmioRaw`] instance that holds a valid pointer to the /// start of the I/O memory mapped region. -pub struct IoMem { +pub struct IoMem<'a, const SIZE: usize = 0> { + dev: &'a Device, io: MmioRaw, } -impl IoMem { - fn ioremap(resource: &Resource) -> Result { +impl<'a, const SIZE: usize> IoMem<'a, SIZE> { + fn ioremap(dev: &'a Device, resource: &Resource) -> Result { // Note: Some ioremap() implementations use types that depend on the CPU // word width rather than the bus address width. // @@ -269,28 +265,33 @@ impl IoMem { } let io = MmioRaw::new(addr as usize, size)?; - let io = IoMem { io }; - Ok(io) + Ok(IoMem { dev, io }) } - /// Creates a new `IoMem` instance from a previously acquired [`IoRequest`]. - pub fn new<'a>(io_request: IoRequest<'a>) -> impl PinInit, Error> + 'a { - let dev = io_request.device; - let res = io_request.resource; - - Devres::new(dev, Self::ioremap(res)) + /// Consume the `IoMem` and register it as a device-managed resource. + /// + /// The returned `Devres>` can outlive the original + /// lifetime `'a`. Access to the I/O memory is revoked when the device + /// is unbound. + pub fn into_devres(self) -> Result>> { + // SAFETY: Casting to `'static` is sound because `Devres` guarantees the `IoMem` does not + // actually outlive the device -- access is revoked and the resource is released when the + // device is unbound. + let iomem: IoMem<'static, SIZE> = unsafe { core::mem::transmute(self) }; + let dev = iomem.dev; + Devres::new(dev, iomem) } } -impl Drop for IoMem { +impl Drop for IoMem<'_, SIZE> { fn drop(&mut self) { // SAFETY: Safe as by the invariant of `Io`. unsafe { bindings::iounmap(self.io.addr() as *mut c_void) } } } -impl Deref for IoMem { +impl Deref for IoMem<'_, SIZE> { type Target = Mmio; fn deref(&self) -> &Self::Target { -- cgit v1.2.3 From e397d405c4c6117b4eeeeecb8170c17c604ee6cc Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Mon, 25 May 2026 22:21:07 +0200 Subject: samples: rust: rust_driver_pci: use HRT lifetime for Bar Convert the sample driver to SampleDriver<'bound>, taking advantage of the lifetime-parameterized Driver trait. The driver struct holds &'bound pci::Device directly instead of ARef, and pci::Bar<'bound> directly instead of Devres. This removes PinnedDrop, pin_init_scope, and runtime revocation checks on BAR access. Reviewed-by: Eliot Courtney Reviewed-by: Greg Kroah-Hartman Reviewed-by: Alexandre Courbot Reviewed-by: Gary Guo Link: https://patch.msgid.link/20260525202921.124698-21-dakr@kernel.org Signed-off-by: Danilo Krummrich --- samples/rust/rust_driver_pci.rs | 89 ++++++++++++++++++----------------------- 1 file changed, 40 insertions(+), 49 deletions(-) diff --git a/samples/rust/rust_driver_pci.rs b/samples/rust/rust_driver_pci.rs index 0353481b0690..1aa8197d8698 100644 --- a/samples/rust/rust_driver_pci.rs +++ b/samples/rust/rust_driver_pci.rs @@ -9,7 +9,6 @@ use kernel::{ Bound, Core, // }, - devres::Devres, io::{ register, register::Array, @@ -17,8 +16,7 @@ use kernel::{ }, num::Bounded, pci, - prelude::*, - sync::aref::ARef, // + prelude::*, // }; mod regs { @@ -45,7 +43,7 @@ mod regs { pub(super) const END: usize = 0x10; } -type Bar0 = pci::Bar<'static, { regs::END }>; +type Bar0<'bound> = pci::Bar<'bound, { regs::END }>; #[derive(Copy, Clone, Debug)] struct TestIndex(u8); @@ -66,14 +64,14 @@ impl TestIndex { const NO_EVENTFD: Self = Self(0); } -#[pin_data(PinnedDrop)] -struct SampleDriver { - pdev: ARef, - #[pin] - bar: Devres, +struct SampleDriverData<'bound> { + pdev: &'bound pci::Device, + bar: Bar0<'bound>, index: TestIndex, } +struct SampleDriver; + kernel::pci_device_table!( PCI_TABLE, MODULE_PCI_TABLE, @@ -84,8 +82,8 @@ kernel::pci_device_table!( )] ); -impl SampleDriver { - fn testdev(index: &TestIndex, bar: &Bar0) -> Result { +impl SampleDriverData<'_> { + fn testdev(index: &TestIndex, bar: &Bar0<'_>) -> Result { // Select the test. bar.write_reg(regs::TEST::zeroed().with_index(*index)); @@ -140,56 +138,49 @@ impl SampleDriver { impl pci::Driver for SampleDriver { type IdInfo = TestIndex; - type Data<'bound> = Self; + type Data<'bound> = SampleDriverData<'bound>; const ID_TABLE: pci::IdTable = &PCI_TABLE; fn probe<'bound>( pdev: &'bound pci::Device>, info: &'bound Self::IdInfo, - ) -> impl PinInit + 'bound { - pin_init::pin_init_scope(move || { - let vendor = pdev.vendor_id(); - dev_dbg!( - pdev, - "Probe Rust PCI driver sample (PCI ID: {}, 0x{:x}).\n", - vendor, - pdev.device_id() - ); - - pdev.enable_device_mem()?; - pdev.set_master(); - - Ok(try_pin_init!(Self { - bar: pdev.iomap_region_sized::<{ regs::END }>(0, c"rust_driver_pci")? - .into_devres()?, - index: *info, - _: { - let bar = bar.access(pdev.as_ref())?; - - dev_info!( - pdev, - "pci-testdev data-match count: {}\n", - Self::testdev(info, bar)? - ); - Self::config_space(pdev); - }, - pdev: pdev.into(), - })) + ) -> impl PinInit, Error> + 'bound { + let vendor = pdev.vendor_id(); + dev_dbg!( + pdev, + "Probe Rust PCI driver sample (PCI ID: {}, 0x{:x}).\n", + vendor, + pdev.device_id() + ); + + pdev.enable_device_mem()?; + pdev.set_master(); + + let bar = pdev.iomap_region_sized::<{ regs::END }>(0, c"rust_driver_pci")?; + + dev_info!( + pdev, + "pci-testdev data-match count: {}\n", + SampleDriverData::testdev(info, &bar)? + ); + SampleDriverData::config_space(pdev); + + Ok(SampleDriverData { + pdev, + bar, + index: *info, }) } - fn unbind(pdev: &pci::Device>, this: Pin<&Self>) { - if let Ok(bar) = this.bar.access(pdev.as_ref()) { - // Reset pci-testdev by writing a new test index. - bar.write_reg(regs::TEST::zeroed().with_index(this.index)); - } + fn unbind<'bound>(_pdev: &'bound pci::Device>, this: Pin<&Self::Data<'bound>>) { + this.bar + .write_reg(regs::TEST::zeroed().with_index(this.index)); } } -#[pinned_drop] -impl PinnedDrop for SampleDriver { - fn drop(self: Pin<&mut Self>) { +impl Drop for SampleDriverData<'_> { + fn drop(&mut self) { dev_dbg!(self.pdev, "Remove Rust PCI driver sample.\n"); } } -- cgit v1.2.3 From bb1cf43f2fa85beba82a0d9bbea21013cf94d7a0 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Mon, 25 May 2026 22:21:08 +0200 Subject: gpu: nova-core: separate driver type from driver data Introduce NovaCoreDriver as the driver type implementing pci::Driver, keeping NovaCore as the per-device data type. This prepares for making NovaCore lifetime-parameterized once auxiliary::Registration requires a lifetime for the binding scope. Reviewed-by: Greg Kroah-Hartman Reviewed-by: Alexandre Courbot Reviewed-by: Eliot Courtney Reviewed-by: Gary Guo Link: https://patch.msgid.link/20260525202921.124698-22-dakr@kernel.org Signed-off-by: Danilo Krummrich --- drivers/gpu/nova-core/driver.rs | 14 ++++++++------ drivers/gpu/nova-core/nova_core.rs | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/nova-core/driver.rs b/drivers/gpu/nova-core/driver.rs index 7dbec0470c26..fa898fe5c893 100644 --- a/drivers/gpu/nova-core/driver.rs +++ b/drivers/gpu/nova-core/driver.rs @@ -35,6 +35,8 @@ pub(crate) struct NovaCore { _reg: Devres>, } +pub(crate) struct NovaCoreDriver; + const BAR0_SIZE: usize = SZ_16M; // For now we only support Ampere which can use up to 47-bit DMA addresses. @@ -50,7 +52,7 @@ pub(crate) type Bar0 = pci::Bar<'static, BAR0_SIZE>; kernel::pci_device_table!( PCI_TABLE, MODULE_PCI_TABLE, - ::IdInfo, + ::IdInfo, [ // Modern NVIDIA GPUs will show up as either VGA or 3D controllers. ( @@ -72,15 +74,15 @@ kernel::pci_device_table!( ] ); -impl pci::Driver for NovaCore { +impl pci::Driver for NovaCoreDriver { type IdInfo = (); - type Data<'bound> = Self; + type Data<'bound> = NovaCore; const ID_TABLE: pci::IdTable = &PCI_TABLE; fn probe<'bound>( pdev: &'bound pci::Device>, _info: &'bound Self::IdInfo, - ) -> impl PinInit + 'bound { + ) -> impl PinInit + 'bound { pin_init::pin_init_scope(move || { dev_dbg!(pdev, "Probe Nova Core GPU driver.\n"); @@ -98,7 +100,7 @@ impl pci::Driver for NovaCore { GFP_KERNEL, )?; - Ok(try_pin_init!(Self { + Ok(try_pin_init!(NovaCore { gpu <- Gpu::new(pdev, bar.clone(), bar.access(pdev.as_ref())?), _reg: auxiliary::Registration::new( pdev.as_ref(), @@ -113,7 +115,7 @@ impl pci::Driver for NovaCore { }) } - fn unbind<'bound>(pdev: &'bound pci::Device>, this: Pin<&Self>) { + fn unbind<'bound>(pdev: &'bound pci::Device>, this: Pin<&NovaCore>) { this.gpu.unbind(pdev.as_ref()); } } diff --git a/drivers/gpu/nova-core/nova_core.rs b/drivers/gpu/nova-core/nova_core.rs index 04a1fa6b25f8..073d87714d3a 100644 --- a/drivers/gpu/nova-core/nova_core.rs +++ b/drivers/gpu/nova-core/nova_core.rs @@ -47,7 +47,7 @@ struct NovaCoreModule { // Fields are dropped in declaration order, so `_driver` is dropped first, // then `_debugfs_guard` clears `DEBUGFS_ROOT`. #[pin] - _driver: Registration>, + _driver: Registration>, _debugfs_guard: DebugfsRootGuard, } -- cgit v1.2.3 From e189bdb687a56bcf389798f1d3a2f261fff2ef54 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Mon, 25 May 2026 22:21:09 +0200 Subject: rust: types: add `ForLt` trait for higher-ranked lifetime support There are a few cases, e.g. when dealing with data referencing each other, one might want to write code that is generic over lifetimes. For example, if you want to take a function that takes `&'a Foo` and gives `Bar<'a>`, you can write: f: impl for<'a> FnOnce(&'a Foo) -> Bar<'a>, However, it becomes tricky when you want that function to not have a fixed `Bar`, but have it be generic again. In this case, one needs something that is generic over types that are themselves generic over lifetimes. `ForLt` provides such support. It provides a trait `ForLt` which describes a type generic over a lifetime. One may use `ForLt::Of<'a>` to get an instance of a type for a specific lifetime. For the case of cross referencing, one would almost always want the lifetime to be covariant. Therefore this is also made a requirement for the `ForLt` trait, so functions with `ForLt` trait bound can assume covariance. A macro `ForLt!()` is provided to be able to obtain a type that implements `ForLt`. For example, `ForLt!(for<'a> Bar<'a>)` would yield a type that `::Of<'a>` is `Bar<'a>`. This also works with lifetime elision, e.g. `ForLt!(Bar<'_>)` or for types without lifetime at all, e.g. `ForLt!(u32)`. The API design draws inspiration from the higher-kinded-types [1] crate, however a different design decision has been taken (e.g. covariance requirement) and the implementation is independent. License headers use "Apache-2.0 OR MIT" because I anticipate this to be used in pin-init crate too which is licensed as such. Link: https://docs.rs/higher-kinded-types/ [1] Reviewed-by: Greg Kroah-Hartman Reviewed-by: Eliot Courtney Signed-off-by: Gary Guo Acked-by: Miguel Ojeda Reviewed-by: Alexandre Courbot Link: https://patch.msgid.link/20260525202921.124698-23-dakr@kernel.org [ Handle macro_rules! invocations in the ForLt! proc macro's covariance and WF checks. Since proc macros cannot expand macro_rules!, add a visit_macro() implementation to conservatively assume macro invocations may contain lifetimes, forcing them through the compiler-assisted covariance proof. Fix a few typos in the documentation and in the commit message, add empty lines before samples, add missing periods and consistently use markdown. - Danilo ] Signed-off-by: Danilo Krummrich --- rust/Makefile | 1 + rust/kernel/types.rs | 4 + rust/kernel/types/for_lt.rs | 122 ++++++++++++++++++++++ rust/macros/for_lt.rs | 248 ++++++++++++++++++++++++++++++++++++++++++++ rust/macros/lib.rs | 13 +++ 5 files changed, 388 insertions(+) create mode 100644 rust/kernel/types/for_lt.rs create mode 100644 rust/macros/for_lt.rs diff --git a/rust/Makefile b/rust/Makefile index b361bfedfdf0..c5a9a3339416 100644 --- a/rust/Makefile +++ b/rust/Makefile @@ -110,6 +110,7 @@ syn-cfgs := \ feature="parsing" \ feature="printing" \ feature="proc-macro" \ + feature="visit" \ feature="visit-mut" syn-flags := \ diff --git a/rust/kernel/types.rs b/rust/kernel/types.rs index 9cf9f869d195..ac316fd7b538 100644 --- a/rust/kernel/types.rs +++ b/rust/kernel/types.rs @@ -11,6 +11,10 @@ use core::{ }; use pin_init::{PinInit, Wrapper, Zeroable}; +#[doc(hidden)] +pub mod for_lt; +pub use for_lt::ForLt; + /// Used to transfer ownership to and from foreign (non-Rust) languages. /// /// Ownership is transferred from Rust to a foreign language by calling [`Self::into_foreign`] and diff --git a/rust/kernel/types/for_lt.rs b/rust/kernel/types/for_lt.rs new file mode 100644 index 000000000000..d44323c28e8d --- /dev/null +++ b/rust/kernel/types/for_lt.rs @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! Provide implementation and test of the `ForLt` trait and macro. +//! +//! This module is hidden and user should just use `ForLt!` directly. + +use core::marker::PhantomData; + +/// Representation of types generic over a lifetime. +/// +/// The type must be covariant over the generic lifetime, i.e. the lifetime parameter +/// can be soundly shortened. +/// +/// The lifetime involved must be covariant. +/// +/// # Macro +/// +/// It is not recommended to implement this trait directly. `ForLt!` macro is provided to obtain a +/// type that implements this trait. +/// +/// The full syntax is +/// +/// ``` +/// # use kernel::types::ForLt; +/// # fn expect_lt() {} +/// # struct TypeThatUse<'a>(&'a ()); +/// # expect_lt::< +/// ForLt!(for<'a> TypeThatUse<'a>) +/// # >(); +/// ``` +/// +/// which gives a type so that ` TypeThatUse<'a>) as ForLt>::Of<'b>` +/// is `TypeThatUse<'b>`. +/// +/// You may also use a short-hand syntax which works similar to lifetime elision. +/// The macro also accepts types that do not involve a lifetime at all. +/// +/// ``` +/// # use kernel::types::ForLt; +/// # fn expect_lt() {} +/// # struct TypeThatUse<'a>(&'a ()); +/// # expect_lt::< +/// ForLt!(TypeThatUse<'_>) // Equivalent to `ForLt!(for<'a> TypeThatUse<'a>)`. +/// # >(); +/// # expect_lt::< +/// ForLt!(&u32) // Equivalent to `ForLt!(for<'a> &'a u32)`. +/// # >(); +/// # expect_lt::< +/// ForLt!(u32) // Equivalent to `ForLt!(for<'a> u32)`. +/// # >(); +/// ``` +/// +/// The macro will attempt to prove that the type is indeed covariant over the lifetime supplied. +/// When it cannot be syntactically proven, it will emit checks to ask the Rust compiler to prove +/// it. +/// +/// ```ignore,compile_fail +/// # use kernel::types::ForLt; +/// # fn expect_lt() {} +/// # expect_lt::< +/// ForLt!(fn(&u32)) // Contravariant, will fail compilation. +/// # >(); +/// ``` +/// +/// There is a limitation if the type refers to generic parameters; if the macro cannot prove the +/// covariance syntactically, the emitted checks will fail the compilation as it needs to refer to +/// the generic parameter but is in a separate item. +/// +/// ``` +/// # use kernel::types::ForLt; +/// fn expect_lt() {} +/// # #[allow(clippy::unnecessary_safety_comment, reason = "false positive")] +/// fn generic_fn() { +/// // Syntactically proven by the macro +/// expect_lt::(); +/// // Syntactically proven by the macro +/// expect_lt::)>(); +/// // Cannot be syntactically proven, need to check covariance of `KBox` +/// // expect_lt::)>(); +/// } +/// ``` +/// +/// # Safety +/// +/// `Self::Of<'a>` must be covariant over the lifetime `'a`. +pub unsafe trait ForLt { + /// The type parameterized by the lifetime. + type Of<'a>: 'a; + + /// Cast a reference to a shorter lifetime. + #[inline(always)] + fn cast_ref<'r, 'short: 'r, 'long: 'short>(long: &'r Self::Of<'long>) -> &'r Self::Of<'short> { + // SAFETY: This is sound as this trait guarantees covariance. + unsafe { core::mem::transmute(long) } + } +} +pub use macros::ForLt; + +/// This is intended to be an "unsafe-to-refer-to" type. +/// +/// Must only be used by the `ForLt!` macro. +/// +/// `T` is the magic `dyn for<'a> WithLt<'a, TypeThatUse<'a>>` generated by macro. +/// +/// `WF` is a type that the macro can use to assert some specific type is well-formed. +/// +/// `N` is to provide the macro a place to emit arbitrary items, in case it needs to prove +/// additional properties. +#[doc(hidden)] +pub struct UnsafeForLtImpl(PhantomData<(WF, T)>); + +// This is a helper trait for implementation `ForLt` to be able to use HRTB. +#[doc(hidden)] +pub trait WithLt<'a> { + type Of: 'a; +} + +// SAFETY: In `ForLt!` macro, a covariance proof is generated when naming `UnsafeForLtImpl` +// and it will fail to evaluate if the type is not covariant. +unsafe impl WithLt<'a>, WF> ForLt for UnsafeForLtImpl { + type Of<'a> = >::Of; +} diff --git a/rust/macros/for_lt.rs b/rust/macros/for_lt.rs new file mode 100644 index 000000000000..364d4113cd10 --- /dev/null +++ b/rust/macros/for_lt.rs @@ -0,0 +1,248 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + +use proc_macro2::{ + Span, + TokenStream, // +}; +use quote::{ + format_ident, + quote, // +}; +use syn::{ + parse::{ + Parse, + ParseStream, // + }, + visit::Visit, + visit_mut::VisitMut, + Lifetime, + Result, + Token, + Type, // +}; + +pub(crate) enum HigherRankedType { + Explicit { + _for_token: Token![for], + _lt_token: Token![<], + lifetime: Lifetime, + _gt_token: Token![>], + ty: Type, + }, + Implicit { + ty: Type, + }, +} + +impl Parse for HigherRankedType { + fn parse(input: ParseStream<'_>) -> Result { + if input.peek(Token![for]) { + Ok(Self::Explicit { + _for_token: input.parse()?, + _lt_token: input.parse()?, + lifetime: input.parse()?, + _gt_token: input.parse()?, + ty: input.parse()?, + }) + } else { + Ok(Self::Implicit { ty: input.parse()? }) + } + } +} + +trait TypeExt { + fn expand_elided_lifetime(&self, explicit_lt: &Lifetime) -> Type; + fn replace_lifetime(&self, src: &Lifetime, dst: &Lifetime) -> Type; + fn has_lifetime(&self, lt: &Lifetime) -> bool; +} + +impl TypeExt for Type { + fn expand_elided_lifetime(&self, explicit_lt: &Lifetime) -> Type { + struct ElidedLifetimeExpander<'a>(&'a Lifetime); + + impl VisitMut for ElidedLifetimeExpander<'_> { + fn visit_lifetime_mut(&mut self, lifetime: &mut Lifetime) { + // Expand explicit `'_` + if lifetime.ident == "_" { + *lifetime = self.0.clone(); + } + } + + fn visit_type_reference_mut(&mut self, reference: &mut syn::TypeReference) { + syn::visit_mut::visit_type_reference_mut(self, reference); + + if reference.lifetime.is_none() { + reference.lifetime = Some(self.0.clone()); + } + } + } + + let mut ret = self.clone(); + ElidedLifetimeExpander(explicit_lt).visit_type_mut(&mut ret); + ret + } + + fn replace_lifetime(&self, src: &Lifetime, dst: &Lifetime) -> Type { + struct LifetimeReplacer<'a>(&'a Lifetime, &'a Lifetime); + + impl VisitMut for LifetimeReplacer<'_> { + fn visit_lifetime_mut(&mut self, lifetime: &mut Lifetime) { + if lifetime.ident == self.0.ident { + *lifetime = self.1.clone(); + } + } + } + + let mut ret = self.clone(); + LifetimeReplacer(src, dst).visit_type_mut(&mut ret); + ret + } + + fn has_lifetime(&self, lt: &Lifetime) -> bool { + struct HasLifetime<'a>(&'a Lifetime, bool); + + impl Visit<'_> for HasLifetime<'_> { + fn visit_lifetime(&mut self, lifetime: &Lifetime) { + if lifetime.ident == self.0.ident { + self.1 = true; + } + } + + // Macro invocations are opaque; conservatively assume they may + // reference the lifetime. + fn visit_macro(&mut self, _: &syn::Macro) { + self.1 = true; + } + } + + let mut visitor = HasLifetime(lt, false); + visitor.visit_type(self); + visitor.1 + } +} + +struct Prover<'a>(&'a Lifetime, Vec<&'a Type>); + +impl<'a> Prover<'a> { + /// Prove that `ty` is covariant over `'lt`. + /// + /// This also needs to prove that it'll be wellformed for any instance of `'lt`. + /// It can be assumed that `ty` will be wellformed if `'lt` is substituted to `'static`. + fn prove(&mut self, ty: &'a Type) { + match ty { + Type::Paren(ty) => self.prove(&ty.elem), + Type::Group(ty) => self.prove(&ty.elem), + + // No lifetime involved + Type::Never(_) => {} + + // `[T; N]` and `[T]` is covariant over `T`. + Type::Array(ty) => self.prove(&ty.elem), + Type::Slice(ty) => self.prove(&ty.elem), + + Type::Tuple(ty) => { + for elem in &ty.elems { + self.prove(elem); + } + } + + // `*const T` is covariant over `T` + Type::Ptr(ty) if ty.const_token.is_some() => self.prove(&ty.elem), + + // `&T` is covariant over `T` and lifetime. + // + // Note that if we encounter `&'other_lt T`, then we still need to make sure the type + // is wellformed if `T` involves `&'lt`, so we defer to the compiler. + // + // This is to block cases like `ForLt!(for<'a> &'static &'a u32)`, as the presence of + // the type implies `'a: 'static` but this is unsound. + Type::Reference(ty) + if ty.mutability.is_none() && ty.lifetime.as_ref() == Some(self.0) => + { + self.prove(&ty.elem) + } + + // `&[mut] T` is covariant over lifetime. + // In case we have `&[mut] NoLifetime`, we don't need to do additional checks. + Type::Reference(ty) if !ty.elem.has_lifetime(self.0) => (), + + // No mention of lifetime at all, no need to perform compiler check. + ty if !ty.has_lifetime(self.0) => (), + + // Otherwise, we need to emit checks so that compiler can determine if the types are + // actually covariant. + ty => self.1.push(ty), + } + } +} + +pub(crate) fn for_lt(input: HigherRankedType) -> TokenStream { + let (ty, lifetime) = match input { + HigherRankedType::Explicit { lifetime, ty, .. } => (ty, lifetime), + HigherRankedType::Implicit { ty } => { + // If there's no explicit `for<'a>` binder, inject a synthetic `'__elided` lifetime + // and expand elided sites. + let lifetime = Lifetime { + apostrophe: Span::mixed_site(), + ident: format_ident!("__elided", span = Span::mixed_site()), + }; + (ty.expand_elided_lifetime(&lifetime), lifetime) + } + }; + + let mut prover = Prover(&lifetime, Vec::new()); + prover.prove(&ty); + + let mut proof = Vec::new(); + + // Emit proofs for every type that requires additional compiler help in proving covariance. + for (idx, required_proof) in prover.1.into_iter().enumerate() { + // Insert a proof that the type is well-formed. + // + // This is intended to workaround a Rust compiler soundness bug related to HRTB. + // https://github.com/rust-lang/rust/issues/152489 + // + // This needs to be a struct instead of fn to avoid the implied WF bounds. + let wf_proof_name = format_ident!("ProveWf{idx}"); + proof.push(quote!( + struct #wf_proof_name<#lifetime>( + ::core::marker::PhantomData<&#lifetime ()>, #required_proof + ); + )); + + // Insert a proof that the type is covariant. + let cov_proof_name = format_ident!("prove_covariant_{idx}"); + proof.push(quote!( + fn #cov_proof_name<'__short, '__long: '__short>( + long: #wf_proof_name<'__long> + ) -> #wf_proof_name<'__short> { + long + } + )); + } + + // Make sure that the type is wellformed when substituting lifetime with `'static`. + // + // Currently the Rust compiler doesn't check this, see the above `ProveWf` documentation. + // + // We prefer to use this way of proving WF-ness as it can work when generics are involved. + let ty_static = ty.replace_lifetime( + &lifetime, + &Lifetime { + apostrophe: Span::mixed_site(), + ident: format_ident!("static"), + }, + ); + + quote!( + ::kernel::types::for_lt::UnsafeForLtImpl::< + dyn for<#lifetime> ::kernel::types::for_lt::WithLt<#lifetime, Of = #ty>, + #ty_static, + { + #(#proof)* + + 0 + } + > + ) +} diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs index 2cfd59e0f9e7..4a48fabbc268 100644 --- a/rust/macros/lib.rs +++ b/rust/macros/lib.rs @@ -17,6 +17,7 @@ mod concat_idents; mod export; mod fmt; +mod for_lt; mod helpers; mod kunit; mod module; @@ -489,3 +490,15 @@ pub fn kunit_tests(attr: TokenStream, input: TokenStream) -> TokenStream { .unwrap_or_else(|e| e.into_compile_error()) .into() } + +/// Obtain a type that implements [`ForLt`] for the given higher-ranked type. +/// +/// Please refer to the documentation of the [`ForLt`] trait. +/// +/// [`ForLt`]: trait.ForLt.html +#[proc_macro] +// The macro shares the name with the trait. +#[allow(non_snake_case)] +pub fn ForLt(input: TokenStream) -> TokenStream { + for_lt::for_lt(parse_macro_input!(input)).into() +} -- cgit v1.2.3 From 4555291ddae9abe2c40a7eae192b1976b07a1fad Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Mon, 25 May 2026 22:21:10 +0200 Subject: rust: auxiliary: generalize Registration over ForLt Generalize Registration to Registration and Device::registration_data() to return Pin<&F::Of<'_>>. The stored 'static lifetime is shortened to the borrow lifetime of &self via ForLt::cast_ref; ForLt's covariance guarantee makes this sound. Reviewed-by: Greg Kroah-Hartman Reviewed-by: Alexandre Courbot Reviewed-by: Eliot Courtney Reviewed-by: Gary Guo Link: https://patch.msgid.link/20260525202921.124698-24-dakr@kernel.org [ Use PhantomData> instead of PhantomData<(fn(&'a ()) -> &'a (), F)>], which also gets us rid of #[allow(clippy::type_complexity)]. - Danilo ] Signed-off-by: Danilo Krummrich --- drivers/gpu/nova-core/driver.rs | 13 +++-- rust/kernel/auxiliary.rs | 107 ++++++++++++++++++++++++---------- samples/rust/rust_driver_auxiliary.rs | 19 +++--- 3 files changed, 95 insertions(+), 44 deletions(-) diff --git a/drivers/gpu/nova-core/driver.rs b/drivers/gpu/nova-core/driver.rs index fa898fe5c893..d3f2245ba2e0 100644 --- a/drivers/gpu/nova-core/driver.rs +++ b/drivers/gpu/nova-core/driver.rs @@ -3,7 +3,6 @@ use kernel::{ auxiliary, device::Core, - devres::Devres, dma::Device, dma::DmaMask, pci, @@ -21,6 +20,7 @@ use kernel::{ }, Arc, }, + types::ForLt, }; use crate::gpu::Gpu; @@ -29,10 +29,11 @@ use crate::gpu::Gpu; static AUXILIARY_ID_COUNTER: Atomic = Atomic::new(0); #[pin_data] -pub(crate) struct NovaCore { +pub(crate) struct NovaCore<'bound> { #[pin] pub(crate) gpu: Gpu, - _reg: Devres>, + #[allow(clippy::type_complexity)] + _reg: auxiliary::Registration<'bound, ForLt!(())>, } pub(crate) struct NovaCoreDriver; @@ -76,13 +77,13 @@ kernel::pci_device_table!( impl pci::Driver for NovaCoreDriver { type IdInfo = (); - type Data<'bound> = NovaCore; + type Data<'bound> = NovaCore<'bound>; const ID_TABLE: pci::IdTable = &PCI_TABLE; fn probe<'bound>( pdev: &'bound pci::Device>, _info: &'bound Self::IdInfo, - ) -> impl PinInit + 'bound { + ) -> impl PinInit, Error> + 'bound { pin_init::pin_init_scope(move || { dev_dbg!(pdev, "Probe Nova Core GPU driver.\n"); @@ -115,7 +116,7 @@ impl pci::Driver for NovaCoreDriver { }) } - fn unbind<'bound>(pdev: &'bound pci::Device>, this: Pin<&NovaCore>) { + fn unbind<'bound>(pdev: &'bound pci::Device>, this: Pin<&Self::Data<'bound>>) { this.gpu.unbind(pdev.as_ref()); } } diff --git a/rust/kernel/auxiliary.rs b/rust/kernel/auxiliary.rs index 7a1b1a7b7ca6..c42928d5a239 100644 --- a/rust/kernel/auxiliary.rs +++ b/rust/kernel/auxiliary.rs @@ -12,7 +12,7 @@ use crate::{ RawDeviceId, RawDeviceIdIndex, // }, - devres::Devres, + driver, error::{ from_result, @@ -20,6 +20,7 @@ use crate::{ }, prelude::*, types::{ + ForLt, ForeignOwnable, Opaque, // }, @@ -271,12 +272,16 @@ impl Device { /// Returns a pinned reference to the registration data set by the registering (parent) driver. /// - /// Returns [`EINVAL`] if `T` does not match the type used by the parent driver when calling + /// `F` is the [`ForLt`](trait@ForLt) encoding of the data type. The returned + /// reference has its lifetime shortened from `'static` to `&self`'s borrow lifetime via + /// [`ForLt::cast_ref`]. + /// + /// Returns [`EINVAL`] if `F` does not match the type used by the parent driver when calling /// [`Registration::new()`]. /// /// Returns [`ENOENT`] if no registration data has been set, e.g. when the device was /// registered by a C driver. - pub fn registration_data(&self) -> Result> { + pub fn registration_data(&self) -> Result>> { // SAFETY: By the type invariant, `self.as_raw()` is a valid `struct auxiliary_device`. let ptr = unsafe { (*self.as_raw()).registration_data_rust }; if ptr.is_null() { @@ -289,18 +294,23 @@ impl Device { // SAFETY: `ptr` is non-null and was set via `into_foreign()` in `Registration::new()`; // `RegistrationData` is `#[repr(C)]` with `type_id` at offset 0, so reading a `TypeId` - // at the start of the allocation is valid regardless of `T`. + // at the start of the allocation is valid regardless of `F`. let type_id = unsafe { ptr.cast::().read() }; - if type_id != TypeId::of::() { + if type_id != TypeId::of::() { return Err(EINVAL); } - // SAFETY: The `TypeId` check above confirms that the stored type is `T`; `ptr` remains - // valid until `Registration::drop()` calls `from_foreign()`. - let wrapper = unsafe { Pin::>>::borrow(ptr) }; + // SAFETY: The `TypeId` check above confirms that the stored type matches + // `F::Of<'static>`; `ptr` remains valid until `Registration::drop()` calls + // `from_foreign()`. + let wrapper = unsafe { Pin::>>>::borrow(ptr) }; // SAFETY: `data` is a structurally pinned field of `RegistrationData`. - Ok(unsafe { wrapper.map_unchecked(|w| &w.data) }) + let pinned: Pin<&F::Of<'_>> = unsafe { wrapper.map_unchecked(|w| &w.data) }; + + // SAFETY: The data was pinned when stored; `cast_ref` only shortens + // the lifetime, so the pinning guarantee is preserved. + Ok(unsafe { Pin::new_unchecked(F::cast_ref(pinned.get_ref())) }) } } @@ -389,43 +399,60 @@ struct RegistrationData { /// This type represents the registration of a [`struct auxiliary_device`]. When its parent device /// is unbound, the corresponding auxiliary device will be unregistered from the system. /// -/// The type parameter `T` is the type of the registration data owned by the registering (parent) -/// driver. It can be accessed by the auxiliary driver through -/// [`Device::registration_data()`]. +/// The type parameter `F` is a [`ForLt`](trait@ForLt) encoding of the registration +/// data type. For non-lifetime-parameterized types, use [`ForLt!(T)`](macro@ForLt). +/// The data can be accessed by the auxiliary driver through [`Device::registration_data()`]. /// /// # Invariants /// /// `self.adev` always holds a valid pointer to an initialized and registered /// [`struct auxiliary_device`] whose `registration_data_rust` field points to a -/// valid `Pin>>`. -pub struct Registration { +/// valid `Pin>>>`. +pub struct Registration<'a, F: ForLt + 'static> { adev: NonNull, - _data: PhantomData, + _phantom: PhantomData>, } -impl Registration { +impl<'a, F: ForLt> Registration<'a, F> +where + for<'b> F::Of<'b>: Send + Sync, +{ /// Create and register a new auxiliary device with the given registration data. /// /// The `data` is owned by the registration and can be accessed through the auxiliary device /// via [`Device::registration_data()`]. - pub fn new( - parent: &device::Device, + /// + /// # Safety + /// + /// The caller must not `mem::forget()` the returned [`Registration`] or otherwise prevent its + /// [`Drop`] implementation from running, since the registration data may contain borrowed + /// references that become invalid after `'a` ends. + /// + /// If the registration data is `'static`, use the safe [`Registration::new()`] instead. + pub unsafe fn new_with_lt( + parent: &'a device::Device, name: &CStr, id: u32, modname: &CStr, - data: impl PinInit, - ) -> Result> + data: impl PinInit, E>, + ) -> Result where Error: From, { let data = KBox::pin_init::( try_pin_init!(RegistrationData { - type_id: TypeId::of::(), + type_id: TypeId::of::(), data <- data, }), GFP_KERNEL, )?; + // SAFETY: `'a` is invariant (via `Registration`'s `PhantomData`). Lifetimes do not + // affect layout, so RegistrationData> and RegistrationData> + // have identical representation. + let data: Pin>>> = + unsafe { core::mem::transmute(data) }; + let boxed: KBox> = KBox::zeroed(GFP_KERNEL)?; let adev = boxed.get(); @@ -455,7 +482,9 @@ impl Registration { if ret != 0 { // SAFETY: `registration_data` was set above via `into_foreign()`. drop(unsafe { - Pin::>>::from_foreign((*adev).registration_data_rust) + Pin::>>>::from_foreign( + (*adev).registration_data_rust, + ) }); // SAFETY: `adev` is guaranteed to be a valid pointer to a @@ -467,18 +496,36 @@ impl Registration { // INVARIANT: The device will remain registered until `auxiliary_device_delete()` is // called, which happens in `Self::drop()`. - let reg = Self { + Ok(Self { // SAFETY: `adev` is guaranteed to be non-null, since the `KBox` was allocated // successfully. adev: unsafe { NonNull::new_unchecked(adev) }, - _data: PhantomData, - }; + _phantom: PhantomData, + }) + } - Devres::new::(parent, reg) + /// Create and register a new auxiliary device with `'static` registration data. + /// + /// Safe variant of [`Registration::new_with_lt()`] for registration data that does not contain + /// borrowed references. + pub fn new( + parent: &'a device::Device, + name: &CStr, + id: u32, + modname: &CStr, + data: impl PinInit, E>, + ) -> Result + where + F::Of<'a>: 'static, + Error: From, + { + // SAFETY: `F::Of<'a>: 'static` guarantees the data contains no borrowed references, + // so forgetting the `Registration` cannot cause use-after-free. + unsafe { Self::new_with_lt(parent, name, id, modname, data) } } } -impl Drop for Registration { +impl Drop for Registration<'_, F> { fn drop(&mut self) { // SAFETY: By the type invariant of `Self`, `self.adev.as_ptr()` is a valid registered // `struct auxiliary_device`. @@ -486,7 +533,7 @@ impl Drop for Registration { // SAFETY: `registration_data` was set in `new()` via `into_foreign()`. drop(unsafe { - Pin::>>::from_foreign( + Pin::>>>::from_foreign( (*self.adev.as_ptr()).registration_data_rust, ) }); @@ -500,7 +547,7 @@ impl Drop for Registration { } // SAFETY: A `Registration` of a `struct auxiliary_device` can be released from any thread. -unsafe impl Send for Registration {} +unsafe impl Send for Registration<'_, F> where for<'a> F::Of<'a>: Send {} // SAFETY: `Registration` does not expose any methods or fields that need synchronization. -unsafe impl Sync for Registration {} +unsafe impl Sync for Registration<'_, F> where for<'a> F::Of<'a>: Send {} diff --git a/samples/rust/rust_driver_auxiliary.rs b/samples/rust/rust_driver_auxiliary.rs index b30a4d5cdf8a..e3e811a14110 100644 --- a/samples/rust/rust_driver_auxiliary.rs +++ b/samples/rust/rust_driver_auxiliary.rs @@ -10,10 +10,10 @@ use kernel::{ Bound, Core, // }, - devres::Devres, driver, pci, prelude::*, + types::ForLt, InPlaceModule, // }; @@ -55,9 +55,12 @@ struct Data { index: u32, } -struct ParentDriver { - _reg0: Devres>, - _reg1: Devres>, +struct ParentDriver; + +#[allow(clippy::type_complexity)] +struct ParentData<'bound> { + _reg0: auxiliary::Registration<'bound, ForLt!(Data)>, + _reg1: auxiliary::Registration<'bound, ForLt!(Data)>, } kernel::pci_device_table!( @@ -69,15 +72,15 @@ kernel::pci_device_table!( impl pci::Driver for ParentDriver { type IdInfo = (); - type Data<'bound> = Self; + type Data<'bound> = ParentData<'bound>; const ID_TABLE: pci::IdTable = &PCI_TABLE; fn probe<'bound>( pdev: &'bound pci::Device>, _info: &'bound Self::IdInfo, - ) -> impl PinInit + 'bound { - Ok(Self { + ) -> impl PinInit, Error> + 'bound { + Ok(ParentData { _reg0: auxiliary::Registration::new( pdev.as_ref(), AUXILIARY_NAME, @@ -101,7 +104,7 @@ impl ParentDriver { let dev = adev.parent(); let pdev: &pci::Device = dev.try_into()?; - let data = adev.registration_data::()?; + let data = adev.registration_data::()?; dev_info!( dev, -- cgit v1.2.3 From d18f3646184fc805d213fc049fc3b5d9fb9a6a27 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Mon, 25 May 2026 22:21:11 +0200 Subject: samples: rust: rust_driver_auxiliary: showcase lifetime-bound registration data Make the Data struct lifetime-parameterized, storing a reference to the parent pci::Device. This demonstrates that registration data can hold device resources tied to the parent driver's lifetime. In connect(), retrieve the parent PCI device from the registration data rather than casting through adev.parent(). Reviewed-by: Eliot Courtney Reviewed-by: Gary Guo Reviewed-by: Greg Kroah-Hartman Reviewed-by: Alexandre Courbot Link: https://patch.msgid.link/20260525202921.124698-25-dakr@kernel.org Signed-off-by: Danilo Krummrich --- samples/rust/rust_driver_auxiliary.rs | 58 +++++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 23 deletions(-) diff --git a/samples/rust/rust_driver_auxiliary.rs b/samples/rust/rust_driver_auxiliary.rs index e3e811a14110..2c1351040e45 100644 --- a/samples/rust/rust_driver_auxiliary.rs +++ b/samples/rust/rust_driver_auxiliary.rs @@ -51,16 +51,17 @@ impl auxiliary::Driver for AuxiliaryDriver { } } -struct Data { +struct Data<'bound> { index: u32, + parent: &'bound pci::Device, } struct ParentDriver; #[allow(clippy::type_complexity)] struct ParentData<'bound> { - _reg0: auxiliary::Registration<'bound, ForLt!(Data)>, - _reg1: auxiliary::Registration<'bound, ForLt!(Data)>, + _reg0: auxiliary::Registration<'bound, ForLt!(Data<'_>)>, + _reg1: auxiliary::Registration<'bound, ForLt!(Data<'_>)>, } kernel::pci_device_table!( @@ -81,33 +82,44 @@ impl pci::Driver for ParentDriver { _info: &'bound Self::IdInfo, ) -> impl PinInit, Error> + 'bound { Ok(ParentData { - _reg0: auxiliary::Registration::new( - pdev.as_ref(), - AUXILIARY_NAME, - 0, - MODULE_NAME, - Data { index: 0 }, - )?, - _reg1: auxiliary::Registration::new( - pdev.as_ref(), - AUXILIARY_NAME, - 1, - MODULE_NAME, - Data { index: 1 }, - )?, + // SAFETY: `ParentData` is the driver's private data, which is dropped when the + // device is unbound; i.e. `mem::forget()` is never called on it. + _reg0: unsafe { + auxiliary::Registration::new_with_lt( + pdev.as_ref(), + AUXILIARY_NAME, + 0, + MODULE_NAME, + Data { + index: 0, + parent: pdev, + }, + )? + }, + // SAFETY: See `_reg0` above. + _reg1: unsafe { + auxiliary::Registration::new_with_lt( + pdev.as_ref(), + AUXILIARY_NAME, + 1, + MODULE_NAME, + Data { + index: 1, + parent: pdev, + }, + )? + }, }) } } impl ParentDriver { fn connect(adev: &auxiliary::Device) -> Result { - let dev = adev.parent(); - let pdev: &pci::Device = dev.try_into()?; - - let data = adev.registration_data::()?; + let data = adev.registration_data::)>()?; + let pdev = data.parent; dev_info!( - dev, + pdev, "Connect auxiliary {} with parent: VendorID={}, DeviceID={:#x}\n", adev.id(), pdev.vendor_id(), @@ -115,7 +127,7 @@ impl ParentDriver { ); dev_info!( - dev, + pdev, "Connected to auxiliary device with index {}.\n", data.index ); -- cgit v1.2.3 From 016267b521b18529c977c9eca9597a1669c3d73c Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Tue, 26 May 2026 02:04:41 +0200 Subject: rust: devres: add 'static bound to Devres Devres::new() registers a callback with the C devres subsystem via devres_node_add(). If the Devres is leaked (e.g. via core::mem::forget(), which is safe), its Drop impl never runs, and the devres release callback will revoke the inner Revocable on device unbind, which drops T in place. If T contains non-'static references, those may be dangling by that point. Add a 'static bound to prevent storing types with borrowed data in Devres. Fixes: 76c01ded724b ("rust: add devres abstraction") Reviewed-by: Alexandre Courbot Reviewed-by: Eliot Courtney Link: https://patch.msgid.link/20260526000447.350558-1-dakr@kernel.org Signed-off-by: Danilo Krummrich --- rust/kernel/devres.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rust/kernel/devres.rs b/rust/kernel/devres.rs index 82cbd8b969fb..11ce500e9b76 100644 --- a/rust/kernel/devres.rs +++ b/rust/kernel/devres.rs @@ -122,7 +122,7 @@ struct Inner { /// # Ok(()) /// # } /// ``` -pub struct Devres { +pub struct Devres { dev: ARef, inner: Arc>, } @@ -184,7 +184,7 @@ mod base { } } -impl Devres { +impl Devres { /// Creates a new [`Devres`] instance of the given `data`. /// /// The `data` encapsulated within the returned `Devres` instance' `data` will be @@ -349,7 +349,7 @@ unsafe impl Send for Devres {} // SAFETY: `Devres` can be shared with any task, if `T: Sync`. unsafe impl Sync for Devres {} -impl Drop for Devres { +impl Drop for Devres { fn drop(&mut self) { // SAFETY: When `drop` runs, it is guaranteed that nobody is accessing the revocable data // anymore, hence it is safe not to wait for the grace period to finish. -- cgit v1.2.3 From 1947229f5f2a8d4ecf8c971aca68a1242bb7b37c Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Tue, 5 May 2026 15:37:21 +0200 Subject: amba: use generic driver_override infrastructure When a driver is probed through __driver_attach(), the bus' match() callback is called without the device lock held, thus accessing the driver_override field without a lock, which can cause a UAF. Fix this by using the driver-core driver_override infrastructure taking care of proper locking internally. Note that calling match() from __driver_attach() without the device lock held is intentional. [1] Link: https://lore.kernel.org/driver-core/DGRGTIRHA62X.3RY09D9SOK77P@kernel.org/ [1] Reported-by: Gui-Dong Han Closes: https://bugzilla.kernel.org/show_bug.cgi?id=220789 Fixes: 3cf385713460 ("ARM: 8256/1: driver coamba: add device binding path 'driver_override'") Reviewed-by: Greg Kroah-Hartman Link: https://patch.msgid.link/20260505133935.3772495-2-dakr@kernel.org Signed-off-by: Danilo Krummrich --- drivers/amba/bus.c | 37 ++++++------------------------------- include/linux/amba/bus.h | 5 ----- 2 files changed, 6 insertions(+), 36 deletions(-) diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c index 6d479caf89cb..d721d64a9858 100644 --- a/drivers/amba/bus.c +++ b/drivers/amba/bus.c @@ -82,33 +82,6 @@ static void amba_put_disable_pclk(struct amba_device *pcdev) } -static ssize_t driver_override_show(struct device *_dev, - struct device_attribute *attr, char *buf) -{ - struct amba_device *dev = to_amba_device(_dev); - ssize_t len; - - device_lock(_dev); - len = sprintf(buf, "%s\n", dev->driver_override); - device_unlock(_dev); - return len; -} - -static ssize_t driver_override_store(struct device *_dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct amba_device *dev = to_amba_device(_dev); - int ret; - - ret = driver_set_override(_dev, &dev->driver_override, buf, count); - if (ret) - return ret; - - return count; -} -static DEVICE_ATTR_RW(driver_override); - #define amba_attr_func(name,fmt,arg...) \ static ssize_t name##_show(struct device *_dev, \ struct device_attribute *attr, char *buf) \ @@ -126,7 +99,6 @@ amba_attr_func(resource, "\t%016llx\t%016llx\t%016lx\n", static struct attribute *amba_dev_attrs[] = { &dev_attr_id.attr, &dev_attr_resource.attr, - &dev_attr_driver_override.attr, NULL, }; ATTRIBUTE_GROUPS(amba_dev); @@ -209,10 +181,11 @@ static int amba_match(struct device *dev, const struct device_driver *drv) { struct amba_device *pcdev = to_amba_device(dev); const struct amba_driver *pcdrv = to_amba_driver(drv); + int ret; mutex_lock(&pcdev->periphid_lock); if (!pcdev->periphid) { - int ret = amba_read_periphid(pcdev); + ret = amba_read_periphid(pcdev); /* * Returning any error other than -EPROBE_DEFER from bus match @@ -230,8 +203,9 @@ static int amba_match(struct device *dev, const struct device_driver *drv) mutex_unlock(&pcdev->periphid_lock); /* When driver_override is set, only bind to the matching driver */ - if (pcdev->driver_override) - return !strcmp(pcdev->driver_override, drv->name); + ret = device_match_driver_override(dev, drv); + if (ret >= 0) + return ret; return amba_lookup(pcdrv->id_table, pcdev) != NULL; } @@ -436,6 +410,7 @@ static const struct dev_pm_ops amba_pm = { const struct bus_type amba_bustype = { .name = "amba", .dev_groups = amba_dev_groups, + .driver_override = true, .match = amba_match, .uevent = amba_uevent, .probe = amba_probe, diff --git a/include/linux/amba/bus.h b/include/linux/amba/bus.h index 9946276aff73..6c54d5c0d21f 100644 --- a/include/linux/amba/bus.h +++ b/include/linux/amba/bus.h @@ -71,11 +71,6 @@ struct amba_device { unsigned int cid; struct amba_cs_uci_id uci; unsigned int irq[AMBA_NR_IRQS]; - /* - * Driver name to force a match. Do not set directly, because core - * frees it. Use driver_set_override() to set or clear it. - */ - const char *driver_override; }; struct amba_driver { -- cgit v1.2.3 From d541aa1897f67f4f14c805785bff894bcc61dca1 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Tue, 5 May 2026 15:37:22 +0200 Subject: cdx: use generic driver_override infrastructure When a driver is probed through __driver_attach(), the bus' match() callback is called without the device lock held, thus accessing the driver_override field without a lock, which can cause a UAF. Fix this by using the driver-core driver_override infrastructure taking care of proper locking internally. Note that calling match() from __driver_attach() without the device lock held is intentional. [1] Link: https://lore.kernel.org/driver-core/DGRGTIRHA62X.3RY09D9SOK77P@kernel.org/ [1] Reported-by: Gui-Dong Han Closes: https://bugzilla.kernel.org/show_bug.cgi?id=220789 Fixes: 2959ab247061 ("cdx: add the cdx bus driver") Reviewed-by: Greg Kroah-Hartman Link: https://patch.msgid.link/20260505133935.3772495-3-dakr@kernel.org Signed-off-by: Danilo Krummrich --- drivers/cdx/cdx.c | 40 +++++----------------------------------- include/linux/cdx/cdx_bus.h | 4 ---- 2 files changed, 5 insertions(+), 39 deletions(-) diff --git a/drivers/cdx/cdx.c b/drivers/cdx/cdx.c index 9196dc50a48d..d3d230247262 100644 --- a/drivers/cdx/cdx.c +++ b/drivers/cdx/cdx.c @@ -156,8 +156,6 @@ static int cdx_unregister_device(struct device *dev, } else { cdx_destroy_res_attr(cdx_dev, MAX_CDX_DEV_RESOURCES); debugfs_remove_recursive(cdx_dev->debugfs_dir); - kfree(cdx_dev->driver_override); - cdx_dev->driver_override = NULL; } /* @@ -268,6 +266,7 @@ static int cdx_bus_match(struct device *dev, const struct device_driver *drv) const struct cdx_driver *cdx_drv = to_cdx_driver(drv); const struct cdx_device_id *found_id = NULL; const struct cdx_device_id *ids; + int ret; if (cdx_dev->is_bus) return false; @@ -275,7 +274,8 @@ static int cdx_bus_match(struct device *dev, const struct device_driver *drv) ids = cdx_drv->match_id_table; /* When driver_override is set, only bind to the matching driver */ - if (cdx_dev->driver_override && strcmp(cdx_dev->driver_override, drv->name)) + ret = device_match_driver_override(dev, drv); + if (ret == 0) return false; found_id = cdx_match_id(ids, cdx_dev); @@ -289,7 +289,7 @@ static int cdx_bus_match(struct device *dev, const struct device_driver *drv) */ if (!found_id->override_only) return true; - if (cdx_dev->driver_override) + if (ret > 0) return true; ids = found_id + 1; @@ -453,36 +453,6 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR_RO(modalias); -static ssize_t driver_override_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct cdx_device *cdx_dev = to_cdx_device(dev); - int ret; - - if (WARN_ON(dev->bus != &cdx_bus_type)) - return -EINVAL; - - ret = driver_set_override(dev, &cdx_dev->driver_override, buf, count); - if (ret) - return ret; - - return count; -} - -static ssize_t driver_override_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct cdx_device *cdx_dev = to_cdx_device(dev); - ssize_t len; - - device_lock(dev); - len = sysfs_emit(buf, "%s\n", cdx_dev->driver_override); - device_unlock(dev); - return len; -} -static DEVICE_ATTR_RW(driver_override); - static ssize_t enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { @@ -552,7 +522,6 @@ static struct attribute *cdx_dev_attrs[] = { &dev_attr_class.attr, &dev_attr_revision.attr, &dev_attr_modalias.attr, - &dev_attr_driver_override.attr, NULL, }; @@ -646,6 +615,7 @@ ATTRIBUTE_GROUPS(cdx_bus); const struct bus_type cdx_bus_type = { .name = "cdx", + .driver_override = true, .match = cdx_bus_match, .probe = cdx_probe, .remove = cdx_remove, diff --git a/include/linux/cdx/cdx_bus.h b/include/linux/cdx/cdx_bus.h index b1ba97f6c9ad..f54770f110bc 100644 --- a/include/linux/cdx/cdx_bus.h +++ b/include/linux/cdx/cdx_bus.h @@ -137,9 +137,6 @@ struct cdx_controller { * @enabled: is this bus enabled * @msi_dev_id: MSI Device ID associated with CDX device * @num_msi: Number of MSI's supported by the device - * @driver_override: driver name to force a match; do not set directly, - * because core frees it; use driver_set_override() to - * set or clear it. * @irqchip_lock: lock to synchronize irq/msi configuration * @msi_write_pending: MSI write pending for this device */ @@ -165,7 +162,6 @@ struct cdx_device { bool enabled; u32 msi_dev_id; u32 num_msi; - const char *driver_override; struct mutex irqchip_lock; bool msi_write_pending; }; -- cgit v1.2.3 From 331d8900121a1d74ecd45cd2db742ddcb5a0a565 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Tue, 5 May 2026 15:37:23 +0200 Subject: Drivers: hv: vmbus: use generic driver_override infrastructure When a driver is probed through __driver_attach(), the bus' match() callback is called without the device lock held, thus accessing the driver_override field without a lock, which can cause a UAF. Fix this by using the driver-core driver_override infrastructure taking care of proper locking internally. Note that calling match() from __driver_attach() without the device lock held is intentional. [1] Tested-by: Michael Kelley Reviewed-by: Michael Kelley Link: https://lore.kernel.org/driver-core/DGRGTIRHA62X.3RY09D9SOK77P@kernel.org/ [1] Reported-by: Gui-Dong Han Closes: https://bugzilla.kernel.org/show_bug.cgi?id=220789 Fixes: d765edbb301c ("vmbus: add driver_override support") Reviewed-by: Greg Kroah-Hartman Link: https://patch.msgid.link/20260505133935.3772495-4-dakr@kernel.org Signed-off-by: Danilo Krummrich --- drivers/hv/vmbus_drv.c | 43 ++++++++++--------------------------------- include/linux/hyperv.h | 5 ----- 2 files changed, 10 insertions(+), 38 deletions(-) diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index d28ff45d4cfd..acfb579828c5 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -538,34 +538,6 @@ static ssize_t device_show(struct device *dev, } static DEVICE_ATTR_RO(device); -static ssize_t driver_override_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct hv_device *hv_dev = device_to_hv_device(dev); - int ret; - - ret = driver_set_override(dev, &hv_dev->driver_override, buf, count); - if (ret) - return ret; - - return count; -} - -static ssize_t driver_override_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct hv_device *hv_dev = device_to_hv_device(dev); - ssize_t len; - - device_lock(dev); - len = sysfs_emit(buf, "%s\n", hv_dev->driver_override); - device_unlock(dev); - - return len; -} -static DEVICE_ATTR_RW(driver_override); - /* Set up per device attributes in /sys/bus/vmbus/devices/ */ static struct attribute *vmbus_dev_attrs[] = { &dev_attr_id.attr, @@ -596,7 +568,6 @@ static struct attribute *vmbus_dev_attrs[] = { &dev_attr_channel_vp_mapping.attr, &dev_attr_vendor.attr, &dev_attr_device.attr, - &dev_attr_driver_override.attr, NULL, }; @@ -708,9 +679,11 @@ static const struct hv_vmbus_device_id *hv_vmbus_get_id(const struct hv_driver * { const guid_t *guid = &dev->dev_type; const struct hv_vmbus_device_id *id; + int ret; - /* When driver_override is set, only bind to the matching driver */ - if (dev->driver_override && strcmp(dev->driver_override, drv->name)) + /* If a driver override is set, only bind to the matching driver */ + ret = device_match_driver_override(&dev->device, &drv->driver); + if (ret == 0) return NULL; /* Look at the dynamic ids first, before the static ones */ @@ -718,8 +691,11 @@ static const struct hv_vmbus_device_id *hv_vmbus_get_id(const struct hv_driver * if (!id) id = hv_vmbus_dev_match(drv->id_table, guid); - /* driver_override will always match, send a dummy id */ - if (!id && dev->driver_override) + /* + * If there's a matching driver override, this function should succeed, + * thus return a dummy device ID if no matching ID is found. + */ + if (!id && ret > 0) id = &vmbus_device_null; return id; @@ -1021,6 +997,7 @@ static const struct dev_pm_ops vmbus_pm = { /* The one and only one */ static const struct bus_type hv_bus = { .name = "vmbus", + .driver_override = true, .match = vmbus_match, .shutdown = vmbus_shutdown, .remove = vmbus_remove, diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 964f1be8150c..c054d7eff622 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -1272,11 +1272,6 @@ struct hv_device { u16 device_id; struct device device; - /* - * Driver name to force a match. Do not set directly, because core - * frees it. Use driver_set_override() to set or clear it. - */ - const char *driver_override; struct vmbus_channel *channel; struct kset *channels_kset; -- cgit v1.2.3 From 55ced13c42921714e90f8fae94b6ed803330dc6a Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Tue, 5 May 2026 15:37:24 +0200 Subject: rpmsg: use generic driver_override infrastructure When a driver is probed through __driver_attach(), the bus' match() callback is called without the device lock held, thus accessing the driver_override field without a lock, which can cause a UAF. Fix this by using the driver-core driver_override infrastructure taking care of proper locking internally. Note that calling match() from __driver_attach() without the device lock held is intentional. [1] Link: https://lore.kernel.org/driver-core/DGRGTIRHA62X.3RY09D9SOK77P@kernel.org/ [1] Reported-by: Gui-Dong Han Closes: https://bugzilla.kernel.org/show_bug.cgi?id=220789 Fixes: e95060478244 ("rpmsg: Introduce a driver override mechanism") Reviewed-by: Mathieu Poirier Reviewed-by: Greg Kroah-Hartman Link: https://patch.msgid.link/20260505133935.3772495-5-dakr@kernel.org Signed-off-by: Danilo Krummrich --- drivers/rpmsg/qcom_glink_native.c | 2 -- drivers/rpmsg/rpmsg_core.c | 43 +++++++-------------------------------- drivers/rpmsg/virtio_rpmsg_bus.c | 1 - include/linux/rpmsg.h | 4 ---- 4 files changed, 7 insertions(+), 43 deletions(-) diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c index 401a4ece0c97..d9d4468e4cbd 100644 --- a/drivers/rpmsg/qcom_glink_native.c +++ b/drivers/rpmsg/qcom_glink_native.c @@ -1626,7 +1626,6 @@ static void qcom_glink_rpdev_release(struct device *dev) { struct rpmsg_device *rpdev = to_rpmsg_device(dev); - kfree(rpdev->driver_override); kfree(rpdev); } @@ -1862,7 +1861,6 @@ static void qcom_glink_device_release(struct device *dev) /* Release qcom_glink_alloc_channel() reference */ kref_put(&channel->refcount, qcom_glink_channel_release); - kfree(rpdev->driver_override); kfree(rpdev); } diff --git a/drivers/rpmsg/rpmsg_core.c b/drivers/rpmsg/rpmsg_core.c index e7f7831d37f8..c56f69c22e42 100644 --- a/drivers/rpmsg/rpmsg_core.c +++ b/drivers/rpmsg/rpmsg_core.c @@ -358,33 +358,6 @@ rpmsg_show_attr(src, src, "0x%x\n"); rpmsg_show_attr(dst, dst, "0x%x\n"); rpmsg_show_attr(announce, announce ? "true" : "false", "%s\n"); -static ssize_t driver_override_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct rpmsg_device *rpdev = to_rpmsg_device(dev); - int ret; - - ret = driver_set_override(dev, &rpdev->driver_override, buf, count); - if (ret) - return ret; - - return count; -} - -static ssize_t driver_override_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct rpmsg_device *rpdev = to_rpmsg_device(dev); - ssize_t len; - - device_lock(dev); - len = sysfs_emit(buf, "%s\n", rpdev->driver_override); - device_unlock(dev); - return len; -} -static DEVICE_ATTR_RW(driver_override); - static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -405,7 +378,6 @@ static struct attribute *rpmsg_dev_attrs[] = { &dev_attr_dst.attr, &dev_attr_src.attr, &dev_attr_announce.attr, - &dev_attr_driver_override.attr, NULL, }; ATTRIBUTE_GROUPS(rpmsg_dev); @@ -424,9 +396,11 @@ static int rpmsg_dev_match(struct device *dev, const struct device_driver *drv) const struct rpmsg_driver *rpdrv = to_rpmsg_driver(drv); const struct rpmsg_device_id *ids = rpdrv->id_table; unsigned int i; + int ret; - if (rpdev->driver_override) - return !strcmp(rpdev->driver_override, drv->name); + ret = device_match_driver_override(dev, drv); + if (ret >= 0) + return ret; if (ids) for (i = 0; ids[i].name[0]; i++) @@ -535,6 +509,7 @@ static const struct bus_type rpmsg_bus = { .name = "rpmsg", .match = rpmsg_dev_match, .dev_groups = rpmsg_dev_groups, + .driver_override = true, .uevent = rpmsg_uevent, .probe = rpmsg_dev_probe, .remove = rpmsg_dev_remove, @@ -560,11 +535,9 @@ int rpmsg_register_device_override(struct rpmsg_device *rpdev, device_initialize(dev); if (driver_override) { - ret = driver_set_override(dev, &rpdev->driver_override, - driver_override, - strlen(driver_override)); + ret = device_set_driver_override(dev, driver_override); if (ret) { - dev_err(dev, "device_set_override failed: %d\n", ret); + dev_err(dev, "device_set_driver_override() failed: %d\n", ret); put_device(dev); return ret; } @@ -573,8 +546,6 @@ int rpmsg_register_device_override(struct rpmsg_device *rpdev, ret = device_add(dev); if (ret) { dev_err(dev, "device_add failed: %d\n", ret); - kfree(rpdev->driver_override); - rpdev->driver_override = NULL; put_device(dev); } diff --git a/drivers/rpmsg/virtio_rpmsg_bus.c b/drivers/rpmsg/virtio_rpmsg_bus.c index 5ae15111fb4f..1b8bb05924af 100644 --- a/drivers/rpmsg/virtio_rpmsg_bus.c +++ b/drivers/rpmsg/virtio_rpmsg_bus.c @@ -374,7 +374,6 @@ static void virtio_rpmsg_release_device(struct device *dev) struct rpmsg_device *rpdev = to_rpmsg_device(dev); struct virtio_rpmsg_channel *vch = to_virtio_rpmsg_channel(rpdev); - kfree(rpdev->driver_override); kfree(vch); } diff --git a/include/linux/rpmsg.h b/include/linux/rpmsg.h index 83266ce14642..2e40eb54155e 100644 --- a/include/linux/rpmsg.h +++ b/include/linux/rpmsg.h @@ -41,9 +41,6 @@ struct rpmsg_channel_info { * rpmsg_device - device that belong to the rpmsg bus * @dev: the device struct * @id: device id (used to match between rpmsg drivers and devices) - * @driver_override: driver name to force a match; do not set directly, - * because core frees it; use driver_set_override() to - * set or clear it. * @src: local address * @dst: destination address * @ept: the rpmsg endpoint of this channel @@ -53,7 +50,6 @@ struct rpmsg_channel_info { struct rpmsg_device { struct device dev; struct rpmsg_device_id id; - const char *driver_override; u32 src; u32 dst; struct rpmsg_endpoint *ept; -- cgit v1.2.3 From 46def663dd34da36464ba059f7cfeacf29d98e5e Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Tue, 5 May 2026 15:37:25 +0200 Subject: driver core: remove driver_set_override() All buses have been converted from driver_set_override() to the generic driver_override infrastructure introduced in commit cb3d1049f4ea ("driver core: generalize driver_override in struct device"). Buses now either opt into the generic sysfs callbacks via the bus_type::driver_override flag, or use device_set_driver_override() / __device_set_driver_override() directly. Thus, remove the now-unused driver_set_override() helper. Link: https://bugzilla.kernel.org/show_bug.cgi?id=220789 Reviewed-by: Greg Kroah-Hartman Link: https://patch.msgid.link/20260505133935.3772495-6-dakr@kernel.org Signed-off-by: Danilo Krummrich --- drivers/base/driver.c | 75 ------------------------------------------- include/linux/device/driver.h | 2 -- 2 files changed, 77 deletions(-) diff --git a/drivers/base/driver.c b/drivers/base/driver.c index c5ebf1fdad75..5d9c39081339 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -30,81 +30,6 @@ static struct device *next_device(struct klist_iter *i) return dev; } -/** - * driver_set_override() - Helper to set or clear driver override. - * @dev: Device to change - * @override: Address of string to change (e.g. &device->driver_override); - * The contents will be freed and hold newly allocated override. - * @s: NUL-terminated string, new driver name to force a match, pass empty - * string to clear it ("" or "\n", where the latter is only for sysfs - * interface). - * @len: length of @s - * - * Helper to set or clear driver override in a device, intended for the cases - * when the driver_override field is allocated by driver/bus code. - * - * Returns: 0 on success or a negative error code on failure. - */ -int driver_set_override(struct device *dev, const char **override, - const char *s, size_t len) -{ - const char *new, *old; - char *cp; - - if (!override || !s) - return -EINVAL; - - /* - * The stored value will be used in sysfs show callback (sysfs_emit()), - * which has a length limit of PAGE_SIZE and adds a trailing newline. - * Thus we can store one character less to avoid truncation during sysfs - * show. - */ - if (len >= (PAGE_SIZE - 1)) - return -EINVAL; - - /* - * Compute the real length of the string in case userspace sends us a - * bunch of \0 characters like python likes to do. - */ - len = strlen(s); - - if (!len) { - /* Empty string passed - clear override */ - device_lock(dev); - old = *override; - *override = NULL; - device_unlock(dev); - kfree(old); - - return 0; - } - - cp = strnchr(s, len, '\n'); - if (cp) - len = cp - s; - - new = kstrndup(s, len, GFP_KERNEL); - if (!new) - return -ENOMEM; - - device_lock(dev); - old = *override; - if (cp != s) { - *override = new; - } else { - /* "\n" passed - clear override */ - kfree(new); - *override = NULL; - } - device_unlock(dev); - - kfree(old); - - return 0; -} -EXPORT_SYMBOL_GPL(driver_set_override); - /** * driver_for_each_device - Iterator for devices bound to a driver. * @drv: Driver we're iterating. diff --git a/include/linux/device/driver.h b/include/linux/device/driver.h index 2fb054868049..38048e74d10a 100644 --- a/include/linux/device/driver.h +++ b/include/linux/device/driver.h @@ -160,8 +160,6 @@ int __must_check driver_create_file(const struct device_driver *driver, void driver_remove_file(const struct device_driver *driver, const struct driver_attribute *attr); -int driver_set_override(struct device *dev, const char **override, - const char *s, size_t len); int __must_check driver_for_each_device(struct device_driver *drv, struct device *start, void *data, device_iter_t fn); struct device *driver_find_device(const struct device_driver *drv, -- cgit v1.2.3 From fda8355f13ea3c0f9499acdeff3024995b474948 Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Mon, 1 Jun 2026 20:56:31 -0700 Subject: driver core: Use system_percpu_wq instead of system_wq Commit 1137838865bf ("driver core: Use mod_delayed_work to prevent lost deferred probe work") added a use of system_wq, which is deprecated in favor of system_percpu_wq added by commit 128ea9f6ccfb ("workqueue: Add system_percpu_wq and system_dfl_wq"). An upcoming warning in the workqueue tree flags this with: workqueue: work func deferred_probe_timeout_work_func enqueued on deprecated workqueue. Use system_{percpu|dfl}_wq instead. Switch to system_percpu_wq to clear up the warning. Fixes: 1137838865bf ("driver core: Use mod_delayed_work to prevent lost deferred probe work") Signed-off-by: Nathan Chancellor Reviewed-by: Rafael J. Wysocki (Intel) Link: https://patch.msgid.link/20260601-driver-core-fix-system_wq-warning-v1-1-f9001a70ee25@kernel.org Signed-off-by: Danilo Krummrich --- drivers/base/dd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/base/dd.c b/drivers/base/dd.c index a8ca2092905e..60c005223844 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -324,7 +324,7 @@ void deferred_probe_extend_timeout(void) * start a new one. */ if (delayed_work_pending(&deferred_probe_timeout_work) && - mod_delayed_work(system_wq, &deferred_probe_timeout_work, + mod_delayed_work(system_percpu_wq, &deferred_probe_timeout_work, secs_to_jiffies(driver_deferred_probe_timeout))) pr_debug("Extended deferred probe timeout by %d secs\n", driver_deferred_probe_timeout); -- cgit v1.2.3 From d3ec78f8f8d48a04a9fac38d47275c34645e5103 Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Fri, 29 May 2026 15:09:06 +0000 Subject: firmware_loader: Fix recursive lock in device_cache_fw_images() A recursive locking deadlock can occur in the firmware loader's power management notification handler. During system suspend or hibernation preparation, fw_pm_notify() calls device_cache_fw_images(). This function acquires fw_lock to set the firmware cache state to FW_LOADER_START_CACHE and then iterates over all devices using dpm_for_each_dev() while still holding the lock. For each device, dev_cache_fw_image() schedules asynchronous work to cache the firmware. If memory allocation for the async work entry fails (e.g., in out-of-memory conditions), async_schedule_node_domain() falls back to executing the work function synchronously in the current thread. The synchronous execution path (__async_dev_cache_fw_image() -> cache_firmware() -> request_firmware() -> assign_fw()) attempts to acquire fw_lock again. Since the current thread already holds fw_lock, this results in a recursive locking deadlock. Fix this by releasing fw_lock immediately after updating the cache state and before calling dpm_for_each_dev(). The lock is only needed to protect the state update. Concurrent firmware requests will correctly see the FW_LOADER_START_CACHE state and use the piggyback mechanism, which is independently protected by its own fwc->name_lock. Fixes: ac39b3ea73aa ("firmware loader: let caching firmware piggyback on loading firmware") Assisted-by: Gemini:gemini-3.1-pro-preview Gemini:gemini-3-flash-preview syzbot Reported-by: syzbot+e70e4c6f6eee43357ba7@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=e70e4c6f6eee43357ba7 Link: https://syzkaller.appspot.com/ai_job?id=8b4af9fd-24af-423f-8acb-1159fd34c1a5 Signed-off-by: Dmitry Vyukov Link: https://patch.msgid.link/48b092a5-f49d-48a4-95f4-f65bebfc6bc3@mail.kernel.org Signed-off-by: Danilo Krummrich --- drivers/base/firmware_loader/main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/base/firmware_loader/main.c b/drivers/base/firmware_loader/main.c index a11b30dda23b..c96312ac2be7 100644 --- a/drivers/base/firmware_loader/main.c +++ b/drivers/base/firmware_loader/main.c @@ -1503,9 +1503,10 @@ static void device_cache_fw_images(void) mutex_lock(&fw_lock); fwc->state = FW_LOADER_START_CACHE; - dpm_for_each_dev(NULL, dev_cache_fw_image); mutex_unlock(&fw_lock); + dpm_for_each_dev(NULL, dev_cache_fw_image); + /* wait for completion of caching firmware for all devices */ async_synchronize_full_domain(&fw_cache_domain); -- cgit v1.2.3 From cd6e95e7ab29c4f67475e3eafef6f836f06eb0e8 Mon Sep 17 00:00:00 2001 From: Shashank Balaji Date: Mon, 18 May 2026 19:19:57 +0900 Subject: soc/tegra: cbb: Move driver registration from pure_initcall to core_initcall Commit "driver core: platform: set mod_name in driver registration" will set struct device_driver's mod_name member for platform driver registration. For a driver to be registered with its mod_name set, module_kset needs to be initialized, which currently happens in a subsys_initcall in param_sysfs_init(). The tegra cbb drivers register themselves before module_kset init, in a pure_initcall. This works currently because lookup_or_create_module_kobject(), which dereferences module_kset via kset_find_obj(), is not called if mod_name is not set, which is the case now. So in preparation for the commit "driver core: platform: set mod_name in driver registration", move tegra cbb driver registration to core_initcall level, and commit "kernel: param: initialize module_kset in a pure_initcall" will move module_kset init to pure_initcall level, ensuring module_kset init happens before tegra cbb driver registration. Suggested-by: Gary Guo Acked-by: Sumit Gupta Co-developed-by: Rahul Bukte Signed-off-by: Rahul Bukte Signed-off-by: Shashank Balaji Reviewed-by: Gary Guo Acked-by: Thierry Reding Link: https://patch.msgid.link/20260518-acpi_mod_name-v5-1-705ccc430885@sony.com Signed-off-by: Danilo Krummrich --- drivers/soc/tegra/cbb/tegra194-cbb.c | 2 +- drivers/soc/tegra/cbb/tegra234-cbb.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/soc/tegra/cbb/tegra194-cbb.c b/drivers/soc/tegra/cbb/tegra194-cbb.c index ab75d50cc85c..2f69e104c838 100644 --- a/drivers/soc/tegra/cbb/tegra194-cbb.c +++ b/drivers/soc/tegra/cbb/tegra194-cbb.c @@ -2342,7 +2342,7 @@ static int __init tegra194_cbb_init(void) { return platform_driver_register(&tegra194_cbb_driver); } -pure_initcall(tegra194_cbb_init); +core_initcall(tegra194_cbb_init); static void __exit tegra194_cbb_exit(void) { diff --git a/drivers/soc/tegra/cbb/tegra234-cbb.c b/drivers/soc/tegra/cbb/tegra234-cbb.c index fb26f085f691..785072fa4e85 100644 --- a/drivers/soc/tegra/cbb/tegra234-cbb.c +++ b/drivers/soc/tegra/cbb/tegra234-cbb.c @@ -1774,7 +1774,7 @@ static int __init tegra234_cbb_init(void) { return platform_driver_register(&tegra234_cbb_driver); } -pure_initcall(tegra234_cbb_init); +core_initcall(tegra234_cbb_init); static void __exit tegra234_cbb_exit(void) { -- cgit v1.2.3 From c82dfce478334dca4e9a42c4519a55ee2a2c43c7 Mon Sep 17 00:00:00 2001 From: Shashank Balaji Date: Mon, 1 Jun 2026 19:19:41 +0900 Subject: kernel: param: initialize module_kset in a pure_initcall Commit "driver core: platform: set mod_name in driver registration" will set struct device_driver's mod_name member for platform driver registration. For a driver to be registered with its mod_name set, module_kset needs to be initialized, which currently happens in a subsys_initcall in param_sysfs_init(). The tegra cbb drivers register themselves before module_kset init, in a core_initcall. This works currently because lookup_or_create_module_kobject(), which dereferences module_kset via kset_find_obj(), is not called if mod_name is not set, which is the case now. So in preparation for the commit "driver core: platform: set mod_name in driver registration", move module_kset init to pure_initcall level, ensuring it happens before tegra cbb driver registration. Suggested-by: Gary Guo Reviewed-by: Gary Guo Co-developed-by: Rahul Bukte Signed-off-by: Rahul Bukte Signed-off-by: Shashank Balaji Link: https://patch.msgid.link/20260601101942.4002661-1-shashank.mahadasyam@sony.com Signed-off-by: Danilo Krummrich --- kernel/params.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/kernel/params.c b/kernel/params.c index 74d620bc2521..a668863a4bb6 100644 --- a/kernel/params.c +++ b/kernel/params.c @@ -942,9 +942,9 @@ const struct kobj_type module_ktype = { /* * param_sysfs_init - create "module" kset * - * This must be done before the initramfs is unpacked and - * request_module() thus becomes possible, because otherwise the - * module load would fail in mod_sysfs_init. + * This must be done before any driver registration so that when a driver comes + * from a built-in module, the driver core can add the module under /sys/module + * and create the associated driver symlinks. */ static int __init param_sysfs_init(void) { @@ -957,7 +957,7 @@ static int __init param_sysfs_init(void) return 0; } -subsys_initcall(param_sysfs_init); +pure_initcall(param_sysfs_init); /* * param_sysfs_builtin_init - add sysfs version and parameter -- cgit v1.2.3 From efc22b3f89a3cab9a779f604ef66e7b9c3bfaa72 Mon Sep 17 00:00:00 2001 From: Shashank Balaji Date: Mon, 18 May 2026 19:19:59 +0900 Subject: coresight: pass THIS_MODULE implicitly through a macro Rename coresight_init_driver() to coresight_init_driver_with_owner() and replace it with a macro wrapper that passes THIS_MODULE implicitly. This is in line with what other buses do. Suggested-by: Greg Kroah-Hartman Reviewed-by: Leo Yan Co-developed-by: Rahul Bukte Signed-off-by: Rahul Bukte Signed-off-by: Shashank Balaji Link: https://patch.msgid.link/20260518-acpi_mod_name-v5-3-705ccc430885@sony.com Signed-off-by: Danilo Krummrich --- drivers/hwtracing/coresight/coresight-catu.c | 2 +- drivers/hwtracing/coresight/coresight-core.c | 6 +++--- drivers/hwtracing/coresight/coresight-cpu-debug.c | 3 +-- drivers/hwtracing/coresight/coresight-funnel.c | 3 +-- drivers/hwtracing/coresight/coresight-replicator.c | 3 +-- drivers/hwtracing/coresight/coresight-stm.c | 2 +- drivers/hwtracing/coresight/coresight-tmc-core.c | 2 +- drivers/hwtracing/coresight/coresight-tnoc.c | 2 +- drivers/hwtracing/coresight/coresight-tpdm.c | 3 +-- drivers/hwtracing/coresight/coresight-tpiu.c | 2 +- include/linux/coresight.h | 6 ++++-- 11 files changed, 16 insertions(+), 18 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-catu.c b/drivers/hwtracing/coresight/coresight-catu.c index ce71dcddfca2..0c8698c8fc5e 100644 --- a/drivers/hwtracing/coresight/coresight-catu.c +++ b/drivers/hwtracing/coresight/coresight-catu.c @@ -706,7 +706,7 @@ static int __init catu_init(void) { int ret; - ret = coresight_init_driver("catu", &catu_driver, &catu_platform_driver, THIS_MODULE); + ret = coresight_init_driver("catu", &catu_driver, &catu_platform_driver); tmc_etr_set_catu_ops(&etr_catu_buf_ops); return ret; } diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index 46f247f73cf6..54ad5193b850 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -1694,8 +1694,8 @@ static void __exit coresight_exit(void) module_init(coresight_init); module_exit(coresight_exit); -int coresight_init_driver(const char *drv, struct amba_driver *amba_drv, - struct platform_driver *pdev_drv, struct module *owner) +int coresight_init_driver_with_owner(const char *drv, struct amba_driver *amba_drv, + struct platform_driver *pdev_drv, struct module *owner) { int ret; @@ -1713,7 +1713,7 @@ int coresight_init_driver(const char *drv, struct amba_driver *amba_drv, amba_driver_unregister(amba_drv); return ret; } -EXPORT_SYMBOL_GPL(coresight_init_driver); +EXPORT_SYMBOL_GPL(coresight_init_driver_with_owner); void coresight_remove_driver(struct amba_driver *amba_drv, struct platform_driver *pdev_drv) diff --git a/drivers/hwtracing/coresight/coresight-cpu-debug.c b/drivers/hwtracing/coresight/coresight-cpu-debug.c index 629614278e46..3a806c1d50ea 100644 --- a/drivers/hwtracing/coresight/coresight-cpu-debug.c +++ b/drivers/hwtracing/coresight/coresight-cpu-debug.c @@ -757,8 +757,7 @@ static struct platform_driver debug_platform_driver = { static int __init debug_init(void) { - return coresight_init_driver("debug", &debug_driver, &debug_platform_driver, - THIS_MODULE); + return coresight_init_driver("debug", &debug_driver, &debug_platform_driver); } static void __exit debug_exit(void) diff --git a/drivers/hwtracing/coresight/coresight-funnel.c b/drivers/hwtracing/coresight/coresight-funnel.c index 3f56ceccd8c9..0abc11f0690c 100644 --- a/drivers/hwtracing/coresight/coresight-funnel.c +++ b/drivers/hwtracing/coresight/coresight-funnel.c @@ -412,8 +412,7 @@ static struct amba_driver dynamic_funnel_driver = { static int __init funnel_init(void) { - return coresight_init_driver("funnel", &dynamic_funnel_driver, &funnel_driver, - THIS_MODULE); + return coresight_init_driver("funnel", &dynamic_funnel_driver, &funnel_driver); } static void __exit funnel_exit(void) diff --git a/drivers/hwtracing/coresight/coresight-replicator.c b/drivers/hwtracing/coresight/coresight-replicator.c index 07fc04f53b88..2f382de357ee 100644 --- a/drivers/hwtracing/coresight/coresight-replicator.c +++ b/drivers/hwtracing/coresight/coresight-replicator.c @@ -418,8 +418,7 @@ static struct amba_driver dynamic_replicator_driver = { static int __init replicator_init(void) { - return coresight_init_driver("replicator", &dynamic_replicator_driver, &replicator_driver, - THIS_MODULE); + return coresight_init_driver("replicator", &dynamic_replicator_driver, &replicator_driver); } static void __exit replicator_exit(void) diff --git a/drivers/hwtracing/coresight/coresight-stm.c b/drivers/hwtracing/coresight/coresight-stm.c index aca6cec7885a..4e860519a73f 100644 --- a/drivers/hwtracing/coresight/coresight-stm.c +++ b/drivers/hwtracing/coresight/coresight-stm.c @@ -1050,7 +1050,7 @@ static struct platform_driver stm_platform_driver = { static int __init stm_init(void) { - return coresight_init_driver("stm", &stm_driver, &stm_platform_driver, THIS_MODULE); + return coresight_init_driver("stm", &stm_driver, &stm_platform_driver); } static void __exit stm_exit(void) diff --git a/drivers/hwtracing/coresight/coresight-tmc-core.c b/drivers/hwtracing/coresight/coresight-tmc-core.c index c89fe996af23..bc5a133ada3e 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-core.c +++ b/drivers/hwtracing/coresight/coresight-tmc-core.c @@ -1046,7 +1046,7 @@ static struct platform_driver tmc_platform_driver = { static int __init tmc_init(void) { - return coresight_init_driver("tmc", &tmc_driver, &tmc_platform_driver, THIS_MODULE); + return coresight_init_driver("tmc", &tmc_driver, &tmc_platform_driver); } static void __exit tmc_exit(void) diff --git a/drivers/hwtracing/coresight/coresight-tnoc.c b/drivers/hwtracing/coresight/coresight-tnoc.c index 96a25877b824..9e8de4323d28 100644 --- a/drivers/hwtracing/coresight/coresight-tnoc.c +++ b/drivers/hwtracing/coresight/coresight-tnoc.c @@ -344,7 +344,7 @@ static struct platform_driver itnoc_driver = { static int __init tnoc_init(void) { - return coresight_init_driver("tnoc", &trace_noc_driver, &itnoc_driver, THIS_MODULE); + return coresight_init_driver("tnoc", &trace_noc_driver, &itnoc_driver); } static void __exit tnoc_exit(void) diff --git a/drivers/hwtracing/coresight/coresight-tpdm.c b/drivers/hwtracing/coresight/coresight-tpdm.c index eaf7210af648..8464edbba2d4 100644 --- a/drivers/hwtracing/coresight/coresight-tpdm.c +++ b/drivers/hwtracing/coresight/coresight-tpdm.c @@ -1563,8 +1563,7 @@ static struct platform_driver static_tpdm_driver = { static int __init tpdm_init(void) { - return coresight_init_driver("tpdm", &dynamic_tpdm_driver, &static_tpdm_driver, - THIS_MODULE); + return coresight_init_driver("tpdm", &dynamic_tpdm_driver, &static_tpdm_driver); } static void __exit tpdm_exit(void) diff --git a/drivers/hwtracing/coresight/coresight-tpiu.c b/drivers/hwtracing/coresight/coresight-tpiu.c index b8560b140e0f..7b029d2eb389 100644 --- a/drivers/hwtracing/coresight/coresight-tpiu.c +++ b/drivers/hwtracing/coresight/coresight-tpiu.c @@ -310,7 +310,7 @@ static struct platform_driver tpiu_platform_driver = { static int __init tpiu_init(void) { - return coresight_init_driver("tpiu", &tpiu_driver, &tpiu_platform_driver, THIS_MODULE); + return coresight_init_driver("tpiu", &tpiu_driver, &tpiu_platform_driver); } static void __exit tpiu_exit(void) diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 2131febebee9..cd8fb47a1192 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -691,8 +691,10 @@ coresight_find_output_type(struct coresight_platform_data *pdata, enum coresight_dev_type type, union coresight_dev_subtype subtype); -int coresight_init_driver(const char *drv, struct amba_driver *amba_drv, - struct platform_driver *pdev_drv, struct module *owner); +int coresight_init_driver_with_owner(const char *drv, struct amba_driver *amba_drv, + struct platform_driver *pdev_drv, struct module *owner); +#define coresight_init_driver(drv, amba_drv, pdev_drv) \ + coresight_init_driver_with_owner(drv, amba_drv, pdev_drv, THIS_MODULE) void coresight_remove_driver(struct amba_driver *amba_drv, struct platform_driver *pdev_drv); -- cgit v1.2.3 From a7a7dc5c46a036e8a581a4269839d92aded0e0ea Mon Sep 17 00:00:00 2001 From: Shashank Balaji Date: Mon, 18 May 2026 19:20:00 +0900 Subject: driver core: platform: set mod_name in driver registration Pass KBUILD_MODNAME through the driver registration macro so that the driver core can create the module symlink in sysfs for built-in drivers, and fixup all callers. The Rust platform adapter is updated to pass the module name through to the new parameter. Tested on qemu with: - x86 defconfig + CONFIG_RUST - arm64 defconfig + CONFIG_RUST + CONFIG_CORESIGHT stuff Examples after this patch: /sys/bus/platform/drivers/... coresight-itnoc/module -> coresight_tnoc coresight-static-tpdm/module -> coresight_tpdm coresight-catu-platform/module -> coresight_catu serial8250/module -> 8250 acpi-ged/module -> acpi vmclock/module -> ptp_vmclock Co-developed-by: Rahul Bukte Signed-off-by: Rahul Bukte Signed-off-by: Shashank Balaji Link: https://patch.msgid.link/20260518-acpi_mod_name-v5-4-705ccc430885@sony.com Signed-off-by: Danilo Krummrich --- Documentation/driver-api/driver-model/platform.rst | 3 ++- drivers/base/platform.c | 21 ++++++++++++++------- drivers/hwtracing/coresight/coresight-core.c | 5 +++-- include/linux/coresight.h | 5 +++-- include/linux/platform_device.h | 17 +++++++++-------- rust/kernel/platform.rs | 4 +++- 6 files changed, 34 insertions(+), 21 deletions(-) diff --git a/Documentation/driver-api/driver-model/platform.rst b/Documentation/driver-api/driver-model/platform.rst index cf5ff48d3115..9673470bded2 100644 --- a/Documentation/driver-api/driver-model/platform.rst +++ b/Documentation/driver-api/driver-model/platform.rst @@ -70,7 +70,8 @@ Kernel modules can be composed of several platform drivers. The platform core provides helpers to register and unregister an array of drivers:: int __platform_register_drivers(struct platform_driver * const *drivers, - unsigned int count, struct module *owner); + unsigned int count, struct module *owner, + const char *mod_name); void platform_unregister_drivers(struct platform_driver * const *drivers, unsigned int count); diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 4824d9a2edbe..fb9120b0bcfe 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -915,11 +915,14 @@ EXPORT_SYMBOL_GPL(platform_device_register_full); * __platform_driver_register - register a driver for platform-level devices * @drv: platform driver structure * @owner: owning module/driver + * @mod_name: module name string */ -int __platform_driver_register(struct platform_driver *drv, struct module *owner) +int __platform_driver_register(struct platform_driver *drv, struct module *owner, + const char *mod_name) { drv->driver.owner = owner; drv->driver.bus = &platform_bus_type; + drv->driver.mod_name = mod_name; return driver_register(&drv->driver); } @@ -952,6 +955,7 @@ static int is_bound_to_driver(struct device *dev, void *driver) * @drv: platform driver structure * @probe: the driver probe routine, probably from an __init section * @module: module which will be the owner of the driver + * @mod_name: module name string * * Use this instead of platform_driver_register() when you know the device * is not hotpluggable and has already been registered, and you want to @@ -969,7 +973,8 @@ static int is_bound_to_driver(struct device *dev, void *driver) */ int __init_or_module __platform_driver_probe(struct platform_driver *drv, int (*probe)(struct platform_device *), - struct module *module) + struct module *module, + const char *mod_name) { int retval; @@ -997,7 +1002,7 @@ int __init_or_module __platform_driver_probe(struct platform_driver *drv, /* temporary section violation during probe() */ drv->probe = probe; - retval = __platform_driver_register(drv, module); + retval = __platform_driver_register(drv, module, mod_name); if (retval) return retval; @@ -1025,6 +1030,7 @@ EXPORT_SYMBOL_GPL(__platform_driver_probe); * @data: platform specific data for this platform device * @size: size of platform specific data * @module: module which will be the owner of the driver + * @mod_name: module name string * * Use this in legacy-style modules that probe hardware directly and * register a single platform device and corresponding platform driver. @@ -1035,7 +1041,7 @@ struct platform_device * __init_or_module __platform_create_bundle(struct platform_driver *driver, int (*probe)(struct platform_device *), struct resource *res, unsigned int n_res, - const void *data, size_t size, struct module *module) + const void *data, size_t size, struct module *module, const char *mod_name) { struct platform_device *pdev; int error; @@ -1058,7 +1064,7 @@ __platform_create_bundle(struct platform_driver *driver, if (error) goto err_pdev_put; - error = __platform_driver_probe(driver, probe, module); + error = __platform_driver_probe(driver, probe, module, mod_name); if (error) goto err_pdev_del; @@ -1078,6 +1084,7 @@ EXPORT_SYMBOL_GPL(__platform_create_bundle); * @drivers: an array of drivers to register * @count: the number of drivers to register * @owner: module owning the drivers + * @mod_name: module name string * * Registers platform drivers specified by an array. On failure to register a * driver, all previously registered drivers will be unregistered. Callers of @@ -1087,7 +1094,7 @@ EXPORT_SYMBOL_GPL(__platform_create_bundle); * Returns: 0 on success or a negative error code on failure. */ int __platform_register_drivers(struct platform_driver * const *drivers, - unsigned int count, struct module *owner) + unsigned int count, struct module *owner, const char *mod_name) { unsigned int i; int err; @@ -1095,7 +1102,7 @@ int __platform_register_drivers(struct platform_driver * const *drivers, for (i = 0; i < count; i++) { pr_debug("registering platform driver %ps\n", drivers[i]); - err = __platform_driver_register(drivers[i], owner); + err = __platform_driver_register(drivers[i], owner, mod_name); if (err < 0) { pr_err("failed to register platform driver %ps: %d\n", drivers[i], err); diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index 54ad5193b850..94ee24afdc6d 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -1695,7 +1695,8 @@ module_init(coresight_init); module_exit(coresight_exit); int coresight_init_driver_with_owner(const char *drv, struct amba_driver *amba_drv, - struct platform_driver *pdev_drv, struct module *owner) + struct platform_driver *pdev_drv, struct module *owner, + const char *mod_name) { int ret; @@ -1705,7 +1706,7 @@ int coresight_init_driver_with_owner(const char *drv, struct amba_driver *amba_d return ret; } - ret = __platform_driver_register(pdev_drv, owner); + ret = __platform_driver_register(pdev_drv, owner, mod_name); if (!ret) return 0; diff --git a/include/linux/coresight.h b/include/linux/coresight.h index cd8fb47a1192..1cf85d772bea 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -692,9 +692,10 @@ coresight_find_output_type(struct coresight_platform_data *pdata, union coresight_dev_subtype subtype); int coresight_init_driver_with_owner(const char *drv, struct amba_driver *amba_drv, - struct platform_driver *pdev_drv, struct module *owner); + struct platform_driver *pdev_drv, struct module *owner, + const char *mod_name); #define coresight_init_driver(drv, amba_drv, pdev_drv) \ - coresight_init_driver_with_owner(drv, amba_drv, pdev_drv, THIS_MODULE) + coresight_init_driver_with_owner(drv, amba_drv, pdev_drv, THIS_MODULE, KBUILD_MODNAME) void coresight_remove_driver(struct amba_driver *amba_drv, struct platform_driver *pdev_drv); diff --git a/include/linux/platform_device.h b/include/linux/platform_device.h index 975400a472e3..26e6a43358e2 100644 --- a/include/linux/platform_device.h +++ b/include/linux/platform_device.h @@ -293,18 +293,19 @@ struct platform_driver { * use a macro to avoid include chaining to get THIS_MODULE */ #define platform_driver_register(drv) \ - __platform_driver_register(drv, THIS_MODULE) + __platform_driver_register(drv, THIS_MODULE, KBUILD_MODNAME) extern int __platform_driver_register(struct platform_driver *, - struct module *); + struct module *, const char *mod_name); extern void platform_driver_unregister(struct platform_driver *); /* non-hotpluggable platform devices may use this so that probe() and * its support may live in __init sections, conserving runtime memory. */ #define platform_driver_probe(drv, probe) \ - __platform_driver_probe(drv, probe, THIS_MODULE) + __platform_driver_probe(drv, probe, THIS_MODULE, KBUILD_MODNAME) extern int __platform_driver_probe(struct platform_driver *driver, - int (*probe)(struct platform_device *), struct module *module); + int (*probe)(struct platform_device *), struct module *module, + const char *mod_name); static inline void *platform_get_drvdata(const struct platform_device *pdev) { @@ -368,19 +369,19 @@ static int __init __platform_driver##_init(void) \ device_initcall(__platform_driver##_init); \ #define platform_create_bundle(driver, probe, res, n_res, data, size) \ - __platform_create_bundle(driver, probe, res, n_res, data, size, THIS_MODULE) + __platform_create_bundle(driver, probe, res, n_res, data, size, THIS_MODULE, KBUILD_MODNAME) extern struct platform_device *__platform_create_bundle( struct platform_driver *driver, int (*probe)(struct platform_device *), struct resource *res, unsigned int n_res, - const void *data, size_t size, struct module *module); + const void *data, size_t size, struct module *module, const char *mod_name); int __platform_register_drivers(struct platform_driver * const *drivers, - unsigned int count, struct module *owner); + unsigned int count, struct module *owner, const char *mod_name); void platform_unregister_drivers(struct platform_driver * const *drivers, unsigned int count); #define platform_register_drivers(drivers, count) \ - __platform_register_drivers(drivers, count, THIS_MODULE) + __platform_register_drivers(drivers, count, THIS_MODULE, KBUILD_MODNAME) #ifdef CONFIG_SUSPEND extern int platform_pm_suspend(struct device *dev); diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs index d8d48f60b0b9..9b362e0495d3 100644 --- a/rust/kernel/platform.rs +++ b/rust/kernel/platform.rs @@ -82,7 +82,9 @@ unsafe impl driver::RegistrationOps for Adapter { } // SAFETY: `pdrv` is guaranteed to be a valid `DriverType`. - to_result(unsafe { bindings::__platform_driver_register(pdrv.get(), module.0) }) + to_result(unsafe { + bindings::__platform_driver_register(pdrv.get(), module.0, name.as_char_ptr()) + }) } unsafe fn unregister(pdrv: &Opaque) { -- cgit v1.2.3 From fe221742e388bea3f5856b5d9b2cb0a037020ea4 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sat, 6 Jun 2026 20:51:29 -0700 Subject: software node: allow passing reference args to PROPERTY_ENTRY_REF() When dynamically creating software nodes and properties for subsequent use with software_node_register() current implementation of PROPERTY_ENTRY_REF() is not suitable because it creates a temporary instance of struct software_node_ref_args on stack which will later disappear, and software_node_register() only does shallow copy of properties. Fix this by allowing to pass address of reference arguments structure directly into PROPERTY_ENTRY_REF(), so that caller can manage lifetime of the object properly. Signed-off-by: Dmitry Torokhov Reviewed-by: Andy Shevchenko Link: https://patch.msgid.link/aiTo4dvKu8pyimHA@google.com Signed-off-by: Danilo Krummrich --- include/linux/property.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/include/linux/property.h b/include/linux/property.h index e30ef23a9af3..14c304db4664 100644 --- a/include/linux/property.h +++ b/include/linux/property.h @@ -471,12 +471,18 @@ struct property_entry { #define PROPERTY_ENTRY_STRING(_name_, _val_) \ __PROPERTY_ENTRY_ELEMENT(_name_, str, STRING, _val_) +#define __PROPERTY_ENTRY_REF_ARGS(_ref_, ...) \ + _Generic(_ref_, \ + const struct software_node_ref_args *: _ref_, \ + struct software_node_ref_args *: _ref_, \ + default: &SOFTWARE_NODE_REFERENCE(_ref_, ##__VA_ARGS__)) + #define PROPERTY_ENTRY_REF(_name_, _ref_, ...) \ (struct property_entry) { \ .name = _name_, \ .length = sizeof(struct software_node_ref_args), \ .type = DEV_PROP_REF, \ - { .pointer = &SOFTWARE_NODE_REFERENCE(_ref_, ##__VA_ARGS__), }, \ + { .pointer = __PROPERTY_ENTRY_REF_ARGS(_ref_, ##__VA_ARGS__) }, \ } #define PROPERTY_ENTRY_BOOL(_name_) \ -- cgit v1.2.3