Merge 4.6-rc4 into driver-core-next
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 18 Apr 2016 19:28:28 +0000 (04:28 +0900)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 18 Apr 2016 19:28:28 +0000 (04:28 +0900)
We want those fixes in here as well.

Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
19 files changed:
CREDITS
Documentation/devices.txt
arch/x86/Kconfig
drivers/base/Makefile
drivers/base/devcoredump.c
drivers/firmware/qemu_fw_cfg.c
drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c
fs/char_dev.c
fs/debugfs/file.c
fs/debugfs/inode.c
fs/debugfs/internal.h [new file with mode: 0644]
fs/kernfs/dir.c
fs/kernfs/inode.c
include/linux/debugfs.h
include/linux/devcoredump.h
include/linux/fs.h
include/linux/isa.h
lib/Kconfig.debug
scripts/coccinelle/api/debugfs/debugfs_simple_attr.cocci [new file with mode: 0644]

diff --git a/CREDITS b/CREDITS
index 4312cd076b5b4306f2a799e4d12bb8db15f0f011..0f0bf22afe0c469507bab4ef1dfbc13cac386c25 100644 (file)
--- a/CREDITS
+++ b/CREDITS
@@ -768,6 +768,7 @@ D: Z85230 driver
 D: Former security contact point (please use vendor-sec@lst.de)
 D: ex 2.2 maintainer
 D: 2.1.x modular sound
+D: Assigned major/minor numbers maintainer at lanana.org
 S: c/o Red Hat UK Ltd
 S: Alexandra House
 S: Alexandra Terrace
index 87b4c5e82d39023094f9b5f9b10cf919e3740f9d..4035eca87144c47527ff46ee4ed937d8f97b8f2d 100644 (file)
@@ -1,20 +1,17 @@
 
-                   LINUX ALLOCATED DEVICES (2.6+ version)
-
-            Maintained by Alan Cox <device@lanana.org>
-
-                     Last revised: 6th April 2009
+                   LINUX ALLOCATED DEVICES (4.x+ version)
 
 This list is the Linux Device List, the official registry of allocated
 device numbers and /dev directory nodes for the Linux operating
 system.
 
-The latest version of this list is available from
-http://www.lanana.org/docs/device-list/ or
-ftp://ftp.kernel.org/pub/linux/docs/device-list/.  This version may be
-newer than the one distributed with the Linux kernel.
-
-The LaTeX version of this document is no longer maintained.
+The LaTeX version of this document is no longer maintained, nor is
+the document that used to reside at lanana.org.  This version in the
+mainline Linux kernel is the master document.  Updates shall be sent
+as patches to the kernel maintainers (see the SubmittingPatches document).
+Specifically explore the sections titled "CHAR and MISC DRIVERS", and
+"BLOCK LAYER" in the MAINTAINERS file to find the right maintainers
+to involve for character and block devices.
 
 This document is included by reference into the Filesystem Hierarchy
 Standard (FHS).         The FHS is available from http://www.pathname.com/fhs/.
@@ -23,60 +20,33 @@ Allocations marked (68k/Amiga) apply to Linux/68k on the Amiga
 platform only. Allocations marked (68k/Atari) apply to Linux/68k on
 the Atari platform only.
 
-The symbol {2.6} means the allocation is obsolete and scheduled for
-removal once kernel version 2.6 (or equivalent) is released. Some of these
-allocations have already been removed.
-
-This document is in the public domain. The author requests, however,
+This document is in the public domain. The authors requests, however,
 that semantically altered versions are not distributed without
-permission of the author, assuming the author can be contacted without
+permission of the authors, assuming the authors can be contacted without
 an unreasonable effort.
 
-In particular, please don't sent patches for this list to Linus, at
-least not without contacting me first.
-
-I do not have any information about these devices beyond what appears
-on this list.  Any such information requests will be deleted without
-reply.
-
 
          **** DEVICE DRIVERS AUTHORS PLEASE READ THIS ****
 
-To have a major number allocated, or a minor number in situations
-where that applies (e.g. busmice), please contact me with the
-appropriate device information.         Also, if you have additional
-information regarding any of the devices listed below, or if I have
-made a mistake, I would greatly appreciate a note.
-
-I do, however, make a few requests about the nature of your report.
-This is necessary for me to be able to keep this list up to date and
-correct in a timely manner.  First of all, *please* send it to the
-correct address... <device@lanana.org>.  I receive hundreds of email
-messages a day, so mail sent to other addresses may very well get lost
-in the avalanche.  Please put in a descriptive subject, so I can find
-your mail again should I need to.  Too many people send me email
-saying just "device number request" in the subject.
-
-Second, please include a description of the device *in the same format
-as this list*. The reason for this is that it is the only way I have
-found to ensure I have all the requisite information to publish your
-device and avoid conflicts.
+Linux now has extensive support for dynamic allocation of device numbering
+and can use sysfs and udev (systemd) to handle the naming needs. There are
+still some exceptions in the serial and boot device area. Before asking
+for a device number make sure you actually need one.
 
-Third, please don't assume that the distributed version of the list is
-up to date.  Due to the number of registrations I have to maintain it
-in "batch mode", so there is likely additional registrations that
-haven't been listed yet.
+To have a major number allocated, or a minor number in situations
+where that applies (e.g. busmice), please submit a patch and send to
+the authors as indicated above.
 
-Fourth, remember that Linux now has extensive support for dynamic allocation
-of device numbering and can use sysfs and udev to handle the naming needs.
-There are still some exceptions in the serial and boot device area. Before
-asking for a device number make sure you actually need one.
+Keep the description of the device *in the same format
+as this list*. The reason for this is that it is the only way we have
+found to ensure we have all the requisite information to publish your
+device and avoid conflicts.
 
-Finally, sometimes I have to play "namespace police."  Please don't be
-offended.  I often get submissions for /dev names that would be bound
-to cause conflicts down the road.  I am trying to avoid getting in a
+Finally, sometimes we have to play "namespace police."  Please don't be
+offended.  We often get submissions for /dev names that would be bound
+to cause conflicts down the road.  We are trying to avoid getting in a
 situation where we would have to suffer an incompatible forward
-change.  Therefore, please consult with me *before* you make your
+change.  Therefore, please consult with us *before* you make your
 device names and numbers in any way public, at least to the point
 where it would be at all difficult to get them changed.
 
@@ -3099,9 +3069,9 @@ Your cooperation is appreciated.
                129 = /dev/ipath_sma    Device used by Subnet Management Agent
                130 = /dev/ipath_diag   Device used by diagnostics programs
 
-234-239                UNASSIGNED
-
-240-254 char   LOCAL/EXPERIMENTAL USE
+234-254        char    RESERVED FOR DYNAMIC ASSIGNMENT
+               Character devices that request a dynamic allocation of major number will
+               take numbers starting from 254 and downward.
 
 240-254 block  LOCAL/EXPERIMENTAL USE
                Allocated for local/experimental use.  For devices not
index 2dc18605831f6e88fd45c2863fc3d1fc7e6b6622..a5977986f38bc3a2b92f8a3770bb89ebcf65d486 100644 (file)
@@ -2472,10 +2472,16 @@ config ISA_DMA_API
          Enables ISA-style DMA support for devices requiring such controllers.
          If unsure, say Y.
 
+config ISA_BUS
+       bool "ISA bus support"
+       help
+         Enables ISA bus support for devices requiring such controllers.
+
 if X86_32
 
 config ISA
        bool "ISA support"
+       depends on ISA_BUS
        ---help---
          Find out whether you have ISA slots on your motherboard.  ISA is the
          name of a bus system, i.e. the way the CPU talks to the other stuff
index 6b2a84e7f2bece5643c2b68a17254067213b99ce..4ebfb81cc7e9206507f45ee88d7dc0f4ce247b42 100644 (file)
@@ -10,7 +10,7 @@ obj-$(CONFIG_DMA_CMA) += dma-contiguous.o
 obj-y                  += power/
 obj-$(CONFIG_HAS_DMA)  += dma-mapping.o
 obj-$(CONFIG_HAVE_GENERIC_DMA_COHERENT) += dma-coherent.o
-obj-$(CONFIG_ISA)      += isa.o
+obj-$(CONFIG_ISA_BUS)  += isa.o
 obj-$(CONFIG_FW_LOADER)        += firmware_class.o
 obj-$(CONFIG_NUMA)     += node.o
 obj-$(CONFIG_MEMORY_HOTPLUG_SPARSE) += memory.o
