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 ccc3ecf7cb98..5900afca0fa9 100644 --- a/mm/truncate.c +++ b/mm/truncate.c | |||
@@ -465,3 +465,67 @@ int invalidate_inode_pages2(struct address_space *mapping) | |||
465 | return invalidate_inode_pages2_range(mapping, 0, -1); | 465 | return invalidate_inode_pages2_range(mapping, 0, -1); |
466 | } | 466 | } |
467 | EXPORT_SYMBOL_GPL(invalidate_inode_pages2); | 467 | EXPORT_SYMBOL_GPL(invalidate_inode_pages2); |
468 | |||
469 | /** | ||
470 | * truncate_pagecache - unmap and remove pagecache that has been truncated | ||
471 | * @inode: inode | ||
472 | * @old: old file offset | ||
473 | * @new: new file offset | ||
474 | * | ||
475 | * inode's new i_size must already be written before truncate_pagecache | ||
476 | * is called. | ||
477 | * | ||
478 | * This function should typically be called before the filesystem | ||
479 | * releases resources associated with the freed range (eg. deallocates | ||
480 | * blocks). This way, pagecache will always stay logically coherent | ||
481 | * with on-disk format, and the filesystem would not have to deal with | ||
482 | * situations such as writepage being called for a page that has already | ||
483 | * had its underlying blocks deallocated. | ||
484 | */ | ||
485 | void truncate_pagecache(struct inode *inode, loff_t old, loff_t new) | ||
486 | { | ||
487 | if (new < old) { | ||
488 | struct address_space *mapping = inode->i_mapping; | ||
489 | |||
490 | /* | ||
491 | * unmap_mapping_range is called twice, first simply for | ||
492 | * efficiency so that truncate_inode_pages does fewer | ||
493 | * single-page unmaps. However after this first call, and | ||
494 | * before truncate_inode_pages finishes, it is possible for | ||
495 | * private pages to be COWed, which remain after | ||
496 | * truncate_inode_pages finishes, hence the second | ||
497 | * unmap_mapping_range call must be made for correctness. | ||
498 | */ | ||
499 | unmap_mapping_range(mapping, new + PAGE_SIZE - 1, 0, 1); | ||
500 | truncate_inode_pages(mapping, new); | ||
501 | unmap_mapping_range(mapping, new + PAGE_SIZE - 1, 0, 1); | ||
502 | } | ||
503 | } | ||
504 | EXPORT_SYMBOL(truncate_pagecache); | ||
505 | |||
506 | /** | ||
507 | * vmtruncate - unmap mappings "freed" by truncate() syscall | ||
508 | * @inode: inode of the file used | ||
509 | * @offset: file offset to start truncating | ||
510 | * | ||
511 | * NOTE! We have to be ready to update the memory sharing | ||
512 | * between the file and the memory map for a potential last | ||
513 | * incomplete page. Ugly, but necessary. | ||
514 | */ | ||
515 | int vmtruncate(struct inode *inode, loff_t offset) | ||
516 | { | ||
517 | loff_t oldsize; | ||
518 | int error; | ||
519 | |||
520 | error = inode_newsize_ok(inode, offset); | ||
521 | if (error) | ||
522 | return error; | ||
523 | oldsize = inode->i_size; | ||
524 | i_size_write(inode, offset); | ||
525 | truncate_pagecache(inode, oldsize, offset); | ||
526 | if (inode->i_op->truncate) | ||
527 | inode->i_op->truncate(inode); | ||
528 | |||
529 | return error; | ||
530 | } | ||
531 | EXPORT_SYMBOL(vmtruncate); | ||