On Thu, Aug 25, 2022 at 01:36:10AM +0200, Alejandro Colomar wrote:
quoted
But from your side what do we have? Just direct NAKs without much
explanation. The only one who gave some explanation was Greg, and he
vaguely pointed to Linus's comments about it in the past, with no precise
pointer to it. I investigated a lot before v2, and could not find anything
strong enough to recommend using kernel types in user space, so I pushed v2,
and the discussion was kept.
So despite me saying that "this is not ok", and many other maintainers
saying "this is not ok", you applied a patch with our objections on it?
That is very odd and a bit rude.
The justifications brought forward are just regurgitating previous
misinformation. If you do that, it's hard to take you seriously.
There is actually a good reason for using __u64: it's always based on
long long, so the format strings are no longer architecture-specific,
and those ugly macro hacks are not needed to achieve portability. But
that's really the only reason I'm aware of. Admittedly, it's a pretty
good reason.
quoted
I would like that if you still oppose to the patch, at least were able to
provide some facts to this discussion.
The fact is that the kernel can not use the namespace that userspace has
with ISO C names. It's that simple as the ISO standard does NOT
describe the variable types for an ABI that can cross the user/kernel
boundry.
You cannot avoid using certain ISO C names with current GCC or Clang,
however hard you try. But currently, the kernel does not try at all,
not really: it is not using -ffreestanding and -fno-builtin, at least
not consistently. This means that if the compiler sees a known function
(with the right name and a compatible prototype), it will optimize based
on that. What kind of headers you use does not matter.
<stdarg.h>, <stddef.h>, <stdint.h> are compiler-provided headers that
are designed to be safe to use for bare-metal contexts (like in
kernels). Avoiding them is not necessary per se. However, <stdint.h>
is not particularly useful if you want to use your own printf-style
functions with the usual format specifiers (see above for __u64). But
on its own, it's perfectly safe to use. You have problems with
<stdint.h> *because* you use well-known, standard facilities in kernel
space (the printf format specifiers), not because you avoid them. So
exactly the opposite of what you say.
But until then, we have to stick to our variable name types,
just like all other operating systems have to (we are not alone here.)
FreeBSD uses <stdint.h> and the <inttypes.h> formatting macros in kernel
space. I don't think that's unusual at all for current kernels. It's
particularly safe for FreeBSD because they use a monorepo and toolchain
variance among developers is greatly reduced. Linux would need to
provide its own <inttypes.h> equivalent for the formatting macros
(as it's not a compiler header; FreeBSD has <machine/_inttypes.h>).
At this point and with the current ABIs we have for Linux, it makes
equal (maybe more) sense to avoid the <stdint.h> types altogether and
use Linux-specific typedefs with have architecture-independent format
strings.
Thanks,
Florian
On Wed, Aug 24, 2022 at 11:41 PM Florian Weimer [off-list ref] wrote:
The justifications brought forward are just regurgitating previous
misinformation. If you do that, it's hard to take you seriously.
Pot, meet kettle.
There is actually a good reason for using __u64: it's always based on
long long, so the format strings are no longer architecture-specific,
[..]
That's a small detail that yes, we've tried to avoid the absolute
humongous mess that the C standard library has with their horrendous
'PRId*' mess, but honestly, it's just a tiny detail.
The real issue is that we want to be able to control our own types,
and our own names, and in the process have sometimes been able to
standardize on types that makes it easier to just not have to deal
with "oh, somebody picked 'int' on this architecture, and 'long' on
this other, and they are both 32-bit types".
We still have to deal with that for '[s]size_t', but that's such a
standard legacy type that thankfully we have the whole '%zu/%zd' thing
for that.
And yes, sometimes we screw up even *though* we were the ones that
picked the types, and we've had pointless differences where '__u64'
could be 'unsigned long' on a 64-bit architecture, and 'unsigned long
long' on a 32-bit one, and then we were able to fix our own little
broken type system exactly because it was *OUR* little type system.
So you are correct that then in the specific case of '__u64' we have
been able to simply just standardize on 'unsigned long long' and make
printf strings simpler.
But you are wrong to think that that is somehow a special thing.
It's not.
It's very much all the same thing: we have types *we* control, and
thanks to that we can do them the way *we* need them done, and can fix
them when we made a silly mistake.
In other words, it's the whole *point* of not ever using 'stdint.h' at
all for those things.
(It's also about avoiding the kinds of unholy things that happen in
system header files. Have you ever *looked* at them? Christ. The
amount of absolute crap you get from including <stdint.h> in user
space is scary)
You cannot avoid using certain ISO C names with current GCC or Clang,
however hard you try.
You are now the one who is regurgitating complete mis-information.
You do it so prettily, and with such weasel-wording, that I know you
must be knowingly threading that fine line between "actively
misleading" but trying to avoid "outright lying"..
You say "certain ISO C names" to try to make it sound as if this was
at all relevant to things like "uint32_t" and friends.
But deep down, you know you're basically lying by omission.
Because it's true that we have to know and care about things like
'size_t', which comes up for all the basic string.h functions.
So yes, we have a very small set of types that we make sure matches
the compiler notion of said types, and we carefully use things like
typedef __kernel_ulong_t __kernel_size_t;
and then we have our own 'stdarg.h which uses
typedef __builtin_va_list va_list;
that is explicitly the one that the compiler exposes with those double
underscores exactly because even the compiler can't expose the
"standard" name due to namespace issues.
And no, NONE OF THOSE ARE USABLE IN THE UAPI HEADERS!
And equally importantly, none of those have *anything* to do with the
'uint32_t' kind of names.
The fact that yes, we care about what the compiler thinks "size_t" is
(because we do want the compiler to do memset() for us) has absolutely
*NOTHING* to do with uint32_t and <stdint.h>.
And I'm pretty sure you knew that, but you tried to make it sound like
they were somehow all in the same boat.
And yes, some drivers tend to actually use 'uint32_t' in the kernel,
and we allow it, but they cannot be used by user interfaces. So a uapi
file really *really* shouldn't ever use them.
And no, we don't use "-ffreestanding" and friends - we actually have
occasionally wanted and tried to do so just to make the boundary lines
clearer, but then that will make gcc no longer do sane things for
'memcpy()'' and friends, so it's kind of a balancing act.
<stdarg.h>, <stddef.h>, <stdint.h> are compiler-provided headers that
are designed to be safe to use for bare-metal contexts (like in
kernels). Avoiding them is not necessary per se.
We explicitly avoid them all.
We historically used stdarg.h and stddef.h (but never stdint.h -
there's absolutely _zero_ upside), but it was always a slight pain.
So we simply bake our own, exactly because it's simply less painful
than having to deal with possible system-provided ones.
People do odd compiler things with host compilers, bad or odd
installations of cross-build environments, it's just not worth the
pain to deal with the "system header files" when they just don't
provide any real value.
Linus
From: Joseph Myers <hidden> Date: 2022-08-25 14:54:18
On Thu, 25 Aug 2022, Linus Torvalds wrote:
That's a small detail that yes, we've tried to avoid the absolute
humongous mess that the C standard library has with their horrendous
'PRId*' mess, but honestly, it's just a tiny detail.
I've not yet implemented it for glibc or for GCC format checking, but C23
adds 'wN' format length modifiers so you will be able to e.g. use "%w64d"
with printf to print an int64_t and won't need those PRI macros any more.
--
Joseph S. Myers
joseph@codesourcery.com
From: David Laight <hidden> Date: 2022-08-25 15:04:05
From: Joseph Myers
Sent: 25 August 2022 15:39
On Thu, 25 Aug 2022, Linus Torvalds wrote:
quoted
That's a small detail that yes, we've tried to avoid the absolute
humongous mess that the C standard library has with their horrendous
'PRId*' mess, but honestly, it's just a tiny detail.
I've not yet implemented it for glibc or for GCC format checking, but C23
adds 'wN' format length modifiers so you will be able to e.g. use "%w64d"
with printf to print an int64_t and won't need those PRI macros any more.
Is that meant to work regardless of whether the type is
int, long or long long provided the size is correct?
Or does it require the compiler know which type inttypes.h
uses for uint32_t and uint64_t?
David
-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK
Registration No: 1397386 (Wales)
From: Joseph Myers <hidden> Date: 2022-08-25 15:52:59
On Thu, 25 Aug 2022, David Laight wrote:
From: Joseph Myers
quoted
Sent: 25 August 2022 15:39
On Thu, 25 Aug 2022, Linus Torvalds wrote:
quoted
That's a small detail that yes, we've tried to avoid the absolute
humongous mess that the C standard library has with their horrendous
'PRId*' mess, but honestly, it's just a tiny detail.
I've not yet implemented it for glibc or for GCC format checking, but C23
adds 'wN' format length modifiers so you will be able to e.g. use "%w64d"
with printf to print an int64_t and won't need those PRI macros any more.
Is that meant to work regardless of whether the type is
int, long or long long provided the size is correct?
Or does it require the compiler know which type inttypes.h
uses for uint32_t and uint64_t?
The type passed needs to be that used for the relevant stdint.h typedef,
not another of the same size. (For format checking, that means the
compiler needs to know what the types used in stdint.h are.)
It's now required that if int32_t exists, int_least32_t must have the same
type, so int_least32_t can also be used with that format (and there are
'wfN' formats for int_fastN_t / uint_fastN_t as well).
--
Joseph S. Myers
joseph@codesourcery.com
On Thu, Aug 25, 2022 at 7:38 AM Joseph Myers [off-list ref] wrote:
I've not yet implemented it for glibc or for GCC format checking, but C23
adds 'wN' format length modifiers so you will be able to e.g. use "%w64d"
with printf to print an int64_t and won't need those PRI macros any more.
Yeah, that's going to help user space.
We don't typically have huge issues with it (any more) in the kernel
exactly because we refused to do the syntactically horrendous PRIxyz
thing.
So in the kernel, we still do have some format string issues, but they
tend to be about "different architectures and configurations do
different things for this type", and those different things are sadly
not necessarily about a fixed width.
IOW, we used to have horrors like "sector_t can be 32-bit or 64-bit
depending on config options" (because small machines didn't want the
overhead of having to pass 64-bit things around - from back when
32-bit was a primary target).
We got rid of *that* thing a few years ago because it just wasn't
worth supporting any more, but some similar issues remain.
So we still have a number of cases of "if you really need to print
this out, you need to use '%llui' and cast the value to 'unsigned long
long'".
But it's happily not as common as it used to be.
Linus