Embeddedxen中断申请

先看xen的中断申请

申请中断离不开request_irq和setup_irq

xen只申请了两个irq一个是timer的,另一个是console.
xen/arch/arm/mach-pxa/time.c

void __init xen_pxa_timer_init(void)
{
unsigned long clock_tick_rate;

OIER = 0;
OSSR = OSSR_M0 | OSSR_M1 | OSSR_M2 | OSSR_M3;

if (cpu_is_pxa25x())
clock_tick_rate = 3686400;
else if (machine_is_mainstone())
clock_tick_rate = 3249600;
else
clock_tick_rate = 3250000;

set_oscr2ns_scale(clock_tick_rate);

ckevt_pxa_osmr0.mult =
div_sc(clock_tick_rate, NSEC_PER_SEC, ckevt_pxa_osmr0.shift);

ckevt_pxa_osmr0.max_delta_ns =
clockevent_delta2ns(0x7fffffff, &ckevt_pxa_osmr0);
ckevt_pxa_osmr0.min_delta_ns =
clockevent_delta2ns(MIN_OSCR_DELTA * 2, &ckevt_pxa_osmr0) + 1;

cksrc_pxa_oscr0.mult =
clocksource_hz2mult(clock_tick_rate, cksrc_pxa_oscr0.shift);

setup_irq(IRQ_OST0, &pxa_ost0_irq);

/* timebase_freq = CLOCK_TICK_RATE: source clock rate in Hz */
//timebase_freq = 1000000LL;
timebase_freq = CLOCK_TICK_RATE;
system_timer_clocksource = &cksrc_pxa_oscr0;
system_timer_clockevent = &ckevt_pxa_osmr0;

    ckevt_pxa_osmr0.set_mode(CLOCK_EVT_MODE_PERIODIC, &ckevt_pxa_osmr0);

}

所安装的irqaction是pxa_ost0_irq,xen/arch/arm/mach-pxa/time.c

static struct irqaction pxa_ost0_irq = {
.name = "ost0",
.flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
.handler = pxa_ost0_interrupt,
.dev_id = &ckevt_pxa_osmr0,
};

xen/arch/arm/hypervisor/irq.c
int setup_irq(unsigned int irq, struct irqaction *new)
{
int rc = 0;
        int shared = 0;
        struct irqaction *old, **p;
        unsigned long flags;
        struct irqdesc *desc;

if(irq >= NR_IRQS) {
printk("BAD IRQ = %d\n", irq);
while(1);
}

        desc = get_irq_descriptor(irq);

        spin_lock_irqsave(&desc->lock, flags);

        p = &desc->action;

        if ((old = *p) != NULL) {
                /* Can't share interrupts unless both agree to */
                if (!(old->flags & new->flags & IRQF_SHARABLE) ||
   ((old->flags ^ new->flags) & IRQF_TRIGGER_MASK)) {
    printk("IRQ sharing failed.\n");

rc = -EBUSY;
goto out;
                }

                /* add new interrupt at end of irq queue */
                do {
                        p = &old->next;
                        old = *p;
                } while (old);
                shared = 1;
        }

        *p = new;

        if (!shared) {
if(new->flags & IRQF_TRIGGER_MASK) {
if(desc->chip->set_type) {
desc->chip->set_type(irq, new->flags & IRQF_TRIGGER_MASK);
} else {
printk("No set_irq_type function for irq %d\n", irq);
rc = -1;
goto out;
}
}

desc->status &= ~(IRQ_AUTO_DETECT | IRQ_WAITING | IRQ_IN_PROGRESS);

if(!(desc->status & IRQ_NO_AUTO_ENABLE)) {
desc->disable_depth = 0;
desc->status &= ~IRQ_DISABLED;
desc->chip->unmask(irq);
} else {
desc->disable_depth = 1;
}

        }

desc->flags &= ~IRQF_GUEST_BOUND;

out:
        spin_unlock_irqrestore(&desc->lock, flags);

        return rc;
}

setup_irq timer后,就开始setup_irq console了。
console的设置稍微复杂了一些:

在__start_xen中:xen/arch/arm/hypersivor/setup.c

pxaserial.baud = 115200;
pxaserial.data_bits = 8;
pxaserial.parity = 'n';
pxaserial.stop_bits = 1;
pxaserial.io_base = 1;
pxaserial.irq = IRQ_FFUART;

