Changeset View
Changeset View
Standalone View
Standalone View
kern/kern_jail.c
Show First 20 Lines • Show All 135 Lines • ▼ Show 20 Lines | |||||
struct prisonlist allprison = TAILQ_HEAD_INITIALIZER(allprison); | struct prisonlist allprison = TAILQ_HEAD_INITIALIZER(allprison); | ||||
LIST_HEAD(, prison_racct) allprison_racct; | LIST_HEAD(, prison_racct) allprison_racct; | ||||
int lastprid = 0; | int lastprid = 0; | ||||
static int get_next_prid(struct prison **insprp); | static int get_next_prid(struct prison **insprp); | ||||
static int do_jail_attach(struct thread *td, struct prison *pr, int drflags); | static int do_jail_attach(struct thread *td, struct prison *pr, int drflags); | ||||
static void prison_complete(void *context, int pending); | static void prison_complete(void *context, int pending); | ||||
static void prison_deref(struct prison *pr, int flags); | static void prison_deref(struct prison *pr, int flags); | ||||
static void prison_deref_kill(struct prison *pr, struct prisonlist *freeprison); | |||||
static int prison_lock_xlock(struct prison *pr, int flags); | static int prison_lock_xlock(struct prison *pr, int flags); | ||||
static void prison_free_not_last(struct prison *pr); | static void prison_free_not_last(struct prison *pr); | ||||
static void prison_proc_free_not_last(struct prison *pr); | |||||
static void prison_set_allow_locked(struct prison *pr, unsigned flag, | static void prison_set_allow_locked(struct prison *pr, unsigned flag, | ||||
int enable); | int enable); | ||||
static char *prison_path(struct prison *pr1, struct prison *pr2); | static char *prison_path(struct prison *pr1, struct prison *pr2); | ||||
static void prison_remove_one(struct prison *pr); | |||||
#ifdef RACCT | #ifdef RACCT | ||||
static void prison_racct_attach(struct prison *pr); | static void prison_racct_attach(struct prison *pr); | ||||
static void prison_racct_modify(struct prison *pr); | static void prison_racct_modify(struct prison *pr); | ||||
static void prison_racct_detach(struct prison *pr); | static void prison_racct_detach(struct prison *pr); | ||||
#endif | #endif | ||||
/* Flags for prison_deref */ | /* Flags for prison_deref */ | ||||
#define PD_DEREF 0x01 /* Decrement pr_ref */ | #define PD_DEREF 0x01 /* Decrement pr_ref */ | ||||
#define PD_DEUREF 0x02 /* Decrement pr_uref */ | #define PD_DEUREF 0x02 /* Decrement pr_uref */ | ||||
#define PD_LOCKED 0x04 /* pr_mtx is held */ | #define PD_KILL 0x04 /* Remove jail, kill processes, etc */ | ||||
#define PD_LIST_SLOCKED 0x08 /* allprison_lock is held shared */ | #define PD_LOCKED 0x10 /* pr_mtx is held */ | ||||
#define PD_LIST_XLOCKED 0x10 /* allprison_lock is held exclusive */ | #define PD_LIST_SLOCKED 0x20 /* allprison_lock is held shared */ | ||||
#define PD_LIST_XLOCKED 0x40 /* allprison_lock is held exclusive */ | |||||
/* | /* | ||||
* Parameter names corresponding to PR_* flag values. Size values are for kvm | * Parameter names corresponding to PR_* flag values. Size values are for kvm | ||||
* as we cannot figure out the size of a sparse array, or an array without a | * as we cannot figure out the size of a sparse array, or an array without a | ||||
* terminating entry. | * terminating entry. | ||||
*/ | */ | ||||
static struct bool_flags pr_flag_bool[] = { | static struct bool_flags pr_flag_bool[] = { | ||||
{"persist", "nopersist", PR_PERSIST}, | {"persist", "nopersist", PR_PERSIST}, | ||||
▲ Show 20 Lines • Show All 1,583 Lines • ▼ Show 20 Lines | if (ch_flags & PR_PERSIST & (pr_flags ^ pr->pr_flags)) { | ||||
} else { | } else { | ||||
drflags |= PD_DEUREF; | drflags |= PD_DEUREF; | ||||
prison_free_not_last(pr); | prison_free_not_last(pr); | ||||
} | } | ||||
} | } | ||||
pr->pr_flags = (pr->pr_flags & ~ch_flags) | pr_flags; | pr->pr_flags = (pr->pr_flags & ~ch_flags) | pr_flags; | ||||
mtx_unlock(&pr->pr_mtx); | mtx_unlock(&pr->pr_mtx); | ||||
drflags &= ~PD_LOCKED; | drflags &= ~PD_LOCKED; | ||||
/* | |||||
* Any errors past this point will need to de-persist newly created | |||||
* prisons, as well as call remove methods. | |||||
*/ | |||||
if (created) | |||||
drflags |= PD_KILL; | |||||
#ifdef RACCT | #ifdef RACCT | ||||
if (racct_enable && created) | if (racct_enable && created) | ||||
prison_racct_attach(pr); | prison_racct_attach(pr); | ||||
#endif | #endif | ||||
/* Locks may have prevented a complete restriction of child IP | /* Locks may have prevented a complete restriction of child IP | ||||
* addresses. If so, allocate some more memory and try again. | * addresses. If so, allocate some more memory and try again. | ||||
▲ Show 20 Lines • Show All 44 Lines • ▼ Show 20 Lines | #endif | ||||
mtx_unlock(&pr->pr_mtx); | mtx_unlock(&pr->pr_mtx); | ||||
} | } | ||||
#endif | #endif | ||||
/* Let the modules do their work. */ | /* Let the modules do their work. */ | ||||
if (born) { | if (born) { | ||||
error = osd_jail_call(pr, PR_METHOD_CREATE, opts); | error = osd_jail_call(pr, PR_METHOD_CREATE, opts); | ||||
if (error) | if (error) | ||||
goto done_remove; | goto done_deref; | ||||
} | } | ||||
error = osd_jail_call(pr, PR_METHOD_SET, opts); | error = osd_jail_call(pr, PR_METHOD_SET, opts); | ||||
if (error) { | if (error) | ||||
if (born) | |||||
goto done_remove; | |||||
goto done_deref; | goto done_deref; | ||||
} | |||||
/* New prisons are now ready to be seen. */ | /* New prisons are now ready to be seen. */ | ||||
if (created) { | if (created) { | ||||
mtx_lock(&pr->pr_mtx); | mtx_lock(&pr->pr_mtx); | ||||
drflags |= PD_LOCKED; | drflags |= PD_LOCKED; | ||||
pr->pr_state = refcount_load(&pr->pr_uref) > 0 | pr->pr_state = refcount_load(&pr->pr_uref) > 0 | ||||
? PRISON_STATE_ALIVE : PRISON_STATE_DYING; | ? PRISON_STATE_ALIVE : PRISON_STATE_DYING; | ||||
} | } | ||||
/* Attach this process to the prison if requested. */ | /* Attach this process to the prison if requested. */ | ||||
if (flags & JAIL_ATTACH) { | if (flags & JAIL_ATTACH) { | ||||
error = do_jail_attach(td, pr, prison_lock_xlock(pr, drflags)); | error = do_jail_attach(td, pr, prison_lock_xlock(pr, drflags)); | ||||
drflags &= ~(PD_LOCKED | PD_LIST_XLOCKED); | drflags &= ~(PD_LOCKED | PD_LIST_XLOCKED); | ||||
if (error) { | if (error) { | ||||
vfs_opterror(opts, "attach failed"); | vfs_opterror(opts, "attach failed"); | ||||
if (born) | |||||
goto done_remove; | |||||
goto done_deref; | goto done_deref; | ||||
} | } | ||||
} | } | ||||
#ifdef RACCT | #ifdef RACCT | ||||
if (racct_enable && !created) { | if (racct_enable && !created) { | ||||
if (drflags & PD_LIST_XLOCKED) { | if (drflags & PD_LIST_XLOCKED) { | ||||
sx_xunlock(&allprison_lock); | sx_xunlock(&allprison_lock); | ||||
drflags &= ~PD_LIST_XLOCKED; | drflags &= ~PD_LIST_XLOCKED; | ||||
} | } | ||||
prison_racct_modify(pr); | prison_racct_modify(pr); | ||||
} | } | ||||
#endif | #endif | ||||
drflags &= ~PD_KILL; | |||||
td->td_retval[0] = pr->pr_id; | td->td_retval[0] = pr->pr_id; | ||||
goto done_deref; | |||||
done_remove: | |||||
/* | |||||
* prison_deref will call the remove method when alive prisons die; | |||||
* otherwise it needs to be called now. | |||||
*/ | |||||
if (!(drflags & (PD_LIST_SLOCKED | PD_LIST_XLOCKED))) { | |||||
sx_xlock(&allprison_lock); | |||||
drflags |= PD_LIST_XLOCKED; | |||||
} | |||||
if (pr->pr_state != PRISON_STATE_ALIVE) | |||||
(void)osd_jail_call(pr, PR_METHOD_REMOVE, NULL); | |||||
/* Remove the persist flag from new (or resurrected) prisons. */ | |||||
drflags = prison_lock_xlock(pr, drflags); | |||||
if (pr->pr_flags & PR_PERSIST) { | |||||
pr->pr_flags &= ~PR_PERSIST; | |||||
drflags |= PD_DEUREF; | |||||
prison_free_not_last(pr); | |||||
} | |||||
done_deref: | done_deref: | ||||
/* Release any temporary prison holds and/or locks. */ | /* Release any temporary prison holds and/or locks. */ | ||||
if (pr != NULL) | if (pr != NULL) | ||||
prison_deref(pr, drflags); | prison_deref(pr, drflags); | ||||
else if (drflags & PD_LIST_SLOCKED) | else if (drflags & PD_LIST_SLOCKED) | ||||
sx_sunlock(&allprison_lock); | sx_sunlock(&allprison_lock); | ||||
else if (drflags & PD_LIST_XLOCKED) | else if (drflags & PD_LIST_XLOCKED) | ||||
sx_xunlock(&allprison_lock); | sx_xunlock(&allprison_lock); | ||||
▲ Show 20 Lines • Show All 407 Lines • ▼ Show 20 Lines | |||||
/* | /* | ||||
* struct jail_remove_args { | * struct jail_remove_args { | ||||
* int jid; | * int jid; | ||||
* }; | * }; | ||||
*/ | */ | ||||
int | int | ||||
sys_jail_remove(struct thread *td, struct jail_remove_args *uap) | sys_jail_remove(struct thread *td, struct jail_remove_args *uap) | ||||
{ | { | ||||
struct prison *pr, *cpr, *lpr; | struct prison *pr; | ||||
int descend, error; | int error; | ||||
error = priv_check(td, PRIV_JAIL_REMOVE); | error = priv_check(td, PRIV_JAIL_REMOVE); | ||||
if (error) | if (error) | ||||
return (error); | return (error); | ||||
sx_xlock(&allprison_lock); | sx_xlock(&allprison_lock); | ||||
pr = prison_find_child(td->td_ucred->cr_prison, uap->jid); | pr = prison_find_child(td->td_ucred->cr_prison, uap->jid); | ||||
if (pr == NULL) { | if (pr == NULL) { | ||||
sx_xunlock(&allprison_lock); | sx_xunlock(&allprison_lock); | ||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
if (!prison_isalive(pr)) { | |||||
/* Remove all descendants of this prison, then remove this prison. */ | /* Silently ignore already-dying prisons. */ | ||||
prison_hold(pr); | |||||
if (!LIST_EMPTY(&pr->pr_children)) { | |||||
mtx_unlock(&pr->pr_mtx); | mtx_unlock(&pr->pr_mtx); | ||||
lpr = NULL; | sx_xunlock(&allprison_lock); | ||||
FOREACH_PRISON_DESCENDANT(pr, cpr, descend) { | return (0); | ||||
KASSERT(prison_isvalid(cpr), | |||||
("Found invalid prison %p", cpr)); | |||||
prison_hold(cpr); | |||||
if (lpr != NULL) { | |||||
mtx_lock(&lpr->pr_mtx); | |||||
prison_remove_one(lpr); | |||||
sx_xlock(&allprison_lock); | |||||
} | } | ||||
lpr = cpr; | prison_deref(pr, PD_KILL | PD_LOCKED | PD_LIST_XLOCKED); | ||||
} | |||||
if (lpr != NULL) { | |||||
mtx_lock(&lpr->pr_mtx); | |||||
prison_remove_one(lpr); | |||||
sx_xlock(&allprison_lock); | |||||
} | |||||
mtx_lock(&pr->pr_mtx); | |||||
} | |||||
prison_remove_one(pr); | |||||
return (0); | return (0); | ||||
} | } | ||||
static void | |||||
prison_remove_one(struct prison *pr) | |||||
{ | |||||
struct proc *p; | |||||
int drflags; | |||||
drflags = PD_DEREF | PD_LOCKED | PD_LIST_XLOCKED; | |||||
/* If the prison was persistent, it is not anymore. */ | |||||
if (pr->pr_flags & PR_PERSIST) { | |||||
prison_free_not_last(pr); | |||||
drflags |= PD_DEUREF; | |||||
pr->pr_flags &= ~PR_PERSIST; | |||||
} | |||||
/* | /* | ||||
* jail_remove added a reference. If that's the only one, remove | |||||
* the prison now. refcount(9) doesn't guarantee the cache coherence | |||||
* of non-zero counters, so force it here. | |||||
*/ | |||||
KASSERT(refcount_load(&pr->pr_ref) > 0, | |||||
("prison_remove_one removing a dead prison (jid=%d)", pr->pr_id)); | |||||
if (atomic_load_acq_int(&pr->pr_ref) == 1) { | |||||
prison_deref(pr, drflags); | |||||
return; | |||||
} | |||||
mtx_unlock(&pr->pr_mtx); | |||||
sx_xunlock(&allprison_lock); | |||||
drflags &= ~(PD_LOCKED | PD_LIST_XLOCKED); | |||||
/* | |||||
* Kill all processes unfortunate enough to be attached to this prison. | |||||
*/ | |||||
sx_slock(&allproc_lock); | |||||
FOREACH_PROC_IN_SYSTEM(p) { | |||||
PROC_LOCK(p); | |||||
if (p->p_state != PRS_NEW && p->p_ucred && | |||||
p->p_ucred->cr_prison == pr) | |||||
kern_psignal(p, SIGKILL); | |||||
PROC_UNLOCK(p); | |||||
} | |||||
sx_sunlock(&allproc_lock); | |||||
/* Remove the temporary reference added by jail_remove. */ | |||||
prison_deref(pr, drflags); | |||||
} | |||||
/* | |||||
* struct jail_attach_args { | * struct jail_attach_args { | ||||
* int jid; | * int jid; | ||||
* }; | * }; | ||||
*/ | */ | ||||
int | int | ||||
sys_jail_attach(struct thread *td, struct jail_attach_args *uap) | sys_jail_attach(struct thread *td, struct jail_attach_args *uap) | ||||
{ | { | ||||
struct prison *pr; | struct prison *pr; | ||||
▲ Show 20 Lines • Show All 329 Lines • ▼ Show 20 Lines | KASSERT(!(pr->pr_flags & PR_COMPLETE_PROC), | ||||
("Redundant last reference in prison_proc_free (jid=%d)", | ("Redundant last reference in prison_proc_free (jid=%d)", | ||||
pr->pr_id)); | pr->pr_id)); | ||||
pr->pr_flags |= PR_COMPLETE_PROC; | pr->pr_flags |= PR_COMPLETE_PROC; | ||||
mtx_unlock(&pr->pr_mtx); | mtx_unlock(&pr->pr_mtx); | ||||
taskqueue_enqueue(taskqueue_thread, &pr->pr_task); | taskqueue_enqueue(taskqueue_thread, &pr->pr_task); | ||||
} | } | ||||
} | } | ||||
static void | |||||
prison_proc_free_not_last(struct prison *pr) | |||||
{ | |||||
#ifdef INVARIANTS | |||||
int lastref; | |||||
KASSERT(refcount_load(&pr->pr_uref) > 0, | |||||
("Trying to free dead prison %p (jid=%d).", | |||||
pr, pr->pr_id)); | |||||
lastref = refcount_release(&pr->pr_uref); | |||||
KASSERT(!lastref, | |||||
("prison_free_not_last freed last ref on prison %p (jid=%d).", | |||||
pr, pr->pr_id)); | |||||
#else | |||||
refcount_release(&pr>pr_uref); | |||||
#endif | |||||
} | |||||
/* | /* | ||||
* Complete a call to either prison_free or prison_proc_free. | * Complete a call to either prison_free or prison_proc_free. | ||||
*/ | */ | ||||
static void | static void | ||||
prison_complete(void *context, int pending) | prison_complete(void *context, int pending) | ||||
{ | { | ||||
struct prison *pr = context; | struct prison *pr = context; | ||||
int drflags; | int drflags; | ||||
Show All 19 Lines | |||||
* If there are no more references, release and delist the prison. | * If there are no more references, release and delist the prison. | ||||
* On completion, the prison lock and the allprison lock are both | * On completion, the prison lock and the allprison lock are both | ||||
* unlocked. | * unlocked. | ||||
*/ | */ | ||||
static void | static void | ||||
prison_deref(struct prison *pr, int flags) | prison_deref(struct prison *pr, int flags) | ||||
{ | { | ||||
struct prisonlist freeprison; | struct prisonlist freeprison; | ||||
struct prison *ppr, *rpr, *tpr; | struct prison *killpr, *ppr, *rpr, *tpr; | ||||
struct proc *p; | |||||
killpr = NULL; | |||||
TAILQ_INIT(&freeprison); | TAILQ_INIT(&freeprison); | ||||
/* | /* | ||||
* Release this prison as requested, which may cause its parent to be | * Release this prison as requested, which may cause its parent to be | ||||
* released, and then maybe its grandparent, etc. | * released, and then maybe its grandparent, etc. | ||||
*/ | */ | ||||
for (;;) { | for (;;) { | ||||
if (flags & PD_KILL) { | |||||
/* Kill the prison and its descendents. */ | |||||
flags &= ~PD_KILL; | |||||
if (!(flags & PD_DEREF)) { | |||||
prison_hold(pr); | |||||
flags |= PD_DEREF; | |||||
} | |||||
flags = prison_lock_xlock(pr, flags); | |||||
if (pr->pr_state != PRISON_STATE_DYING) { | |||||
/* | |||||
* The prison might currently be invalid, | |||||
* but call it alive now so PD_DEUREF will | |||||
* notice it later. | |||||
*/ | |||||
pr->pr_state = PRISON_STATE_ALIVE; | |||||
if (!(flags & PD_DEUREF)) { | |||||
refcount_acquire(&pr->pr_uref); | |||||
flags |= PD_DEUREF; | |||||
} | |||||
prison_deref_kill(pr, &freeprison); | |||||
} | |||||
/* | |||||
* Any remaining user references are probably processes | |||||
* that need to be killed, either in this prison or its | |||||
* descendants. | |||||
*/ | |||||
if (atomic_load_acq_int(&pr->pr_uref) > 1) | |||||
killpr = pr; | |||||
} | |||||
if (flags & PD_DEUREF) { | if (flags & PD_DEUREF) { | ||||
/* Drop a user reference. */ | /* Drop a user reference. */ | ||||
KASSERT(refcount_load(&pr->pr_uref) > 0, | KASSERT(refcount_load(&pr->pr_uref) > 0, | ||||
("prison_deref PD_DEUREF on a dead prison (jid=%d)", | ("prison_deref PD_DEUREF on a dead prison (jid=%d)", | ||||
pr->pr_id)); | pr->pr_id)); | ||||
if (!refcount_release_if_not_last(&pr->pr_uref)) { | if (!refcount_release_if_not_last(&pr->pr_uref)) { | ||||
if (!(flags & PD_DEREF)) { | if (!(flags & PD_DEREF)) { | ||||
prison_hold(pr); | prison_hold(pr); | ||||
▲ Show 20 Lines • Show All 64 Lines • ▼ Show 20 Lines | prison_deref(struct prison *pr, int flags) | ||||
/* Release all the prison locks. */ | /* Release all the prison locks. */ | ||||
if (flags & PD_LOCKED) | if (flags & PD_LOCKED) | ||||
mtx_unlock(&pr->pr_mtx); | mtx_unlock(&pr->pr_mtx); | ||||
if (flags & PD_LIST_SLOCKED) | if (flags & PD_LIST_SLOCKED) | ||||
sx_sunlock(&allprison_lock); | sx_sunlock(&allprison_lock); | ||||
else if (flags & PD_LIST_XLOCKED) | else if (flags & PD_LIST_XLOCKED) | ||||
sx_xunlock(&allprison_lock); | sx_xunlock(&allprison_lock); | ||||
if (killpr != NULL) { | |||||
/* | /* | ||||
* Find and kill any processes attached to the killed prison | |||||
* or its descendants. | |||||
*/ | |||||
sx_slock(&allproc_lock); | |||||
FOREACH_PROC_IN_SYSTEM(p) { | |||||
PROC_LOCK(p); | |||||
if (p->p_state != PRS_NEW && p->p_ucred != NULL) { | |||||
for (ppr = p->p_ucred->cr_prison; | |||||
ppr != &prison0; | |||||
ppr = ppr->pr_parent) | |||||
if (ppr == killpr) { | |||||
kern_psignal(p, SIGKILL); | |||||
break; | |||||
} | |||||
} | |||||
PROC_UNLOCK(p); | |||||
} | |||||
sx_sunlock(&allproc_lock); | |||||
} | |||||
/* | |||||
* Finish removing any unreferenced prisons, which couldn't happen | * Finish removing any unreferenced prisons, which couldn't happen | ||||
* while allprison_lock was held (to avoid a LOR on vrele). | * while allprison_lock was held (to avoid a LOR on vrele). | ||||
*/ | */ | ||||
TAILQ_FOREACH_SAFE(rpr, &freeprison, pr_list, tpr) { | TAILQ_FOREACH_SAFE(rpr, &freeprison, pr_list, tpr) { | ||||
#ifdef VIMAGE | #ifdef VIMAGE | ||||
if (rpr->pr_vnet != rpr->pr_parent->pr_vnet) | if (rpr->pr_vnet != rpr->pr_parent->pr_vnet) | ||||
vnet_destroy(rpr->pr_vnet); | vnet_destroy(rpr->pr_vnet); | ||||
#endif | #endif | ||||
Show All 11 Lines | #endif | ||||
osd_jail_exit(rpr); | osd_jail_exit(rpr); | ||||
#ifdef RACCT | #ifdef RACCT | ||||
if (racct_enable) | if (racct_enable) | ||||
prison_racct_detach(rpr); | prison_racct_detach(rpr); | ||||
#endif | #endif | ||||
TAILQ_REMOVE(&freeprison, rpr, pr_list); | TAILQ_REMOVE(&freeprison, rpr, pr_list); | ||||
free(rpr, M_PRISON); | free(rpr, M_PRISON); | ||||
} | } | ||||
} | |||||
/* | |||||
* Kill the prison and its descendants. Mark them as dying, clear the | |||||
* persist flag, and call module remove methods. | |||||
*/ | |||||
static void | |||||
prison_deref_kill(struct prison *pr, struct prisonlist *freeprison) | |||||
{ | |||||
struct prison *cpr, *ppr; | |||||
bool descend; | |||||
/* | |||||
* The operation each descendant is similar to what prison_deref() | |||||
* does when losing the last references, plus clearing PR_PERSIST. | |||||
*/ | |||||
mtx_unlock(&pr->pr_mtx); | |||||
FOREACH_PRISON_DESCENDANT_PRE_POST(pr, cpr, descend) { | |||||
if (!prison_isalive(cpr)) | |||||
continue; | |||||
if (descend) { | |||||
prison_hold(cpr); | |||||
prison_proc_hold(cpr); | |||||
continue; | |||||
} | |||||
mtx_lock(&cpr->pr_mtx); | |||||
if (cpr->pr_flags & PR_PERSIST) { | |||||
cpr->pr_flags &= ~PR_PERSIST; | |||||
prison_proc_free_not_last(cpr); | |||||
prison_free_not_last(cpr); | |||||
} | |||||
if (refcount_release(&cpr->pr_uref)) { | |||||
cpr->pr_state = PRISON_STATE_DYING; | |||||
mtx_unlock(&cpr->pr_mtx); | |||||
(void)osd_jail_call(cpr, PR_METHOD_REMOVE, NULL); | |||||
mtx_lock(&cpr->pr_mtx); | |||||
} | |||||
if (refcount_release(&cpr->pr_ref)) { | |||||
cpr->pr_state = PRISON_STATE_INVALID; | |||||
TAILQ_REMOVE(&allprison, cpr, pr_list); | |||||
TAILQ_INSERT_TAIL(freeprison, cpr, pr_list); | |||||
mtx_unlock(&cpr->pr_mtx); | |||||
ppr = cpr->pr_parent; | |||||
prison_proc_free_not_last(ppr); | |||||
prison_free_not_last(ppr); | |||||
for (; ppr != NULL; ppr = ppr->pr_parent) | |||||
ppr->pr_childcount--; | |||||
} | |||||
else | |||||
mtx_unlock(&cpr->pr_mtx); | |||||
} | |||||
mtx_lock(&pr->pr_mtx); | |||||
if (pr->pr_flags & PR_PERSIST) { | |||||
pr->pr_flags &= ~PR_PERSIST; | |||||
prison_proc_free_not_last(pr); | |||||
prison_free_not_last(pr); | |||||
} | |||||
/* | |||||
* Disconnect unreferenced descendants from their parents, | |||||
* which couldn't easily be done mid-loop. | |||||
*/ | |||||
TAILQ_FOREACH(cpr, freeprison, pr_list) | |||||
LIST_REMOVE(cpr, pr_sibling); | |||||
} | } | ||||
/* | /* | ||||
* Given the current locking state in the flags, make sure allprison_lock | * Given the current locking state in the flags, make sure allprison_lock | ||||
* is held exclusive, and the prison is locked. Return flags indicating | * is held exclusive, and the prison is locked. Return flags indicating | ||||
* the new state. | * the new state. | ||||
*/ | */ | ||||
static int | static int | ||||
▲ Show 20 Lines • Show All 1,627 Lines • Show Last 20 Lines |