misc: tegra-profiler: fix sleep inside atomic
Igor Nabirushkin [Thu, 1 Sep 2016 06:49:43 +0000 (10:49 +0400)]
Unwinding: fix possible sleep in atomic context.

Bug 200214930
Bug 200224828

Change-Id: Iaca298a689d14a5eb46fea01c24d72c56fd44a79
Signed-off-by: Igor Nabirushkin <inabirushkin@nvidia.com>
Reviewed-on: http://git-master/r/1216911
(cherry picked from commit 2decd51ef944f4804099bee4a1ebd2aca4e7ac47)
Reviewed-on: http://git-master/r/1251881
(cherry picked from commit 71d4937be67ec177c23131a3252752a939c778ca)

drivers/misc/tegra-profiler/backtrace.h
drivers/misc/tegra-profiler/comm.h
drivers/misc/tegra-profiler/disassembler.c
drivers/misc/tegra-profiler/dwarf_unwind.c
drivers/misc/tegra-profiler/eh_unwind.c

index a363db6..85562ad 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * drivers/misc/tegra-profiler/backtrace.h
  *
- * Copyright (c) 2015, NVIDIA CORPORATION.  All rights reserved.
+ * Copyright (c) 2013-2016, NVIDIA CORPORATION.  All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms and conditions of the GNU General Public License,
@@ -19,6 +19,9 @@
 
 #include <linux/mm.h>
 #include <linux/bitops.h>
+#include <linux/uaccess.h>
+
+#include <linux/tegra_profiler.h>
 
 #define QUADD_MAX_STACK_DEPTH          64
 
@@ -110,4 +113,22 @@ validate_stack_addr(unsigned long addr,
        return is_vma_addr(addr, vma, nbytes);
 }
 
+static inline long
+read_user_data(void *dst, const void __user *src, unsigned long n)
+{
+       long err;
+
+       pagefault_disable();
+       err = __copy_from_user_inatomic(dst, src, n);
+       pagefault_enable();
+
+       if (unlikely(err)) {
+               pr_debug("%s: failed for address: %p\n",
+                        __func__, src);
+               err = -QUADD_URC_EACCESS;
+       }
+
+       return err;
+}
+
 #endif  /* __QUADD_BACKTRACE_H */
index f2a6048..db9def3 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * drivers/misc/tegra-profiler/comm.h
  *
- * Copyright (c) 2013-2015, NVIDIA CORPORATION.  All rights reserved.
+ * Copyright (c) 2013-2016, NVIDIA CORPORATION.  All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms and conditions of the GNU General Public License,
 struct quadd_record_data;
 struct quadd_comm_cap;
 struct quadd_module_state;
-struct miscdevice;
 struct quadd_parameters;
 struct quadd_sections;
-struct quadd_unwind_ctx;
 struct quadd_ring_buffer;
