Commit | Line | Data |
---|---|---|
15637ed4 RG |
1 | /*- |
2 | * Copyright (c) 1991 The Regents of the University of California. | |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. All advertising materials mentioning features or use of this software | |
14 | * must display the following acknowledgement: | |
15 | * This product includes software developed by the University of | |
16 | * California, Berkeley and its contributors. | |
17 | * 4. Neither the name of the University nor the names of its contributors | |
18 | * may be used to endorse or promote products derived from this software | |
19 | * without specific prior written permission. | |
20 | * | |
21 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
22 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
25 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
26 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
27 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
28 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
29 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
30 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
31 | * SUCH DAMAGE. | |
32 | * | |
43371f85 | 33 | * from: @(#)tp_subr.c 7.9 (Berkeley) 6/27/91 |
4c45483e | 34 | * $Id: tp_subr.c,v 1.4 1993/11/07 17:50:02 wollman Exp $ |
15637ed4 RG |
35 | */ |
36 | ||
37 | /*********************************************************** | |
38 | Copyright IBM Corporation 1987 | |
39 | ||
40 | All Rights Reserved | |
41 | ||
42 | Permission to use, copy, modify, and distribute this software and its | |
43 | documentation for any purpose and without fee is hereby granted, | |
44 | provided that the above copyright notice appear in all copies and that | |
45 | both that copyright notice and this permission notice appear in | |
46 | supporting documentation, and that the name of IBM not be | |
47 | used in advertising or publicity pertaining to distribution of the | |
48 | software without specific, written prior permission. | |
49 | ||
50 | IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING | |
51 | ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL | |
52 | IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR | |
53 | ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, | |
54 | WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, | |
55 | ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS | |
56 | SOFTWARE. | |
57 | ||
58 | ******************************************************************/ | |
59 | ||
60 | /* | |
61 | * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison | |
62 | */ | |
43371f85 | 63 | |
15637ed4 RG |
64 | /* |
65 | * ARGO TP | |
66 | * | |
15637ed4 RG |
67 | * The main work of data transfer is done here. |
68 | * These routines are called from tp.trans. | |
69 | * They include the routines that check the validity of acks and Xacks, | |
70 | * (tp_goodack() and tp_goodXack() ) | |
71 | * take packets from socket buffers and send them (tp_send()), | |
72 | * drop the data from the socket buffers (tp_sbdrop()), | |
73 | * and put incoming packet data into socket buffers (tp_stash()). | |
74 | */ | |
75 | ||
76 | #include "param.h" | |
5d3c1d60 | 77 | #include "systm.h" |
15637ed4 RG |
78 | #include "mbuf.h" |
79 | #include "socket.h" | |
80 | #include "socketvar.h" | |
81 | #include "protosw.h" | |
82 | #include "errno.h" | |
83 | #include "types.h" | |
84 | #include "time.h" | |
85 | ||
86 | #include "tp_ip.h" | |
87 | #include "iso.h" | |
88 | #include "argo_debug.h" | |
89 | #include "tp_timer.h" | |
90 | #include "tp_param.h" | |
91 | #include "tp_stat.h" | |
92 | #include "tp_pcb.h" | |
93 | #include "tp_tpdu.h" | |
94 | #include "tp_trace.h" | |
95 | #include "tp_meas.h" | |
96 | #include "tp_seq.h" | |
8ace4366 GW |
97 | #include "tp_clnp.h" |
98 | ||
99 | struct isopcb tp_isopcb; | |
100 | struct inpcb tp_inpcb; | |
101 | u_int tp_start_win; | |
102 | struct tp_stat tp_stat; | |
103 | #ifndef TP_PERF_MEAS | |
104 | int PStat_Junk; | |
105 | #endif | |
106 | #ifdef TPPT | |
107 | int tp_Tracen = 0; | |
108 | #endif | |
109 | ||
110 | ||
15637ed4 RG |
111 | |
112 | int tp_emit(); | |
113 | static void tp_sbdrop(); | |
114 | ||
115 | #define SMOOTH(newtype, alpha, old, new) \ | |
116 | (newtype) (((new - old)>>alpha) + (old)) | |
117 | ||
118 | #define ABS(type, val) \ | |
119 | (type) (((int)(val)<0)?-(val):(val)) | |
120 | ||
121 | #define TP_MAKE_RTC( Xreg, Xseq, Xeot, Xdata, Xlen, Xretval, Xtype) \ | |
122 | { struct mbuf *xxn;\ | |
123 | MGET(xxn, M_DONTWAIT, Xtype);\ | |
124 | if( xxn == (struct mbuf *)0 ) {\ | |
125 | printf("MAKE RTC FAILED: ENOBUFS\n");\ | |
126 | return (int)Xretval;\ | |
127 | }\ | |
128 | xxn->m_act=MNULL;\ | |
129 | Xreg = mtod(xxn, struct tp_rtc *);\ | |
130 | if( Xreg == (struct tp_rtc *)0 ) {\ | |
131 | return (int)Xretval;\ | |
132 | }\ | |
133 | Xreg->tprt_eot = Xeot;\ | |
134 | Xreg->tprt_seq = Xseq;\ | |
135 | Xreg->tprt_data = Xdata;\ | |
136 | Xreg->tprt_octets = Xlen;\ | |
137 | } | |
138 | ||
139 | ||
140 | /* | |
141 | * CALLED FROM: | |
142 | * tp.trans, when an XAK arrives | |
143 | * FUNCTION and ARGUMENTS: | |
144 | * Determines if the sequence number (seq) from the XAK | |
145 | * acks anything new. If so, drop the appropriate tpdu | |
146 | * from the XPD send queue. | |
147 | * RETURN VALUE: | |
148 | * Returns 1 if it did this, 0 if the ack caused no action. | |
149 | */ | |
150 | int | |
151 | tp_goodXack(tpcb, seq) | |
152 | struct tp_pcb *tpcb; | |
153 | SeqNum seq; | |
154 | { | |
155 | ||
156 | IFTRACE(D_XPD) | |
157 | tptraceTPCB(TPPTgotXack, | |
158 | seq, tpcb->tp_Xuna, tpcb->tp_Xsndnxt, tpcb->tp_sndhiwat, | |
159 | tpcb->tp_snduna); | |
160 | ENDTRACE | |
161 | ||
162 | if ( seq == tpcb->tp_Xuna ) { | |
163 | tpcb->tp_Xuna = tpcb->tp_Xsndnxt; | |
164 | ||
165 | /* DROP 1 packet from the Xsnd socket buf - just so happens | |
166 | * that only one packet can be there at any time | |
167 | * so drop the whole thing. If you allow > 1 packet | |
168 | * the socket buffer, then you'll have to keep | |
169 | * track of how many characters went w/ each XPD tpdu, so this | |
170 | * will get messier | |
171 | */ | |
172 | IFDEBUG(D_XPD) | |
173 | dump_mbuf(tpcb->tp_Xsnd.sb_mb, | |
174 | "tp_goodXack Xsnd before sbdrop"); | |
175 | ENDDEBUG | |
176 | ||
177 | IFTRACE(D_XPD) | |
178 | tptraceTPCB(TPPTmisc, | |
179 | "goodXack: dropping cc ", | |
180 | (int)(tpcb->tp_Xsnd.sb_cc), | |
181 | 0,0,0); | |
182 | ENDTRACE | |
183 | sbdrop( &tpcb->tp_Xsnd, (int)(tpcb->tp_Xsnd.sb_cc)); | |
184 | CONG_ACK(tpcb, seq); | |
185 | return 1; | |
186 | } | |
187 | return 0; | |
188 | } | |
189 | ||
190 | /* | |
191 | * CALLED FROM: | |
192 | * tp_good_ack() | |
193 | * FUNCTION and ARGUMENTS: | |
194 | * updates | |
195 | * smoothed average round trip time (base_rtt) | |
196 | * roundtrip time variance (base_rtv) - actually deviation, not variance | |
197 | * given the new value (diff) | |
198 | * RETURN VALUE: | |
199 | * void | |
200 | */ | |
201 | ||
202 | void | |
203 | tp_rtt_rtv( base_rtt, base_rtv, newmeas ) | |
204 | struct timeval *base_rtt, *base_rtv, *newmeas; | |
205 | { | |
206 | /* update rt variance (really just the deviation): | |
207 | * rtv.smooth_ave = SMOOTH( | oldrtt.smooth_avg - rtt.this_instance | ) | |
208 | */ | |
209 | base_rtv->tv_sec = | |
210 | SMOOTH( long, TP_RTV_ALPHA, base_rtv->tv_sec, | |
211 | ABS( long, base_rtt->tv_sec - newmeas->tv_sec )); | |
212 | base_rtv->tv_usec = | |
213 | SMOOTH( long, TP_RTV_ALPHA, base_rtv->tv_usec, | |
214 | ABS(long, base_rtt->tv_usec - newmeas->tv_usec )); | |
215 | ||
216 | /* update smoothed average rtt */ | |
217 | base_rtt->tv_sec = | |
218 | SMOOTH( long, TP_RTT_ALPHA, base_rtt->tv_sec, newmeas->tv_sec); | |
219 | base_rtt->tv_usec = | |
220 | SMOOTH( long, TP_RTT_ALPHA, base_rtt->tv_usec, newmeas->tv_usec); | |
221 | ||
222 | } | |
223 | ||
224 | /* | |
225 | * CALLED FROM: | |
226 | * tp.trans when an AK arrives | |
227 | * FUNCTION and ARGUMENTS: | |
228 | * Given (cdt), the credit from the AK tpdu, and | |
229 | * (seq), the sequence number from the AK tpdu, | |
230 | * tp_goodack() determines if the AK acknowledges something in the send | |
231 | * window, and if so, drops the appropriate packets from the retransmission | |
232 | * list, computes the round trip time, and updates the retransmission timer | |
233 | * based on the new smoothed round trip time. | |
234 | * RETURN VALUE: | |
235 | * Returns 1 if | |
236 | * EITHER it actually acked something heretofore unacknowledged | |
237 | * OR no news but the credit should be processed. | |
238 | * If something heretofore unacked was acked with this sequence number, | |
239 | * the appropriate tpdus are dropped from the retransmission control list, | |
240 | * by calling tp_sbdrop(). | |
241 | * No need to see the tpdu itself. | |
242 | */ | |
243 | int | |
244 | tp_goodack(tpcb, cdt, seq, subseq) | |
245 | register struct tp_pcb *tpcb; | |
246 | u_int cdt; | |
247 | register SeqNum seq, subseq; | |
248 | { | |
249 | int old_fcredit = tpcb->tp_fcredit; | |
250 | int bang = 0; /* bang --> ack for something heretofore unacked */ | |
251 | ||
252 | IFDEBUG(D_ACKRECV) | |
253 | printf("goodack seq 0x%x cdt 0x%x snduna 0x%x sndhiwat 0x%x\n", | |
254 | seq, cdt, tpcb->tp_snduna, tpcb->tp_sndhiwat); | |
255 | ENDDEBUG | |
256 | IFTRACE(D_ACKRECV) | |
257 | tptraceTPCB(TPPTgotack, | |
258 | seq,cdt, tpcb->tp_snduna,tpcb->tp_sndhiwat,subseq); | |
259 | ENDTRACE | |
260 | ||
261 | IFPERF(tpcb) | |
262 | tpmeas(tpcb->tp_lref, TPtime_ack_rcvd, (struct timeval *)0, seq, 0, 0); | |
263 | ENDPERF | |
264 | ||
265 | if ( subseq != 0 && (subseq <= tpcb->tp_r_subseq) ) { | |
266 | /* discard the ack */ | |
267 | IFTRACE(D_ACKRECV) | |
268 | tptraceTPCB(TPPTmisc, "goodack discard : subseq tp_r_subseq", | |
269 | subseq, tpcb->tp_r_subseq, 0, 0); | |
270 | ENDTRACE | |
271 | return 0; | |
272 | } else { | |
273 | tpcb->tp_r_subseq = subseq; | |
274 | } | |
275 | ||
276 | if ( IN_SWINDOW(tpcb, seq, | |
277 | tpcb->tp_snduna, SEQ(tpcb, tpcb->tp_sndhiwat+1)) ) { | |
278 | ||
279 | IFDEBUG(D_XPD) | |
280 | dump_mbuf(tpcb->tp_sock->so_snd.sb_mb, | |
281 | "tp_goodack snd before sbdrop"); | |
282 | ENDDEBUG | |
283 | tp_sbdrop(tpcb, SEQ_SUB(tpcb, seq, 1) ); | |
284 | ||
285 | /* increase congestion window but don't let it get too big */ | |
286 | { | |
287 | register int maxcdt = tpcb->tp_xtd_format?0xffff:0xf; | |
288 | CONG_ACK(tpcb, seq); | |
289 | } | |
290 | ||
291 | /* Compute smoothed round trip time. | |
292 | * Only measure rtt for tp_snduna if tp_snduna was among | |
293 | * the last TP_RTT_NUM seq numbers sent, and if the data | |
294 | * were not retransmitted. | |
295 | */ | |
296 | if (SEQ_GEQ(tpcb, tpcb->tp_snduna, | |
297 | SEQ(tpcb, tpcb->tp_sndhiwat - TP_RTT_NUM)) | |
298 | && SEQ_GT(tpcb, seq, SEQ_ADD(tpcb, tpcb->tp_retrans_hiwat, 1))) { | |
299 | ||
300 | struct timeval *t = &tpcb->tp_rttemit[tpcb->tp_snduna & TP_RTT_NUM]; | |
301 | struct timeval x; | |
302 | ||
303 | GET_TIME_SINCE(t, &x); | |
304 | ||
305 | tp_rtt_rtv( &(tpcb->tp_rtt), &(tpcb->tp_rtv), &x ); | |
306 | ||
307 | { /* update the global rtt, rtv stats */ | |
308 | register int i = | |
309 | (int) tpcb->tp_flags & (TPF_PEER_ON_SAMENET | TPF_NLQOS_PDN); | |
310 | tp_rtt_rtv( &(tp_stat.ts_rtt[i]), &(tp_stat.ts_rtv[i]), &x ); | |
311 | ||
312 | IFTRACE(D_RTT) | |
313 | tptraceTPCB(TPPTmisc, "Global rtt, rtv: i", i, 0, 0, 0); | |
314 | ENDTRACE | |
315 | } | |
316 | ||
317 | IFTRACE(D_RTT) | |
318 | tptraceTPCB(TPPTmisc, | |
319 | "Smoothed rtt: tp_snduna, (time.sec, time.usec), peer_acktime", | |
320 | tpcb->tp_snduna, time.tv_sec, time.tv_usec, | |
321 | tpcb->tp_peer_acktime); | |
322 | ||
323 | tptraceTPCB(TPPTmisc, | |
324 | "(secs): emittime diff(x) rtt, rtv", | |
325 | t->tv_sec, | |
326 | x.tv_sec, | |
327 | tpcb->tp_rtt.tv_sec, | |
328 | tpcb->tp_rtv.tv_sec); | |
329 | tptraceTPCB(TPPTmisc, | |
330 | "(usecs): emittime diff(x) rtt rtv", | |
331 | t->tv_usec, | |
332 | x.tv_usec, | |
333 | tpcb->tp_rtt.tv_usec, | |
334 | tpcb->tp_rtv.tv_usec); | |
335 | ENDTRACE | |
336 | ||
337 | { | |
338 | /* Update data retransmission timer based on the smoothed | |
339 | * round trip time, peer ack time, and the pseudo-arbitrary | |
340 | * number 4. | |
341 | * new ticks: avg rtt + 2*dev | |
342 | * rtt, rtv are in microsecs, and ticks are 500 ms | |
343 | * so 1 tick = 500*1000 us = 500000 us | |
344 | * so ticks = (rtt + 2 rtv)/500000 | |
345 | * with ticks no les than peer ack time and no less than 4 | |
346 | */ | |
347 | ||
348 | int rtt = tpcb->tp_rtt.tv_usec + | |
349 | tpcb->tp_rtt.tv_sec*1000000; | |
350 | int rtv = tpcb->tp_rtv.tv_usec + | |
351 | tpcb->tp_rtv.tv_sec*1000000; | |
352 | ||
353 | IFTRACE(D_RTT) | |
354 | tptraceTPCB(TPPTmisc, "oldticks ,rtv, rtt, newticks", | |
355 | tpcb->tp_dt_ticks, | |
356 | rtv, rtt, | |
357 | (rtt/500000 + (2 * rtv)/500000)); | |
358 | ENDTRACE | |
359 | tpcb->tp_dt_ticks = (rtt+ (2 * rtv))/500000; | |
360 | tpcb->tp_dt_ticks = MAX( tpcb->tp_dt_ticks, | |
361 | tpcb->tp_peer_acktime); | |
362 | tpcb->tp_dt_ticks = MAX( tpcb->tp_dt_ticks, 4); | |
363 | } | |
364 | } | |
365 | tpcb->tp_snduna = seq; | |
366 | tpcb->tp_retrans = tpcb->tp_Nretrans; /* CE_BIT */ | |
367 | ||
368 | bang++; | |
369 | } | |
370 | ||
371 | if( cdt != 0 && old_fcredit == 0 ) { | |
372 | tpcb->tp_sendfcc = 1; | |
373 | } | |
374 | if( cdt == 0 && old_fcredit != 0 ) { | |
375 | IncStat(ts_zfcdt); | |
376 | } | |
377 | tpcb->tp_fcredit = cdt; | |
378 | ||
379 | IFDEBUG(D_ACKRECV) | |
380 | printf("goodack returning 0x%x, bang 0x%x cdt 0x%x old_fcredit 0x%x\n", | |
381 | (bang || (old_fcredit < cdt) ), bang, cdt, old_fcredit ); | |
382 | ENDDEBUG | |
383 | ||
384 | return (bang || (old_fcredit < cdt)) ; | |
385 | } | |
386 | ||
387 | /* | |
388 | * CALLED FROM: | |
389 | * tp_goodack() | |
390 | * FUNCTION and ARGUMENTS: | |
391 | * drops everything up TO and INCLUDING seq # (seq) | |
392 | * from the retransmission queue. | |
393 | */ | |
394 | static void | |
395 | tp_sbdrop(tpcb, seq) | |
396 | struct tp_pcb *tpcb; | |
397 | SeqNum seq; | |
398 | { | |
399 | register struct tp_rtc *s = tpcb->tp_snduna_rtc; | |
400 | ||
401 | IFDEBUG(D_ACKRECV) | |
402 | printf("tp_sbdrop up through seq 0x%x\n", seq); | |
403 | ENDDEBUG | |
404 | while (s != (struct tp_rtc *)0 && (SEQ_LEQ(tpcb, s->tprt_seq, seq))) { | |
405 | m_freem( s->tprt_data ); | |
406 | tpcb->tp_snduna_rtc = s->tprt_next; | |
407 | (void) m_free( dtom( s ) ); | |
408 | s = tpcb->tp_snduna_rtc; | |
409 | } | |
410 | if(tpcb->tp_snduna_rtc == (struct tp_rtc *)0) | |
411 | tpcb->tp_sndhiwat_rtc = (struct tp_rtc *) 0; | |
412 | ||
413 | } | |
414 | ||
415 | /* | |
416 | * CALLED FROM: | |
417 | * tp.trans on user send request, arrival of AK and arrival of XAK | |
418 | * FUNCTION and ARGUMENTS: | |
419 | * Emits tpdus starting at sequence number (lowseq). | |
420 | * Emits until a) runs out of data, or b) runs into an XPD mark, or | |
421 | * c) it hits seq number (highseq) | |
422 | * Removes the octets from the front of the socket buffer | |
423 | * and repackages them in one mbuf chain per tpdu. | |
424 | * Moves the mbuf chain to the doubly linked list that runs from | |
425 | * tpcb->tp_sndhiwat_rtc to tpcb->tp_snduna_rtc. | |
426 | * | |
427 | * Creates tpdus that are no larger than <tpcb->tp_l_tpdusize - headersize>, | |
428 | * | |
429 | * If you want XPD to buffer > 1 du per socket buffer, you can | |
430 | * modifiy this to issue XPD tpdus also, but then it'll have | |
431 | * to take some argument(s) to distinguish between the type of DU to | |
432 | * hand tp_emit, the socket buffer from which to get the data, and | |
433 | * the chain of tp_rtc structures on which to put the data sent. | |
434 | * | |
435 | * When something is sent for the first time, its time-of-send | |
436 | * is stashed (the last RTT_NUM of them are stashed). When the | |
437 | * ack arrives, the smoothed round-trip time is figured using this value. | |
438 | * RETURN VALUE: | |
439 | * the highest seq # sent successfully. | |
440 | */ | |
4c45483e | 441 | int |
15637ed4 RG |
442 | tp_send(tpcb) |
443 | register struct tp_pcb *tpcb; | |
444 | { | |
445 | register int len; | |
446 | register struct mbuf *m; /* the one we're inspecting now */ | |
447 | struct mbuf *mb;/* beginning of this tpdu */ | |
448 | struct mbuf *nextrecord; /* NOT next tpdu but next sb record */ | |
449 | struct sockbuf *sb = &tpcb->tp_sock->so_snd; | |
450 | int maxsize = tpcb->tp_l_tpdusize | |
451 | - tp_headersize(DT_TPDU_type, tpcb) | |
452 | - (tpcb->tp_use_checksum?4:0) ; | |
453 | unsigned int eotsdu_reached=0; | |
454 | SeqNum lowseq, highseq ; | |
455 | SeqNum lowsave; | |
456 | #ifdef TP_PERF_MEAS | |
457 | ||
458 | struct timeval send_start_time; | |
459 | IFPERF(tpcb) | |
460 | GET_CUR_TIME(&send_start_time); | |
461 | ENDPERF | |
462 | #endif TP_PERF_MEAS | |
463 | ||
464 | lowsave = lowseq = SEQ(tpcb, tpcb->tp_sndhiwat + 1); | |
465 | ||
466 | ASSERT( tpcb->tp_cong_win > 0 && tpcb->tp_cong_win < 0xffff); | |
467 | ||
468 | if( tpcb->tp_rx_strat & TPRX_USE_CW ) { | |
469 | /*first hiseq is temp vbl*/ | |
470 | highseq = MIN(tpcb->tp_fcredit, tpcb->tp_cong_win); | |
471 | } else { | |
472 | highseq = tpcb->tp_fcredit; | |
473 | } | |
474 | highseq = SEQ(tpcb, tpcb->tp_snduna + highseq); | |
475 | ||
476 | SEQ_DEC(tpcb, highseq); | |
477 | ||
478 | IFDEBUG(D_DATA) | |
479 | printf( | |
480 | "tp_send enter tpcb 0x%x l %d -> h %d\ndump of sb_mb:\n", | |
481 | tpcb, lowseq, highseq); | |
482 | dump_mbuf(sb->sb_mb, "sb_mb:"); | |
483 | ENDDEBUG | |
484 | IFTRACE(D_DATA) | |
485 | tptraceTPCB( TPPTmisc, "tp_send lowsave sndhiwat snduna", | |
486 | lowsave, tpcb->tp_sndhiwat, tpcb->tp_snduna, 0); | |
487 | tptraceTPCB( TPPTmisc, "tp_send low high fcredit congwin", | |
488 | lowseq, highseq, tpcb->tp_fcredit, tpcb->tp_cong_win); | |
489 | ENDTRACE | |
490 | ||
491 | ||
4c45483e GW |
492 | if( SEQ_GT(tpcb, lowseq, highseq) ) { |
493 | /* don't send, don't change hiwat, don't set timers */ | |
494 | return 0; | |
495 | } | |
15637ed4 RG |
496 | |
497 | ASSERT( SEQ_LEQ(tpcb, lowseq, highseq) ); | |
498 | SEQ_DEC(tpcb, lowseq); | |
499 | ||
500 | IFTRACE(D_DATA) | |
501 | tptraceTPCB( TPPTmisc, "tp_send 2 low high fcredit congwin", | |
502 | lowseq, highseq, tpcb->tp_fcredit, tpcb->tp_cong_win); | |
503 | ENDTRACE | |
504 | ||
505 | while ((SEQ_LT(tpcb, lowseq, highseq)) && (mb = m = sb->sb_mb)) { | |
506 | if (tpcb->tp_Xsnd.sb_mb) { | |
507 | IFTRACE(D_XPD) | |
508 | tptraceTPCB( TPPTmisc, | |
509 | "tp_send XPD mark low high tpcb.Xuna", | |
510 | lowseq, highseq, tpcb->tp_Xsnd.sb_mb, 0); | |
511 | ENDTRACE | |
512 | /* stop sending here because there are unacked XPD which were | |
513 | * given to us before the next data were. | |
514 | */ | |
515 | IncStat(ts_xpd_intheway); | |
516 | break; | |
517 | } | |
518 | eotsdu_reached = 0; | |
519 | nextrecord = m->m_act; | |
520 | for (len = 0; m; m = m->m_next) { | |
521 | len += m->m_len; | |
522 | if (m->m_flags & M_EOR) | |
523 | eotsdu_reached = 1; | |
524 | sbfree(sb, m); /* reduce counts in socket buffer */ | |
525 | } | |
526 | sb->sb_mb = nextrecord; | |
527 | IFTRACE(D_STASH) | |
528 | tptraceTPCB(TPPTmisc, "tp_send whole mbuf: m_len len maxsize", | |
529 | 0, mb->m_len, len, maxsize); | |
530 | ENDTRACE | |
531 | ||
532 | if ( len == 0 && !eotsdu_reached) { | |
533 | /* THIS SHOULD NEVER HAPPEN! */ | |
534 | ASSERT( 0 ); | |
535 | goto done; | |
536 | } | |
537 | ||
538 | /* If we arrive here one of the following holds: | |
539 | * 1. We have exactly <maxsize> octets of whole mbufs, | |
540 | * 2. We accumulated <maxsize> octets using partial mbufs, | |
541 | * 3. We found an TPMT_EOT or an XPD mark | |
542 | * 4. We hit the end of a chain through m_next. | |
543 | * In this case, we'd LIKE to continue with the next record, | |
544 | * but for the time being, for simplicity, we'll stop here. | |
545 | * In all cases, m points to mbuf containing first octet to be | |
546 | * sent in the tpdu AFTER the one we're going to send now, | |
547 | * or else m is null. | |
548 | * | |
549 | * The chain we're working on now begins at mb and has length <len>. | |
550 | */ | |
551 | ||
552 | IFTRACE(D_STASH) | |
553 | tptraceTPCB( TPPTmisc, | |
554 | "tp_send mcopy low high eotsdu_reached len", | |
555 | lowseq, highseq, eotsdu_reached, len); | |
556 | ENDTRACE | |
557 | ||
558 | /* make a copy - mb goes into the retransmission list | |
559 | * while m gets emitted. m_copy won't copy a zero-length mbuf. | |
560 | */ | |
561 | if (len) { | |
562 | if ((m = m_copy(mb, 0, len )) == MNULL) | |
563 | goto done; | |
564 | } else { | |
565 | /* eotsdu reached */ | |
566 | MGET(m, M_WAIT, TPMT_DATA); | |
567 | if (m == MNULL) | |
568 | goto done; | |
569 | m->m_len = 0; | |
570 | } | |
571 | ||
572 | SEQ_INC(tpcb,lowseq); /* it was decremented at the beginning */ | |
573 | { | |
574 | struct tp_rtc *t; | |
575 | /* make an rtc and put it at the end of the chain */ | |
576 | ||
577 | TP_MAKE_RTC( t, lowseq, eotsdu_reached, mb, len, lowseq, | |
578 | TPMT_SNDRTC); | |
579 | t->tprt_next = (struct tp_rtc *)0; | |
580 | ||
581 | if ( tpcb->tp_sndhiwat_rtc != (struct tp_rtc *)0 ) | |
582 | tpcb->tp_sndhiwat_rtc->tprt_next = t; | |
583 | else { | |
584 | ASSERT( tpcb->tp_snduna_rtc == (struct tp_rtc *)0 ); | |
585 | tpcb->tp_snduna_rtc = t; | |
586 | } | |
587 | ||
588 | tpcb->tp_sndhiwat_rtc = t; | |
589 | } | |
590 | ||
591 | IFTRACE(D_DATA) | |
592 | tptraceTPCB( TPPTmisc, | |
593 | "tp_send emitting DT lowseq eotsdu_reached len", | |
594 | lowseq, eotsdu_reached, len, 0); | |
595 | ENDTRACE | |
596 | if( tpcb->tp_sock->so_error = | |
597 | tp_emit(DT_TPDU_type, tpcb, lowseq, eotsdu_reached, m) ) { | |
598 | /* error */ | |
599 | SEQ_DEC(tpcb, lowseq); | |
600 | goto done; | |
601 | } | |
602 | /* set the transmit-time for computation of round-trip times */ | |
603 | bcopy( (caddr_t)&time, | |
604 | (caddr_t)&( tpcb->tp_rttemit[ lowseq & TP_RTT_NUM ] ), | |
605 | sizeof(struct timeval)); | |
606 | ||
607 | } | |
608 | ||
609 | done: | |
610 | #ifdef TP_PERF_MEAS | |
611 | IFPERF(tpcb) | |
612 | { | |
613 | register int npkts; | |
614 | struct timeval send_end_time; | |
615 | register struct timeval *t; | |
616 | ||
617 | npkts = lowseq; | |
618 | SEQ_INC(tpcb, npkts); | |
619 | npkts = SEQ_SUB(tpcb, npkts, lowsave); | |
620 | ||
621 | if(npkts > 0) | |
622 | tpcb->tp_Nwindow++; | |
623 | ||
624 | if (npkts > TP_PM_MAX) | |
625 | npkts = TP_PM_MAX; | |
626 | ||
627 | GET_TIME_SINCE(&send_start_time, &send_end_time); | |
628 | t = &(tpcb->tp_p_meas->tps_sendtime[npkts]); | |
629 | t->tv_sec = | |
630 | SMOOTH( long, TP_RTT_ALPHA, t->tv_sec, send_end_time.tv_sec); | |
631 | t->tv_usec = | |
632 | SMOOTH( long, TP_RTT_ALPHA, t->tv_usec, send_end_time.tv_usec); | |
633 | ||
634 | if ( SEQ_LT(tpcb, lowseq, highseq) ) { | |
635 | IncPStat(tpcb, tps_win_lim_by_data[npkts] ); | |
636 | } else { | |
637 | IncPStat(tpcb, tps_win_lim_by_cdt[npkts] ); | |
638 | /* not true with congestion-window being used */ | |
639 | } | |
640 | tpmeas( tpcb->tp_lref, | |
641 | TPsbsend, &send_end_time, lowsave, tpcb->tp_Nwindow, npkts); | |
642 | } | |
643 | ENDPERF | |
644 | #endif TP_PERF_MEAS | |
645 | ||
646 | tpcb->tp_sndhiwat = lowseq; | |
647 | ||
648 | if ( SEQ_LEQ(tpcb, lowsave, tpcb->tp_sndhiwat) && | |
649 | (tpcb->tp_class != TP_CLASS_0) ) | |
650 | tp_etimeout(tpcb->tp_refp, TM_data_retrans, lowsave, | |
651 | tpcb->tp_sndhiwat, | |
652 | (u_int)tpcb->tp_Nretrans, (int)tpcb->tp_dt_ticks); | |
653 | IFTRACE(D_DATA) | |
654 | tptraceTPCB( TPPTmisc, | |
655 | "tp_send at end: sndhiwat lowseq eotsdu_reached error", | |
656 | tpcb->tp_sndhiwat, lowseq, eotsdu_reached, tpcb->tp_sock->so_error); | |
657 | ||
658 | ENDTRACE | |
4c45483e GW |
659 | ; |
660 | return 0; | |
15637ed4 RG |
661 | } |
662 | ||
663 | /* | |
664 | * NAME: tp_stash() | |
665 | * CALLED FROM: | |
666 | * tp.trans on arrival of a DT tpdu | |
667 | * FUNCTION, ARGUMENTS, and RETURN VALUE: | |
668 | * Returns 1 if | |
669 | * a) something new arrived and it's got eotsdu_reached bit on, | |
670 | * b) this arrival was caused other out-of-sequence things to be | |
671 | * accepted, or | |
672 | * c) this arrival is the highest seq # for which we last gave credit | |
673 | * (sender just sent a whole window) | |
674 | * In other words, returns 1 if tp should send an ack immediately, 0 if | |
675 | * the ack can wait a while. | |
676 | * | |
677 | * Note: this implementation no longer renegs on credit, (except | |
678 | * when debugging option D_RENEG is on, for the purpose of testing | |
679 | * ack subsequencing), so we don't need to check for incoming tpdus | |
680 | * being in a reneged portion of the window. | |
681 | */ | |
682 | ||
683 | int | |
684 | tp_stash( tpcb, e ) | |
685 | register struct tp_pcb *tpcb; | |
686 | register struct tp_event *e; | |
687 | { | |
688 | register int ack_reason= tpcb->tp_ack_strat & ACK_STRAT_EACH; | |
689 | /* 0--> delay acks until full window */ | |
690 | /* 1--> ack each tpdu */ | |
691 | int newrec = 0; | |
692 | ||
693 | #ifndef lint | |
694 | #define E e->ATTR(DT_TPDU) | |
695 | #else lint | |
696 | #define E e->ev_union.EV_DT_TPDU | |
697 | #endif lint | |
698 | ||
699 | if ( E.e_eot ) { | |
700 | register struct mbuf *n = E.e_data; | |
701 | n->m_flags |= M_EOR; | |
702 | n->m_act = 0; | |
703 | } | |
704 | IFDEBUG(D_STASH) | |
705 | dump_mbuf(tpcb->tp_sock->so_rcv.sb_mb, | |
706 | "stash: so_rcv before appending"); | |
707 | dump_mbuf(E.e_data, | |
708 | "stash: e_data before appending"); | |
709 | ENDDEBUG | |
710 | ||
711 | IFPERF(tpcb) | |
712 | PStat(tpcb, Nb_from_ll) += E.e_datalen; | |
713 | tpmeas(tpcb->tp_lref, TPtime_from_ll, &e->e_time, | |
714 | E.e_seq, (u_int)PStat(tpcb, Nb_from_ll), (u_int)E.e_datalen); | |
715 | ENDPERF | |
716 | ||
717 | if( E.e_seq == tpcb->tp_rcvnxt ) { | |
718 | ||
719 | IFDEBUG(D_STASH) | |
720 | printf("stash EQ: seq 0x%x datalen 0x%x eot 0x%x\n", | |
721 | E.e_seq, E.e_datalen, E.e_eot); | |
722 | ENDDEBUG | |
723 | ||
724 | IFTRACE(D_STASH) | |
725 | tptraceTPCB(TPPTmisc, "stash EQ: seq len eot", | |
726 | E.e_seq, E.e_datalen, E.e_eot, 0); | |
727 | ENDTRACE | |
728 | ||
729 | sbappend(&tpcb->tp_sock->so_rcv, E.e_data); | |
730 | ||
731 | if (newrec = E.e_eot ) /* ASSIGNMENT */ | |
732 | ack_reason |= ACK_EOT; | |
733 | ||
734 | SEQ_INC( tpcb, tpcb->tp_rcvnxt ); | |
735 | /* | |
736 | * move chains from the rtc list to the socket buffer | |
737 | * and free the rtc header | |
738 | */ | |
739 | { | |
740 | register struct tp_rtc **r = &tpcb->tp_rcvnxt_rtc; | |
741 | register struct tp_rtc *s = tpcb->tp_rcvnxt_rtc; | |
742 | ||
743 | while (s != (struct tp_rtc *)0 && s->tprt_seq == tpcb->tp_rcvnxt) { | |
744 | *r = s->tprt_next; | |
745 | ||
746 | sbappend(&tpcb->tp_sock->so_rcv, s->tprt_data); | |
747 | ||
748 | SEQ_INC( tpcb, tpcb->tp_rcvnxt ); | |
749 | ||
750 | (void) m_free( dtom( s ) ); | |
751 | s = *r; | |
752 | ack_reason |= ACK_REORDER; | |
753 | } | |
754 | } | |
755 | IFDEBUG(D_STASH) | |
756 | dump_mbuf(tpcb->tp_sock->so_rcv.sb_mb, | |
757 | "stash: so_rcv after appending"); | |
758 | ENDDEBUG | |
759 | ||
760 | } else { | |
761 | register struct tp_rtc **s = &tpcb->tp_rcvnxt_rtc; | |
762 | register struct tp_rtc *r = tpcb->tp_rcvnxt_rtc; | |
763 | register struct tp_rtc *t; | |
764 | ||
765 | IFTRACE(D_STASH) | |
766 | tptraceTPCB(TPPTmisc, "stash Reseq: seq rcvnxt lcdt", | |
767 | E.e_seq, tpcb->tp_rcvnxt, tpcb->tp_lcredit, 0); | |
768 | ENDTRACE | |
769 | ||
770 | r = tpcb->tp_rcvnxt_rtc; | |
771 | while (r != (struct tp_rtc *)0 && SEQ_LT(tpcb, r->tprt_seq, E.e_seq)) { | |
772 | s = &r->tprt_next; | |
773 | r = r->tprt_next; | |
774 | } | |
775 | ||
776 | if (r == (struct tp_rtc *)0 || SEQ_GT(tpcb, r->tprt_seq, E.e_seq) ) { | |
777 | IncStat(ts_dt_ooo); | |
778 | ||
779 | IFTRACE(D_STASH) | |
780 | tptrace(TPPTmisc, | |
781 | "tp_stash OUT OF ORDER- MAKE RTC: seq, 1st seq in list\n", | |
782 | E.e_seq, r->tprt_seq,0,0); | |
783 | ENDTRACE | |
784 | IFDEBUG(D_STASH) | |
785 | printf("tp_stash OUT OF ORDER- MAKE RTC\n"); | |
786 | ENDDEBUG | |
787 | TP_MAKE_RTC(t, E.e_seq, E.e_eot, E.e_data, E.e_datalen, 0, | |
788 | TPMT_RCVRTC); | |
789 | ||
790 | *s = t; | |
791 | t->tprt_next = (struct tp_rtc *)r; | |
792 | ack_reason = ACK_DONT; | |
793 | goto done; | |
794 | } else { | |
795 | IFDEBUG(D_STASH) | |
796 | printf("tp_stash - drop & ack\n"); | |
797 | ENDDEBUG | |
798 | ||
799 | /* retransmission - drop it and force an ack */ | |
800 | IncStat(ts_dt_dup); | |
801 | IFPERF(tpcb) | |
802 | IncPStat(tpcb, tps_n_ack_cuz_dup); | |
803 | ENDPERF | |
804 | ||
805 | m_freem( E.e_data ); | |
806 | ack_reason |= ACK_DUP; | |
807 | goto done; | |
808 | } | |
809 | } | |
810 | ||
811 | ||
812 | /* | |
813 | * an ack should be sent when at least one of the | |
814 | * following holds: | |
815 | * a) we've received a TPDU with EOTSDU set | |
816 | * b) the TPDU that just arrived represents the | |
817 | * full window last advertised, or | |
818 | * c) when seq X arrives [ where | |
819 | * X = last_sent_uwe - 1/2 last_lcredit_sent | |
820 | * (the packet representing 1/2 the last advertised window) ] | |
821 | * and lcredit at the time of X arrival > last_lcredit_sent/2 | |
822 | * In other words, if the last ack sent advertised cdt=8 and uwe = 8 | |
823 | * then when seq 4 arrives I'd like to send a new ack | |
824 | * iff the credit at the time of 4's arrival is > 4. | |
825 | * The other end thinks it has cdt of 4 so if local cdt | |
826 | * is still 4 there's no point in sending an ack, but if | |
827 | * my credit has increased because the receiver has taken | |
828 | * some data out of the buffer (soreceive doesn't notify | |
829 | * me until the SYSTEM CALL finishes), I'd like to tell | |
830 | * the other end. | |
831 | */ | |
832 | ||
833 | done: | |
834 | { | |
835 | LOCAL_CREDIT(tpcb); | |
836 | ||
837 | if ( E.e_seq == tpcb->tp_sent_uwe ) | |
838 | ack_reason |= ACK_STRAT_FULLWIN; | |
839 | ||
840 | IFTRACE(D_STASH) | |
841 | tptraceTPCB(TPPTmisc, | |
842 | "end of stash, eot, ack_reason, sent_uwe ", | |
843 | E.e_eot, ack_reason, tpcb->tp_sent_uwe, 0); | |
844 | ENDTRACE | |
845 | ||
846 | if ( ack_reason == ACK_DONT ) { | |
847 | IncStat( ts_ackreason[ACK_DONT] ); | |
848 | return 0; | |
849 | } else { | |
850 | IFPERF(tpcb) | |
851 | if(ack_reason & ACK_EOT) { | |
852 | IncPStat(tpcb, tps_n_ack_cuz_eot); | |
853 | } | |
854 | if(ack_reason & ACK_STRAT_EACH) { | |
855 | IncPStat(tpcb, tps_n_ack_cuz_strat); | |
856 | } else if(ack_reason & ACK_STRAT_FULLWIN) { | |
857 | IncPStat(tpcb, tps_n_ack_cuz_fullwin); | |
858 | } else if(ack_reason & ACK_REORDER) { | |
859 | IncPStat(tpcb, tps_n_ack_cuz_reorder); | |
860 | } | |
861 | tpmeas(tpcb->tp_lref, TPtime_ack_sent, 0, | |
862 | SEQ_ADD(tpcb, E.e_seq, 1), 0, 0); | |
863 | ENDPERF | |
864 | { | |
865 | register int i; | |
866 | ||
867 | /* keep track of all reasons that apply */ | |
868 | for( i=1; i<_ACK_NUM_REASONS_ ;i++) { | |
869 | if( ack_reason & (1<<i) ) | |
870 | IncStat( ts_ackreason[i] ); | |
871 | } | |
872 | } | |
873 | return 1; | |
874 | } | |
875 | } | |
876 | } |