drm/linux: Rewrite READ_ONCE() and WRITE_ONCE()
authorFrançois Tigeot <ftigeot@wolfpond.org>
Sat, 1 Sep 2018 06:34:40 +0000 (08:34 +0200)
committerFrançois Tigeot <ftigeot@wolfpond.org>
Sat, 1 Sep 2018 06:34:40 +0000 (08:34 +0200)
These macros were broken when used with some complex types like
pointers to pointers.

sys/dev/drm/include/linux/compiler.h

index 5c373b6..ecad616 100644 (file)
@@ -3,7 +3,7 @@
  * Copyright (c) 2010 iX Systems, Inc.
  * Copyright (c) 2010 Panasas, Inc.
  * Copyright (c) 2013-2016 Mellanox Technologies, Ltd.
- * Copyright (c) 2015 François Tigeot
+ * Copyright (c) 2015-2018 François Tigeot <ftigeot@wolfpond.org>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
 
 #define ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x))
 
-#define WRITE_ONCE(x,v) do {           \
-       barrier();                      \
-       ACCESS_ONCE(x) = (v);           \
-       barrier();                      \
-} while (0)
-
-#define READ_ONCE(x) ({                        \
-       __typeof(x) __var = ({          \
-               barrier();              \
-               ACCESS_ONCE(x);         \
-       });                             \
-       barrier();                      \
-       __var;                          \
+#ifdef _KERNEL         /* This file is included by kdump(1) */
+
+#include <sys/param.h>
+
+/*
+ * The READ_ONCE() and WRITE_ONCE() macros force volatile accesses to
+ * various data types.
+ * Their complexity is caused by the necessity to work-around
+ * compiler cleverness and bugs.
+ * Some GCC versions drop the volatile modifier if the variable used
+ * is not of a scalar type.
+ */
+static inline void
+__volatile_read(const volatile void *x, int size, void *result)
+{
+       switch(size) {
+       case 8:
+               *(uint64_t *)result = *(const volatile uint64_t *)x;
+               break;
+       case 4:
+               *(uint32_t *)result = *(const volatile uint32_t *)x;
+               break;
+       case 2:
+               *(uint16_t *)result = *(const volatile uint16_t *)x;
+               break;
+       case 1:
+               *(uint8_t *)result = *(const volatile uint8_t *)x;
+               break;
+       default:
+               panic("__volatile_read called with size %d\n", size);
+       }
+}
+
+static inline void
+__volatile_write(volatile void *var, int size, void *value)
+{
+       switch(size) {
+       case 8:
+               *(volatile uint64_t *)var = *(uint64_t *)value;
+               break;
+       case 4:
+               *(volatile uint32_t *)var = *(uint32_t *)value;
+               break;
+       case 2:
+               *(volatile uint16_t *)var = *(uint16_t *)value;
+               break;
+       case 1:
+               *(volatile uint8_t *)var = *(uint8_t *)value;
+               break;
+       default:
+               panic("__volatile_write called with size %d\n", size);
+       }
+
+}
+
+#define READ_ONCE(x) ({                                                \
+       union {                                                 \
+               __typeof(x) initial_type;                       \
+               uint8_t nc_type;                                \
+       } result;                                               \
+                                                               \
+       result.nc_type = 0;                                     \
+       __volatile_read(&(x), sizeof(x), &result.nc_type);      \
+       result.initial_type;                                    \
+})
+
+#define WRITE_ONCE(var, value) ({                              \
+       union {                                                 \
+               __typeof(var) initial_type;                     \
+               uint8_t nc_type;                                \
+       } result;                                               \
+                                                               \
+       result.initial_type = value;                            \
+       __volatile_write(&(var), sizeof(var), &result.nc_type); \
+       result.initial_type;                                    \
 })
 
+#endif /* __KERNEL__ */
+
 #endif /* _LINUX_COMPILER_H_ */