2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* mm/thrash.c
|
|
|
|
*
|
|
|
|
* Copyright (C) 2004, Red Hat, Inc.
|
|
|
|
* Copyright (C) 2004, Rik van Riel <riel@redhat.com>
|
|
|
|
* Released under the GPL, see the file COPYING for details.
|
|
|
|
*
|
|
|
|
* Simple token based thrashing protection, using the algorithm
|
|
|
|
* described in: http://www.cs.wm.edu/~sjiang/token.pdf
|
|
|
|
*/
|
|
|
|
#include <linux/jiffies.h>
|
|
|
|
#include <linux/mm.h>
|
|
|
|
#include <linux/sched.h>
|
|
|
|
#include <linux/swap.h>
|
|
|
|
|
|
|
|
static DEFINE_SPINLOCK(swap_token_lock);
|
|
|
|
static unsigned long swap_token_timeout;
|
|
|
|
static unsigned long swap_token_check;
|
|
|
|
struct mm_struct * swap_token_mm = &init_mm;
|
|
|
|
|
|
|
|
#define SWAP_TOKEN_CHECK_INTERVAL (HZ * 2)
|
[PATCH] swaptoken tuning
It turns out that the original swap token implementation, by Song Jiang, only
enforced the swap token while the task holding the token is handling a page
fault. This patch approximates that, without adding an additional flag to the
mm_struct, by checking whether the mm->mmap_sem is held for reading, like the
page fault code does.
This patch has the effect of automatically, and gradually, disabling the
enforcement of the swap token when there is little or no paging going on, and
"turning up" the intensity of the swap token code the more the task holding
the token is thrashing.
Thanks to Song Jiang for pointing out this aspect of the token based thrashing
control concept.
The new code shows a slight degradation over the old swap token code, but
still a big win over running without the swap token.
2.6.12+ swap token disabled
$ for i in `seq 10` ; do /usr/bin/time ./qsbench -n 30000000 -p 3 ; done
101.74user 23.13system 8:26.91elapsed 24%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (38597major+430315minor)pagefaults 0swaps
101.98user 24.91system 8:03.06elapsed 26%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (33939major+430457minor)pagefaults 0swaps
101.93user 22.12system 7:34.90elapsed 27%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (33166major+421267minor)pagefaults 0swaps
101.82user 22.38system 8:31.40elapsed 24%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (39338major+433262minor)pagefaults 0swaps
2.6.12+ swap token enabled, timeout 300 seconds
$ for i in `seq 4` ; do /usr/bin/time ./qsbench -n 30000000 -p 3 ; done
102.58user 16.08system 3:41.44elapsed 53%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (19707major+285786minor)pagefaults 0swaps
102.07user 19.56system 4:00.64elapsed 50%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (19012major+299259minor)pagefaults 0swaps
102.64user 18.25system 4:07.31elapsed 48%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (21990major+304831minor)pagefaults 0swaps
101.39user 19.41system 5:15.81elapsed 38%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (24850major+323321minor)pagefaults 0swaps
2.6.12+ with new swap token code, timeout 300 seconds
$ for i in `seq 4` ; do /usr/bin/time ./qsbench -n 30000000 -p 3 ; done
101.87user 24.66system 5:53.20elapsed 35%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (26848major+363497minor)pagefaults 0swaps
102.83user 19.95system 4:17.25elapsed 47%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (19946major+305722minor)pagefaults 0swaps
102.09user 19.46system 5:12.57elapsed 38%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (25461major+334994minor)pagefaults 0swaps
101.67user 20.61system 4:52.97elapsed 41%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (22190major+329508minor)pagefaults 0swaps
Signed-off-by: Rik Van Riel <riel@redhat.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-10-30 09:15:46 +08:00
|
|
|
#define SWAP_TOKEN_TIMEOUT (300 * HZ)
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* Currently disabled; Needs further code to work at HZ * 300.
|
|
|
|
*/
|
|
|
|
unsigned long swap_token_default_timeout = SWAP_TOKEN_TIMEOUT;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Take the token away if the process had no page faults
|
|
|
|
* in the last interval, or if it has held the token for
|
|
|
|
* too long.
|
|
|
|
*/
|
|
|
|
#define SWAP_TOKEN_ENOUGH_RSS 1
|
|
|
|
#define SWAP_TOKEN_TIMED_OUT 2
|
|
|
|
static int should_release_swap_token(struct mm_struct *mm)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
if (!mm->recent_pagein)
|
|
|
|
ret = SWAP_TOKEN_ENOUGH_RSS;
|
|
|
|
else if (time_after(jiffies, swap_token_timeout))
|
|
|
|
ret = SWAP_TOKEN_TIMED_OUT;
|
|
|
|
mm->recent_pagein = 0;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Try to grab the swapout protection token. We only try to
|
|
|
|
* grab it once every TOKEN_CHECK_INTERVAL, both to prevent
|
|
|
|
* SMP lock contention and to check that the process that held
|
|
|
|
* the token before is no longer thrashing.
|
|
|
|
*/
|
|
|
|
void grab_swap_token(void)
|
|
|
|
{
|
|
|
|
struct mm_struct *mm;
|
|
|
|
int reason;
|
|
|
|
|
|
|
|
/* We have the token. Let others know we still need it. */
|
|
|
|
if (has_swap_token(current->mm)) {
|
|
|
|
current->mm->recent_pagein = 1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (time_after(jiffies, swap_token_check)) {
|
|
|
|
|
|
|
|
/* Can't get swapout protection if we exceed our RSS limit. */
|
|
|
|
// if (current->mm->rss > current->mm->rlimit_rss)
|
|
|
|
// return;
|
|
|
|
|
|
|
|
/* ... or if we recently held the token. */
|
|
|
|
if (time_before(jiffies, current->mm->swap_token_time))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!spin_trylock(&swap_token_lock))
|
|
|
|
return;
|
|
|
|
|
|
|
|
swap_token_check = jiffies + SWAP_TOKEN_CHECK_INTERVAL;
|
|
|
|
|
|
|
|
mm = swap_token_mm;
|
|
|
|
if ((reason = should_release_swap_token(mm))) {
|
|
|
|
unsigned long eligible = jiffies;
|
|
|
|
if (reason == SWAP_TOKEN_TIMED_OUT) {
|
|
|
|
eligible += swap_token_default_timeout;
|
|
|
|
}
|
|
|
|
mm->swap_token_time = eligible;
|
|
|
|
swap_token_timeout = jiffies + swap_token_default_timeout;
|
|
|
|
swap_token_mm = current->mm;
|
|
|
|
}
|
|
|
|
spin_unlock(&swap_token_lock);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Called on process exit. */
|
|
|
|
void __put_swap_token(struct mm_struct *mm)
|
|
|
|
{
|
|
|
|
spin_lock(&swap_token_lock);
|
|
|
|
if (likely(mm == swap_token_mm)) {
|
|
|
|
swap_token_mm = &init_mm;
|
|
|
|
swap_token_check = jiffies;
|
|
|
|
}
|
|
|
|
spin_unlock(&swap_token_lock);
|
|
|
|
}
|