[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