diff options
Diffstat (limited to 'mm/page-writeback.c')
| -rw-r--r-- | mm/page-writeback.c | 114 | 
1 files changed, 80 insertions, 34 deletions
diff --git a/mm/page-writeback.c b/mm/page-writeback.c index a794945fd19..eec1481ba44 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c @@ -119,6 +119,44 @@ static void background_writeout(unsigned long _min_pages);   * We make sure that the background writeout level is below the adjusted   * clamping level.   */ + +static unsigned long highmem_dirtyable_memory(unsigned long total) +{ +#ifdef CONFIG_HIGHMEM +	int node; +	unsigned long x = 0; + +	for_each_online_node(node) { +		struct zone *z = +			&NODE_DATA(node)->node_zones[ZONE_HIGHMEM]; + +		x += zone_page_state(z, NR_FREE_PAGES) +			+ zone_page_state(z, NR_INACTIVE) +			+ zone_page_state(z, NR_ACTIVE); +	} +	/* +	 * Make sure that the number of highmem pages is never larger +	 * than the number of the total dirtyable memory. This can only +	 * occur in very strange VM situations but we want to make sure +	 * that this does not occur. +	 */ +	return min(x, total); +#else +	return 0; +#endif +} + +static unsigned long determine_dirtyable_memory(void) +{ +	unsigned long x; + +	x = global_page_state(NR_FREE_PAGES) +		+ global_page_state(NR_INACTIVE) +		+ global_page_state(NR_ACTIVE); +	x -= highmem_dirtyable_memory(x); +	return x + 1;	/* Ensure that we never return 0 */ +} +  static void  get_dirty_limits(long *pbackground, long *pdirty,  					struct address_space *mapping) @@ -128,20 +166,12 @@ get_dirty_limits(long *pbackground, long *pdirty,  	int unmapped_ratio;  	long background;  	long dirty; -	unsigned long available_memory = vm_total_pages; +	unsigned long available_memory = determine_dirtyable_memory();  	struct task_struct *tsk; -#ifdef CONFIG_HIGHMEM -	/* -	 * We always exclude high memory from our count. -	 */ -	available_memory -= totalhigh_pages; -#endif - -  	unmapped_ratio = 100 - ((global_page_state(NR_FILE_MAPPED) +  				global_page_state(NR_ANON_PAGES)) * 100) / -					vm_total_pages; +					available_memory;  	dirty_ratio = vm_dirty_ratio;  	if (dirty_ratio > unmapped_ratio / 2) @@ -558,31 +588,27 @@ void __init page_writeback_init(void)  }  /** - * generic_writepages - walk the list of dirty pages of the given address space and writepage() all of them. + * write_cache_pages - walk the list of dirty pages of the given address space and write all of them.   * @mapping: address space structure to write   * @wbc: subtract the number of written pages from *@wbc->nr_to_write + * @writepage: function called for each page + * @data: data passed to writepage function   * - * This is a library function, which implements the writepages() - * address_space_operation. - * - * If a page is already under I/O, generic_writepages() skips it, even + * If a page is already under I/O, write_cache_pages() skips it, even   * if it's dirty.  This is desirable behaviour for memory-cleaning writeback,   * but it is INCORRECT for data-integrity system calls such as fsync().  fsync()   * and msync() need to guarantee that all the data which was dirty at the time   * the call was made get new I/O started against them.  If wbc->sync_mode is   * WB_SYNC_ALL then we were called for data integrity and we must wait for   * existing IO to complete. - * - * Derived from mpage_writepages() - if you fix this you should check that - * also!   */ -int generic_writepages(struct address_space *mapping, -		       struct writeback_control *wbc) +int write_cache_pages(struct address_space *mapping, +		      struct writeback_control *wbc, writepage_t writepage, +		      void *data)  {  	struct backing_dev_info *bdi = mapping->backing_dev_info;  	int ret = 0;  	int done = 0; -	int (*writepage)(struct page *page, struct writeback_control *wbc);  	struct pagevec pvec;  	int nr_pages;  	pgoff_t index; @@ -595,12 +621,6 @@ int generic_writepages(struct address_space *mapping,  		return 0;  	} -	writepage = mapping->a_ops->writepage; - -	/* deal with chardevs and other special file */ -	if (!writepage) -		return 0; -  	pagevec_init(&pvec, 0);  	if (wbc->range_cyclic) {  		index = mapping->writeback_index; /* Start from prev offset */ @@ -652,13 +672,7 @@ retry:  				continue;  			} -			ret = (*writepage)(page, wbc); -			if (ret) { -				if (ret == -ENOSPC) -					set_bit(AS_ENOSPC, &mapping->flags); -				else -					set_bit(AS_EIO, &mapping->flags); -			} +			ret = (*writepage)(page, wbc, data);  			if (unlikely(ret == AOP_WRITEPAGE_ACTIVATE))  				unlock_page(page); @@ -685,6 +699,38 @@ retry:  		mapping->writeback_index = index;  	return ret;  } +EXPORT_SYMBOL(write_cache_pages); + +/* + * Function used by generic_writepages to call the real writepage + * function and set the mapping flags on error + */ +static int __writepage(struct page *page, struct writeback_control *wbc, +		       void *data) +{ +	struct address_space *mapping = data; +	int ret = mapping->a_ops->writepage(page, wbc); +	mapping_set_error(mapping, ret); +	return ret; +} + +/** + * generic_writepages - walk the list of dirty pages of the given address space and writepage() all of them. + * @mapping: address space structure to write + * @wbc: subtract the number of written pages from *@wbc->nr_to_write + * + * This is a library function, which implements the writepages() + * address_space_operation. + */ +int generic_writepages(struct address_space *mapping, +		       struct writeback_control *wbc) +{ +	/* deal with chardevs and other special file */ +	if (!mapping->a_ops->writepage) +		return 0; + +	return write_cache_pages(mapping, wbc, __writepage, mapping); +}  EXPORT_SYMBOL(generic_writepages);  | 
