diff options
author | Mark M. Hoffman <mhoffman@lightlink.com> | 2006-03-05 23:13:47 +0100 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2006-03-23 14:21:55 -0800 |
commit | ded2b66615613093eeb83b81499bc270de8fc499 (patch) | |
tree | 118f87f38a4b7a2c8d65f38beb85fa63da78fb4f /drivers/hwmon | |
parent | f6c27fc17c5e575c5471fb344bdbd5f5f6072136 (diff) |
[PATCH] hwmon: add required idr locking
Add required locking around idr_ routines, retry the idr_pre_get/idr_get_new
pair properly, and sprinkle in some likely/unlikely for good measure.
(Lack of idr locking didn't hurt when all callers were I2C clients, as the
i2c-core serialized for us anyway. Now that we have non I2C hwmon drivers,
this is truly necessary.)
Signed-off-by: Mark M. Hoffman <mhoffman@lightlink.com>
Signed-off-by: Jean Delvare <khali@linux-fr.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/hwmon')
-rw-r--r-- | drivers/hwmon/hwmon.c | 26 |
1 files changed, 20 insertions, 6 deletions
diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c index dddd3eb9b387..106fa01cdb60 100644 --- a/drivers/hwmon/hwmon.c +++ b/drivers/hwmon/hwmon.c @@ -17,6 +17,7 @@ #include <linux/idr.h> #include <linux/hwmon.h> #include <linux/gfp.h> +#include <linux/spinlock.h> #define HWMON_ID_PREFIX "hwmon" #define HWMON_ID_FORMAT HWMON_ID_PREFIX "%d" @@ -24,6 +25,7 @@ static struct class *hwmon_class; static DEFINE_IDR(hwmon_idr); +static DEFINE_SPINLOCK(idr_lock); /** * hwmon_device_register - register w/ hwmon sysfs class @@ -37,20 +39,30 @@ static DEFINE_IDR(hwmon_idr); struct class_device *hwmon_device_register(struct device *dev) { struct class_device *cdev; - int id; + int id, err; - if (idr_pre_get(&hwmon_idr, GFP_KERNEL) == 0) +again: + if (unlikely(idr_pre_get(&hwmon_idr, GFP_KERNEL) == 0)) return ERR_PTR(-ENOMEM); - if (idr_get_new(&hwmon_idr, NULL, &id) < 0) - return ERR_PTR(-ENOMEM); + spin_lock(&idr_lock); + err = idr_get_new(&hwmon_idr, NULL, &id); + spin_unlock(&idr_lock); + + if (unlikely(err == -EAGAIN)) + goto again; + else if (unlikely(err)) + return ERR_PTR(err); id = id & MAX_ID_MASK; cdev = class_device_create(hwmon_class, NULL, MKDEV(0,0), dev, HWMON_ID_FORMAT, id); - if (IS_ERR(cdev)) + if (IS_ERR(cdev)) { + spin_lock(&idr_lock); idr_remove(&hwmon_idr, id); + spin_unlock(&idr_lock); + } return cdev; } @@ -64,9 +76,11 @@ void hwmon_device_unregister(struct class_device *cdev) { int id; - if (sscanf(cdev->class_id, HWMON_ID_FORMAT, &id) == 1) { + if (likely(sscanf(cdev->class_id, HWMON_ID_FORMAT, &id) == 1)) { class_device_unregister(cdev); + spin_lock(&idr_lock); idr_remove(&hwmon_idr, id); + spin_unlock(&idr_lock); } else dev_dbg(cdev->dev, "hwmon_device_unregister() failed: bad class ID!\n"); |