Changeset 4db49344 in mainline for uspace/lib/usbhost


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.

Location:
uspace/lib/usbhost
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • uspace/lib/usbhost/include/usb/host/endpoint.h

    ra6afb4c r4db49344  
    5353typedef struct usb_transfer_batch usb_transfer_batch_t;
    5454
    55 /** Host controller side endpoint structure. */
     55/**
     56 * Host controller side endpoint structure.
     57 *
     58 * This structure, though reference-counted, is very fragile. It is responsible
     59 * for synchronizing transfer batch scheduling and completion.
     60 *
     61 * To avoid situations, in which two locks must be obtained to schedule/finish
     62 * a transfer, the endpoint inherits a lock from the outside. Because the
     63 * concrete instance of mutex can be unknown at the time of initialization,
     64 * the HC shall pass the right lock at the time of onlining the endpoint.
     65 *
     66 * The fields used for scheduling (online, active_batch) are to be used only
     67 * under that guard and by functions designed for this purpose. The driver can
     68 * also completely avoid using this mechanism, in which case it is on its own in
     69 * question of transfer aborting.
     70 *
     71 * Relevant information can be found in the documentation of HelenOS xHCI
     72 * project.
     73 */
    5674typedef struct endpoint {
    5775        /** USB device */
     
    5977        /** Reference count. */
    6078        atomic_t refcnt;
    61         /** Reserved bandwidth. */
    62         size_t bandwidth;
    63         /** The currently active transfer batch. Write using methods, read under guard. */
     79
     80        /** An inherited guard */
     81        fibril_mutex_t *guard;
     82        /** Whether it's allowed to schedule on this endpoint */
     83        bool online;
     84        /** The currently active transfer batch. */
    6485        usb_transfer_batch_t *active_batch;
    65         /** Protects resources and active status changes. */
    66         fibril_mutex_t guard;
    6786        /** Signals change of active status. */
    6887        fibril_condvar_t avail;
    6988
    70         /** Enpoint number */
     89        /** Reserved bandwidth. Needed for USB2 bus. */
     90        size_t bandwidth;
     91        /** Endpoint number */
    7192        usb_endpoint_t endpoint;
    7293        /** Communication direction. */
     
    79100        /** Maximum size of one transfer */
    80101        size_t max_transfer_size;
    81         /** Number of packats that can be sent in one service interval (not necessarily uframe) */
     102        /**
     103         * Number of packets that can be sent in one service interval
     104         * (not necessarily uframe, despite its name)
     105         */
    82106        unsigned packets_per_uframe;
    83107
     
    90114extern void endpoint_del_ref(endpoint_t *);
    91115
     116extern void endpoint_set_online(endpoint_t *, fibril_mutex_t *);
     117extern void endpoint_set_offline_locked(endpoint_t *);
     118
    92119extern void endpoint_wait_timeout_locked(endpoint_t *ep, suseconds_t);
    93 extern void endpoint_activate_locked(endpoint_t *, usb_transfer_batch_t *);
     120extern int endpoint_activate_locked(endpoint_t *, usb_transfer_batch_t *);
    94121extern void endpoint_deactivate_locked(endpoint_t *);
    95122
  • 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.