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