From 8add1b91854143c0b365dfb49de937e132bf9596 Mon Sep 17 00:00:00 2001
From: Nadav Har'El <nyh@cloudius-systems.com>
Date: Mon, 25 Nov 2013 15:21:13 +0200
Subject: [PATCH] Warn about incorrect use of percpu<> / PERCPU(..).

This patch causes incorrect usage of percpu<>/PERCPU() to cause
compilation errors instead of silent runtime corruptions.

Thanks to Dmitry for first noticing this issue in xen_intr.cc (see his
separate patch), and to Avi for suggesting a compile-time fix.

With this patch:

1. Using percpu<...> to *define* a per-cpu variable fails compilation.
   Instead, PERCPU(...) must be used for the definition, which is important
   because it places the variable in the ".percpu" section.

2. If a *declaration* is needed additionally (e.g., for a static class
   member), percpu<...> must be used, not PERCPU().
   Trying to use PERCPU() for declaration will cause a compilation error.

3. PERCPU() only works on statically-constructed objects - global variables,
   static function-variables and static class-members. Trying to use it
   on a dynamically-constructed object - stack variable, class field,
   or operator new - will cause a compilation error.

With this patch, the bug in xen_intr.cc would have been caught at
compile time.

Signed-off-by: Nadav Har'El <nyh@cloudius-systems.com>
Signed-off-by: Pekka Enberg <penberg@cloudius-systems.com>
---
 drivers/kvmclock.cc   |  2 +-
 include/osv/percpu.hh | 23 +++++++++++++++++++----
 2 files changed, 20 insertions(+), 5 deletions(-)

diff --git a/drivers/kvmclock.cc b/drivers/kvmclock.cc
index 408a022ff..b6489ce1c 100644
--- a/drivers/kvmclock.cc
+++ b/drivers/kvmclock.cc
@@ -30,7 +30,7 @@ private:
     static bool _new_kvmclock_msrs;
     pvclock_wall_clock* _wall;
     u64  _wall_ns;
-    static PERCPU(pvclock_vcpu_time_info, _sys);
+    static percpu<pvclock_vcpu_time_info> _sys;
     sched::cpu::notifier cpu_notifier;
 };
 
diff --git a/include/osv/percpu.hh b/include/osv/percpu.hh
index 1f9f85e49..82f15b655 100644
--- a/include/osv/percpu.hh
+++ b/include/osv/percpu.hh
@@ -12,14 +12,28 @@
 #include <type_traits>
 #include <memory>
 
-extern char _percpu_start[];
-
 extern __thread char* percpu_base;
 
 template <typename T>
 class percpu {
 public:
-    constexpr percpu() {}
+    // We need percpu<T> variables to be defined with the PERCPU macro so
+    // they'll end up in the right section. To enforce this, we add a fake
+    // argument to the constructor. The type percpu<T> still needs to be
+    // used to declare the variable separately from its definition (which
+    // is needed for class static variables).
+    static constexpr struct
+        please_use_PERCPU_macro {} please_use_PERCPU_macro {};
+    // percpu<T>'s constructor needs to be constexpr so that the .percpu
+    // section can be constructed at compile time, and then at early run time
+    // be copied to per-cpu copies of this section, without risking that the
+    // constructor hasn't run yet.
+    explicit constexpr percpu(struct please_use_PERCPU_macro) { }
+    // You can't copy a per-cpu variable and get a new per-cpu variable.
+    // Neither can one be moved (its address is important).
+    percpu(const percpu&) = delete;
+    percpu(percpu&&) = delete;
+
     T* operator->() {
         return addr();
     }
@@ -39,7 +53,8 @@ private:
     friend size_t dynamic_percpu_base();
 };
 
-#define PERCPU(type, var) percpu<type> var __attribute__((section(".percpu")))
+#define PERCPU(type, var) __attribute__((section(".percpu"))) \
+            percpu<type> var (percpu<type>::please_use_PERCPU_macro)
 
 size_t dynamic_percpu_alloc(size_t size, size_t align);
 void dynamic_percpu_free(size_t offset, size_t size);
-- 
GitLab