/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
*/
#include <sys/pool_pset.h>
/*
* Calling pool_lock() protects the pools configuration, which includes
* CPU partitions. cpu_lock protects the CPU partition list, and prevents
* partitions from being created or destroyed while the lock is held.
* The lock ordering with respect to related locks is:
*
* pool_lock() ---> cpu_lock ---> pidlock --> p_lock
*
* Blocking memory allocations may be made while holding "pool_lock"
* or cpu_lock.
*/
/*
* The cp_default partition is allocated statically, but its lgroup load average
* (lpl) list is allocated dynamically after kmem subsystem is initialized. This
* saves some memory since the space allocated reflects the actual number of
* lgroups supported by the platform. The lgrp facility provides a temporary
* space to hold lpl information during system bootstrap.
*/
/*
* Need to limit total number of partitions to avoid slowing down the
* clock code too much. The clock code traverses the list of
* partitions and needs to be able to execute in a reasonable amount
* of time (less than 1/hz seconds). The maximum is sized based on
* max_ncpus so it shouldn't be a problem unless there are large
* numbers of empty partitions.
*/
/*
* Processor sets and CPU partitions are different but related concepts.
* A processor set is a user-level abstraction allowing users to create
* sets of CPUs and bind threads exclusively to those sets. A CPU
* partition is a kernel dispatcher object consisting of a set of CPUs
* and a global dispatch queue. The processor set abstraction is
* implemented via a CPU partition, and currently there is a 1-1
* mapping between processor sets and partitions (excluding the default
* partition, which is not visible as a processor set). Hence, the
* numbering for processor sets and CPU partitions is identical. This
* may not always be true in the future, and these macros could become
* less trivial if we support e.g. a processor set containing multiple
* CPU partitions.
*/
/*
* Find a CPU partition given a processor set ID.
*/
static cpupart_t *
{
/* default partition not visible as a processor set */
if (psid == CP_DEFAULT)
return (NULL);
cp = cp_list_head;
do {
return (cp);
} while (cp != cp_list_head);
return (NULL);
}
/*
* Find a CPU partition given a processor set ID if the processor set
* should be visible from the calling zone.
*/
{
return (NULL);
return (cp);
}
static int
{
if (rw == KSTAT_WRITE)
return (EACCES);
(16 - FSHIFT);
(16 - FSHIFT);
(16 - FSHIFT);
return (0);
}
static void
{
/*
* We have a bit of a chicken-egg problem since this code will
* get called to create the kstats for CP_DEFAULT before the
* pools framework gets initialized. We circumvent the problem
* by special-casing cp_default.
*/
else
}
}
/*
* Initialize the cpupart's lgrp partions (lpls)
*/
static void
{
int i, sz;
for (i = 0; i < sz; i++) {
/*
* The last entry of the lpl's resource set is always NULL
* by design (to facilitate iteration)...hence the "oversizing"
* by 1.
*/
}
}
/*
* Teardown the cpupart's lgrp partitions
*/
static void
{
int i, sz;
for (i = 0; i < cp->cp_nlgrploads; i++) {
}
}
/*
* Initialize the default partition and kpreempt disp queue.
*/
void
{
lgrp_id_t i;
cp_default.cp_gen = 0;
for (i = 0; i < S_LOADAVG_SZ; i++) {
}
cp_numparts = 1;
if (cp_max_numparts == 0) /* allow for /etc/system tuning */
/*
* Allocate space for cp_default list of lgrploads
*/
/*
* The initial lpl topology is created in a special lpl list
* lpl_bootstrap. It should be copied to cp_default.
* NOTE: lpl_topo_bootstrap() also updates CPU0 cpu_lpl pointer to point
* to the correct lpl in the cp_default.cp_lgrploads list.
*/
cp_numparts_nonempty = 1;
/*
* Set t0's home
*/
}
static int
{
kthread_t *t;
proc_t *p;
int lgrp_diff_lpl;
int ret;
/*
* Don't need to do anything.
*/
return (0);
}
if (!disp_bound_partition(cp, 0)) {
/*
* Don't need to move threads if there are no threads in
* the partition. Note that threads can't enter the
* partition while we're holding cpu_lock.
*/
move_threads = 0;
/*
* The last CPU is removed from a partition which has threads
* running in it. Some of these threads may be bound to this
* CPU.
*
* Attempt to unbind threads from the CPU and from the processor
* set. Note that no threads should be bound to this CPU since
* cpupart_move_threads will refuse to move bound threads to
* other CPUs.
*/
if (!disp_bound_partition(cp, 0)) {
/*
* No bound threads in this partition any more
*/
move_threads = 0;
} else {
/*
* There are still threads bound to the partition
*/
return (EBUSY);
}
}
/*
* If forced flag is set unbind any threads from this CPU.
* Otherwise unbind soft-bound threads only.
*/
return (ret);
}
/*
* Stop further threads weak binding to this cpu.
*/
cpu_inmotion = cp;
membar_enter();
/*
* Notify the Processor Groups subsystem that the CPU
* will be moving cpu partitions. This is done before
* CPUs are paused to provide an opportunity for any
* needed memory allocations.
*/
if (move_threads) {
int loop_count;
/*
* Check for threads strong or weak bound to this CPU.
*/
if (loop_count >= 5) {
cpu_inmotion = NULL;
return (EBUSY); /* some threads still bound */
}
delay(1);
}
}
/*
* Before we actually start changing data structures, notify
* the cyclic subsystem that we want to move this CPU out of its
* partition.
*/
if (!cyclic_move_out(cp)) {
/*
* This CPU must be the last CPU in a processor set with
* a bound cyclic.
*/
cpu_inmotion = NULL;
return (EBUSY);
}
if (move_threads) {
/*
* The thread on cpu before the pause thread may have read
* cpu_inmotion before we raised the barrier above. Check
* again.
*/
start_cpus();
goto again;
}
}
/*
* Now that CPUs are paused, let the PG subsystem perform
* any necessary data structure updates.
*/
/* save this cpu's lgroup -- it'll be the same in the new partition */
/*
* let the lgroup framework know cp has left the partition
*/
/* move out of old partition */
}
} else {
ASSERT(cp_numparts_nonempty != 0);
}
/* move into new partition */
ASSERT(cp_numparts_nonempty != 0);
} else {
}
/*
* let the lgroup framework know cp has entered the partition
*/
/*
* If necessary, move threads off processor.
*/
if (move_threads) {
/*
* Walk thru the active process list to look for
* threads that need to have a new home lgroup,
* or the last CPU they run on is the same CPU
* being moved out of the partition.
*/
t = p->p_tlist;
if (t == NULL)
continue;
lgrp_diff_lpl = 0;
do {
/*
* Update the count of how many threads are
* in this CPU's lgroup but have a different lpl
*/
/*
* If the lgroup that t is assigned to no
* longer has any CPUs in t's partition,
* we'll have to choose a new lgroup for t.
*/
t->t_cpupart)) {
lgrp_choose(t, t->t_cpupart), 0);
}
/*
* make sure lpl points to our own partition
*/
t->t_cpupart->cp_nlgrploads));
/* Update CPU last ran on if it was this CPU */
t->t_bound_cpu != cp) {
}
t = t->t_forw;
} while (t != p->p_tlist);
/*
* Didn't find any threads in the same lgroup as this
* CPU with a different lpl, so remove the lgroup from
* the process lgroup bitmask.
*/
if (lgrp_diff_lpl)
}
/*
* Walk thread list looking for threads that need to be
* rehomed, since there are some threads that are not in
* their process's p_tlist.
*/
t = curthread;
do {
/*
* If the lgroup that t is assigned to no
* longer has any CPUs in t's partition,
* we'll have to choose a new lgroup for t.
* Also, choose best lgroup for home when
* thread has specified lgroup affinities,
* since there may be an lgroup with more
* affinity available after moving CPUs
* around.
*/
t->t_cpupart) || t->t_lgrp_affinity) {
}
/* make sure lpl points to our own partition */
t->t_cpupart->cp_nlgrploads));
/* Update CPU last ran on if it was this CPU */
t->t_bound_cpu != cp) {
}
t = t->t_next;
} while (t != curthread);
/*
* Clear off the CPU's run queue, and the kp queue if the
* partition is now empty.
*/
/*
* Make cp switch to a thread from the new partition.
*/
}
cpu_inmotion = NULL;
start_cpus();
/*
* Let anyone interested know that cpu has been added to the set.
*/
/*
* Now let the cyclic subsystem know that it can reshuffle cyclics
* bound to the new processor set.
*/
return (0);
}
/*
* Check if thread can be moved to a new cpu partition. Called by
* cpupart_move_thread() and pset_bind_start().
*/
int
{
/*
* CPU-bound threads can't be moved.
*/
if (!ignore) {
return (EBUSY);
}
return (EINVAL); /* For now, sysdc threads can't move */
}
return (0);
}
/*
* Move thread to new partition. If ignore is non-zero, then CPU
* bindings should be ignored (this is used when destroying a
* partition).
*/
static int
{
int ret;
return (EINVAL);
/*
* Check for errors first.
*/
return (ret);
}
/* move the thread */
/*
* Make the thread switch to the new partition.
*/
/*
* Leave the thread on the same lgroup if possible; otherwise
* choose a new lgroup for it. In either case, update its
* t_lpl.
*/
/*
* The thread's lgroup has CPUs in the thread's new
* partition, so the thread can stay assigned to the
* same lgroup. Update its t_lpl to point to the
* lpl_t for its lgroup in its new partition.
*/
} else {
/*
* The thread's lgroup has no cpus in its new
* partition or it has specified lgroup affinities,
* so choose the best lgroup for the thread and
* assign it to that lgroup.
*/
1);
}
/*
* make sure lpl points to our own partition
*/
}
}
/*
* Our binding has changed; set TP_CHANGEBIND.
*/
return (0); /* success */
}
/*
* This function binds a thread to a partition. Must be called with the
* p_lock of the containing process held (to keep the thread from going
* away), and thus also with cpu_lock held (since cpu_lock must be
* acquired before p_lock). If ignore is non-zero, then CPU bindings
* should be ignored (this is used when destroying a partition).
*/
int
void *zonebuf)
{
ASSERT(pool_lock_held());
newpp = &cp_default;
else {
return (EINVAL);
}
}
}
/*
* Create a new partition. On MP systems, this also allocates a
* kpreempt disp queue for that partition.
*/
int
{
ASSERT(pool_lock_held());
KM_SLEEP);
if (cp_numparts == cp_max_numparts) {
return (ENOMEM);
}
cp_numparts++;
/* find the next free partition ID */
cp_id_next++;
/*
* Initialize and size the partition's bitset of halted CPUs.
*/
/*
* Pause all CPUs while changing the partition list, to make sure
* the clock thread (which traverses the list without holding
* cpu_lock) isn't running.
*/
start_cpus();
return (0);
}
/*
* Move threads from specified partition to cp_default. If `force' is specified,
* move all threads, otherwise move only soft-bound threads.
*/
static int
{
kthread_t *t;
proc_t *p;
int err = 0;
ASSERT(pool_lock_held());
return (EINVAL);
}
/*
* Pre-allocate enough buffers for FSS for all active projects and
* for all active zones on the system. Unused buffers will be
* freed later by fss_freebuf().
*/
t = curthread;
do {
if (t->t_bind_pset == psid) {
mutex_enter(&p->p_lock);
if (ttoproc(t) != p) {
/*
* lwp_exit has changed this thread's process
* pointer before we grabbed its p_lock.
*/
mutex_exit(&p->p_lock);
goto again;
}
/*
* Can only unbind threads which have revocable binding
* unless force unbinding requested.
*/
if (unbind_all || TB_PSET_IS_SOFT(t)) {
if (err) {
mutex_exit(&p->p_lock);
return (err);
}
t->t_bind_pset = PS_NONE;
}
mutex_exit(&p->p_lock);
}
t = t->t_next;
} while (t != curthread);
return (err);
}
/*
* Destroy a partition.
*/
int
{
int err = 0;
ASSERT(pool_lock_held());
return (EINVAL);
}
/*
* Unbind all the threads currently bound to the partition.
*/
if (err) {
return (err);
}
newpp = &cp_default;
return (err);
}
}
/*
* Teardown the partition's group of active CMT PGs and halted
* CPUs now that they have all left.
*/
/*
* Reset the pointers in any offline processors so they won't
* try to rejoin the destroyed partition when they're turned
* online.
*/
do {
}
/*
* Pause all CPUs while changing the partition list, to make sure
* the clock thread (which traverses the list without holding
* cpu_lock) isn't running.
*/
if (cp_list_head == pp)
start_cpus();
cp_numparts--;
return (err);
}
/*
* Return the ID of the partition to which the specified processor belongs.
*/
{
}
/*
* Attach a processor to an existing partition.
*/
int
{
int err;
ASSERT(pool_lock_held());
return (EINVAL);
return (EINVAL);
return (err);
}
/*
* Get a list of cpus belonging to the partition. If numcpus is NULL,
* this just checks for a valid partition. If numcpus is non-NULL but
* cpulist is NULL, the current number of cpus is stored in *numcpus.
* If both are non-NULL, the current number of cpus is stored in *numcpus,
* and a list of those cpus up to the size originally in *numcpus is
* stored in cpulist[]. Also, store the processor set id in *psid.
* This is useful in case the processor set id passed in was PS_MYID.
*/
int
{
cpu_t *c;
int i;
return (EINVAL);
}
if (numcpus) {
/*
* Only copy as many cpus as were passed in, but
* pass back the real number.
*/
*numcpus = t;
} else
if (cpulist) {
c = pp->cp_cpulist;
for (i = 0; i < ncpus; i++) {
c = c->cpu_next_part;
}
}
}
return (0);
}
/*
* Reallocate kpreempt queues for each CPU partition. Called from
* disp_setup when a new scheduling class is loaded that increases the
* number of priorities in the system.
*/
void
{
cpp = cp_list_head;
do {
} while (cpp != cp_list_head);
}
int
{
int i;
return (EINVAL);
for (i = 0; i < nelem; i++)
return (0);
}
{
cp = cp_list_head;
do {
break;
}
} while (cp != cp_list_head);
}
else if (flag == CP_NONEMPTY)
return (numpart);
}
int
{
ASSERT(pool_lock_held());
return (EINVAL);
}
/*
* PSET_NOESCAPE attribute for default cpu partition is always set
*/
return (EINVAL);
}
return (0);
}
int
{
return (EINVAL);
}
return (0);
}