diff options
author | Mark M. Hoffman <mhoffman@lightlink.com> | 2006-03-05 17:13:47 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2006-03-23 17:21:55 -0500 |
commit | ded2b66615613093eeb83b81499bc270de8fc499 (patch) | |
tree | 118f87f38a4b7a2c8d65f38beb85fa63da78fb4f | |
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>
-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 @@ | |||
17 | #include <linux/idr.h> | 17 | #include <linux/idr.h> |
18 | #include <linux/hwmon.h> | 18 | #include <linux/hwmon.h> |
19 | #include <linux/gfp.h> | 19 | #include <linux/gfp.h> |
20 | #include <linux/spinlock.h> | ||
20 | 21 | ||
21 | #define HWMON_ID_PREFIX "hwmon" | 22 | #define HWMON_ID_PREFIX "hwmon" |
22 | #define HWMON_ID_FORMAT HWMON_ID_PREFIX "%d" | 23 | #define HWMON_ID_FORMAT HWMON_ID_PREFIX "%d" |
@@ -24,6 +25,7 @@ | |||
24 | static struct class *hwmon_class; | 25 | static struct class *hwmon_class; |
25 | 26 | ||
26 | static DEFINE_IDR(hwmon_idr); | 27 | static DEFINE_IDR(hwmon_idr); |
28 | static DEFINE_SPINLOCK(idr_lock); | ||
27 | 29 | ||
28 | /** | 30 | /** |
29 | * hwmon_device_register - register w/ hwmon sysfs class | 31 | * hwmon_device_register - register w/ hwmon sysfs class |
@@ -37,20 +39,30 @@ static DEFINE_IDR(hwmon_idr); | |||
37 | struct class_device *hwmon_device_register(struct device *dev) | 39 | struct class_device *hwmon_device_register(struct device *dev) |
38 | { | 40 | { |
39 | struct class_device *cdev; | 41 | struct class_device *cdev; |
40 | int id; | 42 | int id, err; |
41 | 43 | ||
42 | if (idr_pre_get(&hwmon_idr, GFP_KERNEL) == 0) | 44 | again: |
45 | if (unlikely(idr_pre_get(&hwmon_idr, GFP_KERNEL) == 0)) | ||
43 | return ERR_PTR(-ENOMEM); | 46 | return ERR_PTR(-ENOMEM); |
44 | 47 | ||
45 | if (idr_get_new(&hwmon_idr, NULL, &id) < 0) | 48 | spin_lock(&idr_lock); |
46 | return ERR_PTR(-ENOMEM); | 49 | err = idr_get_new(&hwmon_idr, NULL, &id); |
50 | spin_unlock(&idr_lock); | ||
51 | |||
52 | if (unlikely(err == -EAGAIN)) | ||
53 | goto again; | ||
54 | else if (unlikely(err)) | ||
55 | return ERR_PTR(err); | ||
47 | 56 | ||
48 | id = id & MAX_ID_MASK; | 57 | id = id & MAX_ID_MASK; |
49 | cdev = class_device_create(hwmon_class, NULL, MKDEV(0,0), dev, | 58 | cdev = class_device_create(hwmon_class, NULL, MKDEV(0,0), dev, |
50 | HWMON_ID_FORMAT, id); | 59 | HWMON_ID_FORMAT, id); |
51 | 60 | ||
52 | if (IS_ERR(cdev)) | 61 | if (IS_ERR(cdev)) { |
62 | spin_lock(&idr_lock); | ||
53 | idr_remove(&hwmon_idr, id); | 63 | idr_remove(&hwmon_idr, id); |
64 | spin_unlock(&idr_lock); | ||
65 | } | ||
54 | 66 | ||
55 | return cdev; | 67 | return cdev; |
56 | } | 68 | } |
@@ -64,9 +76,11 @@ void hwmon_device_unregister(struct class_device *cdev) | |||
64 | { | 76 | { |
65 | int id; | 77 | int id; |
66 | 78 | ||
67 | if (sscanf(cdev->class_id, HWMON_ID_FORMAT, &id) == 1) { | 79 | if (likely(sscanf(cdev->class_id, HWMON_ID_FORMAT, &id) == 1)) { |
68 | class_device_unregister(cdev); | 80 | class_device_unregister(cdev); |
81 | spin_lock(&idr_lock); | ||
69 | idr_remove(&hwmon_idr, id); | 82 | idr_remove(&hwmon_idr, id); |
83 | spin_unlock(&idr_lock); | ||
70 | } else | 84 | } else |
71 | dev_dbg(cdev->dev, | 85 | dev_dbg(cdev->dev, |
72 | "hwmon_device_unregister() failed: bad class ID!\n"); | 86 | "hwmon_device_unregister() failed: bad class ID!\n"); |