aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew Morton <akpm@linux-foundation.org>2011-01-12 19:59:35 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2011-01-13 11:03:07 -0500
commit71a9048448de302d1e968f336de01060d02fae71 (patch)
treefe4667050a4dae315baf6b5c4ebe40777e975f72
parent3e5c12409c54c30f1d1b16bba5d4d24e35aa283c (diff)
include/linux/kernel.h: abs(): fix handling of 32-bit unsigneds on 64-bit
Michal reports: In the framebuffer subsystem the abs() macro is often used as a part of the calculation of a Manhattan metric, which in turn is used as a measure of similarity between video modes. The arguments of abs() are sometimes unsigned numbers. This worked fine until commit a49c59c0 ("Make sure the value in abs() does not get truncated if it is greater than 2^32:) , which changed the definition of abs() to prevent truncation. As a result of this change, in the following piece of code: u32 a = 0, b = 1; u32 c = abs(a - b); 'c' will end up with a value of 0xffffffff instead of the expected 0x1. A problem caused by this change and visible by the end user is that framebuffer drivers relying on functions from modedb.c will fail to find high resolution video modes similar to that explicitly requested by the user if an exact match cannot be found (see e.g. Fix this by special-casing `long' types within abs(). This patch reduces x86_64 code size a bit - drivers/video/uvesafb.o shrunk by 15 bytes, presumably because it is doing abs() on 4-byte quantities, and expanding those to 8-byte longs adds code. testcase: #define oldabs(x) ({ \ long __x = (x); \ (__x < 0) ? -__x : __x; \ }) #define newabs(x) ({ \ long ret; \ if (sizeof(x) == sizeof(long)) { \ long __x = (x); \ ret = (__x < 0) ? -__x : __x; \ } else { \ int __x = (x); \ ret = (__x < 0) ? -__x : __x; \ } \ ret; \ }) typedef unsigned int u32; main() { u32 a = 0; u32 b = 1; u32 oldc = oldabs(a - b); u32 newc = newabs(a - b); printf("%u %u\n", oldc, newc); } akpm:/home/akpm> gcc t.c akpm:/home/akpm> ./a.out 4294967295 1 Reported-by: Michal Januszewski <michalj@gmail.com> Cc: Rolf Eike Beer <eike-kernel@sf-tec.de Cc: Geert Uytterhoeven <geert@linux-m68k.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--include/linux/kernel.h19
1 files changed, 16 insertions, 3 deletions
diff --git a/include/linux/kernel.h b/include/linux/kernel.h
index d0fbc043de60..57dac7022b63 100644
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -143,9 +143,22 @@ extern int _cond_resched(void);
143 143
144#define might_sleep_if(cond) do { if (cond) might_sleep(); } while (0) 144#define might_sleep_if(cond) do { if (cond) might_sleep(); } while (0)
145 145
146#define abs(x) ({ \ 146/*
147 long __x = (x); \ 147 * abs() handles unsigned and signed longs, ints, shorts and chars. For all
148 (__x < 0) ? -__x : __x; \ 148 * input types abs() returns a signed long.
149 * abs() should not be used for 64-bit types (s64, u64, long long) - use abs64()
150 * for those.
151 */
152#define abs(x) ({ \
153 long ret; \
154 if (sizeof(x) == sizeof(long)) { \
155 long __x = (x); \
156 ret = (__x < 0) ? -__x : __x; \
157 } else { \
158 int __x = (x); \
159 ret = (__x < 0) ? -__x : __x; \
160 } \
161 ret; \
149 }) 162 })
150 163
151#define abs64(x) ({ \ 164#define abs64(x) ({ \