What is the need of ternary operator with always true condition in Linux kernel?

Viewed 183

When it comes to some macro functions defined in the Linux kernel, I can find some weird ternary operator with always true condition (1)

#define to_cpumask(bitmap)                                              \
        ((struct cpumask *)(1 ? (bitmap)                                \
                            : (void *)sizeof(__check_is_bitmap(bitmap))))

static inline int __check_is_bitmap(const unsigned long *bitmap)
{
        return 1;
}

It seems that __check_is_bitmap(bitmap) function will not be invoked at any condition because it is written with ternary operator condition 1.

It seems that to check the type of bitmap at the compile time, this macro function intentionally has introduced a function, __check_is_bitmap, that requires an unsigned long pointer parameter, even though it will not be invoked.

If it wants to check the parameter type requirement, it would have been better to use an inline function or just plain function. Why the kernel programmers prefer the #defined function compared to inline function in this case?

1 Answers

I am by far not an expert on kernel, with this in mind here are my two cents:

It looks to me like a type safety check for the macro parameter bitmap. Since macros don't have the type safety checks of the functions, the 3rd operand of the operator is used for this purpose:

__check_is_bitmap(bitmap) is the operand of sizeof so it is in un unevaluated context. This means it is never called. Next, then the result of sizeof is converted to (void *) and is discarded.

This checks two things:

  • __check_is_bitmap(bitmap) is valid. This happens if bitmap is convertible to const unsigned long *

  • the type of bitmap and void * have a common type (required by the conditional operator)

If it wants to check the parameter type requirement, it would have been better to use an inline function or just plain function. Why the kernel programmers prefer the #defined function compared to inline function in this case?

inline was prohibited in the linux kernel. This is explained in "Inlining in linux" from kernel.org (an article from 2006):

[in the linux kernel] We don't use the "inline" keyword because it's broken.

[...]

In theory, the keyword to do this is "inline". In practice, this keyword is broken on newer versions of gcc. Current versions of gcc turned "inline" into a request (similar to the old "register" keyword), rendering it essentially useless. These versions of gcc are free to ignore any such requests to inline functions, as well as to inline functions without the keyword.

The problem with this is that to be effective, inline functions must be defined in header files (since inlining is done by the compiler, not the linker, it must be done on source code not object files). But even when every use of a function is inlined, an unused non-inline version of the function can still be produced and linked "just in case". If multiple .c files #include the same header, this can even result in multiple non-inline versions of the same function being passed to the linker.

Earlier attempts to work around this breakage by declaring functions "static inline" or "extern inline" (instructing the compiler never to emit a non-inline version of the function, breaking the build if necessary to detect when the compiler wasn't following instructions) worked for a while, but were again broken by newer releases of gcc.

Modern guidelines however encourage short inline functions:

Linux kernel coding style

12) Macros, Enums and RTL

Generally, inline functions are preferable to macros resembling functions.

15) The inline disease

There appears to be a common misperception that gcc has a magic “make me faster” speedup option called inline. While the use of inlines can be appropriate (for example as a means of replacing macros, see Chapter 12), it very often is not [...]

A reasonable rule of thumb is to not put inline at functions that have more than 3 lines of code in them.

So it looks like __check_is_bitmap was written in the past when inline was not allowed and nobody changed it recently. Note this is just speculation.

Related