c7111c1318
The problem we can't take advantage of lowest priority delivery mode if the vectors are allocated for only one cpu at a time. Nor can we work around hardware that assumes lowest priority delivery mode is always used with several cpus. So this patch introduces the concept of a vector_allocation_domain. A set of cpus that will receive an irq on the same vector. Currently the code for implementing this is placed in the genapic structure so we can vary this depending on how we are using the io_apics. This allows us to restore the previous behaviour of genapic_flat without removing the benefits of having separate vector allocation for large machines. This should also fix the problem report where a hyperthreaded cpu was receving the irq on the wrong hyperthread when in logical delivery mode because the previous behaviour is restored. This patch properly records our allocation of the first 16 irqs to the first 16 available vectors on all cpus. This should be fine but it may run into problems with multiple interrupts at the same interrupt level. Except for some badly maintained comments in the code and the behaviour of the interrupt allocator I have no real understanding of that problem. Signed-off-by: Eric W. Biederman <ebiederm@xmission.com> Acked-by: Muli Ben-Yehuda <muli@il.ibm.com> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
214 lines
5.1 KiB
C
214 lines
5.1 KiB
C
/*
|
|
* Copyright 2004 James Cleverdon, IBM.
|
|
* Subject to the GNU Public License, v.2
|
|
*
|
|
* Flat APIC subarch code.
|
|
*
|
|
* Hacked for x86-64 by James Cleverdon from i386 architecture code by
|
|
* Martin Bligh, Andi Kleen, James Bottomley, John Stultz, and
|
|
* James Cleverdon.
|
|
*/
|
|
#include <linux/threads.h>
|
|
#include <linux/cpumask.h>
|
|
#include <linux/string.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/ctype.h>
|
|
#include <linux/init.h>
|
|
#include <asm/smp.h>
|
|
#include <asm/ipi.h>
|
|
|
|
static cpumask_t flat_target_cpus(void)
|
|
{
|
|
return cpu_online_map;
|
|
}
|
|
|
|
static cpumask_t flat_vector_allocation_domain(int cpu)
|
|
{
|
|
/* Careful. Some cpus do not strictly honor the set of cpus
|
|
* specified in the interrupt destination when using lowest
|
|
* priority interrupt delivery mode.
|
|
*
|
|
* In particular there was a hyperthreading cpu observed to
|
|
* deliver interrupts to the wrong hyperthread when only one
|
|
* hyperthread was specified in the interrupt desitination.
|
|
*/
|
|
cpumask_t domain = { { [0] = APIC_ALL_CPUS, } };
|
|
return domain;
|
|
}
|
|
|
|
/*
|
|
* Set up the logical destination ID.
|
|
*
|
|
* Intel recommends to set DFR, LDR and TPR before enabling
|
|
* an APIC. See e.g. "AP-388 82489DX User's Manual" (Intel
|
|
* document number 292116). So here it goes...
|
|
*/
|
|
static void flat_init_apic_ldr(void)
|
|
{
|
|
unsigned long val;
|
|
unsigned long num, id;
|
|
|
|
num = smp_processor_id();
|
|
id = 1UL << num;
|
|
x86_cpu_to_log_apicid[num] = id;
|
|
apic_write(APIC_DFR, APIC_DFR_FLAT);
|
|
val = apic_read(APIC_LDR) & ~APIC_LDR_MASK;
|
|
val |= SET_APIC_LOGICAL_ID(id);
|
|
apic_write(APIC_LDR, val);
|
|
}
|
|
|
|
static void flat_send_IPI_mask(cpumask_t cpumask, int vector)
|
|
{
|
|
unsigned long mask = cpus_addr(cpumask)[0];
|
|
unsigned long cfg;
|
|
unsigned long flags;
|
|
|
|
local_irq_save(flags);
|
|
|
|
/*
|
|
* Wait for idle.
|
|
*/
|
|
apic_wait_icr_idle();
|
|
|
|
/*
|
|
* prepare target chip field
|
|
*/
|
|
cfg = __prepare_ICR2(mask);
|
|
apic_write(APIC_ICR2, cfg);
|
|
|
|
/*
|
|
* program the ICR
|
|
*/
|
|
cfg = __prepare_ICR(0, vector, APIC_DEST_LOGICAL);
|
|
|
|
/*
|
|
* Send the IPI. The write to APIC_ICR fires this off.
|
|
*/
|
|
apic_write(APIC_ICR, cfg);
|
|
local_irq_restore(flags);
|
|
}
|
|
|
|
static void flat_send_IPI_allbutself(int vector)
|
|
{
|
|
#ifdef CONFIG_HOTPLUG_CPU
|
|
int hotplug = 1;
|
|
#else
|
|
int hotplug = 0;
|
|
#endif
|
|
if (hotplug || vector == NMI_VECTOR) {
|
|
cpumask_t allbutme = cpu_online_map;
|
|
|
|
cpu_clear(smp_processor_id(), allbutme);
|
|
|
|
if (!cpus_empty(allbutme))
|
|
flat_send_IPI_mask(allbutme, vector);
|
|
} else if (num_online_cpus() > 1) {
|
|
__send_IPI_shortcut(APIC_DEST_ALLBUT, vector,APIC_DEST_LOGICAL);
|
|
}
|
|
}
|
|
|
|
static void flat_send_IPI_all(int vector)
|
|
{
|
|
if (vector == NMI_VECTOR)
|
|
flat_send_IPI_mask(cpu_online_map, vector);
|
|
else
|
|
__send_IPI_shortcut(APIC_DEST_ALLINC, vector, APIC_DEST_LOGICAL);
|
|
}
|
|
|
|
static int flat_apic_id_registered(void)
|
|
{
|
|
return physid_isset(GET_APIC_ID(apic_read(APIC_ID)), phys_cpu_present_map);
|
|
}
|
|
|
|
static unsigned int flat_cpu_mask_to_apicid(cpumask_t cpumask)
|
|
{
|
|
return cpus_addr(cpumask)[0] & APIC_ALL_CPUS;
|
|
}
|
|
|
|
static unsigned int phys_pkg_id(int index_msb)
|
|
{
|
|
return hard_smp_processor_id() >> index_msb;
|
|
}
|
|
|
|
struct genapic apic_flat = {
|
|
.name = "flat",
|
|
.int_delivery_mode = dest_LowestPrio,
|
|
.int_dest_mode = (APIC_DEST_LOGICAL != 0),
|
|
.target_cpus = flat_target_cpus,
|
|
.vector_allocation_domain = flat_vector_allocation_domain,
|
|
.apic_id_registered = flat_apic_id_registered,
|
|
.init_apic_ldr = flat_init_apic_ldr,
|
|
.send_IPI_all = flat_send_IPI_all,
|
|
.send_IPI_allbutself = flat_send_IPI_allbutself,
|
|
.send_IPI_mask = flat_send_IPI_mask,
|
|
.cpu_mask_to_apicid = flat_cpu_mask_to_apicid,
|
|
.phys_pkg_id = phys_pkg_id,
|
|
};
|
|
|
|
/*
|
|
* Physflat mode is used when there are more than 8 CPUs on a AMD system.
|
|
* We cannot use logical delivery in this case because the mask
|
|
* overflows, so use physical mode.
|
|
*/
|
|
|
|
static cpumask_t physflat_target_cpus(void)
|
|
{
|
|
return cpumask_of_cpu(0);
|
|
}
|
|
|
|
static cpumask_t physflat_vector_allocation_domain(int cpu)
|
|
{
|
|
cpumask_t domain = CPU_MASK_NONE;
|
|
cpu_set(cpu, domain);
|
|
return domain;
|
|
}
|
|
|
|
|
|
static void physflat_send_IPI_mask(cpumask_t cpumask, int vector)
|
|
{
|
|
send_IPI_mask_sequence(cpumask, vector);
|
|
}
|
|
|
|
static void physflat_send_IPI_allbutself(int vector)
|
|
{
|
|
cpumask_t allbutme = cpu_online_map;
|
|
|
|
cpu_clear(smp_processor_id(), allbutme);
|
|
physflat_send_IPI_mask(allbutme, vector);
|
|
}
|
|
|
|
static void physflat_send_IPI_all(int vector)
|
|
{
|
|
physflat_send_IPI_mask(cpu_online_map, vector);
|
|
}
|
|
|
|
static unsigned int physflat_cpu_mask_to_apicid(cpumask_t cpumask)
|
|
{
|
|
int cpu;
|
|
|
|
/*
|
|
* We're using fixed IRQ delivery, can only return one phys APIC ID.
|
|
* May as well be the first.
|
|
*/
|
|
cpu = first_cpu(cpumask);
|
|
if ((unsigned)cpu < NR_CPUS)
|
|
return x86_cpu_to_apicid[cpu];
|
|
else
|
|
return BAD_APICID;
|
|
}
|
|
|
|
struct genapic apic_physflat = {
|
|
.name = "physical flat",
|
|
.int_delivery_mode = dest_Fixed,
|
|
.int_dest_mode = (APIC_DEST_PHYSICAL != 0),
|
|
.target_cpus = physflat_target_cpus,
|
|
.vector_allocation_domain = physflat_vector_allocation_domain,
|
|
.apic_id_registered = flat_apic_id_registered,
|
|
.init_apic_ldr = flat_init_apic_ldr,/*not needed, but shouldn't hurt*/
|
|
.send_IPI_all = physflat_send_IPI_all,
|
|
.send_IPI_allbutself = physflat_send_IPI_allbutself,
|
|
.send_IPI_mask = physflat_send_IPI_mask,
|
|
.cpu_mask_to_apicid = physflat_cpu_mask_to_apicid,
|
|
.phys_pkg_id = phys_pkg_id,
|
|
};
|