aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKees Cook <keescook@chromium.org>2017-04-27 18:53:21 -0400
committerKees Cook <keescook@chromium.org>2017-04-27 23:35:34 -0400
commit3a7d2fd16c57a1ef47dc2891171514231c9c7c6e (patch)
tree89837e53f2b86e76275eeca582bdfe68dacc97e5
parent041939c1ec54208b42f5cd819209173d52a29d34 (diff)
pstore: Solve lockdep warning by moving inode locks
Lockdep complains about a possible deadlock between mount and unlink (which is technically impossible), but fixing this improves possible future multiple-backend support, and keeps locking in the right order. The lockdep warning could be triggered by unlinking a file in the pstore filesystem: -> #1 (&sb->s_type->i_mutex_key#14){++++++}: lock_acquire+0xc9/0x220 down_write+0x3f/0x70 pstore_mkfile+0x1f4/0x460 pstore_get_records+0x17a/0x320 pstore_fill_super+0xa4/0xc0 mount_single+0x89/0xb0 pstore_mount+0x13/0x20 mount_fs+0xf/0x90 vfs_kern_mount+0x66/0x170 do_mount+0x190/0xd50 SyS_mount+0x90/0xd0 entry_SYSCALL_64_fastpath+0x1c/0xb1 -> #0 (&psinfo->read_mutex){+.+.+.}: __lock_acquire+0x1ac0/0x1bb0 lock_acquire+0xc9/0x220 __mutex_lock+0x6e/0x990 mutex_lock_nested+0x16/0x20 pstore_unlink+0x3f/0xa0 vfs_unlink+0xb5/0x190 do_unlinkat+0x24c/0x2a0 SyS_unlinkat+0x16/0x30 entry_SYSCALL_64_fastpath+0x1c/0xb1 Possible unsafe locking scenario: CPU0 CPU1 ---- ---- lock(&sb->s_type->i_mutex_key#14); lock(&psinfo->read_mutex); lock(&sb->s_type->i_mutex_key#14); lock(&psinfo->read_mutex); Reported-by: Marta Lofstedt <marta.lofstedt@intel.com> Reported-by: Chris Wilson <chris@chris-wilson.co.uk> Signed-off-by: Kees Cook <keescook@chromium.org> Acked-by: Namhyung Kim <namhyung@kernel.org>
-rw-r--r--fs/pstore/inode.c37
-rw-r--r--fs/pstore/internal.h5
-rw-r--r--fs/pstore/platform.c10
3 files changed, 36 insertions, 16 deletions
diff --git a/fs/pstore/inode.c b/fs/pstore/inode.c
index 06504b69575b..792a4e5f9226 100644
--- a/fs/pstore/inode.c
+++ b/fs/pstore/inode.c
@@ -311,9 +311,8 @@ bool pstore_is_mounted(void)
311 * Load it up with "size" bytes of data from "buf". 311 * Load it up with "size" bytes of data from "buf".
312 * Set the mtime & ctime to the date that this record was originally stored. 312 * Set the mtime & ctime to the date that this record was originally stored.
313 */ 313 */
314int pstore_mkfile(struct pstore_record *record) 314int pstore_mkfile(struct dentry *root, struct pstore_record *record)
315{ 315{
316 struct dentry *root = pstore_sb->s_root;
317 struct dentry *dentry; 316 struct dentry *dentry;
318 struct inode *inode; 317 struct inode *inode;
319 int rc = 0; 318 int rc = 0;
@@ -322,6 +321,8 @@ int pstore_mkfile(struct pstore_record *record)
322 unsigned long flags; 321 unsigned long flags;
323 size_t size = record->size + record->ecc_notice_size; 322 size_t size = record->size + record->ecc_notice_size;
324 323
324 WARN_ON(!inode_is_locked(d_inode(root)));
325
325 spin_lock_irqsave(&allpstore_lock, flags); 326 spin_lock_irqsave(&allpstore_lock, flags);
326 list_for_each_entry(pos, &allpstore, list) { 327 list_for_each_entry(pos, &allpstore, list) {
327 if (pos->record->type == record->type && 328 if (pos->record->type == record->type &&
@@ -336,7 +337,7 @@ int pstore_mkfile(struct pstore_record *record)
336 return rc; 337 return rc;
337 338
338 rc = -ENOMEM; 339 rc = -ENOMEM;
339 inode = pstore_get_inode(pstore_sb); 340 inode = pstore_get_inode(root->d_sb);
340 if (!inode) 341 if (!inode)
341 goto fail; 342 goto fail;
342 inode->i_mode = S_IFREG | 0444; 343 inode->i_mode = S_IFREG | 0444;
@@ -394,11 +395,9 @@ int pstore_mkfile(struct pstore_record *record)
394 break; 395 break;
395 } 396 }
396 397
397 inode_lock(d_inode(root));
398
399 dentry = d_alloc_name(root, name); 398 dentry = d_alloc_name(root, name);
400 if (!dentry) 399 if (!dentry)
401 goto fail_lockedalloc; 400 goto fail_private;
402 401
403 inode->i_size = private->total_size = size; 402 inode->i_size = private->total_size = size;
404 403
@@ -413,12 +412,9 @@ int pstore_mkfile(struct pstore_record *record)
413 list_add(&private->list, &allpstore); 412 list_add(&private->list, &allpstore);
414 spin_unlock_irqrestore(&allpstore_lock, flags); 413 spin_unlock_irqrestore(&allpstore_lock, flags);
415 414
416 inode_unlock(d_inode(root));
417
418 return 0; 415 return 0;
419 416
420fail_lockedalloc: 417fail_private:
421 inode_unlock(d_inode(root));
422 free_pstore_private(private); 418 free_pstore_private(private);
423fail_alloc: 419fail_alloc:
424 iput(inode); 420 iput(inode);
@@ -427,6 +423,27 @@ fail:
427 return rc; 423 return rc;
428} 424}
429 425
426/*
427 * Read all the records from the persistent store. Create
428 * files in our filesystem. Don't warn about -EEXIST errors
429 * when we are re-scanning the backing store looking to add new
430 * error records.
431 */
432void pstore_get_records(int quiet)
433{
434 struct pstore_info *psi = psinfo;
435 struct dentry *root;
436
437 if (!psi || !pstore_sb)
438 return;
439
440 root = pstore_sb->s_root;
441
442 inode_lock(d_inode(root));
443 pstore_get_backend_records(psi, root, quiet);
444 inode_unlock(d_inode(root));
445}
446
430static int pstore_fill_super(struct super_block *sb, void *data, int silent) 447static int pstore_fill_super(struct super_block *sb, void *data, int silent)
431{ 448{
432 struct inode *inode; 449 struct inode *inode;
diff --git a/fs/pstore/internal.h b/fs/pstore/internal.h
index af1df5a36d86..c416e653dc4f 100644
--- a/fs/pstore/internal.h
+++ b/fs/pstore/internal.h
@@ -25,7 +25,10 @@ extern struct pstore_info *psinfo;
25 25
26extern void pstore_set_kmsg_bytes(int); 26extern void pstore_set_kmsg_bytes(int);
27extern void pstore_get_records(int); 27extern void pstore_get_records(int);
28extern int pstore_mkfile(struct pstore_record *record); 28extern void pstore_get_backend_records(struct pstore_info *psi,
29 struct dentry *root, int quiet);
30extern int pstore_mkfile(struct dentry *root,
31 struct pstore_record *record);
29extern bool pstore_is_mounted(void); 32extern bool pstore_is_mounted(void);
30 33
31#endif 34#endif
diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c
index 43b3ca5e045f..d468eec9b8a6 100644
--- a/fs/pstore/platform.c
+++ b/fs/pstore/platform.c
@@ -810,17 +810,17 @@ static void decompress_record(struct pstore_record *record)
810} 810}
811 811
812/* 812/*
813 * Read all the records from the persistent store. Create 813 * Read all the records from one persistent store backend. Create
814 * files in our filesystem. Don't warn about -EEXIST errors 814 * files in our filesystem. Don't warn about -EEXIST errors
815 * when we are re-scanning the backing store looking to add new 815 * when we are re-scanning the backing store looking to add new
816 * error records. 816 * error records.
817 */ 817 */
818void pstore_get_records(int quiet) 818void pstore_get_backend_records(struct pstore_info *psi,
819 struct dentry *root, int quiet)
819{ 820{
820 struct pstore_info *psi = psinfo;
821 int failed = 0; 821 int failed = 0;
822 822
823 if (!psi) 823 if (!psi || !root)
824 return; 824 return;
825 825
826 mutex_lock(&psi->read_mutex); 826 mutex_lock(&psi->read_mutex);
@@ -850,7 +850,7 @@ void pstore_get_records(int quiet)
850 break; 850 break;
851 851
852 decompress_record(record); 852 decompress_record(record);
853 rc = pstore_mkfile(record); 853 rc = pstore_mkfile(root, record);
854 if (rc) { 854 if (rc) {
855 /* pstore_mkfile() did not take record, so free it. */ 855 /* pstore_mkfile() did not take record, so free it. */
856 kfree(record->buf); 856 kfree(record->buf);