NetBSD-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

kern/57719: cargo cult XXXmstohz() idiocy



>Number:         57719
>Category:       kern
>Synopsis:       cargo cult XXXmstohz() idiocy
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    kern-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Wed Nov 22 15:05:00 +0000 2023
>Originator:     Robert Elz
>Release:        NetBSD 10.99.10
>Organization:
>Environment:
	Anything.

>Description:
	There are quite a few XXXmstohz() functions spread throughout the kernel
	source tree - these are more or less mstohz() but deal with the possibility
	that the arg might be 0 or negative (some of them allow -1 with special
	meaning).

	Their general form is (close enough to) always

		int
		XXXmstohz(int m)
		{
			int h = m;  
		 
			if (h > 0) {
				h = h * hz / 1000; 
				if (h == 0)
					h = 1000 / hz;
			} 
			return(h);
		} 

	A minor issue is the use of 'h' as a copy of 'm' - which is not
	necessary, that appears to have originated from the declaration
	of h having been
			regtister int h = m;
	in some earlier versions (and still in at least one of these).
	But that's irrelevant really, the compiler should optimise it
	all away, and (on most architectures) just use a register anyway.

	If h (or m) is > 0, we convert that to ticks, more or less
	open coding mstohz() - that's OK, the h = h * hz / 1000;
	is fine, and is exactly mstohz() for 64 bit architectures.
	We miss the overflow protected version of mstohz() that
	sys/param.h provides for 32 bit architectures (where the
	multiplication, could, perhaps, overflow) but in most of
	these that's unlikely.   Perhaps that line might be converted
	to simply use mstohz() - but that's not the issue here.

	After that, if h == 0, then we know hz < 1000, that's the
	only way that h * hz / 1000 can possibly be 0.

	An example would be if h (or m) == 5, and hz == 100, then
	5 * 100 / 1000 -> 500 / 1000 -> 0.

	In that case, the following line supplies a non-zero value
	to replace that 0 (must be non-zero as hz < 1000).  That's good.

	But the value it generates is nonsense.   That's bad.

	Consider the example again - hz == 100, so 1000/hz == 10.
	The caller asked for 5ms, the result is 10 ticks, which at
	100hz is 100ms.   Way too long.

	Even worse, if hz == 50, we again get 0 from the initial
	calc, but now 1000/hz is 20 - so 20 ticks, that's 400ms (@ 50hz).
	Even worse.

>How-To-Repeat:
	UTSL.

>Fix:
	Perhaps change (all instances in these (many) XXXmstohz functions)
	of that

		if (h == 0)
			h = 1000 / hz;

	into

		if (h == 0)
			h = 1;

	In the cases where h == 0 (here, not earlier), the caller requested a
	delay smaller than is possible to provide (given current mechanisms),
	so the sane response is to simply give it the smallest that we can.

	That's 1 tick.

	But because 1 tick tends to mean "until whenever the clock ticks next"
	which might be much less than 1/hz in some cases, in some of these it
	might be better to use 2 rather than 1, but a case by case analysis by
	someone who understands each environment in which these are used will
	be required for that.



Home | Main Index | Thread Index | Old Index