2009-12-11 21:27:30

by Greg KH

[permalink] [raw]
Subject: [GIT PATCH] driver core patches for 2.6.33-git

Here is the big driver core merge for the 2.6.33-git tree.

Nothing major here:
- sysfs reworks to make things nicer and fix minor problems
- firmware api changes
- devtmpfs tweaks to resolve minor issues reported by users.
- platform devices early buffer changes
- debugfs bugfix

Please pull from:
master.kernel.org:/pub/scm/linux/kernel/git/gregkh/driver-core-2.6.git/

All of these patches have been in the linux-next and mm trees for a
while.

The patches will be sent as a follow-on to this message to lkml for people
to see.

thanks,

greg k-h

------------


drivers/base/core.c | 14 +-
drivers/base/devtmpfs.c | 100 ++++----
drivers/base/firmware_class.c | 14 +-
drivers/base/platform.c | 29 ++-
drivers/firmware/dell_rbu.c | 9 +-
drivers/misc/hpilo.h | 13 +-
drivers/serial/ucc_uart.c | 8 +-
drivers/staging/comedi/drivers/usbdux.c | 5 +-
drivers/staging/comedi/drivers/usbduxfast.c | 5 +-
drivers/usb/atm/ueagle-atm.c | 7 +-
fs/debugfs/inode.c | 55 +++--
fs/namei.c | 22 --
fs/sysfs/dir.c | 388 +++++++++------------------
fs/sysfs/file.c | 41 +---
fs/sysfs/inode.c | 176 ++++++++-----
fs/sysfs/symlink.c | 11 +-
fs/sysfs/sysfs.h | 9 +-
include/linux/device.h | 2 +-
include/linux/firmware.h | 5 +-
include/linux/namei.h | 1 -
include/linux/platform_device.h | 20 +-
21 files changed, 428 insertions(+), 506 deletions(-)

---------------

Alan Stern (1):
Driver core: fix race in dev_driver_string

David Altobelli (1):
hpilo: add locking comment

Eric W. Biederman (15):
Driver core: Don't remove kobjects in device_shutdown.
sysfs: Update sysfs_setxattr so it updates secdata under the sysfs_mutex
sysfs: Rename sysfs_d_iput to sysfs_dentry_iput
sysfs: Use dentry_ops instead of directly playing with the dcache
sysfs: Simplify sysfs_chmod_file semantics
sysfs: Simplify iattr time assignments
sysfs: Fix locking and factor out sysfs_sd_setattr
sysfs: Update s_iattr on link and unlink.
sysfs: Nicely indent sysfs_symlink_inode_operations
sysfs: Implement sysfs_getattr & sysfs_permission
sysfs: In sysfs_chmod_file lazily propagate the mode change.
sysfs: Gut sysfs_addrm_start and sysfs_addrm_finish
sysfs: Propagate renames to the vfs on demand
sysfs: Factor out sysfs_rename from sysfs_rename_dir and sysfs_move_dir
sysfs: sysfs_setattr remove unnecessary permission check.

Johannes Berg (1):
firmware_class: make request_firmware_nowait more useful

Kay Sievers (6):
Driver Core: devtmpfs: ignore umask while setting file mode
Driver core: devtmpfs: prevent concurrent subdirectory creation and removal
Driver Core: devtmpfs: use sys_mount()
Driver Core: devtmpfs: do not remove non-kernel-created directories
Driver Core: devtmpfs: cleanup node on device creation error
Driver-Core: devtmpfs - set root directory mode to 0755

Magnus Damm (1):
Driver Core: Early platform driver buffer

Mathieu Desnoyers (1):
debugfs: fix create mutex racy fops and private data

Stefan Richter (1):
sysfs: mark a locally-only used function static


2009-12-11 22:24:55

by Greg KH

[permalink] [raw]
Subject: [PATCH 01/27] hpilo: add locking comment

From: David Altobelli <[email protected]>

Add explanation about lock nesting and purpose of each lock in hpilo.

Signed-off-by: David Altobelli <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/misc/hpilo.h | 13 ++++++++++++-
1 files changed, 12 insertions(+), 1 deletions(-)

diff --git a/drivers/misc/hpilo.h b/drivers/misc/hpilo.h
index 3857605..247eb38 100644
--- a/drivers/misc/hpilo.h
+++ b/drivers/misc/hpilo.h
@@ -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;
};
--
1.6.5.5

2009-12-11 22:31:25

by Greg KH

[permalink] [raw]
Subject: [PATCH 02/27] sysfs: mark a locally-only used function static

From: Stefan Richter <[email protected]>

Signed-off-by: Stefan Richter <[email protected]>
Acked-by: David P. Quigley <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
fs/sysfs/inode.c | 3 ++-
1 files changed, 2 insertions(+), 1 deletions(-)

diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c
index e28cecf..e144351 100644
--- a/fs/sysfs/inode.c
+++ b/fs/sysfs/inode.c
@@ -46,7 +46,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,6 +64,7 @@ struct sysfs_inode_attrs *sysfs_init_inode_attrs(struct sysfs_dirent *sd)

return attrs;
}
+
int sysfs_setattr(struct dentry * dentry, struct iattr * iattr)
{
struct inode * inode = dentry->d_inode;
--
1.6.5.5

2009-12-11 22:25:17

by Greg KH

[permalink] [raw]
Subject: [PATCH 03/27] Driver Core: devtmpfs: ignore umask while setting file mode

From: Kay Sievers <[email protected]>

Signed-off-by: Kay Sievers <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/base/devtmpfs.c | 26 ++++++++++++++++----------
1 files changed, 16 insertions(+), 10 deletions(-)

diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c
index a1cb5af..48526b9 100644
--- a/drivers/base/devtmpfs.c
+++ b/drivers/base/devtmpfs.c
@@ -156,34 +156,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);
--
1.6.5.5

2009-12-11 22:25:13

by Greg KH

[permalink] [raw]
Subject: [PATCH 04/27] Driver core: devtmpfs: prevent concurrent subdirectory creation and removal

From: Kay Sievers <[email protected]>

Signed-off-by: Kay Sievers <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/base/devtmpfs.c | 26 ++++++++++++++++----------
1 files changed, 16 insertions(+), 10 deletions(-)

diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c
index 48526b9..1cf498f 100644
--- a/drivers/base/devtmpfs.c
+++ b/drivers/base/devtmpfs.c
@@ -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);
@@ -86,16 +88,12 @@ static int dev_mkdir(const char *name, mode_t mode)

static int create_path(const char *nodepath)
{
- char *path;
struct nameidata nd;
int err = 0;

- path = kstrdup(nodepath, GFP_KERNEL);
- if (!path)
- return -ENOMEM;
-
+ read_lock(&dirlock);
err = vfs_path_lookup(dev_mnt->mnt_root, dev_mnt,
- path, LOOKUP_PARENT, &nd);
+ nodepath, LOOKUP_PARENT, &nd);
if (err == 0) {
struct dentry *dentry;

@@ -107,14 +105,17 @@ static int create_path(const char *nodepath)
dput(dentry);
}
mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
-
path_put(&nd.path);
} else 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 +126,10 @@ static int create_path(const char *nodepath)
s[0] = '/';
s++;
}
+ kfree(path);
}
+ read_unlock(&dirlock);

- kfree(path);
return err;
}

@@ -234,7 +236,8 @@ static int delete_path(const char *nodepath)
if (!path)
return -ENOMEM;

- while (1) {
+ write_lock(&dirlock);
+ for (;;) {
char *base;

base = strrchr(path, '/');
@@ -245,6 +248,7 @@ static int delete_path(const char *nodepath)
if (err)
break;
}
+ write_unlock(&dirlock);

kfree(path);
return err;
@@ -360,6 +364,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 "
--
1.6.5.5

2009-12-11 22:25:06

by Greg KH

[permalink] [raw]
Subject: [PATCH 05/27] Driver Core: devtmpfs: use sys_mount()

From: Kay Sievers <[email protected]>

Signed-off-by: Kay Sievers <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/base/devtmpfs.c | 9 ++-------
include/linux/device.h | 2 +-
2 files changed, 3 insertions(+), 8 deletions(-)

diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c
index 1cf498f..880a203 100644
--- a/drivers/base/devtmpfs.c
+++ b/drivers/base/devtmpfs.c
@@ -332,9 +332,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)
@@ -343,15 +342,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;
}

diff --git a/include/linux/device.h b/include/linux/device.h
index 2ea3e49..2a73d9b 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -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; }
--
1.6.5.5

2009-12-11 22:25:09

by Greg KH

[permalink] [raw]
Subject: [PATCH 06/27] Driver Core: devtmpfs: do not remove non-kernel-created directories

From: Kay Sievers <[email protected]>

Signed-off-by: Kay Sievers <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/base/devtmpfs.c | 39 ++++++++++++++++-----------------------
1 files changed, 16 insertions(+), 23 deletions(-)

diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c
index 880a203..c7f5c08 100644
--- a/drivers/base/devtmpfs.c
+++ b/drivers/base/devtmpfs.c
@@ -76,37 +76,26 @@ 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)
{
- struct nameidata nd;
- int err = 0;
+ int err;

read_lock(&dirlock);
- err = vfs_path_lookup(dev_mnt->mnt_root, dev_mnt,
- nodepath, 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) {
+ err = dev_mkdir(nodepath, 0755);
+ if (err == -ENOENT) {
char *path;
char *s;

@@ -129,7 +118,6 @@ static int create_path(const char *nodepath)
kfree(path);
}
read_unlock(&dirlock);
-
return err;
}

@@ -213,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;
}
--
1.6.5.5

2009-12-11 22:33:58

by Greg KH

[permalink] [raw]
Subject: [PATCH 07/27] Driver Core: devtmpfs: cleanup node on device creation error

From: Kay Sievers <[email protected]>

Signed-off-by: Kay Sievers <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/base/core.c | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)

diff --git a/drivers/base/core.c b/drivers/base/core.c
index 6bee6af..0d3c29d 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -987,6 +987,8 @@ done:
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:
if (MAJOR(dev->devt))
--
1.6.5.5

2009-12-11 22:32:23

by Greg KH

[permalink] [raw]
Subject: [PATCH 08/27] Driver-Core: devtmpfs - set root directory mode to 0755

From: Kay Sievers <[email protected]>

Signed-off-by: Kay Sievers <[email protected]>
Cc: Mark Rosenstand <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/base/devtmpfs.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c
index c7f5c08..50375bb 100644
--- a/drivers/base/devtmpfs.c
+++ b/drivers/base/devtmpfs.c
@@ -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);
--
1.6.5.5

2009-12-11 22:25:21

by Greg KH

[permalink] [raw]
Subject: [PATCH 09/27] firmware_class: make request_firmware_nowait more useful

From: Johannes Berg <[email protected]>

Unfortunately, one cannot hold on to the struct firmware
that request_firmware_nowait() hands off, which is needed
in some cases. Allow this by requiring the callback to
free it (via release_firmware).

Additionally, give it a gfp_t parameter -- all the current
users call it from a GFP_KERNEL context so the GFP_ATOMIC
isn't necessary. This also marks an API break which is
useful in a sense, although that is obviously not the
primary purpose of this change.

Signed-off-by: Johannes Berg <[email protected]>
Acked-by: Marcel Holtmann <[email protected]>
Cc: Ming Lei <[email protected]>
Cc: Catalin Marinas <[email protected]>
Cc: David Woodhouse <[email protected]>
Cc: Pavel Roskin <[email protected]>
Cc: Abhay Salunke <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/base/firmware_class.c | 14 ++++++--------
drivers/firmware/dell_rbu.c | 9 +++++++--
drivers/serial/ucc_uart.c | 8 +++++---
drivers/staging/comedi/drivers/usbdux.c | 5 ++++-
drivers/staging/comedi/drivers/usbduxfast.c | 5 ++++-
drivers/usb/atm/ueagle-atm.c | 7 ++++---
include/linux/firmware.h | 5 +++--
7 files changed, 33 insertions(+), 20 deletions(-)

diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
index 7376367..a950241 100644
--- a/drivers/base/firmware_class.c
+++ b/drivers/base/firmware_class.c
@@ -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;
diff --git a/drivers/firmware/dell_rbu.c b/drivers/firmware/dell_rbu.c
index b4704e1..b3a0cf5 100644
--- a/drivers/firmware/dell_rbu.c
+++ b/drivers/firmware/dell_rbu.c
@@ -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
diff --git a/drivers/serial/ucc_uart.c b/drivers/serial/ucc_uart.c
index 46de564..465f2fa 100644
--- a/drivers/serial/ucc_uart.c
+++ b/drivers/serial/ucc_uart.c
@@ -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",
diff --git a/drivers/staging/comedi/drivers/usbdux.c b/drivers/staging/comedi/drivers/usbdux.c
index cca4e86..dfcd12b 100644
--- a/drivers/staging/comedi/drivers/usbdux.c
+++ b/drivers/staging/comedi/drivers/usbdux.c
@@ -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);

diff --git a/drivers/staging/comedi/drivers/usbduxfast.c b/drivers/staging/comedi/drivers/usbduxfast.c
index d143222..2e675cc 100644
--- a/drivers/staging/comedi/drivers/usbduxfast.c
+++ b/drivers/staging/comedi/drivers/usbduxfast.c
@@ -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);

diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c
index bba4d3e..c539524 100644
--- a/drivers/usb/atm/ueagle-atm.c
+++ b/drivers/usb/atm/ueagle-atm.c
@@ -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
diff --git a/include/linux/firmware.h b/include/linux/firmware.h
index d315446..043811f 100644
--- a/include/linux/firmware.h
+++ b/include/linux/firmware.h
@@ -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;
--
1.6.5.5

2009-12-11 22:31:09

by Greg KH

[permalink] [raw]
Subject: [PATCH 10/27] Driver core: Don't remove kobjects in device_shutdown.

From: Eric W. Biederman <[email protected]>

device_shutdown is defined to just shutdown the hardware and to not
clean up any kernel data structures. Therefore don't put the kobjects
for /sys/dev and /sys/dev/block and /sys/dev/char.

This ensures we don't remove /sys/dev/block and /sys/dev/char while
we still have symlinks from there to the actual devices.

Acked-by: Kay Sievers <[email protected]>
Signed-off-by: Eric W. Biederman <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/base/core.c | 3 ---
1 files changed, 0 insertions(+), 3 deletions(-)

diff --git a/drivers/base/core.c b/drivers/base/core.c
index 0d3c29d..353b137 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -1730,8 +1730,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();
}
--
1.6.5.5

2009-12-11 22:25:29

by Greg KH

[permalink] [raw]
Subject: [PATCH 11/27] debugfs: fix create mutex racy fops and private data

From: Mathieu Desnoyers <[email protected]>

Setting fops and private data outside of the mutex at debugfs file
creation introduces a race where the files can be opened with the wrong
file operations and private data. It is easy to trigger with a process
waiting on file creation notification.

Signed-off-by: Mathieu Desnoyers <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Cc: stable <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
fs/debugfs/inode.c | 55 ++++++++++++++++++++++++++++++---------------------
1 files changed, 32 insertions(+), 23 deletions(-)

diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c
index 0d23b52..b486169 100644
--- a/fs/debugfs/inode.c
+++ b/fs/debugfs/inode.c
@@ -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;
}
--
1.6.5.5

2009-12-11 22:34:13

by Greg KH

[permalink] [raw]
Subject: [PATCH 12/27] sysfs: Update sysfs_setxattr so it updates secdata under the sysfs_mutex

From: Eric W. Biederman <[email protected]>

The sysfs_mutex is required to ensure updates are and will remain
atomic with respect to other inode iattr updates, that do not happen
through the filesystem.

Acked-by: Serge Hallyn <[email protected]>
Acked-by: Tejun Heo <[email protected]>
Signed-off-by: Eric W. Biederman <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
fs/sysfs/inode.c | 41 +++++++++++++++++++++++++++++------------
1 files changed, 29 insertions(+), 12 deletions(-)

diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c
index e144351..000bd98 100644
--- a/fs/sysfs/inode.c
+++ b/fs/sysfs/inode.c
@@ -123,23 +123,39 @@ int sysfs_setattr(struct dentry * dentry, struct iattr * iattr)
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;
@@ -151,12 +167,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:
--
1.6.5.5

2009-12-11 22:34:18

by Greg KH

[permalink] [raw]
Subject: [PATCH 13/27] sysfs: Rename sysfs_d_iput to sysfs_dentry_iput

From: Eric W. Biederman <[email protected]>

Using dentry instead of d in the function name is what
several other filesystems are doing and it seems to be
a more readable convention.

Acked-by: Tejun Heo <[email protected]>
Acked-by: Serge Hallyn <[email protected]>
Signed-off-by: Eric W. Biederman <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
fs/sysfs/dir.c | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c
index e020183..01a9a64 100644
--- a/fs/sysfs/dir.c
+++ b/fs/sysfs/dir.c
@@ -298,7 +298,7 @@ void release_sysfs_dirent(struct sysfs_dirent * sd)
goto repeat;
}

-static void sysfs_d_iput(struct dentry * dentry, struct inode * inode)
+static void sysfs_dentry_iput(struct dentry *dentry, struct inode *inode)
{
struct sysfs_dirent * sd = dentry->d_fsdata;

@@ -307,7 +307,7 @@ 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_iput = sysfs_dentry_iput,
};

struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, int type)
--
1.6.5.5

2009-12-11 22:34:43

by Greg KH

[permalink] [raw]
Subject: [PATCH 14/27] sysfs: Use dentry_ops instead of directly playing with the dcache

From: Eric W. Biederman <[email protected]>

Calling d_drop unconditionally when a sysfs_dirent is deleted has
the potential to leak mounts, so instead implement dentry delete
and revalidate operations that cause sysfs dentries to be removed
at the appropriate time.

Acked-by: Tejun Heo <[email protected]>
Acked-by: Serge Hallyn <[email protected]>
Signed-off-by: Eric W. Biederman <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
fs/sysfs/dir.c | 73 +++++++++++++++++++++++++++++++++++--------------------
1 files changed, 46 insertions(+), 27 deletions(-)

diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c
index 01a9a64..9ee130b 100644
--- a/fs/sysfs/dir.c
+++ b/fs/sysfs/dir.c
@@ -298,6 +298,46 @@ void release_sysfs_dirent(struct sysfs_dirent * sd)
goto repeat;
}

+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;
+
+ 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.
+ */
+ 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,6 +347,8 @@ static void sysfs_dentry_iput(struct dentry *dentry, struct inode *inode)
}

static const struct dentry_operations sysfs_dentry_ops = {
+ .d_revalidate = sysfs_dentry_revalidate,
+ .d_delete = sysfs_dentry_delete,
.d_iput = sysfs_dentry_iput,
};

@@ -527,44 +569,21 @@ void sysfs_remove_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)
}

/**
- * sysfs_drop_dentry - drop dentry for the specified sysfs_dirent
+ * sysfs_dec_nlink - Decrement link count for the specified sysfs_dirent
* @sd: target sysfs_dirent
*
- * Drop dentry for @sd. @sd must have been unlinked from its
+ * Decrement nlink 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)
+static void sysfs_dec_nlink(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);

@@ -611,7 +630,7 @@ void sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt)
acxt->removed = sd->s_sibling;
sd->s_sibling = NULL;

- sysfs_drop_dentry(sd);
+ sysfs_dec_nlink(sd);
sysfs_deactivate(sd);
unmap_bin_file(sd);
sysfs_put(sd);
--
1.6.5.5

2009-12-11 22:33:50

by Greg KH

[permalink] [raw]
Subject: [PATCH 15/27] sysfs: Simplify sysfs_chmod_file semantics

From: Eric W. Biederman <[email protected]>

Currently every caller of sysfs_chmod_file happens at either
file creation time to set a non-default mode or in response
to a specific user requested space change in policy. Making
timestamps of when the chmod happens and notification of
a file changing mode uninteresting.

Remove the unnecessary time stamp and filesystem change
notification, and removes the last of the explicit inotify
and donitfy support from sysfs.

Acked-by: Tejun Heo <[email protected]>
Acked-by: Serge Hallyn <[email protected]>
Signed-off-by: Eric W. Biederman <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
fs/sysfs/file.c | 10 +---------
1 files changed, 1 insertions(+), 9 deletions(-)

diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c
index f5ea468..faa1a80 100644
--- a/fs/sysfs/file.c
+++ b/fs/sysfs/file.c
@@ -604,17 +604,9 @@ int sysfs_chmod_file(struct kobject *kobj, struct attribute *attr, mode_t mode)
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);
+ newattrs.ia_valid = ATTR_MODE;
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);
--
1.6.5.5

2009-12-11 22:30:41

by Greg KH

[permalink] [raw]
Subject: [PATCH 16/27] sysfs: Simplify iattr time assignments

From: Eric W. Biederman <[email protected]>

The granularity of sysfs time when we keep it is 1 ns. Which
when passed to timestamp_trunc results in a nop. So remove
the unnecessary function call making sysfs_setattr slightly
easier to read.

Acked-by: Tejun Heo <[email protected]>
Acked-by: Serge Hallyn <[email protected]>
Signed-off-by: Eric W. Biederman <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
fs/sysfs/inode.c | 9 +++------
1 files changed, 3 insertions(+), 6 deletions(-)

diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c
index 000bd98..86fb230 100644
--- a/fs/sysfs/inode.c
+++ b/fs/sysfs/inode.c
@@ -104,14 +104,11 @@ 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;

--
1.6.5.5

2009-12-11 22:25:33

by Greg KH

[permalink] [raw]
Subject: [PATCH 17/27] sysfs: Fix locking and factor out sysfs_sd_setattr

From: Eric W. Biederman <[email protected]>

Cleanly separate the work that is specific to setting the
attributes of a sysfs_dirent from what is needed to update
the attributes of a vfs inode.

Additionally grab the sysfs_mutex to keep any nasties from
surprising us when updating the sysfs_dirent.

Acked-by: Tejun Heo <[email protected]>
Signed-off-by: Eric W. Biederman <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
fs/sysfs/inode.c | 51 +++++++++++++++++++++++++++++++--------------------
fs/sysfs/sysfs.h | 1 +
2 files changed, 32 insertions(+), 20 deletions(-)

diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c
index 86fb230..76e977c 100644
--- a/fs/sysfs/inode.c
+++ b/fs/sysfs/inode.c
@@ -65,30 +65,14 @@ static 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);
@@ -111,12 +95,39 @@ int sysfs_setattr(struct dentry * dentry, struct iattr * iattr)
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 */
+ if (iattr->ia_valid & ATTR_MODE) {
+ if (!in_group_p(inode->i_gid) && !capable(CAP_FSETID))
+ iattr->ia_mode &= ~S_ISGID;
+ }
+
+ 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;
}

diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h
index af4c4e7..a96d967 100644
--- a/fs/sysfs/sysfs.h
+++ b/fs/sysfs/sysfs.h
@@ -155,6 +155,7 @@ 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_setattr(struct dentry *dentry, struct iattr *iattr);
int sysfs_setxattr(struct dentry *dentry, const char *name, const void *value,
size_t size, int flags);
--
1.6.5.5

2009-12-11 22:33:32

by Greg KH

[permalink] [raw]
Subject: [PATCH 18/27] sysfs: Update s_iattr on link and unlink.

From: Eric W. Biederman <[email protected]>

Currently sysfs updates the timestamps on the vfs directory
inode when we create or remove a directory entry but doesn't
update the cached copy on the sysfs_dirent, fix that oversight.

Acked-by: Tejun Heo <[email protected]>
Acked-by: Serge Hallyn <[email protected]>
Signed-off-by: Eric W. Biederman <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
fs/sysfs/dir.c | 18 ++++++++++++++++++
1 files changed, 18 insertions(+), 0 deletions(-)

diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c
index 9ee130b..36ee6d8 100644
--- a/fs/sysfs/dir.c
+++ b/fs/sysfs/dir.c
@@ -464,6 +464,8 @@ 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;

@@ -476,6 +478,13 @@ int __sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)

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;
}

@@ -554,10 +563,19 @@ 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;
--
1.6.5.5

2009-12-11 22:33:12

by Greg KH

[permalink] [raw]
Subject: [PATCH 19/27] sysfs: Nicely indent sysfs_symlink_inode_operations

From: Eric W. Biederman <[email protected]>

Lining up the functions in sysfs_symlink_inode_operations
follows the pattern in the rest of sysfs and makes things
slightly more readable.

Acked-by: Tejun Heo <[email protected]>
Acked-by: Serge Hallyn <[email protected]>
Signed-off-by: Eric W. Biederman <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
fs/sysfs/symlink.c | 8 ++++----
1 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/fs/sysfs/symlink.c b/fs/sysfs/symlink.c
index c5081ad..1137418 100644
--- a/fs/sysfs/symlink.c
+++ b/fs/sysfs/symlink.c
@@ -210,10 +210,10 @@ 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,
};


--
1.6.5.5

2009-12-11 22:25:47

by Greg KH

[permalink] [raw]
Subject: [PATCH 20/27] sysfs: Implement sysfs_getattr & sysfs_permission

From: Eric W. Biederman <[email protected]>

With the implementation of sysfs_getattr and sysfs_permission
sysfs becomes able to lazily propogate inode attribute changes
from the sysfs_dirents to the vfs inodes. This paves the way
for deleting significant chunks of now unnecessary code.

While doing this we did not reference sysfs_setattr from
sysfs_symlink_inode_operations so I added along with
sysfs_getattr and sysfs_permission.

Acked-by: Tejun Heo <[email protected]>
Acked-by: Serge Hallyn <[email protected]>
Signed-off-by: Eric W. Biederman <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
fs/sysfs/dir.c | 2 +
fs/sysfs/inode.c | 64 ++++++++++++++++++++++++++++++++++++++-------------
fs/sysfs/symlink.c | 3 ++
fs/sysfs/sysfs.h | 2 +
4 files changed, 54 insertions(+), 17 deletions(-)

diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c
index 36ee6d8..e319379 100644
--- a/fs/sysfs/dir.c
+++ b/fs/sysfs/dir.c
@@ -800,7 +800,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,
};

diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c
index 76e977c..1ffd555 100644
--- a/fs/sysfs/inode.c
+++ b/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,
};

@@ -196,7 +198,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;
@@ -227,38 +228,56 @@ 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;
@@ -341,3 +360,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);
+}
diff --git a/fs/sysfs/symlink.c b/fs/sysfs/symlink.c
index 1137418..c5eff49 100644
--- a/fs/sysfs/symlink.c
+++ b/fs/sysfs/symlink.c
@@ -214,6 +214,9 @@ const struct inode_operations sysfs_symlink_inode_operations = {
.readlink = generic_readlink,
.follow_link = sysfs_follow_link,
.put_link = sysfs_put_link,
+ .setattr = sysfs_setattr,
+ .getattr = sysfs_getattr,
+ .permission = sysfs_permission,
};


diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h
index a96d967..12ccc07 100644
--- a/fs/sysfs/sysfs.h
+++ b/fs/sysfs/sysfs.h
@@ -156,7 +156,9 @@ 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);
--
1.6.5.5

2009-12-11 22:33:00

by Greg KH

[permalink] [raw]
Subject: [PATCH 21/27] sysfs: In sysfs_chmod_file lazily propagate the mode change.

From: Eric W. Biederman <[email protected]>

Now that sysfs_getattr and sysfs_permission refresh the vfs
inode there is no need to immediatly push the mode change
into the vfs cache. Reducing the amount of work needed and
simplifying the locking.

Acked-by: Tejun Heo <[email protected]>
Acked-by: Serge Hallyn <[email protected]>
Signed-off-by: Eric W. Biederman <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
fs/sysfs/file.c | 31 ++++++++-----------------------
1 files changed, 8 insertions(+), 23 deletions(-)

diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c
index faa1a80..dc30d9e 100644
--- a/fs/sysfs/file.c
+++ b/fs/sysfs/file.c
@@ -579,38 +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;

- rc = -ENOENT;
- victim_sd = sysfs_get_dirent(kobj->sd, attr->name);
- if (!victim_sd)
- goto out;
+ mutex_lock(&sysfs_mutex);

- 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;
+ rc = -ENOENT;
+ sd = sysfs_find_dirent(kobj->sd, attr->name);
+ if (!sd)
goto out;
- }
-
- inode = victim->d_inode;

- mutex_lock(&inode->i_mutex);
-
- newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
+ newattrs.ia_mode = (mode & S_IALLUGO) | (sd->s_mode & ~S_IALLUGO);
newattrs.ia_valid = ATTR_MODE;
- rc = sysfs_setattr(victim, &newattrs);
+ rc = sysfs_sd_setattr(sd, &newattrs);

