Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F148051818
D27876.id83135.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
9 KB
Referenced Files
None
Subscribers
None
D27876.id83135.diff
View Options
diff --git a/kern/kern_jail.c b/kern/kern_jail.c
--- a/kern/kern_jail.c
+++ b/kern/kern_jail.c
@@ -106,6 +106,7 @@
.pr_path = "/",
.pr_securelevel = -1,
.pr_devfs_rsnum = 0,
+ .pr_state = PRISON_STATE_ALIVE,
.pr_childmax = JAIL_MAX,
.pr_hostuuid = DEFAULT_HOSTUUID,
.pr_children = LIST_HEAD_INITIALIZER(prison0.pr_children),
@@ -1040,7 +1041,8 @@
error = ENOENT;
vfs_opterror(opts, "jail %d not found", jid);
goto done_deref;
- } else if (!prison_isalive(pr)) {
+ }
+ if (!prison_isalive(pr)) {
if (!(flags & JAIL_DYING)) {
error = ENOENT;
vfs_opterror(opts, "jail %d is dying",
@@ -1200,6 +1202,8 @@
/* This brings the parent back to life. */
mtx_lock(&ppr->pr_mtx);
refcount_acquire(&ppr->pr_uref);
+ if (ppr->pr_state == PRISON_STATE_DYING)
+ ppr->pr_state = PRISON_STATE_ALIVE;
mtx_unlock(&ppr->pr_mtx);
error = osd_jail_call(ppr, PR_METHOD_CREATE, opts);
if (error) {
@@ -1218,8 +1222,10 @@
}
pr = malloc(sizeof(*pr), M_PRISON, M_WAITOK | M_ZERO);
- refcount_init(&pr->pr_ref, 0);
+ pr->pr_state = PRISON_STATE_INVALID;
+ refcount_init(&pr->pr_ref, 1);
refcount_init(&pr->pr_uref, 0);
+ drflags |= PD_DEREF;
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);
@@ -1313,11 +1319,6 @@
mtx_lock(&pr->pr_mtx);
drflags |= PD_LOCKED;
- /*
- * New prisons do not yet have a reference, because we do not
- * want others to see the incomplete prison once the
- * allprison_lock is downgraded.
- */
} else {
/*
* Grab a reference for existing prisons, to ensure they
@@ -1739,15 +1740,15 @@
prison_set_allow_locked(pr, tallow, 0);
/*
* Persistent prisons get an extra reference, and prisons losing their
- * persist flag lose that reference. Only do this for existing prisons
- * for now, so new ones will remain unseen until after the module
- * handlers have completed.
+ * persist flag lose that reference.
*/
born = !prison_isalive(pr);
- if (!created && (ch_flags & PR_PERSIST & (pr_flags ^ pr->pr_flags))) {
+ if (ch_flags & PR_PERSIST & (pr_flags ^ pr->pr_flags)) {
if (pr_flags & PR_PERSIST) {
prison_hold(pr);
- refcount_acquire(&pr->pr_uref);
+ if (refcount_acquire(&pr->pr_uref) == 0 &&
+ pr->pr_state == PRISON_STATE_DYING)
+ pr->pr_state = PRISON_STATE_ALIVE;
} else {
drflags |= PD_DEUREF;
prison_free_not_last(pr);
@@ -1817,15 +1818,13 @@
drflags = (drflags & ~PD_LIST_XLOCKED) | PD_LIST_SLOCKED;
if (born) {
error = osd_jail_call(pr, PR_METHOD_CREATE, opts);
- if (error) {
- (void)osd_jail_call(pr, PR_METHOD_REMOVE, NULL);
- goto done_deref;
- }
+ if (error)
+ goto done_remove;
}
error = osd_jail_call(pr, PR_METHOD_SET, opts);
if (error) {
if (born)
- (void)osd_jail_call(pr, PR_METHOD_REMOVE, NULL);
+ goto done_remove;
goto done_deref;
}
@@ -1835,11 +1834,9 @@
error = do_jail_attach(td, pr);
drflags &= ~PD_LIST_SLOCKED;
if (error) {
- if (created) {
- /* do_jail_attach has removed the prison. */
- pr = NULL;
- }
vfs_opterror(opts, "attach failed");
+ if (born)
+ goto done_remove;
goto done_deref;
}
}
@@ -1854,23 +1851,34 @@
}
#endif
+ if (created) {
+ /* The new prison is now ready to be seen. */
+ drflags = prison_lock_xlock(pr, drflags);
+ pr->pr_state = refcount_load(&pr->pr_uref) > 0
+ ? PRISON_STATE_ALIVE : PRISON_STATE_DYING;
+ }
+
td->td_retval[0] = pr->pr_id;
+ goto done_deref;
- if (created) {
- /*
- * Add a reference to newly created persistent prisons
- * (which was not done earlier so that the prison would
- * not be publicly visible).
- */
- if (pr_flags & PR_PERSIST) {
- drflags = prison_lock_xlock(pr, drflags);
- refcount_acquire(&pr->pr_ref);
- refcount_acquire(&pr->pr_uref);
- } else {
- /* Non-persistent jails need no further changes. */
- pr = NULL;
- }
+ done_remove:
+ /*
+ * prison_deref will call the remove methods 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:
/* Release any temporary prison holds and/or locks. */
@@ -2437,8 +2445,10 @@
*/
drflags = prison_lock_xlock(pr, drflags);
}
- refcount_acquire(&pr->pr_ref);
- refcount_acquire(&pr->pr_uref);
+ prison_hold(pr);
+ if (refcount_acquire(&pr->pr_uref) == 0 &&
+ pr->pr_state == PRISON_STATE_DYING)
+ pr->pr_state = PRISON_STATE_ALIVE;
mtx_unlock(&pr->pr_mtx);
drflags &= ~PD_LOCKED;
@@ -2720,6 +2730,12 @@
* prison_free() won't re-submit the task.
*/
prison_hold(pr);
+ mtx_lock(&pr->pr_mtx);
+ KASSERT(!(pr->pr_flags & PR_COMPLETE_PROC),
+ ("Redundant last reference in prison_proc_free (jid=%d)",
+ pr->pr_id));
+ pr->pr_flags |= PR_COMPLETE_PROC;
+ mtx_unlock(&pr->pr_mtx);
taskqueue_enqueue(taskqueue_thread, &pr->pr_task);
}
}
@@ -2740,8 +2756,10 @@
* prison_proc_free adds one.
*/
drflags = prison_lock_xlock(pr, PD_DEREF);
- if (refcount_load(&pr->pr_uref) > 0)
+ if (pr->pr_flags & PR_COMPLETE_PROC) {
+ pr->pr_flags &= ~PR_COMPLETE_PROC;
drflags |= PD_DEUREF;
+ }
prison_deref(pr, drflags);
}
@@ -2768,20 +2786,21 @@
/* This is the last user reference. */
prison_hold(pr);
flags = prison_lock_xlock(pr, flags);
- if (refcount_release(&pr->pr_uref)) {
+ if (refcount_release(&pr->pr_uref) &&
+ pr->pr_state == PRISON_STATE_ALIVE) {
/*
* Tell the modules if the last user
* reference was removed (even if the
* prison sticks around in dying state).
*/
- KASSERT(
- refcount_load(&prison0.pr_uref) > 0,
- ("prison0 pr_uref=0"));
+ pr->pr_state = PRISON_STATE_DYING;
mtx_unlock(&pr->pr_mtx);
flags &= ~PD_LOCKED;
(void)osd_jail_call(pr,
PR_METHOD_REMOVE, NULL);
}
+ KASSERT(refcount_load(&prison0.pr_uref) > 0,
+ ("prison0 pr_uref=0"));
if (!(flags & PD_DEREF))
flags |= PD_DEREF;
else
@@ -2799,19 +2818,11 @@
flags = prison_lock_xlock(pr, flags);
if (!refcount_release(&pr->pr_ref))
break;
- } else {
- /* Check the existing reference count. */
- if (!(flags &
- (PD_LOCKED | PD_LIST_SLOCKED | PD_LIST_XLOCKED))) {
- mtx_lock(&pr->pr_mtx);
- flags |= PD_LOCKED;
- }
- if (refcount_load(&pr->pr_ref) > 0)
- break;
- flags = prison_lock_xlock(pr, flags);
- }
+ } else
+ break;
/* Do the work of actually freeing the prison. */
+ pr->pr_state = PRISON_STATE_INVALID;
TAILQ_REMOVE(&allprison, pr, pr_list);
LIST_REMOVE(pr, pr_sibling);
ppr = pr->pr_parent;
@@ -3060,7 +3071,7 @@
prison_isalive(struct prison *pr)
{
- if (__predict_false(refcount_load(&pr->pr_uref) == 0))
+ if (__predict_false(pr->pr_state != PRISON_STATE_ALIVE))
return (false);
return (true);
}
@@ -3073,7 +3084,7 @@
prison_isvalid(struct prison *pr)
{
- if (__predict_false(refcount_load(&pr->pr_ref) == 0))
+ if (__predict_false(pr->pr_state == PRISON_STATE_INVALID))
return (false);
return (true);
}
@@ -3739,8 +3750,7 @@
bzero(xp, sizeof(*xp));
xp->pr_version = XPRISON_VERSION;
xp->pr_id = cpr->pr_id;
- xp->pr_state = prison_isalive(cpr)
- ? PRISON_STATE_ALIVE : PRISON_STATE_DYING;
+ xp->pr_state = cpr->pr_state;
strlcpy(xp->pr_path, prison_path(pr, cpr), sizeof(xp->pr_path));
strlcpy(xp->pr_host, cpr->pr_hostname, sizeof(xp->pr_host));
strlcpy(xp->pr_name, prison_name(pr, cpr), sizeof(xp->pr_name));
@@ -4391,6 +4401,7 @@
db_printf(" parent = %p\n", pr->pr_parent);
db_printf(" ref = %d\n", pr->pr_ref);
db_printf(" uref = %d\n", pr->pr_uref);
+ db_printf(" state = %d\n", pr->pr_state);
db_printf(" path = %s\n", pr->pr_path);
db_printf(" cpuset = %d\n", pr->pr_cpuset
? pr->pr_cpuset->cs_id : -1);
diff --git a/sys/jail.h b/sys/jail.h
--- a/sys/jail.h
+++ b/sys/jail.h
@@ -88,9 +88,11 @@
};
#define XPRISON_VERSION 3
-#define PRISON_STATE_INVALID 0
-#define PRISON_STATE_ALIVE 1
-#define PRISON_STATE_DYING 2
+enum prison_state {
+ PRISON_STATE_INVALID = 0, /* New prison, not ready to be seen */
+ PRISON_STATE_ALIVE, /* Current prison, visible to all */
+ PRISON_STATE_DYING /* Removed but holding resources, */
+}; /* optionally visible. */
/*
* Flags for jail_set and jail_get.
@@ -155,6 +157,7 @@
* (m) locked by pr_mtx
* (p) locked by pr_mtx, and also at least shared allprison_lock required
* to update
+ * (q) locked by both pr_mtx and allprison_lock
* (r) atomic via refcount(9), pr_mtx and allprison_lock required to
* decrement to zero
*/
@@ -185,7 +188,8 @@
int pr_securelevel; /* (p) securelevel */
int pr_enforce_statfs; /* (p) statfs permission */
int pr_devfs_rsnum; /* (p) devfs ruleset */
- int pr_spare[3];
+ enum prison_state pr_state; /* (q) state in life cycle */
+ int pr_spare[2];
int pr_osreldate; /* (c) kern.osreldate value */
unsigned long pr_hostid; /* (p) jail hostid */
char pr_name[MAXHOSTNAMELEN]; /* (p) admin jail name */
@@ -221,6 +225,8 @@
/* by this jail or an ancestor */
#define PR_IP6 0x04000000 /* IPv6 restricted or disabled */
/* by this jail or an ancestor */
+#define PR_COMPLETE_PROC 0x08000000 /* prison_complete called from */
+ /* prison_proc_free, releases uref */
/*
* Flags for pr_allow
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, Mar 16, 10:45 AM (20 h, 45 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
29767190
Default Alt Text
D27876.id83135.diff (9 KB)
Attached To
Mode
D27876: jail: Add pr_state to struct prison
Attached
Detach File
Event Timeline
Log In to Comment