Delay revmoing the last jail reference in prison_proc_free, and instead

put it off into the pr_task.  This is similar to prison_free, and in fact
uses the same task even though they do something slightly different.

This resolves a LOR between the process lock and allprison_lock, which
came about in r298565.

PR:		48471
This commit is contained in:
Jamie Gritton 2016-04-27 02:25:21 +00:00
parent a301044615
commit 73d9e52d2f
2 changed files with 35 additions and 12 deletions

View File

@ -1328,6 +1328,7 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags)
LIST_INIT(&pr->pr_children);
mtx_init(&pr->pr_mtx, "jail mutex", NULL, MTX_DEF | MTX_DUPOK);
TASK_INIT(&pr->pr_task, 0, prison_complete, pr);
#ifdef VIMAGE
/* Allocate a new vnet if specified. */
@ -2575,16 +2576,13 @@ prison_allow(struct ucred *cred, unsigned flag)
void
prison_free_locked(struct prison *pr)
{
int ref;
mtx_assert(&pr->pr_mtx, MA_OWNED);
pr->pr_ref--;
if (pr->pr_ref == 0) {
ref = --pr->pr_ref;
mtx_unlock(&pr->pr_mtx);
TASK_INIT(&pr->pr_task, 0, prison_complete, pr);
if (ref == 0)
taskqueue_enqueue(taskqueue_thread, &pr->pr_task);
return;
}
mtx_unlock(&pr->pr_mtx);
}
void
@ -2595,11 +2593,17 @@ prison_free(struct prison *pr)
prison_free_locked(pr);
}
/*
* Complete a call to either prison_free or prison_proc_free.
*/
static void
prison_complete(void *context, int pending)
{
struct prison *pr = context;
prison_deref((struct prison *)context, 0);
mtx_lock(&pr->pr_mtx);
prison_deref(pr, pr->pr_uref
? PD_DEREF | PD_DEUREF | PD_LOCKED : PD_LOCKED);
}
/*
@ -2618,6 +2622,9 @@ prison_deref(struct prison *pr, int flags)
mtx_lock(&pr->pr_mtx);
for (;;) {
if (flags & PD_DEUREF) {
KASSERT(pr->pr_uref > 0,
("prison_deref PD_DEUREF on a dead prison (jid=%d)",
pr->pr_id));
pr->pr_uref--;
lasturef = pr->pr_uref == 0;
if (lasturef)
@ -2625,8 +2632,12 @@ prison_deref(struct prison *pr, int flags)
KASSERT(prison0.pr_uref != 0, ("prison0 pr_uref=0"));
} else
lasturef = 0;
if (flags & PD_DEREF)
if (flags & PD_DEREF) {
KASSERT(pr->pr_ref > 0,
("prison_deref PD_DEREF on a dead prison (jid=%d)",
pr->pr_id));
pr->pr_ref--;
}
ref = pr->pr_ref;
mtx_unlock(&pr->pr_mtx);
@ -2740,7 +2751,20 @@ prison_proc_free(struct prison *pr)
mtx_lock(&pr->pr_mtx);
KASSERT(pr->pr_uref > 0,
("Trying to kill a process in a dead prison (jid=%d)", pr->pr_id));
prison_deref(pr, PD_DEUREF | PD_LOCKED);
if (pr->pr_uref > 1)
pr->pr_uref--;
else {
/*
* Don't remove the last user reference in this context, which
* is expected to be a process that is not only locked, but
* also half dead.
*/
pr->pr_ref++;
mtx_unlock(&pr->pr_mtx);
taskqueue_enqueue(taskqueue_thread, &pr->pr_task);
return;
}
mtx_unlock(&pr->pr_mtx);
}

View File

@ -149,7 +149,6 @@ struct prison_racct;
* (p) locked by pr_mtx
* (c) set only during creation before the structure is shared, no mutex
* required to read
* (d) set only during destruction of jail, no mutex needed
*/
struct prison {
TAILQ_ENTRY(prison) pr_list; /* (a) all prisons */
@ -161,7 +160,7 @@ struct prison {
LIST_ENTRY(prison) pr_sibling; /* (a) next in parent's list */
struct prison *pr_parent; /* (c) containing jail */
struct mtx pr_mtx;
struct task pr_task; /* (d) destroy task */
struct task pr_task; /* (c) destroy task */
struct osd pr_osd; /* (p) additional data */
struct cpuset *pr_cpuset; /* (p) cpuset */
struct vnet *pr_vnet; /* (c) network stack */