⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sb_tbprof.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * 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, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA. * * Copyright (C) 2001, 2002, 2003 Broadcom Corporation * Copyright (C) 2007 Ralf Baechle <ralf@linux-mips.org> * Copyright (C) 2007 MIPS Technologies, Inc. *    written by Ralf Baechle <ralf@linux-mips.org> */#undef DEBUG#include <linux/device.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/types.h>#include <linux/init.h>#include <linux/interrupt.h>#include <linux/slab.h>#include <linux/vmalloc.h>#include <linux/fs.h>#include <linux/errno.h>#include <linux/wait.h>#include <asm/io.h>#include <asm/sibyte/sb1250.h>#if defined(CONFIG_SIBYTE_BCM1x55) || defined(CONFIG_SIBYTE_BCM1x80)#include <asm/sibyte/bcm1480_regs.h>#include <asm/sibyte/bcm1480_scd.h>#include <asm/sibyte/bcm1480_int.h>#elif defined(CONFIG_SIBYTE_SB1250) || defined(CONFIG_SIBYTE_BCM112X)#include <asm/sibyte/sb1250_regs.h>#include <asm/sibyte/sb1250_scd.h>#include <asm/sibyte/sb1250_int.h>#else#error invalid SiByte UART configuation#endif#if defined(CONFIG_SIBYTE_BCM1x55) || defined(CONFIG_SIBYTE_BCM1x80)#undef K_INT_TRACE_FREEZE#define K_INT_TRACE_FREEZE K_BCM1480_INT_TRACE_FREEZE#undef K_INT_PERF_CNT#define K_INT_PERF_CNT K_BCM1480_INT_PERF_CNT#endif#include <asm/system.h>#include <asm/uaccess.h>#define SBPROF_TB_MAJOR 240typedef u64 tb_sample_t[6*256];enum open_status {	SB_CLOSED,	SB_OPENING,	SB_OPEN};struct sbprof_tb {	wait_queue_head_t	tb_sync;	wait_queue_head_t	tb_read;	struct mutex		lock;	enum open_status	open;	tb_sample_t		*sbprof_tbbuf;	int			next_tb_sample;	volatile int		tb_enable;	volatile int		tb_armed;};static struct sbprof_tb sbp;#define MAX_SAMPLE_BYTES (24*1024*1024)#define MAX_TBSAMPLE_BYTES (12*1024*1024)#define MAX_SAMPLES (MAX_SAMPLE_BYTES/sizeof(u_int32_t))#define TB_SAMPLE_SIZE (sizeof(tb_sample_t))#define MAX_TB_SAMPLES (MAX_TBSAMPLE_BYTES/TB_SAMPLE_SIZE)/* ioctls */#define SBPROF_ZBSTART		_IOW('s', 0, int)#define SBPROF_ZBSTOP		_IOW('s', 1, int)#define SBPROF_ZBWAITFULL	_IOW('s', 2, int)/* * Routines for using 40-bit SCD cycle counter * * Client responsible for either handling interrupts or making sure * the cycles counter never saturates, e.g., by doing * zclk_timer_init(0) at least every 2^40 - 1 ZCLKs. *//* * Configures SCD counter 0 to count ZCLKs starting from val; * Configures SCD counters1,2,3 to count nothing. * Must not be called while gathering ZBbus profiles. */#define zclk_timer_init(val) \  __asm__ __volatile__ (".set push;" \			".set mips64;" \			"la   $8, 0xb00204c0;" /* SCD perf_cnt_cfg */ \			"sd   %0, 0x10($8);"   /* write val to counter0 */ \			"sd   %1, 0($8);"      /* config counter0 for zclks*/ \			".set pop" \			: /* no outputs */ \						     /* enable, counter0 */ \			: /* inputs */ "r"(val), "r" ((1ULL << 33) | 1ULL) \			: /* modifies */ "$8" )/* Reads SCD counter 0 and puts result in value   unsigned long long val; */#define zclk_get(val) \  __asm__ __volatile__ (".set push;" \			".set mips64;" \			"la   $8, 0xb00204c0;" /* SCD perf_cnt_cfg */ \			"ld   %0, 0x10($8);"   /* write val to counter0 */ \			".set pop" \			: /* outputs */ "=r"(val) \			: /* inputs */ \			: /* modifies */ "$8" )#define DEVNAME "sb_tbprof"#define TB_FULL (sbp.next_tb_sample == MAX_TB_SAMPLES)/* * Support for ZBbus sampling using the trace buffer * * We use the SCD performance counter interrupt, caused by a Zclk counter * overflow, to trigger the start of tracing. * * We set the trace buffer to sample everything and freeze on * overflow. * * We map the interrupt for trace_buffer_freeze to handle it on CPU 0. * */static u64 tb_period;static void arm_tb(void){        u64 scdperfcnt;	u64 next = (1ULL << 40) - tb_period;	u64 tb_options = M_SCD_TRACE_CFG_FREEZE_FULL;	/*	 * Generate an SCD_PERFCNT interrupt in TB_PERIOD Zclks to	 * trigger start of trace.  XXX vary sampling period	 */	__raw_writeq(0, IOADDR(A_SCD_PERF_CNT_1));	scdperfcnt = __raw_readq(IOADDR(A_SCD_PERF_CNT_CFG));	/*	 * Unfortunately, in Pass 2 we must clear all counters to knock down	 * a previous interrupt request.  This means that bus profiling	 * requires ALL of the SCD perf counters.	 */#if defined(CONFIG_SIBYTE_BCM1x55) || defined(CONFIG_SIBYTE_BCM1x80)	__raw_writeq((scdperfcnt & ~M_SPC_CFG_SRC1) |						/* keep counters 0,2,3,4,5,6,7 as is */		     V_SPC_CFG_SRC1(1),		/* counter 1 counts cycles */		     IOADDR(A_BCM1480_SCD_PERF_CNT_CFG0));	__raw_writeq(		     M_SPC_CFG_ENABLE |		/* enable counting */		     M_SPC_CFG_CLEAR |		/* clear all counters */		     V_SPC_CFG_SRC1(1),		/* counter 1 counts cycles */		     IOADDR(A_BCM1480_SCD_PERF_CNT_CFG1));#else	__raw_writeq((scdperfcnt & ~M_SPC_CFG_SRC1) |						/* keep counters 0,2,3 as is */		     M_SPC_CFG_ENABLE |		/* enable counting */		     M_SPC_CFG_CLEAR |		/* clear all counters */		     V_SPC_CFG_SRC1(1),		/* counter 1 counts cycles */		     IOADDR(A_SCD_PERF_CNT_CFG));#endif	__raw_writeq(next, IOADDR(A_SCD_PERF_CNT_1));	/* Reset the trace buffer */	__raw_writeq(M_SCD_TRACE_CFG_RESET, IOADDR(A_SCD_TRACE_CFG));#if 0 && defined(M_SCD_TRACE_CFG_FORCECNT)	/* XXXKW may want to expose control to the data-collector */	tb_options |= M_SCD_TRACE_CFG_FORCECNT;#endif	__raw_writeq(tb_options, IOADDR(A_SCD_TRACE_CFG));	sbp.tb_armed = 1;}static irqreturn_t sbprof_tb_intr(int irq, void *dev_id){	int i;	pr_debug(DEVNAME ": tb_intr\n");	if (sbp.next_tb_sample < MAX_TB_SAMPLES) {		/* XXX should use XKPHYS to make writes bypass L2 */		u64 *p = sbp.sbprof_tbbuf[sbp.next_tb_sample++];		/* Read out trace */		__raw_writeq(M_SCD_TRACE_CFG_START_READ,			     IOADDR(A_SCD_TRACE_CFG));		__asm__ __volatile__ ("sync" : : : "memory");		/* Loop runs backwards because bundles are read out in reverse order */		for (i = 256 * 6; i > 0; i -= 6) {			/* Subscripts decrease to put bundle in the order */			/*   t0 lo, t0 hi, t1 lo, t1 hi, t2 lo, t2 hi */			p[i - 1] = __raw_readq(IOADDR(A_SCD_TRACE_READ));			/* read t2 hi */			p[i - 2] = __raw_readq(IOADDR(A_SCD_TRACE_READ));			/* read t2 lo */			p[i - 3] = __raw_readq(IOADDR(A_SCD_TRACE_READ));			/* read t1 hi */			p[i - 4] = __raw_readq(IOADDR(A_SCD_TRACE_READ));			/* read t1 lo */			p[i - 5] = __raw_readq(IOADDR(A_SCD_TRACE_READ));			/* read t0 hi */			p[i - 6] = __raw_readq(IOADDR(A_SCD_TRACE_READ));			/* read t0 lo */		}		if (!sbp.tb_enable) {			pr_debug(DEVNAME ": tb_intr shutdown\n");			__raw_writeq(M_SCD_TRACE_CFG_RESET,				     IOADDR(A_SCD_TRACE_CFG));			sbp.tb_armed = 0;			wake_up_interruptible(&sbp.tb_sync);		} else {			/* knock down current interrupt and get another one later */			arm_tb();		}	} else {		/* No more trace buffer samples */		pr_debug(DEVNAME ": tb_intr full\n");		__raw_writeq(M_SCD_TRACE_CFG_RESET, IOADDR(A_SCD_TRACE_CFG));		sbp.tb_armed = 0;		if (!sbp.tb_enable)			wake_up_interruptible(&sbp.tb_sync);		wake_up_interruptible(&sbp.tb_read);	}	return IRQ_HANDLED;}static irqreturn_t sbprof_pc_intr(int irq, void *dev_id){	printk(DEVNAME ": unexpected pc_intr");	return IRQ_NONE;}/* * Requires: Already called zclk_timer_init with a value that won't *           saturate 40 bits.  No subsequent use of SCD performance counters *           or trace buffer. */static int sbprof_zbprof_start(struct file *filp){	u64 scdperfcnt;	int err;	if (xchg(&sbp.tb_enable, 1))		return -EBUSY;	pr_debug(DEVNAME ": starting\n");	sbp.next_tb_sample = 0;	filp->f_pos = 0;	err = request_irq(K_INT_TRACE_FREEZE, sbprof_tb_intr, 0,			  DEVNAME " trace freeze", &sbp);	if (err)		return -EBUSY;	/* Make sure there isn't a perf-cnt interrupt waiting */	scdperfcnt = __raw_readq(IOADDR(A_SCD_PERF_CNT_CFG));	/* Disable and clear counters, override SRC_1 */	__raw_writeq((scdperfcnt & ~(M_SPC_CFG_SRC1 | M_SPC_CFG_ENABLE)) |		     M_SPC_CFG_ENABLE | M_SPC_CFG_CLEAR | V_SPC_CFG_SRC1(1),		     IOADDR(A_SCD_PERF_CNT_CFG));	/*	 * We grab this interrupt to prevent others from trying to use         * it, even though we don't want to service the interrupts         * (they only feed into the trace-on-interrupt mechanism)	 */	if (request_irq(K_INT_PERF_CNT, sbprof_pc_intr, 0, DEVNAME " scd perfcnt", &sbp)) {		free_irq(K_INT_TRACE_FREEZE, &sbp);		return -EBUSY;	}	/*

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -