Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d42e1712 authored by Stefan Haberland's avatar Stefan Haberland Committed by Martin Schwidefsky
Browse files

s390/dasd: improve speed of dasdfmt



Reorganize format IO requests and enable usage of PAV.

Signed-off-by: default avatarStefan Haberland <stefan.haberland@de.ibm.com>
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent e5dcf002
Loading
Loading
Loading
Loading
+99 −10
Original line number Diff line number Diff line
@@ -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.
+236 −108
Original line number Diff line number Diff line
@@ -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,
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,83 +2100,96 @@ 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,
		prefix(ccw++, (struct PFX_eckd_data *) data,
		       fdata->start_unit, fdata->stop_unit,
		       DASD_ECKD_CCW_WRITE_RECORD_ZERO,
			      device);
		data += sizeof(struct DE_eckd_data);
		       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;
	}

	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);
@@ -2220,7 +2227,10 @@ dasd_eckd_format_device(struct dasd_device * device,
				ect->record = i + 1;
				ect->kl = 0;
				ect->dl = fdata->blksize;
			/* Check for special tracks 0-1 when formatting CDL */
				/*
				 * Check for special tracks 0-1
				 * when formatting CDL
				 */
				if ((intensity & 0x08) &&
				    fdata->start_unit == 0) {
					if (i < 3) {
@@ -2234,21 +2244,138 @@ dasd_eckd_format_device(struct dasd_device * device,
					ect->dl = LABEL_SIZE - 44;
				}
				ccw[-1].flags |= CCW_FLAG_CC;
			ccw->cmd_code = DASD_ECKD_CCW_WRITE_CKD;
				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,
+6 −4
Original line number Diff line number Diff line
@@ -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,7 +318,7 @@ 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 *,
	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);
+10 −21
Original line number Diff line number Diff line
@@ -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);
	rc = base->discipline->format_device(base, fdata);
	if (rc)
		return rc;
		}
		fdata->start_unit++;
	}

	return 0;
}