Skip to content

Commit 8fe93f8

Browse files
bjking1ozbenh
authored andcommitted
powerpc/pseries: Migration code reorganization / hibernation prep
Partition hibernation will use some of the same code as is currently used for Live Partition Migration. This function further abstracts this code such that code outside of rtas.c can utilize it. It also changes the error field in the suspend me data structure to be an atomic type, since it is set and checked on different cpus without any barriers or locking. Signed-off-by: Brian King <brking@linux.vnet.ibm.com> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
1 parent c1aa687 commit 8fe93f8

File tree

3 files changed

+81
-35
lines changed

3 files changed

+81
-35
lines changed

arch/powerpc/include/asm/hvcall.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474
#define H_NOT_ENOUGH_RESOURCES -44
7575
#define H_R_STATE -45
7676
#define H_RESCINDEND -46
77+
#define H_MULTI_THREADS_ACTIVE -9005
7778

7879

7980
/* Long Busy is a condition that can be returned by the firmware

arch/powerpc/include/asm/rtas.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,14 @@ struct rtas_t {
6363
struct device_node *dev; /* virtual address pointer */
6464
};
6565

66+
struct rtas_suspend_me_data {
67+
atomic_t working; /* number of cpus accessing this struct */
68+
atomic_t done;
69+
int token; /* ibm,suspend-me */
70+
atomic_t error;
71+
struct completion *complete; /* wait on this until working == 0 */
72+
};
73+
6674
/* RTAS event classes */
6775
#define RTAS_INTERNAL_ERROR 0x80000000 /* set bit 0 */
6876
#define RTAS_EPOW_WARNING 0x40000000 /* set bit 1 */
@@ -174,6 +182,8 @@ extern int rtas_set_indicator(int indicator, int index, int new_value);
174182
extern int rtas_set_indicator_fast(int indicator, int index, int new_value);
175183
extern void rtas_progress(char *s, unsigned short hex);
176184
extern void rtas_initialize(void);
185+
extern int rtas_suspend_cpu(struct rtas_suspend_me_data *data);
186+
extern int rtas_suspend_last_cpu(struct rtas_suspend_me_data *data);
177187

178188
struct rtc_time;
179189
extern unsigned long rtas_get_boot_time(void);

arch/powerpc/kernel/rtas.c

Lines changed: 70 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -47,14 +47,6 @@ struct rtas_t rtas = {
4747
};
4848
EXPORT_SYMBOL(rtas);
4949

50-
struct rtas_suspend_me_data {
51-
atomic_t working; /* number of cpus accessing this struct */
52-
atomic_t done;
53-
int token; /* ibm,suspend-me */
54-
int error;
55-
struct completion *complete; /* wait on this until working == 0 */
56-
};
57-
5850
DEFINE_SPINLOCK(rtas_data_buf_lock);
5951
EXPORT_SYMBOL(rtas_data_buf_lock);
6052

@@ -714,22 +706,61 @@ void rtas_os_term(char *str)
714706

