shithub: front

Download patch

ref: 8d435d074683b347eec263504986af2afa471276
parent: c910ac83167f6a40fdab053c6fdafd4bee83f811
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Sun Jun 8 14:22:56 EDT 2025

kernel: improve imagecache locking

The assumptions are the following:

- attachimage() is called often (for each exec),
needing to acquire the imagealloc lock to walk the
list of active images.

- putimage() with a Image.ref dropping to 0 is rare,
only occuring when the system is under memory pressure
and we'r reclaiming pages from the image cache.

We used to acquire Image lock while holding
imagealloc lock as this was the only way to make sure
we do not race with putimage().

We want to avoid contending on the imagealloc lock here,
so that attachimage() can make progress and new processes
can be execed.

So the lock order is now changed, and we use Image.ref
to make sure the Image entry isnt freed after releasing
imagealloc lock.

This now means that putimage() must acquire imagealloc
lock and re-check that Image.ref == 0 before moving
the entry to the freelist. (holding allocimage lock
is the only way to make sure the Image isnt grabbed
out of the cache and used). And attachimage() never
holds imagealloc lock and Image lock at the same time.

In addition, simplify allocimage() call and move the
code attaching the first text segment to sysexec().
Recheck the active image list if we had to run
imagereclaim() to avoid duplicates.
Make attachimage() return a Image* with a unconditional
reference so the call is symmetic with putimage().

--- a/sys/src/9/port/portfns.h
+++ b/sys/src/9/port/portfns.h
@@ -9,7 +9,7 @@
 Block*		allocb(int);
 int		anyhigher(void);
 int		anyready(void);
-Image*		attachimage(int, Chan*, uintptr, ulong);
+Image*		attachimage(Chan*);
 ulong		beswal(ulong);
 uvlong		beswav(uvlong);
 int		blocklen(Block*);
--- a/sys/src/9/port/segment.c
+++ b/sys/src/9/port/segment.c
@@ -93,21 +93,16 @@
 	Pte **pte, **emap;
 	Image *i;
 
-	if(s == nil)
+	if(s == nil || decref(s) != 0)
 		return;
 
 	i = s->image;
 	if(i != nil) {
 		lock(i);
-		if(decref(s) != 0){
-			unlock(i);
-			return;
-		}
 		if(i->s == s)
 			i->s = nil;
 		putimage(i);
-	} else if(decref(s) != 0)
-		return;
+	}
 
 	assert(s->sema.prev == &s->sema);
 	assert(s->sema.next == &s->sema);
@@ -240,13 +235,11 @@
 }
 
 Image*
