From: Mike Galbraith <efault@gmx.de>
To: Michal Schmidt <schmidt@kn.vutbr.cz>
Cc: Andrew Ebling <aebling@tao-group.com>, linux-kernel@vger.kernel.org
Subject: Re: Reproducible terrible interactivity since 2.5.64bk2
Date: Fri, 28 Mar 2003 18:50:38 +0100 [thread overview]
Message-ID: <5.2.0.9.2.20030328181731.01997810@pop.gmx.net> (raw)
In-Reply-To: <3E81B63B.8080608@kn.vutbr.cz>
[-- Attachment #1: Type: text/plain, Size: 1307 bytes --]
At 03:16 PM 3/26/2003 +0100, Michal Schmidt wrote:
>Andrew Ebling wrote:
>>
>>I'm seeing similar on 2.5.66; xmms pauses when doing disk intensive
>>tasks.
>
>This may be a different problem. My test is not very disk intensive. It is
>more CPU intensive (bzip2 compression). The disk is only slightly used
>when running my testing script.
Greetings potential victims :)
Care to see if the attached cures your woes?
This is a mixture of Ingo's last posted plus the scheduler tuning knobs
patch (/proc/sys/sched/*). I added three new knobs to watch the effect on
different loads. max_accel_slices limits the amount of sleep_time you may
add in one activation. retard_prct_slices is a percentage of a slice to
deduct from sleep_time each activation (negative feedback for heavy context
switchers.. dang irman process_load). force_switch is there because I'm
playing :) I didn't do much to the scheduler itself, only made it switch
arrays in something closer to a square wave. With the settings as in the
patch, and running a kernel build, top and irman, irman reports worst case
response times of 150ms for NULL load, 316ms for memory_load, 414 for
io_load, and 504ms for process_load.
Anyway, it's attached if you want to play with it ;-)
-Mike
Oh, it's against virgin 2.5.66.
[-- Attachment #2: twiddle.diff --]
[-- Type: application/octet-stream, Size: 11032 bytes --]
--- linux-2.5.66.virgin/kernel/sched.c Thu Mar 27 14:23:58 2003
+++ linux-2.5.66.twiddle/kernel/sched.c Fri Mar 28 18:35:29 2003
@@ -63,17 +63,40 @@
* Minimum timeslice is 10 msecs, default timeslice is 100 msecs,
* maximum timeslice is 200 msecs. Timeslices get refilled after
* they expire.
+ *
+ * They are configurable via /proc/sys/sched
*/
-#define MIN_TIMESLICE ( 10 * HZ / 1000)
-#define MAX_TIMESLICE (200 * HZ / 1000)
-#define CHILD_PENALTY 50
-#define PARENT_PENALTY 100
-#define EXIT_WEIGHT 3
-#define PRIO_BONUS_RATIO 25
-#define INTERACTIVE_DELTA 2
-#define MAX_SLEEP_AVG (10*HZ)
-#define STARVATION_LIMIT (10*HZ)
-#define NODE_THRESHOLD 125
+
+int min_timeslice = (5 * HZ) / 1000;
+int max_timeslice = (200 * HZ) / 1000;
+int child_penalty = 50;
+int parent_penalty = 100;
+int exit_weight = 3;
+int prio_bonus_ratio = 25;
+int interactive_delta = 2;
+int max_sleep_avg = 10 * HZ;
+int starvation_limit = 1 * HZ;
+int node_threshold = 125;
+int max_accel_slices = 5; /* Max bonus per activation in slices. */
+int retard_prct_slice = 10; /* Percent of a slice to deduct. */
+int force_switch = 1;
+
+#define MIN_TIMESLICE (min_timeslice)
+#define MAX_TIMESLICE (max_timeslice)
+#define CHILD_PENALTY (child_penalty)
+#define PARENT_PENALTY (parent_penalty)
+#define EXIT_WEIGHT (exit_weight)
+#define PRIO_BONUS_RATIO (prio_bonus_ratio)
+#define INTERACTIVE_DELTA (interactive_delta)
+#define MAX_SLEEP_AVG (max_sleep_avg)
+#define STARVATION_LIMIT (starvation_limit)
+#define NODE_THRESHOLD (node_threshold)
+#define TIMESLICE_GRANULARITY (HZ/20 ?: 1)
+#define MAX_ACCELERATION(slice) \
+ (max_accel_slices ? max_accel_slices * (slice) : max_sleep_avg)
+#define NEGATIVE_FEEDBACK(slice) \
+ (retard_prct_slice ? retard_prct_slice * (slice) / 100 : 0)
+#define FORCE_SWITCH (force_switch)
/*
* If a task is 'interactive' then we reinsert it in the active
@@ -124,12 +147,17 @@
* task_timeslice() is the interface that is used by the scheduler.
*/
-#define BASE_TIMESLICE(p) (MIN_TIMESLICE + \
- ((MAX_TIMESLICE - MIN_TIMESLICE) * (MAX_PRIO-1-(p)->static_prio)/(MAX_USER_PRIO - 1)))
+#define BASE_TIMESLICE(p) \
+ (MAX_TIMESLICE * (MAX_PRIO-(p)->static_prio)/MAX_USER_PRIO)
static inline unsigned int task_timeslice(task_t *p)
{
- return BASE_TIMESLICE(p);
+ unsigned int time_slice = BASE_TIMESLICE(p);
+
+ if (time_slice < MIN_TIMESLICE)
+ time_slice = MIN_TIMESLICE;
+
+ return time_slice;
}
/*
@@ -347,6 +375,7 @@
if (sleep_time > 0) {
int sleep_avg;
+ int slice = task_timeslice(p);
/*
* This code gives a bonus to interactive tasks.
@@ -355,8 +384,15 @@
* value here, based on ->last_run. The more time a task
* spends sleeping, the higher the average gets - and the
* higher the priority boost gets as well.
+ *
+ * Prevent tasks with an extremely high context switch
+ * rate from becoming CPU hogs by steadily inflating their
+ * sleep_avg until they starve legitimate sleepers.
*/
- sleep_avg = p->sleep_avg + sleep_time;
+ if (p->sleep_avg > MAX_SLEEP_AVG / 2)
+ p->sleep_avg -= NEGATIVE_FEEDBACK(slice);
+ sleep_time %= MAX_ACCELERATION(slice);
+ sleep_avg = sleep_time + p->sleep_avg;
/*
* 'Overflow' bonus ticks go to the waker as well, so the
@@ -1176,10 +1212,8 @@
* load-dependent, as the frequency of array switched decreases with
* increasing number of running tasks:
*/
-#define EXPIRED_STARVING(rq) \
- (STARVATION_LIMIT && ((rq)->expired_timestamp && \
- (jiffies - (rq)->expired_timestamp >= \
- STARVATION_LIMIT * ((rq)->nr_running) + 1)))
+#define EXPIRED_STARVING(rq) ((rq)->expired_timestamp && \
+ time_after_eq(jiffies, (rq)->expired_timestamp+STARVATION_LIMIT))
/*
* This function gets called by the timer code, with HZ frequency.
@@ -1193,6 +1227,10 @@
int cpu = smp_processor_id();
runqueue_t *rq = this_rq();
task_t *p = current;
+ int array_switch = EXPIRED_STARVING(rq);
+
+ if (FORCE_SWITCH && !rq->expired_timestamp)
+ rq->expired_timestamp = jiffies - (STARVATION_LIMIT / 2);
if (rcu_pending(cpu))
rcu_check_callbacks(cpu, user_ticks);
@@ -1242,23 +1280,52 @@
/* put it at the end of the queue: */
dequeue_task(p, rq->active);
- enqueue_task(p, rq->active);
+ enqueue_task(p, array_switch ? rq->expired : rq->active);
+ } else if (array_switch) {
+ if (!rq->expired_timestamp)
+ rq->expired_timestamp = jiffies;
+ dequeue_task(p, rq->active);
+ enqueue_task(p, rq->expired);
}
goto out;
}
- if (!--p->time_slice) {
+ if (unlikely(!--p->time_slice)) {
dequeue_task(p, rq->active);
set_tsk_need_resched(p);
p->prio = effective_prio(p);
p->time_slice = task_timeslice(p);
p->first_time_slice = 0;
- if (!TASK_INTERACTIVE(p) || EXPIRED_STARVING(rq)) {
+ if (!TASK_INTERACTIVE(p) || array_switch) {
if (!rq->expired_timestamp)
rq->expired_timestamp = jiffies;
enqueue_task(p, rq->expired);
} else
enqueue_task(p, rq->active);
+ goto out;
+ }
+ /*
+ * Prevent a too long timeslice allowing a task to monopolize
+ * the CPU. We do this by splitting up the timeslice into
+ * smaller pieces.
+ *
+ * Note: this does not mean the task's timeslices expire or
+ * get lost in any way, they just might be preempted by
+ * another task of equal priority. (one with higher
+ * priority would have preempted this task already.) We
+ * requeue this task to the end of the list on this priority
+ * level, which is in essence a round-robin of tasks with
+ * equal priority.
+ *
+ * If expired tasks are starving, switch arrays ASAP.
+ */
+ if (!(p->time_slice % TIMESLICE_GRANULARITY) || array_switch) {
+ dequeue_task(p, rq->active);
+ set_tsk_need_resched(p);
+ p->prio = effective_prio(p);
+ enqueue_task(p, array_switch ? rq->expired : rq->active);
+ if (array_switch && !rq->expired_timestamp)
+ rq->expired_timestamp = jiffies;
}
out:
spin_unlock(&rq->lock);
@@ -1297,8 +1364,13 @@
rq = this_rq();
release_kernel_lock(prev);
+#if 0 // MIKEDIDIT
prev->last_run = jiffies;
+#endif
spin_lock_irq(&rq->lock);
+#if 1 // MIKEDIDIT
+ prev->last_run = jiffies;
+#endif
/*
* if entering off of a kernel preemption go straight
@@ -1680,7 +1752,7 @@
*/
int task_prio(task_t *p)
{
- return p->prio - MAX_USER_RT_PRIO;
+ return p->prio - MAX_RT_PRIO;
}
/**
--- linux-2.5.66.virgin/kernel/sysctl.c Thu Mar 27 14:23:58 2003
+++ linux-2.5.66.twiddle/kernel/sysctl.c Fri Mar 28 13:51:31 2003
@@ -57,6 +57,19 @@
extern int cad_pid;
extern int pid_max;
extern int sysctl_lower_zone_protection;
+extern int min_timeslice;
+extern int max_timeslice;
+extern int child_penalty;
+extern int parent_penalty;
+extern int exit_weight;
+extern int prio_bonus_ratio;
+extern int interactive_delta;
+extern int max_sleep_avg;
+extern int starvation_limit;
+extern int node_threshold;
+extern int max_accel_slices;
+extern int retard_prct_slice;
+extern int force_switch;
/* this is needed for the proc_dointvec_minmax for [fs_]overflow UID and GID */
static int maxolduid = 65535;
@@ -114,6 +127,7 @@
static ctl_table kern_table[];
static ctl_table vm_table[];
+static ctl_table sched_table[];
#ifdef CONFIG_NET
extern ctl_table net_table[];
#endif
@@ -158,6 +172,7 @@
{CTL_FS, "fs", NULL, 0, 0555, fs_table},
{CTL_DEBUG, "debug", NULL, 0, 0555, debug_table},
{CTL_DEV, "dev", NULL, 0, 0555, dev_table},
+ {CTL_SCHED, "sched", NULL, 0, 0555, sched_table},
{0}
};
@@ -360,7 +375,50 @@
static ctl_table dev_table[] = {
{0}
-};
+};
+
+static ctl_table sched_table[] = {
+ {SCHED_MAX_TIMESLICE, "max_timeslice", &max_timeslice,
+ sizeof(int), 0644, NULL, &proc_dointvec_minmax,
+ &sysctl_intvec, NULL, &one, NULL},
+ {SCHED_MIN_TIMESLICE, "min_timeslice", &min_timeslice,
+ sizeof(int), 0644, NULL, &proc_dointvec_minmax,
+ &sysctl_intvec, NULL, &one, NULL},
+ {SCHED_CHILD_PENALTY, "child_penalty", &child_penalty,
+ sizeof(int), 0644, NULL, &proc_dointvec_minmax,
+ &sysctl_intvec, NULL, &zero, NULL},
+ {SCHED_PARENT_PENALTY, "parent_penalty", &parent_penalty,
+ sizeof(int), 0644, NULL, &proc_dointvec_minmax,
+ &sysctl_intvec, NULL, &zero, NULL},
+ {SCHED_EXIT_WEIGHT, "exit_weight", &exit_weight,
+ sizeof(int), 0644, NULL, &proc_dointvec_minmax,
+ &sysctl_intvec, NULL, &zero, NULL},
+ {SCHED_PRIO_BONUS_RATIO, "prio_bonus_ratio", &prio_bonus_ratio,
+ sizeof(int), 0644, NULL, &proc_dointvec_minmax,
+ &sysctl_intvec, NULL, &zero, NULL},
+ {SCHED_INTERACTIVE_DELTA, "interactive_delta", &interactive_delta,
+ sizeof(int), 0644, NULL, &proc_dointvec_minmax,
+ &sysctl_intvec, NULL, &zero, NULL},
+ {SCHED_MAX_SLEEP_AVG, "max_sleep_avg", &max_sleep_avg,
+ sizeof(int), 0644, NULL, &proc_dointvec_minmax,
+ &sysctl_intvec, NULL, &one, NULL},
+ {SCHED_STARVATION_LIMIT, "starvation_limit", &starvation_limit,
+ sizeof(int), 0644, NULL, &proc_dointvec_minmax,
+ &sysctl_intvec, NULL, &zero, NULL},
+ {SCHED_NODE_THRESHOLD, "node_threshold", &node_threshold,
+ sizeof(int), 0644, NULL, &proc_dointvec_minmax,
+ &sysctl_intvec, NULL, &one, NULL},
+ {SCHED_MAX_ACCELERATION, "max_accel_slices", &max_accel_slices,
+ sizeof(int), 0644, NULL, &proc_dointvec_minmax,
+ &sysctl_intvec, NULL, &zero, NULL},
+ {SCHED_NEG_FEEDBACK, "retard_prct_slice", &retard_prct_slice,
+ sizeof(int), 0644, NULL, &proc_dointvec_minmax,
+ &sysctl_intvec, NULL, &zero, &one_hundred},
+ {SCHED_FORCE_SWITCH, "force_switch", &force_switch,
+ sizeof(int), 0644, NULL, &proc_dointvec_minmax,
+ &sysctl_intvec, NULL, &zero, &one},
+ {0}
+};
extern void init_irq_proc (void);
--- linux-2.5.66.virgin/include/linux/sysctl.h Fri Mar 7 06:07:40 2003
+++ linux-2.5.66.twiddle/include/linux/sysctl.h Fri Mar 28 13:53:47 2003
@@ -66,7 +66,8 @@
CTL_DEV=7, /* Devices */
CTL_BUS=8, /* Busses */
CTL_ABI=9, /* Binary emulation */
- CTL_CPU=10 /* CPU stuff (speed scaling, etc) */
+ CTL_CPU=10, /* CPU stuff (speed scaling, etc) */
+ CTL_SCHED=11, /* scheduler tunables */
};
/* CTL_BUS names: */
@@ -157,6 +158,22 @@
VM_LOWER_ZONE_PROTECTION=20,/* Amount of protection of lower zones */
};
+/* Tunable scheduler parameters in /proc/sys/sched/ */
+enum {
+ SCHED_MIN_TIMESLICE=1, /* minimum process timeslice */
+ SCHED_MAX_TIMESLICE=2, /* maximum process timeslice */
+ SCHED_CHILD_PENALTY=3, /* penalty on fork to child */
+ SCHED_PARENT_PENALTY=4, /* penalty on fork to parent */
+ SCHED_EXIT_WEIGHT=5, /* penalty to parent of CPU hog child */
+ SCHED_PRIO_BONUS_RATIO=6, /* percent of max prio given as bonus */
+ SCHED_INTERACTIVE_DELTA=7, /* delta used to scale interactivity */
+ SCHED_MAX_SLEEP_AVG=8, /* maximum sleep avg attainable */
+ SCHED_STARVATION_LIMIT=9, /* no re-active if expired is starved */
+ SCHED_NODE_THRESHOLD=10, /* NUMA node rebalance threshold */
+ SCHED_MAX_ACCELERATION=11, /* maximum bonus slices per activation */
+ SCHED_NEG_FEEDBACK=12, /* percent slice onus per activation */
+ SCHED_FORCE_SWITCH=13, /* force switch to expired array */
+};
/* CTL_NET names: */
enum
next prev parent reply other threads:[~2003-03-28 17:35 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2003-03-26 11:51 Michal Schmidt
2003-03-26 14:08 ` Andrew Ebling
2003-03-26 14:16 ` Michal Schmidt
2003-03-26 14:40 ` Andrew Ebling
2003-03-28 17:50 ` Mike Galbraith [this message]
2003-03-28 19:54 ` Mike Galbraith
2003-03-30 22:51 ` Michal Schmidt
2003-03-31 2:26 ` Mike Galbraith
2003-03-26 18:25 ` Felipe Alfaro Solana
2003-03-26 18:41 ` Bill Davidsen
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=5.2.0.9.2.20030328181731.01997810@pop.gmx.net \
--to=efault@gmx.de \
--cc=aebling@tao-group.com \
--cc=linux-kernel@vger.kernel.org \
--cc=schmidt@kn.vutbr.cz \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®