Jan Kiszka wrote:
> Gilles Chanteperdrix wrote:
>> Jan Kiszka wrote:
>>> Gilles Chanteperdrix wrote:
>>>> Jan Kiszka wrote:
>>> ...
>>>>> I think I'm getting closer to the issue. Our actual problem comes from
>>>>> the fact that the xnsynch_owner is easily out of sync with the real
>>>>> owner, it even sometimes points to a former owner:
>>>>> Thread A releases a mutex on which thread B pends. It wakes up B,
>>>>> causing it to become the new xnsynch owner, and clears the claimed bit
>>>>> as there are no further sleepers. B returns, and when it wants to
>>>>> release the mutex, it does this happily in user space because claimed is
>>>>> not set. Now the fast lock variable is 'unlocked', while xnsynch still
>>>>> reports B being the owner. This is no problem as the next time two
>>>>> threads fight over this lock the waiter will simply overwrite the
>>>>> xnsynch_owner before it falls asleep. But this "trick" doesn't work for
>>>>> waiters that have been robbed. They will spin inside xnsynch_sleep_on
>>>>> and stumble over this inconsistency.
>>>>> I have two approaches in mind now: First one is something like
>>>>> XNSYNCH_STEALNOINFORM, i.e. causing xnsynch_sleep_on to not set XNROBBED
>>>>> so that the robbed thread spins one level higher in the skin code -
>>>>> which would have to be extended a bit.
>>>> No, the stealing is the xnsynch job.
>>>>> Option two is to clear xnsynch_owner once a new owner is about to return
>>>>> from kernel with the lock held while there are no more xnsynch_sleepers.
>>>>> That should work with even less changes and save us one syscall in the
>>>>> robbed case. Need to think about it more, though.
>>>> In fact the only time when the owner is required to be in sync is when
>>>> PIP occurs, and this is guaranteed to work, because when PIP is needed a
>>>> syscall is emitted anyway. To the extent that xnsynch does not even
>>>> track the owner on non PIP synch (which is why the posix skin originally
>>>>  forcibly set the synch owner, and it was simply kept to get the fastsem
>>>> stuff working).
>>>> Ok. And what about the idea of the xnsynch bit to tell him "hey, the
>>>> owner is tracked in the upper layer, go there to find it".
>>> I'm yet having difficulties to imagine how this should look like when
>>> it's implemented. Would it be simpler than my second idea?
>>> Anyway, here is a patch (on top of my handle-based lock series) for the
>>> approach that clears xnsynch_owner when there are no waiters. At least
>>> it causes no regression based on your test, but I haven't checked lock
>>> stealing yet. In theory, everything still appears to be fine to me. This
>>> approach basically restores the state we find when some thread just
>>> acquired the lock in user space.
>>> Jan
>>> ---
>>>  ksrc/skins/posix/mutex.c |   10 +++++++---
>>>  ksrc/skins/posix/mutex.h |   17 +++++++++++------
>>>  2 files changed, 18 insertions(+), 9 deletions(-)
>>> Index: b/ksrc/skins/posix/mutex.c
>>> ===================================================================
>>> --- a/ksrc/skins/posix/mutex.c
>>> +++ b/ksrc/skins/posix/mutex.c
>>> @@ -117,7 +117,6 @@ int pse51_mutex_init_internal(struct __s
>>>     mutex->attr = *attr;
>>>     mutex->owner = ownerp;
>>>     mutex->owningq = kq;
>>> -   mutex->sleepers = 0;
>>>     xnarch_atomic_set(ownerp, XN_NO_HANDLE);
>>>     xnlock_get_irqsave(&nklock, s);
>>> @@ -305,14 +304,12 @@ int pse51_mutex_timedlock_break(struct _
>>>             /* Attempting to relock a normal mutex, deadlock. */
>>>             xnlock_get_irqsave(&nklock, s);
>>>             for (;;) {
>>> -                   ++mutex->sleepers;
>>>                     if (timed)
>>>                             xnsynch_sleep_on(&mutex->synchbase,
>>>                                              abs_to, XN_REALTIME);
>>>                     else
>>>                             xnsynch_sleep_on(&mutex->synchbase,
>>>                                              XN_INFINITE, XN_RELATIVE);
>>> -                   --mutex->sleepers;
>>>                     if (xnthread_test_info(cur, XNBREAK)) {
>>>                             err = -EINTR;
>>> @@ -329,6 +326,13 @@ int pse51_mutex_timedlock_break(struct _
>>>                             break;
>>>                     }
>>>             }
>>> +           if (!xnsynch_nsleepers(&mutex->synchbase)) {
>>> +                   xnarch_atomic_set
>>> +                           (mutex->owner,
>>> +                            clear_claimed
>>> +                                   (xnarch_atomic_get(mutex->owner)));
>>> +                   xnsynch_set_owner(&mutex->synchbase, NULL);
>>> +           }
>> I think an atomic_cmpxchg would be better here.
> Not really. The claimed bit is set, so no one can change the owner
> without entering the kernel. But there we are also holding nklock. BTW,
> pse51_mutex_timedlock_internal does the same already.

I meant from an assembly generated code point of view. atomic_set +
atomic_get generates two atomic operations (well, except on x86),
whereas atomic_cmpxchg is just an atomic sequence. Except that we will
have to do the atomic_get also.

And we do not give a damn about this code. It is the implementation of
the deadlock that happens when trying to relock a non-recursive mutex.


Xenomai-core mailing list

Reply via email to