From: Ben Pfaff Date: Fri, 7 May 2010 21:31:04 +0000 (-0700) Subject: Add header for access to potentially unaligned data. X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=afa3a93165f1f576d03cbf9fc1a2f8ddc2a64d01;p=openvswitch Add header for access to potentially unaligned data. I had been under the impression that "memcpy" was a valid way to copy unaligned data into an aligned location for access. But testing on SPARC has shown that GCC doesn't always honor that intention. It seems that, if GCC can see that there is a pointer of a type that requires alignment to a given object, then it will access it directly regardless of whether memcpy() is used to copy it. This commit adds a new header with functions to access unaligned data. I managed to come up with two techniques, one GCC-specific, one generic, that do avoid the misaligned access in my test case. The GCC-specific technique is the same one used by the Linux kernel (although no code has been literally copied). The other one seemed obvious but possibly slow depending on the compiler's ability to optimize. The following commit adds a user. --- diff --git a/lib/automake.mk b/lib/automake.mk index 6554b44a..56675384 100644 --- a/lib/automake.mk +++ b/lib/automake.mk @@ -134,6 +134,7 @@ lib_libopenvswitch_a_SOURCES = \ lib/timeval.c \ lib/timeval.h \ lib/type-props.h \ + lib/unaligned.h \ lib/unicode.c \ lib/unicode.h \ lib/unixctl.c \ diff --git a/lib/unaligned.h b/lib/unaligned.h new file mode 100644 index 00000000..fb167a4e --- /dev/null +++ b/lib/unaligned.h @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2010 Nicira Networks. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef UNALIGNED_H +#define UNALIGNED_H 1 + +#include +#include "xtoxll.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); + +#ifdef __GNUC__ +/* GCC implementations. */ +#define GCC_UNALIGNED_ACCESSORS(SIZE) \ +struct unaligned_u##SIZE { \ + uint##SIZE##_t x __attribute__((__packed__)); \ +}; \ +static inline struct unaligned_u##SIZE * \ +unaligned_u##SIZE(const uint##SIZE##_t *p) \ +{ \ + return (struct unaligned_u##SIZE *) p; \ +} \ + \ +static inline uint##SIZE##_t \ +get_unaligned_u##SIZE(const uint##SIZE##_t *p) \ +{ \ + return unaligned_u##SIZE(p)->x; \ +} \ + \ +static inline void \ +put_unaligned_u##SIZE(uint##SIZE##_t *p, uint##SIZE##_t x) \ +{ \ + unaligned_u##SIZE(p)->x = x; \ +} + +GCC_UNALIGNED_ACCESSORS(16); +GCC_UNALIGNED_ACCESSORS(32); +GCC_UNALIGNED_ACCESSORS(64); +#else +/* Generic implementations. */ + +static inline uint16_t get_unaligned_u16(const uint16_t *p_) +{ + const uint8_t *p = (const uint8_t *) p_; + return ntohs((p[0] << 8) | p[1]); +} + +static inline void put_unaligned_u16(uint16_t *p_, uint16_t x_) +{ + uint8_t *p = (uint8_t *) p_; + uint16_t x = ntohs(x_); + + p[0] = x >> 8; + p[1] = x; +} + +static inline uint32_t get_unaligned_u32(const uint32_t *p_) +{ + const uint8_t *p = (const uint8_t *) p_; + return ntohl((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]); +} + +static inline void put_unaligned_u32(uint32_t *p_, uint32_t x_) +{ + uint8_t *p = (uint8_t *) p_; + uint32_t x = ntohl(x_); + + p[0] = x >> 24; + p[1] = x >> 16; + p[2] = x >> 8; + p[3] = x; +} + +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) + | ((uint64_t) p[1] << 48) + | ((uint64_t) p[2] << 40) + | ((uint64_t) p[3] << 32) + | (p[4] << 24) + | (p[5] << 16) + | (p[6] << 8) + | p[7]); +} + +static inline void put_unaligned_u64(uint64_t *p_, uint64_t x_) +{ + uint8_t *p = (uint8_t *) p_; + uint64_t x = ntohll(x_); + + p[0] = x >> 56; + p[1] = x >> 48; + p[2] = x >> 40; + p[3] = x >> 32; + p[4] = x >> 24; + p[5] = x >> 16; + p[6] = x >> 8; + p[7] = x; +} +#endif + +#endif /* unaligned.h */