- mutex_unlock(&inode->i_mutex);
out:
- dput(victim);
- sysfs_put(victim_sd);
+ mutex_unlock(&sysfs_mutex);
return rc;
}
EXPORT_SYMBOL_GPL(sysfs_chmod_file);
--
1.6.5.5

2009-12-11 22:25:42

by Greg KH

[permalink] [raw]
Subject: [PATCH 22/27] sysfs: Gut sysfs_addrm_start and sysfs_addrm_finish

From: Eric W. Biederman <[email protected]>

With lazy inode updates and dentry operations bringing everything
into sync on demand there is no longer any need to immediately
update the vfs or grab i_mutex to protect those updates as we
make changes to sysfs.

Acked-by: Serge Hallyn <[email protected]>
Acked-by: Tejun Heo <[email protected]>
Signed-off-by: Eric W. Biederman <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
fs/sysfs/dir.c | 91 ++---------------------------------------------------
fs/sysfs/sysfs.h | 2 -
2 files changed, 4 insertions(+), 89 deletions(-)

diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c
index e319379..f3af45e 100644
--- a/fs/sysfs/dir.c
+++ b/fs/sysfs/dir.c
@@ -386,12 +386,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
@@ -399,47 +393,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);
- }
- }
}

/**
@@ -471,11 +438,6 @@ int __sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)

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 */
@@ -579,40 +541,6 @@ void sysfs_remove_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)
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_dec_nlink - Decrement link count for the specified sysfs_dirent
- * @sd: target sysfs_dirent
- *
- * Decrement nlink 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_dec_nlink(struct sysfs_dirent *sd)
-{
- struct inode *inode;
-
- inode = ilookup(sysfs_sb, sd->s_ino);
- if (!inode)
- return;
-
- /* 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);
}

/**
@@ -621,25 +549,15 @@ static void sysfs_dec_nlink(struct sysfs_dirent *sd)
*
* 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) {
@@ -648,7 +566,6 @@ void sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt)
acxt->removed = sd->s_sibling;
sd->s_sibling = NULL;

- sysfs_dec_nlink(sd);
sysfs_deactivate(sd);
unmap_bin_file(sd);
sysfs_put(sd);
diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h
index 12ccc07..90b3501 100644
--- a/fs/sysfs/sysfs.h
+++ b/fs/sysfs/sysfs.h
@@ -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;
};

/*
--
1.6.5.5

2009-12-11 22:32:34

by Greg KH

[permalink] [raw]
Subject: [PATCH 23/27] sysfs: Propagate renames to the vfs on demand

From: Eric W. Biederman <[email protected]>

By teaching sysfs_revalidate to hide a dentry for
a sysfs_dirent if the sysfs_dirent has been renamed,
and by teaching sysfs_lookup to return the original
dentry if the sysfs dirent has been renamed. I can
show the results of renames correctly without having to
update the dcache during the directory rename.

This massively simplifies the rename logic allowing a lot
of weird sysfs special cases to be removed along with
a lot of now unnecesary helper code.

Acked-by: Tejun Heo <[email protected]>
Signed-off-by: Eric W. Biederman <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
fs/namei.c | 22 -------
fs/sysfs/dir.c | 158 ++++++++++---------------------------------------
fs/sysfs/inode.c | 12 ----
fs/sysfs/sysfs.h | 1 -
include/linux/namei.h | 1 -
5 files changed, 32 insertions(+), 162 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index d11f404..d3c190c 100644
--- a/fs/namei.c
+++ b/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)
{
diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c
index f3af45e..97954c6 100644
--- a/fs/sysfs/dir.c
+++ b/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);
@@ -85,46 +84,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
*
@@ -315,6 +274,14 @@ static int sysfs_dentry_revalidate(struct dentry *dentry, struct nameidata *nd)
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;
@@ -322,6 +289,12 @@ 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);
@@ -705,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);
@@ -785,62 +763,32 @@ void sysfs_remove_dir(struct kobject * kobj)
int sysfs_rename_dir(struct kobject * kobj, 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);
+ mutex_lock(&sysfs_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 = -ENOMEM;
- new_dentry = d_alloc_name(parent, new_name);
- if (!new_dentry)
- goto out_unlock;
+ goto out;

/* rename sysfs_dirent */
error = -ENOMEM;
new_name = dup_name = kstrdup(new_name, GFP_KERNEL);
if (!new_name)
- goto out_unlock;
+ goto out;

dup_name = sd->s_name;
sd->s_name = new_name;

- /* rename */
- d_add(new_dentry, NULL);
- d_move(old_dentry, new_dentry);
-
error = 0;
- out_unlock:
+ out:
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);
return error;
}

@@ -848,12 +796,11 @@ 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);
+
+ mutex_lock(&sysfs_mutex);
new_parent_sd = (new_parent_kobj && new_parent_kobj->sd) ?
new_parent_kobj->sd : &sysfs_root;

@@ -861,61 +808,20 @@ int sysfs_move_dir(struct kobject *kobj, struct kobject *new_parent_kobj)
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);
+ goto out;

/* 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:
+ error = 0;
+out:
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;
}

diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c
index 1ffd555..9f783d4 100644
--- a/fs/sysfs/inode.c
+++ b/fs/sysfs/inode.c
@@ -205,17 +205,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;
@@ -268,7 +257,6 @@ static void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode)
inode->i_mapping->a_ops = &sysfs_aops;
inode->i_mapping->backing_dev_info = &sysfs_backing_dev_info;
inode->i_op = &sysfs_inode_operations;
- lockdep_set_class(&inode->i_mutex, &sysfs_inode_imutex_key);

set_default_inode_attr(inode, sd->s_mode);
sysfs_refresh_inode(sd, inode);
diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h
index 90b3501..98a15bf 100644
--- a/fs/sysfs/sysfs.h
+++ b/fs/sysfs/sysfs.h
@@ -103,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;
diff --git a/include/linux/namei.h b/include/linux/namei.h
index ec0f607..0289467 100644
--- a/include/linux/namei.h
+++ b/include/linux/namei.h
@@ -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 *);
--
1.6.5.5

2009-12-11 22:26:53

by Greg KH

[permalink] [raw]
Subject: [PATCH 24/27] sysfs: Factor out sysfs_rename from sysfs_rename_dir and sysfs_move_dir

From: Eric W. Biederman <[email protected]>

These two functions do 90% of the same work and it doesn't significantly
obfuscate the function to allow both the parent dir and the name to change
at the same time. So merge them together to simplify maintenance, and
increase testing.

Acked-by: Tejun Heo <[email protected]>
Acked-by: Serge Hallyn <[email protected]>
Signed-off-by: Eric W. Biederman <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
fs/sysfs/dir.c | 62 +++++++++++++++++++++++++----------------------------
fs/sysfs/sysfs.h | 3 ++
2 files changed, 32 insertions(+), 33 deletions(-)

diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c
index 97954c6..f05f230 100644
--- a/fs/sysfs/dir.c
+++ b/fs/sysfs/dir.c
@@ -760,30 +760,42 @@ 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;
const char *dup_name = NULL;
int error;

mutex_lock(&sysfs_mutex);

error = 0;
- if (strcmp(sd->s_name, new_name) == 0)
+ if ((sd->s_parent == new_parent_sd) &&
+ (strcmp(sd->s_name, new_name) == 0))
goto out; /* nothing to rename */

