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