Merge tag 'driver-core-3.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git...
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 1 Apr 2014 23:28:19 +0000 (16:28 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 1 Apr 2014 23:28:19 +0000 (16:28 -0700)
Pull driver core and sysfs updates from Greg KH:
 "Here's the big driver core / sysfs update for 3.15-rc1.

  Lots of kernfs updates to make it useful for other subsystems, and a
  few other tiny driver core patches.

  All have been in linux-next for a while"

* tag 'driver-core-3.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core: (42 commits)
  Revert "sysfs, driver-core: remove unused {sysfs|device}_schedule_callback_owner()"
  kernfs: cache atomic_write_len in kernfs_open_file
  numa: fix NULL pointer access and memory leak in unregister_one_node()
  Revert "driver core: synchronize device shutdown"
  kernfs: fix off by one error.
  kernfs: remove duplicate dir.c at the top dir
  x86: align x86 arch with generic CPU modalias handling
  cpu: add generic support for CPU feature based module autoloading
  sysfs: create bin_attributes under the requested group
  driver core: unexport static function create_syslog_header
  firmware: use power efficient workqueue for unloading and aborting fw load
  firmware: give a protection when map page failed
  firmware: google memconsole driver fixes
  firmware: fix google/gsmi duplicate efivars_sysfs_init()
  drivers/base: delete non-required instances of include <linux/init.h>
  kernfs: fix kernfs_node_from_dentry()
  ACPI / platform: drop redundant ACPI_HANDLE check
  kernfs: fix hash calculation in kernfs_rename_ns()
  kernfs: add CONFIG_KERNFS
  sysfs, kobject: add sysfs wrapper for kernfs_enable_ns()
  ...

53 files changed:
arch/s390/include/asm/ccwgroup.h
arch/s390/pci/pci_sysfs.c
arch/x86/Kconfig
arch/x86/include/asm/cpufeature.h
arch/x86/kernel/cpu/match.c
drivers/base/Kconfig
drivers/base/attribute_container.c
drivers/base/core.c
drivers/base/cpu.c
drivers/base/dma-buf.c
drivers/base/firmware_class.c
drivers/base/node.c
drivers/base/platform.c
drivers/base/power/clock_ops.c
drivers/base/power/common.c
drivers/base/power/domain.c
drivers/base/power/domain_governor.c
drivers/base/power/opp.c
drivers/base/regmap/regmap-i2c.c
drivers/base/regmap/regmap-mmio.c
drivers/base/regmap/regmap-spi.c
drivers/base/topology.c
drivers/firmware/google/gsmi.c
drivers/firmware/google/memconsole.c
drivers/gpu/drm/drm_prime.c
drivers/gpu/drm/exynos/exynos_drm_dmabuf.c
drivers/media/v4l2-core/videobuf2-dma-contig.c
drivers/pci/pci-sysfs.c
drivers/s390/block/dcssblk.c
drivers/s390/cio/ccwgroup.c
drivers/scsi/scsi_sysfs.c
fs/Kconfig
fs/Makefile
fs/kernfs/Kconfig [new file with mode: 0644]
fs/kernfs/dir.c
fs/kernfs/file.c
fs/kernfs/kernfs-internal.h
fs/kernfs/mount.c
fs/kernfs/symlink.c
fs/sysfs/Kconfig
fs/sysfs/dir.c
fs/sysfs/file.c
fs/sysfs/group.c
fs/sysfs/mount.c
include/linux/cpu.h
include/linux/cpufeature.h [new file with mode: 0644]
include/linux/device.h
include/linux/kernfs.h
include/linux/mod_devicetable.h
include/linux/sysfs.h
lib/kobject.c
scripts/mod/devicetable-offsets.c
scripts/mod/file2alias.c

index 23723ce5ca7a9b0b68585eb2faa2f4cdcd6cabc9..6e670f88d125d79fd2108575a6f6509e74985678 100644 (file)
@@ -23,6 +23,7 @@ struct ccwgroup_device {
        unsigned int count;
        struct device   dev;
        struct ccw_device *cdev[0];
+       struct work_struct ungroup_work;
 };
 
 /**
index cf8a12ff733b3883c6b8f32d626e14a47a173f92..ab4a91393005a920b876c610ed5d3d8605af5037 100644 (file)
@@ -48,29 +48,27 @@ static ssize_t show_pfgid(struct device *dev, struct device_attribute *attr,
 }
 static DEVICE_ATTR(pfgid, S_IRUGO, show_pfgid, NULL);
 
-static void recover_callback(struct device *dev)
+static ssize_t store_recover(struct device *dev, struct device_attribute *attr,
+                            const char *buf, size_t count)
 {
        struct pci_dev *pdev = to_pci_dev(dev);
        struct zpci_dev *zdev = get_zdev(pdev);
        int ret;
 
+       if (!device_remove_file_self(dev, attr))
+               return count;
+
        pci_stop_and_remove_bus_device(pdev);
        ret = zpci_disable_device(zdev);
        if (ret)
-               return;
+               return ret;
 
        ret = zpci_enable_device(zdev);
        if (ret)
-               return;
+               return ret;
 
        pci_rescan_bus(zdev->bus);
-}
-
-static ssize_t store_recover(struct device *dev, struct device_attribute *attr,
-                            const char *buf, size_t count)
-{
-       int rc = device_schedule_callback(dev, recover_callback);
-       return rc ? rc : count;
+       return count;
 }
 static DEVICE_ATTR(recover, S_IWUSR, NULL, store_recover);
 
index 8453fe1342eaf137bcf2c707bc93ac4717ee27db..26237934ac87b861fa68d139cd46d3c2cc24d12f 100644 (file)
@@ -127,6 +127,7 @@ config X86
        select HAVE_DEBUG_STACKOVERFLOW
        select HAVE_IRQ_EXIT_ON_IRQ_STACK if X86_64
        select HAVE_CC_STACKPROTECTOR
+       select GENERIC_CPU_AUTOPROBE
 
 config INSTRUCTION_DECODER
        def_bool y
@@ -195,9 +196,6 @@ config ARCH_HAS_CPU_RELAX
 config ARCH_HAS_CACHE_LINE_SIZE
        def_bool y
 
-config ARCH_HAS_CPU_AUTOPROBE
-       def_bool y
-
 config HAVE_SETUP_PER_CPU_AREA
        def_bool y
 
index 63211ef5046aef83ed4efd3ff163f5578202d119..e265ff95d16d6430a9986a39528d4c98d4d6b334 100644 (file)
@@ -546,6 +546,13 @@ static __always_inline __pure bool _static_cpu_has_safe(u16 bit)
 #define static_cpu_has_bug(bit)        static_cpu_has((bit))
 #define boot_cpu_has_bug(bit)  cpu_has_bug(&boot_cpu_data, (bit))
 
+#define MAX_CPU_FEATURES       (NCAPINTS * 32)
+#define cpu_have_feature       boot_cpu_has
+
+#define CPU_FEATURE_TYPEFMT    "x86,ven%04Xfam%04Xmod%04X"
+#define CPU_FEATURE_TYPEVAL    boot_cpu_data.x86_vendor, boot_cpu_data.x86, \
+                               boot_cpu_data.x86_model
+
 #endif /* defined(__KERNEL__) && !defined(__ASSEMBLY__) */
 
 #endif /* _ASM_X86_CPUFEATURE_H */
index 36565373af87b725b1f42a0b10eeaf07af9432d7..afa9f0d487ea07b79936fcd5b62040540102fad3 100644 (file)
@@ -47,45 +47,3 @@ const struct x86_cpu_id *x86_match_cpu(const struct x86_cpu_id *match)
        return NULL;
 }
 EXPORT_SYMBOL(x86_match_cpu);
-
-ssize_t arch_print_cpu_modalias(struct device *dev,
-                               struct device_attribute *attr,
-                               char *bufptr)
-{
-       int size = PAGE_SIZE;
-       int i, n;
-       char *buf = bufptr;
-
-       n = snprintf(buf, size, "x86cpu:vendor:%04X:family:%04X:"
-                    "model:%04X:feature:",
-               boot_cpu_data.x86_vendor,
-               boot_cpu_data.x86,
-               boot_cpu_data.x86_model);
-       size -= n;
-       buf += n;
-       size -= 1;
-       for (i = 0; i < NCAPINTS*32; i++) {
-               if (boot_cpu_has(i)) {
-                       n = snprintf(buf, size, ",%04X", i);
-                       if (n >= size) {
-                               WARN(1, "x86 features overflow page\n");
-                               break;
-                       }
-                       size -= n;
-                       buf += n;
-               }
-       }
-       *buf++ = '\n';
-       return buf - bufptr;
-}
-
-int arch_cpu_uevent(struct device *dev, struct kobj_uevent_env *env)
-{
-       char *buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
-       if (buf) {
-               arch_print_cpu_modalias(NULL, NULL, buf);
-               add_uevent_var(env, "MODALIAS=%s", buf);
-               kfree(buf);
-       }
-       return 0;
-}
index ec36e7772e576667f3e7784daca1735ac4eefd87..8fa8deab64492469ecdc75443aa4f67fdfb5b974 100644 (file)
@@ -185,6 +185,9 @@ config GENERIC_CPU_DEVICES
        bool
        default n
 
+config GENERIC_CPU_AUTOPROBE
+       bool
+
 config SOC_BUS
        bool
 
index ecc1929d7f6a943a086112ad5d61df8816fba8ad..b84ca8f13f9e1361d51b82a90f29cc525fb9c33e 100644 (file)
@@ -12,7 +12,6 @@
  */
 
 #include <linux/attribute_container.h>
-#include <linux/init.h>
 #include <linux/device.h>
 #include <linux/kernel.h>
 #include <linux/slab.h>
index 2b567177ef782973b7e1ad14c065154ca76a986e..0dd65281cc65bb6a368143bea0373c2c373787dd 100644 (file)
@@ -23,7 +23,6 @@
 #include <linux/genhd.h>
 #include <linux/kallsyms.h>
 #include <linux/mutex.h>
-#include <linux/async.h>
 #include <linux/pm_runtime.h>
 #include <linux/netdevice.h>
 #include <linux/sysfs.h>
@@ -570,6 +569,23 @@ void device_remove_file(struct device *dev,
 }
 EXPORT_SYMBOL_GPL(device_remove_file);
 
+/**
+ * device_remove_file_self - remove sysfs attribute file from its own method.
+ * @dev: device.
+ * @attr: device attribute descriptor.
+ *
+ * See kernfs_remove_self() for details.
+ */
+bool device_remove_file_self(struct device *dev,
+                            const struct device_attribute *attr)
+{
+       if (dev)
+               return sysfs_remove_file_self(&dev->kobj, &attr->attr);
+       else
+               return false;
+}
+EXPORT_SYMBOL_GPL(device_remove_file_self);
+
 /**
  * device_create_bin_file - create sysfs binary attribute file for device.
  * @dev: device.
@@ -2003,7 +2019,6 @@ void device_shutdown(void)
                spin_lock(&devices_kset->list_lock);
        }
        spin_unlock(&devices_kset->list_lock);
-       async_synchronize_full();
 }
 
 /*
@@ -2058,7 +2073,6 @@ create_syslog_header(const struct device *dev, char *hdr, size_t hdrlen)
 
        return pos;
 }
-EXPORT_SYMBOL(create_syslog_header);
 
 int dev_vprintk_emit(int level, const struct device *dev,
                     const char *fmt, va_list args)
index f48370dfc908e19a73a86cf817ad05128c0ed034..006b1bc5297d292ba4d6e649b87c24f9bf1a9a82 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/percpu.h>
 #include <linux/acpi.h>
 #include <linux/of.h>
+#include <linux/cpufeature.h>
 
 #include "base.h"
 
@@ -286,6 +287,41 @@ static void cpu_device_release(struct device *dev)
         */
 }
 
+#ifdef CONFIG_GENERIC_CPU_AUTOPROBE
+static ssize_t print_cpu_modalias(struct device *dev,
+                                 struct device_attribute *attr,
+                                 char *buf)
+{
+       ssize_t n;
+       u32 i;
+
+       n = sprintf(buf, "cpu:type:" CPU_FEATURE_TYPEFMT ":feature:",
+                   CPU_FEATURE_TYPEVAL);
+
+       for (i = 0; i < MAX_CPU_FEATURES; i++)
+               if (cpu_have_feature(i)) {
+                       if (PAGE_SIZE < n + sizeof(",XXXX\n")) {
+                               WARN(1, "CPU features overflow page\n");
+                               break;
+                       }
+                       n += sprintf(&buf[n], ",%04X", i);
+               }
+       buf[n++] = '\n';
+       return n;
+}
+
+static int cpu_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+       char *buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
+       if (buf) {
+               print_cpu_modalias(NULL, NULL, buf);
+               add_uevent_var(env, "MODALIAS=%s", buf);
+               kfree(buf);
+       }
+       return 0;
+}
+#endif
+
 /*
  * register_cpu - Setup a sysfs device for a CPU.
  * @cpu - cpu->hotpluggable field set to 1 will generate a control file in
@@ -306,8 +342,8 @@ int register_cpu(struct cpu *cpu, int num)
        cpu->dev.offline_disabled = !cpu->hotpluggable;
        cpu->dev.offline = !cpu_online(num);
        cpu->dev.of_node = of_get_cpu_node(num, NULL);
-#ifdef CONFIG_ARCH_HAS_CPU_AUTOPROBE
-       cpu->dev.bus->uevent = arch_cpu_uevent;
+#ifdef CONFIG_GENERIC_CPU_AUTOPROBE
+       cpu->dev.bus->uevent = cpu_uevent;
 #endif
        cpu->dev.groups = common_cpu_attr_groups;
        if (cpu->hotpluggable)
@@ -330,8 +366,8 @@ struct device *get_cpu_device(unsigned cpu)
 }
 EXPORT_SYMBOL_GPL(get_cpu_device);
 
-#ifdef CONFIG_ARCH_HAS_CPU_AUTOPROBE
-static DEVICE_ATTR(modalias, 0444, arch_print_cpu_modalias, NULL);
+#ifdef CONFIG_GENERIC_CPU_AUTOPROBE
+static DEVICE_ATTR(modalias, 0444, print_cpu_modalias, NULL);
 #endif
 
 static struct attribute *cpu_root_attrs[] = {
@@ -344,7 +380,7 @@ static struct attribute *cpu_root_attrs[] = {
        &cpu_attrs[2].attr.attr,
        &dev_attr_kernel_max.attr,
        &dev_attr_offline.attr,
-#ifdef CONFIG_ARCH_HAS_CPU_AUTOPROBE
+#ifdef CONFIG_GENERIC_CPU_AUTOPROBE
        &dev_attr_modalias.attr,
 #endif
        NULL
index 61d6d62cc0d35ff17d6e9736d9704ad3848e22a1..ea77701deda40074b4b59a03384106b4acb4fd14 100644 (file)
@@ -251,9 +251,8 @@ EXPORT_SYMBOL_GPL(dma_buf_put);
  * @dmabuf:    [in]    buffer to attach device to.
  * @dev:       [in]    device to be attached.
  *
- * Returns struct dma_buf_attachment * for this attachment; may return negative
- * error codes.
- *
+ * Returns struct dma_buf_attachment * for this attachment; returns ERR_PTR on
+ * error.
  */
 struct dma_buf_attachment *dma_buf_attach(struct dma_buf *dmabuf,
                                          struct device *dev)
@@ -319,9 +318,8 @@ EXPORT_SYMBOL_GPL(dma_buf_detach);
  * @attach:    [in]    attachment whose scatterlist is to be returned
  * @direction: [in]    direction of DMA transfer
  *
- * Returns sg_table containing the scatterlist to be returned; may return NULL
- * or ERR_PTR.
- *
+ * Returns sg_table containing the scatterlist to be returned; returns ERR_PTR
+ * on error.
  */
 struct sg_table *dma_buf_map_attachment(struct dma_buf_attachment *attach,
                                        enum dma_data_direction direction)
@@ -334,6 +332,8 @@ struct sg_table *dma_buf_map_attachment(struct dma_buf_attachment *attach,
                return ERR_PTR(-EINVAL);
 
        sg_table = attach->dmabuf->ops->map_dma_buf(attach, direction);
+       if (!sg_table)
+               sg_table = ERR_PTR(-ENOMEM);
 
        return sg_table;
 }
@@ -544,6 +544,8 @@ EXPORT_SYMBOL_GPL(dma_buf_mmap);
  * These calls are optional in drivers. The intended use for them
  * is for mapping objects linear in kernel space for high use objects.
  * Please attempt to use kmap/kunmap before thinking about these interfaces.
+ *
+ * Returns NULL on error.
  */
 void *dma_buf_vmap(struct dma_buf *dmabuf)
 {
@@ -566,7 +568,9 @@ void *dma_buf_vmap(struct dma_buf *dmabuf)
        BUG_ON(dmabuf->vmap_ptr);
 
        ptr = dmabuf->ops->vmap(dmabuf);
-       if (IS_ERR_OR_NULL(ptr))
+       if (WARN_ON_ONCE(IS_ERR(ptr)))
+               ptr = NULL;
+       if (!ptr)
                goto out_unlock;
 
        dmabuf->vmap_ptr = ptr;
index c30df50e4440c3af5639af58dd567c37e8bf40e6..d276e33880be1e632a95f5a960e4c0eab6c7f983 100644 (file)
@@ -649,7 +649,9 @@ static ssize_t firmware_loading_store(struct device *dev,
                         * see the mapped 'buf->data' once the loading
                         * is completed.
                         * */
-                       fw_map_pages_buf(fw_buf);
+                       if (fw_map_pages_buf(fw_buf))
+                               dev_err(dev, "%s: map pages failed\n",
+                                       __func__);
                        list_del_init(&fw_buf->pending_list);
                        complete_all(&fw_buf->completion);
                        break;
@@ -900,7 +902,8 @@ static int _request_firmware_load(struct firmware_priv *fw_priv,
                dev_set_uevent_suppress(f_dev, false);
                dev_dbg(f_dev, "firmware: requesting %s\n", buf->fw_id);
                if (timeout != MAX_SCHEDULE_TIMEOUT)
-                       schedule_delayed_work(&fw_priv->timeout_work, timeout);
+                       queue_delayed_work(system_power_efficient_wq,
+                                          &fw_priv->timeout_work, timeout);
 
                kobject_uevent(&fw_priv->dev.kobj, KOBJ_ADD);
        }
@@ -908,6 +911,8 @@ static int _request_firmware_load(struct firmware_priv *fw_priv,
        wait_for_completion(&buf->completion);
 
        cancel_delayed_work_sync(&fw_priv->timeout_work);
+       if (!buf->data)
+               retval = -ENOMEM;
 
        device_remove_file(f_dev, &dev_attr_loading);
 err_del_bin_attr:
@@ -1570,8 +1575,8 @@ static void device_uncache_fw_images_work(struct work_struct *work)
  */
 static void device_uncache_fw_images_delay(unsigned long delay)
 {
-       schedule_delayed_work(&fw_cache.work,
-                       msecs_to_jiffies(delay));
+       queue_delayed_work(system_power_efficient_wq, &fw_cache.work,
+                          msecs_to_jiffies(delay));
 }
 
 static int fw_pm_notify(struct notifier_block *notify_block,
index bc9f43bf7e29a46714cb1f220bb0d866c5d3eab8..8f7ed9933a7c6939d7703badeeacb97fcfc803aa 100644 (file)
@@ -599,7 +599,11 @@ int register_one_node(int nid)
 
 void unregister_one_node(int nid)
 {
+       if (!node_devices[nid])
+               return;
+
        unregister_node(node_devices[nid]);
+       kfree(node_devices[nid]);
        node_devices[nid] = NULL;
 }
 
index bc78848dd59aec9af389b9ce8caa4be984c4cc42..e714709704e4578ccc3703ca30108e596461ad28 100644 (file)
@@ -481,11 +481,10 @@ static int platform_drv_probe(struct device *_dev)
        struct platform_device *dev = to_platform_device(_dev);
        int ret;
 
-       if (ACPI_HANDLE(_dev))
-               acpi_dev_pm_attach(_dev, true);
+       acpi_dev_pm_attach(_dev, true);
 
        ret = drv->probe(dev);
-       if (ret && ACPI_HANDLE(_dev))
+       if (ret)
                acpi_dev_pm_detach(_dev, true);
 
        if (drv->prevent_deferred_probe && ret == -EPROBE_DEFER) {
@@ -508,8 +507,7 @@ static int platform_drv_remove(struct device *_dev)
        int ret;
 
        ret = drv->remove(dev);
-       if (ACPI_HANDLE(_dev))
-               acpi_dev_pm_detach(_dev, true);
+       acpi_dev_pm_detach(_dev, true);
 
        return ret;
 }
@@ -520,8 +518,7 @@ static void platform_drv_shutdown(struct device *_dev)
        struct platform_device *dev = to_platform_device(_dev);
 
        drv->shutdown(dev);
-       if (ACPI_HANDLE(_dev))
-               acpi_dev_pm_detach(_dev, true);
+       acpi_dev_pm_detach(_dev, true);
 }
 
 /**
index e870bbe9ec4e674d91fb3bede6e4105fd25ccd6a..b99e6c06ee678ecb5bcc6e206d3954976832eb38 100644 (file)
@@ -6,7 +6,6 @@
  * This file is released under the GPLv2.
  */
 
-#include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/device.h>
 #include <linux/io.h>
index 5da914041305907d30f26289f12643b5c581be07..df2e5eeaeb05570757ec7872c2022484c1824c87 100644 (file)
@@ -6,7 +6,6 @@
  * This file is released under the GPLv2.
  */
 
-#include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/device.h>
 #include <linux/export.h>
index dc127e5dec4b5371e65048f9923e80d24d7a85b0..6f54962aae1dd1a6160b777238b37f639f03f80f 100644 (file)
@@ -6,7 +6,6 @@
  * This file is released under the GPLv2.
  */
 
-#include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/io.h>
 #include <linux/pm_runtime.h>
index 28dee3053f1fa14e4e29afedbc29260b757feb77..a089e3bcdfbc5d7ee4aa6850b90f34e9fd2a9efb 100644 (file)
@@ -6,7 +6,6 @@
  * This file is released under the GPLv2.
  */
 
-#include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/pm_domain.h>
 #include <linux/pm_qos.h>
index fa41874184401cd46c155526102fb72aed99e329..25538675d59e29ced3e8f72db2f31fe1427b920f 100644 (file)
@@ -14,7 +14,6 @@
 #include <linux/kernel.h>
 #include <linux/errno.h>
 #include <linux/err.h>
-#include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/cpufreq.h>
 #include <linux/device.h>
index fa6bf5279d28465f095c0829381854a718313e5e..ebd189529760369df33ab021326763b7866c997d 100644 (file)
@@ -13,7 +13,6 @@
 #include <linux/regmap.h>
 #include <linux/i2c.h>
 #include <linux/module.h>
-#include <linux/init.h>
 
 static int regmap_i2c_write(void *context, const void *data, size_t count)
 {
index de45a1e1548fa31894c2afd3af1b658869939afe..1e03e7f8bacb220a47d0edf29700f6a1814fc21e 100644 (file)
@@ -18,7 +18,6 @@
 
 #include <linux/clk.h>
 #include <linux/err.h>
-#include <linux/init.h>
 #include <linux/io.h>
 #include <linux/module.h>
 #include <linux/regmap.h>
index 37f12ae7aadaeadc60005b2bced37ac05bc7210d..0eb3097c0d7649f7c2b8e4aa495f3f84b5d04d36 100644 (file)
@@ -12,7 +12,6 @@
 
 #include <linux/regmap.h>
 #include <linux/spi/spi.h>
-#include <linux/init.h>
 #include <linux/module.h>
 
 #include "internal.h"
index 94ffee378f103d6cd2018497a5f1a93ebcde9baf..ad9d177626640377de6e517fe6d62db093039166 100644 (file)
@@ -23,7 +23,6 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  */
-#include <linux/init.h>
 #include <linux/mm.h>
 #include <linux/cpu.h>
 #include <linux/module.h>
index e5a67b24587ac0efe1848dc08a6b465ef744f553..f1ab05ea56bbc61ca85adf16b438016a1f762349 100644 (file)
@@ -892,13 +892,6 @@ static __init int gsmi_init(void)
                goto out_remove_sysfs_files;
        }
 
-       ret = efivars_sysfs_init();
-       if (ret) {
-               printk(KERN_INFO "gsmi: Failed to create efivars files\n");
-               efivars_unregister(&efivars);
-               goto out_remove_sysfs_files;
-       }
-
        register_reboot_notifier(&gsmi_reboot_notifier);
        register_die_notifier(&gsmi_die_notifier);
        atomic_notifier_chain_register(&panic_notifier_list,
index 2a90ba613613e40ac74affb4a03e908ee17c622c..2f569aaed4c754d361b04c3f09da717db35471e3 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/kobject.h>
 #include <linux/module.h>
 #include <linux/dmi.h>
+#include <linux/io.h>
 #include <asm/bios_ebda.h>
 
 #define BIOS_MEMCONSOLE_V1_MAGIC       0xDEADBABE
@@ -41,15 +42,25 @@ struct biosmemcon_ebda {
        };
 } __packed;
 
-static char *memconsole_baseaddr;
+static u32 memconsole_baseaddr;
 static size_t memconsole_length;
 
 static ssize_t memconsole_read(struct file *filp, struct kobject *kobp,
                               struct bin_attribute *bin_attr, char *buf,
                               loff_t pos, size_t count)
 {
-       return memory_read_from_buffer(buf, count, &pos, memconsole_baseaddr,
-                                      memconsole_length);
+       char *memconsole;
+       ssize_t ret;
+
+       memconsole = ioremap_cache(memconsole_baseaddr, memconsole_length);
+       if (!memconsole) {
+               pr_err("memconsole: ioremap_cache failed\n");
+               return -ENOMEM;
+       }
+       ret = memory_read_from_buffer(buf, count, &pos, memconsole,
+                                     memconsole_length);
+       iounmap(memconsole);
+       return ret;
 }
 
 static struct bin_attribute memconsole_bin_attr = {
@@ -58,43 +69,42 @@ static struct bin_attribute memconsole_bin_attr = {
 };
 
 
-static void found_v1_header(struct biosmemcon_ebda *hdr)
+static void __init found_v1_header(struct biosmemcon_ebda *hdr)
 {
-       printk(KERN_INFO "BIOS console v1 EBDA structure found at %p\n", hdr);
-       printk(KERN_INFO "BIOS console buffer at 0x%.8x, "
+       pr_info("BIOS console v1 EBDA structure found at %p\n", hdr);
+       pr_info("BIOS console buffer at 0x%.8x, "
               "start = %d, end = %d, num = %d\n",
               hdr->v1.buffer_addr, hdr->v1.start,
               hdr->v1.end, hdr->v1.num_chars);
 
        memconsole_length = hdr->v1.num_chars;
-       memconsole_baseaddr = phys_to_virt(hdr->v1.buffer_addr);
+       memconsole_baseaddr = hdr->v1.buffer_addr;
 }
 
-static void found_v2_header(struct biosmemcon_ebda *hdr)
+static void __init found_v2_header(struct biosmemcon_ebda *hdr)
 {
-       printk(KERN_INFO "BIOS console v2 EBDA structure found at %p\n", hdr);
-       printk(KERN_INFO "BIOS console buffer at 0x%.8x, "
+       pr_info("BIOS console v2 EBDA structure found at %p\n", hdr);
+       pr_info("BIOS console buffer at 0x%.8x, "
               "start = %d, end = %d, num_bytes = %d\n",
               hdr->v2.buffer_addr, hdr->v2.start,
               hdr->v2.end, hdr->v2.num_bytes);
 
        memconsole_length = hdr->v2.end - hdr->v2.start;
-       memconsole_baseaddr = phys_to_virt(hdr->v2.buffer_addr
-                                          + hdr->v2.start);
+       memconsole_baseaddr = hdr->v2.buffer_addr + hdr->v2.start;
 }
 
 /*
  * Search through the EBDA for the BIOS Memory Console, and
  * set the global variables to point to it.  Return true if found.
  */
-static bool found_memconsole(void)
+static bool __init found_memconsole(void)
 {
        unsigned int address;
        size_t length, cur;
 
        address = get_bios_ebda();
        if (!address) {
-               printk(KERN_INFO "BIOS EBDA non-existent.\n");
+               pr_info("BIOS EBDA non-existent.\n");
                return false;
        }
 
@@ -122,7 +132,7 @@ static bool found_memconsole(void)
                }
        }
 
-       printk(KERN_INFO "BIOS console EBDA structure not found!\n");
+       pr_info("BIOS console EBDA structure not found!\n");
        return false;
 }
 
@@ -139,8 +149,6 @@ MODULE_DEVICE_TABLE(dmi, memconsole_dmi_table);
 
 static int __init memconsole_init(void)
 {
-       int ret;
-
        if (!dmi_check_system(memconsole_dmi_table))
                return -ENODEV;
 
@@ -148,10 +156,7 @@ static int __init memconsole_init(void)
                return -ENODEV;
 
        memconsole_bin_attr.size = memconsole_length;
-
-       ret = sysfs_create_bin_file(firmware_kobj, &memconsole_bin_attr);
-
-       return ret;
+       return sysfs_create_bin_file(firmware_kobj, &memconsole_bin_attr);
 }
 
 static void __exit memconsole_exit(void)
index 56805c39c906dfa64326dd8003e6445889f7840e..bb516fdd195d463ca7627a2a1a69cb8d665a03fb 100644 (file)
@@ -471,7 +471,7 @@ struct drm_gem_object *drm_gem_prime_import(struct drm_device *dev,
        get_dma_buf(dma_buf);
 
        sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL);
-       if (IS_ERR_OR_NULL(sgt)) {
+       if (IS_ERR(sgt)) {
                ret = PTR_ERR(sgt);
                goto fail_detach;
        }
index 59827cc5e77090d7f1aa03e470a5c4cf40d4bb63..c786cd4f457bb8893fc4f02e8e27338832b1bc58 100644 (file)
@@ -224,7 +224,7 @@ struct drm_gem_object *exynos_dmabuf_prime_import(struct drm_device *drm_dev,
        get_dma_buf(dma_buf);
 
        sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL);
-       if (IS_ERR_OR_NULL(sgt)) {
+       if (IS_ERR(sgt)) {
                ret = PTR_ERR(sgt);
                goto err_buf_detach;
        }
index 33d3871d1e131dce9a2f1d8392020dce5b8ff799..880be0782dd9d707db81d7f26068cf5700e6cba6 100644 (file)
@@ -719,7 +719,7 @@ static int vb2_dc_map_dmabuf(void *mem_priv)
 
        /* get the associated scatterlist for this buffer */
        sgt = dma_buf_map_attachment(buf->db_attach, buf->dma_dir);
-       if (IS_ERR_OR_NULL(sgt)) {
+       if (IS_ERR(sgt)) {
                pr_err("Error getting dmabuf scatterlist\n");
                return -EINVAL;
        }
index 276ef9c18802c07cb8371b1147acf12529ef3e0e..4e0acefb7565d0f3b81013855efa85c86ca5a98e 100644 (file)
@@ -351,28 +351,17 @@ static struct device_attribute dev_rescan_attr = __ATTR(rescan,
                                                        (S_IWUSR|S_IWGRP),
                                                        NULL, dev_rescan_store);
 
-static void remove_callback(struct device *dev)
-{
-       pci_stop_and_remove_bus_device_locked(to_pci_dev(dev));
-}
-
 static ssize_t
-remove_store(struct device *dev, struct device_attribute *dummy,
+remove_store(struct device *dev, struct device_attribute *attr,
             const char *buf, size_t count)
 {
-       int ret = 0;
        unsigned long val;
 
        if (kstrtoul(buf, 0, &val) < 0)
                return -EINVAL;
 
-       /* An attribute cannot be unregistered by one of its own methods,
-        * so we have to use this roundabout approach.
-        */
-       if (val)
-               ret = device_schedule_callback(dev, remove_callback);
-       if (ret)
-               count = ret;
+       if (val && device_remove_file_self(dev, attr))
+               pci_stop_and_remove_bus_device_locked(to_pci_dev(dev));
        return count;
 }
 static struct device_attribute dev_remove_attr = __ATTR(remove,
index ebf41e228e55836e6ec764b105c357e1856c9d1b..ee0e85abe1fd940fe6360a9444c4e7ed267dfd6c 100644 (file)
@@ -304,12 +304,6 @@ dcssblk_load_segment(char *name, struct segment_info **seg_info)
        return rc;
 }
 
-static void dcssblk_unregister_callback(struct device *dev)
-{
-       device_unregister(dev);
-       put_device(dev);
-}
-
 /*
  * device attribute for switching shared/nonshared (exclusive)
  * operation (show + store)
@@ -397,7 +391,13 @@ removeseg:
        blk_cleanup_queue(dev_info->dcssblk_queue);
        dev_info->gd->queue = NULL;
        put_disk(dev_info->gd);
-       rc = device_schedule_callback(dev, dcssblk_unregister_callback);
+       up_write(&dcssblk_devices_sem);
+
+       if (device_remove_file_self(dev, attr)) {
+               device_unregister(dev);
+               put_device(dev);
+       }
+       return rc;
 out:
        up_write(&dcssblk_devices_sem);
        return rc;
index fd3367a1dc7a0bc59cca626e5b7ada39a56bb39c..dfd7bc681c255d62f07b6471048a8342388c78bd 100644 (file)
@@ -168,14 +168,12 @@ static ssize_t ccwgroup_online_show(struct device *dev,
  * Provide an 'ungroup' attribute so the user can remove group devices no
  * longer needed or accidentially created. Saves memory :)
  */
-static void ccwgroup_ungroup_callback(struct device *dev)
+static void ccwgroup_ungroup(struct ccwgroup_device *gdev)
 {
-       struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
-
        mutex_lock(&gdev->reg_mutex);
        if (device_is_registered(&gdev->dev)) {
                __ccwgroup_remove_symlinks(gdev);
-               device_unregister(dev);
+               device_unregister(&gdev->dev);
                __ccwgroup_remove_cdev_refs(gdev);
        }
        mutex_unlock(&gdev->reg_mutex);
@@ -195,10 +193,9 @@ static ssize_t ccwgroup_ungroup_store(struct device *dev,
                rc = -EINVAL;
                goto out;
        }
-       /* Note that we cannot unregister the device from one of its
-        * attribute methods, so we have to use this roundabout approach.
-        */
-       rc = device_schedule_callback(dev, ccwgroup_ungroup_callback);
+
+       if (device_remove_file_self(dev, attr))
+               ccwgroup_ungroup(gdev);
 out:
        if (rc) {
                if (rc != -EAGAIN)
@@ -224,6 +221,14 @@ static const struct attribute_group *ccwgroup_attr_groups[] = {
        NULL,
 };
 
+static void ccwgroup_ungroup_workfn(struct work_struct *work)
+{
+       struct ccwgroup_device *gdev =
+               container_of(work, struct ccwgroup_device, ungroup_work);
+
+       ccwgroup_ungroup(gdev);
+}
+
 static void ccwgroup_release(struct device *dev)
 {
        kfree(to_ccwgroupdev(dev));
@@ -323,6 +328,7 @@ int ccwgroup_create_dev(struct device *parent, struct ccwgroup_driver *gdrv,
        atomic_set(&gdev->onoff, 0);
        mutex_init(&gdev->reg_mutex);
        mutex_lock(&gdev->reg_mutex);
+       INIT_WORK(&gdev->ungroup_work, ccwgroup_ungroup_workfn);
        gdev->count = num_devices;
        gdev->dev.bus = &ccwgroup_bus_type;
        gdev->dev.parent = parent;
@@ -404,10 +410,10 @@ EXPORT_SYMBOL(ccwgroup_create_dev);
 static int ccwgroup_notifier(struct notifier_block *nb, unsigned long action,
                             void *data)
 {
-       struct device *dev = data;
+       struct ccwgroup_device *gdev = to_ccwgroupdev(data);
 
        if (action == BUS_NOTIFY_UNBIND_DRIVER)
-               device_schedule_callback(dev, ccwgroup_ungroup_callback);
+               schedule_work(&gdev->ungroup_work);
 
        return NOTIFY_OK;
 }
index 9117d0bf408e8ea438f716761c59f2bbeee51bc5..8ead24c3453ab81ae80b58ba99b1c393667733bc 100644 (file)
@@ -649,23 +649,12 @@ store_rescan_field (struct device *dev, struct device_attribute *attr,
 }
 static DEVICE_ATTR(rescan, S_IWUSR, NULL, store_rescan_field);
 
-static void sdev_store_delete_callback(struct device *dev)
-{
-       scsi_remove_device(to_scsi_device(dev));
-}
-
 static ssize_t
 sdev_store_delete(struct device *dev, struct device_attribute *attr,
                  const char *buf, size_t count)
 {
-       int rc;
-
-       /* An attribute cannot be unregistered by one of its own methods,
-        * so we have to use this roundabout approach.
-        */
-       rc = device_schedule_callback(dev, sdev_store_delete_callback);
-       if (rc)
-               count = rc;
+       if (device_remove_file_self(dev, attr))
+               scsi_remove_device(to_scsi_device(dev));
        return count;
 };
 static DEVICE_ATTR(delete, S_IWUSR, NULL, sdev_store_delete);
index 7385e54be4b9adbdd8a4c2d0ed3cafd3b516820a..312393f32948689169b8f569e23093d52eed637c 100644 (file)
@@ -96,6 +96,7 @@ endif # BLOCK
 menu "Pseudo filesystems"
 
 source "fs/proc/Kconfig"
+source "fs/kernfs/Kconfig"
 source "fs/sysfs/Kconfig"
 
 config TMPFS
index 47ac07bb4acc6b44b318776c44e7a6cd7bf79113..f9cb9876e466a02f7c0149412bea8a177031c509 100644 (file)
@@ -52,7 +52,8 @@ obj-$(CONFIG_FHANDLE)         += fhandle.o
 obj-y                          += quota/
 
 obj-$(CONFIG_PROC_FS)          += proc/
-obj-$(CONFIG_SYSFS)            += sysfs/ kernfs/
+obj-$(CONFIG_KERNFS)           += kernfs/
+obj-$(CONFIG_SYSFS)            += sysfs/
 obj-$(CONFIG_CONFIGFS_FS)      += configfs/
 obj-y                          += devpts/
 
diff --git a/fs/kernfs/Kconfig b/fs/kernfs/Kconfig
new file mode 100644 (file)
index 0000000..397b5f7
--- /dev/null
@@ -0,0 +1,7 @@
+#
+# KERNFS should be selected by its users
+#
+
+config KERNFS
+       bool
+       default n
index bd6e18be6e1a231c10867bc8f4e292e49353d03f..0bd05ab26003c49f5ecfb349641754a3dc568408 100644 (file)
@@ -8,6 +8,7 @@
  * This file is released under the GPLv2.
  */
 
+#include <linux/sched.h>
 #include <linux/fs.h>
 #include <linux/namei.h>
 #include <linux/idr.h>
 #include "kernfs-internal.h"
 
 DEFINE_MUTEX(kernfs_mutex);
+static DEFINE_SPINLOCK(kernfs_rename_lock);    /* kn->parent and ->name */
+static char kernfs_pr_cont_buf[PATH_MAX];      /* protected by rename_lock */
 
 #define rb_to_kn(X) rb_entry((X), struct kernfs_node, rb)
 
+static bool kernfs_active(struct kernfs_node *kn)
+{
+       lockdep_assert_held(&kernfs_mutex);
+       return atomic_read(&kn->active) >= 0;
+}
+
+static bool kernfs_lockdep(struct kernfs_node *kn)
+{
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+       return kn->flags & KERNFS_LOCKDEP;
+#else
+       return false;
+#endif
+}
+
+static int kernfs_name_locked(struct kernfs_node *kn, char *buf, size_t buflen)
+{
+       return strlcpy(buf, kn->parent ? kn->name : "/", buflen);
+}
+
+static char * __must_check kernfs_path_locked(struct kernfs_node *kn, char *buf,
+                                             size_t buflen)
+{
+       char *p = buf + buflen;
+       int len;
+
+       *--p = '\0';
+
+       do {
+               len = strlen(kn->name);
+               if (p - buf < len + 1) {
+                       buf[0] = '\0';
+                       p = NULL;
+                       break;
+               }
+               p -= len;
+               memcpy(p, kn->name, len);
+               *--p = '/';
+               kn = kn->parent;
+       } while (kn && kn->parent);
+
+       return p;
+}
+
+/**
+ * kernfs_name - obtain the name of a given node
+ * @kn: kernfs_node of interest
+ * @buf: buffer to copy @kn's name into
+ * @buflen: size of @buf
+ *
+ * Copies the name of @kn into @buf of @buflen bytes.  The behavior is
+ * similar to strlcpy().  It returns the length of @kn's name and if @buf
+ * isn't long enough, it's filled upto @buflen-1 and nul terminated.
+ *
+ * This function can be called from any context.
+ */
+int kernfs_name(struct kernfs_node *kn, char *buf, size_t buflen)
+{
+       unsigned long flags;
+       int ret;
+
+       spin_lock_irqsave(&kernfs_rename_lock, flags);
+       ret = kernfs_name_locked(kn, buf, buflen);
+       spin_unlock_irqrestore(&kernfs_rename_lock, flags);
+       return ret;
+}
+
+/**
+ * kernfs_path - build full path of a given node
+ * @kn: kernfs_node of interest
+ * @buf: buffer to copy @kn's name into
+ * @buflen: size of @buf
+ *
+ * Builds and returns the full path of @kn in @buf of @buflen bytes.  The
+ * path is built from the end of @buf so the returned pointer usually
+ * doesn't match @buf.  If @buf isn't long enough, @buf is nul terminated
+ * and %NULL is returned.
+ */
+char *kernfs_path(struct kernfs_node *kn, char *buf, size_t buflen)
+{
+       unsigned long flags;
+       char *p;
+
+       spin_lock_irqsave(&kernfs_rename_lock, flags);
+       p = kernfs_path_locked(kn, buf, buflen);
+       spin_unlock_irqrestore(&kernfs_rename_lock, flags);
+       return p;
+}
+
+/**
+ * pr_cont_kernfs_name - pr_cont name of a kernfs_node
+ * @kn: kernfs_node of interest
+ *
+ * This function can be called from any context.
+ */
+void pr_cont_kernfs_name(struct kernfs_node *kn)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&kernfs_rename_lock, flags);
+
+       kernfs_name_locked(kn, kernfs_pr_cont_buf, sizeof(kernfs_pr_cont_buf));
+       pr_cont("%s", kernfs_pr_cont_buf);
+
+       spin_unlock_irqrestore(&kernfs_rename_lock, flags);
+}
+
+/**
+ * pr_cont_kernfs_path - pr_cont path of a kernfs_node
+ * @kn: kernfs_node of interest
+ *
+ * This function can be called from any context.
+ */
+void pr_cont_kernfs_path(struct kernfs_node *kn)
+{
+       unsigned long flags;
+       char *p;
+
+       spin_lock_irqsave(&kernfs_rename_lock, flags);
+
+       p = kernfs_path_locked(kn, kernfs_pr_cont_buf,
+                              sizeof(kernfs_pr_cont_buf));
+       if (p)
+               pr_cont("%s", p);
+       else
+               pr_cont("<name too long>");
+
+       spin_unlock_irqrestore(&kernfs_rename_lock, flags);
+}
+
+/**
+ * kernfs_get_parent - determine the parent node and pin it
+ * @kn: kernfs_node of interest
+ *
+ * Determines @kn's parent, pins and returns it.  This function can be
+ * called from any context.
+ */
+struct kernfs_node *kernfs_get_parent(struct kernfs_node *kn)
+{
+       struct kernfs_node *parent;
+       unsigned long flags;
+
+       spin_lock_irqsave(&kernfs_rename_lock, flags);
+       parent = kn->parent;
+       kernfs_get(parent);
+       spin_unlock_irqrestore(&kernfs_rename_lock, flags);
+
+       return parent;
+}
+
 /**
  *     kernfs_name_hash
  *     @name: Null terminated string to hash
@@ -37,7 +190,7 @@ static unsigned int kernfs_name_hash(const char *name, const void *ns)
        hash = (end_name_hash(hash) ^ hash_ptr((void *)ns, 31));
        hash &= 0x7fffffffU;
        /* Reserve hash numbers 0, 1 and INT_MAX for magic directory entries */
-       if (hash < 1)
+       if (hash < 2)
                hash += 2;
        if (hash >= INT_MAX)
                hash = INT_MAX - 1;
@@ -105,18 +258,24 @@ static int kernfs_link_sibling(struct kernfs_node *kn)
  *     kernfs_unlink_sibling - unlink kernfs_node from sibling rbtree
  *     @kn: kernfs_node of interest
  *
- *     Unlink @kn from its sibling rbtree which starts from
- *     kn->parent->dir.children.
+ *     Try to unlink @kn from its sibling rbtree which starts from
+ *     kn->parent->dir.children.  Returns %true if @kn was actually
+ *     removed, %false if @kn wasn't on the rbtree.
  *
  *     Locking:
  *     mutex_lock(kernfs_mutex)
  */
-static void kernfs_unlink_sibling(struct kernfs_node *kn)
+static bool kernfs_unlink_sibling(struct kernfs_node *kn)
 {
+       if (RB_EMPTY_NODE(&kn->rb))
+               return false;
+
        if (kernfs_type(kn) == KERNFS_DIR)
                kn->parent->dir.subdirs--;
 
        rb_erase(&kn->rb, &kn->parent->dir.children);
+       RB_CLEAR_NODE(&kn->rb);
+       return true;
 }
 
 /**
@@ -137,7 +296,7 @@ struct kernfs_node *kernfs_get_active(struct kernfs_node *kn)
        if (!atomic_inc_unless_negative(&kn->active))
                return NULL;
 
-       if (kn->flags & KERNFS_LOCKDEP)
+       if (kernfs_lockdep(kn))
                rwsem_acquire_read(&kn->dep_map, 0, 1, _RET_IP_);
        return kn;
 }
@@ -151,59 +310,57 @@ struct kernfs_node *kernfs_get_active(struct kernfs_node *kn)
  */
 void kernfs_put_active(struct kernfs_node *kn)
 {
+       struct kernfs_root *root = kernfs_root(kn);
        int v;
 
        if (unlikely(!kn))
                return;
 
-       if (kn->flags & KERNFS_LOCKDEP)
+       if (kernfs_lockdep(kn))
                rwsem_release(&kn->dep_map, 1, _RET_IP_);
        v = atomic_dec_return(&kn->active);
        if (likely(v != KN_DEACTIVATED_BIAS))
                return;
 
-       /*
-        * atomic_dec_return() is a mb(), we'll always see the updated
-        * kn->u.completion.
-        */
-       complete(kn->u.completion);
+       wake_up_all(&root->deactivate_waitq);
 }
 
 /**
- *     kernfs_deactivate - deactivate kernfs_node
- *     @kn: kernfs_node to deactivate
+ * kernfs_drain - drain kernfs_node
+ * @kn: kernfs_node to drain
  *
- *     Deny new active references and drain existing ones.
+ * Drain existing usages and nuke all existing mmaps of @kn.  Mutiple
+ * removers may invoke this function concurrently on @kn and all will
+ * return after draining is complete.
  */
-static void kernfs_deactivate(struct kernfs_node *kn)
+static void kernfs_drain(struct kernfs_node *kn)
+       __releases(&kernfs_mutex) __acquires(&kernfs_mutex)
 {
-       DECLARE_COMPLETION_ONSTACK(wait);
-       int v;
+       struct kernfs_root *root = kernfs_root(kn);
 
-       BUG_ON(!(kn->flags & KERNFS_REMOVED));
-
-       if (!(kernfs_type(kn) & KERNFS_ACTIVE_REF))
-               return;
+       lockdep_assert_held(&kernfs_mutex);
+       WARN_ON_ONCE(kernfs_active(kn));
 
-       kn->u.completion = (void *)&wait;
+       mutex_unlock(&kernfs_mutex);
 
-       if (kn->flags & KERNFS_LOCKDEP)
+       if (kernfs_lockdep(kn)) {
                rwsem_acquire(&kn->dep_map, 0, 0, _RET_IP_);
-       /* atomic_add_return() is a mb(), put_active() will always see
-        * the updated kn->u.completion.
-        */
-       v = atomic_add_return(KN_DEACTIVATED_BIAS, &kn->active);
-
-       if (v != KN_DEACTIVATED_BIAS) {
-               if (kn->flags & KERNFS_LOCKDEP)
+               if (atomic_read(&kn->active) != KN_DEACTIVATED_BIAS)
                        lock_contended(&kn->dep_map, _RET_IP_);
-               wait_for_completion(&wait);
        }
 
-       if (kn->flags & KERNFS_LOCKDEP) {
+       /* but everyone should wait for draining */
+       wait_event(root->deactivate_waitq,
+                  atomic_read(&kn->active) == KN_DEACTIVATED_BIAS);
+
+       if (kernfs_lockdep(kn)) {
                lock_acquired(&kn->dep_map, _RET_IP_);
                rwsem_release(&kn->dep_map, 1, _RET_IP_);
        }
+
+       kernfs_unmap_bin_file(kn);
+
+       mutex_lock(&kernfs_mutex);
 }
 
 /**
@@ -234,13 +391,15 @@ void kernfs_put(struct kernfs_node *kn)
                return;
        root = kernfs_root(kn);
  repeat:
-       /* Moving/renaming is always done while holding reference.
+       /*
+        * Moving/renaming is always done while holding reference.
         * kn->parent won't change beneath us.
         */
        parent = kn->parent;
 
-       WARN(!(kn->flags & KERNFS_REMOVED), "kernfs: free using entry: %s/%s\n",
-            parent ? parent->name : "", kn->name);
+       WARN_ONCE(atomic_read(&kn->active) != KN_DEACTIVATED_BIAS,
+                 "kernfs_put: %s/%s: released with incorrect active_ref %d\n",
+                 parent ? parent->name : "", kn->name, atomic_read(&kn->active));
 
        if (kernfs_type(kn) == KERNFS_LINK)
                kernfs_put(kn->symlink.target_kn);
@@ -282,8 +441,8 @@ static int kernfs_dop_revalidate(struct dentry *dentry, unsigned int flags)
        kn = dentry->d_fsdata;
        mutex_lock(&kernfs_mutex);
 
-       /* The kernfs node has been deleted */
-       if (kn->flags & KERNFS_REMOVED)
+       /* The kernfs node has been deactivated */
+       if (!kernfs_active(kn))
                goto out_bad;
 
        /* The kernfs node has been moved? */
@@ -328,6 +487,24 @@ const struct dentry_operations kernfs_dops = {
        .d_release      = kernfs_dop_release,
 };
 
+/**
+ * kernfs_node_from_dentry - determine kernfs_node associated with a dentry
+ * @dentry: the dentry in question
+ *
+ * Return the kernfs_node associated with @dentry.  If @dentry is not a
+ * kernfs one, %NULL is returned.
+ *
+ * While the returned kernfs_node will stay accessible as long as @dentry
+ * is accessible, the returned node can be in any state and the caller is
+ * fully responsible for determining what's accessible.
+ */
+struct kernfs_node *kernfs_node_from_dentry(struct dentry *dentry)
+{
+       if (dentry->d_sb->s_op == &kernfs_sops)
+               return dentry->d_fsdata;
+       return NULL;
+}
+
 static struct kernfs_node *__kernfs_new_node(struct kernfs_root *root,
                                             const char *name, umode_t mode,
                                             unsigned flags)
@@ -352,11 +529,12 @@ static struct kernfs_node *__kernfs_new_node(struct kernfs_root *root,
        kn->ino = ret;
 
        atomic_set(&kn->count, 1);
-       atomic_set(&kn->active, 0);
+       atomic_set(&kn->active, KN_DEACTIVATED_BIAS);
+       RB_CLEAR_NODE(&kn->rb);
 
        kn->name = name;
        kn->mode = mode;
-       kn->flags = flags | KERNFS_REMOVED;
+       kn->flags = flags;
 
        return kn;
 
@@ -381,70 +559,45 @@ struct kernfs_node *kernfs_new_node(struct kernfs_node *parent,
        return kn;
 }
 
-/**
- *     kernfs_addrm_start - prepare for kernfs_node add/remove
- *     @acxt: pointer to kernfs_addrm_cxt to be used
- *
- *     This function is called when the caller is about to add or remove
- *     kernfs_node.  This function acquires kernfs_mutex.  @acxt is used
- *     to keep and pass context to other addrm functions.
- *
- *     LOCKING:
- *     Kernel thread context (may sleep).  kernfs_mutex is locked on
- *     return.
- */
-void kernfs_addrm_start(struct kernfs_addrm_cxt *acxt)
-       __acquires(kernfs_mutex)
-{
-       memset(acxt, 0, sizeof(*acxt));
-
-       mutex_lock(&kernfs_mutex);
-}
-
 /**
  *     kernfs_add_one - add kernfs_node to parent without warning
- *     @acxt: addrm context to use
  *     @kn: kernfs_node to be added
  *
  *     The caller must already have initialized @kn->parent.  This
  *     function increments nlink of the parent's inode if @kn is a
  *     directory and link into the children list of the parent.
  *
- *     This function should be called between calls to
- *     kernfs_addrm_start() and kernfs_addrm_finish() and should be passed
- *     the same @acxt as passed to kernfs_addrm_start().
- *
- *     LOCKING:
- *     Determined by kernfs_addrm_start().
- *
  *     RETURNS:
  *     0 on success, -EEXIST if entry with the given name already
  *     exists.
  */
-int kernfs_add_one(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn)
+int kernfs_add_one(struct kernfs_node *kn)
 {
        struct kernfs_node *parent = kn->parent;
-       bool has_ns = kernfs_ns_enabled(parent);
        struct kernfs_iattrs *ps_iattr;
+       bool has_ns;
        int ret;
 
-       if (has_ns != (bool)kn->ns) {
-               WARN(1, KERN_WARNING "kernfs: ns %s in '%s' for '%s'\n",
-                    has_ns ? "required" : "invalid", parent->name, kn->name);
-               return -EINVAL;
-       }
+       mutex_lock(&kernfs_mutex);
+
+       ret = -EINVAL;
+       has_ns = kernfs_ns_enabled(parent);
+       if (WARN(has_ns != (bool)kn->ns, KERN_WARNING "kernfs: ns %s in '%s' for '%s'\n",
+                has_ns ? "required" : "invalid", parent->name, kn->name))
+               goto out_unlock;
 
        if (kernfs_type(parent) != KERNFS_DIR)
-               return -EINVAL;
+               goto out_unlock;
 
-       if (parent->flags & KERNFS_REMOVED)
-               return -ENOENT;
+       ret = -ENOENT;
+       if ((parent->flags & KERNFS_ACTIVATED) && !kernfs_active(parent))
+               goto out_unlock;
 
        kn->hash = kernfs_name_hash(kn->name, kn->ns);
 
        ret = kernfs_link_sibling(kn);
        if (ret)
-               return ret;
+               goto out_unlock;
 
        /* Update timestamps on the parent */
        ps_iattr = parent->iattr;
@@ -453,82 +606,22 @@ int kernfs_add_one(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn)
                ps_iattrs->ia_ctime = ps_iattrs->ia_mtime = CURRENT_TIME;
        }
 
-       /* Mark the entry added into directory tree */
-       kn->flags &= ~KERNFS_REMOVED;
-
-       return 0;
-}
-
-/**
- *     kernfs_remove_one - remove kernfs_node from parent
- *     @acxt: addrm context to use
- *     @kn: kernfs_node to be removed
- *
- *     Mark @kn removed and drop nlink of parent inode if @kn is a
- *     directory.  @kn is unlinked from the children list.
- *
- *     This function should be called between calls to
- *     kernfs_addrm_start() and kernfs_addrm_finish() and should be
- *     passed the same @acxt as passed to kernfs_addrm_start().
- *
- *     LOCKING:
- *     Determined by kernfs_addrm_start().
- */
-static void kernfs_remove_one(struct kernfs_addrm_cxt *acxt,
-                             struct kernfs_node *kn)
-{
-       struct kernfs_iattrs *ps_iattr;
+       mutex_unlock(&kernfs_mutex);
 
        /*
-        * Removal can be called multiple times on the same node.  Only the
-        * first invocation is effective and puts the base ref.
+        * Activate the new node unless CREATE_DEACTIVATED is requested.
+        * If not activated here, the kernfs user is responsible for
+        * activating the node with kernfs_activate().  A node which hasn't
+        * been activated is not visible to userland and its removal won't
+        * trigger deactivation.
         */
-       if (kn->flags & KERNFS_REMOVED)
-               return;
-
-       if (kn->parent) {
-               kernfs_unlink_sibling(kn);
-
-               /* Update timestamps on the parent */
-               ps_iattr = kn->parent->iattr;
-               if (ps_iattr) {
-                       ps_iattr->ia_iattr.ia_ctime = CURRENT_TIME;
-                       ps_iattr->ia_iattr.ia_mtime = CURRENT_TIME;
-               }
-       }
-
-       kn->flags |= KERNFS_REMOVED;
-       kn->u.removed_list = acxt->removed;
-       acxt->removed = kn;
-}
+       if (!(kernfs_root(kn)->flags & KERNFS_ROOT_CREATE_DEACTIVATED))
+               kernfs_activate(kn);
+       return 0;
 
-/**
- *     kernfs_addrm_finish - finish up kernfs_node add/remove
- *     @acxt: addrm context to finish up
- *
- *     Finish up kernfs_node add/remove.  Resources acquired by
- *     kernfs_addrm_start() are released and removed kernfs_nodes are
- *     cleaned up.
- *
- *     LOCKING:
- *     kernfs_mutex is released.
- */
-void kernfs_addrm_finish(struct kernfs_addrm_cxt *acxt)
-       __releases(kernfs_mutex)
-{
-       /* release resources acquired by kernfs_addrm_start() */
+out_unlock:
        mutex_unlock(&kernfs_mutex);
-
-       /* kill removed kernfs_nodes */
-       while (acxt->removed) {
-               struct kernfs_node *kn = acxt->removed;
-
-               acxt->removed = kn->u.removed_list;
-
-               kernfs_deactivate(kn);
-               kernfs_unmap_bin_file(kn);
-               kernfs_put(kn);
-       }
+       return ret;
 }
 
 /**
@@ -599,13 +692,15 @@ EXPORT_SYMBOL_GPL(kernfs_find_and_get_ns);
 
 /**
  * kernfs_create_root - create a new kernfs hierarchy
- * @kdops: optional directory syscall operations for the hierarchy
+ * @scops: optional syscall operations for the hierarchy
+ * @flags: KERNFS_ROOT_* flags
  * @priv: opaque data associated with the new directory
  *
  * Returns the root of the new hierarchy on success, ERR_PTR() value on
  * failure.
  */
-struct kernfs_root *kernfs_create_root(struct kernfs_dir_ops *kdops, void *priv)
+struct kernfs_root *kernfs_create_root(struct kernfs_syscall_ops *scops,
+                                      unsigned int flags, void *priv)
 {
        struct kernfs_root *root;
        struct kernfs_node *kn;
@@ -624,12 +719,16 @@ struct kernfs_root *kernfs_create_root(struct kernfs_dir_ops *kdops, void *priv)
                return ERR_PTR(-ENOMEM);
        }
 
-       kn->flags &= ~KERNFS_REMOVED;
        kn->priv = priv;
        kn->dir.root = root;
 
-       root->dir_ops = kdops;
+       root->syscall_ops = scops;
+       root->flags = flags;
        root->kn = kn;
+       init_waitqueue_head(&root->deactivate_waitq);
+
+       if (!(root->flags & KERNFS_ROOT_CREATE_DEACTIVATED))
+               kernfs_activate(kn);
 
        return root;
 }
@@ -660,7 +759,6 @@ struct kernfs_node *kernfs_create_dir_ns(struct kernfs_node *parent,
                                         const char *name, umode_t mode,
                                         void *priv, const void *ns)
 {
-       struct kernfs_addrm_cxt acxt;
        struct kernfs_node *kn;
        int rc;
 
@@ -674,10 +772,7 @@ struct kernfs_node *kernfs_create_dir_ns(struct kernfs_node *parent,
        kn->priv = priv;
 
        /* link in */
-       kernfs_addrm_start(&acxt);
-       rc = kernfs_add_one(&acxt, kn);
-       kernfs_addrm_finish(&acxt);
-
+       rc = kernfs_add_one(kn);
        if (!rc)
                return kn;
 
@@ -703,7 +798,7 @@ static struct dentry *kernfs_iop_lookup(struct inode *dir,
        kn = kernfs_find_ns(parent, dentry->d_name.name, ns);
 
        /* no such entry */
-       if (!kn) {
+       if (!kn || !kernfs_active(kn)) {
                ret = NULL;
                goto out_unlock;
        }
@@ -728,23 +823,37 @@ static int kernfs_iop_mkdir(struct inode *dir, struct dentry *dentry,
                            umode_t mode)
 {
        struct kernfs_node *parent = dir->i_private;
-       struct kernfs_dir_ops *kdops = kernfs_root(parent)->dir_ops;
+       struct kernfs_syscall_ops *scops = kernfs_root(parent)->syscall_ops;
+       int ret;
 
-       if (!kdops || !kdops->mkdir)
+       if (!scops || !scops->mkdir)
                return -EPERM;
 
-       return kdops->mkdir(parent, dentry->d_name.name, mode);
+       if (!kernfs_get_active(parent))
+               return -ENODEV;
+
+       ret = scops->mkdir(parent, dentry->d_name.name, mode);
+
+       kernfs_put_active(parent);
+       return ret;
 }
 
 static int kernfs_iop_rmdir(struct inode *dir, struct dentry *dentry)
 {
        struct kernfs_node *kn  = dentry->d_fsdata;
-       struct kernfs_dir_ops *kdops = kernfs_root(kn)->dir_ops;
+       struct kernfs_syscall_ops *scops = kernfs_root(kn)->syscall_ops;
+       int ret;
 
-       if (!kdops || !kdops->rmdir)
+       if (!scops || !scops->rmdir)
                return -EPERM;
 
-       return kdops->rmdir(kn);
+       if (!kernfs_get_active(kn))
+               return -ENODEV;
+
+       ret = scops->rmdir(kn);
+
+       kernfs_put_active(kn);
+       return ret;
 }
 
 static int kernfs_iop_rename(struct inode *old_dir, struct dentry *old_dentry,
@@ -752,12 +861,25 @@ static int kernfs_iop_rename(struct inode *old_dir, struct dentry *old_dentry,
 {
        struct kernfs_node *kn  = old_dentry->d_fsdata;
        struct kernfs_node *new_parent = new_dir->i_private;
-       struct kernfs_dir_ops *kdops = kernfs_root(kn)->dir_ops;
+       struct kernfs_syscall_ops *scops = kernfs_root(kn)->syscall_ops;
+       int ret;
 
-       if (!kdops || !kdops->rename)
+       if (!scops || !scops->rename)
                return -EPERM;
 
-       return kdops->rename(kn, new_parent, new_dentry->d_name.name);
+       if (!kernfs_get_active(kn))
+               return -ENODEV;
+
+       if (!kernfs_get_active(new_parent)) {
+               kernfs_put_active(kn);
+               return -ENODEV;
+       }
+
+       ret = scops->rename(kn, new_parent, new_dentry->d_name.name);
+
+       kernfs_put_active(new_parent);
+       kernfs_put_active(kn);
+       return ret;
 }
 
 const struct inode_operations kernfs_dir_iops = {
@@ -830,23 +952,104 @@ static struct kernfs_node *kernfs_next_descendant_post(struct kernfs_node *pos,
        return pos->parent;
 }
 
-static void __kernfs_remove(struct kernfs_addrm_cxt *acxt,
-                           struct kernfs_node *kn)
+/**
+ * kernfs_activate - activate a node which started deactivated
+ * @kn: kernfs_node whose subtree is to be activated
+ *
+ * If the root has KERNFS_ROOT_CREATE_DEACTIVATED set, a newly created node
+ * needs to be explicitly activated.  A node which hasn't been activated
+ * isn't visible to userland and deactivation is skipped during its
+ * removal.  This is useful to construct atomic init sequences where
+ * creation of multiple nodes should either succeed or fail atomically.
+ *
+ * The caller is responsible for ensuring that this function is not called
+ * after kernfs_remove*() is invoked on @kn.
+ */
+void kernfs_activate(struct kernfs_node *kn)
 {
-       struct kernfs_node *pos, *next;
+       struct kernfs_node *pos;
 
-       if (!kn)
+       mutex_lock(&kernfs_mutex);
+
+       pos = NULL;
+       while ((pos = kernfs_next_descendant_post(pos, kn))) {
+               if (!pos || (pos->flags & KERNFS_ACTIVATED))
+                       continue;
+
+               WARN_ON_ONCE(pos->parent && RB_EMPTY_NODE(&pos->rb));
+               WARN_ON_ONCE(atomic_read(&pos->active) != KN_DEACTIVATED_BIAS);
+
+               atomic_sub(KN_DEACTIVATED_BIAS, &pos->active);
+               pos->flags |= KERNFS_ACTIVATED;
+       }
+
+       mutex_unlock(&kernfs_mutex);
+}
+
+static void __kernfs_remove(struct kernfs_node *kn)
+{
+       struct kernfs_node *pos;
+
+       lockdep_assert_held(&kernfs_mutex);
+
+       /*
+        * Short-circuit if non-root @kn has already finished removal.
+        * This is for kernfs_remove_self() which plays with active ref
+        * after removal.
+        */
+       if (!kn || (kn->parent && RB_EMPTY_NODE(&kn->rb)))
                return;
 
        pr_debug("kernfs %s: removing\n", kn->name);
 
-       next = NULL;
+       /* prevent any new usage under @kn by deactivating all nodes */
+       pos = NULL;
+       while ((pos = kernfs_next_descendant_post(pos, kn)))
+               if (kernfs_active(pos))
+                       atomic_add(KN_DEACTIVATED_BIAS, &pos->active);
+
+       /* deactivate and unlink the subtree node-by-node */
        do {
-               pos = next;
-               next = kernfs_next_descendant_post(pos, kn);
-               if (pos)
-                       kernfs_remove_one(acxt, pos);
-       } while (next);
+               pos = kernfs_leftmost_descendant(kn);
+
+               /*
+                * kernfs_drain() drops kernfs_mutex temporarily and @pos's
+                * base ref could have been put by someone else by the time
+                * the function returns.  Make sure it doesn't go away
+                * underneath us.
+                */
+               kernfs_get(pos);
+
+               /*
+                * Drain iff @kn was activated.  This avoids draining and
+                * its lockdep annotations for nodes which have never been
+                * activated and allows embedding kernfs_remove() in create
+                * error paths without worrying about draining.
+                */
+               if (kn->flags & KERNFS_ACTIVATED)
+                       kernfs_drain(pos);
+               else
+                       WARN_ON_ONCE(atomic_read(&kn->active) != KN_DEACTIVATED_BIAS);
+
+               /*
+                * kernfs_unlink_sibling() succeeds once per node.  Use it
+                * to decide who's responsible for cleanups.
+                */
+               if (!pos->parent || kernfs_unlink_sibling(pos)) {
+                       struct kernfs_iattrs *ps_iattr =
+                               pos->parent ? pos->parent->iattr : NULL;
+
+                       /* update timestamps on the parent */
+                       if (ps_iattr) {
+                               ps_iattr->ia_iattr.ia_ctime = CURRENT_TIME;
+                               ps_iattr->ia_iattr.ia_mtime = CURRENT_TIME;
+                       }
+
+                       kernfs_put(pos);
+               }
+
+               kernfs_put(pos);
+       } while (pos != kn);
 }
 
 /**
@@ -857,11 +1060,140 @@ static void __kernfs_remove(struct kernfs_addrm_cxt *acxt,
  */
 void kernfs_remove(struct kernfs_node *kn)
 {
-       struct kernfs_addrm_cxt acxt;
+       mutex_lock(&kernfs_mutex);
+       __kernfs_remove(kn);
+       mutex_unlock(&kernfs_mutex);
+}
 
-       kernfs_addrm_start(&acxt);
-       __kernfs_remove(&acxt, kn);
-       kernfs_addrm_finish(&acxt);
+/**
+ * kernfs_break_active_protection - break out of active protection
+ * @kn: the self kernfs_node
+ *
+ * The caller must be running off of a kernfs operation which is invoked
+ * with an active reference - e.g. one of kernfs_ops.  Each invocation of
+ * this function must also be matched with an invocation of
+ * kernfs_unbreak_active_protection().
+ *
+ * This function releases the active reference of @kn the caller is
+ * holding.  Once this function is called, @kn may be removed at any point
+ * and the caller is solely responsible for ensuring that the objects it
+ * dereferences are accessible.
+ */
+void kernfs_break_active_protection(struct kernfs_node *kn)
+{
+       /*
+        * Take out ourself out of the active ref dependency chain.  If
+        * we're called without an active ref, lockdep will complain.
+        */
+       kernfs_put_active(kn);
+}
+
+/**
+ * kernfs_unbreak_active_protection - undo kernfs_break_active_protection()
+ * @kn: the self kernfs_node
+ *
+ * If kernfs_break_active_protection() was called, this function must be
+ * invoked before finishing the kernfs operation.  Note that while this
+ * function restores the active reference, it doesn't and can't actually
+ * restore the active protection - @kn may already or be in the process of
+ * being removed.  Once kernfs_break_active_protection() is invoked, that
+ * protection is irreversibly gone for the kernfs operation instance.
+ *
+ * While this function may be called at any point after
+ * kernfs_break_active_protection() is invoked, its most useful location
+ * would be right before the enclosing kernfs operation returns.
+ */
+void kernfs_unbreak_active_protection(struct kernfs_node *kn)
+{
+       /*
+        * @kn->active could be in any state; however, the increment we do
+        * here will be undone as soon as the enclosing kernfs operation
+        * finishes and this temporary bump can't break anything.  If @kn
+        * is alive, nothing changes.  If @kn is being deactivated, the
+        * soon-to-follow put will either finish deactivation or restore
+        * deactivated state.  If @kn is already removed, the temporary
+        * bump is guaranteed to be gone before @kn is released.
+        */
+       atomic_inc(&kn->active);
+       if (kernfs_lockdep(kn))
+               rwsem_acquire(&kn->dep_map, 0, 1, _RET_IP_);
+}
+
+/**
+ * kernfs_remove_self - remove a kernfs_node from its own method
+ * @kn: the self kernfs_node to remove
+ *
+ * The caller must be running off of a kernfs operation which is invoked
+ * with an active reference - e.g. one of kernfs_ops.  This can be used to
+ * implement a file operation which deletes itself.
+ *
+ * For example, the "delete" file for a sysfs device directory can be
+ * implemented by invoking kernfs_remove_self() on the "delete" file
+ * itself.  This function breaks the circular dependency of trying to
+ * deactivate self while holding an active ref itself.  It isn't necessary
+ * to modify the usual removal path to use kernfs_remove_self().  The
+ * "delete" implementation can simply invoke kernfs_remove_self() on self
+ * before proceeding with the usual removal path.  kernfs will ignore later
+ * kernfs_remove() on self.
+ *
+ * kernfs_remove_self() can be called multiple times concurrently on the
+ * same kernfs_node.  Only the first one actually performs removal and
+ * returns %true.  All others will wait until the kernfs operation which
+ * won self-removal finishes and return %false.  Note that the losers wait
+ * for the completion of not only the winning kernfs_remove_self() but also
+ * the whole kernfs_ops which won the arbitration.  This can be used to
+ * guarantee, for example, all concurrent writes to a "delete" file to
+ * finish only after the whole operation is complete.
+ */
+bool kernfs_remove_self(struct kernfs_node *kn)
+{
+       bool ret;
+
+       mutex_lock(&kernfs_mutex);
+       kernfs_break_active_protection(kn);
+
+       /*
+        * SUICIDAL is used to arbitrate among competing invocations.  Only
+        * the first one will actually perform removal.  When the removal
+        * is complete, SUICIDED is set and the active ref is restored
+        * while holding kernfs_mutex.  The ones which lost arbitration
+        * waits for SUICDED && drained which can happen only after the
+        * enclosing kernfs operation which executed the winning instance
+        * of kernfs_remove_self() finished.
+        */
+       if (!(kn->flags & KERNFS_SUICIDAL)) {
+               kn->flags |= KERNFS_SUICIDAL;
+               __kernfs_remove(kn);
+               kn->flags |= KERNFS_SUICIDED;
+               ret = true;
+       } else {
+               wait_queue_head_t *waitq = &kernfs_root(kn)->deactivate_waitq;
+               DEFINE_WAIT(wait);
+
+               while (true) {
+                       prepare_to_wait(waitq, &wait, TASK_UNINTERRUPTIBLE);
+
+                       if ((kn->flags & KERNFS_SUICIDED) &&
+                           atomic_read(&kn->active) == KN_DEACTIVATED_BIAS)
+                               break;
+
+                       mutex_unlock(&kernfs_mutex);
+                       schedule();
+                       mutex_lock(&kernfs_mutex);
+               }
+               finish_wait(waitq, &wait);
+               WARN_ON_ONCE(!RB_EMPTY_NODE(&kn->rb));
+               ret = false;
+       }
+
+       /*
+        * This must be done while holding kernfs_mutex; otherwise, waiting
+        * for SUICIDED && deactivated could finish prematurely.
+        */
+       kernfs_unbreak_active_protection(kn);
+
+       mutex_unlock(&kernfs_mutex);
+       return ret;
 }
 
 /**
@@ -876,7 +1208,6 @@ void kernfs_remove(struct kernfs_node *kn)
 int kernfs_remove_by_name_ns(struct kernfs_node *parent, const char *name,
                             const void *ns)
 {
-       struct kernfs_addrm_cxt acxt;
        struct kernfs_node *kn;
 
        if (!parent) {
@@ -885,13 +1216,13 @@ int kernfs_remove_by_name_ns(struct kernfs_node *parent, const char *name,
                return -ENOENT;
        }
 
-       kernfs_addrm_start(&acxt);
+       mutex_lock(&kernfs_mutex);
 
        kn = kernfs_find_ns(parent, name, ns);
        if (kn)
-               __kernfs_remove(&acxt, kn);
+               __kernfs_remove(kn);
 
-       kernfs_addrm_finish(&acxt);
+       mutex_unlock(&kernfs_mutex);
 
        if (kn)
                return 0;
@@ -909,12 +1240,18 @@ int kernfs_remove_by_name_ns(struct kernfs_node *parent, const char *name,
 int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent,
                     const char *new_name, const void *new_ns)
 {
+       struct kernfs_node *old_parent;
+       const char *old_name = NULL;
        int error;
 
+       /* can't move or rename root */
+       if (!kn->parent)
+               return -EINVAL;
+
        mutex_lock(&kernfs_mutex);
 
        error = -ENOENT;
-       if ((kn->flags | new_parent->flags) & KERNFS_REMOVED)
+       if (!kernfs_active(kn) || !kernfs_active(new_parent))
                goto out;
 
        error = 0;
@@ -932,13 +1269,8 @@ int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent,
                new_name = kstrdup(new_name, GFP_KERNEL);
                if (!new_name)
                        goto out;
-
-               if (kn->flags & KERNFS_STATIC_NAME)
-                       kn->flags &= ~KERNFS_STATIC_NAME;
-               else
-                       kfree(kn->name);
-
-               kn->name = new_name;
+       } else {
+               new_name = NULL;
        }
 
        /*
@@ -946,12 +1278,29 @@ int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent,
         */
        kernfs_unlink_sibling(kn);
        kernfs_get(new_parent);
-       kernfs_put(kn->parent);
+
+       /* rename_lock protects ->parent and ->name accessors */
+       spin_lock_irq(&kernfs_rename_lock);
+
+       old_parent = kn->parent;
+       kn->parent = new_parent;
+
        kn->ns = new_ns;
+       if (new_name) {
+               if (!(kn->flags & KERNFS_STATIC_NAME))
+                       old_name = kn->name;
+               kn->flags &= ~KERNFS_STATIC_NAME;
+               kn->name = new_name;
+       }
+
+       spin_unlock_irq(&kernfs_rename_lock);
+
        kn->hash = kernfs_name_hash(kn->name, kn->ns);
-       kn->parent = new_parent;
        kernfs_link_sibling(kn);
 
+       kernfs_put(old_parent);
+       kfree(old_name);
+
        error = 0;
  out:
        mutex_unlock(&kernfs_mutex);
@@ -974,7 +1323,7 @@ static struct kernfs_node *kernfs_dir_pos(const void *ns,
        struct kernfs_node *parent, loff_t hash, struct kernfs_node *pos)
 {
        if (pos) {
-               int valid = !(pos->flags & KERNFS_REMOVED) &&
+               int valid = kernfs_active(pos) &&
                        pos->parent == parent && hash == pos->hash;
                kernfs_put(pos);
                if (!valid)
@@ -993,8 +1342,8 @@ static struct kernfs_node *kernfs_dir_pos(const void *ns,
                                break;
                }
        }
-       /* Skip over entries in the wrong namespace */
-       while (pos && pos->ns != ns) {
+       /* Skip over entries which are dying/dead or in the wrong namespace */
+       while (pos && (!kernfs_active(pos) || pos->ns != ns)) {
                struct rb_node *node = rb_next(&pos->rb);
                if (!node)
                        pos = NULL;
@@ -1008,14 +1357,15 @@ static struct kernfs_node *kernfs_dir_next_pos(const void *ns,
        struct kernfs_node *parent, ino_t ino, struct kernfs_node *pos)
 {
        pos = kernfs_dir_pos(ns, parent, ino, pos);
-       if (pos)
+       if (pos) {
                do {
                        struct rb_node *node = rb_next(&pos->rb);
                        if (!node)
                                pos = NULL;
                        else
                                pos = rb_to_kn(node);
-               } while (pos && pos->ns != ns);
+               } while (pos && (!kernfs_active(pos) || pos->ns != ns));
+       }
        return pos;
 }
 
index dbf397bfdff266d16332c57ac2de90e43ef8d8eb..8034706a7af87523bfc40e8660f21cc54238563f 100644 (file)
@@ -252,10 +252,18 @@ static ssize_t kernfs_fop_write(struct file *file, const char __user *user_buf,
                                size_t count, loff_t *ppos)
 {
        struct kernfs_open_file *of = kernfs_of(file);
-       ssize_t len = min_t(size_t, count, PAGE_SIZE);
        const struct kernfs_ops *ops;
+       size_t len;
        char *buf;
 
+       if (of->atomic_write_len) {
+               len = count;
+               if (len > of->atomic_write_len)
+                       return -E2BIG;
+       } else {
+               len = min_t(size_t, count, PAGE_SIZE);
+       }
+
        buf = kmalloc(len + 1, GFP_KERNEL);
        if (!buf)
                return -ENOMEM;
@@ -652,6 +660,12 @@ static int kernfs_fop_open(struct inode *inode, struct file *file)
        of->kn = kn;
        of->file = file;
 
+       /*
+        * Write path needs to atomic_write_len outside active reference.
+        * Cache it in open_file.  See kernfs_fop_write() for details.
+        */
+       of->atomic_write_len = ops->atomic_write_len;
+
        /*
         * Always instantiate seq_file even if read access doesn't use
         * seq_file or is not requested.  This unifies private data access
@@ -820,7 +834,6 @@ struct kernfs_node *__kernfs_create_file(struct kernfs_node *parent,
                                         bool name_is_static,
                                         struct lock_class_key *key)
 {
-       struct kernfs_addrm_cxt acxt;
        struct kernfs_node *kn;
        unsigned flags;
        int rc;
@@ -855,10 +868,7 @@ struct kernfs_node *__kernfs_create_file(struct kernfs_node *parent,
        if (ops->mmap)
                kn->flags |= KERNFS_HAS_MMAP;
 
-       kernfs_addrm_start(&acxt);
-       rc = kernfs_add_one(&acxt, kn);
-       kernfs_addrm_finish(&acxt);
-
+       rc = kernfs_add_one(kn);
        if (rc) {
                kernfs_put(kn);
                return ERR_PTR(rc);
index eb536b76374aa266b5dccaec425c29088bab4f88..8be13b2a079ba214e5d37373b20fd7afc5c7b94d 100644 (file)
@@ -26,7 +26,8 @@ struct kernfs_iattrs {
        struct simple_xattrs    xattrs;
 };
 
-#define KN_DEACTIVATED_BIAS            INT_MIN
+/* +1 to avoid triggering overflow warning when negating it */
+#define KN_DEACTIVATED_BIAS            (INT_MIN + 1)
 
 /* KERNFS_TYPE_MASK and types are defined in include/linux/kernfs.h */
 
@@ -44,13 +45,6 @@ static inline struct kernfs_root *kernfs_root(struct kernfs_node *kn)
        return kn->dir.root;
 }
 
-/*
- * Context structure to be used while adding/removing nodes.
- */
-struct kernfs_addrm_cxt {
-       struct kernfs_node      *removed;
-};
-
 /*
  * mount.c
  */
@@ -71,6 +65,7 @@ struct kernfs_super_info {
 };
 #define kernfs_info(SB) ((struct kernfs_super_info *)(SB->s_fs_info))
 
+extern const struct super_operations kernfs_sops;
 extern struct kmem_cache *kernfs_node_cache;
 
 /*
@@ -100,9 +95,7 @@ extern const struct inode_operations kernfs_dir_iops;
 
 struct kernfs_node *kernfs_get_active(struct kernfs_node *kn);
 void kernfs_put_active(struct kernfs_node *kn);
-void kernfs_addrm_start(struct kernfs_addrm_cxt *acxt);
-int kernfs_add_one(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn);
-void kernfs_addrm_finish(struct kernfs_addrm_cxt *acxt);
+int kernfs_add_one(struct kernfs_node *kn);
 struct kernfs_node *kernfs_new_node(struct kernfs_node *parent,
                                    const char *name, umode_t mode,
                                    unsigned flags);
index 0f4152defe7b6f96afa752acdbc075a512bc8c5d..6a5f04ac87040d85179420d254d528789ddcf1d6 100644 (file)
 
 struct kmem_cache *kernfs_node_cache;
 
-static const struct super_operations kernfs_sops = {
+static int kernfs_sop_remount_fs(struct super_block *sb, int *flags, char *data)
+{
+       struct kernfs_root *root = kernfs_info(sb)->root;
+       struct kernfs_syscall_ops *scops = root->syscall_ops;
+
+       if (scops && scops->remount_fs)
+               return scops->remount_fs(root, flags, data);
+       return 0;
+}
+
+static int kernfs_sop_show_options(struct seq_file *sf, struct dentry *dentry)
+{
+       struct kernfs_root *root = kernfs_root(dentry->d_fsdata);
+       struct kernfs_syscall_ops *scops = root->syscall_ops;
+
+       if (scops && scops->show_options)
+               return scops->show_options(sf, root);
+       return 0;
+}
+
+const struct super_operations kernfs_sops = {
        .statfs         = simple_statfs,
        .drop_inode     = generic_delete_inode,
        .evict_inode    = kernfs_evict_inode,
+
+       .remount_fs     = kernfs_sop_remount_fs,
+       .show_options   = kernfs_sop_show_options,
 };
 
+/**
+ * kernfs_root_from_sb - determine kernfs_root associated with a super_block
+ * @sb: the super_block in question
+ *
+ * Return the kernfs_root associated with @sb.  If @sb is not a kernfs one,
+ * %NULL is returned.
+ */
+struct kernfs_root *kernfs_root_from_sb(struct super_block *sb)
+{
+       if (sb->s_op == &kernfs_sops)
+               return kernfs_info(sb)->root;
+       return NULL;
+}
+
 static int kernfs_fill_super(struct super_block *sb)
 {
        struct kernfs_super_info *info = kernfs_info(sb);
index 4d457055acb9dfeefeb94d5a3b6bdbea96e20624..8a198898e39afd3ffde994cee7d732dcdfa8bdcd 100644 (file)
@@ -27,7 +27,6 @@ struct kernfs_node *kernfs_create_link(struct kernfs_node *parent,
                                       struct kernfs_node *target)
 {
        struct kernfs_node *kn;
-       struct kernfs_addrm_cxt acxt;
        int error;
 
        kn = kernfs_new_node(parent, name, S_IFLNK|S_IRWXUGO, KERNFS_LINK);
@@ -39,10 +38,7 @@ struct kernfs_node *kernfs_create_link(struct kernfs_node *parent,
        kn->symlink.target_kn = target;
        kernfs_get(target);     /* ref owned by symlink */
 
-       kernfs_addrm_start(&acxt);
-       error = kernfs_add_one(&acxt, kn);
-       kernfs_addrm_finish(&acxt);
-
+       error = kernfs_add_one(kn);
        if (!error)
                return kn;
 
index 8c41feacbac56fade7f81f59444b50b3c00630d7..b2756014508c770fbc024069be235622cca24581 100644 (file)
@@ -1,6 +1,7 @@
 config SYSFS
        bool "sysfs file system support" if EXPERT
        default y
+       select KERNFS
        help
        The sysfs filesystem is a virtual filesystem that the kernel uses to
        export internal kernel objects, their attributes, and their
index ee0d761c3179ca06efcdebe619eb107214079ba7..0b45ff42f3741123a15f58d426e201a78d20b3d0 100644 (file)
 
 DEFINE_SPINLOCK(sysfs_symlink_target_lock);
 
-/**
- *     sysfs_pathname - return full path to sysfs dirent
- *     @kn: kernfs_node whose path we want
- *     @path: caller allocated buffer of size PATH_MAX
- *
- *     Gives the name "/" to the sysfs_root entry; any path returned
- *     is relative to wherever sysfs is mounted.
- */
-static char *sysfs_pathname(struct kernfs_node *kn, char *path)
-{
-       if (kn->parent) {
-               sysfs_pathname(kn->parent, path);
-               strlcat(path, "/", PATH_MAX);
-       }
-       strlcat(path, kn->name, PATH_MAX);
-       return path;
-}
-
 void sysfs_warn_dup(struct kernfs_node *parent, const char *name)
 {
-       char *path;
+       char *buf, *path = NULL;
 
-       path = kzalloc(PATH_MAX, GFP_KERNEL);
-       if (path) {
-               sysfs_pathname(parent, path);
-               strlcat(path, "/", PATH_MAX);
-               strlcat(path, name, PATH_MAX);
-       }
+       buf = kzalloc(PATH_MAX, GFP_KERNEL);
+       if (buf)
+               path = kernfs_path(parent, buf, PATH_MAX);
 
-       WARN(1, KERN_WARNING "sysfs: cannot create duplicate filename '%s'\n",
-            path ? path : name);
+       WARN(1, KERN_WARNING "sysfs: cannot create duplicate filename '%s/%s'\n",
+            path, name);
 
-       kfree(path);
+       kfree(buf);
 }
 
 /**
@@ -122,9 +101,13 @@ void sysfs_remove_dir(struct kobject *kobj)
 int sysfs_rename_dir_ns(struct kobject *kobj, const char *new_name,
                        const void *new_ns)
 {
-       struct kernfs_node *parent = kobj->sd->parent;
+       struct kernfs_node *parent;
+       int ret;
 
-       return kernfs_rename_ns(kobj->sd, parent, new_name, new_ns);
+       parent = kernfs_get_parent(kobj->sd);
+       ret = kernfs_rename_ns(kobj->sd, parent, new_name, new_ns);
+       kernfs_put(parent);
+       return ret;
 }
 
 int sysfs_move_dir_ns(struct kobject *kobj, struct kobject *new_parent_kobj,
@@ -133,7 +116,6 @@ int sysfs_move_dir_ns(struct kobject *kobj, struct kobject *new_parent_kobj,
        struct kernfs_node *kn = kobj->sd;
        struct kernfs_node *new_parent;
 
-       BUG_ON(!kn->parent);
        new_parent = new_parent_kobj && new_parent_kobj->sd ?
                new_parent_kobj->sd : sysfs_root_kn;
 
index 810cf6e613e5142de7be542da24c34073fffc376..1b8b91b67fdb7a4b192b9568ad1d85defdcd863a 100644 (file)
@@ -372,6 +372,29 @@ void sysfs_remove_file_ns(struct kobject *kobj, const struct attribute *attr,
 }
 EXPORT_SYMBOL_GPL(sysfs_remove_file_ns);
 
+/**
+ * sysfs_remove_file_self - remove an object attribute from its own method
+ * @kobj: object we're acting for
+ * @attr: attribute descriptor
+ *
+ * See kernfs_remove_self() for details.
+ */
+bool sysfs_remove_file_self(struct kobject *kobj, const struct attribute *attr)
+{
+       struct kernfs_node *parent = kobj->sd;
+       struct kernfs_node *kn;
+       bool ret;
+
+       kn = kernfs_find_and_get(parent, attr->name);
+       if (WARN_ON_ONCE(!kn))
+               return false;
+
+       ret = kernfs_remove_self(kn);
+
+       kernfs_put(kn);
+       return ret;
+}
+
 void sysfs_remove_files(struct kobject *kobj, const struct attribute **ptr)
 {
        int i;
index 6b579387c67a143d14550ff8f272bb370348c649..aa0406895b53efe76501dc650f41f3ceb5d3b877 100644 (file)
@@ -70,8 +70,11 @@ static int create_files(struct kernfs_node *parent, struct kobject *kobj,
        if (grp->bin_attrs) {
                for (bin_attr = grp->bin_attrs; *bin_attr; bin_attr++) {
                        if (update)
-                               sysfs_remove_bin_file(kobj, *bin_attr);
-                       error = sysfs_create_bin_file(kobj, *bin_attr);
+                               kernfs_remove_by_name(parent,
+                                               (*bin_attr)->attr.name);
+                       error = sysfs_add_file_mode_ns(parent,
+                                       &(*bin_attr)->attr, true,
+                                       (*bin_attr)->attr.mode, NULL);
                        if (error)
                                break;
                }
index 3eaf5c6622eb4fafc82137bc501aac803d7952f4..a66ad6196f59cca2f61a31c4a966d41b92497ad8 100644 (file)
@@ -63,7 +63,7 @@ int __init sysfs_init(void)
 {
        int err;
 
-       sysfs_root = kernfs_create_root(NULL, NULL);
+       sysfs_root = kernfs_create_root(NULL, 0, NULL);
        if (IS_ERR(sysfs_root))
                return PTR_ERR(sysfs_root);
 
index 03e235ad1bba31bf4ba7fe155d9d64566179866c..03e962e23eaf65aad5d161c528ae6a43cdc0ffed 100644 (file)
@@ -46,13 +46,6 @@ extern ssize_t arch_cpu_release(const char *, size_t);
 #endif
 struct notifier_block;
 
-#ifdef CONFIG_ARCH_HAS_CPU_AUTOPROBE
-extern int arch_cpu_uevent(struct device *dev, struct kobj_uevent_env *env);
-extern ssize_t arch_print_cpu_modalias(struct device *dev,
-                                      struct device_attribute *attr,
-                                      char *bufptr);
-#endif
-
 /*
  * CPU notifier priorities.
  */
diff --git a/include/linux/cpufeature.h b/include/linux/cpufeature.h
new file mode 100644 (file)
index 0000000..c4d4eb8
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2014 Linaro Ltd. <ard.biesheuvel@linaro.org>
+ *
+ * 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 __LINUX_CPUFEATURE_H
+#define __LINUX_CPUFEATURE_H
+
+#ifdef CONFIG_GENERIC_CPU_AUTOPROBE
+
+#include <linux/mod_devicetable.h>
+#include <asm/cpufeature.h>
+
+/*
+ * Macros imported from <asm/cpufeature.h>:
+ * - cpu_feature(x)            ordinal value of feature called 'x'
+ * - cpu_have_feature(u32 n)   whether feature #n is available
+ * - MAX_CPU_FEATURES          upper bound for feature ordinal values
+ * Optional:
+ * - CPU_FEATURE_TYPEFMT       format string fragment for printing the cpu type
+ * - CPU_FEATURE_TYPEVAL       set of values matching the format string above
+ */
+
+#ifndef CPU_FEATURE_TYPEFMT
+#define CPU_FEATURE_TYPEFMT    "%s"
+#endif
+
+#ifndef CPU_FEATURE_TYPEVAL
+#define CPU_FEATURE_TYPEVAL    ELF_PLATFORM
+#endif
+
+/*
+ * Use module_cpu_feature_match(feature, module_init_function) to
+ * declare that
+ * a) the module shall be probed upon discovery of CPU feature 'feature'
+ *    (typically at boot time using udev)
+ * b) the module must not be loaded if CPU feature 'feature' is not present
+ *    (not even by manual insmod).
+ *
+ * For a list of legal values for 'feature', please consult the file
+ * 'asm/cpufeature.h' of your favorite architecture.
+ */
+#define module_cpu_feature_match(x, __init)                    \
+static struct cpu_feature const cpu_feature_match_ ## x[] =    \
+       { { .feature = cpu_feature(x) }, { } };                 \
+MODULE_DEVICE_TABLE(cpu, cpu_feature_match_ ## x);             \
+                                                               \
+static int cpu_feature_match_ ## x ## _init(void)              \
+{                                                              \
+       if (!cpu_have_feature(cpu_feature(x)))                  \
+               return -ENODEV;                                 \
+       return __init();                                        \
+}                                                              \
+module_init(cpu_feature_match_ ## x ## _init)
+
+#endif
+#endif
index ec1b6e21f0efb88cdb6c60a83d27c98957278757..233bbbeb768d6f8c5a6aa32411675a14b3401272 100644 (file)
@@ -560,6 +560,8 @@ extern int device_create_file(struct device *device,
                              const struct device_attribute *entry);
 extern void device_remove_file(struct device *dev,
                               const struct device_attribute *attr);
+extern bool device_remove_file_self(struct device *dev,
+                                   const struct device_attribute *attr);
 extern int __must_check device_create_bin_file(struct device *dev,
                                        const struct bin_attribute *attr);
 extern void device_remove_bin_file(struct device *dev,
index d267623c28cfdaa385ab5f288683c0157f9e8ef4..b0122dc6f96a0a21324f86b5a28c725ac3ea74cf 100644 (file)
@@ -15,7 +15,7 @@
 #include <linux/lockdep.h>
 #include <linux/rbtree.h>
 #include <linux/atomic.h>
-#include <linux/completion.h>
+#include <linux/wait.h>
 
 struct file;
 struct dentry;
@@ -35,16 +35,22 @@ enum kernfs_node_type {
 };
 
 #define KERNFS_TYPE_MASK       0x000f
-#define KERNFS_ACTIVE_REF      KERNFS_FILE
 #define KERNFS_FLAG_MASK       ~KERNFS_TYPE_MASK
 
 enum kernfs_node_flag {
-       KERNFS_REMOVED          = 0x0010,
+       KERNFS_ACTIVATED        = 0x0010,
        KERNFS_NS               = 0x0020,
        KERNFS_HAS_SEQ_SHOW     = 0x0040,
        KERNFS_HAS_MMAP         = 0x0080,
        KERNFS_LOCKDEP          = 0x0100,
        KERNFS_STATIC_NAME      = 0x0200,
+       KERNFS_SUICIDAL         = 0x0400,
+       KERNFS_SUICIDED         = 0x0800,
+};
+
+/* @flags for kernfs_create_root() */
+enum kernfs_root_flag {
+       KERNFS_ROOT_CREATE_DEACTIVATED = 0x0001,
 };
 
 /* type-specific structures for kernfs_node union members */
@@ -85,17 +91,17 @@ struct kernfs_node {
 #ifdef CONFIG_DEBUG_LOCK_ALLOC
        struct lockdep_map      dep_map;
 #endif
-       /* the following two fields are published */
+       /*
+        * Use kernfs_get_parent() and kernfs_name/path() instead of
+        * accessing the following two fields directly.  If the node is
+        * never moved to a different parent, it is safe to access the
+        * parent directly.
+        */
        struct kernfs_node      *parent;
        const char              *name;
 
        struct rb_node          rb;
 
-       union {
-               struct completion       *completion;
-               struct kernfs_node      *removed_list;
-       } u;
-
        const void              *ns;    /* namespace tag */
        unsigned int            hash;   /* ns + name hash */
        union {
@@ -113,12 +119,16 @@ struct kernfs_node {
 };
 
 /*
- * kernfs_dir_ops may be specified on kernfs_create_root() to support
- * directory manipulation syscalls.  These optional callbacks are invoked
- * on the matching syscalls and can perform any kernfs operations which
- * don't necessarily have to be the exact operation requested.
+ * kernfs_syscall_ops may be specified on kernfs_create_root() to support
+ * syscalls.  These optional callbacks are invoked on the matching syscalls
+ * and can perform any kernfs operations which don't necessarily have to be
+ * the exact operation requested.  An active reference is held for each
+ * kernfs_node parameter.
  */
-struct kernfs_dir_ops {
+struct kernfs_syscall_ops {
+       int (*remount_fs)(struct kernfs_root *root, int *flags, char *data);
+       int (*show_options)(struct seq_file *sf, struct kernfs_root *root);
+
        int (*mkdir)(struct kernfs_node *parent, const char *name,
                     umode_t mode);
        int (*rmdir)(struct kernfs_node *kn);
@@ -129,22 +139,26 @@ struct kernfs_dir_ops {
 struct kernfs_root {
        /* published fields */
        struct kernfs_node      *kn;
+       unsigned int            flags;  /* KERNFS_ROOT_* flags */
 
        /* private fields, do not use outside kernfs proper */
        struct ida              ino_ida;
-       struct kernfs_dir_ops   *dir_ops;
+       struct kernfs_syscall_ops *syscall_ops;
+       wait_queue_head_t       deactivate_waitq;
 };
 
 struct kernfs_open_file {
        /* published fields */
        struct kernfs_node      *kn;
        struct file             *file;
+       void                    *priv;
 
        /* private fields, do not use outside kernfs proper */
        struct mutex            mutex;
        int                     event;
        struct list_head        list;
 
+       size_t                  atomic_write_len;
        bool                    mmapped;
        const struct vm_operations_struct *vm_ops;
 };
@@ -171,9 +185,13 @@ struct kernfs_ops {
                        loff_t off);
 
        /*
-        * write() is bounced through kernel buffer and a write larger than
-        * PAGE_SIZE results in partial operation of PAGE_SIZE.
+        * write() is bounced through kernel buffer.  If atomic_write_len
+        * is not set, a write larger than PAGE_SIZE results in partial
+        * operations of PAGE_SIZE chunks.  If atomic_write_len is set,
+        * writes upto the specified size are executed atomically but
+        * larger ones are rejected with -E2BIG.
         */
+       size_t atomic_write_len;
        ssize_t (*write)(struct kernfs_open_file *of, char *buf, size_t bytes,
                         loff_t off);
 
@@ -184,7 +202,7 @@ struct kernfs_ops {
 #endif
 };
 
-#ifdef CONFIG_SYSFS
+#ifdef CONFIG_KERNFS
 
 static inline enum kernfs_node_type kernfs_type(struct kernfs_node *kn)
 {
@@ -217,13 +235,22 @@ static inline bool kernfs_ns_enabled(struct kernfs_node *kn)
        return kn->flags & KERNFS_NS;
 }
 
+int kernfs_name(struct kernfs_node *kn, char *buf, size_t buflen);
+char * __must_check kernfs_path(struct kernfs_node *kn, char *buf,
+                               size_t buflen);
+void pr_cont_kernfs_name(struct kernfs_node *kn);
+void pr_cont_kernfs_path(struct kernfs_node *kn);
+struct kernfs_node *kernfs_get_parent(struct kernfs_node *kn);
 struct kernfs_node *kernfs_find_and_get_ns(struct kernfs_node *parent,
                                           const char *name, const void *ns);
 void kernfs_get(struct kernfs_node *kn);
 void kernfs_put(struct kernfs_node *kn);
 
-struct kernfs_root *kernfs_create_root(struct kernfs_dir_ops *kdops,
-                                      void *priv);
+struct kernfs_node *kernfs_node_from_dentry(struct dentry *dentry);
+struct kernfs_root *kernfs_root_from_sb(struct super_block *sb);
+
+struct kernfs_root *kernfs_create_root(struct kernfs_syscall_ops *scops,
+                                      unsigned int flags, void *priv);
 void kernfs_destroy_root(struct kernfs_root *root);
 
 struct kernfs_node *kernfs_create_dir_ns(struct kernfs_node *parent,
@@ -239,7 +266,11 @@ struct kernfs_node *__kernfs_create_file(struct kernfs_node *parent,
 struct kernfs_node *kernfs_create_link(struct kernfs_node *parent,
                                       const char *name,
                                       struct kernfs_node *target);
+void kernfs_activate(struct kernfs_node *kn);
 void kernfs_remove(struct kernfs_node *kn);
+void kernfs_break_active_protection(struct kernfs_node *kn);
+void kernfs_unbreak_active_protection(struct kernfs_node *kn);
+bool kernfs_remove_self(struct kernfs_node *kn);
 int kernfs_remove_by_name_ns(struct kernfs_node *parent, const char *name,
                             const void *ns);
 int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent,
@@ -255,7 +286,7 @@ void kernfs_kill_sb(struct super_block *sb);
 
 void kernfs_init(void);
 
-#else  /* CONFIG_SYSFS */
+#else  /* CONFIG_KERNFS */
 
 static inline enum kernfs_node_type kernfs_type(struct kernfs_node *kn)
 { return 0; }  /* whatever */
@@ -265,6 +296,19 @@ static inline void kernfs_enable_ns(struct kernfs_node *kn) { }
 static inline bool kernfs_ns_enabled(struct kernfs_node *kn)
 { return false; }
 
+static inline int kernfs_name(struct kernfs_node *kn, char *buf, size_t buflen)
+{ return -ENOSYS; }
+
+static inline char * __must_check kernfs_path(struct kernfs_node *kn, char *buf,
+                                             size_t buflen)
+{ return NULL; }
+
+static inline void pr_cont_kernfs_name(struct kernfs_node *kn) { }
+static inline void pr_cont_kernfs_path(struct kernfs_node *kn) { }
+
+static inline struct kernfs_node *kernfs_get_parent(struct kernfs_node *kn)
+{ return NULL; }
+
 static inline struct kernfs_node *
 kernfs_find_and_get_ns(struct kernfs_node *parent, const char *name,
                       const void *ns)
@@ -273,8 +317,15 @@ kernfs_find_and_get_ns(struct kernfs_node *parent, const char *name,
 static inline void kernfs_get(struct kernfs_node *kn) { }
 static inline void kernfs_put(struct kernfs_node *kn) { }
 
+static inline struct kernfs_node *kernfs_node_from_dentry(struct dentry *dentry)
+{ return NULL; }
+
+static inline struct kernfs_root *kernfs_root_from_sb(struct super_block *sb)
+{ return NULL; }
+
 static inline struct kernfs_root *
-kernfs_create_root(struct kernfs_dir_ops *kdops, void *priv)
+kernfs_create_root(struct kernfs_syscall_ops *scops, unsigned int flags,
+                  void *priv)
 { return ERR_PTR(-ENOSYS); }
 
 static inline void kernfs_destroy_root(struct kernfs_root *root) { }
@@ -296,8 +347,13 @@ kernfs_create_link(struct kernfs_node *parent, const char *name,
                   struct kernfs_node *target)
 { return ERR_PTR(-ENOSYS); }
 
+static inline void kernfs_activate(struct kernfs_node *kn) { }
+
 static inline void kernfs_remove(struct kernfs_node *kn) { }
 
+static inline bool kernfs_remove_self(struct kernfs_node *kn)
+{ return false; }
+
 static inline int kernfs_remove_by_name_ns(struct kernfs_node *kn,
                                           const char *name, const void *ns)
 { return -ENOSYS; }
@@ -325,7 +381,7 @@ static inline void kernfs_kill_sb(struct super_block *sb) { }
 
 static inline void kernfs_init(void) { }
 
-#endif /* CONFIG_SYSFS */
+#endif /* CONFIG_KERNFS */
 
 static inline struct kernfs_node *
 kernfs_find_and_get(struct kernfs_node *kn, const char *name)
@@ -367,6 +423,13 @@ static inline int kernfs_remove_by_name(struct kernfs_node *parent,
        return kernfs_remove_by_name_ns(parent, name, NULL);
 }
 
+static inline int kernfs_rename(struct kernfs_node *kn,
+                               struct kernfs_node *new_parent,
+                               const char *new_name)
+{
+       return kernfs_rename_ns(kn, new_parent, new_name, NULL);
+}
+
 static inline struct dentry *
 kernfs_mount(struct file_system_type *fs_type, int flags,
             struct kernfs_root *root, bool *new_sb_created)
index 50f67eff27ef81257bdff16dddc675acafc7b0e3..9a165a213d9320fa79d088ebe8aaa80408c7fb71 100644 (file)
@@ -572,6 +572,15 @@ struct x86_cpu_id {
 #define X86_MODEL_ANY  0
 #define X86_FEATURE_ANY 0      /* Same as FPU, you can't test for that */
 
+/*
+ * Generic table type for matching CPU features.
+ * @feature:   the bit number of the feature (0 - 65535)
+ */
+
+struct cpu_feature {
+       __u16   feature;
+};
+
 #define IPACK_ANY_FORMAT 0xff
 #define IPACK_ANY_ID (~0)
 struct ipack_device_id {
index 30b2ebee643976e39f60c4fe3ff0a9618d5753e3..e0bf210ddffde0108e77820d81adc318332e2f19 100644 (file)
@@ -198,6 +198,7 @@ int __must_check sysfs_chmod_file(struct kobject *kobj,
                                  const struct attribute *attr, umode_t mode);
 void sysfs_remove_file_ns(struct kobject *kobj, const struct attribute *attr,
                          const void *ns);
+bool sysfs_remove_file_self(struct kobject *kobj, const struct attribute *attr);
 void sysfs_remove_files(struct kobject *kobj, const struct attribute **attr);
 
 int __must_check sysfs_create_bin_file(struct kobject *kobj,
@@ -246,6 +247,11 @@ void sysfs_notify(struct kobject *kobj, const char *dir, const char *attr);
 
 int __must_check sysfs_init(void);
 
+static inline void sysfs_enable_ns(struct kernfs_node *kn)
+{
+       return kernfs_enable_ns(kn);
+}
+
 #else /* CONFIG_SYSFS */
 
 static inline int sysfs_schedule_callback(struct kobject *kobj,
@@ -301,6 +307,12 @@ static inline void sysfs_remove_file_ns(struct kobject *kobj,
 {
 }
 
+static inline bool sysfs_remove_file_self(struct kobject *kobj,
+                                         const struct attribute *attr)
+{
+       return false;
+}
+
 static inline void sysfs_remove_files(struct kobject *kobj,
                                     const struct attribute **attr)
 {
@@ -418,6 +430,10 @@ static inline int __must_check sysfs_init(void)
        return 0;
 }
 
+static inline void sysfs_enable_ns(struct kernfs_node *kn)
+{
+}
+
 #endif /* CONFIG_SYSFS */
 
 static inline int __must_check sysfs_create_file(struct kobject *kobj,
index cb14aeac4ccaeedfe537d423c41c3d0b021e1c9f..58751bb80a7cbbe905c7d4e752df641ae2240b51 100644 (file)
@@ -94,7 +94,7 @@ static int create_dir(struct kobject *kobj)
                BUG_ON(ops->type >= KOBJ_NS_TYPES);
                BUG_ON(!kobj_ns_type_registered(ops->type));
 
-               kernfs_enable_ns(kobj->sd);
+               sysfs_enable_ns(kobj->sd);
        }
 
        return 0;
index bb5d115ca671c5b19073526fa815baa5f728fb2c..f282516acc7be7d26be68037d3381a66126b582d 100644 (file)
@@ -174,6 +174,9 @@ int main(void)
        DEVID_FIELD(x86_cpu_id, model);
        DEVID_FIELD(x86_cpu_id, vendor);
 
+       DEVID(cpu_feature);
+       DEVID_FIELD(cpu_feature, feature);
+
        DEVID(mei_cl_device_id);
        DEVID_FIELD(mei_cl_device_id, name);
 
index 25e5cb0aaef6fd68fd77d15037a66f046e6c72e6..25f6f5970552224f07145f5bc2f093a7f8e925c9 100644 (file)
@@ -1110,7 +1110,7 @@ static int do_amba_entry(const char *filename,
 }
 ADD_TO_DEVTABLE("amba", amba_id, do_amba_entry);
 
-/* LOOKS like x86cpu:vendor:VVVV:family:FFFF:model:MMMM:feature:*,FEAT,*
+/* LOOKS like cpu:type:x86,venVVVVfamFFFFmodMMMM:feature:*,FEAT,*
  * All fields are numbers. It would be nicer to use strings for vendor
  * and feature, but getting those out of the build system here is too
  * complicated.
@@ -1124,10 +1124,10 @@ static int do_x86cpu_entry(const char *filename, void *symval,
        DEF_FIELD(symval, x86_cpu_id, model);
        DEF_FIELD(symval, x86_cpu_id, vendor);
 
-       strcpy(alias, "x86cpu:");
-       ADD(alias, "vendor:",  vendor != X86_VENDOR_ANY, vendor);
-       ADD(alias, ":family:", family != X86_FAMILY_ANY, family);
-       ADD(alias, ":model:",  model  != X86_MODEL_ANY,  model);
+       strcpy(alias, "cpu:type:x86,");
+       ADD(alias, "ven", vendor != X86_VENDOR_ANY, vendor);
+       ADD(alias, "fam", family != X86_FAMILY_ANY, family);
+       ADD(alias, "mod", model  != X86_MODEL_ANY,  model);
        strcat(alias, ":feature:*");
        if (feature != X86_FEATURE_ANY)
                sprintf(alias + strlen(alias), "%04X*", feature);
@@ -1135,6 +1135,16 @@ static int do_x86cpu_entry(const char *filename, void *symval,
 }
 ADD_TO_DEVTABLE("x86cpu", x86_cpu_id, do_x86cpu_entry);
 
+/* LOOKS like cpu:type:*:feature:*FEAT* */
+static int do_cpu_entry(const char *filename, void *symval, char *alias)
+{
+       DEF_FIELD(symval, cpu_feature, feature);
+
+       sprintf(alias, "cpu:type:*:feature:*%04X*", feature);
+       return 1;
+}
+ADD_TO_DEVTABLE("cpu", cpu_feature, do_cpu_entry);
+
 /* Looks like: mei:S */
 static int do_mei_entry(const char *filename, void *symval,
                        char *alias)