Commit 23aec600 authored by Anthony Liguori's avatar Anthony Liguori
Browse files

Merge remote-tracking branch 'kraxel/usb.61' into staging

* kraxel/usb.61:
  uas: move transfer kickoff
  ehci: Fix interrupt endpoints no longer working
  ehci: handle TD deactivation of inflight packets
  ehci: add ehci_cancel_queue()
  ehci: simplify ehci_state_executing
  ehci: Remove unnecessary ehci_flush_qh call
  ehci: Schedule async-bh when IAAD bit gets set
  ehci: Fix NULL ptr deref when unplugging an USB dev with an iso stream active
  usb: unique packet ids
  usb: Halt ep queue en cancel pending packets on a packet error
  fix info qtree indention
parents cdedd9d8 347e40ff
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -543,7 +543,7 @@ static void qdev_print(Monitor *mon, DeviceState *dev, int indent)
        qdev_print_props(mon, dev, DEVICE_CLASS(class)->props, indent);
        class = object_class_get_parent(class);
    } while (class != object_class_by_name(TYPE_DEVICE));
    bus_print_dev(dev->parent_bus, mon, dev, indent + 2);
    bus_print_dev(dev->parent_bus, mon, dev, indent);
    QLIST_FOREACH(child, &dev->child_bus, sibling) {
        qbus_print(mon, child, indent);
    }
+3 −1
Original line number Diff line number Diff line
@@ -179,6 +179,7 @@ struct USBEndpoint {
    uint8_t ifnum;
    int max_packet_size;
    bool pipeline;
    bool halted;
    USBDevice *dev;
    QTAILQ_HEAD(, USBPacket) queue;
};
@@ -331,6 +332,7 @@ typedef enum USBPacketState {
struct USBPacket {
    /* Data fields for use by the driver.  */
    int pid;
    uint64_t id;
    USBEndpoint *ep;
    QEMUIOVector iov;
    uint64_t parameter; /* control transfers */
@@ -343,7 +345,7 @@ struct USBPacket {
void usb_packet_init(USBPacket *p);
void usb_packet_set_state(USBPacket *p, USBPacketState state);
void usb_packet_check_state(USBPacket *p, USBPacketState expected);
void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep);
void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep, uint64_t id);
void usb_packet_addbuf(USBPacket *p, void *ptr, size_t len);
int usb_packet_map(USBPacket *p, QEMUSGList *sgl);
void usb_packet_unmap(USBPacket *p, QEMUSGList *sgl);
+30 −8
Original line number Diff line number Diff line
@@ -382,12 +382,23 @@ int usb_handle_packet(USBDevice *dev, USBPacket *p)
    usb_packet_check_state(p, USB_PACKET_SETUP);
    assert(p->ep != NULL);

    /* Submitting a new packet clears halt */
    if (p->ep->halted) {
        assert(QTAILQ_EMPTY(&p->ep->queue));
        p->ep->halted = false;
    }

    if (QTAILQ_EMPTY(&p->ep->queue) || p->ep->pipeline) {
        ret = usb_process_one(p);
        if (ret == USB_RET_ASYNC) {
            usb_packet_set_state(p, USB_PACKET_ASYNC);
            QTAILQ_INSERT_TAIL(&p->ep->queue, p, queue);
        } else {
            /*
             * When pipelining is enabled usb-devices must always return async,
             * otherwise packets can complete out of order!
             */
            assert(!p->ep->pipeline);
            p->result = ret;
            usb_packet_set_state(p, USB_PACKET_COMPLETE);
        }
@@ -399,6 +410,20 @@ int usb_handle_packet(USBDevice *dev, USBPacket *p)
    return ret;
}

static void __usb_packet_complete(USBDevice *dev, USBPacket *p)
{
    USBEndpoint *ep = p->ep;

    assert(p->result != USB_RET_ASYNC && p->result != USB_RET_NAK);

    if (p->result < 0) {
        ep->halted = true;
    }
    usb_packet_set_state(p, USB_PACKET_COMPLETE);
    QTAILQ_REMOVE(&ep->queue, p, queue);
    dev->port->ops->complete(dev->port, p);
}

/* Notify the controller that an async packet is complete.  This should only
   be called for packets previously deferred by returning USB_RET_ASYNC from
   handle_packet. */
@@ -409,11 +434,9 @@ void usb_packet_complete(USBDevice *dev, USBPacket *p)

    usb_packet_check_state(p, USB_PACKET_ASYNC);
    assert(QTAILQ_FIRST(&ep->queue) == p);
    usb_packet_set_state(p, USB_PACKET_COMPLETE);
    QTAILQ_REMOVE(&ep->queue, p, queue);
    dev->port->ops->complete(dev->port, p);
    __usb_packet_complete(dev, p);

    while (!QTAILQ_EMPTY(&ep->queue)) {
    while (!ep->halted && !QTAILQ_EMPTY(&ep->queue)) {
        p = QTAILQ_FIRST(&ep->queue);
        if (p->state == USB_PACKET_ASYNC) {
            break;
@@ -425,9 +448,7 @@ void usb_packet_complete(USBDevice *dev, USBPacket *p)
            break;
        }
        p->result = ret;
        usb_packet_set_state(p, USB_PACKET_COMPLETE);
        QTAILQ_REMOVE(&ep->queue, p, queue);
        dev->port->ops->complete(dev->port, p);
        __usb_packet_complete(ep->dev, p);
    }
}

@@ -499,10 +520,11 @@ void usb_packet_set_state(USBPacket *p, USBPacketState state)
    p->state = state;
}

void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep)
void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep, uint64_t id)
{
    assert(!usb_packet_is_inflight(p));
    assert(p->iov.iov != NULL);
    p->id = id;
    p->pid = pid;
    p->ep = ep;
    p->result = 0;
+1 −2
Original line number Diff line number Diff line
@@ -424,6 +424,7 @@ static void usb_uas_scsi_free_request(SCSIBus *bus, void *priv)
    }
    QTAILQ_REMOVE(&uas->requests, req, next);
    g_free(req);
    usb_uas_start_next_transfer(uas);
}

static UASRequest *usb_uas_find_request(UASDevice *uas, uint16_t tag)
@@ -456,7 +457,6 @@ static void usb_uas_scsi_command_complete(SCSIRequest *r,
                                          uint32_t status, size_t resid)
{
    UASRequest *req = r->hba_private;
    UASDevice *uas = req->uas;

    trace_usb_uas_scsi_complete(req->uas->dev.addr, req->tag, status, resid);
    req->complete = true;
@@ -465,7 +465,6 @@ static void usb_uas_scsi_command_complete(SCSIRequest *r,
    }
    usb_uas_queue_sense(req, status);
    scsi_req_unref(req->req);
    usb_uas_start_next_transfer(uas);
}

static void usb_uas_scsi_request_cancelled(SCSIRequest *r)
+80 −42
Original line number Diff line number Diff line
@@ -766,15 +766,27 @@ static EHCIQueue *ehci_alloc_queue(EHCIState *ehci, uint32_t addr, int async)
    return q;
}

