diff options
author | Catalin Marinas <catalin.marinas@arm.com> | 2009-07-07 05:32:58 -0400 |
---|---|---|
committer | Catalin Marinas <catalin.marinas@arm.com> | 2009-07-08 09:25:13 -0400 |
commit | b87324d082d9d898e3c06b2a07a2b94b2430b8ba (patch) | |
tree | 23183d749d3267e3c3636fdc78e10ceb0cd10b1a /mm | |
parent | 288c857d66a400ca4846dd700eb1c4820d738bb9 (diff) |
kmemleak: Do not acquire scan_mutex in kmemleak_open()
Initially, the scan_mutex was acquired in kmemleak_open() and released
in kmemleak_release() (corresponding to /sys/kernel/debug/kmemleak
operations). This was causing some lockdep reports when the file was
closed from a different task than the one opening it. This patch moves
the scan_mutex acquiring in kmemleak_write() or kmemleak_seq_start()
with releasing in kmemleak_seq_stop().
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
Diffstat (limited to 'mm')
-rw-r--r-- | mm/kmemleak.c | 63 |
1 files changed, 30 insertions, 33 deletions
diff --git a/mm/kmemleak.c b/mm/kmemleak.c index 3c2b24c49a84..7cfb7d014a20 100644 --- a/mm/kmemleak.c +++ b/mm/kmemleak.c | |||
@@ -1101,6 +1101,11 @@ static void *kmemleak_seq_start(struct seq_file *seq, loff_t *pos) | |||
1101 | { | 1101 | { |
1102 | struct kmemleak_object *object; | 1102 | struct kmemleak_object *object; |
1103 | loff_t n = *pos; | 1103 | loff_t n = *pos; |
1104 | int err; | ||
1105 | |||
1106 | err = mutex_lock_interruptible(&scan_mutex); | ||
1107 | if (err < 0) | ||
1108 | return ERR_PTR(err); | ||
1104 | 1109 | ||
1105 | rcu_read_lock(); | 1110 | rcu_read_lock(); |
1106 | list_for_each_entry_rcu(object, &object_list, object_list) { | 1111 | list_for_each_entry_rcu(object, &object_list, object_list) { |
@@ -1144,8 +1149,15 @@ static void *kmemleak_seq_next(struct seq_file *seq, void *v, loff_t *pos) | |||
1144 | */ | 1149 | */ |
1145 | static void kmemleak_seq_stop(struct seq_file *seq, void *v) | 1150 | static void kmemleak_seq_stop(struct seq_file *seq, void *v) |
1146 | { | 1151 | { |
1147 | if (v) | 1152 | if (!IS_ERR(v)) { |
1148 | put_object(v); | 1153 | /* |
1154 | * kmemleak_seq_start may return ERR_PTR if the scan_mutex | ||
1155 | * waiting was interrupted, so only release it if !IS_ERR. | ||
1156 | */ | ||
1157 | mutex_unlock(&scan_mutex); | ||
1158 | if (v) | ||
1159 | put_object(v); | ||
1160 | } | ||
1149 | } | 1161 | } |
1150 | 1162 | ||
1151 | /* | 1163 | /* |
@@ -1172,36 +1184,15 @@ static const struct seq_operations kmemleak_seq_ops = { | |||
1172 | 1184 | ||
1173 | static int kmemleak_open(struct inode *inode, struct file *file) | 1185 | static int kmemleak_open(struct inode *inode, struct file *file) |
1174 | { | 1186 | { |
1175 | int ret = 0; | ||
1176 | |||
1177 | if (!atomic_read(&kmemleak_enabled)) | 1187 | if (!atomic_read(&kmemleak_enabled)) |
1178 | return -EBUSY; | 1188 | return -EBUSY; |
1179 | 1189 | ||
1180 | ret = mutex_lock_interruptible(&scan_mutex); | 1190 | return seq_open(file, &kmemleak_seq_ops); |
1181 | if (ret < 0) | ||
1182 | goto out; | ||
1183 | if (file->f_mode & FMODE_READ) { | ||
1184 | ret = seq_open(file, &kmemleak_seq_ops); | ||
1185 | if (ret < 0) | ||
1186 | goto scan_unlock; | ||
1187 | } | ||
1188 | return ret; | ||
1189 | |||
1190 | scan_unlock: | ||
1191 | mutex_unlock(&scan_mutex); | ||
1192 | out: | ||
1193 | return ret; | ||
1194 | } | 1191 | } |
1195 | 1192 | ||
1196 | static int kmemleak_release(struct inode *inode, struct file *file) | 1193 | static int kmemleak_release(struct inode *inode, struct file *file) |
1197 | { | 1194 | { |
1198 | int ret = 0; | 1195 | return seq_release(inode, file); |
1199 | |||
1200 | if (file->f_mode & FMODE_READ) | ||
1201 | seq_release(inode, file); | ||
1202 | mutex_unlock(&scan_mutex); | ||
1203 | |||
1204 | return ret; | ||
1205 | } | 1196 | } |
1206 | 1197 | ||
1207 | /* | 1198 | /* |
@@ -1221,15 +1212,17 @@ static ssize_t kmemleak_write(struct file *file, const char __user *user_buf, | |||
1221 | { | 1212 | { |
1222 | char buf[64]; | 1213 | char buf[64]; |
1223 | int buf_size; | 1214 | int buf_size; |
1224 | 1215 | int ret; | |
1225 | if (!atomic_read(&kmemleak_enabled)) | ||
1226 | return -EBUSY; | ||
1227 | 1216 | ||
1228 | buf_size = min(size, (sizeof(buf) - 1)); | 1217 | buf_size = min(size, (sizeof(buf) - 1)); |
1229 | if (strncpy_from_user(buf, user_buf, buf_size) < 0) | 1218 | if (strncpy_from_user(buf, user_buf, buf_size) < 0) |
1230 | return -EFAULT; | 1219 | return -EFAULT; |
1231 | buf[buf_size] = 0; | 1220 | buf[buf_size] = 0; |
1232 | 1221 | ||
1222 | ret = mutex_lock_interruptible(&scan_mutex); | ||
1223 | if (ret < 0) | ||
1224 | return ret; | ||
1225 | |||
1233 | if (strncmp(buf, "off", 3) == 0) | 1226 | if (strncmp(buf, "off", 3) == 0) |
1234 | kmemleak_disable(); | 1227 | kmemleak_disable(); |
1235 | else if (strncmp(buf, "stack=on", 8) == 0) | 1228 | else if (strncmp(buf, "stack=on", 8) == 0) |
@@ -1242,11 +1235,10 @@ static ssize_t kmemleak_write(struct file *file, const char __user *user_buf, | |||
1242 | stop_scan_thread(); | 1235 | stop_scan_thread(); |
1243 | else if (strncmp(buf, "scan=", 5) == 0) { | 1236 | else if (strncmp(buf, "scan=", 5) == 0) { |
1244 | unsigned long secs; | 1237 | unsigned long secs; |
1245 | int err; | ||
1246 | 1238 | ||
1247 | err = strict_strtoul(buf + 5, 0, &secs); | 1239 | ret = strict_strtoul(buf + 5, 0, &secs); |
1248 | if (err < 0) | 1240 | if (ret < 0) |
1249 | return err; | 1241 | goto out; |
1250 | stop_scan_thread(); | 1242 | stop_scan_thread(); |
1251 | if (secs) { | 1243 | if (secs) { |
1252 | jiffies_scan_wait = msecs_to_jiffies(secs * 1000); | 1244 | jiffies_scan_wait = msecs_to_jiffies(secs * 1000); |
@@ -1255,7 +1247,12 @@ static ssize_t kmemleak_write(struct file *file, const char __user *user_buf, | |||
1255 | } else if (strncmp(buf, "scan", 4) == 0) | 1247 | } else if (strncmp(buf, "scan", 4) == 0) |
1256 | kmemleak_scan(); | 1248 | kmemleak_scan(); |
1257 | else | 1249 | else |
1258 | return -EINVAL; | 1250 | ret = -EINVAL; |
1251 | |||
1252 | out: | ||
1253 | mutex_unlock(&scan_mutex); | ||
1254 | if (ret < 0) | ||
1255 | return ret; | ||
1259 | 1256 | ||
1260 | /* ignore the rest of the buffer, only one command at a time */ | 1257 | /* ignore the rest of the buffer, only one command at a time */ |
1261 | *ppos += size; | 1258 | *ppos += size; |