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: Sebastian Ott <sebott@linux.vnet.ibm.com> Reviewed-by: Peter Oberparleiter <peter.oberparleiter@linux.vnet.ibm.com> Signed-off-by: Stefan Haberland <stefan.haberland@de.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
This commit is contained in:
parent
f9c87a6f46
commit
f81a49d13b
|
@ -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
|
||||
* if it needs to be started.
|
||||
|
@ -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);
|
||||
if (cqr->status != DASD_CQR_QUEUED)
|
||||
return;
|
||||
/* when device is stopped, return request to previous layer
|
||||
* exception: only the disconnect or unresumed bits are set and the
|
||||
* 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))) {
|
||||
/* if device is not usable return request to upper layer */
|
||||
if (__dasd_device_is_unusable(device, cqr)) {
|
||||
cqr->intrc = -EAGAIN;
|
||||
cqr->status = DASD_CQR_CLEARED;
|
||||
dasd_schedule_device_bh(device);
|
||||
|
|
|
@ -699,7 +699,8 @@ struct dasd_device *dasd_alias_get_start_dev(struct dasd_device *base_device)
|
|||
struct dasd_device, alias_list);
|
||||
spin_unlock_irqrestore(&lcu->lock, flags);
|
||||
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;
|
||||
else
|
||||
return NULL;
|
||||
|
|
Loading…
Reference in New Issue