aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Borntraeger <borntraeger@de.ibm.com>2008-06-20 09:24:08 -0400
committerRusty Russell <rusty@rustcorp.com.au>2008-07-24 22:06:06 -0400
commit611e097d7707741a336a0677d9d69bec40f29f3d (patch)
tree0b0059544914bf027ada51a1ba90f694c54df835
parent066f4d82a67f621ddd547bfa4b9c94631d8457b0 (diff)
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>
-rw-r--r--drivers/char/Kconfig5
-rw-r--r--drivers/char/Makefile1
-rw-r--r--drivers/char/hvc_console.c81
-rw-r--r--drivers/char/hvc_console.h35
-rw-r--r--drivers/char/hvc_irq.c44
-rw-r--r--drivers/char/hvc_iseries.c2
-rw-r--r--drivers/char/hvc_vio.c2
-rw-r--r--drivers/char/hvc_xen.c2
8 files changed, 105 insertions, 67 deletions
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index 67b07576f8bf..d825361a6baf 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -578,11 +578,14 @@ config HVC_DRIVER
578 It will automatically be selected if one of the back-end console drivers 578 It will automatically be selected if one of the back-end console drivers
579 is selected. 579 is selected.
580 580
581config HVC_IRQ
582 bool
581 583
582config HVC_CONSOLE 584config HVC_CONSOLE
583 bool "pSeries Hypervisor Virtual Console support" 585 bool "pSeries Hypervisor Virtual Console support"
584 depends on PPC_PSERIES 586 depends on PPC_PSERIES
585 select HVC_DRIVER 587 select HVC_DRIVER
588 select HVC_IRQ
586 help 589 help
587 pSeries machines when partitioned support a hypervisor virtual 590 pSeries machines when partitioned support a hypervisor virtual
588 console. This driver allows each pSeries partition to have a console 591 console. This driver allows each pSeries partition to have a console
@@ -593,6 +596,7 @@ config HVC_ISERIES
593 depends on PPC_ISERIES 596 depends on PPC_ISERIES
594 default y 597 default y
595 select HVC_DRIVER 598 select HVC_DRIVER
599 select HVC_IRQ
596 help 600 help
597 iSeries machines support a hypervisor virtual console. 601 iSeries machines support a hypervisor virtual console.
598 602
@@ -614,6 +618,7 @@ config HVC_XEN
614 bool "Xen Hypervisor Console support" 618 bool "Xen Hypervisor Console support"
615 depends on XEN 619 depends on XEN
616 select HVC_DRIVER 620 select HVC_DRIVER
621 select HVC_IRQ
617 default y 622 default y
618 help 623 help
619 Xen virtual console device driver 624 Xen virtual console device driver
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index 4b6e736cfa02..eb02c3506800 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -48,6 +48,7 @@ obj-$(CONFIG_HVC_ISERIES) += hvc_iseries.o
48obj-$(CONFIG_HVC_RTAS) += hvc_rtas.o 48obj-$(CONFIG_HVC_RTAS) += hvc_rtas.o
49obj-$(CONFIG_HVC_BEAT) += hvc_beat.o 49obj-$(CONFIG_HVC_BEAT) += hvc_beat.o
50obj-$(CONFIG_HVC_DRIVER) += hvc_console.o 50obj-$(CONFIG_HVC_DRIVER) += hvc_console.o
51obj-$(CONFIG_HVC_IRQ) += hvc_irq.o
51obj-$(CONFIG_HVC_XEN) += hvc_xen.o 52obj-$(CONFIG_HVC_XEN) += hvc_xen.o
52obj-$(CONFIG_VIRTIO_CONSOLE) += virtio_console.o 53obj-$(CONFIG_VIRTIO_CONSOLE) += virtio_console.o
53obj-$(CONFIG_RAW_DRIVER) += raw.o 54obj-$(CONFIG_RAW_DRIVER) += raw.o
diff --git a/drivers/char/hvc_console.c b/drivers/char/hvc_console.c
index 2f9759d625cc..2f5b7fb67045 100644
--- a/drivers/char/hvc_console.c
+++ b/drivers/char/hvc_console.c
@@ -27,7 +27,6 @@
27#include <linux/init.h> 27#include <linux/init.h>
28#include <linux/kbd_kern.h> 28#include <linux/kbd_kern.h>
29#include <linux/kernel.h> 29#include <linux/kernel.h>
30#include <linux/kref.h>
31#include <linux/kthread.h> 30#include <linux/kthread.h>
32#include <linux/list.h> 31#include <linux/list.h>
33#include <linux/module.h> 32#include <linux/module.h>
@@ -75,23 +74,6 @@ static int hvc_init(void);
75static int sysrq_pressed; 74static int sysrq_pressed;
76#endif 75#endif
77 76
78struct hvc_struct {
79 spinlock_t lock;
80 int index;
81 struct tty_struct *tty;
82 unsigned int count;
83 int do_wakeup;
84 char *outbuf;
85 int outbuf_size;
86 int n_outbuf;
87 uint32_t vtermno;
88 struct hv_ops *ops;
89 int irq_requested;
90 int irq;
91 struct list_head next;
92 struct kref kref; /* ref count & hvc_struct lifetime */
93};
94
95/* dynamic list of hvc_struct instances */ 77/* dynamic list of hvc_struct instances */
96static LIST_HEAD(hvc_structs); 78static LIST_HEAD(hvc_structs);
97 79
@@ -300,26 +282,12 @@ int hvc_instantiate(uint32_t vtermno, int index, struct hv_ops *ops)
300} 282}
301 283
302/* Wake the sleeping khvcd */ 284/* Wake the sleeping khvcd */
303static void hvc_kick(void) 285void hvc_kick(void)
304{ 286{
305 hvc_kicked = 1; 287 hvc_kicked = 1;
306 wake_up_process(hvc_task); 288 wake_up_process(hvc_task);
307} 289}
308 290
309static int hvc_poll(struct hvc_struct *hp);
310
311/*
312 * NOTE: This API isn't used if the console adapter doesn't support interrupts.
313 * In this case the console is poll driven.
314 */
315static irqreturn_t hvc_handle_interrupt(int irq, void *dev_instance)
316{
317 /* if hvc_poll request a repoll, then kick the hvcd thread */
318 if (hvc_poll(dev_instance))
319 hvc_kick();
320 return IRQ_HANDLED;
321}
322
323static void hvc_unthrottle(struct tty_struct *tty) 291static void hvc_unthrottle(struct tty_struct *tty)
324{ 292{
325 hvc_kick(); 293 hvc_kick();
@@ -333,7 +301,6 @@ static int hvc_open(struct tty_struct *tty, struct file * filp)
333{ 301{
334 struct hvc_struct *hp; 302 struct hvc_struct *hp;
335 unsigned long flags; 303 unsigned long flags;
336 int irq = 0;
337 int rc = 0; 304 int rc = 0;
338 305
339 /* Auto increments kref reference if found. */ 306 /* Auto increments kref reference if found. */
@@ -352,18 +319,15 @@ static int hvc_open(struct tty_struct *tty, struct file * filp)
352 tty->low_latency = 1; /* Makes flushes to ldisc synchronous. */ 319 tty->low_latency = 1; /* Makes flushes to ldisc synchronous. */
353 320
354 hp->tty = tty; 321 hp->tty = tty;
355 /* Save for request_irq outside of spin_lock. */ 322
356 irq = hp->irq; 323 if (hp->ops->notifier_add)
357 if (irq) 324 rc = hp->ops->notifier_add(hp, hp->data);
358 hp->irq_requested = 1;
359 325
360 spin_unlock_irqrestore(&hp->lock, flags); 326 spin_unlock_irqrestore(&hp->lock, flags);
361 /* check error, fallback to non-irq */ 327
362 if (irq)
363 rc = request_irq(irq, hvc_handle_interrupt, IRQF_DISABLED, "hvc_console", hp);
364 328
365 /* 329 /*
366 * If the request_irq() fails and we return an error. The tty layer 330 * If the notifier fails we return an error. The tty layer
367 * will call hvc_close() after a failed open but we don't want to clean 331 * will call hvc_close() after a failed open but we don't want to clean
368 * up there so we'll clean up here and clear out the previously set 332 * up there so we'll clean up here and clear out the previously set
369 * tty fields and return the kref reference. 333 * tty fields and return the kref reference.
@@ -371,7 +335,6 @@ static int hvc_open(struct tty_struct *tty, struct file * filp)
371 if (rc) { 335 if (rc) {
372 spin_lock_irqsave(&hp->lock, flags); 336 spin_lock_irqsave(&hp->lock, flags);
373 hp->tty = NULL; 337 hp->tty = NULL;
374 hp->irq_requested = 0;
375 spin_unlock_irqrestore(&hp->lock, flags); 338 spin_unlock_irqrestore(&hp->lock, flags);
376 tty->driver_data = NULL; 339 tty->driver_data = NULL;
377 kref_put(&hp->kref, destroy_hvc_struct); 340 kref_put(&hp->kref, destroy_hvc_struct);
@@ -386,7 +349,6 @@ static int hvc_open(struct tty_struct *tty, struct file * filp)
386static void hvc_close(struct tty_struct *tty, struct file * filp) 349static void hvc_close(struct tty_struct *tty, struct file * filp)
387{ 350{
388 struct hvc_struct *hp; 351 struct hvc_struct *hp;
389 int irq = 0;
390 unsigned long flags; 352 unsigned long flags;
391 353
392 if (tty_hung_up_p(filp)) 354 if (tty_hung_up_p(filp))
@@ -404,9 +366,8 @@ static void hvc_close(struct tty_struct *tty, struct file * filp)
404 spin_lock_irqsave(&hp->lock, flags); 366 spin_lock_irqsave(&hp->lock, flags);
405 367
406 if (--hp->count == 0) { 368 if (--hp->count == 0) {
407 if (hp->irq_requested) 369 if (hp->ops->notifier_del)
408 irq = hp->irq; 370 hp->ops->notifier_del(hp, hp->data);
409 hp->irq_requested = 0;
410 371
411 /* We are done with the tty pointer now. */ 372 /* We are done with the tty pointer now. */
412 hp->tty = NULL; 373 hp->tty = NULL;
@@ -418,10 +379,6 @@ static void hvc_close(struct tty_struct *tty, struct file * filp)
418 * waking periodically to check chars_in_buffer(). 379 * waking periodically to check chars_in_buffer().
419 */ 380 */
420 tty_wait_until_sent(tty, HVC_CLOSE_WAIT); 381 tty_wait_until_sent(tty, HVC_CLOSE_WAIT);
421
422 if (irq)
423 free_irq(irq, hp);
424
425 } else { 382 } else {
426 if (hp->count < 0) 383 if (hp->count < 0)
427 printk(KERN_ERR "hvc_close %X: oops, count is %d\n", 384 printk(KERN_ERR "hvc_close %X: oops, count is %d\n",
@@ -436,7 +393,6 @@ static void hvc_hangup(struct tty_struct *tty)
436{ 393{
437 struct hvc_struct *hp = tty->driver_data; 394 struct hvc_struct *hp = tty->driver_data;
438 unsigned long flags; 395 unsigned long flags;
439 int irq = 0;
440 int temp_open_count; 396 int temp_open_count;
441 397
442 if (!hp) 398 if (!hp)
@@ -458,13 +414,12 @@ static void hvc_hangup(struct tty_struct *tty)
458 hp->count = 0; 414 hp->count = 0;
459 hp->n_outbuf = 0; 415 hp->n_outbuf = 0;
460 hp->tty = NULL; 416 hp->tty = NULL;
461 if (hp->irq_requested) 417
462 /* Saved for use outside of spin_lock. */ 418 if (hp->ops->notifier_del)
463 irq = hp->irq; 419 hp->ops->notifier_del(hp, hp->data);
464 hp->irq_requested = 0; 420
465 spin_unlock_irqrestore(&hp->lock, flags); 421 spin_unlock_irqrestore(&hp->lock, flags);
466 if (irq) 422
467 free_irq(irq, hp);
468 while(temp_open_count) { 423 while(temp_open_count) {
469 --temp_open_count; 424 --temp_open_count;
470 kref_put(&hp->kref, destroy_hvc_struct); 425 kref_put(&hp->kref, destroy_hvc_struct);
@@ -575,7 +530,7 @@ static u32 timeout = MIN_TIMEOUT;
575#define HVC_POLL_READ 0x00000001 530#define HVC_POLL_READ 0x00000001
576#define HVC_POLL_WRITE 0x00000002 531#define HVC_POLL_WRITE 0x00000002
577 532
578static int hvc_poll(struct hvc_struct *hp) 533int hvc_poll(struct hvc_struct *hp)
579{ 534{
580 struct tty_struct *tty; 535 struct tty_struct *tty;
581 int i, n, poll_mask = 0; 536 int i, n, poll_mask = 0;
@@ -602,10 +557,10 @@ static int hvc_poll(struct hvc_struct *hp)
602 if (test_bit(TTY_THROTTLED, &tty->flags)) 557 if (test_bit(TTY_THROTTLED, &tty->flags))
603 goto throttled; 558 goto throttled;
604 559
605 /* If we aren't interrupt driven and aren't throttled, we always 560 /* If we aren't notifier driven and aren't throttled, we always
606 * request a reschedule 561 * request a reschedule
607 */ 562 */
608 if (hp->irq == 0) 563 if (!hp->irq_requested)
609 poll_mask |= HVC_POLL_READ; 564 poll_mask |= HVC_POLL_READ;
610 565
611 /* Read data if any */ 566 /* Read data if any */
@@ -733,7 +688,7 @@ static const struct tty_operations hvc_ops = {
733 .chars_in_buffer = hvc_chars_in_buffer, 688 .chars_in_buffer = hvc_chars_in_buffer,
734}; 689};
735 690
736struct hvc_struct __devinit *hvc_alloc(uint32_t vtermno, int irq, 691struct hvc_struct __devinit *hvc_alloc(uint32_t vtermno, int data,
737 struct hv_ops *ops, int outbuf_size) 692 struct hv_ops *ops, int outbuf_size)
738{ 693{
739 struct hvc_struct *hp; 694 struct hvc_struct *hp;
@@ -754,7 +709,7 @@ struct hvc_struct __devinit *hvc_alloc(uint32_t vtermno, int irq,
754 memset(hp, 0x00, sizeof(*hp)); 709 memset(hp, 0x00, sizeof(*hp));
755 710
756 hp->vtermno = vtermno; 711 hp->vtermno = vtermno;
757 hp->irq = irq; 712 hp->data = data;
758 hp->ops = ops; 713 hp->ops = ops;
759 hp->outbuf_size = outbuf_size; 714 hp->outbuf_size = outbuf_size;
760 hp->outbuf = &((char *)hp)[ALIGN(sizeof(*hp), sizeof(long))]; 715 hp->outbuf = &((char *)hp)[ALIGN(sizeof(*hp), sizeof(long))];
diff --git a/drivers/char/hvc_console.h b/drivers/char/hvc_console.h
index 42ffb17e15df..d9ce10915625 100644
--- a/drivers/char/hvc_console.h
+++ b/drivers/char/hvc_console.h
@@ -26,6 +26,7 @@
26 26
27#ifndef HVC_CONSOLE_H 27#ifndef HVC_CONSOLE_H
28#define HVC_CONSOLE_H 28#define HVC_CONSOLE_H
29#include <linux/kref.h>
29 30
30/* 31/*
31 * This is the max number of console adapters that can/will be found as 32 * This is the max number of console adapters that can/will be found as
@@ -42,24 +43,50 @@
42 */ 43 */
43#define HVC_ALLOC_TTY_ADAPTERS 8 44#define HVC_ALLOC_TTY_ADAPTERS 8
44 45
46struct hvc_struct {
47 spinlock_t lock;
48 int index;
49 struct tty_struct *tty;
50 unsigned int count;
51 int do_wakeup;
52 char *outbuf;
53 int outbuf_size;
54 int n_outbuf;
55 uint32_t vtermno;
56 struct hv_ops *ops;
57 int irq_requested;
58 int data;
59 struct list_head next;
60 struct kref kref; /* ref count & hvc_struct lifetime */
61};
45 62
46/* implemented by a low level driver */ 63/* implemented by a low level driver */
47struct hv_ops { 64struct hv_ops {
48 int (*get_chars)(uint32_t vtermno, char *buf, int count); 65 int (*get_chars)(uint32_t vtermno, char *buf, int count);
49 int (*put_chars)(uint32_t vtermno, const char *buf, int count); 66 int (*put_chars)(uint32_t vtermno, const char *buf, int count);
50};
51 67
52struct hvc_struct; 68 /* Callbacks for notification. Called in open and close */
69 int (*notifier_add)(struct hvc_struct *hp, int irq);
70 void (*notifier_del)(struct hvc_struct *hp, int irq);
71};
53 72
54/* Register a vterm and a slot index for use as a console (console_init) */ 73/* Register a vterm and a slot index for use as a console (console_init) */
55extern int hvc_instantiate(uint32_t vtermno, int index, struct hv_ops *ops); 74extern int hvc_instantiate(uint32_t vtermno, int index, struct hv_ops *ops);
56 75
57/* register a vterm for hvc tty operation (module_init or hotplug add) */ 76/* register a vterm for hvc tty operation (module_init or hotplug add) */
58extern struct hvc_struct * __devinit hvc_alloc(uint32_t vtermno, int irq, 77extern struct hvc_struct * __devinit hvc_alloc(uint32_t vtermno, int data,
59 struct hv_ops *ops, int outbuf_size); 78 struct hv_ops *ops, int outbuf_size);
60/* remove a vterm from hvc tty operation (modele_exit or hotplug remove) */ 79/* remove a vterm from hvc tty operation (module_exit or hotplug remove) */
61extern int __devexit hvc_remove(struct hvc_struct *hp); 80extern int __devexit hvc_remove(struct hvc_struct *hp);
62 81
82/* data available */
83int hvc_poll(struct hvc_struct *hp);
84void hvc_kick(void);
85
86/* default notifier for irq based notification */
87extern int notifier_add_irq(struct hvc_struct *hp, int data);
88extern void notifier_del_irq(struct hvc_struct *hp, int data);
89
63 90
64#if defined(CONFIG_XMON) && defined(CONFIG_SMP) 91#if defined(CONFIG_XMON) && defined(CONFIG_SMP)
65#include <asm/xmon.h> 92#include <asm/xmon.h>
diff --git a/drivers/char/hvc_irq.c b/drivers/char/hvc_irq.c
new file mode 100644
index 000000000000..73a59cdb8947
--- /dev/null
+++ b/drivers/char/hvc_irq.c
@@ -0,0 +1,44 @@
1/*
2 * Copyright IBM Corp. 2001,2008
3 *
4 * This file contains the IRQ specific code for hvc_console
5 *
6 */
7
8#include <linux/interrupt.h>
9
10#include "hvc_console.h"
11
12static irqreturn_t hvc_handle_interrupt(int irq, void *dev_instance)
13{
14 /* if hvc_poll request a repoll, then kick the hvcd thread */
15 if (hvc_poll(dev_instance))
16 hvc_kick();
17 return IRQ_HANDLED;
18}
19
20/*
21 * For IRQ based systems these callbacks can be used
22 */
23int notifier_add_irq(struct hvc_struct *hp, int irq)
24{
25 int rc;
26
27 if (!irq) {
28 hp->irq_requested = 0;
29 return 0;
30 }
31 rc = request_irq(irq, hvc_handle_interrupt, IRQF_DISABLED,
32 "hvc_console", hp);
33 if (!rc)
34 hp->irq_requested = 1;
35 return rc;
36}
37
38void notifier_del_irq(struct hvc_struct *hp, int irq)
39{
40 if (!irq)
41 return;
42 free_irq(irq, hp);
43 hp->irq_requested = 0;
44}
diff --git a/drivers/char/hvc_iseries.c b/drivers/char/hvc_iseries.c
index a08f8f981c11..b71c610fe5ae 100644
--- a/drivers/char/hvc_iseries.c
+++ b/drivers/char/hvc_iseries.c
@@ -200,6 +200,8 @@ done:
200static struct hv_ops hvc_get_put_ops = { 200static struct hv_ops hvc_get_put_ops = {
201 .get_chars = get_chars, 201 .get_chars = get_chars,
202 .put_chars = put_chars, 202 .put_chars = put_chars,
203 .notifier_add = notifier_add_irq,
204 .notifier_del = notifier_del_irq,
203}; 205};
204 206
205static int __devinit hvc_vio_probe(struct vio_dev *vdev, 207static int __devinit hvc_vio_probe(struct vio_dev *vdev,
diff --git a/drivers/char/hvc_vio.c b/drivers/char/hvc_vio.c
index 79711aa4b41d..93f3840c1682 100644
--- a/drivers/char/hvc_vio.c
+++ b/drivers/char/hvc_vio.c
@@ -80,6 +80,8 @@ static int filtered_get_chars(uint32_t vtermno, char *buf, int count)
80static struct hv_ops hvc_get_put_ops = { 80static struct hv_ops hvc_get_put_ops = {
81 .get_chars = filtered_get_chars, 81 .get_chars = filtered_get_chars,
82 .put_chars = hvc_put_chars, 82 .put_chars = hvc_put_chars,
83 .notifier_add = notifier_add_irq,
84 .notifier_del = notifier_del_irq,
83}; 85};
84 86
85static int __devinit hvc_vio_probe(struct vio_dev *vdev, 87static int __devinit hvc_vio_probe(struct vio_dev *vdev,
diff --git a/drivers/char/hvc_xen.c b/drivers/char/hvc_xen.c
index db2ae4216279..6b70aa66a587 100644
--- a/drivers/char/hvc_xen.c
+++ b/drivers/char/hvc_xen.c
@@ -100,6 +100,8 @@ static int read_console(uint32_t vtermno, char *buf, int len)
100static struct hv_ops hvc_ops = { 100static struct hv_ops hvc_ops = {
101 .get_chars = read_console, 101 .get_chars = read_console,
102 .put_chars = write_console, 102 .put_chars = write_console,
103 .notifier_add = notifier_add_irq,
104 .notifier_del = notifier_del_irq,
103}; 105};
104 106
105static int __init xen_init(void) 107static int __init xen_init(void)