summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MAINTAINERS2
-rw-r--r--drivers/base/Kconfig9
-rw-r--r--drivers/base/auxiliary.c6
-rw-r--r--drivers/base/base.h98
-rw-r--r--drivers/base/core.c29
-rw-r--r--drivers/base/dd.c52
-rw-r--r--drivers/base/devres.c277
-rw-r--r--drivers/base/init.c1
-rw-r--r--drivers/base/memory.c2
-rw-r--r--drivers/base/platform.c58
-rw-r--r--drivers/base/property.c14
-rw-r--r--drivers/base/soc.c23
-rw-r--r--drivers/base/swnode.c13
-rw-r--r--drivers/bus/fsl-mc/fsl-mc-bus.c43
-rw-r--r--drivers/bus/imx-weim.c2
-rw-r--r--drivers/i2c/i2c-core-of.c2
-rw-r--r--drivers/net/phy/mdio_bus_provider.c4
-rw-r--r--drivers/of/base.c30
-rw-r--r--drivers/of/dynamic.c2
-rw-r--r--drivers/of/platform.c2
-rw-r--r--drivers/pci/pci-driver.c11
-rw-r--r--drivers/pci/pci-sysfs.c28
-rw-r--r--drivers/pci/probe.c1
-rw-r--r--drivers/platform/wmi/core.c36
-rw-r--r--drivers/s390/cio/cio.h5
-rw-r--r--drivers/s390/cio/css.c34
-rw-r--r--drivers/s390/crypto/ap_bus.c34
-rw-r--r--drivers/s390/crypto/ap_bus.h1
-rw-r--r--drivers/s390/crypto/ap_queue.c24
-rw-r--r--drivers/soc/fsl/guts.c12
-rw-r--r--drivers/soc/imx/soc-imx8m.c11
-rw-r--r--drivers/soc/imx/soc-imx9.c4
-rw-r--r--drivers/soc/sunxi/sunxi_mbus.c2
-rw-r--r--drivers/soundwire/debugfs.c9
-rw-r--r--drivers/spi/spi.c2
-rw-r--r--drivers/vdpa/vdpa.c48
-rw-r--r--drivers/vfio/fsl-mc/vfio_fsl_mc.c4
-rw-r--r--drivers/vfio/pci/vfio_pci_core.c5
-rw-r--r--drivers/xen/xen-pciback/pci_stub.c6
-rw-r--r--fs/debugfs/file.c7
-rw-r--r--fs/kernfs/dir.c58
-rw-r--r--fs/kernfs/inode.c2
-rw-r--r--fs/sysfs/group.c10
-rw-r--r--include/linux/device.h5
-rw-r--r--include/linux/device/class.h4
-rw-r--r--include/linux/fsl/mc.h4
-rw-r--r--include/linux/fwnode.h44
-rw-r--r--include/linux/ksysfs.h8
-rw-r--r--include/linux/of.h14
-rw-r--r--include/linux/pci.h6
-rw-r--r--include/linux/platform_device.h58
-rw-r--r--include/linux/sys_soc.h10
-rw-r--r--include/linux/sysfs.h16
-rw-r--r--include/linux/vdpa.h4
-rw-r--r--include/linux/wmi.h4
-rw-r--r--init/main.c2
-rw-r--r--kernel/ksysfs.c9
-rw-r--r--rust/kernel/devres.rs187
-rw-r--r--rust/kernel/io.rs780
-rw-r--r--rust/kernel/io/mem.rs10
-rw-r--r--rust/kernel/io/register.rs1260
-rw-r--r--rust/kernel/irq/request.rs28
-rw-r--r--rust/kernel/lib.rs3
-rw-r--r--rust/kernel/num/bounded.rs70
-rw-r--r--rust/kernel/pci/io.rs99
-rw-r--r--samples/rust/rust_driver_pci.rs90
-rw-r--r--scripts/Makefile.build3
-rw-r--r--tools/testing/selftests/cgroup/test_memcontrol.c112
68 files changed, 2839 insertions, 1014 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 3adc870d523b..40689cbc0b3d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7807,8 +7807,10 @@ F: include/linux/debugfs.h
F: include/linux/device.h
F: include/linux/fwnode.h
F: include/linux/kobj*
+F: include/linux/ksysfs.h
F: include/linux/property.h
F: include/linux/sysfs.h
+F: kernel/ksysfs.c
F: lib/kobj*
F: rust/kernel/debugfs.rs
F: rust/kernel/debugfs/
diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig
index 1786d87b29e2..f7d385cbd3ba 100644
--- a/drivers/base/Kconfig
+++ b/drivers/base/Kconfig
@@ -73,6 +73,15 @@ config DEVTMPFS_SAFE
with the PROT_EXEC flag. This can break, for example, non-KMS
video drivers.
+config DRIVER_DEFERRED_PROBE_TIMEOUT
+ int "Default value for deferred_probe_timeout"
+ default 0 if !MODULES
+ default 10 if MODULES
+ help
+ Set the default value for the deferred_probe_timeout kernel parameter.
+ See Documentation/admin-guide/kernel-parameters.txt for a description
+ of the deferred_probe_timeout kernel parameter.
+
config STANDALONE
bool "Select only drivers that don't need compile-time external firmware"
default y
diff --git a/drivers/base/auxiliary.c b/drivers/base/auxiliary.c
index 9fd3820d1f8a..16b8bdab30c8 100644
--- a/drivers/base/auxiliary.c
+++ b/drivers/base/auxiliary.c
@@ -207,11 +207,6 @@ static int auxiliary_uevent(const struct device *dev, struct kobj_uevent_env *en
(int)(p - name), name);
}
-static const struct dev_pm_ops auxiliary_dev_pm_ops = {
- SET_RUNTIME_PM_OPS(pm_generic_runtime_suspend, pm_generic_runtime_resume, NULL)
- SET_SYSTEM_SLEEP_PM_OPS(pm_generic_suspend, pm_generic_resume)
-};
-
static int auxiliary_bus_probe(struct device *dev)
{
const struct auxiliary_driver *auxdrv = to_auxiliary_drv(dev->driver);
@@ -258,7 +253,6 @@ static const struct bus_type auxiliary_bus_type = {
.shutdown = auxiliary_bus_shutdown,
.match = auxiliary_match,
.uevent = auxiliary_uevent,
- .pm = &auxiliary_dev_pm_ops,
};
/**
diff --git a/drivers/base/base.h b/drivers/base/base.h
index 1af95ac68b77..30b416588617 100644
--- a/drivers/base/base.h
+++ b/drivers/base/base.h
@@ -13,27 +13,28 @@
#include <linux/notifier.h>
/**
- * struct subsys_private - structure to hold the private to the driver core portions of the bus_type/class structure.
- *
- * @subsys - the struct kset that defines this subsystem
- * @devices_kset - the subsystem's 'devices' directory
- * @interfaces - list of subsystem interfaces associated
- * @mutex - protect the devices, and interfaces lists.
- *
- * @drivers_kset - the list of drivers associated
- * @klist_devices - the klist to iterate over the @devices_kset
- * @klist_drivers - the klist to iterate over the @drivers_kset
- * @bus_notifier - the bus notifier list for anything that cares about things
- * on this bus.
- * @bus - pointer back to the struct bus_type that this structure is associated
- * with.
+ * struct subsys_private - structure to hold the private to the driver core
+ * portions of the bus_type/class structure.
+ * @subsys: the struct kset that defines this subsystem
+ * @devices_kset: the subsystem's 'devices' directory
+ * @interfaces: list of subsystem interfaces associated
+ * @mutex: protect the devices, and interfaces lists.
+ * @drivers_kset: the list of drivers associated
+ * @klist_devices: the klist to iterate over the @devices_kset
+ * @klist_drivers: the klist to iterate over the @drivers_kset
+ * @bus_notifier: the bus notifier list for anything that cares about things
+ * on this bus.
+ * @drivers_autoprobe: gate whether new devices are automatically attached to
+ * registered drivers, or new drivers automatically attach
+ * to existing devices.
+ * @bus: pointer back to the struct bus_type that this structure is associated
+ * with.
* @dev_root: Default device to use as the parent.
- *
- * @glue_dirs - "glue" directory to put in-between the parent device to
- * avoid namespace conflicts
- * @class - pointer back to the struct class that this structure is associated
- * with.
- * @lock_key: Lock class key for use by the lock validator
+ * @glue_dirs: "glue" directory to put in-between the parent device to
+ * avoid namespace conflicts
+ * @class: pointer back to the struct class that this structure is associated
+ * with.
+ * @lock_key: Lock class key for use by the lock validator
*
* This structure is the one that is the actual kobject allowing struct
* bus_type/class to be statically allocated safely. Nothing outside of the
@@ -98,24 +99,26 @@ struct driver_type {
#endif
/**
- * struct device_private - structure to hold the private to the driver core portions of the device structure.
- *
- * @klist_children - klist containing all children of this device
- * @knode_parent - node in sibling list
- * @knode_driver - node in driver list
- * @knode_bus - node in bus list
- * @knode_class - node in class list
- * @deferred_probe - entry in deferred_probe_list which is used to retry the
- * binding of drivers which were unable to get all the resources needed by
- * the device; typically because it depends on another driver getting
- * probed first.
- * @async_driver - pointer to device driver awaiting probe via async_probe
- * @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.
+ * struct device_private - structure to hold the private to the driver core
+ * portions of the device structure.
+ * @klist_children: klist containing all children of this device
+ * @knode_parent: node in sibling list
+ * @knode_driver: node in driver list
+ * @knode_bus: node in bus list
+ * @knode_class: node in class list
+ * @deferred_probe: entry in deferred_probe_list which is used to retry the
+ * binding of drivers which were unable to get all the
+ * resources needed by the device; typically because it depends
+ * on another driver getting probed first.
+ * @async_driver: pointer to device driver awaiting probe via async_probe
+ * @deferred_probe_reason: capture the -EPROBE_DEFER message emitted with
+ * 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.
*
* Nothing outside of the driver core should ever touch these fields.
*/
@@ -213,6 +216,24 @@ static inline void device_set_driver(struct device *dev, const struct device_dri
WRITE_ONCE(dev->driver, (struct device_driver *)drv);
}
+struct devres_node;
+typedef void (*dr_node_release_t)(struct device *dev, struct devres_node *node);
+typedef void (*dr_node_free_t)(struct devres_node *node);
+
+struct devres_node {
+ struct list_head entry;
+ dr_node_release_t release;
+ dr_node_free_t free_node;
+ const char *name;
+ size_t size;
+};
+
+void devres_node_init(struct devres_node *node, dr_node_release_t release,
+ dr_node_free_t free_node);
+void devres_node_add(struct device *dev, struct devres_node *node);
+bool devres_node_remove(struct device *dev, struct devres_node *node);
+void devres_set_node_dbginfo(struct devres_node *node, const char *name,
+ size_t size);
void devres_for_each_res(struct device *dev, dr_release_t release,
dr_match_t match, void *match_data,
void (*fn)(struct device *, void *, void *),
@@ -291,6 +312,7 @@ static inline int devtmpfs_create_node(struct device *dev) { return 0; }
static inline int devtmpfs_delete_node(struct device *dev) { return 0; }
#endif
+void software_node_init(void);
void software_node_notify(struct device *dev);
void software_node_notify_remove(struct device *dev);
diff --git a/drivers/base/core.c b/drivers/base/core.c
index 0613de0fbe44..673d709fa7e6 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -182,7 +182,7 @@ void fw_devlink_purge_absent_suppliers(struct fwnode_handle *fwnode)
if (fwnode->dev)
return;
- fwnode->flags |= FWNODE_FLAG_NOT_DEVICE;
+ fwnode_set_flag(fwnode, FWNODE_FLAG_NOT_DEVICE);
fwnode_links_purge_consumers(fwnode);
fwnode_for_each_available_child_node(fwnode, child)
@@ -228,7 +228,7 @@ static void __fw_devlink_pickup_dangling_consumers(struct fwnode_handle *fwnode,
if (fwnode->dev && fwnode->dev->bus)
return;
- fwnode->flags |= FWNODE_FLAG_NOT_DEVICE;
+ fwnode_set_flag(fwnode, FWNODE_FLAG_NOT_DEVICE);
__fwnode_links_move_consumers(fwnode, new_sup);
fwnode_for_each_available_child_node(fwnode, child)
@@ -1012,7 +1012,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) ||
- (dev->fwnode && (dev->fwnode->flags & FWNODE_FLAG_BEST_EFFORT));
+ (dev->fwnode && fwnode_test_flag(dev->fwnode, FWNODE_FLAG_BEST_EFFORT));
}
static struct fwnode_handle *fwnode_links_check_suppliers(
@@ -1723,11 +1723,11 @@ bool fw_devlink_is_strict(void)
static void fw_devlink_parse_fwnode(struct fwnode_handle *fwnode)
{
- if (fwnode->flags & FWNODE_FLAG_LINKS_ADDED)
+ if (fwnode_test_flag(fwnode, FWNODE_FLAG_LINKS_ADDED))
return;
fwnode_call_int_op(fwnode, add_links);
- fwnode->flags |= FWNODE_FLAG_LINKS_ADDED;
+ fwnode_set_flag(fwnode, FWNODE_FLAG_LINKS_ADDED);
}
static void fw_devlink_parse_fwtree(struct fwnode_handle *fwnode)
@@ -1885,7 +1885,7 @@ static bool fwnode_init_without_drv(struct fwnode_handle *fwnode)
struct device *dev;
bool ret;
- if (!(fwnode->flags & FWNODE_FLAG_INITIALIZED))
+ if (!fwnode_test_flag(fwnode, FWNODE_FLAG_INITIALIZED))
return false;
dev = get_dev_from_fwnode(fwnode);
@@ -2001,10 +2001,10 @@ static bool __fw_devlink_relax_cycles(struct fwnode_handle *con_handle,
* We aren't trying to find all cycles. Just a cycle between con and
* sup_handle.
*/
- if (sup_handle->flags & FWNODE_FLAG_VISITED)
+ if (fwnode_test_flag(sup_handle, FWNODE_FLAG_VISITED))
return false;
- sup_handle->flags |= FWNODE_FLAG_VISITED;
+ fwnode_set_flag(sup_handle, FWNODE_FLAG_VISITED);
/* Termination condition. */
if (sup_handle == con_handle) {
@@ -2074,7 +2074,7 @@ static bool __fw_devlink_relax_cycles(struct fwnode_handle *con_handle,
}
out:
- sup_handle->flags &= ~FWNODE_FLAG_VISITED;
+ fwnode_clear_flag(sup_handle, FWNODE_FLAG_VISITED);
put_device(sup_dev);
put_device(con_dev);
put_device(par_dev);
@@ -2127,7 +2127,7 @@ static int fw_devlink_create_devlink(struct device *con,
* When such a flag is set, we can't create device links where P is the
* supplier of C as that would delay the probe of C.
*/
- if (sup_handle->flags & FWNODE_FLAG_NEEDS_CHILD_BOUND_ON_ADD &&
+ if (fwnode_test_flag(sup_handle, FWNODE_FLAG_NEEDS_CHILD_BOUND_ON_ADD) &&
fwnode_is_ancestor_of(sup_handle, con->fwnode))
return -EINVAL;
@@ -2150,7 +2150,7 @@ static int fw_devlink_create_devlink(struct device *con,
else
flags = FW_DEVLINK_FLAGS_PERMISSIVE;
- if (sup_handle->flags & FWNODE_FLAG_NOT_DEVICE)
+ if (fwnode_test_flag(sup_handle, FWNODE_FLAG_NOT_DEVICE))
sup_dev = fwnode_get_next_parent_dev(sup_handle);
else
sup_dev = get_dev_from_fwnode(sup_handle);
@@ -2162,7 +2162,7 @@ static int fw_devlink_create_devlink(struct device *con,
* supplier device indefinitely.
*/
if (sup_dev->links.status == DL_DEV_NO_DRIVER &&
- sup_handle->flags & FWNODE_FLAG_INITIALIZED) {
+ fwnode_test_flag(sup_handle, FWNODE_FLAG_INITIALIZED)) {
dev_dbg(con,
"Not linking %pfwf - dev might never probe\n",
sup_handle);
@@ -2831,14 +2831,15 @@ static ssize_t removable_show(struct device *dev, struct device_attribute *attr,
}
static DEVICE_ATTR_RO(removable);
-int device_add_groups(struct device *dev, const struct attribute_group **groups)
+int device_add_groups(struct device *dev,
+ const struct attribute_group *const *groups)
{
return sysfs_create_groups(&dev->kobj, groups);
}
EXPORT_SYMBOL_GPL(device_add_groups);
void device_remove_groups(struct device *dev,
- const struct attribute_group **groups)
+ const struct attribute_group *const *groups)
{
sysfs_remove_groups(&dev->kobj, groups);
}
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index 37c7e54e0e4c..cb5046f0634d 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -257,11 +257,7 @@ static int deferred_devs_show(struct seq_file *s, void *data)
}
DEFINE_SHOW_ATTRIBUTE(deferred_devs);
-#ifdef CONFIG_MODULES
-static int driver_deferred_probe_timeout = 10;
-#else
-static int driver_deferred_probe_timeout;
-#endif
+static int driver_deferred_probe_timeout = CONFIG_DRIVER_DEFERRED_PROBE_TIMEOUT;
static int __init deferred_probe_timeout_setup(char *str)
{
@@ -383,8 +379,7 @@ __exitcall(deferred_probe_exit);
int __device_set_driver_override(struct device *dev, const char *s, size_t len)
{
- const char *new, *old;
- char *cp;
+ const char *new = NULL, *old;
if (!s)
return -EINVAL;
@@ -404,37 +399,30 @@ int __device_set_driver_override(struct device *dev, const char *s, size_t len)
*/
len = strlen(s);
- if (!len) {
- /* Empty string passed - clear override */
- spin_lock(&dev->driver_override.lock);
- old = dev->driver_override.name;
- dev->driver_override.name = NULL;
- spin_unlock(&dev->driver_override.lock);
- kfree(old);
+ /* Handle trailing newline */
+ if (len) {
+ char *cp;
- return 0;
+ cp = strnchr(s, len, '\n');
+ if (cp)
+ len = cp - s;
}
- cp = strnchr(s, len, '\n');
- if (cp)
- len = cp - s;
-
- new = kstrndup(s, len, GFP_KERNEL);
- if (!new)
- return -ENOMEM;
+ /*
+ * If empty string or "\n" passed, new remains NULL, clearing
+ * the driver_override.name.
+ */
+ if (len) {
+ new = kstrndup(s, len, GFP_KERNEL);
+ if (!new)
+ return -ENOMEM;
+ }
- spin_lock(&dev->driver_override.lock);
- old = dev->driver_override.name;
- if (cp != s) {
+ scoped_guard(spinlock, &dev->driver_override.lock) {
+ old = dev->driver_override.name;
dev->driver_override.name = new;
- spin_unlock(&dev->driver_override.lock);
- } else {
- /* "\n" passed - clear override */
- dev->driver_override.name = NULL;
- spin_unlock(&dev->driver_override.lock);
-
- kfree(new);
}
+
kfree(old);
return 0;
diff --git a/drivers/base/devres.c b/drivers/base/devres.c
index 171750c1f691..9d9842fc5a19 100644
--- a/drivers/base/devres.c
+++ b/drivers/base/devres.c
@@ -16,15 +16,9 @@
#include "base.h"
#include "trace.h"
-struct devres_node {
- struct list_head entry;
- dr_release_t release;
- const char *name;
- size_t size;
-};
-
struct devres {
struct devres_node node;
+ dr_release_t release;
/*
* Some archs want to perform DMA into kmalloc caches
* and need a guaranteed alignment larger than
@@ -42,7 +36,21 @@ struct devres_group {
/* -- 8 pointers */
};
-static void set_node_dbginfo(struct devres_node *node, const char *name,
+void devres_node_init(struct devres_node *node,
+ dr_node_release_t release,
+ dr_node_free_t free_node)
+{
+ INIT_LIST_HEAD(&node->entry);
+ node->release = release;
+ node->free_node = free_node;
+}
+
+static inline void free_node(struct devres_node *node)
+{
+ node->free_node(node);
+}
+
+void devres_set_node_dbginfo(struct devres_node *node, const char *name,
size_t size)
{
node->name = name;
@@ -75,12 +83,12 @@ static void devres_log(struct device *dev, struct devres_node *node,
* Release functions for devres group. These callbacks are used only
* for identification.
*/
-static void group_open_release(struct device *dev, void *res)
+static void group_open_release(struct device *dev, struct devres_node *node)
{
/* noop */
}
-static void group_close_release(struct device *dev, void *res)
+static void group_close_release(struct device *dev, struct devres_node *node)
{
/* noop */
}
@@ -107,6 +115,20 @@ static bool check_dr_size(size_t size, size_t *tot_size)
return true;
}
+static void dr_node_release(struct device *dev, struct devres_node *node)
+{
+ struct devres *dr = container_of(node, struct devres, node);
+
+ dr->release(dev, dr->data);
+}
+
+static void dr_node_free(struct devres_node *node)
+{
+ struct devres *dr = container_of(node, struct devres, node);
+
+ kfree(dr);
+}
+
static __always_inline struct devres *alloc_dr(dr_release_t release,
size_t size, gfp_t gfp, int nid)
{
@@ -124,8 +146,8 @@ static __always_inline struct devres *alloc_dr(dr_release_t release,
if (!(gfp & __GFP_ZERO))
memset(dr, 0, offsetof(struct devres, data));
- INIT_LIST_HEAD(&dr->node.entry);
- dr->node.release = release;
+ devres_node_init(&dr->node, dr_node_release, dr_node_free);
+ dr->release = release;
return dr;
}
@@ -167,7 +189,7 @@ void *__devres_alloc_node(dr_release_t release, size_t size, gfp_t gfp, int nid,
dr = alloc_dr(release, size, gfp | __GFP_ZERO, nid);
if (unlikely(!dr))
return NULL;
- set_node_dbginfo(&dr->node, name, size);
+ devres_set_node_dbginfo(&dr->node, name, size);
return dr->data;
}
EXPORT_SYMBOL_GPL(__devres_alloc_node);
@@ -194,26 +216,31 @@ void devres_for_each_res(struct device *dev, dr_release_t release,
{
struct devres_node *node;
struct devres_node *tmp;
- unsigned long flags;
if (!fn)
return;
- spin_lock_irqsave(&dev->devres_lock, flags);
+ guard(spinlock_irqsave)(&dev->devres_lock);
list_for_each_entry_safe_reverse(node, tmp,
&dev->devres_head, entry) {
struct devres *dr = container_of(node, struct devres, node);
- if (node->release != release)
+ if (node->release != dr_node_release)
+ continue;
+ if (dr->release != release)
continue;
if (match && !match(dev, dr->data, match_data))
continue;
fn(dev, dr->data, data);
}
- spin_unlock_irqrestore(&dev->devres_lock, flags);
}
EXPORT_SYMBOL_GPL(devres_for_each_res);
+static inline void free_dr(struct devres *dr)
+{
+ free_node(&dr->node);
+}
+
/**
* devres_free - Free device resource data
* @res: Pointer to devres data to free
@@ -226,11 +253,18 @@ void devres_free(void *res)
struct devres *dr = container_of(res, struct devres, data);
BUG_ON(!list_empty(&dr->node.entry));
- kfree(dr);
+ free_dr(dr);
}
}
EXPORT_SYMBOL_GPL(devres_free);
+void devres_node_add(struct device *dev, struct devres_node *node)
+{
+ guard(spinlock_irqsave)(&dev->devres_lock);
+
+ add_dr(dev, node);
+}
+
/**
* devres_add - Register device resource
* @dev: Device to add resource to
@@ -243,11 +277,8 @@ EXPORT_SYMBOL_GPL(devres_free);
void devres_add(struct device *dev, void *res)
{
struct devres *dr = container_of(res, struct devres, data);
- unsigned long flags;
- spin_lock_irqsave(&dev->devres_lock, flags);
- add_dr(dev, &dr->node);
- spin_unlock_irqrestore(&dev->devres_lock, flags);
+ devres_node_add(dev, &dr->node);
}
EXPORT_SYMBOL_GPL(devres_add);
@@ -259,7 +290,9 @@ static struct devres *find_dr(struct device *dev, dr_release_t release,
list_for_each_entry_reverse(node, &dev->devres_head, entry) {
struct devres *dr = container_of(node, struct devres, node);
- if (node->release != release)
+ if (node->release != dr_node_release)
+ continue;
+ if (dr->release != release)
continue;
if (match && !match(dev, dr->data, match_data))
continue;
@@ -287,14 +320,12 @@ void *devres_find(struct device *dev, dr_release_t release,
dr_match_t match, void *match_data)
{
struct devres *dr;
- unsigned long flags;
- spin_lock_irqsave(&dev->devres_lock, flags);
+ guard(spinlock_irqsave)(&dev->devres_lock);
dr = find_dr(dev, release, match, match_data);
- spin_unlock_irqrestore(&dev->devres_lock, flags);
-
if (dr)
return dr->data;
+
return NULL;
}
EXPORT_SYMBOL_GPL(devres_find);
@@ -321,7 +352,7 @@ void *devres_get(struct device *dev, void *new_res,
unsigned long flags;
spin_lock_irqsave(&dev->devres_lock, flags);
- dr = find_dr(dev, new_dr->node.release, match, match_data);
+ dr = find_dr(dev, new_dr->release, match, match_data);
if (!dr) {
add_dr(dev, &new_dr->node);
dr = new_dr;
@@ -334,6 +365,22 @@ void *devres_get(struct device *dev, void *new_res,
}
EXPORT_SYMBOL_GPL(devres_get);
+bool devres_node_remove(struct device *dev, struct devres_node *node)
+{
+ struct devres_node *__node;
+
+ guard(spinlock_irqsave)(&dev->devres_lock);
+ list_for_each_entry_reverse(__node, &dev->devres_head, entry) {
+ if (__node == node) {
+ list_del_init(&node->entry);
+ devres_log(dev, node, "REM");
+ return true;
+ }
+ }
+
+ return false;
+}
+
/**
* devres_remove - Find a device resource and remove it
* @dev: Device to find resource from
@@ -353,18 +400,15 @@ void *devres_remove(struct device *dev, dr_release_t release,
dr_match_t match, void *match_data)
{
struct devres *dr;
- unsigned long flags;
- spin_lock_irqsave(&dev->devres_lock, flags);
+ guard(spinlock_irqsave)(&dev->devres_lock);
dr = find_dr(dev, release, match, match_data);
if (dr) {
list_del_init(&dr->node.entry);
devres_log(dev, &dr->node, "REM");
+ return dr->data;
}
- spin_unlock_irqrestore(&dev->devres_lock, flags);
- if (dr)
- return dr->data;
return NULL;
}
EXPORT_SYMBOL_GPL(devres_remove);
@@ -495,15 +539,12 @@ static int remove_nodes(struct device *dev,
static void release_nodes(struct device *dev, struct list_head *todo)
{
- struct devres *dr, *tmp;
+ struct devres_node *node, *tmp;
- /* Release. Note that both devres and devres_group are
- * handled as devres in the following loop. This is safe.
- */
- list_for_each_entry_safe_reverse(dr, tmp, todo, node.entry) {
- devres_log(dev, &dr->node, "REL");
- dr->node.release(dev, dr->data);
- kfree(dr);
+ list_for_each_entry_safe_reverse(node, tmp, todo, entry) {
+ devres_log(dev, node, "REL");
+ node->release(dev, node);
+ free_node(node);
}
}
@@ -536,6 +577,13 @@ int devres_release_all(struct device *dev)
return cnt;
}
+static void devres_group_free(struct devres_node *node)
+{
+ struct devres_group *grp = container_of(node, struct devres_group, node[0]);
+
+ kfree(grp);
+}
+
/**
* devres_open_group - Open a new devres group
* @dev: Device to open devres group for
@@ -552,26 +600,21 @@ int devres_release_all(struct device *dev)
void *devres_open_group(struct device *dev, void *id, gfp_t gfp)
{
struct devres_group *grp;
- unsigned long flags;
grp = kmalloc_obj(*grp, gfp);
if (unlikely(!grp))
return NULL;
- grp->node[0].release = &group_open_release;
- grp->node[1].release = &group_close_release;
- INIT_LIST_HEAD(&grp->node[0].entry);
- INIT_LIST_HEAD(&grp->node[1].entry);
- set_node_dbginfo(&grp->node[0], "grp<", 0);
- set_node_dbginfo(&grp->node[1], "grp>", 0);
+ devres_node_init(&grp->node[0], &group_open_release, devres_group_free);
+ devres_node_init(&grp->node[1], &group_close_release, NULL);
+ devres_set_node_dbginfo(&grp->node[0], "grp<", 0);
+ devres_set_node_dbginfo(&grp->node[1], "grp>", 0);
grp->id = grp;
if (id)
grp->id = id;
grp->color = 0;
- spin_lock_irqsave(&dev->devres_lock, flags);
- add_dr(dev, &grp->node[0]);
- spin_unlock_irqrestore(&dev->devres_lock, flags);
+ devres_node_add(dev, &grp->node[0]);
return grp->id;
}
EXPORT_SYMBOL_GPL(devres_open_group);
@@ -613,17 +656,13 @@ static struct devres_group *find_group(struct device *dev, void *id)
void devres_close_group(struct device *dev, void *id)
{
struct devres_group *grp;
- unsigned long flags;
-
- spin_lock_irqsave(&dev->devres_lock, flags);
+ guard(spinlock_irqsave)(&dev->devres_lock);
grp = find_group(dev, id);
if (grp)
add_dr(dev, &grp->node[1]);
else
WARN_ON(1);
-
- spin_unlock_irqrestore(&dev->devres_lock, flags);
}
EXPORT_SYMBOL_GPL(devres_close_group);
@@ -677,7 +716,6 @@ int devres_release_group(struct device *dev, void *id)
int cnt = 0;
spin_lock_irqsave(&dev->devres_lock, flags);
-
grp = find_group(dev, id);
if (grp) {
struct list_head *first = &grp->node[0].entry;
@@ -687,20 +725,18 @@ int devres_release_group(struct device *dev, void *id)
end = grp->node[1].entry.next;
cnt = remove_nodes(dev, first, end, &todo);
- spin_unlock_irqrestore(&dev->devres_lock, flags);
-
- release_nodes(dev, &todo);
} else if (list_empty(&dev->devres_head)) {
/*
* dev is probably dying via devres_release_all(): groups
* have already been removed and are on the process of
* being released - don't touch and don't warn.
*/
- spin_unlock_irqrestore(&dev->devres_lock, flags);
} else {
WARN_ON(1);
- spin_unlock_irqrestore(&dev->devres_lock, flags);
}
+ spin_unlock_irqrestore(&dev->devres_lock, flags);
+
+ release_nodes(dev, &todo);
return cnt;
}
@@ -716,20 +752,29 @@ struct action_devres {
void (*action)(void *);
};
-static int devm_action_match(struct device *dev, void *res, void *p)
+struct devres_action {
+ struct devres_node node;
+ struct action_devres action;
+};
+
+static int devm_action_match(struct devres_action *devres, struct action_devres *target)
{
- struct action_devres *devres = res;
- struct action_devres *target = p;
+ return devres->action.action == target->action &&
+ devres->action.data == target->data;
+}
- return devres->action == target->action &&
- devres->data == target->data;
+static void devm_action_release(struct device *dev, struct devres_node *node)
+{
+ struct devres_action *devres = container_of(node, struct devres_action, node);
+
+ devres->action.action(devres->action.data);
}
-static void devm_action_release(struct device *dev, void *res)
+static void devm_action_free(struct devres_node *node)
{
- struct action_devres *devres = res;
+ struct devres_action *action = container_of(node, struct devres_action, node);
- devres->action(devres->data);
+ kfree(action);
}
/**
@@ -744,32 +789,71 @@ static void devm_action_release(struct device *dev, void *res)
*/
int __devm_add_action(struct device *dev, void (*action)(void *), void *data, const char *name)
{
- struct action_devres *devres;
+ struct devres_action *devres;
- devres = __devres_alloc_node(devm_action_release, sizeof(struct action_devres),
- GFP_KERNEL, NUMA_NO_NODE, name);
+ devres = kzalloc_obj(*devres);
if (!devres)
return -ENOMEM;
- devres->data = data;
- devres->action = action;
+ devres_node_init(&devres->node, devm_action_release, devm_action_free);
+ devres_set_node_dbginfo(&devres->node, name, sizeof(*devres));
- devres_add(dev, devres);
+ devres->action.data = data;
+ devres->action.action = action;
+
+ devres_node_add(dev, &devres->node);
return 0;
}
EXPORT_SYMBOL_GPL(__devm_add_action);
-bool devm_is_action_added(struct device *dev, void (*action)(void *), void *data)
+static struct devres_action *devres_action_find(struct device *dev,
+ void (*action)(void *),
+ void *data)
{
- struct action_devres devres = {
+ struct devres_node *node;
+ struct action_devres target = {
.data = data,
.action = action,
};
- return devres_find(dev, devm_action_release, devm_action_match, &devres);
+ list_for_each_entry_reverse(node, &dev->devres_head, entry) {
+ struct devres_action *dr = container_of(node, struct devres_action, node);
+
+ if (node->release != devm_action_release)
+ continue;
+ if (devm_action_match(dr, &target))
+ return dr;
+ }
+
+ return NULL;
+}
+
+bool devm_is_action_added(struct device *dev, void (*action)(void *), void *data)
+{
+ guard(spinlock_irqsave)(&dev->devres_lock);
+
+ return !!devres_action_find(dev, action, data);
}
EXPORT_SYMBOL_GPL(devm_is_action_added);
+static struct devres_action *remove_action(struct device *dev,
+ void (*action)(void *),
+ void *data)
+{
+ struct devres_action *dr;
+
+ guard(spinlock_irqsave)(&dev->devres_lock);
+
+ dr = devres_action_find(dev, action, data);
+ if (!dr)
+ return ERR_PTR(-ENOENT);
+
+ list_del_init(&dr->node.entry);
+ devres_log(dev, &dr->node, "REM");
+
+ return dr;
+}
+
/**
* devm_remove_action_nowarn() - removes previously added custom action
* @dev: Device that owns the action
@@ -794,13 +878,15 @@ int devm_remove_action_nowarn(struct device *dev,
void (*action)(void *),
void *data)
{
- struct action_devres devres = {
- .data = data,
- .action = action,
- };
+ struct devres_action *dr;
- return devres_destroy(dev, devm_action_release, devm_action_match,
- &devres);
+ dr = remove_action(dev, action, data);
+ if (IS_ERR(dr))
+ return PTR_ERR(dr);
+
+ kfree(dr);
+
+ return 0;
}
EXPORT_SYMBOL_GPL(devm_remove_action_nowarn);
@@ -816,14 +902,15 @@ EXPORT_SYMBOL_GPL(devm_remove_action_nowarn);
*/
void devm_release_action(struct device *dev, void (*action)(void *), void *data)
{
- struct action_devres devres = {
- .data = data,
- .action = action,
- };
+ struct devres_action *dr;
- WARN_ON(devres_release(dev, devm_action_release, devm_action_match,
- &devres));
+ dr = remove_action(dev, action, data);
+ if (WARN_ON(IS_ERR(dr)))
+ return;
+
+ dr->action.action(dr->action.data);
+ kfree(dr);
}
EXPORT_SYMBOL_GPL(devm_release_action);
@@ -869,7 +956,7 @@ void *devm_kmalloc(struct device *dev, size_t size, gfp_t gfp)
* This is named devm_kzalloc_release for historical reasons
* The initial implementation did not support kmalloc, only kzalloc
*/
- set_node_dbginfo(&dr->node, "devm_kzalloc_release", size);
+ devres_set_node_dbginfo(&dr->node, "devm_kzalloc_release", size);
devres_add(dev, dr->data);
return dr->data;
}
@@ -940,6 +1027,8 @@ void *devm_krealloc(struct device *dev, void *ptr, size_t new_size, gfp_t gfp)
if (!new_dr)
return NULL;
+ devres_set_node_dbginfo(&new_dr->node, "devm_krealloc_release", new_size);
+
/*
* The spinlock protects the linked list against concurrent
* modifications but not the resource itself.
@@ -949,7 +1038,7 @@ void *devm_krealloc(struct device *dev, void *ptr, size_t new_size, gfp_t gfp)
old_dr = find_dr(dev, devm_kmalloc_release, devm_kmalloc_match, ptr);
if (!old_dr) {
spin_unlock_irqrestore(&dev->devres_lock, flags);
- kfree(new_dr);
+ free_dr(new_dr);
WARN(1, "Memory chunk not managed or managed by a different device.");
return NULL;
}
@@ -969,7 +1058,7 @@ void *devm_krealloc(struct device *dev, void *ptr, size_t new_size, gfp_t gfp)
* list. This is also the reason why we must not use devm_kfree() - the
* links are no longer valid.
*/
- kfree(old_dr);
+ free_dr(old_dr);
return new_dr->data;
}
diff --git a/drivers/base/init.c b/drivers/base/init.c
index 9d2b06d65dfc..af8014416c24 100644
--- a/drivers/base/init.c
+++ b/drivers/base/init.c
@@ -34,6 +34,7 @@ void __init driver_init(void)
*/
faux_bus_init();
of_core_init();
+ software_node_init();
platform_bus_init();
auxiliary_bus_init();
memory_dev_init();
diff --git a/drivers/base/memory.c b/drivers/base/memory.c
index a3091924918b..0d6ccc7cdf05 100644
--- a/drivers/base/memory.c
+++ b/drivers/base/memory.c
@@ -815,7 +815,7 @@ static int add_memory_block(unsigned long block_id, int nid, unsigned long state
/*
* MEM_ONLINE at this point implies early memory. With NUMA,
* we'll determine the zone when setting the node id via
- * memory_block_add_nid(). Memory hotplug updated the zone
+ * memory_block_add_nid_early(). Memory hotplug updated the zone
* manually when memory onlining/offlining succeeds.
*/
mem->zone = early_node_zone_for_memory_block(mem, NUMA_NO_NODE);
diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index d44591d52e36..75b4698d0e58 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -75,7 +75,7 @@ struct resource *platform_get_mem_or_io(struct platform_device *dev,
for (i = 0; i < dev->num_resources; i++) {
struct resource *r = &dev->resource[i];
- if ((resource_type(r) & (IORESOURCE_MEM|IORESOURCE_IO)) && num-- == 0)
+ if ((resource_type(r) & (IORESOURCE_MEM | IORESOURCE_IO)) && num-- == 0)
return r;
}
return NULL;
@@ -97,7 +97,7 @@ EXPORT_SYMBOL_GPL(platform_get_mem_or_io);
*/
void __iomem *
devm_platform_get_and_ioremap_resource(struct platform_device *pdev,
- unsigned int index, struct resource **res)
+ unsigned int index, struct resource **res)
{
struct resource *r;
@@ -172,7 +172,7 @@ static const struct cpumask *get_irq_affinity(struct platform_device *dev,
* @num: interrupt number index
* @affinity: optional cpumask pointer to get the affinity of a per-cpu interrupt
*
- * Gets an interupt for a platform device. Device drivers should check the
+ * Gets an interrupt for a platform device. Device drivers should check the
* return value for errors so as to not pass a negative integer value to
* the request_irq() APIs. Optional affinity information is provided in the
* affinity pointer if available, and NULL otherwise.
@@ -843,12 +843,14 @@ EXPORT_SYMBOL_GPL(platform_device_unregister);
*
* Returns &struct platform_device pointer on success, or ERR_PTR() on error.
*/
-struct platform_device *platform_device_register_full(
- const struct platform_device_info *pdevinfo)
+struct platform_device *platform_device_register_full(const struct platform_device_info *pdevinfo)
{
int ret;
struct platform_device *pdev;
+ if (pdevinfo->swnode && pdevinfo->properties)
+ return ERR_PTR(-EINVAL);
+
pdev = platform_device_alloc(pdevinfo->name, pdevinfo->id);
if (!pdev)
return ERR_PTR(-ENOMEM);
@@ -864,17 +866,19 @@ struct platform_device *platform_device_register_full(
pdev->dev.coherent_dma_mask = pdevinfo->dma_mask;
}
- ret = platform_device_add_resources(pdev,
- pdevinfo->res, pdevinfo->num_res);
+ ret = platform_device_add_resources(pdev, pdevinfo->res, pdevinfo->num_res);
if (ret)
goto err;
- ret = platform_device_add_data(pdev,
- pdevinfo->data, pdevinfo->size_data);
+ ret = platform_device_add_data(pdev, pdevinfo->data, pdevinfo->size_data);
if (ret)
goto err;
- if (pdevinfo->properties) {
+ if (pdevinfo->swnode) {
+ ret = device_add_software_node(&pdev->dev, pdevinfo->swnode);
+ if (ret)
+ goto err;
+ } else if (pdevinfo->properties) {
ret = device_create_managed_software_node(&pdev->dev,
pdevinfo->properties, NULL);
if (ret)
@@ -898,8 +902,7 @@ EXPORT_SYMBOL_GPL(platform_device_register_full);
* @drv: platform driver structure
* @owner: owning module/driver
*/
-int __platform_driver_register(struct platform_driver *drv,
- struct module *owner)
+int __platform_driver_register(struct platform_driver *drv, struct module *owner)
{
drv->driver.owner = owner;
drv->driver.bus = &platform_bus_type;
@@ -951,13 +954,14 @@ static int is_bound_to_driver(struct device *dev, void *driver)
* a negative error code and with the driver not registered.
*/
int __init_or_module __platform_driver_probe(struct platform_driver *drv,
- int (*probe)(struct platform_device *), struct module *module)
+ int (*probe)(struct platform_device *),
+ struct module *module)
{
int retval;
if (drv->driver.probe_type == PROBE_PREFER_ASYNCHRONOUS) {
pr_err("%s: drivers registered with %s can not be probed asynchronously\n",
- drv->driver.name, __func__);
+ drv->driver.name, __func__);
return -EINVAL;
}
@@ -1013,11 +1017,11 @@ EXPORT_SYMBOL_GPL(__platform_driver_probe);
*
* Returns &struct platform_device pointer on success, or ERR_PTR() on error.
*/
-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)
+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)
{
struct platform_device *pdev;
int error;
@@ -1116,9 +1120,8 @@ void platform_unregister_drivers(struct platform_driver * const *drivers,
}
EXPORT_SYMBOL_GPL(platform_unregister_drivers);
-static const struct platform_device_id *platform_match_id(
- const struct platform_device_id *id,
- struct platform_device *pdev)
+static const struct platform_device_id *
+platform_match_id(const struct platform_device_id *id, struct platform_device *pdev)
{
while (id->name[0]) {
if (strcmp(pdev->name, id->name) == 0) {
@@ -1311,13 +1314,12 @@ static struct attribute *platform_dev_attrs[] = {
NULL,
};
-static umode_t platform_dev_attrs_visible(struct kobject *kobj, struct attribute *a,
- int n)
+static umode_t platform_dev_attrs_visible(struct kobject *kobj,
+ struct attribute *a, int n)
{
struct device *dev = container_of(kobj, typeof(*dev), kobj);
- if (a == &dev_attr_numa_node.attr &&
- dev_to_node(dev) == NUMA_NO_NODE)
+ if (a == &dev_attr_numa_node.attr && dev_to_node(dev) == NUMA_NO_NODE)
return 0;
return a->mode;
@@ -1329,7 +1331,6 @@ static const struct attribute_group platform_dev_group = {
};
__ATTRIBUTE_GROUPS(platform_dev);
-
/**
* platform_match - bind platform device to platform driver.
* @dev: device.
@@ -1384,8 +1385,7 @@ static int platform_uevent(const struct device *dev, struct kobj_uevent_env *env
if (rc != -ENODEV)
return rc;
- add_uevent_var(env, "MODALIAS=%s%s", PLATFORM_MODULE_PREFIX,
- pdev->name);
+ add_uevent_var(env, "MODALIAS=%s%s", PLATFORM_MODULE_PREFIX, pdev->name);
return 0;
}
diff --git a/drivers/base/property.c b/drivers/base/property.c
index 8d9a34be57fb..d16e9c5f1921 100644
--- a/drivers/base/property.c
+++ b/drivers/base/property.c
@@ -38,6 +38,8 @@ EXPORT_SYMBOL_GPL(__dev_fwnode_const);
* @propname: Name of the property
*
* Check if property @propname is present in the device firmware description.
+ * This function is the unambiguous way to check that given property is present
+ * in the device firmware description.
*
* Return: true if property @propname is present. Otherwise, returns false.
*/
@@ -52,6 +54,10 @@ EXPORT_SYMBOL_GPL(device_property_present);
* @fwnode: Firmware node whose property to check
* @propname: Name of the property
*
+ * Check if property @propname is present in the firmware node description.
+ * This function is the unambiguous way to check that given property is present
+ * in the firmware node description.
+ *
* Return: true if property @propname is present. Otherwise, returns false.
*/
bool fwnode_property_present(const struct fwnode_handle *fwnode,
@@ -75,9 +81,9 @@ EXPORT_SYMBOL_GPL(fwnode_property_present);
* @dev: Device whose property is being checked
* @propname: Name of the property
*
- * Return if property @propname is true or false in the device firmware description.
+ * Use device_property_present() to check for the property presence.
*
- * Return: true if property @propname is present. Otherwise, returns false.
+ * Return: if property @propname is true or false in the device firmware description.
*/
bool device_property_read_bool(const struct device *dev, const char *propname)
{
@@ -90,7 +96,9 @@ EXPORT_SYMBOL_GPL(device_property_read_bool);
* @fwnode: Firmware node whose property to check
* @propname: Name of the property
*
- * Return if property @propname is true or false in the firmware description.
+ * Use fwnode_property_present() to check for the property presence.
+ *
+ * Return: if property @propname is true or false in the firmware node description.
*/
bool fwnode_property_read_bool(const struct fwnode_handle *fwnode,
const char *propname)
diff --git a/drivers/base/soc.c b/drivers/base/soc.c
index c8d3db9daa2f..65ce72d49230 100644
--- a/drivers/base/soc.c
+++ b/drivers/base/soc.c
@@ -5,16 +5,16 @@
* Author: Lee Jones <lee.jones@linaro.org> for ST-Ericsson.
*/
-#include <linux/sysfs.h>
+#include <linux/err.h>
+#include <linux/glob.h>
+#include <linux/idr.h>
#include <linux/init.h>
#include <linux/of.h>
-#include <linux/stat.h>
#include <linux/slab.h>
-#include <linux/idr.h>
#include <linux/spinlock.h>
+#include <linux/stat.h>
+#include <linux/sysfs.h>
#include <linux/sys_soc.h>
-#include <linux/err.h>
-#include <linux/glob.h>
static DEFINE_IDA(soc_ida);
@@ -111,17 +111,14 @@ static void soc_release(struct device *dev)
kfree(soc_dev);
}
-static void soc_device_get_machine(struct soc_device_attribute *soc_dev_attr)
+int soc_attr_read_machine(struct soc_device_attribute *soc_dev_attr)
{
- struct device_node *np;
-
if (soc_dev_attr->machine)
- return;
+ return -EBUSY;
- np = of_find_node_by_path("/");
- of_property_read_string(np, "model", &soc_dev_attr->machine);
- of_node_put(np);
+ return of_machine_read_model(&soc_dev_attr->machine);
}
+EXPORT_SYMBOL_GPL(soc_attr_read_machine);
static struct soc_device_attribute *early_soc_dev_attr;
@@ -131,7 +128,7 @@ struct soc_device *soc_device_register(struct soc_device_attribute *soc_dev_attr
const struct attribute_group **soc_attr_groups;
int ret;
- soc_device_get_machine(soc_dev_attr);
+ soc_attr_read_machine(soc_dev_attr);
if (!soc_bus_registered) {
if (early_soc_dev_attr)
diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c
index 51320837f3a9..a80575bf598b 100644
--- a/drivers/base/swnode.c
+++ b/drivers/base/swnode.c
@@ -1127,18 +1127,9 @@ void software_node_notify_remove(struct device *dev)
}
}
-static int __init software_node_init(void)
+void __init software_node_init(void)
{
swnode_kset = kset_create_and_add("software_nodes", NULL, kernel_kobj);
if (!swnode_kset)
- return -ENOMEM;
- return 0;
-}
-postcore_initcall(software_node_init);
-
-static void __exit software_node_exit(void)
-{
- ida_destroy(&swnode_root_ids);
- kset_unregister(swnode_kset);
+ pr_err("failed to register software nodes\n");
}
-__exitcall(software_node_exit);
diff --git a/drivers/bus/fsl-mc/fsl-mc-bus.c b/drivers/bus/fsl-mc/fsl-mc-bus.c
index c117745cf206..221146e4860b 100644
--- a/drivers/bus/fsl-mc/fsl-mc-bus.c
+++ b/drivers/bus/fsl-mc/fsl-mc-bus.c
@@ -86,12 +86,16 @@ static int fsl_mc_bus_match(struct device *dev, const struct device_driver *drv)
struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
const struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(drv);
bool found = false;
+ int ret;
/* When driver_override is set, only bind to the matching driver */
- if (mc_dev->driver_override) {
- found = !strcmp(mc_dev->driver_override, mc_drv->driver.name);
+ ret = device_match_driver_override(dev, drv);
+ if (ret > 0) {
+ found = true;
goto out;
}
+ if (ret == 0)
+ goto out;
if (!mc_drv->match_id_table)
goto out;
@@ -210,39 +214,8 @@ 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 fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
- int ret;
-
- if (WARN_ON(dev->bus != &fsl_mc_bus_type))
- return -EINVAL;
-
- ret = driver_set_override(dev, &mc_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 fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
- ssize_t len;
-
- device_lock(dev);
- len = sysfs_emit(buf, "%s\n", mc_dev->driver_override);
- device_unlock(dev);
- return len;
-}
-static DEVICE_ATTR_RW(driver_override);
-
static struct attribute *fsl_mc_dev_attrs[] = {
&dev_attr_modalias.attr,
- &dev_attr_driver_override.attr,
NULL,
};
@@ -345,6 +318,7 @@ ATTRIBUTE_GROUPS(fsl_mc_bus);
const struct bus_type fsl_mc_bus_type = {
.name = "fsl-mc",
+ .driver_override = true,
.match = fsl_mc_bus_match,
.uevent = fsl_mc_bus_uevent,
.probe = fsl_mc_probe,
@@ -910,9 +884,6 @@ static struct notifier_block fsl_mc_nb;
*/
void fsl_mc_device_remove(struct fsl_mc_device *mc_dev)
{
- kfree(mc_dev->driver_override);
- mc_dev->driver_override = NULL;
-
/*
* The device-specific remove callback will get invoked by device_del()
*/
diff --git a/drivers/bus/imx-weim.c b/drivers/bus/imx-weim.c
index 83d623d97f5f..f735e0462c55 100644
--- a/drivers/bus/imx-weim.c
+++ b/drivers/bus/imx-weim.c
@@ -332,7 +332,7 @@ static int of_weim_notify(struct notifier_block *nb, unsigned long action,
* fw_devlink doesn't skip adding consumers to this
* device.
*/
- rd->dn->fwnode.flags &= ~FWNODE_FLAG_NOT_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 eb7fb202355f..354a88d0599e 100644
--- a/drivers/i2c/i2c-core-of.c
+++ b/drivers/i2c/i2c-core-of.c
@@ -180,7 +180,7 @@ static int of_i2c_notify(struct notifier_block *nb, unsigned long action,
* Clear the flag before adding the device so that fw_devlink
* doesn't skip adding consumers to this device.
*/
- rd->dn->fwnode.flags &= ~FWNODE_FLAG_NOT_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/net/phy/mdio_bus_provider.c b/drivers/net/phy/mdio_bus_provider.c
index 4b0637405740..fd691c5424ea 100644
--- a/drivers/net/phy/mdio_bus_provider.c
+++ b/drivers/net/phy/mdio_bus_provider.c
@@ -294,8 +294,8 @@ int __mdiobus_register(struct mii_bus *bus, struct module *owner)
return -EINVAL;
if (bus->parent && bus->parent->of_node)
- bus->parent->of_node->fwnode.flags |=
- FWNODE_FLAG_NEEDS_CHILD_BOUND_ON_ADD;
+ fwnode_set_flag(&bus->parent->of_node->fwnode,
+ FWNODE_FLAG_NEEDS_CHILD_BOUND_ON_ADD);
WARN(bus->state != MDIOBUS_ALLOCATED &&
bus->state != MDIOBUS_UNREGISTERED,
diff --git a/drivers/of/base.c b/drivers/of/base.c
index 57420806c1a2..180dbce65b98 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -435,6 +435,34 @@ bool of_machine_compatible_match(const char *const *compats)
EXPORT_SYMBOL(of_machine_compatible_match);
/**
+ * of_machine_read_compatible - Get the compatible string of this machine
+ * @compatible: address at which the address of the compatible string will be
+ * stored
+ * @index: index of the compatible entry in the list
+ *
+ * Returns:
+ * 0 on success, negative error number on failure.
+ */
+int of_machine_read_compatible(const char **compatible, unsigned int index)
+{
+ return of_property_read_string_index(of_root, "compatible", index, compatible);
+}
+EXPORT_SYMBOL_GPL(of_machine_read_compatible);
+
+/**
+ * of_machine_read_model - Get the model string of this machine
+ * @model: address at which the address of the model string will be stored
+ *
+ * Returns:
+ * 0 on success, negative error number on failure.
+ */
+int of_machine_read_model(const char **model)
+{
+ return of_property_read_string(of_root, "model", model);
+}
+EXPORT_SYMBOL_GPL(of_machine_read_model);
+
+/**
* of_machine_device_match - Test root of device tree against a of_device_id array
* @matches: NULL terminated array of of_device_id match structures to search in
*
@@ -1915,7 +1943,7 @@ void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align))
if (name)
of_stdout = of_find_node_opts_by_path(name, &of_stdout_options);
if (of_stdout)
- of_stdout->fwnode.flags |= FWNODE_FLAG_BEST_EFFORT;
+ fwnode_set_flag(&of_stdout->fwnode, FWNODE_FLAG_BEST_EFFORT);
}
if (!of_aliases)
diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c
index 1a06175def37..ade288372101 100644
--- a/drivers/of/dynamic.c
+++ b/drivers/of/dynamic.c
@@ -225,7 +225,7 @@ 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);
- np->fwnode.flags |= FWNODE_FLAG_NOT_DEVICE;
+ 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 ba591fbceb56..7eeaf8e27b5b 100644
--- a/drivers/of/platform.c
+++ b/drivers/of/platform.c
@@ -742,7 +742,7 @@ static int of_platform_notify(struct notifier_block *nb,
* Clear the flag before adding the device so that fw_devlink
* doesn't skip adding consumers to this device.
*/
- rd->dn->fwnode.flags &= ~FWNODE_FLAG_NOT_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/pci/pci-driver.c b/drivers/pci/pci-driver.c
index dd9075403987..d10ece0889f0 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -138,9 +138,11 @@ static const struct pci_device_id *pci_match_device(struct pci_driver *drv,
{
struct pci_dynid *dynid;
const struct pci_device_id *found_id = NULL, *ids;
+ int ret;
/* When driver_override is set, only bind to the matching driver */
- if (dev->driver_override && strcmp(dev->driver_override, drv->name))
+ ret = device_match_driver_override(&dev->dev, &drv->driver);
+ if (ret == 0)
return NULL;
/* Look at the dynamic ids first, before the static ones */
@@ -164,7 +166,7 @@ static const struct pci_device_id *pci_match_device(struct pci_driver *drv,
* matching.
*/
if (found_id->override_only) {
- if (dev->driver_override)
+ if (ret > 0)
return found_id;
} else {
return found_id;
@@ -172,7 +174,7 @@ static const struct pci_device_id *pci_match_device(struct pci_driver *drv,
}
/* driver_override will always match, send a dummy id */
- if (dev->driver_override)
+ if (ret > 0)
return &pci_device_id_any;
return NULL;
}
@@ -452,7 +454,7 @@ static int __pci_device_probe(struct pci_driver *drv, struct pci_dev *pci_dev)
static inline bool pci_device_can_probe(struct pci_dev *pdev)
{
return (!pdev->is_virtfn || pdev->physfn->sriov->drivers_autoprobe ||
- pdev->driver_override);
+ device_has_driver_override(&pdev->dev));
}
#else
static inline bool pci_device_can_probe(struct pci_dev *pdev)
@@ -1722,6 +1724,7 @@ static const struct cpumask *pci_device_irq_get_affinity(struct device *dev,
const struct bus_type pci_bus_type = {
.name = "pci",
+ .driver_override = true,
.match = pci_bus_match,
.uevent = pci_uevent,
.probe = pci_device_probe,
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
index 16eaaf749ba9..a9006cf4e9c8 100644
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -615,33 +615,6 @@ static ssize_t devspec_show(struct device *dev,
static DEVICE_ATTR_RO(devspec);
#endif
-static ssize_t driver_override_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct pci_dev *pdev = to_pci_dev(dev);
- int ret;
-
- ret = driver_set_override(dev, &pdev->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 pci_dev *pdev = to_pci_dev(dev);
- ssize_t len;
-
- device_lock(dev);
- len = sysfs_emit(buf, "%s\n", pdev->driver_override);
- device_unlock(dev);
- return len;
-}
-static DEVICE_ATTR_RW(driver_override);
-
static struct attribute *pci_dev_attrs[] = {
&dev_attr_power_state.attr,
&dev_attr_resource.attr,
@@ -669,7 +642,6 @@ static struct attribute *pci_dev_attrs[] = {
#ifdef CONFIG_OF
&dev_attr_devspec.attr,
#endif
- &dev_attr_driver_override.attr,
&dev_attr_ari_enabled.attr,
NULL,
};
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index bccc7a4bdd79..b4707640e102 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -2488,7 +2488,6 @@ static void pci_release_dev(struct device *dev)
pci_release_of_node(pci_dev);
pcibios_release_device(pci_dev);
pci_bus_put(pci_dev->bus);
- kfree(pci_dev->driver_override);
bitmap_free(pci_dev->dma_alias_mask);
dev_dbg(dev, "device released\n");
kfree(pci_dev);
diff --git a/drivers/platform/wmi/core.c b/drivers/platform/wmi/core.c
index b8e6b9a421c6..750e3619724e 100644
--- a/drivers/platform/wmi/core.c
+++ b/drivers/platform/wmi/core.c
@@ -842,39 +842,11 @@ static ssize_t expensive_show(struct device *dev,
}
static DEVICE_ATTR_RO(expensive);
-static ssize_t driver_override_show(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct wmi_device *wdev = to_wmi_device(dev);
- ssize_t ret;
-
- device_lock(dev);
- ret = sysfs_emit(buf, "%s\n", wdev->driver_override);
- device_unlock(dev);
-
- return ret;
-}
-
-static ssize_t driver_override_store(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct wmi_device *wdev = to_wmi_device(dev);
- int ret;
-
- ret = driver_set_override(dev, &wdev->driver_override, buf, count);
- if (ret < 0)
- return ret;
-
- return count;
-}
-static DEVICE_ATTR_RW(driver_override);
-
static struct attribute *wmi_attrs[] = {
&dev_attr_modalias.attr,
&dev_attr_guid.attr,
&dev_attr_instance_count.attr,
&dev_attr_expensive.attr,
- &dev_attr_driver_override.attr,
NULL
};
ATTRIBUTE_GROUPS(wmi);
@@ -943,7 +915,6 @@ static void wmi_dev_release(struct device *dev)
{
struct wmi_block *wblock = dev_to_wblock(dev);
- kfree(wblock->dev.driver_override);
kfree(wblock);
}
@@ -952,10 +923,12 @@ static int wmi_dev_match(struct device *dev, const struct device_driver *driver)
const struct wmi_driver *wmi_driver = to_wmi_driver(driver);
struct wmi_block *wblock = dev_to_wblock(dev);
const struct wmi_device_id *id = wmi_driver->id_table;
+ int ret;
/* When driver_override is set, only bind to the matching driver */
- if (wblock->dev.driver_override)
- return !strcmp(wblock->dev.driver_override, driver->name);
+ ret = device_match_driver_override(dev, driver);
+ if (ret >= 0)
+ return ret;
if (id == NULL)
return 0;
@@ -1076,6 +1049,7 @@ static struct class wmi_bus_class = {
static const struct bus_type wmi_bus_type = {
.name = "wmi",
.dev_groups = wmi_groups,
+ .driver_override = true,
.match = wmi_dev_match,
.uevent = wmi_dev_uevent,
.probe = wmi_dev_probe,
diff --git a/drivers/s390/cio/cio.h b/drivers/s390/cio/cio.h
index 08a5e9380e75..bad142c536e1 100644
--- a/drivers/s390/cio/cio.h
+++ b/drivers/s390/cio/cio.h
@@ -103,11 +103,6 @@ struct subchannel {
struct work_struct todo_work;
struct schib_config config;
u64 dma_mask;
- /*
- * 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;
} __attribute__ ((aligned(8)));
DECLARE_PER_CPU_ALIGNED(struct irb, cio_irb);
diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c
index 5ab239f38588..e5a0ec6b4e3e 100644
--- a/drivers/s390/cio/css.c
+++ b/drivers/s390/cio/css.c
@@ -159,7 +159,6 @@ static void css_subchannel_release(struct device *dev)
sch->config.intparm = 0;
cio_commit_config(sch);
- kfree(sch->driver_override);
kfree(sch);
}
@@ -323,37 +322,9 @@ 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 subchannel *sch = to_subchannel(dev);
- int ret;
-
- ret = driver_set_override(dev, &sch->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 subchannel *sch = to_subchannel(dev);
- ssize_t len;
-
- device_lock(dev);
- len = sysfs_emit(buf, "%s\n", sch->driver_override);
- device_unlock(dev);
- return len;
-}
-static DEVICE_ATTR_RW(driver_override);
-
static struct attribute *subch_attrs[] = {
&dev_attr_type.attr,
&dev_attr_modalias.attr,
- &dev_attr_driver_override.attr,
NULL,
};
@@ -1356,9 +1327,11 @@ static int css_bus_match(struct device *dev, const struct device_driver *drv)
struct subchannel *sch = to_subchannel(dev);
const struct css_driver *driver = to_cssdriver(drv);
struct css_device_id *id;
+ int ret;
/* When driver_override is set, only bind to the matching driver */
- if (sch->driver_override && strcmp(sch->driver_override, drv->name))
+ ret = device_match_driver_override(dev, drv);
+ if (ret == 0)
return 0;
for (id = driver->subchannel_type; id->match_flags; id++) {
@@ -1415,6 +1388,7 @@ static int css_uevent(const struct device *dev, struct kobj_uevent_env *env)
static const struct bus_type css_bus_type = {
.name = "css",
+ .driver_override = true,
.match = css_bus_match,
.probe = css_probe,
.remove = css_remove,
diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c
index d652df96a507..f24e27add721 100644
--- a/drivers/s390/crypto/ap_bus.c
+++ b/drivers/s390/crypto/ap_bus.c
@@ -859,25 +859,24 @@ static int __ap_queue_devices_with_id_unregister(struct device *dev, void *data)
static int __ap_revise_reserved(struct device *dev, void *dummy)
{
- int rc, card, queue, devres, drvres;
+ int rc, card, queue, devres, drvres, ovrd;
if (is_queue_dev(dev)) {
struct ap_driver *ap_drv = to_ap_drv(dev->driver);
struct ap_queue *aq = to_ap_queue(dev);
- struct ap_device *ap_dev = &aq->ap_dev;
card = AP_QID_CARD(aq->qid);
queue = AP_QID_QUEUE(aq->qid);
- if (ap_dev->driver_override) {
- if (strcmp(ap_dev->driver_override,
- ap_drv->driver.name)) {
- pr_debug("reprobing queue=%02x.%04x\n", card, queue);
- rc = device_reprobe(dev);
- if (rc) {
- AP_DBF_WARN("%s reprobing queue=%02x.%04x failed\n",
- __func__, card, queue);
- }
+ ovrd = device_match_driver_override(dev, &ap_drv->driver);
+ if (ovrd > 0) {
+ /* override set and matches, nothing to do */
+ } else if (ovrd == 0) {
+ pr_debug("reprobing queue=%02x.%04x\n", card, queue);
+ rc = device_reprobe(dev);
+ if (rc) {
+ AP_DBF_WARN("%s reprobing queue=%02x.%04x failed\n",
+ __func__, card, queue);
}
} else {
mutex_lock(&ap_attr_mutex);
@@ -928,7 +927,7 @@ int ap_owned_by_def_drv(int card, int queue)
if (aq) {
const struct device_driver *drv = aq->ap_dev.device.driver;
const struct ap_driver *ap_drv = to_ap_drv(drv);
- bool override = !!aq->ap_dev.driver_override;
+ bool override = device_has_driver_override(&aq->ap_dev.device);
if (override && drv && ap_drv->flags & AP_DRIVER_FLAG_DEFAULT)
rc = 1;
@@ -977,7 +976,7 @@ static int ap_device_probe(struct device *dev)
{
struct ap_device *ap_dev = to_ap_dev(dev);
struct ap_driver *ap_drv = to_ap_drv(dev->driver);
- int card, queue, devres, drvres, rc = -ENODEV;
+ int card, queue, devres, drvres, rc = -ENODEV, ovrd;
if (!get_device(dev))
return rc;
@@ -991,10 +990,11 @@ static int ap_device_probe(struct device *dev)
*/
card = AP_QID_CARD(to_ap_queue(dev)->qid);
queue = AP_QID_QUEUE(to_ap_queue(dev)->qid);
- if (ap_dev->driver_override) {
- if (strcmp(ap_dev->driver_override,
- ap_drv->driver.name))
- goto out;
+ ovrd = device_match_driver_override(dev, &ap_drv->driver);
+ if (ovrd > 0) {
+ /* override set and matches, nothing to do */
+ } else if (ovrd == 0) {
+ goto out;
} else {
mutex_lock(&ap_attr_mutex);
devres = test_bit_inv(card, ap_perms.apm) &&
diff --git a/drivers/s390/crypto/ap_bus.h b/drivers/s390/crypto/ap_bus.h
index 51e08f27bd75..04ea256ecf91 100644
--- a/drivers/s390/crypto/ap_bus.h
+++ b/drivers/s390/crypto/ap_bus.h
@@ -166,7 +166,6 @@ void ap_driver_unregister(struct ap_driver *);
struct ap_device {
struct device device;
int device_type; /* AP device type. */
- const char *driver_override;
};
#define to_ap_dev(x) container_of((x), struct ap_device, device)
diff --git a/drivers/s390/crypto/ap_queue.c b/drivers/s390/crypto/ap_queue.c
index 3fe2e41c5c6b..ca9819e6f7e7 100644
--- a/drivers/s390/crypto/ap_queue.c
+++ b/drivers/s390/crypto/ap_queue.c
@@ -734,26 +734,14 @@ static ssize_t driver_override_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- struct ap_queue *aq = to_ap_queue(dev);
- struct ap_device *ap_dev = &aq->ap_dev;
- int rc;
-
- device_lock(dev);
- if (ap_dev->driver_override)
- rc = sysfs_emit(buf, "%s\n", ap_dev->driver_override);
- else
- rc = sysfs_emit(buf, "\n");
- device_unlock(dev);
-
- return rc;
+ guard(spinlock)(&dev->driver_override.lock);
+ return sysfs_emit(buf, "%s\n", dev->driver_override.name ?: "");
}
static ssize_t driver_override_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
- struct ap_queue *aq = to_ap_queue(dev);
- struct ap_device *ap_dev = &aq->ap_dev;
int rc = -EINVAL;
bool old_value;
@@ -764,13 +752,13 @@ static ssize_t driver_override_store(struct device *dev,
if (ap_apmask_aqmask_in_use)
goto out;
- old_value = ap_dev->driver_override ? true : false;
- rc = driver_set_override(dev, &ap_dev->driver_override, buf, count);
+ old_value = device_has_driver_override(dev);
+ rc = __device_set_driver_override(dev, buf, count);
if (rc)
goto out;
- if (old_value && !ap_dev->driver_override)
+ if (old_value && !device_has_driver_override(dev))
--ap_driver_override_ctr;
- else if (!old_value && ap_dev->driver_override)
+ else if (!old_value && device_has_driver_override(dev))
++ap_driver_override_ctr;
rc = count;
diff --git a/drivers/soc/fsl/guts.c b/drivers/soc/fsl/guts.c
index 40afb27b582b..9bee7baec2b9 100644
--- a/drivers/soc/fsl/guts.c
+++ b/drivers/soc/fsl/guts.c
@@ -186,7 +186,6 @@ static int __init fsl_guts_init(void)
const struct fsl_soc_data *soc_data;
const struct of_device_id *match;
struct ccsr_guts __iomem *regs;
- const char *machine = NULL;
struct device_node *np;
bool little_endian;
u64 soc_uid = 0;
@@ -217,13 +216,9 @@ static int __init fsl_guts_init(void)
if (!soc_dev_attr)
return -ENOMEM;
- if (of_property_read_string(of_root, "model", &machine))
- of_property_read_string_index(of_root, "compatible", 0, &machine);
- if (machine) {
- soc_dev_attr->machine = kstrdup(machine, GFP_KERNEL);
- if (!soc_dev_attr->machine)
- goto err_nomem;
- }
+ ret = soc_attr_read_machine(soc_dev_attr);
+ if (ret)
+ of_machine_read_compatible(&soc_dev_attr->machine, 0);
soc_die = fsl_soc_die_match(svr, fsl_soc_die);
if (soc_die) {
@@ -267,7 +262,6 @@ static int __init fsl_guts_init(void)
err_nomem:
ret = -ENOMEM;
err:
- kfree(soc_dev_attr->machine);
kfree(soc_dev_attr->family);
kfree(soc_dev_attr->soc_id);
kfree(soc_dev_attr->revision);
diff --git a/drivers/soc/imx/soc-imx8m.c b/drivers/soc/imx/soc-imx8m.c
index 8e2322999f09..77763a107edb 100644
--- a/drivers/soc/imx/soc-imx8m.c
+++ b/drivers/soc/imx/soc-imx8m.c
@@ -226,7 +226,6 @@ static int imx8m_soc_probe(struct platform_device *pdev)
const struct imx8_soc_data *data;
struct imx8_soc_drvdata *drvdata;
struct device *dev = &pdev->dev;
- const struct of_device_id *id;
struct soc_device *soc_dev;
u32 soc_rev = 0;
u64 soc_uid[2] = {0, 0};
@@ -244,15 +243,11 @@ static int imx8m_soc_probe(struct platform_device *pdev)
soc_dev_attr->family = "Freescale i.MX";
- ret = of_property_read_string(of_root, "model", &soc_dev_attr->machine);
+ ret = soc_attr_read_machine(soc_dev_attr);
if (ret)
return ret;
- id = of_match_node(imx8_soc_match, of_root);
- if (!id)
- return -ENODEV;
-
- data = id->data;
+ data = device_get_match_data(dev);
if (data) {
soc_dev_attr->soc_id = data->name;
ret = imx8m_soc_prepare(pdev, data->ocotp_compatible);
@@ -326,7 +321,7 @@ static int __init imx8_soc_init(void)
int ret;
/* No match means this is non-i.MX8M hardware, do nothing. */
- if (!of_match_node(imx8_soc_match, of_root))
+ if (!of_machine_device_match(imx8_soc_match))
return 0;
ret = platform_driver_register(&imx8m_soc_driver);
diff --git a/drivers/soc/imx/soc-imx9.c b/drivers/soc/imx/soc-imx9.c
index d67bc7402b10..58eef7d4f908 100644
--- a/drivers/soc/imx/soc-imx9.c
+++ b/drivers/soc/imx/soc-imx9.c
@@ -30,7 +30,7 @@ static int imx9_soc_probe(struct platform_device *pdev)
if (!attr)
return -ENOMEM;
- err = of_property_read_string(of_root, "model", &attr->machine);
+ err = soc_attr_read_machine(attr);
if (err)
return dev_err_probe(dev, err, "%s: missing model property\n", __func__);
@@ -89,7 +89,7 @@ static int __init imx9_soc_init(void)
struct platform_device *pdev;
/* No match means it is not an i.MX 9 series SoC, do nothing. */
- if (!of_match_node(imx9_soc_match, of_root))
+ if (!of_machine_device_match(imx9_soc_match))
return 0;
ret = platform_driver_register(&imx9_soc_driver);
diff --git a/drivers/soc/sunxi/sunxi_mbus.c b/drivers/soc/sunxi/sunxi_mbus.c
index 1734da357ca2..8bc5f62ff258 100644
--- a/drivers/soc/sunxi/sunxi_mbus.c
+++ b/drivers/soc/sunxi/sunxi_mbus.c
@@ -118,7 +118,7 @@ static const char * const sunxi_mbus_platforms[] __initconst = {
static int __init sunxi_mbus_init(void)
{
- if (!of_device_compatible_match(of_root, sunxi_mbus_platforms))
+ if (!of_machine_compatible_match(sunxi_mbus_platforms))
return 0;
bus_register_notifier(&platform_bus_type, &sunxi_mbus_nb);
diff --git a/drivers/soundwire/debugfs.c b/drivers/soundwire/debugfs.c
index ccc9670ef77c..2905ec19b838 100644
--- a/drivers/soundwire/debugfs.c
+++ b/drivers/soundwire/debugfs.c
@@ -358,8 +358,8 @@ void sdw_slave_debugfs_init(struct sdw_slave *slave)
debugfs_create_file("go", 0200, d, slave, &cmd_go_fops);
debugfs_create_file("read_buffer", 0400, d, slave, &read_buffer_fops);
- firmware_file = NULL;
- debugfs_create_str("firmware_file", 0200, d, &firmware_file);
+ if (firmware_file)
+ debugfs_create_str("firmware_file", 0200, d, &firmware_file);
slave->debugfs = d;
}
@@ -371,10 +371,15 @@ void sdw_slave_debugfs_exit(struct sdw_slave *slave)
void sdw_debugfs_init(void)
{
+ if (!firmware_file)
+ firmware_file = kstrdup("", GFP_KERNEL);
+
sdw_debugfs_root = debugfs_create_dir("soundwire", NULL);
}
void sdw_debugfs_exit(void)
{
debugfs_remove_recursive(sdw_debugfs_root);
+ kfree(firmware_file);
+ firmware_file = NULL;
}
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index 9b1125556d29..91dd831d2d3b 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -4943,7 +4943,7 @@ static int of_spi_notify(struct notifier_block *nb, unsigned long action,
* Clear the flag before adding the device so that fw_devlink
* doesn't skip adding consumers to this device.
*/
- rd->dn->fwnode.flags &= ~FWNODE_FLAG_NOT_DEVICE;
+ fwnode_clear_flag(&rd->dn->fwnode, FWNODE_FLAG_NOT_DEVICE);
spi = of_register_spi_device(ctlr, rd->dn);
put_device(&ctlr->dev);
diff --git a/drivers/vdpa/vdpa.c b/drivers/vdpa/vdpa.c
index 34874beb0152..caf0ee5d6856 100644
--- a/drivers/vdpa/vdpa.c
+++ b/drivers/vdpa/vdpa.c
@@ -67,57 +67,20 @@ static void vdpa_dev_remove(struct device *d)
static int vdpa_dev_match(struct device *dev, const struct device_driver *drv)
{
- struct vdpa_device *vdev = dev_to_vdpa(dev);
+ int ret;
/* Check override first, and if set, only use the named driver */
- if (vdev->driver_override)
- return strcmp(vdev->driver_override, drv->name) == 0;
+ ret = device_match_driver_override(dev, drv);
+ if (ret >= 0)
+ return ret;
/* Currently devices must be supported by all vDPA bus drivers */
return 1;
}
-static ssize_t driver_override_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct vdpa_device *vdev = dev_to_vdpa(dev);
- int ret;
-
- ret = driver_set_override(dev, &vdev->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 vdpa_device *vdev = dev_to_vdpa(dev);
- ssize_t len;
-
- device_lock(dev);
- len = sysfs_emit(buf, "%s\n", vdev->driver_override);
- device_unlock(dev);
-
- return len;
-}
-static DEVICE_ATTR_RW(driver_override);
-
-static struct attribute *vdpa_dev_attrs[] = {
- &dev_attr_driver_override.attr,
- NULL,
-};
-
-static const struct attribute_group vdpa_dev_group = {
- .attrs = vdpa_dev_attrs,
-};
-__ATTRIBUTE_GROUPS(vdpa_dev);
-
static const struct bus_type vdpa_bus = {
.name = "vdpa",
- .dev_groups = vdpa_dev_groups,
+ .driver_override = true,
.match = vdpa_dev_match,
.probe = vdpa_dev_probe,
.remove = vdpa_dev_remove,
@@ -132,7 +95,6 @@ static void vdpa_release_dev(struct device *d)
ops->free(vdev);
ida_free(&vdpa_index_ida, vdev->index);
- kfree(vdev->driver_override);
kfree(vdev);
}
diff --git a/drivers/vfio/fsl-mc/vfio_fsl_mc.c b/drivers/vfio/fsl-mc/vfio_fsl_mc.c
index 462fae1aa538..b4c3958201b2 100644
--- a/drivers/vfio/fsl-mc/vfio_fsl_mc.c
+++ b/drivers/vfio/fsl-mc/vfio_fsl_mc.c
@@ -424,9 +424,7 @@ static int vfio_fsl_mc_bus_notifier(struct notifier_block *nb,
if (action == BUS_NOTIFY_ADD_DEVICE &&
vdev->mc_dev == mc_cont) {
- mc_dev->driver_override = kasprintf(GFP_KERNEL, "%s",
- vfio_fsl_mc_ops.name);
- if (!mc_dev->driver_override)
+ if (device_set_driver_override(dev, vfio_fsl_mc_ops.name))
dev_warn(dev, "VFIO_FSL_MC: Setting driver override for device in dprc %s failed\n",
dev_name(&mc_cont->dev));
else
diff --git a/drivers/vfio/pci/vfio_pci_core.c b/drivers/vfio/pci/vfio_pci_core.c
index d43745fe4c84..460852f79f29 100644
--- a/drivers/vfio/pci/vfio_pci_core.c
+++ b/drivers/vfio/pci/vfio_pci_core.c
@@ -1987,9 +1987,8 @@ static int vfio_pci_bus_notifier(struct notifier_block *nb,
pdev->is_virtfn && physfn == vdev->pdev) {
pci_info(vdev->pdev, "Captured SR-IOV VF %s driver_override\n",
pci_name(pdev));
- pdev->driver_override = kasprintf(GFP_KERNEL, "%s",
- vdev->vdev.ops->name);
- WARN_ON(!pdev->driver_override);
+ WARN_ON(device_set_driver_override(&pdev->dev,
+ vdev->vdev.ops->name));
} else if (action == BUS_NOTIFY_BOUND_DRIVER &&
pdev->is_virtfn && physfn == vdev->pdev) {
struct pci_driver *drv = pci_dev_driver(pdev);
diff --git a/drivers/xen/xen-pciback/pci_stub.c b/drivers/xen/xen-pciback/pci_stub.c
index e4b27aecbf05..79a2b5dfd694 100644
--- a/drivers/xen/xen-pciback/pci_stub.c
+++ b/drivers/xen/xen-pciback/pci_stub.c
@@ -598,6 +598,8 @@ static int pcistub_seize(struct pci_dev *dev,
return err;
}
+static struct pci_driver xen_pcibk_pci_driver;
+
/* Called when 'bind'. This means we must _NOT_ call pci_reset_function or
* other functions that take the sysfs lock. */
static int pcistub_probe(struct pci_dev *dev, const struct pci_device_id *id)
@@ -609,8 +611,8 @@ static int pcistub_probe(struct pci_dev *dev, const struct pci_device_id *id)
match = pcistub_match(dev);
- if ((dev->driver_override &&
- !strcmp(dev->driver_override, PCISTUB_DRIVER_NAME)) ||
+ if (device_match_driver_override(&dev->dev,
+ &xen_pcibk_pci_driver.driver) > 0 ||
match) {
if (dev->hdr_type != PCI_HEADER_TYPE_NORMAL
diff --git a/fs/debugfs/file.c b/fs/debugfs/file.c
index 3376ab6a519d..edd6aafbfbaa 100644
--- a/fs/debugfs/file.c
+++ b/fs/debugfs/file.c
@@ -1047,7 +1047,6 @@ ssize_t debugfs_read_file_str(struct file *file, char __user *user_buf,
return ret;
}
-EXPORT_SYMBOL_GPL(debugfs_create_str);
static ssize_t debugfs_write_file_str(struct file *file, const char __user *user_buf,
size_t count, loff_t *ppos)
@@ -1127,7 +1126,7 @@ static const struct file_operations fops_str_wo = {
* directory dentry if set. If this parameter is %NULL, then the
* file will be created in the root of the debugfs filesystem.
* @value: a pointer to the variable that the file should read to and write
- * from.
+ * from. This pointer and the string it points to must not be %NULL.
*
* This function creates a file in debugfs with the given name that
* contains the value of the variable @value. If the @mode variable is so
@@ -1136,9 +1135,13 @@ static const struct file_operations fops_str_wo = {
void debugfs_create_str(const char *name, umode_t mode,
struct dentry *parent, char **value)
{
+ if (WARN_ON(!value || !*value))
+ return;
+
debugfs_create_mode_unsafe(name, mode, parent, value, &fops_str,
&fops_str_ro, &fops_str_wo);
}
+EXPORT_SYMBOL_GPL(debugfs_create_str);
static ssize_t read_file_blob(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c
index cdacdc1dcdd9..4f9ade82b08a 100644
--- a/fs/kernfs/dir.c
+++ b/fs/kernfs/dir.c
@@ -498,12 +498,14 @@ void kernfs_put_active(struct kernfs_node *kn)
/**
* kernfs_drain - drain kernfs_node
* @kn: kernfs_node to drain
+ * @drop_supers: Set to true if this function is called with the
+ * kernfs_supers_rwsem locked.
*
* Drain existing usages and nuke all existing mmaps of @kn. Multiple
* removers may invoke this function concurrently on @kn and all will
* return after draining is complete.
*/
-static void kernfs_drain(struct kernfs_node *kn)
+static void kernfs_drain(struct kernfs_node *kn, bool drop_supers)
__releases(&kernfs_root(kn)->kernfs_rwsem)
__acquires(&kernfs_root(kn)->kernfs_rwsem)
{
@@ -523,6 +525,8 @@ static void kernfs_drain(struct kernfs_node *kn)
return;
up_write(&root->kernfs_rwsem);
+ if (drop_supers)
+ up_read(&root->kernfs_supers_rwsem);
if (kernfs_lockdep(kn)) {
rwsem_acquire(&kn->dep_map, 0, 0, _RET_IP_);
@@ -541,6 +545,8 @@ static void kernfs_drain(struct kernfs_node *kn)
if (kernfs_should_drain_open_files(kn))
kernfs_drain_open_files(kn);
+ if (drop_supers)
+ down_read(&root->kernfs_supers_rwsem);
down_write(&root->kernfs_rwsem);
}
@@ -1492,12 +1498,43 @@ void kernfs_show(struct kernfs_node *kn, bool show)
kn->flags |= KERNFS_HIDDEN;
if (kernfs_active(kn))
atomic_add(KN_DEACTIVATED_BIAS, &kn->active);
- kernfs_drain(kn);
+ kernfs_drain(kn, false);
}
up_write(&root->kernfs_rwsem);
}
+/*
+ * This function enables VFS to send fsnotify events for deletions.
+ * There is gap in this implementation for certain file removals due their
+ * unique nature in kernfs. Directory removals that trigger file removals occur
+ * through vfs_rmdir, which shrinks the dcache and emits fsnotify events after
+ * the rmdir operation; there is no issue here. However kernfs writes to
+ * particular files (e.g. cgroup.subtree_control) can also cause file removal,
+ * but vfs_write does not attempt to emit fsnotify events after the write
+ * operation, even if i_nlink counts are 0. As a usecase for monitoring this
+ * category of file removals is not known, they are left without having
+ * IN_DELETE or IN_DELETE_SELF events generated.
+ * Fanotify recursive monitoring also does not work for kernfs nodes that do not
+ * have inodes attached, as they are created on-demand in kernfs.
+ */
+static void kernfs_clear_inode_nlink(struct kernfs_node *kn)
+{
+ struct kernfs_root *root = kernfs_root(kn);
+ struct kernfs_super_info *info;
+
+ lockdep_assert_held_read(&root->kernfs_supers_rwsem);
+
+ list_for_each_entry(info, &root->supers, node) {
+ struct inode *inode = ilookup(info->sb, kernfs_ino(kn));
+
+ if (inode) {
+ clear_nlink(inode);
+ iput(inode);
+ }
+ }
+}
+
static void __kernfs_remove(struct kernfs_node *kn)
{
struct kernfs_node *pos, *parent;
@@ -1506,6 +1543,7 @@ static void __kernfs_remove(struct kernfs_node *kn)
if (!kn)
return;
+ lockdep_assert_held_read(&kernfs_root(kn)->kernfs_supers_rwsem);
lockdep_assert_held_write(&kernfs_root(kn)->kernfs_rwsem);
/*
@@ -1518,12 +1556,14 @@ static void __kernfs_remove(struct kernfs_node *kn)
pr_debug("kernfs %s: removing\n", kernfs_rcu_name(kn));
/* prevent new usage by marking all nodes removing and deactivating */
+ down_write(&kernfs_root(kn)->kernfs_iattr_rwsem);
pos = NULL;
while ((pos = kernfs_next_descendant_post(pos, kn))) {
pos->flags |= KERNFS_REMOVING;
if (kernfs_active(pos))
atomic_add(KN_DEACTIVATED_BIAS, &pos->active);
}
+ up_write(&kernfs_root(kn)->kernfs_iattr_rwsem);
/* deactivate and unlink the subtree node-by-node */
do {
@@ -1537,7 +1577,7 @@ static void __kernfs_remove(struct kernfs_node *kn)
*/
kernfs_get(pos);
- kernfs_drain(pos);
+ kernfs_drain(pos, true);
parent = kernfs_parent(pos);
/*
* kernfs_unlink_sibling() succeeds once per node. Use it
@@ -1547,9 +1587,11 @@ static void __kernfs_remove(struct kernfs_node *kn)
struct kernfs_iattrs *ps_iattr =
parent ? parent->iattr : NULL;
- /* update timestamps on the parent */
down_write(&kernfs_root(kn)->kernfs_iattr_rwsem);
+ kernfs_clear_inode_nlink(pos);
+
+ /* update timestamps on the parent */
if (ps_iattr) {
ktime_get_real_ts64(&ps_iattr->ia_ctime);
ps_iattr->ia_mtime = ps_iattr->ia_ctime;
@@ -1578,9 +1620,11 @@ void kernfs_remove(struct kernfs_node *kn)
root = kernfs_root(kn);
+ down_read(&root->kernfs_supers_rwsem);
down_write(&root->kernfs_rwsem);
__kernfs_remove(kn);
up_write(&root->kernfs_rwsem);
+ up_read(&root->kernfs_supers_rwsem);
}
/**
@@ -1671,6 +1715,7 @@ bool kernfs_remove_self(struct kernfs_node *kn)
bool ret;
struct kernfs_root *root = kernfs_root(kn);
+ down_read(&root->kernfs_supers_rwsem);
down_write(&root->kernfs_rwsem);
kernfs_break_active_protection(kn);
@@ -1700,7 +1745,9 @@ bool kernfs_remove_self(struct kernfs_node *kn)
break;
up_write(&root->kernfs_rwsem);
+ up_read(&root->kernfs_supers_rwsem);
schedule();
+ down_read(&root->kernfs_supers_rwsem);
down_write(&root->kernfs_rwsem);
}
finish_wait(waitq, &wait);
@@ -1715,6 +1762,7 @@ bool kernfs_remove_self(struct kernfs_node *kn)
kernfs_unbreak_active_protection(kn);
up_write(&root->kernfs_rwsem);
+ up_read(&root->kernfs_supers_rwsem);
return ret;
}
@@ -1741,6 +1789,7 @@ int kernfs_remove_by_name_ns(struct kernfs_node *parent, const char *name,
}
root = kernfs_root(parent);
+ down_read(&root->kernfs_supers_rwsem);
down_write(&root->kernfs_rwsem);
kn = kernfs_find_ns(parent, name, ns);
@@ -1751,6 +1800,7 @@ int kernfs_remove_by_name_ns(struct kernfs_node *parent, const char *name,
}
up_write(&root->kernfs_rwsem);
+ up_read(&root->kernfs_supers_rwsem);
if (kn)
return 0;
diff --git a/fs/kernfs/inode.c b/fs/kernfs/inode.c
index 1de10500842d..38b28aa7cd02 100644
--- a/fs/kernfs/inode.c
+++ b/fs/kernfs/inode.c
@@ -177,7 +177,7 @@ static void kernfs_refresh_inode(struct kernfs_node *kn, struct inode *inode)
*/
set_inode_attr(inode, attrs);
- if (kernfs_type(kn) == KERNFS_DIR)
+ if (kernfs_type(kn) == KERNFS_DIR && !(kn->flags & KERNFS_REMOVING))
set_nlink(inode, kn->dir.subdirs + 2);
}
diff --git a/fs/sysfs/group.c b/fs/sysfs/group.c
index e1e639f515a0..b3edae0578c0 100644
--- a/fs/sysfs/group.c
+++ b/fs/sysfs/group.c
@@ -217,7 +217,7 @@ int sysfs_create_group(struct kobject *kobj,
EXPORT_SYMBOL_GPL(sysfs_create_group);
static int internal_create_groups(struct kobject *kobj, int update,
- const struct attribute_group **groups)
+ const struct attribute_group *const *groups)
{
int error = 0;
int i;
@@ -250,7 +250,7 @@ static int internal_create_groups(struct kobject *kobj, int update,
* Returns 0 on success or error code from sysfs_create_group on failure.
*/
int sysfs_create_groups(struct kobject *kobj,
- const struct attribute_group **groups)
+ const struct attribute_group *const *groups)
{
return internal_create_groups(kobj, 0, groups);
}
@@ -268,7 +268,7 @@ EXPORT_SYMBOL_GPL(sysfs_create_groups);
* Returns 0 on success or error code from sysfs_update_group on failure.
*/
int sysfs_update_groups(struct kobject *kobj,
- const struct attribute_group **groups)
+ const struct attribute_group *const *groups)
{
return internal_create_groups(kobj, 1, groups);
}
@@ -342,7 +342,7 @@ EXPORT_SYMBOL_GPL(sysfs_remove_group);
* If groups is not NULL, remove the specified groups from the kobject.
*/
void sysfs_remove_groups(struct kobject *kobj,
- const struct attribute_group **groups)
+ const struct attribute_group *const *groups)
{
int i;
@@ -613,7 +613,7 @@ EXPORT_SYMBOL_GPL(sysfs_group_change_owner);
* Returns 0 on success or error code on failure.
*/
int sysfs_groups_change_owner(struct kobject *kobj,
- const struct attribute_group **groups,
+ const struct attribute_group *const *groups,
kuid_t kuid, kgid_t kgid)
{
int error = 0, i;
diff --git a/include/linux/device.h b/include/linux/device.h
index e65d564f01cd..f0d52e1a6e07 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -965,6 +965,7 @@ static inline void device_unlock(struct device *dev)
}
DEFINE_GUARD(device, struct device *, device_lock(_T), device_unlock(_T))
+DEFINE_GUARD_COND(device, _intr, device_lock_interruptible(_T), _RET == 0)
static inline void device_lock_assert(struct device *dev)
{
@@ -1185,9 +1186,9 @@ device_create_with_groups(const struct class *cls, struct device *parent, dev_t
void device_destroy(const struct class *cls, dev_t devt);
int __must_check device_add_groups(struct device *dev,
- const struct attribute_group **groups);
+ const struct attribute_group *const *groups);
void device_remove_groups(struct device *dev,
- const struct attribute_group **groups);
+ const struct attribute_group *const *groups);
static inline int __must_check device_add_group(struct device *dev,
const struct attribute_group *grp)
diff --git a/include/linux/device/class.h b/include/linux/device/class.h
index 021da0d61796..78ab8d2b3e30 100644
--- a/include/linux/device/class.h
+++ b/include/linux/device/class.h
@@ -50,8 +50,8 @@ struct fwnode_handle;
struct class {
const char *name;
- const struct attribute_group **class_groups;
- const struct attribute_group **dev_groups;
+ const struct attribute_group *const *class_groups;
+ const struct attribute_group *const *dev_groups;
int (*dev_uevent)(const struct device *dev, struct kobj_uevent_env *env);
char *(*devnode)(const struct device *dev, umode_t *mode);
diff --git a/include/linux/fsl/mc.h b/include/linux/fsl/mc.h
index 897d6211c163..1da63f2d7040 100644
--- a/include/linux/fsl/mc.h
+++ b/include/linux/fsl/mc.h
@@ -178,9 +178,6 @@ struct fsl_mc_obj_desc {
* @regions: pointer to array of MMIO region entries
* @irqs: pointer to array of pointers to interrupts allocated to this device
* @resource: generic resource associated with this MC object device, if any.
- * @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.
*
* Generic device object for MC object devices that are "attached" to a
* MC bus.
@@ -214,7 +211,6 @@ struct fsl_mc_device {
struct fsl_mc_device_irq **irqs;
struct fsl_mc_resource *resource;
struct device_link *consumer_link;
- const char *driver_override;
};
#define to_fsl_mc_device(_dev) \
diff --git a/include/linux/fwnode.h b/include/linux/fwnode.h
index 097be89487bf..80b38fbf2121 100644
--- a/include/linux/fwnode.h
+++ b/include/linux/fwnode.h
@@ -15,6 +15,7 @@
#define _LINUX_FWNODE_H_
#include <linux/bits.h>
+#include <linux/bitops.h>
#include <linux/err.h>
#include <linux/list.h>
#include <linux/types.h>
@@ -42,12 +43,12 @@ struct device;
* suppliers. Only enforce ordering with suppliers that have
* drivers.
*/
-#define FWNODE_FLAG_LINKS_ADDED BIT(0)
-#define FWNODE_FLAG_NOT_DEVICE BIT(1)
-#define FWNODE_FLAG_INITIALIZED BIT(2)
-#define FWNODE_FLAG_NEEDS_CHILD_BOUND_ON_ADD BIT(3)
-#define FWNODE_FLAG_BEST_EFFORT BIT(4)
-#define FWNODE_FLAG_VISITED BIT(5)
+#define FWNODE_FLAG_LINKS_ADDED 0
+#define FWNODE_FLAG_NOT_DEVICE 1
+#define FWNODE_FLAG_INITIALIZED 2
+#define FWNODE_FLAG_NEEDS_CHILD_BOUND_ON_ADD 3
+#define FWNODE_FLAG_BEST_EFFORT 4
+#define FWNODE_FLAG_VISITED 5
struct fwnode_handle {
struct fwnode_handle *secondary;
@@ -57,7 +58,7 @@ struct fwnode_handle {
struct device *dev;
struct list_head suppliers;
struct list_head consumers;
- u8 flags;
+ unsigned long flags;
};
/*
@@ -212,16 +213,37 @@ static inline void fwnode_init(struct fwnode_handle *fwnode,
INIT_LIST_HEAD(&fwnode->suppliers);
}
+static inline void fwnode_set_flag(struct fwnode_handle *fwnode,
+ unsigned int bit)
+{
+ set_bit(bit, &fwnode->flags);
+}
+
+static inline void fwnode_clear_flag(struct fwnode_handle *fwnode,
+ unsigned int bit)
+{
+ clear_bit(bit, &fwnode->flags);
+}
+
+static inline void fwnode_assign_flag(struct fwnode_handle *fwnode,
+ unsigned int bit, bool value)
+{
+ assign_bit(bit, &fwnode->flags, value);
+}
+
+static inline bool fwnode_test_flag(struct fwnode_handle *fwnode,
+ unsigned int bit)
+{
+ return test_bit(bit, &fwnode->flags);
+}
+
static inline void fwnode_dev_initialized(struct fwnode_handle *fwnode,
bool initialized)
{
if (IS_ERR_OR_NULL(fwnode))
return;
- if (initialized)
- fwnode->flags |= FWNODE_FLAG_INITIALIZED;
- else
- fwnode->flags &= ~FWNODE_FLAG_INITIALIZED;
+ fwnode_assign_flag(fwnode, FWNODE_FLAG_INITIALIZED, initialized);
}
int fwnode_link_add(struct fwnode_handle *con, struct fwnode_handle *sup,
diff --git a/include/linux/ksysfs.h b/include/linux/ksysfs.h
new file mode 100644
index 000000000000..c7dc6e18f28e
--- /dev/null
+++ b/include/linux/ksysfs.h
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _KSYSFS_H_
+#define _KSYSFS_H_
+
+void ksysfs_init(void);
+
+#endif /* _KSYSFS_H_ */
diff --git a/include/linux/of.h b/include/linux/of.h
index be6ec4916adf..2b95777f16f6 100644
--- a/include/linux/of.h
+++ b/include/linux/of.h
@@ -426,6 +426,9 @@ static inline bool of_machine_is_compatible(const char *compat)
return of_machine_compatible_match(compats);
}
+int of_machine_read_compatible(const char **compatible, unsigned int index);
+int of_machine_read_model(const char **model);
+
extern int of_add_property(struct device_node *np, struct property *prop);
extern int of_remove_property(struct device_node *np, struct property *prop);
extern int of_update_property(struct device_node *np, struct property *newprop);
@@ -851,6 +854,17 @@ static inline int of_machine_is_compatible(const char *compat)
return 0;
}
+static inline int of_machine_read_compatible(const char **compatible,
+ unsigned int index)
+{
+ return -ENOSYS;
+}
+
+static inline int of_machine_read_model(const char **model)
+{
+ return -ENOSYS;
+}
+
static inline int of_add_property(struct device_node *np, struct property *prop)
{
return 0;
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 1c270f1d5123..57e9463e4347 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -575,12 +575,6 @@ struct pci_dev {
u8 supported_speeds; /* Supported Link Speeds Vector */
phys_addr_t rom; /* Physical address if not from BAR */
size_t romlen; /* Length if not from BAR */
- /*
- * 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;
-
unsigned long priv_flags; /* Private flags for the PCI driver */
/* These methods index pci_reset_fn_methods[] */
diff --git a/include/linux/platform_device.h b/include/linux/platform_device.h
index ed1d50d1c3c1..975400a472e3 100644
--- a/include/linux/platform_device.h
+++ b/include/linux/platform_device.h
@@ -113,22 +113,58 @@ extern int platform_get_irq_byname_optional(struct platform_device *dev,
const char *name);
extern int platform_add_devices(struct platform_device **, int);
+/**
+ * struct platform_device_info - set of parameters for creating a platform device
+ * @parent: parent device for the new platform device.
+ * @fwnode: firmware node associated with the device.
+ * @of_node_reused: indicates that device tree node associated with the device
+ * is shared with another device, typically its ancestor. Setting this to
+ * %true prevents the device from being matched via the OF match table,
+ * and stops the device core from automatically binding pinctrl
+ * configuration to avoid disrupting the other device.
+ * @name: name of the device.
+ * @id: instance ID of the device. Use %PLATFORM_DEVID_NONE if there is only
+ * one instance of the device, or %PLATFORM_DEVID_AUTO to let the
+ * kernel automatically assign a unique instance ID.
+ * @res: set of resources to attach to the device.
+ * @num_res: number of entries in @res.
+ * @data: device-specific data for this platform device.
+ * @size_data: size of device-specific data.
+ * @dma_mask: DMA mask for the device.
+ * @swnode: a secondary software node to be attached to the device. The node
+ * will be automatically registered and its lifetime tied to the platform
+ * device if it is not registered yet.
+ * @properties: a set of software properties for the device. If provided,
+ * a managed software node will be automatically created and
+ * assigned to the device. The properties array must be terminated
+ * with a sentinel entry. Specifying both @properties and @swnode is not
+ * allowed.
+ *
+ * This structure is used to hold information needed to create and register
+ * a platform device using platform_device_register_full().
+ *
+ * platform_device_register_full() makes deep copies of @name, @res, @data and
+ * @properties, so the caller does not need to keep them after registration.
+ * If the registration is performed during initialization, these can be marked
+ * as __initconst.
+ */
struct platform_device_info {
- struct device *parent;
- struct fwnode_handle *fwnode;
- bool of_node_reused;
+ struct device *parent;
+ struct fwnode_handle *fwnode;
+ bool of_node_reused;
- const char *name;
- int id;
+ const char *name;
+ int id;
- const struct resource *res;
- unsigned int num_res;
+ const struct resource *res;
+ unsigned int num_res;
- const void *data;
- size_t size_data;
- u64 dma_mask;
+ const void *data;
+ size_t size_data;
+ u64 dma_mask;
- const struct property_entry *properties;
+ const struct software_node *swnode;
+ const struct property_entry *properties;
};
extern struct platform_device *platform_device_register_full(
const struct platform_device_info *pdevinfo);
diff --git a/include/linux/sys_soc.h b/include/linux/sys_soc.h
index d9b3cf0f410c..f19f5cec18e2 100644
--- a/include/linux/sys_soc.h
+++ b/include/linux/sys_soc.h
@@ -37,6 +37,16 @@ void soc_device_unregister(struct soc_device *soc_dev);
*/
struct device *soc_device_to_device(struct soc_device *soc);
+/**
+ * soc_attr_read_machine - retrieve the machine model and store it in
+ * the soc_device_attribute structure
+ * @soc_dev_attr: SoC attribute structure to store the model in
+ *
+ * Returns:
+ * 0 on success, negative error number on failure.
+ */
+int soc_attr_read_machine(struct soc_device_attribute *soc_dev_attr);
+
#ifdef CONFIG_SOC_BUS
const struct soc_device_attribute *soc_device_match(
const struct soc_device_attribute *matches);
diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h
index 468259fb6049..b1a3a1e6ad09 100644
--- a/include/linux/sysfs.h
+++ b/include/linux/sysfs.h
@@ -445,15 +445,15 @@ void sysfs_delete_link(struct kobject *dir, struct kobject *targ,
int __must_check sysfs_create_group(struct kobject *kobj,
const struct attribute_group *grp);
int __must_check sysfs_create_groups(struct kobject *kobj,
- const struct attribute_group **groups);
+ const struct attribute_group *const *groups);
int __must_check sysfs_update_groups(struct kobject *kobj,
- const struct attribute_group **groups);
+ const struct attribute_group *const *groups);
int sysfs_update_group(struct kobject *kobj,
const struct attribute_group *grp);
void sysfs_remove_group(struct kobject *kobj,
const struct attribute_group *grp);
void sysfs_remove_groups(struct kobject *kobj,
- const struct attribute_group **groups);
+ const struct attribute_group *const *groups);
int sysfs_add_file_to_group(struct kobject *kobj,
const struct attribute *attr, const char *group);
void sysfs_remove_file_from_group(struct kobject *kobj,
@@ -486,7 +486,7 @@ int sysfs_change_owner(struct kobject *kobj, kuid_t kuid, kgid_t kgid);
int sysfs_link_change_owner(struct kobject *kobj, struct kobject *targ,
const char *name, kuid_t kuid, kgid_t kgid);
int sysfs_groups_change_owner(struct kobject *kobj,
- const struct attribute_group **groups,
+ const struct attribute_group *const *groups,
kuid_t kuid, kgid_t kgid);
int sysfs_group_change_owner(struct kobject *kobj,
const struct attribute_group *groups, kuid_t kuid,
@@ -629,13 +629,13 @@ static inline int sysfs_create_group(struct kobject *kobj,
}
static inline int sysfs_create_groups(struct kobject *kobj,
- const struct attribute_group **groups)
+ const struct attribute_group *const *groups)
{
return 0;
}
static inline int sysfs_update_groups(struct kobject *kobj,
- const struct attribute_group **groups)
+ const struct attribute_group *const *groups)
{
return 0;
}
@@ -652,7 +652,7 @@ static inline void sysfs_remove_group(struct kobject *kobj,
}
static inline void sysfs_remove_groups(struct kobject *kobj,
- const struct attribute_group **groups)
+ const struct attribute_group *const *groups)
{
}
@@ -733,7 +733,7 @@ static inline int sysfs_change_owner(struct kobject *kobj, kuid_t kuid, kgid_t k
}
static inline int sysfs_groups_change_owner(struct kobject *kobj,
- const struct attribute_group **groups,
+ const struct attribute_group *const *groups,
kuid_t kuid, kgid_t kgid)
{
return 0;
diff --git a/include/linux/vdpa.h b/include/linux/vdpa.h
index 2bfe3baa63f4..782c42d25db1 100644
--- a/include/linux/vdpa.h
+++ b/include/linux/vdpa.h
@@ -72,9 +72,6 @@ struct vdpa_mgmt_dev;
* struct vdpa_device - representation of a vDPA device
* @dev: underlying device
* @vmap: the metadata passed to upper layer to be used for mapping
- * @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.
* @config: the configuration ops for this device.
* @map: the map ops for this device
* @cf_lock: Protects get and set access to configuration layout.
@@ -90,7 +87,6 @@ struct vdpa_mgmt_dev;
struct vdpa_device {
struct device dev;
union virtio_map vmap;
- const char *driver_override;
const struct vdpa_config_ops *config;
const struct virtio_map_ops *map;
struct rw_semaphore cf_lock; /* Protects get/set config */
diff --git a/include/linux/wmi.h b/include/linux/wmi.h
index 75cb0c7cfe57..14fb644e1701 100644
--- a/include/linux/wmi.h
+++ b/include/linux/wmi.h
@@ -18,16 +18,12 @@
* struct wmi_device - WMI device structure
* @dev: Device associated with this WMI device
* @setable: True for devices implementing the Set Control Method
- * @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.
*
* This represents WMI devices discovered by the WMI driver core.
*/
struct wmi_device {
struct device dev;
bool setable;
- const char *driver_override;
};
/**
diff --git a/init/main.c b/init/main.c
index c9638a6946dc..9481e835406f 100644
--- a/init/main.c
+++ b/init/main.c
@@ -36,6 +36,7 @@
#include <linux/kmod.h>
#include <linux/kprobes.h>
#include <linux/kmsan.h>
+#include <linux/ksysfs.h>
#include <linux/vmalloc.h>
#include <linux/kernel_stat.h>
#include <linux/start_kernel.h>
@@ -1480,6 +1481,7 @@ static void __init do_initcalls(void)
static void __init do_basic_setup(void)
{
cpuset_init_smp();
+ ksysfs_init();
driver_init();
init_irq_proc();
do_ctors();
diff --git a/kernel/ksysfs.c b/kernel/ksysfs.c
index a9e6354d9e25..f45ade718054 100644
--- a/kernel/ksysfs.c
+++ b/kernel/ksysfs.c
@@ -8,6 +8,7 @@
#include <asm/byteorder.h>
#include <linux/kobject.h>
+#include <linux/ksysfs.h>
#include <linux/string.h>
#include <linux/sysfs.h>
#include <linux/export.h>
@@ -213,7 +214,7 @@ static const struct attribute_group kernel_attr_group = {
.attrs = kernel_attrs,
};
-static int __init ksysfs_init(void)
+void __init ksysfs_init(void)
{
int error;
@@ -234,14 +235,12 @@ static int __init ksysfs_init(void)
goto group_exit;
}
- return 0;
+ return;
group_exit:
sysfs_remove_group(kernel_kobj, &kernel_attr_group);
kset_exit:
kobject_put(kernel_kobj);
exit:
- return error;
+ pr_err("failed to initialize the kernel kobject: %d\n", error);
}
-
-core_initcall(ksysfs_init);
diff --git a/rust/kernel/devres.rs b/rust/kernel/devres.rs
index 6afe196be42c..9e5f93aed20c 100644
--- a/rust/kernel/devres.rs
+++ b/rust/kernel/devres.rs
@@ -23,9 +23,22 @@ use crate::{
rcu,
Arc, //
},
- types::ForeignOwnable,
+ types::{
+ ForeignOwnable,
+ Opaque, //
+ },
};
+/// Inner type that embeds a `struct devres_node` and the `Revocable<T>`.
+#[repr(C)]
+#[pin_data]
+struct Inner<T> {
+ #[pin]
+ node: Opaque<bindings::devres_node>,
+ #[pin]
+ data: Revocable<T>,
+}
+
/// This abstraction is meant to be used by subsystems to containerize [`Device`] bound resources to
/// manage their lifetime.
///
@@ -111,12 +124,64 @@ use crate::{
/// ```
pub struct Devres<T: Send> {
dev: ARef<Device>,
- /// Pointer to [`Self::devres_callback`].
- ///
- /// Has to be stored, since Rust does not guarantee to always return the same address for a
- /// function. However, the C API uses the address as a key.
- callback: unsafe extern "C" fn(*mut c_void),
- data: Arc<Revocable<T>>,
+ inner: Arc<Inner<T>>,
+}
+
+// Calling the FFI functions from the `base` module directly from the `Devres<T>` impl may result in
+// them 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.
+mod base {
+ use kernel::{
+ bindings,
+ prelude::*, //
+ };
+
+ #[inline(never)]
+ #[allow(clippy::missing_safety_doc)]
+ pub(super) unsafe fn devres_node_init(
+ node: *mut bindings::devres_node,
+ release: bindings::dr_node_release_t,
+ free: bindings::dr_node_free_t,
+ ) {
+ // SAFETY: Safety requirements are the same as `bindings::devres_node_init`.
+ unsafe { bindings::devres_node_init(node, release, free) }
+ }
+
+ #[inline(never)]
+ #[allow(clippy::missing_safety_doc)]
+ pub(super) unsafe fn devres_set_node_dbginfo(
+ node: *mut bindings::devres_node,
+ name: *const c_char,
+ size: usize,
+ ) {
+ // SAFETY: Safety requirements are the same as `bindings::devres_set_node_dbginfo`.
+ unsafe { bindings::devres_set_node_dbginfo(node, name, size) }
+ }
+
+ #[inline(never)]
+ #[allow(clippy::missing_safety_doc)]
+ pub(super) unsafe fn devres_node_add(
+ dev: *mut bindings::device,
+ node: *mut bindings::devres_node,
+ ) {
+ // SAFETY: Safety requirements are the same as `bindings::devres_node_add`.
+ unsafe { bindings::devres_node_add(dev, node) }
+ }
+
+ #[must_use]
+ #[inline(never)]
+ #[allow(clippy::missing_safety_doc)]
+ pub(super) unsafe fn devres_node_remove(
+ dev: *mut bindings::device,
+ node: *mut bindings::devres_node,
+ ) -> bool {
+ // SAFETY: Safety requirements are the same as `bindings::devres_node_remove`.
+ unsafe { bindings::devres_node_remove(dev, node) }
+ }
}
impl<T: Send> Devres<T> {
@@ -128,58 +193,86 @@ impl<T: Send> Devres<T> {
where
Error: From<E>,
{
- let callback = Self::devres_callback;
- let data = Arc::pin_init(Revocable::new(data), GFP_KERNEL)?;
- let devres_data = data.clone();
+ let inner = Arc::pin_init::<Error>(
+ try_pin_init!(Inner {
+ node <- Opaque::ffi_init(|node: *mut bindings::devres_node| {
+ // SAFETY: `node` is a valid pointer to an uninitialized `struct devres_node`.
+ unsafe {
+ base::devres_node_init(
+ node,
+ Some(Self::devres_node_release),
+ Some(Self::devres_node_free_node),
+ )
+ };
+
+ // SAFETY: `node` is a valid pointer to an uninitialized `struct devres_node`.
+ unsafe {
+ base::devres_set_node_dbginfo(
+ node,
+ // TODO: Use `core::any::type_name::<T>()` once it is a `const fn`,
+ // such that we can convert the `&str` to a `&CStr` at compile-time.
+ c"Devres<T>".as_char_ptr(),
+ core::mem::size_of::<Revocable<T>>(),
+ )
+ };
+ }),
+ data <- Revocable::new(data),
+ }),
+ GFP_KERNEL,
+ )?;
// SAFETY:
- // - `dev.as_raw()` is a pointer to a valid bound device.
- // - `data` is guaranteed to be a valid for the duration of the lifetime of `Self`.
- // - `devm_add_action()` is guaranteed not to call `callback` for the entire lifetime of
- // `dev`.
- to_result(unsafe {
- bindings::devm_add_action(
- dev.as_raw(),
- Some(callback),
- Arc::as_ptr(&data).cast_mut().cast(),
- )
- })?;
-
- // `devm_add_action()` was successful and has consumed the reference count.
- core::mem::forget(devres_data);
+ // - `dev` is a valid pointer to a bound `struct device`.
+ // - `node` is a valid pointer to a `struct devres_node`.
+ // - `devres_node_add()` is guaranteed not to call `devres_node_release()` for the entire
+ // lifetime of `dev`.
+ unsafe { base::devres_node_add(dev.as_raw(), inner.node.get()) };
+
+ // Take additional reference count for `devres_node_add()`.
+ core::mem::forget(inner.clone());
Ok(Self {
dev: dev.into(),
- callback,
- data,
+ inner,
})
}
fn data(&self) -> &Revocable<T> {
- &self.data
+ &self.inner.data
}
#[allow(clippy::missing_safety_doc)]
- unsafe extern "C" fn devres_callback(ptr: *mut kernel::ffi::c_void) {
- // SAFETY: In `Self::new` we've passed a valid pointer of `Revocable<T>` to
- // `devm_add_action()`, hence `ptr` must be a valid pointer to `Revocable<T>`.
- let data = unsafe { Arc::from_raw(ptr.cast::<Revocable<T>>()) };
+ unsafe extern "C" fn devres_node_release(
+ _dev: *mut bindings::device,
+ node: *mut bindings::devres_node,
+ ) {
+ let node = Opaque::cast_from(node);
+
+ // SAFETY: `node` is in the same allocation as its container.
+ let inner = unsafe { kernel::container_of!(node, Inner<T>, node) };
+
+ // SAFETY: `inner` is a valid `Inner<T>` pointer.
+ let inner = unsafe { &*inner };
+
+ inner.data.revoke();
+ }
+
+ #[allow(clippy::missing_safety_doc)]
+ unsafe extern "C" fn devres_node_free_node(node: *mut bindings::devres_node) {
+ let node = Opaque::cast_from(node);
+
+ // SAFETY: `node` is in the same allocation as its container.
+ let inner = unsafe { kernel::container_of!(node, Inner<T>, node) };
- data.revoke();
+ // SAFETY: `inner` points to the entire `Inner<T>` allocation.
+ drop(unsafe { Arc::from_raw(inner) });
}
- fn remove_action(&self) -> bool {
+ fn remove_node(&self) -> bool {
// SAFETY:
- // - `self.dev` is a valid `Device`,
- // - the `action` and `data` pointers are the exact same ones as given to
- // `devm_add_action()` previously,
- (unsafe {
- bindings::devm_remove_action_nowarn(
- self.dev.as_raw(),
- Some(self.callback),
- core::ptr::from_ref(self.data()).cast_mut().cast(),
- )
- } == 0)
+ // - `self.device().as_raw()` is a valid pointer to a bound `struct device`.
+ // - `self.inner.node.get()` is a valid pointer to a `struct devres_node`.
+ unsafe { base::devres_node_remove(self.device().as_raw(), self.inner.node.get()) }
}
/// Return a reference of the [`Device`] this [`Devres`] instance has been created with.
@@ -261,12 +354,12 @@ impl<T: Send> Drop for Devres<T> {
// 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.
if unsafe { self.data().revoke_nosync() } {
- // We revoked `self.data` before the devres action did, hence try to remove it.
- if self.remove_action() {
+ // We revoked `self.data` before devres did, hence try to remove it.
+ if self.remove_node() {
// SAFETY: In `Self::new` we have taken an additional reference count of `self.data`
- // for `devm_add_action()`. Since `remove_action()` was successful, we have to drop
+ // for `devres_node_add()`. Since `remove_node()` was successful, we have to drop
// this additional reference count.
- drop(unsafe { Arc::from_raw(Arc::as_ptr(&self.data)) });
+ drop(unsafe { Arc::from_raw(Arc::as_ptr(&self.inner)) });
}
}
}
diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs
index e5fba6bf6db0..fcc7678fd9e3 100644
--- a/rust/kernel/io.rs
+++ b/rust/kernel/io.rs
@@ -11,10 +11,14 @@ use crate::{
pub mod mem;
pub mod poll;
+pub mod register;
pub mod resource;
+pub use crate::register;
pub use resource::Resource;
+use register::LocatedRegister;
+
/// Physical address type.
///
/// This is a type alias to either `u32` or `u64` depending on the config option
@@ -137,177 +141,6 @@ impl<const SIZE: usize> MmioRaw<SIZE> {
#[repr(transparent)]
pub struct Mmio<const SIZE: usize = 0>(MmioRaw<SIZE>);
-/// Internal helper macros used to invoke C MMIO read functions.
-///
-/// This macro is intended to be used by higher-level MMIO access macros (io_define_read) and
-/// provides a unified expansion for infallible vs. fallible read semantics. It emits a direct call
-/// into the corresponding C helper and performs the required cast to the Rust return type.
-///
-/// # Parameters
-///
-/// * `$c_fn` – The C function performing the MMIO read.
-/// * `$self` – The I/O backend object.
-/// * `$ty` – The type of the value to be read.
-/// * `$addr` – The MMIO address to read.
-///
-/// This macro does not perform any validation; all invariants must be upheld by the higher-level
-/// abstraction invoking it.
-macro_rules! call_mmio_read {
- (infallible, $c_fn:ident, $self:ident, $type:ty, $addr:expr) => {
- // SAFETY: By the type invariant `addr` is a valid address for MMIO operations.
- unsafe { bindings::$c_fn($addr as *const c_void) as $type }
- };
-
- (fallible, $c_fn:ident, $self:ident, $type:ty, $addr:expr) => {{
- // SAFETY: By the type invariant `addr` is a valid address for MMIO operations.
- Ok(unsafe { bindings::$c_fn($addr as *const c_void) as $type })
- }};
-}
-
-/// Internal helper macros used to invoke C MMIO write functions.
-///
-/// This macro is intended to be used by higher-level MMIO access macros (io_define_write) and
-/// provides a unified expansion for infallible vs. fallible write semantics. It emits a direct call
-/// into the corresponding C helper and performs the required cast to the Rust return type.
-///
-/// # Parameters
-///
-/// * `$c_fn` – The C function performing the MMIO write.
-/// * `$self` – The I/O backend object.
-/// * `$ty` – The type of the written value.
-/// * `$addr` – The MMIO address to write.
-/// * `$value` – The value to write.
-///
-/// This macro does not perform any validation; all invariants must be upheld by the higher-level
-/// abstraction invoking it.
-macro_rules! call_mmio_write {
- (infallible, $c_fn:ident, $self:ident, $ty:ty, $addr:expr, $value:expr) => {
- // SAFETY: By the type invariant `addr` is a valid address for MMIO operations.
- unsafe { bindings::$c_fn($value, $addr as *mut c_void) }
- };
-
- (fallible, $c_fn:ident, $self:ident, $ty:ty, $addr:expr, $value:expr) => {{
- // SAFETY: By the type invariant `addr` is a valid address for MMIO operations.
- unsafe { bindings::$c_fn($value, $addr as *mut c_void) };
- Ok(())
- }};
-}
-
-/// Generates an accessor method for reading from an I/O backend.
-///
-/// This macro reduces boilerplate by automatically generating either compile-time bounds-checked
-/// (infallible) or runtime bounds-checked (fallible) read methods. It abstracts the address
-/// calculation and bounds checking, and delegates the actual I/O read operation to a specified
-/// helper macro, making it generic over different I/O backends.
-///
-/// # Parameters
-///
-/// * `infallible` / `fallible` - Determines the bounds-checking strategy. `infallible` relies on
-/// `IoKnownSize` for compile-time checks and returns the value directly. `fallible` performs
-/// runtime checks against `maxsize()` and returns a `Result<T>`.
-/// * `$(#[$attr:meta])*` - Optional attributes to apply to the generated method (e.g.,
-/// `#[cfg(CONFIG_64BIT)]` or inline directives).
-/// * `$vis:vis` - The visibility of the generated method (e.g., `pub`).
-/// * `$name:ident` / `$try_name:ident` - The name of the generated method (e.g., `read32`,
-/// `try_read8`).
-/// * `$call_macro:ident` - The backend-specific helper macro used to emit the actual I/O call
-/// (e.g., `call_mmio_read`).
-/// * `$c_fn:ident` - The backend-specific C function or identifier to be passed into the
-/// `$call_macro`.
-/// * `$type_name:ty` - The Rust type of the value being read (e.g., `u8`, `u32`).
-#[macro_export]
-macro_rules! io_define_read {
- (infallible, $(#[$attr:meta])* $vis:vis $name:ident, $call_macro:ident($c_fn:ident) ->
- $type_name:ty) => {
- /// Read IO data from a given offset known at compile time.
- ///
- /// Bound checks are performed on compile time, hence if the offset is not known at compile
- /// time, the build will fail.
- $(#[$attr])*
- // Always inline to optimize out error path of `io_addr_assert`.
- #[inline(always)]
- $vis fn $name(&self, offset: usize) -> $type_name {
- let addr = self.io_addr_assert::<$type_name>(offset);
-
- // SAFETY: By the type invariant `addr` is a valid address for IO operations.
- $call_macro!(infallible, $c_fn, self, $type_name, addr)
- }
- };
-
- (fallible, $(#[$attr:meta])* $vis:vis $try_name:ident, $call_macro:ident($c_fn:ident) ->
- $type_name:ty) => {
- /// Read IO data from a given offset.
- ///
- /// Bound checks are performed on runtime, it fails if the offset (plus the type size) is
- /// out of bounds.
- $(#[$attr])*
- $vis fn $try_name(&self, offset: usize) -> Result<$type_name> {
- let addr = self.io_addr::<$type_name>(offset)?;
-
- // SAFETY: By the type invariant `addr` is a valid address for IO operations.
- $call_macro!(fallible, $c_fn, self, $type_name, addr)
- }
- };
-}
-pub use io_define_read;
-
-/// Generates an accessor method for writing to an I/O backend.
-///
-/// This macro reduces boilerplate by automatically generating either compile-time bounds-checked
-/// (infallible) or runtime bounds-checked (fallible) write methods. It abstracts the address
-/// calculation and bounds checking, and delegates the actual I/O write operation to a specified
-/// helper macro, making it generic over different I/O backends.
-///
-/// # Parameters
-///
-/// * `infallible` / `fallible` - Determines the bounds-checking strategy. `infallible` relies on
-/// `IoKnownSize` for compile-time checks and returns `()`. `fallible` performs runtime checks
-/// against `maxsize()` and returns a `Result`.
-/// * `$(#[$attr:meta])*` - Optional attributes to apply to the generated method (e.g.,
-/// `#[cfg(CONFIG_64BIT)]` or inline directives).
-/// * `$vis:vis` - The visibility of the generated method (e.g., `pub`).
-/// * `$name:ident` / `$try_name:ident` - The name of the generated method (e.g., `write32`,
-/// `try_write8`).
-/// * `$call_macro:ident` - The backend-specific helper macro used to emit the actual I/O call
-/// (e.g., `call_mmio_write`).
-/// * `$c_fn:ident` - The backend-specific C function or identifier to be passed into the
-/// `$call_macro`.
-/// * `$type_name:ty` - The Rust type of the value being written (e.g., `u8`, `u32`). Note the use
-/// of `<-` before the type to denote a write operation.
-#[macro_export]
-macro_rules! io_define_write {
- (infallible, $(#[$attr:meta])* $vis:vis $name:ident, $call_macro:ident($c_fn:ident) <-
- $type_name:ty) => {
- /// Write IO data from a given offset known at compile time.
- ///
- /// Bound checks are performed on compile time, hence if the offset is not known at compile
- /// time, the build will fail.
- $(#[$attr])*
- // Always inline to optimize out error path of `io_addr_assert`.
- #[inline(always)]
- $vis fn $name(&self, value: $type_name, offset: usize) {
- let addr = self.io_addr_assert::<$type_name>(offset);
-
- $call_macro!(infallible, $c_fn, self, $type_name, addr, value);
- }
- };
-
- (fallible, $(#[$attr:meta])* $vis:vis $try_name:ident, $call_macro:ident($c_fn:ident) <-
- $type_name:ty) => {
- /// Write IO data from a given offset.
- ///
- /// Bound checks are performed on runtime, it fails if the offset (plus the type size) is
- /// out of bounds.
- $(#[$attr])*
- $vis fn $try_name(&self, value: $type_name, offset: usize) -> Result {
- let addr = self.io_addr::<$type_name>(offset)?;
-
- $call_macro!(fallible, $c_fn, self, $type_name, addr, value)
- }
- };
-}
-pub use io_define_write;
-
/// Checks whether an access of type `U` at the given `offset`
/// is valid within this region.
#[inline]
@@ -320,14 +153,74 @@ const fn offset_valid<U>(offset: usize, size: usize) -> bool {
}
}
-/// Marker trait indicating that an I/O backend supports operations of a certain type.
+/// Trait indicating that an I/O backend supports operations of a certain type and providing an
+/// implementation for these operations.
///
/// Different I/O backends can implement this trait to expose only the operations they support.
///
/// For example, a PCI configuration space may implement `IoCapable<u8>`, `IoCapable<u16>`,
/// and `IoCapable<u32>`, but not `IoCapable<u64>`, while an MMIO region on a 64-bit
/// system might implement all four.
-pub trait IoCapable<T> {}
+pub trait IoCapable<T> {
+ /// Performs an I/O read of type `T` at `address` and returns the result.
+ ///
+ /// # Safety
+ ///
+ /// The range `[address..address + size_of::<T>()]` must be within the bounds of `Self`.
+ unsafe fn io_read(&self, address: usize) -> T;
+
+ /// Performs an I/O write of `value` at `address`.
+ ///
+ /// # Safety
+ ///
+ /// The range `[address..address + size_of::<T>()]` must be within the bounds of `Self`.
+ unsafe fn io_write(&self, value: T, address: usize);
+}
+
+/// Describes a given I/O location: its offset, width, and type to convert the raw value from and
+/// into.
+///
+/// This trait is the key abstraction allowing [`Io::read`], [`Io::write`], and [`Io::update`] (and
+/// their fallible [`try_read`](Io::try_read), [`try_write`](Io::try_write) and
+/// [`try_update`](Io::try_update) counterparts) to work uniformly with both raw [`usize`] offsets
+/// (for primitive types like [`u32`]) and typed ones (like those generated by the [`register!`]
+/// macro).
+///
+/// An `IoLoc<T>` carries three pieces of information:
+///
+/// - The offset to access (returned by [`IoLoc::offset`]),
+/// - The width of the access (determined by [`IoLoc::IoType`]),
+/// - The type `T` in which the raw data is returned or provided.
+///
+/// `T` and `IoLoc::IoType` may differ: for instance, a typed register has `T` = the register type
+/// with its bitfields, and `IoType` = its backing primitive (e.g. `u32`).
+pub trait IoLoc<T> {
+ /// Size ([`u8`], [`u16`], etc) of the I/O performed on the returned [`offset`](IoLoc::offset).
+ type IoType: Into<T> + From<T>;
+
+ /// Consumes `self` and returns the offset of this location.
+ fn offset(self) -> usize;
+}
+
+/// Implements [`IoLoc<$ty>`] for [`usize`], allowing [`usize`] to be used as a parameter of
+/// [`Io::read`] and [`Io::write`].
+macro_rules! impl_usize_ioloc {
+ ($($ty:ty),*) => {
+ $(
+ impl IoLoc<$ty> for usize {
+ type IoType = $ty;
+
+ #[inline(always)]
+ fn offset(self) -> usize {
+ self
+ }
+ }
+ )*
+ }
+}
+
+// Provide the ability to read any primitive type from a [`usize`].
+impl_usize_ioloc!(u8, u16, u32, u64);
/// Types implementing this trait (e.g. MMIO BARs or PCI config regions)
/// can perform I/O operations on regions of memory.
@@ -369,146 +262,445 @@ pub trait Io {
/// Fallible 8-bit read with runtime bounds check.
#[inline(always)]
- fn try_read8(&self, _offset: usize) -> Result<u8>
+ fn try_read8(&self, offset: usize) -> Result<u8>
where
Self: IoCapable<u8>,
{
- build_error!("Backend does not support fallible 8-bit read")
+ self.try_read(offset)
}
/// Fallible 16-bit read with runtime bounds check.
#[inline(always)]
- fn try_read16(&self, _offset: usize) -> Result<u16>
+ fn try_read16(&self, offset: usize) -> Result<u16>
where
Self: IoCapable<u16>,
{
- build_error!("Backend does not support fallible 16-bit read")
+ self.try_read(offset)
}
/// Fallible 32-bit read with runtime bounds check.
#[inline(always)]
- fn try_read32(&self, _offset: usize) -> Result<u32>
+ fn try_read32(&self, offset: usize) -> Result<u32>
where
Self: IoCapable<u32>,
{
- build_error!("Backend does not support fallible 32-bit read")
+ self.try_read(offset)
}
/// Fallible 64-bit read with runtime bounds check.
#[inline(always)]
- fn try_read64(&self, _offset: usize) -> Result<u64>
+ fn try_read64(&self, offset: usize) -> Result<u64>
where
Self: IoCapable<u64>,
{
- build_error!("Backend does not support fallible 64-bit read")
+ self.try_read(offset)
}
/// Fallible 8-bit write with runtime bounds check.
#[inline(always)]
- fn try_write8(&self, _value: u8, _offset: usize) -> Result
+ fn try_write8(&self, value: u8, offset: usize) -> Result
where
Self: IoCapable<u8>,
{
- build_error!("Backend does not support fallible 8-bit write")
+ self.try_write(offset, value)
}
/// Fallible 16-bit write with runtime bounds check.
#[inline(always)]
- fn try_write16(&self, _value: u16, _offset: usize) -> Result
+ fn try_write16(&self, value: u16, offset: usize) -> Result
where
Self: IoCapable<u16>,
{
- build_error!("Backend does not support fallible 16-bit write")
+ self.try_write(offset, value)
}
/// Fallible 32-bit write with runtime bounds check.
#[inline(always)]
- fn try_write32(&self, _value: u32, _offset: usize) -> Result
+ fn try_write32(&self, value: u32, offset: usize) -> Result
where
Self: IoCapable<u32>,
{
- build_error!("Backend does not support fallible 32-bit write")
+ self.try_write(offset, value)
}
/// Fallible 64-bit write with runtime bounds check.
#[inline(always)]
- fn try_write64(&self, _value: u64, _offset: usize) -> Result
+ fn try_write64(&self, value: u64, offset: usize) -> Result
where
Self: IoCapable<u64>,
{
- build_error!("Backend does not support fallible 64-bit write")
+ self.try_write(offset, value)
}
/// Infallible 8-bit read with compile-time bounds check.
#[inline(always)]
- fn read8(&self, _offset: usize) -> u8
+ fn read8(&self, offset: usize) -> u8
where
Self: IoKnownSize + IoCapable<u8>,
{
- build_error!("Backend does not support infallible 8-bit read")
+ self.read(offset)
}
/// Infallible 16-bit read with compile-time bounds check.
#[inline(always)]
- fn read16(&self, _offset: usize) -> u16
+ fn read16(&self, offset: usize) -> u16
where
Self: IoKnownSize + IoCapable<u16>,
{
- build_error!("Backend does not support infallible 16-bit read")
+ self.read(offset)
}
/// Infallible 32-bit read with compile-time bounds check.
#[inline(always)]
- fn read32(&self, _offset: usize) -> u32
+ fn read32(&self, offset: usize) -> u32
where
Self: IoKnownSize + IoCapable<u32>,
{
- build_error!("Backend does not support infallible 32-bit read")
+ self.read(offset)
}
/// Infallible 64-bit read with compile-time bounds check.
#[inline(always)]
- fn read64(&self, _offset: usize) -> u64
+ fn read64(&self, offset: usize) -> u64
where
Self: IoKnownSize + IoCapable<u64>,
{
- build_error!("Backend does not support infallible 64-bit read")
+ self.read(offset)
}
/// Infallible 8-bit write with compile-time bounds check.
#[inline(always)]
- fn write8(&self, _value: u8, _offset: usize)
+ fn write8(&self, value: u8, offset: usize)
where
Self: IoKnownSize + IoCapable<u8>,
{
- build_error!("Backend does not support infallible 8-bit write")
+ self.write(offset, value)
}
/// Infallible 16-bit write with compile-time bounds check.
#[inline(always)]
- fn write16(&self, _value: u16, _offset: usize)
+ fn write16(&self, value: u16, offset: usize)
where
Self: IoKnownSize + IoCapable<u16>,
{
- build_error!("Backend does not support infallible 16-bit write")
+ self.write(offset, value)
}
/// Infallible 32-bit write with compile-time bounds check.
#[inline(always)]
- fn write32(&self, _value: u32, _offset: usize)
+ fn write32(&self, value: u32, offset: usize)
where
Self: IoKnownSize + IoCapable<u32>,
{
- build_error!("Backend does not support infallible 32-bit write")
+ self.write(offset, value)
}
/// Infallible 64-bit write with compile-time bounds check.
#[inline(always)]
- fn write64(&self, _value: u64, _offset: usize)
+ fn write64(&self, value: u64, offset: usize)
where
Self: IoKnownSize + IoCapable<u64>,
{
- build_error!("Backend does not support infallible 64-bit write")
+ self.write(offset, value)
+ }
+
+ /// Generic fallible read with runtime bounds check.
+ ///
+ /// # Examples
+ ///
+ /// Read a primitive type from an I/O address:
+ ///
+ /// ```no_run
+ /// use kernel::io::{
+ /// Io,
+ /// Mmio,
+ /// };
+ ///
+ /// fn do_reads(io: &Mmio) -> Result {
+ /// // 32-bit read from address `0x10`.
+ /// let v: u32 = io.try_read(0x10)?;
+ ///
+ /// // 8-bit read from address `0xfff`.
+ /// let v: u8 = io.try_read(0xfff)?;
+ ///
+ /// Ok(())
+ /// }
+ /// ```
+ #[inline(always)]
+ fn try_read<T, L>(&self, location: L) -> Result<T>
+ where
+ L: IoLoc<T>,
+ Self: IoCapable<L::IoType>,
+ {
+ let address = self.io_addr::<L::IoType>(location.offset())?;
+
+ // SAFETY: `address` has been validated by `io_addr`.
+ Ok(unsafe { self.io_read(address) }.into())
+ }
+
+ /// Generic fallible write with runtime bounds check.
+ ///
+ /// # Examples
+ ///
+ /// Write a primitive type to an I/O address:
+ ///
+ /// ```no_run
+ /// use kernel::io::{
+ /// Io,
+ /// Mmio,
+ /// };
+ ///
+ /// fn do_writes(io: &Mmio) -> Result {
+ /// // 32-bit write of value `1` at address `0x10`.
+ /// io.try_write(0x10, 1u32)?;
+ ///
+ /// // 8-bit write of value `0xff` at address `0xfff`.
+ /// io.try_write(0xfff, 0xffu8)?;
+ ///
+ /// Ok(())
+ /// }
+ /// ```
+ #[inline(always)]
+ fn try_write<T, L>(&self, location: L, value: T) -> Result
+ where
+ L: IoLoc<T>,
+ Self: IoCapable<L::IoType>,
+ {
+ let address = self.io_addr::<L::IoType>(location.offset())?;
+ let io_value = value.into();
+
+ // SAFETY: `address` has been validated by `io_addr`.
+ unsafe { self.io_write(io_value, address) }
+
+ Ok(())
+ }
+
+ /// Generic fallible write of a fully-located register value.
+ ///
+ /// # Examples
+ ///
+ /// Tuples carrying a location and a value can be used with this method:
+ ///
+ /// ```no_run
+ /// use kernel::io::{
+ /// register,
+ /// Io,
+ /// Mmio,
+ /// };
+ ///
+ /// register! {
+ /// VERSION(u32) @ 0x100 {
+ /// 15:8 major;
+ /// 7:0 minor;
+ /// }
+ /// }
+ ///
+ /// impl VERSION {
+ /// fn new(major: u8, minor: u8) -> Self {
+ /// VERSION::zeroed().with_major(major).with_minor(minor)
+ /// }
+ /// }
+ ///
+ /// fn do_write_reg(io: &Mmio) -> Result {
+ ///
+ /// io.try_write_reg(VERSION::new(1, 0))
+ /// }
+ /// ```
+ #[inline(always)]
+ fn try_write_reg<T, L, V>(&self, value: V) -> Result
+ where
+ L: IoLoc<T>,
+ V: LocatedRegister<Location = L, Value = T>,
+ Self: IoCapable<L::IoType>,
+ {
+ let (location, value) = value.into_io_op();
+
+ self.try_write(location, value)
+ }
+
+ /// Generic fallible update with runtime bounds check.
+ ///
+ /// Note: this does not perform any synchronization. The caller is responsible for ensuring
+ /// exclusive access if required.
+ ///
+ /// # Examples
+ ///
+ /// Read the u32 value at address `0x10`, increment it, and store the updated value back:
+ ///
+ /// ```no_run
+ /// use kernel::io::{
+ /// Io,
+ /// Mmio,
+ /// };
+ ///
+ /// fn do_update(io: &Mmio<0x1000>) -> Result {
+ /// io.try_update(0x10, |v: u32| {
+ /// v + 1
+ /// })
+ /// }
+ /// ```
+ #[inline(always)]
+ fn try_update<T, L, F>(&self, location: L, f: F) -> Result
+ where
+ L: IoLoc<T>,
+ Self: IoCapable<L::IoType>,
+ F: FnOnce(T) -> T,
+ {
+ let address = self.io_addr::<L::IoType>(location.offset())?;
+
+ // SAFETY: `address` has been validated by `io_addr`.
+ let value: T = unsafe { self.io_read(address) }.into();
+ let io_value = f(value).into();
+
+ // SAFETY: `address` has been validated by `io_addr`.
+ unsafe { self.io_write(io_value, address) }
+
+ Ok(())
+ }
+
+ /// Generic infallible read with compile-time bounds check.
+ ///
+ /// # Examples
+ ///
+ /// Read a primitive type from an I/O address:
+ ///
+ /// ```no_run
+ /// use kernel::io::{
+ /// Io,
+ /// Mmio,
+ /// };
+ ///
+ /// fn do_reads(io: &Mmio<0x1000>) {
+ /// // 32-bit read from address `0x10`.
+ /// let v: u32 = io.read(0x10);
+ ///
+ /// // 8-bit read from the top of the I/O space.
+ /// let v: u8 = io.read(0xfff);
+ /// }
+ /// ```
+ #[inline(always)]
+ fn read<T, L>(&self, location: L) -> T
+ where
+ L: IoLoc<T>,
+ Self: IoKnownSize + IoCapable<L::IoType>,
+ {
+ let address = self.io_addr_assert::<L::IoType>(location.offset());
+
+ // SAFETY: `address` has been validated by `io_addr_assert`.
+ unsafe { self.io_read(address) }.into()
+ }
+
+ /// Generic infallible write with compile-time bounds check.
+ ///
+ /// # Examples
+ ///
+ /// Write a primitive type to an I/O address:
+ ///
+ /// ```no_run
+ /// use kernel::io::{
+ /// Io,
+ /// Mmio,
+ /// };
+ ///
+ /// fn do_writes(io: &Mmio<0x1000>) {
+ /// // 32-bit write of value `1` at address `0x10`.
+ /// io.write(0x10, 1u32);
+ ///
+ /// // 8-bit write of value `0xff` at the top of the I/O space.
+ /// io.write(0xfff, 0xffu8);
+ /// }
+ /// ```
+ #[inline(always)]
+ fn write<T, L>(&self, location: L, value: T)
+ where
+ L: IoLoc<T>,
+ Self: IoKnownSize + IoCapable<L::IoType>,
+ {
+ let address = self.io_addr_assert::<L::IoType>(location.offset());
+ let io_value = value.into();
+
+ // SAFETY: `address` has been validated by `io_addr_assert`.
+ unsafe { self.io_write(io_value, address) }
+ }
+
+ /// Generic infallible write of a fully-located register value.
+ ///
+ /// # Examples
+ ///
+ /// Tuples carrying a location and a value can be used with this method:
+ ///
+ /// ```no_run
+ /// use kernel::io::{
+ /// register,
+ /// Io,
+ /// Mmio,
+ /// };
+ ///
+ /// register! {
+ /// VERSION(u32) @ 0x100 {
+ /// 15:8 major;
+ /// 7:0 minor;
+ /// }
+ /// }
+ ///
+ /// impl VERSION {
+ /// fn new(major: u8, minor: u8) -> Self {
+ /// VERSION::zeroed().with_major(major).with_minor(minor)
+ /// }
+ /// }
+ ///
+ /// fn do_write_reg(io: &Mmio<0x1000>) {
+ /// io.write_reg(VERSION::new(1, 0));
+ /// }
+ /// ```
+ #[inline(always)]
+ fn write_reg<T, L, V>(&self, value: V)
+ where
+ L: IoLoc<T>,
+ V: LocatedRegister<Location = L, Value = T>,
+ Self: IoKnownSize + IoCapable<L::IoType>,
+ {
+ let (location, value) = value.into_io_op();
+
+ self.write(location, value)
+ }
+
+ /// Generic infallible update with compile-time bounds check.
+ ///
+ /// Note: this does not perform any synchronization. The caller is responsible for ensuring
+ /// exclusive access if required.
+ ///
+ /// # Examples
+ ///
+ /// Read the u32 value at address `0x10`, increment it, and store the updated value back:
+ ///
+ /// ```no_run
+ /// use kernel::io::{
+ /// Io,
+ /// Mmio,
+ /// };
+ ///
+ /// fn do_update(io: &Mmio<0x1000>) {
+ /// io.update(0x10, |v: u32| {
+ /// v + 1
+ /// })
+ /// }
+ /// ```
+ #[inline(always)]
+ fn update<T, L, F>(&self, location: L, f: F)
+ where
+ L: IoLoc<T>,
+ Self: IoKnownSize + IoCapable<L::IoType> + Sized,
+ F: FnOnce(T) -> T,
+ {
+ let address = self.io_addr_assert::<L::IoType>(location.offset());
+
+ // SAFETY: `address` has been validated by `io_addr_assert`.
+ let value: T = unsafe { self.io_read(address) }.into();
+ let io_value = f(value).into();
+
+ // SAFETY: `address` has been validated by `io_addr_assert`.
+ unsafe { self.io_write(io_value, address) }
}
}
@@ -534,14 +726,36 @@ pub trait IoKnownSize: Io {
}
}
-// MMIO regions support 8, 16, and 32-bit accesses.
-impl<const SIZE: usize> IoCapable<u8> for Mmio<SIZE> {}
-impl<const SIZE: usize> IoCapable<u16> for Mmio<SIZE> {}
-impl<const SIZE: usize> IoCapable<u32> for Mmio<SIZE> {}
+/// Implements [`IoCapable`] on `$mmio` for `$ty` using `$read_fn` and `$write_fn`.
+macro_rules! impl_mmio_io_capable {
+ ($mmio:ident, $(#[$attr:meta])* $ty:ty, $read_fn:ident, $write_fn:ident) => {
+ $(#[$attr])*
+ impl<const SIZE: usize> IoCapable<$ty> for $mmio<SIZE> {
+ unsafe fn io_read(&self, address: usize) -> $ty {
+ // SAFETY: By the trait invariant `address` is a valid address for MMIO operations.
+ unsafe { bindings::$read_fn(address as *const c_void) }
+ }
+
+ unsafe fn io_write(&self, value: $ty, address: usize) {
+ // SAFETY: By the trait invariant `address` is a valid address for MMIO operations.
+ unsafe { bindings::$write_fn(value, address as *mut c_void) }
+ }
+ }
+ };
+}
+// MMIO regions support 8, 16, and 32-bit accesses.
+impl_mmio_io_capable!(Mmio, u8, readb, writeb);
+impl_mmio_io_capable!(Mmio, u16, readw, writew);
+impl_mmio_io_capable!(Mmio, u32, readl, writel);
// MMIO regions on 64-bit systems also support 64-bit accesses.
-#[cfg(CONFIG_64BIT)]
-impl<const SIZE: usize> IoCapable<u64> for Mmio<SIZE> {}
+impl_mmio_io_capable!(
+ Mmio,
+ #[cfg(CONFIG_64BIT)]
+ u64,
+ readq,
+ writeq
+);
impl<const SIZE: usize> Io for Mmio<SIZE> {
/// Returns the base address of this mapping.
@@ -555,46 +769,6 @@ impl<const SIZE: usize> Io for Mmio<SIZE> {
fn maxsize(&self) -> usize {
self.0.maxsize()
}
-
- io_define_read!(fallible, try_read8, call_mmio_read(readb) -> u8);
- io_define_read!(fallible, try_read16, call_mmio_read(readw) -> u16);
- io_define_read!(fallible, try_read32, call_mmio_read(readl) -> u32);
- io_define_read!(
- fallible,
- #[cfg(CONFIG_64BIT)]
- try_read64,
- call_mmio_read(readq) -> u64
- );
-
- io_define_write!(fallible, try_write8, call_mmio_write(writeb) <- u8);
- io_define_write!(fallible, try_write16, call_mmio_write(writew) <- u16);
- io_define_write!(fallible, try_write32, call_mmio_write(writel) <- u32);
- io_define_write!(
- fallible,
- #[cfg(CONFIG_64BIT)]
- try_write64,
- call_mmio_write(writeq) <- u64
- );
-
- io_define_read!(infallible, read8, call_mmio_read(readb) -> u8);
- io_define_read!(infallible, read16, call_mmio_read(readw) -> u16);
- io_define_read!(infallible, read32, call_mmio_read(readl) -> u32);
- io_define_read!(
- infallible,
- #[cfg(CONFIG_64BIT)]
- read64,
- call_mmio_read(readq) -> u64
- );
-
- io_define_write!(infallible, write8, call_mmio_write(writeb) <- u8);
- io_define_write!(infallible, write16, call_mmio_write(writew) <- u16);
- io_define_write!(infallible, write32, call_mmio_write(writel) <- u32);
- io_define_write!(
- infallible,
- #[cfg(CONFIG_64BIT)]
- write64,
- call_mmio_write(writeq) <- u64
- );
}
impl<const SIZE: usize> IoKnownSize for Mmio<SIZE> {
@@ -612,44 +786,70 @@ impl<const SIZE: usize> Mmio<SIZE> {
// SAFETY: `Mmio` is a transparent wrapper around `MmioRaw`.
unsafe { &*core::ptr::from_ref(raw).cast() }
}
+}
+
+/// [`Mmio`] wrapper using relaxed accessors.
+///
+/// This type provides an implementation of [`Io`] that uses relaxed I/O MMIO operands instead of
+/// the regular ones.
+///
+/// See [`Mmio::relaxed`] for a usage example.
+#[repr(transparent)]
+pub struct RelaxedMmio<const SIZE: usize = 0>(Mmio<SIZE>);
+
+impl<const SIZE: usize> Io for RelaxedMmio<SIZE> {
+ #[inline]
+ fn addr(&self) -> usize {
+ self.0.addr()
+ }
- io_define_read!(infallible, pub read8_relaxed, call_mmio_read(readb_relaxed) -> u8);
- io_define_read!(infallible, pub read16_relaxed, call_mmio_read(readw_relaxed) -> u16);
- io_define_read!(infallible, pub read32_relaxed, call_mmio_read(readl_relaxed) -> u32);
- io_define_read!(
- infallible,
- #[cfg(CONFIG_64BIT)]
- pub read64_relaxed,
- call_mmio_read(readq_relaxed) -> u64
- );
-
- io_define_read!(fallible, pub try_read8_relaxed, call_mmio_read(readb_relaxed) -> u8);
- io_define_read!(fallible, pub try_read16_relaxed, call_mmio_read(readw_relaxed) -> u16);
- io_define_read!(fallible, pub try_read32_relaxed, call_mmio_read(readl_relaxed) -> u32);
- io_define_read!(
- fallible,
- #[cfg(CONFIG_64BIT)]
- pub try_read64_relaxed,
- call_mmio_read(readq_relaxed) -> u64
- );
-
- io_define_write!(infallible, pub write8_relaxed, call_mmio_write(writeb_relaxed) <- u8);
- io_define_write!(infallible, pub write16_relaxed, call_mmio_write(writew_relaxed) <- u16);
- io_define_write!(infallible, pub write32_relaxed, call_mmio_write(writel_relaxed) <- u32);
- io_define_write!(
- infallible,
- #[cfg(CONFIG_64BIT)]
- pub write64_relaxed,
- call_mmio_write(writeq_relaxed) <- u64
- );
-
- io_define_write!(fallible, pub try_write8_relaxed, call_mmio_write(writeb_relaxed) <- u8);
- io_define_write!(fallible, pub try_write16_relaxed, call_mmio_write(writew_relaxed) <- u16);
- io_define_write!(fallible, pub try_write32_relaxed, call_mmio_write(writel_relaxed) <- u32);
- io_define_write!(
- fallible,
- #[cfg(CONFIG_64BIT)]
- pub try_write64_relaxed,
- call_mmio_write(writeq_relaxed) <- u64
- );
+ #[inline]
+ fn maxsize(&self) -> usize {
+ self.0.maxsize()
+ }
+}
+
+impl<const SIZE: usize> IoKnownSize for RelaxedMmio<SIZE> {
+ const MIN_SIZE: usize = SIZE;
}
+
+impl<const SIZE: usize> Mmio<SIZE> {
+ /// Returns a [`RelaxedMmio`] reference that performs relaxed I/O operations.
+ ///
+ /// Relaxed accessors do not provide ordering guarantees with respect to DMA or memory accesses
+ /// and can be used when such ordering is not required.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use kernel::io::{
+ /// Io,
+ /// Mmio,
+ /// RelaxedMmio,
+ /// };
+ ///
+ /// fn do_io(io: &Mmio<0x100>) {
+ /// // The access is performed using `readl_relaxed` instead of `readl`.
+ /// let v = io.relaxed().read32(0x10);
+ /// }
+ ///
+ /// ```
+ pub fn relaxed(&self) -> &RelaxedMmio<SIZE> {
+ // SAFETY: `RelaxedMmio` is `#[repr(transparent)]` over `Mmio`, so `Mmio<SIZE>` and
+ // `RelaxedMmio<SIZE>` have identical layout.
+ unsafe { core::mem::transmute(self) }
+ }
+}
+
+// MMIO regions support 8, 16, and 32-bit accesses.
+impl_mmio_io_capable!(RelaxedMmio, u8, readb_relaxed, writeb_relaxed);
+impl_mmio_io_capable!(RelaxedMmio, u16, readw_relaxed, writew_relaxed);
+impl_mmio_io_capable!(RelaxedMmio, u32, readl_relaxed, writel_relaxed);
+// MMIO regions on 64-bit systems also support 64-bit accesses.
+impl_mmio_io_capable!(
+ RelaxedMmio,
+ #[cfg(CONFIG_64BIT)]
+ u64,
+ readq_relaxed,
+ writeq_relaxed
+);
diff --git a/rust/kernel/io/mem.rs b/rust/kernel/io/mem.rs
index 620022cff401..7dc78d547f7a 100644
--- a/rust/kernel/io/mem.rs
+++ b/rust/kernel/io/mem.rs
@@ -54,6 +54,7 @@ impl<'a> IoRequest<'a> {
/// use kernel::{
/// bindings,
/// device::Core,
+ /// io::Io,
/// of,
/// platform,
/// };
@@ -78,9 +79,9 @@ impl<'a> IoRequest<'a> {
/// let io = iomem.access(pdev.as_ref())?;
///
/// // Read and write a 32-bit value at `offset`.
- /// let data = io.read32_relaxed(offset);
+ /// let data = io.read32(offset);
///
- /// io.write32_relaxed(data, offset);
+ /// io.write32(data, offset);
///
/// # Ok(SampleDriver)
/// }
@@ -117,6 +118,7 @@ impl<'a> IoRequest<'a> {
/// use kernel::{
/// bindings,
/// device::Core,
+ /// io::Io,
/// of,
/// platform,
/// };
@@ -141,9 +143,9 @@ impl<'a> IoRequest<'a> {
///
/// let io = iomem.access(pdev.as_ref())?;
///
- /// let data = io.try_read32_relaxed(offset)?;
+ /// let data = io.try_read32(offset)?;
///
- /// io.try_write32_relaxed(data, offset)?;
+ /// io.try_write32(data, offset)?;
///
/// # Ok(SampleDriver)
/// }
diff --git a/rust/kernel/io/register.rs b/rust/kernel/io/register.rs
new file mode 100644
index 000000000000..abc49926abfe
--- /dev/null
+++ b/rust/kernel/io/register.rs
@@ -0,0 +1,1260 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Macro to define register layout and accessors.
+//!
+//! The [`register!`](kernel::io::register!) macro provides an intuitive and readable syntax for
+//! defining a dedicated type for each register and accessing it using [`Io`](super::Io). Each such
+//! type comes with its own field accessors that can return an error if a field's value is invalid.
+//!
+//! Note: most of the items in this module are public so they can be referenced by the macro, but
+//! most are not to be used directly by users. Outside of the `register!` macro itself, the only
+//! items you might want to import from this module are [`WithBase`] and [`Array`].
+//!
+//! # Simple example
+//!
+//! ```no_run
+//! use kernel::io::register;
+//!
+//! register! {
+//! /// Basic information about the chip.
+//! pub BOOT_0(u32) @ 0x00000100 {
+//! /// Vendor ID.
+//! 15:8 vendor_id;
+//! /// Major revision of the chip.
+//! 7:4 major_revision;
+//! /// Minor revision of the chip.
+//! 3:0 minor_revision;
+//! }
+//! }
+//! ```
+//!
+//! This defines a 32-bit `BOOT_0` type which can be read from or written to offset `0x100` of an
+//! `Io` region, with the described bitfields. For instance, `minor_revision` consists of the 4
+//! least significant bits of the type.
+//!
+//! Fields are instances of [`Bounded`](kernel::num::Bounded) and can be read by calling their
+//! getter method, which is named after them. They also have setter methods prefixed with `with_`
+//! for runtime values and `with_const_` for constant values. All setters return the updated
+//! register value.
+//!
+//! Fields can also be transparently converted from/to an arbitrary type by using the `=>` and
+//! `?=>` syntaxes.
+//!
+//! If present, doc comments above register or fields definitions are added to the relevant item
+//! they document (the register type itself, or the field's setter and getter methods).
+//!
+//! Note that multiple registers can be defined in a single `register!` invocation. This can be
+//! useful to group related registers together.
+//!
+//! Here is how the register defined above can be used in code:
+//!
+//!
+//! ```no_run
+//! use kernel::{
+//! io::{
+//! register,
+//! Io,
+//! IoLoc,
+//! },
+//! num::Bounded,
+//! };
+//! # use kernel::io::Mmio;
+//! # register! {
+//! # pub BOOT_0(u32) @ 0x00000100 {
+//! # 15:8 vendor_id;
+//! # 7:4 major_revision;
+//! # 3:0 minor_revision;
+//! # }
+//! # }
+//! # fn test(io: &Mmio<0x1000>) {
+//! # fn obtain_vendor_id() -> u8 { 0xff }
+//!
+//! // Read from the register's defined offset (0x100).
+//! let boot0 = io.read(BOOT_0);
+//! pr_info!("chip revision: {}.{}", boot0.major_revision().get(), boot0.minor_revision().get());
+//!
+//! // Update some fields and write the new value back.
+//! let new_boot0 = boot0
+//! // Constant values.
+//! .with_const_major_revision::<3>()
+//! .with_const_minor_revision::<10>()
+//! // Runtime value.
+//! .with_vendor_id(obtain_vendor_id());
+//! io.write_reg(new_boot0);
+//!
+//! // Or, build a new value from zero and write it:
+//! io.write_reg(BOOT_0::zeroed()
+//! .with_const_major_revision::<3>()
+//! .with_const_minor_revision::<10>()
+//! .with_vendor_id(obtain_vendor_id())
+//! );
+//!
+//! // Or, read and update the register in a single step.
+//! io.update(BOOT_0, |r| r
+//! .with_const_major_revision::<3>()
+//! .with_const_minor_revision::<10>()
+//! .with_vendor_id(obtain_vendor_id())
+//! );
+//!
+//! // Constant values can also be built using the const setters.
+//! const V: BOOT_0 = pin_init::zeroed::<BOOT_0>()
+//! .with_const_major_revision::<3>()
+//! .with_const_minor_revision::<10>();
+//! # }
+//! ```
+//!
+//! For more extensive documentation about how to define registers, see the
+//! [`register!`](kernel::io::register!) macro.
+
+use core::marker::PhantomData;
+
+use crate::io::IoLoc;
+
+use kernel::build_assert;
+
+/// Trait implemented by all registers.
+pub trait Register: Sized {
+ /// Backing primitive type of the register.
+ type Storage: Into<Self> + From<Self>;
+
+ /// Start offset of the register.
+ ///
+ /// The interpretation of this offset depends on the type of the register.
+ const OFFSET: usize;
+}
+
+/// Trait implemented by registers with a fixed offset.
+pub trait FixedRegister: Register {}
+
+/// Allows `()` to be used as the `location` parameter of [`Io::write`](super::Io::write) when
+/// passing a [`FixedRegister`] value.
+impl<T> IoLoc<T> for ()
+where
+ T: FixedRegister,
+{
+ type IoType = T::Storage;
+
+ #[inline(always)]
+ fn offset(self) -> usize {
+ T::OFFSET
+ }
+}
+
+/// A [`FixedRegister`] carries its location in its type. Thus `FixedRegister` values can be used
+/// as an [`IoLoc`].
+impl<T> IoLoc<T> for T
+where
+ T: FixedRegister,
+{
+ type IoType = T::Storage;
+
+ #[inline(always)]
+ fn offset(self) -> usize {
+ T::OFFSET
+ }
+}
+
+/// Location of a fixed register.
+pub struct FixedRegisterLoc<T: FixedRegister>(PhantomData<T>);
+
+impl<T: FixedRegister> FixedRegisterLoc<T> {
+ /// Returns the location of `T`.
+ #[inline(always)]
+ // We do not implement `Default` so we can be const.
+ #[expect(clippy::new_without_default)]
+ pub const fn new() -> Self {
+ Self(PhantomData)
+ }
+}
+
+impl<T> IoLoc<T> for FixedRegisterLoc<T>
+where
+ T: FixedRegister,
+{
+ type IoType = T::Storage;
+
+ #[inline(always)]
+ fn offset(self) -> usize {
+ T::OFFSET
+ }
+}
+
+/// Trait providing a base address to be added to the offset of a relative register to obtain
+/// its actual offset.
+///
+/// The `T` generic argument is used to distinguish which base to use, in case a type provides
+/// several bases. It is given to the `register!` macro to restrict the use of the register to
+/// implementors of this particular variant.
+pub trait RegisterBase<T> {
+ /// Base address to which register offsets are added.
+ const BASE: usize;
+}
+
+/// Trait implemented by all registers that are relative to a base.
+pub trait WithBase {
+ /// Family of bases applicable to this register.
+ type BaseFamily;
+
+ /// Returns the absolute location of this type when using `B` as its base.
+ #[inline(always)]
+ fn of<B: RegisterBase<Self::BaseFamily>>() -> RelativeRegisterLoc<Self, B>
+ where
+ Self: Register,
+ {
+ RelativeRegisterLoc::new()
+ }
+}
+
+/// Trait implemented by relative registers.
+pub trait RelativeRegister: Register + WithBase {}
+
+/// Location of a relative register.
+///
+/// This can either be an immediately accessible regular [`RelativeRegister`], or a
+/// [`RelativeRegisterArray`] that needs one additional resolution through
+/// [`RelativeRegisterLoc::at`].
+pub struct RelativeRegisterLoc<T: WithBase, B: ?Sized>(PhantomData<T>, PhantomData<B>);
+
+impl<T, B> RelativeRegisterLoc<T, B>
+where
+ T: Register + WithBase,
+ B: RegisterBase<T::BaseFamily> + ?Sized,
+{
+ /// Returns the location of a relative register or register array.
+ #[inline(always)]
+ // We do not implement `Default` so we can be const.
+ #[expect(clippy::new_without_default)]
+ pub const fn new() -> Self {
+ Self(PhantomData, PhantomData)
+ }
+
+ // Returns the absolute offset of the relative register using base `B`.
+ //
+ // This is implemented as a private const method so it can be reused by the [`IoLoc`]
+ // implementations of both [`RelativeRegisterLoc`] and [`RelativeRegisterArrayLoc`].
+ #[inline]
+ const fn offset(self) -> usize {
+ B::BASE + T::OFFSET
+ }
+}
+
+impl<T, B> IoLoc<T> for RelativeRegisterLoc<T, B>
+where
+ T: RelativeRegister,
+ B: RegisterBase<T::BaseFamily> + ?Sized,
+{
+ type IoType = T::Storage;
+
+ #[inline(always)]
+ fn offset(self) -> usize {
+ RelativeRegisterLoc::offset(self)
+ }
+}
+
+/// Trait implemented by arrays of registers.
+pub trait RegisterArray: Register {
+ /// Number of elements in the registers array.
+ const SIZE: usize;
+ /// Number of bytes between the start of elements in the registers array.
+ const STRIDE: usize;
+}
+
+/// Location of an array register.
+pub struct RegisterArrayLoc<T: RegisterArray>(usize, PhantomData<T>);
+
+impl<T: RegisterArray> RegisterArrayLoc<T> {
+ /// Returns the location of register `T` at position `idx`, with build-time validation.
+ #[inline(always)]
+ pub fn new(idx: usize) -> Self {
+ build_assert!(idx < T::SIZE);
+
+ Self(idx, PhantomData)
+ }
+
+ /// Attempts to return the location of register `T` at position `idx`, with runtime validation.
+ #[inline(always)]
+ pub fn try_new(idx: usize) -> Option<Self> {
+ if idx < T::SIZE {
+ Some(Self(idx, PhantomData))
+ } else {
+ None
+ }
+ }
+}
+
+impl<T> IoLoc<T> for RegisterArrayLoc<T>
+where
+ T: RegisterArray,
+{
+ type IoType = T::Storage;
+
+ #[inline(always)]
+ fn offset(self) -> usize {
+ T::OFFSET + self.0 * T::STRIDE
+ }
+}
+
+/// Trait providing location builders for [`RegisterArray`]s.
+pub trait Array {
+ /// Returns the location of the register at position `idx`, with build-time validation.
+ #[inline(always)]
+ fn at(idx: usize) -> RegisterArrayLoc<Self>
+ where
+ Self: RegisterArray,
+ {
+ RegisterArrayLoc::new(idx)
+ }
+
+ /// Returns the location of the register at position `idx`, with runtime validation.
+ #[inline(always)]
+ fn try_at(idx: usize) -> Option<RegisterArrayLoc<Self>>
+ where
+ Self: RegisterArray,
+ {
+ RegisterArrayLoc::try_new(idx)
+ }
+}
+
+/// Trait implemented by arrays of relative registers.
+pub trait RelativeRegisterArray: RegisterArray + WithBase {}
+
+/// Location of a relative array register.
+pub struct RelativeRegisterArrayLoc<
+ T: RelativeRegisterArray,
+ B: RegisterBase<T::BaseFamily> + ?Sized,
+>(RelativeRegisterLoc<T, B>, usize);
+
+impl<T, B> RelativeRegisterArrayLoc<T, B>
+where
+ T: RelativeRegisterArray,
+ B: RegisterBase<T::BaseFamily> + ?Sized,
+{
+ /// Returns the location of register `T` from the base `B` at index `idx`, with build-time
+ /// validation.
+ #[inline(always)]
+ pub fn new(idx: usize) -> Self {
+ build_assert!(idx < T::SIZE);
+
+ Self(RelativeRegisterLoc::new(), idx)
+ }
+
+ /// Attempts to return the location of register `T` from the base `B` at index `idx`, with
+ /// runtime validation.
+ #[inline(always)]
+ pub fn try_new(idx: usize) -> Option<Self> {
+ if idx < T::SIZE {
+ Some(Self(RelativeRegisterLoc::new(), idx))
+ } else {
+ None
+ }
+ }
+}
+
+/// Methods exclusive to [`RelativeRegisterLoc`]s created with a [`RelativeRegisterArray`].
+impl<T, B> RelativeRegisterLoc<T, B>
+where
+ T: RelativeRegisterArray,
+ B: RegisterBase<T::BaseFamily> + ?Sized,
+{
+ /// Returns the location of the register at position `idx`, with build-time validation.
+ #[inline(always)]
+ pub fn at(self, idx: usize) -> RelativeRegisterArrayLoc<T, B> {
+ RelativeRegisterArrayLoc::new(idx)
+ }
+
+ /// Returns the location of the register at position `idx`, with runtime validation.
+ #[inline(always)]
+ pub fn try_at(self, idx: usize) -> Option<RelativeRegisterArrayLoc<T, B>> {
+ RelativeRegisterArrayLoc::try_new(idx)
+ }
+}
+
+impl<T, B> IoLoc<T> for RelativeRegisterArrayLoc<T, B>
+where
+ T: RelativeRegisterArray,
+ B: RegisterBase<T::BaseFamily> + ?Sized,
+{
+ type IoType = T::Storage;
+
+ #[inline(always)]
+ fn offset(self) -> usize {
+ self.0.offset() + self.1 * T::STRIDE
+ }
+}
+
+/// Trait implemented by items that contain both a register value and the absolute I/O location at
+/// which to write it.
+///
+/// Implementors can be used with [`Io::write_reg`](super::Io::write_reg).
+pub trait LocatedRegister {
+ /// Register value to write.
+ type Value: Register;
+ /// Full location information at which to write the value.
+ type Location: IoLoc<Self::Value>;
+
+ /// Consumes `self` and returns a `(location, value)` tuple describing a valid I/O write
+ /// operation.
+ fn into_io_op(self) -> (Self::Location, Self::Value);
+}
+
+impl<T> LocatedRegister for T
+where
+ T: FixedRegister,
+{
+ type Location = FixedRegisterLoc<Self::Value>;
+ type Value = T;
+
+ #[inline(always)]
+ fn into_io_op(self) -> (FixedRegisterLoc<T>, T) {
+ (FixedRegisterLoc::new(), self)
+ }
+}
+
+/// Defines a dedicated type for a register, including getter and setter methods for its fields and
+/// methods to read and write it from an [`Io`](kernel::io::Io) region.
+///
+/// This documentation focuses on how to declare registers. See the [module-level
+/// documentation](mod@kernel::io::register) for examples of how to access them.
+///
+/// There are 4 possible kinds of registers: fixed offset registers, relative registers, arrays of
+/// registers, and relative arrays of registers.
+///
+/// ## Fixed offset registers
+///
+/// These are the simplest kind of registers. Their location is simply an offset inside the I/O
+/// region. For instance:
+///
+/// ```ignore
+/// register! {
+/// pub FIXED_REG(u16) @ 0x80 {
+/// ...
+/// }
+/// }
+/// ```
+///
+/// This creates a 16-bit register named `FIXED_REG` located at offset `0x80` of an I/O region.
+///
+/// These registers' location can be built simply by referencing their name:
+///
+/// ```no_run
+/// use kernel::{
+/// io::{
+/// register,
+/// Io,
+/// },
+/// };
+/// # use kernel::io::Mmio;
+///
+/// register! {
+/// FIXED_REG(u32) @ 0x100 {
+/// 16:8 high_byte;
+/// 7:0 low_byte;
+/// }
+/// }
+///
+/// # fn test(io: &Mmio<0x1000>) {
+/// let val = io.read(FIXED_REG);
+///
+/// // Write from an already-existing value.
+/// io.write(FIXED_REG, val.with_low_byte(0xff));
+///
+/// // Create a register value from scratch.
+/// let val2 = FIXED_REG::zeroed().with_high_byte(0x80);
+///
+/// // The location of fixed offset registers is already contained in their type. Thus, the
+/// // `location` argument of `Io::write` is technically redundant and can be replaced by `()`.
+/// io.write((), val2);
+///
+/// // Or, the single-argument `Io::write_reg` can be used.
+/// io.write_reg(val2);
+/// # }
+///
+/// ```
+///
+/// It is possible to create an alias of an existing register with new field definitions by using
+/// the `=> ALIAS` syntax. This is useful for cases where a register's interpretation depends on
+/// the context:
+///
+/// ```no_run
+/// use kernel::io::register;
+///
+/// register! {
+/// /// Scratch register.
+/// pub SCRATCH(u32) @ 0x00000200 {
+/// 31:0 value;
+/// }
+///
+/// /// Boot status of the firmware.
+/// pub SCRATCH_BOOT_STATUS(u32) => SCRATCH {
+/// 0:0 completed;
+/// }
+/// }
+/// ```
+///
+/// In this example, `SCRATCH_BOOT_STATUS` uses the same I/O address as `SCRATCH`, while providing
+/// its own `completed` field.
+///
+/// ## Relative registers
+///
+/// Relative registers can be instantiated several times at a relative offset of a group of bases.
+/// For instance, imagine the following I/O space:
+///
+/// ```text
+/// +-----------------------------+
+/// | ... |
+/// | |
+/// 0x100--->+------------CPU0-------------+
+/// | |
+/// 0x110--->+-----------------------------+
+/// | CPU_CTL |
+/// +-----------------------------+
+/// | ... |
+/// | |
+/// | |
+/// 0x200--->+------------CPU1-------------+
+/// | |
+/// 0x210--->+-----------------------------+
+/// | CPU_CTL |
+/// +-----------------------------+
+/// | ... |
+/// +-----------------------------+
+/// ```
+///
+/// `CPU0` and `CPU1` both have a `CPU_CTL` register that starts at offset `0x10` of their I/O
+/// space segment. Since both instances of `CPU_CTL` share the same layout, we don't want to define
+/// them twice and would prefer a way to select which one to use from a single definition.
+///
+/// This can be done using the `Base + Offset` syntax when specifying the register's address:
+///
+/// ```ignore
+/// register! {
+/// pub RELATIVE_REG(u32) @ Base + 0x80 {
+/// ...
+/// }
+/// }
+/// ```
+///
+/// This creates a register with an offset of `0x80` from a given base.
+///
+/// `Base` is an arbitrary type (typically a ZST) to be used as a generic parameter of the
+/// [`RegisterBase`] trait to provide the base as a constant, i.e. each type providing a base for
+/// this register needs to implement `RegisterBase<Base>`.
+///
+/// The location of relative registers can be built using the [`WithBase::of`] method to specify
+/// its base. All relative registers implement [`WithBase`].
+///
+/// Here is the above layout translated into code:
+///
+/// ```no_run
+/// use kernel::{
+/// io::{
+/// register,
+/// register::{
+/// RegisterBase,
+/// WithBase,
+/// },
+/// Io,
+/// },
+/// };
+/// # use kernel::io::Mmio;
+///
+/// // Type used to identify the base.
+/// pub struct CpuCtlBase;
+///
+/// // ZST describing `CPU0`.
+/// struct Cpu0;
+/// impl RegisterBase<CpuCtlBase> for Cpu0 {
+/// const BASE: usize = 0x100;
+/// }
+///
+/// // ZST describing `CPU1`.
+/// struct Cpu1;
+/// impl RegisterBase<CpuCtlBase> for Cpu1 {
+/// const BASE: usize = 0x200;
+/// }
+///
+/// // This makes `CPU_CTL` accessible from all implementors of `RegisterBase<CpuCtlBase>`.
+/// register! {
+/// /// CPU core control.
+/// pub CPU_CTL(u32) @ CpuCtlBase + 0x10 {
+/// 0:0 start;
+/// }
+/// }
+///
+/// # fn test(io: Mmio<0x1000>) {
+/// // Read the status of `Cpu0`.
+/// let cpu0_started = io.read(CPU_CTL::of::<Cpu0>());
+///
+/// // Stop `Cpu0`.
+/// io.write(WithBase::of::<Cpu0>(), CPU_CTL::zeroed());
+/// # }
+///
+/// // Aliases can also be defined for relative register.
+/// register! {
+/// /// Alias to CPU core control.
+/// pub CPU_CTL_ALIAS(u32) => CpuCtlBase + CPU_CTL {
+/// /// Start the aliased CPU core.
+/// 1:1 alias_start;
+/// }
+/// }
+///
+/// # fn test2(io: Mmio<0x1000>) {
+/// // Start the aliased `CPU0`, leaving its other fields untouched.
+/// io.update(CPU_CTL_ALIAS::of::<Cpu0>(), |r| r.with_alias_start(true));
+/// # }
+/// ```
+///
+/// ## Arrays of registers
+///
+/// Some I/O areas contain consecutive registers that share the same field layout. These areas can
+/// be defined as an array of identical registers, allowing them to be accessed by index with
+/// compile-time or runtime bound checking:
+///
+/// ```ignore
+/// register! {
+/// pub REGISTER_ARRAY(u8)[10, stride = 4] @ 0x100 {
+/// ...
+/// }
+/// }
+/// ```
+///
+/// This defines `REGISTER_ARRAY`, an array of 10 byte registers starting at offset `0x100`. Each
+/// register is separated from its neighbor by 4 bytes.
+///
+/// The `stride` parameter is optional; if unspecified, the registers are placed consecutively from
+/// each other.
+///
+/// A location for a register in a register array is built using the [`Array::at`] trait method.
+/// All arrays of registers implement [`Array`].
+///
+/// ```no_run
+/// use kernel::{
+/// io::{
+/// register,
+/// register::Array,
+/// Io,
+/// },
+/// };
+/// # use kernel::io::Mmio;
+/// # fn get_scratch_idx() -> usize {
+/// # 0x15
+/// # }
+///
+/// // Array of 64 consecutive registers with the same layout starting at offset `0x80`.
+/// register! {
+/// /// Scratch registers.
+/// pub SCRATCH(u32)[64] @ 0x00000080 {
+/// 31:0 value;
+/// }
+/// }
+///
+/// # fn test(io: &Mmio<0x1000>)
+/// # -> Result<(), Error>{
+/// // Read scratch register 0, i.e. I/O address `0x80`.
+/// let scratch_0 = io.read(SCRATCH::at(0)).value();
+///
+/// // Write scratch register 15, i.e. I/O address `0x80 + (15 * 4)`.
+/// io.write(Array::at(15), SCRATCH::from(0xffeeaabb));
+///
+/// // This is out of bounds and won't build.
+/// // let scratch_128 = io.read(SCRATCH::at(128)).value();
+///
+/// // Runtime-obtained array index.
+/// let idx = get_scratch_idx();
+/// // Access on a runtime index returns an error if it is out-of-bounds.
+/// let some_scratch = io.read(SCRATCH::try_at(idx).ok_or(EINVAL)?).value();
+///
+/// // Alias to a specific register in an array.
+/// // Here `SCRATCH[8]` is used to convey the firmware exit code.
+/// register! {
+/// /// Firmware exit status code.
+/// pub FIRMWARE_STATUS(u32) => SCRATCH[8] {
+/// 7:0 status;
+/// }
+/// }
+///
+/// let status = io.read(FIRMWARE_STATUS).status();
+///
+/// // Non-contiguous register arrays can be defined by adding a stride parameter.
+/// // Here, each of the 16 registers of the array is separated by 8 bytes, meaning that the
+/// // registers of the two declarations below are interleaved.
+/// register! {
+/// /// Scratch registers bank 0.
+/// pub SCRATCH_INTERLEAVED_0(u32)[16, stride = 8] @ 0x000000c0 {
+/// 31:0 value;
+/// }
+///
+/// /// Scratch registers bank 1.
+/// pub SCRATCH_INTERLEAVED_1(u32)[16, stride = 8] @ 0x000000c4 {
+/// 31:0 value;
+/// }
+/// }
+/// # Ok(())
+/// # }
+/// ```
+///
+/// ## Relative arrays of registers
+///
+/// Combining the two features described in the sections above, arrays of registers accessible from
+/// a base can also be defined:
+///
+/// ```ignore
+/// register! {
+/// pub RELATIVE_REGISTER_ARRAY(u8)[10, stride = 4] @ Base + 0x100 {
+/// ...
+/// }
+/// }
+/// ```
+///
+/// Like relative registers, they implement the [`WithBase`] trait. However the return value of
+/// [`WithBase::of`] cannot be used directly as a location and must be further specified using the
+/// [`at`](RelativeRegisterLoc::at) method.
+///
+/// ```no_run
+/// use kernel::{
+/// io::{
+/// register,
+/// register::{
+/// RegisterBase,
+/// WithBase,
+/// },
+/// Io,
+/// },
+/// };
+/// # use kernel::io::Mmio;
+/// # fn get_scratch_idx() -> usize {
+/// # 0x15
+/// # }
+///
+/// // Type used as parameter of `RegisterBase` to specify the base.
+/// pub struct CpuCtlBase;
+///
+/// // ZST describing `CPU0`.
+/// struct Cpu0;
+/// impl RegisterBase<CpuCtlBase> for Cpu0 {
+/// const BASE: usize = 0x100;
+/// }
+///
+/// // ZST describing `CPU1`.
+/// struct Cpu1;
+/// impl RegisterBase<CpuCtlBase> for Cpu1 {
+/// const BASE: usize = 0x200;
+/// }
+///
+/// // 64 per-cpu scratch registers, arranged as a contiguous array.
+/// register! {
+/// /// Per-CPU scratch registers.
+/// pub CPU_SCRATCH(u32)[64] @ CpuCtlBase + 0x00000080 {
+/// 31:0 value;
+/// }
+/// }
+///
+/// # fn test(io: &Mmio<0x1000>) -> Result<(), Error> {
+/// // Read scratch register 0 of CPU0.
+/// let scratch = io.read(CPU_SCRATCH::of::<Cpu0>().at(0));
+///
+/// // Write the retrieved value into scratch register 15 of CPU1.
+/// io.write(WithBase::of::<Cpu1>().at(15), scratch);
+///
+/// // This won't build.
+/// // let cpu0_scratch_128 = io.read(CPU_SCRATCH::of::<Cpu0>().at(128)).value();
+///
+/// // Runtime-obtained array index.
+/// let scratch_idx = get_scratch_idx();
+/// // Access on a runtime index returns an error if it is out-of-bounds.
+/// let cpu0_scratch = io.read(
+/// CPU_SCRATCH::of::<Cpu0>().try_at(scratch_idx).ok_or(EINVAL)?
+/// ).value();
+/// # Ok(())
+/// # }
+///
+/// // Alias to `SCRATCH[8]` used to convey the firmware exit code.
+/// register! {
+/// /// Per-CPU firmware exit status code.
+/// pub CPU_FIRMWARE_STATUS(u32) => CpuCtlBase + CPU_SCRATCH[8] {
+/// 7:0 status;
+/// }
+/// }
+///
+/// // Non-contiguous relative register arrays can be defined by adding a stride parameter.
+/// // Here, each of the 16 registers of the array is separated by 8 bytes, meaning that the
+/// // registers of the two declarations below are interleaved.
+/// register! {
+/// /// Scratch registers bank 0.
+/// pub CPU_SCRATCH_INTERLEAVED_0(u32)[16, stride = 8] @ CpuCtlBase + 0x00000d00 {
+/// 31:0 value;
+/// }
+///
+/// /// Scratch registers bank 1.
+/// pub CPU_SCRATCH_INTERLEAVED_1(u32)[16, stride = 8] @ CpuCtlBase + 0x00000d04 {
+/// 31:0 value;
+/// }
+/// }
+///
+/// # fn test2(io: &Mmio<0x1000>) -> Result<(), Error> {
+/// let cpu0_status = io.read(CPU_FIRMWARE_STATUS::of::<Cpu0>()).status();
+/// # Ok(())
+/// # }
+/// ```
+#[macro_export]
+macro_rules! register {
+ // Entry point for the macro, allowing multiple registers to be defined in one call.
+ // It matches all possible register declaration patterns to dispatch them to corresponding
+ // `@reg` rule that defines a single register.
+ (
+ $(
+ $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty)
+ $([ $size:expr $(, stride = $stride:expr)? ])?
+ $(@ $($base:ident +)? $offset:literal)?
+ $(=> $alias:ident $(+ $alias_offset:ident)? $([$alias_idx:expr])? )?
+ { $($fields:tt)* }
+ )*
+ ) => {
+ $(
+ $crate::register!(
+ @reg $(#[$attr])* $vis $name ($storage) $([$size $(, stride = $stride)?])?
+ $(@ $($base +)? $offset)?
+ $(=> $alias $(+ $alias_offset)? $([$alias_idx])? )?
+ { $($fields)* }
+ );
+ )*
+ };
+
+ // All the rules below are private helpers.
+
+ // Creates a register at a fixed offset of the MMIO space.
+ (
+ @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) @ $offset:literal
+ { $($fields:tt)* }
+ ) => {
+ $crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* });
+ $crate::register!(@io_base $name($storage) @ $offset);
+ $crate::register!(@io_fixed $(#[$attr])* $vis $name($storage));
+ };
+
+ // Creates an alias register of fixed offset register `alias` with its own fields.
+ (
+ @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) => $alias:ident
+ { $($fields:tt)* }
+ ) => {
+ $crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* });
+ $crate::register!(
+ @io_base $name($storage) @
+ <$alias as $crate::io::register::Register>::OFFSET
+ );
+ $crate::register!(@io_fixed $(#[$attr])* $vis $name($storage));
+ };
+
+ // Creates a register at a relative offset from a base address provider.
+ (
+ @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) @ $base:ident + $offset:literal
+ { $($fields:tt)* }
+ ) => {
+ $crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* });
+ $crate::register!(@io_base $name($storage) @ $offset);
+ $crate::register!(@io_relative $vis $name($storage) @ $base);
+ };
+
+ // Creates an alias register of relative offset register `alias` with its own fields.
+ (
+ @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) => $base:ident + $alias:ident
+ { $($fields:tt)* }
+ ) => {
+ $crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* });
+ $crate::register!(
+ @io_base $name($storage) @ <$alias as $crate::io::register::Register>::OFFSET
+ );
+ $crate::register!(@io_relative $vis $name($storage) @ $base);
+ };
+
+ // Creates an array of registers at a fixed offset of the MMIO space.
+ (
+ @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty)
+ [ $size:expr, stride = $stride:expr ] @ $offset:literal { $($fields:tt)* }
+ ) => {
+ ::kernel::static_assert!(::core::mem::size_of::<$storage>() <= $stride);
+
+ $crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* });
+ $crate::register!(@io_base $name($storage) @ $offset);
+ $crate::register!(@io_array $vis $name($storage) [ $size, stride = $stride ]);
+ };
+
+ // Shortcut for contiguous array of registers (stride == size of element).
+ (
+ @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) [ $size:expr ] @ $offset:literal
+ { $($fields:tt)* }
+ ) => {
+ $crate::register!(
+ $(#[$attr])* $vis $name($storage) [ $size, stride = ::core::mem::size_of::<$storage>() ]
+ @ $offset { $($fields)* }
+ );
+ };
+
+ // Creates an alias of register `idx` of array of registers `alias` with its own fields.
+ (
+ @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) => $alias:ident [ $idx:expr ]
+ { $($fields:tt)* }
+ ) => {
+ ::kernel::static_assert!($idx < <$alias as $crate::io::register::RegisterArray>::SIZE);
+
+ $crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* });
+ $crate::register!(
+ @io_base $name($storage) @
+ <$alias as $crate::io::register::Register>::OFFSET
+ + $idx * <$alias as $crate::io::register::RegisterArray>::STRIDE
+ );
+ $crate::register!(@io_fixed $(#[$attr])* $vis $name($storage));
+ };
+
+ // Creates an array of registers at a relative offset from a base address provider.
+ (
+ @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty)
+ [ $size:expr, stride = $stride:expr ]
+ @ $base:ident + $offset:literal { $($fields:tt)* }
+ ) => {
+ ::kernel::static_assert!(::core::mem::size_of::<$storage>() <= $stride);
+
+ $crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* });
+ $crate::register!(@io_base $name($storage) @ $offset);
+ $crate::register!(
+ @io_relative_array $vis $name($storage) [ $size, stride = $stride ] @ $base + $offset
+ );
+ };
+
+ // Shortcut for contiguous array of relative registers (stride == size of element).
+ (
+ @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) [ $size:expr ]
+ @ $base:ident + $offset:literal { $($fields:tt)* }
+ ) => {
+ $crate::register!(
+ $(#[$attr])* $vis $name($storage) [ $size, stride = ::core::mem::size_of::<$storage>() ]
+ @ $base + $offset { $($fields)* }
+ );
+ };
+
+ // Creates an alias of register `idx` of relative array of registers `alias` with its own
+ // fields.
+ (
+ @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty)
+ => $base:ident + $alias:ident [ $idx:expr ] { $($fields:tt)* }
+ ) => {
+ ::kernel::static_assert!($idx < <$alias as $crate::io::register::RegisterArray>::SIZE);
+
+ $crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* });
+ $crate::register!(
+ @io_base $name($storage) @
+ <$alias as $crate::io::register::Register>::OFFSET +
+ $idx * <$alias as $crate::io::register::RegisterArray>::STRIDE
+ );
+ $crate::register!(@io_relative $vis $name($storage) @ $base);
+ };
+
+ // Generates the bitfield for the register.
+ //
+ // `#[allow(non_camel_case_types)]` is added since register names typically use
+ // `SCREAMING_CASE`.
+ (
+ @bitfield $(#[$attr:meta])* $vis:vis struct $name:ident($storage:ty) { $($fields:tt)* }
+ ) => {
+ $crate::register!(@bitfield_core
+ #[allow(non_camel_case_types)]
+ $(#[$attr])* $vis $name $storage
+ );
+ $crate::register!(@bitfield_fields $vis $name $storage { $($fields)* });
+ };
+
+ // Implementations shared by all registers types.
+ (@io_base $name:ident($storage:ty) @ $offset:expr) => {
+ impl $crate::io::register::Register for $name {
+ type Storage = $storage;
+
+ const OFFSET: usize = $offset;
+ }
+ };
+
+ // Implementations of fixed registers.
+ (@io_fixed $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty)) => {
+ impl $crate::io::register::FixedRegister for $name {}
+
+ $(#[$attr])*
+ $vis const $name: $crate::io::register::FixedRegisterLoc<$name> =
+ $crate::io::register::FixedRegisterLoc::<$name>::new();
+ };
+
+ // Implementations of relative registers.
+ (@io_relative $vis:vis $name:ident ($storage:ty) @ $base:ident) => {
+ impl $crate::io::register::WithBase for $name {
+ type BaseFamily = $base;
+ }
+
+ impl $crate::io::register::RelativeRegister for $name {}
+ };
+
+ // Implementations of register arrays.
+ (@io_array $vis:vis $name:ident ($storage:ty) [ $size:expr, stride = $stride:expr ]) => {
+ impl $crate::io::register::Array for $name {}
+
+ impl $crate::io::register::RegisterArray for $name {
+ const SIZE: usize = $size;
+ const STRIDE: usize = $stride;
+ }
+ };
+
+ // Implementations of relative array registers.
+ (
+ @io_relative_array $vis:vis $name:ident ($storage:ty) [ $size:expr, stride = $stride:expr ]
+ @ $base:ident + $offset:literal
+ ) => {
+ impl $crate::io::register::WithBase for $name {
+ type BaseFamily = $base;
+ }
+
+ impl $crate::io::register::RegisterArray for $name {
+ const SIZE: usize = $size;
+ const STRIDE: usize = $stride;
+ }
+
+ impl $crate::io::register::RelativeRegisterArray for $name {}
+ };
+
+ // Defines the wrapper `$name` type and its conversions from/to the storage type.
+ (@bitfield_core $(#[$attr:meta])* $vis:vis $name:ident $storage:ty) => {
+ $(#[$attr])*
+ #[repr(transparent)]
+ #[derive(Clone, Copy, PartialEq, Eq)]
+ $vis struct $name {
+ inner: $storage,
+ }
+
+ #[allow(dead_code)]
+ impl $name {
+ /// Creates a bitfield from a raw value.
+ #[inline(always)]
+ $vis const fn from_raw(value: $storage) -> Self {
+ Self{ inner: value }
+ }
+
+ /// Turns this bitfield into its raw value.
+ ///
+ /// This is similar to the [`From`] implementation, but is shorter to invoke in
+ /// most cases.
+ #[inline(always)]
+ $vis const fn into_raw(self) -> $storage {
+ self.inner
+ }
+ }
+
+ // SAFETY: `$storage` is `Zeroable` and `$name` is transparent.
+ unsafe impl ::pin_init::Zeroable for $name {}
+
+ impl ::core::convert::From<$name> for $storage {
+ #[inline(always)]
+ fn from(val: $name) -> $storage {
+ val.into_raw()
+ }
+ }
+
+ impl ::core::convert::From<$storage> for $name {
+ #[inline(always)]
+ fn from(val: $storage) -> $name {
+ Self::from_raw(val)
+ }
+ }
+ };
+
+ // Definitions requiring knowledge of individual fields: private and public field accessors,
+ // and `Debug` implementation.
+ (@bitfield_fields $vis:vis $name:ident $storage:ty {
+ $($(#[doc = $doc:expr])* $hi:literal:$lo:literal $field:ident
+ $(?=> $try_into_type:ty)?
+ $(=> $into_type:ty)?
+ ;
+ )*
+ }
+ ) => {
+ #[allow(dead_code)]
+ impl $name {
+ $(
+ $crate::register!(@private_field_accessors $vis $name $storage : $hi:$lo $field);
+ $crate::register!(
+ @public_field_accessors $(#[doc = $doc])* $vis $name $storage : $hi:$lo $field
+ $(?=> $try_into_type)?
+ $(=> $into_type)?
+ );
+ )*
+ }
+
+ $crate::register!(@debug $name { $($field;)* });
+ };
+
+ // Private field accessors working with the exact `Bounded` type for the field.
+ (
+ @private_field_accessors $vis:vis $name:ident $storage:ty : $hi:tt:$lo:tt $field:ident
+ ) => {
+ ::kernel::macros::paste!(
+ $vis const [<$field:upper _RANGE>]: ::core::ops::RangeInclusive<u8> = $lo..=$hi;
+ $vis const [<$field:upper _MASK>]: $storage =
+ ((((1 << $hi) - 1) << 1) + 1) - ((1 << $lo) - 1);
+ $vis const [<$field:upper _SHIFT>]: u32 = $lo;
+ );
+
+ ::kernel::macros::paste!(
+ fn [<__ $field>](self) ->
+ ::kernel::num::Bounded<$storage, { $hi + 1 - $lo }> {
+ // Left shift to align the field's MSB with the storage MSB.
+ const ALIGN_TOP: u32 = $storage::BITS - ($hi + 1);
+ // Right shift to move the top-aligned field to bit 0 of the storage.
+ const ALIGN_BOTTOM: u32 = ALIGN_TOP + $lo;
+
+ // Extract the field using two shifts. `Bounded::shr` produces the correctly-sized
+ // output type.
+ let val = ::kernel::num::Bounded::<$storage, { $storage::BITS }>::from(
+ self.inner << ALIGN_TOP
+ );
+ val.shr::<ALIGN_BOTTOM, { $hi + 1 - $lo } >()
+ }
+
+ const fn [<__with_ $field>](
+ mut self,
+ value: ::kernel::num::Bounded<$storage, { $hi + 1 - $lo }>,
+ ) -> Self
+ {
+ const MASK: $storage = <$name>::[<$field:upper _MASK>];
+ const SHIFT: u32 = <$name>::[<$field:upper _SHIFT>];
+
+ let value = value.get() << SHIFT;
+ self.inner = (self.inner & !MASK) | value;
+
+ self
+ }
+ );
+ };
+
+ // Public accessors for fields infallibly (`=>`) converted to a type.
+ (
+ @public_field_accessors $(#[doc = $doc:expr])* $vis:vis $name:ident $storage:ty :
+ $hi:literal:$lo:literal $field:ident => $into_type:ty
+ ) => {
+ ::kernel::macros::paste!(
+
+ $(#[doc = $doc])*
+ #[doc = "Returns the value of this field."]
+ #[inline(always)]
+ $vis fn $field(self) -> $into_type
+ {
+ self.[<__ $field>]().into()
+ }
+
+ $(#[doc = $doc])*
+ #[doc = "Sets this field to the given `value`."]
+ #[inline(always)]
+ $vis fn [<with_ $field>](self, value: $into_type) -> Self
+ {
+ self.[<__with_ $field>](value.into())
+ }
+
+ );
+ };
+
+ // Public accessors for fields fallibly (`?=>`) converted to a type.
+ (
+ @public_field_accessors $(#[doc = $doc:expr])* $vis:vis $name:ident $storage:ty :
+ $hi:tt:$lo:tt $field:ident ?=> $try_into_type:ty
+ ) => {
+ ::kernel::macros::paste!(
+
+ $(#[doc = $doc])*
+ #[doc = "Returns the value of this field."]
+ #[inline(always)]
+ $vis fn $field(self) ->
+ Result<
+ $try_into_type,
+ <$try_into_type as ::core::convert::TryFrom<
+ ::kernel::num::Bounded<$storage, { $hi + 1 - $lo }>
+ >>::Error
+ >
+ {
+ self.[<__ $field>]().try_into()
+ }
+
+ $(#[doc = $doc])*
+ #[doc = "Sets this field to the given `value`."]
+ #[inline(always)]
+ $vis fn [<with_ $field>](self, value: $try_into_type) -> Self
+ {
+ self.[<__with_ $field>](value.into())
+ }
+
+ );
+ };
+
+ // Public accessors for fields not converted to a type.
+ (
+ @public_field_accessors $(#[doc = $doc:expr])* $vis:vis $name:ident $storage:ty :
+ $hi:tt:$lo:tt $field:ident
+ ) => {
+ ::kernel::macros::paste!(
+
+ $(#[doc = $doc])*
+ #[doc = "Returns the value of this field."]
+ #[inline(always)]
+ $vis fn $field(self) ->
+ ::kernel::num::Bounded<$storage, { $hi + 1 - $lo }>
+ {
+ self.[<__ $field>]()
+ }
+
+ $(#[doc = $doc])*
+ #[doc = "Sets this field to the compile-time constant `VALUE`."]
+ #[inline(always)]
+ $vis const fn [<with_const_ $field>]<const VALUE: $storage>(self) -> Self {
+ self.[<__with_ $field>](
+ ::kernel::num::Bounded::<$storage, { $hi + 1 - $lo }>::new::<VALUE>()
+ )
+ }
+
+ $(#[doc = $doc])*
+ #[doc = "Sets this field to the given `value`."]
+ #[inline(always)]
+ $vis fn [<with_ $field>]<T>(
+ self,
+ value: T,
+ ) -> Self
+ where T: Into<::kernel::num::Bounded<$storage, { $hi + 1 - $lo }>>,
+ {
+ self.[<__with_ $field>](value.into())
+ }
+
+ $(#[doc = $doc])*
+ #[doc = "Tries to set this field to `value`, returning an error if it is out of range."]
+ #[inline(always)]
+ $vis fn [<try_with_ $field>]<T>(
+ self,
+ value: T,
+ ) -> ::kernel::error::Result<Self>
+ where T: ::kernel::num::TryIntoBounded<$storage, { $hi + 1 - $lo }>,
+ {
+ Ok(
+ self.[<__with_ $field>](
+ value.try_into_bounded().ok_or(::kernel::error::code::EOVERFLOW)?
+ )
+ )
+ }
+
+ );
+ };
+
+ // `Debug` implementation.
+ (@debug $name:ident { $($field:ident;)* }) => {
+ impl ::kernel::fmt::Debug for $name {
+ fn fmt(&self, f: &mut ::kernel::fmt::Formatter<'_>) -> ::kernel::fmt::Result {
+ f.debug_struct(stringify!($name))
+ .field("<raw>", &::kernel::prelude::fmt!("{:#x}", self.inner))
+ $(
+ .field(stringify!($field), &self.$field())
+ )*
+ .finish()
+ }
+ }
+ };
+}
diff --git a/rust/kernel/irq/request.rs b/rust/kernel/irq/request.rs
index 7a36f790593e..f425fe12f7c8 100644
--- a/rust/kernel/irq/request.rs
+++ b/rust/kernel/irq/request.rs
@@ -27,7 +27,7 @@ pub enum IrqReturn {
}
/// Callbacks for an IRQ handler.
-pub trait Handler: Sync {
+pub trait Handler: Sync + 'static {
/// The hard IRQ handler.
///
/// This is executed in interrupt context, hence all corresponding
@@ -45,7 +45,7 @@ impl<T: ?Sized + Handler + Send> Handler for Arc<T> {
}
}
-impl<T: ?Sized + Handler, A: Allocator> Handler for Box<T, A> {
+impl<T: ?Sized + Handler, A: Allocator + 'static> Handler for Box<T, A> {
fn handle(&self, device: &Device<Bound>) -> IrqReturn {
T::handle(self, device)
}
@@ -181,7 +181,7 @@ impl<'a> IrqRequest<'a> {
///
/// * We own an irq handler whose cookie is a pointer to `Self`.
#[pin_data]
-pub struct Registration<T: Handler + 'static> {
+pub struct Registration<T: Handler> {
#[pin]
inner: Devres<RegistrationInner>,
@@ -194,7 +194,7 @@ pub struct Registration<T: Handler + 'static> {
_pin: PhantomPinned,
}
-impl<T: Handler + 'static> Registration<T> {
+impl<T: Handler> Registration<T> {
/// Registers the IRQ handler with the system for the given IRQ number.
pub fn new<'a>(
request: IrqRequest<'a>,
@@ -260,10 +260,7 @@ impl<T: Handler + 'static> Registration<T> {
/// # Safety
///
/// This function should be only used as the callback in `request_irq`.
-unsafe extern "C" fn handle_irq_callback<T: Handler + 'static>(
- _irq: i32,
- ptr: *mut c_void,
-) -> c_uint {
+unsafe extern "C" fn handle_irq_callback<T: Handler>(_irq: i32, ptr: *mut c_void) -> c_uint {
// SAFETY: `ptr` is a pointer to `Registration<T>` set in `Registration::new`
let registration = unsafe { &*(ptr as *const Registration<T>) };
// SAFETY: The irq callback is removed before the device is unbound, so the fact that the irq
@@ -287,7 +284,7 @@ pub enum ThreadedIrqReturn {
}
/// Callbacks for a threaded IRQ handler.
-pub trait ThreadedHandler: Sync {
+pub trait ThreadedHandler: Sync + 'static {
/// The hard IRQ handler.
///
/// This is executed in interrupt context, hence all corresponding
@@ -318,7 +315,7 @@ impl<T: ?Sized + ThreadedHandler + Send> ThreadedHandler for Arc<T> {
}
}
-impl<T: ?Sized + ThreadedHandler, A: Allocator> ThreadedHandler for Box<T, A> {
+impl<T: ?Sized + ThreadedHandler, A: Allocator + 'static> ThreadedHandler for Box<T, A> {
fn handle(&self, device: &Device<Bound>) -> ThreadedIrqReturn {
T::handle(self, device)
}
@@ -401,7 +398,7 @@ impl<T: ?Sized + ThreadedHandler, A: Allocator> ThreadedHandler for Box<T, A> {
///
/// * We own an irq handler whose cookie is a pointer to `Self`.
#[pin_data]
-pub struct ThreadedRegistration<T: ThreadedHandler + 'static> {
+pub struct ThreadedRegistration<T: ThreadedHandler> {
#[pin]
inner: Devres<RegistrationInner>,
@@ -414,7 +411,7 @@ pub struct ThreadedRegistration<T: ThreadedHandler + 'static> {
_pin: PhantomPinned,
}
-impl<T: ThreadedHandler + 'static> ThreadedRegistration<T> {
+impl<T: ThreadedHandler> ThreadedRegistration<T> {
/// Registers the IRQ handler with the system for the given IRQ number.
pub fn new<'a>(
request: IrqRequest<'a>,
@@ -481,7 +478,7 @@ impl<T: ThreadedHandler + 'static> ThreadedRegistration<T> {
/// # Safety
///
/// This function should be only used as the callback in `request_threaded_irq`.
-unsafe extern "C" fn handle_threaded_irq_callback<T: ThreadedHandler + 'static>(
+unsafe extern "C" fn handle_threaded_irq_callback<T: ThreadedHandler>(
_irq: i32,
ptr: *mut c_void,
) -> c_uint {
@@ -497,10 +494,7 @@ unsafe extern "C" fn handle_threaded_irq_callback<T: ThreadedHandler + 'static>(
/// # Safety
///
/// This function should be only used as the callback in `request_threaded_irq`.
-unsafe extern "C" fn thread_fn_callback<T: ThreadedHandler + 'static>(
- _irq: i32,
- ptr: *mut c_void,
-) -> c_uint {
+unsafe extern "C" fn thread_fn_callback<T: ThreadedHandler>(_irq: i32, ptr: *mut c_void) -> c_uint {
// SAFETY: `ptr` is a pointer to `ThreadedRegistration<T>` set in `ThreadedRegistration::new`
let registration = unsafe { &*(ptr as *const ThreadedRegistration<T>) };
// SAFETY: The irq callback is removed before the device is unbound, so the fact that the irq
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 0fa9d820fe7c..5d22892447bd 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -16,6 +16,9 @@
// Please see https://github.com/Rust-for-Linux/linux/issues/2 for details on
// the unstable features in use.
//
+// Stable since Rust 1.89.0.
+#![feature(generic_arg_infer)]
+//
// Expected to become stable.
#![feature(arbitrary_self_types)]
#![feature(derive_coerce_pointee)]
diff --git a/rust/kernel/num/bounded.rs b/rust/kernel/num/bounded.rs
index 54d0ce3ba595..f9f90d6ec482 100644
--- a/rust/kernel/num/bounded.rs
+++ b/rust/kernel/num/bounded.rs
@@ -375,6 +375,9 @@ where
/// Returns the wrapped value as the backing type.
///
+ /// This is similar to the [`Deref`] implementation, but doesn't enforce the size invariant of
+ /// the [`Bounded`], which might produce slightly less optimal code.
+ ///
/// # Examples
///
/// ```
@@ -383,8 +386,8 @@ where
/// let v = Bounded::<u32, 4>::new::<7>();
/// assert_eq!(v.get(), 7u32);
/// ```
- pub fn get(self) -> T {
- *self.deref()
+ pub const fn get(self) -> T {
+ self.0
}
/// Increases the number of bits usable for `self`.
@@ -467,6 +470,48 @@ where
// `N` bits, and with the same signedness.
unsafe { Bounded::__new(value) }
}
+
+ /// Right-shifts `self` by `SHIFT` and returns the result as a `Bounded<_, RES>`, where `RES >=
+ /// N - SHIFT`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use kernel::num::Bounded;
+ ///
+ /// let v = Bounded::<u32, 16>::new::<0xff00>();
+ /// let v_shifted: Bounded::<u32, 8> = v.shr::<8, _>();
+ ///
+ /// assert_eq!(v_shifted.get(), 0xff);
+ /// ```
+ pub fn shr<const SHIFT: u32, const RES: u32>(self) -> Bounded<T, RES> {
+ const { assert!(RES + SHIFT >= N) }
+
+ // SAFETY: We shift the value right by `SHIFT`, reducing the number of bits needed to
+ // represent the shifted value by as much, and just asserted that `RES >= N - SHIFT`.
+ unsafe { Bounded::__new(self.0 >> SHIFT) }
+ }
+
+ /// Left-shifts `self` by `SHIFT` and returns the result as a `Bounded<_, RES>`, where `RES >=
+ /// N + SHIFT`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use kernel::num::Bounded;
+ ///
+ /// let v = Bounded::<u32, 8>::new::<0xff>();
+ /// let v_shifted: Bounded::<u32, 16> = v.shl::<8, _>();
+ ///
+ /// assert_eq!(v_shifted.get(), 0xff00);
+ /// ```
+ pub fn shl<const SHIFT: u32, const RES: u32>(self) -> Bounded<T, RES> {
+ const { assert!(RES >= N + SHIFT) }
+
+ // SAFETY: We shift the value left by `SHIFT`, augmenting the number of bits needed to
+ // represent the shifted value by as much, and just asserted that `RES >= N + SHIFT`.
+ unsafe { Bounded::__new(self.0 << SHIFT) }
+ }
}
impl<T, const N: u32> Deref for Bounded<T, N>
@@ -1053,3 +1098,24 @@ where
unsafe { Self::__new(T::from(value)) }
}
}
+
+impl<T> Bounded<T, 1>
+where
+ T: Integer + Zeroable,
+{
+ /// Converts this [`Bounded`] into a [`bool`].
+ ///
+ /// This is a shorter way of writing `bool::from(self)`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use kernel::num::Bounded;
+ ///
+ /// assert_eq!(Bounded::<u8, 1>::new::<0>().into_bool(), false);
+ /// assert_eq!(Bounded::<u8, 1>::new::<1>().into_bool(), true);
+ /// ```
+ pub fn into_bool(self) -> bool {
+ self.into()
+ }
+}
diff --git a/rust/kernel/pci/io.rs b/rust/kernel/pci/io.rs
index fb6edab2aea7..ae78676c927f 100644
--- a/rust/kernel/pci/io.rs
+++ b/rust/kernel/pci/io.rs
@@ -8,8 +8,6 @@ use crate::{
device,
devres::Devres,
io::{
- io_define_read,
- io_define_write,
Io,
IoCapable,
IoKnownSize,
@@ -85,67 +83,41 @@ pub struct ConfigSpace<'a, S: ConfigSpaceKind = Extended> {
_marker: PhantomData<S>,
}
-/// Internal helper macros used to invoke C PCI configuration space read functions.
-///
-/// This macro is intended to be used by higher-level PCI configuration space access macros
-/// (io_define_read) and provides a unified expansion for infallible vs. fallible read semantics. It
-/// emits a direct call into the corresponding C helper and performs the required cast to the Rust
-/// return type.
-///
-/// # Parameters
-///
-/// * `$c_fn` – The C function performing the PCI configuration space write.
-/// * `$self` – The I/O backend object.
-/// * `$ty` – The type of the value to read.
-/// * `$addr` – The PCI configuration space offset to read.
-///
-/// This macro does not perform any validation; all invariants must be upheld by the higher-level
-/// abstraction invoking it.
-macro_rules! call_config_read {
- (infallible, $c_fn:ident, $self:ident, $ty:ty, $addr:expr) => {{
- let mut val: $ty = 0;
- // SAFETY: By the type invariant `$self.pdev` is a valid address.
- // CAST: The offset is cast to `i32` because the C functions expect a 32-bit signed offset
- // parameter. PCI configuration space size is at most 4096 bytes, so the value always fits
- // within `i32` without truncation or sign change.
- // Return value from C function is ignored in infallible accessors.
- let _ret = unsafe { bindings::$c_fn($self.pdev.as_raw(), $addr as i32, &mut val) };
- val
- }};
-}
+/// Implements [`IoCapable`] on [`ConfigSpace`] for `$ty` using `$read_fn` and `$write_fn`.
+macro_rules! impl_config_space_io_capable {
+ ($ty:ty, $read_fn:ident, $write_fn:ident) => {
+ impl<'a, S: ConfigSpaceKind> IoCapable<$ty> for ConfigSpace<'a, S> {
+ unsafe fn io_read(&self, address: usize) -> $ty {
+ let mut val: $ty = 0;
+
+ // Return value from C function is ignored in infallible accessors.
+ let _ret =
+ // SAFETY: By the type invariant `self.pdev` is a valid address.
+ // CAST: The offset is cast to `i32` because the C functions expect a 32-bit
+ // signed offset parameter. PCI configuration space size is at most 4096 bytes,
+ // so the value always fits within `i32` without truncation or sign change.
+ unsafe { bindings::$read_fn(self.pdev.as_raw(), address as i32, &mut val) };
+
+ val
+ }
-/// Internal helper macros used to invoke C PCI configuration space write functions.
-///
-/// This macro is intended to be used by higher-level PCI configuration space access macros
-/// (io_define_write) and provides a unified expansion for infallible vs. fallible read semantics.
-/// It emits a direct call into the corresponding C helper and performs the required cast to the
-/// Rust return type.
-///
-/// # Parameters
-///
-/// * `$c_fn` – The C function performing the PCI configuration space write.
-/// * `$self` – The I/O backend object.
-/// * `$ty` – The type of the written value.
-/// * `$addr` – The configuration space offset to write.
-/// * `$value` – The value to write.
-///
-/// This macro does not perform any validation; all invariants must be upheld by the higher-level
-/// abstraction invoking it.
-macro_rules! call_config_write {
- (infallible, $c_fn:ident, $self:ident, $ty:ty, $addr:expr, $value:expr) => {
- // SAFETY: By the type invariant `$self.pdev` is a valid address.
- // CAST: The offset is cast to `i32` because the C functions expect a 32-bit signed offset
- // parameter. PCI configuration space size is at most 4096 bytes, so the value always fits
- // within `i32` without truncation or sign change.
- // Return value from C function is ignored in infallible accessors.
- let _ret = unsafe { bindings::$c_fn($self.pdev.as_raw(), $addr as i32, $value) };
+ unsafe fn io_write(&self, value: $ty, address: usize) {
+ // Return value from C function is ignored in infallible accessors.
+ let _ret =
+ // SAFETY: By the type invariant `self.pdev` is a valid address.
+ // CAST: The offset is cast to `i32` because the C functions expect a 32-bit
+ // signed offset parameter. PCI configuration space size is at most 4096 bytes,
+ // so the value always fits within `i32` without truncation or sign change.
+ unsafe { bindings::$write_fn(self.pdev.as_raw(), address as i32, value) };
+ }
+ }
};
}
// PCI configuration space supports 8, 16, and 32-bit accesses.
-impl<'a, S: ConfigSpaceKind> IoCapable<u8> for ConfigSpace<'a, S> {}
-impl<'a, S: ConfigSpaceKind> IoCapable<u16> for ConfigSpace<'a, S> {}
-impl<'a, S: ConfigSpaceKind> IoCapable<u32> for ConfigSpace<'a, S> {}
+impl_config_space_io_capable!(u8, pci_read_config_byte, pci_write_config_byte);
+impl_config_space_io_capable!(u16, pci_read_config_word, pci_write_config_word);
+impl_config_space_io_capable!(u32, pci_read_config_dword, pci_write_config_dword);
impl<'a, S: ConfigSpaceKind> Io for ConfigSpace<'a, S> {
/// Returns the base address of the I/O region. It is always 0 for configuration space.
@@ -159,17 +131,6 @@ impl<'a, S: ConfigSpaceKind> Io for ConfigSpace<'a, S> {
fn maxsize(&self) -> usize {
self.pdev.cfg_size().into_raw()
}
-
- // PCI configuration space does not support fallible operations.
- // The default implementations from the Io trait are not used.
-
- io_define_read!(infallible, read8, call_config_read(pci_read_config_byte) -> u8);
- io_define_read!(infallible, read16, call_config_read(pci_read_config_word) -> u16);
- io_define_read!(infallible, read32, call_config_read(pci_read_config_dword) -> u32);
-
- io_define_write!(infallible, write8, call_config_write(pci_write_config_byte) <- u8);
- io_define_write!(infallible, write16, call_config_write(pci_write_config_word) <- u16);
- io_define_write!(infallible, write32, call_config_write(pci_write_config_dword) <- u32);
}
impl<'a, S: ConfigSpaceKind> IoKnownSize for ConfigSpace<'a, S> {
diff --git a/samples/rust/rust_driver_pci.rs b/samples/rust/rust_driver_pci.rs
index d3d4a7931deb..47d3e84fab63 100644
--- a/samples/rust/rust_driver_pci.rs
+++ b/samples/rust/rust_driver_pci.rs
@@ -5,30 +5,63 @@
//! To make this driver probe, QEMU must be run with `-device pci-testdev`.
use kernel::{
- device::Bound,
- device::Core,
+ device::{
+ Bound,
+ Core, //
+ },
devres::Devres,
- io::Io,
+ io::{
+ register,
+ register::Array,
+ Io, //
+ },
+ num::Bounded,
pci,
prelude::*,
sync::aref::ARef, //
};
-struct Regs;
+mod regs {
+ use super::*;
-impl Regs {
- const TEST: usize = 0x0;
- const OFFSET: usize = 0x4;
- const DATA: usize = 0x8;
- const COUNT: usize = 0xC;
- const END: usize = 0x10;
+ register! {
+ pub(super) TEST(u8) @ 0x0 {
+ 7:0 index => TestIndex;
+ }
+
+ pub(super) OFFSET(u32) @ 0x4 {
+ 31:0 offset;
+ }
+
+ pub(super) DATA(u8) @ 0x8 {
+ 7:0 data;
+ }
+
+ pub(super) COUNT(u32) @ 0xC {
+ 31:0 count;
+ }
+ }
+
+ pub(super) const END: usize = 0x10;
}
-type Bar0 = pci::Bar<{ Regs::END }>;
+type Bar0 = pci::Bar<{ regs::END }>;
#[derive(Copy, Clone, Debug)]
struct TestIndex(u8);
+impl From<Bounded<u8, 8>> for TestIndex {
+ fn from(value: Bounded<u8, 8>) -> Self {
+ Self(value.into())
+ }
+}
+
+impl From<TestIndex> for Bounded<u8, 8> {
+ fn from(value: TestIndex) -> Self {
+ value.0.into()
+ }
+}
+
impl TestIndex {
const NO_EVENTFD: Self = Self(0);
}
@@ -54,40 +87,53 @@ kernel::pci_device_table!(
impl SampleDriver {
fn testdev(index: &TestIndex, bar: &Bar0) -> Result<u32> {
// Select the test.
- bar.write8(index.0, Regs::TEST);
+ bar.write_reg(regs::TEST::zeroed().with_index(*index));
- let offset = bar.read32(Regs::OFFSET) as usize;
- let data = bar.read8(Regs::DATA);
+ let offset = bar.read(regs::OFFSET).into_raw() as usize;
+ let data = bar.read(regs::DATA).into();
// Write `data` to `offset` to increase `count` by one.
//
// Note that we need `try_write8`, since `offset` can't be checked at compile-time.
bar.try_write8(data, offset)?;
- Ok(bar.read32(Regs::COUNT))
+ Ok(bar.read(regs::COUNT).into())
}
fn config_space(pdev: &pci::Device<Bound>) {
let config = pdev.config_space();
- // TODO: use the register!() macro for defining PCI configuration space registers once it
- // has been move out of nova-core.
+ // Some PCI configuration space registers.
+ register! {
+ VENDOR_ID(u16) @ 0x0 {
+ 15:0 vendor_id;
+ }
+
+ REVISION_ID(u8) @ 0x8 {
+ 7:0 revision_id;
+ }
+
+ BAR(u32)[6] @ 0x10 {
+ 31:0 value;
+ }
+ }
+
dev_info!(
pdev,
"pci-testdev config space read8 rev ID: {:x}\n",
- config.read8(0x8)
+ config.read(REVISION_ID).revision_id()
);
dev_info!(
pdev,
"pci-testdev config space read16 vendor ID: {:x}\n",
- config.read16(0)
+ config.read(VENDOR_ID).vendor_id()
);
dev_info!(
pdev,
"pci-testdev config space read32 BAR 0: {:x}\n",
- config.read32(0x10)
+ config.read(BAR::at(0)).value()
);
}
}
@@ -111,7 +157,7 @@ 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"),
index: *info,
_: {
let bar = bar.access(pdev.as_ref())?;
@@ -131,7 +177,7 @@ impl pci::Driver for SampleDriver {
fn unbind(pdev: &pci::Device<Core>, this: Pin<&Self>) {
if let Ok(bar) = this.bar.access(pdev.as_ref()) {
// Reset pci-testdev by writing a new test index.
- bar.write8(this.index.0, Regs::TEST);
+ bar.write_reg(regs::TEST::zeroed().with_index(this.index));
}
}
}
diff --git a/scripts/Makefile.build b/scripts/Makefile.build
index 57cff77c2897..3498d25b15e8 100644
--- a/scripts/Makefile.build
+++ b/scripts/Makefile.build
@@ -311,12 +311,13 @@ $(obj)/%.lst: $(obj)/%.c FORCE
# The features in this list are the ones allowed for non-`rust/` code.
#
# - Stable since Rust 1.87.0: `feature(asm_goto)`.
+# - Stable since Rust 1.89.0: `feature(generic_arg_infer)`.
# - Expected to become stable: `feature(arbitrary_self_types)`.
# - To be determined: `feature(used_with_arg)`.
#
# Please see https://github.com/Rust-for-Linux/linux/issues/2 for details on
# the unstable features in use.
-rust_allowed_features := arbitrary_self_types,asm_goto,used_with_arg
+rust_allowed_features := arbitrary_self_types,asm_goto,generic_arg_infer,used_with_arg
# `--out-dir` is required to avoid temporaries being created by `rustc` in the
# current working directory, which may be not accessible in the out-of-tree
diff --git a/tools/testing/selftests/cgroup/test_memcontrol.c b/tools/testing/selftests/cgroup/test_memcontrol.c
index 2fb096a2a9f9..ea05a2524d0f 100644
--- a/tools/testing/selftests/cgroup/test_memcontrol.c
+++ b/tools/testing/selftests/cgroup/test_memcontrol.c
@@ -10,6 +10,7 @@
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
+#include <sys/inotify.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <arpa/inet.h>
@@ -1643,6 +1644,115 @@ cleanup:
return ret;
}
+static int read_event(int inotify_fd, int expected_event, int expected_wd)
+{
+ struct inotify_event event;
+ ssize_t len = 0;
+
+ len = read(inotify_fd, &event, sizeof(event));
+ if (len < (ssize_t)sizeof(event))
+ return -1;
+
+ if (event.mask != expected_event || event.wd != expected_wd) {
+ fprintf(stderr,
+ "event does not match expected values: mask %d (expected %d) wd %d (expected %d)\n",
+ event.mask, expected_event, event.wd, expected_wd);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int test_memcg_inotify_delete_file(const char *root)
+{
+ int ret = KSFT_FAIL;
+ char *memcg = NULL;
+ int fd, wd;
+
+ memcg = cg_name(root, "memcg_test_0");
+
+ if (!memcg)
+ goto cleanup;
+
+ if (cg_create(memcg))
+ goto cleanup;
+
+ fd = inotify_init1(0);
+ if (fd == -1)
+ goto cleanup;
+
+ wd = inotify_add_watch(fd, cg_control(memcg, "memory.events"), IN_DELETE_SELF);
+ if (wd == -1)
+ goto cleanup;
+
+ if (cg_destroy(memcg))
+ goto cleanup;
+ free(memcg);
+ memcg = NULL;
+
+ if (read_event(fd, IN_DELETE_SELF, wd))
+ goto cleanup;
+
+ if (read_event(fd, IN_IGNORED, wd))
+ goto cleanup;
+
+ ret = KSFT_PASS;
+
+cleanup:
+ if (fd >= 0)
+ close(fd);
+ if (memcg)
+ cg_destroy(memcg);
+ free(memcg);
+
+ return ret;
+}
+
+static int test_memcg_inotify_delete_dir(const char *root)
+{
+ int ret = KSFT_FAIL;
+ char *memcg = NULL;
+ int fd, wd;
+
+ memcg = cg_name(root, "memcg_test_0");
+
+ if (!memcg)
+ goto cleanup;
+
+ if (cg_create(memcg))
+ goto cleanup;
+
+ fd = inotify_init1(0);
+ if (fd == -1)
+ goto cleanup;
+
+ wd = inotify_add_watch(fd, memcg, IN_DELETE_SELF);
+ if (wd == -1)
+ goto cleanup;
+
+ if (cg_destroy(memcg))
+ goto cleanup;
+ free(memcg);
+ memcg = NULL;
+
+ if (read_event(fd, IN_DELETE_SELF, wd))
+ goto cleanup;
+
+ if (read_event(fd, IN_IGNORED, wd))
+ goto cleanup;
+
+ ret = KSFT_PASS;
+
+cleanup:
+ if (fd >= 0)
+ close(fd);
+ if (memcg)
+ cg_destroy(memcg);
+ free(memcg);
+
+ return ret;
+}
+
#define T(x) { x, #x }
struct memcg_test {
int (*fn)(const char *root);
@@ -1662,6 +1772,8 @@ struct memcg_test {
T(test_memcg_oom_group_leaf_events),
T(test_memcg_oom_group_parent_events),
T(test_memcg_oom_group_score_events),
+ T(test_memcg_inotify_delete_file),
+ T(test_memcg_inotify_delete_dir),
};
#undef T