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 @@
# 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 \
tubttyaid.o tubttybld.o tubttyscl.o \
......@@ -10,8 +10,8 @@ tub3270-objs := tuball.o tubfs.o tubtty.o \
obj-y += ctrlchar.o
obj-$(CONFIG_TN3215) += con3215.o
obj-$(CONFIG_SCLP) += sclp.o
obj-$(CONFIG_SCLP_TTY) += sclp_rw.o sclp_tty.o
obj-$(CONFIG_SCLP) += sclp.o sclp_rw.o
obj-$(CONFIG_SCLP_TTY) += sclp_tty.o
obj-$(CONFIG_SCLP_CONSOLE) += sclp_con.o
obj-$(CONFIG_SCLP_CPI) += sclp_cpi.o
obj-$(CONFIG_TN3270) += tub3270.o
......
......@@ -10,6 +10,7 @@
#include <linux/config.h>
#include <linux/version.h>
#include <linux/module.h>
#include <linux/kmod.h>
#include <linux/bootmem.h>
#include <linux/err.h>
......@@ -17,6 +18,7 @@
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/timer.h>
#include <asm/s390_ext.h>
#include "sclp.h"
......@@ -53,12 +55,21 @@ static char sclp_read_sccb[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE)));
/* sccb for write mask sccb */
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;
/* some status flags */
#define SCLP_INIT 0
#define SCLP_RUNNING 1
#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
*/
......@@ -86,7 +97,7 @@ __service_call(sclp_cmdw_t command, void *sccb)
* new SCCB unchanged
* cc == 3: SCLP function not operational
*/
if (cc == 3)
if (cc == SCLP_NOT_OPERATIONAL)
return -EIO;
/*
* 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)
* that has to complete first
*/
set_bit(SCLP_RUNNING, &sclp_status);
if (cc == 2)
if (cc == SCLP_BUSY)
return -EBUSY;
return 0;
}
......@@ -130,6 +141,7 @@ __sclp_start_request(void)
static int
sclp_process_evbufs(struct sccb_header *sccb)
{
int result;
unsigned long flags;
struct evbuf_header *evbuf;
struct list_head *l;
......@@ -137,8 +149,10 @@ sclp_process_evbufs(struct sccb_header *sccb)
spin_lock_irqsave(&sclp_lock, flags);
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 */
t = NULL;
list_for_each(l, &sclp_reg_list) {
t = list_entry(l, struct sclp_register, list);
if (t->receive_mask & (1 << (32 - evbuf->type))) {
......@@ -146,56 +160,72 @@ sclp_process_evbufs(struct sccb_header *sccb)
t->receiver_fn(evbuf);
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);
return -ENOSYS;
return result;
}
/*
* postprocessing of unconditional read service call
*/
static void
sclp_unconditional_read_cb(struct sclp_req *read_req, void *data)
char *
sclp_error_message(u16 rc)
{
static struct {
u16 code; char *msg;
} errors[] = {
} sclp_errors[] = {
{ 0x0000, "No response code stored (machine malfunction)" },
{ 0x0020, "Normal Completion" },
{ 0x0040, "SCLP equipment check" },
{ 0x0100, "SCCB boundary violation" },
{ 0x01f0, "invalid command" },
{ 0x0300, "insufficient SCCB length" },
{ 0x40f0, "invalid function code" },
{ 0x60f0, "got interrupt but no data found" },
{ 0x62f0, "got interrupt but no data found" },
{ 0x70f0, "invalid selection mask" }
{ 0x01f0, "Invalid command" },
{ 0x0220, "Normal Completion; suppressed buffers pending" },
{ 0x0300, "Insufficient SCCB length" },
{ 0x0340, "Contained SCLP equipment check" },
{ 0x05f0, "Target resource in improper state" },
{ 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;
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;
if (sccb->response_code == 0x0020 ||
sccb->response_code == 0x0220) {
/* normal read completion, event buffers stored in sccb */
if (sclp_process_evbufs(sccb) == 0) {
clear_bit(SCLP_READING, &sclp_status);
return;
}
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";
if (sclp_process_evbufs(sccb) != 0)
printk(KERN_WARNING SCLP_CORE_PRINT_HEADER
"unconditional read: "
"unrequested event buffer received.\n");
}
if (sccb->response_code != 0x0020)
printk(KERN_WARNING SCLP_CORE_PRINT_HEADER
"unconditional read: %s (response code =0x%x).\n",
errmsg, sccb->response_code);
"unconditional read: %s (response code=0x%x).\n",
sclp_error_message(sccb->response_code),
sccb->response_code);
clear_bit(SCLP_READING, &sclp_status);
}
......@@ -209,7 +239,7 @@ __sclp_unconditional_read(void)
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
*/
if (sclp_receive_mask == 0)
......@@ -217,13 +247,13 @@ __sclp_unconditional_read(void)
/* Don't try reading if a read is already outstanding */
if (test_and_set_bit(SCLP_READING, &sclp_status))
return;
/* Initialise read sccb */
/* Initialize read sccb */
sccb = (struct sccb_header *) sclp_read_sccb;
clear_page(sccb);
sccb->length = PAGE_SIZE;
sccb->function_code = 0; /* unconditional read */
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->command = SCLP_CMDW_READDATA;
read_req->status = SCLP_REQ_QUEUED;
......@@ -233,6 +263,11 @@ __sclp_unconditional_read(void)
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
*/
......@@ -242,15 +277,14 @@ sclp_interrupt_handler(struct pt_regs *regs, __u16 code)
u32 ext_int_param, finished_sccb, evbuf_pending;
struct list_head *l;
struct sclp_req *req, *tmp;
int cpu;
cpu = smp_processor_id();
ext_int_param = S390_lowcore.ext_params;
finished_sccb = ext_int_param & -8U;
evbuf_pending = ext_int_param & 1;
finished_sccb = ext_int_param & EXT_INT_SCCB_MASK;
evbuf_pending = ext_int_param & (EXT_INT_EVBUF_PENDING |
EXT_INT_STATECHANGE_PENDING);
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
* from before the last re-ipl.
*/
......@@ -326,8 +360,8 @@ sclp_sync_wait(void)
}
/*
* Queue a request to the tail of the ccw_queue. Start the I/O if
* possible.
* Queue an SCLP request. Request will immediately be processed if queue is
* empty.
*/
void
sclp_add_request(struct sclp_req *req)
......@@ -391,17 +425,17 @@ sclp_state_change(struct evbuf_header *evbuf)
scbuf = (struct sclp_statechangebuf *) evbuf;
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
"state change event with mask length %i\n",
scbuf->mask_length);
else
/* set new send mask */
/* set new receive mask */
sclp_receive_mask = scbuf->sclp_receive_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
"state change event with mask length %i\n",
scbuf->mask_length);
......@@ -496,6 +530,8 @@ struct init_sccb {
sccb_mask_t sclp_receive_mask;
} __attribute__((packed));
static void sclp_init_mask_retry(unsigned long);
static int
sclp_init_mask(void)
{
......@@ -557,13 +593,34 @@ sclp_init_mask(void)
spin_lock_irqsave(&sclp_lock, flags);
} while (rc == -EBUSY);
}
if (sccb->header.response_code != 0x0020) {
/* WRITEMASK failed - we cannot rely on receiving a state
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);
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().
*/
......@@ -573,7 +630,7 @@ sclp_init(void)
int rc;
if (test_bit(SCLP_INIT, &sclp_status))
/* Already initialised. */
/* Already initialized. */
return 0;
/*
......@@ -600,6 +657,7 @@ sclp_init(void)
*/
ctl_set_bit(0, 9);
init_timer(&retry_timer);
/* do the initial write event mask */
rc = sclp_init_mask();
if (rc == 0) {
......@@ -616,6 +674,15 @@ sclp_init(void)
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
sclp_register(struct sclp_register *reg)
{
......@@ -650,6 +717,9 @@ sclp_register(struct sclp_register *reg)
return 0;
}
/*
* Unregister the SCLP event listener identified by REG.
*/
void
sclp_unregister(struct sclp_register *reg)
{
......@@ -661,3 +731,7 @@ sclp_unregister(struct sclp_register *reg)
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 @@
#define LnTpFlgs_EndText 0x1000
#define LnTpFlgs_PromptText 0x0800
#define SCLP_COMMAND_INITIATED 0
#define SCLP_BUSY 2
#define SCLP_NOT_OPERATIONAL 3
typedef unsigned int sclp_cmdw_t;
#define SCLP_CMDW_READDATA 0x00770005
......@@ -129,6 +125,7 @@ void 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);
/* useful inlines */
......
......@@ -51,13 +51,14 @@ sclp_conbuf_callback(struct sclp_buffer *buffer, int rc)
struct sclp_buffer *next;
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);
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 */
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. */
next = NULL;
if (!list_empty(&sclp_con_outqueue))
......@@ -104,7 +105,7 @@ sclp_console_write(struct console *console, const char *message,
void *page;
int written;
if (count <= 0)
if (count == 0)
return;
spin_lock_irqsave(&sclp_con_lock, flags);
/*
......@@ -125,7 +126,8 @@ sclp_console_write(struct console *console, const char *message,
sclp_con_width_htab);
}
/* 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)
break;
/*
......@@ -232,6 +234,6 @@ sclp_console_init(void)
sclp_con_columns = 80;
sclp_con_width_htab = 8;
/* enable printks access to this driver */
/* enable printk-access to this driver */
register_console(&sclp_console);
}
......@@ -130,7 +130,7 @@ cpi_prepare_req(void)
req = (struct sclp_req *) kmalloc(sizeof(struct sclp_req), GFP_KERNEL);
if (req == NULL)
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) {
kfree(req);
return ERR_PTR(-ENOMEM);
......@@ -227,6 +227,7 @@ cpi_module_init(void)
rc = 0;
cpi_free_req(req);
sclp_unregister(&sclp_cpi_event);
return rc;
}
......
......@@ -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->sccb = sccb;
buffer->retry_count = 0;
init_timer(&buffer->retry_timer);
buffer->mto_number = 0;
buffer->mto_char_sum = 0;
buffer->current_line = NULL;
......@@ -87,14 +89,11 @@ sclp_unmake_buffer(struct sclp_buffer *buffer)
}
/*
* Creates a new Message Text Object with enough room for str_len
* characters. This function will create a new sccb for buffering
* 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.
* Initialize a new Message Text Object (MTO) at the end of the provided buffer
* with enough room for max_len characters. Return 0 on success.
*/
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 mto *mto;
......@@ -109,7 +108,7 @@ sclp_create_mto(struct sclp_buffer *buffer, int max_len)
return -ENOMEM;
/* 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,
......@@ -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
* the str_len parameter as the real length of the string.
* Finalize MTO initialized by sclp_initialize_mto(), updating the sizes of
* MTO, enclosing MDB, event buffer and SCCB.
*/
static void
sclp_add_mto(struct sclp_buffer *buffer)
sclp_finalize_mto(struct sclp_buffer *buffer)
{
struct write_sccb *sccb;
struct mto *mto;
......@@ -147,7 +146,7 @@ sclp_add_mto(struct sclp_buffer *buffer)
/* find address of new message text object */
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 */
mto->length = mto_size;
......@@ -169,23 +168,6 @@ sclp_add_mto(struct sclp_buffer *buffer)
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,
* 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)
*/
int
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;
char ch;
......@@ -203,7 +185,7 @@ sclp_write(struct sclp_buffer *buffer,
/*
* 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
* these characters on the native machine and only partial support
......@@ -235,11 +217,11 @@ sclp_write(struct sclp_buffer *buffer,
case '\n': /* new line, line feed (ASCII) */
/* check if new mto needs to be created */
if (buffer->current_line == NULL) {
rc = sclp_create_mto(buffer, 0);
rc = sclp_initialize_mto(buffer, 0);
if (rc)
return i_msg;
}
sclp_add_mto(buffer);
sclp_finalize_mto(buffer);
break;
case '\a': /* bell, one for several times */
/* set SCLP sound alarm bit in General Object */
......@@ -249,7 +231,8 @@ sclp_write(struct sclp_buffer *buffer,
case '\t': /* horizontal tabulator */
/* check if new mto needs to be created */
if (buffer->current_line == NULL) {
rc = sclp_create_mto(buffer, buffer->columns);
rc = sclp_initialize_mto(buffer,
buffer->columns);
if (rc)
return i_msg;
}
......@@ -268,8 +251,9 @@ sclp_write(struct sclp_buffer *buffer,
/* = new line, leading spaces */
if (buffer->current_line != NULL) {
spaces = buffer->current_length;
sclp_add_mto(buffer);
rc = sclp_create_mto(buffer, buffer->columns);
sclp_finalize_mto(buffer);
rc = sclp_initialize_mto(buffer,
buffer->columns);
if (rc)
return i_msg;
memset(buffer->current_line, 0x40, spaces);
......@@ -277,10 +261,11 @@ sclp_write(struct sclp_buffer *buffer,
buffer->current_length = spaces;
} else {
/* 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)
return i_msg;
sclp_add_mto(buffer);
sclp_finalize_mto(buffer);
}
break;
case '\b': /* backspace */
......@@ -296,7 +281,7 @@ sclp_write(struct sclp_buffer *buffer,
case 0x00: /* end of string */
/* transfer current line to SCCB */
if (buffer->current_line != NULL)
sclp_add_mto(buffer);
sclp_finalize_mto(buffer);
/* skip the rest of the message including the 0 byte */
i_msg = count;
break;
......@@ -306,7 +291,8 @@ sclp_write(struct sclp_buffer *buffer,
break;
/* check if new mto needs to be created */
if (buffer->current_line == NULL) {
rc = sclp_create_mto(buffer, buffer->columns);
rc = sclp_initialize_mto(buffer,
buffer->columns);
if (rc)
return i_msg;
}
......@@ -317,7 +303,7 @@ sclp_write(struct sclp_buffer *buffer,
/* check if current mto is full */
if (buffer->current_line != NULL &&
buffer->current_length >= buffer->columns)
sclp_add_mto(buffer);
sclp_finalize_mto(buffer);
}
/* return number of processed characters */
......@@ -361,7 +347,7 @@ sclp_set_columns(struct sclp_buffer *buffer, unsigned short columns)
buffer->columns = columns;
if (buffer->current_line != NULL &&
buffer->current_length > buffer->columns)
sclp_add_mto(buffer);
sclp_finalize_mto(buffer);
}
void
......@@ -377,13 +363,62 @@ int
sclp_rw_init(void)
{
static int init_done = 0;
int rc;
if (init_done)
return 0;
rc = sclp_register(&sclp_rw_event);
if (rc == 0)
init_done = 1;
return sclp_register(&sclp_rw_event);
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
* interruption indicating completion of Service Call.
......@@ -391,32 +426,68 @@ sclp_rw_init(void)
static void
sclp_writedata_callback(struct sclp_req *request, void *data)
{
int rc;
struct sclp_buffer *buffer;
struct write_sccb *sccb;
buffer = (struct sclp_buffer *) data;
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 */
switch (sccb->header.response_code) {
case 0x0020 :
/* normal completion, buffer processed, message(s) sent */
/* Normal completion, buffer processed, message(s) sent */
rc = 0;
break;
case 0x0340: /* contained SCLP equipment check */
case 0x40F0: /* function code disabled in SCLP receive mask */
case 0x0340: /* Contained SCLP equipment check */
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;
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:
/* sclp_free_sccb(sccb); */
if (sccb->header.response_code == 0x71f0)
rc = -ENOMEM;
else
rc = -EINVAL;
printk(KERN_WARNING SCLP_RW_PRINT_HEADER
"write event data failed "
"(response code: 0x%x SCCB address %p).\n",
sccb->header.response_code, sccb);
"sclp_writedata_callback: %s (response code=0x%x).\n",
sclp_error_message(sccb->header.response_code),
sccb->header.response_code);
break;
}
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,
/* add current line if there is one */
if (buffer->current_line != NULL)
sclp_add_mto(buffer);
sclp_finalize_mto(buffer);
/* Are there messages in the output buffer ? */
if (buffer->mto_number <= 0)
if (buffer->mto_number == 0)
return;
sccb = buffer->sccb;
......
......@@ -11,6 +11,9 @@
#ifndef __SCLP_RW_H__
#define __SCLP_RW_H__
#include <linux/list.h>
#include <linux/timer.h>
struct mto {
u16 length;
u16 type;
......@@ -70,6 +73,8 @@ struct sclp_buffer {
struct write_sccb *sccb;
char *current_line;
int current_length;
int retry_count;
struct timer_list retry_timer;
/* output format settings */
unsigned short columns;
unsigned short htab;
......@@ -84,8 +89,7 @@ int sclp_rw_init(void);
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 char *, int, int);
void sclp_move_current_line(struct sclp_buffer *, struct sclp_buffer *);
int sclp_write(struct sclp_buffer *buffer, const unsigned char *, int, 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_htab(struct sclp_buffer *, unsigned short);
......
......@@ -32,7 +32,7 @@
#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.
*/
......@@ -265,13 +265,14 @@ sclp_ttybuf_callback(struct sclp_buffer *buffer, int rc)
struct sclp_buffer *next;
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);
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 */
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. */
next = NULL;
if (!list_empty(&sclp_tty_outqueue))
......@@ -489,8 +490,8 @@ void sclp_tty_input(unsigned char* buf, unsigned int count)
/* send (normal) input to line discipline */
memcpy(sclp_tty->flip.char_buf_ptr, buf, count);
if (count < 2 ||
strncmp (buf + count - 2, "^n", 2) ||
strncmp (buf + count - 2, "\0252n", 2)) {
strncmp ((const char *) buf + count - 2, "^n", 2) ||
strncmp ((const char *) buf + count - 2, "\0252n", 2)) {
sclp_tty->flip.char_buf_ptr[count] = '\n';
count++;
} else
......@@ -558,7 +559,7 @@ sclp_switch_cases(unsigned char *buf, int count,
}
static void
sclp_get_input(char *start, char *end)
sclp_get_input(unsigned char *start, unsigned char *end)
{
int count;
......@@ -572,7 +573,7 @@ sclp_get_input(char *start, char *end)
/*
* 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
*/
if (sclp_ioctls.delim)
......@@ -625,8 +626,8 @@ sclp_eval_selfdeftextmsg(struct gds_subvector *start,
subvec = find_gds_subvector(subvec, end, 0x30);
if (!subvec)
break;
sclp_get_input((void *)(subvec + 1),
(void *) subvec + subvec->length);
sclp_get_input((unsigned char *)(subvec + 1),
(unsigned char *) subvec + subvec->length);
(void *) subvec += subvec->length;
}
}
......
......@@ -18,9 +18,9 @@ struct sclp_ioctls {
unsigned short htab;
unsigned char echo;
unsigned short columns;
signed char final_nl;
unsigned char final_nl;
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 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