index 1bd120a0b084195df0a1d1bfe532f7e34874e946..240374fd18387721125bfee149fb037113f5f09f 100644 (file)
@@ -4,6 +4,7 @@
  * GPL LICENSE SUMMARY
  *
  * Copyright(c) 2014 Intel Mobile Communications GmbH
+ * Copyright(c) 2015 Intel Deutschland GmbH
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -41,12 +42,12 @@ static bool devcd_disabled;
 
 struct devcd_entry {
        struct device devcd_dev;
-       const void *data;
+       void *data;
        size_t datalen;
        struct module *owner;
        ssize_t (*read)(char *buffer, loff_t offset, size_t count,
-                       const void *data, size_t datalen);
-       void (*free)(const void *data);
+                       void *data, size_t datalen);
+       void (*free)(void *data);
        struct delayed_work del_wk;
        struct device *failing_dev;
 };
@@ -174,7 +175,7 @@ static struct class devcd_class = {
 };
 
 static ssize_t devcd_readv(char *buffer, loff_t offset, size_t count,
-                          const void *data, size_t datalen)
+                          void *data, size_t datalen)
 {
        if (offset > datalen)
                return -EINVAL;
@@ -188,6 +189,11 @@ static ssize_t devcd_readv(char *buffer, loff_t offset, size_t count,
        return count;
 }
 
+static void devcd_freev(void *data)
+{
+       vfree(data);
+}
+
 /**
  * dev_coredumpv - create device coredump with vmalloc data
  * @dev: the struct device for the crashed device
@@ -198,10 +204,10 @@ static ssize_t devcd_readv(char *buffer, loff_t offset, size_t count,
  * This function takes ownership of the vmalloc'ed data and will free
  * it when it is no longer used. See dev_coredumpm() for more information.
  */
-void dev_coredumpv(struct device *dev, const void *data, size_t datalen,
+void dev_coredumpv(struct device *dev, void *data, size_t datalen,
                   gfp_t gfp)
 {
-       dev_coredumpm(dev, NULL, data, datalen, gfp, devcd_readv, vfree);
+       dev_coredumpm(dev, NULL, data, datalen, gfp, devcd_readv, devcd_freev);
 }
 EXPORT_SYMBOL_GPL(dev_coredumpv);
 
@@ -212,6 +218,44 @@ static int devcd_match_failing(struct device *dev, const void *failing)
        return devcd->failing_dev == failing;
 }
 
+/**
+ * devcd_free_sgtable - free all the memory of the given scatterlist table
+ * (i.e. both pages and scatterlist instances)
+ * NOTE: if two tables allocated with devcd_alloc_sgtable and then chained
+ * using the sg_chain function then that function should be called only once
+ * on the chained table
+ * @table: pointer to sg_table to free
+ */
+static void devcd_free_sgtable(void *data)
+{
+       _devcd_free_sgtable(data);
+}
+
+/**
+ * devcd_read_from_table - copy data from sg_table to a given buffer
+ * and return the number of bytes read
+ * @buffer: the buffer to copy the data to it
+ * @buf_len: the length of the buffer
+ * @data: the scatterlist table to copy from
+ * @offset: start copy from @offset@ bytes from the head of the data
+ *     in the given scatterlist
+ * @data_len: the length of the data in the sg_table
+ */
+static ssize_t devcd_read_from_sgtable(char *buffer, loff_t offset,
+                                      size_t buf_len, void *data,
+                                      size_t data_len)
+{
+       struct scatterlist *table = data;
+
+       if (offset > data_len)
+               return -EINVAL;
+
+       if (offset + buf_len > data_len)
+               buf_len = data_len - offset;
+       return sg_pcopy_to_buffer(table, sg_nents(table), buffer, buf_len,
+                                 offset);
+}
+
 /**
  * dev_coredumpm - create device coredump with read/free methods
  * @dev: the struct device for the crashed device
@@ -228,10 +272,10 @@ static int devcd_match_failing(struct device *dev, const void *failing)
  * function will be called to free the data.
  */
 void dev_coredumpm(struct device *dev, struct module *owner,
-                  const void *data, size_t datalen, gfp_t gfp,
+                  void *data, size_t datalen, gfp_t gfp,
                   ssize_t (*read)(char *buffer, loff_t offset, size_t count,
-                                  const void *data, size_t datalen),
-                  void (*free)(const void *data))
+                                  void *data, size_t datalen),
+                  void (*free)(void *data))
 {
        static atomic_t devcd_count = ATOMIC_INIT(0);
        struct devcd_entry *devcd;
@@ -291,6 +335,27 @@ void dev_coredumpm(struct device *dev, struct module *owner,
 }
 EXPORT_SYMBOL_GPL(dev_coredumpm);
 
+/**
+ * dev_coredumpmsg - create device coredump that uses scatterlist as data
+ * parameter
+ * @dev: the struct device for the crashed device
+ * @table: the dump data
+ * @datalen: length of the data
+ * @gfp: allocation flags
+ *
+ * Creates a new device coredump for the given device. If a previous one hasn't
+ * been read yet, the new coredump is discarded. The data lifetime is determined
+ * by the device coredump framework and when it is no longer needed
+ * it will free the data.
+ */
+void dev_coredumpsg(struct device *dev, struct scatterlist *table,
+                   size_t datalen, gfp_t gfp)
+{
+       dev_coredumpm(dev, NULL, table, datalen, gfp, devcd_read_from_sgtable,
+                     devcd_free_sgtable);
+}
+EXPORT_SYMBOL_GPL(dev_coredumpsg);
+
 static int __init devcoredump_init(void)
 {
        return class_register(&devcd_class);
index 815c4a5cae543e228a634eae28241bc6f24ac1ed..d999fe3b1de692544511ade85a25eaaaff7f0419 100644 (file)
@@ -125,9 +125,7 @@ static void fw_cfg_io_cleanup(void)
 #  define FW_CFG_CTRL_OFF 0x00
 #  define FW_CFG_DATA_OFF 0x01
 # else
-#  warning "QEMU FW_CFG may not be available on this architecture!"
-#  define FW_CFG_CTRL_OFF 0x00
-#  define FW_CFG_DATA_OFF 0x01
+#  error "QEMU FW_CFG not available on this architecture!"
 # endif
 #endif
 
index 4856eac120f60d5eff2e9707e828e541761a76ae..a4b0581d2275f59f68fc519d718866630ed18386 100644 (file)
@@ -71,7 +71,7 @@
 #include "iwl-csr.h"
 
 static ssize_t iwl_mvm_read_coredump(char *buffer, loff_t offset, size_t count,
-                                    const void *data, size_t datalen)
+                                    void *data, size_t datalen)
 {
        const struct iwl_mvm_dump_ptrs *dump_ptrs = data;
        ssize_t bytes_read;
@@ -104,7 +104,7 @@ static ssize_t iwl_mvm_read_coredump(char *buffer, loff_t offset, size_t count,
        return bytes_read + bytes_read_trans;
 }
 
-static void iwl_mvm_free_coredump(const void *data)
+static void iwl_mvm_free_coredump(void *data)
 {
        const struct iwl_mvm_dump_ptrs *fw_error_dump = data;
 
index 24b142569ca9b5beea55b5cf6224b8ad81e6a16c..687471dc04a0a515f4a8dc4953297e6156880b64 100644 (file)
@@ -91,6 +91,10 @@ __register_chrdev_region(unsigned int major, unsigned int baseminor,
                                break;
                }
 
+               if (i < CHRDEV_MAJOR_DYN_END)
+                       pr_warn("CHRDEV \"%s\" major number %d goes below the dynamic allocation range",
+                               name, i);
+
                if (i == 0) {
                        ret = -EBUSY;
                        goto out;
index d2ba12e23ed94d78b35e0f01977be8433e80fca4..9c1c9a01b7e5120da23888828300d3f1b16e04c4 100644 (file)
 #include <linux/slab.h>
 #include <linux/atomic.h>
 #include <linux/device.h>
+#include <linux/srcu.h>
+#include <asm/poll.h>
+
+#include "internal.h"
+
+struct poll_table_struct;
 
 static ssize_t default_read_file(struct file *file, char __user *buf,
                                 size_t count, loff_t *ppos)
@@ -35,27 +41,293 @@ static ssize_t default_write_file(struct file *file, const char __user *buf,
        return count;
 }
 
-const struct file_operations debugfs_file_operations = {
+const struct file_operations debugfs_noop_file_operations = {
        .read =         default_read_file,
        .write =        default_write_file,
        .open =         simple_open,
        .llseek =       noop_llseek,
 };
 
-static struct dentry *debugfs_create_mode(const char *name, umode_t mode,
-                                         struct dentry *parent, void *value,
-                                         const struct file_operations *fops,
-                                         const struct file_operations *fops_ro,
-                                         const struct file_operations *fops_wo)
+/**
+ * debugfs_use_file_start - 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
+ * 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.
+ *
+ * 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)
+{
+       *srcu_idx = srcu_read_lock(&debugfs_srcu);
+       barrier();
+       if (d_unlinked(dentry))
+               return -EIO;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(debugfs_use_file_start);
+
+/**
+ * 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().
+ *
+ * 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.
+ */
+void debugfs_use_file_finish(int srcu_idx) __releases(&debugfs_srcu)
+{
+       srcu_read_unlock(&debugfs_srcu, srcu_idx);
+}
+EXPORT_SYMBOL_GPL(debugfs_use_file_finish);
+
+#define F_DENTRY(filp) ((filp)->f_path.dentry)
+
+#define REAL_FOPS_DEREF(dentry)                                        \
+       ((const struct file_operations *)(dentry)->d_fsdata)
+
+static int open_proxy_open(struct inode *inode, struct file *filp)
+{
+       const struct dentry *dentry = F_DENTRY(filp);
+       const struct file_operations *real_fops = NULL;
+       int srcu_idx, r;
+
+       r = debugfs_use_file_start(dentry, &srcu_idx);
+       if (r) {
+               r = -ENOENT;
+               goto out;
+       }
+
+       real_fops = REAL_FOPS_DEREF(dentry);
+       real_fops = fops_get(real_fops);
+       if (!real_fops) {
+               /* Huh? Module did not clean up after itself at exit? */
+               WARN(1, "debugfs file owner did not clean up at exit: %pd",
+                       dentry);
+               r = -ENXIO;
+               goto out;
+       }
+       replace_fops(filp, real_fops);
+
+       if (real_fops->open)
+               r = real_fops->open(inode, filp);
+
+out:
+       fops_put(real_fops);
+       debugfs_use_file_finish(srcu_idx);
+       return r;
+}
+
+const struct file_operations debugfs_open_proxy_file_operations = {
+       .open = open_proxy_open,
+};
+
+#define PROTO(args...) args
+#define ARGS(args...) args
+
+#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 =                       \
+               REAL_FOPS_DEREF(dentry);                                \
+       int srcu_idx;                                                   \
+       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);                              \
+       return r;                                                       \
+}
+
+FULL_PROXY_FUNC(llseek, loff_t, filp,
+               PROTO(struct file *filp, loff_t offset, int whence),
+               ARGS(filp, offset, whence));
+
+FULL_PROXY_FUNC(read, ssize_t, filp,
+               PROTO(struct file *filp, char __user *buf, size_t size,
+                       loff_t *ppos),
+               ARGS(filp, buf, size, ppos));
+
+FULL_PROXY_FUNC(write, ssize_t, filp,
+               PROTO(struct file *filp, const char __user *buf, size_t size,
+                       loff_t *ppos),
+               ARGS(filp, buf, size, ppos));
+
+FULL_PROXY_FUNC(unlocked_ioctl, long, filp,
+               PROTO(struct file *filp, unsigned int cmd, unsigned long arg),
+               ARGS(filp, cmd, arg));
+
+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 = REAL_FOPS_DEREF(dentry);
+       int srcu_idx;
+       unsigned int r = 0;
+
+       if (debugfs_use_file_start(dentry, &srcu_idx)) {
+               debugfs_use_file_finish(srcu_idx);
+               return POLLHUP;
+       }
+
+       r = real_fops->poll(filp, wait);
+       debugfs_use_file_finish(srcu_idx);
+       return r;
+}
+
+static int full_proxy_release(struct inode *inode, struct file *filp)
+{
+       const struct dentry *dentry = F_DENTRY(filp);
+       const struct file_operations *real_fops = REAL_FOPS_DEREF(dentry);
+       const struct file_operations *proxy_fops = filp->f_op;
+       int r = 0;
+
+       /*
+        * We must not protect this against removal races here: the
+        * original releaser should be called unconditionally in order
+        * not to leak any resources. Releasers must not assume that
+        * ->i_private is still being meaningful here.
+        */
+       if (real_fops->release)
+               r = real_fops->release(inode, filp);
+
+       replace_fops(filp, d_inode(dentry)->i_fop);
+       kfree((void *)proxy_fops);
+       fops_put(real_fops);
+       return 0;
+}
+
+static void __full_proxy_fops_init(struct file_operations *proxy_fops,
+                               const struct file_operations *real_fops)
+{
+       proxy_fops->release = full_proxy_release;
+       if (real_fops->llseek)
+               proxy_fops->llseek = full_proxy_llseek;
+       if (real_fops->read)
+               proxy_fops->read = full_proxy_read;
+       if (real_fops->write)
+               proxy_fops->write = full_proxy_write;
+       if (real_fops->poll)
+               proxy_fops->poll = full_proxy_poll;
+       if (real_fops->unlocked_ioctl)
+               proxy_fops->unlocked_ioctl = full_proxy_unlocked_ioctl;
+}
+
+static int full_proxy_open(struct inode *inode, struct file *filp)
+{
+       const struct dentry *dentry = F_DENTRY(filp);
+       const struct file_operations *real_fops = NULL;
+       struct file_operations *proxy_fops = NULL;
+       int srcu_idx, r;
+
+       r = debugfs_use_file_start(dentry, &srcu_idx);
+       if (r) {
+               r = -ENOENT;
+               goto out;
+       }
+
+       real_fops = REAL_FOPS_DEREF(dentry);
+       real_fops = fops_get(real_fops);
+       if (!real_fops) {
+               /* Huh? Module did not cleanup after itself at exit? */
+               WARN(1, "debugfs file owner did not clean up at exit: %pd",
+                       dentry);
+               r = -ENXIO;
+               goto out;
+       }
+
+       proxy_fops = kzalloc(sizeof(*proxy_fops), GFP_KERNEL);
+       if (!proxy_fops) {
+               r = -ENOMEM;
+               goto free_proxy;
+       }
+       __full_proxy_fops_init(proxy_fops, real_fops);
+       replace_fops(filp, proxy_fops);
+
+       if (real_fops->open) {
+               r = real_fops->open(inode, filp);
+
+               if (filp->f_op != proxy_fops) {
+                       /* No protection against file removal anymore. */
+                       WARN(1, "debugfs file owner replaced proxy fops: %pd",
+                               dentry);
+                       goto free_proxy;
+               }
+       }
+
+       goto out;
+free_proxy:
+       kfree(proxy_fops);
+       fops_put(real_fops);
+out:
+       debugfs_use_file_finish(srcu_idx);
+       return r;
+}
+
+const struct file_operations debugfs_full_proxy_file_operations = {
+       .open = full_proxy_open,
+};
+
+ssize_t debugfs_attr_read(struct file *file, char __user *buf,
+                       size_t len, loff_t *ppos)
+{
+       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);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(debugfs_attr_read);
+
+ssize_t debugfs_attr_write(struct file *file, const char __user *buf,
+                        size_t len, loff_t *ppos)
+{
+       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);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(debugfs_attr_write);
+
+static struct dentry *debugfs_create_mode_unsafe(const char *name, umode_t mode,
+                                       struct dentry *parent, void *value,
+                                       const struct file_operations *fops,
+                                       const struct file_operations *fops_ro,
+                                       const struct file_operations *fops_wo)
 {
        /* if there are no write bits set, make read only */
        if (!(mode & S_IWUGO))
-               return debugfs_create_file(name, mode, parent, value, fops_ro);
+               return debugfs_create_file_unsafe(name, mode, parent, value,
+                                               fops_ro);
        /* if there are no read bits set, make write only */
        if (!(mode & S_IRUGO))
-               return debugfs_create_file(name, mode, parent, value, fops_wo);
+               return debugfs_create_file_unsafe(name, mode, parent, value,
+                                               fops_wo);
 
-       return debugfs_create_file(name, mode, parent, value, fops);
+       return debugfs_create_file_unsafe(name, mode, parent, value, fops);
 }
 
 static int debugfs_u8_set(void *data, u64 val)
@@ -68,9 +340,9 @@ static int debugfs_u8_get(void *data, u64 *val)
        *val = *(u8 *)data;
        return 0;
 }
-DEFINE_SIMPLE_ATTRIBUTE(fops_u8, debugfs_u8_get, debugfs_u8_set, "%llu\n");
-DEFINE_SIMPLE_ATTRIBUTE(fops_u8_ro, debugfs_u8_get, NULL, "%llu\n");
-DEFINE_SIMPLE_ATTRIBUTE(fops_u8_wo, NULL, debugfs_u8_set, "%llu\n");
+DEFINE_DEBUGFS_ATTRIBUTE(fops_u8, debugfs_u8_get, debugfs_u8_set, "%llu\n");
+DEFINE_DEBUGFS_ATTRIBUTE(fops_u8_ro, debugfs_u8_get, NULL, "%llu\n");
+DEFINE_DEBUGFS_ATTRIBUTE(fops_u8_wo, NULL, debugfs_u8_set, "%llu\n");
 
 /**
  * debugfs_create_u8 - create a debugfs file that is used to read and write an unsigned 8-bit value
@@ -99,7 +371,7 @@ DEFINE_SIMPLE_ATTRIBUTE(fops_u8_wo, NULL, debugfs_u8_set, "%llu\n");
 struct dentry *debugfs_create_u8(const char *name, umode_t mode,
                                 struct dentry *parent, u8 *value)
 {
-       return debugfs_create_mode(name, mode, parent, value, &fops_u8,
+       return debugfs_create_mode_unsafe(name, mode, parent, value, &fops_u8,
                                   &fops_u8_ro, &fops_u8_wo);
 }
 EXPORT_SYMBOL_GPL(debugfs_create_u8);
@@ -114,9 +386,9 @@ static int debugfs_u16_get(void *data, u64 *val)
        *val = *(u16 *)data;
        return 0;
 }
-DEFINE_SIMPLE_ATTRIBUTE(fops_u16, debugfs_u16_get, debugfs_u16_set, "%llu\n");
-DEFINE_SIMPLE_ATTRIBUTE(fops_u16_ro, debugfs_u16_get, NULL, "%llu\n");
-DEFINE_SIMPLE_ATTRIBUTE(fops_u16_wo, NULL, debugfs_u16_set, "%llu\n");
+DEFINE_DEBUGFS_ATTRIBUTE(fops_u16, debugfs_u16_get, debugfs_u16_set, "%llu\n");
+DEFINE_DEBUGFS_ATTRIBUTE(fops_u16_ro, debugfs_u16_get, NULL, "%llu\n");
+DEFINE_DEBUGFS_ATTRIBUTE(fops_u16_wo, NULL, debugfs_u16_set, "%llu\n");
 
 /**
  * debugfs_create_u16 - create a debugfs file that is used to read and write an unsigned 16-bit value
@@ -145,7 +417,7 @@ DEFINE_SIMPLE_ATTRIBUTE(fops_u16_wo, NULL, debugfs_u16_set, "%llu\n");
 struct dentry *debugfs_create_u16(const char *name, umode_t mode,
                                  struct dentry *parent, u16 *value)
 {
-       return debugfs_create_mode(name, mode, parent, value, &fops_u16,
+       return debugfs_create_mode_unsafe(name, mode, parent, value, &fops_u16,
                                   &fops_u16_ro, &fops_u16_wo);
 }
 EXPORT_SYMBOL_GPL(debugfs_create_u16);
@@ -160,9 +432,9 @@ static int debugfs_u32_get(void *data, u64 *val)
        *val = *(u32 *)data;
        return 0;
 }
-DEFINE_SIMPLE_ATTRIBUTE(fops_u32, debugfs_u32_get, debugfs_u32_set, "%llu\n");
-DEFINE_SIMPLE_ATTRIBUTE(fops_u32_ro, debugfs_u32_get, NULL, "%llu\n");
-DEFINE_SIMPLE_ATTRIBUTE(fops_u32_wo, NULL, debugfs_u32_set, "%llu\n");
+DEFINE_DEBUGFS_ATTRIBUTE(fops_u32, debugfs_u32_get, debugfs_u32_set, "%llu\n");
+DEFINE_DEBUGFS_ATTRIBUTE(fops_u32_ro, debugfs_u32_get, NULL, "%llu\n");
+DEFINE_DEBUGFS_ATTRIBUTE(fops_u32_wo, NULL, debugfs_u32_set, "%llu\n");
 
 /**
  * debugfs_create_u32 - create a debugfs file that is used to read and write an unsigned 32-bit value
@@ -191,7 +463,7 @@ DEFINE_SIMPLE_ATTRIBUTE(fops_u32_wo, NULL, debugfs_u32_set, "%llu\n");
 struct dentry *debugfs_create_u32(const char *name, umode_t mode,
                                 struct dentry *parent, u32 *value)
 {
-       return debugfs_create_mode(name, mode, parent, value, &fops_u32,
+       return debugfs_create_mode_unsafe(name, mode, parent, value, &fops_u32,
                                   &fops_u32_ro, &fops_u32_wo);
 }
 EXPORT_SYMBOL_GPL(debugfs_create_u32);
@@ -207,9 +479,9 @@ static int debugfs_u64_get(void *data, u64 *val)
        *val = *(u64 *)data;
        return 0;
 }
-DEFINE_SIMPLE_ATTRIBUTE(fops_u64, debugfs_u64_get, debugfs_u64_set, "%llu\n");
-DEFINE_SIMPLE_ATTRIBUTE(fops_u64_ro, debugfs_u64_get, NULL, "%llu\n");
-DEFINE_SIMPLE_ATTRIBUTE(fops_u64_wo, NULL, debugfs_u64_set, "%llu\n");
+DEFINE_DEBUGFS_ATTRIBUTE(fops_u64, debugfs_u64_get, debugfs_u64_set, "%llu\n");
+DEFINE_DEBUGFS_ATTRIBUTE(fops_u64_ro, debugfs_u64_get, NULL, "%llu\n");
+DEFINE_DEBUGFS_ATTRIBUTE(fops_u64_wo, NULL, debugfs_u64_set, "%llu\n");
 
 /**
  * debugfs_create_u64 - create a debugfs file that is used to read and write an unsigned 64-bit value
@@ -238,7 +510,7 @@ DEFINE_SIMPLE_ATTRIBUTE(fops_u64_wo, NULL, debugfs_u64_set, "%llu\n");
 struct dentry *debugfs_create_u64(const char *name, umode_t mode,
                                 struct dentry *parent, u64 *value)
 {
-       return debugfs_create_mode(name, mode, parent, value, &fops_u64,
+       return debugfs_create_mode_unsafe(name, mode, parent, value, &fops_u64,
                                   &fops_u64_ro, &fops_u64_wo);
 }
 EXPORT_SYMBOL_GPL(debugfs_create_u64);
@@ -254,9 +526,10 @@ static int debugfs_ulong_get(void *data, u64 *val)
        *val = *(unsigned long *)data;
        return 0;
 }
-DEFINE_SIMPLE_ATTRIBUTE(fops_ulong, debugfs_ulong_get, debugfs_ulong_set, "%llu\n");
-DEFINE_SIMPLE_ATTRIBUTE(fops_ulong_ro, debugfs_ulong_get, NULL, "%llu\n");
-DEFINE_SIMPLE_ATTRIBUTE(fops_ulong_wo, NULL, debugfs_ulong_set, "%llu\n");
+DEFINE_DEBUGFS_ATTRIBUTE(fops_ulong, debugfs_ulong_get, debugfs_ulong_set,
+                       "%llu\n");
+DEFINE_DEBUGFS_ATTRIBUTE(fops_ulong_ro, debugfs_ulong_get, NULL, "%llu\n");
+DEFINE_DEBUGFS_ATTRIBUTE(fops_ulong_wo, NULL, debugfs_ulong_set, "%llu\n");
 
 /**
  * debugfs_create_ulong - create a debugfs file that is used to read and write
@@ -286,26 +559,30 @@ DEFINE_SIMPLE_ATTRIBUTE(fops_ulong_wo, NULL, debugfs_ulong_set, "%llu\n");
 struct dentry *debugfs_create_ulong(const char *name, umode_t mode,
                                    struct dentry *parent, unsigned long *value)
 {
-       return debugfs_create_mode(name, mode, parent, value, &fops_ulong,
-                                  &fops_ulong_ro, &fops_ulong_wo);
+       return debugfs_create_mode_unsafe(name, mode, parent, value,
+                                       &fops_ulong, &fops_ulong_ro,
+                                       &fops_ulong_wo);
 }
 EXPORT_SYMBOL_GPL(debugfs_create_ulong);
 
-DEFINE_SIMPLE_ATTRIBUTE(fops_x8, debugfs_u8_get, debugfs_u8_set, "0x%02llx\n");
-DEFINE_SIMPLE_ATTRIBUTE(fops_x8_ro, debugfs_u8_get, NULL, "0x%02llx\n");
-DEFINE_SIMPLE_ATTRIBUTE(fops_x8_wo, NULL, debugfs_u8_set, "0x%02llx\n");
+DEFINE_DEBUGFS_ATTRIBUTE(fops_x8, debugfs_u8_get, debugfs_u8_set, "0x%02llx\n");
+DEFINE_DEBUGFS_ATTRIBUTE(fops_x8_ro, debugfs_u8_get, NULL, "0x%02llx\n");
+DEFINE_DEBUGFS_ATTRIBUTE(fops_x8_wo, NULL, debugfs_u8_set, "0x%02llx\n");
 
-DEFINE_SIMPLE_ATTRIBUTE(fops_x16, debugfs_u16_get, debugfs_u16_set, "0x%04llx\n");
-DEFINE_SIMPLE_ATTRIBUTE(fops_x16_ro, debugfs_u16_get, NULL, "0x%04llx\n");
-DEFINE_SIMPLE_ATTRIBUTE(fops_x16_wo, NULL, debugfs_u16_set, "0x%04llx\n");
+DEFINE_DEBUGFS_ATTRIBUTE(fops_x16, debugfs_u16_get, debugfs_u16_set,
+                       "0x%04llx\n");
+DEFINE_DEBUGFS_ATTRIBUTE(fops_x16_ro, debugfs_u16_get, NULL, "0x%04llx\n");
+DEFINE_DEBUGFS_ATTRIBUTE(fops_x16_wo, NULL, debugfs_u16_set, "0x%04llx\n");
 
-DEFINE_SIMPLE_ATTRIBUTE(fops_x32, debugfs_u32_get, debugfs_u32_set, "0x%08llx\n");
-DEFINE_SIMPLE_ATTRIBUTE(fops_x32_ro, debugfs_u32_get, NULL, "0x%08llx\n");
-DEFINE_SIMPLE_ATTRIBUTE(fops_x32_wo, NULL, debugfs_u32_set, "0x%08llx\n");
+DEFINE_DEBUGFS_ATTRIBUTE(fops_x32, debugfs_u32_get, debugfs_u32_set,
+                       "0x%08llx\n");
+DEFINE_DEBUGFS_ATTRIBUTE(fops_x32_ro, debugfs_u32_get, NULL, "0x%08llx\n");
+DEFINE_DEBUGFS_ATTRIBUTE(fops_x32_wo, NULL, debugfs_u32_set, "0x%08llx\n");
 
-DEFINE_SIMPLE_ATTRIBUTE(fops_x64, debugfs_u64_get, debugfs_u64_set, "0x%016llx\n");
-DEFINE_SIMPLE_ATTRIBUTE(fops_x64_ro, debugfs_u64_get, NULL, "0x%016llx\n");
-DEFINE_SIMPLE_ATTRIBUTE(fops_x64_wo, NULL, debugfs_u64_set, "0x%016llx\n");
+DEFINE_DEBUGFS_ATTRIBUTE(fops_x64, debugfs_u64_get, debugfs_u64_set,
+                       "0x%016llx\n");
+DEFINE_DEBUGFS_ATTRIBUTE(fops_x64_ro, debugfs_u64_get, NULL, "0x%016llx\n");
+DEFINE_DEBUGFS_ATTRIBUTE(fops_x64_wo, NULL, debugfs_u64_set, "0x%016llx\n");
 
 /*
  * debugfs_create_x{8,16,32,64} - create a debugfs file that is used to read and write an unsigned {8,16,32,64}-bit value
@@ -328,7 +605,7 @@ DEFINE_SIMPLE_ATTRIBUTE(fops_x64_wo, NULL, debugfs_u64_set, "0x%016llx\n");
 struct dentry *debugfs_create_x8(const char *name, umode_t mode,
                                 struct dentry *parent, u8 *value)
 {
-       return debugfs_create_mode(name, mode, parent, value, &fops_x8,
+       return debugfs_create_mode_unsafe(name, mode, parent, value, &fops_x8,
                                   &fops_x8_ro, &fops_x8_wo);
 }
 EXPORT_SYMBOL_GPL(debugfs_create_x8);
@@ -346,7 +623,7 @@ EXPORT_SYMBOL_GPL(debugfs_create_x8);
 struct dentry *debugfs_create_x16(const char *name, umode_t mode,
                                 struct dentry *parent, u16 *value)
 {
-       return debugfs_create_mode(name, mode, parent, value, &fops_x16,
+       return debugfs_create_mode_unsafe(name, mode, parent, value, &fops_x16,
                                   &fops_x16_ro, &fops_x16_wo);
 }
 EXPORT_SYMBOL_GPL(debugfs_create_x16);
@@ -364,7 +641,7 @@ EXPORT_SYMBOL_GPL(debugfs_create_x16);
 struct dentry *debugfs_create_x32(const char *name, umode_t mode,
                                 struct dentry *parent, u32 *value)
 {
-       return debugfs_create_mode(name, mode, parent, value, &fops_x32,
+       return debugfs_create_mode_unsafe(name, mode, parent, value, &fops_x32,
                                   &fops_x32_ro, &fops_x32_wo);
 }
 EXPORT_SYMBOL_GPL(debugfs_create_x32);
@@ -382,7 +659,7 @@ EXPORT_SYMBOL_GPL(debugfs_create_x32);
 struct dentry *debugfs_create_x64(const char *name, umode_t mode,
                                 struct dentry *parent, u64 *value)
 {
-       return debugfs_create_mode(name, mode, parent, value, &fops_x64,
+       return debugfs_create_mode_unsafe(name, mode, parent, value, &fops_x64,
                                   &fops_x64_ro, &fops_x64_wo);
 }
 EXPORT_SYMBOL_GPL(debugfs_create_x64);
@@ -398,10 +675,10 @@ static int debugfs_size_t_get(void *data, u64 *val)
        *val = *(size_t *)data;
        return 0;
 }
-DEFINE_SIMPLE_ATTRIBUTE(fops_size_t, debugfs_size_t_get, debugfs_size_t_set,
-                       "%llu\n");      /* %llu and %zu are more or less the same */
-DEFINE_SIMPLE_ATTRIBUTE(fops_size_t_ro, debugfs_size_t_get, NULL, "%llu\n");
-DEFINE_SIMPLE_ATTRIBUTE(fops_size_t_wo, NULL, debugfs_size_t_set, "%llu\n");
+DEFINE_DEBUGFS_ATTRIBUTE(fops_size_t, debugfs_size_t_get, debugfs_size_t_set,
+                       "%llu\n"); /* %llu and %zu are more or less the same */
+DEFINE_DEBUGFS_ATTRIBUTE(fops_size_t_ro, debugfs_size_t_get, NULL, "%llu\n");
+DEFINE_DEBUGFS_ATTRIBUTE(fops_size_t_wo, NULL, debugfs_size_t_set, "%llu\n");
 
 /**
  * debugfs_create_size_t - create a debugfs file that is used to read and write an size_t value
@@ -416,8 +693,9 @@ DEFINE_SIMPLE_ATTRIBUTE(fops_size_t_wo, NULL, debugfs_size_t_set, "%llu\n");
 struct dentry *debugfs_create_size_t(const char *name, umode_t mode,
                                     struct dentry *parent, size_t *value)
 {
-       return debugfs_create_mode(name, mode, parent, value, &fops_size_t,
-                                  &fops_size_t_ro, &fops_size_t_wo);
+       return debugfs_create_mode_unsafe(name, mode, parent, value,
+                                       &fops_size_t, &fops_size_t_ro,
+                                       &fops_size_t_wo);
 }
 EXPORT_SYMBOL_GPL(debugfs_create_size_t);
 
@@ -431,10 +709,12 @@ static int debugfs_atomic_t_get(void *data, u64 *val)
        *val = atomic_read((atomic_t *)data);
        return 0;
 }
-DEFINE_SIMPLE_ATTRIBUTE(fops_atomic_t, debugfs_atomic_t_get,
+DEFINE_DEBUGFS_ATTRIBUTE(fops_atomic_t, debugfs_atomic_t_get,
                        debugfs_atomic_t_set, "%lld\n");
-DEFINE_SIMPLE_ATTRIBUTE(fops_atomic_t_ro, debugfs_atomic_t_get, NULL, "%lld\n");
-DEFINE_SIMPLE_ATTRIBUTE(fops_atomic_t_wo, NULL, debugfs_atomic_t_set, "%lld\n");
+DEFINE_DEBUGFS_ATTRIBUTE(fops_atomic_t_ro, debugfs_atomic_t_get, NULL,
+                       "%lld\n");
+DEFINE_DEBUGFS_ATTRIBUTE(fops_atomic_t_wo, NULL, debugfs_atomic_t_set,
+                       "%lld\n");
 
 /**
  * debugfs_create_atomic_t - create a debugfs file that is used to read and
@@ -450,8 +730,9 @@ DEFINE_SIMPLE_ATTRIBUTE(fops_atomic_t_wo, NULL, debugfs_atomic_t_set, "%lld\n");
 struct dentry *debugfs_create_atomic_t(const char *name, umode_t mode,
                                 struct dentry *parent, atomic_t *value)
 {
-       return debugfs_create_mode(name, mode, parent, value, &fops_atomic_t,
-                                  &fops_atomic_t_ro, &fops_atomic_t_wo);
+       return debugfs_create_mode_unsafe(name, mode, parent, value,
+                                       &fops_atomic_t, &fops_atomic_t_ro,
+                                       &fops_atomic_t_wo);
 }
 EXPORT_SYMBOL_GPL(debugfs_create_atomic_t);
 
@@ -459,9 +740,17 @@ ssize_t debugfs_read_file_bool(struct file *file, char __user *user_buf,
                               size_t count, loff_t *ppos)
 {
        char buf[3];
-       bool *val = file->private_data;
+       bool val;
+       int r, srcu_idx;
 
-       if (*val)
+       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)
+               return r;
+
+       if (val)
                buf[0] = 'Y';
        else
                buf[0] = 'N';
@@ -477,6 +766,7 @@ 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;
        bool *val = file->private_data;
 
        buf_size = min(count, (sizeof(buf)-1));
@@ -484,8 +774,14 @@ ssize_t debugfs_write_file_bool(struct file *file, const char __user *user_buf,
                return -EFAULT;
 
        buf[buf_size] = '\0';
-       if (strtobool(buf, &bv) == 0)
-               *val = bv;
+       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)
+                       return r;
+       }
 
        return count;
 }
@@ -537,7 +833,7 @@ static const struct file_operations fops_bool_wo = {
 struct dentry *debugfs_create_bool(const char *name, umode_t mode,
                                   struct dentry *parent, bool *value)
 {
-       return debugfs_create_mode(name, mode, parent, value, &fops_bool,
+       return debugfs_create_mode_unsafe(name, mode, parent, value, &fops_bool,
                                   &fops_bool_ro, &fops_bool_wo);
 }
 EXPORT_SYMBOL_GPL(debugfs_create_bool);
@@ -546,8 +842,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;
-       return simple_read_from_buffer(user_buf, count, ppos, blob->data,
-                       blob->size);
+       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);
+       return r;
 }
 
 static const struct file_operations fops_blob = {
@@ -584,7 +887,7 @@ struct dentry *debugfs_create_blob(const char *name, umode_t mode,
                                   struct dentry *parent,
                                   struct debugfs_blob_wrapper *blob)
 {
-       return debugfs_create_file(name, mode, parent, blob, &fops_blob);
+       return debugfs_create_file_unsafe(name, mode, parent, blob, &fops_blob);
 }
 EXPORT_SYMBOL_GPL(debugfs_create_blob);
 
@@ -689,7 +992,8 @@ struct dentry *debugfs_create_u32_array(const char *name, umode_t mode,
        data->array = array;
        data->elements = elements;
 
-       return debugfs_create_file(name, mode, parent, data, &u32_array_fops);
+       return debugfs_create_file_unsafe(name, mode, parent, data,
+                                       &u32_array_fops);
 }
 EXPORT_SYMBOL_GPL(debugfs_create_u32_array);
 
index 8580831ed237f4e81df7eb607bcd1a89c3766f33..4bc1f68243c1a67db2446097aa18b7ef21ead440 100644 (file)
 #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;
@@ -39,7 +44,8 @@ static struct inode *debugfs_get_inode(struct super_block *sb)
        struct inode *inode = new_inode(sb);
        if (inode) {
                inode->i_ino = get_next_ino();
-               inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+               inode->i_atime = inode->i_mtime =
+                       inode->i_ctime = current_fs_time(sb);
        }
        return inode;
 }
@@ -294,6 +300,37 @@ static struct dentry *end_creating(struct dentry *dentry)
        return dentry;
 }
 
+static struct dentry *__debugfs_create_file(const char *name, umode_t mode,
+                               struct dentry *parent, void *data,
+                               const struct file_operations *proxy_fops,
+                               const struct file_operations *real_fops)
+{
+       struct dentry *dentry;
+       struct inode *inode;
+
+       if (!(mode & S_IFMT))
+               mode |= S_IFREG;
+       BUG_ON(!S_ISREG(mode));
+       dentry = start_creating(name, parent);
+
+       if (IS_ERR(dentry))
+               return NULL;
+
+       inode = debugfs_get_inode(dentry->d_sb);
+       if (unlikely(!inode))
+               return failed_creating(dentry);
+
+       inode->i_mode = mode;
+       inode->i_private = data;
+
+       inode->i_fop = proxy_fops;
+       dentry->d_fsdata = (void *)real_fops;
+
+       d_instantiate(dentry, inode);
+       fsnotify_create(d_inode(dentry->d_parent), dentry);
+       return end_creating(dentry);
+}
+
 /**
  * debugfs_create_file - create a file in the debugfs filesystem
  * @name: a pointer to a string containing the name of the file to create.
@@ -324,29 +361,52 @@ struct dentry *debugfs_create_file(const char *name, umode_t mode,
                                   struct dentry *parent, void *data,
                                   const struct file_operations *fops)
 {
-       struct dentry *dentry;
-       struct inode *inode;
 
-       if (!(mode & S_IFMT))
-               mode |= S_IFREG;
-       BUG_ON(!S_ISREG(mode));
-       dentry = start_creating(name, parent);
-
-       if (IS_ERR(dentry))
-               return NULL;
+       return __debugfs_create_file(name, mode, parent, data,
+                               fops ? &debugfs_full_proxy_file_operations :
+                                       &debugfs_noop_file_operations,
+                               fops);
+}
+EXPORT_SYMBOL_GPL(debugfs_create_file);
 
-       inode = debugfs_get_inode(dentry->d_sb);
-       if (unlikely(!inode))
-               return failed_creating(dentry);
+/**
+ * debugfs_create_file_unsafe - create a file in the debugfs filesystem
+ * @name: a pointer to a string containing the name of the file to create.
+ * @mode: the permission that the file should have.
+ * @parent: a pointer to the parent dentry for this file.  This should be a
+ *          directory dentry if set.  If this parameter is NULL, then the
+ *          file will be created in the root of the debugfs filesystem.
+ * @data: a pointer to something that the caller will want to get to later
+ *        on.  The inode.i_private pointer will point to this value on
+ *        the open() call.
+ * @fops: a pointer to a struct file_operations that should be used for
+ *        this file.
+ *
+ * debugfs_create_file_unsafe() is completely analogous to
+ * debugfs_create_file(), the only difference being that the fops
+ * handed it will not get protected against file removals by the
+ * debugfs core.
+ *
+ * It is your responsibility to protect your struct file_operation
+ * methods against file removals by means of debugfs_use_file_start()
+ * and debugfs_use_file_finish(). ->open() is still protected by
+ * debugfs though.
+ *
+ * Any struct file_operations defined by means of
+ * DEFINE_DEBUGFS_ATTRIBUTE() is protected against file removals and
+ * thus, may be used here.
+ */
+struct dentry *debugfs_create_file_unsafe(const char *name, umode_t mode,
+                                  struct dentry *parent, void *data,
+                                  const struct file_operations *fops)
+{
 
-       inode->i_mode = mode;
-       inode->i_fop = fops ? fops : &debugfs_file_operations;
-       inode->i_private = data;
-       d_instantiate(dentry, inode);
-       fsnotify_create(d_inode(dentry->d_parent), dentry);
-       return end_creating(dentry);
+       return __debugfs_create_file(name, mode, parent, data,
+                               fops ? &debugfs_open_proxy_file_operations :
+                                       &debugfs_noop_file_operations,
+                               fops);
 }
-EXPORT_SYMBOL_GPL(debugfs_create_file);
+EXPORT_SYMBOL_GPL(debugfs_create_file_unsafe);
 
 /**
  * debugfs_create_file_size - create a file in the debugfs filesystem
@@ -461,7 +521,11 @@ struct dentry *debugfs_create_automount(const char *name,
        inode->i_flags |= S_AUTOMOUNT;
        inode->i_private = data;
        dentry->d_fsdata = (void *)f;
+       /* directory inodes start off with i_nlink == 2 (for "." entry) */
+       inc_nlink(inode);
        d_instantiate(dentry, inode);
+       inc_nlink(d_inode(dentry->d_parent));
+       fsnotify_mkdir(d_inode(dentry->d_parent), dentry);
        return end_creating(dentry);
 }
 EXPORT_SYMBOL(debugfs_create_automount);
