Commit 42e093a1 authored by Cornelia Huck's avatar Cornelia Huck Committed by Linus Torvalds

[PATCH] s390: common i/o layer

Common i/o layer changes:
 - Fix error handling in io_subchannel_register.
 - Fix __MAX_SUBCHANNELS limit checking.
 - Clear slow_subchannel structure after kmalloc.
 - Update ssd_info if a different device appears at an already known
   subchannel to get the correct set of chpids.
 - Avoid struct initializers to reduce stack usage of ccwgroup_create,
   readall_cmb and io_subchannel_recog,
 - Setup fields in pmcw in each retry because msch might fail and a stsch
   overwrites them.
 - Prevent irq_exit() in cio_tpi from calling do_softirq by adding a
   local_bh_disable/__local_bh_enable pair.
 - Retry sense id after receiving an unsolicited interrupt.
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 929422e9
/* /*
* drivers/s390/cio/blacklist.c * drivers/s390/cio/blacklist.c
* S/390 common I/O routines -- blacklisting of specific devices * S/390 common I/O routines -- blacklisting of specific devices
* $Revision: 1.31 $ * $Revision: 1.33 $
* *
* Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH, * Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
* IBM Corporation * IBM Corporation
...@@ -229,7 +229,7 @@ s390_redo_validation (void) ...@@ -229,7 +229,7 @@ s390_redo_validation (void)
unsigned int irq; unsigned int irq;
CIO_TRACE_EVENT (0, "redoval"); CIO_TRACE_EVENT (0, "redoval");
for (irq = 0; irq <= __MAX_SUBCHANNELS; irq++) { for (irq = 0; irq < __MAX_SUBCHANNELS; irq++) {
int ret; int ret;
struct subchannel *sch; struct subchannel *sch;
......
/* /*
* drivers/s390/cio/ccwgroup.c * drivers/s390/cio/ccwgroup.c
* bus driver for ccwgroup * bus driver for ccwgroup
* $Revision: 1.28 $ * $Revision: 1.29 $
* *
* Copyright (C) 2002 IBM Deutschland Entwicklung GmbH, * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
* IBM Corporation * IBM Corporation
...@@ -191,14 +191,12 @@ ccwgroup_create(struct device *root, ...@@ -191,14 +191,12 @@ ccwgroup_create(struct device *root,
gdev->cdev[i]->dev.driver_data = gdev; gdev->cdev[i]->dev.driver_data = gdev;
del_drvdata = 1; del_drvdata = 1;
*gdev = (struct ccwgroup_device) { gdev->creator_id = creator_id;
.creator_id = creator_id, gdev->count = argc;
.count = argc, gdev->dev = (struct device ) {
.dev = { .bus = &ccwgroup_bus_type,
.bus = &ccwgroup_bus_type, .parent = root,
.parent = root, .release = ccwgroup_release,
.release = ccwgroup_release,
},
}; };
snprintf (gdev->dev.bus_id, BUS_ID_SIZE, "%s", snprintf (gdev->dev.bus_id, BUS_ID_SIZE, "%s",
......
/* /*
* 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.123 $ * $Revision: 1.128 $
* *
* Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH, * Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
* IBM Corporation * IBM Corporation
...@@ -141,6 +141,7 @@ cio_tpi(void) ...@@ -141,6 +141,7 @@ cio_tpi(void)
sch = (struct subchannel *)(unsigned long)tpi_info->intparm; sch = (struct subchannel *)(unsigned long)tpi_info->intparm;
if (!sch) if (!sch)
return 1; return 1;
local_bh_disable();
irq_enter (); irq_enter ();
spin_lock(&sch->lock); spin_lock(&sch->lock);
memcpy (&sch->schib.scsw, &irb->scsw, sizeof (struct scsw)); memcpy (&sch->schib.scsw, &irb->scsw, sizeof (struct scsw));
...@@ -148,6 +149,7 @@ cio_tpi(void) ...@@ -148,6 +149,7 @@ cio_tpi(void)
sch->driver->irq(&sch->dev); sch->driver->irq(&sch->dev);
spin_unlock(&sch->lock); spin_unlock(&sch->lock);
irq_exit (); irq_exit ();
__local_bh_enable();
return 1; return 1;
} }
...@@ -409,10 +411,10 @@ cio_enable_subchannel (struct subchannel *sch, unsigned int isc) ...@@ -409,10 +411,10 @@ cio_enable_subchannel (struct subchannel *sch, unsigned int isc)
if (ccode) if (ccode)
return -ENODEV; return -ENODEV;
sch->schib.pmcw.ena = 1;
sch->schib.pmcw.isc = isc;
sch->schib.pmcw.intparm = (__u32)(unsigned long)sch;
for (retry = 5, ret = 0; retry > 0; retry--) { for (retry = 5, ret = 0; retry > 0; retry--) {
sch->schib.pmcw.ena = 1;
sch->schib.pmcw.isc = isc;
sch->schib.pmcw.intparm = (__u32)(unsigned long)sch;
ret = cio_modify(sch); ret = cio_modify(sch);
if (ret == -ENODEV) if (ret == -ENODEV)
break; break;
...@@ -463,9 +465,8 @@ cio_disable_subchannel (struct subchannel *sch) ...@@ -463,9 +465,8 @@ cio_disable_subchannel (struct subchannel *sch)
*/ */
return -EBUSY; return -EBUSY;
sch->schib.pmcw.ena = 0;
for (retry = 5, ret = 0; retry > 0; retry--) { for (retry = 5, ret = 0; retry > 0; retry--) {
sch->schib.pmcw.ena = 0;
ret = cio_modify(sch); ret = cio_modify(sch);
if (ret == -ENODEV) if (ret == -ENODEV)
break; break;
......
/* /*
* linux/drivers/s390/cio/cmf.c ($Revision: 1.15 $) * linux/drivers/s390/cio/cmf.c ($Revision: 1.16 $)
* *
* Linux on zSeries Channel Measurement Facility support * Linux on zSeries Channel Measurement Facility support
* *
...@@ -526,29 +526,26 @@ readall_cmb (struct ccw_device *cdev, struct cmbdata *data) ...@@ -526,29 +526,26 @@ readall_cmb (struct ccw_device *cdev, struct cmbdata *data)
time = get_clock() - cdev->private->cmb_start_time; time = get_clock() - cdev->private->cmb_start_time;
spin_unlock_irqrestore(cdev->ccwlock, flags); spin_unlock_irqrestore(cdev->ccwlock, flags);
*data = (struct cmbdata) { memset(data, sizeof(struct cmbdata), 0);
/* we only know values before device_busy_time */
.size = offsetof(struct cmbdata, device_busy_time), /* we only know values before device_busy_time */
data->size = offsetof(struct cmbdata, device_busy_time);
/* conver to nanoseconds */
.elapsed_time = (time * 1000) >> 12, /* convert to nanoseconds */
data->elapsed_time = (time * 1000) >> 12;
/* copy data to new structure */
.ssch_rsch_count = cmb.ssch_rsch_count, /* copy data to new structure */
.sample_count = cmb.sample_count, data->ssch_rsch_count = cmb.ssch_rsch_count;
data->sample_count = cmb.sample_count;
/* time fields are converted to nanoseconds while copying */
.device_connect_time /* time fields are converted to nanoseconds while copying */
= time_to_nsec(cmb.device_connect_time), data->device_connect_time = time_to_nsec(cmb.device_connect_time);
.function_pending_time data->function_pending_time = time_to_nsec(cmb.function_pending_time);
= time_to_nsec(cmb.function_pending_time), data->device_disconnect_time = time_to_nsec(cmb.device_disconnect_time);
.device_disconnect_time data->control_unit_queuing_time
= time_to_nsec(cmb.device_disconnect_time), = time_to_nsec(cmb.control_unit_queuing_time);
.control_unit_queuing_time data->device_active_only_time
= time_to_nsec(cmb.control_unit_queuing_time), = time_to_nsec(cmb.device_active_only_time);
.device_active_only_time
= time_to_nsec(cmb.device_active_only_time),
};
return 0; return 0;
} }
...@@ -739,33 +736,29 @@ readall_cmbe (struct ccw_device *cdev, struct cmbdata *data) ...@@ -739,33 +736,29 @@ readall_cmbe (struct ccw_device *cdev, struct cmbdata *data)
time = get_clock() - cdev->private->cmb_start_time; time = get_clock() - cdev->private->cmb_start_time;
spin_unlock_irqrestore(cdev->ccwlock, flags); spin_unlock_irqrestore(cdev->ccwlock, flags);
*data = (struct cmbdata) { memset (data, sizeof(struct cmbdata), 0);
/* we only know values before device_busy_time */
.size = offsetof(struct cmbdata, device_busy_time), /* we only know values before device_busy_time */
data->size = offsetof(struct cmbdata, device_busy_time);
/* conver to nanoseconds */
.elapsed_time = (time * 1000) >> 12, /* conver to nanoseconds */
data->elapsed_time = (time * 1000) >> 12;
/* copy data to new structure */
.ssch_rsch_count = cmb.ssch_rsch_count, /* copy data to new structure */
.sample_count = cmb.sample_count, data->ssch_rsch_count = cmb.ssch_rsch_count;
data->sample_count = cmb.sample_count;
/* time fields are converted to nanoseconds while copying */
.device_connect_time /* time fields are converted to nanoseconds while copying */
= time_to_nsec(cmb.device_connect_time), data->device_connect_time = time_to_nsec(cmb.device_connect_time);
.function_pending_time data->function_pending_time = time_to_nsec(cmb.function_pending_time);
= time_to_nsec(cmb.function_pending_time), data->device_disconnect_time = time_to_nsec(cmb.device_disconnect_time);
.device_disconnect_time data->control_unit_queuing_time
= time_to_nsec(cmb.device_disconnect_time), = time_to_nsec(cmb.control_unit_queuing_time);
.control_unit_queuing_time data->device_active_only_time
= time_to_nsec(cmb.control_unit_queuing_time), = time_to_nsec(cmb.device_active_only_time);
.device_active_only_time data->device_busy_time = time_to_nsec(cmb.device_busy_time);
= time_to_nsec(cmb.device_active_only_time), data->initial_command_response_time
.device_busy_time = time_to_nsec(cmb.initial_command_response_time);
= time_to_nsec(cmb.device_busy_time),
.initial_command_response_time
= time_to_nsec(cmb.initial_command_response_time),
};
return 0; return 0;
} }
......
/* /*
* drivers/s390/cio/css.c * drivers/s390/cio/css.c
* driver for channel subsystem * driver for channel subsystem
* $Revision: 1.80 $ * $Revision: 1.82 $
* *
* Copyright (C) 2002 IBM Deutschland Entwicklung GmbH, * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
* IBM Corporation * IBM Corporation
...@@ -278,7 +278,7 @@ css_rescan_devices(void) ...@@ -278,7 +278,7 @@ css_rescan_devices(void)
{ {
int irq, ret; int irq, ret;
for (irq = 0; irq <= __MAX_SUBCHANNELS; irq++) { for (irq = 0; irq < __MAX_SUBCHANNELS; irq++) {
ret = css_evaluate_subchannel(irq, 1); ret = css_evaluate_subchannel(irq, 1);
/* No more memory. It doesn't make sense to continue. No /* No more memory. It doesn't make sense to continue. No
* panic because this can happen in midflight and just * panic because this can happen in midflight and just
...@@ -521,6 +521,7 @@ css_enqueue_subchannel_slow(unsigned long schid) ...@@ -521,6 +521,7 @@ css_enqueue_subchannel_slow(unsigned long schid)
new_slow_sch = kmalloc(sizeof(struct slow_subchannel), GFP_ATOMIC); new_slow_sch = kmalloc(sizeof(struct slow_subchannel), GFP_ATOMIC);
if (!new_slow_sch) if (!new_slow_sch)
return -ENOMEM; return -ENOMEM;
memset(new_slow_sch, sizeof(struct slow_subchannel), 0);
new_slow_sch->schid = schid; new_slow_sch->schid = schid;
spin_lock_irqsave(&slow_subchannel_lock, flags); spin_lock_irqsave(&slow_subchannel_lock, flags);
list_add_tail(&new_slow_sch->slow_list, &slow_subchannels_head); list_add_tail(&new_slow_sch->slow_list, &slow_subchannels_head);
......
/* /*
* drivers/s390/cio/device.c * drivers/s390/cio/device.c
* bus driver for ccw devices * bus driver for ccw devices
* $Revision: 1.120 $ * $Revision: 1.124 $
* *
* Copyright (C) 2002 IBM Deutschland Entwicklung GmbH, * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
* IBM Corporation * IBM Corporation
...@@ -540,6 +540,8 @@ get_disc_ccwdev_by_devno(unsigned int devno, struct ccw_device *sibling) ...@@ -540,6 +540,8 @@ get_disc_ccwdev_by_devno(unsigned int devno, struct ccw_device *sibling)
return cdev; return cdev;
} }
extern int css_get_ssd_info(struct subchannel *sch);
void void
ccw_device_do_unreg_rereg(void *data) ccw_device_do_unreg_rereg(void *data)
{ {
...@@ -581,6 +583,8 @@ ccw_device_do_unreg_rereg(void *data) ...@@ -581,6 +583,8 @@ ccw_device_do_unreg_rereg(void *data)
device_unregister(&other_sch->dev); device_unregister(&other_sch->dev);
} }
} }
/* Update ssd info here. */
css_get_ssd_info(sch);
cdev->private->devno = sch->schib.pmcw.dev; cdev->private->devno = sch->schib.pmcw.dev;
} else } else
need_rename = 0; need_rename = 0;
...@@ -633,7 +637,8 @@ io_subchannel_register(void *data) ...@@ -633,7 +637,8 @@ io_subchannel_register(void *data)
sch->dev.driver_data = 0; sch->dev.driver_data = 0;
kfree (cdev->private); kfree (cdev->private);
kfree (cdev); kfree (cdev);
goto out; put_device(&sch->dev);
return;
} }
ret = subchannel_add_files(cdev->dev.parent); ret = subchannel_add_files(cdev->dev.parent);
...@@ -707,18 +712,19 @@ static int ...@@ -707,18 +712,19 @@ 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;
struct ccw_device_private *priv;
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;
*cdev->private = (struct ccw_device_private) { /* Init private data. */
.devno = sch->schib.pmcw.dev, priv = cdev->private;
.irq = sch->irq, priv->devno = sch->schib.pmcw.dev;
.state = DEV_STATE_NOT_OPER, priv->irq = sch->irq;
.cmb_list = LIST_HEAD_INIT(cdev->private->cmb_list), priv->state = DEV_STATE_NOT_OPER;
}; INIT_LIST_HEAD(&priv->cmb_list);
init_waitqueue_head(&cdev->private->wait_q); init_waitqueue_head(&priv->wait_q);
init_timer(&cdev->private->timer); init_timer(&priv->timer);
/* Set an initial name for the device. */ /* Set an initial name for the device. */
snprintf (cdev->dev.bus_id, BUS_ID_SIZE, "0.0.%04x", snprintf (cdev->dev.bus_id, BUS_ID_SIZE, "0.0.%04x",
......
...@@ -995,6 +995,7 @@ device_trigger_reprobe(struct subchannel *sch) ...@@ -995,6 +995,7 @@ device_trigger_reprobe(struct subchannel *sch)
if ((sch->lpm & (sch->lpm - 1)) != 0) if ((sch->lpm & (sch->lpm - 1)) != 0)
sch->schib.pmcw.mp = 1; sch->schib.pmcw.mp = 1;
sch->schib.pmcw.intparm = (__u32)(unsigned long)sch; sch->schib.pmcw.intparm = (__u32)(unsigned long)sch;
/* We should also udate ssd info, but this has to wait. */
ccw_device_start_id(cdev, 0); ccw_device_start_id(cdev, 0);
spin_unlock_irqrestore(&sch->lock, flags); spin_unlock_irqrestore(&sch->lock, flags);
} }
......
...@@ -303,10 +303,10 @@ ccw_device_sense_id_irq(struct ccw_device *cdev, enum dev_event dev_event) ...@@ -303,10 +303,10 @@ ccw_device_sense_id_irq(struct ccw_device *cdev, enum dev_event dev_event)
sch = to_subchannel(cdev->dev.parent); sch = to_subchannel(cdev->dev.parent);
irb = (struct irb *) __LC_IRB; irb = (struct irb *) __LC_IRB;
/* Retry sense id for cc=1. */ /* Retry sense id, if needed. */
if (irb->scsw.stctl == if (irb->scsw.stctl ==
(SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) { (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
if (irb->scsw.cc == 1) { if ((irb->scsw.cc == 1) || !irb->scsw.actl) {
ret = __ccw_device_sense_id_start(cdev); ret = __ccw_device_sense_id_start(cdev);
if (ret && ret != -EBUSY) if (ret && ret != -EBUSY)
ccw_device_sense_id_done(cdev, ret); ccw_device_sense_id_done(cdev, ret);
......
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