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:
commit
b9743042b3
|
@ -105,7 +105,7 @@ subsys_initcall(register_cpu_capacity_sysctl);
|
|||
static u32 capacity_scale;
|
||||
static u32 *raw_capacity;
|
||||
|
||||
static int __init free_raw_capacity(void)
|
||||
static int free_raw_capacity(void)
|
||||
{
|
||||
kfree(raw_capacity);
|
||||
raw_capacity = NULL;
|
||||
|
|
|
@ -1958,7 +1958,6 @@ void device_del(struct device *dev)
|
|||
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
|
||||
BUS_NOTIFY_DEL_DEVICE, dev);
|
||||
|
||||
device_links_purge(dev);
|
||||
dpm_sysfs_remove(dev);
|
||||
if (parent)
|
||||
klist_del(&dev->p->knode_parent);
|
||||
|
@ -1986,6 +1985,7 @@ void device_del(struct device *dev)
|
|||
device_pm_remove(dev);
|
||||
driver_deferred_probe_del(dev);
|
||||
device_remove_properties(dev);
|
||||
device_links_purge(dev);
|
||||
|
||||
/* Notify the platform of the removal, in case they
|
||||
* need to do anything...
|
||||
|
|
|
@ -350,6 +350,15 @@ EXPORT_SYMBOL_GPL(device_bind_driver);
|
|||
static atomic_t probe_count = ATOMIC_INIT(0);
|
||||
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)
|
||||
{
|
||||
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);
|
||||
if (ret == -EPROBE_DEFER)
|
||||
driver_deferred_probe_add_trigger(dev, local_trigger_count);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -470,10 +481,7 @@ pinctrl_bind_failed:
|
|||
case -EPROBE_DEFER:
|
||||
/* Driver requested deferred probing */
|
||||
dev_dbg(dev, "Driver %s requests probe deferral\n", drv->name);
|
||||
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();
|
||||
driver_deferred_probe_add_trigger(dev, local_trigger_count);
|
||||
break;
|
||||
case -ENODEV:
|
||||
case -ENXIO:
|
||||
|
|
|
@ -64,7 +64,7 @@ static int __init test_async_probe_init(void)
|
|||
NULL, 0);
|
||||
if (IS_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;
|
||||
}
|
||||
|
||||
|
@ -91,7 +91,7 @@ static int __init test_async_probe_init(void)
|
|||
NULL, 0);
|
||||
if (IS_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;
|
||||
}
|
||||
|
||||
|
@ -118,7 +118,7 @@ static int __init test_async_probe_init(void)
|
|||
NULL, 0);
|
||||
if (IS_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;
|
||||
}
|
||||
|
||||
|
|
|
@ -71,13 +71,13 @@ static ssize_t hfi1_seq_read(
|
|||
loff_t *ppos)
|
||||
{
|
||||
struct dentry *d = file->f_path.dentry;
|
||||
int srcu_idx;
|
||||
ssize_t r;
|
||||
|
||||
r = debugfs_use_file_start(d, &srcu_idx);
|
||||
if (likely(!r))
|
||||
r = seq_read(file, buf, size, ppos);
|
||||
debugfs_use_file_finish(srcu_idx);
|
||||
r = debugfs_file_get(d);
|
||||
if (unlikely(r))
|
||||
return r;
|
||||
r = seq_read(file, buf, size, ppos);
|
||||
debugfs_file_put(d);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
@ -87,13 +87,13 @@ static loff_t hfi1_seq_lseek(
|
|||
int whence)
|
||||
{
|
||||
struct dentry *d = file->f_path.dentry;
|
||||
int srcu_idx;
|
||||
loff_t r;
|
||||
|
||||
r = debugfs_use_file_start(d, &srcu_idx);
|
||||
if (likely(!r))
|
||||
r = seq_lseek(file, offset, whence);
|
||||
debugfs_use_file_finish(srcu_idx);
|
||||
r = debugfs_file_get(d);
|
||||
if (unlikely(r))
|
||||
return r;
|
||||
r = seq_lseek(file, offset, whence);
|
||||
debugfs_file_put(d);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,16 +1,12 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* file.c - part of debugfs, a tiny little debug file system
|
||||
*
|
||||
* Copyright (C) 2004 Greg Kroah-Hartman <greg@kroah.com>
|
||||
* 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.
|
||||
* See Documentation/filesystems/ for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
|
@ -22,7 +18,6 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/srcu.h>
|
||||
#include <asm/poll.h>
|
||||
|
||||
#include "internal.h"
|
||||
|
@ -48,66 +43,108 @@ const struct file_operations debugfs_noop_file_operations = {
|
|||
.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.
|
||||
* @srcu_idx: a pointer to some memory to store a SRCU index in.
|
||||
*
|
||||
* Up to a matching call to debugfs_use_file_finish(), any
|
||||
* successive call into the file removing functions debugfs_remove()
|
||||
* and debugfs_remove_recursive() will block. Since associated private
|
||||
* Up to a matching call to debugfs_file_put(), any successive call
|
||||
* into the file removing functions debugfs_remove() and
|
||||
* debugfs_remove_recursive() will block. Since associated private
|
||||
* file data may only get freed after a successful return of any of
|
||||
* the removal functions, you may safely access it after a successful
|
||||
* call to debugfs_use_file_start() without worrying about
|
||||
* lifetime issues.
|
||||
* call to debugfs_file_get() without worrying about lifetime issues.
|
||||
*
|
||||
* 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 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)
|
||||
__acquires(&debugfs_srcu)
|
||||
int debugfs_file_get(struct dentry *dentry)
|
||||
{
|
||||
*srcu_idx = srcu_read_lock(&debugfs_srcu);
|
||||
barrier();
|
||||
struct debugfs_fsdata *fsd;
|
||||
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))
|
||||
return -EIO;
|
||||
|
||||
if (!refcount_inc_not_zero(&fsd->active_users))
|
||||
return -EIO;
|
||||
|
||||
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
|
||||
* @srcu_idx: the SRCU index "created" by a former call to
|
||||
* debugfs_use_file_start().
|
||||
* debugfs_file_put - mark the end of file data access
|
||||
* @dentry: the dentry object formerly passed to
|
||||
* debugfs_file_get().
|
||||
*
|
||||
* Allow any ongoing concurrent call into debugfs_remove() or
|
||||
* 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);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(debugfs_use_file_finish);
|
||||
struct debugfs_fsdata *fsd = READ_ONCE(dentry->d_fsdata);
|
||||
|
||||
#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)
|
||||
{
|
||||
const struct dentry *dentry = F_DENTRY(filp);
|
||||
struct dentry *dentry = F_DENTRY(filp);
|
||||
const struct file_operations *real_fops = NULL;
|
||||
int srcu_idx, r;
|
||||
int r;
|
||||
|
||||
r = debugfs_use_file_start(dentry, &srcu_idx);
|
||||
if (r) {
|
||||
r = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
r = debugfs_file_get(dentry);
|
||||
if (r)
|
||||
return r == -EIO ? -ENOENT : r;
|
||||
|
||||
real_fops = debugfs_real_fops(filp);
|
||||
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);
|
||||
|
||||
out:
|
||||
debugfs_use_file_finish(srcu_idx);
|
||||
debugfs_file_put(dentry);
|
||||
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) \
|
||||
static ret_type full_proxy_ ## name(proto) \
|
||||
{ \
|
||||
const struct dentry *dentry = F_DENTRY(filp); \
|
||||
const struct file_operations *real_fops = \
|
||||
debugfs_real_fops(filp); \
|
||||
int srcu_idx; \
|
||||
struct dentry *dentry = F_DENTRY(filp); \
|
||||
const struct file_operations *real_fops; \
|
||||
ret_type r; \
|
||||
\
|
||||
r = debugfs_use_file_start(dentry, &srcu_idx); \
|
||||
if (likely(!r)) \
|
||||
r = real_fops->name(args); \
|
||||
debugfs_use_file_finish(srcu_idx); \
|
||||
r = debugfs_file_get(dentry); \
|
||||
if (unlikely(r)) \
|
||||
return r; \
|
||||
real_fops = debugfs_real_fops(filp); \
|
||||
r = real_fops->name(args); \
|
||||
debugfs_file_put(dentry); \
|
||||
return r; \
|
||||
}
|
||||
|
||||
|
@ -172,18 +209,16 @@ FULL_PROXY_FUNC(unlocked_ioctl, long, filp,
|
|||
static unsigned int full_proxy_poll(struct file *filp,
|
||||
struct poll_table_struct *wait)
|
||||
{
|
||||
const struct dentry *dentry = F_DENTRY(filp);
|
||||
const struct file_operations *real_fops = debugfs_real_fops(filp);
|
||||
int srcu_idx;
|
||||
struct dentry *dentry = F_DENTRY(filp);
|
||||
unsigned int r = 0;
|
||||
const struct file_operations *real_fops;
|
||||
|
||||
if (debugfs_use_file_start(dentry, &srcu_idx)) {
|
||||
debugfs_use_file_finish(srcu_idx);
|
||||
if (debugfs_file_get(dentry))
|
||||
return POLLHUP;
|
||||
}
|
||||
|
||||
real_fops = debugfs_real_fops(filp);
|
||||
r = real_fops->poll(filp, wait);
|
||||
debugfs_use_file_finish(srcu_idx);
|
||||
debugfs_file_put(dentry);
|
||||
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)
|
||||
{
|
||||
const struct dentry *dentry = F_DENTRY(filp);
|
||||
struct dentry *dentry = F_DENTRY(filp);
|
||||
const struct file_operations *real_fops = NULL;
|
||||
struct file_operations *proxy_fops = NULL;
|
||||
int srcu_idx, r;
|
||||
int r;
|
||||
|
||||
r = debugfs_use_file_start(dentry, &srcu_idx);
|
||||
if (r) {
|
||||
r = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
r = debugfs_file_get(dentry);
|
||||
if (r)
|
||||
return r == -EIO ? -ENOENT : r;
|
||||
|
||||
real_fops = debugfs_real_fops(filp);
|
||||
real_fops = fops_get(real_fops);
|
||||
|
@ -274,7 +307,7 @@ free_proxy:
|
|||
kfree(proxy_fops);
|
||||
fops_put(real_fops);
|
||||
out:
|
||||
debugfs_use_file_finish(srcu_idx);
|
||||
debugfs_file_put(dentry);
|
||||
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,
|
||||
size_t len, loff_t *ppos)
|
||||
{
|
||||
struct dentry *dentry = F_DENTRY(file);
|
||||
ssize_t ret;
|
||||
int srcu_idx;
|
||||
|
||||
ret = debugfs_use_file_start(F_DENTRY(file), &srcu_idx);
|
||||
if (likely(!ret))
|
||||
ret = simple_attr_read(file, buf, len, ppos);
|
||||
debugfs_use_file_finish(srcu_idx);
|
||||
ret = debugfs_file_get(dentry);
|
||||
if (unlikely(ret))
|
||||
return ret;
|
||||
ret = simple_attr_read(file, buf, len, ppos);
|
||||
debugfs_file_put(dentry);
|
||||
return ret;
|
||||
}
|
||||
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,
|
||||
size_t len, loff_t *ppos)
|
||||
{
|
||||
struct dentry *dentry = F_DENTRY(file);
|
||||
ssize_t ret;
|
||||
int srcu_idx;
|
||||
|
||||
ret = debugfs_use_file_start(F_DENTRY(file), &srcu_idx);
|
||||
if (likely(!ret))
|
||||
ret = simple_attr_write(file, buf, len, ppos);
|
||||
debugfs_use_file_finish(srcu_idx);
|
||||
ret = debugfs_file_get(dentry);
|
||||
if (unlikely(ret))
|
||||
return ret;
|
||||
ret = simple_attr_write(file, buf, len, ppos);
|
||||
debugfs_file_put(dentry);
|
||||
return ret;
|
||||
}
|
||||
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];
|
||||
bool val;
|
||||
int r, srcu_idx;
|
||||
int r;
|
||||
struct dentry *dentry = F_DENTRY(file);
|
||||
|
||||
r = debugfs_use_file_start(F_DENTRY(file), &srcu_idx);
|
||||
if (likely(!r))
|
||||
val = *(bool *)file->private_data;
|
||||
debugfs_use_file_finish(srcu_idx);
|
||||
if (r)
|
||||
r = debugfs_file_get(dentry);
|
||||
if (unlikely(r))
|
||||
return r;
|
||||
val = *(bool *)file->private_data;
|
||||
debugfs_file_put(dentry);
|
||||
|
||||
if (val)
|
||||
buf[0] = 'Y';
|
||||
|
@ -764,8 +799,9 @@ ssize_t debugfs_write_file_bool(struct file *file, const char __user *user_buf,
|
|||
char buf[32];
|
||||
size_t buf_size;
|
||||
bool bv;
|
||||
int r, srcu_idx;
|
||||
int r;
|
||||
bool *val = file->private_data;
|
||||
struct dentry *dentry = F_DENTRY(file);
|
||||
|
||||
buf_size = min(count, (sizeof(buf)-1));
|
||||
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';
|
||||
if (strtobool(buf, &bv) == 0) {
|
||||
r = debugfs_use_file_start(F_DENTRY(file), &srcu_idx);
|
||||
if (likely(!r))
|
||||
*val = bv;
|
||||
debugfs_use_file_finish(srcu_idx);
|
||||
if (r)
|
||||
r = debugfs_file_get(dentry);
|
||||
if (unlikely(r))
|
||||
return r;
|
||||
*val = bv;
|
||||
debugfs_file_put(dentry);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
struct debugfs_blob_wrapper *blob = file->private_data;
|
||||
struct dentry *dentry = F_DENTRY(file);
|
||||
ssize_t r;
|
||||
int srcu_idx;
|
||||
|
||||
r = debugfs_use_file_start(F_DENTRY(file), &srcu_idx);
|
||||
if (likely(!r))
|
||||
r = simple_read_from_buffer(user_buf, count, ppos, blob->data,
|
||||
blob->size);
|
||||
debugfs_use_file_finish(srcu_idx);
|
||||
r = debugfs_file_get(dentry);
|
||||
if (unlikely(r))
|
||||
return r;
|
||||
r = simple_read_from_buffer(user_buf, count, ppos, blob->data,
|
||||
blob->size);
|
||||
debugfs_file_put(dentry);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,16 +1,12 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* inode.c - part of debugfs, a tiny little debug file system
|
||||
*
|
||||
* Copyright (C) 2004 Greg Kroah-Hartman <greg@kroah.com>
|
||||
* 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.
|
||||
* See ./Documentation/core-api/kernel-api.rst for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
|
@ -27,14 +23,11 @@
|
|||
#include <linux/parser.h>
|
||||
#include <linux/magic.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/srcu.h>
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
#define DEBUGFS_DEFAULT_MODE 0700
|
||||
|
||||
DEFINE_SRCU(debugfs_srcu);
|
||||
|
||||
static struct vfsmount *debugfs_mount;
|
||||
static int debugfs_mount_count;
|
||||
static bool debugfs_registered;
|
||||
|
@ -185,6 +178,14 @@ static const struct super_operations debugfs_super_operations = {
|
|||
.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)
|
||||
{
|
||||
debugfs_automount_t f;
|
||||
|
@ -194,6 +195,7 @@ static struct vfsmount *debugfs_automount(struct path *path)
|
|||
|
||||
static const struct dentry_operations debugfs_dops = {
|
||||
.d_delete = always_delete_dentry,
|
||||
.d_release = debugfs_release_dentry,
|
||||
.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_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);
|
||||
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);
|
||||
|
||||
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)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (simple_positive(dentry)) {
|
||||
dget(dentry);
|
||||
if (d_is_dir(dentry))
|
||||
ret = simple_rmdir(d_inode(parent), dentry);
|
||||
else
|
||||
simple_unlink(d_inode(parent), dentry);
|
||||
if (!ret)
|
||||
d_delete(dentry);
|
||||
if (!d_is_reg(dentry)) {
|
||||
if (d_is_dir(dentry))
|
||||
ret = simple_rmdir(d_inode(parent), dentry);
|
||||
else
|
||||
simple_unlink(d_inode(parent), dentry);
|
||||
if (!ret)
|
||||
d_delete(dentry);
|
||||
} else {
|
||||
__debugfs_remove_file(dentry, parent);
|
||||
}
|
||||
dput(dentry);
|
||||
}
|
||||
return ret;
|
||||
|
@ -660,8 +688,6 @@ void debugfs_remove(struct dentry *dentry)
|
|||
inode_unlock(d_inode(parent));
|
||||
if (!ret)
|
||||
simple_release_fs(&debugfs_mount, &debugfs_mount_count);
|
||||
|
||||
synchronize_srcu(&debugfs_srcu);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(debugfs_remove);
|
||||
|
||||
|
@ -735,8 +761,6 @@ void debugfs_remove_recursive(struct dentry *dentry)
|
|||
if (!__debugfs_remove(child, parent))
|
||||
simple_release_fs(&debugfs_mount, &debugfs_mount_count);
|
||||
inode_unlock(d_inode(parent));
|
||||
|
||||
synchronize_srcu(&debugfs_srcu);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(debugfs_remove_recursive);
|
||||
|
||||
|
|
|
@ -1,12 +1,8 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* internal.h - declarations internal to debugfs
|
||||
*
|
||||
* 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_
|
||||
|
@ -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_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_ */
|
||||
|
|
|
@ -1,13 +1,10 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* debugfs.h - a tiny little debug file system
|
||||
*
|
||||
* Copyright (C) 2004 Greg Kroah-Hartman <greg@kroah.com>
|
||||
* 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.
|
||||
* See Documentation/filesystems/ for more details.
|
||||
*/
|
||||
|
@ -23,7 +20,6 @@
|
|||
|
||||
struct device;
|
||||
struct file_operations;
|
||||
struct srcu_struct;
|
||||
|
||||
struct debugfs_blob_wrapper {
|
||||
void *data;
|
||||
|
@ -43,25 +39,6 @@ struct debugfs_regset32 {
|
|||
|
||||
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) \
|
||||
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_recursive(struct dentry *dentry);
|
||||
|
||||
int debugfs_use_file_start(const struct dentry *dentry, int *srcu_idx)
|
||||
__acquires(&debugfs_srcu);
|
||||
const struct file_operations *debugfs_real_fops(const struct file *filp);
|
||||
|
||||
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,
|
||||
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 int debugfs_use_file_start(const struct dentry *dentry,
|
||||
int *srcu_idx)
|
||||
__acquires(&debugfs_srcu)
|
||||
static inline int debugfs_file_get(struct dentry *dentry)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void debugfs_use_file_finish(int srcu_idx)
|
||||
__releases(&debugfs_srcu)
|
||||
static inline void debugfs_file_put(struct dentry *dentry)
|
||||
{ }
|
||||
|
||||
static inline ssize_t debugfs_attr_read(struct file *file, char __user *buf,
|
||||
|
|
|
@ -280,7 +280,6 @@ config PAGE_OWNER
|
|||
|
||||
config DEBUG_FS
|
||||
bool "Debug Filesystem"
|
||||
select SRCU
|
||||
help
|
||||
debugfs is a virtual file system that kernel developers use to put
|
||||
debugging files into. Enable this option to be able to read and
|
||||
|
|
Loading…
Reference in New Issue