2509 lines
61 KiB
C
2509 lines
61 KiB
C
/*
|
|
* SPDX-License-Identifier: GPL-2.0
|
|
* Copyright (c) 2023 MCST
|
|
*/
|
|
|
|
/*
|
|
* This file contains implementation of interface functions for working with
|
|
* monitors and implementation of mechanism for adjusting monitors.
|
|
*/
|
|
|
|
#include <linux/seq_file.h>
|
|
#include <linux/proc_fs.h>
|
|
#include <linux/module.h>
|
|
#include <linux/uaccess.h>
|
|
|
|
#include <asm/sic_regs.h>
|
|
#include <asm/sic_regs_access.h>
|
|
#include <asm/mmu_regs_access.h>
|
|
#include <asm/monitors.h>
|
|
|
|
#include "../../../fs/proc/internal.h" /* for get_proc_task() */
|
|
|
|
|
|
#define MONITORS_FILENAME "monitors"
|
|
static struct proc_dir_entry *monitors_dir_entry;
|
|
|
|
/*
|
|
* Monitors
|
|
*/
|
|
|
|
#define MONITORS_SETTINGS_FILENAME "monitors_settings"
|
|
#define MONITORS_EVENTS_FILENAME "monitors_events"
|
|
#define MONITORS_DEAD_PROC_EVENTS_FILENAME "monitors_dead_proc_events"
|
|
#define MONITORS_HELP_FILENAME "monitors_help"
|
|
|
|
#define MONITORS_MODE_NAME_LEN 1
|
|
#define MONITORS_SETTINGS_STR_MAX_SIZE 256
|
|
|
|
#define MONITORS_MODES_COUNT 3
|
|
#define SYSTEM_MODE 1
|
|
#define USER_MODE 2
|
|
#define COMMON_MODE (SYSTEM_MODE | USER_MODE)
|
|
|
|
struct monitors_mode_info {
|
|
char *name;
|
|
unsigned char mode;
|
|
};
|
|
|
|
static struct monitors_mode_info monitors_mode_info[] = {
|
|
{"S", SYSTEM_MODE},
|
|
{"U", USER_MODE },
|
|
{"C", COMMON_MODE}
|
|
};
|
|
|
|
struct monitors_info {
|
|
int mode;
|
|
unsigned short event;
|
|
unsigned char is_used;
|
|
};
|
|
|
|
static struct monitors_info monitors[MONITORS_COUNT];
|
|
|
|
int monitors_used __read_mostly = 0;
|
|
|
|
#undef DIM0
|
|
#undef DIM1
|
|
#undef DDM0
|
|
#undef DDM1
|
|
|
|
enum {
|
|
DIM0,
|
|
DIM1,
|
|
DDM0,
|
|
DDM1
|
|
};
|
|
|
|
static char *monitors_id_names[] = {
|
|
"I0",
|
|
"I1",
|
|
"D0",
|
|
"D1"
|
|
};
|
|
|
|
struct monitors_events_range {
|
|
unsigned short start;
|
|
unsigned short end;
|
|
};
|
|
|
|
#define DDM0_EVENTS_RANGE_COUNT_V3 7
|
|
#define DDM1_EVENTS_RANGE_COUNT_V3 6
|
|
#define DIM_EVENTS_RANGE_COUNT_V3 9
|
|
|
|
static struct monitors_events_range ddm0_monitors_events_list_v3[] = {
|
|
{0x00, 0x03}, {0x10, 0x19}, {0x20, 0x24}, {0x30, 0x3a}, {0x40, 0x46},
|
|
{0x48, 0x48}, {0x4a, 0x4b}
|
|
};
|
|
|
|
static struct monitors_events_range ddm1_monitors_events_list_v3[] = {
|
|
{0x00, 0x02}, {0x10, 0x19}, {0x20, 0x24}, {0x30, 0x3a}, {0x40, 0x48},
|
|
{0x4a, 0x4b}
|
|
};
|
|
|
|
static struct monitors_events_range dim_monitors_events_list_v3[] = {
|
|
{0x00, 0x03}, {0x07, 0x0a}, {0x0f, 0x26}, {0x30, 0x3d}, {0x40, 0x4a},
|
|
{0x50, 0x5a}, {0x60, 0x69}, {0x70, 0x74}, {0x7c, 0x7e}
|
|
};
|
|
|
|
#define DDM0_EVENTS_RANGE_COUNT_V5 6
|
|
#define DDM1_EVENTS_RANGE_COUNT_V5 7
|
|
#define DIM_EVENTS_RANGE_COUNT_V5 9
|
|
|
|
static struct monitors_events_range ddm0_monitors_events_list_v5[] = {
|
|
{0x00, 0x04}, {0x10, 0x19}, {0x20, 0x24}, {0x30, 0x3a}, {0x40, 0x48},
|
|
{0x4a, 0x4b}
|
|
};
|
|
|
|
static struct monitors_events_range ddm1_monitors_events_list_v5[] = {
|
|
{0x00, 0x02}, {0x04, 0x04}, {0x10, 0x19}, {0x20, 0x24}, {0x30, 0x3a},
|
|
{0x40, 0x48}, {0x4a, 0x4b}
|
|
};
|
|
|
|
static struct monitors_events_range dim_monitors_events_list_v5[] = {
|
|
{0x00, 0x03}, {0x07, 0x0a}, {0x0f, 0x26}, {0x2d, 0x3d}, {0x40, 0x4a},
|
|
{0x50, 0x5a}, {0x60, 0x69}, {0x70, 0x74}, {0x7c, 0x7e}
|
|
};
|
|
|
|
#define DDM0_EVENTS_RANGE_COUNT_V6 5
|
|
#define DDM1_EVENTS_RANGE_COUNT_V6 6
|
|
#define DIM_EVENTS_RANGE_COUNT_V6 9
|
|
|
|
static struct monitors_events_range ddm0_monitors_events_list_v6[] = {
|
|
{0x00, 0x07}, {0x10, 0x1c}, {0x20, 0x24}, {0x30, 0x3a}, {0x40, 0x4f}
|
|
};
|
|
|
|
static struct monitors_events_range ddm1_monitors_events_list_v6[] = {
|
|
{0x00, 0x07}, {0x10, 0x1c}, {0x20, 0x24}, {0x30, 0x3a}, {0x40, 0x4b},
|
|
{0x4d, 0x4f}
|
|
};
|
|
|
|
static struct monitors_events_range dim_monitors_events_list_v6[] = {
|
|
{0x00, 0x03}, {0x07, 0x0a}, {0x0f, 0x27}, {0x2d, 0x3d}, {0x40, 0x4a},
|
|
{0x50, 0x5a}, {0x60, 0x69}, {0x70, 0x74}, {0x7c, 0x7e}
|
|
};
|
|
|
|
#define DDM0_EVENTS_RANGE_COUNT_V7 DDM0_EVENTS_RANGE_COUNT_V6
|
|
#define DDM1_EVENTS_RANGE_COUNT_V7 DDM1_EVENTS_RANGE_COUNT_V6
|
|
#define DIM_EVENTS_RANGE_COUNT_V7 11
|
|
|
|
#define ddm0_monitors_events_list_v7 ddm0_monitors_events_list_v6
|
|
#define ddm1_monitors_events_list_v7 ddm1_monitors_events_list_v6
|
|
|
|
static struct monitors_events_range dim_monitors_events_list_v7[] = {
|
|
{0x00, 0x03}, {0x07, 0x0a}, {0x0f, 0x28}, {0x2d, 0x3d}, {0x40, 0x4a},
|
|
{0x50, 0x5a}, {0x60, 0x69}, {0x70, 0x74}, {0x7c, 0x7e}, {0x80, 0x99},
|
|
{0x9c, 0x9f}
|
|
};
|
|
|
|
static struct monitors_events_range *ddm0_monitors_events_list;
|
|
static struct monitors_events_range *ddm1_monitors_events_list;
|
|
static struct monitors_events_range *dim_monitors_events_list;
|
|
|
|
static unsigned char ddm0_monitors_events_range_count;
|
|
static unsigned char ddm1_monitors_events_range_count;
|
|
static unsigned char dim_monitors_events_range_count;
|
|
|
|
static atomic64_t common_events_count[NR_CPUS][MONITORS_COUNT];
|
|
|
|
typedef struct {
|
|
unsigned long monitors_count[NR_CPUS][MONITORS_COUNT];
|
|
struct monitors_info monitors[MONITORS_COUNT];
|
|
pid_t pid;
|
|
cpumask_t cpus_mask;
|
|
} dead_proc_events_t;
|
|
|
|
#define DEAD_PROC_EVENTS_COUNT 20
|
|
|
|
static dead_proc_events_t dead_proc_events_buf[DEAD_PROC_EVENTS_COUNT];
|
|
static dead_proc_events_t dead_proc_events_buf_tmp[DEAD_PROC_EVENTS_COUNT];
|
|
static char dead_proc_events_buf_id = -1;
|
|
static char dead_proc_events_buf_id_tmp;
|
|
static unsigned char dead_proc_events_buf_full = 0;
|
|
static unsigned char dead_proc_events_buf_full_tmp;
|
|
|
|
static DEFINE_RAW_SPINLOCK(monitors_lock);
|
|
static DEFINE_RAW_SPINLOCK(dead_proc_lock);
|
|
static DEFINE_RAW_SPINLOCK(dead_proc_lock_tmp);
|
|
|
|
/*
|
|
* SIC monitors
|
|
*/
|
|
|
|
#define SICMONITORS_SETTINGS_FILENAME "sicmonitors_settings"
|
|
#define SICMONITORS_EVENTS_FILENAME "sicmonitors_events"
|
|
#define SICMONITORS_HELP_FILENAME "sicmonitors_help"
|
|
|
|
#define SICMONITORS_SETTINGS_STR_MAX_SIZE 256
|
|
|
|
#define HAS_MACHINE_SICMONITORS \
|
|
(IS_MACHINE_E2S || IS_MACHINE_E8C || IS_MACHINE_E8C2)
|
|
|
|
struct sicmonitors_info {
|
|
unsigned short event;
|
|
unsigned char is_used;
|
|
};
|
|
|
|
static struct sicmonitors_info sicmonitors[SICMONITORS_COUNT];
|
|
|
|
enum {
|
|
MCM0,
|
|
MCM1,
|
|
};
|
|
|
|
static char *sicmonitors_id_names[] = {
|
|
"M0",
|
|
"M1",
|
|
};
|
|
|
|
#define sicmonitors_events_range monitors_events_range
|
|
|
|
#define MCM0_EVENTS_RANGE_COUNT_E4C 1
|
|
#define MCM1_EVENTS_RANGE_COUNT_E4C 1
|
|
|
|
static struct sicmonitors_events_range mcm0_sicmonitors_events_list_e4c[] = {
|
|
{0x00, 0x08}
|
|
};
|
|
|
|
static struct sicmonitors_events_range mcm1_sicmonitors_events_list_e4c[] = {
|
|
{0x00, 0x06}
|
|
};
|
|
|
|
#define MCM0_EVENTS_RANGE_COUNT_E8C 2
|
|
#define MCM1_EVENTS_RANGE_COUNT_E8C 2
|
|
|
|
static struct sicmonitors_events_range mcm0_sicmonitors_events_list_e8c[] = {
|
|
{0x00, 0x05}, {0x20, 0x3f}
|
|
};
|
|
|
|
static struct sicmonitors_events_range mcm1_sicmonitors_events_list_e8c[] = {
|
|
{0x00, 0x05}, {0x20, 0x3f}
|
|
};
|
|
|
|
#define MCM0_EVENTS_RANGE_COUNT_E8C2 2
|
|
#define MCM1_EVENTS_RANGE_COUNT_E8C2 2
|
|
|
|
static struct sicmonitors_events_range mcm0_sicmonitors_events_list_e8c2[] = {
|
|
{0x00, 0x1b}, {0x20, 0x3f}
|
|
};
|
|
|
|
static struct sicmonitors_events_range mcm1_sicmonitors_events_list_e8c2[] = {
|
|
{0x00, 0x1b}, {0x20, 0x3f}
|
|
};
|
|
|
|
static struct sicmonitors_events_range *mcm0_sicmonitors_events_list;
|
|
static struct sicmonitors_events_range *mcm1_sicmonitors_events_list;
|
|
|
|
static unsigned char mcm0_sicmonitors_events_range_count;
|
|
static unsigned char mcm1_sicmonitors_events_range_count;
|
|
|
|
static DEFINE_RAW_SPINLOCK(sicmonitors_lock);
|
|
|
|
/*
|
|
* IPCC monitors
|
|
*/
|
|
|
|
#define IPCCMONITORS_SETTINGS_FILENAME "ipccmonitors_settings"
|
|
#define IPCCMONITORS_EVENTS_FILENAME "ipccmonitors_events"
|
|
#define IPCCMONITORS_HELP_FILENAME "ipccmonitors_help"
|
|
|
|
#define IPCCMONITORS_SETTINGS_STR_MAX_SIZE 16
|
|
|
|
#define HAS_MACHINE_IPCCMONITORS \
|
|
(IS_MACHINE_E2S || IS_MACHINE_E8C)
|
|
|
|
struct ipccmonitors_info {
|
|
unsigned short event;
|
|
unsigned char is_used;
|
|
};
|
|
|
|
static struct ipccmonitors_info ipccmonitors;
|
|
|
|
struct ipccmonitors_event_info {
|
|
char *name;
|
|
unsigned short event;
|
|
};
|
|
|
|
#define IPCC_EVENTS_COUNT 2
|
|
|
|
enum {
|
|
IPCC_LERR,
|
|
IPCC_RTRY,
|
|
};
|
|
|
|
static struct ipccmonitors_event_info IPCC_event_info[] = {
|
|
{"IPCC_LERR", 0x01},
|
|
{"IPCC_RTRY", 0x02},
|
|
};
|
|
|
|
static DEFINE_RAW_SPINLOCK(ipccmonitors_lock);
|
|
|
|
/*
|
|
* IOCC monitors
|
|
*/
|
|
|
|
#define IOCCMONITORS_SETTINGS_FILENAME "ioccmonitors_settings"
|
|
#define IOCCMONITORS_EVENTS_FILENAME "ioccmonitors_events"
|
|
#define IOCCMONITORS_HELP_FILENAME "ioccmonitors_help"
|
|
|
|
#define E2K_IO_STR_EVENT_SHIFT 29
|
|
#define E2K_IO_STR_EVENT_MASK 0xE0000000
|
|
|
|
#define IOCCMONITORS_SETTINGS_STR_MAX_SIZE 16
|
|
|
|
#define HAS_MACHINE_IOCCMONITORS (IS_MACHINE_E2S || IS_MACHINE_E1CP)
|
|
|
|
struct ioccmonitors_info {
|
|
unsigned short event;
|
|
unsigned char is_used;
|
|
};
|
|
|
|
static struct ioccmonitors_info ioccmonitors;
|
|
|
|
struct ioccmonitors_event_info {
|
|
char *name;
|
|
unsigned short event;
|
|
};
|
|
|
|
#define IOCC_EVENTS_COUNT 4
|
|
|
|
enum {
|
|
IOCC_BSY_RC,
|
|
IOCC_ERR_RC,
|
|
IOCC_TO_RC,
|
|
IOCC_CMN_RC,
|
|
};
|
|
|
|
static struct ioccmonitors_event_info IOCC_event_info[] = {
|
|
{"IOCC_BSY_RC", 0x01},
|
|
{"IOCC_ERR_RC", 0x02},
|
|
{"IOCC_TO_RC", 0x04},
|
|
{"IOCC_CMN_RC", 0x07},
|
|
};
|
|
|
|
static DEFINE_RAW_SPINLOCK(ioccmonitors_lock);
|
|
|
|
|
|
/*
|
|
* Monitors
|
|
*/
|
|
|
|
static inline int dim_check_start_monitoring(int monitor)
|
|
{
|
|
e2k_dimcr_t dimcr_reg;
|
|
unsigned char user, new_user;
|
|
unsigned char system, new_system;
|
|
unsigned short event, new_event;
|
|
unsigned char new_mode;
|
|
|
|
if (monitors[monitor].is_used) {
|
|
dimcr_reg = READ_DIMCR_REG();
|
|
|
|
user = dimcr_reg.fields[monitor].user;
|
|
system = dimcr_reg.fields[monitor].system;
|
|
event = dimcr_reg.fields[monitor].event;
|
|
|
|
new_mode = monitors_mode_info[monitors[monitor].mode].mode;
|
|
new_user = (new_mode & USER_MODE) ? 1 : 0;
|
|
new_system = (new_mode & SYSTEM_MODE) ? 1 : 0;
|
|
new_event = monitors[monitor].event;
|
|
|
|
if (user != new_user || system != new_system ||
|
|
event != new_event)
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline unsigned char ddm_check_start_monitoring(int monitor)
|
|
{
|
|
e2k_ddmcr_t ddmcr_reg;
|
|
unsigned char user, new_user;
|
|
unsigned char system, new_system;
|
|
unsigned short event, new_event = 0;
|
|
unsigned char new_mode;
|
|
unsigned char num;
|
|
|
|
if (monitors[monitor].is_used) {
|
|
ddmcr_reg = READ_DDMCR_REG();
|
|
|
|
num = monitor - 2;
|
|
|
|
user = ddmcr_reg.fields[num].user;
|
|
system = ddmcr_reg.fields[num].system;
|
|
event = ddmcr_reg.fields[num].event;
|
|
|
|
new_mode = monitors_mode_info[monitors[monitor].mode].mode;
|
|
new_user = (new_mode & USER_MODE) ? 1 : 0;
|
|
new_system = (new_mode & SYSTEM_MODE) ? 1 : 0;
|
|
new_event = monitors[monitor].event;
|
|
|
|
if (user != new_user || system != new_system ||
|
|
event != new_event)
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline unsigned char check_start_monitoring(int monitor)
|
|
{
|
|
if (monitor == DIM0 || monitor == DIM1)
|
|
return dim_check_start_monitoring(monitor);
|
|
else if (monitor == DDM0 || monitor == DDM1)
|
|
return ddm_check_start_monitoring(monitor);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline unsigned char dim_check_process_start_monitoring(
|
|
int monitor, struct task_struct *task)
|
|
{
|
|
e2k_dimcr_t dimcr_reg;
|
|
unsigned char user, proc_user;
|
|
unsigned char system, proc_system;
|
|
unsigned short event, proc_event;
|
|
unsigned char proc_mode;
|
|
|
|
if (monitors[monitor].is_used) {
|
|
dimcr_reg = task->thread.sw_regs.dimcr;
|
|
|
|
user = dimcr_reg.fields[monitor].user;
|
|
system = dimcr_reg.fields[monitor].system;
|
|
event = dimcr_reg.fields[monitor].event;
|
|
|
|
proc_mode = monitors_mode_info[monitors[monitor].mode].mode;
|
|
proc_user = (proc_mode & USER_MODE) ? 1 : 0;
|
|
proc_system = (proc_mode & SYSTEM_MODE) ? 1 : 0;
|
|
proc_event = monitors[monitor].event;
|
|
|
|
if (user != proc_user || system != proc_system ||
|
|
event != proc_event)
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline unsigned char ddm_check_process_start_monitoring(
|
|
int monitor, struct task_struct *task)
|
|
{
|
|
e2k_ddmcr_t ddmcr_reg;
|
|
unsigned char user, proc_user;
|
|
unsigned char system, proc_system;
|
|
unsigned short event, proc_event = 0;
|
|
unsigned char proc_mode;
|
|
unsigned char num;
|
|
|
|
if (monitors[monitor].is_used) {
|
|
ddmcr_reg = task->thread.sw_regs.ddmcr;
|
|
|
|
num = monitor - 2;
|
|
|
|
user = ddmcr_reg.fields[num].user;
|
|
system = ddmcr_reg.fields[num].system;
|
|
event = ddmcr_reg.fields[num].event;
|
|
|
|
proc_mode = monitors_mode_info[monitors[monitor].mode].mode;
|
|
proc_user = (proc_mode & USER_MODE) ? 1 : 0;
|
|
proc_system = (proc_mode & SYSTEM_MODE) ? 1 : 0;
|
|
proc_event = monitors[monitor].event;
|
|
|
|
if (user != proc_user || system != proc_system ||
|
|
event != proc_event)
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline unsigned char check_process_start_monitoring(
|
|
int monitor, struct task_struct *task)
|
|
{
|
|
if (monitor == DIM0 || monitor == DIM1)
|
|
return dim_check_process_start_monitoring(monitor, task);
|
|
else if (monitor == DDM0 || monitor == DDM1)
|
|
return ddm_check_process_start_monitoring(monitor, task);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline void dim_start_monitoring(int monitor)
|
|
{
|
|
e2k_dimcr_t dimcr_reg;
|
|
unsigned char user;
|
|
unsigned char system;
|
|
unsigned short event;
|
|
unsigned char mode;
|
|
|
|
dimcr_reg = READ_DIMCR_REG();
|
|
|
|
mode = monitors_mode_info[monitors[monitor].mode].mode;
|
|
user = (mode & USER_MODE) ? 1 : 0;
|
|
system = (mode & SYSTEM_MODE) ? 1 : 0;
|
|
event = monitors[monitor].event;
|
|
|
|
dimcr_reg.fields[monitor].user = user;
|
|
dimcr_reg.fields[monitor].system = system;
|
|
dimcr_reg.fields[monitor].event = event;
|
|
|
|
WRITE_DIMCR_REG(dimcr_reg);
|
|
|
|
/*
|
|
* We should reset dimar0 and dimar1 at the end or we can receive some
|
|
* events of previous type.
|
|
*/
|
|
if (monitor == DIM0)
|
|
WRITE_DIMAR0_REG_VALUE(0);
|
|
else if (monitor == DIM1)
|
|
WRITE_DIMAR1_REG_VALUE(0);
|
|
}
|
|
|
|
static inline void ddm_start_monitoring(int monitor)
|
|
{
|
|
e2k_ddmcr_t ddmcr_reg;
|
|
unsigned char user;
|
|
unsigned char system;
|
|
unsigned short event;
|
|
unsigned char mode;
|
|
unsigned char num;
|
|
|
|
ddmcr_reg = READ_DDMCR_REG();
|
|
|
|
mode = monitors_mode_info[monitors[monitor].mode].mode;
|
|
user = (mode & USER_MODE) ? 1 : 0;
|
|
system = (mode & SYSTEM_MODE) ? 1 : 0;
|
|
event = monitors[monitor].event;
|
|
|
|
num = monitor - 2;
|
|
|
|
ddmcr_reg.fields[num].user = user;
|
|
ddmcr_reg.fields[num].system = system;
|
|
ddmcr_reg.fields[num].event = event;
|
|
|
|
WRITE_DDMCR_REG(ddmcr_reg);
|
|
|
|
/*
|
|
* We should reset ddmar0 and ddmar1 at the end or we can receive some
|
|
* events of previous type.
|
|
*/
|
|
if (monitor == DDM0)
|
|
WRITE_DDMAR0_REG_VALUE(0);
|
|
else if (monitor == DDM1)
|
|
WRITE_DDMAR1_REG_VALUE(0);
|
|
}
|
|
|
|
static inline void start_monitoring(int monitor)
|
|
{
|
|
if (monitor == DIM0 || monitor == DIM1)
|
|
dim_start_monitoring(monitor);
|
|
else if (monitor == DDM0 || monitor == DDM1)
|
|
ddm_start_monitoring(monitor);
|
|
}
|
|
|
|
static inline void start_process_monitoring(int monitor,
|
|
struct task_struct *task)
|
|
{
|
|
struct thread_info *thread_info = task_thread_info(task);
|
|
unsigned char i;
|
|
|
|
for (i = 0; i < NR_CPUS; i++)
|
|
atomic64_set(&thread_info->monitors_count[i][monitor], 0);
|
|
|
|
/*
|
|
* When monitoring is activated for the process, monitoring might
|
|
* already been activated. So, we need right values of
|
|
* thread_info->monitors_delta.dim0, thread_info->monitors_delta.dim1,
|
|
* thread_info->monitors_delta.ddm0 and
|
|
* thread_info->monitors_delta.ddm1 to count right value of
|
|
* delta_event_count, when the processor will be switched on the
|
|
* process next time.
|
|
*/
|
|
switch (monitor) {
|
|
case DIM0:
|
|
thread_info->monitors_delta.dim0 = 0;
|
|
break;
|
|
case DIM1:
|
|
thread_info->monitors_delta.dim1 = 0;
|
|
break;
|
|
case DDM0:
|
|
thread_info->monitors_delta.ddm0 = 0;
|
|
break;
|
|
case DDM1:
|
|
thread_info->monitors_delta.ddm1 = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void process_monitors(struct task_struct *task)
|
|
{
|
|
struct thread_info *thread_info = task_thread_info(task);
|
|
unsigned long delta_event_count;
|
|
unsigned char cpu_num;
|
|
unsigned long flags;
|
|
unsigned char i;
|
|
|
|
if (!thread_info)
|
|
return;
|
|
|
|
raw_spin_lock_irqsave(&monitors_lock, flags);
|
|
|
|
for (i = 0; i < MONITORS_COUNT; i++) {
|
|
if (check_process_start_monitoring(i, task))
|
|
start_process_monitoring(i, task);
|
|
|
|
if (check_start_monitoring(i))
|
|
start_monitoring(i);
|
|
|
|
if (monitors[i].is_used) {
|
|
switch (i) {
|
|
case DIM0:
|
|
delta_event_count =
|
|
thread_info->monitors_delta.dim0;
|
|
task->thread.sw_regs.dimar0 =
|
|
READ_DIMAR0_REG_VALUE();
|
|
break;
|
|
case DIM1:
|
|
delta_event_count =
|
|
thread_info->monitors_delta.dim1;
|
|
task->thread.sw_regs.dimar1 =
|
|
READ_DIMAR1_REG_VALUE();
|
|
break;
|
|
case DDM0:
|
|
delta_event_count =
|
|
thread_info->monitors_delta.ddm0;
|
|
task->thread.sw_regs.ddmar0 =
|
|
READ_DDMAR0_REG_VALUE();
|
|
break;
|
|
case DDM1:
|
|
delta_event_count =
|
|
thread_info->monitors_delta.ddm1;
|
|
task->thread.sw_regs.ddmar1 =
|
|
READ_DDMAR1_REG_VALUE();
|
|
break;
|
|
default:
|
|
delta_event_count = 0;
|
|
}
|
|
|
|
/*
|
|
* We do it here, because we are not already
|
|
* interested in common_events_count for monitor, when
|
|
* it is stopping, and because there is no events for
|
|
* monitor, when it is started.
|
|
*/
|
|
cpu_num = thread_info->monitors_delta.cpu_num;
|
|
atomic64_add(delta_event_count,
|
|
&common_events_count[cpu_num][i]);
|
|
atomic64_add(delta_event_count,
|
|
&thread_info->monitors_count[cpu_num][i]);
|
|
}
|
|
}
|
|
|
|
raw_spin_unlock_irqrestore(&monitors_lock, flags);
|
|
}
|
|
|
|
void init_monitors(struct task_struct *task)
|
|
{
|
|
AW(task->thread.sw_regs.dimcr) = 0;
|
|
AW(task->thread.sw_regs.ddmcr) = 0;
|
|
}
|
|
|
|
void store_monitors_delta(struct task_struct *task)
|
|
{
|
|
struct thread_info *thread_info = task_thread_info(task);
|
|
unsigned long initial_count;
|
|
unsigned long current_count;
|
|
unsigned long flags;
|
|
|
|
if (!thread_info)
|
|
return;
|
|
|
|
raw_spin_lock_irqsave(&monitors_lock, flags);
|
|
|
|
thread_info->monitors_delta.cpu_num = task_cpu(task);
|
|
|
|
if (monitors[DIM0].is_used) {
|
|
initial_count = task->thread.sw_regs.dimar0;
|
|
current_count = READ_DIMAR0_REG_VALUE();
|
|
thread_info->monitors_delta.dim0 =
|
|
current_count - initial_count;
|
|
} else
|
|
thread_info->monitors_delta.dim0 = 0;
|
|
|
|
if (monitors[DIM1].is_used) {
|
|
initial_count = task->thread.sw_regs.dimar1;
|
|
current_count = READ_DIMAR1_REG_VALUE();
|
|
thread_info->monitors_delta.dim1 =
|
|
current_count - initial_count;
|
|
} else
|
|
thread_info->monitors_delta.dim1 = 0;
|
|
|
|
if (monitors[DDM0].is_used) {
|
|
initial_count = task->thread.sw_regs.ddmar0;
|
|
current_count = READ_DDMAR0_REG_VALUE();
|
|
thread_info->monitors_delta.ddm0 =
|
|
current_count - initial_count;
|
|
} else
|
|
thread_info->monitors_delta.ddm0 = 0;
|
|
|
|
if (monitors[DDM1].is_used) {
|
|
initial_count = task->thread.sw_regs.ddmar1;
|
|
current_count = READ_DDMAR1_REG_VALUE();
|
|
thread_info->monitors_delta.ddm1 =
|
|
current_count - initial_count;
|
|
} else
|
|
thread_info->monitors_delta.ddm1 = 0;
|
|
|
|
raw_spin_unlock_irqrestore(&monitors_lock, flags);
|
|
}
|
|
|
|
void add_dead_proc_events(struct task_struct *task)
|
|
{
|
|
/*
|
|
* We can have a situation, when a monitoring event or mode have been
|
|
* changed or monitoring has been started or stopped after the last
|
|
* call of process_monitors function and before the call of this
|
|
* function. In this case we will have wrong data in
|
|
* dead_proc_events_buf. But as this situation is almost impossible,
|
|
* we do nothing to avoid it.
|
|
*/
|
|
|
|
struct thread_info *thread_info = task_thread_info(task);
|
|
unsigned char id;
|
|
unsigned long flags;
|
|
|
|
if (!thread_info)
|
|
return;
|
|
|
|
raw_spin_lock_irqsave(&dead_proc_lock, flags);
|
|
|
|
if (++dead_proc_events_buf_id == DEAD_PROC_EVENTS_COUNT) {
|
|
dead_proc_events_buf_full = 1;
|
|
dead_proc_events_buf_id = 0;
|
|
}
|
|
|
|
id = dead_proc_events_buf_id;
|
|
|
|
raw_spin_lock(&monitors_lock);
|
|
memcpy(dead_proc_events_buf[id].monitors, monitors,
|
|
sizeof(struct monitors_info) * MONITORS_COUNT);
|
|
raw_spin_unlock(&monitors_lock);
|
|
|
|
memcpy(dead_proc_events_buf[id].monitors_count,
|
|
thread_info->monitors_count,
|
|
sizeof(atomic64_t) * NR_CPUS * MONITORS_COUNT);
|
|
|
|
dead_proc_events_buf[id].pid = task->pid;
|
|
dead_proc_events_buf[id].cpus_mask = *cpu_online_mask;
|
|
|
|
raw_spin_unlock_irqrestore(&dead_proc_lock, flags);
|
|
}
|
|
|
|
unsigned char get_monitors_mask(char *title)
|
|
{
|
|
unsigned char mask = 0;
|
|
unsigned char title_start = 0;
|
|
unsigned short len = 0;
|
|
unsigned short event_id;
|
|
char event_name[8];
|
|
unsigned long flags;
|
|
unsigned char i;
|
|
|
|
raw_spin_lock_irqsave(&monitors_lock, flags);
|
|
|
|
for (i = 0; i < MONITORS_COUNT; i++) {
|
|
if (!monitors[i].is_used)
|
|
continue;
|
|
|
|
event_id = monitors[i].event;
|
|
|
|
memset(event_name, 0, 8);
|
|
sprintf(event_name, "0x%X", event_id);
|
|
|
|
mask |= 1 << i;
|
|
|
|
if (title_start) {
|
|
sprintf(title + len, "%s", event_name);
|
|
len += strlen(event_name);
|
|
title_start = 0;
|
|
} else {
|
|
sprintf(title + len, " %s", event_name);
|
|
len += strlen(event_name) + 1;
|
|
}
|
|
}
|
|
|
|
raw_spin_unlock_irqrestore(&monitors_lock, flags);
|
|
|
|
title[len] = 0;
|
|
|
|
return mask;
|
|
}
|
|
|
|
static int pid_monitors_events_show(struct seq_file *file, void *data)
|
|
{
|
|
struct inode *inode;
|
|
struct task_struct *task;
|
|
struct thread_info *thread_info;
|
|
e2k_ddmcr_t mcr_reg;
|
|
unsigned char user;
|
|
unsigned char system;
|
|
unsigned char mode;
|
|
unsigned long count;
|
|
unsigned char num;
|
|
unsigned long flags;
|
|
unsigned char i, j;
|
|
|
|
inode = file->private;
|
|
if (!inode)
|
|
return 0;
|
|
|
|
task = get_proc_task(inode);
|
|
if (!task)
|
|
return 0;
|
|
|
|
thread_info = task_thread_info(task);
|
|
if (!thread_info)
|
|
return 0;
|
|
|
|
raw_spin_lock_irqsave(&monitors_lock, flags);
|
|
|
|
for (i = 0; i < NR_CPUS; i++) {
|
|
if (!cpu_online(i))
|
|
continue;
|
|
|
|
for (j = 0; j < MONITORS_COUNT; j++) {
|
|
if (!monitors[j].is_used)
|
|
continue;
|
|
|
|
mode = monitors_mode_info[monitors[j].mode].mode;
|
|
user = (mode & USER_MODE) ? 1 : 0;
|
|
system = (mode & SYSTEM_MODE) ? 1 : 0;
|
|
|
|
switch (j) {
|
|
case DIM0:
|
|
AW(mcr_reg) = AW(task->thread.sw_regs.dimcr);
|
|
break;
|
|
case DIM1:
|
|
AW(mcr_reg) = AW(task->thread.sw_regs.dimcr);
|
|
break;
|
|
case DDM0:
|
|
mcr_reg = task->thread.sw_regs.ddmcr;
|
|
break;
|
|
case DDM1:
|
|
mcr_reg = task->thread.sw_regs.ddmcr;
|
|
break;
|
|
default:
|
|
continue;
|
|
}
|
|
|
|
num = j % 2;
|
|
|
|
count = atomic64_read(
|
|
&thread_info->monitors_count[i][j]);
|
|
|
|
/*
|
|
* We should do it, because we can have a situation,
|
|
* when a monitoring event or mode have been changed
|
|
* or monitoring has been started, but the process,
|
|
* for which we want to see a count of monitoring
|
|
* events, has not yet started processing, so a count
|
|
* of monitoring events, taken from the process
|
|
* context, is invalid.
|
|
*/
|
|
if (mcr_reg.fields[num].event != monitors[j].event
|
|
|| mcr_reg.fields[num].user != user
|
|
|| mcr_reg.fields[num].system != system)
|
|
count = 0;
|
|
|
|
seq_printf(file, "CPU%d:%s:%s:0x%x=%lu\n",
|
|
i,
|
|
monitors_id_names[j],
|
|
monitors_mode_info[monitors[j].mode].name,
|
|
monitors[j].event,
|
|
count);
|
|
}
|
|
}
|
|
|
|
raw_spin_unlock_irqrestore(&monitors_lock, flags);
|
|
|
|
put_task_struct(task);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int pid_monitors_events_open(struct inode *inode, struct file *file)
|
|
{
|
|
single_open(file, pid_monitors_events_show, inode);
|
|
return 0;
|
|
}
|
|
|
|
const struct file_operations proc_pid_monitors_events_operations =
|
|
{
|
|
.open = pid_monitors_events_open,
|
|
.read = seq_read,
|
|
.llseek = seq_lseek,
|
|
.release = single_release,
|
|
};
|
|
|
|
static inline unsigned short monitors_settings_string_get_next_word_len(
|
|
char *str, unsigned char *is_last)
|
|
{
|
|
unsigned short len;
|
|
|
|
*is_last = 0;
|
|
len = strcspn(str, " \n");
|
|
|
|
if (str[len] == '\n' || len == strlen(str))
|
|
*is_last = 1;
|
|
|
|
return len;
|
|
}
|
|
|
|
static inline char lookup_monitors_id(char *str, unsigned short len)
|
|
{
|
|
unsigned char i;
|
|
char *name;
|
|
|
|
for (i = 0; i < MONITORS_COUNT; i++) {
|
|
name = monitors_id_names[i];
|
|
|
|
if (len == strlen(name) && strncmp(str, name, len) == 0)
|
|
return i;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static inline char lookup_monitors_mode_id(char *str, unsigned short len)
|
|
{
|
|
unsigned char i;
|
|
char *name;
|
|
|
|
if (len > MONITORS_MODE_NAME_LEN)
|
|
return -1;
|
|
|
|
for (i = 0; i < MONITORS_MODES_COUNT; i++) {
|
|
name = monitors_mode_info[i].name;
|
|
|
|
if (len == strlen(name) && strncmp(str, name, len) == 0)
|
|
return i;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static inline int lookup_monitors_event(char *str,
|
|
unsigned short len, int monitor)
|
|
{
|
|
int event = -1;
|
|
struct monitors_events_range *monitors_events_list;
|
|
unsigned char monitors_events_range_count;
|
|
unsigned char i;
|
|
|
|
switch (monitor) {
|
|
case DIM0:
|
|
case DIM1:
|
|
monitors_events_range_count = dim_monitors_events_range_count;
|
|
monitors_events_list = dim_monitors_events_list;
|
|
break;
|
|
case DDM0:
|
|
monitors_events_range_count = ddm0_monitors_events_range_count;
|
|
monitors_events_list = ddm0_monitors_events_list;
|
|
break;
|
|
case DDM1:
|
|
monitors_events_range_count = ddm1_monitors_events_range_count;
|
|
monitors_events_list = ddm1_monitors_events_list;
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
|
|
sscanf(str, "0x%X", &event);
|
|
|
|
for (i = 0; i < monitors_events_range_count; i++) {
|
|
if (monitors_events_list[i].start <= event &&
|
|
monitors_events_list[i].end >= event)
|
|
return event;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static inline void parse_monitors_settings_string(char *str)
|
|
{
|
|
unsigned short i = 0;
|
|
unsigned char j;
|
|
unsigned short len1 = 0, len2 = 0, len3 = 0;
|
|
unsigned char is_last = 0;
|
|
struct monitors_info new_monitors[MONITORS_COUNT];
|
|
int new_monitors_used = 0;
|
|
char monitor_id;
|
|
char mode_id;
|
|
int event;
|
|
unsigned long flags;
|
|
|
|
memset(new_monitors, 0, sizeof(struct monitors_info) *
|
|
MONITORS_COUNT);
|
|
|
|
while (!is_last) {
|
|
if (i % 3 == 0) {
|
|
len1 = monitors_settings_string_get_next_word_len(
|
|
str, &is_last);
|
|
|
|
/*
|
|
* We check, if input string is an empty string, or if
|
|
* it is an invalid string (without monitor name or
|
|
* number), or if it is a valid string.
|
|
*/
|
|
if (is_last && (i || len1 > 1 || (len1 &&
|
|
strncmp(str, "\n", 1)))) {
|
|
pr_err("Failed to adjust monitors (invalid "
|
|
"settings string).\n");
|
|
return;
|
|
}
|
|
} else if (i % 3 == 1) {
|
|
len2 = monitors_settings_string_get_next_word_len(
|
|
str + len1 + 1, &is_last);
|
|
|
|
if (is_last) {
|
|
pr_err("Failed to adjust monitors (invalid "
|
|
"settings string).\n");
|
|
return;
|
|
}
|
|
} else {
|
|
len3 = monitors_settings_string_get_next_word_len(
|
|
str + len1 + len2 + 2, &is_last);
|
|
|
|
monitor_id = lookup_monitors_id(str, len1);
|
|
if (monitor_id == -1) {
|
|
pr_err("Failed to adjust monitors (invalid "
|
|
"monitor name).\n");
|
|
return;
|
|
}
|
|
|
|
mode_id = lookup_monitors_mode_id(
|
|
str + len1 + 1, len2);
|
|
if (mode_id == -1) {
|
|
pr_err("Failed to adjust monitors (invalid "
|
|
"mode name).\n");
|
|
return;
|
|
}
|
|
|
|
event = lookup_monitors_event(str + len1 + len2 + 2,
|
|
len3, monitor_id);
|
|
if (event == -1) {
|
|
pr_err("Failed to adjust monitors (invalid "
|
|
"event number).\n");
|
|
return;
|
|
}
|
|
|
|
new_monitors[(unsigned char)
|
|
monitor_id].is_used = 1;
|
|
new_monitors[(unsigned char)
|
|
monitor_id].event = event;
|
|
new_monitors[(unsigned char)
|
|
monitor_id].mode = mode_id;
|
|
|
|
str += len1 + len2 + len3 + 3;
|
|
}
|
|
|
|
i++;
|
|
}
|
|
|
|
raw_spin_lock_irqsave(&monitors_lock, flags);
|
|
|
|
for (i = 0; i < MONITORS_COUNT; i++) {
|
|
if ((new_monitors[i].is_used && !monitors[i].is_used) ||
|
|
(new_monitors[i].is_used && monitors[i].is_used &&
|
|
(new_monitors[i].event != monitors[i].event ||
|
|
new_monitors[i].mode !=
|
|
monitors[i].mode))) {
|
|
for (j = 0; j < NR_CPUS; j++)
|
|
atomic64_set(&common_events_count[j][i], 0);
|
|
}
|
|
|
|
if (new_monitors[i].is_used)
|
|
new_monitors_used |= 1 << i;
|
|
}
|
|
|
|
memcpy(monitors, new_monitors,
|
|
sizeof(struct monitors_info) * MONITORS_COUNT);
|
|
|
|
monitors_used = new_monitors_used;
|
|
|
|
raw_spin_unlock_irqrestore(&monitors_lock, flags);
|
|
}
|
|
|
|
static ssize_t monitors_settings_write(struct file *file,
|
|
const char __user *buffer, size_t count, loff_t *data)
|
|
{
|
|
char monitors_settings_buffer[MONITORS_SETTINGS_STR_MAX_SIZE];
|
|
int ret;
|
|
|
|
memset(monitors_settings_buffer, 0, sizeof(char) *
|
|
MONITORS_SETTINGS_STR_MAX_SIZE);
|
|
|
|
if (count > MONITORS_SETTINGS_STR_MAX_SIZE - 1) {
|
|
pr_err("Failed to adjust monitors (too long settings "
|
|
"string).\n");
|
|
ret = -EINVAL;
|
|
} else if (copy_from_user(monitors_settings_buffer, buffer, count)) {
|
|
pr_err("Failed to adjust monitors (kernel error).\n");
|
|
ret = -EFAULT;
|
|
} else {
|
|
parse_monitors_settings_string(monitors_settings_buffer);
|
|
ret = count;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int monitors_settings_proc_show(struct seq_file *m, void *data)
|
|
{
|
|
unsigned short event;
|
|
unsigned char str_start = 1;
|
|
char *mode_name;
|
|
unsigned long flags;
|
|
unsigned char i;
|
|
|
|
raw_spin_lock_irqsave(&monitors_lock, flags);
|
|
|
|
for (i = 0; i < MONITORS_COUNT; i++) {
|
|
if (monitors[i].is_used) {
|
|
event = monitors[i].event;
|
|
mode_name = monitors_mode_info[monitors[i].mode].name;
|
|
|
|
if (str_start) {
|
|
seq_printf(m, "%s %s 0x%x",
|
|
monitors_id_names[i],
|
|
mode_name,
|
|
event);
|
|
str_start = 0;
|
|
} else
|
|
seq_printf(m, " %s %s 0x%x",
|
|
monitors_id_names[i],
|
|
mode_name,
|
|
event);
|
|
}
|
|
}
|
|
|
|
raw_spin_unlock_irqrestore(&monitors_lock, flags);
|
|
|
|
if (!str_start)
|
|
seq_printf(m, "%s", "\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int monitors_events_proc_show(struct seq_file *m, void *data)
|
|
{
|
|
unsigned long flags;
|
|
unsigned char i, j;
|
|
|
|
raw_spin_lock_irqsave(&monitors_lock, flags);
|
|
|
|
for (i = 0; i < NR_CPUS; i++) {
|
|
if (!cpu_online(i))
|
|
continue;
|
|
|
|
for (j = 0; j < MONITORS_COUNT; j++) {
|
|
if (!monitors[j].is_used)
|
|
continue;
|
|
|
|
seq_printf(m, "CPU%d:%s:%s:0x%x=%lld\n",
|
|
i,
|
|
monitors_id_names[j],
|
|
monitors_mode_info[monitors[j].mode].name,
|
|
monitors[j].event,
|
|
atomic64_read(&common_events_count[i][j]));
|
|
}
|
|
}
|
|
|
|
raw_spin_unlock_irqrestore(&monitors_lock, flags);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int monitors_dead_proc_events_seq_show(struct seq_file *s, void *v)
|
|
{
|
|
unsigned char dead_proc_id;
|
|
dead_proc_events_t *dead_proc_event;
|
|
pid_t pid;
|
|
cpumask_t cpus_mask;
|
|
struct monitors_info monitor;
|
|
unsigned long events_count;
|
|
unsigned char i, j;
|
|
|
|
dead_proc_id = *((loff_t *)v);
|
|
|
|
if (dead_proc_events_buf_full_tmp)
|
|
dead_proc_id =
|
|
(dead_proc_events_buf_id_tmp + dead_proc_id + 1) %
|
|
DEAD_PROC_EVENTS_COUNT;
|
|
|
|
dead_proc_event = &dead_proc_events_buf_tmp[dead_proc_id];
|
|
pid = dead_proc_event->pid;
|
|
cpus_mask = dead_proc_event->cpus_mask;
|
|
|
|
seq_printf(s, "pid=%d:\n", pid);
|
|
|
|
for (i = 0; i < NR_CPUS; i++) {
|
|
if (!cpumask_test_cpu(i, &cpus_mask))
|
|
continue;
|
|
|
|
for (j = 0; j < MONITORS_COUNT; j++) {
|
|
monitor = dead_proc_event->monitors[j];
|
|
|
|
if (!monitor.is_used)
|
|
continue;
|
|
|
|
events_count =
|
|
dead_proc_event->monitors_count[i][j];
|
|
|
|
seq_printf(s, "CPU%d:%s:%s:0x%x=%lu\n",
|
|
i,
|
|
monitors_id_names[j],
|
|
monitors_mode_info[monitor.mode].name,
|
|
monitor.event,
|
|
events_count);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void *monitors_dead_proc_events_seq_start(
|
|
struct seq_file *s, loff_t *pos)
|
|
{
|
|
unsigned char dead_proc_count;
|
|
unsigned long flags;
|
|
|
|
raw_spin_lock(&dead_proc_lock_tmp);
|
|
|
|
if (*pos == 0) {
|
|
raw_spin_lock_irqsave(&dead_proc_lock, flags);
|
|
|
|
memcpy(dead_proc_events_buf_tmp, dead_proc_events_buf,
|
|
sizeof(dead_proc_events_t) * DEAD_PROC_EVENTS_COUNT);
|
|
dead_proc_events_buf_id_tmp = dead_proc_events_buf_id;
|
|
dead_proc_events_buf_full_tmp = dead_proc_events_buf_full;
|
|
|
|
dead_proc_events_buf_id = -1;
|
|
dead_proc_events_buf_full = 0;
|
|
|
|
raw_spin_unlock_irqrestore(&dead_proc_lock, flags);
|
|
}
|
|
|
|
if (dead_proc_events_buf_full_tmp)
|
|
dead_proc_count = DEAD_PROC_EVENTS_COUNT;
|
|
else
|
|
dead_proc_count = dead_proc_events_buf_id_tmp + 1;
|
|
|
|
if (*pos >= dead_proc_count)
|
|
return NULL;
|
|
|
|
return (void *)pos;
|
|
}
|
|
|
|
static void *monitors_dead_proc_events_seq_next(struct seq_file *s, void *v,
|
|
loff_t *pos)
|
|
{
|
|
unsigned char dead_proc_count;
|
|
|
|
(*pos)++;
|
|
|
|
if (dead_proc_events_buf_full_tmp)
|
|
dead_proc_count = DEAD_PROC_EVENTS_COUNT;
|
|
else
|
|
dead_proc_count = dead_proc_events_buf_id_tmp + 1;
|
|
|
|
if (*pos >= dead_proc_count)
|
|
return NULL;
|
|
|
|
return (void *)pos;
|
|
}
|
|
|
|
static void monitors_dead_proc_events_seq_stop(struct seq_file *s, void *v)
|
|
{
|
|
/*
|
|
* We unlock dead_proc_lock_tmp here, because we could not lock it
|
|
* for a long time and perform user code with it is locked. But one
|
|
* could 'cat /proc/monitors/dead_proc_events' simultaneously with us.
|
|
* In this case we recieve wrong data for some dead processes. Now we
|
|
* do nothing with it.
|
|
*/
|
|
raw_spin_unlock(&dead_proc_lock_tmp);
|
|
}
|
|
|
|
static const struct seq_operations monitors_dead_proc_events_seq_ops = {
|
|
.start = monitors_dead_proc_events_seq_start,
|
|
.next = monitors_dead_proc_events_seq_next,
|
|
.stop = monitors_dead_proc_events_seq_stop,
|
|
.show = monitors_dead_proc_events_seq_show
|
|
};
|
|
|
|
static int monitors_dead_proc_events_proc_open(struct inode *inode,
|
|
struct file *file)
|
|
{
|
|
return seq_open(file, &monitors_dead_proc_events_seq_ops);
|
|
}
|
|
|
|
static const struct proc_ops monitors_dead_proc_events_proc_ops = {
|
|
.proc_open = monitors_dead_proc_events_proc_open,
|
|
.proc_read = seq_read,
|
|
.proc_lseek = seq_lseek,
|
|
.proc_release = seq_release
|
|
};
|
|
|
|
static int monitors_help_seq_show(struct seq_file *s, void *v)
|
|
{
|
|
unsigned char id = *((loff_t *)v);
|
|
unsigned char i;
|
|
|
|
if (id == DIM0) {
|
|
seq_printf(s, "I0, I1 events:\n");
|
|
for (i = 0; i < dim_monitors_events_range_count; i++) {
|
|
if (i)
|
|
seq_printf(s, ", ");
|
|
if (dim_monitors_events_list[i].start !=
|
|
dim_monitors_events_list[i].end)
|
|
seq_printf(s, "0x%x, ..., 0x%x",
|
|
dim_monitors_events_list[i].start,
|
|
dim_monitors_events_list[i].end);
|
|
else
|
|
seq_printf(s, "0x%x",
|
|
dim_monitors_events_list[i].end);
|
|
}
|
|
} else if (id == DDM0) {
|
|
seq_printf(s, "\nD0 events:\n");
|
|
for (i = 0; i < ddm0_monitors_events_range_count; i++) {
|
|
if (i)
|
|
seq_printf(s, ", ");
|
|
if (ddm0_monitors_events_list[i].start !=
|
|
ddm0_monitors_events_list[i].end)
|
|
seq_printf(s, "0x%x, ..., 0x%x",
|
|
ddm0_monitors_events_list[i].start,
|
|
ddm0_monitors_events_list[i].end);
|
|
else
|
|
seq_printf(s, "0x%x",
|
|
ddm0_monitors_events_list[i].end);
|
|
}
|
|
} else if (id == DDM1) {
|
|
seq_printf(s, "\nD1 events:\n");
|
|
for (i = 0; i < ddm1_monitors_events_range_count; i++) {
|
|
if (i)
|
|
seq_printf(s, ", ");
|
|
if (ddm1_monitors_events_list[i].start !=
|
|
ddm1_monitors_events_list[i].end)
|
|
seq_printf(s, "0x%x, ..., 0x%x",
|
|
ddm1_monitors_events_list[i].start,
|
|
ddm1_monitors_events_list[i].end);
|
|
else
|
|
seq_printf(s, "0x%x",
|
|
ddm1_monitors_events_list[i].end);
|
|
}
|
|
} else if (id == MONITORS_COUNT) {
|
|
seq_printf(s, "\nSetting example:\n"
|
|
"echo \"D0 S 0x1 D1 C 0x2\" > "
|
|
"/proc/monitors/monitors_settings\n");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void *monitors_help_seq_start(struct seq_file *s, loff_t *pos)
|
|
{
|
|
if (*pos >= MONITORS_COUNT + 1)
|
|
return NULL;
|
|
return (void *)pos;
|
|
}
|
|
|
|
static void *monitors_help_seq_next(struct seq_file *s, void *v, loff_t *pos)
|
|
{
|
|
(*pos)++;
|
|
if (*pos >= MONITORS_COUNT + 1)
|
|
return NULL;
|
|
return (void *)pos;
|
|
}
|
|
|
|
static void monitors_help_seq_stop(struct seq_file *s, void *v)
|
|
{
|
|
}
|
|
|
|
static const struct seq_operations monitors_help_seq_ops = {
|
|
.start = monitors_help_seq_start,
|
|
.next = monitors_help_seq_next,
|
|
.stop = monitors_help_seq_stop,
|
|
.show = monitors_help_seq_show
|
|
};
|
|
|
|
static int monitors_help_proc_open(struct inode *inode, struct file *file)
|
|
{
|
|
return seq_open(file, &monitors_help_seq_ops);
|
|
}
|
|
|
|
static const struct proc_ops monitors_help_proc_ops = {
|
|
.proc_open = monitors_help_proc_open,
|
|
.proc_read = seq_read,
|
|
.proc_lseek = seq_lseek,
|
|
.proc_release = seq_release
|
|
};
|
|
|
|
|
|
/*
|
|
* SIC monitors
|
|
*/
|
|
|
|
static int sicmonitors_events_proc_show(struct seq_file *m, void *data)
|
|
{
|
|
e2k_sic_mar_lo_t mar_lo;
|
|
e2k_sic_mar_hi_struct_t mar_hi;
|
|
int monitor_id;
|
|
int node;
|
|
unsigned long flags;
|
|
unsigned char i;
|
|
|
|
raw_spin_lock_irqsave(&sicmonitors_lock, flags);
|
|
|
|
for (i = 0; i < SICMONITORS_COUNT; i++) {
|
|
if (sicmonitors[i].is_used) {
|
|
node = i / SICMONITORS_COUNT_PER_NODE;
|
|
monitor_id = i % SICMONITORS_COUNT_PER_NODE;
|
|
|
|
switch (monitor_id) {
|
|
case MCM0:
|
|
mar_lo = sic_read_node_nbsr_reg(
|
|
node, SIC_sic_mar0_lo);
|
|
mar_hi.E2K_SIC_MAR_HI_reg =
|
|
sic_read_node_nbsr_reg(
|
|
node, SIC_sic_mar0_hi);
|
|
break;
|
|
case MCM1:
|
|
mar_lo = sic_read_node_nbsr_reg(
|
|
node, SIC_sic_mar1_lo);
|
|
mar_hi.E2K_SIC_MAR_HI_reg =
|
|
sic_read_node_nbsr_reg(
|
|
node, SIC_sic_mar1_hi);
|
|
break;
|
|
default:
|
|
continue;
|
|
}
|
|
|
|
seq_printf(m, "NODE%d:%s:0x%x=0x%x%x\n", node,
|
|
sicmonitors_id_names[monitor_id],
|
|
sicmonitors[i].event,
|
|
mar_hi.E2K_SIC_MAR_HI_val, mar_lo);
|
|
}
|
|
}
|
|
|
|
raw_spin_unlock_irqrestore(&sicmonitors_lock, flags);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline char lookup_sicmonitors_id(char *str, unsigned short len)
|
|
{
|
|
unsigned char i;
|
|
char *name;
|
|
|
|
for (i = 0; i < SICMONITORS_COUNT; i++) {
|
|
name = sicmonitors_id_names[i];
|
|
|
|
if (len == strlen(name) && strncmp(str, name, len) == 0)
|
|
return i;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static inline int lookup_sicmonitors_event(char *str, unsigned short len,
|
|
int monitor)
|
|
{
|
|
int event = -1;
|
|
struct sicmonitors_events_range *sicmonitors_events_list;
|
|
unsigned char sicmonitors_events_range_count;
|
|
unsigned char i;
|
|
|
|
switch (monitor) {
|
|
case MCM0:
|
|
sicmonitors_events_range_count =
|
|
mcm0_sicmonitors_events_range_count;
|
|
sicmonitors_events_list = mcm0_sicmonitors_events_list;
|
|
break;
|
|
case MCM1:
|
|
sicmonitors_events_range_count =
|
|
mcm1_sicmonitors_events_range_count;
|
|
sicmonitors_events_list = mcm1_sicmonitors_events_list;
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
|
|
sscanf(str, "0x%X", &event);
|
|
|
|
for (i = 0; i < sicmonitors_events_range_count; i++) {
|
|
if (sicmonitors_events_list[i].start <= event &&
|
|
sicmonitors_events_list[i].end >= event)
|
|
return event;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static inline bool is_mcr_l3_event(u64 event)
|
|
{
|
|
if (IS_MACHINE_E8C && event >= 32 && event < 64)
|
|
return true;
|
|
|
|
if (IS_MACHINE_E8C2 && event >= 32 && event < 64)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
static void sicmonitors_adjust(int node, int monitor,
|
|
struct sicmonitors_info *new_monitors)
|
|
{
|
|
e2k_sic_mcr_struct_t mcr_reg;
|
|
e2k_sic_mar_lo_t mar_lo;
|
|
e2k_sic_mar_hi_struct_t mar_hi;
|
|
int idx;
|
|
u64 event;
|
|
|
|
idx = node * SICMONITORS_COUNT_PER_NODE + monitor;
|
|
event = new_monitors[idx].event;
|
|
|
|
mcr_reg.E2K_SIC_MCR_reg = sic_read_node_nbsr_reg(node, SIC_sic_mcr);
|
|
mar_lo = 0;
|
|
mar_hi.E2K_SIC_MAR_HI_reg = 0;
|
|
if (is_mcr_l3_event(event))
|
|
AS(mcr_reg).mcnmo = 1;
|
|
|
|
if (monitor == MCM0) {
|
|
mcr_reg.E2K_SIC_MCR_v0 = new_monitors[idx].is_used;
|
|
mcr_reg.E2K_SIC_MCR_es0 = event;
|
|
|
|
sic_write_node_nbsr_reg(
|
|
node, SIC_sic_mcr, mcr_reg.E2K_SIC_MCR_reg);
|
|
sic_write_node_nbsr_reg(
|
|
node, SIC_sic_mar0_lo, mar_lo);
|
|
sic_write_node_nbsr_reg(
|
|
node, SIC_sic_mar0_hi, mar_hi.E2K_SIC_MAR_HI_reg);
|
|
} else if (monitor == MCM1) {
|
|
mcr_reg.E2K_SIC_MCR_v1 = new_monitors[idx].is_used;
|
|
mcr_reg.E2K_SIC_MCR_es1 = event;
|
|
|
|
sic_write_node_nbsr_reg(
|
|
node, SIC_sic_mcr, mcr_reg.E2K_SIC_MCR_reg);
|
|
sic_write_node_nbsr_reg(
|
|
node, SIC_sic_mar1_lo, mar_lo);
|
|
sic_write_node_nbsr_reg(
|
|
node, SIC_sic_mar1_hi, mar_hi.E2K_SIC_MAR_HI_reg);
|
|
}
|
|
}
|
|
|
|
static inline void parse_sicmonitors_settings_string(char *str)
|
|
{
|
|
unsigned short i = 0;
|
|
unsigned short len1 = 0, len2 = 0, len3 = 0;
|
|
unsigned char is_last = 0;
|
|
struct sicmonitors_info new_monitors[SICMONITORS_COUNT];
|
|
int node;
|
|
char monitor_id;
|
|
int event;
|
|
unsigned long flags;
|
|
|
|
memset(new_monitors, 0, sizeof(struct sicmonitors_info) *
|
|
SICMONITORS_COUNT);
|
|
|
|
while (!is_last) {
|
|
if (i % 3 == 0) {
|
|
len1 = monitors_settings_string_get_next_word_len(
|
|
str, &is_last);
|
|
|
|
/*
|
|
* We check, if input string is an empty string, or if
|
|
* it is an invalid string (without sicmonitor name or
|
|
* number), or if it is a valid string.
|
|
*/
|
|
if (is_last && (i || len1 > 1 || (len1 &&
|
|
strncmp(str, "\n", 1)))) {
|
|
pr_err("Failed to adjust sicmonitors (invalid "
|
|
"settings string).\n");
|
|
return;
|
|
}
|
|
} else if (i % 3 == 1) {
|
|
len2 = monitors_settings_string_get_next_word_len(
|
|
str + len1 + 1, &is_last);
|
|
|
|
if (is_last) {
|
|
pr_err("Failed to adjust sicmonitors (invalid "
|
|
"settings string).\n");
|
|
return;
|
|
}
|
|
} else {
|
|
len3 = monitors_settings_string_get_next_word_len(
|
|
str + len1 + len2 + 2, &is_last);
|
|
|
|
node = NUMA_NO_NODE;
|
|
sscanf(str, "%d", &node);
|
|
if (!node_online(node)) {
|
|
pr_err("Failed to adjust sicmonitors (invalid "
|
|
"node number).\n");
|
|
return;
|
|
}
|
|
|
|
monitor_id = lookup_sicmonitors_id(
|
|
str + len1 + 1, len2);
|
|
if (monitor_id == -1) {
|
|
pr_err("Failed to adjust sicmonitors (invalid "
|
|
"monitor name).\n");
|
|
return;
|
|
}
|
|
|
|
event = lookup_sicmonitors_event(
|
|
str + len1 + len2 + 2,
|
|
len3, monitor_id);
|
|
if (event == -1) {
|
|
pr_err("Failed to adjust sicmonitors (invalid "
|
|
"event number).\n");
|
|
return;
|
|
}
|
|
|
|
monitor_id += node * SICMONITORS_COUNT_PER_NODE;
|
|
|
|
new_monitors[(unsigned char)
|
|
monitor_id].is_used = 1;
|
|
new_monitors[(unsigned char)
|
|
monitor_id].event = event;
|
|
|
|
str += len1 + len2 + len3 + 3;
|
|
}
|
|
|
|
i++;
|
|
}
|
|
|
|
raw_spin_lock_irqsave(&sicmonitors_lock, flags);
|
|
|
|
for (i = 0; i < SICMONITORS_COUNT; i++) {
|
|
if (new_monitors[i].is_used != sicmonitors[i].is_used ||
|
|
new_monitors[i].event !=
|
|
sicmonitors[i].event) {
|
|
sicmonitors_adjust(i / SICMONITORS_COUNT_PER_NODE,
|
|
i % SICMONITORS_COUNT_PER_NODE, new_monitors);
|
|
}
|
|
}
|
|
|
|
memcpy(sicmonitors, new_monitors,
|
|
sizeof(struct sicmonitors_info) * SICMONITORS_COUNT);
|
|
|
|
raw_spin_unlock_irqrestore(&sicmonitors_lock, flags);
|
|
}
|
|
|
|
static ssize_t sicmonitors_settings_write(struct file *file,
|
|
const char __user *buffer, size_t count, loff_t *data)
|
|
{
|
|
char monitors_settings_buffer[SICMONITORS_SETTINGS_STR_MAX_SIZE];
|
|
|
|
memset(monitors_settings_buffer, 0, sizeof(char) *
|
|
SICMONITORS_SETTINGS_STR_MAX_SIZE);
|
|
|
|
if (count > SICMONITORS_SETTINGS_STR_MAX_SIZE - 1) {
|
|
pr_err("Failed to adjust sicmonitors (too long settings string).\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (copy_from_user(monitors_settings_buffer, buffer, count)) {
|
|
pr_err("Failed to adjust sicmonitors (kernel error).\n");
|
|
return -EFAULT;
|
|
}
|
|
|
|
parse_sicmonitors_settings_string(monitors_settings_buffer);
|
|
|
|
return count;
|
|
}
|
|
|
|
static int sicmonitors_settings_proc_show(struct seq_file *m, void *data)
|
|
{
|
|
int monitor_id;
|
|
int node;
|
|
unsigned char str_start = 1;
|
|
unsigned long flags;
|
|
unsigned char i;
|
|
|
|
raw_spin_lock_irqsave(&sicmonitors_lock, flags);
|
|
|
|
for (i = 0; i < SICMONITORS_COUNT; i++) {
|
|
if (sicmonitors[i].is_used) {
|
|
node = i / SICMONITORS_COUNT_PER_NODE;
|
|
monitor_id = i % SICMONITORS_COUNT_PER_NODE;
|
|
|
|
if (str_start) {
|
|
seq_printf(m, "NODE%d %s 0x%x",
|
|
node,
|
|
sicmonitors_id_names[monitor_id],
|
|
sicmonitors[i].event);
|
|
str_start = 0;
|
|
} else
|
|
seq_printf(m, " NODE%d %s 0x%x",
|
|
node,
|
|
sicmonitors_id_names[monitor_id],
|
|
sicmonitors[i].event);
|
|
}
|
|
}
|
|
|
|
raw_spin_unlock_irqrestore(&sicmonitors_lock, flags);
|
|
|
|
if (!str_start)
|
|
seq_printf(m, "%s", "\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sicmonitors_help_seq_show(struct seq_file *s, void *v)
|
|
{
|
|
unsigned char id = *((loff_t *)v);
|
|
unsigned char i;
|
|
|
|
if (id == MCM0) {
|
|
seq_printf(s, "M0 events:\n");
|
|
for (i = 0; i < mcm0_sicmonitors_events_range_count; i++) {
|
|
if (i)
|
|
seq_printf(s, ", ");
|
|
if (mcm0_sicmonitors_events_list[i].start !=
|
|
mcm0_sicmonitors_events_list[i].end)
|
|
seq_printf(s, "0x%x, ..., 0x%x",
|
|
mcm0_sicmonitors_events_list[i].start,
|
|
mcm0_sicmonitors_events_list[i].end);
|
|
else
|
|
seq_printf(s, "0x%x",
|
|
mcm0_sicmonitors_events_list[i].end);
|
|
}
|
|
} else if (id == MCM1) {
|
|
seq_printf(s, "\nM1 events:\n");
|
|
for (i = 0; i < mcm1_sicmonitors_events_range_count; i++) {
|
|
if (i)
|
|
seq_printf(s, ", ");
|
|
if (mcm1_sicmonitors_events_list[i].start !=
|
|
mcm1_sicmonitors_events_list[i].end)
|
|
seq_printf(s, "0x%x, ..., 0x%x",
|
|
mcm1_sicmonitors_events_list[i].start,
|
|
mcm1_sicmonitors_events_list[i].end);
|
|
else
|
|
seq_printf(s, "0x%x",
|
|
mcm1_sicmonitors_events_list[i].end);
|
|
}
|
|
} else if (id == SICMONITORS_COUNT_PER_NODE) {
|
|
seq_printf(s, "\nSetting example:\n"
|
|
"echo \"0 M0 0x2 0 M1 0x3\" > "
|
|
"/proc/monitors/sicmonitors_settings\n");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void *sicmonitors_help_seq_start(struct seq_file *s, loff_t *pos)
|
|
{
|
|
if (*pos >= SICMONITORS_COUNT_PER_NODE + 1)
|
|
return NULL;
|
|
return (void *)pos;
|
|
}
|
|
|
|
static void *sicmonitors_help_seq_next(struct seq_file *s, void *v,
|
|
loff_t *pos)
|
|
{
|
|
(*pos)++;
|
|
if (*pos >= SICMONITORS_COUNT_PER_NODE + 1)
|
|
return NULL;
|
|
return (void *)pos;
|
|
}
|
|
|
|
static void sicmonitors_help_seq_stop(struct seq_file *s, void *v)
|
|
{
|
|
}
|
|
|
|
static const struct seq_operations sicmonitors_help_seq_ops = {
|
|
.start = sicmonitors_help_seq_start,
|
|
.next = sicmonitors_help_seq_next,
|
|
.stop = sicmonitors_help_seq_stop,
|
|
.show = sicmonitors_help_seq_show
|
|
};
|
|
|
|
static int sicmonitors_help_proc_open(struct inode *inode, struct file *file)
|
|
{
|
|
return seq_open(file, &sicmonitors_help_seq_ops);
|
|
}
|
|
|
|
static const struct proc_ops sicmonitors_help_proc_ops = {
|
|
.proc_open = sicmonitors_help_proc_open,
|
|
.proc_read = seq_read,
|
|
.proc_lseek = seq_lseek,
|
|
.proc_release = seq_release
|
|
};
|
|
|
|
|
|
/*
|
|
* IPCC monitors
|
|
*/
|
|
|
|
static int ipccmonitors_events_proc_show(struct seq_file *m, void *data)
|
|
{
|
|
e2k_ipcc_str_struct_t ipcc_str_reg;
|
|
struct ipccmonitors_event_info event;
|
|
unsigned short event_id;
|
|
int node;
|
|
unsigned long flags;
|
|
unsigned char i;
|
|
|
|
raw_spin_lock_irqsave(&ipccmonitors_lock, flags);
|
|
|
|
if (ipccmonitors.is_used) {
|
|
event_id = ipccmonitors.event;
|
|
event = IPCC_event_info[event_id];
|
|
|
|
for_each_online_node(node) {
|
|
for (i = 1; i < SIC_IPCC_LINKS_COUNT + 1; i++) {
|
|
ipcc_str_reg.E2K_IPCC_STR_reg =
|
|
sic_get_ipcc_str(node, i);
|
|
|
|
seq_printf(m, "NODE%d:IPCC%d:%s(0x%x)=0x%x\n",
|
|
node, i,
|
|
event.name,
|
|
event.event,
|
|
ipcc_str_reg.E2K_IPCC_STR_ecnt);
|
|
}
|
|
}
|
|
}
|
|
|
|
raw_spin_unlock_irqrestore(&ipccmonitors_lock, flags);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline short lookup_ipccmonitors_event_id(char *str, unsigned short len)
|
|
{
|
|
unsigned char i;
|
|
char *name;
|
|
unsigned short event;
|
|
int input_event;
|
|
unsigned char is_event_as_num = 0;
|
|
|
|
is_event_as_num = sscanf(str, "0x%X", &input_event);
|
|
|
|
for (i = 0; i < IPCC_EVENTS_COUNT; i++) {
|
|
name = IPCC_event_info[i].name;
|
|
event = IPCC_event_info[i].event;
|
|
|
|
/*
|
|
* We can set event, using event name or using event number.
|
|
*/
|
|
if ((len == strlen(name) && strncmp(str, name, len) == 0) ||
|
|
(is_event_as_num && event == input_event))
|
|
return i;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static void ipccmonitors_adjust(struct ipccmonitors_info new_monitors)
|
|
{
|
|
e2k_ipcc_str_struct_t ipcc_str_reg;
|
|
unsigned short event_id = new_monitors.event;
|
|
int node;
|
|
int i;
|
|
|
|
for_each_online_node(node) {
|
|
for (i = 1; i < SIC_IPCC_LINKS_COUNT + 1; i++) {
|
|
ipcc_str_reg.E2K_IPCC_STR_reg =
|
|
sic_get_ipcc_str(node, i);
|
|
|
|
ipcc_str_reg.E2K_IPCC_STR_ecf =
|
|
(new_monitors.is_used ?
|
|
IPCC_event_info[event_id].event : 0);
|
|
ipcc_str_reg.E2K_IPCC_STR_eco = 1;
|
|
|
|
sic_set_ipcc_str(
|
|
node, i, ipcc_str_reg.E2K_IPCC_STR_reg);
|
|
}
|
|
}
|
|
}
|
|
|
|
static inline void parse_ipccmonitors_settings_string(char *str)
|
|
{
|
|
unsigned short len = 0;
|
|
unsigned char is_last = 0;
|
|
struct ipccmonitors_info new_monitors;
|
|
short event_id;
|
|
unsigned long flags;
|
|
|
|
memset(&new_monitors, 0, sizeof(struct ipccmonitors_info));
|
|
|
|
len = monitors_settings_string_get_next_word_len(str, &is_last);
|
|
|
|
if (!is_last) {
|
|
pr_err("Failed to adjust ipccmonitors (invalid settings "
|
|
"string).\n");
|
|
return;
|
|
}
|
|
|
|
if (len && strncmp(str, "\n", 1)) {
|
|
event_id = lookup_ipccmonitors_event_id(str, len);
|
|
if (event_id == -1) {
|
|
pr_err("Failed to adjust ipccmonitors (invalid event "
|
|
"name or number).\n");
|
|
return;
|
|
}
|
|
|
|
new_monitors.is_used = 1;
|
|
new_monitors.event = event_id;
|
|
}
|
|
|
|
raw_spin_lock_irqsave(&ipccmonitors_lock, flags);
|
|
|
|
if (new_monitors.is_used != ipccmonitors.is_used ||
|
|
new_monitors.event !=
|
|
ipccmonitors.event) {
|
|
ipccmonitors_adjust(new_monitors);
|
|
}
|
|
|
|
memcpy(&ipccmonitors, &new_monitors, sizeof(struct ipccmonitors_info));
|
|
|
|
raw_spin_unlock_irqrestore(&ipccmonitors_lock, flags);
|
|
}
|
|
|
|
static ssize_t ipccmonitors_settings_write(struct file *file,
|
|
const char __user *buffer, size_t count, loff_t *data)
|
|
{
|
|
char monitors_settings_buffer[IPCCMONITORS_SETTINGS_STR_MAX_SIZE];
|
|
int ret;
|
|
|
|
memset(monitors_settings_buffer, 0, sizeof(char) *
|
|
IPCCMONITORS_SETTINGS_STR_MAX_SIZE);
|
|
|
|
if (count > IPCCMONITORS_SETTINGS_STR_MAX_SIZE - 1) {
|
|
pr_err("Failed to adjust ipccmonitors (too long settings "
|
|
"string).\n");
|
|
ret = -EINVAL;
|
|
} else if (copy_from_user(monitors_settings_buffer, buffer, count)) {
|
|
pr_err("Failed to adjust ipccmonitors (kernel error).\n");
|
|
ret = -EFAULT;
|
|
} else {
|
|
parse_ipccmonitors_settings_string(monitors_settings_buffer);
|
|
ret = count;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int ipccmonitors_settings_proc_show(struct seq_file *m, void *data)
|
|
{
|
|
struct ipccmonitors_event_info event;
|
|
unsigned short event_id;
|
|
unsigned char is_used;
|
|
unsigned long flags;
|
|
|
|
raw_spin_lock_irqsave(&ipccmonitors_lock, flags);
|
|
is_used = ipccmonitors.is_used;
|
|
event_id = ipccmonitors.event;
|
|
raw_spin_unlock_irqrestore(&ipccmonitors_lock, flags);
|
|
|
|
if (is_used) {
|
|
event = IPCC_event_info[event_id];
|
|
seq_printf(m, "%s(0x%x)\n", event.name, event.event);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ipccmonitors_help_seq_show(struct seq_file *s, void *v)
|
|
{
|
|
unsigned char i;
|
|
|
|
seq_printf(s, "Events:\n");
|
|
|
|
for (i = 0; i < IPCC_EVENTS_COUNT; i++)
|
|
seq_printf(s, "%s=0x%x\n",
|
|
IPCC_event_info[i].name,
|
|
IPCC_event_info[i].event);
|
|
|
|
seq_printf(s, "\nSetting example:\n"
|
|
"echo \"0x1\" > /proc/monitors/ipccmonitors_settings\n"
|
|
"echo \"IPCC_LERR\" > /proc/monitors/ipccmonitors_settings\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void *ipccmonitors_help_seq_start(struct seq_file *s, loff_t *pos)
|
|
{
|
|
if (*pos >= IPCCMONITORS_COUNT)
|
|
return NULL;
|
|
return (void *)pos;
|
|
}
|
|
|
|
static void *ipccmonitors_help_seq_next(struct seq_file *s, void *v,
|
|
loff_t *pos)
|
|
{
|
|
(*pos)++;
|
|
if (*pos >= IPCCMONITORS_COUNT)
|
|
return NULL;
|
|
return (void *)pos;
|
|
}
|
|
|
|
static void ipccmonitors_help_seq_stop(struct seq_file *s, void *v)
|
|
{
|
|
}
|
|
|
|
static const struct seq_operations ipccmonitors_help_seq_ops = {
|
|
.start = ipccmonitors_help_seq_start,
|
|
.next = ipccmonitors_help_seq_next,
|
|
.stop = ipccmonitors_help_seq_stop,
|
|
.show = ipccmonitors_help_seq_show
|
|
};
|
|
|
|
static int ipccmonitors_help_proc_open(struct inode *inode, struct file *file)
|
|
{
|
|
return seq_open(file, &ipccmonitors_help_seq_ops);
|
|
}
|
|
|
|
static const struct proc_ops ipccmonitors_help_proc_ops = {
|
|
.proc_open = ipccmonitors_help_proc_open,
|
|
.proc_read = seq_read,
|
|
.proc_lseek = seq_lseek,
|
|
.proc_release = seq_release
|
|
};
|
|
|
|
|
|
/*
|
|
* IOCC monitors
|
|
*/
|
|
|
|
static int ioccmonitors_events_proc_show(struct seq_file *m, void *data)
|
|
{
|
|
e2k_io_str_struct_t io_str_reg;
|
|
struct ioccmonitors_event_info event;
|
|
unsigned short event_id;
|
|
int node;
|
|
char *s;
|
|
unsigned long flags;
|
|
unsigned char i;
|
|
|
|
raw_spin_lock_irqsave(&ioccmonitors_lock, flags);
|
|
|
|
if (ioccmonitors.is_used) {
|
|
event_id = ioccmonitors.event;
|
|
event = IOCC_event_info[event_id];
|
|
|
|
for_each_online_node(node) {
|
|
for (i = 0; i < SIC_IO_LINKS_COUNT; i++) {
|
|
io_str_reg.E2K_IO_STR_reg =
|
|
sic_get_io_str(node, i);
|
|
|
|
if (!i)
|
|
s = "IOCC";
|
|
else if (IS_MACHINE_E2S)
|
|
s = "IOCC_HI";
|
|
else
|
|
s = "IOCC1";
|
|
|
|
seq_printf(m, "NODE%d:%s:%s(0x%x)=0x%x\n",
|
|
node, s,
|
|
event.name,
|
|
event.event,
|
|
io_str_reg.E2K_IO_STR_rc);
|
|
}
|
|
}
|
|
}
|
|
|
|
raw_spin_unlock_irqrestore(&ioccmonitors_lock, flags);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline short lookup_ioccmonitors_event_id(char *str, unsigned short len)
|
|
{
|
|
unsigned char i;
|
|
char *name;
|
|
unsigned short event;
|
|
int input_event;
|
|
unsigned char is_event_as_num = 0;
|
|
|
|
is_event_as_num = sscanf(str, "0x%X", &input_event);
|
|
|
|
for (i = 0; i < IOCC_EVENTS_COUNT; i++) {
|
|
name = IOCC_event_info[i].name;
|
|
event = IOCC_event_info[i].event;
|
|
|
|
/*
|
|
* We can set event, using event name or using event number.
|
|
*/
|
|
if ((len == strlen(name) && strncmp(str, name, len) == 0) ||
|
|
(is_event_as_num && event == input_event))
|
|
return i;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static void ioccmonitors_adjust(struct ioccmonitors_info new_monitors)
|
|
{
|
|
e2k_io_str_struct_t io_str_reg;
|
|
unsigned short event_id = new_monitors.event;
|
|
int node;
|
|
int i;
|
|
|
|
for_each_online_node(node) {
|
|
for (i = 0; i < SIC_IO_LINKS_COUNT; i++) {
|
|
io_str_reg.E2K_IO_STR_reg = sic_get_io_str(node, i);
|
|
|
|
io_str_reg.E2K_IO_STR_reg &= ~E2K_IO_STR_EVENT_MASK;
|
|
if (new_monitors.is_used)
|
|
io_str_reg.E2K_IO_STR_reg |=
|
|
IOCC_event_info[event_id].event <<
|
|
E2K_IO_STR_EVENT_SHIFT;
|
|
io_str_reg.E2K_IO_STR_rcol = 1;
|
|
|
|
sic_set_io_str(node, i, io_str_reg.E2K_IO_STR_reg);
|
|
}
|
|
}
|
|
}
|
|
|
|
static inline void parse_ioccmonitors_settings_string(char *str)
|
|
{
|
|
unsigned short len = 0;
|
|
unsigned char is_last = 0;
|
|
struct ioccmonitors_info new_monitors;
|
|
short event_id;
|
|
unsigned long flags;
|
|
|
|
memset(&new_monitors, 0, sizeof(struct ioccmonitors_info));
|
|
|
|
len = monitors_settings_string_get_next_word_len(str, &is_last);
|
|
|
|
if (!is_last) {
|
|
pr_err("Failed to adjust ioccmonitors (invalid settings "
|
|
"string).\n");
|
|
return;
|
|
}
|
|
|
|
if (len && strncmp(str, "\n", 1)) {
|
|
event_id = lookup_ioccmonitors_event_id(str, len);
|
|
if (event_id == -1) {
|
|
pr_err("Failed to adjust ioccmonitors (invalid event "
|
|
"name or number).\n");
|
|
return;
|
|
}
|
|
|
|
new_monitors.is_used = 1;
|
|
new_monitors.event = event_id;
|
|
}
|
|
|
|
raw_spin_lock_irqsave(&ioccmonitors_lock, flags);
|
|
|
|
if (new_monitors.is_used != ioccmonitors.is_used ||
|
|
new_monitors.event !=
|
|
ioccmonitors.event) {
|
|
ioccmonitors_adjust(new_monitors);
|
|
}
|
|
|
|
memcpy(&ioccmonitors, &new_monitors, sizeof(struct ioccmonitors_info));
|
|
|
|
raw_spin_unlock_irqrestore(&ioccmonitors_lock, flags);
|
|
}
|
|
|
|
static ssize_t ioccmonitors_settings_write(struct file *file,
|
|
const char __user *buffer, size_t count, loff_t *data)
|
|
{
|
|
char monitors_settings_buffer[IOCCMONITORS_SETTINGS_STR_MAX_SIZE];
|
|
int ret;
|
|
|
|
memset(monitors_settings_buffer, 0, sizeof(char) *
|
|
IOCCMONITORS_SETTINGS_STR_MAX_SIZE);
|
|
|
|
if (count > IOCCMONITORS_SETTINGS_STR_MAX_SIZE - 1) {
|
|
pr_err("Failed to adjust ioccmonitors (too long settings "
|
|
"string).\n");
|
|
ret = -EINVAL;
|
|
} else if (copy_from_user(monitors_settings_buffer, buffer, count)) {
|
|
pr_err("Failed to adjust ioccmonitors (kernel error).\n");
|
|
ret = -EFAULT;
|
|
} else {
|
|
parse_ioccmonitors_settings_string(monitors_settings_buffer);
|
|
ret = count;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int ioccmonitors_settings_proc_show(struct seq_file *m, void *data)
|
|
{
|
|
struct ioccmonitors_event_info event;
|
|
unsigned short event_id;
|
|
unsigned char is_used;
|
|
unsigned long flags;
|
|
|
|
raw_spin_lock_irqsave(&ioccmonitors_lock, flags);
|
|
is_used = ioccmonitors.is_used;
|
|
event_id = ioccmonitors.event;
|
|
raw_spin_unlock_irqrestore(&ioccmonitors_lock, flags);
|
|
|
|
if (is_used) {
|
|
event = IOCC_event_info[event_id];
|
|
seq_printf(m, "%s(0x%x)\n", event.name, event.event);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ioccmonitors_help_seq_show(struct seq_file *s, void *v)
|
|
{
|
|
unsigned char i;
|
|
|
|
seq_printf(s, "Events:\n");
|
|
|
|
for (i = 0; i < IOCC_EVENTS_COUNT; i++)
|
|
seq_printf(s, "%s=0x%x\n",
|
|
IOCC_event_info[i].name,
|
|
IOCC_event_info[i].event);
|
|
|
|
seq_printf(s, "\nSetting example:\n"
|
|
"echo \"0x1\" > /proc/monitors/ioccmonitors_settings\n"
|
|
"echo \"IOCC_BSY_RC\" > /proc/monitors/ioccmonitors_settings\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void *ioccmonitors_help_seq_start(struct seq_file *s, loff_t *pos)
|
|
{
|
|
if (*pos >= IOCCMONITORS_COUNT)
|
|
return NULL;
|
|
return (void *)pos;
|
|
}
|
|
|
|
static void *ioccmonitors_help_seq_next(struct seq_file *s, void *v,
|
|
loff_t *pos)
|
|
{
|
|
(*pos)++;
|
|
if (*pos >= IOCCMONITORS_COUNT)
|
|
return NULL;
|
|
return (void *)pos;
|
|
}
|
|
|
|
static void ioccmonitors_help_seq_stop(struct seq_file *s, void *v)
|
|
{
|
|
}
|
|
|
|
static const struct seq_operations ioccmonitors_help_seq_ops = {
|
|
.start = ioccmonitors_help_seq_start,
|
|
.next = ioccmonitors_help_seq_next,
|
|
.stop = ioccmonitors_help_seq_stop,
|
|
.show = ioccmonitors_help_seq_show
|
|
};
|
|
|
|
static int ioccmonitors_help_proc_open(struct inode *inode, struct file *file)
|
|
{
|
|
return seq_open(file, &ioccmonitors_help_seq_ops);
|
|
}
|
|
|
|
static const struct proc_ops ioccmonitors_help_proc_ops = {
|
|
.proc_open = ioccmonitors_help_proc_open,
|
|
.proc_read = seq_read,
|
|
.proc_lseek = seq_lseek,
|
|
.proc_release = seq_release
|
|
};
|
|
|
|
static int monitors_settings_proc_open(struct inode *inode, struct file *file)
|
|
{
|
|
return single_open(file, monitors_settings_proc_show, NULL);
|
|
}
|
|
|
|
static const struct proc_ops monitors_settings_proc_ops = {
|
|
.proc_open = monitors_settings_proc_open,
|
|
.proc_read = seq_read,
|
|
.proc_lseek = seq_lseek,
|
|
.proc_release = single_release,
|
|
.proc_write = monitors_settings_write,
|
|
};
|
|
|
|
static int monitors_events_proc_open(struct inode *inode, struct file *file)
|
|
{
|
|
return single_open(file, monitors_events_proc_show, NULL);
|
|
}
|
|
|
|
static const struct proc_ops monitors_events_proc_ops = {
|
|
.proc_open = monitors_events_proc_open,
|
|
.proc_read = seq_read,
|
|
.proc_lseek = seq_lseek,
|
|
.proc_release = single_release,
|
|
};
|
|
|
|
static int sicmonitors_settings_proc_open(struct inode *inode,
|
|
struct file *file)
|
|
{
|
|
return single_open(file, sicmonitors_settings_proc_show, NULL);
|
|
}
|
|
|
|
static const struct proc_ops sicmonitors_settings_proc_ops = {
|
|
.proc_open = sicmonitors_settings_proc_open,
|
|
.proc_read = seq_read,
|
|
.proc_lseek = seq_lseek,
|
|
.proc_release = single_release,
|
|
.proc_write = sicmonitors_settings_write,
|
|
};
|
|
|
|
static int sicmonitors_events_proc_open(struct inode *inode, struct file *file)
|
|
{
|
|
return single_open(file, sicmonitors_events_proc_show, NULL);
|
|
}
|
|
|
|
static const struct proc_ops sicmonitors_events_proc_ops = {
|
|
.proc_open = sicmonitors_events_proc_open,
|
|
.proc_read = seq_read,
|
|
.proc_lseek = seq_lseek,
|
|
.proc_release = single_release,
|
|
};
|
|
|
|
static int ipccmonitors_settings_proc_open(struct inode *inode,
|
|
struct file *file)
|
|
{
|
|
return single_open(file, ipccmonitors_settings_proc_show, NULL);
|
|
}
|
|
|
|
static const struct proc_ops ipccmonitors_settings_proc_ops = {
|
|
.proc_open = ipccmonitors_settings_proc_open,
|
|
.proc_read = seq_read,
|
|
.proc_lseek = seq_lseek,
|
|
.proc_release = single_release,
|
|
.proc_write = ipccmonitors_settings_write,
|
|
};
|
|
|
|
static int ipccmonitors_events_proc_open(struct inode *inode, struct file *file)
|
|
{
|
|
return single_open(file, ipccmonitors_events_proc_show, NULL);
|
|
}
|
|
|
|
static const struct proc_ops ipccmonitors_events_proc_ops = {
|
|
.proc_open = ipccmonitors_events_proc_open,
|
|
.proc_read = seq_read,
|
|
.proc_lseek = seq_lseek,
|
|
.proc_release = single_release,
|
|
};
|
|
|
|
static int ioccmonitors_settings_proc_open(struct inode *inode,
|
|
struct file *file)
|
|
{
|
|
return single_open(file, ioccmonitors_settings_proc_show, NULL);
|
|
}
|
|
|
|
static const struct proc_ops ioccmonitors_settings_proc_ops = {
|
|
.proc_open = ioccmonitors_settings_proc_open,
|
|
.proc_read = seq_read,
|
|
.proc_lseek = seq_lseek,
|
|
.proc_release = single_release,
|
|
.proc_write = ioccmonitors_settings_write,
|
|
};
|
|
|
|
static int ioccmonitors_events_proc_open(struct inode *inode, struct file *file)
|
|
{
|
|
return single_open(file, ioccmonitors_events_proc_show, NULL);
|
|
}
|
|
|
|
static const struct proc_ops ioccmonitors_events_proc_ops = {
|
|
.proc_open = ioccmonitors_events_proc_open,
|
|
.proc_read = seq_read,
|
|
.proc_lseek = seq_lseek,
|
|
.proc_release = single_release,
|
|
};
|
|
|
|
|
|
/*
|
|
* Init
|
|
*/
|
|
|
|
#define setup_monitors(iset) \
|
|
({ \
|
|
ddm0_monitors_events_list = ddm0_monitors_events_list_v##iset; \
|
|
ddm1_monitors_events_list = ddm1_monitors_events_list_v##iset; \
|
|
dim_monitors_events_list = dim_monitors_events_list_v##iset; \
|
|
ddm0_monitors_events_range_count = \
|
|
sizeof(ddm0_monitors_events_list_v##iset) / \
|
|
sizeof(struct monitors_events_range); \
|
|
ddm1_monitors_events_range_count = \
|
|
sizeof(ddm1_monitors_events_list_v##iset) / \
|
|
sizeof(struct monitors_events_range); \
|
|
dim_monitors_events_range_count = \
|
|
sizeof(dim_monitors_events_list_v##iset) / \
|
|
sizeof(struct monitors_events_range); \
|
|
})
|
|
|
|
static void monitors_init(void)
|
|
{
|
|
switch (machine.native_iset_ver) {
|
|
case E2K_ISET_V3:
|
|
case E2K_ISET_V4:
|
|
setup_monitors(3);
|
|
break;
|
|
case E2K_ISET_V5:
|
|
setup_monitors(5);
|
|
break;
|
|
case E2K_ISET_V6:
|
|
setup_monitors(6);
|
|
case E2K_ISET_V7:
|
|
setup_monitors(7);
|
|
break;
|
|
default:
|
|
BUG();
|
|
}
|
|
|
|
proc_create(MONITORS_SETTINGS_FILENAME, S_IRUGO | S_IWUSR,
|
|
monitors_dir_entry, &monitors_settings_proc_ops);
|
|
proc_create(MONITORS_EVENTS_FILENAME, S_IRUGO,
|
|
monitors_dir_entry, &monitors_events_proc_ops);
|
|
proc_create(MONITORS_DEAD_PROC_EVENTS_FILENAME, S_IRUGO,
|
|
monitors_dir_entry, &monitors_dead_proc_events_proc_ops);
|
|
proc_create(MONITORS_HELP_FILENAME, S_IRUGO,
|
|
monitors_dir_entry, &monitors_help_proc_ops);
|
|
}
|
|
|
|
static void sicmonitors_init(void)
|
|
{
|
|
if (!HAS_MACHINE_SICMONITORS)
|
|
return;
|
|
|
|
if (IS_MACHINE_E2S) {
|
|
mcm0_sicmonitors_events_list = mcm0_sicmonitors_events_list_e4c;
|
|
mcm1_sicmonitors_events_list = mcm1_sicmonitors_events_list_e4c;
|
|
mcm0_sicmonitors_events_range_count =
|
|
MCM0_EVENTS_RANGE_COUNT_E4C;
|
|
mcm1_sicmonitors_events_range_count =
|
|
MCM1_EVENTS_RANGE_COUNT_E4C;
|
|
} else if (IS_MACHINE_E8C) {
|
|
mcm0_sicmonitors_events_list = mcm0_sicmonitors_events_list_e8c;
|
|
mcm1_sicmonitors_events_list = mcm1_sicmonitors_events_list_e8c;
|
|
mcm0_sicmonitors_events_range_count =
|
|
MCM0_EVENTS_RANGE_COUNT_E8C;
|
|
mcm1_sicmonitors_events_range_count =
|
|
MCM1_EVENTS_RANGE_COUNT_E8C;
|
|
} else if (IS_MACHINE_E8C2) {
|
|
mcm0_sicmonitors_events_list =
|
|
mcm0_sicmonitors_events_list_e8c2;
|
|
mcm1_sicmonitors_events_list =
|
|
mcm1_sicmonitors_events_list_e8c2;
|
|
mcm0_sicmonitors_events_range_count =
|
|
MCM0_EVENTS_RANGE_COUNT_E8C2;
|
|
mcm1_sicmonitors_events_range_count =
|
|
MCM1_EVENTS_RANGE_COUNT_E8C2;
|
|
} else {
|
|
BUG();
|
|
}
|
|
|
|
proc_create(SICMONITORS_SETTINGS_FILENAME, S_IRUGO | S_IWUSR,
|
|
monitors_dir_entry, &sicmonitors_settings_proc_ops);
|
|
proc_create(SICMONITORS_EVENTS_FILENAME, S_IRUGO,
|
|
monitors_dir_entry, &sicmonitors_events_proc_ops);
|
|
proc_create(SICMONITORS_HELP_FILENAME, S_IRUGO,
|
|
monitors_dir_entry, &sicmonitors_help_proc_ops);
|
|
}
|
|
|
|
static void ipccmonitors_init(void)
|
|
{
|
|
if (!HAS_MACHINE_IPCCMONITORS)
|
|
return;
|
|
|
|
proc_create(IPCCMONITORS_SETTINGS_FILENAME, S_IRUGO | S_IWUSR,
|
|
monitors_dir_entry, &ipccmonitors_settings_proc_ops);
|
|
proc_create(IPCCMONITORS_EVENTS_FILENAME, S_IRUGO,
|
|
monitors_dir_entry, &ipccmonitors_events_proc_ops);
|
|
proc_create(IPCCMONITORS_HELP_FILENAME, S_IRUGO,
|
|
monitors_dir_entry, &ipccmonitors_help_proc_ops);
|
|
}
|
|
|
|
static void ioccmonitors_init(void)
|
|
{
|
|
if (!HAS_MACHINE_IOCCMONITORS)
|
|
return;
|
|
|
|
proc_create(IOCCMONITORS_SETTINGS_FILENAME, S_IRUGO,
|
|
monitors_dir_entry, &ioccmonitors_settings_proc_ops);
|
|
proc_create(IOCCMONITORS_EVENTS_FILENAME, S_IRUGO,
|
|
monitors_dir_entry, &ioccmonitors_events_proc_ops);
|
|
proc_create(IOCCMONITORS_HELP_FILENAME, S_IRUGO,
|
|
monitors_dir_entry, &ioccmonitors_help_proc_ops);
|
|
}
|
|
|
|
static int __init monitors_module_init(void)
|
|
{
|
|
monitors_dir_entry = proc_mkdir(MONITORS_FILENAME, NULL);
|
|
if (!monitors_dir_entry)
|
|
return 0;
|
|
|
|
monitors_init();
|
|
sicmonitors_init();
|
|
ipccmonitors_init();
|
|
ioccmonitors_init();
|
|
|
|
return 0;
|
|
}
|
|
|
|
module_init(monitors_module_init);
|
|
|