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 ...@@ -11,7 +11,7 @@ obj-$(CONFIG_TN3270_FS) += fs3270.o
obj-$(CONFIG_TN3215) += con3215.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_TTY) += sclp_tty.o
obj-$(CONFIG_SCLP_CONSOLE) += sclp_con.o obj-$(CONFIG_SCLP_CONSOLE) += sclp_con.o
obj-$(CONFIG_SCLP_VT220_TTY) += sclp_vt220.o obj-$(CONFIG_SCLP_VT220_TTY) += sclp_vt220.o
......
This diff is collapsed.
...@@ -95,6 +95,7 @@ struct sclp_req { ...@@ -95,6 +95,7 @@ struct sclp_req {
sclp_cmdw_t command; /* sclp command to execute */ sclp_cmdw_t command; /* sclp command to execute */
void *sccb; /* pointer to the sccb to execute */ void *sccb; /* pointer to the sccb to execute */
char status; /* status of this request */ char status; /* status of this request */
int start_count; /* number of SVCs done for this req */
/* Callback that is called after reaching final status. */ /* Callback that is called after reaching final status. */
void (*callback)(struct sclp_req *, void *data); void (*callback)(struct sclp_req *, void *data);
void *callback_data; void *callback_data;
...@@ -123,12 +124,13 @@ struct sclp_register { ...@@ -123,12 +124,13 @@ struct sclp_register {
}; };
/* externals from sclp.c */ /* 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); 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);
int sclp_remove_processed(struct sccb_header *sccb); int sclp_remove_processed(struct sccb_header *sccb);
int sclp_deactivate(void);
int sclp_reactivate(void);
/* useful inlines */ /* useful inlines */
......
...@@ -49,25 +49,22 @@ static void ...@@ -49,25 +49,22 @@ static void
sclp_conbuf_callback(struct sclp_buffer *buffer, int rc) sclp_conbuf_callback(struct sclp_buffer *buffer, int rc)
{ {
unsigned long flags; unsigned long flags;
struct sclp_buffer *next;
void *page; void *page;
/* Ignore return code - because console-writes aren't critical, do {
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); /* Remove buffer from outqueue */
/* Remove buffer from outqueue */ list_del(&buffer->list);
list_del(&buffer->list); sclp_con_buffer_count--;
sclp_con_buffer_count--; list_add_tail((struct list_head *) page, &sclp_con_pages);
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. */ buffer = NULL;
next = NULL; if (!list_empty(&sclp_con_outqueue))
if (!list_empty(&sclp_con_outqueue)) buffer = list_entry(sclp_con_outqueue.next,
next = list_entry(sclp_con_outqueue.next, struct sclp_buffer, list);
struct sclp_buffer, list); spin_unlock_irqrestore(&sclp_con_lock, flags);
spin_unlock_irqrestore(&sclp_con_lock, flags); } while (buffer && sclp_emit_buffer(buffer, sclp_conbuf_callback));
if (next != NULL)
sclp_emit_buffer(next, sclp_conbuf_callback);
} }
static inline void static inline void
...@@ -76,6 +73,7 @@ sclp_conbuf_emit(void) ...@@ -76,6 +73,7 @@ sclp_conbuf_emit(void)
struct sclp_buffer* buffer; struct sclp_buffer* buffer;
unsigned long flags; unsigned long flags;
int count; int count;
int rc;
spin_lock_irqsave(&sclp_con_lock, flags); spin_lock_irqsave(&sclp_con_lock, flags);
buffer = sclp_conbuf; buffer = sclp_conbuf;
...@@ -87,8 +85,11 @@ sclp_conbuf_emit(void) ...@@ -87,8 +85,11 @@ sclp_conbuf_emit(void)
list_add_tail(&buffer->list, &sclp_con_outqueue); list_add_tail(&buffer->list, &sclp_con_outqueue);
count = sclp_con_buffer_count++; count = sclp_con_buffer_count++;
spin_unlock_irqrestore(&sclp_con_lock, flags); spin_unlock_irqrestore(&sclp_con_lock, flags);
if (count == 0) if (count)
sclp_emit_buffer(buffer, sclp_conbuf_callback); return;
rc = sclp_emit_buffer(buffer, sclp_conbuf_callback);
if (rc)
sclp_conbuf_callback(buffer, rc);
} }
/* /*
......
...@@ -196,18 +196,20 @@ cpi_module_init(void) ...@@ -196,18 +196,20 @@ cpi_module_init(void)
rc = sclp_register(&sclp_cpi_event); rc = sclp_register(&sclp_cpi_event);
if (rc) { if (rc) {
/* could not register sclp event. Die. */ /* 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; return -EINVAL;
} }
if (!(sclp_cpi_event.sclp_send_mask & EvTyp_CtlProgIdent_Mask)) { 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); sclp_unregister(&sclp_cpi_event);
return -ENOTSUPP; return -ENOTSUPP;
} }
req = cpi_prepare_req(); req = cpi_prepare_req();
if (IS_ERR(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); sclp_unregister(&sclp_cpi_event);
return PTR_ERR(req); return PTR_ERR(req);
} }
...@@ -216,13 +218,20 @@ cpi_module_init(void) ...@@ -216,13 +218,20 @@ cpi_module_init(void)
sema_init(&sem, 0); sema_init(&sem, 0);
req->callback_data = &sem; req->callback_data = &sem;
/* Add request to sclp queue */ /* 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 */ /* make "insmod" sleep until callback arrives */
down(&sem); down(&sem);
rc = ((struct cpi_sccb *) req->sccb)->header.response_code; rc = ((struct cpi_sccb *) req->sccb)->header.response_code;
if (rc != 0x0020) { 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; rc = -ECOMM;
} else } else
rc = 0; 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) ...@@ -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 = ((struct sclp_buffer *) ((addr_t) sccb + PAGE_SIZE)) - 1;
buffer->sccb = sccb; buffer->sccb = sccb;
buffer->retry_count = 0; 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;
...@@ -365,17 +364,7 @@ sclp_rw_init(void) ...@@ -365,17 +364,7 @@ sclp_rw_init(void)
return rc; return rc;
} }
static void #define SCLP_BUFFER_MAX_RETRY 1
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
...@@ -404,7 +393,7 @@ sclp_writedata_callback(struct sclp_req *request, void *data) ...@@ -404,7 +393,7 @@ sclp_writedata_callback(struct sclp_req *request, void *data)
break; break;
case 0x0340: /* Contained SCLP equipment check */ case 0x0340: /* Contained SCLP equipment check */
if (buffer->retry_count++ > SCLP_BUFFER_MAX_RETRY) { if (++buffer->retry_count > SCLP_BUFFER_MAX_RETRY) {
rc = -EIO; rc = -EIO;
break; break;
} }
...@@ -413,26 +402,26 @@ sclp_writedata_callback(struct sclp_req *request, void *data) ...@@ -413,26 +402,26 @@ sclp_writedata_callback(struct sclp_req *request, void *data)
/* not all buffers were processed */ /* not all buffers were processed */
sccb->header.response_code = 0x0000; sccb->header.response_code = 0x0000;
buffer->request.status = SCLP_REQ_FILLED; buffer->request.status = SCLP_REQ_FILLED;
sclp_add_request(request); rc = sclp_add_request(request);
return; if (rc == 0)
} return;
rc = 0; } else
rc = 0;
break; break;
case 0x0040: /* SCLP equipment check */ case 0x0040: /* SCLP equipment check */
case 0x05f0: /* Target resource in improper state */ 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; rc = -EIO;
break; break;
} }
/* wait some time, then retry request */ /* retry request */
buffer->retry_timer.function = sclp_buffer_retry; sccb->header.response_code = 0x0000;
buffer->retry_timer.data = (unsigned long) buffer; buffer->request.status = SCLP_REQ_FILLED;
buffer->retry_timer.expires = jiffies + rc = sclp_add_request(request);
SCLP_BUFFER_RETRY_INTERVAL*HZ; if (rc == 0)
add_timer(&buffer->retry_timer); return;
return; break;
default: default:
if (sccb->header.response_code == 0x71f0) if (sccb->header.response_code == 0x71f0)
rc = -ENOMEM; rc = -ENOMEM;
...@@ -446,9 +435,10 @@ sclp_writedata_callback(struct sclp_req *request, void *data) ...@@ -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 * 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, sclp_emit_buffer(struct sclp_buffer *buffer,
void (*callback)(struct sclp_buffer *, int)) void (*callback)(struct sclp_buffer *, int))
{ {
...@@ -459,11 +449,8 @@ sclp_emit_buffer(struct sclp_buffer *buffer, ...@@ -459,11 +449,8 @@ sclp_emit_buffer(struct sclp_buffer *buffer,
sclp_finalize_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)
if (callback != NULL) return -EIO;
callback(buffer, 0);
return;
}
sccb = buffer->sccb; sccb = buffer->sccb;
if (sclp_rw_event.sclp_send_mask & EvTyp_Msg_Mask) if (sclp_rw_event.sclp_send_mask & EvTyp_Msg_Mask)
...@@ -472,16 +459,13 @@ sclp_emit_buffer(struct sclp_buffer *buffer, ...@@ -472,16 +459,13 @@ sclp_emit_buffer(struct sclp_buffer *buffer,
else if (sclp_rw_event.sclp_send_mask & EvTyp_PMsgCmd_Mask) else if (sclp_rw_event.sclp_send_mask & EvTyp_PMsgCmd_Mask)
/* Use write priority message */ /* Use write priority message */
sccb->msg_buf.header.type = EvTyp_PMsgCmd; sccb->msg_buf.header.type = EvTyp_PMsgCmd;
else { else
if (callback != NULL) return -ENOSYS;
callback(buffer, -ENOSYS);
return;
}
buffer->request.command = SCLP_CMDW_WRITEDATA; buffer->request.command = SCLP_CMDW_WRITEDATA;
buffer->request.status = SCLP_REQ_FILLED; buffer->request.status = SCLP_REQ_FILLED;
buffer->request.callback = sclp_writedata_callback; buffer->request.callback = sclp_writedata_callback;
buffer->request.callback_data = buffer; buffer->request.callback_data = buffer;
buffer->request.sccb = sccb; buffer->request.sccb = sccb;
buffer->callback = callback; buffer->callback = callback;
sclp_add_request(&buffer->request); return sclp_add_request(&buffer->request);
} }
...@@ -12,7 +12,6 @@ ...@@ -12,7 +12,6 @@
#define __SCLP_RW_H__ #define __SCLP_RW_H__
#include <linux/list.h> #include <linux/list.h>
#include <linux/timer.h>
struct mto { struct mto {
u16 length; u16 length;
...@@ -74,7 +73,6 @@ struct sclp_buffer { ...@@ -74,7 +73,6 @@ struct sclp_buffer {
char *current_line; char *current_line;
int current_length; int current_length;
int retry_count; 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;
...@@ -90,7 +88,7 @@ struct sclp_buffer *sclp_make_buffer(void *, unsigned short, unsigned short); ...@@ -90,7 +88,7 @@ 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 unsigned char *, int); 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_columns(struct sclp_buffer *, unsigned short);
void sclp_set_htab(struct sclp_buffer *, unsigned short); void sclp_set_htab(struct sclp_buffer *, unsigned short);
int sclp_chars_in_buffer(struct sclp_buffer *); int sclp_chars_in_buffer(struct sclp_buffer *);
......
...@@ -255,25 +255,22 @@ static void ...@@ -255,25 +255,22 @@ static void
sclp_ttybuf_callback(struct sclp_buffer *buffer, int rc) sclp_ttybuf_callback(struct sclp_buffer *buffer, int rc)
{ {
unsigned long flags; unsigned long flags;
struct sclp_buffer *next;
void *page; void *page;
/* Ignore return code - because tty-writes aren't critical, do {
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); /* Remove buffer from outqueue */
/* Remove buffer from outqueue */ list_del(&buffer->list);
list_del(&buffer->list); sclp_tty_buffer_count--;
sclp_tty_buffer_count--; list_add_tail((struct list_head *) page, &sclp_tty_pages);
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. */ buffer = NULL;
next = NULL; if (!list_empty(&sclp_tty_outqueue))
if (!list_empty(&sclp_tty_outqueue)) buffer = list_entry(sclp_tty_outqueue.next,
next = list_entry(sclp_tty_outqueue.next, struct sclp_buffer, list);
struct sclp_buffer, list); spin_unlock_irqrestore(&sclp_tty_lock, flags);
spin_unlock_irqrestore(&sclp_tty_lock, flags); } while (buffer && sclp_emit_buffer(buffer, sclp_ttybuf_callback));
if (next != NULL)
sclp_emit_buffer(next, sclp_ttybuf_callback);
wake_up(&sclp_tty_waitq); wake_up(&sclp_tty_waitq);
/* check if the tty needs a wake up call */ /* check if the tty needs a wake up call */
if (sclp_tty != NULL) { if (sclp_tty != NULL) {
...@@ -286,14 +283,17 @@ __sclp_ttybuf_emit(struct sclp_buffer *buffer) ...@@ -286,14 +283,17 @@ __sclp_ttybuf_emit(struct sclp_buffer *buffer)
{ {
unsigned long flags; unsigned long flags;
int count; int count;
int rc;
spin_lock_irqsave(&sclp_tty_lock, flags); spin_lock_irqsave(&sclp_tty_lock, flags);
list_add_tail(&buffer->list, &sclp_tty_outqueue); list_add_tail(&buffer->list, &sclp_tty_outqueue);
count = sclp_tty_buffer_count++; count = sclp_tty_buffer_count++;
spin_unlock_irqrestore(&sclp_tty_lock, flags); spin_unlock_irqrestore(&sclp_tty_lock, flags);
if (count)
if (count == 0) return;
sclp_emit_buffer(buffer, sclp_ttybuf_callback); rc = sclp_emit_buffer(buffer, sclp_ttybuf_callback);
if (rc)
sclp_ttybuf_callback(buffer, rc);
} }
/* /*
......
...@@ -42,7 +42,6 @@ struct sclp_vt220_request { ...@@ -42,7 +42,6 @@ struct sclp_vt220_request {
struct list_head list; struct list_head list;
struct sclp_req sclp_req; struct sclp_req sclp_req;
int retry_count; int retry_count;
struct timer_list retry_timer;
}; };
/* VT220 SCCB */ /* VT220 SCCB */
...@@ -96,7 +95,7 @@ static int sclp_vt220_initialized = 0; ...@@ -96,7 +95,7 @@ static int sclp_vt220_initialized = 0;
static int sclp_vt220_flush_later; static int sclp_vt220_flush_later;
static void sclp_vt220_receiver_fn(struct evbuf_header *evbuf); 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); static void sclp_vt220_emit_current(void);
/* Registration structure for our interest in SCLP event buffers */ /* Registration structure for our interest in SCLP event buffers */
...@@ -116,25 +115,24 @@ static void ...@@ -116,25 +115,24 @@ static void
sclp_vt220_process_queue(struct sclp_vt220_request *request) sclp_vt220_process_queue(struct sclp_vt220_request *request)
{ {
unsigned long flags; unsigned long flags;
struct sclp_vt220_request *next;
void *page; void *page;
/* Put buffer back to list of empty buffers */ do {
page = request->sclp_req.sccb; /* Put buffer back to list of empty buffers */
spin_lock_irqsave(&sclp_vt220_lock, flags); page = request->sclp_req.sccb;
/* Move request from outqueue to empty queue */ spin_lock_irqsave(&sclp_vt220_lock, flags);
list_del(&request->list); /* Move request from outqueue to empty queue */
sclp_vt220_outqueue_count--; list_del(&request->list);
list_add_tail((struct list_head *) page, &sclp_vt220_empty); sclp_vt220_outqueue_count--;
/* Check if there is a pending buffer on the out queue. */ list_add_tail((struct list_head *) page, &sclp_vt220_empty);
next = NULL; /* Check if there is a pending buffer on the out queue. */
if (!list_empty(&sclp_vt220_outqueue)) request = NULL;
next = list_entry(sclp_vt220_outqueue.next, if (!list_empty(&sclp_vt220_outqueue))
struct sclp_vt220_request, list); request = list_entry(sclp_vt220_outqueue.next,
spin_unlock_irqrestore(&sclp_vt220_lock, flags); struct sclp_vt220_request, list);
if (next != NULL) spin_unlock_irqrestore(&sclp_vt220_lock, flags);
__sclp_vt220_emit(next); } while (request && __sclp_vt220_emit(request));
else if (sclp_vt220_flush_later) if (request == NULL && sclp_vt220_flush_later)
sclp_vt220_emit_current(); sclp_vt220_emit_current();
wake_up(&sclp_vt220_waitq); wake_up(&sclp_vt220_waitq);
/* Check if the tty needs a wake up call */ /* Check if the tty needs a wake up call */
...@@ -143,25 +141,7 @@ sclp_vt220_process_queue(struct sclp_vt220_request *request) ...@@ -143,25 +141,7 @@ sclp_vt220_process_queue(struct sclp_vt220_request *request)
} }
} }
/* #define SCLP_BUFFER_MAX_RETRY 1
* 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
/* /*
* Callback through which the result of a write request is reported by the * 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) ...@@ -189,29 +169,26 @@ sclp_vt220_callback(struct sclp_req *request, void *data)
break; break;
case 0x0340: /* Contained SCLP equipment check */ 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; break;
/* Remove processed buffers and requeue rest */ /* Remove processed buffers and requeue rest */
if (sclp_remove_processed((struct sccb_header *) sccb) > 0) { if (sclp_remove_processed((struct sccb_header *) sccb) > 0) {
/* Not all buffers were processed */ /* Not all buffers were processed */
sccb->header.response_code = 0x0000; sccb->header.response_code = 0x0000;
vt220_request->sclp_req.status = SCLP_REQ_FILLED; vt220_request->sclp_req.status = SCLP_REQ_FILLED;
sclp_add_request(request); if (sclp_add_request(request) == 0)
return; return;
} }
break; break;
case 0x0040: /* SCLP equipment check */ case 0x0040: /* SCLP equipment check */
if (vt220_request->retry_count++ > SCLP_BUFFER_MAX_RETRY) if (++vt220_request->retry_count > SCLP_BUFFER_MAX_RETRY)
break; break;
/* Wait some time, then retry request */ sccb->header.response_code = 0x0000;
vt220_request->retry_timer.function = sclp_vt220_retry; vt220_request->sclp_req.status = SCLP_REQ_FILLED;
vt220_request->retry_timer.data = if (sclp_add_request(request) == 0)
(unsigned long) vt220_request; return;
vt220_request->retry_timer.expires = break;
jiffies + SCLP_BUFFER_RETRY_INTERVAL*HZ;
add_timer(&vt220_request->retry_timer);
return;
default: default:
break; break;
...@@ -220,22 +197,22 @@ sclp_vt220_callback(struct sclp_req *request, void *data) ...@@ -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) __sclp_vt220_emit(struct sclp_vt220_request *request)
{ {
if (!(sclp_vt220_register.sclp_send_mask & EvTyp_VT220Msg_Mask)) { if (!(sclp_vt220_register.sclp_send_mask & EvTyp_VT220Msg_Mask)) {
request->sclp_req.status = SCLP_REQ_FAILED; request->sclp_req.status = SCLP_REQ_FAILED;
sclp_vt220_callback(&request->sclp_req, (void *) request); return -EIO;
return;
} }
request->sclp_req.command = SCLP_CMDW_WRITEDATA; request->sclp_req.command = SCLP_CMDW_WRITEDATA;
request->sclp_req.status = SCLP_REQ_FILLED; request->sclp_req.status = SCLP_REQ_FILLED;
request->sclp_req.callback = sclp_vt220_callback; request->sclp_req.callback = sclp_vt220_callback;
request->sclp_req.callback_data = (void *) request; 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) ...@@ -253,12 +230,12 @@ sclp_vt220_emit(struct sclp_vt220_request *request)
spin_unlock_irqrestore(&sclp_vt220_lock, flags); spin_unlock_irqrestore(&sclp_vt220_lock, flags);
/* Emit only the first buffer immediately - callback takes care of /* Emit only the first buffer immediately - callback takes care of
* the rest */ * the rest */
if (count == 0) if (count == 0 && __sclp_vt220_emit(request))
__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 static void
sclp_vt220_emit_current(void) sclp_vt220_emit_current(void)
...@@ -300,7 +277,6 @@ sclp_vt220_initialize_page(void *page) ...@@ -300,7 +277,6 @@ sclp_vt220_initialize_page(void *page)
/* Place request structure at end of page */ /* Place request structure at end of page */
request = ((struct sclp_vt220_request *) request = ((struct sclp_vt220_request *)
((addr_t) page + PAGE_SIZE)) - 1; ((addr_t) page + PAGE_SIZE)) - 1;
init_timer(&request->retry_timer);
request->retry_count = 0; request->retry_count = 0;
request->sclp_req.sccb = page; request->sclp_req.sccb = page;
/* SCCB goes at start of 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