dpif-linux: Fix build with certain 64-bit kernel/userspace combinations.
authorBen Pfaff <blp@nicira.com>
Fri, 14 Oct 2011 16:39:48 +0000 (09:39 -0700)
committerBen Pfaff <blp@nicira.com>
Fri, 14 Oct 2011 16:39:48 +0000 (09:39 -0700)
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<N>
because OVS always defines those as aliases for the kernel's __be<N> types
when those are available.

This compiled cleanly for me in Scientific Linux 6.0 x86-64.

Reported-by: Pravin Shelar <pshelar@nicira.com>
lib/unaligned.h
lib/util.h

index f1aab23adc814b8ac3dd7b29955228548e394405..a5ae4be60067ee7dd486d0ca59b42e5d193ff52c 100644 (file)
 #include <stdint.h>
 #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);
+}
 \f
 /* Returns the value in 'x'. */
 static inline uint64_t
index 5ae0775f1b36491e36b6d720a4ee73456000403a..61039be8182f8c60abd55a730076e98bfb95d8ed 100644 (file)
 #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. */