if.c 6.5 KB
Newer Older
B
bellard 已提交
1 2 3 4 5 6 7 8
/*
 * Copyright (c) 1995 Danny Gasparovski.
 *
 * Please read the file COPYRIGHT for the
 * terms and conditions of the copyright.
 */

#include <slirp.h>
F
Fabien Chouteau 已提交
9
#include "qemu-timer.h"
B
bellard 已提交
10

B
blueswir1 已提交
11
static void
12
ifs_insque(struct mbuf *ifm, struct mbuf *ifmhead)
B
bellard 已提交
13 14 15 16 17 18 19
{
	ifm->ifs_next = ifmhead->ifs_next;
	ifmhead->ifs_next = ifm;
	ifm->ifs_prev = ifmhead;
	ifm->ifs_next->ifs_prev = ifm;
}

B
blueswir1 已提交
20
static void
21
ifs_remque(struct mbuf *ifm)
B
bellard 已提交
22 23 24 25 26 27
{
	ifm->ifs_prev->ifs_next = ifm->ifs_next;
	ifm->ifs_next->ifs_prev = ifm->ifs_prev;
}

void
28
if_init(Slirp *slirp)
B
bellard 已提交
29
{
30 31 32
    slirp->if_fastq.ifq_next = slirp->if_fastq.ifq_prev = &slirp->if_fastq;
    slirp->if_batchq.ifq_next = slirp->if_batchq.ifq_prev = &slirp->if_batchq;
    slirp->next_m = &slirp->if_batchq;
B
bellard 已提交
33 34 35 36
}

/*
 * if_output: Queue packet into an output queue.
37
 * There are 2 output queue's, if_fastq and if_batchq.
B
bellard 已提交
38 39 40 41
 * Each output queue is a doubly linked list of double linked lists
 * of mbufs, each list belonging to one "session" (socket).  This
 * way, we can output packets fairly by sending one packet from each
 * session, instead of all the packets from one session, then all packets
42
 * from the next session, etc.  Packets on the if_fastq get absolute
B
bellard 已提交
43 44 45 46 47 48
 * priority, but if one session hogs the link, it gets "downgraded"
 * to the batchq until it runs out of packets, then it'll return
 * to the fastq (eg. if the user does an ls -alR in a telnet session,
 * it'll temporarily get downgraded to the batchq)
 */
void
49
if_output(struct socket *so, struct mbuf *ifm)
B
bellard 已提交
50
{
51
	Slirp *slirp = ifm->slirp;
B
bellard 已提交
52 53
	struct mbuf *ifq;
	int on_fastq = 1;
54

B
bellard 已提交
55 56 57
	DEBUG_CALL("if_output");
	DEBUG_ARG("so = %lx", (long)so);
	DEBUG_ARG("ifm = %lx", (long)ifm);
58

B
bellard 已提交
59 60 61 62 63 64 65 66 67
	/*
	 * First remove the mbuf from m_usedlist,
	 * since we're gonna use m_next and m_prev ourselves
	 * XXX Shouldn't need this, gotta change dtom() etc.
	 */
	if (ifm->m_flags & M_USEDLIST) {
		remque(ifm);
		ifm->m_flags &= ~M_USEDLIST;
	}
68

B
bellard 已提交
69
	/*
70
	 * See if there's already a batchq list for this session.
B
bellard 已提交
71 72 73 74 75
	 * This can include an interactive session, which should go on fastq,
	 * but gets too greedy... hence it'll be downgraded from fastq to batchq.
	 * We mustn't put this packet back on the fastq (or we'll send it out of order)
	 * XXX add cache here?
	 */
76 77
	for (ifq = slirp->if_batchq.ifq_prev; ifq != &slirp->if_batchq;
	     ifq = ifq->ifq_prev) {
B
bellard 已提交
78 79 80 81 82 83 84
		if (so == ifq->ifq_so) {
			/* A match! */
			ifm->ifq_so = so;
			ifs_insque(ifm, ifq->ifs_prev);
			goto diddit;
		}
	}
85

B
bellard 已提交
86 87
	/* No match, check which queue to put it on */
	if (so && (so->so_iptos & IPTOS_LOWDELAY)) {
88
		ifq = slirp->if_fastq.ifq_prev;
B
bellard 已提交
89 90 91 92 93 94 95 96 97 98
		on_fastq = 1;
		/*
		 * Check if this packet is a part of the last
		 * packet's session
		 */
		if (ifq->ifq_so == so) {
			ifm->ifq_so = so;
			ifs_insque(ifm, ifq->ifs_prev);
			goto diddit;
		}
J
Jan Kiszka 已提交
99
        } else {
100
		ifq = slirp->if_batchq.ifq_prev;
J
Jan Kiszka 已提交
101 102 103 104 105
                /* Set next_m if the queue was empty so far */
                if (slirp->next_m == &slirp->if_batchq) {
                    slirp->next_m = ifm;
                }
        }
106

B
bellard 已提交
107 108 109 110
	/* Create a new doubly linked list for this session */
	ifm->ifq_so = so;
	ifs_init(ifm);
	insque(ifm, ifq);
111

B
bellard 已提交
112
diddit:
113
	slirp->if_queued++;
114

B
bellard 已提交
115 116 117 118 119 120 121 122 123 124 125
	if (so) {
		/* Update *_queued */
		so->so_queued++;
		so->so_nqueued++;
		/*
		 * Check if the interactive session should be downgraded to
		 * the batchq.  A session is downgraded if it has queued 6
		 * packets without pausing, and at least 3 of those packets
		 * have been sent over the link
		 * (XXX These are arbitrary numbers, probably not optimal..)
		 */
126
		if (on_fastq && ((so->so_nqueued >= 6) &&
B
bellard 已提交
127
				 (so->so_nqueued - so->so_queued) >= 3)) {
128

B
bellard 已提交
129 130
			/* Remove from current queue... */
			remque(ifm->ifs_next);
131

B
bellard 已提交
132
			/* ...And insert in the new.  That'll teach ya! */
133
			insque(ifm->ifs_next, &slirp->if_batchq);
B
bellard 已提交
134 135 136 137 138 139 140
		}
	}

#ifndef FULL_BOLT
	/*
	 * This prevents us from malloc()ing too many mbufs
	 */
141
	if_start(ifm->slirp);
B
bellard 已提交
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
#endif
}