+struct quadd_pmu_setup_for_cpu;
+struct quadd_comm_cap_for_cpu;
 
 struct quadd_iovec {
        void *base;
index 6fb762b..1cf52fc 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * drivers/misc/tegra-profiler/disassembler.c
  *
- * Copyright (c) 2015, NVIDIA CORPORATION.  All rights reserved.
+ * Copyright (c) 2015-2016, NVIDIA CORPORATION.  All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms and conditions of the GNU General Public License,
 #include <linux/tegra_profiler.h>
 
 #include "tegra.h"
+#include "backtrace.h"
 #include "disassembler.h"
 
-/* FIXME: grossly duplicated */
-
-#define read_user_data(addr, retval)                           \
-({                                                             \
-       long ret;                                               \
-                                                               \
-       pagefault_disable();                                    \
-       ret = __get_user(retval, addr);                         \
-       pagefault_enable();                                     \
-                                                               \
-       if (ret) {                                              \
-               pr_debug("%s: failed for address: %p\n",        \
-                        __func__, addr);                       \
-               ret = -QUADD_URC_EACCESS;                       \
-       }                                                       \
-                                                               \
-       ret;                                                    \
-})
-
 static long
 quadd_arm_imm(u32 val)
 {
@@ -74,13 +56,16 @@ quadd_print_reg(char *buf, size_t size, int reg)
 static long
 quadd_disassemble_arm(struct quadd_disasm_data *qd)
 {
+       long err;
        unsigned long addr;
 
        for (addr = qd->min; addr < qd->max; addr += 4) {
                u32 val;
 
-               if (read_user_data((const u32 __user *) addr, val) < 0)
-                       return -QUADD_URC_EACCESS;
+               err = read_user_data(&val, (const void __user *)addr,
+                                    sizeof(u32));
+               if (err < 0)
+                       return err;
 
                if (((val & 0x0def0ff0) == 0x01a00000) &&
                    !quadd_stack_found(qd)) {
@@ -160,13 +145,16 @@ quadd_disassemble_arm(struct quadd_disasm_data *qd)
 static long
 quadd_disassemble_thumb(struct quadd_disasm_data *qd)
 {
+       long err;
        unsigned long addr;
 
        for (addr = qd->min; addr < qd->max; addr += 2) {
                u16 val1;
 
-               if (read_user_data((const u16 __user *) addr, val1) < 0)
-                       return -QUADD_URC_EACCESS;
+               err = read_user_data(&val1, (const void __user *)addr,
+                                    sizeof(u16));
+               if (err < 0)
+                       return err;
 
                if ((val1 & 0xf800) == 0xa800 && !quadd_stack_found(qd)) {
                        /* add x, sp, i */
@@ -208,9 +196,11 @@ quadd_disassemble_thumb(struct quadd_disasm_data *qd)
                        u16 val2;
                        u32 val;
 
-                       if (read_user_data((const u16 __user *)
-                                          (addr + 2), val2) < 0)
-                               return -QUADD_URC_EACCESS;
+                       err = read_user_data(&val2, (const void __user *)
+                                            (addr + 2), sizeof(u16));
+                       if (err < 0)
+                               return err;
+
                        val = (val1 << 16) | val2;
                        addr += 2;
 
index 191368d..4ec5238 100644 (file)
@@ -180,23 +180,6 @@ struct eh_sec_data {
        unsigned char *data;
 };
 
-#define read_user_data(addr, retval)                           \
-({                                                             \
-       long ret;                                               \
-                                                               \
-       pagefault_disable();                                    \
-       ret = __get_user(retval, addr);                         \
-       pagefault_enable();                                     \
-                                                               \
-       if (ret) {                                              \
-               pr_debug("%s: failed for address: %p\n",        \
-                        __func__, addr);                       \
-               ret = -QUADD_URC_EACCESS;                       \
-       }                                                       \
-                                                               \
-       ret;                                                    \
-})
-
 static struct quadd_dwarf_context ctx;
 
 static inline int regnum_sp(int mode)
@@ -1908,10 +1891,11 @@ unwind_frame(struct ex_region_info *ri,
                                return -QUADD_URC_SP_INCORRECT;
 
                        if (mode == DW_MODE_ARM32)
-                               err = read_user_data((u32 __user *)addr, val);
+                               err = read_user_data(&val, (void __user *)addr,
+                                                    sizeof(u32));
                        else
-                               err = read_user_data((unsigned long __user *)
-                                                    addr, val);
+                               err = read_user_data(&val, (void __user *)addr,
+                                                    sizeof(unsigned long));
 
                        if (err < 0)
                                return err;
index ebf4268..1fa523d 100644 (file)
@@ -117,23 +117,6 @@ validate_mmap_addr(struct quadd_mmap_area *mmap,
        return 1;
 }
 
-#define read_user_data(addr, retval)                           \
-({                                                             \
-       long ret;                                               \
-                                                               \
-       pagefault_disable();                                    \
-       ret = __get_user(retval, addr);                         \
-       pagefault_enable();                                     \
-                                                               \
-       if (ret) {                                              \
-               pr_debug("%s: failed for address: %p\n",        \
-                        __func__, addr);                       \
-               ret = -QUADD_URC_EACCESS;                       \
-       }                                                       \
-                                                               \
-       ret;                                                    \
-})
-
 static inline long
 read_mmap_data(struct quadd_mmap_area *mmap, const u32 *addr, u32 *retval)
 {
@@ -181,10 +164,12 @@ mmap_addr_to_ex_addr(unsigned long addr,
 static inline u32 __maybe_unused
 prel31_to_addr(const u32 *ptr)
 {
+       long err;
        u32 value;
        s32 offset;
 
-       if (read_user_data(ptr, value))
+       err = read_user_data(&value, ptr, sizeof(*ptr));
+       if (err < 0)
                return 0;
 
        /* sign-extend to 32 bits */
@@ -811,7 +796,8 @@ unwind_exec_insn(struct quadd_mmap_area *mmap,
                load_sp = mask & (1 << (13 - 4));
                while (mask) {
                        if (mask & 1) {
-                               err = read_user_data(vsp++, ctrl->vrs[reg]);
+                               err = read_user_data(&ctrl->vrs[reg], vsp++,
+                                                    sizeof(u32));
                                if (err < 0)
                                        return err;
 
@@ -836,7 +822,8 @@ unwind_exec_insn(struct quadd_mmap_area *mmap,
 
                /* pop R4-R[4+bbb] */
                for (reg = 4; reg <= 4 + (insn & 7); reg++) {
-                       err = read_user_data(vsp++, ctrl->vrs[reg]);
+                       err = read_user_data(&ctrl->vrs[reg], vsp++,
+                                            sizeof(u32));
                        if (err < 0)
                                return err;
 
@@ -845,7 +832,8 @@ unwind_exec_insn(struct quadd_mmap_area *mmap,
                }
 
                if (insn & 0x08) {
-                       err = read_user_data(vsp++, ctrl->vrs[14]);
+                       err = read_user_data(&ctrl->vrs[14], vsp++,
+                                            sizeof(u32));
                        if (err < 0)
                                return err;
 
@@ -879,7 +867,8 @@ unwind_exec_insn(struct quadd_mmap_area *mmap,
                /* pop R0-R3 according to mask */
                while (mask) {
                        if (mask & 1) {
-                               err = read_user_data(vsp++, ctrl->vrs[reg]);
+                               err = read_user_data(&ctrl->vrs[reg], vsp++,
+                                                    sizeof(u32));
                                if (err < 0)
                                        return err;