 25985edced
			
		
	
	
	25985edced
	
	
	
		
			
			Fixes generated by 'codespell' and manually reviewed. Signed-off-by: Lucas De Marchi <lucas.demarchi@profusion.mobi>
		
			
				
	
	
		
			296 lines
		
	
	
	
		
			7.3 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			296 lines
		
	
	
	
		
			7.3 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * Copyright (C) 2002-2003 Hewlett-Packard Co
 | |
|  *               Stephane Eranian <eranian@hpl.hp.com>
 | |
|  *
 | |
|  * This file implements the default sampling buffer format
 | |
|  * for the Linux/ia64 perfmon-2 subsystem.
 | |
|  */
 | |
| #include <linux/kernel.h>
 | |
| #include <linux/types.h>
 | |
| #include <linux/module.h>
 | |
| #include <linux/init.h>
 | |
| #include <asm/delay.h>
 | |
| #include <linux/smp.h>
 | |
| 
 | |
| #include <asm/perfmon.h>
 | |
| #include <asm/perfmon_default_smpl.h>
 | |
| 
 | |
| MODULE_AUTHOR("Stephane Eranian <eranian@hpl.hp.com>");
 | |
| MODULE_DESCRIPTION("perfmon default sampling format");
 | |
| MODULE_LICENSE("GPL");
 | |
| 
 | |
| #define DEFAULT_DEBUG 1
 | |
| 
 | |
| #ifdef DEFAULT_DEBUG
 | |
| #define DPRINT(a) \
 | |
| 	do { \
 | |
| 		if (unlikely(pfm_sysctl.debug >0)) { printk("%s.%d: CPU%d ", __func__, __LINE__, smp_processor_id()); printk a; } \
 | |
| 	} while (0)
 | |
| 
 | |
| #define DPRINT_ovfl(a) \
 | |
| 	do { \
 | |
| 		if (unlikely(pfm_sysctl.debug > 0 && pfm_sysctl.debug_ovfl >0)) { printk("%s.%d: CPU%d ", __func__, __LINE__, smp_processor_id()); printk a; } \
 | |
| 	} while (0)
 | |
| 
 | |
| #else
 | |
| #define DPRINT(a)
 | |
| #define DPRINT_ovfl(a)
 | |
| #endif
 | |
| 
 | |
| static int
 | |
| default_validate(struct task_struct *task, unsigned int flags, int cpu, void *data)
 | |
