Commit b9743042 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'driver-core-4.15-rc1' of...

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
parents e60e1ee6 0ff26c66
......@@ -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 @@ static int really_probe(struct device *dev, struct device_driver *drv)
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;
}
......
// 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 @@ static int full_proxy_open(struct inode *inode, struct file *filp)
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;
}
......
// 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);
......
// 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_ */
// 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.