ule+smp: small optimization for turnstile priority lending
Attilio Rao
attilio at freebsd.org
Mon Oct 29 16:57:01 UTC 2012
On Wed, Oct 3, 2012 at 3:05 PM, Andriy Gapon <avg at freebsd.org> wrote:
> on 20/09/2012 16:14 Attilio Rao said the following:
>> On 9/20/12, Andriy Gapon <avg at freebsd.org> wrote:
> [snip]
>>> The patch works well as far as I can tell. Thank you!
>>> There is one warning with full witness enables but it appears to be harmless
>>> (so
>>> far):
>>
>> Andriy,
>> thanks a lot for your testing and reports you made so far.
>> Unfortunately I'm going off for 2 weeks now and I won't work on
>> FreeBSD for that timeframe. I will get back to those in 2 weeks then.
>> If you want to play more with this idea feel free to extend/fix/etc.
>> this patch.
>
> Unfortunately I haven't found time to work on this further, but I have some
> additional thoughts.
>
> First, I think that the witness message was not benign and it actually warns about
> the same kind of deadlock that I originally had.
> The problem is that sched_lend_prio() is called with target thread's td_lock held,
> which is a lock of tdq on the thread's CPU. Then, in your patch, we acquire
> current tdq's lock to modify its load. But if two tdq locks are to be held at the
> same time, then they must be locked using tdq_lock_pair, so that lock order is
> maintained. With the patch no tdq lock order can be maintained (can arbitrary)
> and thus it is possible to run into a deadlock.
Indeed. I realized this after thinking about this problem while I was
on holiday.
>
> I see two possibilities here, but don't rule out that there can be more.
>
> 1. Do something like my patch does. That is, manipulate current thread's tdq in
> advance before any other thread/tdq locks are acquired (or td_lock is changed to
> point to a different lock and current tdq is unlocked). The API can be made more
> generic in nature. E.g. it can look like this:
> void
> sched_thread_block(struct thread *td, int inhibitor)
> {
> struct tdq *tdq;
>
> THREAD_LOCK_ASSERT(td, MA_OWNED);
> KASSERT(td == curthread,
> ("sched_thread_block: only curthread is supported"));
> tdq = TDQ_SELF();
> TDQ_LOCK_ASSERT(tdq, MA_OWNED);
> MPASS(td->td_lock == TDQ_LOCKPTR(tdq));
> TD_SET_INHIB(td, inhibitor);
> tdq_load_rem(tdq, td);
> tdq_setlowpri(tdq, td);
> }
>
>
> 2. Try to do things from sched_lend_prio based on curthread's state. This, as it
> seems, would require completely lock-less manipulations of current tdq. E.g. for
> tdq_load we could use atomic operations (it is already accessed locklessly, but
> not modified). But for tdq_lowpri a more elaborate trick would be required like
> having a separate field for a temporary value.
No, this is not going to work because tdq_lowpri and tdq_load are
updated and used in the same critical path (and possibly also other
tdq* members in tdq_runqueue_add(), for example, but I didn't bother
to also check that).
Let me think some more about this and get back to you.
Attilio
--
Peace can only be achieved by understanding - A. Einstein
More information about the freebsd-hackers
mailing list