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); ...@@ -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;
......
...@@ -1958,7 +1958,6 @@ void device_del(struct device *dev) ...@@ -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) ...@@ -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...
......
...@@ -350,6 +350,15 @@ EXPORT_SYMBOL_GPL(device_bind_driver); ...@@ -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) ...@@ -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 @@ static int really_probe(struct device *dev, struct device_driver *drv) ...@@ -470,10 +481,7 @@ static int really_probe(struct device *dev, struct device_driver *drv)
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:
......
...@@ -64,7 +64,7 @@ static int __init test_async_probe_init(void) ...@@ -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) ...@@ -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) ...@@ -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;
} }
......
...@@ -71,13 +71,13 @@ static ssize_t hfi1_seq_read( ...@@ -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( ...@@ -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;
} }
......
This diff is collapsed.
// 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 @@ ...@@ -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 = { ...@@ -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) ...@@ -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, ...@@ -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, ...@@ -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) ...@@ -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) ...@@ -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);
......
// 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; ...@@ -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_ */
// 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 @@ ...@@ -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 { ...@@ -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, ...@@ -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) ...@@ -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,
......
...@@ -280,7 +280,6 @@ config PAGE_OWNER ...@@ -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
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment