git: 9front

Download patch

ref: 6a3d7f26bf284bade672022fada6c6f831e0a8bc
parent: f153b4cc9850af2b01878ee2e0146b6d61fb316b
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Sun May 4 14:42:41 EDT 2025

kernel: Improve killbig() function

The mfreeseg() call in killbig() was wrong, as
mfreeseg() assumes to be called from a process
sharing the segment and relies on Segment.ref
to optimize the cross-processor TLB flushing.

One could try to fix that by incref()'ing the
segment before the mfreeseg() call, but we can
also just rely on the processes exiting itself
after getting killed.

Putting a comment above mfreeseg() to avoid this
mistake in the future.

For killing a process, refactor the code
from devproc.c into a killproc() function
in proc.c which handles cases for "Stopped" and
"Broken" processes on one place now.

As killbig() is called again (pages are not
freed immediately), we return when our victim
has: kp->procctl == Proc_exitbig. The caller
calls sched() in this case, giving these processes
a chance to exit themslfs.

--- a/sys/src/9/port/devproc.c
+++ b/sys/src/9/port/devproc.c
@@ -1445,12 +1445,6 @@
 static void
 procctlreq(Proc *p, char *va, int n)
 {
-	static Note killnote = {
-		"sys: killed",
-		NExit,
-		1,
-	};
-
 	Segment *s;
 	uintptr npc;
 	int pri;
@@ -1479,21 +1473,7 @@
 		p->hang = 1;
 		break;
 	case CMkill:
-		switch(p->state) {
-		case Broken:
-			unbreak(p);
-			break;
-		case Stopped:
-			p->procctl = Proc_exitme;
-			incref(&killnote);
-			pushnote(p, &killnote);
-			ready(p);
-			break;
-		default:
-			p->procctl = Proc_exitme;
-			incref(&killnote);
-			pushnote(p, &killnote);
-		}
+		killproc(p, Proc_exitme);
 		break;
 	case CMnohang:
 		p->hang = 0;
--- a/sys/src/9/port/devswap.c
+++ b/sys/src/9/port/devswap.c
@@ -176,7 +176,7 @@
 		if(swapimage.c == nil || swapalloc.free == 0){
 		Killbig:
 			if(!freebroken())
-				killbig("out of memory");
+				killbig();
 			sched();
 			continue;
 		}
--- a/sys/src/9/port/portfns.h
+++ b/sys/src/9/port/portfns.h
@@ -162,7 +162,8 @@
 int		kenter(Ureg*);
 void		kexit(Ureg*);
 void		kickpager(void);
-void		killbig(char*);
+void		killbig(void);
+void		killproc(Proc*,int);
 void		kproc(char*, void(*)(void*), void*);
 void		kprocchild(Proc*, void (*)(void));
 void		(*kproftimer)(uintptr);
--- a/sys/src/9/port/proc.c
+++ b/sys/src/9/port/proc.c
@@ -1744,7 +1744,7 @@
 }
 
 void
-killbig(char *why)
+killbig(void)
 {
 	int i;
 	Segment *s;
@@ -1766,31 +1766,52 @@
 	}
 	if(kp == nil)
 		return;
-	print("%lud: %s killed: %s\n", kp->pid, kp->text, why);
-	qlock(&kp->seglock);
-	for(i = 0; (p = proctab(i)) != nil; i++) {
-		if(p->state <= New || p->kp)
-			continue;
-		if(p != kp && p->seg[BSEG] != nil && p->seg[BSEG] == kp->seg[BSEG])
-			p->procctl = Proc_exitbig;
+	qlock(&kp->debug);
+	if(kp->pid == 0 || kp->procctl == Proc_exitbig){
+		qunlock(&kp->debug);
+		return;
 	}
-	kp->procctl = Proc_exitbig;
-	for(i = 0; i < NSEG; i++) {
-		s = kp->seg[i];
-		if(s == nil)
-			continue;
-		switch(s->type & SG_TYPE){
-		case SG_SHARED:
-		case SG_PHYSICAL:
-		case SG_FIXED:
-		case SG_STICKY:
-			continue;
+	qlock(&kp->seglock);
+	s = kp->seg[BSEG];
+	if(s != nil && s->ref > 1){
+		for(i = 0; (p = proctab(i)) != nil; i++) {
+			if(p == kp || p->state <= New || p->kp)
+				continue;
+			qlock(&p->debug);
+			if(p->pid != 0 && matchseg(p, s))
+				killproc(p, Proc_exitbig);
+			qunlock(&p->debug);
 		}
-		qlock(s);
-		mfreeseg(s, s->base, (s->top - s->base)/BY2PG);
-		qunlock(s);
 	}
 	qunlock(&kp->seglock);
+	killproc(kp, Proc_exitbig);
+	qunlock(&kp->debug);
+}
+
+/*
+ *  called with p->debug locked.
+ */
+void
+killproc(Proc *p, int ctl)
+{
+	static Note killnote = {
+		"sys: killed",
+		NExit,
+		1,
+	};
+
+	if(p->state <= New || p->pid == 0 || p->kp)
+		return;
+	if(p->state == Broken){
+		unbreak(p);
+		return;
+	}
+	if(ctl != 0)
+		p->procctl = ctl;
+	incref(&killnote);
+	pushnote(p, &killnote);
+	if(p->state == Stopped)
+		ready(p);
 }
 
 /*
--- a/sys/src/9/port/segment.c
+++ b/sys/src/9/port/segment.c
@@ -497,7 +497,13 @@
 }
 
 /*
- *  called with s locked
+ *  Must be called with s locked.
+ *  This relies on s->ref > 1 indicating that
+ *  the segment is shared with other processes
+ *  different from the calling one.
+ *  The calling process (up) is responsible for
+ *  flushing its own TBL by calling flushmmu()
+ *  afterwards.
  */
 void
 mfreeseg(Segment *s, uintptr start, ulong pages)
--