diff options
| -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; |
