Merge branch 'driver-core-next' into cgroup/for-3.15
Pending kernfs conversion depends on kernfs improvements in driver-core-next. Pull it into for-3.15. Signed-off-by: Tejun Heo <tj@kernel.org>
This commit is contained in:
commit
f7cef064aa
@ -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,
|
|||||||
}
|
}
|
||||||
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);
|
||||||
|
|
||||||
|
@ -570,6 +570,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.
|
||||||
@ -598,39 +615,6 @@ void device_remove_bin_file(struct device *dev,
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(device_remove_bin_file);
|
EXPORT_SYMBOL_GPL(device_remove_bin_file);
|
||||||
|
|
||||||
/**
|
|
||||||
* device_schedule_callback_owner - helper to schedule a callback for a device
|
|
||||||
* @dev: device.
|
|
||||||
* @func: callback function to invoke later.
|
|
||||||
* @owner: module owning the callback routine
|
|
||||||
*
|
|
||||||
* Attribute methods must not unregister themselves or their parent device
|
|
||||||
* (which would amount to the same thing). Attempts to do so will deadlock,
|
|
||||||
* since unregistration is mutually exclusive with driver callbacks.
|
|
||||||
*
|
|
||||||
* Instead methods can call this routine, which will attempt to allocate
|
|
||||||
* and schedule a workqueue request to call back @func with @dev as its
|
|
||||||
* argument in the workqueue's process context. @dev will be pinned until
|
|
||||||
* @func returns.
|
|
||||||
*
|
|
||||||
* This routine is usually called via the inline device_schedule_callback(),
|
|
||||||
* which automatically sets @owner to THIS_MODULE.
|
|
||||||
*
|
|
||||||
* Returns 0 if the request was submitted, -ENOMEM if storage could not
|
|
||||||
* be allocated, -ENODEV if a reference to @owner isn't available.
|
|
||||||
*
|
|
||||||
* NOTE: This routine won't work if CONFIG_SYSFS isn't set! It uses an
|
|
||||||
* underlying sysfs routine (since it is intended for use by attribute
|
|
||||||
* methods), and if sysfs isn't available you'll get nothing but -ENOSYS.
|
|
||||||
*/
|
|
||||||
int device_schedule_callback_owner(struct device *dev,
|
|
||||||
void (*func)(struct device *), struct module *owner)
|
|
||||||
{
|
|
||||||
return sysfs_schedule_callback(&dev->kobj,
|
|
||||||
(void (*)(void *)) func, dev, owner);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(device_schedule_callback_owner);
|
|
||||||
|
|
||||||
static void klist_children_get(struct klist_node *n)
|
static void klist_children_get(struct klist_node *n)
|
||||||
{
|
{
|
||||||
struct device_private *p = to_device_private_parent(n);
|
struct device_private *p = to_device_private_parent(n);
|
||||||
|
@ -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);
|
|||||||
* @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,
|
|||||||
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);
|
|||||||
* 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)
|
|||||||
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;
|
||||||
|
@ -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,
|
|||||||
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)
|
|||||||
|
|
||||||
/* 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,
|
|||||||
(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)
|
|||||||
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 @@ removeseg:
|
|||||||
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,
|
|||||||
* 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,
|
|||||||
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[] = {
|
|||||||
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,
|
|||||||
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);
|
|||||||
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,
|
|||||||
}
|
}
|
||||||
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
|
|||||||
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
|
|||||||
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/
|
||||||
|
|
||||||
|
7
fs/kernfs/Kconfig
Normal file
7
fs/kernfs/Kconfig
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#
|
||||||
|
# KERNFS should be selected by its users
|
||||||
|
#
|
||||||
|
|
||||||
|
config KERNFS
|
||||||
|
bool
|
||||||
|
default n
|
762
fs/kernfs/dir.c
762
fs/kernfs/dir.c
File diff suppressed because it is too large
Load Diff
@ -252,19 +252,9 @@ 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;
|
||||||
char *buf;
|
char *buf = NULL;
|
||||||
|
ssize_t len;
|
||||||
buf = kmalloc(len + 1, GFP_KERNEL);
|
|
||||||
if (!buf)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
if (copy_from_user(buf, user_buf, len)) {
|
|
||||||
len = -EFAULT;
|
|
||||||
goto out_free;
|
|
||||||
}
|
|
||||||
buf[len] = '\0'; /* guarantee string termination */
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @of->mutex nests outside active ref and is just to ensure that
|
* @of->mutex nests outside active ref and is just to ensure that
|
||||||
@ -273,22 +263,45 @@ static ssize_t kernfs_fop_write(struct file *file, const char __user *user_buf,
|
|||||||
mutex_lock(&of->mutex);
|
mutex_lock(&of->mutex);
|
||||||
if (!kernfs_get_active(of->kn)) {
|
if (!kernfs_get_active(of->kn)) {
|
||||||
mutex_unlock(&of->mutex);
|
mutex_unlock(&of->mutex);
|
||||||
len = -ENODEV;
|
return -ENODEV;
|
||||||
goto out_free;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ops = kernfs_ops(of->kn);
|
ops = kernfs_ops(of->kn);
|
||||||
if (ops->write)
|
if (!ops->write) {
|
||||||
len = ops->write(of, buf, len, *ppos);
|
|
||||||
else
|
|
||||||
len = -EINVAL;
|
len = -EINVAL;
|
||||||
|
goto out_unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ops->atomic_write_len) {
|
||||||
|
len = count;
|
||||||
|
if (len > ops->atomic_write_len) {
|
||||||
|
len = -E2BIG;
|
||||||
|
goto out_unlock;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
len = min_t(size_t, count, PAGE_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = kmalloc(len + 1, GFP_KERNEL);
|
||||||
|
if (!buf) {
|
||||||
|
len = -ENOMEM;
|
||||||
|
goto out_unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (copy_from_user(buf, user_buf, len)) {
|
||||||
|
len = -EFAULT;
|
||||||
|
goto out_unlock;
|
||||||
|
}
|
||||||
|
buf[len] = '\0'; /* guarantee string termination */
|
||||||
|
|
||||||
|
len = ops->write(of, buf, len, *ppos);
|
||||||
|
out_unlock:
|
||||||
kernfs_put_active(of->kn);
|
kernfs_put_active(of->kn);
|
||||||
mutex_unlock(&of->mutex);
|
mutex_unlock(&of->mutex);
|
||||||
|
|
||||||
if (len > 0)
|
if (len > 0)
|
||||||
*ppos += len;
|
*ppos += len;
|
||||||
out_free:
|
|
||||||
kfree(buf);
|
kfree(buf);
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
@ -820,7 +833,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 +867,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 {
|
|||||||
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)
|
|||||||
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
|
||||||
*/
|
*/
|
||||||
@ -100,9 +94,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 @@
|
|||||||
|
|
||||||
struct kmem_cache *kernfs_node_cache;
|
struct kmem_cache *kernfs_node_cache;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
static const struct super_operations kernfs_sops = {
|
static 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,
|
|||||||
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,
|
|||||||
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;
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
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 @@
|
|||||||
|
|
||||||
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)
|
|||||||
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,
|
|||||||
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;
|
||||||
|
|
||||||
|
115
fs/sysfs/file.c
115
fs/sysfs/file.c
@ -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;
|
||||||
@ -430,95 +453,3 @@ void sysfs_remove_bin_file(struct kobject *kobj,
|
|||||||
kernfs_remove_by_name(kobj->sd, attr->attr.name);
|
kernfs_remove_by_name(kobj->sd, attr->attr.name);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(sysfs_remove_bin_file);
|
EXPORT_SYMBOL_GPL(sysfs_remove_bin_file);
|
||||||
|
|
||||||
struct sysfs_schedule_callback_struct {
|
|
||||||
struct list_head workq_list;
|
|
||||||
struct kobject *kobj;
|
|
||||||
void (*func)(void *);
|
|
||||||
void *data;
|
|
||||||
struct module *owner;
|
|
||||||
struct work_struct work;
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct workqueue_struct *sysfs_workqueue;
|
|
||||||
static DEFINE_MUTEX(sysfs_workq_mutex);
|
|
||||||
static LIST_HEAD(sysfs_workq);
|
|
||||||
static void sysfs_schedule_callback_work(struct work_struct *work)
|
|
||||||
{
|
|
||||||
struct sysfs_schedule_callback_struct *ss = container_of(work,
|
|
||||||
struct sysfs_schedule_callback_struct, work);
|
|
||||||
|
|
||||||
(ss->func)(ss->data);
|
|
||||||
kobject_put(ss->kobj);
|
|
||||||
module_put(ss->owner);
|
|
||||||
mutex_lock(&sysfs_workq_mutex);
|
|
||||||
list_del(&ss->workq_list);
|
|
||||||
mutex_unlock(&sysfs_workq_mutex);
|
|
||||||
kfree(ss);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* sysfs_schedule_callback - helper to schedule a callback for a kobject
|
|
||||||
* @kobj: object we're acting for.
|
|
||||||
* @func: callback function to invoke later.
|
|
||||||
* @data: argument to pass to @func.
|
|
||||||
* @owner: module owning the callback code
|
|
||||||
*
|
|
||||||
* sysfs attribute methods must not unregister themselves or their parent
|
|
||||||
* kobject (which would amount to the same thing). Attempts to do so will
|
|
||||||
* deadlock, since unregistration is mutually exclusive with driver
|
|
||||||
* callbacks.
|
|
||||||
*
|
|
||||||
* Instead methods can call this routine, which will attempt to allocate
|
|
||||||
* and schedule a workqueue request to call back @func with @data as its
|
|
||||||
* argument in the workqueue's process context. @kobj will be pinned
|
|
||||||
* until @func returns.
|
|
||||||
*
|
|
||||||
* Returns 0 if the request was submitted, -ENOMEM if storage could not
|
|
||||||
* be allocated, -ENODEV if a reference to @owner isn't available,
|
|
||||||
* -EAGAIN if a callback has already been scheduled for @kobj.
|
|
||||||
*/
|
|
||||||
int sysfs_schedule_callback(struct kobject *kobj, void (*func)(void *),
|
|
||||||
void *data, struct module *owner)
|
|
||||||
{
|
|
||||||
struct sysfs_schedule_callback_struct *ss, *tmp;
|
|
||||||
|
|
||||||
if (!try_module_get(owner))
|
|
||||||
return -ENODEV;
|
|
||||||
|
|
||||||
mutex_lock(&sysfs_workq_mutex);
|
|
||||||
list_for_each_entry_safe(ss, tmp, &sysfs_workq, workq_list)
|
|
||||||
if (ss->kobj == kobj) {
|
|
||||||
module_put(owner);
|
|
||||||
mutex_unlock(&sysfs_workq_mutex);
|
|
||||||
return -EAGAIN;
|
|
||||||
}
|
|
||||||
mutex_unlock(&sysfs_workq_mutex);
|
|
||||||
|
|
||||||
if (sysfs_workqueue == NULL) {
|
|
||||||
sysfs_workqueue = create_singlethread_workqueue("sysfsd");
|
|
||||||
if (sysfs_workqueue == NULL) {
|
|
||||||
module_put(owner);
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ss = kmalloc(sizeof(*ss), GFP_KERNEL);
|
|
||||||
if (!ss) {
|
|
||||||
module_put(owner);
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
kobject_get(kobj);
|
|
||||||
ss->kobj = kobj;
|
|
||||||
ss->func = func;
|
|
||||||
ss->data = data;
|
|
||||||
ss->owner = owner;
|
|
||||||
INIT_WORK(&ss->work, sysfs_schedule_callback_work);
|
|
||||||
INIT_LIST_HEAD(&ss->workq_list);
|
|
||||||
mutex_lock(&sysfs_workq_mutex);
|
|
||||||
list_add_tail(&ss->workq_list, &sysfs_workq);
|
|
||||||
mutex_unlock(&sysfs_workq_mutex);
|
|
||||||
queue_work(sysfs_workqueue, &ss->work);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(sysfs_schedule_callback);
|
|
||||||
|
@ -62,7 +62,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);
|
||||||
|
|
||||||
|
@ -560,16 +560,12 @@ 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,
|
||||||
const struct bin_attribute *attr);
|
const struct bin_attribute *attr);
|
||||||
extern int device_schedule_callback_owner(struct device *dev,
|
|
||||||
void (*func)(struct device *dev), struct module *owner);
|
|
||||||
|
|
||||||
/* This is a macro to avoid include problems with THIS_MODULE */
|
|
||||||
#define device_schedule_callback(dev, func) \
|
|
||||||
device_schedule_callback_owner(dev, func, THIS_MODULE)
|
|
||||||
|
|
||||||
/* device resource management */
|
/* device resource management */
|
||||||
typedef void (*dr_release_t)(struct device *dev, void *res);
|
typedef void (*dr_release_t)(struct device *dev, void *res);
|
||||||
@ -929,10 +925,7 @@ extern int device_online(struct device *dev);
|
|||||||
extern struct device *__root_device_register(const char *name,
|
extern struct device *__root_device_register(const char *name,
|
||||||
struct module *owner);
|
struct module *owner);
|
||||||
|
|
||||||
/*
|
/* This is a macro to avoid include problems with THIS_MODULE */
|
||||||
* This is a macro to avoid include problems with THIS_MODULE,
|
|
||||||
* just as per what is done for device_schedule_callback() above.
|
|
||||||
*/
|
|
||||||
#define root_device_register(name) \
|
#define root_device_register(name) \
|
||||||
__root_device_register(name, THIS_MODULE)
|
__root_device_register(name, THIS_MODULE)
|
||||||
|
|
||||||
|
@ -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 {
|
|||||||
};
|
};
|
||||||
|
|
||||||
#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 {
|
|||||||
#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 {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 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,16 +139,19 @@ 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;
|
||||||
@ -171,9 +184,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 +201,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 +234,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 +265,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,
|
||||||
@ -254,7 +284,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 */
|
||||||
@ -264,6 +294,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)
|
||||||
@ -272,8 +315,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) { }
|
||||||
@ -295,8 +345,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; }
|
||||||
@ -324,7 +379,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)
|
||||||
@ -366,6 +421,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)
|
struct kernfs_root *root)
|
||||||
|
@ -178,9 +178,6 @@ struct sysfs_ops {
|
|||||||
|
|
||||||
#ifdef CONFIG_SYSFS
|
#ifdef CONFIG_SYSFS
|
||||||
|
|
||||||
int sysfs_schedule_callback(struct kobject *kobj, void (*func)(void *),
|
|
||||||
void *data, struct module *owner);
|
|
||||||
|
|
||||||
int __must_check sysfs_create_dir_ns(struct kobject *kobj, const void *ns);
|
int __must_check sysfs_create_dir_ns(struct kobject *kobj, const void *ns);
|
||||||
void sysfs_remove_dir(struct kobject *kobj);
|
void sysfs_remove_dir(struct kobject *kobj);
|
||||||
int __must_check sysfs_rename_dir_ns(struct kobject *kobj, const char *new_name,
|
int __must_check sysfs_rename_dir_ns(struct kobject *kobj, const char *new_name,
|
||||||
@ -198,6 +195,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,14 +244,13 @@ void sysfs_notify(struct kobject *kobj, const char *dir, const char *attr);
|
|||||||
|
|
||||||
int __must_check sysfs_init(void);
|
int __must_check sysfs_init(void);
|
||||||
|
|
||||||
#else /* CONFIG_SYSFS */
|
static inline void sysfs_enable_ns(struct kernfs_node *kn)
|
||||||
|
|
||||||
static inline int sysfs_schedule_callback(struct kobject *kobj,
|
|
||||||
void (*func)(void *), void *data, struct module *owner)
|
|
||||||
{
|
{
|
||||||
return -ENOSYS;
|
return kernfs_enable_ns(kn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#else /* CONFIG_SYSFS */
|
||||||
|
|
||||||
static inline int sysfs_create_dir_ns(struct kobject *kobj, const void *ns)
|
static inline int sysfs_create_dir_ns(struct kobject *kobj, const void *ns)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
@ -301,6 +298,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 +421,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)
|
|||||||
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;
|
||||||
|
Loading…
Reference in New Issue
Block a user