error = -EEXIST;
- if (sysfs_find_dirent(sd->s_parent, new_name))
+ 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;
+ 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;
+ /* 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:
@@ -792,37 +804,21 @@ int sysfs_rename_dir(struct kobject * kobj, const char *new_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;
- int error;

BUG_ON(!sd->s_parent);
-
- mutex_lock(&sysfs_mutex);
- 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 */
-
- error = -EEXIST;
- if (sysfs_find_dirent(new_parent_sd, sd->s_name))
- goto out;
-
- /* Remove from old parent's list and insert into new parent's list. */
- 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:
- mutex_unlock(&sysfs_mutex);
- return error;
+ return sysfs_rename(sd, new_parent_sd, sd->s_name);
}

/* Relationship between s_mode and the DT_xxx types */
diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h
index 98a15bf..ca52e7b 100644
--- a/fs/sysfs/sysfs.h
+++ b/fs/sysfs/sysfs.h
@@ -130,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) {
--
1.6.5.5

2009-12-11 22:32:05

by Greg KH

[permalink] [raw]
Subject: [PATCH 25/27] sysfs: sysfs_setattr remove unnecessary permission check.

From: Eric W. Biederman <[email protected]>

inode_change_ok already clears the SGID bit when necessary
so there is no reason for sysfs_setattr to carry code to do
the same, and it is good to kill the extra copy because when
I moved the code last in certain corner cases the code will
look at the wrong gid.

Acked-by: Serge Hallyn <[email protected]>
Acked-by: Tejun Heo <[email protected]>
Signed-off-by: Eric W. Biederman <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
fs/sysfs/inode.c | 4 ----
1 files changed, 0 insertions(+), 4 deletions(-)

diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c
index 9f783d4..220b758 100644
--- a/fs/sysfs/inode.c
+++ b/fs/sysfs/inode.c
@@ -117,10 +117,6 @@ int sysfs_setattr(struct dentry *dentry, struct iattr *iattr)
return error;

iattr->ia_valid &= ~ATTR_SIZE; /* ignore size changes */
- if (iattr->ia_valid & ATTR_MODE) {
- if (!in_group_p(inode->i_gid) && !capable(CAP_FSETID))
- iattr->ia_mode &= ~S_ISGID;
- }

error = inode_setattr(inode, iattr);
if (error)
--
1.6.5.5

2009-12-11 22:31:37

by Greg KH

[permalink] [raw]
Subject: [PATCH 26/27] Driver Core: Early platform driver buffer

From: Magnus Damm <[email protected]>

Add early_platform_init_buffer() support and update the
early platform driver code to allow passing parameters
to the driver on the kernel command line.

early_platform_init_buffer() simply allows early platform
drivers to provide a pointer and length to a memory area
where the remaining part of the kernel command line option
will be stored.

Needed to pass baud rate and other serial port options
to the reworked early serial console code on SuperH.

Signed-off-by: Magnus Damm <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/base/platform.c | 29 ++++++++++++++++++++++-------
include/linux/platform_device.h | 20 +++++++++++++++-----
2 files changed, 37 insertions(+), 12 deletions(-)

diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index 4fa954b..9d2ee25 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -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;
diff --git a/include/linux/platform_device.h b/include/linux/platform_device.h
index 3c6675c..71ff887 100644
--- a/include/linux/platform_device.h
+++ b/include/linux/platform_device.h
@@ -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_ */
--
1.6.5.5

2009-12-11 22:26:01

by Greg KH

[permalink] [raw]
Subject: [PATCH 27/27] Driver core: fix race in dev_driver_string

From: Alan Stern <[email protected]>

This patch (as1310) works around a race in dev_driver_string(). If
the device is unbound while the function is running, dev->driver might
become NULL after we test it and before we dereference it.

Signed-off-by: Alan Stern <[email protected]>
Cc: stable <[email protected]>
Cc: Oliver Neukum <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/base/core.c | 9 ++++++++-
1 files changed, 8 insertions(+), 1 deletions(-)

diff --git a/drivers/base/core.c b/drivers/base/core.c
index 353b137..f1290cb 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -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 : ""));
}
--
1.6.5.5

2009-12-21 13:37:51

by Kirill A. Shutemov

[permalink] [raw]
Subject: Re: [PATCH 04/27] Driver core: devtmpfs: prevent concurrent subdirectory creation and removal

v2.6.33-rc1-96-gdd59f6c:

...
[ 0.868406] sched: BUG: sleeping function called from invalid
context at kernel/mutex.c:94
[ 0.868635] sched: in_atomic(): 1, irqs_disabled(): 0, pid: 642,
name: modprobe
[ 0.868858] Pid: 642, comm: modprobe Not tainted 2.6.33-rc1 #39
[ 0.868998] Call Trace:
[ 0.869157] [<c1024f88>] __might_sleep+0xef/0xf7
[ 0.869313] [<c129b6a3>] mutex_lock+0x1f/0x39
[ 0.869469] [<c10ca984>] do_lookup+0x87/0x139
[ 0.869615] [<c10cb02f>] link_path_walk+0x22b/0x520
[ 0.869755] [<c10cb435>] path_walk+0x3f/0x89
[ 0.869895] [<c10cb608>] vfs_path_lookup+0x53/0x99
[ 0.870038] [<c11e6a9b>] dev_mkdir.clone.0+0x23/0x8d
[ 0.870195] [<c10ca6ef>] ? mntput+0x18/0x1a
[ 0.870371] [<c10ca7d3>] ? path_put+0x1a/0x1d
[ 0.870522] [<c10cb63f>] ? vfs_path_lookup+0x8a/0x99
[ 0.870668] [<c129ee99>] ? add_preempt_count+0x8/0x75
[ 0.870808] [<c11e6bbc>] devtmpfs_create_node+0xb7/0x1f3
[ 0.870950] [<c10ffc78>] ? sysfs_find_dirent+0x1b/0x2c
[ 0.871091] [<c10fff50>] ? __sysfs_add_one+0x2a/0x78
[ 0.871249] [<c1100313>] ? sysfs_addrm_finish+0x1e/0x93
[ 0.871402] [<c11004d0>] ? sysfs_add_one+0x18/0xe1
[ 0.871542] [<c1100886>] ? sysfs_do_create_link+0xbc/0x109
[ 0.871683] [<c11008f3>] ? sysfs_create_link+0xf/0x14
[ 0.871823] [<c11e1e69>] device_add+0x141/0x44d
[ 0.871963] [<c1003935>] ? arch_irq_stat_cpu+0x5d/0xc8
[ 0.872137] [<c11e218c>] device_register+0x17/0x1a
[ 0.872290] [<c11e2206>] device_create_vargs+0x77/0x98
[ 0.872435] [<f8045000>] ? dm_init+0x0/0x3b [dm_mod]
[ 0.872574] [<c11e2247>] device_create+0x20/0x22
[ 0.872715] [<c11c96dc>] misc_register+0x9d/0xf1
[ 0.872859] [<f804521a>] dm_interface_init+0x36/0x74 [dm_mod]
[ 0.873003] [<f8045011>] dm_init+0x11/0x3b [dm_mod]
[ 0.873144] [<c1001143>] do_one_initcall+0x51/0x13f
[ 0.873311] [<c105c0db>] sys_init_module+0xac/0x1e0
[ 0.873456] [<c129c820>] syscall_call+0x7/0xb
....
[ 13.438106] sched: BUG: sleeping function called from invalid
context at kernel/mutex.c:94
[ 13.438110] sched: in_atomic(): 1, irqs_disabled(): 0, pid: 18387,
name: modprobe
[ 13.438114] Pid: 18387, comm: modprobe Not tainted 2.6.33-rc1 #39
[ 13.438116] Call Trace:
[ 13.438124] [<c1024f88>] __might_sleep+0xef/0xf7
[ 13.438129] [<c129b6a3>] mutex_lock+0x1f/0x39
[ 13.438133] [<c10ca984>] do_lookup+0x87/0x139
[ 13.438136] [<c10cb02f>] link_path_walk+0x22b/0x520
[ 13.438140] [<c10cb435>] path_walk+0x3f/0x89
[ 13.438143] [<c10cb608>] vfs_path_lookup+0x53/0x99
[ 13.438147] [<c11e6a9b>] dev_mkdir.clone.0+0x23/0x8d
[ 13.438150] [<c10ca6ef>] ? mntput+0x18/0x1a
[ 13.438153] [<c10ca7d3>] ? path_put+0x1a/0x1d
[ 13.438156] [<c10cb63f>] ? vfs_path_lookup+0x8a/0x99
[ 13.438159] [<c129ee99>] ? add_preempt_count+0x8/0x75
[ 13.438162] [<c11e6bbc>] devtmpfs_create_node+0xb7/0x1f3
[ 13.438166] [<c10ffc78>] ? sysfs_find_dirent+0x1b/0x2c
[ 13.438168] [<c10fff50>] ? __sysfs_add_one+0x2a/0x78
[ 13.438171] [<c1100313>] ? sysfs_addrm_finish+0x1e/0x93
[ 13.438173] [<c11004d0>] ? sysfs_add_one+0x18/0xe1
[ 13.438176] [<c1100886>] ? sysfs_do_create_link+0xbc/0x109
[ 13.438179] [<c11008f3>] ? sysfs_create_link+0xf/0x14
[ 13.438183] [<c11e1e69>] device_add+0x141/0x44d
[ 13.438187] [<c100323a>] ? do_stack_segment+0x33/0x5c
[ 13.438190] [<c11e218c>] device_register+0x17/0x1a
[ 13.438193] [<c11e2206>] device_create_vargs+0x77/0x98
[ 13.438196] [<c11e2247>] device_create+0x20/0x22
[ 13.438203] [<f9ea153a>] snd_register_device_for_dev+0xfa/0x158 [snd]
[ 13.438208] [<fa2150d5>] alsa_timer_init+0xd5/0x129 [snd_timer]
[ 13.438213] [<fa215000>] ? alsa_timer_init+0x0/0x129 [snd_timer]
[ 13.438216] [<c1001143>] do_one_initcall+0x51/0x13f
[ 13.438220] [<c105c0db>] sys_init_module+0xac/0x1e0
[ 13.438223] [<c129c820>] syscall_call+0x7/0xb
....
[ 56.108746] Call Trace:
[ 56.108754] [<c1024f88>] __might_sleep+0xef/0xf7
[ 56.108758] [<c129b6a3>] mutex_lock+0x1f/0x39
[ 56.108762] [<c10ca984>] do_lookup+0x87/0x139
[ 56.108765] [<c10cb02f>] link_path_walk+0x22b/0x520
[ 56.108768] [<c10cb435>] path_walk+0x3f/0x89
[ 56.108771] [<c10cb608>] vfs_path_lookup+0x53/0x99
[ 56.108775] [<c11e6a9b>] dev_mkdir.clone.0+0x23/0x8d
[ 56.108778] [<c10ca6ef>] ? mntput+0x18/0x1a
[ 56.108780] [<c10ca7d3>] ? path_put+0x1a/0x1d
[ 56.108783] [<c10cb63f>] ? vfs_path_lookup+0x8a/0x99
[ 56.108786] [<c129ee99>] ? add_preempt_count+0x8/0x75
[ 56.108789] [<c11e6bbc>] devtmpfs_create_node+0xb7/0x1f3
[ 56.108792] [<c10ffc78>] ? sysfs_find_dirent+0x1b/0x2c
[ 56.108795] [<c10fff50>] ? __sysfs_add_one+0x2a/0x78
[ 56.108797] [<c1100313>] ? sysfs_addrm_finish+0x1e/0x93
[ 56.108800] [<c11004d0>] ? sysfs_add_one+0x18/0xe1
[ 56.108803] [<c1100886>] ? sysfs_do_create_link+0xbc/0x109
[ 56.108805] [<c11008f3>] ? sysfs_create_link+0xf/0x14
[ 56.108809] [<c11e1e69>] device_add+0x141/0x44d
[ 56.108813] [<c100303a>] ? do_spurious_interrupt_bug+0xa/0x2a
[ 56.108816] [<c11e218c>] device_register+0x17/0x1a
[ 56.108827] [<f818d4e9>] drm_sysfs_device_add+0x71/0x94 [drm]
[ 56.108837] [<f818b6eb>] drm_get_minor+0x1a5/0x216 [drm]
[ 56.108846] [<f818ba1b>] drm_get_dev+0x2bf/0x3bc [drm]
[ 56.108850] [<c11721b8>] ? kobject_get+0x12/0x17
[ 56.108858] [<f81874ea>] drm_init+0x81/0xba [drm]
[ 56.108875] [<f81b9000>] ? radeon_init+0x0/0xae [radeon]
[ 56.108891] [<f81b90ac>] radeon_init+0xac/0xae [radeon]
[ 56.108893] [<c1001143>] do_one_initcall+0x51/0x13f
[ 56.108898] [<c105c0db>] sys_init_module+0xac/0x1e0
[ 56.108900] [<c129c820>] syscall_call+0x7/0xb


I guess it can be related to this commit.

Config attached.


