s390/dasd: improve speed of dasdfmt
Reorganize format IO requests and enable usage of PAV. 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
e5dcf0025d
commit
d42e17129b
|
@ -246,7 +246,7 @@ static struct dentry *dasd_debugfs_setup(const char *name,
|
||||||
static int dasd_state_known_to_basic(struct dasd_device *device)
|
static int dasd_state_known_to_basic(struct dasd_device *device)
|
||||||
{
|
{
|
||||||
struct dasd_block *block = device->block;
|
struct dasd_block *block = device->block;
|
||||||
int rc;
|
int rc = 0;
|
||||||
|
|
||||||
/* Allocate and register gendisk structure. */
|
/* Allocate and register gendisk structure. */
|
||||||
if (block) {
|
if (block) {
|
||||||
|
@ -273,7 +273,8 @@ static int dasd_state_known_to_basic(struct dasd_device *device)
|
||||||
DBF_DEV_EVENT(DBF_EMERG, device, "%s", "debug area created");
|
DBF_DEV_EVENT(DBF_EMERG, device, "%s", "debug area created");
|
||||||
|
|
||||||
device->state = DASD_STATE_BASIC;
|
device->state = DASD_STATE_BASIC;
|
||||||
return 0;
|
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -282,6 +283,7 @@ static int dasd_state_known_to_basic(struct dasd_device *device)
|
||||||
static int dasd_state_basic_to_known(struct dasd_device *device)
|
static int dasd_state_basic_to_known(struct dasd_device *device)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
if (device->block) {
|
if (device->block) {
|
||||||
dasd_profile_exit(&device->block->profile);
|
dasd_profile_exit(&device->block->profile);
|
||||||
if (device->block->debugfs_dentry)
|
if (device->block->debugfs_dentry)
|
||||||
|
@ -332,8 +334,10 @@ static int dasd_state_basic_to_ready(struct dasd_device *device)
|
||||||
if (block->base->discipline->do_analysis != NULL)
|
if (block->base->discipline->do_analysis != NULL)
|
||||||
rc = block->base->discipline->do_analysis(block);
|
rc = block->base->discipline->do_analysis(block);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
if (rc != -EAGAIN)
|
if (rc != -EAGAIN) {
|
||||||
device->state = DASD_STATE_UNFMT;
|
device->state = DASD_STATE_UNFMT;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
dasd_setup_queue(block);
|
dasd_setup_queue(block);
|
||||||
|
@ -341,11 +345,16 @@ static int dasd_state_basic_to_ready(struct dasd_device *device)
|
||||||
block->blocks << block->s2b_shift);
|
block->blocks << block->s2b_shift);
|
||||||
device->state = DASD_STATE_READY;
|
device->state = DASD_STATE_READY;
|
||||||
rc = dasd_scan_partitions(block);
|
rc = dasd_scan_partitions(block);
|
||||||
if (rc)
|
if (rc) {
|
||||||
device->state = DASD_STATE_BASIC;
|
device->state = DASD_STATE_BASIC;
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
device->state = DASD_STATE_READY;
|
device->state = DASD_STATE_READY;
|
||||||
}
|
}
|
||||||
|
out:
|
||||||
|
if (device->discipline->basic_to_ready)
|
||||||
|
rc = device->discipline->basic_to_ready(device);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -368,6 +377,11 @@ static int dasd_state_ready_to_basic(struct dasd_device *device)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
|
if (device->discipline->ready_to_basic) {
|
||||||
|
rc = device->discipline->ready_to_basic(device);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
device->state = DASD_STATE_BASIC;
|
device->state = DASD_STATE_BASIC;
|
||||||
if (device->block) {
|
if (device->block) {
|
||||||
struct dasd_block *block = device->block;
|
struct dasd_block *block = device->block;
|
||||||
|
@ -402,16 +416,10 @@ static int dasd_state_unfmt_to_basic(struct dasd_device *device)
|
||||||
static int
|
static int
|
||||||
dasd_state_ready_to_online(struct dasd_device * device)
|
dasd_state_ready_to_online(struct dasd_device * device)
|
||||||
{
|
{
|
||||||
int rc;
|
|
||||||
struct gendisk *disk;
|
struct gendisk *disk;
|
||||||
struct disk_part_iter piter;
|
struct disk_part_iter piter;
|
||||||
struct hd_struct *part;
|
struct hd_struct *part;
|
||||||
|
|
||||||
if (device->discipline->ready_to_online) {
|
|
||||||
rc = device->discipline->ready_to_online(device);
|
|
||||||
if (rc)
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
device->state = DASD_STATE_ONLINE;
|
device->state = DASD_STATE_ONLINE;
|
||||||
if (device->block) {
|
if (device->block) {
|
||||||
dasd_schedule_block_bh(device->block);
|
dasd_schedule_block_bh(device->block);
|
||||||
|
@ -444,6 +452,7 @@ static int dasd_state_online_to_ready(struct dasd_device *device)
|
||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
device->state = DASD_STATE_READY;
|
device->state = DASD_STATE_READY;
|
||||||
if (device->block && !(device->features & DASD_FEATURE_USERAW)) {
|
if (device->block && !(device->features & DASD_FEATURE_USERAW)) {
|
||||||
disk = device->block->bdev->bd_disk;
|
disk = device->block->bdev->bd_disk;
|
||||||
|
@ -2223,6 +2232,77 @@ static int _dasd_sleep_on(struct dasd_ccw_req *maincqr, int interruptible)
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int _wait_for_wakeup_queue(struct list_head *ccw_queue)
|
||||||
|
{
|
||||||
|
struct dasd_ccw_req *cqr;
|
||||||
|
|
||||||
|
list_for_each_entry(cqr, ccw_queue, blocklist) {
|
||||||
|
if (cqr->callback_data != DASD_SLEEPON_END_TAG)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _dasd_sleep_on_queue(struct list_head *ccw_queue, int interruptible)
|
||||||
|
{
|
||||||
|
struct dasd_device *device;
|
||||||
|
int rc;
|
||||||
|
struct dasd_ccw_req *cqr, *n;
|
||||||
|
|
||||||
|
retry:
|
||||||
|
list_for_each_entry_safe(cqr, n, ccw_queue, blocklist) {
|
||||||
|
device = cqr->startdev;
|
||||||
|
if (cqr->status != DASD_CQR_FILLED) /*could be failed*/
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (test_bit(DASD_FLAG_LOCK_STOLEN, &device->flags) &&
|
||||||
|
!test_bit(DASD_CQR_ALLOW_SLOCK, &cqr->flags)) {
|
||||||
|
cqr->status = DASD_CQR_FAILED;
|
||||||
|
cqr->intrc = -EPERM;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/*Non-temporary stop condition will trigger fail fast*/
|
||||||
|
if (device->stopped & ~DASD_STOPPED_PENDING &&
|
||||||
|
test_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags) &&
|
||||||
|
!dasd_eer_enabled(device)) {
|
||||||
|
cqr->status = DASD_CQR_FAILED;
|
||||||
|
cqr->intrc = -EAGAIN;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*Don't try to start requests if device is stopped*/
|
||||||
|
if (interruptible) {
|
||||||
|
rc = wait_event_interruptible(
|
||||||
|
generic_waitq, !device->stopped);
|
||||||
|
if (rc == -ERESTARTSYS) {
|
||||||
|
cqr->status = DASD_CQR_FAILED;
|
||||||
|
cqr->intrc = rc;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
wait_event(generic_waitq, !(device->stopped));
|
||||||
|
|
||||||
|
if (!cqr->callback)
|
||||||
|
cqr->callback = dasd_wakeup_cb;
|
||||||
|
cqr->callback_data = DASD_SLEEPON_START_TAG;
|
||||||
|
dasd_add_request_tail(cqr);
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_event(generic_waitq, _wait_for_wakeup_queue(ccw_queue));
|
||||||
|
|
||||||
|
rc = 0;
|
||||||
|
list_for_each_entry_safe(cqr, n, ccw_queue, blocklist) {
|
||||||
|
if (__dasd_sleep_on_erp(cqr))
|
||||||
|
rc = 1;
|
||||||
|
}
|
||||||
|
if (rc)
|
||||||
|
goto retry;
|
||||||
|
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Queue a request to the tail of the device ccw_queue and wait for
|
* Queue a request to the tail of the device ccw_queue and wait for
|
||||||
* it's completion.
|
* it's completion.
|
||||||
|
@ -2232,6 +2312,15 @@ int dasd_sleep_on(struct dasd_ccw_req *cqr)
|
||||||
return _dasd_sleep_on(cqr, 0);
|
return _dasd_sleep_on(cqr, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Start requests from a ccw_queue and wait for their completion.
|
||||||
|
*/
|
||||||
|
int dasd_sleep_on_queue(struct list_head *ccw_queue)
|
||||||
|
{
|
||||||
|
return _dasd_sleep_on_queue(ccw_queue, 0);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(dasd_sleep_on_queue);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Queue a request to the tail of the device ccw_queue and wait
|
* Queue a request to the tail of the device ccw_queue and wait
|
||||||
* interruptible for it's completion.
|
* interruptible for it's completion.
|
||||||
|
|
|
@ -2022,7 +2022,7 @@ static int dasd_eckd_do_analysis(struct dasd_block *block)
|
||||||
return dasd_eckd_end_analysis(block);
|
return dasd_eckd_end_analysis(block);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dasd_eckd_ready_to_online(struct dasd_device *device)
|
static int dasd_eckd_basic_to_ready(struct dasd_device *device)
|
||||||
{
|
{
|
||||||
return dasd_alias_add_device(device);
|
return dasd_alias_add_device(device);
|
||||||
};
|
};
|
||||||
|
@ -2031,6 +2031,11 @@ static int dasd_eckd_online_to_ready(struct dasd_device *device)
|
||||||
{
|
{
|
||||||
cancel_work_sync(&device->reload_device);
|
cancel_work_sync(&device->reload_device);
|
||||||
cancel_work_sync(&device->kick_validate);
|
cancel_work_sync(&device->kick_validate);
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int dasd_eckd_ready_to_basic(struct dasd_device *device)
|
||||||
|
{
|
||||||
return dasd_alias_remove_device(device);
|
return dasd_alias_remove_device(device);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2050,45 +2055,34 @@ dasd_eckd_fill_geometry(struct dasd_block *block, struct hd_geometry *geo)
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct dasd_ccw_req *
|
static struct dasd_ccw_req *
|
||||||
dasd_eckd_format_device(struct dasd_device * device,
|
dasd_eckd_build_format(struct dasd_device *base,
|
||||||
struct format_data_t * fdata)
|
struct format_data_t *fdata)
|
||||||
{
|
{
|
||||||
struct dasd_eckd_private *private;
|
struct dasd_eckd_private *base_priv;
|
||||||
|
struct dasd_eckd_private *start_priv;
|
||||||
|
struct dasd_device *startdev;
|
||||||
struct dasd_ccw_req *fcp;
|
struct dasd_ccw_req *fcp;
|
||||||
struct eckd_count *ect;
|
struct eckd_count *ect;
|
||||||
|
struct ch_t address;
|
||||||
struct ccw1 *ccw;
|
struct ccw1 *ccw;
|
||||||
void *data;
|
void *data;
|
||||||
int rpt;
|
int rpt;
|
||||||
struct ch_t address;
|
|
||||||
int cplength, datasize;
|
int cplength, datasize;
|
||||||
int i;
|
int i, j;
|
||||||
int intensity = 0;
|
int intensity = 0;
|
||||||
int r0_perm;
|
int r0_perm;
|
||||||
|
int nr_tracks;
|
||||||
|
|
||||||
private = (struct dasd_eckd_private *) device->private;
|
startdev = dasd_alias_get_start_dev(base);
|
||||||
rpt = recs_per_track(&private->rdc_data, 0, fdata->blksize);
|
if (!startdev)
|
||||||
set_ch_t(&address,
|
startdev = base;
|
||||||
fdata->start_unit / private->rdc_data.trk_per_cyl,
|
|
||||||
fdata->start_unit % private->rdc_data.trk_per_cyl);
|
|
||||||
|
|
||||||
/* Sanity checks. */
|
start_priv = (struct dasd_eckd_private *) startdev->private;
|
||||||
if (fdata->start_unit >=
|
base_priv = (struct dasd_eckd_private *) base->private;
|
||||||
(private->real_cyl * private->rdc_data.trk_per_cyl)) {
|
|
||||||
dev_warn(&device->cdev->dev, "Start track number %d used in "
|
rpt = recs_per_track(&base_priv->rdc_data, 0, fdata->blksize);
|
||||||
"formatting is too big\n", fdata->start_unit);
|
|
||||||
return ERR_PTR(-EINVAL);
|
nr_tracks = fdata->stop_unit - fdata->start_unit + 1;
|
||||||
}
|
|
||||||
if (fdata->start_unit > fdata->stop_unit) {
|
|
||||||
dev_warn(&device->cdev->dev, "Start track %d used in "
|
|
||||||
"formatting exceeds end track\n", fdata->start_unit);
|
|
||||||
return ERR_PTR(-EINVAL);
|
|
||||||
}
|
|
||||||
if (dasd_check_blocksize(fdata->blksize) != 0) {
|
|
||||||
dev_warn(&device->cdev->dev,
|
|
||||||
"The DASD cannot be formatted with block size %d\n",
|
|
||||||
fdata->blksize);
|
|
||||||
return ERR_PTR(-EINVAL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* fdata->intensity is a bit string that tells us what to do:
|
* fdata->intensity is a bit string that tells us what to do:
|
||||||
|
@ -2106,149 +2100,282 @@ dasd_eckd_format_device(struct dasd_device * device,
|
||||||
r0_perm = 1;
|
r0_perm = 1;
|
||||||
intensity = fdata->intensity;
|
intensity = fdata->intensity;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (intensity) {
|
switch (intensity) {
|
||||||
case 0x00: /* Normal format */
|
case 0x00: /* Normal format */
|
||||||
case 0x08: /* Normal format, use cdl. */
|
case 0x08: /* Normal format, use cdl. */
|
||||||
cplength = 2 + rpt;
|
cplength = 2 + (rpt*nr_tracks);
|
||||||
datasize = sizeof(struct DE_eckd_data) +
|
datasize = sizeof(struct PFX_eckd_data) +
|
||||||
sizeof(struct LO_eckd_data) +
|
sizeof(struct LO_eckd_data) +
|
||||||
rpt * sizeof(struct eckd_count);
|
rpt * nr_tracks * sizeof(struct eckd_count);
|
||||||
break;
|
break;
|
||||||
case 0x01: /* Write record zero and format track. */
|
case 0x01: /* Write record zero and format track. */
|
||||||
case 0x09: /* Write record zero and format track, use cdl. */
|
case 0x09: /* Write record zero and format track, use cdl. */
|
||||||
cplength = 3 + rpt;
|
cplength = 2 + rpt * nr_tracks;
|
||||||
datasize = sizeof(struct DE_eckd_data) +
|
datasize = sizeof(struct PFX_eckd_data) +
|
||||||
sizeof(struct LO_eckd_data) +
|
sizeof(struct LO_eckd_data) +
|
||||||
sizeof(struct eckd_count) +
|
sizeof(struct eckd_count) +
|
||||||
rpt * sizeof(struct eckd_count);
|
rpt * nr_tracks * sizeof(struct eckd_count);
|
||||||
break;
|
break;
|
||||||
case 0x04: /* Invalidate track. */
|
case 0x04: /* Invalidate track. */
|
||||||
case 0x0c: /* Invalidate track, use cdl. */
|
case 0x0c: /* Invalidate track, use cdl. */
|
||||||
cplength = 3;
|
cplength = 3;
|
||||||
datasize = sizeof(struct DE_eckd_data) +
|
datasize = sizeof(struct PFX_eckd_data) +
|
||||||
sizeof(struct LO_eckd_data) +
|
sizeof(struct LO_eckd_data) +
|
||||||
sizeof(struct eckd_count);
|
sizeof(struct eckd_count);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
dev_warn(&device->cdev->dev, "An I/O control call used "
|
dev_warn(&startdev->cdev->dev,
|
||||||
"incorrect flags 0x%x\n", fdata->intensity);
|
"An I/O control call used incorrect flags 0x%x\n",
|
||||||
|
fdata->intensity);
|
||||||
return ERR_PTR(-EINVAL);
|
return ERR_PTR(-EINVAL);
|
||||||
}
|
}
|
||||||
/* Allocate the format ccw request. */
|
/* Allocate the format ccw request. */
|
||||||
fcp = dasd_smalloc_request(DASD_ECKD_MAGIC, cplength, datasize, device);
|
fcp = dasd_smalloc_request(DASD_ECKD_MAGIC, cplength,
|
||||||
|
datasize, startdev);
|
||||||
if (IS_ERR(fcp))
|
if (IS_ERR(fcp))
|
||||||
return fcp;
|
return fcp;
|
||||||
|
|
||||||
|
start_priv->count++;
|
||||||
data = fcp->data;
|
data = fcp->data;
|
||||||
ccw = fcp->cpaddr;
|
ccw = fcp->cpaddr;
|
||||||
|
|
||||||
switch (intensity & ~0x08) {
|
switch (intensity & ~0x08) {
|
||||||
case 0x00: /* Normal format. */
|
case 0x00: /* Normal format. */
|
||||||
define_extent(ccw++, (struct DE_eckd_data *) data,
|
prefix(ccw++, (struct PFX_eckd_data *) data,
|
||||||
fdata->start_unit, fdata->start_unit,
|
fdata->start_unit, fdata->stop_unit,
|
||||||
DASD_ECKD_CCW_WRITE_CKD, device);
|
DASD_ECKD_CCW_WRITE_CKD, base, startdev);
|
||||||
/* grant subsystem permission to format R0 */
|
/* grant subsystem permission to format R0 */
|
||||||
if (r0_perm)
|
if (r0_perm)
|
||||||
((struct DE_eckd_data *)data)->ga_extended |= 0x04;
|
((struct PFX_eckd_data *)data)
|
||||||
data += sizeof(struct DE_eckd_data);
|
->define_extent.ga_extended |= 0x04;
|
||||||
|
data += sizeof(struct PFX_eckd_data);
|
||||||
ccw[-1].flags |= CCW_FLAG_CC;
|
ccw[-1].flags |= CCW_FLAG_CC;
|
||||||
locate_record(ccw++, (struct LO_eckd_data *) data,
|
locate_record(ccw++, (struct LO_eckd_data *) data,
|
||||||
fdata->start_unit, 0, rpt,
|
fdata->start_unit, 0, rpt*nr_tracks,
|
||||||
DASD_ECKD_CCW_WRITE_CKD, device,
|
DASD_ECKD_CCW_WRITE_CKD, base,
|
||||||
fdata->blksize);
|
fdata->blksize);
|
||||||
data += sizeof(struct LO_eckd_data);
|
data += sizeof(struct LO_eckd_data);
|
||||||
break;
|
break;
|
||||||
case 0x01: /* Write record zero + format track. */
|
case 0x01: /* Write record zero + format track. */
|
||||||
define_extent(ccw++, (struct DE_eckd_data *) data,
|
prefix(ccw++, (struct PFX_eckd_data *) data,
|
||||||
fdata->start_unit, fdata->start_unit,
|
fdata->start_unit, fdata->stop_unit,
|
||||||
DASD_ECKD_CCW_WRITE_RECORD_ZERO,
|
DASD_ECKD_CCW_WRITE_RECORD_ZERO,
|
||||||
device);
|
base, startdev);
|
||||||
data += sizeof(struct DE_eckd_data);
|
data += sizeof(struct PFX_eckd_data);
|
||||||
ccw[-1].flags |= CCW_FLAG_CC;
|
ccw[-1].flags |= CCW_FLAG_CC;
|
||||||
locate_record(ccw++, (struct LO_eckd_data *) data,
|
locate_record(ccw++, (struct LO_eckd_data *) data,
|
||||||
fdata->start_unit, 0, rpt + 1,
|
fdata->start_unit, 0, rpt * nr_tracks + 1,
|
||||||
DASD_ECKD_CCW_WRITE_RECORD_ZERO, device,
|
DASD_ECKD_CCW_WRITE_RECORD_ZERO, base,
|
||||||
device->block->bp_block);
|
base->block->bp_block);
|
||||||
data += sizeof(struct LO_eckd_data);
|
data += sizeof(struct LO_eckd_data);
|
||||||
break;
|
break;
|
||||||
case 0x04: /* Invalidate track. */
|
case 0x04: /* Invalidate track. */
|
||||||
define_extent(ccw++, (struct DE_eckd_data *) data,
|
prefix(ccw++, (struct PFX_eckd_data *) data,
|
||||||
fdata->start_unit, fdata->start_unit,
|
fdata->start_unit, fdata->stop_unit,
|
||||||
DASD_ECKD_CCW_WRITE_CKD, device);
|
DASD_ECKD_CCW_WRITE_CKD, base, startdev);
|
||||||
data += sizeof(struct DE_eckd_data);
|
data += sizeof(struct PFX_eckd_data);
|
||||||
ccw[-1].flags |= CCW_FLAG_CC;
|
ccw[-1].flags |= CCW_FLAG_CC;
|
||||||
locate_record(ccw++, (struct LO_eckd_data *) data,
|
locate_record(ccw++, (struct LO_eckd_data *) data,
|
||||||
fdata->start_unit, 0, 1,
|
fdata->start_unit, 0, 1,
|
||||||
DASD_ECKD_CCW_WRITE_CKD, device, 8);
|
DASD_ECKD_CCW_WRITE_CKD, base, 8);
|
||||||
data += sizeof(struct LO_eckd_data);
|
data += sizeof(struct LO_eckd_data);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (intensity & 0x01) { /* write record zero */
|
|
||||||
ect = (struct eckd_count *) data;
|
for (j = 0; j < nr_tracks; j++) {
|
||||||
data += sizeof(struct eckd_count);
|
/* calculate cylinder and head for the current track */
|
||||||
ect->cyl = address.cyl;
|
set_ch_t(&address,
|
||||||
ect->head = address.head;
|
(fdata->start_unit + j) /
|
||||||
ect->record = 0;
|
base_priv->rdc_data.trk_per_cyl,
|
||||||
ect->kl = 0;
|
(fdata->start_unit + j) %
|
||||||
ect->dl = 8;
|
base_priv->rdc_data.trk_per_cyl);
|
||||||
ccw[-1].flags |= CCW_FLAG_CC;
|
if (intensity & 0x01) { /* write record zero */
|
||||||
ccw->cmd_code = DASD_ECKD_CCW_WRITE_RECORD_ZERO;
|
|
||||||
ccw->flags = CCW_FLAG_SLI;
|
|
||||||
ccw->count = 8;
|
|
||||||
ccw->cda = (__u32)(addr_t) ect;
|
|
||||||
ccw++;
|
|
||||||
}
|
|
||||||
if ((intensity & ~0x08) & 0x04) { /* erase track */
|
|
||||||
ect = (struct eckd_count *) data;
|
|
||||||
data += sizeof(struct eckd_count);
|
|
||||||
ect->cyl = address.cyl;
|
|
||||||
ect->head = address.head;
|
|
||||||
ect->record = 1;
|
|
||||||
ect->kl = 0;
|
|
||||||
ect->dl = 0;
|
|
||||||
ccw[-1].flags |= CCW_FLAG_CC;
|
|
||||||
ccw->cmd_code = DASD_ECKD_CCW_WRITE_CKD;
|
|
||||||
ccw->flags = CCW_FLAG_SLI;
|
|
||||||
ccw->count = 8;
|
|
||||||
ccw->cda = (__u32)(addr_t) ect;
|
|
||||||
} else { /* write remaining records */
|
|
||||||
for (i = 0; i < rpt; i++) {
|
|
||||||
ect = (struct eckd_count *) data;
|
ect = (struct eckd_count *) data;
|
||||||
data += sizeof(struct eckd_count);
|
data += sizeof(struct eckd_count);
|
||||||
ect->cyl = address.cyl;
|
ect->cyl = address.cyl;
|
||||||
ect->head = address.head;
|
ect->head = address.head;
|
||||||
ect->record = i + 1;
|
ect->record = 0;
|
||||||
ect->kl = 0;
|
ect->kl = 0;
|
||||||
ect->dl = fdata->blksize;
|
ect->dl = 8;
|
||||||
/* Check for special tracks 0-1 when formatting CDL */
|
|
||||||
if ((intensity & 0x08) &&
|
|
||||||
fdata->start_unit == 0) {
|
|
||||||
if (i < 3) {
|
|
||||||
ect->kl = 4;
|
|
||||||
ect->dl = sizes_trk0[i] - 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ((intensity & 0x08) &&
|
|
||||||
fdata->start_unit == 1) {
|
|
||||||
ect->kl = 44;
|
|
||||||
ect->dl = LABEL_SIZE - 44;
|
|
||||||
}
|
|
||||||
ccw[-1].flags |= CCW_FLAG_CC;
|
ccw[-1].flags |= CCW_FLAG_CC;
|
||||||
ccw->cmd_code = DASD_ECKD_CCW_WRITE_CKD;
|
ccw->cmd_code = DASD_ECKD_CCW_WRITE_RECORD_ZERO;
|
||||||
ccw->flags = CCW_FLAG_SLI;
|
ccw->flags = CCW_FLAG_SLI;
|
||||||
ccw->count = 8;
|
ccw->count = 8;
|
||||||
ccw->cda = (__u32)(addr_t) ect;
|
ccw->cda = (__u32)(addr_t) ect;
|
||||||
ccw++;
|
ccw++;
|
||||||
}
|
}
|
||||||
|
if ((intensity & ~0x08) & 0x04) { /* erase track */
|
||||||
|
ect = (struct eckd_count *) data;
|
||||||
|
data += sizeof(struct eckd_count);
|
||||||
|
ect->cyl = address.cyl;
|
||||||
|
ect->head = address.head;
|
||||||
|
ect->record = 1;
|
||||||
|
ect->kl = 0;
|
||||||
|
ect->dl = 0;
|
||||||
|
ccw[-1].flags |= CCW_FLAG_CC;
|
||||||
|
ccw->cmd_code = DASD_ECKD_CCW_WRITE_CKD;
|
||||||
|
ccw->flags = CCW_FLAG_SLI;
|
||||||
|
ccw->count = 8;
|
||||||
|
ccw->cda = (__u32)(addr_t) ect;
|
||||||
|
} else { /* write remaining records */
|
||||||
|
for (i = 0; i < rpt; i++) {
|
||||||
|
ect = (struct eckd_count *) data;
|
||||||
|
data += sizeof(struct eckd_count);
|
||||||
|
ect->cyl = address.cyl;
|
||||||
|
ect->head = address.head;
|
||||||
|
ect->record = i + 1;
|
||||||
|
ect->kl = 0;
|
||||||
|
ect->dl = fdata->blksize;
|
||||||
|
/*
|
||||||
|
* Check for special tracks 0-1
|
||||||
|
* when formatting CDL
|
||||||
|
*/
|
||||||
|
if ((intensity & 0x08) &&
|
||||||
|
fdata->start_unit == 0) {
|
||||||
|
if (i < 3) {
|
||||||
|
ect->kl = 4;
|
||||||
|
ect->dl = sizes_trk0[i] - 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((intensity & 0x08) &&
|
||||||
|
fdata->start_unit == 1) {
|
||||||
|
ect->kl = 44;
|
||||||
|
ect->dl = LABEL_SIZE - 44;
|
||||||
|
}
|
||||||
|
ccw[-1].flags |= CCW_FLAG_CC;
|
||||||
|
if (i != 0 || j == 0)
|
||||||
|
ccw->cmd_code =
|
||||||
|
DASD_ECKD_CCW_WRITE_CKD;
|
||||||
|
else
|
||||||
|
ccw->cmd_code =
|
||||||
|
DASD_ECKD_CCW_WRITE_CKD_MT;
|
||||||
|
ccw->flags = CCW_FLAG_SLI;
|
||||||
|
ccw->count = 8;
|
||||||
|
ccw->cda = (__u32)(addr_t) ect;
|
||||||
|
ccw++;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fcp->startdev = device;
|
|
||||||
fcp->memdev = device;
|
fcp->startdev = startdev;
|
||||||
|
fcp->memdev = startdev;
|
||||||
fcp->retries = 256;
|
fcp->retries = 256;
|
||||||
|
fcp->expires = startdev->default_expires * HZ;
|
||||||
fcp->buildclk = get_tod_clock();
|
fcp->buildclk = get_tod_clock();
|
||||||
fcp->status = DASD_CQR_FILLED;
|
fcp->status = DASD_CQR_FILLED;
|
||||||
|
|
||||||
return fcp;
|
return fcp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
dasd_eckd_format_device(struct dasd_device *base,
|
||||||
|
struct format_data_t *fdata)
|
||||||
|
{
|
||||||
|
struct dasd_ccw_req *cqr, *n;
|
||||||
|
struct dasd_block *block;
|
||||||
|
struct dasd_eckd_private *private;
|
||||||
|
struct list_head format_queue;
|
||||||
|
struct dasd_device *device;
|
||||||
|
int old_stop, format_step;
|
||||||
|
int step, rc = 0;
|
||||||
|
|
||||||
|
block = base->block;
|
||||||
|
private = (struct dasd_eckd_private *) base->private;
|
||||||
|
|
||||||
|
/* Sanity checks. */
|
||||||
|
if (fdata->start_unit >=
|
||||||
|
(private->real_cyl * private->rdc_data.trk_per_cyl)) {
|
||||||
|
dev_warn(&base->cdev->dev,
|
||||||
|
"Start track number %u used in formatting is too big\n",
|
||||||
|
fdata->start_unit);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
if (fdata->stop_unit >=
|
||||||
|
(private->real_cyl * private->rdc_data.trk_per_cyl)) {
|
||||||
|
dev_warn(&base->cdev->dev,
|
||||||
|
"Stop track number %u used in formatting is too big\n",
|
||||||
|
fdata->stop_unit);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
if (fdata->start_unit > fdata->stop_unit) {
|
||||||
|
dev_warn(&base->cdev->dev,
|
||||||
|
"Start track %u used in formatting exceeds end track\n",
|
||||||
|
fdata->start_unit);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
if (dasd_check_blocksize(fdata->blksize) != 0) {
|
||||||
|
dev_warn(&base->cdev->dev,
|
||||||
|
"The DASD cannot be formatted with block size %u\n",
|
||||||
|
fdata->blksize);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
INIT_LIST_HEAD(&format_queue);
|
||||||
|
old_stop = fdata->stop_unit;
|
||||||
|
|
||||||
|
while (fdata->start_unit <= 1) {
|
||||||
|
fdata->stop_unit = fdata->start_unit;
|
||||||
|
cqr = dasd_eckd_build_format(base, fdata);
|
||||||
|
list_add(&cqr->blocklist, &format_queue);
|
||||||
|
|
||||||
|
fdata->stop_unit = old_stop;
|
||||||
|
fdata->start_unit++;
|
||||||
|
|
||||||
|
if (fdata->start_unit > fdata->stop_unit)
|
||||||
|
goto sleep;
|
||||||
|
}
|
||||||
|
|
||||||
|
retry:
|
||||||
|
format_step = 255 / recs_per_track(&private->rdc_data, 0,
|
||||||
|
fdata->blksize);
|
||||||
|
while (fdata->start_unit <= old_stop) {
|
||||||
|
step = fdata->stop_unit - fdata->start_unit + 1;
|
||||||
|
if (step > format_step)
|
||||||
|
fdata->stop_unit = fdata->start_unit + format_step - 1;
|
||||||
|
|
||||||
|
cqr = dasd_eckd_build_format(base, fdata);
|
||||||
|
if (IS_ERR(cqr)) {
|
||||||
|
if (PTR_ERR(cqr) == -ENOMEM) {
|
||||||
|
/*
|
||||||
|
* not enough memory available
|
||||||
|
* go to out and start requests
|
||||||
|
* retry after first requests were finished
|
||||||
|
*/
|
||||||
|
fdata->stop_unit = old_stop;
|
||||||
|
goto sleep;
|
||||||
|
} else
|
||||||
|
return PTR_ERR(cqr);
|
||||||
|
}
|
||||||
|
list_add(&cqr->blocklist, &format_queue);
|
||||||
|
|
||||||
|
fdata->start_unit = fdata->stop_unit + 1;
|
||||||
|
fdata->stop_unit = old_stop;
|
||||||
|
}
|
||||||
|
|
||||||
|
sleep:
|
||||||
|
dasd_sleep_on_queue(&format_queue);
|
||||||
|
|
||||||
|
list_for_each_entry_safe(cqr, n, &format_queue, blocklist) {
|
||||||
|
device = cqr->startdev;
|
||||||
|
private = (struct dasd_eckd_private *) device->private;
|
||||||
|
if (cqr->status == DASD_CQR_FAILED)
|
||||||
|
rc = -EIO;
|
||||||
|
list_del_init(&cqr->blocklist);
|
||||||
|
dasd_sfree_request(cqr, device);
|
||||||
|
private->count--;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* in case of ENOMEM we need to retry after
|
||||||
|
* first requests are finished
|
||||||
|
*/
|
||||||
|
if (fdata->start_unit <= fdata->stop_unit)
|
||||||
|
goto retry;
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
static void dasd_eckd_handle_terminated_request(struct dasd_ccw_req *cqr)
|
static void dasd_eckd_handle_terminated_request(struct dasd_ccw_req *cqr)
|
||||||
{
|
{
|
||||||
cqr->status = DASD_CQR_FILLED;
|
cqr->status = DASD_CQR_FILLED;
|
||||||
|
@ -4305,8 +4432,9 @@ static struct dasd_discipline dasd_eckd_discipline = {
|
||||||
.uncheck_device = dasd_eckd_uncheck_device,
|
.uncheck_device = dasd_eckd_uncheck_device,
|
||||||
.do_analysis = dasd_eckd_do_analysis,
|
.do_analysis = dasd_eckd_do_analysis,
|
||||||
.verify_path = dasd_eckd_verify_path,
|
.verify_path = dasd_eckd_verify_path,
|
||||||
.ready_to_online = dasd_eckd_ready_to_online,
|
.basic_to_ready = dasd_eckd_basic_to_ready,
|
||||||
.online_to_ready = dasd_eckd_online_to_ready,
|
.online_to_ready = dasd_eckd_online_to_ready,
|
||||||
|
.ready_to_basic = dasd_eckd_ready_to_basic,
|
||||||
.fill_geometry = dasd_eckd_fill_geometry,
|
.fill_geometry = dasd_eckd_fill_geometry,
|
||||||
.start_IO = dasd_start_IO,
|
.start_IO = dasd_start_IO,
|
||||||
.term_IO = dasd_term_IO,
|
.term_IO = dasd_term_IO,
|
||||||
|
|
|
@ -300,10 +300,11 @@ struct dasd_discipline {
|
||||||
* Last things to do when a device is set online, and first things
|
* Last things to do when a device is set online, and first things
|
||||||
* when it is set offline.
|
* when it is set offline.
|
||||||
*/
|
*/
|
||||||
int (*ready_to_online) (struct dasd_device *);
|
int (*basic_to_ready) (struct dasd_device *);
|
||||||
int (*online_to_ready) (struct dasd_device *);
|
int (*online_to_ready) (struct dasd_device *);
|
||||||
|
int (*ready_to_basic) (struct dasd_device *);
|
||||||
|
|
||||||
/*
|
/* (struct dasd_device *);
|
||||||
* Device operation functions. build_cp creates a ccw chain for
|
* Device operation functions. build_cp creates a ccw chain for
|
||||||
* a block device request, start_io starts the request and
|
* a block device request, start_io starts the request and
|
||||||
* term_IO cancels it (e.g. in case of a timeout). format_device
|
* term_IO cancels it (e.g. in case of a timeout). format_device
|
||||||
|
@ -317,8 +318,8 @@ struct dasd_discipline {
|
||||||
int (*start_IO) (struct dasd_ccw_req *);
|
int (*start_IO) (struct dasd_ccw_req *);
|
||||||
int (*term_IO) (struct dasd_ccw_req *);
|
int (*term_IO) (struct dasd_ccw_req *);
|
||||||
void (*handle_terminated_request) (struct dasd_ccw_req *);
|
void (*handle_terminated_request) (struct dasd_ccw_req *);
|
||||||
struct dasd_ccw_req *(*format_device) (struct dasd_device *,
|
int (*format_device) (struct dasd_device *,
|
||||||
struct format_data_t *);
|
struct format_data_t *);
|
||||||
int (*free_cp) (struct dasd_ccw_req *, struct request *);
|
int (*free_cp) (struct dasd_ccw_req *, struct request *);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -672,6 +673,7 @@ int dasd_term_IO(struct dasd_ccw_req *);
|
||||||
void dasd_schedule_device_bh(struct dasd_device *);
|
void dasd_schedule_device_bh(struct dasd_device *);
|
||||||
void dasd_schedule_block_bh(struct dasd_block *);
|
void dasd_schedule_block_bh(struct dasd_block *);
|
||||||
int dasd_sleep_on(struct dasd_ccw_req *);
|
int dasd_sleep_on(struct dasd_ccw_req *);
|
||||||
|
int dasd_sleep_on_queue(struct list_head *);
|
||||||
int dasd_sleep_on_immediatly(struct dasd_ccw_req *);
|
int dasd_sleep_on_immediatly(struct dasd_ccw_req *);
|
||||||
int dasd_sleep_on_interruptible(struct dasd_ccw_req *);
|
int dasd_sleep_on_interruptible(struct dasd_ccw_req *);
|
||||||
void dasd_device_set_timer(struct dasd_device *, int);
|
void dasd_device_set_timer(struct dasd_device *, int);
|
||||||
|
|
|
@ -143,12 +143,12 @@ static int dasd_ioctl_resume(struct dasd_block *block)
|
||||||
/*
|
/*
|
||||||
* performs formatting of _device_ according to _fdata_
|
* performs formatting of _device_ according to _fdata_
|
||||||
* Note: The discipline's format_function is assumed to deliver formatting
|
* Note: The discipline's format_function is assumed to deliver formatting
|
||||||
* commands to format a single unit of the device. In terms of the ECKD
|
* commands to format multiple units of the device. In terms of the ECKD
|
||||||
* devices this means CCWs are generated to format a single track.
|
* devices this means CCWs are generated to format multiple tracks.
|
||||||
*/
|
*/
|
||||||
static int dasd_format(struct dasd_block *block, struct format_data_t *fdata)
|
static int
|
||||||
|
dasd_format(struct dasd_block *block, struct format_data_t *fdata)
|
||||||
{
|
{
|
||||||
struct dasd_ccw_req *cqr;
|
|
||||||
struct dasd_device *base;
|
struct dasd_device *base;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
|
@ -157,8 +157,8 @@ static int dasd_format(struct dasd_block *block, struct format_data_t *fdata)
|
||||||
return -EPERM;
|
return -EPERM;
|
||||||
|
|
||||||
if (base->state != DASD_STATE_BASIC) {
|
if (base->state != DASD_STATE_BASIC) {
|
||||||
pr_warning("%s: The DASD cannot be formatted while it is "
|
pr_warn("%s: The DASD cannot be formatted while it is enabled\n",
|
||||||
"enabled\n", dev_name(&base->cdev->dev));
|
dev_name(&base->cdev->dev));
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,21 +178,10 @@ static int dasd_format(struct dasd_block *block, struct format_data_t *fdata)
|
||||||
bdput(bdev);
|
bdput(bdev);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (fdata->start_unit <= fdata->stop_unit) {
|
rc = base->discipline->format_device(base, fdata);
|
||||||
cqr = base->discipline->format_device(base, fdata);
|
if (rc)
|
||||||
if (IS_ERR(cqr))
|
return rc;
|
||||||
return PTR_ERR(cqr);
|
|
||||||
rc = dasd_sleep_on_interruptible(cqr);
|
|
||||||
dasd_sfree_request(cqr, cqr->memdev);
|
|
||||||
if (rc) {
|
|
||||||
if (rc != -ERESTARTSYS)
|
|
||||||
pr_err("%s: Formatting unit %d failed with "
|
|
||||||
"rc=%d\n", dev_name(&base->cdev->dev),
|
|
||||||
fdata->start_unit, rc);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
fdata->start_unit++;
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue