diff options
Diffstat (limited to 'linden/indra/llmessage/llthrottle.cpp')
-rw-r--r-- | linden/indra/llmessage/llthrottle.cpp | 561 |
1 files changed, 561 insertions, 0 deletions
diff --git a/linden/indra/llmessage/llthrottle.cpp b/linden/indra/llmessage/llthrottle.cpp new file mode 100644 index 0000000..87dc929 --- /dev/null +++ b/linden/indra/llmessage/llthrottle.cpp | |||
@@ -0,0 +1,561 @@ | |||
1 | /** | ||
2 | * @file llthrottle.cpp | ||
3 | * @brief LLThrottle class used for network bandwidth control. | ||
4 | * | ||
5 | * Copyright (c) 2001-2007, Linden Research, Inc. | ||
6 | * | ||
7 | * The source code in this file ("Source Code") is provided by Linden Lab | ||
8 | * to you under the terms of the GNU General Public License, version 2.0 | ||
9 | * ("GPL"), unless you have obtained a separate licensing agreement | ||
10 | * ("Other License"), formally executed by you and Linden Lab. Terms of | ||
11 | * the GPL can be found in doc/GPL-license.txt in this distribution, or | ||
12 | * online at http://secondlife.com/developers/opensource/gplv2 | ||
13 | * | ||
14 | * There are special exceptions to the terms and conditions of the GPL as | ||
15 | * it is applied to this Source Code. View the full text of the exception | ||
16 | * in the file doc/FLOSS-exception.txt in this software distribution, or | ||
17 | * online at http://secondlife.com/developers/opensource/flossexception | ||
18 | * | ||
19 | * By copying, modifying or distributing this software, you acknowledge | ||
20 | * that you have read and understood your obligations described above, | ||
21 | * and agree to abide by those obligations. | ||
22 | * | ||
23 | * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO | ||
24 | * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, | ||
25 | * COMPLETENESS OR PERFORMANCE. | ||
26 | */ | ||
27 | |||
28 | #include "linden_common.h" | ||
29 | |||
30 | #include "llthrottle.h" | ||
31 | #include "llmath.h" | ||
32 | #include "lldatapacker.h" | ||
33 | #include "message.h" | ||
34 | |||
35 | |||
36 | LLThrottle::LLThrottle(const F32 rate) | ||
37 | { | ||
38 | mRate = rate; | ||
39 | mAvailable = 0.f; | ||
40 | mLookaheadSecs = 0.25f; | ||
41 | mLastSendTime = LLMessageSystem::getMessageTimeSeconds(TRUE); | ||
42 | } | ||
43 | |||
44 | |||
45 | void LLThrottle::setRate(const F32 rate) | ||
46 | { | ||
47 | // Need to accumulate available bits when adjusting the rate. | ||
48 | mAvailable = getAvailable(); | ||
49 | mLastSendTime = LLMessageSystem::getMessageTimeSeconds(); | ||
50 | mRate = rate; | ||
51 | } | ||
52 | |||
53 | F32 LLThrottle::getAvailable() | ||
54 | { | ||
55 | // use a temporary bits_available | ||
56 | // since we don't want to change mBitsAvailable every time | ||
57 | F32 elapsed_time = (F32)(LLMessageSystem::getMessageTimeSeconds() - mLastSendTime); | ||
58 | return mAvailable + (mRate * elapsed_time); | ||
59 | } | ||
60 | |||
61 | BOOL LLThrottle::checkOverflow(const F32 amount) | ||
62 | { | ||
63 | BOOL retval = TRUE; | ||
64 | |||
65 | F32 lookahead_amount = mRate * mLookaheadSecs; | ||
66 | |||
67 | // use a temporary bits_available | ||
68 | // since we don't want to change mBitsAvailable every time | ||
69 | F32 elapsed_time = (F32)(LLMessageSystem::getMessageTimeSeconds() - mLastSendTime); | ||
70 | F32 amount_available = mAvailable + (mRate * elapsed_time); | ||
71 | |||
72 | if ((amount_available >= lookahead_amount) || (amount_available > amount)) | ||
73 | { | ||
74 | // ...enough space to send this message | ||
75 | // Also do if > lookahead so we can use if amount > capped amount. | ||
76 | retval = FALSE; | ||
77 | } | ||
78 | |||
79 | return retval; | ||
80 | } | ||
81 | |||
82 | BOOL LLThrottle::throttleOverflow(const F32 amount) | ||
83 | { | ||
84 | F32 elapsed_time; | ||
85 | F32 lookahead_amount; | ||
86 | BOOL retval = TRUE; | ||
87 | |||
88 | lookahead_amount = mRate * mLookaheadSecs; | ||
89 | |||
90 | F64 mt_sec = LLMessageSystem::getMessageTimeSeconds(); | ||
91 | elapsed_time = (F32)(mt_sec - mLastSendTime); | ||
92 | mLastSendTime = mt_sec; | ||
93 | |||
94 | mAvailable += mRate * elapsed_time; | ||
95 | |||
96 | if (mAvailable >= lookahead_amount) | ||
97 | { | ||
98 | // ...channel completely open, so allow send regardless | ||
99 | // of size. This allows sends on very low BPS channels. | ||
100 | mAvailable = lookahead_amount; | ||
101 | retval = FALSE; | ||
102 | } | ||
103 | else if (mAvailable > amount) | ||
104 | { | ||
105 | // ...enough space to send this message | ||
106 | retval = FALSE; | ||
107 | } | ||
108 | |||
109 | // We actually already sent the bits. | ||
110 | mAvailable -= amount; | ||
111 | |||
112 | // What if bitsavailable goes negative? | ||
113 | // That's OK, because it means someone is banging on the channel, | ||
114 | // so we need some time to recover. | ||
115 | |||
116 | return retval; | ||
117 | } | ||
118 | |||
119 | |||
120 | |||
121 | const F32 THROTTLE_LOOKAHEAD_TIME = 1.f; // seconds | ||
122 | |||
123 | // Make sure that we don't set above these | ||
124 | // values, even if the client asks to be set | ||
125 | // higher | ||
126 | // Note that these values are replicated on the | ||
127 | // client side to set max bandwidth throttling there, | ||
128 | // in llviewerthrottle.cpp. These values are the sum | ||
129 | // of the top two tiers of bandwidth there. | ||
130 | |||
131 | F32 gThrottleMaximumBPS[TC_EOF] = | ||
132 | { | ||
133 | 150000.f, // TC_RESEND | ||
134 | 170000.f, // TC_LAND | ||
135 | 34000.f, // TC_WIND | ||
136 | 34000.f, // TC_CLOUD | ||
137 | 446000.f, // TC_TASK | ||
138 | 446000.f, // TC_TEXTURE | ||
139 | 220000.f, // TC_ASSET | ||
140 | }; | ||
141 | |||
142 | // Start low until viewer informs us of capability | ||
143 | // Asset and resend get high values, since they | ||
144 | // aren't used JUST by the viewer necessarily. | ||
145 | // This is a HACK and should be dealt with more properly on | ||
146 | // circuit creation. | ||
147 | |||
148 | F32 gThrottleDefaultBPS[TC_EOF] = | ||
149 | { | ||
150 | 100000.f, // TC_RESEND | ||
151 | 4000.f, // TC_LAND | ||
152 | 4000.f, // TC_WIND | ||
153 | 4000.f, // TC_CLOUD | ||
154 | 4000.f, // TC_TASK | ||
155 | 4000.f, // TC_TEXTURE | ||
156 | 100000.f, // TC_ASSET | ||
157 | }; | ||
158 | |||
159 | // Don't throttle down lower than this | ||
160 | // This potentially wastes 50 kbps, but usually | ||
161 | // wont. | ||
162 | F32 gThrottleMinimumBPS[TC_EOF] = | ||
163 | { | ||
164 | 10000.f, // TC_RESEND | ||
165 | 10000.f, // TC_LAND | ||
166 | 4000.f, // TC_WIND | ||
167 | 4000.f, // TC_CLOUD | ||
168 | 20000.f, // TC_TASK | ||
169 | 10000.f, // TC_TEXTURE | ||
170 | 10000.f, // TC_ASSET | ||
171 | }; | ||
172 | |||
173 | const char* THROTTLE_NAMES[TC_EOF] = | ||
174 | { | ||
175 | "Resend ", | ||
176 | "Land ", | ||
177 | "Wind ", | ||
178 | "Cloud ", | ||
179 | "Task ", | ||
180 | "Texture", | ||
181 | "Asset " | ||
182 | }; | ||
183 | |||
184 | LLThrottleGroup::LLThrottleGroup() | ||
185 | { | ||
186 | S32 i; | ||
187 | for (i = 0; i < TC_EOF; i++) | ||
188 | { | ||
189 | mThrottleTotal[i] = gThrottleDefaultBPS[i]; | ||
190 | mNominalBPS[i] = gThrottleDefaultBPS[i]; | ||
191 | } | ||
192 | |||
193 | resetDynamicAdjust(); | ||
194 | } | ||
195 | |||
196 | void LLThrottleGroup::packThrottle(LLDataPacker &dp) const | ||
197 | { | ||
198 | S32 i; | ||
199 | for (i = 0; i < TC_EOF; i++) | ||
200 | { | ||
201 | dp.packF32(mThrottleTotal[i], "Throttle"); | ||
202 | } | ||
203 | } | ||
204 | |||
205 | void LLThrottleGroup::unpackThrottle(LLDataPacker &dp) | ||
206 | { | ||
207 | S32 i; | ||
208 | for (i = 0; i < TC_EOF; i++) | ||
209 | { | ||
210 | F32 temp_throttle; | ||
211 | dp.unpackF32(temp_throttle, "Throttle"); | ||
212 | temp_throttle = llclamp(temp_throttle, 0.f, 2250000.f); | ||
213 | mThrottleTotal[i] = temp_throttle; | ||
214 | if(mThrottleTotal[i] > gThrottleMaximumBPS[i]) | ||
215 | { | ||
216 | mThrottleTotal[i] = gThrottleMaximumBPS[i]; | ||
217 | } | ||
218 | } | ||
219 | } | ||
220 | |||
221 | // Call this whenever mNominalBPS changes. Need to reset | ||
222 | // the measurement systems. In the future, we should look | ||
223 | // into NOT resetting the system. | ||
224 | void LLThrottleGroup::resetDynamicAdjust() | ||
225 | { | ||
226 | F64 mt_sec = LLMessageSystem::getMessageTimeSeconds(); | ||
227 | S32 i; | ||
228 | for (i = 0; i < TC_EOF; i++) | ||
229 | { | ||
230 | mCurrentBPS[i] = mNominalBPS[i]; | ||
231 | mBitsAvailable[i] = mNominalBPS[i] * THROTTLE_LOOKAHEAD_TIME; | ||
232 | mLastSendTime[i] = mt_sec; | ||
233 | mBitsSentThisPeriod[i] = 0; | ||
234 | mBitsSentHistory[i] = 0; | ||
235 | } | ||
236 | mDynamicAdjustTime = mt_sec; | ||
237 | } | ||
238 | |||
239 | |||
240 | BOOL LLThrottleGroup::setNominalBPS(F32* throttle_vec) | ||
241 | { | ||
242 | BOOL changed = FALSE; | ||
243 | S32 i; | ||
244 | for (i = 0; i < TC_EOF; i++) | ||
245 | { | ||
246 | if (mNominalBPS[i] != throttle_vec[i]) | ||
247 | { | ||
248 | changed = TRUE; | ||
249 | mNominalBPS[i] = throttle_vec[i]; | ||
250 | } | ||
251 | } | ||
252 | |||
253 | // If we changed the nominal settings, reset the dynamic | ||
254 | // adjustment subsystem. | ||
255 | if (changed) | ||
256 | { | ||
257 | resetDynamicAdjust(); | ||
258 | } | ||
259 | |||
260 | return changed; | ||
261 | } | ||
262 | |||
263 | |||
264 | BOOL LLThrottleGroup::checkOverflow(S32 throttle_cat, F32 bits) | ||
265 | { | ||
266 | BOOL retval = TRUE; | ||
267 | |||
268 | F32 category_bps = mCurrentBPS[throttle_cat]; | ||
269 | F32 lookahead_bits = category_bps * THROTTLE_LOOKAHEAD_TIME; | ||
270 | |||
271 | // use a temporary bits_available | ||
272 | // since we don't want to change mBitsAvailable every time | ||
273 | F32 elapsed_time = (F32)(LLMessageSystem::getMessageTimeSeconds() - mLastSendTime[throttle_cat]); | ||
274 | F32 bits_available = mBitsAvailable[throttle_cat] + (category_bps * elapsed_time); | ||
275 | |||
276 | if (bits_available >= lookahead_bits) | ||
277 | { | ||
278 | // ...channel completely open, so allow send regardless | ||
279 | // of size. This allows sends on very low BPS channels. | ||
280 | mBitsAvailable[throttle_cat] = lookahead_bits; | ||
281 | retval = FALSE; | ||
282 | } | ||
283 | else if ( bits_available > bits ) | ||
284 | { | ||
285 | // ...enough space to send this message | ||
286 | retval = FALSE; | ||
287 | } | ||
288 | |||
289 | return retval; | ||
290 | } | ||
291 | |||
292 | BOOL LLThrottleGroup::throttleOverflow(S32 throttle_cat, F32 bits) | ||
293 | { | ||
294 | F32 elapsed_time; | ||
295 | F32 category_bps; | ||
296 | F32 lookahead_bits; | ||
297 | BOOL retval = TRUE; | ||
298 | |||
299 | category_bps = mCurrentBPS[throttle_cat]; | ||
300 | lookahead_bits = category_bps * THROTTLE_LOOKAHEAD_TIME; | ||
301 | |||
302 | F64 mt_sec = LLMessageSystem::getMessageTimeSeconds(); | ||
303 | elapsed_time = (F32)(mt_sec - mLastSendTime[throttle_cat]); | ||
304 | mLastSendTime[throttle_cat] = mt_sec; | ||
305 | mBitsAvailable[throttle_cat] += category_bps * elapsed_time; | ||
306 | |||
307 | if (mBitsAvailable[throttle_cat] >= lookahead_bits) | ||
308 | { | ||
309 | // ...channel completely open, so allow send regardless | ||
310 | // of size. This allows sends on very low BPS channels. | ||
311 | mBitsAvailable[throttle_cat] = lookahead_bits; | ||
312 | retval = FALSE; | ||
313 | } | ||
314 | else if ( mBitsAvailable[throttle_cat] > bits ) | ||
315 | { | ||
316 | // ...enough space to send this message | ||
317 | retval = FALSE; | ||
318 | } | ||
319 | |||
320 | // We actually already sent the bits. | ||
321 | mBitsAvailable[throttle_cat] -= bits; | ||
322 | |||
323 | mBitsSentThisPeriod[throttle_cat] += bits; | ||
324 | |||
325 | // What if bitsavailable goes negative? | ||
326 | // That's OK, because it means someone is banging on the channel, | ||
327 | // so we need some time to recover. | ||
328 | |||
329 | return retval; | ||
330 | } | ||
331 | |||
332 | |||
333 | BOOL LLThrottleGroup::dynamicAdjust() | ||
334 | { | ||
335 | const F32 DYNAMIC_ADJUST_TIME = 1.0f; // seconds | ||
336 | const F32 CURRENT_PERIOD_WEIGHT = .25f; // how much weight to give to last period while determining BPS utilization | ||
337 | const F32 BUSY_PERCENT = 0.75f; // if use more than this fraction of BPS, you are busy | ||
338 | const F32 IDLE_PERCENT = 0.70f; // if use less than this fraction, you are "idle" | ||
339 | const F32 TRANSFER_PERCENT = 0.90f; // how much unused bandwidth to take away each adjustment | ||
340 | const F32 RECOVER_PERCENT = 0.25f; // how much to give back during recovery phase | ||
341 | |||
342 | S32 i; | ||
343 | |||
344 | F64 mt_sec = LLMessageSystem::getMessageTimeSeconds(); | ||
345 | |||
346 | // Only dynamically adjust every few seconds | ||
347 | if ((mt_sec - mDynamicAdjustTime) < DYNAMIC_ADJUST_TIME) | ||
348 | { | ||
349 | return FALSE; | ||
350 | } | ||
351 | mDynamicAdjustTime = mt_sec; | ||
352 | |||
353 | S32 total = 0; | ||
354 | // Update historical information | ||
355 | for (i = 0; i < TC_EOF; i++) | ||
356 | { | ||
357 | if (mBitsSentHistory[i] == 0) | ||
358 | { | ||
359 | // first run, just copy current period | ||
360 | mBitsSentHistory[i] = mBitsSentThisPeriod[i]; | ||
361 | } | ||
362 | else | ||
363 | { | ||
364 | // have some history, so weight accordingly | ||
365 | mBitsSentHistory[i] = (1.f - CURRENT_PERIOD_WEIGHT) * mBitsSentHistory[i] | ||
366 | + CURRENT_PERIOD_WEIGHT * mBitsSentThisPeriod[i]; | ||
367 | } | ||
368 | |||
369 | mBitsSentThisPeriod[i] = 0; | ||
370 | total += llround(mBitsSentHistory[i]); | ||
371 | } | ||
372 | |||
373 | // Look for busy channels | ||
374 | // TODO: Fold into loop above. | ||
375 | BOOL channels_busy = FALSE; | ||
376 | F32 busy_nominal_sum = 0; | ||
377 | BOOL channel_busy[TC_EOF]; | ||
378 | BOOL channel_idle[TC_EOF]; | ||
379 | BOOL channel_over_nominal[TC_EOF]; | ||
380 | |||
381 | for (i = 0; i < TC_EOF; i++) | ||
382 | { | ||
383 | // Is this a busy channel? | ||
384 | if (mBitsSentHistory[i] >= BUSY_PERCENT * DYNAMIC_ADJUST_TIME * mCurrentBPS[i]) | ||
385 | { | ||
386 | // this channel is busy | ||
387 | channels_busy = TRUE; | ||
388 | busy_nominal_sum += mNominalBPS[i]; // use for allocation of pooled idle bandwidth | ||
389 | channel_busy[i] = TRUE; | ||
390 | } | ||
391 | else | ||
392 | { | ||
393 | channel_busy[i] = FALSE; | ||
394 | } | ||
395 | |||
396 | // Is this an idle channel? | ||
397 | if ((mBitsSentHistory[i] < IDLE_PERCENT * DYNAMIC_ADJUST_TIME * mCurrentBPS[i]) && | ||
398 | (mBitsAvailable[i] > 0)) | ||
399 | { | ||
400 | channel_idle[i] = TRUE; | ||
401 | } | ||
402 | else | ||
403 | { | ||
404 | channel_idle[i] = FALSE; | ||
405 | } | ||
406 | |||
407 | // Is this an overpumped channel? | ||
408 | if (mCurrentBPS[i] > mNominalBPS[i]) | ||
409 | { | ||
410 | channel_over_nominal[i] = TRUE; | ||
411 | } | ||
412 | else | ||
413 | { | ||
414 | channel_over_nominal[i] = FALSE; | ||
415 | } | ||
416 | |||
417 | //if (total) | ||
418 | //{ | ||
419 | // llinfos << i << ": B" << channel_busy[i] << " I" << channel_idle[i] << " N" << channel_over_nominal[i]; | ||
420 | // llcont << " Nom: " << mNominalBPS[i] << " Cur: " << mCurrentBPS[i] << " BS: " << mBitsSentHistory[i] << llendl; | ||
421 | //} | ||
422 | } | ||
423 | |||
424 | if (channels_busy) | ||
425 | { | ||
426 | // Some channels are busy. Let's see if we can get them some bandwidth. | ||
427 | F32 used_bps; | ||
428 | F32 avail_bps; | ||
429 | F32 transfer_bps; | ||
430 | |||
431 | F32 pool_bps = 0; | ||
432 | |||
433 | for (i = 0; i < TC_EOF; i++) | ||
434 | { | ||
435 | if (channel_idle[i] || channel_over_nominal[i] ) | ||
436 | { | ||
437 | // Either channel i is idle, or has been overpumped. | ||
438 | // Therefore it's a candidate to give up some bandwidth. | ||
439 | // Figure out how much bandwidth it has been using, and how | ||
440 | // much is available to steal. | ||
441 | used_bps = mBitsSentHistory[i] / DYNAMIC_ADJUST_TIME; | ||
442 | |||
443 | // CRO make sure to keep a minimum amount of throttle available | ||
444 | // CRO NB: channels set to < MINIMUM_BPS will never give up bps, | ||
445 | // which is correct I think | ||
446 | if (used_bps < gThrottleMinimumBPS[i]) | ||
447 | { | ||
448 | used_bps = gThrottleMinimumBPS[i]; | ||
449 | } | ||
450 | |||
451 | if (channel_over_nominal[i]) | ||
452 | { | ||
453 | F32 unused_current = mCurrentBPS[i] - used_bps; | ||
454 | avail_bps = llmax(mCurrentBPS[i] - mNominalBPS[i], unused_current); | ||
455 | } | ||
456 | else | ||
457 | { | ||
458 | avail_bps = mCurrentBPS[i] - used_bps; | ||
459 | } | ||
460 | |||
461 | //llinfos << i << " avail " << avail_bps << llendl; | ||
462 | |||
463 | // Historically, a channel could have used more than its current share, | ||
464 | // even if it's idle right now. | ||
465 | // Make sure we don't steal too much. | ||
466 | if (avail_bps < 0) | ||
467 | { | ||
468 | continue; | ||
469 | } | ||
470 | |||
471 | // Transfer some bandwidth from this channel into the global pool. | ||
472 | transfer_bps = avail_bps * TRANSFER_PERCENT; | ||
473 | mCurrentBPS[i] -= transfer_bps; | ||
474 | pool_bps += transfer_bps; | ||
475 | } | ||
476 | } | ||
477 | |||
478 | //llinfos << "Pool BPS: " << pool_bps << llendl; | ||
479 | // Now redistribute the bandwidth to busy channels. | ||
480 | F32 unused_bps = 0.f; | ||
481 | |||
482 | for (i = 0; i < TC_EOF; i++) | ||
483 | { | ||
484 | if (channel_busy[i]) | ||
485 | { | ||
486 | F32 add_amount = pool_bps * (mNominalBPS[i] / busy_nominal_sum); | ||
487 | //llinfos << "Busy " << i << " gets " << pool_bps << llendl; | ||
488 | mCurrentBPS[i] += add_amount; | ||
489 | |||
490 | // CRO: make sure this doesn't get too huge | ||
491 | // JC - Actually, need to let mCurrentBPS go less than nominal, otherwise | ||
492 | // you aren't allowing bandwidth to actually be moved from one channel | ||
493 | // to another. | ||
494 | // *TODO: If clamping high end, would be good to re- | ||
495 | // allocate to other channels in the above code. | ||
496 | const F32 MAX_BPS = 4 * mNominalBPS[i]; | ||
497 | if (mCurrentBPS[i] > MAX_BPS) | ||
498 | { | ||
499 | F32 overage = mCurrentBPS[i] - MAX_BPS; | ||
500 | mCurrentBPS[i] -= overage; | ||
501 | unused_bps += overage; | ||
502 | } | ||
503 | |||
504 | // Paranoia | ||
505 | if (mCurrentBPS[i] < gThrottleMinimumBPS[i]) | ||
506 | { | ||
507 | mCurrentBPS[i] = gThrottleMinimumBPS[i]; | ||
508 | } | ||
509 | } | ||
510 | } | ||
511 | |||
512 | // For fun, add the overage back in to objects | ||
513 | if (unused_bps > 0.f) | ||
514 | { | ||
515 | mCurrentBPS[TC_TASK] += unused_bps; | ||
516 | } | ||
517 | } | ||
518 | else | ||
519 | { | ||
520 | // No one is busy. | ||
521 | // Make the channel allocations seek toward nominal. | ||
522 | |||
523 | // Look for overpumped channels | ||
524 | F32 starved_nominal_sum = 0; | ||
525 | F32 avail_bps = 0; | ||
526 | F32 transfer_bps = 0; | ||
527 | F32 pool_bps = 0; | ||
528 | for (i = 0; i < TC_EOF; i++) | ||
529 | { | ||
530 | if (mCurrentBPS[i] > mNominalBPS[i]) | ||
531 | { | ||
532 | avail_bps = (mCurrentBPS[i] - mNominalBPS[i]); | ||
533 | transfer_bps = avail_bps * RECOVER_PERCENT; | ||
534 | |||
535 | mCurrentBPS[i] -= transfer_bps; | ||
536 | pool_bps += transfer_bps; | ||
537 | } | ||
538 | } | ||
539 | |||
540 | // Evenly distribute bandwidth to channels currently | ||
541 | // using less than nominal. | ||
542 | for (i = 0; i < TC_EOF; i++) | ||
543 | { | ||
544 | if (mCurrentBPS[i] < mNominalBPS[i]) | ||
545 | { | ||
546 | // We're going to weight allocations by nominal BPS. | ||
547 | starved_nominal_sum += mNominalBPS[i]; | ||
548 | } | ||
549 | } | ||
550 | |||
551 | for (i = 0; i < TC_EOF; i++) | ||
552 | { | ||
553 | if (mCurrentBPS[i] < mNominalBPS[i]) | ||
554 | { | ||
555 | // Distribute bandwidth according to nominal allocation ratios. | ||
556 | mCurrentBPS[i] += pool_bps * (mNominalBPS[i] / starved_nominal_sum); | ||
557 | } | ||
558 | } | ||
559 | } | ||
560 | return TRUE; | ||
561 | } | ||