use config-host.h instead of config.h
[qemu] / 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
10 int     if_queued = 0;                  /* Number of packets queued so far */
11
12 struct  mbuf if_fastq;                  /* fast queue (for interactive data) */
13 struct  mbuf if_batchq;                 /* queue for non-interactive data */
14 struct  mbuf *next_m;                   /* Pointer to next mbuf to output */
15
16 #define ifs_init(ifm) ((ifm)->ifs_next = (ifm)->ifs_prev = (ifm))
17
18 void
19 ifs_insque(ifm, ifmhead)
20         struct mbuf *ifm, *ifmhead;
21 {
22         ifm->ifs_next = ifmhead->ifs_next;
23         ifmhead->ifs_next = ifm;
24         ifm->ifs_prev = ifmhead;
25         ifm->ifs_next->ifs_prev = ifm;
26 }
27
28 void
29 ifs_remque(ifm)
30         struct mbuf *ifm;
31 {
32         ifm->ifs_prev->ifs_next = ifm->ifs_next;
33         ifm->ifs_next->ifs_prev = ifm->ifs_prev;
34 }
35
36 void
37 if_init()
38 {
39         if_fastq.ifq_next = if_fastq.ifq_prev = &if_fastq;
40         if_batchq.ifq_next = if_batchq.ifq_prev = &if_batchq;
41         //      sl_compress_init(&comp_s);
42         next_m = &if_batchq;
43 }
44
45 #if 0
46 /*
47  * This shouldn't be needed since the modem is blocking and
48  * we don't expect any signals, but what the hell..
49  */
50 inline int
51 writen(fd, bptr, n)
52         int fd;
53         char *bptr;
54         int n;
55 {
56         int ret;
57         int total;
58
59         /* This should succeed most of the time */
60         ret = send(fd, bptr, n,0);
61         if (ret == n || ret <= 0)
62            return ret;
63
64         /* Didn't write everything, go into the loop */
65         total = ret;
66         while (n > total) {
67                 ret = send(fd, bptr+total, n-total,0);
68                 if (ret <= 0)
69                    return ret;
70                 total += ret;
71         }
72         return total;
73 }
74
75 /*
76  * if_input - read() the tty, do "top level" processing (ie: check for any escapes),
77  * and pass onto (*ttyp->if_input)
78  *
79  * XXXXX Any zeros arriving by themselves are NOT placed into the arriving packet.
80  */
81 #define INBUFF_SIZE 2048 /* XXX */
82 void
83 if_input(ttyp)
84         struct ttys *ttyp;
85 {
86         u_char if_inbuff[INBUFF_SIZE];
87         int if_n;
88
89         DEBUG_CALL("if_input");
90         DEBUG_ARG("ttyp = %lx", (long)ttyp);
91
92         if_n = recv(ttyp->fd, (char *)if_inbuff, INBUFF_SIZE,0);
93
94         DEBUG_MISC((dfd, " read %d bytes\n", if_n));
95
96         if (if_n <= 0) {
97                 if (if_n == 0 || (errno != EINTR && errno != EAGAIN)) {
98                         if (ttyp->up)
99                            link_up--;
100                         tty_detached(ttyp, 0);
101                 }
102                 return;
103         }
104         if (if_n == 1) {
105                 if (*if_inbuff == '0') {
106                         ttyp->ones = 0;
107                         if (++ttyp->zeros >= 5)
108                            slirp_exit(0);
109                         return;
110                 }
111                 if (*if_inbuff == '1') {
112                         ttyp->zeros = 0;
113                         if (++ttyp->ones >= 5)
114                            tty_detached(ttyp, 0);
115                         return;
116                 }
117         }
118         ttyp->ones = ttyp->zeros = 0;
119
120         (*ttyp->if_input)(ttyp, if_inbuff, if_n);
121 }
122 #endif
123
124 /*
125  * if_output: Queue packet into an output queue.
126  * There are 2 output queue's, if_fastq and if_batchq.
127  * Each output queue is a doubly linked list of double linked lists
128  * of mbufs, each list belonging to one "session" (socket).  This
129  * way, we can output packets fairly by sending one packet from each
130  * session, instead of all the packets from one session, then all packets
131  * from the next session, etc.  Packets on the if_fastq get absolute
132  * priority, but if one session hogs the link, it gets "downgraded"
133  * to the batchq until it runs out of packets, then it'll return
134  * to the fastq (eg. if the user does an ls -alR in a telnet session,
135  * it'll temporarily get downgraded to the batchq)
136  */
137 void
138 if_output(so, ifm)
139         struct socket *so;
140         struct mbuf *ifm;
141 {
142         struct mbuf *ifq;
143         int on_fastq = 1;
144
145         DEBUG_CALL("if_output");
146         DEBUG_ARG("so = %lx", (long)so);
147         DEBUG_ARG("ifm = %lx", (long)ifm);
148
149         /*
150          * First remove the mbuf from m_usedlist,
151          * since we're gonna use m_next and m_prev ourselves
152          * XXX Shouldn't need this, gotta change dtom() etc.
153          */
154         if (ifm->m_flags & M_USEDLIST) {
155                 remque(ifm);
156                 ifm->m_flags &= ~M_USEDLIST;
157         }
158
159         /*
160          * See if there's already a batchq list for this session.
161          * This can include an interactive session, which should go on fastq,
162          * but gets too greedy... hence it'll be downgraded from fastq to batchq.
163          * We mustn't put this packet back on the fastq (or we'll send it out of order)
164          * XXX add cache here?
165          */
166         for (ifq = if_batchq.ifq_prev; ifq != &if_batchq; ifq = ifq->ifq_prev) {
167                 if (so == ifq->ifq_so) {
168                         /* A match! */
169                         ifm->ifq_so = so;
170                         ifs_insque(ifm, ifq->ifs_prev);
171                         goto diddit;
172                 }
173         }
174
175         /* No match, check which queue to put it on */
176         if (so && (so->so_iptos & IPTOS_LOWDELAY)) {
177                 ifq = if_fastq.ifq_prev;
178                 on_fastq = 1;
179                 /*
180                  * Check if this packet is a part of the last
181                  * packet's session
182                  */
183                 if (ifq->ifq_so == so) {
184                         ifm->ifq_so = so;
185                         ifs_insque(ifm, ifq->ifs_prev);
186                         goto diddit;
187                 }
188         } else
189                 ifq = if_batchq.ifq_prev;
190
191         /* Create a new doubly linked list for this session */
192         ifm->ifq_so = so;
193         ifs_init(ifm);
194         insque(ifm, ifq);
195
196 diddit:
197         ++if_queued;
198
199         if (so) {
200                 /* Update *_queued */
201                 so->so_queued++;
202                 so->so_nqueued++;
203                 /*
204                  * Check if the interactive session should be downgraded to
205                  * the batchq.  A session is downgraded if it has queued 6
206                  * packets without pausing, and at least 3 of those packets
207                  * have been sent over the link
208                  * (XXX These are arbitrary numbers, probably not optimal..)
209                  */
210                 if (on_fastq && ((so->so_nqueued >= 6) &&
211                                  (so->so_nqueued - so->so_queued) >= 3)) {
212
213                         /* Remove from current queue... */
214                         remque(ifm->ifs_next);
215
216                         /* ...And insert in the new.  That'll teach ya! */
217                         insque(ifm->ifs_next, &if_batchq);
218                 }
219         }
220
221 #ifndef FULL_BOLT
222         /*
223          * This prevents us from malloc()ing too many mbufs
224          */
225         if (link_up) {
226                 /* if_start will check towrite */
227                 if_start();
228         }
229 #endif
230 }
231
232 /*
233  * Send a packet
234  * We choose a packet based on it's position in the output queues;
235  * If there are packets on the fastq, they are sent FIFO, before
236  * everything else.  Otherwise we choose the first packet from the
237  * batchq and send it.  the next packet chosen will be from the session
238  * after this one, then the session after that one, and so on..  So,
239  * for example, if there are 3 ftp session's fighting for bandwidth,
240  * one packet will be sent from the first session, then one packet
241  * from the second session, then one packet from the third, then back
242  * to the first, etc. etc.
243  */
244 void
245 if_start(void)
246 {
247         struct mbuf *ifm, *ifqt;
248
249         DEBUG_CALL("if_start");
250
251         if (if_queued == 0)
252            return; /* Nothing to do */
253
254  again:
255         /* check if we can really output */
256         if (!slirp_can_output())
257             return;
258
259         /*
260          * See which queue to get next packet from
261          * If there's something in the fastq, select it immediately
262          */
263         if (if_fastq.ifq_next != &if_fastq) {
264                 ifm = if_fastq.ifq_next;
265         } else {
266                 /* Nothing on fastq, see if next_m is valid */
267                 if (next_m != &if_batchq)
268                    ifm = next_m;
269                 else
270                    ifm = if_batchq.ifq_next;
271
272                 /* Set which packet to send on next iteration */
273                 next_m = ifm->ifq_next;
274         }
275         /* Remove it from the queue */
276         ifqt = ifm->ifq_prev;
277         remque(ifm);
278         --if_queued;
279
280         /* If there are more packets for this session, re-queue them */
281         if (ifm->ifs_next != /* ifm->ifs_prev != */ ifm) {
282                 insque(ifm->ifs_next, ifqt);
283                 ifs_remque(ifm);
284         }
285
286         /* Update so_queued */
287         if (ifm->ifq_so) {
288                 if (--ifm->ifq_so->so_queued == 0)
289                    /* If there's no more queued, reset nqueued */
290                    ifm->ifq_so->so_nqueued = 0;
291         }
292
293         /* Encapsulate the packet for sending */
294         if_encap(ifm->m_data, ifm->m_len);
295
296         m_free(ifm);
297
298         if (if_queued)
299            goto again;
300 }