#include <sys/types.h>
#include <sys/systm.h>
#include <sys/schedctl.h>
#include <sys/proc.h>
#include <sys/thread.h>
#include <sys/class.h>
#include <sys/cred.h>
#include <sys/kmem.h>
#include <sys/cmn_err.h>
#include <sys/stack.h>
#include <sys/debug.h>
#include <sys/cpuvar.h>
#include <sys/sobject.h>
#include <sys/door.h>
#include <sys/modctl.h>
#include <sys/syscall.h>
#include <sys/sysmacros.h>
#include <sys/vmsystm.h>
#include <sys/mman.h>
#include <sys/vnode.h>
#include <sys/swap.h>
#include <sys/lwp.h>
#include <sys/bitmap.h>
#include <sys/atomic.h>
#include <sys/fcntl.h>
#include <vm/seg_kp.h>
#include <vm/seg_vn.h>
#include <vm/as.h>
#include <fs/fs_subr.h>
typedef struct sc_page_ctl {
struct sc_page_ctl *spc_next;
sc_shared_t *spc_base;
sc_shared_t *spc_end;
ulong_t *spc_map;
size_t spc_space;
caddr_t spc_uaddr;
struct anon_map *spc_amp;
} sc_page_ctl_t;
static size_t sc_pagesize;
static size_t sc_bitmap_len;
static size_t sc_bitmap_words;
static void schedctl_save(void *);
static void schedctl_restore(void *);
static void schedctl_fork(void *, void *);
static int schedctl_shared_alloc(sc_shared_t **, uintptr_t *);
static sc_page_ctl_t *schedctl_page_lookup(sc_shared_t *);
static int schedctl_map(struct anon_map *, caddr_t *, caddr_t);
static int schedctl_getpage(struct anon_map **, caddr_t *);
static void schedctl_freepage(struct anon_map *, caddr_t);
static const struct ctxop_template schedctl_ctxop_tpl = {
.ct_rev = CTXOP_TPL_REV,
.ct_save = schedctl_save,
.ct_restore = schedctl_restore,
.ct_fork = schedctl_fork,
};
caddr_t
schedctl(void)
{
kthread_t *t = curthread;
sc_shared_t *ssp;
uintptr_t uaddr;
int error;
if (t->t_schedctl == NULL) {
if ((error = schedctl_shared_alloc(&ssp, &uaddr)) != 0)
return ((caddr_t)(uintptr_t)set_errno(error));
bzero(ssp, sizeof (*ssp));
ctxop_install(t, &schedctl_ctxop_tpl, ssp);
thread_lock(t);
t->t_schedctl = ssp;
t->t_sc_uaddr = uaddr;
ssp->sc_cid = t->t_cid;
ssp->sc_cpri = t->t_cpri;
ssp->sc_priority = DISP_PRIO(t);
thread_unlock(t);
}
return ((caddr_t)t->t_sc_uaddr);
}
void
schedctl_lwp_cleanup(kthread_t *t)
{
sc_shared_t *ssp = t->t_schedctl;
proc_t *p = ttoproc(t);
sc_page_ctl_t *pagep;
index_t index;
ASSERT(MUTEX_NOT_HELD(&p->p_lock));
thread_lock(t);
t->t_schedctl = NULL;
t->t_sc_uaddr = 0;
thread_unlock(t);
(void) ctxop_remove(t, &schedctl_ctxop_tpl, ssp);
mutex_enter(&p->p_sc_lock);
ssp->sc_state = SC_FREE;
pagep = schedctl_page_lookup(ssp);
index = (index_t)(ssp - pagep->spc_base);
BT_CLEAR(pagep->spc_map, index);
pagep->spc_space += sizeof (sc_shared_t);
mutex_exit(&p->p_sc_lock);
}
void
schedctl_proc_cleanup(void)
{
proc_t *p = curproc;
sc_page_ctl_t *pagep;
sc_page_ctl_t *next;
ASSERT(p->p_lwpcnt == 1);
ASSERT(curthread->t_schedctl == NULL);
pagep = p->p_pagep;
p->p_pagep = NULL;
while (pagep != NULL) {
ASSERT(pagep->spc_space == sc_pagesize);
next = pagep->spc_next;
(void) as_unmap(p->p_as, pagep->spc_uaddr, PAGESIZE);
schedctl_freepage(pagep->spc_amp, (caddr_t)(pagep->spc_base));
kmem_free(pagep->spc_map, sizeof (ulong_t) * sc_bitmap_words);
kmem_free(pagep, sizeof (sc_page_ctl_t));
pagep = next;
}
}
static void
schedctl_save(void *arg)
{
sc_shared_t *ssp = arg;
ssp->sc_state = curthread->t_state;
}
static void
schedctl_restore(void *arg)
{
sc_shared_t *ssp = arg;
ssp->sc_state = SC_ONPROC;
ssp->sc_cpu = CPU->cpu_id;
}
static void
schedctl_fork(void *parent, void *child)
{
kthread_t *pt = parent, *ct = child;
proc_t *pp = ttoproc(pt);
proc_t *cp = ttoproc(ct);
sc_page_ctl_t *pagep;
ASSERT(ct->t_schedctl == NULL);
if (pt != curthread || (cp->p_flag & SVFORK))
return;
mutex_enter(&pp->p_sc_lock);
for (pagep = pp->p_pagep; pagep != NULL; pagep = pagep->spc_next)
(void) as_unmap(cp->p_as, pagep->spc_uaddr, PAGESIZE);
mutex_exit(&pp->p_sc_lock);
}
int
schedctl_get_nopreempt(kthread_t *t)
{
ASSERT(THREAD_LOCK_HELD(t));
return (t->t_schedctl->sc_preemptctl.sc_nopreempt);
}
void
schedctl_set_nopreempt(kthread_t *t, short val)
{
ASSERT(THREAD_LOCK_HELD(t));
t->t_schedctl->sc_preemptctl.sc_nopreempt = val;
}
void
schedctl_set_yield(kthread_t *t, short val)
{
ASSERT(THREAD_LOCK_HELD(t));
t->t_schedctl->sc_preemptctl.sc_yield = val;
}
void
schedctl_set_cidpri(kthread_t *t)
{
sc_shared_t *tdp = t->t_schedctl;
if (tdp != NULL) {
tdp->sc_cid = t->t_cid;
tdp->sc_cpri = t->t_cpri;
tdp->sc_priority = DISP_PRIO(t);
}
}
int
schedctl_sigblock(kthread_t *t)
{
sc_shared_t *tdp = t->t_schedctl;
if (tdp != NULL)
return (tdp->sc_sigblock);
return (0);
}
void
schedctl_finish_sigblock(kthread_t *t)
{
sc_shared_t *tdp = t->t_schedctl;
ASSERT(t == curthread || MUTEX_HELD(&ttoproc(t)->p_lock));
if (tdp != NULL && tdp->sc_sigblock) {
t->t_hold.__sigbits[0] = FILLSET0 & ~CANTMASK0;
t->t_hold.__sigbits[1] = FILLSET1 & ~CANTMASK1;
t->t_hold.__sigbits[2] = FILLSET2 & ~CANTMASK2;
tdp->sc_sigblock = 0;
}
}
int
schedctl_cancel_pending(void)
{
sc_shared_t *tdp = curthread->t_schedctl;
if (tdp != NULL &&
(tdp->sc_flgs & SC_CANCEL_FLG) &&
!tdp->sc_sigblock &&
!sigismember(&curthread->t_hold, SIGCANCEL))
return (1);
return (0);
}
void
schedctl_cancel_eintr(void)
{
sc_shared_t *tdp = curthread->t_schedctl;
if (tdp != NULL)
tdp->sc_flgs |= SC_EINTR_FLG;
}
int
schedctl_is_park(void)
{
sc_shared_t *tdp = curthread->t_schedctl;
if (tdp != NULL)
return ((tdp->sc_flgs & SC_PARK_FLG) != 0);
return (1);
}
void
schedctl_set_park(void)
{
sc_shared_t *tdp = curthread->t_schedctl;
if (tdp != NULL)
tdp->sc_flgs |= SC_PARK_FLG;
}
void
schedctl_unpark(void)
{
sc_shared_t *tdp = curthread->t_schedctl;
if (tdp != NULL)
tdp->sc_flgs &= ~SC_PARK_FLG;
}
void
schedctl_init(void)
{
sc_pagesize = PAGESIZE - (PAGESIZE % sizeof (sc_shared_t));
sc_bitmap_len = sc_pagesize / sizeof (sc_shared_t);
sc_bitmap_words = howmany(sc_bitmap_len, BT_NBIPUL);
}
static int
schedctl_shared_alloc(sc_shared_t **kaddrp, uintptr_t *uaddrp)
{
proc_t *p = curproc;
sc_page_ctl_t *pagep;
sc_shared_t *ssp;
caddr_t base;
index_t index;
int error;
ASSERT(MUTEX_NOT_HELD(&p->p_lock));
mutex_enter(&p->p_sc_lock);
for (pagep = p->p_pagep; pagep != NULL; pagep = pagep->spc_next)
if (pagep->spc_space != 0)
break;
if (pagep != NULL)
base = pagep->spc_uaddr;
else {
struct anon_map *amp;
caddr_t kaddr;
if ((error = schedctl_getpage(&, &kaddr)) != 0) {
mutex_exit(&p->p_sc_lock);
return (error);
}
if ((error = schedctl_map(amp, &base, kaddr)) != 0) {
schedctl_freepage(amp, kaddr);
mutex_exit(&p->p_sc_lock);
return (error);
}
pagep = kmem_alloc(sizeof (sc_page_ctl_t), KM_SLEEP);
pagep->spc_amp = amp;
pagep->spc_base = (sc_shared_t *)kaddr;
pagep->spc_end = (sc_shared_t *)(kaddr + sc_pagesize);
pagep->spc_uaddr = base;
pagep->spc_map = kmem_zalloc(sizeof (ulong_t) * sc_bitmap_words,
KM_SLEEP);
pagep->spc_space = sc_pagesize;
pagep->spc_next = p->p_pagep;
p->p_pagep = pagep;
}
ASSERT(pagep != NULL && pagep->spc_space >= sizeof (sc_shared_t));
index = bt_availbit(pagep->spc_map, sc_bitmap_len);
ASSERT(index != -1);
ssp = pagep->spc_base + index;
BT_SET(pagep->spc_map, index);
pagep->spc_space -= sizeof (sc_shared_t);
mutex_exit(&p->p_sc_lock);
*kaddrp = ssp;
*uaddrp = (uintptr_t)base + ((uintptr_t)ssp & PAGEOFFSET);
return (0);
}
static sc_page_ctl_t *
schedctl_page_lookup(sc_shared_t *ssp)
{
proc_t *p = curproc;
sc_page_ctl_t *pagep;
ASSERT(MUTEX_HELD(&p->p_sc_lock));
for (pagep = p->p_pagep; pagep != NULL; pagep = pagep->spc_next) {
if (ssp >= pagep->spc_base && ssp < pagep->spc_end)
return (pagep);
}
return (NULL);
}
static int
schedctl_map(struct anon_map *amp, caddr_t *uaddrp, caddr_t kaddr)
{
caddr_t addr = NULL;
struct as *as = curproc->p_as;
struct segvn_crargs vn_a;
int error;
as_rangelock(as);
map_addr(&addr, PAGESIZE, (offset_t)(uintptr_t)kaddr, 1, 0);
if (addr == NULL) {
as_rangeunlock(as);
return (ENOMEM);
}
vn_a.vp = NULL;
vn_a.offset = 0;
vn_a.cred = NULL;
vn_a.type = MAP_SHARED;
vn_a.prot = vn_a.maxprot = PROT_ALL;
vn_a.flags = 0;
vn_a.amp = amp;
vn_a.szc = 0;
vn_a.lgrp_mem_policy_flags = 0;
error = as_map(as, addr, PAGESIZE, segvn_create, &vn_a);
as_rangeunlock(as);
if (error)
return (error);
*uaddrp = addr;
return (0);
}
static int
schedctl_getpage(struct anon_map **newamp, caddr_t *newaddr)
{
struct anon_map *amp;
caddr_t kaddr;
amp = anonmap_alloc(PAGESIZE, 0, ANON_SLEEP);
kaddr = segkp_get_withanonmap(segkp, PAGESIZE,
KPD_NO_ANON | KPD_LOCKED | KPD_ZERO, amp);
if (kaddr == NULL) {
amp->refcnt--;
anonmap_free(amp);
return (ENOMEM);
}
*newamp = amp;
*newaddr = kaddr;
return (0);
}
static void
schedctl_freepage(struct anon_map *amp, caddr_t kaddr)
{
ANON_LOCK_ENTER(&->a_rwlock, RW_WRITER);
segkp_release(segkp, kaddr);
if (--amp->refcnt == 0) {
anonmap_purge(amp);
anon_free(amp->ahp, 0, PAGESIZE);
ANON_LOCK_EXIT(&->a_rwlock);
anonmap_free(amp);
} else {
ANON_LOCK_EXIT(&->a_rwlock);
}
}