Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1676838 > unrolled thread
| Started by | Joe Lawrence <joe.lawrence@redhat.com> |
|---|---|
| First post | 2017-06-28 17:40 +0200 |
| Last post | 2017-06-28 17:40 +0200 |
| Articles | 4 — 2 participants |
Back to article view | Back to linux.kernel
[PATCH v2 0/2] livepatch: add shadow variable API Joe Lawrence <joe.lawrence@redhat.com> - 2017-06-28 17:40 +0200
[PATCH v2 1/2] livepatch: introduce shadow variable API Joe Lawrence <joe.lawrence@redhat.com> - 2017-06-28 17:40 +0200
Re: [PATCH v2 1/2] livepatch: introduce shadow variable API kbuild test robot <lkp@intel.com> - 2017-06-30 16:00 +0200
[PATCH v2 2/2] livepatch: add shadow variable sample programs Joe Lawrence <joe.lawrence@redhat.com> - 2017-06-28 17:40 +0200
| From | Joe Lawrence <joe.lawrence@redhat.com> |
|---|---|
| Date | 2017-06-28 17:40 +0200 |
| Subject | [PATCH v2 0/2] livepatch: add shadow variable API |
| Message-ID | <tXneq-ad-1@gated-at.bofh.it> |
This is v2 of the shadow variable implementation patchset, incorporating
much of the feedback from the first version:
v2:
- squashed the Documentation patch with the API implementation patch
- converted API parameter/return documentation to docbook style comments
in the .c implementation
- converted the klp_shadow string descriptor to an unsigned long
- combined shadow data and klp_shadow structure to one allocation
- adopted kfree_rcu() suggestion
- added klp_shadow_get_or_create() to the API to help avoid racing
klp_shadow_get + klp_shadow_attach() instances
- added klp_shadow_detach_all() to the API to cleanup a set of
<*, num> shadow variables
- created a new set of sample modules to demonstrate the API:
- a buggy module
- fix 1 to plug a memory leak in newly allocate data structures
- fix 2 to add functionality to in-flight data structures
The sample modules are contrived to demonstrate the shadow variable API.
Instead of patching already in-tree code, I created a simple module to
avoid any kallsyms workarounds. That said, the description and
demonstration debug printing could stand further refinement. IMHO, the
code is easier to follow than the periodic kernel messages logged.
Suggestions welcome.
Joe Lawrence (2):
livepatch: introduce shadow variable API
livepatch: add shadow variable sample programs
Documentation/livepatch/shadow-vars.txt | 156 +++++++++++++
include/linux/livepatch.h | 8 +
kernel/livepatch/Makefile | 2 +-
kernel/livepatch/shadow.c | 257 ++++++++++++++++++++++
samples/Kconfig | 5 +-
samples/livepatch/Makefile | 3 +
samples/livepatch/livepatch-shadow-fix1.c | 160 ++++++++++++++
samples/livepatch/livepatch-shadow-fix2.c | 157 +++++++++++++
samples/livepatch/livepatch-shadow-mod.c | 353 ++++++++++++++++++++++++++++++
9 files changed, 1097 insertions(+), 4 deletions(-)
create mode 100644 Documentation/livepatch/shadow-vars.txt
create mode 100644 kernel/livepatch/shadow.c
create mode 100644 samples/livepatch/livepatch-shadow-fix1.c
create mode 100644 samples/livepatch/livepatch-shadow-fix2.c
create mode 100644 samples/livepatch/livepatch-shadow-mod.c
--
1.8.3.1
[toc] | [next] | [standalone]
| From | Joe Lawrence <joe.lawrence@redhat.com> |
|---|---|
| Date | 2017-06-28 17:40 +0200 |
| Subject | [PATCH v2 1/2] livepatch: introduce shadow variable API |
| Message-ID | <tXneq-ad-5@gated-at.bofh.it> |
| In reply to | #1676838 |
Add exported API for livepatch modules:
klp_shadow_get()
klp_shadow_attach()
klp_shadow_get_or_attach()
klp_shadow_detach()
klp_shadow_detach_all()
that implement "shadow" variables, which allow callers to associate new
shadow fields to existing data structures. This is intended to be used
by livepatch modules seeking to emulate additions to data structure
definitions.
See Documentation/livepatch/shadow-vars.txt for a summary of the new
shadow variable API, including a few common use cases.
Signed-off-by: Joe Lawrence <joe.lawrence@redhat.com>
---
Documentation/livepatch/shadow-vars.txt | 156 +++++++++++++++++++
include/linux/livepatch.h | 8 +
kernel/livepatch/Makefile | 2 +-
kernel/livepatch/shadow.c | 257 ++++++++++++++++++++++++++++++++
4 files changed, 422 insertions(+), 1 deletion(-)
create mode 100644 Documentation/livepatch/shadow-vars.txt
create mode 100644 kernel/livepatch/shadow.c
diff --git a/Documentation/livepatch/shadow-vars.txt b/Documentation/livepatch/shadow-vars.txt
new file mode 100644
index 000000000000..7f28982e6b1c
--- /dev/null
+++ b/Documentation/livepatch/shadow-vars.txt
@@ -0,0 +1,156 @@
+Shadow Variables
+================
+
+Shadow variables are a simple way for livepatch modules to associate new
+"shadow" data to existing data structures. Original data structures
+(both their definition and storage) are left unmodified and "new" data
+is allocated separately. A shadow variable hashtable associates a
+string key, enumeration pair with a pointer to the new data.
+
+
+Brief API summary
+-----------------
+
+See the full API usage docbook notes in the livepatch/shadow.c
+implementation.
+
+An in-kernel hashtable references all of the shadow variables. These
+references are stored/retrieved through a <obj, num> key pair.
+
+* The klp_shadow variable data structure encapsulates both tracking
+meta-data and shadow-data:
+ - meta-data
+ - obj - pointer to original data
+ - num - numerical description of new data
+ - new_data[] - storage for shadow data
+
+* klp_shadow_attach() - allocate and add a new shadow variable:
+ - allocate a new shadow variable
+ - push a <obj, num> key pair into hashtable
+
+* klp_shadow_get() - retrieve a shadow variable new_data pointer
+ - search hashtable for <obj, num> key pair
+
+* klp_shadow_get_or_attach() - get existing or attach a new shadow variable
+ - search hashtable for <obj, num> key pair
+ - if not found, call klp_shadow_attach()
+
+* klp_shadow_detach() - detach and free a <obj, num> shadow variable
+ - find and remove any <obj, num> references from hashtable
+ - if found, release shadow variable
+
+* klp_shadow_detach() - detach and free all <*, num> shadow variables
+ - find and remove any <*, num> references from hashtable
+ - if found, release shadow variable
+
+
+Use cases
+---------
+
+See the example shadow variable livepatch modules in samples/livepatch
+for full working demonstrations.
+
+Example 1: Commit 1d147bfa6429 ("mac80211: fix AP powersave TX vs.
+wakeup race") added a spinlock to net/mac80211/sta_info.h :: struct
+sta_info. Implementing this change with a shadow variable is
+straightforward.
+
+Allocation - when a host sta_info structure is allocated, attach a
+shadow variable copy of the ps_lock:
+
+#define PS_LOCK 1
+struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
+ const u8 *addr, gfp_t gfp)
+{
+ struct sta_info *sta;
+ spinlock_t *ps_lock;
+ ...
+ sta = kzalloc(sizeof(*sta) + hw->sta_data_size, gfp);
+ ...
+ ps_lock = klp_shadow_attach(sta, PS_LOCK, NULL, sizeof(*ps_lock), gfp);
+ if (!ps_lock)
+ goto shadow_fail;
+ spin_lock_init(ps_lock);
+ ...
+
+Usage - when using the shadow spinlock, query the shadow variable API to
+retrieve it:
+
+void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)
+{
+ spinlock_t *ps_lock;
+ ...
+ /* sync with ieee80211_tx_h_unicast_ps_buf */
+ ps_lock = klp_shadow_get(sta, "ps_lock");
+ if (ps_lock)
+ spin_lock(ps_lock);
+ ...
+ if (ps_lock)
+ spin_unlock(ps_lock);
+ ...
+
+Release - when the host sta_info structure is freed, first detach the
+shadow variable and then free the shadow spinlock:
+
+void sta_info_free(struct ieee80211_local *local, struct sta_info *sta)
+{
+ spinlock_t *ps_lock;
+ ...
+ ps_lock = klp_shadow_get(sta, "ps_lock");
+ if (ps_lock)
+ klp_shadow_detach(sta, "ps_lock");
+
+ kfree(sta);
+
+
+Example 2: Commit 82486aa6f1b9 ("ipv4: restore rt->fi for reference
+counting") added a struct fib_info pointer to include/net/route.h ::
+struct rtable. A shadow variable can be used to implement the new
+pointer.
+
+This implementation diverges from the original commit, as it can attach
+the shadow variable when the code actually uses it:
+
+#define FIB_INFO 1
+static void rt_init_metrics(struct rtable *rt, struct fib_info *fi)
+{
+ if (fi->fib_metrics != (u32 *)dst_default_metrics) {
+ fib_info_hold(&fi);
+ klp_shadow_attach(rt, FIB_INFO, &fi, sizeof(fi), GFP_KERNEL)
+ }
+
+ dst_init_metrics(&rt->dst, fi->fib_metrics, true);
+}
+
+The shadow variable can be detached when it's no longer needed:
+
+static void ipv4_dst_destroy(struct dst_entry *dst)
+{
+ struct rtable *rt = (struct rtable *) dst;
+ struct fib_info *shadow_fi;
+
+ shadow_fi = klp_shadow_get(rt, "fi");
+ if (shadow_fi) {
+ klp_shadow_detach(rt, "fi");
+ fib_info_put(shadow_fi);
+ }
+
+
+Other examples: shadow variables can also be used as a simple flag
+indicating that a data structure had been allocated by new, livepatched
+code. In this case, it doesn't matter what new_data value the shadow
+variable holds, its existence can be keyed off of to handle the data
+structure accordingly.
+
+
+Reference
+==========
+
+* https://github.com/dynup/kpatch
+The livepatch implementation is based on the kpatch version of shadow
+variables.
+
+* http://files.mkgnu.net/files/dynamos/doc/papers/dynamos_eurosys_07.pdf
+Dynamic and Adaptive Updates of Non-Quiescent Subsystems in Commodity
+Operating System Kernels (Kritis Makris, Kyung Dong Ryu 2007) presented
+a datatype update technique called "shadow data structures".
diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h
index 194991ef9347..4cf3c285784d 100644
--- a/include/linux/livepatch.h
+++ b/include/linux/livepatch.h
@@ -164,6 +164,14 @@ static inline bool klp_have_reliable_stack(void)
IS_ENABLED(CONFIG_HAVE_RELIABLE_STACKTRACE);
}
+void *klp_shadow_get(void *obj, unsigned long num);
+void *klp_shadow_attach(void *obj, unsigned long num, void *new_data,
+ size_t new_size, gfp_t gfp_flags);
+void *klp_shadow_get_or_attach(void *obj, unsigned long num, void *new_data,
+ size_t new_size, gfp_t gfp_flags);
+void klp_shadow_detach(void *obj, unsigned long num);
+void klp_shadow_detach_all(unsigned long num);
+
#else /* !CONFIG_LIVEPATCH */
static inline int klp_module_coming(struct module *mod) { return 0; }
diff --git a/kernel/livepatch/Makefile b/kernel/livepatch/Makefile
index 2b8bdb1925da..b36ceda6488e 100644
--- a/kernel/livepatch/Makefile
+++ b/kernel/livepatch/Makefile
@@ -1,3 +1,3 @@
obj-$(CONFIG_LIVEPATCH) += livepatch.o
-livepatch-objs := core.o patch.o transition.o
+livepatch-objs := core.o patch.o shadow.o transition.o
diff --git a/kernel/livepatch/shadow.c b/kernel/livepatch/shadow.c
new file mode 100644
index 000000000000..d37a61c57e72
--- /dev/null
+++ b/kernel/livepatch/shadow.c
@@ -0,0 +1,257 @@
+/*
+ * shadow.c - Shadow Variables
+ *
+ * Copyright (C) 2014 Josh Poimboeuf <jpoimboe@redhat.com>
+ * Copyright (C) 2014 Seth Jennings <sjenning@redhat.com>
+ * Copyright (C) 2017 Joe Lawrence <joe.lawrence@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * DOC: Shadow variable API concurrency notes:
+ *
+ * The shadow variable API simply provides a relationship between an
+ * <obj, num> pair and a pointer value. It is the responsibility of the
+ * caller to provide any mutual exclusion required of the shadow data.
+ *
+ * Once klp_shadow_attach() adds a shadow variable to the
+ * klp_shadow_hash, it is considered live and klp_shadow_get() may
+ * return the shadow variable's new_data pointer. Therefore,
+ * initialization of shadow new_data should be completed before
+ * attaching the shadow variable.
+ *
+ * Alternatively, the klp_shadow_get_or_attach() call may be used to
+ * safely fetch any existing <obj, num> match, or create a new
+ * <obj, num> shadow variable if none exists.
+ *
+ * If the API is called under a special context (like spinlocks), set
+ * the GFP flags passed to klp_shadow_attach() accordingly.
+ *
+ * The klp_shadow_hash is an RCU-enabled hashtable and should be safe
+ * against concurrent klp_shadow_detach() and klp_shadow_get()
+ * operations.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/hashtable.h>
+#include <linux/slab.h>
+#include <linux/livepatch.h>
+
+static DEFINE_HASHTABLE(klp_shadow_hash, 12);
+static DEFINE_SPINLOCK(klp_shadow_lock);
+
+/**
+ * struct klp_shadow - shadow variable structure
+ * @node: klp_shadow_hash hash table node
+ * @rcu_head: RCU is used to safely free this structure
+ * @obj: pointer to original data
+ * @num: numerical description of new data
+ * @new_data: new data area
+ */
+struct klp_shadow {
+ struct hlist_node node;
+ struct rcu_head rcu_head;
+ void *obj;
+ unsigned long num;
+ char new_data[];
+};
+
+/**
+ * shadow_match() - verify a shadow variable matches given <obj, num>
+ * @shadow: shadow variable to match
+ * @obj: pointer to original data
+ * @num: numerical description of new data
+ *
+ * Return: true if the shadow variable matches.
+ */
+static inline bool shadow_match(struct klp_shadow *shadow, void *obj,
+ unsigned long num)
+{
+ return shadow->obj == obj && shadow->num == num;
+}
+
+/**
+ * klp_shadow_get() - retrieve a shadow variable new_data pointer
+ * @obj: pointer to original data
+ * @num: numerical description of new data
+ *
+ * Return: a pointer to shadow variable new data
+ */
+void *klp_shadow_get(void *obj, unsigned long num)
+{
+ struct klp_shadow *shadow;
+
+ rcu_read_lock();
+
+ hash_for_each_possible_rcu(klp_shadow_hash, shadow, node,
+ (unsigned long)obj) {
+
+ if (shadow_match(shadow, obj, num)) {
+ rcu_read_unlock();
+ return shadow->new_data;
+ }
+ }
+
+ rcu_read_unlock();
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(klp_shadow_get);
+
+/**
+ * _klp_shadow_attach() - allocate and add a new shadow variable
+ * @obj: pointer to original data
+ * @num: numerical description of new data
+ * @new_data: pointer to new data
+ * @new_size: size of new data
+ * @gfp_flags: GFP mask for allocation
+ * @lock: take klp_shadow_lock during klp_shadow_hash operations
+ *
+ * Note: allocates @new_size space for shadow variable data and copies
+ * @new_size bytes from @new_data into the shadow varaible's own @new_data
+ * space. If @new_data is NULL, @new_size is still allocated, but no
+ * copy is performed.
+ *
+ * Return: the shadow variable new_data element, NULL on failure.
+ */
+static void *_klp_shadow_attach(void *obj, unsigned long num, void *new_data,
+ size_t new_size, gfp_t gfp_flags,
+ bool lock)
+{
+ struct klp_shadow *shadow;
+ unsigned long flags;
+
+ shadow = kzalloc(new_size + sizeof(*shadow), gfp_flags);
+ if (!shadow)
+ return NULL;
+
+ shadow->obj = obj;
+ shadow->num = num;
+ if (new_data)
+ memcpy(shadow->new_data, new_data, new_size);
+
+ if (lock)
+ spin_lock_irqsave(&klp_shadow_lock, flags);
+ hash_add_rcu(klp_shadow_hash, &shadow->node, (unsigned long)obj);
+ if (lock)
+ spin_unlock_irqrestore(&klp_shadow_lock, flags);
+
+ return shadow->new_data;
+}
+
+/**
+ * klp_shadow_attach() - allocate and add a new shadow variable
+ * @obj: pointer to original data
+ * @num: numerical description of new num
+ * @new_data: pointer to new data
+ * @new_size: size of new data
+ * @gfp_flags: GFP mask for allocation
+ *
+ * Return: the shadow variable new_data element, NULL on failure.
+ */
+void *klp_shadow_attach(void *obj, unsigned long num, void *new_data,
+ size_t new_size, gfp_t gfp_flags)
+{
+ return _klp_shadow_attach(obj, num, new_data, new_size,
+ gfp_flags, true);
+}
+EXPORT_SYMBOL_GPL(klp_shadow_attach);
+
+/**
+ * klp_shadow_get_or_attach() - get existing or attach a new shadow variable
+ * @obj: pointer to original data
+ * @num: numerical description of new data
+ * @new_data: pointer to new data
+ * @new_size: size of new data
+ * @gfp_flags: GFP mask used to allocate shadow variable metadata
+ *
+ * Note: if memory allocation is necessary, it will do so under a spinlock,
+ * so @gfp_flags should include GFP_NOWAIT, or GFP_ATOMIC, etc.
+ *
+ * Return: the shadow variable new_data element, NULL on failure.
+ */
+void *klp_shadow_get_or_attach(void *obj, unsigned long num, void *new_data,
+ size_t new_size, gfp_t gfp_flags)
+{
+ void *nd;
+ unsigned long flags;
+
+ nd = klp_shadow_get(obj, num);
+
+ if (!nd) {
+ spin_lock_irqsave(&klp_shadow_lock, flags);
+ nd = klp_shadow_get(obj, num);
+ if (!nd)
+ nd = _klp_shadow_attach(obj, num, new_data, new_size,
+ gfp_flags, false);
+ spin_unlock_irqrestore(&klp_shadow_lock, flags);
+ }
+
+ return nd;
+
+}
+EXPORT_SYMBOL_GPL(klp_shadow_get_or_attach);
+
+/**
+ * klp_shadow_detach() - detach and free a <obj, num> shadow variable
+ * @obj: pointer to original data
+ * @num: numerical description of new data
+ */
+void klp_shadow_detach(void *obj, unsigned long num)
+{
+ struct klp_shadow *shadow;
+ unsigned long flags;
+
+ spin_lock_irqsave(&klp_shadow_lock, flags);
+
+ /* Delete all <obj, num> from hash */
+ hash_for_each_possible(klp_shadow_hash, shadow, node,
+ (unsigned long)obj) {
+
+ if (shadow_match(shadow, obj, num)) {
+ hash_del_rcu(&shadow->node);
+ kfree_rcu(shadow, rcu_head);
+ break;
+ }
+ }
+
+ spin_unlock_irqrestore(&klp_shadow_lock, flags);
+}
+EXPORT_SYMBOL_GPL(klp_shadow_detach);
+
+/**
+ * klp_shadow_detach_all() - detach all <*, num> shadow variables
+ * @num: numerical description of new data
+ */
+void klp_shadow_detach_all(unsigned long num)
+{
+ struct klp_shadow *shadow;
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&klp_shadow_lock, flags);
+
+ /* Delete all <*, num> from hash */
+ hash_for_each(klp_shadow_hash, i, shadow, node) {
+ if (shadow_match(shadow, shadow->obj, num)) {
+ hash_del_rcu(&shadow->node);
+ kfree_rcu(shadow, rcu_head);
+ }
+ }
+
+ spin_unlock_irqrestore(&klp_shadow_lock, flags);
+}
+EXPORT_SYMBOL_GPL(klp_shadow_detach_all);
--
1.8.3.1
[toc] | [prev] | [next] | [standalone]
| From | kbuild test robot <lkp@intel.com> |
|---|---|
| Date | 2017-06-30 16:00 +0200 |
| Subject | Re: [PATCH v2 1/2] livepatch: introduce shadow variable API |
| Message-ID | <tY4CL-6b8-23@gated-at.bofh.it> |
| In reply to | #1676840 |
[Multipart message — attachments visible in raw view] — view raw
Hi Joe,
[auto build test WARNING on jikos-livepatching/for-next]
[also build test WARNING on v4.12-rc7 next-20170630]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/Joe-Lawrence/livepatch-introduce-shadow-variable-API/20170630-061942
base: https://git.kernel.org/pub/scm/linux/kernel/git/jikos/livepatching.git for-next
config: s390-performance_defconfig (attached as .config)
compiler: s390x-linux-gnu-gcc (Debian 6.1.1-9) 6.1.1 20160705
reproduce:
wget https://raw.githubusercontent.com/01org/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# save the attached .config to linux build tree
make.cross ARCH=s390
Note: it may well be a FALSE warning. FWIW you are at least aware of it now.
http://gcc.gnu.org/wiki/Better_Uninitialized_Warnings
All warnings (new ones prefixed by >>):
In file included from include/linux/irqflags.h:15:0,
from arch/s390/include/asm/processor.h:35,
from arch/s390/include/asm/thread_info.h:24,
from include/linux/thread_info.h:37,
from arch/s390/include/asm/preempt.h:5,
from include/linux/preempt.h:80,
from include/linux/spinlock.h:50,
from include/linux/rcupdate.h:38,
from include/linux/rculist.h:10,
from include/linux/hashtable.h:13,
from kernel/livepatch/shadow.c:49:
kernel/livepatch/shadow.c: In function '_klp_shadow_attach':
>> arch/s390/include/asm/irqflags.h:63:12: warning: 'flags' may be used uninitialized in this function [-Wmaybe-uninitialized]
if (flags & ARCH_IRQ_ENABLED)
^
kernel/livepatch/shadow.c:135:16: note: 'flags' was declared here
unsigned long flags;
^~~~~
--
In file included from include/linux/irqflags.h:15:0,
from arch/s390/include/asm/processor.h:35,
from arch/s390/include/asm/thread_info.h:24,
from include/linux/thread_info.h:37,
from arch/s390/include/asm/preempt.h:5,
from include/linux/preempt.h:80,
from include/linux/spinlock.h:50,
from include/linux/rcupdate.h:38,
from include/linux/rculist.h:10,
from include/linux/hashtable.h:13,
from kernel//livepatch/shadow.c:49:
kernel//livepatch/shadow.c: In function '_klp_shadow_attach':
>> arch/s390/include/asm/irqflags.h:63:12: warning: 'flags' may be used uninitialized in this function [-Wmaybe-uninitialized]
if (flags & ARCH_IRQ_ENABLED)
^
kernel//livepatch/shadow.c:135:16: note: 'flags' was declared here
unsigned long flags;
^~~~~
vim +/flags +63 arch/s390/include/asm/irqflags.h
94c12cc7d include/asm-s390/irqflags.h Martin Schwidefsky 2006-09-28 47 }
94c12cc7d include/asm-s390/irqflags.h Martin Schwidefsky 2006-09-28 48
f433c4aec arch/s390/include/asm/irqflags.h Steven Rostedt 2011-07-24 49 static inline notrace void arch_local_irq_disable(void)
df9ee2927 arch/s390/include/asm/irqflags.h David Howells 2010-10-07 50 {
df9ee2927 arch/s390/include/asm/irqflags.h David Howells 2010-10-07 51 arch_local_irq_save();
df9ee2927 arch/s390/include/asm/irqflags.h David Howells 2010-10-07 52 }
1f194a4c3 include/asm-s390/irqflags.h Heiko Carstens 2006-07-03 53
f433c4aec arch/s390/include/asm/irqflags.h Steven Rostedt 2011-07-24 54 static inline notrace void arch_local_irq_enable(void)
94c12cc7d include/asm-s390/irqflags.h Martin Schwidefsky 2006-09-28 55 {
df9ee2927 arch/s390/include/asm/irqflags.h David Howells 2010-10-07 56 __arch_local_irq_stosm(0x03);
94c12cc7d include/asm-s390/irqflags.h Martin Schwidefsky 2006-09-28 57 }
1f194a4c3 include/asm-s390/irqflags.h Heiko Carstens 2006-07-03 58
204ee2c56 arch/s390/include/asm/irqflags.h Christian Borntraeger 2016-01-11 59 /* This only restores external and I/O interrupt state */
f433c4aec arch/s390/include/asm/irqflags.h Steven Rostedt 2011-07-24 60 static inline notrace void arch_local_irq_restore(unsigned long flags)
df9ee2927 arch/s390/include/asm/irqflags.h David Howells 2010-10-07 61 {
204ee2c56 arch/s390/include/asm/irqflags.h Christian Borntraeger 2016-01-11 62 /* only disabled->disabled and disabled->enabled is valid */
204ee2c56 arch/s390/include/asm/irqflags.h Christian Borntraeger 2016-01-11 @63 if (flags & ARCH_IRQ_ENABLED)
204ee2c56 arch/s390/include/asm/irqflags.h Christian Borntraeger 2016-01-11 64 arch_local_irq_enable();
df9ee2927 arch/s390/include/asm/irqflags.h David Howells 2010-10-07 65 }
df9ee2927 arch/s390/include/asm/irqflags.h David Howells 2010-10-07 66
f433c4aec arch/s390/include/asm/irqflags.h Steven Rostedt 2011-07-24 67 static inline notrace bool arch_irqs_disabled_flags(unsigned long flags)
1f194a4c3 include/asm-s390/irqflags.h Heiko Carstens 2006-07-03 68 {
204ee2c56 arch/s390/include/asm/irqflags.h Christian Borntraeger 2016-01-11 69 return !(flags & ARCH_IRQ_ENABLED);
1f194a4c3 include/asm-s390/irqflags.h Heiko Carstens 2006-07-03 70 }
1f194a4c3 include/asm-s390/irqflags.h Heiko Carstens 2006-07-03 71
:::::: The code at line 63 was first introduced by commit
:::::: 204ee2c5643199a25181ec04ea645d00709c2a5a s390/irqflags: optimize irq restore
:::::: TO: Christian Borntraeger <borntraeger@de.ibm.com>
:::::: CC: Martin Schwidefsky <schwidefsky@de.ibm.com>
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
[toc] | [prev] | [next] | [standalone]
| From | Joe Lawrence <joe.lawrence@redhat.com> |
|---|---|
| Date | 2017-06-28 17:40 +0200 |
| Subject | [PATCH v2 2/2] livepatch: add shadow variable sample programs |
| Message-ID | <tXneq-ad-7@gated-at.bofh.it> |
| In reply to | #1676838 |
Add sample livepatch modules to demonstrate the shadow variable API.
Signed-off-by: Joe Lawrence <joe.lawrence@redhat.com>
---
Reviewers -- it's probably easier to grok reading livepatch-shadow-mod.c
*before* the two livepatch modules, livepatch-shadow-fix1.c
and livepatch-shadow-fix2.c.
samples/Kconfig | 5 +-
samples/livepatch/Makefile | 3 +
samples/livepatch/livepatch-shadow-fix1.c | 160 ++++++++++++++
samples/livepatch/livepatch-shadow-fix2.c | 157 +++++++++++++
samples/livepatch/livepatch-shadow-mod.c | 353 ++++++++++++++++++++++++++++++
5 files changed, 675 insertions(+), 3 deletions(-)
create mode 100644 samples/livepatch/livepatch-shadow-fix1.c
create mode 100644 samples/livepatch/livepatch-shadow-fix2.c
create mode 100644 samples/livepatch/livepatch-shadow-mod.c
diff --git a/samples/Kconfig b/samples/Kconfig
index 9cb63188d3ef..c332a3b9de05 100644
--- a/samples/Kconfig
+++ b/samples/Kconfig
@@ -71,11 +71,10 @@ config SAMPLE_RPMSG_CLIENT
the rpmsg bus.
config SAMPLE_LIVEPATCH
- tristate "Build live patching sample -- loadable modules only"
+ tristate "Build live patching samples -- loadable modules only"
depends on LIVEPATCH && m
help
- Builds a sample live patch that replaces the procfs handler
- for /proc/cmdline to print "this has been live patched".
+ Build sample live patch demonstrations.
config SAMPLE_CONFIGFS
tristate "Build configfs patching sample -- loadable modules only"
diff --git a/samples/livepatch/Makefile b/samples/livepatch/Makefile
index 10319d7ea0b1..539e81d433cd 100644
--- a/samples/livepatch/Makefile
+++ b/samples/livepatch/Makefile
@@ -1 +1,4 @@
obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-sample.o
+obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-shadow-mod.o
+obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-shadow-fix1.o
+obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-shadow-fix2.o
diff --git a/samples/livepatch/livepatch-shadow-fix1.c b/samples/livepatch/livepatch-shadow-fix1.c
new file mode 100644
index 000000000000..2e1d9cb89fad
--- /dev/null
+++ b/samples/livepatch/livepatch-shadow-fix1.c
@@ -0,0 +1,160 @@
+/*
+ * livepatch-shadow-fix1.c - Shadow variables, livepatch demo
+ *
+ * Copyright (C) 2017 Joe Lawrence <joe.lawrence@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Fixes the memory leak introduced in livepatch-shadow-mod through the
+ * use of a shadow variable. This fix demonstrates the "extending" of
+ * short-lived data structures by patching its allocation and release
+ * functions.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/livepatch.h>
+#include <linux/slab.h>
+
+/* Shadow variable enums */
+#define SV_LEAK 1
+
+#define T1_PERIOD 1 /* allocator thread */
+#define T2_PERIOD (3 * T1_PERIOD) /* cleanup thread */
+
+struct dummy {
+ struct list_head list;
+ unsigned long jiffies_expire;
+};
+
+struct dummy *livepatch_fix1_dummy_alloc(void)
+{
+ struct dummy *d;
+ void *leak;
+ void **shadow_leak;
+
+ d = kzalloc(sizeof(*d), GFP_KERNEL);
+ if (!d)
+ return NULL;
+
+ /* Dummies live long enough to see a few t2 instances */
+ d->jiffies_expire = jiffies + 1000 * 4 * T2_PERIOD;
+
+ /*
+ * Patch: save the extra memory location into a SV_LEAK shadow
+ * variable. A patched dummy_free routine can later fetch this
+ * pointer to handle resource release.
+ */
+ leak = kzalloc(sizeof(int), GFP_KERNEL);
+ shadow_leak =
+ klp_shadow_attach(d, SV_LEAK, &leak, sizeof(leak), GFP_KERNEL);
+
+ pr_info("%s: dummy @ %p, expires @ %lx\n",
+ __func__, d, d->jiffies_expire);
+
+ return d;
+}
+
+void livepatch_fix1_dummy_free(struct dummy *d)
+{
+ void **shadow_leak;
+
+ /*
+ * Patch: fetch the saved SV_LEAK shadow variable, detach and
+ * free it. Note: handle cases where this shadow variable does
+ * not exist (ie, dummy structures allocated before this livepatch
+ * was loaded.)
+ */
+ shadow_leak = klp_shadow_get(d, SV_LEAK);
+ if (shadow_leak) {
+ klp_shadow_detach(d, SV_LEAK);
+ kfree(*shadow_leak);
+ pr_info("%s: dummy @ %p, prevented leak @ %p\n",
+ __func__, d, *shadow_leak);
+ } else {
+ pr_info("%s: dummy @ %p leaked!\n", __func__, d);
+ }
+
+ kfree(d);
+}
+
+static struct klp_func funcs[] = {
+ {
+ .old_name = "dummy_alloc",
+ .new_func = livepatch_fix1_dummy_alloc,
+ },
+ {
+ .old_name = "dummy_free",
+ .new_func = livepatch_fix1_dummy_free,
+ }, { }
+};
+
+static struct klp_object objs[] = {
+ {
+ .name = "livepatch_shadow_mod",
+ .funcs = funcs,
+ }, { }
+};
+
+static struct klp_patch patch = {
+ .mod = THIS_MODULE,
+ .objs = objs,
+};
+
+static int livepatch_shadow_fix1_init(void)
+{
+ int ret;
+
+ if (!klp_have_reliable_stack() && !patch.immediate) {
+ /*
+ * WARNING: Be very careful when using 'patch.immediate' in
+ * your patches. It's ok to use it for simple patches like
+ * this, but for more complex patches which change function
+ * semantics, locking semantics, or data structures, it may not
+ * be safe. Use of this option will also prevent removal of
+ * the patch.
+ *
+ * See Documentation/livepatch/livepatch.txt for more details.
+ */
+ patch.immediate = true;
+ pr_notice("The consistency model isn't supported for your architecture. Bypassing safety mechanisms and applying the patch immediately.\n");
+ }
+
+ ret = klp_register_patch(&patch);
+ if (ret)
+ return ret;
+ ret = klp_enable_patch(&patch);
+ if (ret) {
+ WARN_ON(klp_unregister_patch(&patch));
+ return ret;
+ }
+ return 0;
+}
+
+static void livepatch_shadow_fix1_exit(void)
+{
+ /* Cleanup any existing SV_LEAK shadow variables */
+ klp_shadow_detach_all(SV_LEAK);
+
+ WARN_ON(klp_unregister_patch(&patch));
+}
+
+module_init(livepatch_shadow_fix1_init);
+module_exit(livepatch_shadow_fix1_exit);
+MODULE_LICENSE("GPL");
+MODULE_INFO(livepatch, "Y");
diff --git a/samples/livepatch/livepatch-shadow-fix2.c b/samples/livepatch/livepatch-shadow-fix2.c
new file mode 100644
index 000000000000..ad6346da4aa3
--- /dev/null
+++ b/samples/livepatch/livepatch-shadow-fix2.c
@@ -0,0 +1,157 @@
+/*
+ * livepatch-shadow-fix2.c - Shadow variables, livepatch demo
+ *
+ * Copyright (C) 2017 Joe Lawrence <joe.lawrence@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Adds functionality to livepatch-shadow-mod's in-flight data
+ * structures through a shadow variable. The livepatch patches a
+ * routine that periodically inspects data structures, incrementing a
+ * per-data-structure counter, creating the counter if needed.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/livepatch.h>
+#include <linux/slab.h>
+
+/* Shadow variable enums */
+#define SV_LEAK 1
+#define SV_COUNTER 2
+
+struct dummy {
+ struct list_head list;
+ unsigned long jiffies_expire;
+};
+
+bool livepatch_fix2_dummy_check(struct dummy *d, unsigned long jiffies)
+{
+ int *shadow_count;
+ int count;
+
+ /*
+ * Patch: handle in-flight dummy structures, if they do not
+ * already have a SV_COUNTER shadow variable, then attach a
+ * new one.
+ */
+ count = 0;
+ shadow_count = klp_shadow_get_or_attach(d, SV_COUNTER,
+ &count, sizeof(count),
+ GFP_NOWAIT);
+ if (shadow_count)
+ *shadow_count += 1;
+
+ return time_after(jiffies, d->jiffies_expire);
+}
+
+void livepatch_fix2_dummy_free(struct dummy *d)
+{
+ void **shadow_leak;
+ int *shadow_count;
+
+ /* Patch: copy the memory leak patch from the fix1 module. */
+ shadow_leak = klp_shadow_get(d, SV_LEAK);
+ if (shadow_leak) {
+ pr_info("%s: dummy @ %p, prevented leak @ %p\n",
+ __func__, d, *shadow_leak);
+ klp_shadow_detach(d, SV_LEAK);
+ kfree(*shadow_leak);
+ } else {
+ pr_info("%s: dummy @ %p leaked!\n", __func__, d);
+ }
+
+ /*
+ * Patch: fetch the SV_COUNTER shadow variable and display
+ * the final count. Detach the shadow variable.
+ */
+ shadow_count = klp_shadow_get(d, SV_COUNTER);
+ if (shadow_count) {
+ pr_info("%s: dummy @ %p, check counter = %d\n",
+ __func__, d, *shadow_count);
+ klp_shadow_detach(d, SV_COUNTER);
+ }
+
+ kfree(d);
+}
+
+static struct klp_func funcs[] = {
+ {
+ .old_name = "dummy_check",
+ .new_func = livepatch_fix2_dummy_check,
+ },
+ {
+ .old_name = "dummy_free",
+ .new_func = livepatch_fix2_dummy_free,
+ }, { }
+};
+
+static struct klp_object objs[] = {
+ {
+ .name = "livepatch_shadow_mod",
+ .funcs = funcs,
+ }, { }
+};
+
+static struct klp_patch patch = {
+ .mod = THIS_MODULE,
+ .objs = objs,
+};
+
+static int livepatch_shadow_fix2_init(void)
+{
+ int ret;
+
+ if (!klp_have_reliable_stack() && !patch.immediate) {
+ /*
+ * WARNING: Be very careful when using 'patch.immediate' in
+ * your patches. It's ok to use it for simple patches like
+ * this, but for more complex patches which change function
+ * semantics, locking semantics, or data structures, it may not
+ * be safe. Use of this option will also prevent removal of
+ * the patch.
+ *
+ * See Documentation/livepatch/livepatch.txt for more details.
+ */
+ patch.immediate = true;
+ pr_notice("The consistency model isn't supported for your architecture. Bypassing safety mechanisms and applying the patch immediately.\n");
+ }
+
+ ret = klp_register_patch(&patch);
+ if (ret)
+ return ret;
+ ret = klp_enable_patch(&patch);
+ if (ret) {
+ WARN_ON(klp_unregister_patch(&patch));
+ return ret;
+ }
+ return 0;
+}
+
+static void livepatch_shadow_fix2_exit(void)
+{
+ /* Cleanup any existing SV_COUNTER shadow variables */
+ klp_shadow_detach_all(SV_COUNTER);
+
+ WARN_ON(klp_unregister_patch(&patch));
+}
+
+module_init(livepatch_shadow_fix2_init);
+module_exit(livepatch_shadow_fix2_exit);
+MODULE_LICENSE("GPL");
+MODULE_INFO(livepatch, "Y");
diff --git a/samples/livepatch/livepatch-shadow-mod.c b/samples/livepatch/livepatch-shadow-mod.c
new file mode 100644
index 000000000000..423f4b7b0adb
--- /dev/null
+++ b/samples/livepatch/livepatch-shadow-mod.c
@@ -0,0 +1,353 @@
+/*
+ * livepatch-shadow-mod.c - Shadow variables, buggy module demo
+ *
+ * Copyright (C) 2017 Joe Lawrence <joe.lawrence@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* Creates two running threads:
+ *
+ * T1 - allocs a new dummy structure, sets a jiffie expiration time
+ * in the future, adds the new structure to a list
+ *
+ * T2 - cleans up expired dummies on the list
+ *
+ * For the purposes of demonstrating a livepatch shadow variable fix,
+ * the creation thread also allocates additional memory, but doesn't
+ * save a pointer to it in the dummy structure. The cleanup thread
+ * then leaks the extra memory when it frees (only) the dummy
+ * structure.
+ *
+ *
+ * Usage
+ * -----
+ *
+ * Load the buggy demonstration module:
+ * $ insmod samples/livepatch/livepatch-shadow-mod.ko
+ *
+ * T1 allocator thread periodically wakes up and creates new dummy
+ * structures allocating extra memory and set to expire some jiffie time
+ * in the future:
+ *
+ * [ 529.503048] dummy_alloc: dummy @ ffff880112921e38, expires @ 10003af60
+ * [ 530.527051] dummy_alloc: dummy @ ffff8801129219e8, expires @ 10003b360
+ * [ 531.551049] dummy_alloc: dummy @ ffff880112920458, expires @ 10003b760
+ *
+ * T2 cleanup thread wakes up, but doesn't find any expired dummies to
+ * cleanup yet:
+ *
+ * [ 531.551212] cleanup_thread: jiffies = 100038880
+ * [ 532.575045] dummy_alloc: dummy @ ffff880112921708, expires @ 10003bb60
+ * [ 533.599051] dummy_alloc: dummy @ ffff880112920a18, expires @ 10003bf60
+ * [ 534.623052] dummy_alloc: dummy @ ffff8801129205c8, expires @ 10003c360
+ * [ 535.647056] dummy_alloc: dummy @ ffff880112921598, expires @ 10003c760
+ * [ 536.671032] dummy_alloc: dummy @ ffff880112920fd8, expires @ 10003cb60
+ * [ 537.567089] cleanup_thread: jiffies = 10003a000
+ * [ 537.695043] dummy_alloc: dummy @ ffff880112920008, expires @ 10003cf60
+ * [ 538.719047] dummy_alloc: dummy @ ffff880112921878, expires @ 10003d360
+ * [ 539.743061] dummy_alloc: dummy @ ffff880112920cf8, expires @ 10003d760
+ * [ 540.767041] dummy_alloc: dummy @ ffff880116567148, expires @ 10003db60
+ * [ 541.791045] dummy_alloc: dummy @ ffff880116567598, expires @ 10003df60
+ * [ 542.815046] dummy_alloc: dummy @ ffff880116567cc8, expires @ 10003e360
+ *
+ * T2 cleanup thread eventually finds a few expired dummies, frees them,
+ * and in the process leaks memory!
+ *
+ * [ 543.711058] cleanup_thread: jiffies = 10003b800
+ * [ 543.711366] dummy_free: dummy @ ffff880112920458, expired = 10003b760
+ * [ 543.711853] dummy_free: dummy @ ffff8801129219e8, expired = 10003b360
+ * [ 543.712294] dummy_free: dummy @ ffff880112921e38, expired = 10003af60
+ * [ 543.839046] dummy_alloc: dummy @ ffff8801165672b8, expires @ 10003e760
+ * [ 544.863054] dummy_alloc: dummy @ ffff8801165665c8, expires @ 10003eb60
+ * [ 545.887066] dummy_alloc: dummy @ ffff880119c42008, expires @ 10003ef60
+ * [ 546.911046] dummy_alloc: dummy @ ffff88011aa05b58, expires @ 10003f360
+ * [ 547.935048] dummy_alloc: dummy @ ffff8801150005c8, expires @ 10003f760
+ * [ 548.959062] dummy_alloc: dummy @ ffff880113ee9e38, expires @ 10003fb60
+ *
+ *
+ * Fix the memory leak
+ * -------------------
+ *
+ * One way to fix this memory leak is to attach a shadow variable
+ * pointer to each dummy structure at its allocation point. This
+ * use-case demonstrates a livepatch/shadow variable fix for short-lived
+ * data structures.
+ *
+ * In this example, existing dummy structures will unfortunately
+ * continue to leak memory, however once all of the dummies that were
+ * allocated before the live patch are retired, the memory leak will be
+ * closed.
+ *
+ * Load the livepatch fix1:
+ * $ insmod samples/livepatch/livepatch-shadow-fix1.ko
+ *
+ * T1 alloc thread wakes up and now calls a patched dummy_alloc() which
+ * saves the extra memory into a shadow variable:
+ *
+ * [ 564.147027] livepatch: enabling patch 'livepatch_shadow_fix1'
+ * [ 564.153922] livepatch: 'livepatch_shadow_fix1': patching...
+ * [ 564.319052] livepatch_shadow_fix1: livepatch_fix1_dummy_alloc: dummy @ ffff880112af88a8, expires @ 100043760
+ * [ 565.343060] livepatch_shadow_fix1: livepatch_fix1_dummy_alloc: dummy @ ffff880112af9708, expires @ 100043b60
+ * [ 565.727039] livepatch: 'livepatch_shadow_fix1': patching complete
+ * [ 566.367050] livepatch_shadow_fix1: livepatch_fix1_dummy_alloc: dummy @ ffff880112af8fd8, expires @ 100043f60
+ * [ 567.391047] livepatch_shadow_fix1: livepatch_fix1_dummy_alloc: dummy @ ffff880112af9878, expires @ 100044360
+ *
+ * T2 cleanup thread calls a patched dummy_free() routine which retrives
+ * the shadow variable that saved the memory pointer.
+ *
+ * Note: Initially, memory will still be leaked as no shadow variables
+ * are found for dummy structures already created:
+ *
+ * [ 568.287070] cleanup_thread: jiffies = 100041800
+ * [ 568.287492] livepatch_shadow_fix1: livepatch_fix1_dummy_free: dummy @ ffff880113ee8738 leaked!
+ * [ 568.288062] livepatch_shadow_fix1: livepatch_fix1_dummy_free: dummy @ ffff880113ee9598 leaked!
+ * [ 568.288644] livepatch_shadow_fix1: livepatch_fix1_dummy_free: dummy @ ffff880113ee8178 leaked!
+ * [ 568.289178] livepatch_shadow_fix1: livepatch_fix1_dummy_free: dummy @ ffff880113ee8b88 leaked!
+ * [ 568.289724] livepatch_shadow_fix1: livepatch_fix1_dummy_free: dummy @ ffff880113ee9b58 leaked!
+ * [ 568.290258] livepatch_shadow_fix1: livepatch_fix1_dummy_free: dummy @ ffff880113ee82e8 leaked!
+ * [ 568.415046] livepatch_shadow_fix1: livepatch_fix1_dummy_alloc: dummy @ ffff880112af8a18, expires @ 100044760
+ * [ 569.439050] livepatch_shadow_fix1: livepatch_fix1_dummy_alloc: dummy @ ffff880112af85c8, expires @ 100044b60
+ * [ 570.463049] livepatch_shadow_fix1: livepatch_fix1_dummy_alloc: dummy @ ffff880112af9148, expires @ 100044f60
+ * [ 571.487022] livepatch_shadow_fix1: livepatch_fix1_dummy_alloc: dummy @ ffff880112af8458, expires @ 100045360
+ * [ 572.511049] livepatch_shadow_fix1: livepatch_fix1_dummy_alloc: dummy @ ffff880112af99e8, expires @ 100045760
+ * [ 573.535036] livepatch_shadow_fix1: livepatch_fix1_dummy_alloc: dummy @ ffff88011650e008, expires @ 100045b60
+ *
+ * T2 cleanup thread will eventually begin to reap dummy structures that
+ * do have an associated shadow variable. Over time, this memory leak
+ * will be closed completely as all dummy structures will have a
+ * corresponding shadow variable tracking the extra allocated memory:
+ *
+ * [ 580.575028] cleanup_thread: jiffies = 100044800
+ * [ 580.575675] livepatch_shadow_fix1: livepatch_fix1_dummy_free: dummy @ ffff880112af8a18, prevented leak @ ffff880112b7c568
+ * [ 580.576607] livepatch_shadow_fix1: livepatch_fix1_dummy_free: dummy @ ffff880112af9878, prevented leak @ ffff880112b7d990
+ * [ 580.577545] livepatch_shadow_fix1: livepatch_fix1_dummy_free: dummy @ ffff880112af8fd8, prevented leak @ ffff880112b7c410
+ * [ 580.578483] livepatch_shadow_fix1: livepatch_fix1_dummy_free: dummy @ ffff880112af9708, prevented leak @ ffff880112b7dae8
+ * [ 580.579327] livepatch_shadow_fix1: livepatch_fix1_dummy_free: dummy @ ffff880112af88a8, prevented leak @ ffff880112b7c2b8
+ * [ 580.580324] livepatch_shadow_fix1: livepatch_fix1_dummy_free: dummy @ ffff8801151bf148 leaked!
+ * ...
+ * [ 586.719022] cleanup_thread: jiffies = 100046000
+ * [ 586.719648] livepatch_shadow_fix1: livepatch_fix1_dummy_free: dummy @ ffff88011650e738, prevented leak @ ffff880112b7c970
+ * [ 586.720948] livepatch_shadow_fix1: livepatch_fix1_dummy_free: dummy @ ffff88011650e008, prevented leak @ ffff880112b7d588
+ * [ 586.722215] livepatch_shadow_fix1: livepatch_fix1_dummy_free: dummy @ ffff880112af99e8, prevented leak @ ffff880112b7c818
+ * [ 586.723496] livepatch_shadow_fix1: livepatch_fix1_dummy_free: dummy @ ffff880112af8458, prevented leak @ ffff880112b7d6e0
+ * [ 586.724824] livepatch_shadow_fix1: livepatch_fix1_dummy_free: dummy @ ffff880112af9148, prevented leak @ ffff880112b7c6c0
+ * [ 586.726146] livepatch_shadow_fix1: livepatch_fix1_dummy_free: dummy @ ffff880112af85c8, prevented leak @ ffff880112b7d838
+ *
+ *
+ * Extend functionality
+ * --------------------
+ *
+ * Shadow variables can also be attached to in-flight dummy structures.
+ * In the second livepatch, use a shadow variable counter to keep track
+ * of the number of times a given dummy structure is inspected for
+ * expiration.
+ *
+ * Load the livepatch fix2 (on top of fix1):
+ * $ insmod samples/livepatch/livepatch-shadow-fix2.ko
+ *
+ * [ 592.303611] livepatch: enabling patch 'livepatch_shadow_fix2'
+ * [ 592.307458] livepatch: 'livepatch_shadow_fix2': patching...
+ *
+ * T2 cleanup thread calls a patched dummy_check(), which keeps a shadow
+ * variable counter of the number times it inspects a given dummy
+ * structure. The final count is reported by a patched dummy_free().
+ *
+ * Note: initially the final count will be 1, as soon-to-expire dummies
+ * will only have had a shadow variable counter for a single pass
+ * through the alloc - check - cleanup cycle. Overtime, newer dummies
+ * will have increased count values:
+ *
+ * [ 592.863035] cleanup_thread: jiffies = 100047800
+ * [ 592.863355] livepatch_shadow_fix2: livepatch_fix2_dummy_free: dummy @ ffff880129d89b58, prevented leak @ ffff880112b7c2b8
+ * [ 592.864100] livepatch_shadow_fix2: livepatch_fix2_dummy_free: dummy @ ffff880129d89b58, check counter = 1
+ * ...
+ * [ 599.007047] cleanup_thread: jiffies = 100049000
+ * [ 599.007368] livepatch_shadow_fix2: livepatch_fix2_dummy_free: dummy @ ffff880112920cf8, prevented leak @ ffff880112b7d838
+ * [ 599.008167] livepatch_shadow_fix2: livepatch_fix2_dummy_free: dummy @ ffff880112920cf8, check counter = 2
+ * ...
+ * [ 605.151049] cleanup_thread: jiffies = 10004a800
+ * [ 605.151464] livepatch_shadow_fix2: livepatch_fix2_dummy_free: dummy @ ffff880113ee9b58, prevented leak @ ffff88011643c818
+ * [ 605.152146] livepatch_shadow_fix2: livepatch_fix2_dummy_free: dummy @ ffff880113ee9b58, check counter = 2
+ * [ 605.152783] livepatch_shadow_fix2: livepatch_fix2_dummy_free: dummy @ ffff880112920fd8, prevented leak @ ffff880112b7c970
+ * [ 605.153490] livepatch_shadow_fix2: livepatch_fix2_dummy_free: dummy @ ffff880112920fd8, check counter = 3
+ * ...
+ *
+ *
+ * Cleanup
+ * -------
+ *
+ * $ echo 0 > /sys/kernel/livepatch/livepatch_shadow_fix2/enabled
+ * $ echo 0 > /sys/kernel/livepatch/livepatch_shadow_fix1/enabled
+ * $ rmmod livepatch-shadow-fix2
+ * $ rmmod livepatch-shadow-fix1
+ * $ rmmod livepatch-shadow-mod
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/workqueue.h>
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Joe Lawrence <joe.lawrence@redhat.com>");
+MODULE_DESCRIPTION("Buggy module for shadow variable demo");
+
+#define T1_PERIOD 1 /* allocator thread */
+#define T2_PERIOD (3 * T1_PERIOD) /* cleanup thread */
+
+LIST_HEAD(dummy_list);
+DEFINE_MUTEX(dummy_list_mutex);
+
+struct dummy {
+ struct list_head list;
+ unsigned long jiffies_expire;
+};
+
+noinline struct dummy *dummy_alloc(void)
+{
+ struct dummy *d;
+ void *leak;
+
+ d = kzalloc(sizeof(*d), GFP_KERNEL);
+ if (!d)
+ return NULL;
+
+ /* Dummies live long enough to see a few t2 instances */
+ d->jiffies_expire = jiffies + 1000 * 4 * T2_PERIOD;
+
+ /* Oops, forgot to save leak! */
+ leak = kzalloc(sizeof(int), GFP_KERNEL);
+
+ pr_info("%s: dummy @ %p, expires @ %lx\n",
+ __func__, d, d->jiffies_expire);
+
+ return d;
+}
+
+noinline void dummy_free(struct dummy *d)
+{
+ pr_info("%s: dummy @ %p, expired = %lx\n",
+ __func__, d, d->jiffies_expire);
+
+ kfree(d);
+}
+
+noinline bool dummy_check(struct dummy *d, unsigned long jiffies)
+{
+ return time_after(jiffies, d->jiffies_expire);
+}
+
+/*
+ * T1: alloc_thread allocates new dummy structures, allocates additional
+ * memory, aptly named "leak", but doesn't keep permanent record of it.
+ */
+struct workqueue_struct *alloc_wq;
+struct delayed_work alloc_dwork;
+static void alloc_thread(struct work_struct *work)
+{
+ struct dummy *d;
+
+ d = dummy_alloc();
+ if (!d)
+ return;
+
+ mutex_lock(&dummy_list_mutex);
+ list_add(&d->list, &dummy_list);
+ mutex_unlock(&dummy_list_mutex);
+
+ queue_delayed_work(alloc_wq, &alloc_dwork,
+ msecs_to_jiffies(1000 * T1_PERIOD));
+}
+
+/*
+ * T2: cleanup_thread frees dummy structures. Without knownledge of "leak",
+ * it leaks the additional memory that alloc_thread created.
+ */
+struct workqueue_struct *cleanup_wq;
+struct delayed_work cleanup_dwork;
+static void cleanup_thread(struct work_struct *work)
+{
+ struct dummy *d, *tmp;
+ unsigned long j;
+
+ j = jiffies;
+ pr_info("%s: jiffies = %lx\n", __func__, j);
+
+ mutex_lock(&dummy_list_mutex);
+ list_for_each_entry_safe(d, tmp, &dummy_list, list) {
+
+ /* Kick out and free any expired dummies */
+ if (dummy_check(d, j)) {
+ list_del(&d->list);
+ dummy_free(d);
+ }
+ }
+ mutex_unlock(&dummy_list_mutex);
+
+ queue_delayed_work(cleanup_wq, &cleanup_dwork,
+ msecs_to_jiffies(1000 * 2 * T2_PERIOD));
+}
+
+static int livepatch_shadow_mod_init(void)
+{
+ alloc_wq = create_singlethread_workqueue("klp_demo_alloc_wq");
+ if (!alloc_wq)
+ return -1;
+
+ cleanup_wq = create_singlethread_workqueue("klp_demo_cleanup_wq");
+ if (!cleanup_wq)
+ goto exit_free_alloc;
+
+ INIT_DELAYED_WORK(&alloc_dwork, alloc_thread);
+ queue_delayed_work(alloc_wq, &alloc_dwork, 1000 * T1_PERIOD);
+
+ INIT_DELAYED_WORK(&cleanup_dwork, cleanup_thread);
+ queue_delayed_work(cleanup_wq, &cleanup_dwork,
+ msecs_to_jiffies(1000 * T2_PERIOD));
+
+ return 0;
+
+exit_free_alloc:
+ destroy_workqueue(alloc_wq);
+
+ return -1;
+}
+
+static void livepatch_shadow_mod_exit(void)
+{
+ struct dummy *d, *tmp;
+
+ /* Cleanup T1 */
+ if (!cancel_delayed_work(&alloc_dwork))
+ flush_workqueue(alloc_wq);
+ destroy_workqueue(alloc_wq);
+
+ /* Cleanup T2 */
+ if (!cancel_delayed_work(&cleanup_dwork))
+ flush_workqueue(cleanup_wq);
+ destroy_workqueue(cleanup_wq);
+
+ /* Cleanup residual dummies */
+ list_for_each_entry_safe(d, tmp, &dummy_list, list) {
+ list_del(&d->list);
+ dummy_free(d);
+ }
+}
+
+module_init(livepatch_shadow_mod_init);
+module_exit(livepatch_shadow_mod_exit);
--
1.8.3.1
[toc] | [prev] | [standalone]
Back to top | Article view | linux.kernel
csiph-web