#ifndef __BACKPORT_LINUX_NET_H
#define __BACKPORT_LINUX_NET_H
#include_next <linux/net.h>
+#include <linux/static_key.h>
/* This backports:
*
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 */
/*
* 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.
*
#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)
{
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 */