| {
 | |
| 	pfm_default_smpl_arg_t *arg = (pfm_default_smpl_arg_t*)data;
 | |
| 	int ret = 0;
 | |
| 
 | |
| 	if (data == NULL) {
 | |
| 		DPRINT(("[%d] no argument passed\n", task_pid_nr(task)));
 | |
| 		return -EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	DPRINT(("[%d] validate flags=0x%x CPU%d\n", task_pid_nr(task), flags, cpu));
 | |
| 
 | |
| 	/*
 | |
| 	 * must hold at least the buffer header + one minimally sized entry
 | |
| 	 */
 | |
| 	if (arg->buf_size < PFM_DEFAULT_SMPL_MIN_BUF_SIZE) return -EINVAL;
 | |
| 
 | |
| 	DPRINT(("buf_size=%lu\n", arg->buf_size));
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int
 | |
| default_get_size(struct task_struct *task, unsigned int flags, int cpu, void *data, unsigned long *size)
 | |
| {
 | |
| 	pfm_default_smpl_arg_t *arg = (pfm_default_smpl_arg_t *)data;
 | |
| 
 | |
| 	/*
 | |
| 	 * size has been validated in default_validate
 | |
| 	 */
 | |
| 	*size = arg->buf_size;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int
 | |
| default_init(struct task_struct *task, void *buf, unsigned int flags, int cpu, void *data)
 | |
| {
 | |
| 	pfm_default_smpl_hdr_t *hdr;
 | |
| 	pfm_default_smpl_arg_t *arg = (pfm_default_smpl_arg_t *)data;
 | |
| 
 | |
| 	hdr = (pfm_default_smpl_hdr_t *)buf;
 | |
| 
 | |
| 	hdr->hdr_version      = PFM_DEFAULT_SMPL_VERSION;
 | |
| 	hdr->hdr_buf_size     = arg->buf_size;
 | |
| 	hdr->hdr_cur_offs     = sizeof(*hdr);
 | |
| 	hdr->hdr_overflows    = 0UL;
 | |
| 	hdr->hdr_count        = 0UL;
 | |
| 
 | |
| 	DPRINT(("[%d] buffer=%p buf_size=%lu hdr_size=%lu hdr_version=%u cur_offs=%lu\n",
 | |
| 		task_pid_nr(task),
 | |
| 		buf,
 | |
| 		hdr->hdr_buf_size,
 | |
| 		sizeof(*hdr),
 | |
| 		hdr->hdr_version,
 | |
| 		hdr->hdr_cur_offs));
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int
 | |
| default_handler(struct task_struct *task, void *buf, pfm_ovfl_arg_t *arg, struct pt_regs *regs, unsigned long stamp)
 | |
| {
 | |
| 	pfm_default_smpl_hdr_t *hdr;
 | |
| 	pfm_default_smpl_entry_t *ent;
 | |
| 	void *cur, *last;
 | |
| 	unsigned long *e, entry_size;
 | |
| 	unsigned int npmds, i;
 | |
| 	unsigned char ovfl_pmd;
 | |
| 	unsigned char ovfl_notify;
 | |
| 
 | |
| 	if (unlikely(buf == NULL || arg == NULL|| regs == NULL || task == NULL)) {
 | |
| 		DPRINT(("[%d] invalid arguments buf=%p arg=%p\n", task->pid, buf, arg));
 | |
| 		return -EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	hdr         = (pfm_default_smpl_hdr_t *)buf;
 | |
| 	cur         = buf+hdr->hdr_cur_offs;
 | |
| 	last        = buf+hdr->hdr_buf_size;
 | |
| 	ovfl_pmd    = arg->ovfl_pmd;
 | |
| 	ovfl_notify = arg->ovfl_notify;
 | |
| 
 | |
| 	/*
 | |
| 	 * precheck for sanity
 | |
| 	 */
 | |
| 	if ((last - cur) < PFM_DEFAULT_MAX_ENTRY_SIZE) goto full;
 | |
| 
 | |
| 	npmds = hweight64(arg->smpl_pmds[0]);
 | |
| 
 | |
| 	ent = (pfm_default_smpl_entry_t *)cur;
 | |
| 
 | |
| 	prefetch(arg->smpl_pmds_values);
 | |
| 
 | |
| 	entry_size = sizeof(*ent) + (npmds << 3);
 | |
| 
 | |
| 	/* position for first pmd */
 | |
| 	e = (unsigned long *)(ent+1);
 | |
| 
 | |
| 	hdr->hdr_count++;
 | |
| 
 | |
| 	DPRINT_ovfl(("[%d] count=%lu cur=%p last=%p free_bytes=%lu ovfl_pmd=%d ovfl_notify=%d npmds=%u\n",
 | |
| 			task->pid,
 | |
| 			hdr->hdr_count,
 | |
| 			cur, last,
 | |
| 			last-cur,
 | |
| 			ovfl_pmd,
 | |
| 			ovfl_notify, npmds));
 | |
| 
 | |
| 	/*
 | |
| 	 * current = task running at the time of the overflow.
 | |
| 	 *
 | |
| 	 * per-task mode:
 | |
| 	 * 	- this is usually the task being monitored.
 | |
| 	 * 	  Under certain conditions, it might be a different task
 | |
| 	 *
 | |
| 	 * system-wide:
 | |
| 	 * 	- this is not necessarily the task controlling the session
 | |
| 	 */
 | |
| 	ent->pid            = current->pid;
 | |
| 	ent->ovfl_pmd  	    = ovfl_pmd;
 | |
| 	ent->last_reset_val = arg->pmd_last_reset; //pmd[0].reg_last_reset_val;
 | |
| 
 | |
| 	/*
 | |
| 	 * where did the fault happen (includes slot number)
 | |
| 	 */
 | |
| 	ent->ip = regs->cr_iip | ((regs->cr_ipsr >> 41) & 0x3);
 | |
| 
 | |
| 	ent->tstamp    = stamp;
 | |
| 	ent->cpu       = smp_processor_id();
 | |
| 	ent->set       = arg->active_set;
 | |
| 	ent->tgid      = current->tgid;
 | |
| 
 | |
| 	/*
 | |
| 	 * selectively store PMDs in increasing index number
 | |
| 	 */
 | |
| 	if (npmds) {
 | |
| 		unsigned long *val = arg->smpl_pmds_values;
 | |
| 		for(i=0; i < npmds; i++) {
 | |
| 			*e++ = *val++;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * update position for next entry
 | |
| 	 */
 | |
| 	hdr->hdr_cur_offs += entry_size;
 | |
| 	cur               += entry_size;
 | |
| 
 | |
| 	/*
 | |
| 	 * post check to avoid losing the last sample
 | |
| 	 */
 | |
| 	if ((last - cur) < PFM_DEFAULT_MAX_ENTRY_SIZE) goto full;
 | |
| 
 | |
| 	/*
 | |
| 	 * keep same ovfl_pmds, ovfl_notify
 | |
| 	 */
 | |
| 	arg->ovfl_ctrl.bits.notify_user     = 0;
 | |
| 	arg->ovfl_ctrl.bits.block_task      = 0;
 | |
| 	arg->ovfl_ctrl.bits.mask_monitoring = 0;
 | |
| 	arg->ovfl_ctrl.bits.reset_ovfl_pmds = 1; /* reset before returning from interrupt handler */
 | |
| 
 | |
| 	return 0;
 | |
| full:
 | |
| 	DPRINT_ovfl(("sampling buffer full free=%lu, count=%lu, ovfl_notify=%d\n", last-cur, hdr->hdr_count, ovfl_notify));
 | |
| 
 | |
| 	/*
 | |
| 	 * increment number of buffer overflow.
 | |
| 	 * important to detect duplicate set of samples.
 | |
| 	 */
 | |
| 	hdr->hdr_overflows++;
 | |
| 
 | |
| 	/*
 | |
| 	 * if no notification requested, then we saturate the buffer
 | |
| 	 */
 | |
| 	if (ovfl_notify == 0) {
 | |
| 		arg->ovfl_ctrl.bits.notify_user     = 0;
 | |
| 		arg->ovfl_ctrl.bits.block_task      = 0;
 | |
| 		arg->ovfl_ctrl.bits.mask_monitoring = 1;
 | |
| 		arg->ovfl_ctrl.bits.reset_ovfl_pmds = 0;
 | |
| 	} else {
 | |
| 		arg->ovfl_ctrl.bits.notify_user     = 1;
 | |
| 		arg->ovfl_ctrl.bits.block_task      = 1; /* ignored for non-blocking context */
 | |
| 		arg->ovfl_ctrl.bits.mask_monitoring = 1;
 | |
| 		arg->ovfl_ctrl.bits.reset_ovfl_pmds = 0; /* no reset now */
 | |
| 	}
 | |
| 	return -1; /* we are full, sorry */
 | |
| }
 | |
| 
 | |
| static int
 | |
| default_restart(struct task_struct *task, pfm_ovfl_ctrl_t *ctrl, void *buf, struct pt_regs *regs)
 | |
| {
 | |
| 	pfm_default_smpl_hdr_t *hdr;
 | |
| 
 | |
| 	hdr = (pfm_default_smpl_hdr_t *)buf;
 | |
| 
 | |
| 	hdr->hdr_count    = 0UL;
 | |
| 	hdr->hdr_cur_offs = sizeof(*hdr);
 | |
| 
 | |
| 	ctrl->bits.mask_monitoring = 0;
 | |
| 	ctrl->bits.reset_ovfl_pmds = 1; /* uses long-reset values */
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int
 | |
| default_exit(struct task_struct *task, void *buf, struct pt_regs *regs)
 | |
| {
 | |
| 	DPRINT(("[%d] exit(%p)\n", task_pid_nr(task), buf));
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static pfm_buffer_fmt_t default_fmt={
 | |
|  	.fmt_name 	    = "default_format",
 | |
|  	.fmt_uuid	    = PFM_DEFAULT_SMPL_UUID,
 | |
|  	.fmt_arg_size	    = sizeof(pfm_default_smpl_arg_t),
 | |
|  	.fmt_validate	    = default_validate,
 | |
|  	.fmt_getsize	    = default_get_size,
 | |
|  	.fmt_init	    = default_init,
 | |
|  	.fmt_handler	    = default_handler,
 | |
|  	.fmt_restart	    = default_restart,
 | |
|  	.fmt_restart_active = default_restart,
 | |
|  	.fmt_exit	    = default_exit,
 | |
| };
 | |
| 
 | |
| static int __init
 | |
| pfm_default_smpl_init_module(void)
 | |
| {
 | |
| 	int ret;
 | |
| 
 | |
| 	ret = pfm_register_buffer_fmt(&default_fmt);
 | |
| 	if (ret == 0) {
 | |
| 		printk("perfmon_default_smpl: %s v%u.%u registered\n",
 | |
| 			default_fmt.fmt_name,
 | |
| 			PFM_DEFAULT_SMPL_VERSION_MAJ,
 | |
| 			PFM_DEFAULT_SMPL_VERSION_MIN);
 | |
| 	} else {
 | |
| 		printk("perfmon_default_smpl: %s cannot register ret=%d\n",
 | |
| 			default_fmt.fmt_name,
 | |
| 			ret);
 | |
| 	}
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static void __exit
 | |
| pfm_default_smpl_cleanup_module(void)
 | |
| {
 | |
| 	int ret;
 | |
| 	ret = pfm_unregister_buffer_fmt(default_fmt.fmt_uuid);
 | |
| 
 | |
| 	printk("perfmon_default_smpl: unregister %s=%d\n", default_fmt.fmt_name, ret);
 | |
| }
 | |
| 
 | |
| module_init(pfm_default_smpl_init_module);
 | |
| module_exit(pfm_default_smpl_cleanup_module);
 | |
| 
 |