diff options
author | cinap_lenrek <cinap_lenrek@felloff.net> | 2014-07-14 06:02:21 +0200 |
---|---|---|
committer | cinap_lenrek <cinap_lenrek@felloff.net> | 2014-07-14 06:02:21 +0200 |
commit | 655ec332a714d3e5cc6aace798daf832e17e001e (patch) | |
tree | 90234eaf806722bf1fc54b298226128ce50f2b91 /sys/src/9/port/devproc.c | |
parent | e53511ef4c7d4db443543506e74e4de537da5475 (diff) |
devproc: fix proccrlmemio bugs
dont kill the calling process when demand load fails if fixfault()
is called from devproc. this happens when you delete the binary
of a running process and try to debug the process accessing uncached
pages thru /proc/$pid/mem file.
fixes to procctlmemio():
- fix missed unlock as txt2data() can error
- make sure the segment isnt freed by taking a reference (under p->seglock)
- access the page with segment locked (see comment)
- get rid of the segment stealer lock
other stuff:
- move txt2data() and data2txt() to segment.c
- add procpagecount() function
- make return type mcounseg() to ulong
Diffstat (limited to 'sys/src/9/port/devproc.c')
-rw-r--r-- | sys/src/9/port/devproc.c | 202 |
1 files changed, 110 insertions, 92 deletions
diff --git a/sys/src/9/port/devproc.c b/sys/src/9/port/devproc.c index 346da3c3a..c0ed27c92 100644 --- a/sys/src/9/port/devproc.c +++ b/sys/src/9/port/devproc.c @@ -154,9 +154,9 @@ static char *sname[]={ "Text", "Data", "Bss", "Stack", "Shared", "Phys", }; void procctlreq(Proc*, char*, int); int procctlmemio(Proc*, uintptr, int, void*, int); Chan* proctext(Chan*, Proc*); -Segment* txt2data(Proc*, Segment*); int procstopped(void*); void mntscan(Mntwalk*, Proc*); +ulong procpagecount(Proc *); static Traceevent *tevents; static Lock tlock; @@ -896,23 +896,7 @@ procread(Chan *c, void *va, long n, vlong off) readnum(0, statbuf+j+NUMSIZE*i, NUMSIZE, l, NUMSIZE); } - l = 0; - eqlock(&p->seglock); - if(waserror()){ - qunlock(&p->seglock); - nexterror(); - } - for(i=0; i<NSEG; i++){ - if(s = p->seg[i]){ - eqlock(s); - l += mcountseg(s); - qunlock(s); - } - } - poperror(); - qunlock(&p->seglock); - - readnum(0, statbuf+j+NUMSIZE*6, NUMSIZE, l*BY2PG/1024, NUMSIZE); + readnum(0, statbuf+j+NUMSIZE*6, NUMSIZE, procpagecount(p)*BY2PG/1024, NUMSIZE); readnum(0, statbuf+j+NUMSIZE*7, NUMSIZE, p->basepri, NUMSIZE); readnum(0, statbuf+j+NUMSIZE*8, NUMSIZE, p->priority, NUMSIZE); memmove(a, statbuf+offset, n); @@ -1540,6 +1524,32 @@ procstopped(void *a) return p->state == Stopped; } +ulong +procpagecount(Proc *p) +{ + Segment *s; + ulong pages; + int i; + + eqlock(&p->seglock); + if(waserror()){ + qunlock(&p->seglock); + nexterror(); + } + pages = 0; + for(i=0; i<NSEG; i++){ + if((s = p->seg[i]) != nil){ + eqlock(s); + pages += mcountseg(s); + qunlock(s); + } + } + qunlock(&p->seglock); + poperror(); + + return pages; +} + int procctlmemio(Proc *p, uintptr offset, int n, void *va, int read) { @@ -1547,110 +1557,118 @@ procctlmemio(Proc *p, uintptr offset, int n, void *va, int read) Pte *pte; Page *pg; Segment *s; - uintptr soff, l; - char *a = va, *b; + uintptr soff; + char *a, *b; + int i, l; + + /* Only one page at a time */ + l = BY2PG - (offset&(BY2PG-1)); + if(n > l) + n = l; + + /* + * Make temporary copy to avoid fault while we have + * segment locked as we would deadlock when trying + * to read the calling procs memory. + */ + a = malloc(n); + if(a == nil) + error(Enomem); + if(waserror()) { + free(a); + nexterror(); + } + + if(!read) + memmove(a, va, n); /* can fault */ for(;;) { - s = seg(p, offset, 1); - if(s == 0) + s = seg(p, offset, 0); + if(s == nil) error(Ebadarg); - if(offset+n >= s->top) - n = s->top-offset; + eqlock(&p->seglock); + if(waserror()) { + qunlock(&p->seglock); + nexterror(); + } - if(!read && (s->type&SG_TYPE) == SG_TEXT) - s = txt2data(p, s); + for(i = 0; i < NSEG; i++) { + if(p->seg[i] == s) + break; + } + if(i == NSEG) + error(Egreg); /* segment gone */ - s->steal++; - soff = offset-s->base; - if(waserror()) { - s->steal--; + eqlock(s); + if(waserror()){ + qunlock(s); nexterror(); } - if(fixfault(s, offset, read, 0) == 0) - break; + if(!read && (s->type&SG_TYPE) == SG_TEXT) { + s = txt2data(s); + p->seg[i] = s; + } + incref(s); + qunlock(&p->seglock); + poperror(); poperror(); - s->steal--; + /* segment s still locked, fixfault() unlocks */ + if(!waserror()){ + if(fixfault(s, offset, read, 0) == 0) + break; + poperror(); + } + putseg(s); } - poperror(); + + /* + * Only access the page while segment is locked + * as the proc could segfree or relocate the pte + * concurrently. + */ + eqlock(s); + if(waserror()){ + qunlock(s); + nexterror(); + } + if(offset+n >= s->top) + n = s->top-offset; + soff = offset-s->base; pte = s->map[soff/PTEMAPMEM]; - if(pte == 0) - panic("procctlmemio"); + if(pte == nil) + error(Egreg); /* page gone, should retry? */ pg = pte->pages[(soff&(PTEMAPMEM-1))/BY2PG]; if(pagedout(pg)) - panic("procctlmemio1"); - - l = BY2PG - (offset&(BY2PG-1)); - if(n > l) - n = l; + error(Egreg); /* page gone, should retry? */ + /* Map and copy the page */ k = kmap(pg); - if(waserror()) { - s->steal--; - kunmap(k); - nexterror(); - } b = (char*)VA(k); b += offset&(BY2PG-1); - if(read == 1) - memmove(a, b, n); /* This can fault */ + if(read) + memmove(a, b, n); else memmove(b, a, n); kunmap(k); - poperror(); /* Ensure the process sees text page changes */ if(s->flushme) memset(pg->cachectl, PG_TXTFLUSH, sizeof(pg->cachectl)); - s->steal--; - - if(read == 0) + if(!read) p->newtlb = 1; - return n; -} - -Segment* -txt2data(Proc *p, Segment *s) -{ - int i; - Segment *ps; - - ps = newseg(SG_DATA, s->base, s->size); - ps->image = s->image; - incref(ps->image); - ps->fstart = s->fstart; - ps->flen = s->flen; - ps->flushme = 1; - - qlock(&p->seglock); - for(i = 0; i < NSEG; i++) - if(p->seg[i] == s) - break; - if(i == NSEG) - panic("segment gone"); - qunlock(s); + poperror(); putseg(s); - qlock(ps); - p->seg[i] = ps; - qunlock(&p->seglock); - - return ps; -} + poperror(); -Segment* -data2txt(Segment *s) -{ - Segment *ps; + if(read) + memmove(va, a, n); /* can fault */ - ps = newseg(SG_TEXT, s->base, s->size); - ps->image = s->image; - incref(ps->image); - ps->fstart = s->fstart; - ps->flen = s->flen; - ps->flushme = 1; + free(a); + poperror(); - return ps; + return n; } |