Commit 611e097d authored by Christian Borntraeger's avatar Christian Borntraeger Committed by Rusty Russell

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: default avatarChristian Borntraeger <borntraeger@de.ibm.com>
Signed-off-by: default avatarRusty Russell <rusty@rustcorp.com.au>
parent 066f4d82
...@@ -578,11 +578,14 @@ config HVC_DRIVER ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 @@ ...@@ -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); ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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; ...@@ -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) ...@@ -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 = { ...@@ -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, ...@@ -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 @@ ...@@ -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 @@ ...@@ -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>
......
/*
* 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: ...@@ -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) ...@@ -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) ...@@ -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)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment