#include <sys/types.h>
#include <sys/t_lock.h>
#include <sys/param.h>
#include <sys/cmn_err.h>
#include <sys/cred.h>
#include <sys/priv.h>
#include <sys/debug.h>
#include <sys/errno.h>
#include <sys/inline.h>
#include <sys/kmem.h>
#include <sys/mman.h>
#include <sys/proc.h>
#include <sys/brand.h>
#include <sys/sobject.h>
#include <sys/sysmacros.h>
#include <sys/systm.h>
#include <sys/uio.h>
#include <sys/var.h>
#include <sys/vfs.h>
#include <sys/vnode.h>
#include <sys/session.h>
#include <sys/pcb.h>
#include <sys/signal.h>
#include <sys/user.h>
#include <sys/disp.h>
#include <sys/class.h>
#include <sys/ts.h>
#include <sys/bitmap.h>
#include <sys/poll.h>
#include <sys/shm_impl.h>
#include <sys/fault.h>
#include <sys/syscall.h>
#include <sys/procfs.h>
#include <sys/processor.h>
#include <sys/cpuvar.h>
#include <sys/copyops.h>
#include <sys/time.h>
#include <sys/msacct.h>
#include <sys/flock_impl.h>
#include <sys/stropts.h>
#include <sys/strsubr.h>
#include <sys/pathname.h>
#include <sys/mode.h>
#include <sys/socketvar.h>
#include <sys/autoconf.h>
#include <sys/dtrace.h>
#include <sys/timod.h>
#include <sys/fs/namenode.h>
#include <netinet/udp.h>
#include <netinet/tcp.h>
#include <inet/cc.h>
#include <vm/as.h>
#include <vm/rm.h>
#include <vm/seg.h>
#include <vm/seg_vn.h>
#include <vm/seg_dev.h>
#include <vm/seg_spt.h>
#include <vm/page.h>
#include <sys/vmparam.h>
#include <sys/swap.h>
#include <fs/proc/prdata.h>
#include <sys/task.h>
#include <sys/project.h>
#include <sys/contract_impl.h>
#include <sys/contract/process.h>
#include <sys/contract/process_impl.h>
#include <sys/schedctl.h>
#include <sys/pool.h>
#include <sys/zone.h>
#include <sys/atomic.h>
#include <sys/sdt.h>
#define MAX_ITERS_SPIN 5
typedef struct prpagev {
uint_t *pg_protv;
char *pg_incore;
size_t pg_npages;
ulong_t pg_pnbase;
} prpagev_t;
size_t pagev_lim = 256 * 1024;
extern struct seg_ops segdev_ops;
extern struct seg_ops segspt_shmops;
static int set_watched_page(proc_t *, caddr_t, caddr_t, ulong_t, ulong_t);
static void clear_watched_page(proc_t *, caddr_t, caddr_t, ulong_t);
kthread_t *
prchoose(proc_t *p)
{
kthread_t *t;
kthread_t *t_onproc = NULL;
kthread_t *t_run = NULL;
kthread_t *t_sleep = NULL;
kthread_t *t_hold = NULL;
kthread_t *t_susp = NULL;
kthread_t *t_jstop = NULL;
kthread_t *t_jdstop = NULL;
kthread_t *t_req = NULL;
kthread_t *t_istop = NULL;
kthread_t *t_dtrace = NULL;
ASSERT(MUTEX_HELD(&p->p_lock));
if ((t = p->p_agenttp) != NULL) {
thread_lock(t);
return (t);
}
if ((t = p->p_tlist) == NULL)
return (t);
do {
if (VSTOPPED(t)) {
if (t_req == NULL)
t_req = t;
continue;
}
if ((t->t_proc_flag & TP_KTHREAD) != 0) {
continue;
}
thread_lock(t);
switch (t->t_state) {
default:
panic("prchoose: bad thread state %d, thread 0x%p",
t->t_state, (void *)t);
case TS_SLEEP:
if (t->t_wchan == (caddr_t)&p->p_holdlwps &&
t->t_wchan0 == NULL) {
if (t_hold == NULL)
t_hold = t;
} else {
if (t_sleep == NULL)
t_sleep = t;
}
break;
case TS_RUN:
case TS_WAIT:
if (t_run == NULL)
t_run = t;
break;
case TS_ONPROC:
if (t_onproc == NULL)
t_onproc = t;
break;
case TS_ZOMB:
break;
case TS_STOPPED:
switch (t->t_whystop) {
case PR_SUSPENDED:
if (t_susp == NULL)
t_susp = t;
break;
case PR_JOBCONTROL:
if (t->t_proc_flag & TP_PRSTOP) {
if (t_jdstop == NULL)
t_jdstop = t;
} else {
if (t_jstop == NULL)
t_jstop = t;
}
break;
case PR_REQUESTED:
if (t->t_dtrace_stop && t_dtrace == NULL)
t_dtrace = t;
else if (t_req == NULL)
t_req = t;
break;
case PR_SYSENTRY:
case PR_SYSEXIT:
case PR_SIGNALLED:
case PR_FAULTED:
if (t_istop == NULL ||
(t_istop->t_whystop == PR_SYSENTRY &&
t_istop->t_whatstop == SYS_exit))
t_istop = t;
break;
case PR_CHECKPOINT:
break;
default:
panic("prchoose: bad t_whystop %d, thread 0x%p",
t->t_whystop, (void *)t);
}
break;
}
thread_unlock(t);
} while ((t = t->t_forw) != p->p_tlist);
if (t_onproc)
t = t_onproc;
else if (t_run)
t = t_run;
else if (t_sleep)
t = t_sleep;
else if (t_jstop)
t = t_jstop;
else if (t_jdstop)
t = t_jdstop;
else if (t_istop)
t = t_istop;
else if (t_dtrace)
t = t_dtrace;
else if (t_req)
t = t_req;
else if (t_hold)
t = t_hold;
else if (t_susp)
t = t_susp;
else
t = p->p_tlist;
if (t != NULL)
thread_lock(t);
return (t);
}
void
prnotify(struct vnode *vp)
{
prcommon_t *pcp = VTOP(vp)->pr_common;
mutex_enter(&pcp->prc_mutex);
cv_broadcast(&pcp->prc_wait);
mutex_exit(&pcp->prc_mutex);
if (pcp->prc_flags & PRC_POLL) {
pcp->prc_flags &= ~PRC_POLL;
pollwakeup(&pcp->prc_pollhead, POLLHUP);
}
}
static void
prfreenotify(vnode_t *vp)
{
prnode_t *pnp;
prcommon_t *pcp;
while (vp != NULL) {
pnp = VTOP(vp);
pcp = pnp->pr_common;
ASSERT(pcp->prc_thread == NULL);
pcp->prc_proc = NULL;
mutex_enter(&pcp->prc_mutex);
cv_broadcast(&pcp->prc_wait);
mutex_exit(&pcp->prc_mutex);
ASSERT(!(pcp->prc_flags & PRC_POLL));
vp = pnp->pr_next;
pnp->pr_next = NULL;
}
}
void
prfree(proc_t *p)
{
uint_t slot = p->p_slot;
ASSERT(MUTEX_HELD(&pidlock));
mutex_enter(&pr_pidlock);
mutex_enter(&p->p_lock);
while (p->p_proc_flag & P_PR_LOCK) {
mutex_exit(&pr_pidlock);
cv_wait(&pr_pid_cv[slot], &p->p_lock);
mutex_exit(&p->p_lock);
mutex_enter(&pr_pidlock);
mutex_enter(&p->p_lock);
}
ASSERT(p->p_tlist == NULL);
prfreenotify(p->p_plist);
p->p_plist = NULL;
prfreenotify(p->p_trace);
p->p_trace = NULL;
cv_broadcast(&pr_pid_cv[slot]);
mutex_exit(&p->p_lock);
mutex_exit(&pr_pidlock);
}
void
prexit(proc_t *p)
{
ASSERT(MUTEX_HELD(&p->p_lock));
if (pr_watch_active(p)) {
pr_free_watchpoints(p);
watch_disable(curthread);
}
if (p->p_trace) {
VTOP(p->p_trace)->pr_common->prc_flags |= PRC_DESTROY;
prnotify(p->p_trace);
}
cv_broadcast(&pr_pid_cv[p->p_slot]);
}
void
prlwpexit(kthread_t *t)
{
vnode_t *vp;
prnode_t *pnp;
prcommon_t *pcp;
proc_t *p = ttoproc(t);
lwpent_t *lep = p->p_lwpdir[t->t_dslot].ld_entry;
ASSERT(t == curthread);
ASSERT(MUTEX_HELD(&p->p_lock));
ASSERT(!(p->p_proc_flag & P_PR_LOCK));
for (vp = p->p_plist; vp != NULL; vp = pnp->pr_next) {
pnp = VTOP(vp);
pcp = pnp->pr_common;
if (pcp->prc_thread == t) {
pcp->prc_thread = NULL;
pcp->prc_flags |= PRC_DESTROY;
}
}
for (vp = lep->le_trace; vp != NULL; vp = pnp->pr_next) {
pnp = VTOP(vp);
pcp = pnp->pr_common;
pcp->prc_thread = NULL;
pcp->prc_flags |= PRC_DESTROY;
prnotify(vp);
}
if (p->p_trace)
prnotify(p->p_trace);
}
void
prlwpfree(proc_t *p, lwpent_t *lep)
{
vnode_t *vp;
prnode_t *pnp;
prcommon_t *pcp;
ASSERT(MUTEX_HELD(&p->p_lock));
ASSERT(!(p->p_proc_flag & P_PR_LOCK));
vp = lep->le_trace;
lep->le_trace = NULL;
while (vp) {
prnotify(vp);
pnp = VTOP(vp);
pcp = pnp->pr_common;
ASSERT(pcp->prc_thread == NULL &&
(pcp->prc_flags & PRC_DESTROY));
pcp->prc_tslot = -1;
vp = pnp->pr_next;
pnp->pr_next = NULL;
}
if (p->p_trace)
prnotify(p->p_trace);
}
void
prexecstart(void)
{
proc_t *p = ttoproc(curthread);
klwp_t *lwp = ttolwp(curthread);
ASSERT(MUTEX_HELD(&p->p_lock));
prbarrier(p);
lwp->lwp_nostop++;
p->p_proc_flag |= P_PR_EXEC;
}
void
prexecend(void)
{
proc_t *p = ttoproc(curthread);
klwp_t *lwp = ttolwp(curthread);
vnode_t *vp;
prnode_t *pnp;
prcommon_t *pcp;
model_t model = p->p_model;
id_t tid = curthread->t_tid;
int tslot = curthread->t_dslot;
ASSERT(MUTEX_HELD(&p->p_lock));
lwp->lwp_nostop--;
if (p->p_flag & SEXITLWPS) {
return;
}
p->p_proc_flag &= ~P_PR_EXEC;
if ((vp = p->p_trace) != NULL) {
pcp = VTOP(vp)->pr_common;
mutex_enter(&pcp->prc_mutex);
cv_broadcast(&pcp->prc_wait);
mutex_exit(&pcp->prc_mutex);
for (; vp != NULL; vp = pnp->pr_next) {
pnp = VTOP(vp);
pnp->pr_common->prc_datamodel = model;
}
}
if ((vp = p->p_lwpdir[tslot].ld_entry->le_trace) != NULL) {
ASSERT(p->p_trace != NULL);
pcp = VTOP(vp)->pr_common;
mutex_enter(&pcp->prc_mutex);
cv_broadcast(&pcp->prc_wait);
mutex_exit(&pcp->prc_mutex);
for (; vp != NULL; vp = pnp->pr_next) {
pnp = VTOP(vp);
pcp = pnp->pr_common;
pcp->prc_datamodel = model;
pcp->prc_tid = tid;
pcp->prc_tslot = tslot;
}
}
}
void
prrelvm(void)
{
proc_t *p = ttoproc(curthread);
mutex_enter(&p->p_lock);
prbarrier(p);
if (pr_watch_active(p)) {
pr_free_watchpoints(p);
watch_disable(curthread);
}
mutex_exit(&p->p_lock);
pr_free_watched_pages(p);
}
void
prinvalidate(struct user *up)
{
kthread_t *t = curthread;
proc_t *p = ttoproc(t);
vnode_t *vp;
prnode_t *pnp;
int writers = 0;
mutex_enter(&p->p_lock);
prbarrier(p);
ASSERT(p->p_lwpcnt == 1 && p->p_zombcnt == 0);
for (vp = p->p_plist; vp != NULL; vp = pnp->pr_next) {
pnp = VTOP(vp);
switch (pnp->pr_type) {
case PR_PSINFO:
case PR_LPSINFO:
case PR_LWPSINFO:
case PR_LWPDIR:
case PR_LWPIDDIR:
case PR_USAGE:
case PR_LUSAGE:
case PR_LWPUSAGE:
break;
default:
pnp->pr_flags |= PR_INVAL;
break;
}
}
if ((vp = p->p_trace) != NULL) {
prcommon_t *pcp = VTOP(vp)->pr_pcommon;
prnotify(vp);
if ((writers = pcp->prc_writers) != 0) {
pcp->prc_flags &= ~PRC_EXCL;
ASSERT(pcp->prc_selfopens <= writers);
pcp->prc_selfopens = writers;
}
}
vp = p->p_lwpdir[t->t_dslot].ld_entry->le_trace;
while (vp != NULL) {
pnp = VTOP(vp);
pnp->pr_flags |= PR_INVAL;
prnotify(vp);
vp = pnp->pr_next;
}
t->t_proc_flag &= ~TP_PAUSE;
if ((p->p_proc_flag & P_PR_TRACE) && writers) {
t->t_proc_flag |= TP_PRSTOP;
aston(t);
p->p_proc_flag |= P_PR_RUNLCL;
} else {
premptyset(&up->u_entrymask);
premptyset(&up->u_exitmask);
up->u_systrap = 0;
premptyset(&p->p_sigmask);
premptyset(&p->p_fltmask);
t->t_proc_flag &= ~(TP_PRSTOP|TP_PRVSTOP|TP_STOPPING);
p->p_proc_flag &= ~(P_PR_RUNLCL|P_PR_KILLCL|P_PR_TRACE);
prnostep(ttolwp(t));
}
mutex_exit(&p->p_lock);
}
proc_t *
pr_p_lock(prnode_t *pnp)
{
proc_t *p;
prcommon_t *pcp;
mutex_enter(&pr_pidlock);
if ((pcp = pnp->pr_pcommon) == NULL || (p = pcp->prc_proc) == NULL)
return (NULL);
mutex_enter(&p->p_lock);
while (p->p_proc_flag & P_PR_LOCK) {
kcondvar_t *cv = &pr_pid_cv[p->p_slot];
kmutex_t *mp = &p->p_lock;
mutex_exit(&pr_pidlock);
cv_wait(cv, mp);
mutex_exit(mp);
mutex_enter(&pr_pidlock);
if (pcp->prc_proc == NULL)
return (NULL);
ASSERT(p == pcp->prc_proc);
mutex_enter(&p->p_lock);
}
p->p_proc_flag |= P_PR_LOCK;
return (p);
}
int
prlock(prnode_t *pnp, int zdisp)
{
prcommon_t *pcp;
proc_t *p;
again:
pcp = pnp->pr_common;
p = pr_p_lock(pnp);
mutex_exit(&pr_pidlock);
if (p == NULL)
return (ENOENT);
ASSERT(p == pcp->prc_proc && p->p_stat != 0 && p->p_stat != SIDL);
if (zdisp == ZNO &&
((pcp->prc_flags & PRC_DESTROY) || (p->p_flag & SEXITING))) {
prunlock(pnp);
return (ENOENT);
}
if (pcp->prc_flags & PRC_LWP) {
if ((zdisp == ZNO && (pcp->prc_flags & PRC_DESTROY)) ||
pcp->prc_tslot == -1) {
prunlock(pnp);
return (ENOENT);
}
}
if (pnp->pr_flags & PR_INVAL) {
prunlock(pnp);
return (EAGAIN);
}
if (p->p_proc_flag & P_PR_EXEC) {
pcp = pnp->pr_pcommon;
mutex_enter(&pcp->prc_mutex);
prunlock(pnp);
if (!cv_wait_sig(&pcp->prc_wait, &pcp->prc_mutex)) {
mutex_exit(&pcp->prc_mutex);
return (EINTR);
}
mutex_exit(&pcp->prc_mutex);
goto again;
}
return (0);
}
void
prunmark(proc_t *p)
{
ASSERT(p->p_proc_flag & P_PR_LOCK);
ASSERT(MUTEX_HELD(&p->p_lock));
cv_signal(&pr_pid_cv[p->p_slot]);
p->p_proc_flag &= ~P_PR_LOCK;
}
void
prunlock(prnode_t *pnp)
{
prcommon_t *pcp = pnp->pr_common;
proc_t *p = pcp->prc_proc;
if ((p->p_flag & SKILLED) &&
!(p->p_flag & SEXITING) &&
!(pcp->prc_flags & PRC_DESTROY) &&
!((pcp->prc_flags & PRC_LWP) && pcp->prc_tslot == -1)) {
int err = pr_setrun(pnp, 0);
if (err == EBUSY && p->p_agenttp == NULL) {
runlwps(p, TS_XSTART);
}
}
prunmark(p);
mutex_exit(&p->p_lock);
}
void
prbarrier(proc_t *p)
{
ASSERT(MUTEX_HELD(&p->p_lock));
if (p->p_proc_flag & P_PR_LOCK) {
uint_t slot = p->p_slot;
while (p->p_proc_flag & P_PR_LOCK)
cv_wait(&pr_pid_cv[slot], &p->p_lock);
cv_signal(&pr_pid_cv[slot]);
}
}
void
prgetstatus(proc_t *p, pstatus_t *sp, zone_t *zp)
{
kthread_t *t;
ASSERT(MUTEX_HELD(&p->p_lock));
t = prchoose(p);
ASSERT(t != NULL);
thread_unlock(t);
bzero(sp, sizeof (pstatus_t) - sizeof (lwpstatus_t));
sp->pr_nlwp = p->p_lwpcnt;
sp->pr_nzomb = p->p_zombcnt;
prassignset(&sp->pr_sigpend, &p->p_sig);
sp->pr_brkbase = (uintptr_t)p->p_brkbase;
sp->pr_brksize = p->p_brksize;
sp->pr_stkbase = (uintptr_t)prgetstackbase(p);
sp->pr_stksize = p->p_stksize;
sp->pr_pid = p->p_pid;
if (curproc->p_zone->zone_id != GLOBAL_ZONEID &&
(p->p_flag & SZONETOP)) {
ASSERT(p->p_zone->zone_id != GLOBAL_ZONEID);
sp->pr_ppid = curproc->p_zone->zone_zsched->p_pid;
} else {
sp->pr_ppid = p->p_ppid;
}
sp->pr_pgid = p->p_pgrp;
sp->pr_sid = p->p_sessp->s_sid;
sp->pr_taskid = p->p_task->tk_tkid;
sp->pr_projid = p->p_task->tk_proj->kpj_id;
sp->pr_zoneid = p->p_zone->zone_id;
hrt2ts(mstate_aggr_state(p, LMS_USER), &sp->pr_utime);
hrt2ts(mstate_aggr_state(p, LMS_SYSTEM), &sp->pr_stime);
TICK_TO_TIMESTRUC(p->p_cutime, &sp->pr_cutime);
TICK_TO_TIMESTRUC(p->p_cstime, &sp->pr_cstime);
prassignset(&sp->pr_sigtrace, &p->p_sigmask);
prassignset(&sp->pr_flttrace, &p->p_fltmask);
prassignset(&sp->pr_sysentry, &PTOU(p)->u_entrymask);
prassignset(&sp->pr_sysexit, &PTOU(p)->u_exitmask);
switch (p->p_model) {
case DATAMODEL_ILP32:
sp->pr_dmodel = PR_MODEL_ILP32;
break;
case DATAMODEL_LP64:
sp->pr_dmodel = PR_MODEL_LP64;
break;
}
if (p->p_agenttp)
sp->pr_agentid = p->p_agenttp->t_tid;
prgetlwpstatus(t, &sp->pr_lwp, zp);
sp->pr_flags = sp->pr_lwp.pr_flags;
}
void
prgethold(kthread_t *t, sigset_t *sp)
{
k_sigset_t set;
if (schedctl_sigblock(t)) {
set.__sigbits[0] = FILLSET0 & ~CANTMASK0;
set.__sigbits[1] = FILLSET1 & ~CANTMASK1;
set.__sigbits[2] = FILLSET2 & ~CANTMASK2;
} else {
set = t->t_hold;
}
sigktou(&set, sp);
}
#ifdef _SYSCALL32_IMPL
void
prgetlwpstatus32(kthread_t *t, lwpstatus32_t *sp, zone_t *zp)
{
proc_t *p = ttoproc(t);
klwp_t *lwp = ttolwp(t);
struct mstate *ms = &lwp->lwp_mstate;
hrtime_t usr, sys;
int flags;
ulong_t instr;
ASSERT(MUTEX_HELD(&p->p_lock));
bzero(sp, sizeof (*sp));
flags = 0L;
if (t->t_state == TS_STOPPED) {
flags |= PR_STOPPED;
if ((t->t_schedflag & TS_PSTART) == 0)
flags |= PR_ISTOP;
} else if (VSTOPPED(t)) {
flags |= PR_STOPPED|PR_ISTOP;
}
if (!(flags & PR_ISTOP) && (t->t_proc_flag & TP_PRSTOP))
flags |= PR_DSTOP;
if (lwp->lwp_asleep)
flags |= PR_ASLEEP;
if (t == p->p_agenttp)
flags |= PR_AGENT;
if (!(t->t_proc_flag & TP_TWAIT))
flags |= PR_DETACH;
if (t->t_proc_flag & TP_DAEMON)
flags |= PR_DAEMON;
if (p->p_proc_flag & P_PR_FORK)
flags |= PR_FORK;
if (p->p_proc_flag & P_PR_RUNLCL)
flags |= PR_RLC;
if (p->p_proc_flag & P_PR_KILLCL)
flags |= PR_KLC;
if (p->p_proc_flag & P_PR_ASYNC)
flags |= PR_ASYNC;
if (p->p_proc_flag & P_PR_BPTADJ)
flags |= PR_BPTADJ;
if (p->p_proc_flag & P_PR_PTRACE)
flags |= PR_PTRACE;
if (p->p_flag & SMSACCT)
flags |= PR_MSACCT;
if (p->p_flag & SMSFORK)
flags |= PR_MSFORK;
if (p->p_flag & SVFWAIT)
flags |= PR_VFORKP;
sp->pr_flags = flags;
if (VSTOPPED(t)) {
sp->pr_why = PR_REQUESTED;
sp->pr_what = 0;
} else {
sp->pr_why = t->t_whystop;
sp->pr_what = t->t_whatstop;
}
sp->pr_lwpid = t->t_tid;
sp->pr_cursig = lwp->lwp_cursig;
prassignset(&sp->pr_lwppend, &t->t_sig);
prgethold(t, &sp->pr_lwphold);
if (t->t_whystop == PR_FAULTED) {
siginfo_kto32(&lwp->lwp_siginfo, &sp->pr_info);
if (t->t_whatstop == FLTPAGE)
sp->pr_info.si_addr =
(caddr32_t)(uintptr_t)lwp->lwp_siginfo.si_addr;
} else if (lwp->lwp_curinfo)
siginfo_kto32(&lwp->lwp_curinfo->sq_info, &sp->pr_info);
if (SI_FROMUSER(&lwp->lwp_siginfo) && zp->zone_id != GLOBAL_ZONEID &&
sp->pr_info.si_zoneid != zp->zone_id) {
sp->pr_info.si_pid = zp->zone_zsched->p_pid;
sp->pr_info.si_uid = 0;
sp->pr_info.si_ctid = -1;
sp->pr_info.si_zoneid = zp->zone_id;
}
sp->pr_altstack.ss_sp =
(caddr32_t)(uintptr_t)lwp->lwp_sigaltstack.ss_sp;
sp->pr_altstack.ss_size = (size32_t)lwp->lwp_sigaltstack.ss_size;
sp->pr_altstack.ss_flags = (int32_t)lwp->lwp_sigaltstack.ss_flags;
prgetaction32(p, PTOU(p), lwp->lwp_cursig, &sp->pr_action);
sp->pr_oldcontext = (caddr32_t)lwp->lwp_oldcontext;
sp->pr_ustack = (caddr32_t)lwp->lwp_ustack;
(void) strncpy(sp->pr_clname, sclass[t->t_cid].cl_name,
sizeof (sp->pr_clname) - 1);
if (flags & PR_STOPPED)
hrt2ts32(t->t_stoptime, &sp->pr_tstamp);
usr = ms->ms_acct[LMS_USER];
sys = ms->ms_acct[LMS_SYSTEM] + ms->ms_acct[LMS_TRAP];
scalehrtime(&usr);
scalehrtime(&sys);
hrt2ts32(usr, &sp->pr_utime);
hrt2ts32(sys, &sp->pr_stime);
if ((p->p_flag & SSYS) || p->p_as == &kas)
sp->pr_flags |= (PR_ISSYS|PR_PCINVAL);
else if (!(flags & PR_STOPPED))
sp->pr_flags |= PR_PCINVAL;
else if (!prfetchinstr(lwp, &instr))
sp->pr_flags |= PR_PCINVAL;
else
sp->pr_instr = (uint32_t)instr;
mutex_exit(&p->p_lock);
if (prisstep(lwp))
sp->pr_flags |= PR_STEP;
if ((flags & (PR_STOPPED|PR_ASLEEP)) && t->t_sysnum) {
int i;
sp->pr_syscall = get_syscall32_args(lwp,
(int *)sp->pr_sysarg, &i);
sp->pr_nsysarg = (ushort_t)i;
}
if ((flags & PR_STOPPED) || t == curthread)
prgetprregs32(lwp, sp->pr_reg);
if ((t->t_state == TS_STOPPED && t->t_whystop == PR_SYSEXIT) ||
(flags & PR_VFORKP)) {
long r1, r2;
user_t *up;
auxv_t *auxp;
int i;
sp->pr_errno = prgetrvals(lwp, &r1, &r2);
if (sp->pr_errno == 0) {
sp->pr_rval1 = (int32_t)r1;
sp->pr_rval2 = (int32_t)r2;
sp->pr_errpriv = PRIV_NONE;
} else
sp->pr_errpriv = lwp->lwp_badpriv;
if (t->t_sysnum == SYS_execve) {
up = PTOU(p);
sp->pr_sysarg[0] = 0;
sp->pr_sysarg[1] = (caddr32_t)up->u_argv;
sp->pr_sysarg[2] = (caddr32_t)up->u_envp;
sp->pr_sysarg[3] = 0;
for (i = 0, auxp = up->u_auxv;
i < sizeof (up->u_auxv) / sizeof (up->u_auxv[0]);
i++, auxp++) {
if (auxp->a_type == AT_SUN_EXECNAME) {
sp->pr_sysarg[0] =
(caddr32_t)
(uintptr_t)auxp->a_un.a_ptr;
break;
}
}
}
}
if (prhasfp())
prgetprfpregs32(lwp, &sp->pr_fpreg);
mutex_enter(&p->p_lock);
}
void
prgetstatus32(proc_t *p, pstatus32_t *sp, zone_t *zp)
{
kthread_t *t;
ASSERT(MUTEX_HELD(&p->p_lock));
t = prchoose(p);
ASSERT(t != NULL);
thread_unlock(t);
bzero(sp, sizeof (pstatus32_t) - sizeof (lwpstatus32_t));
sp->pr_nlwp = p->p_lwpcnt;
sp->pr_nzomb = p->p_zombcnt;
prassignset(&sp->pr_sigpend, &p->p_sig);
sp->pr_brkbase = (uint32_t)(uintptr_t)p->p_brkbase;
sp->pr_brksize = (uint32_t)p->p_brksize;
sp->pr_stkbase = (uint32_t)(uintptr_t)prgetstackbase(p);
sp->pr_stksize = (uint32_t)p->p_stksize;
sp->pr_pid = p->p_pid;
if (curproc->p_zone->zone_id != GLOBAL_ZONEID &&
(p->p_flag & SZONETOP)) {
ASSERT(p->p_zone->zone_id != GLOBAL_ZONEID);
sp->pr_ppid = curproc->p_zone->zone_zsched->p_pid;
} else {
sp->pr_ppid = p->p_ppid;
}
sp->pr_pgid = p->p_pgrp;
sp->pr_sid = p->p_sessp->s_sid;
sp->pr_taskid = p->p_task->tk_tkid;
sp->pr_projid = p->p_task->tk_proj->kpj_id;
sp->pr_zoneid = p->p_zone->zone_id;
hrt2ts32(mstate_aggr_state(p, LMS_USER), &sp->pr_utime);
hrt2ts32(mstate_aggr_state(p, LMS_SYSTEM), &sp->pr_stime);
TICK_TO_TIMESTRUC32(p->p_cutime, &sp->pr_cutime);
TICK_TO_TIMESTRUC32(p->p_cstime, &sp->pr_cstime);
prassignset(&sp->pr_sigtrace, &p->p_sigmask);
prassignset(&sp->pr_flttrace, &p->p_fltmask);
prassignset(&sp->pr_sysentry, &PTOU(p)->u_entrymask);
prassignset(&sp->pr_sysexit, &PTOU(p)->u_exitmask);
switch (p->p_model) {
case DATAMODEL_ILP32:
sp->pr_dmodel = PR_MODEL_ILP32;
break;
case DATAMODEL_LP64:
sp->pr_dmodel = PR_MODEL_LP64;
break;
}
if (p->p_agenttp)
sp->pr_agentid = p->p_agenttp->t_tid;
prgetlwpstatus32(t, &sp->pr_lwp, zp);
sp->pr_flags = sp->pr_lwp.pr_flags;
}
#endif
void
prgetlwpstatus(kthread_t *t, lwpstatus_t *sp, zone_t *zp)
{
proc_t *p = ttoproc(t);
klwp_t *lwp = ttolwp(t);
struct mstate *ms = &lwp->lwp_mstate;
hrtime_t usr, sys;
int flags;
ulong_t instr;
ASSERT(MUTEX_HELD(&p->p_lock));
bzero(sp, sizeof (*sp));
flags = 0L;
if (t->t_state == TS_STOPPED) {
flags |= PR_STOPPED;
if ((t->t_schedflag & TS_PSTART) == 0)
flags |= PR_ISTOP;
} else if (VSTOPPED(t)) {
flags |= PR_STOPPED|PR_ISTOP;
}
if (!(flags & PR_ISTOP) && (t->t_proc_flag & TP_PRSTOP))
flags |= PR_DSTOP;
if (lwp->lwp_asleep)
flags |= PR_ASLEEP;
if (t == p->p_agenttp)
flags |= PR_AGENT;
if (!(t->t_proc_flag & TP_TWAIT))
flags |= PR_DETACH;
if (t->t_proc_flag & TP_DAEMON)
flags |= PR_DAEMON;
if (p->p_proc_flag & P_PR_FORK)
flags |= PR_FORK;
if (p->p_proc_flag & P_PR_RUNLCL)
flags |= PR_RLC;
if (p->p_proc_flag & P_PR_KILLCL)
flags |= PR_KLC;
if (p->p_proc_flag & P_PR_ASYNC)
flags |= PR_ASYNC;
if (p->p_proc_flag & P_PR_BPTADJ)
flags |= PR_BPTADJ;
if (p->p_proc_flag & P_PR_PTRACE)
flags |= PR_PTRACE;
if (p->p_flag & SMSACCT)
flags |= PR_MSACCT;
if (p->p_flag & SMSFORK)
flags |= PR_MSFORK;
if (p->p_flag & SVFWAIT)
flags |= PR_VFORKP;
if (p->p_pgidp->pid_pgorphaned)
flags |= PR_ORPHAN;
if (p->p_pidflag & CLDNOSIGCHLD)
flags |= PR_NOSIGCHLD;
if (p->p_pidflag & CLDWAITPID)
flags |= PR_WAITPID;
sp->pr_flags = flags;
if (VSTOPPED(t)) {
sp->pr_why = PR_REQUESTED;
sp->pr_what = 0;
} else {
sp->pr_why = t->t_whystop;
sp->pr_what = t->t_whatstop;
}
sp->pr_lwpid = t->t_tid;
sp->pr_cursig = lwp->lwp_cursig;
prassignset(&sp->pr_lwppend, &t->t_sig);
prgethold(t, &sp->pr_lwphold);
if (t->t_whystop == PR_FAULTED)
bcopy(&lwp->lwp_siginfo,
&sp->pr_info, sizeof (k_siginfo_t));
else if (lwp->lwp_curinfo)
bcopy(&lwp->lwp_curinfo->sq_info,
&sp->pr_info, sizeof (k_siginfo_t));
if (SI_FROMUSER(&lwp->lwp_siginfo) && zp->zone_id != GLOBAL_ZONEID &&
sp->pr_info.si_zoneid != zp->zone_id) {
sp->pr_info.si_pid = zp->zone_zsched->p_pid;
sp->pr_info.si_uid = 0;
sp->pr_info.si_ctid = -1;
sp->pr_info.si_zoneid = zp->zone_id;
}
sp->pr_altstack = lwp->lwp_sigaltstack;
prgetaction(p, PTOU(p), lwp->lwp_cursig, &sp->pr_action);
sp->pr_oldcontext = (uintptr_t)lwp->lwp_oldcontext;
sp->pr_ustack = lwp->lwp_ustack;
(void) strncpy(sp->pr_clname, sclass[t->t_cid].cl_name,
sizeof (sp->pr_clname) - 1);
if (flags & PR_STOPPED)
hrt2ts(t->t_stoptime, &sp->pr_tstamp);
usr = ms->ms_acct[LMS_USER];
sys = ms->ms_acct[LMS_SYSTEM] + ms->ms_acct[LMS_TRAP];
scalehrtime(&usr);
scalehrtime(&sys);
hrt2ts(usr, &sp->pr_utime);
hrt2ts(sys, &sp->pr_stime);
if ((p->p_flag & SSYS) || p->p_as == &kas)
sp->pr_flags |= (PR_ISSYS|PR_PCINVAL);
else if (!(flags & PR_STOPPED))
sp->pr_flags |= PR_PCINVAL;
else if (!prfetchinstr(lwp, &instr))
sp->pr_flags |= PR_PCINVAL;
else
sp->pr_instr = instr;
mutex_exit(&p->p_lock);
if (prisstep(lwp))
sp->pr_flags |= PR_STEP;
if ((flags & (PR_STOPPED|PR_ASLEEP)) && t->t_sysnum) {
int i;
sp->pr_syscall = get_syscall_args(lwp,
(long *)sp->pr_sysarg, &i);
sp->pr_nsysarg = (ushort_t)i;
}
if ((flags & PR_STOPPED) || t == curthread)
prgetprregs(lwp, sp->pr_reg);
if ((t->t_state == TS_STOPPED && t->t_whystop == PR_SYSEXIT) ||
(flags & PR_VFORKP)) {
user_t *up;
auxv_t *auxp;
int i;
sp->pr_errno = prgetrvals(lwp, &sp->pr_rval1, &sp->pr_rval2);
if (sp->pr_errno == 0)
sp->pr_errpriv = PRIV_NONE;
else
sp->pr_errpriv = lwp->lwp_badpriv;
if (t->t_sysnum == SYS_execve) {
up = PTOU(p);
sp->pr_sysarg[0] = 0;
sp->pr_sysarg[1] = (uintptr_t)up->u_argv;
sp->pr_sysarg[2] = (uintptr_t)up->u_envp;
sp->pr_sysarg[3] = 0;
for (i = 0, auxp = up->u_auxv;
i < sizeof (up->u_auxv) / sizeof (up->u_auxv[0]);
i++, auxp++) {
if (auxp->a_type == AT_SUN_EXECNAME) {
sp->pr_sysarg[0] =
(uintptr_t)auxp->a_un.a_ptr;
break;
}
}
}
}
if (prhasfp())
prgetprfpregs(lwp, &sp->pr_fpreg);
mutex_enter(&p->p_lock);
}
void
prgetaction(proc_t *p, user_t *up, uint_t sig, struct sigaction *sp)
{
int nsig = PROC_IS_BRANDED(curproc)? BROP(curproc)->b_nsig : NSIG;
bzero(sp, sizeof (*sp));
if (sig != 0 && (unsigned)sig < nsig) {
sp->sa_handler = up->u_signal[sig-1];
prassignset(&sp->sa_mask, &up->u_sigmask[sig-1]);
if (sigismember(&up->u_sigonstack, sig))
sp->sa_flags |= SA_ONSTACK;
if (sigismember(&up->u_sigresethand, sig))
sp->sa_flags |= SA_RESETHAND;
if (sigismember(&up->u_sigrestart, sig))
sp->sa_flags |= SA_RESTART;
if (sigismember(&p->p_siginfo, sig))
sp->sa_flags |= SA_SIGINFO;
if (sigismember(&up->u_signodefer, sig))
sp->sa_flags |= SA_NODEFER;
if (sig == SIGCLD) {
if (p->p_flag & SNOWAIT)
sp->sa_flags |= SA_NOCLDWAIT;
if ((p->p_flag & SJCTL) == 0)
sp->sa_flags |= SA_NOCLDSTOP;
}
}
}
#ifdef _SYSCALL32_IMPL
void
prgetaction32(proc_t *p, user_t *up, uint_t sig, struct sigaction32 *sp)
{
int nsig = PROC_IS_BRANDED(curproc)? BROP(curproc)->b_nsig : NSIG;
bzero(sp, sizeof (*sp));
if (sig != 0 && (unsigned)sig < nsig) {
sp->sa_handler = (caddr32_t)(uintptr_t)up->u_signal[sig-1];
prassignset(&sp->sa_mask, &up->u_sigmask[sig-1]);
if (sigismember(&up->u_sigonstack, sig))
sp->sa_flags |= SA_ONSTACK;
if (sigismember(&up->u_sigresethand, sig))
sp->sa_flags |= SA_RESETHAND;
if (sigismember(&up->u_sigrestart, sig))
sp->sa_flags |= SA_RESTART;
if (sigismember(&p->p_siginfo, sig))
sp->sa_flags |= SA_SIGINFO;
if (sigismember(&up->u_signodefer, sig))
sp->sa_flags |= SA_NODEFER;
if (sig == SIGCLD) {
if (p->p_flag & SNOWAIT)
sp->sa_flags |= SA_NOCLDWAIT;
if ((p->p_flag & SJCTL) == 0)
sp->sa_flags |= SA_NOCLDSTOP;
}
}
}
#endif
uint_t
prnsegs(struct as *as, int reserved)
{
uint_t n = 0;
struct seg *seg;
ASSERT(as != &kas && AS_WRITE_HELD(as));
for (seg = AS_SEGFIRST(as); seg != NULL; seg = AS_SEGNEXT(as, seg)) {
caddr_t eaddr = seg->s_base + pr_getsegsize(seg, reserved);
caddr_t saddr, naddr;
void *tmp = NULL;
if ((seg->s_flags & S_HOLE) != 0) {
continue;
}
for (saddr = seg->s_base; saddr < eaddr; saddr = naddr) {
(void) pr_getprot(seg, reserved, &tmp,
&saddr, &naddr, eaddr);
if (saddr != naddr) {
n++;
if (n == INT_MAX) {
pr_getprot_done(&tmp);
ASSERT(tmp == NULL);
return (n);
}
}
}
ASSERT(tmp == NULL);
}
return (n);
}
int
pr_u32tos(uint32_t n, char *s, int len)
{
char cbuf[11];
char *cp = cbuf;
char *end = s + len;
do {
*cp++ = (char)(n % 10 + '0');
n /= 10;
} while (n);
len = (int)(cp - cbuf);
do {
*s++ = *--cp;
} while (cp > cbuf);
while (s < end)
*s++ = '\0';
return (len);
}
static int
pr_u64tos(uint64_t n, char *s)
{
char cbuf[21];
char *cp = cbuf;
int len;
do {
*cp++ = (char)(n % 10 + '0');
n /= 10;
} while (n);
len = (int)(cp - cbuf);
do {
*s++ = *--cp;
} while (cp > cbuf);
return (len);
}
uint64_t pr_getf_tryfails;
int pr_getf_maxtries = 3;
file_t *
pr_getf(proc_t *p, uint_t fd, short *flag)
{
uf_entry_t *ufp;
uf_info_t *fip;
file_t *fp;
int tries = 0;
ASSERT(MUTEX_HELD(&p->p_lock) && (p->p_proc_flag & P_PR_LOCK));
retry:
fip = P_FINFO(p);
if (fd >= fip->fi_nfiles)
return (NULL);
mutex_exit(&p->p_lock);
mutex_enter(&fip->fi_lock);
UF_ENTER(ufp, fip, fd);
if ((fp = ufp->uf_file) != NULL && fp->f_count > 0) {
if (mutex_tryenter(&fp->f_tlock)) {
ASSERT(fp->f_count > 0);
fp->f_count++;
mutex_exit(&fp->f_tlock);
if (flag != NULL)
*flag = ufp->uf_flag;
} else {
tries++;
pr_getf_tryfails++;
DTRACE_PROBE2(pr_getf_tryfail, file_t *, fp, int,
tries);
fp = NULL;
}
} else {
fp = NULL;
DTRACE_PROBE1(pr_getf_emptyslot, int, tries);
tries = pr_getf_maxtries;
}
UF_EXIT(ufp);
mutex_exit(&fip->fi_lock);
mutex_enter(&p->p_lock);
if (fp == NULL) {
if (tries < pr_getf_maxtries)
goto retry;
} else {
DTRACE_PROBE2(pr_getf_trysuccess, file_t *, fp, int, tries + 1);
}
return (fp);
}
void
pr_releasef(file_t *fp)
{
mutex_enter(&fp->f_tlock);
if (fp->f_count > 1) {
fp->f_count--;
mutex_exit(&fp->f_tlock);
} else {
mutex_exit(&fp->f_tlock);
(void) closef(fp);
}
}
void
pr_object_name(char *name, vnode_t *vp, struct vattr *vattr)
{
char *s = name;
struct vfs *vfsp;
struct vfssw *vfsswp;
if ((vfsp = vp->v_vfsp) != NULL &&
((vfsswp = vfssw + vfsp->vfs_fstype), vfsswp->vsw_name) &&
*vfsswp->vsw_name) {
(void) strcpy(s, vfsswp->vsw_name);
s += strlen(s);
*s++ = '.';
}
s += pr_u32tos(getmajor(vattr->va_fsid), s, 0);
*s++ = '.';
s += pr_u32tos(getminor(vattr->va_fsid), s, 0);
*s++ = '.';
s += pr_u64tos(vattr->va_nodeid, s);
*s++ = '\0';
}
struct seg *
break_seg(proc_t *p)
{
caddr_t addr = p->p_brkbase;
struct seg *seg;
struct vnode *vp;
if (p->p_brksize != 0)
addr += p->p_brksize - 1;
seg = as_segat(p->p_as, addr);
if (seg != NULL && seg->s_ops == &segvn_ops &&
(SEGOP_GETVP(seg, seg->s_base, &vp) != 0 || vp == NULL))
return (seg);
return (NULL);
}
typedef struct pr_iobuf_list {
list_node_t piol_link;
size_t piol_size;
size_t piol_usedsize;
} piol_t;
#define MAPSIZE (64 * 1024)
#define PIOL_DATABUF(iol) ((void *)(&(iol)[1]))
void
pr_iol_initlist(list_t *iolhead, size_t itemsize, int n)
{
piol_t *iol;
size_t initial_size = MIN(1, n) * itemsize;
list_create(iolhead, sizeof (piol_t), offsetof(piol_t, piol_link));
ASSERT(list_head(iolhead) == NULL);
ASSERT(itemsize < MAPSIZE - sizeof (*iol));
ASSERT(initial_size > 0);
initial_size = MIN(MAPSIZE, initial_size + sizeof (*iol));
iol = kmem_alloc(initial_size, KM_SLEEP);
list_insert_head(iolhead, iol);
iol->piol_usedsize = 0;
iol->piol_size = initial_size;
}
void *
pr_iol_newbuf(list_t *iolhead, size_t itemsize)
{
piol_t *iol;
char *new;
ASSERT(itemsize < MAPSIZE - sizeof (*iol));
ASSERT(list_head(iolhead) != NULL);
iol = (piol_t *)list_tail(iolhead);
if (iol->piol_size <
iol->piol_usedsize + sizeof (*iol) + itemsize) {
piol_t *newiol;
newiol = kmem_alloc(MAPSIZE, KM_SLEEP);
newiol->piol_size = MAPSIZE;
newiol->piol_usedsize = 0;
list_insert_after(iolhead, iol, newiol);
iol = list_next(iolhead, iol);
ASSERT(iol == newiol);
}
new = (char *)PIOL_DATABUF(iol) + iol->piol_usedsize;
iol->piol_usedsize += itemsize;
bzero(new, itemsize);
return (new);
}
void
pr_iol_freelist(list_t *iolhead)
{
piol_t *iol;
while ((iol = list_head(iolhead)) != NULL) {
list_remove(iolhead, iol);
kmem_free(iol, iol->piol_size);
}
list_destroy(iolhead);
}
int
pr_iol_copyout_and_free(list_t *iolhead, caddr_t *tgt, int errin)
{
int error = errin;
piol_t *iol;
while ((iol = list_head(iolhead)) != NULL) {
list_remove(iolhead, iol);
if (!error) {
if (copyout(PIOL_DATABUF(iol), *tgt,
iol->piol_usedsize))
error = EFAULT;
*tgt += iol->piol_usedsize;
}
kmem_free(iol, iol->piol_size);
}
list_destroy(iolhead);
return (error);
}
int
pr_iol_uiomove_and_free(list_t *iolhead, uio_t *uiop, int errin)
{
offset_t off = uiop->uio_offset;
char *base;
size_t size;
piol_t *iol;
int error = errin;
while ((iol = list_head(iolhead)) != NULL) {
list_remove(iolhead, iol);
base = PIOL_DATABUF(iol);
size = iol->piol_usedsize;
if (off <= size && error == 0 && uiop->uio_resid > 0)
error = uiomove(base + off, size - off,
UIO_READ, uiop);
off = MAX(0, off - (offset_t)size);
kmem_free(iol, iol->piol_size);
}
list_destroy(iolhead);
return (error);
}
int
prgetmap(proc_t *p, int reserved, list_t *iolhead)
{
struct as *as = p->p_as;
prmap_t *mp;
struct seg *seg;
struct seg *brkseg, *stkseg;
struct vnode *vp;
struct vattr vattr;
uint_t prot;
ASSERT(as != &kas && AS_WRITE_HELD(as));
pr_iol_initlist(iolhead, sizeof (*mp), avl_numnodes(&as->a_segtree));
if ((seg = AS_SEGFIRST(as)) == NULL)
return (0);
brkseg = break_seg(p);
stkseg = as_segat(as, prgetstackbase(p));
do {
caddr_t eaddr = seg->s_base + pr_getsegsize(seg, reserved);
caddr_t saddr, naddr;
void *tmp = NULL;
if ((seg->s_flags & S_HOLE) != 0) {
continue;
}
for (saddr = seg->s_base; saddr < eaddr; saddr = naddr) {
prot = pr_getprot(seg, reserved, &tmp,
&saddr, &naddr, eaddr);
if (saddr == naddr)
continue;
mp = pr_iol_newbuf(iolhead, sizeof (*mp));
mp->pr_vaddr = (uintptr_t)saddr;
mp->pr_size = naddr - saddr;
mp->pr_offset = SEGOP_GETOFFSET(seg, saddr);
mp->pr_mflags = 0;
if (prot & PROT_READ)
mp->pr_mflags |= MA_READ;
if (prot & PROT_WRITE)
mp->pr_mflags |= MA_WRITE;
if (prot & PROT_EXEC)
mp->pr_mflags |= MA_EXEC;
if (SEGOP_GETTYPE(seg, saddr) & MAP_SHARED)
mp->pr_mflags |= MA_SHARED;
if (SEGOP_GETTYPE(seg, saddr) & MAP_NORESERVE)
mp->pr_mflags |= MA_NORESERVE;
if (seg->s_ops == &segspt_shmops ||
(seg->s_ops == &segvn_ops &&
(SEGOP_GETVP(seg, saddr, &vp) != 0 || vp == NULL)))
mp->pr_mflags |= MA_ANON;
if (seg == brkseg)
mp->pr_mflags |= MA_BREAK;
else if (seg == stkseg) {
mp->pr_mflags |= MA_STACK;
if (reserved) {
size_t maxstack =
((size_t)p->p_stk_ctl +
PAGEOFFSET) & PAGEMASK;
mp->pr_vaddr =
(uintptr_t)prgetstackbase(p) +
p->p_stksize - maxstack;
mp->pr_size = (uintptr_t)naddr -
mp->pr_vaddr;
}
}
if (seg->s_ops == &segspt_shmops)
mp->pr_mflags |= MA_ISM | MA_SHM;
mp->pr_pagesize = PAGESIZE;
vattr.va_mask = AT_FSID|AT_NODEID;
if (seg->s_ops == &segvn_ops &&
SEGOP_GETVP(seg, saddr, &vp) == 0 &&
vp != NULL && vp->v_type == VREG &&
VOP_GETATTR(vp, &vattr, 0, CRED(), NULL) == 0) {
if (vp == p->p_exec)
(void) strcpy(mp->pr_mapname, "a.out");
else
pr_object_name(mp->pr_mapname,
vp, &vattr);
}
if ((mp->pr_mflags & MA_SHARED) && p->p_segacct &&
(mp->pr_shmid = shmgetid(p, seg->s_base)) !=
SHMID_NONE) {
if (mp->pr_shmid == SHMID_FREE)
mp->pr_shmid = -1;
mp->pr_mflags |= MA_SHM;
} else {
mp->pr_shmid = -1;
}
}
ASSERT(tmp == NULL);
} while ((seg = AS_SEGNEXT(as, seg)) != NULL);
return (0);
}
#ifdef _SYSCALL32_IMPL
int
prgetmap32(proc_t *p, int reserved, list_t *iolhead)
{
struct as *as = p->p_as;
prmap32_t *mp;
struct seg *seg;
struct seg *brkseg, *stkseg;
struct vnode *vp;
struct vattr vattr;
uint_t prot;
ASSERT(as != &kas && AS_WRITE_HELD(as));
pr_iol_initlist(iolhead, sizeof (*mp), avl_numnodes(&as->a_segtree));
if ((seg = AS_SEGFIRST(as)) == NULL)
return (0);
brkseg = break_seg(p);
stkseg = as_segat(as, prgetstackbase(p));
do {
caddr_t eaddr = seg->s_base + pr_getsegsize(seg, reserved);
caddr_t saddr, naddr;
void *tmp = NULL;
if ((seg->s_flags & S_HOLE) != 0) {
continue;
}
for (saddr = seg->s_base; saddr < eaddr; saddr = naddr) {
prot = pr_getprot(seg, reserved, &tmp,
&saddr, &naddr, eaddr);
if (saddr == naddr)
continue;
mp = pr_iol_newbuf(iolhead, sizeof (*mp));
mp->pr_vaddr = (caddr32_t)(uintptr_t)saddr;
mp->pr_size = (size32_t)(naddr - saddr);
mp->pr_offset = SEGOP_GETOFFSET(seg, saddr);
mp->pr_mflags = 0;
if (prot & PROT_READ)
mp->pr_mflags |= MA_READ;
if (prot & PROT_WRITE)
mp->pr_mflags |= MA_WRITE;
if (prot & PROT_EXEC)
mp->pr_mflags |= MA_EXEC;
if (SEGOP_GETTYPE(seg, saddr) & MAP_SHARED)
mp->pr_mflags |= MA_SHARED;
if (SEGOP_GETTYPE(seg, saddr) & MAP_NORESERVE)
mp->pr_mflags |= MA_NORESERVE;
if (seg->s_ops == &segspt_shmops ||
(seg->s_ops == &segvn_ops &&
(SEGOP_GETVP(seg, saddr, &vp) != 0 || vp == NULL)))
mp->pr_mflags |= MA_ANON;
if (seg == brkseg)
mp->pr_mflags |= MA_BREAK;
else if (seg == stkseg) {
mp->pr_mflags |= MA_STACK;
if (reserved) {
size_t maxstack =
((size_t)p->p_stk_ctl +
PAGEOFFSET) & PAGEMASK;
uintptr_t vaddr =
(uintptr_t)prgetstackbase(p) +
p->p_stksize - maxstack;
mp->pr_vaddr = (caddr32_t)vaddr;
mp->pr_size = (size32_t)
((uintptr_t)naddr - vaddr);
}
}
if (seg->s_ops == &segspt_shmops)
mp->pr_mflags |= MA_ISM | MA_SHM;
mp->pr_pagesize = PAGESIZE;
vattr.va_mask = AT_FSID|AT_NODEID;
if (seg->s_ops == &segvn_ops &&
SEGOP_GETVP(seg, saddr, &vp) == 0 &&
vp != NULL && vp->v_type == VREG &&
VOP_GETATTR(vp, &vattr, 0, CRED(), NULL) == 0) {
if (vp == p->p_exec)
(void) strcpy(mp->pr_mapname, "a.out");
else
pr_object_name(mp->pr_mapname,
vp, &vattr);
}
if ((mp->pr_mflags & MA_SHARED) && p->p_segacct &&
(mp->pr_shmid = shmgetid(p, seg->s_base)) !=
SHMID_NONE) {
if (mp->pr_shmid == SHMID_FREE)
mp->pr_shmid = -1;
mp->pr_mflags |= MA_SHM;
} else {
mp->pr_shmid = -1;
}
}
ASSERT(tmp == NULL);
} while ((seg = AS_SEGNEXT(as, seg)) != NULL);
return (0);
}
#endif
size_t
prpdsize(struct as *as)
{
struct seg *seg;
size_t size;
ASSERT(as != &kas && AS_WRITE_HELD(as));
if ((seg = AS_SEGFIRST(as)) == NULL)
return (0);
size = sizeof (prpageheader_t);
do {
caddr_t eaddr = seg->s_base + pr_getsegsize(seg, 0);
caddr_t saddr, naddr;
void *tmp = NULL;
size_t npage;
if ((seg->s_flags & S_HOLE) != 0) {
continue;
}
for (saddr = seg->s_base; saddr < eaddr; saddr = naddr) {
(void) pr_getprot(seg, 0, &tmp, &saddr, &naddr, eaddr);
if ((npage = (naddr - saddr) / PAGESIZE) != 0)
size += sizeof (prasmap_t) + round8(npage);
}
ASSERT(tmp == NULL);
} while ((seg = AS_SEGNEXT(as, seg)) != NULL);
return (size);
}
#ifdef _SYSCALL32_IMPL
size_t
prpdsize32(struct as *as)
{
struct seg *seg;
size_t size;
ASSERT(as != &kas && AS_WRITE_HELD(as));
if ((seg = AS_SEGFIRST(as)) == NULL)
return (0);
size = sizeof (prpageheader32_t);
do {
caddr_t eaddr = seg->s_base + pr_getsegsize(seg, 0);
caddr_t saddr, naddr;
void *tmp = NULL;
size_t npage;
if ((seg->s_flags & S_HOLE) != 0) {
continue;
}
for (saddr = seg->s_base; saddr < eaddr; saddr = naddr) {
(void) pr_getprot(seg, 0, &tmp, &saddr, &naddr, eaddr);
if ((npage = (naddr - saddr) / PAGESIZE) != 0)
size += sizeof (prasmap32_t) + round8(npage);
}
ASSERT(tmp == NULL);
} while ((seg = AS_SEGNEXT(as, seg)) != NULL);
return (size);
}
#endif
int
prpdread(proc_t *p, uint_t hatid, struct uio *uiop)
{
struct as *as = p->p_as;
caddr_t buf;
size_t size;
prpageheader_t *php;
prasmap_t *pmp;
struct seg *seg;
int error;
again:
AS_LOCK_ENTER(as, RW_WRITER);
if ((seg = AS_SEGFIRST(as)) == NULL) {
AS_LOCK_EXIT(as);
return (0);
}
size = prpdsize(as);
if (uiop->uio_resid < size) {
AS_LOCK_EXIT(as);
return (E2BIG);
}
buf = kmem_zalloc(size, KM_SLEEP);
php = (prpageheader_t *)buf;
pmp = (prasmap_t *)(buf + sizeof (prpageheader_t));
hrt2ts(gethrtime(), &php->pr_tstamp);
php->pr_nmap = 0;
php->pr_npage = 0;
do {
caddr_t eaddr = seg->s_base + pr_getsegsize(seg, 0);
caddr_t saddr, naddr;
void *tmp = NULL;
if ((seg->s_flags & S_HOLE) != 0) {
continue;
}
for (saddr = seg->s_base; saddr < eaddr; saddr = naddr) {
struct vnode *vp;
struct vattr vattr;
size_t len;
size_t npage;
uint_t prot;
uintptr_t next;
prot = pr_getprot(seg, 0, &tmp, &saddr, &naddr, eaddr);
if ((len = (size_t)(naddr - saddr)) == 0)
continue;
npage = len / PAGESIZE;
next = (uintptr_t)(pmp + 1) + round8(npage);
if (next > (uintptr_t)buf + size) {
pr_getprot_done(&tmp);
AS_LOCK_EXIT(as);
kmem_free(buf, size);
if (ISSIG(curthread, JUSTLOOKING))
return (EINTR);
goto again;
}
php->pr_nmap++;
php->pr_npage += npage;
pmp->pr_vaddr = (uintptr_t)saddr;
pmp->pr_npage = npage;
pmp->pr_offset = SEGOP_GETOFFSET(seg, saddr);
pmp->pr_mflags = 0;
if (prot & PROT_READ)
pmp->pr_mflags |= MA_READ;
if (prot & PROT_WRITE)
pmp->pr_mflags |= MA_WRITE;
if (prot & PROT_EXEC)
pmp->pr_mflags |= MA_EXEC;
if (SEGOP_GETTYPE(seg, saddr) & MAP_SHARED)
pmp->pr_mflags |= MA_SHARED;
if (SEGOP_GETTYPE(seg, saddr) & MAP_NORESERVE)
pmp->pr_mflags |= MA_NORESERVE;
if (seg->s_ops == &segspt_shmops ||
(seg->s_ops == &segvn_ops &&
(SEGOP_GETVP(seg, saddr, &vp) != 0 || vp == NULL)))
pmp->pr_mflags |= MA_ANON;
if (seg->s_ops == &segspt_shmops)
pmp->pr_mflags |= MA_ISM | MA_SHM;
pmp->pr_pagesize = PAGESIZE;
vattr.va_mask = AT_FSID|AT_NODEID;
if (seg->s_ops == &segvn_ops &&
SEGOP_GETVP(seg, saddr, &vp) == 0 &&
vp != NULL && vp->v_type == VREG &&
VOP_GETATTR(vp, &vattr, 0, CRED(), NULL) == 0) {
if (vp == p->p_exec)
(void) strcpy(pmp->pr_mapname, "a.out");
else
pr_object_name(pmp->pr_mapname,
vp, &vattr);
}
if ((pmp->pr_mflags & MA_SHARED) && p->p_segacct &&
(pmp->pr_shmid = shmgetid(p, seg->s_base)) !=
SHMID_NONE) {
if (pmp->pr_shmid == SHMID_FREE)
pmp->pr_shmid = -1;
pmp->pr_mflags |= MA_SHM;
} else {
pmp->pr_shmid = -1;
}
hat_getstat(as, saddr, len, hatid,
(char *)(pmp + 1), HAT_SYNC_ZERORM);
pmp = (prasmap_t *)next;
}
ASSERT(tmp == NULL);
} while ((seg = AS_SEGNEXT(as, seg)) != NULL);
AS_LOCK_EXIT(as);
ASSERT((uintptr_t)pmp <= (uintptr_t)buf + size);
error = uiomove(buf, (caddr_t)pmp - buf, UIO_READ, uiop);
kmem_free(buf, size);
return (error);
}
#ifdef _SYSCALL32_IMPL
int
prpdread32(proc_t *p, uint_t hatid, struct uio *uiop)
{
struct as *as = p->p_as;
caddr_t buf;
size_t size;
prpageheader32_t *php;
prasmap32_t *pmp;
struct seg *seg;
int error;
again:
AS_LOCK_ENTER(as, RW_WRITER);
if ((seg = AS_SEGFIRST(as)) == NULL) {
AS_LOCK_EXIT(as);
return (0);
}
size = prpdsize32(as);
if (uiop->uio_resid < size) {
AS_LOCK_EXIT(as);
return (E2BIG);
}
buf = kmem_zalloc(size, KM_SLEEP);
php = (prpageheader32_t *)buf;
pmp = (prasmap32_t *)(buf + sizeof (prpageheader32_t));
hrt2ts32(gethrtime(), &php->pr_tstamp);
php->pr_nmap = 0;
php->pr_npage = 0;
do {
caddr_t eaddr = seg->s_base + pr_getsegsize(seg, 0);
caddr_t saddr, naddr;
void *tmp = NULL;
if ((seg->s_flags & S_HOLE) != 0) {
continue;
}
for (saddr = seg->s_base; saddr < eaddr; saddr = naddr) {
struct vnode *vp;
struct vattr vattr;
size_t len;
size_t npage;
uint_t prot;
uintptr_t next;
prot = pr_getprot(seg, 0, &tmp, &saddr, &naddr, eaddr);
if ((len = (size_t)(naddr - saddr)) == 0)
continue;
npage = len / PAGESIZE;
next = (uintptr_t)(pmp + 1) + round8(npage);
if (next > (uintptr_t)buf + size) {
pr_getprot_done(&tmp);
AS_LOCK_EXIT(as);
kmem_free(buf, size);
if (ISSIG(curthread, JUSTLOOKING))
return (EINTR);
goto again;
}
php->pr_nmap++;
php->pr_npage += npage;
pmp->pr_vaddr = (caddr32_t)(uintptr_t)saddr;
pmp->pr_npage = (size32_t)npage;
pmp->pr_offset = SEGOP_GETOFFSET(seg, saddr);
pmp->pr_mflags = 0;
if (prot & PROT_READ)
pmp->pr_mflags |= MA_READ;
if (prot & PROT_WRITE)
pmp->pr_mflags |= MA_WRITE;
if (prot & PROT_EXEC)
pmp->pr_mflags |= MA_EXEC;
if (SEGOP_GETTYPE(seg, saddr) & MAP_SHARED)
pmp->pr_mflags |= MA_SHARED;
if (SEGOP_GETTYPE(seg, saddr) & MAP_NORESERVE)
pmp->pr_mflags |= MA_NORESERVE;
if (seg->s_ops == &segspt_shmops ||
(seg->s_ops == &segvn_ops &&
(SEGOP_GETVP(seg, saddr, &vp) != 0 || vp == NULL)))
pmp->pr_mflags |= MA_ANON;
if (seg->s_ops == &segspt_shmops)
pmp->pr_mflags |= MA_ISM | MA_SHM;
pmp->pr_pagesize = PAGESIZE;
vattr.va_mask = AT_FSID|AT_NODEID;
if (seg->s_ops == &segvn_ops &&
SEGOP_GETVP(seg, saddr, &vp) == 0 &&
vp != NULL && vp->v_type == VREG &&
VOP_GETATTR(vp, &vattr, 0, CRED(), NULL) == 0) {
if (vp == p->p_exec)
(void) strcpy(pmp->pr_mapname, "a.out");
else
pr_object_name(pmp->pr_mapname,
vp, &vattr);
}
if ((pmp->pr_mflags & MA_SHARED) && p->p_segacct &&
(pmp->pr_shmid = shmgetid(p, seg->s_base)) !=
SHMID_NONE) {
if (pmp->pr_shmid == SHMID_FREE)
pmp->pr_shmid = -1;
pmp->pr_mflags |= MA_SHM;
} else {
pmp->pr_shmid = -1;
}
hat_getstat(as, saddr, len, hatid,
(char *)(pmp + 1), HAT_SYNC_ZERORM);
pmp = (prasmap32_t *)next;
}
ASSERT(tmp == NULL);
} while ((seg = AS_SEGNEXT(as, seg)) != NULL);
AS_LOCK_EXIT(as);
ASSERT((uintptr_t)pmp <= (uintptr_t)buf + size);
error = uiomove(buf, (caddr_t)pmp - buf, UIO_READ, uiop);
kmem_free(buf, size);
return (error);
}
#endif
ushort_t
prgetpctcpu(uint64_t pct)
{
int nonline = zone_ncpus_online_get(curproc->p_zone);
if (nonline > 1)
pct /= nonline;
pct >>= 16;
if (pct > 0x8000)
pct = 0x8000;
return ((ushort_t)pct);
}
void
prgetpsinfo(proc_t *p, psinfo_t *psp)
{
kthread_t *t;
struct cred *cred;
hrtime_t hrutime, hrstime;
ASSERT(MUTEX_HELD(&p->p_lock));
if ((t = prchoose(p)) == NULL)
bzero(psp, sizeof (*psp));
else {
thread_unlock(t);
bzero(psp, sizeof (*psp) - sizeof (psp->pr_lwp));
}
psp->pr_flag = p->p_flag & (SSYS | SMSACCT);
psp->pr_nlwp = p->p_lwpcnt;
psp->pr_nzomb = p->p_zombcnt;
mutex_enter(&p->p_crlock);
cred = p->p_cred;
psp->pr_uid = crgetruid(cred);
psp->pr_euid = crgetuid(cred);
psp->pr_gid = crgetrgid(cred);
psp->pr_egid = crgetgid(cred);
mutex_exit(&p->p_crlock);
psp->pr_pid = p->p_pid;
if (curproc->p_zone->zone_id != GLOBAL_ZONEID &&
(p->p_flag & SZONETOP)) {
ASSERT(p->p_zone->zone_id != GLOBAL_ZONEID);
psp->pr_ppid = curproc->p_zone->zone_zsched->p_pid;
} else {
psp->pr_ppid = p->p_ppid;
}
psp->pr_pgid = p->p_pgrp;
psp->pr_sid = p->p_sessp->s_sid;
psp->pr_taskid = p->p_task->tk_tkid;
psp->pr_projid = p->p_task->tk_proj->kpj_id;
psp->pr_poolid = p->p_pool->pool_id;
psp->pr_zoneid = p->p_zone->zone_id;
if ((psp->pr_contract = PRCTID(p)) == 0)
psp->pr_contract = -1;
psp->pr_addr = (uintptr_t)prgetpsaddr(p);
switch (p->p_model) {
case DATAMODEL_ILP32:
psp->pr_dmodel = PR_MODEL_ILP32;
break;
case DATAMODEL_LP64:
psp->pr_dmodel = PR_MODEL_LP64;
break;
}
hrutime = mstate_aggr_state(p, LMS_USER);
hrstime = mstate_aggr_state(p, LMS_SYSTEM);
hrt2ts((hrutime + hrstime), &psp->pr_time);
TICK_TO_TIMESTRUC(p->p_cutime + p->p_cstime, &psp->pr_ctime);
if (t == NULL) {
int wcode = p->p_wcode;
if (wcode)
psp->pr_wstat = wstat(wcode, p->p_wdata);
psp->pr_ttydev = PRNODEV;
psp->pr_lwp.pr_state = SZOMB;
psp->pr_lwp.pr_sname = 'Z';
psp->pr_lwp.pr_bindpro = PBIND_NONE;
psp->pr_lwp.pr_bindpset = PS_NONE;
} else {
user_t *up = PTOU(p);
struct as *as;
dev_t d;
extern dev_t rwsconsdev, rconsdev, uconsdev;
d = cttydev(p);
if ((d == rwsconsdev || d == rconsdev) && d != NODEV)
d = uconsdev;
psp->pr_ttydev = (d == NODEV) ? PRNODEV : d;
psp->pr_start = up->u_start;
bcopy(up->u_comm, psp->pr_fname,
MIN(sizeof (up->u_comm), sizeof (psp->pr_fname)-1));
bcopy(up->u_psargs, psp->pr_psargs,
MIN(PRARGSZ-1, PSARGSZ));
psp->pr_argc = up->u_argc;
psp->pr_argv = up->u_argv;
psp->pr_envp = up->u_envp;
prgetlwpsinfo(t, &psp->pr_lwp);
if (p->p_lwpcnt == 1)
psp->pr_pctcpu = psp->pr_lwp.pr_pctcpu;
else {
uint64_t pct = 0;
hrtime_t cur_time = gethrtime_unscaled();
t = p->p_tlist;
do {
pct += cpu_update_pct(t, cur_time);
} while ((t = t->t_forw) != p->p_tlist);
psp->pr_pctcpu = prgetpctcpu(pct);
}
if ((p->p_flag & SSYS) || (as = p->p_as) == &kas) {
psp->pr_size = 0;
psp->pr_rssize = 0;
} else {
mutex_exit(&p->p_lock);
AS_LOCK_ENTER(as, RW_READER);
psp->pr_size = btopr(as->a_resvsize) *
(PAGESIZE / 1024);
psp->pr_rssize = rm_asrss(as) * (PAGESIZE / 1024);
psp->pr_pctmem = rm_pctmemory(as);
AS_LOCK_EXIT(as);
mutex_enter(&p->p_lock);
}
}
}
static size_t
prfdinfomisc(list_t *data, uint_t type, const void *val, size_t vlen)
{
pr_misc_header_t *misc;
size_t len;
len = PRFDINFO_ROUNDUP(sizeof (*misc) + vlen);
if (data != NULL) {
misc = pr_iol_newbuf(data, len);
misc->pr_misc_type = type;
misc->pr_misc_size = len;
misc++;
bcopy((char *)val, (char *)misc, vlen);
}
return (len);
}
static boolean_t
pristli(vnode_t *vp)
{
static const char *tlidevs[] = {
"udp", "udp6", "tcp", "tcp6"
};
char *devname;
uint_t i;
ASSERT(vp != NULL);
if (vp->v_type != VCHR || vp->v_stream == NULL || vp->v_rdev == 0)
return (B_FALSE);
if ((devname = mod_major_to_name(getmajor(vp->v_rdev))) == NULL)
return (B_FALSE);
for (i = 0; i < ARRAY_SIZE(tlidevs); i++) {
if (strcmp(devname, tlidevs[i]) == 0)
return (B_TRUE);
}
return (B_FALSE);
}
static size_t
prfdinfopath(proc_t *p, vnode_t *vp, list_t *data, cred_t *cred)
{
char *pathname;
size_t pathlen;
size_t sz = 0;
pathlen = MAXPATHLEN * 2 + 1;
pathname = kmem_alloc(pathlen, KM_SLEEP);
if (vnodetopath(NULL, vp, pathname, pathlen, cred) == 0) {
sz += prfdinfomisc(data, PR_PATHNAME,
pathname, strlen(pathname) + 1);
}
kmem_free(pathname, pathlen);
return (sz);
}
static size_t
prfdinfotlisockopt(vnode_t *vp, list_t *data, cred_t *cred)
{
strcmd_t strcmd;
int32_t rval;
size_t sz = 0;
strcmd.sc_cmd = TI_GETMYNAME;
strcmd.sc_timeout = 1;
strcmd.sc_len = STRCMDBUFSIZE;
if (VOP_IOCTL(vp, _I_CMD, (intptr_t)&strcmd, FKIOCTL, cred,
&rval, NULL) == 0 && strcmd.sc_len > 0) {
sz += prfdinfomisc(data, PR_SOCKETNAME, strcmd.sc_buf,
strcmd.sc_len);
}
strcmd.sc_cmd = TI_GETPEERNAME;
strcmd.sc_timeout = 1;
strcmd.sc_len = STRCMDBUFSIZE;
if (VOP_IOCTL(vp, _I_CMD, (intptr_t)&strcmd, FKIOCTL, cred,
&rval, NULL) == 0 && strcmd.sc_len > 0) {
sz += prfdinfomisc(data, PR_PEERSOCKNAME, strcmd.sc_buf,
strcmd.sc_len);
}
return (sz);
}
static size_t
prfdinfosockopt(vnode_t *vp, list_t *data, cred_t *cred)
{
sonode_t *so;
socklen_t vlen;
size_t sz = 0;
uint_t i;
if (vp->v_stream != NULL) {
so = VTOSO(vp->v_stream->sd_vnode);
if (so->so_version == SOV_STREAM)
so = NULL;
} else {
so = VTOSO(vp);
}
if (so == NULL)
return (0);
DTRACE_PROBE1(sonode, sonode_t *, so);
struct sockaddr_storage buf;
struct sockaddr *name = (struct sockaddr *)&buf;
vlen = sizeof (buf);
if (SOP_GETSOCKNAME(so, name, &vlen, cred) == 0 && vlen > 0)
sz += prfdinfomisc(data, PR_SOCKETNAME, name, vlen);
vlen = sizeof (buf);
if (SOP_GETPEERNAME(so, name, &vlen, B_FALSE, cred) == 0 && vlen > 0)
sz += prfdinfomisc(data, PR_PEERSOCKNAME, name, vlen);
static struct boolopt {
int level;
int opt;
int bopt;
} boolopts[] = {
{ SOL_SOCKET, SO_DEBUG, PR_SO_DEBUG },
{ SOL_SOCKET, SO_REUSEADDR, PR_SO_REUSEADDR },
#ifdef SO_REUSEPORT
{ SOL_SOCKET, SO_REUSEPORT, PR_SO_REUSEPORT },
#endif
{ SOL_SOCKET, SO_KEEPALIVE, PR_SO_KEEPALIVE },
{ SOL_SOCKET, SO_DONTROUTE, PR_SO_DONTROUTE },
{ SOL_SOCKET, SO_BROADCAST, PR_SO_BROADCAST },
{ SOL_SOCKET, SO_OOBINLINE, PR_SO_OOBINLINE },
{ SOL_SOCKET, SO_DGRAM_ERRIND, PR_SO_DGRAM_ERRIND },
{ SOL_SOCKET, SO_ALLZONES, PR_SO_ALLZONES },
{ SOL_SOCKET, SO_MAC_EXEMPT, PR_SO_MAC_EXEMPT },
{ SOL_SOCKET, SO_MAC_IMPLICIT, PR_SO_MAC_IMPLICIT },
{ SOL_SOCKET, SO_EXCLBIND, PR_SO_EXCLBIND },
{ SOL_SOCKET, SO_VRRP, PR_SO_VRRP },
{ IPPROTO_UDP, UDP_NAT_T_ENDPOINT,
PR_UDP_NAT_T_ENDPOINT }
};
prsockopts_bool_opts_t opts;
int val;
if (data != NULL) {
opts.prsock_bool_opts = 0;
for (i = 0; i < ARRAY_SIZE(boolopts); i++) {
vlen = sizeof (val);
if (SOP_GETSOCKOPT(so, boolopts[i].level,
boolopts[i].opt, &val, &vlen, 0, cred) == 0 &&
val != 0) {
opts.prsock_bool_opts |= boolopts[i].bopt;
}
}
}
sz += prfdinfomisc(data, PR_SOCKOPTS_BOOL_OPTS, &opts, sizeof (opts));
struct linger l;
vlen = sizeof (l);
if (SOP_GETSOCKOPT(so, SOL_SOCKET, SO_LINGER, &l, &vlen,
0, cred) == 0 && vlen > 0) {
sz += prfdinfomisc(data, PR_SOCKOPT_LINGER, &l, vlen);
}
static struct sopt {
int level;
int opt;
int bopt;
} sopts[] = {
{ SOL_SOCKET, SO_TYPE, PR_SOCKOPT_TYPE },
{ SOL_SOCKET, SO_SNDBUF, PR_SOCKOPT_SNDBUF },
{ SOL_SOCKET, SO_RCVBUF, PR_SOCKOPT_RCVBUF }
};
for (i = 0; i < ARRAY_SIZE(sopts); i++) {
vlen = sizeof (val);
if (SOP_GETSOCKOPT(so, sopts[i].level, sopts[i].opt,
&val, &vlen, 0, cred) == 0 && vlen > 0) {
sz += prfdinfomisc(data, sopts[i].bopt, &val, vlen);
}
}
in_addr_t nexthop_val;
vlen = sizeof (nexthop_val);
if (SOP_GETSOCKOPT(so, IPPROTO_IP, IP_NEXTHOP,
&nexthop_val, &vlen, 0, cred) == 0 && vlen > 0) {
sz += prfdinfomisc(data, PR_SOCKOPT_IP_NEXTHOP,
&nexthop_val, vlen);
}
struct sockaddr_in6 nexthop6_val;
vlen = sizeof (nexthop6_val);
if (SOP_GETSOCKOPT(so, IPPROTO_IPV6, IPV6_NEXTHOP,
&nexthop6_val, &vlen, 0, cred) == 0 && vlen > 0) {
sz += prfdinfomisc(data, PR_SOCKOPT_IPV6_NEXTHOP,
&nexthop6_val, vlen);
}
char cong[CC_ALGO_NAME_MAX];
vlen = sizeof (cong);
if (SOP_GETSOCKOPT(so, IPPROTO_TCP, TCP_CONGESTION,
&cong, &vlen, 0, cred) == 0 && vlen > 0) {
sz += prfdinfomisc(data, PR_SOCKOPT_TCP_CONGESTION, cong, vlen);
}
struct fil_info fi;
vlen = sizeof (fi);
if (SOP_GETSOCKOPT(so, SOL_FILTER, FIL_LIST,
&fi, &vlen, 0, cred) == 0 && vlen != 0) {
pr_misc_header_t *misc;
size_t len;
vlen = MIN(32, fi.fi_pos + 1);
vlen *= sizeof (fi);
len = PRFDINFO_ROUNDUP(sizeof (*misc) + vlen);
sz += len;
if (data != NULL) {
misc = pr_iol_newbuf(data, len);
misc->pr_misc_type = PR_SOCKFILTERS_PRIV;
misc->pr_misc_size = len;
misc++;
len = vlen;
if (SOP_GETSOCKOPT(so, SOL_FILTER, FIL_LIST,
misc, &vlen, 0, cred) == 0) {
if (vlen < len)
bzero(misc + vlen, len - vlen);
} else {
bzero(misc, vlen);
}
}
}
return (sz);
}
typedef struct prfdinfo_nm_path_cbdata {
proc_t *nmp_p;
u_offset_t nmp_sz;
list_t *nmp_data;
} prfdinfo_nm_path_cbdata_t;
static int
prfdinfo_nm_path(const struct namenode *np, cred_t *cred, void *arg)
{
prfdinfo_nm_path_cbdata_t *cb = arg;
cb->nmp_sz += prfdinfopath(cb->nmp_p, np->nm_vnode, cb->nmp_data, cred);
return (0);
}
u_offset_t
prgetfdinfosize(proc_t *p, vnode_t *vp, cred_t *cred)
{
u_offset_t sz;
sz = offsetof(prfdinfo_t, pr_misc) + sizeof (pr_misc_header_t);
switch (vp->v_type) {
case VDOOR: {
prfdinfo_nm_path_cbdata_t cb = {
.nmp_p = p,
.nmp_data = NULL,
.nmp_sz = 0
};
(void) nm_walk_mounts(vp, prfdinfo_nm_path, cred, &cb);
sz += cb.nmp_sz;
break;
}
case VSOCK:
break;
default:
sz += prfdinfopath(p, vp, NULL, cred);
}
if (vp->v_type == VSOCK)
sz += prfdinfosockopt(vp, NULL, cred);
if (pristli(vp))
sz += prfdinfotlisockopt(vp, NULL, cred);
return (sz);
}
int
prgetfdinfo(proc_t *p, vnode_t *vp, prfdinfo_t *fdinfo, cred_t *cred,
cred_t *file_cred, list_t *data)
{
vattr_t vattr;
int error;
fdinfo->pr_uid = (uid_t)-1;
fdinfo->pr_gid = (gid_t)-1;
fdinfo->pr_size = -1;
fdinfo->pr_locktype = F_UNLCK;
fdinfo->pr_lockpid = -1;
fdinfo->pr_locksysid = -1;
fdinfo->pr_peerpid = -1;
if (fdinfo->pr_offset != -1 &&
VOP_SEEK(vp, 0, (offset_t *)&fdinfo->pr_offset, NULL) != 0)
fdinfo->pr_offset = -1;
vattr.va_mask = AT_STAT;
if (VOP_GETATTR(vp, &vattr, 0, file_cred, NULL) == 0) {
fdinfo->pr_major = getmajor(vattr.va_fsid);
fdinfo->pr_minor = getminor(vattr.va_fsid);
fdinfo->pr_rmajor = getmajor(vattr.va_rdev);
fdinfo->pr_rminor = getminor(vattr.va_rdev);
fdinfo->pr_ino = (ino64_t)vattr.va_nodeid;
fdinfo->pr_size = (off64_t)vattr.va_size;
fdinfo->pr_mode = VTTOIF(vattr.va_type) | vattr.va_mode;
fdinfo->pr_uid = vattr.va_uid;
fdinfo->pr_gid = vattr.va_gid;
if (vp->v_type == VSOCK)
fdinfo->pr_fileflags |= sock_getfasync(vp);
}
flock64_t bf;
bzero(&bf, sizeof (bf));
bf.l_type = F_WRLCK;
if (VOP_FRLOCK(vp, F_GETLK, &bf,
(uint16_t)(fdinfo->pr_fileflags & 0xffff), 0, NULL,
cred, NULL) == 0 && bf.l_type != F_UNLCK) {
fdinfo->pr_locktype = bf.l_type;
fdinfo->pr_lockpid = bf.l_pid;
fdinfo->pr_locksysid = bf.l_sysid;
}
k_peercred_t kpc;
switch (vp->v_type) {
case VFIFO:
case VSOCK: {
int32_t rval;
error = VOP_IOCTL(vp, _I_GETPEERCRED, (intptr_t)&kpc,
FKIOCTL, cred, &rval, NULL);
break;
}
case VCHR: {
struct strioctl strioc;
int32_t rval;
if (vp->v_stream == NULL) {
error = ENOTSUP;
break;
}
strioc.ic_cmd = _I_GETPEERCRED;
strioc.ic_timout = INFTIM;
strioc.ic_len = (int)sizeof (k_peercred_t);
strioc.ic_dp = (char *)&kpc;
error = strdoioctl(vp->v_stream, &strioc, FNATIVE | FKIOCTL,
STR_NOSIG | K_TO_K, cred, &rval);
break;
}
default:
error = ENOTSUP;
break;
}
if (error == 0 && kpc.pc_cr != NULL) {
proc_t *peerp;
fdinfo->pr_peerpid = kpc.pc_cpid;
crfree(kpc.pc_cr);
mutex_enter(&pidlock);
if ((peerp = prfind(fdinfo->pr_peerpid)) != NULL) {
user_t *up;
mutex_enter(&peerp->p_lock);
mutex_exit(&pidlock);
up = PTOU(peerp);
bcopy(up->u_comm, fdinfo->pr_peername,
MIN(sizeof (up->u_comm),
sizeof (fdinfo->pr_peername) - 1));
mutex_exit(&peerp->p_lock);
} else {
mutex_exit(&pidlock);
}
}
switch (vp->v_type) {
case VDOOR: {
prfdinfo_nm_path_cbdata_t cb = {
.nmp_p = p,
.nmp_data = data,
.nmp_sz = 0
};
(void) nm_walk_mounts(vp, prfdinfo_nm_path, cred, &cb);
break;
}
case VSOCK:
break;
default:
(void) prfdinfopath(p, vp, data, cred);
}
if (vp->v_type == VSOCK)
(void) prfdinfosockopt(vp, data, cred);
if (pristli(vp))
(void) prfdinfotlisockopt(vp, data, cred);
pr_misc_header_t *misc;
misc = pr_iol_newbuf(data, sizeof (*misc));
misc->pr_misc_size = 0;
misc->pr_misc_type = (uint_t)-1;
return (0);
}
#ifdef _SYSCALL32_IMPL
void
prgetpsinfo32(proc_t *p, psinfo32_t *psp)
{
kthread_t *t;
struct cred *cred;
hrtime_t hrutime, hrstime;
ASSERT(MUTEX_HELD(&p->p_lock));
if ((t = prchoose(p)) == NULL)
bzero(psp, sizeof (*psp));
else {
thread_unlock(t);
bzero(psp, sizeof (*psp) - sizeof (psp->pr_lwp));
}
psp->pr_flag = p->p_flag & (SSYS | SMSACCT);
psp->pr_nlwp = p->p_lwpcnt;
psp->pr_nzomb = p->p_zombcnt;
mutex_enter(&p->p_crlock);
cred = p->p_cred;
psp->pr_uid = crgetruid(cred);
psp->pr_euid = crgetuid(cred);
psp->pr_gid = crgetrgid(cred);
psp->pr_egid = crgetgid(cred);
mutex_exit(&p->p_crlock);
psp->pr_pid = p->p_pid;
if (curproc->p_zone->zone_id != GLOBAL_ZONEID &&
(p->p_flag & SZONETOP)) {
ASSERT(p->p_zone->zone_id != GLOBAL_ZONEID);
psp->pr_ppid = curproc->p_zone->zone_zsched->p_pid;
} else {
psp->pr_ppid = p->p_ppid;
}
psp->pr_pgid = p->p_pgrp;
psp->pr_sid = p->p_sessp->s_sid;
psp->pr_taskid = p->p_task->tk_tkid;
psp->pr_projid = p->p_task->tk_proj->kpj_id;
psp->pr_poolid = p->p_pool->pool_id;
psp->pr_zoneid = p->p_zone->zone_id;
if ((psp->pr_contract = PRCTID(p)) == 0)
psp->pr_contract = -1;
psp->pr_addr = 0;
switch (p->p_model) {
case DATAMODEL_ILP32:
psp->pr_dmodel = PR_MODEL_ILP32;
break;
case DATAMODEL_LP64:
psp->pr_dmodel = PR_MODEL_LP64;
break;
}
hrutime = mstate_aggr_state(p, LMS_USER);
hrstime = mstate_aggr_state(p, LMS_SYSTEM);
hrt2ts32(hrutime + hrstime, &psp->pr_time);
TICK_TO_TIMESTRUC32(p->p_cutime + p->p_cstime, &psp->pr_ctime);
if (t == NULL) {
extern int wstat(int, int);
int wcode = p->p_wcode;
if (wcode)
psp->pr_wstat = wstat(wcode, p->p_wdata);
psp->pr_ttydev = PRNODEV32;
psp->pr_lwp.pr_state = SZOMB;
psp->pr_lwp.pr_sname = 'Z';
} else {
user_t *up = PTOU(p);
struct as *as;
dev_t d;
extern dev_t rwsconsdev, rconsdev, uconsdev;
d = cttydev(p);
if ((d == rwsconsdev || d == rconsdev) && d != NODEV)
d = uconsdev;
(void) cmpldev(&psp->pr_ttydev, d);
TIMESPEC_TO_TIMESPEC32(&psp->pr_start, &up->u_start);
bcopy(up->u_comm, psp->pr_fname,
MIN(sizeof (up->u_comm), sizeof (psp->pr_fname)-1));
bcopy(up->u_psargs, psp->pr_psargs,
MIN(PRARGSZ-1, PSARGSZ));
psp->pr_argc = up->u_argc;
psp->pr_argv = (caddr32_t)up->u_argv;
psp->pr_envp = (caddr32_t)up->u_envp;
prgetlwpsinfo32(t, &psp->pr_lwp);
if (p->p_lwpcnt == 1)
psp->pr_pctcpu = psp->pr_lwp.pr_pctcpu;
else {
uint64_t pct = 0;
hrtime_t cur_time;
t = p->p_tlist;
cur_time = gethrtime_unscaled();
do {
pct += cpu_update_pct(t, cur_time);
} while ((t = t->t_forw) != p->p_tlist);
psp->pr_pctcpu = prgetpctcpu(pct);
}
if ((p->p_flag & SSYS) || (as = p->p_as) == &kas) {
psp->pr_size = 0;
psp->pr_rssize = 0;
} else {
mutex_exit(&p->p_lock);
AS_LOCK_ENTER(as, RW_READER);
psp->pr_size = (size32_t)
(btopr(as->a_resvsize) * (PAGESIZE / 1024));
psp->pr_rssize = (size32_t)
(rm_asrss(as) * (PAGESIZE / 1024));
psp->pr_pctmem = rm_pctmemory(as);
AS_LOCK_EXIT(as);
mutex_enter(&p->p_lock);
}
}
if (p->p_model != DATAMODEL_ILP32) {
psp->pr_size = 0;
psp->pr_rssize = 0;
psp->pr_argv = 0;
psp->pr_envp = 0;
}
}
#endif
void
prgetlwpsinfo(kthread_t *t, lwpsinfo_t *psp)
{
klwp_t *lwp = ttolwp(t);
sobj_ops_t *sobj;
char c, state;
uint64_t pct;
int retval, niceval;
hrtime_t hrutime, hrstime;
ASSERT(MUTEX_HELD(&ttoproc(t)->p_lock));
bzero(psp, sizeof (*psp));
psp->pr_flag = 0;
psp->pr_lwpid = t->t_tid;
psp->pr_addr = (uintptr_t)t;
psp->pr_wchan = (uintptr_t)t->t_wchan;
state = VSTOPPED(t) ? TS_STOPPED : t->t_state;
switch (state) {
case TS_SLEEP: state = SSLEEP; c = 'S'; break;
case TS_RUN: state = SRUN; c = 'R'; break;
case TS_ONPROC: state = SONPROC; c = 'O'; break;
case TS_ZOMB: state = SZOMB; c = 'Z'; break;
case TS_STOPPED: state = SSTOP; c = 'T'; break;
case TS_WAIT: state = SWAIT; c = 'W'; break;
default: state = 0; c = '?'; break;
}
psp->pr_state = state;
psp->pr_sname = c;
if ((sobj = t->t_sobj_ops) != NULL)
psp->pr_stype = SOBJ_TYPE(sobj);
retval = CL_DONICE(t, NULL, 0, &niceval);
if (retval == 0) {
psp->pr_oldpri = v.v_maxsyspri - t->t_pri;
psp->pr_nice = niceval + NZERO;
}
psp->pr_syscall = t->t_sysnum;
psp->pr_pri = t->t_pri;
psp->pr_start.tv_sec = t->t_start;
psp->pr_start.tv_nsec = 0L;
hrutime = lwp->lwp_mstate.ms_acct[LMS_USER];
scalehrtime(&hrutime);
hrstime = lwp->lwp_mstate.ms_acct[LMS_SYSTEM] +
lwp->lwp_mstate.ms_acct[LMS_TRAP];
scalehrtime(&hrstime);
hrt2ts(hrutime + hrstime, &psp->pr_time);
pct = cpu_update_pct(t, gethrtime_unscaled());
psp->pr_pctcpu = prgetpctcpu(pct);
psp->pr_cpu = (psp->pr_pctcpu*100 + 0x6000) >> 15;
if (psp->pr_cpu > 99)
psp->pr_cpu = 99;
(void) strncpy(psp->pr_clname, sclass[t->t_cid].cl_name,
sizeof (psp->pr_clname) - 1);
bzero(psp->pr_name, sizeof (psp->pr_name));
psp->pr_onpro = t->t_cpu->cpu_id;
psp->pr_bindpro = t->t_bind_cpu;
psp->pr_bindpset = t->t_bind_pset;
psp->pr_lgrp = t->t_lpl->lpl_lgrpid;
}
#ifdef _SYSCALL32_IMPL
void
prgetlwpsinfo32(kthread_t *t, lwpsinfo32_t *psp)
{
klwp_t *lwp = ttolwp(t);
sobj_ops_t *sobj;
char c, state;
uint64_t pct;
int retval, niceval;
hrtime_t hrutime, hrstime;
ASSERT(MUTEX_HELD(&ttoproc(t)->p_lock));
bzero(psp, sizeof (*psp));
psp->pr_flag = 0;
psp->pr_lwpid = t->t_tid;
psp->pr_addr = 0;
psp->pr_wchan = 0;
state = VSTOPPED(t) ? TS_STOPPED : t->t_state;
switch (state) {
case TS_SLEEP: state = SSLEEP; c = 'S'; break;
case TS_RUN: state = SRUN; c = 'R'; break;
case TS_ONPROC: state = SONPROC; c = 'O'; break;
case TS_ZOMB: state = SZOMB; c = 'Z'; break;
case TS_STOPPED: state = SSTOP; c = 'T'; break;
case TS_WAIT: state = SWAIT; c = 'W'; break;
default: state = 0; c = '?'; break;
}
psp->pr_state = state;
psp->pr_sname = c;
if ((sobj = t->t_sobj_ops) != NULL)
psp->pr_stype = SOBJ_TYPE(sobj);
retval = CL_DONICE(t, NULL, 0, &niceval);
if (retval == 0) {
psp->pr_oldpri = v.v_maxsyspri - t->t_pri;
psp->pr_nice = niceval + NZERO;
} else {
psp->pr_oldpri = 0;
psp->pr_nice = 0;
}
psp->pr_syscall = t->t_sysnum;
psp->pr_pri = t->t_pri;
psp->pr_start.tv_sec = (time32_t)t->t_start;
psp->pr_start.tv_nsec = 0L;
hrutime = lwp->lwp_mstate.ms_acct[LMS_USER];
scalehrtime(&hrutime);
hrstime = lwp->lwp_mstate.ms_acct[LMS_SYSTEM] +
lwp->lwp_mstate.ms_acct[LMS_TRAP];
scalehrtime(&hrstime);
hrt2ts32(hrutime + hrstime, &psp->pr_time);
pct = cpu_update_pct(t, gethrtime_unscaled());
psp->pr_pctcpu = prgetpctcpu(pct);
psp->pr_cpu = (psp->pr_pctcpu*100 + 0x6000) >> 15;
if (psp->pr_cpu > 99)
psp->pr_cpu = 99;
(void) strncpy(psp->pr_clname, sclass[t->t_cid].cl_name,
sizeof (psp->pr_clname) - 1);
bzero(psp->pr_name, sizeof (psp->pr_name));
psp->pr_onpro = t->t_cpu->cpu_id;
psp->pr_bindpro = t->t_bind_cpu;
psp->pr_bindpset = t->t_bind_pset;
psp->pr_lgrp = t->t_lpl->lpl_lgrpid;
}
#endif
#ifdef _SYSCALL32_IMPL
#define PR_COPY_FIELD(s, d, field) d->field = s->field
#define PR_COPY_FIELD_ILP32(s, d, field) \
if (s->pr_dmodel == PR_MODEL_ILP32) { \
d->field = s->field; \
}
#define PR_COPY_TIMESPEC(s, d, field) \
TIMESPEC_TO_TIMESPEC32(&d->field, &s->field);
#define PR_COPY_BUF(s, d, field) \
bcopy(s->field, d->field, sizeof (d->field));
#define PR_IGNORE_FIELD(s, d, field)
void
lwpsinfo_kto32(const struct lwpsinfo *src, struct lwpsinfo32 *dest)
{
bzero(dest, sizeof (*dest));
PR_COPY_FIELD(src, dest, pr_flag);
PR_COPY_FIELD(src, dest, pr_lwpid);
PR_IGNORE_FIELD(src, dest, pr_addr);
PR_IGNORE_FIELD(src, dest, pr_wchan);
PR_COPY_FIELD(src, dest, pr_stype);
PR_COPY_FIELD(src, dest, pr_state);
PR_COPY_FIELD(src, dest, pr_sname);
PR_COPY_FIELD(src, dest, pr_nice);
PR_COPY_FIELD(src, dest, pr_syscall);
PR_COPY_FIELD(src, dest, pr_oldpri);
PR_COPY_FIELD(src, dest, pr_cpu);
PR_COPY_FIELD(src, dest, pr_pri);
PR_COPY_FIELD(src, dest, pr_pctcpu);
PR_COPY_TIMESPEC(src, dest, pr_start);
PR_COPY_BUF(src, dest, pr_clname);
PR_COPY_BUF(src, dest, pr_name);
PR_COPY_FIELD(src, dest, pr_onpro);
PR_COPY_FIELD(src, dest, pr_bindpro);
PR_COPY_FIELD(src, dest, pr_bindpset);
PR_COPY_FIELD(src, dest, pr_lgrp);
}
void
psinfo_kto32(const struct psinfo *src, struct psinfo32 *dest)
{
bzero(dest, sizeof (*dest));
PR_COPY_FIELD(src, dest, pr_flag);
PR_COPY_FIELD(src, dest, pr_nlwp);
PR_COPY_FIELD(src, dest, pr_pid);
PR_COPY_FIELD(src, dest, pr_ppid);
PR_COPY_FIELD(src, dest, pr_pgid);
PR_COPY_FIELD(src, dest, pr_sid);
PR_COPY_FIELD(src, dest, pr_uid);
PR_COPY_FIELD(src, dest, pr_euid);
PR_COPY_FIELD(src, dest, pr_gid);
PR_COPY_FIELD(src, dest, pr_egid);
PR_IGNORE_FIELD(src, dest, pr_addr);
PR_COPY_FIELD_ILP32(src, dest, pr_size);
PR_COPY_FIELD_ILP32(src, dest, pr_rssize);
PR_COPY_FIELD(src, dest, pr_ttydev);
PR_COPY_FIELD(src, dest, pr_pctcpu);
PR_COPY_FIELD(src, dest, pr_pctmem);
PR_COPY_TIMESPEC(src, dest, pr_start);
PR_COPY_TIMESPEC(src, dest, pr_time);
PR_COPY_TIMESPEC(src, dest, pr_ctime);
PR_COPY_BUF(src, dest, pr_fname);
PR_COPY_BUF(src, dest, pr_psargs);
PR_COPY_FIELD(src, dest, pr_wstat);
PR_COPY_FIELD(src, dest, pr_argc);
PR_COPY_FIELD_ILP32(src, dest, pr_argv);
PR_COPY_FIELD_ILP32(src, dest, pr_envp);
PR_COPY_FIELD(src, dest, pr_dmodel);
PR_COPY_FIELD(src, dest, pr_taskid);
PR_COPY_FIELD(src, dest, pr_projid);
PR_COPY_FIELD(src, dest, pr_nzomb);
PR_COPY_FIELD(src, dest, pr_poolid);
PR_COPY_FIELD(src, dest, pr_contract);
PR_COPY_FIELD(src, dest, pr_poolid);
PR_COPY_FIELD(src, dest, pr_poolid);
lwpsinfo_kto32(&src->pr_lwp, &dest->pr_lwp);
}
#undef PR_COPY_FIELD
#undef PR_COPY_FIELD_ILP32
#undef PR_COPY_TIMESPEC
#undef PR_COPY_BUF
#undef PR_IGNORE_FIELD
#endif
void
estimate_msacct(kthread_t *t, hrtime_t curtime)
{
proc_t *p;
if (t == NULL)
return;
p = ttoproc(t);
ASSERT(MUTEX_HELD(&p->p_lock));
if (p->p_flag & SSYS)
return;
t = p->p_tlist;
do {
t->t_proc_flag |= TP_MSACCT;
} while ((t = t->t_forw) != p->p_tlist);
p->p_flag |= SMSACCT;
}
void
disable_msacct(proc_t *p)
{
kthread_t *t;
ASSERT(MUTEX_HELD(&p->p_lock));
p->p_flag &= ~SMSACCT;
if ((t = p->p_tlist) != NULL) {
do {
t->t_proc_flag &= ~TP_MSACCT;
} while ((t = t->t_forw) != p->p_tlist);
}
}
void
prgetusage(kthread_t *t, prhusage_t *pup)
{
klwp_t *lwp = ttolwp(t);
hrtime_t *mstimep;
struct mstate *ms = &lwp->lwp_mstate;
int state;
int i;
hrtime_t curtime;
hrtime_t waitrq;
hrtime_t tmp1;
curtime = gethrtime_unscaled();
pup->pr_lwpid = t->t_tid;
pup->pr_count = 1;
pup->pr_create = ms->ms_start;
pup->pr_term = ms->ms_term;
scalehrtime(&pup->pr_create);
scalehrtime(&pup->pr_term);
if (ms->ms_term == 0) {
pup->pr_rtime = curtime - ms->ms_start;
scalehrtime(&pup->pr_rtime);
} else {
pup->pr_rtime = ms->ms_term - ms->ms_start;
scalehrtime(&pup->pr_rtime);
}
pup->pr_utime = ms->ms_acct[LMS_USER];
pup->pr_stime = ms->ms_acct[LMS_SYSTEM];
pup->pr_ttime = ms->ms_acct[LMS_TRAP];
pup->pr_tftime = ms->ms_acct[LMS_TFAULT];
pup->pr_dftime = ms->ms_acct[LMS_DFAULT];
pup->pr_kftime = ms->ms_acct[LMS_KFAULT];
pup->pr_ltime = ms->ms_acct[LMS_USER_LOCK];
pup->pr_slptime = ms->ms_acct[LMS_SLEEP];
pup->pr_wtime = ms->ms_acct[LMS_WAIT_CPU];
pup->pr_stoptime = ms->ms_acct[LMS_STOPPED];
prscaleusage(pup);
waitrq = t->t_waitrq;
if (waitrq != 0) {
if (waitrq > curtime) {
curtime = gethrtime_unscaled();
}
tmp1 = curtime - waitrq;
scalehrtime(&tmp1);
pup->pr_wtime += tmp1;
curtime = waitrq;
}
if (ms->ms_state_start > curtime) {
curtime = gethrtime_unscaled();
}
i = 0;
do {
switch (state = t->t_mstate) {
case LMS_SLEEP:
switch (state = ms->ms_prev) {
case LMS_TFAULT:
case LMS_DFAULT:
case LMS_KFAULT:
case LMS_USER_LOCK:
break;
default:
state = LMS_SLEEP;
break;
}
break;
case LMS_TFAULT:
case LMS_DFAULT:
case LMS_KFAULT:
case LMS_USER_LOCK:
state = LMS_SYSTEM;
break;
}
switch (state) {
case LMS_USER: mstimep = &pup->pr_utime; break;
case LMS_SYSTEM: mstimep = &pup->pr_stime; break;
case LMS_TRAP: mstimep = &pup->pr_ttime; break;
case LMS_TFAULT: mstimep = &pup->pr_tftime; break;
case LMS_DFAULT: mstimep = &pup->pr_dftime; break;
case LMS_KFAULT: mstimep = &pup->pr_kftime; break;
case LMS_USER_LOCK: mstimep = &pup->pr_ltime; break;
case LMS_SLEEP: mstimep = &pup->pr_slptime; break;
case LMS_WAIT_CPU: mstimep = &pup->pr_wtime; break;
case LMS_STOPPED: mstimep = &pup->pr_stoptime; break;
default: panic("prgetusage: unknown microstate");
}
tmp1 = curtime - ms->ms_state_start;
if (tmp1 < 0) {
curtime = gethrtime_unscaled();
i++;
continue;
}
scalehrtime(&tmp1);
} while (tmp1 < 0 && i < MAX_ITERS_SPIN);
*mstimep += tmp1;
pup->pr_tstamp = curtime;
scalehrtime(&pup->pr_tstamp);
pup->pr_minf = lwp->lwp_ru.minflt;
pup->pr_majf = lwp->lwp_ru.majflt;
pup->pr_nswap = lwp->lwp_ru.nswap;
pup->pr_inblk = lwp->lwp_ru.inblock;
pup->pr_oublk = lwp->lwp_ru.oublock;
pup->pr_msnd = lwp->lwp_ru.msgsnd;
pup->pr_mrcv = lwp->lwp_ru.msgrcv;
pup->pr_sigs = lwp->lwp_ru.nsignals;
pup->pr_vctx = lwp->lwp_ru.nvcsw;
pup->pr_ictx = lwp->lwp_ru.nivcsw;
pup->pr_sysc = lwp->lwp_ru.sysc;
pup->pr_ioch = lwp->lwp_ru.ioch;
}
void
prscaleusage(prhusage_t *usg)
{
scalehrtime(&usg->pr_utime);
scalehrtime(&usg->pr_stime);
scalehrtime(&usg->pr_ttime);
scalehrtime(&usg->pr_tftime);
scalehrtime(&usg->pr_dftime);
scalehrtime(&usg->pr_kftime);
scalehrtime(&usg->pr_ltime);
scalehrtime(&usg->pr_slptime);
scalehrtime(&usg->pr_wtime);
scalehrtime(&usg->pr_stoptime);
}
void
praddusage(kthread_t *t, prhusage_t *pup)
{
klwp_t *lwp = ttolwp(t);
hrtime_t *mstimep;
struct mstate *ms = &lwp->lwp_mstate;
int state;
int i;
hrtime_t curtime;
hrtime_t waitrq;
hrtime_t tmp;
prhusage_t conv;
curtime = gethrtime_unscaled();
if (ms->ms_term == 0) {
tmp = curtime - ms->ms_start;
scalehrtime(&tmp);
pup->pr_rtime += tmp;
} else {
tmp = ms->ms_term - ms->ms_start;
scalehrtime(&tmp);
pup->pr_rtime += tmp;
}
conv.pr_utime = ms->ms_acct[LMS_USER];
conv.pr_stime = ms->ms_acct[LMS_SYSTEM];
conv.pr_ttime = ms->ms_acct[LMS_TRAP];
conv.pr_tftime = ms->ms_acct[LMS_TFAULT];
conv.pr_dftime = ms->ms_acct[LMS_DFAULT];
conv.pr_kftime = ms->ms_acct[LMS_KFAULT];
conv.pr_ltime = ms->ms_acct[LMS_USER_LOCK];
conv.pr_slptime = ms->ms_acct[LMS_SLEEP];
conv.pr_wtime = ms->ms_acct[LMS_WAIT_CPU];
conv.pr_stoptime = ms->ms_acct[LMS_STOPPED];
prscaleusage(&conv);
pup->pr_utime += conv.pr_utime;
pup->pr_stime += conv.pr_stime;
pup->pr_ttime += conv.pr_ttime;
pup->pr_tftime += conv.pr_tftime;
pup->pr_dftime += conv.pr_dftime;
pup->pr_kftime += conv.pr_kftime;
pup->pr_ltime += conv.pr_ltime;
pup->pr_slptime += conv.pr_slptime;
pup->pr_wtime += conv.pr_wtime;
pup->pr_stoptime += conv.pr_stoptime;
waitrq = t->t_waitrq;
if (waitrq != 0) {
if (waitrq > curtime) {
curtime = gethrtime_unscaled();
}
tmp = curtime - waitrq;
scalehrtime(&tmp);
pup->pr_wtime += tmp;
curtime = waitrq;
}
if (ms->ms_state_start > curtime) {
curtime = gethrtime_unscaled();
}
i = 0;
do {
switch (state = t->t_mstate) {
case LMS_SLEEP:
switch (state = ms->ms_prev) {
case LMS_TFAULT:
case LMS_DFAULT:
case LMS_KFAULT:
case LMS_USER_LOCK:
break;
default:
state = LMS_SLEEP;
break;
}
break;
case LMS_TFAULT:
case LMS_DFAULT:
case LMS_KFAULT:
case LMS_USER_LOCK:
state = LMS_SYSTEM;
break;
}
switch (state) {
case LMS_USER: mstimep = &pup->pr_utime; break;
case LMS_SYSTEM: mstimep = &pup->pr_stime; break;
case LMS_TRAP: mstimep = &pup->pr_ttime; break;
case LMS_TFAULT: mstimep = &pup->pr_tftime; break;
case LMS_DFAULT: mstimep = &pup->pr_dftime; break;
case LMS_KFAULT: mstimep = &pup->pr_kftime; break;
case LMS_USER_LOCK: mstimep = &pup->pr_ltime; break;
case LMS_SLEEP: mstimep = &pup->pr_slptime; break;
case LMS_WAIT_CPU: mstimep = &pup->pr_wtime; break;
case LMS_STOPPED: mstimep = &pup->pr_stoptime; break;
default: panic("praddusage: unknown microstate");
}
tmp = curtime - ms->ms_state_start;
if (tmp < 0) {
curtime = gethrtime_unscaled();
i++;
continue;
}
scalehrtime(&tmp);
} while (tmp < 0 && i < MAX_ITERS_SPIN);
*mstimep += tmp;
pup->pr_tstamp = curtime;
scalehrtime(&pup->pr_tstamp);
pup->pr_minf += lwp->lwp_ru.minflt;
pup->pr_majf += lwp->lwp_ru.majflt;
pup->pr_nswap += lwp->lwp_ru.nswap;
pup->pr_inblk += lwp->lwp_ru.inblock;
pup->pr_oublk += lwp->lwp_ru.oublock;
pup->pr_msnd += lwp->lwp_ru.msgsnd;
pup->pr_mrcv += lwp->lwp_ru.msgrcv;
pup->pr_sigs += lwp->lwp_ru.nsignals;
pup->pr_vctx += lwp->lwp_ru.nvcsw;
pup->pr_ictx += lwp->lwp_ru.nivcsw;
pup->pr_sysc += lwp->lwp_ru.sysc;
pup->pr_ioch += lwp->lwp_ru.ioch;
}
void
prcvtusage(prhusage_t *pup, prusage_t *upup)
{
uint64_t *ullp;
ulong_t *ulp;
int i;
upup->pr_lwpid = pup->pr_lwpid;
upup->pr_count = pup->pr_count;
hrt2ts(pup->pr_tstamp, &upup->pr_tstamp);
hrt2ts(pup->pr_create, &upup->pr_create);
hrt2ts(pup->pr_term, &upup->pr_term);
hrt2ts(pup->pr_rtime, &upup->pr_rtime);
hrt2ts(pup->pr_utime, &upup->pr_utime);
hrt2ts(pup->pr_stime, &upup->pr_stime);
hrt2ts(pup->pr_ttime, &upup->pr_ttime);
hrt2ts(pup->pr_tftime, &upup->pr_tftime);
hrt2ts(pup->pr_dftime, &upup->pr_dftime);
hrt2ts(pup->pr_kftime, &upup->pr_kftime);
hrt2ts(pup->pr_ltime, &upup->pr_ltime);
hrt2ts(pup->pr_slptime, &upup->pr_slptime);
hrt2ts(pup->pr_wtime, &upup->pr_wtime);
hrt2ts(pup->pr_stoptime, &upup->pr_stoptime);
bzero(upup->filltime, sizeof (upup->filltime));
ullp = &pup->pr_minf;
ulp = &upup->pr_minf;
for (i = 0; i < 22; i++)
*ulp++ = (ulong_t)*ullp++;
}
#ifdef _SYSCALL32_IMPL
void
prcvtusage32(prhusage_t *pup, prusage32_t *upup)
{
uint64_t *ullp;
uint32_t *ulp;
int i;
upup->pr_lwpid = pup->pr_lwpid;
upup->pr_count = pup->pr_count;
hrt2ts32(pup->pr_tstamp, &upup->pr_tstamp);
hrt2ts32(pup->pr_create, &upup->pr_create);
hrt2ts32(pup->pr_term, &upup->pr_term);
hrt2ts32(pup->pr_rtime, &upup->pr_rtime);
hrt2ts32(pup->pr_utime, &upup->pr_utime);
hrt2ts32(pup->pr_stime, &upup->pr_stime);
hrt2ts32(pup->pr_ttime, &upup->pr_ttime);
hrt2ts32(pup->pr_tftime, &upup->pr_tftime);
hrt2ts32(pup->pr_dftime, &upup->pr_dftime);
hrt2ts32(pup->pr_kftime, &upup->pr_kftime);
hrt2ts32(pup->pr_ltime, &upup->pr_ltime);
hrt2ts32(pup->pr_slptime, &upup->pr_slptime);
hrt2ts32(pup->pr_wtime, &upup->pr_wtime);
hrt2ts32(pup->pr_stoptime, &upup->pr_stoptime);
bzero(upup->filltime, sizeof (upup->filltime));
ullp = &pup->pr_minf;
ulp = &upup->pr_minf;
for (i = 0; i < 22; i++)
*ulp++ = (uint32_t)*ullp++;
}
#endif
int
setisempty(uint32_t *sp, uint_t n)
{
while (n--)
if (*sp++)
return (0);
return (1);
}
int
set_watched_area(proc_t *p, struct watched_area *pwa)
{
caddr_t vaddr = pwa->wa_vaddr;
caddr_t eaddr = pwa->wa_eaddr;
ulong_t flags = pwa->wa_flags;
struct watched_area *target;
avl_index_t where;
int error = 0;
ASSERT(MUTEX_NOT_HELD(&p->p_lock));
ASSERT(p->p_proc_flag & P_PR_LOCK);
if (!pr_watch_active(p)) {
kthread_t *t;
mutex_enter(&p->p_lock);
if ((t = p->p_tlist) != NULL) {
do {
watch_enable(t);
} while ((t = t->t_forw) != p->p_tlist);
}
mutex_exit(&p->p_lock);
}
target = pr_find_watched_area(p, pwa, &where);
if (target != NULL) {
if (target->wa_vaddr != vaddr ||
target->wa_eaddr != eaddr)
error = EINVAL;
else if (target->wa_flags != flags) {
error = set_watched_page(p, vaddr, eaddr,
flags, target->wa_flags);
target->wa_flags = flags;
}
kmem_free(pwa, sizeof (struct watched_area));
} else {
avl_insert(&p->p_warea, pwa, where);
error = set_watched_page(p, vaddr, eaddr, flags, 0);
}
return (error);
}
int
clear_watched_area(proc_t *p, struct watched_area *pwa)
{
struct watched_area *found;
ASSERT(MUTEX_NOT_HELD(&p->p_lock));
ASSERT(p->p_proc_flag & P_PR_LOCK);
if (!pr_watch_active(p)) {
kmem_free(pwa, sizeof (struct watched_area));
return (0);
}
if ((found = pr_find_watched_area(p, pwa, NULL)) != NULL &&
found->wa_vaddr == pwa->wa_vaddr) {
clear_watched_page(p, found->wa_vaddr, found->wa_eaddr,
found->wa_flags);
avl_remove(&p->p_warea, found);
kmem_free(found, sizeof (struct watched_area));
}
kmem_free(pwa, sizeof (struct watched_area));
if (!pr_watch_active(p)) {
kthread_t *t;
mutex_enter(&p->p_lock);
if ((t = p->p_tlist) != NULL) {
do {
watch_disable(t);
} while ((t = t->t_forw) != p->p_tlist);
}
mutex_exit(&p->p_lock);
}
return (0);
}
void
pr_free_watchpoints(proc_t *p)
{
struct watched_area *delp;
void *cookie;
cookie = NULL;
while ((delp = avl_destroy_nodes(&p->p_warea, &cookie)) != NULL)
kmem_free(delp, sizeof (struct watched_area));
avl_destroy(&p->p_warea);
}
void
pr_free_watched_pages(proc_t *p)
{
struct as *as = p->p_as;
struct watched_page *pwp;
uint_t prot;
int retrycnt, err;
void *cookie;
if (as == NULL || avl_numnodes(&as->a_wpage) == 0)
return;
ASSERT(MUTEX_NOT_HELD(&curproc->p_lock));
AS_LOCK_ENTER(as, RW_WRITER);
pwp = avl_first(&as->a_wpage);
cookie = NULL;
while ((pwp = avl_destroy_nodes(&as->a_wpage, &cookie)) != NULL) {
retrycnt = 0;
if ((prot = pwp->wp_oprot) != 0) {
caddr_t addr = pwp->wp_vaddr;
struct seg *seg;
retry:
if ((pwp->wp_prot != prot ||
(pwp->wp_flags & WP_NOWATCH)) &&
(seg = as_segat(as, addr)) != NULL) {
err = SEGOP_SETPROT(seg, addr, PAGESIZE, prot);
if (err == IE_RETRY) {
ASSERT(retrycnt == 0);
retrycnt++;
goto retry;
}
}
}
kmem_free(pwp, sizeof (struct watched_page));
}
avl_destroy(&as->a_wpage);
p->p_wprot = NULL;
AS_LOCK_EXIT(as);
}
static int
set_watched_page(proc_t *p, caddr_t vaddr, caddr_t eaddr,
ulong_t flags, ulong_t oflags)
{
struct as *as = p->p_as;
avl_tree_t *pwp_tree;
struct watched_page *pwp, *newpwp;
struct watched_page tpw;
avl_index_t where;
struct seg *seg;
uint_t prot;
caddr_t addr;
newpwp = NULL;
for (addr = (caddr_t)((uintptr_t)vaddr & (uintptr_t)PAGEMASK);
addr < eaddr; addr += PAGESIZE) {
pwp = kmem_zalloc(sizeof (struct watched_page), KM_SLEEP);
pwp->wp_list = newpwp;
newpwp = pwp;
}
AS_LOCK_ENTER(as, RW_WRITER);
if (p->p_flag & SVFWAIT)
pwp_tree = &p->p_wpage;
else
pwp_tree = &as->a_wpage;
again:
if (avl_numnodes(pwp_tree) > prnwatch) {
AS_LOCK_EXIT(as);
while (newpwp != NULL) {
pwp = newpwp->wp_list;
kmem_free(newpwp, sizeof (struct watched_page));
newpwp = pwp;
}
return (E2BIG);
}
tpw.wp_vaddr = (caddr_t)((uintptr_t)vaddr & (uintptr_t)PAGEMASK);
if ((pwp = avl_find(pwp_tree, &tpw, &where)) == NULL) {
pwp = newpwp;
newpwp = newpwp->wp_list;
pwp->wp_list = NULL;
pwp->wp_vaddr = (caddr_t)((uintptr_t)vaddr &
(uintptr_t)PAGEMASK);
avl_insert(pwp_tree, pwp, where);
}
ASSERT(vaddr >= pwp->wp_vaddr && vaddr < pwp->wp_vaddr + PAGESIZE);
if (oflags & WA_READ)
pwp->wp_read--;
if (oflags & WA_WRITE)
pwp->wp_write--;
if (oflags & WA_EXEC)
pwp->wp_exec--;
ASSERT(pwp->wp_read >= 0);
ASSERT(pwp->wp_write >= 0);
ASSERT(pwp->wp_exec >= 0);
if (flags & WA_READ)
pwp->wp_read++;
if (flags & WA_WRITE)
pwp->wp_write++;
if (flags & WA_EXEC)
pwp->wp_exec++;
if (!(p->p_flag & SVFWAIT)) {
vaddr = pwp->wp_vaddr;
if (pwp->wp_oprot == 0 &&
(seg = as_segat(as, vaddr)) != NULL) {
SEGOP_GETPROT(seg, vaddr, 0, &prot);
pwp->wp_oprot = (uchar_t)prot;
pwp->wp_prot = (uchar_t)prot;
}
if (pwp->wp_oprot != 0) {
prot = pwp->wp_oprot;
if (pwp->wp_read)
prot &= ~(PROT_READ|PROT_WRITE|PROT_EXEC);
if (pwp->wp_write)
prot &= ~PROT_WRITE;
if (pwp->wp_exec)
prot &= ~(PROT_READ|PROT_WRITE|PROT_EXEC);
if (!(pwp->wp_flags & WP_NOWATCH) &&
pwp->wp_prot != prot &&
(pwp->wp_flags & WP_SETPROT) == 0) {
pwp->wp_flags |= WP_SETPROT;
pwp->wp_list = p->p_wprot;
p->p_wprot = pwp;
}
pwp->wp_prot = (uchar_t)prot;
}
}
if ((vaddr = pwp->wp_vaddr + PAGESIZE) < eaddr)
goto again;
AS_LOCK_EXIT(as);
while (newpwp != NULL) {
pwp = newpwp->wp_list;
kmem_free(newpwp, sizeof (struct watched_page));
newpwp = pwp;
}
return (0);
}
static void
clear_watched_page(proc_t *p, caddr_t vaddr, caddr_t eaddr, ulong_t flags)
{
struct as *as = p->p_as;
struct watched_page *pwp;
struct watched_page tpw;
avl_tree_t *tree;
avl_index_t where;
AS_LOCK_ENTER(as, RW_WRITER);
if (p->p_flag & SVFWAIT)
tree = &p->p_wpage;
else
tree = &as->a_wpage;
tpw.wp_vaddr = vaddr =
(caddr_t)((uintptr_t)vaddr & (uintptr_t)PAGEMASK);
pwp = avl_find(tree, &tpw, &where);
if (pwp == NULL)
pwp = avl_nearest(tree, where, AVL_AFTER);
while (pwp != NULL && pwp->wp_vaddr < eaddr) {
ASSERT(vaddr <= pwp->wp_vaddr);
if (flags & WA_READ)
pwp->wp_read--;
if (flags & WA_WRITE)
pwp->wp_write--;
if (flags & WA_EXEC)
pwp->wp_exec--;
if (pwp->wp_read + pwp->wp_write + pwp->wp_exec != 0) {
if (pwp->wp_oprot != 0) {
uint_t prot = pwp->wp_oprot;
if (pwp->wp_read)
prot &=
~(PROT_READ|PROT_WRITE|PROT_EXEC);
if (pwp->wp_write)
prot &= ~PROT_WRITE;
if (pwp->wp_exec)
prot &=
~(PROT_READ|PROT_WRITE|PROT_EXEC);
if (!(pwp->wp_flags & WP_NOWATCH) &&
pwp->wp_prot != prot &&
(pwp->wp_flags & WP_SETPROT) == 0) {
pwp->wp_flags |= WP_SETPROT;
pwp->wp_list = p->p_wprot;
p->p_wprot = pwp;
}
pwp->wp_prot = (uchar_t)prot;
}
} else {
if (pwp->wp_oprot != 0) {
pwp->wp_prot = pwp->wp_oprot;
if ((pwp->wp_flags & WP_SETPROT) == 0) {
pwp->wp_flags |= WP_SETPROT;
pwp->wp_list = p->p_wprot;
p->p_wprot = pwp;
}
}
}
pwp = AVL_NEXT(tree, pwp);
}
AS_LOCK_EXIT(as);
}
static void
getwatchprot(struct as *as, caddr_t addr, uint_t *prot)
{
struct watched_page *pwp;
struct watched_page tpw;
ASSERT(AS_LOCK_HELD(as));
tpw.wp_vaddr = (caddr_t)((uintptr_t)addr & (uintptr_t)PAGEMASK);
if ((pwp = avl_find(&as->a_wpage, &tpw, NULL)) != NULL)
*prot = pwp->wp_oprot;
}
static prpagev_t *
pr_pagev_create(struct seg *seg, int check_noreserve)
{
prpagev_t *pagev = kmem_alloc(sizeof (prpagev_t), KM_SLEEP);
size_t total_pages = seg_pages(seg);
pagev->pg_npages = MIN(total_pages, pagev_lim);
pagev->pg_pnbase = 0;
pagev->pg_protv =
kmem_alloc(pagev->pg_npages * sizeof (uint_t), KM_SLEEP);
if (check_noreserve)
pagev->pg_incore =
kmem_alloc(pagev->pg_npages * sizeof (char), KM_SLEEP);
else
pagev->pg_incore = NULL;
return (pagev);
}
static void
pr_pagev_destroy(prpagev_t *pagev)
{
if (pagev->pg_incore != NULL)
kmem_free(pagev->pg_incore, pagev->pg_npages * sizeof (char));
kmem_free(pagev->pg_protv, pagev->pg_npages * sizeof (uint_t));
kmem_free(pagev, sizeof (prpagev_t));
}
static caddr_t
pr_pagev_fill(prpagev_t *pagev, struct seg *seg, caddr_t addr, caddr_t eaddr)
{
ulong_t lastpg = seg_page(seg, eaddr - 1);
ulong_t pn, pnlim;
caddr_t saddr;
size_t len;
ASSERT(addr >= seg->s_base && addr <= eaddr);
if (addr == eaddr)
return (eaddr);
refill:
ASSERT(addr < eaddr);
pagev->pg_pnbase = seg_page(seg, addr);
pnlim = pagev->pg_pnbase + pagev->pg_npages;
saddr = addr;
if (lastpg < pnlim)
len = (size_t)(eaddr - addr);
else
len = pagev->pg_npages * PAGESIZE;
if (pagev->pg_incore != NULL) {
SEGOP_INCORE(seg, addr, len, pagev->pg_incore);
pn = pagev->pg_pnbase;
do {
if (pagev->pg_incore[pn++ - pagev->pg_pnbase])
goto out;
} while ((addr += PAGESIZE) < eaddr && pn < pnlim);
if (addr < eaddr)
goto refill;
}
out:
SEGOP_GETPROT(seg, saddr, len - 1, pagev->pg_protv);
return (addr);
}
static caddr_t
pr_pagev_nextprot(prpagev_t *pagev, struct seg *seg,
caddr_t *saddrp, caddr_t eaddr, uint_t *protp)
{
caddr_t base = seg->s_base + pagev->pg_pnbase * PAGESIZE;
caddr_t addr = MAX(*saddrp, base);
ulong_t pn = seg_page(seg, addr);
uint_t prot, nprot;
if (pagev->pg_incore != NULL) {
while (pagev->pg_incore[pn - pagev->pg_pnbase] == 0) {
if ((addr += PAGESIZE) == eaddr) {
*saddrp = addr;
prot = 0;
goto out;
}
if (++pn == pagev->pg_pnbase + pagev->pg_npages) {
addr = pr_pagev_fill(pagev, seg, addr, eaddr);
if (addr == eaddr) {
*saddrp = addr;
prot = 0;
goto out;
}
pn = seg_page(seg, addr);
}
}
}
pn = seg_page(seg, addr);
ASSERT(pn >= pagev->pg_pnbase);
ASSERT(pn < (pagev->pg_pnbase + pagev->pg_npages));
prot = pagev->pg_protv[pn - pagev->pg_pnbase];
getwatchprot(seg->s_as, addr, &prot);
*saddrp = addr;
while ((addr += PAGESIZE) < eaddr) {
if (++pn == pagev->pg_pnbase + pagev->pg_npages) {
caddr_t naddr = pr_pagev_fill(pagev, seg, addr, eaddr);
if (naddr != addr)
goto out;
pn = seg_page(seg, addr);
}
if (pagev->pg_incore != NULL &&
pagev->pg_incore[pn - pagev->pg_pnbase] == 0)
break;
nprot = pagev->pg_protv[pn - pagev->pg_pnbase];
getwatchprot(seg->s_as, addr, &nprot);
if (nprot != prot)
break;
}
out:
*protp = prot;
return (addr);
}
size_t
pr_getsegsize(struct seg *seg, int reserved)
{
size_t size = seg->s_size;
if (reserved)
return (size);
if (seg->s_ops == &segvn_ops) {
vattr_t vattr;
vnode_t *vp;
vattr.va_mask = AT_SIZE;
if (SEGOP_GETVP(seg, seg->s_base, &vp) == 0 &&
vp != NULL && vp->v_type == VREG &&
VOP_GETATTR(vp, &vattr, 0, CRED(), NULL) == 0) {
u_offset_t fsize = vattr.va_size;
u_offset_t offset = SEGOP_GETOFFSET(seg, seg->s_base);
if (fsize < offset)
fsize = 0;
else
fsize -= offset;
fsize = roundup(fsize, (u_offset_t)PAGESIZE);
if (fsize < (u_offset_t)size)
size = (size_t)fsize;
}
return (size);
}
if (seg->s_ops == &segspt_shmops)
return (MIN(spt_realsize(seg), size));
if (seg->s_ops == &segdev_ops &&
((SEGOP_GETTYPE(seg, seg->s_base) &
(MAP_SHARED | MAP_PRIVATE)) == 0))
return (0);
return (size);
}
uint_t
pr_getprot(struct seg *seg, int reserved, void **tmp,
caddr_t *saddrp, caddr_t *naddrp, caddr_t eaddr)
{
struct as *as = seg->s_as;
caddr_t saddr = *saddrp;
caddr_t naddr;
int check_noreserve;
uint_t prot;
union {
struct segvn_data *svd;
struct segdev_data *sdp;
void *data;
} s;
s.data = seg->s_data;
ASSERT(AS_WRITE_HELD(as));
ASSERT(saddr >= seg->s_base && saddr < eaddr);
ASSERT(eaddr <= seg->s_base + seg->s_size);
check_noreserve =
(!reserved && seg->s_ops == &segvn_ops && s.svd != NULL &&
(s.svd->vp == NULL || s.svd->vp->v_type != VREG) &&
(s.svd->flags & MAP_NORESERVE));
if (!check_noreserve && saddr == seg->s_base &&
seg->s_ops == &segvn_ops && s.svd != NULL && s.svd->pageprot == 0) {
prot = s.svd->prot;
getwatchprot(as, saddr, &prot);
naddr = eaddr;
} else if (saddr == seg->s_base && seg->s_ops == &segdev_ops &&
s.sdp != NULL && s.sdp->pageprot == 0) {
prot = s.sdp->prot;
getwatchprot(as, saddr, &prot);
naddr = eaddr;
} else {
prpagev_t *pagev;
if (saddr == seg->s_base) {
pagev = pr_pagev_create(seg, check_noreserve);
saddr = pr_pagev_fill(pagev, seg, saddr, eaddr);
ASSERT(*tmp == NULL);
*tmp = pagev;
ASSERT(saddr <= eaddr);
*saddrp = saddr;
if (saddr == eaddr) {
naddr = saddr;
prot = 0;
goto out;
}
} else {
ASSERT(*tmp != NULL);
pagev = (prpagev_t *)*tmp;
}
naddr = pr_pagev_nextprot(pagev, seg, saddrp, eaddr, &prot);
ASSERT(naddr <= eaddr);
}
out:
if (naddr == eaddr)
pr_getprot_done(tmp);
*naddrp = naddr;
return (prot);
}
void
pr_getprot_done(void **tmp)
{
if (*tmp != NULL) {
pr_pagev_destroy((prpagev_t *)*tmp);
*tmp = NULL;
}
}
int
pr_isobject(vnode_t *vp)
{
return (vn_matchops(vp, prvnodeops) && VTOP(vp)->pr_type == PR_OBJECT);
}
int
pr_isself(vnode_t *vp)
{
return (vn_matchops(vp, prvnodeops) &&
(VTOP(vp)->pr_flags & PR_ISSELF) &&
VTOP(vp)->pr_type != PR_PIDDIR);
}
static ssize_t
pr_getpagesize(struct seg *seg, caddr_t saddr, caddr_t *naddrp, caddr_t eaddr)
{
ssize_t pagesize, hatsize;
ASSERT(AS_WRITE_HELD(seg->s_as));
ASSERT(IS_P2ALIGNED(saddr, PAGESIZE));
ASSERT(IS_P2ALIGNED(eaddr, PAGESIZE));
ASSERT(saddr < eaddr);
pagesize = hatsize = hat_getpagesize(seg->s_as->a_hat, saddr);
ASSERT(pagesize == -1 || IS_P2ALIGNED(pagesize, pagesize));
ASSERT(pagesize != 0);
if (pagesize == -1)
pagesize = PAGESIZE;
saddr += P2NPHASE((uintptr_t)saddr, pagesize);
while (saddr < eaddr) {
if (hatsize != hat_getpagesize(seg->s_as->a_hat, saddr))
break;
ASSERT(IS_P2ALIGNED(saddr, pagesize));
saddr += pagesize;
}
*naddrp = ((saddr < eaddr) ? saddr : eaddr);
return (hatsize);
}
int
prgetxmap(proc_t *p, list_t *iolhead)
{
struct as *as = p->p_as;
prxmap_t *mp;
struct seg *seg;
struct seg *brkseg, *stkseg;
struct vnode *vp;
struct vattr vattr;
uint_t prot;
ASSERT(as != &kas && AS_WRITE_HELD(as));
pr_iol_initlist(iolhead, sizeof (*mp), avl_numnodes(&as->a_segtree));
if ((seg = AS_SEGFIRST(as)) == NULL)
return (0);
brkseg = break_seg(p);
stkseg = as_segat(as, prgetstackbase(p));
do {
caddr_t eaddr = seg->s_base + pr_getsegsize(seg, 0);
caddr_t saddr, naddr, baddr;
void *tmp = NULL;
ssize_t psz;
char *parr;
uint64_t npages;
uint64_t pagenum;
if ((seg->s_flags & S_HOLE) != 0) {
continue;
}
for (saddr = seg->s_base; saddr < eaddr; saddr = baddr) {
prot = pr_getprot(seg, 0, &tmp, &saddr, &baddr, eaddr);
ASSERT(baddr >= saddr && baddr <= eaddr);
for (; saddr < baddr; saddr = naddr) {
psz = pr_getpagesize(seg, saddr, &naddr, baddr);
ASSERT(naddr >= saddr && naddr <= baddr);
mp = pr_iol_newbuf(iolhead, sizeof (*mp));
mp->pr_vaddr = (uintptr_t)saddr;
mp->pr_size = naddr - saddr;
mp->pr_offset = SEGOP_GETOFFSET(seg, saddr);
mp->pr_mflags = 0;
if (prot & PROT_READ)
mp->pr_mflags |= MA_READ;
if (prot & PROT_WRITE)
mp->pr_mflags |= MA_WRITE;
if (prot & PROT_EXEC)
mp->pr_mflags |= MA_EXEC;
if (SEGOP_GETTYPE(seg, saddr) & MAP_SHARED)
mp->pr_mflags |= MA_SHARED;
if (SEGOP_GETTYPE(seg, saddr) & MAP_NORESERVE)
mp->pr_mflags |= MA_NORESERVE;
if (seg->s_ops == &segspt_shmops ||
(seg->s_ops == &segvn_ops &&
(SEGOP_GETVP(seg, saddr, &vp) != 0 ||
vp == NULL)))
mp->pr_mflags |= MA_ANON;
if (seg == brkseg)
mp->pr_mflags |= MA_BREAK;
else if (seg == stkseg)
mp->pr_mflags |= MA_STACK;
if (seg->s_ops == &segspt_shmops)
mp->pr_mflags |= MA_ISM | MA_SHM;
mp->pr_pagesize = PAGESIZE;
if (psz == -1) {
mp->pr_hatpagesize = 0;
} else {
mp->pr_hatpagesize = psz;
}
mp->pr_dev = PRNODEV;
vattr.va_mask = AT_FSID|AT_NODEID;
if (seg->s_ops == &segvn_ops &&
SEGOP_GETVP(seg, saddr, &vp) == 0 &&
vp != NULL && vp->v_type == VREG &&
VOP_GETATTR(vp, &vattr, 0, CRED(),
NULL) == 0) {
mp->pr_dev = vattr.va_fsid;
mp->pr_ino = vattr.va_nodeid;
if (vp == p->p_exec)
(void) strcpy(mp->pr_mapname,
"a.out");
else
pr_object_name(mp->pr_mapname,
vp, &vattr);
}
if ((mp->pr_mflags & MA_SHARED) &&
p->p_segacct && (mp->pr_shmid = shmgetid(p,
seg->s_base)) != SHMID_NONE) {
if (mp->pr_shmid == SHMID_FREE)
mp->pr_shmid = -1;
mp->pr_mflags |= MA_SHM;
} else {
mp->pr_shmid = -1;
}
npages = ((uintptr_t)(naddr - saddr)) >>
PAGESHIFT;
parr = kmem_zalloc(npages, KM_SLEEP);
SEGOP_INCORE(seg, saddr, naddr - saddr, parr);
for (pagenum = 0; pagenum < npages; pagenum++) {
if (parr[pagenum] & SEG_PAGE_INCORE)
mp->pr_rss++;
if (parr[pagenum] & SEG_PAGE_ANON)
mp->pr_anon++;
if (parr[pagenum] & SEG_PAGE_LOCKED)
mp->pr_locked++;
}
kmem_free(parr, npages);
}
}
ASSERT(tmp == NULL);
} while ((seg = AS_SEGNEXT(as, seg)) != NULL);
return (0);
}
void
prgetcred(proc_t *p, prcred_t *pcrp)
{
mutex_enter(&p->p_crlock);
cred2prcred(p->p_cred, pcrp);
mutex_exit(&p->p_crlock);
}
void
prgetsecflags(proc_t *p, prsecflags_t *psfp)
{
ASSERT(psfp != NULL);
bzero(psfp, sizeof (*psfp));
psfp->pr_version = PRSECFLAGS_VERSION_CURRENT;
psfp->pr_lower = p->p_secflags.psf_lower;
psfp->pr_upper = p->p_secflags.psf_upper;
psfp->pr_effective = p->p_secflags.psf_effective;
psfp->pr_inherit = p->p_secflags.psf_inherit;
}
size_t
prgetprivsize(void)
{
return (priv_prgetprivsize(NULL));
}
void
prgetpriv(proc_t *p, prpriv_t *pprp)
{
mutex_enter(&p->p_crlock);
cred2prpriv(p->p_cred, pprp);
mutex_exit(&p->p_crlock);
}
#ifdef _SYSCALL32_IMPL
int
prgetxmap32(proc_t *p, list_t *iolhead)
{
struct as *as = p->p_as;
prxmap32_t *mp;
struct seg *seg;
struct seg *brkseg, *stkseg;
struct vnode *vp;
struct vattr vattr;
uint_t prot;
ASSERT(as != &kas && AS_WRITE_HELD(as));
pr_iol_initlist(iolhead, sizeof (*mp), avl_numnodes(&as->a_segtree));
if ((seg = AS_SEGFIRST(as)) == NULL)
return (0);
brkseg = break_seg(p);
stkseg = as_segat(as, prgetstackbase(p));
do {
caddr_t eaddr = seg->s_base + pr_getsegsize(seg, 0);
caddr_t saddr, naddr, baddr;
void *tmp = NULL;
ssize_t psz;
char *parr;
uint64_t npages;
uint64_t pagenum;
if ((seg->s_flags & S_HOLE) != 0) {
continue;
}
for (saddr = seg->s_base; saddr < eaddr; saddr = baddr) {
prot = pr_getprot(seg, 0, &tmp, &saddr, &baddr, eaddr);
ASSERT(baddr >= saddr && baddr <= eaddr);
for (; saddr < baddr; saddr = naddr) {
psz = pr_getpagesize(seg, saddr, &naddr, baddr);
ASSERT(naddr >= saddr && naddr <= baddr);
mp = pr_iol_newbuf(iolhead, sizeof (*mp));
mp->pr_vaddr = (caddr32_t)(uintptr_t)saddr;
mp->pr_size = (size32_t)(naddr - saddr);
mp->pr_offset = SEGOP_GETOFFSET(seg, saddr);
mp->pr_mflags = 0;
if (prot & PROT_READ)
mp->pr_mflags |= MA_READ;
if (prot & PROT_WRITE)
mp->pr_mflags |= MA_WRITE;
if (prot & PROT_EXEC)
mp->pr_mflags |= MA_EXEC;
if (SEGOP_GETTYPE(seg, saddr) & MAP_SHARED)
mp->pr_mflags |= MA_SHARED;
if (SEGOP_GETTYPE(seg, saddr) & MAP_NORESERVE)
mp->pr_mflags |= MA_NORESERVE;
if (seg->s_ops == &segspt_shmops ||
(seg->s_ops == &segvn_ops &&
(SEGOP_GETVP(seg, saddr, &vp) != 0 ||
vp == NULL)))
mp->pr_mflags |= MA_ANON;
if (seg == brkseg)
mp->pr_mflags |= MA_BREAK;
else if (seg == stkseg)
mp->pr_mflags |= MA_STACK;
if (seg->s_ops == &segspt_shmops)
mp->pr_mflags |= MA_ISM | MA_SHM;
mp->pr_pagesize = PAGESIZE;
if (psz == -1) {
mp->pr_hatpagesize = 0;
} else {
mp->pr_hatpagesize = psz;
}
mp->pr_dev = PRNODEV32;
vattr.va_mask = AT_FSID|AT_NODEID;
if (seg->s_ops == &segvn_ops &&
SEGOP_GETVP(seg, saddr, &vp) == 0 &&
vp != NULL && vp->v_type == VREG &&
VOP_GETATTR(vp, &vattr, 0, CRED(),
NULL) == 0) {
(void) cmpldev(&mp->pr_dev,
vattr.va_fsid);
mp->pr_ino = vattr.va_nodeid;
if (vp == p->p_exec)
(void) strcpy(mp->pr_mapname,
"a.out");
else
pr_object_name(mp->pr_mapname,
vp, &vattr);
}
if ((mp->pr_mflags & MA_SHARED) &&
p->p_segacct && (mp->pr_shmid = shmgetid(p,
seg->s_base)) != SHMID_NONE) {
if (mp->pr_shmid == SHMID_FREE)
mp->pr_shmid = -1;
mp->pr_mflags |= MA_SHM;
} else {
mp->pr_shmid = -1;
}
npages = ((uintptr_t)(naddr - saddr)) >>
PAGESHIFT;
parr = kmem_zalloc(npages, KM_SLEEP);
SEGOP_INCORE(seg, saddr, naddr - saddr, parr);
for (pagenum = 0; pagenum < npages; pagenum++) {
if (parr[pagenum] & SEG_PAGE_INCORE)
mp->pr_rss++;
if (parr[pagenum] & SEG_PAGE_ANON)
mp->pr_anon++;
if (parr[pagenum] & SEG_PAGE_LOCKED)
mp->pr_locked++;
}
kmem_free(parr, npages);
}
}
ASSERT(tmp == NULL);
} while ((seg = AS_SEGNEXT(as, seg)) != NULL);
return (0);
}
#endif