Page MenuHomeFreeBSD

Fix callout at high tick rates
Needs ReviewPublic

Authored by darius-dons.net.au on Nov 18 2015, 12:57 AM.

Details

Reviewers
None
Group Reviewers
Contributor Reviews (base)
Summary

If kern.hz is >= 596524 this timeout hangs due to an integer overflow (I believe).

Test Plan

Boots in a VM at 596524Hz, can't get to 1MHz though (mps hangs).

Diff Detail

Repository
rS FreeBSD src repository - subversion
Lint
Lint Skipped
Unit
Unit Tests Skipped

Event Timeline

darius-dons.net.au retitled this revision from to Fix callout at high tick rates.
darius-dons.net.au updated this object.
darius-dons.net.au edited the test plan for this revision. (Show Details)
darius-dons.net.au set the repository for this revision to rS FreeBSD src repository - subversion.

Looks like ND6_RECALC_REACHTM_INTERVAL is used to set nd6_recalc_reachtm_interval, which in turn sets recalctm both of which are int's.

Also callout_reset takes an int not a ULL, so not sure this is valid

In D4198#88277, @smh wrote:

Looks like ND6_RECALC_REACHTM_INTERVAL is used to set nd6_recalc_reachtm_interval, which in turn sets recalctm both of which are int's.

Ah OK, I only changed that one because of proximity in the source.

Also callout_reset takes an int not a ULL, so not sure this is valid

Agreed.

I'm not sure what the right solution, but I wanted to point out that callout_reset does _not_ take an int. All the callout_reset functions are macros around callout_reset_sbt_on which takes a sbintime_t, which is in fact 64-bit.

In D4198#88461, @kmacy wrote:

I'm not sure what the right solution, but I wanted to point out that callout_reset does _not_ take an int. All the callout_reset functions are macros around callout_reset_sbt_on which takes a sbintime_t, which is in fact 64-bit.

If that's the case the man timeout(9) may need some work:

int
callout_reset(struct callout *c, int ticks, timeout_t *func, void *arg);
In D4198#88461, @kmacy wrote:

I'm not sure what the right solution, but I wanted to point out that callout_reset does _not_ take an int. All the callout_reset functions are macros around callout_reset_sbt_on which takes a sbintime_t, which is in fact 64-bit.

I think the problem is that hz is an int, eg..

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>

void
foo(int64_t t) {
        printf("%jd\n", (intmax_t)t);
}

int
main(int argc, char **argv) {
        int hz = 596524;

        foo(60 * 60 * hz);

        exit(0);
}

... fails (it prints -2147480896). Changing hz to int64_t or adding LL to any of the 60's prints the correct result (2147486400).

ie all the variables are implicitly converted to int because nothing bigger is used, the calculation overflows, and then callout_reset gets fed a negative value.

... that said I'm not sure why callout_reset doesn't behave more sanely.