Driver core updates for 6.15-rc1

Here is the big set of driver core updates for 6.15-rc1.  Lots of stuff
 happened this development cycle, including:
   - kernfs scaling changes to make it even faster thanks to rcu
   - bin_attribute constify work in many subsystems
   - faux bus minor tweaks for the rust bindings
   - rust binding updates for driver core, pci, and platform busses,
     making more functionaliy available to rust drivers.  These are all
     due to people actually trying to use the bindings that were in 6.14.
   - make Rafael and Danilo full co-maintainers of the driver core
     codebase
   - other minor fixes and updates.
 
 This has been in linux-next for a while now, with the only reported
 issue being some merge conflicts with the rust tree.  Depending on which
 tree you pull first, you will have conflicts in one of them.  The merge
 resolution has been in linux-next as an example of what to do, or can be
 found here:
 	https://lore.kernel.org/r/CANiq72n3Xe8JcnEjirDhCwQgvWoE65dddWecXnfdnbrmuah-RQ@mail.gmail.com
 
 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 -----BEGIN PGP SIGNATURE-----
 
 iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCZ+mMrg8cZ3JlZ0Brcm9h
 aC5jb20ACgkQMUfUDdst+ylRgwCdH58OE3BgL0uoFY5vFImStpmPtqUAoL5HpVWI
 jtbJ+UuXGsnmO+JVNBEv
 =gy6W
 -----END PGP SIGNATURE-----

Merge tag 'driver-core-6.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core

Pull driver core updatesk from Greg KH:
 "Here is the big set of driver core updates for 6.15-rc1. Lots of stuff
  happened this development cycle, including:

   - kernfs scaling changes to make it even faster thanks to rcu

   - bin_attribute constify work in many subsystems

   - faux bus minor tweaks for the rust bindings

   - rust binding updates for driver core, pci, and platform busses,
     making more functionaliy available to rust drivers. These are all
     due to people actually trying to use the bindings that were in
     6.14.

   - make Rafael and Danilo full co-maintainers of the driver core
     codebase

   - other minor fixes and updates"

* tag 'driver-core-6.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core: (52 commits)
  rust: platform: require Send for Driver trait implementers
  rust: pci: require Send for Driver trait implementers
  rust: platform: impl Send + Sync for platform::Device
  rust: pci: impl Send + Sync for pci::Device
  rust: platform: fix unrestricted &mut platform::Device
  rust: pci: fix unrestricted &mut pci::Device
  rust: device: implement device context marker
  rust: pci: use to_result() in enable_device_mem()
  MAINTAINERS: driver core: mark Rafael and Danilo as co-maintainers
  rust/kernel/faux: mark Registration methods inline
  driver core: faux: only create the device if probe() succeeds
  rust/faux: Add missing parent argument to Registration::new()
  rust/faux: Drop #[repr(transparent)] from faux::Registration
  rust: io: fix devres test with new io accessor functions
  rust: io: rename `io::Io` accessors
  kernfs: Move dput() outside of the RCU section.
  efi: rci2: mark bin_attribute as __ro_after_init
  rapidio: constify 'struct bin_attribute'
  firmware: qemu_fw_cfg: constify 'struct bin_attribute'
  powerpc/perf/hv-24x7: Constify 'struct bin_attribute'
  ...
This commit is contained in:
Linus Torvalds 2025-04-01 11:02:03 -07:00
commit 2cd5769fb0
62 changed files with 746 additions and 457 deletions

View File

@ -7193,15 +7193,17 @@ F: include/linux/component.h
DRIVER CORE, KOBJECTS, DEBUGFS AND SYSFS DRIVER CORE, KOBJECTS, DEBUGFS AND SYSFS
M: Greg Kroah-Hartman <gregkh@linuxfoundation.org> M: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
R: "Rafael J. Wysocki" <rafael@kernel.org> M: "Rafael J. Wysocki" <rafael@kernel.org>
R: Danilo Krummrich <dakr@kernel.org> M: Danilo Krummrich <dakr@kernel.org>
S: Supported S: Supported
T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core.git
F: Documentation/core-api/kobject.rst F: Documentation/core-api/kobject.rst
F: drivers/base/ F: drivers/base/
F: fs/debugfs/ F: fs/debugfs/
F: fs/sysfs/ F: fs/sysfs/
F: include/linux/device/
F: include/linux/debugfs.h F: include/linux/debugfs.h
F: include/linux/device.h
F: include/linux/fwnode.h F: include/linux/fwnode.h
F: include/linux/kobj* F: include/linux/kobj*
F: include/linux/property.h F: include/linux/property.h

View File

