android_kernel_modules_leno.../debug_tools/vtunedk/src/vtsspp/nmiwd.c

276 lines
7.2 KiB
C

/* Copyright (C) 2014-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
*/
#include "vtss_config.h"
#include "nmiwd.h"
#include "vtsserr.h"
#include <linux/cred.h> /* for current_uid_gid() */
#include <linux/watchdog.h>
#include <linux/namei.h>
#include <linux/fs.h>
#include <linux/file.h> //fput
#include <linux/sched.h>
#include <asm/uaccess.h>
#include <linux/proc_fs.h>
#include <linux/kthread.h> // for threads
#include <linux/delay.h>
static int vtss_wd_state = 0; //disabled
#ifdef CONFIG_PREEMPT_RT
static DEFINE_RAW_SPINLOCK(vtss_nmiwd_lock);
#else
static DEFINE_SPINLOCK(vtss_nmiwd_lock);
#endif
static ssize_t vtss_kernel_write(struct file *file, const char *buf, size_t count,
loff_t pos)
{
unsigned long res = 5;
unsigned long flags = WDIOS_DISABLECARD;
int options = 0;
mm_segment_t old_fs = get_fs();
set_fs(get_ds());
res = vfs_write(file, (const char __user *)buf, count, &pos);
set_fs(old_fs);
return res;
}
static struct file* vtss_open_nmi_wd(void)
{
struct file* fd = NULL;
fd = filp_open("/proc/sys/kernel/nmi_watchdog", O_RDWR, 0);
return fd;
}
void vtss_close(struct file* fd)
{
filp_close(fd,0);
}
/*int get_watchdog_device(void)
{
struct watchdog_device *wdd = NULL;
struct path nmi_path;
if (kern_path("/proc/sys/kernel/nmi_watchdog", 0, &nmi_path)){
TRACE("No kernel path for nmi_watchod.");
return -ENODEV;
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0)
if (!nmi_path.dentry->d_inode){
ERROR("nmi path does not contain inode");
path_put(&nmi_path);
return -1;
}
if (!PDE(nmi_path.dentry->d_inode)){
ERROR("nmi: no PDE related");
path_put(&nmi_path);
return -1;
}
wdd = (struct watchdog_device*)PDE(nmi_path.dentry->d_inode)->data;
#else
wdd = (struct watchdog_device*)PDE_DATA(nmi_path.dentry->d_inode);
#endif
if (!wdd){
ERROR("cannot get wdd");
} else {
INFO("wdd is found");
}
path_put(&nmi_path);
return 0;
}
*/
int vtss_nmi_watchdog_disable_thread(void* data)
{
char c = '0';
struct file* fd = NULL;
int ret_code = 1;
unsigned long flags;
struct cred* new;
//struct cred* old = (struct cred*)get_current_cred();
int* mode_ptr = NULL;
//get_watchdog_device();
if (!data)
{
ERROR("Internal error!");
return VTSS_ERR_INTERNAL;
}
mode_ptr = data;
new = prepare_kernel_cred(NULL);
if ( new != NULL ) {
commit_creds(new);
} else {
ERROR("Cannot prepare kernel creds.");
}
// preempt_disable();
// The function vtss_open_nmi_wd cannot be called when irqs disabled. Otherwise can lead deadlock in smp_call_function_single!
fd = vtss_open_nmi_wd();
if (IS_ERR(fd)) {
TRACE("Watchdog device not enabled.");
ret_code=2;
goto exit_mark_no_file;
}
spin_lock_irqsave(&vtss_nmiwd_lock, flags);
vtss_wd_state=vtss_wd_state + 1;
//check watchdog state
kernel_read(fd, 0, &c, 1);
if (c=='1' && vtss_wd_state != 1){
ERROR("wixing counter");
vtss_wd_state = 1;
}
if (vtss_wd_state == 1)
{
if (c=='0'){
TRACE("Watchdog device not enabled 1.");
vtss_wd_state=vtss_wd_state - 1;
ret_code=2;
goto unlock_exit_mark;
}
vtss_kernel_write(fd, "0", 1, 0);
INFO("writing 0");
kernel_read(fd, 0, &c, 1);
if (c!='0'){
ERROR("Internal error: Watchdog is not disabled yet! Add wait into the code");
}
} else {
INFO("was disabled already");
}
if (*mode_ptr == 1){
vtss_wd_state = vtss_wd_state - 1;
if (vtss_wd_state != 0) {
INFO("Watchdog state will not be changed as VTSS collection is runing");
ret_code = 2;
}
}
unlock_exit_mark:
spin_unlock_irqrestore(&vtss_nmiwd_lock, flags);
exit_mark:
vtss_close(fd);
exit_mark_no_file:
*mode_ptr = *mode_ptr+ret_code; //done!
return ret_code;
}
//static atom_t vtss_thread_num=0;
int vtsspp_nmi_root_thread_create(int (*threadfn)(void *data), const char* name, int mode)
{
int thread_ret = mode;
int count = 50;
struct task_struct *task = NULL;
task = kthread_run(threadfn,(void*)&thread_ret, name);
// if (IS_ERR(task)){ //the task can be finished! we need to check another way
// ERROR("thread %s cannot be created", name);
// return -1;
// }
TRACE("task tid= %d, task->state=%lx, task->exit_state=%lx", task->tgid, task->state, (unsigned long)task->exit_state);
while(thread_ret == mode && count > 0)
{
msleep_interruptible(10);
TRACE("in wait, task->state=%lx, task->exit_state=%lx", task->state, (unsigned long)task->exit_state);
count--;
}
return thread_ret - mode-1;
}
int vtss_nmi_watchdog_disable(int mode)
{
int ret;
ret = vtsspp_nmi_root_thread_create(&vtss_nmi_watchdog_disable_thread, "vtsspp_nmiwd_0", mode);
return ret;
}
int vtss_nmi_watchdog_enable_thread(void* data)
{
struct file* fd = NULL;
unsigned long nmi_flags = WDIOS_ENABLECARD;
unsigned long flags;
int ret_code = 1;
struct cred* new;
struct cred* old = (struct cred*)get_current_cred();
int* mode_ptr = NULL;
if (!data)
{
ERROR("Internal error!");
return VTSS_ERR_INTERNAL;
}
mode_ptr = data;
new = prepare_kernel_cred(NULL);
if ( new != NULL ) {
commit_creds(new);
}
fd = vtss_open_nmi_wd();
if (IS_ERR(fd)) {
TRACE("Watchdog device not enabled at the end of vtss collection.");
ret_code=2;
goto exit_mark_no_file;
}
spin_lock_irqsave(&vtss_nmiwd_lock, flags);
if (vtss_wd_state == 0 && *mode_ptr == 0)
{
TRACE("Watchdog device was not enabled before collection.");
ret_code=2;
goto unlock_exit_mark;
}
if (*mode_ptr == 0) vtss_wd_state=vtss_wd_state - 1;
if (vtss_wd_state == 0){
vtss_kernel_write(fd, "1", 1, 0);
}
else if (*mode_ptr == 1)
{
TRACE("NMI watchdog timer cannot be enabled as VTSS collection is not finshed yet");
ret_code = 2;
}
unlock_exit_mark:
spin_unlock_irqrestore(&vtss_nmiwd_lock, flags);
exit_mark:
vtss_close(fd);
exit_mark_no_file:
*mode_ptr = *mode_ptr+ret_code; //done!
return ret_code;
}
int vtss_nmi_watchdog_enable(int mode)
{
int ret;
ret = vtsspp_nmi_root_thread_create(&vtss_nmi_watchdog_enable_thread, "vtsspp_nmiwd_1", mode);
return ret;
}