Driver core patches for 4.15-rc1

Here is the set of driver core / debugfs patches for 4.15-rc1.
 
 Not many here, mostly all are debugfs fixes to resolve some
 long-reported problems with files going away with references to them in
 userspace.  There's also some SPDX cleanups for the debugfs code, as
 well as a few other minor driver core changes for issues reported by
 people.
 
 All of these have been in linux-next for a week or more with no reported
 issues.
 
 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 -----BEGIN PGP SIGNATURE-----
 
 iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCWg2NCA8cZ3JlZ0Brcm9h
 aC5jb20ACgkQMUfUDdst+ymUNgCfYq434CFh+YtwITBNYdqkFYFf0ZAAn3qfhh2+
 M3rmZzwk2MKBvNQ2npvt
 =/8+Y
 -----END PGP SIGNATURE-----

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

Pull driver core updates from Greg KH:
 "Here is the set of driver core / debugfs patches for 4.15-rc1.

  Not many here, mostly all are debugfs fixes to resolve some
  long-reported problems with files going away with references to them
  in userspace. There's also some SPDX cleanups for the debugfs code, as
  well as a few other minor driver core changes for issues reported by
  people.

  All of these have been in linux-next for a week or more with no
  reported issues"

* tag 'driver-core-4.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core:
  driver core: Fix device link deferred probe
  debugfs: Remove redundant license text
  debugfs: add SPDX identifiers to all debugfs files
  debugfs: defer debugfs_fsdata allocation to first usage
  debugfs: call debugfs_real_fops() only after debugfs_file_get()
  debugfs: purge obsolete SRCU based removal protection
  IB/hfi1: convert to debugfs_file_get() and -put()
  debugfs: convert to debugfs_file_get() and -put()
  debugfs: debugfs_real_fops(): drop __must_hold sparse annotation
  debugfs: implement per-file removal protection
  debugfs: add support for more elaborate ->d_fsdata
  driver core: Move device_links_purge() after bus_remove_device()
  arch_topology: Fix section miss match warning due to free_raw_capacity()
  driver-core: pr_err() strings should end with newlines
This commit is contained in:
Linus Torvalds 2017-11-16 08:55:30 -08:00
commit b9743042b3
10 changed files with 217 additions and 166 deletions

View File

@ -105,7 +105,7 @@ subsys_initcall(register_cpu_capacity_sysctl);
static u32 capacity_scale; static u32 capacity_scale;
static u32 *raw_capacity; static u32 *raw_capacity;
static int __init free_raw_capacity(void) static int free_raw_capacity(void)
{ {
kfree(raw_capacity); kfree(raw_capacity);
raw_capacity = NULL; raw_capacity = NULL;

View File

@ -1958,7 +1958,6 @@ void device_del(struct device *dev)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier, blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_DEL_DEVICE, dev); BUS_NOTIFY_DEL_DEVICE, dev);
device_links_purge(dev);
dpm_sysfs_remove(dev); dpm_sysfs_remove(dev);
if (parent) if (parent)
klist_del(&dev->p->knode_parent); klist_del(&dev->p->knode_parent);
@ -1986,6 +1985,7 @@ void device_del(struct device *dev)
device_pm_remove(dev); device_pm_remove(dev);
driver_deferred_probe_del(dev); driver_deferred_probe_del(dev);
device_remove_properties(dev); device_remove_properties(dev);
device_links_purge(dev);
/* Notify the platform of the removal, in case they /* Notify the platform of the removal, in case they
* need to do anything... * need to do anything...

View File

@ -350,6 +350,15 @@ EXPORT_SYMBOL_GPL(device_bind_driver);
static atomic_t probe_count = ATOMIC_INIT(0); static atomic_t probe_count = ATOMIC_INIT(0);
static DECLARE_WAIT_QUEUE_HEAD(probe_waitqueue); static DECLARE_WAIT_QUEUE_HEAD(probe_waitqueue);
static void driver_deferred_probe_add_trigger(struct device *dev,
int local_trigger_count)
{
driver_deferred_probe_add(dev);
/* Did a trigger occur while probing? Need to re-trigger if yes */
if (local_trigger_count != atomic_read(&deferred_trigger_count))
driver_deferred_probe_trigger();
}
static int really_probe(struct device *dev, struct device_driver *drv) static int really_probe(struct device *dev, struct device_driver *drv)
{ {
int ret = -EPROBE_DEFER; int ret = -EPROBE_DEFER;
@ -369,6 +378,8 @@ static int really_probe(struct device *dev, struct device_driver *drv)
} }
ret = device_links_check_suppliers(dev); ret = device_links_check_suppliers(dev);
if (ret == -EPROBE_DEFER)
driver_deferred_probe_add_trigger(dev, local_trigger_count);
if (ret) if (ret)
return ret; return ret;
@ -470,10 +481,7 @@ pinctrl_bind_failed:
case -EPROBE_DEFER: case -EPROBE_DEFER:
/* Driver requested deferred probing */ /* Driver requested deferred probing */
dev_dbg(dev, "Driver %s requests probe deferral\n", drv->name); dev_dbg(dev, "Driver %s requests probe deferral\n", drv->name);
driver_deferred_probe_add(dev); driver_deferred_probe_add_trigger(dev, local_trigger_count);
/* Did a trigger occur while probing? Need to re-trigger if yes */
if (local_trigger_count != atomic_read(&deferred_trigger_count))
driver_deferred_probe_trigger();
break; break;
case -ENODEV: case -ENODEV:
case -ENXIO: case -ENXIO:

View File

@ -64,7 +64,7 @@ static int __init test_async_probe_init(void)
NULL, 0); NULL, 0);
if (IS_ERR(async_dev_1)) { if (IS_ERR(async_dev_1)) {
error = PTR_ERR(async_dev_1); error = PTR_ERR(async_dev_1);
pr_err("failed to create async_dev_1: %d", error); pr_err("failed to create async_dev_1: %d\n", error);
return error; return error;
} }
@ -91,7 +91,7 @@ static int __init test_async_probe_init(void)
NULL, 0); NULL, 0);
if (IS_ERR(async_dev_2)) { if (IS_ERR(async_dev_2)) {
error = PTR_ERR(async_dev_2); error = PTR_ERR(async_dev_2);
pr_err("failed to create async_dev_2: %d", error); pr_err("failed to create async_dev_2: %d\n", error);
goto err_unregister_async_driver; goto err_unregister_async_driver;
} }
@ -118,7 +118,7 @@ static int __init test_async_probe_init(void)
NULL, 0); NULL, 0);
if (IS_ERR(sync_dev_1)) { if (IS_ERR(sync_dev_1)) {
error = PTR_ERR(sync_dev_1); error = PTR_ERR(sync_dev_1);
pr_err("failed to create sync_dev_1: %d", error); pr_err("failed to create sync_dev_1: %d\n", error);
goto err_unregister_sync_driver; goto err_unregister_sync_driver;
} }

View File

@ -71,13 +71,13 @@ static ssize_t hfi1_seq_read(
loff_t *ppos) loff_t *ppos)
{ {
struct dentry *d = file->f_path.dentry; struct dentry *d = file->f_path.dentry;
int srcu_idx;
ssize_t r; ssize_t r;
r = debugfs_use_file_start(d, &srcu_idx); r = debugfs_file_get(d);
if (likely(!r)) if (unlikely(r))
r = seq_read(file, buf, size, ppos); return r;
debugfs_use_file_finish(srcu_idx); r = seq_read(file, buf, size, ppos);
debugfs_file_put(d);
return r; return r;
} }
@ -87,13 +87,13 @@ static loff_t hfi1_seq_lseek(
int whence) int whence)
{ {
struct dentry *d = file->f_path.dentry; struct dentry *d = file->f_path.dentry;
int srcu_idx;
loff_t r; loff_t r;
r = debugfs_use_file_start(d, &srcu_idx); r = debugfs_file_get(d);
if (likely(!r)) if (unlikely(r))
r = seq_lseek(file, offset, whence); return r;
debugfs_use_file_finish(srcu_idx); r = seq_lseek(file, offset, whence);
debugfs_file_put(d);
return r; return r;
} }

View File