On Sat, Dec 12, 2009 at 12:24 AM, Greg Kroah-Hartman <[email protected]> wrote:
> From: Kay Sievers <[email protected]>
>
> Signed-off-by: Kay Sievers <[email protected]>
> Signed-off-by: Greg Kroah-Hartman <[email protected]>
> ---
>  drivers/base/devtmpfs.c |   26 ++++++++++++++++----------
>  1 files changed, 16 insertions(+), 10 deletions(-)
>
> diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c
> index 48526b9..1cf498f 100644
> --- a/drivers/base/devtmpfs.c
> +++ b/drivers/base/devtmpfs.c
> @@ -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);
> @@ -86,16 +88,12 @@ static int dev_mkdir(const char *name, mode_t mode)
>
>  static int create_path(const char *nodepath)
>  {
> -       char *path;
>        struct nameidata nd;
>        int err = 0;
>
> -       path = kstrdup(nodepath, GFP_KERNEL);
> -       if (!path)
> -               return -ENOMEM;
> -
> +       read_lock(&dirlock);
>        err = vfs_path_lookup(dev_mnt->mnt_root, dev_mnt,
> -                             path, LOOKUP_PARENT, &nd);
> +                             nodepath, LOOKUP_PARENT, &nd);
>        if (err == 0) {
>                struct dentry *dentry;
>
> @@ -107,14 +105,17 @@ static int create_path(const char *nodepath)
>                        dput(dentry);
>                }
>                mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
> -
>                path_put(&nd.path);
>        } else 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 +126,10 @@ static int create_path(const char *nodepath)
>                        s[0] = '/';
>                        s++;
>                }
> +               kfree(path);
>        }
> +       read_unlock(&dirlock);
>
> -       kfree(path);
>        return err;
>  }
>
> @@ -234,7 +236,8 @@ static int delete_path(const char *nodepath)
>        if (!path)
>                return -ENOMEM;
>
> -       while (1) {
> +       write_lock(&dirlock);
> +       for (;;) {
>                char *base;
>
>                base = strrchr(path, '/');
> @@ -245,6 +248,7 @@ static int delete_path(const char *nodepath)
>                if (err)
>                        break;
>        }
> +       write_unlock(&dirlock);
>
>        kfree(path);
>        return err;
> @@ -360,6 +364,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 "
> --
> 1.6.5.5
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to [email protected]
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/
>


Attachments:
.config (68.38 kB)

2009-12-21 14:37:28

by Kay Sievers

[permalink] [raw]
Subject: Re: [PATCH 04/27] Driver core: devtmpfs: prevent concurrent subdirectory creation and removal

On Mon, Dec 21, 2009 at 14:37, Kirill A. Shutemov <[email protected]> wrote:
> v2.6.33-rc1-96-gdd59f6c:
> I guess it can be related to this commit.

The fix is already pending here:
http://patchwork.kernel.org/patch/68337/

Thanks,
Kay

2009-12-22 14:10:33

by Kirill A. Shutemov

[permalink] [raw]
Subject: Re: [PATCH 04/27] Driver core: devtmpfs: prevent concurrent subdirectory creation and removal

On Mon, Dec 21, 2009 at 4:37 PM, Kay Sievers <[email protected]> wrote:
> On Mon, Dec 21, 2009 at 14:37, Kirill A. Shutemov <[email protected]> wrote:
>> v2.6.33-rc1-96-gdd59f6c:
>> I guess it can be related to this commit.
>
> The fix is already pending here:
>  http://patchwork.kernel.org/patch/68337/

One more problem: you don't unlock dirlock if kstrdup() failed.

2009-12-22 16:58:33

by Greg KH

[permalink] [raw]
Subject: Re: [PATCH 04/27] Driver core: devtmpfs: prevent concurrent subdirectory creation and removal

On Tue, Dec 22, 2009 at 04:10:31PM +0200, Kirill A. Shutemov wrote:
> On Mon, Dec 21, 2009 at 4:37 PM, Kay Sievers <[email protected]> wrote:
> > On Mon, Dec 21, 2009 at 14:37, Kirill A. Shutemov <[email protected]> wrote:
> >> v2.6.33-rc1-96-gdd59f6c:
> >> I guess it can be related to this commit.
> >
> > The fix is already pending here:
> > ?http://patchwork.kernel.org/patch/68337/
>
> One more problem: you don't unlock dirlock if kstrdup() failed.

Care to provide a patch?

thanks,

greg k-h

2009-12-22 16:58:14

by Kay Sievers

[permalink] [raw]
Subject: Re: [PATCH 04/27] Driver core: devtmpfs: prevent concurrent subdirectory creation and removal

On Tue, Dec 22, 2009 at 15:10, Kirill A. Shutemov <[email protected]> wrote:
> On Mon, Dec 21, 2009 at 4:37 PM, Kay Sievers <[email protected]> wrote:
>> On Mon, Dec 21, 2009 at 14:37, Kirill A. Shutemov <[email protected]> wrote:
>>> v2.6.33-rc1-96-gdd59f6c:
>>> I guess it can be related to this commit.
>>
>> The fix is already pending here:
>>  http://patchwork.kernel.org/patch/68337/
>
> One more problem: you don't unlock dirlock if kstrdup() failed.

Oh right, will send a fix tomorrow.

Thanks,
Kay

2009-12-22 18:56:36

by Kirill A. Shutemov

[permalink] [raw]
Subject: Re: [PATCH 04/27] Driver core: devtmpfs: prevent concurrent subdirectory creation and removal

On Tue, Dec 22, 2009 at 6:57 PM, Kay Sievers <[email protected]> wrote:
> On Tue, Dec 22, 2009 at 15:10, Kirill A. Shutemov <[email protected]> wrote:
>> On Mon, Dec 21, 2009 at 4:37 PM, Kay Sievers <[email protected]> wrote:
>>> On Mon, Dec 21, 2009 at 14:37, Kirill A. Shutemov <[email protected]> wrote:
>>>> v2.6.33-rc1-96-gdd59f6c:
>>>> I guess it can be related to this commit.
>>>
>>> The fix is already pending here:
>>>  http://patchwork.kernel.org/patch/68337/
>>
>> One more problem: you don't unlock dirlock if kstrdup() failed.
>
> Oh right, will send a fix tomorrow.

Reported-by Kirill A. Shutemov <[email protected]>

2009-12-22 21:25:37

by Kay Sievers

[permalink] [raw]
Subject: Re: [PATCH 04/27] Driver core: devtmpfs: prevent concurrent subdirectory creation and removal

On Tue, 2009-12-22 at 20:56 +0200, Kirill A. Shutemov wrote:
> On Tue, Dec 22, 2009 at 6:57 PM, Kay Sievers <[email protected]> wrote:
> > On Tue, Dec 22, 2009 at 15:10, Kirill A. Shutemov <[email protected]> wrote:
> >> On Mon, Dec 21, 2009 at 4:37 PM, Kay Sievers <[email protected]> wrote:
> >>> On Mon, Dec 21, 2009 at 14:37, Kirill A. Shutemov <[email protected]> wrote:
> >>>> v2.6.33-rc1-96-gdd59f6c:
> >>>> I guess it can be related to this commit.
> >>>
> >>> The fix is already pending here:
> >>> http://patchwork.kernel.org/patch/68337/
> >>
> >> One more problem: you don't unlock dirlock if kstrdup() failed.
> >
> > Oh right, will send a fix tomorrow.
>
> Reported-by Kirill A. Shutemov <[email protected]>

Thanks!

Kay



From: Kay Sievers <[email protected]>
Subject: devtmpfs: unlock mutex in case of string allocation error

Reported-by: Kirill A. Shutemov <[email protected]>
Signed-off-by: Kay Sievers <[email protected]>
---
drivers/base/devtmpfs.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)

--- a/drivers/base/devtmpfs.c
+++ b/drivers/base/devtmpfs.c
@@ -101,8 +101,10 @@ static int create_path(const char *nodep

/* parent directories do not exist, create them */
path = kstrdup(nodepath, GFP_KERNEL);
- if (!path)
- return -ENOMEM;
+ if (!path) {
+ err = -ENOMEM;
+ goto out;
+ }
s = path;
for (;;) {
s = strchr(s, '/');
@@ -117,6 +119,7 @@ static int create_path(const char *nodep
}
kfree(path);
}
+out:
mutex_unlock(&dirlock);
return err;
}