s390/dasd: improve speed of dasdfmt
authorStefan Haberland <stefan.haberland@de.ibm.com>
Mon, 15 Apr 2013 14:22:23 +0000 (16:22 +0200)
committerMartin Schwidefsky <schwidefsky@de.ibm.com>
Wed, 17 Apr 2013 12:07:34 +0000 (14:07 +0200)
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>
drivers/s390/block/dasd.c
drivers/s390/block/dasd_eckd.c
drivers/s390/block/dasd_int.h
drivers/s390/block/dasd_ioctl.c

index f1b7fdc..4195cc0 100644 (file)
@@ -246,7 +246,7 @@ static struct dentry *dasd_debugfs_setup(const char *name,
 static int dasd_state_known_to_basic(struct dasd_device *device)
 {
        struct dasd_block *block = device->block;
-       int rc;
+       int rc = 0;
 
        /* Allocate and register gendisk structure. */
        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");
 
        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)
 {
        int rc;
+
        if (device->block) {
                dasd_profile_exit(&device->block->profile);
                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)
                        rc = block->base->discipline->do_analysis(block);
                if (rc) {
-                       if (rc != -EAGAIN)
+                       if (rc != -EAGAIN) {
                                device->state = DASD_STATE_UNFMT;
+                               goto out;
+                       }
                        return rc;
                }
                dasd_setup_queue(block);
@@ -341,11 +345,16 @@ static int dasd_state_basic_to_ready(struct dasd_device *device)
                             block->blocks << block->s2b_shift);
                device->state = DASD_STATE_READY;
                rc = dasd_scan_partitions(block);
-               if (rc)
+               if (rc) {
                        device->state = DASD_STATE_BASIC;
+                       return rc;
+               }
        } else {
                device->state = DASD_STATE_READY;
        }
+out:
+       if (device->discipline->basic_to_ready)
+               rc = device->discipline->basic_to_ready(device);
        return rc;
 }
 
@@ -368,6 +377,11 @@ static int dasd_state_ready_to_basic(struct dasd_device *device)
 {
        int rc;
 
+       if (device->discipline->ready_to_basic) {
+               rc = device->discipline->ready_to_basic(device);
+               if (rc)
+                       return rc;
+       }
        device->state = DASD_STATE_BASIC;
        if (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
 dasd_state_ready_to_online(struct dasd_device * device)
 {
-       int rc;
        struct gendisk *disk;
        struct disk_part_iter piter;
        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;
        if (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)
                        return rc;
        }
+
        device->state = DASD_STATE_READY;
        if (device->block && !(device->features & DASD_FEATURE_USERAW)) {
                disk = device->block->bdev->bd_disk;
@@ -2223,6 +2232,77 @@ static int _dasd_sleep_on(struct dasd_ccw_req *maincqr, int interruptible)
        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
  * it's completion.
@@ -2232,6 +2312,15 @@ int dasd_sleep_on(struct dasd_ccw_req *cqr)
        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
  * interruptible for it's completion.
index 6999fd9..6a44b27 100644 (file)
@@ -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,
index 899e3f5..0785bd9 100644 (file)
@@ -300,10 +300,11 @@ struct dasd_discipline {
         * Last things to do when a device is set online, and first things
         * 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 (*ready_to_basic)  (struct dasd_device *);
 
-       /*
+       /* (struct dasd_device *);
         * Device operation functions. build_cp creates a ccw chain for
         * a block device request, start_io starts the request and
         * 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 (*term_IO) (struct dasd_ccw_req *);
        void (*handle_terminated_request) (struct dasd_ccw_req *);
-       struct dasd_ccw_req *(*format_device) (struct dasd_device *,
-                                              struct format_data_t *);
+       int (*format_device) (struct dasd_device *,
+                             struct format_data_t *);
        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_block_bh(struct dasd_block *);
 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_interruptible(struct dasd_ccw_req *);
 void dasd_device_set_timer(struct dasd_device *, int);
index 03c0e04..8be1b51 100644 (file)
@@ -143,12 +143,12 @@ static int dasd_ioctl_resume(struct dasd_block *block)
 /*
  * performs formatting of _device_ according to _fdata_
  * 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
- * devices this means CCWs are generated to format a single track.
+ * commands to format multiple units of the device. In terms of the ECKD
+ * 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;
        int rc;
 
@@ -157,8 +157,8 @@ static int dasd_format(struct dasd_block *block, struct format_data_t *fdata)
                return -EPERM;
 
        if (base->state != DASD_STATE_BASIC) {
-               pr_warning("%s: The DASD cannot be formatted while it is "
-                          "enabled\n",  dev_name(&base->cdev->dev));
+               pr_warn("%s: The DASD cannot be formatted while it is enabled\n",
+                       dev_name(&base->cdev->dev));
                return -EBUSY;
        }
 
@@ -178,21 +178,10 @@ static int dasd_format(struct dasd_block *block, struct format_data_t *fdata)
                bdput(bdev);
        }
 
-       while (fdata->start_unit <= fdata->stop_unit) {
-               cqr = base->discipline->format_device(base, fdata);
-               if (IS_ERR(cqr))
-                       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++;
-       }
+       rc = base->discipline->format_device(base, fdata);
+       if (rc)
+               return rc;
+
        return 0;
 }