/*
 * Send a packet
 * We choose a packet based on it's position in the output queues;
 * If there are packets on the fastq, they are sent FIFO, before
 * everything else.  Otherwise we choose the first packet from the
 * batchq and send it.  the next packet chosen will be from the session
 * after this one, then the session after that one, and so on..  So,
 * for example, if there are 3 ftp session's fighting for bandwidth,
 * one packet will be sent from the first session, then one packet
 * from the second session, then one packet from the third, then back
 * to the first, etc. etc.
 */
J
Jan Kiszka 已提交
157
void if_start(Slirp *slirp)
B
bellard 已提交
158
{
159
    uint64_t now = qemu_get_clock_ns(rt_clock);
F
Fabien Chouteau 已提交
160
    int requeued = 0;
161
    bool from_batchq = false;
J
Jan Kiszka 已提交
162
    struct mbuf *ifm, *ifqt;
163

J
Jan Kiszka 已提交
164
    DEBUG_CALL("if_start");
165

166 167 168 169 170
    if (slirp->if_start_busy) {
        return;
    }
    slirp->if_start_busy = true;

J
Jan Kiszka 已提交
171
    while (slirp->if_queued) {
B
bellard 已提交
172
        /* check if we can really output */
173 174
        if (!slirp_can_output(slirp->opaque)) {
            slirp->if_start_busy = false;
B
bellard 已提交
175
            return;
176
        }
B
bellard 已提交
177

J
Jan Kiszka 已提交
178 179 180 181 182 183 184
        /*
         * See which queue to get next packet from
         * If there's something in the fastq, select it immediately
         */
        if (slirp->if_fastq.ifq_next != &slirp->if_fastq) {
            ifm = slirp->if_fastq.ifq_next;
        } else {
J
Jan Kiszka 已提交
185 186
            /* Nothing on fastq, pick up from batchq via next_m */
            ifm = slirp->next_m;
J
Jan Kiszka 已提交
187 188
            from_batchq = true;
        }
189 190 191 192 193 194 195

        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++;
J
Jan Kiszka 已提交
196
            continue;
197 198 199 200 201 202 203
        }

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

J
Jan Kiszka 已提交
204 205 206
        /* Remove it from the queue */
        ifqt = ifm->ifq_prev;
        remque(ifm);
207

J
Jan Kiszka 已提交
208 209 210 211
        /* If there are more packets for this session, re-queue them */
        if (ifm->ifs_next != ifm) {
            insque(ifm->ifs_next, ifqt);
            ifs_remque(ifm);
J
Jan Kiszka 已提交
212 213 214 215 216 217
            /* Set next_m if the session packet is now the only one on
             * batchq */
            if (ifqt == &slirp->if_batchq &&
                slirp->next_m == &slirp->if_batchq) {
                slirp->next_m = ifm->ifs_next;
            }
J
Jan Kiszka 已提交
218
        }
219

J
Jan Kiszka 已提交
220 221 222 223 224
        /* Update so_queued */
        if (ifm->ifq_so && --ifm->ifq_so->so_queued == 0) {
            /* If there's no more queued, reset nqueued */
            ifm->ifq_so->so_nqueued = 0;
        }
225

226
        m_free(ifm);
B
bellard 已提交
227

J
Jan Kiszka 已提交
228
    }
F
Fabien Chouteau 已提交
229

J
Jan Kiszka 已提交
230
    slirp->if_queued = requeued;
231 232

    slirp->if_start_busy = false;
B
bellard 已提交
233
}