Commit e376bc34 authored by Heiko Carstens's avatar Heiko Carstens Committed by Linus Torvalds

[PATCH] s390: SCLP device driver cleanup

From: Peter Oberparleiter <peter.oberparleiter@de.ibm.com>

sclp: core driver cleanup

Details:
- moved signal shutdown (quiesce) handling into a separate file
- cleanup of SCLP core driver:
  . introduced driver states instead of bits
  . introduced request retry count and retry limit
  . sclp_add_request now returns an error code if a request couldn't be started
  . introduced separate request structure for init_mask requests to simplify
    code
  . request timer is now manually checked in sclp_sync_wait because timer
    interrupts are disabled in this context
  . removed busy timer - request timer now handles both cases
  . split up sclp_start_request into __sclp_start_request and sclp_process
    queue
  . removed sclp_error_message (unused)
  . introduced sclp_check_handler function to split up initial init mask
    test from standard init mask request processing
  . introduced sclp_deactivate and sclp_reactivate for simplified reboot
    event handling (and potential use in suspend/resume scenario)
  . added protection against multiple concurrent init mask calls
- minor changes in SCLP core driver:
  . updated comments
  . renamed functions to be consistent with "function name starts with __ =>
    needs lock"
  . renamed internal functions for consistency reasons
  . introduced inlined helper functions to simplify code
  . moved EXPORT_SYMBOL definitions next to function definition
- changes in sclp console driver
  . removed callback recursion to prevent stack overflow
- changes to CPI module
  . added check for sclp_add_request return code
  . changed printks to specify a message level
- changes to generic sclp tty layer
  . removed timed buffer retry after error (timers may not work in some
    situations)
  . introduced return code for sclp_emit_buffer
- changes to sclp tty driver
  . removed callback recursion
- changes to sclp vt220 driver
  . removed callback recursion
  . removed timed buffer retry after error
