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);
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
......
......@@ -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);
......
......@@ -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);
......
......@@ -15,9 +15,11 @@
#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);
......
......@@ -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);
......@@ -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__ */
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