@ -52,7 +52,7 @@ static ssize_t size_show(struct kobject *kobj, struct kobj_attribute *attr,
} }
static ssize_t data_read(struct file *filep, struct kobject *kobj, static ssize_t data_read(struct file *filep, struct kobject *kobj,
struct bin_attribute *attr, char *buf, loff_t off, const struct bin_attribute *attr, char *buf, loff_t off,
size_t count) size_t count)
{ {
char *data; char *data;
@ -85,7 +85,7 @@ static ssize_t data_read(struct file *filep, struct kobject *kobj,
} }
static ssize_t update_write(struct file *filep, struct kobject *kobj, static ssize_t update_write(struct file *filep, struct kobject *kobj,
struct bin_attribute *attr, char *buf, loff_t off, const struct bin_attribute *attr, char *buf, loff_t off,
size_t count) size_t count)
{ {
int rc; int rc;
@ -104,11 +104,11 @@ static struct kobj_attribute format_attr = __ATTR_RO(format);
static struct kobj_attribute size_attr = __ATTR_RO(size); static struct kobj_attribute size_attr = __ATTR_RO(size);
static struct bin_attribute data_attr = __BIN_ATTR_RO(data, 0); static struct bin_attribute data_attr __ro_after_init = __BIN_ATTR_RO(data, 0);
static struct bin_attribute update_attr = __BIN_ATTR_WO(update, 0); static struct bin_attribute update_attr __ro_after_init = __BIN_ATTR_WO(update, 0);
static struct bin_attribute *secvar_bin_attrs[] = { static const struct bin_attribute *const secvar_bin_attrs[] = {
&data_attr, &data_attr,
&update_attr, &update_attr,
NULL, NULL,
@ -121,7 +121,7 @@ static struct attribute *secvar_attrs[] = {
static const struct attribute_group secvar_attr_group = { static const struct attribute_group secvar_attr_group = {
.attrs = secvar_attrs, .attrs = secvar_attrs,
.bin_attrs = secvar_bin_attrs, .bin_attrs_new = secvar_bin_attrs,
}; };
__ATTRIBUTE_GROUPS(secvar_attr); __ATTRIBUTE_GROUPS(secvar_attr);
@ -130,7 +130,7 @@ static const struct kobj_type secvar_ktype = {
.default_groups = secvar_attr_groups, .default_groups = secvar_attr_groups,
}; };
static int update_kobj_size(void) static __init int update_kobj_size(void)
{ {
u64 varsize; u64 varsize;
@ -145,7 +145,7 @@ static int update_kobj_size(void)
return 0; return 0;
} }
static int secvar_sysfs_config(struct kobject *kobj) static __init int secvar_sysfs_config(struct kobject *kobj)
{ {
struct attribute_group config_group = { struct attribute_group config_group = {
.name = "config", .name = "config",
@ -158,7 +158,7 @@ static int secvar_sysfs_config(struct kobject *kobj)
return 0; return 0;
} }
static int add_var(const char *name) static __init int add_var(const char *name)
{ {
struct kobject *kobj; struct kobject *kobj;
int rc; int rc;
@ -181,7 +181,7 @@ static int add_var(const char *name)
return 0; return 0;
} }
static int secvar_sysfs_load(void) static __init int secvar_sysfs_load(void)
{ {
u64 namesize = 0; u64 namesize = 0;
char *name; char *name;
@ -209,7 +209,7 @@ static int secvar_sysfs_load(void)
return rc; return rc;
} }
static int secvar_sysfs_load_static(void) static __init int secvar_sysfs_load_static(void)
{ {
const char * const *name_ptr = secvar_ops->var_names; const char * const *name_ptr = secvar_ops->var_names;
int rc; int rc;
@ -224,7 +224,7 @@ static int secvar_sysfs_load_static(void)
return 0; return 0;
} }
static int secvar_sysfs_init(void) static __init int secvar_sysfs_init(void)
{ {
u64 max_size; u64 max_size;
int rc; int rc;

View File

@ -998,7 +998,7 @@ static int create_events_from_catalog(struct attribute ***events_,
} }
static ssize_t catalog_read(struct file *filp, struct kobject *kobj, static ssize_t catalog_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr, char *buf, const struct bin_attribute *bin_attr, char *buf,
loff_t offset, size_t count) loff_t offset, size_t count)
{ {
long hret; long hret;
@ -1108,14 +1108,14 @@ PAGE_0_ATTR(catalog_version, "%lld\n",
(unsigned long long)be64_to_cpu(page_0->version)); (unsigned long long)be64_to_cpu(page_0->version));
PAGE_0_ATTR(catalog_len, "%lld\n", PAGE_0_ATTR(catalog_len, "%lld\n",
(unsigned long long)be32_to_cpu(page_0->length) * 4096); (unsigned long long)be32_to_cpu(page_0->length) * 4096);
static BIN_ATTR_RO(catalog, 0/* real length varies */); static const BIN_ATTR_RO(catalog, 0/* real length varies */);
static DEVICE_ATTR_RO(domains); static DEVICE_ATTR_RO(domains);
static DEVICE_ATTR_RO(sockets); static DEVICE_ATTR_RO(sockets);
static DEVICE_ATTR_RO(chipspersocket); static DEVICE_ATTR_RO(chipspersocket);
static DEVICE_ATTR_RO(coresperchip); static DEVICE_ATTR_RO(coresperchip);
static DEVICE_ATTR_RO(cpumask); static DEVICE_ATTR_RO(cpumask);
static struct bin_attribute *if_bin_attrs[] = { static const struct bin_attribute *const if_bin_attrs[] = {
&bin_attr_catalog, &bin_attr_catalog,
NULL, NULL,
}; };
@ -1141,7 +1141,7 @@ static struct attribute *if_attrs[] = {
static const struct attribute_group if_group = { static const struct attribute_group if_group = {
.name = "interface", .name = "interface",
.bin_attrs = if_bin_attrs, .bin_attrs_new = if_bin_attrs,
.attrs = if_attrs, .attrs = if_attrs,
}; };

View File

@ -159,7 +159,7 @@ static Elf64_Word *__init auxv_to_elf64_notes(Elf64_Word *buf,
* Returns number of bytes read on success, -errno on failure. * Returns number of bytes read on success, -errno on failure.
*/ */
static ssize_t read_opalcore(struct file *file, struct kobject *kobj, static ssize_t read_opalcore(struct file *file, struct kobject *kobj,
struct bin_attribute *bin_attr, char *to, const struct bin_attribute *bin_attr, char *to,
loff_t pos, size_t count) loff_t pos, size_t count)
{ {
struct opalcore *m; struct opalcore *m;
@ -206,9 +206,9 @@ static ssize_t read_opalcore(struct file *file, struct kobject *kobj,
return (tpos - pos); return (tpos - pos);
} }
static struct bin_attribute opal_core_attr = { static struct bin_attribute opal_core_attr __ro_after_init = {
.attr = {.name = "core", .mode = 0400}, .attr = {.name = "core", .mode = 0400},
.read = read_opalcore .read_new = read_opalcore
}; };
/* /*
@ -599,7 +599,7 @@ static struct attribute *mpipl_attr[] = {
NULL, NULL,
}; };
static struct bin_attribute *mpipl_bin_attr[] = { static const struct bin_attribute *const mpipl_bin_attr[] = {
&opal_core_attr, &opal_core_attr,
NULL, NULL,
@ -607,7 +607,7 @@ static struct bin_attribute *mpipl_bin_attr[] = {
static const struct attribute_group mpipl_group = { static const struct attribute_group mpipl_group = {
.attrs = mpipl_attr, .attrs = mpipl_attr,
.bin_attrs = mpipl_bin_attr, .bin_attrs_new = mpipl_bin_attr,
}; };
static int __init opalcore_init(void) static int __init opalcore_init(void)

View File

@ -286,7 +286,7 @@ static int64_t dump_read_data(struct dump_obj *dump)
} }
static ssize_t dump_attr_read(struct file *filep, struct kobject *kobj, static ssize_t dump_attr_read(struct file *filep, struct kobject *kobj,
struct bin_attribute *bin_attr, const struct bin_attribute *bin_attr,
char *buffer, loff_t pos, size_t count) char *buffer, loff_t pos, size_t count)
{ {
ssize_t rc; ssize_t rc;
@ -342,7 +342,7 @@ static void create_dump_obj(uint32_t id, size_t size, uint32_t type)
dump->dump_attr.attr.name = "dump"; dump->dump_attr.attr.name = "dump";
dump->dump_attr.attr.mode = 0400; dump->dump_attr.attr.mode = 0400;
dump->dump_attr.size = size; dump->dump_attr.size = size;
dump->dump_attr.read = dump_attr_read; dump->dump_attr.read_new = dump_attr_read;
dump->id = id; dump->id = id;
dump->size = size; dump->size = size;

View File

@ -156,7 +156,7 @@ static const struct kobj_type elog_ktype = {
#define OPAL_MAX_ERRLOG_SIZE 16384 #define OPAL_MAX_ERRLOG_SIZE 16384
static ssize_t raw_attr_read(struct file *filep, struct kobject *kobj, static ssize_t raw_attr_read(struct file *filep, struct kobject *kobj,
struct bin_attribute *bin_attr, const struct bin_attribute *bin_attr,
char *buffer, loff_t pos, size_t count) char *buffer, loff_t pos, size_t count)
{ {
int opal_rc; int opal_rc;
@ -203,7 +203,7 @@ static void create_elog_obj(uint64_t id, size_t size, uint64_t type)
elog->raw_attr.attr.name = "raw"; elog->raw_attr.attr.name = "raw";
elog->raw_attr.attr.mode = 0400; elog->raw_attr.attr.mode = 0400;
elog->raw_attr.size = size; elog->raw_attr.size = size;
elog->raw_attr.read = raw_attr_read; elog->raw_attr.read_new = raw_attr_read;
elog->id = id; elog->id = id;
elog->size = size; elog->size = size;

View File

@ -432,7 +432,7 @@ static int alloc_image_buf(char *buffer, size_t count)
* and pre-allocate required memory. * and pre-allocate required memory.
*/ */
static ssize_t image_data_write(struct file *filp, struct kobject *kobj, static ssize_t image_data_write(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr, const struct bin_attribute *bin_attr,
char *buffer, loff_t pos, size_t count) char *buffer, loff_t pos, size_t count)
{ {
int rc; int rc;
@ -493,7 +493,7 @@ static ssize_t image_data_write(struct file *filp, struct kobject *kobj,
static const struct bin_attribute image_data_attr = { static const struct bin_attribute image_data_attr = {
.attr = {.name = "image", .mode = 0200}, .attr = {.name = "image", .mode = 0200},
.size = MAX_IMAGE_SIZE, /* Limit image size */ .size = MAX_IMAGE_SIZE, /* Limit image size */
.write = image_data_write, .write_new = image_data_write,
}; };
static struct kobj_attribute validate_attribute = static struct kobj_attribute validate_attribute =

View File

@ -94,15 +94,15 @@ ssize_t opal_msglog_copy(char *to, loff_t pos, size_t count)
} }
static ssize_t opal_msglog_read(struct file *file, struct kobject *kobj, static ssize_t opal_msglog_read(struct file *file, struct kobject *kobj,
struct bin_attribute *bin_attr, char *to, const struct bin_attribute *bin_attr, char *to,
loff_t pos, size_t count) loff_t pos, size_t count)
{ {
return opal_msglog_copy(to, pos, count); return opal_msglog_copy(to, pos, count);
} }
static struct bin_attribute opal_msglog_attr = { static struct bin_attribute opal_msglog_attr __ro_after_init = {
.attr = {.name = "msglog", .mode = 0400}, .attr = {.name = "msglog", .mode = 0400},
.read = opal_msglog_read .read_new = opal_msglog_read
}; };
struct memcons *__init memcons_init(struct device_node *node, const char *mc_prop_name) struct memcons *__init memcons_init(struct device_node *node, const char *mc_prop_name)

View File

@ -32,15 +32,15 @@ int __init early_init_dt_scan_ultravisor(unsigned long node, const char *uname,
static struct memcons *uv_memcons; static struct memcons *uv_memcons;
static ssize_t uv_msglog_read(struct file *file, struct kobject *kobj, static ssize_t uv_msglog_read(struct file *file, struct kobject *kobj,
struct bin_attribute *bin_attr, char *to, const struct bin_attribute *bin_attr, char *to,
loff_t pos, size_t count) loff_t pos, size_t count)
{ {
return memcons_copy(uv_memcons, to, pos, count); return memcons_copy(uv_memcons, to, pos, count);
} }
static struct bin_attribute uv_msglog_attr = { static struct bin_attribute uv_msglog_attr __ro_after_init = {
.attr = {.name = "msglog", .mode = 0400}, .attr = {.name = "msglog", .mode = 0400},
.read = uv_msglog_read .read_new = uv_msglog_read
}; };
static int __init uv_init(void) static int __init uv_init(void)

View File

@ -403,6 +403,11 @@ static inline struct rdt_hw_resource *resctrl_to_arch_res(struct rdt_resource *r
extern struct mutex rdtgroup_mutex; extern struct mutex rdtgroup_mutex;
static inline const char *rdt_kn_name(const struct kernfs_node *kn)
{
return rcu_dereference_check(kn->name, lockdep_is_held(&rdtgroup_mutex));
}
extern struct rdt_hw_resource rdt_resources_all[]; extern struct rdt_hw_resource rdt_resources_all[];
extern struct rdtgroup rdtgroup_default; extern struct rdtgroup rdtgroup_default;
extern struct dentry *debugfs_resctrl; extern struct dentry *debugfs_resctrl;

View File

@ -52,7 +52,8 @@ static char *pseudo_lock_devnode(const struct device *dev, umode_t *mode)
rdtgrp = dev_get_drvdata(dev); rdtgrp = dev_get_drvdata(dev);
if (mode) if (mode)
*mode = 0600; *mode = 0600;
return kasprintf(GFP_KERNEL, "pseudo_lock/%s", rdtgrp->kn->name); guard(mutex)(&rdtgroup_mutex);
return kasprintf(GFP_KERNEL, "pseudo_lock/%s", rdt_kn_name(rdtgrp->kn));
} }
static const struct class pseudo_lock_class = { static const struct class pseudo_lock_class = {
@ -1298,6 +1299,7 @@ int rdtgroup_pseudo_lock_create(struct rdtgroup *rdtgrp)
struct task_struct *thread; struct task_struct *thread;
unsigned int new_minor; unsigned int new_minor;
struct device *dev; struct device *dev;
char *kn_name __free(kfree) = NULL;
int ret; int ret;
ret = pseudo_lock_region_alloc(plr); ret = pseudo_lock_region_alloc(plr);
@ -1309,6 +1311,11 @@ int rdtgroup_pseudo_lock_create(struct rdtgroup *rdtgrp)
ret = -EINVAL; ret = -EINVAL;
goto out_region; goto out_region;
} }
kn_name = kstrdup(rdt_kn_name(rdtgrp->kn), GFP_KERNEL);
if (!kn_name) {
ret = -ENOMEM;
goto out_cstates;
}
plr->thread_done = 0; plr->thread_done = 0;
@ -1353,8 +1360,7 @@ int rdtgroup_pseudo_lock_create(struct rdtgroup *rdtgrp)
mutex_unlock(&rdtgroup_mutex); mutex_unlock(&rdtgroup_mutex);
if (!IS_ERR_OR_NULL(debugfs_resctrl)) { if (!IS_ERR_OR_NULL(debugfs_resctrl)) {
plr->debugfs_dir = debugfs_create_dir(rdtgrp->kn->name, plr->debugfs_dir = debugfs_create_dir(kn_name, debugfs_resctrl);
debugfs_resctrl);
if (!IS_ERR_OR_NULL(plr->debugfs_dir)) if (!IS_ERR_OR_NULL(plr->debugfs_dir))
debugfs_create_file("pseudo_lock_measure", 0200, debugfs_create_file("pseudo_lock_measure", 0200,
plr->debugfs_dir, rdtgrp, plr->debugfs_dir, rdtgrp,
@ -1363,7 +1369,7 @@ int rdtgroup_pseudo_lock_create(struct rdtgroup *rdtgrp)
dev = device_create(&pseudo_lock_class, NULL, dev = device_create(&pseudo_lock_class, NULL,
MKDEV(pseudo_lock_major, new_minor), MKDEV(pseudo_lock_major, new_minor),
rdtgrp, "%s", rdtgrp->kn->name); rdtgrp, "%s", kn_name);
mutex_lock(&rdtgroup_mutex); mutex_lock(&rdtgroup_mutex);

View File

@ -944,14 +944,14 @@ int proc_resctrl_show(struct seq_file *s, struct pid_namespace *ns,
continue; continue;
seq_printf(s, "res:%s%s\n", (rdtg == &rdtgroup_default) ? "/" : "", seq_printf(s, "res:%s%s\n", (rdtg == &rdtgroup_default) ? "/" : "",
rdtg->kn->name); rdt_kn_name(rdtg->kn));
seq_puts(s, "mon:"); seq_puts(s, "mon:");
list_for_each_entry(crg, &rdtg->mon.crdtgrp_list, list_for_each_entry(crg, &rdtg->mon.crdtgrp_list,
mon.crdtgrp_list) { mon.crdtgrp_list) {
if (!resctrl_arch_match_rmid(tsk, crg->mon.parent->closid, if (!resctrl_arch_match_rmid(tsk, crg->mon.parent->closid,
crg->mon.rmid)) crg->mon.rmid))
continue; continue;
seq_printf(s, "%s", crg->kn->name); seq_printf(s, "%s", rdt_kn_name(crg->kn));
break; break;
} }
seq_putc(s, '\n'); seq_putc(s, '\n');
@ -984,10 +984,20 @@ static int rdt_last_cmd_status_show(struct kernfs_open_file *of,
return 0; return 0;
} }
static void *rdt_kn_parent_priv(struct kernfs_node *kn)
{
/*
* The parent pointer is only valid within RCU section since it can be
* replaced.
*/
guard(rcu)();
return rcu_dereference(kn->__parent)->priv;
}
static int rdt_num_closids_show(struct kernfs_open_file *of, static int rdt_num_closids_show(struct kernfs_open_file *of,
struct seq_file *seq, void *v) struct seq_file *seq, void *v)
{ {
struct resctrl_schema *s = of->kn->parent->priv; struct resctrl_schema *s = rdt_kn_parent_priv(of->kn);
seq_printf(seq, "%u\n", s->num_closid); seq_printf(seq, "%u\n", s->num_closid);
return 0; return 0;
@ -996,7 +1006,7 @@ static int rdt_num_closids_show(struct kernfs_open_file *of,
static int rdt_default_ctrl_show(struct kernfs_open_file *of, static int rdt_default_ctrl_show(struct kernfs_open_file *of,
struct seq_file *seq, void *v) struct seq_file *seq, void *v)
{ {
struct resctrl_schema *s = of->kn->parent->priv; struct resctrl_schema *s = rdt_kn_parent_priv(of->kn);
struct rdt_resource *r = s->res; struct rdt_resource *r = s->res;
seq_printf(seq, "%x\n", resctrl_get_default_ctrl(r)); seq_printf(seq, "%x\n", resctrl_get_default_ctrl(r));
@ -1006,7 +1016,7 @@ static int rdt_default_ctrl_show(struct kernfs_open_file *of,
static int rdt_min_cbm_bits_show(struct kernfs_open_file *of, static int rdt_min_cbm_bits_show(struct kernfs_open_file *of,
struct seq_file *seq, void *v) struct seq_file *seq, void *v)
{ {
struct resctrl_schema *s = of->kn->parent->priv; struct resctrl_schema *s = rdt_kn_parent_priv(of->kn);
struct rdt_resource *r = s->res; struct rdt_resource *r = s->res;
seq_printf(seq, "%u\n", r->cache.min_cbm_bits); seq_printf(seq, "%u\n", r->cache.min_cbm_bits);
@ -1016,7 +1026,7 @@ static int rdt_min_cbm_bits_show(struct kernfs_open_file *of,
static int rdt_shareable_bits_show(struct kernfs_open_file *of, static int rdt_shareable_bits_show(struct kernfs_open_file *of,
struct seq_file *seq, void *v) struct seq_file *seq, void *v)
{ {
struct resctrl_schema *s = of->kn->parent->priv; struct resctrl_schema *s = rdt_kn_parent_priv(of->kn);
struct rdt_resource *r = s->res; struct rdt_resource *r = s->res;
seq_printf(seq, "%x\n", r->cache.shareable_bits); seq_printf(seq, "%x\n", r->cache.shareable_bits);
@ -1040,7 +1050,7 @@ static int rdt_shareable_bits_show(struct kernfs_open_file *of,
static int rdt_bit_usage_show(struct kernfs_open_file *of, static int rdt_bit_usage_show(struct kernfs_open_file *of,
struct seq_file *seq, void *v) struct seq_file *seq, void *v)
{ {
struct resctrl_schema *s = of->kn->parent->priv; struct resctrl_schema *s = rdt_kn_parent_priv(of->kn);
/* /*
* Use unsigned long even though only 32 bits are used to ensure * Use unsigned long even though only 32 bits are used to ensure
* test_bit() is used safely. * test_bit() is used safely.
@ -1122,7 +1132,7 @@ static int rdt_bit_usage_show(struct kernfs_open_file *of,
static int rdt_min_bw_show(struct kernfs_open_file *of, static int rdt_min_bw_show(struct kernfs_open_file *of,
struct seq_file *seq, void *v) struct seq_file *seq, void *v)
{ {
struct resctrl_schema *s = of->kn->parent->priv; struct resctrl_schema *s = rdt_kn_parent_priv(of->kn);
struct rdt_resource *r = s->res; struct rdt_resource *r = s->res;
seq_printf(seq, "%u\n", r->membw.min_bw); seq_printf(seq, "%u\n", r->membw.min_bw);
@ -1132,7 +1142,7 @@ static int rdt_min_bw_show(struct kernfs_open_file *of,
static int rdt_num_rmids_show(struct kernfs_open_file *of, static int rdt_num_rmids_show(struct kernfs_open_file *of,
struct seq_file *seq, void *v) struct seq_file *seq, void *v)
{ {
struct rdt_resource *r = of->kn->parent->priv; struct rdt_resource *r = rdt_kn_parent_priv(of->kn);
seq_printf(seq, "%d\n", r->num_rmid); seq_printf(seq, "%d\n", r->num_rmid);
@ -1142,7 +1152,7 @@ static int rdt_num_rmids_show(struct kernfs_open_file *of,
static int rdt_mon_features_show(struct kernfs_open_file *of, static int rdt_mon_features_show(struct kernfs_open_file *of,
struct seq_file *seq, void *v) struct seq_file *seq, void *v)
{ {
struct rdt_resource *r = of->kn->parent->priv; struct rdt_resource *r = rdt_kn_parent_priv(of->kn);
struct mon_evt *mevt; struct mon_evt *mevt;
list_for_each_entry(mevt, &r->evt_list, list) { list_for_each_entry(mevt, &r->evt_list, list) {
@ -1157,7 +1167,7 @@ static int rdt_mon_features_show(struct kernfs_open_file *of,
static int rdt_bw_gran_show(struct kernfs_open_file *of, static int rdt_bw_gran_show(struct kernfs_open_file *of,
struct seq_file *seq, void *v) struct seq_file *seq, void *v)
{ {
struct resctrl_schema *s = of->kn->parent->priv; struct resctrl_schema *s = rdt_kn_parent_priv(of->kn);
struct rdt_resource *r = s->res; struct rdt_resource *r = s->res;
seq_printf(seq, "%u\n", r->membw.bw_gran); seq_printf(seq, "%u\n", r->membw.bw_gran);
@ -1167,7 +1177,7 @@ static int rdt_bw_gran_show(struct kernfs_open_file *of,
static int rdt_delay_linear_show(struct kernfs_open_file *of, static int rdt_delay_linear_show(struct kernfs_open_file *of,
struct seq_file *seq, void *v) struct seq_file *seq, void *v)
{ {
struct resctrl_schema *s = of->kn->parent->priv; struct resctrl_schema *s = rdt_kn_parent_priv(of->kn);
struct rdt_resource *r = s->res; struct rdt_resource *r = s->res;
seq_printf(seq, "%u\n", r->membw.delay_linear); seq_printf(seq, "%u\n", r->membw.delay_linear);
@ -1185,7 +1195,7 @@ static int max_threshold_occ_show(struct kernfs_open_file *of,
static int rdt_thread_throttle_mode_show(struct kernfs_open_file *of, static int rdt_thread_throttle_mode_show(struct kernfs_open_file *of,
struct seq_file *seq, void *v) struct seq_file *seq, void *v)
{ {
struct resctrl_schema *s = of->kn->parent->priv; struct resctrl_schema *s = rdt_kn_parent_priv(of->kn);
struct rdt_resource *r = s->res; struct rdt_resource *r = s->res;
switch (r->membw.throttle_mode) { switch (r->membw.throttle_mode) {
@ -1259,7 +1269,7 @@ static enum resctrl_conf_type resctrl_peer_type(enum resctrl_conf_type my_type)
static int rdt_has_sparse_bitmasks_show(struct kernfs_open_file *of, static int rdt_has_sparse_bitmasks_show(struct kernfs_open_file *of,
struct seq_file *seq, void *v) struct seq_file *seq, void *v)
{ {
struct resctrl_schema *s = of->kn->parent->priv; struct resctrl_schema *s = rdt_kn_parent_priv(of->kn);
struct rdt_resource *r = s->res; struct rdt_resource *r = s->res;
seq_printf(seq, "%u\n", r->cache.arch_has_sparse_bitmasks); seq_printf(seq, "%u\n", r->cache.arch_has_sparse_bitmasks);
@ -1670,7 +1680,7 @@ static int mbm_config_show(struct seq_file *s, struct rdt_resource *r, u32 evtid
static int mbm_total_bytes_config_show(struct kernfs_open_file *of, static int mbm_total_bytes_config_show(struct kernfs_open_file *of,
struct seq_file *seq, void *v) struct seq_file *seq, void *v)
{ {
struct rdt_resource *r = of->kn->parent->priv; struct rdt_resource *r = rdt_kn_parent_priv(of->kn);
mbm_config_show(seq, r, QOS_L3_MBM_TOTAL_EVENT_ID); mbm_config_show(seq, r, QOS_L3_MBM_TOTAL_EVENT_ID);
@ -1680,7 +1690,7 @@ static int mbm_total_bytes_config_show(struct kernfs_open_file *of,
static int mbm_local_bytes_config_show(struct kernfs_open_file *of, static int mbm_local_bytes_config_show(struct kernfs_open_file *of,
struct seq_file *seq, void *v) struct seq_file *seq, void *v)
{ {
struct rdt_resource *r = of->kn->parent->priv; struct rdt_resource *r = rdt_kn_parent_priv(of->kn);
mbm_config_show(seq, r, QOS_L3_MBM_LOCAL_EVENT_ID); mbm_config_show(seq, r, QOS_L3_MBM_LOCAL_EVENT_ID);
@ -1787,7 +1797,7 @@ static ssize_t mbm_total_bytes_config_write(struct kernfs_open_file *of,
char *buf, size_t nbytes, char *buf, size_t nbytes,
loff_t off) loff_t off)
{ {
struct rdt_resource *r = of->kn->parent->priv; struct rdt_resource *r = rdt_kn_parent_priv(of->kn);
int ret; int ret;
/* Valid input requires a trailing newline */ /* Valid input requires a trailing newline */
@ -1813,7 +1823,7 @@ static ssize_t mbm_local_bytes_config_write(struct kernfs_open_file *of,
char *buf, size_t nbytes, char *buf, size_t nbytes,
loff_t off) loff_t off)
{ {
struct rdt_resource *r = of->kn->parent->priv; struct rdt_resource *r = rdt_kn_parent_priv(of->kn);
int ret; int ret;
/* Valid input requires a trailing newline */ /* Valid input requires a trailing newline */
@ -2513,12 +2523,13 @@ static struct rdtgroup *kernfs_to_rdtgroup(struct kernfs_node *kn)
* resource. "info" and its subdirectories don't * resource. "info" and its subdirectories don't
* have rdtgroup structures, so return NULL here. * have rdtgroup structures, so return NULL here.
*/ */
if (kn == kn_info || kn->parent == kn_info) if (kn == kn_info ||
rcu_access_pointer(kn->__parent) == kn_info)
return NULL; return NULL;
else else
return kn->priv; return kn->priv;
} else { } else {
return kn->parent->priv; return rdt_kn_parent_priv(kn);
} }
} }
@ -3752,7 +3763,7 @@ static int rdtgroup_mkdir_ctrl_mon(struct kernfs_node *parent_kn,
*/ */
static bool is_mon_groups(struct kernfs_node *kn, const char *name) static bool is_mon_groups(struct kernfs_node *kn, const char *name)
{ {
return (!strcmp(kn->name, "mon_groups") && return (!strcmp(rdt_kn_name(kn), "mon_groups") &&
strcmp(name, "mon_groups")); strcmp(name, "mon_groups"));
} }
@ -3867,9 +3878,18 @@ static int rdtgroup_rmdir_ctrl(struct rdtgroup *rdtgrp, cpumask_var_t tmpmask)
return 0; return 0;
} }
static struct kernfs_node *rdt_kn_parent(struct kernfs_node *kn)
{
/*
* Valid within the RCU section it was obtained or while rdtgroup_mutex
* is held.
*/
return rcu_dereference_check(kn->__parent, lockdep_is_held(&rdtgroup_mutex));
}
static int rdtgroup_rmdir(struct kernfs_node *kn) static int rdtgroup_rmdir(struct kernfs_node *kn)
{ {
struct kernfs_node *parent_kn = kn->parent; struct kernfs_node *parent_kn;
struct rdtgroup *rdtgrp; struct rdtgroup *rdtgrp;
cpumask_var_t tmpmask; cpumask_var_t tmpmask;
int ret = 0; int ret = 0;
@ -3882,6 +3902,7 @@ static int rdtgroup_rmdir(struct kernfs_node *kn)
ret = -EPERM; ret = -EPERM;
goto out; goto out;
} }
parent_kn = rdt_kn_parent(kn);
/* /*
* If the rdtgroup is a ctrl_mon group and parent directory * If the rdtgroup is a ctrl_mon group and parent directory
@ -3899,7 +3920,7 @@ static int rdtgroup_rmdir(struct kernfs_node *kn)
ret = rdtgroup_rmdir_ctrl(rdtgrp, tmpmask); ret = rdtgroup_rmdir_ctrl(rdtgrp, tmpmask);
} }
} else if (rdtgrp->type == RDTMON_GROUP && } else if (rdtgrp->type == RDTMON_GROUP &&
is_mon_groups(parent_kn, kn->name)) { is_mon_groups(parent_kn, rdt_kn_name(kn))) {
ret = rdtgroup_rmdir_mon(rdtgrp, tmpmask); ret = rdtgroup_rmdir_mon(rdtgrp, tmpmask);
} else { } else {
ret = -EPERM; ret = -EPERM;
@ -3950,6 +3971,7 @@ static void mongrp_reparent(struct rdtgroup *rdtgrp,
static int rdtgroup_rename(struct kernfs_node *kn, static int rdtgroup_rename(struct kernfs_node *kn,
struct kernfs_node *new_parent, const char *new_name) struct kernfs_node *new_parent, const char *new_name)
{ {
struct kernfs_node *kn_parent;
struct rdtgroup *new_prdtgrp; struct rdtgroup *new_prdtgrp;
struct rdtgroup *rdtgrp; struct rdtgroup *rdtgrp;
cpumask_var_t tmpmask; cpumask_var_t tmpmask;
@ -3984,8 +4006,9 @@ static int rdtgroup_rename(struct kernfs_node *kn,
goto out; goto out;
} }
if (rdtgrp->type != RDTMON_GROUP || !kn->parent || kn_parent = rdt_kn_parent(kn);
!is_mon_groups(kn->parent, kn->name)) { if (rdtgrp->type != RDTMON_GROUP || !kn_parent ||
!is_mon_groups(kn_parent, rdt_kn_name(kn))) {
rdt_last_cmd_puts("Source must be a MON group\n"); rdt_last_cmd_puts("Source must be a MON group\n");
ret = -EPERM; ret = -EPERM;
goto out; goto out;

View File

@ -368,7 +368,7 @@ static ssize_t max_power_store(struct device *dev,
} }
static ssize_t eeprom_read_handler(struct file *filp, struct kobject *kobj, static ssize_t eeprom_read_handler(struct file *filp, struct kobject *kobj,
struct bin_attribute *attr, char *buf, loff_t offset, const struct bin_attribute *attr, char *buf, loff_t offset,
size_t max_size) size_t max_size)
{ {
struct device *dev = kobj_to_dev(kobj); struct device *dev = kobj_to_dev(kobj);
@ -443,10 +443,10 @@ static DEVICE_ATTR_RO(security_enabled);
static DEVICE_ATTR_RO(module_id); static DEVICE_ATTR_RO(module_id);
static DEVICE_ATTR_RO(parent_device); static DEVICE_ATTR_RO(parent_device);
static struct bin_attribute bin_attr_eeprom = { static const struct bin_attribute bin_attr_eeprom = {
.attr = {.name = "eeprom", .mode = (0444)}, .attr = {.name = "eeprom", .mode = (0444)},
.size = PAGE_SIZE, .size = PAGE_SIZE,
.read = eeprom_read_handler .read_new = eeprom_read_handler
}; };
static struct attribute *hl_dev_attrs[] = { static struct attribute *hl_dev_attrs[] = {
@ -472,14 +472,14 @@ static struct attribute *hl_dev_attrs[] = {
NULL, NULL,
}; };
static struct bin_attribute *hl_dev_bin_attrs[] = { static const struct bin_attribute *const hl_dev_bin_attrs[] = {
&bin_attr_eeprom, &bin_attr_eeprom,
NULL NULL
}; };
static struct attribute_group hl_dev_attr_group = { static struct attribute_group hl_dev_attr_group = {
.attrs = hl_dev_attrs, .attrs = hl_dev_attrs,
.bin_attrs = hl_dev_bin_attrs, .bin_attrs_new = hl_dev_bin_attrs,
}; };
static struct attribute_group hl_dev_clks_attr_group; static struct attribute_group hl_dev_clks_attr_group;

View File

@ -1291,7 +1291,7 @@ EXPORT_SYMBOL_GPL(subsys_system_register);
* @groups: default attributes for the root device * @groups: default attributes for the root device
* *
* All 'virtual' subsystems have a /sys/devices/system/<name> root device * All 'virtual' subsystems have a /sys/devices/system/<name> root device
* with the name of the subystem. The root device can carry subsystem-wide * with the name of the subsystem. The root device can carry subsystem-wide
* attributes. All registered devices are below this single root device. * attributes. All registered devices are below this single root device.
* There's no restriction on device naming. This is for kernel software * There's no restriction on device naming. This is for kernel software
* constructs which need sysfs interface. * constructs which need sysfs interface.

View File

@ -87,17 +87,17 @@ static int component_devices_show(struct seq_file *s, void *data)
size_t i; size_t i;
mutex_lock(&component_mutex); mutex_lock(&component_mutex);
seq_printf(s, "%-40s %20s\n", "aggregate_device name", "status"); seq_printf(s, "%-50s %20s\n", "aggregate_device name", "status");
seq_puts(s, "-------------------------------------------------------------\n"); seq_puts(s, "-----------------------------------------------------------------------\n");
seq_printf(s, "%-40s %20s\n\n", seq_printf(s, "%-50s %20s\n\n",
dev_name(m->parent), m->bound ? "bound" : "not bound"); dev_name(m->parent), m->bound ? "bound" : "not bound");
seq_printf(s, "%-40s %20s\n", "device name", "status"); seq_printf(s, "%-50s %20s\n", "device name", "status");
seq_puts(s, "-------------------------------------------------------------\n"); seq_puts(s, "-----------------------------------------------------------------------\n");
for (i = 0; i < match->num; i++) { for (i = 0; i < match->num; i++) {
struct component *component = match->compare[i].component; struct component *component = match->compare[i].component;
seq_printf(s, "%-40s %20s\n", seq_printf(s, "%-50s %20s\n",
component ? dev_name(component->dev) : "(unknown)", component ? dev_name(component->dev) : "(unknown)",
component ? (component->bound ? "bound" : "not bound") : "not registered"); component ? (component->bound ? "bound" : "not bound") : "not registered");
} }

View File

@ -102,7 +102,9 @@ static void faux_device_release(struct device *dev)
* *
* Note, when this function is called, the functions specified in struct * Note, when this function is called, the functions specified in struct
* faux_ops can be called before the function returns, so be prepared for * faux_ops can be called before the function returns, so be prepared for
* everything to be properly initialized before that point in time. * everything to be properly initialized before that point in time. If the
* probe callback (if one is present) does NOT succeed, the creation of the
* device will fail and NULL will be returned.
* *
* Return: * Return:
* * NULL if an error happened with creating the device * * NULL if an error happened with creating the device
@ -147,6 +149,17 @@ struct faux_device *faux_device_create_with_groups(const char *name,
return NULL; return NULL;
} }
/*
* Verify that we did bind the driver to the device (i.e. probe worked),
* if not, let's fail the creation as trying to guess if probe was
* successful is almost impossible to determine by the caller.
*/
if (!dev->driver) {
dev_err(dev, "probe did not succeed, tearing down the device\n");
faux_device_destroy(faux_dev);
faux_dev = NULL;
}
return faux_dev; return faux_dev;
} }
EXPORT_SYMBOL_GPL(faux_device_create_with_groups); EXPORT_SYMBOL_GPL(faux_device_create_with_groups);

View File

@ -7,6 +7,7 @@
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/sysfs.h> #include <linux/sysfs.h>
#include <linux/string_choices.h>
#include "physical_location.h" #include "physical_location.h"
@ -116,7 +117,7 @@ static ssize_t dock_show(struct device *dev, struct device_attribute *attr,
char *buf) char *buf)
{ {
return sysfs_emit(buf, "%s\n", return sysfs_emit(buf, "%s\n",
dev->physical_location->dock ? "yes" : "no"); str_yes_no(dev->physical_location->dock));
} }
static DEVICE_ATTR_RO(dock); static DEVICE_ATTR_RO(dock);
@ -124,7 +125,7 @@ static ssize_t lid_show(struct device *dev, struct device_attribute *attr,
char *buf) char *buf)
{ {
return sysfs_emit(buf, "%s\n", return sysfs_emit(buf, "%s\n",
dev->physical_location->lid ? "yes" : "no"); str_yes_no(dev->physical_location->lid));
} }
static DEVICE_ATTR_RO(lid); static DEVICE_ATTR_RO(lid);

View File

@ -153,7 +153,7 @@ static int cxl_port_probe(struct device *dev)
} }
static ssize_t CDAT_read(struct file *filp, struct kobject *kobj, static ssize_t CDAT_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr, char *buf, const struct bin_attribute *bin_attr, char *buf,
loff_t offset, size_t count) loff_t offset, size_t count)
{ {
struct device *dev = kobj_to_dev(kobj); struct device *dev = kobj_to_dev(kobj);
@ -170,7 +170,7 @@ static ssize_t CDAT_read(struct file *filp, struct kobject *kobj,
port->cdat.length); port->cdat.length);
} }
static BIN_ATTR_ADMIN_RO(CDAT, 0); static const BIN_ATTR_ADMIN_RO(CDAT, 0);
static umode_t cxl_port_bin_attr_is_visible(struct kobject *kobj, static umode_t cxl_port_bin_attr_is_visible(struct kobject *kobj,
const struct bin_attribute *attr, int i) const struct bin_attribute *attr, int i)
@ -184,13 +184,13 @@ static umode_t cxl_port_bin_attr_is_visible(struct kobject *kobj,
return 0; return 0;
} }
static struct bin_attribute *cxl_cdat_bin_attributes[] = { static const struct bin_attribute *const cxl_cdat_bin_attributes[] = {
&bin_attr_CDAT, &bin_attr_CDAT,
NULL, NULL,
}; };
static struct attribute_group cxl_cdat_attribute_group = { static const struct attribute_group cxl_cdat_attribute_group = {
.bin_attrs = cxl_cdat_bin_attributes, .bin_attrs_new = cxl_cdat_bin_attributes,
.is_bin_visible = cxl_port_bin_attr_is_visible, .is_bin_visible = cxl_port_bin_attr_is_visible,
}; };

View File

@ -431,9 +431,9 @@ static ssize_t dmi_sel_raw_read_helper(struct dmi_sysfs_entry *entry,
} }
} }
static ssize_t dmi_sel_raw_read(struct file *filp, struct kobject *kobj, static ssize_t raw_event_log_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr, const struct bin_attribute *bin_attr,
char *buf, loff_t pos, size_t count) char *buf, loff_t pos, size_t count)
{ {
struct dmi_sysfs_entry *entry = to_entry(kobj->parent); struct dmi_sysfs_entry *entry = to_entry(kobj->parent);
struct dmi_read_state state = { struct dmi_read_state state = {
@ -445,10 +445,7 @@ static ssize_t dmi_sel_raw_read(struct file *filp, struct kobject *kobj,
return find_dmi_entry(entry, dmi_sel_raw_read_helper, &state); return find_dmi_entry(entry, dmi_sel_raw_read_helper, &state);
} }
static struct bin_attribute dmi_sel_raw_attr = { static const BIN_ATTR_ADMIN_RO(raw_event_log, 0);
.attr = {.name = "raw_event_log", .mode = 0400},
.read = dmi_sel_raw_read,
};
static int dmi_system_event_log(struct dmi_sysfs_entry *entry) static int dmi_system_event_log(struct dmi_sysfs_entry *entry)
{ {
@ -464,7 +461,7 @@ static int dmi_system_event_log(struct dmi_sysfs_entry *entry)
if (ret) if (ret)
goto out_free; goto out_free;
ret = sysfs_create_bin_file(entry->child, &dmi_sel_raw_attr); ret = sysfs_create_bin_file(entry->child, &bin_attr_raw_event_log);
if (ret) if (ret)
goto out_del; goto out_del;
@ -537,10 +534,10 @@ static ssize_t dmi_entry_raw_read_helper(struct dmi_sysfs_entry *entry,
&state->pos, dh, entry_length); &state->pos, dh, entry_length);
} }
static ssize_t dmi_entry_raw_read(struct file *filp, static ssize_t raw_read(struct file *filp,
struct kobject *kobj, struct kobject *kobj,
struct bin_attribute *bin_attr, const struct bin_attribute *bin_attr,
char *buf, loff_t pos, size_t count) char *buf, loff_t pos, size_t count)
{ {
struct dmi_sysfs_entry *entry = to_entry(kobj); struct dmi_sysfs_entry *entry = to_entry(kobj);
struct dmi_read_state state = { struct dmi_read_state state = {
@ -552,10 +549,7 @@ static ssize_t dmi_entry_raw_read(struct file *filp,
return find_dmi_entry(entry, dmi_entry_raw_read_helper, &state); return find_dmi_entry(entry, dmi_entry_raw_read_helper, &state);
} }
static const struct bin_attribute dmi_entry_raw_attr = { static const BIN_ATTR_ADMIN_RO(raw, 0);
.attr = {.name = "raw", .mode = 0400},
.read = dmi_entry_raw_read,
};
static void dmi_sysfs_entry_release(struct kobject *kobj) static void dmi_sysfs_entry_release(struct kobject *kobj)
{ {
@ -630,7 +624,7 @@ static void __init dmi_sysfs_register_handle(const struct dmi_header *dh,
goto out_err; goto out_err;
/* Create the raw binary file to access the entry */ /* Create the raw binary file to access the entry */
*ret = sysfs_create_bin_file(&entry->kobj, &dmi_entry_raw_attr); *ret = sysfs_create_bin_file(&entry->kobj, &bin_attr_raw);
if (*ret) if (*ret)
goto out_err; goto out_err;

View File

@ -761,8 +761,8 @@ static void __init dmi_scan_machine(void)
pr_info("DMI not present or invalid.\n"); pr_info("DMI not present or invalid.\n");
} }
static BIN_ATTR_SIMPLE_ADMIN_RO(smbios_entry_point); static __ro_after_init BIN_ATTR_SIMPLE_ADMIN_RO(smbios_entry_point);
static BIN_ATTR_SIMPLE_ADMIN_RO(DMI); static __ro_after_init BIN_ATTR_SIMPLE_ADMIN_RO(DMI);
static int __init dmi_init(void) static int __init dmi_init(void)
{ {

View File

@ -263,7 +263,7 @@ struct efi_mokvar_table_entry *efi_mokvar_entry_find(const char *name)
* amount of data in this mokvar config table entry. * amount of data in this mokvar config table entry.
*/ */
static ssize_t efi_mokvar_sysfs_read(struct file *file, struct kobject *kobj, static ssize_t efi_mokvar_sysfs_read(struct file *file, struct kobject *kobj,
struct bin_attribute *bin_attr, char *buf, const struct bin_attribute *bin_attr, char *buf,
loff_t off, size_t count) loff_t off, size_t count)
{ {
struct efi_mokvar_table_entry *mokvar_entry = bin_attr->private; struct efi_mokvar_table_entry *mokvar_entry = bin_attr->private;
@ -340,7 +340,7 @@ static int __init efi_mokvar_sysfs_init(void)
mokvar_sysfs->bin_attr.attr.name = mokvar_entry->name; mokvar_sysfs->bin_attr.attr.name = mokvar_entry->name;
mokvar_sysfs->bin_attr.attr.mode = 0400; mokvar_sysfs->bin_attr.attr.mode = 0400;
mokvar_sysfs->bin_attr.size = mokvar_entry->data_size; mokvar_sysfs->bin_attr.size = mokvar_entry->data_size;
mokvar_sysfs->bin_attr.read = efi_mokvar_sysfs_read; mokvar_sysfs->bin_attr.read_new = efi_mokvar_sysfs_read;
err = sysfs_create_bin_file(mokvar_kobj, err = sysfs_create_bin_file(mokvar_kobj,
&mokvar_sysfs->bin_attr); &mokvar_sysfs->bin_attr);

View File

@ -40,7 +40,7 @@ static u8 *rci2_base;
static u32 rci2_table_len; static u32 rci2_table_len;
unsigned long rci2_table_phys __ro_after_init = EFI_INVALID_TABLE_ADDR; unsigned long rci2_table_phys __ro_after_init = EFI_INVALID_TABLE_ADDR;
static BIN_ATTR_SIMPLE_ADMIN_RO(rci2); static __ro_after_init BIN_ATTR_SIMPLE_ADMIN_RO(rci2);
static u16 checksum(void) static u16 checksum(void)
{ {

View File

@ -460,7 +460,7 @@ static const struct kobj_type fw_cfg_sysfs_entry_ktype = {
/* raw-read method and attribute */ /* raw-read method and attribute */
static ssize_t fw_cfg_sysfs_read_raw(struct file *filp, struct kobject *kobj, static ssize_t fw_cfg_sysfs_read_raw(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr, const struct bin_attribute *bin_attr,
char *buf, loff_t pos, size_t count) char *buf, loff_t pos, size_t count)
{ {
struct fw_cfg_sysfs_entry *entry = to_entry(kobj); struct fw_cfg_sysfs_entry *entry = to_entry(kobj);
@ -474,9 +474,9 @@ static ssize_t fw_cfg_sysfs_read_raw(struct file *filp, struct kobject *kobj,
return fw_cfg_read_blob(entry->select, buf, pos, count); return fw_cfg_read_blob(entry->select, buf, pos, count);
} }
static struct bin_attribute fw_cfg_sysfs_attr_raw = { static const struct bin_attribute fw_cfg_sysfs_attr_raw = {
.attr = { .name = "raw", .mode = S_IRUSR }, .attr = { .name = "raw", .mode = S_IRUSR },
.read = fw_cfg_sysfs_read_raw, .read_new = fw_cfg_sysfs_read_raw,
}; };
/* /*

View File

@ -554,7 +554,7 @@ static unsigned long aligned_access_size(size_t offset, size_t count)
} }
static ssize_t fsi_slave_sysfs_raw_read(struct file *file, static ssize_t fsi_slave_sysfs_raw_read(struct file *file,
struct kobject *kobj, struct bin_attribute *attr, char *buf, struct kobject *kobj, const struct bin_attribute *attr, char *buf,
loff_t off, size_t count) loff_t off, size_t count)
{ {
struct fsi_slave *slave = to_fsi_slave(kobj_to_dev(kobj)); struct fsi_slave *slave = to_fsi_slave(kobj_to_dev(kobj));
@ -581,7 +581,7 @@ static ssize_t fsi_slave_sysfs_raw_read(struct file *file,
} }
static ssize_t fsi_slave_sysfs_raw_write(struct file *file, static ssize_t fsi_slave_sysfs_raw_write(struct file *file,
struct kobject *kobj, struct bin_attribute *attr, struct kobject *kobj, const struct bin_attribute *attr,
char *buf, loff_t off, size_t count) char *buf, loff_t off, size_t count)
{ {
struct fsi_slave *slave = to_fsi_slave(kobj_to_dev(kobj)); struct fsi_slave *slave = to_fsi_slave(kobj_to_dev(kobj));
@ -613,8 +613,8 @@ static const struct bin_attribute fsi_slave_raw_attr = {
.mode = 0600, .mode = 0600,
}, },
.size = 0, .size = 0,
.read = fsi_slave_sysfs_raw_read, .read_new = fsi_slave_sysfs_raw_read,
.write = fsi_slave_sysfs_raw_write, .write_new = fsi_slave_sysfs_raw_write,
}; };
static void fsi_slave_release(struct device *dev) static void fsi_slave_release(struct device *dev)

View File

@ -246,7 +246,7 @@ static void amdgpu_device_attr_sysfs_fini(struct amdgpu_device *adev)
} }
static ssize_t amdgpu_sysfs_reg_state_get(struct file *f, struct kobject *kobj, static ssize_t amdgpu_sysfs_reg_state_get(struct file *f, struct kobject *kobj,
struct bin_attribute *attr, char *buf, const struct bin_attribute *attr, char *buf,
loff_t ppos, size_t count) loff_t ppos, size_t count)
{ {
struct device *dev = kobj_to_dev(kobj); struct device *dev = kobj_to_dev(kobj);
@ -282,8 +282,8 @@ static ssize_t amdgpu_sysfs_reg_state_get(struct file *f, struct kobject *kobj,
return bytes_read; return bytes_read;
} }
BIN_ATTR(reg_state, 0444, amdgpu_sysfs_reg_state_get, NULL, static const BIN_ATTR(reg_state, 0444, amdgpu_sysfs_reg_state_get, NULL,
AMDGPU_SYS_REG_STATE_END); AMDGPU_SYS_REG_STATE_END);
int amdgpu_reg_state_sysfs_init(struct amdgpu_device *adev) int amdgpu_reg_state_sysfs_init(struct amdgpu_device *adev)
{ {

View File

@ -4025,7 +4025,7 @@ int is_psp_fw_valid(struct psp_bin_desc bin)
} }
static ssize_t amdgpu_psp_vbflash_write(struct file *filp, struct kobject *kobj, static ssize_t amdgpu_psp_vbflash_write(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr, const struct bin_attribute *bin_attr,
char *buffer, loff_t pos, size_t count) char *buffer, loff_t pos, size_t count)
{ {
struct device *dev = kobj_to_dev(kobj); struct device *dev = kobj_to_dev(kobj);
@ -4061,7 +4061,7 @@ static ssize_t amdgpu_psp_vbflash_write(struct file *filp, struct kobject *kobj,
} }
static ssize_t amdgpu_psp_vbflash_read(struct file *filp, struct kobject *kobj, static ssize_t amdgpu_psp_vbflash_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr, char *buffer, const struct bin_attribute *bin_attr, char *buffer,
loff_t pos, size_t count) loff_t pos, size_t count)
{ {
struct device *dev = kobj_to_dev(kobj); struct device *dev = kobj_to_dev(kobj);
@ -4113,11 +4113,11 @@ static ssize_t amdgpu_psp_vbflash_read(struct file *filp, struct kobject *kobj,
* Writing to this file will stage an IFWI for update. Reading from this file * Writing to this file will stage an IFWI for update. Reading from this file
* will trigger the update process. * will trigger the update process.
*/ */
static struct bin_attribute psp_vbflash_bin_attr = { static const struct bin_attribute psp_vbflash_bin_attr = {
.attr = {.name = "psp_vbflash", .mode = 0660}, .attr = {.name = "psp_vbflash", .mode = 0660},
.size = 0, .size = 0,
.write = amdgpu_psp_vbflash_write, .write_new = amdgpu_psp_vbflash_write,
.read = amdgpu_psp_vbflash_read, .read_new = amdgpu_psp_vbflash_read,
}; };
/** /**
@ -4144,7 +4144,7 @@ static ssize_t amdgpu_psp_vbflash_status(struct device *dev,
} }
static DEVICE_ATTR(psp_vbflash_status, 0440, amdgpu_psp_vbflash_status, NULL); static DEVICE_ATTR(psp_vbflash_status, 0440, amdgpu_psp_vbflash_status, NULL);
static struct bin_attribute *bin_flash_attrs[] = { static const struct bin_attribute *const bin_flash_attrs[] = {
&psp_vbflash_bin_attr, &psp_vbflash_bin_attr,
NULL NULL
}; };
@ -4180,7 +4180,7 @@ static umode_t amdgpu_bin_flash_attr_is_visible(struct kobject *kobj,
const struct attribute_group amdgpu_flash_attr_group = { const struct attribute_group amdgpu_flash_attr_group = {
.attrs = flash_attrs, .attrs = flash_attrs,
.bin_attrs = bin_flash_attrs, .bin_attrs_new = bin_flash_attrs,
.is_bin_visible = amdgpu_bin_flash_attr_is_visible, .is_bin_visible = amdgpu_bin_flash_attr_is_visible,
.is_visible = amdgpu_flash_attr_is_visible, .is_visible = amdgpu_flash_attr_is_visible,
}; };

View File

@ -1733,7 +1733,7 @@ static char *amdgpu_ras_badpage_flags_str(unsigned int flags)
*/ */
static ssize_t amdgpu_ras_sysfs_badpages_read(struct file *f, static ssize_t amdgpu_ras_sysfs_badpages_read(struct file *f,
struct kobject *kobj, struct bin_attribute *attr, struct kobject *kobj, const struct bin_attribute *attr,
char *buf, loff_t ppos, size_t count) char *buf, loff_t ppos, size_t count)
{ {
struct amdgpu_ras *con = struct amdgpu_ras *con =
@ -2068,8 +2068,8 @@ void amdgpu_ras_debugfs_create_all(struct amdgpu_device *adev)
/* debugfs end */ /* debugfs end */
/* ras fs */ /* ras fs */
static BIN_ATTR(gpu_vram_bad_pages, S_IRUGO, static const BIN_ATTR(gpu_vram_bad_pages, S_IRUGO,
amdgpu_ras_sysfs_badpages_read, NULL, 0); amdgpu_ras_sysfs_badpages_read, NULL, 0);
static DEVICE_ATTR(features, S_IRUGO, static DEVICE_ATTR(features, S_IRUGO,
amdgpu_ras_sysfs_features_read, NULL); amdgpu_ras_sysfs_features_read, NULL);
static DEVICE_ATTR(version, 0444, static DEVICE_ATTR(version, 0444,
@ -2091,7 +2091,7 @@ static int amdgpu_ras_fs_init(struct amdgpu_device *adev)
&con->event_state_attr.attr, &con->event_state_attr.attr,
NULL NULL
}; };
struct bin_attribute *bin_attrs[] = { const struct bin_attribute *bin_attrs[] = {
NULL, NULL,
NULL, NULL,
}; };
@ -2117,11 +2117,10 @@ static int amdgpu_ras_fs_init(struct amdgpu_device *adev)
if (amdgpu_bad_page_threshold != 0) { if (amdgpu_bad_page_threshold != 0) {
/* add bad_page_features entry */ /* add bad_page_features entry */
bin_attr_gpu_vram_bad_pages.private = NULL;
con->badpages_attr = bin_attr_gpu_vram_bad_pages; con->badpages_attr = bin_attr_gpu_vram_bad_pages;
sysfs_bin_attr_init(&con->badpages_attr);
bin_attrs[0] = &con->badpages_attr; bin_attrs[0] = &con->badpages_attr;
group.bin_attrs = bin_attrs; group.bin_attrs_new = bin_attrs;
sysfs_bin_attr_init(bin_attrs[0]);
} }
r = sysfs_create_group(&adev->dev->kobj, &group); r = sysfs_create_group(&adev->dev->kobj, &group);

View File

@ -597,7 +597,7 @@ static void update_config(void *handle, struct cp_psp_stream_config *config)
* incorrect/corrupted and we should correct our SRM by getting it from PSP * incorrect/corrupted and we should correct our SRM by getting it from PSP
*/ */
static ssize_t srm_data_write(struct file *filp, struct kobject *kobj, static ssize_t srm_data_write(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr, char *buffer, const struct bin_attribute *bin_attr, char *buffer,
loff_t pos, size_t count) loff_t pos, size_t count)
{ {
struct hdcp_workqueue *work; struct hdcp_workqueue *work;
@ -621,7 +621,7 @@ static ssize_t srm_data_write(struct file *filp, struct kobject *kobj,
} }
static ssize_t srm_data_read(struct file *filp, struct kobject *kobj, static ssize_t srm_data_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr, char *buffer, const struct bin_attribute *bin_attr, char *buffer,
loff_t pos, size_t count) loff_t pos, size_t count)
{ {
struct hdcp_workqueue *work; struct hdcp_workqueue *work;
@ -681,8 +681,8 @@ static ssize_t srm_data_read(struct file *filp, struct kobject *kobj,
static const struct bin_attribute data_attr = { static const struct bin_attribute data_attr = {
.attr = {.name = "hdcp_srm", .mode = 0664}, .attr = {.name = "hdcp_srm", .mode = 0664},
.size = PSP_HDCP_SRM_FIRST_GEN_MAX_SIZE, /* Limit SRM size */ .size = PSP_HDCP_SRM_FIRST_GEN_MAX_SIZE, /* Limit SRM size */
.write = srm_data_write, .write_new = srm_data_write,
.read = srm_data_read, .read_new = srm_data_read,
}; };
struct hdcp_workqueue *hdcp_create_workqueue(struct amdgpu_device *adev, struct hdcp_workqueue *hdcp_create_workqueue(struct amdgpu_device *adev,

View File

@ -261,7 +261,7 @@ static ssize_t enabled_show(struct device *device,
} }
static ssize_t edid_show(struct file *filp, struct kobject *kobj, static ssize_t edid_show(struct file *filp, struct kobject *kobj,
struct bin_attribute *attr, char *buf, loff_t off, const struct bin_attribute *attr, char *buf, loff_t off,
size_t count) size_t count)
{ {
struct device *connector_dev = kobj_to_dev(kobj); struct device *connector_dev = kobj_to_dev(kobj);
@ -315,21 +315,21 @@ static struct attribute *connector_dev_attrs[] = {
NULL NULL
}; };
static struct bin_attribute edid_attr = { static const struct bin_attribute edid_attr = {
.attr.name = "edid", .attr.name = "edid",
.attr.mode = 0444, .attr.mode = 0444,
.size = 0, .size = 0,
.read = edid_show, .read_new = edid_show,
}; };
static struct bin_attribute *connector_bin_attrs[] = { static const struct bin_attribute *const connector_bin_attrs[] = {
&edid_attr, &edid_attr,
NULL NULL
}; };
static const struct attribute_group connector_dev_group = { static const struct attribute_group connector_dev_group = {
.attrs = connector_dev_attrs, .attrs = connector_dev_attrs,
.bin_attrs = connector_bin_attrs, .bin_attrs_new = connector_bin_attrs,
}; };
static const struct attribute_group *connector_dev_groups[] = { static const struct attribute_group *connector_dev_groups[] = {

View File

@ -2490,7 +2490,7 @@ void i915_gpu_error_debugfs_register(struct drm_i915_private *i915)
} }
static ssize_t error_state_read(struct file *filp, struct kobject *kobj, static ssize_t error_state_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *attr, char *buf, const struct bin_attribute *attr, char *buf,
loff_t off, size_t count) loff_t off, size_t count)
{ {
@ -2526,7 +2526,7 @@ static ssize_t error_state_read(struct file *filp, struct kobject *kobj,
} }
static ssize_t error_state_write(struct file *file, struct kobject *kobj, static ssize_t error_state_write(struct file *file, struct kobject *kobj,
struct bin_attribute *attr, char *buf, const struct bin_attribute *attr, char *buf,
loff_t off, size_t count) loff_t off, size_t count)
{ {
struct device *kdev = kobj_to_dev(kobj); struct device *kdev = kobj_to_dev(kobj);
@ -2542,8 +2542,8 @@ static const struct bin_attribute error_state_attr = {
.attr.name = "error", .attr.name = "error",
.attr.mode = S_IRUSR | S_IWUSR, .attr.mode = S_IRUSR | S_IWUSR,
.size = 0, .size = 0,
.read = error_state_read, .read_new = error_state_read,
.write = error_state_write, .write_new = error_state_write,
}; };
void i915_gpu_error_sysfs_setup(struct drm_i915_private *i915) void i915_gpu_error_sysfs_setup(struct drm_i915_private *i915)

View File

@ -60,7 +60,7 @@ static int l3_access_valid(struct drm_i915_private *i915, loff_t offset)
static ssize_t static ssize_t
i915_l3_read(struct file *filp, struct kobject *kobj, i915_l3_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *attr, char *buf, const struct bin_attribute *attr, char *buf,
loff_t offset, size_t count) loff_t offset, size_t count)
{ {
struct device *kdev = kobj_to_dev(kobj); struct device *kdev = kobj_to_dev(kobj);
@ -88,7 +88,7 @@ i915_l3_read(struct file *filp, struct kobject *kobj,
static ssize_t static ssize_t
i915_l3_write(struct file *filp, struct kobject *kobj, i915_l3_write(struct file *filp, struct kobject *kobj,
struct bin_attribute *attr, char *buf, const struct bin_attribute *attr, char *buf,
loff_t offset, size_t count) loff_t offset, size_t count)
{ {
struct device *kdev = kobj_to_dev(kobj); struct device *kdev = kobj_to_dev(kobj);
@ -140,8 +140,8 @@ i915_l3_write(struct file *filp, struct kobject *kobj,
static const struct bin_attribute dpf_attrs = { static const struct bin_attribute dpf_attrs = {
.attr = {.name = "l3_parity", .mode = (S_IRUSR | S_IWUSR)}, .attr = {.name = "l3_parity", .mode = (S_IRUSR | S_IWUSR)},
.size = GEN7_L3LOG_SIZE, .size = GEN7_L3LOG_SIZE,
.read = i915_l3_read, .read_new = i915_l3_read,
.write = i915_l3_write, .write_new = i915_l3_write,
.mmap = NULL, .mmap = NULL,
.private = (void *)0 .private = (void *)0
}; };
@ -149,8 +149,8 @@ static const struct bin_attribute dpf_attrs = {
static const struct bin_attribute dpf_attrs_1 = { static const struct bin_attribute dpf_attrs_1 = {
.attr = {.name = "l3_parity_slice_1", .mode = (S_IRUSR | S_IWUSR)}, .attr = {.name = "l3_parity_slice_1", .mode = (S_IRUSR | S_IWUSR)},
.size = GEN7_L3LOG_SIZE, .size = GEN7_L3LOG_SIZE,
.read = i915_l3_read, .read_new = i915_l3_read,
.write = i915_l3_write, .write_new = i915_l3_write,
.mmap = NULL, .mmap = NULL,
.private = (void *)1 .private = (void *)1
}; };

View File

@ -310,7 +310,7 @@ static bool lima_read_block(struct lima_block_reader *reader,
} }
static ssize_t lima_error_state_read(struct file *filp, struct kobject *kobj, static ssize_t lima_error_state_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *attr, char *buf, const struct bin_attribute *attr, char *buf,
loff_t off, size_t count) loff_t off, size_t count)
{ {
struct device *dev = kobj_to_dev(kobj); struct device *dev = kobj_to_dev(kobj);
@ -336,7 +336,7 @@ static ssize_t lima_error_state_read(struct file *filp, struct kobject *kobj,
} }
static ssize_t lima_error_state_write(struct file *file, struct kobject *kobj, static ssize_t lima_error_state_write(struct file *file, struct kobject *kobj,
struct bin_attribute *attr, char *buf, const struct bin_attribute *attr, char *buf,
loff_t off, size_t count) loff_t off, size_t count)
{ {
struct device *dev = kobj_to_dev(kobj); struct device *dev = kobj_to_dev(kobj);
@ -362,8 +362,8 @@ static const struct bin_attribute lima_error_state_attr = {
.attr.name = "error", .attr.name = "error",
.attr.mode = 0600, .attr.mode = 0600,
.size = 0, .size = 0,
.read = lima_error_state_read, .read_new = lima_error_state_read,
.write = lima_error_state_write, .write_new = lima_error_state_write,
}; };
static int lima_pdev_probe(struct platform_device *pdev) static int lima_pdev_probe(struct platform_device *pdev)

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 // SPDX-License-Identifier: GPL-2.0
use kernel::{bindings, c_str, pci, prelude::*}; use kernel::{bindings, c_str, device::Core, pci, prelude::*};
use crate::gpu::Gpu; use crate::gpu::Gpu;
@ -27,7 +27,7 @@ impl pci::Driver for NovaCore {
type IdInfo = (); type IdInfo = ();
const ID_TABLE: pci::IdTable<Self::IdInfo> = &PCI_TABLE; const ID_TABLE: pci::IdTable<Self::IdInfo> = &PCI_TABLE;
fn probe(pdev: &mut pci::Device, _info: &Self::IdInfo) -> Result<Pin<KBox<Self>>> { fn probe(pdev: &pci::Device<Core>, _info: &Self::IdInfo) -> Result<Pin<KBox<Self>>> {
dev_dbg!(pdev.as_ref(), "Probe Nova Core GPU driver.\n"); dev_dbg!(pdev.as_ref(), "Probe Nova Core GPU driver.\n");
pdev.enable_device_mem()?; pdev.enable_device_mem()?;

View File

@ -35,7 +35,7 @@
impl Boot0 { impl Boot0 {
#[inline] #[inline]
pub(crate) fn read(bar: &Bar0) -> Self { pub(crate) fn read(bar: &Bar0) -> Self {
Self(bar.readl(BOOT0_OFFSET)) Self(bar.read32(BOOT0_OFFSET))
} }
#[inline] #[inline]

View File

@ -673,7 +673,7 @@ static void goodix_berlin_power_off_act(void *data)
} }
static ssize_t registers_read(struct file *filp, struct kobject *kobj, static ssize_t registers_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr, const struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count) char *buf, loff_t off, size_t count)
{ {
struct device *dev = kobj_to_dev(kobj); struct device *dev = kobj_to_dev(kobj);
@ -686,7 +686,7 @@ static ssize_t registers_read(struct file *filp, struct kobject *kobj,
} }
static ssize_t registers_write(struct file *filp, struct kobject *kobj, static ssize_t registers_write(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr, const struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count) char *buf, loff_t off, size_t count)
{ {
struct device *dev = kobj_to_dev(kobj); struct device *dev = kobj_to_dev(kobj);
@ -698,15 +698,15 @@ static ssize_t registers_write(struct file *filp, struct kobject *kobj,
return error ? error : count; return error ? error : count;
} }
static BIN_ATTR_ADMIN_RW(registers, 0); static const BIN_ATTR_ADMIN_RW(registers, 0);
static struct bin_attribute *goodix_berlin_bin_attrs[] = { static const struct bin_attribute *const goodix_berlin_bin_attrs[] = {
&bin_attr_registers, &bin_attr_registers,
NULL, NULL,
}; };
static const struct attribute_group goodix_berlin_attr_group = { static const struct attribute_group goodix_berlin_attr_group = {
.bin_attrs = goodix_berlin_bin_attrs, .bin_attrs_new = goodix_berlin_bin_attrs,
}; };
const struct attribute_group *goodix_berlin_groups[] = { const struct attribute_group *goodix_berlin_groups[] = {

View File

@ -1540,7 +1540,7 @@ static ssize_t pccard_extract_cis(struct pcmcia_socket *s, char *buf,
static ssize_t pccard_show_cis(struct file *filp, struct kobject *kobj, static ssize_t pccard_show_cis(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr, const struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count) char *buf, loff_t off, size_t count)
{ {
unsigned int size = 0x200; unsigned int size = 0x200;
@ -1571,7 +1571,7 @@ static ssize_t pccard_show_cis(struct file *filp, struct kobject *kobj,
static ssize_t pccard_store_cis(struct file *filp, struct kobject *kobj, static ssize_t pccard_store_cis(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr, const struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count) char *buf, loff_t off, size_t count)
{ {
struct pcmcia_socket *s; struct pcmcia_socket *s;
@ -1605,6 +1605,6 @@ static ssize_t pccard_store_cis(struct file *filp, struct kobject *kobj,
const struct bin_attribute pccard_cis_attr = { const struct bin_attribute pccard_cis_attr = {
.attr = { .name = "cis", .mode = S_IRUGO | S_IWUSR }, .attr = { .name = "cis", .mode = S_IRUGO | S_IWUSR },
.size = 0x200, .size = 0x200,
.read = pccard_show_cis, .read_new = pccard_show_cis,
.write = pccard_store_cis, .write_new = pccard_store_cis,
}; };

View File

@ -114,7 +114,7 @@ static struct attribute *rio_dev_attrs[] = {
static ssize_t static ssize_t
rio_read_config(struct file *filp, struct kobject *kobj, rio_read_config(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr, const struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count) char *buf, loff_t off, size_t count)
{ {
struct rio_dev *dev = to_rio_dev(kobj_to_dev(kobj)); struct rio_dev *dev = to_rio_dev(kobj_to_dev(kobj));
@ -185,7 +185,7 @@ rio_read_config(struct file *filp, struct kobject *kobj,
static ssize_t static ssize_t
rio_write_config(struct file *filp, struct kobject *kobj, rio_write_config(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr, const struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count) char *buf, loff_t off, size_t count)
{ {
struct rio_dev *dev = to_rio_dev(kobj_to_dev(kobj)); struct rio_dev *dev = to_rio_dev(kobj_to_dev(kobj));
@ -241,17 +241,17 @@ rio_write_config(struct file *filp, struct kobject *kobj,
return count; return count;
} }
static struct bin_attribute rio_config_attr = { static const struct bin_attribute rio_config_attr = {
.attr = { .attr = {
.name = "config", .name = "config",
.mode = S_IRUGO | S_IWUSR, .mode = S_IRUGO | S_IWUSR,
}, },
.size = RIO_MAINT_SPACE_SZ, .size = RIO_MAINT_SPACE_SZ,
.read = rio_read_config, .read_new = rio_read_config,
.write = rio_write_config, .write_new = rio_write_config,
}; };
static struct bin_attribute *rio_dev_bin_attrs[] = { static const struct bin_attribute *const rio_dev_bin_attrs[] = {
&rio_config_attr, &rio_config_attr,
NULL, NULL,
}; };
@ -278,7 +278,7 @@ static umode_t rio_dev_is_attr_visible(struct kobject *kobj,
static const struct attribute_group rio_dev_group = { static const struct attribute_group rio_dev_group = {
.attrs = rio_dev_attrs, .attrs = rio_dev_attrs,
.is_visible = rio_dev_is_attr_visible, .is_visible = rio_dev_is_attr_visible,
.bin_attrs = rio_dev_bin_attrs, .bin_attrs_new = rio_dev_bin_attrs,
}; };
const struct attribute_group *rio_dev_groups[] = { const struct attribute_group *rio_dev_groups[] = {

View File

@ -17,7 +17,7 @@
#include "kernfs-internal.h" #include "kernfs-internal.h"
static DEFINE_RWLOCK(kernfs_rename_lock); /* kn->parent and ->name */ DEFINE_RWLOCK(kernfs_rename_lock); /* kn->parent and ->name */
/* /*
* Don't use rename_lock to piggy back on pr_cont_buf. We don't want to * Don't use rename_lock to piggy back on pr_cont_buf. We don't want to
* call pr_cont() while holding rename_lock. Because sometimes pr_cont() * call pr_cont() while holding rename_lock. Because sometimes pr_cont()
@ -51,22 +51,14 @@ static bool kernfs_lockdep(struct kernfs_node *kn)
#endif #endif
} }
static int kernfs_name_locked(struct kernfs_node *kn, char *buf, size_t buflen)
{
if (!kn)
return strscpy(buf, "(null)", buflen);
return strscpy(buf, kn->parent ? kn->name : "/", buflen);
}
/* kernfs_node_depth - compute depth from @from to @to */ /* kernfs_node_depth - compute depth from @from to @to */
static size_t kernfs_depth(struct kernfs_node *from, struct kernfs_node *to) static size_t kernfs_depth(struct kernfs_node *from, struct kernfs_node *to)
{ {
size_t depth = 0; size_t depth = 0;
while (to->parent && to != from) { while (rcu_dereference(to->__parent) && to != from) {
depth++; depth++;
to = to->parent; to = rcu_dereference(to->__parent);
} }
return depth; return depth;
} }
@ -84,18 +76,18 @@ static struct kernfs_node *kernfs_common_ancestor(struct kernfs_node *a,
db = kernfs_depth(rb->kn, b); db = kernfs_depth(rb->kn, b);
while (da > db) { while (da > db) {
a = a->parent; a = rcu_dereference(a->__parent);
da--; da--;
} }
while (db > da) { while (db > da) {
b = b->parent; b = rcu_dereference(b->__parent);
db--; db--;
} }
/* worst case b and a will be the same at root */ /* worst case b and a will be the same at root */
while (b != a) { while (b != a) {
b = b->parent; b = rcu_dereference(b->__parent);
a = a->parent; a = rcu_dereference(a->__parent);
} }
return a; return a;
@ -168,10 +160,13 @@ static int kernfs_path_from_node_locked(struct kernfs_node *kn_to,
/* Calculate how many bytes we need for the rest */ /* Calculate how many bytes we need for the rest */
for (i = depth_to - 1; i >= 0; i--) { for (i = depth_to - 1; i >= 0; i--) {
for (kn = kn_to, j = 0; j < i; j++) const char *name;
kn = kn->parent;
len += scnprintf(buf + len, buflen - len, "/%s", kn->name); for (kn = kn_to, j = 0; j < i; j++)
kn = rcu_dereference(kn->__parent);
name = rcu_dereference(kn->name);
len += scnprintf(buf + len, buflen - len, "/%s", name);
} }
return len; return len;
@ -195,13 +190,18 @@ static int kernfs_path_from_node_locked(struct kernfs_node *kn_to,
*/ */
int kernfs_name(struct kernfs_node *kn, char *buf, size_t buflen) int kernfs_name(struct kernfs_node *kn, char *buf, size_t buflen)
{ {
unsigned long flags; struct kernfs_node *kn_parent;
int ret;
read_lock_irqsave(&kernfs_rename_lock, flags); if (!kn)
ret = kernfs_name_locked(kn, buf, buflen); return strscpy(buf, "(null)", buflen);
read_unlock_irqrestore(&kernfs_rename_lock, flags);
return ret; guard(rcu)();
/*
* KERNFS_ROOT_INVARIANT_PARENT is ignored here. The name is RCU freed and
* the parent is either existing or not.
*/
kn_parent = rcu_dereference(kn->__parent);
return strscpy(buf, kn_parent ? rcu_dereference(kn->name) : "/", buflen);
} }
/** /**
@ -223,13 +223,17 @@ int kernfs_name(struct kernfs_node *kn, char *buf, size_t buflen)
int kernfs_path_from_node(struct kernfs_node *to, struct kernfs_node *from, int kernfs_path_from_node(struct kernfs_node *to, struct kernfs_node *from,
char *buf, size_t buflen) char *buf, size_t buflen)
{ {
unsigned long flags; struct kernfs_root *root;
int ret;
read_lock_irqsave(&kernfs_rename_lock, flags); guard(rcu)();
ret = kernfs_path_from_node_locked(to, from, buf, buflen); if (to) {
read_unlock_irqrestore(&kernfs_rename_lock, flags); root = kernfs_root(to);
return ret; if (!(root->flags & KERNFS_ROOT_INVARIANT_PARENT)) {
guard(read_lock_irqsave)(&kernfs_rename_lock);
return kernfs_path_from_node_locked(to, from, buf, buflen);
}
}
return kernfs_path_from_node_locked(to, from, buf, buflen);
} }
EXPORT_SYMBOL_GPL(kernfs_path_from_node); EXPORT_SYMBOL_GPL(kernfs_path_from_node);
@ -295,7 +299,7 @@ struct kernfs_node *kernfs_get_parent(struct kernfs_node *kn)
unsigned long flags; unsigned long flags;
read_lock_irqsave(&kernfs_rename_lock, flags); read_lock_irqsave(&kernfs_rename_lock, flags);
parent = kn->parent; parent = kernfs_parent(kn);
kernfs_get(parent); kernfs_get(parent);
read_unlock_irqrestore(&kernfs_rename_lock, flags); read_unlock_irqrestore(&kernfs_rename_lock, flags);
@ -336,13 +340,13 @@ static int kernfs_name_compare(unsigned int hash, const char *name,
return -1; return -1;
if (ns > kn->ns) if (ns > kn->ns)
return 1; return 1;
return strcmp(name, kn->name); return strcmp(name, kernfs_rcu_name(kn));
} }
static int kernfs_sd_compare(const struct kernfs_node *left, static int kernfs_sd_compare(const struct kernfs_node *left,
const struct kernfs_node *right) const struct kernfs_node *right)
{ {
return kernfs_name_compare(left->hash, left->name, left->ns, right); return kernfs_name_compare(left->hash, kernfs_rcu_name(left), left->ns, right);
} }
/** /**
@ -360,8 +364,12 @@ static int kernfs_sd_compare(const struct kernfs_node *left,
*/ */
static int kernfs_link_sibling(struct kernfs_node *kn) static int kernfs_link_sibling(struct kernfs_node *kn)
{ {
struct rb_node **node = &kn->parent->dir.children.rb_node;
struct rb_node *parent = NULL; struct rb_node *parent = NULL;
struct kernfs_node *kn_parent;
struct rb_node **node;
kn_parent = kernfs_parent(kn);
node = &kn_parent->dir.children.rb_node;
while (*node) { while (*node) {
struct kernfs_node *pos; struct kernfs_node *pos;
@ -380,13 +388,13 @@ static int kernfs_link_sibling(struct kernfs_node *kn)
/* add new node and rebalance the tree */ /* add new node and rebalance the tree */
rb_link_node(&kn->rb, parent, node); rb_link_node(&kn->rb, parent, node);
rb_insert_color(&kn->rb, &kn->parent->dir.children); rb_insert_color(&kn->rb, &kn_parent->dir.children);
/* successfully added, account subdir number */ /* successfully added, account subdir number */
down_write(&kernfs_root(kn)->kernfs_iattr_rwsem); down_write(&kernfs_root(kn)->kernfs_iattr_rwsem);
if (kernfs_type(kn) == KERNFS_DIR) if (kernfs_type(kn) == KERNFS_DIR)
kn->parent->dir.subdirs++; kn_parent->dir.subdirs++;
kernfs_inc_rev(kn->parent); kernfs_inc_rev(kn_parent);
up_write(&kernfs_root(kn)->kernfs_iattr_rwsem); up_write(&kernfs_root(kn)->kernfs_iattr_rwsem);
return 0; return 0;
@ -407,16 +415,19 @@ static int kernfs_link_sibling(struct kernfs_node *kn)
*/ */
static bool kernfs_unlink_sibling(struct kernfs_node *kn) static bool kernfs_unlink_sibling(struct kernfs_node *kn)
{ {
struct kernfs_node *kn_parent;
if (RB_EMPTY_NODE(&kn->rb)) if (RB_EMPTY_NODE(&kn->rb))
return false; return false;
kn_parent = kernfs_parent(kn);
down_write(&kernfs_root(kn)->kernfs_iattr_rwsem); down_write(&kernfs_root(kn)->kernfs_iattr_rwsem);
if (kernfs_type(kn) == KERNFS_DIR) if (kernfs_type(kn) == KERNFS_DIR)
kn->parent->dir.subdirs--; kn_parent->dir.subdirs--;
kernfs_inc_rev(kn->parent); kernfs_inc_rev(kn_parent);
up_write(&kernfs_root(kn)->kernfs_iattr_rwsem); up_write(&kernfs_root(kn)->kernfs_iattr_rwsem);
rb_erase(&kn->rb, &kn->parent->dir.children); rb_erase(&kn->rb, &kn_parent->dir.children);
RB_CLEAR_NODE(&kn->rb); RB_CLEAR_NODE(&kn->rb);
return true; return true;
} }
@ -533,7 +544,8 @@ static void kernfs_free_rcu(struct rcu_head *rcu)
{ {
struct kernfs_node *kn = container_of(rcu, struct kernfs_node, rcu); struct kernfs_node *kn = container_of(rcu, struct kernfs_node, rcu);
kfree_const(kn->name); /* If the whole node goes away, then name can't be used outside */
kfree_const(rcu_access_pointer(kn->name));
if (kn->iattr) { if (kn->iattr) {
simple_xattrs_free(&kn->iattr->xattrs, NULL); simple_xattrs_free(&kn->iattr->xattrs, NULL);
@ -562,11 +574,12 @@ void kernfs_put(struct kernfs_node *kn)
* Moving/renaming is always done while holding reference. * Moving/renaming is always done while holding reference.
* kn->parent won't change beneath us. * kn->parent won't change beneath us.
*/ */
parent = kn->parent; parent = kernfs_parent(kn);
WARN_ONCE(atomic_read(&kn->active) != KN_DEACTIVATED_BIAS, WARN_ONCE(atomic_read(&kn->active) != KN_DEACTIVATED_BIAS,
"kernfs_put: %s/%s: released with incorrect active_ref %d\n", "kernfs_put: %s/%s: released with incorrect active_ref %d\n",
parent ? parent->name : "", kn->name, atomic_read(&kn->active)); parent ? rcu_dereference(parent->name) : "",
rcu_dereference(kn->name), atomic_read(&kn->active));
if (kernfs_type(kn) == KERNFS_LINK) if (kernfs_type(kn) == KERNFS_LINK)
kernfs_put(kn->symlink.target_kn); kernfs_put(kn->symlink.target_kn);
@ -643,7 +656,7 @@ static struct kernfs_node *__kernfs_new_node(struct kernfs_root *root,
atomic_set(&kn->active, KN_DEACTIVATED_BIAS); atomic_set(&kn->active, KN_DEACTIVATED_BIAS);
RB_CLEAR_NODE(&kn->rb); RB_CLEAR_NODE(&kn->rb);
kn->name = name; rcu_assign_pointer(kn->name, name);
kn->mode = mode; kn->mode = mode;
kn->flags = flags; kn->flags = flags;
@ -701,7 +714,7 @@ struct kernfs_node *kernfs_new_node(struct kernfs_node *parent,
name, mode, uid, gid, flags); name, mode, uid, gid, flags);
if (kn) { if (kn) {
kernfs_get(parent); kernfs_get(parent);
kn->parent = parent; rcu_assign_pointer(kn->__parent, parent);
} }
return kn; return kn;
} }
@ -769,18 +782,20 @@ struct kernfs_node *kernfs_find_and_get_node_by_id(struct kernfs_root *root,
*/ */
int kernfs_add_one(struct kernfs_node *kn) int kernfs_add_one(struct kernfs_node *kn)
{ {
struct kernfs_node *parent = kn->parent; struct kernfs_root *root = kernfs_root(kn);
struct kernfs_root *root = kernfs_root(parent);
struct kernfs_iattrs *ps_iattr; struct kernfs_iattrs *ps_iattr;
struct kernfs_node *parent;
bool has_ns; bool has_ns;
int ret; int ret;
down_write(&root->kernfs_rwsem); down_write(&root->kernfs_rwsem);
parent = kernfs_parent(kn);
ret = -EINVAL; ret = -EINVAL;
has_ns = kernfs_ns_enabled(parent); has_ns = kernfs_ns_enabled(parent);
if (WARN(has_ns != (bool)kn->ns, KERN_WARNING "kernfs: ns %s in '%s' for '%s'\n", if (WARN(has_ns != (bool)kn->ns, KERN_WARNING "kernfs: ns %s in '%s' for '%s'\n",
has_ns ? "required" : "invalid", parent->name, kn->name)) has_ns ? "required" : "invalid",
kernfs_rcu_name(parent), kernfs_rcu_name(kn)))
goto out_unlock; goto out_unlock;
if (kernfs_type(parent) != KERNFS_DIR) if (kernfs_type(parent) != KERNFS_DIR)
@ -790,7 +805,7 @@ int kernfs_add_one(struct kernfs_node *kn)
if (parent->flags & (KERNFS_REMOVING | KERNFS_EMPTY_DIR)) if (parent->flags & (KERNFS_REMOVING | KERNFS_EMPTY_DIR))
goto out_unlock; goto out_unlock;
kn->hash = kernfs_name_hash(kn->name, kn->ns); kn->hash = kernfs_name_hash(kernfs_rcu_name(kn), kn->ns);
ret = kernfs_link_sibling(kn); ret = kernfs_link_sibling(kn);
if (ret) if (ret)
@ -846,7 +861,7 @@ static struct kernfs_node *kernfs_find_ns(struct kernfs_node *parent,
if (has_ns != (bool)ns) { if (has_ns != (bool)ns) {
WARN(1, KERN_WARNING "kernfs: ns %s in '%s' for '%s'\n", WARN(1, KERN_WARNING "kernfs: ns %s in '%s' for '%s'\n",
has_ns ? "required" : "invalid", parent->name, name); has_ns ? "required" : "invalid", kernfs_rcu_name(parent), name);
return NULL; return NULL;
} }
@ -949,6 +964,11 @@ struct kernfs_node *kernfs_walk_and_get_ns(struct kernfs_node *parent,
return kn; return kn;
} }
unsigned int kernfs_root_flags(struct kernfs_node *kn)
{
return kernfs_root(kn)->flags;
}
/** /**
* kernfs_create_root - create a new kernfs hierarchy * kernfs_create_root - create a new kernfs hierarchy
* @scops: optional syscall operations for the hierarchy * @scops: optional syscall operations for the hierarchy
@ -1112,7 +1132,7 @@ struct kernfs_node *kernfs_create_empty_dir(struct kernfs_node *parent,
static int kernfs_dop_revalidate(struct inode *dir, const struct qstr *name, static int kernfs_dop_revalidate(struct inode *dir, const struct qstr *name,
struct dentry *dentry, unsigned int flags) struct dentry *dentry, unsigned int flags)
{ {
struct kernfs_node *kn; struct kernfs_node *kn, *parent;
struct kernfs_root *root; struct kernfs_root *root;
if (flags & LOOKUP_RCU) if (flags & LOOKUP_RCU)
@ -1120,8 +1140,6 @@ static int kernfs_dop_revalidate(struct inode *dir, const struct qstr *name,
/* Negative hashed dentry? */ /* Negative hashed dentry? */
if (d_really_is_negative(dentry)) { if (d_really_is_negative(dentry)) {
struct kernfs_node *parent;
/* If the kernfs parent node has changed discard and /* If the kernfs parent node has changed discard and
* proceed to ->lookup. * proceed to ->lookup.
* *
@ -1163,16 +1181,17 @@ static int kernfs_dop_revalidate(struct inode *dir, const struct qstr *name,
if (!kernfs_active(kn)) if (!kernfs_active(kn))
goto out_bad; goto out_bad;
parent = kernfs_parent(kn);
/* The kernfs node has been moved? */ /* The kernfs node has been moved? */
if (kernfs_dentry_node(dentry->d_parent) != kn->parent) if (kernfs_dentry_node(dentry->d_parent) != parent)
goto out_bad; goto out_bad;
/* The kernfs node has been renamed */ /* The kernfs node has been renamed */
if (strcmp(dentry->d_name.name, kn->name) != 0) if (strcmp(dentry->d_name.name, kernfs_rcu_name(kn)) != 0)
goto out_bad; goto out_bad;
/* The kernfs node has been moved to a different namespace */ /* The kernfs node has been moved to a different namespace */
if (kn->parent && kernfs_ns_enabled(kn->parent) && if (parent && kernfs_ns_enabled(parent) &&
kernfs_info(dentry->d_sb)->ns != kn->ns) kernfs_info(dentry->d_sb)->ns != kn->ns)
goto out_bad; goto out_bad;
@ -1365,7 +1384,7 @@ static struct kernfs_node *kernfs_next_descendant_post(struct kernfs_node *pos,
return kernfs_leftmost_descendant(rb_to_kn(rbn)); return kernfs_leftmost_descendant(rb_to_kn(rbn));
/* no sibling left, visit parent */ /* no sibling left, visit parent */
return pos->parent; return kernfs_parent(pos);
} }
static void kernfs_activate_one(struct kernfs_node *kn) static void kernfs_activate_one(struct kernfs_node *kn)
@ -1377,7 +1396,7 @@ static void kernfs_activate_one(struct kernfs_node *kn)
if (kernfs_active(kn) || (kn->flags & (KERNFS_HIDDEN | KERNFS_REMOVING))) if (kernfs_active(kn) || (kn->flags & (KERNFS_HIDDEN | KERNFS_REMOVING)))
return; return;
WARN_ON_ONCE(kn->parent && RB_EMPTY_NODE(&kn->rb)); WARN_ON_ONCE(rcu_access_pointer(kn->__parent) && RB_EMPTY_NODE(&kn->rb));
WARN_ON_ONCE(atomic_read(&kn->active) != KN_DEACTIVATED_BIAS); WARN_ON_ONCE(atomic_read(&kn->active) != KN_DEACTIVATED_BIAS);
atomic_sub(KN_DEACTIVATED_BIAS, &kn->active); atomic_sub(KN_DEACTIVATED_BIAS, &kn->active);
@ -1447,7 +1466,7 @@ void kernfs_show(struct kernfs_node *kn, bool show)
static void __kernfs_remove(struct kernfs_node *kn) static void __kernfs_remove(struct kernfs_node *kn)
{ {
struct kernfs_node *pos; struct kernfs_node *pos, *parent;
/* Short-circuit if non-root @kn has already finished removal. */ /* Short-circuit if non-root @kn has already finished removal. */
if (!kn) if (!kn)
@ -1459,10 +1478,10 @@ static void __kernfs_remove(struct kernfs_node *kn)
* This is for kernfs_remove_self() which plays with active ref * This is for kernfs_remove_self() which plays with active ref
* after removal. * after removal.
*/ */
if (kn->parent && RB_EMPTY_NODE(&kn->rb)) if (kernfs_parent(kn) && RB_EMPTY_NODE(&kn->rb))
return; return;
pr_debug("kernfs %s: removing\n", kn->name); pr_debug("kernfs %s: removing\n", kernfs_rcu_name(kn));
/* prevent new usage by marking all nodes removing and deactivating */ /* prevent new usage by marking all nodes removing and deactivating */
pos = NULL; pos = NULL;
@ -1485,14 +1504,14 @@ static void __kernfs_remove(struct kernfs_node *kn)
kernfs_get(pos); kernfs_get(pos);
kernfs_drain(pos); kernfs_drain(pos);
parent = kernfs_parent(pos);
/* /*
* kernfs_unlink_sibling() succeeds once per node. Use it * kernfs_unlink_sibling() succeeds once per node. Use it
* to decide who's responsible for cleanups. * to decide who's responsible for cleanups.
*/ */
if (!pos->parent || kernfs_unlink_sibling(pos)) { if (!parent || kernfs_unlink_sibling(pos)) {
struct kernfs_iattrs *ps_iattr = struct kernfs_iattrs *ps_iattr =
pos->parent ? pos->parent->iattr : NULL; parent ? parent->iattr : NULL;
/* update timestamps on the parent */ /* update timestamps on the parent */
down_write(&kernfs_root(kn)->kernfs_iattr_rwsem); down_write(&kernfs_root(kn)->kernfs_iattr_rwsem);
@ -1718,11 +1737,11 @@ int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent,
{ {
struct kernfs_node *old_parent; struct kernfs_node *old_parent;
struct kernfs_root *root; struct kernfs_root *root;
const char *old_name = NULL; const char *old_name;
int error; int error;
/* can't move or rename root */ /* can't move or rename root */
if (!kn->parent) if (!rcu_access_pointer(kn->__parent))
return -EINVAL; return -EINVAL;
root = kernfs_root(kn); root = kernfs_root(kn);
@ -1733,9 +1752,19 @@ int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent,
(new_parent->flags & KERNFS_EMPTY_DIR)) (new_parent->flags & KERNFS_EMPTY_DIR))
goto out; goto out;
old_parent = kernfs_parent(kn);
if (root->flags & KERNFS_ROOT_INVARIANT_PARENT) {
error = -EINVAL;
if (WARN_ON_ONCE(old_parent != new_parent))
goto out;
}
error = 0; error = 0;
if ((kn->parent == new_parent) && (kn->ns == new_ns) && old_name = kernfs_rcu_name(kn);
(strcmp(kn->name, new_name) == 0)) if (!new_name)
new_name = old_name;
if ((old_parent == new_parent) && (kn->ns == new_ns) &&
(strcmp(old_name, new_name) == 0))
goto out; /* nothing to rename */ goto out; /* nothing to rename */
error = -EEXIST; error = -EEXIST;
@ -1743,7 +1772,7 @@ int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent,
goto out; goto out;
/* rename kernfs_node */ /* rename kernfs_node */
if (strcmp(kn->name, new_name) != 0) { if (strcmp(old_name, new_name) != 0) {
error = -ENOMEM; error = -ENOMEM;
new_name = kstrdup_const(new_name, GFP_KERNEL); new_name = kstrdup_const(new_name, GFP_KERNEL);
if (!new_name) if (!new_name)
@ -1756,27 +1785,32 @@ int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent,
* Move to the appropriate place in the appropriate directories rbtree. * Move to the appropriate place in the appropriate directories rbtree.
*/ */
kernfs_unlink_sibling(kn); kernfs_unlink_sibling(kn);
kernfs_get(new_parent);
/* rename_lock protects ->parent and ->name accessors */ /* rename_lock protects ->parent accessors */
write_lock_irq(&kernfs_rename_lock); if (old_parent != new_parent) {
kernfs_get(new_parent);
write_lock_irq(&kernfs_rename_lock);
old_parent = kn->parent; rcu_assign_pointer(kn->__parent, new_parent);
kn->parent = new_parent;
kn->ns = new_ns; kn->ns = new_ns;
if (new_name) { if (new_name)
old_name = kn->name; rcu_assign_pointer(kn->name, new_name);
kn->name = new_name;
write_unlock_irq(&kernfs_rename_lock);
kernfs_put(old_parent);
} else {
/* name assignment is RCU protected, parent is the same */
kn->ns = new_ns;
if (new_name)
rcu_assign_pointer(kn->name, new_name);
} }
write_unlock_irq(&kernfs_rename_lock); kn->hash = kernfs_name_hash(new_name ?: old_name, kn->ns);
kn->hash = kernfs_name_hash(kn->name, kn->ns);
kernfs_link_sibling(kn); kernfs_link_sibling(kn);
kernfs_put(old_parent); if (new_name && !is_kernel_rodata((unsigned long)old_name))
kfree_const(old_name); kfree_rcu_mightsleep(old_name);
error = 0; error = 0;
out: out:
@ -1795,7 +1829,8 @@ static struct kernfs_node *kernfs_dir_pos(const void *ns,
{ {
if (pos) { if (pos) {
int valid = kernfs_active(pos) && int valid = kernfs_active(pos) &&
pos->parent == parent && hash == pos->hash; rcu_access_pointer(pos->__parent) == parent &&
hash == pos->hash;
kernfs_put(pos); kernfs_put(pos);
if (!valid) if (!valid)
pos = NULL; pos = NULL;
@ -1860,7 +1895,7 @@ static int kernfs_fop_readdir(struct file *file, struct dir_context *ctx)
for (pos = kernfs_dir_pos(ns, parent, ctx->pos, pos); for (pos = kernfs_dir_pos(ns, parent, ctx->pos, pos);
pos; pos;
pos = kernfs_dir_next_pos(ns, parent, ctx->pos, pos)) { pos = kernfs_dir_next_pos(ns, parent, ctx->pos, pos)) {
const char *name = pos->name; const char *name = kernfs_rcu_name(pos);
unsigned int type = fs_umode_to_dtype(pos->mode); unsigned int type = fs_umode_to_dtype(pos->mode);
int len = strlen(name); int len = strlen(name);
ino_t ino = kernfs_ino(pos); ino_t ino = kernfs_ino(pos);
@ -1869,10 +1904,10 @@ static int kernfs_fop_readdir(struct file *file, struct dir_context *ctx)
file->private_data = pos; file->private_data = pos;
kernfs_get(pos); kernfs_get(pos);
up_read(&root->kernfs_rwsem); if (!dir_emit(ctx, name, len, ino, type)) {
if (!dir_emit(ctx, name, len, ino, type)) up_read(&root->kernfs_rwsem);
return 0; return 0;
down_read(&root->kernfs_rwsem); }
} }
up_read(&root->kernfs_rwsem); up_read(&root->kernfs_rwsem);
file->private_data = NULL; file->private_data = NULL;

View File

@ -911,9 +911,11 @@ static void kernfs_notify_workfn(struct work_struct *work)
/* kick fsnotify */ /* kick fsnotify */
down_read(&root->kernfs_supers_rwsem); down_read(&root->kernfs_supers_rwsem);
down_read(&root->kernfs_rwsem);
list_for_each_entry(info, &kernfs_root(kn)->supers, node) { list_for_each_entry(info, &kernfs_root(kn)->supers, node) {
struct kernfs_node *parent; struct kernfs_node *parent;
struct inode *p_inode = NULL; struct inode *p_inode = NULL;
const char *kn_name;
struct inode *inode; struct inode *inode;
struct qstr name; struct qstr name;
@ -927,7 +929,8 @@ static void kernfs_notify_workfn(struct work_struct *work)
if (!inode) if (!inode)
continue; continue;
name = QSTR(kn->name); kn_name = kernfs_rcu_name(kn);
name = QSTR(kn_name);
parent = kernfs_get_parent(kn); parent = kernfs_get_parent(kn);
if (parent) { if (parent) {
p_inode = ilookup(info->sb, kernfs_ino(parent)); p_inode = ilookup(info->sb, kernfs_ino(parent));
@ -947,6 +950,7 @@ static void kernfs_notify_workfn(struct work_struct *work)
iput(inode); iput(inode);
} }
up_read(&root->kernfs_rwsem);
up_read(&root->kernfs_supers_rwsem); up_read(&root->kernfs_supers_rwsem);
kernfs_put(kn); kernfs_put(kn);
goto repeat; goto repeat;

View File

@ -19,6 +19,8 @@
#include <linux/kernfs.h> #include <linux/kernfs.h>
#include <linux/fs_context.h> #include <linux/fs_context.h>
extern rwlock_t kernfs_rename_lock;
struct kernfs_iattrs { struct kernfs_iattrs {
kuid_t ia_uid; kuid_t ia_uid;
kgid_t ia_gid; kgid_t ia_gid;
@ -64,11 +66,14 @@ struct kernfs_root {
* *
* Return: the kernfs_root @kn belongs to. * Return: the kernfs_root @kn belongs to.
*/ */
static inline struct kernfs_root *kernfs_root(struct kernfs_node *kn) static inline struct kernfs_root *kernfs_root(const struct kernfs_node *kn)
{ {
const struct kernfs_node *knp;
/* if parent exists, it's always a dir; otherwise, @sd is a dir */ /* if parent exists, it's always a dir; otherwise, @sd is a dir */
if (kn->parent) guard(rcu)();
kn = kn->parent; knp = rcu_dereference(kn->__parent);
if (knp)
kn = knp;
return kn->dir.root; return kn->dir.root;
} }
@ -97,6 +102,32 @@ struct kernfs_super_info {
}; };
#define kernfs_info(SB) ((struct kernfs_super_info *)(SB->s_fs_info)) #define kernfs_info(SB) ((struct kernfs_super_info *)(SB->s_fs_info))
static inline bool kernfs_root_is_locked(const struct kernfs_node *kn)
{
return lockdep_is_held(&kernfs_root(kn)->kernfs_rwsem);
}
static inline const char *kernfs_rcu_name(const struct kernfs_node *kn)
{
return rcu_dereference_check(kn->name, kernfs_root_is_locked(kn));
}
static inline struct kernfs_node *kernfs_parent(const struct kernfs_node *kn)
{
/*
* The kernfs_node::__parent remains valid within a RCU section. The kn
* can be reparented (and renamed) which changes the entry. This can be
* avoided by locking kernfs_root::kernfs_rwsem or kernfs_rename_lock.
* Both locks can be used to obtain a reference on __parent. Once the
* reference count reaches 0 then the node is about to be freed
* and can not be renamed (or become a different parent) anymore.
*/
return rcu_dereference_check(kn->__parent,
kernfs_root_is_locked(kn) ||
lockdep_is_held(&kernfs_rename_lock) ||
!atomic_read(&kn->count));
}
static inline struct kernfs_node *kernfs_dentry_node(struct dentry *dentry) static inline struct kernfs_node *kernfs_dentry_node(struct dentry *dentry)
{ {
if (d_really_is_negative(dentry)) if (d_really_is_negative(dentry))

View File

@ -145,8 +145,10 @@ static struct dentry *kernfs_fh_to_parent(struct super_block *sb,
static struct dentry *kernfs_get_parent_dentry(struct dentry *child) static struct dentry *kernfs_get_parent_dentry(struct dentry *child)
{ {
struct kernfs_node *kn = kernfs_dentry_node(child); struct kernfs_node *kn = kernfs_dentry_node(child);
struct kernfs_root *root = kernfs_root(kn);
return d_obtain_alias(kernfs_get_inode(child->d_sb, kn->parent)); guard(rwsem_read)(&root->kernfs_rwsem);
return d_obtain_alias(kernfs_get_inode(child->d_sb, kernfs_parent(kn)));
} }
static const struct export_operations kernfs_export_ops = { static const struct export_operations kernfs_export_ops = {
@ -186,10 +188,10 @@ static struct kernfs_node *find_next_ancestor(struct kernfs_node *child,
return NULL; return NULL;
} }
while (child->parent != parent) { while (kernfs_parent(child) != parent) {
if (!child->parent) child = kernfs_parent(child);
if (!child)
return NULL; return NULL;
child = child->parent;
} }
return child; return child;
@ -207,16 +209,27 @@ struct dentry *kernfs_node_dentry(struct kernfs_node *kn,
{ {
struct dentry *dentry; struct dentry *dentry;
struct kernfs_node *knparent; struct kernfs_node *knparent;
struct kernfs_root *root;
BUG_ON(sb->s_op != &kernfs_sops); BUG_ON(sb->s_op != &kernfs_sops);
dentry = dget(sb->s_root); dentry = dget(sb->s_root);
/* Check if this is the root kernfs_node */ /* Check if this is the root kernfs_node */
if (!kn->parent) if (!rcu_access_pointer(kn->__parent))
return dentry; return dentry;
knparent = find_next_ancestor(kn, NULL); root = kernfs_root(kn);
/*
* As long as kn is valid, its parent can not vanish. This is cgroup's
* kn so it can't have its parent replaced. Therefore it is safe to use
* the ancestor node outside of the RCU or locked section.
*/
if (WARN_ON_ONCE(!(root->flags & KERNFS_ROOT_INVARIANT_PARENT)))
return ERR_PTR(-EINVAL);
scoped_guard(rcu) {
knparent = find_next_ancestor(kn, NULL);
}
if (WARN_ON(!knparent)) { if (WARN_ON(!knparent)) {
dput(dentry); dput(dentry);
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
@ -225,17 +238,26 @@ struct dentry *kernfs_node_dentry(struct kernfs_node *kn,
do { do {
struct dentry *dtmp; struct dentry *dtmp;
struct kernfs_node *kntmp; struct kernfs_node *kntmp;
const char *name;
if (kn == knparent) if (kn == knparent)
return dentry; return dentry;
kntmp = find_next_ancestor(kn, knparent);
if (WARN_ON(!kntmp)) { scoped_guard(rwsem_read, &root->kernfs_rwsem) {
dput(dentry); kntmp = find_next_ancestor(kn, knparent);
return ERR_PTR(-EINVAL); if (WARN_ON(!kntmp)) {
dput(dentry);
return ERR_PTR(-EINVAL);
}
name = kstrdup(kernfs_rcu_name(kntmp), GFP_KERNEL);
} }
dtmp = lookup_positive_unlocked(kntmp->name, dentry, if (!name) {
strlen(kntmp->name)); dput(dentry);
return ERR_PTR(-ENOMEM);
}
dtmp = lookup_positive_unlocked(name, dentry, strlen(name));
dput(dentry); dput(dentry);
kfree(name);
if (IS_ERR(dtmp)) if (IS_ERR(dtmp))
return dtmp; return dtmp;
knparent = kntmp; knparent = kntmp;

View File

@ -62,10 +62,10 @@ static int kernfs_get_target_path(struct kernfs_node *parent,
/* go up to the root, stop at the base */ /* go up to the root, stop at the base */
base = parent; base = parent;
while (base->parent) { while (kernfs_parent(base)) {
kn = target->parent; kn = kernfs_parent(target);
while (kn->parent && base != kn) while (kernfs_parent(kn) && base != kn)
kn = kn->parent; kn = kernfs_parent(kn);
if (base == kn) if (base == kn)
break; break;
@ -75,14 +75,14 @@ static int kernfs_get_target_path(struct kernfs_node *parent,
strcpy(s, "../"); strcpy(s, "../");
s += 3; s += 3;
base = base->parent; base = kernfs_parent(base);
} }
/* determine end of target string for reverse fillup */ /* determine end of target string for reverse fillup */
kn = target; kn = target;
while (kn->parent && kn != base) { while (kernfs_parent(kn) && kn != base) {
len += strlen(kn->name) + 1; len += strlen(kernfs_rcu_name(kn)) + 1;
kn = kn->parent; kn = kernfs_parent(kn);
} }
/* check limits */ /* check limits */
@ -94,15 +94,16 @@ static int kernfs_get_target_path(struct kernfs_node *parent,
/* reverse fillup of target string from target to base */ /* reverse fillup of target string from target to base */
kn = target; kn = target;
while (kn->parent && kn != base) { while (kernfs_parent(kn) && kn != base) {
int slen = strlen(kn->name); const char *name = kernfs_rcu_name(kn);
int slen = strlen(name);
len -= slen; len -= slen;
memcpy(s + len, kn->name, slen); memcpy(s + len, name, slen);
if (len) if (len)
s[--len] = '/'; s[--len] = '/';
kn = kn->parent; kn = kernfs_parent(kn);
} }
return 0; return 0;
@ -111,12 +112,13 @@ static int kernfs_get_target_path(struct kernfs_node *parent,
static int kernfs_getlink(struct inode *inode, char *path) static int kernfs_getlink(struct inode *inode, char *path)
{ {
struct kernfs_node *kn = inode->i_private; struct kernfs_node *kn = inode->i_private;
struct kernfs_node *parent = kn->parent; struct kernfs_node *parent;
struct kernfs_node *target = kn->symlink.target_kn; struct kernfs_node *target = kn->symlink.target_kn;
struct kernfs_root *root = kernfs_root(parent); struct kernfs_root *root = kernfs_root(kn);
int error; int error;
down_read(&root->kernfs_rwsem); down_read(&root->kernfs_rwsem);
parent = kernfs_parent(kn);
error = kernfs_get_target_path(parent, target, path); error = kernfs_get_target_path(parent, target, path);
up_read(&root->kernfs_rwsem); up_read(&root->kernfs_rwsem);

View File

@ -123,7 +123,7 @@ int sysfs_move_dir_ns(struct kobject *kobj, struct kobject *new_parent_kobj,
new_parent = new_parent_kobj && new_parent_kobj->sd ? new_parent = new_parent_kobj && new_parent_kobj->sd ?
new_parent_kobj->sd : sysfs_root_kn; new_parent_kobj->sd : sysfs_root_kn;
return kernfs_rename_ns(kn, new_parent, kn->name, new_ns); return kernfs_rename_ns(kn, new_parent, NULL, new_ns);
} }
/** /**

View File

@ -19,13 +19,19 @@
#include "sysfs.h" #include "sysfs.h"
static struct kobject *sysfs_file_kobj(struct kernfs_node *kn)
{
guard(rcu)();
return rcu_dereference(kn->__parent)->priv;
}
/* /*
* Determine ktype->sysfs_ops for the given kernfs_node. This function * Determine ktype->sysfs_ops for the given kernfs_node. This function
* must be called while holding an active reference. * must be called while holding an active reference.
*/ */
static const struct sysfs_ops *sysfs_file_ops(struct kernfs_node *kn) static const struct sysfs_ops *sysfs_file_ops(struct kernfs_node *kn)
{ {
struct kobject *kobj = kn->parent->priv; struct kobject *kobj = sysfs_file_kobj(kn);
if (kn->flags & KERNFS_LOCKDEP) if (kn->flags & KERNFS_LOCKDEP)
lockdep_assert_held(kn); lockdep_assert_held(kn);
@ -40,7 +46,7 @@ static const struct sysfs_ops *sysfs_file_ops(struct kernfs_node *kn)
static int sysfs_kf_seq_show(struct seq_file *sf, void *v) static int sysfs_kf_seq_show(struct seq_file *sf, void *v)
{ {
struct kernfs_open_file *of = sf->private; struct kernfs_open_file *of = sf->private;
struct kobject *kobj = of->kn->parent->priv; struct kobject *kobj = sysfs_file_kobj(of->kn);
const struct sysfs_ops *ops = sysfs_file_ops(of->kn); const struct sysfs_ops *ops = sysfs_file_ops(of->kn);
ssize_t count; ssize_t count;
char *buf; char *buf;
@ -78,7 +84,7 @@ static ssize_t sysfs_kf_bin_read(struct kernfs_open_file *of, char *buf,
size_t count, loff_t pos) size_t count, loff_t pos)
{ {
struct bin_attribute *battr = of->kn->priv; struct bin_attribute *battr = of->kn->priv;
struct kobject *kobj = of->kn->parent->priv; struct kobject *kobj = sysfs_file_kobj(of->kn);
loff_t size = file_inode(of->file)->i_size; loff_t size = file_inode(of->file)->i_size;
if (!count) if (!count)
@ -105,7 +111,7 @@ static ssize_t sysfs_kf_read(struct kernfs_open_file *of, char *buf,
size_t count, loff_t pos) size_t count, loff_t pos)
{ {
const struct sysfs_ops *ops = sysfs_file_ops(of->kn); const struct sysfs_ops *ops = sysfs_file_ops(of->kn);
struct kobject *kobj = of->kn->parent->priv; struct kobject *kobj = sysfs_file_kobj(of->kn);
ssize_t len; ssize_t len;
/* /*
@ -131,7 +137,7 @@ static ssize_t sysfs_kf_write(struct kernfs_open_file *of, char *buf,
size_t count, loff_t pos) size_t count, loff_t pos)
{ {
const struct sysfs_ops *ops = sysfs_file_ops(of->kn); const struct sysfs_ops *ops = sysfs_file_ops(of->kn);
struct kobject *kobj = of->kn->parent->priv; struct kobject *kobj = sysfs_file_kobj(of->kn);
if (!count) if (!count)
return 0; return 0;
@ -144,7 +150,7 @@ static ssize_t sysfs_kf_bin_write(struct kernfs_open_file *of, char *buf,
size_t count, loff_t pos) size_t count, loff_t pos)
{ {
struct bin_attribute *battr = of->kn->priv; struct bin_attribute *battr = of->kn->priv;
struct kobject *kobj = of->kn->parent->priv; struct kobject *kobj = sysfs_file_kobj(of->kn);
loff_t size = file_inode(of->file)->i_size; loff_t size = file_inode(of->file)->i_size;
if (size) { if (size) {
@ -168,7 +174,7 @@ static int sysfs_kf_bin_mmap(struct kernfs_open_file *of,
struct vm_area_struct *vma) struct vm_area_struct *vma)
{ {
struct bin_attribute *battr = of->kn->priv; struct bin_attribute *battr = of->kn->priv;
struct kobject *kobj = of->kn->parent->priv; struct kobject *kobj = sysfs_file_kobj(of->kn);
return battr->mmap(of->file, kobj, battr, vma); return battr->mmap(of->file, kobj, battr, vma);
} }
@ -177,7 +183,7 @@ static loff_t sysfs_kf_bin_llseek(struct kernfs_open_file *of, loff_t offset,
int whence) int whence)
{ {
struct bin_attribute *battr = of->kn->priv; struct bin_attribute *battr = of->kn->priv;
struct kobject *kobj = of->kn->parent->priv; struct kobject *kobj = sysfs_file_kobj(of->kn);
if (battr->llseek) if (battr->llseek)
return battr->llseek(of->file, kobj, battr, offset, whence); return battr->llseek(of->file, kobj, battr, offset, whence);
@ -494,7 +500,7 @@ EXPORT_SYMBOL_GPL(sysfs_break_active_protection);
*/ */
void sysfs_unbreak_active_protection(struct kernfs_node *kn) void sysfs_unbreak_active_protection(struct kernfs_node *kn)
{ {
struct kobject *kobj = kn->parent->priv; struct kobject *kobj = sysfs_file_kobj(kn);
kernfs_unbreak_active_protection(kn); kernfs_unbreak_active_protection(kn);
kernfs_put(kn); kernfs_put(kn);

View File

@ -1162,7 +1162,7 @@ static inline void device_remove_group(struct device *dev,
{ {
const struct attribute_group *groups[] = { grp, NULL }; const struct attribute_group *groups[] = { grp, NULL };
return device_remove_groups(dev, groups); device_remove_groups(dev, groups);
} }
int __must_check devm_device_add_group(struct device *dev, int __must_check devm_device_add_group(struct device *dev,

View File

@ -193,7 +193,7 @@ static inline int __must_check class_create_file(const struct class *class,
static inline void class_remove_file(const struct class *class, static inline void class_remove_file(const struct class *class,
const struct class_attribute *attr) const struct class_attribute *attr)
{ {
return class_remove_file_ns(class, attr, NULL); class_remove_file_ns(class, attr, NULL);
} }
/* Simple class attribute that is just a static string */ /* Simple class attribute that is just a static string */

View File

@ -147,6 +147,11 @@ enum kernfs_root_flag {
* Support user xattrs to be written to nodes rooted at this root. * Support user xattrs to be written to nodes rooted at this root.
*/ */
KERNFS_ROOT_SUPPORT_USER_XATTR = 0x0008, KERNFS_ROOT_SUPPORT_USER_XATTR = 0x0008,
/*
* Renames must not change the parent node.
*/
KERNFS_ROOT_INVARIANT_PARENT = 0x0010,
}; };
/* type-specific structures for kernfs_node union members */ /* type-specific structures for kernfs_node union members */
@ -199,8 +204,8 @@ struct kernfs_node {
* never moved to a different parent, it is safe to access the * never moved to a different parent, it is safe to access the
* parent directly. * parent directly.
*/ */
struct kernfs_node *parent; struct kernfs_node __rcu *__parent;
const char *name; const char __rcu *name;
struct rb_node rb; struct rb_node rb;
@ -395,7 +400,7 @@ static inline bool kernfs_ns_enabled(struct kernfs_node *kn)
} }
int kernfs_name(struct kernfs_node *kn, char *buf, size_t buflen); int kernfs_name(struct kernfs_node *kn, char *buf, size_t buflen);
int kernfs_path_from_node(struct kernfs_node *root_kn, struct kernfs_node *kn, int kernfs_path_from_node(struct kernfs_node *kn_to, struct kernfs_node *kn_from,
char *buf, size_t buflen); char *buf, size_t buflen);
void pr_cont_kernfs_name(struct kernfs_node *kn); void pr_cont_kernfs_name(struct kernfs_node *kn);
void pr_cont_kernfs_path(struct kernfs_node *kn); void pr_cont_kernfs_path(struct kernfs_node *kn);
@ -416,6 +421,7 @@ struct dentry *kernfs_node_dentry(struct kernfs_node *kn,
struct kernfs_root *kernfs_create_root(struct kernfs_syscall_ops *scops, struct kernfs_root *kernfs_create_root(struct kernfs_syscall_ops *scops,
unsigned int flags, void *priv); unsigned int flags, void *priv);
void kernfs_destroy_root(struct kernfs_root *root); void kernfs_destroy_root(struct kernfs_root *root);
unsigned int kernfs_root_flags(struct kernfs_node *kn);
struct kernfs_node *kernfs_create_dir_ns(struct kernfs_node *parent, struct kernfs_node *kernfs_create_dir_ns(struct kernfs_node *parent,
const char *name, umode_t mode, const char *name, umode_t mode,
@ -514,6 +520,8 @@ kernfs_create_root(struct kernfs_syscall_ops *scops, unsigned int flags,
{ return ERR_PTR(-ENOSYS); } { return ERR_PTR(-ENOSYS); }
static inline void kernfs_destroy_root(struct kernfs_root *root) { } static inline void kernfs_destroy_root(struct kernfs_root *root) { }
static inline unsigned int kernfs_root_flags(struct kernfs_node *kn)
{ return 0; }
static inline struct kernfs_node * static inline struct kernfs_node *
kernfs_create_dir_ns(struct kernfs_node *parent, const char *name, kernfs_create_dir_ns(struct kernfs_node *parent, const char *name,

View File

@ -30,7 +30,7 @@ choice
250 Hz is a good compromise choice allowing server performance 250 Hz is a good compromise choice allowing server performance
while also showing good interactive responsiveness even while also showing good interactive responsiveness even
on SMP and NUMA systems. If you are going to be using NTSC video on SMP and NUMA systems. If you are going to be using NTSC video
or multimedia, selected 300Hz instead. or multimedia, select 300Hz instead.
config HZ_300 config HZ_300
bool "300 HZ" bool "300 HZ"

View File

@ -851,7 +851,7 @@ static int cgroup1_rename(struct kernfs_node *kn, struct kernfs_node *new_parent
if (kernfs_type(kn) != KERNFS_DIR) if (kernfs_type(kn) != KERNFS_DIR)
return -ENOTDIR; return -ENOTDIR;
if (kn->parent != new_parent) if (rcu_access_pointer(kn->__parent) != new_parent)
return -EIO; return -EIO;
/* /*

View File

@ -633,9 +633,22 @@ int cgroup_task_count(const struct cgroup *cgrp)
return count; return count;
} }
static struct cgroup *kn_priv(struct kernfs_node *kn)
{
struct kernfs_node *parent;
/*
* The parent can not be replaced due to KERNFS_ROOT_INVARIANT_PARENT.
* Therefore it is always safe to dereference this pointer outside of a
* RCU section.
*/
parent = rcu_dereference_check(kn->__parent,
kernfs_root_flags(kn) & KERNFS_ROOT_INVARIANT_PARENT);
return parent->priv;
}
struct cgroup_subsys_state *of_css(struct kernfs_open_file *of) struct cgroup_subsys_state *of_css(struct kernfs_open_file *of)
{ {
struct cgroup *cgrp = of->kn->parent->priv; struct cgroup *cgrp = kn_priv(of->kn);
struct cftype *cft = of_cft(of); struct cftype *cft = of_cft(of);
/* /*
@ -1612,7 +1625,7 @@ void cgroup_kn_unlock(struct kernfs_node *kn)
if (kernfs_type(kn) == KERNFS_DIR) if (kernfs_type(kn) == KERNFS_DIR)
cgrp = kn->priv; cgrp = kn->priv;
else else
cgrp = kn->parent->priv; cgrp = kn_priv(kn);
cgroup_unlock(); cgroup_unlock();
@ -1644,7 +1657,7 @@ struct cgroup *cgroup_kn_lock_live(struct kernfs_node *kn, bool drain_offline)
if (kernfs_type(kn) == KERNFS_DIR) if (kernfs_type(kn) == KERNFS_DIR)
cgrp = kn->priv; cgrp = kn->priv;
else else
cgrp = kn->parent->priv; cgrp = kn_priv(kn);
/* /*
* We're gonna grab cgroup_mutex which nests outside kernfs * We're gonna grab cgroup_mutex which nests outside kernfs
@ -2118,7 +2131,8 @@ int cgroup_setup_root(struct cgroup_root *root, u16 ss_mask)
root->kf_root = kernfs_create_root(kf_sops, root->kf_root = kernfs_create_root(kf_sops,
KERNFS_ROOT_CREATE_DEACTIVATED | KERNFS_ROOT_CREATE_DEACTIVATED |
KERNFS_ROOT_SUPPORT_EXPORTOP | KERNFS_ROOT_SUPPORT_EXPORTOP |
KERNFS_ROOT_SUPPORT_USER_XATTR, KERNFS_ROOT_SUPPORT_USER_XATTR |
KERNFS_ROOT_INVARIANT_PARENT,
root_cgrp); root_cgrp);
if (IS_ERR(root->kf_root)) { if (IS_ERR(root->kf_root)) {
ret = PTR_ERR(root->kf_root); ret = PTR_ERR(root->kf_root);
@ -4115,7 +4129,7 @@ static ssize_t cgroup_file_write(struct kernfs_open_file *of, char *buf,
size_t nbytes, loff_t off) size_t nbytes, loff_t off)
{ {
struct cgroup_file_ctx *ctx = of->priv; struct cgroup_file_ctx *ctx = of->priv;
struct cgroup *cgrp = of->kn->parent->priv; struct cgroup *cgrp = kn_priv(of->kn);
struct cftype *cft = of_cft(of); struct cftype *cft = of_cft(of);
struct cgroup_subsys_state *css; struct cgroup_subsys_state *css;
int ret; int ret;

View File

@ -209,6 +209,32 @@ unsafe impl Send for Device {}
// synchronization in `struct device`. // synchronization in `struct device`.
unsafe impl Sync for Device {} unsafe impl Sync for Device {}
/// Marker trait for the context of a bus specific device.
///
/// Some functions of a bus specific device should only be called from a certain context, i.e. bus
/// callbacks, such as `probe()`.
///
/// This is the marker trait for structures representing the context of a bus specific device.
pub trait DeviceContext: private::Sealed {}
/// The [`Normal`] context is the context of a bus specific device when it is not an argument of
/// any bus callback.
pub struct Normal;
/// The [`Core`] context is the context of a bus specific device when it is supplied as argument of
/// any of the bus callbacks, such as `probe()`.
pub struct Core;
mod private {
pub trait Sealed {}
impl Sealed for super::Core {}
impl Sealed for super::Normal {}
}
impl DeviceContext for Core {}
impl DeviceContext for Normal {}
#[doc(hidden)] #[doc(hidden)]
#[macro_export] #[macro_export]
macro_rules! dev_printk { macro_rules! dev_printk {

View File

@ -92,7 +92,7 @@ struct DevresInner<T> {
/// let devres = Devres::new(&dev, iomem, GFP_KERNEL)?; /// let devres = Devres::new(&dev, iomem, GFP_KERNEL)?;
/// ///
/// let res = devres.try_access().ok_or(ENXIO)?; /// let res = devres.try_access().ok_or(ENXIO)?;
/// res.writel(0x42, 0x0); /// res.write8(0x42, 0x0);
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```

View File

@ -19,16 +19,25 @@
/// `self.0` always holds a valid pointer to an initialized and registered [`struct faux_device`]. /// `self.0` always holds a valid pointer to an initialized and registered [`struct faux_device`].
/// ///
/// [`struct faux_device`]: srctree/include/linux/device/faux.h /// [`struct faux_device`]: srctree/include/linux/device/faux.h
#[repr(transparent)]
pub struct Registration(NonNull<bindings::faux_device>); pub struct Registration(NonNull<bindings::faux_device>);
impl Registration { impl Registration {
/// Create and register a new faux device with the given name. /// Create and register a new faux device with the given name.
pub fn new(name: &CStr) -> Result<Self> { #[inline]
pub fn new(name: &CStr, parent: Option<&device::Device>) -> Result<Self> {
// SAFETY: // SAFETY:
// - `name` is copied by this function into its own storage // - `name` is copied by this function into its own storage
// - `faux_ops` is safe to leave NULL according to the C API // - `faux_ops` is safe to leave NULL according to the C API
let dev = unsafe { bindings::faux_device_create(name.as_char_ptr(), null_mut(), null()) }; // - `parent` can be either NULL or a pointer to a `struct device`, and `faux_device_create`
// will take a reference to `parent` using `device_add` - ensuring that it remains valid
// for the lifetime of the faux device.
let dev = unsafe {
bindings::faux_device_create(
name.as_char_ptr(),
parent.map_or(null_mut(), |p| p.as_raw()),
null(),
)
};
// The above function will return either a valid device, or NULL on failure // The above function will return either a valid device, or NULL on failure
// INVARIANT: The device will remain registered until faux_device_destroy() is called, which // INVARIANT: The device will remain registered until faux_device_destroy() is called, which
@ -50,6 +59,7 @@ fn as_ref(&self) -> &device::Device {
} }
impl Drop for Registration { impl Drop for Registration {
#[inline]
fn drop(&mut self) { fn drop(&mut self) {
// SAFETY: `self.0` is a valid registered faux_device via our type invariants. // SAFETY: `self.0` is a valid registered faux_device via our type invariants.
unsafe { bindings::faux_device_destroy(self.as_raw()) } unsafe { bindings::faux_device_destroy(self.as_raw()) }

View File

@ -98,9 +98,9 @@ pub fn maxsize(&self) -> usize {
///# fn no_run() -> Result<(), Error> { ///# fn no_run() -> Result<(), Error> {
/// // SAFETY: Invalid usage for example purposes. /// // SAFETY: Invalid usage for example purposes.
/// let iomem = unsafe { IoMem::<{ core::mem::size_of::<u32>() }>::new(0xBAAAAAAD)? }; /// let iomem = unsafe { IoMem::<{ core::mem::size_of::<u32>() }>::new(0xBAAAAAAD)? };
/// iomem.writel(0x42, 0x0); /// iomem.write32(0x42, 0x0);
/// assert!(iomem.try_writel(0x42, 0x0).is_ok()); /// assert!(iomem.try_write32(0x42, 0x0).is_ok());
/// assert!(iomem.try_writel(0x42, 0x4).is_err()); /// assert!(iomem.try_write32(0x42, 0x4).is_err());
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
@ -108,7 +108,7 @@ pub fn maxsize(&self) -> usize {
pub struct Io<const SIZE: usize = 0>(IoRaw<SIZE>); pub struct Io<const SIZE: usize = 0>(IoRaw<SIZE>);
macro_rules! define_read { macro_rules! define_read {
($(#[$attr:meta])* $name:ident, $try_name:ident, $type_name:ty) => { ($(#[$attr:meta])* $name:ident, $try_name:ident, $c_fn:ident -> $type_name:ty) => {
/// Read IO data from a given offset known at compile time. /// 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 /// Bound checks are performed on compile time, hence if the offset is not known at compile
@ -119,7 +119,7 @@ pub fn $name(&self, offset: usize) -> $type_name {
let addr = self.io_addr_assert::<$type_name>(offset); let addr = self.io_addr_assert::<$type_name>(offset);
// SAFETY: By the type invariant `addr` is a valid address for MMIO operations. // SAFETY: By the type invariant `addr` is a valid address for MMIO operations.
unsafe { bindings::$name(addr as _) } unsafe { bindings::$c_fn(addr as _) }
} }
/// Read IO data from a given offset. /// Read IO data from a given offset.
@ -131,13 +131,13 @@ pub fn $try_name(&self, offset: usize) -> Result<$type_name> {
let addr = self.io_addr::<$type_name>(offset)?; let addr = self.io_addr::<$type_name>(offset)?;
// SAFETY: By the type invariant `addr` is a valid address for MMIO operations. // SAFETY: By the type invariant `addr` is a valid address for MMIO operations.
Ok(unsafe { bindings::$name(addr as _) }) Ok(unsafe { bindings::$c_fn(addr as _) })
} }
}; };
} }
macro_rules! define_write { macro_rules! define_write {
($(#[$attr:meta])* $name:ident, $try_name:ident, $type_name:ty) => { ($(#[$attr:meta])* $name:ident, $try_name:ident, $c_fn:ident <- $type_name:ty) => {
/// Write IO data from a given offset known at compile time. /// 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 /// Bound checks are performed on compile time, hence if the offset is not known at compile
@ -148,7 +148,7 @@ pub fn $name(&self, value: $type_name, offset: usize) {
let addr = self.io_addr_assert::<$type_name>(offset); let addr = self.io_addr_assert::<$type_name>(offset);
// SAFETY: By the type invariant `addr` is a valid address for MMIO operations. // SAFETY: By the type invariant `addr` is a valid address for MMIO operations.
unsafe { bindings::$name(value, addr as _, ) } unsafe { bindings::$c_fn(value, addr as _, ) }
} }
/// Write IO data from a given offset. /// Write IO data from a given offset.
@ -160,7 +160,7 @@ pub fn $try_name(&self, value: $type_name, offset: usize) -> Result {
let addr = self.io_addr::<$type_name>(offset)?; let addr = self.io_addr::<$type_name>(offset)?;
// SAFETY: By the type invariant `addr` is a valid address for MMIO operations. // SAFETY: By the type invariant `addr` is a valid address for MMIO operations.
unsafe { bindings::$name(value, addr as _) } unsafe { bindings::$c_fn(value, addr as _) }
Ok(()) Ok(())
} }
}; };
@ -218,43 +218,43 @@ fn io_addr_assert<U>(&self, offset: usize) -> usize {
self.addr() + offset self.addr() + offset
} }
define_read!(readb, try_readb, u8); define_read!(read8, try_read8, readb -> u8);
define_read!(readw, try_readw, u16); define_read!(read16, try_read16, readw -> u16);
define_read!(readl, try_readl, u32); define_read!(read32, try_read32, readl -> u32);
define_read!( define_read!(
#[cfg(CONFIG_64BIT)] #[cfg(CONFIG_64BIT)]
readq, read64,
try_readq, try_read64,
u64 readq -> u64
); );
define_read!(readb_relaxed, try_readb_relaxed, u8); define_read!(read8_relaxed, try_read8_relaxed, readb_relaxed -> u8);
define_read!(readw_relaxed, try_readw_relaxed, u16); define_read!(read16_relaxed, try_read16_relaxed, readw_relaxed -> u16);
define_read!(readl_relaxed, try_readl_relaxed, u32); define_read!(read32_relaxed, try_read32_relaxed, readl_relaxed -> u32);
define_read!( define_read!(
#[cfg(CONFIG_64BIT)] #[cfg(CONFIG_64BIT)]
readq_relaxed, read64_relaxed,
try_readq_relaxed, try_read64_relaxed,
u64 readq_relaxed -> u64
); );
define_write!(writeb, try_writeb, u8); define_write!(write8, try_write8, writeb <- u8);
define_write!(writew, try_writew, u16); define_write!(write16, try_write16, writew <- u16);
define_write!(writel, try_writel, u32); define_write!(write32, try_write32, writel <- u32);
define_write!( define_write!(
#[cfg(CONFIG_64BIT)] #[cfg(CONFIG_64BIT)]
writeq, write64,
try_writeq, try_write64,
u64 writeq <- u64
); );
define_write!(writeb_relaxed, try_writeb_relaxed, u8); define_write!(write8_relaxed, try_write8_relaxed, writeb_relaxed <- u8);
define_write!(writew_relaxed, try_writew_relaxed, u16); define_write!(write16_relaxed, try_write16_relaxed, writew_relaxed <- u16);
define_write!(writel_relaxed, try_writel_relaxed, u32); define_write!(write32_relaxed, try_write32_relaxed, writel_relaxed <- u32);
define_write!( define_write!(
#[cfg(CONFIG_64BIT)] #[cfg(CONFIG_64BIT)]
writeq_relaxed, write64_relaxed,
try_writeq_relaxed, try_write64_relaxed,
u64 writeq_relaxed <- u64
); );
} }

View File

@ -6,7 +6,7 @@
use crate::{ use crate::{
alloc::flags::*, alloc::flags::*,
bindings, container_of, device, bindings, device,
device_id::RawDeviceId, device_id::RawDeviceId,
devres::Devres, devres::Devres,
driver, driver,
@ -17,7 +17,11 @@
types::{ARef, ForeignOwnable, Opaque}, types::{ARef, ForeignOwnable, Opaque},
ThisModule, ThisModule,
}; };
use core::{ops::Deref, ptr::addr_of_mut}; use core::{
marker::PhantomData,
ops::Deref,
ptr::{addr_of_mut, NonNull},
};
use kernel::prelude::*; use kernel::prelude::*;
/// An adapter for the registration of PCI drivers. /// An adapter for the registration of PCI drivers.
@ -60,17 +64,16 @@ extern "C" fn probe_callback(
) -> kernel::ffi::c_int { ) -> kernel::ffi::c_int {
// SAFETY: The PCI bus only ever calls the probe callback with a valid pointer to a // SAFETY: The PCI bus only ever calls the probe callback with a valid pointer to a
// `struct pci_dev`. // `struct pci_dev`.
let dev = unsafe { device::Device::get_device(addr_of_mut!((*pdev).dev)) }; //
// SAFETY: `dev` is guaranteed to be embedded in a valid `struct pci_dev` by the call // INVARIANT: `pdev` is valid for the duration of `probe_callback()`.
// above. let pdev = unsafe { &*pdev.cast::<Device<device::Core>>() };
let mut pdev = unsafe { Device::from_dev(dev) };
// SAFETY: `DeviceId` is a `#[repr(transparent)` wrapper of `struct pci_device_id` and // SAFETY: `DeviceId` is a `#[repr(transparent)` wrapper of `struct pci_device_id` and
// does not add additional invariants, so it's safe to transmute. // does not add additional invariants, so it's safe to transmute.
let id = unsafe { &*id.cast::<DeviceId>() }; let id = unsafe { &*id.cast::<DeviceId>() };
let info = T::ID_TABLE.info(id.index()); let info = T::ID_TABLE.info(id.index());
match T::probe(&mut pdev, info) { match T::probe(pdev, info) {
Ok(data) => { Ok(data) => {
// Let the `struct pci_dev` own a reference of the driver's private data. // Let the `struct pci_dev` own a reference of the driver's private data.
// SAFETY: By the type invariant `pdev.as_raw` returns a valid pointer to a // SAFETY: By the type invariant `pdev.as_raw` returns a valid pointer to a
@ -192,7 +195,7 @@ macro_rules! pci_device_table {
/// # Example /// # Example
/// ///
///``` ///```
/// # use kernel::{bindings, pci}; /// # use kernel::{bindings, device::Core, pci};
/// ///
/// struct MyDriver; /// struct MyDriver;
/// ///
@ -210,7 +213,7 @@ macro_rules! pci_device_table {
/// const ID_TABLE: pci::IdTable<Self::IdInfo> = &PCI_TABLE; /// const ID_TABLE: pci::IdTable<Self::IdInfo> = &PCI_TABLE;
/// ///
/// fn probe( /// fn probe(
/// _pdev: &mut pci::Device, /// _pdev: &pci::Device<Core>,
/// _id_info: &Self::IdInfo, /// _id_info: &Self::IdInfo,
/// ) -> Result<Pin<KBox<Self>>> { /// ) -> Result<Pin<KBox<Self>>> {
/// Err(ENODEV) /// Err(ENODEV)
@ -219,7 +222,7 @@ macro_rules! pci_device_table {
///``` ///```
/// Drivers must implement this trait in order to get a PCI driver registered. Please refer to the /// Drivers must implement this trait in order to get a PCI driver registered. Please refer to the
/// `Adapter` documentation for an example. /// `Adapter` documentation for an example.
pub trait Driver { pub trait Driver: Send {
/// The type holding information about each device id supported by the driver. /// The type holding information about each device id supported by the driver.
/// ///
/// TODO: Use associated_type_defaults once stabilized: /// TODO: Use associated_type_defaults once stabilized:
@ -234,20 +237,23 @@ pub trait Driver {
/// ///
/// Called when a new platform device is added or discovered. /// Called when a new platform device is added or discovered.
/// Implementers should attempt to initialize the device here. /// Implementers should attempt to initialize the device here.
fn probe(dev: &mut Device, id_info: &Self::IdInfo) -> Result<Pin<KBox<Self>>>; fn probe(dev: &Device<device::Core>, id_info: &Self::IdInfo) -> Result<Pin<KBox<Self>>>;
} }
/// The PCI device representation. /// The PCI device representation.
/// ///
/// A PCI device is based on an always reference counted `device:Device` instance. Cloning a PCI /// This structure represents the Rust abstraction for a C `struct pci_dev`. The implementation
/// device, hence, also increments the base device' reference count. /// abstracts the usage of an already existing C `struct pci_dev` within Rust code that we get
/// passed from the C side.
/// ///
/// # Invariants /// # Invariants
/// ///
/// `Device` hold a valid reference of `ARef<device::Device>` whose underlying `struct device` is a /// A [`Device`] instance represents a valid `struct device` created by the C portion of the kernel.
/// member of a `struct pci_dev`. #[repr(transparent)]
#[derive(Clone)] pub struct Device<Ctx: device::DeviceContext = device::Normal>(
pub struct Device(ARef<device::Device>); Opaque<bindings::pci_dev>,
PhantomData<Ctx>,
);
/// A PCI BAR to perform I/O-Operations on. /// A PCI BAR to perform I/O-Operations on.
/// ///
@ -256,13 +262,13 @@ pub trait Driver {
/// `Bar` always holds an `IoRaw` inststance that holds a valid pointer to the start of the I/O /// `Bar` always holds an `IoRaw` inststance that holds a valid pointer to the start of the I/O
/// memory mapped PCI bar and its size. /// memory mapped PCI bar and its size.
pub struct Bar<const SIZE: usize = 0> { pub struct Bar<const SIZE: usize = 0> {
pdev: Device, pdev: ARef<Device>,
io: IoRaw<SIZE>, io: IoRaw<SIZE>,
num: i32, num: i32,
} }
impl<const SIZE: usize> Bar<SIZE> { impl<const SIZE: usize> Bar<SIZE> {
fn new(pdev: Device, num: u32, name: &CStr) -> Result<Self> { fn new(pdev: &Device, num: u32, name: &CStr) -> Result<Self> {
let len = pdev.resource_len(num)?; let len = pdev.resource_len(num)?;
if len == 0 { if len == 0 {
return Err(ENOMEM); return Err(ENOMEM);
@ -300,12 +306,16 @@ fn new(pdev: Device, num: u32, name: &CStr) -> Result<Self> {
// `pdev` is valid by the invariants of `Device`. // `pdev` is valid by the invariants of `Device`.
// `ioptr` is guaranteed to be the start of a valid I/O mapped memory region. // `ioptr` is guaranteed to be the start of a valid I/O mapped memory region.
// `num` is checked for validity by a previous call to `Device::resource_len`. // `num` is checked for validity by a previous call to `Device::resource_len`.
unsafe { Self::do_release(&pdev, ioptr, num) }; unsafe { Self::do_release(pdev, ioptr, num) };
return Err(err); return Err(err);
} }
}; };
Ok(Bar { pdev, io, num }) Ok(Bar {
pdev: pdev.into(),
io,
num,
})
} }
/// # Safety /// # Safety
@ -351,20 +361,8 @@ fn deref(&self) -> &Self::Target {
} }
impl Device { impl Device {
/// Create a PCI Device instance from an existing `device::Device`.
///
/// # Safety
///
/// `dev` must be an `ARef<device::Device>` whose underlying `bindings::device` is a member of
/// a `bindings::pci_dev`.
pub unsafe fn from_dev(dev: ARef<device::Device>) -> Self {
Self(dev)
}
fn as_raw(&self) -> *mut bindings::pci_dev { fn as_raw(&self) -> *mut bindings::pci_dev {
// SAFETY: By the type invariant `self.0.as_raw` is a pointer to the `struct device` self.0.get()
// embedded in `struct pci_dev`.
unsafe { container_of!(self.0.as_raw(), bindings::pci_dev, dev) as _ }
} }
/// Returns the PCI vendor ID. /// Returns the PCI vendor ID.
@ -379,23 +377,6 @@ pub fn device_id(&self) -> u16 {
unsafe { (*self.as_raw()).device } unsafe { (*self.as_raw()).device }
} }
/// Enable memory resources for this device.
pub fn enable_device_mem(&self) -> Result {
// SAFETY: `self.as_raw` is guaranteed to be a pointer to a valid `struct pci_dev`.
let ret = unsafe { bindings::pci_enable_device_mem(self.as_raw()) };
if ret != 0 {
Err(Error::from_errno(ret))
} else {
Ok(())
}
}
/// Enable bus-mastering for this device.
pub fn set_master(&self) {
// SAFETY: `self.as_raw` is guaranteed to be a pointer to a valid `struct pci_dev`.
unsafe { bindings::pci_set_master(self.as_raw()) };
}
/// Returns the size of the given PCI bar resource. /// Returns the size of the given PCI bar resource.
pub fn resource_len(&self, bar: u32) -> Result<bindings::resource_size_t> { pub fn resource_len(&self, bar: u32) -> Result<bindings::resource_size_t> {
if !Bar::index_is_valid(bar) { if !Bar::index_is_valid(bar) {
@ -415,7 +396,7 @@ pub fn iomap_region_sized<const SIZE: usize>(
bar: u32, bar: u32,
name: &CStr, name: &CStr,
) -> Result<Devres<Bar<SIZE>>> { ) -> Result<Devres<Bar<SIZE>>> {
let bar = Bar::<SIZE>::new(self.clone(), bar, name)?; let bar = Bar::<SIZE>::new(self, bar, name)?;
let devres = Devres::new(self.as_ref(), bar, GFP_KERNEL)?; let devres = Devres::new(self.as_ref(), bar, GFP_KERNEL)?;
Ok(devres) Ok(devres)
@ -427,8 +408,67 @@ pub fn iomap_region(&self, bar: u32, name: &CStr) -> Result<Devres<Bar>> {
} }
} }
impl AsRef<device::Device> for Device { impl Device<device::Core> {
fn as_ref(&self) -> &device::Device { /// Enable memory resources for this device.
&self.0 pub fn enable_device_mem(&self) -> Result {
// SAFETY: `self.as_raw` is guaranteed to be a pointer to a valid `struct pci_dev`.
to_result(unsafe { bindings::pci_enable_device_mem(self.as_raw()) })
}
/// Enable bus-mastering for this device.
pub fn set_master(&self) {
// SAFETY: `self.as_raw` is guaranteed to be a pointer to a valid `struct pci_dev`.
unsafe { bindings::pci_set_master(self.as_raw()) };
} }
} }
impl Deref for Device<device::Core> {
type Target = Device;
fn deref(&self) -> &Self::Target {
let ptr: *const Self = self;
// CAST: `Device<Ctx>` is a transparent wrapper of `Opaque<bindings::pci_dev>`.
let ptr = ptr.cast::<Device>();
// SAFETY: `ptr` was derived from `&self`.
unsafe { &*ptr }
}
}
impl From<&Device<device::Core>> for ARef<Device> {
fn from(dev: &Device<device::Core>) -> Self {
(&**dev).into()
}
}
// SAFETY: Instances of `Device` are always reference-counted.
unsafe impl crate::types::AlwaysRefCounted for Device {
fn inc_ref(&self) {
// SAFETY: The existence of a shared reference guarantees that the refcount is non-zero.
unsafe { bindings::pci_dev_get(self.as_raw()) };
}
unsafe fn dec_ref(obj: NonNull<Self>) {
// SAFETY: The safety requirements guarantee that the refcount is non-zero.
unsafe { bindings::pci_dev_put(obj.cast().as_ptr()) }
}
}
impl AsRef<device::Device> for Device {
fn as_ref(&self) -> &device::Device {
// SAFETY: By the type invariant of `Self`, `self.as_raw()` is a pointer to a valid
// `struct pci_dev`.
let dev = unsafe { addr_of_mut!((*self.as_raw()).dev) };
// SAFETY: `dev` points to a valid `struct device`.
unsafe { device::Device::as_ref(dev) }
}
}
// SAFETY: A `Device` is always reference-counted and can be released from any thread.
unsafe impl Send for Device {}
// SAFETY: `Device` can be shared among threads because all methods of `Device`
// (i.e. `Device<Normal>) are thread safe.
unsafe impl Sync for Device {}

View File

@ -5,7 +5,7 @@
//! C header: [`include/linux/platform_device.h`](srctree/include/linux/platform_device.h) //! C header: [`include/linux/platform_device.h`](srctree/include/linux/platform_device.h)
use crate::{ use crate::{
bindings, container_of, device, driver, bindings, device, driver,
error::{to_result, Result}, error::{to_result, Result},
of, of,
prelude::*, prelude::*,
@ -14,7 +14,11 @@
ThisModule, ThisModule,
}; };
use core::ptr::addr_of_mut; use core::{
marker::PhantomData,
ops::Deref,
ptr::{addr_of_mut, NonNull},
};
/// An adapter for the registration of platform drivers. /// An adapter for the registration of platform drivers.
pub struct Adapter<T: Driver>(T); pub struct Adapter<T: Driver>(T);
@ -54,14 +58,14 @@ unsafe fn unregister(pdrv: &Opaque<Self::RegType>) {
impl<T: Driver + 'static> Adapter<T> { impl<T: Driver + 'static> Adapter<T> {
extern "C" fn probe_callback(pdev: *mut bindings::platform_device) -> kernel::ffi::c_int { extern "C" fn probe_callback(pdev: *mut bindings::platform_device) -> kernel::ffi::c_int {
// SAFETY: The platform bus only ever calls the probe callback with a valid `pdev`. // SAFETY: The platform bus only ever calls the probe callback with a valid pointer to a
let dev = unsafe { device::Device::get_device(addr_of_mut!((*pdev).dev)) }; // `struct platform_device`.
// SAFETY: `dev` is guaranteed to be embedded in a valid `struct platform_device` by the //
// call above. // INVARIANT: `pdev` is valid for the duration of `probe_callback()`.
let mut pdev = unsafe { Device::from_dev(dev) }; let pdev = unsafe { &*pdev.cast::<Device<device::Core>>() };
let info = <Self as driver::Adapter>::id_info(pdev.as_ref()); let info = <Self as driver::Adapter>::id_info(pdev.as_ref());
match T::probe(&mut pdev, info) { match T::probe(pdev, info) {
Ok(data) => { Ok(data) => {
// Let the `struct platform_device` own a reference of the driver's private data. // Let the `struct platform_device` own a reference of the driver's private data.
// SAFETY: By the type invariant `pdev.as_raw` returns a valid pointer to a // SAFETY: By the type invariant `pdev.as_raw` returns a valid pointer to a
@ -120,7 +124,7 @@ macro_rules! module_platform_driver {
/// # Example /// # Example
/// ///
///``` ///```
/// # use kernel::{bindings, c_str, of, platform}; /// # use kernel::{bindings, c_str, device::Core, of, platform};
/// ///
/// struct MyDriver; /// struct MyDriver;
/// ///
@ -138,14 +142,14 @@ macro_rules! module_platform_driver {
/// const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE); /// const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE);
/// ///
/// fn probe( /// fn probe(
/// _pdev: &mut platform::Device, /// _pdev: &platform::Device<Core>,
/// _id_info: Option<&Self::IdInfo>, /// _id_info: Option<&Self::IdInfo>,
/// ) -> Result<Pin<KBox<Self>>> { /// ) -> Result<Pin<KBox<Self>>> {
/// Err(ENODEV) /// Err(ENODEV)
/// } /// }
/// } /// }
///``` ///```
pub trait Driver { pub trait Driver: Send {
/// The type holding driver private data about each device id supported by the driver. /// The type holding driver private data about each device id supported by the driver.
/// ///
/// TODO: Use associated_type_defaults once stabilized: /// TODO: Use associated_type_defaults once stabilized:
@ -160,41 +164,79 @@ pub trait Driver {
/// ///
/// Called when a new platform device is added or discovered. /// Called when a new platform device is added or discovered.
/// Implementers should attempt to initialize the device here. /// Implementers should attempt to initialize the device here.
fn probe(dev: &mut Device, id_info: Option<&Self::IdInfo>) -> Result<Pin<KBox<Self>>>; fn probe(dev: &Device<device::Core>, id_info: Option<&Self::IdInfo>)
-> Result<Pin<KBox<Self>>>;
} }
/// The platform device representation. /// The platform device representation.
/// ///
/// A platform device is based on an always reference counted `device:Device` instance. Cloning a /// This structure represents the Rust abstraction for a C `struct platform_device`. The
/// platform device, hence, also increments the base device' reference count. /// implementation abstracts the usage of an already existing C `struct platform_device` within Rust
/// code that we get passed from the C side.
/// ///
/// # Invariants /// # Invariants
/// ///
/// `Device` holds a valid reference of `ARef<device::Device>` whose underlying `struct device` is a /// A [`Device`] instance represents a valid `struct platform_device` created by the C portion of
/// member of a `struct platform_device`. /// the kernel.
#[derive(Clone)] #[repr(transparent)]
pub struct Device(ARef<device::Device>); pub struct Device<Ctx: device::DeviceContext = device::Normal>(
Opaque<bindings::platform_device>,
PhantomData<Ctx>,
);
impl Device { impl Device {
/// Convert a raw kernel device into a `Device` fn as_raw(&self) -> *mut bindings::platform_device {
/// self.0.get()
/// # Safety }
/// }
/// `dev` must be an `Aref<device::Device>` whose underlying `bindings::device` is a member of a
/// `bindings::platform_device`. impl Deref for Device<device::Core> {
unsafe fn from_dev(dev: ARef<device::Device>) -> Self { type Target = Device;
Self(dev)
fn deref(&self) -> &Self::Target {
let ptr: *const Self = self;
// CAST: `Device<Ctx>` is a transparent wrapper of `Opaque<bindings::platform_device>`.
let ptr = ptr.cast::<Device>();
// SAFETY: `ptr` was derived from `&self`.
unsafe { &*ptr }
}
}
impl From<&Device<device::Core>> for ARef<Device> {
fn from(dev: &Device<device::Core>) -> Self {
(&**dev).into()
}
}
// SAFETY: Instances of `Device` are always reference-counted.
unsafe impl crate::types::AlwaysRefCounted for Device {
fn inc_ref(&self) {
// SAFETY: The existence of a shared reference guarantees that the refcount is non-zero.
unsafe { bindings::get_device(self.as_ref().as_raw()) };
} }
fn as_raw(&self) -> *mut bindings::platform_device { unsafe fn dec_ref(obj: NonNull<Self>) {
// SAFETY: By the type invariant `self.0.as_raw` is a pointer to the `struct device` // SAFETY: The safety requirements guarantee that the refcount is non-zero.
// embedded in `struct platform_device`. unsafe { bindings::platform_device_put(obj.cast().as_ptr()) }
unsafe { container_of!(self.0.as_raw(), bindings::platform_device, dev) }.cast_mut()
} }
} }
impl AsRef<device::Device> for Device { impl AsRef<device::Device> for Device {
fn as_ref(&self) -> &device::Device { fn as_ref(&self) -> &device::Device {
&self.0 // SAFETY: By the type invariant of `Self`, `self.as_raw()` is a pointer to a valid
// `struct platform_device`.
let dev = unsafe { addr_of_mut!((*self.as_raw()).dev) };
// SAFETY: `dev` points to a valid `struct device`.
unsafe { device::Device::as_ref(dev) }
} }
} }
// SAFETY: A `Device` is always reference-counted and can be released from any thread.
unsafe impl Send for Device {}
// SAFETY: `Device` can be shared among threads because all methods of `Device`
// (i.e. `Device<Normal>) are thread safe.
unsafe impl Sync for Device {}

View File

@ -4,10 +4,10 @@
//! //!
//! To make this driver probe, QEMU must be run with `-device pci-testdev`. //! To make this driver probe, QEMU must be run with `-device pci-testdev`.
use kernel::{bindings, dma::CoherentAllocation, pci, prelude::*}; use kernel::{bindings, device::Core, dma::CoherentAllocation, pci, prelude::*, types::ARef};
struct DmaSampleDriver { struct DmaSampleDriver {
pdev: pci::Device, pdev: ARef<pci::Device>,
ca: CoherentAllocation<MyStruct>, ca: CoherentAllocation<MyStruct>,
} }
@ -48,7 +48,7 @@ impl pci::Driver for DmaSampleDriver {
type IdInfo = (); type IdInfo = ();
const ID_TABLE: pci::IdTable<Self::IdInfo> = &PCI_TABLE; const ID_TABLE: pci::IdTable<Self::IdInfo> = &PCI_TABLE;
fn probe(pdev: &mut pci::Device, _info: &Self::IdInfo) -> Result<Pin<KBox<Self>>> { fn probe(pdev: &pci::Device<Core>, _info: &Self::IdInfo) -> Result<Pin<KBox<Self>>> {
dev_info!(pdev.as_ref(), "Probe DMA test driver.\n"); dev_info!(pdev.as_ref(), "Probe DMA test driver.\n");
let ca: CoherentAllocation<MyStruct> = let ca: CoherentAllocation<MyStruct> =
@ -64,7 +64,7 @@ fn probe(pdev: &mut pci::Device, _info: &Self::IdInfo) -> Result<Pin<KBox<Self>>
let drvdata = KBox::new( let drvdata = KBox::new(
Self { Self {
pdev: pdev.clone(), pdev: pdev.into(),
ca, ca,
}, },
GFP_KERNEL, GFP_KERNEL,

View File

@ -20,7 +20,7 @@ impl Module for SampleModule {
fn init(_module: &'static ThisModule) -> Result<Self> { fn init(_module: &'static ThisModule) -> Result<Self> {
pr_info!("Initialising Rust Faux Device Sample\n"); pr_info!("Initialising Rust Faux Device Sample\n");
let reg = faux::Registration::new(c_str!("rust-faux-sample-device"))?; let reg = faux::Registration::new(c_str!("rust-faux-sample-device"), None)?;
dev_info!(reg.as_ref(), "Hello from faux device!\n"); dev_info!(reg.as_ref(), "Hello from faux device!\n");

View File

@ -4,7 +4,7 @@
//! //!
//! To make this driver probe, QEMU must be run with `-device pci-testdev`. //! To make this driver probe, QEMU must be run with `-device pci-testdev`.
use kernel::{bindings, c_str, devres::Devres, pci, prelude::*}; use kernel::{bindings, c_str, device::Core, devres::Devres, pci, prelude::*, types::ARef};
struct Regs; struct Regs;
@ -26,7 +26,7 @@ impl TestIndex {
} }
struct SampleDriver { struct SampleDriver {
pdev: pci::Device, pdev: ARef<pci::Device>,
bar: Devres<Bar0>, bar: Devres<Bar0>,
} }
@ -43,17 +43,17 @@ struct SampleDriver {
impl SampleDriver { impl SampleDriver {
fn testdev(index: &TestIndex, bar: &Bar0) -> Result<u32> { fn testdev(index: &TestIndex, bar: &Bar0) -> Result<u32> {
// Select the test. // Select the test.
bar.writeb(index.0, Regs::TEST); bar.write8(index.0, Regs::TEST);
let offset = u32::from_le(bar.readl(Regs::OFFSET)) as usize; let offset = u32::from_le(bar.read32(Regs::OFFSET)) as usize;
let data = bar.readb(Regs::DATA); let data = bar.read8(Regs::DATA);
// Write `data` to `offset` to increase `count` by one. // Write `data` to `offset` to increase `count` by one.
// //
// Note that we need `try_writeb`, since `offset` can't be checked at compile-time. // Note that we need `try_write8`, since `offset` can't be checked at compile-time.
bar.try_writeb(data, offset)?; bar.try_write8(data, offset)?;
Ok(bar.readl(Regs::COUNT)) Ok(bar.read32(Regs::COUNT))
} }
} }
@ -62,7 +62,7 @@ impl pci::Driver for SampleDriver {
const ID_TABLE: pci::IdTable<Self::IdInfo> = &PCI_TABLE; const ID_TABLE: pci::IdTable<Self::IdInfo> = &PCI_TABLE;
fn probe(pdev: &mut pci::Device, info: &Self::IdInfo) -> Result<Pin<KBox<Self>>> { fn probe(pdev: &pci::Device<Core>, info: &Self::IdInfo) -> Result<Pin<KBox<Self>>> {
dev_dbg!( dev_dbg!(
pdev.as_ref(), pdev.as_ref(),
"Probe Rust PCI driver sample (PCI ID: 0x{:x}, 0x{:x}).\n", "Probe Rust PCI driver sample (PCI ID: 0x{:x}, 0x{:x}).\n",
@ -77,7 +77,7 @@ fn probe(pdev: &mut pci::Device, info: &Self::IdInfo) -> Result<Pin<KBox<Self>>>
let drvdata = KBox::new( let drvdata = KBox::new(
Self { Self {
pdev: pdev.clone(), pdev: pdev.into(),
bar, bar,
}, },
GFP_KERNEL, GFP_KERNEL,

View File

@ -2,10 +2,10 @@
//! Rust Platform driver sample. //! Rust Platform driver sample.
use kernel::{c_str, of, platform, prelude::*}; use kernel::{c_str, device::Core, of, platform, prelude::*, types::ARef};
struct SampleDriver { struct SampleDriver {
pdev: platform::Device, pdev: ARef<platform::Device>,
} }
struct Info(u32); struct Info(u32);
@ -21,14 +21,17 @@ impl platform::Driver for SampleDriver {
type IdInfo = Info; type IdInfo = Info;
const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE); const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE);
fn probe(pdev: &mut platform::Device, info: Option<&Self::IdInfo>) -> Result<Pin<KBox<Self>>> { fn probe(
pdev: &platform::Device<Core>,
info: Option<&Self::IdInfo>,
) -> Result<Pin<KBox<Self>>> {
dev_dbg!(pdev.as_ref(), "Probe Rust Platform driver sample.\n"); dev_dbg!(pdev.as_ref(), "Probe Rust Platform driver sample.\n");
if let Some(info) = info { if let Some(info) = info {
dev_info!(pdev.as_ref(), "Probed with info: '{}'.\n", info.0); dev_info!(pdev.as_ref(), "Probed with info: '{}'.\n", info.0);
} }
let drvdata = KBox::new(Self { pdev: pdev.clone() }, GFP_KERNEL)?; let drvdata = KBox::new(Self { pdev: pdev.into() }, GFP_KERNEL)?;
Ok(drvdata.into()) Ok(drvdata.into())
} }

View File

@ -3587,10 +3587,13 @@ static int selinux_kernfs_init_security(struct kernfs_node *kn_dir,
newsid = tsec->create_sid; newsid = tsec->create_sid;
} else { } else {
u16 secclass = inode_mode_to_security_class(kn->mode); u16 secclass = inode_mode_to_security_class(kn->mode);
const char *kn_name;
struct qstr q; struct qstr q;
q.name = kn->name; /* kn is fresh, can't be renamed, name goes not away */
q.hash_len = hashlen_string(kn_dir, kn->name); kn_name = rcu_dereference_check(kn->name, true);
q.name = kn_name;
q.hash_len = hashlen_string(kn_dir, kn_name);
rc = security_transition_sid(tsec->sid, rc = security_transition_sid(tsec->sid,
parent_sid, secclass, &q, parent_sid, secclass, &q,

View File

@ -223,7 +223,7 @@ static INLINE void* read_full_cgroup_path(struct kernfs_node* cgroup_node,
if (bpf_cmp_likely(filepart_length, <=, MAX_PATH)) { if (bpf_cmp_likely(filepart_length, <=, MAX_PATH)) {
payload += filepart_length; payload += filepart_length;
} }
cgroup_node = BPF_CORE_READ(cgroup_node, parent); cgroup_node = BPF_CORE_READ(cgroup_node, __parent);
} }
return payload; return payload;
} }