Changeset 4db49344 in mainline for uspace/lib/usbhost/src/endpoint.c


Ignore:
Timestamp:
2018-01-23T21:52:28Z (7 years ago)
Author:
Ondřej Hlavatý <aearsis@…>
Branches:
lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
Children:
3dd80f8
Parents:
a6afb4c
git-author:
Ondřej Hlavatý <aearsis@…> (2018-01-23 20:49:35)
git-committer:
Ondřej Hlavatý <aearsis@…> (2018-01-23 21:52:28)
Message:

usb: fix wrong design of transfer aborting

Apparently, we didn't do a good job in thinking through the problem.
In older HCs, it was done just wrong - the UHCI implementation commited
a batch that could have been already aborted, and EHCI+OHCI might miss
an interrupt because they commited the batch sooner than they added it
to their checked list.

This commit takes everything from the other end, which is probably the
only right one. Instead of an endpoint having an extra mutex, it
inherits a mutex from the outside. It never locks it though, it just
checks if the mutex is locked and uses it for waiting on condition
variables.

This mutex is supposed to be the one which the HC driver uses for
locking its structures in scheduling. This way, we avoid the ABBA
deadlock completely, while preserving the synchronization on an
endpoint.

The good thing is that this implementation is much easier to extend with
multiple active batches per endpoint.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • uspace/lib/usbhost/src/endpoint.c

    ra6afb4c r4db49344  
    6161
    6262        atomic_set(&ep->refcnt, 0);
    63         fibril_mutex_initialize(&ep->guard);
    6463        fibril_condvar_initialize(&ep->avail);
    6564
     
    122121
    123122/**
    124  * Wait until the endpoint have no transfer scheduled.
     123 * Mark the endpoint as online. Supply a guard to be used for this endpoint
     124 * synchronization.
     125 */
     126void endpoint_set_online(endpoint_t *ep, fibril_mutex_t *guard)
     127{
     128        ep->guard = guard;
     129        ep->online = true;
     130}
     131
     132/**
     133 * Mark the endpoint as offline. All other fibrils waiting to activate this
     134 * endpoint will be interrupted.
     135 */
     136void endpoint_set_offline_locked(endpoint_t *ep)
     137{
     138        assert(ep);
     139        assert(fibril_mutex_is_locked(ep->guard));
     140
     141        ep->online = false;
     142        fibril_condvar_broadcast(&ep->avail);
     143}
     144
     145/**
     146 * Wait until a transfer finishes. Can be used even when the endpoint is
     147 * offline (and is interrupted by the endpoint going offline).
    125148 */
    126149void endpoint_wait_timeout_locked(endpoint_t *ep, suseconds_t timeout)
    127150{
    128         assert(fibril_mutex_is_locked(&ep->guard));
    129 
    130         if (ep->active_batch != NULL)
    131                 fibril_condvar_wait_timeout(&ep->avail, &ep->guard, timeout);
    132 
    133         while (timeout == 0 && ep->active_batch != NULL)
    134                 fibril_condvar_wait_timeout(&ep->avail, &ep->guard, timeout);
     151        assert(ep);
     152        assert(fibril_mutex_is_locked(ep->guard));
     153
     154        if (ep->active_batch == NULL)
     155                return;
     156
     157        fibril_condvar_wait_timeout(&ep->avail, ep->guard, timeout);
    135158}
    136159
     
    140163 *
    141164 * Call only under endpoint guard. After you activate the endpoint and release
    142  * the guard, you must assume that particular transfer is already finished/aborted.
    143  *
    144  * @param ep endpoint_t structure.
    145  * @param batch Transfer batch this endpoint is bocked by.
    146  */
    147 void endpoint_activate_locked(endpoint_t *ep, usb_transfer_batch_t *batch)
     165 * the guard, you must assume that particular transfer is already
     166 * finished/aborted.
     167 *
     168 * Activation and deactivation is not done by the library to maximize
     169 * performance. The HC might want to prepare some memory buffers prior to
     170 * interfering with other world.
     171 *
     172 * @param batch Transfer batch this endpoint is blocked by.
     173 */
     174int endpoint_activate_locked(endpoint_t *ep, usb_transfer_batch_t *batch)
    148175{
    149176        assert(ep);
    150177        assert(batch);
    151178        assert(batch->ep == ep);
    152 
    153         endpoint_wait_timeout_locked(ep, 0);
     179        assert(ep->guard);
     180        assert(fibril_mutex_is_locked(ep->guard));
     181
     182        while (ep->online && ep->active_batch != NULL)
     183                fibril_condvar_wait(&ep->avail, ep->guard);
     184
     185        if (!ep->online)
     186                return EINTR;
     187
     188        assert(ep->active_batch == NULL);
    154189        ep->active_batch = batch;
     190        return EOK;
    155191}
    156192
    157193/**
    158194 * Mark the endpoint as inactive and allow access for further fibrils.
    159  *
    160  * @param ep endpoint_t structure.
    161195 */
    162196void endpoint_deactivate_locked(endpoint_t *ep)
    163197{
    164198        assert(ep);
    165         assert(fibril_mutex_is_locked(&ep->guard));
     199        assert(fibril_mutex_is_locked(ep->guard));
     200
    166201        ep->active_batch = NULL;
    167202        fibril_condvar_signal(&ep->avail);
Note: See TracChangeset for help on using the changeset viewer.