[PATCH][2.6] rtas error-inject support
Jake Moilanen
moilanen at austin.ibm.com
Sat Jan 31 09:29:03 EST 2004
>
> That statement was alarming... :) I only found one memcpy left in that
> file in 2.4, but I guess we're supposed to check for EFAULT:
Here's a patch w/ check for EFAULT.
Thanks,
Jake
-------------- next part --------------
# This is a BitKeeper generated patch for the following project:
# Project Name: Linux kernel tree
# This patch format is intended for GNU patch command version 2.5 or higher.
# This patch includes the following deltas:
# ChangeSet 1.1393 -> 1.1394
# arch/ppc64/kernel/rtas.c 1.21 -> 1.22
# arch/ppc64/defconfig 1.41 -> 1.42
# arch/ppc64/kernel/rtas-proc.c 1.12 -> 1.13
# arch/ppc64/Kconfig 1.35 -> 1.36
# include/asm-ppc64/rtas.h 1.17 -> 1.18
#
# The following is the BitKeeper ChangeSet Log
# --------------------------------------------
# 04/01/30 moilanen at threadlp13.austin.ibm.com 1.1394
# Error inject support.
# --------------------------------------------
#
diff -Nru a/arch/ppc64/Kconfig b/arch/ppc64/Kconfig
--- a/arch/ppc64/Kconfig Fri Jan 30 16:25:35 2004
+++ b/arch/ppc64/Kconfig Fri Jan 30 16:25:35 2004
@@ -164,6 +164,14 @@
Provide system capacity information via human readable
<key word>=<value> pairs through a /proc/ppc64/lparcfg interface.
+config RTAS_ERRINJCT
+ bool "RTAS Errinject"
+ depends on PPC_RTAS
+ help
+ Provide ability to inject errors into hardware for the purpose
+ of testing hardware error code path. Do not use on production
+ machine.
+
endmenu
diff -Nru a/arch/ppc64/defconfig b/arch/ppc64/defconfig
--- a/arch/ppc64/defconfig Fri Jan 30 16:25:35 2004
+++ b/arch/ppc64/defconfig Fri Jan 30 16:25:35 2004
@@ -59,7 +59,7 @@
# CONFIG_RTAS_FLASH is not set
CONFIG_SCANLOG=y
CONFIG_PPC_RTAS=y
-
+# CONFIG_RTAS_ERRINJCT is not set
#
# General setup
#
diff -Nru a/arch/ppc64/kernel/rtas-proc.c b/arch/ppc64/kernel/rtas-proc.c
--- a/arch/ppc64/kernel/rtas-proc.c Fri Jan 30 16:25:35 2004
+++ b/arch/ppc64/kernel/rtas-proc.c Fri Jan 30 16:25:35 2004
@@ -126,6 +126,7 @@
static unsigned long rtas_tone_frequency = 1000;
static unsigned long rtas_tone_volume = 0;
+static unsigned int open_token = 0;
/* ****************STRUCTS******************************************* */
struct individual_sensor {
@@ -165,6 +166,12 @@
size_t count, loff_t *ppos);
static ssize_t ppc_rtas_rmo_buf_read(struct file *file, char *buf,
size_t count, loff_t *ppos);
+static int ppc_rtas_errinjct_open(struct inode *inode, struct file *file);
+static int ppc_rtas_errinjct_release(struct inode *inode, struct file *file);
+static ssize_t ppc_rtas_errinjct_write(struct file * file, const char * buf,
+ size_t count, loff_t *ppos);
+static ssize_t ppc_rtas_errinjct_read(struct file *file, char *buf,
+ size_t count, loff_t *ppos);
struct file_operations ppc_rtas_poweron_operations = {
.read = ppc_rtas_poweron_read,
@@ -189,6 +196,13 @@
.write = ppc_rtas_tone_volume_write
};
+struct file_operations ppc_rtas_errinjct_operations = {
+ .open = ppc_rtas_errinjct_open,
+ .read = ppc_rtas_errinjct_read,
+ .write = ppc_rtas_errinjct_write,
+ .release = ppc_rtas_errinjct_release
+};
+
static struct file_operations ppc_rtas_rmo_buf_ops = {
.read = ppc_rtas_rmo_buf_read,
};
@@ -207,7 +221,8 @@
void proc_rtas_init(void)
{
struct proc_dir_entry *entry;
-
+ int errinjct_token;
+
rtas_node = of_find_node_by_name(NULL, "rtas");
if ((rtas_node == NULL) || (systemcfg->platform == PLATFORM_ISERIES_LPAR)) {
return;
@@ -244,6 +259,14 @@
entry = create_proc_entry("rmo_buffer", S_IRUSR, proc_ppc64.rtas);
if (entry) entry->proc_fops = &ppc_rtas_rmo_buf_ops;
+
+#ifdef CONFIG_RTAS_ERRINJCT
+ errinjct_token = rtas_token("ibm,errinjct");
+ if (errinjct_token != RTAS_UNKNOWN_SERVICE) {
+ entry = create_proc_entry("errinjct",S_IWUSR|S_IRUGO, proc_ppc64.rtas);
+ if (entry) entry->proc_fops = &ppc_rtas_errinjct_operations;
+ }
+#endif
}
/* ****************************************************************** */
@@ -928,6 +951,146 @@
return -EFAULT;
}
*ppos += n;
+ return n;
+}
+
+/* ****************************************************************** */
+/* ERRINJCT */
+/* ****************************************************************** */
+static int ppc_rtas_errinjct_open(struct inode *inode, struct file *file)
+{
+ int rc;
+
+ /* We will only allow one process to use error inject at a
+ time. Since errinjct is usually only used for testing,
+ this shouldn't be an issue */
+ if (open_token) {
+ return -EAGAIN;
+ }
+ rc = rtas_errinjct_open();
+ if (rc < 0) {
+ return -EIO;
+ }
+ open_token = rc;
+
+ return 0;
+}
+
+static ssize_t ppc_rtas_errinjct_write(struct file * file, const char * buf,
+ size_t count, loff_t *ppos)
+{
+
+ char * ei_token;
+ char * workspace = NULL;
+ size_t max_len;
+ int token_len;
+ int rc;
+
+ /* Verify the errinjct token length */
+ if (count < ERRINJCT_TOKEN_LEN) {
+ max_len = count;
+ } else {
+ max_len = ERRINJCT_TOKEN_LEN;
+ }
+
+ token_len = strnlen(buf, max_len);
+ token_len++; /* Add one for the null termination */
+
+ ei_token = (char *)kmalloc(token_len, GFP_KERNEL);
+ if (!ei_token) {
+ printk(KERN_WARNING "error: kmalloc failed\n");
+ return -ENOMEM;
+ }
+
+ strncpy(ei_token, buf, token_len);
+
+ if (count > token_len + WORKSPACE_SIZE) {
+ count = token_len + WORKSPACE_SIZE;
+ }
+
+ buf += token_len;
+
+ /* check if there is a workspace */
+ if (count > token_len) {
+ /* Verify the workspace size */
+ if ((count - token_len) > WORKSPACE_SIZE) {
+ max_len = WORKSPACE_SIZE;
+ } else {
+ max_len = count - token_len;
+ }
+
+ workspace = (char *)kmalloc(max_len, GFP_KERNEL);
+ if (!workspace) {
+ printk(KERN_WARNING "error: failed kmalloc\n");
+ kfree(ei_token);
+ return -ENOMEM;
+ }
+ if (copy_from_user(workspace, buf, max_len)) {
+ kfree(ei_token);
+ kfree(workspace);
+ return -EFAULT;
+ }
+ }
+
+ rc = rtas_errinjct(open_token, ei_token, workspace, max_len);
+
+ if (count > token_len) {
+ kfree(workspace);
+ }
+ kfree(ei_token);
+
+ return rc < 0 ? rc : count;
+}
+
+static int ppc_rtas_errinjct_release(struct inode *inode, struct file *file)
+{
+ int rc;
+
+ rc = rtas_errinjct_close(open_token);
+ if (rc) {
+ return rc;
+ }
+ open_token = 0;
+ return 0;
+}
+
+static ssize_t ppc_rtas_errinjct_read(struct file *file, char *buf,
+ size_t count, loff_t *ppos)
+{
+ char * buffer;
+ int i;
+ int n = 0;
+
+ buffer = (char *)kmalloc(MAX_ERRINJCT_TOKENS * (ERRINJCT_TOKEN_LEN+1),
+ GFP_KERNEL);
+ if (!buffer) {
+ printk(KERN_ERR "error: kmalloc failed\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < MAX_ERRINJCT_TOKENS && ei_token_list[i].value; i++) {
+ n += sprintf(buffer+n, ei_token_list[i].name);
+ n += sprintf(buffer+n, "\n");
+ }
+
+ if (*ppos >= strlen(buffer)) {
+ kfree(buffer);
+ return 0;
+ }
+ if (n > strlen(buffer) - *ppos)
+ n = strlen(buffer) - *ppos;
+
+ if (n > count)
+ n = count;
+
+ if (copy_to_user(buf, buffer + *ppos, n)) {
+ kfree(buffer);
+ return -EFAULT;
+ }
+
+ *ppos += n;
+
+ kfree(buffer);
return n;
}
diff -Nru a/arch/ppc64/kernel/rtas.c b/arch/ppc64/kernel/rtas.c
--- a/arch/ppc64/kernel/rtas.c Fri Jan 30 16:25:35 2004
+++ b/arch/ppc64/kernel/rtas.c Fri Jan 30 16:25:35 2004
@@ -33,6 +33,7 @@
#include <asm/uaccess.h>
struct flash_block_list_header rtas_firmware_flash_list = {0, 0};
+struct errinjct_token ei_token_list[MAX_ERRINJCT_TOKENS];
/*
* prom_init() is called very early on, before the kernel text
@@ -191,6 +192,10 @@
int order = status - 9900;
unsigned long ms;
+ if (status < RTAS_EXTENDED_DELAY_MIN ||
+ status > RTAS_EXTENDED_DELAY_MAX)
+ return 0;
+
if (order < 0)
order = 0; /* RTC depends on this for -2 clock busy */
else if (order > 5)
@@ -423,6 +428,159 @@
return 0;
}
+
+#ifdef CONFIG_RTAS_ERRINJCT
+int
+rtas_errinjct_open(void)
+{
+ u32 ret[2];
+ int open_token;
+ int rc;
+ unsigned int time;
+
+
+ while (1) {
+ /*
+ * The rc and open_token values are backwards due to a
+ * misprint in the RPA.
+ */
+ open_token = rtas_call(rtas_token("ibm,open-errinjct"), 0, 2, (void *) &ret);
+ rc = ret[0];
+
+ if (rc == RTAS_BUSY) {
+ continue;
+ }
+
+ if ((time = rtas_extended_busy_delay_time(rc))) {
+ udelay(time * 1000);
+ continue;
+ }
+
+ if (rc < 0) {
+ printk(KERN_WARNING "error: ibm,open-errinjct failed (%d)\n", rc);
+ return rc;
+ }
+
+ return open_token;
+ }
+}
+
+int
+rtas_errinjct(unsigned int open_token, char * ei_token, char * workspace, size_t workspace_size)
+{
+ struct errinjct_token * ei;
+ int rtas_ei_token = -1;
+ unsigned int time;
+ int rc = 0;
+ int i;
+
+ ei = ei_token_list;
+ for (i = 0; i < MAX_ERRINJCT_TOKENS && ei->name; i++) {
+ if (strcmp(ei_token, ei->name) == 0) {
+ rtas_ei_token = ei->value;
+ break;
+ }
+ ei++;
+ }
+ if (rtas_ei_token == -1) {
+ return -EINVAL;
+ }
+
+ spin_lock(&rtas_data_buf_lock);
+
+ while (1) {
+ if (rc != RTAS_BUSY && workspace) {
+ memset(rtas_data_buf, 0, RTAS_DATA_BUF_SIZE);
+ memcpy(rtas_data_buf, workspace, workspace_size);
+ }
+
+ rc = rtas_call(rtas_token("ibm,errinjct"), 3, 1, NULL,
+ rtas_ei_token, open_token, __pa(rtas_data_buf));
+
+ if (rc == RTAS_BUSY) {
+ continue;
+ }
+
+ if ((time = rtas_extended_busy_delay_time(rc))) {
+ spin_unlock(&rtas_data_buf_lock);
+ udelay(time * 1000);
+ spin_lock(&rtas_data_buf_lock);
+ continue;
+ }
+
+ if (rc != 0) {
+ printk(KERN_WARNING "error: ibm,errinjct failed (%d)\n", rc);
+ }
+
+ spin_unlock(&rtas_data_buf_lock);
+
+ return rc;
+ }
+}
+
+int
+rtas_errinjct_close(unsigned int open_token)
+{
+ int rc;
+ unsigned int time;
+
+ while (1) {
+ rc = rtas_call(rtas_token("ibm,close-errinjct"), 1, 1, NULL, open_token);
+
+ if (rc == RTAS_BUSY) {
+ continue;
+ }
+
+ if ((time = rtas_extended_busy_delay_time(rc))) {
+ udelay(time * 1000);
+ continue;
+ }
+
+ if (rc != 0) {
+ printk(KERN_WARNING "error: ibm,close-errinjct failed (%d)\n", rc);
+ }
+
+ return rc;
+ }
+}
+
+static int __init rtas_errinjct_init(void)
+{
+ char * token_array;
+ char * end_array;
+ int array_len = 0;
+ int len;
+ int i, j;
+
+ token_array = (char *) get_property(rtas.dev, "ibm,errinjct-tokens",
+ &array_len);
+ end_array = token_array + array_len;
+ for (i = 0, j = 0; i < MAX_ERRINJCT_TOKENS && token_array < end_array; i++) {
+
+ len = strnlen(token_array, ERRINJCT_TOKEN_LEN) + 1;
+ ei_token_list[i].name = (char *) kmalloc(len, GFP_KERNEL);
+ if (!ei_token_list[i].name) {
+ printk(KERN_WARNING "error: kmalloc failed\n");
+ return -ENOMEM;
+ }
+
+ strcpy(ei_token_list[i].name, token_array);
+ token_array += len;
+
+ ei_token_list[i].value = *(int *)token_array;
+ token_array += sizeof(int);
+ }
+ for (; i < MAX_ERRINJCT_TOKENS; i++) {
+ ei_token_list[i].name = 0;
+ ei_token_list[i].value = 0;
+ }
+
+ return 0;
+
+}
+#endif
+
+__initcall(rtas_errinjct_init);
EXPORT_SYMBOL(rtas_firmware_flash_list);
diff -Nru a/include/asm-ppc64/rtas.h b/include/asm-ppc64/rtas.h
--- a/include/asm-ppc64/rtas.h Fri Jan 30 16:25:35 2004
+++ b/include/asm-ppc64/rtas.h Fri Jan 30 16:25:35 2004
@@ -22,6 +22,11 @@
/* Buffer size for ppc_rtas system call. */
#define RTAS_RMOBUF_MAX (64 * 1024)
+/* Error inject defines */
+#define ERRINJCT_TOKEN_LEN 24 /* Max length of an error inject token */
+#define MAX_ERRINJCT_TOKENS 15 /* Max # tokens. */
+#define WORKSPACE_SIZE 1024
+
/* RTAS return codes */
#define RTAS_BUSY -2 /* RTAS Return Status - Busy */
#define RTAS_EXTENDED_DELAY_MIN 9900
@@ -141,6 +146,11 @@
unsigned char buffer[1]; /* allocated by klimit bump */
};
+struct errinjct_token {
+ char * name;
+ int value;
+};
+
struct flash_block {
char *data;
unsigned long length;
@@ -178,6 +188,9 @@
extern int rtas_get_sensor(int sensor, int index, int *state);
extern int rtas_get_power_level(int powerdomain, int *level);
extern int rtas_set_indicator(int indicator, int index, int new_value);
+extern int rtas_errinjct_open(void);
+extern int rtas_errinjct(unsigned int, char *, char *, size_t);
+extern int rtas_errinjct_close(unsigned int);
/* Given an RTAS status code of 9900..9905 compute the hinted delay */
unsigned int rtas_extended_busy_delay_time(int status);
@@ -187,6 +200,7 @@
}
extern void pSeries_log_error(char *buf, unsigned int err_type, int fatal);
+extern struct errinjct_token ei_token_list[MAX_ERRINJCT_TOKENS];
/* Error types logged. */
#define ERR_FLAG_ALREADY_LOGGED 0x0
More information about the Linuxppc64-dev
mailing list