newbie newbie - 1 year ago 70
C Question

Understanding Linux module code snippet

I am interpreting a generated intermediate code of a linux kernel module. But i am unable to understand the following line of code of


if ((err = pci_set_dma_mask(pdev, (((32) == 64) ? ~0ULL : ((1ULL<<(32))-1))))) {

(void)((NETIF_MSG_PROBE & nic->msg_enable) && printk("<3>" "e100" ": " "%s: %s: "
"No usable DMA configuration, aborting.\n", nic->netdev->name, func));

My understanding is that, since 32 != 64,
will execute. But what kind of value is
? What is happening internally when we left shift
32 times?. And can someone provide some code samples to understand this? Thanks in advance

Answer Source

The redundant parentheses and the trivial condition (32) == 64 show that this is generated code, possibly the result of running the preprocessor. That condition tests whether something is 32-bit or 64-bit and the code has been generated for a system where that thing is 32-bit.

On a 64-bit system, the value ~0ULL is used. With the ULL suffix, the constant is of type unsigned long long, which is a 64-bit type on all platforms that the Linux kernel supports (standard C specifies that unsigned long long is 64-bit or larger but almost all systems have it as exactly 64 bits). The ~ operator takes the bitwise complement, so the result is the number 264-1, i.e. a 64-bit constant whose binary representation is all-bits-one. In hexadecimal, that's 0xffffffffffffffff.

The reason the code uses ~0ULL and not ~0 is that in ~0, the constant 0 is an int, which is a 32-bit type on all platforms that the Linux kernel support. The ~ operator applies to this would yield a 32-bit value with all-bits-one, i.e. 0xffffffff in hexadecimal, i.e. the number number 232-1. When converted to a 64-bit value, the numerical value remains the same — not an all-64-bits-one constant.

On a 32-bit system, the code uses 1ULL<<(32))-1. First 1ULL is a 64-bit value; 1ULL << 32 shifts it left by 32, which means multiplying the numerical value by 232, which yields the number 232, or in hexadecimal 0x100000000. Subtracting 1 yields 232, i.e. 0xffffffff (an all-32-bits-one constant).

Doing the operations on an unsigned long long is necessary here too, because a left shift by 32 is not defined for 32-bit values. Standard C says that x << 32 is undefined behavior if the type of x is a 32-bit type or less. In practice, on many systems, the processor instruction for shifting on a 32-bit type takes a shift amount in the range 0…31, and 1 << 32 would be compiled to the same code as 1 << 0 (wrapping modulo 32), yielding 1 instead of 232.