Commit 3805981a authored by Paul Mackerras's avatar Paul Mackerras Committed by Linus Torvalds

[PATCH] ppc64: RTAS error log locking fix

From: Linas Vepstas <linas@austin.ibm.com>

When an RTAS call returns the "hardware error" code, we need to do another
RTAS call to find out what went wrong.  Previously we weren't doing that
inside the lock that serializes RTAS calls, and thus another cpu could get
in and do another RTAS call in the meantime.  This patch fixes it.  This
patch also includes some minor whitespace fixes.
Signed-off-by: default avatarLinas Vepstas <linas@linas.org>
Signed-off-by: default avatarPaul Mackerras <paulus@samba.org>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent e92ae54f
...@@ -74,8 +74,14 @@ rtas_token(const char *service) ...@@ -74,8 +74,14 @@ rtas_token(const char *service)
} }
/** Return a copy of the detailed error text associated with the
* most recent failed call to rtas. Because the error text
* might go stale if there are any other intervening rtas calls,
* this routine must be called atomically with whatever produced
* the error (i.e. with rtas.lock still held from the previous call).
*/
static int static int
__log_rtas_error(void) __fetch_rtas_last_error(void)
{ {
struct rtas_args err_args, save_args; struct rtas_args err_args, save_args;
...@@ -102,25 +108,13 @@ __log_rtas_error(void) ...@@ -102,25 +108,13 @@ __log_rtas_error(void)
return err_args.rets[0]; return err_args.rets[0];
} }
void
log_rtas_error(void)
{
unsigned long s;
int rc;
spin_lock_irqsave(&rtas.lock, s);
rc = __log_rtas_error();
spin_unlock_irqrestore(&rtas.lock, s);
if (rc == 0)
log_error(rtas_err_buf, ERR_TYPE_RTAS_LOG, 0);
}
int rtas_call(int token, int nargs, int nret, int *outputs, ...) int rtas_call(int token, int nargs, int nret, int *outputs, ...)
{ {
va_list list; va_list list;
int i, logit = 0; int i, logit = 0;
unsigned long s; unsigned long s;
struct rtas_args *rtas_args; struct rtas_args *rtas_args;
char * buff_copy = NULL;
int ret; int ret;
PPCDBG(PPCDBG_RTAS, "Entering rtas_call\n"); PPCDBG(PPCDBG_RTAS, "Entering rtas_call\n");
...@@ -154,8 +148,10 @@ int rtas_call(int token, int nargs, int nret, int *outputs, ...) ...@@ -154,8 +148,10 @@ int rtas_call(int token, int nargs, int nret, int *outputs, ...)
enter_rtas(__pa(rtas_args)); enter_rtas(__pa(rtas_args));
PPCDBG(PPCDBG_RTAS, "\treturned from rtas ...\n"); PPCDBG(PPCDBG_RTAS, "\treturned from rtas ...\n");
/* A -1 return code indicates that the last command couldn't
be completed due to a hardware error. */
if (rtas_args->rets[0] == -1) if (rtas_args->rets[0] == -1)
logit = (__log_rtas_error() == 0); logit = (__fetch_rtas_last_error() == 0);
ifppcdebug(PPCDBG_RTAS) { ifppcdebug(PPCDBG_RTAS) {
for(i=0; i < nret ;i++) for(i=0; i < nret ;i++)
...@@ -167,12 +163,21 @@ int rtas_call(int token, int nargs, int nret, int *outputs, ...) ...@@ -167,12 +163,21 @@ int rtas_call(int token, int nargs, int nret, int *outputs, ...)
outputs[i] = rtas_args->rets[i+1]; outputs[i] = rtas_args->rets[i+1];
ret = (nret > 0)? rtas_args->rets[0]: 0; ret = (nret > 0)? rtas_args->rets[0]: 0;
/* Log the error in the unlikely case that there was one. */
if (unlikely(logit)) {
buff_copy = kmalloc(RTAS_ERROR_LOG_MAX, GFP_ATOMIC);
if (buff_copy) {
memcpy(buff_copy, rtas_err_buf, RTAS_ERROR_LOG_MAX);
}
}
/* Gotta do something different here, use global lock for now... */ /* Gotta do something different here, use global lock for now... */
spin_unlock_irqrestore(&rtas.lock, s); spin_unlock_irqrestore(&rtas.lock, s);
if (logit) if (buff_copy) {
log_error(rtas_err_buf, ERR_TYPE_RTAS_LOG, 0); log_error(buff_copy, ERR_TYPE_RTAS_LOG, 0);
kfree(buff_copy);
}
return ret; return ret;
} }
...@@ -192,7 +197,7 @@ rtas_extended_busy_delay_time(int status) ...@@ -192,7 +197,7 @@ rtas_extended_busy_delay_time(int status)
/* Use microseconds for reasonable accuracy */ /* Use microseconds for reasonable accuracy */
for (ms=1; order > 0; order--) for (ms=1; order > 0; order--)
ms *= 10; ms *= 10;
return ms; return ms;
} }
...@@ -367,9 +372,9 @@ rtas_restart(char *cmd) ...@@ -367,9 +372,9 @@ rtas_restart(char *cmd)
if (rtas_firmware_flash_list.next) if (rtas_firmware_flash_list.next)
rtas_flash_firmware(); rtas_flash_firmware();
printk("RTAS system-reboot returned %d\n", printk("RTAS system-reboot returned %d\n",
rtas_call(rtas_token("system-reboot"), 0, 1, NULL)); rtas_call(rtas_token("system-reboot"), 0, 1, NULL));
for (;;); for (;;);
} }
void void
...@@ -377,10 +382,10 @@ rtas_power_off(void) ...@@ -377,10 +382,10 @@ rtas_power_off(void)
{ {
if (rtas_firmware_flash_list.next) if (rtas_firmware_flash_list.next)
rtas_flash_bypass_warning(); rtas_flash_bypass_warning();
/* allow power on only with power button press */ /* allow power on only with power button press */
printk("RTAS power-off returned %d\n", printk("RTAS power-off returned %d\n",
rtas_call(rtas_token("power-off"), 2, 1, NULL, -1, -1)); rtas_call(rtas_token("power-off"), 2, 1, NULL, -1, -1));
for (;;); for (;;);
} }
void void
...@@ -388,7 +393,7 @@ rtas_halt(void) ...@@ -388,7 +393,7 @@ rtas_halt(void)
{ {
if (rtas_firmware_flash_list.next) if (rtas_firmware_flash_list.next)
rtas_flash_bypass_warning(); rtas_flash_bypass_warning();
rtas_power_off(); rtas_power_off();
} }
/* Must be in the RMO region, so we place it here */ /* Must be in the RMO region, so we place it here */
...@@ -418,7 +423,9 @@ asmlinkage int ppc_rtas(struct rtas_args __user *uargs) ...@@ -418,7 +423,9 @@ asmlinkage int ppc_rtas(struct rtas_args __user *uargs)
{ {
struct rtas_args args; struct rtas_args args;
unsigned long flags; unsigned long flags;
char * buff_copy;
int nargs; int nargs;
int err_rc = 0;
if (!capable(CAP_SYS_ADMIN)) if (!capable(CAP_SYS_ADMIN))
return -EPERM; return -EPERM;
...@@ -437,17 +444,33 @@ asmlinkage int ppc_rtas(struct rtas_args __user *uargs) ...@@ -437,17 +444,33 @@ asmlinkage int ppc_rtas(struct rtas_args __user *uargs)
nargs * sizeof(rtas_arg_t)) != 0) nargs * sizeof(rtas_arg_t)) != 0)
return -EFAULT; return -EFAULT;
buff_copy = kmalloc(RTAS_ERROR_LOG_MAX, GFP_KERNEL);
spin_lock_irqsave(&rtas.lock, flags); spin_lock_irqsave(&rtas.lock, flags);
rtas.args = args; rtas.args = args;
enter_rtas(__pa(&rtas.args)); enter_rtas(__pa(&rtas.args));
args = rtas.args; args = rtas.args;
args.rets = &args.args[nargs];
/* A -1 return code indicates that the last command couldn't
be completed due to a hardware error. */
if (args.rets[0] == -1) {
err_rc = __fetch_rtas_last_error();
if ((err_rc == 0) && buff_copy) {
memcpy(buff_copy, rtas_err_buf, RTAS_ERROR_LOG_MAX);
}
}
spin_unlock_irqrestore(&rtas.lock, flags); spin_unlock_irqrestore(&rtas.lock, flags);
args.rets = (rtas_arg_t *)&(args.args[nargs]); if (buff_copy) {
if (args.rets[0] == -1) if ((args.rets[0] == -1) && (err_rc == 0)) {
log_rtas_error(); log_error(buff_copy, ERR_TYPE_RTAS_LOG, 0);
}
kfree(buff_copy);
}
/* Copy out args. */ /* Copy out args. */
if (copy_to_user(uargs->args + nargs, if (copy_to_user(uargs->args + nargs,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment