forked from Minki/linux
Merge git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core-2.6
* git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core-2.6: (27 commits) Driver core: fix race in dev_driver_string Driver Core: Early platform driver buffer sysfs: sysfs_setattr remove unnecessary permission check. sysfs: Factor out sysfs_rename from sysfs_rename_dir and sysfs_move_dir sysfs: Propagate renames to the vfs on demand sysfs: Gut sysfs_addrm_start and sysfs_addrm_finish sysfs: In sysfs_chmod_file lazily propagate the mode change. sysfs: Implement sysfs_getattr & sysfs_permission sysfs: Nicely indent sysfs_symlink_inode_operations sysfs: Update s_iattr on link and unlink. sysfs: Fix locking and factor out sysfs_sd_setattr sysfs: Simplify iattr time assignments sysfs: Simplify sysfs_chmod_file semantics sysfs: Use dentry_ops instead of directly playing with the dcache sysfs: Rename sysfs_d_iput to sysfs_dentry_iput sysfs: Update sysfs_setxattr so it updates secdata under the sysfs_mutex debugfs: fix create mutex racy fops and private data Driver core: Don't remove kobjects in device_shutdown. firmware_class: make request_firmware_nowait more useful Driver-Core: devtmpfs - set root directory mode to 0755 ...
This commit is contained in:
commit
f58df54a54
@ -56,7 +56,14 @@ static inline int device_is_not_partition(struct device *dev)
|
||||
*/
|
||||
const char *dev_driver_string(const struct device *dev)
|
||||
{
|
||||
return dev->driver ? dev->driver->name :
|
||||
struct device_driver *drv;
|
||||
|
||||
/* dev->driver can change to NULL underneath us because of unbinding,
|
||||
* so be careful about accessing it. dev->bus and dev->class should
|
||||
* never change once they are set, so they don't need special care.
|
||||
*/
|
||||
drv = ACCESS_ONCE(dev->driver);
|
||||
return drv ? drv->name :
|
||||
(dev->bus ? dev->bus->name :
|
||||
(dev->class ? dev->class->name : ""));
|
||||
}
|
||||
@ -986,6 +993,8 @@ done:
|
||||
AttrsError:
|
||||
device_remove_class_symlinks(dev);
|
||||
SymlinkError:
|
||||
if (MAJOR(dev->devt))
|
||||
devtmpfs_delete_node(dev);
|
||||
if (MAJOR(dev->devt))
|
||||
device_remove_sys_dev_entry(dev);
|
||||
devtattrError:
|
||||
@ -1728,8 +1737,5 @@ void device_shutdown(void)
|
||||
dev->driver->shutdown(dev);
|
||||
}
|
||||
}
|
||||
kobject_put(sysfs_dev_char_kobj);
|
||||
kobject_put(sysfs_dev_block_kobj);
|
||||
kobject_put(dev_kobj);
|
||||
async_synchronize_full();
|
||||
}
|
||||
|
@ -32,6 +32,8 @@ static int dev_mount = 1;
|
||||
static int dev_mount;
|
||||
#endif
|
||||
|
||||
static rwlock_t dirlock;
|
||||
|
||||
static int __init mount_param(char *str)
|
||||
{
|
||||
dev_mount = simple_strtoul(str, NULL, 0);
|
||||
@ -74,47 +76,35 @@ static int dev_mkdir(const char *name, mode_t mode)
|
||||
dentry = lookup_create(&nd, 1);
|
||||
if (!IS_ERR(dentry)) {
|
||||
err = vfs_mkdir(nd.path.dentry->d_inode, dentry, mode);
|
||||
if (!err)
|
||||
/* mark as kernel-created inode */
|
||||
dentry->d_inode->i_private = &dev_mnt;
|
||||
dput(dentry);
|
||||
} else {
|
||||
err = PTR_ERR(dentry);
|
||||
}
|
||||
mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
|
||||
|
||||
mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
|
||||
path_put(&nd.path);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int create_path(const char *nodepath)
|
||||
{
|
||||
char *path;
|
||||
struct nameidata nd;
|
||||
int err = 0;
|
||||
int err;
|
||||
|
||||
path = kstrdup(nodepath, GFP_KERNEL);
|
||||
if (!path)
|
||||
return -ENOMEM;
|
||||
|
||||
err = vfs_path_lookup(dev_mnt->mnt_root, dev_mnt,
|
||||
path, LOOKUP_PARENT, &nd);
|
||||
if (err == 0) {
|
||||
struct dentry *dentry;
|
||||
|
||||
/* create directory right away */
|
||||
dentry = lookup_create(&nd, 1);
|
||||
if (!IS_ERR(dentry)) {
|
||||
err = vfs_mkdir(nd.path.dentry->d_inode,
|
||||
dentry, 0755);
|
||||
dput(dentry);
|
||||
}
|
||||
mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
|
||||
|
||||
path_put(&nd.path);
|
||||
} else if (err == -ENOENT) {
|
||||
read_lock(&dirlock);
|
||||
err = dev_mkdir(nodepath, 0755);
|
||||
if (err == -ENOENT) {
|
||||
char *path;
|
||||
char *s;
|
||||
|
||||
/* parent directories do not exist, create them */
|
||||
path = kstrdup(nodepath, GFP_KERNEL);
|
||||
if (!path)
|
||||
return -ENOMEM;
|
||||
s = path;
|
||||
while (1) {
|
||||
for (;;) {
|
||||
s = strchr(s, '/');
|
||||
if (!s)
|
||||
break;
|
||||
@ -125,9 +115,9 @@ static int create_path(const char *nodepath)
|
||||
s[0] = '/';
|
||||
s++;
|
||||
}
|
||||
kfree(path);
|
||||
}
|
||||
|
||||
kfree(path);
|
||||
read_unlock(&dirlock);
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -156,34 +146,40 @@ int devtmpfs_create_node(struct device *dev)
|
||||
mode |= S_IFCHR;
|
||||
|
||||
curr_cred = override_creds(&init_cred);
|
||||
|
||||
err = vfs_path_lookup(dev_mnt->mnt_root, dev_mnt,
|
||||
nodename, LOOKUP_PARENT, &nd);
|
||||
if (err == -ENOENT) {
|
||||
/* create missing parent directories */
|
||||
create_path(nodename);
|
||||
err = vfs_path_lookup(dev_mnt->mnt_root, dev_mnt,
|
||||
nodename, LOOKUP_PARENT, &nd);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
dentry = lookup_create(&nd, 0);
|
||||
if (!IS_ERR(dentry)) {
|
||||
int umask;
|
||||
|
||||
umask = sys_umask(0000);
|
||||
err = vfs_mknod(nd.path.dentry->d_inode,
|
||||
dentry, mode, dev->devt);
|
||||
sys_umask(umask);
|
||||
/* mark as kernel created inode */
|
||||
if (!err)
|
||||
if (!err) {
|
||||
struct iattr newattrs;
|
||||
|
||||
/* fixup possibly umasked mode */
|
||||
newattrs.ia_mode = mode;
|
||||
newattrs.ia_valid = ATTR_MODE;
|
||||
mutex_lock(&dentry->d_inode->i_mutex);
|
||||
notify_change(dentry, &newattrs);
|
||||
mutex_unlock(&dentry->d_inode->i_mutex);
|
||||
|
||||
/* mark as kernel-created inode */
|
||||
dentry->d_inode->i_private = &dev_mnt;
|
||||
}
|
||||
dput(dentry);
|
||||
} else {
|
||||
err = PTR_ERR(dentry);
|
||||
}
|
||||
mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
|
||||
|
||||
mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
|
||||
path_put(&nd.path);
|
||||
out:
|
||||
kfree(tmp);
|
||||
@ -205,16 +201,21 @@ static int dev_rmdir(const char *name)
|
||||
mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
|
||||
dentry = lookup_one_len(nd.last.name, nd.path.dentry, nd.last.len);
|
||||
if (!IS_ERR(dentry)) {
|
||||
if (dentry->d_inode)
|
||||
err = vfs_rmdir(nd.path.dentry->d_inode, dentry);
|
||||
else
|
||||
if (dentry->d_inode) {
|
||||
if (dentry->d_inode->i_private == &dev_mnt)
|
||||
err = vfs_rmdir(nd.path.dentry->d_inode,
|
||||
dentry);
|
||||
else
|
||||
err = -EPERM;
|
||||
} else {
|
||||
err = -ENOENT;
|
||||
}
|
||||
dput(dentry);
|
||||
} else {
|
||||
err = PTR_ERR(dentry);
|
||||
}
|
||||
mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
|
||||
|
||||
mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
|
||||
path_put(&nd.path);
|
||||
return err;
|
||||
}
|
||||
@ -228,7 +229,8 @@ static int delete_path(const char *nodepath)
|
||||
if (!path)
|
||||
return -ENOMEM;
|
||||
|
||||
while (1) {
|
||||
write_lock(&dirlock);
|
||||
for (;;) {
|
||||
char *base;
|
||||
|
||||
base = strrchr(path, '/');
|
||||
@ -239,6 +241,7 @@ static int delete_path(const char *nodepath)
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
write_unlock(&dirlock);
|
||||
|
||||
kfree(path);
|
||||
return err;
|
||||
@ -322,9 +325,8 @@ out:
|
||||
* If configured, or requested by the commandline, devtmpfs will be
|
||||
* auto-mounted after the kernel mounted the root filesystem.
|
||||
*/
|
||||
int devtmpfs_mount(const char *mountpoint)
|
||||
int devtmpfs_mount(const char *mntdir)
|
||||
{
|
||||
struct path path;
|
||||
int err;
|
||||
|
||||
if (!dev_mount)
|
||||
@ -333,15 +335,11 @@ int devtmpfs_mount(const char *mountpoint)
|
||||
if (!dev_mnt)
|
||||
return 0;
|
||||
|
||||
err = kern_path(mountpoint, LOOKUP_FOLLOW, &path);
|
||||
if (err)
|
||||
return err;
|
||||
err = do_add_mount(dev_mnt, &path, 0, NULL);
|
||||
err = sys_mount("devtmpfs", (char *)mntdir, "devtmpfs", MS_SILENT, NULL);
|
||||
if (err)
|
||||
printk(KERN_INFO "devtmpfs: error mounting %i\n", err);
|
||||
else
|
||||
printk(KERN_INFO "devtmpfs: mounted\n");
|
||||
path_put(&path);
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -354,6 +352,8 @@ int __init devtmpfs_init(void)
|
||||
int err;
|
||||
struct vfsmount *mnt;
|
||||
|
||||
rwlock_init(&dirlock);
|
||||
|
||||
err = register_filesystem(&dev_fs_type);
|
||||
if (err) {
|
||||
printk(KERN_ERR "devtmpfs: unable to register devtmpfs "
|
||||
@ -361,7 +361,7 @@ int __init devtmpfs_init(void)
|
||||
return err;
|
||||
}
|
||||
|
||||
mnt = kern_mount(&dev_fs_type);
|
||||
mnt = kern_mount_data(&dev_fs_type, "mode=0755");
|
||||
if (IS_ERR(mnt)) {
|
||||
err = PTR_ERR(mnt);
|
||||
printk(KERN_ERR "devtmpfs: unable to create devtmpfs %i\n", err);
|
||||
|
@ -601,12 +601,9 @@ request_firmware_work_func(void *arg)
|
||||
}
|
||||
ret = _request_firmware(&fw, fw_work->name, fw_work->device,
|
||||
fw_work->uevent);
|
||||
if (ret < 0)
|
||||
fw_work->cont(NULL, fw_work->context);
|
||||
else {
|
||||
fw_work->cont(fw, fw_work->context);
|
||||
release_firmware(fw);
|
||||
}
|
||||
|
||||
fw_work->cont(fw, fw_work->context);
|
||||
|
||||
module_put(fw_work->module);
|
||||
kfree(fw_work);
|
||||
return ret;
|
||||
@ -619,6 +616,7 @@ request_firmware_work_func(void *arg)
|
||||
* is non-zero else the firmware copy must be done manually.
|
||||
* @name: name of firmware file
|
||||
* @device: device for which firmware is being loaded
|
||||
* @gfp: allocation flags
|
||||
* @context: will be passed over to @cont, and
|
||||
* @fw may be %NULL if firmware request fails.
|
||||
* @cont: function will be called asynchronously when the firmware
|
||||
@ -631,12 +629,12 @@ request_firmware_work_func(void *arg)
|
||||
int
|
||||
request_firmware_nowait(
|
||||
struct module *module, int uevent,
|
||||
const char *name, struct device *device, void *context,
|
||||
const char *name, struct device *device, gfp_t gfp, void *context,
|
||||
void (*cont)(const struct firmware *fw, void *context))
|
||||
{
|
||||
struct task_struct *task;
|
||||
struct firmware_work *fw_work = kmalloc(sizeof (struct firmware_work),
|
||||
GFP_ATOMIC);
|
||||
gfp);
|
||||
|
||||
if (!fw_work)
|
||||
return -ENOMEM;
|
||||
|
@ -1000,7 +1000,7 @@ static __initdata LIST_HEAD(early_platform_device_list);
|
||||
int __init early_platform_driver_register(struct early_platform_driver *epdrv,
|
||||
char *buf)
|
||||
{
|
||||
unsigned long index;
|
||||
char *tmp;
|
||||
int n;
|
||||
|
||||
/* Simply add the driver to the end of the global list.
|
||||
@ -1019,13 +1019,28 @@ int __init early_platform_driver_register(struct early_platform_driver *epdrv,
|
||||
if (buf && !strncmp(buf, epdrv->pdrv->driver.name, n)) {
|
||||
list_move(&epdrv->list, &early_platform_driver_list);
|
||||
|
||||
if (!strcmp(buf, epdrv->pdrv->driver.name))
|
||||
/* Allow passing parameters after device name */
|
||||
if (buf[n] == '\0' || buf[n] == ',')
|
||||
epdrv->requested_id = -1;
|
||||
else if (buf[n] == '.' && strict_strtoul(&buf[n + 1], 10,
|
||||
&index) == 0)
|
||||
epdrv->requested_id = index;
|
||||
else
|
||||
epdrv->requested_id = EARLY_PLATFORM_ID_ERROR;
|
||||
else {
|
||||
epdrv->requested_id = simple_strtoul(&buf[n + 1],
|
||||
&tmp, 10);
|
||||
|
||||
if (buf[n] != '.' || (tmp == &buf[n + 1])) {
|
||||
epdrv->requested_id = EARLY_PLATFORM_ID_ERROR;
|
||||
n = 0;
|
||||
} else
|
||||
n += strcspn(&buf[n + 1], ",") + 1;
|
||||
}
|
||||
|
||||
if (buf[n] == ',')
|
||||
n++;
|
||||
|
||||
if (epdrv->bufsize) {
|
||||
memcpy(epdrv->buffer, &buf[n],
|
||||
min_t(int, epdrv->bufsize, strlen(&buf[n]) + 1));
|
||||
epdrv->buffer[epdrv->bufsize - 1] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -544,9 +544,12 @@ static void callbackfn_rbu(const struct firmware *fw, void *context)
|
||||
{
|
||||
rbu_data.entry_created = 0;
|
||||
|
||||
if (!fw || !fw->size)
|
||||
if (!fw)
|
||||
return;
|
||||
|
||||
if (!fw->size)
|
||||
goto out;
|
||||
|
||||
spin_lock(&rbu_data.lock);
|
||||
if (!strcmp(image_type, "mono")) {
|
||||
if (!img_update_realloc(fw->size))
|
||||
@ -568,6 +571,8 @@ static void callbackfn_rbu(const struct firmware *fw, void *context)
|
||||
} else
|
||||
pr_debug("invalid image type specified.\n");
|
||||
spin_unlock(&rbu_data.lock);
|
||||
out:
|
||||
release_firmware(fw);
|
||||
}
|
||||
|
||||
static ssize_t read_rbu_image_type(struct kobject *kobj,
|
||||
@ -615,7 +620,7 @@ static ssize_t write_rbu_image_type(struct kobject *kobj,
|
||||
spin_unlock(&rbu_data.lock);
|
||||
req_firm_rc = request_firmware_nowait(THIS_MODULE,
|
||||
FW_ACTION_NOHOTPLUG, "dell_rbu",
|
||||
&rbu_device->dev, &context,
|
||||
&rbu_device->dev, GFP_KERNEL, &context,
|
||||
callbackfn_rbu);
|
||||
if (req_firm_rc) {
|
||||
printk(KERN_ERR
|
||||
|
@ -44,9 +44,20 @@ struct ilo_hwinfo {
|
||||
|
||||
struct pci_dev *ilo_dev;
|
||||
|
||||
/*
|
||||
* open_lock serializes ccb_cnt during open and close
|
||||
* [ irq disabled ]
|
||||
* -> alloc_lock used when adding/removing/searching ccb_alloc,
|
||||
* which represents all ccbs open on the device
|
||||
* --> fifo_lock controls access to fifo queues shared with hw
|
||||
*
|
||||
* Locks must be taken in this order, but open_lock and alloc_lock
|
||||
* are optional, they do not need to be held in order to take a
|
||||
* lower level lock.
|
||||
*/
|
||||
spinlock_t open_lock;
|
||||
spinlock_t alloc_lock;
|
||||
spinlock_t fifo_lock;
|
||||
spinlock_t open_lock;
|
||||
|
||||
struct cdev cdev;
|
||||
};
|
||||
|
@ -1179,16 +1179,18 @@ static void uart_firmware_cont(const struct firmware *fw, void *context)
|
||||
|
||||
if (firmware->header.length != fw->size) {
|
||||
dev_err(dev, "invalid firmware\n");
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = qe_upload_firmware(firmware);
|
||||
if (ret) {
|
||||
dev_err(dev, "could not load firmware\n");
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
|
||||
firmware_loaded = 1;
|
||||
out:
|
||||
release_firmware(fw);
|
||||
}
|
||||
|
||||
static int ucc_uart_probe(struct of_device *ofdev,
|
||||
@ -1247,7 +1249,7 @@ static int ucc_uart_probe(struct of_device *ofdev,
|
||||
*/
|
||||
ret = request_firmware_nowait(THIS_MODULE,
|
||||
FW_ACTION_HOTPLUG, filename, &ofdev->dev,
|
||||
&ofdev->dev, uart_firmware_cont);
|
||||
GFP_KERNEL, &ofdev->dev, uart_firmware_cont);
|
||||
if (ret) {
|
||||
dev_err(&ofdev->dev,
|
||||
"could not load firmware %s\n",
|
||||
|
@ -2327,9 +2327,11 @@ static void usbdux_firmware_request_complete_handler(const struct firmware *fw,
|
||||
if (ret) {
|
||||
dev_err(&usbdev->dev,
|
||||
"Could not upload firmware (err=%d)\n", ret);
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
comedi_usb_auto_config(usbdev, BOARDNAME);
|
||||
out:
|
||||
release_firmware(fw);
|
||||
}
|
||||
|
||||
/* allocate memory for the urbs and initialise them */
|
||||
@ -2580,6 +2582,7 @@ static int usbduxsub_probe(struct usb_interface *uinterf,
|
||||
FW_ACTION_HOTPLUG,
|
||||
"usbdux_firmware.bin",
|
||||
&udev->dev,
|
||||
GFP_KERNEL,
|
||||
usbduxsub + index,
|
||||
usbdux_firmware_request_complete_handler);
|
||||
|
||||
|
@ -1451,10 +1451,12 @@ static void usbduxfast_firmware_request_complete_handler(const struct firmware
|
||||
if (ret) {
|
||||
dev_err(&usbdev->dev,
|
||||
"Could not upload firmware (err=%d)\n", ret);
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
|
||||
comedi_usb_auto_config(usbdev, BOARDNAME);
|
||||
out:
|
||||
release_firmware(fw);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1569,6 +1571,7 @@ static int usbduxfastsub_probe(struct usb_interface *uinterf,
|
||||
FW_ACTION_HOTPLUG,
|
||||
"usbduxfast_firmware.bin",
|
||||
&udev->dev,
|
||||
GFP_KERNEL,
|
||||
usbduxfastsub + index,
|
||||
usbduxfast_firmware_request_complete_handler);
|
||||
|
||||
|
@ -667,12 +667,12 @@ static void uea_upload_pre_firmware(const struct firmware *fw_entry, void *conte
|
||||
else
|
||||
uea_info(usb, "firmware uploaded\n");
|
||||
|
||||
uea_leaves(usb);
|
||||
return;
|
||||
goto err;
|
||||
|
||||
err_fw_corrupted:
|
||||
uea_err(usb, "firmware is corrupted\n");
|
||||
err:
|
||||
release_firmware(fw_entry);
|
||||
uea_leaves(usb);
|
||||
}
|
||||
|
||||
@ -705,7 +705,8 @@ static int uea_load_firmware(struct usb_device *usb, unsigned int ver)
|
||||
break;
|
||||
}
|
||||
|
||||
ret = request_firmware_nowait(THIS_MODULE, 1, fw_name, &usb->dev, usb, uea_upload_pre_firmware);
|
||||
ret = request_firmware_nowait(THIS_MODULE, 1, fw_name, &usb->dev,
|
||||
GFP_KERNEL, usb, uea_upload_pre_firmware);
|
||||
if (ret)
|
||||
uea_err(usb, "firmware %s is not available\n", fw_name);
|
||||
else
|
||||
|
@ -32,7 +32,9 @@ static struct vfsmount *debugfs_mount;
|
||||
static int debugfs_mount_count;
|
||||
static bool debugfs_registered;
|
||||
|
||||
static struct inode *debugfs_get_inode(struct super_block *sb, int mode, dev_t dev)
|
||||
static struct inode *debugfs_get_inode(struct super_block *sb, int mode, dev_t dev,
|
||||
void *data, const struct file_operations *fops)
|
||||
|
||||
{
|
||||
struct inode *inode = new_inode(sb);
|
||||
|
||||
@ -44,14 +46,18 @@ static struct inode *debugfs_get_inode(struct super_block *sb, int mode, dev_t d
|
||||
init_special_inode(inode, mode, dev);
|
||||
break;
|
||||
case S_IFREG:
|
||||
inode->i_fop = &debugfs_file_operations;
|
||||
inode->i_fop = fops ? fops : &debugfs_file_operations;
|
||||
inode->i_private = data;
|
||||
break;
|
||||
case S_IFLNK:
|
||||
inode->i_op = &debugfs_link_operations;
|
||||
inode->i_fop = fops;
|
||||
inode->i_private = data;
|
||||
break;
|
||||
case S_IFDIR:
|
||||
inode->i_op = &simple_dir_inode_operations;
|
||||
inode->i_fop = &simple_dir_operations;
|
||||
inode->i_fop = fops ? fops : &simple_dir_operations;
|
||||
inode->i_private = data;
|
||||
|
||||
/* directory inodes start off with i_nlink == 2
|
||||
* (for "." entry) */
|
||||
@ -64,7 +70,8 @@ static struct inode *debugfs_get_inode(struct super_block *sb, int mode, dev_t d
|
||||
|
||||
/* SMP-safe */
|
||||
static int debugfs_mknod(struct inode *dir, struct dentry *dentry,
|
||||
int mode, dev_t dev)
|
||||
int mode, dev_t dev, void *data,
|
||||
const struct file_operations *fops)
|
||||
{
|
||||
struct inode *inode;
|
||||
int error = -EPERM;
|
||||
@ -72,7 +79,7 @@ static int debugfs_mknod(struct inode *dir, struct dentry *dentry,
|
||||
if (dentry->d_inode)
|
||||
return -EEXIST;
|
||||
|
||||
inode = debugfs_get_inode(dir->i_sb, mode, dev);
|
||||
inode = debugfs_get_inode(dir->i_sb, mode, dev, data, fops);
|
||||
if (inode) {
|
||||
d_instantiate(dentry, inode);
|
||||
dget(dentry);
|
||||
@ -81,12 +88,13 @@ static int debugfs_mknod(struct inode *dir, struct dentry *dentry,
|
||||
return error;
|
||||
}
|
||||
|
||||
static int debugfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
|
||||
static int debugfs_mkdir(struct inode *dir, struct dentry *dentry, int mode,
|
||||
void *data, const struct file_operations *fops)
|
||||
{
|
||||
int res;
|
||||
|
||||
mode = (mode & (S_IRWXUGO | S_ISVTX)) | S_IFDIR;
|
||||
res = debugfs_mknod(dir, dentry, mode, 0);
|
||||
res = debugfs_mknod(dir, dentry, mode, 0, data, fops);
|
||||
if (!res) {
|
||||
inc_nlink(dir);
|
||||
fsnotify_mkdir(dir, dentry);
|
||||
@ -94,18 +102,20 @@ static int debugfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
|
||||
return res;
|
||||
}
|
||||
|
||||
static int debugfs_link(struct inode *dir, struct dentry *dentry, int mode)
|
||||
static int debugfs_link(struct inode *dir, struct dentry *dentry, int mode,
|
||||
void *data, const struct file_operations *fops)
|
||||
{
|
||||
mode = (mode & S_IALLUGO) | S_IFLNK;
|
||||
return debugfs_mknod(dir, dentry, mode, 0);
|
||||
return debugfs_mknod(dir, dentry, mode, 0, data, fops);
|
||||
}
|
||||
|
||||
static int debugfs_create(struct inode *dir, struct dentry *dentry, int mode)
|
||||
static int debugfs_create(struct inode *dir, struct dentry *dentry, int mode,
|
||||
void *data, const struct file_operations *fops)
|
||||
{
|
||||
int res;
|
||||
|
||||
mode = (mode & S_IALLUGO) | S_IFREG;
|
||||
res = debugfs_mknod(dir, dentry, mode, 0);
|
||||
res = debugfs_mknod(dir, dentry, mode, 0, data, fops);
|
||||
if (!res)
|
||||
fsnotify_create(dir, dentry);
|
||||
return res;
|
||||
@ -139,7 +149,9 @@ static struct file_system_type debug_fs_type = {
|
||||
|
||||
static int debugfs_create_by_name(const char *name, mode_t mode,
|
||||
struct dentry *parent,
|
||||
struct dentry **dentry)
|
||||
struct dentry **dentry,
|
||||
void *data,
|
||||
const struct file_operations *fops)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
@ -164,13 +176,16 @@ static int debugfs_create_by_name(const char *name, mode_t mode,
|
||||
if (!IS_ERR(*dentry)) {
|
||||
switch (mode & S_IFMT) {
|
||||
case S_IFDIR:
|
||||
error = debugfs_mkdir(parent->d_inode, *dentry, mode);
|
||||
error = debugfs_mkdir(parent->d_inode, *dentry, mode,
|
||||
data, fops);
|
||||
break;
|
||||
case S_IFLNK:
|
||||
error = debugfs_link(parent->d_inode, *dentry, mode);
|
||||
error = debugfs_link(parent->d_inode, *dentry, mode,
|
||||
data, fops);
|
||||
break;
|
||||
default:
|
||||
error = debugfs_create(parent->d_inode, *dentry, mode);
|
||||
error = debugfs_create(parent->d_inode, *dentry, mode,
|
||||
data, fops);
|
||||
break;
|
||||
}
|
||||
dput(*dentry);
|
||||
@ -221,19 +236,13 @@ struct dentry *debugfs_create_file(const char *name, mode_t mode,
|
||||
if (error)
|
||||
goto exit;
|
||||
|
||||
error = debugfs_create_by_name(name, mode, parent, &dentry);
|
||||
error = debugfs_create_by_name(name, mode, parent, &dentry,
|
||||
data, fops);
|
||||
if (error) {
|
||||
dentry = NULL;
|
||||
simple_release_fs(&debugfs_mount, &debugfs_mount_count);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (dentry->d_inode) {
|
||||
if (data)
|
||||
dentry->d_inode->i_private = data;
|
||||
if (fops)
|
||||
dentry->d_inode->i_fop = fops;
|
||||
}
|
||||
exit:
|
||||
return dentry;
|
||||
}
|
||||
|
22
fs/namei.c
22
fs/namei.c
@ -1279,28 +1279,6 @@ struct dentry *lookup_one_len(const char *name, struct dentry *base, int len)
|
||||
return __lookup_hash(&this, base, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* lookup_one_noperm - bad hack for sysfs
|
||||
* @name: pathname component to lookup
|
||||
* @base: base directory to lookup from
|
||||
*
|
||||
* This is a variant of lookup_one_len that doesn't perform any permission
|
||||
* checks. It's a horrible hack to work around the braindead sysfs
|
||||
* architecture and should not be used anywhere else.
|
||||
*
|
||||
* DON'T USE THIS FUNCTION EVER, thanks.
|
||||
*/
|
||||
struct dentry *lookup_one_noperm(const char *name, struct dentry *base)
|
||||
{
|
||||
int err;
|
||||
struct qstr this;
|
||||
|
||||
err = __lookup_one_len(name, &this, base, strlen(name));
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
return __lookup_hash(&this, base, NULL);
|
||||
}
|
||||
|
||||
int user_path_at(int dfd, const char __user *name, unsigned flags,
|
||||
struct path *path)
|
||||
{
|
||||
|
392
fs/sysfs/dir.c
392
fs/sysfs/dir.c
@ -25,7 +25,6 @@
|
||||
#include "sysfs.h"
|
||||
|
||||
DEFINE_MUTEX(sysfs_mutex);
|
||||
DEFINE_MUTEX(sysfs_rename_mutex);
|
||||
DEFINE_SPINLOCK(sysfs_assoc_lock);
|
||||
|
||||
static DEFINE_SPINLOCK(sysfs_ino_lock);
|
||||
@ -84,46 +83,6 @@ static void sysfs_unlink_sibling(struct sysfs_dirent *sd)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* sysfs_get_dentry - get dentry for the given sysfs_dirent
|
||||
* @sd: sysfs_dirent of interest
|
||||
*
|
||||
* Get dentry for @sd. Dentry is looked up if currently not
|
||||
* present. This function descends from the root looking up
|
||||
* dentry for each step.
|
||||
*
|
||||
* LOCKING:
|
||||
* mutex_lock(sysfs_rename_mutex)
|
||||
*
|
||||
* RETURNS:
|
||||
* Pointer to found dentry on success, ERR_PTR() value on error.
|
||||
*/
|
||||
struct dentry *sysfs_get_dentry(struct sysfs_dirent *sd)
|
||||
{
|
||||
struct dentry *dentry = dget(sysfs_sb->s_root);
|
||||
|
||||
while (dentry->d_fsdata != sd) {
|
||||
struct sysfs_dirent *cur;
|
||||
struct dentry *parent;
|
||||
|
||||
/* find the first ancestor which hasn't been looked up */
|
||||
cur = sd;
|
||||
while (cur->s_parent != dentry->d_fsdata)
|
||||
cur = cur->s_parent;
|
||||
|
||||
/* look it up */
|
||||
parent = dentry;
|
||||
mutex_lock(&parent->d_inode->i_mutex);
|
||||
dentry = lookup_one_noperm(cur->s_name, parent);
|
||||
mutex_unlock(&parent->d_inode->i_mutex);
|
||||
dput(parent);
|
||||
|
||||
if (IS_ERR(dentry))
|
||||
break;
|
||||
}
|
||||
return dentry;
|
||||
}
|
||||
|
||||
/**
|
||||
* sysfs_get_active - get an active reference to sysfs_dirent
|
||||
* @sd: sysfs_dirent to get an active reference to
|
||||
@ -298,7 +257,61 @@ void release_sysfs_dirent(struct sysfs_dirent * sd)
|
||||
goto repeat;
|
||||
}
|
||||
|
||||
static void sysfs_d_iput(struct dentry * dentry, struct inode * inode)
|
||||
static int sysfs_dentry_delete(struct dentry *dentry)
|
||||
{
|
||||
struct sysfs_dirent *sd = dentry->d_fsdata;
|
||||
return !!(sd->s_flags & SYSFS_FLAG_REMOVED);
|
||||
}
|
||||
|
||||
static int sysfs_dentry_revalidate(struct dentry *dentry, struct nameidata *nd)
|
||||
{
|
||||
struct sysfs_dirent *sd = dentry->d_fsdata;
|
||||
int is_dir;
|
||||
|
||||
mutex_lock(&sysfs_mutex);
|
||||
|
||||
/* The sysfs dirent has been deleted */
|
||||
if (sd->s_flags & SYSFS_FLAG_REMOVED)
|
||||
goto out_bad;
|
||||
|
||||
/* The sysfs dirent has been moved? */
|
||||
if (dentry->d_parent->d_fsdata != sd->s_parent)
|
||||
goto out_bad;
|
||||
|
||||
/* The sysfs dirent has been renamed */
|
||||
if (strcmp(dentry->d_name.name, sd->s_name) != 0)
|
||||
goto out_bad;
|
||||
|
||||
mutex_unlock(&sysfs_mutex);
|
||||
out_valid:
|
||||
return 1;
|
||||
out_bad:
|
||||
/* Remove the dentry from the dcache hashes.
|
||||
* If this is a deleted dentry we use d_drop instead of d_delete
|
||||
* so sysfs doesn't need to cope with negative dentries.
|
||||
*
|
||||
* If this is a dentry that has simply been renamed we
|
||||
* use d_drop to remove it from the dcache lookup on its
|
||||
* old parent. If this dentry persists later when a lookup
|
||||
* is performed at its new name the dentry will be readded
|
||||
* to the dcache hashes.
|
||||
*/
|
||||
is_dir = (sysfs_type(sd) == SYSFS_DIR);
|
||||
mutex_unlock(&sysfs_mutex);
|
||||
if (is_dir) {
|
||||
/* If we have submounts we must allow the vfs caches
|
||||
* to lie about the state of the filesystem to prevent
|
||||
* leaks and other nasty things.
|
||||
*/
|
||||
if (have_submounts(dentry))
|
||||
goto out_valid;
|
||||
shrink_dcache_parent(dentry);
|
||||
}
|
||||
d_drop(dentry);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sysfs_dentry_iput(struct dentry *dentry, struct inode *inode)
|
||||
{
|
||||
struct sysfs_dirent * sd = dentry->d_fsdata;
|
||||
|
||||
@ -307,7 +320,9 @@ static void sysfs_d_iput(struct dentry * dentry, struct inode * inode)
|
||||
}
|
||||
|
||||
static const struct dentry_operations sysfs_dentry_ops = {
|
||||
.d_iput = sysfs_d_iput,
|
||||
.d_revalidate = sysfs_dentry_revalidate,
|
||||
.d_delete = sysfs_dentry_delete,
|
||||
.d_iput = sysfs_dentry_iput,
|
||||
};
|
||||
|
||||
struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, int type)
|
||||
@ -344,12 +359,6 @@ struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, int type)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int sysfs_ilookup_test(struct inode *inode, void *arg)
|
||||
{
|
||||
struct sysfs_dirent *sd = arg;
|
||||
return inode->i_ino == sd->s_ino;
|
||||
}
|
||||
|
||||
/**
|
||||
* sysfs_addrm_start - prepare for sysfs_dirent add/remove
|
||||
* @acxt: pointer to sysfs_addrm_cxt to be used
|
||||
@ -357,47 +366,20 @@ static int sysfs_ilookup_test(struct inode *inode, void *arg)
|
||||
*
|
||||
* This function is called when the caller is about to add or
|
||||
* remove sysfs_dirent under @parent_sd. This function acquires
|
||||
* sysfs_mutex, grabs inode for @parent_sd if available and lock
|
||||
* i_mutex of it. @acxt is used to keep and pass context to
|
||||
* sysfs_mutex. @acxt is used to keep and pass context to
|
||||
* other addrm functions.
|
||||
*
|
||||
* LOCKING:
|
||||
* Kernel thread context (may sleep). sysfs_mutex is locked on
|
||||
* return. i_mutex of parent inode is locked on return if
|
||||
* available.
|
||||
* return.
|
||||
*/
|
||||
void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt,
|
||||
struct sysfs_dirent *parent_sd)
|
||||
{
|
||||
struct inode *inode;
|
||||
|
||||
memset(acxt, 0, sizeof(*acxt));
|
||||
acxt->parent_sd = parent_sd;
|
||||
|
||||
/* Lookup parent inode. inode initialization is protected by
|
||||
* sysfs_mutex, so inode existence can be determined by
|
||||
* looking up inode while holding sysfs_mutex.
|
||||
*/
|
||||
mutex_lock(&sysfs_mutex);
|
||||
|
||||
inode = ilookup5(sysfs_sb, parent_sd->s_ino, sysfs_ilookup_test,
|
||||
parent_sd);
|
||||
if (inode) {
|
||||
WARN_ON(inode->i_state & I_NEW);
|
||||
|
||||
/* parent inode available */
|
||||
acxt->parent_inode = inode;
|
||||
|
||||
/* sysfs_mutex is below i_mutex in lock hierarchy.
|
||||
* First, trylock i_mutex. If fails, unlock
|
||||
* sysfs_mutex and lock them in order.
|
||||
*/
|
||||
if (!mutex_trylock(&inode->i_mutex)) {
|
||||
mutex_unlock(&sysfs_mutex);
|
||||
mutex_lock(&inode->i_mutex);
|
||||
mutex_lock(&sysfs_mutex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -422,18 +404,22 @@ void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt,
|
||||
*/
|
||||
int __sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)
|
||||
{
|
||||
struct sysfs_inode_attrs *ps_iattr;
|
||||
|
||||
if (sysfs_find_dirent(acxt->parent_sd, sd->s_name))
|
||||
return -EEXIST;
|
||||
|
||||
sd->s_parent = sysfs_get(acxt->parent_sd);
|
||||
|
||||
if (sysfs_type(sd) == SYSFS_DIR && acxt->parent_inode)
|
||||
inc_nlink(acxt->parent_inode);
|
||||
|
||||
acxt->cnt++;
|
||||
|
||||
sysfs_link_sibling(sd);
|
||||
|
||||
/* Update timestamps on the parent */
|
||||
ps_iattr = acxt->parent_sd->s_iattr;
|
||||
if (ps_iattr) {
|
||||
struct iattr *ps_iattrs = &ps_iattr->ia_iattr;
|
||||
ps_iattrs->ia_ctime = ps_iattrs->ia_mtime = CURRENT_TIME;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -512,70 +498,22 @@ int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)
|
||||
*/
|
||||
void sysfs_remove_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)
|
||||
{
|
||||
struct sysfs_inode_attrs *ps_iattr;
|
||||
|
||||
BUG_ON(sd->s_flags & SYSFS_FLAG_REMOVED);
|
||||
|
||||
sysfs_unlink_sibling(sd);
|
||||
|
||||
/* Update timestamps on the parent */
|
||||
ps_iattr = acxt->parent_sd->s_iattr;
|
||||
if (ps_iattr) {
|
||||
struct iattr *ps_iattrs = &ps_iattr->ia_iattr;
|
||||
ps_iattrs->ia_ctime = ps_iattrs->ia_mtime = CURRENT_TIME;
|
||||
}
|
||||
|
||||
sd->s_flags |= SYSFS_FLAG_REMOVED;
|
||||
sd->s_sibling = acxt->removed;
|
||||
acxt->removed = sd;
|
||||
|
||||
if (sysfs_type(sd) == SYSFS_DIR && acxt->parent_inode)
|
||||
drop_nlink(acxt->parent_inode);
|
||||
|
||||
acxt->cnt++;
|
||||
}
|
||||
|
||||
/**
|
||||
* sysfs_drop_dentry - drop dentry for the specified sysfs_dirent
|
||||
* @sd: target sysfs_dirent
|
||||
*
|
||||
* Drop dentry for @sd. @sd must have been unlinked from its
|
||||
* parent on entry to this function such that it can't be looked
|
||||
* up anymore.
|
||||
*/
|
||||
static void sysfs_drop_dentry(struct sysfs_dirent *sd)
|
||||
{
|
||||
struct inode *inode;
|
||||
struct dentry *dentry;
|
||||
|
||||
inode = ilookup(sysfs_sb, sd->s_ino);
|
||||
if (!inode)
|
||||
return;
|
||||
|
||||
/* Drop any existing dentries associated with sd.
|
||||
*
|
||||
* For the dentry to be properly freed we need to grab a
|
||||
* reference to the dentry under the dcache lock, unhash it,
|
||||
* and then put it. The playing with the dentry count allows
|
||||
* dput to immediately free the dentry if it is not in use.
|
||||
*/
|
||||
repeat:
|
||||
spin_lock(&dcache_lock);
|
||||
list_for_each_entry(dentry, &inode->i_dentry, d_alias) {
|
||||
if (d_unhashed(dentry))
|
||||
continue;
|
||||
dget_locked(dentry);
|
||||
spin_lock(&dentry->d_lock);
|
||||
__d_drop(dentry);
|
||||
spin_unlock(&dentry->d_lock);
|
||||
spin_unlock(&dcache_lock);
|
||||
dput(dentry);
|
||||
goto repeat;
|
||||
}
|
||||
spin_unlock(&dcache_lock);
|
||||
|
||||
/* adjust nlink and update timestamp */
|
||||
mutex_lock(&inode->i_mutex);
|
||||
|
||||
inode->i_ctime = CURRENT_TIME;
|
||||
drop_nlink(inode);
|
||||
if (sysfs_type(sd) == SYSFS_DIR)
|
||||
drop_nlink(inode);
|
||||
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
|
||||
iput(inode);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -584,25 +522,15 @@ repeat:
|
||||
*
|
||||
* Finish up sysfs_dirent add/remove. Resources acquired by
|
||||
* sysfs_addrm_start() are released and removed sysfs_dirents are
|
||||
* cleaned up. Timestamps on the parent inode are updated.
|
||||
* cleaned up.
|
||||
*
|
||||
* LOCKING:
|
||||
* All mutexes acquired by sysfs_addrm_start() are released.
|
||||
* sysfs_mutex is released.
|
||||
*/
|
||||
void sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt)
|
||||
{
|
||||
/* release resources acquired by sysfs_addrm_start() */
|
||||
mutex_unlock(&sysfs_mutex);
|
||||
if (acxt->parent_inode) {
|
||||
struct inode *inode = acxt->parent_inode;
|
||||
|
||||
/* if added/removed, update timestamps on the parent */
|
||||
if (acxt->cnt)
|
||||
inode->i_ctime = inode->i_mtime = CURRENT_TIME;
|
||||
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
iput(inode);
|
||||
}
|
||||
|
||||
/* kill removed sysfs_dirents */
|
||||
while (acxt->removed) {
|
||||
@ -611,7 +539,6 @@ void sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt)
|
||||
acxt->removed = sd->s_sibling;
|
||||
sd->s_sibling = NULL;
|
||||
|
||||
sysfs_drop_dentry(sd);
|
||||
sysfs_deactivate(sd);
|
||||
unmap_bin_file(sd);
|
||||
sysfs_put(sd);
|
||||
@ -751,10 +678,15 @@ static struct dentry * sysfs_lookup(struct inode *dir, struct dentry *dentry,
|
||||
}
|
||||
|
||||
/* instantiate and hash dentry */
|
||||
dentry->d_op = &sysfs_dentry_ops;
|
||||
dentry->d_fsdata = sysfs_get(sd);
|
||||
d_instantiate(dentry, inode);
|
||||
d_rehash(dentry);
|
||||
ret = d_find_alias(inode);
|
||||
if (!ret) {
|
||||
dentry->d_op = &sysfs_dentry_ops;
|
||||
dentry->d_fsdata = sysfs_get(sd);
|
||||
d_add(dentry, inode);
|
||||
} else {
|
||||
d_move(ret, dentry);
|
||||
iput(inode);
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&sysfs_mutex);
|
||||
@ -763,7 +695,9 @@ static struct dentry * sysfs_lookup(struct inode *dir, struct dentry *dentry,
|
||||
|
||||
const struct inode_operations sysfs_dir_inode_operations = {
|
||||
.lookup = sysfs_lookup,
|
||||
.permission = sysfs_permission,
|
||||
.setattr = sysfs_setattr,
|
||||
.getattr = sysfs_getattr,
|
||||
.setxattr = sysfs_setxattr,
|
||||
};
|
||||
|
||||
@ -826,141 +760,65 @@ void sysfs_remove_dir(struct kobject * kobj)
|
||||
__sysfs_remove_dir(sd);
|
||||
}
|
||||
|
||||
int sysfs_rename_dir(struct kobject * kobj, const char *new_name)
|
||||
int sysfs_rename(struct sysfs_dirent *sd,
|
||||
struct sysfs_dirent *new_parent_sd, const char *new_name)
|
||||
{
|
||||
struct sysfs_dirent *sd = kobj->sd;
|
||||
struct dentry *parent = NULL;
|
||||
struct dentry *old_dentry = NULL, *new_dentry = NULL;
|
||||
const char *dup_name = NULL;
|
||||
int error;
|
||||
|
||||
mutex_lock(&sysfs_rename_mutex);
|
||||
|
||||
error = 0;
|
||||
if (strcmp(sd->s_name, new_name) == 0)
|
||||
goto out; /* nothing to rename */
|
||||
|
||||
/* get the original dentry */
|
||||
old_dentry = sysfs_get_dentry(sd);
|
||||
if (IS_ERR(old_dentry)) {
|
||||
error = PTR_ERR(old_dentry);
|
||||
old_dentry = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
parent = old_dentry->d_parent;
|
||||
|
||||
/* lock parent and get dentry for new name */
|
||||
mutex_lock(&parent->d_inode->i_mutex);
|
||||
mutex_lock(&sysfs_mutex);
|
||||
|
||||
error = -EEXIST;
|
||||
if (sysfs_find_dirent(sd->s_parent, new_name))
|
||||
goto out_unlock;
|
||||
error = 0;
|
||||
if ((sd->s_parent == new_parent_sd) &&
|
||||
(strcmp(sd->s_name, new_name) == 0))
|
||||
goto out; /* nothing to rename */
|
||||
|
||||
error = -ENOMEM;
|
||||
new_dentry = d_alloc_name(parent, new_name);
|
||||
if (!new_dentry)
|
||||
goto out_unlock;
|
||||
error = -EEXIST;
|
||||
if (sysfs_find_dirent(new_parent_sd, new_name))
|
||||
goto out;
|
||||
|
||||
/* rename sysfs_dirent */
|
||||
error = -ENOMEM;
|
||||
new_name = dup_name = kstrdup(new_name, GFP_KERNEL);
|
||||
if (!new_name)
|
||||
goto out_unlock;
|
||||
if (strcmp(sd->s_name, new_name) != 0) {
|
||||
error = -ENOMEM;
|
||||
new_name = dup_name = kstrdup(new_name, GFP_KERNEL);
|
||||
if (!new_name)
|
||||
goto out;
|
||||
|
||||
dup_name = sd->s_name;
|
||||
sd->s_name = new_name;
|
||||
dup_name = sd->s_name;
|
||||
sd->s_name = new_name;
|
||||
}
|
||||
|
||||
/* rename */
|
||||
d_add(new_dentry, NULL);
|
||||
d_move(old_dentry, new_dentry);
|
||||
/* Remove from old parent's list and insert into new parent's list. */
|
||||
if (sd->s_parent != new_parent_sd) {
|
||||
sysfs_unlink_sibling(sd);
|
||||
sysfs_get(new_parent_sd);
|
||||
sysfs_put(sd->s_parent);
|
||||
sd->s_parent = new_parent_sd;
|
||||
sysfs_link_sibling(sd);
|
||||
}
|
||||
|
||||
error = 0;
|
||||
out_unlock:
|
||||
mutex_unlock(&sysfs_mutex);
|
||||
mutex_unlock(&parent->d_inode->i_mutex);
|
||||
kfree(dup_name);
|
||||
dput(old_dentry);
|
||||
dput(new_dentry);
|
||||
out:
|
||||
mutex_unlock(&sysfs_rename_mutex);
|
||||
mutex_unlock(&sysfs_mutex);
|
||||
kfree(dup_name);
|
||||
return error;
|
||||
}
|
||||
|
||||
int sysfs_rename_dir(struct kobject *kobj, const char *new_name)
|
||||
{
|
||||
return sysfs_rename(kobj->sd, kobj->sd->s_parent, new_name);
|
||||
}
|
||||
|
||||
int sysfs_move_dir(struct kobject *kobj, struct kobject *new_parent_kobj)
|
||||
{
|
||||
struct sysfs_dirent *sd = kobj->sd;
|
||||
struct sysfs_dirent *new_parent_sd;
|
||||
struct dentry *old_parent, *new_parent = NULL;
|
||||
struct dentry *old_dentry = NULL, *new_dentry = NULL;
|
||||
int error;
|
||||
|
||||
mutex_lock(&sysfs_rename_mutex);
|
||||
BUG_ON(!sd->s_parent);
|
||||
new_parent_sd = (new_parent_kobj && new_parent_kobj->sd) ?
|
||||
new_parent_sd = new_parent_kobj && new_parent_kobj->sd ?
|
||||
new_parent_kobj->sd : &sysfs_root;
|
||||
|
||||
error = 0;
|
||||
if (sd->s_parent == new_parent_sd)
|
||||
goto out; /* nothing to move */
|
||||
|
||||
/* get dentries */
|
||||
old_dentry = sysfs_get_dentry(sd);
|
||||
if (IS_ERR(old_dentry)) {
|
||||
error = PTR_ERR(old_dentry);
|
||||
old_dentry = NULL;
|
||||
goto out;
|
||||
}
|
||||
old_parent = old_dentry->d_parent;
|
||||
|
||||
new_parent = sysfs_get_dentry(new_parent_sd);
|
||||
if (IS_ERR(new_parent)) {
|
||||
error = PTR_ERR(new_parent);
|
||||
new_parent = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
again:
|
||||
mutex_lock(&old_parent->d_inode->i_mutex);
|
||||
if (!mutex_trylock(&new_parent->d_inode->i_mutex)) {
|
||||
mutex_unlock(&old_parent->d_inode->i_mutex);
|
||||
goto again;
|
||||
}
|
||||
mutex_lock(&sysfs_mutex);
|
||||
|
||||
error = -EEXIST;
|
||||
if (sysfs_find_dirent(new_parent_sd, sd->s_name))
|
||||
goto out_unlock;
|
||||
|
||||
error = -ENOMEM;
|
||||
new_dentry = d_alloc_name(new_parent, sd->s_name);
|
||||
if (!new_dentry)
|
||||
goto out_unlock;
|
||||
|
||||
error = 0;
|
||||
d_add(new_dentry, NULL);
|
||||
d_move(old_dentry, new_dentry);
|
||||
|
||||
/* Remove from old parent's list and insert into new parent's list. */
|
||||
sysfs_unlink_sibling(sd);
|
||||
sysfs_get(new_parent_sd);
|
||||
drop_nlink(old_parent->d_inode);
|
||||
sysfs_put(sd->s_parent);
|
||||
sd->s_parent = new_parent_sd;
|
||||
inc_nlink(new_parent->d_inode);
|
||||
sysfs_link_sibling(sd);
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&sysfs_mutex);
|
||||
mutex_unlock(&new_parent->d_inode->i_mutex);
|
||||
mutex_unlock(&old_parent->d_inode->i_mutex);
|
||||
out:
|
||||
dput(new_parent);
|
||||
dput(old_dentry);
|
||||
dput(new_dentry);
|
||||
mutex_unlock(&sysfs_rename_mutex);
|
||||
return error;
|
||||
return sysfs_rename(sd, new_parent_sd, sd->s_name);
|
||||
}
|
||||
|
||||
/* Relationship between s_mode and the DT_xxx types */
|
||||
|
@ -579,46 +579,23 @@ EXPORT_SYMBOL_GPL(sysfs_add_file_to_group);
|
||||
*/
|
||||
int sysfs_chmod_file(struct kobject *kobj, struct attribute *attr, mode_t mode)
|
||||
{
|
||||
struct sysfs_dirent *victim_sd = NULL;
|
||||
struct dentry *victim = NULL;
|
||||
struct inode * inode;
|
||||
struct sysfs_dirent *sd;
|
||||
struct iattr newattrs;
|
||||
int rc;
|
||||
|
||||
mutex_lock(&sysfs_mutex);
|
||||
|
||||
rc = -ENOENT;
|
||||
victim_sd = sysfs_get_dirent(kobj->sd, attr->name);
|
||||
if (!victim_sd)
|
||||
sd = sysfs_find_dirent(kobj->sd, attr->name);
|
||||
if (!sd)
|
||||
goto out;
|
||||
|
||||
mutex_lock(&sysfs_rename_mutex);
|
||||
victim = sysfs_get_dentry(victim_sd);
|
||||
mutex_unlock(&sysfs_rename_mutex);
|
||||
if (IS_ERR(victim)) {
|
||||
rc = PTR_ERR(victim);
|
||||
victim = NULL;
|
||||
goto out;
|
||||
}
|
||||
newattrs.ia_mode = (mode & S_IALLUGO) | (sd->s_mode & ~S_IALLUGO);
|
||||
newattrs.ia_valid = ATTR_MODE;
|
||||
rc = sysfs_sd_setattr(sd, &newattrs);
|
||||
|
||||
inode = victim->d_inode;
|
||||
|
||||
mutex_lock(&inode->i_mutex);
|
||||
|
||||
newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
|
||||
newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
|
||||
newattrs.ia_ctime = current_fs_time(inode->i_sb);
|
||||
rc = sysfs_setattr(victim, &newattrs);
|
||||
|
||||
if (rc == 0) {
|
||||
fsnotify_change(victim, newattrs.ia_valid);
|
||||
mutex_lock(&sysfs_mutex);
|
||||
victim_sd->s_mode = newattrs.ia_mode;
|
||||
mutex_unlock(&sysfs_mutex);
|
||||
}
|
||||
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
out:
|
||||
dput(victim);
|
||||
sysfs_put(victim_sd);
|
||||
mutex_unlock(&sysfs_mutex);
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sysfs_chmod_file);
|
||||
|
176
fs/sysfs/inode.c
176
fs/sysfs/inode.c
@ -37,7 +37,9 @@ static struct backing_dev_info sysfs_backing_dev_info = {
|
||||
};
|
||||
|
||||
static const struct inode_operations sysfs_inode_operations ={
|
||||
.permission = sysfs_permission,
|
||||
.setattr = sysfs_setattr,
|
||||
.getattr = sysfs_getattr,
|
||||
.setxattr = sysfs_setxattr,
|
||||
};
|
||||
|
||||
@ -46,7 +48,7 @@ int __init sysfs_inode_init(void)
|
||||
return bdi_init(&sysfs_backing_dev_info);
|
||||
}
|
||||
|
||||
struct sysfs_inode_attrs *sysfs_init_inode_attrs(struct sysfs_dirent *sd)
|
||||
static struct sysfs_inode_attrs *sysfs_init_inode_attrs(struct sysfs_dirent *sd)
|
||||
{
|
||||
struct sysfs_inode_attrs *attrs;
|
||||
struct iattr *iattrs;
|
||||
@ -64,30 +66,15 @@ struct sysfs_inode_attrs *sysfs_init_inode_attrs(struct sysfs_dirent *sd)
|
||||
|
||||
return attrs;
|
||||
}
|
||||
int sysfs_setattr(struct dentry * dentry, struct iattr * iattr)
|
||||
|
||||
int sysfs_sd_setattr(struct sysfs_dirent *sd, struct iattr * iattr)
|
||||
{
|
||||
struct inode * inode = dentry->d_inode;
|
||||
struct sysfs_dirent * sd = dentry->d_fsdata;
|
||||
struct sysfs_inode_attrs *sd_attrs;
|
||||
struct iattr *iattrs;
|
||||
unsigned int ia_valid = iattr->ia_valid;
|
||||
int error;
|
||||
|
||||
if (!sd)
|
||||
return -EINVAL;
|
||||
|
||||
sd_attrs = sd->s_iattr;
|
||||
|
||||
error = inode_change_ok(inode, iattr);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
iattr->ia_valid &= ~ATTR_SIZE; /* ignore size changes */
|
||||
|
||||
error = inode_setattr(inode, iattr);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (!sd_attrs) {
|
||||
/* setting attributes for the first time, allocate now */
|
||||
sd_attrs = sysfs_init_inode_attrs(sd);
|
||||
@ -103,42 +90,78 @@ int sysfs_setattr(struct dentry * dentry, struct iattr * iattr)
|
||||
if (ia_valid & ATTR_GID)
|
||||
iattrs->ia_gid = iattr->ia_gid;
|
||||
if (ia_valid & ATTR_ATIME)
|
||||
iattrs->ia_atime = timespec_trunc(iattr->ia_atime,
|
||||
inode->i_sb->s_time_gran);
|
||||
iattrs->ia_atime = iattr->ia_atime;
|
||||
if (ia_valid & ATTR_MTIME)
|
||||
iattrs->ia_mtime = timespec_trunc(iattr->ia_mtime,
|
||||
inode->i_sb->s_time_gran);
|
||||
iattrs->ia_mtime = iattr->ia_mtime;
|
||||
if (ia_valid & ATTR_CTIME)
|
||||
iattrs->ia_ctime = timespec_trunc(iattr->ia_ctime,
|
||||
inode->i_sb->s_time_gran);
|
||||
iattrs->ia_ctime = iattr->ia_ctime;
|
||||
if (ia_valid & ATTR_MODE) {
|
||||
umode_t mode = iattr->ia_mode;
|
||||
|
||||
if (!in_group_p(inode->i_gid) && !capable(CAP_FSETID))
|
||||
mode &= ~S_ISGID;
|
||||
iattrs->ia_mode = sd->s_mode = mode;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sysfs_setattr(struct dentry *dentry, struct iattr *iattr)
|
||||
{
|
||||
struct inode *inode = dentry->d_inode;
|
||||
struct sysfs_dirent *sd = dentry->d_fsdata;
|
||||
int error;
|
||||
|
||||
if (!sd)
|
||||
return -EINVAL;
|
||||
|
||||
error = inode_change_ok(inode, iattr);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
iattr->ia_valid &= ~ATTR_SIZE; /* ignore size changes */
|
||||
|
||||
error = inode_setattr(inode, iattr);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
mutex_lock(&sysfs_mutex);
|
||||
error = sysfs_sd_setattr(sd, iattr);
|
||||
mutex_unlock(&sysfs_mutex);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int sysfs_sd_setsecdata(struct sysfs_dirent *sd, void **secdata, u32 *secdata_len)
|
||||
{
|
||||
struct sysfs_inode_attrs *iattrs;
|
||||
void *old_secdata;
|
||||
size_t old_secdata_len;
|
||||
|
||||
iattrs = sd->s_iattr;
|
||||
if (!iattrs)
|
||||
iattrs = sysfs_init_inode_attrs(sd);
|
||||
if (!iattrs)
|
||||
return -ENOMEM;
|
||||
|
||||
old_secdata = iattrs->ia_secdata;
|
||||
old_secdata_len = iattrs->ia_secdata_len;
|
||||
|
||||
iattrs->ia_secdata = *secdata;
|
||||
iattrs->ia_secdata_len = *secdata_len;
|
||||
|
||||
*secdata = old_secdata;
|
||||
*secdata_len = old_secdata_len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sysfs_setxattr(struct dentry *dentry, const char *name, const void *value,
|
||||
size_t size, int flags)
|
||||
{
|
||||
struct sysfs_dirent *sd = dentry->d_fsdata;
|
||||
struct sysfs_inode_attrs *iattrs;
|
||||
void *secdata;
|
||||
int error;
|
||||
u32 secdata_len = 0;
|
||||
|
||||
if (!sd)
|
||||
return -EINVAL;
|
||||
if (!sd->s_iattr)
|
||||
sd->s_iattr = sysfs_init_inode_attrs(sd);
|
||||
if (!sd->s_iattr)
|
||||
return -ENOMEM;
|
||||
|
||||
iattrs = sd->s_iattr;
|
||||
|
||||
if (!strncmp(name, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN)) {
|
||||
const char *suffix = name + XATTR_SECURITY_PREFIX_LEN;
|
||||
@ -150,12 +173,13 @@ int sysfs_setxattr(struct dentry *dentry, const char *name, const void *value,
|
||||
&secdata, &secdata_len);
|
||||
if (error)
|
||||
goto out;
|
||||
if (iattrs->ia_secdata)
|
||||
security_release_secctx(iattrs->ia_secdata,
|
||||
iattrs->ia_secdata_len);
|
||||
iattrs->ia_secdata = secdata;
|
||||
iattrs->ia_secdata_len = secdata_len;
|
||||
|
||||
mutex_lock(&sysfs_mutex);
|
||||
error = sysfs_sd_setsecdata(sd, &secdata, &secdata_len);
|
||||
mutex_unlock(&sysfs_mutex);
|
||||
|
||||
if (secdata)
|
||||
security_release_secctx(secdata, secdata_len);
|
||||
} else
|
||||
return -EINVAL;
|
||||
out:
|
||||
@ -170,7 +194,6 @@ static inline void set_default_inode_attr(struct inode * inode, mode_t mode)
|
||||
|
||||
static inline void set_inode_attr(struct inode * inode, struct iattr * iattr)
|
||||
{
|
||||
inode->i_mode = iattr->ia_mode;
|
||||
inode->i_uid = iattr->ia_uid;
|
||||
inode->i_gid = iattr->ia_gid;
|
||||
inode->i_atime = iattr->ia_atime;
|
||||
@ -178,17 +201,6 @@ static inline void set_inode_attr(struct inode * inode, struct iattr * iattr)
|
||||
inode->i_ctime = iattr->ia_ctime;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* sysfs has a different i_mutex lock order behavior for i_mutex than other
|
||||
* filesystems; sysfs i_mutex is called in many places with subsystem locks
|
||||
* held. At the same time, many of the VFS locking rules do not apply to
|
||||
* sysfs at all (cross directory rename for example). To untangle this mess
|
||||
* (which gives false positives in lockdep), we're giving sysfs inodes their
|
||||
* own class for i_mutex.
|
||||
*/
|
||||
static struct lock_class_key sysfs_inode_imutex_key;
|
||||
|
||||
static int sysfs_count_nlink(struct sysfs_dirent *sd)
|
||||
{
|
||||
struct sysfs_dirent *child;
|
||||
@ -201,38 +213,55 @@ static int sysfs_count_nlink(struct sysfs_dirent *sd)
|
||||
return nr + 2;
|
||||
}
|
||||
|
||||
static void sysfs_refresh_inode(struct sysfs_dirent *sd, struct inode *inode)
|
||||
{
|
||||
struct sysfs_inode_attrs *iattrs = sd->s_iattr;
|
||||
|
||||
inode->i_mode = sd->s_mode;
|
||||
if (iattrs) {
|
||||
/* sysfs_dirent has non-default attributes
|
||||
* get them from persistent copy in sysfs_dirent
|
||||
*/
|
||||
set_inode_attr(inode, &iattrs->ia_iattr);
|
||||
security_inode_notifysecctx(inode,
|
||||
iattrs->ia_secdata,
|
||||
iattrs->ia_secdata_len);
|
||||
}
|
||||
|
||||
if (sysfs_type(sd) == SYSFS_DIR)
|
||||
inode->i_nlink = sysfs_count_nlink(sd);
|
||||
}
|
||||
|
||||
int sysfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
|
||||
{
|
||||
struct sysfs_dirent *sd = dentry->d_fsdata;
|
||||
struct inode *inode = dentry->d_inode;
|
||||
|
||||
mutex_lock(&sysfs_mutex);
|
||||
sysfs_refresh_inode(sd, inode);
|
||||
mutex_unlock(&sysfs_mutex);
|
||||
|
||||
generic_fillattr(inode, stat);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode)
|
||||
{
|
||||
struct bin_attribute *bin_attr;
|
||||
struct sysfs_inode_attrs *iattrs;
|
||||
|
||||
inode->i_private = sysfs_get(sd);
|
||||
inode->i_mapping->a_ops = &sysfs_aops;
|
||||
inode->i_mapping->backing_dev_info = &sysfs_backing_dev_info;
|
||||
inode->i_op = &sysfs_inode_operations;
|
||||
inode->i_ino = sd->s_ino;
|
||||
lockdep_set_class(&inode->i_mutex, &sysfs_inode_imutex_key);
|
||||
|
||||
iattrs = sd->s_iattr;
|
||||
if (iattrs) {
|
||||
/* sysfs_dirent has non-default attributes
|
||||
* get them for the new inode from persistent copy
|
||||
* in sysfs_dirent
|
||||
*/
|
||||
set_inode_attr(inode, &iattrs->ia_iattr);
|
||||
if (iattrs->ia_secdata)
|
||||
security_inode_notifysecctx(inode,
|
||||
iattrs->ia_secdata,
|
||||
iattrs->ia_secdata_len);
|
||||
} else
|
||||
set_default_inode_attr(inode, sd->s_mode);
|
||||
set_default_inode_attr(inode, sd->s_mode);
|
||||
sysfs_refresh_inode(sd, inode);
|
||||
|
||||
/* initialize inode according to type */
|
||||
switch (sysfs_type(sd)) {
|
||||
case SYSFS_DIR:
|
||||
inode->i_op = &sysfs_dir_inode_operations;
|
||||
inode->i_fop = &sysfs_dir_operations;
|
||||
inode->i_nlink = sysfs_count_nlink(sd);
|
||||
break;
|
||||
case SYSFS_KOBJ_ATTR:
|
||||
inode->i_size = PAGE_SIZE;
|
||||
@ -315,3 +344,14 @@ int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const char *name)
|
||||
else
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
int sysfs_permission(struct inode *inode, int mask)
|
||||
{
|
||||
struct sysfs_dirent *sd = inode->i_private;
|
||||
|
||||
mutex_lock(&sysfs_mutex);
|
||||
sysfs_refresh_inode(sd, inode);
|
||||
mutex_unlock(&sysfs_mutex);
|
||||
|
||||
return generic_permission(inode, mask, NULL);
|
||||
}
|
||||
|
@ -210,10 +210,13 @@ static void sysfs_put_link(struct dentry *dentry, struct nameidata *nd, void *co
|
||||
}
|
||||
|
||||
const struct inode_operations sysfs_symlink_inode_operations = {
|
||||
.setxattr = sysfs_setxattr,
|
||||
.readlink = generic_readlink,
|
||||
.follow_link = sysfs_follow_link,
|
||||
.put_link = sysfs_put_link,
|
||||
.setxattr = sysfs_setxattr,
|
||||
.readlink = generic_readlink,
|
||||
.follow_link = sysfs_follow_link,
|
||||
.put_link = sysfs_put_link,
|
||||
.setattr = sysfs_setattr,
|
||||
.getattr = sysfs_getattr,
|
||||
.permission = sysfs_permission,
|
||||
};
|
||||
|
||||
|
||||
|
@ -89,9 +89,7 @@ static inline unsigned int sysfs_type(struct sysfs_dirent *sd)
|
||||
*/
|
||||
struct sysfs_addrm_cxt {
|
||||
struct sysfs_dirent *parent_sd;
|
||||
struct inode *parent_inode;
|
||||
struct sysfs_dirent *removed;
|
||||
int cnt;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -105,7 +103,6 @@ extern struct kmem_cache *sysfs_dir_cachep;
|
||||
* dir.c
|
||||
*/
|
||||
extern struct mutex sysfs_mutex;
|
||||
extern struct mutex sysfs_rename_mutex;
|
||||
extern spinlock_t sysfs_assoc_lock;
|
||||
|
||||
extern const struct file_operations sysfs_dir_operations;
|
||||
@ -133,6 +130,9 @@ int sysfs_create_subdir(struct kobject *kobj, const char *name,
|
||||
struct sysfs_dirent **p_sd);
|
||||
void sysfs_remove_subdir(struct sysfs_dirent *sd);
|
||||
|
||||
int sysfs_rename(struct sysfs_dirent *sd,
|
||||
struct sysfs_dirent *new_parent_sd, const char *new_name);
|
||||
|
||||
static inline struct sysfs_dirent *__sysfs_get(struct sysfs_dirent *sd)
|
||||
{
|
||||
if (sd) {
|
||||
@ -155,7 +155,10 @@ static inline void __sysfs_put(struct sysfs_dirent *sd)
|
||||
*/
|
||||
struct inode *sysfs_get_inode(struct sysfs_dirent *sd);
|
||||
void sysfs_delete_inode(struct inode *inode);
|
||||
int sysfs_sd_setattr(struct sysfs_dirent *sd, struct iattr *iattr);
|
||||
int sysfs_permission(struct inode *inode, int mask);
|
||||
int sysfs_setattr(struct dentry *dentry, struct iattr *iattr);
|
||||
int sysfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat);
|
||||
int sysfs_setxattr(struct dentry *dentry, const char *name, const void *value,
|
||||
size_t size, int flags);
|
||||
int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const char *name);
|
||||
|
@ -558,7 +558,7 @@ extern void wait_for_device_probe(void);
|
||||
#ifdef CONFIG_DEVTMPFS
|
||||
extern int devtmpfs_create_node(struct device *dev);
|
||||
extern int devtmpfs_delete_node(struct device *dev);
|
||||
extern int devtmpfs_mount(const char *mountpoint);
|
||||
extern int devtmpfs_mount(const char *mntdir);
|
||||
#else
|
||||
static inline int devtmpfs_create_node(struct device *dev) { return 0; }
|
||||
static inline int devtmpfs_delete_node(struct device *dev) { return 0; }
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/gfp.h>
|
||||
|
||||
#define FW_ACTION_NOHOTPLUG 0
|
||||
#define FW_ACTION_HOTPLUG 1
|
||||
@ -38,7 +39,7 @@ int request_firmware(const struct firmware **fw, const char *name,
|
||||
struct device *device);
|
||||
int request_firmware_nowait(
|
||||
struct module *module, int uevent,
|
||||
const char *name, struct device *device, void *context,
|
||||
const char *name, struct device *device, gfp_t gfp, void *context,
|
||||
void (*cont)(const struct firmware *fw, void *context));
|
||||
|
||||
void release_firmware(const struct firmware *fw);
|
||||
@ -51,7 +52,7 @@ static inline int request_firmware(const struct firmware **fw,
|
||||
}
|
||||
static inline int request_firmware_nowait(
|
||||
struct module *module, int uevent,
|
||||
const char *name, struct device *device, void *context,
|
||||
const char *name, struct device *device, gfp_t gfp, void *context,
|
||||
void (*cont)(const struct firmware *fw, void *context))
|
||||
{
|
||||
return -EINVAL;
|
||||
|
@ -76,7 +76,6 @@ extern struct file *nameidata_to_filp(struct nameidata *nd, int flags);
|
||||
extern void release_open_intent(struct nameidata *);
|
||||
|
||||
extern struct dentry *lookup_one_len(const char *, struct dentry *, int);
|
||||
extern struct dentry *lookup_one_noperm(const char *, struct dentry *);
|
||||
|
||||
extern int follow_down(struct path *);
|
||||
extern int follow_up(struct path *);
|
||||
|
@ -83,6 +83,8 @@ struct early_platform_driver {
|
||||
struct platform_driver *pdrv;
|
||||
struct list_head list;
|
||||
int requested_id;
|
||||
char *buffer;
|
||||
int bufsize;
|
||||
};
|
||||
|
||||
#define EARLY_PLATFORM_ID_UNSET -2
|
||||
@ -102,21 +104,29 @@ extern int early_platform_driver_probe(char *class_str,
|
||||
int nr_probe, int user_only);
|
||||
extern void early_platform_cleanup(void);
|
||||
|
||||
#define early_platform_init(class_string, platdrv) \
|
||||
early_platform_init_buffer(class_string, platdrv, NULL, 0)
|
||||
|
||||
#ifndef MODULE
|
||||
#define early_platform_init(class_string, platform_driver) \
|
||||
#define early_platform_init_buffer(class_string, platdrv, buf, bufsiz) \
|
||||
static __initdata struct early_platform_driver early_driver = { \
|
||||
.class_str = class_string, \
|
||||
.pdrv = platform_driver, \
|
||||
.buffer = buf, \
|
||||
.bufsize = bufsiz, \
|
||||
.pdrv = platdrv, \
|
||||
.requested_id = EARLY_PLATFORM_ID_UNSET, \
|
||||
}; \
|
||||
static int __init early_platform_driver_setup_func(char *buf) \
|
||||
static int __init early_platform_driver_setup_func(char *buffer) \
|
||||
{ \
|
||||
return early_platform_driver_register(&early_driver, buf); \
|
||||
return early_platform_driver_register(&early_driver, buffer); \
|
||||
} \
|
||||
early_param(class_string, early_platform_driver_setup_func)
|
||||
#else /* MODULE */
|
||||
#define early_platform_init(class_string, platform_driver)
|
||||
#define early_platform_init_buffer(class_string, platdrv, buf, bufsiz) \
|
||||
static inline char *early_platform_driver_setup_func(void) \
|
||||
{ \
|
||||
return bufsiz ? buf : NULL; \
|
||||
}
|
||||
#endif /* MODULE */
|
||||
|
||||
#endif /* _PLATFORM_DEVICE_H_ */
|
||||
|
Loading…
Reference in New Issue
Block a user