Changeset 4db49344 in mainline for uspace/drv/bus/usb/uhci


Ignore:
Timestamp:
2018-01-23T21:52:28Z (8 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/drv/bus/usb/uhci
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • uspace/drv/bus/usb/uhci/hc.c

    ra6afb4c r4db49344  
    9797
    9898static void hc_init_hw(const hc_t *instance);
    99 static int hc_init_mem_structures(hc_t *instance, hc_device_t *);
     99static int hc_init_mem_structures(hc_t *instance);
    100100static int hc_init_transfer_lists(hc_t *instance);
    101101
     
    164164        /* Lower 2 bits are transaction error and transaction complete */
    165165        if (status & (UHCI_STATUS_INTERRUPT | UHCI_STATUS_ERROR_INTERRUPT)) {
    166                 LIST_INITIALIZE(done);
    167                 transfer_list_remove_finished(
    168                     &instance->transfers_interrupt, &done);
    169                 transfer_list_remove_finished(
    170                     &instance->transfers_control_slow, &done);
    171                 transfer_list_remove_finished(
    172                     &instance->transfers_control_full, &done);
    173                 transfer_list_remove_finished(
    174                     &instance->transfers_bulk_full, &done);
    175 
    176                 list_foreach_safe(done, current, next) {
    177                         list_remove(current);
    178                         uhci_transfer_batch_t *batch =
    179                             uhci_transfer_batch_from_link(current);
    180                         usb_transfer_batch_finish(&batch->base);
    181                 }
    182         }
     166                transfer_list_check_finished(&instance->transfers_interrupt);
     167                transfer_list_check_finished(&instance->transfers_control_slow);
     168                transfer_list_check_finished(&instance->transfers_control_full);
     169                transfer_list_check_finished(&instance->transfers_bulk_full);
     170        }
     171
    183172        /* Resume interrupts are not supported */
    184173        if (status & UHCI_STATUS_RESUME) {
     
    239228            hw_res->io_ranges.ranges[0].size);
    240229
    241         ret = hc_init_mem_structures(instance, hcd);
     230        ret = hc_init_mem_structures(instance);
    242231        if (ret != EOK) {
    243232                usb_log_error("Failed to init UHCI memory structures: %s.",
     
    328317}
    329318
     319static int endpoint_register(endpoint_t *ep)
     320{
     321        hc_t * const hc = bus_to_hc(endpoint_get_bus(ep));
     322
     323        const int err = usb2_bus_ops.endpoint_register(ep);
     324        if (err)
     325                return err;
     326
     327        transfer_list_t *list = hc->transfers[ep->device->speed][ep->transfer_type];
     328        if (!list)
     329                /*
     330                 * We don't support this combination (e.g. isochronous). Do not
     331                 * fail early, because that would block any device with these
     332                 * endpoints from connecting. Instead, make sure these transfers
     333                 * are denied soon enough with ENOTSUP not to fail on asserts.
     334                 */
     335                return EOK;
     336
     337        endpoint_set_online(ep, &list->guard);
     338        return EOK;
     339}
     340
    330341static void endpoint_unregister(endpoint_t *ep)
    331342{
    332343        hc_t * const hc = bus_to_hc(endpoint_get_bus(ep));
    333344        usb2_bus_ops.endpoint_unregister(ep);
    334 
    335         uhci_transfer_batch_t *batch = NULL;
    336345
    337346        // Check for the roothub, as it does not schedule into lists
     
    344353
    345354        transfer_list_t *list = hc->transfers[ep->device->speed][ep->transfer_type];
    346 
    347355        if (!list)
    348356                /*
     
    352360                return;
    353361
    354         // To avoid ABBA deadlock, we need to take the list first
    355362        fibril_mutex_lock(&list->guard);
    356         fibril_mutex_lock(&ep->guard);
    357         if (ep->active_batch) {
    358                 batch = uhci_transfer_batch_get(ep->active_batch);
    359                 endpoint_deactivate_locked(ep);
    360                 transfer_list_remove_batch(list, batch);
    361         }
    362         fibril_mutex_unlock(&ep->guard);
     363
     364        endpoint_set_offline_locked(ep);
     365        /* From now on, no other transfer will be scheduled. */
     366
     367        if (!ep->active_batch) {
     368                fibril_mutex_unlock(&list->guard);
     369                return;
     370        }
     371
     372        /* First, offer the batch a short chance to be finished. */
     373        endpoint_wait_timeout_locked(ep, 10000);
     374
     375        if (!ep->active_batch) {
     376                fibril_mutex_unlock(&list->guard);
     377                return;
     378        }
     379
     380        uhci_transfer_batch_t * const batch =
     381                uhci_transfer_batch_get(ep->active_batch);
     382
     383        /* Remove the batch from the schedule to stop it from being finished. */
     384        endpoint_deactivate_locked(ep);
     385        transfer_list_remove_batch(list, batch);
     386
    363387        fibril_mutex_unlock(&list->guard);
    364388
    365         if (batch) {
    366                 // The HW could have been looking at the batch.
    367                 // Better wait two frames before we release the buffers.
    368                 async_usleep(2000);
    369                 batch->base.error = EINTR;
    370                 batch->base.transferred_size = 0;
    371                 usb_transfer_batch_finish(&batch->base);
    372         }
     389        /*
     390         * We removed the batch from software schedule only, it's still possible
     391         * that HC has it in its caches. Better wait a while before we release
     392         * the buffers.
     393         */
     394        async_usleep(20000);
     395        batch->base.error = EINTR;
     396        batch->base.transferred_size = 0;
     397        usb_transfer_batch_finish(&batch->base);
    373398}
    374399
     
    382407        .status = hc_status,
    383408
     409        .endpoint_register = endpoint_register,
    384410        .endpoint_unregister = endpoint_unregister,
    385411        .endpoint_count_bw = bandwidth_count_usb11,
     
    400426 *  - frame list page (needs to be one UHCI hw accessible 4K page)
    401427 */
    402 int hc_init_mem_structures(hc_t *instance, hc_device_t *hcd)
     428int hc_init_mem_structures(hc_t *instance)
    403429{
    404430        assert(instance);
     
    425451                return ENOMEM;
    426452        }
     453        list_initialize(&instance->pending_endpoints);
    427454        usb_log_debug("Initialized transfer lists.");
    428455
     
    514541}
    515542
    516 /** Schedule batch for execution.
     543/**
     544 * Schedule batch for execution.
    517545 *
    518546 * @param[in] instance UHCI structure to use.
    519547 * @param[in] batch Transfer batch to schedule.
    520548 * @return Error code
    521  *
    522  * Checks for bandwidth availability and appends the batch to the proper queue.
    523549 */
    524550static int hc_schedule(usb_transfer_batch_t *batch)
     
    531557                return uhci_rh_schedule(&hc->rh, batch);
    532558
    533 
    534         const int err = uhci_transfer_batch_prepare(uhci_batch);
    535         if (err)
     559        transfer_list_t * const list =
     560            hc->transfers[ep->device->speed][ep->transfer_type];
     561
     562        if (!list)
     563                return ENOTSUP;
     564
     565        int err;
     566        if ((err = uhci_transfer_batch_prepare(uhci_batch)))
    536567                return err;
    537568
    538         transfer_list_t *list = hc->transfers[ep->device->speed][ep->transfer_type];
    539         assert(list);
    540         transfer_list_add_batch(list, uhci_batch);
    541 
    542         return EOK;
    543 }
    544 
    545 int hc_unschedule_batch(usb_transfer_batch_t *batch)
    546 {
    547 
    548         return EOK;
     569        return transfer_list_add_batch(list, uhci_batch);
    549570}
    550571
  • uspace/drv/bus/usb/uhci/hc.h

    ra6afb4c r4db49344  
    123123        transfer_list_t *transfers[2][4];
    124124
     125        /**
     126         * Guard for the pending list. Can be locked under EP guard, but not
     127         * vice versa.
     128         */
     129        fibril_mutex_t guard;
     130        /** List of endpoints with a transfer scheduled */
     131        list_t pending_endpoints;
     132
    125133        /** Number of hw failures detected. */
    126134        unsigned hw_failures;
  • uspace/drv/bus/usb/uhci/transfer_list.c

    ra6afb4c r4db49344  
    102102}
    103103
    104 /** Add transfer batch to the list and queue.
    105  *
    106  * @param[in] instance List to use.
    107  * @param[in] batch Transfer batch to submit.
     104/**
     105 * Add transfer batch to the list and queue.
    108106 *
    109107 * The batch is added to the end of the list and queue.
    110  */
    111 void transfer_list_add_batch(
     108 *
     109 * @param[in] instance List to use.
     110 * @param[in] batch Transfer batch to submit. After return, the batch must
     111 *                  not be used further.
     112 */
     113int transfer_list_add_batch(
    112114    transfer_list_t *instance, uhci_transfer_batch_t *uhci_batch)
    113115{
     
    117119        endpoint_t *ep = uhci_batch->base.ep;
    118120
    119         /* First, wait until the endpoint is free to use */
    120         fibril_mutex_lock(&ep->guard);
    121         endpoint_activate_locked(ep, &uhci_batch->base);
    122         fibril_mutex_unlock(&ep->guard);
     121        fibril_mutex_lock(&instance->guard);
     122
     123        const int err = endpoint_activate_locked(ep, &uhci_batch->base);
     124        if (err) {
     125                fibril_mutex_unlock(&instance->guard);
     126                return err;
     127        }
    123128
    124129        usb_log_debug2("Batch %p adding to queue %s.",
    125130            uhci_batch, instance->name);
    126 
    127         fibril_mutex_lock(&instance->guard);
    128131
    129132        /* Assume there is nothing scheduled */
     
    155158            USB_TRANSFER_BATCH_ARGS(uhci_batch->base), instance->name);
    156159        fibril_mutex_unlock(&instance->guard);
     160        return EOK;
    157161}
    158162
     
    171175 * @param[in] done list to fill
    172176 */
    173 void transfer_list_remove_finished(transfer_list_t *instance, list_t *done)
    174 {
    175         assert(instance);
    176         assert(done);
     177void transfer_list_check_finished(transfer_list_t *instance)
     178{
     179        assert(instance);
    177180
    178181        fibril_mutex_lock(&instance->guard);
    179         link_t *current = list_first(&instance->batch_list);
    180         while (current && current != &instance->batch_list.head) {
    181                 link_t * const next = current->next;
    182                 uhci_transfer_batch_t *batch =
    183                     uhci_transfer_batch_from_link(current);
     182        list_foreach_safe(instance->batch_list, current, next) {
     183                uhci_transfer_batch_t *batch = uhci_transfer_batch_from_link(current);
    184184
    185185                if (uhci_transfer_batch_check_completed(batch)) {
    186                         /* Remove from schedule, save for processing */
    187                         fibril_mutex_lock(&batch->base.ep->guard);
    188186                        assert(batch->base.ep->active_batch == &batch->base);
     187                        endpoint_deactivate_locked(batch->base.ep);
    189188                        hc_reset_toggles(&batch->base, &uhci_reset_toggle);
    190                         endpoint_deactivate_locked(batch->base.ep);
    191189                        transfer_list_remove_batch(instance, batch);
    192                         fibril_mutex_unlock(&batch->base.ep->guard);
    193 
    194                         list_append(current, done);
     190                        usb_transfer_batch_finish(&batch->base);
    195191                }
    196                 current = next;
    197192        }
    198193        fibril_mutex_unlock(&instance->guard);
  • uspace/drv/bus/usb/uhci/transfer_list.h

    ra6afb4c r4db49344  
    5959int transfer_list_init(transfer_list_t *, const char *);
    6060void transfer_list_set_next(transfer_list_t *, transfer_list_t *);
    61 void transfer_list_add_batch(transfer_list_t *, uhci_transfer_batch_t *);
     61int transfer_list_add_batch(transfer_list_t *, uhci_transfer_batch_t *);
    6262void transfer_list_remove_batch(transfer_list_t *, uhci_transfer_batch_t *);
    63 void transfer_list_remove_finished(transfer_list_t *, list_t *);
     63void transfer_list_check_finished(transfer_list_t *);
    6464void transfer_list_abort_all(transfer_list_t *);
    6565
Note: See TracChangeset for help on using the changeset viewer.