diff options
author | Rusty Russell <rusty@rustcorp.com.au> | 2009-06-13 00:27:11 -0400 |
---|---|---|
committer | Rusty Russell <rusty@rustcorp.com.au> | 2009-06-12 08:57:11 -0400 |
commit | 38bc2b8c56a2e212bbd19de7cf9976dcc7bf9953 (patch) | |
tree | 26071dbb540926c329ece0ab4d4425807021b5c8 /Documentation/lguest/lguest.c | |
parent | 5dac051bc6030963181b69faddd9e0ad04f85fa8 (diff) |
lguest: implement deferred interrupts in example Launcher
Rather than sending an interrupt on every buffer, we only send an interrupt
when we're about to wait for the Guest to send us a new one. The console
input and network input still send interrupts manually, but the block device,
network and console output queues can simply rely on this logic to send
interrupts to the Guest at the right time.
The patch is cluttered by moving trigger_irq() higher in the code.
In practice, two factors make this optimization less interesting:
(1) we often only get one input at a time, even for networking,
(2) triggering an interrupt rapidly tends to get coalesced anyway.
Before: Secs RxIRQS TxIRQs
1G TCP Guest->Host: 3.72 32784 32771
1M normal pings: 99 1000004 995541
100,000 1k pings (-l 120): 5 49510 49058
After:
1G TCP Guest->Host: 3.69 32809 32769
1M normal pings: 99 1000004 996196
100,000 1k pings (-l 120): 5 52435 52361
(Note the interrupt count on 100k pings goes *up*: see next patch).
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Diffstat (limited to 'Documentation/lguest/lguest.c')
-rw-r--r-- | Documentation/lguest/lguest.c | 41 |
1 files changed, 22 insertions, 19 deletions
diff --git a/Documentation/lguest/lguest.c b/Documentation/lguest/lguest.c index 5470b8ed2149..84c471b07c27 100644 --- a/Documentation/lguest/lguest.c +++ b/Documentation/lguest/lguest.c | |||
@@ -551,6 +551,21 @@ static unsigned next_desc(struct virtqueue *vq, unsigned int i) | |||
551 | return next; | 551 | return next; |
552 | } | 552 | } |
553 | 553 | ||
554 | /* This actually sends the interrupt for this virtqueue */ | ||
555 | static void trigger_irq(struct virtqueue *vq) | ||
556 | { | ||
557 | unsigned long buf[] = { LHREQ_IRQ, vq->config.irq }; | ||
558 | |||
559 | /* If they don't want an interrupt, don't send one, unless empty. */ | ||
560 | if ((vq->vring.avail->flags & VRING_AVAIL_F_NO_INTERRUPT) | ||
561 | && lg_last_avail(vq) != vq->vring.avail->idx) | ||
562 | return; | ||
563 | |||
564 | /* Send the Guest an interrupt tell them we used something up. */ | ||
565 | if (write(lguest_fd, buf, sizeof(buf)) != 0) | ||
566 | err(1, "Triggering irq %i", vq->config.irq); | ||
567 | } | ||
568 | |||
554 | /* This looks in the virtqueue and for the first available buffer, and converts | 569 | /* This looks in the virtqueue and for the first available buffer, and converts |
555 | * it to an iovec for convenient access. Since descriptors consist of some | 570 | * it to an iovec for convenient access. Since descriptors consist of some |
556 | * number of output then some number of input descriptors, it's actually two | 571 | * number of output then some number of input descriptors, it's actually two |
@@ -567,6 +582,9 @@ static unsigned wait_for_vq_desc(struct virtqueue *vq, | |||
567 | while (last_avail == vq->vring.avail->idx) { | 582 | while (last_avail == vq->vring.avail->idx) { |
568 | u64 event; | 583 | u64 event; |
569 | 584 | ||
585 | /* OK, tell Guest about progress up to now. */ | ||
586 | trigger_irq(vq); | ||
587 | |||
570 | /* Nothing new? Wait for eventfd to tell us they refilled. */ | 588 | /* Nothing new? Wait for eventfd to tell us they refilled. */ |
571 | if (read(vq->eventfd, &event, sizeof(event)) != sizeof(event)) | 589 | if (read(vq->eventfd, &event, sizeof(event)) != sizeof(event)) |
572 | errx(1, "Event read failed?"); | 590 | errx(1, "Event read failed?"); |
@@ -631,21 +649,6 @@ static void add_used(struct virtqueue *vq, unsigned int head, int len) | |||
631 | vq->vring.used->idx++; | 649 | vq->vring.used->idx++; |
632 | } | 650 | } |
633 | 651 | ||
634 | /* This actually sends the interrupt for this virtqueue */ | ||
635 | static void trigger_irq(struct virtqueue *vq) | ||
636 | { | ||
637 | unsigned long buf[] = { LHREQ_IRQ, vq->config.irq }; | ||
638 | |||
639 | /* If they don't want an interrupt, don't send one, unless empty. */ | ||
640 | if ((vq->vring.avail->flags & VRING_AVAIL_F_NO_INTERRUPT) | ||
641 | && lg_last_avail(vq) != vq->vring.avail->idx) | ||
642 | return; | ||
643 | |||
644 | /* Send the Guest an interrupt tell them we used something up. */ | ||
645 | if (write(lguest_fd, buf, sizeof(buf)) != 0) | ||
646 | err(1, "Triggering irq %i", vq->config.irq); | ||
647 | } | ||
648 | |||
649 | /* And here's the combo meal deal. Supersize me! */ | 652 | /* And here's the combo meal deal. Supersize me! */ |
650 | static void add_used_and_trigger(struct virtqueue *vq, unsigned head, int len) | 653 | static void add_used_and_trigger(struct virtqueue *vq, unsigned head, int len) |
651 | { | 654 | { |
@@ -730,7 +733,7 @@ static void console_output(struct virtqueue *vq) | |||
730 | err(1, "Write to stdout gave %i", len); | 733 | err(1, "Write to stdout gave %i", len); |
731 | iov_consume(iov, out, len); | 734 | iov_consume(iov, out, len); |
732 | } | 735 | } |
733 | add_used_and_trigger(vq, head, 0); | 736 | add_used(vq, head, 0); |
734 | } | 737 | } |
735 | 738 | ||
736 | /* | 739 | /* |
@@ -754,7 +757,7 @@ static void net_output(struct virtqueue *vq) | |||
754 | errx(1, "Input buffers in net output queue?"); | 757 | errx(1, "Input buffers in net output queue?"); |
755 | if (writev(net_info->tunfd, iov, out) < 0) | 758 | if (writev(net_info->tunfd, iov, out) < 0) |
756 | errx(1, "Write to tun failed?"); | 759 | errx(1, "Write to tun failed?"); |
757 | add_used_and_trigger(vq, head, 0); | 760 | add_used(vq, head, 0); |
758 | } | 761 | } |
759 | 762 | ||
760 | /* This is where we handle packets coming in from the tun device to our | 763 | /* This is where we handle packets coming in from the tun device to our |
@@ -1422,7 +1425,7 @@ static void blk_request(struct virtqueue *vq) | |||
1422 | if (out->type & VIRTIO_BLK_T_BARRIER) | 1425 | if (out->type & VIRTIO_BLK_T_BARRIER) |
1423 | fdatasync(vblk->fd); | 1426 | fdatasync(vblk->fd); |
1424 | 1427 | ||
1425 | add_used_and_trigger(vq, head, wlen); | 1428 | add_used(vq, head, wlen); |
1426 | } | 1429 | } |
1427 | 1430 | ||
1428 | /*L:198 This actually sets up a virtual block device. */ | 1431 | /*L:198 This actually sets up a virtual block device. */ |
@@ -1496,7 +1499,7 @@ static void rng_input(struct virtqueue *vq) | |||
1496 | } | 1499 | } |
1497 | 1500 | ||
1498 | /* Tell the Guest about the new input. */ | 1501 | /* Tell the Guest about the new input. */ |
1499 | add_used_and_trigger(vq, head, totlen); | 1502 | add_used(vq, head, totlen); |
1500 | } | 1503 | } |
1501 | 1504 | ||
1502 | /* And this creates a "hardware" random number device for the Guest. */ | 1505 | /* And this creates a "hardware" random number device for the Guest. */ |