Merge branches 'cma', 'cxgb3', 'ehca', 'ipath', 'ipoib', 'mad', 'misc', 'mlx4', ...
[safe/jmp/linux-2.6] / drivers / s390 / char / sclp_vt220.c
index 40cd21b..ad51738 100644 (file)
@@ -3,7 +3,7 @@
  *    SCLP VT220 terminal driver.
  *
  *  S390 version
- *    Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation
+ *    Copyright IBM Corp. 2003,2008
  *    Author(s): Peter Oberparleiter <Peter.Oberparleiter@de.ibm.com>
  */
 
@@ -27,7 +27,6 @@
 #include <asm/uaccess.h>
 #include "sclp.h"
 
-#define SCLP_VT220_PRINT_HEADER        "sclp vt220 tty driver: "
 #define SCLP_VT220_MAJOR               TTY_MAJOR
 #define SCLP_VT220_MINOR               65
 #define SCLP_VT220_DRIVER_NAME         "sclp_vt220"
@@ -71,9 +70,6 @@ static struct list_head sclp_vt220_outqueue;
 /* Number of requests in outqueue */
 static int sclp_vt220_outqueue_count;
 
-/* Wait queue used to delay write requests while we've run out of buffers */
-static wait_queue_head_t sclp_vt220_waitq;
-
 /* Timer used for delaying write requests to merge subsequent messages into
  * a single buffer */
 static struct timer_list sclp_vt220_timer;
@@ -85,8 +81,8 @@ static struct sclp_vt220_request *sclp_vt220_current_request;
 /* Number of characters in current request buffer */
 static int sclp_vt220_buffered_chars;
 