@ -1,16 +1,12 @@
// SPDX-License-Identifier: GPL-2.0
/* /*
* file.c - part of debugfs, a tiny little debug file system * file.c - part of debugfs, a tiny little debug file system
* *
* Copyright (C) 2004 Greg Kroah-Hartman <greg@kroah.com> * Copyright (C) 2004 Greg Kroah-Hartman <greg@kroah.com>
* Copyright (C) 2004 IBM Inc. * Copyright (C) 2004 IBM Inc.
* *
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 as published by the Free Software Foundation.
*
* debugfs is for people to use instead of /proc or /sys. * debugfs is for people to use instead of /proc or /sys.
* See Documentation/filesystems/ for more details. * See Documentation/filesystems/ for more details.
*
*/ */
#include <linux/module.h> #include <linux/module.h>
@ -22,7 +18,6 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/atomic.h> #include <linux/atomic.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/srcu.h>
#include <asm/poll.h> #include <asm/poll.h>
#include "internal.h" #include "internal.h"
@ -48,66 +43,108 @@ const struct file_operations debugfs_noop_file_operations = {
.llseek = noop_llseek, .llseek = noop_llseek,
}; };
#define F_DENTRY(filp) ((filp)->f_path.dentry)
const struct file_operations *debugfs_real_fops(const struct file *filp)
{
struct debugfs_fsdata *fsd = F_DENTRY(filp)->d_fsdata;
if ((unsigned long)fsd & DEBUGFS_FSDATA_IS_REAL_FOPS_BIT) {
/*
* Urgh, we've been called w/o a protecting
* debugfs_file_get().
*/
WARN_ON(1);
return NULL;
}
return fsd->real_fops;
}
EXPORT_SYMBOL_GPL(debugfs_real_fops);
/** /**
* debugfs_use_file_start - mark the beginning of file data access * debugfs_file_get - mark the beginning of file data access
* @dentry: the dentry object whose data is being accessed. * @dentry: the dentry object whose data is being accessed.
* @srcu_idx: a pointer to some memory to store a SRCU index in.
* *
* Up to a matching call to debugfs_use_file_finish(), any * Up to a matching call to debugfs_file_put(), any successive call
* successive call into the file removing functions debugfs_remove() * into the file removing functions debugfs_remove() and
* and debugfs_remove_recursive() will block. Since associated private * debugfs_remove_recursive() will block. Since associated private
* file data may only get freed after a successful return of any of * file data may only get freed after a successful return of any of
* the removal functions, you may safely access it after a successful * the removal functions, you may safely access it after a successful
* call to debugfs_use_file_start() without worrying about * call to debugfs_file_get() without worrying about lifetime issues.
* lifetime issues.
* *
* If -%EIO is returned, the file has already been removed and thus, * If -%EIO is returned, the file has already been removed and thus,
* it is not safe to access any of its data. If, on the other hand, * it is not safe to access any of its data. If, on the other hand,
* it is allowed to access the file data, zero is returned. * it is allowed to access the file data, zero is returned.
*
* Regardless of the return code, any call to
* debugfs_use_file_start() must be followed by a matching call
* to debugfs_use_file_finish().
*/ */
int debugfs_use_file_start(const struct dentry *dentry, int *srcu_idx) int debugfs_file_get(struct dentry *dentry)
__acquires(&debugfs_srcu)
{ {
*srcu_idx = srcu_read_lock(&debugfs_srcu); struct debugfs_fsdata *fsd;
barrier(); void *d_fsd;
d_fsd = READ_ONCE(dentry->d_fsdata);
if (!((unsigned long)d_fsd & DEBUGFS_FSDATA_IS_REAL_FOPS_BIT)) {
fsd = d_fsd;
} else {
fsd = kmalloc(sizeof(*fsd), GFP_KERNEL);
if (!fsd)
return -ENOMEM;
fsd->real_fops = (void *)((unsigned long)d_fsd &
~DEBUGFS_FSDATA_IS_REAL_FOPS_BIT);
refcount_set(&fsd->active_users, 1);
init_completion(&fsd->active_users_drained);
if (cmpxchg(&dentry->d_fsdata, d_fsd, fsd) != d_fsd) {
kfree(fsd);
fsd = READ_ONCE(dentry->d_fsdata);
}
}
/*
* In case of a successful cmpxchg() above, this check is
* strictly necessary and must follow it, see the comment in
* __debugfs_remove_file().
* OTOH, if the cmpxchg() hasn't been executed or wasn't
* successful, this serves the purpose of not starving
* removers.
*/
if (d_unlinked(dentry)) if (d_unlinked(dentry))
return -EIO; return -EIO;
if (!refcount_inc_not_zero(&fsd->active_users))
return -EIO;
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(debugfs_use_file_start); EXPORT_SYMBOL_GPL(debugfs_file_get);
/** /**
* debugfs_use_file_finish - mark the end of file data access * debugfs_file_put - mark the end of file data access
* @srcu_idx: the SRCU index "created" by a former call to * @dentry: the dentry object formerly passed to
* debugfs_use_file_start(). * debugfs_file_get().
* *
* Allow any ongoing concurrent call into debugfs_remove() or * Allow any ongoing concurrent call into debugfs_remove() or
* debugfs_remove_recursive() blocked by a former call to * debugfs_remove_recursive() blocked by a former call to
* debugfs_use_file_start() to proceed and return to its caller. * debugfs_file_get() to proceed and return to its caller.
*/ */
void debugfs_use_file_finish(int srcu_idx) __releases(&debugfs_srcu) void debugfs_file_put(struct dentry *dentry)
{ {
srcu_read_unlock(&debugfs_srcu, srcu_idx); struct debugfs_fsdata *fsd = READ_ONCE(dentry->d_fsdata);
}
EXPORT_SYMBOL_GPL(debugfs_use_file_finish);
#define F_DENTRY(filp) ((filp)->f_path.dentry) if (refcount_dec_and_test(&fsd->active_users))
complete(&fsd->active_users_drained);
}
EXPORT_SYMBOL_GPL(debugfs_file_put);
static int open_proxy_open(struct inode *inode, struct file *filp) static int open_proxy_open(struct inode *inode, struct file *filp)
{ {
const struct dentry *dentry = F_DENTRY(filp); struct dentry *dentry = F_DENTRY(filp);
const struct file_operations *real_fops = NULL; const struct file_operations *real_fops = NULL;
int srcu_idx, r; int r;
r = debugfs_use_file_start(dentry, &srcu_idx); r = debugfs_file_get(dentry);
if (r) { if (r)
r = -ENOENT; return r == -EIO ? -ENOENT : r;
goto out;
}
real_fops = debugfs_real_fops(filp); real_fops = debugfs_real_fops(filp);
real_fops = fops_get(real_fops); real_fops = fops_get(real_fops);
@ -124,7 +161,7 @@ static int open_proxy_open(struct inode *inode, struct file *filp)
r = real_fops->open(inode, filp); r = real_fops->open(inode, filp);
out: out:
debugfs_use_file_finish(srcu_idx); debugfs_file_put(dentry);
return r; return r;
} }
@ -138,16 +175,16 @@ const struct file_operations debugfs_open_proxy_file_operations = {
#define FULL_PROXY_FUNC(name, ret_type, filp, proto, args) \ #define FULL_PROXY_FUNC(name, ret_type, filp, proto, args) \
static ret_type full_proxy_ ## name(proto) \ static ret_type full_proxy_ ## name(proto) \
{ \ { \
const struct dentry *dentry = F_DENTRY(filp); \ struct dentry *dentry = F_DENTRY(filp); \
const struct file_operations *real_fops = \ const struct file_operations *real_fops; \
debugfs_real_fops(filp); \
int srcu_idx; \
ret_type r; \ ret_type r; \
\ \
r = debugfs_use_file_start(dentry, &srcu_idx); \ r = debugfs_file_get(dentry); \
if (likely(!r)) \ if (unlikely(r)) \
r = real_fops->name(args); \ return r; \
debugfs_use_file_finish(srcu_idx); \ real_fops = debugfs_real_fops(filp); \
r = real_fops->name(args); \
debugfs_file_put(dentry); \
return r; \ return r; \
} }
@ -172,18 +209,16 @@ FULL_PROXY_FUNC(unlocked_ioctl, long, filp,
static unsigned int full_proxy_poll(struct file *filp, static unsigned int full_proxy_poll(struct file *filp,
struct poll_table_struct *wait) struct poll_table_struct *wait)
{ {
const struct dentry *dentry = F_DENTRY(filp); struct dentry *dentry = F_DENTRY(filp);
const struct file_operations *real_fops = debugfs_real_fops(filp);
int srcu_idx;
unsigned int r = 0; unsigned int r = 0;
const struct file_operations *real_fops;
if (debugfs_use_file_start(dentry, &srcu_idx)) { if (debugfs_file_get(dentry))
debugfs_use_file_finish(srcu_idx);
return POLLHUP; return POLLHUP;
}
real_fops = debugfs_real_fops(filp);
r = real_fops->poll(filp, wait); r = real_fops->poll(filp, wait);
debugfs_use_file_finish(srcu_idx); debugfs_file_put(dentry);
return r; return r;
} }
@ -227,16 +262,14 @@ static void __full_proxy_fops_init(struct file_operations *proxy_fops,
static int full_proxy_open(struct inode *inode, struct file *filp) static int full_proxy_open(struct inode *inode, struct file *filp)
{ {
const struct dentry *dentry = F_DENTRY(filp); struct dentry *dentry = F_DENTRY(filp);
const struct file_operations *real_fops = NULL; const struct file_operations *real_fops = NULL;
struct file_operations *proxy_fops = NULL; struct file_operations *proxy_fops = NULL;
int srcu_idx, r; int r;
r = debugfs_use_file_start(dentry, &srcu_idx); r = debugfs_file_get(dentry);
if (r) { if (r)
r = -ENOENT; return r == -EIO ? -ENOENT : r;
goto out;
}
real_fops = debugfs_real_fops(filp); real_fops = debugfs_real_fops(filp);
real_fops = fops_get(real_fops); real_fops = fops_get(real_fops);
@ -274,7 +307,7 @@ free_proxy:
kfree(proxy_fops); kfree(proxy_fops);
fops_put(real_fops); fops_put(real_fops);
out: out:
debugfs_use_file_finish(srcu_idx); debugfs_file_put(dentry);
return r; return r;
} }
@ -285,13 +318,14 @@ const struct file_operations debugfs_full_proxy_file_operations = {
ssize_t debugfs_attr_read(struct file *file, char __user *buf, ssize_t debugfs_attr_read(struct file *file, char __user *buf,
size_t len, loff_t *ppos) size_t len, loff_t *ppos)
{ {
struct dentry *dentry = F_DENTRY(file);
ssize_t ret; ssize_t ret;
int srcu_idx;
ret = debugfs_use_file_start(F_DENTRY(file), &srcu_idx); ret = debugfs_file_get(dentry);
if (likely(!ret)) if (unlikely(ret))
ret = simple_attr_read(file, buf, len, ppos); return ret;
debugfs_use_file_finish(srcu_idx); ret = simple_attr_read(file, buf, len, ppos);
debugfs_file_put(dentry);
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(debugfs_attr_read); EXPORT_SYMBOL_GPL(debugfs_attr_read);
@ -299,13 +333,14 @@ EXPORT_SYMBOL_GPL(debugfs_attr_read);
ssize_t debugfs_attr_write(struct file *file, const char __user *buf, ssize_t debugfs_attr_write(struct file *file, const char __user *buf,
size_t len, loff_t *ppos) size_t len, loff_t *ppos)
{ {
struct dentry *dentry = F_DENTRY(file);
ssize_t ret; ssize_t ret;
int srcu_idx;
ret = debugfs_use_file_start(F_DENTRY(file), &srcu_idx); ret = debugfs_file_get(dentry);
if (likely(!ret)) if (unlikely(ret))
ret = simple_attr_write(file, buf, len, ppos); return ret;
debugfs_use_file_finish(srcu_idx); ret = simple_attr_write(file, buf, len, ppos);
debugfs_file_put(dentry);
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(debugfs_attr_write); EXPORT_SYMBOL_GPL(debugfs_attr_write);
@ -739,14 +774,14 @@ ssize_t debugfs_read_file_bool(struct file *file, char __user *user_buf,
{ {
char buf[3]; char buf[3];
bool val; bool val;
int r, srcu_idx; int r;
struct dentry *dentry = F_DENTRY(file);
r = debugfs_use_file_start(F_DENTRY(file), &srcu_idx); r = debugfs_file_get(dentry);
if (likely(!r)) if (unlikely(r))
val = *(bool *)file->private_data;
debugfs_use_file_finish(srcu_idx);
if (r)
return r; return r;
val = *(bool *)file->private_data;
debugfs_file_put(dentry);
if (val) if (val)
buf[0] = 'Y'; buf[0] = 'Y';
@ -764,8 +799,9 @@ ssize_t debugfs_write_file_bool(struct file *file, const char __user *user_buf,
char buf[32]; char buf[32];
size_t buf_size; size_t buf_size;
bool bv; bool bv;
int r, srcu_idx; int r;
bool *val = file->private_data; bool *val = file->private_data;
struct dentry *dentry = F_DENTRY(file);
buf_size = min(count, (sizeof(buf)-1)); buf_size = min(count, (sizeof(buf)-1));
if (copy_from_user(buf, user_buf, buf_size)) if (copy_from_user(buf, user_buf, buf_size))
@ -773,12 +809,11 @@ ssize_t debugfs_write_file_bool(struct file *file, const char __user *user_buf,
buf[buf_size] = '\0'; buf[buf_size] = '\0';
if (strtobool(buf, &bv) == 0) { if (strtobool(buf, &bv) == 0) {
r = debugfs_use_file_start(F_DENTRY(file), &srcu_idx); r = debugfs_file_get(dentry);
if (likely(!r)) if (unlikely(r))
*val = bv;
debugfs_use_file_finish(srcu_idx);
if (r)
return r; return r;
*val = bv;
debugfs_file_put(dentry);
} }
return count; return count;
@ -840,14 +875,15 @@ static ssize_t read_file_blob(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos) size_t count, loff_t *ppos)
{ {
struct debugfs_blob_wrapper *blob = file->private_data; struct debugfs_blob_wrapper *blob = file->private_data;
struct dentry *dentry = F_DENTRY(file);
ssize_t r; ssize_t r;
int srcu_idx;
r = debugfs_use_file_start(F_DENTRY(file), &srcu_idx); r = debugfs_file_get(dentry);
if (likely(!r)) if (unlikely(r))
r = simple_read_from_buffer(user_buf, count, ppos, blob->data, return r;
blob->size); r = simple_read_from_buffer(user_buf, count, ppos, blob->data,
debugfs_use_file_finish(srcu_idx); blob->size);
debugfs_file_put(dentry);
return r; return r;
} }

View File

@ -1,16 +1,12 @@
// SPDX-License-Identifier: GPL-2.0
/* /*
* inode.c - part of debugfs, a tiny little debug file system * inode.c - part of debugfs, a tiny little debug file system
* *
* Copyright (C) 2004 Greg Kroah-Hartman <greg@kroah.com> * Copyright (C) 2004 Greg Kroah-Hartman <greg@kroah.com>
* Copyright (C) 2004 IBM Inc. * Copyright (C) 2004 IBM Inc.
* *
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 as published by the Free Software Foundation.
*
* debugfs is for people to use instead of /proc or /sys. * debugfs is for people to use instead of /proc or /sys.
* See ./Documentation/core-api/kernel-api.rst for more details. * See ./Documentation/core-api/kernel-api.rst for more details.
*
*/ */
#include <linux/module.h> #include <linux/module.h>
@ -27,14 +23,11 @@
#include <linux/parser.h> #include <linux/parser.h>
#include <linux/magic.h> #include <linux/magic.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/srcu.h>
#include "internal.h" #include "internal.h"
#define DEBUGFS_DEFAULT_MODE 0700 #define DEBUGFS_DEFAULT_MODE 0700
DEFINE_SRCU(debugfs_srcu);
static struct vfsmount *debugfs_mount; static struct vfsmount *debugfs_mount;
static int debugfs_mount_count; static int debugfs_mount_count;
static bool debugfs_registered; static bool debugfs_registered;
@ -185,6 +178,14 @@ static const struct super_operations debugfs_super_operations = {
.evict_inode = debugfs_evict_inode, .evict_inode = debugfs_evict_inode,
}; };
static void debugfs_release_dentry(struct dentry *dentry)
{
void *fsd = dentry->d_fsdata;
if (!((unsigned long)fsd & DEBUGFS_FSDATA_IS_REAL_FOPS_BIT))
kfree(dentry->d_fsdata);
}
static struct vfsmount *debugfs_automount(struct path *path) static struct vfsmount *debugfs_automount(struct path *path)
{ {
debugfs_automount_t f; debugfs_automount_t f;
@ -194,6 +195,7 @@ static struct vfsmount *debugfs_automount(struct path *path)
static const struct dentry_operations debugfs_dops = { static const struct dentry_operations debugfs_dops = {
.d_delete = always_delete_dentry, .d_delete = always_delete_dentry,
.d_release = debugfs_release_dentry,
.d_automount = debugfs_automount, .d_automount = debugfs_automount,
}; };
@ -358,7 +360,8 @@ static struct dentry *__debugfs_create_file(const char *name, umode_t mode,
inode->i_private = data; inode->i_private = data;
inode->i_fop = proxy_fops; inode->i_fop = proxy_fops;
dentry->d_fsdata = (void *)real_fops; dentry->d_fsdata = (void *)((unsigned long)real_fops |
DEBUGFS_FSDATA_IS_REAL_FOPS_BIT);
d_instantiate(dentry, inode); d_instantiate(dentry, inode);
fsnotify_create(d_inode(dentry->d_parent), dentry); fsnotify_create(d_inode(dentry->d_parent), dentry);
@ -615,18 +618,43 @@ struct dentry *debugfs_create_symlink(const char *name, struct dentry *parent,
} }
EXPORT_SYMBOL_GPL(debugfs_create_symlink); EXPORT_SYMBOL_GPL(debugfs_create_symlink);
static void __debugfs_remove_file(struct dentry *dentry, struct dentry *parent)
{
struct debugfs_fsdata *fsd;
simple_unlink(d_inode(parent), dentry);
d_delete(dentry);
/*
* Paired with the closing smp_mb() implied by a successful
* cmpxchg() in debugfs_file_get(): either
* debugfs_file_get() must see a dead dentry or we must see a
* debugfs_fsdata instance at ->d_fsdata here (or both).
*/
smp_mb();
fsd = READ_ONCE(dentry->d_fsdata);
if ((unsigned long)fsd & DEBUGFS_FSDATA_IS_REAL_FOPS_BIT)
return;
if (!refcount_dec_and_test(&fsd->active_users))
wait_for_completion(&fsd->active_users_drained);
}
static int __debugfs_remove(struct dentry *dentry, struct dentry *parent) static int __debugfs_remove(struct dentry *dentry, struct dentry *parent)
{ {
int ret = 0; int ret = 0;
if (simple_positive(dentry)) { if (simple_positive(dentry)) {
dget(dentry); dget(dentry);
if (d_is_dir(dentry)) if (!d_is_reg(dentry)) {
ret = simple_rmdir(d_inode(parent), dentry); if (d_is_dir(dentry))
else ret = simple_rmdir(d_inode(parent), dentry);
simple_unlink(d_inode(parent), dentry); else
if (!ret) simple_unlink(d_inode(parent), dentry);
d_delete(dentry); if (!ret)
d_delete(dentry);
} else {
__debugfs_remove_file(dentry, parent);
}
dput(dentry); dput(dentry);
} }
return ret; return ret;
@ -660,8 +688,6 @@ void debugfs_remove(struct dentry *dentry)
inode_unlock(d_inode(parent)); inode_unlock(d_inode(parent));
if (!ret) if (!ret)
simple_release_fs(&debugfs_mount, &debugfs_mount_count); simple_release_fs(&debugfs_mount, &debugfs_mount_count);
synchronize_srcu(&debugfs_srcu);
} }
EXPORT_SYMBOL_GPL(debugfs_remove); EXPORT_SYMBOL_GPL(debugfs_remove);
@ -735,8 +761,6 @@ void debugfs_remove_recursive(struct dentry *dentry)
if (!__debugfs_remove(child, parent)) if (!__debugfs_remove(child, parent))
simple_release_fs(&debugfs_mount, &debugfs_mount_count); simple_release_fs(&debugfs_mount, &debugfs_mount_count);
inode_unlock(d_inode(parent)); inode_unlock(d_inode(parent));
synchronize_srcu(&debugfs_srcu);
} }
EXPORT_SYMBOL_GPL(debugfs_remove_recursive); EXPORT_SYMBOL_GPL(debugfs_remove_recursive);

View File

@ -1,12 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
/* /*
* internal.h - declarations internal to debugfs * internal.h - declarations internal to debugfs
* *
* Copyright (C) 2016 Nicolai Stange <nicstange@gmail.com> * Copyright (C) 2016 Nicolai Stange <nicstange@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 as published by the Free Software Foundation.
*
*/ */
#ifndef _DEBUGFS_INTERNAL_H_ #ifndef _DEBUGFS_INTERNAL_H_
@ -19,4 +15,18 @@ extern const struct file_operations debugfs_noop_file_operations;
extern const struct file_operations debugfs_open_proxy_file_operations; extern const struct file_operations debugfs_open_proxy_file_operations;
extern const struct file_operations debugfs_full_proxy_file_operations; extern const struct file_operations debugfs_full_proxy_file_operations;
struct debugfs_fsdata {
const struct file_operations *real_fops;
refcount_t active_users;
struct completion active_users_drained;
};
/*
* A dentry's ->d_fsdata either points to the real fops or to a
* dynamically allocated debugfs_fsdata instance.
* In order to distinguish between these two cases, a real fops
* pointer gets its lowest bit set.
*/
#define DEBUGFS_FSDATA_IS_REAL_FOPS_BIT BIT(0)
#endif /* _DEBUGFS_INTERNAL_H_ */ #endif /* _DEBUGFS_INTERNAL_H_ */

View File

@ -1,13 +1,10 @@
// SPDX-License-Identifier: GPL-2.0
/* /*
* debugfs.h - a tiny little debug file system * debugfs.h - a tiny little debug file system
* *
* Copyright (C) 2004 Greg Kroah-Hartman <greg@kroah.com> * Copyright (C) 2004 Greg Kroah-Hartman <greg@kroah.com>
* Copyright (C) 2004 IBM Inc. * Copyright (C) 2004 IBM Inc.
* *
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 as published by the Free Software Foundation.
*
* debugfs is for people to use instead of /proc or /sys. * debugfs is for people to use instead of /proc or /sys.
* See Documentation/filesystems/ for more details. * See Documentation/filesystems/ for more details.
*/ */
@ -23,7 +20,6 @@
struct device; struct device;
struct file_operations; struct file_operations;
struct srcu_struct;
struct debugfs_blob_wrapper { struct debugfs_blob_wrapper {
void *data; void *data;
@ -43,25 +39,6 @@ struct debugfs_regset32 {
extern struct dentry *arch_debugfs_dir; extern struct dentry *arch_debugfs_dir;
extern struct srcu_struct debugfs_srcu;
/**
* debugfs_real_fops - getter for the real file operation
* @filp: a pointer to a struct file
*
* Must only be called under the protection established by
* debugfs_use_file_start().
*/
static inline const struct file_operations *debugfs_real_fops(const struct file *filp)
__must_hold(&debugfs_srcu)
{
/*
* Neither the pointer to the struct file_operations, nor its
* contents ever change -- srcu_dereference() is not needed here.
*/
return filp->f_path.dentry->d_fsdata;
}
#define DEFINE_DEBUGFS_ATTRIBUTE(__fops, __get, __set, __fmt) \ #define DEFINE_DEBUGFS_ATTRIBUTE(__fops, __get, __set, __fmt) \
static int __fops ## _open(struct inode *inode, struct file *file) \ static int __fops ## _open(struct inode *inode, struct file *file) \
{ \ { \
@ -107,10 +84,10 @@ struct dentry *debugfs_create_automount(const char *name,
void debugfs_remove(struct dentry *dentry); void debugfs_remove(struct dentry *dentry);
void debugfs_remove_recursive(struct dentry *dentry); void debugfs_remove_recursive(struct dentry *dentry);
int debugfs_use_file_start(const struct dentry *dentry, int *srcu_idx) const struct file_operations *debugfs_real_fops(const struct file *filp);
__acquires(&debugfs_srcu);
void debugfs_use_file_finish(int srcu_idx) __releases(&debugfs_srcu); int debugfs_file_get(struct dentry *dentry);
void debugfs_file_put(struct dentry *dentry);
ssize_t debugfs_attr_read(struct file *file, char __user *buf, ssize_t debugfs_attr_read(struct file *file, char __user *buf,
size_t len, loff_t *ppos); size_t len, loff_t *ppos);
@ -239,15 +216,12 @@ static inline void debugfs_remove(struct dentry *dentry)
static inline void debugfs_remove_recursive(struct dentry *dentry) static inline void debugfs_remove_recursive(struct dentry *dentry)
{ } { }
static inline int debugfs_use_file_start(const struct dentry *dentry, static inline int debugfs_file_get(struct dentry *dentry)
int *srcu_idx)
__acquires(&debugfs_srcu)
{ {
return 0; return 0;
} }
static inline void debugfs_use_file_finish(int srcu_idx) static inline void debugfs_file_put(struct dentry *dentry)
__releases(&debugfs_srcu)
{ } { }
static inline ssize_t debugfs_attr_read(struct file *file, char __user *buf, static inline ssize_t debugfs_attr_read(struct file *file, char __user *buf,

View File

@ -280,7 +280,6 @@ config PAGE_OWNER
config DEBUG_FS config DEBUG_FS
bool "Debug Filesystem" bool "Debug Filesystem"
select SRCU
help help
debugfs is a virtual file system that kernel developers use to put debugfs is a virtual file system that kernel developers use to put
debugging files into. Enable this option to be able to read and debugging files into. Enable this option to be able to read and