Page MenuHomeFreeBSD

tcp_hostcache: explicitly typecast atomic_load_int to (int) on comparsion against negative values
ClosedPublic

Authored by rscheff on Mon, Jul 20, 7:28 PM.
Tags
None
Referenced Files
F164209369: D58360.diff
Wed, Jul 29, 5:02 PM
Unknown Object (File)
Mon, Jul 27, 4:25 AM
Unknown Object (File)
Mon, Jul 27, 3:15 AM
Unknown Object (File)
Mon, Jul 27, 1:52 AM
Unknown Object (File)
Mon, Jul 27, 12:18 AM
Unknown Object (File)
Sun, Jul 26, 9:57 PM
Unknown Object (File)
Sun, Jul 26, 8:02 PM
Unknown Object (File)
Sun, Jul 26, 6:06 PM
Subscribers

Details

Summary
While unit-testing the hostcache facilities it was found that on this machine, the internal
macro which resolves atomic_load_int would be treated as uint by the compiler for some reason,
thus the only way the expiry properly works if the initial value and intermediate decrements 
are even multiples of each other. 

On systems where net.inet.tcp.hostcache.prune is set to a different value, the hostcache may
only get pruned when completely full instead.

Diff Detail

Repository
rG FreeBSD src repository
Lint
Lint Passed
Unit
No Test Coverage
Build Status
Buildable 74962
Build 71845: arc lint + arc unit

Event Timeline

That sounds like pretty serious compiler bug. What is the compiler and what is the machine arch?

That sounds like pretty serious compiler bug. What is the compiler and what is the machine arch?

Nothing fancy. all defaults with x86_64 - clang 21.1.8

I didn't check with gcc, but this is how atomic_load_int appears to resolve:

#define	__atomic_load_int_relaxed(p)	(*(const volatile u_int *)(p))

/*
 * When _Generic is available, try to provide some type checking.
 */
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) || \
    __has_extension(c_generic_selections)

#define	__atomic_load_generic(p, t, ut, n)	\
	_Generic(*(p),				\
	    t: __atomic_load_ ## n ## _relaxed(p), \
	    ut: __atomic_load_ ## n ## _relaxed(p))
#else
#define	__atomic_load_generic(p, t, ut, n)	\
	__atomic_load_ ## n ## _relaxed(p)
#endif

#define	atomic_load_int(p)	__atomic_load_generic(p, int, u_int, int)

So, the fallback appears to be uint by default, and only when the extentions are available, then maybe its using the int type? But I don't fully parse that precompile directives...

But reading c language generic extention shouldn't the signed and unsigned type ultimately resolve to *different* macros? After being separated for the type, they both go back to poin to (*(const volatile u_int *)(p)) - for the compiler to actually do the right thing, shouldn't there be a (*(const volatile int *)(p))

Adding Marc as he initially put in the typechecking - which doesn't appear to be doing all that much though.

There are apparently is only one other instances with a comparison for lower than zero, and in this other instance, a typecast is also used; everything else compares to non-zero values, exactly zero, etc...

sys/kern/vfs_default.c: return ((int)atomic_load_int(&ap->a_vp->v_writecount) < 0);
sys/netinet/tcp_hostcache.c:                        (int)atomic_load_int(&hc_entry->hc_expire) <= 0) {

Although with the name implying signed-type, and the compiler trying to do the right thing, maybe there should be a macro in sys/sys/atomic_common.h to (*(const volatile int *)(p))...

Adding Marc as he initially put in the typechecking - which doesn't appear to be doing all that much though.

That type-checking is really meant to catch cases where you're doing something more egregious, like atomic_load_int() of a pointer to long, for example.

I agree that always returning an unsigned value here is somewhat surprising, though it is documented.

We could make the atomic_common.h macros always return the underlying type, i.e., atomic_load_int() could return a signed or unsigned int depending on the underlying type. The problem with this is that it'd be inconsistent with the macros defined in machdep/atomic.h, i.e., we'd want to make, e.g., atomic_load_acq_int() behave the same as atomic_load_int(), and that's not a small amount of work.

This revision is now accepted and ready to land.Tue, Jul 21, 2:00 PM

Since there appear only two instances of comparisons where negative values are relevant in the full kernel source tree, I think the most progressive way would then be to simply also typecast the atomic_load_int here.