void spinlock_check() { const int NUMLOCKS=10; const int NUMRUNS=5; int i,j,run; const char* file = "spinlock_check"; spinlock locks[NUMLOCKS]; // Initialize the locks for(i=0;i<NUMLOCKS;i++) spinlock_init_(&locks[i], file, 0); // Make sure that all locks have CPU set to NULL initially for(i=0;i<NUMLOCKS;i++) assert(locks[i].cpu==NULL); // Make sure that all locks have the correct debug info. for(i=0;i<NUMLOCKS;i++) assert(locks[i].file==file); for (run=0;run<NUMRUNS;run++) { // Lock all locks for(i=0;i<NUMLOCKS;i++) spinlock_godeep(i, &locks[i]); // Make sure that all locks have the right CPU for(i=0;i<NUMLOCKS;i++) assert(locks[i].cpu == cpu_cur()); // Make sure that all locks have holding correctly implemented. for(i=0;i<NUMLOCKS;i++) assert(spinlock_holding(&locks[i]) != 0); // Make sure that top i frames are somewhere in godeep. for(i=0;i<NUMLOCKS;i++) { for(j=0; j<=i && j < DEBUG_TRACEFRAMES ; j++) { assert(locks[i].eips[j] >= (uint32_t)spinlock_godeep); assert(locks[i].eips[j] < (uint32_t)spinlock_godeep+100); } } // Release all locks for(i=0;i<NUMLOCKS;i++) spinlock_release(&locks[i]); // Make sure that the CPU has been cleared for(i=0;i<NUMLOCKS;i++) assert(locks[i].cpu == NULL); for(i=0;i<NUMLOCKS;i++) assert(locks[i].eips[0]==0); // Make sure that all locks have holding correctly implemented. for(i=0;i<NUMLOCKS;i++) assert(spinlock_holding(&locks[i]) == 0); } cprintf("spinlock_check() succeeded!\n"); }
// Save the current process's state before switching to another process. // Copies trapframe 'tf' into the proc struct, // and saves any other relevant state such as FPU state. // The 'entry' parameter is one of: // -1 if we entered the kernel via a trap before executing an insn // 0 if we entered via a syscall and must abort/rollback the syscall // 1 if we entered via a syscall and are completing the syscall void proc_save(proc *p, trapframe *tf, int entry) { if(!spinlock_holding(&(p->lock))) panic("not holding p->lock"); // memcpy(&(p->sv.tf), tf, sizeof(p->sv.tf)); p->sv.tf = *tf; if(entry == 0) p->sv.tf.eip -= 2; }
// Release the lock. void spinlock_release(struct spinlock *lk) { if(!spinlock_holding(lk)) panic("Not holding lock"); lk->cpu = NULL; lk->eips[0] = 0; xchg(&(lk->locked), 0); }
// Acquire the lock. // Loops (spins) until the lock is acquired. // Holding a lock for a long time may cause // other CPUs to waste time spinning to acquire it. void spinlock_acquire(struct spinlock *lk) { if(spinlock_holding(lk)) panic("Already holding lock."); while(xchg(&(lk->locked), 1) != 0) pause(); lk->cpu = cpu_cur(); debug_trace(read_ebp(), lk->eips); }
// Switch to and run a specified process, which must already be locked. void gcc_noreturn proc_run(proc *p) { // cprintf("proc_run %p\n", p); if(!spinlock_holding(&(p->lock))) panic("should have p->lock.\n"); p->runcpu = cpu_cur(); p->state = PROC_RUN; cpu_cur()->proc = p; spinlock_release(&(p->lock)); trap_return(&(p->sv.tf)); }
// Go to sleep waiting for a given child process to finish running. // Parent process 'p' must be running and locked on entry. // The supplied trapframe represents p's register state on syscall entry. void gcc_noreturn proc_wait(proc *p, proc *cp, trapframe *tf) { //assert(proc_cur() == p); assert(spinlock_holding(&p->lock)); proc_save(p, tf, 0); //saves the trapframe in the parent, roll back the INT instruction (entry of 0) p->state = PROC_WAIT; p->runcpu = NULL; p->waitchild = cp; spinlock_release(&p->lock); //release the lock on the parent proc_sched(); //runs scheduler again }
// // Allocates a physical page from the page free list. // Does NOT set the contents of the physical page to zero - // the caller must do that if necessary. // // RETURNS // - a pointer to the page's pageinfo struct if successful // - NULL if no available physical pages. // // Hint: pi->refs should not be incremented // Hint: be sure to use proper mutual exclusion for multiprocessor operation. pageinfo * mem_alloc(void) { if(!spinlock_holding(&_freelist_lock)); spinlock_acquire(&_freelist_lock); pageinfo *p = mem_freelist; if(p != NULL) mem_freelist = p->free_next; // Remove page from free list p->home = 0; p->shared = 0; spinlock_release(&_freelist_lock); return p; }
// `High'-level console I/O. Used by readline and cprintf. void cputs(const char *str) { if (read_cs() & 3) return sys_cputs(str); // use syscall from user mode // Hold the console spinlock while printing the entire string, // so that the output of different cputs calls won't get mixed. // Implement ad hoc recursive locking for debugging convenience. bool already = spinlock_holding(&cons_lock); if (!already) spinlock_acquire(&cons_lock); char ch; while (*str) cons_putc(*str++); if (!already) spinlock_release(&cons_lock); }
// Switch to and run a specified process, which must already be locked. void gcc_noreturn proc_run(proc *p) { if (!spinlock_holding(&p->lock)) panic("proc_run without lock"); //if (p->parent) cprintf("running child\n"); // else cprintf("running root\n"); p->state = PROC_RUN; p->runcpu = cpu_cur(); cpu_cur()->proc = p; // Enable interrupts (for preemption) p->sv.tf.eflags |= (1 << 9); p->sv.tf.ds = CPU_GDT_UDATA | 3; p->sv.tf.es = CPU_GDT_UDATA | 3; p->sv.tf.cs = CPU_GDT_UCODE | 3; p->sv.tf.ss = CPU_GDT_UDATA | 3; lcr3 (mem_phys(p->pdir)); spinlock_release(&p->lock); trap_return(&p->sv.tf); }