| 
						
					 | 
				
			
			 | 
			 | 
			
				@ -173,10 +173,13 @@ static inline int shmem_reacct_size(unsigned long flags,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				 * shmem_getpage reports shmem_acct_block failure as -ENOSPC not -ENOMEM,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				 * so that a failure on a sparse tmpfs mapping will give SIGBUS not OOM.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				 */
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				static inline int shmem_acct_block(unsigned long flags)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				static inline int shmem_acct_block(unsigned long flags, long pages)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					return (flags & VM_NORESERVE) ?
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						security_vm_enough_memory_mm(current->mm, VM_ACCT(PAGE_SIZE)) : 0;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if (!(flags & VM_NORESERVE))
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						return 0;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					return security_vm_enough_memory_mm(current->mm,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							pages * VM_ACCT(PAGE_SIZE));
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				static inline void shmem_unacct_blocks(unsigned long flags, long pages)
 | 
			
		
		
	
	
		
			
				
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@ -249,6 +252,51 @@ static void shmem_recalc_inode(struct inode *inode)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				bool shmem_charge(struct inode *inode, long pages)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					struct shmem_inode_info *info = SHMEM_I(inode);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if (shmem_acct_block(info->flags, pages))
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						return false;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					spin_lock(&info->lock);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					info->alloced += pages;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					inode->i_blocks += pages * BLOCKS_PER_PAGE;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					shmem_recalc_inode(inode);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					spin_unlock(&info->lock);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					inode->i_mapping->nrpages += pages;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if (!sbinfo->max_blocks)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						return true;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if (percpu_counter_compare(&sbinfo->used_blocks,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								sbinfo->max_blocks - pages) > 0) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						inode->i_mapping->nrpages -= pages;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						spin_lock(&info->lock);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						info->alloced -= pages;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						shmem_recalc_inode(inode);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						spin_unlock(&info->lock);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						return false;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					percpu_counter_add(&sbinfo->used_blocks, pages);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					return true;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				void shmem_uncharge(struct inode *inode, long pages)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					struct shmem_inode_info *info = SHMEM_I(inode);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					spin_lock(&info->lock);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					info->alloced -= pages;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					inode->i_blocks -= pages * BLOCKS_PER_PAGE;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					shmem_recalc_inode(inode);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					spin_unlock(&info->lock);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if (sbinfo->max_blocks)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						percpu_counter_sub(&sbinfo->used_blocks, pages);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				/*
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				 * Replace item expected in radix tree by a new item, while holding tree lock.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				 */
 | 
			
		
		
	
	
		
			
				
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@ -376,30 +424,57 @@ static int shmem_add_to_page_cache(struct page *page,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								   struct address_space *mapping,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								   pgoff_t index, void *expected)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					int error;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					int error, nr = hpage_nr_pages(page);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					VM_BUG_ON_PAGE(PageTail(page), page);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					VM_BUG_ON_PAGE(index != round_down(index, nr), page);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					VM_BUG_ON_PAGE(!PageLocked(page), page);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					VM_BUG_ON_PAGE(!PageSwapBacked(page), page);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					VM_BUG_ON(expected && PageTransHuge(page));
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					get_page(page);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					page_ref_add(page, nr);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					page->mapping = mapping;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					page->index = index;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					spin_lock_irq(&mapping->tree_lock);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if (!expected)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if (PageTransHuge(page)) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						void __rcu **results;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						pgoff_t idx;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						int i;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						error = 0;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						if (radix_tree_gang_lookup_slot(&mapping->page_tree,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
									&results, &idx, index, 1) &&
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								idx < index + HPAGE_PMD_NR) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							error = -EEXIST;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						if (!error) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							for (i = 0; i < HPAGE_PMD_NR; i++) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								error = radix_tree_insert(&mapping->page_tree,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
										index + i, page + i);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								VM_BUG_ON(error);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							count_vm_event(THP_FILE_ALLOC);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					} else if (!expected) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						error = radix_tree_insert(&mapping->page_tree, index, page);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					else
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					} else {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						error = shmem_radix_tree_replace(mapping, index, expected,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
												 page);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if (!error) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						mapping->nrpages++;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						__inc_zone_page_state(page, NR_FILE_PAGES);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						__inc_zone_page_state(page, NR_SHMEM);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						mapping->nrpages += nr;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						if (PageTransHuge(page))
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							__inc_zone_page_state(page, NR_SHMEM_THPS);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						__mod_zone_page_state(page_zone(page), NR_FILE_PAGES, nr);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						__mod_zone_page_state(page_zone(page), NR_SHMEM, nr);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						spin_unlock_irq(&mapping->tree_lock);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					} else {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						page->mapping = NULL;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						spin_unlock_irq(&mapping->tree_lock);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						put_page(page);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						page_ref_sub(page, nr);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					return error;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
	
		
			
				
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@ -412,6 +487,8 @@ static void shmem_delete_from_page_cache(struct page *page, void *radswap)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					struct address_space *mapping = page->mapping;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					int error;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					VM_BUG_ON_PAGE(PageCompound(page), page);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					spin_lock_irq(&mapping->tree_lock);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					error = shmem_radix_tree_replace(mapping, page->index, page, radswap);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					page->mapping = NULL;
 | 
			
		
		
	
	
		
			
				
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@ -591,10 +668,33 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, loff_t lend,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								continue;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							VM_BUG_ON_PAGE(page_to_pgoff(page) != index, page);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							if (!trylock_page(page))
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								continue;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							if (PageTransTail(page)) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								/* Middle of THP: zero out the page */
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								clear_highpage(page);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								unlock_page(page);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								continue;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							} else if (PageTransHuge(page)) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								if (index == round_down(end, HPAGE_PMD_NR)) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
									/*
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
									 * Range ends in the middle of THP:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
									 * zero out the page
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
									 */
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
									clear_highpage(page);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
									unlock_page(page);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
									continue;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								index += HPAGE_PMD_NR - 1;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								i += HPAGE_PMD_NR - 1;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							if (!unfalloc || !PageUptodate(page)) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								if (page->mapping == mapping) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								VM_BUG_ON_PAGE(PageTail(page), page);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								if (page_mapping(page) == mapping) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
									VM_BUG_ON_PAGE(PageWriteback(page), page);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
									truncate_inode_page(mapping, page);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								}
 | 
			
		
		
	
	
		
			
				
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@ -670,8 +770,36 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, loff_t lend,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							lock_page(page);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							if (PageTransTail(page)) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								/* Middle of THP: zero out the page */
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								clear_highpage(page);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								unlock_page(page);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								/*
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								 * Partial thp truncate due 'start' in middle
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								 * of THP: don't need to look on these pages
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								 * again on !pvec.nr restart.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								 */
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								if (index != round_down(end, HPAGE_PMD_NR))
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
									start++;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								continue;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							} else if (PageTransHuge(page)) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								if (index == round_down(end, HPAGE_PMD_NR)) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
									/*
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
									 * Range ends in the middle of THP:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
									 * zero out the page
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
									 */
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
									clear_highpage(page);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
									unlock_page(page);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
									continue;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								index += HPAGE_PMD_NR - 1;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								i += HPAGE_PMD_NR - 1;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							if (!unfalloc || !PageUptodate(page)) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								if (page->mapping == mapping) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								VM_BUG_ON_PAGE(PageTail(page), page);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								if (page_mapping(page) == mapping) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
									VM_BUG_ON_PAGE(PageWriteback(page), page);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
									truncate_inode_page(mapping, page);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								} else {
 | 
			
		
		
	
	
		
			
				
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@ -929,6 +1057,7 @@ static int shmem_writepage(struct page *page, struct writeback_control *wbc)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					swp_entry_t swap;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					pgoff_t index;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					VM_BUG_ON_PAGE(PageCompound(page), page);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					BUG_ON(!PageLocked(page));
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					mapping = page->mapping;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					index = page->index;
 | 
			
		
		
	
	
		
			
				
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@ -1065,24 +1194,63 @@ static inline struct mempolicy *shmem_get_sbmpol(struct shmem_sb_info *sbinfo)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				#define vm_policy vm_private_data
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				#endif
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				static void shmem_pseudo_vma_init(struct vm_area_struct *vma,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						struct shmem_inode_info *info, pgoff_t index)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					/* Create a pseudo vma that just contains the policy */
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					vma->vm_start = 0;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					/* Bias interleave by inode number to distribute better across nodes */
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					vma->vm_pgoff = index + info->vfs_inode.i_ino;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					vma->vm_ops = NULL;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					vma->vm_policy = mpol_shared_policy_lookup(&info->policy, index);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				static void shmem_pseudo_vma_destroy(struct vm_area_struct *vma)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					/* Drop reference taken by mpol_shared_policy_lookup() */
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					mpol_cond_put(vma->vm_policy);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				static struct page *shmem_swapin(swp_entry_t swap, gfp_t gfp,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							struct shmem_inode_info *info, pgoff_t index)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					struct vm_area_struct pvma;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					struct page *page;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					/* Create a pseudo vma that just contains the policy */
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					pvma.vm_start = 0;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					/* Bias interleave by inode number to distribute better across nodes */
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					pvma.vm_pgoff = index + info->vfs_inode.i_ino;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					pvma.vm_ops = NULL;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					pvma.vm_policy = mpol_shared_policy_lookup(&info->policy, index);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					shmem_pseudo_vma_init(&pvma, info, index);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					page = swapin_readahead(swap, gfp, &pvma, 0);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					shmem_pseudo_vma_destroy(&pvma);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					/* Drop reference taken by mpol_shared_policy_lookup() */
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					mpol_cond_put(pvma.vm_policy);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					return page;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				static struct page *shmem_alloc_hugepage(gfp_t gfp,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						struct shmem_inode_info *info, pgoff_t index)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					struct vm_area_struct pvma;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					struct inode *inode = &info->vfs_inode;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					struct address_space *mapping = inode->i_mapping;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					pgoff_t idx, hindex = round_down(index, HPAGE_PMD_NR);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					void __rcu **results;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					struct page *page;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE))
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						return NULL;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					rcu_read_lock();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if (radix_tree_gang_lookup_slot(&mapping->page_tree, &results, &idx,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								hindex, 1) && idx < hindex + HPAGE_PMD_NR) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						rcu_read_unlock();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						return NULL;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					rcu_read_unlock();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					shmem_pseudo_vma_init(&pvma, info, hindex);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					page = alloc_pages_vma(gfp | __GFP_COMP | __GFP_NORETRY | __GFP_NOWARN,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							HPAGE_PMD_ORDER, &pvma, 0, numa_node_id(), true);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					shmem_pseudo_vma_destroy(&pvma);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if (page)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						prep_transhuge_page(page);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					return page;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
	
		
			
				
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@ -1092,23 +1260,51 @@ static struct page *shmem_alloc_page(gfp_t gfp,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					struct vm_area_struct pvma;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					struct page *page;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					/* Create a pseudo vma that just contains the policy */
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					pvma.vm_start = 0;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					/* Bias interleave by inode number to distribute better across nodes */
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					pvma.vm_pgoff = index + info->vfs_inode.i_ino;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					pvma.vm_ops = NULL;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					pvma.vm_policy = mpol_shared_policy_lookup(&info->policy, index);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					shmem_pseudo_vma_init(&pvma, info, index);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					page = alloc_page_vma(gfp, &pvma, 0);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					shmem_pseudo_vma_destroy(&pvma);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					page = alloc_pages_vma(gfp, 0, &pvma, 0, numa_node_id(), false);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					return page;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				static struct page *shmem_alloc_and_acct_page(gfp_t gfp,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						struct shmem_inode_info *info, struct shmem_sb_info *sbinfo,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						pgoff_t index, bool huge)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					struct page *page;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					int nr;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					int err = -ENOSPC;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE))
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						huge = false;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					nr = huge ? HPAGE_PMD_NR : 1;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if (shmem_acct_block(info->flags, nr))
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						goto failed;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if (sbinfo->max_blocks) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						if (percpu_counter_compare(&sbinfo->used_blocks,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
									sbinfo->max_blocks - nr) > 0)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							goto unacct;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						percpu_counter_add(&sbinfo->used_blocks, nr);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if (huge)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						page = shmem_alloc_hugepage(gfp, info, index);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					else
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						page = shmem_alloc_page(gfp, info, index);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if (page) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						__SetPageLocked(page);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						__SetPageSwapBacked(page);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						return page;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					/* Drop reference taken by mpol_shared_policy_lookup() */
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					mpol_cond_put(pvma.vm_policy);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					return page;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					err = -ENOMEM;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if (sbinfo->max_blocks)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						percpu_counter_add(&sbinfo->used_blocks, -nr);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				unacct:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					shmem_unacct_blocks(info->flags, nr);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				failed:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					return ERR_PTR(err);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				/*
 | 
			
		
		
	
	
		
			
				
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@ -1213,6 +1409,7 @@ static int shmem_getpage_gfp(struct inode *inode, pgoff_t index,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					struct mem_cgroup *memcg;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					struct page *page;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					swp_entry_t swap;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					pgoff_t hindex = index;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					int error;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					int once = 0;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					int alloced = 0;
 | 
			
		
		
	
	
		
			
				
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@ -1334,47 +1531,74 @@ static int shmem_getpage_gfp(struct inode *inode, pgoff_t index,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						swap_free(swap);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					} else {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						if (shmem_acct_block(info->flags)) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							error = -ENOSPC;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							goto failed;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						if (sbinfo->max_blocks) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							if (percpu_counter_compare(&sbinfo->used_blocks,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
										sbinfo->max_blocks) >= 0) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								error = -ENOSPC;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								goto unacct;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							percpu_counter_inc(&sbinfo->used_blocks);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						/* shmem_symlink() */
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						if (mapping->a_ops != &shmem_aops)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							goto alloc_nohuge;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						if (shmem_huge == SHMEM_HUGE_DENY)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							goto alloc_nohuge;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						if (shmem_huge == SHMEM_HUGE_FORCE)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							goto alloc_huge;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						switch (sbinfo->huge) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							loff_t i_size;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							pgoff_t off;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						case SHMEM_HUGE_NEVER:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							goto alloc_nohuge;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						case SHMEM_HUGE_WITHIN_SIZE:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							off = round_up(index, HPAGE_PMD_NR);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							i_size = round_up(i_size_read(inode), PAGE_SIZE);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							if (i_size >= HPAGE_PMD_SIZE &&
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
									i_size >> PAGE_SHIFT >= off)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								goto alloc_huge;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							/* fallthrough */
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						case SHMEM_HUGE_ADVISE:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							/* TODO: wire up fadvise()/madvise() */
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							goto alloc_nohuge;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						page = shmem_alloc_page(gfp, info, index);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						if (!page) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							error = -ENOMEM;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							goto decused;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				alloc_huge:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						page = shmem_alloc_and_acct_page(gfp, info, sbinfo,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								index, true);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						if (IS_ERR(page)) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				alloc_nohuge:		page = shmem_alloc_and_acct_page(gfp, info, sbinfo,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
									index, false);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						if (IS_ERR(page)) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							error = PTR_ERR(page);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							page = NULL;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							goto failed;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						if (PageTransHuge(page))
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							hindex = round_down(index, HPAGE_PMD_NR);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						else
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							hindex = index;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						if (sgp == SGP_WRITE)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							__SetPageReferenced(page);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						error = mem_cgroup_try_charge(page, charge_mm, gfp, &memcg,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								false);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								PageTransHuge(page));
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						if (error)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							goto decused;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						error = radix_tree_maybe_preload(gfp & GFP_RECLAIM_MASK);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							goto unacct;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						error = radix_tree_maybe_preload_order(gfp & GFP_RECLAIM_MASK,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								compound_order(page));
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						if (!error) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							error = shmem_add_to_page_cache(page, mapping, index,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							error = shmem_add_to_page_cache(page, mapping, hindex,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
											NULL);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							radix_tree_preload_end();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						if (error) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							mem_cgroup_cancel_charge(page, memcg, false);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							goto decused;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							mem_cgroup_cancel_charge(page, memcg,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
									PageTransHuge(page));
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							goto unacct;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						mem_cgroup_commit_charge(page, memcg, false, false);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						mem_cgroup_commit_charge(page, memcg, false,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								PageTransHuge(page));
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						lru_cache_add_anon(page);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						spin_lock(&info->lock);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						info->alloced++;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						inode->i_blocks += BLOCKS_PER_PAGE;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						info->alloced += 1 << compound_order(page);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						inode->i_blocks += BLOCKS_PER_PAGE << compound_order(page);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						shmem_recalc_inode(inode);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						spin_unlock(&info->lock);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						alloced = true;
 | 
			
		
		
	
	
		
			
				
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@ -1390,10 +1614,15 @@ static int shmem_getpage_gfp(struct inode *inode, pgoff_t index,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						 * but SGP_FALLOC on a page fallocated earlier must initialize
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						 * it now, lest undo on failure cancel our earlier guarantee.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						 */
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						if (sgp != SGP_WRITE) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							clear_highpage(page);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							flush_dcache_page(page);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							SetPageUptodate(page);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						if (sgp != SGP_WRITE && !PageUptodate(page)) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							struct page *head = compound_head(page);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							int i;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							for (i = 0; i < (1 << compound_order(head)); i++) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								clear_highpage(head + i);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								flush_dcache_page(head + i);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							SetPageUptodate(head);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
	
		
			
				
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@ -1410,17 +1639,23 @@ static int shmem_getpage_gfp(struct inode *inode, pgoff_t index,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						error = -EINVAL;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						goto unlock;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					*pagep = page;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					*pagep = page + index - hindex;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					return 0;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					/*
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					 * Error recovery.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					 */
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				decused:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if (sbinfo->max_blocks)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						percpu_counter_add(&sbinfo->used_blocks, -1);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				unacct:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					shmem_unacct_blocks(info->flags, 1);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if (sbinfo->max_blocks)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						percpu_counter_sub(&sbinfo->used_blocks,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								1 << compound_order(page));
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					shmem_unacct_blocks(info->flags, 1 << compound_order(page));
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if (PageTransHuge(page)) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						unlock_page(page);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						put_page(page);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						goto alloc_nohuge;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				failed:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if (swap.val && !shmem_confirm_swap(mapping, index, swap))
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						error = -EEXIST;
 | 
			
		
		
	
	
		
			
				
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@ -1758,12 +1993,23 @@ shmem_write_end(struct file *file, struct address_space *mapping,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						i_size_write(inode, pos + copied);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if (!PageUptodate(page)) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						struct page *head = compound_head(page);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						if (PageTransCompound(page)) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							int i;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							for (i = 0; i < HPAGE_PMD_NR; i++) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								if (head + i == page)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
									continue;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								clear_highpage(head + i);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								flush_dcache_page(head + i);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						if (copied < PAGE_SIZE) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							unsigned from = pos & (PAGE_SIZE - 1);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							zero_user_segments(page, 0, from,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
									from + copied, PAGE_SIZE);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						SetPageUptodate(page);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						SetPageUptodate(head);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					set_page_dirty(page);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					unlock_page(page);
 | 
			
		
		
	
	
		
			
				
					| 
						
					 | 
				
			
			 | 
			 | 
			
				
 
 |