aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/amd64/conf/GENERIC1
-rw-r--r--sys/conf/NOTES1
-rw-r--r--sys/conf/files1
-rw-r--r--sys/conf/options1
-rw-r--r--sys/i386/conf/GENERIC1
-rw-r--r--sys/kern/kern_clock.c1
-rw-r--r--sys/kern/kern_switch.c7
-rw-r--r--sys/kern/sched_4bsd.c5
-rw-r--r--sys/kern/sched_core.c2329
-rw-r--r--sys/kern/sched_ule.c5
-rw-r--r--sys/sys/sched.h1
11 files changed, 2352 insertions, 1 deletions
diff --git a/sys/amd64/conf/GENERIC b/sys/amd64/conf/GENERIC
index db756325c61e..7dc771578e3f 100644
--- a/sys/amd64/conf/GENERIC
+++ b/sys/amd64/conf/GENERIC
@@ -28,6 +28,7 @@ makeoptions DEBUG=-g # Build kernel with gdb(1) debug symbols
#options SCHED_ULE # ULE scheduler
options SCHED_4BSD # 4BSD scheduler
+#options SCHED_CORE # CORE scheduler
options PREEMPTION # Enable kernel thread preemption
options INET # InterNETworking
options INET6 # IPv6 communications protocols
diff --git a/sys/conf/NOTES b/sys/conf/NOTES
index 31d195d4ea2c..a9618b456922 100644
--- a/sys/conf/NOTES
+++ b/sys/conf/NOTES
@@ -166,6 +166,7 @@ options ROOTDEVNAME=\"ufs:da0s2e\"
# over time.
#
options SCHED_4BSD
+#options SCHED_CORE
#options SCHED_ULE
#####################################################################
diff --git a/sys/conf/files b/sys/conf/files
index 8455bc801808..2be0954661b6 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -1324,6 +1324,7 @@ kern/linker_if.m standard
kern/md4c.c optional netsmb
kern/md5c.c standard
kern/sched_4bsd.c optional sched_4bsd
+kern/sched_core.c optional sched_core
kern/sched_ule.c optional sched_ule
kern/serdev_if.m optional puc | scc
kern/subr_autoconf.c standard
diff --git a/sys/conf/options b/sys/conf/options
index e55fc8fcc1df..19cca6f96c1a 100644
--- a/sys/conf/options
+++ b/sys/conf/options
@@ -128,6 +128,7 @@ PPS_SYNC opt_ntp.h
PREEMPTION opt_sched.h
QUOTA
SCHED_4BSD opt_sched.h
+SCHED_CORE opt_sched.h
SCHED_ULE opt_sched.h
SHOW_BUSYBUFS
SLEEPQUEUE_PROFILING
diff --git a/sys/i386/conf/GENERIC b/sys/i386/conf/GENERIC
index 930099308ea0..4fcbb7aea5f6 100644
--- a/sys/i386/conf/GENERIC
+++ b/sys/i386/conf/GENERIC
@@ -30,6 +30,7 @@ makeoptions DEBUG=-g # Build kernel with gdb(1) debug symbols
#options SCHED_ULE # ULE scheduler
options SCHED_4BSD # 4BSD scheduler
+#options SCHED_CORE # CORE scheduler
options PREEMPTION # Enable kernel thread preemption
options INET # InterNETworking
options INET6 # IPv6 communications protocols
diff --git a/sys/kern/kern_clock.c b/sys/kern/kern_clock.c
index e00d8243df64..182e1297a129 100644
--- a/sys/kern/kern_clock.c
+++ b/sys/kern/kern_clock.c
@@ -201,6 +201,7 @@ hardclock_cpu(int usermode)
* Run current process's virtual and profile time, as needed.
*/
mtx_lock_spin_flags(&sched_lock, MTX_QUIET);
+ sched_tick();
if (p->p_flag & P_SA) {
/* XXXKSE What to do? */
} else {
diff --git a/sys/kern/kern_switch.c b/sys/kern/kern_switch.c
index c7d8ed694fa6..229085e9526c 100644
--- a/sys/kern/kern_switch.c
+++ b/sys/kern/kern_switch.c
@@ -308,7 +308,12 @@ adjustrunqueue( struct thread *td, int newpri)
if ((td->td_proc->p_flag & P_HADTHREADS) == 0) {
/* We only care about the kse in the run queue. */
td->td_priority = newpri;
- if (ke->ke_rqindex != (newpri / RQ_PPQ)) {
+#ifndef SCHED_CORE
+ if (ke->ke_rqindex != (newpri / RQ_PPQ))
+#else
+ if (ke->ke_rqindex != newpri)
+#endif
+ {
sched_rem(td);
sched_add(td, SRQ_BORING);
}
diff --git a/sys/kern/sched_4bsd.c b/sys/kern/sched_4bsd.c
index 25eba837b65d..60f6df6a5d14 100644
--- a/sys/kern/sched_4bsd.c
+++ b/sys/kern/sched_4bsd.c
@@ -1386,5 +1386,10 @@ sched_pctcpu(struct thread *td)
return (0);
}
+
+void
+sched_tick(void)
+{
+}
#define KERN_SWITCH_INCLUDE 1
#include "kern/kern_switch.c"
diff --git a/sys/kern/sched_core.c b/sys/kern/sched_core.c
new file mode 100644
index 000000000000..e74194e7946b
--- /dev/null
+++ b/sys/kern/sched_core.c
@@ -0,0 +1,2329 @@
+/*-
+ * Copyright (c) 2005-2006, David Xu <yfxu@corp.netease.com>
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_hwpmc_hooks.h"
+#include "opt_sched.h"
+
+#define kse td_sched
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kdb.h>
+#include <sys/kernel.h>
+#include <sys/kthread.h>
+#include <sys/ktr.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/proc.h>
+#include <sys/resource.h>
+#include <sys/resourcevar.h>
+#include <sys/sched.h>
+#include <sys/smp.h>
+#include <sys/sx.h>
+#include <sys/sysctl.h>
+#include <sys/sysproto.h>
+#include <sys/turnstile.h>
+#include <sys/unistd.h>
+#include <sys/vmmeter.h>
+#ifdef KTRACE
+#include <sys/uio.h>
+#include <sys/ktrace.h>
+#endif
+
+#ifdef HWPMC_HOOKS
+#include <sys/pmckern.h>
+#endif
+
+#include <machine/cpu.h>
+#include <machine/smp.h>
+
+/* get process's nice value, skip value 20 which is not supported */
+#define PROC_NICE(p) MIN((p)->p_nice, 19)
+
+/* convert nice to kernel thread priority */
+#define NICE_TO_PRI(nice) (PUSER + 20 + (nice))
+
+/* get process's static priority */
+#define PROC_PRI(p) NICE_TO_PRI(PROC_NICE(p))
+
+/* convert kernel thread priority to user priority */
+#define USER_PRI(pri) MIN((pri) - PUSER, 39)
+
+/* convert nice value to user priority */
+#define PROC_USER_PRI(p) (PROC_NICE(p) + 20)
+
+/* maximum user priority, highest prio + 1 */
+#define MAX_USER_PRI 40
+
+/* maximum kernel priority its nice is 19 */
+#define PUSER_MAX (PUSER + 39)
+
+/* ticks and nanosecond converters */
+#define NS_TO_HZ(n) ((n) / (1000000000 / hz))
+#define HZ_TO_NS(h) ((h) * (1000000000 / hz))
+
+/* ticks and microsecond converters */
+#define MS_TO_HZ(m) ((m) / (1000000 / hz))
+
+#define PRI_SCORE_RATIO 25
+#define MAX_SCORE (MAX_USER_PRI * PRI_SCORE_RATIO / 100)
+#define MAX_SLEEP_TIME (def_timeslice * MAX_SCORE)
+#define NS_MAX_SLEEP_TIME (HZ_TO_NS(MAX_SLEEP_TIME))
+#define STARVATION_TIME (MAX_SLEEP_TIME)
+
+#define CURRENT_SCORE(kg) \
+ (MAX_SCORE * NS_TO_HZ((kg)->kg_slptime) / MAX_SLEEP_TIME)
+
+#define SCALE_USER_PRI(x, upri) \
+ MAX(x * (upri + 1) / (MAX_USER_PRI/2), min_timeslice)
+
+/*
+ * For a thread whose nice is zero, the score is used to determine
+ * if it is an interactive thread.
+ */
+#define INTERACTIVE_BASE_SCORE (MAX_SCORE * 20)/100
+
+/*
+ * Calculate a score which a thread must have to prove itself is
+ * an interactive thread.
+ */
+#define INTERACTIVE_SCORE(ke) \
+ (PROC_NICE((ke)->ke_proc) * MAX_SCORE / 40 + INTERACTIVE_BASE_SCORE)
+
+/* Test if a thread is an interactive thread */
+#define THREAD_IS_INTERACTIVE(ke) \
+ ((ke)->ke_ksegrp->kg_user_pri <= \
+ PROC_PRI((ke)->ke_proc) - INTERACTIVE_SCORE(ke))
+
+/*
+ * Calculate how long a thread must sleep to prove itself is an
+ * interactive sleep.
+ */
+#define INTERACTIVE_SLEEP_TIME(ke) \
+ (HZ_TO_NS(MAX_SLEEP_TIME * \
+ (MAX_SCORE / 2 + INTERACTIVE_SCORE((ke)) + 1) / MAX_SCORE - 1))
+
+#define CHILD_WEIGHT 90
+#define PARENT_WEIGHT 90
+#define EXIT_WEIGHT 3
+
+#define SCHED_LOAD_SCALE 128UL
+
+#define IDLE 0
+#define IDLE_IDLE 1
+#define NOT_IDLE 2
+
+#define KQB_LEN (8) /* Number of priority status words. */
+#define KQB_L2BPW (5) /* Log2(sizeof(rqb_word_t) * NBBY)). */
+#define KQB_BPW (1<<KQB_L2BPW) /* Bits in an rqb_word_t. */
+
+#define KQB_BIT(pri) (1 << ((pri) & (KQB_BPW - 1)))
+#define KQB_WORD(pri) ((pri) >> KQB_L2BPW)
+#define KQB_FFS(word) (ffs(word) - 1)
+
+#define KQ_NQS 256
+
+/*
+ * Type of run queue status word.
+ */
+typedef u_int32_t kqb_word_t;
+
+/*
+ * Head of run queues.
+ */
+TAILQ_HEAD(krqhead, kse);
+
+/*
+ * Bit array which maintains the status of a run queue. When a queue is
+ * non-empty the bit corresponding to the queue number will be set.
+ */
+struct krqbits {
+ kqb_word_t rqb_bits[KQB_LEN];
+};
+
+/*
+ * Run queue structure. Contains an array of run queues on which processes
+ * are placed, and a structure to maintain the status of each queue.
+ */
+struct krunq {
+ struct krqbits rq_status;
+ struct krqhead rq_queues[KQ_NQS];
+};
+
+/*
+ * The following datastructures are allocated within their parent structure
+ * but are scheduler specific.
+ */
+/*
+ * The schedulable entity that can be given a context to run. A process may
+ * have several of these.
+ */
+struct kse {
+ TAILQ_ENTRY(kse) ke_procq; /* (j/z) Run queue. */
+ int ke_flags; /* (j) KEF_* flags. */
+ struct thread *ke_thread; /* (*) Active associated thread. */
+ fixpt_t ke_pctcpu; /* (j) %cpu during p_swtime. */
+ u_char ke_rqindex; /* (j) Run queue index. */
+ enum {
+ KES_THREAD = 0x0, /* slaved to thread state */
+ KES_ONRUNQ
+ } ke_state; /* (j) thread sched specific status. */
+ int ke_slice;
+ struct krunq *ke_runq;
+ int ke_cpu; /* CPU that we have affinity for. */
+ int ke_activated;
+ uint64_t ke_timestamp;
+ uint64_t ke_lastran;
+#ifdef SMP
+ int ke_tocpu;
+#endif
+ /* The following variables are only used for pctcpu calculation */
+ int ke_ltick; /* Last tick that we were running on */
+ int ke_ftick; /* First tick that we were running on */
+ int ke_ticks; /* Tick count */
+};
+
+#define td_kse td_sched
+#define ke_proc ke_thread->td_proc
+#define ke_ksegrp ke_thread->td_ksegrp
+
+/* flags kept in ke_flags */
+#define KEF_ASSIGNED 0x0001 /* Thread is being migrated. */
+#define KEF_BOUND 0x0002 /* Thread can not migrate. */
+#define KEF_XFERABLE 0x0004 /* Thread was added as transferable. */
+#define KEF_HOLD 0x0008 /* Thread is temporarily bound. */
+#define KEF_REMOVED 0x0010 /* Thread was removed while ASSIGNED */
+#define KEF_INTERNAL 0x0020 /* Thread added due to migration. */
+#define KEF_PREEMPTED 0x0040 /* Thread was preempted. */
+#define KEF_MIGRATING 0x0080 /* Thread is migrating. */
+#define KEF_SLEEP 0x0100 /* Thread did sleep. */
+#define KEF_DIDRUN 0x2000 /* Thread actually ran. */
+#define KEF_EXIT 0x4000 /* Thread is being killed. */
+#define KEF_NEXTRQ 0x8000 /* Thread should be in next queue. */
+#define KEF_FIRST_SLICE 0x10000 /* Thread has first time slice left. */
+
+struct kg_sched {
+ struct thread *skg_last_assigned; /* (j) Last thread assigned to */
+ /* the system scheduler */
+ u_long skg_slptime; /* (j) Number of ticks we vol. slept */
+ u_long skg_runtime; /* (j) Temp total run time. */
+ int skg_avail_opennings; /* (j) Num unfilled slots in group.*/
+ int skg_concurrency; /* (j) Num threads requested in group.*/
+};
+#define kg_last_assigned kg_sched->skg_last_assigned
+#define kg_avail_opennings kg_sched->skg_avail_opennings
+#define kg_concurrency kg_sched->skg_concurrency
+#define kg_slptime kg_sched->skg_slptime
+#define kg_runtime kg_sched->skg_runtime
+
+#define SLOT_RELEASE(kg) (kg)->kg_avail_opennings++
+#define SLOT_USE(kg) (kg)->kg_avail_opennings--
+
+/*
+ * Cpu percentage computation macros and defines.
+ *
+ * SCHED_CPU_TIME: Number of seconds to average the cpu usage across.
+ * SCHED_CPU_TICKS: Number of hz ticks to average the cpu usage across.
+ */
+
+#define SCHED_CPU_TIME 10
+#define SCHED_CPU_TICKS (hz * SCHED_CPU_TIME)
+
+/*
+ * kseq - per processor runqs and statistics.
+ */
+struct kseq {
+ struct krunq ksq_idle; /* Queue of IDLE threads. */
+ struct krunq ksq_timeshare[2]; /* Run queues for !IDLE. */
+ struct krunq *ksq_next; /* Next timeshare queue. */
+ struct krunq *ksq_curr; /* Current queue. */
+ int ksq_load_timeshare; /* Load for timeshare. */
+ int ksq_load_idle;
+ int ksq_load; /* Aggregate load. */
+ int ksq_sysload; /* For loadavg, !P_NOLOAD */
+ uint64_t ksq_expired_timestamp;
+ uint64_t ksq_last_timestamp;
+ signed char ksq_best_expired_nice;
+#ifdef SMP
+ int ksq_transferable;
+ LIST_ENTRY(kseq) ksq_siblings; /* Next in kseq group. */
+ struct kseq_group *ksq_group; /* Our processor group. */
+ struct thread *ksq_migrated;
+ TAILQ_HEAD(,kse) ksq_migrateq;
+ int ksq_avgload;
+#endif
+};
+
+#ifdef SMP
+/*
+ * kseq groups are groups of processors which can cheaply share threads. When
+ * one processor in the group goes idle it will check the runqs of the other
+ * processors in its group prior to halting and waiting for an interrupt.
+ * These groups are suitable for SMT (Symetric Multi-Threading) and not NUMA.
+ * In a NUMA environment we'd want an idle bitmap per group and a two tiered
+ * load balancer.
+ */
+struct kseq_group {
+ int ksg_cpus; /* Count of CPUs in this kseq group. */
+ cpumask_t ksg_cpumask; /* Mask of cpus in this group. */
+ cpumask_t ksg_idlemask; /* Idle cpus in this group. */
+ cpumask_t ksg_mask; /* Bit mask for first cpu. */
+ int ksg_transferable; /* Transferable load of this group. */
+ LIST_HEAD(, kseq) ksg_members; /* Linked list of all members. */
+ int ksg_balance_tick;
+};
+#endif
+
+static struct kse kse0;
+static struct kg_sched kg_sched0;
+
+static int min_timeslice = 5;
+static int def_timeslice = 100;
+static int granularity = 10;
+static int realstathz;
+
+/*
+ * One kse queue per processor.
+ */
+#ifdef SMP
+static cpumask_t kseq_idle;
+static int ksg_maxid;
+static struct kseq kseq_cpu[MAXCPU];
+static struct kseq_group kseq_groups[MAXCPU];
+static int balance_tick;
+static int balance_interval = 1;
+static int balance_interval_max = 32;
+static int balance_interval_min = 8;
+static int balance_busy_factor = 32;
+static int imbalance_pct = 25;
+static int imbalance_pct2 = 50;
+static int ignore_topology = 1;
+
+#define KSEQ_SELF() (&kseq_cpu[PCPU_GET(cpuid)])
+#define KSEQ_CPU(x) (&kseq_cpu[(x)])
+#define KSEQ_ID(x) ((x) - kseq_cpu)
+#define KSEQ_GROUP(x) (&kseq_groups[(x)])
+#else /* !SMP */
+static struct kseq kseq_cpu;
+
+#define KSEQ_SELF() (&kseq_cpu)
+#define KSEQ_CPU(x) (&kseq_cpu)
+#endif
+
+/* decay 95% of `p_pctcpu' in 60 seconds; see CCPU_SHIFT before changing */
+static fixpt_t ccpu = 0.95122942450071400909 * FSCALE; /* exp(-1/20) */
+SYSCTL_INT(_kern, OID_AUTO, ccpu, CTLFLAG_RD, &ccpu, 0, "");
+
+static void sched_setup(void *dummy);
+SYSINIT(sched_setup, SI_SUB_RUN_QUEUE, SI_ORDER_FIRST, sched_setup, NULL);
+
+static void sched_initticks(void *dummy);
+SYSINIT(sched_initticks, SI_SUB_CLOCKS, SI_ORDER_THIRD, sched_initticks, NULL)
+
+static SYSCTL_NODE(_kern, OID_AUTO, sched, CTLFLAG_RW, 0, "Scheduler");
+
+SYSCTL_STRING(_kern_sched, OID_AUTO, name, CTLFLAG_RD, "core", 0,
+ "Scheduler name");
+
+#ifdef SMP
+SYSCTL_INT(_kern_sched, OID_AUTO, imbalance_pct, CTLFLAG_RW,
+ &imbalance_pct, 0, "");
+
+SYSCTL_INT(_kern_sched, OID_AUTO, imbalance_pct2, CTLFLAG_RW,
+ &imbalance_pct2, 0, "");
+
+SYSCTL_INT(_kern_sched, OID_AUTO, balance_interval_min, CTLFLAG_RW,
+ &balance_interval_min, 0, "");
+
+SYSCTL_INT(_kern_sched, OID_AUTO, balance_interval_max, CTLFLAG_RW,
+ &balance_interval_max, 0, "");
+#endif
+
+static void slot_fill(struct ksegrp *);
+
+static void krunq_add(struct krunq *, struct kse *, int flags);
+static struct kse *krunq_choose(struct krunq *);
+static void krunq_clrbit(struct krunq *rq, int pri);
+static int krunq_findbit(struct krunq *rq);
+static void krunq_init(struct krunq *);
+static void krunq_remove(struct krunq *, struct kse *);
+#ifdef SMP
+static struct kse *krunq_steal(struct krunq *rq, int my_cpu);
+#endif
+
+static struct kse * kseq_choose(struct kseq *);
+static void kseq_load_add(struct kseq *, struct kse *);
+static void kseq_load_rem(struct kseq *, struct kse *);
+static void kseq_runq_add(struct kseq *, struct kse *, int);
+static void kseq_runq_rem(struct kseq *, struct kse *);
+static void kseq_setup(struct kseq *);
+
+static int sched_is_timeshare(struct ksegrp *kg);
+static struct kse *sched_choose(void);
+static int sched_calc_pri(struct ksegrp *kg);
+static int sched_starving(struct kseq *, uint64_t, struct kse *);
+static void sched_pctcpu_update(struct kse *);
+static void sched_thread_priority(struct thread *, u_char);
+static uint64_t sched_timestamp(void);
+static int sched_recalc_pri(struct kse *ke, uint64_t now);
+static int sched_timeslice(struct kse *ke);
+static void sched_update_runtime(struct kse *ke, uint64_t now);
+static void sched_commit_runtime(struct kse *ke);
+
+#ifdef SMP
+static void sched_balance_tick(int my_cpu, int idle);
+static int sched_balance_idle(int my_cpu, int idle);
+static int sched_balance(int my_cpu, int idle);
+struct kseq_group *sched_find_busiest_group(int my_cpu, int idle,
+ int *imbalance);
+static struct kseq *sched_find_busiest_queue(struct kseq_group *ksg);
+static int sched_find_idlest_cpu(struct kse *ke, int cpu);
+static int sched_pull_threads(struct kseq *high, struct kseq *myksq,
+ int max_move, int idle);
+static int sched_pull_one(struct kseq *from, struct kseq *myksq, int idle);
+static struct kse *sched_steal(struct kseq *, int my_cpu, int stealidle);
+static int sched_idled(struct kseq *, int idle);
+static int sched_find_idle_cpu(int defcpu);
+static void migrated_setup(void *dummy);
+static void migrated(void *dummy);
+SYSINIT(migrated_setup, SI_SUB_KTHREAD_IDLE, SI_ORDER_MIDDLE, migrated_setup,
+ NULL);
+
+#endif /* SMP */
+
+static inline int
+kse_pinned(struct kse *ke)
+{
+ if (ke->ke_thread->td_pinned)
+ return (1);
+
+ if (ke->ke_flags & KEF_BOUND)
+ return (1);
+
+ return (0);
+}
+
+#ifdef SMP
+static inline int
+kse_can_migrate(struct kse *ke)
+{
+ if (kse_pinned(ke))
+ return (0);
+ return (1);
+}
+#endif
+
+/*
+ * Initialize a run structure.
+ */
+static void
+krunq_init(struct krunq *rq)
+{
+ int i;
+
+ bzero(rq, sizeof *rq);
+ for (i = 0; i < KQ_NQS; i++)
+ TAILQ_INIT(&rq->rq_queues[i]);
+}
+
+/*
+ * Clear the status bit of the queue corresponding to priority level pri,
+ * indicating that it is empty.
+ */
+static inline void
+krunq_clrbit(struct krunq *rq, int pri)
+{
+ struct krqbits *rqb;
+
+ rqb = &rq->rq_status;
+ rqb->rqb_bits[KQB_WORD(pri)] &= ~KQB_BIT(pri);
+}
+
+/*
+ * Find the index of the first non-empty run queue. This is done by
+ * scanning the status bits, a set bit indicates a non-empty queue.
+ */
+static int
+krunq_findbit(struct krunq *rq)
+{
+ struct krqbits *rqb;
+ int pri;
+ int i;
+
+ rqb = &rq->rq_status;
+ for (i = 0; i < KQB_LEN; i++) {
+ if (rqb->rqb_bits[i]) {
+ pri = KQB_FFS(rqb->rqb_bits[i]) + (i << KQB_L2BPW);
+ return (pri);
+ }
+ }
+ return (-1);
+}
+
+/*
+ * Set the status bit of the queue corresponding to priority level pri,
+ * indicating that it is non-empty.
+ */
+static inline void
+krunq_setbit(struct krunq *rq, int pri)
+{
+ struct krqbits *rqb;
+
+ rqb = &rq->rq_status;
+ rqb->rqb_bits[KQB_WORD(pri)] |= KQB_BIT(pri);
+}
+
+/*
+ * Add the KSE to the queue specified by its priority, and set the
+ * corresponding status bit.
+ */
+static void
+krunq_add(struct krunq *rq, struct kse *ke, int flags)
+{
+ struct krqhead *rqh;
+ int pri;
+
+ pri = ke->ke_thread->td_priority;
+ ke->ke_rqindex = pri;
+ krunq_setbit(rq, pri);
+ rqh = &rq->rq_queues[pri];
+ if (flags & SRQ_PREEMPTED)
+ TAILQ_INSERT_HEAD(rqh, ke, ke_procq);
+ else
+ TAILQ_INSERT_TAIL(rqh, ke, ke_procq);
+}
+
+/*
+ * Find the highest priority process on the run queue.
+ */
+static struct kse *
+krunq_choose(struct krunq *rq)
+{
+ struct krqhead *rqh;
+ struct kse *ke;
+ int pri;
+
+ mtx_assert(&sched_lock, MA_OWNED);
+ if ((pri = krunq_findbit(rq)) != -1) {
+ rqh = &rq->rq_queues[pri];
+ ke = TAILQ_FIRST(rqh);
+ KASSERT(ke != NULL, ("runq_choose: no proc on busy queue"));
+ return (ke);
+ }
+
+ return (NULL);
+}
+
+/*
+ * Remove the KSE from the queue specified by its priority, and clear the
+ * corresponding status bit if the queue becomes empty.
+ * Caller must set ke->ke_state afterwards.
+ */
+static void
+krunq_remove(struct krunq *rq, struct kse *ke)
+{
+ struct krqhead *rqh;
+ int pri;
+
+ KASSERT(ke->ke_proc->p_sflag & PS_INMEM,
+ ("runq_remove: process swapped out"));
+ pri = ke->ke_rqindex;
+ rqh = &rq->rq_queues[pri];
+ KASSERT(ke != NULL, ("krunq_remove: no proc on busy queue"));
+ TAILQ_REMOVE(rqh, ke, ke_procq);
+ if (TAILQ_EMPTY(rqh))
+ krunq_clrbit(rq, pri);
+}
+
+#ifdef SMP
+static struct kse *
+krunq_steal(struct krunq *rq, int my_cpu)
+{
+ struct krqhead *rqh;
+ struct krqbits *rqb;
+ struct kse *ke;
+ kqb_word_t word;
+ int i, bit;
+
+ (void)my_cpu;
+
+ mtx_assert(&sched_lock, MA_OWNED);
+ rqb = &rq->rq_status;
+ for (i = 0; i < KQB_LEN; i++) {
+ if ((word = rqb->rqb_bits[i]) == 0)
+ continue;
+ do {
+ bit = KQB_FFS(word);
+ rqh = &rq->rq_queues[bit + (i << KQB_L2BPW)];
+ TAILQ_FOREACH(ke, rqh, ke_procq) {
+ if (kse_can_migrate(ke))
+ return (ke);
+ }
+ word &= ~((kqb_word_t)1 << bit);
+ } while (word != 0);
+ }
+ return (NULL);
+}
+#endif
+
+static inline void
+kseq_runq_add(struct kseq *kseq, struct kse *ke, int flags)
+{
+#ifdef SMP
+ if (kse_pinned(ke) == 0) {
+ kseq->ksq_transferable++;
+ kseq->ksq_group->ksg_transferable++;
+ ke->ke_flags |= KEF_XFERABLE;
+ }
+#endif
+ if (ke->ke_flags & KEF_PREEMPTED)
+ flags |= SRQ_PREEMPTED;
+ krunq_add(ke->ke_runq, ke, flags);
+}
+
+static inline void
+kseq_runq_rem(struct kseq *kseq, struct kse *ke)
+{
+#ifdef SMP
+ if (ke->ke_flags & KEF_XFERABLE) {
+ kseq->ksq_transferable--;
+ kseq->ksq_group->ksg_transferable--;
+ ke->ke_flags &= ~KEF_XFERABLE;
+ }
+#endif
+ krunq_remove(ke->ke_runq, ke);
+ ke->ke_runq = NULL;
+}
+
+static void
+kseq_load_add(struct kseq *kseq, struct kse *ke)
+{
+ int class;
+
+ mtx_assert(&sched_lock, MA_OWNED);
+#ifdef SMP
+ if (__predict_false(ke->ke_thread == kseq->ksq_migrated))
+ return;
+#endif
+ class = PRI_BASE(ke->ke_ksegrp->kg_pri_class);
+ if (class == PRI_TIMESHARE)
+ kseq->ksq_load_timeshare++;
+ else if (class == PRI_IDLE)
+ kseq->ksq_load_idle++;
+ kseq->ksq_load++;
+ if ((ke->ke_proc->p_flag & P_NOLOAD) == 0)
+ kseq->ksq_sysload++;
+}
+
+static void
+kseq_load_rem(struct kseq *kseq, struct kse *ke)
+{
+ int class;
+
+ mtx_assert(&sched_lock, MA_OWNED);
+#ifdef SMP
+ if (__predict_false(ke->ke_thread == kseq->ksq_migrated))
+ return;
+#endif
+ class = PRI_BASE(ke->ke_ksegrp->kg_pri_class);
+ if (class == PRI_TIMESHARE)
+ kseq->ksq_load_timeshare--;
+ else if (class == PRI_IDLE)
+ kseq->ksq_load_idle--;
+ kseq->ksq_load--;
+ if ((ke->ke_proc->p_flag & P_NOLOAD) == 0)
+ kseq->ksq_sysload--;
+}
+
+/*
+ * Pick the highest priority task we have and return it.
+ */
+
+static struct kse *
+kseq_choose(struct kseq *kseq)
+{
+ struct krunq *swap;
+ struct kse *ke;
+
+ mtx_assert(&sched_lock, MA_OWNED);
+
+ ke = krunq_choose(kseq->ksq_curr);
+ if (ke != NULL)
+ return (ke);
+
+ kseq->ksq_best_expired_nice = 21;
+ kseq->ksq_expired_timestamp = 0;
+ swap = kseq->ksq_curr;
+ kseq->ksq_curr = kseq->ksq_next;
+ kseq->ksq_next = swap;
+ ke = krunq_choose(kseq->ksq_curr);
+ if (ke != NULL)
+ return (ke);
+
+ return krunq_choose(&kseq->ksq_idle);
+}
+
+static inline uint64_t
+sched_timestamp(void)
+{
+ uint64_t now = cputick2usec(cpu_ticks()) * 1000;
+ return (now);
+}
+
+static inline int
+sched_timeslice(struct kse *ke)
+{
+ struct proc *p = ke->ke_proc;
+
+ if (ke->ke_proc->p_nice < 0)
+ return SCALE_USER_PRI(def_timeslice*4, PROC_USER_PRI(p));
+ else
+ return SCALE_USER_PRI(def_timeslice, PROC_USER_PRI(p));
+}
+
+static inline int
+sched_is_timeshare(struct ksegrp *kg)
+{
+ /*
+ * XXX P_KTHREAD should be checked, but unfortunately, the
+ * readonly flag resides in a volatile member p_flag, reading
+ * it could cause lots of cache line sharing and invalidating.
+ */
+ return (kg->kg_pri_class != PRI_TIMESHARE);
+}
+
+static int
+sched_calc_pri(struct ksegrp *kg)
+{
+ int score, pri;
+
+ if (__predict_false(!sched_is_timeshare(kg)))
+ return (kg->kg_user_pri);
+ score = CURRENT_SCORE(kg) - MAX_SCORE / 2;
+ pri = PROC_PRI(kg->kg_proc) - score;
+ if (pri < PUSER)
+ pri = PUSER;
+ if (pri > PUSER_MAX)
+ pri = PUSER_MAX;
+ return (pri);
+}
+
+static int
+sched_recalc_pri(struct kse *ke, uint64_t now)
+{
+ uint64_t delta;
+ unsigned int sleep_time;
+ struct ksegrp *kg;
+
+ kg = ke->ke_ksegrp;
+ delta = now - ke->ke_timestamp;
+ if (__predict_false(!sched_is_timeshare(kg)))
+ return (kg->kg_user_pri);
+
+ if (delta > NS_MAX_SLEEP_TIME)
+ sleep_time = NS_MAX_SLEEP_TIME;
+ else
+ sleep_time = (unsigned int)delta;
+ if (__predict_false(sleep_time == 0))
+ goto out;
+
+ if (ke->ke_activated != -1 &&
+ sleep_time > INTERACTIVE_SLEEP_TIME(ke)) {
+ kg->kg_slptime = HZ_TO_NS(MAX_SLEEP_TIME - def_timeslice);
+ } else {
+ sleep_time *= (MAX_SCORE - CURRENT_SCORE(kg)) ? : 1;
+
+ /*
+ * If thread is waking from uninterruptible sleep, it is
+ * unlikely an interactive sleep, limit its sleep time to
+ * prevent it from being an interactive thread.
+ */
+ if (ke->ke_activated == -1) {
+ if (kg->kg_slptime >= INTERACTIVE_SLEEP_TIME(ke))
+ sleep_time = 0;
+ else if (kg->kg_slptime + sleep_time >=
+ INTERACTIVE_SLEEP_TIME(ke)) {
+ kg->kg_slptime = INTERACTIVE_SLEEP_TIME(ke);
+ sleep_time = 0;
+ }
+ }
+
+ /*
+ * Thread gets priority boost here.
+ */
+ kg->kg_slptime += sleep_time;
+
+ /* Sleep time should never be larger than maximum */
+ if (kg->kg_slptime > NS_MAX_SLEEP_TIME)
+ kg->kg_slptime = NS_MAX_SLEEP_TIME;
+ }
+
+out:
+ return (sched_calc_pri(kg));
+}
+
+static void
+sched_update_runtime(struct kse *ke, uint64_t now)
+{
+ uint64_t runtime;
+ struct ksegrp *kg = ke->ke_ksegrp;
+
+ if (sched_is_timeshare(kg)) {
+ if ((int64_t)(now - ke->ke_timestamp) < NS_MAX_SLEEP_TIME) {
+ runtime = now - ke->ke_timestamp;
+ if ((int64_t)(now - ke->ke_timestamp) < 0)
+ runtime = 0;
+ } else {
+ runtime = NS_MAX_SLEEP_TIME;
+ }
+ runtime /= (CURRENT_SCORE(kg) ? : 1);
+ kg->kg_runtime += runtime;
+ ke->ke_timestamp = now;
+ }
+}
+
+static void
+sched_commit_runtime(struct kse *ke)
+{
+ struct ksegrp *kg = ke->ke_ksegrp;
+
+ if (kg->kg_runtime > kg->kg_slptime)
+ kg->kg_slptime = 0;
+ else
+ kg->kg_slptime -= kg->kg_runtime;
+ kg->kg_runtime = 0;
+}
+
+#ifdef SMP
+
+/* staged balancing operations between CPUs */
+#define CPU_OFFSET(cpu) (hz * cpu / MAXCPU)
+
+static void
+sched_balance_tick(int my_cpu, int idle)
+{
+ struct kseq *kseq = KSEQ_CPU(my_cpu);
+ unsigned t = ticks + CPU_OFFSET(my_cpu);
+ int old_load, cur_load;
+ int interval;
+
+ old_load = kseq->ksq_avgload;
+ cur_load = kseq->ksq_load * SCHED_LOAD_SCALE;
+ if (cur_load > old_load)
+ old_load++;
+ kseq->ksq_avgload = (old_load + cur_load) / 2;
+
+ interval = balance_interval;
+ if (idle == NOT_IDLE)
+ interval *= balance_busy_factor;
+ interval = MS_TO_HZ(interval);
+ if (interval == 0)
+ interval = 1;
+ if (t - balance_tick >= interval) {
+ sched_balance(my_cpu, idle);
+ balance_tick += interval;
+ }
+}
+
+static int
+sched_balance(int my_cpu, int idle)
+{
+ struct kseq_group *high_group;
+ struct kseq *high_queue;
+ int imbalance, pulled;
+
+ mtx_assert(&sched_lock, MA_OWNED);
+ high_group = sched_find_busiest_group(my_cpu, idle, &imbalance);
+ if (high_group == NULL)
+ goto out;
+ high_queue = sched_find_busiest_queue(high_group);
+ if (high_queue == NULL)
+ goto out;
+ pulled = sched_pull_threads(high_queue, KSEQ_CPU(my_cpu), imbalance,
+ idle);
+ if (pulled == 0) {
+ if (balance_interval < balance_interval_max)
+ balance_interval++;
+ } else {
+ balance_interval = balance_interval_min;
+ }
+ return (pulled);
+out:
+ if (balance_interval < balance_interval_max)
+ balance_interval *= 2;
+ return (0);
+}
+
+static int
+sched_balance_idle(int my_cpu, int idle)
+{
+ struct kseq_group *high_group;
+ struct kseq *high_queue;
+ int imbalance, pulled;
+
+ mtx_assert(&sched_lock, MA_OWNED);
+ high_group = sched_find_busiest_group(my_cpu, idle, &imbalance);
+ if (high_group == NULL)
+ return (0);
+ high_queue = sched_find_busiest_queue(high_group);
+ if (high_queue == NULL)
+ return (0);
+ pulled = sched_pull_threads(high_queue, KSEQ_CPU(my_cpu), imbalance,
+ idle);
+ return (pulled);
+}
+
+static inline int
+kseq_source_load(struct kseq *ksq)
+{
+ int load = ksq->ksq_load * SCHED_LOAD_SCALE;
+ return (MIN(ksq->ksq_avgload, load));
+}
+
+static inline int
+kseq_dest_load(struct kseq *ksq)
+{
+ int load = ksq->ksq_load * SCHED_LOAD_SCALE;
+ return (MAX(ksq->ksq_avgload, load));
+}
+
+struct kseq_group *
+sched_find_busiest_group(int my_cpu, int idle, int *imbalance)
+{
+ static unsigned stage_cpu;
+ struct kseq_group *high;
+ struct kseq_group *ksg;
+ struct kseq *my_ksq, *ksq;
+ int my_load, high_load, avg_load, total_load, load;
+ int diff, cnt, i;
+
+ *imbalance = 0;
+ if (__predict_false(smp_started == 0))
+ return (NULL);
+
+ my_ksq = KSEQ_CPU(my_cpu);
+ high = NULL;
+ high_load = total_load = my_load = 0;
+ i = (stage_cpu++) % (ksg_maxid + 1);
+ for (cnt = 0; cnt <= ksg_maxid; cnt++) {
+ ksg = KSEQ_GROUP(i);
+ /*
+ * Find the CPU with the highest load that has some
+ * threads to transfer.
+ */
+ load = 0;
+ LIST_FOREACH(ksq, &ksg->ksg_members, ksq_siblings) {
+ if (ksg == my_ksq->ksq_group)
+ load += kseq_dest_load(ksq);
+ else
+ load += kseq_source_load(ksq);
+ }
+ if (ksg == my_ksq->ksq_group) {
+ my_load = load;
+ } else if (load > high_load && ksg->ksg_transferable) {
+ high = ksg;
+ high_load = load;
+ }
+ total_load += load;
+ if (++i > ksg_maxid)
+ i = 0;
+ }
+
+ avg_load = total_load / (ksg_maxid + 1);
+
+ if (high == NULL)
+ return (NULL);
+
+ if (my_load >= avg_load ||
+ (high_load - my_load) * 100 < imbalance_pct * my_load) {
+ if (idle == IDLE_IDLE ||
+ (idle == IDLE && high_load > SCHED_LOAD_SCALE)) {
+ *imbalance = 1;
+ return (high);
+ } else {
+ return (NULL);
+ }
+ }
+
+ /*
+ * Pick a minimum imbalance value, avoid raising our load
+ * higher than average and pushing busiest load under average.
+ */
+ diff = MIN(high_load - avg_load, avg_load - my_load);
+ if (diff < SCHED_LOAD_SCALE) {
+ if (high_load - my_load >= SCHED_LOAD_SCALE * 2) {
+ *imbalance = 1;
+ return (high);
+ }
+ }
+
+ *imbalance = diff / SCHED_LOAD_SCALE;
+ return (high);
+}
+
+static struct kseq *
+sched_find_busiest_queue(struct kseq_group *ksg)
+{
+ struct kseq *kseq, *high = NULL;
+ int load, high_load = 0;
+
+ LIST_FOREACH(kseq, &ksg->ksg_members, ksq_siblings) {
+ load = kseq_source_load(kseq);
+ if (load > high_load) {
+ high_load = load;
+ high = kseq;
+ }
+ }
+
+ return (high);
+}
+
+static int
+sched_pull_threads(struct kseq *high, struct kseq *myksq, int max_pull,
+ int idle)
+{
+ int pulled, i;
+
+ mtx_assert(&sched_lock, MA_OWNED);
+ pulled = 0;
+ for (i = 0; i < max_pull; i++) {
+ if (sched_pull_one(high, myksq, idle))
+ pulled++;
+ else
+ break;
+ }
+ return (pulled);
+}
+
+static int
+sched_pull_one(struct kseq *from, struct kseq *myksq, int idle)
+{
+ struct kseq *kseq;
+ struct kse *ke;
+ struct krunq *destq;
+ int class;
+
+ mtx_assert(&sched_lock, MA_OWNED);
+ kseq = from;
+ ke = sched_steal(kseq, KSEQ_ID(myksq), idle);
+ if (ke == NULL) {
+ /* doing balance in same group */
+ if (from->ksq_group == myksq->ksq_group)
+ return (0);
+
+ struct kseq_group *ksg;
+
+ ksg = kseq->ksq_group;
+ LIST_FOREACH(kseq, &ksg->ksg_members, ksq_siblings) {
+ if (kseq == from || kseq == myksq ||
+ kseq->ksq_transferable == 0)
+ continue;
+ ke = sched_steal(kseq, KSEQ_ID(myksq), idle);
+ break;
+ }
+ if (ke == NULL)
+ return (0);
+ }
+ ke->ke_timestamp = ke->ke_timestamp + myksq->ksq_last_timestamp -
+ kseq->ksq_last_timestamp;
+ ke->ke_lastran = 0;
+ if (ke->ke_runq == from->ksq_curr)
+ destq = myksq->ksq_curr;
+ else if (ke->ke_runq == from->ksq_next)
+ destq = myksq->ksq_next;
+ else
+ destq = &myksq->ksq_idle;
+ kseq_runq_rem(kseq, ke);
+ kseq_load_rem(kseq, ke);
+ ke->ke_cpu = KSEQ_ID(myksq);
+ ke->ke_runq = destq;
+ ke->ke_state = KES_ONRUNQ;
+ kseq_runq_add(myksq, ke, 0);
+ kseq_load_add(myksq, ke);
+ class = PRI_BASE(ke->ke_ksegrp->kg_pri_class);
+ if (class != PRI_IDLE) {
+ if (kseq_idle & myksq->ksq_group->ksg_mask)
+ kseq_idle &= ~myksq->ksq_group->ksg_mask;
+ if (myksq->ksq_group->ksg_idlemask & PCPU_GET(cpumask))
+ myksq->ksq_group->ksg_idlemask &= ~PCPU_GET(cpumask);
+ }
+ if (ke->ke_thread->td_priority < curthread->td_priority)
+ curthread->td_flags |= TDF_NEEDRESCHED;
+ return (1);
+}
+
+static struct kse *
+sched_steal(struct kseq *kseq, int my_cpu, int idle)
+{
+ struct kse *ke;
+
+ /*
+ * Steal from expired queue first to try to get a non-interactive
+ * task that may not have run for a while.
+ */
+ if ((ke = krunq_steal(kseq->ksq_next, my_cpu)) != NULL)
+ return (ke);
+ if ((ke = krunq_steal(kseq->ksq_curr, my_cpu)) != NULL)
+ return (ke);
+ if (idle == IDLE_IDLE)
+ return (krunq_steal(&kseq->ksq_idle, my_cpu));
+ return (NULL);
+}
+
+static int
+sched_idled(struct kseq *kseq, int idle)
+{
+ struct kseq_group *ksg;
+ struct kseq *steal;
+
+ mtx_assert(&sched_lock, MA_OWNED);
+ ksg = kseq->ksq_group;
+ /*
+ * If we're in a cpu group, try and steal kses from another cpu in
+ * the group before idling.
+ */
+ if (ksg->ksg_cpus > 1 && ksg->ksg_transferable) {
+ LIST_FOREACH(steal, &ksg->ksg_members, ksq_siblings) {
+ if (steal == kseq || steal->ksq_transferable == 0)
+ continue;
+ if (sched_pull_one(steal, kseq, idle))
+ return (0);
+ }
+ }
+
+ if (sched_balance_idle(PCPU_GET(cpuid), idle))
+ return (0);
+
+ /*
+ * We only set the idled bit when all of the cpus in the group are
+ * idle. Otherwise we could get into a situation where a KSE bounces
+ * back and forth between two idle cores on seperate physical CPUs.
+ */
+ ksg->ksg_idlemask |= PCPU_GET(cpumask);
+ if (ksg->ksg_idlemask != ksg->ksg_cpumask)
+ return (1);
+ kseq_idle |= ksg->ksg_mask;
+ return (1);
+}
+
+static int
+sched_find_idle_cpu(int defcpu)
+{
+ struct pcpu *pcpu;
+ struct kseq_group *ksg;
+ struct kseq *ksq;
+ int cpu;
+
+ mtx_assert(&sched_lock, MA_OWNED);
+ ksq = KSEQ_CPU(defcpu);
+ ksg = ksq->ksq_group;
+ pcpu = pcpu_find(defcpu);
+ if (ksg->ksg_idlemask & pcpu->pc_cpumask)
+ return (defcpu);
+
+ /* Try to find a fully idled cpu. */
+ if (kseq_idle) {
+ cpu = ffs(kseq_idle);
+ if (cpu)
+ goto migrate;
+ }
+
+ /*
+ * If another cpu in this group has idled, assign a thread over
+ * to them after checking to see if there are idled groups.
+ */
+ if (ksg->ksg_idlemask) {
+ cpu = ffs(ksg->ksg_idlemask);
+ if (cpu)
+ goto migrate;
+ }
+ return (defcpu);
+
+migrate:
+ /*
+ * Now that we've found an idle CPU, migrate the thread.
+ */
+ cpu--;
+ return (cpu);
+}
+
+static int
+sched_find_idlest_cpu(struct kse *ke, int cpu)
+{
+ static unsigned stage_cpu;
+
+ struct kseq_group *ksg;
+ struct kseq *ksq;
+ int load, min_load = INT_MAX;
+ int first = 1;
+ int idlest = -1;
+ int i, cnt;
+
+ (void)ke;
+
+ if (__predict_false(smp_started == 0))
+ return (cpu);
+
+ first = 1;
+ i = (stage_cpu++) % (ksg_maxid + 1);
+ for (cnt = 0; cnt <= ksg_maxid; cnt++) {
+ ksg = KSEQ_GROUP(i);
+ LIST_FOREACH(ksq, &ksg->ksg_members, ksq_siblings) {
+ load = kseq_source_load(ksq);
+ if (first || load < min_load) {
+ first = 0;
+ load = min_load;
+ idlest = KSEQ_ID(ksq);
+ }
+ }
+ if (++i > ksg_maxid)
+ i = 0;
+ }
+ return (idlest);
+}
+
+static void
+migrated_setup(void *dummy)
+{
+ struct kseq *kseq;
+ struct proc *p;
+ struct thread *td;
+ int i, error;
+
+ for (i = 0; i < MAXCPU; i++) {
+ if (CPU_ABSENT(i))
+ continue;
+ kseq = &kseq_cpu[i];
+ error = kthread_create(migrated, kseq, &p, RFSTOPPED, 0,
+ "migrated%d", i);
+ if (error)
+ panic("can not create migration thread");
+ PROC_LOCK(p);
+ p->p_flag |= P_NOLOAD;
+ mtx_lock_spin(&sched_lock);
+ td = FIRST_THREAD_IN_PROC(p);
+ td->td_kse->ke_flags |= KEF_BOUND;
+ td->td_kse->ke_cpu = i;
+ kseq->ksq_migrated = td;
+ sched_class(td->td_ksegrp, PRI_ITHD);
+ td->td_kse->ke_runq = kseq->ksq_curr;
+ sched_prio(td, PRI_MIN);
+ SLOT_USE(td->td_ksegrp);
+ kseq_runq_add(kseq, td->td_kse, 0);
+ td->td_kse->ke_state = KES_ONRUNQ;
+ mtx_unlock_spin(&sched_lock);
+ PROC_UNLOCK(p);
+ }
+}
+
+static void
+migrated(void *dummy)
+{
+ struct thread *td = curthread;
+ struct kseq *kseq = KSEQ_SELF();
+ struct kse *ke;
+
+ mtx_lock_spin(&sched_lock);
+ for (;;) {
+ while ((ke = TAILQ_FIRST(&kseq->ksq_migrateq)) != NULL) {
+ TAILQ_REMOVE(&kseq->ksq_migrateq, ke, ke_procq);
+ kseq_load_rem(kseq, ke);
+ ke->ke_flags &= ~KEF_MIGRATING;
+ ke->ke_cpu = ke->ke_tocpu;
+ setrunqueue(ke->ke_thread, SRQ_BORING);
+ }
+ TD_SET_IWAIT(td);
+ mi_switch(SW_VOL, NULL);
+ }
+ mtx_unlock_spin(&sched_lock);
+}
+#else
+
+static inline void
+sched_balance_tick(int my_cpu, int idle)
+{
+}
+
+#endif /* SMP */
+
+
+static void
+kseq_setup(struct kseq *kseq)
+{
+ krunq_init(&kseq->ksq_timeshare[0]);
+ krunq_init(&kseq->ksq_timeshare[1]);
+ krunq_init(&kseq->ksq_idle);
+ kseq->ksq_curr = &kseq->ksq_timeshare[0];
+ kseq->ksq_next = &kseq->ksq_timeshare[1];
+ kseq->ksq_best_expired_nice = 21;
+#ifdef SMP
+ TAILQ_INIT(&kseq->ksq_migrateq);
+#endif
+}
+
+static void
+sched_setup(void *dummy)
+{
+#ifdef SMP
+ int i;
+ int t;
+#endif
+
+ /*
+ * To avoid divide-by-zero, we set realstathz a dummy value
+ * in case which sched_clock() called before sched_initticks().
+ */
+ realstathz = hz;
+ min_timeslice = MAX(5 * hz / 1000, 1);
+ def_timeslice = MAX(100 * hz / 1000, 1);
+ granularity = MAX(10 * hz / 1000, 1);
+
+#ifdef SMP
+ t = ticks;
+ balance_tick = t;
+ /*
+ * Initialize the kseqs.
+ */
+ for (i = 0; i < MAXCPU; i++) {
+ struct kseq *ksq;
+
+ ksq = &kseq_cpu[i];
+ kseq_setup(&kseq_cpu[i]);
+ }
+ if (smp_topology == NULL || ignore_topology) {
+ struct kseq_group *ksg;
+ struct kseq *ksq;
+ int cpus;
+
+ for (cpus = 0, i = 0; i < MAXCPU; i++) {
+ if (CPU_ABSENT(i))
+ continue;
+ ksq = &kseq_cpu[i];
+ ksg = &kseq_groups[cpus];
+ /*
+ * Setup a kseq group with one member.
+ */
+ ksq->ksq_group = ksg;
+ ksg->ksg_cpus = 1;
+ ksg->ksg_idlemask = 0;
+ ksg->ksg_cpumask = ksg->ksg_mask = 1 << i;
+ ksg->ksg_balance_tick = t;
+ LIST_INIT(&ksg->ksg_members);
+ LIST_INSERT_HEAD(&ksg->ksg_members, ksq, ksq_siblings);
+ cpus++;
+ }
+ ksg_maxid = cpus - 1;
+ } else {
+ struct kseq_group *ksg;
+ struct cpu_group *cg;
+ int j;
+
+ for (i = 0; i < smp_topology->ct_count; i++) {
+ cg = &smp_topology->ct_group[i];
+ ksg = &kseq_groups[i];
+ /*
+ * Initialize the group.
+ */
+ ksg->ksg_idlemask = 0;
+ ksg->ksg_cpus = cg->cg_count;
+ ksg->ksg_cpumask = cg->cg_mask;
+ LIST_INIT(&ksg->ksg_members);
+ /*
+ * Find all of the group members and add them.
+ */
+ for (j = 0; j < MAXCPU; j++) {
+ if ((cg->cg_mask & (1 << j)) != 0) {
+ if (ksg->ksg_mask == 0)
+ ksg->ksg_mask = 1 << j;
+ kseq_cpu[j].ksq_group = ksg;
+ LIST_INSERT_HEAD(&ksg->ksg_members,
+ &kseq_cpu[j], ksq_siblings);
+ }
+ }
+ ksg->ksg_balance_tick = t;
+ }
+ ksg_maxid = smp_topology->ct_count - 1;
+ }
+#else
+ kseq_setup(KSEQ_SELF());
+#endif
+ mtx_lock_spin(&sched_lock);
+ kseq_load_add(KSEQ_SELF(), &kse0);
+ mtx_unlock_spin(&sched_lock);
+}
+
+/* ARGSUSED */
+static void
+sched_initticks(void *dummy)
+{
+ mtx_lock_spin(&sched_lock);
+ realstathz = stathz ? stathz : hz;
+ mtx_unlock_spin(&sched_lock);
+}
+
+/*
+ * Very early in the boot some setup of scheduler-specific
+ * parts of proc0 and of soem scheduler resources needs to be done.
+ * Called from:
+ * proc0_init()
+ */
+void
+schedinit(void)
+{
+ /*
+ * Set up the scheduler specific parts of proc0.
+ */
+ proc0.p_sched = NULL; /* XXX */
+ ksegrp0.kg_sched = &kg_sched0;
+ thread0.td_sched = &kse0;
+ kse0.ke_thread = &thread0;
+ kse0.ke_state = KES_THREAD;
+ kse0.ke_slice = 100;
+ kg_sched0.skg_concurrency = 1;
+ kg_sched0.skg_avail_opennings = 0; /* we are already running */
+}
+
+/*
+ * This is only somewhat accurate since given many processes of the same
+ * priority they will switch when their slices run out, which will be
+ * at most SCHED_SLICE_MAX.
+ */
+int
+sched_rr_interval(void)
+{
+ return (def_timeslice);
+}
+
+static void
+sched_pctcpu_update(struct kse *ke)
+{
+ /*
+ * Adjust counters and watermark for pctcpu calc.
+ */
+ if (ke->ke_ltick > ticks - SCHED_CPU_TICKS) {
+ /*
+ * Shift the tick count out so that the divide doesn't
+ * round away our results.
+ */
+ ke->ke_ticks <<= 10;
+ ke->ke_ticks = (ke->ke_ticks / (ticks - ke->ke_ftick)) *
+ SCHED_CPU_TICKS;
+ ke->ke_ticks >>= 10;
+ } else
+ ke->ke_ticks = 0;
+ ke->ke_ltick = ticks;
+ ke->ke_ftick = ke->ke_ltick - SCHED_CPU_TICKS;
+}
+
+void
+sched_thread_priority(struct thread *td, u_char prio)
+{
+ struct kse *ke;
+
+ ke = td->td_kse;
+ mtx_assert(&sched_lock, MA_OWNED);
+ if (__predict_false(td->td_priority == prio))
+ return;
+
+ if (TD_ON_RUNQ(td)) {
+ /*
+ * If the priority has been elevated due to priority
+ * propagation, we may have to move ourselves to a new
+ * queue. We still call adjustrunqueue below in case kse
+ * needs to fix things up.
+ */
+ if (prio < td->td_priority && ke->ke_runq != NULL &&
+ ke->ke_runq != KSEQ_CPU(ke->ke_cpu)->ksq_curr) {
+ krunq_remove(ke->ke_runq, ke);
+ ke->ke_runq = KSEQ_CPU(ke->ke_cpu)->ksq_curr;
+ krunq_add(ke->ke_runq, ke, 0);
+ }
+ /*
+ * Hold this kse on this cpu so that sched_prio() doesn't
+ * cause excessive migration. We only want migration to
+ * happen as the result of a wakeup.
+ */
+ ke->ke_flags |= KEF_HOLD;
+ adjustrunqueue(td, prio);
+ ke->ke_flags &= ~KEF_HOLD;
+ } else
+ td->td_priority = prio;
+}
+
+/*
+ * 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)
+ base_pri = td->td_ksegrp->kg_user_pri;
+ else
+ base_pri = td->td_base_pri;
+ if (prio >= base_pri) {
+ td->td_flags &= ~TDF_BORROWING;
+ sched_thread_priority(td, base_pri);
+ } else
+ sched_lend_prio(td, prio);
+}
+
+void
+sched_prio(struct thread *td, u_char prio)
+{
+ u_char oldprio;
+
+ /* First, update the base priority. */
+ td->td_base_pri = prio;
+
+ /*
+ * If the thread is borrowing another thread's priority, don't
+ * 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);
+}
+
+void
+sched_switch(struct thread *td, struct thread *newtd, int flags)
+{
+ struct kseq *ksq;
+ struct kse *ke;
+ struct ksegrp *kg;
+ uint64_t now;
+
+ mtx_assert(&sched_lock, MA_OWNED);
+
+ now = sched_timestamp();
+ ke = td->td_kse;
+ kg = td->td_ksegrp;
+ ksq = KSEQ_SELF();
+
+ td->td_lastcpu = td->td_oncpu;
+ td->td_oncpu = NOCPU;
+ td->td_flags &= ~TDF_NEEDRESCHED;
+ td->td_owepreempt = 0;
+
+ /*
+ * If the KSE has been assigned it may be in the process of switching
+ * to the new cpu. This is the case in sched_bind().
+ */
+ if (__predict_false(td == PCPU_GET(idlethread))) {
+ TD_SET_CAN_RUN(td);
+ } else if (__predict_false(ke->ke_flags & KEF_MIGRATING)) {
+ SLOT_RELEASE(td->td_ksegrp);
+ } else {
+ /* We are ending our run so make our slot available again */
+ SLOT_RELEASE(td->td_ksegrp);
+ kseq_load_rem(ksq, ke);
+ if (TD_IS_RUNNING(td)) {
+ /*
+ * Don't allow the thread to migrate
+ * from a preemption.
+ */
+ ke->ke_flags |= KEF_HOLD;
+ setrunqueue(td, (flags & SW_PREEMPT) ?
+ SRQ_OURSELF|SRQ_YIELDING|SRQ_PREEMPTED :
+ SRQ_OURSELF|SRQ_YIELDING);
+ ke->ke_flags &= ~KEF_HOLD;
+ } else if ((td->td_proc->p_flag & P_HADTHREADS) &&
+ (newtd == NULL || newtd->td_ksegrp != td->td_ksegrp))
+ /*
+ * We will not be on the run queue.
+ * So we must be sleeping or similar.
+ * Don't use the slot if we will need it
+ * for newtd.
+ */
+ slot_fill(td->td_ksegrp);
+ }
+
+ if (newtd != NULL) {
+ /*
+ * If we bring in a thread account for it as if it had been
+ * added to the run queue and then chosen.
+ */
+ newtd->td_kse->ke_flags |= KEF_DIDRUN;
+ TD_SET_RUNNING(newtd);
+ kseq_load_add(KSEQ_SELF(), newtd->td_kse);
+ /*
+ * XXX When we preempt, we've already consumed a slot because
+ * we got here through sched_add(). However, newtd can come
+ * from thread_switchout() which can't SLOT_USE() because
+ * the SLOT code is scheduler dependent. We must use the
+ * slot here otherwise.
+ */
+ if ((flags & SW_PREEMPT) == 0)
+ SLOT_USE(newtd->td_ksegrp);
+ newtd->td_kse->ke_timestamp = now;
+ } else
+ newtd = choosethread();
+ if (td != newtd) {
+ sched_update_runtime(ke, now);
+ ke->ke_lastran = now;
+
+#ifdef HWPMC_HOOKS
+ if (PMC_PROC_IS_USING_PMCS(td->td_proc))
+ PMC_SWITCH_CONTEXT(td, PMC_FN_CSW_OUT);
+#endif
+ cpu_switch(td, newtd);
+#ifdef HWPMC_HOOKS
+ if (PMC_PROC_IS_USING_PMCS(td->td_proc))
+ PMC_SWITCH_CONTEXT(td, PMC_FN_CSW_IN);
+#endif
+ }
+
+ sched_lock.mtx_lock = (uintptr_t)td;
+
+ td->td_oncpu = PCPU_GET(cpuid);
+}
+
+void
+sched_nice(struct proc *p, int nice)
+{
+ struct ksegrp *kg;
+ struct thread *td;
+
+ PROC_LOCK_ASSERT(p, MA_OWNED);
+ mtx_assert(&sched_lock, MA_OWNED);
+ p->p_nice = nice;
+ FOREACH_KSEGRP_IN_PROC(p, kg) {
+ if (kg->kg_pri_class == PRI_TIMESHARE) {
+ kg->kg_user_pri = sched_calc_pri(kg);
+ FOREACH_THREAD_IN_GROUP(kg, td)
+ td->td_flags |= TDF_NEEDRESCHED;
+ }
+ }
+}
+
+void
+sched_sleep(struct thread *td)
+{
+ struct kse *ke;
+
+ mtx_assert(&sched_lock, MA_OWNED);
+ ke = td->td_kse;
+ if (td->td_flags & TDF_SINTR)
+ ke->ke_activated = 0;
+ else
+ ke->ke_activated = -1;
+ ke->ke_flags |= KEF_SLEEP;
+}
+
+void
+sched_wakeup(struct thread *td)
+{
+ struct kse *ke;
+ struct ksegrp *kg;
+ struct kseq *kseq, *mykseq;
+ uint64_t now;
+
+ mtx_assert(&sched_lock, MA_OWNED);
+ ke = td->td_kse;
+ kg = td->td_ksegrp;
+ kseq = KSEQ_CPU(ke->ke_cpu);
+ mykseq = KSEQ_SELF();
+ if (ke->ke_flags & KEF_SLEEP) {
+ ke->ke_flags &= ~KEF_SLEEP;
+ if (sched_is_timeshare(kg)) {
+ now = sched_timestamp();
+ sched_commit_runtime(ke);
+#ifdef SMP
+ if (kseq != mykseq)
+ now = now - mykseq->ksq_last_timestamp +
+ kseq->ksq_last_timestamp;
+#endif
+ kg->kg_user_pri = sched_recalc_pri(ke, now);
+ }
+ }
+ ke->ke_flags &= ~KEF_NEXTRQ;
+ setrunqueue(td, SRQ_BORING);
+}
+
+/*
+ * Penalize the parent for creating a new child and initialize the child's
+ * priority.
+ */
+void
+sched_fork(struct thread *td, struct thread *childtd)
+{
+
+ mtx_assert(&sched_lock, MA_OWNED);
+ sched_fork_ksegrp(td, childtd->td_ksegrp);
+ sched_fork_thread(td, childtd);
+}
+
+void
+sched_fork_ksegrp(struct thread *td, struct ksegrp *child)
+{
+ struct ksegrp *kg = td->td_ksegrp;
+
+ mtx_assert(&sched_lock, MA_OWNED);
+ child->kg_slptime = kg->kg_slptime * CHILD_WEIGHT / 100;
+ if (child->kg_pri_class == PRI_TIMESHARE)
+ child->kg_user_pri = sched_calc_pri(child);
+ kg->kg_slptime = kg->kg_slptime * PARENT_WEIGHT / 100;
+}
+
+void
+sched_fork_thread(struct thread *td, struct thread *child)
+{
+ struct kse *ke;
+ struct kse *ke2;
+
+ sched_newthread(child);
+
+ ke = td->td_kse;
+ ke2 = child->td_kse;
+#ifdef SMP
+ ke2->ke_cpu = sched_find_idlest_cpu(ke, PCPU_GET(cpuid));
+#else
+ ke2->ke_cpu = ke->ke_cpu;
+#endif
+ ke2->ke_slice = (ke->ke_slice + 1) >> 1;
+ ke2->ke_flags |= KEF_FIRST_SLICE;
+ ke2->ke_activated = 0;
+ ke2->ke_timestamp = sched_timestamp();
+ ke->ke_slice >>= 1;
+ if (ke->ke_slice == 0) {
+ ke->ke_slice = 1;
+ sched_tick();
+ }
+
+ /* Grab our parents cpu estimation information. */
+ ke2->ke_ticks = ke->ke_ticks;
+ ke2->ke_ltick = ke->ke_ltick;
+ ke2->ke_ftick = ke->ke_ftick;
+}
+
+void
+sched_class(struct ksegrp *kg, int class)
+{
+ struct kseq *kseq;
+ struct kse *ke;
+ struct thread *td;
+ int nclass;
+ int oclass;
+
+ mtx_assert(&sched_lock, MA_OWNED);
+ if (kg->kg_pri_class == class)
+ return;
+
+ nclass = PRI_BASE(class);
+ oclass = PRI_BASE(kg->kg_pri_class);
+ FOREACH_THREAD_IN_GROUP(kg, td) {
+ ke = td->td_kse;
+
+ /* New thread does not have runq assigned */
+ if (ke->ke_runq == NULL)
+ continue;
+
+ kseq = KSEQ_CPU(ke->ke_cpu);
+ if (oclass == PRI_TIMESHARE)
+ kseq->ksq_load_timeshare--;
+ else if (oclass == PRI_IDLE)
+ kseq->ksq_load_idle--;
+
+ if (nclass == PRI_TIMESHARE)
+ kseq->ksq_load_timeshare++;
+ else if (nclass == PRI_IDLE)
+ kseq->ksq_load_idle++;
+ }
+
+ kg->kg_pri_class = class;
+}
+
+/*
+ * Return some of the child's priority and interactivity to the parent.
+ */
+void
+sched_exit(struct proc *p, struct thread *childtd)
+{
+ mtx_assert(&sched_lock, MA_OWNED);
+ sched_exit_thread(FIRST_THREAD_IN_PROC(p), childtd);
+ sched_exit_ksegrp(FIRST_KSEGRP_IN_PROC(p), childtd);
+}
+
+void
+sched_exit_ksegrp(struct ksegrp *parentkg, struct thread *td)
+{
+ if (td->td_ksegrp->kg_slptime < parentkg->kg_slptime) {
+ parentkg->kg_slptime = parentkg->kg_slptime /
+ (EXIT_WEIGHT) * (EXIT_WEIGHT - 1) +
+ td->td_ksegrp->kg_slptime / EXIT_WEIGHT;
+ }
+}
+
+void
+sched_exit_thread(struct thread *td, struct thread *childtd)
+{
+ struct kse *childke = childtd->td_kse;
+ struct kse *parentke = td->td_kse;
+
+ kseq_load_rem(KSEQ_CPU(childke->ke_cpu), childke);
+ sched_update_runtime(childke, sched_timestamp());
+ sched_commit_runtime(childke);
+ if ((childke->ke_flags & KEF_FIRST_SLICE) &&
+ td->td_proc == childtd->td_proc->p_pptr) {
+ parentke->ke_slice += childke->ke_slice;
+ if (parentke->ke_slice > sched_timeslice(parentke))
+ parentke->ke_slice = sched_timeslice(parentke);
+ }
+}
+
+static int
+sched_starving(struct kseq *ksq, uint64_t now, struct kse *ke)
+{
+ uint64_t delta;
+
+ if (PROC_NICE(ke->ke_proc) > ksq->ksq_best_expired_nice)
+ return (1);
+ if (ksq->ksq_expired_timestamp == 0)
+ return (0);
+ delta = now - ksq->ksq_expired_timestamp;
+ if (delta > STARVATION_TIME * (ksq->ksq_load - ksq->ksq_load_idle))
+ return (1);
+ return (0);
+}
+
+/*
+ * An interactive thread has smaller time slice granularity,
+ * a cpu hog can have larger granularity.
+ */
+static inline int
+sched_timeslice_split(struct kse *ke)
+{
+ int score, g;
+
+ score = (int)(MAX_SCORE - CURRENT_SCORE(ke->ke_ksegrp));
+ if (score == 0)
+ score = 1;
+#ifdef SMP
+ g = granularity * ((1 << score) - 1) * smp_cpus;
+#else
+ g = granularity * ((1 << score) - 1);
+#endif
+ return (ke->ke_slice >= g && ke->ke_slice % g == 0);
+}
+
+void
+sched_tick(void)
+{
+ struct thread *td;
+ struct proc *p;
+ struct kse *ke;
+ struct ksegrp *kg;
+ struct kseq *kseq;
+ uint64_t now;
+ int cpuid;
+ int class;
+
+ mtx_assert(&sched_lock, MA_OWNED);
+
+ td = curthread;
+ ke = td->td_kse;
+ kg = ke->ke_ksegrp;
+ p = ke->ke_proc;
+ class = PRI_BASE(kg->kg_pri_class);
+ now = sched_timestamp();
+ cpuid = PCPU_GET(cpuid);
+ kseq = KSEQ_CPU(cpuid);
+ kseq->ksq_last_timestamp = now;
+
+ if (class == PRI_IDLE) {
+ int idle_td = (curthread == PCPU_GET(idlethread));
+ /*
+ * Processes of equal idle priority are run round-robin.
+ */
+ if (!idle_td && --ke->ke_slice <= 0) {
+ ke->ke_slice = def_timeslice;
+ td->td_flags |= TDF_NEEDRESCHED;
+ }
+ sched_balance_tick(cpuid, idle_td ? IDLE_IDLE : IDLE);
+ return;
+ }
+
+ if (ke->ke_flags & KEF_NEXTRQ) {
+ /* The thread was already scheduled off. */
+ curthread->td_flags |= TDF_NEEDRESCHED;
+ goto out;
+ }
+
+ if (class == PRI_REALTIME) {
+ /*
+ * Realtime scheduling, do round robin for RR class, FIFO
+ * is not affected.
+ */
+ if (PRI_NEED_RR(kg->kg_pri_class) && --ke->ke_slice <= 0) {
+ ke->ke_slice = def_timeslice;
+ curthread->td_flags |= TDF_NEEDRESCHED;
+ }
+ goto out;
+ }
+
+ /*
+ * Current, we skip kernel thread, though it may be classified as
+ * TIMESHARE.
+ */
+ if (class != PRI_TIMESHARE || (p->p_flag & P_KTHREAD) != 0)
+ goto out;
+
+ if (--ke->ke_slice <= 0) {
+ curthread->td_flags |= TDF_NEEDRESCHED;
+ sched_update_runtime(ke, now);
+ sched_commit_runtime(ke);
+ kg->kg_user_pri = sched_calc_pri(kg);
+ ke->ke_slice = sched_timeslice(ke);
+ ke->ke_flags &= ~KEF_FIRST_SLICE;
+ if (!kseq->ksq_expired_timestamp)
+ kseq->ksq_expired_timestamp = now;
+ if (!THREAD_IS_INTERACTIVE(ke) ||
+ sched_starving(kseq, now, ke)) {
+ /* The thead becomes cpu hog, schedule it off. */
+ ke->ke_flags |= KEF_NEXTRQ;
+ if (PROC_NICE(p) < kseq->ksq_best_expired_nice)
+ kseq->ksq_best_expired_nice = PROC_NICE(p);
+ }
+ } else {
+ /*
+ * Don't allow an interactive thread which has long timeslice
+ * to monopolize CPU, split the long timeslice into small
+ * chunks. This essentially does round-robin between
+ * interactive threads.
+ */
+ if (THREAD_IS_INTERACTIVE(ke) && sched_timeslice_split(ke))
+ curthread->td_flags |= TDF_NEEDRESCHED;
+ }
+
+out:
+ sched_balance_tick(cpuid, NOT_IDLE);
+}
+
+void
+sched_clock(struct thread *td)
+{
+ struct ksegrp *kg;
+ struct kse *ke;
+
+ mtx_assert(&sched_lock, MA_OWNED);
+ ke = td->td_kse;
+ kg = ke->ke_ksegrp;
+
+ /* Adjust ticks for pctcpu */
+ ke->ke_ticks++;
+ ke->ke_ltick = ticks;
+
+ /* Go up to one second beyond our max and then trim back down */
+ if (ke->ke_ftick + SCHED_CPU_TICKS + hz < ke->ke_ltick)
+ sched_pctcpu_update(ke);
+}
+
+int
+sched_runnable(void)
+{
+ struct kseq *kseq;
+
+ kseq = KSEQ_SELF();
+ if (krunq_findbit(kseq->ksq_curr) != -1 ||
+ krunq_findbit(kseq->ksq_next) != -1 ||
+ krunq_findbit(&kseq->ksq_idle) != -1)
+ return (1);
+ return (0);
+}
+
+void
+sched_userret(struct thread *td)
+{
+ struct ksegrp *kg;
+
+ KASSERT((td->td_flags & TDF_BORROWING) == 0,
+ ("thread with borrowed priority returning to userland"));
+ kg = td->td_ksegrp;
+ if (td->td_priority != kg->kg_user_pri) {
+ mtx_lock_spin(&sched_lock);
+ td->td_priority = kg->kg_user_pri;
+ td->td_base_pri = kg->kg_user_pri;
+ mtx_unlock_spin(&sched_lock);
+ }
+}
+
+struct kse *
+sched_choose(void)
+{
+ struct kseq *kseq;
+ struct kse *ke;
+
+ mtx_assert(&sched_lock, MA_OWNED);
+ kseq = KSEQ_SELF();
+#ifdef SMP
+restart:
+#endif
+ ke = kseq_choose(kseq);
+ if (ke) {
+#ifdef SMP
+ if (ke->ke_ksegrp->kg_pri_class == PRI_IDLE)
+ if (sched_idled(kseq, IDLE) == 0)
+ goto restart;
+#endif
+ kseq_runq_rem(kseq, ke);
+ ke->ke_state = KES_THREAD;
+ ke->ke_flags &= ~KEF_PREEMPTED;
+ ke->ke_timestamp = sched_timestamp();
+ return (ke);
+ }
+#ifdef SMP
+ if (sched_idled(kseq, IDLE_IDLE) == 0)
+ goto restart;
+#endif
+ return (NULL);
+}
+
+void
+sched_add(struct thread *td, int flags)
+{
+ struct kseq *ksq, *my_ksq;
+ struct ksegrp *kg;
+ struct kse *ke;
+ int preemptive;
+ int canmigrate;
+ int class;
+ int my_cpu;
+ int nextrq;
+#ifdef SMP
+ struct thread *td2;
+ struct pcpu *pcpu;
+ int cpu, new_cpu;
+ int load, my_load;
+#endif
+
+ mtx_assert(&sched_lock, MA_OWNED);
+ ke = td->td_kse;
+ kg = td->td_ksegrp;
+ KASSERT(ke->ke_state != KES_ONRUNQ,
+ ("sched_add: kse %p (%s) already in run queue", ke,
+ ke->ke_proc->p_comm));
+ KASSERT(ke->ke_proc->p_sflag & PS_INMEM,
+ ("sched_add: process swapped out"));
+ KASSERT(ke->ke_runq == NULL,
+ ("sched_add: KSE %p is still assigned to a run queue", ke));
+
+ canmigrate = 1;
+ preemptive = !(flags & SRQ_YIELDING);
+ class = PRI_BASE(kg->kg_pri_class);
+ my_cpu = PCPU_GET(cpuid);
+ my_ksq = KSEQ_CPU(my_cpu);
+ if (flags & SRQ_PREEMPTED)
+ ke->ke_flags |= KEF_PREEMPTED;
+ if ((ke->ke_flags & KEF_INTERNAL) == 0)
+ SLOT_USE(td->td_ksegrp);
+ nextrq = (ke->ke_flags & KEF_NEXTRQ);
+ ke->ke_flags &= ~(KEF_NEXTRQ | KEF_INTERNAL);
+
+#ifdef SMP
+ cpu = ke->ke_cpu;
+ canmigrate = kse_can_migrate(ke);
+ /*
+ * Don't migrate running threads here. Force the long term balancer
+ * to do it.
+ */
+ if (ke->ke_flags & KEF_HOLD) {
+ ke->ke_flags &= ~KEF_HOLD;
+ canmigrate = 0;
+ }
+
+ /*
+ * If this thread is pinned or bound, notify the target cpu.
+ */
+ if (!canmigrate)
+ goto activate_it;
+
+ if (class == PRI_ITHD) {
+ ke->ke_cpu = my_cpu;
+ goto activate_it;
+ }
+
+ if (ke->ke_cpu == my_cpu)
+ goto activate_it;
+
+ if (my_ksq->ksq_group->ksg_idlemask & PCPU_GET(cpumask)) {
+ ke->ke_cpu = my_cpu;
+ goto activate_it;
+ }
+
+ new_cpu = my_cpu;
+
+ load = kseq_source_load(KSEQ_CPU(cpu));
+ my_load = kseq_dest_load(my_ksq);
+ if ((my_load - load) * 100 < my_load * imbalance_pct2)
+ goto try_idle_cpu;
+ new_cpu = cpu;
+
+try_idle_cpu:
+ new_cpu = sched_find_idle_cpu(new_cpu);
+ ke->ke_cpu = new_cpu;
+
+activate_it:
+ if (ke->ke_cpu != cpu)
+ ke->ke_lastran = 0;
+#endif
+ ksq = KSEQ_CPU(ke->ke_cpu);
+ switch (class) {
+ case PRI_ITHD:
+ case PRI_REALTIME:
+ ke->ke_runq = ksq->ksq_curr;
+ break;
+ case PRI_TIMESHARE:
+ if ((td->td_flags & TDF_BORROWING) == 0 && nextrq)
+ ke->ke_runq = ksq->ksq_next;
+ else
+ ke->ke_runq = ksq->ksq_curr;
+ break;
+ case PRI_IDLE:
+ /*
+ * This is for priority prop.
+ */
+ if (td->td_priority < PRI_MIN_IDLE)
+ ke->ke_runq = ksq->ksq_curr;
+ else
+ ke->ke_runq = &ksq->ksq_idle;
+ break;
+ default:
+ panic("Unknown pri class.");
+ break;
+ }
+
+ if (ke->ke_runq == my_ksq->ksq_curr &&
+ td->td_priority < curthread->td_priority) {
+ curthread->td_flags |= TDF_NEEDRESCHED;
+ if (preemptive && maybe_preempt(td))
+ return;
+ if (curthread->td_ksegrp->kg_pri_class == PRI_IDLE)
+ td->td_owepreempt = 1;
+ }
+ ke->ke_state = KES_ONRUNQ;
+ kseq_runq_add(ksq, ke, flags);
+ kseq_load_add(ksq, ke);
+#ifdef SMP
+ pcpu = pcpu_find(ke->ke_cpu);
+ if (class != PRI_IDLE) {
+ if (kseq_idle & ksq->ksq_group->ksg_mask)
+ kseq_idle &= ~ksq->ksq_group->ksg_mask;
+ if (ksq->ksq_group->ksg_idlemask & pcpu->pc_cpumask)
+ ksq->ksq_group->ksg_idlemask &= ~pcpu->pc_cpumask;
+ }
+ if (ke->ke_cpu != my_cpu) {
+ td2 = pcpu->pc_curthread;
+ if (__predict_false(td2 == pcpu->pc_idlethread)) {
+ td2->td_flags |= TDF_NEEDRESCHED;
+ ipi_selected(pcpu->pc_cpumask, IPI_AST);
+ } else if (td->td_priority < td2->td_priority) {
+ if (class == PRI_ITHD || class == PRI_REALTIME ||
+ td2->td_ksegrp->kg_pri_class == PRI_IDLE)
+ ipi_selected(pcpu->pc_cpumask, IPI_PREEMPT);
+ else if ((td2->td_flags & TDF_NEEDRESCHED) == 0) {
+ td2->td_flags |= TDF_NEEDRESCHED;
+ ipi_selected(pcpu->pc_cpumask, IPI_AST);
+ }
+ }
+ }
+#endif
+}
+
+void
+sched_rem(struct thread *td)
+{
+ struct kseq *kseq;
+ struct kse *ke;
+
+ mtx_assert(&sched_lock, MA_OWNED);
+ ke = td->td_kse;
+ SLOT_RELEASE(td->td_ksegrp);
+ ke->ke_flags &= ~KEF_PREEMPTED;
+ KASSERT((ke->ke_state == KES_ONRUNQ),
+ ("sched_rem: KSE not on run queue"));
+
+ kseq = KSEQ_CPU(ke->ke_cpu);
+#ifdef SMP
+ if (ke->ke_flags & KEF_MIGRATING) {
+ ke->ke_flags &= ~KEF_MIGRATING;
+ kseq_load_rem(kseq, ke);
+ TAILQ_REMOVE(&kseq->ksq_migrateq, ke, ke_procq);
+ ke->ke_cpu = ke->ke_tocpu;
+ } else
+#endif
+ {
+ KASSERT((ke->ke_state == KES_ONRUNQ),
+ ("sched_rem: KSE not on run queue"));
+ kseq_runq_rem(kseq, ke);
+ kseq_load_rem(kseq, ke);
+ }
+ ke->ke_state = KES_THREAD;
+}
+
+fixpt_t
+sched_pctcpu(struct thread *td)
+{
+ fixpt_t pctcpu;
+ struct kse *ke;
+
+ pctcpu = 0;
+ ke = td->td_kse;
+ if (ke == NULL)
+ return (0);
+
+ mtx_lock_spin(&sched_lock);
+ if (ke->ke_ticks) {
+ int rtick;
+
+ /*
+ * Don't update more frequently than twice a second. Allowing
+ * this causes the cpu usage to decay away too quickly due to
+ * rounding errors.
+ */
+ if (ke->ke_ftick + SCHED_CPU_TICKS < ke->ke_ltick ||
+ ke->ke_ltick < (ticks - (hz / 2)))
+ sched_pctcpu_update(ke);
+ /* How many rtick per second ? */
+ rtick = MIN(ke->ke_ticks / SCHED_CPU_TIME, SCHED_CPU_TICKS);
+ pctcpu = (FSCALE * ((FSCALE * rtick)/realstathz)) >> FSHIFT;
+ }
+
+ ke->ke_proc->p_swtime = ke->ke_ltick - ke->ke_ftick;
+ mtx_unlock_spin(&sched_lock);
+
+ return (pctcpu);
+}
+
+void
+sched_bind(struct thread *td, int cpu)
+{
+ struct kseq *kseq;
+ struct kse *ke;
+
+ mtx_assert(&sched_lock, MA_OWNED);
+ ke = td->td_kse;
+ ke->ke_flags |= KEF_BOUND;
+#ifdef SMP
+ if (PCPU_GET(cpuid) == cpu)
+ return;
+ kseq = KSEQ_SELF();
+ ke->ke_flags |= KEF_MIGRATING;
+ ke->ke_tocpu = cpu;
+ TAILQ_INSERT_TAIL(&kseq->ksq_migrateq, ke, ke_procq);
+ if (kseq->ksq_migrated) {
+ if (TD_AWAITING_INTR(kseq->ksq_migrated)) {
+ TD_CLR_IWAIT(kseq->ksq_migrated);
+ setrunqueue(kseq->ksq_migrated, SRQ_YIELDING);
+ }
+ }
+ /* When we return from mi_switch we'll be on the correct cpu. */
+ mi_switch(SW_VOL, NULL);
+#else
+ (void)kseq;
+#endif
+}
+
+void
+sched_unbind(struct thread *td)
+{
+ mtx_assert(&sched_lock, MA_OWNED);
+ td->td_kse->ke_flags &= ~KEF_BOUND;
+}
+
+int
+sched_is_bound(struct thread *td)
+{
+ mtx_assert(&sched_lock, MA_OWNED);
+ return (td->td_kse->ke_flags & KEF_BOUND);
+}
+
+int
+sched_load(void)
+{
+#ifdef SMP
+ int total;
+ int i;
+
+ total = 0;
+ for (i = 0; i < MAXCPU; i++)
+ total += KSEQ_CPU(i)->ksq_sysload;
+ return (total);
+#else
+ return (KSEQ_SELF()->ksq_sysload);
+#endif
+}
+
+int
+sched_sizeof_ksegrp(void)
+{
+ return (sizeof(struct ksegrp) + sizeof(struct kg_sched));
+}
+
+int
+sched_sizeof_proc(void)
+{
+ return (sizeof(struct proc));
+}
+
+int
+sched_sizeof_thread(void)
+{
+ return (sizeof(struct thread) + sizeof(struct td_sched));
+}
+#define KERN_SWITCH_INCLUDE 1
+#include "kern/kern_switch.c"
diff --git a/sys/kern/sched_ule.c b/sys/kern/sched_ule.c
index dff87f0f5121..a3cccfe89e52 100644
--- a/sys/kern/sched_ule.c
+++ b/sys/kern/sched_ule.c
@@ -2007,5 +2007,10 @@ sched_sizeof_thread(void)
{
return (sizeof(struct thread) + sizeof(struct td_sched));
}
+
+void
+sched_tick(void)
+{
+}
#define KERN_SWITCH_INCLUDE 1
#include "kern/kern_switch.c"
diff --git a/sys/sys/sched.h b/sys/sys/sched.h
index ff555d21ef83..5c5825fff45f 100644
--- a/sys/sys/sched.h
+++ b/sys/sys/sched.h
@@ -78,6 +78,7 @@ void sched_wakeup(struct thread *td);
void sched_add(struct thread *td, int flags);
void sched_clock(struct thread *td);
void sched_rem(struct thread *td);
+void sched_tick(void);
/*
* Binding makes cpu affinity permanent while pinning is used to temporarily