- modified sclp_init_mask to prevent problems with some compiler versions
Signed-off-by: default avatarHeiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent ae798b69
......@@ -11,7 +11,7 @@ obj-$(CONFIG_TN3270_FS) += fs3270.o
obj-$(CONFIG_TN3215) += con3215.o
obj-$(CONFIG_SCLP) += sclp.o sclp_rw.o
obj-$(CONFIG_SCLP) += sclp.o sclp_rw.o sclp_quiesce.o
obj-$(CONFIG_SCLP_TTY) += sclp_tty.o
obj-$(CONFIG_SCLP_CONSOLE) += sclp_con.o
obj-$(CONFIG_SCLP_VT220_TTY) += sclp_vt220.o
......
This diff is collapsed.
......@@ -95,6 +95,7 @@ struct sclp_req {
sclp_cmdw_t command; /* sclp command to execute */
void *sccb; /* pointer to the sccb to execute */
char status; /* status of this request */
int start_count; /* number of SVCs done for this req */
/* Callback that is called after reaching final status. */
void (*callback)(struct sclp_req *, void *data);
void *callback_data;
......@@ -123,12 +124,13 @@ struct sclp_register {
};
/* externals from sclp.c */
void sclp_add_request(struct sclp_req *req);
int sclp_add_request(struct sclp_req *req);
void sclp_sync_wait(void);
int sclp_register(struct sclp_register *reg);
void sclp_unregister(struct sclp_register *reg);
char *sclp_error_message(u16 response_code);
int sclp_remove_processed(struct sccb_header *sccb);
int sclp_deactivate(void);
int sclp_reactivate(void);
/* useful inlines */
......
......@@ -49,11 +49,9 @@ static void
sclp_conbuf_callback(struct sclp_buffer *buffer, int rc)
{
unsigned long flags;
struct sclp_buffer *next;
void *page;
/* Ignore return code - because console-writes aren't critical,
we do without a sophisticated error recovery mechanism. */
do {
page = sclp_unmake_buffer(buffer);
spin_lock_irqsave(&sclp_con_lock, flags);
/* Remove buffer from outqueue */
......@@ -61,13 +59,12 @@ sclp_conbuf_callback(struct sclp_buffer *buffer, int rc)
sclp_con_buffer_count--;
list_add_tail((struct list_head *) page, &sclp_con_pages);
/* Check if there is a pending buffer on the out queue. */
next = NULL;
buffer = NULL;
if (!list_empty(&sclp_con_outqueue))
next = list_entry(sclp_con_outqueue.next,
buffer = list_entry(sclp_con_outqueue.next,
struct sclp_buffer, list);
spin_unlock_irqrestore(&sclp_con_lock, flags);
if (next != NULL)
sclp_emit_buffer(next, sclp_conbuf_callback);
} while (buffer && sclp_emit_buffer(buffer, sclp_conbuf_callback));
}
static inline void
......@@ -76,6 +73,7 @@ sclp_conbuf_emit(void)
struct sclp_buffer* buffer;
unsigned long flags;
int count;
int rc;
spin_lock_irqsave(&sclp_con_lock, flags);
buffer = sclp_conbuf;
......@@ -87,8 +85,11 @@ sclp_conbuf_emit(void)
list_add_tail(&buffer->list, &sclp_con_outqueue);
count = sclp_con_buffer_count++;
spin_unlock_irqrestore(&sclp_con_lock, flags);
if (count == 0)
sclp_emit_buffer(buffer, sclp_conbuf_callback);
if (count)
return;
rc = sclp_emit_buffer(buffer, sclp_conbuf_callback);
if (rc)
sclp_conbuf_callback(buffer, rc);
}
/*
......
......@@ -196,18 +196,20 @@ cpi_module_init(void)
rc = sclp_register(&sclp_cpi_event);
if (rc) {
/* could not register sclp event. Die. */
printk("cpi: could not register to hardware console.\n");
printk(KERN_WARNING "cpi: could not register to hardware "
"console.\n");
return -EINVAL;
}
if (!(sclp_cpi_event.sclp_send_mask & EvTyp_CtlProgIdent_Mask)) {
printk("cpi: no control program identification support\n");
printk(KERN_WARNING "cpi: no control program identification "
"support\n");
sclp_unregister(&sclp_cpi_event);
return -ENOTSUPP;
}
req = cpi_prepare_req();
if (IS_ERR(req)) {
printk("cpi: couldn't allocate request\n");
printk(KERN_WARNING "cpi: couldn't allocate request\n");
sclp_unregister(&sclp_cpi_event);
return PTR_ERR(req);
}
......@@ -216,13 +218,20 @@ cpi_module_init(void)
sema_init(&sem, 0);
req->callback_data = &sem;
/* Add request to sclp queue */
sclp_add_request(req);
rc = sclp_add_request(req);
if (rc) {
printk(KERN_WARNING "cpi: could not start request\n");
cpi_free_req(req);
sclp_unregister(&sclp_cpi_event);
return rc;
}
/* make "insmod" sleep until callback arrives */
down(&sem);
rc = ((struct cpi_sccb *) req->sccb)->header.response_code;
if (rc != 0x0020) {
printk("cpi: failed with response code 0x%x\n", rc);
printk(KERN_WARNING "cpi: failed with response code 0x%x\n",
rc);
rc = -ECOMM;
} else
rc = 0;
......
/*
* drivers/s390/char/sclp_quiesce.c
* signal quiesce handler
*
* (C) Copyright IBM Corp. 1999,2004
* Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
* Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/cpumask.h>
#include <linux/smp.h>
#include <linux/init.h>
#include <asm/atomic.h>
#include <asm/ptrace.h>
#include <asm/sigp.h>
#include "sclp.h"
#ifdef CONFIG_SMP
/* Signal completion of shutdown process. All CPUs except the first to enter
* this function: go to stopped state. First CPU: wait until all other
* CPUs are in stopped or check stop state. Afterwards, load special PSW
* to indicate completion. */
static void
do_load_quiesce_psw(void * __unused)
{
static atomic_t cpuid = ATOMIC_INIT(-1);
psw_t quiesce_psw;
__u32 status;
int i;
if (atomic_compare_and_swap(-1, smp_processor_id(), &cpuid))
signal_processor(smp_processor_id(), sigp_stop);
/* Wait for all other cpus to enter stopped state */
i = 1;
while (i < NR_CPUS) {
if (!cpu_online(i)) {
i++;
continue;
}
switch (signal_processor_ps(&status, 0, i, sigp_sense)) {
case sigp_order_code_accepted:
case sigp_status_stored:
/* Check for stopped and check stop state */
if (status & 0x50)
i++;
break;
case sigp_busy:
break;
case sigp_not_operational:
i++;
break;
}
}
/* Quiesce the last cpu with the special psw */
quiesce_psw.mask = PSW_BASE_BITS | PSW_MASK_WAIT;
quiesce_psw.addr = 0xfff;
__load_psw(quiesce_psw);
}
/* Shutdown handler. Perform shutdown function on all CPUs. */
static void
do_machine_quiesce(void)
{
on_each_cpu(do_load_quiesce_psw, NULL, 0, 0);
}
#else
/* Shutdown handler. Signal completion of shutdown by loading special PSW. */
static void
do_machine_quiesce(void)
{
psw_t quiesce_psw;
quiesce_psw.mask = PSW_BASE_BITS | PSW_MASK_WAIT;
quiesce_psw.addr = 0xfff;
__load_psw(quiesce_psw);
}
#endif
extern void ctrl_alt_del(void);
/* Handler for quiesce event. Start shutdown procedure. */
static void
sclp_quiesce_handler(struct evbuf_header *evbuf)
{
_machine_restart = (void *) do_machine_quiesce;
_machine_halt = do_machine_quiesce;
_machine_power_off = do_machine_quiesce;
ctrl_alt_del();
}
static struct sclp_register sclp_quiesce_event = {
.receive_mask = EvTyp_SigQuiesce_Mask,
.receiver_fn = sclp_quiesce_handler
};
/* Initialize quiesce driver. */
static int __init
sclp_quiesce_init(void)
{
int rc;
rc = sclp_register(&sclp_quiesce_event);
if (rc)
printk(KERN_WARNING "sclp: could not register quiesce handler "
"(rc=%d)\n", rc);
return rc;
}
module_init(sclp_quiesce_init);
......@@ -54,7 +54,6 @@ sclp_make_buffer(void *page, unsigned short columns, unsigned short htab)
buffer = ((struct sclp_buffer *) ((addr_t) sccb + PAGE_SIZE)) - 1;
buffer->sccb = sccb;
buffer->retry_count = 0;
init_timer(&buffer->retry_timer);
buffer->mto_number = 0;
buffer->mto_char_sum = 0;
buffer->current_line = NULL;
......@@ -365,17 +364,7 @@ sclp_rw_init(void)
return rc;
}
static void
sclp_buffer_retry(unsigned long data)
{
struct sclp_buffer *buffer = (struct sclp_buffer *) data;
buffer->request.status = SCLP_REQ_FILLED;
buffer->sccb->header.response_code = 0x0000;
sclp_add_request(&buffer->request);
}
#define SCLP_BUFFER_MAX_RETRY 5
#define SCLP_BUFFER_RETRY_INTERVAL 2
#define SCLP_BUFFER_MAX_RETRY 1
/*
* second half of Write Event Data-function that has to be done after
......@@ -404,7 +393,7 @@ sclp_writedata_callback(struct sclp_req *request, void *data)
break;
case 0x0340: /* Contained SCLP equipment check */
if (buffer->retry_count++ > SCLP_BUFFER_MAX_RETRY) {
if (++buffer->retry_count > SCLP_BUFFER_MAX_RETRY) {
rc = -EIO;
break;
}
......@@ -413,26 +402,26 @@ sclp_writedata_callback(struct sclp_req *request, void *data)
/* not all buffers were processed */
sccb->header.response_code = 0x0000;
buffer->request.status = SCLP_REQ_FILLED;
sclp_add_request(request);
rc = sclp_add_request(request);
if (rc == 0)
return;
}
} else
rc = 0;
break;
case 0x0040: /* SCLP equipment check */
case 0x05f0: /* Target resource in improper state */
if (buffer->retry_count++ > SCLP_BUFFER_MAX_RETRY) {
if (++buffer->retry_count > SCLP_BUFFER_MAX_RETRY) {
rc = -EIO;
break;
}
/* wait some time, then retry request */
buffer->retry_timer.function = sclp_buffer_retry;
buffer->retry_timer.data = (unsigned long) buffer;
buffer->retry_timer.expires = jiffies +
SCLP_BUFFER_RETRY_INTERVAL*HZ;
add_timer(&buffer->retry_timer);
/* retry request */
sccb->header.response_code = 0x0000;
buffer->request.status = SCLP_REQ_FILLED;
rc = sclp_add_request(request);
if (rc == 0)
return;
break;
default:
if (sccb->header.response_code == 0x71f0)
rc = -ENOMEM;
......@@ -446,9 +435,10 @@ sclp_writedata_callback(struct sclp_req *request, void *data)
/*
* Setup the request structure in the struct sclp_buffer to do SCLP Write
* Event Data and pass the request to the core SCLP loop.
* Event Data and pass the request to the core SCLP loop. Return zero on
* success, non-zero otherwise.
*/
void
int
sclp_emit_buffer(struct sclp_buffer *buffer,
void (*callback)(struct sclp_buffer *, int))
{
......@@ -459,11 +449,8 @@ sclp_emit_buffer(struct sclp_buffer *buffer,
sclp_finalize_mto(buffer);
/* Are there messages in the output buffer ? */
if (buffer->mto_number == 0) {
if (callback != NULL)
callback(buffer, 0);
return;
}
if (buffer->mto_number == 0)
return -EIO;
sccb = buffer->sccb;
if (sclp_rw_event.sclp_send_mask & EvTyp_Msg_Mask)
......@@ -472,16 +459,13 @@ sclp_emit_buffer(struct sclp_buffer *buffer,
else if (sclp_rw_event.sclp_send_mask & EvTyp_PMsgCmd_Mask)
/* Use write priority message */
sccb->msg_buf.header.type = EvTyp_PMsgCmd;
else {
if (callback != NULL)
callback(buffer, -ENOSYS);
return;
}
else
return -ENOSYS;
buffer->request.command = SCLP_CMDW_WRITEDATA;
buffer->request.status = SCLP_REQ_FILLED;
buffer->request.callback = sclp_writedata_callback;
buffer->request.callback_data = buffer;
buffer->request.sccb = sccb;
buffer->callback = callback;
sclp_add_request(&buffer->request);
return sclp_add_request(&buffer->request);
}
......@@ -12,7 +12,6 @@
#define __SCLP_RW_H__
#include <linux/list.h>
#include <linux/timer.h>
struct mto {
u16 length;
......@@ -74,7 +73,6 @@ struct sclp_buffer {
char *current_line;
int current_length;
int retry_count;
struct timer_list retry_timer;
/* output format settings */
unsigned short columns;
unsigned short htab;
......@@ -90,7 +88,7 @@ struct sclp_buffer *sclp_make_buffer(void *, unsigned short, unsigned short);
void *sclp_unmake_buffer(struct sclp_buffer *);
int sclp_buffer_space(struct sclp_buffer *);
int sclp_write(struct sclp_buffer *buffer, const unsigned char *, int);
void sclp_emit_buffer(struct sclp_buffer *,void (*)(struct sclp_buffer *,int));
int sclp_emit_buffer(struct sclp_buffer *,void (*)(struct sclp_buffer *,int));
void sclp_set_columns(struct sclp_buffer *, unsigned short);
void sclp_set_htab(struct sclp_buffer *, unsigned short);
int sclp_chars_in_buffer(struct sclp_buffer *);
......
......@@ -255,11 +255,9 @@ static void
sclp_ttybuf_callback(struct sclp_buffer *buffer, int rc)
{
unsigned long flags;
struct sclp_buffer *next;
void *page;
/* Ignore return code - because tty-writes aren't critical,
we do without a sophisticated error recovery mechanism. */
do {
page = sclp_unmake_buffer(buffer);
spin_lock_irqsave(&sclp_tty_lock, flags);
/* Remove buffer from outqueue */
......@@ -267,13 +265,12 @@ sclp_ttybuf_callback(struct sclp_buffer *buffer, int rc)
sclp_tty_buffer_count--;
list_add_tail((struct list_head *) page, &sclp_tty_pages);
/* Check if there is a pending buffer on the out queue. */
next = NULL;
buffer = NULL;
if (!list_empty(&sclp_tty_outqueue))
next = list_entry(sclp_tty_outqueue.next,
buffer = list_entry(sclp_tty_outqueue.next,
struct sclp_buffer, list);
spin_unlock_irqrestore(&sclp_tty_lock, flags);
if (next != NULL)
sclp_emit_buffer(next, sclp_ttybuf_callback);
} while (buffer && sclp_emit_buffer(buffer, sclp_ttybuf_callback));
wake_up(&sclp_tty_waitq);
/* check if the tty needs a wake up call */
if (sclp_tty != NULL) {
......@@ -286,14 +283,17 @@ __sclp_ttybuf_emit(struct sclp_buffer *buffer)
{
unsigned long flags;
int count;
int rc;
spin_lock_irqsave(&sclp_tty_lock, flags);
list_add_tail(&buffer->list, &sclp_tty_outqueue);
count = sclp_tty_buffer_count++;
spin_unlock_irqrestore(&sclp_tty_lock, flags);
if (count == 0)
sclp_emit_buffer(buffer, sclp_ttybuf_callback);
if (count)
return;
rc = sclp_emit_buffer(buffer, sclp_ttybuf_callback);
if (rc)
sclp_ttybuf_callback(buffer, rc);
}
/*
......
......@@ -42,7 +42,6 @@ struct sclp_vt220_request {
struct list_head list;
struct sclp_req sclp_req;
int retry_count;
struct timer_list retry_timer;
};
/* VT220 SCCB */
......@@ -96,7 +95,7 @@ static int sclp_vt220_initialized = 0;
static int sclp_vt220_flush_later;
static void sclp_vt220_receiver_fn(struct evbuf_header *evbuf);
static void __sclp_vt220_emit(struct sclp_vt220_request *request);
static int __sclp_vt220_emit(struct sclp_vt220_request *request);
static void sclp_vt220_emit_current(void);
/* Registration structure for our interest in SCLP event buffers */
......@@ -116,9 +115,9 @@ static void
sclp_vt220_process_queue(struct sclp_vt220_request *request)
{
unsigned long flags;
struct sclp_vt220_request *next;
void *page;
do {
/* Put buffer back to list of empty buffers */
page = request->sclp_req.sccb;
spin_lock_irqsave(&sclp_vt220_lock, flags);
......@@ -127,14 +126,13 @@ sclp_vt220_process_queue(struct sclp_vt220_request *request)
sclp_vt220_outqueue_count--;
list_add_tail((struct list_head *) page, &sclp_vt220_empty);
/* Check if there is a pending buffer on the out queue. */
next = NULL;
request = NULL;
if (!list_empty(&sclp_vt220_outqueue))
next = list_entry(sclp_vt220_outqueue.next,
request = list_entry(sclp_vt220_outqueue.next,
struct sclp_vt220_request, list);
spin_unlock_irqrestore(&sclp_vt220_lock, flags);
if (next != NULL)
__sclp_vt220_emit(next);
else if (sclp_vt220_flush_later)
} 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 */
......@@ -143,25 +141,7 @@ sclp_vt220_process_queue(struct sclp_vt220_request *request)
}
}
/*
* Retry sclp write request after waiting some time for an sclp equipment
* check to pass.
*/
static void
sclp_vt220_retry(unsigned long data)
{
struct sclp_vt220_request *request;
struct sclp_vt220_sccb *sccb;
request = (struct sclp_vt220_request *) data;
request->sclp_req.status = SCLP_REQ_FILLED;
sccb = (struct sclp_vt220_sccb *) request->sclp_req.sccb;
sccb->header.response_code = 0x0000;
sclp_add_request(&request->sclp_req);
}
#define SCLP_BUFFER_MAX_RETRY 5
#define SCLP_BUFFER_RETRY_INTERVAL 2
#define SCLP_BUFFER_MAX_RETRY 1
/*
* Callback through which the result of a write request is reported by the
......@@ -189,29 +169,26 @@ sclp_vt220_callback(struct sclp_req *request, void *data)
break;
case 0x0340: /* Contained SCLP equipment check */
if (vt220_request->retry_count++ > SCLP_BUFFER_MAX_RETRY)
if (++vt220_request->retry_count > SCLP_BUFFER_MAX_RETRY)
break;
/* Remove processed buffers and requeue rest */
if (sclp_remove_processed((struct sccb_header *) sccb) > 0) {
/* Not all buffers were processed */
sccb->header.response_code = 0x0000;
vt220_request->sclp_req.status = SCLP_REQ_FILLED;
sclp_add_request(request);
if (sclp_add_request(request) == 0)
return;
}
break;
case 0x0040: /* SCLP equipment check */
if (vt220_request->retry_count++ > SCLP_BUFFER_MAX_RETRY)
if (++vt220_request->retry_count > SCLP_BUFFER_MAX_RETRY)
break;
/* Wait some time, then retry request */
vt220_request->retry_timer.function = sclp_vt220_retry;
vt220_request->retry_timer.data =
(unsigned long) vt220_request;
vt220_request->retry_timer.expires =
jiffies + SCLP_BUFFER_RETRY_INTERVAL*HZ;
add_timer(&vt220_request->retry_timer);
sccb->header.response_code = 0x0000;
vt220_request->sclp_req.status = SCLP_REQ_FILLED;
if (sclp_add_request(request) == 0)
return;
break;
default:
break;
......@@ -220,22 +197,22 @@ sclp_vt220_callback(struct sclp_req *request, void *data)
}
/*
* Emit vt220 request buffer to SCLP.
* Emit vt220 request buffer to SCLP. Return zero on success, non-zero
* otherwise.
*/
static void
static int
__sclp_vt220_emit(struct sclp_vt220_request *request)
{
if (!(sclp_vt220_register.sclp_send_mask & EvTyp_VT220Msg_Mask)) {
request->sclp_req.status = SCLP_REQ_FAILED;
sclp_vt220_callback(&request->sclp_req, (void *) request);
return;
return -EIO;
}
request->sclp_req.command = SCLP_CMDW_WRITEDATA;
request->sclp_req.status = SCLP_REQ_FILLED;
request->sclp_req.callback = sclp_vt220_callback;
request->sclp_req.callback_data = (void *) request;
sclp_add_request(&request->sclp_req);
return sclp_add_request(&request->sclp_req);
}
/*
......@@ -253,12 +230,12 @@ sclp_vt220_emit(struct sclp_vt220_request *request)
spin_unlock_irqrestore(&sclp_vt220_lock, flags);
/* Emit only the first buffer immediately - callback takes care of
* the rest */
if (count == 0)
__sclp_vt220_emit(request);
if (count == 0 && __sclp_vt220_emit(request))
sclp_vt220_process_queue(request);
}
/*
* Queue and emit current request.
* Queue and emit current request. Return zero on success, non-zero otherwise.
*/
static void
sclp_vt220_emit_current(void)
......@@ -300,7 +277,6 @@ sclp_vt220_initialize_page(void *page)
/* Place request structure at end of page */
request = ((struct sclp_vt220_request *)
((addr_t) page + PAGE_SIZE)) - 1;
init_timer(&request->retry_timer);
request->retry_count = 0;
request->sclp_req.sccb = page;
/* SCCB goes at start of page */
......
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