Changeset View
Changeset View
Standalone View
Standalone View
sys/kern/kern_jail.c
Show First 20 Lines • Show All 2,787 Lines • ▼ Show 20 Lines | |||||
* with no non-sleeping locks held, except perhaps the prison itself. | * with no non-sleeping locks held, except perhaps the prison itself. | ||||
* 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 prison *ppr, *tpr; | struct prisonlist freeprison; | ||||
struct prison *rpr, *tpr; | |||||
int lastref, lasturef; | int lastref, lasturef; | ||||
TAILQ_INIT(&freeprison); | |||||
if (!(flags & PD_LOCKED)) | if (!(flags & PD_LOCKED)) | ||||
mtx_lock(&pr->pr_mtx); | mtx_lock(&pr->pr_mtx); | ||||
/* | |||||
* Release this prison as requested, which may cause its parent | |||||
* to be released, and then maybe its grandparent, etc. | |||||
*/ | |||||
for (;;) { | for (;;) { | ||||
if (flags & PD_DEUREF) { | if (flags & PD_DEUREF) { | ||||
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)); | ||||
lasturef = refcount_release(&pr->pr_uref); | lasturef = refcount_release(&pr->pr_uref); | ||||
if (lasturef) | if (lasturef) | ||||
refcount_acquire(&pr->pr_ref); | refcount_acquire(&pr->pr_ref); | ||||
Show All 26 Lines | if (lasturef) { | ||||
} | } | ||||
} | } | ||||
(void)osd_jail_call(pr, PR_METHOD_REMOVE, NULL); | (void)osd_jail_call(pr, PR_METHOD_REMOVE, NULL); | ||||
mtx_lock(&pr->pr_mtx); | mtx_lock(&pr->pr_mtx); | ||||
lastref = refcount_release(&pr->pr_ref); | lastref = refcount_release(&pr->pr_ref); | ||||
mtx_unlock(&pr->pr_mtx); | mtx_unlock(&pr->pr_mtx); | ||||
} | } | ||||
/* If the prison still has references, nothing else to do. */ | if (!lastref) | ||||
if (!lastref) { | break; | ||||
if (flags & PD_LIST_SLOCKED) | |||||
sx_sunlock(&allprison_lock); | |||||
else if (flags & PD_LIST_XLOCKED) | |||||
sx_xunlock(&allprison_lock); | |||||
return; | |||||
} | |||||
if (flags & PD_LIST_SLOCKED) { | if (flags & PD_LIST_SLOCKED) { | ||||
if (!sx_try_upgrade(&allprison_lock)) { | if (!sx_try_upgrade(&allprison_lock)) { | ||||
sx_sunlock(&allprison_lock); | sx_sunlock(&allprison_lock); | ||||
sx_xlock(&allprison_lock); | sx_xlock(&allprison_lock); | ||||
} | } | ||||
flags &= ~PD_LIST_SLOCKED; | |||||
} else if (!(flags & PD_LIST_XLOCKED)) | } else if (!(flags & PD_LIST_XLOCKED)) | ||||
sx_xlock(&allprison_lock); | sx_xlock(&allprison_lock); | ||||
flags |= PD_LIST_XLOCKED; | |||||
TAILQ_REMOVE(&allprison, pr, pr_list); | TAILQ_REMOVE(&allprison, pr, pr_list); | ||||
LIST_REMOVE(pr, pr_sibling); | LIST_REMOVE(pr, pr_sibling); | ||||
ppr = pr->pr_parent; | TAILQ_INSERT_TAIL(&freeprison, pr, pr_list); | ||||
for (tpr = ppr; tpr != NULL; tpr = tpr->pr_parent) | for (tpr = pr->pr_parent; tpr != NULL; tpr = tpr->pr_parent) | ||||
tpr->pr_childcount--; | tpr->pr_childcount--; | ||||
/* Removing a prison frees a reference on its parent. */ | |||||
pr = pr->pr_parent; | |||||
mtx_lock(&pr->pr_mtx); | |||||
flags |= PD_DEREF | PD_DEUREF; | |||||
} | |||||
/* Release all the prison locks. */ | |||||
if (flags & PD_LIST_SLOCKED) | |||||
sx_sunlock(&allprison_lock); | |||||
else if (flags & PD_LIST_XLOCKED) | |||||
sx_xunlock(&allprison_lock); | sx_xunlock(&allprison_lock); | ||||
/* | |||||
* Finish removing any unreferenced prisons, which couldn't happen | |||||
* while allprison_lock was held (to avoid a LOR on vrele). | |||||
*/ | |||||
TAILQ_FOREACH_SAFE(rpr, &freeprison, pr_list, tpr) { | |||||
#ifdef VIMAGE | #ifdef VIMAGE | ||||
if (pr->pr_vnet != ppr->pr_vnet) | if (rpr->pr_vnet != rpr->pr_parent->pr_vnet) | ||||
vnet_destroy(pr->pr_vnet); | vnet_destroy(rpr->pr_vnet); | ||||
#endif | #endif | ||||
if (pr->pr_root != NULL) | if (rpr->pr_root != NULL) | ||||
vrele(pr->pr_root); | vrele(rpr->pr_root); | ||||
mtx_destroy(&pr->pr_mtx); | mtx_destroy(&rpr->pr_mtx); | ||||
#ifdef INET | #ifdef INET | ||||
free(pr->pr_ip4, M_PRISON); | free(rpr->pr_ip4, M_PRISON); | ||||
#endif | #endif | ||||
#ifdef INET6 | #ifdef INET6 | ||||
free(pr->pr_ip6, M_PRISON); | free(rpr->pr_ip6, M_PRISON); | ||||
#endif | #endif | ||||
if (pr->pr_cpuset != NULL) | if (rpr->pr_cpuset != NULL) | ||||
cpuset_rel(pr->pr_cpuset); | cpuset_rel(rpr->pr_cpuset); | ||||
osd_jail_exit(pr); | osd_jail_exit(rpr); | ||||
#ifdef RACCT | #ifdef RACCT | ||||
if (racct_enable) | if (racct_enable) | ||||
prison_racct_detach(pr); | prison_racct_detach(rpr); | ||||
#endif | #endif | ||||
free(pr, M_PRISON); | free(rpr, M_PRISON); | ||||
/* Removing a prison frees a reference on its parent. */ | |||||
pr = ppr; | |||||
mtx_lock(&pr->pr_mtx); | |||||
flags = PD_DEREF | PD_DEUREF; | |||||
} | } | ||||
} | } | ||||
/* | /* | ||||
* Set or clear a permission bit in the pr_allow field, passing restrictions | * Set or clear a permission bit in the pr_allow field, passing restrictions | ||||
* (cleared permission) down to child jails. | * (cleared permission) down to child jails. | ||||
*/ | */ | ||||
void | void | ||||
▲ Show 20 Lines • Show All 1,597 Lines • Show Last 20 Lines |