aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2012-05-14 13:30:03 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-05-14 15:19:56 -0400
commit356c05d58af05d582e634b54b40050c73609617b (patch)
tree49fd49203678cee0e7650f1b4d55aa3f53527575
parentc836d0ab70acf7b7bd2b698278e8abae9e6d9978 (diff)
sysfs: get rid of some lockdep false positives
This patch (as1554) fixes a lockdep false-positive report. The problem arises because lockdep is unable to deal with the tree-structured locks created by the device core and sysfs. This particular problem involves a sysfs attribute method that unregisters itself, not from the device it was called for, but from a descendant device. Lockdep doesn't understand the distinction and reports a possible deadlock, even though the operation is safe. This is the sort of thing that would normally be handled by using a nested lock annotation; unfortunately it's not feasible to do that here. There's no sensible way to tell sysfs when attribute removal occurs in the context of a parent attribute method. As a workaround, the patch adds a new flag to struct attribute telling sysfs not to inform lockdep when it acquires a readlock on a sysfs_dirent instance for the attribute. The readlock is still acquired, but lockdep doesn't know about it and hence does not complain about impossible deadlock scenarios. Also added are macros for static initialization of attribute structures with the ignore_lockdep flag set. The three offending attributes in the USB subsystem are converted to use the new macros. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Acked-by: Tejun Heo <tj@kernel.org> CC: Eric W. Biederman <ebiederm@xmission.com> CC: Peter Zijlstra <peterz@infradead.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/usb/core/sysfs.c6
-rw-r--r--fs/sysfs/dir.c31
-rw-r--r--include/linux/device.h3
-rw-r--r--include/linux/sysfs.h12
4 files changed, 44 insertions, 8 deletions
diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c
index 566d9f94f735..9a56e3adf476 100644
--- a/drivers/usb/core/sysfs.c
+++ b/drivers/usb/core/sysfs.c
@@ -73,7 +73,7 @@ set_bConfigurationValue(struct device *dev, struct device_attribute *attr,
73 return (value < 0) ? value : count; 73 return (value < 0) ? value : count;
74} 74}
75 75
76static DEVICE_ATTR(bConfigurationValue, S_IRUGO | S_IWUSR, 76static DEVICE_ATTR_IGNORE_LOCKDEP(bConfigurationValue, S_IRUGO | S_IWUSR,
77 show_bConfigurationValue, set_bConfigurationValue); 77 show_bConfigurationValue, set_bConfigurationValue);
78 78
79/* String fields */ 79/* String fields */
@@ -595,7 +595,7 @@ static ssize_t usb_dev_authorized_store(struct device *dev,
595 return result < 0? result : size; 595 return result < 0? result : size;
596} 596}
597 597
598static DEVICE_ATTR(authorized, 0644, 598static DEVICE_ATTR_IGNORE_LOCKDEP(authorized, 0644,
599 usb_dev_authorized_show, usb_dev_authorized_store); 599 usb_dev_authorized_show, usb_dev_authorized_store);
600 600
601/* "Safely remove a device" */ 601/* "Safely remove a device" */
@@ -618,7 +618,7 @@ static ssize_t usb_remove_store(struct device *dev,
618 usb_unlock_device(udev); 618 usb_unlock_device(udev);
619 return rc; 619 return rc;
620} 620}
621static DEVICE_ATTR(remove, 0200, NULL, usb_remove_store); 621static DEVICE_ATTR_IGNORE_LOCKDEP(remove, 0200, NULL, usb_remove_store);
622 622
623 623
624static struct attribute *dev_attrs[] = { 624static struct attribute *dev_attrs[] = {
diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c
index 24fa995f0312..e6bb9b2a4cbe 100644
--- a/fs/sysfs/dir.c
+++ b/fs/sysfs/dir.c
@@ -132,6 +132,24 @@ static void sysfs_unlink_sibling(struct sysfs_dirent *sd)
132 rb_erase(&sd->s_rb, &sd->s_parent->s_dir.children); 132 rb_erase(&sd->s_rb, &sd->s_parent->s_dir.children);
133} 133}
134 134
135#ifdef CONFIG_DEBUG_LOCK_ALLOC
136
137/* Test for attributes that want to ignore lockdep for read-locking */
138static bool ignore_lockdep(struct sysfs_dirent *sd)
139{
140 return sysfs_type(sd) == SYSFS_KOBJ_ATTR &&
141 sd->s_attr.attr->ignore_lockdep;
142}
143
144#else
145
146static inline bool ignore_lockdep(struct sysfs_dirent *sd)
147{
148 return true;
149}
150
151#endif
152
135/** 153/**
136 * sysfs_get_active - get an active reference to sysfs_dirent 154 * sysfs_get_active - get an active reference to sysfs_dirent
137 * @sd: sysfs_dirent to get an active reference to 155 * @sd: sysfs_dirent to get an active reference to
@@ -155,15 +173,17 @@ struct sysfs_dirent *sysfs_get_active(struct sysfs_dirent *sd)
155 return NULL; 173 return NULL;
156 174
157 t = atomic_cmpxchg(&sd->s_active, v, v + 1); 175 t = atomic_cmpxchg(&sd->s_active, v, v + 1);
158 if (likely(t == v)) { 176 if (likely(t == v))
159 rwsem_acquire_read(&sd->dep_map, 0, 1, _RET_IP_); 177 break;
160 return sd;
161 }
162 if (t < 0) 178 if (t < 0)
163 return NULL; 179 return NULL;
164 180
165 cpu_relax(); 181 cpu_relax();
166 } 182 }
183
184 if (likely(!ignore_lockdep(sd)))
185 rwsem_acquire_read(&sd->dep_map, 0, 1, _RET_IP_);
186 return sd;
167} 187}
168 188
169/** 189/**
@@ -180,7 +200,8 @@ void sysfs_put_active(struct sysfs_dirent *sd)
180 if (unlikely(!sd)) 200 if (unlikely(!sd))
181 return; 201 return;
182 202
183 rwsem_release(&sd->dep_map, 1, _RET_IP_); 203 if (likely(!ignore_lockdep(sd)))
204 rwsem_release(&sd->dep_map, 1, _RET_IP_);
184 v = atomic_dec_return(&sd->s_active); 205 v = atomic_dec_return(&sd->s_active);
185 if (likely(v != SD_DEACTIVATED_BIAS)) 206 if (likely(v != SD_DEACTIVATED_BIAS))
186 return; 207 return;
diff --git a/include/linux/device.h b/include/linux/device.h
index a8db2cfa2c81..e04f5776f6d0 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -504,6 +504,9 @@ ssize_t device_store_int(struct device *dev, struct device_attribute *attr,
504#define DEVICE_INT_ATTR(_name, _mode, _var) \ 504#define DEVICE_INT_ATTR(_name, _mode, _var) \
505 struct dev_ext_attribute dev_attr_##_name = \ 505 struct dev_ext_attribute dev_attr_##_name = \
506 { __ATTR(_name, _mode, device_show_int, device_store_int), &(_var) } 506 { __ATTR(_name, _mode, device_show_int, device_store_int), &(_var) }
507#define DEVICE_ATTR_IGNORE_LOCKDEP(_name, _mode, _show, _store) \
508 struct device_attribute dev_attr_##_name = \
509 __ATTR_IGNORE_LOCKDEP(_name, _mode, _show, _store)
507 510
508extern int device_create_file(struct device *device, 511extern int device_create_file(struct device *device,
509 const struct device_attribute *entry); 512 const struct device_attribute *entry);
diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h
index 0010009b2f00..381f06db2fe5 100644
--- a/include/linux/sysfs.h
+++ b/include/linux/sysfs.h
@@ -27,6 +27,7 @@ struct attribute {
27 const char *name; 27 const char *name;
28 umode_t mode; 28 umode_t mode;
29#ifdef CONFIG_DEBUG_LOCK_ALLOC 29#ifdef CONFIG_DEBUG_LOCK_ALLOC
30 bool ignore_lockdep:1;
30 struct lock_class_key *key; 31 struct lock_class_key *key;
31 struct lock_class_key skey; 32 struct lock_class_key skey;
32#endif 33#endif
@@ -80,6 +81,17 @@ struct attribute_group {
80 81
81#define __ATTR_NULL { .attr = { .name = NULL } } 82#define __ATTR_NULL { .attr = { .name = NULL } }
82 83
84#ifdef CONFIG_DEBUG_LOCK_ALLOC
85#define __ATTR_IGNORE_LOCKDEP(_name, _mode, _show, _store) { \
86 .attr = {.name = __stringify(_name), .mode = _mode, \
87 .ignore_lockdep = true }, \
88 .show = _show, \
89 .store = _store, \
90}
91#else
92#define __ATTR_IGNORE_LOCKDEP __ATTR
93#endif
94
83#define attr_name(_attr) (_attr).attr.name 95#define attr_name(_attr) (_attr).attr.name
84 96
85struct file; 97struct file;