//pxaserial.io_base = 0x3f8;
pxaserial_init(0, &pxaserial);

pxaserial的结构体:xen/drivers/char/pxa.c
static struct pxaserial {
int baud, data_bits, parity, stop_bits, irq;

unsigned long io_base; /* I/O port or memory-mapped I/O address. */

char *remapped_io_base; /* Remapped virtual address of mmap I/O.  */

/* UART with IRQ line: interrupt-driven I/O. */

struct irqaction irqaction;

/* UART with no IRQ line: periodically-polled I/O. */

struct timer timer;

unsigned int timeout_ms;

} pxaserial_com[2] = { { 0 }, { 0 } };

pxaserial_init  xen/drivers/char/pxa.c
void __init pxaserial_init(int index, struct pxaserial_defaults *defaults)

{

struct pxaserial *uart = &pxaserial_com[index];

if ( (index < 0) || (index> 1) )

return;

if ( defaults != NULL )

{

uart->baud = defaults->baud;

uart->data_bits = defaults->data_bits;

uart->parity = parse_parity_char(defaults->parity);

uart->stop_bits = defaults->stop_bits;

uart->irq = defaults->irq;

uart->io_base = defaults->io_base;

}

pxaserial_parse_port_config(uart, index);

}

---
pxaserial_parse_port_config xen/drivers/char/pxa.c

static void __init pxaserial_parse_port_config(struct pxaserial *uart, int index)

{

int baud;

const char *conf = ((index == 0) ? opt_com1 : opt_com2);

/* (LSU) Defined in xen/lib/vsprintf.c */

extern unsigned long simple_strtoul(const char *cp, char **endp, unsigned int base);

/* No user-specified configuration? */

if ( (conf == NULL) || (*conf == '\0') )

{

/* Some platforms may automatically probe the UART configuartion. */

if ( uart->baud != 0 )

goto config_parsed;

return;

}

if ( strncmp(conf, "auto", 4) == 0 )

{

uart->baud = BAUD_AUTO;

conf += 4;

}

else if ( (baud = simple_strtoul(conf, (char **) &conf, 10)) != 0 )

uart->baud = baud;

if ( *conf != ',' )

goto config_parsed;

conf++;

uart->data_bits = simple_strtoul(conf, (char **) &conf, 10);

uart->parity = parse_parity_char(*conf);

conf++;

uart->stop_bits = simple_strtoul(conf,(char **)  &conf, 10);

if ( *conf == ',' )

{

conf++;

uart->io_base = simple_strtoul(conf, (char **) &conf, 0);

if ( *conf == ',' )

{

conf++;

uart->irq = simple_strtoul(conf, (char **) &conf, 10);

}

}

config_parsed:

/* Sanity checks. */

if ( (uart->baud != BAUD_AUTO) && ((uart->baud < 1200) || (uart->baud> 115200)) )

PARSE_ERR("Baud rate %d outside supported range.", uart->baud);

if ( (uart->data_bits < 5) || (uart->data_bits> 8) )

PARSE_ERR("%d data bits are unsupported.", uart->data_bits);

if ( (uart->stop_bits < 1) || (uart->stop_bits> 2) )

PARSE_ERR("%d stop bits are unsupported.", uart->stop_bits);

if ( uart->io_base == 0 )

PARSE_ERR("I/O base address must be specified.");

/* Register with generic serial driver. */

serial_register_uart(0, &pxaserial_driver, uart);

}

上面的这个pxaserial_parse_port_config函数有点长,不过,除了最后一句有用外,其他的都在扯淡。特别是中间那一大段完全没有被执行,所以不要有压力哈。

serial_register_uart xen/drivers/char/serial.c
void __init serial_register_uart(int idx, struct uart_driver *driver, void *uart)

{

    /* Store UART-specific info. */

    com[idx].driver = driver;

    com[idx].uart   = uart;

    /* Default is no transmit FIFO. */

    com[idx].tx_fifo_size = 1;

}

其实这个函数不再是重点了,重点的是其中的值:
我们看一下,com[0]的类型是:
struct serial_port {

    /* Uart-driver parameters. */

    struct uart_driver *driver;

    void               *uart;

    /* Number of characters the port can hold for transmit. */

    int                 tx_fifo_size;

    /* Transmit data buffer (interrupt-driven uart). */

    char               *txbuf;

