powerpc/rtas: Add rtas_call_unlocked()
authorMichael Ellerman <mpe@ellerman.id.au>
Wed, 16 Dec 2015 10:01:42 +0000 (21:01 +1100)
committerMichael Ellerman <mpe@ellerman.id.au>
Thu, 17 Dec 2015 11:40:55 +0000 (22:40 +1100)
Most users of RTAS (Run-Time Abstraction Services) use rtas_call(),
which deals with locking as well as endian handling.

However we have two users outside of rtas.c that can't use rtas_call()
because they have different locking requirements.

The hotplug CPU code can't take the RTAS lock because the CPU would go
offline with the lock held and no other CPUs would be able to call RTAS
until the CPU came back online.

The xmon code doesn't want to take the lock because it would risk dead
locking when we are trying to recover from a crash.

Both sites required multiple patches when we added little endian
support, proving that programmers can't do endian right.

Although that ship has sailed, we can still clean the code up by
providing an unlocked version of rtas_call() which avoids the need to
open code the logic elsewhere.

Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
arch/powerpc/include/asm/rtas.h
arch/powerpc/kernel/rtas.c

index b77ef369c0f0ea9b3641201c049aa11378788db1..6db1d6977a0df90830ea66bfa332e3e7acd97830 100644 (file)
@@ -338,6 +338,8 @@ extern void enter_rtas(unsigned long);
 extern int rtas_token(const char *service);
 extern int rtas_service_present(const char *service);
 extern int rtas_call(int token, int, int, int *, ...);
+void rtas_call_unlocked(struct rtas_args *args, int token, int nargs,
+                       int nret, ...);
 extern void rtas_restart(char *cmd);
 extern void rtas_power_off(void);
 extern void rtas_halt(void);
index 5a753fae8265ae8fc2f9e2e99152c0192fd62b9d..fcf2d653a6fe32bd54a84cd27f9f2b64033e5a8d 100644 (file)
@@ -418,6 +418,36 @@ static char *__fetch_rtas_last_error(char *altbuf)
 #define get_errorlog_buffer()          NULL
 #endif
 
+
+static void
+va_rtas_call_unlocked(struct rtas_args *args, int token, int nargs, int nret,
+                     va_list list)
+{
+       int i;
+
+       args->token = cpu_to_be32(token);
+       args->nargs = cpu_to_be32(nargs);
+       args->nret  = cpu_to_be32(nret);
+       args->rets  = &(args->args[nargs]);
+
+       for (i = 0; i < nargs; ++i)
+               args->args[i] = cpu_to_be32(va_arg(list, __u32));
+
+       for (i = 0; i < nret; ++i)
+               args->rets[i] = 0;
+
+       enter_rtas(__pa(args));
+}
+
+void rtas_call_unlocked(struct rtas_args *args, int token, int nargs, int nret, ...)
+{
+       va_list list;
+
+       va_start(list, nret);
+       va_rtas_call_unlocked(args, token, nargs, nret, list);
+       va_end(list);
+}
+
 int rtas_call(int token, int nargs, int nret, int *outputs, ...)
 {
        va_list list;
@@ -431,22 +461,14 @@ int rtas_call(int token, int nargs, int nret, int *outputs, ...)
                return -1;
 
        s = lock_rtas();
+
+       /* We use the global rtas args buffer */
        rtas_args = &rtas.args;
 
-       rtas_args->token = cpu_to_be32(token);
-       rtas_args->nargs = cpu_to_be32(nargs);
-       rtas_args->nret  = cpu_to_be32(nret);
-       rtas_args->rets  = &(rtas_args->args[nargs]);
        va_start(list, outputs);
-       for (i = 0; i < nargs; ++i)
-               rtas_args->args[i] = cpu_to_be32(va_arg(list, __u32));
+       va_rtas_call_unlocked(rtas_args, token, nargs, nret, list);
        va_end(list);
 
-       for (i = 0; i < nret; ++i)
-               rtas_args->rets[i] = 0;
-
-       enter_rtas(__pa(rtas_args));
-
        /* A -1 return code indicates that the last command couldn't
           be completed due to a hardware error. */
        if (be32_to_cpu(rtas_args->rets[0]) == -1)