diff options
Diffstat (limited to 'mm/shmem.c')
-rw-r--r-- | mm/shmem.c | 34 |
1 files changed, 25 insertions, 9 deletions
diff --git a/mm/shmem.c b/mm/shmem.c index d9fc277940da..a1f2f02af724 100644 --- a/mm/shmem.c +++ b/mm/shmem.c | |||
@@ -457,7 +457,7 @@ static void shmem_free_pages(struct list_head *next) | |||
457 | } while (next); | 457 | } while (next); |
458 | } | 458 | } |
459 | 459 | ||
460 | static void shmem_truncate(struct inode *inode) | 460 | static void shmem_truncate_range(struct inode *inode, loff_t start, loff_t end) |
461 | { | 461 | { |
462 | struct shmem_inode_info *info = SHMEM_I(inode); | 462 | struct shmem_inode_info *info = SHMEM_I(inode); |
463 | unsigned long idx; | 463 | unsigned long idx; |
@@ -475,18 +475,27 @@ static void shmem_truncate(struct inode *inode) | |||
475 | long nr_swaps_freed = 0; | 475 | long nr_swaps_freed = 0; |
476 | int offset; | 476 | int offset; |
477 | int freed; | 477 | int freed; |
478 | int punch_hole = 0; | ||
478 | 479 | ||
479 | inode->i_ctime = inode->i_mtime = CURRENT_TIME; | 480 | inode->i_ctime = inode->i_mtime = CURRENT_TIME; |
480 | idx = (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; | 481 | idx = (start + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; |
481 | if (idx >= info->next_index) | 482 | if (idx >= info->next_index) |
482 | return; | 483 | return; |
483 | 484 | ||
484 | spin_lock(&info->lock); | 485 | spin_lock(&info->lock); |
485 | info->flags |= SHMEM_TRUNCATE; | 486 | info->flags |= SHMEM_TRUNCATE; |
486 | limit = info->next_index; | 487 | if (likely(end == (loff_t) -1)) { |
487 | info->next_index = idx; | 488 | limit = info->next_index; |
489 | info->next_index = idx; | ||
490 | } else { | ||
491 | limit = (end + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; | ||
492 | if (limit > info->next_index) | ||
493 | limit = info->next_index; | ||
494 | punch_hole = 1; | ||
495 | } | ||
496 | |||
488 | topdir = info->i_indirect; | 497 | topdir = info->i_indirect; |
489 | if (topdir && idx <= SHMEM_NR_DIRECT) { | 498 | if (topdir && idx <= SHMEM_NR_DIRECT && !punch_hole) { |
490 | info->i_indirect = NULL; | 499 | info->i_indirect = NULL; |
491 | nr_pages_to_free++; | 500 | nr_pages_to_free++; |
492 | list_add(&topdir->lru, &pages_to_free); | 501 | list_add(&topdir->lru, &pages_to_free); |
@@ -573,11 +582,12 @@ static void shmem_truncate(struct inode *inode) | |||
573 | set_page_private(subdir, page_private(subdir) - freed); | 582 | set_page_private(subdir, page_private(subdir) - freed); |
574 | if (offset) | 583 | if (offset) |
575 | spin_unlock(&info->lock); | 584 | spin_unlock(&info->lock); |
576 | BUG_ON(page_private(subdir) > offset); | 585 | if (!punch_hole) |
586 | BUG_ON(page_private(subdir) > offset); | ||
577 | } | 587 | } |
578 | if (offset) | 588 | if (offset) |
579 | offset = 0; | 589 | offset = 0; |
580 | else if (subdir) { | 590 | else if (subdir && !page_private(subdir)) { |
581 | dir[diroff] = NULL; | 591 | dir[diroff] = NULL; |
582 | nr_pages_to_free++; | 592 | nr_pages_to_free++; |
583 | list_add(&subdir->lru, &pages_to_free); | 593 | list_add(&subdir->lru, &pages_to_free); |
@@ -594,7 +604,7 @@ done2: | |||
594 | * Also, though shmem_getpage checks i_size before adding to | 604 | * Also, though shmem_getpage checks i_size before adding to |
595 | * cache, no recheck after: so fix the narrow window there too. | 605 | * cache, no recheck after: so fix the narrow window there too. |
596 | */ | 606 | */ |
597 | truncate_inode_pages(inode->i_mapping, inode->i_size); | 607 | truncate_inode_pages_range(inode->i_mapping, start, end); |
598 | } | 608 | } |
599 | 609 | ||
600 | spin_lock(&info->lock); | 610 | spin_lock(&info->lock); |
@@ -614,6 +624,11 @@ done2: | |||
614 | } | 624 | } |
615 | } | 625 | } |
616 | 626 | ||
627 | static void shmem_truncate(struct inode *inode) | ||
628 | { | ||
629 | shmem_truncate_range(inode, inode->i_size, (loff_t)-1); | ||
630 | } | ||
631 | |||
617 | static int shmem_notify_change(struct dentry *dentry, struct iattr *attr) | 632 | static int shmem_notify_change(struct dentry *dentry, struct iattr *attr) |
618 | { | 633 | { |
619 | struct inode *inode = dentry->d_inode; | 634 | struct inode *inode = dentry->d_inode; |
@@ -1255,7 +1270,7 @@ out_nomem: | |||
1255 | return retval; | 1270 | return retval; |
1256 | } | 1271 | } |
1257 | 1272 | ||
1258 | static int shmem_mmap(struct file *file, struct vm_area_struct *vma) | 1273 | int shmem_mmap(struct file *file, struct vm_area_struct *vma) |
1259 | { | 1274 | { |
1260 | file_accessed(file); | 1275 | file_accessed(file); |
1261 | vma->vm_ops = &shmem_vm_ops; | 1276 | vma->vm_ops = &shmem_vm_ops; |
@@ -2083,6 +2098,7 @@ static struct file_operations shmem_file_operations = { | |||
2083 | static struct inode_operations shmem_inode_operations = { | 2098 | static struct inode_operations shmem_inode_operations = { |
2084 | .truncate = shmem_truncate, | 2099 | .truncate = shmem_truncate, |
2085 | .setattr = shmem_notify_change, | 2100 | .setattr = shmem_notify_change, |
2101 | .truncate_range = shmem_truncate_range, | ||
2086 | }; | 2102 | }; |
2087 | 2103 | ||
2088 | static struct inode_operations shmem_dir_inode_operations = { | 2104 | static struct inode_operations shmem_dir_inode_operations = { |