critical_exit()
Jeff Roberson
jroberson at chesapeake.net
Mon Mar 10 14:16:26 PDT 2008
On Mon, 10 Mar 2008, Julian Elischer wrote:
> Stephan Uphoff wrote:
>> Julian Elischer wrote:
>>>
>>> Why would the following:
>>> void
>>> critical_exit(void)
>>> {
>>> struct thread *td;
>>>
>>> td = curthread;
>>> KASSERT(td->td_critnest != 0,
>>> ("critical_exit: td_critnest == 0"));
>>>
>>> if (td->td_critnest == 1) {
>>> td->td_critnest = 0;
>>> if (td->td_owepreempt) {
>>> td->td_critnest = 1;
>>> thread_lock(td);
>>> td->td_critnest--;
>>> SCHED_STAT_INC(switch_owepreempt);
>>> mi_switch(SW_INVOL|SW_PREEMPT, NULL);
>>> thread_unlock(td);
>>> }
>>> } else
>>> td->td_critnest--;
>>>
>>> CTR4(KTR_CRITICAL, "critical_exit by thread %p (%ld, %s) to %d",
>>> td,
>>> (long)td->td_proc->p_pid, td->td_name, td->td_critnest);
>>> }
>>>
>>>
>>> not be expressed:
>>>
>>> void
>>> critical_exit(void)
>>> {
>>> struct thread *td;
>>>
>>> td = curthread;
>>> KASSERT(td->td_critnest != 0,
>>> ("critical_exit: td_critnest == 0"));
>>>
>>> if (td->td_critnest == 1) {
>>> if (td->td_owepreempt) {
>>> thread_lock(td);
>>> td->td_critnest = 0;
>>> SCHED_STAT_INC(switch_owepreempt);
>>> mi_switch(SW_INVOL|SW_PREEMPT, NULL);
>>> thread_unlock(td);
>>> } else {
>> XXXXX If preemption happens here td_owepreempt will be set to preempt the
>> current thread
>> XXXXX since td_critnest != 0 . However td_owepreempt is not checked again
>> so we will not
>> XXXXX preempt on td_critnest = 0;
>
> jeff's comment was that it could be expresssed as:
>
> if (--(td->td_critnest) == 0) {
> if (td->td_owepreempt) {
> thread_lock(td);
> td->td_critnest = 0;
> SCHED_STAT_INC(switch_owepreempt);
> mi_switch(SW_INVOL|SW_PREEMPT, NULL);
> thread_unlock(td);
> }
> }
if (--(td->td_critnest) == 0) {
if (td->td_owepreempt) {
thread_lock(td);
if (td->td_owepreempt) {
SCHED_STAT_INC(switch_owepreempt);
mi_switch(SW_INVOL|SW_PREEMPT, NULL);
}
thread_unlock(td);
}
}
Wouldn't that do just fine? If a preemption occurred before you disabled
interrupts in thread_lock you'd skip the switch after acquiring it.
I don't see a way that we could miss owepreempt with the above code. I'd
also like to make critical_enter/critical_exit inlines with a
_critical_exit() that does the switch. with thread_lock we now do a lot
more nested spinlocking etc. All of these non-contiguous instruction
pointers add up.
>
> This has the same race.. but as you say, it probably doesn't matter.
> In fact the race is probably required to ensure that pre-emption Does occur
> one way or another.
>
>
>
>
>>> td_critnest = 0;
>>> }
>>> } else
>>> td->td_critnest--;
>>>
>>> CTR4(KTR_CRITICAL, "critical_exit by thread %p (%ld, %s) to %d",
>>> td,
>>> (long)td->td_proc->p_pid, td->td_name, td->td_critnest);
>>> }
>>>
>>> It seems to me there is a race in the current version, where the
>>> critical count is temporarily 0, where the thread could be pre-empted
>>> when it shouldn't be..
>>
>> Yes - there is a race where the thread could be preempted twice.
>> However this is fairly harmless in comparison to not being preempted at
>> all.
>> This being said it may be worthwhile to see if that race can be fixed now
>> after
>> the thread lock changes.
>>
>>>
>>> (prompted by a comment by jeffr that made me go look at this code)..
>>>
>>
>> Stephan
>
>
More information about the freebsd-current
mailing list