    unsigned int        txbufp, txbufc;

    /* Force synchronous transmit. */

    int                 sync;

    /* Receiver callback functions (asynchronous receivers). */

    serial_rx_fn        rx_lorx_hi, rx;

    /* Receive data buffer (polling receivers). */

    char                rxbuf[SERIAL_RXBUFSZ];

    unsigned int        rxbufp, rxbufc;

    /* Serial I/O is concurrency-safe. */

    spinlock_t          rx_lock, tx_lock;

};

也就是说,设置了com[0]的两个成员变量,一个是driver,另一个是uart.我们在回头看一下pxaserial_parse_port_config(struct pxaserial *uart, int index),其实这个函数调用serial_register_uart(0, &pxaserial_driver, uart)的并不优雅,它将原本是index确定为了0.不过对于本项目显然是没有问题的。可以看看linux是怎样实现的。
在函数serial_register_uart里面讲3个成员变量定以后,就剩下其他的小东西了。这些东西紧接着就定义了,不要急。我们先来看一下struct uart_driver *driver=&pxaserial_driver.

改变定义在xen/drivers/char/pxa.c文件中
static struct uart_driver pxaserial_driver = {

.init_preirq = pxa_initpreirq,

.putc = pxaserial_putc,

.getc = pxaserial_getc,

.irq = pxaserial_irq,

.init_postirq = pxaserial_init_postirq,

.endboot = NULL,

.tx_empty = serial_pxa_tx_empty,

};

以上分别定义了struct pxaserial类型的pxaserial,定义了struct uart_driver类型的pxaserial_driver.然后将它们赋值给了struct serial_port类型的com[0].

我们回过头来看看,实际上,并没有定义struct pxaserial pxaserial的全部成员,比如irqaction,比如timer.也许是以后还会有。我们需要耐心。
好,这里已经定义好了serial_port的3个重要的成员变量了,革命尚未成功,同志仍需努力!

返回到__start_xen的主程序,接下来开始执行
serial_init_preirq(void);

void __init serial_init_preirq(void)

{

    int i;

    for ( i = 0; i < ARRAY_SIZE(com); i++ )

        if ( com[i].driver && com[i].driver->init_preirq )

            com[i].driver->init_preirq(&com[i]);

}

可以看到,实际上执行的是pxa_initpreirq(&com[0]),com[1]没有初始化,因此com[1]并没有被执行。

/* from startup...*/

/* (LSU) Applied awaited signature */

//static int pxa_initpreirq(struct serial_port *port) {

static void pxa_initpreirq(struct serial_port *port) {

struct uart_pxa_port *up = &serial_pxa_port;

unsigned long flags;

// int retval;

if (up->line == 3) /* HWUART */

up->mcr |= UART_MCR_AFE;

else

up->mcr = 0;

/*

* Clear the FIFO buffers and disable them.

* (they will be reenabled in set_termios())

*/

serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO);

serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR

| UART_FCR_CLEAR_XMIT);

serial_out(up, UART_FCR, 0);

/*

* Clear the interrupt registers.

*/

(void) serial_in(up, UART_LSR);

(void) serial_in(up, UART_RX);

(void) serial_in(up, UART_IIR);

(void) serial_in(up, UART_MSR);

/*

* Now, initialize the UART

*/

serial_out(up, UART_LCR, UART_LCR_WLEN8);

spin_lock_irqsave(&up->lock, flags);

up->mctrl |= TIOCM_OUT2;

serial_pxa_set_mctrl(port, up->mctrl);

spin_unlock_irqrestore(&up->lock, flags);

//return 0;

}

只里面又包含了已经被定义好的东东    xen/drivers/char/pxa.c
static struct uart_pxa_port serial_pxa_port = {

.name = "FFUART", 

.cken = CKEN6_FFUART,

.membase = (void *)&FFUART, 

.mapbase = __PREG(FFUART),

 .irq = IRQ_FFUART,

 .uartclk = 921600 * 16,

 .line = 0, 

};

struct uart_pxa_port {

spinlock_t lock;

unsigned char ier;

unsigned char lcr;

unsigned char mcr;

unsigned int mctrl;

unsigned int lsr_break_flag;

unsigned int cken;

int line;

char *name;

unsigned long iobase; /* io base address */

void __iomem *membase; /* ioremap cookie or NULL */

unsigned long mapbase; /* resource base */

unsigned int irq; /* interrupt number */

unsigned int uartclk; /* UART clock rate */

unsigned char regshift; /* register shift */

unsigned char iotype; /* UPIO_* */

unsigned char hub6;

upf_t flags; /* UPF_* flags */

};

