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