Commit c27ff60871aff588a35e51d1a90faed410993e55
Committed by
Mark McLoughlin
1 parent
c8aa237c
net: Fix and improved ordered packet delivery
Fix a race in qemu_send_packet when delivering deferred packets and add proper deferring also to qemu_sendv_packet. Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com> Signed-off-by: Mark McLoughlin <markmc@redhat.com>
Showing
1 changed file
with
42 additions
and
15 deletions
net.c
@@ -438,8 +438,8 @@ void qemu_send_packet(VLANClientState *vc, const uint8_t *buf, int size) | @@ -438,8 +438,8 @@ void qemu_send_packet(VLANClientState *vc, const uint8_t *buf, int size) | ||
438 | vlan->delivering = 1; | 438 | vlan->delivering = 1; |
439 | qemu_deliver_packet(vc, buf, size); | 439 | qemu_deliver_packet(vc, buf, size); |
440 | while ((packet = vlan->send_queue) != NULL) { | 440 | while ((packet = vlan->send_queue) != NULL) { |
441 | - qemu_deliver_packet(packet->sender, packet->data, packet->size); | ||
442 | vlan->send_queue = packet->next; | 441 | vlan->send_queue = packet->next; |
442 | + qemu_deliver_packet(packet->sender, packet->data, packet->size); | ||
443 | qemu_free(packet); | 443 | qemu_free(packet); |
444 | } | 444 | } |
445 | vlan->delivering = 0; | 445 | vlan->delivering = 0; |
@@ -476,30 +476,57 @@ static ssize_t calc_iov_length(const struct iovec *iov, int iovcnt) | @@ -476,30 +476,57 @@ static ssize_t calc_iov_length(const struct iovec *iov, int iovcnt) | ||
476 | return offset; | 476 | return offset; |
477 | } | 477 | } |
478 | 478 | ||
479 | -ssize_t qemu_sendv_packet(VLANClientState *vc1, const struct iovec *iov, | 479 | +ssize_t qemu_sendv_packet(VLANClientState *sender, const struct iovec *iov, |
480 | int iovcnt) | 480 | int iovcnt) |
481 | { | 481 | { |
482 | - VLANState *vlan = vc1->vlan; | 482 | + VLANState *vlan = sender->vlan; |
483 | VLANClientState *vc; | 483 | VLANClientState *vc; |
484 | + VLANPacket *packet; | ||
484 | ssize_t max_len = 0; | 485 | ssize_t max_len = 0; |
486 | + int i; | ||
485 | 487 | ||
486 | - if (vc1->link_down) | 488 | + if (sender->link_down) |
487 | return calc_iov_length(iov, iovcnt); | 489 | return calc_iov_length(iov, iovcnt); |
488 | 490 | ||
489 | - for (vc = vlan->first_client; vc != NULL; vc = vc->next) { | ||
490 | - ssize_t len = 0; | 491 | + if (vlan->delivering) { |
492 | + max_len = calc_iov_length(iov, iovcnt); | ||
491 | 493 | ||
492 | - if (vc == vc1) | ||
493 | - continue; | 494 | + packet = qemu_malloc(sizeof(VLANPacket) + max_len); |
495 | + packet->next = vlan->send_queue; | ||
496 | + packet->sender = sender; | ||
497 | + packet->size = 0; | ||
498 | + for (i = 0; i < iovcnt; i++) { | ||
499 | + size_t len = iov[i].iov_len; | ||
500 | + | ||
501 | + memcpy(packet->data + packet->size, iov[i].iov_base, len); | ||
502 | + packet->size += len; | ||
503 | + } | ||
504 | + vlan->send_queue = packet; | ||
505 | + } else { | ||
506 | + vlan->delivering = 1; | ||
507 | + | ||
508 | + for (vc = vlan->first_client; vc != NULL; vc = vc->next) { | ||
509 | + ssize_t len = 0; | ||
494 | 510 | ||
495 | - if (vc->link_down) | ||
496 | - len = calc_iov_length(iov, iovcnt); | ||
497 | - else if (vc->fd_readv) | ||
498 | - len = vc->fd_readv(vc->opaque, iov, iovcnt); | ||
499 | - else if (vc->fd_read) | ||
500 | - len = vc_sendv_compat(vc, iov, iovcnt); | 511 | + if (vc == sender) { |
512 | + continue; | ||
513 | + } | ||
514 | + if (vc->link_down) { | ||
515 | + len = calc_iov_length(iov, iovcnt); | ||
516 | + } else if (vc->fd_readv) { | ||
517 | + len = vc->fd_readv(vc->opaque, iov, iovcnt); | ||
518 | + } else if (vc->fd_read) { | ||
519 | + len = vc_sendv_compat(vc, iov, iovcnt); | ||
520 | + } | ||
521 | + max_len = MAX(max_len, len); | ||
522 | + } | ||
501 | 523 | ||
502 | - max_len = MAX(max_len, len); | 524 | + while ((packet = vlan->send_queue) != NULL) { |
525 | + vlan->send_queue = packet->next; | ||
526 | + qemu_deliver_packet(packet->sender, packet->data, packet->size); | ||
527 | + qemu_free(packet); | ||
528 | + } | ||
529 | + vlan->delivering = 0; | ||
503 | } | 530 | } |
504 | 531 | ||
505 | return max_len; | 532 | return max_len; |