diff options
Diffstat (limited to 'TRICKS')
-rw-r--r-- | TRICKS | 140 |
1 files changed, 0 insertions, 140 deletions
@@ -1,140 +0,0 @@ -This file lists subtle things that might not be commented -as well as they should be in the source code and that -might be worth pointing out in a longer explanation or in class. - ---- - -[2009/07/12: No longer relevant; forkret1 changed -and this is now cleaner.] - -forkret1 in trapasm.S is called with a tf argument. -In order to use it, forkret1 copies the tf pointer into -%esp and then jumps to trapret, which pops the -register state out of the trap frame. If an interrupt -came in between the mov tf, %esp and the iret that -goes back out to user space, the interrupt stack frame -would end up scribbling over the tf and whatever memory -lay under it. - -Why is this safe? Because forkret1 is only called -the first time a process returns to user space, and -at that point, cp->tf is set to point to a trap frame -constructed at the top of cp's kernel stack. So tf -*is* a valid %esp that can hold interrupt state. - -If other tf's were used in forkret1, we could add -a cli before the mov tf, %esp. - ---- - -In pushcli, must cli() no matter what. It is not safe to do - - if(cpus[cpu()].ncli == 0) - cli(); - cpus[cpu()].ncli++; - -because if interrupts are off then we might call cpu(), get -rescheduled to a different cpu, look at cpus[oldcpu].ncli, -and wrongly decide not to disable interrupts on the new cpu. - -Instead do - - cli(); - cpus[cpu()].ncli++; - -always. - ---- - -There is a (harmless) race in pushcli, which does - - eflags = readeflags(); - cli(); - if(c->ncli++ == 0) - c->intena = eflags & FL_IF; - -Consider a bottom-level pushcli. -If interrupts are disabled already, then the right thing -happens: read_eflags finds that FL_IF is not set, -and intena = 0. If interrupts are enabled, then -it is less clear that the right thing happens: -the readeflags can execute, then the process -can get preempted and rescheduled on another cpu, -and then once it starts running, perhaps with -interrupts disabled (can happen since the scheduler -only enables interrupts once per scheduling loop, -not every time it schedules a process), it will -incorrectly record that interrupts *were* enabled. -This doesn't matter, because if it was safe to be -running with interrupts enabled before the context -switch, it is still safe (and arguably more correct) -to run with them enabled after the context switch too. - -In fact it would be safe if scheduler always set - c->intena = 1; -before calling swtch, and perhaps it should. - ---- - -The x86's processor-ordering memory model -matches spin locks well, so no explicit memory -synchronization instructions are required in -acquire and release. - -Consider two sequences of code on different CPUs: - -CPU0 -A; -release(lk); - -and - -CPU1 -acquire(lk); -B; - -We want to make sure that: - - all reads in B see the effects of writes in A. - - all reads in A do *not* see the effects of writes in B. - -The x86 guarantees that writes in A will go out -to memory before the write of lk->locked = 0 in -release(lk). It further guarantees that CPU1 -will observe CPU0's write of lk->locked = 0 only -after observing the earlier writes by CPU0. -So any reads in B are guaranteed to observe the -effects of writes in A. - -According to the Intel manual behavior spec, the -second condition requires a serialization instruction -in release, to avoid reads in A happening after giving -up lk. No Intel SMP processor in existence actually -moves reads down after writes, but the language in -the spec allows it. There is no telling whether future -processors will need it. - ---- - -The code in fork needs to read np->pid before -setting np->state to RUNNABLE. The following -is not a correct way to do this: - - int - fork(void) - { - ... - np->state = RUNNABLE; - return np->pid; // oops - } - -After setting np->state to RUNNABLE, some other CPU -might run the process, it might exit, and then it might -get reused for a different process (with a new pid), all -before the return statement. So it's not safe to just -"return np->pid". Even saving a copy of np->pid before -setting np->state isn't safe, since the compiler is -allowed to re-order statements. - -The real code saves a copy of np->pid, then acquires a lock -around the write to np->state. The acquire() prevents the -compiler from re-ordering. |