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>
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
index f1b7fdc..4195cc0 100644
--- a/drivers/s390/block/dasd.c
+++ b/drivers/s390/block/dasd.c
@@ -246,7 +246,7 @@
 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 @@
 	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_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 @@
 		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 @@
 			     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 @@
 {
 	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_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 @@
 		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 @@
 	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.
@@ -2233,6 +2313,15 @@
 }
 
 /*
+ * 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.
  */