从上面看来,应该是一些硬件参数信息了。不过和pxaserial那个结构体似乎有某种联系。

还是看那个函数,有用的只有这么一行:serial_pxa_set_mctrl(port, up->mctrl);
我们进去看看:
static void serial_pxa_set_mctrl(struct serial_port *port, unsigned int mctrl) {

struct uart_pxa_port *up = &serial_pxa_port;

unsigned char mcr = 0;

if (mctrl & TIOCM_RTS)

mcr |= UART_MCR_RTS;

if (mctrl & TIOCM_DTR)

mcr |= UART_MCR_DTR;

if (mctrl & TIOCM_OUT1)

mcr |= UART_MCR_OUT1;

if (mctrl & TIOCM_OUT2)

mcr |= UART_MCR_OUT2;

if (mctrl & TIOCM_LOOP)

mcr |= UART_MCR_LOOP;

mcr |= up->mcr;

serial_out(up, UART_MCR, mcr);

}

晕倒啊,将port传进来有意义么?根本就没有用到port,还是用的原来的serial_pxa_port
然后就是serial_out(up,UART_MCR,mcr).
static inline void serial_out(struct uart_pxa_port *up, int offset, int value)

{

offset <<= 2;

writel(value, up->port.membase + offset);

}

drivers/serial/pxa.c
struct uart_pxa_port {

struct uart_port        port;

unsigned char           ier;

unsigned char           lcr;

unsigned char           mcr;

unsigned int            lsr_break_flag;

unsigned int cken;

char *name;

};

这里的uart_port
struct uart_port {

spinlock_t lock; /* port lock */

unsigned int iobase; /* in/out[bwl] */

unsigned char __iomem *membase; /* read/write[bwl] */

unsigned int irq; /* irq number */

unsigned int uartclk; /* base uart clock */

unsigned int fifosize; /* tx fifo size */

unsigned char x_char; /* xon/xoff char */

unsigned char regshift; /* reg offset shift */

unsigned char iotype; /* io access style */

unsigned char unused1;

#define UPIO_PORT (0)

#define UPIO_HUB6 (1)

#define UPIO_MEM (2)

#define UPIO_MEM32 (3)

#define UPIO_AU (4) /* Au1x00 type IO */

#define UPIO_TSI (5) /* Tsi108/109 type IO */

unsigned int read_status_mask; /* driver specific */

unsigned int ignore_status_mask; /* driver specific */

struct uart_info *info; /* pointer to parent info */

struct uart_icount icount; /* statistics */

struct console *cons; /* struct console, if any */

#ifdef CONFIG_SERIAL_CORE_CONSOLE

unsigned long sysrq; /* sysrq timeout */

#endif

upf_t flags;

#define UPF_FOURPORT ((__force upf_t) (1 << 1))

#define UPF_SAK ((__force upf_t) (1 << 2))

#define UPF_SPD_MASK ((__force upf_t) (0x1030))

#define UPF_SPD_HI ((__force upf_t) (0x0010))

#define UPF_SPD_VHI ((__force upf_t) (0x0020))

#define UPF_SPD_CUST ((__force upf_t) (0x0030))

#define UPF_SPD_SHI ((__force upf_t) (0x1000))

#define UPF_SPD_WARP ((__force upf_t) (0x1010))

#define UPF_SKIP_TEST ((__force upf_t) (1 << 6))

#define UPF_AUTO_IRQ ((__force upf_t) (1 << 7))

#define UPF_HARDPPS_CD ((__force upf_t) (1 << 11))

#define UPF_LOW_LATENCY ((__force upf_t) (1 << 13))

#define UPF_BUGGY_UART ((__force upf_t) (1 << 14))

#define UPF_MAGIC_MULTIPLIER ((__force upf_t) (1 << 16))

#define UPF_CONS_FLOW ((__force upf_t) (1 << 23))

#define UPF_SHARE_IRQ ((__force upf_t) (1 << 24))

#define UPF_BOOT_AUTOCONF ((__force upf_t) (1 << 28))

#define UPF_DEAD ((__force upf_t) (1 << 30))

#define UPF_IOREMAP ((__force upf_t) (1 << 31))

#define UPF_CHANGE_MASK ((__force upf_t) (0x17fff))

#define UPF_USR_MASK ((__force upf_t) (UPF_SPD_MASK|UPF_LOW_LATENCY))

unsigned int mctrl; /* current modem ctrl settings */

unsigned int timeout; /* character-based timeout */

unsigned int type; /* port type */

const struct uart_ops *ops;

unsigned int custom_divisor;

unsigned int line; /* port index */

unsigned long mapbase; /* for ioremap */

struct device *dev; /* parent device */

unsigned char hub6; /* this should be in the 8250 driver */

unsigned char unused[3];

};

