]> git.neil.brown.name Git - history.git/commitdiff
[PATCH] s390: console device drivers.
authorMartin Schwidefsky <schwidefsky@de.ibm.com>
Mon, 26 May 2003 06:35:25 +0000 (23:35 -0700)
committerLinus Torvalds <torvalds@home.transmeta.com>
Mon, 26 May 2003 06:35:25 +0000 (23:35 -0700)
s390 console driver fixes:
 - Register console ttys via module_init. Remove sclp_tty_init and
   tty3215_init from tty_io.c
 - con3215: use set_current_state.
 - sclp: Fix race condition in sclp interrupt handler. Fix deadlock on
   sclp_conbuf_lock for certain error conditions.

drivers/char/tty_io.c
drivers/s390/char/con3215.c
drivers/s390/char/sclp.c
drivers/s390/char/sclp_con.c
drivers/s390/char/sclp_tty.c
drivers/s390/char/sclp_tty.h

index 62bcbcac3aa9448d8442dcd941e92583360855b5..2f73626c2b68240f71f9bd29723f419c4d89d74f 100644 (file)
@@ -145,8 +145,6 @@ static int tty_fasync(int fd, struct file * filp, int on);
 extern int vme_scc_init (void);
 extern int serial167_init(void);
 extern int rs_8xx_init(void);
-extern void sclp_tty_init(void);
-extern void tty3215_init(void);
 extern void tub3270_init(void);
 extern void rs_360_init(void);
 extern void tx3912_rs_init(void);
@@ -2480,9 +2478,6 @@ void __init tty_init(void)
 #ifdef CONFIG_TN3270
        tub3270_init();
 #endif
-#ifdef CONFIG_SCLP_TTY
-       sclp_tty_init();
-#endif
 #ifdef CONFIG_A2232
        a2232board_init();
 #endif
index 0c4d434de5aa83ca6c9b39dfad9977b51df6ace0..d663c8b2ff639b173a3c76cb7af9fad2d3c489d2 100644 (file)
@@ -697,12 +697,12 @@ raw3215_shutdown(struct raw3215_info *raw)
            raw->queued_read != NULL) {
                raw->flags |= RAW3215_CLOSING;
                add_wait_queue(&raw->empty_wait, &wait);
-               current->state = TASK_INTERRUPTIBLE;
+               set_current_state(TASK_INTERRUPTIBLE);
                spin_unlock_irqrestore(raw->lock, flags);
                schedule();
                spin_lock_irqsave(raw->lock, flags);
                remove_wait_queue(&raw->empty_wait, &wait);
-               current->state = TASK_RUNNING;
+               set_current_state(TASK_RUNNING);
                raw->flags &= ~(RAW3215_ACTIVE | RAW3215_CLOSING);
        }
        spin_unlock_irqrestore(raw->lock, flags);
index 92c3ae9b2113f90c08b55c23d60d4162725d7706..fd22fec80fb2e98ddf2a168b7b468f13e1adcfc8 100644 (file)
@@ -18,7 +18,9 @@
 #include <linux/spinlock.h>
 #include <linux/interrupt.h>
 #include <linux/timer.h>
+#include <linux/init.h>
 #include <asm/s390_ext.h>
+#include <asm/processor.h>
 
 #include "sclp.h"
 
@@ -49,7 +51,7 @@ static char sclp_init_sccb[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE)));
 /* Timer for init mask retries. */
 static struct timer_list retry_timer;
 
-static unsigned long sclp_status = 0;
+static volatile unsigned long sclp_status = 0;
 /* some status flags */
 #define SCLP_INIT              0
 #define SCLP_RUNNING           1
@@ -275,20 +277,24 @@ sclp_interrupt_handler(struct pt_regs *regs, __u16 code)
        struct list_head *l;
        struct sclp_req *req, *tmp;
 
+       spin_lock(&sclp_lock);
        /*
         * Only process interrupt if sclp is initialized.
         * This avoids strange effects for a pending request
         * from before the last re-ipl.
         */
