diff options
Diffstat (limited to 'fs/buffer.c')
| -rw-r--r-- | fs/buffer.c | 88 | 
1 files changed, 50 insertions, 38 deletions
diff --git a/fs/buffer.c b/fs/buffer.c index cabc045f483d..4cc679d5bf58 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -45,6 +45,7 @@  #include <linux/mpage.h>  #include <linux/bit_spinlock.h>  #include <linux/pagevec.h> +#include <linux/sched/mm.h>  #include <trace/events/block.h>  static int fsync_buffers_list(spinlock_t *lock, struct list_head *list); @@ -813,12 +814,16 @@ struct buffer_head *alloc_page_buffers(struct page *page, unsigned long size,  		bool retry)  {  	struct buffer_head *bh, *head; -	gfp_t gfp = GFP_NOFS; +	gfp_t gfp = GFP_NOFS | __GFP_ACCOUNT;  	long offset; +	struct mem_cgroup *memcg;  	if (retry)  		gfp |= __GFP_NOFAIL; +	memcg = get_mem_cgroup_from_page(page); +	memalloc_use_memcg(memcg); +  	head = NULL;  	offset = PAGE_SIZE;  	while ((offset -= size) >= 0) { @@ -835,6 +840,9 @@ struct buffer_head *alloc_page_buffers(struct page *page, unsigned long size,  		/* Link the buffer to its page */  		set_bh_page(bh, page, offset);  	} +out: +	memalloc_unuse_memcg(); +	mem_cgroup_put(memcg);  	return head;  /*   * In case anything failed, we just free everything we got. @@ -848,7 +856,7 @@ no_grow:  		} while (head);  	} -	return NULL; +	goto out;  }  EXPORT_SYMBOL_GPL(alloc_page_buffers); @@ -1900,15 +1908,16 @@ iomap_to_bh(struct inode *inode, sector_t block, struct buffer_head *bh,  		break;  	case IOMAP_UNWRITTEN:  		/* -		 * For unwritten regions, we always need to ensure that -		 * sub-block writes cause the regions in the block we are not -		 * writing to are zeroed. Set the buffer as new to ensure this. +		 * For unwritten regions, we always need to ensure that regions +		 * in the block we are not writing to are zeroed. Mark the +		 * buffer as new to ensure this.  		 */  		set_buffer_new(bh);  		set_buffer_unwritten(bh);  		/* FALLTHRU */  	case IOMAP_MAPPED: -		if (offset >= i_size_read(inode)) +		if ((iomap->flags & IOMAP_F_NEW) || +		    offset >= i_size_read(inode))  			set_buffer_new(bh);  		bh->b_blocknr = (iomap->addr + offset - iomap->offset) >>  				inode->i_blkbits; @@ -2076,6 +2085,40 @@ int block_write_begin(struct address_space *mapping, loff_t pos, unsigned len,  }  EXPORT_SYMBOL(block_write_begin); +int __generic_write_end(struct inode *inode, loff_t pos, unsigned copied, +		struct page *page) +{ +	loff_t old_size = inode->i_size; +	bool i_size_changed = false; + +	/* +	 * No need to use i_size_read() here, the i_size cannot change under us +	 * because we hold i_rwsem. +	 * +	 * But it's important to update i_size while still holding page lock: +	 * page writeout could otherwise come in and zero beyond i_size. +	 */ +	if (pos + copied > inode->i_size) { +		i_size_write(inode, pos + copied); +		i_size_changed = true; +	} + +	unlock_page(page); +	put_page(page); + +	if (old_size < pos) +		pagecache_isize_extended(inode, old_size, pos); +	/* +	 * Don't mark the inode dirty under page lock. First, it unnecessarily +	 * makes the holding time of page lock longer. Second, it forces lock +	 * ordering of page lock and transaction start for journaling +	 * filesystems. +	 */ +	if (i_size_changed) +		mark_inode_dirty(inode); +	return copied; +} +  int block_write_end(struct file *file, struct address_space *mapping,  			loff_t pos, unsigned len, unsigned copied,  			struct page *page, void *fsdata) @@ -2116,39 +2159,8 @@ int generic_write_end(struct file *file, struct address_space *mapping,  			loff_t pos, unsigned len, unsigned copied,  			struct page *page, void *fsdata)  { -	struct inode *inode = mapping->host; -	loff_t old_size = inode->i_size; -	int i_size_changed = 0; -  	copied = block_write_end(file, mapping, pos, len, copied, page, fsdata); - -	/* -	 * No need to use i_size_read() here, the i_size -	 * cannot change under us because we hold i_mutex. -	 * -	 * But it's important to update i_size while still holding page lock: -	 * page writeout could otherwise come in and zero beyond i_size. -	 */ -	if (pos+copied > inode->i_size) { -		i_size_write(inode, pos+copied); -		i_size_changed = 1; -	} - -	unlock_page(page); -	put_page(page); - -	if (old_size < pos) -		pagecache_isize_extended(inode, old_size, pos); -	/* -	 * Don't mark the inode dirty under page lock. First, it unnecessarily -	 * makes the holding time of page lock longer. Second, it forces lock -	 * ordering of page lock and transaction start for journaling -	 * filesystems. -	 */ -	if (i_size_changed) -		mark_inode_dirty(inode); - -	return copied; +	return __generic_write_end(mapping->host, pos, copied, page);  }  EXPORT_SYMBOL(generic_write_end);  |