Changeset 4ed803f1 in mainline


Ignore:
Timestamp:
2018-01-11T17:35:02Z (6 years ago)
Author:
Salmelu <salmelu@…>
Branches:
lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
Children:
69a93d02
Parents:
929599a8
git-author:
Salmelu <salmelu@…> (2018-01-11 17:29:27)
git-committer:
Salmelu <salmelu@…> (2018-01-11 17:35:02)
Message:

xhci: Rewritten isoch event handling

Traversing loop looking for finished xhci transfer is now only 1 loop traversal in average case.
Added reset timer which resets the endpoint after there is no action for a given period.

Location:
uspace/drv/bus/usb/xhci
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • uspace/drv/bus/usb/xhci/isoch.c

    r929599a8 r4ed803f1  
    8484}
    8585
     86static void isoch_reset_no_timer(xhci_endpoint_t *ep)
     87{
     88        xhci_isoch_t * const isoch = ep->isoch;
     89        assert(fibril_mutex_is_locked(&isoch->guard));
     90        /*
     91         * As we cannot clear timer when we are triggered by it,
     92         * we have to avoid doing it in common method.
     93         */
     94        fibril_timer_clear_locked(isoch->reset_timer);
     95        isoch_reset(ep);
     96}
     97
     98static void isoch_reset_timer(void *ep) {
     99        xhci_isoch_t * const isoch = xhci_endpoint_get(ep)->isoch;
     100        fibril_mutex_lock(&isoch->guard);
     101        isoch_reset(ep);
     102        fibril_mutex_unlock(&isoch->guard);
     103}
     104
     105/*
     106 * Fast transfers could trigger the reset timer before the data is processed,
     107 * leading into false reset.
     108 */
     109#define RESET_TIMER_DELAY 100000
     110static void timer_schedule_reset(xhci_endpoint_t *ep) {
     111        xhci_isoch_t * const isoch = ep->isoch;
     112        const suseconds_t delay = isoch->buffer_count * ep->interval * 125 + RESET_TIMER_DELAY;
     113
     114        fibril_timer_clear_locked(isoch->reset_timer);
     115        fibril_timer_set_locked(isoch->reset_timer, delay,
     116                isoch_reset_timer, ep);
     117}
     118
    86119void isoch_fini(xhci_endpoint_t *ep)
    87120{
     
    92125                fibril_timer_clear(isoch->feeding_timer);
    93126                fibril_timer_destroy(isoch->feeding_timer);
     127                fibril_timer_clear(isoch->reset_timer);
     128                fibril_timer_destroy(isoch->reset_timer);
    94129        }
    95130
     
    109144
    110145        isoch->feeding_timer = fibril_timer_create(&isoch->guard);
     146        isoch->reset_timer = fibril_timer_create(&isoch->guard);
    111147        if (!isoch->feeding_timer)
    112148                return ENOMEM;
     
    115151        if(!isoch->transfers)
    116152                goto err;
    117  
     153
    118154        for (size_t i = 0; i < isoch->buffer_count; ++i) {
    119155                xhci_isoch_transfer_t *transfer = &isoch->transfers[i];
     
    124160
    125161        fibril_mutex_lock(&isoch->guard);
    126         isoch_reset(ep);
     162        isoch_reset_no_timer(ep);
    127163        fibril_mutex_unlock(&isoch->guard);
    128164
     
    226262         * to ensure correct scheduling of transfers, that are
    227263         * buffer_count * interval away from now.
    228          * Maximum interval is 8 seconds, which means we need a size of 
     264         * Maximum interval is 8 seconds, which means we need a size of
    229265         * 16 seconds. The size of MFIINDEX is 2 seconds only.
    230266         *
     
    301337                }
    302338        }
     339
    303340out:
    304 
    305341        if (fed) {
    306342                const uint8_t slot_id = xhci_device_get(ep->base.device)->slot_id;
    307343                const uint8_t target = xhci_endpoint_index(ep) + 1; /* EP Doorbells start at 1 */
    308344                hc_ring_doorbell(hc, slot_id, target);
     345                /* The ring may be dead. If no event happens until the delay, reset the endpoint. */
     346                timer_schedule_reset(ep);
    309347        }
    310348
     
    394432                const uint8_t target = xhci_endpoint_index(ep) + 1; /* EP Doorbells start at 1 */
    395433                hc_ring_doorbell(hc, slot_id, target);
     434                /* The ring may be dead. If no event happens until the delay, reset the endpoint. */
     435                timer_schedule_reset(ep);
    396436        }
    397437}
     
    541581                        /* For IN, the buffer has overfilled, we empty the buffers and readd TRBs */
    542582                        usb_log_warning("Ring over/underrun.");
    543                         isoch_reset(ep);
     583                        isoch_reset_no_timer(ep);
    544584                        fibril_condvar_broadcast(&ep->isoch->avail);
    545585                        fibril_mutex_unlock(&ep->isoch->guard);
     
    555595        }
    556596
    557         bool found_mine = false;
    558         bool found_incomplete = false;
    559 
    560597        /*
    561598         * The order of delivering events is not necessarily the one we would
    562          * expect. It is safer to walk the list of our 4 transfers and check
     599         * expect. It is safer to walk the list of our transfers and check
    563600         * which one it is.
     601         * To minimize the amount of transfers checked, we start at dequeue pointer
     602         * and exit the loop as soon as the transfer is found.
    564603         */
    565         for (size_t i = 0; i < isoch->buffer_count; ++i) {
    566                 xhci_isoch_transfer_t * const it = &isoch->transfers[i];
    567 
    568                 switch (it->state) {
    569                 case ISOCH_FILLED:
    570                         found_incomplete = true;
    571                         break;
    572 
    573                 case ISOCH_FED:
    574                         if (it->interrupt_trb_phys != trb->parameter) {
    575                                 found_incomplete = true;
    576                                 break;
    577                         }
    578 
     604        bool found_mine = false;
     605        for (size_t i = 0, di = isoch->dequeue; i < isoch->buffer_count; ++i, ++di) {
     606                /* Wrap it back to 0, don't use modulo every loop traversal */
     607                if (di == isoch->buffer_count) {
     608                        di = 0;
     609                }
     610
     611                xhci_isoch_transfer_t * const it = &isoch->transfers[di];
     612
     613                if (it->state == ISOCH_FED && it->interrupt_trb_phys == trb->parameter) {
    579614                        usb_log_debug2("[isoch] buffer %zu completed", it - isoch->transfers);
    580615                        it->state = ISOCH_COMPLETE;
     
    583618                        found_mine = true;
    584619                        break;
    585                 default:
    586                         break;
    587620                }
    588621        }
     
    595628         * It may happen that the driver already stopped reading (writing),
    596629         * and our buffers are filled (empty). As QEMU (and possibly others)
    597          * does not send RING_UNDERRUN (OVERRUN) event, detect it here.
     630         * does not send RING_UNDERRUN (OVERRUN) event, we set a timer to
     631         * reset it after the buffers should have been consumed. If there
     632         * is no issue, the timer will get restarted often enough.
    598633         */
    599         if (!found_incomplete) {
    600                 usb_log_warning("[isoch] Endpoint" XHCI_EP_FMT ": Detected "
    601                     "isochronous ring %s.", XHCI_EP_ARGS(*ep),
    602                     (ep->base.direction == USB_DIRECTION_IN) ? "underrun" : "overrun");
    603                 isoch_reset(ep);
    604         }
     634        timer_schedule_reset(ep);
    605635
    606636        fibril_condvar_broadcast(&ep->isoch->avail);
  • uspace/drv/bus/usb/xhci/isoch.h

    r929599a8 r4ed803f1  
    8181        fibril_timer_t *feeding_timer;
    8282
     83        /** Resets endpoint if there is no traffic. */
     84        fibril_timer_t *reset_timer;
     85
    8386        /** The maximum size of an isochronous transfer and therefore the size of buffers */
    8487        size_t max_size;
Note: See TracChangeset for help on using the changeset viewer.