diff options
| author | npiggin@suse.de <npiggin@suse.de> | 2009-08-20 12:35:05 -0400 |
|---|---|---|
| committer | al <al@dizzy.pdmi.ras.ru> | 2009-09-24 08:41:47 -0400 |
| commit | 25d9e2d15286281ec834b829a4aaf8969011f1cd (patch) | |
| tree | e4329a481ca197afae30f04335e023c7d04f7d67 | |
| parent | eca6f534e61919b28fb21aafbd1c2983deae75be (diff) | |
truncate: new helpers
Introduce new truncate helpers truncate_pagecache and inode_newsize_ok.
vmtruncate is also consolidated from mm/memory.c and mm/nommu.c and
into mm/truncate.c.
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Nick Piggin <npiggin@suse.de>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
| -rw-r--r-- | Documentation/vm/locking | 2 | ||||
| -rw-r--r-- | fs/attr.c | 46 | ||||
| -rw-r--r-- | include/linux/fs.h | 3 | ||||
| -rw-r--r-- | include/linux/mm.h | 5 | ||||
| -rw-r--r-- | mm/filemap.c | 2 | ||||
| -rw-r--r-- | mm/memory.c | 62 | ||||
| -rw-r--r-- | mm/mremap.c | 4 | ||||
| -rw-r--r-- | mm/nommu.c | 40 | ||||
| -rw-r--r-- | mm/truncate.c | 64 |
9 files changed, 120 insertions, 108 deletions
diff --git a/Documentation/vm/locking b/Documentation/vm/locking index f366fa956179..25fadb448760 100644 --- a/Documentation/vm/locking +++ b/Documentation/vm/locking | |||
| @@ -80,7 +80,7 @@ Note: PTL can also be used to guarantee that no new clones using the | |||
| 80 | mm start up ... this is a loose form of stability on mm_users. For | 80 | mm start up ... this is a loose form of stability on mm_users. For |
| 81 | example, it is used in copy_mm to protect against a racing tlb_gather_mmu | 81 | example, it is used in copy_mm to protect against a racing tlb_gather_mmu |
| 82 | single address space optimization, so that the zap_page_range (from | 82 | single address space optimization, so that the zap_page_range (from |
| 83 | vmtruncate) does not lose sending ipi's to cloned threads that might | 83 | truncate) does not lose sending ipi's to cloned threads that might |
| 84 | be spawned underneath it and go to user mode to drag in pte's into tlbs. | 84 | be spawned underneath it and go to user mode to drag in pte's into tlbs. |
| 85 | 85 | ||
| 86 | swap_lock | 86 | swap_lock |
| @@ -18,7 +18,7 @@ | |||
| 18 | /* Taken over from the old code... */ | 18 | /* Taken over from the old code... */ |
| 19 | 19 | ||
| 20 | /* POSIX UID/GID verification for setting inode attributes. */ | 20 | /* POSIX UID/GID verification for setting inode attributes. */ |
| 21 | int inode_change_ok(struct inode *inode, struct iattr *attr) | 21 | int inode_change_ok(const struct inode *inode, struct iattr *attr) |
| 22 | { | 22 | { |
| 23 | int retval = -EPERM; | 23 | int retval = -EPERM; |
| 24 | unsigned int ia_valid = attr->ia_valid; | 24 | unsigned int ia_valid = attr->ia_valid; |
| @@ -60,9 +60,51 @@ fine: | |||
| 60 | error: | 60 | error: |
| 61 | return retval; | 61 | return retval; |
| 62 | } | 62 | } |
| 63 | |||
| 64 | EXPORT_SYMBOL(inode_change_ok); | 63 | EXPORT_SYMBOL(inode_change_ok); |
| 65 | 64 | ||
| 65 | /** | ||
| 66 | * inode_newsize_ok - may this inode be truncated to a given size | ||
| 67 | * @inode: the inode to be truncated | ||
| 68 | * @offset: the new size to assign to the inode | ||
| 69 | * @Returns: 0 on success, -ve errno on failure | ||
| 70 | * | ||
| 71 | * inode_newsize_ok will check filesystem limits and ulimits to check that the | ||
| 72 | * new inode size is within limits. inode_newsize_ok will also send SIGXFSZ | ||
| 73 | * when necessary. Caller must not proceed with inode size change if failure is | ||
| 74 | * returned. @inode must be a file (not directory), with appropriate | ||
| 75 | * permissions to allow truncate (inode_newsize_ok does NOT check these | ||
| 76 | * conditions). | ||
| 77 | * | ||
| 78 | * inode_newsize_ok must be called with i_mutex held. | ||
| 79 | */ | ||
| 80 | int inode_newsize_ok(const struct inode *inode, loff_t offset) | ||
| 81 | { | ||
| 82 | if (inode->i_size < offset) { | ||
| 83 | unsigned long limit; | ||
| 84 | |||
| 85 | limit = current->signal->rlim[RLIMIT_FSIZE].rlim_cur; | ||
| 86 | if (limit != RLIM_INFINITY && offset > limit) | ||
| 87 | goto out_sig; | ||
| 88 | if (offset > inode->i_sb->s_maxbytes) | ||
| 89 | goto out_big; | ||
| 90 | } else { | ||
| 91 | /* | ||
| 92 | * truncation of in-use swapfiles is disallowed - it would | ||
| 93 | * cause subsequent swapout to scribble on the now-freed | ||
| 94 | * blocks. | ||
| 95 | */ | ||
| 96 | if (IS_SWAPFILE(inode)) | ||
| 97 | return -ETXTBSY; | ||
| 98 | } | ||
| 99 | |||
| 100 | return 0; | ||
| 101 | out_sig: | ||
| 102 | send_sig(SIGXFSZ, current, 0); | ||
| 103 | out_big: | ||
| 104 | return -EFBIG; | ||
| 105 | } | ||
| 106 | EXPORT_SYMBOL(inode_newsize_ok); | ||
| 107 | |||
| 66 | int inode_setattr(struct inode * inode, struct iattr * attr) | 108 | int inode_setattr(struct inode * inode, struct iattr * attr) |
| 67 | { | 109 | { |
| 68 | unsigned int ia_valid = attr->ia_valid; | 110 | unsigned int ia_valid = attr->ia_valid; |
diff --git a/include/linux/fs.h b/include/linux/fs.h index 502d96ef345d..2b08b5ce09b6 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h | |||
| @@ -2382,7 +2382,8 @@ extern int buffer_migrate_page(struct address_space *, | |||
| 2382 | #define buffer_migrate_page NULL | 2382 | #define buffer_migrate_page NULL |
| 2383 | #endif | 2383 | #endif |
| 2384 | 2384 | ||
| 2385 | extern int inode_change_ok(struct inode *, struct iattr *); | 2385 | extern int inode_change_ok(const struct inode *, struct iattr *); |
| 2386 | extern int inode_newsize_ok(const struct inode *, loff_t offset); | ||
| 2386 | extern int __must_check inode_setattr(struct inode *, struct iattr *); | 2387 | extern int __must_check inode_setattr(struct inode *, struct iattr *); |
| 2387 | 2388 | ||
| 2388 | extern void file_update_time(struct file *file); | 2389 | extern void file_update_time(struct file *file); |
diff --git a/include/linux/mm.h b/include/linux/mm.h index b6eae5e3144b..8347e938fb2f 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h | |||
| @@ -791,8 +791,9 @@ static inline void unmap_shared_mapping_range(struct address_space *mapping, | |||
| 791 | unmap_mapping_range(mapping, holebegin, holelen, 0); | 791 | unmap_mapping_range(mapping, holebegin, holelen, 0); |
| 792 | } | 792 | } |
| 793 | 793 | ||
| 794 | extern int vmtruncate(struct inode * inode, loff_t offset); | 794 | extern void truncate_pagecache(struct inode *inode, loff_t old, loff_t new); |
| 795 | extern int vmtruncate_range(struct inode * inode, loff_t offset, loff_t end); | 795 | extern int vmtruncate(struct inode *inode, loff_t offset); |
| 796 | extern int vmtruncate_range(struct inode *inode, loff_t offset, loff_t end); | ||
| 796 | 797 | ||
| 797 | #ifdef CONFIG_MMU | 798 | #ifdef CONFIG_MMU |
| 798 | extern int handle_mm_fault(struct mm_struct *mm, struct vm_area_struct *vma, | 799 | extern int handle_mm_fault(struct mm_struct *mm, struct vm_area_struct *vma, |
diff --git a/mm/filemap.c b/mm/filemap.c index bcc7372aebbc..33349adb227a 100644 --- a/mm/filemap.c +++ b/mm/filemap.c | |||
| @@ -58,7 +58,7 @@ | |||
| 58 | /* | 58 | /* |
| 59 | * Lock ordering: | 59 | * Lock ordering: |
| 60 | * | 60 | * |
| 61 | * ->i_mmap_lock (vmtruncate) | 61 | * ->i_mmap_lock (truncate_pagecache) |
| 62 | * ->private_lock (__free_pte->__set_page_dirty_buffers) | 62 | * ->private_lock (__free_pte->__set_page_dirty_buffers) |
| 63 | * ->swap_lock (exclusive_swap_page, others) | 63 | * ->swap_lock (exclusive_swap_page, others) |
| 64 | * ->mapping->tree_lock | 64 | * ->mapping->tree_lock |
diff --git a/mm/memory.c b/mm/memory.c index b1443ac07c00..ebcd3decac89 100644 --- a/mm/memory.c +++ b/mm/memory.c | |||
| @@ -297,7 +297,8 @@ void free_pgtables(struct mmu_gather *tlb, struct vm_area_struct *vma, | |||
| 297 | unsigned long addr = vma->vm_start; | 297 | unsigned long addr = vma->vm_start; |
| 298 | 298 | ||
| 299 | /* | 299 | /* |
| 300 | * Hide vma from rmap and vmtruncate before freeing pgtables | 300 | * Hide vma from rmap and truncate_pagecache before freeing |
| 301 | * pgtables | ||
| 301 | */ | 302 | */ |
| 302 | anon_vma_unlink(vma); | 303 | anon_vma_unlink(vma); |
| 303 | unlink_file_vma(vma); | 304 | unlink_file_vma(vma); |
| @@ -2407,7 +2408,7 @@ restart: | |||
| 2407 | * @mapping: the address space containing mmaps to be unmapped. | 2408 | * @mapping: the address space containing mmaps to be unmapped. |
| 2408 | * @holebegin: byte in first page to unmap, relative to the start of | 2409 | * @holebegin: byte in first page to unmap, relative to the start of |
| 2409 | * the underlying file. This will be rounded down to a PAGE_SIZE | 2410 | * the underlying file. This will be rounded down to a PAGE_SIZE |
| 2410 | * boundary. Note that this is different from vmtruncate(), which | 2411 | * boundary. Note that this is different from truncate_pagecache(), which |
| 2411 | * must keep the partial page. In contrast, we must get rid of | 2412 | * must keep the partial page. In contrast, we must get rid of |
| 2412 | * partial pages. | 2413 | * partial pages. |
| 2413 | * @holelen: size of prospective hole in bytes. This will be rounded | 2414 | * @holelen: size of prospective hole in bytes. This will be rounded |
| @@ -2458,63 +2459,6 @@ void unmap_mapping_range(struct address_space *mapping, | |||
| 2458 | } | 2459 | } |
| 2459 | EXPORT_SYMBOL(unmap_mapping_range); | 2460 | EXPORT_SYMBOL(unmap_mapping_range); |
| 2460 | 2461 | ||
| 2461 | /** | ||
| 2462 | * vmtruncate - unmap mappings "freed" by truncate() syscall | ||
| 2463 | * @inode: inode of the file used | ||
| 2464 | * @offset: file offset to start truncating | ||
| 2465 | * | ||
| 2466 | * NOTE! We have to be ready to update the memory sharing | ||
| 2467 | * between the file and the memory map for a potential last | ||
| 2468 | * incomplete page. Ugly, but necessary. | ||
| 2469 | */ | ||
| 2470 | int vmtruncate(struct inode * inode, loff_t offset) | ||
| 2471 | { | ||
| 2472 | if (inode->i_size < offset) { | ||
| 2473 | unsigned long limit; | ||
| 2474 | |||
| 2475 | limit = current->signal->rlim[RLIMIT_FSIZE].rlim_cur; | ||
| 2476 | if (limit != RLIM_INFINITY && offset > limit) | ||
| 2477 | goto out_sig; | ||
| 2478 | if (offset > inode->i_sb->s_maxbytes) | ||
| 2479 | goto out_big; | ||
| 2480 | i_size_write(inode, offset); | ||
| 2481 | } else { | ||
| 2482 | struct address_space *mapping = inode->i_mapping; | ||
| 2483 | |||
| 2484 | /* | ||
| 2485 | * truncation of in-use swapfiles is disallowed - it would | ||
| 2486 | * cause subsequent swapout to scribble on the now-freed | ||
| 2487 | * blocks. | ||
| 2488 | */ | ||
| 2489 | if (IS_SWAPFILE(inode)) | ||
| 2490 | return -ETXTBSY; | ||
| 2491 | i_size_write(inode, offset); | ||
| 2492 | |||
| 2493 | /* | ||
| 2494 | * unmap_mapping_range is called twice, first simply for | ||
| 2495 | * efficiency so that truncate_inode_pages does fewer | ||
| 2496 | |||
