]> Git Repo - qemu.git/blob - slirp/if.c
Merge remote-tracking branch 'riku/linux-user-for-upstream' into staging
[qemu.git] / slirp / if.c
1 /*
2  * Copyright (c) 1995 Danny Gasparovski.
3  *
4  * Please read the file COPYRIGHT for the
5  * terms and conditions of the copyright.
6  */
7
8 #include <slirp.h>
9 #include "qemu-timer.h"
10
11 #define ifs_init(ifm) ((ifm)->ifs_next = (ifm)->ifs_prev = (ifm))
12
13 static void
14 ifs_insque(struct mbuf *ifm, struct mbuf *ifmhead)
15 {
16         ifm->ifs_next = ifmhead->ifs_next;
17         ifmhead->ifs_next = ifm;
18         ifm->ifs_prev = ifmhead;
19         ifm->ifs_next->ifs_prev = ifm;
20 }
21
22 static void
23 ifs_remque(struct mbuf *ifm)
24 {
25         ifm->ifs_prev->ifs_next = ifm->ifs_next;
26         ifm->ifs_next->ifs_prev = ifm->ifs_prev;
27 }
28
29 void
30 if_init(Slirp *slirp)
31 {
32     slirp->if_fastq.ifq_next = slirp->if_fastq.ifq_prev = &slirp->if_fastq;
33     slirp->if_batchq.ifq_next = slirp->if_batchq.ifq_prev = &slirp->if_batchq;
34     slirp->next_m = &slirp->if_batchq;
35 }
36
37 /*
38  * if_output: Queue packet into an output queue.
39  * There are 2 output queue's, if_fastq and if_batchq.
40  * Each output queue is a doubly linked list of double linked lists
41  * of mbufs, each list belonging to one "session" (socket).  This
42  * way, we can output packets fairly by sending one packet from each
43  * session, instead of all the packets from one session, then all packets
44  * from the next session, etc.  Packets on the if_fastq get absolute
45  * priority, but if one session hogs the link, it gets "downgraded"
46  * to the batchq until it runs out of packets, then it'll return
47  * to the fastq (eg. if the user does an ls -alR in a telnet session,
48  * it'll temporarily get downgraded to the batchq)
49  */
50 void
51 if_output(struct socket *so, struct mbuf *ifm)
52 {
53         Slirp *slirp = ifm->slirp;
54         struct mbuf *ifq;
55         int on_fastq = 1;
56
57         DEBUG_CALL("if_output");
58         DEBUG_ARG("so = %lx", (long)so);
59         DEBUG_ARG("ifm = %lx", (long)ifm);
60
61         /*
62          * First remove the mbuf from m_usedlist,
63          * since we're gonna use m_next and m_prev ourselves
64          * XXX Shouldn't need this, gotta change dtom() etc.
65          */
66         if (ifm->m_flags & M_USEDLIST) {
67                 remque(ifm);
68                 ifm->m_flags &= ~M_USEDLIST;
69         }
70
71         /*
72          * See if there's already a batchq list for this session.
73          * This can include an interactive session, which should go on fastq,
74          * but gets too greedy... hence it'll be downgraded from fastq to batchq.
75          * We mustn't put this packet back on the fastq (or we'll send it out of order)
76          * XXX add cache here?
77          */
78         for (ifq = slirp->if_batchq.ifq_prev; ifq != &slirp->if_batchq;
79              ifq = ifq->ifq_prev) {
80                 if (so == ifq->ifq_so) {
81                         /* A match! */
82                         ifm->ifq_so = so;
83                         ifs_insque(ifm, ifq->ifs_prev);
84                         goto diddit;
85                 }
86         }
87
88         /* No match, check which queue to put it on */
89         if (so && (so->so_iptos & IPTOS_LOWDELAY)) {
90                 ifq = slirp->if_fastq.ifq_prev;
91                 on_fastq = 1;
92                 /*
93                  * Check if this packet is a part of the last
94                  * packet's session
95                  */
96                 if (ifq->ifq_so == so) {
97                         ifm->ifq_so = so;
98                         ifs_insque(ifm, ifq->ifs_prev);
99                         goto diddit;
100                 }
101         } else
102                 ifq = slirp->if_batchq.ifq_prev;
103
104         /* Create a new doubly linked list for this session */
105         ifm->ifq_so = so;
106         ifs_init(ifm);
107         insque(ifm, ifq);
108
109 diddit:
110         slirp->if_queued++;
111
112         if (so) {
113                 /* Update *_queued */
114                 so->so_queued++;
115                 so->so_nqueued++;
116                 /*
117                  * Check if the interactive session should be downgraded to
118                  * the batchq.  A session is downgraded if it has queued 6
119                  * packets without pausing, and at least 3 of those packets
120                  * have been sent over the link
121                  * (XXX These are arbitrary numbers, probably not optimal..)
122                  */
123                 if (on_fastq && ((so->so_nqueued >= 6) &&
124                                  (so->so_nqueued - so->so_queued) >= 3)) {
125
126                         /* Remove from current queue... */
127                         remque(ifm->ifs_next);
128
129                         /* ...And insert in the new.  That'll teach ya! */
130                         insque(ifm->ifs_next, &slirp->if_batchq);
131                 }
132         }
133
134 #ifndef FULL_BOLT
135         /*
136          * This prevents us from malloc()ing too many mbufs
137          */
138         if_start(ifm->slirp);
139 #endif
140 }
141
142 /*
143  * Send a packet
144  * We choose a packet based on it's position in the output queues;
145  * If there are packets on the fastq, they are sent FIFO, before
146  * everything else.  Otherwise we choose the first packet from the
147  * batchq and send it.  the next packet chosen will be from the session
148  * after this one, then the session after that one, and so on..  So,
149  * for example, if there are 3 ftp session's fighting for bandwidth,
150  * one packet will be sent from the first session, then one packet
151  * from the second session, then one packet from the third, then back
152  * to the first, etc. etc.
153  */
154 void
155 if_start(Slirp *slirp)
156 {
157     uint64_t now = qemu_get_clock_ns(rt_clock);
158     int requeued = 0;
159         struct mbuf *ifm, *ifqt;
160
161         DEBUG_CALL("if_start");
162
163         if (slirp->if_queued == 0)
164            return; /* Nothing to do */
165
166  again:
167         /* check if we can really output */
168         if (!slirp_can_output(slirp->opaque))
169             return;
170
171         /*
172          * See which queue to get next packet from
173          * If there's something in the fastq, select it immediately
174          */
175         if (slirp->if_fastq.ifq_next != &slirp->if_fastq) {
176                 ifm = slirp->if_fastq.ifq_next;
177         } else {
178                 /* Nothing on fastq, see if next_m is valid */
179                 if (slirp->next_m != &slirp->if_batchq)
180                    ifm = slirp->next_m;
181                 else
182                    ifm = slirp->if_batchq.ifq_next;
183
184                 /* Set which packet to send on next iteration */
185                 slirp->next_m = ifm->ifq_next;
186         }
187         /* Remove it from the queue */
188         ifqt = ifm->ifq_prev;
189         remque(ifm);
190         slirp->if_queued--;
191
192         /* If there are more packets for this session, re-queue them */
193         if (ifm->ifs_next != /* ifm->ifs_prev != */ ifm) {
194                 insque(ifm->ifs_next, ifqt);
195                 ifs_remque(ifm);
196         }
197
198         /* Update so_queued */
199         if (ifm->ifq_so) {
200                 if (--ifm->ifq_so->so_queued == 0)
201                    /* If there's no more queued, reset nqueued */
202                    ifm->ifq_so->so_nqueued = 0;
203         }
204
205         if (ifm->expiration_date < now) {
206             /* Expired */
207             m_free(ifm);
208         } else {
209             /* Encapsulate the packet for sending */
210             if (if_encap(slirp, ifm)) {
211                 m_free(ifm);
212             } else {
213                 /* re-queue */
214                 insque(ifm, ifqt);
215                 requeued++;
216             }
217         }
218
219         if (slirp->if_queued)
220            goto again;
221
222         slirp->if_queued = requeued;
223 }
This page took 0.037191 seconds and 4 git commands to generate.