From: David Smith <dsmith@redhat.com>
Date:   Fri Dec 9 11:41:35 2016 -0600
Subject: Fix BZ1386120 by updating systemtap to handle the 4.9 kernel
Git-commit: 00960517fb4a4d8388854b470da30e0c886b0e89 
References: bsc#1031409, bsc#1125906
Signed-off-by: Tony Jones <tonyj@suse.de>

    Fix BZ1386120 by updating systemtap to handle the 4.9 kernel.
    
    * runtime/linux/access_process_vm.h (__access_process_vm_): Use
      STAPCONF_GET_USER_PAGES_REMOTE_FLAGS to handle get_user_pages_remote()
      kernel function API change.
    * runtime/transport/procfs.c (_stp_proc_read): Use _stp_get_rchan_subbuf()
      macro to handle kernel relayfs rchan->buf changes.
    * runtime/transport/relay_v2.c (__stp_relay_wakeup_timer): Ditto.
      (_stp_data_write_reserve): Ditto.
    * runtime/transport/symbols.c (_stp_module_panic_notifier): Ditto.
    * buildrun.cxx (compile_pass): Add autoconf-style tests.
    * runtime/linux/autoconf-get_user_pages_remote-flags.c: New file.
    * runtime/linux/autoconf-relay_buf-per_cpu_ptr.c: New file.
    * runtime/transport/relay_compat.h: New file.

diff --git a/buildrun.cxx b/buildrun.cxx
index 7c68ba03e..f5019b925 100644
--- a/buildrun.cxx
+++ b/buildrun.cxx
@@ -393,6 +393,7 @@ compile_pass (systemtap_session& s)
   output_autoconf(s, o, "autoconf-task_work-struct.c", "STAPCONF_TASK_WORK_STRUCT", NULL);
   output_autoconf(s, o, "autoconf-vm-area-pte.c", "STAPCONF_VM_AREA_PTE", NULL);
   output_autoconf(s, o, "autoconf-relay-umode_t.c", "STAPCONF_RELAY_UMODE_T", NULL);
+  output_autoconf(s, o, "autoconf-relay_buf-per_cpu_ptr.c", "STAPCONF_RELAY_BUF_PER_CPU_PTR", NULL);
   output_autoconf(s, o, "autoconf-fs_supers-hlist.c", "STAPCONF_FS_SUPERS_HLIST", NULL);
   output_autoconf(s, o, "autoconf-compat_sigaction.c", "STAPCONF_COMPAT_SIGACTION", NULL);
   output_autoconf(s, o, "autoconf-netfilter.c", "STAPCONF_NETFILTER_V313", NULL);
@@ -447,6 +448,8 @@ compile_pass (systemtap_session& s)
   output_autoconf(s, o, "autoconf-mod_kallsyms.c",
 		  "STAPCONF_MOD_KALLSYMS", NULL);
   output_exportconf(s, o, "get_user_pages_remote", "STAPCONF_GET_USER_PAGES_REMOTE");
+  output_autoconf(s, o, "autoconf-get_user_pages_remote-flags.c",
+		  "STAPCONF_GET_USER_PAGES_REMOTE_FLAGS", NULL);
 
   o << module_cflags << " += -include $(STAPCONF_HEADER)" << endl;
 
