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