Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 283011
b: refs/heads/master
c: a756cf5
h: refs/heads/master
i:
  283009: 4e1125f
  283007: 848b852
v: v3
  • Loading branch information
Johannes Weiner authored and Linus Torvalds committed Jan 11, 2012
1 parent af29799 commit 323feb7
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 2 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: ccafa2879fb8d13b8031337a8743eac4189e5d6e
refs/heads/master: a756cf5908530e8b40bdf569eb48b40139e8d7fd
4 changes: 3 additions & 1 deletion trunk/include/linux/gfp.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ struct vm_area_struct;
#endif
#define ___GFP_NO_KSWAPD 0x400000u
#define ___GFP_OTHER_NODE 0x800000u
#define ___GFP_WRITE 0x1000000u

/*
* GFP bitmasks..
Expand Down Expand Up @@ -85,14 +86,15 @@ struct vm_area_struct;

#define __GFP_NO_KSWAPD ((__force gfp_t)___GFP_NO_KSWAPD)
#define __GFP_OTHER_NODE ((__force gfp_t)___GFP_OTHER_NODE) /* On behalf of other node */
#define __GFP_WRITE ((__force gfp_t)___GFP_WRITE) /* Allocator intends to dirty page */

/*
* This may seem redundant, but it's a way of annotating false positives vs.
* allocations that simply cannot be supported (e.g. page tables).
*/
#define __GFP_NOTRACK_FALSE_POSITIVE (__GFP_NOTRACK)

#define __GFP_BITS_SHIFT 24 /* Room for N __GFP_FOO bits */
#define __GFP_BITS_SHIFT 25 /* Room for N __GFP_FOO bits */
#define __GFP_BITS_MASK ((__force gfp_t)((1 << __GFP_BITS_SHIFT) - 1))

/* This equals 0, but use constants in case they ever change */
Expand Down
1 change: 1 addition & 0 deletions trunk/include/linux/writeback.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ void laptop_mode_timer_fn(unsigned long data);
static inline void laptop_sync_completion(void) { }
#endif
void throttle_vm_writeout(gfp_t gfp_mask);
bool zone_dirty_ok(struct zone *zone);

extern unsigned long global_dirty_limit;

Expand Down
82 changes: 82 additions & 0 deletions trunk/mm/page-writeback.c
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,24 @@ static struct prop_descriptor vm_completions;
* clamping level.
*/

/*
* In a memory zone, there is a certain amount of pages we consider
* available for the page cache, which is essentially the number of
* free and reclaimable pages, minus some zone reserves to protect
* lowmem and the ability to uphold the zone's watermarks without
* requiring writeback.
*
* This number of dirtyable pages is the base value of which the
* user-configurable dirty ratio is the effictive number of pages that
* are allowed to be actually dirtied. Per individual zone, or
* globally by using the sum of dirtyable pages over all zones.
*
* Because the user is allowed to specify the dirty limit globally as
* absolute number of bytes, calculating the per-zone dirty limit can
* require translating the configured limit into a percentage of
* global dirtyable memory first.
*/

static unsigned long highmem_dirtyable_memory(unsigned long total)
{
#ifdef CONFIG_HIGHMEM
Expand Down Expand Up @@ -232,6 +250,70 @@ void global_dirty_limits(unsigned long *pbackground, unsigned long *pdirty)
trace_global_dirty_state(background, dirty);
}

/**
* zone_dirtyable_memory - number of dirtyable pages in a zone
* @zone: the zone
*
* Returns the zone's number of pages potentially available for dirty
* page cache. This is the base value for the per-zone dirty limits.
*/
static unsigned long zone_dirtyable_memory(struct zone *zone)
{
/*
* The effective global number of dirtyable pages may exclude
* highmem as a big-picture measure to keep the ratio between
* dirty memory and lowmem reasonable.
*
* But this function is purely about the individual zone and a
* highmem zone can hold its share of dirty pages, so we don't
* care about vm_highmem_is_dirtyable here.
*/
return zone_page_state(zone, NR_FREE_PAGES) +
zone_reclaimable_pages(zone) -
zone->dirty_balance_reserve;
}

/**
* zone_dirty_limit - maximum number of dirty pages allowed in a zone
* @zone: the zone
*
* Returns the maximum number of dirty pages allowed in a zone, based
* on the zone's dirtyable memory.
*/
static unsigned long zone_dirty_limit(struct zone *zone)
{
unsigned long zone_memory = zone_dirtyable_memory(zone);
struct task_struct *tsk = current;
unsigned long dirty;

if (vm_dirty_bytes)
dirty = DIV_ROUND_UP(vm_dirty_bytes, PAGE_SIZE) *
zone_memory / global_dirtyable_memory();
else
dirty = vm_dirty_ratio * zone_memory / 100;

if (tsk->flags & PF_LESS_THROTTLE || rt_task(tsk))
dirty += dirty / 4;

return dirty;
}

/**
* zone_dirty_ok - tells whether a zone is within its dirty limits
* @zone: the zone to check
*
* Returns %true when the dirty pages in @zone are within the zone's
* dirty limit, %false if the limit is exceeded.
*/
bool zone_dirty_ok(struct zone *zone)
{
unsigned long limit = zone_dirty_limit(zone);

return zone_page_state(zone, NR_FILE_DIRTY) +
zone_page_state(zone, NR_UNSTABLE_NFS) +
zone_page_state(zone, NR_WRITEBACK) <= limit;
}

/*
* couple the period to the dirty_ratio:
*
Expand Down
29 changes: 29 additions & 0 deletions trunk/mm/page_alloc.c
Original file line number Diff line number Diff line change
Expand Up @@ -1735,6 +1735,35 @@ get_page_from_freelist(gfp_t gfp_mask, nodemask_t *nodemask, unsigned int order,
if ((alloc_flags & ALLOC_CPUSET) &&
!cpuset_zone_allowed_softwall(zone, gfp_mask))
continue;
/*
* When allocating a page cache page for writing, we
* want to get it from a zone that is within its dirty
* limit, such that no single zone holds more than its
* proportional share of globally allowed dirty pages.
* The dirty limits take into account the zone's
* lowmem reserves and high watermark so that kswapd
* should be able to balance it without having to
* write pages from its LRU list.
*
* This may look like it could increase pressure on
* lower zones by failing allocations in higher zones
* before they are full. But the pages that do spill
* over are limited as the lower zones are protected
* by this very same mechanism. It should not become
* a practical burden to them.
*
* XXX: For now, allow allocations to potentially
* exceed the per-zone dirty limit in the slowpath
* (ALLOC_WMARK_LOW unset) before going into reclaim,
* which is important when on a NUMA setup the allowed
* zones are together not big enough to reach the
* global limit. The proper fix for these situations
* will require awareness of zones in the
* dirty-throttling and the flusher threads.
*/
if ((alloc_flags & ALLOC_WMARK_LOW) &&
(gfp_mask & __GFP_WRITE) && !zone_dirty_ok(zone))
goto this_zone_full;

BUILD_BUG_ON(ALLOC_NO_WATERMARKS < NR_WMARK);
if (!(alloc_flags & ALLOC_NO_WATERMARKS)) {
Expand Down

0 comments on commit 323feb7

Please sign in to comment.