@@ -565,6 +629,8 @@ 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);
 
@@ -642,6 +708,8 @@ 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);
 
diff --git a/fs/debugfs/internal.h b/fs/debugfs/internal.h
new file mode 100644 (file)
index 0000000..bba5263
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ *  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_
+#define _DEBUGFS_INTERNAL_H_
+
+struct file_operations;
+
+/* declared over in file.c */
+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 dentry *debugfs_create_file_unsafe(const char *name, umode_t mode,
+                                       struct dentry *parent, void *data,
+                                       const struct file_operations *fops);
+
+#endif /* _DEBUGFS_INTERNAL_H_ */
index 03b688d19f6964010c27c16759520315892c780d..eb2c58732bcfef41a7c23fba20694772276fab28 100644 (file)
@@ -753,7 +753,8 @@ int kernfs_add_one(struct kernfs_node *kn)
        ps_iattr = parent->iattr;
        if (ps_iattr) {
                struct iattr *ps_iattrs = &ps_iattr->ia_iattr;
-               ps_iattrs->ia_ctime = ps_iattrs->ia_mtime = CURRENT_TIME;
+               ktime_get_real_ts(&ps_iattrs->ia_ctime);
+               ps_iattrs->ia_mtime = ps_iattrs->ia_ctime;
        }
 
        mutex_unlock(&kernfs_mutex);
