* Copyright (c) 1982, 1986, 1990, 1991, 1993
* The Regents of the University of California. All rights reserved.
* %sccs.include.redist.c%
* @(#)kern_synch.c 8.3 (Berkeley) %G%
#include <sys/signalvar.h>
#include <sys/resourcevar.h>
u_char curpriority
; /* usrpri of curproc */
int lbolt
; /* once a second sleep address */
* Force switch among equal priority processes every 100ms.
timeout(roundrobin
, NULL
, hz
/ 10);
* constants for digital decay and forget
* 90% of (p_cpu) usage in 5*loadav time
* 95% of (p_pctcpu) usage in 60 seconds (load insensitive)
* Note that, as ps(1) mentions, this can let percentages
* total over 100% (I've seen 137.9% for 3 processes).
* Note that hardclock updates p_cpu and p_cpticks independently.
* We wish to decay away 90% of p_cpu in (5 * loadavg) seconds.
* That is, the system wants to compute a value of decay such
* that the following for loop:
* for (i = 0; i < (5 * loadavg); i++)
* for all values of loadavg:
* Mathematically this loop can be expressed by saying:
* decay ** (5 * loadavg) ~= .1
* The system computes decay as:
* decay = (2 * loadavg) / (2 * loadavg + 1)
* We wish to prove that the system's computation of decay
* will always fulfill the equation:
* decay ** (5 * loadavg) ~= .1
* We now need to prove two things:
* 1) Given factor ** (5 * loadavg) ~= .1, prove factor == b/(b+1)
* 2) Given b/(b+1) ** power ~= .1, prove power == (5 * loadavg)
* For x close to zero, exp(x) =~ 1 + x, since
* exp(x) = 0! + x**1/1! + x**2/2! + ... .
* therefore exp(-1/b) =~ 1 - (1/b) = (b-1)/b.
* For x close to zero, ln(1+x) =~ x, since
* ln(1+x) = x - x**2/2 + x**3/3 - ... -1 < x < 1
* therefore ln(b/(b+1)) = ln(1 - 1/(b+1)) =~ -1/(b+1).
* Solve (factor)**(power) =~ .1 given power (5*loadav):
* ln(factor) =~ (-2.30/5*loadav), or
* factor =~ exp(-1/((5/2.30)*loadav)) =~ exp(-1/(2*loadav)) =
* exp(-1/b) =~ (b-1)/b =~ b/(b+1). QED
* Solve (factor)**(power) =~ .1 given factor == (b/(b+1)):
* power*ln(b/(b+1)) =~ -2.30, or
* power =~ 2.3 * (b + 1) = 4.6*loadav + 2.3 =~ 5*loadav. QED
* Actual power values for the implemented algorithm are as follows:
* power: 5.68 10.32 14.94 19.55
/* calculations for digital decay to forget 90% of usage in 5*loadav sec */
#define loadfactor(loadav) (2 * (loadav))
#define decay_cpu(loadfac, cpu) (((loadfac) * (cpu)) / ((loadfac) + FSCALE))
/* decay 95% of `p_pctcpu' in 60 seconds; see CCPU_SHIFT before changing */
fixpt_t ccpu
= 0.95122942450071400909 * FSCALE
; /* exp(-1/20) */
* If `ccpu' is not equal to `exp(-1/20)' and you still want to use the
* faster/more-accurate formula, you'll have to estimate CCPU_SHIFT below
* and possibly adjust FSHIFT in "param.h" so that (FSHIFT >= CCPU_SHIFT).
* To estimate CCPU_SHIFT for exp(-1/20), the following formula was used:
* 1 - exp(-1/20) ~= 0.0487 ~= 0.0488 == 1 (fixed pt, *11* bits).
* If you dont want to bother with the faster/more-accurate formula, you
* can set CCPU_SHIFT to (FSHIFT + 1) which will use a slower/less-accurate
* (more general) method of calculating the %age of CPU used by a process.
* Recompute process priorities, every hz ticks.
register fixpt_t loadfac
= loadfactor(averunnable
.ldavg
[0]);
register unsigned int newcpu
;
for (p
= (struct proc
*)allproc
; p
!= NULL
; p
= p
->p_nxt
) {
* Increment time in/out of memory and sleep time
* (if sleeping). We ignore overflow; with 16-bit int's
* (remember them?) overflow takes 45 days.
if (p
->p_stat
== SSLEEP
|| p
->p_stat
== SSTOP
)
p
->p_pctcpu
= (p
->p_pctcpu
* ccpu
) >> FSHIFT
;
* If the process has slept the entire second,
* stop recalculating its priority until it wakes up.
s
= splstatclock(); /* prevent state changes */
* p_pctcpu is only for ps.
#if (FSHIFT >= CCPU_SHIFT)
p
->p_pctcpu
+= (hz
== 100)?
((fixpt_t
) p
->p_cpticks
) << (FSHIFT
- CCPU_SHIFT
):
100 * (((fixpt_t
) p
->p_cpticks
)
<< (FSHIFT
- CCPU_SHIFT
)) / hz
;
p
->p_pctcpu
+= ((FSCALE
- ccpu
) *
(p
->p_cpticks
* FSCALE
/ hz
)) >> FSHIFT
;
newcpu
= (u_int
) decay_cpu(loadfac
, p
->p_cpu
) + p
->p_nice
;
p
->p_cpu
= min(newcpu
, UCHAR_MAX
);
#define PPQ (128 / NQS) /* priorities per queue */
(p
->p_pri
/ PPQ
) != (p
->p_usrpri
/ PPQ
)) {
wakeup((caddr_t
)pageproc
);
timeout(schedcpu
, (void *)0, hz
);
* Recalculate the priority of a process after it has slept for a while.
* For all load averages >= 1 and max p_cpu of 255, sleeping for at least
* six times the loadfactor will decay p_cpu to zero.
register unsigned int newcpu
= p
->p_cpu
;
register fixpt_t loadfac
= loadfactor(averunnable
.ldavg
[0]);
if (p
->p_slptime
> 5 * loadfac
)
p
->p_slptime
--; /* the first time was done in schedcpu */
while (newcpu
&& --p
->p_slptime
)
newcpu
= (int) decay_cpu(loadfac
, newcpu
);
p
->p_cpu
= min(newcpu
, UCHAR_MAX
);
#define SQSIZE 0100 /* Must be power of 2 */
#define HASH(x) (( (int) x >> 5) & (SQSIZE-1))
* During autoconfiguration or after a panic, a sleep will simply
* lower the priority briefly to allow interrupts, then return.
* The priority to be used (safepri) is machine-dependent, thus this
* value is initialized and maintained in the machine-dependent layers.
* This priority will typically be 0, or the lowest priority
* that is safe for use on the interrupt stack; it can be made
* higher to block network software interrupts after panics.
* General sleep call. Suspends the current process until a wakeup is
* performed on the specified identifier. The process will then be made
* runnable with the specified priority. Sleeps at most timo/hz seconds
* (0 means no timeout). If pri includes PCATCH flag, signals are checked
* before and after sleeping, else signals are not checked. Returns 0 if
* awakened, EWOULDBLOCK if the timeout expires. If PCATCH is set and a
* signal needs to be delivered, ERESTART is returned if the current system
* call should be restarted if possible, and EINTR is returned if the system
* call should be interrupted by the signal (return EINTR).
tsleep(ident
, priority
, wmesg
, timo
)
register struct proc
*p
= curproc
;
register struct slpque
*qp
;
int sig
, catch = priority
& PCATCH
;
void endtsleep
__P((void *));
if (KTRPOINT(p
, KTR_CSW
))
ktrcsw(p
->p_tracep
, 1, 0);
* After a panic, or during autoconfiguration,
* just give interrupts a chance, then just return;
* don't run any other procs or panic below,
* in case this is the idle process and already asleep.
if (ident
== NULL
|| p
->p_stat
!= SRUN
|| p
->p_rlink
)
p
->p_pri
= priority
& PRIMASK
;
qp
= &slpque
[HASH(ident
)];
*(qp
->sq_tailp
= &p
->p_link
) = 0;
timeout(endtsleep
, (void *)p
, timo
);
* We put ourselves on the sleep queue and start our timeout
* before calling CURSIG, as we could stop there, and a wakeup
* or a SIGCONT (or both) could occur while we were stopped.
* A SIGCONT would cause us to be marked as SSLEEP
* without resuming us, thus we must be ready for sleep
* when CURSIG is called. If the wakeup happens while we're
* stopped, p->p_wchan will be 0 upon return from CURSIG.
p
->p_stats
->p_ru
.ru_nvcsw
++;
curpriority
= p
->p_usrpri
;
if (KTRPOINT(p
, KTR_CSW
))
ktrcsw(p
->p_tracep
, 0, 0);
untimeout(endtsleep
, (void *)p
);
if (catch && (sig
!= 0 || (sig
= CURSIG(p
)))) {
if (KTRPOINT(p
, KTR_CSW
))
ktrcsw(p
->p_tracep
, 0, 0);
if (p
->p_sigacts
->ps_sigintr
& sigmask(sig
))
if (KTRPOINT(p
, KTR_CSW
))
ktrcsw(p
->p_tracep
, 0, 0);
* Implement timeout for tsleep.
* If process hasn't been awakened (wchan non-zero),
* set timeout flag and undo the sleep. If proc
* is stopped, just unsleep so it will remain stopped.
* Short-term, non-interruptable sleep.
register struct proc
*p
= curproc
;
register struct slpque
*qp
;
printf("sleep called with priority %d > PZERO, wchan: %x\n",
* After a panic, or during autoconfiguration,
* just give interrupts a chance, then just return;
* don't run any other procs or panic below,
* in case this is the idle process and already asleep.
if (ident
== NULL
|| p
->p_stat
!= SRUN
|| p
->p_rlink
)
qp
= &slpque
[HASH(ident
)];
*(qp
->sq_tailp
= &p
->p_link
) = 0;
p
->p_stats
->p_ru
.ru_nvcsw
++;
if (KTRPOINT(p
, KTR_CSW
))
ktrcsw(p
->p_tracep
, 1, 0);
if (KTRPOINT(p
, KTR_CSW
))
ktrcsw(p
->p_tracep
, 0, 0);
curpriority
= p
->p_usrpri
;
* Remove a process from its wait queue
register struct slpque
*qp
;
register struct proc
**hp
;
hp
= &(qp
= &slpque
[HASH(p
->p_wchan
)])->sq_head
;
if (qp
->sq_tailp
== &p
->p_link
)
* Make all processes sleeping on the specified identifier runnable.
register struct slpque
*qp
;
register struct proc
*p
, **q
;
qp
= &slpque
[HASH(ident
)];
for (q
= &qp
->sq_head
; p
= *q
; ) {
if (p
->p_rlink
|| p
->p_stat
!= SSLEEP
&& p
->p_stat
!= SSTOP
)
if (p
->p_wchan
== ident
) {
if (qp
->sq_tailp
== &p
->p_link
)
if (p
->p_stat
== SSLEEP
) {
/* OPTIMIZED EXPANSION OF setrunnable(p); */
* Since curpriority is a user priority,
* p->p_pri is always better than curpriority.
if ((p
->p_flag
&SLOAD
) == 0)
/* END INLINE EXPANSION */
* The machine independent parts of swtch().
* Must be called at splstatclock() or higher.
register struct proc
*p
= curproc
; /* XXX */
register struct rlimit
*rlim
;
* Compute the amount of time during which the current
* process was running, and add that to its total so far.
u
= p
->p_rtime
.tv_usec
+ (tv
.tv_usec
- runtime
.tv_usec
);
s
= p
->p_rtime
.tv_sec
+ (tv
.tv_sec
- runtime
.tv_sec
);
} else if (u
>= 1000000) {
* Check if the process exceeds its cpu resource allocation.
* If over max, kill it. In any case, if it has run for more
* than 10 minutes, reduce priority to give others a chance.
rlim
= &p
->p_rlimit
[RLIMIT_CPU
];
if (s
>= rlim
->rlim_cur
) {
if (rlim
->rlim_cur
< rlim
->rlim_max
)
if (s
> 10 * 60 && p
->p_ucred
->cr_uid
&& p
->p_nice
== NZERO
) {
* Pick a new current process and record its start time.
* Initialize the (doubly-linked) run queues
for (i
= 0; i
< NQS
; i
++)
qs
[i
].ph_link
= qs
[i
].ph_rlink
= (struct proc
*)&qs
[i
];
* Change process state to be runnable,
* placing it on the run queue if it is in memory,
* and awakening the swapper if it isn't in memory.
unsleep(p
); /* e.g. when sending signals */
if ((p
->p_flag
&SLOAD
) == 0)
else if (p
->p_pri
< curpriority
)
* Compute the priority of a process when running in user mode.
* Arrange to reschedule if the resulting priority is better
* than that of the current process.
register unsigned int newpriority
;
newpriority
= PUSER
+ p
->p_cpu
/ 4 + 2 * p
->p_nice
;
newpriority
= min(newpriority
, MAXPRI
);
p
->p_usrpri
= newpriority
;
if (newpriority
< curpriority
)