|
@@ -47,14 +47,6 @@ struct rtas_t rtas = {
|
|
|
};
|
|
|
EXPORT_SYMBOL(rtas);
|
|
|
|
|
|
-struct rtas_suspend_me_data {
|
|
|
- atomic_t working; /* number of cpus accessing this struct */
|
|
|
- atomic_t done;
|
|
|
- int token; /* ibm,suspend-me */
|
|
|
- int error;
|
|
|
- struct completion *complete; /* wait on this until working == 0 */
|
|
|
-};
|
|
|
-
|
|
|
DEFINE_SPINLOCK(rtas_data_buf_lock);
|
|
|
EXPORT_SYMBOL(rtas_data_buf_lock);
|
|
|
|
|
@@ -714,14 +706,53 @@ void rtas_os_term(char *str)
|
|
|
|
|
|
static int ibm_suspend_me_token = RTAS_UNKNOWN_SERVICE;
|
|
|
#ifdef CONFIG_PPC_PSERIES
|
|
|
-static void rtas_percpu_suspend_me(void *info)
|
|
|
+static int __rtas_suspend_last_cpu(struct rtas_suspend_me_data *data, int wake_when_done)
|
|
|
+{
|
|
|
+ u16 slb_size = mmu_slb_size;
|
|
|
+ int rc = H_MULTI_THREADS_ACTIVE;
|
|
|
+ int cpu;
|
|
|
+
|
|
|
+ slb_set_size(SLB_MIN_SIZE);
|
|
|
+ printk(KERN_DEBUG "calling ibm,suspend-me on cpu %i\n", smp_processor_id());
|
|
|
+
|
|
|
+ while (rc == H_MULTI_THREADS_ACTIVE && !atomic_read(&data->done) &&
|
|
|
+ !atomic_read(&data->error))
|
|
|
+ rc = rtas_call(data->token, 0, 1, NULL);
|
|
|
+
|
|
|
+ if (rc || atomic_read(&data->error)) {
|
|
|
+ printk(KERN_DEBUG "ibm,suspend-me returned %d\n", rc);
|
|
|
+ slb_set_size(slb_size);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (atomic_read(&data->error))
|
|
|
+ rc = atomic_read(&data->error);
|
|
|
+
|
|
|
+ atomic_set(&data->error, rc);
|
|
|
+
|
|
|
+ if (wake_when_done) {
|
|
|
+ atomic_set(&data->done, 1);
|
|
|
+
|
|
|
+ for_each_online_cpu(cpu)
|
|
|
+ plpar_hcall_norets(H_PROD, get_hard_smp_processor_id(cpu));
|
|
|
+ }
|
|
|
+
|
|
|
+ if (atomic_dec_return(&data->working) == 0)
|
|
|
+ complete(data->complete);
|
|
|
+
|
|
|
+ return rc;
|
|
|
+}
|
|
|
+
|
|
|
+int rtas_suspend_last_cpu(struct rtas_suspend_me_data *data)
|
|
|
+{
|
|
|
+ atomic_inc(&data->working);
|
|
|
+ return __rtas_suspend_last_cpu(data, 0);
|
|
|
+}
|
|
|
+
|
|
|
+static int __rtas_suspend_cpu(struct rtas_suspend_me_data *data, int wake_when_done)
|
|
|
{
|
|
|
long rc = H_SUCCESS;
|
|
|
unsigned long msr_save;
|
|
|
- u16 slb_size = mmu_slb_size;
|
|
|
int cpu;
|
|
|
- struct rtas_suspend_me_data *data =
|
|
|
- (struct rtas_suspend_me_data *)info;
|
|
|
|
|
|
atomic_inc(&data->working);
|
|
|
|
|
@@ -729,7 +760,7 @@ static void rtas_percpu_suspend_me(void *info)
|
|
|
msr_save = mfmsr();
|
|
|
mtmsr(msr_save & ~(MSR_EE));
|
|
|
|
|
|
- while (rc == H_SUCCESS && !atomic_read(&data->done))
|
|
|
+ while (rc == H_SUCCESS && !atomic_read(&data->done) && !atomic_read(&data->error))
|
|
|
rc = plpar_hcall_norets(H_JOIN);
|
|
|
|
|
|
mtmsr(msr_save);
|
|
@@ -741,33 +772,37 @@ static void rtas_percpu_suspend_me(void *info)
|
|
|
/* All other cpus are in H_JOIN, this cpu does
|
|
|
* the suspend.
|
|
|
*/
|
|
|
- slb_set_size(SLB_MIN_SIZE);
|
|
|
- printk(KERN_DEBUG "calling ibm,suspend-me on cpu %i\n",
|
|
|
- smp_processor_id());
|
|
|
- data->error = rtas_call(data->token, 0, 1, NULL);
|
|
|
-
|
|
|
- if (data->error) {
|
|
|
- printk(KERN_DEBUG "ibm,suspend-me returned %d\n",
|
|
|
- data->error);
|
|
|
- slb_set_size(slb_size);
|
|
|
- }
|
|
|
+ return __rtas_suspend_last_cpu(data, wake_when_done);
|
|
|
} else {
|
|
|
printk(KERN_ERR "H_JOIN on cpu %i failed with rc = %ld\n",
|
|
|
smp_processor_id(), rc);
|
|
|
- data->error = rc;
|
|
|
+ atomic_set(&data->error, rc);
|
|
|
}
|
|
|
|
|
|
- atomic_set(&data->done, 1);
|
|
|
+ if (wake_when_done) {
|
|
|
+ atomic_set(&data->done, 1);
|
|
|
|
|
|
- /* This cpu did the suspend or got an error; in either case,
|
|
|
- * we need to prod all other other cpus out of join state.
|
|
|
- * Extra prods are harmless.
|
|
|
- */
|
|
|
- for_each_online_cpu(cpu)
|
|
|
- plpar_hcall_norets(H_PROD, get_hard_smp_processor_id(cpu));
|
|
|
+ /* This cpu did the suspend or got an error; in either case,
|
|
|
+ * we need to prod all other other cpus out of join state.
|
|
|
+ * Extra prods are harmless.
|
|
|
+ */
|
|
|
+ for_each_online_cpu(cpu)
|
|
|
+ plpar_hcall_norets(H_PROD, get_hard_smp_processor_id(cpu));
|
|
|
+ }
|
|
|
out:
|
|
|
if (atomic_dec_return(&data->working) == 0)
|
|
|
complete(data->complete);
|
|
|
+ return rc;
|
|
|
+}
|
|
|
+
|
|
|
+int rtas_suspend_cpu(struct rtas_suspend_me_data *data)
|
|
|
+{
|
|
|
+ return __rtas_suspend_cpu(data, 0);
|
|
|
+}
|
|
|
+
|
|
|
+static void rtas_percpu_suspend_me(void *info)
|
|
|
+{
|
|
|
+ __rtas_suspend_cpu((struct rtas_suspend_me_data *)info, 1);
|
|
|
}
|
|
|
|
|
|
static int rtas_ibm_suspend_me(struct rtas_args *args)
|
|
@@ -802,22 +837,22 @@ static int rtas_ibm_suspend_me(struct rtas_args *args)
|
|
|
|
|
|
atomic_set(&data.working, 0);
|
|
|
atomic_set(&data.done, 0);
|
|
|
+ atomic_set(&data.error, 0);
|
|
|
data.token = rtas_token("ibm,suspend-me");
|
|
|
- data.error = 0;
|
|
|
data.complete = &done;
|
|
|
|
|
|
/* Call function on all CPUs. One of us will make the
|
|
|
* rtas call
|
|
|
*/
|
|
|
if (on_each_cpu(rtas_percpu_suspend_me, &data, 0))
|
|
|
- data.error = -EINVAL;
|
|
|
+ atomic_set(&data.error, -EINVAL);
|
|
|
|
|
|
wait_for_completion(&done);
|
|
|
|
|
|
- if (data.error != 0)
|
|
|
+ if (atomic_read(&data.error) != 0)
|
|
|
printk(KERN_ERR "Error doing global join\n");
|
|
|
|
|
|
- return data.error;
|
|
|
+ return atomic_read(&data.error);
|
|
|
}
|
|
|
#else /* CONFIG_PPC_PSERIES */
|
|
|
static int rtas_ibm_suspend_me(struct rtas_args *args)
|