Merge branch 'upstream' of git://ftp.linux-mips.org/pub/scm/upstream-linus

* 'upstream' of git://ftp.linux-mips.org/pub/scm/upstream-linus:
  [MIPS] Vr41xx: Fix after GENERIC_HARDIRQS_NO__DO_IRQ change
  [MIPS] SMTC: Instant IPI replay.
This commit is contained in:
Linus Torvalds 2007-01-23 11:19:32 -08:00
commit d227e87e6c
5 changed files with 131 additions and 87 deletions

View file

@ -1568,6 +1568,20 @@ config MIPS_MT_FPAFF
depends on MIPS_MT
default y
config MIPS_MT_SMTC_INSTANT_REPLAY
bool "Low-latency Dispatch of Deferred SMTC IPIs"
depends on MIPS_MT_SMTC
default y
help
SMTC pseudo-interrupts between TCs are deferred and queued
if the target TC is interrupt-inhibited (IXMT). In the first
SMTC prototypes, these queued IPIs were serviced on return
to user mode, or on entry into the kernel idle loop. The
INSTANT_REPLAY option dispatches them as part of local_irq_restore()
processing, which adds runtime overhead (hence the option to turn
it off), but ensures that IPIs are handled promptly even under
heavy I/O interrupt load.
config MIPS_VPE_LOADER_TOM
bool "Load VPE program into memory hidden from linux"
depends on MIPS_VPE_LOADER

View file

@ -1017,6 +1017,33 @@ void setup_cross_vpe_interrupts(void)
* SMTC-specific hacks invoked from elsewhere in the kernel.
*/
void smtc_ipi_replay(void)
{
/*
* To the extent that we've ever turned interrupts off,
* we may have accumulated deferred IPIs. This is subtle.
* If we use the smtc_ipi_qdepth() macro, we'll get an
* exact number - but we'll also disable interrupts
* and create a window of failure where a new IPI gets
* queued after we test the depth but before we re-enable
* interrupts. So long as IXMT never gets set, however,
* we should be OK: If we pick up something and dispatch
* it here, that's great. If we see nothing, but concurrent
* with this operation, another TC sends us an IPI, IXMT
* is clear, and we'll handle it as a real pseudo-interrupt
* and not a pseudo-pseudo interrupt.
*/
if (IPIQ[smp_processor_id()].depth > 0) {
struct smtc_ipi *pipi;
extern void self_ipi(struct smtc_ipi *);
while ((pipi = smtc_ipi_dq(&IPIQ[smp_processor_id()]))) {
self_ipi(pipi);
smtc_cpu_stats[smp_processor_id()].selfipis++;
}
}
}
void smtc_idle_loop_hook(void)
{
#ifdef SMTC_IDLE_HOOK_DEBUG
@ -1113,29 +1140,14 @@ void smtc_idle_loop_hook(void)
if (pdb_msg != &id_ho_db_msg[0])
printk("CPU%d: %s", smp_processor_id(), id_ho_db_msg);
#endif /* SMTC_IDLE_HOOK_DEBUG */
/*
* To the extent that we've ever turned interrupts off,
* we may have accumulated deferred IPIs. This is subtle.
* If we use the smtc_ipi_qdepth() macro, we'll get an
* exact number - but we'll also disable interrupts
* and create a window of failure where a new IPI gets
* queued after we test the depth but before we re-enable
* interrupts. So long as IXMT never gets set, however,
* we should be OK: If we pick up something and dispatch
* it here, that's great. If we see nothing, but concurrent
* with this operation, another TC sends us an IPI, IXMT
* is clear, and we'll handle it as a real pseudo-interrupt
* and not a pseudo-pseudo interrupt.
*/
if (IPIQ[smp_processor_id()].depth > 0) {
struct smtc_ipi *pipi;
extern void self_ipi(struct smtc_ipi *);
if ((pipi = smtc_ipi_dq(&IPIQ[smp_processor_id()])) != NULL) {
self_ipi(pipi);
smtc_cpu_stats[smp_processor_id()].selfipis++;
}
}
/*
* Replay any accumulated deferred IPIs. If "Instant Replay"
* is in use, there should never be any.
*/
#ifndef CONFIG_MIPS_MT_SMTC_INSTANT_REPLAY
smtc_ipi_replay();
#endif /* CONFIG_MIPS_MT_SMTC_INSTANT_REPLAY */
}
void smtc_soft_dump(void)

View file