-/* Flag indicating whether this driver has already been initialized */
-static int sclp_vt220_initialized = 0;
+/* Counter controlling core driver initialization. */
+static int __initdata sclp_vt220_init_count;
 
 /* Flag indicating that sclp_vt220_current_request should really
  * have been already queued but wasn't because the SCLP was processing
@@ -133,7 +129,6 @@ sclp_vt220_process_queue(struct sclp_vt220_request *request)
        } while (request && __sclp_vt220_emit(request));
        if (request == NULL && sclp_vt220_flush_later)
                sclp_vt220_emit_current();
-       wake_up(&sclp_vt220_waitq);
        /* Check if the tty needs a wake up call */
        if (sclp_vt220_tty != NULL) {
                tty_wakeup(sclp_vt220_tty);
@@ -202,7 +197,7 @@ sclp_vt220_callback(struct sclp_req *request, void *data)
 static int
 __sclp_vt220_emit(struct sclp_vt220_request *request)
 {
-       if (!(sclp_vt220_register.sclp_send_mask & EVTYP_VT220MSG_MASK)) {
+       if (!(sclp_vt220_register.sclp_receive_mask & EVTYP_VT220MSG_MASK)) {
                request->sclp_req.status = SCLP_REQ_FAILED;
                return -EIO;
        }
@@ -367,7 +362,7 @@ sclp_vt220_timeout(unsigned long data)
        sclp_vt220_emit_current();
 }
 
-#define BUFFER_MAX_DELAY       HZ/2
+#define BUFFER_MAX_DELAY       HZ/20
 
 /* 
  * Internal implementation of the write function. Write COUNT bytes of data
@@ -383,7 +378,7 @@ sclp_vt220_timeout(unsigned long data)
  */
 static int
 __sclp_vt220_write(const unsigned char *buf, int count, int do_schedule,
-                  int convertlf)
+                  int convertlf, int may_fail)
 {
        unsigned long flags;
        void *page;
@@ -395,16 +390,14 @@ __sclp_vt220_write(const unsigned char *buf, int count, int do_schedule,
        overall_written = 0;
        spin_lock_irqsave(&sclp_vt220_lock, flags);
        do {
-               /* Create a sclp output buffer if none exists yet */
+               /* Create an sclp output buffer if none exists yet */
                if (sclp_vt220_current_request == NULL) {
                        while (list_empty(&sclp_vt220_empty)) {
-                               spin_unlock_irqrestore(&sclp_vt220_lock,
-                                                      flags);
-                               if (in_interrupt())
-                                       sclp_sync_wait();
+                               spin_unlock_irqrestore(&sclp_vt220_lock, flags);
+                               if (may_fail)
+                                       goto out;
                                else
-                                       wait_event(sclp_vt220_waitq,
-                                               !list_empty(&sclp_vt220_empty));
+                                       sclp_sync_wait();
                                spin_lock_irqsave(&sclp_vt220_lock, flags);
                        }
                        page = (void *) sclp_vt220_empty.next;
@@ -438,6 +431,7 @@ __sclp_vt220_write(const unsigned char *buf, int count, int do_schedule,
                add_timer(&sclp_vt220_timer);
        }
        spin_unlock_irqrestore(&sclp_vt220_lock, flags);
+out:
        return overall_written;
 }
 
@@ -450,7 +444,7 @@ __sclp_vt220_write(const unsigned char *buf, int count, int do_schedule,
 static int
 sclp_vt220_write(struct tty_struct *tty, const unsigned char *buf, int count)
 {
-       return __sclp_vt220_write(buf, count, 1, 0);
+       return __sclp_vt220_write(buf, count, 1, 0, 1);
 }
 
 #define SCLP_VT220_SESSION_ENDED       0x01
@@ -521,15 +515,11 @@ sclp_vt220_close(struct tty_struct *tty, struct file *filp)
  * character to the tty device.  If the kernel uses this routine,
  * it must call the flush_chars() routine (if defined) when it is
  * done stuffing characters into the driver.
- *
- * NOTE: include/linux/tty_driver.h specifies that a character should be
- * ignored if there is no room in the queue. This driver implements a different
- * semantic in that it will block when there is no more room left.
  */
-static void
+static int
 sclp_vt220_put_char(struct tty_struct *tty, unsigned char ch)
 {
-       __sclp_vt220_write(&ch, 1, 0, 0);
+       return __sclp_vt220_write(&ch, 1, 0, 0, 1);
 }
 
 /*
@@ -618,10 +608,8 @@ sclp_vt220_flush_buffer(struct tty_struct *tty)
        sclp_vt220_emit_current();
 }
 
-/*
- * Initialize all relevant components and register driver with system.
- */
-static void __init __sclp_vt220_cleanup(void)
+/* Release allocated pages. */
+static void __init __sclp_vt220_free_pages(void)
 {
        struct list_head *page, *p;
 
@@ -634,19 +622,31 @@ static void __init __sclp_vt220_cleanup(void)
        }
 }
 
-static int __init __sclp_vt220_init(void)
+/* Release memory and unregister from sclp core. Controlled by init counting -
+ * only the last invoker will actually perform these actions. */
+static void __init __sclp_vt220_cleanup(void)
+{
+       sclp_vt220_init_count--;
+       if (sclp_vt220_init_count != 0)
+               return;
+       sclp_unregister(&sclp_vt220_register);
+       __sclp_vt220_free_pages();
+}
+
+/* Allocate buffer pages and register with sclp core. Controlled by init
+ * counting - only the first invoker will actually perform these actions. */
+static int __init __sclp_vt220_init(int num_pages)
 {
        void *page;
        int i;
-       int num_pages;
+       int rc;
 
-       if (sclp_vt220_initialized)
+       sclp_vt220_init_count++;
+       if (sclp_vt220_init_count != 1)
                return 0;
-       sclp_vt220_initialized = 1;
        spin_lock_init(&sclp_vt220_lock);
        INIT_LIST_HEAD(&sclp_vt220_empty);
        INIT_LIST_HEAD(&sclp_vt220_outqueue);
-       init_waitqueue_head(&sclp_vt220_waitq);
        init_timer(&sclp_vt220_timer);
        sclp_vt220_current_request = NULL;
        sclp_vt220_buffered_chars = 0;
@@ -655,19 +655,24 @@ static int __init __sclp_vt220_init(void)
        sclp_vt220_flush_later = 0;
 
        /* Allocate pages for output buffering */
-       num_pages = slab_is_available() ? MAX_KMEM_PAGES : MAX_CONSOLE_PAGES;
        for (i = 0; i < num_pages; i++) {
                if (slab_is_available())
                        page = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
                else
                        page = alloc_bootmem_low_pages(PAGE_SIZE);
                if (!page) {
-                       __sclp_vt220_cleanup();
-                       return -ENOMEM;
+                       rc = -ENOMEM;
+                       goto out;
                }
                list_add_tail((struct list_head *) page, &sclp_vt220_empty);
        }
-       return 0;
+       rc = sclp_register(&sclp_vt220_register);
+out:
+       if (rc) {
+               __sclp_vt220_free_pages();
+               sclp_vt220_init_count--;
+       }
+       return rc;
 }
 
 static const struct tty_operations sclp_vt220_ops = {
@@ -694,16 +699,9 @@ static int __init sclp_vt220_tty_init(void)
        driver = alloc_tty_driver(1);
        if (!driver)
                return -ENOMEM;
-       rc = __sclp_vt220_init();
+       rc = __sclp_vt220_init(MAX_KMEM_PAGES);
        if (rc)
                goto out_driver;
-       rc = sclp_register(&sclp_vt220_register);
-       if (rc) {
-               printk(KERN_ERR SCLP_VT220_PRINT_HEADER
-                      "could not register tty - "
-                      "sclp_register returned %d\n", rc);
-               goto out_init;
-       }
 
        driver->owner = THIS_MODULE;
        driver->driver_name = SCLP_VT220_DRIVER_NAME;
@@ -717,17 +715,11 @@ static int __init sclp_vt220_tty_init(void)
        tty_set_operations(driver, &sclp_vt220_ops);
 
        rc = tty_register_driver(driver);
-       if (rc) {
-               printk(KERN_ERR SCLP_VT220_PRINT_HEADER
-                      "could not register tty - "
-                      "tty_register_driver returned %d\n", rc);
-               goto out_sclp;
-       }
+       if (rc)
+               goto out_init;
        sclp_vt220_driver = driver;
        return 0;
 
-out_sclp:
-       sclp_unregister(&sclp_vt220_register);
 out_init:
        __sclp_vt220_cleanup();
 out_driver:
@@ -741,7 +733,7 @@ __initcall(sclp_vt220_tty_init);
 static void
 sclp_vt220_con_write(struct console *con, const char *buf, unsigned int count)
 {
-       __sclp_vt220_write((const unsigned char *) buf, count, 1, 1);
+       __sclp_vt220_write((const unsigned char *) buf, count, 1, 1, 0);
 }
 
 static struct tty_driver *
@@ -780,7 +772,7 @@ sclp_vt220_con_init(void)
 
        if (!CONSOLE_IS_SCLP)
                return 0;
-       rc = __sclp_vt220_init();
+       rc = __sclp_vt220_init(MAX_CONSOLE_PAGES);
        if (rc)
                return rc;
        /* Attach linux console */