diff --git a/runtime/linux/access_process_vm.h b/runtime/linux/access_process_vm.h
index 214d4e254..d9400d723 100644
--- a/runtime/linux/access_process_vm.h
+++ b/runtime/linux/access_process_vm.h
@@ -33,7 +33,14 @@ __access_process_vm_ (struct task_struct *tsk, unsigned long addr, void *buf,
       void *maddr;
 
 #ifdef STAPCONF_GET_USER_PAGES_REMOTE
+#ifdef STAPCONF_GET_USER_PAGES_REMOTE_FLAGS
+      unsigned int flags = FOLL_FORCE;
+      if (write)
+	  flags |= FOLL_WRITE;
+      ret = get_user_pages_remote (tsk, mm, addr, 1, flags, &page, &vma);
+#else  /* !STAPCONF_GET_USER_PAGES_REMOTE_FLAGS */
       ret = get_user_pages_remote (tsk, mm, addr, 1, write, 1, &page, &vma);
+#endif /* !STAPCONF_GET_USER_PAGES_REMOTE_FLAGS */
 #else
       ret = get_user_pages (tsk, mm, addr, 1, write, 1, &page, &vma);
 #endif
diff --git a/runtime/linux/autoconf-get_user_pages_remote-flags.c b/runtime/linux/autoconf-get_user_pages_remote-flags.c
new file mode 100644
index 000000000..1313e8957
--- /dev/null
+++ b/runtime/linux/autoconf-get_user_pages_remote-flags.c
@@ -0,0 +1,42 @@
+#ifdef STAPCONF_GET_USER_PAGES_REMOTE
+#include <linux/mm.h>
+
+//
+// The following kernel commit changed the get_user_pages_remote()
+// function signature:
+//
+// commit 9beae1ea89305a9667ceaab6d0bf46a045ad71e7
+// Author: Lorenzo Stoakes <lstoakes@gmail.com>
+// Date:   Thu Oct 13 01:20:17 2016 +0100
+//
+//    mm: replace get_user_pages_remote() write/force parameters with gup_flags
+//
+//    This removes the 'write' and 'force' from get_user_pages_remote() and
+//    replaces them with 'gup_flags' to make the use of FOLL_FORCE explicit in
+//    callers as use of this flag can result in surprising behaviour (and
+//    hence bugs) within the mm subsystem.
+//
+// This changed the function signature from:
+//
+// long get_user_pages_remote(struct task_struct *tsk, struct mm_struct *mm,
+//                            unsigned long start, unsigned long nr_pages,
+//                            int write, int force, struct page **pages,
+//                            struct vm_area_struct **vmas);
+//
+// to:
+//
+// long get_user_pages_remote(struct task_struct *tsk, struct mm_struct *mm,
+//                            unsigned long start, unsigned long nr_pages,
+//                            unsigned int gup_flags, struct page **pages,
+//                            struct vm_area_struct **vmas);
+//
+
+long gupr_wrapper(struct task_struct *tsk, struct mm_struct *mm,
+		  unsigned long start, unsigned long nr_pages,
+		  unsigned int gup_flags, struct page **pages,
+		  struct vm_area_struct **vmas)
+{
+    return get_user_pages_remote(tsk, mm, start, nr_pages, gup_flags,
+				 pages, vmas);
+}
+#endif
diff --git a/runtime/linux/autoconf-relay_buf-per_cpu_ptr.c b/runtime/linux/autoconf-relay_buf-per_cpu_ptr.c
new file mode 100644
index 000000000..d2af8d32d
--- /dev/null
+++ b/runtime/linux/autoconf-relay_buf-per_cpu_ptr.c
@@ -0,0 +1,27 @@
+//
+// The following kernel commit:
+//
+// commit 017c59c042d01fc84cae7a8ea475861e702c77ab
+// Author: Akash Goel <akash.goel@intel.com>
+// Date:   Fri Sep 2 21:47:38 2016 +0200
+//
+//     relay: Use per CPU constructs for the relay channel buffer pointers
+//
+//     relay essentially needs to maintain a per CPU array of channel buffer
+//     pointers but it manually creates that array.  Instead its better to use
+//     the per CPU constructs, provided by the kernel, to allocate & access the
+//     array of pointer to channel buffers.
+//
+// changed the way the 'rchan->buf' field works. It just to be a
+// regular array, and is now a per_cpu_ptr-style array.
+//
+// If we can call per_cpu_ptr() on rchan->buf, we'll assume that we've
+// got the new style relay channel buffer pointers.
+
+#include <linux/relay.h>
+#include <linux/percpu.h>
+
+struct rchan_buf *relay_buf_test(struct rchan *chan, unsigned int cpu)
+{
+    return *per_cpu_ptr(chan->buf, cpu);
+}
diff --git a/runtime/transport/procfs.c b/runtime/transport/procfs.c
index 05d824ec8..857f713e1 100644
--- a/runtime/transport/procfs.c
+++ b/runtime/transport/procfs.c
@@ -10,6 +10,7 @@
  */
 
 #include "../procfs.c"		   // for _stp_mkdir_proc_module()
+#include "relay_compat.h"
 
 #define STP_DEFAULT_BUFFERS 256
 
@@ -19,6 +20,7 @@ static ssize_t _stp_proc_read(struct file *file, char __user *buf, size_t count,
 {
 	int num;
 	struct _stp_buf_info out;
+	struct rchan_buf *sub_buf;
 
 	int cpu = *(int *)(PDE(file->f_dentry->d_inode)->data);
 
@@ -26,8 +28,9 @@ static ssize_t _stp_proc_read(struct file *file, char __user *buf, size_t count,
 		return -EINVAL;
 
 	out.cpu = cpu;
-	out.produced = atomic_read(&_stp_relay_data.rchan->buf[cpu]->subbufs_produced);
-	out.consumed = atomic_read(&_stp_relay_data.rchan->buf[cpu]->subbufs_consumed);
+	sub_buf = _stp_get_rchan_subbuf(_stp_relay_data.rchan->buf, cpu);
+	out.produced = atomic_read(&sub_buf->subbufs_produced);
+	out.consumed = atomic_read(&sub_buf->subbufs_consumed);
 	out.flushing = _stp_relay_data.flushing;
 
 	num = sizeof(out);
diff --git a/runtime/transport/relay_compat.h b/runtime/transport/relay_compat.h
new file mode 100644
index 000000000..02b822861
--- /dev/null
+++ b/runtime/transport/relay_compat.h
@@ -0,0 +1,25 @@
+/* -*- linux-c -*- */
+#ifndef _TRANSPORT_RELAY_COMPAT_H_
+#define _TRANSPORT_RELAY_COMPAT_H_
+
+#if defined (CONFIG_RELAYFS_FS) || defined (CONFIG_RELAYFS_FS_MODULE)
+#  include <linux/relayfs_fs.h>
+#elif defined (CONFIG_RELAY)
+#  include <linux/relay.h>
+#endif
+
+#ifdef STAPCONF_RELAY_BUF_PER_CPU_PTR
+
+#include <linux/percpu.h>
+
+#define _stp_get_rchan_subbuf(rchan_buf, cpu) \
+	(*per_cpu_ptr((rchan_buf), (cpu)))
+
+#else
+
+#define _stp_get_rchan_subbuf(rchan_buf, cpu) \
+	((rchan_buf)[(cpu)])
+
+#endif
+
+#endif /* _TRANSPORT_RELAY_COMPAT_H_ */
diff --git a/runtime/transport/relay_v2.c b/runtime/transport/relay_v2.c
index 18fca8bd3..ccf4dc80d 100644
--- a/runtime/transport/relay_v2.c
+++ b/runtime/transport/relay_v2.c
@@ -32,6 +32,7 @@
 #include <linux/relay.h>
 #include <linux/timer.h>
 #include "../uidgid_compatibility.h"
+#include "relay_compat.h"
 
 #ifndef STP_RELAY_TIMER_INTERVAL
 /* Wakeup timer interval in jiffies (default 10 ms) */
@@ -126,12 +127,18 @@ static void __stp_relay_wakeup_timer(unsigned long val)
 #endif
 
 	if (atomic_read(&_stp_relay_data.wakeup)) {
+		struct rchan_buf *buf;
+		
 		atomic_set(&_stp_relay_data.wakeup, 0);
 #ifdef STP_BULKMODE
-		for_each_possible_cpu(i)
-			__stp_relay_wakeup_readers(_stp_relay_data.rchan->buf[i]);
+		for_each_possible_cpu(i) {
+			buf = _stp_get_rchan_subbuf(_stp->relay_data.rchan->buf,
+						    i);
+			__stp_relay_wakeup_readers(buf);
+		}
 #else
-		__stp_relay_wakeup_readers(_stp_relay_data.rchan->buf[0]);
+		buf = _stp_get_rchan_subbuf(_stp_relay_data.rchan->buf, 0);
+		__stp_relay_wakeup_readers(buf);
 #endif
 	}
 
@@ -397,7 +404,8 @@ _stp_data_write_reserve(size_t size_request, void **entry)
 	if (entry == NULL)
 		return -EINVAL;
 
-	buf = _stp_relay_data.rchan->buf[smp_processor_id()];
+	buf = _stp_get_rchan_subbuf(_stp_relay_data.rchan->buf,
+				    smp_processor_id());
 	if (unlikely(buf->offset + size_request > buf->chan->subbuf_size)) {
 		size_request = __stp_relay_switch_subbuf(buf, size_request);
 		if (!size_request)
diff --git a/runtime/transport/symbols.c b/runtime/transport/symbols.c
index 98b023938..692a6f3f7 100644
--- a/runtime/transport/symbols.c
+++ b/runtime/transport/symbols.c
@@ -12,6 +12,7 @@
 #ifndef _STP_SYMBOLS_C_
 #define _STP_SYMBOLS_C_
 #include "../sym.h"
+#include "relay_compat.h"
 
 #ifndef KERN_CONT
 #define KERN_CONT	""
@@ -327,7 +328,7 @@ static int _stp_module_panic_notifier (struct notifier_block *nb, unsigned long
                 int printed;
                 int first_iteration;
 
-                sub_buf = _stp_relay_data.rchan->buf[i];
+                sub_buf = _stp_get_rchan_subbuf(_stp_relay_data.rchan->buf, i);
 
                 /* Set our pointer to the beginning of the channel buffer */
                 subbuf_start = (char *)sub_buf->start;
