Commit 9b710506 authored by H. Peter Anvin's avatar H. Peter Anvin

x86, bitops: Change bitops to be native operand size

Change the bitops operation to be naturally "long", i.e. 63 bits on
the 64-bit kernel.  Additional bugs are likely to crop up in the
future.

We already have bugs which machines with > 16 TiB of memory in a
single node, as can happen if memory is interleaved.  The x86 bitop
operations take a signed index, so using an unsigned type is not an
option.

Jim Kukunas measured the effect of this patch on kernel size: it adds
2779 bytes to the allyesconfig kernel.  Some of that probably could be
elided by replacing the inline functions with macros which select the
32-bit type if the index is a 32-bit value, something like:

In that case we could also use "Jr" constraints for the 64-bit
version.

However, this would more than double the amount of code for a
relatively small gain.

Note that we can't use ilog2() for _BITOPS_LONG_SHIFT, as that causes
a recursive header inclusion problem.

The change to constant_test_bit() should both generate better code and
give correct result for negative bit indicies.  As previously written
the compiler had to generate extra code to create the proper wrong
result for negative values.
Signed-off-by: default avatarH. Peter Anvin <hpa@linux.intel.com>
Cc: Jim Kukunas <james.t.kukunas@intel.com>
Link: http://lkml.kernel.org/n/tip-z61ofiwe90xeyb461o72h8ya@git.kernel.org
parent 00e55a79
...@@ -15,6 +15,14 @@ ...@@ -15,6 +15,14 @@
#include <linux/compiler.h> #include <linux/compiler.h>
#include <asm/alternative.h> #include <asm/alternative.h>
#if BITS_PER_LONG == 32
# define _BITOPS_LONG_SHIFT 5
#elif BITS_PER_LONG == 64
# define _BITOPS_LONG_SHIFT 6
#else
# error "Unexpected BITS_PER_LONG"
#endif
#define BIT_64(n) (U64_C(1) << (n)) #define BIT_64(n) (U64_C(1) << (n))
/* /*
...@@ -59,7 +67,7 @@ ...@@ -59,7 +67,7 @@
* restricted to acting on a single-word quantity. * restricted to acting on a single-word quantity.
*/ */
static __always_inline void static __always_inline void
set_bit(unsigned int nr, volatile unsigned long *addr) set_bit(long nr, volatile unsigned long *addr)
{ {
if (IS_IMMEDIATE(nr)) { if (IS_IMMEDIATE(nr)) {
asm volatile(LOCK_PREFIX "orb %1,%0" asm volatile(LOCK_PREFIX "orb %1,%0"
...@@ -81,7 +89,7 @@ set_bit(unsigned int nr, volatile unsigned long *addr) ...@@ -81,7 +89,7 @@ set_bit(unsigned int nr, volatile unsigned long *addr)
* If it's called on the same region of memory simultaneously, the effect * If it's called on the same region of memory simultaneously, the effect
* may be that only one operation succeeds. * may be that only one operation succeeds.
*/ */
static inline void __set_bit(int nr, volatile unsigned long *addr) static inline void __set_bit(long nr, volatile unsigned long *addr)
{ {
asm volatile("bts %1,%0" : ADDR : "Ir" (nr) : "memory"); asm volatile("bts %1,%0" : ADDR : "Ir" (nr) : "memory");
} }
...@@ -97,7 +105,7 @@ static inline void __set_bit(int nr, volatile unsigned long *addr) ...@@ -97,7 +105,7 @@ static inline void __set_bit(int nr, volatile unsigned long *addr)
* in order to ensure changes are visible on other processors. * in order to ensure changes are visible on other processors.
*/ */
static __always_inline void static __always_inline void
clear_bit(int nr, volatile unsigned long *addr) clear_bit(long nr, volatile unsigned long *addr)
{ {
if (IS_IMMEDIATE(nr)) { if (IS_IMMEDIATE(nr)) {
asm volatile(LOCK_PREFIX "andb %1,%0" asm volatile(LOCK_PREFIX "andb %1,%0"
...@@ -118,13 +126,13 @@ clear_bit(int nr, volatile unsigned long *addr) ...@@ -118,13 +126,13 @@ clear_bit(int nr, volatile unsigned long *addr)
* clear_bit() is atomic and implies release semantics before the memory * clear_bit() is atomic and implies release semantics before the memory
* operation. It can be used for an unlock. * operation. It can be used for an unlock.
*/ */
static inline void clear_bit_unlock(unsigned nr, volatile unsigned long *addr) static inline void clear_bit_unlock(long nr, volatile unsigned long *addr)
{ {
barrier(); barrier();
clear_bit(nr, addr); clear_bit(nr, addr);
} }
static inline void __clear_bit(int nr, volatile unsigned long *addr) static inline void __clear_bit(long nr, volatile unsigned long *addr)
{ {
asm volatile("btr %1,%0" : ADDR : "Ir" (nr)); asm volatile("btr %1,%0" : ADDR : "Ir" (nr));
} }
...@@ -141,7 +149,7 @@ static inline void __clear_bit(int nr, volatile unsigned long *addr) ...@@ -141,7 +149,7 @@ static inline void __clear_bit(int nr, volatile unsigned long *addr)
* No memory barrier is required here, because x86 cannot reorder stores past * No memory barrier is required here, because x86 cannot reorder stores past
* older loads. Same principle as spin_unlock. * older loads. Same principle as spin_unlock.
*/ */
static inline void __clear_bit_unlock(unsigned nr, volatile unsigned long *addr) static inline void __clear_bit_unlock(long nr, volatile unsigned long *addr)
{ {
barrier(); barrier();
__clear_bit(nr, addr); __clear_bit(nr, addr);
...@@ -159,7 +167,7 @@ static inline void __clear_bit_unlock(unsigned nr, volatile unsigned long *addr) ...@@ -159,7 +167,7 @@ static inline void __clear_bit_unlock(unsigned nr, volatile unsigned long *addr)
* If it's called on the same region of memory simultaneously, the effect * If it's called on the same region of memory simultaneously, the effect
* may be that only one operation succeeds. * may be that only one operation succeeds.
*/ */
static inline void __change_bit(int nr, volatile unsigned long *addr) static inline void __change_bit(long nr, volatile unsigned long *addr)
{ {
asm volatile("btc %1,%0" : ADDR : "Ir" (nr)); asm volatile("btc %1,%0" : ADDR : "Ir" (nr));
} }
...@@ -173,7 +181,7 @@ static inline void __change_bit(int nr, volatile unsigned long *addr) ...@@ -173,7 +181,7 @@ static inline void __change_bit(int nr, volatile unsigned long *addr)
* Note that @nr may be almost arbitrarily large; this function is not * Note that @nr may be almost arbitrarily large; this function is not
* restricted to acting on a single-word quantity. * restricted to acting on a single-word quantity.
*/ */
static inline void change_bit(int nr, volatile unsigned long *addr) static inline void change_bit(long nr, volatile unsigned long *addr)
{ {
if (IS_IMMEDIATE(nr)) { if (IS_IMMEDIATE(nr)) {
asm volatile(LOCK_PREFIX "xorb %1,%0" asm volatile(LOCK_PREFIX "xorb %1,%0"
...@@ -194,7 +202,7 @@ static inline void change_bit(int nr, volatile unsigned long *addr) ...@@ -194,7 +202,7 @@ static inline void change_bit(int nr, volatile unsigned long *addr)
* This operation is atomic and cannot be reordered. * This operation is atomic and cannot be reordered.
* It also implies a memory barrier. * It also implies a memory barrier.
*/ */
static inline int test_and_set_bit(int nr, volatile unsigned long *addr) static inline int test_and_set_bit(long nr, volatile unsigned long *addr)
{ {
int oldbit; int oldbit;
...@@ -212,7 +220,7 @@ static inline int test_and_set_bit(int nr, volatile unsigned long *addr) ...@@ -212,7 +220,7 @@ static inline int test_and_set_bit(int nr, volatile unsigned long *addr)
* This is the same as test_and_set_bit on x86. * This is the same as test_and_set_bit on x86.
*/ */
static __always_inline int static __always_inline int
test_and_set_bit_lock(int nr, volatile unsigned long *addr) test_and_set_bit_lock(long nr, volatile unsigned long *addr)
{ {
return test_and_set_bit(nr, addr); return test_and_set_bit(nr, addr);
} }
...@@ -226,7 +234,7 @@ test_and_set_bit_lock(int nr, volatile unsigned long *addr) ...@@ -226,7 +234,7 @@ test_and_set_bit_lock(int nr, volatile unsigned long *addr)
* If two examples of this operation race, one can appear to succeed * If two examples of this operation race, one can appear to succeed
* but actually fail. You must protect multiple accesses with a lock. * but actually fail. You must protect multiple accesses with a lock.
*/ */
static inline int __test_and_set_bit(int nr, volatile unsigned long *addr) static inline int __test_and_set_bit(long nr, volatile unsigned long *addr)
{ {
int oldbit; int oldbit;
...@@ -245,7 +253,7 @@ static inline int __test_and_set_bit(int nr, volatile unsigned long *addr) ...@@ -245,7 +253,7 @@ static inline int __test_and_set_bit(int nr, volatile unsigned long *addr)
* This operation is atomic and cannot be reordered. * This operation is atomic and cannot be reordered.
* It also implies a memory barrier. * It also implies a memory barrier.
*/ */
static inline int test_and_clear_bit(int nr, volatile unsigned long *addr) static inline int test_and_clear_bit(long nr, volatile unsigned long *addr)
{ {
int oldbit; int oldbit;
...@@ -272,7 +280,7 @@ static inline int test_and_clear_bit(int nr, volatile unsigned long *addr) ...@@ -272,7 +280,7 @@ static inline int test_and_clear_bit(int nr, volatile unsigned long *addr)
* accessed from a hypervisor on the same CPU if running in a VM: don't change * accessed from a hypervisor on the same CPU if running in a VM: don't change
* this without also updating arch/x86/kernel/kvm.c * this without also updating arch/x86/kernel/kvm.c
*/ */
static inline int __test_and_clear_bit(int nr, volatile unsigned long *addr) static inline int __test_and_clear_bit(long nr, volatile unsigned long *addr)
{ {
int oldbit; int oldbit;
...@@ -284,7 +292,7 @@ static inline int __test_and_clear_bit(int nr, volatile unsigned long *addr) ...@@ -284,7 +292,7 @@ static inline int __test_and_clear_bit(int nr, volatile unsigned long *addr)
} }
/* WARNING: non atomic and it can be reordered! */ /* WARNING: non atomic and it can be reordered! */
static inline int __test_and_change_bit(int nr, volatile unsigned long *addr) static inline int __test_and_change_bit(long nr, volatile unsigned long *addr)
{ {
int oldbit; int oldbit;
...@@ -304,7 +312,7 @@ static inline int __test_and_change_bit(int nr, volatile unsigned long *addr) ...@@ -304,7 +312,7 @@ static inline int __test_and_change_bit(int nr, volatile unsigned long *addr)
* This operation is atomic and cannot be reordered. * This operation is atomic and cannot be reordered.
* It also implies a memory barrier. * It also implies a memory barrier.
*/ */
static inline int test_and_change_bit(int nr, volatile unsigned long *addr) static inline int test_and_change_bit(long nr, volatile unsigned long *addr)
{ {
int oldbit; int oldbit;
...@@ -315,13 +323,13 @@ static inline int test_and_change_bit(int nr, volatile unsigned long *addr) ...@@ -315,13 +323,13 @@ static inline int test_and_change_bit(int nr, volatile unsigned long *addr)
return oldbit; return oldbit;
} }
static __always_inline int constant_test_bit(unsigned int nr, const volatile unsigned long *addr) static __always_inline int constant_test_bit(long nr, const volatile unsigned long *addr)
{ {
return ((1UL << (nr % BITS_PER_LONG)) & return ((1UL << (nr & (BITS_PER_LONG-1))) &
(addr[nr / BITS_PER_LONG])) != 0; (addr[nr >> _BITOPS_LONG_SHIFT])) != 0;
} }
static inline int variable_test_bit(int nr, volatile const unsigned long *addr) static inline int variable_test_bit(long nr, volatile const unsigned long *addr)
{ {
int oldbit; int oldbit;
......
...@@ -26,9 +26,9 @@ ...@@ -26,9 +26,9 @@
* Note that @nr may be almost arbitrarily large; this function is not * Note that @nr may be almost arbitrarily large; this function is not
* restricted to acting on a single-word quantity. * restricted to acting on a single-word quantity.
*/ */
static inline void sync_set_bit(int nr, volatile unsigned long *addr) static inline void sync_set_bit(long nr, volatile unsigned long *addr)
{ {
asm volatile("lock; btsl %1,%0" asm volatile("lock; bts %1,%0"
: "+m" (ADDR) : "+m" (ADDR)
: "Ir" (nr) : "Ir" (nr)
: "memory"); : "memory");
...@@ -44,9 +44,9 @@ static inline void sync_set_bit(int nr, volatile unsigned long *addr) ...@@ -44,9 +44,9 @@ static inline void sync_set_bit(int nr, volatile unsigned long *addr)
* you should call smp_mb__before_clear_bit() and/or smp_mb__after_clear_bit() * you should call smp_mb__before_clear_bit() and/or smp_mb__after_clear_bit()
* in order to ensure changes are visible on other processors. * in order to ensure changes are visible on other processors.
*/ */
static inline void sync_clear_bit(int nr, volatile unsigned long *addr) static inline void sync_clear_bit(long nr, volatile unsigned long *addr)
{ {
asm volatile("lock; btrl %1,%0" asm volatile("lock; btr %1,%0"
: "+m" (ADDR) : "+m" (ADDR)
: "Ir" (nr) : "Ir" (nr)
: "memory"); : "memory");
...@@ -61,9 +61,9 @@ static inline void sync_clear_bit(int nr, volatile unsigned long *addr) ...@@ -61,9 +61,9 @@ static inline void sync_clear_bit(int nr, volatile unsigned long *addr)
* Note that @nr may be almost arbitrarily large; this function is not * Note that @nr may be almost arbitrarily large; this function is not
* restricted to acting on a single-word quantity. * restricted to acting on a single-word quantity.
*/ */
static inline void sync_change_bit(int nr, volatile unsigned long *addr) static inline void sync_change_bit(long nr, volatile unsigned long *addr)
{ {
asm volatile("lock; btcl %1,%0" asm volatile("lock; btc %1,%0"
: "+m" (ADDR) : "+m" (ADDR)
: "Ir" (nr) : "Ir" (nr)
: "memory"); : "memory");
...@@ -77,11 +77,11 @@ static inline void sync_change_bit(int nr, volatile unsigned long *addr) ...@@ -77,11 +77,11 @@ static inline void sync_change_bit(int nr, volatile unsigned long *addr)
* This operation is atomic and cannot be reordered. * This operation is atomic and cannot be reordered.
* It also implies a memory barrier. * It also implies a memory barrier.
*/ */
static inline int sync_test_and_set_bit(int nr, volatile unsigned long *addr) static inline int sync_test_and_set_bit(long nr, volatile unsigned long *addr)
{ {
int oldbit; int oldbit;
asm volatile("lock; btsl %2,%1\n\tsbbl %0,%0" asm volatile("lock; bts %2,%1\n\tsbbl %0,%0"
: "=r" (oldbit), "+m" (ADDR) : "=r" (oldbit), "+m" (ADDR)
: "Ir" (nr) : "memory"); : "Ir" (nr) : "memory");
return oldbit; return oldbit;
...@@ -95,11 +95,11 @@ static inline int sync_test_and_set_bit(int nr, volatile unsigned long *addr) ...@@ -95,11 +95,11 @@ static inline int sync_test_and_set_bit(int nr, volatile unsigned long *addr)
* This operation is atomic and cannot be reordered. * This operation is atomic and cannot be reordered.
* It also implies a memory barrier. * It also implies a memory barrier.
*/ */
static inline int sync_test_and_clear_bit(int nr, volatile unsigned long *addr) static inline int sync_test_and_clear_bit(long nr, volatile unsigned long *addr)
{ {
int oldbit; int oldbit;
asm volatile("lock; btrl %2,%1\n\tsbbl %0,%0" asm volatile("lock; btr %2,%1\n\tsbbl %0,%0"
: "=r" (oldbit), "+m" (ADDR) : "=r" (oldbit), "+m" (ADDR)
: "Ir" (nr) : "memory"); : "Ir" (nr) : "memory");
return oldbit; return oldbit;
...@@ -113,11 +113,11 @@ static inline int sync_test_and_clear_bit(int nr, volatile unsigned long *addr) ...@@ -113,11 +113,11 @@ static inline int sync_test_and_clear_bit(int nr, volatile unsigned long *addr)
* This operation is atomic and cannot be reordered. * This operation is atomic and cannot be reordered.
* It also implies a memory barrier. * It also implies a memory barrier.
*/ */
static inline int sync_test_and_change_bit(int nr, volatile unsigned long *addr) static inline int sync_test_and_change_bit(long nr, volatile unsigned long *addr)
{ {
int oldbit; int oldbit;
asm volatile("lock; btcl %2,%1\n\tsbbl %0,%0" asm volatile("lock; btc %2,%1\n\tsbbl %0,%0"
: "=r" (oldbit), "+m" (ADDR) : "=r" (oldbit), "+m" (ADDR)
: "Ir" (nr) : "memory"); : "Ir" (nr) : "memory");
return oldbit; return oldbit;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment