/* * a write to a closed pipe causes a note to be sent to * the process. */ static long pipewrite(Chan *c, void *va, long n, vlong) { Pipe *p; if(!islo()) print("pipewrite hi %#p\n", getcallerpc(&c)); if(waserror()) { /* avoid notes when pipe is a mounted queue */ if((c->flag & CMSG) == 0) postnote(up, 1, "sys: write on closed pipe", NUser); nexterror(); } p = c->aux; switch(NETTYPE(c->qid.path)){ case Qdata0: n = qwrite(p->q[1], va, n); break; case Qdata1: n = qwrite(p->q[0], va, n); break; default: panic("pipewrite"); } poperror(); return n; }
/* * write to a queue. only Maxatomic bytes at a time is atomic. */ int qwrite(Queue *q, void *vp, int len) { int n, sofar; Block *b; uchar *p = vp; QDEBUG if(!islo()) print("qwrite hi %#p\n", getcallerpc(&q)); sofar = 0; do { n = len-sofar; if(n > Maxatomic) n = Maxatomic; b = allocb(n); setmalloctag(b, (up->text[0]<<24)|(up->text[1]<<16)|(up->text[2]<<8)|up->text[3]); if(waserror()){ freeb(b); nexterror(); } memmove(b->wp, p+sofar, n); poperror(); b->wp += n; qbwrite(q, b); sofar += n; } while(sofar < len && (q->state & Qmsg) == 0); return len; }
static void myscreenputs(char *s, int n) { int i; Rune r; char buf[4]; if(!islo()) { /* don't deadlock trying to print in interrupt */ if(!canlock(&screenlock)) return; } else lock(&screenlock); while(n > 0){ i = chartorune(&r, s); if(i == 0){ s++; --n; continue; } memmove(buf, s, i); buf[i] = 0; n -= i; s += i; screenputc(buf); } unlock(&screenlock); }
/* * a write to a closed pipe causes a note to be sent to * the process. */ static int32_t pipewrite(Chan *c, void *va, int32_t n, int64_t mm) { Proc *up = externup(); Pipe *p; if(0)if(!islo()) print("pipewrite hi %#p\n", getcallerpc()); // devmnt? if(waserror()) { /* avoid notes when pipe is a mounted queue */ if((c->flag & CMSG) == 0) postnote(up, 1, "sys: write on closed pipe", NUser); nexterror(); } p = c->aux; switch(PIPETYPE(c->qid.path)){ case Qdata0: n = qwrite(p->q[1], va, n); break; case Qdata1: n = qwrite(p->q[0], va, n); break; default: panic("pipewrite"); } poperror(); return n; }
void idlehands(void) { char *msgb = "idlehands called with splhi\n"; char *msga = "doze returns with splhi\n"; if(!islo()){ uartputs(msga, strlen(msga)); spllo(); } doze(); if(!islo()){ uartputs(msgb, strlen(msgb)); spllo(); } }
void iunlock(Lock *l) { ulong sr; #ifdef LOCKCYCLES l->lockcycles += lcycles(); cumilockcycles += l->lockcycles; if(l->lockcycles > maxilockcycles){ maxilockcycles = l->lockcycles; maxilockpc = l->pc; } if(l->lockcycles > 2400) ilockpcs[n++ & 0xff] = l->pc; #endif if(l->key == 0) print("iunlock: not locked: pc %#p\n", getcallerpc(&l)); if(!l->isilock) print("iunlock of lock: pc %#p, held by %#lux\n", getcallerpc(&l), l->pc); if(islo()) print("iunlock while lo: pc %#p, held by %#lux\n", getcallerpc(&l), l->pc); sr = l->sr; l->m = nil; coherence(); l->key = 0; coherence(); m->ilockdepth--; if(up) up->lastilock = nil; splx(sr); }
void unlock(Lock *l) { Proc *up = externup(); uint64_t x; if(LOCKCYCLES){ cycles(&x); l->lockcycles = x - l->lockcycles; if(l->lockcycles > maxlockcycles){ maxlockcycles = l->lockcycles; maxlockpc = l->_pc; } } if(l->key == 0) print("unlock: not locked: pc %#p\n", getcallerpc()); if(l->isilock) print("unlock of ilock: pc %#p, held by %#p\n", getcallerpc(), l->_pc); if(l->p != up) print("unlock: up changed: pc %#p, acquired at pc %#p, lock p %#p, unlock up %#p\n", getcallerpc(), l->_pc, l->p, up); l->m = nil; l->key = 0; coherence(); if(up && adec(&up->nlocks) == 0 && up->delaysched && islo()){ /* * Call sched if the need arose while locks were held * But, don't do it from interrupt routines, hence the islo() test */ sched(); } }
void unlock(Lock *l) { #ifdef LOCKCYCLES l->lockcycles += lcycles(); cumlockcycles += l->lockcycles; if(l->lockcycles > maxlockcycles){ maxlockcycles = l->lockcycles; maxlockpc = l->pc; } #endif if(l->key == 0) print("unlock: not locked: pc %#p\n", getcallerpc(&l)); if(l->isilock) print("unlock of ilock: pc %lux, held by %lux\n", getcallerpc(&l), l->pc); if(l->p != up) print("unlock: up changed: pc %#p, acquired at pc %lux, lock p %#p, unlock up %#p\n", getcallerpc(&l), l->pc, l->p, up); l->m = nil; coherence(); l->key = 0; coherence(); if(up && deccnt(&up->nlocks) == 0 && up->delaysched && islo()){ /* * Call sched if the need arose while locks were held * But, don't do it from interrupt routines, hence the islo() test */ sched(); } }
/* * Print a string on the console. Convert \n to \r\n for serial * line consoles. Locking of the queues is left up to the screen * or uart code. Multi-line messages to serial consoles may get * interspersed with other messages. */ static void putstrn0(char *str, int n, int usewrite) { if(!islo()) usewrite = 0; /* * how many different output devices do we need? */ kmesgputs(str, n); /* * if someone is reading /dev/kprint, * put the message there. * if not and there's an attached bit mapped display, * put the message there. * * if there's a serial line being used as a console, * put the message there. */ if(kprintoq != nil && !qisclosed(kprintoq)){ if(usewrite) qwrite(kprintoq, str, n); else qiwrite(kprintoq, str, n); }else if(screenputs != nil) screenputs(str, n); uartputs(str, n); #if 0 // Plan 9 VX if(serialoq == nil){ uartputs(str, n); return; } while(n > 0) { t = memchr(str, '\n', n); if(t && !kbd.raw) { m = t-str; if(usewrite){ qwrite(serialoq, str, m); qwrite(serialoq, "\r\n", 2); } else { qiwrite(serialoq, str, m); qiwrite(serialoq, "\r\n", 2); } n -= m+1; str = t+1; } else { if(usewrite) qwrite(serialoq, str, n); else qiwrite(serialoq, str, n); break; } } #endif }
void qlock(QLock *q) { Proc *up = externup(); Proc *p; uint64_t t0; cycles(&t0); if(!islo() && machp()->ilockdepth != 0){ print("qlock with ilockdepth %d,", machp()->ilockdepth); stacksnippet(); } if(up != nil && up->nlocks) print("qlock: %#p: nlocks %d", getcallerpc(&q), up->nlocks); if(!canlock(&q->use)){ lock(&q->use); slockstat(getcallerpc(&q), t0); } qlockstats.qlock++; if(!q->locked) { q->locked = 1; q->pc = getcallerpc(&q); unlock(&q->use); return; } if(up == nil) panic("qlock"); qlockstats.qlockq++; p = q->tail; if(p == 0) q->head = up; else p->qnext = up; q->tail = up; up->qnext = 0; up->state = Queueing; up->qpc = getcallerpc(&q); if(up->trace) proctrace(up, SLock, 0); unlock(&q->use); sched(); lockstat(getcallerpc(&q), t0); }
/* * write to a queue. only Maxatomic bytes at a time is atomic. */ int qwrite(struct queue *q, void *vp, int len) { int n, sofar; struct block *b; uint8_t *p = vp; void *ext_buf; QDEBUG if (!islo()) printd("qwrite hi %p\n", getcallerpc(&q)); sofar = 0; do { n = len - sofar; /* This is 64K, the max amount per single block. Still a good value? */ if (n > Maxatomic) n = Maxatomic; /* If n is small, we don't need to bother with the extra_data. But * until the whole stack can handle extd blocks, we'll use them * unconditionally. */ #ifdef CONFIG_BLOCK_EXTRAS /* allocb builds in 128 bytes of header space to all blocks, but this is * only available via padblock (to the left). we also need some space * for pullupblock for some basic headers (like icmp) that get written * in directly */ b = allocb(64); ext_buf = kmalloc(n, 0); memcpy(ext_buf, p + sofar, n); block_add_extd(b, 1, KMALLOC_WAIT); /* returns 0 on success */ b->extra_data[0].base = (uintptr_t)ext_buf; b->extra_data[0].off = 0; b->extra_data[0].len = n; b->extra_len += n; #else b = allocb(n); memmove(b->wp, p + sofar, n); b->wp += n; #endif qbwrite(q, b); sofar += n; } while (sofar < len && (q->state & Qmsg) == 0); return len; }
/* * write to a queue. only Maxatomic bytes at a time is atomic. */ int qwrite(Queue *q, void *vp, int len) { int n, sofar; Block *b; uchar *p = vp; QDEBUG if(!islo()) print("qwrite hi %#p\n", getcallerpc(&q)); /* stop queue bloat before allocating blocks */ if(q->len/2 >= q->limit && q->noblock == 0 && q->bypass == nil){ while(waserror()){ if(up->procctl == Proc_exitme || up->procctl == Proc_exitbig) error(Egreg); } qflow(q); poperror(); } sofar = 0; do { n = len-sofar; if(n > Maxatomic) n = Maxatomic; b = allocb(n); setmalloctag(b, (up->text[0]<<24)|(up->text[1]<<16)|(up->text[2]<<8)|up->text[3]); if(waserror()){ freeb(b); nexterror(); } memmove(b->wp, p+sofar, n); poperror(); b->wp += n; qbwrite(q, b); sofar += n; } while(sofar < len && (q->state & Qmsg) == 0); return len; }
static void vgascreenputs(char* s, int n) { int i; Rune r; char buf[4]; VGAscr *scr; Rectangle flushr; scr = &vgascreen[0]; if(!islo()){ /* * Don't deadlock trying to * print in an interrupt. */ if(!canlock(&vgascreenlock)) return; } else lock(&vgascreenlock); flushr = Rect(10000, 10000, -10000, -10000); while(n > 0){ i = chartorune(&r, s); if(i == 0){ s++; --n; continue; } memmove(buf, s, i); buf[i] = 0; n -= i; s += i; vgascreenputc(scr, buf, &flushr); } flushmemscreen(flushr); unlock(&vgascreenlock); }
static char* flushq(Ctlr *ctlr, uint qid) { TXQ *q; int i; q = &ctlr->tx[qid]; qlock(q); for(i = 0; i < 200 && !ctlr->broken; i++){ if(txqempty(q)){ qunlock(q); return nil; } if(islo() && !waserror()){ tsleep(q, txqempty, q, 10); poperror(); } } qunlock(q); if(ctlr->broken) return "flushq: broken"; return "flushq: timeout"; }
void iunlock(Lock *l) { Proc *up = externup(); Mpl pl; uint64_t x; if(LOCKCYCLES){ cycles(&x); l->lockcycles = x - l->lockcycles; if(l->lockcycles > maxilockcycles){ maxilockcycles = l->lockcycles; maxilockpc = l->_pc; } } if(l->key == 0) print("iunlock: not locked: pc %#p\n", getcallerpc()); if(!l->isilock) print("iunlock of lock: pc %#p, held by %#p\n", getcallerpc(), l->_pc); if(islo()) print("iunlock while lo: pc %#p, held by %#p\n", getcallerpc(), l->_pc); if(l->m != machp()){ print("iunlock by cpu%d, locked by cpu%d: pc %#p, held by %#p\n", machp()->machno, l->m->machno, getcallerpc(), l->_pc); } pl = l->pl; l->m = nil; l->key = 0; coherence(); machp()->ilockdepth--; if(up) up->lastilock = nil; splx(pl); }
/* * All traps come here. It is slower to have all traps call trap() * rather than directly vectoring the handler. However, this avoids a * lot of code duplication and possible bugs. The only exception is * VectorSYSCALL. * Trap is called with interrupts disabled via interrupt-gates. */ void trap(Ureg* ureg) { int clockintr, vno, user; // cache the previous vno to see what might be causing // trouble vno = ureg->type; uint64_t gsbase = rdmsr(GSbase); //if (sce > scx) iprint("===================="); lastvno = vno; if (gsbase < 1ULL<<63) die("bogus gsbase"); Proc *up = externup(); char buf[ERRMAX]; Vctl *ctl, *v; machp()->perf.intrts = perfticks(); user = userureg(ureg); if(user && (machp()->NIX.nixtype == NIXTC)) { up->dbgreg = ureg; cycles(&up->kentry); } clockintr = 0; //_pmcupdate(machp()); if(ctl = vctl[vno]) { if(ctl->isintr) { machp()->intr++; if(vno >= VectorPIC && vno != VectorSYSCALL) machp()->lastintr = ctl->Vkey.irq; } else if(up) up->nqtrap++; if(ctl->isr) { ctl->isr(vno); if(islo())print("trap %d: isr %p enabled interrupts\n", vno, ctl->isr); } for(v = ctl; v != nil; v = v->next) { if(v->f) { v->f(ureg, v->a); if(islo())print("trap %d: ctlf %p enabled interrupts\n", vno, v->f); } } if(ctl->eoi) { ctl->eoi(vno); if(islo())print("trap %d: eoi %p enabled interrupts\n", vno, ctl->eoi); } intrtime(vno); if(ctl->isintr) { if(ctl->Vkey.irq == IrqCLOCK || ctl->Vkey.irq == IrqTIMER) clockintr = 1; if (ctl->Vkey.irq == IrqTIMER) oprof_alarm_handler(ureg); if(up && !clockintr) preempted(); } } else if(vno < nelem(excname) && user) { spllo(); snprint(buf, sizeof buf, "sys: trap: %s", excname[vno]); postnote(up, 1, buf, NDebug); } else if(vno >= VectorPIC && vno != VectorSYSCALL) { /* * An unknown interrupt. * Check for a default IRQ7. This can happen when * the IRQ input goes away before the acknowledge. * In this case, a 'default IRQ7' is generated, but * the corresponding bit in the ISR isn't set. * In fact, just ignore all such interrupts. */ /* clear the interrupt */ i8259isr(vno); iprint("cpu%d: spurious interrupt %d, last %d\n", machp()->machno, vno, machp()->lastintr); intrtime(vno); if(user) kexit(ureg); return; } else { if(vno == VectorNMI) { nmienable(); if(machp()->machno != 0) { iprint("cpu%d: PC %#llx\n", machp()->machno, ureg->ip); for(;;); } } dumpregs(ureg); if(!user) { ureg->sp = PTR2UINT(&ureg->sp); dumpstackwithureg(ureg); } if(vno < nelem(excname)) panic("%s", excname[vno]); panic("unknown trap/intr: %d\n", vno); } splhi(); /* delaysched set because we held a lock or because our quantum ended */ if(up && up->delaysched && clockintr) { if(0) if(user && up->ac == nil && up->nqtrap == 0 && up->nqsyscall == 0) { if(!waserror()) { up->ac = getac(up, -1); poperror(); runacore(); return; } } sched(); splhi(); } if(user) { if(up && up->procctl || up->nnote) notify(ureg); kexit(ureg); } }
/* * If changing this routine, look also at sleep(). It * contains a copy of the guts of sched(). */ void sched(void) { Proc *up = externup(); Proc *p; if(!islo() && machp()->ilockdepth) panic("cpu%d: ilockdepth %d, last lock %#p at %#p, sched called from %#p", machp()->machno, machp()->ilockdepth, up? up->lastilock: nil, (up && up->lastilock)? up->lastilock->_pc: 0, getcallerpc()); kstackok(); if(up){ /* * Delay the sched until the process gives up the locks * it is holding. This avoids dumb lock loops. * Don't delay if the process is Moribund. * It called sched to die. * But do sched eventually. This avoids a missing unlock * from hanging the entire kernel. * But don't reschedule procs holding palloc or procalloc. * Those are far too important to be holding while asleep. * * This test is not exact. There can still be a few * instructions in the middle of taslock when a process * holds a lock but Lock.p has not yet been initialized. */ if(up->nlocks) if(up->state != Moribund) if(up->delaysched < 20 || pga.l.p == up || procalloc.l.p == up){ up->delaysched++; run.delayedscheds++; return; } up->delaysched = 0; splhi(); /* statistics */ if(up->nqtrap == 0 && up->nqsyscall == 0) up->nfullq++; machp()->cs++; procsave(up); mmuflushtlb(machp()->MMU.pml4->pa); if(setlabel(&up->sched)){ procrestore(up); spllo(); return; } /*debug*/gotolabel(&machp()->sched); } machp()->inidle = 1; p = runproc(); /* core 0 never returns */ machp()->inidle = 0; if(!p->edf){ updatecpu(p); p->priority = reprioritize(p); } if(nosmp){ if(p != machp()->readied) machp()->schedticks = machp()->ticks + HZ/10; machp()->readied = 0; } machp()->externup = p; up = p; machp()->qstart = machp()->ticks; up->nqtrap = 0; up->nqsyscall = 0; up->state = Running; //up->mach = m; up->mach = sys->machptr[machp()->machno]; machp()->proc = up; // iprint("up->sched.sp %p * %p\n", up->sched.sp, // *(void **) up->sched.sp); mmuswitch(up); assert(!up->wired || up->wired == machp()); if (0) hi("gotolabel\n"); /*debug*/gotolabel(&up->sched); }