1 # rlm.tcl --
2 #
3 # FIXME: This file needs a description here.
4 #
5 # Copyright (c) 1996-2002 The Regents of the University of California.
6 # All rights reserved.
7 #
8 # Redistribution and use in source and binary forms, with or without
9 # modification, are permitted provided that the following conditions are met:
10 #
11 # A. Redistributions of source code must retain the above copyright notice,
12 # this list of conditions and the following disclaimer.
13 # B. Redistributions in binary form must reproduce the above copyright notice,
14 # this list of conditions and the following disclaimer in the documentation
15 # and/or other materials provided with the distribution.
16 # C. Neither the names of the copyright holders nor the names of its
17 # contributors may be used to endorse or promote products derived from this
18 # software without specific prior written permission.
19 #
20 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
21 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 # ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
24 # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 #
31 # @(#) $Header: /usr/mash/src/repository/mash/mash-1/tcl/net/rlm.tcl,v 1.10 2002/02/03 04:28:06 lim Exp $
32
33
34 #
35 # exponential factor for backing off join-timer
36 #
37 set rlm_param(alpha) 4
38 set rlm_param(alpha) 2
39 set rlm_param(beta) 0.75
40 set rlm_param(init-tj) 1.5
41 set rlm_param(init-tj) 10
42 set rlm_param(init-tj) 5
43 set rlm_param(init-td) 5
44 #FIXME
45 #set rlm_param(init-td) 10
46 set rlm_param(init-td-var) 2
47 set rlm_param(max) 600
48 #FIXME
49 set rlm_param(max) 60
50 set rlm_param(g1) 0.25
51 set rlm_param(g2) 0.25
52
53 #FIXME
54 #set rlm_param(target-exp-time) 5
55 #puts stderr "rlm: scaling min-rate by M=$M"
56 #set rlm_param(max) [expr $rlm_param(target-exp-time) * 60 * $M]
57
58 #FIXME
59 #puts stderr "rlm: scaling alpha by M=$M"
60 #set rlm_param(alpha) [expr $rlm_param(alpha) * $M]
61
62 #
63 # The MMG (Multiple Multicast Groups) class implements the RLM
64 # protocol (Receiver-driven Layered Multicast). See
65 # <a href=http://www.cs.berkeley.edu/~mccanne/phd-work/>McCanne's
66 # thesis</a> for a detailed description of RLM.<p>
67 #
68 # This class implements only the basic protocol machinery, it
69 # does not know anything about either ns or mash. MMG is an
70 # abstract class -- you should not instantiate it directly.
71 # Instead, to use RLM a subclass needs to be created that
72 # actually joins and leaves groups, makes upcalls on packet
73 # losses, etc...<p>
74 #
75 # Two such subclasses are implemented at the moment, one for
76 # ns and one for mash. Note that since all code in the MMG
77 # base class is shared between ns and mash, you should not
78 # change anything in this file without being certain that the
79 # changes will work properly in both ns and mash.<p>
80 #
81 # See documentation for the appropriate subclass (i.e., MMG/ns
82 # or MMG/mash) for details about RLM in different environments.
83 Class MMG
84
85 MMG instproc init { levels } {
86 $self next
87
88 $self instvar debug_ env_ maxlevel_
89 set debug_ 0
90 set env_ [lindex [split [$self info class] /] 1]
91 set maxlevel_ $levels
92
93 #FIXME
94 global rlm_debug_flag
95 if [info exists rlm_debug_flag] {
96 set debug_ $rlm_debug_flag
97 }
98
99 $self instvar TD TDVAR state_ subscription_
100 #FIXME
101 global rlm_param
102 set TD $rlm_param(init-td)
103 set TDVAR $rlm_param(init-td-var)
104 set state_ /S
105
106 #
107 # we number the subscription level starting at 1.
108 # level 0 means no groups are subscribed to.
109 #
110 $self instvar layer_ layers_
111 set i 1
112 while { $i <= $maxlevel_ } {
113 set layer_($i) [$self create-layer [expr $i - 1]]
114 lappend layers_ $layer_($i)
115 incr i
116 }
117
118 #
119 # set the subscription level to 0 and call add_layer
120 # to start out with at least one group
121 #
122 set subscription_ 0
123 $self add-layer
124
125 set state_ /S
126
127 #
128 # Schedule the initial join-timer.
129 #
130 $self set_TJ_timer
131 }
132
133 MMG instproc set-state s {
134 $self instvar state_
135 set old $state_
136 set state_ $s
137 $self debug "FSM: $old -> $s"
138 }
139
140 MMG instproc drop-layer {} {
141 $self dumpLevel
142 $self instvar subscription_ layer_
143 set n $subscription_
144
145 #
146 # if we have an active layer, drop it
147 #
148 if { $n > 0 } {
149 $self debug "DRP-LAYER $n"
150 $layer_($n) leave-group
151 incr n -1
152 set subscription_ $n
153 }
154 $self dumpLevel
155 }
156
157 MMG instproc add-layer {} {
158 $self dumpLevel
159 $self instvar maxlevel_ subscription_ layer_
160 set n $subscription_
161 if { $n < $maxlevel_ } {
162 $self debug "ADD-LAYER"
163 incr n
164 set subscription_ $n
165 $layer_($n) join-group
166 }
167 $self dumpLevel
168 }
169
170 MMG instproc current_layer_getting_packets {} {
171 $self instvar subscription_ layer_ TD
172 set n $subscription_
173 if { $n == 0 } {
174 return 0
175 }
176
177 set l $layer_($subscription_)
178 $self debug "npkts [$l npkts]"
179 if [$l getting-pkts] {
180 return 1
181 }
182
183 #FIXME hack to adjust TD for large latency case
184 set delta [expr [$self now] - [$l last-add]]
185 if { $delta > $TD } {
186 set TD [expr 1.2 * $delta]
187 }
188 return 0
189 }
190
191 #
192 # return the amount of loss across all the groups of the given mmg
193 #
194 MMG instproc mmg_loss {} {
195 $self instvar layers_
196 set loss 0
197 foreach l $layers_ {
198 incr loss [$l nlost]
199 }
200 return $loss
201 }
202
203 #
204 # return the number of packets received across all the groups of the given mmg
205 #
206 MMG instproc mmg_pkts {} {
207 $self instvar layers_
208 set npkts 0
209 foreach l $layers_ {
210 incr npkts [$l npkts]
211 }
212 return $npkts
213 }
214
215 #FIXME what is this for?
216 # deleted some code that didn't seem to be used...
217 MMG instproc check-equilibrium {} {
218 global rlm_param
219 $self instvar subscription_ maxlevel_ layer_
220
221 # see if the next higher-level is maxed out
222 set n [expr $subscription_ + 1]
223 if { $n >= $maxlevel_ || [$layer_($n) timer] >= $rlm_param(max) } {
224 set eq 1
225 } else {
226 set eq 0
227 }
228
229 $self debug "EQ $eq"
230 }
231
232 MMG instproc backoff-one { n alpha } {
233 $self debug "BACKOFF $n by $alpha"
234 $self instvar layer_
235 $layer_($n) backoff $alpha
236 }
237
238 MMG instproc backoff n {
239 $self debug "BACKOFF $n"
240 global rlm_param
241 $self instvar maxlevel_ layer_
242 set alpha $rlm_param(alpha)
243 set L $layer_($n)
244 $L backoff $alpha
245 incr n
246 while { $n <= $maxlevel_ } {
247 $layer_($n) peg-backoff $L
248 incr n
249 }
250 $self check-equilibrium
251 }
252
253 MMG instproc highest_level_pending {} {
254 $self instvar maxlevel_
255 set m ""
256 set n 0
257 incr n
258 while { $n <= $maxlevel_ } {
259 if [$self level_pending $n] {
260 set m $n
261 }
262 incr n
263 }
264 return $m
265 }
266
267 MMG instproc rlm_update_D D {
268 #
269 # update detection time estimate
270 #
271 global rlm_param
272 $self instvar TD TDVAR
273
274 set v [expr abs($D - $TD)]
275 set TD [expr $TD * (1 - $rlm_param(g1)) \
276 + $rlm_param(g1) * $D]
277 set TDVAR [expr $TDVAR * (1 - $rlm_param(g2)) \
278 + $rlm_param(g2) * $v]
279 }
280
281 MMG instproc exceed_loss_thresh {} {
282 $self instvar h_npkts h_nlost
283 set npkts [expr [$self mmg_pkts] - $h_npkts]
284 if { $npkts >= 10 } {
285 set nloss [expr [$self mmg_loss] - $h_nlost]
286 #FIXME 0.4
287 set loss [expr double($nloss) / ($nloss + $npkts)]
288 $self debug "H-THRESH $nloss $npkts $loss"
289 if { $loss > 0.25 } {
290 return 1
291 }
292 }
293 return 0
294 }
295
296 MMG instproc enter_M {} {
297 $self set-state /M
298 $self set_TD_timer_wait
299 $self instvar h_npkts h_nlost
300 set h_npkts [$self mmg_pkts]
301 set h_nlost [$self mmg_loss]
302 }
303
304 MMG instproc enter_D {} {
305 $self set-state /D
306 $self set_TD_timer_conservative
307 }
308
309 MMG instproc enter_H {} {
310 $self set_TD_timer_conservative
311 $self set-state /H
312 }
313
314 MMG instproc log-loss {} {
315 $self debug "LOSS [$self mmg_loss]"
316
317 $self instvar state_ subscription_ pending_ts_
318 if { $state_ == "/M" } {
319 if [$self exceed_loss_thresh] {
320 $self cancel_timer TD
321 $self drop-layer
322 $self check-equilibrium
323 $self enter_D
324 }
325 return
326 }
327 if { $state_ == "/S" } {
328 $self cancel_timer TD
329 set n [$self highest_level_pending]
330 if { $n != "" } {
331 #
332 # there is a join-experiment in progress --
333 # back off the join-experiment rate for the
334 # layer that was doing the experiment
335 # if we're at that layer, drop it, and
336 # update the detection time estimator.
337 #
338 $self backoff $n
339 if { $n == $subscription_ } {
340 set ts $pending_ts_($subscription_)
341 $self rlm_update_D [expr [$self now] - $ts]
342 $self drop-layer
343 $self check-equilibrium
344 $self enter_D
345 return
346 }
347 #
348 # If we're at the level just below the experimental
349 # layer that cause a problem, reset our join timer.
350 # The logic is that we just effectively ran an
351 # experiment, so we might as well reset our timer.
352 # This improves the scalability of the algorithm
353 # by limiting the frequency of experiments.
354 #
355 if { $n == [expr $subscription_ + 1] } {
356 $self cancel_timer TJ
357 $self set_TJ_timer
358 }
359 }
360 if [$self our_level_recently_added] {
361 $self enter_M
362 return
363 }
364 $self enter_H
365 return
366 }
367 if { $state_ == "/H" || $state_ == "/D" } {
368 return
369 }
370 puts stderr "rlm state machine botched"
371 exit -1
372 }
373
374 MMG instproc relax_TJ {} {
375 $self instvar subscription_ layer_
376 if { $subscription_ > 0 } {
377 $layer_($subscription_) relax
378 $self check-equilibrium
379 }
380 }
381
382 MMG instproc trigger_TD {} {
383 $self instvar state_
384 if { $state_ == "/H" } {
385 $self enter_M
386 return
387 }
388 if { $state_ == "/D" || $state_ == "/M" } {
389 $self set-state /S
390 $self set_TD_timer_conservative
391 return
392 }
393 if { $state_ == "/S" } {
394 $self relax_TJ
395 $self set_TD_timer_conservative
396 return
397 }
398 puts stderr "trigger_TD: rlm state machine botched $state)"
399 exit -1
400 }
401
402 MMG instproc set_TJ_timer {} {
403 global rlm_param
404 $self instvar subscription_ layer_
405 set n [expr $subscription_ + 1]
406 if ![info exists layer_($n)] {
407 #
408 # no timer -- means we're maximally subscribed
409 #
410 return
411 }
412 set I [$layer_($n) timer]
413 set d [expr $I / 2.0 + [trunc_exponential $I]]
414 $self debug "TJ $d"
415 $self set_timer TJ $d
416 }
417
418 MMG instproc set_TD_timer_conservative {} {
419 $self instvar TD TDVAR
420 set delay [expr $TD + 1.5 * $TDVAR]
421 $self set_timer TD $delay
422 }
423
424 MMG instproc set_TD_timer_wait {} {
425 $self instvar TD TDVAR
426 #FIXME factor of 2?
427 $self instvar subscription_
428 set k [expr $subscription_ / 2. + 1.5]
429 # set k 2
430 $self set_timer TD [expr $TD + $k * $TDVAR]
431 }
432
433 #
434 # Return true iff the time given by $ts is recent enough
435 # such that any action taken since then is likely to influence the
436 # present or future
437 #
438 MMG instproc is-recent { ts } {
439 $self instvar TD TDVAR
440 set ts [expr $ts + ($TD + 2 * $TDVAR)]
441 if { $ts > [$self now] } {
442 return 1
443 }
444 return 0
445 }
446
447 MMG instproc level_pending n {
448 $self instvar pending_ts_
449 if { [info exists pending_ts_($n)] && \
450 [$self is-recent $pending_ts_($n)] } {
451 return 1
452 }
453 return 0
454 }
455
456 MMG instproc level_recently_joined n {
457 $self instvar join_ts_
458 if { [info exists join_ts_($n)] && \
459 [$self is-recent $join_ts_($n)] } {
460 return 1
461 }
462 return 0
463 }
464
465 MMG instproc pending_inferior_jexps {} {
466 set n 0
467 $self instvar subscription_
468 while { $n <= $subscription_ } {
469 if [$self level_recently_joined $n] {
470 return 1
471 }
472 incr n
473 }
474 $self debug "NO-PEND-INF"
475 return 0
476 }
477
478 #
479 # join the next higher layer when in /S
480 #
481 MMG instproc trigger_TJ {} {
482 $self debug "trigger-TJ"
483 $self instvar state_ ctrl_ subscription_
484 if { ($state_ == "/S" && ![$self pending_inferior_jexps] && \
485 [$self current_layer_getting_packets]) } {
486 $self add-layer
487 $self check-equilibrium
488 set msg "add $subscription_"
489 $ctrl_ send $msg
490 #FIXME loop back message
491 $self local-join
492 }
493 $self set_TJ_timer
494 }
495
496 MMG instproc our_level_recently_added {} {
497 $self instvar subscription_ layer_
498 return [$self is-recent [$layer_($subscription_) last-add]]
499 }
500
501
502 MMG instproc recv-ctrl msg {
503 $self instvar join_ts_ pending_ts_ subscription_
504 $self debug "X-JOIN $msg"
505 set what [lindex $msg 0]
506 if { $what != "add" } {
507 #puts RECV/$msg
508 return
509 }
510 set level [lindex $msg 1]
511 #
512 #FIXME
513 # only set the join-ts if the outside J.E. is greater
514 # than our level. if not, then we do not want to falsely
515 # increase the ts of our levels.FIXME say this better.
516 #
517 set join_ts_($level) [$self now]
518 if { $level > $subscription_ } {
519 set pending_ts_($level) [$self now]
520 }
521 }
522
523 MMG instproc local-join {} {
524 $self instvar subscription_ pending_ts_ join_ts_
525 set join_ts_($subscription_) [$self now]
526 set pending_ts_($subscription_) [$self now]
527 }
528
529 MMG instproc debug { msg } {
530 $self instvar debug_ subscription_ state_
531 if {$debug_} {
532 puts stderr "[gettimeofday] layer $subscription_ $state_ $msg"
533 }
534 }
535
536 #FIXME
537 MMG instproc dumpLevel {} {
538 # global rlmTraceFile rates
539 # if [info exists rlmTraceFile] {
540 # $self instvar subscription node rateMap
541 # #FIXME
542 # if ![info exists rateMap] {
543 # set s 0
544 # set rateMap ""
545 # foreach r $rates {
546 # set s [expr $s + $r]
547 # lappend rateMap $s
548 # }
549 # }
550 # set r [lindex $rateMap $subscription]
551 # puts $rlmTraceFile "[$node id] [ns-now] $r"
552 # }
553 }
554
555
556
557 Class Layer
558
559 Layer instproc init { mmg } {
560 $self next
561
562 $self instvar mmg_ TJ npkts_
563 global rlm_param
564 set mmg_ $mmg
565 set TJ $rlm_param(init-tj)
566 set npkts_ 0
567 # loss trace created in constructor of derived class
568 }
569
570 #Layer should relax by beta and not alpha
571 Layer instproc relax {} {
572 global rlm_param
573 $self instvar TJ
574 set TJ [expr $TJ * $rlm_param(beta)]
575 if { $TJ <= $rlm_param(init-tj) } {
576 set TJ $rlm_param(init-tj)
577 }
578 }
579
580 Layer instproc backoff alpha {
581 global rlm_param
582 $self instvar TJ
583 set TJ [expr $TJ * $alpha]
584 if { $TJ >= $rlm_param(max) } {
585 set TJ $rlm_param(max)
586 }
587 }
588
589 Layer instproc peg-backoff L {
590 $self instvar TJ
591 set t [$L set TJ]
592 if { $t >= $TJ } {
593 set TJ $t
594 }
595 }
596
597 Layer instproc timer {} {
598 $self instvar TJ
599 return $TJ
600 }
601
602 Layer instproc last-add {} {
603 $self instvar add_time_
604 return $add_time_
605 }
606
607 Layer instproc join-group {} {
608 $self instvar npkts_ add_time_ mmg_
609 set npkts_ [$self npkts]
610 set add_time_ [$mmg_ now]
611 # derived class actually joins group
612 }
613
614 Layer instproc leave-group {} {
615 # derived class actually leaves group
616 }
617
618 Layer instproc getting-pkts {} {
619 $self instvar npkts_
620 return [expr [$self npkts] != $npkts_]
621 }
622
This page was automatically generated by the
LXR engine.
Visit the LXR main site for more
information.