git: plan9front

Download patch

ref: 9d0f484ef87f139aa61f4bb20ca99605cf09322b
parent: 077c1515172c66789b30d18be01f088381b2b8cb
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)
--