-       if (!test_bit(SCLP_INIT, &sclp_status))
+       if (!test_bit(SCLP_INIT, &sclp_status)) {
+               /* Now clear the running bit */
+               clear_bit(SCLP_RUNNING, &sclp_status);
+               spin_unlock(&sclp_lock);
                return;
+       }
        ext_int_param = S390_lowcore.ext_params;
        finished_sccb = ext_int_param & EXT_INT_SCCB_MASK;
        evbuf_pending = ext_int_param & (EXT_INT_EVBUF_PENDING |
                                         EXT_INT_STATECHANGE_PENDING);
        irq_enter();
        req = NULL;
-       spin_lock(&sclp_lock);
        if (finished_sccb != 0U) {
                list_for_each(l, &sclp_req_queue) {
                        tmp = list_entry(l, struct sclp_req, list);
@@ -299,9 +305,6 @@ sclp_interrupt_handler(struct pt_regs *regs, __u16 code)
                        }
                }
        }
-       /* Head queue a read sccb if an event buffer is pending */
-       if (evbuf_pending)
-               __sclp_unconditional_read();
        spin_unlock(&sclp_lock);
        /* Perform callback */
        if (req != NULL) {
@@ -309,8 +312,13 @@ sclp_interrupt_handler(struct pt_regs *regs, __u16 code)
                if (req->callback != NULL)
                        req->callback(req, req->callback_data);
        }
+       spin_lock(&sclp_lock);
+       /* Head queue a read sccb if an event buffer is pending */
+       if (evbuf_pending)
+               __sclp_unconditional_read();
        /* Now clear the running bit */
        clear_bit(SCLP_RUNNING, &sclp_status);
+       spin_unlock(&sclp_lock);
        /* and start next request on the queue */
        sclp_start_request();
        irq_exit();
@@ -344,8 +352,10 @@ sclp_sync_wait(void)
                      : "=m" (psw_mask) : "a" (&psw_mask) : "memory");
 
        /* wait until ISR signals receipt of interrupt */
