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