diff options
author | Christian Borntraeger <borntraeger@de.ibm.com> | 2008-06-20 09:24:08 -0400 |
---|---|---|
committer | Rusty Russell <rusty@rustcorp.com.au> | 2008-07-24 22:06:06 -0400 |
commit | 611e097d7707741a336a0677d9d69bec40f29f3d (patch) | |
tree | 0b0059544914bf027ada51a1ba90f694c54df835 /drivers/char/hvc_console.c | |
parent | 066f4d82a67f621ddd547bfa4b9c94631d8457b0 (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>
Diffstat (limited to 'drivers/char/hvc_console.c')
-rw-r--r-- | drivers/char/hvc_console.c | 81 |
1 files changed, 18 insertions, 63 deletions
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); | |||
75 | static int sysrq_pressed; | 74 | static int sysrq_pressed; |
76 | #endif | 75 | #endif |
77 | 76 | ||
78 | struct 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 */ |
96 | static LIST_HEAD(hvc_structs); | 78 | static 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 */ |
303 | static void hvc_kick(void) | 285 | void 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 | ||
309 | static 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 | */ | ||
315 | static 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 | |||
323 | static void hvc_unthrottle(struct tty_struct *tty) | 291 | static 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) | |||
386 | static void hvc_close(struct tty_struct *tty, struct file * filp) | 349 | static 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 | ||
578 | static int hvc_poll(struct hvc_struct *hp) | 533 | int 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 | ||
736 | struct hvc_struct __devinit *hvc_alloc(uint32_t vtermno, int irq, | 691 | struct 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))]; |