2003-01-26 05:23:15 +00:00
|
|
|
/*-
|
2007-01-04 08:56:25 +00:00
|
|
|
* Copyright (c) 2002-2007, Jeffrey Roberson <jeff@freebsd.org>
|
2003-01-26 05:23:15 +00:00
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice unmodified, this list of conditions, and the following
|
|
|
|
* disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
2007-07-17 22:53:23 +00:00
|
|
|
/*
|
|
|
|
* This file implements the ULE scheduler. ULE supports independent CPU
|
|
|
|
* run queues and fine grain locking. It has superior interactive
|
|
|
|
* performance under load even on uni-processor systems.
|
|
|
|
*
|
|
|
|
* etymology:
|
2007-09-22 02:20:14 +00:00
|
|
|
* ULE is the last three letters in schedule. It owes its name to a
|
2007-07-17 22:53:23 +00:00
|
|
|
* generic user created for a scheduling system by Paul Mikesell at
|
|
|
|
* Isilon Systems and a general lack of creativity on the part of the author.
|
|
|
|
*/
|
|
|
|
|
2003-06-11 00:56:59 +00:00
|
|
|
#include <sys/cdefs.h>
|
|
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
|
2005-06-24 00:16:57 +00:00
|
|
|
#include "opt_hwpmc_hooks.h"
|
2008-05-25 01:44:58 +00:00
|
|
|
#include "opt_kdtrace.h"
|
2005-06-24 00:16:57 +00:00
|
|
|
#include "opt_sched.h"
|
2004-09-02 18:59:15 +00:00
|
|
|
|
2003-01-26 05:23:15 +00:00
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/systm.h>
|
2004-07-10 21:38:22 +00:00
|
|
|
#include <sys/kdb.h>
|
2003-01-26 05:23:15 +00:00
|
|
|
#include <sys/kernel.h>
|
|
|
|
#include <sys/ktr.h>
|
|
|
|
#include <sys/lock.h>
|
|
|
|
#include <sys/mutex.h>
|
|
|
|
#include <sys/proc.h>
|
2003-04-02 06:46:43 +00:00
|
|
|
#include <sys/resource.h>
|
2003-11-04 07:45:41 +00:00
|
|
|
#include <sys/resourcevar.h>
|
2003-01-26 05:23:15 +00:00
|
|
|
#include <sys/sched.h>
|
|
|
|
#include <sys/smp.h>
|
|
|
|
#include <sys/sx.h>
|
|
|
|
#include <sys/sysctl.h>
|
|
|
|
#include <sys/sysproto.h>
|
Rework the interface between priority propagation (lending) and the
schedulers a bit to ensure more correct handling of priorities and fewer
priority inversions:
- Add two functions to the sched(9) API to handle priority lending:
sched_lend_prio() and sched_unlend_prio(). The turnstile code uses these
functions to ask the scheduler to lend a thread a set priority and to
tell the scheduler when it thinks it is ok for a thread to stop borrowing
priority. The unlend case is slightly complex in that the turnstile code
tells the scheduler what the minimum priority of the thread needs to be
to satisfy the requirements of any other threads blocked on locks owned
by the thread in question. The scheduler then decides where the thread
can go back to normal mode (if it's normal priority is high enough to
satisfy the pending lock requests) or it it should continue to use the
priority specified to the sched_unlend_prio() call. This involves adding
a new per-thread flag TDF_BORROWING that replaces the ULE-only kse flag
for priority elevation.
- Schedulers now refuse to lower the priority of a thread that is currently
borrowing another therad's priority.
- If a scheduler changes the priority of a thread that is currently sitting
on a turnstile, it will call a new function turnstile_adjust() to inform
the turnstile code of the change. This function resorts the thread on
the priority list of the turnstile if needed, and if the thread ends up
at the head of the list (due to having the highest priority) and its
priority was raised, then it will propagate that new priority to the
owner of the lock it is blocked on.
Some additional fixes specific to the 4BSD scheduler include:
- Common code for updating the priority of a thread when the user priority
of its associated kse group has been consolidated in a new static
function resetpriority_thread(). One change to this function is that
it will now only adjust the priority of a thread if it already has a
time sharing priority, thus preserving any boosts from a tsleep() until
the thread returns to userland. Also, resetpriority() no longer calls
maybe_resched() on each thread in the group. Instead, the code calling
resetpriority() is responsible for calling resetpriority_thread() on
any threads that need to be updated.
- schedcpu() now uses resetpriority_thread() instead of just calling
sched_prio() directly after it updates a kse group's user priority.
- sched_clock() now uses resetpriority_thread() rather than writing
directly to td_priority.
- sched_nice() now updates all the priorities of the threads after the
group priority has been adjusted.
Discussed with: bde
Reviewed by: ups, jeffr
Tested on: 4bsd, ule
Tested on: i386, alpha, sparc64
2004-12-30 20:52:44 +00:00
|
|
|
#include <sys/turnstile.h>
|
2006-08-25 06:12:53 +00:00
|
|
|
#include <sys/umtx.h>
|
2003-01-26 05:23:15 +00:00
|
|
|
#include <sys/vmmeter.h>
|
2008-03-02 08:20:59 +00:00
|
|
|
#include <sys/cpuset.h>
|
Introduce a new sysctl, kern.sched.topology_spec, that returns an XML
dump of detected ULE CPU topology. This dump can be used to check the
topology detection and for general system information.
An example of CPU topology dump is:
kern.sched.topology_spec: <groups>
<group level="1" cache-level="0">
<cpu count="8" mask="0xff">0, 1, 2, 3, 4, 5, 6, 7</cpu>
<flags></flags>
<children>
<group level="2" cache-level="0">
<cpu count="4" mask="0xf">0, 1, 2, 3</cpu>
<flags></flags>
</group>
<group level="2" cache-level="0">
<cpu count="4" mask="0xf0">4, 5, 6, 7</cpu>
<flags></flags>
</group>
</children>
</group>
</groups>
Reviewed by: jeff
Approved by: gnn (mentor)
2008-10-29 13:36:23 +00:00
|
|
|
#include <sys/sbuf.h>
|
2003-01-26 05:23:15 +00:00
|
|
|
#ifdef KTRACE
|
|
|
|
#include <sys/uio.h>
|
|
|
|
#include <sys/ktrace.h>
|
|
|
|
#endif
|
|
|
|
|
2005-04-19 04:01:25 +00:00
|
|
|
#ifdef HWPMC_HOOKS
|
|
|
|
#include <sys/pmckern.h>
|
|
|
|
#endif
|
|
|
|
|
2008-05-25 01:44:58 +00:00
|
|
|
#ifdef KDTRACE_HOOKS
|
|
|
|
#include <sys/dtrace_bsd.h>
|
|
|
|
int dtrace_vtime_active;
|
|
|
|
dtrace_vtime_switch_func_t dtrace_vtime_switch_func;
|
|
|
|
#endif
|
|
|
|
|
2003-01-26 05:23:15 +00:00
|
|
|
#include <machine/cpu.h>
|
- Add static to local functions and data where it was missing.
- Add an IPI based mechanism for migrating kses. This mechanism is
broken down into several components. This is intended to reduce cache
thrashing by eliminating most cases where one cpu touches another's
run queues.
- kseq_notify() appends a kse to a lockless singly linked list and
conditionally sends an IPI to the target processor. Right now this is
protected by sched_lock but at some point I'd like to get rid of the
global lock. This is why I used something more complicated than a
standard queue.
- kseq_assign() processes our list of kses that have been assigned to us
by other processors. This simply calls sched_add() for each item on the
list after clearing the new KEF_ASSIGNED flag. This flag is used to
indicate that we have been appeneded to the assigned queue but not
added to the run queue yet.
- In sched_add(), instead of adding a KSE to another processor's queue we
use kse_notify() so that we don't touch their queue. Also in sched_add(),
if KEF_ASSIGNED is already set return immediately. This can happen if
a thread is removed and readded so that the priority is recorded properly.
- In sched_rem() return immediately if KEF_ASSIGNED is set. All callers
immediately readd simply to adjust priorites etc.
- In sched_choose(), if we're running an IDLE task or the per cpu idle thread
set our cpumask bit in 'kseq_idle' so that other processors may know that
we are idle. Before this, make a single pass through the run queues of
other processors so that we may find work more immediately if it is
available.
- In sched_runnable(), don't scan each processor's run queue, they will IPI
us if they have work for us to do.
- In sched_add(), if we're adding a thread that can be migrated and we have
plenty of work to do, try to migrate the thread to an idle kseq.
- Simplify the logic in sched_prio() and take the KEF_ASSIGNED flag into
consideration.
- No longer use kseq_choose() to steal threads, it can lose it's last
argument.
- Create a new function runq_steal() which operates like runq_choose() but
skips threads based on some criteria. Currently it will not steal
PRI_ITHD threads. In the future this will be used for CPU binding.
- Create a kseq_steal() that checks each run queue with runq_steal(), use
kseq_steal() in the places where we used kseq_choose() to steal with
before.
2003-10-31 11:16:04 +00:00
|
|
|
#include <machine/smp.h>
|
2003-01-26 05:23:15 +00:00
|
|
|
|
2008-04-15 05:02:42 +00:00
|
|
|
#if defined(__sparc64__) || defined(__mips__)
|
2007-09-27 16:39:27 +00:00
|
|
|
#error "This architecture is not currently compatible with ULE"
|
2007-01-23 08:50:34 +00:00
|
|
|
#endif
|
|
|
|
|
2007-07-17 22:53:23 +00:00
|
|
|
#define KTR_ULE 0
|
2007-01-25 19:14:11 +00:00
|
|
|
|
2005-06-04 09:23:28 +00:00
|
|
|
/*
|
2007-07-17 22:53:23 +00:00
|
|
|
* Thread scheduler specific section. All fields are protected
|
|
|
|
* by the thread lock.
|
2004-09-05 02:09:54 +00:00
|
|
|
*/
|
2006-12-06 06:34:57 +00:00
|
|
|
struct td_sched {
|
2007-07-17 22:53:23 +00:00
|
|
|
struct runq *ts_runq; /* Run-queue we're queued on. */
|
|
|
|
short ts_flags; /* TSF_* flags. */
|
2006-12-06 06:34:57 +00:00
|
|
|
u_char ts_cpu; /* CPU that we have affinity for. */
|
2008-03-10 03:15:19 +00:00
|
|
|
int ts_rltick; /* Real last tick, for affinity. */
|
2007-07-17 22:53:23 +00:00
|
|
|
int ts_slice; /* Ticks of slice remaining. */
|
|
|
|
u_int ts_slptime; /* Number of ticks we vol. slept */
|
|
|
|
u_int ts_runtime; /* Number of ticks we were running */
|
2006-12-06 06:34:57 +00:00
|
|
|
int ts_ltick; /* Last tick that we were running on */
|
|
|
|
int ts_ftick; /* First tick that we were running on */
|
|
|
|
int ts_ticks; /* Tick count */
|
2004-09-05 02:09:54 +00:00
|
|
|
};
|
2006-12-06 06:34:57 +00:00
|
|
|
/* flags kept in ts_flags */
|
2007-01-19 21:56:08 +00:00
|
|
|
#define TSF_BOUND 0x0001 /* Thread can not migrate. */
|
|
|
|
#define TSF_XFERABLE 0x0002 /* Thread was added as transferable. */
|
2006-12-06 06:34:57 +00:00
|
|
|
|
|
|
|
static struct td_sched td_sched0;
|
2003-01-26 05:23:15 +00:00
|
|
|
|
2008-03-02 08:20:59 +00:00
|
|
|
#define THREAD_CAN_MIGRATE(td) ((td)->td_pinned == 0)
|
|
|
|
#define THREAD_CAN_SCHED(td, cpu) \
|
|
|
|
CPU_ISSET((cpu), &(td)->td_cpuset->cs_mask)
|
|
|
|
|
2003-01-26 05:23:15 +00:00
|
|
|
/*
|
2007-01-04 08:56:25 +00:00
|
|
|
* Cpu percentage computation macros and defines.
|
|
|
|
*
|
|
|
|
* SCHED_TICK_SECS: Number of seconds to average the cpu usage across.
|
|
|
|
* SCHED_TICK_TARG: Number of hz ticks to average the cpu usage across.
|
2007-01-05 08:50:38 +00:00
|
|
|
* SCHED_TICK_MAX: Maximum number of ticks before scaling back.
|
2007-01-04 08:56:25 +00:00
|
|
|
* SCHED_TICK_SHIFT: Shift factor to avoid rounding away results.
|
|
|
|
* SCHED_TICK_HZ: Compute the number of hz ticks for a given ticks count.
|
|
|
|
* SCHED_TICK_TOTAL: Gives the amount of time we've been recording ticks.
|
|
|
|
*/
|
|
|
|
#define SCHED_TICK_SECS 10
|
|
|
|
#define SCHED_TICK_TARG (hz * SCHED_TICK_SECS)
|
2007-01-05 08:50:38 +00:00
|
|
|
#define SCHED_TICK_MAX (SCHED_TICK_TARG + hz)
|
2007-01-04 08:56:25 +00:00
|
|
|
#define SCHED_TICK_SHIFT 10
|
|
|
|
#define SCHED_TICK_HZ(ts) ((ts)->ts_ticks >> SCHED_TICK_SHIFT)
|
2007-01-06 12:33:43 +00:00
|
|
|
#define SCHED_TICK_TOTAL(ts) (max((ts)->ts_ltick - (ts)->ts_ftick, hz))
|
2007-01-04 08:56:25 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* These macros determine priorities for non-interactive threads. They are
|
|
|
|
* assigned a priority based on their recent cpu utilization as expressed
|
|
|
|
* by the ratio of ticks to the tick total. NHALF priorities at the start
|
|
|
|
* and end of the MIN to MAX timeshare range are only reachable with negative
|
|
|
|
* or positive nice respectively.
|
2003-03-04 02:45:59 +00:00
|
|
|
*
|
2007-01-04 08:56:25 +00:00
|
|
|
* PRI_RANGE: Priority range for utilization dependent priorities.
|
2003-06-21 02:22:47 +00:00
|
|
|
* PRI_NRESV: Number of nice values.
|
2007-01-04 08:56:25 +00:00
|
|
|
* PRI_TICKS: Compute a priority in PRI_RANGE from the ticks count and total.
|
|
|
|
* PRI_NICE: Determines the part of the priority inherited from nice.
|
2003-01-26 05:23:15 +00:00
|
|
|
*/
|
2007-01-04 08:56:25 +00:00
|
|
|
#define SCHED_PRI_NRESV (PRIO_MAX - PRIO_MIN)
|
2003-11-02 03:49:32 +00:00
|
|
|
#define SCHED_PRI_NHALF (SCHED_PRI_NRESV / 2)
|
2007-01-04 08:56:25 +00:00
|
|
|
#define SCHED_PRI_MIN (PRI_MIN_TIMESHARE + SCHED_PRI_NHALF)
|
|
|
|
#define SCHED_PRI_MAX (PRI_MAX_TIMESHARE - SCHED_PRI_NHALF)
|
2007-06-15 19:33:58 +00:00
|
|
|
#define SCHED_PRI_RANGE (SCHED_PRI_MAX - SCHED_PRI_MIN)
|
2007-01-04 08:56:25 +00:00
|
|
|
#define SCHED_PRI_TICKS(ts) \
|
|
|
|
(SCHED_TICK_HZ((ts)) / \
|
2007-01-06 08:44:13 +00:00
|
|
|
(roundup(SCHED_TICK_TOTAL((ts)), SCHED_PRI_RANGE) / SCHED_PRI_RANGE))
|
2007-01-04 08:56:25 +00:00
|
|
|
#define SCHED_PRI_NICE(nice) (nice)
|
2003-01-26 05:23:15 +00:00
|
|
|
|
|
|
|
/*
|
2007-01-04 08:56:25 +00:00
|
|
|
* These determine the interactivity of a process. Interactivity differs from
|
|
|
|
* cpu utilization in that it expresses the voluntary time slept vs time ran
|
|
|
|
* while cpu utilization includes all time not running. This more accurately
|
|
|
|
* models the intent of the thread.
|
2003-01-26 05:23:15 +00:00
|
|
|
*
|
2003-02-10 14:03:45 +00:00
|
|
|
* SLP_RUN_MAX: Maximum amount of sleep time + run time we'll accumulate
|
|
|
|
* before throttling back.
|
2003-11-02 03:36:33 +00:00
|
|
|
* SLP_RUN_FORK: Maximum slp+run time to inherit at fork time.
|
2003-06-15 02:18:29 +00:00
|
|
|
* INTERACT_MAX: Maximum interactivity value. Smaller is better.
|
2003-03-04 02:45:59 +00:00
|
|
|
* INTERACT_THRESH: Threshhold for placement on the current runq.
|
2003-01-26 05:23:15 +00:00
|
|
|
*/
|
2007-01-04 08:56:25 +00:00
|
|
|
#define SCHED_SLP_RUN_MAX ((hz * 5) << SCHED_TICK_SHIFT)
|
|
|
|
#define SCHED_SLP_RUN_FORK ((hz / 2) << SCHED_TICK_SHIFT)
|
2003-06-15 02:18:29 +00:00
|
|
|
#define SCHED_INTERACT_MAX (100)
|
|
|
|
#define SCHED_INTERACT_HALF (SCHED_INTERACT_MAX / 2)
|
2003-10-16 08:17:43 +00:00
|
|
|
#define SCHED_INTERACT_THRESH (30)
|
2003-03-04 02:45:59 +00:00
|
|
|
|
2003-01-26 05:23:15 +00:00
|
|
|
/*
|
2007-01-04 08:56:25 +00:00
|
|
|
* tickincr: Converts a stathz tick into a hz domain scaled by
|
|
|
|
* the shift factor. Without the shift the error rate
|
|
|
|
* due to rounding would be unacceptably high.
|
|
|
|
* realstathz: stathz is sometimes 0 and run off of hz.
|
|
|
|
* sched_slice: Runtime of each thread before rescheduling.
|
2007-07-17 22:53:23 +00:00
|
|
|
* preempt_thresh: Priority threshold for preemption and remote IPIs.
|
2003-01-26 05:23:15 +00:00
|
|
|
*/
|
2007-01-04 08:56:25 +00:00
|
|
|
static int sched_interact = SCHED_INTERACT_THRESH;
|
|
|
|
static int realstathz;
|
|
|
|
static int tickincr;
|
2008-03-10 03:15:19 +00:00
|
|
|
static int sched_slice = 1;
|
2007-09-27 16:39:27 +00:00
|
|
|
#ifdef PREEMPTION
|
|
|
|
#ifdef FULL_PREEMPTION
|
|
|
|
static int preempt_thresh = PRI_MAX_IDLE;
|
|
|
|
#else
|
2007-07-17 22:53:23 +00:00
|
|
|
static int preempt_thresh = PRI_MIN_KERN;
|
2007-09-27 16:39:27 +00:00
|
|
|
#endif
|
|
|
|
#else
|
|
|
|
static int preempt_thresh = 0;
|
|
|
|
#endif
|
2008-04-04 01:16:18 +00:00
|
|
|
static int static_boost = PRI_MIN_TIMESHARE;
|
2008-04-17 09:56:01 +00:00
|
|
|
static int sched_idlespins = 10000;
|
|
|
|
static int sched_idlespinthresh = 4;
|
2007-07-17 22:53:23 +00:00
|
|
|
|
2003-01-26 05:23:15 +00:00
|
|
|
/*
|
2007-07-17 22:53:23 +00:00
|
|
|
* tdq - per processor runqs and statistics. All fields are protected by the
|
|
|
|
* tdq_lock. The load and lowpri may be accessed without to avoid excess
|
|
|
|
* locking in sched_pickcpu();
|
2003-01-26 05:23:15 +00:00
|
|
|
*/
|
2006-12-06 06:34:57 +00:00
|
|
|
struct tdq {
|
2008-03-10 03:15:19 +00:00
|
|
|
/* Ordered to improve efficiency of cpu_search() and switch(). */
|
2008-03-02 08:20:59 +00:00
|
|
|
struct mtx tdq_lock; /* run queue lock. */
|
2008-03-10 03:15:19 +00:00
|
|
|
struct cpu_group *tdq_cg; /* Pointer to cpu topology. */
|
2008-04-17 09:56:01 +00:00
|
|
|
volatile int tdq_load; /* Aggregate load. */
|
2008-03-02 08:20:59 +00:00
|
|
|
int tdq_sysload; /* For loadavg, !ITHD load. */
|
2008-03-10 03:15:19 +00:00
|
|
|
int tdq_transferable; /* Transferable thread count. */
|
2008-04-17 09:56:01 +00:00
|
|
|
volatile int tdq_idlestate; /* State of the idle thread. */
|
|
|
|
short tdq_switchcnt; /* Switches this tick. */
|
|
|
|
short tdq_oldswitchcnt; /* Switches last tick. */
|
2007-07-17 22:53:23 +00:00
|
|
|
u_char tdq_lowpri; /* Lowest priority thread. */
|
2008-03-10 01:32:01 +00:00
|
|
|
u_char tdq_ipipending; /* IPI pending. */
|
2008-03-10 03:15:19 +00:00
|
|
|
u_char tdq_idx; /* Current insert index. */
|
|
|
|
u_char tdq_ridx; /* Current removal index. */
|
|
|
|
struct runq tdq_realtime; /* real-time run queue. */
|
|
|
|
struct runq tdq_timeshare; /* timeshare run queue. */
|
|
|
|
struct runq tdq_idle; /* Queue of IDLE threads. */
|
2008-03-02 08:20:59 +00:00
|
|
|
char tdq_name[sizeof("sched lock") + 6];
|
2007-07-17 22:53:23 +00:00
|
|
|
} __aligned(64);
|
2003-01-26 05:23:15 +00:00
|
|
|
|
2008-04-17 09:56:01 +00:00
|
|
|
/* Idle thread states and config. */
|
|
|
|
#define TDQ_RUNNING 1
|
|
|
|
#define TDQ_IDLE 2
|
2007-01-19 21:56:08 +00:00
|
|
|
|
2003-12-11 03:57:10 +00:00
|
|
|
#ifdef SMP
|
Introduce a new sysctl, kern.sched.topology_spec, that returns an XML
dump of detected ULE CPU topology. This dump can be used to check the
topology detection and for general system information.
An example of CPU topology dump is:
kern.sched.topology_spec: <groups>
<group level="1" cache-level="0">
<cpu count="8" mask="0xff">0, 1, 2, 3, 4, 5, 6, 7</cpu>
<flags></flags>
<children>
<group level="2" cache-level="0">
<cpu count="4" mask="0xf">0, 1, 2, 3</cpu>
<flags></flags>
</group>
<group level="2" cache-level="0">
<cpu count="4" mask="0xf0">4, 5, 6, 7</cpu>
<flags></flags>
</group>
</children>
</group>
</groups>
Reviewed by: jeff
Approved by: gnn (mentor)
2008-10-29 13:36:23 +00:00
|
|
|
struct cpu_group *cpu_top; /* CPU topology */
|
2007-01-19 21:56:08 +00:00
|
|
|
|
2008-03-02 08:20:59 +00:00
|
|
|
#define SCHED_AFFINITY_DEFAULT (max(1, hz / 1000))
|
|
|
|
#define SCHED_AFFINITY(ts, t) ((ts)->ts_rltick > ticks - ((t) * affinity))
|
2007-01-19 21:56:08 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Run-time tunables.
|
|
|
|
*/
|
2007-07-19 20:03:15 +00:00
|
|
|
static int rebalance = 1;
|
2007-10-02 00:36:06 +00:00
|
|
|
static int balance_interval = 128; /* Default set in sched_initticks(). */
|
2007-01-19 21:56:08 +00:00
|
|
|
static int affinity;
|
2007-10-02 00:36:06 +00:00
|
|
|
static int steal_htt = 1;
|
2007-07-19 20:03:15 +00:00
|
|
|
static int steal_idle = 1;
|
|
|
|
static int steal_thresh = 2;
|
2003-12-11 03:57:10 +00:00
|
|
|
|
2003-01-26 05:23:15 +00:00
|
|
|
/*
|
2006-12-29 10:37:07 +00:00
|
|
|
* One thread queue per processor.
|
2003-01-26 05:23:15 +00:00
|
|
|
*/
|
2006-12-06 06:34:57 +00:00
|
|
|
static struct tdq tdq_cpu[MAXCPU];
|
2007-10-02 00:36:06 +00:00
|
|
|
static struct tdq *balance_tdq;
|
|
|
|
static int balance_ticks;
|
2004-06-02 05:46:48 +00:00
|
|
|
|
2006-12-06 06:34:57 +00:00
|
|
|
#define TDQ_SELF() (&tdq_cpu[PCPU_GET(cpuid)])
|
|
|
|
#define TDQ_CPU(x) (&tdq_cpu[(x)])
|
2007-08-03 23:38:46 +00:00
|
|
|
#define TDQ_ID(x) ((int)((x) - tdq_cpu))
|
2003-12-11 03:57:10 +00:00
|
|
|
#else /* !SMP */
|
2006-12-06 06:34:57 +00:00
|
|
|
static struct tdq tdq_cpu;
|
2004-06-02 05:46:48 +00:00
|
|
|
|
2007-06-05 02:53:51 +00:00
|
|
|
#define TDQ_ID(x) (0)
|
2006-12-06 06:34:57 +00:00
|
|
|
#define TDQ_SELF() (&tdq_cpu)
|
|
|
|
#define TDQ_CPU(x) (&tdq_cpu)
|
2003-01-29 07:00:51 +00:00
|
|
|
#endif
|
2003-01-26 05:23:15 +00:00
|
|
|
|
2007-07-17 22:53:23 +00:00
|
|
|
#define TDQ_LOCK_ASSERT(t, type) mtx_assert(TDQ_LOCKPTR((t)), (type))
|
|
|
|
#define TDQ_LOCK(t) mtx_lock_spin(TDQ_LOCKPTR((t)))
|
|
|
|
#define TDQ_LOCK_FLAGS(t, f) mtx_lock_spin_flags(TDQ_LOCKPTR((t)), (f))
|
|
|
|
#define TDQ_UNLOCK(t) mtx_unlock_spin(TDQ_LOCKPTR((t)))
|
2008-03-02 08:20:59 +00:00
|
|
|
#define TDQ_LOCKPTR(t) (&(t)->tdq_lock)
|
2007-07-17 22:53:23 +00:00
|
|
|
|
2006-10-26 21:42:22 +00:00
|
|
|
static void sched_priority(struct thread *);
|
2005-06-04 09:23:28 +00:00
|
|
|
static void sched_thread_priority(struct thread *, u_char);
|
2006-10-26 21:42:22 +00:00
|
|
|
static int sched_interact_score(struct thread *);
|
|
|
|
static void sched_interact_update(struct thread *);
|
|
|
|
static void sched_interact_fork(struct thread *);
|
2006-12-06 06:34:57 +00:00
|
|
|
static void sched_pctcpu_update(struct td_sched *);
|
2003-01-26 05:23:15 +00:00
|
|
|
|
2003-02-03 05:30:07 +00:00
|
|
|
/* Operations on per processor queues */
|
2008-03-20 05:51:16 +00:00
|
|
|
static struct thread *tdq_choose(struct tdq *);
|
2006-12-06 06:34:57 +00:00
|
|
|
static void tdq_setup(struct tdq *);
|
2008-03-20 05:51:16 +00:00
|
|
|
static void tdq_load_add(struct tdq *, struct thread *);
|
|
|
|
static void tdq_load_rem(struct tdq *, struct thread *);
|
|
|
|
static __inline void tdq_runq_add(struct tdq *, struct thread *, int);
|
|
|
|
static __inline void tdq_runq_rem(struct tdq *, struct thread *);
|
2008-03-10 01:32:01 +00:00
|
|
|
static inline int sched_shouldpreempt(int, int, int);
|
2006-12-06 06:34:57 +00:00
|
|
|
void tdq_print(int cpu);
|
2007-01-04 08:56:25 +00:00
|
|
|
static void runq_print(struct runq *rq);
|
2007-07-17 22:53:23 +00:00
|
|
|
static void tdq_add(struct tdq *, struct thread *, int);
|
2003-02-03 05:30:07 +00:00
|
|
|
#ifdef SMP
|
2008-03-02 08:20:59 +00:00
|
|
|
static int tdq_move(struct tdq *, struct tdq *);
|
2006-12-06 06:34:57 +00:00
|
|
|
static int tdq_idled(struct tdq *);
|
2008-03-20 05:51:16 +00:00
|
|
|
static void tdq_notify(struct tdq *, struct thread *);
|
|
|
|
static struct thread *tdq_steal(struct tdq *, int);
|
|
|
|
static struct thread *runq_steal(struct runq *, int);
|
|
|
|
static int sched_pickcpu(struct thread *, int);
|
2007-10-02 00:36:06 +00:00
|
|
|
static void sched_balance(void);
|
2008-03-02 08:20:59 +00:00
|
|
|
static int sched_balance_pair(struct tdq *, struct tdq *);
|
2008-03-20 05:51:16 +00:00
|
|
|
static inline struct tdq *sched_setcpu(struct thread *, int, int);
|
2007-07-17 22:53:23 +00:00
|
|
|
static inline struct mtx *thread_block_switch(struct thread *);
|
|
|
|
static inline void thread_unblock_switch(struct thread *, struct mtx *);
|
2007-08-03 23:38:46 +00:00
|
|
|
static struct mtx *sched_switch_migrate(struct tdq *, struct thread *, int);
|
Introduce a new sysctl, kern.sched.topology_spec, that returns an XML
dump of detected ULE CPU topology. This dump can be used to check the
topology detection and for general system information.
An example of CPU topology dump is:
kern.sched.topology_spec: <groups>
<group level="1" cache-level="0">
<cpu count="8" mask="0xff">0, 1, 2, 3, 4, 5, 6, 7</cpu>
<flags></flags>
<children>
<group level="2" cache-level="0">
<cpu count="4" mask="0xf">0, 1, 2, 3</cpu>
<flags></flags>
</group>
<group level="2" cache-level="0">
<cpu count="4" mask="0xf0">4, 5, 6, 7</cpu>
<flags></flags>
</group>
</children>
</group>
</groups>
Reviewed by: jeff
Approved by: gnn (mentor)
2008-10-29 13:36:23 +00:00
|
|
|
static int sysctl_kern_sched_topology_spec(SYSCTL_HANDLER_ARGS);
|
|
|
|
static int sysctl_kern_sched_topology_spec_internal(struct sbuf *sb,
|
|
|
|
struct cpu_group *cg, int indent);
|
2003-02-03 05:30:07 +00:00
|
|
|
#endif
|
|
|
|
|
2007-01-04 08:56:25 +00:00
|
|
|
static void sched_setup(void *dummy);
|
2008-03-16 10:58:09 +00:00
|
|
|
SYSINIT(sched_setup, SI_SUB_RUN_QUEUE, SI_ORDER_FIRST, sched_setup, NULL);
|
2007-01-04 08:56:25 +00:00
|
|
|
|
|
|
|
static void sched_initticks(void *dummy);
|
2008-03-16 10:58:09 +00:00
|
|
|
SYSINIT(sched_initticks, SI_SUB_CLOCKS, SI_ORDER_THIRD, sched_initticks,
|
|
|
|
NULL);
|
2007-01-04 08:56:25 +00:00
|
|
|
|
2007-07-17 22:53:23 +00:00
|
|
|
/*
|
|
|
|
* Print the threads waiting on a run-queue.
|
|
|
|
*/
|
2007-01-04 08:56:25 +00:00
|
|
|
static void
|
|
|
|
runq_print(struct runq *rq)
|
|
|
|
{
|
|
|
|
struct rqhead *rqh;
|
2008-03-20 05:51:16 +00:00
|
|
|
struct thread *td;
|
2007-01-04 08:56:25 +00:00
|
|
|
int pri;
|
|
|
|
int j;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < RQB_LEN; i++) {
|
|
|
|
printf("\t\trunq bits %d 0x%zx\n",
|
|
|
|
i, rq->rq_status.rqb_bits[i]);
|
|
|
|
for (j = 0; j < RQB_BPW; j++)
|
|
|
|
if (rq->rq_status.rqb_bits[i] & (1ul << j)) {
|
|
|
|
pri = j + (i << RQB_L2BPW);
|
|
|
|
rqh = &rq->rq_queues[pri];
|
2008-03-20 05:51:16 +00:00
|
|
|
TAILQ_FOREACH(td, rqh, td_runq) {
|
2007-01-04 08:56:25 +00:00
|
|
|
printf("\t\t\ttd %p(%s) priority %d rqindex %d pri %d\n",
|
2008-03-20 05:51:16 +00:00
|
|
|
td, td->td_name, td->td_priority,
|
|
|
|
td->td_rqindex, pri);
|
2007-01-04 08:56:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-07-17 22:53:23 +00:00
|
|
|
/*
|
|
|
|
* Print the status of a per-cpu thread queue. Should be a ddb show cmd.
|
|
|
|
*/
|
2003-04-11 03:47:14 +00:00
|
|
|
void
|
2006-12-06 06:34:57 +00:00
|
|
|
tdq_print(int cpu)
|
2003-02-03 05:30:07 +00:00
|
|
|
{
|
2006-12-06 06:34:57 +00:00
|
|
|
struct tdq *tdq;
|
2003-04-03 00:29:28 +00:00
|
|
|
|
2006-12-06 06:34:57 +00:00
|
|
|
tdq = TDQ_CPU(cpu);
|
2003-04-03 00:29:28 +00:00
|
|
|
|
2007-08-03 23:38:46 +00:00
|
|
|
printf("tdq %d:\n", TDQ_ID(tdq));
|
2008-03-02 08:20:59 +00:00
|
|
|
printf("\tlock %p\n", TDQ_LOCKPTR(tdq));
|
|
|
|
printf("\tLock name: %s\n", tdq->tdq_name);
|
2006-12-29 10:37:07 +00:00
|
|
|
printf("\tload: %d\n", tdq->tdq_load);
|
2008-04-17 09:56:01 +00:00
|
|
|
printf("\tswitch cnt: %d\n", tdq->tdq_switchcnt);
|
|
|
|
printf("\told switch cnt: %d\n", tdq->tdq_oldswitchcnt);
|
|
|
|
printf("\tidle state: %d\n", tdq->tdq_idlestate);
|
2007-07-17 22:53:23 +00:00
|
|
|
printf("\ttimeshare idx: %d\n", tdq->tdq_idx);
|
2007-01-04 12:16:19 +00:00
|
|
|
printf("\ttimeshare ridx: %d\n", tdq->tdq_ridx);
|
2008-04-17 09:56:01 +00:00
|
|
|
printf("\tload transferable: %d\n", tdq->tdq_transferable);
|
|
|
|
printf("\tlowest priority: %d\n", tdq->tdq_lowpri);
|
2007-01-04 08:56:25 +00:00
|
|
|
printf("\trealtime runq:\n");
|
|
|
|
runq_print(&tdq->tdq_realtime);
|
|
|
|
printf("\ttimeshare runq:\n");
|
|
|
|
runq_print(&tdq->tdq_timeshare);
|
|
|
|
printf("\tidle runq:\n");
|
|
|
|
runq_print(&tdq->tdq_idle);
|
2003-04-11 03:47:14 +00:00
|
|
|
}
|
2003-04-03 00:29:28 +00:00
|
|
|
|
2008-03-10 01:32:01 +00:00
|
|
|
static inline int
|
|
|
|
sched_shouldpreempt(int pri, int cpri, int remote)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* If the new priority is not better than the current priority there is
|
|
|
|
* nothing to do.
|
|
|
|
*/
|
|
|
|
if (pri >= cpri)
|
|
|
|
return (0);
|
|
|
|
/*
|
|
|
|
* Always preempt idle.
|
|
|
|
*/
|
|
|
|
if (cpri >= PRI_MIN_IDLE)
|
|
|
|
return (1);
|
|
|
|
/*
|
|
|
|
* If preemption is disabled don't preempt others.
|
|
|
|
*/
|
|
|
|
if (preempt_thresh == 0)
|
|
|
|
return (0);
|
|
|
|
/*
|
|
|
|
* Preempt if we exceed the threshold.
|
|
|
|
*/
|
|
|
|
if (pri <= preempt_thresh)
|
|
|
|
return (1);
|
|
|
|
/*
|
|
|
|
* If we're realtime or better and there is timeshare or worse running
|
|
|
|
* preempt only remote processors.
|
|
|
|
*/
|
|
|
|
if (remote && pri <= PRI_MAX_REALTIME && cpri > PRI_MAX_REALTIME)
|
|
|
|
return (1);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
2007-07-17 22:53:23 +00:00
|
|
|
#define TS_RQ_PPQ (((PRI_MAX_TIMESHARE - PRI_MIN_TIMESHARE) + 1) / RQ_NQS)
|
|
|
|
/*
|
|
|
|
* Add a thread to the actual run-queue. Keeps transferable counts up to
|
|
|
|
* date with what is actually on the run-queue. Selects the correct
|
|
|
|
* queue position for timeshare threads.
|
|
|
|
*/
|
2003-11-15 07:32:07 +00:00
|
|
|
static __inline void
|
2008-03-20 05:51:16 +00:00
|
|
|
tdq_runq_add(struct tdq *tdq, struct thread *td, int flags)
|
2003-11-15 07:32:07 +00:00
|
|
|
{
|
2008-03-20 05:51:16 +00:00
|
|
|
struct td_sched *ts;
|
2008-03-10 22:48:27 +00:00
|
|
|
u_char pri;
|
|
|
|
|
2007-07-17 22:53:23 +00:00
|
|
|
TDQ_LOCK_ASSERT(tdq, MA_OWNED);
|
2008-03-20 05:51:16 +00:00
|
|
|
THREAD_LOCK_ASSERT(td, MA_OWNED);
|
2008-03-10 03:15:19 +00:00
|
|
|
|
2008-03-20 05:51:16 +00:00
|
|
|
pri = td->td_priority;
|
|
|
|
ts = td->td_sched;
|
|
|
|
TD_SET_RUNQ(td);
|
|
|
|
if (THREAD_CAN_MIGRATE(td)) {
|
2006-12-29 10:37:07 +00:00
|
|
|
tdq->tdq_transferable++;
|
2006-12-06 06:34:57 +00:00
|
|
|
ts->ts_flags |= TSF_XFERABLE;
|
2003-12-11 03:57:10 +00:00
|
|
|
}
|
2008-03-10 22:48:27 +00:00
|
|
|
if (pri <= PRI_MAX_REALTIME) {
|
|
|
|
ts->ts_runq = &tdq->tdq_realtime;
|
|
|
|
} else if (pri <= PRI_MAX_TIMESHARE) {
|
|
|
|
ts->ts_runq = &tdq->tdq_timeshare;
|
2007-01-04 08:56:25 +00:00
|
|
|
KASSERT(pri <= PRI_MAX_TIMESHARE && pri >= PRI_MIN_TIMESHARE,
|
|
|
|
("Invalid priority %d on timeshare runq", pri));
|
|
|
|
/*
|
|
|
|
* This queue contains only priorities between MIN and MAX
|
|
|
|
* realtime. Use the whole queue to represent these values.
|
|
|
|
*/
|
2007-08-03 23:38:46 +00:00
|
|
|
if ((flags & (SRQ_BORROWING|SRQ_PREEMPTED)) == 0) {
|
2007-01-04 08:56:25 +00:00
|
|
|
pri = (pri - PRI_MIN_TIMESHARE) / TS_RQ_PPQ;
|
|
|
|
pri = (pri + tdq->tdq_idx) % RQ_NQS;
|
2007-01-04 12:16:19 +00:00
|
|
|
/*
|
|
|
|
* This effectively shortens the queue by one so we
|
|
|
|
* can have a one slot difference between idx and
|
|
|
|
* ridx while we wait for threads to drain.
|
|
|
|
*/
|
|
|
|
if (tdq->tdq_ridx != tdq->tdq_idx &&
|
|
|
|
pri == tdq->tdq_ridx)
|
2007-03-17 18:13:32 +00:00
|
|
|
pri = (unsigned char)(pri - 1) % RQ_NQS;
|
2007-01-04 08:56:25 +00:00
|
|
|
} else
|
2007-01-04 12:16:19 +00:00
|
|
|
pri = tdq->tdq_ridx;
|
2008-03-20 05:51:16 +00:00
|
|
|
runq_add_pri(ts->ts_runq, td, pri, flags);
|
2008-03-10 22:48:27 +00:00
|
|
|
return;
|
2007-01-04 08:56:25 +00:00
|
|
|
} else
|
2008-03-10 03:15:19 +00:00
|
|
|
ts->ts_runq = &tdq->tdq_idle;
|
2008-03-20 05:51:16 +00:00
|
|
|
runq_add(ts->ts_runq, td, flags);
|
2008-03-10 03:15:19 +00:00
|
|
|
}
|
|
|
|
|
2007-07-17 22:53:23 +00:00
|
|
|
/*
|
|
|
|
* Remove a thread from a run-queue. This typically happens when a thread
|
|
|
|
* is selected to run. Running threads are not on the queue and the
|
|
|
|
* transferable count does not reflect them.
|
|
|
|
*/
|
2003-11-15 07:32:07 +00:00
|
|
|
static __inline void
|
2008-03-20 05:51:16 +00:00
|
|
|
tdq_runq_rem(struct tdq *tdq, struct thread *td)
|
2003-11-15 07:32:07 +00:00
|
|
|
{
|
2008-03-20 05:51:16 +00:00
|
|
|
struct td_sched *ts;
|
|
|
|
|
|
|
|
ts = td->td_sched;
|
2007-07-17 22:53:23 +00:00
|
|
|
TDQ_LOCK_ASSERT(tdq, MA_OWNED);
|
|
|
|
KASSERT(ts->ts_runq != NULL,
|
2008-03-20 05:51:16 +00:00
|
|
|
("tdq_runq_remove: thread %p null ts_runq", td));
|
2006-12-06 06:34:57 +00:00
|
|
|
if (ts->ts_flags & TSF_XFERABLE) {
|
2006-12-29 10:37:07 +00:00
|
|
|
tdq->tdq_transferable--;
|
2006-12-06 06:34:57 +00:00
|
|
|
ts->ts_flags &= ~TSF_XFERABLE;
|
2003-12-11 03:57:10 +00:00
|
|
|
}
|
2007-01-04 12:16:19 +00:00
|
|
|
if (ts->ts_runq == &tdq->tdq_timeshare) {
|
|
|
|
if (tdq->tdq_idx != tdq->tdq_ridx)
|
2008-03-20 05:51:16 +00:00
|
|
|
runq_remove_idx(ts->ts_runq, td, &tdq->tdq_ridx);
|
2007-01-04 12:16:19 +00:00
|
|
|
else
|
2008-03-20 05:51:16 +00:00
|
|
|
runq_remove_idx(ts->ts_runq, td, NULL);
|
2007-01-04 12:16:19 +00:00
|
|
|
} else
|
2008-03-20 05:51:16 +00:00
|
|
|
runq_remove(ts->ts_runq, td);
|
2003-11-15 07:32:07 +00:00
|
|
|
}
|
|
|
|
|
2007-07-17 22:53:23 +00:00
|
|
|
/*
|
|
|
|
* Load is maintained for all threads RUNNING and ON_RUNQ. Add the load
|
|
|
|
* for this thread to the referenced thread queue.
|
|
|
|
*/
|
2003-04-11 03:47:14 +00:00
|
|
|
static void
|
2008-03-20 05:51:16 +00:00
|
|
|
tdq_load_add(struct tdq *tdq, struct thread *td)
|
2003-04-11 03:47:14 +00:00
|
|
|
{
|
2007-07-17 22:53:23 +00:00
|
|
|
|
|
|
|
TDQ_LOCK_ASSERT(tdq, MA_OWNED);
|
2008-03-20 05:51:16 +00:00
|
|
|
THREAD_LOCK_ASSERT(td, MA_OWNED);
|
2008-04-04 01:04:43 +00:00
|
|
|
|
2006-12-29 10:37:07 +00:00
|
|
|
tdq->tdq_load++;
|
2008-04-04 01:04:43 +00:00
|
|
|
if ((td->td_proc->p_flag & P_NOLOAD) == 0)
|
2006-12-29 10:37:07 +00:00
|
|
|
tdq->tdq_sysload++;
|
2008-04-04 01:04:43 +00:00
|
|
|
CTR2(KTR_SCHED, "cpu %d load: %d", TDQ_ID(tdq), tdq->tdq_load);
|
2003-02-03 05:30:07 +00:00
|
|
|
}
|
2003-04-11 03:47:14 +00:00
|
|
|
|
2007-07-17 22:53:23 +00:00
|
|
|
/*
|
|
|
|
* Remove the load from a thread that is transitioning to a sleep state or
|
|
|
|
* exiting.
|
|
|
|
*/
|
2003-04-03 00:29:28 +00:00
|
|
|
static void
|
2008-03-20 05:51:16 +00:00
|
|
|
tdq_load_rem(struct tdq *tdq, struct thread *td)
|
2003-02-03 05:30:07 +00:00
|
|
|
{
|
2007-07-17 22:53:23 +00:00
|
|
|
|
2008-03-20 05:51:16 +00:00
|
|
|
THREAD_LOCK_ASSERT(td, MA_OWNED);
|
2007-07-17 22:53:23 +00:00
|
|
|
TDQ_LOCK_ASSERT(tdq, MA_OWNED);
|
|
|
|
KASSERT(tdq->tdq_load != 0,
|
2007-08-03 23:38:46 +00:00
|
|
|
("tdq_load_rem: Removing with 0 load on queue %d", TDQ_ID(tdq)));
|
2008-04-04 01:04:43 +00:00
|
|
|
|
2006-12-29 10:37:07 +00:00
|
|
|
tdq->tdq_load--;
|
2008-04-04 01:04:43 +00:00
|
|
|
if ((td->td_proc->p_flag & P_NOLOAD) == 0)
|
|
|
|
tdq->tdq_sysload--;
|
2006-12-29 10:37:07 +00:00
|
|
|
CTR1(KTR_SCHED, "load: %d", tdq->tdq_load);
|
2003-02-03 05:30:07 +00:00
|
|
|
}
|
|
|
|
|
2008-03-02 08:20:59 +00:00
|
|
|
/*
|
|
|
|
* Set lowpri to its exact value by searching the run-queue and
|
|
|
|
* evaluating curthread. curthread may be passed as an optimization.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
tdq_setlowpri(struct tdq *tdq, struct thread *ctd)
|
|
|
|
{
|
|
|
|
struct thread *td;
|
|
|
|
|
|
|
|
TDQ_LOCK_ASSERT(tdq, MA_OWNED);
|
|
|
|
if (ctd == NULL)
|
|
|
|
ctd = pcpu_find(TDQ_ID(tdq))->pc_curthread;
|
2008-03-20 05:51:16 +00:00
|
|
|
td = tdq_choose(tdq);
|
|
|
|
if (td == NULL || td->td_priority > ctd->td_priority)
|
2008-03-02 08:20:59 +00:00
|
|
|
tdq->tdq_lowpri = ctd->td_priority;
|
|
|
|
else
|
|
|
|
tdq->tdq_lowpri = td->td_priority;
|
|
|
|
}
|
|
|
|
|
2003-04-11 03:47:14 +00:00
|
|
|
#ifdef SMP
|
2008-03-02 08:20:59 +00:00
|
|
|
struct cpu_search {
|
|
|
|
cpumask_t cs_mask; /* Mask of valid cpus. */
|
|
|
|
u_int cs_load;
|
|
|
|
u_int cs_cpu;
|
|
|
|
int cs_limit; /* Min priority for low min load for high. */
|
|
|
|
};
|
|
|
|
|
|
|
|
#define CPU_SEARCH_LOWEST 0x1
|
|
|
|
#define CPU_SEARCH_HIGHEST 0x2
|
|
|
|
#define CPU_SEARCH_BOTH (CPU_SEARCH_LOWEST|CPU_SEARCH_HIGHEST)
|
|
|
|
|
|
|
|
#define CPUMASK_FOREACH(cpu, mask) \
|
|
|
|
for ((cpu) = 0; (cpu) < sizeof((mask)) * 8; (cpu)++) \
|
|
|
|
if ((mask) & 1 << (cpu))
|
|
|
|
|
2008-03-14 15:22:38 +00:00
|
|
|
static __inline int cpu_search(struct cpu_group *cg, struct cpu_search *low,
|
2008-03-02 08:20:59 +00:00
|
|
|
struct cpu_search *high, const int match);
|
|
|
|
int cpu_search_lowest(struct cpu_group *cg, struct cpu_search *low);
|
|
|
|
int cpu_search_highest(struct cpu_group *cg, struct cpu_search *high);
|
|
|
|
int cpu_search_both(struct cpu_group *cg, struct cpu_search *low,
|
|
|
|
struct cpu_search *high);
|
|
|
|
|
2003-06-09 00:39:09 +00:00
|
|
|
/*
|
2008-03-02 08:20:59 +00:00
|
|
|
* This routine compares according to the match argument and should be
|
|
|
|
* reduced in actual instantiations via constant propagation and dead code
|
|
|
|
* elimination.
|
|
|
|
*/
|
|
|
|
static __inline int
|
|
|
|
cpu_compare(int cpu, struct cpu_search *low, struct cpu_search *high,
|
|
|
|
const int match)
|
|
|
|
{
|
|
|
|
struct tdq *tdq;
|
|
|
|
|
|
|
|
tdq = TDQ_CPU(cpu);
|
|
|
|
if (match & CPU_SEARCH_LOWEST)
|
|
|
|
if (low->cs_mask & (1 << cpu) &&
|
|
|
|
tdq->tdq_load < low->cs_load &&
|
|
|
|
tdq->tdq_lowpri > low->cs_limit) {
|
|
|
|
low->cs_cpu = cpu;
|
|
|
|
low->cs_load = tdq->tdq_load;
|
|
|
|
}
|
|
|
|
if (match & CPU_SEARCH_HIGHEST)
|
|
|
|
if (high->cs_mask & (1 << cpu) &&
|
|
|
|
tdq->tdq_load >= high->cs_limit &&
|
|
|
|
tdq->tdq_load > high->cs_load &&
|
|
|
|
tdq->tdq_transferable) {
|
|
|
|
high->cs_cpu = cpu;
|
|
|
|
high->cs_load = tdq->tdq_load;
|
|
|
|
}
|
|
|
|
return (tdq->tdq_load);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Search the tree of cpu_groups for the lowest or highest loaded cpu
|
|
|
|
* according to the match argument. This routine actually compares the
|
|
|
|
* load on all paths through the tree and finds the least loaded cpu on
|
|
|
|
* the least loaded path, which may differ from the least loaded cpu in
|
|
|
|
* the system. This balances work among caches and busses.
|
2003-06-09 00:39:09 +00:00
|
|
|
*
|
2008-03-02 08:20:59 +00:00
|
|
|
* This inline is instantiated in three forms below using constants for the
|
|
|
|
* match argument. It is reduced to the minimum set for each case. It is
|
|
|
|
* also recursive to the depth of the tree.
|
|
|
|
*/
|
2008-03-14 15:22:38 +00:00
|
|
|
static __inline int
|
2008-03-02 08:20:59 +00:00
|
|
|
cpu_search(struct cpu_group *cg, struct cpu_search *low,
|
|
|
|
struct cpu_search *high, const int match)
|
|
|
|
{
|
|
|
|
int total;
|
|
|
|
|
|
|
|
total = 0;
|
|
|
|
if (cg->cg_children) {
|
|
|
|
struct cpu_search lgroup;
|
|
|
|
struct cpu_search hgroup;
|
|
|
|
struct cpu_group *child;
|
|
|
|
u_int lload;
|
|
|
|
int hload;
|
|
|
|
int load;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
lload = -1;
|
|
|
|
hload = -1;
|
|
|
|
for (i = 0; i < cg->cg_children; i++) {
|
|
|
|
child = &cg->cg_child[i];
|
|
|
|
if (match & CPU_SEARCH_LOWEST) {
|
|
|
|
lgroup = *low;
|
|
|
|
lgroup.cs_load = -1;
|
|
|
|
}
|
|
|
|
if (match & CPU_SEARCH_HIGHEST) {
|
|
|
|
hgroup = *high;
|
|
|
|
lgroup.cs_load = 0;
|
|
|
|
}
|
|
|
|
switch (match) {
|
|
|
|
case CPU_SEARCH_LOWEST:
|
|
|
|
load = cpu_search_lowest(child, &lgroup);
|
|
|
|
break;
|
|
|
|
case CPU_SEARCH_HIGHEST:
|
|
|
|
load = cpu_search_highest(child, &hgroup);
|
|
|
|
break;
|
|
|
|
case CPU_SEARCH_BOTH:
|
|
|
|
load = cpu_search_both(child, &lgroup, &hgroup);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
total += load;
|
|
|
|
if (match & CPU_SEARCH_LOWEST)
|
|
|
|
if (load < lload || low->cs_cpu == -1) {
|
|
|
|
*low = lgroup;
|
|
|
|
lload = load;
|
|
|
|
}
|
|
|
|
if (match & CPU_SEARCH_HIGHEST)
|
|
|
|
if (load > hload || high->cs_cpu == -1) {
|
|
|
|
hload = load;
|
|
|
|
*high = hgroup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
int cpu;
|
|
|
|
|
|
|
|
CPUMASK_FOREACH(cpu, cg->cg_mask)
|
|
|
|
total += cpu_compare(cpu, low, high, match);
|
|
|
|
}
|
|
|
|
return (total);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* cpu_search instantiations must pass constants to maintain the inline
|
|
|
|
* optimization.
|
2003-06-09 00:39:09 +00:00
|
|
|
*/
|
2008-03-02 08:20:59 +00:00
|
|
|
int
|
|
|
|
cpu_search_lowest(struct cpu_group *cg, struct cpu_search *low)
|
|
|
|
{
|
|
|
|
return cpu_search(cg, low, NULL, CPU_SEARCH_LOWEST);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
cpu_search_highest(struct cpu_group *cg, struct cpu_search *high)
|
|
|
|
{
|
|
|
|
return cpu_search(cg, NULL, high, CPU_SEARCH_HIGHEST);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
cpu_search_both(struct cpu_group *cg, struct cpu_search *low,
|
|
|
|
struct cpu_search *high)
|
|
|
|
{
|
|
|
|
return cpu_search(cg, low, high, CPU_SEARCH_BOTH);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find the cpu with the least load via the least loaded path that has a
|
|
|
|
* lowpri greater than pri pri. A pri of -1 indicates any priority is
|
|
|
|
* acceptable.
|
|
|
|
*/
|
|
|
|
static inline int
|
|
|
|
sched_lowest(struct cpu_group *cg, cpumask_t mask, int pri)
|
|
|
|
{
|
|
|
|
struct cpu_search low;
|
|
|
|
|
|
|
|
low.cs_cpu = -1;
|
|
|
|
low.cs_load = -1;
|
|
|
|
low.cs_mask = mask;
|
|
|
|
low.cs_limit = pri;
|
|
|
|
cpu_search_lowest(cg, &low);
|
|
|
|
return low.cs_cpu;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find the cpu with the highest load via the highest loaded path.
|
|
|
|
*/
|
|
|
|
static inline int
|
|
|
|
sched_highest(struct cpu_group *cg, cpumask_t mask, int minload)
|
|
|
|
{
|
|
|
|
struct cpu_search high;
|
|
|
|
|
|
|
|
high.cs_cpu = -1;
|
|
|
|
high.cs_load = 0;
|
|
|
|
high.cs_mask = mask;
|
|
|
|
high.cs_limit = minload;
|
|
|
|
cpu_search_highest(cg, &high);
|
|
|
|
return high.cs_cpu;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Simultaneously find the highest and lowest loaded cpu reachable via
|
|
|
|
* cg.
|
|
|
|
*/
|
|
|
|
static inline void
|
|
|
|
sched_both(struct cpu_group *cg, cpumask_t mask, int *lowcpu, int *highcpu)
|
|
|
|
{
|
|
|
|
struct cpu_search high;
|
|
|
|
struct cpu_search low;
|
|
|
|
|
|
|
|
low.cs_cpu = -1;
|
|
|
|
low.cs_limit = -1;
|
|
|
|
low.cs_load = -1;
|
|
|
|
low.cs_mask = mask;
|
|
|
|
high.cs_load = 0;
|
|
|
|
high.cs_cpu = -1;
|
|
|
|
high.cs_limit = -1;
|
|
|
|
high.cs_mask = mask;
|
|
|
|
cpu_search_both(cg, &low, &high);
|
|
|
|
*lowcpu = low.cs_cpu;
|
|
|
|
*highcpu = high.cs_cpu;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
- Add static to local functions and data where it was missing.
- Add an IPI based mechanism for migrating kses. This mechanism is
broken down into several components. This is intended to reduce cache
thrashing by eliminating most cases where one cpu touches another's
run queues.
- kseq_notify() appends a kse to a lockless singly linked list and
conditionally sends an IPI to the target processor. Right now this is
protected by sched_lock but at some point I'd like to get rid of the
global lock. This is why I used something more complicated than a
standard queue.
- kseq_assign() processes our list of kses that have been assigned to us
by other processors. This simply calls sched_add() for each item on the
list after clearing the new KEF_ASSIGNED flag. This flag is used to
indicate that we have been appeneded to the assigned queue but not
added to the run queue yet.
- In sched_add(), instead of adding a KSE to another processor's queue we
use kse_notify() so that we don't touch their queue. Also in sched_add(),
if KEF_ASSIGNED is already set return immediately. This can happen if
a thread is removed and readded so that the priority is recorded properly.
- In sched_rem() return immediately if KEF_ASSIGNED is set. All callers
immediately readd simply to adjust priorites etc.
- In sched_choose(), if we're running an IDLE task or the per cpu idle thread
set our cpumask bit in 'kseq_idle' so that other processors may know that
we are idle. Before this, make a single pass through the run queues of
other processors so that we may find work more immediately if it is
available.
- In sched_runnable(), don't scan each processor's run queue, they will IPI
us if they have work for us to do.
- In sched_add(), if we're adding a thread that can be migrated and we have
plenty of work to do, try to migrate the thread to an idle kseq.
- Simplify the logic in sched_prio() and take the KEF_ASSIGNED flag into
consideration.
- No longer use kseq_choose() to steal threads, it can lose it's last
argument.
- Create a new function runq_steal() which operates like runq_choose() but
skips threads based on some criteria. Currently it will not steal
PRI_ITHD threads. In the future this will be used for CPU binding.
- Create a kseq_steal() that checks each run queue with runq_steal(), use
kseq_steal() in the places where we used kseq_choose() to steal with
before.
2003-10-31 11:16:04 +00:00
|
|
|
static void
|
2008-03-02 08:20:59 +00:00
|
|
|
sched_balance_group(struct cpu_group *cg)
|
2003-06-09 00:39:09 +00:00
|
|
|
{
|
2008-03-02 08:20:59 +00:00
|
|
|
cpumask_t mask;
|
|
|
|
int high;
|
|
|
|
int low;
|
2003-06-09 00:39:09 +00:00
|
|
|
int i;
|
|
|
|
|
2008-03-02 08:20:59 +00:00
|
|
|
mask = -1;
|
|
|
|
for (;;) {
|
|
|
|
sched_both(cg, mask, &low, &high);
|
|
|
|
if (low == high || low == -1 || high == -1)
|
|
|
|
break;
|
|
|
|
if (sched_balance_pair(TDQ_CPU(high), TDQ_CPU(low)))
|
|
|
|
break;
|
2003-12-11 03:57:10 +00:00
|
|
|
/*
|
2008-03-02 08:20:59 +00:00
|
|
|
* If we failed to move any threads determine which cpu
|
|
|
|
* to kick out of the set and try again.
|
|
|
|
*/
|
|
|
|
if (TDQ_CPU(high)->tdq_transferable == 0)
|
|
|
|
mask &= ~(1 << high);
|
|
|
|
else
|
|
|
|
mask &= ~(1 << low);
|
2003-06-09 00:39:09 +00:00
|
|
|
}
|
2008-03-02 08:20:59 +00:00
|
|
|
|
|
|
|
for (i = 0; i < cg->cg_children; i++)
|
|
|
|
sched_balance_group(&cg->cg_child[i]);
|
2003-12-12 07:33:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2008-03-02 08:20:59 +00:00
|
|
|
sched_balance()
|
2003-12-12 07:33:51 +00:00
|
|
|
{
|
2007-10-02 00:36:06 +00:00
|
|
|
struct tdq *tdq;
|
2003-12-12 07:33:51 +00:00
|
|
|
|
2007-10-02 00:36:06 +00:00
|
|
|
/*
|
|
|
|
* Select a random time between .5 * balance_interval and
|
|
|
|
* 1.5 * balance_interval.
|
|
|
|
*/
|
2008-03-02 08:20:59 +00:00
|
|
|
balance_ticks = max(balance_interval / 2, 1);
|
|
|
|
balance_ticks += random() % balance_interval;
|
2007-07-17 22:53:23 +00:00
|
|
|
if (smp_started == 0 || rebalance == 0)
|
|
|
|
return;
|
2007-10-02 00:36:06 +00:00
|
|
|
tdq = TDQ_SELF();
|
|
|
|
TDQ_UNLOCK(tdq);
|
2008-03-02 08:20:59 +00:00
|
|
|
sched_balance_group(cpu_top);
|
2007-10-02 00:36:06 +00:00
|
|
|
TDQ_LOCK(tdq);
|
2003-12-12 07:33:51 +00:00
|
|
|
}
|
|
|
|
|
2007-07-17 22:53:23 +00:00
|
|
|
/*
|
|
|
|
* Lock two thread queues using their address to maintain lock order.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
tdq_lock_pair(struct tdq *one, struct tdq *two)
|
|
|
|
{
|
|
|
|
if (one < two) {
|
|
|
|
TDQ_LOCK(one);
|
|
|
|
TDQ_LOCK_FLAGS(two, MTX_DUPOK);
|
|
|
|
} else {
|
|
|
|
TDQ_LOCK(two);
|
|
|
|
TDQ_LOCK_FLAGS(one, MTX_DUPOK);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-10-02 00:36:06 +00:00
|
|
|
/*
|
|
|
|
* Unlock two thread queues. Order is not important here.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
tdq_unlock_pair(struct tdq *one, struct tdq *two)
|
|
|
|
{
|
|
|
|
TDQ_UNLOCK(one);
|
|
|
|
TDQ_UNLOCK(two);
|
|
|
|
}
|
|
|
|
|
2007-07-17 22:53:23 +00:00
|
|
|
/*
|
|
|
|
* Transfer load between two imbalanced thread queues.
|
|
|
|
*/
|
2008-03-02 08:20:59 +00:00
|
|
|
static int
|
2006-12-06 06:34:57 +00:00
|
|
|
sched_balance_pair(struct tdq *high, struct tdq *low)
|
2003-12-12 07:33:51 +00:00
|
|
|
{
|
|
|
|
int transferable;
|
|
|
|
int high_load;
|
|
|
|
int low_load;
|
2008-03-02 08:20:59 +00:00
|
|
|
int moved;
|
2003-12-12 07:33:51 +00:00
|
|
|
int move;
|
|
|
|
int diff;
|
|
|
|
int i;
|
|
|
|
|
2007-07-17 22:53:23 +00:00
|
|
|
tdq_lock_pair(high, low);
|
2008-03-02 08:20:59 +00:00
|
|
|
transferable = high->tdq_transferable;
|
|
|
|
high_load = high->tdq_load;
|
|
|
|
low_load = low->tdq_load;
|
|
|
|
moved = 0;
|
2003-11-15 07:32:07 +00:00
|
|
|
/*
|
|
|
|
* Determine what the imbalance is and then adjust that to how many
|
2006-12-29 10:37:07 +00:00
|
|
|
* threads we actually have to give up (transferable).
|
2003-11-15 07:32:07 +00:00
|
|
|
*/
|
2007-07-17 22:53:23 +00:00
|
|
|
if (transferable != 0) {
|
|
|
|
diff = high_load - low_load;
|
|
|
|
move = diff / 2;
|
|
|
|
if (diff & 0x1)
|
|
|
|
move++;
|
|
|
|
move = min(move, transferable);
|
|
|
|
for (i = 0; i < move; i++)
|
2008-03-02 08:20:59 +00:00
|
|
|
moved += tdq_move(high, low);
|
2007-09-22 02:20:14 +00:00
|
|
|
/*
|
|
|
|
* IPI the target cpu to force it to reschedule with the new
|
|
|
|
* workload.
|
|
|
|
*/
|
|
|
|
ipi_selected(1 << TDQ_ID(low), IPI_PREEMPT);
|
2007-07-17 22:53:23 +00:00
|
|
|
}
|
2007-10-02 00:36:06 +00:00
|
|
|
tdq_unlock_pair(high, low);
|
2008-03-02 08:20:59 +00:00
|
|
|
return (moved);
|
2003-06-09 00:39:09 +00:00
|
|
|
}
|
|
|
|
|
2007-07-17 22:53:23 +00:00
|
|
|
/*
|
|
|
|
* Move a thread from one thread queue to another.
|
|
|
|
*/
|
2008-03-02 08:20:59 +00:00
|
|
|
static int
|
2007-07-17 22:53:23 +00:00
|
|
|
tdq_move(struct tdq *from, struct tdq *to)
|
2003-06-09 00:39:09 +00:00
|
|
|
{
|
2006-12-06 06:34:57 +00:00
|
|
|
struct td_sched *ts;
|
2007-07-17 22:53:23 +00:00
|
|
|
struct thread *td;
|
|
|
|
struct tdq *tdq;
|
|
|
|
int cpu;
|
2006-12-06 06:34:57 +00:00
|
|
|
|
2007-10-02 00:36:06 +00:00
|
|
|
TDQ_LOCK_ASSERT(from, MA_OWNED);
|
|
|
|
TDQ_LOCK_ASSERT(to, MA_OWNED);
|
|
|
|
|
2006-12-06 06:34:57 +00:00
|
|
|
tdq = from;
|
2007-07-17 22:53:23 +00:00
|
|
|
cpu = TDQ_ID(to);
|
2008-03-20 05:51:16 +00:00
|
|
|
td = tdq_steal(tdq, cpu);
|
|
|
|
if (td == NULL)
|
2008-03-02 08:20:59 +00:00
|
|
|
return (0);
|
2008-03-20 05:51:16 +00:00
|
|
|
ts = td->td_sched;
|
2007-07-17 22:53:23 +00:00
|
|
|
/*
|
|
|
|
* Although the run queue is locked the thread may be blocked. Lock
|
2007-10-02 00:36:06 +00:00
|
|
|
* it to clear this and acquire the run-queue lock.
|
2007-07-17 22:53:23 +00:00
|
|
|
*/
|
|
|
|
thread_lock(td);
|
2007-10-02 00:36:06 +00:00
|
|
|
/* Drop recursive lock on from acquired via thread_lock(). */
|
2007-07-17 22:53:23 +00:00
|
|
|
TDQ_UNLOCK(from);
|
|
|
|
sched_rem(td);
|
2007-01-19 21:56:08 +00:00
|
|
|
ts->ts_cpu = cpu;
|
2007-07-17 22:53:23 +00:00
|
|
|
td->td_lock = TDQ_LOCKPTR(to);
|
|
|
|
tdq_add(to, td, SRQ_YIELDING);
|
2008-03-02 08:20:59 +00:00
|
|
|
return (1);
|
2003-06-09 00:39:09 +00:00
|
|
|
}
|
- Add static to local functions and data where it was missing.
- Add an IPI based mechanism for migrating kses. This mechanism is
broken down into several components. This is intended to reduce cache
thrashing by eliminating most cases where one cpu touches another's
run queues.
- kseq_notify() appends a kse to a lockless singly linked list and
conditionally sends an IPI to the target processor. Right now this is
protected by sched_lock but at some point I'd like to get rid of the
global lock. This is why I used something more complicated than a
standard queue.
- kseq_assign() processes our list of kses that have been assigned to us
by other processors. This simply calls sched_add() for each item on the
list after clearing the new KEF_ASSIGNED flag. This flag is used to
indicate that we have been appeneded to the assigned queue but not
added to the run queue yet.
- In sched_add(), instead of adding a KSE to another processor's queue we
use kse_notify() so that we don't touch their queue. Also in sched_add(),
if KEF_ASSIGNED is already set return immediately. This can happen if
a thread is removed and readded so that the priority is recorded properly.
- In sched_rem() return immediately if KEF_ASSIGNED is set. All callers
immediately readd simply to adjust priorites etc.
- In sched_choose(), if we're running an IDLE task or the per cpu idle thread
set our cpumask bit in 'kseq_idle' so that other processors may know that
we are idle. Before this, make a single pass through the run queues of
other processors so that we may find work more immediately if it is
available.
- In sched_runnable(), don't scan each processor's run queue, they will IPI
us if they have work for us to do.
- In sched_add(), if we're adding a thread that can be migrated and we have
plenty of work to do, try to migrate the thread to an idle kseq.
- Simplify the logic in sched_prio() and take the KEF_ASSIGNED flag into
consideration.
- No longer use kseq_choose() to steal threads, it can lose it's last
argument.
- Create a new function runq_steal() which operates like runq_choose() but
skips threads based on some criteria. Currently it will not steal
PRI_ITHD threads. In the future this will be used for CPU binding.
- Create a kseq_steal() that checks each run queue with runq_steal(), use
kseq_steal() in the places where we used kseq_choose() to steal with
before.
2003-10-31 11:16:04 +00:00
|
|
|
|
2007-07-17 22:53:23 +00:00
|
|
|
/*
|
|
|
|
* This tdq has idled. Try to steal a thread from another cpu and switch
|
|
|
|
* to it.
|
|
|
|
*/
|
2003-12-11 03:57:10 +00:00
|
|
|
static int
|
2006-12-06 06:34:57 +00:00
|
|
|
tdq_idled(struct tdq *tdq)
|
- Add static to local functions and data where it was missing.
- Add an IPI based mechanism for migrating kses. This mechanism is
broken down into several components. This is intended to reduce cache
thrashing by eliminating most cases where one cpu touches another's
run queues.
- kseq_notify() appends a kse to a lockless singly linked list and
conditionally sends an IPI to the target processor. Right now this is
protected by sched_lock but at some point I'd like to get rid of the
global lock. This is why I used something more complicated than a
standard queue.
- kseq_assign() processes our list of kses that have been assigned to us
by other processors. This simply calls sched_add() for each item on the
list after clearing the new KEF_ASSIGNED flag. This flag is used to
indicate that we have been appeneded to the assigned queue but not
added to the run queue yet.
- In sched_add(), instead of adding a KSE to another processor's queue we
use kse_notify() so that we don't touch their queue. Also in sched_add(),
if KEF_ASSIGNED is already set return immediately. This can happen if
a thread is removed and readded so that the priority is recorded properly.
- In sched_rem() return immediately if KEF_ASSIGNED is set. All callers
immediately readd simply to adjust priorites etc.
- In sched_choose(), if we're running an IDLE task or the per cpu idle thread
set our cpumask bit in 'kseq_idle' so that other processors may know that
we are idle. Before this, make a single pass through the run queues of
other processors so that we may find work more immediately if it is
available.
- In sched_runnable(), don't scan each processor's run queue, they will IPI
us if they have work for us to do.
- In sched_add(), if we're adding a thread that can be migrated and we have
plenty of work to do, try to migrate the thread to an idle kseq.
- Simplify the logic in sched_prio() and take the KEF_ASSIGNED flag into
consideration.
- No longer use kseq_choose() to steal threads, it can lose it's last
argument.
- Create a new function runq_steal() which operates like runq_choose() but
skips threads based on some criteria. Currently it will not steal
PRI_ITHD threads. In the future this will be used for CPU binding.
- Create a kseq_steal() that checks each run queue with runq_steal(), use
kseq_steal() in the places where we used kseq_choose() to steal with
before.
2003-10-31 11:16:04 +00:00
|
|
|
{
|
2008-03-02 08:20:59 +00:00
|
|
|
struct cpu_group *cg;
|
2006-12-06 06:34:57 +00:00
|
|
|
struct tdq *steal;
|
2008-03-02 08:20:59 +00:00
|
|
|
cpumask_t mask;
|
|
|
|
int thresh;
|
2007-07-17 22:53:23 +00:00
|
|
|
int cpu;
|
2003-12-11 03:57:10 +00:00
|
|
|
|
2007-10-08 23:50:39 +00:00
|
|
|
if (smp_started == 0 || steal_idle == 0)
|
|
|
|
return (1);
|
2008-03-02 08:20:59 +00:00
|
|
|
mask = -1;
|
|
|
|
mask &= ~PCPU_GET(cpumask);
|
|
|
|
/* We don't want to be preempted while we're iterating. */
|
2007-07-17 22:53:23 +00:00
|
|
|
spinlock_enter();
|
2008-03-02 08:20:59 +00:00
|
|
|
for (cg = tdq->tdq_cg; cg != NULL; ) {
|
|
|
|
if ((cg->cg_flags & (CG_FLAG_HTT | CG_FLAG_THREAD)) == 0)
|
|
|
|
thresh = steal_thresh;
|
|
|
|
else
|
|
|
|
thresh = 1;
|
|
|
|
cpu = sched_highest(cg, mask, thresh);
|
|
|
|
if (cpu == -1) {
|
|
|
|
cg = cg->cg_parent;
|
|
|
|
continue;
|
2003-12-11 03:57:10 +00:00
|
|
|
}
|
2008-03-02 08:20:59 +00:00
|
|
|
steal = TDQ_CPU(cpu);
|
|
|
|
mask &= ~(1 << cpu);
|
2007-10-02 00:36:06 +00:00
|
|
|
tdq_lock_pair(tdq, steal);
|
2008-03-02 08:20:59 +00:00
|
|
|
if (steal->tdq_load < thresh || steal->tdq_transferable == 0) {
|
|
|
|
tdq_unlock_pair(tdq, steal);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* If a thread was added while interrupts were disabled don't
|
|
|
|
* steal one here. If we fail to acquire one due to affinity
|
|
|
|
* restrictions loop again with this cpu removed from the
|
|
|
|
* set.
|
|
|
|
*/
|
|
|
|
if (tdq->tdq_load == 0 && tdq_move(steal, tdq) == 0) {
|
|
|
|
tdq_unlock_pair(tdq, steal);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
spinlock_exit();
|
|
|
|
TDQ_UNLOCK(steal);
|
2008-04-17 04:20:10 +00:00
|
|
|
mi_switch(SW_VOL | SWT_IDLE, NULL);
|
2008-03-02 08:20:59 +00:00
|
|
|
thread_unlock(curthread);
|
|
|
|
|
|
|
|
return (0);
|
2003-12-11 03:57:10 +00:00
|
|
|
}
|
2007-07-17 22:53:23 +00:00
|
|
|
spinlock_exit();
|
2003-12-11 03:57:10 +00:00
|
|
|
return (1);
|
- Add static to local functions and data where it was missing.
- Add an IPI based mechanism for migrating kses. This mechanism is
broken down into several components. This is intended to reduce cache
thrashing by eliminating most cases where one cpu touches another's
run queues.
- kseq_notify() appends a kse to a lockless singly linked list and
conditionally sends an IPI to the target processor. Right now this is
protected by sched_lock but at some point I'd like to get rid of the
global lock. This is why I used something more complicated than a
standard queue.
- kseq_assign() processes our list of kses that have been assigned to us
by other processors. This simply calls sched_add() for each item on the
list after clearing the new KEF_ASSIGNED flag. This flag is used to
indicate that we have been appeneded to the assigned queue but not
added to the run queue yet.
- In sched_add(), instead of adding a KSE to another processor's queue we
use kse_notify() so that we don't touch their queue. Also in sched_add(),
if KEF_ASSIGNED is already set return immediately. This can happen if
a thread is removed and readded so that the priority is recorded properly.
- In sched_rem() return immediately if KEF_ASSIGNED is set. All callers
immediately readd simply to adjust priorites etc.
- In sched_choose(), if we're running an IDLE task or the per cpu idle thread
set our cpumask bit in 'kseq_idle' so that other processors may know that
we are idle. Before this, make a single pass through the run queues of
other processors so that we may find work more immediately if it is
available.
- In sched_runnable(), don't scan each processor's run queue, they will IPI
us if they have work for us to do.
- In sched_add(), if we're adding a thread that can be migrated and we have
plenty of work to do, try to migrate the thread to an idle kseq.
- Simplify the logic in sched_prio() and take the KEF_ASSIGNED flag into
consideration.
- No longer use kseq_choose() to steal threads, it can lose it's last
argument.
- Create a new function runq_steal() which operates like runq_choose() but
skips threads based on some criteria. Currently it will not steal
PRI_ITHD threads. In the future this will be used for CPU binding.
- Create a kseq_steal() that checks each run queue with runq_steal(), use
kseq_steal() in the places where we used kseq_choose() to steal with
before.
2003-10-31 11:16:04 +00:00
|
|
|
}
|
|
|
|
|
2007-07-17 22:53:23 +00:00
|
|
|
/*
|
|
|
|
* Notify a remote cpu of new work. Sends an IPI if criteria are met.
|
|
|
|
*/
|
- Add static to local functions and data where it was missing.
- Add an IPI based mechanism for migrating kses. This mechanism is
broken down into several components. This is intended to reduce cache
thrashing by eliminating most cases where one cpu touches another's
run queues.
- kseq_notify() appends a kse to a lockless singly linked list and
conditionally sends an IPI to the target processor. Right now this is
protected by sched_lock but at some point I'd like to get rid of the
global lock. This is why I used something more complicated than a
standard queue.
- kseq_assign() processes our list of kses that have been assigned to us
by other processors. This simply calls sched_add() for each item on the
list after clearing the new KEF_ASSIGNED flag. This flag is used to
indicate that we have been appeneded to the assigned queue but not
added to the run queue yet.
- In sched_add(), instead of adding a KSE to another processor's queue we
use kse_notify() so that we don't touch their queue. Also in sched_add(),
if KEF_ASSIGNED is already set return immediately. This can happen if
a thread is removed and readded so that the priority is recorded properly.
- In sched_rem() return immediately if KEF_ASSIGNED is set. All callers
immediately readd simply to adjust priorites etc.
- In sched_choose(), if we're running an IDLE task or the per cpu idle thread
set our cpumask bit in 'kseq_idle' so that other processors may know that
we are idle. Before this, make a single pass through the run queues of
other processors so that we may find work more immediately if it is
available.
- In sched_runnable(), don't scan each processor's run queue, they will IPI
us if they have work for us to do.
- In sched_add(), if we're adding a thread that can be migrated and we have
plenty of work to do, try to migrate the thread to an idle kseq.
- Simplify the logic in sched_prio() and take the KEF_ASSIGNED flag into
consideration.
- No longer use kseq_choose() to steal threads, it can lose it's last
argument.
- Create a new function runq_steal() which operates like runq_choose() but
skips threads based on some criteria. Currently it will not steal
PRI_ITHD threads. In the future this will be used for CPU binding.
- Create a kseq_steal() that checks each run queue with runq_steal(), use
kseq_steal() in the places where we used kseq_choose() to steal with
before.
2003-10-31 11:16:04 +00:00
|
|
|
static void
|
2008-03-20 05:51:16 +00:00
|
|
|
tdq_notify(struct tdq *tdq, struct thread *td)
|
- Add static to local functions and data where it was missing.
- Add an IPI based mechanism for migrating kses. This mechanism is
broken down into several components. This is intended to reduce cache
thrashing by eliminating most cases where one cpu touches another's
run queues.
- kseq_notify() appends a kse to a lockless singly linked list and
conditionally sends an IPI to the target processor. Right now this is
protected by sched_lock but at some point I'd like to get rid of the
global lock. This is why I used something more complicated than a
standard queue.
- kseq_assign() processes our list of kses that have been assigned to us
by other processors. This simply calls sched_add() for each item on the
list after clearing the new KEF_ASSIGNED flag. This flag is used to
indicate that we have been appeneded to the assigned queue but not
added to the run queue yet.
- In sched_add(), instead of adding a KSE to another processor's queue we
use kse_notify() so that we don't touch their queue. Also in sched_add(),
if KEF_ASSIGNED is already set return immediately. This can happen if
a thread is removed and readded so that the priority is recorded properly.
- In sched_rem() return immediately if KEF_ASSIGNED is set. All callers
immediately readd simply to adjust priorites etc.
- In sched_choose(), if we're running an IDLE task or the per cpu idle thread
set our cpumask bit in 'kseq_idle' so that other processors may know that
we are idle. Before this, make a single pass through the run queues of
other processors so that we may find work more immediately if it is
available.
- In sched_runnable(), don't scan each processor's run queue, they will IPI
us if they have work for us to do.
- In sched_add(), if we're adding a thread that can be migrated and we have
plenty of work to do, try to migrate the thread to an idle kseq.
- Simplify the logic in sched_prio() and take the KEF_ASSIGNED flag into
consideration.
- No longer use kseq_choose() to steal threads, it can lose it's last
argument.
- Create a new function runq_steal() which operates like runq_choose() but
skips threads based on some criteria. Currently it will not steal
PRI_ITHD threads. In the future this will be used for CPU binding.
- Create a kseq_steal() that checks each run queue with runq_steal(), use
kseq_steal() in the places where we used kseq_choose() to steal with
before.
2003-10-31 11:16:04 +00:00
|
|
|
{
|
2007-01-25 23:51:59 +00:00
|
|
|
int cpri;
|
|
|
|
int pri;
|
2007-01-19 21:56:08 +00:00
|
|
|
int cpu;
|
- Add static to local functions and data where it was missing.
- Add an IPI based mechanism for migrating kses. This mechanism is
broken down into several components. This is intended to reduce cache
thrashing by eliminating most cases where one cpu touches another's
run queues.
- kseq_notify() appends a kse to a lockless singly linked list and
conditionally sends an IPI to the target processor. Right now this is
protected by sched_lock but at some point I'd like to get rid of the
global lock. This is why I used something more complicated than a
standard queue.
- kseq_assign() processes our list of kses that have been assigned to us
by other processors. This simply calls sched_add() for each item on the
list after clearing the new KEF_ASSIGNED flag. This flag is used to
indicate that we have been appeneded to the assigned queue but not
added to the run queue yet.
- In sched_add(), instead of adding a KSE to another processor's queue we
use kse_notify() so that we don't touch their queue. Also in sched_add(),
if KEF_ASSIGNED is already set return immediately. This can happen if
a thread is removed and readded so that the priority is recorded properly.
- In sched_rem() return immediately if KEF_ASSIGNED is set. All callers
immediately readd simply to adjust priorites etc.
- In sched_choose(), if we're running an IDLE task or the per cpu idle thread
set our cpumask bit in 'kseq_idle' so that other processors may know that
we are idle. Before this, make a single pass through the run queues of
other processors so that we may find work more immediately if it is
available.
- In sched_runnable(), don't scan each processor's run queue, they will IPI
us if they have work for us to do.
- In sched_add(), if we're adding a thread that can be migrated and we have
plenty of work to do, try to migrate the thread to an idle kseq.
- Simplify the logic in sched_prio() and take the KEF_ASSIGNED flag into
consideration.
- No longer use kseq_choose() to steal threads, it can lose it's last
argument.
- Create a new function runq_steal() which operates like runq_choose() but
skips threads based on some criteria. Currently it will not steal
PRI_ITHD threads. In the future this will be used for CPU binding.
- Create a kseq_steal() that checks each run queue with runq_steal(), use
kseq_steal() in the places where we used kseq_choose() to steal with
before.
2003-10-31 11:16:04 +00:00
|
|
|
|
2008-03-10 01:32:01 +00:00
|
|
|
if (tdq->tdq_ipipending)
|
|
|
|
return;
|
2008-03-20 05:51:16 +00:00
|
|
|
cpu = td->td_sched->ts_cpu;
|
|
|
|
pri = td->td_priority;
|
2008-03-10 01:32:01 +00:00
|
|
|
cpri = pcpu_find(cpu)->pc_curthread->td_priority;
|
|
|
|
if (!sched_shouldpreempt(pri, cpri, 1))
|
2007-01-05 23:45:38 +00:00
|
|
|
return;
|
2008-04-17 09:56:01 +00:00
|
|
|
if (TD_IS_IDLETHREAD(td)) {
|
|
|
|
/*
|
|
|
|
* If the idle thread is still 'running' it's probably
|
|
|
|
* waiting on us to release the tdq spinlock already. No
|
|
|
|
* need to ipi.
|
|
|
|
*/
|
|
|
|
if (tdq->tdq_idlestate == TDQ_RUNNING)
|
|
|
|
return;
|
2008-04-25 05:18:50 +00:00
|
|
|
/*
|
|
|
|
* If the MD code has an idle wakeup routine try that before
|
|
|
|
* falling back to IPI.
|
|
|
|
*/
|
|
|
|
if (cpu_idle_wakeup(cpu))
|
|
|
|
return;
|
2008-04-17 09:56:01 +00:00
|
|
|
}
|
2008-03-10 01:32:01 +00:00
|
|
|
tdq->tdq_ipipending = 1;
|
2007-07-17 22:53:23 +00:00
|
|
|
ipi_selected(1 << cpu, IPI_PREEMPT);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Steals load from a timeshare queue. Honors the rotating queue head
|
|
|
|
* index.
|
|
|
|
*/
|
2008-03-20 05:51:16 +00:00
|
|
|
static struct thread *
|
2008-03-02 08:20:59 +00:00
|
|
|
runq_steal_from(struct runq *rq, int cpu, u_char start)
|
2007-07-17 22:53:23 +00:00
|
|
|
{
|
|
|
|
struct rqbits *rqb;
|
|
|
|
struct rqhead *rqh;
|
2008-03-20 05:51:16 +00:00
|
|
|
struct thread *td;
|
2007-07-17 22:53:23 +00:00
|
|
|
int first;
|
|
|
|
int bit;
|
|
|
|
int pri;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
rqb = &rq->rq_status;
|
|
|
|
bit = start & (RQB_BPW -1);
|
|
|
|
pri = 0;
|
|
|
|
first = 0;
|
|
|
|
again:
|
|
|
|
for (i = RQB_WORD(start); i < RQB_LEN; bit = 0, i++) {
|
|
|
|
if (rqb->rqb_bits[i] == 0)
|
|
|
|
continue;
|
|
|
|
if (bit != 0) {
|
|
|
|
for (pri = bit; pri < RQB_BPW; pri++)
|
|
|
|
if (rqb->rqb_bits[i] & (1ul << pri))
|
|
|
|
break;
|
|
|
|
if (pri >= RQB_BPW)
|
|
|
|
continue;
|
|
|
|
} else
|
|
|
|
pri = RQB_FFS(rqb->rqb_bits[i]);
|
|
|
|
pri += (i << RQB_L2BPW);
|
|
|
|
rqh = &rq->rq_queues[pri];
|
2008-03-20 05:51:16 +00:00
|
|
|
TAILQ_FOREACH(td, rqh, td_runq) {
|
|
|
|
if (first && THREAD_CAN_MIGRATE(td) &&
|
|
|
|
THREAD_CAN_SCHED(td, cpu))
|
|
|
|
return (td);
|
2007-07-17 22:53:23 +00:00
|
|
|
first = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (start != 0) {
|
|
|
|
start = 0;
|
|
|
|
goto again;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (NULL);
|
- Add static to local functions and data where it was missing.
- Add an IPI based mechanism for migrating kses. This mechanism is
broken down into several components. This is intended to reduce cache
thrashing by eliminating most cases where one cpu touches another's
run queues.
- kseq_notify() appends a kse to a lockless singly linked list and
conditionally sends an IPI to the target processor. Right now this is
protected by sched_lock but at some point I'd like to get rid of the
global lock. This is why I used something more complicated than a
standard queue.
- kseq_assign() processes our list of kses that have been assigned to us
by other processors. This simply calls sched_add() for each item on the
list after clearing the new KEF_ASSIGNED flag. This flag is used to
indicate that we have been appeneded to the assigned queue but not
added to the run queue yet.
- In sched_add(), instead of adding a KSE to another processor's queue we
use kse_notify() so that we don't touch their queue. Also in sched_add(),
if KEF_ASSIGNED is already set return immediately. This can happen if
a thread is removed and readded so that the priority is recorded properly.
- In sched_rem() return immediately if KEF_ASSIGNED is set. All callers
immediately readd simply to adjust priorites etc.
- In sched_choose(), if we're running an IDLE task or the per cpu idle thread
set our cpumask bit in 'kseq_idle' so that other processors may know that
we are idle. Before this, make a single pass through the run queues of
other processors so that we may find work more immediately if it is
available.
- In sched_runnable(), don't scan each processor's run queue, they will IPI
us if they have work for us to do.
- In sched_add(), if we're adding a thread that can be migrated and we have
plenty of work to do, try to migrate the thread to an idle kseq.
- Simplify the logic in sched_prio() and take the KEF_ASSIGNED flag into
consideration.
- No longer use kseq_choose() to steal threads, it can lose it's last
argument.
- Create a new function runq_steal() which operates like runq_choose() but
skips threads based on some criteria. Currently it will not steal
PRI_ITHD threads. In the future this will be used for CPU binding.
- Create a kseq_steal() that checks each run queue with runq_steal(), use
kseq_steal() in the places where we used kseq_choose() to steal with
before.
2003-10-31 11:16:04 +00:00
|
|
|
}
|
|
|
|
|
2007-07-17 22:53:23 +00:00
|
|
|
/*
|
|
|
|
* Steals load from a standard linear queue.
|
|
|
|
*/
|
2008-03-20 05:51:16 +00:00
|
|
|
static struct thread *
|
2008-03-02 08:20:59 +00:00
|
|
|
runq_steal(struct runq *rq, int cpu)
|
- Add static to local functions and data where it was missing.
- Add an IPI based mechanism for migrating kses. This mechanism is
broken down into several components. This is intended to reduce cache
thrashing by eliminating most cases where one cpu touches another's
run queues.
- kseq_notify() appends a kse to a lockless singly linked list and
conditionally sends an IPI to the target processor. Right now this is
protected by sched_lock but at some point I'd like to get rid of the
global lock. This is why I used something more complicated than a
standard queue.
- kseq_assign() processes our list of kses that have been assigned to us
by other processors. This simply calls sched_add() for each item on the
list after clearing the new KEF_ASSIGNED flag. This flag is used to
indicate that we have been appeneded to the assigned queue but not
added to the run queue yet.
- In sched_add(), instead of adding a KSE to another processor's queue we
use kse_notify() so that we don't touch their queue. Also in sched_add(),
if KEF_ASSIGNED is already set return immediately. This can happen if
a thread is removed and readded so that the priority is recorded properly.
- In sched_rem() return immediately if KEF_ASSIGNED is set. All callers
immediately readd simply to adjust priorites etc.
- In sched_choose(), if we're running an IDLE task or the per cpu idle thread
set our cpumask bit in 'kseq_idle' so that other processors may know that
we are idle. Before this, make a single pass through the run queues of
other processors so that we may find work more immediately if it is
available.
- In sched_runnable(), don't scan each processor's run queue, they will IPI
us if they have work for us to do.
- In sched_add(), if we're adding a thread that can be migrated and we have
plenty of work to do, try to migrate the thread to an idle kseq.
- Simplify the logic in sched_prio() and take the KEF_ASSIGNED flag into
consideration.
- No longer use kseq_choose() to steal threads, it can lose it's last
argument.
- Create a new function runq_steal() which operates like runq_choose() but
skips threads based on some criteria. Currently it will not steal
PRI_ITHD threads. In the future this will be used for CPU binding.
- Create a kseq_steal() that checks each run queue with runq_steal(), use
kseq_steal() in the places where we used kseq_choose() to steal with
before.
2003-10-31 11:16:04 +00:00
|
|
|
{
|
|
|
|
struct rqhead *rqh;
|
|
|
|
struct rqbits *rqb;
|
2008-03-20 05:51:16 +00:00
|
|
|
struct thread *td;
|
- Add static to local functions and data where it was missing.
- Add an IPI based mechanism for migrating kses. This mechanism is
broken down into several components. This is intended to reduce cache
thrashing by eliminating most cases where one cpu touches another's
run queues.
- kseq_notify() appends a kse to a lockless singly linked list and
conditionally sends an IPI to the target processor. Right now this is
protected by sched_lock but at some point I'd like to get rid of the
global lock. This is why I used something more complicated than a
standard queue.
- kseq_assign() processes our list of kses that have been assigned to us
by other processors. This simply calls sched_add() for each item on the
list after clearing the new KEF_ASSIGNED flag. This flag is used to
indicate that we have been appeneded to the assigned queue but not
added to the run queue yet.
- In sched_add(), instead of adding a KSE to another processor's queue we
use kse_notify() so that we don't touch their queue. Also in sched_add(),
if KEF_ASSIGNED is already set return immediately. This can happen if
a thread is removed and readded so that the priority is recorded properly.
- In sched_rem() return immediately if KEF_ASSIGNED is set. All callers
immediately readd simply to adjust priorites etc.
- In sched_choose(), if we're running an IDLE task or the per cpu idle thread
set our cpumask bit in 'kseq_idle' so that other processors may know that
we are idle. Before this, make a single pass through the run queues of
other processors so that we may find work more immediately if it is
available.
- In sched_runnable(), don't scan each processor's run queue, they will IPI
us if they have work for us to do.
- In sched_add(), if we're adding a thread that can be migrated and we have
plenty of work to do, try to migrate the thread to an idle kseq.
- Simplify the logic in sched_prio() and take the KEF_ASSIGNED flag into
consideration.
- No longer use kseq_choose() to steal threads, it can lose it's last
argument.
- Create a new function runq_steal() which operates like runq_choose() but
skips threads based on some criteria. Currently it will not steal
PRI_ITHD threads. In the future this will be used for CPU binding.
- Create a kseq_steal() that checks each run queue with runq_steal(), use
kseq_steal() in the places where we used kseq_choose() to steal with
before.
2003-10-31 11:16:04 +00:00
|
|
|
int word;
|
|
|
|
int bit;
|
|
|
|
|
|
|
|
rqb = &rq->rq_status;
|
|
|
|
for (word = 0; word < RQB_LEN; word++) {
|
|
|
|
if (rqb->rqb_bits[word] == 0)
|
|
|
|
continue;
|
|
|
|
for (bit = 0; bit < RQB_BPW; bit++) {
|
2003-12-07 09:57:51 +00:00
|
|
|
if ((rqb->rqb_bits[word] & (1ul << bit)) == 0)
|
- Add static to local functions and data where it was missing.
- Add an IPI based mechanism for migrating kses. This mechanism is
broken down into several components. This is intended to reduce cache
thrashing by eliminating most cases where one cpu touches another's
run queues.
- kseq_notify() appends a kse to a lockless singly linked list and
conditionally sends an IPI to the target processor. Right now this is
protected by sched_lock but at some point I'd like to get rid of the
global lock. This is why I used something more complicated than a
standard queue.
- kseq_assign() processes our list of kses that have been assigned to us
by other processors. This simply calls sched_add() for each item on the
list after clearing the new KEF_ASSIGNED flag. This flag is used to
indicate that we have been appeneded to the assigned queue but not
added to the run queue yet.
- In sched_add(), instead of adding a KSE to another processor's queue we
use kse_notify() so that we don't touch their queue. Also in sched_add(),
if KEF_ASSIGNED is already set return immediately. This can happen if
a thread is removed and readded so that the priority is recorded properly.
- In sched_rem() return immediately if KEF_ASSIGNED is set. All callers
immediately readd simply to adjust priorites etc.
- In sched_choose(), if we're running an IDLE task or the per cpu idle thread
set our cpumask bit in 'kseq_idle' so that other processors may know that
we are idle. Before this, make a single pass through the run queues of
other processors so that we may find work more immediately if it is
available.
- In sched_runnable(), don't scan each processor's run queue, they will IPI
us if they have work for us to do.
- In sched_add(), if we're adding a thread that can be migrated and we have
plenty of work to do, try to migrate the thread to an idle kseq.
- Simplify the logic in sched_prio() and take the KEF_ASSIGNED flag into
consideration.
- No longer use kseq_choose() to steal threads, it can lose it's last
argument.
- Create a new function runq_steal() which operates like runq_choose() but
skips threads based on some criteria. Currently it will not steal
PRI_ITHD threads. In the future this will be used for CPU binding.
- Create a kseq_steal() that checks each run queue with runq_steal(), use
kseq_steal() in the places where we used kseq_choose() to steal with
before.
2003-10-31 11:16:04 +00:00
|
|
|
continue;
|
|
|
|
rqh = &rq->rq_queues[bit + (word << RQB_L2BPW)];
|
2008-03-20 05:51:16 +00:00
|
|
|
TAILQ_FOREACH(td, rqh, td_runq)
|
|
|
|
if (THREAD_CAN_MIGRATE(td) &&
|
|
|
|
THREAD_CAN_SCHED(td, cpu))
|
|
|
|
return (td);
|
- Add static to local functions and data where it was missing.
- Add an IPI based mechanism for migrating kses. This mechanism is
broken down into several components. This is intended to reduce cache
thrashing by eliminating most cases where one cpu touches another's
run queues.
- kseq_notify() appends a kse to a lockless singly linked list and
conditionally sends an IPI to the target processor. Right now this is
protected by sched_lock but at some point I'd like to get rid of the
global lock. This is why I used something more complicated than a
standard queue.
- kseq_assign() processes our list of kses that have been assigned to us
by other processors. This simply calls sched_add() for each item on the
list after clearing the new KEF_ASSIGNED flag. This flag is used to
indicate that we have been appeneded to the assigned queue but not
added to the run queue yet.
- In sched_add(), instead of adding a KSE to another processor's queue we
use kse_notify() so that we don't touch their queue. Also in sched_add(),
if KEF_ASSIGNED is already set return immediately. This can happen if
a thread is removed and readded so that the priority is recorded properly.
- In sched_rem() return immediately if KEF_ASSIGNED is set. All callers
immediately readd simply to adjust priorites etc.
- In sched_choose(), if we're running an IDLE task or the per cpu idle thread
set our cpumask bit in 'kseq_idle' so that other processors may know that
we are idle. Before this, make a single pass through the run queues of
other processors so that we may find work more immediately if it is
available.
- In sched_runnable(), don't scan each processor's run queue, they will IPI
us if they have work for us to do.
- In sched_add(), if we're adding a thread that can be migrated and we have
plenty of work to do, try to migrate the thread to an idle kseq.
- Simplify the logic in sched_prio() and take the KEF_ASSIGNED flag into
consideration.
- No longer use kseq_choose() to steal threads, it can lose it's last
argument.
- Create a new function runq_steal() which operates like runq_choose() but
skips threads based on some criteria. Currently it will not steal
PRI_ITHD threads. In the future this will be used for CPU binding.
- Create a kseq_steal() that checks each run queue with runq_steal(), use
kseq_steal() in the places where we used kseq_choose() to steal with
before.
2003-10-31 11:16:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
|
2007-07-17 22:53:23 +00:00
|
|
|
/*
|
|
|
|
* Attempt to steal a thread in priority order from a thread queue.
|
|
|
|
*/
|
2008-03-20 05:51:16 +00:00
|
|
|
static struct thread *
|
2008-03-02 08:20:59 +00:00
|
|
|
tdq_steal(struct tdq *tdq, int cpu)
|
- Add static to local functions and data where it was missing.
- Add an IPI based mechanism for migrating kses. This mechanism is
broken down into several components. This is intended to reduce cache
thrashing by eliminating most cases where one cpu touches another's
run queues.
- kseq_notify() appends a kse to a lockless singly linked list and
conditionally sends an IPI to the target processor. Right now this is
protected by sched_lock but at some point I'd like to get rid of the
global lock. This is why I used something more complicated than a
standard queue.
- kseq_assign() processes our list of kses that have been assigned to us
by other processors. This simply calls sched_add() for each item on the
list after clearing the new KEF_ASSIGNED flag. This flag is used to
indicate that we have been appeneded to the assigned queue but not
added to the run queue yet.
- In sched_add(), instead of adding a KSE to another processor's queue we
use kse_notify() so that we don't touch their queue. Also in sched_add(),
if KEF_ASSIGNED is already set return immediately. This can happen if
a thread is removed and readded so that the priority is recorded properly.
- In sched_rem() return immediately if KEF_ASSIGNED is set. All callers
immediately readd simply to adjust priorites etc.
- In sched_choose(), if we're running an IDLE task or the per cpu idle thread
set our cpumask bit in 'kseq_idle' so that other processors may know that
we are idle. Before this, make a single pass through the run queues of
other processors so that we may find work more immediately if it is
available.
- In sched_runnable(), don't scan each processor's run queue, they will IPI
us if they have work for us to do.
- In sched_add(), if we're adding a thread that can be migrated and we have
plenty of work to do, try to migrate the thread to an idle kseq.
- Simplify the logic in sched_prio() and take the KEF_ASSIGNED flag into
consideration.
- No longer use kseq_choose() to steal threads, it can lose it's last
argument.
- Create a new function runq_steal() which operates like runq_choose() but
skips threads based on some criteria. Currently it will not steal
PRI_ITHD threads. In the future this will be used for CPU binding.
- Create a kseq_steal() that checks each run queue with runq_steal(), use
kseq_steal() in the places where we used kseq_choose() to steal with
before.
2003-10-31 11:16:04 +00:00
|
|
|
{
|
2008-03-20 05:51:16 +00:00
|
|
|
struct thread *td;
|
- Add static to local functions and data where it was missing.
- Add an IPI based mechanism for migrating kses. This mechanism is
broken down into several components. This is intended to reduce cache
thrashing by eliminating most cases where one cpu touches another's
run queues.
- kseq_notify() appends a kse to a lockless singly linked list and
conditionally sends an IPI to the target processor. Right now this is
protected by sched_lock but at some point I'd like to get rid of the
global lock. This is why I used something more complicated than a
standard queue.
- kseq_assign() processes our list of kses that have been assigned to us
by other processors. This simply calls sched_add() for each item on the
list after clearing the new KEF_ASSIGNED flag. This flag is used to
indicate that we have been appeneded to the assigned queue but not
added to the run queue yet.
- In sched_add(), instead of adding a KSE to another processor's queue we
use kse_notify() so that we don't touch their queue. Also in sched_add(),
if KEF_ASSIGNED is already set return immediately. This can happen if
a thread is removed and readded so that the priority is recorded properly.
- In sched_rem() return immediately if KEF_ASSIGNED is set. All callers
immediately readd simply to adjust priorites etc.
- In sched_choose(), if we're running an IDLE task or the per cpu idle thread
set our cpumask bit in 'kseq_idle' so that other processors may know that
we are idle. Before this, make a single pass through the run queues of
other processors so that we may find work more immediately if it is
available.
- In sched_runnable(), don't scan each processor's run queue, they will IPI
us if they have work for us to do.
- In sched_add(), if we're adding a thread that can be migrated and we have
plenty of work to do, try to migrate the thread to an idle kseq.
- Simplify the logic in sched_prio() and take the KEF_ASSIGNED flag into
consideration.
- No longer use kseq_choose() to steal threads, it can lose it's last
argument.
- Create a new function runq_steal() which operates like runq_choose() but
skips threads based on some criteria. Currently it will not steal
PRI_ITHD threads. In the future this will be used for CPU binding.
- Create a kseq_steal() that checks each run queue with runq_steal(), use
kseq_steal() in the places where we used kseq_choose() to steal with
before.
2003-10-31 11:16:04 +00:00
|
|
|
|
2007-07-17 22:53:23 +00:00
|
|
|
TDQ_LOCK_ASSERT(tdq, MA_OWNED);
|
2008-03-20 05:51:16 +00:00
|
|
|
if ((td = runq_steal(&tdq->tdq_realtime, cpu)) != NULL)
|
|
|
|
return (td);
|
|
|
|
if ((td = runq_steal_from(&tdq->tdq_timeshare,
|
|
|
|
cpu, tdq->tdq_ridx)) != NULL)
|
|
|
|
return (td);
|
2008-03-02 08:20:59 +00:00
|
|
|
return (runq_steal(&tdq->tdq_idle, cpu));
|
2003-12-11 03:57:10 +00:00
|
|
|
}
|
|
|
|
|
2007-07-17 22:53:23 +00:00
|
|
|
/*
|
|
|
|
* Sets the thread lock and ts_cpu to match the requested cpu. Unlocks the
|
2007-10-02 00:36:06 +00:00
|
|
|
* current lock and returns with the assigned queue locked.
|
2007-07-17 22:53:23 +00:00
|
|
|
*/
|
|
|
|
static inline struct tdq *
|
2008-03-20 05:51:16 +00:00
|
|
|
sched_setcpu(struct thread *td, int cpu, int flags)
|
2003-12-11 03:57:10 +00:00
|
|
|
{
|
|
|
|
|
2008-03-20 05:51:16 +00:00
|
|
|
struct tdq *tdq;
|
2007-07-17 22:53:23 +00:00
|
|
|
|
2008-03-20 05:51:16 +00:00
|
|
|
THREAD_LOCK_ASSERT(td, MA_OWNED);
|
2007-07-17 22:53:23 +00:00
|
|
|
tdq = TDQ_CPU(cpu);
|
2008-03-20 05:51:16 +00:00
|
|
|
td->td_sched->ts_cpu = cpu;
|
|
|
|
/*
|
|
|
|
* If the lock matches just return the queue.
|
|
|
|
*/
|
2007-07-17 22:53:23 +00:00
|
|
|
if (td->td_lock == TDQ_LOCKPTR(tdq))
|
|
|
|
return (tdq);
|
|
|
|
#ifdef notyet
|
2007-01-19 21:56:08 +00:00
|
|
|
/*
|
2007-09-22 02:20:14 +00:00
|
|
|
* If the thread isn't running its lockptr is a
|
2007-07-17 22:53:23 +00:00
|
|
|
* turnstile or a sleepqueue. We can just lock_set without
|
|
|
|
* blocking.
|
2007-01-19 21:56:08 +00:00
|
|
|
*/
|
2007-07-17 22:53:23 +00:00
|
|
|
if (TD_CAN_RUN(td)) {
|
|
|
|
TDQ_LOCK(tdq);
|
|
|
|
thread_lock_set(td, TDQ_LOCKPTR(tdq));
|
|
|
|
return (tdq);
|
|
|
|
}
|
|
|
|
#endif
|
2007-01-19 21:56:08 +00:00
|
|
|
/*
|
2007-07-17 22:53:23 +00:00
|
|
|
* The hard case, migration, we need to block the thread first to
|
|
|
|
* prevent order reversals with other cpus locks.
|
2007-01-19 21:56:08 +00:00
|
|
|
*/
|
2007-07-17 22:53:23 +00:00
|
|
|
thread_lock_block(td);
|
|
|
|
TDQ_LOCK(tdq);
|
2007-08-03 23:38:46 +00:00
|
|
|
thread_lock_unblock(td, TDQ_LOCKPTR(tdq));
|
2007-07-17 22:53:23 +00:00
|
|
|
return (tdq);
|
2007-01-19 21:56:08 +00:00
|
|
|
}
|
|
|
|
|
2008-04-17 04:20:10 +00:00
|
|
|
SCHED_STAT_DEFINE(pickcpu_intrbind, "Soft interrupt binding");
|
|
|
|
SCHED_STAT_DEFINE(pickcpu_idle_affinity, "Picked idle cpu based on affinity");
|
|
|
|
SCHED_STAT_DEFINE(pickcpu_affinity, "Picked cpu based on affinity");
|
|
|
|
SCHED_STAT_DEFINE(pickcpu_lowest, "Selected lowest load");
|
|
|
|
SCHED_STAT_DEFINE(pickcpu_local, "Migrated to current cpu");
|
|
|
|
SCHED_STAT_DEFINE(pickcpu_migration, "Selection may have caused migration");
|
|
|
|
|
2007-07-17 22:53:23 +00:00
|
|
|
static int
|
2008-03-20 05:51:16 +00:00
|
|
|
sched_pickcpu(struct thread *td, int flags)
|
2007-07-17 22:53:23 +00:00
|
|
|
{
|
2008-03-02 08:20:59 +00:00
|
|
|
struct cpu_group *cg;
|
2008-03-20 05:51:16 +00:00
|
|
|
struct td_sched *ts;
|
2007-07-17 22:53:23 +00:00
|
|
|
struct tdq *tdq;
|
2008-03-02 08:20:59 +00:00
|
|
|
cpumask_t mask;
|
2007-01-19 21:56:08 +00:00
|
|
|
int self;
|
|
|
|
int pri;
|
|
|
|
int cpu;
|
|
|
|
|
2008-03-02 08:20:59 +00:00
|
|
|
self = PCPU_GET(cpuid);
|
2008-03-20 05:51:16 +00:00
|
|
|
ts = td->td_sched;
|
2007-01-19 21:56:08 +00:00
|
|
|
if (smp_started == 0)
|
|
|
|
return (self);
|
2007-07-19 20:03:15 +00:00
|
|
|
/*
|
|
|
|
* Don't migrate a running thread from sched_switch().
|
|
|
|
*/
|
2008-03-02 08:20:59 +00:00
|
|
|
if ((flags & SRQ_OURSELF) || !THREAD_CAN_MIGRATE(td))
|
|
|
|
return (ts->ts_cpu);
|
2007-01-19 21:56:08 +00:00
|
|
|
/*
|
2008-03-02 08:20:59 +00:00
|
|
|
* Prefer to run interrupt threads on the processors that generate
|
|
|
|
* the interrupt.
|
2007-01-19 21:56:08 +00:00
|
|
|
*/
|
2008-03-02 08:20:59 +00:00
|
|
|
if (td->td_priority <= PRI_MAX_ITHD && THREAD_CAN_SCHED(td, self) &&
|
2008-04-17 04:20:10 +00:00
|
|
|
curthread->td_intr_nesting_level && ts->ts_cpu != self) {
|
|
|
|
SCHED_STAT_INC(pickcpu_intrbind);
|
2008-03-02 08:20:59 +00:00
|
|
|
ts->ts_cpu = self;
|
2008-04-17 04:20:10 +00:00
|
|
|
}
|
2007-01-19 21:56:08 +00:00
|
|
|
/*
|
2008-03-02 08:20:59 +00:00
|
|
|
* If the thread can run on the last cpu and the affinity has not
|
|
|
|
* expired or it is idle run it there.
|
2007-01-19 21:56:08 +00:00
|
|
|
*/
|
2008-03-02 08:20:59 +00:00
|
|
|
pri = td->td_priority;
|
|
|
|
tdq = TDQ_CPU(ts->ts_cpu);
|
|
|
|
if (THREAD_CAN_SCHED(td, ts->ts_cpu)) {
|
2008-04-17 04:20:10 +00:00
|
|
|
if (tdq->tdq_lowpri > PRI_MIN_IDLE) {
|
|
|
|
SCHED_STAT_INC(pickcpu_idle_affinity);
|
2008-03-02 08:20:59 +00:00
|
|
|
return (ts->ts_cpu);
|
2008-04-17 04:20:10 +00:00
|
|
|
}
|
|
|
|
if (SCHED_AFFINITY(ts, CG_SHARE_L2) && tdq->tdq_lowpri > pri) {
|
|
|
|
SCHED_STAT_INC(pickcpu_affinity);
|
2008-03-02 08:20:59 +00:00
|
|
|
return (ts->ts_cpu);
|
2008-04-17 04:20:10 +00:00
|
|
|
}
|
2004-12-26 22:56:08 +00:00
|
|
|
}
|
2003-12-11 03:57:10 +00:00
|
|
|
/*
|
2008-03-02 08:20:59 +00:00
|
|
|
* Search for the highest level in the tree that still has affinity.
|
2003-12-11 03:57:10 +00:00
|
|
|
*/
|
2008-03-02 08:20:59 +00:00
|
|
|
cg = NULL;
|
|
|
|
for (cg = tdq->tdq_cg; cg != NULL; cg = cg->cg_parent)
|
|
|
|
if (SCHED_AFFINITY(ts, cg->cg_level))
|
|
|
|
break;
|
|
|
|
cpu = -1;
|
|
|
|
mask = td->td_cpuset->cs_mask.__bits[0];
|
|
|
|
if (cg)
|
|
|
|
cpu = sched_lowest(cg, mask, pri);
|
|
|
|
if (cpu == -1)
|
|
|
|
cpu = sched_lowest(cpu_top, mask, -1);
|
2007-07-19 20:03:15 +00:00
|
|
|
/*
|
2008-03-02 08:20:59 +00:00
|
|
|
* Compare the lowest loaded cpu to current cpu.
|
2007-07-19 20:03:15 +00:00
|
|
|
*/
|
2008-03-10 01:32:01 +00:00
|
|
|
if (THREAD_CAN_SCHED(td, self) && TDQ_CPU(self)->tdq_lowpri > pri &&
|
2008-04-17 04:20:10 +00:00
|
|
|
TDQ_CPU(cpu)->tdq_lowpri < PRI_MIN_IDLE) {
|
|
|
|
SCHED_STAT_INC(pickcpu_local);
|
2008-03-10 01:32:01 +00:00
|
|
|
cpu = self;
|
2008-04-17 04:20:10 +00:00
|
|
|
} else
|
|
|
|
SCHED_STAT_INC(pickcpu_lowest);
|
|
|
|
if (cpu != ts->ts_cpu)
|
|
|
|
SCHED_STAT_INC(pickcpu_migration);
|
2008-03-10 01:32:01 +00:00
|
|
|
KASSERT(cpu != -1, ("sched_pickcpu: Failed to find a cpu."));
|
2007-07-17 22:53:23 +00:00
|
|
|
return (cpu);
|
- Add static to local functions and data where it was missing.
- Add an IPI based mechanism for migrating kses. This mechanism is
broken down into several components. This is intended to reduce cache
thrashing by eliminating most cases where one cpu touches another's
run queues.
- kseq_notify() appends a kse to a lockless singly linked list and
conditionally sends an IPI to the target processor. Right now this is
protected by sched_lock but at some point I'd like to get rid of the
global lock. This is why I used something more complicated than a
standard queue.
- kseq_assign() processes our list of kses that have been assigned to us
by other processors. This simply calls sched_add() for each item on the
list after clearing the new KEF_ASSIGNED flag. This flag is used to
indicate that we have been appeneded to the assigned queue but not
added to the run queue yet.
- In sched_add(), instead of adding a KSE to another processor's queue we
use kse_notify() so that we don't touch their queue. Also in sched_add(),
if KEF_ASSIGNED is already set return immediately. This can happen if
a thread is removed and readded so that the priority is recorded properly.
- In sched_rem() return immediately if KEF_ASSIGNED is set. All callers
immediately readd simply to adjust priorites etc.
- In sched_choose(), if we're running an IDLE task or the per cpu idle thread
set our cpumask bit in 'kseq_idle' so that other processors may know that
we are idle. Before this, make a single pass through the run queues of
other processors so that we may find work more immediately if it is
available.
- In sched_runnable(), don't scan each processor's run queue, they will IPI
us if they have work for us to do.
- In sched_add(), if we're adding a thread that can be migrated and we have
plenty of work to do, try to migrate the thread to an idle kseq.
- Simplify the logic in sched_prio() and take the KEF_ASSIGNED flag into
consideration.
- No longer use kseq_choose() to steal threads, it can lose it's last
argument.
- Create a new function runq_steal() which operates like runq_choose() but
skips threads based on some criteria. Currently it will not steal
PRI_ITHD threads. In the future this will be used for CPU binding.
- Create a kseq_steal() that checks each run queue with runq_steal(), use
kseq_steal() in the places where we used kseq_choose() to steal with
before.
2003-10-31 11:16:04 +00:00
|
|
|
}
|
2008-03-02 08:20:59 +00:00
|
|
|
#endif
|
2003-02-03 05:30:07 +00:00
|
|
|
|
2003-07-08 06:19:40 +00:00
|
|
|
/*
|
- Add static to local functions and data where it was missing.
- Add an IPI based mechanism for migrating kses. This mechanism is
broken down into several components. This is intended to reduce cache
thrashing by eliminating most cases where one cpu touches another's
run queues.
- kseq_notify() appends a kse to a lockless singly linked list and
conditionally sends an IPI to the target processor. Right now this is
protected by sched_lock but at some point I'd like to get rid of the
global lock. This is why I used something more complicated than a
standard queue.
- kseq_assign() processes our list of kses that have been assigned to us
by other processors. This simply calls sched_add() for each item on the
list after clearing the new KEF_ASSIGNED flag. This flag is used to
indicate that we have been appeneded to the assigned queue but not
added to the run queue yet.
- In sched_add(), instead of adding a KSE to another processor's queue we
use kse_notify() so that we don't touch their queue. Also in sched_add(),
if KEF_ASSIGNED is already set return immediately. This can happen if
a thread is removed and readded so that the priority is recorded properly.
- In sched_rem() return immediately if KEF_ASSIGNED is set. All callers
immediately readd simply to adjust priorites etc.
- In sched_choose(), if we're running an IDLE task or the per cpu idle thread
set our cpumask bit in 'kseq_idle' so that other processors may know that
we are idle. Before this, make a single pass through the run queues of
other processors so that we may find work more immediately if it is
available.
- In sched_runnable(), don't scan each processor's run queue, they will IPI
us if they have work for us to do.
- In sched_add(), if we're adding a thread that can be migrated and we have
plenty of work to do, try to migrate the thread to an idle kseq.
- Simplify the logic in sched_prio() and take the KEF_ASSIGNED flag into
consideration.
- No longer use kseq_choose() to steal threads, it can lose it's last
argument.
- Create a new function runq_steal() which operates like runq_choose() but
skips threads based on some criteria. Currently it will not steal
PRI_ITHD threads. In the future this will be used for CPU binding.
- Create a kseq_steal() that checks each run queue with runq_steal(), use
kseq_steal() in the places where we used kseq_choose() to steal with
before.
2003-10-31 11:16:04 +00:00
|
|
|
* Pick the highest priority task we have and return it.
|
2003-07-08 06:19:40 +00:00
|
|
|
*/
|
2008-03-20 05:51:16 +00:00
|
|
|
static struct thread *
|
2006-12-06 06:34:57 +00:00
|
|
|
tdq_choose(struct tdq *tdq)
|
2003-02-03 05:30:07 +00:00
|
|
|
{
|
2008-03-20 05:51:16 +00:00
|
|
|
struct thread *td;
|
2003-02-03 05:30:07 +00:00
|
|
|
|
2007-07-17 22:53:23 +00:00
|
|
|
TDQ_LOCK_ASSERT(tdq, MA_OWNED);
|
2008-03-20 05:51:16 +00:00
|
|
|
td = runq_choose(&tdq->tdq_realtime);
|
|
|
|
if (td != NULL)
|
|
|
|
return (td);
|
|
|
|
td = runq_choose_from(&tdq->tdq_timeshare, tdq->tdq_ridx);
|
|
|
|
if (td != NULL) {
|
|
|
|
KASSERT(td->td_priority >= PRI_MIN_TIMESHARE,
|
2007-01-04 08:56:25 +00:00
|
|
|
("tdq_choose: Invalid priority on timeshare queue %d",
|
2008-03-20 05:51:16 +00:00
|
|
|
td->td_priority));
|
|
|
|
return (td);
|
2007-01-04 08:56:25 +00:00
|
|
|
}
|
2008-03-20 05:51:16 +00:00
|
|
|
td = runq_choose(&tdq->tdq_idle);
|
|
|
|
if (td != NULL) {
|
|
|
|
KASSERT(td->td_priority >= PRI_MIN_IDLE,
|
2007-01-04 08:56:25 +00:00
|
|
|
("tdq_choose: Invalid priority on idle queue %d",
|
2008-03-20 05:51:16 +00:00
|
|
|
td->td_priority));
|
|
|
|
return (td);
|
2003-02-03 05:30:07 +00:00
|
|
|
}
|
|
|
|
|
2007-01-04 08:56:25 +00:00
|
|
|
return (NULL);
|
2003-04-02 06:46:43 +00:00
|
|
|
}
|
2003-01-29 07:00:51 +00:00
|
|
|
|
2007-07-17 22:53:23 +00:00
|
|
|
/*
|
|
|
|
* Initialize a thread queue.
|
|
|
|
*/
|
2003-01-29 07:00:51 +00:00
|
|
|
static void
|
2006-12-06 06:34:57 +00:00
|
|
|
tdq_setup(struct tdq *tdq)
|
2003-01-29 07:00:51 +00:00
|
|
|
{
|
2007-07-17 22:53:23 +00:00
|
|
|
|
2007-08-03 23:38:46 +00:00
|
|
|
if (bootverbose)
|
|
|
|
printf("ULE: setup cpu %d\n", TDQ_ID(tdq));
|
2007-01-04 08:56:25 +00:00
|
|
|
runq_init(&tdq->tdq_realtime);
|
|
|
|
runq_init(&tdq->tdq_timeshare);
|
2006-12-29 10:37:07 +00:00
|
|
|
runq_init(&tdq->tdq_idle);
|
2008-03-02 08:20:59 +00:00
|
|
|
snprintf(tdq->tdq_name, sizeof(tdq->tdq_name),
|
|
|
|
"sched lock %d", (int)TDQ_ID(tdq));
|
|
|
|
mtx_init(&tdq->tdq_lock, tdq->tdq_name, "sched lock",
|
2007-08-03 23:38:46 +00:00
|
|
|
MTX_SPIN | MTX_RECURSE);
|
|
|
|
}
|
|
|
|
|
2008-03-02 08:20:59 +00:00
|
|
|
#ifdef SMP
|
2007-08-03 23:38:46 +00:00
|
|
|
static void
|
|
|
|
sched_setup_smp(void)
|
|
|
|
{
|
|
|
|
struct tdq *tdq;
|
|
|
|
int i;
|
2003-04-11 03:47:14 +00:00
|
|
|
|
2008-03-02 08:20:59 +00:00
|
|
|
cpu_top = smp_topo();
|
|
|
|
for (i = 0; i < MAXCPU; i++) {
|
2007-08-03 23:38:46 +00:00
|
|
|
if (CPU_ABSENT(i))
|
|
|
|
continue;
|
2008-03-02 08:20:59 +00:00
|
|
|
tdq = TDQ_CPU(i);
|
2007-08-03 23:38:46 +00:00
|
|
|
tdq_setup(tdq);
|
2008-03-02 08:20:59 +00:00
|
|
|
tdq->tdq_cg = smp_topo_find(cpu_top, i);
|
|
|
|
if (tdq->tdq_cg == NULL)
|
|
|
|
panic("Can't find cpu group for %d\n", i);
|
2003-07-04 19:59:00 +00:00
|
|
|
}
|
2008-03-02 08:20:59 +00:00
|
|
|
balance_tdq = TDQ_SELF();
|
|
|
|
sched_balance();
|
2007-08-03 23:38:46 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Setup the thread queues and initialize the topology based on MD
|
|
|
|
* information.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
sched_setup(void *dummy)
|
|
|
|
{
|
|
|
|
struct tdq *tdq;
|
|
|
|
|
|
|
|
tdq = TDQ_SELF();
|
|
|
|
#ifdef SMP
|
2008-03-02 07:58:42 +00:00
|
|
|
sched_setup_smp();
|
2003-07-04 19:59:00 +00:00
|
|
|
#else
|
2007-08-03 23:38:46 +00:00
|
|
|
tdq_setup(tdq);
|
2003-06-09 00:39:09 +00:00
|
|
|
#endif
|
2007-07-17 22:53:23 +00:00
|
|
|
/*
|
|
|
|
* To avoid divide-by-zero, we set realstathz a dummy value
|
|
|
|
* in case which sched_clock() called before sched_initticks().
|
|
|
|
*/
|
|
|
|
realstathz = hz;
|
|
|
|
sched_slice = (realstathz/10); /* ~100ms */
|
|
|
|
tickincr = 1 << SCHED_TICK_SHIFT;
|
|
|
|
|
|
|
|
/* Add thread0's load since it's running. */
|
|
|
|
TDQ_LOCK(tdq);
|
2007-08-03 23:38:46 +00:00
|
|
|
thread0.td_lock = TDQ_LOCKPTR(TDQ_SELF());
|
2008-03-20 05:51:16 +00:00
|
|
|
tdq_load_add(tdq, &thread0);
|
2008-03-02 08:20:59 +00:00
|
|
|
tdq->tdq_lowpri = thread0.td_priority;
|
2007-07-17 22:53:23 +00:00
|
|
|
TDQ_UNLOCK(tdq);
|
2003-01-26 05:23:15 +00:00
|
|
|
}
|
|
|
|
|
2007-07-17 22:53:23 +00:00
|
|
|
/*
|
|
|
|
* This routine determines the tickincr after stathz and hz are setup.
|
|
|
|
*/
|
2005-12-19 08:26:09 +00:00
|
|
|
/* ARGSUSED */
|
|
|
|
static void
|
|
|
|
sched_initticks(void *dummy)
|
|
|
|
{
|
2007-07-17 22:53:23 +00:00
|
|
|
int incr;
|
|
|
|
|
2005-12-19 08:26:09 +00:00
|
|
|
realstathz = stathz ? stathz : hz;
|
2007-01-25 19:14:11 +00:00
|
|
|
sched_slice = (realstathz/10); /* ~100ms */
|
2005-12-19 08:26:09 +00:00
|
|
|
|
|
|
|
/*
|
2007-01-04 08:56:25 +00:00
|
|
|
* tickincr is shifted out by 10 to avoid rounding errors due to
|
2007-01-04 12:16:19 +00:00
|
|
|
* hz not being evenly divisible by stathz on all platforms.
|
2007-01-04 08:56:25 +00:00
|
|
|
*/
|
2007-07-17 22:53:23 +00:00
|
|
|
incr = (hz << SCHED_TICK_SHIFT) / realstathz;
|
2007-01-04 08:56:25 +00:00
|
|
|
/*
|
|
|
|
* This does not work for values of stathz that are more than
|
|
|
|
* 1 << SCHED_TICK_SHIFT * hz. In practice this does not happen.
|
2005-12-19 08:26:09 +00:00
|
|
|
*/
|
2007-07-17 22:53:23 +00:00
|
|
|
if (incr == 0)
|
|
|
|
incr = 1;
|
|
|
|
tickincr = incr;
|
2007-01-19 21:56:08 +00:00
|
|
|
#ifdef SMP
|
2007-10-02 00:36:06 +00:00
|
|
|
/*
|
|
|
|
* Set the default balance interval now that we know
|
|
|
|
* what realstathz is.
|
|
|
|
*/
|
|
|
|
balance_interval = realstathz;
|
2007-08-20 06:34:20 +00:00
|
|
|
/*
|
|
|
|
* Set steal thresh to log2(mp_ncpu) but no greater than 4. This
|
|
|
|
* prevents excess thrashing on large machines and excess idle on
|
|
|
|
* smaller machines.
|
|
|
|
*/
|
2008-03-02 08:20:59 +00:00
|
|
|
steal_thresh = min(ffs(mp_ncpus) - 1, 3);
|
2007-01-19 21:56:08 +00:00
|
|
|
affinity = SCHED_AFFINITY_DEFAULT;
|
|
|
|
#endif
|
2005-12-19 08:26:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-07-17 22:53:23 +00:00
|
|
|
/*
|
|
|
|
* This is the core of the interactivity algorithm. Determines a score based
|
|
|
|
* on past behavior. It is the ratio of sleep time to run time scaled to
|
|
|
|
* a [0, 100] integer. This is the voluntary sleep time of a process, which
|
|
|
|
* differs from the cpu usage because it does not account for time spent
|
|
|
|
* waiting on a run-queue. Would be prettier if we had floating point.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
sched_interact_score(struct thread *td)
|
|
|
|
{
|
|
|
|
struct td_sched *ts;
|
|
|
|
int div;
|
|
|
|
|
|
|
|
ts = td->td_sched;
|
|
|
|
/*
|
|
|
|
* The score is only needed if this is likely to be an interactive
|
|
|
|
* task. Don't go through the expense of computing it if there's
|
|
|
|
* no chance.
|
|
|
|
*/
|
|
|
|
if (sched_interact <= SCHED_INTERACT_HALF &&
|
|
|
|
ts->ts_runtime >= ts->ts_slptime)
|
|
|
|
return (SCHED_INTERACT_HALF);
|
|
|
|
|
|
|
|
if (ts->ts_runtime > ts->ts_slptime) {
|
|
|
|
div = max(1, ts->ts_runtime / SCHED_INTERACT_HALF);
|
|
|
|
return (SCHED_INTERACT_HALF +
|
|
|
|
(SCHED_INTERACT_HALF - (ts->ts_slptime / div)));
|
|
|
|
}
|
|
|
|
if (ts->ts_slptime > ts->ts_runtime) {
|
|
|
|
div = max(1, ts->ts_slptime / SCHED_INTERACT_HALF);
|
|
|
|
return (ts->ts_runtime / div);
|
|
|
|
}
|
|
|
|
/* runtime == slptime */
|
|
|
|
if (ts->ts_runtime)
|
|
|
|
return (SCHED_INTERACT_HALF);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This can happen if slptime and runtime are 0.
|
|
|
|
*/
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2003-01-26 05:23:15 +00:00
|
|
|
/*
|
|
|
|
* Scale the scheduling priority according to the "interactivity" of this
|
|
|
|
* process.
|
|
|
|
*/
|
2003-04-11 03:47:14 +00:00
|
|
|
static void
|
2006-10-26 21:42:22 +00:00
|
|
|
sched_priority(struct thread *td)
|
2003-01-26 05:23:15 +00:00
|
|
|
{
|
2007-01-04 08:56:25 +00:00
|
|
|
int score;
|
2003-01-26 05:23:15 +00:00
|
|
|
int pri;
|
|
|
|
|
2006-10-26 21:42:22 +00:00
|
|
|
if (td->td_pri_class != PRI_TIMESHARE)
|
2003-04-11 03:47:14 +00:00
|
|
|
return;
|
2003-04-02 06:46:43 +00:00
|
|
|
/*
|
2007-01-04 08:56:25 +00:00
|
|
|
* If the score is interactive we place the thread in the realtime
|
|
|
|
* queue with a priority that is less than kernel and interrupt
|
|
|
|
* priorities. These threads are not subject to nice restrictions.
|
2003-04-02 06:46:43 +00:00
|
|
|
*
|
2007-07-17 22:53:23 +00:00
|
|
|
* Scores greater than this are placed on the normal timeshare queue
|
2007-01-04 08:56:25 +00:00
|
|
|
* where the priority is partially decided by the most recent cpu
|
|
|
|
* utilization and the rest is decided by nice value.
|
2007-09-22 02:20:14 +00:00
|
|
|
*
|
|
|
|
* The nice value of the process has a linear effect on the calculated
|
|
|
|
* score. Negative nice values make it easier for a thread to be
|
|
|
|
* considered interactive.
|
2003-04-02 06:46:43 +00:00
|
|
|
*/
|
2007-09-24 00:28:54 +00:00
|
|
|
score = imax(0, sched_interact_score(td) - td->td_proc->p_nice);
|
2007-01-04 08:56:25 +00:00
|
|
|
if (score < sched_interact) {
|
|
|
|
pri = PRI_MIN_REALTIME;
|
|
|
|
pri += ((PRI_MAX_REALTIME - PRI_MIN_REALTIME) / sched_interact)
|
|
|
|
* score;
|
|
|
|
KASSERT(pri >= PRI_MIN_REALTIME && pri <= PRI_MAX_REALTIME,
|
2007-01-24 18:18:43 +00:00
|
|
|
("sched_priority: invalid interactive priority %d score %d",
|
|
|
|
pri, score));
|
2007-01-04 08:56:25 +00:00
|
|
|
} else {
|
|
|
|
pri = SCHED_PRI_MIN;
|
|
|
|
if (td->td_sched->ts_ticks)
|
|
|
|
pri += SCHED_PRI_TICKS(td->td_sched);
|
|
|
|
pri += SCHED_PRI_NICE(td->td_proc->p_nice);
|
2007-07-17 22:53:23 +00:00
|
|
|
KASSERT(pri >= PRI_MIN_TIMESHARE && pri <= PRI_MAX_TIMESHARE,
|
|
|
|
("sched_priority: invalid priority %d: nice %d, "
|
|
|
|
"ticks %d ftick %d ltick %d tick pri %d",
|
|
|
|
pri, td->td_proc->p_nice, td->td_sched->ts_ticks,
|
|
|
|
td->td_sched->ts_ftick, td->td_sched->ts_ltick,
|
|
|
|
SCHED_PRI_TICKS(td->td_sched)));
|
2007-01-04 08:56:25 +00:00
|
|
|
}
|
|
|
|
sched_user_prio(td, pri);
|
2003-01-26 05:23:15 +00:00
|
|
|
|
2003-04-02 06:46:43 +00:00
|
|
|
return;
|
2003-01-26 05:23:15 +00:00
|
|
|
}
|
|
|
|
|
2003-11-02 03:36:33 +00:00
|
|
|
/*
|
|
|
|
* This routine enforces a maximum limit on the amount of scheduling history
|
2007-07-17 22:53:23 +00:00
|
|
|
* kept. It is called after either the slptime or runtime is adjusted. This
|
|
|
|
* function is ugly due to integer math.
|
2003-11-02 03:36:33 +00:00
|
|
|
*/
|
2003-06-17 06:39:51 +00:00
|
|
|
static void
|
2006-10-26 21:42:22 +00:00
|
|
|
sched_interact_update(struct thread *td)
|
2003-06-17 06:39:51 +00:00
|
|
|
{
|
2007-01-05 23:45:38 +00:00
|
|
|
struct td_sched *ts;
|
2007-01-24 18:18:43 +00:00
|
|
|
u_int sum;
|
2003-11-02 03:36:33 +00:00
|
|
|
|
2007-01-05 23:45:38 +00:00
|
|
|
ts = td->td_sched;
|
2007-07-17 22:53:23 +00:00
|
|
|
sum = ts->ts_runtime + ts->ts_slptime;
|
2003-11-02 03:36:33 +00:00
|
|
|
if (sum < SCHED_SLP_RUN_MAX)
|
|
|
|
return;
|
2007-01-05 23:45:38 +00:00
|
|
|
/*
|
|
|
|
* This only happens from two places:
|
|
|
|
* 1) We have added an unusual amount of run time from fork_exit.
|
|
|
|
* 2) We have added an unusual amount of sleep time from sched_sleep().
|
|
|
|
*/
|
|
|
|
if (sum > SCHED_SLP_RUN_MAX * 2) {
|
2007-07-17 22:53:23 +00:00
|
|
|
if (ts->ts_runtime > ts->ts_slptime) {
|
|
|
|
ts->ts_runtime = SCHED_SLP_RUN_MAX;
|
|
|
|
ts->ts_slptime = 1;
|
2007-01-05 23:45:38 +00:00
|
|
|
} else {
|
2007-07-17 22:53:23 +00:00
|
|
|
ts->ts_slptime = SCHED_SLP_RUN_MAX;
|
|
|
|
ts->ts_runtime = 1;
|
2007-01-05 23:45:38 +00:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
2003-11-02 03:36:33 +00:00
|
|
|
/*
|
|
|
|
* If we have exceeded by more than 1/5th then the algorithm below
|
|
|
|
* will not bring us back into range. Dividing by two here forces
|
2004-08-10 07:52:21 +00:00
|
|
|
* us into the range of [4/5 * SCHED_INTERACT_MAX, SCHED_INTERACT_MAX]
|
2003-11-02 03:36:33 +00:00
|
|
|
*/
|
2004-04-04 19:12:56 +00:00
|
|
|
if (sum > (SCHED_SLP_RUN_MAX / 5) * 6) {
|
2007-07-17 22:53:23 +00:00
|
|
|
ts->ts_runtime /= 2;
|
|
|
|
ts->ts_slptime /= 2;
|
2003-11-02 03:36:33 +00:00
|
|
|
return;
|
|
|
|
}
|
2007-07-17 22:53:23 +00:00
|
|
|
ts->ts_runtime = (ts->ts_runtime / 5) * 4;
|
|
|
|
ts->ts_slptime = (ts->ts_slptime / 5) * 4;
|
2003-11-02 03:36:33 +00:00
|
|
|
}
|
2003-10-27 06:47:05 +00:00
|
|
|
|
2007-07-17 22:53:23 +00:00
|
|
|
/*
|
|
|
|
* Scale back the interactivity history when a child thread is created. The
|
|
|
|
* history is inherited from the parent but the thread may behave totally
|
|
|
|
* differently. For example, a shell spawning a compiler process. We want
|
|
|
|
* to learn that the compiler is behaving badly very quickly.
|
|
|
|
*/
|
2003-11-02 03:36:33 +00:00
|
|
|
static void
|
2006-10-26 21:42:22 +00:00
|
|
|
sched_interact_fork(struct thread *td)
|
2003-11-02 03:36:33 +00:00
|
|
|
{
|
|
|
|
int ratio;
|
|
|
|
int sum;
|
|
|
|
|
2007-07-17 22:53:23 +00:00
|
|
|
sum = td->td_sched->ts_runtime + td->td_sched->ts_slptime;
|
2003-11-02 03:36:33 +00:00
|
|
|
if (sum > SCHED_SLP_RUN_FORK) {
|
|
|
|
ratio = sum / SCHED_SLP_RUN_FORK;
|
2007-07-17 22:53:23 +00:00
|
|
|
td->td_sched->ts_runtime /= ratio;
|
|
|
|
td->td_sched->ts_slptime /= ratio;
|
2003-06-17 06:39:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-09-05 02:09:54 +00:00
|
|
|
/*
|
2007-07-17 22:53:23 +00:00
|
|
|
* Called from proc0_init() to setup the scheduler fields.
|
2004-09-05 02:09:54 +00:00
|
|
|
*/
|
|
|
|
void
|
|
|
|
schedinit(void)
|
|
|
|
{
|
2007-01-04 08:56:25 +00:00
|
|
|
|
2004-09-05 02:09:54 +00:00
|
|
|
/*
|
|
|
|
* Set up the scheduler specific parts of proc0.
|
|
|
|
*/
|
|
|
|
proc0.p_sched = NULL; /* XXX */
|
2006-12-06 06:34:57 +00:00
|
|
|
thread0.td_sched = &td_sched0;
|
2007-01-04 08:56:25 +00:00
|
|
|
td_sched0.ts_ltick = ticks;
|
2007-01-05 08:50:38 +00:00
|
|
|
td_sched0.ts_ftick = ticks;
|
2008-03-10 03:15:19 +00:00
|
|
|
td_sched0.ts_slice = sched_slice;
|
2004-09-05 02:09:54 +00:00
|
|
|
}
|
|
|
|
|
2003-04-11 03:47:14 +00:00
|
|
|
/*
|
|
|
|
* This is only somewhat accurate since given many processes of the same
|
|
|
|
* priority they will switch when their slices run out, which will be
|
2007-01-04 08:56:25 +00:00
|
|
|
* at most sched_slice stathz ticks.
|
2003-04-11 03:47:14 +00:00
|
|
|
*/
|
2003-01-26 05:23:15 +00:00
|
|
|
int
|
|
|
|
sched_rr_interval(void)
|
|
|
|
{
|
2007-01-04 08:56:25 +00:00
|
|
|
|
|
|
|
/* Convert sched_slice to hz */
|
|
|
|
return (hz/(realstathz/sched_slice));
|
2003-01-26 05:23:15 +00:00
|
|
|
}
|
|
|
|
|
2007-07-17 22:53:23 +00:00
|
|
|
/*
|
|
|
|
* Update the percent cpu tracking information when it is requested or
|
|
|
|
* the total history exceeds the maximum. We keep a sliding history of
|
|
|
|
* tick counts that slowly decays. This is less precise than the 4BSD
|
|
|
|
* mechanism since it happens with less regular and frequent events.
|
|
|
|
*/
|
- Add static to local functions and data where it was missing.
- Add an IPI based mechanism for migrating kses. This mechanism is
broken down into several components. This is intended to reduce cache
thrashing by eliminating most cases where one cpu touches another's
run queues.
- kseq_notify() appends a kse to a lockless singly linked list and
conditionally sends an IPI to the target processor. Right now this is
protected by sched_lock but at some point I'd like to get rid of the
global lock. This is why I used something more complicated than a
standard queue.
- kseq_assign() processes our list of kses that have been assigned to us
by other processors. This simply calls sched_add() for each item on the
list after clearing the new KEF_ASSIGNED flag. This flag is used to
indicate that we have been appeneded to the assigned queue but not
added to the run queue yet.
- In sched_add(), instead of adding a KSE to another processor's queue we
use kse_notify() so that we don't touch their queue. Also in sched_add(),
if KEF_ASSIGNED is already set return immediately. This can happen if
a thread is removed and readded so that the priority is recorded properly.
- In sched_rem() return immediately if KEF_ASSIGNED is set. All callers
immediately readd simply to adjust priorites etc.
- In sched_choose(), if we're running an IDLE task or the per cpu idle thread
set our cpumask bit in 'kseq_idle' so that other processors may know that
we are idle. Before this, make a single pass through the run queues of
other processors so that we may find work more immediately if it is
available.
- In sched_runnable(), don't scan each processor's run queue, they will IPI
us if they have work for us to do.
- In sched_add(), if we're adding a thread that can be migrated and we have
plenty of work to do, try to migrate the thread to an idle kseq.
- Simplify the logic in sched_prio() and take the KEF_ASSIGNED flag into
consideration.
- No longer use kseq_choose() to steal threads, it can lose it's last
argument.
- Create a new function runq_steal() which operates like runq_choose() but
skips threads based on some criteria. Currently it will not steal
PRI_ITHD threads. In the future this will be used for CPU binding.
- Create a kseq_steal() that checks each run queue with runq_steal(), use
kseq_steal() in the places where we used kseq_choose() to steal with
before.
2003-10-31 11:16:04 +00:00
|
|
|
static void
|
2006-12-06 06:34:57 +00:00
|
|
|
sched_pctcpu_update(struct td_sched *ts)
|
2003-01-26 05:23:15 +00:00
|
|
|
{
|
2007-01-04 08:56:25 +00:00
|
|
|
|
|
|
|
if (ts->ts_ticks == 0)
|
|
|
|
return;
|
2007-01-05 08:50:38 +00:00
|
|
|
if (ticks - (hz / 10) < ts->ts_ltick &&
|
|
|
|
SCHED_TICK_TOTAL(ts) < SCHED_TICK_MAX)
|
|
|
|
return;
|
2003-01-26 05:23:15 +00:00
|
|
|
/*
|
|
|
|
* Adjust counters and watermark for pctcpu calc.
|
2003-06-15 02:18:29 +00:00
|
|
|
*/
|
2007-01-04 08:56:25 +00:00
|
|
|
if (ts->ts_ltick > ticks - SCHED_TICK_TARG)
|
2006-12-06 06:34:57 +00:00
|
|
|
ts->ts_ticks = (ts->ts_ticks / (ticks - ts->ts_ftick)) *
|
2007-01-04 08:56:25 +00:00
|
|
|
SCHED_TICK_TARG;
|
|
|
|
else
|
2006-12-06 06:34:57 +00:00
|
|
|
ts->ts_ticks = 0;
|
|
|
|
ts->ts_ltick = ticks;
|
2007-01-04 08:56:25 +00:00
|
|
|
ts->ts_ftick = ts->ts_ltick - SCHED_TICK_TARG;
|
2003-01-26 05:23:15 +00:00
|
|
|
}
|
|
|
|
|
2007-07-17 22:53:23 +00:00
|
|
|
/*
|
|
|
|
* Adjust the priority of a thread. Move it to the appropriate run-queue
|
|
|
|
* if necessary. This is the back-end for several priority related
|
|
|
|
* functions.
|
|
|
|
*/
|
2007-01-04 08:56:25 +00:00
|
|
|
static void
|
Rework the interface between priority propagation (lending) and the
schedulers a bit to ensure more correct handling of priorities and fewer
priority inversions:
- Add two functions to the sched(9) API to handle priority lending:
sched_lend_prio() and sched_unlend_prio(). The turnstile code uses these
functions to ask the scheduler to lend a thread a set priority and to
tell the scheduler when it thinks it is ok for a thread to stop borrowing
priority. The unlend case is slightly complex in that the turnstile code
tells the scheduler what the minimum priority of the thread needs to be
to satisfy the requirements of any other threads blocked on locks owned
by the thread in question. The scheduler then decides where the thread
can go back to normal mode (if it's normal priority is high enough to
satisfy the pending lock requests) or it it should continue to use the
priority specified to the sched_unlend_prio() call. This involves adding
a new per-thread flag TDF_BORROWING that replaces the ULE-only kse flag
for priority elevation.
- Schedulers now refuse to lower the priority of a thread that is currently
borrowing another therad's priority.
- If a scheduler changes the priority of a thread that is currently sitting
on a turnstile, it will call a new function turnstile_adjust() to inform
the turnstile code of the change. This function resorts the thread on
the priority list of the turnstile if needed, and if the thread ends up
at the head of the list (due to having the highest priority) and its
priority was raised, then it will propagate that new priority to the
owner of the lock it is blocked on.
Some additional fixes specific to the 4BSD scheduler include:
- Common code for updating the priority of a thread when the user priority
of its associated kse group has been consolidated in a new static
function resetpriority_thread(). One change to this function is that
it will now only adjust the priority of a thread if it already has a
time sharing priority, thus preserving any boosts from a tsleep() until
the thread returns to userland. Also, resetpriority() no longer calls
maybe_resched() on each thread in the group. Instead, the code calling
resetpriority() is responsible for calling resetpriority_thread() on
any threads that need to be updated.
- schedcpu() now uses resetpriority_thread() instead of just calling
sched_prio() directly after it updates a kse group's user priority.
- sched_clock() now uses resetpriority_thread() rather than writing
directly to td_priority.
- sched_nice() now updates all the priorities of the threads after the
group priority has been adjusted.
Discussed with: bde
Reviewed by: ups, jeffr
Tested on: 4bsd, ule
Tested on: i386, alpha, sparc64
2004-12-30 20:52:44 +00:00
|
|
|
sched_thread_priority(struct thread *td, u_char prio)
|
2003-01-26 05:23:15 +00:00
|
|
|
{
|
2006-12-06 06:34:57 +00:00
|
|
|
struct td_sched *ts;
|
2008-03-10 03:15:19 +00:00
|
|
|
struct tdq *tdq;
|
|
|
|
int oldpri;
|
2003-01-26 05:23:15 +00:00
|
|
|
|
2004-12-26 00:15:33 +00:00
|
|
|
CTR6(KTR_SCHED, "sched_prio: %p(%s) prio %d newprio %d by %p(%s)",
|
2007-11-14 06:21:24 +00:00
|
|
|
td, td->td_name, td->td_priority, prio, curthread,
|
|
|
|
curthread->td_name);
|
2006-12-06 06:34:57 +00:00
|
|
|
ts = td->td_sched;
|
Commit 1/14 of sched_lock decomposition.
- Move all scheduler locking into the schedulers utilizing a technique
similar to solaris's container locking.
- A per-process spinlock is now used to protect the queue of threads,
thread count, suspension count, p_sflags, and other process
related scheduling fields.
- The new thread lock is actually a pointer to a spinlock for the
container that the thread is currently owned by. The container may
be a turnstile, sleepqueue, or run queue.
- thread_lock() is now used to protect access to thread related scheduling
fields. thread_unlock() unlocks the lock and thread_set_lock()
implements the transition from one lock to another.
- A new "blocked_lock" is used in cases where it is not safe to hold the
actual thread's lock yet we must prevent access to the thread.
- sched_throw() and sched_fork_exit() are introduced to allow the
schedulers to fix-up locking at these points.
- Add some minor infrastructure for optionally exporting scheduler
statistics that were invaluable in solving performance problems with
this patch. Generally these statistics allow you to differentiate
between different causes of context switches.
Tested by: kris, current@
Tested on: i386, amd64, ULE, 4BSD, libthr, libkse, PREEMPTION, etc.
Discussed with: kris, attilio, kmacy, jhb, julian, bde (small parts each)
2007-06-04 23:50:30 +00:00
|
|
|
THREAD_LOCK_ASSERT(td, MA_OWNED);
|
Rework the interface between priority propagation (lending) and the
schedulers a bit to ensure more correct handling of priorities and fewer
priority inversions:
- Add two functions to the sched(9) API to handle priority lending:
sched_lend_prio() and sched_unlend_prio(). The turnstile code uses these
functions to ask the scheduler to lend a thread a set priority and to
tell the scheduler when it thinks it is ok for a thread to stop borrowing
priority. The unlend case is slightly complex in that the turnstile code
tells the scheduler what the minimum priority of the thread needs to be
to satisfy the requirements of any other threads blocked on locks owned
by the thread in question. The scheduler then decides where the thread
can go back to normal mode (if it's normal priority is high enough to
satisfy the pending lock requests) or it it should continue to use the
priority specified to the sched_unlend_prio() call. This involves adding
a new per-thread flag TDF_BORROWING that replaces the ULE-only kse flag
for priority elevation.
- Schedulers now refuse to lower the priority of a thread that is currently
borrowing another therad's priority.
- If a scheduler changes the priority of a thread that is currently sitting
on a turnstile, it will call a new function turnstile_adjust() to inform
the turnstile code of the change. This function resorts the thread on
the priority list of the turnstile if needed, and if the thread ends up
at the head of the list (due to having the highest priority) and its
priority was raised, then it will propagate that new priority to the
owner of the lock it is blocked on.
Some additional fixes specific to the 4BSD scheduler include:
- Common code for updating the priority of a thread when the user priority
of its associated kse group has been consolidated in a new static
function resetpriority_thread(). One change to this function is that
it will now only adjust the priority of a thread if it already has a
time sharing priority, thus preserving any boosts from a tsleep() until
the thread returns to userland. Also, resetpriority() no longer calls
maybe_resched() on each thread in the group. Instead, the code calling
resetpriority() is responsible for calling resetpriority_thread() on
any threads that need to be updated.
- schedcpu() now uses resetpriority_thread() instead of just calling
sched_prio() directly after it updates a kse group's user priority.
- sched_clock() now uses resetpriority_thread() rather than writing
directly to td_priority.
- sched_nice() now updates all the priorities of the threads after the
group priority has been adjusted.
Discussed with: bde
Reviewed by: ups, jeffr
Tested on: 4bsd, ule
Tested on: i386, alpha, sparc64
2004-12-30 20:52:44 +00:00
|
|
|
if (td->td_priority == prio)
|
|
|
|
return;
|
2008-03-19 07:36:37 +00:00
|
|
|
/*
|
|
|
|
* If the priority has been elevated due to priority
|
|
|
|
* propagation, we may have to move ourselves to a new
|
|
|
|
* queue. This could be optimized to not re-add in some
|
|
|
|
* cases.
|
|
|
|
*/
|
2007-01-04 12:16:19 +00:00
|
|
|
if (TD_ON_RUNQ(td) && prio < td->td_priority) {
|
2007-01-04 08:56:25 +00:00
|
|
|
sched_rem(td);
|
|
|
|
td->td_priority = prio;
|
2007-07-17 22:53:23 +00:00
|
|
|
sched_add(td, SRQ_BORROWING);
|
2008-03-10 03:15:19 +00:00
|
|
|
return;
|
|
|
|
}
|
2008-03-19 07:36:37 +00:00
|
|
|
/*
|
|
|
|
* If the thread is currently running we may have to adjust the lowpri
|
|
|
|
* information so other cpus are aware of our current priority.
|
|
|
|
*/
|
2008-03-10 03:15:19 +00:00
|
|
|
if (TD_IS_RUNNING(td)) {
|
2008-03-19 07:36:37 +00:00
|
|
|
tdq = TDQ_CPU(ts->ts_cpu);
|
|
|
|
oldpri = td->td_priority;
|
|
|
|
td->td_priority = prio;
|
2008-03-02 08:20:59 +00:00
|
|
|
if (prio < tdq->tdq_lowpri)
|
|
|
|
tdq->tdq_lowpri = prio;
|
|
|
|
else if (tdq->tdq_lowpri == oldpri)
|
|
|
|
tdq_setlowpri(tdq, td);
|
2008-03-19 07:36:37 +00:00
|
|
|
return;
|
2008-03-10 03:15:19 +00:00
|
|
|
}
|
2008-03-19 07:36:37 +00:00
|
|
|
td->td_priority = prio;
|
2003-01-26 05:23:15 +00:00
|
|
|
}
|
|
|
|
|
Rework the interface between priority propagation (lending) and the
schedulers a bit to ensure more correct handling of priorities and fewer
priority inversions:
- Add two functions to the sched(9) API to handle priority lending:
sched_lend_prio() and sched_unlend_prio(). The turnstile code uses these
functions to ask the scheduler to lend a thread a set priority and to
tell the scheduler when it thinks it is ok for a thread to stop borrowing
priority. The unlend case is slightly complex in that the turnstile code
tells the scheduler what the minimum priority of the thread needs to be
to satisfy the requirements of any other threads blocked on locks owned
by the thread in question. The scheduler then decides where the thread
can go back to normal mode (if it's normal priority is high enough to
satisfy the pending lock requests) or it it should continue to use the
priority specified to the sched_unlend_prio() call. This involves adding
a new per-thread flag TDF_BORROWING that replaces the ULE-only kse flag
for priority elevation.
- Schedulers now refuse to lower the priority of a thread that is currently
borrowing another therad's priority.
- If a scheduler changes the priority of a thread that is currently sitting
on a turnstile, it will call a new function turnstile_adjust() to inform
the turnstile code of the change. This function resorts the thread on
the priority list of the turnstile if needed, and if the thread ends up
at the head of the list (due to having the highest priority) and its
priority was raised, then it will propagate that new priority to the
owner of the lock it is blocked on.
Some additional fixes specific to the 4BSD scheduler include:
- Common code for updating the priority of a thread when the user priority
of its associated kse group has been consolidated in a new static
function resetpriority_thread(). One change to this function is that
it will now only adjust the priority of a thread if it already has a
time sharing priority, thus preserving any boosts from a tsleep() until
the thread returns to userland. Also, resetpriority() no longer calls
maybe_resched() on each thread in the group. Instead, the code calling
resetpriority() is responsible for calling resetpriority_thread() on
any threads that need to be updated.
- schedcpu() now uses resetpriority_thread() instead of just calling
sched_prio() directly after it updates a kse group's user priority.
- sched_clock() now uses resetpriority_thread() rather than writing
directly to td_priority.
- sched_nice() now updates all the priorities of the threads after the
group priority has been adjusted.
Discussed with: bde
Reviewed by: ups, jeffr
Tested on: 4bsd, ule
Tested on: i386, alpha, sparc64
2004-12-30 20:52:44 +00:00
|
|
|
/*
|
|
|
|
* Update a thread's priority when it is lent another thread's
|
|
|
|
* priority.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
sched_lend_prio(struct thread *td, u_char prio)
|
|
|
|
{
|
|
|
|
|
|
|
|
td->td_flags |= TDF_BORROWING;
|
|
|
|
sched_thread_priority(td, prio);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Restore a thread's priority when priority propagation is
|
|
|
|
* over. The prio argument is the minimum priority the thread
|
|
|
|
* needs to have to satisfy other possible priority lending
|
|
|
|
* requests. If the thread's regular priority is less
|
|
|
|
* important than prio, the thread will keep a priority boost
|
|
|
|
* of prio.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
sched_unlend_prio(struct thread *td, u_char prio)
|
|
|
|
{
|
|
|
|
u_char base_pri;
|
|
|
|
|
|
|
|
if (td->td_base_pri >= PRI_MIN_TIMESHARE &&
|
|
|
|
td->td_base_pri <= PRI_MAX_TIMESHARE)
|
2006-10-26 21:42:22 +00:00
|
|
|
base_pri = td->td_user_pri;
|
Rework the interface between priority propagation (lending) and the
schedulers a bit to ensure more correct handling of priorities and fewer
priority inversions:
- Add two functions to the sched(9) API to handle priority lending:
sched_lend_prio() and sched_unlend_prio(). The turnstile code uses these
functions to ask the scheduler to lend a thread a set priority and to
tell the scheduler when it thinks it is ok for a thread to stop borrowing
priority. The unlend case is slightly complex in that the turnstile code
tells the scheduler what the minimum priority of the thread needs to be
to satisfy the requirements of any other threads blocked on locks owned
by the thread in question. The scheduler then decides where the thread
can go back to normal mode (if it's normal priority is high enough to
satisfy the pending lock requests) or it it should continue to use the
priority specified to the sched_unlend_prio() call. This involves adding
a new per-thread flag TDF_BORROWING that replaces the ULE-only kse flag
for priority elevation.
- Schedulers now refuse to lower the priority of a thread that is currently
borrowing another therad's priority.
- If a scheduler changes the priority of a thread that is currently sitting
on a turnstile, it will call a new function turnstile_adjust() to inform
the turnstile code of the change. This function resorts the thread on
the priority list of the turnstile if needed, and if the thread ends up
at the head of the list (due to having the highest priority) and its
priority was raised, then it will propagate that new priority to the
owner of the lock it is blocked on.
Some additional fixes specific to the 4BSD scheduler include:
- Common code for updating the priority of a thread when the user priority
of its associated kse group has been consolidated in a new static
function resetpriority_thread(). One change to this function is that
it will now only adjust the priority of a thread if it already has a
time sharing priority, thus preserving any boosts from a tsleep() until
the thread returns to userland. Also, resetpriority() no longer calls
maybe_resched() on each thread in the group. Instead, the code calling
resetpriority() is responsible for calling resetpriority_thread() on
any threads that need to be updated.
- schedcpu() now uses resetpriority_thread() instead of just calling
sched_prio() directly after it updates a kse group's user priority.
- sched_clock() now uses resetpriority_thread() rather than writing
directly to td_priority.
- sched_nice() now updates all the priorities of the threads after the
group priority has been adjusted.
Discussed with: bde
Reviewed by: ups, jeffr
Tested on: 4bsd, ule
Tested on: i386, alpha, sparc64
2004-12-30 20:52:44 +00:00
|
|
|
else
|
|
|
|
base_pri = td->td_base_pri;
|
|
|
|
if (prio >= base_pri) {
|
2004-12-30 22:17:00 +00:00
|
|
|
td->td_flags &= ~TDF_BORROWING;
|
Rework the interface between priority propagation (lending) and the
schedulers a bit to ensure more correct handling of priorities and fewer
priority inversions:
- Add two functions to the sched(9) API to handle priority lending:
sched_lend_prio() and sched_unlend_prio(). The turnstile code uses these
functions to ask the scheduler to lend a thread a set priority and to
tell the scheduler when it thinks it is ok for a thread to stop borrowing
priority. The unlend case is slightly complex in that the turnstile code
tells the scheduler what the minimum priority of the thread needs to be
to satisfy the requirements of any other threads blocked on locks owned
by the thread in question. The scheduler then decides where the thread
can go back to normal mode (if it's normal priority is high enough to
satisfy the pending lock requests) or it it should continue to use the
priority specified to the sched_unlend_prio() call. This involves adding
a new per-thread flag TDF_BORROWING that replaces the ULE-only kse flag
for priority elevation.
- Schedulers now refuse to lower the priority of a thread that is currently
borrowing another therad's priority.
- If a scheduler changes the priority of a thread that is currently sitting
on a turnstile, it will call a new function turnstile_adjust() to inform
the turnstile code of the change. This function resorts the thread on
the priority list of the turnstile if needed, and if the thread ends up
at the head of the list (due to having the highest priority) and its
priority was raised, then it will propagate that new priority to the
owner of the lock it is blocked on.
Some additional fixes specific to the 4BSD scheduler include:
- Common code for updating the priority of a thread when the user priority
of its associated kse group has been consolidated in a new static
function resetpriority_thread(). One change to this function is that
it will now only adjust the priority of a thread if it already has a
time sharing priority, thus preserving any boosts from a tsleep() until
the thread returns to userland. Also, resetpriority() no longer calls
maybe_resched() on each thread in the group. Instead, the code calling
resetpriority() is responsible for calling resetpriority_thread() on
any threads that need to be updated.
- schedcpu() now uses resetpriority_thread() instead of just calling
sched_prio() directly after it updates a kse group's user priority.
- sched_clock() now uses resetpriority_thread() rather than writing
directly to td_priority.
- sched_nice() now updates all the priorities of the threads after the
group priority has been adjusted.
Discussed with: bde
Reviewed by: ups, jeffr
Tested on: 4bsd, ule
Tested on: i386, alpha, sparc64
2004-12-30 20:52:44 +00:00
|
|
|
sched_thread_priority(td, base_pri);
|
|
|
|
} else
|
|
|
|
sched_lend_prio(td, prio);
|
|
|
|
}
|
|
|
|
|
2007-07-17 22:53:23 +00:00
|
|
|
/*
|
|
|
|
* Standard entry for setting the priority to an absolute value.
|
|
|
|
*/
|
Rework the interface between priority propagation (lending) and the
schedulers a bit to ensure more correct handling of priorities and fewer
priority inversions:
- Add two functions to the sched(9) API to handle priority lending:
sched_lend_prio() and sched_unlend_prio(). The turnstile code uses these
functions to ask the scheduler to lend a thread a set priority and to
tell the scheduler when it thinks it is ok for a thread to stop borrowing
priority. The unlend case is slightly complex in that the turnstile code
tells the scheduler what the minimum priority of the thread needs to be
to satisfy the requirements of any other threads blocked on locks owned
by the thread in question. The scheduler then decides where the thread
can go back to normal mode (if it's normal priority is high enough to
satisfy the pending lock requests) or it it should continue to use the
priority specified to the sched_unlend_prio() call. This involves adding
a new per-thread flag TDF_BORROWING that replaces the ULE-only kse flag
for priority elevation.
- Schedulers now refuse to lower the priority of a thread that is currently
borrowing another therad's priority.
- If a scheduler changes the priority of a thread that is currently sitting
on a turnstile, it will call a new function turnstile_adjust() to inform
the turnstile code of the change. This function resorts the thread on
the priority list of the turnstile if needed, and if the thread ends up
at the head of the list (due to having the highest priority) and its
priority was raised, then it will propagate that new priority to the
owner of the lock it is blocked on.
Some additional fixes specific to the 4BSD scheduler include:
- Common code for updating the priority of a thread when the user priority
of its associated kse group has been consolidated in a new static
function resetpriority_thread(). One change to this function is that
it will now only adjust the priority of a thread if it already has a
time sharing priority, thus preserving any boosts from a tsleep() until
the thread returns to userland. Also, resetpriority() no longer calls
maybe_resched() on each thread in the group. Instead, the code calling
resetpriority() is responsible for calling resetpriority_thread() on
any threads that need to be updated.
- schedcpu() now uses resetpriority_thread() instead of just calling
sched_prio() directly after it updates a kse group's user priority.
- sched_clock() now uses resetpriority_thread() rather than writing
directly to td_priority.
- sched_nice() now updates all the priorities of the threads after the
group priority has been adjusted.
Discussed with: bde
Reviewed by: ups, jeffr
Tested on: 4bsd, ule
Tested on: i386, alpha, sparc64
2004-12-30 20:52:44 +00:00
|
|
|
void
|
|
|
|
sched_prio(struct thread *td, u_char prio)
|
|
|
|
{
|
|
|
|
u_char oldprio;
|
|
|
|
|
|
|
|
/* First, update the base priority. */
|
|
|
|
td->td_base_pri = prio;
|
|
|
|
|
|
|
|
/*
|
2004-12-30 22:17:00 +00:00
|
|
|
* If the thread is borrowing another thread's priority, don't
|
Rework the interface between priority propagation (lending) and the
schedulers a bit to ensure more correct handling of priorities and fewer
priority inversions:
- Add two functions to the sched(9) API to handle priority lending:
sched_lend_prio() and sched_unlend_prio(). The turnstile code uses these
functions to ask the scheduler to lend a thread a set priority and to
tell the scheduler when it thinks it is ok for a thread to stop borrowing
priority. The unlend case is slightly complex in that the turnstile code
tells the scheduler what the minimum priority of the thread needs to be
to satisfy the requirements of any other threads blocked on locks owned
by the thread in question. The scheduler then decides where the thread
can go back to normal mode (if it's normal priority is high enough to
satisfy the pending lock requests) or it it should continue to use the
priority specified to the sched_unlend_prio() call. This involves adding
a new per-thread flag TDF_BORROWING that replaces the ULE-only kse flag
for priority elevation.
- Schedulers now refuse to lower the priority of a thread that is currently
borrowing another therad's priority.
- If a scheduler changes the priority of a thread that is currently sitting
on a turnstile, it will call a new function turnstile_adjust() to inform
the turnstile code of the change. This function resorts the thread on
the priority list of the turnstile if needed, and if the thread ends up
at the head of the list (due to having the highest priority) and its
priority was raised, then it will propagate that new priority to the
owner of the lock it is blocked on.
Some additional fixes specific to the 4BSD scheduler include:
- Common code for updating the priority of a thread when the user priority
of its associated kse group has been consolidated in a new static
function resetpriority_thread(). One change to this function is that
it will now only adjust the priority of a thread if it already has a
time sharing priority, thus preserving any boosts from a tsleep() until
the thread returns to userland. Also, resetpriority() no longer calls
maybe_resched() on each thread in the group. Instead, the code calling
resetpriority() is responsible for calling resetpriority_thread() on
any threads that need to be updated.
- schedcpu() now uses resetpriority_thread() instead of just calling
sched_prio() directly after it updates a kse group's user priority.
- sched_clock() now uses resetpriority_thread() rather than writing
directly to td_priority.
- sched_nice() now updates all the priorities of the threads after the
group priority has been adjusted.
Discussed with: bde
Reviewed by: ups, jeffr
Tested on: 4bsd, ule
Tested on: i386, alpha, sparc64
2004-12-30 20:52:44 +00:00
|
|
|
* ever lower the priority.
|
|
|
|
*/
|
|
|
|
if (td->td_flags & TDF_BORROWING && td->td_priority < prio)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* Change the real priority. */
|
|
|
|
oldprio = td->td_priority;
|
|
|
|
sched_thread_priority(td, prio);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the thread is on a turnstile, then let the turnstile update
|
|
|
|
* its state.
|
|
|
|
*/
|
|
|
|
if (TD_ON_LOCK(td) && oldprio != prio)
|
|
|
|
turnstile_adjust(td, oldprio);
|
|
|
|
}
|
2004-12-30 22:17:00 +00:00
|
|
|
|
2007-07-17 22:53:23 +00:00
|
|
|
/*
|
|
|
|
* Set the base user priority, does not effect current running priority.
|
|
|
|
*/
|
2006-08-25 06:12:53 +00:00
|
|
|
void
|
2006-10-26 21:42:22 +00:00
|
|
|
sched_user_prio(struct thread *td, u_char prio)
|
2006-08-25 06:12:53 +00:00
|
|
|
{
|
|
|
|
u_char oldprio;
|
|
|
|
|
2006-10-26 21:42:22 +00:00
|
|
|
td->td_base_user_pri = prio;
|
2006-12-06 06:55:59 +00:00
|
|
|
if (td->td_flags & TDF_UBORROWING && td->td_user_pri <= prio)
|
|
|
|
return;
|
2006-10-26 21:42:22 +00:00
|
|
|
oldprio = td->td_user_pri;
|
|
|
|
td->td_user_pri = prio;
|
2006-08-25 06:12:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
sched_lend_user_prio(struct thread *td, u_char prio)
|
|
|
|
{
|
|
|
|
u_char oldprio;
|
|
|
|
|
2007-12-11 08:25:36 +00:00
|
|
|
THREAD_LOCK_ASSERT(td, MA_OWNED);
|
2006-08-25 06:12:53 +00:00
|
|
|
td->td_flags |= TDF_UBORROWING;
|
2006-11-08 09:09:07 +00:00
|
|
|
oldprio = td->td_user_pri;
|
2006-10-26 21:42:22 +00:00
|
|
|
td->td_user_pri = prio;
|
2006-08-25 06:12:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
sched_unlend_user_prio(struct thread *td, u_char prio)
|
|
|
|
{
|
|
|
|
u_char base_pri;
|
|
|
|
|
2007-12-11 08:25:36 +00:00
|
|
|
THREAD_LOCK_ASSERT(td, MA_OWNED);
|
2006-10-26 21:42:22 +00:00
|
|
|
base_pri = td->td_base_user_pri;
|
2006-08-25 06:12:53 +00:00
|
|
|
if (prio >= base_pri) {
|
|
|
|
td->td_flags &= ~TDF_UBORROWING;
|
2006-10-26 21:42:22 +00:00
|
|
|
sched_user_prio(td, base_pri);
|
2007-12-11 08:25:36 +00:00
|
|
|
} else {
|
2006-08-25 06:12:53 +00:00
|
|
|
sched_lend_user_prio(td, prio);
|
2007-12-11 08:25:36 +00:00
|
|
|
}
|
2006-08-25 06:12:53 +00:00
|
|
|
}
|
|
|
|
|
2007-12-21 23:30:18 +00:00
|
|
|
/*
|
|
|
|
* Block a thread for switching. Similar to thread_block() but does not
|
|
|
|
* bump the spin count.
|
|
|
|
*/
|
|
|
|
static inline struct mtx *
|
|
|
|
thread_block_switch(struct thread *td)
|
|
|
|
{
|
|
|
|
struct mtx *lock;
|
|
|
|
|
|
|
|
THREAD_LOCK_ASSERT(td, MA_OWNED);
|
|
|
|
lock = td->td_lock;
|
|
|
|
td->td_lock = &blocked_lock;
|
|
|
|
mtx_unlock_spin(lock);
|
|
|
|
|
|
|
|
return (lock);
|
|
|
|
}
|
|
|
|
|
2007-08-03 23:38:46 +00:00
|
|
|
/*
|
|
|
|
* Handle migration from sched_switch(). This happens only for
|
|
|
|
* cpu binding.
|
|
|
|
*/
|
|
|
|
static struct mtx *
|
|
|
|
sched_switch_migrate(struct tdq *tdq, struct thread *td, int flags)
|
|
|
|
{
|
|
|
|
struct tdq *tdn;
|
|
|
|
|
|
|
|
tdn = TDQ_CPU(td->td_sched->ts_cpu);
|
|
|
|
#ifdef SMP
|
2008-03-20 05:51:16 +00:00
|
|
|
tdq_load_rem(tdq, td);
|
2007-08-03 23:38:46 +00:00
|
|
|
/*
|
|
|
|
* Do the lock dance required to avoid LOR. We grab an extra
|
|
|
|
* spinlock nesting to prevent preemption while we're
|
|
|
|
* not holding either run-queue lock.
|
|
|
|
*/
|
|
|
|
spinlock_enter();
|
|
|
|
thread_block_switch(td); /* This releases the lock on tdq. */
|
|
|
|
TDQ_LOCK(tdn);
|
|
|
|
tdq_add(tdn, td, flags);
|
2008-03-20 05:51:16 +00:00
|
|
|
tdq_notify(tdn, td);
|
2007-08-03 23:38:46 +00:00
|
|
|
/*
|
|
|
|
* After we unlock tdn the new cpu still can't switch into this
|
|
|
|
* thread until we've unblocked it in cpu_switch(). The lock
|
|
|
|
* pointers may match in the case of HTT cores. Don't unlock here
|
|
|
|
* or we can deadlock when the other CPU runs the IPI handler.
|
|
|
|
*/
|
|
|
|
if (TDQ_LOCKPTR(tdn) != TDQ_LOCKPTR(tdq)) {
|
|
|
|
TDQ_UNLOCK(tdn);
|
|
|
|
TDQ_LOCK(tdq);
|
|
|
|
}
|
|
|
|
spinlock_exit();
|
|
|
|
#endif
|
|
|
|
return (TDQ_LOCKPTR(tdn));
|
|
|
|
}
|
|
|
|
|
2007-07-17 22:53:23 +00:00
|
|
|
/*
|
|
|
|
* Release a thread that was blocked with thread_block_switch().
|
|
|
|
*/
|
|
|
|
static inline void
|
|
|
|
thread_unblock_switch(struct thread *td, struct mtx *mtx)
|
|
|
|
{
|
|
|
|
atomic_store_rel_ptr((volatile uintptr_t *)&td->td_lock,
|
|
|
|
(uintptr_t)mtx);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Switch threads. This function has to handle threads coming in while
|
|
|
|
* blocked for some reason, running, or idle. It also must deal with
|
|
|
|
* migrating a thread from one queue to another as running threads may
|
|
|
|
* be assigned elsewhere via binding.
|
|
|
|
*/
|
2003-01-26 05:23:15 +00:00
|
|
|
void
|
2004-09-10 21:04:38 +00:00
|
|
|
sched_switch(struct thread *td, struct thread *newtd, int flags)
|
2003-01-26 05:23:15 +00:00
|
|
|
{
|
2006-12-29 12:55:32 +00:00
|
|
|
struct tdq *tdq;
|
2006-12-06 06:34:57 +00:00
|
|
|
struct td_sched *ts;
|
2007-07-17 22:53:23 +00:00
|
|
|
struct mtx *mtx;
|
2007-08-03 23:38:46 +00:00
|
|
|
int srqflag;
|
2007-07-17 22:53:23 +00:00
|
|
|
int cpuid;
|
2003-01-26 05:23:15 +00:00
|
|
|
|
Commit 1/14 of sched_lock decomposition.
- Move all scheduler locking into the schedulers utilizing a technique
similar to solaris's container locking.
- A per-process spinlock is now used to protect the queue of threads,
thread count, suspension count, p_sflags, and other process
related scheduling fields.
- The new thread lock is actually a pointer to a spinlock for the
container that the thread is currently owned by. The container may
be a turnstile, sleepqueue, or run queue.
- thread_lock() is now used to protect access to thread related scheduling
fields. thread_unlock() unlocks the lock and thread_set_lock()
implements the transition from one lock to another.
- A new "blocked_lock" is used in cases where it is not safe to hold the
actual thread's lock yet we must prevent access to the thread.
- sched_throw() and sched_fork_exit() are introduced to allow the
schedulers to fix-up locking at these points.
- Add some minor infrastructure for optionally exporting scheduler
statistics that were invaluable in solving performance problems with
this patch. Generally these statistics allow you to differentiate
between different causes of context switches.
Tested by: kris, current@
Tested on: i386, amd64, ULE, 4BSD, libthr, libkse, PREEMPTION, etc.
Discussed with: kris, attilio, kmacy, jhb, julian, bde (small parts each)
2007-06-04 23:50:30 +00:00
|
|
|
THREAD_LOCK_ASSERT(td, MA_OWNED);
|
2008-03-19 07:36:37 +00:00
|
|
|
KASSERT(newtd == NULL, ("sched_switch: Unsupported newtd argument"));
|
2003-01-26 05:23:15 +00:00
|
|
|
|
2007-07-17 22:53:23 +00:00
|
|
|
cpuid = PCPU_GET(cpuid);
|
|
|
|
tdq = TDQ_CPU(cpuid);
|
2007-01-04 08:56:25 +00:00
|
|
|
ts = td->td_sched;
|
2007-08-03 23:38:46 +00:00
|
|
|
mtx = td->td_lock;
|
2007-07-17 22:53:23 +00:00
|
|
|
ts->ts_rltick = ticks;
|
2004-08-12 07:56:33 +00:00
|
|
|
td->td_lastcpu = td->td_oncpu;
|
2003-04-10 17:35:44 +00:00
|
|
|
td->td_oncpu = NOCPU;
|
2004-07-16 21:04:55 +00:00
|
|
|
td->td_flags &= ~TDF_NEEDRESCHED;
|
2005-04-08 03:37:53 +00:00
|
|
|
td->td_owepreempt = 0;
|
2008-04-17 09:56:01 +00:00
|
|
|
tdq->tdq_switchcnt++;
|
2003-12-11 04:00:49 +00:00
|
|
|
/*
|
2007-07-17 22:53:23 +00:00
|
|
|
* The lock pointer in an idle thread should never change. Reset it
|
|
|
|
* to CAN_RUN as well.
|
Commit 1/14 of sched_lock decomposition.
- Move all scheduler locking into the schedulers utilizing a technique
similar to solaris's container locking.
- A per-process spinlock is now used to protect the queue of threads,
thread count, suspension count, p_sflags, and other process
related scheduling fields.
- The new thread lock is actually a pointer to a spinlock for the
container that the thread is currently owned by. The container may
be a turnstile, sleepqueue, or run queue.
- thread_lock() is now used to protect access to thread related scheduling
fields. thread_unlock() unlocks the lock and thread_set_lock()
implements the transition from one lock to another.
- A new "blocked_lock" is used in cases where it is not safe to hold the
actual thread's lock yet we must prevent access to the thread.
- sched_throw() and sched_fork_exit() are introduced to allow the
schedulers to fix-up locking at these points.
- Add some minor infrastructure for optionally exporting scheduler
statistics that were invaluable in solving performance problems with
this patch. Generally these statistics allow you to differentiate
between different causes of context switches.
Tested by: kris, current@
Tested on: i386, amd64, ULE, 4BSD, libthr, libkse, PREEMPTION, etc.
Discussed with: kris, attilio, kmacy, jhb, julian, bde (small parts each)
2007-06-04 23:50:30 +00:00
|
|
|
*/
|
2007-03-08 06:44:34 +00:00
|
|
|
if (TD_IS_IDLETHREAD(td)) {
|
2007-07-17 22:53:23 +00:00
|
|
|
MPASS(td->td_lock == TDQ_LOCKPTR(tdq));
|
2004-12-26 22:56:08 +00:00
|
|
|
TD_SET_CAN_RUN(td);
|
Commit 1/14 of sched_lock decomposition.
- Move all scheduler locking into the schedulers utilizing a technique
similar to solaris's container locking.
- A per-process spinlock is now used to protect the queue of threads,
thread count, suspension count, p_sflags, and other process
related scheduling fields.
- The new thread lock is actually a pointer to a spinlock for the
container that the thread is currently owned by. The container may
be a turnstile, sleepqueue, or run queue.
- thread_lock() is now used to protect access to thread related scheduling
fields. thread_unlock() unlocks the lock and thread_set_lock()
implements the transition from one lock to another.
- A new "blocked_lock" is used in cases where it is not safe to hold the
actual thread's lock yet we must prevent access to the thread.
- sched_throw() and sched_fork_exit() are introduced to allow the
schedulers to fix-up locking at these points.
- Add some minor infrastructure for optionally exporting scheduler
statistics that were invaluable in solving performance problems with
this patch. Generally these statistics allow you to differentiate
between different causes of context switches.
Tested by: kris, current@
Tested on: i386, amd64, ULE, 4BSD, libthr, libkse, PREEMPTION, etc.
Discussed with: kris, attilio, kmacy, jhb, julian, bde (small parts each)
2007-06-04 23:50:30 +00:00
|
|
|
} else if (TD_IS_RUNNING(td)) {
|
2007-07-17 22:53:23 +00:00
|
|
|
MPASS(td->td_lock == TDQ_LOCKPTR(tdq));
|
2007-08-03 23:38:46 +00:00
|
|
|
srqflag = (flags & SW_PREEMPT) ?
|
Commit 1/14 of sched_lock decomposition.
- Move all scheduler locking into the schedulers utilizing a technique
similar to solaris's container locking.
- A per-process spinlock is now used to protect the queue of threads,
thread count, suspension count, p_sflags, and other process
related scheduling fields.
- The new thread lock is actually a pointer to a spinlock for the
container that the thread is currently owned by. The container may
be a turnstile, sleepqueue, or run queue.
- thread_lock() is now used to protect access to thread related scheduling
fields. thread_unlock() unlocks the lock and thread_set_lock()
implements the transition from one lock to another.
- A new "blocked_lock" is used in cases where it is not safe to hold the
actual thread's lock yet we must prevent access to the thread.
- sched_throw() and sched_fork_exit() are introduced to allow the
schedulers to fix-up locking at these points.
- Add some minor infrastructure for optionally exporting scheduler
statistics that were invaluable in solving performance problems with
this patch. Generally these statistics allow you to differentiate
between different causes of context switches.
Tested by: kris, current@
Tested on: i386, amd64, ULE, 4BSD, libthr, libkse, PREEMPTION, etc.
Discussed with: kris, attilio, kmacy, jhb, julian, bde (small parts each)
2007-06-04 23:50:30 +00:00
|
|
|
SRQ_OURSELF|SRQ_YIELDING|SRQ_PREEMPTED :
|
2007-08-03 23:38:46 +00:00
|
|
|
SRQ_OURSELF|SRQ_YIELDING;
|
|
|
|
if (ts->ts_cpu == cpuid)
|
2008-03-20 05:51:16 +00:00
|
|
|
tdq_runq_add(tdq, td, srqflag);
|
2007-08-03 23:38:46 +00:00
|
|
|
else
|
|
|
|
mtx = sched_switch_migrate(tdq, td, srqflag);
|
2007-07-17 22:53:23 +00:00
|
|
|
} else {
|
|
|
|
/* This thread must be going to sleep. */
|
|
|
|
TDQ_LOCK(tdq);
|
|
|
|
mtx = thread_block_switch(td);
|
2008-03-20 05:51:16 +00:00
|
|
|
tdq_load_rem(tdq, td);
|
2007-07-17 22:53:23 +00:00
|
|
|
}
|
|
|
|
/*
|
|
|
|
* We enter here with the thread blocked and assigned to the
|
|
|
|
* appropriate cpu run-queue or sleep-queue and with the current
|
|
|
|
* thread-queue locked.
|
|
|
|
*/
|
|
|
|
TDQ_LOCK_ASSERT(tdq, MA_OWNED | MA_NOTRECURSED);
|
|
|
|
newtd = choosethread();
|
|
|
|
/*
|
|
|
|
* Call the MD code to switch contexts if necessary.
|
|
|
|
*/
|
2005-04-19 04:01:25 +00:00
|
|
|
if (td != newtd) {
|
|
|
|
#ifdef HWPMC_HOOKS
|
|
|
|
if (PMC_PROC_IS_USING_PMCS(td->td_proc))
|
|
|
|
PMC_SWITCH_CONTEXT(td, PMC_FN_CSW_OUT);
|
|
|
|
#endif
|
2007-12-15 23:13:31 +00:00
|
|
|
lock_profile_release_lock(&TDQ_LOCKPTR(tdq)->lock_object);
|
2007-10-02 01:30:18 +00:00
|
|
|
TDQ_LOCKPTR(tdq)->mtx_lock = (uintptr_t)newtd;
|
2008-05-25 01:44:58 +00:00
|
|
|
|
|
|
|
#ifdef KDTRACE_HOOKS
|
|
|
|
/*
|
|
|
|
* If DTrace has set the active vtime enum to anything
|
|
|
|
* other than INACTIVE (0), then it should have set the
|
|
|
|
* function to call.
|
|
|
|
*/
|
|
|
|
if (dtrace_vtime_active)
|
|
|
|
(*dtrace_vtime_switch_func)(newtd);
|
|
|
|
#endif
|
|
|
|
|
2007-07-17 22:53:23 +00:00
|
|
|
cpu_switch(td, newtd, mtx);
|
|
|
|
/*
|
|
|
|
* We may return from cpu_switch on a different cpu. However,
|
|
|
|
* we always return with td_lock pointing to the current cpu's
|
|
|
|
* run queue lock.
|
|
|
|
*/
|
|
|
|
cpuid = PCPU_GET(cpuid);
|
|
|
|
tdq = TDQ_CPU(cpuid);
|
2007-12-15 23:13:31 +00:00
|
|
|
lock_profile_obtain_lock_success(
|
|
|
|
&TDQ_LOCKPTR(tdq)->lock_object, 0, 0, __FILE__, __LINE__);
|
2005-04-19 04:01:25 +00:00
|
|
|
#ifdef HWPMC_HOOKS
|
|
|
|
if (PMC_PROC_IS_USING_PMCS(td->td_proc))
|
|
|
|
PMC_SWITCH_CONTEXT(td, PMC_FN_CSW_IN);
|
|
|
|
#endif
|
2007-07-17 22:53:23 +00:00
|
|
|
} else
|
|
|
|
thread_unblock_switch(td, mtx);
|
2008-03-02 08:20:59 +00:00
|
|
|
/*
|
|
|
|
* Assert that all went well and return.
|
|
|
|
*/
|
2007-07-17 22:53:23 +00:00
|
|
|
TDQ_LOCK_ASSERT(tdq, MA_OWNED|MA_NOTRECURSED);
|
|
|
|
MPASS(td->td_lock == TDQ_LOCKPTR(tdq));
|
|
|
|
td->td_oncpu = cpuid;
|
2003-01-26 05:23:15 +00:00
|
|
|
}
|
|
|
|
|
2007-07-17 22:53:23 +00:00
|
|
|
/*
|
|
|
|
* Adjust thread priorities as a result of a nice request.
|
|
|
|
*/
|
2003-01-26 05:23:15 +00:00
|
|
|
void
|
2004-06-16 00:26:31 +00:00
|
|
|
sched_nice(struct proc *p, int nice)
|
2003-01-26 05:23:15 +00:00
|
|
|
{
|
|
|
|
struct thread *td;
|
|
|
|
|
2004-06-16 00:26:31 +00:00
|
|
|
PROC_LOCK_ASSERT(p, MA_OWNED);
|
2007-01-04 08:56:25 +00:00
|
|
|
|
2004-06-16 00:26:31 +00:00
|
|
|
p->p_nice = nice;
|
2006-10-26 21:42:22 +00:00
|
|
|
FOREACH_THREAD_IN_PROC(p, td) {
|
Commit 1/14 of sched_lock decomposition.
- Move all scheduler locking into the schedulers utilizing a technique
similar to solaris's container locking.
- A per-process spinlock is now used to protect the queue of threads,
thread count, suspension count, p_sflags, and other process
related scheduling fields.
- The new thread lock is actually a pointer to a spinlock for the
container that the thread is currently owned by. The container may
be a turnstile, sleepqueue, or run queue.
- thread_lock() is now used to protect access to thread related scheduling
fields. thread_unlock() unlocks the lock and thread_set_lock()
implements the transition from one lock to another.
- A new "blocked_lock" is used in cases where it is not safe to hold the
actual thread's lock yet we must prevent access to the thread.
- sched_throw() and sched_fork_exit() are introduced to allow the
schedulers to fix-up locking at these points.
- Add some minor infrastructure for optionally exporting scheduler
statistics that were invaluable in solving performance problems with
this patch. Generally these statistics allow you to differentiate
between different causes of context switches.
Tested by: kris, current@
Tested on: i386, amd64, ULE, 4BSD, libthr, libkse, PREEMPTION, etc.
Discussed with: kris, attilio, kmacy, jhb, julian, bde (small parts each)
2007-06-04 23:50:30 +00:00
|
|
|
thread_lock(td);
|
2006-10-26 21:42:22 +00:00
|
|
|
sched_priority(td);
|
2007-01-04 08:56:25 +00:00
|
|
|
sched_prio(td, td->td_base_user_pri);
|
Commit 1/14 of sched_lock decomposition.
- Move all scheduler locking into the schedulers utilizing a technique
similar to solaris's container locking.
- A per-process spinlock is now used to protect the queue of threads,
thread count, suspension count, p_sflags, and other process
related scheduling fields.
- The new thread lock is actually a pointer to a spinlock for the
container that the thread is currently owned by. The container may
be a turnstile, sleepqueue, or run queue.
- thread_lock() is now used to protect access to thread related scheduling
fields. thread_unlock() unlocks the lock and thread_set_lock()
implements the transition from one lock to another.
- A new "blocked_lock" is used in cases where it is not safe to hold the
actual thread's lock yet we must prevent access to the thread.
- sched_throw() and sched_fork_exit() are introduced to allow the
schedulers to fix-up locking at these points.
- Add some minor infrastructure for optionally exporting scheduler
statistics that were invaluable in solving performance problems with
this patch. Generally these statistics allow you to differentiate
between different causes of context switches.
Tested by: kris, current@
Tested on: i386, amd64, ULE, 4BSD, libthr, libkse, PREEMPTION, etc.
Discussed with: kris, attilio, kmacy, jhb, julian, bde (small parts each)
2007-06-04 23:50:30 +00:00
|
|
|
thread_unlock(td);
|
2004-06-16 00:26:31 +00:00
|
|
|
}
|
2003-01-26 05:23:15 +00:00
|
|
|
}
|
|
|
|
|
2007-07-17 22:53:23 +00:00
|
|
|
/*
|
|
|
|
* Record the sleep time for the interactivity scorer.
|
|
|
|
*/
|
2003-01-26 05:23:15 +00:00
|
|
|
void
|
2008-03-12 06:31:06 +00:00
|
|
|
sched_sleep(struct thread *td, int prio)
|
2003-01-26 05:23:15 +00:00
|
|
|
{
|
2007-01-04 08:56:25 +00:00
|
|
|
|
Commit 1/14 of sched_lock decomposition.
- Move all scheduler locking into the schedulers utilizing a technique
similar to solaris's container locking.
- A per-process spinlock is now used to protect the queue of threads,
thread count, suspension count, p_sflags, and other process
related scheduling fields.
- The new thread lock is actually a pointer to a spinlock for the
container that the thread is currently owned by. The container may
be a turnstile, sleepqueue, or run queue.
- thread_lock() is now used to protect access to thread related scheduling
fields. thread_unlock() unlocks the lock and thread_set_lock()
implements the transition from one lock to another.
- A new "blocked_lock" is used in cases where it is not safe to hold the
actual thread's lock yet we must prevent access to the thread.
- sched_throw() and sched_fork_exit() are introduced to allow the
schedulers to fix-up locking at these points.
- Add some minor infrastructure for optionally exporting scheduler
statistics that were invaluable in solving performance problems with
this patch. Generally these statistics allow you to differentiate
between different causes of context switches.
Tested by: kris, current@
Tested on: i386, amd64, ULE, 4BSD, libthr, libkse, PREEMPTION, etc.
Discussed with: kris, attilio, kmacy, jhb, julian, bde (small parts each)
2007-06-04 23:50:30 +00:00
|
|
|
THREAD_LOCK_ASSERT(td, MA_OWNED);
|
2003-01-26 05:23:15 +00:00
|
|
|
|
2007-09-21 04:10:23 +00:00
|
|
|
td->td_slptick = ticks;
|
2008-03-12 06:31:06 +00:00
|
|
|
if (TD_IS_SUSPENDED(td) || prio <= PSOCK)
|
|
|
|
td->td_flags |= TDF_CANSWAP;
|
2008-04-04 01:16:18 +00:00
|
|
|
if (static_boost == 1 && prio)
|
2008-03-12 06:31:06 +00:00
|
|
|
sched_prio(td, prio);
|
2008-04-04 01:16:18 +00:00
|
|
|
else if (static_boost && td->td_priority > static_boost)
|
|
|
|
sched_prio(td, static_boost);
|
2003-01-26 05:23:15 +00:00
|
|
|
}
|
|
|
|
|
2007-07-17 22:53:23 +00:00
|
|
|
/*
|
|
|
|
* Schedule a thread to resume execution and record how long it voluntarily
|
|
|
|
* slept. We also update the pctcpu, interactivity, and priority.
|
|
|
|
*/
|
2003-01-26 05:23:15 +00:00
|
|
|
void
|
|
|
|
sched_wakeup(struct thread *td)
|
|
|
|
{
|
2007-01-25 19:14:11 +00:00
|
|
|
struct td_sched *ts;
|
2007-07-17 22:53:23 +00:00
|
|
|
int slptick;
|
2007-01-04 08:56:25 +00:00
|
|
|
|
Commit 1/14 of sched_lock decomposition.
- Move all scheduler locking into the schedulers utilizing a technique
similar to solaris's container locking.
- A per-process spinlock is now used to protect the queue of threads,
thread count, suspension count, p_sflags, and other process
related scheduling fields.
- The new thread lock is actually a pointer to a spinlock for the
container that the thread is currently owned by. The container may
be a turnstile, sleepqueue, or run queue.
- thread_lock() is now used to protect access to thread related scheduling
fields. thread_unlock() unlocks the lock and thread_set_lock()
implements the transition from one lock to another.
- A new "blocked_lock" is used in cases where it is not safe to hold the
actual thread's lock yet we must prevent access to the thread.
- sched_throw() and sched_fork_exit() are introduced to allow the
schedulers to fix-up locking at these points.
- Add some minor infrastructure for optionally exporting scheduler
statistics that were invaluable in solving performance problems with
this patch. Generally these statistics allow you to differentiate
between different causes of context switches.
Tested by: kris, current@
Tested on: i386, amd64, ULE, 4BSD, libthr, libkse, PREEMPTION, etc.
Discussed with: kris, attilio, kmacy, jhb, julian, bde (small parts each)
2007-06-04 23:50:30 +00:00
|
|
|
THREAD_LOCK_ASSERT(td, MA_OWNED);
|
2007-01-25 19:14:11 +00:00
|
|
|
ts = td->td_sched;
|
2008-03-12 06:31:06 +00:00
|
|
|
td->td_flags &= ~TDF_CANSWAP;
|
2003-01-26 05:23:15 +00:00
|
|
|
/*
|
2007-01-04 08:56:25 +00:00
|
|
|
* If we slept for more than a tick update our interactivity and
|
|
|
|
* priority.
|
2003-01-26 05:23:15 +00:00
|
|
|
*/
|
2007-09-21 04:10:23 +00:00
|
|
|
slptick = td->td_slptick;
|
|
|
|
td->td_slptick = 0;
|
2007-07-17 22:53:23 +00:00
|
|
|
if (slptick && slptick != ticks) {
|
2007-01-24 18:18:43 +00:00
|
|
|
u_int hzticks;
|
2003-03-03 04:11:40 +00:00
|
|
|
|
2007-07-17 22:53:23 +00:00
|
|
|
hzticks = (ticks - slptick) << SCHED_TICK_SHIFT;
|
|
|
|
ts->ts_slptime += hzticks;
|
2007-01-05 23:45:38 +00:00
|
|
|
sched_interact_update(td);
|
2007-01-25 19:14:11 +00:00
|
|
|
sched_pctcpu_update(ts);
|
2003-01-26 05:23:15 +00:00
|
|
|
}
|
2007-01-25 19:14:11 +00:00
|
|
|
/* Reset the slice value after we sleep. */
|
|
|
|
ts->ts_slice = sched_slice;
|
2007-01-23 08:50:34 +00:00
|
|
|
sched_add(td, SRQ_BORING);
|
2003-01-26 05:23:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Penalize the parent for creating a new child and initialize the child's
|
|
|
|
* priority.
|
|
|
|
*/
|
|
|
|
void
|
2006-10-26 21:42:22 +00:00
|
|
|
sched_fork(struct thread *td, struct thread *child)
|
2003-01-26 05:23:15 +00:00
|
|
|
{
|
Commit 1/14 of sched_lock decomposition.
- Move all scheduler locking into the schedulers utilizing a technique
similar to solaris's container locking.
- A per-process spinlock is now used to protect the queue of threads,
thread count, suspension count, p_sflags, and other process
related scheduling fields.
- The new thread lock is actually a pointer to a spinlock for the
container that the thread is currently owned by. The container may
be a turnstile, sleepqueue, or run queue.
- thread_lock() is now used to protect access to thread related scheduling
fields. thread_unlock() unlocks the lock and thread_set_lock()
implements the transition from one lock to another.
- A new "blocked_lock" is used in cases where it is not safe to hold the
actual thread's lock yet we must prevent access to the thread.
- sched_throw() and sched_fork_exit() are introduced to allow the
schedulers to fix-up locking at these points.
- Add some minor infrastructure for optionally exporting scheduler
statistics that were invaluable in solving performance problems with
this patch. Generally these statistics allow you to differentiate
between different causes of context switches.
Tested by: kris, current@
Tested on: i386, amd64, ULE, 4BSD, libthr, libkse, PREEMPTION, etc.
Discussed with: kris, attilio, kmacy, jhb, julian, bde (small parts each)
2007-06-04 23:50:30 +00:00
|
|
|
THREAD_LOCK_ASSERT(td, MA_OWNED);
|
2006-12-06 06:34:57 +00:00
|
|
|
sched_fork_thread(td, child);
|
2007-01-04 08:56:25 +00:00
|
|
|
/*
|
|
|
|
* Penalize the parent and child for forking.
|
|
|
|
*/
|
|
|
|
sched_interact_fork(child);
|
|
|
|
sched_priority(child);
|
2007-07-17 22:53:23 +00:00
|
|
|
td->td_sched->ts_runtime += tickincr;
|
2007-01-04 08:56:25 +00:00
|
|
|
sched_interact_update(td);
|
|
|
|
sched_priority(td);
|
2006-12-06 06:34:57 +00:00
|
|
|
}
|
|
|
|
|
2007-07-17 22:53:23 +00:00
|
|
|
/*
|
|
|
|
* Fork a new thread, may be within the same process.
|
|
|
|
*/
|
2006-12-06 06:34:57 +00:00
|
|
|
void
|
|
|
|
sched_fork_thread(struct thread *td, struct thread *child)
|
|
|
|
{
|
|
|
|
struct td_sched *ts;
|
|
|
|
struct td_sched *ts2;
|
2003-01-26 05:23:15 +00:00
|
|
|
|
2008-03-20 03:06:33 +00:00
|
|
|
THREAD_LOCK_ASSERT(td, MA_OWNED);
|
2007-01-04 08:56:25 +00:00
|
|
|
/*
|
|
|
|
* Initialize child.
|
|
|
|
*/
|
2006-12-06 06:34:57 +00:00
|
|
|
ts = td->td_sched;
|
|
|
|
ts2 = child->td_sched;
|
2008-03-20 03:06:33 +00:00
|
|
|
child->td_lock = TDQ_LOCKPTR(TDQ_SELF());
|
|
|
|
child->td_cpuset = cpuset_ref(td->td_cpuset);
|
2006-12-06 06:34:57 +00:00
|
|
|
ts2->ts_cpu = ts->ts_cpu;
|
2008-03-20 03:06:33 +00:00
|
|
|
ts2->ts_flags = 0;
|
2007-01-04 08:56:25 +00:00
|
|
|
/*
|
|
|
|
* Grab our parents cpu estimation information and priority.
|
|
|
|
*/
|
2006-12-06 06:34:57 +00:00
|
|
|
ts2->ts_ticks = ts->ts_ticks;
|
|
|
|
ts2->ts_ltick = ts->ts_ltick;
|
|
|
|
ts2->ts_ftick = ts->ts_ftick;
|
2007-01-04 08:56:25 +00:00
|
|
|
child->td_user_pri = td->td_user_pri;
|
|
|
|
child->td_base_user_pri = td->td_base_user_pri;
|
|
|
|
/*
|
|
|
|
* And update interactivity score.
|
|
|
|
*/
|
2007-07-17 22:53:23 +00:00
|
|
|
ts2->ts_slptime = ts->ts_slptime;
|
|
|
|
ts2->ts_runtime = ts->ts_runtime;
|
2007-01-04 08:56:25 +00:00
|
|
|
ts2->ts_slice = 1; /* Attempt to quickly learn interactivity. */
|
2003-04-11 03:47:14 +00:00
|
|
|
}
|
|
|
|
|
2007-07-17 22:53:23 +00:00
|
|
|
/*
|
|
|
|
* Adjust the priority class of a thread.
|
|
|
|
*/
|
2003-04-11 03:47:14 +00:00
|
|
|
void
|
2006-10-26 21:42:22 +00:00
|
|
|
sched_class(struct thread *td, int class)
|
2003-04-11 03:47:14 +00:00
|
|
|
{
|
|
|
|
|
Commit 1/14 of sched_lock decomposition.
- Move all scheduler locking into the schedulers utilizing a technique
similar to solaris's container locking.
- A per-process spinlock is now used to protect the queue of threads,
thread count, suspension count, p_sflags, and other process
related scheduling fields.
- The new thread lock is actually a pointer to a spinlock for the
container that the thread is currently owned by. The container may
be a turnstile, sleepqueue, or run queue.
- thread_lock() is now used to protect access to thread related scheduling
fields. thread_unlock() unlocks the lock and thread_set_lock()
implements the transition from one lock to another.
- A new "blocked_lock" is used in cases where it is not safe to hold the
actual thread's lock yet we must prevent access to the thread.
- sched_throw() and sched_fork_exit() are introduced to allow the
schedulers to fix-up locking at these points.
- Add some minor infrastructure for optionally exporting scheduler
statistics that were invaluable in solving performance problems with
this patch. Generally these statistics allow you to differentiate
between different causes of context switches.
Tested by: kris, current@
Tested on: i386, amd64, ULE, 4BSD, libthr, libkse, PREEMPTION, etc.
Discussed with: kris, attilio, kmacy, jhb, julian, bde (small parts each)
2007-06-04 23:50:30 +00:00
|
|
|
THREAD_LOCK_ASSERT(td, MA_OWNED);
|
2006-10-26 21:42:22 +00:00
|
|
|
if (td->td_pri_class == class)
|
2003-04-11 03:47:14 +00:00
|
|
|
return;
|
2006-10-26 21:42:22 +00:00
|
|
|
td->td_pri_class = class;
|
2003-01-26 05:23:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return some of the child's priority and interactivity to the parent.
|
|
|
|
*/
|
|
|
|
void
|
2006-12-06 06:55:59 +00:00
|
|
|
sched_exit(struct proc *p, struct thread *child)
|
2003-01-26 05:23:15 +00:00
|
|
|
{
|
2007-01-04 08:56:25 +00:00
|
|
|
struct thread *td;
|
2006-12-06 06:55:59 +00:00
|
|
|
|
2006-10-26 21:42:22 +00:00
|
|
|
CTR3(KTR_SCHED, "sched_exit: %p(%s) prio %d",
|
2007-11-14 06:21:24 +00:00
|
|
|
child, child->td_name, child->td_priority);
|
2006-10-26 21:42:22 +00:00
|
|
|
|
2008-03-19 06:19:01 +00:00
|
|
|
PROC_LOCK_ASSERT(p, MA_OWNED);
|
2007-01-04 08:56:25 +00:00
|
|
|
td = FIRST_THREAD_IN_PROC(p);
|
|
|
|
sched_exit_thread(td, child);
|
2006-12-06 06:34:57 +00:00
|
|
|
}
|
|
|
|
|
2007-07-17 22:53:23 +00:00
|
|
|
/*
|
|
|
|
* Penalize another thread for the time spent on this one. This helps to
|
|
|
|
* worsen the priority and interactivity of processes which schedule batch
|
|
|
|
* jobs such as make. This has little effect on the make process itself but
|
|
|
|
* causes new processes spawned by it to receive worse scores immediately.
|
|
|
|
*/
|
2006-12-06 06:34:57 +00:00
|
|
|
void
|
2006-12-06 06:55:59 +00:00
|
|
|
sched_exit_thread(struct thread *td, struct thread *child)
|
2006-12-06 06:34:57 +00:00
|
|
|
{
|
2007-01-04 08:56:25 +00:00
|
|
|
|
2006-12-06 06:55:59 +00:00
|
|
|
CTR3(KTR_SCHED, "sched_exit_thread: %p(%s) prio %d",
|
2007-11-14 06:21:24 +00:00
|
|
|
child, child->td_name, child->td_priority);
|
2006-12-06 06:55:59 +00:00
|
|
|
|
2007-01-04 08:56:25 +00:00
|
|
|
/*
|
|
|
|
* Give the child's runtime to the parent without returning the
|
|
|
|
* sleep time as a penalty to the parent. This causes shells that
|
|
|
|
* launch expensive things to mark their children as expensive.
|
|
|
|
*/
|
Commit 1/14 of sched_lock decomposition.
- Move all scheduler locking into the schedulers utilizing a technique
similar to solaris's container locking.
- A per-process spinlock is now used to protect the queue of threads,
thread count, suspension count, p_sflags, and other process
related scheduling fields.
- The new thread lock is actually a pointer to a spinlock for the
container that the thread is currently owned by. The container may
be a turnstile, sleepqueue, or run queue.
- thread_lock() is now used to protect access to thread related scheduling
fields. thread_unlock() unlocks the lock and thread_set_lock()
implements the transition from one lock to another.
- A new "blocked_lock" is used in cases where it is not safe to hold the
actual thread's lock yet we must prevent access to the thread.
- sched_throw() and sched_fork_exit() are introduced to allow the
schedulers to fix-up locking at these points.
- Add some minor infrastructure for optionally exporting scheduler
statistics that were invaluable in solving performance problems with
this patch. Generally these statistics allow you to differentiate
between different causes of context switches.
Tested by: kris, current@
Tested on: i386, amd64, ULE, 4BSD, libthr, libkse, PREEMPTION, etc.
Discussed with: kris, attilio, kmacy, jhb, julian, bde (small parts each)
2007-06-04 23:50:30 +00:00
|
|
|
thread_lock(td);
|
2007-07-17 22:53:23 +00:00
|
|
|
td->td_sched->ts_runtime += child->td_sched->ts_runtime;
|
2006-12-06 06:55:59 +00:00
|
|
|
sched_interact_update(td);
|
2007-01-04 08:56:25 +00:00
|
|
|
sched_priority(td);
|
Commit 1/14 of sched_lock decomposition.
- Move all scheduler locking into the schedulers utilizing a technique
similar to solaris's container locking.
- A per-process spinlock is now used to protect the queue of threads,
thread count, suspension count, p_sflags, and other process
related scheduling fields.
- The new thread lock is actually a pointer to a spinlock for the
container that the thread is currently owned by. The container may
be a turnstile, sleepqueue, or run queue.
- thread_lock() is now used to protect access to thread related scheduling
fields. thread_unlock() unlocks the lock and thread_set_lock()
implements the transition from one lock to another.
- A new "blocked_lock" is used in cases where it is not safe to hold the
actual thread's lock yet we must prevent access to the thread.
- sched_throw() and sched_fork_exit() are introduced to allow the
schedulers to fix-up locking at these points.
- Add some minor infrastructure for optionally exporting scheduler
statistics that were invaluable in solving performance problems with
this patch. Generally these statistics allow you to differentiate
between different causes of context switches.
Tested by: kris, current@
Tested on: i386, amd64, ULE, 4BSD, libthr, libkse, PREEMPTION, etc.
Discussed with: kris, attilio, kmacy, jhb, julian, bde (small parts each)
2007-06-04 23:50:30 +00:00
|
|
|
thread_unlock(td);
|
2006-12-06 06:34:57 +00:00
|
|
|
}
|
|
|
|
|
2008-03-10 01:32:01 +00:00
|
|
|
void
|
|
|
|
sched_preempt(struct thread *td)
|
|
|
|
{
|
|
|
|
struct tdq *tdq;
|
|
|
|
|
|
|
|
thread_lock(td);
|
|
|
|
tdq = TDQ_SELF();
|
|
|
|
TDQ_LOCK_ASSERT(tdq, MA_OWNED);
|
|
|
|
tdq->tdq_ipipending = 0;
|
|
|
|
if (td->td_priority > tdq->tdq_lowpri) {
|
2008-04-17 04:20:10 +00:00
|
|
|
int flags;
|
|
|
|
|
|
|
|
flags = SW_INVOL | SW_PREEMPT;
|
2008-03-10 01:32:01 +00:00
|
|
|
if (td->td_critnest > 1)
|
|
|
|
td->td_owepreempt = 1;
|
2008-04-17 04:20:10 +00:00
|
|
|
else if (TD_IS_IDLETHREAD(td))
|
|
|
|
mi_switch(flags | SWT_REMOTEWAKEIDLE, NULL);
|
2008-03-10 01:32:01 +00:00
|
|
|
else
|
2008-04-17 04:20:10 +00:00
|
|
|
mi_switch(flags | SWT_REMOTEPREEMPT, NULL);
|
2008-03-10 01:32:01 +00:00
|
|
|
}
|
|
|
|
thread_unlock(td);
|
|
|
|
}
|
|
|
|
|
2007-07-17 22:53:23 +00:00
|
|
|
/*
|
|
|
|
* Fix priorities on return to user-space. Priorities may be elevated due
|
|
|
|
* to static priorities in msleep() or similar.
|
|
|
|
*/
|
2006-12-06 06:34:57 +00:00
|
|
|
void
|
|
|
|
sched_userret(struct thread *td)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* XXX we cheat slightly on the locking here to avoid locking in
|
|
|
|
* the usual case. Setting td_priority here is essentially an
|
|
|
|
* incomplete workaround for not setting it properly elsewhere.
|
|
|
|
* Now that some interrupt handlers are threads, not setting it
|
|
|
|
* properly elsewhere can clobber it in the window between setting
|
|
|
|
* it here and returning to user mode, so don't waste time setting
|
|
|
|
* it perfectly here.
|
|
|
|
*/
|
|
|
|
KASSERT((td->td_flags & TDF_BORROWING) == 0,
|
|
|
|
("thread with borrowed priority returning to userland"));
|
|
|
|
if (td->td_priority != td->td_user_pri) {
|
Commit 1/14 of sched_lock decomposition.
- Move all scheduler locking into the schedulers utilizing a technique
similar to solaris's container locking.
- A per-process spinlock is now used to protect the queue of threads,
thread count, suspension count, p_sflags, and other process
related scheduling fields.
- The new thread lock is actually a pointer to a spinlock for the
container that the thread is currently owned by. The container may
be a turnstile, sleepqueue, or run queue.
- thread_lock() is now used to protect access to thread related scheduling
fields. thread_unlock() unlocks the lock and thread_set_lock()
implements the transition from one lock to another.
- A new "blocked_lock" is used in cases where it is not safe to hold the
actual thread's lock yet we must prevent access to the thread.
- sched_throw() and sched_fork_exit() are introduced to allow the
schedulers to fix-up locking at these points.
- Add some minor infrastructure for optionally exporting scheduler
statistics that were invaluable in solving performance problems with
this patch. Generally these statistics allow you to differentiate
between different causes of context switches.
Tested by: kris, current@
Tested on: i386, amd64, ULE, 4BSD, libthr, libkse, PREEMPTION, etc.
Discussed with: kris, attilio, kmacy, jhb, julian, bde (small parts each)
2007-06-04 23:50:30 +00:00
|
|
|
thread_lock(td);
|
2006-12-06 06:34:57 +00:00
|
|
|
td->td_priority = td->td_user_pri;
|
|
|
|
td->td_base_pri = td->td_user_pri;
|
2008-03-10 01:32:01 +00:00
|
|
|
tdq_setlowpri(TDQ_SELF(), td);
|
Commit 1/14 of sched_lock decomposition.
- Move all scheduler locking into the schedulers utilizing a technique
similar to solaris's container locking.
- A per-process spinlock is now used to protect the queue of threads,
thread count, suspension count, p_sflags, and other process
related scheduling fields.
- The new thread lock is actually a pointer to a spinlock for the
container that the thread is currently owned by. The container may
be a turnstile, sleepqueue, or run queue.
- thread_lock() is now used to protect access to thread related scheduling
fields. thread_unlock() unlocks the lock and thread_set_lock()
implements the transition from one lock to another.
- A new "blocked_lock" is used in cases where it is not safe to hold the
actual thread's lock yet we must prevent access to the thread.
- sched_throw() and sched_fork_exit() are introduced to allow the
schedulers to fix-up locking at these points.
- Add some minor infrastructure for optionally exporting scheduler
statistics that were invaluable in solving performance problems with
this patch. Generally these statistics allow you to differentiate
between different causes of context switches.
Tested by: kris, current@
Tested on: i386, amd64, ULE, 4BSD, libthr, libkse, PREEMPTION, etc.
Discussed with: kris, attilio, kmacy, jhb, julian, bde (small parts each)
2007-06-04 23:50:30 +00:00
|
|
|
thread_unlock(td);
|
2006-12-06 06:34:57 +00:00
|
|
|
}
|
2003-01-26 05:23:15 +00:00
|
|
|
}
|
|
|
|
|
2007-07-17 22:53:23 +00:00
|
|
|
/*
|
|
|
|
* Handle a stathz tick. This is really only relevant for timeshare
|
|
|
|
* threads.
|
|
|
|
*/
|
2003-01-26 05:23:15 +00:00
|
|
|
void
|
2003-10-16 08:39:15 +00:00
|
|
|
sched_clock(struct thread *td)
|
2003-01-26 05:23:15 +00:00
|
|
|
{
|
2006-12-06 06:34:57 +00:00
|
|
|
struct tdq *tdq;
|
|
|
|
struct td_sched *ts;
|
2003-01-26 05:23:15 +00:00
|
|
|
|
2007-07-17 22:53:23 +00:00
|
|
|
THREAD_LOCK_ASSERT(td, MA_OWNED);
|
2007-01-04 12:16:19 +00:00
|
|
|
tdq = TDQ_SELF();
|
2007-10-02 00:36:06 +00:00
|
|
|
#ifdef SMP
|
|
|
|
/*
|
|
|
|
* We run the long term load balancer infrequently on the first cpu.
|
|
|
|
*/
|
|
|
|
if (balance_tdq == tdq) {
|
|
|
|
if (balance_ticks && --balance_ticks == 0)
|
|
|
|
sched_balance();
|
|
|
|
}
|
|
|
|
#endif
|
2008-04-17 09:56:01 +00:00
|
|
|
/*
|
|
|
|
* Save the old switch count so we have a record of the last ticks
|
|
|
|
* activity. Initialize the new switch count based on our load.
|
|
|
|
* If there is some activity seed it to reflect that.
|
|
|
|
*/
|
|
|
|
tdq->tdq_oldswitchcnt = tdq->tdq_switchcnt;
|
2008-04-25 05:18:50 +00:00
|
|
|
tdq->tdq_switchcnt = tdq->tdq_load;
|
2004-08-10 07:52:21 +00:00
|
|
|
/*
|
2007-01-04 12:16:19 +00:00
|
|
|
* Advance the insert index once for each tick to ensure that all
|
|
|
|
* threads get a chance to run.
|
2004-08-10 07:52:21 +00:00
|
|
|
*/
|
2007-01-04 12:16:19 +00:00
|
|
|
if (tdq->tdq_idx == tdq->tdq_ridx) {
|
|
|
|
tdq->tdq_idx = (tdq->tdq_idx + 1) % RQ_NQS;
|
|
|
|
if (TAILQ_EMPTY(&tdq->tdq_timeshare.rq_queues[tdq->tdq_ridx]))
|
|
|
|
tdq->tdq_ridx = tdq->tdq_idx;
|
|
|
|
}
|
|
|
|
ts = td->td_sched;
|
2008-01-05 04:47:31 +00:00
|
|
|
if (td->td_pri_class & PRI_FIFO_BIT)
|
2003-10-27 06:47:05 +00:00
|
|
|
return;
|
2008-01-05 04:47:31 +00:00
|
|
|
if (td->td_pri_class == PRI_TIMESHARE) {
|
|
|
|
/*
|
|
|
|
* We used a tick; charge it to the thread so
|
|
|
|
* that we can compute our interactivity.
|
|
|
|
*/
|
|
|
|
td->td_sched->ts_runtime += tickincr;
|
|
|
|
sched_interact_update(td);
|
2008-03-10 03:15:19 +00:00
|
|
|
sched_priority(td);
|
2008-01-05 04:47:31 +00:00
|
|
|
}
|
2003-01-26 05:23:15 +00:00
|
|
|
/*
|
|
|
|
* We used up one time slice.
|
|
|
|
*/
|
2006-12-06 06:34:57 +00:00
|
|
|
if (--ts->ts_slice > 0)
|
2003-04-11 03:47:14 +00:00
|
|
|
return;
|
2003-01-26 05:23:15 +00:00
|
|
|
/*
|
2008-03-10 03:15:19 +00:00
|
|
|
* We're out of time, force a requeue at userret().
|
2003-01-26 05:23:15 +00:00
|
|
|
*/
|
2008-03-10 03:15:19 +00:00
|
|
|
ts->ts_slice = sched_slice;
|
2003-04-11 03:47:14 +00:00
|
|
|
td->td_flags |= TDF_NEEDRESCHED;
|
2003-01-26 05:23:15 +00:00
|
|
|
}
|
|
|
|
|
2007-07-17 22:53:23 +00:00
|
|
|
/*
|
|
|
|
* Called once per hz tick. Used for cpu utilization information. This
|
|
|
|
* is easier than trying to scale based on stathz.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
sched_tick(void)
|
|
|
|
{
|
|
|
|
struct td_sched *ts;
|
|
|
|
|
|
|
|
ts = curthread->td_sched;
|
2008-07-19 05:13:47 +00:00
|
|
|
/*
|
|
|
|
* Ticks is updated asynchronously on a single cpu. Check here to
|
|
|
|
* avoid incrementing ts_ticks multiple times in a single tick.
|
|
|
|
*/
|
|
|
|
if (ts->ts_ltick == ticks)
|
|
|
|
return;
|
2007-07-17 22:53:23 +00:00
|
|
|
/* Adjust ticks for pctcpu */
|
|
|
|
ts->ts_ticks += 1 << SCHED_TICK_SHIFT;
|
|
|
|
ts->ts_ltick = ticks;
|
|
|
|
/*
|
|
|
|
* Update if we've exceeded our desired tick threshhold by over one
|
|
|
|
* second.
|
|
|
|
*/
|
|
|
|
if (ts->ts_ftick + SCHED_TICK_MAX < ts->ts_ltick)
|
|
|
|
sched_pctcpu_update(ts);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return whether the current CPU has runnable tasks. Used for in-kernel
|
|
|
|
* cooperative idle threads.
|
|
|
|
*/
|
2003-01-26 05:23:15 +00:00
|
|
|
int
|
|
|
|
sched_runnable(void)
|
|
|
|
{
|
2006-12-06 06:34:57 +00:00
|
|
|
struct tdq *tdq;
|
2003-06-08 00:47:33 +00:00
|
|
|
int load;
|
2003-01-26 05:23:15 +00:00
|
|
|
|
2003-06-08 00:47:33 +00:00
|
|
|
load = 1;
|
|
|
|
|
2006-12-06 06:34:57 +00:00
|
|
|
tdq = TDQ_SELF();
|
2003-10-27 06:47:05 +00:00
|
|
|
if ((curthread->td_flags & TDF_IDLETD) != 0) {
|
2006-12-29 10:37:07 +00:00
|
|
|
if (tdq->tdq_load > 0)
|
2003-10-27 06:47:05 +00:00
|
|
|
goto out;
|
|
|
|
} else
|
2006-12-29 10:37:07 +00:00
|
|
|
if (tdq->tdq_load - 1 > 0)
|
2003-10-27 06:47:05 +00:00
|
|
|
goto out;
|
2003-06-08 00:47:33 +00:00
|
|
|
load = 0;
|
|
|
|
out:
|
|
|
|
return (load);
|
2003-01-26 05:23:15 +00:00
|
|
|
}
|
|
|
|
|
2007-07-17 22:53:23 +00:00
|
|
|
/*
|
|
|
|
* Choose the highest priority thread to run. The thread is removed from
|
|
|
|
* the run-queue while running however the load remains. For SMP we set
|
|
|
|
* the tdq in the global idle bitmask if it idles here.
|
|
|
|
*/
|
2007-01-23 08:50:34 +00:00
|
|
|
struct thread *
|
2003-01-28 09:28:20 +00:00
|
|
|
sched_choose(void)
|
|
|
|
{
|
2008-03-20 05:51:16 +00:00
|
|
|
struct thread *td;
|
2007-07-17 22:53:23 +00:00
|
|
|
struct tdq *tdq;
|
2003-01-28 09:28:20 +00:00
|
|
|
|
2006-12-06 06:34:57 +00:00
|
|
|
tdq = TDQ_SELF();
|
2007-07-17 22:53:23 +00:00
|
|
|
TDQ_LOCK_ASSERT(tdq, MA_OWNED);
|
2008-03-20 05:51:16 +00:00
|
|
|
td = tdq_choose(tdq);
|
|
|
|
if (td) {
|
|
|
|
td->td_sched->ts_ltick = ticks;
|
|
|
|
tdq_runq_rem(tdq, td);
|
2008-04-04 01:16:18 +00:00
|
|
|
tdq->tdq_lowpri = td->td_priority;
|
2008-03-20 05:51:16 +00:00
|
|
|
return (td);
|
2003-01-26 05:23:15 +00:00
|
|
|
}
|
2008-04-04 01:16:18 +00:00
|
|
|
tdq->tdq_lowpri = PRI_MAX_IDLE;
|
2008-03-02 08:20:59 +00:00
|
|
|
return (PCPU_GET(idlethread));
|
2007-01-23 08:50:34 +00:00
|
|
|
}
|
|
|
|
|
2007-07-17 22:53:23 +00:00
|
|
|
/*
|
|
|
|
* Set owepreempt if necessary. Preemption never happens directly in ULE,
|
|
|
|
* we always request it once we exit a critical section.
|
|
|
|
*/
|
|
|
|
static inline void
|
|
|
|
sched_setpreempt(struct thread *td)
|
2007-01-23 08:50:34 +00:00
|
|
|
{
|
|
|
|
struct thread *ctd;
|
|
|
|
int cpri;
|
|
|
|
int pri;
|
|
|
|
|
2008-03-10 01:32:01 +00:00
|
|
|
THREAD_LOCK_ASSERT(curthread, MA_OWNED);
|
|
|
|
|
2007-01-23 08:50:34 +00:00
|
|
|
ctd = curthread;
|
|
|
|
pri = td->td_priority;
|
|
|
|
cpri = ctd->td_priority;
|
2008-03-10 01:32:01 +00:00
|
|
|
if (pri < cpri)
|
|
|
|
ctd->td_flags |= TDF_NEEDRESCHED;
|
2007-01-23 08:50:34 +00:00
|
|
|
if (panicstr != NULL || pri >= cpri || cold || TD_IS_INHIBITED(ctd))
|
2007-07-17 22:53:23 +00:00
|
|
|
return;
|
2008-03-10 01:32:01 +00:00
|
|
|
if (!sched_shouldpreempt(pri, cpri, 0))
|
2007-07-17 22:53:23 +00:00
|
|
|
return;
|
|
|
|
ctd->td_owepreempt = 1;
|
2003-01-26 05:23:15 +00:00
|
|
|
}
|
|
|
|
|
2007-07-17 22:53:23 +00:00
|
|
|
/*
|
2008-03-10 03:15:19 +00:00
|
|
|
* Add a thread to a thread queue. Select the appropriate runq and add the
|
|
|
|
* thread to it. This is the internal function called when the tdq is
|
|
|
|
* predetermined.
|
2007-07-17 22:53:23 +00:00
|
|
|
*/
|
2003-01-26 05:23:15 +00:00
|
|
|
void
|
2007-07-17 22:53:23 +00:00
|
|
|
tdq_add(struct tdq *tdq, struct thread *td, int flags)
|
2003-01-26 05:23:15 +00:00
|
|
|
{
|
|
|
|
|
2007-07-17 22:53:23 +00:00
|
|
|
TDQ_LOCK_ASSERT(tdq, MA_OWNED);
|
2007-01-23 08:50:34 +00:00
|
|
|
KASSERT((td->td_inhibitors == 0),
|
|
|
|
("sched_add: trying to run inhibited thread"));
|
|
|
|
KASSERT((TD_CAN_RUN(td) || TD_IS_RUNNING(td)),
|
|
|
|
("sched_add: bad thread state"));
|
2007-09-17 05:31:39 +00:00
|
|
|
KASSERT(td->td_flags & TDF_INMEM,
|
|
|
|
("sched_add: thread swapped out"));
|
2007-07-17 22:53:23 +00:00
|
|
|
|
|
|
|
if (td->td_priority < tdq->tdq_lowpri)
|
|
|
|
tdq->tdq_lowpri = td->td_priority;
|
2008-03-20 05:51:16 +00:00
|
|
|
tdq_runq_add(tdq, td, flags);
|
|
|
|
tdq_load_add(tdq, td);
|
2007-07-17 22:53:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Select the target thread queue and add a thread to it. Request
|
|
|
|
* preemption or IPI a remote processor if required.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
sched_add(struct thread *td, int flags)
|
|
|
|
{
|
|
|
|
struct tdq *tdq;
|
2007-01-19 21:56:08 +00:00
|
|
|
#ifdef SMP
|
2007-07-17 22:53:23 +00:00
|
|
|
int cpu;
|
|
|
|
#endif
|
|
|
|
CTR5(KTR_SCHED, "sched_add: %p(%s) prio %d by %p(%s)",
|
2007-11-14 06:21:24 +00:00
|
|
|
td, td->td_name, td->td_priority, curthread,
|
|
|
|
curthread->td_name);
|
2007-07-17 22:53:23 +00:00
|
|
|
THREAD_LOCK_ASSERT(td, MA_OWNED);
|
|
|
|
/*
|
|
|
|
* Recalculate the priority before we select the target cpu or
|
|
|
|
* run-queue.
|
|
|
|
*/
|
|
|
|
if (PRI_BASE(td->td_pri_class) == PRI_TIMESHARE)
|
|
|
|
sched_priority(td);
|
|
|
|
#ifdef SMP
|
|
|
|
/*
|
|
|
|
* Pick the destination cpu and if it isn't ours transfer to the
|
|
|
|
* target cpu.
|
|
|
|
*/
|
2008-03-20 05:51:16 +00:00
|
|
|
cpu = sched_pickcpu(td, flags);
|
|
|
|
tdq = sched_setcpu(td, cpu, flags);
|
2007-07-17 22:53:23 +00:00
|
|
|
tdq_add(tdq, td, flags);
|
2008-03-10 03:15:19 +00:00
|
|
|
if (cpu != PCPU_GET(cpuid)) {
|
2008-03-20 05:51:16 +00:00
|
|
|
tdq_notify(tdq, td);
|
2007-01-19 21:56:08 +00:00
|
|
|
return;
|
|
|
|
}
|
2007-07-17 22:53:23 +00:00
|
|
|
#else
|
|
|
|
tdq = TDQ_SELF();
|
|
|
|
TDQ_LOCK(tdq);
|
|
|
|
/*
|
|
|
|
* Now that the thread is moving to the run-queue, set the lock
|
|
|
|
* to the scheduler's lock.
|
|
|
|
*/
|
|
|
|
thread_lock_set(td, TDQ_LOCKPTR(tdq));
|
|
|
|
tdq_add(tdq, td, flags);
|
2007-01-19 21:56:08 +00:00
|
|
|
#endif
|
2007-07-17 22:53:23 +00:00
|
|
|
if (!(flags & SRQ_YIELDING))
|
|
|
|
sched_setpreempt(td);
|
2003-01-26 05:23:15 +00:00
|
|
|
}
|
|
|
|
|
2007-07-17 22:53:23 +00:00
|
|
|
/*
|
|
|
|
* Remove a thread from a run-queue without running it. This is used
|
|
|
|
* when we're stealing a thread from a remote queue. Otherwise all threads
|
|
|
|
* exit by calling sched_exit_thread() and sched_throw() themselves.
|
|
|
|
*/
|
2003-01-26 05:23:15 +00:00
|
|
|
void
|
2003-10-16 08:39:15 +00:00
|
|
|
sched_rem(struct thread *td)
|
2003-01-26 05:23:15 +00:00
|
|
|
{
|
2006-12-06 06:34:57 +00:00
|
|
|
struct tdq *tdq;
|
2003-10-16 08:39:15 +00:00
|
|
|
|
2004-12-26 00:15:33 +00:00
|
|
|
CTR5(KTR_SCHED, "sched_rem: %p(%s) prio %d by %p(%s)",
|
2007-11-14 06:21:24 +00:00
|
|
|
td, td->td_name, td->td_priority, curthread,
|
|
|
|
curthread->td_name);
|
2008-03-20 05:51:16 +00:00
|
|
|
tdq = TDQ_CPU(td->td_sched->ts_cpu);
|
2007-07-17 22:53:23 +00:00
|
|
|
TDQ_LOCK_ASSERT(tdq, MA_OWNED);
|
|
|
|
MPASS(td->td_lock == TDQ_LOCKPTR(tdq));
|
2007-01-23 08:50:34 +00:00
|
|
|
KASSERT(TD_ON_RUNQ(td),
|
2006-12-06 06:34:57 +00:00
|
|
|
("sched_rem: thread not on run queue"));
|
2008-03-20 05:51:16 +00:00
|
|
|
tdq_runq_rem(tdq, td);
|
|
|
|
tdq_load_rem(tdq, td);
|
2007-01-23 08:50:34 +00:00
|
|
|
TD_SET_CAN_RUN(td);
|
2008-03-02 08:20:59 +00:00
|
|
|
if (td->td_priority == tdq->tdq_lowpri)
|
|
|
|
tdq_setlowpri(tdq, NULL);
|
2003-01-26 05:23:15 +00:00
|
|
|
}
|
|
|
|
|
2007-07-17 22:53:23 +00:00
|
|
|
/*
|
|
|
|
* Fetch cpu utilization information. Updates on demand.
|
|
|
|
*/
|
2003-01-26 05:23:15 +00:00
|
|
|
fixpt_t
|
2003-10-16 08:39:15 +00:00
|
|
|
sched_pctcpu(struct thread *td)
|
2003-01-26 05:23:15 +00:00
|
|
|
{
|
|
|
|
fixpt_t pctcpu;
|
2006-12-06 06:34:57 +00:00
|
|
|
struct td_sched *ts;
|
2003-01-26 05:23:15 +00:00
|
|
|
|
|
|
|
pctcpu = 0;
|
2006-12-06 06:34:57 +00:00
|
|
|
ts = td->td_sched;
|
|
|
|
if (ts == NULL)
|
2003-10-20 19:55:21 +00:00
|
|
|
return (0);
|
2003-01-26 05:23:15 +00:00
|
|
|
|
Commit 1/14 of sched_lock decomposition.
- Move all scheduler locking into the schedulers utilizing a technique
similar to solaris's container locking.
- A per-process spinlock is now used to protect the queue of threads,
thread count, suspension count, p_sflags, and other process
related scheduling fields.
- The new thread lock is actually a pointer to a spinlock for the
container that the thread is currently owned by. The container may
be a turnstile, sleepqueue, or run queue.
- thread_lock() is now used to protect access to thread related scheduling
fields. thread_unlock() unlocks the lock and thread_set_lock()
implements the transition from one lock to another.
- A new "blocked_lock" is used in cases where it is not safe to hold the
actual thread's lock yet we must prevent access to the thread.
- sched_throw() and sched_fork_exit() are introduced to allow the
schedulers to fix-up locking at these points.
- Add some minor infrastructure for optionally exporting scheduler
statistics that were invaluable in solving performance problems with
this patch. Generally these statistics allow you to differentiate
between different causes of context switches.
Tested by: kris, current@
Tested on: i386, amd64, ULE, 4BSD, libthr, libkse, PREEMPTION, etc.
Discussed with: kris, attilio, kmacy, jhb, julian, bde (small parts each)
2007-06-04 23:50:30 +00:00
|
|
|
thread_lock(td);
|
2006-12-06 06:34:57 +00:00
|
|
|
if (ts->ts_ticks) {
|
2003-01-26 05:23:15 +00:00
|
|
|
int rtick;
|
|
|
|
|
2007-01-05 08:50:38 +00:00
|
|
|
sched_pctcpu_update(ts);
|
2003-01-26 05:23:15 +00:00
|
|
|
/* How many rtick per second ? */
|
2007-01-04 08:56:25 +00:00
|
|
|
rtick = min(SCHED_TICK_HZ(ts) / SCHED_TICK_SECS, hz);
|
|
|
|
pctcpu = (FSCALE * ((FSCALE * rtick)/hz)) >> FSHIFT;
|
2003-01-26 05:23:15 +00:00
|
|
|
}
|
Commit 1/14 of sched_lock decomposition.
- Move all scheduler locking into the schedulers utilizing a technique
similar to solaris's container locking.
- A per-process spinlock is now used to protect the queue of threads,
thread count, suspension count, p_sflags, and other process
related scheduling fields.
- The new thread lock is actually a pointer to a spinlock for the
container that the thread is currently owned by. The container may
be a turnstile, sleepqueue, or run queue.
- thread_lock() is now used to protect access to thread related scheduling
fields. thread_unlock() unlocks the lock and thread_set_lock()
implements the transition from one lock to another.
- A new "blocked_lock" is used in cases where it is not safe to hold the
actual thread's lock yet we must prevent access to the thread.
- sched_throw() and sched_fork_exit() are introduced to allow the
schedulers to fix-up locking at these points.
- Add some minor infrastructure for optionally exporting scheduler
statistics that were invaluable in solving performance problems with
this patch. Generally these statistics allow you to differentiate
between different causes of context switches.
Tested by: kris, current@
Tested on: i386, amd64, ULE, 4BSD, libthr, libkse, PREEMPTION, etc.
Discussed with: kris, attilio, kmacy, jhb, julian, bde (small parts each)
2007-06-04 23:50:30 +00:00
|
|
|
thread_unlock(td);
|
2003-01-26 05:23:15 +00:00
|
|
|
|
|
|
|
return (pctcpu);
|
|
|
|
}
|
|
|
|
|
2008-03-02 08:20:59 +00:00
|
|
|
/*
|
|
|
|
* Enforce affinity settings for a thread. Called after adjustments to
|
|
|
|
* cpumask.
|
|
|
|
*/
|
2008-03-02 07:19:35 +00:00
|
|
|
void
|
|
|
|
sched_affinity(struct thread *td)
|
|
|
|
{
|
2008-03-02 08:20:59 +00:00
|
|
|
#ifdef SMP
|
|
|
|
struct td_sched *ts;
|
|
|
|
int cpu;
|
|
|
|
|
|
|
|
THREAD_LOCK_ASSERT(td, MA_OWNED);
|
|
|
|
ts = td->td_sched;
|
|
|
|
if (THREAD_CAN_SCHED(td, ts->ts_cpu))
|
|
|
|
return;
|
|
|
|
if (!TD_IS_RUNNING(td))
|
|
|
|
return;
|
|
|
|
td->td_flags |= TDF_NEEDRESCHED;
|
|
|
|
if (!THREAD_CAN_MIGRATE(td))
|
|
|
|
return;
|
|
|
|
/*
|
|
|
|
* Assign the new cpu and force a switch before returning to
|
|
|
|
* userspace. If the target thread is not running locally send
|
|
|
|
* an ipi to force the issue.
|
|
|
|
*/
|
|
|
|
cpu = ts->ts_cpu;
|
2008-03-20 05:51:16 +00:00
|
|
|
ts->ts_cpu = sched_pickcpu(td, 0);
|
2008-03-02 08:20:59 +00:00
|
|
|
if (cpu != PCPU_GET(cpuid))
|
|
|
|
ipi_selected(1 << cpu, IPI_PREEMPT);
|
|
|
|
#endif
|
2008-03-02 07:19:35 +00:00
|
|
|
}
|
|
|
|
|
2007-07-17 22:53:23 +00:00
|
|
|
/*
|
|
|
|
* Bind a thread to a target cpu.
|
|
|
|
*/
|
2003-11-04 07:45:41 +00:00
|
|
|
void
|
|
|
|
sched_bind(struct thread *td, int cpu)
|
|
|
|
{
|
2006-12-06 06:34:57 +00:00
|
|
|
struct td_sched *ts;
|
2003-11-04 07:45:41 +00:00
|
|
|
|
2007-08-03 23:38:46 +00:00
|
|
|
THREAD_LOCK_ASSERT(td, MA_OWNED|MA_NOTRECURSED);
|
2006-12-06 06:34:57 +00:00
|
|
|
ts = td->td_sched;
|
2007-01-20 09:03:43 +00:00
|
|
|
if (ts->ts_flags & TSF_BOUND)
|
2007-01-20 17:03:33 +00:00
|
|
|
sched_unbind(td);
|
2006-12-06 06:34:57 +00:00
|
|
|
ts->ts_flags |= TSF_BOUND;
|
2007-01-20 09:03:43 +00:00
|
|
|
sched_pin();
|
2003-12-11 03:57:10 +00:00
|
|
|
if (PCPU_GET(cpuid) == cpu)
|
2003-11-04 07:45:41 +00:00
|
|
|
return;
|
2007-01-20 09:03:43 +00:00
|
|
|
ts->ts_cpu = cpu;
|
2003-11-04 07:45:41 +00:00
|
|
|
/* When we return from mi_switch we'll be on the correct cpu. */
|
2004-07-03 16:57:51 +00:00
|
|
|
mi_switch(SW_VOL, NULL);
|
2003-11-04 07:45:41 +00:00
|
|
|
}
|
|
|
|
|
2007-07-17 22:53:23 +00:00
|
|
|
/*
|
|
|
|
* Release a bound thread.
|
|
|
|
*/
|
2003-11-04 07:45:41 +00:00
|
|
|
void
|
|
|
|
sched_unbind(struct thread *td)
|
|
|
|
{
|
2007-01-04 08:56:25 +00:00
|
|
|
struct td_sched *ts;
|
|
|
|
|
Commit 1/14 of sched_lock decomposition.
- Move all scheduler locking into the schedulers utilizing a technique
similar to solaris's container locking.
- A per-process spinlock is now used to protect the queue of threads,
thread count, suspension count, p_sflags, and other process
related scheduling fields.
- The new thread lock is actually a pointer to a spinlock for the
container that the thread is currently owned by. The container may
be a turnstile, sleepqueue, or run queue.
- thread_lock() is now used to protect access to thread related scheduling
fields. thread_unlock() unlocks the lock and thread_set_lock()
implements the transition from one lock to another.
- A new "blocked_lock" is used in cases where it is not safe to hold the
actual thread's lock yet we must prevent access to the thread.
- sched_throw() and sched_fork_exit() are introduced to allow the
schedulers to fix-up locking at these points.
- Add some minor infrastructure for optionally exporting scheduler
statistics that were invaluable in solving performance problems with
this patch. Generally these statistics allow you to differentiate
between different causes of context switches.
Tested by: kris, current@
Tested on: i386, amd64, ULE, 4BSD, libthr, libkse, PREEMPTION, etc.
Discussed with: kris, attilio, kmacy, jhb, julian, bde (small parts each)
2007-06-04 23:50:30 +00:00
|
|
|
THREAD_LOCK_ASSERT(td, MA_OWNED);
|
2007-01-04 08:56:25 +00:00
|
|
|
ts = td->td_sched;
|
2007-01-20 09:03:43 +00:00
|
|
|
if ((ts->ts_flags & TSF_BOUND) == 0)
|
|
|
|
return;
|
2007-01-04 08:56:25 +00:00
|
|
|
ts->ts_flags &= ~TSF_BOUND;
|
|
|
|
sched_unpin();
|
2003-11-04 07:45:41 +00:00
|
|
|
}
|
|
|
|
|
2005-04-19 04:01:25 +00:00
|
|
|
int
|
|
|
|
sched_is_bound(struct thread *td)
|
|
|
|
{
|
Commit 1/14 of sched_lock decomposition.
- Move all scheduler locking into the schedulers utilizing a technique
similar to solaris's container locking.
- A per-process spinlock is now used to protect the queue of threads,
thread count, suspension count, p_sflags, and other process
related scheduling fields.
- The new thread lock is actually a pointer to a spinlock for the
container that the thread is currently owned by. The container may
be a turnstile, sleepqueue, or run queue.
- thread_lock() is now used to protect access to thread related scheduling
fields. thread_unlock() unlocks the lock and thread_set_lock()
implements the transition from one lock to another.
- A new "blocked_lock" is used in cases where it is not safe to hold the
actual thread's lock yet we must prevent access to the thread.
- sched_throw() and sched_fork_exit() are introduced to allow the
schedulers to fix-up locking at these points.
- Add some minor infrastructure for optionally exporting scheduler
statistics that were invaluable in solving performance problems with
this patch. Generally these statistics allow you to differentiate
between different causes of context switches.
Tested by: kris, current@
Tested on: i386, amd64, ULE, 4BSD, libthr, libkse, PREEMPTION, etc.
Discussed with: kris, attilio, kmacy, jhb, julian, bde (small parts each)
2007-06-04 23:50:30 +00:00
|
|
|
THREAD_LOCK_ASSERT(td, MA_OWNED);
|
2006-12-06 06:34:57 +00:00
|
|
|
return (td->td_sched->ts_flags & TSF_BOUND);
|
2005-04-19 04:01:25 +00:00
|
|
|
}
|
|
|
|
|
2007-07-17 22:53:23 +00:00
|
|
|
/*
|
|
|
|
* Basic yield call.
|
|
|
|
*/
|
2006-06-15 06:37:39 +00:00
|
|
|
void
|
|
|
|
sched_relinquish(struct thread *td)
|
|
|
|
{
|
Commit 1/14 of sched_lock decomposition.
- Move all scheduler locking into the schedulers utilizing a technique
similar to solaris's container locking.
- A per-process spinlock is now used to protect the queue of threads,
thread count, suspension count, p_sflags, and other process
related scheduling fields.
- The new thread lock is actually a pointer to a spinlock for the
container that the thread is currently owned by. The container may
be a turnstile, sleepqueue, or run queue.
- thread_lock() is now used to protect access to thread related scheduling
fields. thread_unlock() unlocks the lock and thread_set_lock()
implements the transition from one lock to another.
- A new "blocked_lock" is used in cases where it is not safe to hold the
actual thread's lock yet we must prevent access to the thread.
- sched_throw() and sched_fork_exit() are introduced to allow the
schedulers to fix-up locking at these points.
- Add some minor infrastructure for optionally exporting scheduler
statistics that were invaluable in solving performance problems with
this patch. Generally these statistics allow you to differentiate
between different causes of context switches.
Tested by: kris, current@
Tested on: i386, amd64, ULE, 4BSD, libthr, libkse, PREEMPTION, etc.
Discussed with: kris, attilio, kmacy, jhb, julian, bde (small parts each)
2007-06-04 23:50:30 +00:00
|
|
|
thread_lock(td);
|
2008-04-17 04:20:10 +00:00
|
|
|
mi_switch(SW_VOL | SWT_RELINQUISH, NULL);
|
Commit 1/14 of sched_lock decomposition.
- Move all scheduler locking into the schedulers utilizing a technique
similar to solaris's container locking.
- A per-process spinlock is now used to protect the queue of threads,
thread count, suspension count, p_sflags, and other process
related scheduling fields.
- The new thread lock is actually a pointer to a spinlock for the
container that the thread is currently owned by. The container may
be a turnstile, sleepqueue, or run queue.
- thread_lock() is now used to protect access to thread related scheduling
fields. thread_unlock() unlocks the lock and thread_set_lock()
implements the transition from one lock to another.
- A new "blocked_lock" is used in cases where it is not safe to hold the
actual thread's lock yet we must prevent access to the thread.
- sched_throw() and sched_fork_exit() are introduced to allow the
schedulers to fix-up locking at these points.
- Add some minor infrastructure for optionally exporting scheduler
statistics that were invaluable in solving performance problems with
this patch. Generally these statistics allow you to differentiate
between different causes of context switches.
Tested by: kris, current@
Tested on: i386, amd64, ULE, 4BSD, libthr, libkse, PREEMPTION, etc.
Discussed with: kris, attilio, kmacy, jhb, julian, bde (small parts each)
2007-06-04 23:50:30 +00:00
|
|
|
thread_unlock(td);
|
2006-06-15 06:37:39 +00:00
|
|
|
}
|
|
|
|
|
2007-07-17 22:53:23 +00:00
|
|
|
/*
|
|
|
|
* Return the total system load.
|
|
|
|
*/
|
2004-02-01 02:48:36 +00:00
|
|
|
int
|
|
|
|
sched_load(void)
|
|
|
|
{
|
|
|
|
#ifdef SMP
|
|
|
|
int total;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
total = 0;
|
2008-03-02 08:20:59 +00:00
|
|
|
for (i = 0; i <= mp_maxid; i++)
|
|
|
|
total += TDQ_CPU(i)->tdq_sysload;
|
2004-02-01 02:48:36 +00:00
|
|
|
return (total);
|
|
|
|
#else
|
2006-12-29 10:37:07 +00:00
|
|
|
return (TDQ_SELF()->tdq_sysload);
|
2004-02-01 02:48:36 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2003-01-26 05:23:15 +00:00
|
|
|
int
|
|
|
|
sched_sizeof_proc(void)
|
|
|
|
{
|
|
|
|
return (sizeof(struct proc));
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
sched_sizeof_thread(void)
|
|
|
|
{
|
|
|
|
return (sizeof(struct thread) + sizeof(struct td_sched));
|
|
|
|
}
|
Add scheduler CORE, the work I have done half a year ago, recent,
I picked it up again. The scheduler is forked from ULE, but the
algorithm to detect an interactive process is almost completely
different with ULE, it comes from Linux paper "Understanding the
Linux 2.6.8.1 CPU Scheduler", although I still use same word
"score" as a priority boost in ULE scheduler.
Briefly, the scheduler has following characteristic:
1. Timesharing process's nice value is seriously respected,
timeslice and interaction detecting algorithm are based
on nice value.
2. per-cpu scheduling queue and load balancing.
3. O(1) scheduling.
4. Some cpu affinity code in wakeup path.
5. Support POSIX SCHED_FIFO and SCHED_RR.
Unlike scheduler 4BSD and ULE which using fuzzy RQ_PPQ, the scheduler
uses 256 priority queues. Unlike ULE which using pull and push, the
scheduelr uses pull method, the main reason is to let relative idle
cpu do the work, but current the whole scheduler is protected by the
big sched_lock, so the benefit is not visible, it really can be worse
than nothing because all other cpu are locked out when we are doing
balancing work, which the 4BSD scheduelr does not have this problem.
The scheduler does not support hyperthreading very well, in fact,
the scheduler does not make the difference between physical CPU and
logical CPU, this should be improved in feature. The scheduler has
priority inversion problem on MP machine, it is not good for
realtime scheduling, it can cause realtime process starving.
As a result, it seems the MySQL super-smack runs better on my
Pentium-D machine when using libthr, despite on UP or SMP kernel.
2006-06-13 13:12:56 +00:00
|
|
|
|
2007-01-23 08:50:34 +00:00
|
|
|
/*
|
|
|
|
* The actual idle process.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
sched_idletd(void *dummy)
|
|
|
|
{
|
|
|
|
struct thread *td;
|
2007-07-17 22:53:23 +00:00
|
|
|
struct tdq *tdq;
|
2008-04-17 09:56:01 +00:00
|
|
|
int switchcnt;
|
|
|
|
int i;
|
2007-01-23 08:50:34 +00:00
|
|
|
|
|
|
|
td = curthread;
|
2007-07-17 22:53:23 +00:00
|
|
|
tdq = TDQ_SELF();
|
2007-01-23 08:50:34 +00:00
|
|
|
mtx_assert(&Giant, MA_NOTOWNED);
|
2007-07-17 22:53:23 +00:00
|
|
|
/* ULE relies on preemption for idle interruption. */
|
|
|
|
for (;;) {
|
2008-04-17 09:56:01 +00:00
|
|
|
tdq->tdq_idlestate = TDQ_RUNNING;
|
2007-07-17 22:53:23 +00:00
|
|
|
#ifdef SMP
|
2008-04-17 09:56:01 +00:00
|
|
|
if (tdq_idled(tdq) == 0)
|
|
|
|
continue;
|
2007-07-17 22:53:23 +00:00
|
|
|
#endif
|
2008-04-17 09:56:01 +00:00
|
|
|
switchcnt = tdq->tdq_switchcnt + tdq->tdq_oldswitchcnt;
|
|
|
|
/*
|
|
|
|
* If we're switching very frequently, spin while checking
|
|
|
|
* for load rather than entering a low power state that
|
|
|
|
* requires an IPI.
|
|
|
|
*/
|
|
|
|
if (switchcnt > sched_idlespinthresh) {
|
|
|
|
for (i = 0; i < sched_idlespins; i++) {
|
|
|
|
if (tdq->tdq_load)
|
|
|
|
break;
|
|
|
|
cpu_spinwait();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* We must set our state to IDLE before checking
|
|
|
|
* tdq_load for the last time to avoid a race with
|
|
|
|
* tdq_notify().
|
|
|
|
*/
|
|
|
|
if (tdq->tdq_load == 0) {
|
2008-04-25 05:18:50 +00:00
|
|
|
switchcnt = tdq->tdq_switchcnt + tdq->tdq_oldswitchcnt;
|
2008-04-17 09:56:01 +00:00
|
|
|
tdq->tdq_idlestate = TDQ_IDLE;
|
|
|
|
if (tdq->tdq_load == 0)
|
2008-04-25 05:18:50 +00:00
|
|
|
cpu_idle(switchcnt > 1);
|
2008-04-17 09:56:01 +00:00
|
|
|
}
|
|
|
|
if (tdq->tdq_load) {
|
|
|
|
thread_lock(td);
|
|
|
|
mi_switch(SW_VOL | SWT_IDLE, NULL);
|
|
|
|
thread_unlock(td);
|
|
|
|
}
|
2007-07-17 22:53:23 +00:00
|
|
|
}
|
Add scheduler CORE, the work I have done half a year ago, recent,
I picked it up again. The scheduler is forked from ULE, but the
algorithm to detect an interactive process is almost completely
different with ULE, it comes from Linux paper "Understanding the
Linux 2.6.8.1 CPU Scheduler", although I still use same word
"score" as a priority boost in ULE scheduler.
Briefly, the scheduler has following characteristic:
1. Timesharing process's nice value is seriously respected,
timeslice and interaction detecting algorithm are based
on nice value.
2. per-cpu scheduling queue and load balancing.
3. O(1) scheduling.
4. Some cpu affinity code in wakeup path.
5. Support POSIX SCHED_FIFO and SCHED_RR.
Unlike scheduler 4BSD and ULE which using fuzzy RQ_PPQ, the scheduler
uses 256 priority queues. Unlike ULE which using pull and push, the
scheduelr uses pull method, the main reason is to let relative idle
cpu do the work, but current the whole scheduler is protected by the
big sched_lock, so the benefit is not visible, it really can be worse
than nothing because all other cpu are locked out when we are doing
balancing work, which the 4BSD scheduelr does not have this problem.
The scheduler does not support hyperthreading very well, in fact,
the scheduler does not make the difference between physical CPU and
logical CPU, this should be improved in feature. The scheduler has
priority inversion problem on MP machine, it is not good for
realtime scheduling, it can cause realtime process starving.
As a result, it seems the MySQL super-smack runs better on my
Pentium-D machine when using libthr, despite on UP or SMP kernel.
2006-06-13 13:12:56 +00:00
|
|
|
}
|
2007-01-04 08:56:25 +00:00
|
|
|
|
Commit 1/14 of sched_lock decomposition.
- Move all scheduler locking into the schedulers utilizing a technique
similar to solaris's container locking.
- A per-process spinlock is now used to protect the queue of threads,
thread count, suspension count, p_sflags, and other process
related scheduling fields.
- The new thread lock is actually a pointer to a spinlock for the
container that the thread is currently owned by. The container may
be a turnstile, sleepqueue, or run queue.
- thread_lock() is now used to protect access to thread related scheduling
fields. thread_unlock() unlocks the lock and thread_set_lock()
implements the transition from one lock to another.
- A new "blocked_lock" is used in cases where it is not safe to hold the
actual thread's lock yet we must prevent access to the thread.
- sched_throw() and sched_fork_exit() are introduced to allow the
schedulers to fix-up locking at these points.
- Add some minor infrastructure for optionally exporting scheduler
statistics that were invaluable in solving performance problems with
this patch. Generally these statistics allow you to differentiate
between different causes of context switches.
Tested by: kris, current@
Tested on: i386, amd64, ULE, 4BSD, libthr, libkse, PREEMPTION, etc.
Discussed with: kris, attilio, kmacy, jhb, julian, bde (small parts each)
2007-06-04 23:50:30 +00:00
|
|
|
/*
|
|
|
|
* A CPU is entering for the first time or a thread is exiting.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
sched_throw(struct thread *td)
|
|
|
|
{
|
2007-10-02 01:30:18 +00:00
|
|
|
struct thread *newtd;
|
2007-07-17 22:53:23 +00:00
|
|
|
struct tdq *tdq;
|
|
|
|
|
|
|
|
tdq = TDQ_SELF();
|
Commit 1/14 of sched_lock decomposition.
- Move all scheduler locking into the schedulers utilizing a technique
similar to solaris's container locking.
- A per-process spinlock is now used to protect the queue of threads,
thread count, suspension count, p_sflags, and other process
related scheduling fields.
- The new thread lock is actually a pointer to a spinlock for the
container that the thread is currently owned by. The container may
be a turnstile, sleepqueue, or run queue.
- thread_lock() is now used to protect access to thread related scheduling
fields. thread_unlock() unlocks the lock and thread_set_lock()
implements the transition from one lock to another.
- A new "blocked_lock" is used in cases where it is not safe to hold the
actual thread's lock yet we must prevent access to the thread.
- sched_throw() and sched_fork_exit() are introduced to allow the
schedulers to fix-up locking at these points.
- Add some minor infrastructure for optionally exporting scheduler
statistics that were invaluable in solving performance problems with
this patch. Generally these statistics allow you to differentiate
between different causes of context switches.
Tested by: kris, current@
Tested on: i386, amd64, ULE, 4BSD, libthr, libkse, PREEMPTION, etc.
Discussed with: kris, attilio, kmacy, jhb, julian, bde (small parts each)
2007-06-04 23:50:30 +00:00
|
|
|
if (td == NULL) {
|
2007-07-17 22:53:23 +00:00
|
|
|
/* Correct spinlock nesting and acquire the correct lock. */
|
|
|
|
TDQ_LOCK(tdq);
|
Commit 1/14 of sched_lock decomposition.
- Move all scheduler locking into the schedulers utilizing a technique
similar to solaris's container locking.
- A per-process spinlock is now used to protect the queue of threads,
thread count, suspension count, p_sflags, and other process
related scheduling fields.
- The new thread lock is actually a pointer to a spinlock for the
container that the thread is currently owned by. The container may
be a turnstile, sleepqueue, or run queue.
- thread_lock() is now used to protect access to thread related scheduling
fields. thread_unlock() unlocks the lock and thread_set_lock()
implements the transition from one lock to another.
- A new "blocked_lock" is used in cases where it is not safe to hold the
actual thread's lock yet we must prevent access to the thread.
- sched_throw() and sched_fork_exit() are introduced to allow the
schedulers to fix-up locking at these points.
- Add some minor infrastructure for optionally exporting scheduler
statistics that were invaluable in solving performance problems with
this patch. Generally these statistics allow you to differentiate
between different causes of context switches.
Tested by: kris, current@
Tested on: i386, amd64, ULE, 4BSD, libthr, libkse, PREEMPTION, etc.
Discussed with: kris, attilio, kmacy, jhb, julian, bde (small parts each)
2007-06-04 23:50:30 +00:00
|
|
|
spinlock_exit();
|
|
|
|
} else {
|
2007-07-17 22:53:23 +00:00
|
|
|
MPASS(td->td_lock == TDQ_LOCKPTR(tdq));
|
2008-03-20 05:51:16 +00:00
|
|
|
tdq_load_rem(tdq, td);
|
2007-12-15 23:13:31 +00:00
|
|
|
lock_profile_release_lock(&TDQ_LOCKPTR(tdq)->lock_object);
|
Commit 1/14 of sched_lock decomposition.
- Move all scheduler locking into the schedulers utilizing a technique
similar to solaris's container locking.
- A per-process spinlock is now used to protect the queue of threads,
thread count, suspension count, p_sflags, and other process
related scheduling fields.
- The new thread lock is actually a pointer to a spinlock for the
container that the thread is currently owned by. The container may
be a turnstile, sleepqueue, or run queue.
- thread_lock() is now used to protect access to thread related scheduling
fields. thread_unlock() unlocks the lock and thread_set_lock()
implements the transition from one lock to another.
- A new "blocked_lock" is used in cases where it is not safe to hold the
actual thread's lock yet we must prevent access to the thread.
- sched_throw() and sched_fork_exit() are introduced to allow the
schedulers to fix-up locking at these points.
- Add some minor infrastructure for optionally exporting scheduler
statistics that were invaluable in solving performance problems with
this patch. Generally these statistics allow you to differentiate
between different causes of context switches.
Tested by: kris, current@
Tested on: i386, amd64, ULE, 4BSD, libthr, libkse, PREEMPTION, etc.
Discussed with: kris, attilio, kmacy, jhb, julian, bde (small parts each)
2007-06-04 23:50:30 +00:00
|
|
|
}
|
|
|
|
KASSERT(curthread->td_md.md_spinlock_count == 1, ("invalid count"));
|
2007-10-02 01:30:18 +00:00
|
|
|
newtd = choosethread();
|
|
|
|
TDQ_LOCKPTR(tdq)->mtx_lock = (uintptr_t)newtd;
|
Commit 1/14 of sched_lock decomposition.
- Move all scheduler locking into the schedulers utilizing a technique
similar to solaris's container locking.
- A per-process spinlock is now used to protect the queue of threads,
thread count, suspension count, p_sflags, and other process
related scheduling fields.
- The new thread lock is actually a pointer to a spinlock for the
container that the thread is currently owned by. The container may
be a turnstile, sleepqueue, or run queue.
- thread_lock() is now used to protect access to thread related scheduling
fields. thread_unlock() unlocks the lock and thread_set_lock()
implements the transition from one lock to another.
- A new "blocked_lock" is used in cases where it is not safe to hold the
actual thread's lock yet we must prevent access to the thread.
- sched_throw() and sched_fork_exit() are introduced to allow the
schedulers to fix-up locking at these points.
- Add some minor infrastructure for optionally exporting scheduler
statistics that were invaluable in solving performance problems with
this patch. Generally these statistics allow you to differentiate
between different causes of context switches.
Tested by: kris, current@
Tested on: i386, amd64, ULE, 4BSD, libthr, libkse, PREEMPTION, etc.
Discussed with: kris, attilio, kmacy, jhb, julian, bde (small parts each)
2007-06-04 23:50:30 +00:00
|
|
|
PCPU_SET(switchtime, cpu_ticks());
|
|
|
|
PCPU_SET(switchticks, ticks);
|
2007-10-02 01:30:18 +00:00
|
|
|
cpu_throw(td, newtd); /* doesn't return */
|
Commit 1/14 of sched_lock decomposition.
- Move all scheduler locking into the schedulers utilizing a technique
similar to solaris's container locking.
- A per-process spinlock is now used to protect the queue of threads,
thread count, suspension count, p_sflags, and other process
related scheduling fields.
- The new thread lock is actually a pointer to a spinlock for the
container that the thread is currently owned by. The container may
be a turnstile, sleepqueue, or run queue.
- thread_lock() is now used to protect access to thread related scheduling
fields. thread_unlock() unlocks the lock and thread_set_lock()
implements the transition from one lock to another.
- A new "blocked_lock" is used in cases where it is not safe to hold the
actual thread's lock yet we must prevent access to the thread.
- sched_throw() and sched_fork_exit() are introduced to allow the
schedulers to fix-up locking at these points.
- Add some minor infrastructure for optionally exporting scheduler
statistics that were invaluable in solving performance problems with
this patch. Generally these statistics allow you to differentiate
between different causes of context switches.
Tested by: kris, current@
Tested on: i386, amd64, ULE, 4BSD, libthr, libkse, PREEMPTION, etc.
Discussed with: kris, attilio, kmacy, jhb, julian, bde (small parts each)
2007-06-04 23:50:30 +00:00
|
|
|
}
|
|
|
|
|
2007-07-17 22:53:23 +00:00
|
|
|
/*
|
|
|
|
* This is called from fork_exit(). Just acquire the correct locks and
|
|
|
|
* let fork do the rest of the work.
|
|
|
|
*/
|
Commit 1/14 of sched_lock decomposition.
- Move all scheduler locking into the schedulers utilizing a technique
similar to solaris's container locking.
- A per-process spinlock is now used to protect the queue of threads,
thread count, suspension count, p_sflags, and other process
related scheduling fields.
- The new thread lock is actually a pointer to a spinlock for the
container that the thread is currently owned by. The container may
be a turnstile, sleepqueue, or run queue.
- thread_lock() is now used to protect access to thread related scheduling
fields. thread_unlock() unlocks the lock and thread_set_lock()
implements the transition from one lock to another.
- A new "blocked_lock" is used in cases where it is not safe to hold the
actual thread's lock yet we must prevent access to the thread.
- sched_throw() and sched_fork_exit() are introduced to allow the
schedulers to fix-up locking at these points.
- Add some minor infrastructure for optionally exporting scheduler
statistics that were invaluable in solving performance problems with
this patch. Generally these statistics allow you to differentiate
between different causes of context switches.
Tested by: kris, current@
Tested on: i386, amd64, ULE, 4BSD, libthr, libkse, PREEMPTION, etc.
Discussed with: kris, attilio, kmacy, jhb, julian, bde (small parts each)
2007-06-04 23:50:30 +00:00
|
|
|
void
|
2007-06-12 07:47:09 +00:00
|
|
|
sched_fork_exit(struct thread *td)
|
Commit 1/14 of sched_lock decomposition.
- Move all scheduler locking into the schedulers utilizing a technique
similar to solaris's container locking.
- A per-process spinlock is now used to protect the queue of threads,
thread count, suspension count, p_sflags, and other process
related scheduling fields.
- The new thread lock is actually a pointer to a spinlock for the
container that the thread is currently owned by. The container may
be a turnstile, sleepqueue, or run queue.
- thread_lock() is now used to protect access to thread related scheduling
fields. thread_unlock() unlocks the lock and thread_set_lock()
implements the transition from one lock to another.
- A new "blocked_lock" is used in cases where it is not safe to hold the
actual thread's lock yet we must prevent access to the thread.
- sched_throw() and sched_fork_exit() are introduced to allow the
schedulers to fix-up locking at these points.
- Add some minor infrastructure for optionally exporting scheduler
statistics that were invaluable in solving performance problems with
this patch. Generally these statistics allow you to differentiate
between different causes of context switches.
Tested by: kris, current@
Tested on: i386, amd64, ULE, 4BSD, libthr, libkse, PREEMPTION, etc.
Discussed with: kris, attilio, kmacy, jhb, julian, bde (small parts each)
2007-06-04 23:50:30 +00:00
|
|
|
{
|
2007-07-17 22:53:23 +00:00
|
|
|
struct td_sched *ts;
|
|
|
|
struct tdq *tdq;
|
|
|
|
int cpuid;
|
Commit 1/14 of sched_lock decomposition.
- Move all scheduler locking into the schedulers utilizing a technique
similar to solaris's container locking.
- A per-process spinlock is now used to protect the queue of threads,
thread count, suspension count, p_sflags, and other process
related scheduling fields.
- The new thread lock is actually a pointer to a spinlock for the
container that the thread is currently owned by. The container may
be a turnstile, sleepqueue, or run queue.
- thread_lock() is now used to protect access to thread related scheduling
fields. thread_unlock() unlocks the lock and thread_set_lock()
implements the transition from one lock to another.
- A new "blocked_lock" is used in cases where it is not safe to hold the
actual thread's lock yet we must prevent access to the thread.
- sched_throw() and sched_fork_exit() are introduced to allow the
schedulers to fix-up locking at these points.
- Add some minor infrastructure for optionally exporting scheduler
statistics that were invaluable in solving performance problems with
this patch. Generally these statistics allow you to differentiate
between different causes of context switches.
Tested by: kris, current@
Tested on: i386, amd64, ULE, 4BSD, libthr, libkse, PREEMPTION, etc.
Discussed with: kris, attilio, kmacy, jhb, julian, bde (small parts each)
2007-06-04 23:50:30 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Finish setting up thread glue so that it begins execution in a
|
2007-07-17 22:53:23 +00:00
|
|
|
* non-nested critical section with the scheduler lock held.
|
Commit 1/14 of sched_lock decomposition.
- Move all scheduler locking into the schedulers utilizing a technique
similar to solaris's container locking.
- A per-process spinlock is now used to protect the queue of threads,
thread count, suspension count, p_sflags, and other process
related scheduling fields.
- The new thread lock is actually a pointer to a spinlock for the
container that the thread is currently owned by. The container may
be a turnstile, sleepqueue, or run queue.
- thread_lock() is now used to protect access to thread related scheduling
fields. thread_unlock() unlocks the lock and thread_set_lock()
implements the transition from one lock to another.
- A new "blocked_lock" is used in cases where it is not safe to hold the
actual thread's lock yet we must prevent access to the thread.
- sched_throw() and sched_fork_exit() are introduced to allow the
schedulers to fix-up locking at these points.
- Add some minor infrastructure for optionally exporting scheduler
statistics that were invaluable in solving performance problems with
this patch. Generally these statistics allow you to differentiate
between different causes of context switches.
Tested by: kris, current@
Tested on: i386, amd64, ULE, 4BSD, libthr, libkse, PREEMPTION, etc.
Discussed with: kris, attilio, kmacy, jhb, julian, bde (small parts each)
2007-06-04 23:50:30 +00:00
|
|
|
*/
|
2007-07-17 22:53:23 +00:00
|
|
|
cpuid = PCPU_GET(cpuid);
|
|
|
|
tdq = TDQ_CPU(cpuid);
|
|
|
|
ts = td->td_sched;
|
|
|
|
if (TD_IS_IDLETHREAD(td))
|
|
|
|
td->td_lock = TDQ_LOCKPTR(tdq);
|
|
|
|
MPASS(td->td_lock == TDQ_LOCKPTR(tdq));
|
|
|
|
td->td_oncpu = cpuid;
|
2007-10-02 01:30:18 +00:00
|
|
|
TDQ_LOCK_ASSERT(tdq, MA_OWNED | MA_NOTRECURSED);
|
2007-12-15 23:13:31 +00:00
|
|
|
lock_profile_obtain_lock_success(
|
|
|
|
&TDQ_LOCKPTR(tdq)->lock_object, 0, 0, __FILE__, __LINE__);
|
Commit 1/14 of sched_lock decomposition.
- Move all scheduler locking into the schedulers utilizing a technique
similar to solaris's container locking.
- A per-process spinlock is now used to protect the queue of threads,
thread count, suspension count, p_sflags, and other process
related scheduling fields.
- The new thread lock is actually a pointer to a spinlock for the
container that the thread is currently owned by. The container may
be a turnstile, sleepqueue, or run queue.
- thread_lock() is now used to protect access to thread related scheduling
fields. thread_unlock() unlocks the lock and thread_set_lock()
implements the transition from one lock to another.
- A new "blocked_lock" is used in cases where it is not safe to hold the
actual thread's lock yet we must prevent access to the thread.
- sched_throw() and sched_fork_exit() are introduced to allow the
schedulers to fix-up locking at these points.
- Add some minor infrastructure for optionally exporting scheduler
statistics that were invaluable in solving performance problems with
this patch. Generally these statistics allow you to differentiate
between different causes of context switches.
Tested by: kris, current@
Tested on: i386, amd64, ULE, 4BSD, libthr, libkse, PREEMPTION, etc.
Discussed with: kris, attilio, kmacy, jhb, julian, bde (small parts each)
2007-06-04 23:50:30 +00:00
|
|
|
}
|
|
|
|
|
Introduce a new sysctl, kern.sched.topology_spec, that returns an XML
dump of detected ULE CPU topology. This dump can be used to check the
topology detection and for general system information.
An example of CPU topology dump is:
kern.sched.topology_spec: <groups>
<group level="1" cache-level="0">
<cpu count="8" mask="0xff">0, 1, 2, 3, 4, 5, 6, 7</cpu>
<flags></flags>
<children>
<group level="2" cache-level="0">
<cpu count="4" mask="0xf">0, 1, 2, 3</cpu>
<flags></flags>
</group>
<group level="2" cache-level="0">
<cpu count="4" mask="0xf0">4, 5, 6, 7</cpu>
<flags></flags>
</group>
</children>
</group>
</groups>
Reviewed by: jeff
Approved by: gnn (mentor)
2008-10-29 13:36:23 +00:00
|
|
|
#ifdef SMP
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Build the CPU topology dump string. Is recursively called to collect
|
|
|
|
* the topology tree.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
sysctl_kern_sched_topology_spec_internal(struct sbuf *sb, struct cpu_group *cg,
|
|
|
|
int indent)
|
|
|
|
{
|
|
|
|
int i, first;
|
|
|
|
|
|
|
|
sbuf_printf(sb, "%*s<group level=\"%d\" cache-level=\"%d\">\n", indent,
|
|
|
|
"", indent, cg->cg_level);
|
|
|
|
sbuf_printf(sb, "%*s <cpu count=\"%d\" mask=\"0x%x\">", indent, "",
|
|
|
|
cg->cg_count, cg->cg_mask);
|
|
|
|
first = TRUE;
|
|
|
|
for (i = 0; i < MAXCPU; i++) {
|
|
|
|
if ((cg->cg_mask & (1 << i)) != 0) {
|
|
|
|
if (!first)
|
|
|
|
sbuf_printf(sb, ", ");
|
|
|
|
else
|
|
|
|
first = FALSE;
|
|
|
|
sbuf_printf(sb, "%d", i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sbuf_printf(sb, "</cpu>\n");
|
|
|
|
|
|
|
|
sbuf_printf(sb, "%*s <flags>", indent, "");
|
|
|
|
if (cg->cg_flags != 0) {
|
|
|
|
if ((cg->cg_flags & CG_FLAG_HTT) != 0)
|
|
|
|
sbuf_printf(sb, "<flag name=\"HTT\">HTT group</flag>");
|
|
|
|
if ((cg->cg_flags & CG_FLAG_THREAD) != 0)
|
|
|
|
sbuf_printf(sb, "<flag name=\"THREAD\">SMT group</flag>");
|
|
|
|
}
|
|
|
|
sbuf_printf(sb, "</flags>\n");
|
|
|
|
|
|
|
|
if (cg->cg_children > 0) {
|
|
|
|
sbuf_printf(sb, "%*s <children>\n", indent, "");
|
|
|
|
for (i = 0; i < cg->cg_children; i++)
|
|
|
|
sysctl_kern_sched_topology_spec_internal(sb,
|
|
|
|
&cg->cg_child[i], indent+2);
|
|
|
|
sbuf_printf(sb, "%*s </children>\n", indent, "");
|
|
|
|
}
|
|
|
|
sbuf_printf(sb, "%*s</group>\n", indent, "");
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Sysctl handler for retrieving topology dump. It's a wrapper for
|
|
|
|
* the recursive sysctl_kern_smp_topology_spec_internal().
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
sysctl_kern_sched_topology_spec(SYSCTL_HANDLER_ARGS)
|
|
|
|
{
|
|
|
|
struct sbuf *topo;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
KASSERT(cpu_top != NULL, ("cpu_top isn't initialized"));
|
|
|
|
|
2008-11-02 23:11:20 +00:00
|
|
|
topo = sbuf_new(NULL, NULL, 500, SBUF_AUTOEXTEND);
|
Introduce a new sysctl, kern.sched.topology_spec, that returns an XML
dump of detected ULE CPU topology. This dump can be used to check the
topology detection and for general system information.
An example of CPU topology dump is:
kern.sched.topology_spec: <groups>
<group level="1" cache-level="0">
<cpu count="8" mask="0xff">0, 1, 2, 3, 4, 5, 6, 7</cpu>
<flags></flags>
<children>
<group level="2" cache-level="0">
<cpu count="4" mask="0xf">0, 1, 2, 3</cpu>
<flags></flags>
</group>
<group level="2" cache-level="0">
<cpu count="4" mask="0xf0">4, 5, 6, 7</cpu>
<flags></flags>
</group>
</children>
</group>
</groups>
Reviewed by: jeff
Approved by: gnn (mentor)
2008-10-29 13:36:23 +00:00
|
|
|
if (topo == NULL)
|
|
|
|
return (ENOMEM);
|
|
|
|
|
|
|
|
sbuf_printf(topo, "<groups>\n");
|
|
|
|
err = sysctl_kern_sched_topology_spec_internal(topo, cpu_top, 1);
|
|
|
|
sbuf_printf(topo, "</groups>\n");
|
|
|
|
|
|
|
|
if (err == 0) {
|
|
|
|
sbuf_finish(topo);
|
|
|
|
err = SYSCTL_OUT(req, sbuf_data(topo), sbuf_len(topo));
|
|
|
|
}
|
|
|
|
sbuf_delete(topo);
|
|
|
|
return (err);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2008-03-20 05:51:16 +00:00
|
|
|
SYSCTL_NODE(_kern, OID_AUTO, sched, CTLFLAG_RW, 0, "Scheduler");
|
2007-07-17 22:53:23 +00:00
|
|
|
SYSCTL_STRING(_kern_sched, OID_AUTO, name, CTLFLAG_RD, "ULE", 0,
|
2007-01-04 08:56:25 +00:00
|
|
|
"Scheduler name");
|
2007-07-17 22:53:23 +00:00
|
|
|
SYSCTL_INT(_kern_sched, OID_AUTO, slice, CTLFLAG_RW, &sched_slice, 0,
|
|
|
|
"Slice size for timeshare threads");
|
|
|
|
SYSCTL_INT(_kern_sched, OID_AUTO, interact, CTLFLAG_RW, &sched_interact, 0,
|
|
|
|
"Interactivity score threshold");
|
|
|
|
SYSCTL_INT(_kern_sched, OID_AUTO, preempt_thresh, CTLFLAG_RW, &preempt_thresh,
|
|
|
|
0,"Min priority for preemption, lower priorities have greater precedence");
|
2008-03-12 06:31:06 +00:00
|
|
|
SYSCTL_INT(_kern_sched, OID_AUTO, static_boost, CTLFLAG_RW, &static_boost,
|
|
|
|
0,"Controls whether static kernel priorities are assigned to sleeping threads.");
|
2008-04-17 09:56:01 +00:00
|
|
|
SYSCTL_INT(_kern_sched, OID_AUTO, idlespins, CTLFLAG_RW, &sched_idlespins,
|
|
|
|
0,"Number of times idle will spin waiting for new work.");
|
|
|
|
SYSCTL_INT(_kern_sched, OID_AUTO, idlespinthresh, CTLFLAG_RW, &sched_idlespinthresh,
|
|
|
|
0,"Threshold before we will permit idle spinning.");
|
2007-01-19 21:56:08 +00:00
|
|
|
#ifdef SMP
|
2007-07-17 22:53:23 +00:00
|
|
|
SYSCTL_INT(_kern_sched, OID_AUTO, affinity, CTLFLAG_RW, &affinity, 0,
|
|
|
|
"Number of hz ticks to keep thread affinity for");
|
|
|
|
SYSCTL_INT(_kern_sched, OID_AUTO, balance, CTLFLAG_RW, &rebalance, 0,
|
|
|
|
"Enables the long-term load balancer");
|
2007-10-02 00:36:06 +00:00
|
|
|
SYSCTL_INT(_kern_sched, OID_AUTO, balance_interval, CTLFLAG_RW,
|
|
|
|
&balance_interval, 0,
|
|
|
|
"Average frequency in stathz ticks to run the long-term balancer");
|
2007-07-17 22:53:23 +00:00
|
|
|
SYSCTL_INT(_kern_sched, OID_AUTO, steal_htt, CTLFLAG_RW, &steal_htt, 0,
|
|
|
|
"Steals work from another hyper-threaded core on idle");
|
|
|
|
SYSCTL_INT(_kern_sched, OID_AUTO, steal_idle, CTLFLAG_RW, &steal_idle, 0,
|
|
|
|
"Attempts to steal work from other cores before idling");
|
2007-07-19 20:03:15 +00:00
|
|
|
SYSCTL_INT(_kern_sched, OID_AUTO, steal_thresh, CTLFLAG_RW, &steal_thresh, 0,
|
|
|
|
"Minimum load on remote cpu before we'll steal");
|
Introduce a new sysctl, kern.sched.topology_spec, that returns an XML
dump of detected ULE CPU topology. This dump can be used to check the
topology detection and for general system information.
An example of CPU topology dump is:
kern.sched.topology_spec: <groups>
<group level="1" cache-level="0">
<cpu count="8" mask="0xff">0, 1, 2, 3, 4, 5, 6, 7</cpu>
<flags></flags>
<children>
<group level="2" cache-level="0">
<cpu count="4" mask="0xf">0, 1, 2, 3</cpu>
<flags></flags>
</group>
<group level="2" cache-level="0">
<cpu count="4" mask="0xf0">4, 5, 6, 7</cpu>
<flags></flags>
</group>
</children>
</group>
</groups>
Reviewed by: jeff
Approved by: gnn (mentor)
2008-10-29 13:36:23 +00:00
|
|
|
|
|
|
|
/* Retrieve SMP topology */
|
|
|
|
SYSCTL_PROC(_kern_sched, OID_AUTO, topology_spec, CTLTYPE_STRING |
|
|
|
|
CTLFLAG_RD, NULL, 0, sysctl_kern_sched_topology_spec, "A",
|
|
|
|
"XML dump of detected CPU topology");
|
2007-01-19 21:56:08 +00:00
|
|
|
#endif
|
2007-01-04 08:56:25 +00:00
|
|
|
|
2007-09-21 04:10:23 +00:00
|
|
|
/* ps compat. All cpu percentages from ULE are weighted. */
|
2007-09-22 02:20:14 +00:00
|
|
|
static int ccpu = 0;
|
2007-01-04 08:56:25 +00:00
|
|
|
SYSCTL_INT(_kern, OID_AUTO, ccpu, CTLFLAG_RD, &ccpu, 0, "");
|