Commit 8fea2343 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] s390: dasd driver.

From: Martin Schwidefsky <schwidefsky@de.ibm.com>

dasd device driver changes:
 - After a state change interrupt restart all running i/o on queue
   and reset device timer.
 - Improve some debug messages.
 - Lower timeout of reserve/release/steal_lock to 2 seconds.
 - Fix BIODASDPSRD ioctl.
 - Replace ro_flag, use_diag_flag and disconnect_error_flag words by bits.
 - Use BLKPG_DEL_PARTITION ioctl instead of a call to delete_partition because
   delete_partition is not an exported function. Since dasd_destroy_partitions
   can't do blkdev_get because dasd_open would fail, keep the block device
   open as long as partitions exist. This in turn requires a different
   approach to the open vs. offline race.
parent d2ca1196
......@@ -7,7 +7,7 @@
* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999-2001
*
* $Revision: 1.133 $
* $Revision: 1.136 $
*/
#include <linux/config.h>
......@@ -224,7 +224,8 @@ dasd_state_basic_to_ready(struct dasd_device * device)
return rc;
dasd_setup_queue(device);
device->state = DASD_STATE_READY;
dasd_scan_partitions(device);
if (dasd_scan_partitions(device) != 0)
device->state = DASD_STATE_BASIC;
return 0;
}
......@@ -687,7 +688,10 @@ dasd_term_IO(struct dasd_ccw_req * cqr)
rc = ccw_device_clear(device->cdev, (long) cqr);
switch (rc) {
case 0: /* termination successful */
cqr->status = DASD_CQR_FAILED;
if (cqr->retries > 0)
cqr->status = DASD_CQR_QUEUED;
else
cqr->status = DASD_CQR_FAILED;
cqr->stopclk = get_clock();
break;
case -ENODEV:
......@@ -779,7 +783,7 @@ dasd_timeout_device(unsigned long ptr)
device = (struct dasd_device *) ptr;
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
/* re-activate first request in queue */
/* re-activate request queue */
device->stopped &= ~DASD_STOPPED_PENDING;
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
dasd_schedule_bh(device);
......@@ -827,12 +831,25 @@ do_state_change_pending(void *data)
struct dasd_device *device;
} *p;
struct dasd_device *device;
struct dasd_ccw_req *cqr;
struct list_head *l, *n;
unsigned long flags;
p = data;
device = p->device;
DBF_EVENT(DBF_NOTICE, "State change Interrupt for bus_id %s",
device->cdev->dev.bus_id);
device->stopped &= ~DASD_STOPPED_PENDING;
/* restart all 'running' IO on queue */
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
list_for_each_safe(l, n, &device->ccw_queue) {
cqr = list_entry(l, struct dasd_ccw_req, list);
if (cqr->status == DASD_CQR_IN_IO)
cqr->status = DASD_CQR_QUEUED;
}
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
dasd_set_timer (device, 0);
dasd_schedule_bh(device);
dasd_put_device(device);
kfree(p);
......@@ -847,7 +864,8 @@ dasd_handle_killed_request(struct ccw_device *cdev, unsigned long intparm)
cqr = (struct dasd_ccw_req *) intparm;
if (cqr->status != DASD_CQR_IN_IO) {
MESSAGE(KERN_DEBUG,
"invalid status: bus_id %s, status %02x",
"invalid status in handle_killed_request: "
"bus_id %s, status %02x",
cdev->dev.bus_id, cqr->status);
return;
}
......@@ -1142,7 +1160,8 @@ __dasd_process_blk_queue(struct dasd_device * device)
elv_next_request(queue) &&
nr_queued < DASD_CHANQ_MAX_SIZE) {
req = elv_next_request(queue);
if (device->ro_flag && rq_data_dir(req) == WRITE) {
if (test_bit(DASD_FLAG_RO, &device->flags) &&
rq_data_dir(req) == WRITE) {
DBF_EVENT(DBF_ERR,
"(%s) Rejecting write request %p",
device->cdev->dev.bus_id,
......@@ -1186,13 +1205,11 @@ static inline void
__dasd_check_expire(struct dasd_device * device)
{
struct dasd_ccw_req *cqr;
unsigned long long now;
if (list_empty(&device->ccw_queue))
return;
cqr = list_entry(device->ccw_queue.next, struct dasd_ccw_req, list);
if (cqr->status == DASD_CQR_IN_IO && cqr->expires != 0) {
now = get_clock();
if (time_after_eq(jiffies, cqr->expires + cqr->starttime)) {
if (device->discipline->term_IO(cqr) != 0)
/* Hmpf, try again in 1/100 sec */
......@@ -1517,7 +1534,8 @@ dasd_sleep_on_immediatly(struct dasd_ccw_req * cqr)
* terminated if it is currently in i/o.
* Returns 1 if the request has been terminated.
*/
int dasd_cancel_req(struct dasd_ccw_req *cqr)
int
dasd_cancel_req(struct dasd_ccw_req *cqr)
{
struct dasd_device *device = cqr->device;
unsigned long flags;
......@@ -1655,18 +1673,13 @@ dasd_open(struct inode *inp, struct file *filp)
{
struct gendisk *disk = inp->i_bdev->bd_disk;
struct dasd_device *device = disk->private_data;
int old_count, rc;
int rc;
/*
* We use a negative value in open_count to indicate that
* the device must not be used.
*/
do {
old_count = atomic_read(&device->open_count);
if (old_count < 0)
return -ENODEV;
} while (atomic_compare_and_swap(old_count, old_count + 1,
&device->open_count));
atomic_inc(&device->open_count);
if (test_bit(DASD_FLAG_OFFLINE, &device->flags)) {
rc = -ENODEV;
goto unlock;
}
if (!try_module_get(device->discipline->owner)) {
rc = -EINVAL;
......@@ -1681,7 +1694,6 @@ dasd_open(struct inode *inp, struct file *filp)
goto out;
}
rc = -ENODEV;
if (device->state < DASD_STATE_BASIC) {
DBF_DEV_EVENT(DBF_ERR, device, " %s",
" Cannot open unrecognized device");
......@@ -1704,12 +1716,6 @@ dasd_release(struct inode *inp, struct file *filp)
struct gendisk *disk = inp->i_bdev->bd_disk;
struct dasd_device *device = disk->private_data;
if (device->state < DASD_STATE_BASIC) {
DBF_DEV_EVENT(DBF_ERR, device, " %s",
" Cannot release unrecognized device");
return -EINVAL;
}
atomic_dec(&device->open_count);
module_put(device->discipline->owner);
return 0;
......@@ -1773,17 +1779,21 @@ dasd_generic_remove (struct ccw_device *cdev)
dasd_remove_sysfs_files(cdev);
device = dasd_device_from_cdev(cdev);
if (!IS_ERR(device)) {
/*
* This device is removed unconditionally. Set open_count
* to -1 to prevent dasd_open from opening it while it is
* no quite down yet.
*/
atomic_set(&device->open_count,-1);
dasd_set_target_state(device, DASD_STATE_NEW);
/* dasd_delete_device destroys the device reference. */
dasd_delete_device(device);
if (IS_ERR(device))
return;
if (test_and_set_bit(DASD_FLAG_OFFLINE, &device->flags)) {
/* Already doing offline processing */
dasd_put_device(device);
return;
}
/*
* This device is removed unconditionally. Set offline
* flag to prevent dasd_open from opening it while it is
* no quite down yet.
*/
dasd_set_target_state(device, DASD_STATE_NEW);
/* dasd_delete_device destroys the device reference. */
dasd_delete_device(device);
}
/* activate a device. This is called from dasd_{eckd,fba}_probe() when either
......@@ -1801,7 +1811,7 @@ dasd_generic_set_online (struct ccw_device *cdev,
if (IS_ERR(device))
return PTR_ERR(device);
if (device->use_diag_flag) {
if (test_bit(DASD_FLAG_USE_DIAG, &device->flags)) {
if (!dasd_diag_discipline_pointer) {
printk (KERN_WARNING
"dasd_generic couldn't online device %s "
......@@ -1849,18 +1859,28 @@ int
dasd_generic_set_offline (struct ccw_device *cdev)
{
struct dasd_device *device;
int max_count;
device = dasd_device_from_cdev(cdev);
if (IS_ERR(device))
return PTR_ERR(device);
if (test_and_set_bit(DASD_FLAG_OFFLINE, &device->flags)) {
/* Already doing offline processing */
dasd_put_device(device);
return 0;
}
/*
* We must make sure that this device is currently not in use
* (current open_count == 0 ). We set open_count to -1 to indicate
* that from now on set_offline is in progress and the device must
* not be used otherwise.
* We must make sure that this device is currently not in use.
* The open_count is increased for every opener, that includes
* the blkdev_get in dasd_scan_partitions. We are only interested
* in the other openers.
*/
if (atomic_compare_and_swap(0, -1, &device->open_count)) {
max_count = device->bdev ? 1 : 0;
if (atomic_read(&device->open_count) > max_count) {
printk (KERN_WARNING "Can't offline dasd device with open"
" count = %i.\n",
atomic_read(&device->open_count));
clear_bit(DASD_FLAG_OFFLINE, &device->flags);
dasd_put_device(device);
return -EBUSY;
}
......@@ -1890,7 +1910,7 @@ dasd_generic_notify(struct ccw_device *cdev, int event)
if (device->state < DASD_STATE_BASIC)
break;
/* Device is active. We want to keep it. */
if (device->disconnect_error_flag) {
if (test_bit(DASD_FLAG_DSC_ERROR, &device->flags)) {
list_for_each_entry(cqr, &device->ccw_queue, list)
if (cqr->status == DASD_CQR_IN_IO)
cqr->status = DASD_CQR_FAILED;
......
......@@ -5,7 +5,7 @@
* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000, 2001
*
* $Revision: 1.27 $
* $Revision: 1.28 $
*/
#include <linux/timer.h>
......@@ -229,7 +229,7 @@ dasd_3990_erp_block_queue(struct dasd_ccw_req * erp, int expires)
struct dasd_device *device = erp->device;
DEV_MESSAGE(KERN_INFO, device,
"blocking request queue for %is", expires);
"blocking request queue for %is", expires/HZ);
device->stopped |= DASD_STOPPED_PENDING;
erp->status = DASD_CQR_QUEUED;
......@@ -2623,7 +2623,7 @@ dasd_3990_erp_action(struct dasd_ccw_req * cqr)
#ifdef ERP_DEBUG
/* print current erp_chain */
DEV_MESSAGE(KERN_DEBUG, device, "%s",
DEV_MESSAGE(KERN_ERR, device, "%s",
"ERP chain at BEGINNING of ERP-ACTION");
{
struct dasd_ccw_req *temp_erp = NULL;
......@@ -2631,9 +2631,10 @@ dasd_3990_erp_action(struct dasd_ccw_req * cqr)
for (temp_erp = cqr;
temp_erp != NULL; temp_erp = temp_erp->refers) {
DEV_MESSAGE(KERN_DEBUG, device,
" erp %p refers to %p",
temp_erp, temp_erp->refers);
DEV_MESSAGE(KERN_ERR, device,
" erp %p (%02x) refers to %p",
temp_erp, temp_erp->status,
temp_erp->refers);
}
}
#endif /* ERP_DEBUG */
......@@ -2675,15 +2676,16 @@ dasd_3990_erp_action(struct dasd_ccw_req * cqr)
#ifdef ERP_DEBUG
/* print current erp_chain */
DEV_MESSAGE(KERN_DEBUG, device, "%s", "ERP chain at END of ERP-ACTION");
DEV_MESSAGE(KERN_ERR, device, "%s", "ERP chain at END of ERP-ACTION");
{
struct dasd_ccw_req *temp_erp = NULL;
for (temp_erp = erp;
temp_erp != NULL; temp_erp = temp_erp->refers) {
DEV_MESSAGE(KERN_DEBUG, device,
" erp %p refers to %p",
temp_erp, temp_erp->refers);
DEV_MESSAGE(KERN_ERR, device,
" erp %p (%02x) refers to %p",
temp_erp, temp_erp->status,
temp_erp->refers);
}
}
#endif /* ERP_DEBUG */
......
......@@ -11,7 +11,7 @@
* functions may not be called from interrupt context. In particular
* dasd_get_device is a no-no from interrupt context.
*
* $Revision: 1.26 $
* $Revision: 1.27 $
*/
#include <linux/config.h>
......@@ -466,10 +466,14 @@ dasd_create_device(struct ccw_device *cdev)
if (!devmap->device) {
devmap->device = device;
device->devindex = devmap->devindex;
device->ro_flag =
(devmap->features & DASD_FEATURE_READONLY) != 0;
device->use_diag_flag =
(devmap->features & DASD_FEATURE_USEDIAG) != 0;
if (devmap->features & DASD_FEATURE_READONLY)
set_bit(DASD_FLAG_RO, &device->flags);
else
clear_bit(DASD_FLAG_RO, &device->flags);
if (devmap->features & DASD_FEATURE_USEDIAG)
set_bit(DASD_FLAG_USE_DIAG, &device->flags);
else
clear_bit(DASD_FLAG_USE_DIAG, &device->flags);
get_device(&cdev->dev);
device->cdev = cdev;
rc = 0;
......@@ -596,7 +600,10 @@ dasd_ro_store(struct device *dev, const char *buf, size_t count)
if (devmap->device) {
if (devmap->device->gdp)
set_disk_ro(devmap->device->gdp, ro_flag);
devmap->device->ro_flag = ro_flag;
if (ro_flag)
set_bit(DASD_FLAG_RO, &devmap->device->flags);
else
clear_bit(DASD_FLAG_RO, &devmap->device->flags);
}
spin_unlock(&dasd_devmap_lock);
return count;
......
......@@ -7,7 +7,7 @@
* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
*
* $Revision: 1.51 $
* $Revision: 1.53 $
*/
#include <linux/config.h>
......@@ -1131,7 +1131,7 @@ dasd_eckd_release(struct block_device *bdev, int no, long args)
cqr->cpaddr->cda = (__u32)(addr_t) cqr->data;
cqr->device = device;
cqr->retries = 0;
cqr->expires = 10 * HZ;
cqr->expires = 2 * HZ;
cqr->buildclk = get_clock();
cqr->status = DASD_CQR_FILLED;
......@@ -1174,7 +1174,7 @@ dasd_eckd_reserve(struct block_device *bdev, int no, long args)
cqr->cpaddr->cda = (__u32)(addr_t) cqr->data;
cqr->device = device;
cqr->retries = 0;
cqr->expires = 10 * HZ;
cqr->expires = 2 * HZ;
cqr->buildclk = get_clock();
cqr->status = DASD_CQR_FILLED;
......@@ -1216,7 +1216,7 @@ dasd_eckd_steal_lock(struct block_device *bdev, int no, long args)
cqr->cpaddr->cda = (__u32)(addr_t) cqr->data;
cqr->device = device;
cqr->retries = 0;
cqr->expires = 10 * HZ;
cqr->expires = 2 * HZ;
cqr->buildclk = get_clock();
cqr->status = DASD_CQR_FILLED;
......@@ -1274,6 +1274,7 @@ dasd_eckd_performance(struct block_device *bdev, int no, long args)
stats = (struct dasd_rssd_perf_stats_t *) (prssdp + 1);
memset(stats, 0, sizeof (struct dasd_rssd_perf_stats_t));
ccw++;
ccw->cmd_code = DASD_ECKD_CCW_RSSD;
ccw->count = sizeof (struct dasd_rssd_perf_stats_t);
ccw->cda = (__u32)(addr_t) stats;
......
......@@ -9,7 +9,7 @@
*
* gendisk related functions for the dasd driver.
*
* $Revision: 1.44 $
* $Revision: 1.46 $
*/
#include <linux/config.h>
......@@ -71,7 +71,7 @@ dasd_gendisk_alloc(struct dasd_device *device)
sprintf(gdp->devfs_name, "dasd/%s", device->cdev->dev.bus_id);
if (device->ro_flag)
if (test_bit(DASD_FLAG_RO, &device->flags))
set_disk_ro(gdp, 1);
gdp->private_data = device;
gdp->queue = device->request_queue;
......@@ -96,22 +96,33 @@ dasd_gendisk_free(struct dasd_device *device)
/*
* Trigger a partition detection.
*/
void
int
dasd_scan_partitions(struct dasd_device * device)
{
struct block_device *bdev;
/* Make the disk known. */
set_capacity(device->gdp, device->blocks << device->s2b_shift);
/* See fs/partition/check.c:register_disk,rescan_partitions */
bdev = bdget_disk(device->gdp, 0);
if (bdev) {
if (blkdev_get(bdev, FMODE_READ, 1) >= 0) {
/* Can't call rescan_partitions directly. Use ioctl. */
ioctl_by_bdev(bdev, BLKRRPART, 0);
blkdev_put(bdev);
}
}
if (!bdev || blkdev_get(bdev, FMODE_READ, 1) < 0)
return -ENODEV;
/*
* See fs/partition/check.c:register_disk,rescan_partitions
* Can't call rescan_partitions directly. Use ioctl.
*/
ioctl_by_bdev(bdev, BLKRRPART, 0);
/*
* Since the matching blkdev_put call to the blkdev_get in
* this function is not called before dasd_destroy_partitions
* the offline open_count limit needs to be increased from
* 0 to 1. This is done by setting device->bdev (see
* dasd_generic_set_offline). As long as the partition
* detection is running no offline should be allowed. That
* is why the assignment to device->bdev is done AFTER
* the BLKRRPART ioctl.
*/
device->bdev = bdev;
return 0;
}
/*
......@@ -121,13 +132,32 @@ dasd_scan_partitions(struct dasd_device * device)
void
dasd_destroy_partitions(struct dasd_device * device)
{
int p;
/* The two structs have 168/176 byte on 31/64 bit. */
struct blkpg_partition bpart;
struct blkpg_ioctl_arg barg;
struct block_device *bdev;
/*
* Get the bdev pointer from the device structure and clear
* device->bdev to lower the offline open_count limit again.
*/
bdev = device->bdev;
device->bdev = 0;
/*
* See fs/partition/check.c:delete_partition
* Can't call delete_partitions directly. Use ioctl.
* The ioctl also does locking and invalidation.
*/
memset(&bpart, sizeof(struct blkpg_partition), 0);
memset(&barg, sizeof(struct blkpg_ioctl_arg), 0);
barg.data = &bpart;
for (bpart.pno = device->gdp->minors - 1; bpart.pno > 0; bpart.pno--)
ioctl_by_bdev(bdev, BLKPG_DEL_PARTITION, (unsigned long) &barg);
for (p = device->gdp->minors - 1; p > 0; p--) {
invalidate_partition(device->gdp, p);
delete_partition(device->gdp, p);
}
invalidate_partition(device->gdp, 0);
/* Matching blkdev_put to the blkdev_get in dasd_scan_partitions. */
blkdev_put(bdev);
set_capacity(device->gdp, 0);
}
......
......@@ -6,7 +6,7 @@
* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
*
* $Revision: 1.55 $
* $Revision: 1.56 $
*/
#ifndef DASD_INT_H
......@@ -268,14 +268,12 @@ struct dasd_device {
struct gendisk *gdp;
request_queue_t *request_queue;
spinlock_t request_queue_lock;
struct block_device *bdev;
unsigned int devindex;
unsigned long blocks; /* size of volume in blocks */
unsigned int bp_block; /* bytes per block */
unsigned int s2b_shift; /* log2 (bp_block/512) */
int ro_flag; /* read-only flag */
int use_diag_flag; /* diag allowed flag */
int disconnect_error_flag; /* return -EIO when disconnected */
unsigned long flags; /* per device flags */
/* Device discipline stuff. */
struct dasd_discipline *discipline;
......@@ -318,6 +316,11 @@ struct dasd_device {
#define DASD_STOPPED_DC_WAIT 8 /* disconnected, wait */
#define DASD_STOPPED_DC_EIO 16 /* disconnected, return -EIO */
/* per device flags */
#define DASD_FLAG_RO 0 /* device is read-only */
#define DASD_FLAG_USE_DIAG 1 /* use diag disciplnie */
#define DASD_FLAG_DSC_ERROR 2 /* return -EIO when disconnected */
#define DASD_FLAG_OFFLINE 3 /* device is in offline processing */
void dasd_put_device_wake(struct dasd_device *);
......@@ -498,7 +501,7 @@ int dasd_gendisk_init(void);
void dasd_gendisk_exit(void);
int dasd_gendisk_alloc(struct dasd_device *);
void dasd_gendisk_free(struct dasd_device *);
void dasd_scan_partitions(struct dasd_device *);
int dasd_scan_partitions(struct dasd_device *);
void dasd_destroy_partitions(struct dasd_device *);
/* externals in dasd_ioctl.c */
......
......@@ -303,7 +303,7 @@ dasd_ioctl_format(struct block_device *bdev, int no, long args)
if (device == NULL)
return -ENODEV;
if (device->ro_flag)
if (test_bit(DASD_FLAG_RO, &device->flags))
return -EROFS;
if (copy_from_user(&fdata, (void *) args,
sizeof (struct format_data_t)))
......@@ -415,8 +415,8 @@ dasd_ioctl_information(struct block_device *bdev, int no, long args)
(dasd_check_blocksize(device->bp_block)))
dasd_info->format = DASD_FORMAT_NONE;
dasd_info->features |= device->ro_flag ? DASD_FEATURE_READONLY
: DASD_FEATURE_DEFAULT;
dasd_info->features |= test_bit(DASD_FLAG_RO, &device->flags) ?
DASD_FEATURE_READONLY : DASD_FEATURE_DEFAULT;
if (device->discipline)
memcpy(dasd_info->type, device->discipline->name, 4);
......@@ -472,7 +472,10 @@ dasd_ioctl_set_ro(struct block_device *bdev, int no, long args)
if (device == NULL)
return -ENODEV;
set_disk_ro(bdev->bd_disk, intval);
device->ro_flag = intval;
if (intval)
set_bit(DASD_FLAG_RO, &device->flags);
else
clear_bit(DASD_FLAG_RO, &device->flags);
return 0;
}
......
......@@ -9,7 +9,7 @@
*
* /proc interface for the dasd driver.
*
* $Revision: 1.26 $
* $Revision: 1.27 $
*/
#include <linux/config.h>
......@@ -77,7 +77,7 @@ dasd_devices_show(struct seq_file *m, void *v)
else
seq_printf(m, " is ????????");
/* Print devices features. */
substr = device->ro_flag ? "(ro)" : " ";
substr = test_bit(DASD_FLAG_RO, &device->flags) ? "(ro)" : " ";
seq_printf(m, "%4s: ", substr);
/* Print device status information. */
switch ((device != NULL) ? device->state : -1) {
......
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