162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
462306a36Sopenharmony_ci *		    Horst Hummel <Horst.Hummel@de.ibm.com>
562306a36Sopenharmony_ci *		    Carsten Otte <Cotte@de.ibm.com>
662306a36Sopenharmony_ci *		    Martin Schwidefsky <schwidefsky@de.ibm.com>
762306a36Sopenharmony_ci * Bugreports.to..: <Linux390@de.ibm.com>
862306a36Sopenharmony_ci * Copyright IBM Corp. 1999, 2009
962306a36Sopenharmony_ci * EMC Symmetrix ioctl Copyright EMC Corporation, 2008
1062306a36Sopenharmony_ci * Author.........: Nigel Hislop <hislop_nigel@emc.com>
1162306a36Sopenharmony_ci */
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#define KMSG_COMPONENT "dasd-eckd"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include <linux/stddef.h>
1662306a36Sopenharmony_ci#include <linux/kernel.h>
1762306a36Sopenharmony_ci#include <linux/slab.h>
1862306a36Sopenharmony_ci#include <linux/hdreg.h>	/* HDIO_GETGEO			    */
1962306a36Sopenharmony_ci#include <linux/bio.h>
2062306a36Sopenharmony_ci#include <linux/module.h>
2162306a36Sopenharmony_ci#include <linux/compat.h>
2262306a36Sopenharmony_ci#include <linux/init.h>
2362306a36Sopenharmony_ci#include <linux/seq_file.h>
2462306a36Sopenharmony_ci#include <linux/uaccess.h>
2562306a36Sopenharmony_ci#include <linux/io.h>
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#include <asm/css_chars.h>
2862306a36Sopenharmony_ci#include <asm/debug.h>
2962306a36Sopenharmony_ci#include <asm/idals.h>
3062306a36Sopenharmony_ci#include <asm/ebcdic.h>
3162306a36Sopenharmony_ci#include <asm/cio.h>
3262306a36Sopenharmony_ci#include <asm/ccwdev.h>
3362306a36Sopenharmony_ci#include <asm/itcw.h>
3462306a36Sopenharmony_ci#include <asm/schid.h>
3562306a36Sopenharmony_ci#include <asm/chpid.h>
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#include "dasd_int.h"
3862306a36Sopenharmony_ci#include "dasd_eckd.h"
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci#ifdef PRINTK_HEADER
4162306a36Sopenharmony_ci#undef PRINTK_HEADER
4262306a36Sopenharmony_ci#endif				/* PRINTK_HEADER */
4362306a36Sopenharmony_ci#define PRINTK_HEADER "dasd(eckd):"
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci/*
4662306a36Sopenharmony_ci * raw track access always map to 64k in memory
4762306a36Sopenharmony_ci * so it maps to 16 blocks of 4k per track
4862306a36Sopenharmony_ci */
4962306a36Sopenharmony_ci#define DASD_RAW_BLOCK_PER_TRACK 16
5062306a36Sopenharmony_ci#define DASD_RAW_BLOCKSIZE 4096
5162306a36Sopenharmony_ci/* 64k are 128 x 512 byte sectors  */
5262306a36Sopenharmony_ci#define DASD_RAW_SECTORS_PER_TRACK 128
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ciMODULE_LICENSE("GPL");
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic struct dasd_discipline dasd_eckd_discipline;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci/* The ccw bus type uses this table to find devices that it sends to
5962306a36Sopenharmony_ci * dasd_eckd_probe */
6062306a36Sopenharmony_cistatic struct ccw_device_id dasd_eckd_ids[] = {
6162306a36Sopenharmony_ci	{ CCW_DEVICE_DEVTYPE (0x3990, 0, 0x3390, 0), .driver_info = 0x1},
6262306a36Sopenharmony_ci	{ CCW_DEVICE_DEVTYPE (0x2105, 0, 0x3390, 0), .driver_info = 0x2},
6362306a36Sopenharmony_ci	{ CCW_DEVICE_DEVTYPE (0x3880, 0, 0x3380, 0), .driver_info = 0x3},
6462306a36Sopenharmony_ci	{ CCW_DEVICE_DEVTYPE (0x3990, 0, 0x3380, 0), .driver_info = 0x4},
6562306a36Sopenharmony_ci	{ CCW_DEVICE_DEVTYPE (0x2105, 0, 0x3380, 0), .driver_info = 0x5},
6662306a36Sopenharmony_ci	{ CCW_DEVICE_DEVTYPE (0x9343, 0, 0x9345, 0), .driver_info = 0x6},
6762306a36Sopenharmony_ci	{ CCW_DEVICE_DEVTYPE (0x2107, 0, 0x3390, 0), .driver_info = 0x7},
6862306a36Sopenharmony_ci	{ CCW_DEVICE_DEVTYPE (0x2107, 0, 0x3380, 0), .driver_info = 0x8},
6962306a36Sopenharmony_ci	{ CCW_DEVICE_DEVTYPE (0x1750, 0, 0x3390, 0), .driver_info = 0x9},
7062306a36Sopenharmony_ci	{ CCW_DEVICE_DEVTYPE (0x1750, 0, 0x3380, 0), .driver_info = 0xa},
7162306a36Sopenharmony_ci	{ /* end of list */ },
7262306a36Sopenharmony_ci};
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(ccw, dasd_eckd_ids);
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic struct ccw_driver dasd_eckd_driver; /* see below */
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistatic void *rawpadpage;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci#define INIT_CQR_OK 0
8162306a36Sopenharmony_ci#define INIT_CQR_UNFORMATTED 1
8262306a36Sopenharmony_ci#define INIT_CQR_ERROR 2
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci/* emergency request for reserve/release */
8562306a36Sopenharmony_cistatic struct {
8662306a36Sopenharmony_ci	struct dasd_ccw_req cqr;
8762306a36Sopenharmony_ci	struct ccw1 ccw;
8862306a36Sopenharmony_ci	char data[32];
8962306a36Sopenharmony_ci} *dasd_reserve_req;
9062306a36Sopenharmony_cistatic DEFINE_MUTEX(dasd_reserve_mutex);
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic struct {
9362306a36Sopenharmony_ci	struct dasd_ccw_req cqr;
9462306a36Sopenharmony_ci	struct ccw1 ccw[2];
9562306a36Sopenharmony_ci	char data[40];
9662306a36Sopenharmony_ci} *dasd_vol_info_req;
9762306a36Sopenharmony_cistatic DEFINE_MUTEX(dasd_vol_info_mutex);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistruct ext_pool_exhaust_work_data {
10062306a36Sopenharmony_ci	struct work_struct worker;
10162306a36Sopenharmony_ci	struct dasd_device *device;
10262306a36Sopenharmony_ci	struct dasd_device *base;
10362306a36Sopenharmony_ci};
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci/* definitions for the path verification worker */
10662306a36Sopenharmony_cistruct pe_handler_work_data {
10762306a36Sopenharmony_ci	struct work_struct worker;
10862306a36Sopenharmony_ci	struct dasd_device *device;
10962306a36Sopenharmony_ci	struct dasd_ccw_req cqr;
11062306a36Sopenharmony_ci	struct ccw1 ccw;
11162306a36Sopenharmony_ci	__u8 rcd_buffer[DASD_ECKD_RCD_DATA_SIZE];
11262306a36Sopenharmony_ci	int isglobal;
11362306a36Sopenharmony_ci	__u8 tbvpm;
11462306a36Sopenharmony_ci	__u8 fcsecpm;
11562306a36Sopenharmony_ci};
11662306a36Sopenharmony_cistatic struct pe_handler_work_data *pe_handler_worker;
11762306a36Sopenharmony_cistatic DEFINE_MUTEX(dasd_pe_handler_mutex);
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cistruct check_attention_work_data {
12062306a36Sopenharmony_ci	struct work_struct worker;
12162306a36Sopenharmony_ci	struct dasd_device *device;
12262306a36Sopenharmony_ci	__u8 lpum;
12362306a36Sopenharmony_ci};
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cistatic int dasd_eckd_ext_pool_id(struct dasd_device *);
12662306a36Sopenharmony_cistatic int prepare_itcw(struct itcw *, unsigned int, unsigned int, int,
12762306a36Sopenharmony_ci			struct dasd_device *, struct dasd_device *,
12862306a36Sopenharmony_ci			unsigned int, int, unsigned int, unsigned int,
12962306a36Sopenharmony_ci			unsigned int, unsigned int);
13062306a36Sopenharmony_cistatic int dasd_eckd_query_pprc_status(struct dasd_device *,
13162306a36Sopenharmony_ci				       struct dasd_pprc_data_sc4 *);
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci/* initial attempt at a probe function. this can be simplified once
13462306a36Sopenharmony_ci * the other detection code is gone */
13562306a36Sopenharmony_cistatic int
13662306a36Sopenharmony_cidasd_eckd_probe (struct ccw_device *cdev)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	int ret;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	/* set ECKD specific ccw-device options */
14162306a36Sopenharmony_ci	ret = ccw_device_set_options(cdev, CCWDEV_ALLOW_FORCE |
14262306a36Sopenharmony_ci				     CCWDEV_DO_PATHGROUP | CCWDEV_DO_MULTIPATH);
14362306a36Sopenharmony_ci	if (ret) {
14462306a36Sopenharmony_ci		DBF_EVENT_DEVID(DBF_WARNING, cdev, "%s",
14562306a36Sopenharmony_ci				"dasd_eckd_probe: could not set "
14662306a36Sopenharmony_ci				"ccw-device options");
14762306a36Sopenharmony_ci		return ret;
14862306a36Sopenharmony_ci	}
14962306a36Sopenharmony_ci	ret = dasd_generic_probe(cdev);
15062306a36Sopenharmony_ci	return ret;
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_cistatic int
15462306a36Sopenharmony_cidasd_eckd_set_online(struct ccw_device *cdev)
15562306a36Sopenharmony_ci{
15662306a36Sopenharmony_ci	return dasd_generic_set_online(cdev, &dasd_eckd_discipline);
15762306a36Sopenharmony_ci}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_cistatic const int sizes_trk0[] = { 28, 148, 84 };
16062306a36Sopenharmony_ci#define LABEL_SIZE 140
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci/* head and record addresses of count_area read in analysis ccw */
16362306a36Sopenharmony_cistatic const int count_area_head[] = { 0, 0, 0, 0, 1 };
16462306a36Sopenharmony_cistatic const int count_area_rec[] = { 1, 2, 3, 4, 1 };
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_cistatic inline unsigned int
16762306a36Sopenharmony_ciceil_quot(unsigned int d1, unsigned int d2)
16862306a36Sopenharmony_ci{
16962306a36Sopenharmony_ci	return (d1 + (d2 - 1)) / d2;
17062306a36Sopenharmony_ci}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_cistatic unsigned int
17362306a36Sopenharmony_cirecs_per_track(struct dasd_eckd_characteristics * rdc,
17462306a36Sopenharmony_ci	       unsigned int kl, unsigned int dl)
17562306a36Sopenharmony_ci{
17662306a36Sopenharmony_ci	int dn, kn;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	switch (rdc->dev_type) {
17962306a36Sopenharmony_ci	case 0x3380:
18062306a36Sopenharmony_ci		if (kl)
18162306a36Sopenharmony_ci			return 1499 / (15 + 7 + ceil_quot(kl + 12, 32) +
18262306a36Sopenharmony_ci				       ceil_quot(dl + 12, 32));
18362306a36Sopenharmony_ci		else
18462306a36Sopenharmony_ci			return 1499 / (15 + ceil_quot(dl + 12, 32));
18562306a36Sopenharmony_ci	case 0x3390:
18662306a36Sopenharmony_ci		dn = ceil_quot(dl + 6, 232) + 1;
18762306a36Sopenharmony_ci		if (kl) {
18862306a36Sopenharmony_ci			kn = ceil_quot(kl + 6, 232) + 1;
18962306a36Sopenharmony_ci			return 1729 / (10 + 9 + ceil_quot(kl + 6 * kn, 34) +
19062306a36Sopenharmony_ci				       9 + ceil_quot(dl + 6 * dn, 34));
19162306a36Sopenharmony_ci		} else
19262306a36Sopenharmony_ci			return 1729 / (10 + 9 + ceil_quot(dl + 6 * dn, 34));
19362306a36Sopenharmony_ci	case 0x9345:
19462306a36Sopenharmony_ci		dn = ceil_quot(dl + 6, 232) + 1;
19562306a36Sopenharmony_ci		if (kl) {
19662306a36Sopenharmony_ci			kn = ceil_quot(kl + 6, 232) + 1;
19762306a36Sopenharmony_ci			return 1420 / (18 + 7 + ceil_quot(kl + 6 * kn, 34) +
19862306a36Sopenharmony_ci				       ceil_quot(dl + 6 * dn, 34));
19962306a36Sopenharmony_ci		} else
20062306a36Sopenharmony_ci			return 1420 / (18 + 7 + ceil_quot(dl + 6 * dn, 34));
20162306a36Sopenharmony_ci	}
20262306a36Sopenharmony_ci	return 0;
20362306a36Sopenharmony_ci}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_cistatic void set_ch_t(struct ch_t *geo, __u32 cyl, __u8 head)
20662306a36Sopenharmony_ci{
20762306a36Sopenharmony_ci	geo->cyl = (__u16) cyl;
20862306a36Sopenharmony_ci	geo->head = cyl >> 16;
20962306a36Sopenharmony_ci	geo->head <<= 4;
21062306a36Sopenharmony_ci	geo->head |= head;
21162306a36Sopenharmony_ci}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci/*
21462306a36Sopenharmony_ci * calculate failing track from sense data depending if
21562306a36Sopenharmony_ci * it is an EAV device or not
21662306a36Sopenharmony_ci */
21762306a36Sopenharmony_cistatic int dasd_eckd_track_from_irb(struct irb *irb, struct dasd_device *device,
21862306a36Sopenharmony_ci				    sector_t *track)
21962306a36Sopenharmony_ci{
22062306a36Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
22162306a36Sopenharmony_ci	u8 *sense = NULL;
22262306a36Sopenharmony_ci	u32 cyl;
22362306a36Sopenharmony_ci	u8 head;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	sense = dasd_get_sense(irb);
22662306a36Sopenharmony_ci	if (!sense) {
22762306a36Sopenharmony_ci		DBF_DEV_EVENT(DBF_WARNING, device, "%s",
22862306a36Sopenharmony_ci			      "ESE error no sense data\n");
22962306a36Sopenharmony_ci		return -EINVAL;
23062306a36Sopenharmony_ci	}
23162306a36Sopenharmony_ci	if (!(sense[27] & DASD_SENSE_BIT_2)) {
23262306a36Sopenharmony_ci		DBF_DEV_EVENT(DBF_WARNING, device, "%s",
23362306a36Sopenharmony_ci			      "ESE error no valid track data\n");
23462306a36Sopenharmony_ci		return -EINVAL;
23562306a36Sopenharmony_ci	}
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	if (sense[27] & DASD_SENSE_BIT_3) {
23862306a36Sopenharmony_ci		/* enhanced addressing */
23962306a36Sopenharmony_ci		cyl = sense[30] << 20;
24062306a36Sopenharmony_ci		cyl |= (sense[31] & 0xF0) << 12;
24162306a36Sopenharmony_ci		cyl |= sense[28] << 8;
24262306a36Sopenharmony_ci		cyl |= sense[29];
24362306a36Sopenharmony_ci	} else {
24462306a36Sopenharmony_ci		cyl = sense[29] << 8;
24562306a36Sopenharmony_ci		cyl |= sense[30];
24662306a36Sopenharmony_ci	}
24762306a36Sopenharmony_ci	head = sense[31] & 0x0F;
24862306a36Sopenharmony_ci	*track = cyl * private->rdc_data.trk_per_cyl + head;
24962306a36Sopenharmony_ci	return 0;
25062306a36Sopenharmony_ci}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_cistatic int set_timestamp(struct ccw1 *ccw, struct DE_eckd_data *data,
25362306a36Sopenharmony_ci		     struct dasd_device *device)
25462306a36Sopenharmony_ci{
25562306a36Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
25662306a36Sopenharmony_ci	int rc;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	rc = get_phys_clock(&data->ep_sys_time);
25962306a36Sopenharmony_ci	/*
26062306a36Sopenharmony_ci	 * Ignore return code if XRC is not supported or
26162306a36Sopenharmony_ci	 * sync clock is switched off
26262306a36Sopenharmony_ci	 */
26362306a36Sopenharmony_ci	if ((rc && !private->rdc_data.facilities.XRC_supported) ||
26462306a36Sopenharmony_ci	    rc == -EOPNOTSUPP || rc == -EACCES)
26562306a36Sopenharmony_ci		return 0;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	/* switch on System Time Stamp - needed for XRC Support */
26862306a36Sopenharmony_ci	data->ga_extended |= 0x08; /* switch on 'Time Stamp Valid'   */
26962306a36Sopenharmony_ci	data->ga_extended |= 0x02; /* switch on 'Extended Parameter' */
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	if (ccw) {
27262306a36Sopenharmony_ci		ccw->count = sizeof(struct DE_eckd_data);
27362306a36Sopenharmony_ci		ccw->flags |= CCW_FLAG_SLI;
27462306a36Sopenharmony_ci	}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	return rc;
27762306a36Sopenharmony_ci}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_cistatic int
28062306a36Sopenharmony_cidefine_extent(struct ccw1 *ccw, struct DE_eckd_data *data, unsigned int trk,
28162306a36Sopenharmony_ci	      unsigned int totrk, int cmd, struct dasd_device *device,
28262306a36Sopenharmony_ci	      int blksize)
28362306a36Sopenharmony_ci{
28462306a36Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
28562306a36Sopenharmony_ci	u16 heads, beghead, endhead;
28662306a36Sopenharmony_ci	u32 begcyl, endcyl;
28762306a36Sopenharmony_ci	int rc = 0;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	if (ccw) {
29062306a36Sopenharmony_ci		ccw->cmd_code = DASD_ECKD_CCW_DEFINE_EXTENT;
29162306a36Sopenharmony_ci		ccw->flags = 0;
29262306a36Sopenharmony_ci		ccw->count = 16;
29362306a36Sopenharmony_ci		ccw->cda = (__u32)virt_to_phys(data);
29462306a36Sopenharmony_ci	}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	memset(data, 0, sizeof(struct DE_eckd_data));
29762306a36Sopenharmony_ci	switch (cmd) {
29862306a36Sopenharmony_ci	case DASD_ECKD_CCW_READ_HOME_ADDRESS:
29962306a36Sopenharmony_ci	case DASD_ECKD_CCW_READ_RECORD_ZERO:
30062306a36Sopenharmony_ci	case DASD_ECKD_CCW_READ:
30162306a36Sopenharmony_ci	case DASD_ECKD_CCW_READ_MT:
30262306a36Sopenharmony_ci	case DASD_ECKD_CCW_READ_CKD:
30362306a36Sopenharmony_ci	case DASD_ECKD_CCW_READ_CKD_MT:
30462306a36Sopenharmony_ci	case DASD_ECKD_CCW_READ_KD:
30562306a36Sopenharmony_ci	case DASD_ECKD_CCW_READ_KD_MT:
30662306a36Sopenharmony_ci		data->mask.perm = 0x1;
30762306a36Sopenharmony_ci		data->attributes.operation = private->attrib.operation;
30862306a36Sopenharmony_ci		break;
30962306a36Sopenharmony_ci	case DASD_ECKD_CCW_READ_COUNT:
31062306a36Sopenharmony_ci		data->mask.perm = 0x1;
31162306a36Sopenharmony_ci		data->attributes.operation = DASD_BYPASS_CACHE;
31262306a36Sopenharmony_ci		break;
31362306a36Sopenharmony_ci	case DASD_ECKD_CCW_READ_TRACK:
31462306a36Sopenharmony_ci	case DASD_ECKD_CCW_READ_TRACK_DATA:
31562306a36Sopenharmony_ci		data->mask.perm = 0x1;
31662306a36Sopenharmony_ci		data->attributes.operation = private->attrib.operation;
31762306a36Sopenharmony_ci		data->blk_size = 0;
31862306a36Sopenharmony_ci		break;
31962306a36Sopenharmony_ci	case DASD_ECKD_CCW_WRITE:
32062306a36Sopenharmony_ci	case DASD_ECKD_CCW_WRITE_MT:
32162306a36Sopenharmony_ci	case DASD_ECKD_CCW_WRITE_KD:
32262306a36Sopenharmony_ci	case DASD_ECKD_CCW_WRITE_KD_MT:
32362306a36Sopenharmony_ci		data->mask.perm = 0x02;
32462306a36Sopenharmony_ci		data->attributes.operation = private->attrib.operation;
32562306a36Sopenharmony_ci		rc = set_timestamp(ccw, data, device);
32662306a36Sopenharmony_ci		break;
32762306a36Sopenharmony_ci	case DASD_ECKD_CCW_WRITE_CKD:
32862306a36Sopenharmony_ci	case DASD_ECKD_CCW_WRITE_CKD_MT:
32962306a36Sopenharmony_ci		data->attributes.operation = DASD_BYPASS_CACHE;
33062306a36Sopenharmony_ci		rc = set_timestamp(ccw, data, device);
33162306a36Sopenharmony_ci		break;
33262306a36Sopenharmony_ci	case DASD_ECKD_CCW_ERASE:
33362306a36Sopenharmony_ci	case DASD_ECKD_CCW_WRITE_HOME_ADDRESS:
33462306a36Sopenharmony_ci	case DASD_ECKD_CCW_WRITE_RECORD_ZERO:
33562306a36Sopenharmony_ci		data->mask.perm = 0x3;
33662306a36Sopenharmony_ci		data->mask.auth = 0x1;
33762306a36Sopenharmony_ci		data->attributes.operation = DASD_BYPASS_CACHE;
33862306a36Sopenharmony_ci		rc = set_timestamp(ccw, data, device);
33962306a36Sopenharmony_ci		break;
34062306a36Sopenharmony_ci	case DASD_ECKD_CCW_WRITE_FULL_TRACK:
34162306a36Sopenharmony_ci		data->mask.perm = 0x03;
34262306a36Sopenharmony_ci		data->attributes.operation = private->attrib.operation;
34362306a36Sopenharmony_ci		data->blk_size = 0;
34462306a36Sopenharmony_ci		break;
34562306a36Sopenharmony_ci	case DASD_ECKD_CCW_WRITE_TRACK_DATA:
34662306a36Sopenharmony_ci		data->mask.perm = 0x02;
34762306a36Sopenharmony_ci		data->attributes.operation = private->attrib.operation;
34862306a36Sopenharmony_ci		data->blk_size = blksize;
34962306a36Sopenharmony_ci		rc = set_timestamp(ccw, data, device);
35062306a36Sopenharmony_ci		break;
35162306a36Sopenharmony_ci	default:
35262306a36Sopenharmony_ci		dev_err(&device->cdev->dev,
35362306a36Sopenharmony_ci			"0x%x is not a known command\n", cmd);
35462306a36Sopenharmony_ci		break;
35562306a36Sopenharmony_ci	}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	data->attributes.mode = 0x3;	/* ECKD */
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	if ((private->rdc_data.cu_type == 0x2105 ||
36062306a36Sopenharmony_ci	     private->rdc_data.cu_type == 0x2107 ||
36162306a36Sopenharmony_ci	     private->rdc_data.cu_type == 0x1750)
36262306a36Sopenharmony_ci	    && !(private->uses_cdl && trk < 2))
36362306a36Sopenharmony_ci		data->ga_extended |= 0x40; /* Regular Data Format Mode */
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	heads = private->rdc_data.trk_per_cyl;
36662306a36Sopenharmony_ci	begcyl = trk / heads;
36762306a36Sopenharmony_ci	beghead = trk % heads;
36862306a36Sopenharmony_ci	endcyl = totrk / heads;
36962306a36Sopenharmony_ci	endhead = totrk % heads;
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	/* check for sequential prestage - enhance cylinder range */
37262306a36Sopenharmony_ci	if (data->attributes.operation == DASD_SEQ_PRESTAGE ||
37362306a36Sopenharmony_ci	    data->attributes.operation == DASD_SEQ_ACCESS) {
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci		if (endcyl + private->attrib.nr_cyl < private->real_cyl)
37662306a36Sopenharmony_ci			endcyl += private->attrib.nr_cyl;
37762306a36Sopenharmony_ci		else
37862306a36Sopenharmony_ci			endcyl = (private->real_cyl - 1);
37962306a36Sopenharmony_ci	}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	set_ch_t(&data->beg_ext, begcyl, beghead);
38262306a36Sopenharmony_ci	set_ch_t(&data->end_ext, endcyl, endhead);
38362306a36Sopenharmony_ci	return rc;
38462306a36Sopenharmony_ci}
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_cistatic void locate_record_ext(struct ccw1 *ccw, struct LRE_eckd_data *data,
38862306a36Sopenharmony_ci			      unsigned int trk, unsigned int rec_on_trk,
38962306a36Sopenharmony_ci			      int count, int cmd, struct dasd_device *device,
39062306a36Sopenharmony_ci			      unsigned int reclen, unsigned int tlf)
39162306a36Sopenharmony_ci{
39262306a36Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
39362306a36Sopenharmony_ci	int sector;
39462306a36Sopenharmony_ci	int dn, d;
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	if (ccw) {
39762306a36Sopenharmony_ci		ccw->cmd_code = DASD_ECKD_CCW_LOCATE_RECORD_EXT;
39862306a36Sopenharmony_ci		ccw->flags = 0;
39962306a36Sopenharmony_ci		if (cmd == DASD_ECKD_CCW_WRITE_FULL_TRACK)
40062306a36Sopenharmony_ci			ccw->count = 22;
40162306a36Sopenharmony_ci		else
40262306a36Sopenharmony_ci			ccw->count = 20;
40362306a36Sopenharmony_ci		ccw->cda = (__u32)virt_to_phys(data);
40462306a36Sopenharmony_ci	}
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	memset(data, 0, sizeof(*data));
40762306a36Sopenharmony_ci	sector = 0;
40862306a36Sopenharmony_ci	if (rec_on_trk) {
40962306a36Sopenharmony_ci		switch (private->rdc_data.dev_type) {
41062306a36Sopenharmony_ci		case 0x3390:
41162306a36Sopenharmony_ci			dn = ceil_quot(reclen + 6, 232);
41262306a36Sopenharmony_ci			d = 9 + ceil_quot(reclen + 6 * (dn + 1), 34);
41362306a36Sopenharmony_ci			sector = (49 + (rec_on_trk - 1) * (10 + d)) / 8;
41462306a36Sopenharmony_ci			break;
41562306a36Sopenharmony_ci		case 0x3380:
41662306a36Sopenharmony_ci			d = 7 + ceil_quot(reclen + 12, 32);
41762306a36Sopenharmony_ci			sector = (39 + (rec_on_trk - 1) * (8 + d)) / 7;
41862306a36Sopenharmony_ci			break;
41962306a36Sopenharmony_ci		}
42062306a36Sopenharmony_ci	}
42162306a36Sopenharmony_ci	data->sector = sector;
42262306a36Sopenharmony_ci	/* note: meaning of count depends on the operation
42362306a36Sopenharmony_ci	 *	 for record based I/O it's the number of records, but for
42462306a36Sopenharmony_ci	 *	 track based I/O it's the number of tracks
42562306a36Sopenharmony_ci	 */
42662306a36Sopenharmony_ci	data->count = count;
42762306a36Sopenharmony_ci	switch (cmd) {
42862306a36Sopenharmony_ci	case DASD_ECKD_CCW_WRITE_HOME_ADDRESS:
42962306a36Sopenharmony_ci		data->operation.orientation = 0x3;
43062306a36Sopenharmony_ci		data->operation.operation = 0x03;
43162306a36Sopenharmony_ci		break;
43262306a36Sopenharmony_ci	case DASD_ECKD_CCW_READ_HOME_ADDRESS:
43362306a36Sopenharmony_ci		data->operation.orientation = 0x3;
43462306a36Sopenharmony_ci		data->operation.operation = 0x16;
43562306a36Sopenharmony_ci		break;
43662306a36Sopenharmony_ci	case DASD_ECKD_CCW_WRITE_RECORD_ZERO:
43762306a36Sopenharmony_ci		data->operation.orientation = 0x1;
43862306a36Sopenharmony_ci		data->operation.operation = 0x03;
43962306a36Sopenharmony_ci		data->count++;
44062306a36Sopenharmony_ci		break;
44162306a36Sopenharmony_ci	case DASD_ECKD_CCW_READ_RECORD_ZERO:
44262306a36Sopenharmony_ci		data->operation.orientation = 0x3;
44362306a36Sopenharmony_ci		data->operation.operation = 0x16;
44462306a36Sopenharmony_ci		data->count++;
44562306a36Sopenharmony_ci		break;
44662306a36Sopenharmony_ci	case DASD_ECKD_CCW_WRITE:
44762306a36Sopenharmony_ci	case DASD_ECKD_CCW_WRITE_MT:
44862306a36Sopenharmony_ci	case DASD_ECKD_CCW_WRITE_KD:
44962306a36Sopenharmony_ci	case DASD_ECKD_CCW_WRITE_KD_MT:
45062306a36Sopenharmony_ci		data->auxiliary.length_valid = 0x1;
45162306a36Sopenharmony_ci		data->length = reclen;
45262306a36Sopenharmony_ci		data->operation.operation = 0x01;
45362306a36Sopenharmony_ci		break;
45462306a36Sopenharmony_ci	case DASD_ECKD_CCW_WRITE_CKD:
45562306a36Sopenharmony_ci	case DASD_ECKD_CCW_WRITE_CKD_MT:
45662306a36Sopenharmony_ci		data->auxiliary.length_valid = 0x1;
45762306a36Sopenharmony_ci		data->length = reclen;
45862306a36Sopenharmony_ci		data->operation.operation = 0x03;
45962306a36Sopenharmony_ci		break;
46062306a36Sopenharmony_ci	case DASD_ECKD_CCW_WRITE_FULL_TRACK:
46162306a36Sopenharmony_ci		data->operation.orientation = 0x0;
46262306a36Sopenharmony_ci		data->operation.operation = 0x3F;
46362306a36Sopenharmony_ci		data->extended_operation = 0x11;
46462306a36Sopenharmony_ci		data->length = 0;
46562306a36Sopenharmony_ci		data->extended_parameter_length = 0x02;
46662306a36Sopenharmony_ci		if (data->count > 8) {
46762306a36Sopenharmony_ci			data->extended_parameter[0] = 0xFF;
46862306a36Sopenharmony_ci			data->extended_parameter[1] = 0xFF;
46962306a36Sopenharmony_ci			data->extended_parameter[1] <<= (16 - count);
47062306a36Sopenharmony_ci		} else {
47162306a36Sopenharmony_ci			data->extended_parameter[0] = 0xFF;
47262306a36Sopenharmony_ci			data->extended_parameter[0] <<= (8 - count);
47362306a36Sopenharmony_ci			data->extended_parameter[1] = 0x00;
47462306a36Sopenharmony_ci		}
47562306a36Sopenharmony_ci		data->sector = 0xFF;
47662306a36Sopenharmony_ci		break;
47762306a36Sopenharmony_ci	case DASD_ECKD_CCW_WRITE_TRACK_DATA:
47862306a36Sopenharmony_ci		data->auxiliary.length_valid = 0x1;
47962306a36Sopenharmony_ci		data->length = reclen;	/* not tlf, as one might think */
48062306a36Sopenharmony_ci		data->operation.operation = 0x3F;
48162306a36Sopenharmony_ci		data->extended_operation = 0x23;
48262306a36Sopenharmony_ci		break;
48362306a36Sopenharmony_ci	case DASD_ECKD_CCW_READ:
48462306a36Sopenharmony_ci	case DASD_ECKD_CCW_READ_MT:
48562306a36Sopenharmony_ci	case DASD_ECKD_CCW_READ_KD:
48662306a36Sopenharmony_ci	case DASD_ECKD_CCW_READ_KD_MT:
48762306a36Sopenharmony_ci		data->auxiliary.length_valid = 0x1;
48862306a36Sopenharmony_ci		data->length = reclen;
48962306a36Sopenharmony_ci		data->operation.operation = 0x06;
49062306a36Sopenharmony_ci		break;
49162306a36Sopenharmony_ci	case DASD_ECKD_CCW_READ_CKD:
49262306a36Sopenharmony_ci	case DASD_ECKD_CCW_READ_CKD_MT:
49362306a36Sopenharmony_ci		data->auxiliary.length_valid = 0x1;
49462306a36Sopenharmony_ci		data->length = reclen;
49562306a36Sopenharmony_ci		data->operation.operation = 0x16;
49662306a36Sopenharmony_ci		break;
49762306a36Sopenharmony_ci	case DASD_ECKD_CCW_READ_COUNT:
49862306a36Sopenharmony_ci		data->operation.operation = 0x06;
49962306a36Sopenharmony_ci		break;
50062306a36Sopenharmony_ci	case DASD_ECKD_CCW_READ_TRACK:
50162306a36Sopenharmony_ci		data->operation.orientation = 0x1;
50262306a36Sopenharmony_ci		data->operation.operation = 0x0C;
50362306a36Sopenharmony_ci		data->extended_parameter_length = 0;
50462306a36Sopenharmony_ci		data->sector = 0xFF;
50562306a36Sopenharmony_ci		break;
50662306a36Sopenharmony_ci	case DASD_ECKD_CCW_READ_TRACK_DATA:
50762306a36Sopenharmony_ci		data->auxiliary.length_valid = 0x1;
50862306a36Sopenharmony_ci		data->length = tlf;
50962306a36Sopenharmony_ci		data->operation.operation = 0x0C;
51062306a36Sopenharmony_ci		break;
51162306a36Sopenharmony_ci	case DASD_ECKD_CCW_ERASE:
51262306a36Sopenharmony_ci		data->length = reclen;
51362306a36Sopenharmony_ci		data->auxiliary.length_valid = 0x1;
51462306a36Sopenharmony_ci		data->operation.operation = 0x0b;
51562306a36Sopenharmony_ci		break;
51662306a36Sopenharmony_ci	default:
51762306a36Sopenharmony_ci		DBF_DEV_EVENT(DBF_ERR, device,
51862306a36Sopenharmony_ci			    "fill LRE unknown opcode 0x%x", cmd);
51962306a36Sopenharmony_ci		BUG();
52062306a36Sopenharmony_ci	}
52162306a36Sopenharmony_ci	set_ch_t(&data->seek_addr,
52262306a36Sopenharmony_ci		 trk / private->rdc_data.trk_per_cyl,
52362306a36Sopenharmony_ci		 trk % private->rdc_data.trk_per_cyl);
52462306a36Sopenharmony_ci	data->search_arg.cyl = data->seek_addr.cyl;
52562306a36Sopenharmony_ci	data->search_arg.head = data->seek_addr.head;
52662306a36Sopenharmony_ci	data->search_arg.record = rec_on_trk;
52762306a36Sopenharmony_ci}
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_cistatic int prefix_LRE(struct ccw1 *ccw, struct PFX_eckd_data *pfxdata,
53062306a36Sopenharmony_ci		      unsigned int trk, unsigned int totrk, int cmd,
53162306a36Sopenharmony_ci		      struct dasd_device *basedev, struct dasd_device *startdev,
53262306a36Sopenharmony_ci		      unsigned int format, unsigned int rec_on_trk, int count,
53362306a36Sopenharmony_ci		      unsigned int blksize, unsigned int tlf)
53462306a36Sopenharmony_ci{
53562306a36Sopenharmony_ci	struct dasd_eckd_private *basepriv, *startpriv;
53662306a36Sopenharmony_ci	struct LRE_eckd_data *lredata;
53762306a36Sopenharmony_ci	struct DE_eckd_data *dedata;
53862306a36Sopenharmony_ci	int rc = 0;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	basepriv = basedev->private;
54162306a36Sopenharmony_ci	startpriv = startdev->private;
54262306a36Sopenharmony_ci	dedata = &pfxdata->define_extent;
54362306a36Sopenharmony_ci	lredata = &pfxdata->locate_record;
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	ccw->cmd_code = DASD_ECKD_CCW_PFX;
54662306a36Sopenharmony_ci	ccw->flags = 0;
54762306a36Sopenharmony_ci	if (cmd == DASD_ECKD_CCW_WRITE_FULL_TRACK) {
54862306a36Sopenharmony_ci		ccw->count = sizeof(*pfxdata) + 2;
54962306a36Sopenharmony_ci		ccw->cda = (__u32)virt_to_phys(pfxdata);
55062306a36Sopenharmony_ci		memset(pfxdata, 0, sizeof(*pfxdata) + 2);
55162306a36Sopenharmony_ci	} else {
55262306a36Sopenharmony_ci		ccw->count = sizeof(*pfxdata);
55362306a36Sopenharmony_ci		ccw->cda = (__u32)virt_to_phys(pfxdata);
55462306a36Sopenharmony_ci		memset(pfxdata, 0, sizeof(*pfxdata));
55562306a36Sopenharmony_ci	}
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	/* prefix data */
55862306a36Sopenharmony_ci	if (format > 1) {
55962306a36Sopenharmony_ci		DBF_DEV_EVENT(DBF_ERR, basedev,
56062306a36Sopenharmony_ci			      "PFX LRE unknown format 0x%x", format);
56162306a36Sopenharmony_ci		BUG();
56262306a36Sopenharmony_ci		return -EINVAL;
56362306a36Sopenharmony_ci	}
56462306a36Sopenharmony_ci	pfxdata->format = format;
56562306a36Sopenharmony_ci	pfxdata->base_address = basepriv->conf.ned->unit_addr;
56662306a36Sopenharmony_ci	pfxdata->base_lss = basepriv->conf.ned->ID;
56762306a36Sopenharmony_ci	pfxdata->validity.define_extent = 1;
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	/* private uid is kept up to date, conf_data may be outdated */
57062306a36Sopenharmony_ci	if (startpriv->uid.type == UA_BASE_PAV_ALIAS)
57162306a36Sopenharmony_ci		pfxdata->validity.verify_base = 1;
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	if (startpriv->uid.type == UA_HYPER_PAV_ALIAS) {
57462306a36Sopenharmony_ci		pfxdata->validity.verify_base = 1;
57562306a36Sopenharmony_ci		pfxdata->validity.hyper_pav = 1;
57662306a36Sopenharmony_ci	}
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	rc = define_extent(NULL, dedata, trk, totrk, cmd, basedev, blksize);
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	/*
58162306a36Sopenharmony_ci	 * For some commands the System Time Stamp is set in the define extent
58262306a36Sopenharmony_ci	 * data when XRC is supported. The validity of the time stamp must be
58362306a36Sopenharmony_ci	 * reflected in the prefix data as well.
58462306a36Sopenharmony_ci	 */
58562306a36Sopenharmony_ci	if (dedata->ga_extended & 0x08 && dedata->ga_extended & 0x02)
58662306a36Sopenharmony_ci		pfxdata->validity.time_stamp = 1; /* 'Time Stamp Valid'   */
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	if (format == 1) {
58962306a36Sopenharmony_ci		locate_record_ext(NULL, lredata, trk, rec_on_trk, count, cmd,
59062306a36Sopenharmony_ci				  basedev, blksize, tlf);
59162306a36Sopenharmony_ci	}
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	return rc;
59462306a36Sopenharmony_ci}
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_cistatic int prefix(struct ccw1 *ccw, struct PFX_eckd_data *pfxdata,
59762306a36Sopenharmony_ci		  unsigned int trk, unsigned int totrk, int cmd,
59862306a36Sopenharmony_ci		  struct dasd_device *basedev, struct dasd_device *startdev)
59962306a36Sopenharmony_ci{
60062306a36Sopenharmony_ci	return prefix_LRE(ccw, pfxdata, trk, totrk, cmd, basedev, startdev,
60162306a36Sopenharmony_ci			  0, 0, 0, 0, 0);
60262306a36Sopenharmony_ci}
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_cistatic void
60562306a36Sopenharmony_cilocate_record(struct ccw1 *ccw, struct LO_eckd_data *data, unsigned int trk,
60662306a36Sopenharmony_ci	      unsigned int rec_on_trk, int no_rec, int cmd,
60762306a36Sopenharmony_ci	      struct dasd_device * device, int reclen)
60862306a36Sopenharmony_ci{
60962306a36Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
61062306a36Sopenharmony_ci	int sector;
61162306a36Sopenharmony_ci	int dn, d;
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	DBF_DEV_EVENT(DBF_INFO, device,
61462306a36Sopenharmony_ci		  "Locate: trk %d, rec %d, no_rec %d, cmd %d, reclen %d",
61562306a36Sopenharmony_ci		  trk, rec_on_trk, no_rec, cmd, reclen);
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	ccw->cmd_code = DASD_ECKD_CCW_LOCATE_RECORD;
61862306a36Sopenharmony_ci	ccw->flags = 0;
61962306a36Sopenharmony_ci	ccw->count = 16;
62062306a36Sopenharmony_ci	ccw->cda = (__u32)virt_to_phys(data);
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	memset(data, 0, sizeof(struct LO_eckd_data));
62362306a36Sopenharmony_ci	sector = 0;
62462306a36Sopenharmony_ci	if (rec_on_trk) {
62562306a36Sopenharmony_ci		switch (private->rdc_data.dev_type) {
62662306a36Sopenharmony_ci		case 0x3390:
62762306a36Sopenharmony_ci			dn = ceil_quot(reclen + 6, 232);
62862306a36Sopenharmony_ci			d = 9 + ceil_quot(reclen + 6 * (dn + 1), 34);
62962306a36Sopenharmony_ci			sector = (49 + (rec_on_trk - 1) * (10 + d)) / 8;
63062306a36Sopenharmony_ci			break;
63162306a36Sopenharmony_ci		case 0x3380:
63262306a36Sopenharmony_ci			d = 7 + ceil_quot(reclen + 12, 32);
63362306a36Sopenharmony_ci			sector = (39 + (rec_on_trk - 1) * (8 + d)) / 7;
63462306a36Sopenharmony_ci			break;
63562306a36Sopenharmony_ci		}
63662306a36Sopenharmony_ci	}
63762306a36Sopenharmony_ci	data->sector = sector;
63862306a36Sopenharmony_ci	data->count = no_rec;
63962306a36Sopenharmony_ci	switch (cmd) {
64062306a36Sopenharmony_ci	case DASD_ECKD_CCW_WRITE_HOME_ADDRESS:
64162306a36Sopenharmony_ci		data->operation.orientation = 0x3;
64262306a36Sopenharmony_ci		data->operation.operation = 0x03;
64362306a36Sopenharmony_ci		break;
64462306a36Sopenharmony_ci	case DASD_ECKD_CCW_READ_HOME_ADDRESS:
64562306a36Sopenharmony_ci		data->operation.orientation = 0x3;
64662306a36Sopenharmony_ci		data->operation.operation = 0x16;
64762306a36Sopenharmony_ci		break;
64862306a36Sopenharmony_ci	case DASD_ECKD_CCW_WRITE_RECORD_ZERO:
64962306a36Sopenharmony_ci		data->operation.orientation = 0x1;
65062306a36Sopenharmony_ci		data->operation.operation = 0x03;
65162306a36Sopenharmony_ci		data->count++;
65262306a36Sopenharmony_ci		break;
65362306a36Sopenharmony_ci	case DASD_ECKD_CCW_READ_RECORD_ZERO:
65462306a36Sopenharmony_ci		data->operation.orientation = 0x3;
65562306a36Sopenharmony_ci		data->operation.operation = 0x16;
65662306a36Sopenharmony_ci		data->count++;
65762306a36Sopenharmony_ci		break;
65862306a36Sopenharmony_ci	case DASD_ECKD_CCW_WRITE:
65962306a36Sopenharmony_ci	case DASD_ECKD_CCW_WRITE_MT:
66062306a36Sopenharmony_ci	case DASD_ECKD_CCW_WRITE_KD:
66162306a36Sopenharmony_ci	case DASD_ECKD_CCW_WRITE_KD_MT:
66262306a36Sopenharmony_ci		data->auxiliary.last_bytes_used = 0x1;
66362306a36Sopenharmony_ci		data->length = reclen;
66462306a36Sopenharmony_ci		data->operation.operation = 0x01;
66562306a36Sopenharmony_ci		break;
66662306a36Sopenharmony_ci	case DASD_ECKD_CCW_WRITE_CKD:
66762306a36Sopenharmony_ci	case DASD_ECKD_CCW_WRITE_CKD_MT:
66862306a36Sopenharmony_ci		data->auxiliary.last_bytes_used = 0x1;
66962306a36Sopenharmony_ci		data->length = reclen;
67062306a36Sopenharmony_ci		data->operation.operation = 0x03;
67162306a36Sopenharmony_ci		break;
67262306a36Sopenharmony_ci	case DASD_ECKD_CCW_READ:
67362306a36Sopenharmony_ci	case DASD_ECKD_CCW_READ_MT:
67462306a36Sopenharmony_ci	case DASD_ECKD_CCW_READ_KD:
67562306a36Sopenharmony_ci	case DASD_ECKD_CCW_READ_KD_MT:
67662306a36Sopenharmony_ci		data->auxiliary.last_bytes_used = 0x1;
67762306a36Sopenharmony_ci		data->length = reclen;
67862306a36Sopenharmony_ci		data->operation.operation = 0x06;
67962306a36Sopenharmony_ci		break;
68062306a36Sopenharmony_ci	case DASD_ECKD_CCW_READ_CKD:
68162306a36Sopenharmony_ci	case DASD_ECKD_CCW_READ_CKD_MT:
68262306a36Sopenharmony_ci		data->auxiliary.last_bytes_used = 0x1;
68362306a36Sopenharmony_ci		data->length = reclen;
68462306a36Sopenharmony_ci		data->operation.operation = 0x16;
68562306a36Sopenharmony_ci		break;
68662306a36Sopenharmony_ci	case DASD_ECKD_CCW_READ_COUNT:
68762306a36Sopenharmony_ci		data->operation.operation = 0x06;
68862306a36Sopenharmony_ci		break;
68962306a36Sopenharmony_ci	case DASD_ECKD_CCW_ERASE:
69062306a36Sopenharmony_ci		data->length = reclen;
69162306a36Sopenharmony_ci		data->auxiliary.last_bytes_used = 0x1;
69262306a36Sopenharmony_ci		data->operation.operation = 0x0b;
69362306a36Sopenharmony_ci		break;
69462306a36Sopenharmony_ci	default:
69562306a36Sopenharmony_ci		DBF_DEV_EVENT(DBF_ERR, device, "unknown locate record "
69662306a36Sopenharmony_ci			      "opcode 0x%x", cmd);
69762306a36Sopenharmony_ci	}
69862306a36Sopenharmony_ci	set_ch_t(&data->seek_addr,
69962306a36Sopenharmony_ci		 trk / private->rdc_data.trk_per_cyl,
70062306a36Sopenharmony_ci		 trk % private->rdc_data.trk_per_cyl);
70162306a36Sopenharmony_ci	data->search_arg.cyl = data->seek_addr.cyl;
70262306a36Sopenharmony_ci	data->search_arg.head = data->seek_addr.head;
70362306a36Sopenharmony_ci	data->search_arg.record = rec_on_trk;
70462306a36Sopenharmony_ci}
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci/*
70762306a36Sopenharmony_ci * Returns 1 if the block is one of the special blocks that needs
70862306a36Sopenharmony_ci * to get read/written with the KD variant of the command.
70962306a36Sopenharmony_ci * That is DASD_ECKD_READ_KD_MT instead of DASD_ECKD_READ_MT and
71062306a36Sopenharmony_ci * DASD_ECKD_WRITE_KD_MT instead of DASD_ECKD_WRITE_MT.
71162306a36Sopenharmony_ci * Luckily the KD variants differ only by one bit (0x08) from the
71262306a36Sopenharmony_ci * normal variant. So don't wonder about code like:
71362306a36Sopenharmony_ci * if (dasd_eckd_cdl_special(blk_per_trk, recid))
71462306a36Sopenharmony_ci *         ccw->cmd_code |= 0x8;
71562306a36Sopenharmony_ci */
71662306a36Sopenharmony_cistatic inline int
71762306a36Sopenharmony_cidasd_eckd_cdl_special(int blk_per_trk, int recid)
71862306a36Sopenharmony_ci{
71962306a36Sopenharmony_ci	if (recid < 3)
72062306a36Sopenharmony_ci		return 1;
72162306a36Sopenharmony_ci	if (recid < blk_per_trk)
72262306a36Sopenharmony_ci		return 0;
72362306a36Sopenharmony_ci	if (recid < 2 * blk_per_trk)
72462306a36Sopenharmony_ci		return 1;
72562306a36Sopenharmony_ci	return 0;
72662306a36Sopenharmony_ci}
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci/*
72962306a36Sopenharmony_ci * Returns the record size for the special blocks of the cdl format.
73062306a36Sopenharmony_ci * Only returns something useful if dasd_eckd_cdl_special is true
73162306a36Sopenharmony_ci * for the recid.
73262306a36Sopenharmony_ci */
73362306a36Sopenharmony_cistatic inline int
73462306a36Sopenharmony_cidasd_eckd_cdl_reclen(int recid)
73562306a36Sopenharmony_ci{
73662306a36Sopenharmony_ci	if (recid < 3)
73762306a36Sopenharmony_ci		return sizes_trk0[recid];
73862306a36Sopenharmony_ci	return LABEL_SIZE;
73962306a36Sopenharmony_ci}
74062306a36Sopenharmony_ci/* create unique id from private structure. */
74162306a36Sopenharmony_cistatic void create_uid(struct dasd_conf *conf, struct dasd_uid *uid)
74262306a36Sopenharmony_ci{
74362306a36Sopenharmony_ci	int count;
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	memset(uid, 0, sizeof(struct dasd_uid));
74662306a36Sopenharmony_ci	memcpy(uid->vendor, conf->ned->HDA_manufacturer,
74762306a36Sopenharmony_ci	       sizeof(uid->vendor) - 1);
74862306a36Sopenharmony_ci	EBCASC(uid->vendor, sizeof(uid->vendor) - 1);
74962306a36Sopenharmony_ci	memcpy(uid->serial, &conf->ned->serial,
75062306a36Sopenharmony_ci	       sizeof(uid->serial) - 1);
75162306a36Sopenharmony_ci	EBCASC(uid->serial, sizeof(uid->serial) - 1);
75262306a36Sopenharmony_ci	uid->ssid = conf->gneq->subsystemID;
75362306a36Sopenharmony_ci	uid->real_unit_addr = conf->ned->unit_addr;
75462306a36Sopenharmony_ci	if (conf->sneq) {
75562306a36Sopenharmony_ci		uid->type = conf->sneq->sua_flags;
75662306a36Sopenharmony_ci		if (uid->type == UA_BASE_PAV_ALIAS)
75762306a36Sopenharmony_ci			uid->base_unit_addr = conf->sneq->base_unit_addr;
75862306a36Sopenharmony_ci	} else {
75962306a36Sopenharmony_ci		uid->type = UA_BASE_DEVICE;
76062306a36Sopenharmony_ci	}
76162306a36Sopenharmony_ci	if (conf->vdsneq) {
76262306a36Sopenharmony_ci		for (count = 0; count < 16; count++) {
76362306a36Sopenharmony_ci			sprintf(uid->vduit+2*count, "%02x",
76462306a36Sopenharmony_ci				conf->vdsneq->uit[count]);
76562306a36Sopenharmony_ci		}
76662306a36Sopenharmony_ci	}
76762306a36Sopenharmony_ci}
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci/*
77062306a36Sopenharmony_ci * Generate device unique id that specifies the physical device.
77162306a36Sopenharmony_ci */
77262306a36Sopenharmony_cistatic int dasd_eckd_generate_uid(struct dasd_device *device)
77362306a36Sopenharmony_ci{
77462306a36Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
77562306a36Sopenharmony_ci	unsigned long flags;
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	if (!private)
77862306a36Sopenharmony_ci		return -ENODEV;
77962306a36Sopenharmony_ci	if (!private->conf.ned || !private->conf.gneq)
78062306a36Sopenharmony_ci		return -ENODEV;
78162306a36Sopenharmony_ci	spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
78262306a36Sopenharmony_ci	create_uid(&private->conf, &private->uid);
78362306a36Sopenharmony_ci	spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
78462306a36Sopenharmony_ci	return 0;
78562306a36Sopenharmony_ci}
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_cistatic int dasd_eckd_get_uid(struct dasd_device *device, struct dasd_uid *uid)
78862306a36Sopenharmony_ci{
78962306a36Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
79062306a36Sopenharmony_ci	unsigned long flags;
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci	if (private) {
79362306a36Sopenharmony_ci		spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
79462306a36Sopenharmony_ci		*uid = private->uid;
79562306a36Sopenharmony_ci		spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
79662306a36Sopenharmony_ci		return 0;
79762306a36Sopenharmony_ci	}
79862306a36Sopenharmony_ci	return -EINVAL;
79962306a36Sopenharmony_ci}
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci/*
80262306a36Sopenharmony_ci * compare device UID with data of a given dasd_eckd_private structure
80362306a36Sopenharmony_ci * return 0 for match
80462306a36Sopenharmony_ci */
80562306a36Sopenharmony_cistatic int dasd_eckd_compare_path_uid(struct dasd_device *device,
80662306a36Sopenharmony_ci				      struct dasd_conf *path_conf)
80762306a36Sopenharmony_ci{
80862306a36Sopenharmony_ci	struct dasd_uid device_uid;
80962306a36Sopenharmony_ci	struct dasd_uid path_uid;
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	create_uid(path_conf, &path_uid);
81262306a36Sopenharmony_ci	dasd_eckd_get_uid(device, &device_uid);
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci	return memcmp(&device_uid, &path_uid, sizeof(struct dasd_uid));
81562306a36Sopenharmony_ci}
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_cistatic void dasd_eckd_fill_rcd_cqr(struct dasd_device *device,
81862306a36Sopenharmony_ci				   struct dasd_ccw_req *cqr,
81962306a36Sopenharmony_ci				   __u8 *rcd_buffer,
82062306a36Sopenharmony_ci				   __u8 lpm)
82162306a36Sopenharmony_ci{
82262306a36Sopenharmony_ci	struct ccw1 *ccw;
82362306a36Sopenharmony_ci	/*
82462306a36Sopenharmony_ci	 * buffer has to start with EBCDIC "V1.0" to show
82562306a36Sopenharmony_ci	 * support for virtual device SNEQ
82662306a36Sopenharmony_ci	 */
82762306a36Sopenharmony_ci	rcd_buffer[0] = 0xE5;
82862306a36Sopenharmony_ci	rcd_buffer[1] = 0xF1;
82962306a36Sopenharmony_ci	rcd_buffer[2] = 0x4B;
83062306a36Sopenharmony_ci	rcd_buffer[3] = 0xF0;
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	ccw = cqr->cpaddr;
83362306a36Sopenharmony_ci	ccw->cmd_code = DASD_ECKD_CCW_RCD;
83462306a36Sopenharmony_ci	ccw->flags = 0;
83562306a36Sopenharmony_ci	ccw->cda = (__u32)virt_to_phys(rcd_buffer);
83662306a36Sopenharmony_ci	ccw->count = DASD_ECKD_RCD_DATA_SIZE;
83762306a36Sopenharmony_ci	cqr->magic = DASD_ECKD_MAGIC;
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	cqr->startdev = device;
84062306a36Sopenharmony_ci	cqr->memdev = device;
84162306a36Sopenharmony_ci	cqr->block = NULL;
84262306a36Sopenharmony_ci	cqr->expires = 10*HZ;
84362306a36Sopenharmony_ci	cqr->lpm = lpm;
84462306a36Sopenharmony_ci	cqr->retries = 256;
84562306a36Sopenharmony_ci	cqr->buildclk = get_tod_clock();
84662306a36Sopenharmony_ci	cqr->status = DASD_CQR_FILLED;
84762306a36Sopenharmony_ci	set_bit(DASD_CQR_VERIFY_PATH, &cqr->flags);
84862306a36Sopenharmony_ci}
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci/*
85162306a36Sopenharmony_ci * Wakeup helper for read_conf
85262306a36Sopenharmony_ci * if the cqr is not done and needs some error recovery
85362306a36Sopenharmony_ci * the buffer has to be re-initialized with the EBCDIC "V1.0"
85462306a36Sopenharmony_ci * to show support for virtual device SNEQ
85562306a36Sopenharmony_ci */
85662306a36Sopenharmony_cistatic void read_conf_cb(struct dasd_ccw_req *cqr, void *data)
85762306a36Sopenharmony_ci{
85862306a36Sopenharmony_ci	struct ccw1 *ccw;
85962306a36Sopenharmony_ci	__u8 *rcd_buffer;
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci	if (cqr->status !=  DASD_CQR_DONE) {
86262306a36Sopenharmony_ci		ccw = cqr->cpaddr;
86362306a36Sopenharmony_ci		rcd_buffer = phys_to_virt(ccw->cda);
86462306a36Sopenharmony_ci		memset(rcd_buffer, 0, sizeof(*rcd_buffer));
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci		rcd_buffer[0] = 0xE5;
86762306a36Sopenharmony_ci		rcd_buffer[1] = 0xF1;
86862306a36Sopenharmony_ci		rcd_buffer[2] = 0x4B;
86962306a36Sopenharmony_ci		rcd_buffer[3] = 0xF0;
87062306a36Sopenharmony_ci	}
87162306a36Sopenharmony_ci	dasd_wakeup_cb(cqr, data);
87262306a36Sopenharmony_ci}
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_cistatic int dasd_eckd_read_conf_immediately(struct dasd_device *device,
87562306a36Sopenharmony_ci					   struct dasd_ccw_req *cqr,
87662306a36Sopenharmony_ci					   __u8 *rcd_buffer,
87762306a36Sopenharmony_ci					   __u8 lpm)
87862306a36Sopenharmony_ci{
87962306a36Sopenharmony_ci	struct ciw *ciw;
88062306a36Sopenharmony_ci	int rc;
88162306a36Sopenharmony_ci	/*
88262306a36Sopenharmony_ci	 * sanity check: scan for RCD command in extended SenseID data
88362306a36Sopenharmony_ci	 * some devices do not support RCD
88462306a36Sopenharmony_ci	 */
88562306a36Sopenharmony_ci	ciw = ccw_device_get_ciw(device->cdev, CIW_TYPE_RCD);
88662306a36Sopenharmony_ci	if (!ciw || ciw->cmd != DASD_ECKD_CCW_RCD)
88762306a36Sopenharmony_ci		return -EOPNOTSUPP;
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci	dasd_eckd_fill_rcd_cqr(device, cqr, rcd_buffer, lpm);
89062306a36Sopenharmony_ci	clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
89162306a36Sopenharmony_ci	set_bit(DASD_CQR_ALLOW_SLOCK, &cqr->flags);
89262306a36Sopenharmony_ci	cqr->retries = 5;
89362306a36Sopenharmony_ci	cqr->callback = read_conf_cb;
89462306a36Sopenharmony_ci	rc = dasd_sleep_on_immediatly(cqr);
89562306a36Sopenharmony_ci	return rc;
89662306a36Sopenharmony_ci}
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_cistatic int dasd_eckd_read_conf_lpm(struct dasd_device *device,
89962306a36Sopenharmony_ci				   void **rcd_buffer,
90062306a36Sopenharmony_ci				   int *rcd_buffer_size, __u8 lpm)
90162306a36Sopenharmony_ci{
90262306a36Sopenharmony_ci	struct ciw *ciw;
90362306a36Sopenharmony_ci	char *rcd_buf = NULL;
90462306a36Sopenharmony_ci	int ret;
90562306a36Sopenharmony_ci	struct dasd_ccw_req *cqr;
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	/*
90862306a36Sopenharmony_ci	 * sanity check: scan for RCD command in extended SenseID data
90962306a36Sopenharmony_ci	 * some devices do not support RCD
91062306a36Sopenharmony_ci	 */
91162306a36Sopenharmony_ci	ciw = ccw_device_get_ciw(device->cdev, CIW_TYPE_RCD);
91262306a36Sopenharmony_ci	if (!ciw || ciw->cmd != DASD_ECKD_CCW_RCD) {
91362306a36Sopenharmony_ci		ret = -EOPNOTSUPP;
91462306a36Sopenharmony_ci		goto out_error;
91562306a36Sopenharmony_ci	}
91662306a36Sopenharmony_ci	rcd_buf = kzalloc(DASD_ECKD_RCD_DATA_SIZE, GFP_KERNEL | GFP_DMA);
91762306a36Sopenharmony_ci	if (!rcd_buf) {
91862306a36Sopenharmony_ci		ret = -ENOMEM;
91962306a36Sopenharmony_ci		goto out_error;
92062306a36Sopenharmony_ci	}
92162306a36Sopenharmony_ci	cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* RCD */,
92262306a36Sopenharmony_ci				   0, /* use rcd_buf as data ara */
92362306a36Sopenharmony_ci				   device, NULL);
92462306a36Sopenharmony_ci	if (IS_ERR(cqr)) {
92562306a36Sopenharmony_ci		DBF_DEV_EVENT(DBF_WARNING, device, "%s",
92662306a36Sopenharmony_ci			      "Could not allocate RCD request");
92762306a36Sopenharmony_ci		ret = -ENOMEM;
92862306a36Sopenharmony_ci		goto out_error;
92962306a36Sopenharmony_ci	}
93062306a36Sopenharmony_ci	dasd_eckd_fill_rcd_cqr(device, cqr, rcd_buf, lpm);
93162306a36Sopenharmony_ci	cqr->callback = read_conf_cb;
93262306a36Sopenharmony_ci	ret = dasd_sleep_on(cqr);
93362306a36Sopenharmony_ci	/*
93462306a36Sopenharmony_ci	 * on success we update the user input parms
93562306a36Sopenharmony_ci	 */
93662306a36Sopenharmony_ci	dasd_sfree_request(cqr, cqr->memdev);
93762306a36Sopenharmony_ci	if (ret)
93862306a36Sopenharmony_ci		goto out_error;
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	*rcd_buffer_size = DASD_ECKD_RCD_DATA_SIZE;
94162306a36Sopenharmony_ci	*rcd_buffer = rcd_buf;
94262306a36Sopenharmony_ci	return 0;
94362306a36Sopenharmony_ciout_error:
94462306a36Sopenharmony_ci	kfree(rcd_buf);
94562306a36Sopenharmony_ci	*rcd_buffer = NULL;
94662306a36Sopenharmony_ci	*rcd_buffer_size = 0;
94762306a36Sopenharmony_ci	return ret;
94862306a36Sopenharmony_ci}
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_cistatic int dasd_eckd_identify_conf_parts(struct dasd_conf *conf)
95162306a36Sopenharmony_ci{
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci	struct dasd_sneq *sneq;
95462306a36Sopenharmony_ci	int i, count;
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci	conf->ned = NULL;
95762306a36Sopenharmony_ci	conf->sneq = NULL;
95862306a36Sopenharmony_ci	conf->vdsneq = NULL;
95962306a36Sopenharmony_ci	conf->gneq = NULL;
96062306a36Sopenharmony_ci	count = conf->len / sizeof(struct dasd_sneq);
96162306a36Sopenharmony_ci	sneq = (struct dasd_sneq *)conf->data;
96262306a36Sopenharmony_ci	for (i = 0; i < count; ++i) {
96362306a36Sopenharmony_ci		if (sneq->flags.identifier == 1 && sneq->format == 1)
96462306a36Sopenharmony_ci			conf->sneq = sneq;
96562306a36Sopenharmony_ci		else if (sneq->flags.identifier == 1 && sneq->format == 4)
96662306a36Sopenharmony_ci			conf->vdsneq = (struct vd_sneq *)sneq;
96762306a36Sopenharmony_ci		else if (sneq->flags.identifier == 2)
96862306a36Sopenharmony_ci			conf->gneq = (struct dasd_gneq *)sneq;
96962306a36Sopenharmony_ci		else if (sneq->flags.identifier == 3 && sneq->res1 == 1)
97062306a36Sopenharmony_ci			conf->ned = (struct dasd_ned *)sneq;
97162306a36Sopenharmony_ci		sneq++;
97262306a36Sopenharmony_ci	}
97362306a36Sopenharmony_ci	if (!conf->ned || !conf->gneq) {
97462306a36Sopenharmony_ci		conf->ned = NULL;
97562306a36Sopenharmony_ci		conf->sneq = NULL;
97662306a36Sopenharmony_ci		conf->vdsneq = NULL;
97762306a36Sopenharmony_ci		conf->gneq = NULL;
97862306a36Sopenharmony_ci		return -EINVAL;
97962306a36Sopenharmony_ci	}
98062306a36Sopenharmony_ci	return 0;
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci};
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_cistatic unsigned char dasd_eckd_path_access(void *conf_data, int conf_len)
98562306a36Sopenharmony_ci{
98662306a36Sopenharmony_ci	struct dasd_gneq *gneq;
98762306a36Sopenharmony_ci	int i, count, found;
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci	count = conf_len / sizeof(*gneq);
99062306a36Sopenharmony_ci	gneq = (struct dasd_gneq *)conf_data;
99162306a36Sopenharmony_ci	found = 0;
99262306a36Sopenharmony_ci	for (i = 0; i < count; ++i) {
99362306a36Sopenharmony_ci		if (gneq->flags.identifier == 2) {
99462306a36Sopenharmony_ci			found = 1;
99562306a36Sopenharmony_ci			break;
99662306a36Sopenharmony_ci		}
99762306a36Sopenharmony_ci		gneq++;
99862306a36Sopenharmony_ci	}
99962306a36Sopenharmony_ci	if (found)
100062306a36Sopenharmony_ci		return ((char *)gneq)[18] & 0x07;
100162306a36Sopenharmony_ci	else
100262306a36Sopenharmony_ci		return 0;
100362306a36Sopenharmony_ci}
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_cistatic void dasd_eckd_store_conf_data(struct dasd_device *device,
100662306a36Sopenharmony_ci				      struct dasd_conf_data *conf_data, int chp)
100762306a36Sopenharmony_ci{
100862306a36Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
100962306a36Sopenharmony_ci	struct channel_path_desc_fmt0 *chp_desc;
101062306a36Sopenharmony_ci	struct subchannel_id sch_id;
101162306a36Sopenharmony_ci	void *cdp;
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci	/*
101462306a36Sopenharmony_ci	 * path handling and read_conf allocate data
101562306a36Sopenharmony_ci	 * free it before replacing the pointer
101662306a36Sopenharmony_ci	 * also replace the old private->conf_data pointer
101762306a36Sopenharmony_ci	 * with the new one if this points to the same data
101862306a36Sopenharmony_ci	 */
101962306a36Sopenharmony_ci	cdp = device->path[chp].conf_data;
102062306a36Sopenharmony_ci	if (private->conf.data == cdp) {
102162306a36Sopenharmony_ci		private->conf.data = (void *)conf_data;
102262306a36Sopenharmony_ci		dasd_eckd_identify_conf_parts(&private->conf);
102362306a36Sopenharmony_ci	}
102462306a36Sopenharmony_ci	ccw_device_get_schid(device->cdev, &sch_id);
102562306a36Sopenharmony_ci	device->path[chp].conf_data = conf_data;
102662306a36Sopenharmony_ci	device->path[chp].cssid = sch_id.cssid;
102762306a36Sopenharmony_ci	device->path[chp].ssid = sch_id.ssid;
102862306a36Sopenharmony_ci	chp_desc = ccw_device_get_chp_desc(device->cdev, chp);
102962306a36Sopenharmony_ci	if (chp_desc)
103062306a36Sopenharmony_ci		device->path[chp].chpid = chp_desc->chpid;
103162306a36Sopenharmony_ci	kfree(chp_desc);
103262306a36Sopenharmony_ci	kfree(cdp);
103362306a36Sopenharmony_ci}
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_cistatic void dasd_eckd_clear_conf_data(struct dasd_device *device)
103662306a36Sopenharmony_ci{
103762306a36Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
103862306a36Sopenharmony_ci	int i;
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_ci	private->conf.data = NULL;
104162306a36Sopenharmony_ci	private->conf.len = 0;
104262306a36Sopenharmony_ci	for (i = 0; i < 8; i++) {
104362306a36Sopenharmony_ci		kfree(device->path[i].conf_data);
104462306a36Sopenharmony_ci		device->path[i].conf_data = NULL;
104562306a36Sopenharmony_ci		device->path[i].cssid = 0;
104662306a36Sopenharmony_ci		device->path[i].ssid = 0;
104762306a36Sopenharmony_ci		device->path[i].chpid = 0;
104862306a36Sopenharmony_ci		dasd_path_notoper(device, i);
104962306a36Sopenharmony_ci	}
105062306a36Sopenharmony_ci}
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_cistatic void dasd_eckd_read_fc_security(struct dasd_device *device)
105362306a36Sopenharmony_ci{
105462306a36Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
105562306a36Sopenharmony_ci	u8 esm_valid;
105662306a36Sopenharmony_ci	u8 esm[8];
105762306a36Sopenharmony_ci	int chp;
105862306a36Sopenharmony_ci	int rc;
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci	rc = chsc_scud(private->uid.ssid, (u64 *)esm, &esm_valid);
106162306a36Sopenharmony_ci	if (rc) {
106262306a36Sopenharmony_ci		for (chp = 0; chp < 8; chp++)
106362306a36Sopenharmony_ci			device->path[chp].fc_security = 0;
106462306a36Sopenharmony_ci		return;
106562306a36Sopenharmony_ci	}
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ci	for (chp = 0; chp < 8; chp++) {
106862306a36Sopenharmony_ci		if (esm_valid & (0x80 >> chp))
106962306a36Sopenharmony_ci			device->path[chp].fc_security = esm[chp];
107062306a36Sopenharmony_ci		else
107162306a36Sopenharmony_ci			device->path[chp].fc_security = 0;
107262306a36Sopenharmony_ci	}
107362306a36Sopenharmony_ci}
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_cistatic void dasd_eckd_get_uid_string(struct dasd_conf *conf,
107662306a36Sopenharmony_ci				     char *print_uid)
107762306a36Sopenharmony_ci{
107862306a36Sopenharmony_ci	struct dasd_uid uid;
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci	create_uid(conf, &uid);
108162306a36Sopenharmony_ci	if (strlen(uid.vduit) > 0)
108262306a36Sopenharmony_ci		snprintf(print_uid, DASD_UID_STRLEN,
108362306a36Sopenharmony_ci			 "%s.%s.%04x.%02x.%s",
108462306a36Sopenharmony_ci			 uid.vendor, uid.serial, uid.ssid,
108562306a36Sopenharmony_ci			 uid.real_unit_addr, uid.vduit);
108662306a36Sopenharmony_ci	else
108762306a36Sopenharmony_ci		snprintf(print_uid, DASD_UID_STRLEN,
108862306a36Sopenharmony_ci			 "%s.%s.%04x.%02x",
108962306a36Sopenharmony_ci			 uid.vendor, uid.serial, uid.ssid,
109062306a36Sopenharmony_ci			 uid.real_unit_addr);
109162306a36Sopenharmony_ci}
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_cistatic int dasd_eckd_check_cabling(struct dasd_device *device,
109462306a36Sopenharmony_ci				   void *conf_data, __u8 lpm)
109562306a36Sopenharmony_ci{
109662306a36Sopenharmony_ci	char print_path_uid[DASD_UID_STRLEN], print_device_uid[DASD_UID_STRLEN];
109762306a36Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
109862306a36Sopenharmony_ci	struct dasd_conf path_conf;
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ci	path_conf.data = conf_data;
110162306a36Sopenharmony_ci	path_conf.len = DASD_ECKD_RCD_DATA_SIZE;
110262306a36Sopenharmony_ci	if (dasd_eckd_identify_conf_parts(&path_conf))
110362306a36Sopenharmony_ci		return 1;
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci	if (dasd_eckd_compare_path_uid(device, &path_conf)) {
110662306a36Sopenharmony_ci		dasd_eckd_get_uid_string(&path_conf, print_path_uid);
110762306a36Sopenharmony_ci		dasd_eckd_get_uid_string(&private->conf, print_device_uid);
110862306a36Sopenharmony_ci		dev_err(&device->cdev->dev,
110962306a36Sopenharmony_ci			"Not all channel paths lead to the same device, path %02X leads to device %s instead of %s\n",
111062306a36Sopenharmony_ci			lpm, print_path_uid, print_device_uid);
111162306a36Sopenharmony_ci		return 1;
111262306a36Sopenharmony_ci	}
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci	return 0;
111562306a36Sopenharmony_ci}
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_cistatic int dasd_eckd_read_conf(struct dasd_device *device)
111862306a36Sopenharmony_ci{
111962306a36Sopenharmony_ci	void *conf_data;
112062306a36Sopenharmony_ci	int conf_len, conf_data_saved;
112162306a36Sopenharmony_ci	int rc, path_err, pos;
112262306a36Sopenharmony_ci	__u8 lpm, opm;
112362306a36Sopenharmony_ci	struct dasd_eckd_private *private;
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ci	private = device->private;
112662306a36Sopenharmony_ci	opm = ccw_device_get_path_mask(device->cdev);
112762306a36Sopenharmony_ci	conf_data_saved = 0;
112862306a36Sopenharmony_ci	path_err = 0;
112962306a36Sopenharmony_ci	/* get configuration data per operational path */
113062306a36Sopenharmony_ci	for (lpm = 0x80; lpm; lpm>>= 1) {
113162306a36Sopenharmony_ci		if (!(lpm & opm))
113262306a36Sopenharmony_ci			continue;
113362306a36Sopenharmony_ci		rc = dasd_eckd_read_conf_lpm(device, &conf_data,
113462306a36Sopenharmony_ci					     &conf_len, lpm);
113562306a36Sopenharmony_ci		if (rc && rc != -EOPNOTSUPP) {	/* -EOPNOTSUPP is ok */
113662306a36Sopenharmony_ci			DBF_EVENT_DEVID(DBF_WARNING, device->cdev,
113762306a36Sopenharmony_ci					"Read configuration data returned "
113862306a36Sopenharmony_ci					"error %d", rc);
113962306a36Sopenharmony_ci			return rc;
114062306a36Sopenharmony_ci		}
114162306a36Sopenharmony_ci		if (conf_data == NULL) {
114262306a36Sopenharmony_ci			DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s",
114362306a36Sopenharmony_ci					"No configuration data "
114462306a36Sopenharmony_ci					"retrieved");
114562306a36Sopenharmony_ci			/* no further analysis possible */
114662306a36Sopenharmony_ci			dasd_path_add_opm(device, opm);
114762306a36Sopenharmony_ci			continue;	/* no error */
114862306a36Sopenharmony_ci		}
114962306a36Sopenharmony_ci		/* save first valid configuration data */
115062306a36Sopenharmony_ci		if (!conf_data_saved) {
115162306a36Sopenharmony_ci			/* initially clear previously stored conf_data */
115262306a36Sopenharmony_ci			dasd_eckd_clear_conf_data(device);
115362306a36Sopenharmony_ci			private->conf.data = conf_data;
115462306a36Sopenharmony_ci			private->conf.len = conf_len;
115562306a36Sopenharmony_ci			if (dasd_eckd_identify_conf_parts(&private->conf)) {
115662306a36Sopenharmony_ci				private->conf.data = NULL;
115762306a36Sopenharmony_ci				private->conf.len = 0;
115862306a36Sopenharmony_ci				kfree(conf_data);
115962306a36Sopenharmony_ci				continue;
116062306a36Sopenharmony_ci			}
116162306a36Sopenharmony_ci			/*
116262306a36Sopenharmony_ci			 * build device UID that other path data
116362306a36Sopenharmony_ci			 * can be compared to it
116462306a36Sopenharmony_ci			 */
116562306a36Sopenharmony_ci			dasd_eckd_generate_uid(device);
116662306a36Sopenharmony_ci			conf_data_saved++;
116762306a36Sopenharmony_ci		} else if (dasd_eckd_check_cabling(device, conf_data, lpm)) {
116862306a36Sopenharmony_ci			dasd_path_add_cablepm(device, lpm);
116962306a36Sopenharmony_ci			path_err = -EINVAL;
117062306a36Sopenharmony_ci			kfree(conf_data);
117162306a36Sopenharmony_ci			continue;
117262306a36Sopenharmony_ci		}
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci		pos = pathmask_to_pos(lpm);
117562306a36Sopenharmony_ci		dasd_eckd_store_conf_data(device, conf_data, pos);
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci		switch (dasd_eckd_path_access(conf_data, conf_len)) {
117862306a36Sopenharmony_ci		case 0x02:
117962306a36Sopenharmony_ci			dasd_path_add_nppm(device, lpm);
118062306a36Sopenharmony_ci			break;
118162306a36Sopenharmony_ci		case 0x03:
118262306a36Sopenharmony_ci			dasd_path_add_ppm(device, lpm);
118362306a36Sopenharmony_ci			break;
118462306a36Sopenharmony_ci		}
118562306a36Sopenharmony_ci		if (!dasd_path_get_opm(device)) {
118662306a36Sopenharmony_ci			dasd_path_set_opm(device, lpm);
118762306a36Sopenharmony_ci			dasd_generic_path_operational(device);
118862306a36Sopenharmony_ci		} else {
118962306a36Sopenharmony_ci			dasd_path_add_opm(device, lpm);
119062306a36Sopenharmony_ci		}
119162306a36Sopenharmony_ci	}
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci	return path_err;
119462306a36Sopenharmony_ci}
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_cistatic u32 get_fcx_max_data(struct dasd_device *device)
119762306a36Sopenharmony_ci{
119862306a36Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
119962306a36Sopenharmony_ci	int fcx_in_css, fcx_in_gneq, fcx_in_features;
120062306a36Sopenharmony_ci	unsigned int mdc;
120162306a36Sopenharmony_ci	int tpm;
120262306a36Sopenharmony_ci
120362306a36Sopenharmony_ci	if (dasd_nofcx)
120462306a36Sopenharmony_ci		return 0;
120562306a36Sopenharmony_ci	/* is transport mode supported? */
120662306a36Sopenharmony_ci	fcx_in_css = css_general_characteristics.fcx;
120762306a36Sopenharmony_ci	fcx_in_gneq = private->conf.gneq->reserved2[7] & 0x04;
120862306a36Sopenharmony_ci	fcx_in_features = private->features.feature[40] & 0x80;
120962306a36Sopenharmony_ci	tpm = fcx_in_css && fcx_in_gneq && fcx_in_features;
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci	if (!tpm)
121262306a36Sopenharmony_ci		return 0;
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_ci	mdc = ccw_device_get_mdc(device->cdev, 0);
121562306a36Sopenharmony_ci	if (mdc == 0) {
121662306a36Sopenharmony_ci		dev_warn(&device->cdev->dev, "Detecting the maximum supported data size for zHPF requests failed\n");
121762306a36Sopenharmony_ci		return 0;
121862306a36Sopenharmony_ci	} else {
121962306a36Sopenharmony_ci		return (u32)mdc * FCX_MAX_DATA_FACTOR;
122062306a36Sopenharmony_ci	}
122162306a36Sopenharmony_ci}
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_cistatic int verify_fcx_max_data(struct dasd_device *device, __u8 lpm)
122462306a36Sopenharmony_ci{
122562306a36Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
122662306a36Sopenharmony_ci	unsigned int mdc;
122762306a36Sopenharmony_ci	u32 fcx_max_data;
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_ci	if (private->fcx_max_data) {
123062306a36Sopenharmony_ci		mdc = ccw_device_get_mdc(device->cdev, lpm);
123162306a36Sopenharmony_ci		if (mdc == 0) {
123262306a36Sopenharmony_ci			dev_warn(&device->cdev->dev,
123362306a36Sopenharmony_ci				 "Detecting the maximum data size for zHPF "
123462306a36Sopenharmony_ci				 "requests failed (rc=%d) for a new path %x\n",
123562306a36Sopenharmony_ci				 mdc, lpm);
123662306a36Sopenharmony_ci			return mdc;
123762306a36Sopenharmony_ci		}
123862306a36Sopenharmony_ci		fcx_max_data = (u32)mdc * FCX_MAX_DATA_FACTOR;
123962306a36Sopenharmony_ci		if (fcx_max_data < private->fcx_max_data) {
124062306a36Sopenharmony_ci			dev_warn(&device->cdev->dev,
124162306a36Sopenharmony_ci				 "The maximum data size for zHPF requests %u "
124262306a36Sopenharmony_ci				 "on a new path %x is below the active maximum "
124362306a36Sopenharmony_ci				 "%u\n", fcx_max_data, lpm,
124462306a36Sopenharmony_ci				 private->fcx_max_data);
124562306a36Sopenharmony_ci			return -EACCES;
124662306a36Sopenharmony_ci		}
124762306a36Sopenharmony_ci	}
124862306a36Sopenharmony_ci	return 0;
124962306a36Sopenharmony_ci}
125062306a36Sopenharmony_ci
125162306a36Sopenharmony_cistatic int rebuild_device_uid(struct dasd_device *device,
125262306a36Sopenharmony_ci			      struct pe_handler_work_data *data)
125362306a36Sopenharmony_ci{
125462306a36Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
125562306a36Sopenharmony_ci	__u8 lpm, opm = dasd_path_get_opm(device);
125662306a36Sopenharmony_ci	int rc = -ENODEV;
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_ci	for (lpm = 0x80; lpm; lpm >>= 1) {
125962306a36Sopenharmony_ci		if (!(lpm & opm))
126062306a36Sopenharmony_ci			continue;
126162306a36Sopenharmony_ci		memset(&data->rcd_buffer, 0, sizeof(data->rcd_buffer));
126262306a36Sopenharmony_ci		memset(&data->cqr, 0, sizeof(data->cqr));
126362306a36Sopenharmony_ci		data->cqr.cpaddr = &data->ccw;
126462306a36Sopenharmony_ci		rc = dasd_eckd_read_conf_immediately(device, &data->cqr,
126562306a36Sopenharmony_ci						     data->rcd_buffer,
126662306a36Sopenharmony_ci						     lpm);
126762306a36Sopenharmony_ci
126862306a36Sopenharmony_ci		if (rc) {
126962306a36Sopenharmony_ci			if (rc == -EOPNOTSUPP) /* -EOPNOTSUPP is ok */
127062306a36Sopenharmony_ci				continue;
127162306a36Sopenharmony_ci			DBF_EVENT_DEVID(DBF_WARNING, device->cdev,
127262306a36Sopenharmony_ci					"Read configuration data "
127362306a36Sopenharmony_ci					"returned error %d", rc);
127462306a36Sopenharmony_ci			break;
127562306a36Sopenharmony_ci		}
127662306a36Sopenharmony_ci		memcpy(private->conf.data, data->rcd_buffer,
127762306a36Sopenharmony_ci		       DASD_ECKD_RCD_DATA_SIZE);
127862306a36Sopenharmony_ci		if (dasd_eckd_identify_conf_parts(&private->conf)) {
127962306a36Sopenharmony_ci			rc = -ENODEV;
128062306a36Sopenharmony_ci		} else /* first valid path is enough */
128162306a36Sopenharmony_ci			break;
128262306a36Sopenharmony_ci	}
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci	if (!rc)
128562306a36Sopenharmony_ci		rc = dasd_eckd_generate_uid(device);
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_ci	return rc;
128862306a36Sopenharmony_ci}
128962306a36Sopenharmony_ci
129062306a36Sopenharmony_cistatic void dasd_eckd_path_available_action(struct dasd_device *device,
129162306a36Sopenharmony_ci					    struct pe_handler_work_data *data)
129262306a36Sopenharmony_ci{
129362306a36Sopenharmony_ci	__u8 path_rcd_buf[DASD_ECKD_RCD_DATA_SIZE];
129462306a36Sopenharmony_ci	__u8 lpm, opm, npm, ppm, epm, hpfpm, cablepm;
129562306a36Sopenharmony_ci	struct dasd_conf_data *conf_data;
129662306a36Sopenharmony_ci	char print_uid[DASD_UID_STRLEN];
129762306a36Sopenharmony_ci	struct dasd_conf path_conf;
129862306a36Sopenharmony_ci	unsigned long flags;
129962306a36Sopenharmony_ci	int rc, pos;
130062306a36Sopenharmony_ci
130162306a36Sopenharmony_ci	opm = 0;
130262306a36Sopenharmony_ci	npm = 0;
130362306a36Sopenharmony_ci	ppm = 0;
130462306a36Sopenharmony_ci	epm = 0;
130562306a36Sopenharmony_ci	hpfpm = 0;
130662306a36Sopenharmony_ci	cablepm = 0;
130762306a36Sopenharmony_ci
130862306a36Sopenharmony_ci	for (lpm = 0x80; lpm; lpm >>= 1) {
130962306a36Sopenharmony_ci		if (!(lpm & data->tbvpm))
131062306a36Sopenharmony_ci			continue;
131162306a36Sopenharmony_ci		memset(&data->rcd_buffer, 0, sizeof(data->rcd_buffer));
131262306a36Sopenharmony_ci		memset(&data->cqr, 0, sizeof(data->cqr));
131362306a36Sopenharmony_ci		data->cqr.cpaddr = &data->ccw;
131462306a36Sopenharmony_ci		rc = dasd_eckd_read_conf_immediately(device, &data->cqr,
131562306a36Sopenharmony_ci						     data->rcd_buffer,
131662306a36Sopenharmony_ci						     lpm);
131762306a36Sopenharmony_ci		if (!rc) {
131862306a36Sopenharmony_ci			switch (dasd_eckd_path_access(data->rcd_buffer,
131962306a36Sopenharmony_ci						      DASD_ECKD_RCD_DATA_SIZE)
132062306a36Sopenharmony_ci				) {
132162306a36Sopenharmony_ci			case 0x02:
132262306a36Sopenharmony_ci				npm |= lpm;
132362306a36Sopenharmony_ci				break;
132462306a36Sopenharmony_ci			case 0x03:
132562306a36Sopenharmony_ci				ppm |= lpm;
132662306a36Sopenharmony_ci				break;
132762306a36Sopenharmony_ci			}
132862306a36Sopenharmony_ci			opm |= lpm;
132962306a36Sopenharmony_ci		} else if (rc == -EOPNOTSUPP) {
133062306a36Sopenharmony_ci			DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s",
133162306a36Sopenharmony_ci					"path verification: No configuration "
133262306a36Sopenharmony_ci					"data retrieved");
133362306a36Sopenharmony_ci			opm |= lpm;
133462306a36Sopenharmony_ci		} else if (rc == -EAGAIN) {
133562306a36Sopenharmony_ci			DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s",
133662306a36Sopenharmony_ci					"path verification: device is stopped,"
133762306a36Sopenharmony_ci					" try again later");
133862306a36Sopenharmony_ci			epm |= lpm;
133962306a36Sopenharmony_ci		} else {
134062306a36Sopenharmony_ci			dev_warn(&device->cdev->dev,
134162306a36Sopenharmony_ci				 "Reading device feature codes failed "
134262306a36Sopenharmony_ci				 "(rc=%d) for new path %x\n", rc, lpm);
134362306a36Sopenharmony_ci			continue;
134462306a36Sopenharmony_ci		}
134562306a36Sopenharmony_ci		if (verify_fcx_max_data(device, lpm)) {
134662306a36Sopenharmony_ci			opm &= ~lpm;
134762306a36Sopenharmony_ci			npm &= ~lpm;
134862306a36Sopenharmony_ci			ppm &= ~lpm;
134962306a36Sopenharmony_ci			hpfpm |= lpm;
135062306a36Sopenharmony_ci			continue;
135162306a36Sopenharmony_ci		}
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_ci		/*
135462306a36Sopenharmony_ci		 * save conf_data for comparison after
135562306a36Sopenharmony_ci		 * rebuild_device_uid may have changed
135662306a36Sopenharmony_ci		 * the original data
135762306a36Sopenharmony_ci		 */
135862306a36Sopenharmony_ci		memcpy(&path_rcd_buf, data->rcd_buffer,
135962306a36Sopenharmony_ci		       DASD_ECKD_RCD_DATA_SIZE);
136062306a36Sopenharmony_ci		path_conf.data = (void *)&path_rcd_buf;
136162306a36Sopenharmony_ci		path_conf.len = DASD_ECKD_RCD_DATA_SIZE;
136262306a36Sopenharmony_ci		if (dasd_eckd_identify_conf_parts(&path_conf)) {
136362306a36Sopenharmony_ci			path_conf.data = NULL;
136462306a36Sopenharmony_ci			path_conf.len = 0;
136562306a36Sopenharmony_ci			continue;
136662306a36Sopenharmony_ci		}
136762306a36Sopenharmony_ci
136862306a36Sopenharmony_ci		/*
136962306a36Sopenharmony_ci		 * compare path UID with device UID only if at least
137062306a36Sopenharmony_ci		 * one valid path is left
137162306a36Sopenharmony_ci		 * in other case the device UID may have changed and
137262306a36Sopenharmony_ci		 * the first working path UID will be used as device UID
137362306a36Sopenharmony_ci		 */
137462306a36Sopenharmony_ci		if (dasd_path_get_opm(device) &&
137562306a36Sopenharmony_ci		    dasd_eckd_compare_path_uid(device, &path_conf)) {
137662306a36Sopenharmony_ci			/*
137762306a36Sopenharmony_ci			 * the comparison was not successful
137862306a36Sopenharmony_ci			 * rebuild the device UID with at least one
137962306a36Sopenharmony_ci			 * known path in case a z/VM hyperswap command
138062306a36Sopenharmony_ci			 * has changed the device
138162306a36Sopenharmony_ci			 *
138262306a36Sopenharmony_ci			 * after this compare again
138362306a36Sopenharmony_ci			 *
138462306a36Sopenharmony_ci			 * if either the rebuild or the recompare fails
138562306a36Sopenharmony_ci			 * the path can not be used
138662306a36Sopenharmony_ci			 */
138762306a36Sopenharmony_ci			if (rebuild_device_uid(device, data) ||
138862306a36Sopenharmony_ci			    dasd_eckd_compare_path_uid(
138962306a36Sopenharmony_ci				    device, &path_conf)) {
139062306a36Sopenharmony_ci				dasd_eckd_get_uid_string(&path_conf, print_uid);
139162306a36Sopenharmony_ci				dev_err(&device->cdev->dev,
139262306a36Sopenharmony_ci					"The newly added channel path %02X "
139362306a36Sopenharmony_ci					"will not be used because it leads "
139462306a36Sopenharmony_ci					"to a different device %s\n",
139562306a36Sopenharmony_ci					lpm, print_uid);
139662306a36Sopenharmony_ci				opm &= ~lpm;
139762306a36Sopenharmony_ci				npm &= ~lpm;
139862306a36Sopenharmony_ci				ppm &= ~lpm;
139962306a36Sopenharmony_ci				cablepm |= lpm;
140062306a36Sopenharmony_ci				continue;
140162306a36Sopenharmony_ci			}
140262306a36Sopenharmony_ci		}
140362306a36Sopenharmony_ci
140462306a36Sopenharmony_ci		conf_data = kzalloc(DASD_ECKD_RCD_DATA_SIZE, GFP_KERNEL);
140562306a36Sopenharmony_ci		if (conf_data) {
140662306a36Sopenharmony_ci			memcpy(conf_data, data->rcd_buffer,
140762306a36Sopenharmony_ci			       DASD_ECKD_RCD_DATA_SIZE);
140862306a36Sopenharmony_ci		} else {
140962306a36Sopenharmony_ci			/*
141062306a36Sopenharmony_ci			 * path is operational but path config data could not
141162306a36Sopenharmony_ci			 * be stored due to low mem condition
141262306a36Sopenharmony_ci			 * add it to the error path mask and schedule a path
141362306a36Sopenharmony_ci			 * verification later that this could be added again
141462306a36Sopenharmony_ci			 */
141562306a36Sopenharmony_ci			epm |= lpm;
141662306a36Sopenharmony_ci		}
141762306a36Sopenharmony_ci		pos = pathmask_to_pos(lpm);
141862306a36Sopenharmony_ci		dasd_eckd_store_conf_data(device, conf_data, pos);
141962306a36Sopenharmony_ci
142062306a36Sopenharmony_ci		/*
142162306a36Sopenharmony_ci		 * There is a small chance that a path is lost again between
142262306a36Sopenharmony_ci		 * above path verification and the following modification of
142362306a36Sopenharmony_ci		 * the device opm mask. We could avoid that race here by using
142462306a36Sopenharmony_ci		 * yet another path mask, but we rather deal with this unlikely
142562306a36Sopenharmony_ci		 * situation in dasd_start_IO.
142662306a36Sopenharmony_ci		 */
142762306a36Sopenharmony_ci		spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
142862306a36Sopenharmony_ci		if (!dasd_path_get_opm(device) && opm) {
142962306a36Sopenharmony_ci			dasd_path_set_opm(device, opm);
143062306a36Sopenharmony_ci			dasd_generic_path_operational(device);
143162306a36Sopenharmony_ci		} else {
143262306a36Sopenharmony_ci			dasd_path_add_opm(device, opm);
143362306a36Sopenharmony_ci		}
143462306a36Sopenharmony_ci		dasd_path_add_nppm(device, npm);
143562306a36Sopenharmony_ci		dasd_path_add_ppm(device, ppm);
143662306a36Sopenharmony_ci		if (epm) {
143762306a36Sopenharmony_ci			dasd_path_add_tbvpm(device, epm);
143862306a36Sopenharmony_ci			dasd_device_set_timer(device, 50);
143962306a36Sopenharmony_ci		}
144062306a36Sopenharmony_ci		dasd_path_add_cablepm(device, cablepm);
144162306a36Sopenharmony_ci		dasd_path_add_nohpfpm(device, hpfpm);
144262306a36Sopenharmony_ci		spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
144362306a36Sopenharmony_ci
144462306a36Sopenharmony_ci		dasd_path_create_kobj(device, pos);
144562306a36Sopenharmony_ci	}
144662306a36Sopenharmony_ci}
144762306a36Sopenharmony_ci
144862306a36Sopenharmony_cistatic void do_pe_handler_work(struct work_struct *work)
144962306a36Sopenharmony_ci{
145062306a36Sopenharmony_ci	struct pe_handler_work_data *data;
145162306a36Sopenharmony_ci	struct dasd_device *device;
145262306a36Sopenharmony_ci
145362306a36Sopenharmony_ci	data = container_of(work, struct pe_handler_work_data, worker);
145462306a36Sopenharmony_ci	device = data->device;
145562306a36Sopenharmony_ci
145662306a36Sopenharmony_ci	/* delay path verification until device was resumed */
145762306a36Sopenharmony_ci	if (test_bit(DASD_FLAG_SUSPENDED, &device->flags)) {
145862306a36Sopenharmony_ci		schedule_work(work);
145962306a36Sopenharmony_ci		return;
146062306a36Sopenharmony_ci	}
146162306a36Sopenharmony_ci	/* check if path verification already running and delay if so */
146262306a36Sopenharmony_ci	if (test_and_set_bit(DASD_FLAG_PATH_VERIFY, &device->flags)) {
146362306a36Sopenharmony_ci		schedule_work(work);
146462306a36Sopenharmony_ci		return;
146562306a36Sopenharmony_ci	}
146662306a36Sopenharmony_ci
146762306a36Sopenharmony_ci	if (data->tbvpm)
146862306a36Sopenharmony_ci		dasd_eckd_path_available_action(device, data);
146962306a36Sopenharmony_ci	if (data->fcsecpm)
147062306a36Sopenharmony_ci		dasd_eckd_read_fc_security(device);
147162306a36Sopenharmony_ci
147262306a36Sopenharmony_ci	clear_bit(DASD_FLAG_PATH_VERIFY, &device->flags);
147362306a36Sopenharmony_ci	dasd_put_device(device);
147462306a36Sopenharmony_ci	if (data->isglobal)
147562306a36Sopenharmony_ci		mutex_unlock(&dasd_pe_handler_mutex);
147662306a36Sopenharmony_ci	else
147762306a36Sopenharmony_ci		kfree(data);
147862306a36Sopenharmony_ci}
147962306a36Sopenharmony_ci
148062306a36Sopenharmony_cistatic int dasd_eckd_pe_handler(struct dasd_device *device,
148162306a36Sopenharmony_ci				__u8 tbvpm, __u8 fcsecpm)
148262306a36Sopenharmony_ci{
148362306a36Sopenharmony_ci	struct pe_handler_work_data *data;
148462306a36Sopenharmony_ci
148562306a36Sopenharmony_ci	data = kzalloc(sizeof(*data), GFP_ATOMIC | GFP_DMA);
148662306a36Sopenharmony_ci	if (!data) {
148762306a36Sopenharmony_ci		if (mutex_trylock(&dasd_pe_handler_mutex)) {
148862306a36Sopenharmony_ci			data = pe_handler_worker;
148962306a36Sopenharmony_ci			data->isglobal = 1;
149062306a36Sopenharmony_ci		} else {
149162306a36Sopenharmony_ci			return -ENOMEM;
149262306a36Sopenharmony_ci		}
149362306a36Sopenharmony_ci	}
149462306a36Sopenharmony_ci	INIT_WORK(&data->worker, do_pe_handler_work);
149562306a36Sopenharmony_ci	dasd_get_device(device);
149662306a36Sopenharmony_ci	data->device = device;
149762306a36Sopenharmony_ci	data->tbvpm = tbvpm;
149862306a36Sopenharmony_ci	data->fcsecpm = fcsecpm;
149962306a36Sopenharmony_ci	schedule_work(&data->worker);
150062306a36Sopenharmony_ci	return 0;
150162306a36Sopenharmony_ci}
150262306a36Sopenharmony_ci
150362306a36Sopenharmony_cistatic void dasd_eckd_reset_path(struct dasd_device *device, __u8 pm)
150462306a36Sopenharmony_ci{
150562306a36Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
150662306a36Sopenharmony_ci	unsigned long flags;
150762306a36Sopenharmony_ci
150862306a36Sopenharmony_ci	if (!private->fcx_max_data)
150962306a36Sopenharmony_ci		private->fcx_max_data = get_fcx_max_data(device);
151062306a36Sopenharmony_ci	spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
151162306a36Sopenharmony_ci	dasd_path_set_tbvpm(device, pm ? : dasd_path_get_notoperpm(device));
151262306a36Sopenharmony_ci	dasd_schedule_device_bh(device);
151362306a36Sopenharmony_ci	spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
151462306a36Sopenharmony_ci}
151562306a36Sopenharmony_ci
151662306a36Sopenharmony_cistatic int dasd_eckd_read_features(struct dasd_device *device)
151762306a36Sopenharmony_ci{
151862306a36Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
151962306a36Sopenharmony_ci	struct dasd_psf_prssd_data *prssdp;
152062306a36Sopenharmony_ci	struct dasd_rssd_features *features;
152162306a36Sopenharmony_ci	struct dasd_ccw_req *cqr;
152262306a36Sopenharmony_ci	struct ccw1 *ccw;
152362306a36Sopenharmony_ci	int rc;
152462306a36Sopenharmony_ci
152562306a36Sopenharmony_ci	memset(&private->features, 0, sizeof(struct dasd_rssd_features));
152662306a36Sopenharmony_ci	cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */	+ 1 /* RSSD */,
152762306a36Sopenharmony_ci				   (sizeof(struct dasd_psf_prssd_data) +
152862306a36Sopenharmony_ci				    sizeof(struct dasd_rssd_features)),
152962306a36Sopenharmony_ci				   device, NULL);
153062306a36Sopenharmony_ci	if (IS_ERR(cqr)) {
153162306a36Sopenharmony_ci		DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", "Could not "
153262306a36Sopenharmony_ci				"allocate initialization request");
153362306a36Sopenharmony_ci		return PTR_ERR(cqr);
153462306a36Sopenharmony_ci	}
153562306a36Sopenharmony_ci	cqr->startdev = device;
153662306a36Sopenharmony_ci	cqr->memdev = device;
153762306a36Sopenharmony_ci	cqr->block = NULL;
153862306a36Sopenharmony_ci	cqr->retries = 256;
153962306a36Sopenharmony_ci	cqr->expires = 10 * HZ;
154062306a36Sopenharmony_ci
154162306a36Sopenharmony_ci	/* Prepare for Read Subsystem Data */
154262306a36Sopenharmony_ci	prssdp = (struct dasd_psf_prssd_data *) cqr->data;
154362306a36Sopenharmony_ci	memset(prssdp, 0, sizeof(struct dasd_psf_prssd_data));
154462306a36Sopenharmony_ci	prssdp->order = PSF_ORDER_PRSSD;
154562306a36Sopenharmony_ci	prssdp->suborder = 0x41;	/* Read Feature Codes */
154662306a36Sopenharmony_ci	/* all other bytes of prssdp must be zero */
154762306a36Sopenharmony_ci
154862306a36Sopenharmony_ci	ccw = cqr->cpaddr;
154962306a36Sopenharmony_ci	ccw->cmd_code = DASD_ECKD_CCW_PSF;
155062306a36Sopenharmony_ci	ccw->count = sizeof(struct dasd_psf_prssd_data);
155162306a36Sopenharmony_ci	ccw->flags |= CCW_FLAG_CC;
155262306a36Sopenharmony_ci	ccw->cda = (__u32)virt_to_phys(prssdp);
155362306a36Sopenharmony_ci
155462306a36Sopenharmony_ci	/* Read Subsystem Data - feature codes */
155562306a36Sopenharmony_ci	features = (struct dasd_rssd_features *) (prssdp + 1);
155662306a36Sopenharmony_ci	memset(features, 0, sizeof(struct dasd_rssd_features));
155762306a36Sopenharmony_ci
155862306a36Sopenharmony_ci	ccw++;
155962306a36Sopenharmony_ci	ccw->cmd_code = DASD_ECKD_CCW_RSSD;
156062306a36Sopenharmony_ci	ccw->count = sizeof(struct dasd_rssd_features);
156162306a36Sopenharmony_ci	ccw->cda = (__u32)virt_to_phys(features);
156262306a36Sopenharmony_ci
156362306a36Sopenharmony_ci	cqr->buildclk = get_tod_clock();
156462306a36Sopenharmony_ci	cqr->status = DASD_CQR_FILLED;
156562306a36Sopenharmony_ci	rc = dasd_sleep_on(cqr);
156662306a36Sopenharmony_ci	if (rc == 0) {
156762306a36Sopenharmony_ci		prssdp = (struct dasd_psf_prssd_data *) cqr->data;
156862306a36Sopenharmony_ci		features = (struct dasd_rssd_features *) (prssdp + 1);
156962306a36Sopenharmony_ci		memcpy(&private->features, features,
157062306a36Sopenharmony_ci		       sizeof(struct dasd_rssd_features));
157162306a36Sopenharmony_ci	} else
157262306a36Sopenharmony_ci		dev_warn(&device->cdev->dev, "Reading device feature codes"
157362306a36Sopenharmony_ci			 " failed with rc=%d\n", rc);
157462306a36Sopenharmony_ci	dasd_sfree_request(cqr, cqr->memdev);
157562306a36Sopenharmony_ci	return rc;
157662306a36Sopenharmony_ci}
157762306a36Sopenharmony_ci
157862306a36Sopenharmony_ci/* Read Volume Information - Volume Storage Query */
157962306a36Sopenharmony_cistatic int dasd_eckd_read_vol_info(struct dasd_device *device)
158062306a36Sopenharmony_ci{
158162306a36Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
158262306a36Sopenharmony_ci	struct dasd_psf_prssd_data *prssdp;
158362306a36Sopenharmony_ci	struct dasd_rssd_vsq *vsq;
158462306a36Sopenharmony_ci	struct dasd_ccw_req *cqr;
158562306a36Sopenharmony_ci	struct ccw1 *ccw;
158662306a36Sopenharmony_ci	int useglobal;
158762306a36Sopenharmony_ci	int rc;
158862306a36Sopenharmony_ci
158962306a36Sopenharmony_ci	/* This command cannot be executed on an alias device */
159062306a36Sopenharmony_ci	if (private->uid.type == UA_BASE_PAV_ALIAS ||
159162306a36Sopenharmony_ci	    private->uid.type == UA_HYPER_PAV_ALIAS)
159262306a36Sopenharmony_ci		return 0;
159362306a36Sopenharmony_ci
159462306a36Sopenharmony_ci	useglobal = 0;
159562306a36Sopenharmony_ci	cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 2 /* PSF + RSSD */,
159662306a36Sopenharmony_ci				   sizeof(*prssdp) + sizeof(*vsq), device, NULL);
159762306a36Sopenharmony_ci	if (IS_ERR(cqr)) {
159862306a36Sopenharmony_ci		DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s",
159962306a36Sopenharmony_ci				"Could not allocate initialization request");
160062306a36Sopenharmony_ci		mutex_lock(&dasd_vol_info_mutex);
160162306a36Sopenharmony_ci		useglobal = 1;
160262306a36Sopenharmony_ci		cqr = &dasd_vol_info_req->cqr;
160362306a36Sopenharmony_ci		memset(cqr, 0, sizeof(*cqr));
160462306a36Sopenharmony_ci		memset(dasd_vol_info_req, 0, sizeof(*dasd_vol_info_req));
160562306a36Sopenharmony_ci		cqr->cpaddr = &dasd_vol_info_req->ccw;
160662306a36Sopenharmony_ci		cqr->data = &dasd_vol_info_req->data;
160762306a36Sopenharmony_ci		cqr->magic = DASD_ECKD_MAGIC;
160862306a36Sopenharmony_ci	}
160962306a36Sopenharmony_ci
161062306a36Sopenharmony_ci	/* Prepare for Read Subsystem Data */
161162306a36Sopenharmony_ci	prssdp = cqr->data;
161262306a36Sopenharmony_ci	prssdp->order = PSF_ORDER_PRSSD;
161362306a36Sopenharmony_ci	prssdp->suborder = PSF_SUBORDER_VSQ;	/* Volume Storage Query */
161462306a36Sopenharmony_ci	prssdp->lss = private->conf.ned->ID;
161562306a36Sopenharmony_ci	prssdp->volume = private->conf.ned->unit_addr;
161662306a36Sopenharmony_ci
161762306a36Sopenharmony_ci	ccw = cqr->cpaddr;
161862306a36Sopenharmony_ci	ccw->cmd_code = DASD_ECKD_CCW_PSF;
161962306a36Sopenharmony_ci	ccw->count = sizeof(*prssdp);
162062306a36Sopenharmony_ci	ccw->flags |= CCW_FLAG_CC;
162162306a36Sopenharmony_ci	ccw->cda = (__u32)virt_to_phys(prssdp);
162262306a36Sopenharmony_ci
162362306a36Sopenharmony_ci	/* Read Subsystem Data - Volume Storage Query */
162462306a36Sopenharmony_ci	vsq = (struct dasd_rssd_vsq *)(prssdp + 1);
162562306a36Sopenharmony_ci	memset(vsq, 0, sizeof(*vsq));
162662306a36Sopenharmony_ci
162762306a36Sopenharmony_ci	ccw++;
162862306a36Sopenharmony_ci	ccw->cmd_code = DASD_ECKD_CCW_RSSD;
162962306a36Sopenharmony_ci	ccw->count = sizeof(*vsq);
163062306a36Sopenharmony_ci	ccw->flags |= CCW_FLAG_SLI;
163162306a36Sopenharmony_ci	ccw->cda = (__u32)virt_to_phys(vsq);
163262306a36Sopenharmony_ci
163362306a36Sopenharmony_ci	cqr->buildclk = get_tod_clock();
163462306a36Sopenharmony_ci	cqr->status = DASD_CQR_FILLED;
163562306a36Sopenharmony_ci	cqr->startdev = device;
163662306a36Sopenharmony_ci	cqr->memdev = device;
163762306a36Sopenharmony_ci	cqr->block = NULL;
163862306a36Sopenharmony_ci	cqr->retries = 256;
163962306a36Sopenharmony_ci	cqr->expires = device->default_expires * HZ;
164062306a36Sopenharmony_ci	/* The command might not be supported. Suppress the error output */
164162306a36Sopenharmony_ci	__set_bit(DASD_CQR_SUPPRESS_CR, &cqr->flags);
164262306a36Sopenharmony_ci
164362306a36Sopenharmony_ci	rc = dasd_sleep_on_interruptible(cqr);
164462306a36Sopenharmony_ci	if (rc == 0) {
164562306a36Sopenharmony_ci		memcpy(&private->vsq, vsq, sizeof(*vsq));
164662306a36Sopenharmony_ci	} else {
164762306a36Sopenharmony_ci		DBF_EVENT_DEVID(DBF_WARNING, device->cdev,
164862306a36Sopenharmony_ci				"Reading the volume storage information failed with rc=%d", rc);
164962306a36Sopenharmony_ci	}
165062306a36Sopenharmony_ci
165162306a36Sopenharmony_ci	if (useglobal)
165262306a36Sopenharmony_ci		mutex_unlock(&dasd_vol_info_mutex);
165362306a36Sopenharmony_ci	else
165462306a36Sopenharmony_ci		dasd_sfree_request(cqr, cqr->memdev);
165562306a36Sopenharmony_ci
165662306a36Sopenharmony_ci	return rc;
165762306a36Sopenharmony_ci}
165862306a36Sopenharmony_ci
165962306a36Sopenharmony_cistatic int dasd_eckd_is_ese(struct dasd_device *device)
166062306a36Sopenharmony_ci{
166162306a36Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
166262306a36Sopenharmony_ci
166362306a36Sopenharmony_ci	return private->vsq.vol_info.ese;
166462306a36Sopenharmony_ci}
166562306a36Sopenharmony_ci
166662306a36Sopenharmony_cistatic int dasd_eckd_ext_pool_id(struct dasd_device *device)
166762306a36Sopenharmony_ci{
166862306a36Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
166962306a36Sopenharmony_ci
167062306a36Sopenharmony_ci	return private->vsq.extent_pool_id;
167162306a36Sopenharmony_ci}
167262306a36Sopenharmony_ci
167362306a36Sopenharmony_ci/*
167462306a36Sopenharmony_ci * This value represents the total amount of available space. As more space is
167562306a36Sopenharmony_ci * allocated by ESE volumes, this value will decrease.
167662306a36Sopenharmony_ci * The data for this value is therefore updated on any call.
167762306a36Sopenharmony_ci */
167862306a36Sopenharmony_cistatic int dasd_eckd_space_configured(struct dasd_device *device)
167962306a36Sopenharmony_ci{
168062306a36Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
168162306a36Sopenharmony_ci	int rc;
168262306a36Sopenharmony_ci
168362306a36Sopenharmony_ci	rc = dasd_eckd_read_vol_info(device);
168462306a36Sopenharmony_ci
168562306a36Sopenharmony_ci	return rc ? : private->vsq.space_configured;
168662306a36Sopenharmony_ci}
168762306a36Sopenharmony_ci
168862306a36Sopenharmony_ci/*
168962306a36Sopenharmony_ci * The value of space allocated by an ESE volume may have changed and is
169062306a36Sopenharmony_ci * therefore updated on any call.
169162306a36Sopenharmony_ci */
169262306a36Sopenharmony_cistatic int dasd_eckd_space_allocated(struct dasd_device *device)
169362306a36Sopenharmony_ci{
169462306a36Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
169562306a36Sopenharmony_ci	int rc;
169662306a36Sopenharmony_ci
169762306a36Sopenharmony_ci	rc = dasd_eckd_read_vol_info(device);
169862306a36Sopenharmony_ci
169962306a36Sopenharmony_ci	return rc ? : private->vsq.space_allocated;
170062306a36Sopenharmony_ci}
170162306a36Sopenharmony_ci
170262306a36Sopenharmony_cistatic int dasd_eckd_logical_capacity(struct dasd_device *device)
170362306a36Sopenharmony_ci{
170462306a36Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
170562306a36Sopenharmony_ci
170662306a36Sopenharmony_ci	return private->vsq.logical_capacity;
170762306a36Sopenharmony_ci}
170862306a36Sopenharmony_ci
170962306a36Sopenharmony_cistatic void dasd_eckd_ext_pool_exhaust_work(struct work_struct *work)
171062306a36Sopenharmony_ci{
171162306a36Sopenharmony_ci	struct ext_pool_exhaust_work_data *data;
171262306a36Sopenharmony_ci	struct dasd_device *device;
171362306a36Sopenharmony_ci	struct dasd_device *base;
171462306a36Sopenharmony_ci
171562306a36Sopenharmony_ci	data = container_of(work, struct ext_pool_exhaust_work_data, worker);
171662306a36Sopenharmony_ci	device = data->device;
171762306a36Sopenharmony_ci	base = data->base;
171862306a36Sopenharmony_ci
171962306a36Sopenharmony_ci	if (!base)
172062306a36Sopenharmony_ci		base = device;
172162306a36Sopenharmony_ci	if (dasd_eckd_space_configured(base) != 0) {
172262306a36Sopenharmony_ci		dasd_generic_space_avail(device);
172362306a36Sopenharmony_ci	} else {
172462306a36Sopenharmony_ci		dev_warn(&device->cdev->dev, "No space left in the extent pool\n");
172562306a36Sopenharmony_ci		DBF_DEV_EVENT(DBF_WARNING, device, "%s", "out of space");
172662306a36Sopenharmony_ci	}
172762306a36Sopenharmony_ci
172862306a36Sopenharmony_ci	dasd_put_device(device);
172962306a36Sopenharmony_ci	kfree(data);
173062306a36Sopenharmony_ci}
173162306a36Sopenharmony_ci
173262306a36Sopenharmony_cistatic int dasd_eckd_ext_pool_exhaust(struct dasd_device *device,
173362306a36Sopenharmony_ci				      struct dasd_ccw_req *cqr)
173462306a36Sopenharmony_ci{
173562306a36Sopenharmony_ci	struct ext_pool_exhaust_work_data *data;
173662306a36Sopenharmony_ci
173762306a36Sopenharmony_ci	data = kzalloc(sizeof(*data), GFP_ATOMIC);
173862306a36Sopenharmony_ci	if (!data)
173962306a36Sopenharmony_ci		return -ENOMEM;
174062306a36Sopenharmony_ci	INIT_WORK(&data->worker, dasd_eckd_ext_pool_exhaust_work);
174162306a36Sopenharmony_ci	dasd_get_device(device);
174262306a36Sopenharmony_ci	data->device = device;
174362306a36Sopenharmony_ci
174462306a36Sopenharmony_ci	if (cqr->block)
174562306a36Sopenharmony_ci		data->base = cqr->block->base;
174662306a36Sopenharmony_ci	else if (cqr->basedev)
174762306a36Sopenharmony_ci		data->base = cqr->basedev;
174862306a36Sopenharmony_ci	else
174962306a36Sopenharmony_ci		data->base = NULL;
175062306a36Sopenharmony_ci
175162306a36Sopenharmony_ci	schedule_work(&data->worker);
175262306a36Sopenharmony_ci
175362306a36Sopenharmony_ci	return 0;
175462306a36Sopenharmony_ci}
175562306a36Sopenharmony_ci
175662306a36Sopenharmony_cistatic void dasd_eckd_cpy_ext_pool_data(struct dasd_device *device,
175762306a36Sopenharmony_ci					struct dasd_rssd_lcq *lcq)
175862306a36Sopenharmony_ci{
175962306a36Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
176062306a36Sopenharmony_ci	int pool_id = dasd_eckd_ext_pool_id(device);
176162306a36Sopenharmony_ci	struct dasd_ext_pool_sum eps;
176262306a36Sopenharmony_ci	int i;
176362306a36Sopenharmony_ci
176462306a36Sopenharmony_ci	for (i = 0; i < lcq->pool_count; i++) {
176562306a36Sopenharmony_ci		eps = lcq->ext_pool_sum[i];
176662306a36Sopenharmony_ci		if (eps.pool_id == pool_id) {
176762306a36Sopenharmony_ci			memcpy(&private->eps, &eps,
176862306a36Sopenharmony_ci			       sizeof(struct dasd_ext_pool_sum));
176962306a36Sopenharmony_ci		}
177062306a36Sopenharmony_ci	}
177162306a36Sopenharmony_ci}
177262306a36Sopenharmony_ci
177362306a36Sopenharmony_ci/* Read Extent Pool Information - Logical Configuration Query */
177462306a36Sopenharmony_cistatic int dasd_eckd_read_ext_pool_info(struct dasd_device *device)
177562306a36Sopenharmony_ci{
177662306a36Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
177762306a36Sopenharmony_ci	struct dasd_psf_prssd_data *prssdp;
177862306a36Sopenharmony_ci	struct dasd_rssd_lcq *lcq;
177962306a36Sopenharmony_ci	struct dasd_ccw_req *cqr;
178062306a36Sopenharmony_ci	struct ccw1 *ccw;
178162306a36Sopenharmony_ci	int rc;
178262306a36Sopenharmony_ci
178362306a36Sopenharmony_ci	/* This command cannot be executed on an alias device */
178462306a36Sopenharmony_ci	if (private->uid.type == UA_BASE_PAV_ALIAS ||
178562306a36Sopenharmony_ci	    private->uid.type == UA_HYPER_PAV_ALIAS)
178662306a36Sopenharmony_ci		return 0;
178762306a36Sopenharmony_ci
178862306a36Sopenharmony_ci	cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 2 /* PSF + RSSD */,
178962306a36Sopenharmony_ci				   sizeof(*prssdp) + sizeof(*lcq), device, NULL);
179062306a36Sopenharmony_ci	if (IS_ERR(cqr)) {
179162306a36Sopenharmony_ci		DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s",
179262306a36Sopenharmony_ci				"Could not allocate initialization request");
179362306a36Sopenharmony_ci		return PTR_ERR(cqr);
179462306a36Sopenharmony_ci	}
179562306a36Sopenharmony_ci
179662306a36Sopenharmony_ci	/* Prepare for Read Subsystem Data */
179762306a36Sopenharmony_ci	prssdp = cqr->data;
179862306a36Sopenharmony_ci	memset(prssdp, 0, sizeof(*prssdp));
179962306a36Sopenharmony_ci	prssdp->order = PSF_ORDER_PRSSD;
180062306a36Sopenharmony_ci	prssdp->suborder = PSF_SUBORDER_LCQ;	/* Logical Configuration Query */
180162306a36Sopenharmony_ci
180262306a36Sopenharmony_ci	ccw = cqr->cpaddr;
180362306a36Sopenharmony_ci	ccw->cmd_code = DASD_ECKD_CCW_PSF;
180462306a36Sopenharmony_ci	ccw->count = sizeof(*prssdp);
180562306a36Sopenharmony_ci	ccw->flags |= CCW_FLAG_CC;
180662306a36Sopenharmony_ci	ccw->cda = (__u32)virt_to_phys(prssdp);
180762306a36Sopenharmony_ci
180862306a36Sopenharmony_ci	lcq = (struct dasd_rssd_lcq *)(prssdp + 1);
180962306a36Sopenharmony_ci	memset(lcq, 0, sizeof(*lcq));
181062306a36Sopenharmony_ci
181162306a36Sopenharmony_ci	ccw++;
181262306a36Sopenharmony_ci	ccw->cmd_code = DASD_ECKD_CCW_RSSD;
181362306a36Sopenharmony_ci	ccw->count = sizeof(*lcq);
181462306a36Sopenharmony_ci	ccw->flags |= CCW_FLAG_SLI;
181562306a36Sopenharmony_ci	ccw->cda = (__u32)virt_to_phys(lcq);
181662306a36Sopenharmony_ci
181762306a36Sopenharmony_ci	cqr->buildclk = get_tod_clock();
181862306a36Sopenharmony_ci	cqr->status = DASD_CQR_FILLED;
181962306a36Sopenharmony_ci	cqr->startdev = device;
182062306a36Sopenharmony_ci	cqr->memdev = device;
182162306a36Sopenharmony_ci	cqr->block = NULL;
182262306a36Sopenharmony_ci	cqr->retries = 256;
182362306a36Sopenharmony_ci	cqr->expires = device->default_expires * HZ;
182462306a36Sopenharmony_ci	/* The command might not be supported. Suppress the error output */
182562306a36Sopenharmony_ci	__set_bit(DASD_CQR_SUPPRESS_CR, &cqr->flags);
182662306a36Sopenharmony_ci
182762306a36Sopenharmony_ci	rc = dasd_sleep_on_interruptible(cqr);
182862306a36Sopenharmony_ci	if (rc == 0) {
182962306a36Sopenharmony_ci		dasd_eckd_cpy_ext_pool_data(device, lcq);
183062306a36Sopenharmony_ci	} else {
183162306a36Sopenharmony_ci		DBF_EVENT_DEVID(DBF_WARNING, device->cdev,
183262306a36Sopenharmony_ci				"Reading the logical configuration failed with rc=%d", rc);
183362306a36Sopenharmony_ci	}
183462306a36Sopenharmony_ci
183562306a36Sopenharmony_ci	dasd_sfree_request(cqr, cqr->memdev);
183662306a36Sopenharmony_ci
183762306a36Sopenharmony_ci	return rc;
183862306a36Sopenharmony_ci}
183962306a36Sopenharmony_ci
184062306a36Sopenharmony_ci/*
184162306a36Sopenharmony_ci * Depending on the device type, the extent size is specified either as
184262306a36Sopenharmony_ci * cylinders per extent (CKD) or size per extent (FBA)
184362306a36Sopenharmony_ci * A 1GB size corresponds to 1113cyl, and 16MB to 21cyl.
184462306a36Sopenharmony_ci */
184562306a36Sopenharmony_cistatic int dasd_eckd_ext_size(struct dasd_device *device)
184662306a36Sopenharmony_ci{
184762306a36Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
184862306a36Sopenharmony_ci	struct dasd_ext_pool_sum eps = private->eps;
184962306a36Sopenharmony_ci
185062306a36Sopenharmony_ci	if (!eps.flags.extent_size_valid)
185162306a36Sopenharmony_ci		return 0;
185262306a36Sopenharmony_ci	if (eps.extent_size.size_1G)
185362306a36Sopenharmony_ci		return 1113;
185462306a36Sopenharmony_ci	if (eps.extent_size.size_16M)
185562306a36Sopenharmony_ci		return 21;
185662306a36Sopenharmony_ci
185762306a36Sopenharmony_ci	return 0;
185862306a36Sopenharmony_ci}
185962306a36Sopenharmony_ci
186062306a36Sopenharmony_cistatic int dasd_eckd_ext_pool_warn_thrshld(struct dasd_device *device)
186162306a36Sopenharmony_ci{
186262306a36Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
186362306a36Sopenharmony_ci
186462306a36Sopenharmony_ci	return private->eps.warn_thrshld;
186562306a36Sopenharmony_ci}
186662306a36Sopenharmony_ci
186762306a36Sopenharmony_cistatic int dasd_eckd_ext_pool_cap_at_warnlevel(struct dasd_device *device)
186862306a36Sopenharmony_ci{
186962306a36Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
187062306a36Sopenharmony_ci
187162306a36Sopenharmony_ci	return private->eps.flags.capacity_at_warnlevel;
187262306a36Sopenharmony_ci}
187362306a36Sopenharmony_ci
187462306a36Sopenharmony_ci/*
187562306a36Sopenharmony_ci * Extent Pool out of space
187662306a36Sopenharmony_ci */
187762306a36Sopenharmony_cistatic int dasd_eckd_ext_pool_oos(struct dasd_device *device)
187862306a36Sopenharmony_ci{
187962306a36Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
188062306a36Sopenharmony_ci
188162306a36Sopenharmony_ci	return private->eps.flags.pool_oos;
188262306a36Sopenharmony_ci}
188362306a36Sopenharmony_ci
188462306a36Sopenharmony_ci/*
188562306a36Sopenharmony_ci * Build CP for Perform Subsystem Function - SSC.
188662306a36Sopenharmony_ci */
188762306a36Sopenharmony_cistatic struct dasd_ccw_req *dasd_eckd_build_psf_ssc(struct dasd_device *device,
188862306a36Sopenharmony_ci						    int enable_pav)
188962306a36Sopenharmony_ci{
189062306a36Sopenharmony_ci	struct dasd_ccw_req *cqr;
189162306a36Sopenharmony_ci	struct dasd_psf_ssc_data *psf_ssc_data;
189262306a36Sopenharmony_ci	struct ccw1 *ccw;
189362306a36Sopenharmony_ci
189462306a36Sopenharmony_ci	cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */ ,
189562306a36Sopenharmony_ci				  sizeof(struct dasd_psf_ssc_data),
189662306a36Sopenharmony_ci				   device, NULL);
189762306a36Sopenharmony_ci
189862306a36Sopenharmony_ci	if (IS_ERR(cqr)) {
189962306a36Sopenharmony_ci		DBF_DEV_EVENT(DBF_WARNING, device, "%s",
190062306a36Sopenharmony_ci			   "Could not allocate PSF-SSC request");
190162306a36Sopenharmony_ci		return cqr;
190262306a36Sopenharmony_ci	}
190362306a36Sopenharmony_ci	psf_ssc_data = (struct dasd_psf_ssc_data *)cqr->data;
190462306a36Sopenharmony_ci	psf_ssc_data->order = PSF_ORDER_SSC;
190562306a36Sopenharmony_ci	psf_ssc_data->suborder = 0xc0;
190662306a36Sopenharmony_ci	if (enable_pav) {
190762306a36Sopenharmony_ci		psf_ssc_data->suborder |= 0x08;
190862306a36Sopenharmony_ci		psf_ssc_data->reserved[0] = 0x88;
190962306a36Sopenharmony_ci	}
191062306a36Sopenharmony_ci	ccw = cqr->cpaddr;
191162306a36Sopenharmony_ci	ccw->cmd_code = DASD_ECKD_CCW_PSF;
191262306a36Sopenharmony_ci	ccw->cda = (__u32)virt_to_phys(psf_ssc_data);
191362306a36Sopenharmony_ci	ccw->count = 66;
191462306a36Sopenharmony_ci
191562306a36Sopenharmony_ci	cqr->startdev = device;
191662306a36Sopenharmony_ci	cqr->memdev = device;
191762306a36Sopenharmony_ci	cqr->block = NULL;
191862306a36Sopenharmony_ci	cqr->retries = 256;
191962306a36Sopenharmony_ci	cqr->expires = 10*HZ;
192062306a36Sopenharmony_ci	cqr->buildclk = get_tod_clock();
192162306a36Sopenharmony_ci	cqr->status = DASD_CQR_FILLED;
192262306a36Sopenharmony_ci	return cqr;
192362306a36Sopenharmony_ci}
192462306a36Sopenharmony_ci
192562306a36Sopenharmony_ci/*
192662306a36Sopenharmony_ci * Perform Subsystem Function.
192762306a36Sopenharmony_ci * It is necessary to trigger CIO for channel revalidation since this
192862306a36Sopenharmony_ci * call might change behaviour of DASD devices.
192962306a36Sopenharmony_ci */
193062306a36Sopenharmony_cistatic int
193162306a36Sopenharmony_cidasd_eckd_psf_ssc(struct dasd_device *device, int enable_pav,
193262306a36Sopenharmony_ci		  unsigned long flags)
193362306a36Sopenharmony_ci{
193462306a36Sopenharmony_ci	struct dasd_ccw_req *cqr;
193562306a36Sopenharmony_ci	int rc;
193662306a36Sopenharmony_ci
193762306a36Sopenharmony_ci	cqr = dasd_eckd_build_psf_ssc(device, enable_pav);
193862306a36Sopenharmony_ci	if (IS_ERR(cqr))
193962306a36Sopenharmony_ci		return PTR_ERR(cqr);
194062306a36Sopenharmony_ci
194162306a36Sopenharmony_ci	/*
194262306a36Sopenharmony_ci	 * set flags e.g. turn on failfast, to prevent blocking
194362306a36Sopenharmony_ci	 * the calling function should handle failed requests
194462306a36Sopenharmony_ci	 */
194562306a36Sopenharmony_ci	cqr->flags |= flags;
194662306a36Sopenharmony_ci
194762306a36Sopenharmony_ci	rc = dasd_sleep_on(cqr);
194862306a36Sopenharmony_ci	if (!rc)
194962306a36Sopenharmony_ci		/* trigger CIO to reprobe devices */
195062306a36Sopenharmony_ci		css_schedule_reprobe();
195162306a36Sopenharmony_ci	else if (cqr->intrc == -EAGAIN)
195262306a36Sopenharmony_ci		rc = -EAGAIN;
195362306a36Sopenharmony_ci
195462306a36Sopenharmony_ci	dasd_sfree_request(cqr, cqr->memdev);
195562306a36Sopenharmony_ci	return rc;
195662306a36Sopenharmony_ci}
195762306a36Sopenharmony_ci
195862306a36Sopenharmony_ci/*
195962306a36Sopenharmony_ci * Valide storage server of current device.
196062306a36Sopenharmony_ci */
196162306a36Sopenharmony_cistatic int dasd_eckd_validate_server(struct dasd_device *device,
196262306a36Sopenharmony_ci				     unsigned long flags)
196362306a36Sopenharmony_ci{
196462306a36Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
196562306a36Sopenharmony_ci	int enable_pav, rc;
196662306a36Sopenharmony_ci
196762306a36Sopenharmony_ci	if (private->uid.type == UA_BASE_PAV_ALIAS ||
196862306a36Sopenharmony_ci	    private->uid.type == UA_HYPER_PAV_ALIAS)
196962306a36Sopenharmony_ci		return 0;
197062306a36Sopenharmony_ci	if (dasd_nopav || MACHINE_IS_VM)
197162306a36Sopenharmony_ci		enable_pav = 0;
197262306a36Sopenharmony_ci	else
197362306a36Sopenharmony_ci		enable_pav = 1;
197462306a36Sopenharmony_ci	rc = dasd_eckd_psf_ssc(device, enable_pav, flags);
197562306a36Sopenharmony_ci
197662306a36Sopenharmony_ci	/* may be requested feature is not available on server,
197762306a36Sopenharmony_ci	 * therefore just report error and go ahead */
197862306a36Sopenharmony_ci	DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "PSF-SSC for SSID %04x "
197962306a36Sopenharmony_ci			"returned rc=%d", private->uid.ssid, rc);
198062306a36Sopenharmony_ci	return rc;
198162306a36Sopenharmony_ci}
198262306a36Sopenharmony_ci
198362306a36Sopenharmony_ci/*
198462306a36Sopenharmony_ci * worker to do a validate server in case of a lost pathgroup
198562306a36Sopenharmony_ci */
198662306a36Sopenharmony_cistatic void dasd_eckd_do_validate_server(struct work_struct *work)
198762306a36Sopenharmony_ci{
198862306a36Sopenharmony_ci	struct dasd_device *device = container_of(work, struct dasd_device,
198962306a36Sopenharmony_ci						  kick_validate);
199062306a36Sopenharmony_ci	unsigned long flags = 0;
199162306a36Sopenharmony_ci
199262306a36Sopenharmony_ci	set_bit(DASD_CQR_FLAGS_FAILFAST, &flags);
199362306a36Sopenharmony_ci	if (dasd_eckd_validate_server(device, flags)
199462306a36Sopenharmony_ci	    == -EAGAIN) {
199562306a36Sopenharmony_ci		/* schedule worker again if failed */
199662306a36Sopenharmony_ci		schedule_work(&device->kick_validate);
199762306a36Sopenharmony_ci		return;
199862306a36Sopenharmony_ci	}
199962306a36Sopenharmony_ci
200062306a36Sopenharmony_ci	dasd_put_device(device);
200162306a36Sopenharmony_ci}
200262306a36Sopenharmony_ci
200362306a36Sopenharmony_cistatic void dasd_eckd_kick_validate_server(struct dasd_device *device)
200462306a36Sopenharmony_ci{
200562306a36Sopenharmony_ci	dasd_get_device(device);
200662306a36Sopenharmony_ci	/* exit if device not online or in offline processing */
200762306a36Sopenharmony_ci	if (test_bit(DASD_FLAG_OFFLINE, &device->flags) ||
200862306a36Sopenharmony_ci	   device->state < DASD_STATE_ONLINE) {
200962306a36Sopenharmony_ci		dasd_put_device(device);
201062306a36Sopenharmony_ci		return;
201162306a36Sopenharmony_ci	}
201262306a36Sopenharmony_ci	/* queue call to do_validate_server to the kernel event daemon. */
201362306a36Sopenharmony_ci	if (!schedule_work(&device->kick_validate))
201462306a36Sopenharmony_ci		dasd_put_device(device);
201562306a36Sopenharmony_ci}
201662306a36Sopenharmony_ci
201762306a36Sopenharmony_ci/*
201862306a36Sopenharmony_ci * return if the device is the copy relation primary if a copy relation is active
201962306a36Sopenharmony_ci */
202062306a36Sopenharmony_cistatic int dasd_device_is_primary(struct dasd_device *device)
202162306a36Sopenharmony_ci{
202262306a36Sopenharmony_ci	if (!device->copy)
202362306a36Sopenharmony_ci		return 1;
202462306a36Sopenharmony_ci
202562306a36Sopenharmony_ci	if (device->copy->active->device == device)
202662306a36Sopenharmony_ci		return 1;
202762306a36Sopenharmony_ci
202862306a36Sopenharmony_ci	return 0;
202962306a36Sopenharmony_ci}
203062306a36Sopenharmony_ci
203162306a36Sopenharmony_cistatic int dasd_eckd_alloc_block(struct dasd_device *device)
203262306a36Sopenharmony_ci{
203362306a36Sopenharmony_ci	struct dasd_block *block;
203462306a36Sopenharmony_ci	struct dasd_uid temp_uid;
203562306a36Sopenharmony_ci
203662306a36Sopenharmony_ci	if (!dasd_device_is_primary(device))
203762306a36Sopenharmony_ci		return 0;
203862306a36Sopenharmony_ci
203962306a36Sopenharmony_ci	dasd_eckd_get_uid(device, &temp_uid);
204062306a36Sopenharmony_ci	if (temp_uid.type == UA_BASE_DEVICE) {
204162306a36Sopenharmony_ci		block = dasd_alloc_block();
204262306a36Sopenharmony_ci		if (IS_ERR(block)) {
204362306a36Sopenharmony_ci			DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s",
204462306a36Sopenharmony_ci					"could not allocate dasd block structure");
204562306a36Sopenharmony_ci			return PTR_ERR(block);
204662306a36Sopenharmony_ci		}
204762306a36Sopenharmony_ci		device->block = block;
204862306a36Sopenharmony_ci		block->base = device;
204962306a36Sopenharmony_ci	}
205062306a36Sopenharmony_ci	return 0;
205162306a36Sopenharmony_ci}
205262306a36Sopenharmony_ci
205362306a36Sopenharmony_cistatic bool dasd_eckd_pprc_enabled(struct dasd_device *device)
205462306a36Sopenharmony_ci{
205562306a36Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
205662306a36Sopenharmony_ci
205762306a36Sopenharmony_ci	return private->rdc_data.facilities.PPRC_enabled;
205862306a36Sopenharmony_ci}
205962306a36Sopenharmony_ci
206062306a36Sopenharmony_ci/*
206162306a36Sopenharmony_ci * Check device characteristics.
206262306a36Sopenharmony_ci * If the device is accessible using ECKD discipline, the device is enabled.
206362306a36Sopenharmony_ci */
206462306a36Sopenharmony_cistatic int
206562306a36Sopenharmony_cidasd_eckd_check_characteristics(struct dasd_device *device)
206662306a36Sopenharmony_ci{
206762306a36Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
206862306a36Sopenharmony_ci	int rc, i;
206962306a36Sopenharmony_ci	int readonly;
207062306a36Sopenharmony_ci	unsigned long value;
207162306a36Sopenharmony_ci
207262306a36Sopenharmony_ci	/* setup work queue for validate server*/
207362306a36Sopenharmony_ci	INIT_WORK(&device->kick_validate, dasd_eckd_do_validate_server);
207462306a36Sopenharmony_ci	/* setup work queue for summary unit check */
207562306a36Sopenharmony_ci	INIT_WORK(&device->suc_work, dasd_alias_handle_summary_unit_check);
207662306a36Sopenharmony_ci
207762306a36Sopenharmony_ci	if (!ccw_device_is_pathgroup(device->cdev)) {
207862306a36Sopenharmony_ci		dev_warn(&device->cdev->dev,
207962306a36Sopenharmony_ci			 "A channel path group could not be established\n");
208062306a36Sopenharmony_ci		return -EIO;
208162306a36Sopenharmony_ci	}
208262306a36Sopenharmony_ci	if (!ccw_device_is_multipath(device->cdev)) {
208362306a36Sopenharmony_ci		dev_info(&device->cdev->dev,
208462306a36Sopenharmony_ci			 "The DASD is not operating in multipath mode\n");
208562306a36Sopenharmony_ci	}
208662306a36Sopenharmony_ci	if (!private) {
208762306a36Sopenharmony_ci		private = kzalloc(sizeof(*private), GFP_KERNEL | GFP_DMA);
208862306a36Sopenharmony_ci		if (!private) {
208962306a36Sopenharmony_ci			dev_warn(&device->cdev->dev,
209062306a36Sopenharmony_ci				 "Allocating memory for private DASD data "
209162306a36Sopenharmony_ci				 "failed\n");
209262306a36Sopenharmony_ci			return -ENOMEM;
209362306a36Sopenharmony_ci		}
209462306a36Sopenharmony_ci		device->private = private;
209562306a36Sopenharmony_ci	} else {
209662306a36Sopenharmony_ci		memset(private, 0, sizeof(*private));
209762306a36Sopenharmony_ci	}
209862306a36Sopenharmony_ci	/* Invalidate status of initial analysis. */
209962306a36Sopenharmony_ci	private->init_cqr_status = -1;
210062306a36Sopenharmony_ci	/* Set default cache operations. */
210162306a36Sopenharmony_ci	private->attrib.operation = DASD_NORMAL_CACHE;
210262306a36Sopenharmony_ci	private->attrib.nr_cyl = 0;
210362306a36Sopenharmony_ci
210462306a36Sopenharmony_ci	/* Read Configuration Data */
210562306a36Sopenharmony_ci	rc = dasd_eckd_read_conf(device);
210662306a36Sopenharmony_ci	if (rc)
210762306a36Sopenharmony_ci		goto out_err1;
210862306a36Sopenharmony_ci
210962306a36Sopenharmony_ci	/* set some default values */
211062306a36Sopenharmony_ci	device->default_expires = DASD_EXPIRES;
211162306a36Sopenharmony_ci	device->default_retries = DASD_RETRIES;
211262306a36Sopenharmony_ci	device->path_thrhld = DASD_ECKD_PATH_THRHLD;
211362306a36Sopenharmony_ci	device->path_interval = DASD_ECKD_PATH_INTERVAL;
211462306a36Sopenharmony_ci	device->aq_timeouts = DASD_RETRIES_MAX;
211562306a36Sopenharmony_ci
211662306a36Sopenharmony_ci	if (private->conf.gneq) {
211762306a36Sopenharmony_ci		value = 1;
211862306a36Sopenharmony_ci		for (i = 0; i < private->conf.gneq->timeout.value; i++)
211962306a36Sopenharmony_ci			value = 10 * value;
212062306a36Sopenharmony_ci		value = value * private->conf.gneq->timeout.number;
212162306a36Sopenharmony_ci		/* do not accept useless values */
212262306a36Sopenharmony_ci		if (value != 0 && value <= DASD_EXPIRES_MAX)
212362306a36Sopenharmony_ci			device->default_expires = value;
212462306a36Sopenharmony_ci	}
212562306a36Sopenharmony_ci
212662306a36Sopenharmony_ci	/* Read Device Characteristics */
212762306a36Sopenharmony_ci	rc = dasd_generic_read_dev_chars(device, DASD_ECKD_MAGIC,
212862306a36Sopenharmony_ci					 &private->rdc_data, 64);
212962306a36Sopenharmony_ci	if (rc) {
213062306a36Sopenharmony_ci		DBF_EVENT_DEVID(DBF_WARNING, device->cdev,
213162306a36Sopenharmony_ci				"Read device characteristic failed, rc=%d", rc);
213262306a36Sopenharmony_ci		goto out_err1;
213362306a36Sopenharmony_ci	}
213462306a36Sopenharmony_ci
213562306a36Sopenharmony_ci	/* setup PPRC for device from devmap */
213662306a36Sopenharmony_ci	rc = dasd_devmap_set_device_copy_relation(device->cdev,
213762306a36Sopenharmony_ci						  dasd_eckd_pprc_enabled(device));
213862306a36Sopenharmony_ci	if (rc) {
213962306a36Sopenharmony_ci		DBF_EVENT_DEVID(DBF_WARNING, device->cdev,
214062306a36Sopenharmony_ci				"copy relation setup failed, rc=%d", rc);
214162306a36Sopenharmony_ci		goto out_err1;
214262306a36Sopenharmony_ci	}
214362306a36Sopenharmony_ci
214462306a36Sopenharmony_ci	/* check if block device is needed and allocate in case */
214562306a36Sopenharmony_ci	rc = dasd_eckd_alloc_block(device);
214662306a36Sopenharmony_ci	if (rc)
214762306a36Sopenharmony_ci		goto out_err1;
214862306a36Sopenharmony_ci
214962306a36Sopenharmony_ci	/* register lcu with alias handling, enable PAV */
215062306a36Sopenharmony_ci	rc = dasd_alias_make_device_known_to_lcu(device);
215162306a36Sopenharmony_ci	if (rc)
215262306a36Sopenharmony_ci		goto out_err2;
215362306a36Sopenharmony_ci
215462306a36Sopenharmony_ci	dasd_eckd_validate_server(device, 0);
215562306a36Sopenharmony_ci
215662306a36Sopenharmony_ci	/* device may report different configuration data after LCU setup */
215762306a36Sopenharmony_ci	rc = dasd_eckd_read_conf(device);
215862306a36Sopenharmony_ci	if (rc)
215962306a36Sopenharmony_ci		goto out_err3;
216062306a36Sopenharmony_ci
216162306a36Sopenharmony_ci	dasd_eckd_read_fc_security(device);
216262306a36Sopenharmony_ci	dasd_path_create_kobjects(device);
216362306a36Sopenharmony_ci
216462306a36Sopenharmony_ci	/* Read Feature Codes */
216562306a36Sopenharmony_ci	dasd_eckd_read_features(device);
216662306a36Sopenharmony_ci
216762306a36Sopenharmony_ci	/* Read Volume Information */
216862306a36Sopenharmony_ci	dasd_eckd_read_vol_info(device);
216962306a36Sopenharmony_ci
217062306a36Sopenharmony_ci	/* Read Extent Pool Information */
217162306a36Sopenharmony_ci	dasd_eckd_read_ext_pool_info(device);
217262306a36Sopenharmony_ci
217362306a36Sopenharmony_ci	if ((device->features & DASD_FEATURE_USERAW) &&
217462306a36Sopenharmony_ci	    !(private->rdc_data.facilities.RT_in_LR)) {
217562306a36Sopenharmony_ci		dev_err(&device->cdev->dev, "The storage server does not "
217662306a36Sopenharmony_ci			"support raw-track access\n");
217762306a36Sopenharmony_ci		rc = -EINVAL;
217862306a36Sopenharmony_ci		goto out_err3;
217962306a36Sopenharmony_ci	}
218062306a36Sopenharmony_ci
218162306a36Sopenharmony_ci	/* find the valid cylinder size */
218262306a36Sopenharmony_ci	if (private->rdc_data.no_cyl == LV_COMPAT_CYL &&
218362306a36Sopenharmony_ci	    private->rdc_data.long_no_cyl)
218462306a36Sopenharmony_ci		private->real_cyl = private->rdc_data.long_no_cyl;
218562306a36Sopenharmony_ci	else
218662306a36Sopenharmony_ci		private->real_cyl = private->rdc_data.no_cyl;
218762306a36Sopenharmony_ci
218862306a36Sopenharmony_ci	private->fcx_max_data = get_fcx_max_data(device);
218962306a36Sopenharmony_ci
219062306a36Sopenharmony_ci	readonly = dasd_device_is_ro(device);
219162306a36Sopenharmony_ci	if (readonly)
219262306a36Sopenharmony_ci		set_bit(DASD_FLAG_DEVICE_RO, &device->flags);
219362306a36Sopenharmony_ci
219462306a36Sopenharmony_ci	dev_info(&device->cdev->dev, "New DASD %04X/%02X (CU %04X/%02X) "
219562306a36Sopenharmony_ci		 "with %d cylinders, %d heads, %d sectors%s\n",
219662306a36Sopenharmony_ci		 private->rdc_data.dev_type,
219762306a36Sopenharmony_ci		 private->rdc_data.dev_model,
219862306a36Sopenharmony_ci		 private->rdc_data.cu_type,
219962306a36Sopenharmony_ci		 private->rdc_data.cu_model.model,
220062306a36Sopenharmony_ci		 private->real_cyl,
220162306a36Sopenharmony_ci		 private->rdc_data.trk_per_cyl,
220262306a36Sopenharmony_ci		 private->rdc_data.sec_per_trk,
220362306a36Sopenharmony_ci		 readonly ? ", read-only device" : "");
220462306a36Sopenharmony_ci	return 0;
220562306a36Sopenharmony_ci
220662306a36Sopenharmony_ciout_err3:
220762306a36Sopenharmony_ci	dasd_alias_disconnect_device_from_lcu(device);
220862306a36Sopenharmony_ciout_err2:
220962306a36Sopenharmony_ci	dasd_free_block(device->block);
221062306a36Sopenharmony_ci	device->block = NULL;
221162306a36Sopenharmony_ciout_err1:
221262306a36Sopenharmony_ci	dasd_eckd_clear_conf_data(device);
221362306a36Sopenharmony_ci	dasd_path_remove_kobjects(device);
221462306a36Sopenharmony_ci	kfree(device->private);
221562306a36Sopenharmony_ci	device->private = NULL;
221662306a36Sopenharmony_ci	return rc;
221762306a36Sopenharmony_ci}
221862306a36Sopenharmony_ci
221962306a36Sopenharmony_cistatic void dasd_eckd_uncheck_device(struct dasd_device *device)
222062306a36Sopenharmony_ci{
222162306a36Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
222262306a36Sopenharmony_ci
222362306a36Sopenharmony_ci	if (!private)
222462306a36Sopenharmony_ci		return;
222562306a36Sopenharmony_ci
222662306a36Sopenharmony_ci	dasd_alias_disconnect_device_from_lcu(device);
222762306a36Sopenharmony_ci	private->conf.ned = NULL;
222862306a36Sopenharmony_ci	private->conf.sneq = NULL;
222962306a36Sopenharmony_ci	private->conf.vdsneq = NULL;
223062306a36Sopenharmony_ci	private->conf.gneq = NULL;
223162306a36Sopenharmony_ci	dasd_eckd_clear_conf_data(device);
223262306a36Sopenharmony_ci	dasd_path_remove_kobjects(device);
223362306a36Sopenharmony_ci}
223462306a36Sopenharmony_ci
223562306a36Sopenharmony_cistatic struct dasd_ccw_req *
223662306a36Sopenharmony_cidasd_eckd_analysis_ccw(struct dasd_device *device)
223762306a36Sopenharmony_ci{
223862306a36Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
223962306a36Sopenharmony_ci	struct eckd_count *count_data;
224062306a36Sopenharmony_ci	struct LO_eckd_data *LO_data;
224162306a36Sopenharmony_ci	struct dasd_ccw_req *cqr;
224262306a36Sopenharmony_ci	struct ccw1 *ccw;
224362306a36Sopenharmony_ci	int cplength, datasize;
224462306a36Sopenharmony_ci	int i;
224562306a36Sopenharmony_ci
224662306a36Sopenharmony_ci	cplength = 8;
224762306a36Sopenharmony_ci	datasize = sizeof(struct DE_eckd_data) + 2*sizeof(struct LO_eckd_data);
224862306a36Sopenharmony_ci	cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, cplength, datasize, device,
224962306a36Sopenharmony_ci				   NULL);
225062306a36Sopenharmony_ci	if (IS_ERR(cqr))
225162306a36Sopenharmony_ci		return cqr;
225262306a36Sopenharmony_ci	ccw = cqr->cpaddr;
225362306a36Sopenharmony_ci	/* Define extent for the first 2 tracks. */
225462306a36Sopenharmony_ci	define_extent(ccw++, cqr->data, 0, 1,
225562306a36Sopenharmony_ci		      DASD_ECKD_CCW_READ_COUNT, device, 0);
225662306a36Sopenharmony_ci	LO_data = cqr->data + sizeof(struct DE_eckd_data);
225762306a36Sopenharmony_ci	/* Locate record for the first 4 records on track 0. */
225862306a36Sopenharmony_ci	ccw[-1].flags |= CCW_FLAG_CC;
225962306a36Sopenharmony_ci	locate_record(ccw++, LO_data++, 0, 0, 4,
226062306a36Sopenharmony_ci		      DASD_ECKD_CCW_READ_COUNT, device, 0);
226162306a36Sopenharmony_ci
226262306a36Sopenharmony_ci	count_data = private->count_area;
226362306a36Sopenharmony_ci	for (i = 0; i < 4; i++) {
226462306a36Sopenharmony_ci		ccw[-1].flags |= CCW_FLAG_CC;
226562306a36Sopenharmony_ci		ccw->cmd_code = DASD_ECKD_CCW_READ_COUNT;
226662306a36Sopenharmony_ci		ccw->flags = 0;
226762306a36Sopenharmony_ci		ccw->count = 8;
226862306a36Sopenharmony_ci		ccw->cda = (__u32)virt_to_phys(count_data);
226962306a36Sopenharmony_ci		ccw++;
227062306a36Sopenharmony_ci		count_data++;
227162306a36Sopenharmony_ci	}
227262306a36Sopenharmony_ci
227362306a36Sopenharmony_ci	/* Locate record for the first record on track 1. */
227462306a36Sopenharmony_ci	ccw[-1].flags |= CCW_FLAG_CC;
227562306a36Sopenharmony_ci	locate_record(ccw++, LO_data++, 1, 0, 1,
227662306a36Sopenharmony_ci		      DASD_ECKD_CCW_READ_COUNT, device, 0);
227762306a36Sopenharmony_ci	/* Read count ccw. */
227862306a36Sopenharmony_ci	ccw[-1].flags |= CCW_FLAG_CC;
227962306a36Sopenharmony_ci	ccw->cmd_code = DASD_ECKD_CCW_READ_COUNT;
228062306a36Sopenharmony_ci	ccw->flags = 0;
228162306a36Sopenharmony_ci	ccw->count = 8;
228262306a36Sopenharmony_ci	ccw->cda = (__u32)virt_to_phys(count_data);
228362306a36Sopenharmony_ci
228462306a36Sopenharmony_ci	cqr->block = NULL;
228562306a36Sopenharmony_ci	cqr->startdev = device;
228662306a36Sopenharmony_ci	cqr->memdev = device;
228762306a36Sopenharmony_ci	cqr->retries = 255;
228862306a36Sopenharmony_ci	cqr->buildclk = get_tod_clock();
228962306a36Sopenharmony_ci	cqr->status = DASD_CQR_FILLED;
229062306a36Sopenharmony_ci	/* Set flags to suppress output for expected errors */
229162306a36Sopenharmony_ci	set_bit(DASD_CQR_SUPPRESS_NRF, &cqr->flags);
229262306a36Sopenharmony_ci
229362306a36Sopenharmony_ci	return cqr;
229462306a36Sopenharmony_ci}
229562306a36Sopenharmony_ci
229662306a36Sopenharmony_ci/* differentiate between 'no record found' and any other error */
229762306a36Sopenharmony_cistatic int dasd_eckd_analysis_evaluation(struct dasd_ccw_req *init_cqr)
229862306a36Sopenharmony_ci{
229962306a36Sopenharmony_ci	char *sense;
230062306a36Sopenharmony_ci	if (init_cqr->status == DASD_CQR_DONE)
230162306a36Sopenharmony_ci		return INIT_CQR_OK;
230262306a36Sopenharmony_ci	else if (init_cqr->status == DASD_CQR_NEED_ERP ||
230362306a36Sopenharmony_ci		 init_cqr->status == DASD_CQR_FAILED) {
230462306a36Sopenharmony_ci		sense = dasd_get_sense(&init_cqr->irb);
230562306a36Sopenharmony_ci		if (sense && (sense[1] & SNS1_NO_REC_FOUND))
230662306a36Sopenharmony_ci			return INIT_CQR_UNFORMATTED;
230762306a36Sopenharmony_ci		else
230862306a36Sopenharmony_ci			return INIT_CQR_ERROR;
230962306a36Sopenharmony_ci	} else
231062306a36Sopenharmony_ci		return INIT_CQR_ERROR;
231162306a36Sopenharmony_ci}
231262306a36Sopenharmony_ci
231362306a36Sopenharmony_ci/*
231462306a36Sopenharmony_ci * This is the callback function for the init_analysis cqr. It saves
231562306a36Sopenharmony_ci * the status of the initial analysis ccw before it frees it and kicks
231662306a36Sopenharmony_ci * the device to continue the startup sequence. This will call
231762306a36Sopenharmony_ci * dasd_eckd_do_analysis again (if the devices has not been marked
231862306a36Sopenharmony_ci * for deletion in the meantime).
231962306a36Sopenharmony_ci */
232062306a36Sopenharmony_cistatic void dasd_eckd_analysis_callback(struct dasd_ccw_req *init_cqr,
232162306a36Sopenharmony_ci					void *data)
232262306a36Sopenharmony_ci{
232362306a36Sopenharmony_ci	struct dasd_device *device = init_cqr->startdev;
232462306a36Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
232562306a36Sopenharmony_ci
232662306a36Sopenharmony_ci	private->init_cqr_status = dasd_eckd_analysis_evaluation(init_cqr);
232762306a36Sopenharmony_ci	dasd_sfree_request(init_cqr, device);
232862306a36Sopenharmony_ci	dasd_kick_device(device);
232962306a36Sopenharmony_ci}
233062306a36Sopenharmony_ci
233162306a36Sopenharmony_cistatic int dasd_eckd_start_analysis(struct dasd_block *block)
233262306a36Sopenharmony_ci{
233362306a36Sopenharmony_ci	struct dasd_ccw_req *init_cqr;
233462306a36Sopenharmony_ci
233562306a36Sopenharmony_ci	init_cqr = dasd_eckd_analysis_ccw(block->base);
233662306a36Sopenharmony_ci	if (IS_ERR(init_cqr))
233762306a36Sopenharmony_ci		return PTR_ERR(init_cqr);
233862306a36Sopenharmony_ci	init_cqr->callback = dasd_eckd_analysis_callback;
233962306a36Sopenharmony_ci	init_cqr->callback_data = NULL;
234062306a36Sopenharmony_ci	init_cqr->expires = 5*HZ;
234162306a36Sopenharmony_ci	/* first try without ERP, so we can later handle unformatted
234262306a36Sopenharmony_ci	 * devices as special case
234362306a36Sopenharmony_ci	 */
234462306a36Sopenharmony_ci	clear_bit(DASD_CQR_FLAGS_USE_ERP, &init_cqr->flags);
234562306a36Sopenharmony_ci	init_cqr->retries = 0;
234662306a36Sopenharmony_ci	dasd_add_request_head(init_cqr);
234762306a36Sopenharmony_ci	return -EAGAIN;
234862306a36Sopenharmony_ci}
234962306a36Sopenharmony_ci
235062306a36Sopenharmony_cistatic int dasd_eckd_end_analysis(struct dasd_block *block)
235162306a36Sopenharmony_ci{
235262306a36Sopenharmony_ci	struct dasd_device *device = block->base;
235362306a36Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
235462306a36Sopenharmony_ci	struct eckd_count *count_area;
235562306a36Sopenharmony_ci	unsigned int sb, blk_per_trk;
235662306a36Sopenharmony_ci	int status, i;
235762306a36Sopenharmony_ci	struct dasd_ccw_req *init_cqr;
235862306a36Sopenharmony_ci
235962306a36Sopenharmony_ci	status = private->init_cqr_status;
236062306a36Sopenharmony_ci	private->init_cqr_status = -1;
236162306a36Sopenharmony_ci	if (status == INIT_CQR_ERROR) {
236262306a36Sopenharmony_ci		/* try again, this time with full ERP */
236362306a36Sopenharmony_ci		init_cqr = dasd_eckd_analysis_ccw(device);
236462306a36Sopenharmony_ci		dasd_sleep_on(init_cqr);
236562306a36Sopenharmony_ci		status = dasd_eckd_analysis_evaluation(init_cqr);
236662306a36Sopenharmony_ci		dasd_sfree_request(init_cqr, device);
236762306a36Sopenharmony_ci	}
236862306a36Sopenharmony_ci
236962306a36Sopenharmony_ci	if (device->features & DASD_FEATURE_USERAW) {
237062306a36Sopenharmony_ci		block->bp_block = DASD_RAW_BLOCKSIZE;
237162306a36Sopenharmony_ci		blk_per_trk = DASD_RAW_BLOCK_PER_TRACK;
237262306a36Sopenharmony_ci		block->s2b_shift = 3;
237362306a36Sopenharmony_ci		goto raw;
237462306a36Sopenharmony_ci	}
237562306a36Sopenharmony_ci
237662306a36Sopenharmony_ci	if (status == INIT_CQR_UNFORMATTED) {
237762306a36Sopenharmony_ci		dev_warn(&device->cdev->dev, "The DASD is not formatted\n");
237862306a36Sopenharmony_ci		return -EMEDIUMTYPE;
237962306a36Sopenharmony_ci	} else if (status == INIT_CQR_ERROR) {
238062306a36Sopenharmony_ci		dev_err(&device->cdev->dev,
238162306a36Sopenharmony_ci			"Detecting the DASD disk layout failed because "
238262306a36Sopenharmony_ci			"of an I/O error\n");
238362306a36Sopenharmony_ci		return -EIO;
238462306a36Sopenharmony_ci	}
238562306a36Sopenharmony_ci
238662306a36Sopenharmony_ci	private->uses_cdl = 1;
238762306a36Sopenharmony_ci	/* Check Track 0 for Compatible Disk Layout */
238862306a36Sopenharmony_ci	count_area = NULL;
238962306a36Sopenharmony_ci	for (i = 0; i < 3; i++) {
239062306a36Sopenharmony_ci		if (private->count_area[i].kl != 4 ||
239162306a36Sopenharmony_ci		    private->count_area[i].dl != dasd_eckd_cdl_reclen(i) - 4 ||
239262306a36Sopenharmony_ci		    private->count_area[i].cyl != 0 ||
239362306a36Sopenharmony_ci		    private->count_area[i].head != count_area_head[i] ||
239462306a36Sopenharmony_ci		    private->count_area[i].record != count_area_rec[i]) {
239562306a36Sopenharmony_ci			private->uses_cdl = 0;
239662306a36Sopenharmony_ci			break;
239762306a36Sopenharmony_ci		}
239862306a36Sopenharmony_ci	}
239962306a36Sopenharmony_ci	if (i == 3)
240062306a36Sopenharmony_ci		count_area = &private->count_area[3];
240162306a36Sopenharmony_ci
240262306a36Sopenharmony_ci	if (private->uses_cdl == 0) {
240362306a36Sopenharmony_ci		for (i = 0; i < 5; i++) {
240462306a36Sopenharmony_ci			if ((private->count_area[i].kl != 0) ||
240562306a36Sopenharmony_ci			    (private->count_area[i].dl !=
240662306a36Sopenharmony_ci			     private->count_area[0].dl) ||
240762306a36Sopenharmony_ci			    private->count_area[i].cyl !=  0 ||
240862306a36Sopenharmony_ci			    private->count_area[i].head != count_area_head[i] ||
240962306a36Sopenharmony_ci			    private->count_area[i].record != count_area_rec[i])
241062306a36Sopenharmony_ci				break;
241162306a36Sopenharmony_ci		}
241262306a36Sopenharmony_ci		if (i == 5)
241362306a36Sopenharmony_ci			count_area = &private->count_area[0];
241462306a36Sopenharmony_ci	} else {
241562306a36Sopenharmony_ci		if (private->count_area[3].record == 1)
241662306a36Sopenharmony_ci			dev_warn(&device->cdev->dev,
241762306a36Sopenharmony_ci				 "Track 0 has no records following the VTOC\n");
241862306a36Sopenharmony_ci	}
241962306a36Sopenharmony_ci
242062306a36Sopenharmony_ci	if (count_area != NULL && count_area->kl == 0) {
242162306a36Sopenharmony_ci		/* we found notthing violating our disk layout */
242262306a36Sopenharmony_ci		if (dasd_check_blocksize(count_area->dl) == 0)
242362306a36Sopenharmony_ci			block->bp_block = count_area->dl;
242462306a36Sopenharmony_ci	}
242562306a36Sopenharmony_ci	if (block->bp_block == 0) {
242662306a36Sopenharmony_ci		dev_warn(&device->cdev->dev,
242762306a36Sopenharmony_ci			 "The disk layout of the DASD is not supported\n");
242862306a36Sopenharmony_ci		return -EMEDIUMTYPE;
242962306a36Sopenharmony_ci	}
243062306a36Sopenharmony_ci	block->s2b_shift = 0;	/* bits to shift 512 to get a block */
243162306a36Sopenharmony_ci	for (sb = 512; sb < block->bp_block; sb = sb << 1)
243262306a36Sopenharmony_ci		block->s2b_shift++;
243362306a36Sopenharmony_ci
243462306a36Sopenharmony_ci	blk_per_trk = recs_per_track(&private->rdc_data, 0, block->bp_block);
243562306a36Sopenharmony_ci
243662306a36Sopenharmony_ciraw:
243762306a36Sopenharmony_ci	block->blocks = ((unsigned long) private->real_cyl *
243862306a36Sopenharmony_ci			  private->rdc_data.trk_per_cyl *
243962306a36Sopenharmony_ci			  blk_per_trk);
244062306a36Sopenharmony_ci
244162306a36Sopenharmony_ci	dev_info(&device->cdev->dev,
244262306a36Sopenharmony_ci		 "DASD with %u KB/block, %lu KB total size, %u KB/track, "
244362306a36Sopenharmony_ci		 "%s\n", (block->bp_block >> 10),
244462306a36Sopenharmony_ci		 (((unsigned long) private->real_cyl *
244562306a36Sopenharmony_ci		   private->rdc_data.trk_per_cyl *
244662306a36Sopenharmony_ci		   blk_per_trk * (block->bp_block >> 9)) >> 1),
244762306a36Sopenharmony_ci		 ((blk_per_trk * block->bp_block) >> 10),
244862306a36Sopenharmony_ci		 private->uses_cdl ?
244962306a36Sopenharmony_ci		 "compatible disk layout" : "linux disk layout");
245062306a36Sopenharmony_ci
245162306a36Sopenharmony_ci	return 0;
245262306a36Sopenharmony_ci}
245362306a36Sopenharmony_ci
245462306a36Sopenharmony_cistatic int dasd_eckd_do_analysis(struct dasd_block *block)
245562306a36Sopenharmony_ci{
245662306a36Sopenharmony_ci	struct dasd_eckd_private *private = block->base->private;
245762306a36Sopenharmony_ci
245862306a36Sopenharmony_ci	if (private->init_cqr_status < 0)
245962306a36Sopenharmony_ci		return dasd_eckd_start_analysis(block);
246062306a36Sopenharmony_ci	else
246162306a36Sopenharmony_ci		return dasd_eckd_end_analysis(block);
246262306a36Sopenharmony_ci}
246362306a36Sopenharmony_ci
246462306a36Sopenharmony_cistatic int dasd_eckd_basic_to_ready(struct dasd_device *device)
246562306a36Sopenharmony_ci{
246662306a36Sopenharmony_ci	return dasd_alias_add_device(device);
246762306a36Sopenharmony_ci};
246862306a36Sopenharmony_ci
246962306a36Sopenharmony_cistatic int dasd_eckd_online_to_ready(struct dasd_device *device)
247062306a36Sopenharmony_ci{
247162306a36Sopenharmony_ci	if (cancel_work_sync(&device->reload_device))
247262306a36Sopenharmony_ci		dasd_put_device(device);
247362306a36Sopenharmony_ci	if (cancel_work_sync(&device->kick_validate))
247462306a36Sopenharmony_ci		dasd_put_device(device);
247562306a36Sopenharmony_ci
247662306a36Sopenharmony_ci	return 0;
247762306a36Sopenharmony_ci};
247862306a36Sopenharmony_ci
247962306a36Sopenharmony_cistatic int dasd_eckd_basic_to_known(struct dasd_device *device)
248062306a36Sopenharmony_ci{
248162306a36Sopenharmony_ci	return dasd_alias_remove_device(device);
248262306a36Sopenharmony_ci};
248362306a36Sopenharmony_ci
248462306a36Sopenharmony_cistatic int
248562306a36Sopenharmony_cidasd_eckd_fill_geometry(struct dasd_block *block, struct hd_geometry *geo)
248662306a36Sopenharmony_ci{
248762306a36Sopenharmony_ci	struct dasd_eckd_private *private = block->base->private;
248862306a36Sopenharmony_ci
248962306a36Sopenharmony_ci	if (dasd_check_blocksize(block->bp_block) == 0) {
249062306a36Sopenharmony_ci		geo->sectors = recs_per_track(&private->rdc_data,
249162306a36Sopenharmony_ci					      0, block->bp_block);
249262306a36Sopenharmony_ci	}
249362306a36Sopenharmony_ci	geo->cylinders = private->rdc_data.no_cyl;
249462306a36Sopenharmony_ci	geo->heads = private->rdc_data.trk_per_cyl;
249562306a36Sopenharmony_ci	return 0;
249662306a36Sopenharmony_ci}
249762306a36Sopenharmony_ci
249862306a36Sopenharmony_ci/*
249962306a36Sopenharmony_ci * Build the TCW request for the format check
250062306a36Sopenharmony_ci */
250162306a36Sopenharmony_cistatic struct dasd_ccw_req *
250262306a36Sopenharmony_cidasd_eckd_build_check_tcw(struct dasd_device *base, struct format_data_t *fdata,
250362306a36Sopenharmony_ci			  int enable_pav, struct eckd_count *fmt_buffer,
250462306a36Sopenharmony_ci			  int rpt)
250562306a36Sopenharmony_ci{
250662306a36Sopenharmony_ci	struct dasd_eckd_private *start_priv;
250762306a36Sopenharmony_ci	struct dasd_device *startdev = NULL;
250862306a36Sopenharmony_ci	struct tidaw *last_tidaw = NULL;
250962306a36Sopenharmony_ci	struct dasd_ccw_req *cqr;
251062306a36Sopenharmony_ci	struct itcw *itcw;
251162306a36Sopenharmony_ci	int itcw_size;
251262306a36Sopenharmony_ci	int count;
251362306a36Sopenharmony_ci	int rc;
251462306a36Sopenharmony_ci	int i;
251562306a36Sopenharmony_ci
251662306a36Sopenharmony_ci	if (enable_pav)
251762306a36Sopenharmony_ci		startdev = dasd_alias_get_start_dev(base);
251862306a36Sopenharmony_ci
251962306a36Sopenharmony_ci	if (!startdev)
252062306a36Sopenharmony_ci		startdev = base;
252162306a36Sopenharmony_ci
252262306a36Sopenharmony_ci	start_priv = startdev->private;
252362306a36Sopenharmony_ci
252462306a36Sopenharmony_ci	count = rpt * (fdata->stop_unit - fdata->start_unit + 1);
252562306a36Sopenharmony_ci
252662306a36Sopenharmony_ci	/*
252762306a36Sopenharmony_ci	 * we're adding 'count' amount of tidaw to the itcw.
252862306a36Sopenharmony_ci	 * calculate the corresponding itcw_size
252962306a36Sopenharmony_ci	 */
253062306a36Sopenharmony_ci	itcw_size = itcw_calc_size(0, count, 0);
253162306a36Sopenharmony_ci
253262306a36Sopenharmony_ci	cqr = dasd_fmalloc_request(DASD_ECKD_MAGIC, 0, itcw_size, startdev);
253362306a36Sopenharmony_ci	if (IS_ERR(cqr))
253462306a36Sopenharmony_ci		return cqr;
253562306a36Sopenharmony_ci
253662306a36Sopenharmony_ci	start_priv->count++;
253762306a36Sopenharmony_ci
253862306a36Sopenharmony_ci	itcw = itcw_init(cqr->data, itcw_size, ITCW_OP_READ, 0, count, 0);
253962306a36Sopenharmony_ci	if (IS_ERR(itcw)) {
254062306a36Sopenharmony_ci		rc = -EINVAL;
254162306a36Sopenharmony_ci		goto out_err;
254262306a36Sopenharmony_ci	}
254362306a36Sopenharmony_ci
254462306a36Sopenharmony_ci	cqr->cpaddr = itcw_get_tcw(itcw);
254562306a36Sopenharmony_ci	rc = prepare_itcw(itcw, fdata->start_unit, fdata->stop_unit,
254662306a36Sopenharmony_ci			  DASD_ECKD_CCW_READ_COUNT_MT, base, startdev, 0, count,
254762306a36Sopenharmony_ci			  sizeof(struct eckd_count),
254862306a36Sopenharmony_ci			  count * sizeof(struct eckd_count), 0, rpt);
254962306a36Sopenharmony_ci	if (rc)
255062306a36Sopenharmony_ci		goto out_err;
255162306a36Sopenharmony_ci
255262306a36Sopenharmony_ci	for (i = 0; i < count; i++) {
255362306a36Sopenharmony_ci		last_tidaw = itcw_add_tidaw(itcw, 0, fmt_buffer++,
255462306a36Sopenharmony_ci					    sizeof(struct eckd_count));
255562306a36Sopenharmony_ci		if (IS_ERR(last_tidaw)) {
255662306a36Sopenharmony_ci			rc = -EINVAL;
255762306a36Sopenharmony_ci			goto out_err;
255862306a36Sopenharmony_ci		}
255962306a36Sopenharmony_ci	}
256062306a36Sopenharmony_ci
256162306a36Sopenharmony_ci	last_tidaw->flags |= TIDAW_FLAGS_LAST;
256262306a36Sopenharmony_ci	itcw_finalize(itcw);
256362306a36Sopenharmony_ci
256462306a36Sopenharmony_ci	cqr->cpmode = 1;
256562306a36Sopenharmony_ci	cqr->startdev = startdev;
256662306a36Sopenharmony_ci	cqr->memdev = startdev;
256762306a36Sopenharmony_ci	cqr->basedev = base;
256862306a36Sopenharmony_ci	cqr->retries = startdev->default_retries;
256962306a36Sopenharmony_ci	cqr->expires = startdev->default_expires * HZ;
257062306a36Sopenharmony_ci	cqr->buildclk = get_tod_clock();
257162306a36Sopenharmony_ci	cqr->status = DASD_CQR_FILLED;
257262306a36Sopenharmony_ci	/* Set flags to suppress output for expected errors */
257362306a36Sopenharmony_ci	set_bit(DASD_CQR_SUPPRESS_FP, &cqr->flags);
257462306a36Sopenharmony_ci	set_bit(DASD_CQR_SUPPRESS_IL, &cqr->flags);
257562306a36Sopenharmony_ci
257662306a36Sopenharmony_ci	return cqr;
257762306a36Sopenharmony_ci
257862306a36Sopenharmony_ciout_err:
257962306a36Sopenharmony_ci	dasd_sfree_request(cqr, startdev);
258062306a36Sopenharmony_ci
258162306a36Sopenharmony_ci	return ERR_PTR(rc);
258262306a36Sopenharmony_ci}
258362306a36Sopenharmony_ci
258462306a36Sopenharmony_ci/*
258562306a36Sopenharmony_ci * Build the CCW request for the format check
258662306a36Sopenharmony_ci */
258762306a36Sopenharmony_cistatic struct dasd_ccw_req *
258862306a36Sopenharmony_cidasd_eckd_build_check(struct dasd_device *base, struct format_data_t *fdata,
258962306a36Sopenharmony_ci		      int enable_pav, struct eckd_count *fmt_buffer, int rpt)
259062306a36Sopenharmony_ci{
259162306a36Sopenharmony_ci	struct dasd_eckd_private *start_priv;
259262306a36Sopenharmony_ci	struct dasd_eckd_private *base_priv;
259362306a36Sopenharmony_ci	struct dasd_device *startdev = NULL;
259462306a36Sopenharmony_ci	struct dasd_ccw_req *cqr;
259562306a36Sopenharmony_ci	struct ccw1 *ccw;
259662306a36Sopenharmony_ci	void *data;
259762306a36Sopenharmony_ci	int cplength, datasize;
259862306a36Sopenharmony_ci	int use_prefix;
259962306a36Sopenharmony_ci	int count;
260062306a36Sopenharmony_ci	int i;
260162306a36Sopenharmony_ci
260262306a36Sopenharmony_ci	if (enable_pav)
260362306a36Sopenharmony_ci		startdev = dasd_alias_get_start_dev(base);
260462306a36Sopenharmony_ci
260562306a36Sopenharmony_ci	if (!startdev)
260662306a36Sopenharmony_ci		startdev = base;
260762306a36Sopenharmony_ci
260862306a36Sopenharmony_ci	start_priv = startdev->private;
260962306a36Sopenharmony_ci	base_priv = base->private;
261062306a36Sopenharmony_ci
261162306a36Sopenharmony_ci	count = rpt * (fdata->stop_unit - fdata->start_unit + 1);
261262306a36Sopenharmony_ci
261362306a36Sopenharmony_ci	use_prefix = base_priv->features.feature[8] & 0x01;
261462306a36Sopenharmony_ci
261562306a36Sopenharmony_ci	if (use_prefix) {
261662306a36Sopenharmony_ci		cplength = 1;
261762306a36Sopenharmony_ci		datasize = sizeof(struct PFX_eckd_data);
261862306a36Sopenharmony_ci	} else {
261962306a36Sopenharmony_ci		cplength = 2;
262062306a36Sopenharmony_ci		datasize = sizeof(struct DE_eckd_data) +
262162306a36Sopenharmony_ci			sizeof(struct LO_eckd_data);
262262306a36Sopenharmony_ci	}
262362306a36Sopenharmony_ci	cplength += count;
262462306a36Sopenharmony_ci
262562306a36Sopenharmony_ci	cqr = dasd_fmalloc_request(DASD_ECKD_MAGIC, cplength, datasize, startdev);
262662306a36Sopenharmony_ci	if (IS_ERR(cqr))
262762306a36Sopenharmony_ci		return cqr;
262862306a36Sopenharmony_ci
262962306a36Sopenharmony_ci	start_priv->count++;
263062306a36Sopenharmony_ci	data = cqr->data;
263162306a36Sopenharmony_ci	ccw = cqr->cpaddr;
263262306a36Sopenharmony_ci
263362306a36Sopenharmony_ci	if (use_prefix) {
263462306a36Sopenharmony_ci		prefix_LRE(ccw++, data, fdata->start_unit, fdata->stop_unit,
263562306a36Sopenharmony_ci			   DASD_ECKD_CCW_READ_COUNT, base, startdev, 1, 0,
263662306a36Sopenharmony_ci			   count, 0, 0);
263762306a36Sopenharmony_ci	} else {
263862306a36Sopenharmony_ci		define_extent(ccw++, data, fdata->start_unit, fdata->stop_unit,
263962306a36Sopenharmony_ci			      DASD_ECKD_CCW_READ_COUNT, startdev, 0);
264062306a36Sopenharmony_ci
264162306a36Sopenharmony_ci		data += sizeof(struct DE_eckd_data);
264262306a36Sopenharmony_ci		ccw[-1].flags |= CCW_FLAG_CC;
264362306a36Sopenharmony_ci
264462306a36Sopenharmony_ci		locate_record(ccw++, data, fdata->start_unit, 0, count,
264562306a36Sopenharmony_ci			      DASD_ECKD_CCW_READ_COUNT, base, 0);
264662306a36Sopenharmony_ci	}
264762306a36Sopenharmony_ci
264862306a36Sopenharmony_ci	for (i = 0; i < count; i++) {
264962306a36Sopenharmony_ci		ccw[-1].flags |= CCW_FLAG_CC;
265062306a36Sopenharmony_ci		ccw->cmd_code = DASD_ECKD_CCW_READ_COUNT;
265162306a36Sopenharmony_ci		ccw->flags = CCW_FLAG_SLI;
265262306a36Sopenharmony_ci		ccw->count = 8;
265362306a36Sopenharmony_ci		ccw->cda = (__u32)virt_to_phys(fmt_buffer);
265462306a36Sopenharmony_ci		ccw++;
265562306a36Sopenharmony_ci		fmt_buffer++;
265662306a36Sopenharmony_ci	}
265762306a36Sopenharmony_ci
265862306a36Sopenharmony_ci	cqr->startdev = startdev;
265962306a36Sopenharmony_ci	cqr->memdev = startdev;
266062306a36Sopenharmony_ci	cqr->basedev = base;
266162306a36Sopenharmony_ci	cqr->retries = DASD_RETRIES;
266262306a36Sopenharmony_ci	cqr->expires = startdev->default_expires * HZ;
266362306a36Sopenharmony_ci	cqr->buildclk = get_tod_clock();
266462306a36Sopenharmony_ci	cqr->status = DASD_CQR_FILLED;
266562306a36Sopenharmony_ci	/* Set flags to suppress output for expected errors */
266662306a36Sopenharmony_ci	set_bit(DASD_CQR_SUPPRESS_NRF, &cqr->flags);
266762306a36Sopenharmony_ci
266862306a36Sopenharmony_ci	return cqr;
266962306a36Sopenharmony_ci}
267062306a36Sopenharmony_ci
267162306a36Sopenharmony_cistatic struct dasd_ccw_req *
267262306a36Sopenharmony_cidasd_eckd_build_format(struct dasd_device *base, struct dasd_device *startdev,
267362306a36Sopenharmony_ci		       struct format_data_t *fdata, int enable_pav)
267462306a36Sopenharmony_ci{
267562306a36Sopenharmony_ci	struct dasd_eckd_private *base_priv;
267662306a36Sopenharmony_ci	struct dasd_eckd_private *start_priv;
267762306a36Sopenharmony_ci	struct dasd_ccw_req *fcp;
267862306a36Sopenharmony_ci	struct eckd_count *ect;
267962306a36Sopenharmony_ci	struct ch_t address;
268062306a36Sopenharmony_ci	struct ccw1 *ccw;
268162306a36Sopenharmony_ci	void *data;
268262306a36Sopenharmony_ci	int rpt;
268362306a36Sopenharmony_ci	int cplength, datasize;
268462306a36Sopenharmony_ci	int i, j;
268562306a36Sopenharmony_ci	int intensity = 0;
268662306a36Sopenharmony_ci	int r0_perm;
268762306a36Sopenharmony_ci	int nr_tracks;
268862306a36Sopenharmony_ci	int use_prefix;
268962306a36Sopenharmony_ci
269062306a36Sopenharmony_ci	if (enable_pav)
269162306a36Sopenharmony_ci		startdev = dasd_alias_get_start_dev(base);
269262306a36Sopenharmony_ci
269362306a36Sopenharmony_ci	if (!startdev)
269462306a36Sopenharmony_ci		startdev = base;
269562306a36Sopenharmony_ci
269662306a36Sopenharmony_ci	start_priv = startdev->private;
269762306a36Sopenharmony_ci	base_priv = base->private;
269862306a36Sopenharmony_ci
269962306a36Sopenharmony_ci	rpt = recs_per_track(&base_priv->rdc_data, 0, fdata->blksize);
270062306a36Sopenharmony_ci
270162306a36Sopenharmony_ci	nr_tracks = fdata->stop_unit - fdata->start_unit + 1;
270262306a36Sopenharmony_ci
270362306a36Sopenharmony_ci	/*
270462306a36Sopenharmony_ci	 * fdata->intensity is a bit string that tells us what to do:
270562306a36Sopenharmony_ci	 *   Bit 0: write record zero
270662306a36Sopenharmony_ci	 *   Bit 1: write home address, currently not supported
270762306a36Sopenharmony_ci	 *   Bit 2: invalidate tracks
270862306a36Sopenharmony_ci	 *   Bit 3: use OS/390 compatible disk layout (cdl)
270962306a36Sopenharmony_ci	 *   Bit 4: do not allow storage subsystem to modify record zero
271062306a36Sopenharmony_ci	 * Only some bit combinations do make sense.
271162306a36Sopenharmony_ci	 */
271262306a36Sopenharmony_ci	if (fdata->intensity & 0x10) {
271362306a36Sopenharmony_ci		r0_perm = 0;
271462306a36Sopenharmony_ci		intensity = fdata->intensity & ~0x10;
271562306a36Sopenharmony_ci	} else {
271662306a36Sopenharmony_ci		r0_perm = 1;
271762306a36Sopenharmony_ci		intensity = fdata->intensity;
271862306a36Sopenharmony_ci	}
271962306a36Sopenharmony_ci
272062306a36Sopenharmony_ci	use_prefix = base_priv->features.feature[8] & 0x01;
272162306a36Sopenharmony_ci
272262306a36Sopenharmony_ci	switch (intensity) {
272362306a36Sopenharmony_ci	case 0x00:	/* Normal format */
272462306a36Sopenharmony_ci	case 0x08:	/* Normal format, use cdl. */
272562306a36Sopenharmony_ci		cplength = 2 + (rpt*nr_tracks);
272662306a36Sopenharmony_ci		if (use_prefix)
272762306a36Sopenharmony_ci			datasize = sizeof(struct PFX_eckd_data) +
272862306a36Sopenharmony_ci				sizeof(struct LO_eckd_data) +
272962306a36Sopenharmony_ci				rpt * nr_tracks * sizeof(struct eckd_count);
273062306a36Sopenharmony_ci		else
273162306a36Sopenharmony_ci			datasize = sizeof(struct DE_eckd_data) +
273262306a36Sopenharmony_ci				sizeof(struct LO_eckd_data) +
273362306a36Sopenharmony_ci				rpt * nr_tracks * sizeof(struct eckd_count);
273462306a36Sopenharmony_ci		break;
273562306a36Sopenharmony_ci	case 0x01:	/* Write record zero and format track. */
273662306a36Sopenharmony_ci	case 0x09:	/* Write record zero and format track, use cdl. */
273762306a36Sopenharmony_ci		cplength = 2 + rpt * nr_tracks;
273862306a36Sopenharmony_ci		if (use_prefix)
273962306a36Sopenharmony_ci			datasize = sizeof(struct PFX_eckd_data) +
274062306a36Sopenharmony_ci				sizeof(struct LO_eckd_data) +
274162306a36Sopenharmony_ci				sizeof(struct eckd_count) +
274262306a36Sopenharmony_ci				rpt * nr_tracks * sizeof(struct eckd_count);
274362306a36Sopenharmony_ci		else
274462306a36Sopenharmony_ci			datasize = sizeof(struct DE_eckd_data) +
274562306a36Sopenharmony_ci				sizeof(struct LO_eckd_data) +
274662306a36Sopenharmony_ci				sizeof(struct eckd_count) +
274762306a36Sopenharmony_ci				rpt * nr_tracks * sizeof(struct eckd_count);
274862306a36Sopenharmony_ci		break;
274962306a36Sopenharmony_ci	case 0x04:	/* Invalidate track. */
275062306a36Sopenharmony_ci	case 0x0c:	/* Invalidate track, use cdl. */
275162306a36Sopenharmony_ci		cplength = 3;
275262306a36Sopenharmony_ci		if (use_prefix)
275362306a36Sopenharmony_ci			datasize = sizeof(struct PFX_eckd_data) +
275462306a36Sopenharmony_ci				sizeof(struct LO_eckd_data) +
275562306a36Sopenharmony_ci				sizeof(struct eckd_count);
275662306a36Sopenharmony_ci		else
275762306a36Sopenharmony_ci			datasize = sizeof(struct DE_eckd_data) +
275862306a36Sopenharmony_ci				sizeof(struct LO_eckd_data) +
275962306a36Sopenharmony_ci				sizeof(struct eckd_count);
276062306a36Sopenharmony_ci		break;
276162306a36Sopenharmony_ci	default:
276262306a36Sopenharmony_ci		dev_warn(&startdev->cdev->dev,
276362306a36Sopenharmony_ci			 "An I/O control call used incorrect flags 0x%x\n",
276462306a36Sopenharmony_ci			 fdata->intensity);
276562306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
276662306a36Sopenharmony_ci	}
276762306a36Sopenharmony_ci
276862306a36Sopenharmony_ci	fcp = dasd_fmalloc_request(DASD_ECKD_MAGIC, cplength, datasize, startdev);
276962306a36Sopenharmony_ci	if (IS_ERR(fcp))
277062306a36Sopenharmony_ci		return fcp;
277162306a36Sopenharmony_ci
277262306a36Sopenharmony_ci	start_priv->count++;
277362306a36Sopenharmony_ci	data = fcp->data;
277462306a36Sopenharmony_ci	ccw = fcp->cpaddr;
277562306a36Sopenharmony_ci
277662306a36Sopenharmony_ci	switch (intensity & ~0x08) {
277762306a36Sopenharmony_ci	case 0x00: /* Normal format. */
277862306a36Sopenharmony_ci		if (use_prefix) {
277962306a36Sopenharmony_ci			prefix(ccw++, (struct PFX_eckd_data *) data,
278062306a36Sopenharmony_ci			       fdata->start_unit, fdata->stop_unit,
278162306a36Sopenharmony_ci			       DASD_ECKD_CCW_WRITE_CKD, base, startdev);
278262306a36Sopenharmony_ci			/* grant subsystem permission to format R0 */
278362306a36Sopenharmony_ci			if (r0_perm)
278462306a36Sopenharmony_ci				((struct PFX_eckd_data *)data)
278562306a36Sopenharmony_ci					->define_extent.ga_extended |= 0x04;
278662306a36Sopenharmony_ci			data += sizeof(struct PFX_eckd_data);
278762306a36Sopenharmony_ci		} else {
278862306a36Sopenharmony_ci			define_extent(ccw++, (struct DE_eckd_data *) data,
278962306a36Sopenharmony_ci				      fdata->start_unit, fdata->stop_unit,
279062306a36Sopenharmony_ci				      DASD_ECKD_CCW_WRITE_CKD, startdev, 0);
279162306a36Sopenharmony_ci			/* grant subsystem permission to format R0 */
279262306a36Sopenharmony_ci			if (r0_perm)
279362306a36Sopenharmony_ci				((struct DE_eckd_data *) data)
279462306a36Sopenharmony_ci					->ga_extended |= 0x04;
279562306a36Sopenharmony_ci			data += sizeof(struct DE_eckd_data);
279662306a36Sopenharmony_ci		}
279762306a36Sopenharmony_ci		ccw[-1].flags |= CCW_FLAG_CC;
279862306a36Sopenharmony_ci		locate_record(ccw++, (struct LO_eckd_data *) data,
279962306a36Sopenharmony_ci			      fdata->start_unit, 0, rpt*nr_tracks,
280062306a36Sopenharmony_ci			      DASD_ECKD_CCW_WRITE_CKD, base,
280162306a36Sopenharmony_ci			      fdata->blksize);
280262306a36Sopenharmony_ci		data += sizeof(struct LO_eckd_data);
280362306a36Sopenharmony_ci		break;
280462306a36Sopenharmony_ci	case 0x01: /* Write record zero + format track. */
280562306a36Sopenharmony_ci		if (use_prefix) {
280662306a36Sopenharmony_ci			prefix(ccw++, (struct PFX_eckd_data *) data,
280762306a36Sopenharmony_ci			       fdata->start_unit, fdata->stop_unit,
280862306a36Sopenharmony_ci			       DASD_ECKD_CCW_WRITE_RECORD_ZERO,
280962306a36Sopenharmony_ci			       base, startdev);
281062306a36Sopenharmony_ci			data += sizeof(struct PFX_eckd_data);
281162306a36Sopenharmony_ci		} else {
281262306a36Sopenharmony_ci			define_extent(ccw++, (struct DE_eckd_data *) data,
281362306a36Sopenharmony_ci			       fdata->start_unit, fdata->stop_unit,
281462306a36Sopenharmony_ci			       DASD_ECKD_CCW_WRITE_RECORD_ZERO, startdev, 0);
281562306a36Sopenharmony_ci			data += sizeof(struct DE_eckd_data);
281662306a36Sopenharmony_ci		}
281762306a36Sopenharmony_ci		ccw[-1].flags |= CCW_FLAG_CC;
281862306a36Sopenharmony_ci		locate_record(ccw++, (struct LO_eckd_data *) data,
281962306a36Sopenharmony_ci			      fdata->start_unit, 0, rpt * nr_tracks + 1,
282062306a36Sopenharmony_ci			      DASD_ECKD_CCW_WRITE_RECORD_ZERO, base,
282162306a36Sopenharmony_ci			      base->block->bp_block);
282262306a36Sopenharmony_ci		data += sizeof(struct LO_eckd_data);
282362306a36Sopenharmony_ci		break;
282462306a36Sopenharmony_ci	case 0x04: /* Invalidate track. */
282562306a36Sopenharmony_ci		if (use_prefix) {
282662306a36Sopenharmony_ci			prefix(ccw++, (struct PFX_eckd_data *) data,
282762306a36Sopenharmony_ci			       fdata->start_unit, fdata->stop_unit,
282862306a36Sopenharmony_ci			       DASD_ECKD_CCW_WRITE_CKD, base, startdev);
282962306a36Sopenharmony_ci			data += sizeof(struct PFX_eckd_data);
283062306a36Sopenharmony_ci		} else {
283162306a36Sopenharmony_ci			define_extent(ccw++, (struct DE_eckd_data *) data,
283262306a36Sopenharmony_ci			       fdata->start_unit, fdata->stop_unit,
283362306a36Sopenharmony_ci			       DASD_ECKD_CCW_WRITE_CKD, startdev, 0);
283462306a36Sopenharmony_ci			data += sizeof(struct DE_eckd_data);
283562306a36Sopenharmony_ci		}
283662306a36Sopenharmony_ci		ccw[-1].flags |= CCW_FLAG_CC;
283762306a36Sopenharmony_ci		locate_record(ccw++, (struct LO_eckd_data *) data,
283862306a36Sopenharmony_ci			      fdata->start_unit, 0, 1,
283962306a36Sopenharmony_ci			      DASD_ECKD_CCW_WRITE_CKD, base, 8);
284062306a36Sopenharmony_ci		data += sizeof(struct LO_eckd_data);
284162306a36Sopenharmony_ci		break;
284262306a36Sopenharmony_ci	}
284362306a36Sopenharmony_ci
284462306a36Sopenharmony_ci	for (j = 0; j < nr_tracks; j++) {
284562306a36Sopenharmony_ci		/* calculate cylinder and head for the current track */
284662306a36Sopenharmony_ci		set_ch_t(&address,
284762306a36Sopenharmony_ci			 (fdata->start_unit + j) /
284862306a36Sopenharmony_ci			 base_priv->rdc_data.trk_per_cyl,
284962306a36Sopenharmony_ci			 (fdata->start_unit + j) %
285062306a36Sopenharmony_ci			 base_priv->rdc_data.trk_per_cyl);
285162306a36Sopenharmony_ci		if (intensity & 0x01) {	/* write record zero */
285262306a36Sopenharmony_ci			ect = (struct eckd_count *) data;
285362306a36Sopenharmony_ci			data += sizeof(struct eckd_count);
285462306a36Sopenharmony_ci			ect->cyl = address.cyl;
285562306a36Sopenharmony_ci			ect->head = address.head;
285662306a36Sopenharmony_ci			ect->record = 0;
285762306a36Sopenharmony_ci			ect->kl = 0;
285862306a36Sopenharmony_ci			ect->dl = 8;
285962306a36Sopenharmony_ci			ccw[-1].flags |= CCW_FLAG_CC;
286062306a36Sopenharmony_ci			ccw->cmd_code = DASD_ECKD_CCW_WRITE_RECORD_ZERO;
286162306a36Sopenharmony_ci			ccw->flags = CCW_FLAG_SLI;
286262306a36Sopenharmony_ci			ccw->count = 8;
286362306a36Sopenharmony_ci			ccw->cda = (__u32)virt_to_phys(ect);
286462306a36Sopenharmony_ci			ccw++;
286562306a36Sopenharmony_ci		}
286662306a36Sopenharmony_ci		if ((intensity & ~0x08) & 0x04) {	/* erase track */
286762306a36Sopenharmony_ci			ect = (struct eckd_count *) data;
286862306a36Sopenharmony_ci			data += sizeof(struct eckd_count);
286962306a36Sopenharmony_ci			ect->cyl = address.cyl;
287062306a36Sopenharmony_ci			ect->head = address.head;
287162306a36Sopenharmony_ci			ect->record = 1;
287262306a36Sopenharmony_ci			ect->kl = 0;
287362306a36Sopenharmony_ci			ect->dl = 0;
287462306a36Sopenharmony_ci			ccw[-1].flags |= CCW_FLAG_CC;
287562306a36Sopenharmony_ci			ccw->cmd_code = DASD_ECKD_CCW_WRITE_CKD;
287662306a36Sopenharmony_ci			ccw->flags = CCW_FLAG_SLI;
287762306a36Sopenharmony_ci			ccw->count = 8;
287862306a36Sopenharmony_ci			ccw->cda = (__u32)virt_to_phys(ect);
287962306a36Sopenharmony_ci		} else {		/* write remaining records */
288062306a36Sopenharmony_ci			for (i = 0; i < rpt; i++) {
288162306a36Sopenharmony_ci				ect = (struct eckd_count *) data;
288262306a36Sopenharmony_ci				data += sizeof(struct eckd_count);
288362306a36Sopenharmony_ci				ect->cyl = address.cyl;
288462306a36Sopenharmony_ci				ect->head = address.head;
288562306a36Sopenharmony_ci				ect->record = i + 1;
288662306a36Sopenharmony_ci				ect->kl = 0;
288762306a36Sopenharmony_ci				ect->dl = fdata->blksize;
288862306a36Sopenharmony_ci				/*
288962306a36Sopenharmony_ci				 * Check for special tracks 0-1
289062306a36Sopenharmony_ci				 * when formatting CDL
289162306a36Sopenharmony_ci				 */
289262306a36Sopenharmony_ci				if ((intensity & 0x08) &&
289362306a36Sopenharmony_ci				    address.cyl == 0 && address.head == 0) {
289462306a36Sopenharmony_ci					if (i < 3) {
289562306a36Sopenharmony_ci						ect->kl = 4;
289662306a36Sopenharmony_ci						ect->dl = sizes_trk0[i] - 4;
289762306a36Sopenharmony_ci					}
289862306a36Sopenharmony_ci				}
289962306a36Sopenharmony_ci				if ((intensity & 0x08) &&
290062306a36Sopenharmony_ci				    address.cyl == 0 && address.head == 1) {
290162306a36Sopenharmony_ci					ect->kl = 44;
290262306a36Sopenharmony_ci					ect->dl = LABEL_SIZE - 44;
290362306a36Sopenharmony_ci				}
290462306a36Sopenharmony_ci				ccw[-1].flags |= CCW_FLAG_CC;
290562306a36Sopenharmony_ci				if (i != 0 || j == 0)
290662306a36Sopenharmony_ci					ccw->cmd_code =
290762306a36Sopenharmony_ci						DASD_ECKD_CCW_WRITE_CKD;
290862306a36Sopenharmony_ci				else
290962306a36Sopenharmony_ci					ccw->cmd_code =
291062306a36Sopenharmony_ci						DASD_ECKD_CCW_WRITE_CKD_MT;
291162306a36Sopenharmony_ci				ccw->flags = CCW_FLAG_SLI;
291262306a36Sopenharmony_ci				ccw->count = 8;
291362306a36Sopenharmony_ci				ccw->cda = (__u32)virt_to_phys(ect);
291462306a36Sopenharmony_ci				ccw++;
291562306a36Sopenharmony_ci			}
291662306a36Sopenharmony_ci		}
291762306a36Sopenharmony_ci	}
291862306a36Sopenharmony_ci
291962306a36Sopenharmony_ci	fcp->startdev = startdev;
292062306a36Sopenharmony_ci	fcp->memdev = startdev;
292162306a36Sopenharmony_ci	fcp->basedev = base;
292262306a36Sopenharmony_ci	fcp->retries = 256;
292362306a36Sopenharmony_ci	fcp->expires = startdev->default_expires * HZ;
292462306a36Sopenharmony_ci	fcp->buildclk = get_tod_clock();
292562306a36Sopenharmony_ci	fcp->status = DASD_CQR_FILLED;
292662306a36Sopenharmony_ci
292762306a36Sopenharmony_ci	return fcp;
292862306a36Sopenharmony_ci}
292962306a36Sopenharmony_ci
293062306a36Sopenharmony_ci/*
293162306a36Sopenharmony_ci * Wrapper function to build a CCW request depending on input data
293262306a36Sopenharmony_ci */
293362306a36Sopenharmony_cistatic struct dasd_ccw_req *
293462306a36Sopenharmony_cidasd_eckd_format_build_ccw_req(struct dasd_device *base,
293562306a36Sopenharmony_ci			       struct format_data_t *fdata, int enable_pav,
293662306a36Sopenharmony_ci			       int tpm, struct eckd_count *fmt_buffer, int rpt)
293762306a36Sopenharmony_ci{
293862306a36Sopenharmony_ci	struct dasd_ccw_req *ccw_req;
293962306a36Sopenharmony_ci
294062306a36Sopenharmony_ci	if (!fmt_buffer) {
294162306a36Sopenharmony_ci		ccw_req = dasd_eckd_build_format(base, NULL, fdata, enable_pav);
294262306a36Sopenharmony_ci	} else {
294362306a36Sopenharmony_ci		if (tpm)
294462306a36Sopenharmony_ci			ccw_req = dasd_eckd_build_check_tcw(base, fdata,
294562306a36Sopenharmony_ci							    enable_pav,
294662306a36Sopenharmony_ci							    fmt_buffer, rpt);
294762306a36Sopenharmony_ci		else
294862306a36Sopenharmony_ci			ccw_req = dasd_eckd_build_check(base, fdata, enable_pav,
294962306a36Sopenharmony_ci							fmt_buffer, rpt);
295062306a36Sopenharmony_ci	}
295162306a36Sopenharmony_ci
295262306a36Sopenharmony_ci	return ccw_req;
295362306a36Sopenharmony_ci}
295462306a36Sopenharmony_ci
295562306a36Sopenharmony_ci/*
295662306a36Sopenharmony_ci * Sanity checks on format_data
295762306a36Sopenharmony_ci */
295862306a36Sopenharmony_cistatic int dasd_eckd_format_sanity_checks(struct dasd_device *base,
295962306a36Sopenharmony_ci					  struct format_data_t *fdata)
296062306a36Sopenharmony_ci{
296162306a36Sopenharmony_ci	struct dasd_eckd_private *private = base->private;
296262306a36Sopenharmony_ci
296362306a36Sopenharmony_ci	if (fdata->start_unit >=
296462306a36Sopenharmony_ci	    (private->real_cyl * private->rdc_data.trk_per_cyl)) {
296562306a36Sopenharmony_ci		dev_warn(&base->cdev->dev,
296662306a36Sopenharmony_ci			 "Start track number %u used in formatting is too big\n",
296762306a36Sopenharmony_ci			 fdata->start_unit);
296862306a36Sopenharmony_ci		return -EINVAL;
296962306a36Sopenharmony_ci	}
297062306a36Sopenharmony_ci	if (fdata->stop_unit >=
297162306a36Sopenharmony_ci	    (private->real_cyl * private->rdc_data.trk_per_cyl)) {
297262306a36Sopenharmony_ci		dev_warn(&base->cdev->dev,
297362306a36Sopenharmony_ci			 "Stop track number %u used in formatting is too big\n",
297462306a36Sopenharmony_ci			 fdata->stop_unit);
297562306a36Sopenharmony_ci		return -EINVAL;
297662306a36Sopenharmony_ci	}
297762306a36Sopenharmony_ci	if (fdata->start_unit > fdata->stop_unit) {
297862306a36Sopenharmony_ci		dev_warn(&base->cdev->dev,
297962306a36Sopenharmony_ci			 "Start track %u used in formatting exceeds end track\n",
298062306a36Sopenharmony_ci			 fdata->start_unit);
298162306a36Sopenharmony_ci		return -EINVAL;
298262306a36Sopenharmony_ci	}
298362306a36Sopenharmony_ci	if (dasd_check_blocksize(fdata->blksize) != 0) {
298462306a36Sopenharmony_ci		dev_warn(&base->cdev->dev,
298562306a36Sopenharmony_ci			 "The DASD cannot be formatted with block size %u\n",
298662306a36Sopenharmony_ci			 fdata->blksize);
298762306a36Sopenharmony_ci		return -EINVAL;
298862306a36Sopenharmony_ci	}
298962306a36Sopenharmony_ci	return 0;
299062306a36Sopenharmony_ci}
299162306a36Sopenharmony_ci
299262306a36Sopenharmony_ci/*
299362306a36Sopenharmony_ci * This function will process format_data originally coming from an IOCTL
299462306a36Sopenharmony_ci */
299562306a36Sopenharmony_cistatic int dasd_eckd_format_process_data(struct dasd_device *base,
299662306a36Sopenharmony_ci					 struct format_data_t *fdata,
299762306a36Sopenharmony_ci					 int enable_pav, int tpm,
299862306a36Sopenharmony_ci					 struct eckd_count *fmt_buffer, int rpt,
299962306a36Sopenharmony_ci					 struct irb *irb)
300062306a36Sopenharmony_ci{
300162306a36Sopenharmony_ci	struct dasd_eckd_private *private = base->private;
300262306a36Sopenharmony_ci	struct dasd_ccw_req *cqr, *n;
300362306a36Sopenharmony_ci	struct list_head format_queue;
300462306a36Sopenharmony_ci	struct dasd_device *device;
300562306a36Sopenharmony_ci	char *sense = NULL;
300662306a36Sopenharmony_ci	int old_start, old_stop, format_step;
300762306a36Sopenharmony_ci	int step, retry;
300862306a36Sopenharmony_ci	int rc;
300962306a36Sopenharmony_ci
301062306a36Sopenharmony_ci	rc = dasd_eckd_format_sanity_checks(base, fdata);
301162306a36Sopenharmony_ci	if (rc)
301262306a36Sopenharmony_ci		return rc;
301362306a36Sopenharmony_ci
301462306a36Sopenharmony_ci	INIT_LIST_HEAD(&format_queue);
301562306a36Sopenharmony_ci
301662306a36Sopenharmony_ci	old_start = fdata->start_unit;
301762306a36Sopenharmony_ci	old_stop = fdata->stop_unit;
301862306a36Sopenharmony_ci
301962306a36Sopenharmony_ci	if (!tpm && fmt_buffer != NULL) {
302062306a36Sopenharmony_ci		/* Command Mode / Format Check */
302162306a36Sopenharmony_ci		format_step = 1;
302262306a36Sopenharmony_ci	} else if (tpm && fmt_buffer != NULL) {
302362306a36Sopenharmony_ci		/* Transport Mode / Format Check */
302462306a36Sopenharmony_ci		format_step = DASD_CQR_MAX_CCW / rpt;
302562306a36Sopenharmony_ci	} else {
302662306a36Sopenharmony_ci		/* Normal Formatting */
302762306a36Sopenharmony_ci		format_step = DASD_CQR_MAX_CCW /
302862306a36Sopenharmony_ci			recs_per_track(&private->rdc_data, 0, fdata->blksize);
302962306a36Sopenharmony_ci	}
303062306a36Sopenharmony_ci
303162306a36Sopenharmony_ci	do {
303262306a36Sopenharmony_ci		retry = 0;
303362306a36Sopenharmony_ci		while (fdata->start_unit <= old_stop) {
303462306a36Sopenharmony_ci			step = fdata->stop_unit - fdata->start_unit + 1;
303562306a36Sopenharmony_ci			if (step > format_step) {
303662306a36Sopenharmony_ci				fdata->stop_unit =
303762306a36Sopenharmony_ci					fdata->start_unit + format_step - 1;
303862306a36Sopenharmony_ci			}
303962306a36Sopenharmony_ci
304062306a36Sopenharmony_ci			cqr = dasd_eckd_format_build_ccw_req(base, fdata,
304162306a36Sopenharmony_ci							     enable_pav, tpm,
304262306a36Sopenharmony_ci							     fmt_buffer, rpt);
304362306a36Sopenharmony_ci			if (IS_ERR(cqr)) {
304462306a36Sopenharmony_ci				rc = PTR_ERR(cqr);
304562306a36Sopenharmony_ci				if (rc == -ENOMEM) {
304662306a36Sopenharmony_ci					if (list_empty(&format_queue))
304762306a36Sopenharmony_ci						goto out;
304862306a36Sopenharmony_ci					/*
304962306a36Sopenharmony_ci					 * not enough memory available, start
305062306a36Sopenharmony_ci					 * requests retry after first requests
305162306a36Sopenharmony_ci					 * were finished
305262306a36Sopenharmony_ci					 */
305362306a36Sopenharmony_ci					retry = 1;
305462306a36Sopenharmony_ci					break;
305562306a36Sopenharmony_ci				}
305662306a36Sopenharmony_ci				goto out_err;
305762306a36Sopenharmony_ci			}
305862306a36Sopenharmony_ci			list_add_tail(&cqr->blocklist, &format_queue);
305962306a36Sopenharmony_ci
306062306a36Sopenharmony_ci			if (fmt_buffer) {
306162306a36Sopenharmony_ci				step = fdata->stop_unit - fdata->start_unit + 1;
306262306a36Sopenharmony_ci				fmt_buffer += rpt * step;
306362306a36Sopenharmony_ci			}
306462306a36Sopenharmony_ci			fdata->start_unit = fdata->stop_unit + 1;
306562306a36Sopenharmony_ci			fdata->stop_unit = old_stop;
306662306a36Sopenharmony_ci		}
306762306a36Sopenharmony_ci
306862306a36Sopenharmony_ci		rc = dasd_sleep_on_queue(&format_queue);
306962306a36Sopenharmony_ci
307062306a36Sopenharmony_ciout_err:
307162306a36Sopenharmony_ci		list_for_each_entry_safe(cqr, n, &format_queue, blocklist) {
307262306a36Sopenharmony_ci			device = cqr->startdev;
307362306a36Sopenharmony_ci			private = device->private;
307462306a36Sopenharmony_ci
307562306a36Sopenharmony_ci			if (cqr->status == DASD_CQR_FAILED) {
307662306a36Sopenharmony_ci				/*
307762306a36Sopenharmony_ci				 * Only get sense data if called by format
307862306a36Sopenharmony_ci				 * check
307962306a36Sopenharmony_ci				 */
308062306a36Sopenharmony_ci				if (fmt_buffer && irb) {
308162306a36Sopenharmony_ci					sense = dasd_get_sense(&cqr->irb);
308262306a36Sopenharmony_ci					memcpy(irb, &cqr->irb, sizeof(*irb));
308362306a36Sopenharmony_ci				}
308462306a36Sopenharmony_ci				rc = -EIO;
308562306a36Sopenharmony_ci			}
308662306a36Sopenharmony_ci			list_del_init(&cqr->blocklist);
308762306a36Sopenharmony_ci			dasd_ffree_request(cqr, device);
308862306a36Sopenharmony_ci			private->count--;
308962306a36Sopenharmony_ci		}
309062306a36Sopenharmony_ci
309162306a36Sopenharmony_ci		if (rc && rc != -EIO)
309262306a36Sopenharmony_ci			goto out;
309362306a36Sopenharmony_ci		if (rc == -EIO) {
309462306a36Sopenharmony_ci			/*
309562306a36Sopenharmony_ci			 * In case fewer than the expected records are on the
309662306a36Sopenharmony_ci			 * track, we will most likely get a 'No Record Found'
309762306a36Sopenharmony_ci			 * error (in command mode) or a 'File Protected' error
309862306a36Sopenharmony_ci			 * (in transport mode). Those particular cases shouldn't
309962306a36Sopenharmony_ci			 * pass the -EIO to the IOCTL, therefore reset the rc
310062306a36Sopenharmony_ci			 * and continue.
310162306a36Sopenharmony_ci			 */
310262306a36Sopenharmony_ci			if (sense &&
310362306a36Sopenharmony_ci			    (sense[1] & SNS1_NO_REC_FOUND ||
310462306a36Sopenharmony_ci			     sense[1] & SNS1_FILE_PROTECTED))
310562306a36Sopenharmony_ci				retry = 1;
310662306a36Sopenharmony_ci			else
310762306a36Sopenharmony_ci				goto out;
310862306a36Sopenharmony_ci		}
310962306a36Sopenharmony_ci
311062306a36Sopenharmony_ci	} while (retry);
311162306a36Sopenharmony_ci
311262306a36Sopenharmony_ciout:
311362306a36Sopenharmony_ci	fdata->start_unit = old_start;
311462306a36Sopenharmony_ci	fdata->stop_unit = old_stop;
311562306a36Sopenharmony_ci
311662306a36Sopenharmony_ci	return rc;
311762306a36Sopenharmony_ci}
311862306a36Sopenharmony_ci
311962306a36Sopenharmony_cistatic int dasd_eckd_format_device(struct dasd_device *base,
312062306a36Sopenharmony_ci				   struct format_data_t *fdata, int enable_pav)
312162306a36Sopenharmony_ci{
312262306a36Sopenharmony_ci	return dasd_eckd_format_process_data(base, fdata, enable_pav, 0, NULL,
312362306a36Sopenharmony_ci					     0, NULL);
312462306a36Sopenharmony_ci}
312562306a36Sopenharmony_ci
312662306a36Sopenharmony_cistatic bool test_and_set_format_track(struct dasd_format_entry *to_format,
312762306a36Sopenharmony_ci				      struct dasd_ccw_req *cqr)
312862306a36Sopenharmony_ci{
312962306a36Sopenharmony_ci	struct dasd_block *block = cqr->block;
313062306a36Sopenharmony_ci	struct dasd_format_entry *format;
313162306a36Sopenharmony_ci	unsigned long flags;
313262306a36Sopenharmony_ci	bool rc = false;
313362306a36Sopenharmony_ci
313462306a36Sopenharmony_ci	spin_lock_irqsave(&block->format_lock, flags);
313562306a36Sopenharmony_ci	if (cqr->trkcount != atomic_read(&block->trkcount)) {
313662306a36Sopenharmony_ci		/*
313762306a36Sopenharmony_ci		 * The number of formatted tracks has changed after request
313862306a36Sopenharmony_ci		 * start and we can not tell if the current track was involved.
313962306a36Sopenharmony_ci		 * To avoid data corruption treat it as if the current track is
314062306a36Sopenharmony_ci		 * involved
314162306a36Sopenharmony_ci		 */
314262306a36Sopenharmony_ci		rc = true;
314362306a36Sopenharmony_ci		goto out;
314462306a36Sopenharmony_ci	}
314562306a36Sopenharmony_ci	list_for_each_entry(format, &block->format_list, list) {
314662306a36Sopenharmony_ci		if (format->track == to_format->track) {
314762306a36Sopenharmony_ci			rc = true;
314862306a36Sopenharmony_ci			goto out;
314962306a36Sopenharmony_ci		}
315062306a36Sopenharmony_ci	}
315162306a36Sopenharmony_ci	list_add_tail(&to_format->list, &block->format_list);
315262306a36Sopenharmony_ci
315362306a36Sopenharmony_ciout:
315462306a36Sopenharmony_ci	spin_unlock_irqrestore(&block->format_lock, flags);
315562306a36Sopenharmony_ci	return rc;
315662306a36Sopenharmony_ci}
315762306a36Sopenharmony_ci
315862306a36Sopenharmony_cistatic void clear_format_track(struct dasd_format_entry *format,
315962306a36Sopenharmony_ci			      struct dasd_block *block)
316062306a36Sopenharmony_ci{
316162306a36Sopenharmony_ci	unsigned long flags;
316262306a36Sopenharmony_ci
316362306a36Sopenharmony_ci	spin_lock_irqsave(&block->format_lock, flags);
316462306a36Sopenharmony_ci	atomic_inc(&block->trkcount);
316562306a36Sopenharmony_ci	list_del_init(&format->list);
316662306a36Sopenharmony_ci	spin_unlock_irqrestore(&block->format_lock, flags);
316762306a36Sopenharmony_ci}
316862306a36Sopenharmony_ci
316962306a36Sopenharmony_ci/*
317062306a36Sopenharmony_ci * Callback function to free ESE format requests.
317162306a36Sopenharmony_ci */
317262306a36Sopenharmony_cistatic void dasd_eckd_ese_format_cb(struct dasd_ccw_req *cqr, void *data)
317362306a36Sopenharmony_ci{
317462306a36Sopenharmony_ci	struct dasd_device *device = cqr->startdev;
317562306a36Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
317662306a36Sopenharmony_ci	struct dasd_format_entry *format = data;
317762306a36Sopenharmony_ci
317862306a36Sopenharmony_ci	clear_format_track(format, cqr->basedev->block);
317962306a36Sopenharmony_ci	private->count--;
318062306a36Sopenharmony_ci	dasd_ffree_request(cqr, device);
318162306a36Sopenharmony_ci}
318262306a36Sopenharmony_ci
318362306a36Sopenharmony_cistatic struct dasd_ccw_req *
318462306a36Sopenharmony_cidasd_eckd_ese_format(struct dasd_device *startdev, struct dasd_ccw_req *cqr,
318562306a36Sopenharmony_ci		     struct irb *irb)
318662306a36Sopenharmony_ci{
318762306a36Sopenharmony_ci	struct dasd_eckd_private *private;
318862306a36Sopenharmony_ci	struct dasd_format_entry *format;
318962306a36Sopenharmony_ci	struct format_data_t fdata;
319062306a36Sopenharmony_ci	unsigned int recs_per_trk;
319162306a36Sopenharmony_ci	struct dasd_ccw_req *fcqr;
319262306a36Sopenharmony_ci	struct dasd_device *base;
319362306a36Sopenharmony_ci	struct dasd_block *block;
319462306a36Sopenharmony_ci	unsigned int blksize;
319562306a36Sopenharmony_ci	struct request *req;
319662306a36Sopenharmony_ci	sector_t first_trk;
319762306a36Sopenharmony_ci	sector_t last_trk;
319862306a36Sopenharmony_ci	sector_t curr_trk;
319962306a36Sopenharmony_ci	int rc;
320062306a36Sopenharmony_ci
320162306a36Sopenharmony_ci	req = dasd_get_callback_data(cqr);
320262306a36Sopenharmony_ci	block = cqr->block;
320362306a36Sopenharmony_ci	base = block->base;
320462306a36Sopenharmony_ci	private = base->private;
320562306a36Sopenharmony_ci	blksize = block->bp_block;
320662306a36Sopenharmony_ci	recs_per_trk = recs_per_track(&private->rdc_data, 0, blksize);
320762306a36Sopenharmony_ci	format = &startdev->format_entry;
320862306a36Sopenharmony_ci
320962306a36Sopenharmony_ci	first_trk = blk_rq_pos(req) >> block->s2b_shift;
321062306a36Sopenharmony_ci	sector_div(first_trk, recs_per_trk);
321162306a36Sopenharmony_ci	last_trk =
321262306a36Sopenharmony_ci		(blk_rq_pos(req) + blk_rq_sectors(req) - 1) >> block->s2b_shift;
321362306a36Sopenharmony_ci	sector_div(last_trk, recs_per_trk);
321462306a36Sopenharmony_ci	rc = dasd_eckd_track_from_irb(irb, base, &curr_trk);
321562306a36Sopenharmony_ci	if (rc)
321662306a36Sopenharmony_ci		return ERR_PTR(rc);
321762306a36Sopenharmony_ci
321862306a36Sopenharmony_ci	if (curr_trk < first_trk || curr_trk > last_trk) {
321962306a36Sopenharmony_ci		DBF_DEV_EVENT(DBF_WARNING, startdev,
322062306a36Sopenharmony_ci			      "ESE error track %llu not within range %llu - %llu\n",
322162306a36Sopenharmony_ci			      curr_trk, first_trk, last_trk);
322262306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
322362306a36Sopenharmony_ci	}
322462306a36Sopenharmony_ci	format->track = curr_trk;
322562306a36Sopenharmony_ci	/* test if track is already in formatting by another thread */
322662306a36Sopenharmony_ci	if (test_and_set_format_track(format, cqr)) {
322762306a36Sopenharmony_ci		/* this is no real error so do not count down retries */
322862306a36Sopenharmony_ci		cqr->retries++;
322962306a36Sopenharmony_ci		return ERR_PTR(-EEXIST);
323062306a36Sopenharmony_ci	}
323162306a36Sopenharmony_ci
323262306a36Sopenharmony_ci	fdata.start_unit = curr_trk;
323362306a36Sopenharmony_ci	fdata.stop_unit = curr_trk;
323462306a36Sopenharmony_ci	fdata.blksize = blksize;
323562306a36Sopenharmony_ci	fdata.intensity = private->uses_cdl ? DASD_FMT_INT_COMPAT : 0;
323662306a36Sopenharmony_ci
323762306a36Sopenharmony_ci	rc = dasd_eckd_format_sanity_checks(base, &fdata);
323862306a36Sopenharmony_ci	if (rc)
323962306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
324062306a36Sopenharmony_ci
324162306a36Sopenharmony_ci	/*
324262306a36Sopenharmony_ci	 * We're building the request with PAV disabled as we're reusing
324362306a36Sopenharmony_ci	 * the former startdev.
324462306a36Sopenharmony_ci	 */
324562306a36Sopenharmony_ci	fcqr = dasd_eckd_build_format(base, startdev, &fdata, 0);
324662306a36Sopenharmony_ci	if (IS_ERR(fcqr))
324762306a36Sopenharmony_ci		return fcqr;
324862306a36Sopenharmony_ci
324962306a36Sopenharmony_ci	fcqr->callback = dasd_eckd_ese_format_cb;
325062306a36Sopenharmony_ci	fcqr->callback_data = (void *) format;
325162306a36Sopenharmony_ci
325262306a36Sopenharmony_ci	return fcqr;
325362306a36Sopenharmony_ci}
325462306a36Sopenharmony_ci
325562306a36Sopenharmony_ci/*
325662306a36Sopenharmony_ci * When data is read from an unformatted area of an ESE volume, this function
325762306a36Sopenharmony_ci * returns zeroed data and thereby mimics a read of zero data.
325862306a36Sopenharmony_ci *
325962306a36Sopenharmony_ci * The first unformatted track is the one that got the NRF error, the address is
326062306a36Sopenharmony_ci * encoded in the sense data.
326162306a36Sopenharmony_ci *
326262306a36Sopenharmony_ci * All tracks before have returned valid data and should not be touched.
326362306a36Sopenharmony_ci * All tracks after the unformatted track might be formatted or not. This is
326462306a36Sopenharmony_ci * currently not known, remember the processed data and return the remainder of
326562306a36Sopenharmony_ci * the request to the blocklayer in __dasd_cleanup_cqr().
326662306a36Sopenharmony_ci */
326762306a36Sopenharmony_cistatic int dasd_eckd_ese_read(struct dasd_ccw_req *cqr, struct irb *irb)
326862306a36Sopenharmony_ci{
326962306a36Sopenharmony_ci	struct dasd_eckd_private *private;
327062306a36Sopenharmony_ci	sector_t first_trk, last_trk;
327162306a36Sopenharmony_ci	sector_t first_blk, last_blk;
327262306a36Sopenharmony_ci	unsigned int blksize, off;
327362306a36Sopenharmony_ci	unsigned int recs_per_trk;
327462306a36Sopenharmony_ci	struct dasd_device *base;
327562306a36Sopenharmony_ci	struct req_iterator iter;
327662306a36Sopenharmony_ci	struct dasd_block *block;
327762306a36Sopenharmony_ci	unsigned int skip_block;
327862306a36Sopenharmony_ci	unsigned int blk_count;
327962306a36Sopenharmony_ci	struct request *req;
328062306a36Sopenharmony_ci	struct bio_vec bv;
328162306a36Sopenharmony_ci	sector_t curr_trk;
328262306a36Sopenharmony_ci	sector_t end_blk;
328362306a36Sopenharmony_ci	char *dst;
328462306a36Sopenharmony_ci	int rc;
328562306a36Sopenharmony_ci
328662306a36Sopenharmony_ci	req = (struct request *) cqr->callback_data;
328762306a36Sopenharmony_ci	base = cqr->block->base;
328862306a36Sopenharmony_ci	blksize = base->block->bp_block;
328962306a36Sopenharmony_ci	block =  cqr->block;
329062306a36Sopenharmony_ci	private = base->private;
329162306a36Sopenharmony_ci	skip_block = 0;
329262306a36Sopenharmony_ci	blk_count = 0;
329362306a36Sopenharmony_ci
329462306a36Sopenharmony_ci	recs_per_trk = recs_per_track(&private->rdc_data, 0, blksize);
329562306a36Sopenharmony_ci	first_trk = first_blk = blk_rq_pos(req) >> block->s2b_shift;
329662306a36Sopenharmony_ci	sector_div(first_trk, recs_per_trk);
329762306a36Sopenharmony_ci	last_trk = last_blk =
329862306a36Sopenharmony_ci		(blk_rq_pos(req) + blk_rq_sectors(req) - 1) >> block->s2b_shift;
329962306a36Sopenharmony_ci	sector_div(last_trk, recs_per_trk);
330062306a36Sopenharmony_ci	rc = dasd_eckd_track_from_irb(irb, base, &curr_trk);
330162306a36Sopenharmony_ci	if (rc)
330262306a36Sopenharmony_ci		return rc;
330362306a36Sopenharmony_ci
330462306a36Sopenharmony_ci	/* sanity check if the current track from sense data is valid */
330562306a36Sopenharmony_ci	if (curr_trk < first_trk || curr_trk > last_trk) {
330662306a36Sopenharmony_ci		DBF_DEV_EVENT(DBF_WARNING, base,
330762306a36Sopenharmony_ci			      "ESE error track %llu not within range %llu - %llu\n",
330862306a36Sopenharmony_ci			      curr_trk, first_trk, last_trk);
330962306a36Sopenharmony_ci		return -EINVAL;
331062306a36Sopenharmony_ci	}
331162306a36Sopenharmony_ci
331262306a36Sopenharmony_ci	/*
331362306a36Sopenharmony_ci	 * if not the first track got the NRF error we have to skip over valid
331462306a36Sopenharmony_ci	 * blocks
331562306a36Sopenharmony_ci	 */
331662306a36Sopenharmony_ci	if (curr_trk != first_trk)
331762306a36Sopenharmony_ci		skip_block = curr_trk * recs_per_trk - first_blk;
331862306a36Sopenharmony_ci
331962306a36Sopenharmony_ci	/* we have no information beyond the current track */
332062306a36Sopenharmony_ci	end_blk = (curr_trk + 1) * recs_per_trk;
332162306a36Sopenharmony_ci
332262306a36Sopenharmony_ci	rq_for_each_segment(bv, req, iter) {
332362306a36Sopenharmony_ci		dst = bvec_virt(&bv);
332462306a36Sopenharmony_ci		for (off = 0; off < bv.bv_len; off += blksize) {
332562306a36Sopenharmony_ci			if (first_blk + blk_count >= end_blk) {
332662306a36Sopenharmony_ci				cqr->proc_bytes = blk_count * blksize;
332762306a36Sopenharmony_ci				return 0;
332862306a36Sopenharmony_ci			}
332962306a36Sopenharmony_ci			if (dst && !skip_block)
333062306a36Sopenharmony_ci				memset(dst, 0, blksize);
333162306a36Sopenharmony_ci			else
333262306a36Sopenharmony_ci				skip_block--;
333362306a36Sopenharmony_ci			dst += blksize;
333462306a36Sopenharmony_ci			blk_count++;
333562306a36Sopenharmony_ci		}
333662306a36Sopenharmony_ci	}
333762306a36Sopenharmony_ci	return 0;
333862306a36Sopenharmony_ci}
333962306a36Sopenharmony_ci
334062306a36Sopenharmony_ci/*
334162306a36Sopenharmony_ci * Helper function to count consecutive records of a single track.
334262306a36Sopenharmony_ci */
334362306a36Sopenharmony_cistatic int dasd_eckd_count_records(struct eckd_count *fmt_buffer, int start,
334462306a36Sopenharmony_ci				   int max)
334562306a36Sopenharmony_ci{
334662306a36Sopenharmony_ci	int head;
334762306a36Sopenharmony_ci	int i;
334862306a36Sopenharmony_ci
334962306a36Sopenharmony_ci	head = fmt_buffer[start].head;
335062306a36Sopenharmony_ci
335162306a36Sopenharmony_ci	/*
335262306a36Sopenharmony_ci	 * There are 3 conditions where we stop counting:
335362306a36Sopenharmony_ci	 * - if data reoccurs (same head and record may reoccur), which may
335462306a36Sopenharmony_ci	 *   happen due to the way DASD_ECKD_CCW_READ_COUNT works
335562306a36Sopenharmony_ci	 * - when the head changes, because we're iterating over several tracks
335662306a36Sopenharmony_ci	 *   then (DASD_ECKD_CCW_READ_COUNT_MT)
335762306a36Sopenharmony_ci	 * - when we've reached the end of sensible data in the buffer (the
335862306a36Sopenharmony_ci	 *   record will be 0 then)
335962306a36Sopenharmony_ci	 */
336062306a36Sopenharmony_ci	for (i = start; i < max; i++) {
336162306a36Sopenharmony_ci		if (i > start) {
336262306a36Sopenharmony_ci			if ((fmt_buffer[i].head == head &&
336362306a36Sopenharmony_ci			    fmt_buffer[i].record == 1) ||
336462306a36Sopenharmony_ci			    fmt_buffer[i].head != head ||
336562306a36Sopenharmony_ci			    fmt_buffer[i].record == 0)
336662306a36Sopenharmony_ci				break;
336762306a36Sopenharmony_ci		}
336862306a36Sopenharmony_ci	}
336962306a36Sopenharmony_ci
337062306a36Sopenharmony_ci	return i - start;
337162306a36Sopenharmony_ci}
337262306a36Sopenharmony_ci
337362306a36Sopenharmony_ci/*
337462306a36Sopenharmony_ci * Evaluate a given range of tracks. Data like number of records, blocksize,
337562306a36Sopenharmony_ci * record ids, and key length are compared with expected data.
337662306a36Sopenharmony_ci *
337762306a36Sopenharmony_ci * If a mismatch occurs, the corresponding error bit is set, as well as
337862306a36Sopenharmony_ci * additional information, depending on the error.
337962306a36Sopenharmony_ci */
338062306a36Sopenharmony_cistatic void dasd_eckd_format_evaluate_tracks(struct eckd_count *fmt_buffer,
338162306a36Sopenharmony_ci					     struct format_check_t *cdata,
338262306a36Sopenharmony_ci					     int rpt_max, int rpt_exp,
338362306a36Sopenharmony_ci					     int trk_per_cyl, int tpm)
338462306a36Sopenharmony_ci{
338562306a36Sopenharmony_ci	struct ch_t geo;
338662306a36Sopenharmony_ci	int max_entries;
338762306a36Sopenharmony_ci	int count = 0;
338862306a36Sopenharmony_ci	int trkcount;
338962306a36Sopenharmony_ci	int blksize;
339062306a36Sopenharmony_ci	int pos = 0;
339162306a36Sopenharmony_ci	int i, j;
339262306a36Sopenharmony_ci	int kl;
339362306a36Sopenharmony_ci
339462306a36Sopenharmony_ci	trkcount = cdata->expect.stop_unit - cdata->expect.start_unit + 1;
339562306a36Sopenharmony_ci	max_entries = trkcount * rpt_max;
339662306a36Sopenharmony_ci
339762306a36Sopenharmony_ci	for (i = cdata->expect.start_unit; i <= cdata->expect.stop_unit; i++) {
339862306a36Sopenharmony_ci		/* Calculate the correct next starting position in the buffer */
339962306a36Sopenharmony_ci		if (tpm) {
340062306a36Sopenharmony_ci			while (fmt_buffer[pos].record == 0 &&
340162306a36Sopenharmony_ci			       fmt_buffer[pos].dl == 0) {
340262306a36Sopenharmony_ci				if (pos++ > max_entries)
340362306a36Sopenharmony_ci					break;
340462306a36Sopenharmony_ci			}
340562306a36Sopenharmony_ci		} else {
340662306a36Sopenharmony_ci			if (i != cdata->expect.start_unit)
340762306a36Sopenharmony_ci				pos += rpt_max - count;
340862306a36Sopenharmony_ci		}
340962306a36Sopenharmony_ci
341062306a36Sopenharmony_ci		/* Calculate the expected geo values for the current track */
341162306a36Sopenharmony_ci		set_ch_t(&geo, i / trk_per_cyl, i % trk_per_cyl);
341262306a36Sopenharmony_ci
341362306a36Sopenharmony_ci		/* Count and check number of records */
341462306a36Sopenharmony_ci		count = dasd_eckd_count_records(fmt_buffer, pos, pos + rpt_max);
341562306a36Sopenharmony_ci
341662306a36Sopenharmony_ci		if (count < rpt_exp) {
341762306a36Sopenharmony_ci			cdata->result = DASD_FMT_ERR_TOO_FEW_RECORDS;
341862306a36Sopenharmony_ci			break;
341962306a36Sopenharmony_ci		}
342062306a36Sopenharmony_ci		if (count > rpt_exp) {
342162306a36Sopenharmony_ci			cdata->result = DASD_FMT_ERR_TOO_MANY_RECORDS;
342262306a36Sopenharmony_ci			break;
342362306a36Sopenharmony_ci		}
342462306a36Sopenharmony_ci
342562306a36Sopenharmony_ci		for (j = 0; j < count; j++, pos++) {
342662306a36Sopenharmony_ci			blksize = cdata->expect.blksize;
342762306a36Sopenharmony_ci			kl = 0;
342862306a36Sopenharmony_ci
342962306a36Sopenharmony_ci			/*
343062306a36Sopenharmony_ci			 * Set special values when checking CDL formatted
343162306a36Sopenharmony_ci			 * devices.
343262306a36Sopenharmony_ci			 */
343362306a36Sopenharmony_ci			if ((cdata->expect.intensity & 0x08) &&
343462306a36Sopenharmony_ci			    geo.cyl == 0 && geo.head == 0) {
343562306a36Sopenharmony_ci				if (j < 3) {
343662306a36Sopenharmony_ci					blksize = sizes_trk0[j] - 4;
343762306a36Sopenharmony_ci					kl = 4;
343862306a36Sopenharmony_ci				}
343962306a36Sopenharmony_ci			}
344062306a36Sopenharmony_ci			if ((cdata->expect.intensity & 0x08) &&
344162306a36Sopenharmony_ci			    geo.cyl == 0 && geo.head == 1) {
344262306a36Sopenharmony_ci				blksize = LABEL_SIZE - 44;
344362306a36Sopenharmony_ci				kl = 44;
344462306a36Sopenharmony_ci			}
344562306a36Sopenharmony_ci
344662306a36Sopenharmony_ci			/* Check blocksize */
344762306a36Sopenharmony_ci			if (fmt_buffer[pos].dl != blksize) {
344862306a36Sopenharmony_ci				cdata->result = DASD_FMT_ERR_BLKSIZE;
344962306a36Sopenharmony_ci				goto out;
345062306a36Sopenharmony_ci			}
345162306a36Sopenharmony_ci			/* Check if key length is 0 */
345262306a36Sopenharmony_ci			if (fmt_buffer[pos].kl != kl) {
345362306a36Sopenharmony_ci				cdata->result = DASD_FMT_ERR_KEY_LENGTH;
345462306a36Sopenharmony_ci				goto out;
345562306a36Sopenharmony_ci			}
345662306a36Sopenharmony_ci			/* Check if record_id is correct */
345762306a36Sopenharmony_ci			if (fmt_buffer[pos].cyl != geo.cyl ||
345862306a36Sopenharmony_ci			    fmt_buffer[pos].head != geo.head ||
345962306a36Sopenharmony_ci			    fmt_buffer[pos].record != (j + 1)) {
346062306a36Sopenharmony_ci				cdata->result = DASD_FMT_ERR_RECORD_ID;
346162306a36Sopenharmony_ci				goto out;
346262306a36Sopenharmony_ci			}
346362306a36Sopenharmony_ci		}
346462306a36Sopenharmony_ci	}
346562306a36Sopenharmony_ci
346662306a36Sopenharmony_ciout:
346762306a36Sopenharmony_ci	/*
346862306a36Sopenharmony_ci	 * In case of no errors, we need to decrease by one
346962306a36Sopenharmony_ci	 * to get the correct positions.
347062306a36Sopenharmony_ci	 */
347162306a36Sopenharmony_ci	if (!cdata->result) {
347262306a36Sopenharmony_ci		i--;
347362306a36Sopenharmony_ci		pos--;
347462306a36Sopenharmony_ci	}
347562306a36Sopenharmony_ci
347662306a36Sopenharmony_ci	cdata->unit = i;
347762306a36Sopenharmony_ci	cdata->num_records = count;
347862306a36Sopenharmony_ci	cdata->rec = fmt_buffer[pos].record;
347962306a36Sopenharmony_ci	cdata->blksize = fmt_buffer[pos].dl;
348062306a36Sopenharmony_ci	cdata->key_length = fmt_buffer[pos].kl;
348162306a36Sopenharmony_ci}
348262306a36Sopenharmony_ci
348362306a36Sopenharmony_ci/*
348462306a36Sopenharmony_ci * Check the format of a range of tracks of a DASD.
348562306a36Sopenharmony_ci */
348662306a36Sopenharmony_cistatic int dasd_eckd_check_device_format(struct dasd_device *base,
348762306a36Sopenharmony_ci					 struct format_check_t *cdata,
348862306a36Sopenharmony_ci					 int enable_pav)
348962306a36Sopenharmony_ci{
349062306a36Sopenharmony_ci	struct dasd_eckd_private *private = base->private;
349162306a36Sopenharmony_ci	struct eckd_count *fmt_buffer;
349262306a36Sopenharmony_ci	struct irb irb;
349362306a36Sopenharmony_ci	int rpt_max, rpt_exp;
349462306a36Sopenharmony_ci	int fmt_buffer_size;
349562306a36Sopenharmony_ci	int trk_per_cyl;
349662306a36Sopenharmony_ci	int trkcount;
349762306a36Sopenharmony_ci	int tpm = 0;
349862306a36Sopenharmony_ci	int rc;
349962306a36Sopenharmony_ci
350062306a36Sopenharmony_ci	trk_per_cyl = private->rdc_data.trk_per_cyl;
350162306a36Sopenharmony_ci
350262306a36Sopenharmony_ci	/* Get maximum and expected amount of records per track */
350362306a36Sopenharmony_ci	rpt_max = recs_per_track(&private->rdc_data, 0, 512) + 1;
350462306a36Sopenharmony_ci	rpt_exp = recs_per_track(&private->rdc_data, 0, cdata->expect.blksize);
350562306a36Sopenharmony_ci
350662306a36Sopenharmony_ci	trkcount = cdata->expect.stop_unit - cdata->expect.start_unit + 1;
350762306a36Sopenharmony_ci	fmt_buffer_size = trkcount * rpt_max * sizeof(struct eckd_count);
350862306a36Sopenharmony_ci
350962306a36Sopenharmony_ci	fmt_buffer = kzalloc(fmt_buffer_size, GFP_KERNEL | GFP_DMA);
351062306a36Sopenharmony_ci	if (!fmt_buffer)
351162306a36Sopenharmony_ci		return -ENOMEM;
351262306a36Sopenharmony_ci
351362306a36Sopenharmony_ci	/*
351462306a36Sopenharmony_ci	 * A certain FICON feature subset is needed to operate in transport
351562306a36Sopenharmony_ci	 * mode. Additionally, the support for transport mode is implicitly
351662306a36Sopenharmony_ci	 * checked by comparing the buffer size with fcx_max_data. As long as
351762306a36Sopenharmony_ci	 * the buffer size is smaller we can operate in transport mode and
351862306a36Sopenharmony_ci	 * process multiple tracks. If not, only one track at once is being
351962306a36Sopenharmony_ci	 * processed using command mode.
352062306a36Sopenharmony_ci	 */
352162306a36Sopenharmony_ci	if ((private->features.feature[40] & 0x04) &&
352262306a36Sopenharmony_ci	    fmt_buffer_size <= private->fcx_max_data)
352362306a36Sopenharmony_ci		tpm = 1;
352462306a36Sopenharmony_ci
352562306a36Sopenharmony_ci	rc = dasd_eckd_format_process_data(base, &cdata->expect, enable_pav,
352662306a36Sopenharmony_ci					   tpm, fmt_buffer, rpt_max, &irb);
352762306a36Sopenharmony_ci	if (rc && rc != -EIO)
352862306a36Sopenharmony_ci		goto out;
352962306a36Sopenharmony_ci	if (rc == -EIO) {
353062306a36Sopenharmony_ci		/*
353162306a36Sopenharmony_ci		 * If our first attempt with transport mode enabled comes back
353262306a36Sopenharmony_ci		 * with an incorrect length error, we're going to retry the
353362306a36Sopenharmony_ci		 * check with command mode.
353462306a36Sopenharmony_ci		 */
353562306a36Sopenharmony_ci		if (tpm && scsw_cstat(&irb.scsw) == 0x40) {
353662306a36Sopenharmony_ci			tpm = 0;
353762306a36Sopenharmony_ci			rc = dasd_eckd_format_process_data(base, &cdata->expect,
353862306a36Sopenharmony_ci							   enable_pav, tpm,
353962306a36Sopenharmony_ci							   fmt_buffer, rpt_max,
354062306a36Sopenharmony_ci							   &irb);
354162306a36Sopenharmony_ci			if (rc)
354262306a36Sopenharmony_ci				goto out;
354362306a36Sopenharmony_ci		} else {
354462306a36Sopenharmony_ci			goto out;
354562306a36Sopenharmony_ci		}
354662306a36Sopenharmony_ci	}
354762306a36Sopenharmony_ci
354862306a36Sopenharmony_ci	dasd_eckd_format_evaluate_tracks(fmt_buffer, cdata, rpt_max, rpt_exp,
354962306a36Sopenharmony_ci					 trk_per_cyl, tpm);
355062306a36Sopenharmony_ci
355162306a36Sopenharmony_ciout:
355262306a36Sopenharmony_ci	kfree(fmt_buffer);
355362306a36Sopenharmony_ci
355462306a36Sopenharmony_ci	return rc;
355562306a36Sopenharmony_ci}
355662306a36Sopenharmony_ci
355762306a36Sopenharmony_cistatic void dasd_eckd_handle_terminated_request(struct dasd_ccw_req *cqr)
355862306a36Sopenharmony_ci{
355962306a36Sopenharmony_ci	if (cqr->retries < 0) {
356062306a36Sopenharmony_ci		cqr->status = DASD_CQR_FAILED;
356162306a36Sopenharmony_ci		return;
356262306a36Sopenharmony_ci	}
356362306a36Sopenharmony_ci	cqr->status = DASD_CQR_FILLED;
356462306a36Sopenharmony_ci	if (cqr->block && (cqr->startdev != cqr->block->base)) {
356562306a36Sopenharmony_ci		dasd_eckd_reset_ccw_to_base_io(cqr);
356662306a36Sopenharmony_ci		cqr->startdev = cqr->block->base;
356762306a36Sopenharmony_ci		cqr->lpm = dasd_path_get_opm(cqr->block->base);
356862306a36Sopenharmony_ci	}
356962306a36Sopenharmony_ci};
357062306a36Sopenharmony_ci
357162306a36Sopenharmony_cistatic dasd_erp_fn_t
357262306a36Sopenharmony_cidasd_eckd_erp_action(struct dasd_ccw_req * cqr)
357362306a36Sopenharmony_ci{
357462306a36Sopenharmony_ci	struct dasd_device *device = (struct dasd_device *) cqr->startdev;
357562306a36Sopenharmony_ci	struct ccw_device *cdev = device->cdev;
357662306a36Sopenharmony_ci
357762306a36Sopenharmony_ci	switch (cdev->id.cu_type) {
357862306a36Sopenharmony_ci	case 0x3990:
357962306a36Sopenharmony_ci	case 0x2105:
358062306a36Sopenharmony_ci	case 0x2107:
358162306a36Sopenharmony_ci	case 0x1750:
358262306a36Sopenharmony_ci		return dasd_3990_erp_action;
358362306a36Sopenharmony_ci	case 0x9343:
358462306a36Sopenharmony_ci	case 0x3880:
358562306a36Sopenharmony_ci	default:
358662306a36Sopenharmony_ci		return dasd_default_erp_action;
358762306a36Sopenharmony_ci	}
358862306a36Sopenharmony_ci}
358962306a36Sopenharmony_ci
359062306a36Sopenharmony_cistatic dasd_erp_fn_t
359162306a36Sopenharmony_cidasd_eckd_erp_postaction(struct dasd_ccw_req * cqr)
359262306a36Sopenharmony_ci{
359362306a36Sopenharmony_ci	return dasd_default_erp_postaction;
359462306a36Sopenharmony_ci}
359562306a36Sopenharmony_ci
359662306a36Sopenharmony_cistatic void dasd_eckd_check_for_device_change(struct dasd_device *device,
359762306a36Sopenharmony_ci					      struct dasd_ccw_req *cqr,
359862306a36Sopenharmony_ci					      struct irb *irb)
359962306a36Sopenharmony_ci{
360062306a36Sopenharmony_ci	char mask;
360162306a36Sopenharmony_ci	char *sense = NULL;
360262306a36Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
360362306a36Sopenharmony_ci
360462306a36Sopenharmony_ci	/* first of all check for state change pending interrupt */
360562306a36Sopenharmony_ci	mask = DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP;
360662306a36Sopenharmony_ci	if ((scsw_dstat(&irb->scsw) & mask) == mask) {
360762306a36Sopenharmony_ci		/*
360862306a36Sopenharmony_ci		 * for alias only, not in offline processing
360962306a36Sopenharmony_ci		 * and only if not suspended
361062306a36Sopenharmony_ci		 */
361162306a36Sopenharmony_ci		if (!device->block && private->lcu &&
361262306a36Sopenharmony_ci		    device->state == DASD_STATE_ONLINE &&
361362306a36Sopenharmony_ci		    !test_bit(DASD_FLAG_OFFLINE, &device->flags) &&
361462306a36Sopenharmony_ci		    !test_bit(DASD_FLAG_SUSPENDED, &device->flags)) {
361562306a36Sopenharmony_ci			/* schedule worker to reload device */
361662306a36Sopenharmony_ci			dasd_reload_device(device);
361762306a36Sopenharmony_ci		}
361862306a36Sopenharmony_ci		dasd_generic_handle_state_change(device);
361962306a36Sopenharmony_ci		return;
362062306a36Sopenharmony_ci	}
362162306a36Sopenharmony_ci
362262306a36Sopenharmony_ci	sense = dasd_get_sense(irb);
362362306a36Sopenharmony_ci	if (!sense)
362462306a36Sopenharmony_ci		return;
362562306a36Sopenharmony_ci
362662306a36Sopenharmony_ci	/* summary unit check */
362762306a36Sopenharmony_ci	if ((sense[27] & DASD_SENSE_BIT_0) && (sense[7] == 0x0D) &&
362862306a36Sopenharmony_ci	    (scsw_dstat(&irb->scsw) & DEV_STAT_UNIT_CHECK)) {
362962306a36Sopenharmony_ci		if (test_and_set_bit(DASD_FLAG_SUC, &device->flags)) {
363062306a36Sopenharmony_ci			DBF_DEV_EVENT(DBF_WARNING, device, "%s",
363162306a36Sopenharmony_ci				      "eckd suc: device already notified");
363262306a36Sopenharmony_ci			return;
363362306a36Sopenharmony_ci		}
363462306a36Sopenharmony_ci		sense = dasd_get_sense(irb);
363562306a36Sopenharmony_ci		if (!sense) {
363662306a36Sopenharmony_ci			DBF_DEV_EVENT(DBF_WARNING, device, "%s",
363762306a36Sopenharmony_ci				      "eckd suc: no reason code available");
363862306a36Sopenharmony_ci			clear_bit(DASD_FLAG_SUC, &device->flags);
363962306a36Sopenharmony_ci			return;
364062306a36Sopenharmony_ci
364162306a36Sopenharmony_ci		}
364262306a36Sopenharmony_ci		private->suc_reason = sense[8];
364362306a36Sopenharmony_ci		DBF_DEV_EVENT(DBF_NOTICE, device, "%s %x",
364462306a36Sopenharmony_ci			      "eckd handle summary unit check: reason",
364562306a36Sopenharmony_ci			      private->suc_reason);
364662306a36Sopenharmony_ci		dasd_get_device(device);
364762306a36Sopenharmony_ci		if (!schedule_work(&device->suc_work))
364862306a36Sopenharmony_ci			dasd_put_device(device);
364962306a36Sopenharmony_ci
365062306a36Sopenharmony_ci		return;
365162306a36Sopenharmony_ci	}
365262306a36Sopenharmony_ci
365362306a36Sopenharmony_ci	/* service information message SIM */
365462306a36Sopenharmony_ci	if (!cqr && !(sense[27] & DASD_SENSE_BIT_0) &&
365562306a36Sopenharmony_ci	    ((sense[6] & DASD_SIM_SENSE) == DASD_SIM_SENSE)) {
365662306a36Sopenharmony_ci		dasd_3990_erp_handle_sim(device, sense);
365762306a36Sopenharmony_ci		return;
365862306a36Sopenharmony_ci	}
365962306a36Sopenharmony_ci
366062306a36Sopenharmony_ci	/* loss of device reservation is handled via base devices only
366162306a36Sopenharmony_ci	 * as alias devices may be used with several bases
366262306a36Sopenharmony_ci	 */
366362306a36Sopenharmony_ci	if (device->block && (sense[27] & DASD_SENSE_BIT_0) &&
366462306a36Sopenharmony_ci	    (sense[7] == 0x3F) &&
366562306a36Sopenharmony_ci	    (scsw_dstat(&irb->scsw) & DEV_STAT_UNIT_CHECK) &&
366662306a36Sopenharmony_ci	    test_bit(DASD_FLAG_IS_RESERVED, &device->flags)) {
366762306a36Sopenharmony_ci		if (device->features & DASD_FEATURE_FAILONSLCK)
366862306a36Sopenharmony_ci			set_bit(DASD_FLAG_LOCK_STOLEN, &device->flags);
366962306a36Sopenharmony_ci		clear_bit(DASD_FLAG_IS_RESERVED, &device->flags);
367062306a36Sopenharmony_ci		dev_err(&device->cdev->dev,
367162306a36Sopenharmony_ci			"The device reservation was lost\n");
367262306a36Sopenharmony_ci	}
367362306a36Sopenharmony_ci}
367462306a36Sopenharmony_ci
367562306a36Sopenharmony_cistatic int dasd_eckd_ras_sanity_checks(struct dasd_device *device,
367662306a36Sopenharmony_ci				       unsigned int first_trk,
367762306a36Sopenharmony_ci				       unsigned int last_trk)
367862306a36Sopenharmony_ci{
367962306a36Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
368062306a36Sopenharmony_ci	unsigned int trks_per_vol;
368162306a36Sopenharmony_ci	int rc = 0;
368262306a36Sopenharmony_ci
368362306a36Sopenharmony_ci	trks_per_vol = private->real_cyl * private->rdc_data.trk_per_cyl;
368462306a36Sopenharmony_ci
368562306a36Sopenharmony_ci	if (first_trk >= trks_per_vol) {
368662306a36Sopenharmony_ci		dev_warn(&device->cdev->dev,
368762306a36Sopenharmony_ci			 "Start track number %u used in the space release command is too big\n",
368862306a36Sopenharmony_ci			 first_trk);
368962306a36Sopenharmony_ci		rc = -EINVAL;
369062306a36Sopenharmony_ci	} else if (last_trk >= trks_per_vol) {
369162306a36Sopenharmony_ci		dev_warn(&device->cdev->dev,
369262306a36Sopenharmony_ci			 "Stop track number %u used in the space release command is too big\n",
369362306a36Sopenharmony_ci			 last_trk);
369462306a36Sopenharmony_ci		rc = -EINVAL;
369562306a36Sopenharmony_ci	} else if (first_trk > last_trk) {
369662306a36Sopenharmony_ci		dev_warn(&device->cdev->dev,
369762306a36Sopenharmony_ci			 "Start track %u used in the space release command exceeds the end track\n",
369862306a36Sopenharmony_ci			 first_trk);
369962306a36Sopenharmony_ci		rc = -EINVAL;
370062306a36Sopenharmony_ci	}
370162306a36Sopenharmony_ci	return rc;
370262306a36Sopenharmony_ci}
370362306a36Sopenharmony_ci
370462306a36Sopenharmony_ci/*
370562306a36Sopenharmony_ci * Helper function to count the amount of involved extents within a given range
370662306a36Sopenharmony_ci * with extent alignment in mind.
370762306a36Sopenharmony_ci */
370862306a36Sopenharmony_cistatic int count_exts(unsigned int from, unsigned int to, int trks_per_ext)
370962306a36Sopenharmony_ci{
371062306a36Sopenharmony_ci	int cur_pos = 0;
371162306a36Sopenharmony_ci	int count = 0;
371262306a36Sopenharmony_ci	int tmp;
371362306a36Sopenharmony_ci
371462306a36Sopenharmony_ci	if (from == to)
371562306a36Sopenharmony_ci		return 1;
371662306a36Sopenharmony_ci
371762306a36Sopenharmony_ci	/* Count first partial extent */
371862306a36Sopenharmony_ci	if (from % trks_per_ext != 0) {
371962306a36Sopenharmony_ci		tmp = from + trks_per_ext - (from % trks_per_ext) - 1;
372062306a36Sopenharmony_ci		if (tmp > to)
372162306a36Sopenharmony_ci			tmp = to;
372262306a36Sopenharmony_ci		cur_pos = tmp - from + 1;
372362306a36Sopenharmony_ci		count++;
372462306a36Sopenharmony_ci	}
372562306a36Sopenharmony_ci	/* Count full extents */
372662306a36Sopenharmony_ci	if (to - (from + cur_pos) + 1 >= trks_per_ext) {
372762306a36Sopenharmony_ci		tmp = to - ((to - trks_per_ext + 1) % trks_per_ext);
372862306a36Sopenharmony_ci		count += (tmp - (from + cur_pos) + 1) / trks_per_ext;
372962306a36Sopenharmony_ci		cur_pos = tmp;
373062306a36Sopenharmony_ci	}
373162306a36Sopenharmony_ci	/* Count last partial extent */
373262306a36Sopenharmony_ci	if (cur_pos < to)
373362306a36Sopenharmony_ci		count++;
373462306a36Sopenharmony_ci
373562306a36Sopenharmony_ci	return count;
373662306a36Sopenharmony_ci}
373762306a36Sopenharmony_ci
373862306a36Sopenharmony_cistatic int dasd_in_copy_relation(struct dasd_device *device)
373962306a36Sopenharmony_ci{
374062306a36Sopenharmony_ci	struct dasd_pprc_data_sc4 *temp;
374162306a36Sopenharmony_ci	int rc;
374262306a36Sopenharmony_ci
374362306a36Sopenharmony_ci	if (!dasd_eckd_pprc_enabled(device))
374462306a36Sopenharmony_ci		return 0;
374562306a36Sopenharmony_ci
374662306a36Sopenharmony_ci	temp = kzalloc(sizeof(*temp), GFP_KERNEL);
374762306a36Sopenharmony_ci	if (!temp)
374862306a36Sopenharmony_ci		return -ENOMEM;
374962306a36Sopenharmony_ci
375062306a36Sopenharmony_ci	rc = dasd_eckd_query_pprc_status(device, temp);
375162306a36Sopenharmony_ci	if (!rc)
375262306a36Sopenharmony_ci		rc = temp->dev_info[0].state;
375362306a36Sopenharmony_ci
375462306a36Sopenharmony_ci	kfree(temp);
375562306a36Sopenharmony_ci	return rc;
375662306a36Sopenharmony_ci}
375762306a36Sopenharmony_ci
375862306a36Sopenharmony_ci/*
375962306a36Sopenharmony_ci * Release allocated space for a given range or an entire volume.
376062306a36Sopenharmony_ci */
376162306a36Sopenharmony_cistatic struct dasd_ccw_req *
376262306a36Sopenharmony_cidasd_eckd_dso_ras(struct dasd_device *device, struct dasd_block *block,
376362306a36Sopenharmony_ci		  struct request *req, unsigned int first_trk,
376462306a36Sopenharmony_ci		  unsigned int last_trk, int by_extent)
376562306a36Sopenharmony_ci{
376662306a36Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
376762306a36Sopenharmony_ci	struct dasd_dso_ras_ext_range *ras_range;
376862306a36Sopenharmony_ci	struct dasd_rssd_features *features;
376962306a36Sopenharmony_ci	struct dasd_dso_ras_data *ras_data;
377062306a36Sopenharmony_ci	u16 heads, beg_head, end_head;
377162306a36Sopenharmony_ci	int cur_to_trk, cur_from_trk;
377262306a36Sopenharmony_ci	struct dasd_ccw_req *cqr;
377362306a36Sopenharmony_ci	u32 beg_cyl, end_cyl;
377462306a36Sopenharmony_ci	int copy_relation;
377562306a36Sopenharmony_ci	struct ccw1 *ccw;
377662306a36Sopenharmony_ci	int trks_per_ext;
377762306a36Sopenharmony_ci	size_t ras_size;
377862306a36Sopenharmony_ci	size_t size;
377962306a36Sopenharmony_ci	int nr_exts;
378062306a36Sopenharmony_ci	void *rq;
378162306a36Sopenharmony_ci	int i;
378262306a36Sopenharmony_ci
378362306a36Sopenharmony_ci	if (dasd_eckd_ras_sanity_checks(device, first_trk, last_trk))
378462306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
378562306a36Sopenharmony_ci
378662306a36Sopenharmony_ci	copy_relation = dasd_in_copy_relation(device);
378762306a36Sopenharmony_ci	if (copy_relation < 0)
378862306a36Sopenharmony_ci		return ERR_PTR(copy_relation);
378962306a36Sopenharmony_ci
379062306a36Sopenharmony_ci	rq = req ? blk_mq_rq_to_pdu(req) : NULL;
379162306a36Sopenharmony_ci
379262306a36Sopenharmony_ci	features = &private->features;
379362306a36Sopenharmony_ci
379462306a36Sopenharmony_ci	trks_per_ext = dasd_eckd_ext_size(device) * private->rdc_data.trk_per_cyl;
379562306a36Sopenharmony_ci	nr_exts = 0;
379662306a36Sopenharmony_ci	if (by_extent)
379762306a36Sopenharmony_ci		nr_exts = count_exts(first_trk, last_trk, trks_per_ext);
379862306a36Sopenharmony_ci	ras_size = sizeof(*ras_data);
379962306a36Sopenharmony_ci	size = ras_size + (nr_exts * sizeof(*ras_range));
380062306a36Sopenharmony_ci
380162306a36Sopenharmony_ci	cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1, size, device, rq);
380262306a36Sopenharmony_ci	if (IS_ERR(cqr)) {
380362306a36Sopenharmony_ci		DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s",
380462306a36Sopenharmony_ci				"Could not allocate RAS request");
380562306a36Sopenharmony_ci		return cqr;
380662306a36Sopenharmony_ci	}
380762306a36Sopenharmony_ci
380862306a36Sopenharmony_ci	ras_data = cqr->data;
380962306a36Sopenharmony_ci	memset(ras_data, 0, size);
381062306a36Sopenharmony_ci
381162306a36Sopenharmony_ci	ras_data->order = DSO_ORDER_RAS;
381262306a36Sopenharmony_ci	ras_data->flags.vol_type = 0; /* CKD volume */
381362306a36Sopenharmony_ci	/* Release specified extents or entire volume */
381462306a36Sopenharmony_ci	ras_data->op_flags.by_extent = by_extent;
381562306a36Sopenharmony_ci	/*
381662306a36Sopenharmony_ci	 * This bit guarantees initialisation of tracks within an extent that is
381762306a36Sopenharmony_ci	 * not fully specified, but is only supported with a certain feature
381862306a36Sopenharmony_ci	 * subset and for devices not in a copy relation.
381962306a36Sopenharmony_ci	 */
382062306a36Sopenharmony_ci	if (features->feature[56] & 0x01 && !copy_relation)
382162306a36Sopenharmony_ci		ras_data->op_flags.guarantee_init = 1;
382262306a36Sopenharmony_ci
382362306a36Sopenharmony_ci	ras_data->lss = private->conf.ned->ID;
382462306a36Sopenharmony_ci	ras_data->dev_addr = private->conf.ned->unit_addr;
382562306a36Sopenharmony_ci	ras_data->nr_exts = nr_exts;
382662306a36Sopenharmony_ci
382762306a36Sopenharmony_ci	if (by_extent) {
382862306a36Sopenharmony_ci		heads = private->rdc_data.trk_per_cyl;
382962306a36Sopenharmony_ci		cur_from_trk = first_trk;
383062306a36Sopenharmony_ci		cur_to_trk = first_trk + trks_per_ext -
383162306a36Sopenharmony_ci			(first_trk % trks_per_ext) - 1;
383262306a36Sopenharmony_ci		if (cur_to_trk > last_trk)
383362306a36Sopenharmony_ci			cur_to_trk = last_trk;
383462306a36Sopenharmony_ci		ras_range = (struct dasd_dso_ras_ext_range *)(cqr->data + ras_size);
383562306a36Sopenharmony_ci
383662306a36Sopenharmony_ci		for (i = 0; i < nr_exts; i++) {
383762306a36Sopenharmony_ci			beg_cyl = cur_from_trk / heads;
383862306a36Sopenharmony_ci			beg_head = cur_from_trk % heads;
383962306a36Sopenharmony_ci			end_cyl = cur_to_trk / heads;
384062306a36Sopenharmony_ci			end_head = cur_to_trk % heads;
384162306a36Sopenharmony_ci
384262306a36Sopenharmony_ci			set_ch_t(&ras_range->beg_ext, beg_cyl, beg_head);
384362306a36Sopenharmony_ci			set_ch_t(&ras_range->end_ext, end_cyl, end_head);
384462306a36Sopenharmony_ci
384562306a36Sopenharmony_ci			cur_from_trk = cur_to_trk + 1;
384662306a36Sopenharmony_ci			cur_to_trk = cur_from_trk + trks_per_ext - 1;
384762306a36Sopenharmony_ci			if (cur_to_trk > last_trk)
384862306a36Sopenharmony_ci				cur_to_trk = last_trk;
384962306a36Sopenharmony_ci			ras_range++;
385062306a36Sopenharmony_ci		}
385162306a36Sopenharmony_ci	}
385262306a36Sopenharmony_ci
385362306a36Sopenharmony_ci	ccw = cqr->cpaddr;
385462306a36Sopenharmony_ci	ccw->cda = (__u32)virt_to_phys(cqr->data);
385562306a36Sopenharmony_ci	ccw->cmd_code = DASD_ECKD_CCW_DSO;
385662306a36Sopenharmony_ci	ccw->count = size;
385762306a36Sopenharmony_ci
385862306a36Sopenharmony_ci	cqr->startdev = device;
385962306a36Sopenharmony_ci	cqr->memdev = device;
386062306a36Sopenharmony_ci	cqr->block = block;
386162306a36Sopenharmony_ci	cqr->retries = 256;
386262306a36Sopenharmony_ci	cqr->expires = device->default_expires * HZ;
386362306a36Sopenharmony_ci	cqr->buildclk = get_tod_clock();
386462306a36Sopenharmony_ci	cqr->status = DASD_CQR_FILLED;
386562306a36Sopenharmony_ci
386662306a36Sopenharmony_ci	return cqr;
386762306a36Sopenharmony_ci}
386862306a36Sopenharmony_ci
386962306a36Sopenharmony_cistatic int dasd_eckd_release_space_full(struct dasd_device *device)
387062306a36Sopenharmony_ci{
387162306a36Sopenharmony_ci	struct dasd_ccw_req *cqr;
387262306a36Sopenharmony_ci	int rc;
387362306a36Sopenharmony_ci
387462306a36Sopenharmony_ci	cqr = dasd_eckd_dso_ras(device, NULL, NULL, 0, 0, 0);
387562306a36Sopenharmony_ci	if (IS_ERR(cqr))
387662306a36Sopenharmony_ci		return PTR_ERR(cqr);
387762306a36Sopenharmony_ci
387862306a36Sopenharmony_ci	rc = dasd_sleep_on_interruptible(cqr);
387962306a36Sopenharmony_ci
388062306a36Sopenharmony_ci	dasd_sfree_request(cqr, cqr->memdev);
388162306a36Sopenharmony_ci
388262306a36Sopenharmony_ci	return rc;
388362306a36Sopenharmony_ci}
388462306a36Sopenharmony_ci
388562306a36Sopenharmony_cistatic int dasd_eckd_release_space_trks(struct dasd_device *device,
388662306a36Sopenharmony_ci					unsigned int from, unsigned int to)
388762306a36Sopenharmony_ci{
388862306a36Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
388962306a36Sopenharmony_ci	struct dasd_block *block = device->block;
389062306a36Sopenharmony_ci	struct dasd_ccw_req *cqr, *n;
389162306a36Sopenharmony_ci	struct list_head ras_queue;
389262306a36Sopenharmony_ci	unsigned int device_exts;
389362306a36Sopenharmony_ci	int trks_per_ext;
389462306a36Sopenharmony_ci	int stop, step;
389562306a36Sopenharmony_ci	int cur_pos;
389662306a36Sopenharmony_ci	int rc = 0;
389762306a36Sopenharmony_ci	int retry;
389862306a36Sopenharmony_ci
389962306a36Sopenharmony_ci	INIT_LIST_HEAD(&ras_queue);
390062306a36Sopenharmony_ci
390162306a36Sopenharmony_ci	device_exts = private->real_cyl / dasd_eckd_ext_size(device);
390262306a36Sopenharmony_ci	trks_per_ext = dasd_eckd_ext_size(device) * private->rdc_data.trk_per_cyl;
390362306a36Sopenharmony_ci
390462306a36Sopenharmony_ci	/* Make sure device limits are not exceeded */
390562306a36Sopenharmony_ci	step = trks_per_ext * min(device_exts, DASD_ECKD_RAS_EXTS_MAX);
390662306a36Sopenharmony_ci	cur_pos = from;
390762306a36Sopenharmony_ci
390862306a36Sopenharmony_ci	do {
390962306a36Sopenharmony_ci		retry = 0;
391062306a36Sopenharmony_ci		while (cur_pos < to) {
391162306a36Sopenharmony_ci			stop = cur_pos + step -
391262306a36Sopenharmony_ci				((cur_pos + step) % trks_per_ext) - 1;
391362306a36Sopenharmony_ci			if (stop > to)
391462306a36Sopenharmony_ci				stop = to;
391562306a36Sopenharmony_ci
391662306a36Sopenharmony_ci			cqr = dasd_eckd_dso_ras(device, NULL, NULL, cur_pos, stop, 1);
391762306a36Sopenharmony_ci			if (IS_ERR(cqr)) {
391862306a36Sopenharmony_ci				rc = PTR_ERR(cqr);
391962306a36Sopenharmony_ci				if (rc == -ENOMEM) {
392062306a36Sopenharmony_ci					if (list_empty(&ras_queue))
392162306a36Sopenharmony_ci						goto out;
392262306a36Sopenharmony_ci					retry = 1;
392362306a36Sopenharmony_ci					break;
392462306a36Sopenharmony_ci				}
392562306a36Sopenharmony_ci				goto err_out;
392662306a36Sopenharmony_ci			}
392762306a36Sopenharmony_ci
392862306a36Sopenharmony_ci			spin_lock_irq(&block->queue_lock);
392962306a36Sopenharmony_ci			list_add_tail(&cqr->blocklist, &ras_queue);
393062306a36Sopenharmony_ci			spin_unlock_irq(&block->queue_lock);
393162306a36Sopenharmony_ci			cur_pos = stop + 1;
393262306a36Sopenharmony_ci		}
393362306a36Sopenharmony_ci
393462306a36Sopenharmony_ci		rc = dasd_sleep_on_queue_interruptible(&ras_queue);
393562306a36Sopenharmony_ci
393662306a36Sopenharmony_cierr_out:
393762306a36Sopenharmony_ci		list_for_each_entry_safe(cqr, n, &ras_queue, blocklist) {
393862306a36Sopenharmony_ci			device = cqr->startdev;
393962306a36Sopenharmony_ci			private = device->private;
394062306a36Sopenharmony_ci
394162306a36Sopenharmony_ci			spin_lock_irq(&block->queue_lock);
394262306a36Sopenharmony_ci			list_del_init(&cqr->blocklist);
394362306a36Sopenharmony_ci			spin_unlock_irq(&block->queue_lock);
394462306a36Sopenharmony_ci			dasd_sfree_request(cqr, device);
394562306a36Sopenharmony_ci			private->count--;
394662306a36Sopenharmony_ci		}
394762306a36Sopenharmony_ci	} while (retry);
394862306a36Sopenharmony_ci
394962306a36Sopenharmony_ciout:
395062306a36Sopenharmony_ci	return rc;
395162306a36Sopenharmony_ci}
395262306a36Sopenharmony_ci
395362306a36Sopenharmony_cistatic int dasd_eckd_release_space(struct dasd_device *device,
395462306a36Sopenharmony_ci				   struct format_data_t *rdata)
395562306a36Sopenharmony_ci{
395662306a36Sopenharmony_ci	if (rdata->intensity & DASD_FMT_INT_ESE_FULL)
395762306a36Sopenharmony_ci		return dasd_eckd_release_space_full(device);
395862306a36Sopenharmony_ci	else if (rdata->intensity == 0)
395962306a36Sopenharmony_ci		return dasd_eckd_release_space_trks(device, rdata->start_unit,
396062306a36Sopenharmony_ci						    rdata->stop_unit);
396162306a36Sopenharmony_ci	else
396262306a36Sopenharmony_ci		return -EINVAL;
396362306a36Sopenharmony_ci}
396462306a36Sopenharmony_ci
396562306a36Sopenharmony_cistatic struct dasd_ccw_req *dasd_eckd_build_cp_cmd_single(
396662306a36Sopenharmony_ci					       struct dasd_device *startdev,
396762306a36Sopenharmony_ci					       struct dasd_block *block,
396862306a36Sopenharmony_ci					       struct request *req,
396962306a36Sopenharmony_ci					       sector_t first_rec,
397062306a36Sopenharmony_ci					       sector_t last_rec,
397162306a36Sopenharmony_ci					       sector_t first_trk,
397262306a36Sopenharmony_ci					       sector_t last_trk,
397362306a36Sopenharmony_ci					       unsigned int first_offs,
397462306a36Sopenharmony_ci					       unsigned int last_offs,
397562306a36Sopenharmony_ci					       unsigned int blk_per_trk,
397662306a36Sopenharmony_ci					       unsigned int blksize)
397762306a36Sopenharmony_ci{
397862306a36Sopenharmony_ci	struct dasd_eckd_private *private;
397962306a36Sopenharmony_ci	unsigned long *idaws;
398062306a36Sopenharmony_ci	struct LO_eckd_data *LO_data;
398162306a36Sopenharmony_ci	struct dasd_ccw_req *cqr;
398262306a36Sopenharmony_ci	struct ccw1 *ccw;
398362306a36Sopenharmony_ci	struct req_iterator iter;
398462306a36Sopenharmony_ci	struct bio_vec bv;
398562306a36Sopenharmony_ci	char *dst;
398662306a36Sopenharmony_ci	unsigned int off;
398762306a36Sopenharmony_ci	int count, cidaw, cplength, datasize;
398862306a36Sopenharmony_ci	sector_t recid;
398962306a36Sopenharmony_ci	unsigned char cmd, rcmd;
399062306a36Sopenharmony_ci	int use_prefix;
399162306a36Sopenharmony_ci	struct dasd_device *basedev;
399262306a36Sopenharmony_ci
399362306a36Sopenharmony_ci	basedev = block->base;
399462306a36Sopenharmony_ci	private = basedev->private;
399562306a36Sopenharmony_ci	if (rq_data_dir(req) == READ)
399662306a36Sopenharmony_ci		cmd = DASD_ECKD_CCW_READ_MT;
399762306a36Sopenharmony_ci	else if (rq_data_dir(req) == WRITE)
399862306a36Sopenharmony_ci		cmd = DASD_ECKD_CCW_WRITE_MT;
399962306a36Sopenharmony_ci	else
400062306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
400162306a36Sopenharmony_ci
400262306a36Sopenharmony_ci	/* Check struct bio and count the number of blocks for the request. */
400362306a36Sopenharmony_ci	count = 0;
400462306a36Sopenharmony_ci	cidaw = 0;
400562306a36Sopenharmony_ci	rq_for_each_segment(bv, req, iter) {
400662306a36Sopenharmony_ci		if (bv.bv_len & (blksize - 1))
400762306a36Sopenharmony_ci			/* Eckd can only do full blocks. */
400862306a36Sopenharmony_ci			return ERR_PTR(-EINVAL);
400962306a36Sopenharmony_ci		count += bv.bv_len >> (block->s2b_shift + 9);
401062306a36Sopenharmony_ci		if (idal_is_needed (page_address(bv.bv_page), bv.bv_len))
401162306a36Sopenharmony_ci			cidaw += bv.bv_len >> (block->s2b_shift + 9);
401262306a36Sopenharmony_ci	}
401362306a36Sopenharmony_ci	/* Paranoia. */
401462306a36Sopenharmony_ci	if (count != last_rec - first_rec + 1)
401562306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
401662306a36Sopenharmony_ci
401762306a36Sopenharmony_ci	/* use the prefix command if available */
401862306a36Sopenharmony_ci	use_prefix = private->features.feature[8] & 0x01;
401962306a36Sopenharmony_ci	if (use_prefix) {
402062306a36Sopenharmony_ci		/* 1x prefix + number of blocks */
402162306a36Sopenharmony_ci		cplength = 2 + count;
402262306a36Sopenharmony_ci		/* 1x prefix + cidaws*sizeof(long) */
402362306a36Sopenharmony_ci		datasize = sizeof(struct PFX_eckd_data) +
402462306a36Sopenharmony_ci			sizeof(struct LO_eckd_data) +
402562306a36Sopenharmony_ci			cidaw * sizeof(unsigned long);
402662306a36Sopenharmony_ci	} else {
402762306a36Sopenharmony_ci		/* 1x define extent + 1x locate record + number of blocks */
402862306a36Sopenharmony_ci		cplength = 2 + count;
402962306a36Sopenharmony_ci		/* 1x define extent + 1x locate record + cidaws*sizeof(long) */
403062306a36Sopenharmony_ci		datasize = sizeof(struct DE_eckd_data) +
403162306a36Sopenharmony_ci			sizeof(struct LO_eckd_data) +
403262306a36Sopenharmony_ci			cidaw * sizeof(unsigned long);
403362306a36Sopenharmony_ci	}
403462306a36Sopenharmony_ci	/* Find out the number of additional locate record ccws for cdl. */
403562306a36Sopenharmony_ci	if (private->uses_cdl && first_rec < 2*blk_per_trk) {
403662306a36Sopenharmony_ci		if (last_rec >= 2*blk_per_trk)
403762306a36Sopenharmony_ci			count = 2*blk_per_trk - first_rec;
403862306a36Sopenharmony_ci		cplength += count;
403962306a36Sopenharmony_ci		datasize += count*sizeof(struct LO_eckd_data);
404062306a36Sopenharmony_ci	}
404162306a36Sopenharmony_ci	/* Allocate the ccw request. */
404262306a36Sopenharmony_ci	cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, cplength, datasize,
404362306a36Sopenharmony_ci				   startdev, blk_mq_rq_to_pdu(req));
404462306a36Sopenharmony_ci	if (IS_ERR(cqr))
404562306a36Sopenharmony_ci		return cqr;
404662306a36Sopenharmony_ci	ccw = cqr->cpaddr;
404762306a36Sopenharmony_ci	/* First ccw is define extent or prefix. */
404862306a36Sopenharmony_ci	if (use_prefix) {
404962306a36Sopenharmony_ci		if (prefix(ccw++, cqr->data, first_trk,
405062306a36Sopenharmony_ci			   last_trk, cmd, basedev, startdev) == -EAGAIN) {
405162306a36Sopenharmony_ci			/* Clock not in sync and XRC is enabled.
405262306a36Sopenharmony_ci			 * Try again later.
405362306a36Sopenharmony_ci			 */
405462306a36Sopenharmony_ci			dasd_sfree_request(cqr, startdev);
405562306a36Sopenharmony_ci			return ERR_PTR(-EAGAIN);
405662306a36Sopenharmony_ci		}
405762306a36Sopenharmony_ci		idaws = (unsigned long *) (cqr->data +
405862306a36Sopenharmony_ci					   sizeof(struct PFX_eckd_data));
405962306a36Sopenharmony_ci	} else {
406062306a36Sopenharmony_ci		if (define_extent(ccw++, cqr->data, first_trk,
406162306a36Sopenharmony_ci				  last_trk, cmd, basedev, 0) == -EAGAIN) {
406262306a36Sopenharmony_ci			/* Clock not in sync and XRC is enabled.
406362306a36Sopenharmony_ci			 * Try again later.
406462306a36Sopenharmony_ci			 */
406562306a36Sopenharmony_ci			dasd_sfree_request(cqr, startdev);
406662306a36Sopenharmony_ci			return ERR_PTR(-EAGAIN);
406762306a36Sopenharmony_ci		}
406862306a36Sopenharmony_ci		idaws = (unsigned long *) (cqr->data +
406962306a36Sopenharmony_ci					   sizeof(struct DE_eckd_data));
407062306a36Sopenharmony_ci	}
407162306a36Sopenharmony_ci	/* Build locate_record+read/write/ccws. */
407262306a36Sopenharmony_ci	LO_data = (struct LO_eckd_data *) (idaws + cidaw);
407362306a36Sopenharmony_ci	recid = first_rec;
407462306a36Sopenharmony_ci	if (private->uses_cdl == 0 || recid > 2*blk_per_trk) {
407562306a36Sopenharmony_ci		/* Only standard blocks so there is just one locate record. */
407662306a36Sopenharmony_ci		ccw[-1].flags |= CCW_FLAG_CC;
407762306a36Sopenharmony_ci		locate_record(ccw++, LO_data++, first_trk, first_offs + 1,
407862306a36Sopenharmony_ci			      last_rec - recid + 1, cmd, basedev, blksize);
407962306a36Sopenharmony_ci	}
408062306a36Sopenharmony_ci	rq_for_each_segment(bv, req, iter) {
408162306a36Sopenharmony_ci		dst = bvec_virt(&bv);
408262306a36Sopenharmony_ci		if (dasd_page_cache) {
408362306a36Sopenharmony_ci			char *copy = kmem_cache_alloc(dasd_page_cache,
408462306a36Sopenharmony_ci						      GFP_DMA | __GFP_NOWARN);
408562306a36Sopenharmony_ci			if (copy && rq_data_dir(req) == WRITE)
408662306a36Sopenharmony_ci				memcpy(copy + bv.bv_offset, dst, bv.bv_len);
408762306a36Sopenharmony_ci			if (copy)
408862306a36Sopenharmony_ci				dst = copy + bv.bv_offset;
408962306a36Sopenharmony_ci		}
409062306a36Sopenharmony_ci		for (off = 0; off < bv.bv_len; off += blksize) {
409162306a36Sopenharmony_ci			sector_t trkid = recid;
409262306a36Sopenharmony_ci			unsigned int recoffs = sector_div(trkid, blk_per_trk);
409362306a36Sopenharmony_ci			rcmd = cmd;
409462306a36Sopenharmony_ci			count = blksize;
409562306a36Sopenharmony_ci			/* Locate record for cdl special block ? */
409662306a36Sopenharmony_ci			if (private->uses_cdl && recid < 2*blk_per_trk) {
409762306a36Sopenharmony_ci				if (dasd_eckd_cdl_special(blk_per_trk, recid)){
409862306a36Sopenharmony_ci					rcmd |= 0x8;
409962306a36Sopenharmony_ci					count = dasd_eckd_cdl_reclen(recid);
410062306a36Sopenharmony_ci					if (count < blksize &&
410162306a36Sopenharmony_ci					    rq_data_dir(req) == READ)
410262306a36Sopenharmony_ci						memset(dst + count, 0xe5,
410362306a36Sopenharmony_ci						       blksize - count);
410462306a36Sopenharmony_ci				}
410562306a36Sopenharmony_ci				ccw[-1].flags |= CCW_FLAG_CC;
410662306a36Sopenharmony_ci				locate_record(ccw++, LO_data++,
410762306a36Sopenharmony_ci					      trkid, recoffs + 1,
410862306a36Sopenharmony_ci					      1, rcmd, basedev, count);
410962306a36Sopenharmony_ci			}
411062306a36Sopenharmony_ci			/* Locate record for standard blocks ? */
411162306a36Sopenharmony_ci			if (private->uses_cdl && recid == 2*blk_per_trk) {
411262306a36Sopenharmony_ci				ccw[-1].flags |= CCW_FLAG_CC;
411362306a36Sopenharmony_ci				locate_record(ccw++, LO_data++,
411462306a36Sopenharmony_ci					      trkid, recoffs + 1,
411562306a36Sopenharmony_ci					      last_rec - recid + 1,
411662306a36Sopenharmony_ci					      cmd, basedev, count);
411762306a36Sopenharmony_ci			}
411862306a36Sopenharmony_ci			/* Read/write ccw. */
411962306a36Sopenharmony_ci			ccw[-1].flags |= CCW_FLAG_CC;
412062306a36Sopenharmony_ci			ccw->cmd_code = rcmd;
412162306a36Sopenharmony_ci			ccw->count = count;
412262306a36Sopenharmony_ci			if (idal_is_needed(dst, blksize)) {
412362306a36Sopenharmony_ci				ccw->cda = (__u32)virt_to_phys(idaws);
412462306a36Sopenharmony_ci				ccw->flags = CCW_FLAG_IDA;
412562306a36Sopenharmony_ci				idaws = idal_create_words(idaws, dst, blksize);
412662306a36Sopenharmony_ci			} else {
412762306a36Sopenharmony_ci				ccw->cda = (__u32)virt_to_phys(dst);
412862306a36Sopenharmony_ci				ccw->flags = 0;
412962306a36Sopenharmony_ci			}
413062306a36Sopenharmony_ci			ccw++;
413162306a36Sopenharmony_ci			dst += blksize;
413262306a36Sopenharmony_ci			recid++;
413362306a36Sopenharmony_ci		}
413462306a36Sopenharmony_ci	}
413562306a36Sopenharmony_ci	if (blk_noretry_request(req) ||
413662306a36Sopenharmony_ci	    block->base->features & DASD_FEATURE_FAILFAST)
413762306a36Sopenharmony_ci		set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags);
413862306a36Sopenharmony_ci	cqr->startdev = startdev;
413962306a36Sopenharmony_ci	cqr->memdev = startdev;
414062306a36Sopenharmony_ci	cqr->block = block;
414162306a36Sopenharmony_ci	cqr->expires = startdev->default_expires * HZ;	/* default 5 minutes */
414262306a36Sopenharmony_ci	cqr->lpm = dasd_path_get_ppm(startdev);
414362306a36Sopenharmony_ci	cqr->retries = startdev->default_retries;
414462306a36Sopenharmony_ci	cqr->buildclk = get_tod_clock();
414562306a36Sopenharmony_ci	cqr->status = DASD_CQR_FILLED;
414662306a36Sopenharmony_ci
414762306a36Sopenharmony_ci	/* Set flags to suppress output for expected errors */
414862306a36Sopenharmony_ci	if (dasd_eckd_is_ese(basedev)) {
414962306a36Sopenharmony_ci		set_bit(DASD_CQR_SUPPRESS_FP, &cqr->flags);
415062306a36Sopenharmony_ci		set_bit(DASD_CQR_SUPPRESS_IL, &cqr->flags);
415162306a36Sopenharmony_ci		set_bit(DASD_CQR_SUPPRESS_NRF, &cqr->flags);
415262306a36Sopenharmony_ci	}
415362306a36Sopenharmony_ci
415462306a36Sopenharmony_ci	return cqr;
415562306a36Sopenharmony_ci}
415662306a36Sopenharmony_ci
415762306a36Sopenharmony_cistatic struct dasd_ccw_req *dasd_eckd_build_cp_cmd_track(
415862306a36Sopenharmony_ci					       struct dasd_device *startdev,
415962306a36Sopenharmony_ci					       struct dasd_block *block,
416062306a36Sopenharmony_ci					       struct request *req,
416162306a36Sopenharmony_ci					       sector_t first_rec,
416262306a36Sopenharmony_ci					       sector_t last_rec,
416362306a36Sopenharmony_ci					       sector_t first_trk,
416462306a36Sopenharmony_ci					       sector_t last_trk,
416562306a36Sopenharmony_ci					       unsigned int first_offs,
416662306a36Sopenharmony_ci					       unsigned int last_offs,
416762306a36Sopenharmony_ci					       unsigned int blk_per_trk,
416862306a36Sopenharmony_ci					       unsigned int blksize)
416962306a36Sopenharmony_ci{
417062306a36Sopenharmony_ci	unsigned long *idaws;
417162306a36Sopenharmony_ci	struct dasd_ccw_req *cqr;
417262306a36Sopenharmony_ci	struct ccw1 *ccw;
417362306a36Sopenharmony_ci	struct req_iterator iter;
417462306a36Sopenharmony_ci	struct bio_vec bv;
417562306a36Sopenharmony_ci	char *dst, *idaw_dst;
417662306a36Sopenharmony_ci	unsigned int cidaw, cplength, datasize;
417762306a36Sopenharmony_ci	unsigned int tlf;
417862306a36Sopenharmony_ci	sector_t recid;
417962306a36Sopenharmony_ci	unsigned char cmd;
418062306a36Sopenharmony_ci	struct dasd_device *basedev;
418162306a36Sopenharmony_ci	unsigned int trkcount, count, count_to_trk_end;
418262306a36Sopenharmony_ci	unsigned int idaw_len, seg_len, part_len, len_to_track_end;
418362306a36Sopenharmony_ci	unsigned char new_track, end_idaw;
418462306a36Sopenharmony_ci	sector_t trkid;
418562306a36Sopenharmony_ci	unsigned int recoffs;
418662306a36Sopenharmony_ci
418762306a36Sopenharmony_ci	basedev = block->base;
418862306a36Sopenharmony_ci	if (rq_data_dir(req) == READ)
418962306a36Sopenharmony_ci		cmd = DASD_ECKD_CCW_READ_TRACK_DATA;
419062306a36Sopenharmony_ci	else if (rq_data_dir(req) == WRITE)
419162306a36Sopenharmony_ci		cmd = DASD_ECKD_CCW_WRITE_TRACK_DATA;
419262306a36Sopenharmony_ci	else
419362306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
419462306a36Sopenharmony_ci
419562306a36Sopenharmony_ci	/* Track based I/O needs IDAWs for each page, and not just for
419662306a36Sopenharmony_ci	 * 64 bit addresses. We need additional idals for pages
419762306a36Sopenharmony_ci	 * that get filled from two tracks, so we use the number
419862306a36Sopenharmony_ci	 * of records as upper limit.
419962306a36Sopenharmony_ci	 */
420062306a36Sopenharmony_ci	cidaw = last_rec - first_rec + 1;
420162306a36Sopenharmony_ci	trkcount = last_trk - first_trk + 1;
420262306a36Sopenharmony_ci
420362306a36Sopenharmony_ci	/* 1x prefix + one read/write ccw per track */
420462306a36Sopenharmony_ci	cplength = 1 + trkcount;
420562306a36Sopenharmony_ci
420662306a36Sopenharmony_ci	datasize = sizeof(struct PFX_eckd_data) + cidaw * sizeof(unsigned long);
420762306a36Sopenharmony_ci
420862306a36Sopenharmony_ci	/* Allocate the ccw request. */
420962306a36Sopenharmony_ci	cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, cplength, datasize,
421062306a36Sopenharmony_ci				   startdev, blk_mq_rq_to_pdu(req));
421162306a36Sopenharmony_ci	if (IS_ERR(cqr))
421262306a36Sopenharmony_ci		return cqr;
421362306a36Sopenharmony_ci	ccw = cqr->cpaddr;
421462306a36Sopenharmony_ci	/* transfer length factor: how many bytes to read from the last track */
421562306a36Sopenharmony_ci	if (first_trk == last_trk)
421662306a36Sopenharmony_ci		tlf = last_offs - first_offs + 1;
421762306a36Sopenharmony_ci	else
421862306a36Sopenharmony_ci		tlf = last_offs + 1;
421962306a36Sopenharmony_ci	tlf *= blksize;
422062306a36Sopenharmony_ci
422162306a36Sopenharmony_ci	if (prefix_LRE(ccw++, cqr->data, first_trk,
422262306a36Sopenharmony_ci		       last_trk, cmd, basedev, startdev,
422362306a36Sopenharmony_ci		       1 /* format */, first_offs + 1,
422462306a36Sopenharmony_ci		       trkcount, blksize,
422562306a36Sopenharmony_ci		       tlf) == -EAGAIN) {
422662306a36Sopenharmony_ci		/* Clock not in sync and XRC is enabled.
422762306a36Sopenharmony_ci		 * Try again later.
422862306a36Sopenharmony_ci		 */
422962306a36Sopenharmony_ci		dasd_sfree_request(cqr, startdev);
423062306a36Sopenharmony_ci		return ERR_PTR(-EAGAIN);
423162306a36Sopenharmony_ci	}
423262306a36Sopenharmony_ci
423362306a36Sopenharmony_ci	/*
423462306a36Sopenharmony_ci	 * The translation of request into ccw programs must meet the
423562306a36Sopenharmony_ci	 * following conditions:
423662306a36Sopenharmony_ci	 * - all idaws but the first and the last must address full pages
423762306a36Sopenharmony_ci	 *   (or 2K blocks on 31-bit)
423862306a36Sopenharmony_ci	 * - the scope of a ccw and it's idal ends with the track boundaries
423962306a36Sopenharmony_ci	 */
424062306a36Sopenharmony_ci	idaws = (unsigned long *) (cqr->data + sizeof(struct PFX_eckd_data));
424162306a36Sopenharmony_ci	recid = first_rec;
424262306a36Sopenharmony_ci	new_track = 1;
424362306a36Sopenharmony_ci	end_idaw = 0;
424462306a36Sopenharmony_ci	len_to_track_end = 0;
424562306a36Sopenharmony_ci	idaw_dst = NULL;
424662306a36Sopenharmony_ci	idaw_len = 0;
424762306a36Sopenharmony_ci	rq_for_each_segment(bv, req, iter) {
424862306a36Sopenharmony_ci		dst = bvec_virt(&bv);
424962306a36Sopenharmony_ci		seg_len = bv.bv_len;
425062306a36Sopenharmony_ci		while (seg_len) {
425162306a36Sopenharmony_ci			if (new_track) {
425262306a36Sopenharmony_ci				trkid = recid;
425362306a36Sopenharmony_ci				recoffs = sector_div(trkid, blk_per_trk);
425462306a36Sopenharmony_ci				count_to_trk_end = blk_per_trk - recoffs;
425562306a36Sopenharmony_ci				count = min((last_rec - recid + 1),
425662306a36Sopenharmony_ci					    (sector_t)count_to_trk_end);
425762306a36Sopenharmony_ci				len_to_track_end = count * blksize;
425862306a36Sopenharmony_ci				ccw[-1].flags |= CCW_FLAG_CC;
425962306a36Sopenharmony_ci				ccw->cmd_code = cmd;
426062306a36Sopenharmony_ci				ccw->count = len_to_track_end;
426162306a36Sopenharmony_ci				ccw->cda = (__u32)virt_to_phys(idaws);
426262306a36Sopenharmony_ci				ccw->flags = CCW_FLAG_IDA;
426362306a36Sopenharmony_ci				ccw++;
426462306a36Sopenharmony_ci				recid += count;
426562306a36Sopenharmony_ci				new_track = 0;
426662306a36Sopenharmony_ci				/* first idaw for a ccw may start anywhere */
426762306a36Sopenharmony_ci				if (!idaw_dst)
426862306a36Sopenharmony_ci					idaw_dst = dst;
426962306a36Sopenharmony_ci			}
427062306a36Sopenharmony_ci			/* If we start a new idaw, we must make sure that it
427162306a36Sopenharmony_ci			 * starts on an IDA_BLOCK_SIZE boundary.
427262306a36Sopenharmony_ci			 * If we continue an idaw, we must make sure that the
427362306a36Sopenharmony_ci			 * current segment begins where the so far accumulated
427462306a36Sopenharmony_ci			 * idaw ends
427562306a36Sopenharmony_ci			 */
427662306a36Sopenharmony_ci			if (!idaw_dst) {
427762306a36Sopenharmony_ci				if ((__u32)virt_to_phys(dst) & (IDA_BLOCK_SIZE - 1)) {
427862306a36Sopenharmony_ci					dasd_sfree_request(cqr, startdev);
427962306a36Sopenharmony_ci					return ERR_PTR(-ERANGE);
428062306a36Sopenharmony_ci				} else
428162306a36Sopenharmony_ci					idaw_dst = dst;
428262306a36Sopenharmony_ci			}
428362306a36Sopenharmony_ci			if ((idaw_dst + idaw_len) != dst) {
428462306a36Sopenharmony_ci				dasd_sfree_request(cqr, startdev);
428562306a36Sopenharmony_ci				return ERR_PTR(-ERANGE);
428662306a36Sopenharmony_ci			}
428762306a36Sopenharmony_ci			part_len = min(seg_len, len_to_track_end);
428862306a36Sopenharmony_ci			seg_len -= part_len;
428962306a36Sopenharmony_ci			dst += part_len;
429062306a36Sopenharmony_ci			idaw_len += part_len;
429162306a36Sopenharmony_ci			len_to_track_end -= part_len;
429262306a36Sopenharmony_ci			/* collected memory area ends on an IDA_BLOCK border,
429362306a36Sopenharmony_ci			 * -> create an idaw
429462306a36Sopenharmony_ci			 * idal_create_words will handle cases where idaw_len
429562306a36Sopenharmony_ci			 * is larger then IDA_BLOCK_SIZE
429662306a36Sopenharmony_ci			 */
429762306a36Sopenharmony_ci			if (!((__u32)virt_to_phys(idaw_dst + idaw_len) & (IDA_BLOCK_SIZE - 1)))
429862306a36Sopenharmony_ci				end_idaw = 1;
429962306a36Sopenharmony_ci			/* We also need to end the idaw at track end */
430062306a36Sopenharmony_ci			if (!len_to_track_end) {
430162306a36Sopenharmony_ci				new_track = 1;
430262306a36Sopenharmony_ci				end_idaw = 1;
430362306a36Sopenharmony_ci			}
430462306a36Sopenharmony_ci			if (end_idaw) {
430562306a36Sopenharmony_ci				idaws = idal_create_words(idaws, idaw_dst,
430662306a36Sopenharmony_ci							  idaw_len);
430762306a36Sopenharmony_ci				idaw_dst = NULL;
430862306a36Sopenharmony_ci				idaw_len = 0;
430962306a36Sopenharmony_ci				end_idaw = 0;
431062306a36Sopenharmony_ci			}
431162306a36Sopenharmony_ci		}
431262306a36Sopenharmony_ci	}
431362306a36Sopenharmony_ci
431462306a36Sopenharmony_ci	if (blk_noretry_request(req) ||
431562306a36Sopenharmony_ci	    block->base->features & DASD_FEATURE_FAILFAST)
431662306a36Sopenharmony_ci		set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags);
431762306a36Sopenharmony_ci	cqr->startdev = startdev;
431862306a36Sopenharmony_ci	cqr->memdev = startdev;
431962306a36Sopenharmony_ci	cqr->block = block;
432062306a36Sopenharmony_ci	cqr->expires = startdev->default_expires * HZ;	/* default 5 minutes */
432162306a36Sopenharmony_ci	cqr->lpm = dasd_path_get_ppm(startdev);
432262306a36Sopenharmony_ci	cqr->retries = startdev->default_retries;
432362306a36Sopenharmony_ci	cqr->buildclk = get_tod_clock();
432462306a36Sopenharmony_ci	cqr->status = DASD_CQR_FILLED;
432562306a36Sopenharmony_ci
432662306a36Sopenharmony_ci	/* Set flags to suppress output for expected errors */
432762306a36Sopenharmony_ci	if (dasd_eckd_is_ese(basedev))
432862306a36Sopenharmony_ci		set_bit(DASD_CQR_SUPPRESS_NRF, &cqr->flags);
432962306a36Sopenharmony_ci
433062306a36Sopenharmony_ci	return cqr;
433162306a36Sopenharmony_ci}
433262306a36Sopenharmony_ci
433362306a36Sopenharmony_cistatic int prepare_itcw(struct itcw *itcw,
433462306a36Sopenharmony_ci			unsigned int trk, unsigned int totrk, int cmd,
433562306a36Sopenharmony_ci			struct dasd_device *basedev,
433662306a36Sopenharmony_ci			struct dasd_device *startdev,
433762306a36Sopenharmony_ci			unsigned int rec_on_trk, int count,
433862306a36Sopenharmony_ci			unsigned int blksize,
433962306a36Sopenharmony_ci			unsigned int total_data_size,
434062306a36Sopenharmony_ci			unsigned int tlf,
434162306a36Sopenharmony_ci			unsigned int blk_per_trk)
434262306a36Sopenharmony_ci{
434362306a36Sopenharmony_ci	struct PFX_eckd_data pfxdata;
434462306a36Sopenharmony_ci	struct dasd_eckd_private *basepriv, *startpriv;
434562306a36Sopenharmony_ci	struct DE_eckd_data *dedata;
434662306a36Sopenharmony_ci	struct LRE_eckd_data *lredata;
434762306a36Sopenharmony_ci	struct dcw *dcw;
434862306a36Sopenharmony_ci
434962306a36Sopenharmony_ci	u32 begcyl, endcyl;
435062306a36Sopenharmony_ci	u16 heads, beghead, endhead;
435162306a36Sopenharmony_ci	u8 pfx_cmd;
435262306a36Sopenharmony_ci
435362306a36Sopenharmony_ci	int rc = 0;
435462306a36Sopenharmony_ci	int sector = 0;
435562306a36Sopenharmony_ci	int dn, d;
435662306a36Sopenharmony_ci
435762306a36Sopenharmony_ci
435862306a36Sopenharmony_ci	/* setup prefix data */
435962306a36Sopenharmony_ci	basepriv = basedev->private;
436062306a36Sopenharmony_ci	startpriv = startdev->private;
436162306a36Sopenharmony_ci	dedata = &pfxdata.define_extent;
436262306a36Sopenharmony_ci	lredata = &pfxdata.locate_record;
436362306a36Sopenharmony_ci
436462306a36Sopenharmony_ci	memset(&pfxdata, 0, sizeof(pfxdata));
436562306a36Sopenharmony_ci	pfxdata.format = 1; /* PFX with LRE */
436662306a36Sopenharmony_ci	pfxdata.base_address = basepriv->conf.ned->unit_addr;
436762306a36Sopenharmony_ci	pfxdata.base_lss = basepriv->conf.ned->ID;
436862306a36Sopenharmony_ci	pfxdata.validity.define_extent = 1;
436962306a36Sopenharmony_ci
437062306a36Sopenharmony_ci	/* private uid is kept up to date, conf_data may be outdated */
437162306a36Sopenharmony_ci	if (startpriv->uid.type == UA_BASE_PAV_ALIAS)
437262306a36Sopenharmony_ci		pfxdata.validity.verify_base = 1;
437362306a36Sopenharmony_ci
437462306a36Sopenharmony_ci	if (startpriv->uid.type == UA_HYPER_PAV_ALIAS) {
437562306a36Sopenharmony_ci		pfxdata.validity.verify_base = 1;
437662306a36Sopenharmony_ci		pfxdata.validity.hyper_pav = 1;
437762306a36Sopenharmony_ci	}
437862306a36Sopenharmony_ci
437962306a36Sopenharmony_ci	switch (cmd) {
438062306a36Sopenharmony_ci	case DASD_ECKD_CCW_READ_TRACK_DATA:
438162306a36Sopenharmony_ci		dedata->mask.perm = 0x1;
438262306a36Sopenharmony_ci		dedata->attributes.operation = basepriv->attrib.operation;
438362306a36Sopenharmony_ci		dedata->blk_size = blksize;
438462306a36Sopenharmony_ci		dedata->ga_extended |= 0x42;
438562306a36Sopenharmony_ci		lredata->operation.orientation = 0x0;
438662306a36Sopenharmony_ci		lredata->operation.operation = 0x0C;
438762306a36Sopenharmony_ci		lredata->auxiliary.check_bytes = 0x01;
438862306a36Sopenharmony_ci		pfx_cmd = DASD_ECKD_CCW_PFX_READ;
438962306a36Sopenharmony_ci		break;
439062306a36Sopenharmony_ci	case DASD_ECKD_CCW_WRITE_TRACK_DATA:
439162306a36Sopenharmony_ci		dedata->mask.perm = 0x02;
439262306a36Sopenharmony_ci		dedata->attributes.operation = basepriv->attrib.operation;
439362306a36Sopenharmony_ci		dedata->blk_size = blksize;
439462306a36Sopenharmony_ci		rc = set_timestamp(NULL, dedata, basedev);
439562306a36Sopenharmony_ci		dedata->ga_extended |= 0x42;
439662306a36Sopenharmony_ci		lredata->operation.orientation = 0x0;
439762306a36Sopenharmony_ci		lredata->operation.operation = 0x3F;
439862306a36Sopenharmony_ci		lredata->extended_operation = 0x23;
439962306a36Sopenharmony_ci		lredata->auxiliary.check_bytes = 0x2;
440062306a36Sopenharmony_ci		/*
440162306a36Sopenharmony_ci		 * If XRC is supported the System Time Stamp is set. The
440262306a36Sopenharmony_ci		 * validity of the time stamp must be reflected in the prefix
440362306a36Sopenharmony_ci		 * data as well.
440462306a36Sopenharmony_ci		 */
440562306a36Sopenharmony_ci		if (dedata->ga_extended & 0x08 && dedata->ga_extended & 0x02)
440662306a36Sopenharmony_ci			pfxdata.validity.time_stamp = 1; /* 'Time Stamp Valid' */
440762306a36Sopenharmony_ci		pfx_cmd = DASD_ECKD_CCW_PFX;
440862306a36Sopenharmony_ci		break;
440962306a36Sopenharmony_ci	case DASD_ECKD_CCW_READ_COUNT_MT:
441062306a36Sopenharmony_ci		dedata->mask.perm = 0x1;
441162306a36Sopenharmony_ci		dedata->attributes.operation = DASD_BYPASS_CACHE;
441262306a36Sopenharmony_ci		dedata->ga_extended |= 0x42;
441362306a36Sopenharmony_ci		dedata->blk_size = blksize;
441462306a36Sopenharmony_ci		lredata->operation.orientation = 0x2;
441562306a36Sopenharmony_ci		lredata->operation.operation = 0x16;
441662306a36Sopenharmony_ci		lredata->auxiliary.check_bytes = 0x01;
441762306a36Sopenharmony_ci		pfx_cmd = DASD_ECKD_CCW_PFX_READ;
441862306a36Sopenharmony_ci		break;
441962306a36Sopenharmony_ci	default:
442062306a36Sopenharmony_ci		DBF_DEV_EVENT(DBF_ERR, basedev,
442162306a36Sopenharmony_ci			      "prepare itcw, unknown opcode 0x%x", cmd);
442262306a36Sopenharmony_ci		BUG();
442362306a36Sopenharmony_ci		break;
442462306a36Sopenharmony_ci	}
442562306a36Sopenharmony_ci	if (rc)
442662306a36Sopenharmony_ci		return rc;
442762306a36Sopenharmony_ci
442862306a36Sopenharmony_ci	dedata->attributes.mode = 0x3;	/* ECKD */
442962306a36Sopenharmony_ci
443062306a36Sopenharmony_ci	heads = basepriv->rdc_data.trk_per_cyl;
443162306a36Sopenharmony_ci	begcyl = trk / heads;
443262306a36Sopenharmony_ci	beghead = trk % heads;
443362306a36Sopenharmony_ci	endcyl = totrk / heads;
443462306a36Sopenharmony_ci	endhead = totrk % heads;
443562306a36Sopenharmony_ci
443662306a36Sopenharmony_ci	/* check for sequential prestage - enhance cylinder range */
443762306a36Sopenharmony_ci	if (dedata->attributes.operation == DASD_SEQ_PRESTAGE ||
443862306a36Sopenharmony_ci	    dedata->attributes.operation == DASD_SEQ_ACCESS) {
443962306a36Sopenharmony_ci
444062306a36Sopenharmony_ci		if (endcyl + basepriv->attrib.nr_cyl < basepriv->real_cyl)
444162306a36Sopenharmony_ci			endcyl += basepriv->attrib.nr_cyl;
444262306a36Sopenharmony_ci		else
444362306a36Sopenharmony_ci			endcyl = (basepriv->real_cyl - 1);
444462306a36Sopenharmony_ci	}
444562306a36Sopenharmony_ci
444662306a36Sopenharmony_ci	set_ch_t(&dedata->beg_ext, begcyl, beghead);
444762306a36Sopenharmony_ci	set_ch_t(&dedata->end_ext, endcyl, endhead);
444862306a36Sopenharmony_ci
444962306a36Sopenharmony_ci	dedata->ep_format = 0x20; /* records per track is valid */
445062306a36Sopenharmony_ci	dedata->ep_rec_per_track = blk_per_trk;
445162306a36Sopenharmony_ci
445262306a36Sopenharmony_ci	if (rec_on_trk) {
445362306a36Sopenharmony_ci		switch (basepriv->rdc_data.dev_type) {
445462306a36Sopenharmony_ci		case 0x3390:
445562306a36Sopenharmony_ci			dn = ceil_quot(blksize + 6, 232);
445662306a36Sopenharmony_ci			d = 9 + ceil_quot(blksize + 6 * (dn + 1), 34);
445762306a36Sopenharmony_ci			sector = (49 + (rec_on_trk - 1) * (10 + d)) / 8;
445862306a36Sopenharmony_ci			break;
445962306a36Sopenharmony_ci		case 0x3380:
446062306a36Sopenharmony_ci			d = 7 + ceil_quot(blksize + 12, 32);
446162306a36Sopenharmony_ci			sector = (39 + (rec_on_trk - 1) * (8 + d)) / 7;
446262306a36Sopenharmony_ci			break;
446362306a36Sopenharmony_ci		}
446462306a36Sopenharmony_ci	}
446562306a36Sopenharmony_ci
446662306a36Sopenharmony_ci	if (cmd == DASD_ECKD_CCW_READ_COUNT_MT) {
446762306a36Sopenharmony_ci		lredata->auxiliary.length_valid = 0;
446862306a36Sopenharmony_ci		lredata->auxiliary.length_scope = 0;
446962306a36Sopenharmony_ci		lredata->sector = 0xff;
447062306a36Sopenharmony_ci	} else {
447162306a36Sopenharmony_ci		lredata->auxiliary.length_valid = 1;
447262306a36Sopenharmony_ci		lredata->auxiliary.length_scope = 1;
447362306a36Sopenharmony_ci		lredata->sector = sector;
447462306a36Sopenharmony_ci	}
447562306a36Sopenharmony_ci	lredata->auxiliary.imbedded_ccw_valid = 1;
447662306a36Sopenharmony_ci	lredata->length = tlf;
447762306a36Sopenharmony_ci	lredata->imbedded_ccw = cmd;
447862306a36Sopenharmony_ci	lredata->count = count;
447962306a36Sopenharmony_ci	set_ch_t(&lredata->seek_addr, begcyl, beghead);
448062306a36Sopenharmony_ci	lredata->search_arg.cyl = lredata->seek_addr.cyl;
448162306a36Sopenharmony_ci	lredata->search_arg.head = lredata->seek_addr.head;
448262306a36Sopenharmony_ci	lredata->search_arg.record = rec_on_trk;
448362306a36Sopenharmony_ci
448462306a36Sopenharmony_ci	dcw = itcw_add_dcw(itcw, pfx_cmd, 0,
448562306a36Sopenharmony_ci		     &pfxdata, sizeof(pfxdata), total_data_size);
448662306a36Sopenharmony_ci	return PTR_ERR_OR_ZERO(dcw);
448762306a36Sopenharmony_ci}
448862306a36Sopenharmony_ci
448962306a36Sopenharmony_cistatic struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track(
449062306a36Sopenharmony_ci					       struct dasd_device *startdev,
449162306a36Sopenharmony_ci					       struct dasd_block *block,
449262306a36Sopenharmony_ci					       struct request *req,
449362306a36Sopenharmony_ci					       sector_t first_rec,
449462306a36Sopenharmony_ci					       sector_t last_rec,
449562306a36Sopenharmony_ci					       sector_t first_trk,
449662306a36Sopenharmony_ci					       sector_t last_trk,
449762306a36Sopenharmony_ci					       unsigned int first_offs,
449862306a36Sopenharmony_ci					       unsigned int last_offs,
449962306a36Sopenharmony_ci					       unsigned int blk_per_trk,
450062306a36Sopenharmony_ci					       unsigned int blksize)
450162306a36Sopenharmony_ci{
450262306a36Sopenharmony_ci	struct dasd_ccw_req *cqr;
450362306a36Sopenharmony_ci	struct req_iterator iter;
450462306a36Sopenharmony_ci	struct bio_vec bv;
450562306a36Sopenharmony_ci	char *dst;
450662306a36Sopenharmony_ci	unsigned int trkcount, ctidaw;
450762306a36Sopenharmony_ci	unsigned char cmd;
450862306a36Sopenharmony_ci	struct dasd_device *basedev;
450962306a36Sopenharmony_ci	unsigned int tlf;
451062306a36Sopenharmony_ci	struct itcw *itcw;
451162306a36Sopenharmony_ci	struct tidaw *last_tidaw = NULL;
451262306a36Sopenharmony_ci	int itcw_op;
451362306a36Sopenharmony_ci	size_t itcw_size;
451462306a36Sopenharmony_ci	u8 tidaw_flags;
451562306a36Sopenharmony_ci	unsigned int seg_len, part_len, len_to_track_end;
451662306a36Sopenharmony_ci	unsigned char new_track;
451762306a36Sopenharmony_ci	sector_t recid, trkid;
451862306a36Sopenharmony_ci	unsigned int offs;
451962306a36Sopenharmony_ci	unsigned int count, count_to_trk_end;
452062306a36Sopenharmony_ci	int ret;
452162306a36Sopenharmony_ci
452262306a36Sopenharmony_ci	basedev = block->base;
452362306a36Sopenharmony_ci	if (rq_data_dir(req) == READ) {
452462306a36Sopenharmony_ci		cmd = DASD_ECKD_CCW_READ_TRACK_DATA;
452562306a36Sopenharmony_ci		itcw_op = ITCW_OP_READ;
452662306a36Sopenharmony_ci	} else if (rq_data_dir(req) == WRITE) {
452762306a36Sopenharmony_ci		cmd = DASD_ECKD_CCW_WRITE_TRACK_DATA;
452862306a36Sopenharmony_ci		itcw_op = ITCW_OP_WRITE;
452962306a36Sopenharmony_ci	} else
453062306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
453162306a36Sopenharmony_ci
453262306a36Sopenharmony_ci	/* trackbased I/O needs address all memory via TIDAWs,
453362306a36Sopenharmony_ci	 * not just for 64 bit addresses. This allows us to map
453462306a36Sopenharmony_ci	 * each segment directly to one tidaw.
453562306a36Sopenharmony_ci	 * In the case of write requests, additional tidaws may
453662306a36Sopenharmony_ci	 * be needed when a segment crosses a track boundary.
453762306a36Sopenharmony_ci	 */
453862306a36Sopenharmony_ci	trkcount = last_trk - first_trk + 1;
453962306a36Sopenharmony_ci	ctidaw = 0;
454062306a36Sopenharmony_ci	rq_for_each_segment(bv, req, iter) {
454162306a36Sopenharmony_ci		++ctidaw;
454262306a36Sopenharmony_ci	}
454362306a36Sopenharmony_ci	if (rq_data_dir(req) == WRITE)
454462306a36Sopenharmony_ci		ctidaw += (last_trk - first_trk);
454562306a36Sopenharmony_ci
454662306a36Sopenharmony_ci	/* Allocate the ccw request. */
454762306a36Sopenharmony_ci	itcw_size = itcw_calc_size(0, ctidaw, 0);
454862306a36Sopenharmony_ci	cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 0, itcw_size, startdev,
454962306a36Sopenharmony_ci				   blk_mq_rq_to_pdu(req));
455062306a36Sopenharmony_ci	if (IS_ERR(cqr))
455162306a36Sopenharmony_ci		return cqr;
455262306a36Sopenharmony_ci
455362306a36Sopenharmony_ci	/* transfer length factor: how many bytes to read from the last track */
455462306a36Sopenharmony_ci	if (first_trk == last_trk)
455562306a36Sopenharmony_ci		tlf = last_offs - first_offs + 1;
455662306a36Sopenharmony_ci	else
455762306a36Sopenharmony_ci		tlf = last_offs + 1;
455862306a36Sopenharmony_ci	tlf *= blksize;
455962306a36Sopenharmony_ci
456062306a36Sopenharmony_ci	itcw = itcw_init(cqr->data, itcw_size, itcw_op, 0, ctidaw, 0);
456162306a36Sopenharmony_ci	if (IS_ERR(itcw)) {
456262306a36Sopenharmony_ci		ret = -EINVAL;
456362306a36Sopenharmony_ci		goto out_error;
456462306a36Sopenharmony_ci	}
456562306a36Sopenharmony_ci	cqr->cpaddr = itcw_get_tcw(itcw);
456662306a36Sopenharmony_ci	if (prepare_itcw(itcw, first_trk, last_trk,
456762306a36Sopenharmony_ci			 cmd, basedev, startdev,
456862306a36Sopenharmony_ci			 first_offs + 1,
456962306a36Sopenharmony_ci			 trkcount, blksize,
457062306a36Sopenharmony_ci			 (last_rec - first_rec + 1) * blksize,
457162306a36Sopenharmony_ci			 tlf, blk_per_trk) == -EAGAIN) {
457262306a36Sopenharmony_ci		/* Clock not in sync and XRC is enabled.
457362306a36Sopenharmony_ci		 * Try again later.
457462306a36Sopenharmony_ci		 */
457562306a36Sopenharmony_ci		ret = -EAGAIN;
457662306a36Sopenharmony_ci		goto out_error;
457762306a36Sopenharmony_ci	}
457862306a36Sopenharmony_ci	len_to_track_end = 0;
457962306a36Sopenharmony_ci	/*
458062306a36Sopenharmony_ci	 * A tidaw can address 4k of memory, but must not cross page boundaries
458162306a36Sopenharmony_ci	 * We can let the block layer handle this by setting
458262306a36Sopenharmony_ci	 * blk_queue_segment_boundary to page boundaries and
458362306a36Sopenharmony_ci	 * blk_max_segment_size to page size when setting up the request queue.
458462306a36Sopenharmony_ci	 * For write requests, a TIDAW must not cross track boundaries, because
458562306a36Sopenharmony_ci	 * we have to set the CBC flag on the last tidaw for each track.
458662306a36Sopenharmony_ci	 */
458762306a36Sopenharmony_ci	if (rq_data_dir(req) == WRITE) {
458862306a36Sopenharmony_ci		new_track = 1;
458962306a36Sopenharmony_ci		recid = first_rec;
459062306a36Sopenharmony_ci		rq_for_each_segment(bv, req, iter) {
459162306a36Sopenharmony_ci			dst = bvec_virt(&bv);
459262306a36Sopenharmony_ci			seg_len = bv.bv_len;
459362306a36Sopenharmony_ci			while (seg_len) {
459462306a36Sopenharmony_ci				if (new_track) {
459562306a36Sopenharmony_ci					trkid = recid;
459662306a36Sopenharmony_ci					offs = sector_div(trkid, blk_per_trk);
459762306a36Sopenharmony_ci					count_to_trk_end = blk_per_trk - offs;
459862306a36Sopenharmony_ci					count = min((last_rec - recid + 1),
459962306a36Sopenharmony_ci						    (sector_t)count_to_trk_end);
460062306a36Sopenharmony_ci					len_to_track_end = count * blksize;
460162306a36Sopenharmony_ci					recid += count;
460262306a36Sopenharmony_ci					new_track = 0;
460362306a36Sopenharmony_ci				}
460462306a36Sopenharmony_ci				part_len = min(seg_len, len_to_track_end);
460562306a36Sopenharmony_ci				seg_len -= part_len;
460662306a36Sopenharmony_ci				len_to_track_end -= part_len;
460762306a36Sopenharmony_ci				/* We need to end the tidaw at track end */
460862306a36Sopenharmony_ci				if (!len_to_track_end) {
460962306a36Sopenharmony_ci					new_track = 1;
461062306a36Sopenharmony_ci					tidaw_flags = TIDAW_FLAGS_INSERT_CBC;
461162306a36Sopenharmony_ci				} else
461262306a36Sopenharmony_ci					tidaw_flags = 0;
461362306a36Sopenharmony_ci				last_tidaw = itcw_add_tidaw(itcw, tidaw_flags,
461462306a36Sopenharmony_ci							    dst, part_len);
461562306a36Sopenharmony_ci				if (IS_ERR(last_tidaw)) {
461662306a36Sopenharmony_ci					ret = -EINVAL;
461762306a36Sopenharmony_ci					goto out_error;
461862306a36Sopenharmony_ci				}
461962306a36Sopenharmony_ci				dst += part_len;
462062306a36Sopenharmony_ci			}
462162306a36Sopenharmony_ci		}
462262306a36Sopenharmony_ci	} else {
462362306a36Sopenharmony_ci		rq_for_each_segment(bv, req, iter) {
462462306a36Sopenharmony_ci			dst = bvec_virt(&bv);
462562306a36Sopenharmony_ci			last_tidaw = itcw_add_tidaw(itcw, 0x00,
462662306a36Sopenharmony_ci						    dst, bv.bv_len);
462762306a36Sopenharmony_ci			if (IS_ERR(last_tidaw)) {
462862306a36Sopenharmony_ci				ret = -EINVAL;
462962306a36Sopenharmony_ci				goto out_error;
463062306a36Sopenharmony_ci			}
463162306a36Sopenharmony_ci		}
463262306a36Sopenharmony_ci	}
463362306a36Sopenharmony_ci	last_tidaw->flags |= TIDAW_FLAGS_LAST;
463462306a36Sopenharmony_ci	last_tidaw->flags &= ~TIDAW_FLAGS_INSERT_CBC;
463562306a36Sopenharmony_ci	itcw_finalize(itcw);
463662306a36Sopenharmony_ci
463762306a36Sopenharmony_ci	if (blk_noretry_request(req) ||
463862306a36Sopenharmony_ci	    block->base->features & DASD_FEATURE_FAILFAST)
463962306a36Sopenharmony_ci		set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags);
464062306a36Sopenharmony_ci	cqr->cpmode = 1;
464162306a36Sopenharmony_ci	cqr->startdev = startdev;
464262306a36Sopenharmony_ci	cqr->memdev = startdev;
464362306a36Sopenharmony_ci	cqr->block = block;
464462306a36Sopenharmony_ci	cqr->expires = startdev->default_expires * HZ;	/* default 5 minutes */
464562306a36Sopenharmony_ci	cqr->lpm = dasd_path_get_ppm(startdev);
464662306a36Sopenharmony_ci	cqr->retries = startdev->default_retries;
464762306a36Sopenharmony_ci	cqr->buildclk = get_tod_clock();
464862306a36Sopenharmony_ci	cqr->status = DASD_CQR_FILLED;
464962306a36Sopenharmony_ci
465062306a36Sopenharmony_ci	/* Set flags to suppress output for expected errors */
465162306a36Sopenharmony_ci	if (dasd_eckd_is_ese(basedev)) {
465262306a36Sopenharmony_ci		set_bit(DASD_CQR_SUPPRESS_FP, &cqr->flags);
465362306a36Sopenharmony_ci		set_bit(DASD_CQR_SUPPRESS_IL, &cqr->flags);
465462306a36Sopenharmony_ci		set_bit(DASD_CQR_SUPPRESS_NRF, &cqr->flags);
465562306a36Sopenharmony_ci	}
465662306a36Sopenharmony_ci
465762306a36Sopenharmony_ci	return cqr;
465862306a36Sopenharmony_ciout_error:
465962306a36Sopenharmony_ci	dasd_sfree_request(cqr, startdev);
466062306a36Sopenharmony_ci	return ERR_PTR(ret);
466162306a36Sopenharmony_ci}
466262306a36Sopenharmony_ci
466362306a36Sopenharmony_cistatic struct dasd_ccw_req *dasd_eckd_build_cp(struct dasd_device *startdev,
466462306a36Sopenharmony_ci					       struct dasd_block *block,
466562306a36Sopenharmony_ci					       struct request *req)
466662306a36Sopenharmony_ci{
466762306a36Sopenharmony_ci	int cmdrtd, cmdwtd;
466862306a36Sopenharmony_ci	int use_prefix;
466962306a36Sopenharmony_ci	int fcx_multitrack;
467062306a36Sopenharmony_ci	struct dasd_eckd_private *private;
467162306a36Sopenharmony_ci	struct dasd_device *basedev;
467262306a36Sopenharmony_ci	sector_t first_rec, last_rec;
467362306a36Sopenharmony_ci	sector_t first_trk, last_trk;
467462306a36Sopenharmony_ci	unsigned int first_offs, last_offs;
467562306a36Sopenharmony_ci	unsigned int blk_per_trk, blksize;
467662306a36Sopenharmony_ci	int cdlspecial;
467762306a36Sopenharmony_ci	unsigned int data_size;
467862306a36Sopenharmony_ci	struct dasd_ccw_req *cqr;
467962306a36Sopenharmony_ci
468062306a36Sopenharmony_ci	basedev = block->base;
468162306a36Sopenharmony_ci	private = basedev->private;
468262306a36Sopenharmony_ci
468362306a36Sopenharmony_ci	/* Calculate number of blocks/records per track. */
468462306a36Sopenharmony_ci	blksize = block->bp_block;
468562306a36Sopenharmony_ci	blk_per_trk = recs_per_track(&private->rdc_data, 0, blksize);
468662306a36Sopenharmony_ci	if (blk_per_trk == 0)
468762306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
468862306a36Sopenharmony_ci	/* Calculate record id of first and last block. */
468962306a36Sopenharmony_ci	first_rec = first_trk = blk_rq_pos(req) >> block->s2b_shift;
469062306a36Sopenharmony_ci	first_offs = sector_div(first_trk, blk_per_trk);
469162306a36Sopenharmony_ci	last_rec = last_trk =
469262306a36Sopenharmony_ci		(blk_rq_pos(req) + blk_rq_sectors(req) - 1) >> block->s2b_shift;
469362306a36Sopenharmony_ci	last_offs = sector_div(last_trk, blk_per_trk);
469462306a36Sopenharmony_ci	cdlspecial = (private->uses_cdl && first_rec < 2*blk_per_trk);
469562306a36Sopenharmony_ci
469662306a36Sopenharmony_ci	fcx_multitrack = private->features.feature[40] & 0x20;
469762306a36Sopenharmony_ci	data_size = blk_rq_bytes(req);
469862306a36Sopenharmony_ci	if (data_size % blksize)
469962306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
470062306a36Sopenharmony_ci	/* tpm write request add CBC data on each track boundary */
470162306a36Sopenharmony_ci	if (rq_data_dir(req) == WRITE)
470262306a36Sopenharmony_ci		data_size += (last_trk - first_trk) * 4;
470362306a36Sopenharmony_ci
470462306a36Sopenharmony_ci	/* is read track data and write track data in command mode supported? */
470562306a36Sopenharmony_ci	cmdrtd = private->features.feature[9] & 0x20;
470662306a36Sopenharmony_ci	cmdwtd = private->features.feature[12] & 0x40;
470762306a36Sopenharmony_ci	use_prefix = private->features.feature[8] & 0x01;
470862306a36Sopenharmony_ci
470962306a36Sopenharmony_ci	cqr = NULL;
471062306a36Sopenharmony_ci	if (cdlspecial || dasd_page_cache) {
471162306a36Sopenharmony_ci		/* do nothing, just fall through to the cmd mode single case */
471262306a36Sopenharmony_ci	} else if ((data_size <= private->fcx_max_data)
471362306a36Sopenharmony_ci		   && (fcx_multitrack || (first_trk == last_trk))) {
471462306a36Sopenharmony_ci		cqr = dasd_eckd_build_cp_tpm_track(startdev, block, req,
471562306a36Sopenharmony_ci						    first_rec, last_rec,
471662306a36Sopenharmony_ci						    first_trk, last_trk,
471762306a36Sopenharmony_ci						    first_offs, last_offs,
471862306a36Sopenharmony_ci						    blk_per_trk, blksize);
471962306a36Sopenharmony_ci		if (IS_ERR(cqr) && (PTR_ERR(cqr) != -EAGAIN) &&
472062306a36Sopenharmony_ci		    (PTR_ERR(cqr) != -ENOMEM))
472162306a36Sopenharmony_ci			cqr = NULL;
472262306a36Sopenharmony_ci	} else if (use_prefix &&
472362306a36Sopenharmony_ci		   (((rq_data_dir(req) == READ) && cmdrtd) ||
472462306a36Sopenharmony_ci		    ((rq_data_dir(req) == WRITE) && cmdwtd))) {
472562306a36Sopenharmony_ci		cqr = dasd_eckd_build_cp_cmd_track(startdev, block, req,
472662306a36Sopenharmony_ci						   first_rec, last_rec,
472762306a36Sopenharmony_ci						   first_trk, last_trk,
472862306a36Sopenharmony_ci						   first_offs, last_offs,
472962306a36Sopenharmony_ci						   blk_per_trk, blksize);
473062306a36Sopenharmony_ci		if (IS_ERR(cqr) && (PTR_ERR(cqr) != -EAGAIN) &&
473162306a36Sopenharmony_ci		    (PTR_ERR(cqr) != -ENOMEM))
473262306a36Sopenharmony_ci			cqr = NULL;
473362306a36Sopenharmony_ci	}
473462306a36Sopenharmony_ci	if (!cqr)
473562306a36Sopenharmony_ci		cqr = dasd_eckd_build_cp_cmd_single(startdev, block, req,
473662306a36Sopenharmony_ci						    first_rec, last_rec,
473762306a36Sopenharmony_ci						    first_trk, last_trk,
473862306a36Sopenharmony_ci						    first_offs, last_offs,
473962306a36Sopenharmony_ci						    blk_per_trk, blksize);
474062306a36Sopenharmony_ci	return cqr;
474162306a36Sopenharmony_ci}
474262306a36Sopenharmony_ci
474362306a36Sopenharmony_cistatic struct dasd_ccw_req *dasd_eckd_build_cp_raw(struct dasd_device *startdev,
474462306a36Sopenharmony_ci						   struct dasd_block *block,
474562306a36Sopenharmony_ci						   struct request *req)
474662306a36Sopenharmony_ci{
474762306a36Sopenharmony_ci	sector_t start_padding_sectors, end_sector_offset, end_padding_sectors;
474862306a36Sopenharmony_ci	unsigned int seg_len, len_to_track_end;
474962306a36Sopenharmony_ci	unsigned int cidaw, cplength, datasize;
475062306a36Sopenharmony_ci	sector_t first_trk, last_trk, sectors;
475162306a36Sopenharmony_ci	struct dasd_eckd_private *base_priv;
475262306a36Sopenharmony_ci	struct dasd_device *basedev;
475362306a36Sopenharmony_ci	struct req_iterator iter;
475462306a36Sopenharmony_ci	struct dasd_ccw_req *cqr;
475562306a36Sopenharmony_ci	unsigned int trkcount;
475662306a36Sopenharmony_ci	unsigned long *idaws;
475762306a36Sopenharmony_ci	unsigned int size;
475862306a36Sopenharmony_ci	unsigned char cmd;
475962306a36Sopenharmony_ci	struct bio_vec bv;
476062306a36Sopenharmony_ci	struct ccw1 *ccw;
476162306a36Sopenharmony_ci	int use_prefix;
476262306a36Sopenharmony_ci	void *data;
476362306a36Sopenharmony_ci	char *dst;
476462306a36Sopenharmony_ci
476562306a36Sopenharmony_ci	/*
476662306a36Sopenharmony_ci	 * raw track access needs to be mutiple of 64k and on 64k boundary
476762306a36Sopenharmony_ci	 * For read requests we can fix an incorrect alignment by padding
476862306a36Sopenharmony_ci	 * the request with dummy pages.
476962306a36Sopenharmony_ci	 */
477062306a36Sopenharmony_ci	start_padding_sectors = blk_rq_pos(req) % DASD_RAW_SECTORS_PER_TRACK;
477162306a36Sopenharmony_ci	end_sector_offset = (blk_rq_pos(req) + blk_rq_sectors(req)) %
477262306a36Sopenharmony_ci		DASD_RAW_SECTORS_PER_TRACK;
477362306a36Sopenharmony_ci	end_padding_sectors = (DASD_RAW_SECTORS_PER_TRACK - end_sector_offset) %
477462306a36Sopenharmony_ci		DASD_RAW_SECTORS_PER_TRACK;
477562306a36Sopenharmony_ci	basedev = block->base;
477662306a36Sopenharmony_ci	if ((start_padding_sectors || end_padding_sectors) &&
477762306a36Sopenharmony_ci	    (rq_data_dir(req) == WRITE)) {
477862306a36Sopenharmony_ci		DBF_DEV_EVENT(DBF_ERR, basedev,
477962306a36Sopenharmony_ci			      "raw write not track aligned (%llu,%llu) req %p",
478062306a36Sopenharmony_ci			      start_padding_sectors, end_padding_sectors, req);
478162306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
478262306a36Sopenharmony_ci	}
478362306a36Sopenharmony_ci
478462306a36Sopenharmony_ci	first_trk = blk_rq_pos(req) / DASD_RAW_SECTORS_PER_TRACK;
478562306a36Sopenharmony_ci	last_trk = (blk_rq_pos(req) + blk_rq_sectors(req) - 1) /
478662306a36Sopenharmony_ci		DASD_RAW_SECTORS_PER_TRACK;
478762306a36Sopenharmony_ci	trkcount = last_trk - first_trk + 1;
478862306a36Sopenharmony_ci
478962306a36Sopenharmony_ci	if (rq_data_dir(req) == READ)
479062306a36Sopenharmony_ci		cmd = DASD_ECKD_CCW_READ_TRACK;
479162306a36Sopenharmony_ci	else if (rq_data_dir(req) == WRITE)
479262306a36Sopenharmony_ci		cmd = DASD_ECKD_CCW_WRITE_FULL_TRACK;
479362306a36Sopenharmony_ci	else
479462306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
479562306a36Sopenharmony_ci
479662306a36Sopenharmony_ci	/*
479762306a36Sopenharmony_ci	 * Raw track based I/O needs IDAWs for each page,
479862306a36Sopenharmony_ci	 * and not just for 64 bit addresses.
479962306a36Sopenharmony_ci	 */
480062306a36Sopenharmony_ci	cidaw = trkcount * DASD_RAW_BLOCK_PER_TRACK;
480162306a36Sopenharmony_ci
480262306a36Sopenharmony_ci	/*
480362306a36Sopenharmony_ci	 * struct PFX_eckd_data and struct LRE_eckd_data can have up to 2 bytes
480462306a36Sopenharmony_ci	 * of extended parameter. This is needed for write full track.
480562306a36Sopenharmony_ci	 */
480662306a36Sopenharmony_ci	base_priv = basedev->private;
480762306a36Sopenharmony_ci	use_prefix = base_priv->features.feature[8] & 0x01;
480862306a36Sopenharmony_ci	if (use_prefix) {
480962306a36Sopenharmony_ci		cplength = 1 + trkcount;
481062306a36Sopenharmony_ci		size = sizeof(struct PFX_eckd_data) + 2;
481162306a36Sopenharmony_ci	} else {
481262306a36Sopenharmony_ci		cplength = 2 + trkcount;
481362306a36Sopenharmony_ci		size = sizeof(struct DE_eckd_data) +
481462306a36Sopenharmony_ci			sizeof(struct LRE_eckd_data) + 2;
481562306a36Sopenharmony_ci	}
481662306a36Sopenharmony_ci	size = ALIGN(size, 8);
481762306a36Sopenharmony_ci
481862306a36Sopenharmony_ci	datasize = size + cidaw * sizeof(unsigned long);
481962306a36Sopenharmony_ci
482062306a36Sopenharmony_ci	/* Allocate the ccw request. */
482162306a36Sopenharmony_ci	cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, cplength,
482262306a36Sopenharmony_ci				   datasize, startdev, blk_mq_rq_to_pdu(req));
482362306a36Sopenharmony_ci	if (IS_ERR(cqr))
482462306a36Sopenharmony_ci		return cqr;
482562306a36Sopenharmony_ci
482662306a36Sopenharmony_ci	ccw = cqr->cpaddr;
482762306a36Sopenharmony_ci	data = cqr->data;
482862306a36Sopenharmony_ci
482962306a36Sopenharmony_ci	if (use_prefix) {
483062306a36Sopenharmony_ci		prefix_LRE(ccw++, data, first_trk, last_trk, cmd, basedev,
483162306a36Sopenharmony_ci			   startdev, 1, 0, trkcount, 0, 0);
483262306a36Sopenharmony_ci	} else {
483362306a36Sopenharmony_ci		define_extent(ccw++, data, first_trk, last_trk, cmd, basedev, 0);
483462306a36Sopenharmony_ci		ccw[-1].flags |= CCW_FLAG_CC;
483562306a36Sopenharmony_ci
483662306a36Sopenharmony_ci		data += sizeof(struct DE_eckd_data);
483762306a36Sopenharmony_ci		locate_record_ext(ccw++, data, first_trk, 0,
483862306a36Sopenharmony_ci				  trkcount, cmd, basedev, 0, 0);
483962306a36Sopenharmony_ci	}
484062306a36Sopenharmony_ci
484162306a36Sopenharmony_ci	idaws = (unsigned long *)(cqr->data + size);
484262306a36Sopenharmony_ci	len_to_track_end = 0;
484362306a36Sopenharmony_ci	if (start_padding_sectors) {
484462306a36Sopenharmony_ci		ccw[-1].flags |= CCW_FLAG_CC;
484562306a36Sopenharmony_ci		ccw->cmd_code = cmd;
484662306a36Sopenharmony_ci		/* maximum 3390 track size */
484762306a36Sopenharmony_ci		ccw->count = 57326;
484862306a36Sopenharmony_ci		/* 64k map to one track */
484962306a36Sopenharmony_ci		len_to_track_end = 65536 - start_padding_sectors * 512;
485062306a36Sopenharmony_ci		ccw->cda = (__u32)virt_to_phys(idaws);
485162306a36Sopenharmony_ci		ccw->flags |= CCW_FLAG_IDA;
485262306a36Sopenharmony_ci		ccw->flags |= CCW_FLAG_SLI;
485362306a36Sopenharmony_ci		ccw++;
485462306a36Sopenharmony_ci		for (sectors = 0; sectors < start_padding_sectors; sectors += 8)
485562306a36Sopenharmony_ci			idaws = idal_create_words(idaws, rawpadpage, PAGE_SIZE);
485662306a36Sopenharmony_ci	}
485762306a36Sopenharmony_ci	rq_for_each_segment(bv, req, iter) {
485862306a36Sopenharmony_ci		dst = bvec_virt(&bv);
485962306a36Sopenharmony_ci		seg_len = bv.bv_len;
486062306a36Sopenharmony_ci		if (cmd == DASD_ECKD_CCW_READ_TRACK)
486162306a36Sopenharmony_ci			memset(dst, 0, seg_len);
486262306a36Sopenharmony_ci		if (!len_to_track_end) {
486362306a36Sopenharmony_ci			ccw[-1].flags |= CCW_FLAG_CC;
486462306a36Sopenharmony_ci			ccw->cmd_code = cmd;
486562306a36Sopenharmony_ci			/* maximum 3390 track size */
486662306a36Sopenharmony_ci			ccw->count = 57326;
486762306a36Sopenharmony_ci			/* 64k map to one track */
486862306a36Sopenharmony_ci			len_to_track_end = 65536;
486962306a36Sopenharmony_ci			ccw->cda = (__u32)virt_to_phys(idaws);
487062306a36Sopenharmony_ci			ccw->flags |= CCW_FLAG_IDA;
487162306a36Sopenharmony_ci			ccw->flags |= CCW_FLAG_SLI;
487262306a36Sopenharmony_ci			ccw++;
487362306a36Sopenharmony_ci		}
487462306a36Sopenharmony_ci		len_to_track_end -= seg_len;
487562306a36Sopenharmony_ci		idaws = idal_create_words(idaws, dst, seg_len);
487662306a36Sopenharmony_ci	}
487762306a36Sopenharmony_ci	for (sectors = 0; sectors < end_padding_sectors; sectors += 8)
487862306a36Sopenharmony_ci		idaws = idal_create_words(idaws, rawpadpage, PAGE_SIZE);
487962306a36Sopenharmony_ci	if (blk_noretry_request(req) ||
488062306a36Sopenharmony_ci	    block->base->features & DASD_FEATURE_FAILFAST)
488162306a36Sopenharmony_ci		set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags);
488262306a36Sopenharmony_ci	cqr->startdev = startdev;
488362306a36Sopenharmony_ci	cqr->memdev = startdev;
488462306a36Sopenharmony_ci	cqr->block = block;
488562306a36Sopenharmony_ci	cqr->expires = startdev->default_expires * HZ;
488662306a36Sopenharmony_ci	cqr->lpm = dasd_path_get_ppm(startdev);
488762306a36Sopenharmony_ci	cqr->retries = startdev->default_retries;
488862306a36Sopenharmony_ci	cqr->buildclk = get_tod_clock();
488962306a36Sopenharmony_ci	cqr->status = DASD_CQR_FILLED;
489062306a36Sopenharmony_ci
489162306a36Sopenharmony_ci	return cqr;
489262306a36Sopenharmony_ci}
489362306a36Sopenharmony_ci
489462306a36Sopenharmony_ci
489562306a36Sopenharmony_cistatic int
489662306a36Sopenharmony_cidasd_eckd_free_cp(struct dasd_ccw_req *cqr, struct request *req)
489762306a36Sopenharmony_ci{
489862306a36Sopenharmony_ci	struct dasd_eckd_private *private;
489962306a36Sopenharmony_ci	struct ccw1 *ccw;
490062306a36Sopenharmony_ci	struct req_iterator iter;
490162306a36Sopenharmony_ci	struct bio_vec bv;
490262306a36Sopenharmony_ci	char *dst, *cda;
490362306a36Sopenharmony_ci	unsigned int blksize, blk_per_trk, off;
490462306a36Sopenharmony_ci	sector_t recid;
490562306a36Sopenharmony_ci	int status;
490662306a36Sopenharmony_ci
490762306a36Sopenharmony_ci	if (!dasd_page_cache)
490862306a36Sopenharmony_ci		goto out;
490962306a36Sopenharmony_ci	private = cqr->block->base->private;
491062306a36Sopenharmony_ci	blksize = cqr->block->bp_block;
491162306a36Sopenharmony_ci	blk_per_trk = recs_per_track(&private->rdc_data, 0, blksize);
491262306a36Sopenharmony_ci	recid = blk_rq_pos(req) >> cqr->block->s2b_shift;
491362306a36Sopenharmony_ci	ccw = cqr->cpaddr;
491462306a36Sopenharmony_ci	/* Skip over define extent & locate record. */
491562306a36Sopenharmony_ci	ccw++;
491662306a36Sopenharmony_ci	if (private->uses_cdl == 0 || recid > 2*blk_per_trk)
491762306a36Sopenharmony_ci		ccw++;
491862306a36Sopenharmony_ci	rq_for_each_segment(bv, req, iter) {
491962306a36Sopenharmony_ci		dst = bvec_virt(&bv);
492062306a36Sopenharmony_ci		for (off = 0; off < bv.bv_len; off += blksize) {
492162306a36Sopenharmony_ci			/* Skip locate record. */
492262306a36Sopenharmony_ci			if (private->uses_cdl && recid <= 2*blk_per_trk)
492362306a36Sopenharmony_ci				ccw++;
492462306a36Sopenharmony_ci			if (dst) {
492562306a36Sopenharmony_ci				if (ccw->flags & CCW_FLAG_IDA)
492662306a36Sopenharmony_ci					cda = *((char **)phys_to_virt(ccw->cda));
492762306a36Sopenharmony_ci				else
492862306a36Sopenharmony_ci					cda = phys_to_virt(ccw->cda);
492962306a36Sopenharmony_ci				if (dst != cda) {
493062306a36Sopenharmony_ci					if (rq_data_dir(req) == READ)
493162306a36Sopenharmony_ci						memcpy(dst, cda, bv.bv_len);
493262306a36Sopenharmony_ci					kmem_cache_free(dasd_page_cache,
493362306a36Sopenharmony_ci					    (void *)((addr_t)cda & PAGE_MASK));
493462306a36Sopenharmony_ci				}
493562306a36Sopenharmony_ci				dst = NULL;
493662306a36Sopenharmony_ci			}
493762306a36Sopenharmony_ci			ccw++;
493862306a36Sopenharmony_ci			recid++;
493962306a36Sopenharmony_ci		}
494062306a36Sopenharmony_ci	}
494162306a36Sopenharmony_ciout:
494262306a36Sopenharmony_ci	status = cqr->status == DASD_CQR_DONE;
494362306a36Sopenharmony_ci	dasd_sfree_request(cqr, cqr->memdev);
494462306a36Sopenharmony_ci	return status;
494562306a36Sopenharmony_ci}
494662306a36Sopenharmony_ci
494762306a36Sopenharmony_ci/*
494862306a36Sopenharmony_ci * Modify ccw/tcw in cqr so it can be started on a base device.
494962306a36Sopenharmony_ci *
495062306a36Sopenharmony_ci * Note that this is not enough to restart the cqr!
495162306a36Sopenharmony_ci * Either reset cqr->startdev as well (summary unit check handling)
495262306a36Sopenharmony_ci * or restart via separate cqr (as in ERP handling).
495362306a36Sopenharmony_ci */
495462306a36Sopenharmony_civoid dasd_eckd_reset_ccw_to_base_io(struct dasd_ccw_req *cqr)
495562306a36Sopenharmony_ci{
495662306a36Sopenharmony_ci	struct ccw1 *ccw;
495762306a36Sopenharmony_ci	struct PFX_eckd_data *pfxdata;
495862306a36Sopenharmony_ci	struct tcw *tcw;
495962306a36Sopenharmony_ci	struct tccb *tccb;
496062306a36Sopenharmony_ci	struct dcw *dcw;
496162306a36Sopenharmony_ci
496262306a36Sopenharmony_ci	if (cqr->cpmode == 1) {
496362306a36Sopenharmony_ci		tcw = cqr->cpaddr;
496462306a36Sopenharmony_ci		tccb = tcw_get_tccb(tcw);
496562306a36Sopenharmony_ci		dcw = (struct dcw *)&tccb->tca[0];
496662306a36Sopenharmony_ci		pfxdata = (struct PFX_eckd_data *)&dcw->cd[0];
496762306a36Sopenharmony_ci		pfxdata->validity.verify_base = 0;
496862306a36Sopenharmony_ci		pfxdata->validity.hyper_pav = 0;
496962306a36Sopenharmony_ci	} else {
497062306a36Sopenharmony_ci		ccw = cqr->cpaddr;
497162306a36Sopenharmony_ci		pfxdata = cqr->data;
497262306a36Sopenharmony_ci		if (ccw->cmd_code == DASD_ECKD_CCW_PFX) {
497362306a36Sopenharmony_ci			pfxdata->validity.verify_base = 0;
497462306a36Sopenharmony_ci			pfxdata->validity.hyper_pav = 0;
497562306a36Sopenharmony_ci		}
497662306a36Sopenharmony_ci	}
497762306a36Sopenharmony_ci}
497862306a36Sopenharmony_ci
497962306a36Sopenharmony_ci#define DASD_ECKD_CHANQ_MAX_SIZE 4
498062306a36Sopenharmony_ci
498162306a36Sopenharmony_cistatic struct dasd_ccw_req *dasd_eckd_build_alias_cp(struct dasd_device *base,
498262306a36Sopenharmony_ci						     struct dasd_block *block,
498362306a36Sopenharmony_ci						     struct request *req)
498462306a36Sopenharmony_ci{
498562306a36Sopenharmony_ci	struct dasd_eckd_private *private;
498662306a36Sopenharmony_ci	struct dasd_device *startdev;
498762306a36Sopenharmony_ci	unsigned long flags;
498862306a36Sopenharmony_ci	struct dasd_ccw_req *cqr;
498962306a36Sopenharmony_ci
499062306a36Sopenharmony_ci	startdev = dasd_alias_get_start_dev(base);
499162306a36Sopenharmony_ci	if (!startdev)
499262306a36Sopenharmony_ci		startdev = base;
499362306a36Sopenharmony_ci	private = startdev->private;
499462306a36Sopenharmony_ci	if (private->count >= DASD_ECKD_CHANQ_MAX_SIZE)
499562306a36Sopenharmony_ci		return ERR_PTR(-EBUSY);
499662306a36Sopenharmony_ci
499762306a36Sopenharmony_ci	spin_lock_irqsave(get_ccwdev_lock(startdev->cdev), flags);
499862306a36Sopenharmony_ci	private->count++;
499962306a36Sopenharmony_ci	if ((base->features & DASD_FEATURE_USERAW))
500062306a36Sopenharmony_ci		cqr = dasd_eckd_build_cp_raw(startdev, block, req);
500162306a36Sopenharmony_ci	else
500262306a36Sopenharmony_ci		cqr = dasd_eckd_build_cp(startdev, block, req);
500362306a36Sopenharmony_ci	if (IS_ERR(cqr))
500462306a36Sopenharmony_ci		private->count--;
500562306a36Sopenharmony_ci	spin_unlock_irqrestore(get_ccwdev_lock(startdev->cdev), flags);
500662306a36Sopenharmony_ci	return cqr;
500762306a36Sopenharmony_ci}
500862306a36Sopenharmony_ci
500962306a36Sopenharmony_cistatic int dasd_eckd_free_alias_cp(struct dasd_ccw_req *cqr,
501062306a36Sopenharmony_ci				   struct request *req)
501162306a36Sopenharmony_ci{
501262306a36Sopenharmony_ci	struct dasd_eckd_private *private;
501362306a36Sopenharmony_ci	unsigned long flags;
501462306a36Sopenharmony_ci
501562306a36Sopenharmony_ci	spin_lock_irqsave(get_ccwdev_lock(cqr->memdev->cdev), flags);
501662306a36Sopenharmony_ci	private = cqr->memdev->private;
501762306a36Sopenharmony_ci	private->count--;
501862306a36Sopenharmony_ci	spin_unlock_irqrestore(get_ccwdev_lock(cqr->memdev->cdev), flags);
501962306a36Sopenharmony_ci	return dasd_eckd_free_cp(cqr, req);
502062306a36Sopenharmony_ci}
502162306a36Sopenharmony_ci
502262306a36Sopenharmony_cistatic int
502362306a36Sopenharmony_cidasd_eckd_fill_info(struct dasd_device * device,
502462306a36Sopenharmony_ci		    struct dasd_information2_t * info)
502562306a36Sopenharmony_ci{
502662306a36Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
502762306a36Sopenharmony_ci
502862306a36Sopenharmony_ci	info->label_block = 2;
502962306a36Sopenharmony_ci	info->FBA_layout = private->uses_cdl ? 0 : 1;
503062306a36Sopenharmony_ci	info->format = private->uses_cdl ? DASD_FORMAT_CDL : DASD_FORMAT_LDL;
503162306a36Sopenharmony_ci	info->characteristics_size = sizeof(private->rdc_data);
503262306a36Sopenharmony_ci	memcpy(info->characteristics, &private->rdc_data,
503362306a36Sopenharmony_ci	       sizeof(private->rdc_data));
503462306a36Sopenharmony_ci	info->confdata_size = min_t(unsigned long, private->conf.len,
503562306a36Sopenharmony_ci				    sizeof(info->configuration_data));
503662306a36Sopenharmony_ci	memcpy(info->configuration_data, private->conf.data,
503762306a36Sopenharmony_ci	       info->confdata_size);
503862306a36Sopenharmony_ci	return 0;
503962306a36Sopenharmony_ci}
504062306a36Sopenharmony_ci
504162306a36Sopenharmony_ci/*
504262306a36Sopenharmony_ci * SECTION: ioctl functions for eckd devices.
504362306a36Sopenharmony_ci */
504462306a36Sopenharmony_ci
504562306a36Sopenharmony_ci/*
504662306a36Sopenharmony_ci * Release device ioctl.
504762306a36Sopenharmony_ci * Buils a channel programm to releases a prior reserved
504862306a36Sopenharmony_ci * (see dasd_eckd_reserve) device.
504962306a36Sopenharmony_ci */
505062306a36Sopenharmony_cistatic int
505162306a36Sopenharmony_cidasd_eckd_release(struct dasd_device *device)
505262306a36Sopenharmony_ci{
505362306a36Sopenharmony_ci	struct dasd_ccw_req *cqr;
505462306a36Sopenharmony_ci	int rc;
505562306a36Sopenharmony_ci	struct ccw1 *ccw;
505662306a36Sopenharmony_ci	int useglobal;
505762306a36Sopenharmony_ci
505862306a36Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
505962306a36Sopenharmony_ci		return -EACCES;
506062306a36Sopenharmony_ci
506162306a36Sopenharmony_ci	useglobal = 0;
506262306a36Sopenharmony_ci	cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1, 32, device, NULL);
506362306a36Sopenharmony_ci	if (IS_ERR(cqr)) {
506462306a36Sopenharmony_ci		mutex_lock(&dasd_reserve_mutex);
506562306a36Sopenharmony_ci		useglobal = 1;
506662306a36Sopenharmony_ci		cqr = &dasd_reserve_req->cqr;
506762306a36Sopenharmony_ci		memset(cqr, 0, sizeof(*cqr));
506862306a36Sopenharmony_ci		memset(&dasd_reserve_req->ccw, 0,
506962306a36Sopenharmony_ci		       sizeof(dasd_reserve_req->ccw));
507062306a36Sopenharmony_ci		cqr->cpaddr = &dasd_reserve_req->ccw;
507162306a36Sopenharmony_ci		cqr->data = &dasd_reserve_req->data;
507262306a36Sopenharmony_ci		cqr->magic = DASD_ECKD_MAGIC;
507362306a36Sopenharmony_ci	}
507462306a36Sopenharmony_ci	ccw = cqr->cpaddr;
507562306a36Sopenharmony_ci	ccw->cmd_code = DASD_ECKD_CCW_RELEASE;
507662306a36Sopenharmony_ci	ccw->flags |= CCW_FLAG_SLI;
507762306a36Sopenharmony_ci	ccw->count = 32;
507862306a36Sopenharmony_ci	ccw->cda = (__u32)virt_to_phys(cqr->data);
507962306a36Sopenharmony_ci	cqr->startdev = device;
508062306a36Sopenharmony_ci	cqr->memdev = device;
508162306a36Sopenharmony_ci	clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
508262306a36Sopenharmony_ci	set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags);
508362306a36Sopenharmony_ci	cqr->retries = 2;	/* set retry counter to enable basic ERP */
508462306a36Sopenharmony_ci	cqr->expires = 2 * HZ;
508562306a36Sopenharmony_ci	cqr->buildclk = get_tod_clock();
508662306a36Sopenharmony_ci	cqr->status = DASD_CQR_FILLED;
508762306a36Sopenharmony_ci
508862306a36Sopenharmony_ci	rc = dasd_sleep_on_immediatly(cqr);
508962306a36Sopenharmony_ci	if (!rc)
509062306a36Sopenharmony_ci		clear_bit(DASD_FLAG_IS_RESERVED, &device->flags);
509162306a36Sopenharmony_ci
509262306a36Sopenharmony_ci	if (useglobal)
509362306a36Sopenharmony_ci		mutex_unlock(&dasd_reserve_mutex);
509462306a36Sopenharmony_ci	else
509562306a36Sopenharmony_ci		dasd_sfree_request(cqr, cqr->memdev);
509662306a36Sopenharmony_ci	return rc;
509762306a36Sopenharmony_ci}
509862306a36Sopenharmony_ci
509962306a36Sopenharmony_ci/*
510062306a36Sopenharmony_ci * Reserve device ioctl.
510162306a36Sopenharmony_ci * Options are set to 'synchronous wait for interrupt' and
510262306a36Sopenharmony_ci * 'timeout the request'. This leads to a terminate IO if
510362306a36Sopenharmony_ci * the interrupt is outstanding for a certain time.
510462306a36Sopenharmony_ci */
510562306a36Sopenharmony_cistatic int
510662306a36Sopenharmony_cidasd_eckd_reserve(struct dasd_device *device)
510762306a36Sopenharmony_ci{
510862306a36Sopenharmony_ci	struct dasd_ccw_req *cqr;
510962306a36Sopenharmony_ci	int rc;
511062306a36Sopenharmony_ci	struct ccw1 *ccw;
511162306a36Sopenharmony_ci	int useglobal;
511262306a36Sopenharmony_ci
511362306a36Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
511462306a36Sopenharmony_ci		return -EACCES;
511562306a36Sopenharmony_ci
511662306a36Sopenharmony_ci	useglobal = 0;
511762306a36Sopenharmony_ci	cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1, 32, device, NULL);
511862306a36Sopenharmony_ci	if (IS_ERR(cqr)) {
511962306a36Sopenharmony_ci		mutex_lock(&dasd_reserve_mutex);
512062306a36Sopenharmony_ci		useglobal = 1;
512162306a36Sopenharmony_ci		cqr = &dasd_reserve_req->cqr;
512262306a36Sopenharmony_ci		memset(cqr, 0, sizeof(*cqr));
512362306a36Sopenharmony_ci		memset(&dasd_reserve_req->ccw, 0,
512462306a36Sopenharmony_ci		       sizeof(dasd_reserve_req->ccw));
512562306a36Sopenharmony_ci		cqr->cpaddr = &dasd_reserve_req->ccw;
512662306a36Sopenharmony_ci		cqr->data = &dasd_reserve_req->data;
512762306a36Sopenharmony_ci		cqr->magic = DASD_ECKD_MAGIC;
512862306a36Sopenharmony_ci	}
512962306a36Sopenharmony_ci	ccw = cqr->cpaddr;
513062306a36Sopenharmony_ci	ccw->cmd_code = DASD_ECKD_CCW_RESERVE;
513162306a36Sopenharmony_ci	ccw->flags |= CCW_FLAG_SLI;
513262306a36Sopenharmony_ci	ccw->count = 32;
513362306a36Sopenharmony_ci	ccw->cda = (__u32)virt_to_phys(cqr->data);
513462306a36Sopenharmony_ci	cqr->startdev = device;
513562306a36Sopenharmony_ci	cqr->memdev = device;
513662306a36Sopenharmony_ci	clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
513762306a36Sopenharmony_ci	set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags);
513862306a36Sopenharmony_ci	cqr->retries = 2;	/* set retry counter to enable basic ERP */
513962306a36Sopenharmony_ci	cqr->expires = 2 * HZ;
514062306a36Sopenharmony_ci	cqr->buildclk = get_tod_clock();
514162306a36Sopenharmony_ci	cqr->status = DASD_CQR_FILLED;
514262306a36Sopenharmony_ci
514362306a36Sopenharmony_ci	rc = dasd_sleep_on_immediatly(cqr);
514462306a36Sopenharmony_ci	if (!rc)
514562306a36Sopenharmony_ci		set_bit(DASD_FLAG_IS_RESERVED, &device->flags);
514662306a36Sopenharmony_ci
514762306a36Sopenharmony_ci	if (useglobal)
514862306a36Sopenharmony_ci		mutex_unlock(&dasd_reserve_mutex);
514962306a36Sopenharmony_ci	else
515062306a36Sopenharmony_ci		dasd_sfree_request(cqr, cqr->memdev);
515162306a36Sopenharmony_ci	return rc;
515262306a36Sopenharmony_ci}
515362306a36Sopenharmony_ci
515462306a36Sopenharmony_ci/*
515562306a36Sopenharmony_ci * Steal lock ioctl - unconditional reserve device.
515662306a36Sopenharmony_ci * Buils a channel programm to break a device's reservation.
515762306a36Sopenharmony_ci * (unconditional reserve)
515862306a36Sopenharmony_ci */
515962306a36Sopenharmony_cistatic int
516062306a36Sopenharmony_cidasd_eckd_steal_lock(struct dasd_device *device)
516162306a36Sopenharmony_ci{
516262306a36Sopenharmony_ci	struct dasd_ccw_req *cqr;
516362306a36Sopenharmony_ci	int rc;
516462306a36Sopenharmony_ci	struct ccw1 *ccw;
516562306a36Sopenharmony_ci	int useglobal;
516662306a36Sopenharmony_ci
516762306a36Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
516862306a36Sopenharmony_ci		return -EACCES;
516962306a36Sopenharmony_ci
517062306a36Sopenharmony_ci	useglobal = 0;
517162306a36Sopenharmony_ci	cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1, 32, device, NULL);
517262306a36Sopenharmony_ci	if (IS_ERR(cqr)) {
517362306a36Sopenharmony_ci		mutex_lock(&dasd_reserve_mutex);
517462306a36Sopenharmony_ci		useglobal = 1;
517562306a36Sopenharmony_ci		cqr = &dasd_reserve_req->cqr;
517662306a36Sopenharmony_ci		memset(cqr, 0, sizeof(*cqr));
517762306a36Sopenharmony_ci		memset(&dasd_reserve_req->ccw, 0,
517862306a36Sopenharmony_ci		       sizeof(dasd_reserve_req->ccw));
517962306a36Sopenharmony_ci		cqr->cpaddr = &dasd_reserve_req->ccw;
518062306a36Sopenharmony_ci		cqr->data = &dasd_reserve_req->data;
518162306a36Sopenharmony_ci		cqr->magic = DASD_ECKD_MAGIC;
518262306a36Sopenharmony_ci	}
518362306a36Sopenharmony_ci	ccw = cqr->cpaddr;
518462306a36Sopenharmony_ci	ccw->cmd_code = DASD_ECKD_CCW_SLCK;
518562306a36Sopenharmony_ci	ccw->flags |= CCW_FLAG_SLI;
518662306a36Sopenharmony_ci	ccw->count = 32;
518762306a36Sopenharmony_ci	ccw->cda = (__u32)virt_to_phys(cqr->data);
518862306a36Sopenharmony_ci	cqr->startdev = device;
518962306a36Sopenharmony_ci	cqr->memdev = device;
519062306a36Sopenharmony_ci	clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
519162306a36Sopenharmony_ci	set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags);
519262306a36Sopenharmony_ci	cqr->retries = 2;	/* set retry counter to enable basic ERP */
519362306a36Sopenharmony_ci	cqr->expires = 2 * HZ;
519462306a36Sopenharmony_ci	cqr->buildclk = get_tod_clock();
519562306a36Sopenharmony_ci	cqr->status = DASD_CQR_FILLED;
519662306a36Sopenharmony_ci
519762306a36Sopenharmony_ci	rc = dasd_sleep_on_immediatly(cqr);
519862306a36Sopenharmony_ci	if (!rc)
519962306a36Sopenharmony_ci		set_bit(DASD_FLAG_IS_RESERVED, &device->flags);
520062306a36Sopenharmony_ci
520162306a36Sopenharmony_ci	if (useglobal)
520262306a36Sopenharmony_ci		mutex_unlock(&dasd_reserve_mutex);
520362306a36Sopenharmony_ci	else
520462306a36Sopenharmony_ci		dasd_sfree_request(cqr, cqr->memdev);
520562306a36Sopenharmony_ci	return rc;
520662306a36Sopenharmony_ci}
520762306a36Sopenharmony_ci
520862306a36Sopenharmony_ci/*
520962306a36Sopenharmony_ci * SNID - Sense Path Group ID
521062306a36Sopenharmony_ci * This ioctl may be used in situations where I/O is stalled due to
521162306a36Sopenharmony_ci * a reserve, so if the normal dasd_smalloc_request fails, we use the
521262306a36Sopenharmony_ci * preallocated dasd_reserve_req.
521362306a36Sopenharmony_ci */
521462306a36Sopenharmony_cistatic int dasd_eckd_snid(struct dasd_device *device,
521562306a36Sopenharmony_ci			  void __user *argp)
521662306a36Sopenharmony_ci{
521762306a36Sopenharmony_ci	struct dasd_ccw_req *cqr;
521862306a36Sopenharmony_ci	int rc;
521962306a36Sopenharmony_ci	struct ccw1 *ccw;
522062306a36Sopenharmony_ci	int useglobal;
522162306a36Sopenharmony_ci	struct dasd_snid_ioctl_data usrparm;
522262306a36Sopenharmony_ci
522362306a36Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
522462306a36Sopenharmony_ci		return -EACCES;
522562306a36Sopenharmony_ci
522662306a36Sopenharmony_ci	if (copy_from_user(&usrparm, argp, sizeof(usrparm)))
522762306a36Sopenharmony_ci		return -EFAULT;
522862306a36Sopenharmony_ci
522962306a36Sopenharmony_ci	useglobal = 0;
523062306a36Sopenharmony_ci	cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1,
523162306a36Sopenharmony_ci				   sizeof(struct dasd_snid_data), device,
523262306a36Sopenharmony_ci				   NULL);
523362306a36Sopenharmony_ci	if (IS_ERR(cqr)) {
523462306a36Sopenharmony_ci		mutex_lock(&dasd_reserve_mutex);
523562306a36Sopenharmony_ci		useglobal = 1;
523662306a36Sopenharmony_ci		cqr = &dasd_reserve_req->cqr;
523762306a36Sopenharmony_ci		memset(cqr, 0, sizeof(*cqr));
523862306a36Sopenharmony_ci		memset(&dasd_reserve_req->ccw, 0,
523962306a36Sopenharmony_ci		       sizeof(dasd_reserve_req->ccw));
524062306a36Sopenharmony_ci		cqr->cpaddr = &dasd_reserve_req->ccw;
524162306a36Sopenharmony_ci		cqr->data = &dasd_reserve_req->data;
524262306a36Sopenharmony_ci		cqr->magic = DASD_ECKD_MAGIC;
524362306a36Sopenharmony_ci	}
524462306a36Sopenharmony_ci	ccw = cqr->cpaddr;
524562306a36Sopenharmony_ci	ccw->cmd_code = DASD_ECKD_CCW_SNID;
524662306a36Sopenharmony_ci	ccw->flags |= CCW_FLAG_SLI;
524762306a36Sopenharmony_ci	ccw->count = 12;
524862306a36Sopenharmony_ci	ccw->cda = (__u32)virt_to_phys(cqr->data);
524962306a36Sopenharmony_ci	cqr->startdev = device;
525062306a36Sopenharmony_ci	cqr->memdev = device;
525162306a36Sopenharmony_ci	clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
525262306a36Sopenharmony_ci	set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags);
525362306a36Sopenharmony_ci	set_bit(DASD_CQR_ALLOW_SLOCK, &cqr->flags);
525462306a36Sopenharmony_ci	cqr->retries = 5;
525562306a36Sopenharmony_ci	cqr->expires = 10 * HZ;
525662306a36Sopenharmony_ci	cqr->buildclk = get_tod_clock();
525762306a36Sopenharmony_ci	cqr->status = DASD_CQR_FILLED;
525862306a36Sopenharmony_ci	cqr->lpm = usrparm.path_mask;
525962306a36Sopenharmony_ci
526062306a36Sopenharmony_ci	rc = dasd_sleep_on_immediatly(cqr);
526162306a36Sopenharmony_ci	/* verify that I/O processing didn't modify the path mask */
526262306a36Sopenharmony_ci	if (!rc && usrparm.path_mask && (cqr->lpm != usrparm.path_mask))
526362306a36Sopenharmony_ci		rc = -EIO;
526462306a36Sopenharmony_ci	if (!rc) {
526562306a36Sopenharmony_ci		usrparm.data = *((struct dasd_snid_data *)cqr->data);
526662306a36Sopenharmony_ci		if (copy_to_user(argp, &usrparm, sizeof(usrparm)))
526762306a36Sopenharmony_ci			rc = -EFAULT;
526862306a36Sopenharmony_ci	}
526962306a36Sopenharmony_ci
527062306a36Sopenharmony_ci	if (useglobal)
527162306a36Sopenharmony_ci		mutex_unlock(&dasd_reserve_mutex);
527262306a36Sopenharmony_ci	else
527362306a36Sopenharmony_ci		dasd_sfree_request(cqr, cqr->memdev);
527462306a36Sopenharmony_ci	return rc;
527562306a36Sopenharmony_ci}
527662306a36Sopenharmony_ci
527762306a36Sopenharmony_ci/*
527862306a36Sopenharmony_ci * Read performance statistics
527962306a36Sopenharmony_ci */
528062306a36Sopenharmony_cistatic int
528162306a36Sopenharmony_cidasd_eckd_performance(struct dasd_device *device, void __user *argp)
528262306a36Sopenharmony_ci{
528362306a36Sopenharmony_ci	struct dasd_psf_prssd_data *prssdp;
528462306a36Sopenharmony_ci	struct dasd_rssd_perf_stats_t *stats;
528562306a36Sopenharmony_ci	struct dasd_ccw_req *cqr;
528662306a36Sopenharmony_ci	struct ccw1 *ccw;
528762306a36Sopenharmony_ci	int rc;
528862306a36Sopenharmony_ci
528962306a36Sopenharmony_ci	cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */  + 1 /* RSSD */,
529062306a36Sopenharmony_ci				   (sizeof(struct dasd_psf_prssd_data) +
529162306a36Sopenharmony_ci				    sizeof(struct dasd_rssd_perf_stats_t)),
529262306a36Sopenharmony_ci				   device, NULL);
529362306a36Sopenharmony_ci	if (IS_ERR(cqr)) {
529462306a36Sopenharmony_ci		DBF_DEV_EVENT(DBF_WARNING, device, "%s",
529562306a36Sopenharmony_ci			    "Could not allocate initialization request");
529662306a36Sopenharmony_ci		return PTR_ERR(cqr);
529762306a36Sopenharmony_ci	}
529862306a36Sopenharmony_ci	cqr->startdev = device;
529962306a36Sopenharmony_ci	cqr->memdev = device;
530062306a36Sopenharmony_ci	cqr->retries = 0;
530162306a36Sopenharmony_ci	clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
530262306a36Sopenharmony_ci	cqr->expires = 10 * HZ;
530362306a36Sopenharmony_ci
530462306a36Sopenharmony_ci	/* Prepare for Read Subsystem Data */
530562306a36Sopenharmony_ci	prssdp = (struct dasd_psf_prssd_data *) cqr->data;
530662306a36Sopenharmony_ci	memset(prssdp, 0, sizeof(struct dasd_psf_prssd_data));
530762306a36Sopenharmony_ci	prssdp->order = PSF_ORDER_PRSSD;
530862306a36Sopenharmony_ci	prssdp->suborder = 0x01;	/* Performance Statistics */
530962306a36Sopenharmony_ci	prssdp->varies[1] = 0x01;	/* Perf Statistics for the Subsystem */
531062306a36Sopenharmony_ci
531162306a36Sopenharmony_ci	ccw = cqr->cpaddr;
531262306a36Sopenharmony_ci	ccw->cmd_code = DASD_ECKD_CCW_PSF;
531362306a36Sopenharmony_ci	ccw->count = sizeof(struct dasd_psf_prssd_data);
531462306a36Sopenharmony_ci	ccw->flags |= CCW_FLAG_CC;
531562306a36Sopenharmony_ci	ccw->cda = (__u32)virt_to_phys(prssdp);
531662306a36Sopenharmony_ci
531762306a36Sopenharmony_ci	/* Read Subsystem Data - Performance Statistics */
531862306a36Sopenharmony_ci	stats = (struct dasd_rssd_perf_stats_t *) (prssdp + 1);
531962306a36Sopenharmony_ci	memset(stats, 0, sizeof(struct dasd_rssd_perf_stats_t));
532062306a36Sopenharmony_ci
532162306a36Sopenharmony_ci	ccw++;
532262306a36Sopenharmony_ci	ccw->cmd_code = DASD_ECKD_CCW_RSSD;
532362306a36Sopenharmony_ci	ccw->count = sizeof(struct dasd_rssd_perf_stats_t);
532462306a36Sopenharmony_ci	ccw->cda = (__u32)virt_to_phys(stats);
532562306a36Sopenharmony_ci
532662306a36Sopenharmony_ci	cqr->buildclk = get_tod_clock();
532762306a36Sopenharmony_ci	cqr->status = DASD_CQR_FILLED;
532862306a36Sopenharmony_ci	rc = dasd_sleep_on(cqr);
532962306a36Sopenharmony_ci	if (rc == 0) {
533062306a36Sopenharmony_ci		prssdp = (struct dasd_psf_prssd_data *) cqr->data;
533162306a36Sopenharmony_ci		stats = (struct dasd_rssd_perf_stats_t *) (prssdp + 1);
533262306a36Sopenharmony_ci		if (copy_to_user(argp, stats,
533362306a36Sopenharmony_ci				 sizeof(struct dasd_rssd_perf_stats_t)))
533462306a36Sopenharmony_ci			rc = -EFAULT;
533562306a36Sopenharmony_ci	}
533662306a36Sopenharmony_ci	dasd_sfree_request(cqr, cqr->memdev);
533762306a36Sopenharmony_ci	return rc;
533862306a36Sopenharmony_ci}
533962306a36Sopenharmony_ci
534062306a36Sopenharmony_ci/*
534162306a36Sopenharmony_ci * Get attributes (cache operations)
534262306a36Sopenharmony_ci * Returnes the cache attributes used in Define Extend (DE).
534362306a36Sopenharmony_ci */
534462306a36Sopenharmony_cistatic int
534562306a36Sopenharmony_cidasd_eckd_get_attrib(struct dasd_device *device, void __user *argp)
534662306a36Sopenharmony_ci{
534762306a36Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
534862306a36Sopenharmony_ci	struct attrib_data_t attrib = private->attrib;
534962306a36Sopenharmony_ci	int rc;
535062306a36Sopenharmony_ci
535162306a36Sopenharmony_ci        if (!capable(CAP_SYS_ADMIN))
535262306a36Sopenharmony_ci                return -EACCES;
535362306a36Sopenharmony_ci	if (!argp)
535462306a36Sopenharmony_ci                return -EINVAL;
535562306a36Sopenharmony_ci
535662306a36Sopenharmony_ci	rc = 0;
535762306a36Sopenharmony_ci	if (copy_to_user(argp, (long *) &attrib,
535862306a36Sopenharmony_ci			 sizeof(struct attrib_data_t)))
535962306a36Sopenharmony_ci		rc = -EFAULT;
536062306a36Sopenharmony_ci
536162306a36Sopenharmony_ci	return rc;
536262306a36Sopenharmony_ci}
536362306a36Sopenharmony_ci
536462306a36Sopenharmony_ci/*
536562306a36Sopenharmony_ci * Set attributes (cache operations)
536662306a36Sopenharmony_ci * Stores the attributes for cache operation to be used in Define Extend (DE).
536762306a36Sopenharmony_ci */
536862306a36Sopenharmony_cistatic int
536962306a36Sopenharmony_cidasd_eckd_set_attrib(struct dasd_device *device, void __user *argp)
537062306a36Sopenharmony_ci{
537162306a36Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
537262306a36Sopenharmony_ci	struct attrib_data_t attrib;
537362306a36Sopenharmony_ci
537462306a36Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
537562306a36Sopenharmony_ci		return -EACCES;
537662306a36Sopenharmony_ci	if (!argp)
537762306a36Sopenharmony_ci		return -EINVAL;
537862306a36Sopenharmony_ci
537962306a36Sopenharmony_ci	if (copy_from_user(&attrib, argp, sizeof(struct attrib_data_t)))
538062306a36Sopenharmony_ci		return -EFAULT;
538162306a36Sopenharmony_ci	private->attrib = attrib;
538262306a36Sopenharmony_ci
538362306a36Sopenharmony_ci	dev_info(&device->cdev->dev,
538462306a36Sopenharmony_ci		 "The DASD cache mode was set to %x (%i cylinder prestage)\n",
538562306a36Sopenharmony_ci		 private->attrib.operation, private->attrib.nr_cyl);
538662306a36Sopenharmony_ci	return 0;
538762306a36Sopenharmony_ci}
538862306a36Sopenharmony_ci
538962306a36Sopenharmony_ci/*
539062306a36Sopenharmony_ci * Issue syscall I/O to EMC Symmetrix array.
539162306a36Sopenharmony_ci * CCWs are PSF and RSSD
539262306a36Sopenharmony_ci */
539362306a36Sopenharmony_cistatic int dasd_symm_io(struct dasd_device *device, void __user *argp)
539462306a36Sopenharmony_ci{
539562306a36Sopenharmony_ci	struct dasd_symmio_parms usrparm;
539662306a36Sopenharmony_ci	char *psf_data, *rssd_result;
539762306a36Sopenharmony_ci	struct dasd_ccw_req *cqr;
539862306a36Sopenharmony_ci	struct ccw1 *ccw;
539962306a36Sopenharmony_ci	char psf0, psf1;
540062306a36Sopenharmony_ci	int rc;
540162306a36Sopenharmony_ci
540262306a36Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RAWIO))
540362306a36Sopenharmony_ci		return -EACCES;
540462306a36Sopenharmony_ci	psf0 = psf1 = 0;
540562306a36Sopenharmony_ci
540662306a36Sopenharmony_ci	/* Copy parms from caller */
540762306a36Sopenharmony_ci	rc = -EFAULT;
540862306a36Sopenharmony_ci	if (copy_from_user(&usrparm, argp, sizeof(usrparm)))
540962306a36Sopenharmony_ci		goto out;
541062306a36Sopenharmony_ci	if (is_compat_task()) {
541162306a36Sopenharmony_ci		/* Make sure pointers are sane even on 31 bit. */
541262306a36Sopenharmony_ci		rc = -EINVAL;
541362306a36Sopenharmony_ci		if ((usrparm.psf_data >> 32) != 0)
541462306a36Sopenharmony_ci			goto out;
541562306a36Sopenharmony_ci		if ((usrparm.rssd_result >> 32) != 0)
541662306a36Sopenharmony_ci			goto out;
541762306a36Sopenharmony_ci		usrparm.psf_data &= 0x7fffffffULL;
541862306a36Sopenharmony_ci		usrparm.rssd_result &= 0x7fffffffULL;
541962306a36Sopenharmony_ci	}
542062306a36Sopenharmony_ci	/* at least 2 bytes are accessed and should be allocated */
542162306a36Sopenharmony_ci	if (usrparm.psf_data_len < 2) {
542262306a36Sopenharmony_ci		DBF_DEV_EVENT(DBF_WARNING, device,
542362306a36Sopenharmony_ci			      "Symmetrix ioctl invalid data length %d",
542462306a36Sopenharmony_ci			      usrparm.psf_data_len);
542562306a36Sopenharmony_ci		rc = -EINVAL;
542662306a36Sopenharmony_ci		goto out;
542762306a36Sopenharmony_ci	}
542862306a36Sopenharmony_ci	/* alloc I/O data area */
542962306a36Sopenharmony_ci	psf_data = kzalloc(usrparm.psf_data_len, GFP_KERNEL | GFP_DMA);
543062306a36Sopenharmony_ci	rssd_result = kzalloc(usrparm.rssd_result_len, GFP_KERNEL | GFP_DMA);
543162306a36Sopenharmony_ci	if (!psf_data || !rssd_result) {
543262306a36Sopenharmony_ci		rc = -ENOMEM;
543362306a36Sopenharmony_ci		goto out_free;
543462306a36Sopenharmony_ci	}
543562306a36Sopenharmony_ci
543662306a36Sopenharmony_ci	/* get syscall header from user space */
543762306a36Sopenharmony_ci	rc = -EFAULT;
543862306a36Sopenharmony_ci	if (copy_from_user(psf_data,
543962306a36Sopenharmony_ci			   (void __user *)(unsigned long) usrparm.psf_data,
544062306a36Sopenharmony_ci			   usrparm.psf_data_len))
544162306a36Sopenharmony_ci		goto out_free;
544262306a36Sopenharmony_ci	psf0 = psf_data[0];
544362306a36Sopenharmony_ci	psf1 = psf_data[1];
544462306a36Sopenharmony_ci
544562306a36Sopenharmony_ci	/* setup CCWs for PSF + RSSD */
544662306a36Sopenharmony_ci	cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 2, 0, device, NULL);
544762306a36Sopenharmony_ci	if (IS_ERR(cqr)) {
544862306a36Sopenharmony_ci		DBF_DEV_EVENT(DBF_WARNING, device, "%s",
544962306a36Sopenharmony_ci			"Could not allocate initialization request");
545062306a36Sopenharmony_ci		rc = PTR_ERR(cqr);
545162306a36Sopenharmony_ci		goto out_free;
545262306a36Sopenharmony_ci	}
545362306a36Sopenharmony_ci
545462306a36Sopenharmony_ci	cqr->startdev = device;
545562306a36Sopenharmony_ci	cqr->memdev = device;
545662306a36Sopenharmony_ci	cqr->retries = 3;
545762306a36Sopenharmony_ci	cqr->expires = 10 * HZ;
545862306a36Sopenharmony_ci	cqr->buildclk = get_tod_clock();
545962306a36Sopenharmony_ci	cqr->status = DASD_CQR_FILLED;
546062306a36Sopenharmony_ci
546162306a36Sopenharmony_ci	/* Build the ccws */
546262306a36Sopenharmony_ci	ccw = cqr->cpaddr;
546362306a36Sopenharmony_ci
546462306a36Sopenharmony_ci	/* PSF ccw */
546562306a36Sopenharmony_ci	ccw->cmd_code = DASD_ECKD_CCW_PSF;
546662306a36Sopenharmony_ci	ccw->count = usrparm.psf_data_len;
546762306a36Sopenharmony_ci	ccw->flags |= CCW_FLAG_CC;
546862306a36Sopenharmony_ci	ccw->cda = (__u32)virt_to_phys(psf_data);
546962306a36Sopenharmony_ci
547062306a36Sopenharmony_ci	ccw++;
547162306a36Sopenharmony_ci
547262306a36Sopenharmony_ci	/* RSSD ccw  */
547362306a36Sopenharmony_ci	ccw->cmd_code = DASD_ECKD_CCW_RSSD;
547462306a36Sopenharmony_ci	ccw->count = usrparm.rssd_result_len;
547562306a36Sopenharmony_ci	ccw->flags = CCW_FLAG_SLI ;
547662306a36Sopenharmony_ci	ccw->cda = (__u32)virt_to_phys(rssd_result);
547762306a36Sopenharmony_ci
547862306a36Sopenharmony_ci	rc = dasd_sleep_on(cqr);
547962306a36Sopenharmony_ci	if (rc)
548062306a36Sopenharmony_ci		goto out_sfree;
548162306a36Sopenharmony_ci
548262306a36Sopenharmony_ci	rc = -EFAULT;
548362306a36Sopenharmony_ci	if (copy_to_user((void __user *)(unsigned long) usrparm.rssd_result,
548462306a36Sopenharmony_ci			   rssd_result, usrparm.rssd_result_len))
548562306a36Sopenharmony_ci		goto out_sfree;
548662306a36Sopenharmony_ci	rc = 0;
548762306a36Sopenharmony_ci
548862306a36Sopenharmony_ciout_sfree:
548962306a36Sopenharmony_ci	dasd_sfree_request(cqr, cqr->memdev);
549062306a36Sopenharmony_ciout_free:
549162306a36Sopenharmony_ci	kfree(rssd_result);
549262306a36Sopenharmony_ci	kfree(psf_data);
549362306a36Sopenharmony_ciout:
549462306a36Sopenharmony_ci	DBF_DEV_EVENT(DBF_WARNING, device,
549562306a36Sopenharmony_ci		      "Symmetrix ioctl (0x%02x 0x%02x): rc=%d",
549662306a36Sopenharmony_ci		      (int) psf0, (int) psf1, rc);
549762306a36Sopenharmony_ci	return rc;
549862306a36Sopenharmony_ci}
549962306a36Sopenharmony_ci
550062306a36Sopenharmony_cistatic int
550162306a36Sopenharmony_cidasd_eckd_ioctl(struct dasd_block *block, unsigned int cmd, void __user *argp)
550262306a36Sopenharmony_ci{
550362306a36Sopenharmony_ci	struct dasd_device *device = block->base;
550462306a36Sopenharmony_ci
550562306a36Sopenharmony_ci	switch (cmd) {
550662306a36Sopenharmony_ci	case BIODASDGATTR:
550762306a36Sopenharmony_ci		return dasd_eckd_get_attrib(device, argp);
550862306a36Sopenharmony_ci	case BIODASDSATTR:
550962306a36Sopenharmony_ci		return dasd_eckd_set_attrib(device, argp);
551062306a36Sopenharmony_ci	case BIODASDPSRD:
551162306a36Sopenharmony_ci		return dasd_eckd_performance(device, argp);
551262306a36Sopenharmony_ci	case BIODASDRLSE:
551362306a36Sopenharmony_ci		return dasd_eckd_release(device);
551462306a36Sopenharmony_ci	case BIODASDRSRV:
551562306a36Sopenharmony_ci		return dasd_eckd_reserve(device);
551662306a36Sopenharmony_ci	case BIODASDSLCK:
551762306a36Sopenharmony_ci		return dasd_eckd_steal_lock(device);
551862306a36Sopenharmony_ci	case BIODASDSNID:
551962306a36Sopenharmony_ci		return dasd_eckd_snid(device, argp);
552062306a36Sopenharmony_ci	case BIODASDSYMMIO:
552162306a36Sopenharmony_ci		return dasd_symm_io(device, argp);
552262306a36Sopenharmony_ci	default:
552362306a36Sopenharmony_ci		return -ENOTTY;
552462306a36Sopenharmony_ci	}
552562306a36Sopenharmony_ci}
552662306a36Sopenharmony_ci
552762306a36Sopenharmony_ci/*
552862306a36Sopenharmony_ci * Dump the range of CCWs into 'page' buffer
552962306a36Sopenharmony_ci * and return number of printed chars.
553062306a36Sopenharmony_ci */
553162306a36Sopenharmony_cistatic void
553262306a36Sopenharmony_cidasd_eckd_dump_ccw_range(struct ccw1 *from, struct ccw1 *to, char *page)
553362306a36Sopenharmony_ci{
553462306a36Sopenharmony_ci	int len, count;
553562306a36Sopenharmony_ci	char *datap;
553662306a36Sopenharmony_ci
553762306a36Sopenharmony_ci	len = 0;
553862306a36Sopenharmony_ci	while (from <= to) {
553962306a36Sopenharmony_ci		len += sprintf(page + len, PRINTK_HEADER
554062306a36Sopenharmony_ci			       " CCW %p: %08X %08X DAT:",
554162306a36Sopenharmony_ci			       from, ((int *) from)[0], ((int *) from)[1]);
554262306a36Sopenharmony_ci
554362306a36Sopenharmony_ci		/* get pointer to data (consider IDALs) */
554462306a36Sopenharmony_ci		if (from->flags & CCW_FLAG_IDA)
554562306a36Sopenharmony_ci			datap = (char *)*((addr_t *)phys_to_virt(from->cda));
554662306a36Sopenharmony_ci		else
554762306a36Sopenharmony_ci			datap = phys_to_virt(from->cda);
554862306a36Sopenharmony_ci
554962306a36Sopenharmony_ci		/* dump data (max 128 bytes) */
555062306a36Sopenharmony_ci		for (count = 0; count < from->count && count < 128; count++) {
555162306a36Sopenharmony_ci			if (count % 32 == 0)
555262306a36Sopenharmony_ci				len += sprintf(page + len, "\n");
555362306a36Sopenharmony_ci			if (count % 8 == 0)
555462306a36Sopenharmony_ci				len += sprintf(page + len, " ");
555562306a36Sopenharmony_ci			if (count % 4 == 0)
555662306a36Sopenharmony_ci				len += sprintf(page + len, " ");
555762306a36Sopenharmony_ci			len += sprintf(page + len, "%02x", datap[count]);
555862306a36Sopenharmony_ci		}
555962306a36Sopenharmony_ci		len += sprintf(page + len, "\n");
556062306a36Sopenharmony_ci		from++;
556162306a36Sopenharmony_ci	}
556262306a36Sopenharmony_ci	if (len > 0)
556362306a36Sopenharmony_ci		printk(KERN_ERR "%s", page);
556462306a36Sopenharmony_ci}
556562306a36Sopenharmony_ci
556662306a36Sopenharmony_cistatic void
556762306a36Sopenharmony_cidasd_eckd_dump_sense_dbf(struct dasd_device *device, struct irb *irb,
556862306a36Sopenharmony_ci			 char *reason)
556962306a36Sopenharmony_ci{
557062306a36Sopenharmony_ci	u64 *sense;
557162306a36Sopenharmony_ci	u64 *stat;
557262306a36Sopenharmony_ci
557362306a36Sopenharmony_ci	sense = (u64 *) dasd_get_sense(irb);
557462306a36Sopenharmony_ci	stat = (u64 *) &irb->scsw;
557562306a36Sopenharmony_ci	if (sense) {
557662306a36Sopenharmony_ci		DBF_DEV_EVENT(DBF_EMERG, device, "%s: %016llx %08x : "
557762306a36Sopenharmony_ci			      "%016llx %016llx %016llx %016llx",
557862306a36Sopenharmony_ci			      reason, *stat, *((u32 *) (stat + 1)),
557962306a36Sopenharmony_ci			      sense[0], sense[1], sense[2], sense[3]);
558062306a36Sopenharmony_ci	} else {
558162306a36Sopenharmony_ci		DBF_DEV_EVENT(DBF_EMERG, device, "%s: %016llx %08x : %s",
558262306a36Sopenharmony_ci			      reason, *stat, *((u32 *) (stat + 1)),
558362306a36Sopenharmony_ci			      "NO VALID SENSE");
558462306a36Sopenharmony_ci	}
558562306a36Sopenharmony_ci}
558662306a36Sopenharmony_ci
558762306a36Sopenharmony_ci/*
558862306a36Sopenharmony_ci * Print sense data and related channel program.
558962306a36Sopenharmony_ci * Parts are printed because printk buffer is only 1024 bytes.
559062306a36Sopenharmony_ci */
559162306a36Sopenharmony_cistatic void dasd_eckd_dump_sense_ccw(struct dasd_device *device,
559262306a36Sopenharmony_ci				 struct dasd_ccw_req *req, struct irb *irb)
559362306a36Sopenharmony_ci{
559462306a36Sopenharmony_ci	char *page;
559562306a36Sopenharmony_ci	struct ccw1 *first, *last, *fail, *from, *to;
559662306a36Sopenharmony_ci	int len, sl, sct;
559762306a36Sopenharmony_ci
559862306a36Sopenharmony_ci	page = (char *) get_zeroed_page(GFP_ATOMIC);
559962306a36Sopenharmony_ci	if (page == NULL) {
560062306a36Sopenharmony_ci		DBF_DEV_EVENT(DBF_WARNING, device, "%s",
560162306a36Sopenharmony_ci			      "No memory to dump sense data\n");
560262306a36Sopenharmony_ci		return;
560362306a36Sopenharmony_ci	}
560462306a36Sopenharmony_ci	/* dump the sense data */
560562306a36Sopenharmony_ci	len = sprintf(page, PRINTK_HEADER
560662306a36Sopenharmony_ci		      " I/O status report for device %s:\n",
560762306a36Sopenharmony_ci		      dev_name(&device->cdev->dev));
560862306a36Sopenharmony_ci	len += sprintf(page + len, PRINTK_HEADER
560962306a36Sopenharmony_ci		       " in req: %p CC:%02X FC:%02X AC:%02X SC:%02X DS:%02X "
561062306a36Sopenharmony_ci		       "CS:%02X RC:%d\n",
561162306a36Sopenharmony_ci		       req, scsw_cc(&irb->scsw), scsw_fctl(&irb->scsw),
561262306a36Sopenharmony_ci		       scsw_actl(&irb->scsw), scsw_stctl(&irb->scsw),
561362306a36Sopenharmony_ci		       scsw_dstat(&irb->scsw), scsw_cstat(&irb->scsw),
561462306a36Sopenharmony_ci		       req ? req->intrc : 0);
561562306a36Sopenharmony_ci	len += sprintf(page + len, PRINTK_HEADER
561662306a36Sopenharmony_ci		       " device %s: Failing CCW: %p\n",
561762306a36Sopenharmony_ci		       dev_name(&device->cdev->dev),
561862306a36Sopenharmony_ci		       phys_to_virt(irb->scsw.cmd.cpa));
561962306a36Sopenharmony_ci	if (irb->esw.esw0.erw.cons) {
562062306a36Sopenharmony_ci		for (sl = 0; sl < 4; sl++) {
562162306a36Sopenharmony_ci			len += sprintf(page + len, PRINTK_HEADER
562262306a36Sopenharmony_ci				       " Sense(hex) %2d-%2d:",
562362306a36Sopenharmony_ci				       (8 * sl), ((8 * sl) + 7));
562462306a36Sopenharmony_ci
562562306a36Sopenharmony_ci			for (sct = 0; sct < 8; sct++) {
562662306a36Sopenharmony_ci				len += sprintf(page + len, " %02x",
562762306a36Sopenharmony_ci					       irb->ecw[8 * sl + sct]);
562862306a36Sopenharmony_ci			}
562962306a36Sopenharmony_ci			len += sprintf(page + len, "\n");
563062306a36Sopenharmony_ci		}
563162306a36Sopenharmony_ci
563262306a36Sopenharmony_ci		if (irb->ecw[27] & DASD_SENSE_BIT_0) {
563362306a36Sopenharmony_ci			/* 24 Byte Sense Data */
563462306a36Sopenharmony_ci			sprintf(page + len, PRINTK_HEADER
563562306a36Sopenharmony_ci				" 24 Byte: %x MSG %x, "
563662306a36Sopenharmony_ci				"%s MSGb to SYSOP\n",
563762306a36Sopenharmony_ci				irb->ecw[7] >> 4, irb->ecw[7] & 0x0f,
563862306a36Sopenharmony_ci				irb->ecw[1] & 0x10 ? "" : "no");
563962306a36Sopenharmony_ci		} else {
564062306a36Sopenharmony_ci			/* 32 Byte Sense Data */
564162306a36Sopenharmony_ci			sprintf(page + len, PRINTK_HEADER
564262306a36Sopenharmony_ci				" 32 Byte: Format: %x "
564362306a36Sopenharmony_ci				"Exception class %x\n",
564462306a36Sopenharmony_ci				irb->ecw[6] & 0x0f, irb->ecw[22] >> 4);
564562306a36Sopenharmony_ci		}
564662306a36Sopenharmony_ci	} else {
564762306a36Sopenharmony_ci		sprintf(page + len, PRINTK_HEADER
564862306a36Sopenharmony_ci			" SORRY - NO VALID SENSE AVAILABLE\n");
564962306a36Sopenharmony_ci	}
565062306a36Sopenharmony_ci	printk(KERN_ERR "%s", page);
565162306a36Sopenharmony_ci
565262306a36Sopenharmony_ci	if (req) {
565362306a36Sopenharmony_ci		/* req == NULL for unsolicited interrupts */
565462306a36Sopenharmony_ci		/* dump the Channel Program (max 140 Bytes per line) */
565562306a36Sopenharmony_ci		/* Count CCW and print first CCWs (maximum 7) */
565662306a36Sopenharmony_ci		first = req->cpaddr;
565762306a36Sopenharmony_ci		for (last = first; last->flags & (CCW_FLAG_CC | CCW_FLAG_DC); last++);
565862306a36Sopenharmony_ci		to = min(first + 6, last);
565962306a36Sopenharmony_ci		printk(KERN_ERR PRINTK_HEADER " Related CP in req: %p\n", req);
566062306a36Sopenharmony_ci		dasd_eckd_dump_ccw_range(first, to, page);
566162306a36Sopenharmony_ci
566262306a36Sopenharmony_ci		/* print failing CCW area (maximum 4) */
566362306a36Sopenharmony_ci		/* scsw->cda is either valid or zero  */
566462306a36Sopenharmony_ci		from = ++to;
566562306a36Sopenharmony_ci		fail = phys_to_virt(irb->scsw.cmd.cpa); /* failing CCW */
566662306a36Sopenharmony_ci		if (from <  fail - 2) {
566762306a36Sopenharmony_ci			from = fail - 2;     /* there is a gap - print header */
566862306a36Sopenharmony_ci			printk(KERN_ERR PRINTK_HEADER "......\n");
566962306a36Sopenharmony_ci		}
567062306a36Sopenharmony_ci		to = min(fail + 1, last);
567162306a36Sopenharmony_ci		dasd_eckd_dump_ccw_range(from, to, page + len);
567262306a36Sopenharmony_ci
567362306a36Sopenharmony_ci		/* print last CCWs (maximum 2) */
567462306a36Sopenharmony_ci		len = 0;
567562306a36Sopenharmony_ci		from = max(from, ++to);
567662306a36Sopenharmony_ci		if (from < last - 1) {
567762306a36Sopenharmony_ci			from = last - 1;     /* there is a gap - print header */
567862306a36Sopenharmony_ci			printk(KERN_ERR PRINTK_HEADER "......\n");
567962306a36Sopenharmony_ci		}
568062306a36Sopenharmony_ci		dasd_eckd_dump_ccw_range(from, last, page + len);
568162306a36Sopenharmony_ci	}
568262306a36Sopenharmony_ci	free_page((unsigned long) page);
568362306a36Sopenharmony_ci}
568462306a36Sopenharmony_ci
568562306a36Sopenharmony_ci
568662306a36Sopenharmony_ci/*
568762306a36Sopenharmony_ci * Print sense data from a tcw.
568862306a36Sopenharmony_ci */
568962306a36Sopenharmony_cistatic void dasd_eckd_dump_sense_tcw(struct dasd_device *device,
569062306a36Sopenharmony_ci				 struct dasd_ccw_req *req, struct irb *irb)
569162306a36Sopenharmony_ci{
569262306a36Sopenharmony_ci	char *page;
569362306a36Sopenharmony_ci	int len, sl, sct, residual;
569462306a36Sopenharmony_ci	struct tsb *tsb;
569562306a36Sopenharmony_ci	u8 *sense, *rcq;
569662306a36Sopenharmony_ci
569762306a36Sopenharmony_ci	page = (char *) get_zeroed_page(GFP_ATOMIC);
569862306a36Sopenharmony_ci	if (page == NULL) {
569962306a36Sopenharmony_ci		DBF_DEV_EVENT(DBF_WARNING, device, " %s",
570062306a36Sopenharmony_ci			    "No memory to dump sense data");
570162306a36Sopenharmony_ci		return;
570262306a36Sopenharmony_ci	}
570362306a36Sopenharmony_ci	/* dump the sense data */
570462306a36Sopenharmony_ci	len = sprintf(page, PRINTK_HEADER
570562306a36Sopenharmony_ci		      " I/O status report for device %s:\n",
570662306a36Sopenharmony_ci		      dev_name(&device->cdev->dev));
570762306a36Sopenharmony_ci	len += sprintf(page + len, PRINTK_HEADER
570862306a36Sopenharmony_ci		       " in req: %p CC:%02X FC:%02X AC:%02X SC:%02X DS:%02X "
570962306a36Sopenharmony_ci		       "CS:%02X fcxs:%02X schxs:%02X RC:%d\n",
571062306a36Sopenharmony_ci		       req, scsw_cc(&irb->scsw), scsw_fctl(&irb->scsw),
571162306a36Sopenharmony_ci		       scsw_actl(&irb->scsw), scsw_stctl(&irb->scsw),
571262306a36Sopenharmony_ci		       scsw_dstat(&irb->scsw), scsw_cstat(&irb->scsw),
571362306a36Sopenharmony_ci		       irb->scsw.tm.fcxs,
571462306a36Sopenharmony_ci		       (irb->scsw.tm.ifob << 7) | irb->scsw.tm.sesq,
571562306a36Sopenharmony_ci		       req ? req->intrc : 0);
571662306a36Sopenharmony_ci	len += sprintf(page + len, PRINTK_HEADER
571762306a36Sopenharmony_ci		       " device %s: Failing TCW: %p\n",
571862306a36Sopenharmony_ci		       dev_name(&device->cdev->dev),
571962306a36Sopenharmony_ci		       phys_to_virt(irb->scsw.tm.tcw));
572062306a36Sopenharmony_ci
572162306a36Sopenharmony_ci	tsb = NULL;
572262306a36Sopenharmony_ci	sense = NULL;
572362306a36Sopenharmony_ci	if (irb->scsw.tm.tcw && (irb->scsw.tm.fcxs & 0x01))
572462306a36Sopenharmony_ci		tsb = tcw_get_tsb(phys_to_virt(irb->scsw.tm.tcw));
572562306a36Sopenharmony_ci
572662306a36Sopenharmony_ci	if (tsb) {
572762306a36Sopenharmony_ci		len += sprintf(page + len, PRINTK_HEADER
572862306a36Sopenharmony_ci			       " tsb->length %d\n", tsb->length);
572962306a36Sopenharmony_ci		len += sprintf(page + len, PRINTK_HEADER
573062306a36Sopenharmony_ci			       " tsb->flags %x\n", tsb->flags);
573162306a36Sopenharmony_ci		len += sprintf(page + len, PRINTK_HEADER
573262306a36Sopenharmony_ci			       " tsb->dcw_offset %d\n", tsb->dcw_offset);
573362306a36Sopenharmony_ci		len += sprintf(page + len, PRINTK_HEADER
573462306a36Sopenharmony_ci			       " tsb->count %d\n", tsb->count);
573562306a36Sopenharmony_ci		residual = tsb->count - 28;
573662306a36Sopenharmony_ci		len += sprintf(page + len, PRINTK_HEADER
573762306a36Sopenharmony_ci			       " residual %d\n", residual);
573862306a36Sopenharmony_ci
573962306a36Sopenharmony_ci		switch (tsb->flags & 0x07) {
574062306a36Sopenharmony_ci		case 1:	/* tsa_iostat */
574162306a36Sopenharmony_ci			len += sprintf(page + len, PRINTK_HEADER
574262306a36Sopenharmony_ci			       " tsb->tsa.iostat.dev_time %d\n",
574362306a36Sopenharmony_ci				       tsb->tsa.iostat.dev_time);
574462306a36Sopenharmony_ci			len += sprintf(page + len, PRINTK_HEADER
574562306a36Sopenharmony_ci			       " tsb->tsa.iostat.def_time %d\n",
574662306a36Sopenharmony_ci				       tsb->tsa.iostat.def_time);
574762306a36Sopenharmony_ci			len += sprintf(page + len, PRINTK_HEADER
574862306a36Sopenharmony_ci			       " tsb->tsa.iostat.queue_time %d\n",
574962306a36Sopenharmony_ci				       tsb->tsa.iostat.queue_time);
575062306a36Sopenharmony_ci			len += sprintf(page + len, PRINTK_HEADER
575162306a36Sopenharmony_ci			       " tsb->tsa.iostat.dev_busy_time %d\n",
575262306a36Sopenharmony_ci				       tsb->tsa.iostat.dev_busy_time);
575362306a36Sopenharmony_ci			len += sprintf(page + len, PRINTK_HEADER
575462306a36Sopenharmony_ci			       " tsb->tsa.iostat.dev_act_time %d\n",
575562306a36Sopenharmony_ci				       tsb->tsa.iostat.dev_act_time);
575662306a36Sopenharmony_ci			sense = tsb->tsa.iostat.sense;
575762306a36Sopenharmony_ci			break;
575862306a36Sopenharmony_ci		case 2: /* ts_ddpc */
575962306a36Sopenharmony_ci			len += sprintf(page + len, PRINTK_HEADER
576062306a36Sopenharmony_ci			       " tsb->tsa.ddpc.rc %d\n", tsb->tsa.ddpc.rc);
576162306a36Sopenharmony_ci			for (sl = 0; sl < 2; sl++) {
576262306a36Sopenharmony_ci				len += sprintf(page + len, PRINTK_HEADER
576362306a36Sopenharmony_ci					       " tsb->tsa.ddpc.rcq %2d-%2d: ",
576462306a36Sopenharmony_ci					       (8 * sl), ((8 * sl) + 7));
576562306a36Sopenharmony_ci				rcq = tsb->tsa.ddpc.rcq;
576662306a36Sopenharmony_ci				for (sct = 0; sct < 8; sct++) {
576762306a36Sopenharmony_ci					len += sprintf(page + len, " %02x",
576862306a36Sopenharmony_ci						       rcq[8 * sl + sct]);
576962306a36Sopenharmony_ci				}
577062306a36Sopenharmony_ci				len += sprintf(page + len, "\n");
577162306a36Sopenharmony_ci			}
577262306a36Sopenharmony_ci			sense = tsb->tsa.ddpc.sense;
577362306a36Sopenharmony_ci			break;
577462306a36Sopenharmony_ci		case 3: /* tsa_intrg */
577562306a36Sopenharmony_ci			len += sprintf(page + len, PRINTK_HEADER
577662306a36Sopenharmony_ci				      " tsb->tsa.intrg.: not supported yet\n");
577762306a36Sopenharmony_ci			break;
577862306a36Sopenharmony_ci		}
577962306a36Sopenharmony_ci
578062306a36Sopenharmony_ci		if (sense) {
578162306a36Sopenharmony_ci			for (sl = 0; sl < 4; sl++) {
578262306a36Sopenharmony_ci				len += sprintf(page + len, PRINTK_HEADER
578362306a36Sopenharmony_ci					       " Sense(hex) %2d-%2d:",
578462306a36Sopenharmony_ci					       (8 * sl), ((8 * sl) + 7));
578562306a36Sopenharmony_ci				for (sct = 0; sct < 8; sct++) {
578662306a36Sopenharmony_ci					len += sprintf(page + len, " %02x",
578762306a36Sopenharmony_ci						       sense[8 * sl + sct]);
578862306a36Sopenharmony_ci				}
578962306a36Sopenharmony_ci				len += sprintf(page + len, "\n");
579062306a36Sopenharmony_ci			}
579162306a36Sopenharmony_ci
579262306a36Sopenharmony_ci			if (sense[27] & DASD_SENSE_BIT_0) {
579362306a36Sopenharmony_ci				/* 24 Byte Sense Data */
579462306a36Sopenharmony_ci				sprintf(page + len, PRINTK_HEADER
579562306a36Sopenharmony_ci					" 24 Byte: %x MSG %x, "
579662306a36Sopenharmony_ci					"%s MSGb to SYSOP\n",
579762306a36Sopenharmony_ci					sense[7] >> 4, sense[7] & 0x0f,
579862306a36Sopenharmony_ci					sense[1] & 0x10 ? "" : "no");
579962306a36Sopenharmony_ci			} else {
580062306a36Sopenharmony_ci				/* 32 Byte Sense Data */
580162306a36Sopenharmony_ci				sprintf(page + len, PRINTK_HEADER
580262306a36Sopenharmony_ci					" 32 Byte: Format: %x "
580362306a36Sopenharmony_ci					"Exception class %x\n",
580462306a36Sopenharmony_ci					sense[6] & 0x0f, sense[22] >> 4);
580562306a36Sopenharmony_ci			}
580662306a36Sopenharmony_ci		} else {
580762306a36Sopenharmony_ci			sprintf(page + len, PRINTK_HEADER
580862306a36Sopenharmony_ci				" SORRY - NO VALID SENSE AVAILABLE\n");
580962306a36Sopenharmony_ci		}
581062306a36Sopenharmony_ci	} else {
581162306a36Sopenharmony_ci		sprintf(page + len, PRINTK_HEADER
581262306a36Sopenharmony_ci			" SORRY - NO TSB DATA AVAILABLE\n");
581362306a36Sopenharmony_ci	}
581462306a36Sopenharmony_ci	printk(KERN_ERR "%s", page);
581562306a36Sopenharmony_ci	free_page((unsigned long) page);
581662306a36Sopenharmony_ci}
581762306a36Sopenharmony_ci
581862306a36Sopenharmony_cistatic void dasd_eckd_dump_sense(struct dasd_device *device,
581962306a36Sopenharmony_ci				 struct dasd_ccw_req *req, struct irb *irb)
582062306a36Sopenharmony_ci{
582162306a36Sopenharmony_ci	u8 *sense = dasd_get_sense(irb);
582262306a36Sopenharmony_ci
582362306a36Sopenharmony_ci	if (scsw_is_tm(&irb->scsw)) {
582462306a36Sopenharmony_ci		/*
582562306a36Sopenharmony_ci		 * In some cases the 'File Protected' or 'Incorrect Length'
582662306a36Sopenharmony_ci		 * error might be expected and log messages shouldn't be written
582762306a36Sopenharmony_ci		 * then. Check if the according suppress bit is set.
582862306a36Sopenharmony_ci		 */
582962306a36Sopenharmony_ci		if (sense && (sense[1] & SNS1_FILE_PROTECTED) &&
583062306a36Sopenharmony_ci		    test_bit(DASD_CQR_SUPPRESS_FP, &req->flags))
583162306a36Sopenharmony_ci			return;
583262306a36Sopenharmony_ci		if (scsw_cstat(&irb->scsw) == 0x40 &&
583362306a36Sopenharmony_ci		    test_bit(DASD_CQR_SUPPRESS_IL, &req->flags))
583462306a36Sopenharmony_ci			return;
583562306a36Sopenharmony_ci
583662306a36Sopenharmony_ci		dasd_eckd_dump_sense_tcw(device, req, irb);
583762306a36Sopenharmony_ci	} else {
583862306a36Sopenharmony_ci		/*
583962306a36Sopenharmony_ci		 * In some cases the 'Command Reject' or 'No Record Found'
584062306a36Sopenharmony_ci		 * error might be expected and log messages shouldn't be
584162306a36Sopenharmony_ci		 * written then. Check if the according suppress bit is set.
584262306a36Sopenharmony_ci		 */
584362306a36Sopenharmony_ci		if (sense && sense[0] & SNS0_CMD_REJECT &&
584462306a36Sopenharmony_ci		    test_bit(DASD_CQR_SUPPRESS_CR, &req->flags))
584562306a36Sopenharmony_ci			return;
584662306a36Sopenharmony_ci
584762306a36Sopenharmony_ci		if (sense && sense[1] & SNS1_NO_REC_FOUND &&
584862306a36Sopenharmony_ci		    test_bit(DASD_CQR_SUPPRESS_NRF, &req->flags))
584962306a36Sopenharmony_ci			return;
585062306a36Sopenharmony_ci
585162306a36Sopenharmony_ci		dasd_eckd_dump_sense_ccw(device, req, irb);
585262306a36Sopenharmony_ci	}
585362306a36Sopenharmony_ci}
585462306a36Sopenharmony_ci
585562306a36Sopenharmony_cistatic int dasd_eckd_reload_device(struct dasd_device *device)
585662306a36Sopenharmony_ci{
585762306a36Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
585862306a36Sopenharmony_ci	char print_uid[DASD_UID_STRLEN];
585962306a36Sopenharmony_ci	int rc, old_base;
586062306a36Sopenharmony_ci	struct dasd_uid uid;
586162306a36Sopenharmony_ci	unsigned long flags;
586262306a36Sopenharmony_ci
586362306a36Sopenharmony_ci	/*
586462306a36Sopenharmony_ci	 * remove device from alias handling to prevent new requests
586562306a36Sopenharmony_ci	 * from being scheduled on the wrong alias device
586662306a36Sopenharmony_ci	 */
586762306a36Sopenharmony_ci	dasd_alias_remove_device(device);
586862306a36Sopenharmony_ci
586962306a36Sopenharmony_ci	spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
587062306a36Sopenharmony_ci	old_base = private->uid.base_unit_addr;
587162306a36Sopenharmony_ci	spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
587262306a36Sopenharmony_ci
587362306a36Sopenharmony_ci	/* Read Configuration Data */
587462306a36Sopenharmony_ci	rc = dasd_eckd_read_conf(device);
587562306a36Sopenharmony_ci	if (rc)
587662306a36Sopenharmony_ci		goto out_err;
587762306a36Sopenharmony_ci
587862306a36Sopenharmony_ci	dasd_eckd_read_fc_security(device);
587962306a36Sopenharmony_ci
588062306a36Sopenharmony_ci	rc = dasd_eckd_generate_uid(device);
588162306a36Sopenharmony_ci	if (rc)
588262306a36Sopenharmony_ci		goto out_err;
588362306a36Sopenharmony_ci	/*
588462306a36Sopenharmony_ci	 * update unit address configuration and
588562306a36Sopenharmony_ci	 * add device to alias management
588662306a36Sopenharmony_ci	 */
588762306a36Sopenharmony_ci	dasd_alias_update_add_device(device);
588862306a36Sopenharmony_ci
588962306a36Sopenharmony_ci	dasd_eckd_get_uid(device, &uid);
589062306a36Sopenharmony_ci
589162306a36Sopenharmony_ci	if (old_base != uid.base_unit_addr) {
589262306a36Sopenharmony_ci		dasd_eckd_get_uid_string(&private->conf, print_uid);
589362306a36Sopenharmony_ci		dev_info(&device->cdev->dev,
589462306a36Sopenharmony_ci			 "An Alias device was reassigned to a new base device "
589562306a36Sopenharmony_ci			 "with UID: %s\n", print_uid);
589662306a36Sopenharmony_ci	}
589762306a36Sopenharmony_ci	return 0;
589862306a36Sopenharmony_ci
589962306a36Sopenharmony_ciout_err:
590062306a36Sopenharmony_ci	return -1;
590162306a36Sopenharmony_ci}
590262306a36Sopenharmony_ci
590362306a36Sopenharmony_cistatic int dasd_eckd_read_message_buffer(struct dasd_device *device,
590462306a36Sopenharmony_ci					 struct dasd_rssd_messages *messages,
590562306a36Sopenharmony_ci					 __u8 lpum)
590662306a36Sopenharmony_ci{
590762306a36Sopenharmony_ci	struct dasd_rssd_messages *message_buf;
590862306a36Sopenharmony_ci	struct dasd_psf_prssd_data *prssdp;
590962306a36Sopenharmony_ci	struct dasd_ccw_req *cqr;
591062306a36Sopenharmony_ci	struct ccw1 *ccw;
591162306a36Sopenharmony_ci	int rc;
591262306a36Sopenharmony_ci
591362306a36Sopenharmony_ci	cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */	+ 1 /* RSSD */,
591462306a36Sopenharmony_ci				   (sizeof(struct dasd_psf_prssd_data) +
591562306a36Sopenharmony_ci				    sizeof(struct dasd_rssd_messages)),
591662306a36Sopenharmony_ci				   device, NULL);
591762306a36Sopenharmony_ci	if (IS_ERR(cqr)) {
591862306a36Sopenharmony_ci		DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s",
591962306a36Sopenharmony_ci				"Could not allocate read message buffer request");
592062306a36Sopenharmony_ci		return PTR_ERR(cqr);
592162306a36Sopenharmony_ci	}
592262306a36Sopenharmony_ci
592362306a36Sopenharmony_ci	cqr->lpm = lpum;
592462306a36Sopenharmony_ciretry:
592562306a36Sopenharmony_ci	cqr->startdev = device;
592662306a36Sopenharmony_ci	cqr->memdev = device;
592762306a36Sopenharmony_ci	cqr->block = NULL;
592862306a36Sopenharmony_ci	cqr->expires = 10 * HZ;
592962306a36Sopenharmony_ci	set_bit(DASD_CQR_VERIFY_PATH, &cqr->flags);
593062306a36Sopenharmony_ci	/* dasd_sleep_on_immediatly does not do complex error
593162306a36Sopenharmony_ci	 * recovery so clear erp flag and set retry counter to
593262306a36Sopenharmony_ci	 * do basic erp */
593362306a36Sopenharmony_ci	clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
593462306a36Sopenharmony_ci	cqr->retries = 256;
593562306a36Sopenharmony_ci
593662306a36Sopenharmony_ci	/* Prepare for Read Subsystem Data */
593762306a36Sopenharmony_ci	prssdp = (struct dasd_psf_prssd_data *) cqr->data;
593862306a36Sopenharmony_ci	memset(prssdp, 0, sizeof(struct dasd_psf_prssd_data));
593962306a36Sopenharmony_ci	prssdp->order = PSF_ORDER_PRSSD;
594062306a36Sopenharmony_ci	prssdp->suborder = 0x03;	/* Message Buffer */
594162306a36Sopenharmony_ci	/* all other bytes of prssdp must be zero */
594262306a36Sopenharmony_ci
594362306a36Sopenharmony_ci	ccw = cqr->cpaddr;
594462306a36Sopenharmony_ci	ccw->cmd_code = DASD_ECKD_CCW_PSF;
594562306a36Sopenharmony_ci	ccw->count = sizeof(struct dasd_psf_prssd_data);
594662306a36Sopenharmony_ci	ccw->flags |= CCW_FLAG_CC;
594762306a36Sopenharmony_ci	ccw->flags |= CCW_FLAG_SLI;
594862306a36Sopenharmony_ci	ccw->cda = (__u32)virt_to_phys(prssdp);
594962306a36Sopenharmony_ci
595062306a36Sopenharmony_ci	/* Read Subsystem Data - message buffer */
595162306a36Sopenharmony_ci	message_buf = (struct dasd_rssd_messages *) (prssdp + 1);
595262306a36Sopenharmony_ci	memset(message_buf, 0, sizeof(struct dasd_rssd_messages));
595362306a36Sopenharmony_ci
595462306a36Sopenharmony_ci	ccw++;
595562306a36Sopenharmony_ci	ccw->cmd_code = DASD_ECKD_CCW_RSSD;
595662306a36Sopenharmony_ci	ccw->count = sizeof(struct dasd_rssd_messages);
595762306a36Sopenharmony_ci	ccw->flags |= CCW_FLAG_SLI;
595862306a36Sopenharmony_ci	ccw->cda = (__u32)virt_to_phys(message_buf);
595962306a36Sopenharmony_ci
596062306a36Sopenharmony_ci	cqr->buildclk = get_tod_clock();
596162306a36Sopenharmony_ci	cqr->status = DASD_CQR_FILLED;
596262306a36Sopenharmony_ci	rc = dasd_sleep_on_immediatly(cqr);
596362306a36Sopenharmony_ci	if (rc == 0) {
596462306a36Sopenharmony_ci		prssdp = (struct dasd_psf_prssd_data *) cqr->data;
596562306a36Sopenharmony_ci		message_buf = (struct dasd_rssd_messages *)
596662306a36Sopenharmony_ci			(prssdp + 1);
596762306a36Sopenharmony_ci		memcpy(messages, message_buf,
596862306a36Sopenharmony_ci		       sizeof(struct dasd_rssd_messages));
596962306a36Sopenharmony_ci	} else if (cqr->lpm) {
597062306a36Sopenharmony_ci		/*
597162306a36Sopenharmony_ci		 * on z/VM we might not be able to do I/O on the requested path
597262306a36Sopenharmony_ci		 * but instead we get the required information on any path
597362306a36Sopenharmony_ci		 * so retry with open path mask
597462306a36Sopenharmony_ci		 */
597562306a36Sopenharmony_ci		cqr->lpm = 0;
597662306a36Sopenharmony_ci		goto retry;
597762306a36Sopenharmony_ci	} else
597862306a36Sopenharmony_ci		DBF_EVENT_DEVID(DBF_WARNING, device->cdev,
597962306a36Sopenharmony_ci				"Reading messages failed with rc=%d\n"
598062306a36Sopenharmony_ci				, rc);
598162306a36Sopenharmony_ci	dasd_sfree_request(cqr, cqr->memdev);
598262306a36Sopenharmony_ci	return rc;
598362306a36Sopenharmony_ci}
598462306a36Sopenharmony_ci
598562306a36Sopenharmony_cistatic int dasd_eckd_query_host_access(struct dasd_device *device,
598662306a36Sopenharmony_ci				       struct dasd_psf_query_host_access *data)
598762306a36Sopenharmony_ci{
598862306a36Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
598962306a36Sopenharmony_ci	struct dasd_psf_query_host_access *host_access;
599062306a36Sopenharmony_ci	struct dasd_psf_prssd_data *prssdp;
599162306a36Sopenharmony_ci	struct dasd_ccw_req *cqr;
599262306a36Sopenharmony_ci	struct ccw1 *ccw;
599362306a36Sopenharmony_ci	int rc;
599462306a36Sopenharmony_ci
599562306a36Sopenharmony_ci	/* not available for HYPER PAV alias devices */
599662306a36Sopenharmony_ci	if (!device->block && private->lcu->pav == HYPER_PAV)
599762306a36Sopenharmony_ci		return -EOPNOTSUPP;
599862306a36Sopenharmony_ci
599962306a36Sopenharmony_ci	/* may not be supported by the storage server */
600062306a36Sopenharmony_ci	if (!(private->features.feature[14] & 0x80))
600162306a36Sopenharmony_ci		return -EOPNOTSUPP;
600262306a36Sopenharmony_ci
600362306a36Sopenharmony_ci	cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */	+ 1 /* RSSD */,
600462306a36Sopenharmony_ci				   sizeof(struct dasd_psf_prssd_data) + 1,
600562306a36Sopenharmony_ci				   device, NULL);
600662306a36Sopenharmony_ci	if (IS_ERR(cqr)) {
600762306a36Sopenharmony_ci		DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s",
600862306a36Sopenharmony_ci				"Could not allocate read message buffer request");
600962306a36Sopenharmony_ci		return PTR_ERR(cqr);
601062306a36Sopenharmony_ci	}
601162306a36Sopenharmony_ci	host_access = kzalloc(sizeof(*host_access), GFP_KERNEL | GFP_DMA);
601262306a36Sopenharmony_ci	if (!host_access) {
601362306a36Sopenharmony_ci		dasd_sfree_request(cqr, device);
601462306a36Sopenharmony_ci		DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s",
601562306a36Sopenharmony_ci				"Could not allocate host_access buffer");
601662306a36Sopenharmony_ci		return -ENOMEM;
601762306a36Sopenharmony_ci	}
601862306a36Sopenharmony_ci	cqr->startdev = device;
601962306a36Sopenharmony_ci	cqr->memdev = device;
602062306a36Sopenharmony_ci	cqr->block = NULL;
602162306a36Sopenharmony_ci	cqr->retries = 256;
602262306a36Sopenharmony_ci	cqr->expires = 10 * HZ;
602362306a36Sopenharmony_ci
602462306a36Sopenharmony_ci	/* Prepare for Read Subsystem Data */
602562306a36Sopenharmony_ci	prssdp = (struct dasd_psf_prssd_data *) cqr->data;
602662306a36Sopenharmony_ci	memset(prssdp, 0, sizeof(struct dasd_psf_prssd_data));
602762306a36Sopenharmony_ci	prssdp->order = PSF_ORDER_PRSSD;
602862306a36Sopenharmony_ci	prssdp->suborder = PSF_SUBORDER_QHA;	/* query host access */
602962306a36Sopenharmony_ci	/* LSS and Volume that will be queried */
603062306a36Sopenharmony_ci	prssdp->lss = private->conf.ned->ID;
603162306a36Sopenharmony_ci	prssdp->volume = private->conf.ned->unit_addr;
603262306a36Sopenharmony_ci	/* all other bytes of prssdp must be zero */
603362306a36Sopenharmony_ci
603462306a36Sopenharmony_ci	ccw = cqr->cpaddr;
603562306a36Sopenharmony_ci	ccw->cmd_code = DASD_ECKD_CCW_PSF;
603662306a36Sopenharmony_ci	ccw->count = sizeof(struct dasd_psf_prssd_data);
603762306a36Sopenharmony_ci	ccw->flags |= CCW_FLAG_CC;
603862306a36Sopenharmony_ci	ccw->flags |= CCW_FLAG_SLI;
603962306a36Sopenharmony_ci	ccw->cda = (__u32)virt_to_phys(prssdp);
604062306a36Sopenharmony_ci
604162306a36Sopenharmony_ci	/* Read Subsystem Data - query host access */
604262306a36Sopenharmony_ci	ccw++;
604362306a36Sopenharmony_ci	ccw->cmd_code = DASD_ECKD_CCW_RSSD;
604462306a36Sopenharmony_ci	ccw->count = sizeof(struct dasd_psf_query_host_access);
604562306a36Sopenharmony_ci	ccw->flags |= CCW_FLAG_SLI;
604662306a36Sopenharmony_ci	ccw->cda = (__u32)virt_to_phys(host_access);
604762306a36Sopenharmony_ci
604862306a36Sopenharmony_ci	cqr->buildclk = get_tod_clock();
604962306a36Sopenharmony_ci	cqr->status = DASD_CQR_FILLED;
605062306a36Sopenharmony_ci	/* the command might not be supported, suppress error message */
605162306a36Sopenharmony_ci	__set_bit(DASD_CQR_SUPPRESS_CR, &cqr->flags);
605262306a36Sopenharmony_ci	rc = dasd_sleep_on_interruptible(cqr);
605362306a36Sopenharmony_ci	if (rc == 0) {
605462306a36Sopenharmony_ci		*data = *host_access;
605562306a36Sopenharmony_ci	} else {
605662306a36Sopenharmony_ci		DBF_EVENT_DEVID(DBF_WARNING, device->cdev,
605762306a36Sopenharmony_ci				"Reading host access data failed with rc=%d\n",
605862306a36Sopenharmony_ci				rc);
605962306a36Sopenharmony_ci		rc = -EOPNOTSUPP;
606062306a36Sopenharmony_ci	}
606162306a36Sopenharmony_ci
606262306a36Sopenharmony_ci	dasd_sfree_request(cqr, cqr->memdev);
606362306a36Sopenharmony_ci	kfree(host_access);
606462306a36Sopenharmony_ci	return rc;
606562306a36Sopenharmony_ci}
606662306a36Sopenharmony_ci/*
606762306a36Sopenharmony_ci * return number of grouped devices
606862306a36Sopenharmony_ci */
606962306a36Sopenharmony_cistatic int dasd_eckd_host_access_count(struct dasd_device *device)
607062306a36Sopenharmony_ci{
607162306a36Sopenharmony_ci	struct dasd_psf_query_host_access *access;
607262306a36Sopenharmony_ci	struct dasd_ckd_path_group_entry *entry;
607362306a36Sopenharmony_ci	struct dasd_ckd_host_information *info;
607462306a36Sopenharmony_ci	int count = 0;
607562306a36Sopenharmony_ci	int rc, i;
607662306a36Sopenharmony_ci
607762306a36Sopenharmony_ci	access = kzalloc(sizeof(*access), GFP_NOIO);
607862306a36Sopenharmony_ci	if (!access) {
607962306a36Sopenharmony_ci		DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s",
608062306a36Sopenharmony_ci				"Could not allocate access buffer");
608162306a36Sopenharmony_ci		return -ENOMEM;
608262306a36Sopenharmony_ci	}
608362306a36Sopenharmony_ci	rc = dasd_eckd_query_host_access(device, access);
608462306a36Sopenharmony_ci	if (rc) {
608562306a36Sopenharmony_ci		kfree(access);
608662306a36Sopenharmony_ci		return rc;
608762306a36Sopenharmony_ci	}
608862306a36Sopenharmony_ci
608962306a36Sopenharmony_ci	info = (struct dasd_ckd_host_information *)
609062306a36Sopenharmony_ci		access->host_access_information;
609162306a36Sopenharmony_ci	for (i = 0; i < info->entry_count; i++) {
609262306a36Sopenharmony_ci		entry = (struct dasd_ckd_path_group_entry *)
609362306a36Sopenharmony_ci			(info->entry + i * info->entry_size);
609462306a36Sopenharmony_ci		if (entry->status_flags & DASD_ECKD_PG_GROUPED)
609562306a36Sopenharmony_ci			count++;
609662306a36Sopenharmony_ci	}
609762306a36Sopenharmony_ci
609862306a36Sopenharmony_ci	kfree(access);
609962306a36Sopenharmony_ci	return count;
610062306a36Sopenharmony_ci}
610162306a36Sopenharmony_ci
610262306a36Sopenharmony_ci/*
610362306a36Sopenharmony_ci * write host access information to a sequential file
610462306a36Sopenharmony_ci */
610562306a36Sopenharmony_cistatic int dasd_hosts_print(struct dasd_device *device, struct seq_file *m)
610662306a36Sopenharmony_ci{
610762306a36Sopenharmony_ci	struct dasd_psf_query_host_access *access;
610862306a36Sopenharmony_ci	struct dasd_ckd_path_group_entry *entry;
610962306a36Sopenharmony_ci	struct dasd_ckd_host_information *info;
611062306a36Sopenharmony_ci	char sysplex[9] = "";
611162306a36Sopenharmony_ci	int rc, i;
611262306a36Sopenharmony_ci
611362306a36Sopenharmony_ci	access = kzalloc(sizeof(*access), GFP_NOIO);
611462306a36Sopenharmony_ci	if (!access) {
611562306a36Sopenharmony_ci		DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s",
611662306a36Sopenharmony_ci				"Could not allocate access buffer");
611762306a36Sopenharmony_ci		return -ENOMEM;
611862306a36Sopenharmony_ci	}
611962306a36Sopenharmony_ci	rc = dasd_eckd_query_host_access(device, access);
612062306a36Sopenharmony_ci	if (rc) {
612162306a36Sopenharmony_ci		kfree(access);
612262306a36Sopenharmony_ci		return rc;
612362306a36Sopenharmony_ci	}
612462306a36Sopenharmony_ci
612562306a36Sopenharmony_ci	info = (struct dasd_ckd_host_information *)
612662306a36Sopenharmony_ci		access->host_access_information;
612762306a36Sopenharmony_ci	for (i = 0; i < info->entry_count; i++) {
612862306a36Sopenharmony_ci		entry = (struct dasd_ckd_path_group_entry *)
612962306a36Sopenharmony_ci			(info->entry + i * info->entry_size);
613062306a36Sopenharmony_ci		/* PGID */
613162306a36Sopenharmony_ci		seq_printf(m, "pgid %*phN\n", 11, entry->pgid);
613262306a36Sopenharmony_ci		/* FLAGS */
613362306a36Sopenharmony_ci		seq_printf(m, "status_flags %02x\n", entry->status_flags);
613462306a36Sopenharmony_ci		/* SYSPLEX NAME */
613562306a36Sopenharmony_ci		memcpy(&sysplex, &entry->sysplex_name, sizeof(sysplex) - 1);
613662306a36Sopenharmony_ci		EBCASC(sysplex, sizeof(sysplex));
613762306a36Sopenharmony_ci		seq_printf(m, "sysplex_name %8s\n", sysplex);
613862306a36Sopenharmony_ci		/* SUPPORTED CYLINDER */
613962306a36Sopenharmony_ci		seq_printf(m, "supported_cylinder %d\n", entry->cylinder);
614062306a36Sopenharmony_ci		/* TIMESTAMP */
614162306a36Sopenharmony_ci		seq_printf(m, "timestamp %lu\n", (unsigned long)
614262306a36Sopenharmony_ci			   entry->timestamp);
614362306a36Sopenharmony_ci	}
614462306a36Sopenharmony_ci	kfree(access);
614562306a36Sopenharmony_ci
614662306a36Sopenharmony_ci	return 0;
614762306a36Sopenharmony_ci}
614862306a36Sopenharmony_ci
614962306a36Sopenharmony_cistatic struct dasd_device
615062306a36Sopenharmony_ci*copy_relation_find_device(struct dasd_copy_relation *copy,
615162306a36Sopenharmony_ci			   char *busid)
615262306a36Sopenharmony_ci{
615362306a36Sopenharmony_ci	int i;
615462306a36Sopenharmony_ci
615562306a36Sopenharmony_ci	for (i = 0; i < DASD_CP_ENTRIES; i++) {
615662306a36Sopenharmony_ci		if (copy->entry[i].configured &&
615762306a36Sopenharmony_ci		    strncmp(copy->entry[i].busid, busid, DASD_BUS_ID_SIZE) == 0)
615862306a36Sopenharmony_ci			return copy->entry[i].device;
615962306a36Sopenharmony_ci	}
616062306a36Sopenharmony_ci	return NULL;
616162306a36Sopenharmony_ci}
616262306a36Sopenharmony_ci
616362306a36Sopenharmony_ci/*
616462306a36Sopenharmony_ci * set the new active/primary device
616562306a36Sopenharmony_ci */
616662306a36Sopenharmony_cistatic void copy_pair_set_active(struct dasd_copy_relation *copy, char *new_busid,
616762306a36Sopenharmony_ci				 char *old_busid)
616862306a36Sopenharmony_ci{
616962306a36Sopenharmony_ci	int i;
617062306a36Sopenharmony_ci
617162306a36Sopenharmony_ci	for (i = 0; i < DASD_CP_ENTRIES; i++) {
617262306a36Sopenharmony_ci		if (copy->entry[i].configured &&
617362306a36Sopenharmony_ci		    strncmp(copy->entry[i].busid, new_busid,
617462306a36Sopenharmony_ci			    DASD_BUS_ID_SIZE) == 0) {
617562306a36Sopenharmony_ci			copy->active = &copy->entry[i];
617662306a36Sopenharmony_ci			copy->entry[i].primary = true;
617762306a36Sopenharmony_ci		} else if (copy->entry[i].configured &&
617862306a36Sopenharmony_ci			   strncmp(copy->entry[i].busid, old_busid,
617962306a36Sopenharmony_ci				   DASD_BUS_ID_SIZE) == 0) {
618062306a36Sopenharmony_ci			copy->entry[i].primary = false;
618162306a36Sopenharmony_ci		}
618262306a36Sopenharmony_ci	}
618362306a36Sopenharmony_ci}
618462306a36Sopenharmony_ci
618562306a36Sopenharmony_ci/*
618662306a36Sopenharmony_ci * The function will swap the role of a given copy pair.
618762306a36Sopenharmony_ci * During the swap operation the relation of the blockdevice is disconnected
618862306a36Sopenharmony_ci * from the old primary and connected to the new.
618962306a36Sopenharmony_ci *
619062306a36Sopenharmony_ci * IO is paused on the block queue before swap and may be resumed afterwards.
619162306a36Sopenharmony_ci */
619262306a36Sopenharmony_cistatic int dasd_eckd_copy_pair_swap(struct dasd_device *device, char *prim_busid,
619362306a36Sopenharmony_ci				    char *sec_busid)
619462306a36Sopenharmony_ci{
619562306a36Sopenharmony_ci	struct dasd_device *primary, *secondary;
619662306a36Sopenharmony_ci	struct dasd_copy_relation *copy;
619762306a36Sopenharmony_ci	struct dasd_block *block;
619862306a36Sopenharmony_ci	struct gendisk *gdp;
619962306a36Sopenharmony_ci
620062306a36Sopenharmony_ci	copy = device->copy;
620162306a36Sopenharmony_ci	if (!copy)
620262306a36Sopenharmony_ci		return DASD_COPYPAIRSWAP_INVALID;
620362306a36Sopenharmony_ci	primary = copy->active->device;
620462306a36Sopenharmony_ci	if (!primary)
620562306a36Sopenharmony_ci		return DASD_COPYPAIRSWAP_INVALID;
620662306a36Sopenharmony_ci	/* double check if swap has correct primary */
620762306a36Sopenharmony_ci	if (strncmp(dev_name(&primary->cdev->dev), prim_busid, DASD_BUS_ID_SIZE) != 0)
620862306a36Sopenharmony_ci		return DASD_COPYPAIRSWAP_PRIMARY;
620962306a36Sopenharmony_ci
621062306a36Sopenharmony_ci	secondary = copy_relation_find_device(copy, sec_busid);
621162306a36Sopenharmony_ci	if (!secondary)
621262306a36Sopenharmony_ci		return DASD_COPYPAIRSWAP_SECONDARY;
621362306a36Sopenharmony_ci
621462306a36Sopenharmony_ci	/*
621562306a36Sopenharmony_ci	 * usually the device should be quiesced for swap
621662306a36Sopenharmony_ci	 * for paranoia stop device and requeue requests again
621762306a36Sopenharmony_ci	 */
621862306a36Sopenharmony_ci	dasd_device_set_stop_bits(primary, DASD_STOPPED_PPRC);
621962306a36Sopenharmony_ci	dasd_device_set_stop_bits(secondary, DASD_STOPPED_PPRC);
622062306a36Sopenharmony_ci	dasd_generic_requeue_all_requests(primary);
622162306a36Sopenharmony_ci
622262306a36Sopenharmony_ci	/* swap DASD internal device <> block assignment */
622362306a36Sopenharmony_ci	block = primary->block;
622462306a36Sopenharmony_ci	primary->block = NULL;
622562306a36Sopenharmony_ci	secondary->block = block;
622662306a36Sopenharmony_ci	block->base = secondary;
622762306a36Sopenharmony_ci	/* set new primary device in COPY relation */
622862306a36Sopenharmony_ci	copy_pair_set_active(copy, sec_busid, prim_busid);
622962306a36Sopenharmony_ci
623062306a36Sopenharmony_ci	/* swap blocklayer device link */
623162306a36Sopenharmony_ci	gdp = block->gdp;
623262306a36Sopenharmony_ci	dasd_add_link_to_gendisk(gdp, secondary);
623362306a36Sopenharmony_ci
623462306a36Sopenharmony_ci	/* re-enable device */
623562306a36Sopenharmony_ci	dasd_device_remove_stop_bits(primary, DASD_STOPPED_PPRC);
623662306a36Sopenharmony_ci	dasd_device_remove_stop_bits(secondary, DASD_STOPPED_PPRC);
623762306a36Sopenharmony_ci	dasd_schedule_device_bh(secondary);
623862306a36Sopenharmony_ci
623962306a36Sopenharmony_ci	return DASD_COPYPAIRSWAP_SUCCESS;
624062306a36Sopenharmony_ci}
624162306a36Sopenharmony_ci
624262306a36Sopenharmony_ci/*
624362306a36Sopenharmony_ci * Perform Subsystem Function - Peer-to-Peer Remote Copy Extended Query
624462306a36Sopenharmony_ci */
624562306a36Sopenharmony_cistatic int dasd_eckd_query_pprc_status(struct dasd_device *device,
624662306a36Sopenharmony_ci				       struct dasd_pprc_data_sc4 *data)
624762306a36Sopenharmony_ci{
624862306a36Sopenharmony_ci	struct dasd_pprc_data_sc4 *pprc_data;
624962306a36Sopenharmony_ci	struct dasd_psf_prssd_data *prssdp;
625062306a36Sopenharmony_ci	struct dasd_ccw_req *cqr;
625162306a36Sopenharmony_ci	struct ccw1 *ccw;
625262306a36Sopenharmony_ci	int rc;
625362306a36Sopenharmony_ci
625462306a36Sopenharmony_ci	cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */	+ 1 /* RSSD */,
625562306a36Sopenharmony_ci				   sizeof(*prssdp) + sizeof(*pprc_data) + 1,
625662306a36Sopenharmony_ci				   device, NULL);
625762306a36Sopenharmony_ci	if (IS_ERR(cqr)) {
625862306a36Sopenharmony_ci		DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s",
625962306a36Sopenharmony_ci				"Could not allocate query PPRC status request");
626062306a36Sopenharmony_ci		return PTR_ERR(cqr);
626162306a36Sopenharmony_ci	}
626262306a36Sopenharmony_ci	cqr->startdev = device;
626362306a36Sopenharmony_ci	cqr->memdev = device;
626462306a36Sopenharmony_ci	cqr->block = NULL;
626562306a36Sopenharmony_ci	cqr->retries = 256;
626662306a36Sopenharmony_ci	cqr->expires = 10 * HZ;
626762306a36Sopenharmony_ci
626862306a36Sopenharmony_ci	/* Prepare for Read Subsystem Data */
626962306a36Sopenharmony_ci	prssdp = (struct dasd_psf_prssd_data *)cqr->data;
627062306a36Sopenharmony_ci	memset(prssdp, 0, sizeof(struct dasd_psf_prssd_data));
627162306a36Sopenharmony_ci	prssdp->order = PSF_ORDER_PRSSD;
627262306a36Sopenharmony_ci	prssdp->suborder = PSF_SUBORDER_PPRCEQ;
627362306a36Sopenharmony_ci	prssdp->varies[0] = PPRCEQ_SCOPE_4;
627462306a36Sopenharmony_ci	pprc_data = (struct dasd_pprc_data_sc4 *)(prssdp + 1);
627562306a36Sopenharmony_ci
627662306a36Sopenharmony_ci	ccw = cqr->cpaddr;
627762306a36Sopenharmony_ci	ccw->cmd_code = DASD_ECKD_CCW_PSF;
627862306a36Sopenharmony_ci	ccw->count = sizeof(struct dasd_psf_prssd_data);
627962306a36Sopenharmony_ci	ccw->flags |= CCW_FLAG_CC;
628062306a36Sopenharmony_ci	ccw->flags |= CCW_FLAG_SLI;
628162306a36Sopenharmony_ci	ccw->cda = (__u32)(addr_t)prssdp;
628262306a36Sopenharmony_ci
628362306a36Sopenharmony_ci	/* Read Subsystem Data - query host access */
628462306a36Sopenharmony_ci	ccw++;
628562306a36Sopenharmony_ci	ccw->cmd_code = DASD_ECKD_CCW_RSSD;
628662306a36Sopenharmony_ci	ccw->count = sizeof(*pprc_data);
628762306a36Sopenharmony_ci	ccw->flags |= CCW_FLAG_SLI;
628862306a36Sopenharmony_ci	ccw->cda = (__u32)(addr_t)pprc_data;
628962306a36Sopenharmony_ci
629062306a36Sopenharmony_ci	cqr->buildclk = get_tod_clock();
629162306a36Sopenharmony_ci	cqr->status = DASD_CQR_FILLED;
629262306a36Sopenharmony_ci
629362306a36Sopenharmony_ci	rc = dasd_sleep_on_interruptible(cqr);
629462306a36Sopenharmony_ci	if (rc == 0) {
629562306a36Sopenharmony_ci		*data = *pprc_data;
629662306a36Sopenharmony_ci	} else {
629762306a36Sopenharmony_ci		DBF_EVENT_DEVID(DBF_WARNING, device->cdev,
629862306a36Sopenharmony_ci				"PPRC Extended Query failed with rc=%d\n",
629962306a36Sopenharmony_ci				rc);
630062306a36Sopenharmony_ci		rc = -EOPNOTSUPP;
630162306a36Sopenharmony_ci	}
630262306a36Sopenharmony_ci
630362306a36Sopenharmony_ci	dasd_sfree_request(cqr, cqr->memdev);
630462306a36Sopenharmony_ci	return rc;
630562306a36Sopenharmony_ci}
630662306a36Sopenharmony_ci
630762306a36Sopenharmony_ci/*
630862306a36Sopenharmony_ci * ECKD NOP - no operation
630962306a36Sopenharmony_ci */
631062306a36Sopenharmony_cistatic int dasd_eckd_nop(struct dasd_device *device)
631162306a36Sopenharmony_ci{
631262306a36Sopenharmony_ci	struct dasd_ccw_req *cqr;
631362306a36Sopenharmony_ci	struct ccw1 *ccw;
631462306a36Sopenharmony_ci	int rc;
631562306a36Sopenharmony_ci
631662306a36Sopenharmony_ci	cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1, 1, device, NULL);
631762306a36Sopenharmony_ci	if (IS_ERR(cqr)) {
631862306a36Sopenharmony_ci		DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s",
631962306a36Sopenharmony_ci				"Could not allocate NOP request");
632062306a36Sopenharmony_ci		return PTR_ERR(cqr);
632162306a36Sopenharmony_ci	}
632262306a36Sopenharmony_ci	cqr->startdev = device;
632362306a36Sopenharmony_ci	cqr->memdev = device;
632462306a36Sopenharmony_ci	cqr->block = NULL;
632562306a36Sopenharmony_ci	cqr->retries = 1;
632662306a36Sopenharmony_ci	cqr->expires = 10 * HZ;
632762306a36Sopenharmony_ci
632862306a36Sopenharmony_ci	ccw = cqr->cpaddr;
632962306a36Sopenharmony_ci	ccw->cmd_code = DASD_ECKD_CCW_NOP;
633062306a36Sopenharmony_ci	ccw->flags |= CCW_FLAG_SLI;
633162306a36Sopenharmony_ci
633262306a36Sopenharmony_ci	cqr->buildclk = get_tod_clock();
633362306a36Sopenharmony_ci	cqr->status = DASD_CQR_FILLED;
633462306a36Sopenharmony_ci
633562306a36Sopenharmony_ci	rc = dasd_sleep_on_interruptible(cqr);
633662306a36Sopenharmony_ci	if (rc != 0) {
633762306a36Sopenharmony_ci		DBF_EVENT_DEVID(DBF_WARNING, device->cdev,
633862306a36Sopenharmony_ci				"NOP failed with rc=%d\n", rc);
633962306a36Sopenharmony_ci		rc = -EOPNOTSUPP;
634062306a36Sopenharmony_ci	}
634162306a36Sopenharmony_ci	dasd_sfree_request(cqr, cqr->memdev);
634262306a36Sopenharmony_ci	return rc;
634362306a36Sopenharmony_ci}
634462306a36Sopenharmony_ci
634562306a36Sopenharmony_cistatic int dasd_eckd_device_ping(struct dasd_device *device)
634662306a36Sopenharmony_ci{
634762306a36Sopenharmony_ci	return dasd_eckd_nop(device);
634862306a36Sopenharmony_ci}
634962306a36Sopenharmony_ci
635062306a36Sopenharmony_ci/*
635162306a36Sopenharmony_ci * Perform Subsystem Function - CUIR response
635262306a36Sopenharmony_ci */
635362306a36Sopenharmony_cistatic int
635462306a36Sopenharmony_cidasd_eckd_psf_cuir_response(struct dasd_device *device, int response,
635562306a36Sopenharmony_ci			    __u32 message_id, __u8 lpum)
635662306a36Sopenharmony_ci{
635762306a36Sopenharmony_ci	struct dasd_psf_cuir_response *psf_cuir;
635862306a36Sopenharmony_ci	int pos = pathmask_to_pos(lpum);
635962306a36Sopenharmony_ci	struct dasd_ccw_req *cqr;
636062306a36Sopenharmony_ci	struct ccw1 *ccw;
636162306a36Sopenharmony_ci	int rc;
636262306a36Sopenharmony_ci
636362306a36Sopenharmony_ci	cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */ ,
636462306a36Sopenharmony_ci				   sizeof(struct dasd_psf_cuir_response),
636562306a36Sopenharmony_ci				   device, NULL);
636662306a36Sopenharmony_ci
636762306a36Sopenharmony_ci	if (IS_ERR(cqr)) {
636862306a36Sopenharmony_ci		DBF_DEV_EVENT(DBF_WARNING, device, "%s",
636962306a36Sopenharmony_ci			   "Could not allocate PSF-CUIR request");
637062306a36Sopenharmony_ci		return PTR_ERR(cqr);
637162306a36Sopenharmony_ci	}
637262306a36Sopenharmony_ci
637362306a36Sopenharmony_ci	psf_cuir = (struct dasd_psf_cuir_response *)cqr->data;
637462306a36Sopenharmony_ci	psf_cuir->order = PSF_ORDER_CUIR_RESPONSE;
637562306a36Sopenharmony_ci	psf_cuir->cc = response;
637662306a36Sopenharmony_ci	psf_cuir->chpid = device->path[pos].chpid;
637762306a36Sopenharmony_ci	psf_cuir->message_id = message_id;
637862306a36Sopenharmony_ci	psf_cuir->cssid = device->path[pos].cssid;
637962306a36Sopenharmony_ci	psf_cuir->ssid = device->path[pos].ssid;
638062306a36Sopenharmony_ci	ccw = cqr->cpaddr;
638162306a36Sopenharmony_ci	ccw->cmd_code = DASD_ECKD_CCW_PSF;
638262306a36Sopenharmony_ci	ccw->cda = (__u32)virt_to_phys(psf_cuir);
638362306a36Sopenharmony_ci	ccw->flags = CCW_FLAG_SLI;
638462306a36Sopenharmony_ci	ccw->count = sizeof(struct dasd_psf_cuir_response);
638562306a36Sopenharmony_ci
638662306a36Sopenharmony_ci	cqr->startdev = device;
638762306a36Sopenharmony_ci	cqr->memdev = device;
638862306a36Sopenharmony_ci	cqr->block = NULL;
638962306a36Sopenharmony_ci	cqr->retries = 256;
639062306a36Sopenharmony_ci	cqr->expires = 10*HZ;
639162306a36Sopenharmony_ci	cqr->buildclk = get_tod_clock();
639262306a36Sopenharmony_ci	cqr->status = DASD_CQR_FILLED;
639362306a36Sopenharmony_ci	set_bit(DASD_CQR_VERIFY_PATH, &cqr->flags);
639462306a36Sopenharmony_ci
639562306a36Sopenharmony_ci	rc = dasd_sleep_on(cqr);
639662306a36Sopenharmony_ci
639762306a36Sopenharmony_ci	dasd_sfree_request(cqr, cqr->memdev);
639862306a36Sopenharmony_ci	return rc;
639962306a36Sopenharmony_ci}
640062306a36Sopenharmony_ci
640162306a36Sopenharmony_ci/*
640262306a36Sopenharmony_ci * return configuration data that is referenced by record selector
640362306a36Sopenharmony_ci * if a record selector is specified or per default return the
640462306a36Sopenharmony_ci * conf_data pointer for the path specified by lpum
640562306a36Sopenharmony_ci */
640662306a36Sopenharmony_cistatic struct dasd_conf_data *dasd_eckd_get_ref_conf(struct dasd_device *device,
640762306a36Sopenharmony_ci						     __u8 lpum,
640862306a36Sopenharmony_ci						     struct dasd_cuir_message *cuir)
640962306a36Sopenharmony_ci{
641062306a36Sopenharmony_ci	struct dasd_conf_data *conf_data;
641162306a36Sopenharmony_ci	int path, pos;
641262306a36Sopenharmony_ci
641362306a36Sopenharmony_ci	if (cuir->record_selector == 0)
641462306a36Sopenharmony_ci		goto out;
641562306a36Sopenharmony_ci	for (path = 0x80, pos = 0; path; path >>= 1, pos++) {
641662306a36Sopenharmony_ci		conf_data = device->path[pos].conf_data;
641762306a36Sopenharmony_ci		if (conf_data->gneq.record_selector ==
641862306a36Sopenharmony_ci		    cuir->record_selector)
641962306a36Sopenharmony_ci			return conf_data;
642062306a36Sopenharmony_ci	}
642162306a36Sopenharmony_ciout:
642262306a36Sopenharmony_ci	return device->path[pathmask_to_pos(lpum)].conf_data;
642362306a36Sopenharmony_ci}
642462306a36Sopenharmony_ci
642562306a36Sopenharmony_ci/*
642662306a36Sopenharmony_ci * This function determines the scope of a reconfiguration request by
642762306a36Sopenharmony_ci * analysing the path and device selection data provided in the CUIR request.
642862306a36Sopenharmony_ci * Returns a path mask containing CUIR affected paths for the give device.
642962306a36Sopenharmony_ci *
643062306a36Sopenharmony_ci * If the CUIR request does not contain the required information return the
643162306a36Sopenharmony_ci * path mask of the path the attention message for the CUIR request was reveived
643262306a36Sopenharmony_ci * on.
643362306a36Sopenharmony_ci */
643462306a36Sopenharmony_cistatic int dasd_eckd_cuir_scope(struct dasd_device *device, __u8 lpum,
643562306a36Sopenharmony_ci				struct dasd_cuir_message *cuir)
643662306a36Sopenharmony_ci{
643762306a36Sopenharmony_ci	struct dasd_conf_data *ref_conf_data;
643862306a36Sopenharmony_ci	unsigned long bitmask = 0, mask = 0;
643962306a36Sopenharmony_ci	struct dasd_conf_data *conf_data;
644062306a36Sopenharmony_ci	unsigned int pos, path;
644162306a36Sopenharmony_ci	char *ref_gneq, *gneq;
644262306a36Sopenharmony_ci	char *ref_ned, *ned;
644362306a36Sopenharmony_ci	int tbcpm = 0;
644462306a36Sopenharmony_ci
644562306a36Sopenharmony_ci	/* if CUIR request does not specify the scope use the path
644662306a36Sopenharmony_ci	   the attention message was presented on */
644762306a36Sopenharmony_ci	if (!cuir->ned_map ||
644862306a36Sopenharmony_ci	    !(cuir->neq_map[0] | cuir->neq_map[1] | cuir->neq_map[2]))
644962306a36Sopenharmony_ci		return lpum;
645062306a36Sopenharmony_ci
645162306a36Sopenharmony_ci	/* get reference conf data */
645262306a36Sopenharmony_ci	ref_conf_data = dasd_eckd_get_ref_conf(device, lpum, cuir);
645362306a36Sopenharmony_ci	/* reference ned is determined by ned_map field */
645462306a36Sopenharmony_ci	pos = 8 - ffs(cuir->ned_map);
645562306a36Sopenharmony_ci	ref_ned = (char *)&ref_conf_data->neds[pos];
645662306a36Sopenharmony_ci	ref_gneq = (char *)&ref_conf_data->gneq;
645762306a36Sopenharmony_ci	/* transfer 24 bit neq_map to mask */
645862306a36Sopenharmony_ci	mask = cuir->neq_map[2];
645962306a36Sopenharmony_ci	mask |= cuir->neq_map[1] << 8;
646062306a36Sopenharmony_ci	mask |= cuir->neq_map[0] << 16;
646162306a36Sopenharmony_ci
646262306a36Sopenharmony_ci	for (path = 0; path < 8; path++) {
646362306a36Sopenharmony_ci		/* initialise data per path */
646462306a36Sopenharmony_ci		bitmask = mask;
646562306a36Sopenharmony_ci		conf_data = device->path[path].conf_data;
646662306a36Sopenharmony_ci		pos = 8 - ffs(cuir->ned_map);
646762306a36Sopenharmony_ci		ned = (char *) &conf_data->neds[pos];
646862306a36Sopenharmony_ci		/* compare reference ned and per path ned */
646962306a36Sopenharmony_ci		if (memcmp(ref_ned, ned, sizeof(*ned)) != 0)
647062306a36Sopenharmony_ci			continue;
647162306a36Sopenharmony_ci		gneq = (char *)&conf_data->gneq;
647262306a36Sopenharmony_ci		/* compare reference gneq and per_path gneq under
647362306a36Sopenharmony_ci		   24 bit mask where mask bit 0 equals byte 7 of
647462306a36Sopenharmony_ci		   the gneq and mask bit 24 equals byte 31 */
647562306a36Sopenharmony_ci		while (bitmask) {
647662306a36Sopenharmony_ci			pos = ffs(bitmask) - 1;
647762306a36Sopenharmony_ci			if (memcmp(&ref_gneq[31 - pos], &gneq[31 - pos], 1)
647862306a36Sopenharmony_ci			    != 0)
647962306a36Sopenharmony_ci				break;
648062306a36Sopenharmony_ci			clear_bit(pos, &bitmask);
648162306a36Sopenharmony_ci		}
648262306a36Sopenharmony_ci		if (bitmask)
648362306a36Sopenharmony_ci			continue;
648462306a36Sopenharmony_ci		/* device and path match the reference values
648562306a36Sopenharmony_ci		   add path to CUIR scope */
648662306a36Sopenharmony_ci		tbcpm |= 0x80 >> path;
648762306a36Sopenharmony_ci	}
648862306a36Sopenharmony_ci	return tbcpm;
648962306a36Sopenharmony_ci}
649062306a36Sopenharmony_ci
649162306a36Sopenharmony_cistatic void dasd_eckd_cuir_notify_user(struct dasd_device *device,
649262306a36Sopenharmony_ci				       unsigned long paths, int action)
649362306a36Sopenharmony_ci{
649462306a36Sopenharmony_ci	int pos;
649562306a36Sopenharmony_ci
649662306a36Sopenharmony_ci	while (paths) {
649762306a36Sopenharmony_ci		/* get position of bit in mask */
649862306a36Sopenharmony_ci		pos = 8 - ffs(paths);
649962306a36Sopenharmony_ci		/* get channel path descriptor from this position */
650062306a36Sopenharmony_ci		if (action == CUIR_QUIESCE)
650162306a36Sopenharmony_ci			pr_warn("Service on the storage server caused path %x.%02x to go offline",
650262306a36Sopenharmony_ci				device->path[pos].cssid,
650362306a36Sopenharmony_ci				device->path[pos].chpid);
650462306a36Sopenharmony_ci		else if (action == CUIR_RESUME)
650562306a36Sopenharmony_ci			pr_info("Path %x.%02x is back online after service on the storage server",
650662306a36Sopenharmony_ci				device->path[pos].cssid,
650762306a36Sopenharmony_ci				device->path[pos].chpid);
650862306a36Sopenharmony_ci		clear_bit(7 - pos, &paths);
650962306a36Sopenharmony_ci	}
651062306a36Sopenharmony_ci}
651162306a36Sopenharmony_ci
651262306a36Sopenharmony_cistatic int dasd_eckd_cuir_remove_path(struct dasd_device *device, __u8 lpum,
651362306a36Sopenharmony_ci				      struct dasd_cuir_message *cuir)
651462306a36Sopenharmony_ci{
651562306a36Sopenharmony_ci	unsigned long tbcpm;
651662306a36Sopenharmony_ci
651762306a36Sopenharmony_ci	tbcpm = dasd_eckd_cuir_scope(device, lpum, cuir);
651862306a36Sopenharmony_ci	/* nothing to do if path is not in use */
651962306a36Sopenharmony_ci	if (!(dasd_path_get_opm(device) & tbcpm))
652062306a36Sopenharmony_ci		return 0;
652162306a36Sopenharmony_ci	if (!(dasd_path_get_opm(device) & ~tbcpm)) {
652262306a36Sopenharmony_ci		/* no path would be left if the CUIR action is taken
652362306a36Sopenharmony_ci		   return error */
652462306a36Sopenharmony_ci		return -EINVAL;
652562306a36Sopenharmony_ci	}
652662306a36Sopenharmony_ci	/* remove device from operational path mask */
652762306a36Sopenharmony_ci	dasd_path_remove_opm(device, tbcpm);
652862306a36Sopenharmony_ci	dasd_path_add_cuirpm(device, tbcpm);
652962306a36Sopenharmony_ci	return tbcpm;
653062306a36Sopenharmony_ci}
653162306a36Sopenharmony_ci
653262306a36Sopenharmony_ci/*
653362306a36Sopenharmony_ci * walk through all devices and build a path mask to quiesce them
653462306a36Sopenharmony_ci * return an error if the last path to a device would be removed
653562306a36Sopenharmony_ci *
653662306a36Sopenharmony_ci * if only part of the devices are quiesced and an error
653762306a36Sopenharmony_ci * occurs no onlining necessary, the storage server will
653862306a36Sopenharmony_ci * notify the already set offline devices again
653962306a36Sopenharmony_ci */
654062306a36Sopenharmony_cistatic int dasd_eckd_cuir_quiesce(struct dasd_device *device, __u8 lpum,
654162306a36Sopenharmony_ci				  struct dasd_cuir_message *cuir)
654262306a36Sopenharmony_ci{
654362306a36Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
654462306a36Sopenharmony_ci	struct alias_pav_group *pavgroup, *tempgroup;
654562306a36Sopenharmony_ci	struct dasd_device *dev, *n;
654662306a36Sopenharmony_ci	unsigned long paths = 0;
654762306a36Sopenharmony_ci	unsigned long flags;
654862306a36Sopenharmony_ci	int tbcpm;
654962306a36Sopenharmony_ci
655062306a36Sopenharmony_ci	/* active devices */
655162306a36Sopenharmony_ci	list_for_each_entry_safe(dev, n, &private->lcu->active_devices,
655262306a36Sopenharmony_ci				 alias_list) {
655362306a36Sopenharmony_ci		spin_lock_irqsave(get_ccwdev_lock(dev->cdev), flags);
655462306a36Sopenharmony_ci		tbcpm = dasd_eckd_cuir_remove_path(dev, lpum, cuir);
655562306a36Sopenharmony_ci		spin_unlock_irqrestore(get_ccwdev_lock(dev->cdev), flags);
655662306a36Sopenharmony_ci		if (tbcpm < 0)
655762306a36Sopenharmony_ci			goto out_err;
655862306a36Sopenharmony_ci		paths |= tbcpm;
655962306a36Sopenharmony_ci	}
656062306a36Sopenharmony_ci	/* inactive devices */
656162306a36Sopenharmony_ci	list_for_each_entry_safe(dev, n, &private->lcu->inactive_devices,
656262306a36Sopenharmony_ci				 alias_list) {
656362306a36Sopenharmony_ci		spin_lock_irqsave(get_ccwdev_lock(dev->cdev), flags);
656462306a36Sopenharmony_ci		tbcpm = dasd_eckd_cuir_remove_path(dev, lpum, cuir);
656562306a36Sopenharmony_ci		spin_unlock_irqrestore(get_ccwdev_lock(dev->cdev), flags);
656662306a36Sopenharmony_ci		if (tbcpm < 0)
656762306a36Sopenharmony_ci			goto out_err;
656862306a36Sopenharmony_ci		paths |= tbcpm;
656962306a36Sopenharmony_ci	}
657062306a36Sopenharmony_ci	/* devices in PAV groups */
657162306a36Sopenharmony_ci	list_for_each_entry_safe(pavgroup, tempgroup,
657262306a36Sopenharmony_ci				 &private->lcu->grouplist, group) {
657362306a36Sopenharmony_ci		list_for_each_entry_safe(dev, n, &pavgroup->baselist,
657462306a36Sopenharmony_ci					 alias_list) {
657562306a36Sopenharmony_ci			spin_lock_irqsave(get_ccwdev_lock(dev->cdev), flags);
657662306a36Sopenharmony_ci			tbcpm = dasd_eckd_cuir_remove_path(dev, lpum, cuir);
657762306a36Sopenharmony_ci			spin_unlock_irqrestore(
657862306a36Sopenharmony_ci				get_ccwdev_lock(dev->cdev), flags);
657962306a36Sopenharmony_ci			if (tbcpm < 0)
658062306a36Sopenharmony_ci				goto out_err;
658162306a36Sopenharmony_ci			paths |= tbcpm;
658262306a36Sopenharmony_ci		}
658362306a36Sopenharmony_ci		list_for_each_entry_safe(dev, n, &pavgroup->aliaslist,
658462306a36Sopenharmony_ci					 alias_list) {
658562306a36Sopenharmony_ci			spin_lock_irqsave(get_ccwdev_lock(dev->cdev), flags);
658662306a36Sopenharmony_ci			tbcpm = dasd_eckd_cuir_remove_path(dev, lpum, cuir);
658762306a36Sopenharmony_ci			spin_unlock_irqrestore(
658862306a36Sopenharmony_ci				get_ccwdev_lock(dev->cdev), flags);
658962306a36Sopenharmony_ci			if (tbcpm < 0)
659062306a36Sopenharmony_ci				goto out_err;
659162306a36Sopenharmony_ci			paths |= tbcpm;
659262306a36Sopenharmony_ci		}
659362306a36Sopenharmony_ci	}
659462306a36Sopenharmony_ci	/* notify user about all paths affected by CUIR action */
659562306a36Sopenharmony_ci	dasd_eckd_cuir_notify_user(device, paths, CUIR_QUIESCE);
659662306a36Sopenharmony_ci	return 0;
659762306a36Sopenharmony_ciout_err:
659862306a36Sopenharmony_ci	return tbcpm;
659962306a36Sopenharmony_ci}
660062306a36Sopenharmony_ci
660162306a36Sopenharmony_cistatic int dasd_eckd_cuir_resume(struct dasd_device *device, __u8 lpum,
660262306a36Sopenharmony_ci				 struct dasd_cuir_message *cuir)
660362306a36Sopenharmony_ci{
660462306a36Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
660562306a36Sopenharmony_ci	struct alias_pav_group *pavgroup, *tempgroup;
660662306a36Sopenharmony_ci	struct dasd_device *dev, *n;
660762306a36Sopenharmony_ci	unsigned long paths = 0;
660862306a36Sopenharmony_ci	int tbcpm;
660962306a36Sopenharmony_ci
661062306a36Sopenharmony_ci	/*
661162306a36Sopenharmony_ci	 * the path may have been added through a generic path event before
661262306a36Sopenharmony_ci	 * only trigger path verification if the path is not already in use
661362306a36Sopenharmony_ci	 */
661462306a36Sopenharmony_ci	list_for_each_entry_safe(dev, n,
661562306a36Sopenharmony_ci				 &private->lcu->active_devices,
661662306a36Sopenharmony_ci				 alias_list) {
661762306a36Sopenharmony_ci		tbcpm = dasd_eckd_cuir_scope(dev, lpum, cuir);
661862306a36Sopenharmony_ci		paths |= tbcpm;
661962306a36Sopenharmony_ci		if (!(dasd_path_get_opm(dev) & tbcpm)) {
662062306a36Sopenharmony_ci			dasd_path_add_tbvpm(dev, tbcpm);
662162306a36Sopenharmony_ci			dasd_schedule_device_bh(dev);
662262306a36Sopenharmony_ci		}
662362306a36Sopenharmony_ci	}
662462306a36Sopenharmony_ci	list_for_each_entry_safe(dev, n,
662562306a36Sopenharmony_ci				 &private->lcu->inactive_devices,
662662306a36Sopenharmony_ci				 alias_list) {
662762306a36Sopenharmony_ci		tbcpm = dasd_eckd_cuir_scope(dev, lpum, cuir);
662862306a36Sopenharmony_ci		paths |= tbcpm;
662962306a36Sopenharmony_ci		if (!(dasd_path_get_opm(dev) & tbcpm)) {
663062306a36Sopenharmony_ci			dasd_path_add_tbvpm(dev, tbcpm);
663162306a36Sopenharmony_ci			dasd_schedule_device_bh(dev);
663262306a36Sopenharmony_ci		}
663362306a36Sopenharmony_ci	}
663462306a36Sopenharmony_ci	/* devices in PAV groups */
663562306a36Sopenharmony_ci	list_for_each_entry_safe(pavgroup, tempgroup,
663662306a36Sopenharmony_ci				 &private->lcu->grouplist,
663762306a36Sopenharmony_ci				 group) {
663862306a36Sopenharmony_ci		list_for_each_entry_safe(dev, n,
663962306a36Sopenharmony_ci					 &pavgroup->baselist,
664062306a36Sopenharmony_ci					 alias_list) {
664162306a36Sopenharmony_ci			tbcpm = dasd_eckd_cuir_scope(dev, lpum, cuir);
664262306a36Sopenharmony_ci			paths |= tbcpm;
664362306a36Sopenharmony_ci			if (!(dasd_path_get_opm(dev) & tbcpm)) {
664462306a36Sopenharmony_ci				dasd_path_add_tbvpm(dev, tbcpm);
664562306a36Sopenharmony_ci				dasd_schedule_device_bh(dev);
664662306a36Sopenharmony_ci			}
664762306a36Sopenharmony_ci		}
664862306a36Sopenharmony_ci		list_for_each_entry_safe(dev, n,
664962306a36Sopenharmony_ci					 &pavgroup->aliaslist,
665062306a36Sopenharmony_ci					 alias_list) {
665162306a36Sopenharmony_ci			tbcpm = dasd_eckd_cuir_scope(dev, lpum, cuir);
665262306a36Sopenharmony_ci			paths |= tbcpm;
665362306a36Sopenharmony_ci			if (!(dasd_path_get_opm(dev) & tbcpm)) {
665462306a36Sopenharmony_ci				dasd_path_add_tbvpm(dev, tbcpm);
665562306a36Sopenharmony_ci				dasd_schedule_device_bh(dev);
665662306a36Sopenharmony_ci			}
665762306a36Sopenharmony_ci		}
665862306a36Sopenharmony_ci	}
665962306a36Sopenharmony_ci	/* notify user about all paths affected by CUIR action */
666062306a36Sopenharmony_ci	dasd_eckd_cuir_notify_user(device, paths, CUIR_RESUME);
666162306a36Sopenharmony_ci	return 0;
666262306a36Sopenharmony_ci}
666362306a36Sopenharmony_ci
666462306a36Sopenharmony_cistatic void dasd_eckd_handle_cuir(struct dasd_device *device, void *messages,
666562306a36Sopenharmony_ci				 __u8 lpum)
666662306a36Sopenharmony_ci{
666762306a36Sopenharmony_ci	struct dasd_cuir_message *cuir = messages;
666862306a36Sopenharmony_ci	int response;
666962306a36Sopenharmony_ci
667062306a36Sopenharmony_ci	DBF_DEV_EVENT(DBF_WARNING, device,
667162306a36Sopenharmony_ci		      "CUIR request: %016llx %016llx %016llx %08x",
667262306a36Sopenharmony_ci		      ((u64 *)cuir)[0], ((u64 *)cuir)[1], ((u64 *)cuir)[2],
667362306a36Sopenharmony_ci		      ((u32 *)cuir)[3]);
667462306a36Sopenharmony_ci
667562306a36Sopenharmony_ci	if (cuir->code == CUIR_QUIESCE) {
667662306a36Sopenharmony_ci		/* quiesce */
667762306a36Sopenharmony_ci		if (dasd_eckd_cuir_quiesce(device, lpum, cuir))
667862306a36Sopenharmony_ci			response = PSF_CUIR_LAST_PATH;
667962306a36Sopenharmony_ci		else
668062306a36Sopenharmony_ci			response = PSF_CUIR_COMPLETED;
668162306a36Sopenharmony_ci	} else if (cuir->code == CUIR_RESUME) {
668262306a36Sopenharmony_ci		/* resume */
668362306a36Sopenharmony_ci		dasd_eckd_cuir_resume(device, lpum, cuir);
668462306a36Sopenharmony_ci		response = PSF_CUIR_COMPLETED;
668562306a36Sopenharmony_ci	} else
668662306a36Sopenharmony_ci		response = PSF_CUIR_NOT_SUPPORTED;
668762306a36Sopenharmony_ci
668862306a36Sopenharmony_ci	dasd_eckd_psf_cuir_response(device, response,
668962306a36Sopenharmony_ci				    cuir->message_id, lpum);
669062306a36Sopenharmony_ci	DBF_DEV_EVENT(DBF_WARNING, device,
669162306a36Sopenharmony_ci		      "CUIR response: %d on message ID %08x", response,
669262306a36Sopenharmony_ci		      cuir->message_id);
669362306a36Sopenharmony_ci	/* to make sure there is no attention left schedule work again */
669462306a36Sopenharmony_ci	device->discipline->check_attention(device, lpum);
669562306a36Sopenharmony_ci}
669662306a36Sopenharmony_ci
669762306a36Sopenharmony_cistatic void dasd_eckd_oos_resume(struct dasd_device *device)
669862306a36Sopenharmony_ci{
669962306a36Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
670062306a36Sopenharmony_ci	struct alias_pav_group *pavgroup, *tempgroup;
670162306a36Sopenharmony_ci	struct dasd_device *dev, *n;
670262306a36Sopenharmony_ci	unsigned long flags;
670362306a36Sopenharmony_ci
670462306a36Sopenharmony_ci	spin_lock_irqsave(&private->lcu->lock, flags);
670562306a36Sopenharmony_ci	list_for_each_entry_safe(dev, n, &private->lcu->active_devices,
670662306a36Sopenharmony_ci				 alias_list) {
670762306a36Sopenharmony_ci		if (dev->stopped & DASD_STOPPED_NOSPC)
670862306a36Sopenharmony_ci			dasd_generic_space_avail(dev);
670962306a36Sopenharmony_ci	}
671062306a36Sopenharmony_ci	list_for_each_entry_safe(dev, n, &private->lcu->inactive_devices,
671162306a36Sopenharmony_ci				 alias_list) {
671262306a36Sopenharmony_ci		if (dev->stopped & DASD_STOPPED_NOSPC)
671362306a36Sopenharmony_ci			dasd_generic_space_avail(dev);
671462306a36Sopenharmony_ci	}
671562306a36Sopenharmony_ci	/* devices in PAV groups */
671662306a36Sopenharmony_ci	list_for_each_entry_safe(pavgroup, tempgroup,
671762306a36Sopenharmony_ci				 &private->lcu->grouplist,
671862306a36Sopenharmony_ci				 group) {
671962306a36Sopenharmony_ci		list_for_each_entry_safe(dev, n, &pavgroup->baselist,
672062306a36Sopenharmony_ci					 alias_list) {
672162306a36Sopenharmony_ci			if (dev->stopped & DASD_STOPPED_NOSPC)
672262306a36Sopenharmony_ci				dasd_generic_space_avail(dev);
672362306a36Sopenharmony_ci		}
672462306a36Sopenharmony_ci		list_for_each_entry_safe(dev, n, &pavgroup->aliaslist,
672562306a36Sopenharmony_ci					 alias_list) {
672662306a36Sopenharmony_ci			if (dev->stopped & DASD_STOPPED_NOSPC)
672762306a36Sopenharmony_ci				dasd_generic_space_avail(dev);
672862306a36Sopenharmony_ci		}
672962306a36Sopenharmony_ci	}
673062306a36Sopenharmony_ci	spin_unlock_irqrestore(&private->lcu->lock, flags);
673162306a36Sopenharmony_ci}
673262306a36Sopenharmony_ci
673362306a36Sopenharmony_cistatic void dasd_eckd_handle_oos(struct dasd_device *device, void *messages,
673462306a36Sopenharmony_ci				 __u8 lpum)
673562306a36Sopenharmony_ci{
673662306a36Sopenharmony_ci	struct dasd_oos_message *oos = messages;
673762306a36Sopenharmony_ci
673862306a36Sopenharmony_ci	switch (oos->code) {
673962306a36Sopenharmony_ci	case REPO_WARN:
674062306a36Sopenharmony_ci	case POOL_WARN:
674162306a36Sopenharmony_ci		dev_warn(&device->cdev->dev,
674262306a36Sopenharmony_ci			 "Extent pool usage has reached a critical value\n");
674362306a36Sopenharmony_ci		dasd_eckd_oos_resume(device);
674462306a36Sopenharmony_ci		break;
674562306a36Sopenharmony_ci	case REPO_EXHAUST:
674662306a36Sopenharmony_ci	case POOL_EXHAUST:
674762306a36Sopenharmony_ci		dev_warn(&device->cdev->dev,
674862306a36Sopenharmony_ci			 "Extent pool is exhausted\n");
674962306a36Sopenharmony_ci		break;
675062306a36Sopenharmony_ci	case REPO_RELIEVE:
675162306a36Sopenharmony_ci	case POOL_RELIEVE:
675262306a36Sopenharmony_ci		dev_info(&device->cdev->dev,
675362306a36Sopenharmony_ci			 "Extent pool physical space constraint has been relieved\n");
675462306a36Sopenharmony_ci		break;
675562306a36Sopenharmony_ci	}
675662306a36Sopenharmony_ci
675762306a36Sopenharmony_ci	/* In any case, update related data */
675862306a36Sopenharmony_ci	dasd_eckd_read_ext_pool_info(device);
675962306a36Sopenharmony_ci
676062306a36Sopenharmony_ci	/* to make sure there is no attention left schedule work again */
676162306a36Sopenharmony_ci	device->discipline->check_attention(device, lpum);
676262306a36Sopenharmony_ci}
676362306a36Sopenharmony_ci
676462306a36Sopenharmony_cistatic void dasd_eckd_check_attention_work(struct work_struct *work)
676562306a36Sopenharmony_ci{
676662306a36Sopenharmony_ci	struct check_attention_work_data *data;
676762306a36Sopenharmony_ci	struct dasd_rssd_messages *messages;
676862306a36Sopenharmony_ci	struct dasd_device *device;
676962306a36Sopenharmony_ci	int rc;
677062306a36Sopenharmony_ci
677162306a36Sopenharmony_ci	data = container_of(work, struct check_attention_work_data, worker);
677262306a36Sopenharmony_ci	device = data->device;
677362306a36Sopenharmony_ci	messages = kzalloc(sizeof(*messages), GFP_KERNEL);
677462306a36Sopenharmony_ci	if (!messages) {
677562306a36Sopenharmony_ci		DBF_DEV_EVENT(DBF_WARNING, device, "%s",
677662306a36Sopenharmony_ci			      "Could not allocate attention message buffer");
677762306a36Sopenharmony_ci		goto out;
677862306a36Sopenharmony_ci	}
677962306a36Sopenharmony_ci	rc = dasd_eckd_read_message_buffer(device, messages, data->lpum);
678062306a36Sopenharmony_ci	if (rc)
678162306a36Sopenharmony_ci		goto out;
678262306a36Sopenharmony_ci
678362306a36Sopenharmony_ci	if (messages->length == ATTENTION_LENGTH_CUIR &&
678462306a36Sopenharmony_ci	    messages->format == ATTENTION_FORMAT_CUIR)
678562306a36Sopenharmony_ci		dasd_eckd_handle_cuir(device, messages, data->lpum);
678662306a36Sopenharmony_ci	if (messages->length == ATTENTION_LENGTH_OOS &&
678762306a36Sopenharmony_ci	    messages->format == ATTENTION_FORMAT_OOS)
678862306a36Sopenharmony_ci		dasd_eckd_handle_oos(device, messages, data->lpum);
678962306a36Sopenharmony_ci
679062306a36Sopenharmony_ciout:
679162306a36Sopenharmony_ci	dasd_put_device(device);
679262306a36Sopenharmony_ci	kfree(messages);
679362306a36Sopenharmony_ci	kfree(data);
679462306a36Sopenharmony_ci}
679562306a36Sopenharmony_ci
679662306a36Sopenharmony_cistatic int dasd_eckd_check_attention(struct dasd_device *device, __u8 lpum)
679762306a36Sopenharmony_ci{
679862306a36Sopenharmony_ci	struct check_attention_work_data *data;
679962306a36Sopenharmony_ci
680062306a36Sopenharmony_ci	data = kzalloc(sizeof(*data), GFP_ATOMIC);
680162306a36Sopenharmony_ci	if (!data)
680262306a36Sopenharmony_ci		return -ENOMEM;
680362306a36Sopenharmony_ci	INIT_WORK(&data->worker, dasd_eckd_check_attention_work);
680462306a36Sopenharmony_ci	dasd_get_device(device);
680562306a36Sopenharmony_ci	data->device = device;
680662306a36Sopenharmony_ci	data->lpum = lpum;
680762306a36Sopenharmony_ci	schedule_work(&data->worker);
680862306a36Sopenharmony_ci	return 0;
680962306a36Sopenharmony_ci}
681062306a36Sopenharmony_ci
681162306a36Sopenharmony_cistatic int dasd_eckd_disable_hpf_path(struct dasd_device *device, __u8 lpum)
681262306a36Sopenharmony_ci{
681362306a36Sopenharmony_ci	if (~lpum & dasd_path_get_opm(device)) {
681462306a36Sopenharmony_ci		dasd_path_add_nohpfpm(device, lpum);
681562306a36Sopenharmony_ci		dasd_path_remove_opm(device, lpum);
681662306a36Sopenharmony_ci		dev_err(&device->cdev->dev,
681762306a36Sopenharmony_ci			"Channel path %02X lost HPF functionality and is disabled\n",
681862306a36Sopenharmony_ci			lpum);
681962306a36Sopenharmony_ci		return 1;
682062306a36Sopenharmony_ci	}
682162306a36Sopenharmony_ci	return 0;
682262306a36Sopenharmony_ci}
682362306a36Sopenharmony_ci
682462306a36Sopenharmony_cistatic void dasd_eckd_disable_hpf_device(struct dasd_device *device)
682562306a36Sopenharmony_ci{
682662306a36Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
682762306a36Sopenharmony_ci
682862306a36Sopenharmony_ci	dev_err(&device->cdev->dev,
682962306a36Sopenharmony_ci		"High Performance FICON disabled\n");
683062306a36Sopenharmony_ci	private->fcx_max_data = 0;
683162306a36Sopenharmony_ci}
683262306a36Sopenharmony_ci
683362306a36Sopenharmony_cistatic int dasd_eckd_hpf_enabled(struct dasd_device *device)
683462306a36Sopenharmony_ci{
683562306a36Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
683662306a36Sopenharmony_ci
683762306a36Sopenharmony_ci	return private->fcx_max_data ? 1 : 0;
683862306a36Sopenharmony_ci}
683962306a36Sopenharmony_ci
684062306a36Sopenharmony_cistatic void dasd_eckd_handle_hpf_error(struct dasd_device *device,
684162306a36Sopenharmony_ci				       struct irb *irb)
684262306a36Sopenharmony_ci{
684362306a36Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
684462306a36Sopenharmony_ci
684562306a36Sopenharmony_ci	if (!private->fcx_max_data) {
684662306a36Sopenharmony_ci		/* sanity check for no HPF, the error makes no sense */
684762306a36Sopenharmony_ci		DBF_DEV_EVENT(DBF_WARNING, device, "%s",
684862306a36Sopenharmony_ci			      "Trying to disable HPF for a non HPF device");
684962306a36Sopenharmony_ci		return;
685062306a36Sopenharmony_ci	}
685162306a36Sopenharmony_ci	if (irb->scsw.tm.sesq == SCSW_SESQ_DEV_NOFCX) {
685262306a36Sopenharmony_ci		dasd_eckd_disable_hpf_device(device);
685362306a36Sopenharmony_ci	} else if (irb->scsw.tm.sesq == SCSW_SESQ_PATH_NOFCX) {
685462306a36Sopenharmony_ci		if (dasd_eckd_disable_hpf_path(device, irb->esw.esw1.lpum))
685562306a36Sopenharmony_ci			return;
685662306a36Sopenharmony_ci		dasd_eckd_disable_hpf_device(device);
685762306a36Sopenharmony_ci		dasd_path_set_tbvpm(device,
685862306a36Sopenharmony_ci				  dasd_path_get_hpfpm(device));
685962306a36Sopenharmony_ci	}
686062306a36Sopenharmony_ci	/*
686162306a36Sopenharmony_ci	 * prevent that any new I/O ist started on the device and schedule a
686262306a36Sopenharmony_ci	 * requeue of existing requests
686362306a36Sopenharmony_ci	 */
686462306a36Sopenharmony_ci	dasd_device_set_stop_bits(device, DASD_STOPPED_NOT_ACC);
686562306a36Sopenharmony_ci	dasd_schedule_requeue(device);
686662306a36Sopenharmony_ci}
686762306a36Sopenharmony_ci
686862306a36Sopenharmony_ci/*
686962306a36Sopenharmony_ci * Initialize block layer request queue.
687062306a36Sopenharmony_ci */
687162306a36Sopenharmony_cistatic void dasd_eckd_setup_blk_queue(struct dasd_block *block)
687262306a36Sopenharmony_ci{
687362306a36Sopenharmony_ci	unsigned int logical_block_size = block->bp_block;
687462306a36Sopenharmony_ci	struct request_queue *q = block->gdp->queue;
687562306a36Sopenharmony_ci	struct dasd_device *device = block->base;
687662306a36Sopenharmony_ci	int max;
687762306a36Sopenharmony_ci
687862306a36Sopenharmony_ci	if (device->features & DASD_FEATURE_USERAW) {
687962306a36Sopenharmony_ci		/*
688062306a36Sopenharmony_ci		 * the max_blocks value for raw_track access is 256
688162306a36Sopenharmony_ci		 * it is higher than the native ECKD value because we
688262306a36Sopenharmony_ci		 * only need one ccw per track
688362306a36Sopenharmony_ci		 * so the max_hw_sectors are
688462306a36Sopenharmony_ci		 * 2048 x 512B = 1024kB = 16 tracks
688562306a36Sopenharmony_ci		 */
688662306a36Sopenharmony_ci		max = DASD_ECKD_MAX_BLOCKS_RAW << block->s2b_shift;
688762306a36Sopenharmony_ci	} else {
688862306a36Sopenharmony_ci		max = DASD_ECKD_MAX_BLOCKS << block->s2b_shift;
688962306a36Sopenharmony_ci	}
689062306a36Sopenharmony_ci	blk_queue_flag_set(QUEUE_FLAG_NONROT, q);
689162306a36Sopenharmony_ci	q->limits.max_dev_sectors = max;
689262306a36Sopenharmony_ci	blk_queue_logical_block_size(q, logical_block_size);
689362306a36Sopenharmony_ci	blk_queue_max_hw_sectors(q, max);
689462306a36Sopenharmony_ci	blk_queue_max_segments(q, USHRT_MAX);
689562306a36Sopenharmony_ci	/* With page sized segments each segment can be translated into one idaw/tidaw */
689662306a36Sopenharmony_ci	blk_queue_max_segment_size(q, PAGE_SIZE);
689762306a36Sopenharmony_ci	blk_queue_segment_boundary(q, PAGE_SIZE - 1);
689862306a36Sopenharmony_ci	blk_queue_dma_alignment(q, PAGE_SIZE - 1);
689962306a36Sopenharmony_ci}
690062306a36Sopenharmony_ci
690162306a36Sopenharmony_cistatic struct ccw_driver dasd_eckd_driver = {
690262306a36Sopenharmony_ci	.driver = {
690362306a36Sopenharmony_ci		.name	= "dasd-eckd",
690462306a36Sopenharmony_ci		.owner	= THIS_MODULE,
690562306a36Sopenharmony_ci		.dev_groups = dasd_dev_groups,
690662306a36Sopenharmony_ci	},
690762306a36Sopenharmony_ci	.ids	     = dasd_eckd_ids,
690862306a36Sopenharmony_ci	.probe	     = dasd_eckd_probe,
690962306a36Sopenharmony_ci	.remove      = dasd_generic_remove,
691062306a36Sopenharmony_ci	.set_offline = dasd_generic_set_offline,
691162306a36Sopenharmony_ci	.set_online  = dasd_eckd_set_online,
691262306a36Sopenharmony_ci	.notify      = dasd_generic_notify,
691362306a36Sopenharmony_ci	.path_event  = dasd_generic_path_event,
691462306a36Sopenharmony_ci	.shutdown    = dasd_generic_shutdown,
691562306a36Sopenharmony_ci	.uc_handler  = dasd_generic_uc_handler,
691662306a36Sopenharmony_ci	.int_class   = IRQIO_DAS,
691762306a36Sopenharmony_ci};
691862306a36Sopenharmony_ci
691962306a36Sopenharmony_cistatic struct dasd_discipline dasd_eckd_discipline = {
692062306a36Sopenharmony_ci	.owner = THIS_MODULE,
692162306a36Sopenharmony_ci	.name = "ECKD",
692262306a36Sopenharmony_ci	.ebcname = "ECKD",
692362306a36Sopenharmony_ci	.check_device = dasd_eckd_check_characteristics,
692462306a36Sopenharmony_ci	.uncheck_device = dasd_eckd_uncheck_device,
692562306a36Sopenharmony_ci	.do_analysis = dasd_eckd_do_analysis,
692662306a36Sopenharmony_ci	.pe_handler = dasd_eckd_pe_handler,
692762306a36Sopenharmony_ci	.basic_to_ready = dasd_eckd_basic_to_ready,
692862306a36Sopenharmony_ci	.online_to_ready = dasd_eckd_online_to_ready,
692962306a36Sopenharmony_ci	.basic_to_known = dasd_eckd_basic_to_known,
693062306a36Sopenharmony_ci	.setup_blk_queue = dasd_eckd_setup_blk_queue,
693162306a36Sopenharmony_ci	.fill_geometry = dasd_eckd_fill_geometry,
693262306a36Sopenharmony_ci	.start_IO = dasd_start_IO,
693362306a36Sopenharmony_ci	.term_IO = dasd_term_IO,
693462306a36Sopenharmony_ci	.handle_terminated_request = dasd_eckd_handle_terminated_request,
693562306a36Sopenharmony_ci	.format_device = dasd_eckd_format_device,
693662306a36Sopenharmony_ci	.check_device_format = dasd_eckd_check_device_format,
693762306a36Sopenharmony_ci	.erp_action = dasd_eckd_erp_action,
693862306a36Sopenharmony_ci	.erp_postaction = dasd_eckd_erp_postaction,
693962306a36Sopenharmony_ci	.check_for_device_change = dasd_eckd_check_for_device_change,
694062306a36Sopenharmony_ci	.build_cp = dasd_eckd_build_alias_cp,
694162306a36Sopenharmony_ci	.free_cp = dasd_eckd_free_alias_cp,
694262306a36Sopenharmony_ci	.dump_sense = dasd_eckd_dump_sense,
694362306a36Sopenharmony_ci	.dump_sense_dbf = dasd_eckd_dump_sense_dbf,
694462306a36Sopenharmony_ci	.fill_info = dasd_eckd_fill_info,
694562306a36Sopenharmony_ci	.ioctl = dasd_eckd_ioctl,
694662306a36Sopenharmony_ci	.reload = dasd_eckd_reload_device,
694762306a36Sopenharmony_ci	.get_uid = dasd_eckd_get_uid,
694862306a36Sopenharmony_ci	.kick_validate = dasd_eckd_kick_validate_server,
694962306a36Sopenharmony_ci	.check_attention = dasd_eckd_check_attention,
695062306a36Sopenharmony_ci	.host_access_count = dasd_eckd_host_access_count,
695162306a36Sopenharmony_ci	.hosts_print = dasd_hosts_print,
695262306a36Sopenharmony_ci	.handle_hpf_error = dasd_eckd_handle_hpf_error,
695362306a36Sopenharmony_ci	.disable_hpf = dasd_eckd_disable_hpf_device,
695462306a36Sopenharmony_ci	.hpf_enabled = dasd_eckd_hpf_enabled,
695562306a36Sopenharmony_ci	.reset_path = dasd_eckd_reset_path,
695662306a36Sopenharmony_ci	.is_ese = dasd_eckd_is_ese,
695762306a36Sopenharmony_ci	.space_allocated = dasd_eckd_space_allocated,
695862306a36Sopenharmony_ci	.space_configured = dasd_eckd_space_configured,
695962306a36Sopenharmony_ci	.logical_capacity = dasd_eckd_logical_capacity,
696062306a36Sopenharmony_ci	.release_space = dasd_eckd_release_space,
696162306a36Sopenharmony_ci	.ext_pool_id = dasd_eckd_ext_pool_id,
696262306a36Sopenharmony_ci	.ext_size = dasd_eckd_ext_size,
696362306a36Sopenharmony_ci	.ext_pool_cap_at_warnlevel = dasd_eckd_ext_pool_cap_at_warnlevel,
696462306a36Sopenharmony_ci	.ext_pool_warn_thrshld = dasd_eckd_ext_pool_warn_thrshld,
696562306a36Sopenharmony_ci	.ext_pool_oos = dasd_eckd_ext_pool_oos,
696662306a36Sopenharmony_ci	.ext_pool_exhaust = dasd_eckd_ext_pool_exhaust,
696762306a36Sopenharmony_ci	.ese_format = dasd_eckd_ese_format,
696862306a36Sopenharmony_ci	.ese_read = dasd_eckd_ese_read,
696962306a36Sopenharmony_ci	.pprc_status = dasd_eckd_query_pprc_status,
697062306a36Sopenharmony_ci	.pprc_enabled = dasd_eckd_pprc_enabled,
697162306a36Sopenharmony_ci	.copy_pair_swap = dasd_eckd_copy_pair_swap,
697262306a36Sopenharmony_ci	.device_ping = dasd_eckd_device_ping,
697362306a36Sopenharmony_ci};
697462306a36Sopenharmony_ci
697562306a36Sopenharmony_cistatic int __init
697662306a36Sopenharmony_cidasd_eckd_init(void)
697762306a36Sopenharmony_ci{
697862306a36Sopenharmony_ci	int ret;
697962306a36Sopenharmony_ci
698062306a36Sopenharmony_ci	ASCEBC(dasd_eckd_discipline.ebcname, 4);
698162306a36Sopenharmony_ci	dasd_reserve_req = kmalloc(sizeof(*dasd_reserve_req),
698262306a36Sopenharmony_ci				   GFP_KERNEL | GFP_DMA);
698362306a36Sopenharmony_ci	if (!dasd_reserve_req)
698462306a36Sopenharmony_ci		return -ENOMEM;
698562306a36Sopenharmony_ci	dasd_vol_info_req = kmalloc(sizeof(*dasd_vol_info_req),
698662306a36Sopenharmony_ci				    GFP_KERNEL | GFP_DMA);
698762306a36Sopenharmony_ci	if (!dasd_vol_info_req) {
698862306a36Sopenharmony_ci		kfree(dasd_reserve_req);
698962306a36Sopenharmony_ci		return -ENOMEM;
699062306a36Sopenharmony_ci	}
699162306a36Sopenharmony_ci	pe_handler_worker = kmalloc(sizeof(*pe_handler_worker),
699262306a36Sopenharmony_ci				    GFP_KERNEL | GFP_DMA);
699362306a36Sopenharmony_ci	if (!pe_handler_worker) {
699462306a36Sopenharmony_ci		kfree(dasd_reserve_req);
699562306a36Sopenharmony_ci		kfree(dasd_vol_info_req);
699662306a36Sopenharmony_ci		return -ENOMEM;
699762306a36Sopenharmony_ci	}
699862306a36Sopenharmony_ci	rawpadpage = (void *)__get_free_page(GFP_KERNEL);
699962306a36Sopenharmony_ci	if (!rawpadpage) {
700062306a36Sopenharmony_ci		kfree(pe_handler_worker);
700162306a36Sopenharmony_ci		kfree(dasd_reserve_req);
700262306a36Sopenharmony_ci		kfree(dasd_vol_info_req);
700362306a36Sopenharmony_ci		return -ENOMEM;
700462306a36Sopenharmony_ci	}
700562306a36Sopenharmony_ci	ret = ccw_driver_register(&dasd_eckd_driver);
700662306a36Sopenharmony_ci	if (!ret)
700762306a36Sopenharmony_ci		wait_for_device_probe();
700862306a36Sopenharmony_ci	else {
700962306a36Sopenharmony_ci		kfree(pe_handler_worker);
701062306a36Sopenharmony_ci		kfree(dasd_reserve_req);
701162306a36Sopenharmony_ci		kfree(dasd_vol_info_req);
701262306a36Sopenharmony_ci		free_page((unsigned long)rawpadpage);
701362306a36Sopenharmony_ci	}
701462306a36Sopenharmony_ci	return ret;
701562306a36Sopenharmony_ci}
701662306a36Sopenharmony_ci
701762306a36Sopenharmony_cistatic void __exit
701862306a36Sopenharmony_cidasd_eckd_cleanup(void)
701962306a36Sopenharmony_ci{
702062306a36Sopenharmony_ci	ccw_driver_unregister(&dasd_eckd_driver);
702162306a36Sopenharmony_ci	kfree(pe_handler_worker);
702262306a36Sopenharmony_ci	kfree(dasd_reserve_req);
702362306a36Sopenharmony_ci	free_page((unsigned long)rawpadpage);
702462306a36Sopenharmony_ci}
702562306a36Sopenharmony_ci
702662306a36Sopenharmony_cimodule_init(dasd_eckd_init);
702762306a36Sopenharmony_cimodule_exit(dasd_eckd_cleanup);
7028