-attachimage(int type, Chan *c, uintptr base, ulong len)
+attachimage(Chan *c)
 {
 	Image *i, **l;
 
-	c->flag &= ~CCACHE;
-	cclunk(c);
-
+retry:
 	lock(&imagealloc);
 
 	/*
@@ -253,28 +246,22 @@
 	 * Search the image cache for remains of the text from a previous
 	 * or currently running incarnation
 	 */
-	for(i = ihash(c->qid.path); i; i = i->hash) {
-		if(c->qid.path == i->qid.path) {
-			lock(i);
-			if(eqchantdqid(c, i->type, i->dev, i->qid, 0) && c->qid.type == i->qid.type)
-				goto found;
-			unlock(i);
-		}
+	for(i = ihash(c->qid.path); i != nil; i = i->hash){
+		if(eqchantdqid(c, i->type, i->dev, i->qid, 0))
+			goto found;
 	}
 
 	/* dump pages of inactive images to free image structures */
-	while((i = imagealloc.free) == nil) {
+	if((i = imagealloc.free) == nil) {
 		unlock(&imagealloc);
 		if(imagereclaim(0) == 0 && imagealloc.free == nil){
 			freebroken();		/* can use the memory */
 			resrcwait("no image after reclaim");
 		}
-		lock(&imagealloc);
+		goto retry;
 	}
-
 	imagealloc.free = i->next;
 
-	lock(i);
 	i->type = c->type;
 	i->dev = c->dev;
 	i->qid = c->qid;
@@ -282,28 +269,67 @@
 	l = &ihash(c->qid.path);
 	i->hash = *l;
 	*l = i;
-
 found:
+	incref(i);
 	unlock(&imagealloc);
+
+	lock(i);
 	if(i->c == nil){
 		i->c = c;
 		incref(c);
 	}
 
-	if(i->s == nil) {
-		incref(i);
-		if(waserror()) {
-			putimage(i);
-			nexterror();
+	return i;
+}
+
+/* putimage(): called with image locked and unlocks */
+void
+putimage(Image *i)
+{
+	Image *f, **l;
+	Chan *c;
+	long r;
+
+	r = decref(i);
+	if(i->notext){
+		unlock(i);
+		return;
+	}
+
+	/*
+	 * all remaining references to this image are from the
+	 * page cache, so close the chan.
+	 */
+	if(r == i->pgref){
+		c = i->c;
+		i->c = nil;
+	} else
+		c = nil;
+
+	if(r == 0){
+		assert(i->pgref == 0);
+		assert(i->c == nil);
+		assert(i->s == nil);
+
+		lock(&imagealloc);
+		if(i->ref == 0){
+			l = &ihash(i->qid.path);
+			for(f = *l; f != nil; f = f->hash) {
+				if(f == i) {
+					*l = i->hash;
+					break;
+				}
+				l = &f->hash;
+			}
+			i->next = imagealloc.free;
+			imagealloc.free = i;
 		}
-		i->s = newseg(type, base, len);
-		i->s->image = i;
-		poperror();
+		unlock(&imagealloc);
 	}
-	else
-		incref(i->s);
+	unlock(i);
 
-	return i;
+	if(c != nil)
+		ccloseq(c);	/* does not block */
 }
 
 ulong
@@ -345,51 +371,6 @@
 	qunlock(&imagealloc.ireclaim);
 
 	return np;
-}
-
-/* putimage(): called with image locked and unlocks */
-void
-putimage(Image *i)
-{
-	Image *f, **l;
-	Chan *c;
-	long r;
-
-	r = decref(i);
-	if(i->notext){
-		unlock(i);
-		return;
-	}
-
-	c = nil;
-	if(r == i->pgref){
-		/*
-		 * all remaining references to this image are from the
-		 * page cache, so close the chan.
-		 */
-		c = i->c;
-		i->c = nil;
-	}
-	if(r == 0){
-		l = &ihash(i->qid.path);
-		mkqid(&i->qid, ~0, ~0, QTFILE);
-		unlock(i);
-
-		lock(&imagealloc);
-		for(f = *l; f != nil; f = f->hash) {
-			if(f == i) {
-				*l = i->hash;
-				break;
-			}
-			l = &f->hash;
-		}
-		i->next = imagealloc.free;
-		imagealloc.free = i;
-		unlock(&imagealloc);
-	} else
-		unlock(i);
-	if(c != nil)
-		ccloseq(c);	/* does not block */
 }
 
 uintptr
--- a/sys/src/9/port/sysproc.c
+++ b/sys/src/9/port/sysproc.c
@@ -295,7 +295,7 @@
 	int i, n, indir;
 	ulong magic, ssize, nargs, nbytes;
 	uintptr t, d, b, entry, text, data, bss, bssend, tstk, align;
-	Segment *s, *ts;
+	Segment *s;
 	Image *img;
 	Tos *tos;
 	Chan *tc;
@@ -538,23 +538,36 @@
 
 	/* Text.  Shared. Attaches to cache image if possible */
 	/* attachimage returns a locked cache image */
-	img = attachimage(SG_TEXT | SG_RONLY, tc, UTZERO, (t-UTZERO)>>PGSHIFT);
-	ts = img->s;
-	up->seg[TSEG] = ts;
-	ts->flushme = 1;
-	ts->fstart = 0;
-	ts->flen = text;
-	unlock(img);
+	img = attachimage(tc);
+	if((s = img->s) != nil && s->flen == text){
+		assert(s->image == img);
+		incref(s);
+		putimage(img);
+	} else {
+		if(waserror()){
+			putimage(img);
+			nexterror();
+		}
+		s = newseg(SG_TEXT | SG_RONLY, UTZERO, (t-UTZERO)>>PGSHIFT);
+		s->flushme = 1;
+		s->image = img;
+		s->fstart = 0;
+		s->flen = text;
+		img->s = s;
+		unlock(img);
+		poperror();
+	}
+	up->seg[TSEG] = s;
 
 	/* Data. Shared. */
 	s = newseg(SG_DATA, t, (d-t)>>PGSHIFT);
-	up->seg[DSEG] = s;
 
 	/* Attached by hand */
 	incref(img);
 	s->image = img;
-	s->fstart = ts->fstart+ts->flen;
+	s->fstart = text;
 	s->flen = data;
+	up->seg[DSEG] = s;
 
 	/* BSS. Zero fill on demand */
 	up->seg[BSEG] = newseg(SG_BSS, d, (b-d)>>PGSHIFT);
@@ -580,6 +593,11 @@
 	}
 
 	poperror();	/* tc */
+	if(tc == img->c){
+		/* avoid double caching */
+		tc->flag &= ~CCACHE;
+		cclunk(tc);
+	}
 	cclose(tc);
 	poperror();	/* file0 */
 	free(file0);
--