4670 lines
142 KiB
C
4670 lines
142 KiB
C
/*COPYRIGHT**
|
|
Copyright (C) 2005-2014 Intel Corporation. All Rights Reserved.
|
|
|
|
This file is part of SEP Development Kit
|
|
|
|
SEP Development Kit is free software; you can redistribute it
|
|
and/or modify it under the terms of the GNU General Public License
|
|
version 2 as published by the Free Software Foundation.
|
|
|
|
SEP Development Kit 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 SEP Development Kit; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
As a special exception, you may use this file as part of a free software
|
|
library without restriction. Specifically, if other files instantiate
|
|
templates or use macros or inline functions from this file, or you compile
|
|
this file and link it with other files to produce an executable, this
|
|
file does not by itself cause the resulting executable to be covered by
|
|
the GNU General Public License. This exception does not however
|
|
invalidate any other reasons why the executable file might be covered by
|
|
the GNU General Public License.
|
|
**COPYRIGHT*/
|
|
|
|
#include "lwpmudrv_defines.h"
|
|
#include "lwpmudrv_version.h"
|
|
|
|
#include <linux/version.h>
|
|
#include <linux/module.h>
|
|
#include <linux/init.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/types.h>
|
|
#include <asm/page.h>
|
|
#include <linux/cdev.h>
|
|
#include <linux/proc_fs.h>
|
|
#include <linux/fcntl.h>
|
|
#include <linux/device.h>
|
|
#include <asm/uaccess.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/syscalls.h>
|
|
#include <asm/unistd.h>
|
|
#include <linux/compat.h>
|
|
|
|
#include "lwpmudrv_types.h"
|
|
#include "rise_errors.h"
|
|
#include "lwpmudrv_ecb.h"
|
|
#include "lwpmudrv_ioctl.h"
|
|
#include "lwpmudrv_struct.h"
|
|
#include "inc/ecb_iterators.h"
|
|
|
|
|
|
#include "lwpmudrv_chipset.h"
|
|
#include "pci.h"
|
|
|
|
#if defined(DRV_IA32) || defined(DRV_EM64T)
|
|
#include "apic.h"
|
|
#endif
|
|
#include "cpumon.h"
|
|
#include "lwpmudrv.h"
|
|
#include "utility.h"
|
|
#include "control.h"
|
|
#if defined(DRV_IA32) || defined(DRV_EM64T)
|
|
#include "core2.h"
|
|
#endif
|
|
#include "pmi.h"
|
|
|
|
#include "output.h"
|
|
#include "linuxos.h"
|
|
#include "sys_info.h"
|
|
#include "eventmux.h"
|
|
#if defined(DRV_IA32) || defined(DRV_EM64T)
|
|
#include "pebs.h"
|
|
#endif
|
|
|
|
#if defined(CONFIG_TRACING) && defined(CONFIG_TRACEPOINTS)
|
|
#define CREATE_TRACE_POINTS
|
|
#include <linux/string.h>
|
|
#include "lwpmudrv_trace.h"
|
|
#endif
|
|
|
|
MODULE_AUTHOR("Copyright (C) 2007-2014 Intel Corporation");
|
|
MODULE_VERSION(SEP_NAME"_"SEP_VERSION_STR);
|
|
MODULE_LICENSE("Dual BSD/GPL");
|
|
|
|
typedef struct LWPMU_DEV_NODE_S LWPMU_DEV_NODE;
|
|
typedef LWPMU_DEV_NODE *LWPMU_DEV;
|
|
|
|
struct LWPMU_DEV_NODE_S {
|
|
long buffer;
|
|
struct semaphore sem;
|
|
struct cdev cdev;
|
|
};
|
|
|
|
#define LWPMU_DEV_buffer(dev) (dev)->buffer
|
|
#define LWPMU_DEV_sem(dev) (dev)->sem
|
|
#define LWPMU_DEV_cdev(dev) (dev)->cdev
|
|
|
|
/* Global variables of the driver */
|
|
SEP_VERSION_NODE drv_version;
|
|
U64 *read_counter_info = NULL;
|
|
U64 *read_unc_ctr_info = NULL;
|
|
VOID **PMU_register_data = NULL;
|
|
U64 *prev_counter_data = NULL;
|
|
U64 prev_counter_size = 0;
|
|
#if defined(DRV_IA32) || defined(DRV_EM64T)
|
|
#endif
|
|
VOID **desc_data = NULL;
|
|
DISPATCH dispatch = NULL;
|
|
#if defined(DRV_IA32) || defined(DRV_EM64T)
|
|
#endif
|
|
U64 total_ram = 0;
|
|
U32 output_buffer_size = OUTPUT_LARGE_BUFFER;
|
|
static S32 em_groups_count = 0;
|
|
#if defined(DRV_IA32) || defined(DRV_EM64T)
|
|
#endif
|
|
static S32 desc_count = 0;
|
|
uid_t uid = 0;
|
|
EVENT_CONFIG global_ec = NULL;
|
|
DRV_CONFIG pcfg = NULL;
|
|
volatile pid_t control_pid = 0;
|
|
volatile S32 abnormal_terminate = 0;
|
|
U64 *interrupt_counts = NULL;
|
|
#if defined(DRV_IA32) || defined(DRV_EM64T)
|
|
LBR lbr = NULL;
|
|
PWR pwr = NULL;
|
|
#else
|
|
RO ro = NULL;
|
|
#endif
|
|
LWPMU_DEV lwpmu_control = NULL;
|
|
LWPMU_DEV lwmod_control = NULL;
|
|
LWPMU_DEV lwsamp_control = NULL;
|
|
|
|
/* needed for multiple uncores */
|
|
U32 num_devices = 0;
|
|
U32 cur_device = 0;
|
|
LWPMU_DEVICE devices = NULL;
|
|
U32 invoking_processor_id = 0;
|
|
static U32 uncore_em_factor = 0;
|
|
|
|
#define UNCORE_EM_GROUP_SWAP_FACTOR 100
|
|
|
|
#if defined(DRV_USE_UNLOCKED_IOCTL)
|
|
static struct mutex ioctl_lock;
|
|
#endif
|
|
volatile int in_finish_code = 0;
|
|
|
|
#define PMU_DEVICES 2 // pmu, mod
|
|
|
|
CHIPSET_CONFIG pma = NULL;
|
|
CS_DISPATCH cs_dispatch = NULL;
|
|
static S8 *cpu_mask_bits = NULL;
|
|
|
|
/*
|
|
* Global data: Buffer control structure
|
|
*/
|
|
BUFFER_DESC cpu_buf = NULL;
|
|
BUFFER_DESC module_buf = NULL;
|
|
|
|
static dev_t lwpmu_DevNum; /* the major and minor parts for SEP3 base */
|
|
static dev_t lwsamp_DevNum; /* the major and minor parts for SEP3 percpu */
|
|
|
|
#if defined (DRV_ANDROID)
|
|
#define DRV_DEVICE_DELIMITER "_"
|
|
static struct class *pmu_class = NULL;
|
|
#endif
|
|
|
|
extern volatile int config_done;
|
|
|
|
CPU_STATE pcb = NULL;
|
|
size_t pcb_size = 0;
|
|
U32 *core_to_package_map = NULL;
|
|
U32 num_packages = 0;
|
|
U64 *pmu_state = NULL;
|
|
U64 *tsc_info = NULL;
|
|
U64 *restore_bl_bypass = NULL;
|
|
U32 **restore_ha_direct2core = NULL;
|
|
U32 **restore_qpi_direct2core = NULL;
|
|
UNCORE_TOPOLOGY_INFO_NODE uncore_topology;
|
|
|
|
#if defined EMON
|
|
static U64 cpu0_TSC = 0;
|
|
#endif
|
|
|
|
#if !defined(DRV_USE_UNLOCKED_IOCTL)
|
|
#define MUTEX_INIT(lock)
|
|
#define MUTEX_LOCK(lock)
|
|
#define MUTEX_UNLOCK(lock)
|
|
#else
|
|
#define MUTEX_INIT(lock) mutex_init(&(lock));
|
|
#define MUTEX_LOCK(lock) mutex_lock(&(lock))
|
|
#define MUTEX_UNLOCK(lock) mutex_unlock(&(lock))
|
|
#endif
|
|
|
|
#if defined(DRV_IA32) || defined(DRV_EM64T)
|
|
/* ------------------------------------------------------------------------- */
|
|
/*!
|
|
* @fn void lwpmudrv_PWR_Info(IOCTL_ARGS arg)
|
|
*
|
|
* @param arg - Pointer to the IOCTL structure
|
|
*
|
|
* @return OS_STATUS
|
|
*
|
|
* @brief Make a copy of the Power control information that has been passed in.
|
|
*
|
|
* <I>Special Notes</I>
|
|
*/
|
|
static OS_STATUS
|
|
lwpmudrv_PWR_Info (
|
|
IOCTL_ARGS arg
|
|
)
|
|
{
|
|
if (DRV_CONFIG_power_capture(pcfg) == FALSE) {
|
|
SEP_PRINT_WARNING("lwpmudrv_PWR_Info: PWR capture has not been configured\n");
|
|
return OS_SUCCESS;
|
|
}
|
|
|
|
// make sure size of incoming arg is correct
|
|
if ((arg->w_len != sizeof(PWR_NODE)) || (arg->w_buf == NULL)) {
|
|
SEP_PRINT_ERROR("lwpmudrv_PWR_Info: PWR capture has not been configured\n");
|
|
return OS_FAULT;
|
|
}
|
|
|
|
//
|
|
// First things first: Make a copy of the data for global use.
|
|
//
|
|
pwr = CONTROL_Allocate_Memory((int)arg->w_len);
|
|
if (!pwr) {
|
|
return OS_NO_MEM;
|
|
}
|
|
|
|
if (copy_from_user(pwr, arg->w_buf, arg->w_len)) {
|
|
return OS_FAULT;
|
|
}
|
|
|
|
return OS_SUCCESS;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* @fn void lwpmudrv_Allocate_Restore_Buffer
|
|
*
|
|
* @param
|
|
*
|
|
* @return OS_STATUE
|
|
*
|
|
* @brief allocate buffer space to save/restore the data (for JKT, QPILL and HA register) before collection
|
|
*/
|
|
static OS_STATUS
|
|
lwpmudrv_Allocate_Restore_Buffer (
|
|
VOID
|
|
)
|
|
{
|
|
int i = 0;
|
|
|
|
if (!restore_ha_direct2core) {
|
|
restore_ha_direct2core = CONTROL_Allocate_Memory(GLOBAL_STATE_num_cpus(driver_state) * sizeof(U32 *));
|
|
if (!restore_ha_direct2core) {
|
|
return OS_NO_MEM;
|
|
}
|
|
for (i = 0; i < GLOBAL_STATE_num_cpus(driver_state); i++) {
|
|
restore_ha_direct2core[i] = CONTROL_Allocate_Memory(MAX_BUSNO * sizeof(U32));
|
|
}
|
|
}
|
|
if (!restore_qpi_direct2core) {
|
|
restore_qpi_direct2core = CONTROL_Allocate_Memory(GLOBAL_STATE_num_cpus(driver_state) * sizeof(U32 *));
|
|
if (!restore_qpi_direct2core) {
|
|
return OS_NO_MEM;
|
|
}
|
|
for (i = 0; i < GLOBAL_STATE_num_cpus(driver_state); i++) {
|
|
restore_qpi_direct2core[i] = CONTROL_Allocate_Memory(2 * MAX_BUSNO * sizeof(U32));
|
|
}
|
|
}
|
|
if (!restore_bl_bypass) {
|
|
restore_bl_bypass = CONTROL_Allocate_Memory(GLOBAL_STATE_num_cpus(driver_state) * sizeof(U64));
|
|
if (!restore_bl_bypass) {
|
|
return OS_NO_MEM;
|
|
}
|
|
}
|
|
return OS_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* @fn void lwpmudrv_Free_Restore_Buffer
|
|
*
|
|
* @param
|
|
*
|
|
* @return OS_STATUE
|
|
*
|
|
* @brief allocate buffer space to save/restore the data (for JKT, QPILL and HA register) before collection
|
|
*/
|
|
static OS_STATUS
|
|
lwpmudrv_Free_Restore_Buffer (
|
|
VOID
|
|
)
|
|
{
|
|
U32 i = 0;
|
|
if (restore_ha_direct2core) {
|
|
for (i = 0; i < GLOBAL_STATE_num_cpus(driver_state); i++) {
|
|
restore_ha_direct2core[i]= CONTROL_Free_Memory(restore_ha_direct2core[i]);
|
|
}
|
|
restore_ha_direct2core = CONTROL_Free_Memory(restore_ha_direct2core);
|
|
}
|
|
if (restore_qpi_direct2core) {
|
|
for (i = 0; i < GLOBAL_STATE_num_cpus(driver_state); i++) {
|
|
restore_qpi_direct2core[i]= CONTROL_Free_Memory(restore_qpi_direct2core[i]);
|
|
}
|
|
restore_qpi_direct2core = CONTROL_Free_Memory(restore_qpi_direct2core);
|
|
}
|
|
if (restore_bl_bypass) {
|
|
restore_bl_bypass = CONTROL_Free_Memory(restore_bl_bypass);
|
|
}
|
|
return OS_SUCCESS;
|
|
}
|
|
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/*!
|
|
* @fn static OS_STATUS lwpmudrv_Initialize_State(void)
|
|
*
|
|
* @param none
|
|
*
|
|
* @return OS_STATUS
|
|
*
|
|
* @brief Allocates the memory needed at load time. Initializes all the
|
|
* @brief necessary state variables with the default values.
|
|
*
|
|
* <I>Special Notes</I>
|
|
*/
|
|
static OS_STATUS
|
|
lwpmudrv_Initialize_State (
|
|
VOID
|
|
)
|
|
{
|
|
/*
|
|
* Machine Initializations
|
|
* Abstract this information away into a separate entry point
|
|
*
|
|
* Question: Should we allow for the use of Hot-cpu
|
|
* add/subtract functionality while the driver is executing?
|
|
*/
|
|
GLOBAL_STATE_num_cpus(driver_state) = num_online_cpus();
|
|
GLOBAL_STATE_active_cpus(driver_state) = num_online_cpus();
|
|
GLOBAL_STATE_cpu_count(driver_state) = 0;
|
|
GLOBAL_STATE_dpc_count(driver_state) = 0;
|
|
GLOBAL_STATE_num_em_groups(driver_state) = 0;
|
|
GLOBAL_STATE_current_phase(driver_state) = DRV_STATE_UNINITIALIZED;
|
|
|
|
SEP_PRINT_DEBUG("lwpmudrv_Initialize_State: num_cpus=%d, active_cpus=%d\n",
|
|
GLOBAL_STATE_num_cpus(driver_state),
|
|
GLOBAL_STATE_active_cpus(driver_state));
|
|
|
|
return OS_SUCCESS;
|
|
}
|
|
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/*!
|
|
* @fn static void lwpmudrv_Fill_TSC_Info (PVOID param)
|
|
*
|
|
* @param param - pointer the buffer to fill in.
|
|
*
|
|
* @return none
|
|
*
|
|
* @brief Read the TSC and write into the correct array slot.
|
|
*
|
|
* <I>Special Notes</I>
|
|
*/
|
|
atomic_t read_now;
|
|
static wait_queue_head_t read_tsc_now;
|
|
static VOID
|
|
lwpmudrv_Fill_TSC_Info (
|
|
PVOID param
|
|
)
|
|
{
|
|
U32 this_cpu;
|
|
|
|
preempt_disable();
|
|
this_cpu = CONTROL_THIS_CPU();
|
|
preempt_enable();
|
|
//
|
|
// Wait until all CPU's are ready to proceed
|
|
// This will serve as a synchronization point to compute tsc skews.
|
|
//
|
|
|
|
if (atomic_read(&read_now) >= 1) {
|
|
if (atomic_dec_and_test(&read_now) == FALSE) {
|
|
wait_event_interruptible(read_tsc_now, (atomic_read(&read_now) >= 1));
|
|
}
|
|
}
|
|
else {
|
|
wake_up_interruptible_all(&read_tsc_now);
|
|
}
|
|
UTILITY_Read_TSC(&tsc_info[this_cpu]);
|
|
SEP_PRINT_DEBUG("lwpmudrv_Fill_TSC_Info: this cpu %d --- tsc --- 0x%llx\n",
|
|
this_cpu, tsc_info[this_cpu]);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*********************************************************************
|
|
* Internal Driver functions
|
|
* Should be called only from the lwpmudrv_DeviceControl routine
|
|
*********************************************************************/
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/*!
|
|
* @fn static void lwpmudrv_Dump_Tracer(const char *)
|
|
*
|
|
* @param Name of the tracer
|
|
*
|
|
* @return void
|
|
*
|
|
* @brief Function that handles the generation of markers into the ftrace stream
|
|
*
|
|
* <I>Special Notes</I>
|
|
*/
|
|
static void
|
|
lwpmudrv_Dump_Tracer (
|
|
const char *name,
|
|
U64 tsc
|
|
)
|
|
{
|
|
#if defined(CONFIG_TRACING) && defined(CONFIG_TRACEPOINTS)
|
|
if (tsc == 0) {
|
|
preempt_disable();
|
|
UTILITY_Read_TSC(&tsc);
|
|
tsc -= TSC_SKEW(CONTROL_THIS_CPU());
|
|
preempt_enable();
|
|
}
|
|
trace_lwpmudrv_marker(name, tsc);
|
|
#endif
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/*!
|
|
* @fn static OS_STATUS lwpmudrv_Version(IOCTL_ARGS arg)
|
|
*
|
|
* @param arg - pointer to the IOCTL_ARGS structure
|
|
*
|
|
* @return OS_STATUS
|
|
*
|
|
* @brief Local function that handles the LWPMU_IOCTL_VERSION call.
|
|
* @brief Returns the version number of the kernel mode sampling.
|
|
*
|
|
* <I>Special Notes</I>
|
|
*/
|
|
static OS_STATUS
|
|
lwpmudrv_Version (
|
|
IOCTL_ARGS arg
|
|
)
|
|
{
|
|
OS_STATUS status;
|
|
|
|
// Check if enough space is provided for collecting the data
|
|
if ((arg->r_len != sizeof(U32)) || (arg->r_buf == NULL)) {
|
|
return OS_FAULT;
|
|
}
|
|
|
|
status = put_user(SEP_VERSION_NODE_sep_version(&drv_version), (U32 *)arg->r_buf);
|
|
|
|
return status;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/*!
|
|
* @fn static OS_STATUS lwpmudrv_Reserve(IOCTL_ARGS arg)
|
|
*
|
|
* @param arg - pointer to the IOCTL_ARGS structure
|
|
*
|
|
* @return OS_STATUS
|
|
*
|
|
* @brief
|
|
* @brief Local function that handles the LWPMU_IOCTL_RESERVE call.
|
|
* @brief Sets the state to RESERVED if possible. Returns BUSY if unable
|
|
* @brief to reserve the PMU.
|
|
*
|
|
* <I>Special Notes</I>
|
|
*/
|
|
static OS_STATUS
|
|
lwpmudrv_Reserve (
|
|
IOCTL_ARGS arg
|
|
)
|
|
{
|
|
OS_STATUS status = OS_SUCCESS;
|
|
S32 prev_phase;
|
|
|
|
// Check if enough space is provided for collecting the data
|
|
if ((arg->r_len != sizeof(S32)) || (arg->r_buf == NULL)) {
|
|
return OS_FAULT;
|
|
}
|
|
|
|
prev_phase = cmpxchg(&GLOBAL_STATE_current_phase(driver_state),
|
|
DRV_STATE_UNINITIALIZED,
|
|
DRV_STATE_RESERVED);
|
|
|
|
SEP_PRINT_DEBUG("lwpmudrv_Reserve: states --- old_phase = %d; current_phase == %d\n",
|
|
prev_phase, GLOBAL_STATE_current_phase(driver_state));
|
|
|
|
status = put_user((prev_phase != DRV_STATE_UNINITIALIZED), (int*)arg->r_buf);
|
|
|
|
return status;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/*!
|
|
* @fn static VOID lwpmudrv_Clean_Up(DRV_BOOL)
|
|
*
|
|
* @param DRV_BOOL finish - Flag to call finish
|
|
*
|
|
* @return VOID
|
|
*
|
|
* @brief Cleans up the memory allocation.
|
|
*
|
|
* <I>Special Notes</I>
|
|
*/
|
|
static VOID
|
|
lwpmudrv_Clean_Up (
|
|
DRV_BOOL finish
|
|
)
|
|
{
|
|
U32 i;
|
|
|
|
if (DRV_CONFIG_use_pcl(pcfg) == TRUE) {
|
|
pcfg = CONTROL_Free_Memory(pcfg);
|
|
goto signal_end;
|
|
}
|
|
|
|
if (PMU_register_data) {
|
|
for (i = 0; i < GLOBAL_STATE_num_em_groups(driver_state); i++) {
|
|
CONTROL_Free_Memory(PMU_register_data[i]);
|
|
}
|
|
}
|
|
|
|
#if defined(DRV_IA32) || defined(DRV_EM64T)
|
|
if (devices) {
|
|
U32 id;
|
|
EVENT_CONFIG ec;
|
|
for (id = 0; id < num_devices; id++) {
|
|
if (LWPMU_DEVICE_PMU_register_data(&devices[id])) {
|
|
ec = LWPMU_DEVICE_ec(&devices[id]);
|
|
for (i = 0; i < EVENT_CONFIG_num_groups_unc(ec); i++) {
|
|
CONTROL_Free_Memory(LWPMU_DEVICE_PMU_register_data(&devices[id])[i]);
|
|
}
|
|
LWPMU_DEVICE_PMU_register_data(&devices[id]) = CONTROL_Free_Memory(LWPMU_DEVICE_PMU_register_data(&devices[id]));
|
|
}
|
|
LWPMU_DEVICE_pcfg(&devices[id]) = CONTROL_Free_Memory(LWPMU_DEVICE_pcfg(&devices[id]));
|
|
LWPMU_DEVICE_ec(&devices[id]) = CONTROL_Free_Memory(LWPMU_DEVICE_ec(&devices[id]));
|
|
|
|
if (LWPMU_DEVICE_acc_per_thread(&devices[id])) {
|
|
for (i = 0; i < GLOBAL_STATE_num_cpus(driver_state); i++) {
|
|
LWPMU_DEVICE_acc_per_thread(&devices[id])[i] = CONTROL_Free_Memory(LWPMU_DEVICE_acc_per_thread(&devices[id])[i]);
|
|
}
|
|
LWPMU_DEVICE_acc_per_thread(&devices[id]) = CONTROL_Free_Memory(LWPMU_DEVICE_acc_per_thread(&devices[id]));
|
|
}
|
|
if (LWPMU_DEVICE_prev_val_per_thread(&devices[id])) {
|
|
for (i = 0; i < GLOBAL_STATE_num_cpus(driver_state); i++) {
|
|
LWPMU_DEVICE_prev_val_per_thread(&devices[id])[i] = CONTROL_Free_Memory(LWPMU_DEVICE_prev_val_per_thread(&devices[id])[i]);
|
|
}
|
|
LWPMU_DEVICE_prev_val_per_thread(&devices[id]) = CONTROL_Free_Memory(LWPMU_DEVICE_prev_val_per_thread(&devices[id]));
|
|
}
|
|
}
|
|
devices = CONTROL_Free_Memory(devices);
|
|
}
|
|
#endif
|
|
|
|
if (desc_data) {
|
|
for (i = 0; i < GLOBAL_STATE_num_descriptors(driver_state); i++) {
|
|
CONTROL_Free_Memory(desc_data[i]);
|
|
}
|
|
}
|
|
PMU_register_data = CONTROL_Free_Memory(PMU_register_data);
|
|
desc_data = CONTROL_Free_Memory(desc_data);
|
|
global_ec = CONTROL_Free_Memory(global_ec);
|
|
pcfg = CONTROL_Free_Memory(pcfg);
|
|
if (restore_bl_bypass) {
|
|
restore_bl_bypass = CONTROL_Free_Memory(restore_bl_bypass);
|
|
}
|
|
if (restore_qpi_direct2core) {
|
|
for (i = 0; i < GLOBAL_STATE_num_cpus(driver_state); i++) {
|
|
restore_qpi_direct2core[i]= CONTROL_Free_Memory(restore_qpi_direct2core[i]);
|
|
}
|
|
restore_qpi_direct2core = CONTROL_Free_Memory(restore_qpi_direct2core);
|
|
}
|
|
if (restore_ha_direct2core) {
|
|
for (i = 0; i < GLOBAL_STATE_num_cpus(driver_state); i++) {
|
|
restore_ha_direct2core[i]= CONTROL_Free_Memory(restore_ha_direct2core[i]);
|
|
}
|
|
restore_ha_direct2core = CONTROL_Free_Memory(restore_ha_direct2core);
|
|
}
|
|
|
|
#if defined(DRV_IA32) || defined(DRV_EM64T)
|
|
lbr = CONTROL_Free_Memory(lbr);
|
|
#else
|
|
ro = CONTROL_Free_Memory(ro);
|
|
#endif
|
|
if (finish) {
|
|
CONTROL_Invoke_Parallel(dispatch->fini, NULL); // must be done before pcb is freed
|
|
}
|
|
pcb = CONTROL_Free_Memory(pcb);
|
|
pcb_size = 0;
|
|
pmu_state = CONTROL_Free_Memory(pmu_state);
|
|
cpu_mask_bits = CONTROL_Free_Memory(cpu_mask_bits);
|
|
|
|
signal_end:
|
|
GLOBAL_STATE_num_em_groups(driver_state) = 0;
|
|
GLOBAL_STATE_num_descriptors(driver_state) = 0;
|
|
num_devices = 0;
|
|
abnormal_terminate = 0;
|
|
control_pid = 0;
|
|
|
|
return;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/*!
|
|
* @fn static OS_STATUS lwpmudrv_Initialize (PVOID in_buf, size_t in_buf_len)
|
|
*
|
|
* @param in_buf - pointer to the input buffer
|
|
* @param in_buf_len - size of the input buffer
|
|
*
|
|
* @return OS_STATUS
|
|
*
|
|
* @brief Local function that handles the LWPMU_IOCTL_INIT call.
|
|
* @brief Sets up the interrupt handler.
|
|
* @brief Set up the output buffers/files needed to make the driver
|
|
* @brief operational.
|
|
*
|
|
* <I>Special Notes</I>
|
|
*/
|
|
static OS_STATUS
|
|
lwpmudrv_Initialize (
|
|
PVOID in_buf,
|
|
size_t in_buf_len
|
|
)
|
|
{
|
|
U32 initialize;
|
|
S32 cpu_num;
|
|
int status = OS_SUCCESS;
|
|
|
|
SEP_PRINT_DEBUG("lwpmudrv_Initialize: Entered lwpmudrv_Initialize\n");
|
|
|
|
if (in_buf == NULL) {
|
|
return OS_FAULT;
|
|
}
|
|
|
|
initialize = cmpxchg(&GLOBAL_STATE_current_phase(driver_state),
|
|
DRV_STATE_RESERVED,
|
|
DRV_STATE_IDLE);
|
|
|
|
if (initialize != DRV_STATE_RESERVED) {
|
|
SEP_PRINT_ERROR("lwpmudrv_Initialize: Sampling is in progress, cannot start a new session.\n");
|
|
return OS_IN_PROGRESS;
|
|
}
|
|
|
|
/*
|
|
* Program State Initializations
|
|
*/
|
|
pcfg = CONTROL_Allocate_Memory(in_buf_len);
|
|
if (!pcfg) {
|
|
return OS_NO_MEM;
|
|
}
|
|
|
|
if (copy_from_user(pcfg, in_buf, in_buf_len)) {
|
|
return OS_FAULT;
|
|
}
|
|
|
|
if (DRV_CONFIG_enable_cp_mode(pcfg)) {
|
|
interrupt_counts = CONTROL_Allocate_Memory(GLOBAL_STATE_num_cpus(driver_state) *
|
|
DRV_CONFIG_num_events(pcfg) *
|
|
sizeof(U64));
|
|
SEP_PRINT("interrupt_counts allocated\n");
|
|
if (interrupt_counts == NULL) {
|
|
return OS_FAULT; // no memory?
|
|
}
|
|
}
|
|
|
|
if (DRV_CONFIG_use_pcl(pcfg) == TRUE) {
|
|
return OS_SUCCESS;
|
|
}
|
|
|
|
pcb_size = GLOBAL_STATE_num_cpus(driver_state)*sizeof(CPU_STATE_NODE);
|
|
pcb = CONTROL_Allocate_Memory(pcb_size);
|
|
if (!pcb) {
|
|
return OS_NO_MEM;
|
|
}
|
|
|
|
pmu_state = CONTROL_Allocate_KMemory(GLOBAL_STATE_num_cpus(driver_state)*sizeof(U64)*2);
|
|
if (!pmu_state) {
|
|
return OS_NO_MEM;
|
|
}
|
|
uncore_em_factor = 0;
|
|
for (cpu_num = 0; cpu_num < GLOBAL_STATE_num_cpus(driver_state); cpu_num++) {
|
|
CPU_STATE_accept_interrupt(&pcb[cpu_num]) = 1;
|
|
CPU_STATE_initial_mask(&pcb[cpu_num]) = 1;
|
|
CPU_STATE_group_swap(&pcb[cpu_num]) = 1;
|
|
CPU_STATE_reset_mask(&pcb[cpu_num]) = 0;
|
|
CPU_STATE_num_samples(&pcb[cpu_num]) = 0;
|
|
#if (defined(DRV_IA32) || defined(DRV_EM64T))
|
|
CPU_STATE_last_p_state_valid(&pcb[cpu_num]) = FALSE;
|
|
#endif
|
|
}
|
|
|
|
if (dispatch == NULL) {
|
|
dispatch = UTILITY_Configure_CPU(DRV_CONFIG_dispatch_id(pcfg));
|
|
}
|
|
if (dispatch == NULL) {
|
|
return OS_FAULT;
|
|
}
|
|
|
|
SEP_PRINT_DEBUG("Config : size = %d\n", DRV_CONFIG_size(pcfg));
|
|
SEP_PRINT_DEBUG("Config : counting_mode = %ld\n", DRV_CONFIG_counting_mode(pcfg));
|
|
#if defined(DRV_IA32) || defined(DRV_EM64T)
|
|
SEP_PRINT_DEBUG("Config : pebs_mode = %ld\n", DRV_CONFIG_pebs_mode(pcfg));
|
|
SEP_PRINT_DEBUG("Config : pebs_capture = %ld\n", DRV_CONFIG_pebs_capture(pcfg));
|
|
SEP_PRINT_DEBUG("Config : collect_lbrs = %ld\n", DRV_CONFIG_collect_lbrs(pcfg));
|
|
#else
|
|
SEP_PRINT_DEBUG("Config : collect_ro = %ld\n", DRV_CONFIG_collect_ro(pcfg));
|
|
#endif
|
|
SEP_PRINT_DEBUG("Config : seed_name = %s\n", DRV_CONFIG_seed_name(pcfg));
|
|
|
|
control_pid = current->pid;
|
|
SEP_PRINT_DEBUG("Control PID = %d\n", control_pid);
|
|
|
|
if (DRV_CONFIG_counting_mode(pcfg) == FALSE) {
|
|
if (cpu_buf == NULL) {
|
|
cpu_buf = CONTROL_Allocate_Memory(GLOBAL_STATE_num_cpus(driver_state)*sizeof(BUFFER_DESC_NODE));
|
|
if (!cpu_buf) {
|
|
return OS_NO_MEM;
|
|
}
|
|
}
|
|
/*
|
|
* Allocate the output and control buffers for each CPU in the system
|
|
* Allocate and set up the temp output files for each CPU in the system
|
|
* Allocate and set up the temp outout file for detailing the Modules in the system
|
|
*/
|
|
status = OUTPUT_Initialize(DRV_CONFIG_seed_name(pcfg), DRV_CONFIG_seed_name_len(pcfg));
|
|
if (status != OS_SUCCESS) {
|
|
GLOBAL_STATE_current_phase(driver_state) = DRV_STATE_UNINITIALIZED;
|
|
lwpmudrv_Clean_Up(FALSE);
|
|
return status;
|
|
}
|
|
|
|
#if defined(DRV_USE_NMI)
|
|
status = OUTPUT_Initialize_Timers();
|
|
if (status != OS_SUCCESS) {
|
|
GLOBAL_STATE_current_phase(driver_state) = DRV_STATE_UNINITIALIZED;
|
|
lwpmudrv_Clean_Up(FALSE);
|
|
return status;
|
|
}
|
|
#endif
|
|
SEP_PRINT_DEBUG("lwpmudrv_Initialize: After OUTPUT_Initialize\n");
|
|
|
|
/*
|
|
* Program the APIC and set up the interrupt handler
|
|
*/
|
|
CPUMON_Install_Cpuhooks();
|
|
SEP_PRINT_DEBUG("lwpmudrv_Initialize: Finished Installing cpu hooks\n");
|
|
/*
|
|
* Set up the call back routines based on architecture.
|
|
*/
|
|
#if defined(DRV_IA32) || defined(DRV_EM64T)
|
|
PEBS_Initialize(pcfg);
|
|
#endif
|
|
|
|
#if defined(DRV_EM64T)
|
|
SYS_Get_GDT_Base((PVOID*)&gdt_desc);
|
|
#endif
|
|
SEP_PRINT_DEBUG("lwpmudrv_Initialize: about to install module notification");
|
|
LINUXOS_Install_Hooks();
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
#if defined(DRV_IA32) || defined(DRV_EM64T)
|
|
/* ------------------------------------------------------------------------- */
|
|
/*!
|
|
* @fn static OS_STATUS lwpmudrv_Initialize_Num_Devices(IOCTL_ARGS arg)
|
|
*
|
|
* @param arg - pointer to the IOCTL_ARGS structure
|
|
*
|
|
* @return OS_STATUS
|
|
*
|
|
* @brief
|
|
* @brief Local function that handles the LWPMU_IOCTL_INIT_NUM_DEV call.
|
|
* @brief Init # uncore devices.
|
|
*
|
|
* <I>Special Notes</I>
|
|
*/
|
|
static OS_STATUS
|
|
lwpmudrv_Initialize_Num_Devices (
|
|
IOCTL_ARGS arg
|
|
)
|
|
{
|
|
// Check if enough space is provided for collecting the data
|
|
if ((arg->w_len != sizeof(U32)) || (arg->w_buf == NULL)) {
|
|
return OS_FAULT;
|
|
}
|
|
|
|
if (copy_from_user(&num_devices, arg->w_buf, arg->w_len)) {
|
|
return OS_FAULT;
|
|
}
|
|
/*
|
|
* Allocate memory for number of devices
|
|
*/
|
|
if (num_devices != 0) {
|
|
devices = CONTROL_Allocate_Memory(num_devices * sizeof(LWPMU_DEVICE_NODE));
|
|
}
|
|
cur_device = 0;
|
|
|
|
SEP_PRINT_DEBUG("lwpmudrv_Initialize_Num_Devices: num_devices=%d, devices=0x%p\n", num_devices, devices);
|
|
|
|
return OS_SUCCESS;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/*!
|
|
* @fn static OS_STATUS lwpmudrv_Initialize_UNC(PVOID in_buf, U32 in_buf_len)
|
|
*
|
|
* @param in_buf - pointer to the input buffer
|
|
* @param in_buf_len - size of the input buffer
|
|
*
|
|
* @return OS_STATUS
|
|
*
|
|
* @brief Local function that handles the LWPMU_IOCTL_INIT call.
|
|
* @brief Sets up the interrupt handler.
|
|
* @brief Set up the output buffers/files needed to make the driver
|
|
* @brief operational.
|
|
*
|
|
* <I>Special Notes</I>
|
|
*/
|
|
static OS_STATUS
|
|
lwpmudrv_Initialize_UNC (
|
|
PVOID in_buf,
|
|
U32 in_buf_len
|
|
)
|
|
{
|
|
DRV_CONFIG pcfg;
|
|
|
|
SEP_PRINT_DEBUG("Entered lwpmudrv_Initialize_UNC\n");
|
|
|
|
if (GLOBAL_STATE_current_phase(driver_state) != DRV_STATE_IDLE) {
|
|
return OS_IN_PROGRESS;
|
|
}
|
|
/*
|
|
* Program State Initializations:
|
|
* Foreach device, copy over pcfg and configure dispatch table
|
|
*/
|
|
if (cur_device >= num_devices) {
|
|
SEP_PRINT_ERROR("No more devices to allocate! Initial call to lwpmudrv_Init_Num_Devices incorrect.");
|
|
return OS_FAULT;
|
|
}
|
|
if (in_buf == NULL) {
|
|
return OS_FAULT;
|
|
}
|
|
if (in_buf_len != sizeof(DRV_CONFIG_NODE)) {
|
|
SEP_PRINT_ERROR("Got in_buf_len=%d, expecting size=%d\n", in_buf_len, (int)sizeof(DRV_CONFIG_NODE));
|
|
return OS_FAULT;
|
|
}
|
|
// allocate memory
|
|
LWPMU_DEVICE_pcfg(&devices[cur_device]) = CONTROL_Allocate_Memory(sizeof(DRV_CONFIG_NODE));
|
|
// copy over pcfg
|
|
if (copy_from_user(LWPMU_DEVICE_pcfg(&devices[cur_device]), in_buf, in_buf_len)) {
|
|
SEP_PRINT_ERROR("Failed to copy from user");
|
|
return OS_FAULT;
|
|
}
|
|
// configure dispatch from dispatch_id
|
|
pcfg = (DRV_CONFIG)LWPMU_DEVICE_pcfg(&devices[cur_device]);
|
|
LWPMU_DEVICE_dispatch(&devices[cur_device]) = UTILITY_Configure_CPU(DRV_CONFIG_dispatch_id(pcfg));
|
|
if (LWPMU_DEVICE_dispatch(&devices[cur_device]) == NULL) {
|
|
SEP_PRINT_ERROR("Unable to configure CPU");
|
|
return OS_FAULT;
|
|
}
|
|
|
|
LWPMU_DEVICE_em_groups_count(&devices[cur_device]) = 0;
|
|
LWPMU_DEVICE_num_units(&devices[cur_device]) = 0;
|
|
LWPMU_DEVICE_cur_group(&devices[cur_device]) = 0;
|
|
|
|
SEP_PRINT_DEBUG("LWP: ebc unc = %d\n",DRV_CONFIG_event_based_counts(pcfg));
|
|
SEP_PRINT_DEBUG("LWP Config : unc dispatch id = %d\n", DRV_CONFIG_dispatch_id(pcfg));
|
|
|
|
return OS_SUCCESS;
|
|
}
|
|
#endif
|
|
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/*!
|
|
* @fn static OS_STATUS lwpmudrv_Terminate(void)
|
|
*
|
|
* @param none
|
|
*
|
|
* @return OS_STATUS
|
|
*
|
|
* @brief Local function that handles the LWPMUDRV_IOCTL_TERMINATE call.
|
|
* @brief Cleans up the interrupt handler and resets the PMU state.
|
|
*
|
|
* <I>Special Notes</I>
|
|
*/
|
|
static OS_STATUS
|
|
lwpmudrv_Terminate (
|
|
VOID
|
|
)
|
|
{
|
|
U32 previous_state;
|
|
|
|
if (GLOBAL_STATE_current_phase(driver_state) == DRV_STATE_UNINITIALIZED) {
|
|
return OS_SUCCESS;
|
|
}
|
|
|
|
previous_state = cmpxchg(&GLOBAL_STATE_current_phase(driver_state),
|
|
DRV_STATE_STOPPED,
|
|
DRV_STATE_UNINITIALIZED);
|
|
if (previous_state != DRV_STATE_STOPPED) {
|
|
SEP_PRINT_ERROR("lwpmudrv_Terminate: Sampling is in progress, cannot terminate.\n");
|
|
return OS_IN_PROGRESS;
|
|
}
|
|
|
|
GLOBAL_STATE_current_phase(driver_state) = DRV_STATE_UNINITIALIZED;
|
|
lwpmudrv_Clean_Up(TRUE);
|
|
|
|
return OS_SUCCESS;
|
|
}
|
|
|
|
#ifdef EMON
|
|
/* ------------------------------------------------------------------------- */
|
|
/*!
|
|
* @fn static void lwpmudrv_Switch_To_Next_Group(param)
|
|
*
|
|
* @param param - unused dummy
|
|
*
|
|
* @return none
|
|
*
|
|
* @brief Switch to the next event group.
|
|
* @brief This function assumes an active collection is frozen
|
|
* @brief or no collection is active.
|
|
*
|
|
* <I>Special Notes</I>
|
|
*/
|
|
static VOID
|
|
lwpmudrv_Switch_To_Next_Group (
|
|
PVOID param
|
|
)
|
|
{
|
|
U32 this_cpu;
|
|
CPU_STATE pcpu;
|
|
|
|
preempt_disable();
|
|
this_cpu = CONTROL_THIS_CPU();
|
|
pcpu = &pcb[this_cpu];
|
|
preempt_enable();
|
|
|
|
CPU_STATE_current_group(pcpu)++;
|
|
// make the event group list circular
|
|
CPU_STATE_current_group(pcpu) %= EVENT_CONFIG_num_groups(global_ec);
|
|
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/*!
|
|
* @fn static OS_STATUS lwmpudrv_Get_Driver_State(IOCTL_ARGS arg)
|
|
*
|
|
* @param arg - pointer to the IOCTL_ARGS structure
|
|
*
|
|
* @return OS_STATUS
|
|
*
|
|
* @brief Local function that handles the LWPMU_IOCTL_GET_Driver_State call.
|
|
* @brief Returns the current driver state.
|
|
*
|
|
* <I>Special Notes</I>
|
|
*/
|
|
static OS_STATUS
|
|
lwpmudrv_Get_Driver_State (
|
|
IOCTL_ARGS arg
|
|
)
|
|
{
|
|
OS_STATUS status = OS_SUCCESS;
|
|
|
|
// Check if enough space is provided for collecting the data
|
|
if ((arg->r_len != sizeof(U32)) || (arg->r_buf == NULL)) {
|
|
return OS_FAULT;
|
|
}
|
|
|
|
status = put_user(GLOBAL_STATE_current_phase(driver_state), (U32*)arg->r_buf);
|
|
|
|
return status;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/*!
|
|
* @fn static OS_STATUS lwpmudrv_Pause(void)
|
|
*
|
|
* @param - none
|
|
*
|
|
* @return OS_STATUS
|
|
*
|
|
* @brief Pause the collection
|
|
*
|
|
* <I>Special Notes</I>
|
|
*/
|
|
static OS_STATUS
|
|
lwpmudrv_Pause (
|
|
VOID
|
|
)
|
|
{
|
|
U32 previous_state;
|
|
int i;
|
|
int done = FALSE;
|
|
#if defined(DRV_IA32) || defined(DRV_EM64T)
|
|
DRV_CONFIG pcfg_unc = NULL;
|
|
DISPATCH dispatch_unc = NULL;
|
|
U32 j;
|
|
#endif
|
|
|
|
previous_state = cmpxchg(&GLOBAL_STATE_current_phase(driver_state),
|
|
DRV_STATE_RUNNING,
|
|
DRV_STATE_PAUSING);
|
|
|
|
if (previous_state == DRV_STATE_RUNNING) {
|
|
if (DRV_CONFIG_use_pcl(pcfg) == FALSE) {
|
|
for (i = 0; i < GLOBAL_STATE_num_cpus(driver_state); i++) {
|
|
CPU_STATE_accept_interrupt(&pcb[i]) = 0;
|
|
}
|
|
while (!done) {
|
|
done = TRUE;
|
|
for (i = 0; i < GLOBAL_STATE_num_cpus(driver_state); i++) {
|
|
if (atomic_read(&CPU_STATE_in_interrupt(&pcb[i]))) {
|
|
done = FALSE;
|
|
}
|
|
}
|
|
}
|
|
CONTROL_Invoke_Parallel(dispatch->freeze, NULL);
|
|
}
|
|
/*
|
|
* This means that the PAUSE state has been reached.
|
|
*/
|
|
GLOBAL_STATE_current_phase(driver_state) = DRV_STATE_PAUSED;
|
|
#if defined(DRV_IA32) || defined(DRV_EM64T)
|
|
for (j = 0; j < num_devices; j++) {
|
|
pcfg_unc = (DRV_CONFIG)LWPMU_DEVICE_pcfg(&devices[j]);
|
|
dispatch_unc = LWPMU_DEVICE_dispatch(&devices[j]);
|
|
|
|
if (pcfg_unc &&
|
|
(DRV_CONFIG_event_based_counts(pcfg_unc) || DRV_CONFIG_emon_mode(pcfg_unc)) &&
|
|
dispatch_unc &&
|
|
dispatch_unc->freeze) {
|
|
SEP_PRINT_DEBUG("LWP: calling UNC Pause\n");
|
|
preempt_disable();
|
|
invoking_processor_id = CONTROL_THIS_CPU();
|
|
preempt_enable();
|
|
CONTROL_Invoke_Parallel(dispatch_unc->freeze, (VOID *)&j);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
return OS_SUCCESS;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/*!
|
|
* @fn static OS_STATUS lwpmudrv_Resume(void)
|
|
*
|
|
* @param - none
|
|
*
|
|
* @return OS_STATUS
|
|
*
|
|
* @brief Resume the collection
|
|
*
|
|
* <I>Special Notes</I>
|
|
*/
|
|
static OS_STATUS
|
|
lwpmudrv_Resume (
|
|
VOID
|
|
)
|
|
{
|
|
U32 previous_state;
|
|
int i;
|
|
#if defined(DRV_IA32) || defined(DRV_EM64T)
|
|
DRV_CONFIG pcfg_unc = NULL;
|
|
DISPATCH dispatch_unc = NULL;
|
|
U32 j;
|
|
#endif
|
|
|
|
/*
|
|
* If we are in the process of pausing sampling, wait until the pause has been
|
|
* completed. Then start the Resume process.
|
|
*/
|
|
do {
|
|
previous_state = cmpxchg(&GLOBAL_STATE_current_phase(driver_state),
|
|
DRV_STATE_PAUSED,
|
|
DRV_STATE_RUNNING);
|
|
/*
|
|
* This delay probably needs to be expanded a little bit more for large systems.
|
|
* For now, it is probably sufficient.
|
|
*/
|
|
SYS_IO_Delay();
|
|
SYS_IO_Delay();
|
|
} while (previous_state == DRV_STATE_PAUSING);
|
|
|
|
if (previous_state == DRV_STATE_PAUSED) {
|
|
if (cpu_mask_bits) {
|
|
for (i = 0; i < GLOBAL_STATE_num_cpus(driver_state); i++) {
|
|
CPU_STATE_accept_interrupt(&pcb[i]) = cpu_mask_bits[i] ? 1 : 0;
|
|
CPU_STATE_group_swap(&pcb[i]) = 1;
|
|
}
|
|
}
|
|
else {
|
|
for (i = 0; i < GLOBAL_STATE_num_cpus(driver_state); i++) {
|
|
CPU_STATE_accept_interrupt(&pcb[i]) = 1;
|
|
CPU_STATE_group_swap(&pcb[i]) = 1;
|
|
}
|
|
}
|
|
if (DRV_CONFIG_use_pcl(pcfg) == FALSE) {
|
|
CONTROL_Invoke_Parallel(dispatch->restart, (VOID *)(size_t)0);
|
|
}
|
|
#if defined(DRV_IA32) || defined(DRV_EM64T)
|
|
for (j = 0; j < num_devices; j++) {
|
|
pcfg_unc = (DRV_CONFIG)LWPMU_DEVICE_pcfg(&devices[j]);
|
|
dispatch_unc = LWPMU_DEVICE_dispatch(&devices[j]);
|
|
|
|
if (pcfg_unc &&
|
|
(DRV_CONFIG_event_based_counts(pcfg_unc) || DRV_CONFIG_emon_mode(pcfg_unc)) &&
|
|
dispatch_unc &&
|
|
dispatch_unc->restart) {
|
|
SEP_PRINT_DEBUG("LWP: calling UNC Resume\n");
|
|
preempt_disable();
|
|
invoking_processor_id = CONTROL_THIS_CPU();
|
|
preempt_enable();
|
|
CONTROL_Invoke_Parallel(dispatch_unc->restart, (VOID *)&j);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
return OS_SUCCESS;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/*!
|
|
* @fn static OS_STATUS lwpmudrv_Switch_Group(void)
|
|
*
|
|
* @param none
|
|
*
|
|
* @return OS_STATUS
|
|
*
|
|
* @brief Switch the current group that is being collected.
|
|
*
|
|
* <I>Special Notes</I>
|
|
* This routine is called from the user mode code to handle the multiple group
|
|
* situation. 4 distinct steps are taken:
|
|
* Step 1: Pause the sampling
|
|
* Step 2: Increment the current group count
|
|
* Step 3: Write the new group to the PMU
|
|
* Step 4: Resume sampling
|
|
*/
|
|
static OS_STATUS
|
|
lwpmudrv_Switch_Group (
|
|
VOID
|
|
)
|
|
{
|
|
S32 idx;
|
|
CPU_STATE pcpu;
|
|
OS_STATUS status = OS_SUCCESS;
|
|
U32 current_state = GLOBAL_STATE_current_phase(driver_state);
|
|
|
|
if (current_state != DRV_STATE_RUNNING &&
|
|
current_state != DRV_STATE_PAUSED) {
|
|
return status;
|
|
}
|
|
status = lwpmudrv_Pause();
|
|
for (idx = 0; idx < GLOBAL_STATE_num_cpus(driver_state); idx++) {
|
|
pcpu = &pcb[idx];
|
|
CPU_STATE_current_group(pcpu)++;
|
|
}
|
|
CONTROL_Invoke_Parallel(dispatch->write, (VOID *)0);
|
|
if (pcfg && (DRV_CONFIG_start_paused(pcfg) == FALSE)) {
|
|
lwpmudrv_Resume();
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/*!
|
|
* @fn static OS_STATUS lwpmudrv_Uncore_Switch_Group(void)
|
|
*
|
|
* @param none
|
|
*
|
|
* @return OS_STATUS
|
|
*
|
|
* @brief Switch the current group that is being collected.
|
|
*
|
|
* <I>Special Notes</I>
|
|
* This routine is called from the user mode code to handle the multiple group
|
|
* situation. 4 distinct steps are taken:
|
|
* Step 1: Pause the sampling
|
|
* Step 2: Increment the current group count
|
|
* Step 3: Write the new group to the PMU
|
|
* Step 4: Resume sampling
|
|
*/
|
|
static OS_STATUS
|
|
lwpmudrv_Uncore_Switch_Group (
|
|
VOID
|
|
)
|
|
{
|
|
OS_STATUS status = OS_SUCCESS;
|
|
U32 current_state = GLOBAL_STATE_current_phase(driver_state);
|
|
U32 i = 0;
|
|
DRV_CONFIG pcfg_unc;
|
|
DISPATCH dispatch_unc;
|
|
|
|
if (current_state != DRV_STATE_RUNNING &&
|
|
current_state != DRV_STATE_PAUSED) {
|
|
return status;
|
|
}
|
|
|
|
status = lwpmudrv_Pause();
|
|
|
|
#if defined(DRV_IA32) || defined(DRV_EM64T)
|
|
for(i = 0; i < num_devices; i++) {
|
|
pcfg_unc = LWPMU_DEVICE_pcfg(&devices[i]);
|
|
dispatch_unc = LWPMU_DEVICE_dispatch(&devices[i]);
|
|
if (pcfg_unc && dispatch_unc) {
|
|
LWPMU_DEVICE_cur_group(&devices[i])++;
|
|
LWPMU_DEVICE_cur_group(&devices[i]) %= LWPMU_DEVICE_em_groups_count(&devices[i]);
|
|
SEP_PRINT("lwpmudrv_Switch_Group - Swap Group to %d for device %d\n",LWPMU_DEVICE_cur_group(&devices[i]), i);
|
|
preempt_disable();
|
|
invoking_processor_id = CONTROL_THIS_CPU();
|
|
preempt_enable();
|
|
CONTROL_Invoke_Parallel(dispatch_unc->write, (VOID*)&i);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (pcfg && (DRV_CONFIG_start_paused(pcfg) == FALSE)) {
|
|
lwpmudrv_Resume();
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/*!
|
|
* @fn static OS_STATUS lwpmudrv_Trigger_Read(void)
|
|
*
|
|
* @param - none
|
|
*
|
|
* @return - OS_STATUS
|
|
*
|
|
* @brief Read the Counter Data.
|
|
*
|
|
* <I>Special Notes</I>
|
|
*/
|
|
static OS_STATUS
|
|
lwpmudrv_Trigger_Read (
|
|
VOID
|
|
)
|
|
{
|
|
#if defined(DRV_IA32) || defined(DRV_EM64T)
|
|
DRV_CONFIG pcfg_unc = NULL;
|
|
DISPATCH dispatch_unc = NULL;
|
|
U32 i;
|
|
|
|
if (cs_dispatch && cs_dispatch->Trigger_Read) {
|
|
cs_dispatch->Trigger_Read();
|
|
}
|
|
|
|
if (pcfg && DRV_CONFIG_use_pcl(pcfg) == TRUE) {
|
|
return OS_SUCCESS;
|
|
}
|
|
|
|
for (i = 0; i < num_devices; i++) {
|
|
pcfg_unc = (DRV_CONFIG)LWPMU_DEVICE_pcfg(&devices[i]);
|
|
dispatch_unc = LWPMU_DEVICE_dispatch(&devices[i]);
|
|
if (pcfg_unc && dispatch_unc && dispatch_unc->trigger_read ) {
|
|
dispatch_unc->trigger_read();
|
|
}
|
|
|
|
if (LWPMU_DEVICE_em_groups_count(&devices[i]) > 1) {
|
|
uncore_em_factor++;
|
|
if (uncore_em_factor == UNCORE_EM_GROUP_SWAP_FACTOR) {
|
|
lwpmudrv_Uncore_Switch_Group();
|
|
uncore_em_factor = 0;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return OS_SUCCESS;
|
|
}
|
|
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/*!
|
|
* @fn static OS_STATUS lwpmudrv_Init_PMU(void)
|
|
*
|
|
* @param - none
|
|
*
|
|
* @return - OS_STATUS
|
|
*
|
|
* @brief Initialize the PMU and the driver state in preparation for data collection.
|
|
*
|
|
* <I>Special Notes</I>
|
|
*/
|
|
static OS_STATUS
|
|
lwpmudrv_Init_PMU (
|
|
IOCTL_ARGS args
|
|
)
|
|
{
|
|
#if defined(DRV_IA32) || defined(DRV_EM64T)
|
|
DRV_CONFIG pcfg_unc = NULL;
|
|
DISPATCH dispatch_unc = NULL;
|
|
U32 i;
|
|
U32 emon_data_buffer_size = 0;
|
|
S32 max_groups_unc;
|
|
#endif
|
|
if (args->w_len == 0 || args->w_buf == NULL) {
|
|
return OS_NO_MEM;
|
|
}
|
|
|
|
emon_data_buffer_size = *(U32*)args->w_buf;
|
|
prev_counter_size = emon_data_buffer_size;
|
|
|
|
if (!pcfg) {
|
|
return OS_FAULT;
|
|
}
|
|
if (DRV_CONFIG_use_pcl(pcfg) == TRUE) {
|
|
return OS_SUCCESS;
|
|
}
|
|
|
|
if (GLOBAL_STATE_current_phase(driver_state) != DRV_STATE_IDLE) {
|
|
return OS_IN_PROGRESS;
|
|
}
|
|
|
|
if (GLOBAL_STATE_num_em_groups(driver_state) == 0) {
|
|
SEP_PRINT_ERROR("Number of em groups is not set.\n");
|
|
return OS_SUCCESS;
|
|
}
|
|
|
|
#if defined(DRV_IA32) || defined(DRV_EM64T)
|
|
// set cur_device's total groups to max groups of all devices
|
|
max_groups_unc = 0;
|
|
for (i = 0; i < num_devices; i++) {
|
|
if (max_groups_unc < LWPMU_DEVICE_em_groups_count(&devices[i])) {
|
|
max_groups_unc = LWPMU_DEVICE_em_groups_count(&devices[i]);
|
|
}
|
|
}
|
|
// now go back and up total groups for all devices
|
|
for (i = 0; i < num_devices; i++) {
|
|
if (LWPMU_DEVICE_em_groups_count(&devices[i]) < max_groups_unc) {
|
|
LWPMU_DEVICE_em_groups_count(&devices[i]) = max_groups_unc;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// allocate save/restore space before program the PMU
|
|
lwpmudrv_Allocate_Restore_Buffer();
|
|
|
|
// must be done after pcb is created and before PMU is first written to
|
|
CONTROL_Invoke_Parallel(dispatch->init, NULL);
|
|
|
|
//
|
|
// Transfer the data into the PMU registers
|
|
//
|
|
CONTROL_Invoke_Parallel(dispatch->write, (VOID *)(size_t)0);
|
|
|
|
#if defined(DRV_IA32) || defined(DRV_EM64T)
|
|
for (i = 0; i < num_devices; i++) {
|
|
pcfg_unc = (DRV_CONFIG)LWPMU_DEVICE_pcfg(&devices[i]);
|
|
dispatch_unc = LWPMU_DEVICE_dispatch(&devices[i]);
|
|
if (pcfg_unc && dispatch_unc && dispatch_unc->write ) {
|
|
SEP_PRINT_DEBUG("LWP: calling UNC Init\n");
|
|
preempt_disable();
|
|
invoking_processor_id = CONTROL_THIS_CPU();
|
|
preempt_enable();
|
|
CONTROL_Invoke_Parallel(dispatch_unc->write, (VOID *)&i);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
SEP_PRINT_DEBUG("lwpmudrv_Init_PMU: IOCTL_Init_PMU - finished initial Write\n");
|
|
if (DRV_CONFIG_counting_mode(pcfg) == TRUE) {
|
|
if (!read_counter_info) {
|
|
read_counter_info = CONTROL_Allocate_Memory(emon_data_buffer_size);
|
|
if (!read_counter_info) {
|
|
return OS_NO_MEM;
|
|
}
|
|
}
|
|
if (!read_unc_ctr_info) {
|
|
read_unc_ctr_info = CONTROL_Allocate_Memory(emon_data_buffer_size);
|
|
if (!read_unc_ctr_info) {
|
|
return OS_NO_MEM;
|
|
}
|
|
}
|
|
if (!prev_counter_data) {
|
|
prev_counter_data = CONTROL_Allocate_Memory(emon_data_buffer_size);
|
|
if (!prev_counter_data) {
|
|
return OS_NO_MEM;
|
|
}
|
|
}
|
|
// read the data registers
|
|
// yes, the counters are not enabled - we don't care
|
|
// we are capturing the read-only counter counts so we can get a diff when
|
|
// we collect the actual counts - other counts are also collected but are never used
|
|
CONTROL_Invoke_Parallel(dispatch->read_data, (VOID *)(size_t)0);
|
|
for (i = 0; i < num_devices; i++) {
|
|
pcfg_unc = (DRV_CONFIG)LWPMU_DEVICE_pcfg(&devices[i]);
|
|
dispatch_unc = LWPMU_DEVICE_dispatch(&devices[i]);
|
|
if (pcfg_unc && dispatch_unc && dispatch_unc->read_data && DRV_CONFIG_emon_mode(pcfg_unc)) {
|
|
preempt_disable();
|
|
invoking_processor_id = CONTROL_THIS_CPU();
|
|
preempt_enable();
|
|
CONTROL_Invoke_Parallel(dispatch_unc->read_data, (VOID*)&i);
|
|
}
|
|
}
|
|
memcpy(prev_counter_data, read_unc_ctr_info, emon_data_buffer_size);
|
|
}
|
|
|
|
return OS_SUCCESS;
|
|
}
|
|
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/*!
|
|
* @fn static void lwpmudrv_Read_MSR(pvoid param)
|
|
*
|
|
* @param param - pointer to the buffer to store the MSR counts
|
|
*
|
|
* @return none
|
|
*
|
|
* @brief
|
|
* @brief Read the U64 value at address in in_buf and
|
|
* @brief write the result into out_buf.
|
|
*
|
|
* <I>Special Notes</I>
|
|
*/
|
|
static VOID
|
|
lwpmudrv_Read_MSR (
|
|
PVOID param
|
|
)
|
|
{
|
|
#if defined(DRV_IA32) || defined(DRV_EM64T)
|
|
U32 this_cpu;
|
|
MSR_DATA this_node;
|
|
S64 reg_num;
|
|
|
|
preempt_disable();
|
|
this_cpu = CONTROL_THIS_CPU();
|
|
this_node = &msr_data[this_cpu];
|
|
reg_num = MSR_DATA_addr(this_node);
|
|
|
|
if (reg_num == 0) {
|
|
preempt_enable();
|
|
return;
|
|
}
|
|
|
|
MSR_DATA_value(this_node) = (U64)SYS_Read_MSR((U32)MSR_DATA_addr(this_node));
|
|
preempt_enable();
|
|
#endif
|
|
|
|
return;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/*!
|
|
* @fn static OS_STATUS lwpmudrv_Read_MSR_All_Cores(IOCTL_ARGS arg)
|
|
*
|
|
* @param arg - pointer to the IOCTL_ARGS structure
|
|
*
|
|
* @return OS_STATUS
|
|
*
|
|
* @brief Read the U64 value at address into in_buf and write
|
|
* @brief the result into out_buf.
|
|
* @brief Returns OS_SUCCESS if the read across all cores succeed,
|
|
* @brief otherwise OS_FAULT.
|
|
*
|
|
* <I>Special Notes</I>
|
|
*/
|
|
static OS_STATUS
|
|
lwpmudrv_Read_MSR_All_Cores (
|
|
IOCTL_ARGS arg
|
|
)
|
|
{
|
|
U64 *val;
|
|
S32 *reg_num;
|
|
S32 i;
|
|
MSR_DATA node;
|
|
|
|
if ((arg->r_len != sizeof(U32)) || (arg->r_buf == NULL) || (arg->w_buf == NULL)) {
|
|
return OS_FAULT;
|
|
}
|
|
|
|
val = (U64 *)arg->w_buf;
|
|
reg_num = (U32 *)arg->r_buf;
|
|
|
|
if (val == NULL) {
|
|
SEP_PRINT_ERROR("NULL out_buf\n");
|
|
return OS_SUCCESS;
|
|
}
|
|
|
|
msr_data = CONTROL_Allocate_Memory(GLOBAL_STATE_num_cpus(driver_state)*sizeof(MSR_DATA_NODE));
|
|
if (!msr_data) {
|
|
return OS_NO_MEM;
|
|
}
|
|
|
|
for (i = 0; i < GLOBAL_STATE_num_cpus(driver_state); i++) {
|
|
node = &msr_data[i];
|
|
MSR_DATA_addr(node) = *reg_num;
|
|
}
|
|
|
|
CONTROL_Invoke_Parallel(lwpmudrv_Read_MSR, (VOID *)(size_t)0);
|
|
|
|
/* copy values to arg array? */
|
|
if (arg->w_len < GLOBAL_STATE_num_cpus(driver_state)) {
|
|
SEP_PRINT_ERROR("Not enough memory allocated in output buffer.\n");
|
|
msr_data = CONTROL_Free_Memory(msr_data);
|
|
return OS_SUCCESS;
|
|
}
|
|
for (i = 0; i < GLOBAL_STATE_num_cpus(driver_state); i++) {
|
|
node = &msr_data[i];
|
|
if (copy_to_user(&val[i], (U64*)&MSR_DATA_value(node), sizeof(U64))) {
|
|
return OS_FAULT;
|
|
}
|
|
}
|
|
|
|
msr_data = CONTROL_Free_Memory(msr_data);
|
|
|
|
return OS_SUCCESS;
|
|
}
|
|
|
|
#ifdef EMON
|
|
#endif
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/*!
|
|
* @fn static void lwpmudrv_Read_MSRs_Uncore(PVOID param)
|
|
*
|
|
* @param param - dummy
|
|
*
|
|
* @return void
|
|
*
|
|
* @brief Read all the uncore data counters at one shot
|
|
*
|
|
* <I>Special Notes</I>
|
|
*/
|
|
static void
|
|
lwpmudrv_Read_MSRs_Uncore (
|
|
VOID* param
|
|
)
|
|
{
|
|
DISPATCH dispatch_unc;
|
|
U32 dev_idx;
|
|
DRV_CONFIG pcfg_unc;
|
|
|
|
if (devices == NULL) {
|
|
return;
|
|
}
|
|
for (dev_idx = 0; dev_idx < num_devices; dev_idx++) {
|
|
pcfg_unc = (DRV_CONFIG)LWPMU_DEVICE_pcfg(&devices[dev_idx]);
|
|
if (pcfg_unc == NULL) {
|
|
continue;
|
|
}
|
|
if (!DRV_CONFIG_emon_mode(pcfg_unc)) {
|
|
continue;
|
|
}
|
|
dispatch_unc = LWPMU_DEVICE_dispatch(&devices[dev_idx]);
|
|
if (dispatch_unc == NULL) {
|
|
continue;
|
|
}
|
|
if (dispatch_unc->read_data == NULL) {
|
|
continue;
|
|
}
|
|
dispatch_unc->read_data((VOID*)&dev_idx);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/*!
|
|
* @fn static OS_STATUS lwpmudrv_Read_MSRs(IOCTL_ARG arg)
|
|
*
|
|
* @param arg - pointer to the IOCTL_ARGS structure
|
|
*
|
|
* @return OS_STATUS
|
|
*
|
|
* @brief Read all the programmed data counters and accumulate them
|
|
* @brief into a single buffer.
|
|
*
|
|
* <I>Special Notes</I>
|
|
*/
|
|
static OS_STATUS
|
|
lwpmudrv_Read_MSRs (
|
|
IOCTL_ARGS arg
|
|
)
|
|
{
|
|
OS_STATUS status = OS_SUCCESS;
|
|
#if defined(DRV_IA32) || defined(DRV_EM64T)
|
|
DISPATCH dispatch_unc;
|
|
U32 start_index, j, l;
|
|
U32 dev_idx;
|
|
U32 pkg_idx;
|
|
DRV_CONFIG pcfg_unc;
|
|
U32 evt_index = 0;
|
|
S32 prev_ei = -1;
|
|
S32 cur_ei = 0;
|
|
U32 num_inst_idx = 0;
|
|
U32 num_units = 0;
|
|
U32 cur_grp = 0;
|
|
U32 package_event_count = 0;
|
|
U32 module_event_count = 0;
|
|
U32 thread_event_count = 0;
|
|
U32 num_cpus = GLOBAL_STATE_num_cpus(driver_state);
|
|
#endif
|
|
if (arg->r_len == 0 || arg->r_buf == NULL ) {
|
|
return status;
|
|
}
|
|
//
|
|
// Transfer the data in the PMU registers to the output buffer
|
|
//
|
|
if (!read_counter_info) {
|
|
read_counter_info = CONTROL_Allocate_Memory(arg->r_len);
|
|
if (!read_counter_info) {
|
|
return OS_NO_MEM;
|
|
}
|
|
}
|
|
if (!read_unc_ctr_info) {
|
|
read_unc_ctr_info = CONTROL_Allocate_Memory(arg->r_len);
|
|
if (!read_unc_ctr_info) {
|
|
return OS_NO_MEM;
|
|
}
|
|
}
|
|
if (!prev_counter_data) {
|
|
prev_counter_data = CONTROL_Allocate_Memory(arg->r_len);
|
|
if (!prev_counter_data) {
|
|
return OS_NO_MEM;
|
|
}
|
|
}
|
|
|
|
CONTROL_Invoke_Parallel(dispatch->read_data, (VOID *)(size_t)0);
|
|
|
|
#if defined(DRV_IA32) || defined(DRV_EM64T)
|
|
if (devices == NULL) {
|
|
return status;
|
|
}
|
|
CONTROL_Invoke_Parallel(lwpmudrv_Read_MSRs_Uncore, (VOID *)(size_t)0);
|
|
|
|
for (dev_idx = 0; dev_idx < num_devices; dev_idx++) {
|
|
pcfg_unc = (DRV_CONFIG)LWPMU_DEVICE_pcfg(&devices[dev_idx]);
|
|
if (pcfg_unc == NULL) {
|
|
continue;
|
|
}
|
|
if (!DRV_CONFIG_emon_mode(pcfg_unc)) {
|
|
continue;
|
|
}
|
|
dispatch_unc = LWPMU_DEVICE_dispatch(&devices[dev_idx]);
|
|
if (dispatch_unc == NULL) {
|
|
continue;
|
|
}
|
|
if (dispatch_unc->read_data == NULL) {
|
|
continue;
|
|
}
|
|
num_units = LWPMU_DEVICE_num_units(&devices[dev_idx]);
|
|
cur_grp = LWPMU_DEVICE_cur_group(&devices[dev_idx]);
|
|
package_event_count = 0;
|
|
module_event_count = 0;
|
|
thread_event_count = 0;
|
|
prev_ei = -1;
|
|
invoking_processor_id = CONTROL_THIS_CPU();
|
|
start_index = DRV_CONFIG_emon_unc_offset(pcfg_unc,cur_grp);
|
|
|
|
/* Populate the output buffers with the data that is read in the worker routine*/
|
|
FOR_EACH_DATA_REG_UNC_VER2(pecb_unc,k, dev_idx ) {
|
|
|
|
cur_ei = ECB_entries_group_index(pecb_unc, k);
|
|
CHECK_SAVE_RESTORE_EVENT_INDEX(prev_ei, cur_ei, evt_index);
|
|
|
|
if (ECB_entries_is_multi_pkg_bit_set(pecb_unc,k)) {
|
|
for(pkg_idx = 0; pkg_idx < num_packages; pkg_idx++) {
|
|
for (num_inst_idx = 0; num_inst_idx < num_units; num_inst_idx++){
|
|
j = start_index +
|
|
evt_index * num_packages * num_units +
|
|
ECB_entries_group_index(pecb_unc,k) +
|
|
num_inst_idx +
|
|
pkg_idx * num_units;
|
|
|
|
read_counter_info[j] = read_unc_ctr_info[j];
|
|
SEP_PRINT_DEBUG("j = %d value =0x%llx in final\n",j, read_counter_info[j]);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
for (l = 0; l < num_cpus; l++) {
|
|
if (ECB_entries_event_scope(pecb_unc,k) == SYSTEM_EVENT && l > 0) {
|
|
break;
|
|
}
|
|
j = start_index + ECB_entries_group_index(pecb_unc,k) +
|
|
package_event_count*num_packages +
|
|
module_event_count*(GLOBAL_STATE_num_modules(driver_state)) +
|
|
thread_event_count*num_cpus;
|
|
if (ECB_entries_event_scope(pecb_unc,k) == 0) {
|
|
continue;
|
|
}
|
|
if (ECB_entries_event_scope(pecb_unc,k) == PACKAGE_EVENT) {
|
|
j = j + core_to_package_map[l];
|
|
if (!CPU_STATE_socket_master(&pcb[l])) {
|
|
continue;
|
|
}
|
|
}
|
|
if (ECB_entries_event_scope(pecb_unc,k) == MODULE_EVENT) {
|
|
j = j + CPU_STATE_cpu_module_num(&pcb[l]);
|
|
if (!CPU_STATE_cpu_module_master(&pcb[l])) {
|
|
continue;
|
|
}
|
|
}
|
|
if (ECB_entries_event_scope(pecb_unc,k) == THREAD_EVENT) {
|
|
j = j + l;
|
|
}
|
|
if (ECB_entries_counter_type(pecb_unc,k) == FREERUN_COUNTER) {
|
|
if (read_unc_ctr_info[j] >= prev_counter_data[j]) {
|
|
read_counter_info[j] = read_unc_ctr_info[j] - prev_counter_data[j];
|
|
SEP_PRINT_DEBUG("Delta count j=%d start=%llu end=%llu count=%llu k=%d\n",
|
|
j,
|
|
prev_counter_data[j],
|
|
read_unc_ctr_info[j],
|
|
read_counter_info[j],
|
|
k);
|
|
}
|
|
else {
|
|
read_counter_info[j] = read_unc_ctr_info[j] + (ECB_entries_max_bits(pecb_unc,k) - prev_counter_data[j]);
|
|
}
|
|
prev_counter_data[j] = read_unc_ctr_info[j];
|
|
}
|
|
else {
|
|
read_counter_info[j] = read_unc_ctr_info[j];
|
|
SEP_PRINT_DEBUG("Static count j=%d start=%llu end=%llu count=%llu k=%d\n",
|
|
j,
|
|
prev_counter_data[j],
|
|
read_unc_ctr_info[j],
|
|
read_counter_info[j],
|
|
k);
|
|
}
|
|
}
|
|
if (ECB_entries_event_scope(pecb_unc,k) == PACKAGE_EVENT) {
|
|
package_event_count++;
|
|
}
|
|
else if (ECB_entries_event_scope(pecb_unc,k) == MODULE_EVENT) {
|
|
module_event_count++;
|
|
}
|
|
else if (ECB_entries_event_scope(pecb_unc,k) == THREAD_EVENT) {
|
|
thread_event_count++;
|
|
}
|
|
SEP_PRINT_DEBUG("j=%d value=%llu in final\n",
|
|
j,
|
|
read_counter_info[j]);
|
|
}
|
|
} END_FOR_EACH_DATA_REG_UNC_VER2 ;
|
|
}
|
|
#endif
|
|
if (copy_to_user(arg->r_buf, read_counter_info, arg->r_len)) {
|
|
status = OS_FAULT;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
#ifdef EMON
|
|
/* ------------------------------------------------------------------------- */
|
|
/*!
|
|
* @fn static void lwmudrv_Read_Specific_TSC (PVOID param)
|
|
*
|
|
* @param param - pointer to the result
|
|
*
|
|
* @return none
|
|
*
|
|
* @brief Read the tsc value in the current processor and
|
|
* @brief write the result into param.
|
|
*
|
|
* <I>Special Notes</I>
|
|
*/
|
|
static VOID
|
|
lwpmudrv_Read_Specific_TSC (
|
|
PVOID param
|
|
)
|
|
{
|
|
U32 this_cpu;
|
|
|
|
preempt_disable();
|
|
this_cpu = CONTROL_THIS_CPU();
|
|
if (this_cpu == 0) {
|
|
UTILITY_Read_TSC((U64*)param);
|
|
}
|
|
preempt_enable();
|
|
|
|
return;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/*!
|
|
* @fn static OS_STATUS lwpmudrv_Read_Counters_And_Switch_Group(IOCTL_ARGS arg)
|
|
*
|
|
* @param arg - pointer to the IOCTL_ARGS structure
|
|
*
|
|
* @return OS_STATUS
|
|
*
|
|
* @brief Read / Store the counters and switch to the next valid group.
|
|
*
|
|
* <I>Special Notes</I>
|
|
* This routine is called from the user mode code to handle the multiple group
|
|
* situation. 11 distinct steps are taken:
|
|
* Step 1: Save the previously collected CPU 0 TSC value
|
|
* Step 2: Read CPU 0's TSC
|
|
* Step 3: Calculate the difference between the TSCs and save in the first U64 of the buffer
|
|
* Step 4: Save previous buffer ptr and Increment the buffer
|
|
* Step 5: Pause the counting PMUs
|
|
* Step 6: Read the currently programmed data PMUs and copy the data into the output buffer
|
|
* Restore the old buffer ptr.
|
|
* Step 7: Increment the current group count
|
|
* Step 8: Write the new group to the PMU
|
|
* Step 9: Write the new uncore groups to the PMU
|
|
* Step 10: Reread prev_tsc value for next collection (so read MSRs time not included in report)
|
|
* Step 11: Resume the counting PMUs
|
|
*
|
|
*/
|
|
static OS_STATUS
|
|
lwpmudrv_Read_Counters_And_Switch_Group (
|
|
IOCTL_ARGS arg
|
|
)
|
|
{
|
|
U64 prev_tsc, diff;
|
|
U64 *pBuffer;
|
|
U32 this_cpu;
|
|
char *orig_r_buf_ptr;
|
|
OS_STATUS status = OS_SUCCESS;
|
|
#if defined(DRV_IA32) || defined(DRV_EM64T)
|
|
DRV_CONFIG pcfg_unc;
|
|
DISPATCH dispatch_unc;
|
|
U32 i;
|
|
#endif
|
|
|
|
if (arg->r_buf == NULL || arg->r_len == 0) {
|
|
return OS_FAULT;
|
|
}
|
|
preempt_disable();
|
|
this_cpu = CONTROL_THIS_CPU();
|
|
preempt_enable();
|
|
// step 1
|
|
prev_tsc = cpu0_TSC;
|
|
|
|
// step 2
|
|
// read CPU 0's tsc into the global var cpu0_TSC
|
|
// if running on cpu 0, read the tsc directly, else schedule a dpc
|
|
|
|
if (this_cpu == 0) {
|
|
UTILITY_Read_TSC(&cpu0_TSC);
|
|
}
|
|
else {
|
|
CONTROL_Invoke_Cpu (0, lwpmudrv_Read_Specific_TSC, &cpu0_TSC);
|
|
}
|
|
|
|
// step 3
|
|
// get tsc diff (i.e. clocks during this monitor interval)
|
|
diff = cpu0_TSC - prev_tsc;
|
|
|
|
// save diff in first slot in buffer
|
|
pBuffer = (U64*)(arg->r_buf);
|
|
*pBuffer = diff;
|
|
|
|
// step 4
|
|
orig_r_buf_ptr = arg->r_buf;
|
|
pBuffer += 1;
|
|
arg->r_buf = (char *)pBuffer;
|
|
|
|
// step 5
|
|
status = lwpmudrv_Pause();
|
|
// step 6
|
|
status = lwpmudrv_Read_MSRs(arg);
|
|
arg->r_buf = orig_r_buf_ptr;
|
|
|
|
// step 7
|
|
// for each processor, increment its current group number
|
|
CONTROL_Invoke_Parallel(lwpmudrv_Switch_To_Next_Group, (VOID *)(size_t)0);
|
|
// step 8
|
|
CONTROL_Invoke_Parallel(dispatch->write, (VOID *)(size_t)0);
|
|
#if defined(DRV_IA32) || defined(DRV_EM64T)
|
|
for(i = 0; i < num_devices; i++) {
|
|
pcfg_unc = LWPMU_DEVICE_pcfg(&devices[i]);
|
|
dispatch_unc = LWPMU_DEVICE_dispatch(&devices[i]);
|
|
if (pcfg_unc && dispatch_unc && DRV_CONFIG_emon_mode(pcfg_unc)) {
|
|
LWPMU_DEVICE_cur_group(&devices[i])++;
|
|
if (CPU_STATE_current_group(&pcb[this_cpu]) == 0) {
|
|
LWPMU_DEVICE_cur_group(&devices[i]) = 0;
|
|
}
|
|
LWPMU_DEVICE_cur_group(&devices[i]) %= LWPMU_DEVICE_em_groups_count(&devices[i]);
|
|
SEP_PRINT_DEBUG("lwpmudrv_Read_Counters_And_Switch_Group - Swap Group to %d for device %d\n",
|
|
LWPMU_DEVICE_cur_group(&devices[i]),
|
|
i);
|
|
CONTROL_Invoke_Parallel(dispatch_unc->write, (VOID*)&i);
|
|
}
|
|
}
|
|
#endif
|
|
// step 9
|
|
// reset prev_counters for this group
|
|
if (!read_counter_info) {
|
|
read_counter_info = CONTROL_Allocate_Memory(prev_counter_size);
|
|
if (!read_counter_info) {
|
|
return OS_NO_MEM;
|
|
}
|
|
}
|
|
if (!read_unc_ctr_info) {
|
|
read_unc_ctr_info = CONTROL_Allocate_Memory(prev_counter_size);
|
|
if (!read_unc_ctr_info) {
|
|
return OS_NO_MEM;
|
|
}
|
|
}
|
|
if (!prev_counter_data) {
|
|
prev_counter_data = CONTROL_Allocate_Memory(prev_counter_size);
|
|
if (!prev_counter_data) {
|
|
return OS_NO_MEM;
|
|
}
|
|
}
|
|
// read the data registers (this is for FREERUN counters)
|
|
CONTROL_Invoke_Parallel(dispatch->read_data, (VOID *)(size_t)0);
|
|
for (i = 0; i < num_devices; i++) {
|
|
pcfg_unc = (DRV_CONFIG)LWPMU_DEVICE_pcfg(&devices[i]);
|
|
dispatch_unc = LWPMU_DEVICE_dispatch(&devices[i]);
|
|
if (pcfg_unc && dispatch_unc && dispatch_unc->read_data && DRV_CONFIG_emon_mode(pcfg_unc)) {
|
|
preempt_disable();
|
|
invoking_processor_id = CONTROL_THIS_CPU();
|
|
preempt_enable();
|
|
CONTROL_Invoke_Parallel(dispatch_unc->read_data, (VOID*)&i);
|
|
}
|
|
}
|
|
memcpy(prev_counter_data, read_unc_ctr_info, prev_counter_size);
|
|
|
|
// step 10
|
|
// reset cpu0_TSC for next collection interval
|
|
if (this_cpu == 0) {
|
|
UTILITY_Read_TSC(&cpu0_TSC);
|
|
}
|
|
else {
|
|
CONTROL_Invoke_Cpu (0, lwpmudrv_Read_Specific_TSC, &cpu0_TSC);
|
|
}
|
|
|
|
// step 11
|
|
status = lwpmudrv_Resume();
|
|
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* @fn static OS_STATUS lwpmudrv_Read_And_Reset_Counters(IOCTL_ARGS arg)
|
|
*
|
|
* @param arg - pointer to the IOCTL_ARGS structure
|
|
*
|
|
* @return OS_STATUS
|
|
*
|
|
* @brief Read the current value of the counters, and reset them all to 0.
|
|
*
|
|
* <I>Special Notes</I>
|
|
* This routine is called from the user mode code to handle overflows
|
|
* It basically does the same as the lwpmudrv_Read_Counters_And_Switch_Group
|
|
* routine except it doesn't switch groups.
|
|
* Step 1: Save the previously collected CPU 0 TSC value
|
|
* Step 2: Read CPU 0's TSC
|
|
* Step 3: Calculate the difference between the TSCs and save in the first U64 of the buffer
|
|
* Step 4: Save previous buffer ptr and Increment the buffer
|
|
* Step 5: Pause the counting PMUs
|
|
* Step 6: Read the currently programmed data PMUs and copy the data into the output buffer
|
|
* Restore the old buffer ptr.
|
|
* Step 7: Write the new group to the PMU
|
|
* Step 8: Write the new uncore groups to the PMU
|
|
* Step 9: Reread prev_tsc value for next collection (so read MSRs time not included in report)
|
|
* Step 10: Resume the counting PMUs
|
|
*/
|
|
static OS_STATUS
|
|
lwpmudrv_Read_And_Reset_Counters (
|
|
IOCTL_ARGS arg
|
|
)
|
|
{
|
|
U64 prev_tsc, diff;
|
|
U64 *p_buffer;
|
|
U32 this_cpu;
|
|
char *orig_r_buf_ptr;
|
|
OS_STATUS status = OS_SUCCESS;
|
|
#if defined(DRV_IA32) || defined(DRV_EM64T)
|
|
DRV_CONFIG pcfg_unc;
|
|
DISPATCH dispatch_unc;
|
|
U32 i;
|
|
#endif
|
|
|
|
if (arg->r_buf == NULL || arg->r_len == 0) {
|
|
return OS_FAULT;
|
|
}
|
|
preempt_disable();
|
|
this_cpu = CONTROL_THIS_CPU();
|
|
preempt_enable();
|
|
// step 1
|
|
prev_tsc = cpu0_TSC;
|
|
|
|
// step 2
|
|
// read CPU 0's tsc into the global var cpu0_TSC
|
|
// if running on cpu 0, read the tsc directly, else schedule a dpc
|
|
|
|
if (this_cpu == 0) {
|
|
UTILITY_Read_TSC(&cpu0_TSC);
|
|
}
|
|
else {
|
|
CONTROL_Invoke_Cpu (0, lwpmudrv_Read_Specific_TSC, &cpu0_TSC);
|
|
}
|
|
|
|
// step 3
|
|
// get tsc diff (i.e. clocks during this monitor interval)
|
|
diff = cpu0_TSC - prev_tsc;
|
|
|
|
// save diff in first slot in buffer
|
|
p_buffer = (U64*)(arg->r_buf);
|
|
*p_buffer = diff;
|
|
|
|
// step 4
|
|
orig_r_buf_ptr = arg->r_buf;
|
|
p_buffer += 1;
|
|
arg->r_buf = (char *)p_buffer;
|
|
// step 5
|
|
status = lwpmudrv_Pause();
|
|
// step 6
|
|
status = lwpmudrv_Read_MSRs(arg);
|
|
arg->r_buf = orig_r_buf_ptr;
|
|
|
|
// step 8
|
|
CONTROL_Invoke_Parallel(dispatch->write, (VOID *)(size_t)0);
|
|
#if defined(DRV_IA32) || defined(DRV_EM64T)
|
|
for(i=0;i<num_devices;i++){
|
|
pcfg_unc = LWPMU_DEVICE_pcfg(&devices[i]);
|
|
dispatch_unc = LWPMU_DEVICE_dispatch(&devices[i]);
|
|
if (pcfg_unc && dispatch_unc && DRV_CONFIG_emon_mode(pcfg_unc)) {
|
|
SEP_PRINT_DEBUG("lwpmudrv_Read_And_Reset_Counters - Swap Group to %d for device %d\n",
|
|
LWPMU_DEVICE_cur_group(&devices[i]),
|
|
i);
|
|
CONTROL_Invoke_Parallel(dispatch_unc->write, (VOID*)&i);
|
|
}
|
|
}
|
|
#endif
|
|
// step 9
|
|
// reset prev_counters for this group
|
|
if (!read_counter_info) {
|
|
read_counter_info = CONTROL_Allocate_Memory(prev_counter_size);
|
|
if (!read_counter_info) {
|
|
return OS_NO_MEM;
|
|
}
|
|
}
|
|
if (!read_unc_ctr_info) {
|
|
read_unc_ctr_info = CONTROL_Allocate_Memory(prev_counter_size);
|
|
if (!read_unc_ctr_info) {
|
|
return OS_NO_MEM;
|
|
}
|
|
}
|
|
if (!prev_counter_data) {
|
|
prev_counter_data = CONTROL_Allocate_Memory(prev_counter_size);
|
|
if (!prev_counter_data) {
|
|
return OS_NO_MEM;
|
|
}
|
|
}
|
|
// read the data registers (this is for FREERUN counters)
|
|
CONTROL_Invoke_Parallel(dispatch->read_data, (VOID *)(size_t)0);
|
|
for (i = 0; i < num_devices; i++) {
|
|
pcfg_unc = (DRV_CONFIG)LWPMU_DEVICE_pcfg(&devices[i]);
|
|
dispatch_unc = LWPMU_DEVICE_dispatch(&devices[i]);
|
|
if (pcfg_unc && dispatch_unc && dispatch_unc->read_data && DRV_CONFIG_emon_mode(pcfg_unc)) {
|
|
preempt_disable();
|
|
invoking_processor_id = CONTROL_THIS_CPU();
|
|
preempt_enable();
|
|
CONTROL_Invoke_Parallel(dispatch_unc->read_data, (VOID*)&i);
|
|
}
|
|
}
|
|
memcpy(prev_counter_data, read_unc_ctr_info, prev_counter_size);
|
|
|
|
// step 10
|
|
// reset cpu0_TSC for next collection interval
|
|
if (this_cpu == 0) {
|
|
UTILITY_Read_TSC(&cpu0_TSC);
|
|
}
|
|
else {
|
|
CONTROL_Invoke_Cpu (0, lwpmudrv_Read_Specific_TSC, &cpu0_TSC);
|
|
}
|
|
|
|
// step 11
|
|
status = lwpmudrv_Resume();
|
|
|
|
return status;
|
|
}
|
|
#endif
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/*!
|
|
* @fn static OS_STATUS lwpmudrv_Set_Num_EM_Groups(IOCTL_ARGS arg)
|
|
*
|
|
* @param arg - pointer to the IOCTL_ARGS structure
|
|
*
|
|
* @return OS_STATUS
|
|
*
|
|
* @brief Configure the event multiplexing group.
|
|
*
|
|
* <I>Special Notes</I>
|
|
* None
|
|
*/
|
|
static OS_STATUS
|
|
lwpmudrv_Set_EM_Config (
|
|
IOCTL_ARGS arg
|
|
)
|
|
{
|
|
S32 cpu_num;
|
|
|
|
if (GLOBAL_STATE_current_phase(driver_state) != DRV_STATE_IDLE) {
|
|
return OS_IN_PROGRESS;
|
|
}
|
|
if (arg->w_buf == NULL) {
|
|
SEP_PRINT_DEBUG("lwpmudrv_Set_EM_Config: set_num_em_groups got null pointer\n");
|
|
return OS_NO_MEM;
|
|
}
|
|
if (arg->w_len != sizeof(EVENT_CONFIG_NODE)) {
|
|
SEP_PRINT_ERROR("lwpmudrv_Set_EM_Config: Unknown size of value passed to IOCTL_EM_CONFIG: %lld\n", arg->w_len);
|
|
return OS_NO_MEM;
|
|
}
|
|
global_ec = CONTROL_Allocate_Memory(sizeof(EVENT_CONFIG_NODE));
|
|
if (!global_ec) {
|
|
return OS_NO_MEM;
|
|
}
|
|
|
|
if (copy_from_user(global_ec, arg->w_buf, sizeof(EVENT_CONFIG_NODE))) {
|
|
return OS_FAULT;
|
|
}
|
|
for (cpu_num = 0; cpu_num < GLOBAL_STATE_num_cpus(driver_state); cpu_num++) {
|
|
CPU_STATE_trigger_count(&pcb[cpu_num]) = EVENT_CONFIG_em_factor(global_ec);
|
|
CPU_STATE_trigger_event_num(&pcb[cpu_num]) = EVENT_CONFIG_em_event_num(global_ec) ;
|
|
}
|
|
GLOBAL_STATE_num_em_groups(driver_state) = EVENT_CONFIG_num_groups(global_ec);
|
|
PMU_register_data = CONTROL_Allocate_Memory(GLOBAL_STATE_num_em_groups(driver_state) *
|
|
sizeof(VOID *));
|
|
EVENTMUX_Initialize(global_ec);
|
|
|
|
em_groups_count = 0;
|
|
|
|
return OS_SUCCESS;
|
|
}
|
|
|
|
#if defined(DRV_IA32) || defined(DRV_EM64T)
|
|
/* ------------------------------------------------------------------------- */
|
|
/*!
|
|
* @fn static OS_STATUS lwpmudrv_Set_EM_Config_UNC(IOCTL_ARGS arg)
|
|
*
|
|
* @param arg - pointer to the IOCTL_ARGS structure
|
|
*
|
|
* @return OS_STATUS
|
|
*
|
|
* @brief Set the number of em groups in the global state node.
|
|
* @brief Also, copy the EVENT_CONFIG struct that has been passed in,
|
|
* @brief into a global location for now.
|
|
*
|
|
* <I>Special Notes</I>
|
|
*/
|
|
static OS_STATUS
|
|
lwpmudrv_Set_EM_Config_UNC (
|
|
IOCTL_ARGS arg
|
|
)
|
|
{
|
|
EVENT_CONFIG ec;
|
|
SEP_PRINT_DEBUG("enter lwpmudrv_Set_EM_Config_UNC\n");
|
|
if (GLOBAL_STATE_current_phase(driver_state) != DRV_STATE_IDLE) {
|
|
return OS_IN_PROGRESS;
|
|
}
|
|
|
|
SEP_PRINT_DEBUG("Num Groups UNCORE: %d\n", EVENT_CONFIG_num_groups_unc(global_ec));
|
|
// allocate memory
|
|
LWPMU_DEVICE_ec(&devices[cur_device]) = CONTROL_Allocate_Memory(sizeof(EVENT_CONFIG_NODE));
|
|
if (copy_from_user(LWPMU_DEVICE_ec(&devices[cur_device]), arg->w_buf, arg->w_len)) {
|
|
return OS_FAULT;
|
|
}
|
|
// configure num_groups from ec of the specific device
|
|
ec = (EVENT_CONFIG)LWPMU_DEVICE_ec(&devices[cur_device]);
|
|
LWPMU_DEVICE_PMU_register_data(&devices[cur_device]) = CONTROL_Allocate_Memory(EVENT_CONFIG_num_groups_unc(ec) *
|
|
sizeof(VOID *));
|
|
|
|
LWPMU_DEVICE_em_groups_count(&devices[cur_device]) = 0;
|
|
|
|
return OS_SUCCESS;
|
|
}
|
|
#endif
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/*!
|
|
* @fn static OS_STATUS lwpmudrv_Configure_events(IOCTL_ARGS arg)
|
|
*
|
|
* @param arg - pointer to the IOCTL_ARGS structure
|
|
*
|
|
* @return OS_STATUS
|
|
*
|
|
* @brief Copies one group of events into kernel space at
|
|
* @brief PMU_register_data[em_groups_count].
|
|
*
|
|
* <I>Special Notes</I>
|
|
*/
|
|
static OS_STATUS
|
|
lwpmudrv_Configure_Events (
|
|
IOCTL_ARGS arg
|
|
)
|
|
{
|
|
int uncopied;
|
|
U32 group_id = 0;
|
|
ECB ecb = NULL;
|
|
|
|
SEP_PRINT_DEBUG("lwpmudrv_Configure_Events: entered.\n");
|
|
if (GLOBAL_STATE_current_phase(driver_state) != DRV_STATE_IDLE) {
|
|
return OS_IN_PROGRESS;
|
|
}
|
|
|
|
if (em_groups_count >= GLOBAL_STATE_num_em_groups(driver_state)) {
|
|
SEP_PRINT_ERROR("lwpmudrv_Configure_Events: Number of EM groups exceeded the initial configuration.");
|
|
return OS_SUCCESS;
|
|
}
|
|
if (arg->w_buf == NULL || arg->w_len == 0) {
|
|
return OS_FAULT;
|
|
}
|
|
|
|
ecb = (ECB)arg->w_buf;
|
|
group_id = ECB_group_id(ecb);
|
|
PMU_register_data[group_id] = CONTROL_Allocate_Memory(arg->w_len);
|
|
if (!PMU_register_data[group_id]) {
|
|
return OS_NO_MEM;
|
|
}
|
|
//
|
|
// Make a copy of the data for global use.
|
|
//
|
|
uncopied = copy_from_user(PMU_register_data[group_id], arg->w_buf, arg->w_len);
|
|
if (uncopied > 0) {
|
|
return OS_NO_MEM;
|
|
}
|
|
em_groups_count = group_id + 1;
|
|
|
|
return OS_SUCCESS;
|
|
}
|
|
|
|
#if defined(DRV_IA32) || defined(DRV_EM64T)
|
|
/* ------------------------------------------------------------------------- */
|
|
/*!
|
|
* @fn static OS_STATUS lwpmudrv_Configure_events_UNC(IOCTL_ARGS arg)
|
|
*
|
|
* @param arg - pointer to the IOCTL_ARGS structure
|
|
*
|
|
* @return OS_STATUS
|
|
*
|
|
* @brief Make a copy of the uncore registers that need to be programmed
|
|
* @brief for the next event set used for event multiplexing
|
|
*
|
|
* <I>Special Notes</I>
|
|
*/
|
|
static OS_STATUS
|
|
lwpmudrv_Configure_Events_UNC (
|
|
IOCTL_ARGS arg
|
|
)
|
|
{
|
|
VOID **PMU_register_data_unc;
|
|
S32 em_groups_count_unc;
|
|
ECB ecb;
|
|
S32 i;
|
|
U32 j;
|
|
EVENT_CONFIG ec_unc;
|
|
DRV_CONFIG pcfg_unc;
|
|
U32 group_id = 0;
|
|
ECB in_ecb = NULL;
|
|
|
|
if (GLOBAL_STATE_current_phase(driver_state) != DRV_STATE_IDLE) {
|
|
return OS_IN_PROGRESS;
|
|
}
|
|
|
|
em_groups_count_unc = LWPMU_DEVICE_em_groups_count(&devices[cur_device]);
|
|
PMU_register_data_unc = LWPMU_DEVICE_PMU_register_data(&devices[cur_device]);
|
|
ec_unc = LWPMU_DEVICE_ec(&devices[cur_device]);
|
|
pcfg_unc = LWPMU_DEVICE_pcfg(&devices[cur_device]);
|
|
|
|
if (em_groups_count_unc >= (S32)EVENT_CONFIG_num_groups_unc(ec_unc)) {
|
|
SEP_PRINT("Number of Uncore EM groups exceeded the initial configuration.");
|
|
return OS_SUCCESS;
|
|
}
|
|
if (arg->w_buf == NULL || arg->w_len == 0) {
|
|
return OS_FAULT;
|
|
}
|
|
// size is in w_len, data is pointed to by w_buf
|
|
//
|
|
in_ecb = (ECB)arg->w_buf;
|
|
group_id = ECB_group_id(in_ecb);
|
|
PMU_register_data_unc[group_id] = CONTROL_Allocate_Memory(arg->w_len);
|
|
if (!PMU_register_data_unc[group_id]) {
|
|
return OS_NO_MEM;
|
|
}
|
|
|
|
//
|
|
// Make a copy of the data for global use.
|
|
//
|
|
if (copy_from_user(PMU_register_data_unc[group_id], arg->w_buf, arg->w_len)) {
|
|
return OS_NO_MEM;
|
|
}
|
|
|
|
// at this point, we know the number of uncore events for this device,
|
|
// so allocate the results buffer per thread for uncore only for SEP event based uncore counting
|
|
if (DRV_CONFIG_event_based_counts(pcfg_unc)) {
|
|
ecb = PMU_register_data_unc[group_id];
|
|
LWPMU_DEVICE_num_events(&devices[cur_device]) = ECB_num_events(ecb);
|
|
|
|
LWPMU_DEVICE_acc_per_thread(&devices[cur_device]) = CONTROL_Allocate_Memory(GLOBAL_STATE_num_cpus(driver_state) *
|
|
sizeof(VOID *));
|
|
LWPMU_DEVICE_prev_val_per_thread(&devices[cur_device]) = CONTROL_Allocate_Memory(GLOBAL_STATE_num_cpus(driver_state) *
|
|
sizeof(VOID *));
|
|
for (i = 0; i < GLOBAL_STATE_num_cpus(driver_state); i++) {
|
|
LWPMU_DEVICE_acc_per_thread(&devices[cur_device])[i] = CONTROL_Allocate_Memory((ECB_num_events(ecb) + 1)*sizeof(U64));
|
|
LWPMU_DEVICE_prev_val_per_thread(&devices[cur_device])[i] = CONTROL_Allocate_Memory((ECB_num_events(ecb) + 1)*sizeof(U64));
|
|
// initialize all values to 0
|
|
for (j = 0; j < ECB_num_events(ecb) + 1; j++) {
|
|
LWPMU_DEVICE_acc_per_thread(&devices[cur_device])[i][j] = 0LL;
|
|
LWPMU_DEVICE_prev_val_per_thread(&devices[cur_device])[i][j] = 0LL;
|
|
}
|
|
}
|
|
}
|
|
LWPMU_DEVICE_em_groups_count(&devices[cur_device]) = group_id + 1;
|
|
|
|
return OS_SUCCESS;
|
|
}
|
|
#endif
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/*!
|
|
* @fn static OS_STATUS lwpmudrv_Set_Sample_Descriptors(IOCTL_ARGS arg)
|
|
*
|
|
* @param arg - pointer to the IOCTL_ARGS structure
|
|
*
|
|
* @return OS_STATUS
|
|
*
|
|
* @brief Set the number of descriptor groups in the global state node.
|
|
*
|
|
* <I>Special Notes</I>
|
|
*/
|
|
static OS_STATUS
|
|
lwpmudrv_Set_Sample_Descriptors (
|
|
IOCTL_ARGS arg
|
|
)
|
|
{
|
|
if (GLOBAL_STATE_current_phase(driver_state) != DRV_STATE_IDLE) {
|
|
return OS_IN_PROGRESS;
|
|
}
|
|
if (arg->w_len != sizeof(U32) || arg->w_buf == NULL) {
|
|
SEP_PRINT_WARNING("Unknown size of Sample Descriptors\n");
|
|
return OS_SUCCESS;
|
|
}
|
|
|
|
desc_count = 0;
|
|
if (copy_from_user(&GLOBAL_STATE_num_descriptors(driver_state),
|
|
arg->w_buf,
|
|
sizeof(U32))) {
|
|
return OS_FAULT;
|
|
}
|
|
|
|
desc_data = CONTROL_Allocate_Memory(GLOBAL_STATE_num_descriptors(driver_state) *
|
|
sizeof(VOID *));
|
|
return OS_SUCCESS;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/*!
|
|
* @fn static OS_STATUS lwpmudrv_Configure_Descriptors(IOCTL_ARGS arg)
|
|
*
|
|
* @param arg - pointer to the IOCTL_ARGS structure
|
|
* @return OS_STATUS
|
|
*
|
|
* @brief Make a copy of the descriptors that need to be read in order
|
|
* @brief to configure a sample record.
|
|
*
|
|
* <I>Special Notes</I>
|
|
*/
|
|
static OS_STATUS
|
|
lwpmudrv_Configure_Descriptors (
|
|
IOCTL_ARGS arg
|
|
)
|
|
{
|
|
int uncopied;
|
|
|
|
if (GLOBAL_STATE_current_phase(driver_state) != DRV_STATE_IDLE) {
|
|
return OS_IN_PROGRESS;
|
|
}
|
|
|
|
if (desc_count >= GLOBAL_STATE_num_descriptors(driver_state)) {
|
|
SEP_PRINT_WARNING("Number of descriptor groups exceeded the initial configuration.");
|
|
return OS_SUCCESS;
|
|
}
|
|
|
|
if (arg->w_len == 0 || arg->w_buf == NULL) {
|
|
return OS_FAULT;
|
|
}
|
|
|
|
//
|
|
// First things first: Make a copy of the data for global use.
|
|
//
|
|
desc_data[desc_count] = CONTROL_Allocate_Memory(arg->w_len);
|
|
uncopied = copy_from_user(desc_data[desc_count], arg->w_buf, arg->w_len);
|
|
if (uncopied < 0) {
|
|
// no-op ... eliminates "variable not used" compiler warning
|
|
}
|
|
SEP_PRINT_DEBUG("Added descriptor # %d\n", desc_count);
|
|
desc_count++;
|
|
|
|
return OS_SUCCESS;
|
|
}
|
|
|
|
#if defined(DRV_IA32) || defined(DRV_EM64T)
|
|
/* ------------------------------------------------------------------------- */
|
|
/*!
|
|
* @fn static OS_STATUS lwpmudrv_LBR_Info(IOCTL_ARGS arg)
|
|
*
|
|
*
|
|
* @param arg - pointer to the IOCTL_ARGS structure
|
|
* @return OS_STATUS
|
|
*
|
|
* @brief Make a copy of the LBR information that is passed in.
|
|
*
|
|
* <I>Special Notes</I>
|
|
*/
|
|
static OS_STATUS
|
|
lwpmudrv_LBR_Info (
|
|
IOCTL_ARGS arg
|
|
)
|
|
{
|
|
if (GLOBAL_STATE_current_phase(driver_state) != DRV_STATE_IDLE) {
|
|
return OS_IN_PROGRESS;
|
|
}
|
|
|
|
if (DRV_CONFIG_collect_lbrs(pcfg) == FALSE) {
|
|
SEP_PRINT_WARNING("lwpmudrv_LBR_Info: LBR capture has not been configured\n");
|
|
return OS_SUCCESS;
|
|
}
|
|
|
|
if (arg->w_len == 0 || arg->w_buf == NULL) {
|
|
return OS_FAULT;
|
|
}
|
|
|
|
//
|
|
// First things first: Make a copy of the data for global use.
|
|
//
|
|
|
|
lbr = CONTROL_Allocate_Memory((int)arg->w_len);
|
|
if (!lbr) {
|
|
return OS_NO_MEM;
|
|
}
|
|
|
|
if (copy_from_user(lbr, arg->w_buf, arg->w_len)) {
|
|
return OS_FAULT;
|
|
}
|
|
|
|
return OS_SUCCESS;
|
|
}
|
|
|
|
#ifdef EMON
|
|
#define CR4_PCE 0x00000100 //Performance-monitoring counter enable RDPMC
|
|
/* ------------------------------------------------------------------------- */
|
|
/*!
|
|
* @fn static void lwpmudrv_Set_CR4_PCE_Bit(PVOID param)
|
|
*
|
|
* @param param - dummy parameter
|
|
*
|
|
* @return NONE
|
|
*
|
|
* @brief Set CR4's PCE bit on the logical processor
|
|
*
|
|
* <I>Special Notes</I>
|
|
*/
|
|
static VOID
|
|
lwpmudrv_Set_CR4_PCE_Bit (
|
|
PVOID param
|
|
)
|
|
{
|
|
#if defined(DRV_IA32)
|
|
__asm__("movl %%cr4,%%eax\n\t"
|
|
"orl %0,%%eax\n\t"
|
|
"movl %%eax,%%cr4\n"
|
|
: : "irg" (CR4_PCE)
|
|
:"ax");
|
|
#endif
|
|
#if defined(DRV_EM64T)
|
|
__asm__("movq %%cr4,%%rax\n\t"
|
|
"orq %0,%%rax\n\t"
|
|
"movq %%rax,%%cr4\n"
|
|
: : "irg" (CR4_PCE)
|
|
:"ax");
|
|
#endif
|
|
|
|
return;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/*!
|
|
* @fn static void lwpmudrv_Clear_CR4_PCE_Bit(PVOID param)
|
|
*
|
|
* @param param - dummy parameter
|
|
*
|
|
* @return NONE
|
|
*
|
|
* @brief ClearSet CR4's PCE bit on the logical processor
|
|
*
|
|
* <I>Special Notes</I>
|
|
*/
|
|
static VOID
|
|
lwpmudrv_Clear_CR4_PCE_Bit (
|
|
PVOID param
|
|
)
|
|
{
|
|
#if defined(DRV_IA32)
|
|
__asm__("movl %%cr4,%%eax\n\t"
|
|
"andl %0,%%eax\n\t"
|
|
"movl %%eax,%%cr4\n"
|
|
: : "irg" (~CR4_PCE)
|
|
:"ax");
|
|
#endif
|
|
#if defined(DRV_EM64T)
|
|
__asm__("movq %%cr4,%%rax\n\t"
|
|
"andq %0,%%rax\n\t"
|
|
"movq %%rax,%%cr4\n"
|
|
: : "irg" (~CR4_PCE)
|
|
:"ax");
|
|
#endif
|
|
|
|
return;
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/*!
|
|
* @fn static OS_STATUS lwpmudrv_Start(void)
|
|
*
|
|
* @param none
|
|
*
|
|
* @return OS_STATUS
|
|
*
|
|
* @brief Local function that handles the LWPMU_IOCTL_START call.
|
|
* @brief Set up the OS hooks for process/thread/load notifications.
|
|
* @brief Write the initial set of MSRs.
|
|
*
|
|
* <I>Special Notes</I>
|
|
*/
|
|
static OS_STATUS
|
|
lwpmudrv_Start (
|
|
VOID
|
|
)
|
|
{
|
|
OS_STATUS status = OS_SUCCESS;
|
|
U32 previous_state;
|
|
#if defined(DRV_IA32) || defined(DRV_EM64T)
|
|
DRV_CONFIG pcfg_unc = NULL;
|
|
DISPATCH dispatch_unc = NULL;
|
|
U32 i;
|
|
#endif
|
|
|
|
/*
|
|
* To Do: Check for state == STATE_IDLE and only then enable sampling
|
|
*/
|
|
previous_state = cmpxchg(&GLOBAL_STATE_current_phase(driver_state),
|
|
DRV_STATE_IDLE,
|
|
DRV_STATE_RUNNING);
|
|
if (previous_state != DRV_STATE_IDLE) {
|
|
SEP_PRINT_ERROR("lwpmudrv_Start: Unable to start sampling - State is %d\n",
|
|
GLOBAL_STATE_current_phase(driver_state));
|
|
return OS_IN_PROGRESS;
|
|
}
|
|
if (DRV_CONFIG_use_pcl(pcfg) == TRUE) {
|
|
if (DRV_CONFIG_start_paused(pcfg)) {
|
|
GLOBAL_STATE_current_phase(driver_state) = DRV_STATE_PAUSED;
|
|
}
|
|
return status;
|
|
}
|
|
|
|
atomic_set(&read_now, GLOBAL_STATE_num_cpus(driver_state));
|
|
init_waitqueue_head(&read_tsc_now);
|
|
|
|
#ifdef EMON
|
|
#if defined(DRV_IA32) || defined(DRV_EM64T)
|
|
CONTROL_Invoke_Parallel(lwpmudrv_Set_CR4_PCE_Bit, (PVOID)(size_t)0);
|
|
#endif // (DRV_IA32 || DRV_EM64T)
|
|
#endif // EMON
|
|
CONTROL_Invoke_Parallel(lwpmudrv_Fill_TSC_Info, (PVOID)(size_t)0);
|
|
|
|
#ifdef EMON
|
|
// initialize the cpu0_TSC var
|
|
cpu0_TSC = tsc_info[0];
|
|
#endif
|
|
|
|
if (DRV_CONFIG_start_paused(pcfg)) {
|
|
GLOBAL_STATE_current_phase(driver_state) = DRV_STATE_PAUSED;
|
|
}
|
|
else {
|
|
CONTROL_Invoke_Parallel(dispatch->restart, (PVOID)(size_t)0);
|
|
}
|
|
|
|
if (DRV_CONFIG_enable_chipset(pcfg)) {
|
|
cs_dispatch->start_chipset();
|
|
}
|
|
|
|
#if defined(DRV_IA32) || defined(DRV_EM64T)
|
|
for (i = 0; i < num_devices; i++) {
|
|
pcfg_unc = (DRV_CONFIG)LWPMU_DEVICE_pcfg(&devices[i]);
|
|
dispatch_unc = LWPMU_DEVICE_dispatch(&devices[i]);
|
|
|
|
if (pcfg_unc &&
|
|
(DRV_CONFIG_event_based_counts(pcfg_unc) || DRV_CONFIG_emon_mode(pcfg_unc))&&
|
|
dispatch_unc &&
|
|
dispatch_unc->restart) {
|
|
SEP_PRINT_DEBUG("LWP: calling UNC Start\n");
|
|
CONTROL_Invoke_Parallel(dispatch_unc->restart, (VOID *)&i);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
EVENTMUX_Start(global_ec);
|
|
lwpmudrv_Dump_Tracer ("start", 0);
|
|
|
|
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* @fn lwpmudrv_Prepare_Stop();
|
|
*
|
|
* @param NONE
|
|
* @return OS_STATUS
|
|
*
|
|
* @brief Local function that handles the LWPMUDRV_IOCTL_STOP call.
|
|
* @brief Cleans up the interrupt handler.
|
|
*/
|
|
static OS_STATUS
|
|
lwpmudrv_Prepare_Stop (
|
|
VOID
|
|
)
|
|
{
|
|
U32 i;
|
|
S32 done = FALSE;
|
|
U32 current_state = GLOBAL_STATE_current_phase(driver_state);
|
|
|
|
#if defined(DRV_IA32) || defined(DRV_EM64T)
|
|
DRV_CONFIG pcfg_unc = NULL;
|
|
DISPATCH dispatch_unc = NULL;
|
|
#endif
|
|
SEP_PRINT_DEBUG("lwpmudrv_Prepare_Stop: About to stop sampling\n");
|
|
GLOBAL_STATE_current_phase(driver_state) = DRV_STATE_PREPARE_STOP;
|
|
|
|
if (current_state == DRV_STATE_UNINITIALIZED) {
|
|
return OS_SUCCESS;
|
|
}
|
|
|
|
if (pcfg == NULL) {
|
|
return OS_SUCCESS;
|
|
}
|
|
|
|
if (DRV_CONFIG_use_pcl(pcfg) == TRUE) {
|
|
return OS_SUCCESS;
|
|
}
|
|
|
|
if (current_state != DRV_STATE_IDLE &&
|
|
current_state != DRV_STATE_RESERVED) {
|
|
for (i = 0; i < GLOBAL_STATE_num_cpus(driver_state); i++) {
|
|
CPU_STATE_accept_interrupt(&pcb[i]) = 0;
|
|
}
|
|
while (!done) {
|
|
done = TRUE;
|
|
for (i = 0; i < GLOBAL_STATE_num_cpus(driver_state); i++) {
|
|
if (atomic_read(&CPU_STATE_in_interrupt(&pcb[i]))) {
|
|
done = FALSE;
|
|
}
|
|
}
|
|
}
|
|
CONTROL_Invoke_Parallel(dispatch->freeze, (PVOID)(size_t)0);
|
|
SEP_PRINT_DEBUG("lwpmudrv_Prepare_Stop: Outside of all interrupts\n");
|
|
|
|
if (DRV_CONFIG_enable_chipset(pcfg)) {
|
|
cs_dispatch->stop_chipset();
|
|
}
|
|
|
|
}
|
|
|
|
if (pcfg == NULL) {
|
|
return OS_SUCCESS;
|
|
}
|
|
|
|
#if defined(DRV_IA32) || defined(DRV_EM64T)
|
|
for (i = 0; i < num_devices; i++) {
|
|
pcfg_unc = (DRV_CONFIG)LWPMU_DEVICE_pcfg(&devices[i]);
|
|
dispatch_unc = LWPMU_DEVICE_dispatch(&devices[i]);
|
|
|
|
if (pcfg_unc &&
|
|
(DRV_CONFIG_event_based_counts(pcfg_unc) || DRV_CONFIG_emon_mode(pcfg_unc)) &&
|
|
dispatch_unc &&
|
|
dispatch_unc->freeze) {
|
|
SEP_PRINT_DEBUG("LWP: calling UNC Stop\n");
|
|
preempt_disable();
|
|
invoking_processor_id = CONTROL_THIS_CPU();
|
|
preempt_enable();
|
|
CONTROL_Invoke_Parallel(dispatch_unc->freeze, (VOID *)&i);
|
|
}
|
|
}
|
|
#endif
|
|
/*
|
|
* Clean up all the control registers
|
|
*/
|
|
if ((dispatch != NULL) && (dispatch->cleanup != NULL)) {
|
|
preempt_disable();
|
|
invoking_processor_id = CONTROL_THIS_CPU();
|
|
preempt_enable();
|
|
CONTROL_Invoke_Parallel(dispatch->cleanup, (VOID *)(size_t)invoking_processor_id);
|
|
SEP_PRINT_DEBUG("Stop: Cleanup finished\n");
|
|
}
|
|
lwpmudrv_Free_Restore_Buffer();
|
|
|
|
#ifdef EMON
|
|
#if defined(DRV_IA32) || defined(DRV_EM64T)
|
|
CONTROL_Invoke_Parallel(lwpmudrv_Clear_CR4_PCE_Bit, (VOID *)(size_t)0);
|
|
#endif
|
|
#endif
|
|
|
|
if (DRV_CONFIG_enable_chipset(pcfg) &&
|
|
cs_dispatch && cs_dispatch->fini_chipset) {
|
|
cs_dispatch->fini_chipset();
|
|
}
|
|
|
|
return OS_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* @fn lwpmudrv_Finish_Stop();
|
|
*
|
|
* @param NONE
|
|
* @return OS_STATUS
|
|
*
|
|
* @brief Local function that handles the LWPMUDRV_IOCTL_STOP call.
|
|
* @brief Cleans up the interrupt handler.
|
|
*/
|
|
static OS_STATUS
|
|
lwpmudrv_Finish_Stop (
|
|
VOID
|
|
)
|
|
{
|
|
U32 current_state = GLOBAL_STATE_current_phase(driver_state);
|
|
S32 prev_value;
|
|
OS_STATUS status = OS_SUCCESS;
|
|
|
|
prev_value = cmpxchg(&in_finish_code, 0, 1);
|
|
if (prev_value != 0) {
|
|
return OS_SUCCESS;
|
|
}
|
|
if (DRV_CONFIG_counting_mode(pcfg) == FALSE) {
|
|
#if defined(DRV_USE_NMI)
|
|
OUTPUT_Delete_Timers();
|
|
#endif
|
|
if (abnormal_terminate == 0) {
|
|
LINUXOS_Uninstall_Hooks();
|
|
/*
|
|
* Make sure that the module buffers are not deallocated and that the module flush
|
|
* thread has not been terminated.
|
|
*/
|
|
if (current_state != DRV_STATE_IDLE && current_state != DRV_STATE_RESERVED) {
|
|
status = LINUXOS_Enum_Process_Modules(TRUE);
|
|
}
|
|
}
|
|
OUTPUT_Flush();
|
|
/*
|
|
* Clean up the interrupt handler via the IDT
|
|
*/
|
|
CPUMON_Remove_Cpuhooks();
|
|
#if defined(DRV_IA32) || defined(DRV_EM64T)
|
|
PEBS_Destroy(pcfg);
|
|
#endif
|
|
EVENTMUX_Destroy(global_ec);
|
|
}
|
|
GLOBAL_STATE_current_phase(driver_state) = DRV_STATE_STOPPED;
|
|
in_finish_code = 0;
|
|
|
|
if (DRV_CONFIG_enable_cp_mode(pcfg)) {
|
|
if (interrupt_counts) {
|
|
S32 idx, cpu;
|
|
for (cpu = 0; cpu < GLOBAL_STATE_num_cpus(driver_state); cpu++) {
|
|
for(idx = 0; idx < DRV_CONFIG_num_events(pcfg); idx++) {
|
|
SEP_PRINT("Interrupt count: CPU %d, event %d = %lld\n", cpu, idx, interrupt_counts[cpu * DRV_CONFIG_num_events(pcfg) + idx]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#if defined(DRV_IA32) || defined(DRV_EM64T)
|
|
read_counter_info = CONTROL_Free_Memory(read_counter_info);
|
|
read_unc_ctr_info = CONTROL_Free_Memory(read_unc_ctr_info);
|
|
prev_counter_data = CONTROL_Free_Memory(prev_counter_data);
|
|
#endif
|
|
lwpmudrv_Dump_Tracer ("stop", 0);
|
|
return status;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/*!
|
|
* @fn static OS_STATUS lwpmudrv_Get_Normalized_TSC(IOCTL_ARGS arg)
|
|
*
|
|
* @param arg - Pointer to the IOCTL structure
|
|
*
|
|
* @return OS_STATUS
|
|
*
|
|
* @brief Return the current value of the normalized TSC.
|
|
* NOTE: knocked off check for pcb == NULL in order to get a valid
|
|
* CPU frequency when running emon -v.
|
|
*
|
|
* <I>Special Notes</I>
|
|
*/
|
|
static OS_STATUS
|
|
lwpmudrv_Get_Normalized_TSC (
|
|
IOCTL_ARGS arg
|
|
)
|
|
{
|
|
U64 tsc = 0;
|
|
size_t size_to_copy = sizeof(U64);
|
|
|
|
if (arg->r_len < size_to_copy || arg->r_buf == NULL) {
|
|
return OS_FAULT;
|
|
}
|
|
|
|
preempt_disable();
|
|
UTILITY_Read_TSC(&tsc);
|
|
tsc -= TSC_SKEW(CONTROL_THIS_CPU());
|
|
preempt_enable();
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)
|
|
if (pcfg && DRV_CONFIG_use_pcl(pcfg) == TRUE) {
|
|
tsc = cpu_clock(CONTROL_THIS_CPU());
|
|
}
|
|
#endif
|
|
if (copy_to_user(arg->r_buf, (VOID *)&tsc, size_to_copy)) {
|
|
SEP_PRINT_DEBUG("lwpmudrv_Get_Normalized_TSC: copy_to_user() failed\n");
|
|
return OS_FAULT;
|
|
}
|
|
lwpmudrv_Dump_Tracer ("marker", tsc);
|
|
|
|
return OS_SUCCESS;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/*!
|
|
* @fn static OS_STATUS lwpmudrv_Get_Num_Cores(IOCTL_ARGS arg)
|
|
*
|
|
* @param arg - Pointer to the IOCTL structure
|
|
*
|
|
* @return OS_STATUS
|
|
*
|
|
* @brief Quickly return the (total) number of cpus in the system.
|
|
*
|
|
* <I>Special Notes</I>
|
|
*/
|
|
static OS_STATUS
|
|
lwpmudrv_Get_Num_Cores (
|
|
IOCTL_ARGS arg
|
|
)
|
|
{
|
|
OS_STATUS status = OS_SUCCESS;
|
|
int num = GLOBAL_STATE_num_cpus(driver_state);
|
|
|
|
if (arg->r_len < sizeof(int) || arg->r_buf == NULL) {
|
|
return OS_FAULT;
|
|
}
|
|
|
|
SEP_PRINT_DEBUG("lwpmudrv_Get_Num_Cores: Num_Cores is %d, out_buf is 0x%p\n", num, arg->r_buf);
|
|
status = put_user(num, (int*)arg->r_buf);
|
|
|
|
return status;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/*!
|
|
* @fn static OS_STATUS lwpmudrv_Set_CPU_Mask(PVOID in_buf, U32 in_buf_size)
|
|
*
|
|
* @param in_buf - pointer to the CPU mask buffer
|
|
* @param in_buf_size - size of the CPU mask buffer
|
|
*
|
|
* @return OS_STATUS
|
|
*
|
|
* @brief process the CPU mask as requested by the user
|
|
*
|
|
* <I>Special Notes</I>
|
|
*/
|
|
static OS_STATUS
|
|
lwpmudrv_Set_CPU_Mask (
|
|
PVOID in_buf,
|
|
size_t in_buf_len
|
|
)
|
|
{
|
|
U32 cpu_count = 0;
|
|
|
|
if (GLOBAL_STATE_current_phase(driver_state) != DRV_STATE_IDLE) {
|
|
return OS_IN_PROGRESS;
|
|
}
|
|
|
|
if (in_buf_len == 0 || in_buf == NULL) {
|
|
return OS_INVALID;
|
|
}
|
|
|
|
cpu_mask_bits = CONTROL_Allocate_Memory((int)in_buf_len);
|
|
if (!cpu_mask_bits) {
|
|
return OS_NO_MEM;
|
|
}
|
|
|
|
if (copy_from_user(cpu_mask_bits, (S8*)in_buf, (int)in_buf_len)) {
|
|
return OS_FAULT;
|
|
}
|
|
|
|
for (cpu_count = 0; cpu_count < (U32)GLOBAL_STATE_num_cpus(driver_state); cpu_count++) {
|
|
CPU_STATE_accept_interrupt(&pcb[cpu_count]) = cpu_mask_bits[cpu_count] ? 1 : 0;
|
|
CPU_STATE_initial_mask(&pcb[cpu_count ]) = cpu_mask_bits[cpu_count] ? 1 : 0;
|
|
}
|
|
|
|
return OS_SUCCESS;
|
|
}
|
|
|
|
|
|
#if defined(DRV_IA32) || defined(DRV_EM64T)
|
|
/* ------------------------------------------------------------------------- */
|
|
/*!
|
|
* @fn static OS_STATUS lwpmudrv_Get_KERNEL_CS(IOCTL_ARGS arg)
|
|
*
|
|
* @param arg - Pointer to the IOCTL structure
|
|
*
|
|
* @return OS_STATUS
|
|
*
|
|
* @brief Return the value of the Kernel symbol KERNEL_CS.
|
|
*
|
|
* <I>Special Notes</I>
|
|
*/
|
|
static OS_STATUS
|
|
lwpmudrv_Get_KERNEL_CS (
|
|
IOCTL_ARGS arg
|
|
)
|
|
{
|
|
OS_STATUS status = OS_SUCCESS;
|
|
int num = __KERNEL_CS;
|
|
|
|
if (arg->r_len < sizeof(int) || arg->r_buf == NULL) {
|
|
return OS_FAULT;
|
|
}
|
|
|
|
SEP_PRINT_DEBUG("lwpmudrv_Get_KERNEL_CS is %d, out_buf is 0x%p\n", num, arg->r_buf);
|
|
status = put_user(num, (int*)arg->r_buf);
|
|
|
|
return status;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* @fn lwpmudrv_Set_UID
|
|
*
|
|
* @param IN arg - pointer to the output buffer
|
|
* @return OS_STATUS
|
|
*
|
|
* @brief Receive the value of the UID of the collector process.
|
|
*/
|
|
static OS_STATUS
|
|
lwpmudrv_Set_UID (
|
|
IOCTL_ARGS arg
|
|
)
|
|
{
|
|
OS_STATUS status = OS_SUCCESS;
|
|
|
|
if (arg->w_len < sizeof(uid_t) || arg->w_buf == NULL) {
|
|
return OS_FAULT;
|
|
}
|
|
|
|
if (GLOBAL_STATE_current_phase(driver_state) != DRV_STATE_IDLE) {
|
|
return OS_IN_PROGRESS;
|
|
}
|
|
|
|
status = get_user(uid, (int*)arg->w_buf);
|
|
SEP_PRINT_DEBUG("lwpmudrv_Set_UID is %d\n", uid);
|
|
|
|
return status;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/*!
|
|
* @fn static OS_STATUS lwpmudrv_Get_TSC_Skew_Info(IOCTL_ARGS arg)
|
|
*
|
|
* @param arg - Pointer to the IOCTL structure
|
|
*
|
|
* @return OS_STATUS
|
|
* @brief Return the current value of the TSC skew data
|
|
*
|
|
* <I>Special Notes</I>
|
|
*/
|
|
static OS_STATUS
|
|
lwpmudrv_Get_TSC_Skew_Info (
|
|
IOCTL_ARGS arg
|
|
)
|
|
{
|
|
S64 *skew_array;
|
|
size_t skew_array_len;
|
|
S32 i;
|
|
|
|
skew_array_len = GLOBAL_STATE_num_cpus(driver_state) * sizeof(U64);
|
|
|
|
if (arg->r_len < skew_array_len || arg->r_buf == NULL) {
|
|
SEP_PRINT_ERROR("lwpmudrv_Get_TSC_Skew_Info: Buffer too small in Get_TSC_Skew_Info: %lld\n",arg->r_len);
|
|
return OS_FAULT;
|
|
}
|
|
|
|
if (GLOBAL_STATE_current_phase(driver_state) != DRV_STATE_STOPPED) {
|
|
return OS_IN_PROGRESS;
|
|
}
|
|
|
|
SEP_PRINT_DEBUG("lwpmudrv_Get_TSC_Skew_Info dispatched with r_len=%lld\n", arg->r_len);
|
|
|
|
skew_array = CONTROL_Allocate_Memory(skew_array_len);
|
|
if (skew_array == NULL) {
|
|
SEP_PRINT_ERROR("lwpmudrv_Get_TSC_Skew_Info: Unable to allocate memory\n");
|
|
return OS_NO_MEM;
|
|
}
|
|
|
|
for (i = 0; i < GLOBAL_STATE_num_cpus(driver_state); i++) {
|
|
skew_array[i] = TSC_SKEW(i);
|
|
}
|
|
|
|
if (copy_to_user(arg->r_buf, skew_array, skew_array_len)) {
|
|
return OS_FAULT;
|
|
}
|
|
|
|
skew_array = CONTROL_Free_Memory(skew_array);
|
|
return OS_SUCCESS;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/*!
|
|
* @fn static OS_STATUS lwpmudrv_Collect_Sys_Config(IOCTL_ARGS arg)
|
|
*
|
|
* @param arg - Pointer to the IOCTL structure
|
|
*
|
|
* @return OS_STATUS
|
|
*
|
|
* @brief Local function that handles the COLLECT_SYS_CONFIG call.
|
|
* @brief Builds and collects the SYS_INFO data needed.
|
|
* @brief Writes the result into the argument.
|
|
*
|
|
* <I>Special Notes</I>
|
|
*/
|
|
static OS_STATUS
|
|
lwpmudrv_Collect_Sys_Config (
|
|
IOCTL_ARGS arg
|
|
)
|
|
{
|
|
OS_STATUS status = OS_SUCCESS;
|
|
int num = SYS_INFO_Build();
|
|
|
|
if (arg->r_len < sizeof(int) || arg->r_buf == NULL) {
|
|
return OS_FAULT;
|
|
}
|
|
|
|
SEP_PRINT_DEBUG("lwpmudrv_Collect_Sys_Config: size of sys info is %d\n", num);
|
|
status = put_user(num, (int*)arg->r_buf);
|
|
|
|
return status;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/*!
|
|
* @fn static OS_STATUS lwpmudrv_Sys_Config(IOCTL_ARGS arg)
|
|
*
|
|
* @param arg - Pointer to the IOCTL structure
|
|
*
|
|
* @return OS_STATUS
|
|
*
|
|
* @brief Return the current value of the normalized TSC.
|
|
*
|
|
* @brief Transfers the VTSA_SYS_INFO data back to the abstraction layer.
|
|
* @brief The out_buf should have enough space to handle the transfer.
|
|
*/
|
|
static OS_STATUS
|
|
lwpmudrv_Sys_Config (
|
|
IOCTL_ARGS arg
|
|
)
|
|
{
|
|
if (arg->r_len == 0 || arg->r_buf == NULL) {
|
|
return OS_FAULT;
|
|
}
|
|
|
|
SYS_INFO_Transfer(arg->r_buf, arg->r_len);
|
|
|
|
return OS_SUCCESS;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/*!
|
|
* @fn static OS_STATUS lwpmudrv_Samp_Read_Num_Of_Core_Counters(IOCTL_ARGS arg)
|
|
*
|
|
* @param arg - Pointer to the IOCTL structure
|
|
*
|
|
* @return OS_STATUS
|
|
*
|
|
* @brief Read memory mapped i/o physical location
|
|
*
|
|
* <I>Special Notes</I>
|
|
*/
|
|
static OS_STATUS
|
|
lwpmudrv_Samp_Read_Num_Of_Core_Counters (
|
|
IOCTL_ARGS arg
|
|
)
|
|
{
|
|
#if defined(DRV_IA32) || defined(DRV_EM64T)
|
|
U64 rax, rbx, rcx, rdx,num_basic_functions;
|
|
U32 val = 0;
|
|
OS_STATUS status = OS_SUCCESS;
|
|
|
|
if (arg->r_len == 0 || arg->r_buf == NULL) {
|
|
return OS_FAULT;
|
|
}
|
|
|
|
UTILITY_Read_Cpuid(0x0,&num_basic_functions,&rbx, &rcx, &rdx);
|
|
|
|
if (num_basic_functions >= 0xA) {
|
|
UTILITY_Read_Cpuid(0xA,&rax,&rbx, &rcx, &rdx);
|
|
val = ((U32)(rax >> 8)) & 0xFF;
|
|
}
|
|
status = put_user(val, (int*)arg->r_buf);
|
|
SEP_PRINT_DEBUG("num of counter is %d\n",val);
|
|
return status;
|
|
#else
|
|
return OS_SUCCESS;
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/*!
|
|
* @fn static DRV_BOOL lwpmudrv_Is_Physical_Address_Free(U32 physical_addrss)
|
|
*
|
|
* @param physical_address - physical address
|
|
*
|
|
* @return DRV_BOOL
|
|
*
|
|
* @brief Check if physical address is available
|
|
*
|
|
* <I>Special Notes</I>
|
|
*/
|
|
static DRV_BOOL
|
|
lwpmudrv_Is_Physical_Address_Free (
|
|
U32 physical_address
|
|
)
|
|
{
|
|
U32 value;
|
|
U32 new_value;
|
|
U32 test_value;
|
|
|
|
if (physical_address == 0) {
|
|
return OS_FAULT;
|
|
}
|
|
if (GLOBAL_STATE_current_phase(driver_state) != DRV_STATE_IDLE) {
|
|
return FALSE;
|
|
}
|
|
// First attempt read
|
|
//
|
|
PCI_Read_From_Memory_Address(physical_address, &value);
|
|
|
|
// Value must be 0xFFFFFFFFF or there is NO chance
|
|
// that this memory location is available.
|
|
//
|
|
if (value != 0xFFFFFFFF) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Try to write a bit to a zero (this probably
|
|
// isn't too safe, but this is just for testing)
|
|
//
|
|
new_value = 0xFFFFFFFE;
|
|
PCI_Write_To_Memory_Address(physical_address, new_value);
|
|
PCI_Read_From_Memory_Address(physical_address, &test_value);
|
|
|
|
// Write back original
|
|
PCI_Write_To_Memory_Address(physical_address, value);
|
|
|
|
if (new_value == test_value) {
|
|
// The write appeared to change the
|
|
// memory, it must be mapped already
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
if (test_value == 0xFFFFFFFF) {
|
|
// The write did not change the bit, so
|
|
// apparently, this memory must not be mapped
|
|
// to anything.
|
|
//
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/*!
|
|
* @fn static OS_STATUS lwpmudrv_Samp_Find_Physical_Address(IOCTL_ARGS arg)
|
|
*
|
|
* @param arg - Pointer to the IOCTL structure
|
|
*
|
|
* @return OS_STATUS
|
|
*
|
|
* @brief Find a free physical address
|
|
*
|
|
* <I>Special Notes</I>
|
|
*/
|
|
static OS_STATUS
|
|
lwpmudrv_Samp_Find_Physical_Address (
|
|
IOCTL_ARGS arg
|
|
)
|
|
{
|
|
CHIPSET_PCI_SEARCH_ADDR_NODE user_addr;
|
|
CHIPSET_PCI_SEARCH_ADDR search_addr = (CHIPSET_PCI_SEARCH_ADDR)arg->w_buf;
|
|
U32 addr;
|
|
|
|
if (arg->r_len == 0 || arg->r_buf == NULL ||
|
|
arg->w_len == 0 || arg->w_buf == NULL) {
|
|
return OS_FAULT;
|
|
}
|
|
|
|
if (GLOBAL_STATE_current_phase(driver_state) != DRV_STATE_IDLE) {
|
|
return OS_IN_PROGRESS;
|
|
}
|
|
if (!search_addr) {
|
|
return OS_FAULT;
|
|
}
|
|
|
|
if (!access_ok(VERIFY_WRITE, search_addr, sizeof(CHIPSET_PCI_SEARCH_ADDR_NODE))) {
|
|
return OS_FAULT;
|
|
}
|
|
|
|
if (copy_from_user(&user_addr, search_addr, sizeof(CHIPSET_PCI_SEARCH_ADDR_NODE))) {
|
|
SEP_PRINT_DEBUG("lwpmudrv_Samp_Find_Physical_Address: copy_from_user() failed\n");
|
|
return OS_FAULT;
|
|
}
|
|
|
|
if (CHIPSET_PCI_SEARCH_ADDR_start(&user_addr) > CHIPSET_PCI_SEARCH_ADDR_stop(&user_addr)) {
|
|
return OS_INVALID;
|
|
}
|
|
|
|
CHIPSET_PCI_SEARCH_ADDR_address(&user_addr) = 0;
|
|
|
|
for (addr = CHIPSET_PCI_SEARCH_ADDR_start(&user_addr);
|
|
addr <= CHIPSET_PCI_SEARCH_ADDR_stop(&user_addr);
|
|
addr += CHIPSET_PCI_SEARCH_ADDR_increment(&user_addr)) {
|
|
SEP_PRINT_DEBUG("lwpmudrv_Samp_Find_Physical_Address: addr=%x:",addr);
|
|
if (lwpmudrv_Is_Physical_Address_Free(addr)) {
|
|
CHIPSET_PCI_SEARCH_ADDR_address(&user_addr) = addr;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (copy_to_user(arg->r_buf, (VOID *) &user_addr, sizeof(CHIPSET_PCI_SEARCH_ADDR_NODE))) {
|
|
SEP_PRINT_DEBUG("lwpmudrv_Samp_Find_Physical_Address: copy_to_user() failed\n");
|
|
}
|
|
|
|
return OS_SUCCESS;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/*!
|
|
* @fn static OS_STATUS lwpmudrv_Samp_Read_PCI_Config(IOCTL_ARGS arg)
|
|
*
|
|
* @param arg - Pointer to the IOCTL structure
|
|
*
|
|
* @return OS_STATUS
|
|
*
|
|
* @brief Read the PCI Configuration Space
|
|
*
|
|
* <I>Special Notes</I>
|
|
*/
|
|
static OS_STATUS
|
|
lwpmudrv_Samp_Read_PCI_Config (
|
|
IOCTL_ARGS arg
|
|
)
|
|
{
|
|
U32 pci_address;
|
|
CHIPSET_PCI_CONFIG rd_pci;
|
|
|
|
if (arg->r_len == 0 || arg->r_buf == NULL) {
|
|
SEP_PRINT_ERROR("lwpmudrv_Samp_Read_PCI_Config: null read buffer\n");
|
|
return OS_FAULT;
|
|
}
|
|
|
|
rd_pci = CONTROL_Allocate_Memory(arg->r_len);
|
|
if (rd_pci == NULL) {
|
|
SEP_PRINT_ERROR("lwpmudrv_Samp_Read_PCI_Config: unable to allocate local memory\n");
|
|
return OS_FAULT;
|
|
}
|
|
|
|
if (copy_from_user(rd_pci, (CHIPSET_PCI_CONFIG)arg->w_buf, sizeof(CHIPSET_PCI_CONFIG_NODE))) {
|
|
SEP_PRINT_ERROR("lwpmudrv_Samp_Read_PCI_Config: unable to read into local memory\n");
|
|
return OS_FAULT;
|
|
}
|
|
|
|
SEP_PRINT_DEBUG("lwpmudrv_Samp_Read_PCI_Config: reading PCI address:0x%x:0x%x:0x%x, offset 0x%x\n",
|
|
CHIPSET_PCI_CONFIG_bus(rd_pci),
|
|
CHIPSET_PCI_CONFIG_device(rd_pci),
|
|
CHIPSET_PCI_CONFIG_function(rd_pci),
|
|
CHIPSET_PCI_CONFIG_offset(rd_pci));
|
|
|
|
pci_address = FORM_PCI_ADDR(CHIPSET_PCI_CONFIG_bus(rd_pci),
|
|
CHIPSET_PCI_CONFIG_device(rd_pci),
|
|
CHIPSET_PCI_CONFIG_function(rd_pci),
|
|
CHIPSET_PCI_CONFIG_offset(rd_pci));
|
|
CHIPSET_PCI_CONFIG_value(rd_pci) = PCI_Read_Ulong(pci_address);
|
|
|
|
if (copy_to_user(arg->r_buf, (VOID *) rd_pci, sizeof(CHIPSET_PCI_CONFIG_NODE))) {
|
|
SEP_PRINT_ERROR("lwpmudrv_Samp_Read_PCI_Config: unable to copy to user\n");
|
|
return OS_FAULT;
|
|
}
|
|
|
|
SEP_PRINT_DEBUG("lwpmudrv_Samp_Read_PCI_Config: value at this PCI address:0x%x\n",
|
|
CHIPSET_PCI_CONFIG_value(rd_pci));
|
|
|
|
rd_pci = CONTROL_Free_Memory(rd_pci);
|
|
|
|
return OS_SUCCESS;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/*!
|
|
* @fn static OS_STATUS lwpmudrv_Samp_Write_PCI_Config(IOCTL_ARGS arg)
|
|
*
|
|
* @param arg - Pointer to the IOCTL structure
|
|
*
|
|
* @return OS_STATUS
|
|
*
|
|
* @brief Write to the PCI Configuration Space
|
|
*
|
|
* <I>Special Notes</I>
|
|
*/
|
|
static OS_STATUS
|
|
lwpmudrv_Samp_Write_PCI_Config (
|
|
IOCTL_ARGS arg
|
|
)
|
|
{
|
|
U32 pci_address;
|
|
CHIPSET_PCI_CONFIG wr_pci;
|
|
|
|
if (arg->w_len == 0 || arg->w_buf == NULL) {
|
|
SEP_PRINT_ERROR("lwpmudrv_Samp_Write_PCI_Config: null write buffer\n");
|
|
return OS_FAULT;
|
|
}
|
|
|
|
// the following allows "sep -el -pc" to work, since the command must access the
|
|
// the driver ioctls before driver is used for a collection
|
|
if (! (GLOBAL_STATE_current_phase(driver_state) == DRV_STATE_UNINITIALIZED ||
|
|
GLOBAL_STATE_current_phase(driver_state) == DRV_STATE_IDLE)) {
|
|
SEP_PRINT_ERROR("lwpmudrv_Samp_Write_PCI_Config: driver is non-idle or busy\n");
|
|
return OS_IN_PROGRESS;
|
|
}
|
|
|
|
wr_pci = (CHIPSET_PCI_CONFIG)arg->w_buf;
|
|
|
|
SEP_PRINT_DEBUG("lwpmudrv_Samp_Write_PCI_Config: writing 0x%x to PCI address:0x%x:0x%x:0x%x, offset 0x%x\n",
|
|
CHIPSET_PCI_CONFIG_bus(wr_pci),
|
|
CHIPSET_PCI_CONFIG_device(wr_pci),
|
|
CHIPSET_PCI_CONFIG_function(wr_pci),
|
|
CHIPSET_PCI_CONFIG_offset(wr_pci));
|
|
|
|
pci_address = FORM_PCI_ADDR(CHIPSET_PCI_CONFIG_bus(wr_pci),
|
|
CHIPSET_PCI_CONFIG_device(wr_pci),
|
|
CHIPSET_PCI_CONFIG_function(wr_pci),
|
|
CHIPSET_PCI_CONFIG_offset(wr_pci));
|
|
PCI_Write_Ulong(pci_address, CHIPSET_PCI_CONFIG_value(wr_pci));
|
|
|
|
return OS_SUCCESS;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/*!
|
|
* @fn static OS_STATUS lwpmudrv_Samp_Chipset_Init(IOCTL_ARGS arg)
|
|
*
|
|
* @param arg - Pointer to the IOCTL structure
|
|
*
|
|
* @return OS_STATUS
|
|
*
|
|
* @brief Initialize the chipset cnfiguration
|
|
*
|
|
* <I>Special Notes</I>
|
|
*/
|
|
static OS_STATUS
|
|
lwpmudrv_Samp_Chipset_Init (
|
|
IOCTL_ARGS arg
|
|
)
|
|
{
|
|
PVOID in_buf = arg->w_buf;
|
|
U32 in_buf_len = arg->w_len;
|
|
|
|
if (GLOBAL_STATE_current_phase(driver_state) != DRV_STATE_IDLE) {
|
|
SEP_PRINT_ERROR("lwpmudrv_Samp_Chipset_Init: driver is currently busy!\n");
|
|
return OS_IN_PROGRESS;
|
|
}
|
|
|
|
if (in_buf == NULL || in_buf_len == 0) {
|
|
SEP_PRINT_ERROR("lwpmudrv_Samp_Chipset_Init: Chipset information passed in is null\n");
|
|
return OS_NO_MEM;
|
|
}
|
|
|
|
// First things first: Make a copy of the data for global use.
|
|
pma = CONTROL_Allocate_Memory(in_buf_len);
|
|
|
|
if (pma == NULL) {
|
|
SEP_PRINT_ERROR("lwpmudrv_Samp_Chipset_Init: unable to allocate memory\n");
|
|
return OS_NO_MEM;
|
|
}
|
|
|
|
if (copy_from_user(pma, in_buf, in_buf_len)) {
|
|
SEP_PRINT_ERROR("lwpmudrv_Samp_Chipset_Init: unable to copy from user\n");
|
|
return OS_FAULT;
|
|
}
|
|
|
|
#if defined(MY_DEBUG)
|
|
|
|
SEP_PRINT("lwpmudrv_Samp_Chipset_Init: Chipset Configuration follows...\n");
|
|
SEP_PRINT("pma->length=%d\n", CHIPSET_CONFIG_length(pma));
|
|
SEP_PRINT("pma->version=%d\n", CHIPSET_CONFIG_major_version(pma));
|
|
SEP_PRINT("pma->processor=%d\n", CHIPSET_CONFIG_processor(pma));
|
|
SEP_PRINT("pma->mch_chipset=%d\n", CHIPSET_CONFIG_mch_chipset(pma));
|
|
SEP_PRINT("pma->ich_chipset=%d\n", CHIPSET_CONFIG_ich_chipset(pma));
|
|
SEP_PRINT("pma->gmch_chipset=%d\n", CHIPSET_CONFIG_gmch_chipset(pma));
|
|
SEP_PRINT("pma->mother_board_time=%d\n", CHIPSET_CONFIG_motherboard_time(pma));
|
|
SEP_PRINT("pma->host_proc_run=%d\n", CHIPSET_CONFIG_host_proc_run(pma));
|
|
SEP_PRINT("pma->noa_chipset=%d\n", CHIPSET_CONFIG_noa_chipset(pma));
|
|
SEP_PRINT("pma->bnb_chipset=%d\n", CHIPSET_CONFIG_bnb_chipset(pma));
|
|
|
|
if (CHIPSET_CONFIG_mch_chipset(pma)) {
|
|
SEP_PRINT("pma->mch->phys_add=0x%llx\n", CHIPSET_SEGMENT_physical_address(&CHIPSET_CONFIG_mch(pma)));
|
|
SEP_PRINT("pma->mch->size=%d\n", CHIPSET_SEGMENT_size(&CHIPSET_CONFIG_mch(pma)));
|
|
SEP_PRINT("pma->mch->num_counters=%d\n", CHIPSET_SEGMENT_num_counters(&CHIPSET_CONFIG_mch(pma)));
|
|
SEP_PRINT("pma->mch->total_events=%d\n", CHIPSET_SEGMENT_total_events(&CHIPSET_CONFIG_mch(pma)));
|
|
}
|
|
|
|
if (CHIPSET_CONFIG_ich_chipset(pma)) {
|
|
SEP_PRINT("pma->ich->phys_add=0x%llx\n", CHIPSET_SEGMENT_physical_address(&CHIPSET_CONFIG_ich(pma)));
|
|
SEP_PRINT("pma->ich->size=%d\n", CHIPSET_SEGMENT_size(&CHIPSET_CONFIG_ich(pma)));
|
|
SEP_PRINT("pma->ich->num_counters=%d\n", CHIPSET_SEGMENT_num_counters(&CHIPSET_CONFIG_ich(pma)));
|
|
SEP_PRINT("pma->ich->total_events=%d\n", CHIPSET_SEGMENT_total_events(&CHIPSET_CONFIG_ich(pma)));
|
|
}
|
|
|
|
if (CHIPSET_CONFIG_gmch_chipset(pma)) {
|
|
SEP_PRINT("pma->gmch->phys_add=0x%llx\n", CHIPSET_SEGMENT_physical_address(&CHIPSET_CONFIG_gmch(pma)));
|
|
SEP_PRINT("pma->gmch->size=%d\n", CHIPSET_SEGMENT_size(&CHIPSET_CONFIG_gmch(pma)));
|
|
SEP_PRINT("pma->gmch->num_counters=%d\n", CHIPSET_SEGMENT_num_counters(&CHIPSET_CONFIG_gmch(pma)));
|
|
SEP_PRINT("pma->gmch->total_events=%d\n", CHIPSET_SEGMENT_total_events(&CHIPSET_CONFIG_gmch(pma)));
|
|
SEP_PRINT("pma->gmch->read_register=0x%x\n", CHIPSET_SEGMENT_read_register(&CHIPSET_CONFIG_gmch(pma)));
|
|
SEP_PRINT("pma->gmch->write_register=0x%x\n", CHIPSET_SEGMENT_write_register(&CHIPSET_CONFIG_gmch(pma)));
|
|
}
|
|
|
|
#endif
|
|
|
|
// Set up the global cs_dispatch table
|
|
cs_dispatch = UTILITY_Configure_Chipset();
|
|
if (cs_dispatch == NULL) {
|
|
SEP_PRINT_ERROR("lwpmudrv_Samp_Chipset_Init: unknown chipset family\n");
|
|
return OS_INVALID;
|
|
}
|
|
|
|
// Initialize chipset configuration
|
|
if (cs_dispatch->init_chipset()) {
|
|
SEP_PRINT_ERROR("lwpmudrv_Samp_Chipset_Init: failed to initialize the chipset\n");
|
|
return OS_INVALID;
|
|
}
|
|
|
|
return OS_SUCCESS;
|
|
}
|
|
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/*!
|
|
* @fn static OS_STATUS lwpmudrv_Get_Platform_Info(IOCTL_ARGS arg)
|
|
*
|
|
* @param arg - Pointer to the IOCTL structure
|
|
*
|
|
* @return OS_STATUS
|
|
*
|
|
* @brief Reads the MSR_PLATFORM_INFO register if present
|
|
*
|
|
* <I>Special Notes</I>
|
|
*/
|
|
static OS_STATUS
|
|
lwpmudrv_Get_Platform_Info (
|
|
IOCTL_ARGS args
|
|
)
|
|
{
|
|
U32 size = sizeof(DRV_PLATFORM_INFO_NODE);
|
|
OS_STATUS status = OS_SUCCESS;
|
|
DRV_PLATFORM_INFO platform_data = NULL;
|
|
U32 *dispatch_ids = NULL;
|
|
DISPATCH dispatch_ptr = NULL;
|
|
U32 i = 0;
|
|
U32 num_entries = args->r_len/sizeof(U32); // # dispatch ids to process
|
|
|
|
if (!platform_data) {
|
|
platform_data = CONTROL_Allocate_Memory(sizeof(DRV_PLATFORM_INFO_NODE));
|
|
if (!platform_data) {
|
|
return OS_NO_MEM;
|
|
}
|
|
}
|
|
|
|
memset(platform_data, 0, sizeof(DRV_PLATFORM_INFO_NODE));
|
|
if (args->r_len > 0 && args->r_buf != NULL) {
|
|
dispatch_ids = CONTROL_Allocate_Memory(args->r_len);
|
|
if (!dispatch_ids) {
|
|
return OS_NO_MEM;
|
|
}
|
|
|
|
status = copy_from_user(dispatch_ids, args->r_buf, args->r_len);
|
|
if (status) {
|
|
return status;
|
|
}
|
|
for (i = 0; i < num_entries; i++) {
|
|
if (dispatch_ids[i] != -1) {
|
|
dispatch_ptr = UTILITY_Configure_CPU(dispatch_ids[i]);
|
|
if (dispatch_ptr &&
|
|
dispatch_ptr->platform_info) {
|
|
dispatch_ptr->platform_info((PVOID)platform_data);
|
|
}
|
|
}
|
|
}
|
|
dispatch_ids = CONTROL_Free_Memory(dispatch_ids);
|
|
}
|
|
else if (dispatch && dispatch->platform_info) {
|
|
dispatch->platform_info((PVOID)platform_data);
|
|
}
|
|
|
|
if (args->w_len < size || args->w_buf == NULL) {
|
|
return OS_FAULT;
|
|
}
|
|
|
|
status = copy_to_user(args->w_buf, platform_data, size);
|
|
platform_data = CONTROL_Free_Memory(platform_data);
|
|
|
|
return status;
|
|
}
|
|
/* ------------------------------------------------------------------------- */
|
|
/*!
|
|
* @fn void lwpmudrv_Setup_Cpu_Topology (value)
|
|
*
|
|
* @brief Sets up the per CPU state structures
|
|
*
|
|
* @param IOCTL_ARGS args
|
|
*
|
|
* @return OS_STATUS
|
|
*
|
|
* <I>Special Notes:</I>
|
|
* This function was added to support abstract dll creation. Use
|
|
* this function to set the value of abnormal_terminate ouside of
|
|
* sep_common.
|
|
*/
|
|
static OS_STATUS
|
|
lwpmudrv_Setup_Cpu_Topology (
|
|
IOCTL_ARGS args
|
|
)
|
|
{
|
|
S32 cpu_num;
|
|
S32 iter;
|
|
DRV_TOPOLOGY_INFO drv_topology, dt;
|
|
|
|
if (GLOBAL_STATE_current_phase(driver_state) != DRV_STATE_IDLE) {
|
|
return OS_IN_PROGRESS;
|
|
}
|
|
if (args->w_len == 0 || args->w_buf == NULL || pcb == NULL ) {
|
|
SEP_PRINT_ERROR("topology information has been misconfigured\n");
|
|
return OS_NO_MEM;
|
|
}
|
|
|
|
drv_topology = CONTROL_Allocate_Memory(args->w_len);
|
|
if (drv_topology == NULL) {
|
|
return OS_NO_MEM;
|
|
}
|
|
|
|
if (copy_from_user(drv_topology, (DRV_TOPOLOGY_INFO)(args->w_buf), args->w_len)) {
|
|
return OS_FAULT;
|
|
}
|
|
/*
|
|
* Topology Initializations
|
|
*/
|
|
num_packages = 0;
|
|
for (iter = 0; iter < GLOBAL_STATE_num_cpus(driver_state); iter++) {
|
|
dt = &drv_topology[iter];
|
|
cpu_num = DRV_TOPOLOGY_INFO_cpu_number(dt);
|
|
CPU_STATE_socket_master(&pcb[cpu_num]) = DRV_TOPOLOGY_INFO_socket_master(dt);
|
|
num_packages += CPU_STATE_socket_master(&pcb[cpu_num]);
|
|
CPU_STATE_core_master(&pcb[cpu_num]) = DRV_TOPOLOGY_INFO_core_master(dt);
|
|
CPU_STATE_thr_master(&pcb[cpu_num]) = DRV_TOPOLOGY_INFO_thr_master(dt);
|
|
CPU_STATE_cpu_module_num(&pcb[cpu_num]) = (U16)DRV_TOPOLOGY_INFO_cpu_module_num(&drv_topology[iter]);
|
|
CPU_STATE_cpu_module_master(&pcb[cpu_num]) = (U16)DRV_TOPOLOGY_INFO_cpu_module_master(&drv_topology[iter]);
|
|
SEP_PRINT_DEBUG("cpu %d sm = %d cm = %d tm = %d\n",
|
|
cpu_num,
|
|
CPU_STATE_socket_master(&pcb[cpu_num]),
|
|
CPU_STATE_core_master(&pcb[cpu_num]),
|
|
CPU_STATE_thr_master(&pcb[cpu_num]));
|
|
}
|
|
drv_topology = CONTROL_Free_Memory(drv_topology);
|
|
|
|
return OS_SUCCESS;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/*!
|
|
* @fn static OS_STATUS lwpmudrv_Get_Num_Samples(IOCTL_ARGS arg)
|
|
*
|
|
* @param arg - Pointer to the IOCTL structure
|
|
*
|
|
* @return OS_STATUS
|
|
*
|
|
* @brief Returns the number of samples collected during the current
|
|
* @brief sampling run
|
|
*
|
|
* <I>Special Notes</I>
|
|
*/
|
|
static OS_STATUS
|
|
lwpmudrv_Get_Num_Samples (
|
|
IOCTL_ARGS args
|
|
)
|
|
{
|
|
S32 cpu_num;
|
|
U64 samples = 0;
|
|
|
|
if (pcb == NULL) {
|
|
SEP_PRINT_ERROR("PCB was not initialized\n");
|
|
return OS_FAULT;
|
|
}
|
|
if (args->r_len == 0 || args->r_buf == NULL) {
|
|
SEP_PRINT_ERROR("topology information has been misconfigured\n");
|
|
return OS_NO_MEM;
|
|
}
|
|
|
|
for (cpu_num = 0; cpu_num < GLOBAL_STATE_num_cpus(driver_state); cpu_num++) {
|
|
samples += CPU_STATE_num_samples(&pcb[cpu_num]);
|
|
|
|
SEP_PRINT_DEBUG("Samples for cpu %d = %lld\n",
|
|
cpu_num,
|
|
CPU_STATE_num_samples(&pcb[cpu_num]));
|
|
}
|
|
SEP_PRINT_DEBUG("Total number of samples %lld\n", samples);
|
|
return put_user(samples, (U64*)args->r_buf);
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/*!
|
|
* @fn static OS_STATUS lwpmudrv_Set_Device_Num_Units(IOCTL_ARGS arg)
|
|
*
|
|
* @param arg - Pointer to the IOCTL structure
|
|
*
|
|
* @return OS_STATUS
|
|
*
|
|
* @brief Set the number of devices for the sampling run
|
|
*
|
|
* <I>Special Notes</I>
|
|
*/
|
|
static OS_STATUS
|
|
lwpmudrv_Set_Device_Num_Units (
|
|
IOCTL_ARGS args
|
|
)
|
|
{
|
|
SEP_PRINT_DEBUG("Entered lwpmudrv_Set_Device_Num_Units\n");
|
|
|
|
if (GLOBAL_STATE_current_phase(driver_state) != DRV_STATE_IDLE) {
|
|
|
|
return OS_SUCCESS;
|
|
}
|
|
if (args->w_len == 0 || args->w_buf == NULL) {
|
|
return OS_NO_MEM;
|
|
}
|
|
|
|
LWPMU_DEVICE_num_units(&devices[cur_device]) = *(U32 *)args->w_buf;
|
|
SEP_PRINT_DEBUG("LWP: num_units = %d cur_device = %d\n",
|
|
LWPMU_DEVICE_num_units(&devices[cur_device]),
|
|
cur_device);
|
|
// on to the next device.
|
|
cur_device++;
|
|
|
|
return OS_SUCCESS;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/*!
|
|
* @fn static OS_STATUS lwpmudrv_Get_Interval_Counts(IOCTL_ARGS arg)
|
|
*
|
|
* @param arg - Pointer to the IOCTL structure
|
|
*
|
|
* @return OS_STATUS
|
|
*
|
|
* @brief Returns the number of samples collected during the current
|
|
* @brief sampling run
|
|
*
|
|
* <I>Special Notes</I>
|
|
*/
|
|
static OS_STATUS
|
|
lwpmudrv_Get_Interval_Counts (
|
|
IOCTL_ARGS args
|
|
)
|
|
{
|
|
if (!DRV_CONFIG_enable_cp_mode(pcfg)) {
|
|
SEP_PRINT_ERROR("[lwpmudrv_Get_Interval_Counts] Not in CP mode!\n");
|
|
return OS_FAULT;
|
|
}
|
|
if (pcb == NULL) {
|
|
SEP_PRINT_ERROR("PCB was not initialized\n");
|
|
return OS_FAULT;
|
|
}
|
|
if (args->r_len == 0 || args->r_buf == NULL) {
|
|
SEP_PRINT_ERROR("Interval Counts information has been misconfigured\n");
|
|
return OS_NO_MEM;
|
|
}
|
|
if (!interrupt_counts) {
|
|
return OS_FAULT;
|
|
}
|
|
|
|
if (copy_to_user(args->r_buf, interrupt_counts, args->r_len)) {
|
|
return OS_FAULT;
|
|
}
|
|
return OS_SUCCESS;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/*!
|
|
* @fn U64 lwpmudrv_Set_Uncore_Topology_Info_And_Scan
|
|
*
|
|
* @brief Reads the MSR_PLATFORM_INFO register if present
|
|
*
|
|
* @param arg Pointer to the IOCTL structure
|
|
*
|
|
* @return status
|
|
*
|
|
* <I>Special Notes:</I>
|
|
* <NONE>
|
|
*/
|
|
static OS_STATUS
|
|
lwpmudrv_Set_Uncore_Topology_Info_And_Scan (
|
|
IOCTL_ARGS args
|
|
)
|
|
{
|
|
return OS_SUCCESS;
|
|
}
|
|
/* ------------------------------------------------------------------------- */
|
|
/*!
|
|
* @fn U64 lwpmudrv_Get_Uncore_Topology
|
|
*
|
|
* @brief Reads the MSR_PLATFORM_INFO register if present
|
|
*
|
|
* @param arg Pointer to the IOCTL structure
|
|
*
|
|
* @return status
|
|
*
|
|
* <I>Special Notes:</I>
|
|
* <NONE>
|
|
*/
|
|
static OS_STATUS
|
|
lwpmudrv_Get_Uncore_Topology (
|
|
IOCTL_ARGS args
|
|
)
|
|
{
|
|
|
|
DISPATCH temp_dispatch;
|
|
U32 dev;
|
|
U32 temp_dispatch_id;
|
|
static UNCORE_TOPOLOGY_INFO_NODE req_uncore_topology;
|
|
|
|
if (args->r_buf == NULL) {
|
|
return OS_FAULT;
|
|
}
|
|
if (args->r_len != sizeof(UNCORE_TOPOLOGY_INFO_NODE)) {
|
|
return OS_FAULT;
|
|
}
|
|
|
|
memset((char *)&req_uncore_topology, 0, sizeof(UNCORE_TOPOLOGY_INFO_NODE));
|
|
if (copy_from_user(&req_uncore_topology, args->r_buf, args->r_len)) {
|
|
return OS_FAULT;
|
|
}
|
|
|
|
for (dev = 0; dev < MAX_DEVICES; dev++) {
|
|
// skip if user does not require to scan this device
|
|
if (!UNCORE_TOPOLOGY_INFO_device_scan(&req_uncore_topology, dev)) {
|
|
continue;
|
|
}
|
|
// skip if this device has been discovered
|
|
if (UNCORE_TOPOLOGY_INFO_device_scan(&uncore_topology, dev)) {
|
|
continue;
|
|
}
|
|
memcpy((U8 *)&(UNCORE_TOPOLOGY_INFO_device(&uncore_topology, dev)),
|
|
(U8 *)&(UNCORE_TOPOLOGY_INFO_device(&req_uncore_topology, dev)),
|
|
sizeof(UNCORE_PCIDEV_NODE));
|
|
temp_dispatch_id = UNCORE_TOPOLOGY_INFO_device_dispatch_id(&uncore_topology, dev);
|
|
temp_dispatch = UTILITY_Configure_CPU(temp_dispatch_id);
|
|
if (temp_dispatch && temp_dispatch->scan_for_uncore) {
|
|
temp_dispatch->scan_for_uncore((VOID*)&dev);
|
|
}
|
|
}
|
|
|
|
if (copy_to_user(args->r_buf, &uncore_topology, args->r_len)) {
|
|
return OS_FAULT;
|
|
}
|
|
|
|
return OS_SUCCESS;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* External Driver functions - Open
|
|
* This function is common to all drivers
|
|
*******************************************************************************/
|
|
|
|
static int
|
|
lwpmu_Open (
|
|
struct inode *inode,
|
|
struct file *filp
|
|
)
|
|
{
|
|
SEP_PRINT_DEBUG("lwpmu_Open called on maj:%d, min:%d\n",
|
|
imajor(inode), iminor(inode));
|
|
filp->private_data = container_of(inode->i_cdev, LWPMU_DEV_NODE, cdev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* External Driver functions
|
|
* These functions are registered into the file operations table that
|
|
* controls this device.
|
|
* Open, Close, Read, Write, Release
|
|
*******************************************************************************/
|
|
|
|
static ssize_t
|
|
lwpmu_Read (
|
|
struct file *filp,
|
|
char *buf,
|
|
size_t count,
|
|
loff_t *f_pos
|
|
)
|
|
{
|
|
unsigned long retval;
|
|
|
|
/* Transfering data to user space */
|
|
SEP_PRINT_DEBUG("lwpmu_Read dispatched with count=%d\n", (S32)count);
|
|
if (copy_to_user(buf, &LWPMU_DEV_buffer(lwpmu_control), 1)) {
|
|
retval = OS_FAULT;
|
|
return retval;
|
|
}
|
|
/* Changing reading position as best suits */
|
|
if (*f_pos == 0) {
|
|
*f_pos+=1;
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t
|
|
lwpmu_Write (
|
|
struct file *filp,
|
|
const char *buf,
|
|
size_t count,
|
|
loff_t *f_pos
|
|
)
|
|
{
|
|
unsigned long retval;
|
|
|
|
SEP_PRINT_DEBUG("lwpmu_Write dispatched with count=%d\n", (S32)count);
|
|
if (copy_from_user(&LWPMU_DEV_buffer(lwpmu_control), buf+count-1, 1)) {
|
|
retval = OS_FAULT;
|
|
return retval;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/*!
|
|
* @fn extern IOCTL_OP_TYPE lwpmu_Service_IOCTL(IOCTL_USE_NODE, filp, cmd, arg)
|
|
*
|
|
* @param IOCTL_USE_INODE - Used for pre 2.6.32 kernels
|
|
* @param struct file *filp - file pointer
|
|
* @param unsigned int cmd - IOCTL command
|
|
* @param unsigned long arg - args to the IOCTL command
|
|
*
|
|
* @return OS_STATUS
|
|
*
|
|
* @brief SEP Worker function that handles IOCTL requests from the user mode.
|
|
*
|
|
* <I>Special Notes</I>
|
|
*/
|
|
extern IOCTL_OP_TYPE
|
|
lwpmu_Service_IOCTL (
|
|
IOCTL_USE_INODE
|
|
struct file *filp,
|
|
unsigned int cmd,
|
|
IOCTL_ARGS_NODE local_args
|
|
)
|
|
{
|
|
int status = OS_SUCCESS;
|
|
|
|
if (cmd == DRV_OPERATION_GET_DRIVER_STATE) {
|
|
SEP_PRINT_DEBUG("DRV_OPERATION_GET_DRIVER_STATE\n");
|
|
status = lwpmudrv_Get_Driver_State(&local_args);
|
|
return status;
|
|
}
|
|
|
|
MUTEX_LOCK(ioctl_lock);
|
|
switch (cmd) {
|
|
|
|
/*
|
|
* Common IOCTL commands
|
|
*/
|
|
|
|
case DRV_OPERATION_VERSION:
|
|
SEP_PRINT_DEBUG("DRV_OPERATION_VERSION\n");
|
|
status = lwpmudrv_Version(&local_args);
|
|
break;
|
|
|
|
case DRV_OPERATION_RESERVE:
|
|
SEP_PRINT_DEBUG("DRV_OPERATION_RESERVE\n");
|
|
status = lwpmudrv_Reserve(&local_args);
|
|
break;
|
|
|
|
case DRV_OPERATION_INIT:
|
|
SEP_PRINT_DEBUG("DRV_OPERATION_INIT\n");
|
|
status = lwpmudrv_Initialize(local_args.w_buf, local_args.w_len);
|
|
break;
|
|
|
|
case DRV_OPERATION_INIT_PMU:
|
|
SEP_PRINT_DEBUG("DRV_OPERATION_INIT_PMU\n");
|
|
status = lwpmudrv_Init_PMU(&local_args);
|
|
break;
|
|
|
|
case DRV_OPERATION_SET_CPU_MASK:
|
|
SEP_PRINT_DEBUG("DRV_OPERATION_SET_CPU_MASK\n");
|
|
status = lwpmudrv_Set_CPU_Mask(local_args.w_buf, local_args.w_len);
|
|
break;
|
|
|
|
case DRV_OPERATION_START:
|
|
SEP_PRINT_DEBUG("DRV_OPERATION_START\n");
|
|
status = lwpmudrv_Start();
|
|
break;
|
|
|
|
case DRV_OPERATION_STOP:
|
|
SEP_PRINT_DEBUG("DRV_OPERATION_STOP\n");
|
|
status = lwpmudrv_Prepare_Stop();
|
|
break;
|
|
|
|
case DRV_OPERATION_PAUSE:
|
|
SEP_PRINT_DEBUG("DRV_OPERATION_PAUSE\n");
|
|
status = lwpmudrv_Pause();
|
|
break;
|
|
|
|
case DRV_OPERATION_RESUME:
|
|
SEP_PRINT_DEBUG("DRV_OPERATION_RESUME\n");
|
|
status = lwpmudrv_Resume();
|
|
break;
|
|
|
|
case DRV_OPERATION_EM_GROUPS:
|
|
SEP_PRINT_DEBUG("DRV_OPERATION_EM_GROUPS\n");
|
|
status = lwpmudrv_Set_EM_Config(&local_args);
|
|
break;
|
|
|
|
case DRV_OPERATION_EM_CONFIG_NEXT:
|
|
SEP_PRINT_DEBUG("DRV_OPERATION_EM_CONFIG_NEXT\n");
|
|
status = lwpmudrv_Configure_Events(&local_args);
|
|
break;
|
|
|
|
case DRV_OPERATION_NUM_DESCRIPTOR:
|
|
SEP_PRINT_DEBUG("DRV_OPERATION_NUM_DESCRIPTOR\n");
|
|
status = lwpmudrv_Set_Sample_Descriptors(&local_args);
|
|
break;
|
|
|
|
case DRV_OPERATION_DESC_NEXT:
|
|
SEP_PRINT_DEBUG("DRV_OPERATION_DESC_NEXT\n");
|
|
status = lwpmudrv_Configure_Descriptors(&local_args);
|
|
break;
|
|
|
|
case DRV_OPERATION_GET_NORMALIZED_TSC:
|
|
SEP_PRINT_DEBUG("DRV_OPERATION_GET_NORMALIZED_TSC\n");
|
|
status = lwpmudrv_Get_Normalized_TSC(&local_args);
|
|
break;
|
|
|
|
case DRV_OPERATION_GET_NORMALIZED_TSC_STANDALONE:
|
|
SEP_PRINT_DEBUG("DRV_OPERATION_GET_NORMALIZED_TSC_STANDALONE\n");
|
|
status = lwpmudrv_Get_Normalized_TSC(&local_args);
|
|
break;
|
|
|
|
case DRV_OPERATION_NUM_CORES:
|
|
SEP_PRINT_DEBUG("DRV_OPERATION_NUM_CORES\n");
|
|
status = lwpmudrv_Get_Num_Cores(&local_args);
|
|
break;
|
|
|
|
case DRV_OPERATION_KERNEL_CS:
|
|
#if defined(DRV_IA32) || defined(DRV_EM64T)
|
|
SEP_PRINT_DEBUG("DRV_OPERATION_KERNEL_CS\n");
|
|
status = lwpmudrv_Get_KERNEL_CS(&local_args);
|
|
#endif
|
|
break;
|
|
|
|
case DRV_OPERATION_SET_UID:
|
|
SEP_PRINT_DEBUG("DRV_OPERATION_SET_UID\n");
|
|
status = lwpmudrv_Set_UID(&local_args);
|
|
break;
|
|
|
|
case DRV_OPERATION_TSC_SKEW_INFO:
|
|
SEP_PRINT_DEBUG("DRV_OPERATION_TSC_SKEW_INFO\n");
|
|
status = lwpmudrv_Get_TSC_Skew_Info(&local_args);
|
|
break;
|
|
|
|
case DRV_OPERATION_COLLECT_SYS_CONFIG:
|
|
SEP_PRINT_DEBUG("DRV_OPERATION_COLLECT_SYS_CONFIG\n");
|
|
status = lwpmudrv_Collect_Sys_Config(&local_args);
|
|
break;
|
|
|
|
case DRV_OPERATION_GET_SYS_CONFIG:
|
|
SEP_PRINT_DEBUG("DRV_OPERATION_GET_SYS_CONFIG\n");
|
|
status = lwpmudrv_Sys_Config(&local_args);
|
|
break;
|
|
|
|
case DRV_OPERATION_TERMINATE:
|
|
SEP_PRINT_DEBUG("DRV_OPERATION_TERMINATE\n");
|
|
status = lwpmudrv_Terminate();
|
|
break;
|
|
|
|
case DRV_OPERATION_SET_CPU_TOPOLOGY:
|
|
SEP_PRINT_DEBUG("DRV_OPERATION_SET_CPU_TOPOLOGY\n");
|
|
status = lwpmudrv_Setup_Cpu_Topology(&local_args);
|
|
break;
|
|
|
|
case DRV_OPERATION_GET_NUM_CORE_CTRS:
|
|
SEP_PRINT_DEBUG("DRV_OPERATION_GET_NUM_CORE_CTRS\n");
|
|
status = lwpmudrv_Samp_Read_Num_Of_Core_Counters(&local_args);
|
|
break;
|
|
|
|
case DRV_OPERATION_GET_PLATFORM_INFO:
|
|
SEP_PRINT_DEBUG("DRV_OPERATION_GET_PLATFORM_INFO\n");
|
|
status = lwpmudrv_Get_Platform_Info(&local_args);
|
|
break;
|
|
|
|
case DRV_OPERATION_READ_MSRS:
|
|
SEP_PRINT_DEBUG("DRV_OPERATION_READ_MSRs\n");
|
|
status = lwpmudrv_Read_MSRs(&local_args);
|
|
break;
|
|
|
|
case DRV_OPERATION_SWITCH_GROUP:
|
|
SEP_PRINT_DEBUG("DRV_OPERATION_SWITCH_GROUP\n");
|
|
status = lwpmudrv_Switch_Group();
|
|
break;
|
|
|
|
/*
|
|
* EMON-specific IOCTL commands
|
|
*/
|
|
|
|
case DRV_OPERATION_READ_MSR:
|
|
SEP_PRINT_DEBUG("DRV_OPERATION_READ_MSR\n");
|
|
status = lwpmudrv_Read_MSR_All_Cores(&local_args);
|
|
break;
|
|
|
|
#if defined(EMON)
|
|
|
|
case DRV_OPERATION_READ_SWITCH_GROUP:
|
|
SEP_PRINT_DEBUG("DRV_OPERATION_READ_SWITCH_GROUP\n");
|
|
status = lwpmudrv_Read_Counters_And_Switch_Group(&local_args);
|
|
break;
|
|
|
|
case DRV_OPERATION_READ_AND_RESET:
|
|
SEP_PRINT_DEBUG("DRV_OPERATION_READ_AND_RESET\n");
|
|
status = lwpmudrv_Read_And_Reset_Counters(&local_args);
|
|
break;
|
|
#endif
|
|
|
|
|
|
/*
|
|
* Platform-specific IOCTL commands (IA32 and Intel64)
|
|
*/
|
|
|
|
#if defined(DRV_IA32) || defined(DRV_EM64T)
|
|
case DRV_OPERATION_INIT_UNC:
|
|
SEP_PRINT_DEBUG("DRV_OPERATION_INIT_UNC\n");
|
|
status = lwpmudrv_Initialize_UNC(local_args.w_buf, local_args.w_len);
|
|
break;
|
|
|
|
case DRV_OPERATION_EM_GROUPS_UNC:
|
|
SEP_PRINT_DEBUG("DRV_OPERATION_EM_GROUPS_UNC\n");
|
|
status = lwpmudrv_Set_EM_Config_UNC(&local_args);
|
|
break;
|
|
|
|
case DRV_OPERATION_EM_CONFIG_NEXT_UNC:
|
|
SEP_PRINT_DEBUG("DRV_OPERATION_EM_CONFIG_NEXT_UNC\n");
|
|
status = lwpmudrv_Configure_Events_UNC(&local_args);
|
|
break;
|
|
|
|
case DRV_OPERATION_LBR_INFO:
|
|
SEP_PRINT_DEBUG("DRV_OPERATION_LBR_INFO\n");
|
|
status = lwpmudrv_LBR_Info(&local_args);
|
|
break;
|
|
|
|
case DRV_OPERATION_PWR_INFO:
|
|
SEP_PRINT_DEBUG("DRV_OPERATION_PWR_INFO\n");
|
|
status = lwpmudrv_PWR_Info(&local_args);
|
|
break;
|
|
|
|
case DRV_OPERATION_INIT_NUM_DEV:
|
|
SEP_PRINT_DEBUG("DRV_OPERATION_INIT_NUM_DEV\n");
|
|
status = lwpmudrv_Initialize_Num_Devices(&local_args);
|
|
break;
|
|
#endif
|
|
case DRV_OPERATION_GET_NUM_SAMPLES:
|
|
SEP_PRINT_DEBUG("DRV_OPERATION_GET_NUM_SAMPLES\n");
|
|
status = lwpmudrv_Get_Num_Samples(&local_args);
|
|
break;
|
|
|
|
case DRV_OPERATION_SET_DEVICE_NUM_UNITS:
|
|
SEP_PRINT_DEBUG("DRV_OPERATION_SET_DEVICE_NUM_UNITS\n");
|
|
status = lwpmudrv_Set_Device_Num_Units(&local_args);
|
|
break;
|
|
|
|
case DRV_OPERATION_TIMER_TRIGGER_READ:
|
|
lwpmudrv_Trigger_Read();
|
|
break;
|
|
|
|
case DRV_OPERATION_GET_INTERVAL_COUNTS:
|
|
SEP_PRINT_DEBUG("DRV_OPERATION_GET_INTERVAL_COUNTS\n");
|
|
lwpmudrv_Get_Interval_Counts(&local_args);
|
|
break;
|
|
|
|
case DRV_OPERATION_SET_SCAN_UNCORE_TOPOLOGY_INFO:
|
|
SEP_PRINT_DEBUG("LWPMUDRV_IOCTL_SET_SCAN_UNCORE_TOPOLOGY_INFO\n");
|
|
status = lwpmudrv_Set_Uncore_Topology_Info_And_Scan(&local_args);
|
|
break;
|
|
|
|
case DRV_OPERATION_GET_UNCORE_TOPOLOGY:
|
|
SEP_PRINT_DEBUG("LWPMUDRV_IOCTL_GET_UNCORE_TOPOLOGY\n");
|
|
status = lwpmudrv_Get_Uncore_Topology(&local_args);
|
|
break;
|
|
|
|
/*
|
|
* Graphics IOCTL commands
|
|
*/
|
|
|
|
|
|
/*
|
|
* Chipset IOCTL commands
|
|
*/
|
|
|
|
case DRV_OPERATION_PCI_READ:
|
|
{
|
|
CHIPSET_PCI_ARG_NODE pci_data;
|
|
|
|
SEP_PRINT_DEBUG("DRV_OPERATION_PCI_READ\n");
|
|
if (copy_from_user(&pci_data, (CHIPSET_PCI_ARG)local_args.w_buf, sizeof(CHIPSET_PCI_ARG_NODE))) {
|
|
status = OS_FAULT;
|
|
goto cleanup;
|
|
}
|
|
|
|
status = PCI_Read_From_Memory_Address(CHIPSET_PCI_ARG_address(&pci_data),
|
|
&CHIPSET_PCI_ARG_value(&pci_data));
|
|
|
|
if (copy_to_user(local_args.r_buf, &pci_data, sizeof(CHIPSET_PCI_ARG_NODE))) {
|
|
status = OS_FAULT;
|
|
goto cleanup;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case DRV_OPERATION_PCI_WRITE:
|
|
{
|
|
CHIPSET_PCI_ARG_NODE pci_data;
|
|
|
|
SEP_PRINT_DEBUG("DRV_OPERATION_PCI_WRITE\n");
|
|
|
|
if (copy_from_user(&pci_data, (CHIPSET_PCI_ARG)local_args.w_buf, sizeof(CHIPSET_PCI_ARG_NODE))) {
|
|
status = OS_FAULT;
|
|
goto cleanup;
|
|
}
|
|
|
|
status = PCI_Write_To_Memory_Address(CHIPSET_PCI_ARG_address(&pci_data),
|
|
CHIPSET_PCI_ARG_value(&pci_data));
|
|
break;
|
|
}
|
|
|
|
case DRV_OPERATION_FD_PHYS:
|
|
SEP_PRINT_DEBUG("DRV_OPERATION_FD_PHYS\n");
|
|
status = lwpmudrv_Samp_Find_Physical_Address(&local_args);
|
|
break;
|
|
|
|
case DRV_OPERATION_READ_PCI_CONFIG:
|
|
SEP_PRINT_DEBUG("DRV_OPERATION_READ_PCI_CONFIG\n");
|
|
status = lwpmudrv_Samp_Read_PCI_Config(&local_args);
|
|
break;
|
|
|
|
case DRV_OPERATION_WRITE_PCI_CONFIG:
|
|
SEP_PRINT_DEBUG("DRV_OPERATION_WRITE_PCI_CONFIG\n");
|
|
status = lwpmudrv_Samp_Write_PCI_Config(&local_args);
|
|
break;
|
|
|
|
case DRV_OPERATION_CHIPSET_INIT:
|
|
SEP_PRINT_DEBUG("DRV_OPERATION_CHIPSET_INIT\n");
|
|
SEP_PRINT_DEBUG("lwpmudrv_Device_Control: enable_chipset=%d\n",
|
|
(int)DRV_CONFIG_enable_chipset(pcfg));
|
|
status = lwpmudrv_Samp_Chipset_Init(&local_args);
|
|
break;
|
|
|
|
case DRV_OPERATION_GET_CHIPSET_DEVICE_ID:
|
|
SEP_PRINT_DEBUG("DRV_OPERATION_GET_CHIPSET_DEVICE_ID\n");
|
|
status = lwpmudrv_Samp_Read_PCI_Config(&local_args);
|
|
break;
|
|
|
|
/*
|
|
* if none of the above, treat as unknown/illegal IOCTL command
|
|
*/
|
|
|
|
default:
|
|
SEP_PRINT_ERROR("Unknown IOCTL number:%d\n", cmd);
|
|
status = OS_ILLEGAL_IOCTL;
|
|
break;
|
|
}
|
|
cleanup:
|
|
MUTEX_UNLOCK(ioctl_lock);
|
|
if (cmd == DRV_OPERATION_STOP &&
|
|
GLOBAL_STATE_current_phase(driver_state) == DRV_STATE_PREPARE_STOP) {
|
|
status = lwpmudrv_Finish_Stop();
|
|
if (status == OS_SUCCESS) {
|
|
// if stop was successful, relevant memory should have been freed,
|
|
// so try to compact the memory tracker
|
|
CONTROL_Memory_Tracker_Compaction();
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
extern long
|
|
lwpmu_Device_Control (
|
|
IOCTL_USE_INODE
|
|
struct file *filp,
|
|
unsigned int cmd,
|
|
unsigned long arg
|
|
)
|
|
{
|
|
int status = OS_SUCCESS;
|
|
IOCTL_ARGS_NODE local_args;
|
|
|
|
#if !defined(DRV_USE_UNLOCKED_IOCTL)
|
|
SEP_PRINT_DEBUG("lwpmu_DeviceControl(0x%x) called on inode maj:%d, min:%d\n",
|
|
cmd, imajor(inode), iminor(inode));
|
|
#endif
|
|
SEP_PRINT_DEBUG("type: %d, subcommand: %d\n", _IOC_TYPE(cmd), _IOC_NR(cmd));
|
|
|
|
if (_IOC_TYPE(cmd) != LWPMU_IOC_MAGIC) {
|
|
SEP_PRINT_ERROR("Unknown IOCTL magic:%d\n", _IOC_TYPE(cmd));
|
|
return OS_ILLEGAL_IOCTL;
|
|
}
|
|
|
|
if (arg) {
|
|
status = copy_from_user(&local_args, (IOCTL_ARGS)arg, sizeof(IOCTL_ARGS_NODE));
|
|
}
|
|
|
|
status = lwpmu_Service_IOCTL (IOCTL_USE_INODE filp, _IOC_NR(cmd), local_args);
|
|
|
|
return status;
|
|
}
|
|
|
|
#if defined(HAVE_COMPAT_IOCTL) && defined(DRV_EM64T)
|
|
extern long
|
|
lwpmu_Device_Control_Compat (
|
|
struct file *filp,
|
|
unsigned int cmd,
|
|
unsigned long arg
|
|
)
|
|
{
|
|
int status = OS_SUCCESS;
|
|
IOCTL_COMPAT_ARGS_NODE local_args_compat;
|
|
IOCTL_ARGS_NODE local_args;
|
|
|
|
memset(&local_args_compat, 0, sizeof(IOCTL_COMPAT_ARGS_NODE));
|
|
SEP_PRINT_DEBUG("Compat: type: %d, subcommand: %d\n", _IOC_TYPE(cmd), _IOC_NR(cmd));
|
|
|
|
if (_IOC_TYPE(cmd) != LWPMU_IOC_MAGIC) {
|
|
SEP_PRINT_ERROR("Unknown IOCTL magic:%d\n", _IOC_TYPE(cmd));
|
|
return OS_ILLEGAL_IOCTL;
|
|
}
|
|
|
|
if (arg) {
|
|
status = copy_from_user(&local_args_compat, (IOCTL_COMPAT_ARGS)arg, sizeof(IOCTL_COMPAT_ARGS_NODE));
|
|
}
|
|
local_args.r_len = local_args_compat.r_len;
|
|
local_args.w_len = local_args_compat.w_len;
|
|
local_args.r_buf = (char *) compat_ptr(local_args_compat.r_buf);
|
|
local_args.w_buf = (char *) compat_ptr(local_args_compat.w_buf);
|
|
|
|
status = lwpmu_Service_IOCTL (filp, _IOC_NR(cmd), local_args);
|
|
|
|
return status;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* @fn LWPMUDRV_Abnormal_Terminate(void)
|
|
*
|
|
* @brief This routine is called from linuxos_Exit_Task_Notify if the user process has
|
|
* been killed by an uncatchable signal (example kill -9). The state variable
|
|
* abormal_terminate is set to 1 and the clean up routines are called. In this
|
|
* code path the OS notifier hooks should not be unloaded.
|
|
*
|
|
* @param None
|
|
*
|
|
* @return OS_STATUS
|
|
*
|
|
* <I>Special Notes:</I>
|
|
* <none>
|
|
*/
|
|
extern int
|
|
LWPMUDRV_Abnormal_Terminate (
|
|
void
|
|
)
|
|
{
|
|
int status = OS_SUCCESS;
|
|
|
|
abnormal_terminate = 1;
|
|
SEP_PRINT_DEBUG("Abnormal-Termination: Calling lwpmudrv_Prepare_Stop\n");
|
|
status = lwpmudrv_Prepare_Stop();
|
|
SEP_PRINT_DEBUG("Abnormal-Termination: Calling lwpmudrv_Finish_Stop\n");
|
|
status = lwpmudrv_Finish_Stop();
|
|
SEP_PRINT_DEBUG("Abnormal-Termination: Calling lwpmudrv_Terminate\n");
|
|
status = lwpmudrv_Terminate();
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
/*****************************************************************************************
|
|
*
|
|
* Driver Entry / Exit functions that will be called on when the driver is loaded and
|
|
* unloaded
|
|
*
|
|
****************************************************************************************/
|
|
|
|
/*
|
|
* Structure that declares the usual file access functions
|
|
* First one is for lwpmu_c, the control functions
|
|
*/
|
|
static struct file_operations lwpmu_Fops = {
|
|
.owner = THIS_MODULE,
|
|
IOCTL_OP = lwpmu_Device_Control,
|
|
#if defined(HAVE_COMPAT_IOCTL) && defined(DRV_EM64T)
|
|
.compat_ioctl = lwpmu_Device_Control_Compat,
|
|
#endif
|
|
.read = lwpmu_Read,
|
|
.write = lwpmu_Write,
|
|
.open = lwpmu_Open,
|
|
.release = NULL,
|
|
.llseek = NULL,
|
|
};
|
|
|
|
/*
|
|
* Second one is for lwpmu_m, the module notification functions
|
|
*/
|
|
static struct file_operations lwmod_Fops = {
|
|
.owner = THIS_MODULE,
|
|
IOCTL_OP = NULL, //None needed
|
|
.read = OUTPUT_Module_Read,
|
|
.write = NULL, //No writing accepted
|
|
.open = lwpmu_Open,
|
|
.release = NULL,
|
|
.llseek = NULL,
|
|
};
|
|
|
|
/*
|
|
* Third one is for lwsamp_nn, the sampling functions
|
|
*/
|
|
static struct file_operations lwsamp_Fops = {
|
|
.owner = THIS_MODULE,
|
|
IOCTL_OP = NULL, //None needed
|
|
.read = OUTPUT_Sample_Read,
|
|
.write = NULL, //No writing accepted
|
|
.open = lwpmu_Open,
|
|
.release = NULL,
|
|
.llseek = NULL,
|
|
};
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/*!
|
|
* @fn static int lwpmudrv_setup_cdev(dev, fops, dev_number)
|
|
*
|
|
* @param LWPMU_DEV dev - pointer to the device object
|
|
* @param struct file_operations *fops - pointer to the file operations struct
|
|
* @param dev_t dev_number - major/monor device number
|
|
*
|
|
* @return OS_STATUS
|
|
*
|
|
* @brief Set up the device object.
|
|
*
|
|
* <I>Special Notes</I>
|
|
*/
|
|
static int
|
|
lwpmu_setup_cdev (
|
|
LWPMU_DEV dev,
|
|
struct file_operations *fops,
|
|
dev_t dev_number
|
|
)
|
|
{
|
|
cdev_init(&LWPMU_DEV_cdev(dev), fops);
|
|
LWPMU_DEV_cdev(dev).owner = THIS_MODULE;
|
|
LWPMU_DEV_cdev(dev).ops = fops;
|
|
|
|
return cdev_add(&LWPMU_DEV_cdev(dev), dev_number, 1);
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/*!
|
|
* @fn static int lwpmu_Load(void)
|
|
*
|
|
* @param none
|
|
*
|
|
* @return STATUS
|
|
*
|
|
* @brief Load the driver module into the kernel. Set up the driver object.
|
|
* @brief Set up the initial state of the driver and allocate the memory
|
|
* @brief needed to keep basic state information.
|
|
*/
|
|
static int
|
|
lwpmu_Load (
|
|
VOID
|
|
)
|
|
{
|
|
int i, num_cpus;
|
|
dev_t lwmod_DevNum;
|
|
OS_STATUS status = OS_SUCCESS;
|
|
#if defined (DRV_ANDROID)
|
|
char dev_name[MAXNAMELEN];
|
|
#endif
|
|
|
|
CONTROL_Memory_Tracker_Init();
|
|
|
|
/* Get one major device number and two minor numbers. */
|
|
/* The result is formatted as major+minor(0) */
|
|
/* One minor number is for control (lwpmu_c), */
|
|
/* the other (lwpmu_m) is for modules */
|
|
SEP_PRINT_DEBUG("lwpmu driver loading...\n");
|
|
SEP_PRINT_DEBUG("lwpmu driver about to register chrdev...\n");
|
|
|
|
lwpmu_DevNum = MKDEV(0, 0);
|
|
status = alloc_chrdev_region(&lwpmu_DevNum, 0, PMU_DEVICES, SEP_DRIVER_NAME);
|
|
SEP_PRINT_DEBUG("result of alloc_chrdev_region is %d\n", status);
|
|
if (status<0) {
|
|
SEP_PRINT_ERROR("lwpmu driver failed to alloc chrdev_region!\n");
|
|
return status;
|
|
}
|
|
SEP_PRINT_DEBUG("lwpmu driver: major number is %d\n", MAJOR(lwpmu_DevNum));
|
|
status = lwpmudrv_Initialize_State();
|
|
if (status<0) {
|
|
SEP_PRINT_ERROR("lwpmu driver failed to initialize state!\n");
|
|
return status;
|
|
}
|
|
num_cpus = GLOBAL_STATE_num_cpus(driver_state);
|
|
SEP_PRINT_DEBUG("detected %d CPUs in lwpmudrv_Load\n", num_cpus);
|
|
|
|
/* Allocate memory for the control structures */
|
|
lwpmu_control = CONTROL_Allocate_Memory(sizeof(LWPMU_DEV_NODE));
|
|
lwmod_control = CONTROL_Allocate_Memory(sizeof(LWPMU_DEV_NODE));
|
|
lwsamp_control= CONTROL_Allocate_Memory(num_cpus*sizeof(LWPMU_DEV_NODE));
|
|
|
|
if (!lwsamp_control || !lwpmu_control || !lwmod_control) {
|
|
CONTROL_Free_Memory(lwpmu_control);
|
|
CONTROL_Free_Memory(lwmod_control);
|
|
CONTROL_Free_Memory(lwsamp_control);
|
|
return OS_NO_MEM;
|
|
}
|
|
|
|
/* Register the file operations with the OS */
|
|
|
|
#if defined (DRV_ANDROID)
|
|
pmu_class = class_create(THIS_MODULE, SEP_DRIVER_NAME);
|
|
if (IS_ERR(pmu_class)) {
|
|
SEP_PRINT_ERROR("Error registering SEP control class\n");
|
|
}
|
|
device_create(pmu_class, NULL, lwpmu_DevNum, NULL, SEP_DRIVER_NAME DRV_DEVICE_DELIMITER"c");
|
|
#endif
|
|
|
|
status = lwpmu_setup_cdev(lwpmu_control,&lwpmu_Fops,lwpmu_DevNum);
|
|
if (status) {
|
|
SEP_PRINT_ERROR("Error %d adding lwpmu as char device\n", status);
|
|
return status;
|
|
}
|
|
/* _c init was fine, now try _m */
|
|
lwmod_DevNum = MKDEV(MAJOR(lwpmu_DevNum),MINOR(lwpmu_DevNum)+1);
|
|
|
|
#if defined (DRV_ANDROID)
|
|
device_create(pmu_class, NULL, lwmod_DevNum, NULL, SEP_DRIVER_NAME DRV_DEVICE_DELIMITER"m");
|
|
#endif
|
|
|
|
status = lwpmu_setup_cdev(lwmod_control,&lwmod_Fops,lwmod_DevNum);
|
|
if (status) {
|
|
SEP_PRINT_ERROR("Error %d adding lwpmu as char device\n", status);
|
|
cdev_del(&LWPMU_DEV_cdev(lwpmu_control));
|
|
return status;
|
|
}
|
|
|
|
/* allocate one sampling device per cpu */
|
|
lwsamp_DevNum = MKDEV(0, 0);
|
|
status = alloc_chrdev_region(&lwsamp_DevNum, 0, num_cpus, SEP_SAMPLES_NAME);
|
|
|
|
if (status<0) {
|
|
SEP_PRINT_ERROR("lwpmu driver failed to alloc chrdev_region!\n");
|
|
return status;
|
|
}
|
|
|
|
/* Register the file operations with the OS */
|
|
for (i = 0; i < num_cpus; i++) {
|
|
#if defined (DRV_ANDROID)
|
|
snprintf(dev_name, MAXNAMELEN, "%s%ss%d", SEP_DRIVER_NAME, DRV_DEVICE_DELIMITER, i);
|
|
device_create(pmu_class, NULL, lwsamp_DevNum+i, NULL, dev_name);
|
|
#endif
|
|
status = lwpmu_setup_cdev(lwsamp_control+i,
|
|
&lwsamp_Fops,
|
|
lwsamp_DevNum+i);
|
|
if (status) {
|
|
SEP_PRINT_ERROR("Error %d adding lwpmu as char device\n", status);
|
|
return status;
|
|
}
|
|
else {
|
|
SEP_PRINT_DEBUG("added sampling device %d\n", i);
|
|
}
|
|
}
|
|
|
|
tsc_info = CONTROL_Allocate_Memory(GLOBAL_STATE_num_cpus(driver_state)*sizeof(U64));
|
|
atomic_set(&read_now, GLOBAL_STATE_num_cpus(driver_state));
|
|
init_waitqueue_head(&read_tsc_now);
|
|
CONTROL_Invoke_Parallel(lwpmudrv_Fill_TSC_Info, (PVOID)(size_t)0);
|
|
|
|
pcb_size = GLOBAL_STATE_num_cpus(driver_state)*sizeof(CPU_STATE_NODE);
|
|
pcb = CONTROL_Allocate_Memory(pcb_size);
|
|
core_to_package_map = CONTROL_Allocate_Memory(GLOBAL_STATE_num_cpus(driver_state)*sizeof(U32));
|
|
SYS_INFO_Build();
|
|
pcb = CONTROL_Free_Memory(pcb);
|
|
pcb_size = 0;
|
|
if (total_ram <= OUTPUT_MEMORY_THRESHOLD) {
|
|
output_buffer_size = OUTPUT_SMALL_BUFFER;
|
|
}
|
|
|
|
MUTEX_INIT(ioctl_lock);
|
|
in_finish_code = 0;
|
|
|
|
memset((char *)&uncore_topology, 0, sizeof(UNCORE_TOPOLOGY_INFO_NODE));
|
|
|
|
/*
|
|
* Initialize the SEP driver version (done once at driver load time)
|
|
*/
|
|
SEP_VERSION_NODE_major(&drv_version) = SEP_MAJOR_VERSION;
|
|
SEP_VERSION_NODE_minor(&drv_version) = SEP_MINOR_VERSION;
|
|
SEP_VERSION_NODE_api(&drv_version) = SEP_API_VERSION;
|
|
SEP_VERSION_NODE_update(&drv_version)= SEP_UPDATE_VERSION;
|
|
|
|
//
|
|
// Display driver version information
|
|
//
|
|
SEP_PRINT("PMU collection driver v%d.%d.%d%s%s has been loaded.\n",
|
|
SEP_VERSION_NODE_major(&drv_version),
|
|
SEP_VERSION_NODE_minor(&drv_version),
|
|
SEP_VERSION_NODE_api(&drv_version),
|
|
SEP_UPDATE_STRING,
|
|
SEP_DRIVER_MODE);
|
|
|
|
SEP_PRINT("Chipset support is enabled.\n");
|
|
|
|
|
|
#if defined(DRV_IA32) || defined(DRV_EM64T)
|
|
SEP_PRINT("IDT vector 0x%x will be used for handling PMU interrupts.\n", CPU_PERF_VECTOR);
|
|
#endif
|
|
|
|
return status;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/*!
|
|
* @fn static int lwpmu_Unload(void)
|
|
*
|
|
* @param none
|
|
*
|
|
* @return none
|
|
*
|
|
* @brief Remove the driver module from the kernel.
|
|
*/
|
|
static VOID
|
|
lwpmu_Unload (
|
|
VOID
|
|
)
|
|
{
|
|
int i = 0;
|
|
int num_cpus = GLOBAL_STATE_num_cpus(driver_state);
|
|
|
|
SEP_PRINT_DEBUG("lwpmu driver unloading...\n");
|
|
LINUXOS_Uninstall_Hooks();
|
|
SYS_INFO_Destroy();
|
|
OUTPUT_Destroy();
|
|
cpu_buf = CONTROL_Free_Memory(cpu_buf);
|
|
module_buf = CONTROL_Free_Memory(module_buf);
|
|
pcb = CONTROL_Free_Memory(pcb);
|
|
pcb_size = 0;
|
|
tsc_info = CONTROL_Free_Memory(tsc_info);
|
|
core_to_package_map = CONTROL_Free_Memory(core_to_package_map);
|
|
|
|
#if defined (DRV_ANDROID)
|
|
unregister_chrdev(MAJOR(lwpmu_DevNum), SEP_DRIVER_NAME);
|
|
device_destroy(pmu_class, lwpmu_DevNum);
|
|
device_destroy(pmu_class, lwpmu_DevNum+1);
|
|
#endif
|
|
|
|
cdev_del(&LWPMU_DEV_cdev(lwpmu_control));
|
|
cdev_del(&LWPMU_DEV_cdev(lwmod_control));
|
|
unregister_chrdev_region(lwpmu_DevNum, PMU_DEVICES);
|
|
|
|
#if defined (DRV_ANDROID)
|
|
unregister_chrdev(MAJOR(lwsamp_DevNum), SEP_SAMPLES_NAME);
|
|
#endif
|
|
|
|
for (i = 0; i < num_cpus; i++) {
|
|
#if defined (DRV_ANDROID)
|
|
device_destroy(pmu_class, lwsamp_DevNum+i);
|
|
#endif
|
|
cdev_del(&LWPMU_DEV_cdev(&lwsamp_control[i]));
|
|
}
|
|
|
|
#if defined (DRV_ANDROID)
|
|
class_destroy(pmu_class);
|
|
#endif
|
|
|
|
unregister_chrdev_region(lwsamp_DevNum, num_cpus);
|
|
lwpmu_control = CONTROL_Free_Memory(lwpmu_control);
|
|
lwmod_control = CONTROL_Free_Memory(lwmod_control);
|
|
lwsamp_control = CONTROL_Free_Memory(lwsamp_control);
|
|
|
|
CONTROL_Memory_Tracker_Free();
|
|
|
|
//
|
|
// Display driver version information
|
|
//
|
|
SEP_PRINT("PMU collection driver v%d.%d.%d%s%s has been unloaded.\n",
|
|
SEP_VERSION_NODE_major(&drv_version),
|
|
SEP_VERSION_NODE_minor(&drv_version),
|
|
SEP_VERSION_NODE_api(&drv_version),
|
|
SEP_UPDATE_STRING,
|
|
SEP_DRIVER_MODE);
|
|
|
|
return;
|
|
}
|
|
|
|
/* Declaration of the init and exit functions */
|
|
module_init(lwpmu_Load);
|
|
module_exit(lwpmu_Unload);
|