[PATCH][2.6] rtas error-inject support

Jake Moilanen moilanen at austin.ibm.com
Fri Feb 6 06:01:26 EST 2004


> In general, since this is a patch for 2.6 (according to the subject
> line :), it would be better to do all this in userspace using the rtas
> syscall that was added recently.  But here are comments on the patch
> anyway:

I mentioned this to you on IRC, but the main reason I was trying to keep
the same interface was because this funcationality is for test teams.
The test teams already have tools and test suites that use this
interface in 2.4.  I would hate to break them when they will probably
need this interface in the very near future.

> > +config RTAS_ERRINJCT
> > +	bool "RTAS Errinject"
>
> How about bool "RTAS Error injection facility" ?
agreed

> > +static unsigned int open_token = 0;
>
> No need to initialize things to 0, C does that by default.

Done.  Normally I try following the style of the particular file.  If
this is bothering you, you probably want to clean up a couple other
variables in the file.

> > @@ -207,7 +221,8 @@
> >  void proc_rtas_init(void)
> >  {
> >  	struct proc_dir_entry *entry;
> > -
> > + 	int errinjct_token;
> > +
>
> I can't see any difference between the blank line that is deleted and
> the one that is inserted (not even any whitespace).  I wonder why diff
> put that in?  Or has your mailer deleted trailing whitespace?

not sure either.

>
> That's a user pointer that you're using strnlen on.  Ouch.  Use
> strnlen_user or copy_from_user.  In fact, do we need to check the
> string length at all?  Would it matter if there was a null in the
> buffer, and the value taken as a string was shorter than we thought?

strnlen taken out.  I don't remember what the thought was for having
this in there.  It was over a year ago when this was originally written.

>
> Another access to the user buffer without using *user functions.
> Ouch.

Fixed.

>
> This worries me.  We copy the workspace (the contents of which are
> undefined since we just kmalloc'd it) to rtas_data_buf, but we never
> copy rtas_data_buf back to the workspace after the rtas call.  So how
> can the contents of workspace ever be anything but undefined?

I'm not sure I'm following you here.  The workspace is passed in (from
ppc_rtas_errinjct_write).  The workspace information is just for RTAS to
know the specifics on the error that is being injected (or NULL
depending of if uses the workspace on that particular injection).

> Why can't we just store a pointer to the token name within the OF
> property value?  Why do we have to make a copy of it?

IIRC the original thought was to keep the string parsing to one place
and only do it once.

> In fact, why do we need to parse the list here at all?  We use the
> list for two things: matching the token name in the write function,
> and listing the tokens in the read function.  In both cases we could
> just run through the ibm,errinjct-tokens (what do they have against
> vwls?) property value almost as easily as ei_token_list.

Changed it to do this parsing.

> > +/* 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
>
> I worry about these too.  15 tokens sounds like future machines are
> going to going to exceed this limit quite easily.  Also, is the
> workspace size limit there something that applies to all RTAS
> functions, or just to the error injection functions?  If the latter,
> you should choose a name that indicates that.

This was taken directly out of the RPA.  If the RPA changes in the
future, it's just a matter of changing a #define.  A workspace is used
on other rtas calls, but none of them we have implemented
(ibm,get-indices, ibm,get-vpd, etc...).  Until that day, I changed this
to ERRINJCT_WORKSPACE_SIZE.

> Overall, this looks to me like something that could be done just as
> well or better in userspace.  Doing it in userspace would make it
> easy to avoid having an arbitrary limit on the number of tokens, for
> instance.  Userspace could just read
> /proc/device-tree/rtas/ibm,errinjct-tokens to get the list and match
> against that directly.

This could probably done just as well in user land.  The problem is that
some of these test teams need to start testing very soon.  Their
resources are thin, I doubt they'll have time to rewrite interfaces. Why
don't we use this interface for now and depricate it and tell the test
teams that the interface will change in the 2.7 time frame.

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.1415  -> 1.1416
#	arch/ppc64/kernel/rtas.c	1.22    -> 1.23
#	arch/ppc64/defconfig	1.42    -> 1.43
#	arch/ppc64/kernel/rtas-proc.c	1.13    -> 1.14
#	  arch/ppc64/Kconfig	1.39    -> 1.40
#	include/asm-ppc64/rtas.h	1.17    -> 1.18
#
# The following is the BitKeeper ChangeSet Log
# --------------------------------------------
# 04/02/05	moilanen at zippy.ltc.austin.ibm.com	1.1416
# RTAS Error Inject support
# --------------------------------------------
#
diff -Nru a/arch/ppc64/Kconfig b/arch/ppc64/Kconfig
--- a/arch/ppc64/Kconfig	Thu Feb  5 12:47:44 2004
+++ b/arch/ppc64/Kconfig	Thu Feb  5 12:47:44 2004
@@ -165,6 +165,14 @@
 	Provide system capacity information via human readable
 	<key word>=<value> pairs through a /proc/ppc64/lparcfg interface.

+config RTAS_ERRINJCT
+	bool "RTAS Error inject facility"
+	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	Thu Feb  5 12:47:44 2004
+++ b/arch/ppc64/defconfig	Thu Feb  5 12:47:44 2004
@@ -65,6 +65,7 @@
 CONFIG_RTAS_FLASH=m
 CONFIG_SCANLOG=m
 CONFIG_LPARCFG=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	Thu Feb  5 12:47:44 2004
+++ b/arch/ppc64/kernel/rtas-proc.c	Thu Feb  5 12:47:44 2004
@@ -126,6 +126,9 @@

 static unsigned long rtas_tone_frequency = 1000;
 static unsigned long rtas_tone_volume = 0;
+#ifdef CONFIG_RTAS_ERRINJCT
+static unsigned int open_token;
+#endif

 /* ****************STRUCTS******************************************* */
 struct individual_sensor {
@@ -165,6 +168,14 @@
 		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);
+#ifdef CONFIG_RTAS_ERRINJCT
+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);
+#endif

 struct file_operations ppc_rtas_poweron_operations = {
 	.read =		ppc_rtas_poweron_read,
@@ -189,6 +200,15 @@
 	.write =	ppc_rtas_tone_volume_write
 };

+#ifdef CONFIG_RTAS_ERRINJCT
+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
+};
+#endif
+
 static struct file_operations ppc_rtas_rmo_buf_ops = {
 	.read =		ppc_rtas_rmo_buf_read,
 };
@@ -244,6 +264,13 @@

 	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
+ 	if (rtas_token("ibm,errinjct") != 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
 }

 /* ****************************************************************** */
@@ -932,6 +959,157 @@
 	*ppos += n;
 	return n;
 }
+
+#ifdef CONFIG_RTAS_ERRINJCT
+/* ****************************************************************** */
+/* 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;
+	}
+
+	ei_token = (char *)kmalloc(max_len, GFP_KERNEL);
+	if (!ei_token) {
+		printk(KERN_WARNING "error: kmalloc failed\n");
+		return -ENOMEM;
+	}
+
+	token_len = strncpy_from_user(ei_token, buf, max_len);
+	if (token_len <= 0) {
+		kfree(ei_token);
+		return -EFAULT;
+	}
+	token_len++;
+
+	if (count > token_len + ERRINJCT_WORKSPACE_SIZE) {
+		count = token_len + ERRINJCT_WORKSPACE_SIZE;
+	}
+
+	buf += token_len;
+
+	/* check if there is a workspace */
+	if (count > token_len) {
+		/* Verify the workspace size */
+		if ((count - token_len) > ERRINJCT_WORKSPACE_SIZE) {
+			max_len = ERRINJCT_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;
+	char * token_array;
+	char * token_array_end;
+	int array_len;
+	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;
+	}
+
+	token_array = (char *) get_property(rtas.dev, "ibm,errinjct-tokens", &array_len);
+	token_array_end = token_array + array_len;
+
+	while (token_array < token_array_end) {
+		n += sprintf(buffer+n, token_array);
+		token_array += strlen(token_array) + 1;
+		n += sprintf(buffer+n, " %d\n", *(int *)token_array);
+		token_array += sizeof(int);
+	}
+
+	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;
+}
+#endif /* CONFIG_RTAS_ERRINJCT */

 #define RMO_READ_BUF_MAX 30

diff -Nru a/arch/ppc64/kernel/rtas.c b/arch/ppc64/kernel/rtas.c
--- a/arch/ppc64/kernel/rtas.c	Thu Feb  5 12:47:44 2004
+++ b/arch/ppc64/kernel/rtas.c	Thu Feb  5 12:47:44 2004
@@ -225,6 +225,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)
@@ -463,6 +467,127 @@
 	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)
+{
+	char * token_array;
+	char * token_array_end;
+	int array_len;
+	int rtas_ei_token = -1;
+	unsigned int time;
+	int rc = 0;
+
+	token_array = (char *) get_property(rtas.dev, "ibm,errinjct-tokens", &array_len);
+	token_array_end = token_array + array_len;
+
+	while (token_array <= token_array_end) {
+		if (strcmp(token_array, ei_token) == 0) {
+			rtas_ei_token = *(int *)(token_array + strlen(token_array) + 1);
+			break;
+		}
+		token_array += strlen(token_array) + 1;
+		token_array += sizeof(int);
+	}
+
+ 	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;
+ 	}
+}
+
+#endif

 EXPORT_SYMBOL(rtas_firmware_flash_list);
 EXPORT_SYMBOL(rtas_token);
diff -Nru a/include/asm-ppc64/rtas.h b/include/asm-ppc64/rtas.h
--- a/include/asm-ppc64/rtas.h	Thu Feb  5 12:47:44 2004
+++ b/include/asm-ppc64/rtas.h	Thu Feb  5 12:47:44 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 ERRINJCT_WORKSPACE_SIZE 1024
+
 /* RTAS return codes */
 #define RTAS_BUSY		-2	/* RTAS Return Status - Busy */
 #define RTAS_EXTENDED_DELAY_MIN 9900
@@ -178,6 +183,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);


More information about the Linuxppc64-dev mailing list