Re: [PATCH 19/23] truncate: support huge pages

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Sun 04-08-13 05:17:21, Kirill A. Shutemov wrote:
> From: "Kirill A. Shutemov" <kirill.shutemov@xxxxxxxxxxxxxxx>
> 
> truncate_inode_pages_range() drops whole huge page at once if it's fully
> inside the range.
> 
> If a huge page is only partly in the range we zero out the part,
> exactly like we do for partial small pages.
> 
> invalidate_mapping_pages() just skips huge pages if they are not fully
> in the range.
  Umm, this is not a new problem but with THP pagecache it will become more
visible: When we punch holes within a file like <0..2MB>, <2MB-4MB>
(presuming 4 MB hugepages), then we won't free the underlying huge page for
the range 0..4MB. Maybe for initial implementation is doesn't matter but we
should at least note it in truncate_inode_pages_range() so that people are
aware of this.

Otherwise the patch looks OK to me. So you can add:
Reviewed-by: Jan Kara <jack@xxxxxxx>

								Honza

> Signed-off-by: Kirill A. Shutemov <kirill.shutemov@xxxxxxxxxxxxxxx>
> ---
>  mm/truncate.c | 108 +++++++++++++++++++++++++++++++++++++++++++++-------------
>  1 file changed, 84 insertions(+), 24 deletions(-)
> 
> diff --git a/mm/truncate.c b/mm/truncate.c
> index 353b683..fcef7cb 100644
> --- a/mm/truncate.c
> +++ b/mm/truncate.c
> @@ -205,8 +205,7 @@ void truncate_inode_pages_range(struct address_space *mapping,
>  {
>  	pgoff_t		start;		/* inclusive */
>  	pgoff_t		end;		/* exclusive */
> -	unsigned int	partial_start;	/* inclusive */
> -	unsigned int	partial_end;	/* exclusive */
> +	bool		partial_thp_start = false, partial_thp_end = false;
>  	struct pagevec	pvec;
>  	pgoff_t		index;
>  	int		i;
> @@ -215,15 +214,9 @@ void truncate_inode_pages_range(struct address_space *mapping,
>  	if (mapping->nrpages == 0)
>  		return;
>  
> -	/* Offsets within partial pages */
> -	partial_start = lstart & (PAGE_CACHE_SIZE - 1);
> -	partial_end = (lend + 1) & (PAGE_CACHE_SIZE - 1);
> -
>  	/*
>  	 * 'start' and 'end' always covers the range of pages to be fully
> -	 * truncated. Partial pages are covered with 'partial_start' at the
> -	 * start of the range and 'partial_end' at the end of the range.
> -	 * Note that 'end' is exclusive while 'lend' is inclusive.
> +	 * truncated. Note that 'end' is exclusive while 'lend' is inclusive.
>  	 */
>  	start = (lstart + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
>  	if (lend == -1)
> @@ -249,6 +242,23 @@ void truncate_inode_pages_range(struct address_space *mapping,
>  			if (index >= end)
>  				break;
>  
> +			if (PageTransTailCache(page)) {
> +				/* part of already handled huge page */
> +				if (!page->mapping)
> +					continue;
> +				/* the range starts in middle of huge page */
> +				partial_thp_start = true;
> +				start = index & ~HPAGE_CACHE_INDEX_MASK;
> +				continue;
> +			}
> +			/* the range ends on huge page */
> +			if (PageTransHugeCache(page) &&
> +				index == (end & ~HPAGE_CACHE_INDEX_MASK)) {
> +				partial_thp_end = true;
> +				end = index;
> +				break;
> +			}
> +
>  			if (!trylock_page(page))
>  				continue;
>  			WARN_ON(page->index != index);
> @@ -265,34 +275,74 @@ void truncate_inode_pages_range(struct address_space *mapping,
>  		index++;
>  	}
>  
> -	if (partial_start) {
> -		struct page *page = find_lock_page(mapping, start - 1);
> +	if (partial_thp_start || lstart & ~PAGE_CACHE_MASK) {
> +		pgoff_t off;
> +		struct page *page;
> +		unsigned pstart, pend;
> +		void (*zero_segment)(struct page *page,
> +				unsigned start, unsigned len);
> +retry_partial_start:
> +		if (partial_thp_start) {
> +			zero_segment = zero_huge_user_segment;
> +			off = (start - 1) & ~HPAGE_CACHE_INDEX_MASK;
> +			pstart = lstart & ~HPAGE_PMD_MASK;
> +			if ((end & ~HPAGE_CACHE_INDEX_MASK) == off)
> +				pend = (lend - 1) & ~HPAGE_PMD_MASK;
> +			else
> +				pend = HPAGE_PMD_SIZE;
> +		} else {
> +			zero_segment = zero_user_segment;
> +			off = start - 1;
> +			pstart = lstart & ~PAGE_CACHE_MASK;
> +			if (start > end)
> +				pend = (lend - 1) & ~PAGE_CACHE_MASK;
> +			else
> +				pend = PAGE_CACHE_SIZE;
> +		}
> +
> +		page = find_get_page(mapping, off);
>  		if (page) {
> -			unsigned int top = PAGE_CACHE_SIZE;
> -			if (start > end) {
> -				/* Truncation within a single page */
> -				top = partial_end;
> -				partial_end = 0;
> +			/* the last tail page*/
> +			if (PageTransTailCache(page)) {
> +				partial_thp_start = true;
> +				page_cache_release(page);
> +				goto retry_partial_start;
>  			}
> +
> +			lock_page(page);
>  			wait_on_page_writeback(page);
> -			zero_user_segment(page, partial_start, top);
> +			zero_segment(page, pstart, pend);
>  			cleancache_invalidate_page(mapping, page);
>  			if (page_has_private(page))
> -				do_invalidatepage(page, partial_start,
> -						  top - partial_start);
> +				do_invalidatepage(page, pstart,
> +						pend - pstart);
>  			unlock_page(page);
>  			page_cache_release(page);
>  		}
>  	}
> -	if (partial_end) {
> -		struct page *page = find_lock_page(mapping, end);
> +	if (partial_thp_end || (lend + 1) & ~PAGE_CACHE_MASK) {
> +		pgoff_t off;
> +		struct page *page;
> +		unsigned pend;
> +		void (*zero_segment)(struct page *page,
> +				unsigned start, unsigned len);
> +		if (partial_thp_end) {
> +			zero_segment = zero_huge_user_segment;
> +			off = end & ~HPAGE_CACHE_INDEX_MASK;
> +			pend = (lend - 1) & ~HPAGE_PMD_MASK;
> +		} else {
> +			zero_segment = zero_user_segment;
> +			off = end;
> +			pend = (lend - 1) & ~PAGE_CACHE_MASK;
> +		}
> +
> +		page = find_lock_page(mapping, end);
>  		if (page) {
>  			wait_on_page_writeback(page);
> -			zero_user_segment(page, 0, partial_end);
> +			zero_segment(page, 0, pend);
>  			cleancache_invalidate_page(mapping, page);
>  			if (page_has_private(page))
> -				do_invalidatepage(page, 0,
> -						  partial_end);
> +				do_invalidatepage(page, 0, pend);
>  			unlock_page(page);
>  			page_cache_release(page);
>  		}
> @@ -327,6 +377,9 @@ void truncate_inode_pages_range(struct address_space *mapping,
>  			if (index >= end)
>  				break;
>  
> +			if (PageTransTailCache(page))
> +				continue;
> +
>  			lock_page(page);
>  			WARN_ON(page->index != index);
>  			wait_on_page_writeback(page);
> @@ -401,6 +454,13 @@ unsigned long invalidate_mapping_pages(struct address_space *mapping,
>  			if (index > end)
>  				break;
>  
> +			/* skip huge page if it's not fully in the range */
> +			if (PageTransHugeCache(page) &&
> +					index + HPAGE_CACHE_NR - 1 > end)
> +				continue;
> +			if (PageTransTailCache(page))
> +				continue;
> +
>  			if (!trylock_page(page))
>  				continue;
>  			WARN_ON(page->index != index);
> -- 
> 1.8.3.2
> 
-- 
Jan Kara <jack@xxxxxxx>
SUSE Labs, CR

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@xxxxxxxxx.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@xxxxxxxxx";> email@xxxxxxxxx </a>




[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Bugtraq]     [Linux]     [Linux OMAP]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]