hvc_console: rework setup to replace irq functions with callbacks
This patch tries to change hvc_console to not use request_irq/free_irq if the backend does not use irqs. This allows virtio_console to use hvc_console without having a linker reference to request_irq/free_irq. In addition, together with patch 2/3 it improves the performance for virtio console input. (an earlier version of this patch was tested by Yajin on lguest) The irq specific code is moved to hvc_irq.c and selected by the drivers that use irqs (System p, System i, XEN). I replaced "int irq" with the opaque "int data". The request_irq and free_irq calls are replaced with notifier_add and notifier_del. I have also changed the code a bit to call the notifier_add and notifier_del inside the spinlock area as the callbacks are found via hp->ops. Changes since last version: o remove ifdef o reintroduce "irq_requested" as "notified" o cleanups, sparse.. I did not move the timer based polling into a separate polling scheme. I played with several variants, but it seems we need to sleep/schedule in a thread even for irq based consoles, as there are throttleing and buffer size constraints. I also kept hvc_struct defined in hvc_console.h so that hvc_irq.c can access the irq_requested element. Feedback is appreciated. virtio_console is currently the only available console for kvm on s390. I plan to push this change as soon as all affected parties agree on it. I would love to get test results from System p, Xen etc. Signed-off-by: Christian Borntraeger <borntraeger@de.ibm.com> Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
parent
066f4d82a6
commit
611e097d77
|
@ -578,11 +578,14 @@ config HVC_DRIVER
|
||||||
It will automatically be selected if one of the back-end console drivers
|
It will automatically be selected if one of the back-end console drivers
|
||||||
is selected.
|
is selected.
|
||||||
|
|
||||||
|
config HVC_IRQ
|
||||||
|
bool
|
||||||
|
|
||||||
config HVC_CONSOLE
|
config HVC_CONSOLE
|
||||||
bool "pSeries Hypervisor Virtual Console support"
|
bool "pSeries Hypervisor Virtual Console support"
|
||||||
depends on PPC_PSERIES
|
depends on PPC_PSERIES
|
||||||
select HVC_DRIVER
|
select HVC_DRIVER
|
||||||
|
select HVC_IRQ
|
||||||
help
|
help
|
||||||
pSeries machines when partitioned support a hypervisor virtual
|
pSeries machines when partitioned support a hypervisor virtual
|
||||||
console. This driver allows each pSeries partition to have a console
|
console. This driver allows each pSeries partition to have a console
|
||||||
|
@ -593,6 +596,7 @@ config HVC_ISERIES
|
||||||
depends on PPC_ISERIES
|
depends on PPC_ISERIES
|
||||||
default y
|
default y
|
||||||
select HVC_DRIVER
|
select HVC_DRIVER
|
||||||
|
select HVC_IRQ
|
||||||
help
|
help
|
||||||
iSeries machines support a hypervisor virtual console.
|
iSeries machines support a hypervisor virtual console.
|
||||||
|
|
||||||
|
@ -614,6 +618,7 @@ config HVC_XEN
|
||||||
bool "Xen Hypervisor Console support"
|
bool "Xen Hypervisor Console support"
|
||||||
depends on XEN
|
depends on XEN
|
||||||
select HVC_DRIVER
|
select HVC_DRIVER
|
||||||
|
select HVC_IRQ
|
||||||
default y
|
default y
|
||||||
help
|
help
|
||||||
Xen virtual console device driver
|
Xen virtual console device driver
|
||||||
|
|
|
@ -48,6 +48,7 @@ obj-$(CONFIG_HVC_ISERIES) += hvc_iseries.o
|
||||||
obj-$(CONFIG_HVC_RTAS) += hvc_rtas.o
|
obj-$(CONFIG_HVC_RTAS) += hvc_rtas.o
|
||||||
obj-$(CONFIG_HVC_BEAT) += hvc_beat.o
|
obj-$(CONFIG_HVC_BEAT) += hvc_beat.o
|
||||||
obj-$(CONFIG_HVC_DRIVER) += hvc_console.o
|
obj-$(CONFIG_HVC_DRIVER) += hvc_console.o
|
||||||
|
obj-$(CONFIG_HVC_IRQ) += hvc_irq.o
|
||||||
obj-$(CONFIG_HVC_XEN) += hvc_xen.o
|
obj-$(CONFIG_HVC_XEN) += hvc_xen.o
|
||||||
obj-$(CONFIG_VIRTIO_CONSOLE) += virtio_console.o
|
obj-$(CONFIG_VIRTIO_CONSOLE) += virtio_console.o
|
||||||
obj-$(CONFIG_RAW_DRIVER) += raw.o
|
obj-$(CONFIG_RAW_DRIVER) += raw.o
|
||||||
|
|
|
@ -27,7 +27,6 @@
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/kbd_kern.h>
|
#include <linux/kbd_kern.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/kref.h>
|
|
||||||
#include <linux/kthread.h>
|
#include <linux/kthread.h>
|
||||||
#include <linux/list.h>
|
#include <linux/list.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
@ -75,23 +74,6 @@ static int hvc_init(void);
|
||||||
static int sysrq_pressed;
|
static int sysrq_pressed;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct hvc_struct {
|
|
||||||
spinlock_t lock;
|
|
||||||
int index;
|
|
||||||
struct tty_struct *tty;
|
|
||||||
unsigned int count;
|
|
||||||
int do_wakeup;
|
|
||||||
char *outbuf;
|
|
||||||
int outbuf_size;
|
|
||||||
int n_outbuf;
|
|
||||||
uint32_t vtermno;
|
|
||||||
struct hv_ops *ops;
|
|
||||||
int irq_requested;
|
|
||||||
int irq;
|
|
||||||
struct list_head next;
|
|
||||||
struct kref kref; /* ref count & hvc_struct lifetime */
|
|
||||||
};
|
|
||||||
|
|
||||||
/* dynamic list of hvc_struct instances */
|
/* dynamic list of hvc_struct instances */
|
||||||
static LIST_HEAD(hvc_structs);
|
static LIST_HEAD(hvc_structs);
|
||||||
|
|
||||||
|
@ -300,26 +282,12 @@ int hvc_instantiate(uint32_t vtermno, int index, struct hv_ops *ops)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Wake the sleeping khvcd */
|
/* Wake the sleeping khvcd */
|
||||||
static void hvc_kick(void)
|
void hvc_kick(void)
|
||||||
{
|
{
|
||||||
hvc_kicked = 1;
|
hvc_kicked = 1;
|
||||||
wake_up_process(hvc_task);
|
wake_up_process(hvc_task);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int hvc_poll(struct hvc_struct *hp);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* NOTE: This API isn't used if the console adapter doesn't support interrupts.
|
|
||||||
* In this case the console is poll driven.
|
|
||||||
*/
|
|
||||||
static irqreturn_t hvc_handle_interrupt(int irq, void *dev_instance)
|
|
||||||
{
|
|
||||||
/* if hvc_poll request a repoll, then kick the hvcd thread */
|
|
||||||
if (hvc_poll(dev_instance))
|
|
||||||
hvc_kick();
|
|
||||||
return IRQ_HANDLED;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void hvc_unthrottle(struct tty_struct *tty)
|
static void hvc_unthrottle(struct tty_struct *tty)
|
||||||
{
|
{
|
||||||
hvc_kick();
|
hvc_kick();
|
||||||
|
@ -333,7 +301,6 @@ static int hvc_open(struct tty_struct *tty, struct file * filp)
|
||||||
{
|
{
|
||||||
struct hvc_struct *hp;
|
struct hvc_struct *hp;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
int irq = 0;
|
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
|
|
||||||
/* Auto increments kref reference if found. */
|
/* Auto increments kref reference if found. */
|
||||||
|
@ -352,18 +319,15 @@ static int hvc_open(struct tty_struct *tty, struct file * filp)
|
||||||
tty->low_latency = 1; /* Makes flushes to ldisc synchronous. */
|
tty->low_latency = 1; /* Makes flushes to ldisc synchronous. */
|
||||||
|
|
||||||
hp->tty = tty;
|
hp->tty = tty;
|
||||||
/* Save for request_irq outside of spin_lock. */
|
|
||||||
irq = hp->irq;
|
if (hp->ops->notifier_add)
|
||||||
if (irq)
|
rc = hp->ops->notifier_add(hp, hp->data);
|
||||||
hp->irq_requested = 1;
|
|
||||||
|
|
||||||
spin_unlock_irqrestore(&hp->lock, flags);
|
spin_unlock_irqrestore(&hp->lock, flags);
|
||||||
/* check error, fallback to non-irq */
|
|
||||||
if (irq)
|
|
||||||
rc = request_irq(irq, hvc_handle_interrupt, IRQF_DISABLED, "hvc_console", hp);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the request_irq() fails and we return an error. The tty layer
|
* If the notifier fails we return an error. The tty layer
|
||||||
* will call hvc_close() after a failed open but we don't want to clean
|
* will call hvc_close() after a failed open but we don't want to clean
|
||||||
* up there so we'll clean up here and clear out the previously set
|
* up there so we'll clean up here and clear out the previously set
|
||||||
* tty fields and return the kref reference.
|
* tty fields and return the kref reference.
|
||||||
|
@ -371,7 +335,6 @@ static int hvc_open(struct tty_struct *tty, struct file * filp)
|
||||||
if (rc) {
|
if (rc) {
|
||||||
spin_lock_irqsave(&hp->lock, flags);
|
spin_lock_irqsave(&hp->lock, flags);
|
||||||
hp->tty = NULL;
|
hp->tty = NULL;
|
||||||
hp->irq_requested = 0;
|
|
||||||
spin_unlock_irqrestore(&hp->lock, flags);
|
spin_unlock_irqrestore(&hp->lock, flags);
|
||||||
tty->driver_data = NULL;
|
tty->driver_data = NULL;
|
||||||
kref_put(&hp->kref, destroy_hvc_struct);
|
kref_put(&hp->kref, destroy_hvc_struct);
|
||||||
|
@ -386,7 +349,6 @@ static int hvc_open(struct tty_struct *tty, struct file * filp)
|
||||||
static void hvc_close(struct tty_struct *tty, struct file * filp)
|
static void hvc_close(struct tty_struct *tty, struct file * filp)
|
||||||
{
|
{
|
||||||
struct hvc_struct *hp;
|
struct hvc_struct *hp;
|
||||||
int irq = 0;
|
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
if (tty_hung_up_p(filp))
|
if (tty_hung_up_p(filp))
|
||||||
|
@ -404,9 +366,8 @@ static void hvc_close(struct tty_struct *tty, struct file * filp)
|
||||||
spin_lock_irqsave(&hp->lock, flags);
|
spin_lock_irqsave(&hp->lock, flags);
|
||||||
|
|
||||||
if (--hp->count == 0) {
|
if (--hp->count == 0) {
|
||||||
if (hp->irq_requested)
|
if (hp->ops->notifier_del)
|
||||||
irq = hp->irq;
|
hp->ops->notifier_del(hp, hp->data);
|
||||||
hp->irq_requested = 0;
|
|
||||||
|
|
||||||
/* We are done with the tty pointer now. */
|
/* We are done with the tty pointer now. */
|
||||||
hp->tty = NULL;
|
hp->tty = NULL;
|
||||||
|
@ -418,10 +379,6 @@ static void hvc_close(struct tty_struct *tty, struct file * filp)
|
||||||
* waking periodically to check chars_in_buffer().
|
* waking periodically to check chars_in_buffer().
|
||||||
*/
|
*/
|
||||||
tty_wait_until_sent(tty, HVC_CLOSE_WAIT);
|
tty_wait_until_sent(tty, HVC_CLOSE_WAIT);
|
||||||
|
|
||||||
if (irq)
|
|
||||||
free_irq(irq, hp);
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (hp->count < 0)
|
if (hp->count < 0)
|
||||||
printk(KERN_ERR "hvc_close %X: oops, count is %d\n",
|
printk(KERN_ERR "hvc_close %X: oops, count is %d\n",
|
||||||
|
@ -436,7 +393,6 @@ static void hvc_hangup(struct tty_struct *tty)
|
||||||
{
|
{
|
||||||
struct hvc_struct *hp = tty->driver_data;
|
struct hvc_struct *hp = tty->driver_data;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
int irq = 0;
|
|
||||||
int temp_open_count;
|
int temp_open_count;
|
||||||
|
|
||||||
if (!hp)
|
if (!hp)
|
||||||
|
@ -458,13 +414,12 @@ static void hvc_hangup(struct tty_struct *tty)
|
||||||
hp->count = 0;
|
hp->count = 0;
|
||||||
hp->n_outbuf = 0;
|
hp->n_outbuf = 0;
|
||||||
hp->tty = NULL;
|
hp->tty = NULL;
|
||||||
if (hp->irq_requested)
|
|
||||||
/* Saved for use outside of spin_lock. */
|
if (hp->ops->notifier_del)
|
||||||
irq = hp->irq;
|
hp->ops->notifier_del(hp, hp->data);
|
||||||
hp->irq_requested = 0;
|
|
||||||
spin_unlock_irqrestore(&hp->lock, flags);
|
spin_unlock_irqrestore(&hp->lock, flags);
|
||||||
if (irq)
|
|
||||||
free_irq(irq, hp);
|
|
||||||
while(temp_open_count) {
|
while(temp_open_count) {
|
||||||
--temp_open_count;
|
--temp_open_count;
|
||||||
kref_put(&hp->kref, destroy_hvc_struct);
|
kref_put(&hp->kref, destroy_hvc_struct);
|
||||||
|
@ -575,7 +530,7 @@ static u32 timeout = MIN_TIMEOUT;
|
||||||
#define HVC_POLL_READ 0x00000001
|
#define HVC_POLL_READ 0x00000001
|
||||||
#define HVC_POLL_WRITE 0x00000002
|
#define HVC_POLL_WRITE 0x00000002
|
||||||
|
|
||||||
static int hvc_poll(struct hvc_struct *hp)
|
int hvc_poll(struct hvc_struct *hp)
|
||||||
{
|
{
|
||||||
struct tty_struct *tty;
|
struct tty_struct *tty;
|
||||||
int i, n, poll_mask = 0;
|
int i, n, poll_mask = 0;
|
||||||
|
@ -602,10 +557,10 @@ static int hvc_poll(struct hvc_struct *hp)
|
||||||
if (test_bit(TTY_THROTTLED, &tty->flags))
|
if (test_bit(TTY_THROTTLED, &tty->flags))
|
||||||
goto throttled;
|
goto throttled;
|
||||||
|
|
||||||
/* If we aren't interrupt driven and aren't throttled, we always
|
/* If we aren't notifier driven and aren't throttled, we always
|
||||||
* request a reschedule
|
* request a reschedule
|
||||||
*/
|
*/
|
||||||
if (hp->irq == 0)
|
if (!hp->irq_requested)
|
||||||
poll_mask |= HVC_POLL_READ;
|
poll_mask |= HVC_POLL_READ;
|
||||||
|
|
||||||
/* Read data if any */
|
/* Read data if any */
|
||||||
|
@ -733,7 +688,7 @@ static const struct tty_operations hvc_ops = {
|
||||||
.chars_in_buffer = hvc_chars_in_buffer,
|
.chars_in_buffer = hvc_chars_in_buffer,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct hvc_struct __devinit *hvc_alloc(uint32_t vtermno, int irq,
|
struct hvc_struct __devinit *hvc_alloc(uint32_t vtermno, int data,
|
||||||
struct hv_ops *ops, int outbuf_size)
|
struct hv_ops *ops, int outbuf_size)
|
||||||
{
|
{
|
||||||
struct hvc_struct *hp;
|
struct hvc_struct *hp;
|
||||||
|
@ -754,7 +709,7 @@ struct hvc_struct __devinit *hvc_alloc(uint32_t vtermno, int irq,
|
||||||
memset(hp, 0x00, sizeof(*hp));
|
memset(hp, 0x00, sizeof(*hp));
|
||||||
|
|
||||||
hp->vtermno = vtermno;
|
hp->vtermno = vtermno;
|
||||||
hp->irq = irq;
|
hp->data = data;
|
||||||
hp->ops = ops;
|
hp->ops = ops;
|
||||||
hp->outbuf_size = outbuf_size;
|
hp->outbuf_size = outbuf_size;
|
||||||
hp->outbuf = &((char *)hp)[ALIGN(sizeof(*hp), sizeof(long))];
|
hp->outbuf = &((char *)hp)[ALIGN(sizeof(*hp), sizeof(long))];
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
|
|
||||||
#ifndef HVC_CONSOLE_H
|
#ifndef HVC_CONSOLE_H
|
||||||
#define HVC_CONSOLE_H
|
#define HVC_CONSOLE_H
|
||||||
|
#include <linux/kref.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This is the max number of console adapters that can/will be found as
|
* This is the max number of console adapters that can/will be found as
|
||||||
|
@ -42,24 +43,50 @@
|
||||||
*/
|
*/
|
||||||
#define HVC_ALLOC_TTY_ADAPTERS 8
|
#define HVC_ALLOC_TTY_ADAPTERS 8
|
||||||
|
|
||||||
|
struct hvc_struct {
|
||||||
|
spinlock_t lock;
|
||||||
|
int index;
|
||||||
|
struct tty_struct *tty;
|
||||||
|
unsigned int count;
|
||||||
|
int do_wakeup;
|
||||||
|
char *outbuf;
|
||||||
|
int outbuf_size;
|
||||||
|
int n_outbuf;
|
||||||
|
uint32_t vtermno;
|
||||||
|
struct hv_ops *ops;
|
||||||
|
int irq_requested;
|
||||||
|
int data;
|
||||||
|
struct list_head next;
|
||||||
|
struct kref kref; /* ref count & hvc_struct lifetime */
|
||||||
|
};
|
||||||
|
|
||||||
/* implemented by a low level driver */
|
/* implemented by a low level driver */
|
||||||
struct hv_ops {
|
struct hv_ops {
|
||||||
int (*get_chars)(uint32_t vtermno, char *buf, int count);
|
int (*get_chars)(uint32_t vtermno, char *buf, int count);
|
||||||
int (*put_chars)(uint32_t vtermno, const char *buf, int count);
|
int (*put_chars)(uint32_t vtermno, const char *buf, int count);
|
||||||
};
|
|
||||||
|
|
||||||
struct hvc_struct;
|
/* Callbacks for notification. Called in open and close */
|
||||||
|
int (*notifier_add)(struct hvc_struct *hp, int irq);
|
||||||
|
void (*notifier_del)(struct hvc_struct *hp, int irq);
|
||||||
|
};
|
||||||
|
|
||||||
/* Register a vterm and a slot index for use as a console (console_init) */
|
/* Register a vterm and a slot index for use as a console (console_init) */
|
||||||
extern int hvc_instantiate(uint32_t vtermno, int index, struct hv_ops *ops);
|
extern int hvc_instantiate(uint32_t vtermno, int index, struct hv_ops *ops);
|
||||||
|
|
||||||
/* register a vterm for hvc tty operation (module_init or hotplug add) */
|
/* register a vterm for hvc tty operation (module_init or hotplug add) */
|
||||||
extern struct hvc_struct * __devinit hvc_alloc(uint32_t vtermno, int irq,
|
extern struct hvc_struct * __devinit hvc_alloc(uint32_t vtermno, int data,
|
||||||
struct hv_ops *ops, int outbuf_size);
|
struct hv_ops *ops, int outbuf_size);
|
||||||
/* remove a vterm from hvc tty operation (modele_exit or hotplug remove) */
|
/* remove a vterm from hvc tty operation (module_exit or hotplug remove) */
|
||||||
extern int __devexit hvc_remove(struct hvc_struct *hp);
|
extern int __devexit hvc_remove(struct hvc_struct *hp);
|
||||||
|
|
||||||
|
/* data available */
|
||||||
|
int hvc_poll(struct hvc_struct *hp);
|
||||||
|
void hvc_kick(void);
|
||||||
|
|
||||||
|
/* default notifier for irq based notification */
|
||||||
|
extern int notifier_add_irq(struct hvc_struct *hp, int data);
|
||||||
|
extern void notifier_del_irq(struct hvc_struct *hp, int data);
|
||||||
|
|
||||||
|
|
||||||
#if defined(CONFIG_XMON) && defined(CONFIG_SMP)
|
#if defined(CONFIG_XMON) && defined(CONFIG_SMP)
|
||||||
#include <asm/xmon.h>
|
#include <asm/xmon.h>
|
||||||
|
|
44
drivers/char/hvc_irq.c
Normal file
44
drivers/char/hvc_irq.c
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
* Copyright IBM Corp. 2001,2008
|
||||||
|
*
|
||||||
|
* This file contains the IRQ specific code for hvc_console
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
|
||||||
|
#include "hvc_console.h"
|
||||||
|
|
||||||
|
static irqreturn_t hvc_handle_interrupt(int irq, void *dev_instance)
|
||||||
|
{
|
||||||
|
/* if hvc_poll request a repoll, then kick the hvcd thread */
|
||||||
|
if (hvc_poll(dev_instance))
|
||||||
|
hvc_kick();
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For IRQ based systems these callbacks can be used
|
||||||
|
*/
|
||||||
|
int notifier_add_irq(struct hvc_struct *hp, int irq)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
if (!irq) {
|
||||||
|
hp->irq_requested = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
rc = request_irq(irq, hvc_handle_interrupt, IRQF_DISABLED,
|
||||||
|
"hvc_console", hp);
|
||||||
|
if (!rc)
|
||||||
|
hp->irq_requested = 1;
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void notifier_del_irq(struct hvc_struct *hp, int irq)
|
||||||
|
{
|
||||||
|
if (!irq)
|
||||||
|
return;
|
||||||
|
free_irq(irq, hp);
|
||||||
|
hp->irq_requested = 0;
|
||||||
|
}
|
|
@ -200,6 +200,8 @@ done:
|
||||||
static struct hv_ops hvc_get_put_ops = {
|
static struct hv_ops hvc_get_put_ops = {
|
||||||
.get_chars = get_chars,
|
.get_chars = get_chars,
|
||||||
.put_chars = put_chars,
|
.put_chars = put_chars,
|
||||||
|
.notifier_add = notifier_add_irq,
|
||||||
|
.notifier_del = notifier_del_irq,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int __devinit hvc_vio_probe(struct vio_dev *vdev,
|
static int __devinit hvc_vio_probe(struct vio_dev *vdev,
|
||||||
|
|
|
@ -80,6 +80,8 @@ static int filtered_get_chars(uint32_t vtermno, char *buf, int count)
|
||||||
static struct hv_ops hvc_get_put_ops = {
|
static struct hv_ops hvc_get_put_ops = {
|
||||||
.get_chars = filtered_get_chars,
|
.get_chars = filtered_get_chars,
|
||||||
.put_chars = hvc_put_chars,
|
.put_chars = hvc_put_chars,
|
||||||
|
.notifier_add = notifier_add_irq,
|
||||||
|
.notifier_del = notifier_del_irq,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int __devinit hvc_vio_probe(struct vio_dev *vdev,
|
static int __devinit hvc_vio_probe(struct vio_dev *vdev,
|
||||||
|
|
|
@ -100,6 +100,8 @@ static int read_console(uint32_t vtermno, char *buf, int len)
|
||||||
static struct hv_ops hvc_ops = {
|
static struct hv_ops hvc_ops = {
|
||||||
.get_chars = read_console,
|
.get_chars = read_console,
|
||||||
.put_chars = write_console,
|
.put_chars = write_console,
|
||||||
|
.notifier_add = notifier_add_irq,
|
||||||
|
.notifier_del = notifier_del_irq,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int __init xen_init(void)
|
static int __init xen_init(void)
|
||||||
|
|
Loading…
Reference in a new issue