1fc59a3fcSJeremy Kerr /* 2fc59a3fcSJeremy Kerr * Virtual Processor Dispatch Trace Log 3fc59a3fcSJeremy Kerr * 4fc59a3fcSJeremy Kerr * (C) Copyright IBM Corporation 2009 5fc59a3fcSJeremy Kerr * 6fc59a3fcSJeremy Kerr * Author: Jeremy Kerr <jk@ozlabs.org> 7fc59a3fcSJeremy Kerr * 8fc59a3fcSJeremy Kerr * This program is free software; you can redistribute it and/or modify 9fc59a3fcSJeremy Kerr * it under the terms of the GNU General Public License as published by 10fc59a3fcSJeremy Kerr * the Free Software Foundation; either version 2, or (at your option) 11fc59a3fcSJeremy Kerr * any later version. 12fc59a3fcSJeremy Kerr * 13fc59a3fcSJeremy Kerr * This program is distributed in the hope that it will be useful, 14fc59a3fcSJeremy Kerr * but WITHOUT ANY WARRANTY; without even the implied warranty of 15fc59a3fcSJeremy Kerr * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16fc59a3fcSJeremy Kerr * GNU General Public License for more details. 17fc59a3fcSJeremy Kerr * 18fc59a3fcSJeremy Kerr * You should have received a copy of the GNU General Public License 19fc59a3fcSJeremy Kerr * along with this program; if not, write to the Free Software 20fc59a3fcSJeremy Kerr * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 21fc59a3fcSJeremy Kerr */ 22fc59a3fcSJeremy Kerr 23fc59a3fcSJeremy Kerr #include <linux/init.h> 24*5a0e3ad6STejun Heo #include <linux/slab.h> 25fc59a3fcSJeremy Kerr #include <linux/debugfs.h> 26fc59a3fcSJeremy Kerr #include <asm/smp.h> 27fc59a3fcSJeremy Kerr #include <asm/system.h> 28fc59a3fcSJeremy Kerr #include <asm/uaccess.h> 29b71a0c29SSachin Sant #include <asm/firmware.h> 30fc59a3fcSJeremy Kerr 31fc59a3fcSJeremy Kerr #include "plpar_wrappers.h" 32fc59a3fcSJeremy Kerr 33fc59a3fcSJeremy Kerr /* 34fc59a3fcSJeremy Kerr * Layout of entries in the hypervisor's DTL buffer. Although we don't 35fc59a3fcSJeremy Kerr * actually access the internals of an entry (we only need to know the size), 36fc59a3fcSJeremy Kerr * we might as well define it here for reference. 37fc59a3fcSJeremy Kerr */ 38fc59a3fcSJeremy Kerr struct dtl_entry { 39fc59a3fcSJeremy Kerr u8 dispatch_reason; 40fc59a3fcSJeremy Kerr u8 preempt_reason; 41fc59a3fcSJeremy Kerr u16 processor_id; 42fc59a3fcSJeremy Kerr u32 enqueue_to_dispatch_time; 43fc59a3fcSJeremy Kerr u32 ready_to_enqueue_time; 44fc59a3fcSJeremy Kerr u32 waiting_to_ready_time; 45fc59a3fcSJeremy Kerr u64 timebase; 46fc59a3fcSJeremy Kerr u64 fault_addr; 47fc59a3fcSJeremy Kerr u64 srr0; 48fc59a3fcSJeremy Kerr u64 srr1; 49fc59a3fcSJeremy Kerr }; 50fc59a3fcSJeremy Kerr 51fc59a3fcSJeremy Kerr struct dtl { 52fc59a3fcSJeremy Kerr struct dtl_entry *buf; 53fc59a3fcSJeremy Kerr struct dentry *file; 54fc59a3fcSJeremy Kerr int cpu; 55fc59a3fcSJeremy Kerr int buf_entries; 56fc59a3fcSJeremy Kerr u64 last_idx; 57fc59a3fcSJeremy Kerr }; 586b7487fcSTejun Heo static DEFINE_PER_CPU(struct dtl, cpu_dtl); 59fc59a3fcSJeremy Kerr 60fc59a3fcSJeremy Kerr /* 61fc59a3fcSJeremy Kerr * Dispatch trace log event mask: 62fc59a3fcSJeremy Kerr * 0x7: 0x1: voluntary virtual processor waits 63fc59a3fcSJeremy Kerr * 0x2: time-slice preempts 64fc59a3fcSJeremy Kerr * 0x4: virtual partition memory page faults 65fc59a3fcSJeremy Kerr */ 66fc59a3fcSJeremy Kerr static u8 dtl_event_mask = 0x7; 67fc59a3fcSJeremy Kerr 68fc59a3fcSJeremy Kerr 69fc59a3fcSJeremy Kerr /* 70fc59a3fcSJeremy Kerr * Size of per-cpu log buffers. Default is just under 16 pages worth. 71fc59a3fcSJeremy Kerr */ 72fc59a3fcSJeremy Kerr static int dtl_buf_entries = (16 * 85); 73fc59a3fcSJeremy Kerr 74fc59a3fcSJeremy Kerr 75fc59a3fcSJeremy Kerr static int dtl_enable(struct dtl *dtl) 76fc59a3fcSJeremy Kerr { 77fc59a3fcSJeremy Kerr unsigned long addr; 78fc59a3fcSJeremy Kerr int ret, hwcpu; 79fc59a3fcSJeremy Kerr 80fc59a3fcSJeremy Kerr /* only allow one reader */ 81fc59a3fcSJeremy Kerr if (dtl->buf) 82fc59a3fcSJeremy Kerr return -EBUSY; 83fc59a3fcSJeremy Kerr 84fc59a3fcSJeremy Kerr /* we need to store the original allocation size for use during read */ 85fc59a3fcSJeremy Kerr dtl->buf_entries = dtl_buf_entries; 86fc59a3fcSJeremy Kerr 87fc59a3fcSJeremy Kerr dtl->buf = kmalloc_node(dtl->buf_entries * sizeof(struct dtl_entry), 88fc59a3fcSJeremy Kerr GFP_KERNEL, cpu_to_node(dtl->cpu)); 89fc59a3fcSJeremy Kerr if (!dtl->buf) { 90fc59a3fcSJeremy Kerr printk(KERN_WARNING "%s: buffer alloc failed for cpu %d\n", 91fc59a3fcSJeremy Kerr __func__, dtl->cpu); 92fc59a3fcSJeremy Kerr return -ENOMEM; 93fc59a3fcSJeremy Kerr } 94fc59a3fcSJeremy Kerr 95fc59a3fcSJeremy Kerr /* Register our dtl buffer with the hypervisor. The HV expects the 96fc59a3fcSJeremy Kerr * buffer size to be passed in the second word of the buffer */ 97fc59a3fcSJeremy Kerr ((u32 *)dtl->buf)[1] = dtl->buf_entries * sizeof(struct dtl_entry); 98fc59a3fcSJeremy Kerr 99fc59a3fcSJeremy Kerr hwcpu = get_hard_smp_processor_id(dtl->cpu); 100fc59a3fcSJeremy Kerr addr = __pa(dtl->buf); 101fc59a3fcSJeremy Kerr ret = register_dtl(hwcpu, addr); 102fc59a3fcSJeremy Kerr if (ret) { 103fc59a3fcSJeremy Kerr printk(KERN_WARNING "%s: DTL registration for cpu %d (hw %d) " 104fc59a3fcSJeremy Kerr "failed with %d\n", __func__, dtl->cpu, hwcpu, ret); 105fc59a3fcSJeremy Kerr kfree(dtl->buf); 106fc59a3fcSJeremy Kerr return -EIO; 107fc59a3fcSJeremy Kerr } 108fc59a3fcSJeremy Kerr 109fc59a3fcSJeremy Kerr /* set our initial buffer indices */ 110fc59a3fcSJeremy Kerr dtl->last_idx = lppaca[dtl->cpu].dtl_idx = 0; 111fc59a3fcSJeremy Kerr 11282631f5dSJeremy Kerr /* ensure that our updates to the lppaca fields have occurred before 11382631f5dSJeremy Kerr * we actually enable the logging */ 11482631f5dSJeremy Kerr smp_wmb(); 11582631f5dSJeremy Kerr 116fc59a3fcSJeremy Kerr /* enable event logging */ 117fc59a3fcSJeremy Kerr lppaca[dtl->cpu].dtl_enable_mask = dtl_event_mask; 118fc59a3fcSJeremy Kerr 119fc59a3fcSJeremy Kerr return 0; 120fc59a3fcSJeremy Kerr } 121fc59a3fcSJeremy Kerr 122fc59a3fcSJeremy Kerr static void dtl_disable(struct dtl *dtl) 123fc59a3fcSJeremy Kerr { 124fc59a3fcSJeremy Kerr int hwcpu = get_hard_smp_processor_id(dtl->cpu); 125fc59a3fcSJeremy Kerr 126fc59a3fcSJeremy Kerr lppaca[dtl->cpu].dtl_enable_mask = 0x0; 127fc59a3fcSJeremy Kerr 128fc59a3fcSJeremy Kerr unregister_dtl(hwcpu, __pa(dtl->buf)); 129fc59a3fcSJeremy Kerr 130fc59a3fcSJeremy Kerr kfree(dtl->buf); 131fc59a3fcSJeremy Kerr dtl->buf = NULL; 132fc59a3fcSJeremy Kerr dtl->buf_entries = 0; 133fc59a3fcSJeremy Kerr } 134fc59a3fcSJeremy Kerr 135fc59a3fcSJeremy Kerr /* file interface */ 136fc59a3fcSJeremy Kerr 137fc59a3fcSJeremy Kerr static int dtl_file_open(struct inode *inode, struct file *filp) 138fc59a3fcSJeremy Kerr { 139fc59a3fcSJeremy Kerr struct dtl *dtl = inode->i_private; 140fc59a3fcSJeremy Kerr int rc; 141fc59a3fcSJeremy Kerr 142fc59a3fcSJeremy Kerr rc = dtl_enable(dtl); 143fc59a3fcSJeremy Kerr if (rc) 144fc59a3fcSJeremy Kerr return rc; 145fc59a3fcSJeremy Kerr 146fc59a3fcSJeremy Kerr filp->private_data = dtl; 147fc59a3fcSJeremy Kerr return 0; 148fc59a3fcSJeremy Kerr } 149fc59a3fcSJeremy Kerr 150fc59a3fcSJeremy Kerr static int dtl_file_release(struct inode *inode, struct file *filp) 151fc59a3fcSJeremy Kerr { 152fc59a3fcSJeremy Kerr struct dtl *dtl = inode->i_private; 153fc59a3fcSJeremy Kerr dtl_disable(dtl); 154fc59a3fcSJeremy Kerr return 0; 155fc59a3fcSJeremy Kerr } 156fc59a3fcSJeremy Kerr 157fc59a3fcSJeremy Kerr static ssize_t dtl_file_read(struct file *filp, char __user *buf, size_t len, 158fc59a3fcSJeremy Kerr loff_t *pos) 159fc59a3fcSJeremy Kerr { 160fc59a3fcSJeremy Kerr int rc, cur_idx, last_idx, n_read, n_req, read_size; 161fc59a3fcSJeremy Kerr struct dtl *dtl; 162fc59a3fcSJeremy Kerr 163fc59a3fcSJeremy Kerr if ((len % sizeof(struct dtl_entry)) != 0) 164fc59a3fcSJeremy Kerr return -EINVAL; 165fc59a3fcSJeremy Kerr 166fc59a3fcSJeremy Kerr dtl = filp->private_data; 167fc59a3fcSJeremy Kerr 168fc59a3fcSJeremy Kerr /* requested number of entries to read */ 169fc59a3fcSJeremy Kerr n_req = len / sizeof(struct dtl_entry); 170fc59a3fcSJeremy Kerr 171fc59a3fcSJeremy Kerr /* actual number of entries read */ 172fc59a3fcSJeremy Kerr n_read = 0; 173fc59a3fcSJeremy Kerr 174fc59a3fcSJeremy Kerr cur_idx = lppaca[dtl->cpu].dtl_idx; 175fc59a3fcSJeremy Kerr last_idx = dtl->last_idx; 176fc59a3fcSJeremy Kerr 177fc59a3fcSJeremy Kerr if (cur_idx - last_idx > dtl->buf_entries) { 178fc59a3fcSJeremy Kerr pr_debug("%s: hv buffer overflow for cpu %d, samples lost\n", 179fc59a3fcSJeremy Kerr __func__, dtl->cpu); 180fc59a3fcSJeremy Kerr } 181fc59a3fcSJeremy Kerr 182fc59a3fcSJeremy Kerr cur_idx %= dtl->buf_entries; 183fc59a3fcSJeremy Kerr last_idx %= dtl->buf_entries; 184fc59a3fcSJeremy Kerr 185fc59a3fcSJeremy Kerr /* read the tail of the buffer if we've wrapped */ 186fc59a3fcSJeremy Kerr if (last_idx > cur_idx) { 187fc59a3fcSJeremy Kerr read_size = min(n_req, dtl->buf_entries - last_idx); 188fc59a3fcSJeremy Kerr 189fc59a3fcSJeremy Kerr rc = copy_to_user(buf, &dtl->buf[last_idx], 190fc59a3fcSJeremy Kerr read_size * sizeof(struct dtl_entry)); 191fc59a3fcSJeremy Kerr if (rc) 192fc59a3fcSJeremy Kerr return -EFAULT; 193fc59a3fcSJeremy Kerr 194fc59a3fcSJeremy Kerr last_idx = 0; 195fc59a3fcSJeremy Kerr n_req -= read_size; 196fc59a3fcSJeremy Kerr n_read += read_size; 197fc59a3fcSJeremy Kerr buf += read_size * sizeof(struct dtl_entry); 198fc59a3fcSJeremy Kerr } 199fc59a3fcSJeremy Kerr 200fc59a3fcSJeremy Kerr /* .. and now the head */ 201fc59a3fcSJeremy Kerr read_size = min(n_req, cur_idx - last_idx); 202fc59a3fcSJeremy Kerr rc = copy_to_user(buf, &dtl->buf[last_idx], 203fc59a3fcSJeremy Kerr read_size * sizeof(struct dtl_entry)); 204fc59a3fcSJeremy Kerr if (rc) 205fc59a3fcSJeremy Kerr return -EFAULT; 206fc59a3fcSJeremy Kerr 207fc59a3fcSJeremy Kerr n_read += read_size; 208fc59a3fcSJeremy Kerr dtl->last_idx += n_read; 209fc59a3fcSJeremy Kerr 210fc59a3fcSJeremy Kerr return n_read * sizeof(struct dtl_entry); 211fc59a3fcSJeremy Kerr } 212fc59a3fcSJeremy Kerr 213828c0950SAlexey Dobriyan static const struct file_operations dtl_fops = { 214fc59a3fcSJeremy Kerr .open = dtl_file_open, 215fc59a3fcSJeremy Kerr .release = dtl_file_release, 216fc59a3fcSJeremy Kerr .read = dtl_file_read, 217fc59a3fcSJeremy Kerr .llseek = no_llseek, 218fc59a3fcSJeremy Kerr }; 219fc59a3fcSJeremy Kerr 220fc59a3fcSJeremy Kerr static struct dentry *dtl_dir; 221fc59a3fcSJeremy Kerr 222fc59a3fcSJeremy Kerr static int dtl_setup_file(struct dtl *dtl) 223fc59a3fcSJeremy Kerr { 224fc59a3fcSJeremy Kerr char name[10]; 225fc59a3fcSJeremy Kerr 226fc59a3fcSJeremy Kerr sprintf(name, "cpu-%d", dtl->cpu); 227fc59a3fcSJeremy Kerr 228fc59a3fcSJeremy Kerr dtl->file = debugfs_create_file(name, 0400, dtl_dir, dtl, &dtl_fops); 229fc59a3fcSJeremy Kerr if (!dtl->file) 230fc59a3fcSJeremy Kerr return -ENOMEM; 231fc59a3fcSJeremy Kerr 232fc59a3fcSJeremy Kerr return 0; 233fc59a3fcSJeremy Kerr } 234fc59a3fcSJeremy Kerr 235fc59a3fcSJeremy Kerr static int dtl_init(void) 236fc59a3fcSJeremy Kerr { 237fc59a3fcSJeremy Kerr struct dentry *event_mask_file, *buf_entries_file; 238fc59a3fcSJeremy Kerr int rc, i; 239fc59a3fcSJeremy Kerr 240fc59a3fcSJeremy Kerr if (!firmware_has_feature(FW_FEATURE_SPLPAR)) 241fc59a3fcSJeremy Kerr return -ENODEV; 242fc59a3fcSJeremy Kerr 243fc59a3fcSJeremy Kerr /* set up common debugfs structure */ 244fc59a3fcSJeremy Kerr 245fc59a3fcSJeremy Kerr rc = -ENOMEM; 246fc59a3fcSJeremy Kerr dtl_dir = debugfs_create_dir("dtl", powerpc_debugfs_root); 247fc59a3fcSJeremy Kerr if (!dtl_dir) { 248fc59a3fcSJeremy Kerr printk(KERN_WARNING "%s: can't create dtl root dir\n", 249fc59a3fcSJeremy Kerr __func__); 250fc59a3fcSJeremy Kerr goto err; 251fc59a3fcSJeremy Kerr } 252fc59a3fcSJeremy Kerr 253fc59a3fcSJeremy Kerr event_mask_file = debugfs_create_x8("dtl_event_mask", 0600, 254fc59a3fcSJeremy Kerr dtl_dir, &dtl_event_mask); 255fc59a3fcSJeremy Kerr buf_entries_file = debugfs_create_u32("dtl_buf_entries", 0600, 256fc59a3fcSJeremy Kerr dtl_dir, &dtl_buf_entries); 257fc59a3fcSJeremy Kerr 258fc59a3fcSJeremy Kerr if (!event_mask_file || !buf_entries_file) { 259fc59a3fcSJeremy Kerr printk(KERN_WARNING "%s: can't create dtl files\n", __func__); 260fc59a3fcSJeremy Kerr goto err_remove_dir; 261fc59a3fcSJeremy Kerr } 262fc59a3fcSJeremy Kerr 263fc59a3fcSJeremy Kerr /* set up the per-cpu log structures */ 264fc59a3fcSJeremy Kerr for_each_possible_cpu(i) { 2656b7487fcSTejun Heo struct dtl *dtl = &per_cpu(cpu_dtl, i); 266fc59a3fcSJeremy Kerr dtl->cpu = i; 267fc59a3fcSJeremy Kerr 268fc59a3fcSJeremy Kerr rc = dtl_setup_file(dtl); 269fc59a3fcSJeremy Kerr if (rc) 270fc59a3fcSJeremy Kerr goto err_remove_dir; 271fc59a3fcSJeremy Kerr } 272fc59a3fcSJeremy Kerr 273fc59a3fcSJeremy Kerr return 0; 274fc59a3fcSJeremy Kerr 275fc59a3fcSJeremy Kerr err_remove_dir: 276fc59a3fcSJeremy Kerr debugfs_remove_recursive(dtl_dir); 277fc59a3fcSJeremy Kerr err: 278fc59a3fcSJeremy Kerr return rc; 279fc59a3fcSJeremy Kerr } 280fc59a3fcSJeremy Kerr arch_initcall(dtl_init); 281