有这么长。。。
好了,搞这这么久,实际上根本与Port屁事不关。我擦~~~

回到__start_xen的主程序,继续我们未完成的千秋大业。
init_console();
void __init init_console(void)

{

extern void add_taint(unsigned flag);

    char *p;

    /* Where should console output go? */

    for ( p = opt_console; p != NULL; p = strchr(p, ',') )

    {

        if ( *p == ',' )

            p++;

        if ( strncmp(p, "com", 3) == 0 )

            sercon_handle = serial_parse_handle(p);

        else if ( strncmp(p, "vga", 3) == 0 )

            vga_init();

    }

    serial_set_rx_handler(sercon_handle, serial_rx);

    /* HELLO WORLD --- start-of-day banner text. */

    printk(xen_banner());

    printk(" http://www.cl.cam.ac.uk/netos/xen\n");

    printk(" University of Cambridge Computer Laboratory\n\n");

    printk(" Xen version %d.%d%s (%s@%s) (%s) %s\n",

           xen_major_version(), xen_minor_version(), xen_extra_version(),

           xen_compile_by(), xen_compile_domain(),

           xen_compiler(), xen_compile_date());

    printk(" EmbeddedXEN/PENAR Project - Reconfigurable Embedded Digital System (REDS) Institute from HEIG-VD/Switzerland\n");

    printk(" http://reds.heig-vd.ch\n\n");

    printk(" Latest ChangeSet: %s\n\n", xen_changeset());

    set_printk_prefix("(XEN) ");

    if ( opt_sync_console )

    {

        serial_start_sync(sercon_handle);

        add_taint(TAINT_SYNC_CONSOLE);

        printk("Console output is synchronous.\n");

    }

}

这里面有一句挺重要:
serial_set_rx_handler(sercon_handle, serial_rx);
void __init serial_set_rx_handler(int handle, serial_rx_fn fn)

{

    struct serial_port *port = &com[handle & SERHND_IDX];

    unsigned long flags;

    if ( handle == -1 )

        return;

    spin_lock_irqsave(&port->rx_lock, flags);

    if ( port->rx != NULL )

        goto fail;

    if ( handle & SERHND_LO )

    {

        if ( port->rx_lo != NULL )

            goto fail;

        port->rx_lo = fn;

    }

    else if ( handle & SERHND_HI )

    {

        if ( port->rx_hi != NULL )

            goto fail;

        port->rx_hi = fn;

    }

    else

    {

        if ( (port->rx_hi != NULL) || (port->rx_lo != NULL) )

            goto fail;

        port->rx = fn;

    }

    spin_unlock_irqrestore(&port->rx_lock, flags);

    return;

 fail:

    spin_unlock_irqrestore(&port->rx_lock, flags);

    printk("ERROR: Conflicting receive handlers for COM%d\n",

           handle & SERHND_IDX);

}

这里将port的成员变量rx_lo,rx_hi,rx_rx全部定位了fn,也就是serial_rx

然后在start_of_day()中,com实际上才开始注册了。在start_of_day中
serial_init_postirq();
void __init serial_init_postirq(void)

{

    int i;

    for ( i = 0; i < ARRAY_SIZE(com); i++ )

        if ( com[i].driver && com[i].driver->init_postirq )

            com[i].driver->init_postirq(&com[i]);

}

实际上是pxaserial_init_postirq.--这在前面赋值的时候,已经被赋值了。xen/drivers/char/pxa.c
static void __init pxaserial_init_postirq(struct serial_port *port)

