Commit ed0db08d authored by Martin Schwidefsky's avatar Martin Schwidefsky Committed by Linus Torvalds

[PATCH] s390: console device drivers.

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