Skip to content

Commit

Permalink
[S390] dasd: fix online/offline race
Browse files Browse the repository at this point in the history
Setting a DASD online and offline in quick succession may cause
a kernel panic or let the chhccwdev command wait forever.
The Online process is split into two parts. After the first part
is finished the offline process may be called. This may result
in a situation where the second online processing part tries to
set the DASD offline as well.
Use a mutex to protect online and offline against each other.
Also correct some checking.

Signed-off-by: Stefan Haberland <stefan.haberland@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
  • Loading branch information
Stefan Haberland authored and Martin Schwidefsky committed Feb 26, 2010
1 parent 22e0a04 commit 9eb2512
Show file tree
Hide file tree
Showing 3 changed files with 16 additions and 8 deletions.
22 changes: 14 additions & 8 deletions drivers/s390/block/dasd.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include <linux/buffer_head.h>
#include <linux/hdreg.h>
#include <linux/async.h>
#include <linux/mutex.h>

#include <asm/ccwdev.h>
#include <asm/ebcdic.h>
Expand Down Expand Up @@ -112,6 +113,7 @@ struct dasd_device *dasd_alloc_device(void)
INIT_WORK(&device->restore_device, do_restore_device);
device->state = DASD_STATE_NEW;
device->target = DASD_STATE_NEW;
mutex_init(&device->state_mutex);

return device;
}
Expand Down Expand Up @@ -484,10 +486,8 @@ static void dasd_change_state(struct dasd_device *device)
if (rc)
device->target = device->state;

if (device->state == device->target) {
if (device->state == device->target)
wake_up(&dasd_init_waitq);
dasd_put_device(device);
}

/* let user-space know that the device status changed */
kobject_uevent(&device->cdev->dev.kobj, KOBJ_CHANGE);
Expand All @@ -502,7 +502,9 @@ static void dasd_change_state(struct dasd_device *device)
static void do_kick_device(struct work_struct *work)
{
struct dasd_device *device = container_of(work, struct dasd_device, kick_work);
mutex_lock(&device->state_mutex);
dasd_change_state(device);
mutex_unlock(&device->state_mutex);
dasd_schedule_device_bh(device);
dasd_put_device(device);
}
Expand Down Expand Up @@ -539,18 +541,19 @@ void dasd_restore_device(struct dasd_device *device)
void dasd_set_target_state(struct dasd_device *device, int target)
{
dasd_get_device(device);
mutex_lock(&device->state_mutex);
/* If we are in probeonly mode stop at DASD_STATE_READY. */
if (dasd_probeonly && target > DASD_STATE_READY)
target = DASD_STATE_READY;
if (device->target != target) {
if (device->state == target) {
if (device->state == target)
wake_up(&dasd_init_waitq);
dasd_put_device(device);
}
device->target = target;
}
if (device->state != device->target)
dasd_change_state(device);
mutex_unlock(&device->state_mutex);
dasd_put_device(device);
}

/*
Expand Down Expand Up @@ -1692,7 +1695,6 @@ int dasd_cancel_req(struct dasd_ccw_req *cqr)
cqr, rc);
} else {
cqr->stopclk = get_clock();
rc = 1;
}
break;
default: /* already finished or clear pending - do nothing */
Expand Down Expand Up @@ -2170,9 +2172,13 @@ static void dasd_flush_request_queue(struct dasd_block *block)
static int dasd_open(struct block_device *bdev, fmode_t mode)
{
struct dasd_block *block = bdev->bd_disk->private_data;
struct dasd_device *base = block->base;
struct dasd_device *base;
int rc;

if (!block)
return -ENODEV;

base = block->base;
atomic_inc(&block->open_count);
if (test_bit(DASD_FLAG_OFFLINE, &base->flags)) {
rc = -ENODEV;
Expand Down
1 change: 1 addition & 0 deletions drivers/s390/block/dasd_genhd.c
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ void dasd_gendisk_free(struct dasd_block *block)
if (block->gdp) {
del_gendisk(block->gdp);
block->gdp->queue = NULL;
block->gdp->private_data = NULL;
put_disk(block->gdp);
block->gdp = NULL;
}
Expand Down
1 change: 1 addition & 0 deletions drivers/s390/block/dasd_int.h
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,7 @@ struct dasd_device {

/* Device state and target state. */
int state, target;
struct mutex state_mutex;
int stopped; /* device (ccw_device_start) was stopped */

/* reference count. */
Expand Down

0 comments on commit 9eb2512

Please sign in to comment.