{

struct pxaserial *uart = port->uart;

struct uart_pxa_port *up = &serial_pxa_port;

int rc;

//int bits;

if ( uart->irq < 0 )

return;

serial_async_transmit(port);

/* We do not consider polling mode (DRE) */

uart->irqaction.handler = pxaserial_interrupt;

uart->irqaction.name = "pxaserial";

uart->irqaction.dev_id = port;

if ( (rc = setup_irq(uart->irq, &uart->irqaction)) != 0 )

printk("ERROR: Failed to allocate ns16550 IRQ %d\n", uart->irq);

/*

* Finally, enable interrupts.  Note: Modem status interrupts

* are set via set_termios(), which will be occurring imminently

* anyway, so we don't enable them here.

*/

up->ier = UART_IER_RLSI | UART_IER_RDI | UART_IER_RTOIE | UART_IER_UUE;

serial_out(up, UART_IER, up->ier);

/*

* And clear the interrupt registers again for luck.

*/

(void) serial_in(up, UART_LSR);

(void) serial_in(up, UART_RX);

(void) serial_in(up, UART_IIR);

(void) serial_in(up, UART_MSR);

}

看到uart->irqaction了吧,被赋值了,呵呵,终于被赋值了,不过timer还没有被赋值,没关系,我们以后再看,至此,本文基本上也就完成一半了。



现在我们来看一下DOM方面的中断申请,不例外,肯定是用reqeust_irq和setup_irq.我们先来看一下request_irq:


 xen_guest/core/evtchn.c


int request_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char * devname, void *dev_id)
{

 struct irqaction * action;

 int retval;

 /*
  * Sanity-check: shared interrupts must pass in a real dev-ID,

  * otherwise we'll have trouble later trying to figure out

  * which interrupt is which (messes up the interrupt freeing

  * logic etc).

  */

 if ((irqflags & SA_SHIRQ) && !dev_id)

  return -EINVAL;

 if (irq >= NR_IRQS)

  return -EINVAL;

 if (!handler)

  return -EINVAL;

 action = (struct irqaction *)kmalloc(sizeof(struct irqaction), GFP_KERNEL);
 if (!action)

  return -ENOMEM;

 action->handler = handler;
 action->flags = irqflags;

 cpus_clear(action->mask);

 action->name = devname;

 action->next = NULL;

 action->dev_id = dev_id;

 retval = setup_irq(irq, action);

 if (retval)
  kfree(action);

 return retval;
}

setup_irq()
xen_guest/core/evtchn.c



int setup_irq(unsigned int irq, struct irqaction * new)

{

 struct evtchn_desc *desc = evtchn_desc + irq;

 struct irqaction *old, **p;

 unsigned long flags;

 int shared = 0;

 // the following should be commented: jyhwang 2007 July 15

 // printk("irq=%d, NR_IRQS=%d\n", irq, NR_IRQS);

 if (irq >= NR_IRQS)

  return -EINVAL;

 if (desc->handler == NULL)

  return -ENOSYS;

 /*

  * Some drivers like serial.c use request_irq() heavily,

  * so we have to be careful not to interfere with a

  * running system.

  */

 if (new->flags & SA_SAMPLE_RANDOM) {

  /*

   * This function might sleep, we want to call it first,

   * outside of the atomic block.

   * Yes, this might clear the entropy pool if the wrong

   * driver is attempted to be loaded, without actually

   * installing a new handler, but is this really a problem,

   * only the sysadmin is able to do this.

   */

  rand_initialize_irq(irq);

 }

 /*

  * The following block of code has to be executed atomically

  */

 spin_lock_irqsave(&desc->lock,flags);

 p = &desc->action;

 if ((old = *p) != NULL) {

  /* Can't share interrupts unless both agree to */

  if (!(old->flags & new->flags & SA_SHIRQ)) {

   spin_unlock_irqrestore(&desc->lock,flags);

   return -EBUSY;

  }

  /* add new interrupt at end of irq queue */

  do {

   p = &old->next;

   old = *p;

  } while (old);

  shared = 1;

 }

 *p = new;//action赋值给evtchn_desc

 if (!shared) {

  desc->depth = 0;

  desc->status &= ~(IRQ_DISABLED | IRQ_AUTODETECT |

      IRQ_WAITING | IRQ_INPROGRESS);

  if (desc->handler->startup)

   desc->handler->startup(irq);

  else

   desc->handler->enable(irq);

 }

 spin_unlock_irqrestore(&desc->lock,flags);

 new->irq = irq;

 new->dir = NULL;

 return 0;

}

