From 320232ec7f37218a6fce0704f9b0ba7696e5ad21 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Fri, 14 Oct 2011 09:39:48 -0700 Subject: [PATCH] dpif-linux: Fix build with certain 64-bit kernel/userspace combinations. Unix 64-bit ABIs have two 64-bit types: "long" and "long long". Either of these is a reasonable choice for uint64_t (the userspace type) and for __u64 (the kernel type). Unfortunately, kernel and userspace don't necessarily agree on the choice, and in fact the choice varies across kernel versions and architectures. Now that OVS is actually using kernel types in its kernel header, this can make a difference: when __u64 and uint64_t differ, passing a pointer to __u64 to OVS function get_unaligned_u64() yields a compiler warning or error. This commit fixes up the problems of this type found in OVS, by making get_unaligned_u64() accept all 64-bit unsigned integer types, not just whichever one happens to be uint64_t. I didn't do the same thing for put_unaligned_u64() because it is less likely to be a problem in practice: usually, when userspace writes to kernel data structures it does so with copies that it knows to be aligned, so that it's not necessary to use put_unaligned_u64(). This problem won't occur for uint8_t, uint16_t, or uint32_t, since there is only one reasonable choice of type for each. It won't occur for ovs_be because OVS always defines those as aliases for the kernel's __be types when those are available. This compiled cleanly for me in Scientific Linux 6.0 x86-64. Reported-by: Pravin Shelar --- lib/unaligned.h | 41 +++++++++++++++++++++++++++++++++++++---- lib/util.h | 8 ++++++++ 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/lib/unaligned.h b/lib/unaligned.h index f1aab23a..a5ae4be6 100644 --- a/lib/unaligned.h +++ b/lib/unaligned.h @@ -20,11 +20,12 @@ #include #include "byte-order.h" #include "openvswitch/types.h" +#include "type-props.h" +#include "util.h" /* Public API. */ static inline uint16_t get_unaligned_u16(const uint16_t *); static inline uint32_t get_unaligned_u32(const uint32_t *); -static inline uint64_t get_unaligned_u64(const uint64_t *); static inline void put_unaligned_u16(uint16_t *, uint16_t); static inline void put_unaligned_u32(uint32_t *, uint32_t); static inline void put_unaligned_u64(uint64_t *, uint64_t); @@ -62,7 +63,7 @@ put_unaligned_##ABBREV(TYPE *p, TYPE x) \ GCC_UNALIGNED_ACCESSORS(uint16_t, u16); GCC_UNALIGNED_ACCESSORS(uint32_t, u32); -GCC_UNALIGNED_ACCESSORS(uint64_t, u64); +GCC_UNALIGNED_ACCESSORS(uint64_t, u64__); /* Special case: see below. */ GCC_UNALIGNED_ACCESSORS(ovs_be16, be16); GCC_UNALIGNED_ACCESSORS(ovs_be32, be32); @@ -102,7 +103,7 @@ static inline void put_unaligned_u32(uint32_t *p_, uint32_t x_) p[3] = x; } -static inline uint64_t get_unaligned_u64(const uint64_t *p_) +static inline uint64_t get_unaligned_u64__(const uint64_t *p_) { const uint8_t *p = (const uint8_t *) p_; return ntohll(((uint64_t) p[0] << 56) @@ -115,7 +116,7 @@ static inline uint64_t get_unaligned_u64(const uint64_t *p_) | p[7]); } -static inline void put_unaligned_u64(uint64_t *p_, uint64_t x_) +static inline void put_unaligned_u64__(uint64_t *p_, uint64_t x_) { uint8_t *p = (uint8_t *) p_; uint64_t x = ntohll(x_); @@ -140,6 +141,38 @@ static inline void put_unaligned_u64(uint64_t *p_, uint64_t x_) #define put_unaligned_be32 put_unaligned_u32 #define put_unaligned_be64 put_unaligned_u64 #endif + +/* uint64_t get_unaligned_u64(uint64_t *p); + * + * Returns the value of the possibly misaligned uint64_t at 'p'. 'p' may + * actually be any type that points to a 64-bit integer. That is, on Unix-like + * 32-bit ABIs, it may point to an "unsigned long long int", and on Unix-like + * 64-bit ABIs, it may point to an "unsigned long int" or an "unsigned long + * long int". + * + * This is special-cased because on some Linux targets, the kernel __u64 is + * unsigned long long int and the userspace uint64_t is unsigned long int, so + * that any single function prototype would fail to accept one or the other. + * + * Below, "sizeof (*(P) % 1)" verifies that *P has an integer type, since + * operands to % must be integers. + */ +#define get_unaligned_u64(P) \ + (BUILD_ASSERT(sizeof *(P) == 8), \ + BUILD_ASSERT_GCCONLY(!TYPE_IS_SIGNED(typeof(*(P)))), \ + (void) sizeof (*(P) % 1), \ + get_unaligned_u64__((const uint64_t *) (P))) + +/* Stores 'x' at possibly misaligned address 'p'. + * + * put_unaligned_u64() could be overloaded in the same way as + * get_unaligned_u64(), but so far it has not proven necessary. + */ +static inline void +put_unaligned_u64(uint64_t *p, uint64_t x) +{ + put_unaligned_u64__(p, x); +} /* Returns the value in 'x'. */ static inline uint64_t diff --git a/lib/util.h b/lib/util.h index 5ae0775f..61039be8 100644 --- a/lib/util.h +++ b/lib/util.h @@ -53,6 +53,14 @@ #define BUILD_ASSERT_DECL BOOST_STATIC_ASSERT #endif /* __cplusplus */ +#ifdef __GNUC__ +#define BUILD_ASSERT_GCCONLY(EXPR) BUILD_ASSERT(EXPR) +#define BUILD_ASSERT_DECL_GCCONLY(EXPR) BUILD_ASSERT_DECL(EXPR) +#else +#define BUILD_ASSERT_GCCONLY(EXPR) ((void) 0) +#define BUILD_ASSERT_DECL_GCCONLY(EXPR) ((void) 0) +#endif + extern const char *program_name; /* Returns the number of elements in ARRAY. */ -- 2.30.2