@@ -1279,8 +1280,9 @@ static void __kernfs_remove(struct kernfs_node *kn)
 
                        /* update timestamps on the parent */
                        if (ps_iattr) {
-                               ps_iattr->ia_iattr.ia_ctime = CURRENT_TIME;
-                               ps_iattr->ia_iattr.ia_mtime = CURRENT_TIME;
+                               ktime_get_real_ts(&ps_iattr->ia_iattr.ia_ctime);
+                               ps_iattr->ia_iattr.ia_mtime =
+                                       ps_iattr->ia_iattr.ia_ctime;
                        }
 
                        kernfs_put(pos);
index 16405ae88d2d657f278f2e5cf66f399582565eb0..1ac1dbae5aba380637a359e87b6220c6ac5c41b3 100644 (file)
@@ -54,7 +54,10 @@ static struct kernfs_iattrs *kernfs_iattrs(struct kernfs_node *kn)
        iattrs->ia_mode = kn->mode;
        iattrs->ia_uid = GLOBAL_ROOT_UID;
        iattrs->ia_gid = GLOBAL_ROOT_GID;
-       iattrs->ia_atime = iattrs->ia_mtime = iattrs->ia_ctime = CURRENT_TIME;
+
+       ktime_get_real_ts(&iattrs->ia_atime);
+       iattrs->ia_mtime = iattrs->ia_atime;
+       iattrs->ia_ctime = iattrs->ia_atime;
 
        simple_xattrs_init(&kn->iattr->xattrs);
 out_unlock:
@@ -236,16 +239,18 @@ ssize_t kernfs_iop_listxattr(struct dentry *dentry, char *buf, size_t size)
 static inline void set_default_inode_attr(struct inode *inode, umode_t mode)
 {
        inode->i_mode = mode;
-       inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+       inode->i_atime = inode->i_mtime =
+               inode->i_ctime = current_fs_time(inode->i_sb);
 }
 
 static inline void set_inode_attr(struct inode *inode, struct iattr *iattr)
 {
+       struct super_block *sb = inode->i_sb;
        inode->i_uid = iattr->ia_uid;
        inode->i_gid = iattr->ia_gid;
-       inode->i_atime = iattr->ia_atime;
-       inode->i_mtime = iattr->ia_mtime;
-       inode->i_ctime = iattr->ia_ctime;
+       inode->i_atime = timespec_trunc(iattr->ia_atime, sb->s_time_gran);
+       inode->i_mtime = timespec_trunc(iattr->ia_mtime, sb->s_time_gran);
+       inode->i_ctime = timespec_trunc(iattr->ia_ctime, sb->s_time_gran);
 }
 
 static void kernfs_refresh_inode(struct kernfs_node *kn, struct inode *inode)
index 981e53ab84e8e9d48948967d6a98bc90977df9cc..1438e2322d5cf4f15f67db29076d013a4cd157c7 100644 (file)
 #include <linux/seq_file.h>
 
 #include <linux/types.h>
+#include <linux/compiler.h>
 
 struct device;
 struct file_operations;
+struct srcu_struct;
 
 struct debugfs_blob_wrapper {
        void *data;
@@ -41,14 +43,16 @@ struct debugfs_regset32 {
 
 extern struct dentry *arch_debugfs_dir;
 
-#if defined(CONFIG_DEBUG_FS)
+extern struct srcu_struct debugfs_srcu;
 
-/* declared over in file.c */
-extern const struct file_operations debugfs_file_operations;
+#if defined(CONFIG_DEBUG_FS)
 
 struct dentry *debugfs_create_file(const char *name, umode_t mode,
                                   struct dentry *parent, void *data,
                                   const struct file_operations *fops);
+struct dentry *debugfs_create_file_unsafe(const char *name, umode_t mode,
+                                  struct dentry *parent, void *data,
+                                  const struct file_operations *fops);
 
 struct dentry *debugfs_create_file_size(const char *name, umode_t mode,
                                        struct dentry *parent, void *data,
@@ -68,6 +72,31 @@ 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);
+
+void debugfs_use_file_finish(int srcu_idx) __releases(&debugfs_srcu);
+
+ssize_t debugfs_attr_read(struct file *file, char __user *buf,
+                       size_t len, loff_t *ppos);
+ssize_t debugfs_attr_write(struct file *file, const char __user *buf,
+                       size_t len, loff_t *ppos);
+
+#define DEFINE_DEBUGFS_ATTRIBUTE(__fops, __get, __set, __fmt)          \
+static int __fops ## _open(struct inode *inode, struct file *file)     \
+{                                                                      \
+       __simple_attr_check_format(__fmt, 0ull);                        \
+       return simple_attr_open(inode, file, __get, __set, __fmt);      \
+}                                                                      \
+static const struct file_operations __fops = {                         \
+       .owner   = THIS_MODULE,                                 \
+       .open    = __fops ## _open,                                     \
+       .release = simple_attr_release,                         \
+       .read    = debugfs_attr_read,                                   \
+       .write   = debugfs_attr_write,                                  \
+       .llseek  = generic_file_llseek,                         \
+}
+
 struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry,
                 struct dentry *new_dir, const char *new_name);
 
@@ -176,6 +205,20 @@ 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)
+{
+       return 0;
+}
+
+static inline void debugfs_use_file_finish(int srcu_idx)
+       __releases(&debugfs_srcu)
+{ }
+
+#define DEFINE_DEBUGFS_ATTRIBUTE(__fops, __get, __set, __fmt)  \
+       static const struct file_operations __fops = { 0 }
+
 static inline struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry,
                 struct dentry *new_dir, char *new_name)
 {
index c0a360e99f6486fdb1a4e6554e8538260bedbc9e..269521f143ac254240ac6510f8fbdcb5412fd809 100644 (file)
@@ -1,3 +1,22 @@
+/*
+ * This file is provided under the GPLv2 license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ */
 #ifndef __DEVCOREDUMP_H
 #define __DEVCOREDUMP_H
 
 #include <linux/module.h>
 #include <linux/vmalloc.h>
 
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
+
+/*
+ * _devcd_free_sgtable - free all the memory of the given scatterlist table
+ * (i.e. both pages and scatterlist instances)
+ * NOTE: if two tables allocated and chained using the sg_chain function then
+ * this function should be called only once on the first table
+ * @table: pointer to sg_table to free
+ */
+static inline void _devcd_free_sgtable(struct scatterlist *table)
+{
+       int i;
+       struct page *page;
+       struct scatterlist *iter;
+       struct scatterlist *delete_iter;
+
+       /* free pages */
+       iter = table;
+       for_each_sg(table, iter, sg_nents(table), i) {
+               page = sg_page(iter);
+               if (page)
+                       __free_page(page);
+       }
+
+       /* then free all chained tables */
+       iter = table;
+       delete_iter = table;    /* always points on a head of a table */
+       while (!sg_is_last(iter)) {
+               iter++;
+               if (sg_is_chain(iter)) {
+                       iter = sg_chain_ptr(iter);
+                       kfree(delete_iter);
+                       delete_iter = iter;
+               }
+       }
+
+       /* free the last table */
+       kfree(delete_iter);
+}
+
+
 #ifdef CONFIG_DEV_COREDUMP
-void dev_coredumpv(struct device *dev, const void *data, size_t datalen,
+void dev_coredumpv(struct device *dev, void *data, size_t datalen,
                   gfp_t gfp);
 
 void dev_coredumpm(struct device *dev, struct module *owner,
-                  const void *data, size_t datalen, gfp_t gfp,
+                  void *data, size_t datalen, gfp_t gfp,
                   ssize_t (*read)(char *buffer, loff_t offset, size_t count,
-                                  const void *data, size_t datalen),
-                  void (*free)(const void *data));
+                                  void *data, size_t datalen),
+                  void (*free)(void *data));
+
+void dev_coredumpsg(struct device *dev, struct scatterlist *table,
+                   size_t datalen, gfp_t gfp);
 #else
-static inline void dev_coredumpv(struct device *dev, const void *data,
+static inline void dev_coredumpv(struct device *dev, void *data,
                                 size_t datalen, gfp_t gfp)
 {
        vfree(data);
@@ -23,13 +87,19 @@ static inline void dev_coredumpv(struct device *dev, const void *data,
 
 static inline void
 dev_coredumpm(struct device *dev, struct module *owner,
-             const void *data, size_t datalen, gfp_t gfp,
+             void *data, size_t datalen, gfp_t gfp,
              ssize_t (*read)(char *buffer, loff_t offset, size_t count,
-                             const void *data, size_t datalen),
-             void (*free)(const void *data))
+                             void *data, size_t datalen),
+             void (*free)(void *data))
 {
        free(data);
 }
+
+static inline void dev_coredumpsg(struct device *dev, struct scatterlist *table,
+                                 size_t datalen, gfp_t gfp)
+{
+       _devcd_free_sgtable(table);
+}
 #endif /* CONFIG_DEV_COREDUMP */
 
 #endif /* __DEVCOREDUMP_H */
index 70e61b58baaf662d15f4ca67f355b5dcf8d52438..ff7178348ff789ce6af085013402065874ab2e0c 100644 (file)
@@ -2395,6 +2395,8 @@ static inline void bd_unlink_disk_holder(struct block_device *bdev,
 
 /* fs/char_dev.c */
 #define CHRDEV_MAJOR_HASH_SIZE 255
+/* Marks the bottom of the first segment of free char majors */
+#define CHRDEV_MAJOR_DYN_END 234
 extern int alloc_chrdev_region(dev_t *, unsigned, unsigned, const char *);
 extern int register_chrdev_region(dev_t, unsigned, const char *);
 extern int __register_chrdev(unsigned int major, unsigned int baseminor,
index b0270e3814c8570e22bd06fb23fc4c16a0ae161f..2a02862775eb9c194964e3ef4a6039c7f0aa4133 100644 (file)
@@ -22,7 +22,7 @@ struct isa_driver {
 
 #define to_isa_driver(x) container_of((x), struct isa_driver, driver)
 
-#ifdef CONFIG_ISA
+#ifdef CONFIG_ISA_BUS
 int isa_register_driver(struct isa_driver *, unsigned int);
 void isa_unregister_driver(struct isa_driver *);
 #else
index 1e9a607534ca08a973deac3d1de62902b533b1cb..ddb0e8337aae675c958febafb7caad4d3f4f0792 100644 (file)
@@ -257,6 +257,7 @@ 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
diff --git a/scripts/coccinelle/api/debugfs/debugfs_simple_attr.cocci b/scripts/coccinelle/api/debugfs/debugfs_simple_attr.cocci
new file mode 100644 (file)
index 0000000..85cf540
--- /dev/null
@@ -0,0 +1,67 @@
+/// Use DEFINE_DEBUGFS_ATTRIBUTE rather than DEFINE_SIMPLE_ATTRIBUTE
+/// for debugfs files.
+///
+//# Rationale: DEFINE_SIMPLE_ATTRIBUTE + debugfs_create_file()
+//# imposes some significant overhead as compared to
+//# DEFINE_DEBUGFS_ATTRIBUTE + debugfs_create_file_unsafe().
+//
+// Copyright (C): 2016 Nicolai Stange
+// Options: --no-includes
+//
+
+virtual context
+virtual patch
+virtual org
+virtual report
+
+@dsa@
+declarer name DEFINE_SIMPLE_ATTRIBUTE;
+identifier dsa_fops;
+expression dsa_get, dsa_set, dsa_fmt;
+position p;
+@@
+DEFINE_SIMPLE_ATTRIBUTE@p(dsa_fops, dsa_get, dsa_set, dsa_fmt);
+
+@dcf@
+expression name, mode, parent, data;
+identifier dsa.dsa_fops;
+@@
+debugfs_create_file(name, mode, parent, data, &dsa_fops)
+
+
+@context_dsa depends on context && dcf@
+declarer name DEFINE_DEBUGFS_ATTRIBUTE;
+identifier dsa.dsa_fops;
+expression dsa.dsa_get, dsa.dsa_set, dsa.dsa_fmt;
+@@
+* DEFINE_SIMPLE_ATTRIBUTE(dsa_fops, dsa_get, dsa_set, dsa_fmt);
+
+
+@patch_dcf depends on patch expression@
+expression name, mode, parent, data;
+identifier dsa.dsa_fops;
+@@
+- debugfs_create_file(name, mode, parent, data, &dsa_fops)
++ debugfs_create_file_unsafe(name, mode, parent, data, &dsa_fops)
+
+@patch_dsa depends on patch_dcf && patch@
+identifier dsa.dsa_fops;
+expression dsa.dsa_get, dsa.dsa_set, dsa.dsa_fmt;
+@@
+- DEFINE_SIMPLE_ATTRIBUTE(dsa_fops, dsa_get, dsa_set, dsa_fmt);
++ DEFINE_DEBUGFS_ATTRIBUTE(dsa_fops, dsa_get, dsa_set, dsa_fmt);
+
+
+@script:python depends on org && dcf@
+fops << dsa.dsa_fops;
+p << dsa.p;
+@@
+msg="%s should be defined with DEFINE_DEBUGFS_ATTRIBUTE" % (fops)
+coccilib.org.print_todo(p[0], msg)
+
+@script:python depends on report && dcf@
+fops << dsa.dsa_fops;
+p << dsa.p;
+@@
+msg="WARNING: %s should be defined with DEFINE_DEBUGFS_ATTRIBUTE" % (fops)
+coccilib.report.print_report(p[0], msg)
This page took 0.053904 seconds and 5 git commands to generate.