-       while (test_bit(SCLP_RUNNING, &sclp_status))
+       while (test_bit(SCLP_RUNNING, &sclp_status)) {
                barrier();
+               cpu_relax();
+       }
 
        /* disable external interruptions */
        asm volatile ("SSM 0(%0)"
@@ -631,6 +641,14 @@ sclp_init(void)
                /* Already initialized. */
                return 0;
 
+       spin_lock_init(&sclp_lock);
+       INIT_LIST_HEAD(&sclp_req_queue);
+
+       /* init event list */
+       INIT_LIST_HEAD(&sclp_reg_list);
+       list_add(&sclp_state_change_event.list, &sclp_reg_list);
+       list_add(&sclp_quiesce_event.list, &sclp_reg_list);
+
        /*
         * request the 0x2401 external interrupt
         * The sclp driver is initialized early (before kmalloc works). We
@@ -640,14 +658,6 @@ sclp_init(void)
                                              &ext_int_info_hwc) != 0)
                return -EBUSY;
 
-       spin_lock_init(&sclp_lock);
-       INIT_LIST_HEAD(&sclp_req_queue);
-
-       /* init event list */
-       INIT_LIST_HEAD(&sclp_reg_list);
-       list_add(&sclp_state_change_event.list, &sclp_reg_list);
-       list_add(&sclp_quiesce_event.list, &sclp_reg_list);
-
        /* enable service-signal external interruptions,
         * Control Register 0 bit 22 := 1
         * (besides PSW bit 7 must be set to 1 sometimes for external
@@ -762,6 +772,8 @@ sclp_remove_processed(struct sccb_header *sccb)
        return unprocessed;
 }
 
+module_init(sclp_init);
+
 EXPORT_SYMBOL(sclp_add_request);
 EXPORT_SYMBOL(sclp_sync_wait);
 EXPORT_SYMBOL(sclp_register);
index 362ea96a91048d4c34c65aacc8041964794ad95b..f718495494529fbb4dcc42e8d7084c99be3993a9 100644 (file)
 #include <linux/timer.h>
 #include <linux/jiffies.h>
 #include <linux/bootmem.h>
+#include <linux/err.h>
 
 #include "sclp.h"
 #include "sclp_rw.h"
+#include "sclp_tty.h"
 
 #define SCLP_CON_PRINT_HEADER "sclp console driver: "
 
@@ -69,10 +71,23 @@ sclp_conbuf_callback(struct sclp_buffer *buffer, int rc)
 }
 
 static inline void
-__sclp_conbuf_emit(struct sclp_buffer *buffer)
+sclp_conbuf_emit(void)
 {
+       struct sclp_buffer* buffer;
+       unsigned long flags;
+       int count;
+
+       spin_lock_irqsave(&sclp_con_lock, flags);
+       buffer = sclp_conbuf;
+       sclp_conbuf = NULL;
+       if (buffer == NULL) {
+               spin_unlock_irqrestore(&sclp_con_lock, flags);
+               return;
+       }
        list_add_tail(&buffer->list, &sclp_con_outqueue);
-       if (sclp_con_buffer_count++ == 0)
+       count = sclp_con_buffer_count++;
+       spin_unlock_irqrestore(&sclp_con_lock, flags);
+       if (count == 0)
                sclp_emit_buffer(buffer, sclp_conbuf_callback);
 }
 
@@ -83,14 +98,7 @@ __sclp_conbuf_emit(struct sclp_buffer *buffer)
 static void
 sclp_console_timeout(unsigned long data)
 {
-       unsigned long flags;
-
-       spin_lock_irqsave(&sclp_con_lock, flags);
-       if (sclp_conbuf != NULL) {
-               __sclp_conbuf_emit(sclp_conbuf);
-               sclp_conbuf = NULL;
-       }
-       spin_unlock_irqrestore(&sclp_con_lock, flags);
+       sclp_conbuf_emit();
 }
 
 /*
@@ -134,8 +142,9 @@ sclp_console_write(struct console *console, const char *message,
                 * output buffer. Emit the buffer, create a new buffer
                 * and then output the rest of the string.
                 */
-               __sclp_conbuf_emit(sclp_conbuf);
-               sclp_conbuf = NULL;
+               spin_unlock_irqrestore(&sclp_con_lock, flags);
+               sclp_conbuf_emit();
+               spin_lock_irqsave(&sclp_con_lock, flags);
                message += written;
                count -= written;
        } while (count > 0);
@@ -150,11 +159,11 @@ sclp_console_write(struct console *console, const char *message,
        spin_unlock_irqrestore(&sclp_con_lock, flags);
 }
 
-/* returns the device number of the SCLP console */
-static kdev_t
-sclp_console_device(struct console *c)
+static struct tty_driver *
+sclp_console_device(struct console *c, int *index)
 {
-       return  mk_kdev(sclp_console_major, sclp_console_minor);
+       *index = c->index;
+       return &sclp_tty_driver;
 }
 
 /*
@@ -167,13 +176,10 @@ sclp_console_unblank(void)
 {
        unsigned long flags;
 
+       sclp_conbuf_emit();
        spin_lock_irqsave(&sclp_con_lock, flags);
        if (timer_pending(&sclp_con_timer))
                del_timer(&sclp_con_timer);
-       if (sclp_conbuf != NULL) {
-               __sclp_conbuf_emit(sclp_conbuf);
-               sclp_conbuf = NULL;
-       }
        while (sclp_con_buffer_count > 0) {
                spin_unlock_irqrestore(&sclp_con_lock, flags);
                sclp_sync_wait();
@@ -204,17 +210,19 @@ sclp_console_init(void)
 {
        void *page;
        int i;
+       int rc;
 
        if (!CONSOLE_IS_SCLP)
                return 0;
-       if (sclp_rw_init() != 0)
-               return 0;
+       rc = sclp_rw_init();
+       if (rc)
+               return rc;
        /* Allocate pages for output buffering */
        INIT_LIST_HEAD(&sclp_con_pages);
        for (i = 0; i < MAX_CONSOLE_PAGES; i++) {
                page = alloc_bootmem_low_pages(PAGE_SIZE);
                if (page == NULL)
-                       return 0;
+                       return -ENOMEM;
                list_add_tail((struct list_head *) page, &sclp_con_pages);
        }
        INIT_LIST_HEAD(&sclp_con_outqueue);
