backports: backport net_get_random_once()
authorLuis R. Rodriguez <mcgrof@do-not-panic.com>
Sun, 30 Mar 2014 00:26:26 +0000 (00:26 +0000)
committerLuis R. Rodriguez <mcgrof@do-not-panic.com>
Wed, 9 Apr 2014 01:16:21 +0000 (18:16 -0700)
Commit a48e4292 introduced as of v3.13 is used by 6lowpan which
we backport. We carry this over for older kernels that don't
define it -- but we remain sane by requiring at least 3.5 which is
where tons of the jump label / static key stuff seems to have last
settled. Backporting this to any older kernel than 3.5 has a huge
string of dependencies which although I was able to resovle the
other depdendencies on 6lowpan on new net core re-architecture on
skb fragment reassembly makes it pointless to carry. Mark my words:

  !! do not try to backport this to kernels older than 3.5 !!

mcgrof@ergon ~/linux (git::master)$ git describe --contains a48e4292
v3.13-rc1~105^2~157^2~4

commit a48e42920ff38bc90bbf75143fff4555723d4540
Author: Hannes Frederic Sowa <hannes@stressinduktion.org>
Date:   Sat Oct 19 21:48:55 2013 +0200

    net: introduce new macro net_get_random_once

Cc: Hannes Frederic Sowa <hannes@stressinduktion.org>
Cc: Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
Cc: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
Cc: linux-zigbee-devel@lists.sourceforge.net
Signed-off-by: Luis R. Rodriguez <mcgrof@do-not-panic.com>
backport/backport-include/linux/net.h
backport/compat/backport-3.13.c

index 07981dae7ae389b87eed075c8b19df438401839c..687ad0bc4cf97254951d7e9d09cae5bb8f6498a6 100644 (file)
@@ -1,6 +1,7 @@
 #ifndef __BACKPORT_LINUX_NET_H
 #define __BACKPORT_LINUX_NET_H
 #include_next <linux/net.h>
+#include <linux/static_key.h>
 
 /* This backports:
  *
@@ -46,4 +47,54 @@ do {                                                         \
        type dst = ({ __sockaddr_check_size(sizeof(*dst)); (type) src; })
 #endif
 
+/*
+ * Avoid backporting this if a distro did the work already, this
+ * takes the check a bit further than just using LINUX_BACKPORT()
+ * namespace, curious if any distro will hit a wall with this.
+ * Also curious if any distro will be daring enough to even try
+ * to backport this to a release older than 3.5.
+ */
+#ifndef ___NET_RANDOM_STATIC_KEY_INIT
+/*
+ * Backporting this before 3.5 is extremely tricky -- I tried, due
+ * to the fact that it relies on static keys, which were refactored
+ * and optimized through a series of generation of patches from jump
+ * labels. These in turn have also been optimized through kernel revisions
+ * and have architecture specific code, which if you commit to backporting
+ * may affect tracing. My recommendation is that if you have a need for
+ * static keys you just require at least 3.5 to remain sane.
+ */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)
+#define __BACKPORT_NET_GET_RANDOM_ONCE 1
+#endif
+#endif /* ___NET_RANDOM_STATIC_KEY_INIT */
+
+#ifdef __BACKPORT_NET_GET_RANDOM_ONCE
+#define __net_get_random_once LINUX_BACKPORT(__net_get_random_once)
+bool __net_get_random_once(void *buf, int nbytes, bool *done,
+                          struct static_key *done_key);
+
+#ifdef HAVE_JUMP_LABEL
+#define ___NET_RANDOM_STATIC_KEY_INIT ((struct static_key) \
+               { .enabled = ATOMIC_INIT(0), .entries = (void *)1 })
+#else /* !HAVE_JUMP_LABEL */
+#define ___NET_RANDOM_STATIC_KEY_INIT STATIC_KEY_INIT_FALSE
+#endif /* HAVE_JUMP_LABEL */
+
+#define net_get_random_once(buf, nbytes)                               \
+       ({                                                              \
+               bool ___ret = false;                                    \
+               static bool ___done = false;                            \
+               static struct static_key ___done_key =                  \
+                       ___NET_RANDOM_STATIC_KEY_INIT;                  \
+               if (!static_key_true(&___done_key))                     \
+                       ___ret = __net_get_random_once(buf,             \
+                                                      nbytes,          \
+                                                      &___done,        \
+                                                      &___done_key);   \
+               ___ret;                                                 \
+       })
+
+#endif /* __BACKPORT_NET_GET_RANDOM_ONCE */
+
 #endif /* __BACKPORT_LINUX_NET_H */
index d5b70293709049e304b09a82f29022cb812a026e..a76108818cbf16cf9f78eb3ce0043ee4e229cc0d 100644 (file)
@@ -1,5 +1,7 @@
 /*
  * Copyright (c) 2013  Hauke Mehrtens <hauke@hauke-m.de>
+ * Copyright (c) 2013  Hannes Frederic Sowa <hannes@stressinduktion.org>
+ * Copyright (c) 2014  Luis R. Rodriguez <mcgrof@do-not-panic.com>
  *
  * Backport functionality introduced in Linux 3.13.
  *
@@ -16,6 +18,7 @@
 #include <linux/module.h>
 #include <linux/regulator/driver.h>
 #include <linux/device.h>
+#include <linux/static_key.h>
 
 static void devm_rdev_release(struct device *dev, void *res)
 {
@@ -225,3 +228,54 @@ int backport_genl_unregister_family(struct genl_family *family)
        return err;
 }
 EXPORT_SYMBOL_GPL(backport_genl_unregister_family);
+
+#ifdef __BACKPORT_NET_GET_RANDOM_ONCE
+struct __net_random_once_work {
+       struct work_struct work;
+       struct static_key *key;
+};
+
+static void __net_random_once_deferred(struct work_struct *w)
+{
+       struct __net_random_once_work *work =
+               container_of(w, struct __net_random_once_work, work);
+       if (!static_key_enabled(work->key))
+               static_key_slow_inc(work->key);
+       kfree(work);
+}
+
+static void __net_random_once_disable_jump(struct static_key *key)
+{
+       struct __net_random_once_work *w;
+
+       w = kmalloc(sizeof(*w), GFP_ATOMIC);
+       if (!w)
+               return;
+
+       INIT_WORK(&w->work, __net_random_once_deferred);
+       w->key = key;
+       schedule_work(&w->work);
+}
+
+bool __net_get_random_once(void *buf, int nbytes, bool *done,
+                          struct static_key *done_key)
+{
+       static DEFINE_SPINLOCK(lock);
+       unsigned long flags;
+
+       spin_lock_irqsave(&lock, flags);
+       if (*done) {
+               spin_unlock_irqrestore(&lock, flags);
+               return false;
+       }
+
+       get_random_bytes(buf, nbytes);
+       *done = true;
+       spin_unlock_irqrestore(&lock, flags);
+
+       __net_random_once_disable_jump(done_key);
+
+       return true;
+}
+EXPORT_SYMBOL_GPL(__net_get_random_once);
+#endif /* __BACKPORT_NET_GET_RANDOM_ONCE */