Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Set htb burst and cburst size to the minimum useful size. #194

Open
wants to merge 4 commits into
base: master
Choose a base branch
from

Conversation

JonathanLennox
Copy link

@JonathanLennox JonathanLennox commented Feb 10, 2025

This avoids problems where bursts of data can be sent at line speed rather than the limited speed.

Work around a bug in tc where burst values are triply-rounded.

Work around a bug in tc where burst values are triply-rounded.
Copy link
Owner

@thombashi thombashi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Please execute make fmt
  • Could you add type annotations? The existing functions do not have type annotations, but I plan to add type annotations to newly added code to improve maintainability

# Note that "tc class show" will still show burst that are too small,
# due to the inverse bug, but if you use "tc -r class show" you will see
# the actual size of the cbuffer in bytes.
mtu = 1600
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MTU may vary depending on the environment.
Would a fixed value of 1600 be appropriate?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the value that tc sets as its default when it does this calculation. It's overridable from the command line, but tcconfig doesn't override it. Maybe I should also add it as a parameter for tcconfig?

Comment on lines 24 to 37
# Emulation of tc's buggy time2tick implementation, which
# rounds to int twice
def time2tick_bug(time):
return int(int(time) * tick_in_usec)

# An accurate implementation of tick2time (unlike tc's), not rounding.
def tick2time_true(tick):
return tick / tick_in_usec

def calc_xmittime_bug(rate, size):
return int(time2tick_bug(TIME_UNITS_PER_SEC * (float(size) / rate)))

def calc_xmitsize_true(rate, ticks):
return (float(rate)*tick2time_true(ticks))/TIME_UNITS_PER_SEC
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add some simple unit tests to these functions?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The difficulty here is that tick_in_usec is a system-dependent value, so it's hard to write reproducible tests for these functions.

I could override it in the tests, but that's scribbling on the inside of pyroute2's internal state, which seems fraught at best?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alternately, I could add an extra parameter to the function to allow me to pass in an external value of tick_in_usec for testing, but then the tests don't use the same code path as the production code...

# the actual size of the cbuffer in bytes.
mtu = 1600
rate = bandwidth.byte_per_sec
desired_burst = int(rate / get_hz() + mtu)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the reason for adding mtu in this calculation to provide a minimum guaranteed value?
If so, the following seems appropriate:

Suggested change
desired_burst = int(rate / get_hz() + mtu)
desired_burst = max(int(rate / get_hz()), mtu)

Copy link
Author

@JonathanLennox JonathanLennox Feb 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the default burst size calculation that tc tries to set, see its source. (Unfortunately the value it sets is then mangled by tc's buggy calc_xmittime implementation.)

The comment there is

        /* compute minimal allowed burst from rate; mtu is added here to make
           sure that buffer is larger than mtu and to have some safeguard space */

so I followed what they implemented.

Comment on lines 146 to 147
if calc_xmitsize_true(rate, calc_xmittime_bug(rate, burst)) >= desired_burst:
break
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you be sure that this calculation will not go into an infinite loop for any value?
Wouldn't it be safer to set a maximum number of loops?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've switched it to use bisect_left (following the inspiration of https://fanchenbao.medium.com/full-powered-binary-search-with-bisect-in-python-3-10-fb4a76110746) which is O(log(n)) and guaranteed to halt.

@JonathanLennox
Copy link
Author

Note that there are lint errors in files I didn't touch.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants