diff options
Diffstat (limited to 'mm/truncate.c')
-rw-r--r-- | mm/truncate.c | 64 |
1 files changed, 64 insertions, 0 deletions
diff --git a/mm/truncate.c b/mm/truncate.c index a17b3977cfdf..450cebdabfc0 100644 --- a/mm/truncate.c +++ b/mm/truncate.c | |||
@@ -497,3 +497,67 @@ int invalidate_inode_pages2(struct address_space *mapping) | |||
497 | return invalidate_inode_pages2_range(mapping, 0, -1); | 497 | return invalidate_inode_pages2_range(mapping, 0, -1); |
498 | } | 498 | } |
499 | EXPORT_SYMBOL_GPL(invalidate_inode_pages2); | 499 | EXPORT_SYMBOL_GPL(invalidate_inode_pages2); |
500 | |||
501 | /** | ||
502 | * truncate_pagecache - unmap and remove pagecache that has been truncated | ||
503 | * @inode: inode | ||
504 | * @old: old file offset | ||
505 | * @new: new file offset | ||
506 | * | ||
507 | * inode's new i_size must already be written before truncate_pagecache | ||
508 | * is called. | ||
509 | * | ||
510 | * This function should typically be called before the filesystem | ||
511 | * releases resources associated with the freed range (eg. deallocates | ||
512 | * blocks). This way, pagecache will always stay logically coherent | ||
513 | * with on-disk format, and the filesystem would not have to deal with | ||
514 | * situations such as writepage being called for a page that has already | ||
515 | * had its underlying blocks deallocated. | ||
516 | */ | ||
517 | void truncate_pagecache(struct inode *inode, loff_t old, loff_t new) | ||
518 | { | ||
519 | if (new < old) { | ||
520 | struct address_space *mapping = inode->i_mapping; | ||
521 | |||
522 | /* | ||
523 | * unmap_mapping_range is called twice, first simply for | ||
524 | * efficiency so that truncate_inode_pages does fewer | ||
525 | * single-page unmaps. However after this first call, and | ||
526 | * before truncate_inode_pages finishes, it is possible for | ||
527 | * private pages to be COWed, which remain after | ||
528 | * truncate_inode_pages finishes, hence the second | ||
529 | * unmap_mapping_range call must be made for correctness. | ||
530 | */ | ||
531 | unmap_mapping_range(mapping, new + PAGE_SIZE - 1, 0, 1); | ||
532 | truncate_inode_pages(mapping, new); | ||
533 | unmap_mapping_range(mapping, new + PAGE_SIZE - 1, 0, 1); | ||
534 | } | ||
535 | } | ||
536 | EXPORT_SYMBOL(truncate_pagecache); | ||
537 | |||
538 | /** | ||
539 | * vmtruncate - unmap mappings "freed" by truncate() syscall | ||
540 | * @inode: inode of the file used | ||
541 | * @offset: file offset to start truncating | ||
542 | * | ||
543 | * NOTE! We have to be ready to update the memory sharing | ||
544 | * between the file and the memory map for a potential last | ||
545 | * incomplete page. Ugly, but necessary. | ||
546 | */ | ||
547 | int vmtruncate(struct inode *inode, loff_t offset) | ||
548 | { | ||
549 | loff_t oldsize; | ||
550 | int error; | ||
551 | |||
552 | error = inode_newsize_ok(inode, offset); | ||
553 | if (error) | ||
554 | return error; | ||
555 | oldsize = inode->i_size; | ||
556 | i_size_write(inode, offset); | ||
557 | truncate_pagecache(inode, oldsize, offset); | ||
558 | if (inode->i_op->truncate) | ||
559 | inode->i_op->truncate(inode); | ||
560 | |||
561 | return error; | ||
562 | } | ||
563 | EXPORT_SYMBOL(vmtruncate); | ||