index d700ede517cdf64817d71c7a83d3950074bb0bbc..57bdce0f673cf6f4c58105e08ecdeeed1c08d36d 100644 (file)
@@ -16,6 +16,8 @@
 #include <linux/sched.h>
 #include <linux/wait.h>
 #include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/init.h>
 #include <asm/uaccess.h>
 
 #include "ctrlchar.h"
@@ -55,7 +57,7 @@ static struct tty_struct *sclp_tty;
 static unsigned char sclp_tty_chars[SCLP_TTY_BUF_SIZE];
 static unsigned short int sclp_tty_chars_count;
 
-static struct tty_driver sclp_tty_driver;
+struct tty_driver sclp_tty_driver;
 static struct tty_struct * sclp_tty_table[1];
 static struct termios * sclp_tty_termios[1];
 static struct termios * sclp_tty_termios_locked[1];
@@ -710,7 +712,7 @@ static struct sclp_register sclp_input_event =
        .receiver_fn = sclp_tty_receiver
 };
 
-void
+int __init
 sclp_tty_init(void)
 {
        void *page;
@@ -718,20 +720,20 @@ sclp_tty_init(void)
        int rc;
 
        if (!CONSOLE_IS_SCLP)
-               return;
+               return 0;
        rc = sclp_rw_init();
-       if (rc != 0) {
+       if (rc) {
                printk(KERN_ERR SCLP_TTY_PRINT_HEADER
                       "could not register tty - "
                       "sclp_rw_init returned %d\n", rc);
-               return;
+               return rc;
        }
        /* Allocate pages for output buffering */
        INIT_LIST_HEAD(&sclp_tty_pages);
        for (i = 0; i < MAX_KMEM_PAGES; i++) {
                page = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
                if (page == NULL)
-                       return;
+                       return -ENOMEM;
                list_add_tail((struct list_head *) page, &sclp_tty_pages);
        }
        INIT_LIST_HEAD(&sclp_tty_outqueue);
@@ -753,8 +755,9 @@ sclp_tty_init(void)
        sclp_tty_chars_count = 0;
        sclp_tty = NULL;
 
-       if (sclp_register(&sclp_input_event) != 0)
-               return;
+       rc = sclp_register(&sclp_input_event);
+       if (rc)
+               return rc;
 
        memset (&sclp_tty_driver, 0, sizeof(struct tty_driver));
        sclp_tty_driver.magic = TTY_DRIVER_MAGIC;
@@ -810,8 +813,10 @@ sclp_tty_init(void)
        sclp_tty_driver.write_proc = NULL;
 
        rc = tty_register_driver(&sclp_tty_driver);
-       if (rc != 0)
+       if (rc)
                printk(KERN_ERR SCLP_TTY_PRINT_HEADER
                       "could not register tty - "
-                      "sclp_drv_register returned %d\n", rc);
+                      "tty_register_driver returned %d\n", rc);
+       return rc;
 }
+module_init(sclp_tty_init);
index 81bfb39bd43cd59216be496172bf95c5a7e5d948..cdf583f907abe2a2a22daf6cb97280d57acafc3f 100644 (file)
@@ -12,6 +12,8 @@
 #define __SCLP_TTY_H__
 
 #include <linux/ioctl.h>
+#include <linux/termios.h>
+#include <linux/tty_driver.h>
 
 /* This is the type of data structures storing sclp ioctl setting. */
 struct sclp_ioctls {
@@ -64,4 +66,6 @@ struct sclp_ioctls {
 /* get the number of buffers/pages got from kernel at startup */
 #define TIOCSCLPGKBUF  _IOR(SCLP_IOCTL_LETTER, 20, unsigned short)
 
+extern struct tty_driver sclp_tty_driver;
+
 #endif /* __SCLP_TTY_H__ */