• Balbir Singh's avatar
    powerpc/xics: Work around limitations of OPAL XICS priority handling · a69e2fb7
    Balbir Singh authored
    The CPPR (Current Processor Priority Register) of a XICS interrupt
    presentation controller contains a value N, such that only interrupts
    with a priority "more favoured" than N will be received by the CPU,
    where "more favoured" means "less than". So if the CPPR has the value 5
    then only interrupts with a priority of 0-4 inclusive will be received.
    
    In theory the CPPR can support a value of 0 to 255 inclusive.
    In practice Linux only uses values of 0, 4, 5 and 0xff. Setting the CPPR
    to 0 rejects all interrupts, setting it to 0xff allows all interrupts.
    The values 4 and 5 are used to differentiate IPIs from external
    interrupts. Setting the CPPR to 5 allows IPIs to be received but not
    external interrupts.
    
    The CPPR emulation in the OPAL XICS implementation only directly
    supports priorities 0 and 0xff. All other priorities are considered
    equivalent, and mapped to a single priority value internally. This means
    when using icp-opal we can not allow IPIs but not externals.
    
    This breaks Linux's use of priority values when a CPU is hot unplugged.
    After migrating IRQs away from the CPU that is being offlined, we set
    the priority to 5, meaning we still want the offline CPU to receive
    IPIs. But the effect of the OPAL XICS emulation's use of a single
    priority value is that all interrupts are rejected by the CPU. With the
    CPU offline, and not receiving IPIs, we may not be able to wake it up to
    bring it back online.
    
    The first part of the fix is in icp_opal_set_cpu_priority(). CPPR values
    of 0 to 4 inclusive will correctly cause all interrupts to be rejected,
    so we pass those CPPR values through to OPAL. However if we are called
    with a CPPR of 5 or greater, the caller is expecting to be able to allow
    IPIs but not external interrupts. We know this doesn't work, so instead
    of rejecting all interrupts we choose the opposite which is to allow all
    interrupts. This is still not correct behaviour, but we know for the
    only existing caller (xics_migrate_irqs_away()), that it is the better
    option.
    
    The other part of the fix is in xics_migrate_irqs_away(). Instead of
    setting priority (CPPR) to 0, and then back to 5 before migrating IRQs,
    we migrate the IRQs before setting the priority back to 5. This should
    have no effect on an ICP backend with a working set_priority(), and on
    icp-opal it means we will keep all interrupts blocked until after we've
    finished doing the IRQ migration. Additionally we wait for 5ms after
    doing the migration to make sure there are no IRQs in flight.
    
    Fixes: d7436188 ("powerpc/xics: Add ICP OPAL backend")
    Cc: stable@vger.kernel.org # v4.8+
    Suggested-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
    Reported-by: default avatarVaidyanathan Srinivasan <svaidy@linux.vnet.ibm.com>
    Tested-by: default avatarVaidyanathan Srinivasan <svaidy@linux.vnet.ibm.com>
    Signed-off-by: default avatarBalbir Singh <bsingharora@gmail.com>
    [mpe: Rewrote comments and change log, change delay to 5ms]
    Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
    a69e2fb7
icp-opal.c 4.51 KB