Commit 158e0d36 authored by Linus Torvalds's avatar Linus Torvalds

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

Merge tag 'driver-core-3.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core

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()
  ...
parents 675c354a 72099304
...@@ -23,6 +23,7 @@ struct ccwgroup_device { ...@@ -23,6 +23,7 @@ struct ccwgroup_device {
unsigned int count; unsigned int count;
struct device dev; struct device dev;
struct ccw_device *cdev[0]; struct ccw_device *cdev[0];
struct work_struct ungroup_work;
}; };
/** /**
......
...@@ -48,29 +48,27 @@ static ssize_t show_pfgid(struct device *dev, struct device_attribute *attr, ...@@ -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 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 pci_dev *pdev = to_pci_dev(dev);
struct zpci_dev *zdev = get_zdev(pdev); struct zpci_dev *zdev = get_zdev(pdev);
int ret; int ret;
if (!device_remove_file_self(dev, attr))
return count;
pci_stop_and_remove_bus_device(pdev); pci_stop_and_remove_bus_device(pdev);
ret = zpci_disable_device(zdev); ret = zpci_disable_device(zdev);
if (ret) if (ret)
return; return ret;
ret = zpci_enable_device(zdev); ret = zpci_enable_device(zdev);
if (ret) if (ret)
return; return ret;
pci_rescan_bus(zdev->bus); pci_rescan_bus(zdev->bus);
} return count;
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;
} }
static DEVICE_ATTR(recover, S_IWUSR, NULL, store_recover); static DEVICE_ATTR(recover, S_IWUSR, NULL, store_recover);
......
...@@ -127,6 +127,7 @@ config X86 ...@@ -127,6 +127,7 @@ config X86
select HAVE_DEBUG_STACKOVERFLOW select HAVE_DEBUG_STACKOVERFLOW
select HAVE_IRQ_EXIT_ON_IRQ_STACK if X86_64 select HAVE_IRQ_EXIT_ON_IRQ_STACK if X86_64
select HAVE_CC_STACKPROTECTOR select HAVE_CC_STACKPROTECTOR
select GENERIC_CPU_AUTOPROBE
config INSTRUCTION_DECODER config INSTRUCTION_DECODER
def_bool y def_bool y
...@@ -195,9 +196,6 @@ config ARCH_HAS_CPU_RELAX ...@@ -195,9 +196,6 @@ config ARCH_HAS_CPU_RELAX
config ARCH_HAS_CACHE_LINE_SIZE config ARCH_HAS_CACHE_LINE_SIZE
def_bool y def_bool y
config ARCH_HAS_CPU_AUTOPROBE
def_bool y
config HAVE_SETUP_PER_CPU_AREA config HAVE_SETUP_PER_CPU_AREA
def_bool y def_bool y
......
...@@ -546,6 +546,13 @@ static __always_inline __pure bool _static_cpu_has_safe(u16 bit) ...@@ -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 static_cpu_has_bug(bit) static_cpu_has((bit))
#define boot_cpu_has_bug(bit) cpu_has_bug(&boot_cpu_data, (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 /* defined(__KERNEL__) && !defined(__ASSEMBLY__) */
#endif /* _ASM_X86_CPUFEATURE_H */ #endif /* _ASM_X86_CPUFEATURE_H */
...@@ -47,45 +47,3 @@ const struct x86_cpu_id *x86_match_cpu(const struct x86_cpu_id *match) ...@@ -47,45 +47,3 @@ const struct x86_cpu_id *x86_match_cpu(const struct x86_cpu_id *match)
return NULL; return NULL;
} }
EXPORT_SYMBOL(x86_match_cpu); 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;
}
...@@ -185,6 +185,9 @@ config GENERIC_CPU_DEVICES ...@@ -185,6 +185,9 @@ config GENERIC_CPU_DEVICES
bool bool
default n default n
config GENERIC_CPU_AUTOPROBE
bool
config SOC_BUS config SOC_BUS
bool bool
......
...@@ -12,7 +12,6 @@ ...@@ -12,7 +12,6 @@
*/ */
#include <linux/attribute_container.h> #include <linux/attribute_container.h>
#include <linux/init.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/slab.h> #include <linux/slab.h>
......
...@@ -23,7 +23,6 @@ ...@@ -23,7 +23,6 @@
#include <linux/genhd.h> #include <linux/genhd.h>
#include <linux/kallsyms.h> #include <linux/kallsyms.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/async.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/sysfs.h> #include <linux/sysfs.h>
...@@ -570,6 +569,23 @@ void device_remove_file(struct device *dev, ...@@ -570,6 +569,23 @@ void device_remove_file(struct device *dev,
} }
EXPORT_SYMBOL_GPL(device_remove_file); 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. * device_create_bin_file - create sysfs binary attribute file for device.
* @dev: device. * @dev: device.
...@@ -2003,7 +2019,6 @@ void device_shutdown(void) ...@@ -2003,7 +2019,6 @@ void device_shutdown(void)
spin_lock(&devices_kset->list_lock); spin_lock(&devices_kset->list_lock);
} }
spin_unlock(&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) ...@@ -2058,7 +2073,6 @@ create_syslog_header(const struct device *dev, char *hdr, size_t hdrlen)
return pos; return pos;
} }
EXPORT_SYMBOL(create_syslog_header);
int dev_vprintk_emit(int level, const struct device *dev, int dev_vprintk_emit(int level, const struct device *dev,
const char *fmt, va_list args) const char *fmt, va_list args)
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include <linux/percpu.h> #include <linux/percpu.h>
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/cpufeature.h>
#include "base.h" #include "base.h"
...@@ -286,6 +287,41 @@ static void cpu_device_release(struct device *dev) ...@@ -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. * register_cpu - Setup a sysfs device for a CPU.
* @cpu - cpu->hotpluggable field set to 1 will generate a control file in * @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) ...@@ -306,8 +342,8 @@ int register_cpu(struct cpu *cpu, int num)
cpu->dev.offline_disabled = !cpu->hotpluggable; cpu->dev.offline_disabled = !cpu->hotpluggable;
cpu->dev.offline = !cpu_online(num); cpu->dev.offline = !cpu_online(num);
cpu->dev.of_node = of_get_cpu_node(num, NULL); cpu->dev.of_node = of_get_cpu_node(num, NULL);
#ifdef CONFIG_ARCH_HAS_CPU_AUTOPROBE #ifdef CONFIG_GENERIC_CPU_AUTOPROBE
cpu->dev.bus->uevent = arch_cpu_uevent; cpu->dev.bus->uevent = cpu_uevent;
#endif #endif
cpu->dev.groups = common_cpu_attr_groups; cpu->dev.groups = common_cpu_attr_groups;
if (cpu->hotpluggable) if (cpu->hotpluggable)
...@@ -330,8 +366,8 @@ struct device *get_cpu_device(unsigned cpu) ...@@ -330,8 +366,8 @@ struct device *get_cpu_device(unsigned cpu)
} }
EXPORT_SYMBOL_GPL(get_cpu_device); EXPORT_SYMBOL_GPL(get_cpu_device);
#ifdef CONFIG_ARCH_HAS_CPU_AUTOPROBE #ifdef CONFIG_GENERIC_CPU_AUTOPROBE
static DEVICE_ATTR(modalias, 0444, arch_print_cpu_modalias, NULL); static DEVICE_ATTR(modalias, 0444, print_cpu_modalias, NULL);
#endif #endif
static struct attribute *cpu_root_attrs[] = { static struct attribute *cpu_root_attrs[] = {
...@@ -344,7 +380,7 @@ static struct attribute *cpu_root_attrs[] = { ...@@ -344,7 +380,7 @@ static struct attribute *cpu_root_attrs[] = {
&cpu_attrs[2].attr.attr, &cpu_attrs[2].attr.attr,
&dev_attr_kernel_max.attr, &dev_attr_kernel_max.attr,
&dev_attr_offline.attr, &dev_attr_offline.attr,
#ifdef CONFIG_ARCH_HAS_CPU_AUTOPROBE #ifdef CONFIG_GENERIC_CPU_AUTOPROBE
&dev_attr_modalias.attr, &dev_attr_modalias.attr,
#endif #endif
NULL NULL
......
...@@ -251,9 +251,8 @@ EXPORT_SYMBOL_GPL(dma_buf_put); ...@@ -251,9 +251,8 @@ EXPORT_SYMBOL_GPL(dma_buf_put);
* @dmabuf: [in] buffer to attach device to. * @dmabuf: [in] buffer to attach device to.
* @dev: [in] device to be attached. * @dev: [in] device to be attached.
* *
* Returns struct dma_buf_attachment * for this attachment; may return negative * Returns struct dma_buf_attachment * for this attachment; returns ERR_PTR on
* error codes. * error.
*
*/ */
struct dma_buf_attachment *dma_buf_attach(struct dma_buf *dmabuf, struct dma_buf_attachment *dma_buf_attach(struct dma_buf *dmabuf,
struct device *dev) struct device *dev)
...@@ -319,9 +318,8 @@ EXPORT_SYMBOL_GPL(dma_buf_detach); ...@@ -319,9 +318,8 @@ EXPORT_SYMBOL_GPL(dma_buf_detach);
* @attach: [in] attachment whose scatterlist is to be returned * @attach: [in] attachment whose scatterlist is to be returned
* @direction: [in] direction of DMA transfer * @direction: [in] direction of DMA transfer
* *
* Returns sg_table containing the scatterlist to be returned; may return NULL * Returns sg_table containing the scatterlist to be returned; returns ERR_PTR
* or ERR_PTR. * on error.
*
*/ */
struct sg_table *dma_buf_map_attachment(struct dma_buf_attachment *attach, struct sg_table *dma_buf_map_attachment(struct dma_buf_attachment *attach,
enum dma_data_direction direction) enum dma_data_direction direction)
...@@ -334,6 +332,8 @@ struct sg_table *dma_buf_map_attachment(struct dma_buf_attachment *attach, ...@@ -334,6 +332,8 @@ struct sg_table *dma_buf_map_attachment(struct dma_buf_attachment *attach,
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
sg_table = attach->dmabuf->ops->map_dma_buf(attach, direction); sg_table = attach->dmabuf->ops->map_dma_buf(attach, direction);
if (!sg_table)
sg_table = ERR_PTR(-ENOMEM);
return sg_table; return sg_table;
} }
...@@ -544,6 +544,8 @@ EXPORT_SYMBOL_GPL(dma_buf_mmap); ...@@ -544,6 +544,8 @@ EXPORT_SYMBOL_GPL(dma_buf_mmap);
* These calls are optional in drivers. The intended use for them * These calls are optional in drivers. The intended use for them
* is for mapping objects linear in kernel space for high use objects. * is for mapping objects linear in kernel space for high use objects.
* Please attempt to use kmap/kunmap before thinking about these interfaces. * Please attempt to use kmap/kunmap before thinking about these interfaces.
*
* Returns NULL on error.
*/ */
void *dma_buf_vmap(struct dma_buf *dmabuf) void *dma_buf_vmap(struct dma_buf *dmabuf)
{ {
...@@ -566,7 +568,9 @@ 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); BUG_ON(dmabuf->vmap_ptr);
ptr = dmabuf->ops->vmap(dmabuf); 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; goto out_unlock;
dmabuf->vmap_ptr = ptr; dmabuf->vmap_ptr = ptr;
......
...@@ -649,7 +649,9 @@ static ssize_t firmware_loading_store(struct device *dev, ...@@ -649,7 +649,9 @@ static ssize_t firmware_loading_store(struct device *dev,
* see the mapped 'buf->data' once the loading * see the mapped 'buf->data' once the loading
* is completed. * 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); list_del_init(&fw_buf->pending_list);
complete_all(&fw_buf->completion); complete_all(&fw_buf->completion);
break; break;
...@@ -900,7 +902,8 @@ static int _request_firmware_load(struct firmware_priv *fw_priv, ...@@ -900,7 +902,8 @@ static int _request_firmware_load(struct firmware_priv *fw_priv,
dev_set_uevent_suppress(f_dev, false); dev_set_uevent_suppress(f_dev, false);
dev_dbg(f_dev, "firmware: requesting %s\n", buf->fw_id); dev_dbg(f_dev, "firmware: requesting %s\n", buf->fw_id);
if (timeout != MAX_SCHEDULE_TIMEOUT) 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); kobject_uevent(&fw_priv->dev.kobj, KOBJ_ADD);
} }
...@@ -908,6 +911,8 @@ static int _request_firmware_load(struct firmware_priv *fw_priv, ...@@ -908,6 +911,8 @@ static int _request_firmware_load(struct firmware_priv *fw_priv,
wait_for_completion(&buf->completion); wait_for_completion(&buf->completion);
cancel_delayed_work_sync(&fw_priv->timeout_work); cancel_delayed_work_sync(&fw_priv->timeout_work);
if (!buf->data)
retval = -ENOMEM;
device_remove_file(f_dev, &dev_attr_loading); device_remove_file(f_dev, &dev_attr_loading);
err_del_bin_attr: err_del_bin_attr:
...@@ -1570,7 +1575,7 @@ static void device_uncache_fw_images_work(struct work_struct *work) ...@@ -1570,7 +1575,7 @@ static void device_uncache_fw_images_work(struct work_struct *work)
*/ */
static void device_uncache_fw_images_delay(unsigned long delay) static void device_uncache_fw_images_delay(unsigned long delay)
{ {
schedule_delayed_work(&fw_cache.work, queue_delayed_work(system_power_efficient_wq, &fw_cache.work,
msecs_to_jiffies(delay)); msecs_to_jiffies(delay));
} }
......
...@@ -599,7 +599,11 @@ int register_one_node(int nid) ...@@ -599,7 +599,11 @@ int register_one_node(int nid)
void unregister_one_node(int nid) void unregister_one_node(int nid)
{ {
if (!node_devices[nid])
return;
unregister_node(node_devices[nid]); unregister_node(node_devices[nid]);
kfree(node_devices[nid]);
node_devices[nid] = NULL; node_devices[nid] = NULL;
} }
......
...@@ -481,11 +481,10 @@ static int platform_drv_probe(struct device *_dev) ...@@ -481,11 +481,10 @@ static int platform_drv_probe(struct device *_dev)
struct platform_device *dev = to_platform_device(_dev); struct platform_device *dev = to_platform_device(_dev);
int ret; int ret;
if (ACPI_HANDLE(_dev))
acpi_dev_pm_attach(_dev, true); acpi_dev_pm_attach(_dev, true);
ret = drv->probe(dev); ret = drv->probe(dev);
if (ret && ACPI_HANDLE(_dev)) if (ret)
acpi_dev_pm_detach(_dev, true); acpi_dev_pm_detach(_dev, true);
if (drv->prevent_deferred_probe && ret == -EPROBE_DEFER) { if (drv->prevent_deferred_probe && ret == -EPROBE_DEFER) {
...@@ -508,7 +507,6 @@ static int platform_drv_remove(struct device *_dev) ...@@ -508,7 +507,6 @@ static int platform_drv_remove(struct device *_dev)
int ret; int ret;
ret = drv->remove(dev); ret = drv->remove(dev);
if (ACPI_HANDLE(_dev))
acpi_dev_pm_detach(_dev, true); acpi_dev_pm_detach(_dev, true);
return ret; return ret;
...@@ -520,7 +518,6 @@ static void platform_drv_shutdown(struct device *_dev) ...@@ -520,7 +518,6 @@ static void platform_drv_shutdown(struct device *_dev)
struct platform_device *dev = to_platform_device(_dev); struct platform_device *dev = to_platform_device(_dev);
drv->shutdown(dev); drv->shutdown(dev);
if (ACPI_HANDLE(_dev))
acpi_dev_pm_detach(_dev, true); acpi_dev_pm_detach(_dev, true);
} }
......
...@@ -6,7 +6,6 @@ ...@@ -6,7 +6,6 @@
* This file is released under the GPLv2. * This file is released under the GPLv2.
*/ */
#include <linux/init.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/io.h> #include <linux/io.h>
......
...@@ -6,7 +6,6 @@ ...@@ -6,7 +6,6 @@
* This file is released under the GPLv2. * This file is released under the GPLv2.
*/ */
#include <linux/init.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/export.h> #include <linux/export.h>
......
...@@ -6,7 +6,6 @@ ...@@ -6,7 +6,6 @@
* This file is released under the GPLv2. * This file is released under the GPLv2.
*/ */
#include <linux/init.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
......
...@@ -6,7 +6,6 @@ ...@@ -6,7 +6,6 @@
* This file is released under the GPLv2. * This file is released under the GPLv2.
*/ */
#include <linux/init.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/pm_domain.h> #include <linux/pm_domain.h>
#include <linux/pm_qos.h> #include <linux/pm_qos.h>
......
...@@ -14,7 +14,6 @@ ...@@ -14,7 +14,6 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/init.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/cpufreq.h> #include <linux/cpufreq.h>
#include <linux/device.h> #include <linux/device.h>
......
...@@ -13,7 +13,6 @@ ...@@ -13,7 +13,6 @@
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/init.h>
static int regmap_i2c_write(void *context, const void *data, size_t count) static int regmap_i2c_write(void *context, const void *data, size_t count)
{ {
......
...@@ -18,7 +18,6 @@ ...@@ -18,7 +18,6 @@
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/init.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/regmap.h> #include <linux/regmap.h>
......
...@@ -12,7 +12,6 @@ ...@@ -12,7 +12,6 @@
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/spi/spi.h> #include <linux/spi/spi.h>
#include <linux/init.h>
#include <linux/module.h> #include <linux/module.h>
#include "internal.h" #include "internal.h"
......
...@@ -23,7 +23,6 @@ ...@@ -23,7 +23,6 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* *
*/ */
#include <linux/init.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/cpu.h> #include <linux/cpu.h>
#include <linux/module.h> #include <linux/module.h>
......
...@@ -892,13 +892,6 @@ static __init int gsmi_init(void) ...@@ -892,13 +892,6 @@ static __init int gsmi_init(void)
goto out_remove_sysfs_files; 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_reboot_notifier(&gsmi_reboot_notifier);
register_die_notifier(&gsmi_die_notifier); register_die_notifier(&gsmi_die_notifier);
atomic_notifier_chain_register(&panic_notifier_list, atomic_notifier_chain_register(&panic_notifier_list,
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include <linux/kobject.h> #include <linux/kobject.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/dmi.h> #include <linux/dmi.h>
#include <linux/io.h>
#include <asm/bios_ebda.h> #include <asm/bios_ebda.h>
#define BIOS_MEMCONSOLE_V1_MAGIC 0xDEADBABE #define BIOS_MEMCONSOLE_V1_MAGIC 0xDEADBABE
...@@ -41,15 +42,25 @@ struct biosmemcon_ebda { ...@@ -41,15 +42,25 @@ struct biosmemcon_ebda {
}; };
} __packed; } __packed;
static char *memconsole_baseaddr; static u32 memconsole_baseaddr;
static size_t memconsole_length; static size_t memconsole_length;
static ssize_t memconsole_read(struct file *filp, struct kobject *kobp, static ssize_t memconsole_read(struct file *filp, struct kobject *kobp,
struct bin_attribute *bin_attr, char *buf, struct bin_attribute *bin_attr, char *buf,
loff_t pos, size_t count) loff_t pos, size_t count)
{ {
return memory_read_from_buffer(buf, count, &pos, memconsole_baseaddr, 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); memconsole_length);
iounmap(memconsole);
return ret;
} }
static struct bin_attribute memconsole_bin_attr = { static struct bin_attribute memconsole_bin_attr = {
...@@ -58,43 +69,42 @@ 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); pr_info("BIOS console v1 EBDA structure found at %p\n", hdr);
printk(KERN_INFO "BIOS console buffer at 0x%.8x, " pr_info("BIOS console buffer at 0x%.8x, "
"start = %d, end = %d, num = %d\n", "start = %d, end = %d, num = %d\n",
hdr->v1.buffer_addr, hdr->v1.start, hdr->v1.buffer_addr, hdr->v1.start,
hdr->v1.end, hdr->v1.num_chars); hdr->v1.end, hdr->v1.num_chars);
memconsole_length = 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); pr_info("BIOS console v2 EBDA structure found at %p\n", hdr);
printk(KERN_INFO "BIOS console buffer at 0x%.8x, " pr_info("BIOS console buffer at 0x%.8x, "
"start = %d, end = %d, num_bytes = %d\n", "start = %d, end = %d, num_bytes = %d\n",
hdr->v2.buffer_addr, hdr->v2.start, hdr->v2.buffer_addr, hdr->v2.start,
hdr->v2.end, hdr->v2.num_bytes); hdr->v2.end, hdr->v2.num_bytes);
memconsole_length = hdr->v2.end - hdr->v2.start; memconsole_length = hdr->v2.end - hdr->v2.start;
memconsole_baseaddr = phys_to_virt(hdr->v2.buffer_addr memconsole_baseaddr = hdr->v2.buffer_addr + hdr->v2.start;
+ hdr->v2.start);
} }
/* /*
* Search through the EBDA for the BIOS Memory Console, and * Search through the EBDA for the BIOS Memory Console, and
* set the global variables to point to it. Return true if found. * 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; unsigned int address;
size_t length, cur; size_t length, cur;
address = get_bios_ebda(); address = get_bios_ebda();
if (!address) { if (!address) {
printk(KERN_INFO "BIOS EBDA non-existent.\n"); pr_info("BIOS EBDA non-existent.\n");
return false; return false;
} }
...@@ -122,7 +132,7 @@ static bool found_memconsole(void) ...@@ -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; return false;
} }
...@@ -139,8 +149,6 @@ MODULE_DEVICE_TABLE(dmi, memconsole_dmi_table); ...@@ -139,8 +149,6 @@ MODULE_DEVICE_TABLE(dmi, memconsole_dmi_table);
static int __init memconsole_init(void) static int __init memconsole_init(void)
{ {
int ret;
if (!dmi_check_system(memconsole_dmi_table)) if (!dmi_check_system(memconsole_dmi_table))
return -ENODEV; return -ENODEV;
...@@ -148,10 +156,7 @@ static int __init memconsole_init(void) ...@@ -148,10 +156,7 @@ static int __init memconsole_init(void)
return -ENODEV; return -ENODEV;
memconsole_bin_attr.size = memconsole_length; memconsole_bin_attr.size = memconsole_length;
return sysfs_create_bin_file(firmware_kobj, &memconsole_bin_attr);
ret = sysfs_create_bin_file(firmware_kobj, &memconsole_bin_attr);
return ret;
} }
static void __exit memconsole_exit(void) static void __exit memconsole_exit(void)
......
...@@ -471,7 +471,7 @@ struct drm_gem_object *drm_gem_prime_import(struct drm_device *dev, ...@@ -471,7 +471,7 @@ struct drm_gem_object *drm_gem_prime_import(struct drm_device *dev,
get_dma_buf(dma_buf); get_dma_buf(dma_buf);
sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL); sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL);
if (IS_ERR_OR_NULL(sgt)) { if (IS_ERR(sgt)) {
ret = PTR_ERR(sgt); ret = PTR_ERR(sgt);
goto fail_detach; goto fail_detach;
} }
......
...@@ -224,7 +224,7 @@ struct drm_gem_object *exynos_dmabuf_prime_import(struct drm_device *drm_dev, ...@@ -224,7 +224,7 @@ struct drm_gem_object *exynos_dmabuf_prime_import(struct drm_device *drm_dev,
get_dma_buf(dma_buf); get_dma_buf(dma_buf);
sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL); sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL);
if (IS_ERR_OR_NULL(sgt)) { if (IS_ERR(sgt)) {
ret = PTR_ERR(sgt); ret = PTR_ERR(sgt);
goto err_buf_detach; goto err_buf_detach;
} }
......
...@@ -719,7 +719,7 @@ static int vb2_dc_map_dmabuf(void *mem_priv) ...@@ -719,7 +719,7 @@ static int vb2_dc_map_dmabuf(void *mem_priv)
/* get the associated scatterlist for this buffer */ /* get the associated scatterlist for this buffer */
sgt = dma_buf_map_attachment(buf->db_attach, buf->dma_dir); 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"); pr_err("Error getting dmabuf scatterlist\n");
return -EINVAL; return -EINVAL;
} }
......
...@@ -351,28 +351,17 @@ static struct device_attribute dev_rescan_attr = __ATTR(rescan, ...@@ -351,28 +351,17 @@ static struct device_attribute dev_rescan_attr = __ATTR(rescan,
(S_IWUSR|S_IWGRP), (S_IWUSR|S_IWGRP),
NULL, dev_rescan_store); 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 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) const char *buf, size_t count)
{ {
int ret = 0;
unsigned long val; unsigned long val;
if (kstrtoul(buf, 0, &val) < 0) if (kstrtoul(buf, 0, &val) < 0)
return -EINVAL; return -EINVAL;
/* An attribute cannot be unregistered by one of its own methods, if (val && device_remove_file_self(dev, attr))
* so we have to use this roundabout approach. pci_stop_and_remove_bus_device_locked(to_pci_dev(dev));
*/
if (val)
ret = device_schedule_callback(dev, remove_callback);
if (ret)
count = ret;
return count; return count;
} }
static struct device_attribute dev_remove_attr = __ATTR(remove, static struct device_attribute dev_remove_attr = __ATTR(remove,
......
...@@ -304,12 +304,6 @@ dcssblk_load_segment(char *name, struct segment_info **seg_info) ...@@ -304,12 +304,6 @@ dcssblk_load_segment(char *name, struct segment_info **seg_info)
return rc; return rc;
} }
static void dcssblk_unregister_callback(struct device *dev)
{
device_unregister(dev);
put_device(dev);
}
/* /*
* device attribute for switching shared/nonshared (exclusive) * device attribute for switching shared/nonshared (exclusive)
* operation (show + store) * operation (show + store)
...@@ -397,7 +391,13 @@ dcssblk_shared_store(struct device *dev, struct device_attribute *attr, const ch ...@@ -397,7 +391,13 @@ dcssblk_shared_store(struct device *dev, struct device_attribute *attr, const ch
blk_cleanup_queue(dev_info->dcssblk_queue); blk_cleanup_queue(dev_info->dcssblk_queue);
dev_info->gd->queue = NULL; dev_info->gd->queue = NULL;
put_disk(dev_info->gd); 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: out:
up_write(&dcssblk_devices_sem); up_write(&dcssblk_devices_sem);
return rc; return rc;
......
...@@ -168,14 +168,12 @@ static ssize_t ccwgroup_online_show(struct device *dev, ...@@ -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 * Provide an 'ungroup' attribute so the user can remove group devices no
* longer needed or accidentially created. Saves memory :) * 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); mutex_lock(&gdev->reg_mutex);
if (device_is_registered(&gdev->dev)) { if (device_is_registered(&gdev->dev)) {
__ccwgroup_remove_symlinks(gdev); __ccwgroup_remove_symlinks(gdev);
device_unregister(dev); device_unregister(&gdev->dev);
__ccwgroup_remove_cdev_refs(gdev); __ccwgroup_remove_cdev_refs(gdev);
} }
mutex_unlock(&gdev->reg_mutex); mutex_unlock(&gdev->reg_mutex);
...@@ -195,10 +193,9 @@ static ssize_t ccwgroup_ungroup_store(struct device *dev, ...@@ -195,10 +193,9 @@ static ssize_t ccwgroup_ungroup_store(struct device *dev,
rc = -EINVAL; rc = -EINVAL;
goto out; goto out;
} }
/* Note that we cannot unregister the device from one of its
* attribute methods, so we have to use this roundabout approach. if (device_remove_file_self(dev, attr))
*/ ccwgroup_ungroup(gdev);
rc = device_schedule_callback(dev, ccwgroup_ungroup_callback);
out: out:
if (rc) { if (rc) {
if (rc != -EAGAIN) if (rc != -EAGAIN)
...@@ -224,6 +221,14 @@ static const struct attribute_group *ccwgroup_attr_groups[] = { ...@@ -224,6 +221,14 @@ static const struct attribute_group *ccwgroup_attr_groups[] = {
NULL, 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) static void ccwgroup_release(struct device *dev)
{ {
kfree(to_ccwgroupdev(dev)); kfree(to_ccwgroupdev(dev));
...@@ -323,6 +328,7 @@ int ccwgroup_create_dev(struct device *parent, struct ccwgroup_driver *gdrv, ...@@ -323,6 +328,7 @@ int ccwgroup_create_dev(struct device *parent, struct ccwgroup_driver *gdrv,
atomic_set(&gdev->onoff, 0); atomic_set(&gdev->onoff, 0);
mutex_init(&gdev->reg_mutex); mutex_init(&gdev->reg_mutex);
mutex_lock(&gdev->reg_mutex); mutex_lock(&gdev->reg_mutex);
INIT_WORK(&gdev->ungroup_work, ccwgroup_ungroup_workfn);
gdev->count = num_devices; gdev->count = num_devices;
gdev->dev.bus = &ccwgroup_bus_type; gdev->dev.bus = &ccwgroup_bus_type;
gdev->dev.parent = parent; gdev->dev.parent = parent;
...@@ -404,10 +410,10 @@ EXPORT_SYMBOL(ccwgroup_create_dev); ...@@ -404,10 +410,10 @@ EXPORT_SYMBOL(ccwgroup_create_dev);
static int ccwgroup_notifier(struct notifier_block *nb, unsigned long action, static int ccwgroup_notifier(struct notifier_block *nb, unsigned long action,
void *data) void *data)
{ {
struct device *dev = data; struct ccwgroup_device *gdev = to_ccwgroupdev(data);
if (action == BUS_NOTIFY_UNBIND_DRIVER) if (action == BUS_NOTIFY_UNBIND_DRIVER)
device_schedule_callback(dev, ccwgroup_ungroup_callback); schedule_work(&gdev->ungroup_work);
return NOTIFY_OK; return NOTIFY_OK;
} }
......
...@@ -649,23 +649,12 @@ store_rescan_field (struct device *dev, struct device_attribute *attr, ...@@ -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 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 static ssize_t
sdev_store_delete(struct device *dev, struct device_attribute *attr, sdev_store_delete(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count) const char *buf, size_t count)
{ {
int rc; if (device_remove_file_self(dev, attr))
scsi_remove_device(to_scsi_device(dev));
/* 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;
return count; return count;
}; };
static DEVICE_ATTR(delete, S_IWUSR, NULL, sdev_store_delete); static DEVICE_ATTR(delete, S_IWUSR, NULL, sdev_store_delete);
......
...@@ -96,6 +96,7 @@ endif # BLOCK ...@@ -96,6 +96,7 @@ endif # BLOCK
menu "Pseudo filesystems" menu "Pseudo filesystems"
source "fs/proc/Kconfig" source "fs/proc/Kconfig"
source "fs/kernfs/Kconfig"
source "fs/sysfs/Kconfig" source "fs/sysfs/Kconfig"
config TMPFS config TMPFS
......
...@@ -52,7 +52,8 @@ obj-$(CONFIG_FHANDLE) += fhandle.o ...@@ -52,7 +52,8 @@ obj-$(CONFIG_FHANDLE) += fhandle.o
obj-y += quota/ obj-y += quota/
obj-$(CONFIG_PROC_FS) += proc/ obj-$(CONFIG_PROC_FS) += proc/
obj-$(CONFIG_SYSFS) += sysfs/ kernfs/ obj-$(CONFIG_KERNFS) += kernfs/
obj-$(CONFIG_SYSFS) += sysfs/
obj-$(CONFIG_CONFIGFS_FS) += configfs/ obj-$(CONFIG_CONFIGFS_FS) += configfs/
obj-y += devpts/ obj-y += devpts/
......
#
# KERNFS should be selected by its users
#
config KERNFS
bool
default n
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
* This file is released under the GPLv2. * This file is released under the GPLv2.
*/ */
#include <linux/sched.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/namei.h> #include <linux/namei.h>
#include <linux/idr.h> #include <linux/idr.h>
...@@ -18,9 +19,161 @@ ...@@ -18,9 +19,161 @@
#include "kernfs-internal.h" #include "kernfs-internal.h"
DEFINE_MUTEX(kernfs_mutex); 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) #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 * kernfs_name_hash
* @name: Null terminated string to hash * @name: Null terminated string to hash
...@@ -37,7 +190,7 @@ static unsigned int kernfs_name_hash(const char *name, const void *ns) ...@@ -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 = (end_name_hash(hash) ^ hash_ptr((void *)ns, 31));
hash &= 0x7fffffffU; hash &= 0x7fffffffU;
/* Reserve hash numbers 0, 1 and INT_MAX for magic directory entries */ /* Reserve hash numbers 0, 1 and INT_MAX for magic directory entries */
if (hash < 1) if (hash < 2)
hash += 2; hash += 2;
if (hash >= INT_MAX) if (hash >= INT_MAX)
hash = INT_MAX - 1; hash = INT_MAX - 1;
...@@ -105,18 +258,24 @@ static int kernfs_link_sibling(struct kernfs_node *kn) ...@@ -105,18 +258,24 @@ static int kernfs_link_sibling(struct kernfs_node *kn)
* kernfs_unlink_sibling - unlink kernfs_node from sibling rbtree * kernfs_unlink_sibling - unlink kernfs_node from sibling rbtree
* @kn: kernfs_node of interest * @kn: kernfs_node of interest
* *
* Unlink @kn from its sibling rbtree which starts from * Try to unlink @kn from its sibling rbtree which starts from
* kn->parent->dir.children. * kn->parent->dir.children. Returns %true if @kn was actually
* removed, %false if @kn wasn't on the rbtree.
* *
* Locking: * Locking:
* mutex_lock(kernfs_mutex) * 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) if (kernfs_type(kn) == KERNFS_DIR)
kn->parent->dir.subdirs--; kn->parent->dir.subdirs--;
rb_erase(&kn->rb, &kn->parent->dir.children); 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) ...@@ -137,7 +296,7 @@ struct kernfs_node *kernfs_get_active(struct kernfs_node *kn)
if (!atomic_inc_unless_negative(&kn->active)) if (!atomic_inc_unless_negative(&kn->active))
return NULL; return NULL;
if (kn->flags & KERNFS_LOCKDEP) if (kernfs_lockdep(kn))
rwsem_acquire_read(&kn->dep_map, 0, 1, _RET_IP_); rwsem_acquire_read(&kn->dep_map, 0, 1, _RET_IP_);
return kn; return kn;
} }
...@@ -151,59 +310,57 @@ struct kernfs_node *kernfs_get_active(struct kernfs_node *kn) ...@@ -151,59 +310,57 @@ struct kernfs_node *kernfs_get_active(struct kernfs_node *kn)
*/ */
void kernfs_put_active(struct kernfs_node *kn) void kernfs_put_active(struct kernfs_node *kn)
{ {
struct kernfs_root *root = kernfs_root(kn);
int v; int v;
if (unlikely(!kn)) if (unlikely(!kn))
return; return;
if (kn->flags & KERNFS_LOCKDEP) if (kernfs_lockdep(kn))
rwsem_release(&kn->dep_map, 1, _RET_IP_); rwsem_release(&kn->dep_map, 1, _RET_IP_);
v = atomic_dec_return(&kn->active); v = atomic_dec_return(&kn->active);
if (likely(v != KN_DEACTIVATED_BIAS)) if (likely(v != KN_DEACTIVATED_BIAS))
return; return;
/* wake_up_all(&root->deactivate_waitq);
* atomic_dec_return() is a mb(), we'll always see the updated
* kn->u.completion.
*/
complete(kn->u.completion);
} }
/** /**
* kernfs_deactivate - deactivate kernfs_node * kernfs_drain - drain kernfs_node
* @kn: kernfs_node to deactivate * @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); struct kernfs_root *root = kernfs_root(kn);
int v;
BUG_ON(!(kn->flags & KERNFS_REMOVED));
if (!(kernfs_type(kn) & KERNFS_ACTIVE_REF)) lockdep_assert_held(&kernfs_mutex);
return; 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_); rwsem_acquire(&kn->dep_map, 0, 0, _RET_IP_);
/* atomic_add_return() is a mb(), put_active() will always see if (atomic_read(&kn->active) != KN_DEACTIVATED_BIAS)
* the updated kn->u.completion.
*/
v = atomic_add_return(KN_DEACTIVATED_BIAS, &kn->active);
if (v != KN_DEACTIVATED_BIAS) {
if (kn->flags & KERNFS_LOCKDEP)
lock_contended(&kn->dep_map, _RET_IP_); 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_); lock_acquired(&kn->dep_map, _RET_IP_);
rwsem_release(&kn->dep_map, 1, _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) ...@@ -234,13 +391,15 @@ void kernfs_put(struct kernfs_node *kn)
return; return;
root = kernfs_root(kn); root = kernfs_root(kn);
repeat: repeat:
/* Moving/renaming is always done while holding reference. /*
* Moving/renaming is always done while holding reference.
* kn->parent won't change beneath us. * kn->parent won't change beneath us.
*/ */
parent = kn->parent; parent = kn->parent;
WARN(!(kn->flags & KERNFS_REMOVED), "kernfs: free using entry: %s/%s\n", WARN_ONCE(atomic_read(&kn->active) != KN_DEACTIVATED_BIAS,
parent ? parent->name : "", kn->name); "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) if (kernfs_type(kn) == KERNFS_LINK)
kernfs_put(kn->symlink.target_kn); kernfs_put(kn->symlink.target_kn);
...@@ -282,8 +441,8 @@ static int kernfs_dop_revalidate(struct dentry *dentry, unsigned int flags) ...@@ -282,8 +441,8 @@ static int kernfs_dop_revalidate(struct dentry *dentry, unsigned int flags)
kn = dentry->d_fsdata; kn = dentry->d_fsdata;
mutex_lock(&kernfs_mutex); mutex_lock(&kernfs_mutex);
/* The kernfs node has been deleted */ /* The kernfs node has been deactivated */
if (kn->flags & KERNFS_REMOVED) if (!kernfs_active(kn))
goto out_bad; goto out_bad;
/* The kernfs node has been moved? */ /* The kernfs node has been moved? */
...@@ -328,6 +487,24 @@ const struct dentry_operations kernfs_dops = { ...@@ -328,6 +487,24 @@ const struct dentry_operations kernfs_dops = {
.d_release = kernfs_dop_release, .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, static struct kernfs_node *__kernfs_new_node(struct kernfs_root *root,
const char *name, umode_t mode, const char *name, umode_t mode,
unsigned flags) unsigned flags)
...@@ -352,11 +529,12 @@ static struct kernfs_node *__kernfs_new_node(struct kernfs_root *root, ...@@ -352,11 +529,12 @@ static struct kernfs_node *__kernfs_new_node(struct kernfs_root *root,
kn->ino = ret; kn->ino = ret;
atomic_set(&kn->count, 1); 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->name = name;
kn->mode = mode; kn->mode = mode;
kn->flags = flags | KERNFS_REMOVED; kn->flags = flags;
return kn; return kn;
...@@ -381,70 +559,45 @@ struct kernfs_node *kernfs_new_node(struct kernfs_node *parent, ...@@ -381,70 +559,45 @@ struct kernfs_node *kernfs_new_node(struct kernfs_node *parent,
return kn; 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 * kernfs_add_one - add kernfs_node to parent without warning
* @acxt: addrm context to use
* @kn: kernfs_node to be added * @kn: kernfs_node to be added
* *
* The caller must already have initialized @kn->parent. This * The caller must already have initialized @kn->parent. This
* function increments nlink of the parent's inode if @kn is a * function increments nlink of the parent's inode if @kn is a
* directory and link into the children list of the parent. * 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: * RETURNS:
* 0 on success, -EEXIST if entry with the given name already * 0 on success, -EEXIST if entry with the given name already
* exists. * 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; struct kernfs_node *parent = kn->parent;
bool has_ns = kernfs_ns_enabled(parent);
struct kernfs_iattrs *ps_iattr; struct kernfs_iattrs *ps_iattr;
bool has_ns;
int ret; int ret;
if (has_ns != (bool)kn->ns) { mutex_lock(&kernfs_mutex);
WARN(1, KERN_WARNING "kernfs: ns %s in '%s' for '%s'\n",
has_ns ? "required" : "invalid", parent->name, kn->name); ret = -EINVAL;
return -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) if (kernfs_type(parent) != KERNFS_DIR)
return -EINVAL; goto out_unlock;
if (parent->flags & KERNFS_REMOVED) ret = -ENOENT;
return -ENOENT; if ((parent->flags & KERNFS_ACTIVATED) && !kernfs_active(parent))
goto out_unlock;
kn->hash = kernfs_name_hash(kn->name, kn->ns); kn->hash = kernfs_name_hash(kn->name, kn->ns);
ret = kernfs_link_sibling(kn); ret = kernfs_link_sibling(kn);
if (ret) if (ret)
return ret; goto out_unlock;
/* Update timestamps on the parent */ /* Update timestamps on the parent */
ps_iattr = parent->iattr; ps_iattr = parent->iattr;
...@@ -453,82 +606,22 @@ int kernfs_add_one(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn) ...@@ -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; ps_iattrs->ia_ctime = ps_iattrs->ia_mtime = CURRENT_TIME;
} }
/* Mark the entry added into directory tree */ mutex_unlock(&kernfs_mutex);
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;
/* /*
* Removal can be called multiple times on the same node. Only the * Activate the new node unless CREATE_DEACTIVATED is requested.
* first invocation is effective and puts the base ref. * 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) if (!(kernfs_root(kn)->flags & KERNFS_ROOT_CREATE_DEACTIVATED))
return; kernfs_activate(kn);
return 0;
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;
}
/** out_unlock:
* 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() */
mutex_unlock(&kernfs_mutex); mutex_unlock(&kernfs_mutex);
return ret;
/* 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);
}
} }
/** /**
...@@ -599,13 +692,15 @@ EXPORT_SYMBOL_GPL(kernfs_find_and_get_ns); ...@@ -599,13 +692,15 @@ EXPORT_SYMBOL_GPL(kernfs_find_and_get_ns);
/** /**
* kernfs_create_root - create a new kernfs hierarchy * 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 * @priv: opaque data associated with the new directory
* *
* Returns the root of the new hierarchy on success, ERR_PTR() value on * Returns the root of the new hierarchy on success, ERR_PTR() value on
* failure. * 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_root *root;
struct kernfs_node *kn; struct kernfs_node *kn;
...@@ -624,12 +719,16 @@ struct kernfs_root *kernfs_create_root(struct kernfs_dir_ops *kdops, void *priv) ...@@ -624,12 +719,16 @@ struct kernfs_root *kernfs_create_root(struct kernfs_dir_ops *kdops, void *priv)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
} }
kn->flags &= ~KERNFS_REMOVED;
kn->priv = priv; kn->priv = priv;
kn->dir.root = root; kn->dir.root = root;
root->dir_ops = kdops; root->syscall_ops = scops;
root->flags = flags;
root->kn = kn; root->kn = kn;
init_waitqueue_head(&root->deactivate_waitq);
if (!(root->flags & KERNFS_ROOT_CREATE_DEACTIVATED))
kernfs_activate(kn);
return root; return root;
} }
...@@ -660,7 +759,6 @@ struct kernfs_node *kernfs_create_dir_ns(struct kernfs_node *parent, ...@@ -660,7 +759,6 @@ struct kernfs_node *kernfs_create_dir_ns(struct kernfs_node *parent,
const char *name, umode_t mode, const char *name, umode_t mode,
void *priv, const void *ns) void *priv, const void *ns)
{ {
struct kernfs_addrm_cxt acxt;
struct kernfs_node *kn; struct kernfs_node *kn;
int rc; int rc;
...@@ -674,10 +772,7 @@ struct kernfs_node *kernfs_create_dir_ns(struct kernfs_node *parent, ...@@ -674,10 +772,7 @@ struct kernfs_node *kernfs_create_dir_ns(struct kernfs_node *parent,
kn->priv = priv; kn->priv = priv;
/* link in */ /* link in */
kernfs_addrm_start(&acxt); rc = kernfs_add_one(kn);
rc = kernfs_add_one(&acxt, kn);
kernfs_addrm_finish(&acxt);
if (!rc) if (!rc)
return kn; return kn;
...@@ -703,7 +798,7 @@ static struct dentry *kernfs_iop_lookup(struct inode *dir, ...@@ -703,7 +798,7 @@ static struct dentry *kernfs_iop_lookup(struct inode *dir,
kn = kernfs_find_ns(parent, dentry->d_name.name, ns); kn = kernfs_find_ns(parent, dentry->d_name.name, ns);
/* no such entry */ /* no such entry */
if (!kn) { if (!kn || !kernfs_active(kn)) {
ret = NULL; ret = NULL;
goto out_unlock; goto out_unlock;
} }
...@@ -728,23 +823,37 @@ static int kernfs_iop_mkdir(struct inode *dir, struct dentry *dentry, ...@@ -728,23 +823,37 @@ static int kernfs_iop_mkdir(struct inode *dir, struct dentry *dentry,
umode_t mode) umode_t mode)
{ {
struct kernfs_node *parent = dir->i_private; 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 -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) static int kernfs_iop_rmdir(struct inode *dir, struct dentry *dentry)
{ {
struct kernfs_node *kn = dentry->d_fsdata; 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 -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, 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, ...@@ -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 *kn = old_dentry->d_fsdata;
struct kernfs_node *new_parent = new_dir->i_private; 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 -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 = { const struct inode_operations kernfs_dir_iops = {
...@@ -830,23 +952,104 @@ static struct kernfs_node *kernfs_next_descendant_post(struct kernfs_node *pos, ...@@ -830,23 +952,104 @@ static struct kernfs_node *kernfs_next_descendant_post(struct kernfs_node *pos,
return pos->parent; 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; return;
pr_debug("kernfs %s: removing\n", kn->name); 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 { do {
pos = next; pos = kernfs_leftmost_descendant(kn);
next = kernfs_next_descendant_post(pos, kn);
if (pos) /*
kernfs_remove_one(acxt, pos); * kernfs_drain() drops kernfs_mutex temporarily and @pos's
} while (next); * 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, ...@@ -857,11 +1060,140 @@ static void __kernfs_remove(struct kernfs_addrm_cxt *acxt,
*/ */
void kernfs_remove(struct kernfs_node *kn) 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_break_active_protection - break out of active protection
kernfs_addrm_finish(&acxt); * @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) ...@@ -876,7 +1208,6 @@ void kernfs_remove(struct kernfs_node *kn)
int kernfs_remove_by_name_ns(struct kernfs_node *parent, const char *name, int kernfs_remove_by_name_ns(struct kernfs_node *parent, const char *name,
const void *ns) const void *ns)
{ {
struct kernfs_addrm_cxt acxt;
struct kernfs_node *kn; struct kernfs_node *kn;
if (!parent) { if (!parent) {
...@@ -885,13 +1216,13 @@ int kernfs_remove_by_name_ns(struct kernfs_node *parent, const char *name, ...@@ -885,13 +1216,13 @@ int kernfs_remove_by_name_ns(struct kernfs_node *parent, const char *name,
return -ENOENT; return -ENOENT;
} }
kernfs_addrm_start(&acxt); mutex_lock(&kernfs_mutex);
kn = kernfs_find_ns(parent, name, ns); kn = kernfs_find_ns(parent, name, ns);
if (kn) if (kn)
__kernfs_remove(&acxt, kn); __kernfs_remove(kn);
kernfs_addrm_finish(&acxt); mutex_unlock(&kernfs_mutex);
if (kn) if (kn)
return 0; return 0;
...@@ -909,12 +1240,18 @@ int kernfs_remove_by_name_ns(struct kernfs_node *parent, const char *name, ...@@ -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, int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent,
const char *new_name, const void *new_ns) const char *new_name, const void *new_ns)
{ {
struct kernfs_node *old_parent;
const char *old_name = NULL;
int error; int error;
/* can't move or rename root */
if (!kn->parent)
return -EINVAL;
mutex_lock(&kernfs_mutex); mutex_lock(&kernfs_mutex);
error = -ENOENT; error = -ENOENT;
if ((kn->flags | new_parent->flags) & KERNFS_REMOVED) if (!kernfs_active(kn) || !kernfs_active(new_parent))
goto out; goto out;
error = 0; error = 0;
...@@ -932,13 +1269,8 @@ int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent, ...@@ -932,13 +1269,8 @@ int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent,
new_name = kstrdup(new_name, GFP_KERNEL); new_name = kstrdup(new_name, GFP_KERNEL);
if (!new_name) if (!new_name)
goto out; goto out;
} else {
if (kn->flags & KERNFS_STATIC_NAME) new_name = NULL;
kn->flags &= ~KERNFS_STATIC_NAME;
else
kfree(kn->name);
kn->name = new_name;
} }
/* /*
...@@ -946,12 +1278,29 @@ int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent, ...@@ -946,12 +1278,29 @@ int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent,
*/ */
kernfs_unlink_sibling(kn); kernfs_unlink_sibling(kn);
kernfs_get(new_parent); 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; 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->hash = kernfs_name_hash(kn->name, kn->ns);
kn->parent = new_parent;
kernfs_link_sibling(kn); kernfs_link_sibling(kn);
kernfs_put(old_parent);
kfree(old_name);
error = 0; error = 0;
out: out:
mutex_unlock(&kernfs_mutex); mutex_unlock(&kernfs_mutex);
...@@ -974,7 +1323,7 @@ static struct kernfs_node *kernfs_dir_pos(const void *ns, ...@@ -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) struct kernfs_node *parent, loff_t hash, struct kernfs_node *pos)
{ {
if (pos) { if (pos) {
int valid = !(pos->flags & KERNFS_REMOVED) && int valid = kernfs_active(pos) &&
pos->parent == parent && hash == pos->hash; pos->parent == parent && hash == pos->hash;
kernfs_put(pos); kernfs_put(pos);
if (!valid) if (!valid)
...@@ -993,8 +1342,8 @@ static struct kernfs_node *kernfs_dir_pos(const void *ns, ...@@ -993,8 +1342,8 @@ static struct kernfs_node *kernfs_dir_pos(const void *ns,
break; break;
} }
} }
/* Skip over entries in the wrong namespace */ /* Skip over entries which are dying/dead or in the wrong namespace */
while (pos && pos->ns != ns) { while (pos && (!kernfs_active(pos) || pos->ns != ns)) {
struct rb_node *node = rb_next(&pos->rb); struct rb_node *node = rb_next(&pos->rb);
if (!node) if (!node)
pos = NULL; pos = NULL;
...@@ -1008,14 +1357,15 @@ static struct kernfs_node *kernfs_dir_next_pos(const void *ns, ...@@ -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) struct kernfs_node *parent, ino_t ino, struct kernfs_node *pos)
{ {
pos = kernfs_dir_pos(ns, parent, ino, pos); pos = kernfs_dir_pos(ns, parent, ino, pos);
if (pos) if (pos) {
do { do {
struct rb_node *node = rb_next(&pos->rb); struct rb_node *node = rb_next(&pos->rb);
if (!node) if (!node)
pos = NULL; pos = NULL;
else else
pos = rb_to_kn(node); pos = rb_to_kn(node);
} while (pos && pos->ns != ns); } while (pos && (!kernfs_active(pos) || pos->ns != ns));
}
return pos; return pos;
} }
......
...@@ -252,10 +252,18 @@ static ssize_t kernfs_fop_write(struct file *file, const char __user *user_buf, ...@@ -252,10 +252,18 @@ static ssize_t kernfs_fop_write(struct file *file, const char __user *user_buf,
size_t count, loff_t *ppos) size_t count, loff_t *ppos)
{ {
struct kernfs_open_file *of = kernfs_of(file); struct kernfs_open_file *of = kernfs_of(file);
ssize_t len = min_t(size_t, count, PAGE_SIZE);
const struct kernfs_ops *ops; const struct kernfs_ops *ops;
size_t len;
char *buf; 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); buf = kmalloc(len + 1, GFP_KERNEL);
if (!buf) if (!buf)
return -ENOMEM; return -ENOMEM;
...@@ -652,6 +660,12 @@ static int kernfs_fop_open(struct inode *inode, struct file *file) ...@@ -652,6 +660,12 @@ static int kernfs_fop_open(struct inode *inode, struct file *file)
of->kn = kn; of->kn = kn;
of->file = file; 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 * Always instantiate seq_file even if read access doesn't use
* seq_file or is not requested. This unifies private data access * 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, ...@@ -820,7 +834,6 @@ struct kernfs_node *__kernfs_create_file(struct kernfs_node *parent,
bool name_is_static, bool name_is_static,
struct lock_class_key *key) struct lock_class_key *key)
{ {
struct kernfs_addrm_cxt acxt;
struct kernfs_node *kn; struct kernfs_node *kn;
unsigned flags; unsigned flags;
int rc; int rc;
...@@ -855,10 +868,7 @@ struct kernfs_node *__kernfs_create_file(struct kernfs_node *parent, ...@@ -855,10 +868,7 @@ struct kernfs_node *__kernfs_create_file(struct kernfs_node *parent,
if (ops->mmap) if (ops->mmap)
kn->flags |= KERNFS_HAS_MMAP; kn->flags |= KERNFS_HAS_MMAP;
kernfs_addrm_start(&acxt); rc = kernfs_add_one(kn);
rc = kernfs_add_one(&acxt, kn);
kernfs_addrm_finish(&acxt);
if (rc) { if (rc) {
kernfs_put(kn); kernfs_put(kn);
return ERR_PTR(rc); return ERR_PTR(rc);
......
...@@ -26,7 +26,8 @@ struct kernfs_iattrs { ...@@ -26,7 +26,8 @@ struct kernfs_iattrs {
struct simple_xattrs xattrs; 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 */ /* 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) ...@@ -44,13 +45,6 @@ static inline struct kernfs_root *kernfs_root(struct kernfs_node *kn)
return kn->dir.root; return kn->dir.root;
} }
/*
* Context structure to be used while adding/removing nodes.
*/
struct kernfs_addrm_cxt {
struct kernfs_node *removed;
};
/* /*
* mount.c * mount.c
*/ */
...@@ -71,6 +65,7 @@ struct kernfs_super_info { ...@@ -71,6 +65,7 @@ struct kernfs_super_info {
}; };
#define kernfs_info(SB) ((struct kernfs_super_info *)(SB->s_fs_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; extern struct kmem_cache *kernfs_node_cache;
/* /*
...@@ -100,9 +95,7 @@ extern const struct inode_operations kernfs_dir_iops; ...@@ -100,9 +95,7 @@ extern const struct inode_operations kernfs_dir_iops;
struct kernfs_node *kernfs_get_active(struct kernfs_node *kn); struct kernfs_node *kernfs_get_active(struct kernfs_node *kn);
void kernfs_put_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_node *kn);
int kernfs_add_one(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn);
void kernfs_addrm_finish(struct kernfs_addrm_cxt *acxt);
struct kernfs_node *kernfs_new_node(struct kernfs_node *parent, struct kernfs_node *kernfs_new_node(struct kernfs_node *parent,
const char *name, umode_t mode, const char *name, umode_t mode,
unsigned flags); unsigned flags);
......
...@@ -19,12 +19,49 @@ ...@@ -19,12 +19,49 @@
struct kmem_cache *kernfs_node_cache; 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, .statfs = simple_statfs,
.drop_inode = generic_delete_inode, .drop_inode = generic_delete_inode,
.evict_inode = kernfs_evict_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) static int kernfs_fill_super(struct super_block *sb)
{ {
struct kernfs_super_info *info = kernfs_info(sb); struct kernfs_super_info *info = kernfs_info(sb);
......
...@@ -27,7 +27,6 @@ struct kernfs_node *kernfs_create_link(struct kernfs_node *parent, ...@@ -27,7 +27,6 @@ struct kernfs_node *kernfs_create_link(struct kernfs_node *parent,
struct kernfs_node *target) struct kernfs_node *target)
{ {
struct kernfs_node *kn; struct kernfs_node *kn;
struct kernfs_addrm_cxt acxt;
int error; int error;
kn = kernfs_new_node(parent, name, S_IFLNK|S_IRWXUGO, KERNFS_LINK); 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, ...@@ -39,10 +38,7 @@ struct kernfs_node *kernfs_create_link(struct kernfs_node *parent,
kn->symlink.target_kn = target; kn->symlink.target_kn = target;
kernfs_get(target); /* ref owned by symlink */ kernfs_get(target); /* ref owned by symlink */
kernfs_addrm_start(&acxt); error = kernfs_add_one(kn);
error = kernfs_add_one(&acxt, kn);
kernfs_addrm_finish(&acxt);
if (!error) if (!error)
return kn; return kn;
......
config SYSFS config SYSFS
bool "sysfs file system support" if EXPERT bool "sysfs file system support" if EXPERT
default y default y
select KERNFS
help help
The sysfs filesystem is a virtual filesystem that the kernel uses to The sysfs filesystem is a virtual filesystem that the kernel uses to
export internal kernel objects, their attributes, and their export internal kernel objects, their attributes, and their
......
...@@ -19,39 +19,18 @@ ...@@ -19,39 +19,18 @@
DEFINE_SPINLOCK(sysfs_symlink_target_lock); 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) void sysfs_warn_dup(struct kernfs_node *parent, const char *name)
{ {
char *path; char *buf, *path = NULL;
path = kzalloc(PATH_MAX, GFP_KERNEL); buf = kzalloc(PATH_MAX, GFP_KERNEL);
if (path) { if (buf)
sysfs_pathname(parent, path); path = kernfs_path(parent, buf, PATH_MAX);
strlcat(path, "/", PATH_MAX);
strlcat(path, name, PATH_MAX);
}
WARN(1, KERN_WARNING "sysfs: cannot create duplicate filename '%s'\n", WARN(1, KERN_WARNING "sysfs: cannot create duplicate filename '%s/%s'\n",
path ? path : name); path, name);
kfree(path); kfree(buf);
} }
/** /**
...@@ -122,9 +101,13 @@ void sysfs_remove_dir(struct kobject *kobj) ...@@ -122,9 +101,13 @@ void sysfs_remove_dir(struct kobject *kobj)
int sysfs_rename_dir_ns(struct kobject *kobj, const char *new_name, int sysfs_rename_dir_ns(struct kobject *kobj, const char *new_name,
const void *new_ns) 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, 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, ...@@ -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 *kn = kobj->sd;
struct kernfs_node *new_parent; struct kernfs_node *new_parent;
BUG_ON(!kn->parent);
new_parent = new_parent_kobj && new_parent_kobj->sd ? new_parent = new_parent_kobj && new_parent_kobj->sd ?
new_parent_kobj->sd : sysfs_root_kn; new_parent_kobj->sd : sysfs_root_kn;
......
...@@ -372,6 +372,29 @@ void sysfs_remove_file_ns(struct kobject *kobj, const struct attribute *attr, ...@@ -372,6 +372,29 @@ void sysfs_remove_file_ns(struct kobject *kobj, const struct attribute *attr,
} }
EXPORT_SYMBOL_GPL(sysfs_remove_file_ns); 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) void sysfs_remove_files(struct kobject *kobj, const struct attribute **ptr)
{ {
int i; int i;
......
...@@ -70,8 +70,11 @@ static int create_files(struct kernfs_node *parent, struct kobject *kobj, ...@@ -70,8 +70,11 @@ static int create_files(struct kernfs_node *parent, struct kobject *kobj,
if (grp->bin_attrs) { if (grp->bin_attrs) {
for (bin_attr = grp->bin_attrs; *bin_attr; bin_attr++) { for (bin_attr = grp->bin_attrs; *bin_attr; bin_attr++) {
if (update) if (update)
sysfs_remove_bin_file(kobj, *bin_attr); kernfs_remove_by_name(parent,
error = sysfs_create_bin_file(kobj, *bin_attr); (*bin_attr)->attr.name);
error = sysfs_add_file_mode_ns(parent,
&(*bin_attr)->attr, true,
(*bin_attr)->attr.mode, NULL);
if (error) if (error)
break; break;
} }
......
...@@ -63,7 +63,7 @@ int __init sysfs_init(void) ...@@ -63,7 +63,7 @@ int __init sysfs_init(void)
{ {
int err; int err;
sysfs_root = kernfs_create_root(NULL, NULL); sysfs_root = kernfs_create_root(NULL, 0, NULL);
if (IS_ERR(sysfs_root)) if (IS_ERR(sysfs_root))
return PTR_ERR(sysfs_root); return PTR_ERR(sysfs_root);
......
...@@ -46,13 +46,6 @@ extern ssize_t arch_cpu_release(const char *, size_t); ...@@ -46,13 +46,6 @@ extern ssize_t arch_cpu_release(const char *, size_t);
#endif #endif
struct notifier_block; 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. * CPU notifier priorities.
*/ */
......
/*
* 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
...@@ -560,6 +560,8 @@ extern int device_create_file(struct device *device, ...@@ -560,6 +560,8 @@ extern int device_create_file(struct device *device,
const struct device_attribute *entry); const struct device_attribute *entry);
extern void device_remove_file(struct device *dev, extern void device_remove_file(struct device *dev,
const struct device_attribute *attr); 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, extern int __must_check device_create_bin_file(struct device *dev,
const struct bin_attribute *attr); const struct bin_attribute *attr);
extern void device_remove_bin_file(struct device *dev, extern void device_remove_bin_file(struct device *dev,
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
#include <linux/lockdep.h> #include <linux/lockdep.h>
#include <linux/rbtree.h> #include <linux/rbtree.h>
#include <linux/atomic.h> #include <linux/atomic.h>
#include <linux/completion.h> #include <linux/wait.h>
struct file; struct file;
struct dentry; struct dentry;
...@@ -35,16 +35,22 @@ enum kernfs_node_type { ...@@ -35,16 +35,22 @@ enum kernfs_node_type {
}; };
#define KERNFS_TYPE_MASK 0x000f #define KERNFS_TYPE_MASK 0x000f
#define KERNFS_ACTIVE_REF KERNFS_FILE
#define KERNFS_FLAG_MASK ~KERNFS_TYPE_MASK #define KERNFS_FLAG_MASK ~KERNFS_TYPE_MASK
enum kernfs_node_flag { enum kernfs_node_flag {
KERNFS_REMOVED = 0x0010, KERNFS_ACTIVATED = 0x0010,
KERNFS_NS = 0x0020, KERNFS_NS = 0x0020,
KERNFS_HAS_SEQ_SHOW = 0x0040, KERNFS_HAS_SEQ_SHOW = 0x0040,
KERNFS_HAS_MMAP = 0x0080, KERNFS_HAS_MMAP = 0x0080,
KERNFS_LOCKDEP = 0x0100, KERNFS_LOCKDEP = 0x0100,
KERNFS_STATIC_NAME = 0x0200, 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 */ /* type-specific structures for kernfs_node union members */
...@@ -85,17 +91,17 @@ struct kernfs_node { ...@@ -85,17 +91,17 @@ struct kernfs_node {
#ifdef CONFIG_DEBUG_LOCK_ALLOC #ifdef CONFIG_DEBUG_LOCK_ALLOC
struct lockdep_map dep_map; struct lockdep_map dep_map;
#endif #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; struct kernfs_node *parent;
const char *name; const char *name;
struct rb_node rb; struct rb_node rb;
union {
struct completion *completion;
struct kernfs_node *removed_list;
} u;
const void *ns; /* namespace tag */ const void *ns; /* namespace tag */
unsigned int hash; /* ns + name hash */ unsigned int hash; /* ns + name hash */
union { union {
...@@ -113,12 +119,16 @@ struct kernfs_node { ...@@ -113,12 +119,16 @@ struct kernfs_node {
}; };
/* /*
* kernfs_dir_ops may be specified on kernfs_create_root() to support * kernfs_syscall_ops may be specified on kernfs_create_root() to support
* directory manipulation syscalls. These optional callbacks are invoked * syscalls. These optional callbacks are invoked on the matching syscalls
* on the matching syscalls and can perform any kernfs operations which * and can perform any kernfs operations which don't necessarily have to be
* don't necessarily have to be the exact operation requested. * 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, int (*mkdir)(struct kernfs_node *parent, const char *name,
umode_t mode); umode_t mode);
int (*rmdir)(struct kernfs_node *kn); int (*rmdir)(struct kernfs_node *kn);
...@@ -129,22 +139,26 @@ struct kernfs_dir_ops { ...@@ -129,22 +139,26 @@ struct kernfs_dir_ops {
struct kernfs_root { struct kernfs_root {
/* published fields */ /* published fields */
struct kernfs_node *kn; struct kernfs_node *kn;
unsigned int flags; /* KERNFS_ROOT_* flags */
/* private fields, do not use outside kernfs proper */ /* private fields, do not use outside kernfs proper */
struct ida ino_ida; 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 { struct kernfs_open_file {
/* published fields */ /* published fields */
struct kernfs_node *kn; struct kernfs_node *kn;
struct file *file; struct file *file;
void *priv;
/* private fields, do not use outside kernfs proper */ /* private fields, do not use outside kernfs proper */
struct mutex mutex; struct mutex mutex;
int event; int event;
struct list_head list; struct list_head list;
size_t atomic_write_len;
bool mmapped; bool mmapped;
const struct vm_operations_struct *vm_ops; const struct vm_operations_struct *vm_ops;
}; };
...@@ -171,9 +185,13 @@ struct kernfs_ops { ...@@ -171,9 +185,13 @@ struct kernfs_ops {
loff_t off); loff_t off);
/* /*
* write() is bounced through kernel buffer and a write larger than * write() is bounced through kernel buffer. If atomic_write_len
* PAGE_SIZE results in partial operation of PAGE_SIZE. * 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, ssize_t (*write)(struct kernfs_open_file *of, char *buf, size_t bytes,
loff_t off); loff_t off);
...@@ -184,7 +202,7 @@ struct kernfs_ops { ...@@ -184,7 +202,7 @@ struct kernfs_ops {
#endif #endif
}; };
#ifdef CONFIG_SYSFS #ifdef CONFIG_KERNFS
static inline enum kernfs_node_type kernfs_type(struct kernfs_node *kn) 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) ...@@ -217,13 +235,22 @@ static inline bool kernfs_ns_enabled(struct kernfs_node *kn)
return kn->flags & KERNFS_NS; 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, struct kernfs_node *kernfs_find_and_get_ns(struct kernfs_node *parent,
const char *name, const void *ns); const char *name, const void *ns);
void kernfs_get(struct kernfs_node *kn); void kernfs_get(struct kernfs_node *kn);
void kernfs_put(struct kernfs_node *kn); void kernfs_put(struct kernfs_node *kn);
struct kernfs_root *kernfs_create_root(struct kernfs_dir_ops *kdops, struct kernfs_node *kernfs_node_from_dentry(struct dentry *dentry);
void *priv); 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); void kernfs_destroy_root(struct kernfs_root *root);
struct kernfs_node *kernfs_create_dir_ns(struct kernfs_node *parent, 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, ...@@ -239,7 +266,11 @@ struct kernfs_node *__kernfs_create_file(struct kernfs_node *parent,
struct kernfs_node *kernfs_create_link(struct kernfs_node *parent, struct kernfs_node *kernfs_create_link(struct kernfs_node *parent,
const char *name, const char *name,
struct kernfs_node *target); struct kernfs_node *target);
void kernfs_activate(struct kernfs_node *kn);
void kernfs_remove(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, int kernfs_remove_by_name_ns(struct kernfs_node *parent, const char *name,
const void *ns); const void *ns);
int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent, 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); ...@@ -255,7 +286,7 @@ void kernfs_kill_sb(struct super_block *sb);
void kernfs_init(void); void kernfs_init(void);
#else /* CONFIG_SYSFS */ #else /* CONFIG_KERNFS */
static inline enum kernfs_node_type kernfs_type(struct kernfs_node *kn) static inline enum kernfs_node_type kernfs_type(struct kernfs_node *kn)
{ return 0; } /* whatever */ { return 0; } /* whatever */
...@@ -265,6 +296,19 @@ static inline void kernfs_enable_ns(struct kernfs_node *kn) { } ...@@ -265,6 +296,19 @@ static inline void kernfs_enable_ns(struct kernfs_node *kn) { }
static inline bool kernfs_ns_enabled(struct kernfs_node *kn) static inline bool kernfs_ns_enabled(struct kernfs_node *kn)
{ return false; } { 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 * static inline struct kernfs_node *
kernfs_find_and_get_ns(struct kernfs_node *parent, const char *name, kernfs_find_and_get_ns(struct kernfs_node *parent, const char *name,
const void *ns) const void *ns)
...@@ -273,8 +317,15 @@ kernfs_find_and_get_ns(struct kernfs_node *parent, const char *name, ...@@ -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_get(struct kernfs_node *kn) { }
static inline void kernfs_put(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 * 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); } { return ERR_PTR(-ENOSYS); }
static inline void kernfs_destroy_root(struct kernfs_root *root) { } static inline void kernfs_destroy_root(struct kernfs_root *root) { }
...@@ -296,8 +347,13 @@ kernfs_create_link(struct kernfs_node *parent, const char *name, ...@@ -296,8 +347,13 @@ kernfs_create_link(struct kernfs_node *parent, const char *name,
struct kernfs_node *target) struct kernfs_node *target)
{ return ERR_PTR(-ENOSYS); } { return ERR_PTR(-ENOSYS); }
static inline void kernfs_activate(struct kernfs_node *kn) { }
static inline void kernfs_remove(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, static inline int kernfs_remove_by_name_ns(struct kernfs_node *kn,
const char *name, const void *ns) const char *name, const void *ns)
{ return -ENOSYS; } { return -ENOSYS; }
...@@ -325,7 +381,7 @@ static inline void kernfs_kill_sb(struct super_block *sb) { } ...@@ -325,7 +381,7 @@ static inline void kernfs_kill_sb(struct super_block *sb) { }
static inline void kernfs_init(void) { } static inline void kernfs_init(void) { }
#endif /* CONFIG_SYSFS */ #endif /* CONFIG_KERNFS */
static inline struct kernfs_node * static inline struct kernfs_node *
kernfs_find_and_get(struct kernfs_node *kn, const char *name) 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, ...@@ -367,6 +423,13 @@ static inline int kernfs_remove_by_name(struct kernfs_node *parent,
return kernfs_remove_by_name_ns(parent, name, NULL); 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 * static inline struct dentry *
kernfs_mount(struct file_system_type *fs_type, int flags, kernfs_mount(struct file_system_type *fs_type, int flags,
struct kernfs_root *root, bool *new_sb_created) struct kernfs_root *root, bool *new_sb_created)
......
...@@ -572,6 +572,15 @@ struct x86_cpu_id { ...@@ -572,6 +572,15 @@ struct x86_cpu_id {
#define X86_MODEL_ANY 0 #define X86_MODEL_ANY 0
#define X86_FEATURE_ANY 0 /* Same as FPU, you can't test for that */ #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_FORMAT 0xff
#define IPACK_ANY_ID (~0) #define IPACK_ANY_ID (~0)
struct ipack_device_id { struct ipack_device_id {
......
...@@ -198,6 +198,7 @@ int __must_check sysfs_chmod_file(struct kobject *kobj, ...@@ -198,6 +198,7 @@ int __must_check sysfs_chmod_file(struct kobject *kobj,
const struct attribute *attr, umode_t mode); const struct attribute *attr, umode_t mode);
void sysfs_remove_file_ns(struct kobject *kobj, const struct attribute *attr, void sysfs_remove_file_ns(struct kobject *kobj, const struct attribute *attr,
const void *ns); 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); void sysfs_remove_files(struct kobject *kobj, const struct attribute **attr);
int __must_check sysfs_create_bin_file(struct kobject *kobj, 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); ...@@ -246,6 +247,11 @@ void sysfs_notify(struct kobject *kobj, const char *dir, const char *attr);
int __must_check sysfs_init(void); int __must_check sysfs_init(void);
static inline void sysfs_enable_ns(struct kernfs_node *kn)
{
return kernfs_enable_ns(kn);
}
#else /* CONFIG_SYSFS */ #else /* CONFIG_SYSFS */
static inline int sysfs_schedule_callback(struct kobject *kobj, static inline int sysfs_schedule_callback(struct kobject *kobj,
...@@ -301,6 +307,12 @@ static inline void sysfs_remove_file_ns(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, static inline void sysfs_remove_files(struct kobject *kobj,
const struct attribute **attr) const struct attribute **attr)
{ {
...@@ -418,6 +430,10 @@ static inline int __must_check sysfs_init(void) ...@@ -418,6 +430,10 @@ static inline int __must_check sysfs_init(void)
return 0; return 0;
} }
static inline void sysfs_enable_ns(struct kernfs_node *kn)
{
}
#endif /* CONFIG_SYSFS */ #endif /* CONFIG_SYSFS */
static inline int __must_check sysfs_create_file(struct kobject *kobj, static inline int __must_check sysfs_create_file(struct kobject *kobj,
......
...@@ -94,7 +94,7 @@ static int create_dir(struct kobject *kobj) ...@@ -94,7 +94,7 @@ static int create_dir(struct kobject *kobj)
BUG_ON(ops->type >= KOBJ_NS_TYPES); BUG_ON(ops->type >= KOBJ_NS_TYPES);
BUG_ON(!kobj_ns_type_registered(ops->type)); BUG_ON(!kobj_ns_type_registered(ops->type));
kernfs_enable_ns(kobj->sd); sysfs_enable_ns(kobj->sd);
} }
return 0; return 0;
......
...@@ -174,6 +174,9 @@ int main(void) ...@@ -174,6 +174,9 @@ int main(void)
DEVID_FIELD(x86_cpu_id, model); DEVID_FIELD(x86_cpu_id, model);
DEVID_FIELD(x86_cpu_id, vendor); DEVID_FIELD(x86_cpu_id, vendor);
DEVID(cpu_feature);
DEVID_FIELD(cpu_feature, feature);
DEVID(mei_cl_device_id); DEVID(mei_cl_device_id);
DEVID_FIELD(mei_cl_device_id, name); DEVID_FIELD(mei_cl_device_id, name);
......
...@@ -1110,7 +1110,7 @@ static int do_amba_entry(const char *filename, ...@@ -1110,7 +1110,7 @@ static int do_amba_entry(const char *filename,
} }
ADD_TO_DEVTABLE("amba", amba_id, do_amba_entry); 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 * 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 * and feature, but getting those out of the build system here is too
* complicated. * complicated.
...@@ -1124,10 +1124,10 @@ static int do_x86cpu_entry(const char *filename, void *symval, ...@@ -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, model);
DEF_FIELD(symval, x86_cpu_id, vendor); DEF_FIELD(symval, x86_cpu_id, vendor);
strcpy(alias, "x86cpu:"); strcpy(alias, "cpu:type:x86,");
ADD(alias, "vendor:", vendor != X86_VENDOR_ANY, vendor); ADD(alias, "ven", vendor != X86_VENDOR_ANY, vendor);
ADD(alias, ":family:", family != X86_FAMILY_ANY, family); ADD(alias, "fam", family != X86_FAMILY_ANY, family);
ADD(alias, ":model:", model != X86_MODEL_ANY, model); ADD(alias, "mod", model != X86_MODEL_ANY, model);
strcat(alias, ":feature:*"); strcat(alias, ":feature:*");
if (feature != X86_FEATURE_ANY) if (feature != X86_FEATURE_ANY)
sprintf(alias + strlen(alias), "%04X*", feature); sprintf(alias + strlen(alias), "%04X*", feature);
...@@ -1135,6 +1135,16 @@ static int do_x86cpu_entry(const char *filename, void *symval, ...@@ -1135,6 +1135,16 @@ static int do_x86cpu_entry(const char *filename, void *symval,
} }
ADD_TO_DEVTABLE("x86cpu", x86_cpu_id, do_x86cpu_entry); 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 */ /* Looks like: mei:S */
static int do_mei_entry(const char *filename, void *symval, static int do_mei_entry(const char *filename, void *symval,
char *alias) char *alias)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment