Commit f81a49d1 authored by Stefan Haberland's avatar Stefan Haberland Committed by Martin Schwidefsky

s390/dasd: fix kernel panic when alias is set offline

The dasd device driver selects which (alias or base) device is used
for a given requests when the request is build. If the chosen alias
device is set offline before the request gets queued to the device
queue the starting function may use device structures that are
already freed. This might lead to a hanging offline process or a
kernel panic.

Add a check to the starting function that returns the request to the
upper layer if the device is already in offline processing.

In addition to that prevent that an alias device that's already in
offline processing gets chosen as start device.
Reviewed-by: default avatarSebastian Ott <sebott@linux.vnet.ibm.com>
Reviewed-by: default avatarPeter Oberparleiter <peter.oberparleiter@linux.vnet.ibm.com>
Signed-off-by: default avatarStefan Haberland <stefan.haberland@de.ibm.com>
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent f9c87a6f
...@@ -1862,6 +1862,33 @@ static void __dasd_device_check_expire(struct dasd_device *device) ...@@ -1862,6 +1862,33 @@ static void __dasd_device_check_expire(struct dasd_device *device)
} }
} }
/*
* return 1 when device is not eligible for IO
*/
static int __dasd_device_is_unusable(struct dasd_device *device,
struct dasd_ccw_req *cqr)
{
int mask = ~(DASD_STOPPED_DC_WAIT | DASD_UNRESUMED_PM);
if (test_bit(DASD_FLAG_OFFLINE, &device->flags)) {
/* dasd is being set offline. */
return 1;
}
if (device->stopped) {
if (device->stopped & mask) {
/* stopped and CQR will not change that. */
return 1;
}
if (!test_bit(DASD_CQR_VERIFY_PATH, &cqr->flags)) {
/* CQR is not able to change device to
* operational. */
return 1;
}
/* CQR required to get device operational. */
}
return 0;
}
/* /*
* Take a look at the first request on the ccw queue and check * Take a look at the first request on the ccw queue and check
* if it needs to be started. * if it needs to be started.
...@@ -1876,13 +1903,8 @@ static void __dasd_device_start_head(struct dasd_device *device) ...@@ -1876,13 +1903,8 @@ static void __dasd_device_start_head(struct dasd_device *device)
cqr = list_entry(device->ccw_queue.next, struct dasd_ccw_req, devlist); cqr = list_entry(device->ccw_queue.next, struct dasd_ccw_req, devlist);
if (cqr->status != DASD_CQR_QUEUED) if (cqr->status != DASD_CQR_QUEUED)
return; return;
/* when device is stopped, return request to previous layer /* if device is not usable return request to upper layer */
* exception: only the disconnect or unresumed bits are set and the if (__dasd_device_is_unusable(device, cqr)) {
* cqr is a path verification request
*/
if (device->stopped &&
!(!(device->stopped & ~(DASD_STOPPED_DC_WAIT | DASD_UNRESUMED_PM))
&& test_bit(DASD_CQR_VERIFY_PATH, &cqr->flags))) {
cqr->intrc = -EAGAIN; cqr->intrc = -EAGAIN;
cqr->status = DASD_CQR_CLEARED; cqr->status = DASD_CQR_CLEARED;
dasd_schedule_device_bh(device); dasd_schedule_device_bh(device);
......
...@@ -699,7 +699,8 @@ struct dasd_device *dasd_alias_get_start_dev(struct dasd_device *base_device) ...@@ -699,7 +699,8 @@ struct dasd_device *dasd_alias_get_start_dev(struct dasd_device *base_device)
struct dasd_device, alias_list); struct dasd_device, alias_list);
spin_unlock_irqrestore(&lcu->lock, flags); spin_unlock_irqrestore(&lcu->lock, flags);
alias_priv = (struct dasd_eckd_private *) alias_device->private; alias_priv = (struct dasd_eckd_private *) alias_device->private;
if ((alias_priv->count < private->count) && !alias_device->stopped) if ((alias_priv->count < private->count) && !alias_device->stopped &&
!test_bit(DASD_FLAG_OFFLINE, &alias_device->flags))
return alias_device; return alias_device;
else else
return NULL; return NULL;
......
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