Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 4 Aug 2018 20:45:44 +0000 (UTC)
From:      Konstantin Belousov <kib@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r337330 - in head/sys: kern sys vm
Message-ID:  <201808042045.w74KjiP7016982@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: kib
Date: Sat Aug  4 20:45:43 2018
New Revision: 337330
URL: https://svnweb.freebsd.org/changeset/base/337330

Log:
  Swap in WKILLED processes.
  
  Swapped-out process that is WKILLED must be swapped in as soon as
  possible.  The reason is that such process can be killed by OOM and
  its pages can be only freed if the process exits.  To exit, the kernel
  stack of the process must be mapped.
  
  When allocating pages for the stack of the WKILLED process on swap in,
  use VM_ALLOC_SYSTEM requests to increase the chance of the allocation
  to succeed.
  
  Add counter of the swapped out processes to avoid unneeded iteration
  over the allprocs list when there is no work to do, reducing the
  allproc_lock ownership.
  
  Reviewed by:	alc, markj (previous version)
  Tested by:	pho
  Sponsored by:	The FreeBSD Foundation
  MFC after:	2 weeks
  Differential revision:	https://reviews.freebsd.org/D16489

Modified:
  head/sys/kern/kern_sig.c
  head/sys/kern/sys_process.c
  head/sys/sys/proc.h
  head/sys/vm/vm_swapout.c

Modified: head/sys/kern/kern_sig.c
==============================================================================
--- head/sys/kern/kern_sig.c	Sat Aug  4 20:29:58 2018	(r337329)
+++ head/sys/kern/kern_sig.c	Sat Aug  4 20:45:43 2018	(r337330)
@@ -3064,6 +3064,23 @@ postsig(int sig)
 	return (1);
 }
 
+void
+proc_wkilled(struct proc *p)
+{
+
+	PROC_LOCK_ASSERT(p, MA_OWNED);
+	if ((p->p_flag & P_WKILLED) == 0) {
+		p->p_flag |= P_WKILLED;
+		/*
+		 * Notify swapper that there is a process to swap in.
+		 * The notification is racy, at worst it would take 10
+		 * seconds for the swapper process to notice.
+		 */
+		if ((p->p_flag & (P_INMEM | P_SWAPPINGIN)) == 0)
+			wakeup(&proc0);
+	}
+}
+
 /*
  * Kill the current process for stated reason.
  */
@@ -3076,7 +3093,7 @@ killproc(struct proc *p, char *why)
 	    p->p_comm);
 	log(LOG_ERR, "pid %d (%s), uid %d, was killed: %s\n", p->p_pid,
 	    p->p_comm, p->p_ucred ? p->p_ucred->cr_uid : -1, why);
-	p->p_flag |= P_WKILLED;
+	proc_wkilled(p);
 	kern_psignal(p, SIGKILL);
 }
 

