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

[PATCH] s390: common i/o layer.

 - Fix two memory leaks.
 - Clear pending status in cio_enable_subchannel.
 - Don't call device_unregister from interrupt context.
 - Fix refcounting problems on static device structures for the ccw console.
 - Delete timeouts for qdio after successful startup.
parent 91cb2bb4
/*
* drivers/s390/cio/chsc.c
* S/390 common I/O routines -- channel subsystem call
* $Revision: 1.73 $
* $Revision: 1.74 $
*
* Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
* IBM Corporation
......@@ -206,6 +206,7 @@ chsc_get_sch_descriptions(void)
if (!page)
return -ENOMEM;
err = 0;
for (irq = 0; irq <= highest_subchannel; irq++) {
/*
* retrieve information for each sch
......@@ -222,13 +223,14 @@ chsc_get_sch_descriptions(void)
"not work\n", err);
cio_chsc_err_msg = 1;
}
return err;
goto out;
}
clear_page(page);
}
cio_chsc_desc_avail = 1;
out:
free_page((unsigned long)page);
return 0;
return err;
}
__initcall(chsc_get_sch_descriptions);
......@@ -428,7 +430,7 @@ s390_process_res_acc (u8 chpid, __u16 fla, u32 fla_mask)
ret = css_probe_device(irq);
if (ret == -ENXIO)
/* We're through */
return;
break;
continue;
}
......
/*
* drivers/s390/cio/cio.c
* S/390 common I/O routines -- low level i/o calls
* $Revision: 1.98 $
* $Revision: 1.100 $
*
* Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
* IBM Corporation
......@@ -444,6 +444,11 @@ cio_enable_subchannel (struct subchannel *sch, unsigned int isc)
if (sch->schib.pmcw.ena)
break;
}
if (ret == -EBUSY) {
struct irb irb;
if (tsch(sch->irq, &irb) != 0)
break;
}
}
sprintf (dbf_txt, "ret:%d", ret);
CIO_TRACE_EVENT (2, dbf_txt);
......
/*
* drivers/s390/cio/device.c
* bus driver for ccw devices
* $Revision: 1.58 $
* $Revision: 1.60 $
*
* Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
* IBM Corporation
......@@ -434,6 +434,13 @@ ccw_device_register(struct ccw_device *cdev)
return ret;
}
void
ccw_device_unregister(void *data)
{
device_unregister((struct device *)data);
}
static void
ccw_device_release(struct device *dev)
{
......@@ -513,17 +520,11 @@ io_subchannel_recog_done(struct ccw_device *cdev)
wake_up(&ccw_device_init_wq);
}
static void
static int
io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch)
{
int rc;
if (!get_device(&sch->dev)) {
if (cdev->dev.release)
cdev->dev.release(&cdev->dev);
return;
}
sch->dev.driver_data = cdev;
sch->driver = &io_subchannel_driver;
cdev->ccwlock = &sch->lock;
......@@ -540,9 +541,6 @@ io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch)
snprintf (cdev->dev.bus_id, DEVICE_ID_SIZE, "0:%04x",
sch->schib.pmcw.dev);
/* Do first half of device_register. */
device_initialize(&cdev->dev);
/* Increase counter of devices currently in recognition. */
atomic_inc(&ccw_device_init_count);
......@@ -551,13 +549,10 @@ io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch)
rc = ccw_device_recognition(cdev);
spin_unlock_irq(cdev->ccwlock);
if (rc) {
sch->dev.driver_data = 0;
put_device(&sch->dev);
if (cdev->dev.release)
cdev->dev.release(&cdev->dev);
if (atomic_dec_and_test(&ccw_device_init_count))
wake_up(&ccw_device_init_wq);
}
return rc;
}
static int
......@@ -565,6 +560,7 @@ io_subchannel_probe (struct device *pdev)
{
struct subchannel *sch;
struct ccw_device *cdev;
int rc;
sch = to_subchannel(pdev);
if (sch->dev.driver_data) {
......@@ -573,8 +569,20 @@ io_subchannel_probe (struct device *pdev)
* Register it and exit. This happens for all early
* device, e.g. the console.
*/
ccw_device_register(sch->dev.driver_data);
cdev = sch->dev.driver_data;
device_initialize(&cdev->dev);
ccw_device_register(cdev);
subchannel_add_files(&sch->dev);
/*
* Check if the device is already online. If it is
* the reference count needs to be corrected
* (see ccw_device_online and css_init_done for the
* ugly details).
*/
if (cdev->private->state != DEV_STATE_NOT_OPER &&
cdev->private->state != DEV_STATE_OFFLINE &&
cdev->private->state != DEV_STATE_BOXED)
get_device(&cdev->dev);
return 0;
}
cdev = kmalloc (sizeof(*cdev), GFP_KERNEL);
......@@ -592,7 +600,23 @@ io_subchannel_probe (struct device *pdev)
.parent = pdev,
.release = ccw_device_release,
};
io_subchannel_recog(cdev, to_subchannel(pdev));
/* Do first half of device_register. */
device_initialize(&cdev->dev);
if (!get_device(&sch->dev)) {
if (cdev->dev.release)
cdev->dev.release(&cdev->dev);
return 0;
}
rc = io_subchannel_recog(cdev, to_subchannel(pdev));
if (rc) {
sch->dev.driver_data = 0;
put_device(&sch->dev);
if (cdev->dev.release)
cdev->dev.release(&cdev->dev);
}
return 0;
}
......@@ -604,6 +628,8 @@ static int console_cdev_in_use;
static int
ccw_device_console_enable (struct ccw_device *cdev, struct subchannel *sch)
{
int rc;
/* Initialize the ccw_device structure. */
cdev->dev = (struct device) {
.parent = &sch->dev,
......@@ -613,7 +639,11 @@ ccw_device_console_enable (struct ccw_device *cdev, struct subchannel *sch)
.parent = &css_bus_device,
.bus = &css_bus_type,
};
io_subchannel_recog(cdev, sch);
rc = io_subchannel_recog(cdev, sch);
if (rc)
return rc;
/* Now wait for the async. recognition to come to an end. */
while (!dev_fsm_final_state(cdev))
wait_cons_dev();
......
......@@ -65,6 +65,8 @@ extern struct workqueue_struct *ccw_device_work;
void io_subchannel_recog_done(struct ccw_device *cdev);
void ccw_device_unregister(void *);
int ccw_device_recognition(struct ccw_device *);
int ccw_device_online(struct ccw_device *);
int ccw_device_offline(struct ccw_device *);
......
......@@ -188,7 +188,7 @@ ccw_device_done(struct ccw_device *cdev, int state)
wake_up(&cdev->private->wait_q);
if (state != DEV_STATE_ONLINE)
if (css_init_done && state != DEV_STATE_ONLINE)
put_device (&cdev->dev);
}
......@@ -293,7 +293,7 @@ ccw_device_online(struct ccw_device *cdev)
if (cdev->private->state != DEV_STATE_OFFLINE)
return -EINVAL;
sch = to_subchannel(cdev->dev.parent);
if (!get_device(&cdev->dev))
if (css_init_done && !get_device(&cdev->dev))
return -ENODEV;
if (cio_enable_subchannel(sch, sch->schib.pmcw.isc) != 0) {
/* Couldn't enable the subchannel for i/o. Sick device. */
......@@ -384,7 +384,9 @@ static void
ccw_device_offline_notoper(struct ccw_device *cdev, enum dev_event dev_event)
{
cdev->private->state = DEV_STATE_NOT_OPER;
device_unregister(&cdev->dev);
INIT_WORK(&cdev->private->kick_work,
ccw_device_unregister, (void *) &cdev->dev);
queue_work(ccw_device_work, &cdev->private->kick_work);
wake_up(&cdev->private->wait_q);
}
......@@ -403,8 +405,10 @@ ccw_device_online_notoper(struct ccw_device *cdev, enum dev_event dev_event)
// FIXME: not-oper indication to device driver ?
ccw_device_call_handler(cdev);
}
INIT_WORK(&cdev->private->kick_work,
ccw_device_unregister, (void *) &cdev->dev);
queue_work(ccw_device_work, &cdev->private->kick_work);
wake_up(&cdev->private->wait_q);
device_unregister(&cdev->dev);
}
/*
......
......@@ -55,7 +55,7 @@
#include "ioasm.h"
#include "chsc.h"
#define VERSION_QDIO_C "$Revision: 1.51 $"
#define VERSION_QDIO_C "$Revision: 1.55 $"
/****************** MODULE PARAMETER VARIABLES ********************/
MODULE_AUTHOR("Utz Bacher <utz.bacher@de.ibm.com>");
......@@ -1643,6 +1643,7 @@ qdio_timeout_handler(struct ccw_device *cdev)
default:
BUG();
}
ccw_device_set_timeout(cdev, 0);
wake_up(&cdev->private->wait_q);
}
......@@ -1891,26 +1892,25 @@ tiqdio_check_chsc_availability(void)
result=-EIO;
goto exit;
}
/* 4: request block
* 2: general char
* 512: chsc char */
if ((scsc_area->general_char[1] & 0x00800000) != 0x00800000) {
/* Check for bit 41. */
if ((scsc_area->general_char[1] & 0x00400000) != 0x00400000) {
QDIO_PRINT_WARN("Adapter interruption facility not " \
"installed.\n");
result=-ENOENT;
goto exit;
}
if ((scsc_area->chsc_char[2] & 0x00180000) != 0x00180000) {
/* Check for bits 107 and 108. */
if ((scsc_area->chsc_char[3] & 0x00180000) != 0x00180000) {
QDIO_PRINT_WARN("Set Chan Subsys. Char. & Fast-CHSCs " \
"not available.\n");
result=-ENOENT;
goto exit;
}
/* Check for hydra thin interrupts. */
/* Check for hydra thin interrupts (bit 67). */
hydra_thinints = ((scsc_area->general_char[2] & 0x10000000)
== 0x10000000);
sprintf(dbf_text,"hydra_ti%1x", hydra_thinints);
sprintf(dbf_text,"hydrati%1x", hydra_thinints);
QDIO_DBF_TEXT0(0,setup,dbf_text);
exit:
free_page ((unsigned long) scsc_area);
......@@ -2413,8 +2413,10 @@ qdio_establish_handle_irq(struct ccw_device *cdev, int cstat, int dstat)
QDIO_DBF_TEXT0(0,setup,dbf_text);
QDIO_DBF_TEXT0(0,trace,dbf_text);
if (qdio_establish_irq_check_for_errors(cdev, cstat, dstat))
if (qdio_establish_irq_check_for_errors(cdev, cstat, dstat)) {
ccw_device_set_timeout(cdev, 0);
return;
}
irq_ptr = cdev->private->qdio_data;
......@@ -2439,7 +2441,7 @@ qdio_establish_handle_irq(struct ccw_device *cdev, int cstat, int dstat)
qdio_initialize_set_siga_flags_output(irq_ptr);
qdio_set_state(irq_ptr,QDIO_IRQ_STATE_ESTABLISHED);
ccw_device_set_timeout(cdev, 0);
}
int
......@@ -2698,6 +2700,8 @@ qdio_establish(struct ccw_device *cdev)
"returned %i, next try returned %i\n",
irq_ptr->irq,result,result2);
result=result2;
if (result)
ccw_device_set_timeout(cdev, 0);
}
spin_unlock_irqrestore(get_ccwdev_lock(cdev),saveflags);
......@@ -3000,7 +3004,6 @@ qdio_perf_procfile_read(char *buffer, char **buffer_location, off_t offset,
int buffer_length, int *eof, void *data)
{
int c=0;
int irq;
/* we are always called with buffer_length=4k, so we all
deliver on the first read */
......@@ -3020,7 +3023,7 @@ qdio_perf_procfile_read(char *buffer, char **buffer_location, off_t offset,
perf_stats.siga_ins);
_OUTP_IT("Number of SIGA out's issued : %u\n",
perf_stats.siga_outs);
_OUTP_IT("Number of PCIs caught : %u\n",
_OUTP_IT("Number of PCIs caught : %u\n",
perf_stats.pcis);
_OUTP_IT("Number of adapter interrupts caught : %u\n",
perf_stats.thinints);
......@@ -3037,27 +3040,6 @@ qdio_perf_procfile_read(char *buffer, char **buffer_location, off_t offset,
perf_stats.outbound_cnt);
_OUTP_IT("\n");
/*
* FIXME: Rather use driver_for_each_dev, if we had it.
* I know this loop destroys our layering, but at least gets the
* performance stats out...
*/
for (irq=0;irq <= highest_subchannel; irq++) {
struct qdio_irq *irq_ptr;
struct ccw_device *cdev;
if (!ioinfo[irq])
continue;
cdev = ioinfo[irq]->dev.driver_data;
if (!cdev)
continue;
irq_ptr = cdev->private->qdio_data;
if (!irq_ptr)
continue;
_OUTP_IT("Polling time on irq %4x " \
": %u\n",
irq_ptr->irq,irq_ptr->input_qs[0]->timing.threshold);
}
return c;
}
......
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