看到setup_irq,似乎与Linux没有什么太大的不同,可是这也正是移植的妙处,总是要尽量的和原来的系统兼容,对吧。
我们来看看desc->handler->startup(irq);这句吧,这才是核心。我们知道在DOM进行初始化的时候,是怎么初始化呢?如果是pirq的话,分配的handler是pirq_type,这个handler的startup是被startup_pirq()实例化的。那么我们就来看看startup_pirq吧:

#define probing_irq(_irq) (evtchn_desc[(_irq)].action == NULL)

static unsigned int startup_pirq(unsigned int irq)
{

// evtchn_op_t op = { .cmd = EVTCHNOP_bind_pirq };

 evtchn_bind_pirq_t op;

 int evtchn = evtchn_from_irq(irq), ret = 0;

 if (VALID_EVTCHN(evtchn)) {
  goto out;

 }

 op.pirq  = irq;
 /* NB. We are happy to share unless we are probing. */

 op.flags = probing_irq(irq) ? 0 : BIND_PIRQ__WILL_SHARE;

 ret = HYPERVISOR_event_channel_op(EVTCHNOP_bind_pirq, &op);
 if (ret != 0) {

  if (!probing_irq(irq))

   printk(KERN_INFO "Failed to obtain physical IRQ %d\n", irq);

  return 0;

 }

 evtchn = op.port;

 pirq_query_unmask(irq_to_pirq(irq));

 bind_evtchn_to_cpu(evtchn, 0);
 evtchn_to_irq[evtchn] = irq;

 irq_info[irq] = mk_irq_info(IRQT_PIRQ, irq, evtchn);

 out:
 unmask_evtchn(evtchn);

 pirq_unmask_notify(irq_to_pirq(irq));

 return 0;
}


由于我们现在工作于DOMU的内核中,如果在x86架构中的话,是在ring1态,但这是在arm架构中,因此仍然是在ring3。不过,如果要申请进入ring0,也要进行HYPERVISOR_event_channel_op

 

xen/common/event_channel.c 

long do_event_channel_op(int cmd, XEN_GUEST_HANDLE(void) arg)

{

...

  case EVTCHNOP_bind_pirq: {
        struct evtchn_bind_pirq bind_pirq;

        if ( copy_from_guest(&bind_pirq, arg, 1) != 0 )

            return -EFAULT;

        rc = evtchn_bind_pirq(&bind_pirq);

        if ( (rc == 0) && (copy_to_guest(arg, &bind_pirq, 1) != 0) )

            rc = -EFAULT; /* Cleaning up here would be a mess! */

        break;

    }


...

}

 

evtchn_bind_pirq(&bind_pirq);

xen/common/event_channel.c 

static long evtchn_bind_pirq(evtchn_bind_pirq_t *bind)
{

    struct evtchn *chn;

    struct domain *d = current->domain;

    int            port, pirq = bind->pirq;

    long           rc;

    if ( (pirq < 0) || (pirq >= ARRAY_SIZE(d->pirq_to_evtchn)) )
        return -EINVAL;

    if ( !irq_access_permitted(d, pirq) )
        return -EPERM;

    spin_lock(&d->evtchn_lock);

    if ( d->pirq_to_evtchn[pirq] != 0 )
        ERROR_EXIT(-EEXIST);

    if ( (port = get_free_port(d)) < 0 )//得到evtchn号
        ERROR_EXIT(port);

    chn = evtchn_from_port(d, port);//从evtchn号得到chn数据结构

    d->pirq_to_evtchn[pirq] = port;
    rc = pirq_guest_bind(d->vcpu[0], pirq, !!(bind->flags & BIND_PIRQ__WILL_SHARE));

    if ( rc != 0 )

    {

        d->pirq_to_evtchn[pirq] = 0;

        goto out;

    }

    chn->state  = ECS_PIRQ;//充实这个数据结构
    chn->u.pirq = pirq;//充实

    bind->port = port;//充实bind,要带回去的

 out:
    spin_unlock(&d->evtchn_lock);

    return rc;
}


 

port与pirq

port属于DOM的,pirq属于系统的。

当irq来了以后,实际上就是pirq,找到port,然后找到chn,然后就设定vcpu响应的Port了.(这些在中断响应文章中都有叙述)

 

也就是说xen->dom的interface是通过port来进行的,而在申请的时候,是通过结构体evtchn_bind_pirq



