clear. How can I distinguish these two conditions? I mean, whether I
am using my own state/stack or borrowing others' state.
Things become clearer now. Thanks for your reply.
If I understand correctly, which kind of lock should be used depends on
which thread model(i.e "thread filter" or "ithread") we use. If I want
to use a lock, I must know in advance which kind of thread model I am
in, otherwise the interrupt handling code might cause you deadlock or
kernel panic. The problem is, how can I tell which thread model I am
in? I am not so clear about the thread model things and scheduling
code of FreeBSD...
> This means it is unwise to hold spin locks for long periods. In
> fact, if CPU 2 waits too long in that [stuck] section, it will
> panic, on the assumption that CPU 1 has done something terrible
> and the system is now hung.
>
> This is also waht gives rise to the constrant that you must take
> MTX_SPIN locks "inside" any outer MTX_DEF locks.
What do you mean by "must take MTX_SPIN locks 'inside' any outer
MTX_DEF locks?
Regards,
Yubin Ruan
Is this a typo? I guess you mean something like "you are trying
to take a blocking mutex while holding spin-type mutex".
> and this is not allowed (can cause deadlock). But taking the
> PROC_LOCK first (which may block), then taking the PROC_STATLOCK
> (a spin lock) "inside" the outer PROC_LOCK default mutex, is OK.
I think I get your point: if you take a spin-type mutex, you
already disable interrupt, which in effect means that no other
code can preempt you. Under this circumstance, if you continue to
take a blocking mutex, you may get blocked. Since you already
disable interrupt and nobody can interrupt/preempt you, you are blocked
on that CPU, not being able to do anything, which is pretty much a
"deadlock" (actually this is not a deadlock, but, it is similar)
Regards,
Yubin Ruan
I discover that in the current implementation in FreeBSD, spinlock
does not disable interrupt entirely:
607 for (;;) {
608 if (m->mtx_lock == MTX_UNOWNED &&
_mtx_obtain_lock(m, tid))
609 break;
610 /* Give interrupts a chance while we spin. */
611 spinlock_exit();
612 while (m->mtx_lock != MTX_UNOWNED) {
613 if (i++ < 10000000) {
614 cpu_spinwait();
615 continue;
616 }
617 if (i < 60000000 || kdb_active ||
panicstr != NULL)
618 DELAY(1);
619 else
620 _mtx_lock_spin_failed(m);
621 cpu_spinwait();
622 }
623 spinlock_enter();
624 }
This is `_mtx_lock_spin_cookie(...)` in kern/kern_mutex.c, which
implements the core logic of spinning. However, as you can see, while
spinning, it would enable interrupt "occasionally" and disable it
again... What is the rationale for that?
Regards,
Yubin Ruan
Good explanation. I just missed that "local" interrupt point.
> (In fact, the spinlock exit/enter calls that you see inside
> _mtx_lock_spin_cookie() wrap a loop that does not use compare-and-
> swap operations at all, but rather ordinary memory reads. These
> are cheaper than CAS operations on a lot of CPUs, but they may
> produce wrong answers when two CPUs are racing to write the same
why would that produce wrong result? I think what the inner loop wants
to do is to perform some no-op for a while before it tries again to
acquire the spinlock. So there is no race here.
> location; only a CAS produces a guaranteed answer, which might
> still be "you lost the race". The inner loop you are looking at
> occurs after losing a CAS race. Once we think we might *win* a
> future CAS race, _mtx_lock_spin_cookie() calls spinlock_enter()
> again and tries the actual CAS operation, _mtx_obtain_lock_fetch(),
> with interrupts disabled. Note also the calls to cpu_spinwait()
> -- the Linux equivalent macro is cpu_relax() -- which translates
> to a "pause" instruction on amd64.)
Regards,
Yubin Ruan