Skip to content

Commit

Permalink
slirp: Fix requeuing of batchq packets in if_start
Browse files Browse the repository at this point in the history
In case we requeued a packet that was the head of a longer session
queue, we failed to restore this ordering. Also, we did not properly
deal with changes to Slirp::next_m.

Instead of a cumbersome roll back, this fix simply avoids any changes
until we know if the packet was actually sent. Both fixes crashes due
to inconsistent queues and simplifies the logic.

Thanks to Zhi Yong Wu who found the reason for these crashes.

CC: Zhi Yong Wu <[email protected]>
CC: Fabien Chouteau <[email protected]>
Signed-off-by: Jan Kiszka <[email protected]>
  • Loading branch information
jan-kiszka committed Feb 27, 2012
1 parent 79e7e93 commit b248ede
Showing 1 changed file with 19 additions and 16 deletions.
35 changes: 19 additions & 16 deletions slirp/if.c
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ if_start(Slirp *slirp)
{
uint64_t now = qemu_get_clock_ns(rt_clock);
int requeued = 0;
bool from_batchq = false;
struct mbuf *ifm, *ifqt;

DEBUG_CALL("if_start");
Expand All @@ -179,13 +180,26 @@ if_start(Slirp *slirp)
else
ifm = slirp->if_batchq.ifq_next;

/* Set which packet to send on next iteration */
slirp->next_m = ifm->ifq_next;
from_batchq = true;
}

slirp->if_queued--;

/* Try to send packet unless it already expired */
if (ifm->expiration_date >= now && !if_encap(slirp, ifm)) {
/* Packet is delayed due to pending ARP resolution */
requeued++;
goto out;
}

if (from_batchq) {
/* Set which packet to send on next iteration */
slirp->next_m = ifm->ifq_next;
}

/* Remove it from the queue */
ifqt = ifm->ifq_prev;
remque(ifm);
slirp->if_queued--;

/* If there are more packets for this session, re-queue them */
if (ifm->ifs_next != /* ifm->ifs_prev != */ ifm) {
Expand All @@ -200,20 +214,9 @@ if_start(Slirp *slirp)
ifm->ifq_so->so_nqueued = 0;
}

if (ifm->expiration_date < now) {
/* Expired */
m_free(ifm);
} else {
/* Encapsulate the packet for sending */
if (if_encap(slirp, ifm)) {
m_free(ifm);
} else {
/* re-queue */
insque(ifm, ifqt);
requeued++;
}
}
m_free(ifm);

out:
if (slirp->if_queued)
goto again;

Expand Down

0 comments on commit b248ede

Please sign in to comment.