void send_guest_pirq(struct domain *d, int pirq)

{

    int port = d->pirq_to_evtchn[pirq];

    struct evtchn *chn;

 

    ASSERT(port != 0);

 

    chn = evtchn_from_port(d, port);

 

    evtchn_set_pending(d->vcpu[chn->notify_vcpu_id], port);

}

  然后当DOM执行时,

evtchn_do_upcall

 
asmlinkage void evtchn_do_upcall(struct pt_regs *regs)

{

unsigned long  l1, l2;

unsigned int   l1i, l2i, port;

int            irq, cpu = smp_processor_id();

shared_info_t *s = HYPERVISOR_shared_info;

vcpu_info_t   *vcpu_info = &s->vcpu_info[cpu];

retry:

xchg(&vcpu_info->evtchn_upcall_pending,0);

l1 = xchg(&vcpu_info->evtchn_pending_sel, 0);

while (l1 != 0) {

l1i = __ffs(l1);

l1 &= ~(1UL << l1i);

while ((l2 = active_evtchns(cpu, s, l1i)) != 0) {

l2i = __ffs(l2);

port = (l1i * BITS_PER_LONG) + l2i;

if ((irq = evtchn_to_irq[port]) != -1) {  //evtchn号转化为irq号

irq_enter();

do_IRQ(irq, regs); //guest分发中断函数

irq_exit();

}  else {

evtchn_device_upcall(port);

}

}

}

if(vcpu_info->evtchn_upcall_pending) {//当有多个中断的时候

goto retry;

}

}

好了,这部分的回顾,就到此打住。



int pirq_guest_bind(struct vcpu *v, int irq, int will_share)
{

 struct irqdesc      *desc;

 irq_guest_action_t  *action;

 unsigned long       flags;

 int                 rc = 0;

 if ( (irq < 0) || (irq >= NR_IRQS) )
  return -EINVAL;

 desc = get_irq_descriptor(irq);

 spin_lock_irqsave(&desc->lock, flags);

 action = (irq_guest_action_t *)desc->action;//

 if (!(desc->flags & IRQF_GUEST_BOUND)) {
  if (desc->action != NULL ) {

   printk("Cannot bind IRQ %d to guest. In use by %s.\n",(int)irq, desc->action->name);

   rc = -EBUSY;

   goto out;

  }//:

  action = xmalloc(irq_guest_action_t);
  if ((desc->action = (struct irqaction *)action) == NULL ) {

   printk("Cannot bind IRQ %d to guest. Out of memory.\n", irq);

   rc = -ENOMEM;

   goto out;

  }

  action->shareable = 1;
  action->nr_guests = 0;

  action->in_flight = 0;

  action->ack_type  = pirq_ack_type(irq);

  desc->disable_depth = 0;
  desc->flags |= IRQF_GUEST_BOUND;

  if(will_share) {

   desc->flags |= IRQF_SHARABLE;

  }

  desc->chip->unmask(irq);

 } else if ( !will_share || !action->shareable ) {
  printk("Cannot bind IRQ %d to guest. Will not share with others.\n", irq);

  rc = -EBUSY;

  goto out;

 }

 if ( action->nr_guests == IRQ_MAX_GUESTS ) {
  printk("Cannot bind IRQ %d to guest. Already at max share.\n", irq);

  rc = -EBUSY;

  goto out;

 }

 action->guest[action->nr_guests++] = v->domain;

out:

 spin_unlock_irqrestore(&desc->lock, flags);

 return rc;
}

 在上面的这个pirq_guest_bind需要注意的是action的初始化。这里的action和domu的action是两个概念。不是同一回事,一个是irqdesc,一个是evtchn_desc_t。也就是说,一个是中断描述符,另一个是事件通道描述符。当中断由下往上传输的时候,先有irqdesc工作,然后,交给DOMU的时候,evtchn_desc_t开始工作。

我们看到,在request_irq中已经将action实例化了因此,在 pirq_guest_bind(struct vcpu *v, int irq, int will_share)中,直接更改action->guest[acton->nr_guests++]=v->domain,然后返回。

好了,接下来,我们会分析一个实例,来看一下,具体是怎样利用接口request_irq()申请中断.

评论

此博客中的热门博文

提交了30次才AC ---【附】POJ 2488解题报告

n个进程共享m个资源得死锁问题证明