History

mm: prevent concurrent unmap_mapping_range() on the same inode

Michael Leun reported that running parallel opens on a fuse filesystem can trigger a "kernel BUG at mm/truncate.c:475" Gurudas Pai reported the same bug on NFS. The reason is, unmap_mapping_range() is not prepared for more than one concurrent invocation per inode. For example: thread1: going through a big range, stops in the middle of a vma and stores the restart address in vm_truncate_count. thread2: comes in with a small (e.g. single page) unmap request on the same vma, somewhere before restart_address, finds that the vma was already unmapped up to the restart address and happily returns without doing anything. Another scenario would be two big unmap requests, both having to restart the unmapping and each one setting vm_truncate_count to its own value. This could go on forever without any of them being able to finish. Truncate and hole punching already serialize with i_mutex. Other callers of unmap_mapping_range() do not, and it's difficult to get i_mutex protection for all callers. In particular ->d_revalidate(), which calls invalidate_inode_pages2_range() in fuse, may be called with or without i_mutex. This patch adds a new mutex to 'struct address_space' to prevent running multiple concurrent unmap_mapping_range() on the same mapping. [ We'll hopefully get rid of all this with the upcoming mm preemptibility series by Peter Zijlstra, the "mm: Remove i_mmap_mutex lockbreak" patch in particular. But that is for 2.6.39 ]

Miklos Szeredi committed 15 years ago

Commit 2aa15891 parent 78794b210 files · +23 −38Browse files

Showing the changes to include (1 of 10 files). Show all

Unified · Split

include/linux/fs.h+2−0
@@ -649,6 +649,7 @@ struct address_space { · 648 unmodified lines
649 spinlock_t private_lock; /* for use by the address_space */649 spinlock_t private_lock; /* for use by the address_space */
650 struct list_head private_list; /* ditto */650 struct list_head private_list; /* ditto */
651 struct address_space *assoc_mapping; /* ditto */651 struct address_space *assoc_mapping; /* ditto */
652 struct mutex unmap_mutex; /* to protect unmapping */
652} __attribute__((aligned(sizeof(long))));653} __attribute__((aligned(sizeof(long))));
653 /*654 /*
654 * On most architectures that alignment is already the case; but655 * On most architectures that alignment is already the case; but
@@ -2225,6 +2226,7 @@ extern loff_t vfs_llseek(struct file *file, loff_t offset, int origin); · 1570 unmodified lines
2225 2226
2226extern int inode_init_always(struct super_block *, struct inode *);2227extern int inode_init_always(struct super_block *, struct inode *);
2227extern void inode_init_once(struct inode *);2228extern void inode_init_once(struct inode *);
2229extern void address_space_init_once(struct address_space *mapping);
2228extern void ihold(struct inode * inode);2230extern void ihold(struct inode * inode);
2229extern void iput(struct inode *);2231extern void iput(struct inode *);
2230extern struct inode * igrab(struct inode *);2232extern struct inode * igrab(struct inode *);