+void
+proc_setregister(proc_t p)
+{
+ proc_lock(p);
+ p->p_lflag |= P_LREGISTER;
+ proc_unlock(p);
+}
+
+void
+proc_resetregister(proc_t p)
+{
+ proc_lock(p);
+ p->p_lflag &= ~P_LREGISTER;
+ proc_unlock(p);
+}
+
+pid_t
+proc_pgrpid(proc_t p)
+{
+ return p->p_pgrpid;
+}
+
+pid_t
+proc_selfpgrpid()
+{
+ return current_proc()->p_pgrpid;
+}
+
+
+/* return control and action states */
+int
+proc_getpcontrol(int pid, int * pcontrolp)
+{
+ proc_t p;
+
+ p = proc_find(pid);
+ if (p == PROC_NULL)
+ return(ESRCH);
+ if (pcontrolp != NULL)
+ *pcontrolp = p->p_pcaction;
+
+ proc_rele(p);
+ return(0);
+}
+
+int
+proc_dopcontrol(proc_t p, void *num_found)
+{
+ int pcontrol;
+
+ proc_lock(p);
+
+ pcontrol = PROC_CONTROL_STATE(p);
+
+ if (PROC_ACTION_STATE(p) ==0) {
+ switch(pcontrol) {
+ case P_PCTHROTTLE:
+ PROC_SETACTION_STATE(p);
+ proc_unlock(p);
+ printf("low swap: throttling pid %d (%s)\n", p->p_pid, p->p_comm);
+ (*(int *)num_found)++;
+ break;
+
+ case P_PCSUSP:
+ PROC_SETACTION_STATE(p);
+ proc_unlock(p);
+ printf("low swap: suspending pid %d (%s)\n", p->p_pid, p->p_comm);
+ task_suspend(p->task);
+ (*(int *)num_found)++;
+ break;
+
+ case P_PCKILL:
+ PROC_SETACTION_STATE(p);
+ proc_unlock(p);
+ printf("low swap: killing pid %d (%s)\n", p->p_pid, p->p_comm);
+ psignal(p, SIGKILL);
+ (*(int *)num_found)++;
+ break;
+
+ default:
+ proc_unlock(p);
+ }
+
+ } else
+ proc_unlock(p);
+
+ return(PROC_RETURNED);
+}
+
+
+/*
+ * Resume a throttled or suspended process. This is an internal interface that's only
+ * used by the user level code that presents the GUI when we run out of swap space and
+ * hence is restricted to processes with superuser privileges.
+ */
+
+int
+proc_resetpcontrol(int pid)
+{
+ proc_t p;
+ int pcontrol;
+ int error;
+ proc_t self = current_proc();
+
+ /* if the process has been validated to handle resource control or root is valid one */
+ if (((self->p_lflag & P_LVMRSRCOWNER) == 0) && (error = suser(kauth_cred_get(), 0)))
+ return error;
+
+ p = proc_find(pid);
+ if (p == PROC_NULL)
+ return(ESRCH);
+
+ proc_lock(p);
+
+ pcontrol = PROC_CONTROL_STATE(p);
+
+ if(PROC_ACTION_STATE(p) !=0) {
+ switch(pcontrol) {
+ case P_PCTHROTTLE:
+ PROC_RESETACTION_STATE(p);
+ proc_unlock(p);
+ printf("low swap: unthrottling pid %d (%s)\n", p->p_pid, p->p_comm);
+ break;
+
+ case P_PCSUSP:
+ PROC_RESETACTION_STATE(p);
+ proc_unlock(p);
+ printf("low swap: resuming pid %d (%s)\n", p->p_pid, p->p_comm);
+ task_resume(p->task);
+ break;
+
+ case P_PCKILL:
+ /* Huh? */
+ PROC_SETACTION_STATE(p);
+ proc_unlock(p);
+ printf("low swap: attempt to unkill pid %d (%s) ignored\n", p->p_pid, p->p_comm);
+ break;
+
+ default:
+ proc_unlock(p);
+ }
+
+ } else
+ proc_unlock(p);
+
+ proc_rele(p);
+ return(0);
+}
+
+
+/*
+ * Return true if the specified process has an action state specified for it and it isn't
+ * already in an action state and it's using more physical memory than the specified threshold.
+ * Note: the memory_threshold argument is specified in bytes and is of type uint64_t.
+ */
+
+static int
+proc_pcontrol_filter(proc_t p, void *memory_thresholdp)
+{
+
+ return PROC_CONTROL_STATE(p) && /* if there's an action state specified... */
+ (PROC_ACTION_STATE(p) == 0) && /* and we're not in the action state yet... */
+ (get_task_resident_size(p->task) > *((uint64_t *)memory_thresholdp)); /* and this proc is over the mem threshold, */
+ /* then return true to take action on this proc */
+}
+
+
+
+/*
+ * Deal with the out of swap space condition. This routine gets called when
+ * we want to swap something out but there's no more space left. Since this
+ * creates a memory deadlock situtation, we need to take action to free up
+ * some memory resources in order to prevent the system from hanging completely.
+ * The action we take is based on what the system processes running at user level
+ * have specified. Processes are marked in one of four categories: ones that
+ * can be killed immediately, ones that should be suspended, ones that should
+ * be throttled, and all the rest which are basically none of the above. Which
+ * processes are marked as being in which category is a user level policy decision;
+ * we just take action based on those decisions here.
+ */
+
+#define STARTING_PERCENTAGE 50 /* memory threshold expressed as a percentage */
+ /* of physical memory */
+
+struct timeval last_no_space_action = {0, 0};
+
+void
+no_paging_space_action(void)
+{
+
+ uint64_t memory_threshold;
+ int num_found;
+ struct timeval now;
+
+ /*
+ * Throttle how often we come through here. Once every 20 seconds should be plenty.
+ */
+
+ microtime(&now);
+
+ if (now.tv_sec <= last_no_space_action.tv_sec + 20)
+ return;
+
+ last_no_space_action = now;
+
+ /*
+ * Examine all processes and find those that have been marked to have some action
+ * taken when swap space runs out. Of those processes, select one or more and
+ * apply the specified action to them. The idea is to only take action against
+ * a few processes rather than hitting too many at once. If the low swap condition
+ * persists, this routine will get called again and we'll take action against more
+ * processes.
+ *
+ * Of the processes that have been marked, we choose which ones to take action
+ * against according to how much physical memory they're presently using. We
+ * start with the STARTING_THRESHOLD and any processes using more physical memory
+ * than the percentage threshold will have action taken against it. If there
+ * are no processes over the threshold, then the threshold is cut in half and we
+ * look again for processes using more than this threshold. We continue in
+ * this fashion until we find at least one process to take action against. This
+ * iterative approach is less than ideally efficient, however we only get here
+ * when the system is almost in a memory deadlock and is pretty much just
+ * thrashing if it's doing anything at all. Therefore, the cpu overhead of
+ * potentially multiple passes here probably isn't revelant.
+ */
+
+ memory_threshold = (sane_size * STARTING_PERCENTAGE) / 100; /* resident threshold in bytes */
+
+ for (num_found = 0; num_found == 0; memory_threshold = memory_threshold / 2) {
+ proc_iterate(PROC_ALLPROCLIST, proc_dopcontrol, (void *)&num_found, proc_pcontrol_filter, (void *)&memory_threshold);
+
+ /*
+ * If we just looked with memory_threshold == 0, then there's no need to iterate any further since
+ * we won't find any eligible processes at this point.
+ */
+
+ if (memory_threshold == 0) {
+ if (num_found == 0) /* log that we couldn't do anything in this case */
+ printf("low swap: unable to find any eligible processes to take action on\n");
+
+ break;
+ }
+ }
+}