#include <sys/param.h>
#include <sys/systm.h>
#include <sys/sysctl.h>
#include <sys/uio.h>
#include <sys/proc.h>
#include <sys/mount.h>
#include <sys/vnode.h>
#include <sys/malloc.h>
#include <sys/spinlock.h>
#include <sys/spinlock2.h>
#include <vfs/procfs/procfs.h>
#define PFS_HSIZE 1031
struct pfshead {
struct spinlock spin;
struct pfsnode *first;
} __cachealign;
static struct pfshead pfshead[PFS_HSIZE];
static struct lock procfslk = LOCK_INITIALIZER("pvplk", 0, 0);
MALLOC_DEFINE(M_PROCFS, "procfs", "procfs v_data");
#define PFSHASH(pid) &pfshead[((pid) & ~PFS_DEAD) % PFS_HSIZE]
int
procfs_allocvp(struct mount *mp, struct vnode **vpp, long pid, pfstype pfs_type)
{
struct pfsnode *pfs;
struct vnode *vp;
struct pfshead *ph;
int error;
ph = PFSHASH(pid);
loop:
spin_lock(&ph->spin);
for (pfs = ph->first; pfs; pfs = pfs->pfs_next) {
if (pfs->pfs_pid == pid && pfs->pfs_type == pfs_type &&
PFSTOV(pfs)->v_mount == mp) {
vp = PFSTOV(pfs);
vhold(vp);
spin_unlock(&ph->spin);
if (vget(vp, LK_EXCLUSIVE)) {
vdrop(vp);
goto loop;
}
vdrop(vp);
spin_lock(&ph->spin);
for (pfs = ph->first; pfs; pfs = pfs->pfs_next) {
if (PFSTOV(pfs) == vp &&
pfs->pfs_pid == pid &&
pfs->pfs_type == pfs_type &&
PFSTOV(pfs)->v_mount == mp) {
break;
}
}
if (pfs == NULL || PFSTOV(pfs) != vp) {
spin_unlock(&ph->spin);
vput(vp);
goto loop;
}
spin_unlock(&ph->spin);
*vpp = vp;
return (0);
}
}
spin_unlock(&ph->spin);
if (lockmgr(&procfslk, LK_EXCLUSIVE|LK_SLEEPFAIL))
goto loop;
pfs = kmalloc(sizeof(struct pfsnode), M_PROCFS, M_WAITOK);
error = getnewvnode(VT_PROCFS, mp, vpp, 0, 0);
if (error) {
kfree(pfs, M_PROCFS);
goto out;
}
vp = *vpp;
vp->v_data = pfs;
pfs->pfs_next = 0;
pfs->pfs_pid = (pid_t) pid;
pfs->pfs_type = pfs_type;
pfs->pfs_vnode = vp;
pfs->pfs_flags = 0;
pfs->pfs_fileno = PROCFS_FILENO(pid, pfs_type);
lockinit(&pfs->pfs_lock, "pfslk", 0, 0);
switch (pfs_type) {
case Proot:
pfs->pfs_mode = (VREAD|VEXEC) |
(VREAD|VEXEC) >> 3 |
(VREAD|VEXEC) >> 6;
vp->v_type = VDIR;
vp->v_flag = VROOT;
break;
case Pcurproc:
pfs->pfs_mode = (VREAD) |
(VREAD >> 3) |
(VREAD >> 6);
vp->v_type = VLNK;
break;
case Pproc:
pfs->pfs_mode = (VREAD|VEXEC) |
(VREAD|VEXEC) >> 3 |
(VREAD|VEXEC) >> 6;
vp->v_type = VDIR;
break;
case Pfile:
pfs->pfs_mode = (VREAD|VEXEC) |
(VREAD|VEXEC) >> 3 |
(VREAD|VEXEC) >> 6;
vp->v_type = VLNK;
break;
case Pmem:
pfs->pfs_mode = (VREAD|VWRITE);
vp->v_type = VREG;
break;
case Pregs:
case Pfpregs:
case Pdbregs:
pfs->pfs_mode = (VREAD|VWRITE);
vp->v_type = VREG;
break;
case Pctl:
case Pnote:
case Pnotepg:
pfs->pfs_mode = (VWRITE);
vp->v_type = VREG;
break;
case Ptype:
case Pmap:
case Pstatus:
case Pcmdline:
case Prlimit:
pfs->pfs_mode = (VREAD) |
(VREAD >> 3) |
(VREAD >> 6);
vp->v_type = VREG;
break;
default:
panic("procfs_allocvp");
}
spin_lock(&ph->spin);
pfs->pfs_next = ph->first;
ph->first = pfs;
spin_unlock(&ph->spin);
vx_downgrade(vp);
out:
lockmgr(&procfslk, LK_RELEASE);
return (error);
}
int
procfs_freevp(struct vnode *vp)
{
struct pfshead *ph;
struct pfsnode **pp;
struct pfsnode *pfs;
pfs = VTOPFS(vp);
vp->v_data = NULL;
ph = PFSHASH(pfs->pfs_pid);
spin_lock(&ph->spin);
pp = &ph->first;
while (*pp != pfs) {
KKASSERT(*pp != NULL);
pp = &(*pp)->pfs_next;
}
*pp = pfs->pfs_next;
spin_unlock(&ph->spin);
pfs->pfs_next = NULL;
pfs->pfs_vnode = NULL;
kfree(pfs, M_PROCFS);
return (0);
}
struct proc *
pfs_pfind(pid_t pfs_pid)
{
struct proc *p = NULL;
if (pfs_pid == 0) {
p = &proc0;
PHOLD(p);
} else {
p = pfind(pfs_pid);
}
if (p) {
lwkt_gettoken(&p->p_token);
if (p->p_flags & P_POSTEXIT) {
lwkt_reltoken(&p->p_token);
PRELE(p);
p = NULL;
}
}
return p;
}
struct proc *
pfs_zpfind(pid_t pfs_pid)
{
struct proc *p = NULL;
if (pfs_pid == 0) {
p = &proc0;
PHOLD(p);
} else {
p = zpfind(pfs_pid);
}
if (p) {
lwkt_gettoken(&p->p_token);
if (p->p_flags & P_POSTEXIT) {
lwkt_reltoken(&p->p_token);
PRELE(p);
p = NULL;
}
}
return p;
}
void
pfs_pdone(struct proc *p)
{
if (p) {
lwkt_reltoken(&p->p_token);
PRELE(p);
}
}
int
procfs_rw(struct vop_read_args *ap)
{
struct vnode *vp = ap->a_vp;
struct uio *uio = ap->a_uio;
struct thread *curtd = uio->uio_td;
struct proc *curp;
struct pfsnode *pfs = VTOPFS(vp);
struct proc *p;
struct lwp *lp;
int rtval;
if (curtd == NULL)
return (EINVAL);
if ((curp = curtd->td_proc) == NULL)
return (EINVAL);
p = pfs_pfind(pfs->pfs_pid);
if (p == NULL) {
rtval = EINVAL;
goto out;
}
if (p->p_pid == 1 && securelevel > 0 && uio->uio_rw == UIO_WRITE) {
rtval = EACCES;
goto out;
}
lp = FIRST_LWP_IN_PROC(p);
if (lp == NULL) {
rtval = EINVAL;
goto out;
}
LWPHOLD(lp);
lockmgr(&pfs->pfs_lock, LK_EXCLUSIVE);
switch (pfs->pfs_type) {
case Pnote:
case Pnotepg:
rtval = procfs_donote(curp, lp, pfs, uio);
break;
case Pregs:
rtval = procfs_doregs(curp, lp, pfs, uio);
break;
case Pfpregs:
rtval = procfs_dofpregs(curp, lp, pfs, uio);
break;
case Pdbregs:
rtval = procfs_dodbregs(curp, lp, pfs, uio);
break;
case Pctl:
rtval = procfs_doctl(curp, lp, pfs, uio);
break;
case Pstatus:
rtval = procfs_dostatus(curp, lp, pfs, uio);
break;
case Pmap:
rtval = procfs_domap(curp, lp, pfs, uio);
break;
case Pmem:
rtval = procfs_domem(curp, lp, pfs, uio);
break;
case Ptype:
rtval = procfs_dotype(curp, lp, pfs, uio);
break;
case Pcmdline:
rtval = procfs_docmdline(curp, lp, pfs, uio);
break;
case Prlimit:
rtval = procfs_dorlimit(curp, lp, pfs, uio);
break;
default:
rtval = EOPNOTSUPP;
break;
}
LWPRELE(lp);
lockmgr(&pfs->pfs_lock, LK_RELEASE);
if (uio->uio_rw == UIO_WRITE && rtval == 0)
KNOTE(&PFSTOV(pfs)->v_pollinfo.vpi_kqinfo.ki_note, NOTE_WRITE);
out:
pfs_pdone(p);
return rtval;
}
int
vfs_getuserstr(struct uio *uio, char *buf, int *buflenp)
{
int xlen;
int error;
if (uio->uio_offset != 0)
return (EINVAL);
xlen = *buflenp;
if (xlen < uio->uio_resid)
return (EMSGSIZE);
xlen = uio->uio_resid;
if ((error = uiomove(buf, xlen, uio)) != 0)
return (error);
uio->uio_offset = 0;
buf[xlen] = '\0';
xlen = strlen(buf);
if (xlen > 0 && buf[xlen-1] == '\n')
buf[--xlen] = '\0';
*buflenp = xlen;
return (0);
}
vfs_namemap_t *
vfs_findname(vfs_namemap_t *nm, char *buf, int buflen)
{
for (; nm->nm_name; nm++)
if (bcmp(buf, nm->nm_name, buflen+1) == 0)
return (nm);
return (0);
}
void
procfs_exit(struct thread *td)
{
struct pfshead *ph;
struct pfsnode *pfs;
struct vnode *vp;
pid_t pid;
KKASSERT(td->td_proc);
pid = td->td_proc->p_pid;
for (;;) {
ph = PFSHASH(pid);
spin_lock(&ph->spin);
for (pfs = ph->first; pfs; pfs = pfs->pfs_next) {
if (pfs->pfs_pid == pid)
break;
}
if (pfs == NULL) {
spin_unlock(&ph->spin);
break;
}
vp = PFSTOV(pfs);
vhold(vp);
spin_unlock(&ph->spin);
vx_get(vp);
pfs->pfs_pid |= PFS_DEAD;
vx_put(vp);
vdrop(vp);
}
}