715707
static int ibm_suspend_me_token = RTAS_UNKNOWN_SERVICE;
716708
#ifdef CONFIG_PPC_PSERIES
717-
static void rtas_percpu_suspend_me(void *info)
709+
static int __rtas_suspend_last_cpu(struct rtas_suspend_me_data *data, int wake_when_done)
710+
{
711+
u16 slb_size = mmu_slb_size;
712+
int rc = H_MULTI_THREADS_ACTIVE;
713+
int cpu;
714+
715+
slb_set_size(SLB_MIN_SIZE);
716+
printk(KERN_DEBUG "calling ibm,suspend-me on cpu %i\n", smp_processor_id());
717+
718+
while (rc == H_MULTI_THREADS_ACTIVE && !atomic_read(&data->done) &&
719+
!atomic_read(&data->error))
720+
rc = rtas_call(data->token, 0, 1, NULL);
721+
722+
if (rc || atomic_read(&data->error)) {
723+
printk(KERN_DEBUG "ibm,suspend-me returned %d\n", rc);
724+
slb_set_size(slb_size);
725+
}
726+
727+
if (atomic_read(&data->error))
728+
rc = atomic_read(&data->error);
729+
730+
atomic_set(&data->error, rc);
731+
732+
if (wake_when_done) {
733+
atomic_set(&data->done, 1);
734+
735+
for_each_online_cpu(cpu)
736+
plpar_hcall_norets(H_PROD, get_hard_smp_processor_id(cpu));
737+
}
738+
739+
if (atomic_dec_return(&data->working) == 0)
740+
complete(data->complete);
741+
742+
return rc;
743+
}
744+
745+
int rtas_suspend_last_cpu(struct rtas_suspend_me_data *data)
746+
{
747+
atomic_inc(&data->working);
748+
return __rtas_suspend_last_cpu(data, 0);
749+
}
750+
751+
static int __rtas_suspend_cpu(struct rtas_suspend_me_data *data, int wake_when_done)
718752
{
719753
long rc = H_SUCCESS;
720754
unsigned long msr_save;
721-
u16 slb_size = mmu_slb_size;
722755
int cpu;
723-
struct rtas_suspend_me_data *data =
724-
(struct rtas_suspend_me_data *)info;
725756

726757
atomic_inc(&data->working);
727758

728759
/* really need to ensure MSR.EE is off for H_JOIN */
729760
msr_save = mfmsr();
730761
mtmsr(msr_save & ~(MSR_EE));
731762

732-
while (rc == H_SUCCESS && !atomic_read(&data->done))
763+
while (rc == H_SUCCESS && !atomic_read(&data->done) && !atomic_read(&data->error))
733764
rc = plpar_hcall_norets(H_JOIN);
734765

735766
mtmsr(msr_save);
@@ -741,33 +772,37 @@ static void rtas_percpu_suspend_me(void *info)
741772
/* All other cpus are in H_JOIN, this cpu does
742773
* the suspend.
743774
*/
744-
slb_set_size(SLB_MIN_SIZE);
745-
printk(KERN_DEBUG "calling ibm,suspend-me on cpu %i\n",
746-
smp_processor_id());
747-
data->error = rtas_call(data->token, 0, 1, NULL);
748-
749-
if (data->error) {
750-
printk(KERN_DEBUG "ibm,suspend-me returned %d\n",
751-
data->error);
752-
slb_set_size(slb_size);
753-
}
775+
return __rtas_suspend_last_cpu(data, wake_when_done);
754776
} else {
755777
printk(KERN_ERR "H_JOIN on cpu %i failed with rc = %ld\n",
756778
smp_processor_id(), rc);
757-
data->error = rc;
779+
atomic_set(&data->error, rc);
758780
}
759781

760-
atomic_set(&data->done, 1);
782+
if (wake_when_done) {
783+
atomic_set(&data->done, 1);
761784

762-
/* This cpu did the suspend or got an error; in either case,
763-
* we need to prod all other other cpus out of join state.
764-
* Extra prods are harmless.
765-
*/
766-
for_each_online_cpu(cpu)
767-
plpar_hcall_norets(H_PROD, get_hard_smp_processor_id(cpu));
785+
/* This cpu did the suspend or got an error; in either case,
786+
* we need to prod all other other cpus out of join state.
787+
* Extra prods are harmless.
788+
*/
789+
for_each_online_cpu(cpu)
790+
plpar_hcall_norets(H_PROD, get_hard_smp_processor_id(cpu));
791+
}
768792
out:
769793
if (atomic_dec_return(&data->working) == 0)
770794
complete(data->complete);
795+
return rc;
796+
}
797+
798+
int rtas_suspend_cpu(struct rtas_suspend_me_data *data)
799+
{
800+
return __rtas_suspend_cpu(data, 0);
801+
}
802+
803+
static void rtas_percpu_suspend_me(void *info)
804+
{
805+
__rtas_suspend_cpu((struct rtas_suspend_me_data *)info, 1);
771806
}
772807

773808
static int rtas_ibm_suspend_me(struct rtas_args *args)
@@ -802,22 +837,22 @@ static int rtas_ibm_suspend_me(struct rtas_args *args)
802837

803838
atomic_set(&data.working, 0);
804839
atomic_set(&data.done, 0);
840+
atomic_set(&data.error, 0);
805841
data.token = rtas_token("ibm,suspend-me");
806-
data.error = 0;
807842
data.complete = &done;
808843

809844
/* Call function on all CPUs. One of us will make the
810845
* rtas call
811846
*/
812847
if (on_each_cpu(rtas_percpu_suspend_me, &data, 0))
813-
data.error = -EINVAL;
848+
atomic_set(&data.error, -EINVAL);
814849

815850
wait_for_completion(&done);
816851

817-
if (data.error != 0)
852+
if (atomic_read(&data.error) != 0)
818853
printk(KERN_ERR "Error doing global join\n");
819854

820-
return data.error;
855+
return atomic_read(&data.error);
821856
}
822857
#else /* CONFIG_PPC_PSERIES */
823858
static int rtas_ibm_suspend_me(struct rtas_args *args)

0 commit comments

Comments
 (0)