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)
--
⑨