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


Ignore:
Timestamp:
2018-01-23T21:52:28Z (6 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/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
Note: See TracChangeset for help on using the changeset viewer.