Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

arm64: perf: factor out callchain code

We currently bundle the callchain handling code with the PMU code,
despite the fact the two are distinct, and the former can be useful even
in the absence of the latter.

Follow the example of arch/arm and factor the callchain handling into
its own file dependent on CONFIG_PERF_EVENTS rather than
CONFIG_HW_PERF_EVENTS.

Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>

authored by

Mark Rutland and committed by
Will Deacon
52da443e 23e94994

+198 -180
+1 -1
arch/arm64/include/asm/perf_event.h
··· 17 17 #ifndef __ASM_PERF_EVENT_H 18 18 #define __ASM_PERF_EVENT_H 19 19 20 - #ifdef CONFIG_HW_PERF_EVENTS 20 + #ifdef CONFIG_PERF_EVENTS 21 21 struct pt_regs; 22 22 extern unsigned long perf_instruction_pointer(struct pt_regs *regs); 23 23 extern unsigned long perf_misc_flags(struct pt_regs *regs);
+1 -1
arch/arm64/kernel/Makefile
··· 25 25 arm64-obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o entry-ftrace.o 26 26 arm64-obj-$(CONFIG_MODULES) += arm64ksyms.o module.o 27 27 arm64-obj-$(CONFIG_SMP) += smp.o smp_spin_table.o topology.o 28 - arm64-obj-$(CONFIG_PERF_EVENTS) += perf_regs.o 28 + arm64-obj-$(CONFIG_PERF_EVENTS) += perf_regs.o perf_callchain.o 29 29 arm64-obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o 30 30 arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o 31 31 arm64-obj-$(CONFIG_CPU_PM) += sleep.o suspend.o
+196
arch/arm64/kernel/perf_callchain.c
··· 1 + /* 2 + * arm64 callchain support 3 + * 4 + * Copyright (C) 2015 ARM Limited 5 + * 6 + * This program is free software; you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License version 2 as 8 + * published by the Free Software Foundation. 9 + * 10 + * This program is distributed in the hope that it will be useful, 11 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 + * GNU General Public License for more details. 14 + * 15 + * You should have received a copy of the GNU General Public License 16 + * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 + */ 18 + #include <linux/perf_event.h> 19 + #include <linux/uaccess.h> 20 + 21 + #include <asm/stacktrace.h> 22 + 23 + struct frame_tail { 24 + struct frame_tail __user *fp; 25 + unsigned long lr; 26 + } __attribute__((packed)); 27 + 28 + /* 29 + * Get the return address for a single stackframe and return a pointer to the 30 + * next frame tail. 31 + */ 32 + static struct frame_tail __user * 33 + user_backtrace(struct frame_tail __user *tail, 34 + struct perf_callchain_entry *entry) 35 + { 36 + struct frame_tail buftail; 37 + unsigned long err; 38 + 39 + /* Also check accessibility of one struct frame_tail beyond */ 40 + if (!access_ok(VERIFY_READ, tail, sizeof(buftail))) 41 + return NULL; 42 + 43 + pagefault_disable(); 44 + err = __copy_from_user_inatomic(&buftail, tail, sizeof(buftail)); 45 + pagefault_enable(); 46 + 47 + if (err) 48 + return NULL; 49 + 50 + perf_callchain_store(entry, buftail.lr); 51 + 52 + /* 53 + * Frame pointers should strictly progress back up the stack 54 + * (towards higher addresses). 55 + */ 56 + if (tail >= buftail.fp) 57 + return NULL; 58 + 59 + return buftail.fp; 60 + } 61 + 62 + #ifdef CONFIG_COMPAT 63 + /* 64 + * The registers we're interested in are at the end of the variable 65 + * length saved register structure. The fp points at the end of this 66 + * structure so the address of this struct is: 67 + * (struct compat_frame_tail *)(xxx->fp)-1 68 + * 69 + * This code has been adapted from the ARM OProfile support. 70 + */ 71 + struct compat_frame_tail { 72 + compat_uptr_t fp; /* a (struct compat_frame_tail *) in compat mode */ 73 + u32 sp; 74 + u32 lr; 75 + } __attribute__((packed)); 76 + 77 + static struct compat_frame_tail __user * 78 + compat_user_backtrace(struct compat_frame_tail __user *tail, 79 + struct perf_callchain_entry *entry) 80 + { 81 + struct compat_frame_tail buftail; 82 + unsigned long err; 83 + 84 + /* Also check accessibility of one struct frame_tail beyond */ 85 + if (!access_ok(VERIFY_READ, tail, sizeof(buftail))) 86 + return NULL; 87 + 88 + pagefault_disable(); 89 + err = __copy_from_user_inatomic(&buftail, tail, sizeof(buftail)); 90 + pagefault_enable(); 91 + 92 + if (err) 93 + return NULL; 94 + 95 + perf_callchain_store(entry, buftail.lr); 96 + 97 + /* 98 + * Frame pointers should strictly progress back up the stack 99 + * (towards higher addresses). 100 + */ 101 + if (tail + 1 >= (struct compat_frame_tail __user *) 102 + compat_ptr(buftail.fp)) 103 + return NULL; 104 + 105 + return (struct compat_frame_tail __user *)compat_ptr(buftail.fp) - 1; 106 + } 107 + #endif /* CONFIG_COMPAT */ 108 + 109 + void perf_callchain_user(struct perf_callchain_entry *entry, 110 + struct pt_regs *regs) 111 + { 112 + if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) { 113 + /* We don't support guest os callchain now */ 114 + return; 115 + } 116 + 117 + perf_callchain_store(entry, regs->pc); 118 + 119 + if (!compat_user_mode(regs)) { 120 + /* AARCH64 mode */ 121 + struct frame_tail __user *tail; 122 + 123 + tail = (struct frame_tail __user *)regs->regs[29]; 124 + 125 + while (entry->nr < PERF_MAX_STACK_DEPTH && 126 + tail && !((unsigned long)tail & 0xf)) 127 + tail = user_backtrace(tail, entry); 128 + } else { 129 + #ifdef CONFIG_COMPAT 130 + /* AARCH32 compat mode */ 131 + struct compat_frame_tail __user *tail; 132 + 133 + tail = (struct compat_frame_tail __user *)regs->compat_fp - 1; 134 + 135 + while ((entry->nr < PERF_MAX_STACK_DEPTH) && 136 + tail && !((unsigned long)tail & 0x3)) 137 + tail = compat_user_backtrace(tail, entry); 138 + #endif 139 + } 140 + } 141 + 142 + /* 143 + * Gets called by walk_stackframe() for every stackframe. This will be called 144 + * whist unwinding the stackframe and is like a subroutine return so we use 145 + * the PC. 146 + */ 147 + static int callchain_trace(struct stackframe *frame, void *data) 148 + { 149 + struct perf_callchain_entry *entry = data; 150 + perf_callchain_store(entry, frame->pc); 151 + return 0; 152 + } 153 + 154 + void perf_callchain_kernel(struct perf_callchain_entry *entry, 155 + struct pt_regs *regs) 156 + { 157 + struct stackframe frame; 158 + 159 + if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) { 160 + /* We don't support guest os callchain now */ 161 + return; 162 + } 163 + 164 + frame.fp = regs->regs[29]; 165 + frame.sp = regs->sp; 166 + frame.pc = regs->pc; 167 + 168 + walk_stackframe(&frame, callchain_trace, entry); 169 + } 170 + 171 + unsigned long perf_instruction_pointer(struct pt_regs *regs) 172 + { 173 + if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) 174 + return perf_guest_cbs->get_guest_ip(); 175 + 176 + return instruction_pointer(regs); 177 + } 178 + 179 + unsigned long perf_misc_flags(struct pt_regs *regs) 180 + { 181 + int misc = 0; 182 + 183 + if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) { 184 + if (perf_guest_cbs->is_user_mode()) 185 + misc |= PERF_RECORD_MISC_GUEST_USER; 186 + else 187 + misc |= PERF_RECORD_MISC_GUEST_KERNEL; 188 + } else { 189 + if (user_mode(regs)) 190 + misc |= PERF_RECORD_MISC_USER; 191 + else 192 + misc |= PERF_RECORD_MISC_KERNEL; 193 + } 194 + 195 + return misc; 196 + }
-178
arch/arm64/kernel/perf_event.c
··· 36 36 #include <asm/irq.h> 37 37 #include <asm/irq_regs.h> 38 38 #include <asm/pmu.h> 39 - #include <asm/stacktrace.h> 40 39 41 40 /* 42 41 * ARMv8 supports a maximum of 32 events. ··· 1412 1413 } 1413 1414 early_initcall(init_hw_perf_events); 1414 1415 1415 - /* 1416 - * Callchain handling code. 1417 - */ 1418 - struct frame_tail { 1419 - struct frame_tail __user *fp; 1420 - unsigned long lr; 1421 - } __attribute__((packed)); 1422 - 1423 - /* 1424 - * Get the return address for a single stackframe and return a pointer to the 1425 - * next frame tail. 1426 - */ 1427 - static struct frame_tail __user * 1428 - user_backtrace(struct frame_tail __user *tail, 1429 - struct perf_callchain_entry *entry) 1430 - { 1431 - struct frame_tail buftail; 1432 - unsigned long err; 1433 - 1434 - /* Also check accessibility of one struct frame_tail beyond */ 1435 - if (!access_ok(VERIFY_READ, tail, sizeof(buftail))) 1436 - return NULL; 1437 - 1438 - pagefault_disable(); 1439 - err = __copy_from_user_inatomic(&buftail, tail, sizeof(buftail)); 1440 - pagefault_enable(); 1441 - 1442 - if (err) 1443 - return NULL; 1444 - 1445 - perf_callchain_store(entry, buftail.lr); 1446 - 1447 - /* 1448 - * Frame pointers should strictly progress back up the stack 1449 - * (towards higher addresses). 1450 - */ 1451 - if (tail >= buftail.fp) 1452 - return NULL; 1453 - 1454 - return buftail.fp; 1455 - } 1456 - 1457 - #ifdef CONFIG_COMPAT 1458 - /* 1459 - * The registers we're interested in are at the end of the variable 1460 - * length saved register structure. The fp points at the end of this 1461 - * structure so the address of this struct is: 1462 - * (struct compat_frame_tail *)(xxx->fp)-1 1463 - * 1464 - * This code has been adapted from the ARM OProfile support. 1465 - */ 1466 - struct compat_frame_tail { 1467 - compat_uptr_t fp; /* a (struct compat_frame_tail *) in compat mode */ 1468 - u32 sp; 1469 - u32 lr; 1470 - } __attribute__((packed)); 1471 - 1472 - static struct compat_frame_tail __user * 1473 - compat_user_backtrace(struct compat_frame_tail __user *tail, 1474 - struct perf_callchain_entry *entry) 1475 - { 1476 - struct compat_frame_tail buftail; 1477 - unsigned long err; 1478 - 1479 - /* Also check accessibility of one struct frame_tail beyond */ 1480 - if (!access_ok(VERIFY_READ, tail, sizeof(buftail))) 1481 - return NULL; 1482 - 1483 - pagefault_disable(); 1484 - err = __copy_from_user_inatomic(&buftail, tail, sizeof(buftail)); 1485 - pagefault_enable(); 1486 - 1487 - if (err) 1488 - return NULL; 1489 - 1490 - perf_callchain_store(entry, buftail.lr); 1491 - 1492 - /* 1493 - * Frame pointers should strictly progress back up the stack 1494 - * (towards higher addresses). 1495 - */ 1496 - if (tail + 1 >= (struct compat_frame_tail __user *) 1497 - compat_ptr(buftail.fp)) 1498 - return NULL; 1499 - 1500 - return (struct compat_frame_tail __user *)compat_ptr(buftail.fp) - 1; 1501 - } 1502 - #endif /* CONFIG_COMPAT */ 1503 - 1504 - void perf_callchain_user(struct perf_callchain_entry *entry, 1505 - struct pt_regs *regs) 1506 - { 1507 - if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) { 1508 - /* We don't support guest os callchain now */ 1509 - return; 1510 - } 1511 - 1512 - perf_callchain_store(entry, regs->pc); 1513 - 1514 - if (!compat_user_mode(regs)) { 1515 - /* AARCH64 mode */ 1516 - struct frame_tail __user *tail; 1517 - 1518 - tail = (struct frame_tail __user *)regs->regs[29]; 1519 - 1520 - while (entry->nr < PERF_MAX_STACK_DEPTH && 1521 - tail && !((unsigned long)tail & 0xf)) 1522 - tail = user_backtrace(tail, entry); 1523 - } else { 1524 - #ifdef CONFIG_COMPAT 1525 - /* AARCH32 compat mode */ 1526 - struct compat_frame_tail __user *tail; 1527 - 1528 - tail = (struct compat_frame_tail __user *)regs->compat_fp - 1; 1529 - 1530 - while ((entry->nr < PERF_MAX_STACK_DEPTH) && 1531 - tail && !((unsigned long)tail & 0x3)) 1532 - tail = compat_user_backtrace(tail, entry); 1533 - #endif 1534 - } 1535 - } 1536 - 1537 - /* 1538 - * Gets called by walk_stackframe() for every stackframe. This will be called 1539 - * whist unwinding the stackframe and is like a subroutine return so we use 1540 - * the PC. 1541 - */ 1542 - static int callchain_trace(struct stackframe *frame, void *data) 1543 - { 1544 - struct perf_callchain_entry *entry = data; 1545 - perf_callchain_store(entry, frame->pc); 1546 - return 0; 1547 - } 1548 - 1549 - void perf_callchain_kernel(struct perf_callchain_entry *entry, 1550 - struct pt_regs *regs) 1551 - { 1552 - struct stackframe frame; 1553 - 1554 - if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) { 1555 - /* We don't support guest os callchain now */ 1556 - return; 1557 - } 1558 - 1559 - frame.fp = regs->regs[29]; 1560 - frame.sp = regs->sp; 1561 - frame.pc = regs->pc; 1562 - 1563 - walk_stackframe(&frame, callchain_trace, entry); 1564 - } 1565 - 1566 - unsigned long perf_instruction_pointer(struct pt_regs *regs) 1567 - { 1568 - if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) 1569 - return perf_guest_cbs->get_guest_ip(); 1570 - 1571 - return instruction_pointer(regs); 1572 - } 1573 - 1574 - unsigned long perf_misc_flags(struct pt_regs *regs) 1575 - { 1576 - int misc = 0; 1577 - 1578 - if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) { 1579 - if (perf_guest_cbs->is_user_mode()) 1580 - misc |= PERF_RECORD_MISC_GUEST_USER; 1581 - else 1582 - misc |= PERF_RECORD_MISC_GUEST_KERNEL; 1583 - } else { 1584 - if (user_mode(regs)) 1585 - misc |= PERF_RECORD_MISC_USER; 1586 - else 1587 - misc |= PERF_RECORD_MISC_KERNEL; 1588 - } 1589 - 1590 - return misc; 1591 - }