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

[PATCH] s390: sclp driver.

Add error recovery for sclp failures. Fix endless loop in sclp_process_evbufs.
parent a50082c6
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
# S/390 character devices # S/390 character devices
# #
export-objs := sclp_rw.o tape_core.o tape_devmap.o tape_std.o export-objs := sclp.o tape_core.o tape_devmap.o tape_std.o
tub3270-objs := tuball.o tubfs.o tubtty.o \ tub3270-objs := tuball.o tubfs.o tubtty.o \
tubttyaid.o tubttybld.o tubttyscl.o \ tubttyaid.o tubttybld.o tubttyscl.o \
...@@ -10,8 +10,8 @@ tub3270-objs := tuball.o tubfs.o tubtty.o \ ...@@ -10,8 +10,8 @@ tub3270-objs := tuball.o tubfs.o tubtty.o \
obj-y += ctrlchar.o obj-y += ctrlchar.o
obj-$(CONFIG_TN3215) += con3215.o obj-$(CONFIG_TN3215) += con3215.o
obj-$(CONFIG_SCLP) += sclp.o obj-$(CONFIG_SCLP) += sclp.o sclp_rw.o
obj-$(CONFIG_SCLP_TTY) += sclp_rw.o sclp_tty.o obj-$(CONFIG_SCLP_TTY) += sclp_tty.o
obj-$(CONFIG_SCLP_CONSOLE) += sclp_con.o obj-$(CONFIG_SCLP_CONSOLE) += sclp_con.o
obj-$(CONFIG_SCLP_CPI) += sclp_cpi.o obj-$(CONFIG_SCLP_CPI) += sclp_cpi.o
obj-$(CONFIG_TN3270) += tub3270.o obj-$(CONFIG_TN3270) += tub3270.o
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include <linux/config.h> #include <linux/config.h>
#include <linux/version.h> #include <linux/version.h>
#include <linux/module.h>
#include <linux/kmod.h> #include <linux/kmod.h>
#include <linux/bootmem.h> #include <linux/bootmem.h>
#include <linux/err.h> #include <linux/err.h>
...@@ -17,6 +18,7 @@ ...@@ -17,6 +18,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/timer.h>
#include <asm/s390_ext.h> #include <asm/s390_ext.h>
#include "sclp.h" #include "sclp.h"
...@@ -29,7 +31,7 @@ ...@@ -29,7 +31,7 @@
* different behaviour); otherwise we use the default * different behaviour); otherwise we use the default
* settings in sclp_data.init_ioctls * settings in sclp_data.init_ioctls
*/ */
#define USE_VM_DETECTION #define USE_VM_DETECTION
/* Structure for register_early_external_interrupt. */ /* Structure for register_early_external_interrupt. */
static ext_int_info_t ext_int_info_hwc; static ext_int_info_t ext_int_info_hwc;
...@@ -53,12 +55,21 @@ static char sclp_read_sccb[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE))); ...@@ -53,12 +55,21 @@ static char sclp_read_sccb[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE)));
/* sccb for write mask sccb */ /* sccb for write mask sccb */
static char sclp_init_sccb[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE))); 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 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
#define SCLP_READING 2 #define SCLP_READING 2
#define SCLP_INIT_POLL_INTERVAL 1
#define SCLP_COMMAND_INITIATED 0
#define SCLP_BUSY 2
#define SCLP_NOT_OPERATIONAL 3
/* /*
* assembler instruction for Service Call * assembler instruction for Service Call
*/ */
...@@ -86,7 +97,7 @@ __service_call(sclp_cmdw_t command, void *sccb) ...@@ -86,7 +97,7 @@ __service_call(sclp_cmdw_t command, void *sccb)
* new SCCB unchanged * new SCCB unchanged
* cc == 3: SCLP function not operational * cc == 3: SCLP function not operational
*/ */
if (cc == 3) if (cc == SCLP_NOT_OPERATIONAL)
return -EIO; return -EIO;
/* /*
* We set the SCLP_RUNNING bit for cc 2 as well because if * We set the SCLP_RUNNING bit for cc 2 as well because if
...@@ -94,7 +105,7 @@ __service_call(sclp_cmdw_t command, void *sccb) ...@@ -94,7 +105,7 @@ __service_call(sclp_cmdw_t command, void *sccb)
* that has to complete first * that has to complete first
*/ */
set_bit(SCLP_RUNNING, &sclp_status); set_bit(SCLP_RUNNING, &sclp_status);
if (cc == 2) if (cc == SCLP_BUSY)
return -EBUSY; return -EBUSY;
return 0; return 0;
} }
...@@ -130,6 +141,7 @@ __sclp_start_request(void) ...@@ -130,6 +141,7 @@ __sclp_start_request(void)
static int static int
sclp_process_evbufs(struct sccb_header *sccb) sclp_process_evbufs(struct sccb_header *sccb)
{ {
int result;
unsigned long flags; unsigned long flags;
struct evbuf_header *evbuf; struct evbuf_header *evbuf;
struct list_head *l; struct list_head *l;
...@@ -137,8 +149,10 @@ sclp_process_evbufs(struct sccb_header *sccb) ...@@ -137,8 +149,10 @@ sclp_process_evbufs(struct sccb_header *sccb)
spin_lock_irqsave(&sclp_lock, flags); spin_lock_irqsave(&sclp_lock, flags);
evbuf = (struct evbuf_header *) (sccb + 1); evbuf = (struct evbuf_header *) (sccb + 1);
while ((void *) evbuf < (void *) sccb + sccb->length) { result = 0;
while ((addr_t) evbuf < (addr_t) sccb + sccb->length) {
/* check registered event */ /* check registered event */
t = NULL;
list_for_each(l, &sclp_reg_list) { list_for_each(l, &sclp_reg_list) {
t = list_entry(l, struct sclp_register, list); t = list_entry(l, struct sclp_register, list);
if (t->receive_mask & (1 << (32 - evbuf->type))) { if (t->receive_mask & (1 << (32 - evbuf->type))) {
...@@ -146,56 +160,72 @@ sclp_process_evbufs(struct sccb_header *sccb) ...@@ -146,56 +160,72 @@ sclp_process_evbufs(struct sccb_header *sccb)
t->receiver_fn(evbuf); t->receiver_fn(evbuf);
break; break;
} }
else
t = NULL;
} }
/* Check for unrequested event buffer */
if (t == NULL)
result = -ENOSYS;
evbuf = (struct evbuf_header *)
((addr_t) evbuf + evbuf->length);
} }
spin_unlock_irqrestore(&sclp_lock, flags); spin_unlock_irqrestore(&sclp_lock, flags);
return -ENOSYS; return result;
} }
/* char *
* postprocessing of unconditional read service call sclp_error_message(u16 rc)
*/
static void
sclp_unconditional_read_cb(struct sclp_req *read_req, void *data)
{ {
static struct { static struct {
u16 code; char *msg; u16 code; char *msg;
} errors[] = { } sclp_errors[] = {
{ 0x0000, "No response code stored (machine malfunction)" },
{ 0x0020, "Normal Completion" },
{ 0x0040, "SCLP equipment check" }, { 0x0040, "SCLP equipment check" },
{ 0x0100, "SCCB boundary violation" }, { 0x0100, "SCCB boundary violation" },
{ 0x01f0, "invalid command" }, { 0x01f0, "Invalid command" },
{ 0x0300, "insufficient SCCB length" }, { 0x0220, "Normal Completion; suppressed buffers pending" },
{ 0x40f0, "invalid function code" }, { 0x0300, "Insufficient SCCB length" },
{ 0x60f0, "got interrupt but no data found" }, { 0x0340, "Contained SCLP equipment check" },
{ 0x62f0, "got interrupt but no data found" }, { 0x05f0, "Target resource in improper state" },
{ 0x70f0, "invalid selection mask" } { 0x40f0, "Invalid function code/not installed" },
{ 0x60f0, "No buffers stored" },
{ 0x62f0, "No buffers stored; suppressed buffers pending" },
{ 0x70f0, "Invalid selection mask" },
{ 0x71f0, "Event buffer exceeds available space" },
{ 0x72f0, "Inconsistent lengths" },
{ 0x73f0, "Event buffer syntax error" }
}; };
struct sccb_header *sccb;
char *errmsg;
int i; int i;
for (i = 0; i < sizeof(sclp_errors)/sizeof(sclp_errors[0]); i++)
if (rc == sclp_errors[i].code)
return sclp_errors[i].msg;
return "Invalid response code";
}
/*
* postprocessing of unconditional read service call
*/
static void
sclp_unconditional_read_cb(struct sclp_req *read_req, void *data)
{
struct sccb_header *sccb;
sccb = read_req->sccb; sccb = read_req->sccb;
if (sccb->response_code == 0x0020 || if (sccb->response_code == 0x0020 ||
sccb->response_code == 0x0220) { sccb->response_code == 0x0220) {
/* normal read completion, event buffers stored in sccb */ if (sclp_process_evbufs(sccb) != 0)
if (sclp_process_evbufs(sccb) == 0) { printk(KERN_WARNING SCLP_CORE_PRINT_HEADER
clear_bit(SCLP_READING, &sclp_status); "unconditional read: "
return; "unrequested event buffer received.\n");
}
errmsg = "invalid response code";
} else {
errmsg = NULL;
for (i = 0; i < sizeof(errors)/sizeof(errors[0]); i++)
if (sccb->response_code == errors[i].code) {
errmsg = errors[i].msg;
break;
}
if (errmsg == NULL)
errmsg = "invalid response code";
} }
printk(KERN_WARNING SCLP_CORE_PRINT_HEADER
"unconditional read: %s (response code =0x%x).\n", if (sccb->response_code != 0x0020)
errmsg, sccb->response_code); printk(KERN_WARNING SCLP_CORE_PRINT_HEADER
"unconditional read: %s (response code=0x%x).\n",
sclp_error_message(sccb->response_code),
sccb->response_code);
clear_bit(SCLP_READING, &sclp_status); clear_bit(SCLP_READING, &sclp_status);
} }
...@@ -209,7 +239,7 @@ __sclp_unconditional_read(void) ...@@ -209,7 +239,7 @@ __sclp_unconditional_read(void)
struct sclp_req *read_req; struct sclp_req *read_req;
/* /*
* Dont try to initiate Unconditional Read if we are not able to * Don't try to initiate Unconditional Read if we are not able to
* receive anything * receive anything
*/ */
if (sclp_receive_mask == 0) if (sclp_receive_mask == 0)
...@@ -217,13 +247,13 @@ __sclp_unconditional_read(void) ...@@ -217,13 +247,13 @@ __sclp_unconditional_read(void)
/* Don't try reading if a read is already outstanding */ /* Don't try reading if a read is already outstanding */
if (test_and_set_bit(SCLP_READING, &sclp_status)) if (test_and_set_bit(SCLP_READING, &sclp_status))
return; return;
/* Initialise read sccb */ /* Initialize read sccb */
sccb = (struct sccb_header *) sclp_read_sccb; sccb = (struct sccb_header *) sclp_read_sccb;
clear_page(sccb); clear_page(sccb);
sccb->length = PAGE_SIZE; sccb->length = PAGE_SIZE;
sccb->function_code = 0; /* unconditional read */ sccb->function_code = 0; /* unconditional read */
sccb->control_mask[2] = 0x80; /* variable length response */ sccb->control_mask[2] = 0x80; /* variable length response */
/* stick the request structure to the end of the page */ /* Initialize request structure */
read_req = &sclp_read_req; read_req = &sclp_read_req;
read_req->command = SCLP_CMDW_READDATA; read_req->command = SCLP_CMDW_READDATA;
read_req->status = SCLP_REQ_QUEUED; read_req->status = SCLP_REQ_QUEUED;
...@@ -233,6 +263,11 @@ __sclp_unconditional_read(void) ...@@ -233,6 +263,11 @@ __sclp_unconditional_read(void)
list_add(&read_req->list, &sclp_req_queue); list_add(&read_req->list, &sclp_req_queue);
} }
/* Bit masks to interpret external interruption parameter contents. */
#define EXT_INT_SCCB_MASK 0xfffffff8
#define EXT_INT_STATECHANGE_PENDING 0x00000002
#define EXT_INT_EVBUF_PENDING 0x00000001
/* /*
* Handler for service-signal external interruptions * Handler for service-signal external interruptions
*/ */
...@@ -242,15 +277,14 @@ sclp_interrupt_handler(struct pt_regs *regs, __u16 code) ...@@ -242,15 +277,14 @@ sclp_interrupt_handler(struct pt_regs *regs, __u16 code)
u32 ext_int_param, finished_sccb, evbuf_pending; u32 ext_int_param, finished_sccb, evbuf_pending;
struct list_head *l; struct list_head *l;
struct sclp_req *req, *tmp; struct sclp_req *req, *tmp;
int cpu;
cpu = smp_processor_id();
ext_int_param = S390_lowcore.ext_params; ext_int_param = S390_lowcore.ext_params;
finished_sccb = ext_int_param & -8U; finished_sccb = ext_int_param & EXT_INT_SCCB_MASK;
evbuf_pending = ext_int_param & 1; evbuf_pending = ext_int_param & (EXT_INT_EVBUF_PENDING |
EXT_INT_STATECHANGE_PENDING);
irq_enter(); irq_enter();
/* /*
* Only do request callsbacks if sclp is initialised. * Only do request callbacks 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.
*/ */
...@@ -326,8 +360,8 @@ sclp_sync_wait(void) ...@@ -326,8 +360,8 @@ sclp_sync_wait(void)
} }
/* /*
* Queue a request to the tail of the ccw_queue. Start the I/O if * Queue an SCLP request. Request will immediately be processed if queue is
* possible. * empty.
*/ */
void void
sclp_add_request(struct sclp_req *req) sclp_add_request(struct sclp_req *req)
...@@ -391,17 +425,17 @@ sclp_state_change(struct evbuf_header *evbuf) ...@@ -391,17 +425,17 @@ sclp_state_change(struct evbuf_header *evbuf)
scbuf = (struct sclp_statechangebuf *) evbuf; scbuf = (struct sclp_statechangebuf *) evbuf;
if (scbuf->validity_sclp_receive_mask) { if (scbuf->validity_sclp_receive_mask) {
if (scbuf->mask_length != 4) if (scbuf->mask_length != sizeof(sccb_mask_t))
printk(KERN_WARNING SCLP_CORE_PRINT_HEADER printk(KERN_WARNING SCLP_CORE_PRINT_HEADER
"state change event with mask length %i\n", "state change event with mask length %i\n",
scbuf->mask_length); scbuf->mask_length);
else else
/* set new send mask */ /* set new receive mask */
sclp_receive_mask = scbuf->sclp_receive_mask; sclp_receive_mask = scbuf->sclp_receive_mask;
} }
if (scbuf->validity_sclp_send_mask) { if (scbuf->validity_sclp_send_mask) {
if (scbuf->mask_length != 4) if (scbuf->mask_length != sizeof(sccb_mask_t))
printk(KERN_WARNING SCLP_CORE_PRINT_HEADER printk(KERN_WARNING SCLP_CORE_PRINT_HEADER
"state change event with mask length %i\n", "state change event with mask length %i\n",
scbuf->mask_length); scbuf->mask_length);
...@@ -496,6 +530,8 @@ struct init_sccb { ...@@ -496,6 +530,8 @@ struct init_sccb {
sccb_mask_t sclp_receive_mask; sccb_mask_t sclp_receive_mask;
} __attribute__((packed)); } __attribute__((packed));
static void sclp_init_mask_retry(unsigned long);
static int static int
sclp_init_mask(void) sclp_init_mask(void)
{ {
...@@ -557,13 +593,34 @@ sclp_init_mask(void) ...@@ -557,13 +593,34 @@ sclp_init_mask(void)
spin_lock_irqsave(&sclp_lock, flags); spin_lock_irqsave(&sclp_lock, flags);
} while (rc == -EBUSY); } while (rc == -EBUSY);
} }
sclp_receive_mask = sccb->sclp_receive_mask; if (sccb->header.response_code != 0x0020) {
sclp_send_mask = sccb->sclp_send_mask; /* WRITEMASK failed - we cannot rely on receiving a state
__sclp_notify_state_change(); change event, so initially, polling is the only alternative
for us to ever become operational. */
if (!timer_pending(&retry_timer) ||
!mod_timer(&retry_timer,
jiffies + SCLP_INIT_POLL_INTERVAL*HZ)) {
retry_timer.function = sclp_init_mask_retry;
retry_timer.data = 0;
retry_timer.expires = jiffies +
SCLP_INIT_POLL_INTERVAL*HZ;
add_timer(&retry_timer);
}
} else {
sclp_receive_mask = sccb->sclp_receive_mask;
sclp_send_mask = sccb->sclp_send_mask;
__sclp_notify_state_change();
}
spin_unlock_irqrestore(&sclp_lock, flags); spin_unlock_irqrestore(&sclp_lock, flags);
return 0; return 0;
} }
static void
sclp_init_mask_retry(unsigned long data)
{
sclp_init_mask();
}
/* /*
* sclp setup function. Called early (no kmalloc!) from sclp_console_init(). * sclp setup function. Called early (no kmalloc!) from sclp_console_init().
*/ */
...@@ -573,7 +630,7 @@ sclp_init(void) ...@@ -573,7 +630,7 @@ sclp_init(void)
int rc; int rc;
if (test_bit(SCLP_INIT, &sclp_status)) if (test_bit(SCLP_INIT, &sclp_status))
/* Already initialised. */ /* Already initialized. */
return 0; return 0;
/* /*
...@@ -600,6 +657,7 @@ sclp_init(void) ...@@ -600,6 +657,7 @@ sclp_init(void)
*/ */
ctl_set_bit(0, 9); ctl_set_bit(0, 9);
init_timer(&retry_timer);
/* do the initial write event mask */ /* do the initial write event mask */
rc = sclp_init_mask(); rc = sclp_init_mask();
if (rc == 0) { if (rc == 0) {
...@@ -616,6 +674,15 @@ sclp_init(void) ...@@ -616,6 +674,15 @@ sclp_init(void)
return rc; return rc;
} }
/*
* Register the SCLP event listener identified by REG. Return 0 on success.
* Some error codes and their meaning:
*
* -ENODEV = SCLP interface is not supported on this machine
* -EBUSY = there is already a listener registered for the requested
* event type
* -EIO = SCLP interface is currently not operational
*/
int int
sclp_register(struct sclp_register *reg) sclp_register(struct sclp_register *reg)
{ {
...@@ -650,6 +717,9 @@ sclp_register(struct sclp_register *reg) ...@@ -650,6 +717,9 @@ sclp_register(struct sclp_register *reg)
return 0; return 0;
} }
/*
* Unregister the SCLP event listener identified by REG.
*/
void void
sclp_unregister(struct sclp_register *reg) sclp_unregister(struct sclp_register *reg)
{ {
...@@ -661,3 +731,7 @@ sclp_unregister(struct sclp_register *reg) ...@@ -661,3 +731,7 @@ sclp_unregister(struct sclp_register *reg)
sclp_init_mask(); sclp_init_mask();
} }
EXPORT_SYMBOL(sclp_add_request);
EXPORT_SYMBOL(sclp_sync_wait);
EXPORT_SYMBOL(sclp_register);
EXPORT_SYMBOL(sclp_unregister);
...@@ -45,10 +45,6 @@ ...@@ -45,10 +45,6 @@
#define LnTpFlgs_EndText 0x1000 #define LnTpFlgs_EndText 0x1000
#define LnTpFlgs_PromptText 0x0800 #define LnTpFlgs_PromptText 0x0800
#define SCLP_COMMAND_INITIATED 0
#define SCLP_BUSY 2
#define SCLP_NOT_OPERATIONAL 3
typedef unsigned int sclp_cmdw_t; typedef unsigned int sclp_cmdw_t;
#define SCLP_CMDW_READDATA 0x00770005 #define SCLP_CMDW_READDATA 0x00770005
...@@ -129,6 +125,7 @@ void sclp_add_request(struct sclp_req *req); ...@@ -129,6 +125,7 @@ void sclp_add_request(struct sclp_req *req);
void sclp_sync_wait(void); void sclp_sync_wait(void);
int sclp_register(struct sclp_register *reg); int sclp_register(struct sclp_register *reg);
void sclp_unregister(struct sclp_register *reg); void sclp_unregister(struct sclp_register *reg);
char *sclp_error_message(u16 response_code);
/* useful inlines */ /* useful inlines */
......
...@@ -51,13 +51,14 @@ sclp_conbuf_callback(struct sclp_buffer *buffer, int rc) ...@@ -51,13 +51,14 @@ sclp_conbuf_callback(struct sclp_buffer *buffer, int rc)
struct sclp_buffer *next; struct sclp_buffer *next;
void *page; void *page;
/* FIXME: what if rc != 0x0020 */ /* Ignore return code - because console-writes aren't critical,
we do without a sophisticated error recovery mechanism. */
page = sclp_unmake_buffer(buffer); page = sclp_unmake_buffer(buffer);
spin_lock_irqsave(&sclp_con_lock, flags); spin_lock_irqsave(&sclp_con_lock, flags);
list_add_tail((struct list_head *) page, &sclp_con_pages);
sclp_con_buffer_count--;
/* Remove buffer from outqueue */ /* Remove buffer from outqueue */
list_del(&buffer->list); list_del(&buffer->list);
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. */ /* Check if there is a pending buffer on the out queue. */
next = NULL; next = NULL;
if (!list_empty(&sclp_con_outqueue)) if (!list_empty(&sclp_con_outqueue))
...@@ -104,7 +105,7 @@ sclp_console_write(struct console *console, const char *message, ...@@ -104,7 +105,7 @@ sclp_console_write(struct console *console, const char *message,
void *page; void *page;
int written; int written;
if (count <= 0) if (count == 0)
return; return;
spin_lock_irqsave(&sclp_con_lock, flags); spin_lock_irqsave(&sclp_con_lock, flags);
/* /*
...@@ -125,7 +126,8 @@ sclp_console_write(struct console *console, const char *message, ...@@ -125,7 +126,8 @@ sclp_console_write(struct console *console, const char *message,
sclp_con_width_htab); sclp_con_width_htab);
} }
/* try to write the string to the current output buffer */ /* try to write the string to the current output buffer */
written = sclp_write(sclp_conbuf, message, count, 0); written = sclp_write(sclp_conbuf, (const unsigned char *)
message, count, 0);
if (written == -EFAULT || written == count) if (written == -EFAULT || written == count)
break; break;
/* /*
...@@ -232,6 +234,6 @@ sclp_console_init(void) ...@@ -232,6 +234,6 @@ sclp_console_init(void)
sclp_con_columns = 80; sclp_con_columns = 80;
sclp_con_width_htab = 8; sclp_con_width_htab = 8;
/* enable printks access to this driver */ /* enable printk-access to this driver */
register_console(&sclp_console); register_console(&sclp_console);
} }
...@@ -130,7 +130,7 @@ cpi_prepare_req(void) ...@@ -130,7 +130,7 @@ cpi_prepare_req(void)
req = (struct sclp_req *) kmalloc(sizeof(struct sclp_req), GFP_KERNEL); req = (struct sclp_req *) kmalloc(sizeof(struct sclp_req), GFP_KERNEL);
if (req == NULL) if (req == NULL)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
sccb = (struct cpi_sccb *) get_free_page(GFP_KERNEL); sccb = (struct cpi_sccb *) __get_free_page(GFP_KERNEL);
if (sccb == NULL) { if (sccb == NULL) {
kfree(req); kfree(req);
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
...@@ -227,6 +227,7 @@ cpi_module_init(void) ...@@ -227,6 +227,7 @@ cpi_module_init(void)
rc = 0; rc = 0;
cpi_free_req(req); cpi_free_req(req);
sclp_unregister(&sclp_cpi_event);
return rc; return rc;
} }
......
...@@ -54,6 +54,8 @@ sclp_make_buffer(void *page, unsigned short columns, unsigned short htab) ...@@ -54,6 +54,8 @@ sclp_make_buffer(void *page, unsigned short columns, unsigned short htab)
*/ */
buffer = ((struct sclp_buffer *) ((addr_t) sccb + PAGE_SIZE)) - 1; buffer = ((struct sclp_buffer *) ((addr_t) sccb + PAGE_SIZE)) - 1;
buffer->sccb = sccb; buffer->sccb = sccb;
buffer->retry_count = 0;
init_timer(&buffer->retry_timer);
buffer->mto_number = 0; buffer->mto_number = 0;
buffer->mto_char_sum = 0; buffer->mto_char_sum = 0;
buffer->current_line = NULL; buffer->current_line = NULL;
...@@ -87,14 +89,11 @@ sclp_unmake_buffer(struct sclp_buffer *buffer) ...@@ -87,14 +89,11 @@ sclp_unmake_buffer(struct sclp_buffer *buffer)
} }
/* /*
* Creates a new Message Text Object with enough room for str_len * Initialize a new Message Text Object (MTO) at the end of the provided buffer
* characters. This function will create a new sccb for buffering * with enough room for max_len characters. Return 0 on success.
* the mto if the current buffer sccb is full. A full buffer sccb
* is submitted for output.
* Returns a pointer to the start of the string in the mto.
*/ */
static int static int
sclp_create_mto(struct sclp_buffer *buffer, int max_len) sclp_initialize_mto(struct sclp_buffer *buffer, int max_len)
{ {
struct write_sccb *sccb; struct write_sccb *sccb;
struct mto *mto; struct mto *mto;
...@@ -109,7 +108,7 @@ sclp_create_mto(struct sclp_buffer *buffer, int max_len) ...@@ -109,7 +108,7 @@ sclp_create_mto(struct sclp_buffer *buffer, int max_len)
return -ENOMEM; return -ENOMEM;
/* find address of new message text object */ /* find address of new message text object */
mto = (struct mto *)(((unsigned long) sccb) + sccb->header.length); mto = (struct mto *)(((addr_t) sccb) + sccb->header.length);
/* /*
* fill the new Message-Text Object, * fill the new Message-Text Object,
...@@ -128,11 +127,11 @@ sclp_create_mto(struct sclp_buffer *buffer, int max_len) ...@@ -128,11 +127,11 @@ sclp_create_mto(struct sclp_buffer *buffer, int max_len)
} }
/* /*
* Add the mto created with sclp_create_mto to the buffer sccb using * Finalize MTO initialized by sclp_initialize_mto(), updating the sizes of
* the str_len parameter as the real length of the string. * MTO, enclosing MDB, event buffer and SCCB.
*/ */
static void static void
sclp_add_mto(struct sclp_buffer *buffer) sclp_finalize_mto(struct sclp_buffer *buffer)
{ {
struct write_sccb *sccb; struct write_sccb *sccb;
struct mto *mto; struct mto *mto;
...@@ -147,7 +146,7 @@ sclp_add_mto(struct sclp_buffer *buffer) ...@@ -147,7 +146,7 @@ sclp_add_mto(struct sclp_buffer *buffer)
/* find address of new message text object */ /* find address of new message text object */
sccb = buffer->sccb; sccb = buffer->sccb;
mto = (struct mto *)(((unsigned long) sccb) + sccb->header.length); mto = (struct mto *)(((addr_t) sccb) + sccb->header.length);
/* set size of message text object */ /* set size of message text object */
mto->length = mto_size; mto->length = mto_size;
...@@ -169,23 +168,6 @@ sclp_add_mto(struct sclp_buffer *buffer) ...@@ -169,23 +168,6 @@ sclp_add_mto(struct sclp_buffer *buffer)
buffer->mto_char_sum += str_len; buffer->mto_char_sum += str_len;
} }
void
sclp_move_current_line(struct sclp_buffer *to, struct sclp_buffer *from)
{
if (from->current_line == NULL)
return;
if (sclp_create_mto(to, from->columns) != 0)
return;
if (from->current_length > 0) {
memcpy(to->current_line,
from->current_line - from->current_length,
from->current_length);
to->current_line += from->current_length;
to->current_length = from->current_length;
}
from->current_line = NULL;
}
/* /*
* processing of a message including escape characters, * processing of a message including escape characters,
* returns number of characters written to the output sccb * returns number of characters written to the output sccb
...@@ -195,7 +177,7 @@ sclp_move_current_line(struct sclp_buffer *to, struct sclp_buffer *from) ...@@ -195,7 +177,7 @@ sclp_move_current_line(struct sclp_buffer *to, struct sclp_buffer *from)
*/ */
int int
sclp_write(struct sclp_buffer *buffer, sclp_write(struct sclp_buffer *buffer,
const char *msg, int count, int from_user) const unsigned char *msg, int count, int from_user)
{ {
int spaces, i_msg; int spaces, i_msg;
char ch; char ch;
...@@ -203,7 +185,7 @@ sclp_write(struct sclp_buffer *buffer, ...@@ -203,7 +185,7 @@ sclp_write(struct sclp_buffer *buffer,
/* /*
* parse msg for escape sequences (\t,\v ...) and put formated * parse msg for escape sequences (\t,\v ...) and put formated
* msg into an mto (created by sclp_create_mto). * msg into an mto (created by sclp_initialize_mto).
* *
* We have to do this work ourselfs because there is no support for * We have to do this work ourselfs because there is no support for
* these characters on the native machine and only partial support * these characters on the native machine and only partial support
...@@ -235,11 +217,11 @@ sclp_write(struct sclp_buffer *buffer, ...@@ -235,11 +217,11 @@ sclp_write(struct sclp_buffer *buffer,
case '\n': /* new line, line feed (ASCII) */ case '\n': /* new line, line feed (ASCII) */
/* check if new mto needs to be created */ /* check if new mto needs to be created */
if (buffer->current_line == NULL) { if (buffer->current_line == NULL) {
rc = sclp_create_mto(buffer, 0); rc = sclp_initialize_mto(buffer, 0);
if (rc) if (rc)
return i_msg; return i_msg;
} }
sclp_add_mto(buffer); sclp_finalize_mto(buffer);
break; break;
case '\a': /* bell, one for several times */ case '\a': /* bell, one for several times */
/* set SCLP sound alarm bit in General Object */ /* set SCLP sound alarm bit in General Object */
...@@ -249,7 +231,8 @@ sclp_write(struct sclp_buffer *buffer, ...@@ -249,7 +231,8 @@ sclp_write(struct sclp_buffer *buffer,
case '\t': /* horizontal tabulator */ case '\t': /* horizontal tabulator */
/* check if new mto needs to be created */ /* check if new mto needs to be created */
if (buffer->current_line == NULL) { if (buffer->current_line == NULL) {
rc = sclp_create_mto(buffer, buffer->columns); rc = sclp_initialize_mto(buffer,
buffer->columns);
if (rc) if (rc)
return i_msg; return i_msg;
} }
...@@ -268,8 +251,9 @@ sclp_write(struct sclp_buffer *buffer, ...@@ -268,8 +251,9 @@ sclp_write(struct sclp_buffer *buffer,
/* = new line, leading spaces */ /* = new line, leading spaces */
if (buffer->current_line != NULL) { if (buffer->current_line != NULL) {
spaces = buffer->current_length; spaces = buffer->current_length;
sclp_add_mto(buffer); sclp_finalize_mto(buffer);
rc = sclp_create_mto(buffer, buffer->columns); rc = sclp_initialize_mto(buffer,
buffer->columns);
if (rc) if (rc)
return i_msg; return i_msg;
memset(buffer->current_line, 0x40, spaces); memset(buffer->current_line, 0x40, spaces);
...@@ -277,10 +261,11 @@ sclp_write(struct sclp_buffer *buffer, ...@@ -277,10 +261,11 @@ sclp_write(struct sclp_buffer *buffer,
buffer->current_length = spaces; buffer->current_length = spaces;
} else { } else {
/* one an empty line this is the same as \n */ /* one an empty line this is the same as \n */
rc = sclp_create_mto(buffer, buffer->columns); rc = sclp_initialize_mto(buffer,
buffer->columns);
if (rc) if (rc)
return i_msg; return i_msg;
sclp_add_mto(buffer); sclp_finalize_mto(buffer);
} }
break; break;
case '\b': /* backspace */ case '\b': /* backspace */
...@@ -296,7 +281,7 @@ sclp_write(struct sclp_buffer *buffer, ...@@ -296,7 +281,7 @@ sclp_write(struct sclp_buffer *buffer,
case 0x00: /* end of string */ case 0x00: /* end of string */
/* transfer current line to SCCB */ /* transfer current line to SCCB */
if (buffer->current_line != NULL) if (buffer->current_line != NULL)
sclp_add_mto(buffer); sclp_finalize_mto(buffer);
/* skip the rest of the message including the 0 byte */ /* skip the rest of the message including the 0 byte */
i_msg = count; i_msg = count;
break; break;
...@@ -306,7 +291,8 @@ sclp_write(struct sclp_buffer *buffer, ...@@ -306,7 +291,8 @@ sclp_write(struct sclp_buffer *buffer,
break; break;
/* check if new mto needs to be created */ /* check if new mto needs to be created */
if (buffer->current_line == NULL) { if (buffer->current_line == NULL) {
rc = sclp_create_mto(buffer, buffer->columns); rc = sclp_initialize_mto(buffer,
buffer->columns);
if (rc) if (rc)
return i_msg; return i_msg;
} }
...@@ -317,7 +303,7 @@ sclp_write(struct sclp_buffer *buffer, ...@@ -317,7 +303,7 @@ sclp_write(struct sclp_buffer *buffer,
/* check if current mto is full */ /* check if current mto is full */
if (buffer->current_line != NULL && if (buffer->current_line != NULL &&
buffer->current_length >= buffer->columns) buffer->current_length >= buffer->columns)
sclp_add_mto(buffer); sclp_finalize_mto(buffer);
} }
/* return number of processed characters */ /* return number of processed characters */
...@@ -361,7 +347,7 @@ sclp_set_columns(struct sclp_buffer *buffer, unsigned short columns) ...@@ -361,7 +347,7 @@ sclp_set_columns(struct sclp_buffer *buffer, unsigned short columns)
buffer->columns = columns; buffer->columns = columns;
if (buffer->current_line != NULL && if (buffer->current_line != NULL &&
buffer->current_length > buffer->columns) buffer->current_length > buffer->columns)
sclp_add_mto(buffer); sclp_finalize_mto(buffer);
} }
void void
...@@ -377,13 +363,62 @@ int ...@@ -377,13 +363,62 @@ int
sclp_rw_init(void) sclp_rw_init(void)
{ {
static int init_done = 0; static int init_done = 0;
int rc;
if (init_done) if (init_done)
return 0; return 0;
init_done = 1;
return sclp_register(&sclp_rw_event); rc = sclp_register(&sclp_rw_event);
if (rc == 0)
init_done = 1;
return rc;
} }
#define SCLP_EVBUF_PROCESSED 0x80
/*
* Traverse array of event buffers contained in SCCB and remove all buffers
* with a set "processed" flag. Return the number of unprocessed buffers.
*/
static int
sclp_remove_processed(struct sccb_header *sccb)
{
struct evbuf_header *evbuf;
int unprocessed;
u16 remaining;
evbuf = (struct evbuf_header *) (sccb + 1);
unprocessed = 0;
remaining = sccb->length - sizeof(struct sccb_header);
while (remaining > 0) {
remaining -= evbuf->length;
if (evbuf->flags & SCLP_EVBUF_PROCESSED) {
sccb->length -= evbuf->length;
memcpy((void *) evbuf,
(void *) ((addr_t) evbuf + evbuf->length),
remaining);
} else {
unprocessed++;
evbuf = (struct evbuf_header *)
((addr_t) evbuf + evbuf->length);
}
}
return unprocessed;
}
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
/* /*
* second half of Write Event Data-function that has to be done after * second half of Write Event Data-function that has to be done after
* interruption indicating completion of Service Call. * interruption indicating completion of Service Call.
...@@ -391,32 +426,68 @@ sclp_rw_init(void) ...@@ -391,32 +426,68 @@ sclp_rw_init(void)
static void static void
sclp_writedata_callback(struct sclp_req *request, void *data) sclp_writedata_callback(struct sclp_req *request, void *data)
{ {
int rc;
struct sclp_buffer *buffer; struct sclp_buffer *buffer;
struct write_sccb *sccb; struct write_sccb *sccb;
buffer = (struct sclp_buffer *) data; buffer = (struct sclp_buffer *) data;
sccb = buffer->sccb; sccb = buffer->sccb;
/* FIXME: proper error handling */ if (request->status == SCLP_REQ_FAILED) {
if (buffer->callback != NULL)
buffer->callback(buffer, -EIO);
return;
}
/* check SCLP response code and choose suitable action */ /* check SCLP response code and choose suitable action */
switch (sccb->header.response_code) { switch (sccb->header.response_code) {
case 0x0020 : case 0x0020 :
/* normal completion, buffer processed, message(s) sent */ /* Normal completion, buffer processed, message(s) sent */
rc = 0;
break; break;
case 0x0340: /* contained SCLP equipment check */ case 0x0340: /* Contained SCLP equipment check */
case 0x40F0: /* function code disabled in SCLP receive mask */ if (buffer->retry_count++ > SCLP_BUFFER_MAX_RETRY) {
rc = -EIO;
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;
buffer->request.status = SCLP_REQ_FILLED;
sclp_add_request(request);
return;
}
rc = 0;
break; break;
case 0x0040: /* SCLP equipment check */
case 0x05f0: /* Target resource in improper state */
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);
return;
default: default:
/* sclp_free_sccb(sccb); */ if (sccb->header.response_code == 0x71f0)
rc = -ENOMEM;
else
rc = -EINVAL;
printk(KERN_WARNING SCLP_RW_PRINT_HEADER printk(KERN_WARNING SCLP_RW_PRINT_HEADER
"write event data failed " "sclp_writedata_callback: %s (response code=0x%x).\n",
"(response code: 0x%x SCCB address %p).\n", sclp_error_message(sccb->header.response_code),
sccb->header.response_code, sccb); sccb->header.response_code);
break; break;
} }
if (buffer->callback != NULL) if (buffer->callback != NULL)
buffer->callback(buffer, sccb->header.response_code); buffer->callback(buffer, rc);
} }
/* /*
...@@ -431,10 +502,10 @@ sclp_emit_buffer(struct sclp_buffer *buffer, ...@@ -431,10 +502,10 @@ sclp_emit_buffer(struct sclp_buffer *buffer,
/* add current line if there is one */ /* add current line if there is one */
if (buffer->current_line != NULL) if (buffer->current_line != NULL)
sclp_add_mto(buffer); sclp_finalize_mto(buffer);
/* Are there messages in the output buffer ? */ /* Are there messages in the output buffer ? */
if (buffer->mto_number <= 0) if (buffer->mto_number == 0)
return; return;
sccb = buffer->sccb; sccb = buffer->sccb;
......
...@@ -11,6 +11,9 @@ ...@@ -11,6 +11,9 @@
#ifndef __SCLP_RW_H__ #ifndef __SCLP_RW_H__
#define __SCLP_RW_H__ #define __SCLP_RW_H__
#include <linux/list.h>
#include <linux/timer.h>
struct mto { struct mto {
u16 length; u16 length;
u16 type; u16 type;
...@@ -70,6 +73,8 @@ struct sclp_buffer { ...@@ -70,6 +73,8 @@ struct sclp_buffer {
struct write_sccb *sccb; struct write_sccb *sccb;
char *current_line; char *current_line;
int current_length; int current_length;
int retry_count;
struct timer_list retry_timer;
/* output format settings */ /* output format settings */
unsigned short columns; unsigned short columns;
unsigned short htab; unsigned short htab;
...@@ -84,8 +89,7 @@ int sclp_rw_init(void); ...@@ -84,8 +89,7 @@ int sclp_rw_init(void);
struct sclp_buffer *sclp_make_buffer(void *, unsigned short, unsigned short); struct sclp_buffer *sclp_make_buffer(void *, unsigned short, unsigned short);
void *sclp_unmake_buffer(struct sclp_buffer *); void *sclp_unmake_buffer(struct sclp_buffer *);
int sclp_buffer_space(struct sclp_buffer *); int sclp_buffer_space(struct sclp_buffer *);
int sclp_write(struct sclp_buffer *buffer, const char *, int, int); int sclp_write(struct sclp_buffer *buffer, const unsigned char *, int, int);
void sclp_move_current_line(struct sclp_buffer *, struct sclp_buffer *);
void sclp_emit_buffer(struct sclp_buffer *,void (*)(struct sclp_buffer *,int)); void sclp_emit_buffer(struct sclp_buffer *,void (*)(struct sclp_buffer *,int));
void sclp_set_columns(struct sclp_buffer *, unsigned short); void sclp_set_columns(struct sclp_buffer *, unsigned short);
void sclp_set_htab(struct sclp_buffer *, unsigned short); void sclp_set_htab(struct sclp_buffer *, unsigned short);
......
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
#define SCLP_TTY_BUF_SIZE 512 #define SCLP_TTY_BUF_SIZE 512
/* /*
* There is excatly one SCLP terminal, so we can keep things simple * There is exactly one SCLP terminal, so we can keep things simple
* and allocate all variables statically. * and allocate all variables statically.
*/ */
...@@ -265,13 +265,14 @@ sclp_ttybuf_callback(struct sclp_buffer *buffer, int rc) ...@@ -265,13 +265,14 @@ sclp_ttybuf_callback(struct sclp_buffer *buffer, int rc)
struct sclp_buffer *next; struct sclp_buffer *next;
void *page; void *page;
/* FIXME: what if rc != 0x0020 */ /* Ignore return code - because tty-writes aren't critical,
we do without a sophisticated error recovery mechanism. */
page = sclp_unmake_buffer(buffer); page = sclp_unmake_buffer(buffer);
spin_lock_irqsave(&sclp_tty_lock, flags); spin_lock_irqsave(&sclp_tty_lock, flags);
list_add_tail((struct list_head *) page, &sclp_tty_pages);
sclp_tty_buffer_count--;
/* Remove buffer from outqueue */ /* Remove buffer from outqueue */
list_del(&buffer->list); list_del(&buffer->list);
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. */ /* Check if there is a pending buffer on the out queue. */
next = NULL; next = NULL;
if (!list_empty(&sclp_tty_outqueue)) if (!list_empty(&sclp_tty_outqueue))
...@@ -489,8 +490,8 @@ void sclp_tty_input(unsigned char* buf, unsigned int count) ...@@ -489,8 +490,8 @@ void sclp_tty_input(unsigned char* buf, unsigned int count)
/* send (normal) input to line discipline */ /* send (normal) input to line discipline */
memcpy(sclp_tty->flip.char_buf_ptr, buf, count); memcpy(sclp_tty->flip.char_buf_ptr, buf, count);
if (count < 2 || if (count < 2 ||
strncmp (buf + count - 2, "^n", 2) || strncmp ((const char *) buf + count - 2, "^n", 2) ||
strncmp (buf + count - 2, "\0252n", 2)) { strncmp ((const char *) buf + count - 2, "\0252n", 2)) {
sclp_tty->flip.char_buf_ptr[count] = '\n'; sclp_tty->flip.char_buf_ptr[count] = '\n';
count++; count++;
} else } else
...@@ -558,7 +559,7 @@ sclp_switch_cases(unsigned char *buf, int count, ...@@ -558,7 +559,7 @@ sclp_switch_cases(unsigned char *buf, int count,
} }
static void static void
sclp_get_input(char *start, char *end) sclp_get_input(unsigned char *start, unsigned char *end)
{ {
int count; int count;
...@@ -572,7 +573,7 @@ sclp_get_input(char *start, char *end) ...@@ -572,7 +573,7 @@ sclp_get_input(char *start, char *end)
/* /*
* if set in ioctl find out characters in lower or upper case * if set in ioctl find out characters in lower or upper case
* (depends on current case) seperated by a special character, * (depends on current case) separated by a special character,
* works on EBCDIC * works on EBCDIC
*/ */
if (sclp_ioctls.delim) if (sclp_ioctls.delim)
...@@ -625,8 +626,8 @@ sclp_eval_selfdeftextmsg(struct gds_subvector *start, ...@@ -625,8 +626,8 @@ sclp_eval_selfdeftextmsg(struct gds_subvector *start,
subvec = find_gds_subvector(subvec, end, 0x30); subvec = find_gds_subvector(subvec, end, 0x30);
if (!subvec) if (!subvec)
break; break;
sclp_get_input((void *)(subvec + 1), sclp_get_input((unsigned char *)(subvec + 1),
(void *) subvec + subvec->length); (unsigned char *) subvec + subvec->length);
(void *) subvec += subvec->length; (void *) subvec += subvec->length;
} }
} }
......
...@@ -18,9 +18,9 @@ struct sclp_ioctls { ...@@ -18,9 +18,9 @@ struct sclp_ioctls {
unsigned short htab; unsigned short htab;
unsigned char echo; unsigned char echo;
unsigned short columns; unsigned short columns;
signed char final_nl; unsigned char final_nl;
unsigned short max_sccb; unsigned short max_sccb;
unsigned short kmem_sccb; /* cant be modified at run time */ unsigned short kmem_sccb; /* can't be modified at run time */
unsigned char tolower; unsigned char tolower;
unsigned char delim; unsigned char delim;
}; };
......
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