Modified: head/sys/kern/sys_process.c
==============================================================================
--- head/sys/kern/sys_process.c	Sat Aug  4 20:29:58 2018	(r337329)
+++ head/sys/kern/sys_process.c	Sat Aug  4 20:45:43 2018	(r337330)
@@ -1170,7 +1170,7 @@ kern_ptrace(struct thread *td, int req, pid_t pid, voi
 		 * queue cannot accommodate any new signals.
 		 */
 		if (data == SIGKILL)
-			p->p_flag |= P_WKILLED;
+			proc_wkilled(p);
 
 		/*
 		 * Unsuspend all threads.  To leave a thread

Modified: head/sys/sys/proc.h
==============================================================================
--- head/sys/sys/proc.h	Sat Aug  4 20:29:58 2018	(r337329)
+++ head/sys/sys/proc.h	Sat Aug  4 20:45:43 2018	(r337330)
@@ -1050,6 +1050,7 @@ struct proc *proc_realparent(struct proc *child);
 void	proc_reap(struct thread *td, struct proc *p, int *status, int options);
 void	proc_reparent(struct proc *child, struct proc *newparent);
 void	proc_set_traced(struct proc *p, bool stop);
+void	proc_wkilled(struct proc *p);
 struct	pstats *pstats_alloc(void);
 void	pstats_fork(struct pstats *src, struct pstats *dst);
 void	pstats_free(struct pstats *ps);

Modified: head/sys/vm/vm_swapout.c
==============================================================================
--- head/sys/vm/vm_swapout.c	Sat Aug  4 20:29:58 2018	(r337329)
+++ head/sys/vm/vm_swapout.c	Sat Aug  4 20:45:43 2018	(r337330)
@@ -158,13 +158,14 @@ static struct mtx vm_daemon_mtx;
 /* Allow for use by vm_pageout before vm_daemon is initialized. */
 MTX_SYSINIT(vm_daemon, &vm_daemon_mtx, "vm daemon", MTX_DEF);
 
+static int swapped_cnt;
+
 static void swapclear(struct proc *);
 static int swapout(struct proc *);
 static void vm_swapout_map_deactivate_pages(vm_map_t, long);
 static void vm_swapout_object_deactivate_pages(pmap_t, vm_object_t, long);
 static void swapout_procs(int action);
 static void vm_req_vmdaemon(int req);
-static void vm_thread_swapin(struct thread *td);
 static void vm_thread_swapout(struct thread *td);
 
 /*
@@ -563,7 +564,7 @@ vm_thread_swapout(struct thread *td)
  * Bring the kernel stack for a specified thread back in.
  */
 static void
-vm_thread_swapin(struct thread *td)
+vm_thread_swapin(struct thread *td, int oom_alloc)
 {
 	vm_object_t ksobj;
 	vm_page_t ma[KSTACK_MAX_PAGES];
@@ -572,7 +573,7 @@ vm_thread_swapin(struct thread *td)
 	pages = td->td_kstack_pages;
 	ksobj = td->td_kstack_obj;
 	VM_OBJECT_WLOCK(ksobj);
-	(void)vm_page_grab_pages(ksobj, 0, VM_ALLOC_NORMAL | VM_ALLOC_WIRED, ma,
+	(void)vm_page_grab_pages(ksobj, 0, oom_alloc | VM_ALLOC_WIRED, ma,
 	    pages);
 	for (i = 0; i < pages;) {
 		vm_page_assert_xbusied(ma[i]);
@@ -605,8 +606,10 @@ void
 faultin(struct proc *p)
 {
 	struct thread *td;
+	int oom_alloc;
 
 	PROC_LOCK_ASSERT(p, MA_OWNED);
+
 	/*
 	 * If another process is swapping in this process,
 	 * just wait until it finishes.
@@ -616,7 +619,11 @@ faultin(struct proc *p)
 			msleep(&p->p_flag, &p->p_mtx, PVM, "faultin", 0);
 		return;
 	}
+
 	if ((p->p_flag & P_INMEM) == 0) {
+		oom_alloc = (p->p_flag & P_WKILLED) != 0 ? VM_ALLOC_SYSTEM :
+		    VM_ALLOC_NORMAL;
+
 		/*
 		 * Don't let another thread swap process p out while we are
 		 * busy swapping it in.
@@ -624,6 +631,10 @@ faultin(struct proc *p)
 		++p->p_lock;
 		p->p_flag |= P_SWAPPINGIN;
 		PROC_UNLOCK(p);
+		sx_xlock(&allproc_lock);
+		MPASS(swapped_cnt > 0);
+		swapped_cnt--;
+		sx_xunlock(&allproc_lock);
 
 		/*
 		 * We hold no lock here because the list of threads
@@ -631,14 +642,14 @@ faultin(struct proc *p)
 		 * swapped out.
 		 */
 		FOREACH_THREAD_IN_PROC(p, td)
-			vm_thread_swapin(td);
+			vm_thread_swapin(td, oom_alloc);
+
 		PROC_LOCK(p);
 		swapclear(p);
 		p->p_swtick = ticks;
 
-		wakeup(&p->p_flag);
-
 		/* Allow other threads to swap p out now. */
+		wakeup(&p->p_flag);
 		--p->p_lock;
 	}
 }
@@ -648,29 +659,41 @@ faultin(struct proc *p)
  * is enough space for them.  Of course, if a process waits for a long
  * time, it will be swapped in anyway.
  */
-void
-swapper(void)
+
+static struct proc *
+swapper_selector(void)
 {
-	struct proc *p, *pp;
+	struct proc *p, *res;
 	struct thread *td;
-	int ppri, pri, slptime, swtime;
+	int min_flag, ppri, pri, slptime, swtime;
 
-loop:
-	if (vm_page_count_min()) {
-		vm_wait_min();
-		goto loop;
-	}
-
-	pp = NULL;
+	sx_assert(&allproc_lock, SA_SLOCKED);
+	if (swapped_cnt == 0)
+		return (NULL);
+	res = NULL;
 	ppri = INT_MIN;
-	sx_slock(&allproc_lock);
+	min_flag = vm_page_count_min();
 	FOREACH_PROC_IN_SYSTEM(p) {
 		PROC_LOCK(p);
-		if (p->p_state == PRS_NEW ||
-		    p->p_flag & (P_SWAPPINGOUT | P_SWAPPINGIN | P_INMEM)) {
+		if (p->p_state == PRS_NEW || (p->p_flag & (P_SWAPPINGOUT |
+		    P_SWAPPINGIN | P_INMEM)) != 0) {
 			PROC_UNLOCK(p);
 			continue;
 		}
+		if (p->p_state == PRS_NORMAL && (p->p_flag & P_WKILLED) != 0) {
+			/*
+			 * A swapped-out process might have mapped a
+			 * large portion of the system's pages as
+			 * anonymous memory.  There is no other way to
+			 * release the memory other than to kill the
+			 * process, for which we need to swap it in.
+			 */
+			return (p);
+		}
+		if (min_flag) {
+			PROC_UNLOCK(p);
+			continue;
+		}
 		swtime = (ticks - p->p_swtick) / hz;
 		FOREACH_THREAD_IN_PROC(p, td) {
 			/*
@@ -690,7 +713,7 @@ loop:
 				 * selection.
 				 */
 				if (pri > ppri) {
-					pp = p;
+					res = p;
 					ppri = pri;
 				}
 			}
@@ -698,33 +721,40 @@ loop:
 		}
 		PROC_UNLOCK(p);
 	}
-	sx_sunlock(&allproc_lock);
+	if (res != NULL)
+		PROC_LOCK(res);
+	return (res);
+}
 
-	/*
-	 * Nothing to do, back to sleep.
-	 */
-	if ((p = pp) == NULL) {
-		tsleep(&proc0, PVM, "swapin", MAXSLP * hz / 2);
-		goto loop;
-	}
-	PROC_LOCK(p);
+void
+swapper(void)
+{
+	struct proc *p;
 
-	/*
-	 * Another process may be bringing or may have already
-	 * brought this process in while we traverse all threads.
-	 * Or, this process may even be being swapped out again.
-	 */
-	if (p->p_flag & (P_INMEM | P_SWAPPINGOUT | P_SWAPPINGIN)) {
-		PROC_UNLOCK(p);
-		goto loop;
-	}
+	for (;;) {
+		sx_slock(&allproc_lock);
+		p = swapper_selector();
+		sx_sunlock(&allproc_lock);
 
-	/*
-	 * We would like to bring someone in.
-	 */
-	faultin(p);
-	PROC_UNLOCK(p);
-	goto loop;
+		if (p == NULL) {
+			tsleep(&proc0, PVM, "swapin", MAXSLP * hz / 2);
+		} else {
+			PROC_LOCK_ASSERT(p, MA_OWNED);
+
+			/*
+			 * Another process may be bringing or may have
+			 * already brought this process in while we
+			 * traverse all threads.  Or, this process may
+			 * have exited or even being swapped out
+			 * again.
+			 */
+			if (p->p_state == PRS_NORMAL && (p->p_flag & (P_INMEM |
+			    P_SWAPPINGOUT | P_SWAPPINGIN)) == 0) {
+				faultin(p);
+			}
+			PROC_UNLOCK(p);
+		}
+	}
 }
 
 /*
@@ -803,7 +833,12 @@ swapout_procs(int action)
 			didswap = true;
 
 		PROC_UNLOCK(p);
-		sx_slock(&allproc_lock);
+		if (didswap) {
+			sx_xlock(&allproc_lock);
+			swapped_cnt++;
+			sx_downgrade(&allproc_lock);
+		} else
+			sx_slock(&allproc_lock);
 		PRELE(p);
 	}
 	sx_sunlock(&allproc_lock);



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201808042045.w74KjiP7016982>