* [Qemu-devel] usb: input-pipelining + speedups v3
@ 2012-10-24 16:13 Hans de Goede
2012-10-24 16:13 ` [Qemu-devel] [PATCH 01/22] uhci: Properly unmap packets on cancel / invalid pid Hans de Goede
` (22 more replies)
0 siblings, 23 replies; 28+ messages in thread
From: Hans de Goede @ 2012-10-24 16:13 UTC (permalink / raw)
To: Gerd Hoffmann; +Cc: qemu-devel
This is the final version of the input pipelining patchset, please
add these patches to your tree for Anthony.
Changes since the v2 rfc:
- Move the settnig of the int_req flag for xhci, as you requested
- Add "ehci: Retry to fill the queue while waiting for td completion" patch
- Lots of tests run with the code without issues :)
Thanks & Regards,
Hans
^ permalink raw reply [flat|nested] 28+ messages in thread
* [Qemu-devel] [PATCH 01/22] uhci: Properly unmap packets on cancel / invalid pid
2012-10-24 16:13 [Qemu-devel] usb: input-pipelining + speedups v3 Hans de Goede
@ 2012-10-24 16:13 ` Hans de Goede
2012-10-24 16:13 ` [Qemu-devel] [PATCH 02/22] uhci: Move checks to continue queuing to uhci_fill_queue() Hans de Goede
` (21 subsequent siblings)
22 siblings, 0 replies; 28+ messages in thread
From: Hans de Goede @ 2012-10-24 16:13 UTC (permalink / raw)
To: Gerd Hoffmann; +Cc: Hans de Goede, qemu-devel
Packets with an invalid pid, or which were cancelled have
usb_packet_map() called on them on init, but not usb_packet_unmap()
before being freed.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
hw/usb/hcd-uhci.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c
index c2f08e3..671c712 100644
--- a/hw/usb/hcd-uhci.c
+++ b/hw/usb/hcd-uhci.c
@@ -236,6 +236,7 @@ static void uhci_async_cancel(UHCIAsync *async)
trace_usb_uhci_packet_cancel(async->queue->token, async->td, async->done);
if (!async->done)
usb_cancel_packet(&async->packet);
+ usb_packet_unmap(&async->packet, &async->sgl);
uhci_async_free(async);
}
@@ -887,6 +888,7 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td,
default:
/* invalid pid : frame interrupted */
+ usb_packet_unmap(&async->packet, &async->sgl);
uhci_async_free(async);
s->status |= UHCI_STS_HCPERR;
uhci_update_irq(s);
--
1.7.12.1
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [Qemu-devel] [PATCH 02/22] uhci: Move checks to continue queuing to uhci_fill_queue()
2012-10-24 16:13 [Qemu-devel] usb: input-pipelining + speedups v3 Hans de Goede
2012-10-24 16:13 ` [Qemu-devel] [PATCH 01/22] uhci: Properly unmap packets on cancel / invalid pid Hans de Goede
@ 2012-10-24 16:13 ` Hans de Goede
2012-10-24 16:13 ` [Qemu-devel] [PATCH 03/22] ehci: Get rid of packet tbytes field Hans de Goede
` (20 subsequent siblings)
22 siblings, 0 replies; 28+ messages in thread
From: Hans de Goede @ 2012-10-24 16:13 UTC (permalink / raw)
To: Gerd Hoffmann; +Cc: Hans de Goede, qemu-devel
Rather then having a special check to start queuing after the first packet,
and then another check for the other packets in uhci_fill_queue(), simply
check the previous packet beforehand in uhci_fill_queue()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
hw/usb/hcd-uhci.c | 10 +++-------
1 file changed, 3 insertions(+), 7 deletions(-)
diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c
index 671c712..600d095 100644
--- a/hw/usb/hcd-uhci.c
+++ b/hw/usb/hcd-uhci.c
@@ -991,7 +991,8 @@ static void uhci_fill_queue(UHCIState *s, UHCI_TD *td)
UHCI_TD ptd;
int ret;
- while (is_valid(plink)) {
+ ptd.ctrl = td->ctrl;
+ while (is_valid(plink) && !(ptd.ctrl & TD_CTRL_SPD)) {
pci_dma_read(&s->dev, plink & ~0xf, &ptd, sizeof(ptd));
le32_to_cpus(&ptd.link);
le32_to_cpus(&ptd.ctrl);
@@ -1010,9 +1011,6 @@ static void uhci_fill_queue(UHCIState *s, UHCI_TD *td)
}
assert(ret == TD_RESULT_ASYNC_START);
assert(int_mask == 0);
- if (ptd.ctrl & TD_CTRL_SPD) {
- break;
- }
plink = ptd.link;
}
}
@@ -1110,9 +1108,7 @@ static void uhci_process_frame(UHCIState *s)
case TD_RESULT_ASYNC_START:
trace_usb_uhci_td_async(curr_qh & ~0xf, link & ~0xf);
- if (is_valid(td.link) && !(td.ctrl & TD_CTRL_SPD)) {
- uhci_fill_queue(s, &td);
- }
+ uhci_fill_queue(s, &td);
link = curr_qh ? qh.link : td.link;
continue;
--
1.7.12.1
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [Qemu-devel] [PATCH 03/22] ehci: Get rid of packet tbytes field
2012-10-24 16:13 [Qemu-devel] usb: input-pipelining + speedups v3 Hans de Goede
2012-10-24 16:13 ` [Qemu-devel] [PATCH 01/22] uhci: Properly unmap packets on cancel / invalid pid Hans de Goede
2012-10-24 16:13 ` [Qemu-devel] [PATCH 02/22] uhci: Move checks to continue queuing to uhci_fill_queue() Hans de Goede
@ 2012-10-24 16:13 ` Hans de Goede
2012-10-24 16:14 ` [Qemu-devel] [PATCH 04/22] ehci: Set int flag on a short input packet Hans de Goede
` (19 subsequent siblings)
22 siblings, 0 replies; 28+ messages in thread
From: Hans de Goede @ 2012-10-24 16:13 UTC (permalink / raw)
To: Gerd Hoffmann; +Cc: Hans de Goede, qemu-devel
This field is used in some places to track the tbytes field of the token, but
in other places the field is used directly, use it directly everywhere for
consistency.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
hw/usb/hcd-ehci.c | 20 +++++++++-----------
1 file changed, 9 insertions(+), 11 deletions(-)
diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
index 3acd881a..6945992 100644
--- a/hw/usb/hcd-ehci.c
+++ b/hw/usb/hcd-ehci.c
@@ -362,7 +362,6 @@ struct EHCIPacket {
USBPacket packet;
QEMUSGList sgl;
int pid;
- uint32_t tbytes;
enum async_state async;
int usb_status;
};
@@ -1505,15 +1504,16 @@ static void ehci_execute_complete(EHCIQueue *q)
}
} else {
// TODO check 4.12 for splits
+ uint32_t tbytes = get_field(q->qh.token, QTD_TOKEN_TBYTES);
- if (p->tbytes && p->pid == USB_TOKEN_IN) {
- p->tbytes -= p->usb_status;
+ if (tbytes && p->pid == USB_TOKEN_IN) {
+ tbytes -= p->usb_status;
} else {
- p->tbytes = 0;
+ tbytes = 0;
}
- DPRINTF("updating tbytes to %d\n", p->tbytes);
- set_field(&q->qh.token, p->tbytes, QTD_TOKEN_TBYTES);
+ DPRINTF("updating tbytes to %d\n", tbytes);
+ set_field(&q->qh.token, tbytes, QTD_TOKEN_TBYTES);
}
ehci_finish_transfer(q, p->usb_status);
usb_packet_unmap(&p->packet, &p->sgl);
@@ -1544,8 +1544,7 @@ static int ehci_execute(EHCIPacket *p, const char *action)
return USB_RET_PROCERR;
}
- p->tbytes = (p->qtd.token & QTD_TOKEN_TBYTES_MASK) >> QTD_TOKEN_TBYTES_SH;
- if (p->tbytes > BUFF_SIZE) {
+ if (get_field(p->qtd.token, QTD_TOKEN_TBYTES) > BUFF_SIZE) {
ehci_trace_guest_bug(p->queue->ehci,
"guest requested more bytes than allowed");
return USB_RET_PROCERR;
@@ -1582,10 +1581,9 @@ static int ehci_execute(EHCIPacket *p, const char *action)
trace_usb_ehci_packet_action(p->queue, p, action);
ret = usb_handle_packet(p->queue->dev, &p->packet);
- DPRINTF("submit: qh %x next %x qtd %x pid %x len %zd "
- "(total %d) endp %x ret %d\n",
+ DPRINTF("submit: qh %x next %x qtd %x pid %x len %zd endp %x ret %d\n",
q->qhaddr, q->qh.next, q->qtdaddr, q->pid,
- q->packet.iov.size, q->tbytes, endp, ret);
+ q->packet.iov.size, endp, ret);
if (ret > BUFF_SIZE) {
fprintf(stderr, "ret from usb_handle_packet > BUFF_SIZE\n");
--
1.7.12.1
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [Qemu-devel] [PATCH 04/22] ehci: Set int flag on a short input packet
2012-10-24 16:13 [Qemu-devel] usb: input-pipelining + speedups v3 Hans de Goede
` (2 preceding siblings ...)
2012-10-24 16:13 ` [Qemu-devel] [PATCH 03/22] ehci: Get rid of packet tbytes field Hans de Goede
@ 2012-10-24 16:14 ` Hans de Goede
2012-10-24 16:14 ` [Qemu-devel] [PATCH 05/22] ehci: Improve latency of interrupt delivery and async schedule scanning Hans de Goede
` (18 subsequent siblings)
22 siblings, 0 replies; 28+ messages in thread
From: Hans de Goede @ 2012-10-24 16:14 UTC (permalink / raw)
To: Gerd Hoffmann; +Cc: Hans de Goede, qemu-devel
According to 4.15.1.2 an interrupt must be raised when a short packet
is received. If we don't do this it may take a significant time for
the guest to notice a short trasnfer has completed, since only the last td
will have its IOC flag set, and a short transfer may complete in an earlier
packet.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
hw/usb/hcd-ehci.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
index 6945992..d9d4918 100644
--- a/hw/usb/hcd-ehci.c
+++ b/hw/usb/hcd-ehci.c
@@ -1508,6 +1508,10 @@ static void ehci_execute_complete(EHCIQueue *q)
if (tbytes && p->pid == USB_TOKEN_IN) {
tbytes -= p->usb_status;
+ if (tbytes) {
+ /* 4.15.1.2 must raise int on a short input packet */
+ ehci_raise_irq(q->ehci, USBSTS_INT);
+ }
} else {
tbytes = 0;
}
--
1.7.12.1
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [Qemu-devel] [PATCH 05/22] ehci: Improve latency of interrupt delivery and async schedule scanning
2012-10-24 16:13 [Qemu-devel] usb: input-pipelining + speedups v3 Hans de Goede
` (3 preceding siblings ...)
2012-10-24 16:14 ` [Qemu-devel] [PATCH 04/22] ehci: Set int flag on a short input packet Hans de Goede
@ 2012-10-24 16:14 ` Hans de Goede
2012-10-24 16:14 ` [Qemu-devel] [PATCH 06/22] ehci: Speed up the timer of raising int from the async schedule Hans de Goede
` (17 subsequent siblings)
22 siblings, 0 replies; 28+ messages in thread
From: Hans de Goede @ 2012-10-24 16:14 UTC (permalink / raw)
To: Gerd Hoffmann; +Cc: Hans de Goede, qemu-devel
While doing various performance tests of reading from USB mass storage devices
I noticed the following::
1) When an async handled packet completes, we don't immediately report an
interrupt to the guest, instead we wait for the frame-timer to run and
report it from there
2) If 1) has been fixed and an async handled packet takes a while to complete,
then async_stepdown will become a high value, which means that there
will be a large latency before any new packets queued by the guest in
response to the interrupt get seen
1) was done deliberately as part of commit f0ad01f92:
http://www.kraxel.org/cgit/qemu/commit/?h=usb.57&id=f0ad01f92ca02eee7cadbfd225c5de753ebd5fce
Since setting the interrupt immediately on async packet completion was causing
issues with Linux guests, I believe this recently fixed Linux bug explains
why this is happening:
http://git.kernel.org/?p=linux/kernel/git/torvalds/linux.git;a=commitdiff;h=361aabf395e4a23cf554cf4ec0c0c6963b8beb01
Note that we can *not* count on this fix being present in all Linux guests!
I was hoping that the recently added support for Interrupt Threshold Control
would fix the issues with Linux guests, but adding a simple ehci_commit_irq()
call to ehci_async_bh() still caused problems with Linux guests.
The problem is, that when doing ehci_commit_irq() from ehci_async_bh(),
the "old" frindex value is used to calculate usbsts_frindex, and when
the frame-timer then runs possibly very shortly after ehci_async_bh(),
it increases the frame-timer, and thus any interrupts raised from that
frame-timer run, will also get reported to the guest immediately, rather
then being delayed to the next frame-timer run.
Luckily the solution for this is simple, this means that we need to
increase frindex before calling ehci_commit_irq() from ehci_async_bh(),
which in the end boils down to simple calling ehci_frame_timer() instead
of ehci_async_bh() from the bh.
This may seem like it causes a lot of extra work to be done, but this
is not true. Any work done from the frame-timer processing the periodic
schedule is work which then does not need to be done the next time the
frame timer runs, also the frame-timer will re-arm itself at (possibly)
a later time then it was armed for saving a vmexit at that time.
As an additional advantage moving to simply calling the frame-timer also
fixes 2) as the packet completion will set async_stepdown to 0, and the
re-arming of the timer with an async_stepdown of 0 ensures that any
newly queued up packets get seen in a reasonable amount of time.
This improves the speed (MB/s) of a Linux guest reading from a USB mass
storage device by a factor of 1.5 - 1.7 with input pipelining disabled,
and by a factor of 1.8 with input pipelining enabled.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
hw/usb/hcd-ehci.c | 10 ++--------
1 file changed, 2 insertions(+), 8 deletions(-)
diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
index d9d4918..bbfa441 100644
--- a/hw/usb/hcd-ehci.c
+++ b/hw/usb/hcd-ehci.c
@@ -1244,7 +1244,7 @@ static void ehci_opreg_write(void *ptr, target_phys_addr_t addr,
s->usbcmd = val; /* Set usbcmd for ehci_update_halt() */
ehci_update_halt(s);
s->async_stepdown = 0;
- qemu_mod_timer(s->frame_timer, qemu_get_clock_ns(vm_clock));
+ qemu_bh_schedule(s->async_bh);
}
break;
@@ -2509,12 +2509,6 @@ static void ehci_frame_timer(void *opaque)
}
}
-static void ehci_async_bh(void *opaque)
-{
- EHCIState *ehci = opaque;
- ehci_advance_async_state(ehci);
-}
-
static const MemoryRegionOps ehci_mmio_caps_ops = {
.read = ehci_caps_read,
.valid.min_access_size = 1,
@@ -2743,7 +2737,7 @@ static int usb_ehci_initfn(PCIDevice *dev)
}
s->frame_timer = qemu_new_timer_ns(vm_clock, ehci_frame_timer, s);
- s->async_bh = qemu_bh_new(ehci_async_bh, s);
+ s->async_bh = qemu_bh_new(ehci_frame_timer, s);
QTAILQ_INIT(&s->aqueues);
QTAILQ_INIT(&s->pqueues);
usb_packet_init(&s->ipacket);
--
1.7.12.1
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [Qemu-devel] [PATCH 06/22] ehci: Speed up the timer of raising int from the async schedule
2012-10-24 16:13 [Qemu-devel] usb: input-pipelining + speedups v3 Hans de Goede
` (4 preceding siblings ...)
2012-10-24 16:14 ` [Qemu-devel] [PATCH 05/22] ehci: Improve latency of interrupt delivery and async schedule scanning Hans de Goede
@ 2012-10-24 16:14 ` Hans de Goede
2012-10-24 16:14 ` [Qemu-devel] [PATCH 07/22] ehci: Detect going in circles when filling the queue Hans de Goede
` (16 subsequent siblings)
22 siblings, 0 replies; 28+ messages in thread
From: Hans de Goede @ 2012-10-24 16:14 UTC (permalink / raw)
To: Gerd Hoffmann; +Cc: Hans de Goede, qemu-devel
Often the guest will queue up new packets in response to a packet, in the
async schedule with its IOC flag set, completing. By speeding up the
frame-timer, we notice these new packets earlier. This increases the
speed (MB/s) of a Linux guest reading from a USB mass storage device by a
factor of 1.15 on top of the "Improve latency of interrupt delivery"
speed-ups, both with and without input pipelining enabled.
I've not tested the speed-up of this patch without the
"Improve latency of interrupt delivery" patch.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
hw/usb/hcd-ehci.c | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
index bbfa441..58e788b 100644
--- a/hw/usb/hcd-ehci.c
+++ b/hw/usb/hcd-ehci.c
@@ -443,6 +443,7 @@ struct EHCIState {
uint64_t last_run_ns;
uint32_t async_stepdown;
+ bool int_req_by_async;
};
#define SET_LAST_RUN_CLOCK(s) \
@@ -1529,6 +1530,9 @@ static void ehci_execute_complete(EHCIQueue *q)
if (q->qh.token & QTD_TOKEN_IOC) {
ehci_raise_irq(q->ehci, USBSTS_INT);
+ if (q->async) {
+ q->ehci->int_req_by_async = true;
+ }
}
}
@@ -2503,8 +2507,15 @@ static void ehci_frame_timer(void *opaque)
}
if (need_timer) {
- expire_time = t_now + (get_ticks_per_sec()
+ /* If we've raised int, we speed up the timer, so that we quickly
+ * notice any new packets queued up in response */
+ if (ehci->int_req_by_async && (ehci->usbsts & USBSTS_INT)) {
+ expire_time = t_now + get_ticks_per_sec() / (FRAME_TIMER_FREQ * 2);
+ ehci->int_req_by_async = false;
+ } else {
+ expire_time = t_now + (get_ticks_per_sec()
* (ehci->async_stepdown+1) / FRAME_TIMER_FREQ);
+ }
qemu_mod_timer(ehci->frame_timer, expire_time);
}
}
--
1.7.12.1
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [Qemu-devel] [PATCH 07/22] ehci: Detect going in circles when filling the queue
2012-10-24 16:13 [Qemu-devel] usb: input-pipelining + speedups v3 Hans de Goede
` (5 preceding siblings ...)
2012-10-24 16:14 ` [Qemu-devel] [PATCH 06/22] ehci: Speed up the timer of raising int from the async schedule Hans de Goede
@ 2012-10-24 16:14 ` Hans de Goede
2012-10-24 16:14 ` [Qemu-devel] [PATCH 08/22] ehci: Retry to fill the queue while waiting for td completion Hans de Goede
` (15 subsequent siblings)
22 siblings, 0 replies; 28+ messages in thread
From: Hans de Goede @ 2012-10-24 16:14 UTC (permalink / raw)
To: Gerd Hoffmann; +Cc: Hans de Goede, qemu-devel
For ctrl endpoints Windows (atleast Win7) creates circular td lists, so far
these were not a problem because we would stop filling the queue if altnext
was set. Since further patches in this patchset remove the altnext check this
does become a problem and we need detection for going in circles.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
hw/usb/hcd-ehci.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
index 58e788b..c906ab8 100644
--- a/hw/usb/hcd-ehci.c
+++ b/hw/usb/hcd-ehci.c
@@ -2072,7 +2072,7 @@ static int ehci_fill_queue(EHCIPacket *p)
{
EHCIQueue *q = p->queue;
EHCIqtd qtd = p->qtd;
- uint32_t qtdaddr;
+ uint32_t qtdaddr, start_addr = p->qtdaddr;
for (;;) {
if (NLPTR_TBIT(qtd.altnext) == 0) {
@@ -2082,6 +2082,13 @@ static int ehci_fill_queue(EHCIPacket *p)
break;
}
qtdaddr = qtd.next;
+ /*
+ * Detect circular td lists, Windows creates these, counting on the
+ * active bit going low after execution to make the queue stop.
+ */
+ if (qtdaddr == start_addr) {
+ break;
+ }
get_dwords(q->ehci, NLPTR_GET(qtdaddr),
(uint32_t *) &qtd, sizeof(EHCIqtd) >> 2);
ehci_trace_qtd(q, NLPTR_GET(qtdaddr), &qtd);
--
1.7.12.1
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [Qemu-devel] [PATCH 08/22] ehci: Retry to fill the queue while waiting for td completion
2012-10-24 16:13 [Qemu-devel] usb: input-pipelining + speedups v3 Hans de Goede
` (6 preceding siblings ...)
2012-10-24 16:14 ` [Qemu-devel] [PATCH 07/22] ehci: Detect going in circles when filling the queue Hans de Goede
@ 2012-10-24 16:14 ` Hans de Goede
2012-10-24 16:14 ` [Qemu-devel] [PATCH 09/22] xhci: Add a xhci_ep_nuke_one_xfer helper function Hans de Goede
` (14 subsequent siblings)
22 siblings, 0 replies; 28+ messages in thread
From: Hans de Goede @ 2012-10-24 16:14 UTC (permalink / raw)
To: Gerd Hoffmann; +Cc: Hans de Goede, qemu-devel
If the guest is using multiple transfers to try and keep the usb bus busy /
used at maximum efficiency, currently we would see / do the following:
1) submit transfer 1 to the device
2) submit transfer 2 to the device
3) report transfer 1 completion to guest
4) report transfer 2 completion to guest
5) submit transfer 1 to the device
6) report transfer 1 completion to guest
7) submit transfer 2 to the device
8) report transfer 2 completion to guest
etc.
So after the initial submission we would effectively only have 1 transfer
in flight, rather then 2. This is caused by us not checking the queue for
addition of new transfers by the guest (ie the resubmission of a recently
finished transfer), while waiting for a pending transfer to complete.
This patch does add a check for this, changing the sequence to:
1) submit transfer 1 to the device
2) submit transfer 2 to the device
3) report transfer 1 completion to guest
4) submit transfer 1 to the device
5) report transfer 2 completion to guest
6) submit transfer 2 to the device
etc.
Thus keeping 2 transfers in flight (most of the time, and always 1),
as intended by the guest.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
hw/usb/hcd-ehci.c | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
index c906ab8..ad52850 100644
--- a/hw/usb/hcd-ehci.c
+++ b/hw/usb/hcd-ehci.c
@@ -381,7 +381,7 @@ struct EHCIQueue {
uint32_t qhaddr; /* address QH read from */
uint32_t qtdaddr; /* address QTD read from */
USBDevice *dev;
- QTAILQ_HEAD(, EHCIPacket) packets;
+ QTAILQ_HEAD(pkts_head, EHCIPacket) packets;
};
typedef QTAILQ_HEAD(EHCIQueueHead, EHCIQueue) EHCIQueueHead;
@@ -488,6 +488,7 @@ static const char *ehci_mmio_names[] = {
static int ehci_state_executing(EHCIQueue *q);
static int ehci_state_writeback(EHCIQueue *q);
+static int ehci_fill_queue(EHCIPacket *p);
static const char *nr2str(const char **n, size_t len, uint32_t nr)
{
@@ -1993,7 +1994,7 @@ static int ehci_state_fetchqtd(EHCIQueue *q)
{
EHCIqtd qtd;
EHCIPacket *p;
- int again = 0;
+ int again = 1;
get_dwords(q->ehci, NLPTR_GET(q->qtdaddr), (uint32_t *) &qtd,
sizeof(EHCIqtd) >> 2);
@@ -2021,7 +2022,6 @@ static int ehci_state_fetchqtd(EHCIQueue *q)
p = NULL;
}
ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
- again = 1;
} else if (p != NULL) {
switch (p->async) {
case EHCI_ASYNC_NONE:
@@ -2030,6 +2030,9 @@ static int ehci_state_fetchqtd(EHCIQueue *q)
ehci_set_state(q->ehci, q->async, EST_EXECUTE);
break;
case EHCI_ASYNC_INFLIGHT:
+ /* Check if the guest has added new tds to the queue */
+ again = (ehci_fill_queue(QTAILQ_LAST(&q->packets, pkts_head)) ==
+ USB_RET_PROCERR) ? -1 : 1;
/* Unfinished async handled packet, go horizontal */
ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
break;
@@ -2041,13 +2044,11 @@ static int ehci_state_fetchqtd(EHCIQueue *q)
ehci_set_state(q->ehci, q->async, EST_EXECUTING);
break;
}
- again = 1;
} else {
p = ehci_alloc_packet(q);
p->qtdaddr = q->qtdaddr;
p->qtd = qtd;
ehci_set_state(q->ehci, q->async, EST_EXECUTE);
- again = 1;
}
return again;
--
1.7.12.1
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [Qemu-devel] [PATCH 09/22] xhci: Add a xhci_ep_nuke_one_xfer helper function
2012-10-24 16:13 [Qemu-devel] usb: input-pipelining + speedups v3 Hans de Goede
` (7 preceding siblings ...)
2012-10-24 16:14 ` [Qemu-devel] [PATCH 08/22] ehci: Retry to fill the queue while waiting for td completion Hans de Goede
@ 2012-10-24 16:14 ` Hans de Goede
2012-10-24 16:14 ` [Qemu-devel] [PATCH 10/22] usb: Rename __usb_packet_complete to usb_packet_complete_one Hans de Goede
` (13 subsequent siblings)
22 siblings, 0 replies; 28+ messages in thread
From: Hans de Goede @ 2012-10-24 16:14 UTC (permalink / raw)
To: Gerd Hoffmann; +Cc: Hans de Goede, qemu-devel
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
hw/usb/hcd-xhci.c | 49 ++++++++++++++++++++++++++++++-------------------
1 file changed, 30 insertions(+), 19 deletions(-)
diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
index e0ca690..5a2221f 100644
--- a/hw/usb/hcd-xhci.c
+++ b/hw/usb/hcd-xhci.c
@@ -1081,6 +1081,35 @@ static TRBCCode xhci_enable_ep(XHCIState *xhci, unsigned int slotid,
return CC_SUCCESS;
}
+static int xhci_ep_nuke_one_xfer(XHCITransfer *t)
+{
+ int killed = 0;
+
+ if (t->running_async) {
+ usb_cancel_packet(&t->packet);
+ t->running_async = 0;
+ t->cancelled = 1;
+ DPRINTF("xhci: cancelling transfer, waiting for it to complete\n");
+ killed = 1;
+ }
+ if (t->running_retry) {
+ XHCIEPContext *epctx = t->xhci->slots[t->slotid-1].eps[t->epid-1];
+ if (epctx) {
+ epctx->retry = NULL;
+ qemu_del_timer(epctx->kick_timer);
+ }
+ t->running_retry = 0;
+ }
+ if (t->trbs) {
+ g_free(t->trbs);
+ }
+
+ t->trbs = NULL;
+ t->trb_count = t->trb_alloced = 0;
+
+ return killed;
+}
+
static int xhci_ep_nuke_xfers(XHCIState *xhci, unsigned int slotid,
unsigned int epid)
{
@@ -1102,25 +1131,7 @@ static int xhci_ep_nuke_xfers(XHCIState *xhci, unsigned int slotid,
xferi = epctx->next_xfer;
for (i = 0; i < TD_QUEUE; i++) {
- XHCITransfer *t = &epctx->transfers[xferi];
- if (t->running_async) {
- usb_cancel_packet(&t->packet);
- t->running_async = 0;
- t->cancelled = 1;
- DPRINTF("xhci: cancelling transfer %d, waiting for it to complete...\n", i);
- killed++;
- }
- if (t->running_retry) {
- t->running_retry = 0;
- epctx->retry = NULL;
- qemu_del_timer(epctx->kick_timer);
- }
- if (t->trbs) {
- g_free(t->trbs);
- }
-
- t->trbs = NULL;
- t->trb_count = t->trb_alloced = 0;
+ killed += xhci_ep_nuke_one_xfer(&epctx->transfers[xferi]);
xferi = (xferi + 1) % TD_QUEUE;
}
return killed;
--
1.7.12.1
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [Qemu-devel] [PATCH 10/22] usb: Rename __usb_packet_complete to usb_packet_complete_one
2012-10-24 16:13 [Qemu-devel] usb: input-pipelining + speedups v3 Hans de Goede
` (8 preceding siblings ...)
2012-10-24 16:14 ` [Qemu-devel] [PATCH 09/22] xhci: Add a xhci_ep_nuke_one_xfer helper function Hans de Goede
@ 2012-10-24 16:14 ` Hans de Goede
2012-10-24 16:14 ` [Qemu-devel] [PATCH 11/22] usb: Add USB_RET_ADD_TO_QUEUE packet result code Hans de Goede
` (12 subsequent siblings)
22 siblings, 0 replies; 28+ messages in thread
From: Hans de Goede @ 2012-10-24 16:14 UTC (permalink / raw)
To: Gerd Hoffmann; +Cc: Hans de Goede, qemu-devel
And make it available for use outside of core.c
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
hw/usb.h | 1 +
hw/usb/core.c | 8 ++++----
2 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/hw/usb.h b/hw/usb.h
index 48c8926..01dd423 100644
--- a/hw/usb.h
+++ b/hw/usb.h
@@ -370,6 +370,7 @@ USBDevice *usb_find_device(USBPort *port, uint8_t addr);
int usb_handle_packet(USBDevice *dev, USBPacket *p);
void usb_packet_complete(USBDevice *dev, USBPacket *p);
+void usb_packet_complete_one(USBDevice *dev, USBPacket *p);
void usb_cancel_packet(USBPacket * p);
void usb_ep_init(USBDevice *dev);
diff --git a/hw/usb/core.c b/hw/usb/core.c
index b9f1f7a..e2e31ca 100644
--- a/hw/usb/core.c
+++ b/hw/usb/core.c
@@ -412,10 +412,11 @@ int usb_handle_packet(USBDevice *dev, USBPacket *p)
return ret;
}
-static void __usb_packet_complete(USBDevice *dev, USBPacket *p)
+void usb_packet_complete_one(USBDevice *dev, USBPacket *p)
{
USBEndpoint *ep = p->ep;
+ assert(QTAILQ_FIRST(&ep->queue) == p);
assert(p->result != USB_RET_ASYNC && p->result != USB_RET_NAK);
if (p->result < 0) {
@@ -435,8 +436,7 @@ void usb_packet_complete(USBDevice *dev, USBPacket *p)
int ret;
usb_packet_check_state(p, USB_PACKET_ASYNC);
- assert(QTAILQ_FIRST(&ep->queue) == p);
- __usb_packet_complete(dev, p);
+ usb_packet_complete_one(dev, p);
while (!ep->halted && !QTAILQ_EMPTY(&ep->queue)) {
p = QTAILQ_FIRST(&ep->queue);
@@ -450,7 +450,7 @@ void usb_packet_complete(USBDevice *dev, USBPacket *p)
break;
}
p->result = ret;
- __usb_packet_complete(ep->dev, p);
+ usb_packet_complete_one(ep->dev, p);
}
}
--
1.7.12.1
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [Qemu-devel] [PATCH 11/22] usb: Add USB_RET_ADD_TO_QUEUE packet result code
2012-10-24 16:13 [Qemu-devel] usb: input-pipelining + speedups v3 Hans de Goede
` (9 preceding siblings ...)
2012-10-24 16:14 ` [Qemu-devel] [PATCH 10/22] usb: Rename __usb_packet_complete to usb_packet_complete_one Hans de Goede
@ 2012-10-24 16:14 ` Hans de Goede
2012-10-24 16:14 ` [Qemu-devel] [PATCH 12/22] usb: Move clearing of queue on halt to the core Hans de Goede
` (11 subsequent siblings)
22 siblings, 0 replies; 28+ messages in thread
From: Hans de Goede @ 2012-10-24 16:14 UTC (permalink / raw)
To: Gerd Hoffmann; +Cc: Hans de Goede, qemu-devel
This can be used by usb-device code which wishes to process an entire endpoint
queue at once, to do this the usb-device code returns USB_RET_ADD_TO_QUEUE
from its handle_data class method and defines a flush_ep_queue class method
to call when the hcd is done queuing up packets.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
hw/usb.h | 21 +++++++++++++++------
hw/usb/bus.c | 8 ++++++++
hw/usb/core.c | 4 ++++
hw/usb/hcd-ehci.c | 4 ++++
hw/usb/hcd-musb.c | 1 +
hw/usb/hcd-ohci.c | 2 ++
hw/usb/hcd-uhci.c | 16 +++++++++++-----
hw/usb/hcd-xhci.c | 6 ++++++
8 files changed, 51 insertions(+), 11 deletions(-)
diff --git a/hw/usb.h b/hw/usb.h
index 01dd423..435cd42 100644
--- a/hw/usb.h
+++ b/hw/usb.h
@@ -38,12 +38,13 @@
#define USB_TOKEN_IN 0x69 /* device -> host */
#define USB_TOKEN_OUT 0xe1 /* host -> device */
-#define USB_RET_NODEV (-1)
-#define USB_RET_NAK (-2)
-#define USB_RET_STALL (-3)
-#define USB_RET_BABBLE (-4)
-#define USB_RET_IOERROR (-5)
-#define USB_RET_ASYNC (-6)
+#define USB_RET_NODEV (-1)
+#define USB_RET_NAK (-2)
+#define USB_RET_STALL (-3)
+#define USB_RET_BABBLE (-4)
+#define USB_RET_IOERROR (-5)
+#define USB_RET_ASYNC (-6)
+#define USB_RET_ADD_TO_QUEUE (-7)
#define USB_SPEED_LOW 0
#define USB_SPEED_FULL 1
@@ -293,6 +294,12 @@ typedef struct USBDeviceClass {
void (*set_interface)(USBDevice *dev, int interface,
int alt_old, int alt_new);
+ /*
+ * Called when the hcd is done queuing packets for an endpoint, only
+ * necessary for devices which can return USB_RET_ADD_TO_QUEUE.
+ */
+ void (*flush_ep_queue)(USBDevice *dev, USBEndpoint *ep);
+
const char *product_desc;
const USBDesc *usb_desc;
} USBDeviceClass;
@@ -507,6 +514,8 @@ int usb_device_handle_data(USBDevice *dev, USBPacket *p);
void usb_device_set_interface(USBDevice *dev, int interface,
int alt_old, int alt_new);
+void usb_device_flush_ep_queue(USBDevice *dev, USBEndpoint *ep);
+
const char *usb_device_get_product_desc(USBDevice *dev);
const USBDesc *usb_device_get_usb_desc(USBDevice *dev);
diff --git a/hw/usb/bus.c b/hw/usb/bus.c
index b649360..8066291 100644
--- a/hw/usb/bus.c
+++ b/hw/usb/bus.c
@@ -181,6 +181,14 @@ void usb_device_set_interface(USBDevice *dev, int interface,
}
}
+void usb_device_flush_ep_queue(USBDevice *dev, USBEndpoint *ep)
+{
+ USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
+ if (klass->flush_ep_queue) {
+ klass->flush_ep_queue(dev, ep);
+ }
+}
+
static int usb_qdev_init(DeviceState *qdev)
{
USBDevice *dev = USB_DEVICE(qdev);
diff --git a/hw/usb/core.c b/hw/usb/core.c
index e2e31ca..014e3ac 100644
--- a/hw/usb/core.c
+++ b/hw/usb/core.c
@@ -393,6 +393,10 @@ int usb_handle_packet(USBDevice *dev, USBPacket *p)
if (ret == USB_RET_ASYNC) {
usb_packet_set_state(p, USB_PACKET_ASYNC);
QTAILQ_INSERT_TAIL(&p->ep->queue, p, queue);
+ } else if (ret == USB_RET_ADD_TO_QUEUE) {
+ usb_packet_set_state(p, USB_PACKET_QUEUED);
+ QTAILQ_INSERT_TAIL(&p->ep->queue, p, queue);
+ ret = USB_RET_ASYNC;
} else {
/*
* When pipelining is enabled usb-devices must always return async,
diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
index ad52850..ae13057 100644
--- a/hw/usb/hcd-ehci.c
+++ b/hw/usb/hcd-ehci.c
@@ -2071,6 +2071,7 @@ static int ehci_state_horizqh(EHCIQueue *q)
static int ehci_fill_queue(EHCIPacket *p)
{
+ USBEndpoint *ep = p->packet.ep;
EHCIQueue *q = p->queue;
EHCIqtd qtd = p->qtd;
uint32_t qtdaddr, start_addr = p->qtdaddr;
@@ -2106,6 +2107,9 @@ static int ehci_fill_queue(EHCIPacket *p)
assert(p->usb_status == USB_RET_ASYNC);
p->async = EHCI_ASYNC_INFLIGHT;
}
+ if (p->usb_status != USB_RET_PROCERR) {
+ usb_device_flush_ep_queue(ep->dev, ep);
+ }
return p->usb_status;
}
diff --git a/hw/usb/hcd-musb.c b/hw/usb/hcd-musb.c
index 0bb5c7b..4896d38 100644
--- a/hw/usb/hcd-musb.c
+++ b/hw/usb/hcd-musb.c
@@ -635,6 +635,7 @@ static void musb_packet(MUSBState *s, MUSBEndPoint *ep,
ret = usb_handle_packet(dev, &ep->packey[dir].p);
if (ret == USB_RET_ASYNC) {
+ usb_device_flush_ep_queue(dev, uep);
ep->status[dir] = len;
return;
}
diff --git a/hw/usb/hcd-ohci.c b/hw/usb/hcd-ohci.c
index c36184a..a3207f0 100644
--- a/hw/usb/hcd-ohci.c
+++ b/hw/usb/hcd-ohci.c
@@ -816,6 +816,7 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, len);
ret = usb_handle_packet(dev, &ohci->usb_packet);
if (ret == USB_RET_ASYNC) {
+ usb_device_flush_ep_queue(dev, ep);
return 1;
}
}
@@ -1018,6 +1019,7 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
DPRINTF("ret=%d\n", ret);
#endif
if (ret == USB_RET_ASYNC) {
+ usb_device_flush_ep_queue(dev, ep);
ohci->async_td = addr;
return 1;
}
diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c
index 600d095..46e544b 100644
--- a/hw/usb/hcd-uhci.c
+++ b/hw/usb/hcd-uhci.c
@@ -818,7 +818,8 @@ out:
}
static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td,
- uint32_t *int_mask, bool queuing)
+ uint32_t *int_mask, bool queuing,
+ struct USBEndpoint **ep_ret)
{
UHCIAsync *async;
int len = 0, max_len;
@@ -870,6 +871,9 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td,
dev = uhci_find_device(s, (td->token >> 8) & 0x7f);
ep = usb_ep_get(dev, pid, (td->token >> 15) & 0xf);
+ if (ep_ret) {
+ *ep_ret = ep;
+ }
usb_packet_setup(&async->packet, pid, ep, addr);
qemu_sglist_add(&async->sgl, td->buffer, max_len);
usb_packet_map(&async->packet, &async->sgl);
@@ -983,7 +987,7 @@ static int qhdb_insert(QhDb *db, uint32_t addr)
return 0;
}
-static void uhci_fill_queue(UHCIState *s, UHCI_TD *td)
+static void uhci_fill_queue(UHCIState *s, UHCI_TD *td, struct USBEndpoint *ep)
{
uint32_t int_mask = 0;
uint32_t plink = td->link;
@@ -1005,7 +1009,7 @@ static void uhci_fill_queue(UHCIState *s, UHCI_TD *td)
break;
}
trace_usb_uhci_td_queue(plink & ~0xf, ptd.ctrl, ptd.token);
- ret = uhci_handle_td(s, plink, &ptd, &int_mask, true);
+ ret = uhci_handle_td(s, plink, &ptd, &int_mask, true, NULL);
if (ret == TD_RESULT_ASYNC_CONT) {
break;
}
@@ -1013,12 +1017,14 @@ static void uhci_fill_queue(UHCIState *s, UHCI_TD *td)
assert(int_mask == 0);
plink = ptd.link;
}
+ usb_device_flush_ep_queue(ep->dev, ep);
}
static void uhci_process_frame(UHCIState *s)
{
uint32_t frame_addr, link, old_td_ctrl, val, int_mask;
uint32_t curr_qh, td_count = 0;
+ struct USBEndpoint *curr_ep;
int cnt, ret;
UHCI_TD td;
UHCI_QH qh;
@@ -1089,7 +1095,7 @@ static void uhci_process_frame(UHCIState *s)
trace_usb_uhci_td_load(curr_qh & ~0xf, link & ~0xf, td.ctrl, td.token);
old_td_ctrl = td.ctrl;
- ret = uhci_handle_td(s, link, &td, &int_mask, false);
+ ret = uhci_handle_td(s, link, &td, &int_mask, false, &curr_ep);
if (old_td_ctrl != td.ctrl) {
/* update the status bits of the TD */
val = cpu_to_le32(td.ctrl);
@@ -1108,7 +1114,7 @@ static void uhci_process_frame(UHCIState *s)
case TD_RESULT_ASYNC_START:
trace_usb_uhci_td_async(curr_qh & ~0xf, link & ~0xf);
- uhci_fill_queue(s, &td);
+ uhci_fill_queue(s, &td, curr_ep);
link = curr_qh ? qh.link : td.link;
continue;
diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
index 5a2221f..96cdcd2 100644
--- a/hw/usb/hcd-xhci.c
+++ b/hw/usb/hcd-xhci.c
@@ -1661,6 +1661,7 @@ static int xhci_fire_transfer(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext
static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid)
{
XHCIEPContext *epctx;
+ USBEndpoint *ep = NULL;
uint64_t mfindex;
int length;
int i;
@@ -1754,12 +1755,14 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid
if (epid == 1) {
if (xhci_fire_ctl_transfer(xhci, xfer) >= 0) {
epctx->next_xfer = (epctx->next_xfer + 1) % TD_QUEUE;
+ ep = xfer->packet.ep;
} else {
fprintf(stderr, "xhci: error firing CTL transfer\n");
}
} else {
if (xhci_fire_transfer(xhci, xfer, epctx) >= 0) {
epctx->next_xfer = (epctx->next_xfer + 1) % TD_QUEUE;
+ ep = xfer->packet.ep;
} else {
if (!xfer->iso_xfer) {
fprintf(stderr, "xhci: error firing data transfer\n");
@@ -1776,6 +1779,9 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid
break;
}
}
+ if (ep) {
+ usb_device_flush_ep_queue(ep->dev, ep);
+ }
}
static TRBCCode xhci_enable_slot(XHCIState *xhci, unsigned int slotid)
--
1.7.12.1
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [Qemu-devel] [PATCH 12/22] usb: Move clearing of queue on halt to the core
2012-10-24 16:13 [Qemu-devel] usb: input-pipelining + speedups v3 Hans de Goede
` (10 preceding siblings ...)
2012-10-24 16:14 ` [Qemu-devel] [PATCH 11/22] usb: Add USB_RET_ADD_TO_QUEUE packet result code Hans de Goede
@ 2012-10-24 16:14 ` Hans de Goede
2012-10-24 16:14 ` [Qemu-devel] [PATCH 13/22] usb: Move short-not-ok handling " Hans de Goede
` (10 subsequent siblings)
22 siblings, 0 replies; 28+ messages in thread
From: Hans de Goede @ 2012-10-24 16:14 UTC (permalink / raw)
To: Gerd Hoffmann; +Cc: Hans de Goede, qemu-devel
hcds which queue up more then one packet at once (uhci, ehci and xhci),
must clear the queue after an error which has caused the queue to halt.
Currently this is handled as a special case inside the hcd code, this
patch instead adds an USB_RET_REMOVE_FROM_QUEUE packet result code, teaches
the 3 hcds about this and moves the clearing of the queue on a halt into
the USB core.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
hw/usb.h | 1 +
hw/usb/core.c | 8 +++++++-
hw/usb/hcd-ehci.c | 22 ++++++++--------------
hw/usb/hcd-uhci.c | 22 ++++++----------------
hw/usb/hcd-xhci.c | 4 ++++
5 files changed, 26 insertions(+), 31 deletions(-)
diff --git a/hw/usb.h b/hw/usb.h
index 435cd42..ead03c9 100644
--- a/hw/usb.h
+++ b/hw/usb.h
@@ -45,6 +45,7 @@
#define USB_RET_IOERROR (-5)
#define USB_RET_ASYNC (-6)
#define USB_RET_ADD_TO_QUEUE (-7)
+#define USB_RET_REMOVE_FROM_QUEUE (-8)
#define USB_SPEED_LOW 0
#define USB_SPEED_FULL 1
diff --git a/hw/usb/core.c b/hw/usb/core.c
index 014e3ac..5a97a0e 100644
--- a/hw/usb/core.c
+++ b/hw/usb/core.c
@@ -442,8 +442,14 @@ void usb_packet_complete(USBDevice *dev, USBPacket *p)
usb_packet_check_state(p, USB_PACKET_ASYNC);
usb_packet_complete_one(dev, p);
- while (!ep->halted && !QTAILQ_EMPTY(&ep->queue)) {
+ while (!QTAILQ_EMPTY(&ep->queue)) {
p = QTAILQ_FIRST(&ep->queue);
+ if (ep->halted) {
+ /* Empty the queue on a halt */
+ p->result = USB_RET_REMOVE_FROM_QUEUE;
+ dev->port->ops->complete(dev->port, p);
+ continue;
+ }
if (p->state == USB_PACKET_ASYNC) {
break;
}
diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
index ae13057..65e52e2 100644
--- a/hw/usb/hcd-ehci.c
+++ b/hw/usb/hcd-ehci.c
@@ -1457,8 +1457,15 @@ static void ehci_async_complete_packet(USBPort *port, USBPacket *packet)
}
p = container_of(packet, EHCIPacket, packet);
- trace_usb_ehci_packet_action(p->queue, p, "wakeup");
assert(p->async == EHCI_ASYNC_INFLIGHT);
+
+ if (packet->result == USB_RET_REMOVE_FROM_QUEUE) {
+ trace_usb_ehci_packet_action(p->queue, p, "remove");
+ ehci_free_packet(p);
+ return;
+ }
+
+ trace_usb_ehci_packet_action(p->queue, p, "wakeup");
p->async = EHCI_ASYNC_FINISHED;
p->usb_status = packet->result;
@@ -2215,19 +2222,6 @@ 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 {
diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c
index 46e544b..00dc9d5 100644
--- a/hw/usb/hcd-uhci.c
+++ b/hw/usb/hcd-uhci.c
@@ -744,22 +744,6 @@ static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_
return TD_RESULT_COMPLETE;
out:
- /*
- * We should not do any further processing on a queue with errors!
- * 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 (!QTAILQ_EMPTY(&async->queue->asyncs)) {
- UHCIAsync *as = QTAILQ_FIRST(&async->queue->asyncs);
- uhci_async_unlink(as);
- uhci_async_cancel(as);
- }
-
switch(ret) {
case USB_RET_STALL:
td->ctrl |= TD_CTRL_STALL;
@@ -918,6 +902,12 @@ static void uhci_async_complete(USBPort *port, USBPacket *packet)
UHCIAsync *async = container_of(packet, UHCIAsync, packet);
UHCIState *s = async->queue->uhci;
+ if (packet->result == USB_RET_REMOVE_FROM_QUEUE) {
+ uhci_async_unlink(async);
+ uhci_async_cancel(async);
+ return;
+ }
+
if (async->isoc) {
UHCI_TD td;
uint32_t link = async->td;
diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
index 96cdcd2..707c42b 100644
--- a/hw/usb/hcd-xhci.c
+++ b/hw/usb/hcd-xhci.c
@@ -2834,6 +2834,10 @@ static void xhci_complete(USBPort *port, USBPacket *packet)
{
XHCITransfer *xfer = container_of(packet, XHCITransfer, packet);
+ if (packet->result == USB_RET_REMOVE_FROM_QUEUE) {
+ xhci_ep_nuke_one_xfer(xfer);
+ return;
+ }
xhci_complete_packet(xfer, packet->result);
xhci_kick_ep(xfer->xhci, xfer->slotid, xfer->epid);
}
--
1.7.12.1
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [Qemu-devel] [PATCH 13/22] usb: Move short-not-ok handling to the core
2012-10-24 16:13 [Qemu-devel] usb: input-pipelining + speedups v3 Hans de Goede
` (11 preceding siblings ...)
2012-10-24 16:14 ` [Qemu-devel] [PATCH 12/22] usb: Move clearing of queue on halt to the core Hans de Goede
@ 2012-10-24 16:14 ` Hans de Goede
2012-10-24 16:14 ` [Qemu-devel] [PATCH 14/22] usb: Add an int_req flag to USBPacket Hans de Goede
` (9 subsequent siblings)
22 siblings, 0 replies; 28+ messages in thread
From: Hans de Goede @ 2012-10-24 16:14 UTC (permalink / raw)
To: Gerd Hoffmann; +Cc: Hans de Goede, qemu-devel
After a short-not-ok packet ending short, we should not advance the queue.
Move enforcing this to the core, rather then handling it in the hcd code.
This may result in the queue now actually containing multiple input packets
(which would not happen before), and this requires special handling in
combination with pipelining, so disable pipleining for input endpoints
(for now).
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
hw/usb.h | 4 +++-
hw/usb/core.c | 6 ++++--
hw/usb/hcd-ehci.c | 9 ++++-----
hw/usb/hcd-musb.c | 2 +-
hw/usb/hcd-ohci.c | 4 ++--
hw/usb/hcd-uhci.c | 7 ++++---
hw/usb/hcd-xhci.c | 2 +-
hw/usb/host-linux.c | 3 ++-
hw/usb/redirect.c | 18 ++++++++++++------
9 files changed, 33 insertions(+), 22 deletions(-)
diff --git a/hw/usb.h b/hw/usb.h
index ead03c9..1fcf79c 100644
--- a/hw/usb.h
+++ b/hw/usb.h
@@ -351,6 +351,7 @@ struct USBPacket {
USBEndpoint *ep;
QEMUIOVector iov;
uint64_t parameter; /* control transfers */
+ bool short_not_ok;
int result; /* transfer length or USB_RET_* status code */
/* Internal use by the USB layer. */
USBPacketState state;
@@ -360,7 +361,8 @@ 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, uint64_t id);
+void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep, uint64_t id,
+ bool short_not_ok);
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);
diff --git a/hw/usb/core.c b/hw/usb/core.c
index 5a97a0e..f4a5ad2 100644
--- a/hw/usb/core.c
+++ b/hw/usb/core.c
@@ -423,7 +423,7 @@ void usb_packet_complete_one(USBDevice *dev, USBPacket *p)
assert(QTAILQ_FIRST(&ep->queue) == p);
assert(p->result != USB_RET_ASYNC && p->result != USB_RET_NAK);
- if (p->result < 0) {
+ if (p->result < 0 || (p->short_not_ok && (p->result < p->iov.size))) {
ep->halted = true;
}
usb_packet_set_state(p, USB_PACKET_COMPLETE);
@@ -532,7 +532,8 @@ void usb_packet_set_state(USBPacket *p, USBPacketState state)
p->state = state;
}
-void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep, uint64_t id)
+void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep, uint64_t id,
+ bool short_not_ok)
{
assert(!usb_packet_is_inflight(p));
assert(p->iov.iov != NULL);
@@ -541,6 +542,7 @@ void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep, uint64_t id)
p->ep = ep;
p->result = 0;
p->parameter = 0;
+ p->short_not_ok = short_not_ok;
qemu_iovec_reset(&p->iov);
usb_packet_set_state(p, USB_PACKET_SETUP);
}
diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
index 65e52e2..2db933a 100644
--- a/hw/usb/hcd-ehci.c
+++ b/hw/usb/hcd-ehci.c
@@ -1551,6 +1551,7 @@ static int ehci_execute(EHCIPacket *p, const char *action)
USBEndpoint *ep;
int ret;
int endp;
+ bool spd;
assert(p->async == EHCI_ASYNC_NONE ||
p->async == EHCI_ASYNC_INITIALIZED);
@@ -1590,7 +1591,8 @@ static int ehci_execute(EHCIPacket *p, const char *action)
return USB_RET_PROCERR;
}
- usb_packet_setup(&p->packet, p->pid, ep, p->qtdaddr);
+ spd = (p->pid == USB_TOKEN_IN && NLPTR_TBIT(p->qtd.altnext) == 0);
+ usb_packet_setup(&p->packet, p->pid, ep, p->qtdaddr, spd);
usb_packet_map(&p->packet, &p->sgl);
p->async = EHCI_ASYNC_INITIALIZED;
}
@@ -1660,7 +1662,7 @@ static int ehci_process_itd(EHCIState *ehci,
dev = ehci_find_device(ehci, devaddr);
ep = usb_ep_get(dev, pid, endp);
if (ep && ep->type == USB_ENDPOINT_XFER_ISOC) {
- usb_packet_setup(&ehci->ipacket, pid, ep, addr);
+ usb_packet_setup(&ehci->ipacket, pid, ep, addr, false);
usb_packet_map(&ehci->ipacket, &ehci->isgl);
ret = usb_handle_packet(dev, &ehci->ipacket);
assert(ret != USB_RET_ASYNC);
@@ -2084,9 +2086,6 @@ static int ehci_fill_queue(EHCIPacket *p)
uint32_t qtdaddr, start_addr = p->qtdaddr;
for (;;) {
- if (NLPTR_TBIT(qtd.altnext) == 0) {
- break;
- }
if (NLPTR_TBIT(qtd.next) != 0) {
break;
}
diff --git a/hw/usb/hcd-musb.c b/hw/usb/hcd-musb.c
index 4896d38..f65fa3c 100644
--- a/hw/usb/hcd-musb.c
+++ b/hw/usb/hcd-musb.c
@@ -627,7 +627,7 @@ static void musb_packet(MUSBState *s, MUSBEndPoint *ep,
dev = usb_find_device(&s->port, ep->faddr[idx]);
uep = usb_ep_get(dev, pid, ep->type[idx] & 0xf);
usb_packet_setup(&ep->packey[dir].p, pid, uep,
- (dev->addr << 16) | (uep->nr << 8) | pid);
+ (dev->addr << 16) | (uep->nr << 8) | pid, false);
usb_packet_addbuf(&ep->packey[dir].p, ep->buf[idx], len);
ep->packey[dir].ep = ep;
ep->packey[dir].dir = dir;
diff --git a/hw/usb/hcd-ohci.c b/hw/usb/hcd-ohci.c
index a3207f0..8741d0f 100644
--- a/hw/usb/hcd-ohci.c
+++ b/hw/usb/hcd-ohci.c
@@ -812,7 +812,7 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
} else {
dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA));
ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN));
- usb_packet_setup(&ohci->usb_packet, pid, ep, addr);
+ usb_packet_setup(&ohci->usb_packet, pid, ep, addr, false);
usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, len);
ret = usb_handle_packet(dev, &ohci->usb_packet);
if (ret == USB_RET_ASYNC) {
@@ -1012,7 +1012,7 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
}
dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA));
ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN));
- usb_packet_setup(&ohci->usb_packet, pid, ep, addr);
+ usb_packet_setup(&ohci->usb_packet, pid, ep, addr, !flag_r);
usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, pktlen);
ret = usb_handle_packet(dev, &ohci->usb_packet);
#ifdef DEBUG_PACKET
diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c
index 00dc9d5..953897b 100644
--- a/hw/usb/hcd-uhci.c
+++ b/hw/usb/hcd-uhci.c
@@ -808,6 +808,7 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td,
UHCIAsync *async;
int len = 0, max_len;
uint8_t pid;
+ bool spd;
USBDevice *dev;
USBEndpoint *ep;
@@ -852,13 +853,14 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td,
max_len = ((td->token >> 21) + 1) & 0x7ff;
pid = td->token & 0xff;
+ spd = (pid == USB_TOKEN_IN && (td->ctrl & TD_CTRL_SPD) != 0);
dev = uhci_find_device(s, (td->token >> 8) & 0x7f);
ep = usb_ep_get(dev, pid, (td->token >> 15) & 0xf);
if (ep_ret) {
*ep_ret = ep;
}
- usb_packet_setup(&async->packet, pid, ep, addr);
+ usb_packet_setup(&async->packet, pid, ep, addr, spd);
qemu_sglist_add(&async->sgl, td->buffer, max_len);
usb_packet_map(&async->packet, &async->sgl);
@@ -985,8 +987,7 @@ static void uhci_fill_queue(UHCIState *s, UHCI_TD *td, struct USBEndpoint *ep)
UHCI_TD ptd;
int ret;
- ptd.ctrl = td->ctrl;
- while (is_valid(plink) && !(ptd.ctrl & TD_CTRL_SPD)) {
+ while (is_valid(plink)) {
pci_dma_read(&s->dev, plink & ~0xf, &ptd, sizeof(ptd));
le32_to_cpus(&ptd.link);
le32_to_cpus(&ptd.ctrl);
diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
index 707c42b..af633c8 100644
--- a/hw/usb/hcd-xhci.c
+++ b/hw/usb/hcd-xhci.c
@@ -1455,7 +1455,7 @@ static int xhci_setup_packet(XHCITransfer *xfer)
ep = usb_ep_get(dev, dir, xfer->epid >> 1);
}
- usb_packet_setup(&xfer->packet, dir, ep, xfer->trbs[0].addr);
+ usb_packet_setup(&xfer->packet, dir, ep, xfer->trbs[0].addr, false);
xhci_xfer_map(xfer);
DPRINTF("xhci: setup packet pid 0x%x addr %d ep %d\n",
xfer->packet.pid, dev->addr, ep->nr);
diff --git a/hw/usb/host-linux.c b/hw/usb/host-linux.c
index 44f1a64..3a258b4 100644
--- a/hw/usb/host-linux.c
+++ b/hw/usb/host-linux.c
@@ -1224,7 +1224,8 @@ static int usb_linux_update_endp_table(USBHostDevice *s)
usb_ep_set_type(&s->dev, pid, ep, type);
usb_ep_set_ifnum(&s->dev, pid, ep, interface);
if ((s->options & (1 << USB_HOST_OPT_PIPELINE)) &&
- (type == USB_ENDPOINT_XFER_BULK)) {
+ (type == USB_ENDPOINT_XFER_BULK) &&
+ (pid == USB_TOKEN_OUT)) {
usb_ep_set_pipeline(&s->dev, pid, ep, true);
}
diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c
index 022ba42..84b9705 100644
--- a/hw/usb/redirect.c
+++ b/hw/usb/redirect.c
@@ -1293,6 +1293,16 @@ static void usbredir_interface_info(void *priv,
}
}
+static void usbredir_set_pipeline(USBRedirDevice *dev, struct USBEndpoint *uep)
+{
+ if (uep->type != USB_ENDPOINT_XFER_BULK) {
+ return;
+ }
+ if (uep->pid == USB_TOKEN_OUT) {
+ uep->pipeline = true;
+ }
+}
+
static void usbredir_ep_info(void *priv,
struct usb_redir_ep_info_header *ep_info)
{
@@ -1334,9 +1344,7 @@ static void usbredir_ep_info(void *priv,
dev->endpoint[i].max_packet_size =
usb_ep->max_packet_size = ep_info->max_packet_size[i];
}
- if (ep_info->type[i] == usb_redir_type_bulk) {
- usb_ep->pipeline = true;
- }
+ usbredir_set_pipeline(dev, usb_ep);
}
}
@@ -1597,9 +1605,7 @@ static int usbredir_post_load(void *priv, int version_id)
usb_ep->type = dev->endpoint[i].type;
usb_ep->ifnum = dev->endpoint[i].interface;
usb_ep->max_packet_size = dev->endpoint[i].max_packet_size;
- if (dev->endpoint[i].type == usb_redir_type_bulk) {
- usb_ep->pipeline = true;
- }
+ usbredir_set_pipeline(dev, usb_ep);
}
return 0;
}
--
1.7.12.1
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [Qemu-devel] [PATCH 14/22] usb: Add an int_req flag to USBPacket
2012-10-24 16:13 [Qemu-devel] usb: input-pipelining + speedups v3 Hans de Goede
` (12 preceding siblings ...)
2012-10-24 16:14 ` [Qemu-devel] [PATCH 13/22] usb: Move short-not-ok handling " Hans de Goede
@ 2012-10-24 16:14 ` Hans de Goede
2012-10-24 16:14 ` [Qemu-devel] [PATCH 15/22] usb: Add packet combining functions Hans de Goede
` (8 subsequent siblings)
22 siblings, 0 replies; 28+ messages in thread
From: Hans de Goede @ 2012-10-24 16:14 UTC (permalink / raw)
To: Gerd Hoffmann; +Cc: Hans de Goede, qemu-devel
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
hw/usb.h | 3 ++-
hw/usb/core.c | 3 ++-
hw/usb/hcd-ehci.c | 6 ++++--
hw/usb/hcd-musb.c | 2 +-
hw/usb/hcd-ohci.c | 7 +++++--
hw/usb/hcd-uhci.c | 3 ++-
hw/usb/hcd-xhci.c | 16 +++++++++++-----
7 files changed, 27 insertions(+), 13 deletions(-)
diff --git a/hw/usb.h b/hw/usb.h
index 1fcf79c..3a6cc84 100644
--- a/hw/usb.h
+++ b/hw/usb.h
@@ -352,6 +352,7 @@ struct USBPacket {
QEMUIOVector iov;
uint64_t parameter; /* control transfers */
bool short_not_ok;
+ bool int_req;
int result; /* transfer length or USB_RET_* status code */
/* Internal use by the USB layer. */
USBPacketState state;
@@ -362,7 +363,7 @@ 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, uint64_t id,
- bool short_not_ok);
+ bool short_not_ok, bool int_req);
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);
diff --git a/hw/usb/core.c b/hw/usb/core.c
index f4a5ad2..87a513f 100644
--- a/hw/usb/core.c
+++ b/hw/usb/core.c
@@ -533,7 +533,7 @@ void usb_packet_set_state(USBPacket *p, USBPacketState state)
}
void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep, uint64_t id,
- bool short_not_ok)
+ bool short_not_ok, bool int_req)
{
assert(!usb_packet_is_inflight(p));
assert(p->iov.iov != NULL);
@@ -543,6 +543,7 @@ void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep, uint64_t id,
p->result = 0;
p->parameter = 0;
p->short_not_ok = short_not_ok;
+ p->int_req = int_req;
qemu_iovec_reset(&p->iov);
usb_packet_set_state(p, USB_PACKET_SETUP);
}
diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
index 2db933a..35b69c2 100644
--- a/hw/usb/hcd-ehci.c
+++ b/hw/usb/hcd-ehci.c
@@ -1592,7 +1592,8 @@ static int ehci_execute(EHCIPacket *p, const char *action)
}
spd = (p->pid == USB_TOKEN_IN && NLPTR_TBIT(p->qtd.altnext) == 0);
- usb_packet_setup(&p->packet, p->pid, ep, p->qtdaddr, spd);
+ usb_packet_setup(&p->packet, p->pid, ep, p->qtdaddr, spd,
+ (p->qtd.token & QTD_TOKEN_IOC) != 0);
usb_packet_map(&p->packet, &p->sgl);
p->async = EHCI_ASYNC_INITIALIZED;
}
@@ -1662,7 +1663,8 @@ static int ehci_process_itd(EHCIState *ehci,
dev = ehci_find_device(ehci, devaddr);
ep = usb_ep_get(dev, pid, endp);
if (ep && ep->type == USB_ENDPOINT_XFER_ISOC) {
- usb_packet_setup(&ehci->ipacket, pid, ep, addr, false);
+ usb_packet_setup(&ehci->ipacket, pid, ep, addr, false,
+ (itd->transact[i] & ITD_XACT_IOC) != 0);
usb_packet_map(&ehci->ipacket, &ehci->isgl);
ret = usb_handle_packet(dev, &ehci->ipacket);
assert(ret != USB_RET_ASYNC);
diff --git a/hw/usb/hcd-musb.c b/hw/usb/hcd-musb.c
index f65fa3c..a6105d3 100644
--- a/hw/usb/hcd-musb.c
+++ b/hw/usb/hcd-musb.c
@@ -627,7 +627,7 @@ static void musb_packet(MUSBState *s, MUSBEndPoint *ep,
dev = usb_find_device(&s->port, ep->faddr[idx]);
uep = usb_ep_get(dev, pid, ep->type[idx] & 0xf);
usb_packet_setup(&ep->packey[dir].p, pid, uep,
- (dev->addr << 16) | (uep->nr << 8) | pid, false);
+ (dev->addr << 16) | (uep->nr << 8) | pid, false, true);
usb_packet_addbuf(&ep->packey[dir].p, ep->buf[idx], len);
ep->packey[dir].ep = ep;
ep->packey[dir].dir = dir;
diff --git a/hw/usb/hcd-ohci.c b/hw/usb/hcd-ohci.c
index 8741d0f..4c77600 100644
--- a/hw/usb/hcd-ohci.c
+++ b/hw/usb/hcd-ohci.c
@@ -810,9 +810,11 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
if (completion) {
ret = ohci->usb_packet.result;
} else {
+ bool int_req = relative_frame_number == frame_count &&
+ OHCI_BM(iso_td.flags, TD_DI) == 0;
dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA));
ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN));
- usb_packet_setup(&ohci->usb_packet, pid, ep, addr, false);
+ usb_packet_setup(&ohci->usb_packet, pid, ep, addr, false, int_req);
usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, len);
ret = usb_handle_packet(dev, &ohci->usb_packet);
if (ret == USB_RET_ASYNC) {
@@ -1012,7 +1014,8 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
}
dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA));
ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN));
- usb_packet_setup(&ohci->usb_packet, pid, ep, addr, !flag_r);
+ usb_packet_setup(&ohci->usb_packet, pid, ep, addr, !flag_r,
+ OHCI_BM(td.flags, TD_DI) == 0);
usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, pktlen);
ret = usb_handle_packet(dev, &ohci->usb_packet);
#ifdef DEBUG_PACKET
diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c
index 953897b..4a1ea6b 100644
--- a/hw/usb/hcd-uhci.c
+++ b/hw/usb/hcd-uhci.c
@@ -860,7 +860,8 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td,
if (ep_ret) {
*ep_ret = ep;
}
- usb_packet_setup(&async->packet, pid, ep, addr, spd);
+ usb_packet_setup(&async->packet, pid, ep, addr, spd,
+ (td->ctrl & TD_CTRL_IOC) != 0);
qemu_sglist_add(&async->sgl, td->buffer, max_len);
usb_packet_map(&async->packet, &async->sgl);
diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
index af633c8..21c3386 100644
--- a/hw/usb/hcd-xhci.c
+++ b/hw/usb/hcd-xhci.c
@@ -320,6 +320,7 @@ typedef struct XHCITransfer {
bool running_retry;
bool cancelled;
bool complete;
+ bool int_req;
unsigned int iso_pkts;
unsigned int slotid;
unsigned int epid;
@@ -1291,18 +1292,22 @@ static TRBCCode xhci_set_ep_dequeue(XHCIState *xhci, unsigned int slotid,
return CC_SUCCESS;
}
-static int xhci_xfer_map(XHCITransfer *xfer)
+static int xhci_xfer_create_sgl(XHCITransfer *xfer, int in_xfer)
{
- int in_xfer = (xfer->packet.pid == USB_TOKEN_IN);
XHCIState *xhci = xfer->xhci;
int i;
+ xfer->int_req = false;
pci_dma_sglist_init(&xfer->sgl, &xhci->pci_dev, xfer->trb_count);
for (i = 0; i < xfer->trb_count; i++) {
XHCITRB *trb = &xfer->trbs[i];
dma_addr_t addr;
unsigned int chunk = 0;
+ if (trb->control & TRB_TR_IOC) {
+ xfer->int_req = true;
+ }
+
switch (TRB_TYPE(*trb)) {
case TR_DATA:
if ((!(trb->control & TRB_TR_DIR)) != (!in_xfer)) {
@@ -1327,7 +1332,6 @@ static int xhci_xfer_map(XHCITransfer *xfer)
}
}
- usb_packet_map(&xfer->packet, &xfer->sgl);
return 0;
err:
@@ -1455,8 +1459,10 @@ static int xhci_setup_packet(XHCITransfer *xfer)
ep = usb_ep_get(dev, dir, xfer->epid >> 1);
}
- usb_packet_setup(&xfer->packet, dir, ep, xfer->trbs[0].addr, false);
- xhci_xfer_map(xfer);
+ xhci_xfer_create_sgl(xfer, dir == USB_TOKEN_IN); /* Also sets int_req */
+ usb_packet_setup(&xfer->packet, dir, ep, xfer->trbs[0].addr, false,
+ xfer->int_req);
+ usb_packet_map(&xfer->packet, &xfer->sgl);
DPRINTF("xhci: setup packet pid 0x%x addr %d ep %d\n",
xfer->packet.pid, dev->addr, ep->nr);
return 0;
--
1.7.12.1
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [Qemu-devel] [PATCH 15/22] usb: Add packet combining functions
2012-10-24 16:13 [Qemu-devel] usb: input-pipelining + speedups v3 Hans de Goede
` (13 preceding siblings ...)
2012-10-24 16:14 ` [Qemu-devel] [PATCH 14/22] usb: Add an int_req flag to USBPacket Hans de Goede
@ 2012-10-24 16:14 ` Hans de Goede
2012-10-25 6:55 ` Gerd Hoffmann
2012-10-24 16:14 ` [Qemu-devel] [PATCH 16/22] combined-packet: Add a workaround for Linux usbfs + live migration Hans de Goede
` (7 subsequent siblings)
22 siblings, 1 reply; 28+ messages in thread
From: Hans de Goede @ 2012-10-24 16:14 UTC (permalink / raw)
To: Gerd Hoffmann; +Cc: Hans de Goede, qemu-devel
Currently we only do pipelining for output endpoints, since to properly
support short-not-ok semantics we can only have one outstanding input
packet. Since the ehci and uhci controllers have a limited per td packet
size guests will split large input transfers to into multiple packets,
and since we don't pipeline these, this comes with a serious performance
penalty.
This patch adds helper functions to (re-)combine packets which belong to 1
transfer at the guest device-driver level into 1 large transger. This can be
used by (redirection) usb-devices to enable pipelining for input endpoints.
This patch will combine packets together until a transfer terminating packet
is encountered. A terminating packet is a packet which meets one or more of
the following conditions:
1) The packet size is *not* a multiple of the endpoint max packet size
2) The packet does *not* have its short-not-ok flag set
3) The packet has its interrupt-on-complete flag set
The short-not-ok flag of the combined packet is that of the terminating packet.
Multiple combined packets may be submitted to the device, if the combined
packets do not have their short-not-ok flag set, enabling true pipelining.
If a combined packet does have its short-not-ok flag set the queue will
wait with submitting further packets to the device until that packet has
completed.
Once enabled in the usb-redir and ehci code, this improves the speed (MB/s)
of a Linux guest reading from a USB mass storage device by a factor of
1.2 - 1.5.
And the main reason why I started working on this, when reading from a pl2303
USB<->serial converter, it combines the previous 4 packets submitted per
device-driver level read into 1 big read, reducing the number of packets / sec
by a factor 4, and it allows to have multiple reads outstanding. This allows
for much better latency tolerance without the pl2303's internal buffer
overflowing (which was happening at 115200 bps, without serial flow control).
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
hw/usb.h | 21 ++++++
hw/usb/Makefile.objs | 2 +-
hw/usb/combined-packet.c | 183 +++++++++++++++++++++++++++++++++++++++++++++++
hw/usb/core.c | 1 +
4 files changed, 206 insertions(+), 1 deletion(-)
create mode 100644 hw/usb/combined-packet.c
diff --git a/hw/usb.h b/hw/usb.h
index 3a6cc84..3c8ba01 100644
--- a/hw/usb.h
+++ b/hw/usb.h
@@ -160,6 +160,7 @@ typedef struct USBBusOps USBBusOps;
typedef struct USBPort USBPort;
typedef struct USBDevice USBDevice;
typedef struct USBPacket USBPacket;
+typedef struct USBCombinedPacket USBCombinedPacket;
typedef struct USBEndpoint USBEndpoint;
typedef struct USBDesc USBDesc;
@@ -292,6 +293,14 @@ typedef struct USBDeviceClass {
*/
int (*handle_data)(USBDevice *dev, USBPacket *p);
+ /*
+ * Process / cancel combined packets, called from
+ * usb_ep_combine_input_packets() / usb_combined_packet_cancel().
+ * Only called for devices which call these functions themselves.
+ */
+ int (*handle_combined_data)(USBDevice *dev, USBPacket *p);
+ void (*cancel_combined_packet)(USBDevice *dev, USBPacket *p);
+
void (*set_interface)(USBDevice *dev, int interface,
int alt_old, int alt_new);
@@ -356,7 +365,15 @@ struct USBPacket {
int result; /* transfer length or USB_RET_* status code */
/* Internal use by the USB layer. */
USBPacketState state;
+ USBCombinedPacket *combined;
QTAILQ_ENTRY(USBPacket) queue;
+ QTAILQ_ENTRY(USBPacket) combined_entry;
+};
+
+struct USBCombinedPacket {
+ USBPacket *first;
+ QTAILQ_HEAD(packets_head, USBPacket) packets;
+ QEMUIOVector iov;
};
void usb_packet_init(USBPacket *p);
@@ -399,6 +416,10 @@ void usb_ep_set_pipeline(USBDevice *dev, int pid, int ep, bool enabled);
USBPacket *usb_ep_find_packet_by_id(USBDevice *dev, int pid, int ep,
uint64_t id);
+void usb_ep_combine_input_packets(USBEndpoint *ep);
+void usb_combined_input_packet_complete(USBDevice *dev, USBPacket *p);
+void usb_combined_packet_cancel(USBDevice *dev, USBPacket *p);
+
void usb_attach(USBPort *port);
void usb_detach(USBPort *port);
void usb_port_reset(USBPort *port);
diff --git a/hw/usb/Makefile.objs b/hw/usb/Makefile.objs
index 4225136..b193321 100644
--- a/hw/usb/Makefile.objs
+++ b/hw/usb/Makefile.objs
@@ -7,7 +7,7 @@ hw-obj-y += libhw.o
hw-obj-$(CONFIG_SMARTCARD) += dev-smartcard-reader.o
hw-obj-$(CONFIG_USB_REDIR) += redirect.o
-common-obj-y += core.o bus.o desc.o dev-hub.o
+common-obj-y += core.o combined-packet.o bus.o desc.o dev-hub.o
common-obj-y += host-$(HOST_USB).o dev-bluetooth.o
common-obj-y += dev-hid.o dev-storage.o dev-wacom.o
common-obj-y += dev-serial.o dev-network.o dev-audio.o
diff --git a/hw/usb/combined-packet.c b/hw/usb/combined-packet.c
new file mode 100644
index 0000000..5f92ef9
--- /dev/null
+++ b/hw/usb/combined-packet.c
@@ -0,0 +1,183 @@
+/*
+ * QEMU USB packet combining code (for input pipelining)
+ *
+ * Copyright(c) 2012 Red Hat, Inc.
+ *
+ * Red Hat Authors:
+ * Hans de Goede <hdegoede@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or(at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+#include "qemu-common.h"
+#include "hw/usb.h"
+#include "iov.h"
+#include "trace.h"
+
+static void usb_combined_packet_add(USBCombinedPacket *combined, USBPacket *p)
+{
+ qemu_iovec_concat(&combined->iov, &p->iov, 0, p->iov.size);
+ QTAILQ_INSERT_TAIL(&combined->packets, p, combined_entry);
+ p->combined = combined;
+}
+
+static void usb_combined_packet_remove(USBCombinedPacket *combined,
+ USBPacket *p)
+{
+ assert(p->combined == combined);
+ p->combined = NULL;
+ QTAILQ_REMOVE(&combined->packets, p, combined_entry);
+}
+
+/* Also handles completion of non combined packets for pipelined input eps */
+void usb_combined_input_packet_complete(USBDevice *dev, USBPacket *p)
+{
+ USBCombinedPacket *combined = p->combined;
+ USBEndpoint *ep = p->ep;
+ USBPacket *next;
+ enum { completing, complete, leftover };
+ int result, state = completing;
+ bool short_not_ok;
+
+ if (combined == NULL) {
+ usb_packet_complete_one(dev, p);
+ goto leave;
+ }
+
+ assert(combined->first == p && p == QTAILQ_FIRST(&combined->packets));
+
+ result = combined->first->result;
+ short_not_ok = QTAILQ_LAST(&combined->packets, packets_head)->short_not_ok;
+
+ QTAILQ_FOREACH_SAFE(p, &combined->packets, combined_entry, next) {
+ if (state == completing) {
+ /* Distribute data over uncombined packets */
+ if (result >= p->iov.size) {
+ p->result = p->iov.size;
+ } else {
+ /* Send short or error packet to complete the transfer */
+ p->result = result;
+ state = complete;
+ }
+ p->short_not_ok = short_not_ok;
+ usb_combined_packet_remove(combined, p);
+ usb_packet_complete_one(dev, p);
+ result -= p->result;
+ } else {
+ /* Remove any leftover packets from the queue */
+ state = leftover;
+ p->result = USB_RET_REMOVE_FROM_QUEUE;
+ dev->port->ops->complete(dev->port, p);
+ }
+ }
+ /*
+ * If we had leftover packets the hcd driver will have cancelled them
+ * and usb_combined_packet_cancel has already freed combined!
+ */
+ if (state != leftover) {
+ g_free(combined);
+ }
+leave:
+ /* Check if there are packets in the queue waiting for our completion */
+ usb_ep_combine_input_packets(ep);
+}
+
+/* May only be called for combined packets! */
+void usb_combined_packet_cancel(USBDevice *dev, USBPacket *p)
+{
+ USBCombinedPacket *combined = p->combined;
+ USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
+ assert(combined != NULL);
+ assert(klass->cancel_combined_packet != NULL);
+
+ usb_combined_packet_remove(combined, p);
+ if (p == combined->first) {
+ klass->cancel_combined_packet(dev, p);
+ }
+ if (QTAILQ_EMPTY(&combined->packets)) {
+ g_free(combined);
+ }
+}
+
+/*
+ * Large input transfers can get split into multiple input packets, this
+ * function recombines them, removing the short_not_ok checks which all but
+ * the last packet of such splits transfers have, thereby allowing input
+ * transfer pipelining (which we cannot do on short_not_ok transfers)
+ */
+void usb_ep_combine_input_packets(USBEndpoint *ep)
+{
+ USBPacket *p, *u, *next, *prev = NULL, *first = NULL;
+ USBPort *port = ep->dev->port;
+ USBDeviceClass *klass = USB_DEVICE_GET_CLASS(ep->dev);
+ int ret;
+
+ assert(ep->pipeline);
+ assert(ep->pid == USB_TOKEN_IN);
+ assert(klass->handle_combined_data != NULL);
+
+ QTAILQ_FOREACH_SAFE(p, &ep->queue, queue, next) {
+ /* Empty the queue on a halt */
+ if (ep->halted) {
+ p->result = USB_RET_REMOVE_FROM_QUEUE;
+ port->ops->complete(port, p);
+ continue;
+ }
+
+ /* Skip packets already submitted to the device */
+ if (p->state == USB_PACKET_ASYNC) {
+ prev = p;
+ continue;
+ }
+ usb_packet_check_state(p, USB_PACKET_QUEUED);
+
+ /*
+ * If the previous (combined) packet has the short_not_ok flag set
+ * stop, as we must not submit packets to the device after a transfer
+ * ending with short_not_ok packet.
+ */
+ if (prev && prev->short_not_ok) {
+ break;
+ }
+
+ if (first) {
+ if (first->combined == NULL) {
+ USBCombinedPacket *combined = g_new0(USBCombinedPacket, 1);
+
+ combined->first = first;
+ QTAILQ_INIT(&combined->packets);
+ qemu_iovec_init(&combined->iov, 2);
+ usb_combined_packet_add(combined, first);
+ }
+ usb_combined_packet_add(first->combined, p);
+ } else {
+ first = p;
+ }
+
+ /* Is this packet the last one of a (combined) transfer? */
+ if ((p->iov.size % ep->max_packet_size) != 0 || !p->short_not_ok ||
+ next == NULL) {
+ ret = klass->handle_combined_data(ep->dev, first);
+ assert(ret == USB_RET_ASYNC);
+ if (first->combined) {
+ QTAILQ_FOREACH(u, &first->combined->packets, combined_entry) {
+ usb_packet_set_state(u, USB_PACKET_ASYNC);
+ }
+ } else {
+ usb_packet_set_state(first, USB_PACKET_ASYNC);
+ }
+ first = NULL;
+ prev = p;
+ }
+ }
+}
diff --git a/hw/usb/core.c b/hw/usb/core.c
index 87a513f..99b9fdb 100644
--- a/hw/usb/core.c
+++ b/hw/usb/core.c
@@ -544,6 +544,7 @@ void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep, uint64_t id,
p->parameter = 0;
p->short_not_ok = short_not_ok;
p->int_req = int_req;
+ p->combined = NULL;
qemu_iovec_reset(&p->iov);
usb_packet_set_state(p, USB_PACKET_SETUP);
}
--
1.7.12.1
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [Qemu-devel] [PATCH 16/22] combined-packet: Add a workaround for Linux usbfs + live migration
2012-10-24 16:13 [Qemu-devel] usb: input-pipelining + speedups v3 Hans de Goede
` (14 preceding siblings ...)
2012-10-24 16:14 ` [Qemu-devel] [PATCH 15/22] usb: Add packet combining functions Hans de Goede
@ 2012-10-24 16:14 ` Hans de Goede
2012-10-24 16:14 ` [Qemu-devel] [PATCH 17/22] usb-redir: Add support for 32 bits bulk packet length Hans de Goede
` (6 subsequent siblings)
22 siblings, 0 replies; 28+ messages in thread
From: Hans de Goede @ 2012-10-24 16:14 UTC (permalink / raw)
To: Gerd Hoffmann; +Cc: Hans de Goede, qemu-devel
Older versions (anything but the latest) of Linux usbfs + libusb(x),
will submit larger (bulk) transfers split into multiple 16k submissions,
which means that rather then all tds getting linked into the queue in
one atomic operarion they get linked in a bunch at a time, which could
cause problems if:
1) We scan the queue while libusb is in the middle of submitting a split
bulk transfer
2) While this bulk transfer is pending we migrate to another host.
The problem is that after 2, the new host will rescan the queue and
combine the packets in one large transfer, where as 1) has caused the
original host to see them as 2 transfers. This patch fixes this by stopping
combinging if we detect a 16k transfer with its int_req flag set.
This should not adversely effect performance for other cases as:
1) Linux never sets the interrupt flag on packets other then the last
2) Windows does set the in_req flag on each td, but will submit large
transfers in 20k tds thus never triggering the check
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
hw/usb/combined-packet.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/hw/usb/combined-packet.c b/hw/usb/combined-packet.c
index 5f92ef9..ab23ade 100644
--- a/hw/usb/combined-packet.c
+++ b/hw/usb/combined-packet.c
@@ -120,7 +120,7 @@ void usb_ep_combine_input_packets(USBEndpoint *ep)
USBPacket *p, *u, *next, *prev = NULL, *first = NULL;
USBPort *port = ep->dev->port;
USBDeviceClass *klass = USB_DEVICE_GET_CLASS(ep->dev);
- int ret;
+ int ret, totalsize;
assert(ep->pipeline);
assert(ep->pid == USB_TOKEN_IN);
@@ -165,8 +165,11 @@ void usb_ep_combine_input_packets(USBEndpoint *ep)
}
/* Is this packet the last one of a (combined) transfer? */
+ totalsize = (p->combined) ? p->combined->iov.size : p->iov.size;
if ((p->iov.size % ep->max_packet_size) != 0 || !p->short_not_ok ||
- next == NULL) {
+ next == NULL ||
+ /* Work around for Linux usbfs bulk splitting + migration */
+ (totalsize == 16348 && p->int_req)) {
ret = klass->handle_combined_data(ep->dev, first);
assert(ret == USB_RET_ASYNC);
if (first->combined) {
--
1.7.12.1
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [Qemu-devel] [PATCH 17/22] usb-redir: Add support for 32 bits bulk packet length
2012-10-24 16:13 [Qemu-devel] usb: input-pipelining + speedups v3 Hans de Goede
` (15 preceding siblings ...)
2012-10-24 16:14 ` [Qemu-devel] [PATCH 16/22] combined-packet: Add a workaround for Linux usbfs + live migration Hans de Goede
@ 2012-10-24 16:14 ` Hans de Goede
2012-10-24 16:14 ` [Qemu-devel] [PATCH 18/22] usb-redir: Add support for input pipelining Hans de Goede
` (5 subsequent siblings)
22 siblings, 0 replies; 28+ messages in thread
From: Hans de Goede @ 2012-10-24 16:14 UTC (permalink / raw)
To: Gerd Hoffmann; +Cc: Hans de Goede, qemu-devel
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
configure | 2 +-
hw/usb/redirect.c | 7 ++++++-
2 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/configure b/configure
index 248b871..036c38a 100755
--- a/configure
+++ b/configure
@@ -2768,7 +2768,7 @@ fi
# check for usbredirparser for usb network redirection support
if test "$usb_redir" != "no" ; then
- if $pkg_config --atleast-version=0.5 libusbredirparser-0.5 >/dev/null 2>&1 ; then
+ if $pkg_config --atleast-version=0.5.3 libusbredirparser-0.5 >/dev/null 2>&1 ; then
usb_redir="yes"
usb_redir_cflags=$($pkg_config --cflags libusbredirparser-0.5 2>/dev/null)
usb_redir_libs=$($pkg_config --libs libusbredirparser-0.5 2>/dev/null)
diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c
index 84b9705..a24ff63 100644
--- a/hw/usb/redirect.c
+++ b/hw/usb/redirect.c
@@ -585,6 +585,10 @@ static int usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p,
bulk_packet.endpoint = ep;
bulk_packet.length = p->iov.size;
bulk_packet.stream_id = 0;
+ bulk_packet.length_high = p->iov.size >> 16;
+ assert(bulk_packet.length_high == 0 ||
+ usbredirparser_peer_has_cap(dev->parser,
+ usb_redir_cap_32bits_bulk_length));
if (ep & USB_DIR_IN) {
usbredirparser_send_bulk_packet(dev->parser, p->id,
@@ -906,6 +910,7 @@ static void usbredir_create_parser(USBRedirDevice *dev)
usbredirparser_caps_set_cap(caps, usb_redir_cap_filter);
usbredirparser_caps_set_cap(caps, usb_redir_cap_ep_info_max_packet_size);
usbredirparser_caps_set_cap(caps, usb_redir_cap_64bits_ids);
+ usbredirparser_caps_set_cap(caps, usb_redir_cap_32bits_bulk_length);
if (runstate_check(RUN_STATE_INMIGRATE)) {
flags |= usbredirparser_fl_no_hello;
@@ -1475,7 +1480,7 @@ static void usbredir_bulk_packet(void *priv, uint64_t id,
{
USBRedirDevice *dev = priv;
uint8_t ep = bulk_packet->endpoint;
- int len = bulk_packet->length;
+ int len = (bulk_packet->length_high << 16) | bulk_packet->length;
USBPacket *p;
DPRINTF("bulk-in status %d ep %02X len %d id %"PRIu64"\n",
--
1.7.12.1
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [Qemu-devel] [PATCH 18/22] usb-redir: Add support for input pipelining
2012-10-24 16:13 [Qemu-devel] usb: input-pipelining + speedups v3 Hans de Goede
` (16 preceding siblings ...)
2012-10-24 16:14 ` [Qemu-devel] [PATCH 17/22] usb-redir: Add support for 32 bits bulk packet length Hans de Goede
@ 2012-10-24 16:14 ` Hans de Goede
2012-10-24 16:14 ` [Qemu-devel] [PATCH 19/22] usb-redir: Add an usbredir_setup_usb_eps() helper function Hans de Goede
` (4 subsequent siblings)
22 siblings, 0 replies; 28+ messages in thread
From: Hans de Goede @ 2012-10-24 16:14 UTC (permalink / raw)
To: Gerd Hoffmann; +Cc: Hans de Goede, qemu-devel
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
hw/usb/redirect.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++---------
1 file changed, 57 insertions(+), 10 deletions(-)
diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c
index a24ff63..a6f9555 100644
--- a/hw/usb/redirect.c
+++ b/hw/usb/redirect.c
@@ -29,6 +29,7 @@
#include "qemu-timer.h"
#include "monitor.h"
#include "sysemu.h"
+#include "iov.h"
#include <dirent.h>
#include <sys/ioctl.h>
@@ -322,6 +323,11 @@ static void usbredir_cancel_packet(USBDevice *udev, USBPacket *p)
{
USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
+ if (p->combined) {
+ usb_combined_packet_cancel(udev, p);
+ return;
+ }
+
packet_id_queue_add(&dev->cancelled, p->id);
usbredirparser_send_cancel_data_packet(dev->parser, p->id);
usbredirparser_do_write(dev->parser);
@@ -575,17 +581,18 @@ static int usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p,
uint8_t ep)
{
struct usb_redir_bulk_packet_header bulk_packet;
+ size_t size = (p->combined) ? p->combined->iov.size : p->iov.size;
- DPRINTF("bulk-out ep %02X len %zd id %"PRIu64"\n", ep, p->iov.size, p->id);
+ DPRINTF("bulk-out ep %02X len %zd id %"PRIu64"\n", ep, size, p->id);
if (usbredir_already_in_flight(dev, p->id)) {
return USB_RET_ASYNC;
}
bulk_packet.endpoint = ep;
- bulk_packet.length = p->iov.size;
+ bulk_packet.length = size;
bulk_packet.stream_id = 0;
- bulk_packet.length_high = p->iov.size >> 16;
+ bulk_packet.length_high = size >> 16;
assert(bulk_packet.length_high == 0 ||
usbredirparser_peer_has_cap(dev->parser,
usb_redir_cap_32bits_bulk_length));
@@ -594,11 +601,16 @@ static int usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p,
usbredirparser_send_bulk_packet(dev->parser, p->id,
&bulk_packet, NULL, 0);
} else {
- uint8_t buf[p->iov.size];
- usb_packet_copy(p, buf, p->iov.size);
- usbredir_log_data(dev, "bulk data out:", buf, p->iov.size);
+ uint8_t buf[size];
+ if (p->combined) {
+ iov_to_buf(p->combined->iov.iov, p->combined->iov.niov,
+ 0, buf, size);
+ } else {
+ usb_packet_copy(p, buf, size);
+ }
+ usbredir_log_data(dev, "bulk data out:", buf, size);
usbredirparser_send_bulk_packet(dev->parser, p->id,
- &bulk_packet, buf, p->iov.size);
+ &bulk_packet, buf, size);
}
usbredirparser_do_write(dev->parser);
return USB_RET_ASYNC;
@@ -715,6 +727,9 @@ static int usbredir_handle_data(USBDevice *udev, USBPacket *p)
case USB_ENDPOINT_XFER_ISOC:
return usbredir_handle_iso_data(dev, p, ep);
case USB_ENDPOINT_XFER_BULK:
+ if (p->pid == USB_TOKEN_IN && p->ep->pipeline) {
+ return USB_RET_ADD_TO_QUEUE;
+ }
return usbredir_handle_bulk_data(dev, p, ep);
case USB_ENDPOINT_XFER_INT:
return usbredir_handle_interrupt_data(dev, p, ep);
@@ -725,6 +740,20 @@ static int usbredir_handle_data(USBDevice *udev, USBPacket *p)
}
}
+static void usbredir_flush_ep_queue(USBDevice *dev, USBEndpoint *ep)
+{
+ if (ep->pid == USB_TOKEN_IN && ep->pipeline) {
+ usb_ep_combine_input_packets(ep);
+ }
+}
+
+static int usbredir_handle_combined_data(USBDevice *udev, USBPacket *p)
+{
+ USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
+
+ return usbredir_handle_bulk_data(dev, p, p->ep->nr | USB_DIR_IN);
+}
+
static int usbredir_set_config(USBRedirDevice *dev, USBPacket *p,
int config)
{
@@ -1306,6 +1335,11 @@ static void usbredir_set_pipeline(USBRedirDevice *dev, struct USBEndpoint *uep)
if (uep->pid == USB_TOKEN_OUT) {
uep->pipeline = true;
}
+ if (uep->pid == USB_TOKEN_IN && uep->max_packet_size != 0 &&
+ usbredirparser_peer_has_cap(dev->parser,
+ usb_redir_cap_32bits_bulk_length)) {
+ uep->pipeline = true;
+ }
}
static void usbredir_ep_info(void *priv,
@@ -1488,11 +1522,17 @@ static void usbredir_bulk_packet(void *priv, uint64_t id,
p = usbredir_find_packet_by_id(dev, ep, id);
if (p) {
+ size_t size = (p->combined) ? p->combined->iov.size : p->iov.size;
len = usbredir_handle_status(dev, bulk_packet->status, len);
if (len > 0) {
usbredir_log_data(dev, "bulk data in:", data, data_len);
- if (data_len <= p->iov.size) {
- usb_packet_copy(p, data, data_len);
+ if (data_len <= size) {
+ if (p->combined) {
+ iov_from_buf(p->combined->iov.iov, p->combined->iov.niov,
+ 0, data, data_len);
+ } else {
+ usb_packet_copy(p, data, data_len);
+ }
} else {
ERROR("bulk got more data then requested (%d > %zd)\n",
data_len, p->iov.size);
@@ -1500,7 +1540,11 @@ static void usbredir_bulk_packet(void *priv, uint64_t id,
}
}
p->result = len;
- usb_packet_complete(&dev->dev, p);
+ if (p->pid == USB_TOKEN_IN && p->ep->pipeline) {
+ usb_combined_input_packet_complete(&dev->dev, p);
+ } else {
+ usb_packet_complete(&dev->dev, p);
+ }
}
free(data);
}
@@ -1907,6 +1951,9 @@ static void usbredir_class_initfn(ObjectClass *klass, void *data)
uc->handle_reset = usbredir_handle_reset;
uc->handle_data = usbredir_handle_data;
uc->handle_control = usbredir_handle_control;
+ uc->flush_ep_queue = usbredir_flush_ep_queue;
+ uc->handle_combined_data = usbredir_handle_combined_data;
+ uc->cancel_combined_packet = usbredir_cancel_packet;
dc->vmsd = &usbredir_vmstate;
dc->props = usbredir_properties;
}
--
1.7.12.1
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [Qemu-devel] [PATCH 19/22] usb-redir: Add an usbredir_setup_usb_eps() helper function
2012-10-24 16:13 [Qemu-devel] usb: input-pipelining + speedups v3 Hans de Goede
` (17 preceding siblings ...)
2012-10-24 16:14 ` [Qemu-devel] [PATCH 18/22] usb-redir: Add support for input pipelining Hans de Goede
@ 2012-10-24 16:14 ` Hans de Goede
2012-10-24 16:14 ` [Qemu-devel] [PATCH 20/22] usb-redir: Use reject rather the disconnect on bad ep info Hans de Goede
` (3 subsequent siblings)
22 siblings, 0 replies; 28+ messages in thread
From: Hans de Goede @ 2012-10-24 16:14 UTC (permalink / raw)
To: Gerd Hoffmann; +Cc: Hans de Goede, qemu-devel
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
hw/usb/redirect.c | 45 ++++++++++++++++++++++-----------------------
1 file changed, 22 insertions(+), 23 deletions(-)
diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c
index a6f9555..fe4eeea 100644
--- a/hw/usb/redirect.c
+++ b/hw/usb/redirect.c
@@ -1342,17 +1342,35 @@ static void usbredir_set_pipeline(USBRedirDevice *dev, struct USBEndpoint *uep)
}
}
+static void usbredir_setup_usb_eps(USBRedirDevice *dev)
+{
+ struct USBEndpoint *usb_ep;
+ int i, pid;
+
+ for (i = 0; i < MAX_ENDPOINTS; i++) {
+ pid = (i & 0x10) ? USB_TOKEN_IN : USB_TOKEN_OUT;
+ usb_ep = usb_ep_get(&dev->dev, pid, i & 0x0f);
+ usb_ep->type = dev->endpoint[i].type;
+ usb_ep->ifnum = dev->endpoint[i].interface;
+ usb_ep->max_packet_size = dev->endpoint[i].max_packet_size;
+ usbredir_set_pipeline(dev, usb_ep);
+ }
+}
+
static void usbredir_ep_info(void *priv,
struct usb_redir_ep_info_header *ep_info)
{
USBRedirDevice *dev = priv;
- struct USBEndpoint *usb_ep;
int i;
for (i = 0; i < MAX_ENDPOINTS; i++) {
dev->endpoint[i].type = ep_info->type[i];
dev->endpoint[i].interval = ep_info->interval[i];
dev->endpoint[i].interface = ep_info->interface[i];
+ if (usbredirparser_peer_has_cap(dev->parser,
+ usb_redir_cap_ep_info_max_packet_size)) {
+ dev->endpoint[i].max_packet_size = ep_info->max_packet_size[i];
+ }
switch (dev->endpoint[i].type) {
case usb_redir_type_invalid:
break;
@@ -1373,18 +1391,8 @@ static void usbredir_ep_info(void *priv,
usbredir_device_disconnect(dev);
return;
}
- usb_ep = usb_ep_get(&dev->dev,
- (i & 0x10) ? USB_TOKEN_IN : USB_TOKEN_OUT,
- i & 0x0f);
- usb_ep->type = dev->endpoint[i].type;
- usb_ep->ifnum = dev->endpoint[i].interface;
- if (usbredirparser_peer_has_cap(dev->parser,
- usb_redir_cap_ep_info_max_packet_size)) {
- dev->endpoint[i].max_packet_size =
- usb_ep->max_packet_size = ep_info->max_packet_size[i];
- }
- usbredir_set_pipeline(dev, usb_ep);
}
+ usbredir_setup_usb_eps(dev);
}
static void usbredir_configuration_status(void *priv, uint64_t id,
@@ -1626,8 +1634,6 @@ static void usbredir_pre_save(void *priv)
static int usbredir_post_load(void *priv, int version_id)
{
USBRedirDevice *dev = priv;
- struct USBEndpoint *usb_ep;
- int i;
switch (dev->device_info.speed) {
case usb_redir_speed_low:
@@ -1647,15 +1653,8 @@ static int usbredir_post_load(void *priv, int version_id)
}
dev->dev.speedmask = (1 << dev->dev.speed);
- for (i = 0; i < MAX_ENDPOINTS; i++) {
- usb_ep = usb_ep_get(&dev->dev,
- (i & 0x10) ? USB_TOKEN_IN : USB_TOKEN_OUT,
- i & 0x0f);
- usb_ep->type = dev->endpoint[i].type;
- usb_ep->ifnum = dev->endpoint[i].interface;
- usb_ep->max_packet_size = dev->endpoint[i].max_packet_size;
- usbredir_set_pipeline(dev, usb_ep);
- }
+ usbredir_setup_usb_eps(dev);
+
return 0;
}
--
1.7.12.1
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [Qemu-devel] [PATCH 20/22] usb-redir: Use reject rather the disconnect on bad ep info
2012-10-24 16:13 [Qemu-devel] usb: input-pipelining + speedups v3 Hans de Goede
` (18 preceding siblings ...)
2012-10-24 16:14 ` [Qemu-devel] [PATCH 19/22] usb-redir: Add an usbredir_setup_usb_eps() helper function Hans de Goede
@ 2012-10-24 16:14 ` Hans de Goede
2012-10-24 16:14 ` [Qemu-devel] [PATCH 21/22] usb-redir: Allow to attach USB 2.0 devices to 1.1 host controller Hans de Goede
` (2 subsequent siblings)
22 siblings, 0 replies; 28+ messages in thread
From: Hans de Goede @ 2012-10-24 16:14 UTC (permalink / raw)
To: Gerd Hoffmann; +Cc: Hans de Goede, qemu-devel
So that the client gets a notification about us disconnecting the device.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
hw/usb/redirect.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c
index fe4eeea..b1fc7ef 100644
--- a/hw/usb/redirect.c
+++ b/hw/usb/redirect.c
@@ -1378,7 +1378,8 @@ static void usbredir_ep_info(void *priv,
case usb_redir_type_interrupt:
if (dev->endpoint[i].interval == 0) {
ERROR("Received 0 interval for isoc or irq endpoint\n");
- usbredir_device_disconnect(dev);
+ usbredir_reject_device(dev);
+ return;
}
/* Fall through */
case usb_redir_type_control:
@@ -1388,7 +1389,7 @@ static void usbredir_ep_info(void *priv,
break;
default:
ERROR("Received invalid endpoint type\n");
- usbredir_device_disconnect(dev);
+ usbredir_reject_device(dev);
return;
}
}
--
1.7.12.1
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [Qemu-devel] [PATCH 21/22] usb-redir: Allow to attach USB 2.0 devices to 1.1 host controller
2012-10-24 16:13 [Qemu-devel] usb: input-pipelining + speedups v3 Hans de Goede
` (19 preceding siblings ...)
2012-10-24 16:14 ` [Qemu-devel] [PATCH 20/22] usb-redir: Use reject rather the disconnect on bad ep info Hans de Goede
@ 2012-10-24 16:14 ` Hans de Goede
2012-10-24 16:14 ` [Qemu-devel] [PATCH 22/22] usb-redir: Allow redirecting super speed devices to high speed controllers Hans de Goede
2012-10-25 7:13 ` [Qemu-devel] usb: input-pipelining + speedups v3 Gerd Hoffmann
22 siblings, 0 replies; 28+ messages in thread
From: Hans de Goede @ 2012-10-24 16:14 UTC (permalink / raw)
To: Gerd Hoffmann; +Cc: Jan Kiszka, qemu-devel, Hans de Goede
From: Jan Kiszka <jan.kiszka@siemens.com>
This follows the logic of host-linux: If a 2.0 device has no ISO
endpoint and no interrupt endpoint with a packet size > 64, we can
attach it also to an 1.1 host controller. In case the redir server does
not report endpoint sizes, play safe and remove the 1.1 compatibility as
well. Moreover, if we detect a conflicting change in the configuration
after the device was already attached, it will be disconnected
immediately.
HdG: Several small cleanups and fixes
Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
hw/usb/redirect.c | 30 +++++++++++++++++++++++++++++-
1 file changed, 29 insertions(+), 1 deletion(-)
diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c
index b1fc7ef..d2859b7 100644
--- a/hw/usb/redirect.c
+++ b/hw/usb/redirect.c
@@ -106,6 +106,7 @@ struct USBRedirDevice {
struct usb_redir_interface_info_header interface_info;
struct usbredirfilter_rule *filter_rules;
int filter_rules_count;
+ int compatible_speedmask;
};
static void usbredir_hello(void *priv, struct usb_redir_hello_header *h);
@@ -974,6 +975,7 @@ static void usbredir_do_attach(void *opaque)
}
if (usb_device_attach(&dev->dev) != 0) {
+ WARNING("rejecting device due to speed mismatch\n");
usbredir_reject_device(dev);
}
}
@@ -1094,6 +1096,9 @@ static int usbredir_initfn(USBDevice *udev)
/* We'll do the attach once we receive the speed from the usb-host */
udev->auto_attach = 0;
+ /* Will be cleared during setup when we find conflicts */
+ dev->compatible_speedmask = USB_SPEED_MASK_FULL;
+
/* Let the backend know we are ready */
qemu_chr_fe_open(dev->cs);
qemu_chr_add_handlers(dev->cs, &usbredir_chr_handlers, dev);
@@ -1233,6 +1238,7 @@ static void usbredir_device_connect(void *priv,
case usb_redir_speed_low:
speed = "low speed";
dev->dev.speed = USB_SPEED_LOW;
+ dev->compatible_speedmask &= ~USB_SPEED_MASK_FULL;
break;
case usb_redir_speed_full:
speed = "full speed";
@@ -1266,7 +1272,7 @@ static void usbredir_device_connect(void *priv,
device_connect->device_class);
}
- dev->dev.speedmask = (1 << dev->dev.speed);
+ dev->dev.speedmask = (1 << dev->dev.speed) | dev->compatible_speedmask;
dev->device_info = *device_connect;
if (usbredir_check_filter(dev)) {
@@ -1306,6 +1312,7 @@ static void usbredir_device_disconnect(void *priv)
dev->interface_info.interface_count = NO_INTERFACE_INFO;
dev->dev.addr = 0;
dev->dev.speed = 0;
+ dev->compatible_speedmask = USB_SPEED_MASK_FULL;
}
static void usbredir_interface_info(void *priv,
@@ -1327,6 +1334,12 @@ static void usbredir_interface_info(void *priv,
}
}
+static void usbredir_mark_speed_incompatible(USBRedirDevice *dev, int speed)
+{
+ dev->compatible_speedmask &= ~(1 << speed);
+ dev->dev.speedmask = (1 << dev->dev.speed) | dev->compatible_speedmask;
+}
+
static void usbredir_set_pipeline(USBRedirDevice *dev, struct USBEndpoint *uep)
{
if (uep->type != USB_ENDPOINT_XFER_BULK) {
@@ -1375,7 +1388,14 @@ static void usbredir_ep_info(void *priv,
case usb_redir_type_invalid:
break;
case usb_redir_type_iso:
+ usbredir_mark_speed_incompatible(dev, USB_SPEED_FULL);
+ /* Fall through */
case usb_redir_type_interrupt:
+ if (!usbredirparser_peer_has_cap(dev->parser,
+ usb_redir_cap_ep_info_max_packet_size) ||
+ ep_info->max_packet_size[i] > 64) {
+ usbredir_mark_speed_incompatible(dev, USB_SPEED_FULL);
+ }
if (dev->endpoint[i].interval == 0) {
ERROR("Received 0 interval for isoc or irq endpoint\n");
usbredir_reject_device(dev);
@@ -1393,6 +1413,14 @@ static void usbredir_ep_info(void *priv,
return;
}
}
+ /* The new ep info may have caused a speed incompatibility, recheck */
+ if (dev->dev.attached &&
+ !(dev->dev.port->speedmask & dev->dev.speedmask)) {
+ ERROR("Device no longer matches speed after endpoint info change, "
+ "disconnecting!\n");
+ usbredir_reject_device(dev);
+ return;
+ }
usbredir_setup_usb_eps(dev);
}
--
1.7.12.1
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [Qemu-devel] [PATCH 22/22] usb-redir: Allow redirecting super speed devices to high speed controllers
2012-10-24 16:13 [Qemu-devel] usb: input-pipelining + speedups v3 Hans de Goede
` (20 preceding siblings ...)
2012-10-24 16:14 ` [Qemu-devel] [PATCH 21/22] usb-redir: Allow to attach USB 2.0 devices to 1.1 host controller Hans de Goede
@ 2012-10-24 16:14 ` Hans de Goede
2012-10-25 7:13 ` [Qemu-devel] usb: input-pipelining + speedups v3 Gerd Hoffmann
22 siblings, 0 replies; 28+ messages in thread
From: Hans de Goede @ 2012-10-24 16:14 UTC (permalink / raw)
To: Gerd Hoffmann; +Cc: Hans de Goede, qemu-devel
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
hw/usb/redirect.c | 23 +++++++++++++++++++++--
1 file changed, 21 insertions(+), 2 deletions(-)
diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c
index d2859b7..2c5377c 100644
--- a/hw/usb/redirect.c
+++ b/hw/usb/redirect.c
@@ -1097,7 +1097,7 @@ static int usbredir_initfn(USBDevice *udev)
udev->auto_attach = 0;
/* Will be cleared during setup when we find conflicts */
- dev->compatible_speedmask = USB_SPEED_MASK_FULL;
+ dev->compatible_speedmask = USB_SPEED_MASK_FULL | USB_SPEED_MASK_HIGH;
/* Let the backend know we are ready */
qemu_chr_fe_open(dev->cs);
@@ -1239,10 +1239,12 @@ static void usbredir_device_connect(void *priv,
speed = "low speed";
dev->dev.speed = USB_SPEED_LOW;
dev->compatible_speedmask &= ~USB_SPEED_MASK_FULL;
+ dev->compatible_speedmask &= ~USB_SPEED_MASK_HIGH;
break;
case usb_redir_speed_full:
speed = "full speed";
dev->dev.speed = USB_SPEED_FULL;
+ dev->compatible_speedmask &= ~USB_SPEED_MASK_HIGH;
break;
case usb_redir_speed_high:
speed = "high speed";
@@ -1312,7 +1314,7 @@ static void usbredir_device_disconnect(void *priv)
dev->interface_info.interface_count = NO_INTERFACE_INFO;
dev->dev.addr = 0;
dev->dev.speed = 0;
- dev->compatible_speedmask = USB_SPEED_MASK_FULL;
+ dev->compatible_speedmask = USB_SPEED_MASK_FULL | USB_SPEED_MASK_HIGH;
}
static void usbredir_interface_info(void *priv,
@@ -1389,6 +1391,7 @@ static void usbredir_ep_info(void *priv,
break;
case usb_redir_type_iso:
usbredir_mark_speed_incompatible(dev, USB_SPEED_FULL);
+ usbredir_mark_speed_incompatible(dev, USB_SPEED_HIGH);
/* Fall through */
case usb_redir_type_interrupt:
if (!usbredirparser_peer_has_cap(dev->parser,
@@ -1396,6 +1399,11 @@ static void usbredir_ep_info(void *priv,
ep_info->max_packet_size[i] > 64) {
usbredir_mark_speed_incompatible(dev, USB_SPEED_FULL);
}
+ if (!usbredirparser_peer_has_cap(dev->parser,
+ usb_redir_cap_ep_info_max_packet_size) ||
+ ep_info->max_packet_size[i] > 1024) {
+ usbredir_mark_speed_incompatible(dev, USB_SPEED_HIGH);
+ }
if (dev->endpoint[i].interval == 0) {
ERROR("Received 0 interval for isoc or irq endpoint\n");
usbredir_reject_device(dev);
@@ -1526,6 +1534,17 @@ static void usbredir_control_packet(void *priv, uint64_t id,
DPRINTF("ctrl-in status %d len %d id %"PRIu64"\n", control_packet->status,
len, id);
+ /* Fix up USB-3 ep0 maxpacket size to allow superspeed connected devices
+ * to work redirected to a not superspeed capable hcd */
+ if (dev->dev.speed == USB_SPEED_SUPER &&
+ !((dev->dev.port->speedmask & USB_SPEED_MASK_SUPER)) &&
+ control_packet->requesttype == 0x80 &&
+ control_packet->request == 6 &&
+ control_packet->value == 0x100 && control_packet->index == 0 &&
+ data_len >= 18 && data[7] == 9) {
+ data[7] = 64;
+ }
+
p = usbredir_find_packet_by_id(dev, 0, id);
if (p) {
len = usbredir_handle_status(dev, control_packet->status, len);
--
1.7.12.1
^ permalink raw reply related [flat|nested] 28+ messages in thread
* Re: [Qemu-devel] [PATCH 15/22] usb: Add packet combining functions
2012-10-24 16:14 ` [Qemu-devel] [PATCH 15/22] usb: Add packet combining functions Hans de Goede
@ 2012-10-25 6:55 ` Gerd Hoffmann
2012-10-30 13:23 ` Hans de Goede
0 siblings, 1 reply; 28+ messages in thread
From: Gerd Hoffmann @ 2012-10-25 6:55 UTC (permalink / raw)
To: Hans de Goede; +Cc: qemu-devel
On 10/24/12 18:14, Hans de Goede wrote:
> + /*
> + * Process / cancel combined packets, called from
> + * usb_ep_combine_input_packets() / usb_combined_packet_cancel().
> + * Only called for devices which call these functions themselves.
> + */
> + int (*handle_combined_data)(USBDevice *dev, USBPacket *p);
> + void (*cancel_combined_packet)(USBDevice *dev, USBPacket *p);
I still think these should get a USBCombinedPacket not a USBPacket.
cheers,
Gerd
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [Qemu-devel] usb: input-pipelining + speedups v3
2012-10-24 16:13 [Qemu-devel] usb: input-pipelining + speedups v3 Hans de Goede
` (21 preceding siblings ...)
2012-10-24 16:14 ` [Qemu-devel] [PATCH 22/22] usb-redir: Allow redirecting super speed devices to high speed controllers Hans de Goede
@ 2012-10-25 7:13 ` Gerd Hoffmann
22 siblings, 0 replies; 28+ messages in thread
From: Gerd Hoffmann @ 2012-10-25 7:13 UTC (permalink / raw)
To: Hans de Goede; +Cc: qemu-devel
On 10/24/12 18:13, Hans de Goede wrote:
> This is the final version of the input pipelining patchset, please
> add these patches to your tree for Anthony.
>
> Changes since the v2 rfc:
> - Move the settnig of the int_req flag for xhci, as you requested
> - Add "ehci: Retry to fill the queue while waiting for td completion" patch
> - Lots of tests run with the code without issues :)
Queued up patches 1-14.
cheers,
Gerd
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [Qemu-devel] [PATCH 15/22] usb: Add packet combining functions
2012-10-25 6:55 ` Gerd Hoffmann
@ 2012-10-30 13:23 ` Hans de Goede
2012-10-30 13:25 ` Hans de Goede
2012-10-30 13:38 ` Hans de Goede
0 siblings, 2 replies; 28+ messages in thread
From: Hans de Goede @ 2012-10-30 13:23 UTC (permalink / raw)
To: Gerd Hoffmann; +Cc: qemu-devel
Hi,
On 10/25/2012 08:55 AM, Gerd Hoffmann wrote:
> On 10/24/12 18:14, Hans de Goede wrote:
>> + /*
>> + * Process / cancel combined packets, called from
>> + * usb_ep_combine_input_packets() / usb_combined_packet_cancel().
>> + * Only called for devices which call these functions themselves.
>> + */
>> + int (*handle_combined_data)(USBDevice *dev, USBPacket *p);
>> + void (*cancel_combined_packet)(USBDevice *dev, USBPacket *p);
>
> I still think these should get a USBCombinedPacket not a USBPacket.
I just rebased my tree's USB bits to your usb.68 pull req, and then
tried to make this change, and then I realized again why at least
handle_combined_data is not getting a USBCombinedPacket as argument.
The call sequence goes like this:
1) hcd calls usb_handle_packet
2) usb_handle_packet calls devices handle_data (through process_one)
3) device's handle_data sees this is for a input ep on which it is
doing input pipelining, returns USB_RET_ADD_TO_QUEUE
4) hcd calls usb_device_flush_ep_queue
5) usb_device_flush_ep_queue calls usb_ep_combine_input_packets
6) usb_ep_combine_input_packets either ends up with a combined
packet, or with a single regular packet to send to
the device
Currently usb_ep_combine_input_packets calls the device's
handle_combined_data method in both cases, and that can distinguish
between the 2 scenarios by checking the passed in USBPacket's
combined field.
I did things this way, even though it may seem more logical for
usb_ep_combine_input_packets to call the device's "regular"
handle_data method in case no combining is done for a packet,
so it is submitting a single regular packet, but in that case
we would end up at step 3) again, and the device's handle_data
will again return USB_RET_ADD_TO_QUEUE which is not what we want.
This is why handle_combined_data takes a USBPacket, and then checks
USBPacket->combined to see what to do, rather then taking a
USBCombinedPacket, as usb_ep_combine_input_packets simply does
not always have a combined packet to pass.
Alternatives to allow handle_combined_data to take a
USBCombinedPacket, would be:
1) Some flag to the device's handle_data method to indicate
it should *really* process the packet and not return
USB_RET_ADD_TO_QUEUE
2) Always make Uc allocate a USBCombinedPacket,
even when the entire transfer consists of only a single
packet, note that this in essence means an unnecessary
malloc + free call per such packet, and for example with
(redirected) usb-mass-storage one can expect each scsi
sense phase transfer to be only a single packet large,
and for smaller reads the data phase packets as well!
IMHO either alternative is worse then simply passing
a USBPacket to handle_combined_data, and let the
device's handle_combined_data figure out what to do
based on USBPacket->combined. Note that if it were
to take a USBCombinedPacket, it would end up getting
back to the USBPacket itself through
USBCombinedPacket->first anyways to get info from there
such as the packet id.
Regards,
Hans
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [Qemu-devel] [PATCH 15/22] usb: Add packet combining functions
2012-10-30 13:23 ` Hans de Goede
@ 2012-10-30 13:25 ` Hans de Goede
2012-10-30 13:38 ` Hans de Goede
1 sibling, 0 replies; 28+ messages in thread
From: Hans de Goede @ 2012-10-30 13:25 UTC (permalink / raw)
To: Gerd Hoffmann; +Cc: qemu-devel
Hi,
On 10/30/2012 02:23 PM, Hans de Goede wrote:
> Hi,
>
> On 10/25/2012 08:55 AM, Gerd Hoffmann wrote:
>> On 10/24/12 18:14, Hans de Goede wrote:
>>> + /*
>>> + * Process / cancel combined packets, called from
>>> + * usb_ep_combine_input_packets() / usb_combined_packet_cancel().
>>> + * Only called for devices which call these functions themselves.
>>> + */
>>> + int (*handle_combined_data)(USBDevice *dev, USBPacket *p);
>>> + void (*cancel_combined_packet)(USBDevice *dev, USBPacket *p);
>>
>> I still think these should get a USBCombinedPacket not a USBPacket.
>
> I just rebased my tree's USB bits to your usb.68 pull req, and then
> tried to make this change, and then I realized again why at least
> handle_combined_data is not getting a USBCombinedPacket as argument.
>
> The call sequence goes like this:
>
> 1) hcd calls usb_handle_packet
> 2) usb_handle_packet calls devices handle_data (through process_one)
> 3) device's handle_data sees this is for a input ep on which it is
> doing input pipelining, returns USB_RET_ADD_TO_QUEUE
> 4) hcd calls usb_device_flush_ep_queue
> 5) usb_device_flush_ep_queue calls usb_ep_combine_input_packets
> 6) usb_ep_combine_input_packets either ends up with a combined
> packet, or with a single regular packet to send to
> the device
>
> Currently usb_ep_combine_input_packets calls the device's
> handle_combined_data method in both cases, and that can distinguish
> between the 2 scenarios by checking the passed in USBPacket's
> combined field.
>
> I did things this way, even though it may seem more logical for
> usb_ep_combine_input_packets to call the device's "regular"
> handle_data method in case no combining is done for a packet,
> so it is submitting a single regular packet, but in that case
> we would end up at step 3) again, and the device's handle_data
> will again return USB_RET_ADD_TO_QUEUE which is not what we want.
>
> This is why handle_combined_data takes a USBPacket, and then checks
> USBPacket->combined to see what to do, rather then taking a
> USBCombinedPacket, as usb_ep_combine_input_packets simply does
> not always have a combined packet to pass.
>
> Alternatives to allow handle_combined_data to take a
> USBCombinedPacket, would be:
> 1) Some flag to the device's handle_data method to indicate
> it should *really* process the packet and not return
> USB_RET_ADD_TO_QUEUE
> 2) Always make Uc allocate a USBCombinedPacket,
> even when the entire transfer consists of only a single
> packet, note that this in essence means an unnecessary
> malloc + free call per such packet, and for example with
> (redirected) usb-mass-storage one can expect each scsi
> sense phase transfer to be only a single packet large,
> and for smaller reads the data phase packets as well!
>
> IMHO either alternative is worse then simply passing
> a USBPacket to handle_combined_data, and let the
> device's handle_combined_data figure out what to do
> based on USBPacket->combined. Note that if it were
> to take a USBCombinedPacket, it would end up getting
> back to the USBPacket itself through
> USBCombinedPacket->first anyways to get info from there
> such as the packet id.
>
p.s.
If we stick with handle_combined_data taking a USBPacket
as argument, then I would like to do the same for
cancel_combined_packet for consistency!
Regards,
Hans
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [Qemu-devel] [PATCH 15/22] usb: Add packet combining functions
2012-10-30 13:23 ` Hans de Goede
2012-10-30 13:25 ` Hans de Goede
@ 2012-10-30 13:38 ` Hans de Goede
1 sibling, 0 replies; 28+ messages in thread
From: Hans de Goede @ 2012-10-30 13:38 UTC (permalink / raw)
To: Gerd Hoffmann; +Cc: qemu-devel
Hi,
On 10/30/2012 02:23 PM, Hans de Goede wrote:
> Hi,
>
> On 10/25/2012 08:55 AM, Gerd Hoffmann wrote:
>> On 10/24/12 18:14, Hans de Goede wrote:
>>> + /*
>>> + * Process / cancel combined packets, called from
>>> + * usb_ep_combine_input_packets() / usb_combined_packet_cancel().
>>> + * Only called for devices which call these functions themselves.
>>> + */
>>> + int (*handle_combined_data)(USBDevice *dev, USBPacket *p);
>>> + void (*cancel_combined_packet)(USBDevice *dev, USBPacket *p);
>>
>> I still think these should get a USBCombinedPacket not a USBPacket.
>
> I just rebased my tree's USB bits to your usb.68 pull req, and then
> tried to make this change, and then I realized again why at least
> handle_combined_data is not getting a USBCombinedPacket as argument.
>
> The call sequence goes like this:
>
> 1) hcd calls usb_handle_packet
> 2) usb_handle_packet calls devices handle_data (through process_one)
> 3) device's handle_data sees this is for a input ep on which it is
> doing input pipelining, returns USB_RET_ADD_TO_QUEUE
> 4) hcd calls usb_device_flush_ep_queue
> 5) usb_device_flush_ep_queue calls usb_ep_combine_input_packets
> 6) usb_ep_combine_input_packets either ends up with a combined
> packet, or with a single regular packet to send to
> the device
>
> Currently usb_ep_combine_input_packets calls the device's
> handle_combined_data method in both cases, and that can distinguish
> between the 2 scenarios by checking the passed in USBPacket's
> combined field.
>
> I did things this way, even though it may seem more logical for
> usb_ep_combine_input_packets to call the device's "regular"
> handle_data method in case no combining is done for a packet,
> so it is submitting a single regular packet, but in that case
> we would end up at step 3) again, and the device's handle_data
> will again return USB_RET_ADD_TO_QUEUE which is not what we want.
Oh wait, thinking more about this, the device's handle_data
method can see the difference between the 1st and 2nd call,
as the packets state will have changed from USB_PACKET_SETUP
to USB_PACKET_QUEUED. Using that, we can do even better and
completely get rid of the handle_combined_data and
cancel_combined_packet methods. Instead have the device code
check for USBPacket->combined where appropriate, this also
makes the handle_data and cancel_packet paths more alike,
as the device already needed to check for USBPacket->combined
in its cancel_packet method.
New version coming up!
Regards,
Hans
^ permalink raw reply [flat|nested] 28+ messages in thread
end of thread, other threads:[~2012-10-30 13:37 UTC | newest]
Thread overview: 28+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-10-24 16:13 [Qemu-devel] usb: input-pipelining + speedups v3 Hans de Goede
2012-10-24 16:13 ` [Qemu-devel] [PATCH 01/22] uhci: Properly unmap packets on cancel / invalid pid Hans de Goede
2012-10-24 16:13 ` [Qemu-devel] [PATCH 02/22] uhci: Move checks to continue queuing to uhci_fill_queue() Hans de Goede
2012-10-24 16:13 ` [Qemu-devel] [PATCH 03/22] ehci: Get rid of packet tbytes field Hans de Goede
2012-10-24 16:14 ` [Qemu-devel] [PATCH 04/22] ehci: Set int flag on a short input packet Hans de Goede
2012-10-24 16:14 ` [Qemu-devel] [PATCH 05/22] ehci: Improve latency of interrupt delivery and async schedule scanning Hans de Goede
2012-10-24 16:14 ` [Qemu-devel] [PATCH 06/22] ehci: Speed up the timer of raising int from the async schedule Hans de Goede
2012-10-24 16:14 ` [Qemu-devel] [PATCH 07/22] ehci: Detect going in circles when filling the queue Hans de Goede
2012-10-24 16:14 ` [Qemu-devel] [PATCH 08/22] ehci: Retry to fill the queue while waiting for td completion Hans de Goede
2012-10-24 16:14 ` [Qemu-devel] [PATCH 09/22] xhci: Add a xhci_ep_nuke_one_xfer helper function Hans de Goede
2012-10-24 16:14 ` [Qemu-devel] [PATCH 10/22] usb: Rename __usb_packet_complete to usb_packet_complete_one Hans de Goede
2012-10-24 16:14 ` [Qemu-devel] [PATCH 11/22] usb: Add USB_RET_ADD_TO_QUEUE packet result code Hans de Goede
2012-10-24 16:14 ` [Qemu-devel] [PATCH 12/22] usb: Move clearing of queue on halt to the core Hans de Goede
2012-10-24 16:14 ` [Qemu-devel] [PATCH 13/22] usb: Move short-not-ok handling " Hans de Goede
2012-10-24 16:14 ` [Qemu-devel] [PATCH 14/22] usb: Add an int_req flag to USBPacket Hans de Goede
2012-10-24 16:14 ` [Qemu-devel] [PATCH 15/22] usb: Add packet combining functions Hans de Goede
2012-10-25 6:55 ` Gerd Hoffmann
2012-10-30 13:23 ` Hans de Goede
2012-10-30 13:25 ` Hans de Goede
2012-10-30 13:38 ` Hans de Goede
2012-10-24 16:14 ` [Qemu-devel] [PATCH 16/22] combined-packet: Add a workaround for Linux usbfs + live migration Hans de Goede
2012-10-24 16:14 ` [Qemu-devel] [PATCH 17/22] usb-redir: Add support for 32 bits bulk packet length Hans de Goede
2012-10-24 16:14 ` [Qemu-devel] [PATCH 18/22] usb-redir: Add support for input pipelining Hans de Goede
2012-10-24 16:14 ` [Qemu-devel] [PATCH 19/22] usb-redir: Add an usbredir_setup_usb_eps() helper function Hans de Goede
2012-10-24 16:14 ` [Qemu-devel] [PATCH 20/22] usb-redir: Use reject rather the disconnect on bad ep info Hans de Goede
2012-10-24 16:14 ` [Qemu-devel] [PATCH 21/22] usb-redir: Allow to attach USB 2.0 devices to 1.1 host controller Hans de Goede
2012-10-24 16:14 ` [Qemu-devel] [PATCH 22/22] usb-redir: Allow redirecting super speed devices to high speed controllers Hans de Goede
2012-10-25 7:13 ` [Qemu-devel] usb: input-pipelining + speedups v3 Gerd Hoffmann
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).