Skip to content

Commit 9c2ef23

Browse files
committed
Merge tag 'powerpc-5.12-5' of git://git.kernel.org/pub/scm/linux/kernel/git/powerpc/linux
Pull powerpc fixes from Michael Ellerman: "Fix a bug on pseries where spurious wakeups from H_PROD would prevent partition migration from succeeding. Fix oopses seen in pcpu_alloc(), caused by parallel faults of the percpu mapping causing us to corrupt the protection key used for the mapping, and cause a fatal key fault. Thanks to Aneesh Kumar K.V, Murilo Opsfelder Araujo, and Nathan Lynch" * tag 'powerpc-5.12-5' of git://git.kernel.org/pub/scm/linux/kernel/git/powerpc/linux: powerpc/mm/book3s64: Use the correct storage key value when calling H_PROTECT powerpc/pseries/mobility: handle premature return from H_JOIN powerpc/pseries/mobility: use struct for shared state
2 parents fa16199 + 53f1d31 commit 9c2ef23

File tree

2 files changed

+46
-5
lines changed

2 files changed

+46
-5
lines changed

arch/powerpc/platforms/pseries/lpar.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -887,7 +887,8 @@ static long pSeries_lpar_hpte_updatepp(unsigned long slot,
887887

888888
want_v = hpte_encode_avpn(vpn, psize, ssize);
889889

890-
flags = (newpp & 7) | H_AVPN;
890+
flags = (newpp & (HPTE_R_PP | HPTE_R_N | HPTE_R_KEY_LO)) | H_AVPN;
891+
flags |= (newpp & HPTE_R_KEY_HI) >> 48;
891892
if (mmu_has_feature(MMU_FTR_KERNEL_RO))
892893
/* Move pp0 into bit 8 (IBM 55) */
893894
flags |= (newpp & HPTE_R_PP0) >> 55;

arch/powerpc/platforms/pseries/mobility.c

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -452,12 +452,28 @@ static int do_suspend(void)
452452
return ret;
453453
}
454454

455+
/**
456+
* struct pseries_suspend_info - State shared between CPUs for join/suspend.
457+
* @counter: Threads are to increment this upon resuming from suspend
458+
* or if an error is received from H_JOIN. The thread which performs
459+
* the first increment (i.e. sets it to 1) is responsible for
460+
* waking the other threads.
461+
* @done: False if join/suspend is in progress. True if the operation is
462+
* complete (successful or not).
463+
*/
464+
struct pseries_suspend_info {
465+
atomic_t counter;
466+
bool done;
467+
};
468+
455469
static int do_join(void *arg)
456470
{
457-
atomic_t *counter = arg;
471+
struct pseries_suspend_info *info = arg;
472+
atomic_t *counter = &info->counter;
458473
long hvrc;
459474
int ret;
460475

476+
retry:
461477
/* Must ensure MSR.EE off for H_JOIN. */
462478
hard_irq_disable();
463479
hvrc = plpar_hcall_norets(H_JOIN);
@@ -473,8 +489,20 @@ static int do_join(void *arg)
473489
case H_SUCCESS:
474490
/*
475491
* The suspend is complete and this cpu has received a
476-
* prod.
492+
* prod, or we've received a stray prod from unrelated
493+
* code (e.g. paravirt spinlocks) and we need to join
494+
* again.
495+
*
496+
* This barrier orders the return from H_JOIN above vs
497+
* the load of info->done. It pairs with the barrier
498+
* in the wakeup/prod path below.
477499
*/
500+
smp_mb();
501+
if (READ_ONCE(info->done) == false) {
502+
pr_info_ratelimited("premature return from H_JOIN on CPU %i, retrying",
503+
smp_processor_id());
504+
goto retry;
505+
}
478506
ret = 0;
479507
break;
480508
case H_BAD_MODE:
@@ -488,6 +516,13 @@ static int do_join(void *arg)
488516

489517
if (atomic_inc_return(counter) == 1) {
490518
pr_info("CPU %u waking all threads\n", smp_processor_id());
519+
WRITE_ONCE(info->done, true);
520+
/*
521+
* This barrier orders the store to info->done vs subsequent
522+
* H_PRODs to wake the other CPUs. It pairs with the barrier
523+
* in the H_SUCCESS case above.
524+
*/
525+
smp_mb();
491526
prod_others();
492527
}
493528
/*
@@ -535,11 +570,16 @@ static int pseries_suspend(u64 handle)
535570
int ret;
536571

537572
while (true) {
538-
atomic_t counter = ATOMIC_INIT(0);
573+
struct pseries_suspend_info info;
539574
unsigned long vasi_state;
540575
int vasi_err;
541576

542-
ret = stop_machine(do_join, &counter, cpu_online_mask);
577+
info = (struct pseries_suspend_info) {
578+
.counter = ATOMIC_INIT(0),
579+
.done = false,
580+
};
581+
582+
ret = stop_machine(do_join, &info, cpu_online_mask);
543583
if (ret == 0)
544584
break;
545585
/*

0 commit comments

Comments
 (0)