Spin lock implementation and interrupts

Discussions on more advanced topics such as monolithic vs micro-kernels, transactional memory models, and paging vs segmentation should go here. Use this forum to expand and improve the wiki!
Post Reply
thewrongchristian
Member
Member
Posts: 425
Joined: Tue Apr 03, 2018 2:44 am

Spin lock implementation and interrupts

Post by thewrongchristian »

thewrongchristian wrote:
nexos wrote:
Ethin wrote:You need to send the EOI in the interrupt routine, not when your loading the LDT/IDT. So you'd send the EOI right after incrementing the tick counter.
Also, just a side note: you may wish to turn that tick counter into an atomic integer, especially once you get to SMP. Better to do it earlier than later though. (That's my Rust background speaking, but its generally good to use mutex/locks/atomics on static mutable variables if possible. You can wait though if you don't know how.)
Using a spinlock on a uniproccesser is one of the worst performace decisions out there. For example, T1 acquires a spinlock and gets preempted by T2 while that holding spinlock. T2 then acquires that same spinlock and sitsin its whole timeslice looping. For the timer counter, it would just be better to have a per CPU timer. I think we just got waay off topic.
If you're pre-empting code that has a spin lock, you're doing it wrong whether you're on UP or MP.

I get nervous about even calling other functions with a spin lock held, and anything protected by a spin lock should be held for the minimal amount of time. I spin lock my scheduler, device driver structures that need to communicate with interrupt handlers, and higher level synchronisation structures like mutexes, which maintain wait lists. Everything else uses the higher primitives built on top of spin locks.

And while you hold the spin lock, you should also disable interrupts, to prevent an interrupt handler also trying to get that spin lock, further reinforcing the notion that spin locks should be help for the minimal amount of time.
I'm making a big presumption on how spin locks should be implemented. Some of that is based on experience, I didn't have a reliable way of synchronising between a driver and its interrupt handlers, as my first spin lock implementation didn't disable interrupts and I'd deadlock when an interrupt handler tried to lock a spin lock already held by the interrupted code.

Looking through the forums, spin locks tend to be advised to be used with interrupts disabled.

But looking at the Spinlock wiki page, no mention is made of interrupts.

Is this a hole in the wiki page?
rdos
Member
Member
Posts: 3276
Joined: Wed Oct 01, 2008 1:55 pm

Re: Spin lock implementation and interrupts

Post by rdos »

thewrongchristian wrote: I'm making a big presumption on how spin locks should be implemented. Some of that is based on experience, I didn't have a reliable way of synchronising between a driver and its interrupt handlers, as my first spin lock implementation didn't disable interrupts and I'd deadlock when an interrupt handler tried to lock a spin lock already held by the interrupted code.

Looking through the forums, spin locks tend to be advised to be used with interrupts disabled.

But looking at the Spinlock wiki page, no mention is made of interrupts.

Is this a hole in the wiki page?
You don't need to have interrups disabled all the time, but before attempting to take the spinlock you should disable interrupts, do the protected operation and then release the lock and enable interrupts. If taking the spinlock fails, it's ok to enable interrupts, execute a "yield" instruction and try again.

Generally, when programming for single core systems enable/disable interrupts is sufficient to protect & synchronize code with IRQs, but when there are multiple cores involved, every instance of enable/disable intterupts potentially needs to be replaced with spinlocks to be multicore safe.

Example of spinlock implementation for x86:

Code: Select all

InitSpinlock     MACRO spinlock
    mov spinlock.sl_value,0
                        ENDM

RequestSpinlock MACRO spinlock
    local rs_lock
    local rs_get
    local rs_done
    local rs_pause

    push ax
    push cx
;
    xor cx,cx

rs_lock:    
    mov ax,spinlock.sl_value
    or ax,ax
    je rs_get
;
    sti
    add cx,1
    jnc rs_pause
;
    CrashGate

rs_pause:    
    pause
    jmp rs_lock

rs_get:
    cli
    inc ax
    xchg ax,spinlock.sl_value
    or ax,ax
    je rs_done
;
    jmp rs_lock

rs_done:
    pop cx
    pop ax
    ENDM


ReleaseSpinlock MACRO spinlock
    mov spinlock.sl_value,0
    sti
    ENDM
Post Reply