static void ehci_free_queue(EHCIQueue *q)
static void ehci_cancel_queue(EHCIQueue *q)
{
    EHCIQueueHead *head = q->async ? &q->ehci->aqueues : &q->ehci->pqueues;
    EHCIPacket *p;

    trace_usb_ehci_queue_action(q, "free");
    while ((p = QTAILQ_FIRST(&q->packets)) != NULL) {
    p = QTAILQ_FIRST(&q->packets);
    if (p == NULL) {
        return;
    }

    trace_usb_ehci_queue_action(q, "cancel");
    do {
        ehci_free_packet(p);
    } while ((p = QTAILQ_FIRST(&q->packets)) != NULL);
}

static void ehci_free_queue(EHCIQueue *q)
{
    EHCIQueueHead *head = q->async ? &q->ehci->aqueues : &q->ehci->pqueues;

    trace_usb_ehci_queue_action(q, "free");
    ehci_cancel_queue(q);
    QTAILQ_REMOVE(head, q, next);
    g_free(q);
}
@@ -1194,6 +1206,15 @@ static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val)
            val &= ~USBCMD_FLS;
        }

        if (val & USBCMD_IAAD) {
            /*
             * Process IAAD immediately, otherwise the Linux IAAD watchdog may
             * trigger and re-use a qh without us seeing the unlink.
             */
            s->async_stepdown = 0;
            qemu_bh_schedule(s->async_bh);
        }

        if (((USBCMD_RUNSTOP | USBCMD_PSE | USBCMD_ASE) & val) !=
            ((USBCMD_RUNSTOP | USBCMD_PSE | USBCMD_ASE) & s->usbcmd)) {
            if (s->pstate == EST_INACTIVE) {
@@ -1530,7 +1551,7 @@ static int ehci_execute(EHCIPacket *p, const char *action)
    endp = get_field(p->queue->qh.epchar, QH_EPCHAR_EP);
    ep = usb_ep_get(p->queue->dev, p->pid, endp);

    usb_packet_setup(&p->packet, p->pid, ep);
    usb_packet_setup(&p->packet, p->pid, ep, p->qtdaddr);
    usb_packet_map(&p->packet, &p->sgl);

    trace_usb_ehci_packet_action(p->queue, p, action);
@@ -1552,7 +1573,8 @@ static int ehci_execute(EHCIPacket *p, const char *action)
 */

static int ehci_process_itd(EHCIState *ehci,
                            EHCIitd *itd)
                            EHCIitd *itd,
                            uint32_t addr)
{
    USBDevice *dev;
    USBEndpoint *ep;
@@ -1597,8 +1619,8 @@ static int ehci_process_itd(EHCIState *ehci,

            dev = ehci_find_device(ehci, devaddr);
            ep = usb_ep_get(dev, pid, endp);
            if (ep->type == USB_ENDPOINT_XFER_ISOC) {
                usb_packet_setup(&ehci->ipacket, pid, ep);
            if (ep && ep->type == USB_ENDPOINT_XFER_ISOC) {
                usb_packet_setup(&ehci->ipacket, pid, ep, addr);
                usb_packet_map(&ehci->ipacket, &ehci->isgl);
                ret = usb_handle_packet(dev, &ehci->ipacket);
                assert(ret != USB_RET_ASYNC);
@@ -1786,9 +1808,7 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async)
    if (q->dev != NULL && q->dev->addr != devaddr) {
        if (!QTAILQ_EMPTY(&q->packets)) {
            /* should not happen (guest bug) */
            while ((p = QTAILQ_FIRST(&q->packets)) != NULL) {
                ehci_free_packet(p);
            }
            ehci_cancel_queue(q);
        }
        q->dev = NULL;
    }
@@ -1796,11 +1816,6 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async)
        q->dev = ehci_find_device(q->ehci, devaddr);
    }

    if (p && p->async == EHCI_ASYNC_INFLIGHT) {
        /* I/O still in progress -- skip queue */
        ehci_set_state(ehci, async, EST_HORIZONTALQH);
        goto out;
    }
    if (p && p->async == EHCI_ASYNC_FINISHED) {
        /* I/O finished -- continue processing queue */
        trace_usb_ehci_packet_action(p->queue, p, "complete");
@@ -1862,7 +1877,7 @@ static int ehci_state_fetchitd(EHCIState *ehci, int async)
               sizeof(EHCIitd) >> 2);
    ehci_trace_itd(ehci, entry, &itd);

    if (ehci_process_itd(ehci, &itd) != 0) {
    if (ehci_process_itd(ehci, &itd, entry) != 0) {
        return -1;
    }

@@ -1949,29 +1964,50 @@ static int ehci_state_fetchqtd(EHCIQueue *q)
    ehci_trace_qtd(q, NLPTR_GET(q->qtdaddr), &qtd);

    p = QTAILQ_FIRST(&q->packets);
    while (p != NULL && p->qtdaddr != q->qtdaddr) {
        /* should not happen (guest bug) */
        ehci_free_packet(p);
        p = QTAILQ_FIRST(&q->packets);
    }
    if (p != NULL) {
        if (p->qtdaddr != q->qtdaddr ||
            (!NLPTR_TBIT(p->qtd.next) && (p->qtd.next != qtd.next)) ||
            (!NLPTR_TBIT(p->qtd.altnext) && (p->qtd.altnext != qtd.altnext)) ||
            p->qtd.bufptr[0] != qtd.bufptr[0]) {
            /* guest bug: guest updated active QH or qTD underneath us */
            ehci_cancel_queue(q);
            p = NULL;
        } else {
            p->qtd = qtd;
            ehci_qh_do_overlay(q);
        ehci_flush_qh(q);
        if (p->async == EHCI_ASYNC_INFLIGHT) {
        }
    }

    if (!(qtd.token & QTD_TOKEN_ACTIVE)) {
        if (p != NULL) {
            /* transfer canceled by guest (clear active) */
            ehci_cancel_queue(q);
            p = NULL;
        }
        ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
        } else {
        again = 1;
    } else if (p != NULL) {
        switch (p->async) {
        case EHCI_ASYNC_NONE:
            /* Previously nacked packet (likely interrupt ep) */
           ehci_set_state(q->ehci, q->async, EST_EXECUTE);
           break;
        case EHCI_ASYNC_INFLIGHT:
            /* Unfinyshed async handled packet, go horizontal */
            ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
            break;
        case EHCI_ASYNC_FINISHED:
            /* Should never happen, as this case is caught by fetchqh */
            ehci_set_state(q->ehci, q->async, EST_EXECUTING);
            break;
        }
        again = 1;
    } else if (qtd.token & QTD_TOKEN_ACTIVE) {
    } else {
        p = ehci_alloc_packet(q);
        p->qtdaddr = q->qtdaddr;
        p->qtd = qtd;
        ehci_set_state(q->ehci, q->async, EST_EXECUTE);
        again = 1;
    } else {
        ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
        again = 1;
    }

    return again;
@@ -2075,19 +2111,11 @@ out:
static int ehci_state_executing(EHCIQueue *q)
{
    EHCIPacket *p = QTAILQ_FIRST(&q->packets);
    int again = 0;

    assert(p != NULL);
    assert(p->qtdaddr == q->qtdaddr);

    ehci_execute_complete(q);
    if (p->usb_status == USB_RET_ASYNC) {
        goto out;
    }
    if (p->usb_status == USB_RET_PROCERR) {
        again = -1;
        goto out;
    }

    // 4.10.3
    if (!q->async) {
@@ -2105,11 +2133,8 @@ static int ehci_state_executing(EHCIQueue *q)
        ehci_set_state(q->ehci, q->async, EST_WRITEBACK);
    }

    again = 1;

out:
    ehci_flush_qh(q);
    return again;
    return 1;
}


@@ -2138,6 +2163,19 @@ static int ehci_state_writeback(EHCIQueue *q)
     * bit is clear.
     */
    if (q->qh.token & QTD_TOKEN_HALT) {
        /*
         * We should not do any further processing on a halted queue!
         * This is esp. important for bulk endpoints with pipelining enabled
         * (redirection to a real USB device), where we must cancel all the
         * transfers after this one so that:
         * 1) If they've completed already, they are not processed further
         *    causing more stalls, originating from the same failed transfer
         * 2) If still in flight, they are cancelled before the guest does
         *    a clear stall, otherwise the guest and device can loose sync!
         */
        while ((p = QTAILQ_FIRST(&q->packets)) != NULL) {
            ehci_free_packet(p);
        }
        ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
        again = 1;
    } else {
Loading