|
@@ -2022,7 +2022,7 @@ static int dasd_eckd_do_analysis(struct dasd_block *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);
|
|
|
};
|
|
@@ -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->kick_validate);
|
|
|
+ return 0;
|
|
|
+};
|
|
|
+
|
|
|
+static int dasd_eckd_ready_to_basic(struct dasd_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 *
|
|
|
-dasd_eckd_format_device(struct dasd_device * device,
|
|
|
- struct format_data_t * fdata)
|
|
|
+dasd_eckd_build_format(struct dasd_device *base,
|
|
|
+ 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 eckd_count *ect;
|
|
|
+ struct ch_t address;
|
|
|
struct ccw1 *ccw;
|
|
|
void *data;
|
|
|
int rpt;
|
|
|
- struct ch_t address;
|
|
|
int cplength, datasize;
|
|
|
- int i;
|
|
|
+ int i, j;
|
|
|
int intensity = 0;
|
|
|
int r0_perm;
|
|
|
+ int nr_tracks;
|
|
|
|
|
|
- private = (struct dasd_eckd_private *) device->private;
|
|
|
- rpt = recs_per_track(&private->rdc_data, 0, fdata->blksize);
|
|
|
- set_ch_t(&address,
|
|
|
- fdata->start_unit / private->rdc_data.trk_per_cyl,
|
|
|
- fdata->start_unit % private->rdc_data.trk_per_cyl);
|
|
|
+ startdev = dasd_alias_get_start_dev(base);
|
|
|
+ if (!startdev)
|
|
|
+ startdev = base;
|
|
|
|
|
|
- /* Sanity checks. */
|
|
|
- if (fdata->start_unit >=
|
|
|
- (private->real_cyl * private->rdc_data.trk_per_cyl)) {
|
|
|
- dev_warn(&device->cdev->dev, "Start track number %d used in "
|
|
|
- "formatting is too big\n", fdata->start_unit);
|
|
|
- return ERR_PTR(-EINVAL);
|
|
|
- }
|
|
|
- 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);
|
|
|
- }
|
|
|
+ start_priv = (struct dasd_eckd_private *) startdev->private;
|
|
|
+ base_priv = (struct dasd_eckd_private *) base->private;
|
|
|
+
|
|
|
+ rpt = recs_per_track(&base_priv->rdc_data, 0, fdata->blksize);
|
|
|
+
|
|
|
+ nr_tracks = fdata->stop_unit - fdata->start_unit + 1;
|
|
|
|
|
|
/*
|
|
|
* 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;
|
|
|
intensity = fdata->intensity;
|
|
|
}
|
|
|
+
|
|
|
switch (intensity) {
|
|
|
case 0x00: /* Normal format */
|
|
|
case 0x08: /* Normal format, use cdl. */
|
|
|
- cplength = 2 + rpt;
|
|
|
- datasize = sizeof(struct DE_eckd_data) +
|
|
|
+ cplength = 2 + (rpt*nr_tracks);
|
|
|
+ datasize = sizeof(struct PFX_eckd_data) +
|
|
|
sizeof(struct LO_eckd_data) +
|
|
|
- rpt * sizeof(struct eckd_count);
|
|
|
+ rpt * nr_tracks * sizeof(struct eckd_count);
|
|
|
break;
|
|
|
case 0x01: /* Write record zero and format track. */
|
|
|
case 0x09: /* Write record zero and format track, use cdl. */
|
|
|
- cplength = 3 + rpt;
|
|
|
- datasize = sizeof(struct DE_eckd_data) +
|
|
|
+ cplength = 2 + rpt * nr_tracks;
|
|
|
+ datasize = sizeof(struct PFX_eckd_data) +
|
|
|
sizeof(struct LO_eckd_data) +
|
|
|
sizeof(struct eckd_count) +
|
|
|
- rpt * sizeof(struct eckd_count);
|
|
|
+ rpt * nr_tracks * sizeof(struct eckd_count);
|
|
|
break;
|
|
|
case 0x04: /* Invalidate track. */
|
|
|
case 0x0c: /* Invalidate track, use cdl. */
|
|
|
cplength = 3;
|
|
|
- datasize = sizeof(struct DE_eckd_data) +
|
|
|
+ datasize = sizeof(struct PFX_eckd_data) +
|
|
|
sizeof(struct LO_eckd_data) +
|
|
|
sizeof(struct eckd_count);
|
|
|
break;
|
|
|
default:
|
|
|
- dev_warn(&device->cdev->dev, "An I/O control call used "
|
|
|
- "incorrect flags 0x%x\n", fdata->intensity);
|
|
|
+ dev_warn(&startdev->cdev->dev,
|
|
|
+ "An I/O control call used incorrect flags 0x%x\n",
|
|
|
+ fdata->intensity);
|
|
|
return ERR_PTR(-EINVAL);
|
|
|
}
|
|
|
/* 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))
|
|
|
return fcp;
|
|
|
|
|
|
+ start_priv->count++;
|
|
|
data = fcp->data;
|
|
|
ccw = fcp->cpaddr;
|
|
|
|
|
|
switch (intensity & ~0x08) {
|
|
|
case 0x00: /* Normal format. */
|
|
|
- define_extent(ccw++, (struct DE_eckd_data *) data,
|
|
|
- fdata->start_unit, fdata->start_unit,
|
|
|
- DASD_ECKD_CCW_WRITE_CKD, device);
|
|
|
+ prefix(ccw++, (struct PFX_eckd_data *) data,
|
|
|
+ fdata->start_unit, fdata->stop_unit,
|
|
|
+ DASD_ECKD_CCW_WRITE_CKD, base, startdev);
|
|
|
/* grant subsystem permission to format R0 */
|
|
|
if (r0_perm)
|
|
|
- ((struct DE_eckd_data *)data)->ga_extended |= 0x04;
|
|
|
- data += sizeof(struct DE_eckd_data);
|
|
|
+ ((struct PFX_eckd_data *)data)
|
|
|
+ ->define_extent.ga_extended |= 0x04;
|
|
|
+ data += sizeof(struct PFX_eckd_data);
|
|
|
ccw[-1].flags |= CCW_FLAG_CC;
|
|
|
locate_record(ccw++, (struct LO_eckd_data *) data,
|
|
|
- fdata->start_unit, 0, rpt,
|
|
|
- DASD_ECKD_CCW_WRITE_CKD, device,
|
|
|
+ fdata->start_unit, 0, rpt*nr_tracks,
|
|
|
+ DASD_ECKD_CCW_WRITE_CKD, base,
|
|
|
fdata->blksize);
|
|
|
data += sizeof(struct LO_eckd_data);
|
|
|
break;
|
|
|
case 0x01: /* Write record zero + format track. */
|
|
|
- define_extent(ccw++, (struct DE_eckd_data *) data,
|
|
|
- fdata->start_unit, fdata->start_unit,
|
|
|
- DASD_ECKD_CCW_WRITE_RECORD_ZERO,
|
|
|
- device);
|
|
|
- data += sizeof(struct DE_eckd_data);
|
|
|
+ prefix(ccw++, (struct PFX_eckd_data *) data,
|
|
|
+ fdata->start_unit, fdata->stop_unit,
|
|
|
+ DASD_ECKD_CCW_WRITE_RECORD_ZERO,
|
|
|
+ base, startdev);
|
|
|
+ data += sizeof(struct PFX_eckd_data);
|
|
|
ccw[-1].flags |= CCW_FLAG_CC;
|
|
|
locate_record(ccw++, (struct LO_eckd_data *) data,
|
|
|
- fdata->start_unit, 0, rpt + 1,
|
|
|
- DASD_ECKD_CCW_WRITE_RECORD_ZERO, device,
|
|
|
- device->block->bp_block);
|
|
|
+ fdata->start_unit, 0, rpt * nr_tracks + 1,
|
|
|
+ DASD_ECKD_CCW_WRITE_RECORD_ZERO, base,
|
|
|
+ base->block->bp_block);
|
|
|
data += sizeof(struct LO_eckd_data);
|
|
|
break;
|
|
|
case 0x04: /* Invalidate track. */
|
|
|
- define_extent(ccw++, (struct DE_eckd_data *) data,
|
|
|
- fdata->start_unit, fdata->start_unit,
|
|
|
- DASD_ECKD_CCW_WRITE_CKD, device);
|
|
|
- data += sizeof(struct DE_eckd_data);
|
|
|
+ prefix(ccw++, (struct PFX_eckd_data *) data,
|
|
|
+ fdata->start_unit, fdata->stop_unit,
|
|
|
+ DASD_ECKD_CCW_WRITE_CKD, base, startdev);
|
|
|
+ data += sizeof(struct PFX_eckd_data);
|
|
|
ccw[-1].flags |= CCW_FLAG_CC;
|
|
|
locate_record(ccw++, (struct LO_eckd_data *) data,
|
|
|
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);
|
|
|
break;
|
|
|
}
|
|
|
- if (intensity & 0x01) { /* write record zero */
|
|
|
- ect = (struct eckd_count *) data;
|
|
|
- data += sizeof(struct eckd_count);
|
|
|
- ect->cyl = address.cyl;
|
|
|
- ect->head = address.head;
|
|
|
- ect->record = 0;
|
|
|
- ect->kl = 0;
|
|
|
- ect->dl = 8;
|
|
|
- ccw[-1].flags |= CCW_FLAG_CC;
|
|
|
- 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++) {
|
|
|
+
|
|
|
+ for (j = 0; j < nr_tracks; j++) {
|
|
|
+ /* calculate cylinder and head for the current track */
|
|
|
+ set_ch_t(&address,
|
|
|
+ (fdata->start_unit + j) /
|
|
|
+ base_priv->rdc_data.trk_per_cyl,
|
|
|
+ (fdata->start_unit + j) %
|
|
|
+ base_priv->rdc_data.trk_per_cyl);
|
|
|
+ if (intensity & 0x01) { /* write record zero */
|
|
|
ect = (struct eckd_count *) data;
|
|
|
data += sizeof(struct eckd_count);
|
|
|
ect->cyl = address.cyl;
|
|
|
ect->head = address.head;
|
|
|
- ect->record = i + 1;
|
|
|
+ ect->record = 0;
|
|
|
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;
|
|
|
- }
|
|
|
+ ect->dl = 8;
|
|
|
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->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;
|
|
|
+ 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->expires = startdev->default_expires * HZ;
|
|
|
fcp->buildclk = get_tod_clock();
|
|
|
fcp->status = DASD_CQR_FILLED;
|
|
|
+
|
|
|
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)
|
|
|
{
|
|
|
cqr->status = DASD_CQR_FILLED;
|
|
@@ -4305,8 +4432,9 @@ static struct dasd_discipline dasd_eckd_discipline = {
|
|
|
.uncheck_device = dasd_eckd_uncheck_device,
|
|
|
.do_analysis = dasd_eckd_do_analysis,
|
|
|
.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,
|
|
|
+ .ready_to_basic = dasd_eckd_ready_to_basic,
|
|
|
.fill_geometry = dasd_eckd_fill_geometry,
|
|
|
.start_IO = dasd_start_IO,
|
|
|
.term_IO = dasd_term_IO,
|