return(p);
}
-#if PROC_REF_DEBUG
void
uthread_reset_proc_refcount(void *uthread) {
uthread_t uth;
+ uth = (uthread_t) uthread;
+ uth->uu_proc_refcount = 0;
+
+#if PROC_REF_DEBUG
if (proc_ref_tracking_disabled) {
return;
}
- uth = (uthread_t) uthread;
-
- uth->uu_proc_refcount = 0;
uth->uu_pindex = 0;
+#endif
}
+#if PROC_REF_DEBUG
int
uthread_get_proc_refcount(void *uthread) {
uthread_t uth;
return uth->uu_proc_refcount;
}
+#endif
static void
-record_procref(proc_t p, int count) {
+record_procref(proc_t p __unused, int count) {
uthread_t uth;
+ uth = current_uthread();
+ uth->uu_proc_refcount += count;
+
+#if PROC_REF_DEBUG
if (proc_ref_tracking_disabled) {
return;
}
- uth = current_uthread();
- uth->uu_proc_refcount += count;
-
if (count == 1) {
if (uth->uu_pindex < NUM_PROC_REFS_TO_TRACK) {
backtrace((uintptr_t *) &uth->uu_proc_pcs[uth->uu_pindex], PROC_REF_STACK_DEPTH);
uth->uu_pindex++;
}
}
-}
#endif
+}
+
+static boolean_t
+uthread_needs_to_wait_in_proc_refwait(void) {
+ uthread_t uth = current_uthread();
+
+ /*
+ * Allow threads holding no proc refs to wait
+ * in proc_refwait, allowing threads holding
+ * proc refs to wait in proc_refwait causes
+ * deadlocks and makes proc_find non-reentrant.
+ */
+ if (uth->uu_proc_refcount == 0)
+ return TRUE;
+
+ return FALSE;
+}
int
proc_rele(proc_t p)
/* if process still in creation return failure */
if ((p == PROC_NULL) || ((p->p_listflag & P_LIST_INCREATE) != 0))
return (PROC_NULL);
- /* do not return process marked for termination */
- if ((p->p_stat != SZOMB) && ((p->p_listflag & P_LIST_EXITED) == 0) && ((p->p_listflag & (P_LIST_DRAINWAIT | P_LIST_DRAIN | P_LIST_DEAD)) == 0)) {
+retry:
+ /*
+ * Do not return process marked for termination
+ * or proc_refdrain called without ref wait.
+ * Wait for proc_refdrain_with_refwait to complete if
+ * process in refdrain and refwait flag is set, unless
+ * the current thread is holding to a proc_ref
+ * for any proc.
+ */
+ if ((p->p_stat != SZOMB) &&
+ ((p->p_listflag & P_LIST_EXITED) == 0) &&
+ ((p->p_listflag & P_LIST_DEAD) == 0) &&
+ (((p->p_listflag & (P_LIST_DRAIN | P_LIST_DRAINWAIT)) == 0) ||
+ ((p->p_listflag & P_LIST_REFWAIT) != 0))) {
+ if ((p->p_listflag & P_LIST_REFWAIT) != 0 && uthread_needs_to_wait_in_proc_refwait()) {
+ msleep(&p->p_listflag, proc_list_mlock, 0, "proc_refwait", 0) ;
+ goto retry;
+ }
p->p_refcount++;
-#if PROC_REF_DEBUG
record_procref(p, 1);
-#endif
}
else
p1 = PROC_NULL;
if (p->p_refcount > 0) {
p->p_refcount--;
-#if PROC_REF_DEBUG
record_procref(p, -1);
-#endif
if ((p->p_refcount == 0) && ((p->p_listflag & P_LIST_DRAINWAIT) == P_LIST_DRAINWAIT)) {
p->p_listflag &= ~P_LIST_DRAINWAIT;
wakeup(&p->p_refcount);
void
proc_refdrain(proc_t p)
{
+ proc_refdrain_with_refwait(p, FALSE);
+}
+proc_t
+proc_refdrain_with_refwait(proc_t p, boolean_t get_ref_and_allow_wait)
+{
+ boolean_t initexec = FALSE;
proc_list_lock();
p->p_listflag |= P_LIST_DRAIN;
- while (p->p_refcount) {
+ if (get_ref_and_allow_wait) {
+ /*
+ * All the calls to proc_ref_locked will wait
+ * for the flag to get cleared before returning a ref,
+ * unless the current thread is holding to a proc ref
+ * for any proc.
+ */
+ p->p_listflag |= P_LIST_REFWAIT;
+ if (p == initproc) {
+ initexec = TRUE;
+ }
+ }
+
+ /* Do not wait in ref drain for launchd exec */
+ while (p->p_refcount && !initexec) {
p->p_listflag |= P_LIST_DRAINWAIT;
msleep(&p->p_refcount, proc_list_mlock, 0, "proc_refdrain", 0) ;
}
+
p->p_listflag &= ~P_LIST_DRAIN;
- p->p_listflag |= P_LIST_DEAD;
+ if (!get_ref_and_allow_wait) {
+ p->p_listflag |= P_LIST_DEAD;
+ } else {
+ /* Return a ref to the caller */
+ p->p_refcount++;
+ record_procref(p, 1);
+ }
proc_list_unlock();
+ if (get_ref_and_allow_wait) {
+ return (p);
+ }
+ return NULL;
+}
+void
+proc_refwake(proc_t p)
+{
+ proc_list_lock();
+ p->p_listflag &= ~P_LIST_REFWAIT;
+ wakeup(&p->p_listflag);
+ proc_list_unlock();
}
proc_t
struct timeval last_no_space_action = {0, 0};
+#if DEVELOPMENT || DEBUG
+extern boolean_t kill_on_no_paging_space;
+#endif /* DEVELOPMENT || DEBUG */
+
+#define MB_SIZE (1024 * 1024ULL)
+
int
no_paging_space_action()
{
*/
last_no_space_action = now;
- printf("low swap: killing pid %d (%s)\n", p->p_pid, p->p_comm);
+ printf("low swap: killing largest compressed process with pid %d (%s) and size %llu MB\n", p->p_pid, p->p_comm, (nps.pcs_max_size/MB_SIZE));
psignal(p, SIGKILL);
proc_rele(p);
*/
last_no_space_action = now;
+#if DEVELOPMENT || DEBUG
+ if (kill_on_no_paging_space == TRUE) {
+ /*
+ * We found the largest process that has a process policy i.e. one of
+ * PC_KILL, PC_SUSP, PC_THROTTLE.
+ * But we are in a mode where we will kill it regardless of its policy.
+ */
+ printf("low swap: killing largest process with pid %d (%s) and size %llu MB\n", p->p_pid, p->p_comm, (nps.pcs_max_size/MB_SIZE));
+ psignal(p, SIGKILL);
+
+ proc_rele(p);
+
+ return 1;
+ }
+#endif /* DEVELOPMENT || DEBUG */
+
proc_dopcontrol(p);
proc_rele(p);