@ -1,7 +1,7 @@
/*
* Interrupt handing routines for NEC VR4100 series.
*
* Copyright (C) 2005 Yoichi Yuasa <yoichi_yuasa@tripeaks.co.jp>
* Copyright (C) 2005-2007 Yoichi Yuasa <yoichi_yuasa@tripeaks.co.jp>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -73,13 +73,19 @@ static void irq_dispatch(unsigned int irq)
if (cascade->get_irq != NULL) {
unsigned int source_irq = irq;
desc = irq_desc + source_irq;
if (desc->chip->mask_ack)
desc->chip->mask_ack(source_irq);
else {
desc->chip->mask(source_irq);
desc->chip->ack(source_irq);
}
irq = cascade->get_irq(irq);
if (irq < 0)
atomic_inc(&irq_err_count);
else
irq_dispatch(irq);
desc->chip->end(source_irq);
if (!(desc->status & IRQ_DISABLED) && desc->chip->unmask)
desc->chip->unmask(source_irq);
} else
do_IRQ(irq);
}

View file

@ -3,7 +3,7 @@
*
* Copyright (C) 2002 MontaVista Software Inc.
* Author: Yoichi Yuasa <yyuasa@mvista.com or source@mvista.com>
* Copyright (C) 2003-2005 Yoichi Yuasa <yoichi_yuasa@tripeaks.co.jp>
* Copyright (C) 2003-2007 Yoichi Yuasa <yoichi_yuasa@tripeaks.co.jp>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -125,30 +125,17 @@ static inline uint16_t giu_clear(uint16_t offset, uint16_t clear)
return data;
}
static unsigned int startup_giuint_low_irq(unsigned int irq)
static void ack_giuint_low(unsigned int irq)
{
unsigned int pin;
pin = GPIO_PIN_OF_IRQ(irq);
giu_write(GIUINTSTATL, 1 << pin);
giu_set(GIUINTENL, 1 << pin);
return 0;
giu_write(GIUINTSTATL, 1 << GPIO_PIN_OF_IRQ(irq));
}
static void shutdown_giuint_low_irq(unsigned int irq)
static void mask_giuint_low(unsigned int irq)
{
giu_clear(GIUINTENL, 1 << GPIO_PIN_OF_IRQ(irq));
}
static void enable_giuint_low_irq(unsigned int irq)
{
giu_set(GIUINTENL, 1 << GPIO_PIN_OF_IRQ(irq));
}
#define disable_giuint_low_irq shutdown_giuint_low_irq
static void ack_giuint_low_irq(unsigned int irq)
static void mask_ack_giuint_low(unsigned int irq)
{
unsigned int pin;
@ -157,46 +144,30 @@ static void ack_giuint_low_irq(unsigned int irq)
giu_write(GIUINTSTATL, 1 << pin);
}
static void end_giuint_low_irq(unsigned int irq)
static void unmask_giuint_low(unsigned int irq)
{
if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS)))
giu_set(GIUINTENL, 1 << GPIO_PIN_OF_IRQ(irq));
}
static struct hw_interrupt_type giuint_low_irq_type = {
.typename = "GIUINTL",
.startup = startup_giuint_low_irq,
.shutdown = shutdown_giuint_low_irq,
.enable = enable_giuint_low_irq,
.disable = disable_giuint_low_irq,
.ack = ack_giuint_low_irq,
.end = end_giuint_low_irq,
static struct irq_chip giuint_low_irq_chip = {
.name = "GIUINTL",
.ack = ack_giuint_low,
.mask = mask_giuint_low,
.mask_ack = mask_ack_giuint_low,
.unmask = unmask_giuint_low,
};
static unsigned int startup_giuint_high_irq(unsigned int irq)
static void ack_giuint_high(unsigned int irq)
{
unsigned int pin;
pin = GPIO_PIN_OF_IRQ(irq) - GIUINT_HIGH_OFFSET;
giu_write(GIUINTSTATH, 1 << pin);
giu_set(GIUINTENH, 1 << pin);
return 0;
giu_write(GIUINTSTATH, 1 << (GPIO_PIN_OF_IRQ(irq) - GIUINT_HIGH_OFFSET));
}
static void shutdown_giuint_high_irq(unsigned int irq)
static void mask_giuint_high(unsigned int irq)
{
giu_clear(GIUINTENH, 1 << (GPIO_PIN_OF_IRQ(irq) - GIUINT_HIGH_OFFSET));
}
static void enable_giuint_high_irq(unsigned int irq)
{
giu_set(GIUINTENH, 1 << (GPIO_PIN_OF_IRQ(irq) - GIUINT_HIGH_OFFSET));
}
#define disable_giuint_high_irq shutdown_giuint_high_irq
static void ack_giuint_high_irq(unsigned int irq)
static void mask_ack_giuint_high(unsigned int irq)
{
unsigned int pin;
@ -205,20 +176,17 @@ static void ack_giuint_high_irq(unsigned int irq)
giu_write(GIUINTSTATH, 1 << pin);
}
static void end_giuint_high_irq(unsigned int irq)
static void unmask_giuint_high(unsigned int irq)
{
if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS)))
giu_set(GIUINTENH, 1 << (GPIO_PIN_OF_IRQ(irq) - GIUINT_HIGH_OFFSET));
}
static struct hw_interrupt_type giuint_high_irq_type = {
.typename = "GIUINTH",
.startup = startup_giuint_high_irq,
.shutdown = shutdown_giuint_high_irq,
.enable = enable_giuint_high_irq,
.disable = disable_giuint_high_irq,
.ack = ack_giuint_high_irq,
.end = end_giuint_high_irq,
static struct irq_chip giuint_high_irq_chip = {
.name = "GIUINTH",
.ack = ack_giuint_high,
.mask = mask_giuint_high,
.mask_ack = mask_ack_giuint_high,
.unmask = unmask_giuint_high,
};
static int giu_get_irq(unsigned int irq)
@ -282,9 +250,15 @@ void vr41xx_set_irq_trigger(unsigned int pin, irq_trigger_t trigger, irq_signal_
break;
}
}
set_irq_chip_and_handler(GIU_IRQ(pin),
&giuint_low_irq_chip,
handle_edge_irq);
} else {
giu_clear(GIUINTTYPL, mask);
giu_clear(GIUINTHTSELL, mask);
set_irq_chip_and_handler(GIU_IRQ(pin),
&giuint_low_irq_chip,
handle_level_irq);
}
giu_write(GIUINTSTATL, mask);
} else if (pin < GIUINT_HIGH_MAX) {
@ -311,9 +285,15 @@ void vr41xx_set_irq_trigger(unsigned int pin, irq_trigger_t trigger, irq_signal_
break;
}
}
set_irq_chip_and_handler(GIU_IRQ(pin),
&giuint_high_irq_chip,
handle_edge_irq);
} else {
giu_clear(GIUINTTYPH, mask);
giu_clear(GIUINTHTSELH, mask);
set_irq_chip_and_handler(GIU_IRQ(pin),
&giuint_high_irq_chip,
handle_level_irq);
}
giu_write(GIUINTSTATH, mask);
}
@ -617,10 +597,11 @@ static const struct file_operations gpio_fops = {
static int __devinit giu_probe(struct platform_device *dev)
{
unsigned long start, size, flags = 0;
unsigned int nr_pins = 0;
unsigned int nr_pins = 0, trigger, i, pin;
struct resource *res1, *res2 = NULL;
void *base;
int retval, i;
struct irq_chip *chip;
int retval;
switch (current_cpu_data.cputype) {
case CPU_VR4111:
@ -688,11 +669,20 @@ static int __devinit giu_probe(struct platform_device *dev)
giu_write(GIUINTENL, 0);
giu_write(GIUINTENH, 0);
trigger = giu_read(GIUINTTYPH) << 16;
trigger |= giu_read(GIUINTTYPL);
for (i = GIU_IRQ_BASE; i <= GIU_IRQ_LAST; i++) {
if (i < GIU_IRQ(GIUINT_HIGH_OFFSET))
irq_desc[i].chip = &giuint_low_irq_type;
pin = GPIO_PIN_OF_IRQ(i);
if (pin < GIUINT_HIGH_OFFSET)
chip = &giuint_low_irq_chip;
else
irq_desc[i].chip = &giuint_high_irq_type;
chip = &giuint_high_irq_chip;
if (trigger & (1 << pin))
set_irq_chip_and_handler(i, chip, handle_edge_irq);
else
set_irq_chip_and_handler(i, chip, handle_level_irq);
}
return cascade_irq(GIUINT_IRQ, giu_get_irq);

View file

@ -15,6 +15,27 @@
#include <asm/hazards.h>
/*
* CONFIG_MIPS_MT_SMTC_INSTANT_REPLAY does prompt replay of deferred IPIs,
* at the cost of branch and call overhead on each local_irq_restore()
*/
#ifdef CONFIG_MIPS_MT_SMTC_INSTANT_REPLAY
extern void smtc_ipi_replay(void);
#define irq_restore_epilog(flags) \
do { \
if (!(flags & 0x0400)) \
smtc_ipi_replay(); \
} while (0)
#else
#define irq_restore_epilog(ignore) do { } while (0)
#endif /* CONFIG_MIPS_MT_SMTC_INSTANT_REPLAY */
__asm__ (
" .macro raw_local_irq_enable \n"
" .set push \n"
@ -193,6 +214,7 @@ do { \
: "=r" (__tmp1) \
: "0" (flags) \
: "memory"); \
irq_restore_epilog(flags); \
} while(0)
static inline int raw_irqs_disabled_flags(unsigned long flags)