18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
48c2ecf20Sopenharmony_ci *		    Horst Hummel <Horst.Hummel@de.ibm.com>
58c2ecf20Sopenharmony_ci *		    Carsten Otte <Cotte@de.ibm.com>
68c2ecf20Sopenharmony_ci *		    Martin Schwidefsky <schwidefsky@de.ibm.com>
78c2ecf20Sopenharmony_ci * Bugreports.to..: <Linux390@de.ibm.com>
88c2ecf20Sopenharmony_ci * Copyright IBM Corp. 1999, 2009
98c2ecf20Sopenharmony_ci * EMC Symmetrix ioctl Copyright EMC Corporation, 2008
108c2ecf20Sopenharmony_ci * Author.........: Nigel Hislop <hislop_nigel@emc.com>
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#define KMSG_COMPONENT "dasd-eckd"
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <linux/stddef.h>
168c2ecf20Sopenharmony_ci#include <linux/kernel.h>
178c2ecf20Sopenharmony_ci#include <linux/slab.h>
188c2ecf20Sopenharmony_ci#include <linux/hdreg.h>	/* HDIO_GETGEO			    */
198c2ecf20Sopenharmony_ci#include <linux/bio.h>
208c2ecf20Sopenharmony_ci#include <linux/module.h>
218c2ecf20Sopenharmony_ci#include <linux/compat.h>
228c2ecf20Sopenharmony_ci#include <linux/init.h>
238c2ecf20Sopenharmony_ci#include <linux/seq_file.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include <asm/css_chars.h>
268c2ecf20Sopenharmony_ci#include <asm/debug.h>
278c2ecf20Sopenharmony_ci#include <asm/idals.h>
288c2ecf20Sopenharmony_ci#include <asm/ebcdic.h>
298c2ecf20Sopenharmony_ci#include <asm/io.h>
308c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
318c2ecf20Sopenharmony_ci#include <asm/cio.h>
328c2ecf20Sopenharmony_ci#include <asm/ccwdev.h>
338c2ecf20Sopenharmony_ci#include <asm/itcw.h>
348c2ecf20Sopenharmony_ci#include <asm/schid.h>
358c2ecf20Sopenharmony_ci#include <asm/chpid.h>
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci#include "dasd_int.h"
388c2ecf20Sopenharmony_ci#include "dasd_eckd.h"
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci#ifdef PRINTK_HEADER
418c2ecf20Sopenharmony_ci#undef PRINTK_HEADER
428c2ecf20Sopenharmony_ci#endif				/* PRINTK_HEADER */
438c2ecf20Sopenharmony_ci#define PRINTK_HEADER "dasd(eckd):"
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci/*
468c2ecf20Sopenharmony_ci * raw track access always map to 64k in memory
478c2ecf20Sopenharmony_ci * so it maps to 16 blocks of 4k per track
488c2ecf20Sopenharmony_ci */
498c2ecf20Sopenharmony_ci#define DASD_RAW_BLOCK_PER_TRACK 16
508c2ecf20Sopenharmony_ci#define DASD_RAW_BLOCKSIZE 4096
518c2ecf20Sopenharmony_ci/* 64k are 128 x 512 byte sectors  */
528c2ecf20Sopenharmony_ci#define DASD_RAW_SECTORS_PER_TRACK 128
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistatic struct dasd_discipline dasd_eckd_discipline;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci/* The ccw bus type uses this table to find devices that it sends to
598c2ecf20Sopenharmony_ci * dasd_eckd_probe */
608c2ecf20Sopenharmony_cistatic struct ccw_device_id dasd_eckd_ids[] = {
618c2ecf20Sopenharmony_ci	{ CCW_DEVICE_DEVTYPE (0x3990, 0, 0x3390, 0), .driver_info = 0x1},
628c2ecf20Sopenharmony_ci	{ CCW_DEVICE_DEVTYPE (0x2105, 0, 0x3390, 0), .driver_info = 0x2},
638c2ecf20Sopenharmony_ci	{ CCW_DEVICE_DEVTYPE (0x3880, 0, 0x3380, 0), .driver_info = 0x3},
648c2ecf20Sopenharmony_ci	{ CCW_DEVICE_DEVTYPE (0x3990, 0, 0x3380, 0), .driver_info = 0x4},
658c2ecf20Sopenharmony_ci	{ CCW_DEVICE_DEVTYPE (0x2105, 0, 0x3380, 0), .driver_info = 0x5},
668c2ecf20Sopenharmony_ci	{ CCW_DEVICE_DEVTYPE (0x9343, 0, 0x9345, 0), .driver_info = 0x6},
678c2ecf20Sopenharmony_ci	{ CCW_DEVICE_DEVTYPE (0x2107, 0, 0x3390, 0), .driver_info = 0x7},
688c2ecf20Sopenharmony_ci	{ CCW_DEVICE_DEVTYPE (0x2107, 0, 0x3380, 0), .driver_info = 0x8},
698c2ecf20Sopenharmony_ci	{ CCW_DEVICE_DEVTYPE (0x1750, 0, 0x3390, 0), .driver_info = 0x9},
708c2ecf20Sopenharmony_ci	{ CCW_DEVICE_DEVTYPE (0x1750, 0, 0x3380, 0), .driver_info = 0xa},
718c2ecf20Sopenharmony_ci	{ /* end of list */ },
728c2ecf20Sopenharmony_ci};
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(ccw, dasd_eckd_ids);
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistatic struct ccw_driver dasd_eckd_driver; /* see below */
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_cistatic void *rawpadpage;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci#define INIT_CQR_OK 0
818c2ecf20Sopenharmony_ci#define INIT_CQR_UNFORMATTED 1
828c2ecf20Sopenharmony_ci#define INIT_CQR_ERROR 2
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci/* emergency request for reserve/release */
858c2ecf20Sopenharmony_cistatic struct {
868c2ecf20Sopenharmony_ci	struct dasd_ccw_req cqr;
878c2ecf20Sopenharmony_ci	struct ccw1 ccw;
888c2ecf20Sopenharmony_ci	char data[32];
898c2ecf20Sopenharmony_ci} *dasd_reserve_req;
908c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(dasd_reserve_mutex);
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistatic struct {
938c2ecf20Sopenharmony_ci	struct dasd_ccw_req cqr;
948c2ecf20Sopenharmony_ci	struct ccw1 ccw[2];
958c2ecf20Sopenharmony_ci	char data[40];
968c2ecf20Sopenharmony_ci} *dasd_vol_info_req;
978c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(dasd_vol_info_mutex);
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_cistruct ext_pool_exhaust_work_data {
1008c2ecf20Sopenharmony_ci	struct work_struct worker;
1018c2ecf20Sopenharmony_ci	struct dasd_device *device;
1028c2ecf20Sopenharmony_ci	struct dasd_device *base;
1038c2ecf20Sopenharmony_ci};
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci/* definitions for the path verification worker */
1068c2ecf20Sopenharmony_cistruct pe_handler_work_data {
1078c2ecf20Sopenharmony_ci	struct work_struct worker;
1088c2ecf20Sopenharmony_ci	struct dasd_device *device;
1098c2ecf20Sopenharmony_ci	struct dasd_ccw_req cqr;
1108c2ecf20Sopenharmony_ci	struct ccw1 ccw;
1118c2ecf20Sopenharmony_ci	__u8 rcd_buffer[DASD_ECKD_RCD_DATA_SIZE];
1128c2ecf20Sopenharmony_ci	int isglobal;
1138c2ecf20Sopenharmony_ci	__u8 tbvpm;
1148c2ecf20Sopenharmony_ci};
1158c2ecf20Sopenharmony_cistatic struct pe_handler_work_data *pe_handler_worker;
1168c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(dasd_pe_handler_mutex);
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_cistruct check_attention_work_data {
1198c2ecf20Sopenharmony_ci	struct work_struct worker;
1208c2ecf20Sopenharmony_ci	struct dasd_device *device;
1218c2ecf20Sopenharmony_ci	__u8 lpum;
1228c2ecf20Sopenharmony_ci};
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_cistatic int dasd_eckd_ext_pool_id(struct dasd_device *);
1258c2ecf20Sopenharmony_cistatic int prepare_itcw(struct itcw *, unsigned int, unsigned int, int,
1268c2ecf20Sopenharmony_ci			struct dasd_device *, struct dasd_device *,
1278c2ecf20Sopenharmony_ci			unsigned int, int, unsigned int, unsigned int,
1288c2ecf20Sopenharmony_ci			unsigned int, unsigned int);
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci/* initial attempt at a probe function. this can be simplified once
1318c2ecf20Sopenharmony_ci * the other detection code is gone */
1328c2ecf20Sopenharmony_cistatic int
1338c2ecf20Sopenharmony_cidasd_eckd_probe (struct ccw_device *cdev)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ci	int ret;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	/* set ECKD specific ccw-device options */
1388c2ecf20Sopenharmony_ci	ret = ccw_device_set_options(cdev, CCWDEV_ALLOW_FORCE |
1398c2ecf20Sopenharmony_ci				     CCWDEV_DO_PATHGROUP | CCWDEV_DO_MULTIPATH);
1408c2ecf20Sopenharmony_ci	if (ret) {
1418c2ecf20Sopenharmony_ci		DBF_EVENT_DEVID(DBF_WARNING, cdev, "%s",
1428c2ecf20Sopenharmony_ci				"dasd_eckd_probe: could not set "
1438c2ecf20Sopenharmony_ci				"ccw-device options");
1448c2ecf20Sopenharmony_ci		return ret;
1458c2ecf20Sopenharmony_ci	}
1468c2ecf20Sopenharmony_ci	ret = dasd_generic_probe(cdev, &dasd_eckd_discipline);
1478c2ecf20Sopenharmony_ci	return ret;
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cistatic int
1518c2ecf20Sopenharmony_cidasd_eckd_set_online(struct ccw_device *cdev)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	return dasd_generic_set_online(cdev, &dasd_eckd_discipline);
1548c2ecf20Sopenharmony_ci}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_cistatic const int sizes_trk0[] = { 28, 148, 84 };
1578c2ecf20Sopenharmony_ci#define LABEL_SIZE 140
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci/* head and record addresses of count_area read in analysis ccw */
1608c2ecf20Sopenharmony_cistatic const int count_area_head[] = { 0, 0, 0, 0, 1 };
1618c2ecf20Sopenharmony_cistatic const int count_area_rec[] = { 1, 2, 3, 4, 1 };
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_cistatic inline unsigned int
1648c2ecf20Sopenharmony_ciceil_quot(unsigned int d1, unsigned int d2)
1658c2ecf20Sopenharmony_ci{
1668c2ecf20Sopenharmony_ci	return (d1 + (d2 - 1)) / d2;
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistatic unsigned int
1708c2ecf20Sopenharmony_cirecs_per_track(struct dasd_eckd_characteristics * rdc,
1718c2ecf20Sopenharmony_ci	       unsigned int kl, unsigned int dl)
1728c2ecf20Sopenharmony_ci{
1738c2ecf20Sopenharmony_ci	int dn, kn;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	switch (rdc->dev_type) {
1768c2ecf20Sopenharmony_ci	case 0x3380:
1778c2ecf20Sopenharmony_ci		if (kl)
1788c2ecf20Sopenharmony_ci			return 1499 / (15 + 7 + ceil_quot(kl + 12, 32) +
1798c2ecf20Sopenharmony_ci				       ceil_quot(dl + 12, 32));
1808c2ecf20Sopenharmony_ci		else
1818c2ecf20Sopenharmony_ci			return 1499 / (15 + ceil_quot(dl + 12, 32));
1828c2ecf20Sopenharmony_ci	case 0x3390:
1838c2ecf20Sopenharmony_ci		dn = ceil_quot(dl + 6, 232) + 1;
1848c2ecf20Sopenharmony_ci		if (kl) {
1858c2ecf20Sopenharmony_ci			kn = ceil_quot(kl + 6, 232) + 1;
1868c2ecf20Sopenharmony_ci			return 1729 / (10 + 9 + ceil_quot(kl + 6 * kn, 34) +
1878c2ecf20Sopenharmony_ci				       9 + ceil_quot(dl + 6 * dn, 34));
1888c2ecf20Sopenharmony_ci		} else
1898c2ecf20Sopenharmony_ci			return 1729 / (10 + 9 + ceil_quot(dl + 6 * dn, 34));
1908c2ecf20Sopenharmony_ci	case 0x9345:
1918c2ecf20Sopenharmony_ci		dn = ceil_quot(dl + 6, 232) + 1;
1928c2ecf20Sopenharmony_ci		if (kl) {
1938c2ecf20Sopenharmony_ci			kn = ceil_quot(kl + 6, 232) + 1;
1948c2ecf20Sopenharmony_ci			return 1420 / (18 + 7 + ceil_quot(kl + 6 * kn, 34) +
1958c2ecf20Sopenharmony_ci				       ceil_quot(dl + 6 * dn, 34));
1968c2ecf20Sopenharmony_ci		} else
1978c2ecf20Sopenharmony_ci			return 1420 / (18 + 7 + ceil_quot(dl + 6 * dn, 34));
1988c2ecf20Sopenharmony_ci	}
1998c2ecf20Sopenharmony_ci	return 0;
2008c2ecf20Sopenharmony_ci}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_cistatic void set_ch_t(struct ch_t *geo, __u32 cyl, __u8 head)
2038c2ecf20Sopenharmony_ci{
2048c2ecf20Sopenharmony_ci	geo->cyl = (__u16) cyl;
2058c2ecf20Sopenharmony_ci	geo->head = cyl >> 16;
2068c2ecf20Sopenharmony_ci	geo->head <<= 4;
2078c2ecf20Sopenharmony_ci	geo->head |= head;
2088c2ecf20Sopenharmony_ci}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci/*
2118c2ecf20Sopenharmony_ci * calculate failing track from sense data depending if
2128c2ecf20Sopenharmony_ci * it is an EAV device or not
2138c2ecf20Sopenharmony_ci */
2148c2ecf20Sopenharmony_cistatic int dasd_eckd_track_from_irb(struct irb *irb, struct dasd_device *device,
2158c2ecf20Sopenharmony_ci				    sector_t *track)
2168c2ecf20Sopenharmony_ci{
2178c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
2188c2ecf20Sopenharmony_ci	u8 *sense = NULL;
2198c2ecf20Sopenharmony_ci	u32 cyl;
2208c2ecf20Sopenharmony_ci	u8 head;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	sense = dasd_get_sense(irb);
2238c2ecf20Sopenharmony_ci	if (!sense) {
2248c2ecf20Sopenharmony_ci		DBF_DEV_EVENT(DBF_WARNING, device, "%s",
2258c2ecf20Sopenharmony_ci			      "ESE error no sense data\n");
2268c2ecf20Sopenharmony_ci		return -EINVAL;
2278c2ecf20Sopenharmony_ci	}
2288c2ecf20Sopenharmony_ci	if (!(sense[27] & DASD_SENSE_BIT_2)) {
2298c2ecf20Sopenharmony_ci		DBF_DEV_EVENT(DBF_WARNING, device, "%s",
2308c2ecf20Sopenharmony_ci			      "ESE error no valid track data\n");
2318c2ecf20Sopenharmony_ci		return -EINVAL;
2328c2ecf20Sopenharmony_ci	}
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	if (sense[27] & DASD_SENSE_BIT_3) {
2358c2ecf20Sopenharmony_ci		/* enhanced addressing */
2368c2ecf20Sopenharmony_ci		cyl = sense[30] << 20;
2378c2ecf20Sopenharmony_ci		cyl |= (sense[31] & 0xF0) << 12;
2388c2ecf20Sopenharmony_ci		cyl |= sense[28] << 8;
2398c2ecf20Sopenharmony_ci		cyl |= sense[29];
2408c2ecf20Sopenharmony_ci	} else {
2418c2ecf20Sopenharmony_ci		cyl = sense[29] << 8;
2428c2ecf20Sopenharmony_ci		cyl |= sense[30];
2438c2ecf20Sopenharmony_ci	}
2448c2ecf20Sopenharmony_ci	head = sense[31] & 0x0F;
2458c2ecf20Sopenharmony_ci	*track = cyl * private->rdc_data.trk_per_cyl + head;
2468c2ecf20Sopenharmony_ci	return 0;
2478c2ecf20Sopenharmony_ci}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_cistatic int set_timestamp(struct ccw1 *ccw, struct DE_eckd_data *data,
2508c2ecf20Sopenharmony_ci		     struct dasd_device *device)
2518c2ecf20Sopenharmony_ci{
2528c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
2538c2ecf20Sopenharmony_ci	int rc;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	rc = get_phys_clock(&data->ep_sys_time);
2568c2ecf20Sopenharmony_ci	/*
2578c2ecf20Sopenharmony_ci	 * Ignore return code if XRC is not supported or
2588c2ecf20Sopenharmony_ci	 * sync clock is switched off
2598c2ecf20Sopenharmony_ci	 */
2608c2ecf20Sopenharmony_ci	if ((rc && !private->rdc_data.facilities.XRC_supported) ||
2618c2ecf20Sopenharmony_ci	    rc == -EOPNOTSUPP || rc == -EACCES)
2628c2ecf20Sopenharmony_ci		return 0;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	/* switch on System Time Stamp - needed for XRC Support */
2658c2ecf20Sopenharmony_ci	data->ga_extended |= 0x08; /* switch on 'Time Stamp Valid'   */
2668c2ecf20Sopenharmony_ci	data->ga_extended |= 0x02; /* switch on 'Extended Parameter' */
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	if (ccw) {
2698c2ecf20Sopenharmony_ci		ccw->count = sizeof(struct DE_eckd_data);
2708c2ecf20Sopenharmony_ci		ccw->flags |= CCW_FLAG_SLI;
2718c2ecf20Sopenharmony_ci	}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	return rc;
2748c2ecf20Sopenharmony_ci}
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_cistatic int
2778c2ecf20Sopenharmony_cidefine_extent(struct ccw1 *ccw, struct DE_eckd_data *data, unsigned int trk,
2788c2ecf20Sopenharmony_ci	      unsigned int totrk, int cmd, struct dasd_device *device,
2798c2ecf20Sopenharmony_ci	      int blksize)
2808c2ecf20Sopenharmony_ci{
2818c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
2828c2ecf20Sopenharmony_ci	u16 heads, beghead, endhead;
2838c2ecf20Sopenharmony_ci	u32 begcyl, endcyl;
2848c2ecf20Sopenharmony_ci	int rc = 0;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	if (ccw) {
2878c2ecf20Sopenharmony_ci		ccw->cmd_code = DASD_ECKD_CCW_DEFINE_EXTENT;
2888c2ecf20Sopenharmony_ci		ccw->flags = 0;
2898c2ecf20Sopenharmony_ci		ccw->count = 16;
2908c2ecf20Sopenharmony_ci		ccw->cda = (__u32)__pa(data);
2918c2ecf20Sopenharmony_ci	}
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	memset(data, 0, sizeof(struct DE_eckd_data));
2948c2ecf20Sopenharmony_ci	switch (cmd) {
2958c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_READ_HOME_ADDRESS:
2968c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_READ_RECORD_ZERO:
2978c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_READ:
2988c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_READ_MT:
2998c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_READ_CKD:
3008c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_READ_CKD_MT:
3018c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_READ_KD:
3028c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_READ_KD_MT:
3038c2ecf20Sopenharmony_ci		data->mask.perm = 0x1;
3048c2ecf20Sopenharmony_ci		data->attributes.operation = private->attrib.operation;
3058c2ecf20Sopenharmony_ci		break;
3068c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_READ_COUNT:
3078c2ecf20Sopenharmony_ci		data->mask.perm = 0x1;
3088c2ecf20Sopenharmony_ci		data->attributes.operation = DASD_BYPASS_CACHE;
3098c2ecf20Sopenharmony_ci		break;
3108c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_READ_TRACK:
3118c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_READ_TRACK_DATA:
3128c2ecf20Sopenharmony_ci		data->mask.perm = 0x1;
3138c2ecf20Sopenharmony_ci		data->attributes.operation = private->attrib.operation;
3148c2ecf20Sopenharmony_ci		data->blk_size = 0;
3158c2ecf20Sopenharmony_ci		break;
3168c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_WRITE:
3178c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_WRITE_MT:
3188c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_WRITE_KD:
3198c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_WRITE_KD_MT:
3208c2ecf20Sopenharmony_ci		data->mask.perm = 0x02;
3218c2ecf20Sopenharmony_ci		data->attributes.operation = private->attrib.operation;
3228c2ecf20Sopenharmony_ci		rc = set_timestamp(ccw, data, device);
3238c2ecf20Sopenharmony_ci		break;
3248c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_WRITE_CKD:
3258c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_WRITE_CKD_MT:
3268c2ecf20Sopenharmony_ci		data->attributes.operation = DASD_BYPASS_CACHE;
3278c2ecf20Sopenharmony_ci		rc = set_timestamp(ccw, data, device);
3288c2ecf20Sopenharmony_ci		break;
3298c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_ERASE:
3308c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_WRITE_HOME_ADDRESS:
3318c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_WRITE_RECORD_ZERO:
3328c2ecf20Sopenharmony_ci		data->mask.perm = 0x3;
3338c2ecf20Sopenharmony_ci		data->mask.auth = 0x1;
3348c2ecf20Sopenharmony_ci		data->attributes.operation = DASD_BYPASS_CACHE;
3358c2ecf20Sopenharmony_ci		rc = set_timestamp(ccw, data, device);
3368c2ecf20Sopenharmony_ci		break;
3378c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_WRITE_FULL_TRACK:
3388c2ecf20Sopenharmony_ci		data->mask.perm = 0x03;
3398c2ecf20Sopenharmony_ci		data->attributes.operation = private->attrib.operation;
3408c2ecf20Sopenharmony_ci		data->blk_size = 0;
3418c2ecf20Sopenharmony_ci		break;
3428c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_WRITE_TRACK_DATA:
3438c2ecf20Sopenharmony_ci		data->mask.perm = 0x02;
3448c2ecf20Sopenharmony_ci		data->attributes.operation = private->attrib.operation;
3458c2ecf20Sopenharmony_ci		data->blk_size = blksize;
3468c2ecf20Sopenharmony_ci		rc = set_timestamp(ccw, data, device);
3478c2ecf20Sopenharmony_ci		break;
3488c2ecf20Sopenharmony_ci	default:
3498c2ecf20Sopenharmony_ci		dev_err(&device->cdev->dev,
3508c2ecf20Sopenharmony_ci			"0x%x is not a known command\n", cmd);
3518c2ecf20Sopenharmony_ci		break;
3528c2ecf20Sopenharmony_ci	}
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	data->attributes.mode = 0x3;	/* ECKD */
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	if ((private->rdc_data.cu_type == 0x2105 ||
3578c2ecf20Sopenharmony_ci	     private->rdc_data.cu_type == 0x2107 ||
3588c2ecf20Sopenharmony_ci	     private->rdc_data.cu_type == 0x1750)
3598c2ecf20Sopenharmony_ci	    && !(private->uses_cdl && trk < 2))
3608c2ecf20Sopenharmony_ci		data->ga_extended |= 0x40; /* Regular Data Format Mode */
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	heads = private->rdc_data.trk_per_cyl;
3638c2ecf20Sopenharmony_ci	begcyl = trk / heads;
3648c2ecf20Sopenharmony_ci	beghead = trk % heads;
3658c2ecf20Sopenharmony_ci	endcyl = totrk / heads;
3668c2ecf20Sopenharmony_ci	endhead = totrk % heads;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	/* check for sequential prestage - enhance cylinder range */
3698c2ecf20Sopenharmony_ci	if (data->attributes.operation == DASD_SEQ_PRESTAGE ||
3708c2ecf20Sopenharmony_ci	    data->attributes.operation == DASD_SEQ_ACCESS) {
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci		if (endcyl + private->attrib.nr_cyl < private->real_cyl)
3738c2ecf20Sopenharmony_ci			endcyl += private->attrib.nr_cyl;
3748c2ecf20Sopenharmony_ci		else
3758c2ecf20Sopenharmony_ci			endcyl = (private->real_cyl - 1);
3768c2ecf20Sopenharmony_ci	}
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	set_ch_t(&data->beg_ext, begcyl, beghead);
3798c2ecf20Sopenharmony_ci	set_ch_t(&data->end_ext, endcyl, endhead);
3808c2ecf20Sopenharmony_ci	return rc;
3818c2ecf20Sopenharmony_ci}
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_cistatic void locate_record_ext(struct ccw1 *ccw, struct LRE_eckd_data *data,
3858c2ecf20Sopenharmony_ci			      unsigned int trk, unsigned int rec_on_trk,
3868c2ecf20Sopenharmony_ci			      int count, int cmd, struct dasd_device *device,
3878c2ecf20Sopenharmony_ci			      unsigned int reclen, unsigned int tlf)
3888c2ecf20Sopenharmony_ci{
3898c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
3908c2ecf20Sopenharmony_ci	int sector;
3918c2ecf20Sopenharmony_ci	int dn, d;
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	if (ccw) {
3948c2ecf20Sopenharmony_ci		ccw->cmd_code = DASD_ECKD_CCW_LOCATE_RECORD_EXT;
3958c2ecf20Sopenharmony_ci		ccw->flags = 0;
3968c2ecf20Sopenharmony_ci		if (cmd == DASD_ECKD_CCW_WRITE_FULL_TRACK)
3978c2ecf20Sopenharmony_ci			ccw->count = 22;
3988c2ecf20Sopenharmony_ci		else
3998c2ecf20Sopenharmony_ci			ccw->count = 20;
4008c2ecf20Sopenharmony_ci		ccw->cda = (__u32)__pa(data);
4018c2ecf20Sopenharmony_ci	}
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	memset(data, 0, sizeof(*data));
4048c2ecf20Sopenharmony_ci	sector = 0;
4058c2ecf20Sopenharmony_ci	if (rec_on_trk) {
4068c2ecf20Sopenharmony_ci		switch (private->rdc_data.dev_type) {
4078c2ecf20Sopenharmony_ci		case 0x3390:
4088c2ecf20Sopenharmony_ci			dn = ceil_quot(reclen + 6, 232);
4098c2ecf20Sopenharmony_ci			d = 9 + ceil_quot(reclen + 6 * (dn + 1), 34);
4108c2ecf20Sopenharmony_ci			sector = (49 + (rec_on_trk - 1) * (10 + d)) / 8;
4118c2ecf20Sopenharmony_ci			break;
4128c2ecf20Sopenharmony_ci		case 0x3380:
4138c2ecf20Sopenharmony_ci			d = 7 + ceil_quot(reclen + 12, 32);
4148c2ecf20Sopenharmony_ci			sector = (39 + (rec_on_trk - 1) * (8 + d)) / 7;
4158c2ecf20Sopenharmony_ci			break;
4168c2ecf20Sopenharmony_ci		}
4178c2ecf20Sopenharmony_ci	}
4188c2ecf20Sopenharmony_ci	data->sector = sector;
4198c2ecf20Sopenharmony_ci	/* note: meaning of count depends on the operation
4208c2ecf20Sopenharmony_ci	 *	 for record based I/O it's the number of records, but for
4218c2ecf20Sopenharmony_ci	 *	 track based I/O it's the number of tracks
4228c2ecf20Sopenharmony_ci	 */
4238c2ecf20Sopenharmony_ci	data->count = count;
4248c2ecf20Sopenharmony_ci	switch (cmd) {
4258c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_WRITE_HOME_ADDRESS:
4268c2ecf20Sopenharmony_ci		data->operation.orientation = 0x3;
4278c2ecf20Sopenharmony_ci		data->operation.operation = 0x03;
4288c2ecf20Sopenharmony_ci		break;
4298c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_READ_HOME_ADDRESS:
4308c2ecf20Sopenharmony_ci		data->operation.orientation = 0x3;
4318c2ecf20Sopenharmony_ci		data->operation.operation = 0x16;
4328c2ecf20Sopenharmony_ci		break;
4338c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_WRITE_RECORD_ZERO:
4348c2ecf20Sopenharmony_ci		data->operation.orientation = 0x1;
4358c2ecf20Sopenharmony_ci		data->operation.operation = 0x03;
4368c2ecf20Sopenharmony_ci		data->count++;
4378c2ecf20Sopenharmony_ci		break;
4388c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_READ_RECORD_ZERO:
4398c2ecf20Sopenharmony_ci		data->operation.orientation = 0x3;
4408c2ecf20Sopenharmony_ci		data->operation.operation = 0x16;
4418c2ecf20Sopenharmony_ci		data->count++;
4428c2ecf20Sopenharmony_ci		break;
4438c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_WRITE:
4448c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_WRITE_MT:
4458c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_WRITE_KD:
4468c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_WRITE_KD_MT:
4478c2ecf20Sopenharmony_ci		data->auxiliary.length_valid = 0x1;
4488c2ecf20Sopenharmony_ci		data->length = reclen;
4498c2ecf20Sopenharmony_ci		data->operation.operation = 0x01;
4508c2ecf20Sopenharmony_ci		break;
4518c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_WRITE_CKD:
4528c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_WRITE_CKD_MT:
4538c2ecf20Sopenharmony_ci		data->auxiliary.length_valid = 0x1;
4548c2ecf20Sopenharmony_ci		data->length = reclen;
4558c2ecf20Sopenharmony_ci		data->operation.operation = 0x03;
4568c2ecf20Sopenharmony_ci		break;
4578c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_WRITE_FULL_TRACK:
4588c2ecf20Sopenharmony_ci		data->operation.orientation = 0x0;
4598c2ecf20Sopenharmony_ci		data->operation.operation = 0x3F;
4608c2ecf20Sopenharmony_ci		data->extended_operation = 0x11;
4618c2ecf20Sopenharmony_ci		data->length = 0;
4628c2ecf20Sopenharmony_ci		data->extended_parameter_length = 0x02;
4638c2ecf20Sopenharmony_ci		if (data->count > 8) {
4648c2ecf20Sopenharmony_ci			data->extended_parameter[0] = 0xFF;
4658c2ecf20Sopenharmony_ci			data->extended_parameter[1] = 0xFF;
4668c2ecf20Sopenharmony_ci			data->extended_parameter[1] <<= (16 - count);
4678c2ecf20Sopenharmony_ci		} else {
4688c2ecf20Sopenharmony_ci			data->extended_parameter[0] = 0xFF;
4698c2ecf20Sopenharmony_ci			data->extended_parameter[0] <<= (8 - count);
4708c2ecf20Sopenharmony_ci			data->extended_parameter[1] = 0x00;
4718c2ecf20Sopenharmony_ci		}
4728c2ecf20Sopenharmony_ci		data->sector = 0xFF;
4738c2ecf20Sopenharmony_ci		break;
4748c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_WRITE_TRACK_DATA:
4758c2ecf20Sopenharmony_ci		data->auxiliary.length_valid = 0x1;
4768c2ecf20Sopenharmony_ci		data->length = reclen;	/* not tlf, as one might think */
4778c2ecf20Sopenharmony_ci		data->operation.operation = 0x3F;
4788c2ecf20Sopenharmony_ci		data->extended_operation = 0x23;
4798c2ecf20Sopenharmony_ci		break;
4808c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_READ:
4818c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_READ_MT:
4828c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_READ_KD:
4838c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_READ_KD_MT:
4848c2ecf20Sopenharmony_ci		data->auxiliary.length_valid = 0x1;
4858c2ecf20Sopenharmony_ci		data->length = reclen;
4868c2ecf20Sopenharmony_ci		data->operation.operation = 0x06;
4878c2ecf20Sopenharmony_ci		break;
4888c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_READ_CKD:
4898c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_READ_CKD_MT:
4908c2ecf20Sopenharmony_ci		data->auxiliary.length_valid = 0x1;
4918c2ecf20Sopenharmony_ci		data->length = reclen;
4928c2ecf20Sopenharmony_ci		data->operation.operation = 0x16;
4938c2ecf20Sopenharmony_ci		break;
4948c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_READ_COUNT:
4958c2ecf20Sopenharmony_ci		data->operation.operation = 0x06;
4968c2ecf20Sopenharmony_ci		break;
4978c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_READ_TRACK:
4988c2ecf20Sopenharmony_ci		data->operation.orientation = 0x1;
4998c2ecf20Sopenharmony_ci		data->operation.operation = 0x0C;
5008c2ecf20Sopenharmony_ci		data->extended_parameter_length = 0;
5018c2ecf20Sopenharmony_ci		data->sector = 0xFF;
5028c2ecf20Sopenharmony_ci		break;
5038c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_READ_TRACK_DATA:
5048c2ecf20Sopenharmony_ci		data->auxiliary.length_valid = 0x1;
5058c2ecf20Sopenharmony_ci		data->length = tlf;
5068c2ecf20Sopenharmony_ci		data->operation.operation = 0x0C;
5078c2ecf20Sopenharmony_ci		break;
5088c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_ERASE:
5098c2ecf20Sopenharmony_ci		data->length = reclen;
5108c2ecf20Sopenharmony_ci		data->auxiliary.length_valid = 0x1;
5118c2ecf20Sopenharmony_ci		data->operation.operation = 0x0b;
5128c2ecf20Sopenharmony_ci		break;
5138c2ecf20Sopenharmony_ci	default:
5148c2ecf20Sopenharmony_ci		DBF_DEV_EVENT(DBF_ERR, device,
5158c2ecf20Sopenharmony_ci			    "fill LRE unknown opcode 0x%x", cmd);
5168c2ecf20Sopenharmony_ci		BUG();
5178c2ecf20Sopenharmony_ci	}
5188c2ecf20Sopenharmony_ci	set_ch_t(&data->seek_addr,
5198c2ecf20Sopenharmony_ci		 trk / private->rdc_data.trk_per_cyl,
5208c2ecf20Sopenharmony_ci		 trk % private->rdc_data.trk_per_cyl);
5218c2ecf20Sopenharmony_ci	data->search_arg.cyl = data->seek_addr.cyl;
5228c2ecf20Sopenharmony_ci	data->search_arg.head = data->seek_addr.head;
5238c2ecf20Sopenharmony_ci	data->search_arg.record = rec_on_trk;
5248c2ecf20Sopenharmony_ci}
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_cistatic int prefix_LRE(struct ccw1 *ccw, struct PFX_eckd_data *pfxdata,
5278c2ecf20Sopenharmony_ci		      unsigned int trk, unsigned int totrk, int cmd,
5288c2ecf20Sopenharmony_ci		      struct dasd_device *basedev, struct dasd_device *startdev,
5298c2ecf20Sopenharmony_ci		      unsigned int format, unsigned int rec_on_trk, int count,
5308c2ecf20Sopenharmony_ci		      unsigned int blksize, unsigned int tlf)
5318c2ecf20Sopenharmony_ci{
5328c2ecf20Sopenharmony_ci	struct dasd_eckd_private *basepriv, *startpriv;
5338c2ecf20Sopenharmony_ci	struct LRE_eckd_data *lredata;
5348c2ecf20Sopenharmony_ci	struct DE_eckd_data *dedata;
5358c2ecf20Sopenharmony_ci	int rc = 0;
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci	basepriv = basedev->private;
5388c2ecf20Sopenharmony_ci	startpriv = startdev->private;
5398c2ecf20Sopenharmony_ci	dedata = &pfxdata->define_extent;
5408c2ecf20Sopenharmony_ci	lredata = &pfxdata->locate_record;
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	ccw->cmd_code = DASD_ECKD_CCW_PFX;
5438c2ecf20Sopenharmony_ci	ccw->flags = 0;
5448c2ecf20Sopenharmony_ci	if (cmd == DASD_ECKD_CCW_WRITE_FULL_TRACK) {
5458c2ecf20Sopenharmony_ci		ccw->count = sizeof(*pfxdata) + 2;
5468c2ecf20Sopenharmony_ci		ccw->cda = (__u32) __pa(pfxdata);
5478c2ecf20Sopenharmony_ci		memset(pfxdata, 0, sizeof(*pfxdata) + 2);
5488c2ecf20Sopenharmony_ci	} else {
5498c2ecf20Sopenharmony_ci		ccw->count = sizeof(*pfxdata);
5508c2ecf20Sopenharmony_ci		ccw->cda = (__u32) __pa(pfxdata);
5518c2ecf20Sopenharmony_ci		memset(pfxdata, 0, sizeof(*pfxdata));
5528c2ecf20Sopenharmony_ci	}
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	/* prefix data */
5558c2ecf20Sopenharmony_ci	if (format > 1) {
5568c2ecf20Sopenharmony_ci		DBF_DEV_EVENT(DBF_ERR, basedev,
5578c2ecf20Sopenharmony_ci			      "PFX LRE unknown format 0x%x", format);
5588c2ecf20Sopenharmony_ci		BUG();
5598c2ecf20Sopenharmony_ci		return -EINVAL;
5608c2ecf20Sopenharmony_ci	}
5618c2ecf20Sopenharmony_ci	pfxdata->format = format;
5628c2ecf20Sopenharmony_ci	pfxdata->base_address = basepriv->ned->unit_addr;
5638c2ecf20Sopenharmony_ci	pfxdata->base_lss = basepriv->ned->ID;
5648c2ecf20Sopenharmony_ci	pfxdata->validity.define_extent = 1;
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	/* private uid is kept up to date, conf_data may be outdated */
5678c2ecf20Sopenharmony_ci	if (startpriv->uid.type == UA_BASE_PAV_ALIAS)
5688c2ecf20Sopenharmony_ci		pfxdata->validity.verify_base = 1;
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	if (startpriv->uid.type == UA_HYPER_PAV_ALIAS) {
5718c2ecf20Sopenharmony_ci		pfxdata->validity.verify_base = 1;
5728c2ecf20Sopenharmony_ci		pfxdata->validity.hyper_pav = 1;
5738c2ecf20Sopenharmony_ci	}
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	rc = define_extent(NULL, dedata, trk, totrk, cmd, basedev, blksize);
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci	/*
5788c2ecf20Sopenharmony_ci	 * For some commands the System Time Stamp is set in the define extent
5798c2ecf20Sopenharmony_ci	 * data when XRC is supported. The validity of the time stamp must be
5808c2ecf20Sopenharmony_ci	 * reflected in the prefix data as well.
5818c2ecf20Sopenharmony_ci	 */
5828c2ecf20Sopenharmony_ci	if (dedata->ga_extended & 0x08 && dedata->ga_extended & 0x02)
5838c2ecf20Sopenharmony_ci		pfxdata->validity.time_stamp = 1; /* 'Time Stamp Valid'   */
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	if (format == 1) {
5868c2ecf20Sopenharmony_ci		locate_record_ext(NULL, lredata, trk, rec_on_trk, count, cmd,
5878c2ecf20Sopenharmony_ci				  basedev, blksize, tlf);
5888c2ecf20Sopenharmony_ci	}
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci	return rc;
5918c2ecf20Sopenharmony_ci}
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_cistatic int prefix(struct ccw1 *ccw, struct PFX_eckd_data *pfxdata,
5948c2ecf20Sopenharmony_ci		  unsigned int trk, unsigned int totrk, int cmd,
5958c2ecf20Sopenharmony_ci		  struct dasd_device *basedev, struct dasd_device *startdev)
5968c2ecf20Sopenharmony_ci{
5978c2ecf20Sopenharmony_ci	return prefix_LRE(ccw, pfxdata, trk, totrk, cmd, basedev, startdev,
5988c2ecf20Sopenharmony_ci			  0, 0, 0, 0, 0);
5998c2ecf20Sopenharmony_ci}
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_cistatic void
6028c2ecf20Sopenharmony_cilocate_record(struct ccw1 *ccw, struct LO_eckd_data *data, unsigned int trk,
6038c2ecf20Sopenharmony_ci	      unsigned int rec_on_trk, int no_rec, int cmd,
6048c2ecf20Sopenharmony_ci	      struct dasd_device * device, int reclen)
6058c2ecf20Sopenharmony_ci{
6068c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
6078c2ecf20Sopenharmony_ci	int sector;
6088c2ecf20Sopenharmony_ci	int dn, d;
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci	DBF_DEV_EVENT(DBF_INFO, device,
6118c2ecf20Sopenharmony_ci		  "Locate: trk %d, rec %d, no_rec %d, cmd %d, reclen %d",
6128c2ecf20Sopenharmony_ci		  trk, rec_on_trk, no_rec, cmd, reclen);
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	ccw->cmd_code = DASD_ECKD_CCW_LOCATE_RECORD;
6158c2ecf20Sopenharmony_ci	ccw->flags = 0;
6168c2ecf20Sopenharmony_ci	ccw->count = 16;
6178c2ecf20Sopenharmony_ci	ccw->cda = (__u32) __pa(data);
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	memset(data, 0, sizeof(struct LO_eckd_data));
6208c2ecf20Sopenharmony_ci	sector = 0;
6218c2ecf20Sopenharmony_ci	if (rec_on_trk) {
6228c2ecf20Sopenharmony_ci		switch (private->rdc_data.dev_type) {
6238c2ecf20Sopenharmony_ci		case 0x3390:
6248c2ecf20Sopenharmony_ci			dn = ceil_quot(reclen + 6, 232);
6258c2ecf20Sopenharmony_ci			d = 9 + ceil_quot(reclen + 6 * (dn + 1), 34);
6268c2ecf20Sopenharmony_ci			sector = (49 + (rec_on_trk - 1) * (10 + d)) / 8;
6278c2ecf20Sopenharmony_ci			break;
6288c2ecf20Sopenharmony_ci		case 0x3380:
6298c2ecf20Sopenharmony_ci			d = 7 + ceil_quot(reclen + 12, 32);
6308c2ecf20Sopenharmony_ci			sector = (39 + (rec_on_trk - 1) * (8 + d)) / 7;
6318c2ecf20Sopenharmony_ci			break;
6328c2ecf20Sopenharmony_ci		}
6338c2ecf20Sopenharmony_ci	}
6348c2ecf20Sopenharmony_ci	data->sector = sector;
6358c2ecf20Sopenharmony_ci	data->count = no_rec;
6368c2ecf20Sopenharmony_ci	switch (cmd) {
6378c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_WRITE_HOME_ADDRESS:
6388c2ecf20Sopenharmony_ci		data->operation.orientation = 0x3;
6398c2ecf20Sopenharmony_ci		data->operation.operation = 0x03;
6408c2ecf20Sopenharmony_ci		break;
6418c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_READ_HOME_ADDRESS:
6428c2ecf20Sopenharmony_ci		data->operation.orientation = 0x3;
6438c2ecf20Sopenharmony_ci		data->operation.operation = 0x16;
6448c2ecf20Sopenharmony_ci		break;
6458c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_WRITE_RECORD_ZERO:
6468c2ecf20Sopenharmony_ci		data->operation.orientation = 0x1;
6478c2ecf20Sopenharmony_ci		data->operation.operation = 0x03;
6488c2ecf20Sopenharmony_ci		data->count++;
6498c2ecf20Sopenharmony_ci		break;
6508c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_READ_RECORD_ZERO:
6518c2ecf20Sopenharmony_ci		data->operation.orientation = 0x3;
6528c2ecf20Sopenharmony_ci		data->operation.operation = 0x16;
6538c2ecf20Sopenharmony_ci		data->count++;
6548c2ecf20Sopenharmony_ci		break;
6558c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_WRITE:
6568c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_WRITE_MT:
6578c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_WRITE_KD:
6588c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_WRITE_KD_MT:
6598c2ecf20Sopenharmony_ci		data->auxiliary.last_bytes_used = 0x1;
6608c2ecf20Sopenharmony_ci		data->length = reclen;
6618c2ecf20Sopenharmony_ci		data->operation.operation = 0x01;
6628c2ecf20Sopenharmony_ci		break;
6638c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_WRITE_CKD:
6648c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_WRITE_CKD_MT:
6658c2ecf20Sopenharmony_ci		data->auxiliary.last_bytes_used = 0x1;
6668c2ecf20Sopenharmony_ci		data->length = reclen;
6678c2ecf20Sopenharmony_ci		data->operation.operation = 0x03;
6688c2ecf20Sopenharmony_ci		break;
6698c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_READ:
6708c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_READ_MT:
6718c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_READ_KD:
6728c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_READ_KD_MT:
6738c2ecf20Sopenharmony_ci		data->auxiliary.last_bytes_used = 0x1;
6748c2ecf20Sopenharmony_ci		data->length = reclen;
6758c2ecf20Sopenharmony_ci		data->operation.operation = 0x06;
6768c2ecf20Sopenharmony_ci		break;
6778c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_READ_CKD:
6788c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_READ_CKD_MT:
6798c2ecf20Sopenharmony_ci		data->auxiliary.last_bytes_used = 0x1;
6808c2ecf20Sopenharmony_ci		data->length = reclen;
6818c2ecf20Sopenharmony_ci		data->operation.operation = 0x16;
6828c2ecf20Sopenharmony_ci		break;
6838c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_READ_COUNT:
6848c2ecf20Sopenharmony_ci		data->operation.operation = 0x06;
6858c2ecf20Sopenharmony_ci		break;
6868c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_ERASE:
6878c2ecf20Sopenharmony_ci		data->length = reclen;
6888c2ecf20Sopenharmony_ci		data->auxiliary.last_bytes_used = 0x1;
6898c2ecf20Sopenharmony_ci		data->operation.operation = 0x0b;
6908c2ecf20Sopenharmony_ci		break;
6918c2ecf20Sopenharmony_ci	default:
6928c2ecf20Sopenharmony_ci		DBF_DEV_EVENT(DBF_ERR, device, "unknown locate record "
6938c2ecf20Sopenharmony_ci			      "opcode 0x%x", cmd);
6948c2ecf20Sopenharmony_ci	}
6958c2ecf20Sopenharmony_ci	set_ch_t(&data->seek_addr,
6968c2ecf20Sopenharmony_ci		 trk / private->rdc_data.trk_per_cyl,
6978c2ecf20Sopenharmony_ci		 trk % private->rdc_data.trk_per_cyl);
6988c2ecf20Sopenharmony_ci	data->search_arg.cyl = data->seek_addr.cyl;
6998c2ecf20Sopenharmony_ci	data->search_arg.head = data->seek_addr.head;
7008c2ecf20Sopenharmony_ci	data->search_arg.record = rec_on_trk;
7018c2ecf20Sopenharmony_ci}
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ci/*
7048c2ecf20Sopenharmony_ci * Returns 1 if the block is one of the special blocks that needs
7058c2ecf20Sopenharmony_ci * to get read/written with the KD variant of the command.
7068c2ecf20Sopenharmony_ci * That is DASD_ECKD_READ_KD_MT instead of DASD_ECKD_READ_MT and
7078c2ecf20Sopenharmony_ci * DASD_ECKD_WRITE_KD_MT instead of DASD_ECKD_WRITE_MT.
7088c2ecf20Sopenharmony_ci * Luckily the KD variants differ only by one bit (0x08) from the
7098c2ecf20Sopenharmony_ci * normal variant. So don't wonder about code like:
7108c2ecf20Sopenharmony_ci * if (dasd_eckd_cdl_special(blk_per_trk, recid))
7118c2ecf20Sopenharmony_ci *         ccw->cmd_code |= 0x8;
7128c2ecf20Sopenharmony_ci */
7138c2ecf20Sopenharmony_cistatic inline int
7148c2ecf20Sopenharmony_cidasd_eckd_cdl_special(int blk_per_trk, int recid)
7158c2ecf20Sopenharmony_ci{
7168c2ecf20Sopenharmony_ci	if (recid < 3)
7178c2ecf20Sopenharmony_ci		return 1;
7188c2ecf20Sopenharmony_ci	if (recid < blk_per_trk)
7198c2ecf20Sopenharmony_ci		return 0;
7208c2ecf20Sopenharmony_ci	if (recid < 2 * blk_per_trk)
7218c2ecf20Sopenharmony_ci		return 1;
7228c2ecf20Sopenharmony_ci	return 0;
7238c2ecf20Sopenharmony_ci}
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci/*
7268c2ecf20Sopenharmony_ci * Returns the record size for the special blocks of the cdl format.
7278c2ecf20Sopenharmony_ci * Only returns something useful if dasd_eckd_cdl_special is true
7288c2ecf20Sopenharmony_ci * for the recid.
7298c2ecf20Sopenharmony_ci */
7308c2ecf20Sopenharmony_cistatic inline int
7318c2ecf20Sopenharmony_cidasd_eckd_cdl_reclen(int recid)
7328c2ecf20Sopenharmony_ci{
7338c2ecf20Sopenharmony_ci	if (recid < 3)
7348c2ecf20Sopenharmony_ci		return sizes_trk0[recid];
7358c2ecf20Sopenharmony_ci	return LABEL_SIZE;
7368c2ecf20Sopenharmony_ci}
7378c2ecf20Sopenharmony_ci/* create unique id from private structure. */
7388c2ecf20Sopenharmony_cistatic void create_uid(struct dasd_eckd_private *private)
7398c2ecf20Sopenharmony_ci{
7408c2ecf20Sopenharmony_ci	int count;
7418c2ecf20Sopenharmony_ci	struct dasd_uid *uid;
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ci	uid = &private->uid;
7448c2ecf20Sopenharmony_ci	memset(uid, 0, sizeof(struct dasd_uid));
7458c2ecf20Sopenharmony_ci	memcpy(uid->vendor, private->ned->HDA_manufacturer,
7468c2ecf20Sopenharmony_ci	       sizeof(uid->vendor) - 1);
7478c2ecf20Sopenharmony_ci	EBCASC(uid->vendor, sizeof(uid->vendor) - 1);
7488c2ecf20Sopenharmony_ci	memcpy(uid->serial, private->ned->HDA_location,
7498c2ecf20Sopenharmony_ci	       sizeof(uid->serial) - 1);
7508c2ecf20Sopenharmony_ci	EBCASC(uid->serial, sizeof(uid->serial) - 1);
7518c2ecf20Sopenharmony_ci	uid->ssid = private->gneq->subsystemID;
7528c2ecf20Sopenharmony_ci	uid->real_unit_addr = private->ned->unit_addr;
7538c2ecf20Sopenharmony_ci	if (private->sneq) {
7548c2ecf20Sopenharmony_ci		uid->type = private->sneq->sua_flags;
7558c2ecf20Sopenharmony_ci		if (uid->type == UA_BASE_PAV_ALIAS)
7568c2ecf20Sopenharmony_ci			uid->base_unit_addr = private->sneq->base_unit_addr;
7578c2ecf20Sopenharmony_ci	} else {
7588c2ecf20Sopenharmony_ci		uid->type = UA_BASE_DEVICE;
7598c2ecf20Sopenharmony_ci	}
7608c2ecf20Sopenharmony_ci	if (private->vdsneq) {
7618c2ecf20Sopenharmony_ci		for (count = 0; count < 16; count++) {
7628c2ecf20Sopenharmony_ci			sprintf(uid->vduit+2*count, "%02x",
7638c2ecf20Sopenharmony_ci				private->vdsneq->uit[count]);
7648c2ecf20Sopenharmony_ci		}
7658c2ecf20Sopenharmony_ci	}
7668c2ecf20Sopenharmony_ci}
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ci/*
7698c2ecf20Sopenharmony_ci * Generate device unique id that specifies the physical device.
7708c2ecf20Sopenharmony_ci */
7718c2ecf20Sopenharmony_cistatic int dasd_eckd_generate_uid(struct dasd_device *device)
7728c2ecf20Sopenharmony_ci{
7738c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
7748c2ecf20Sopenharmony_ci	unsigned long flags;
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci	if (!private)
7778c2ecf20Sopenharmony_ci		return -ENODEV;
7788c2ecf20Sopenharmony_ci	if (!private->ned || !private->gneq)
7798c2ecf20Sopenharmony_ci		return -ENODEV;
7808c2ecf20Sopenharmony_ci	spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
7818c2ecf20Sopenharmony_ci	create_uid(private);
7828c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
7838c2ecf20Sopenharmony_ci	return 0;
7848c2ecf20Sopenharmony_ci}
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_cistatic int dasd_eckd_get_uid(struct dasd_device *device, struct dasd_uid *uid)
7878c2ecf20Sopenharmony_ci{
7888c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
7898c2ecf20Sopenharmony_ci	unsigned long flags;
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci	if (private) {
7928c2ecf20Sopenharmony_ci		spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
7938c2ecf20Sopenharmony_ci		*uid = private->uid;
7948c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
7958c2ecf20Sopenharmony_ci		return 0;
7968c2ecf20Sopenharmony_ci	}
7978c2ecf20Sopenharmony_ci	return -EINVAL;
7988c2ecf20Sopenharmony_ci}
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_ci/*
8018c2ecf20Sopenharmony_ci * compare device UID with data of a given dasd_eckd_private structure
8028c2ecf20Sopenharmony_ci * return 0 for match
8038c2ecf20Sopenharmony_ci */
8048c2ecf20Sopenharmony_cistatic int dasd_eckd_compare_path_uid(struct dasd_device *device,
8058c2ecf20Sopenharmony_ci				      struct dasd_eckd_private *private)
8068c2ecf20Sopenharmony_ci{
8078c2ecf20Sopenharmony_ci	struct dasd_uid device_uid;
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci	create_uid(private);
8108c2ecf20Sopenharmony_ci	dasd_eckd_get_uid(device, &device_uid);
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci	return memcmp(&device_uid, &private->uid, sizeof(struct dasd_uid));
8138c2ecf20Sopenharmony_ci}
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_cistatic void dasd_eckd_fill_rcd_cqr(struct dasd_device *device,
8168c2ecf20Sopenharmony_ci				   struct dasd_ccw_req *cqr,
8178c2ecf20Sopenharmony_ci				   __u8 *rcd_buffer,
8188c2ecf20Sopenharmony_ci				   __u8 lpm)
8198c2ecf20Sopenharmony_ci{
8208c2ecf20Sopenharmony_ci	struct ccw1 *ccw;
8218c2ecf20Sopenharmony_ci	/*
8228c2ecf20Sopenharmony_ci	 * buffer has to start with EBCDIC "V1.0" to show
8238c2ecf20Sopenharmony_ci	 * support for virtual device SNEQ
8248c2ecf20Sopenharmony_ci	 */
8258c2ecf20Sopenharmony_ci	rcd_buffer[0] = 0xE5;
8268c2ecf20Sopenharmony_ci	rcd_buffer[1] = 0xF1;
8278c2ecf20Sopenharmony_ci	rcd_buffer[2] = 0x4B;
8288c2ecf20Sopenharmony_ci	rcd_buffer[3] = 0xF0;
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci	ccw = cqr->cpaddr;
8318c2ecf20Sopenharmony_ci	ccw->cmd_code = DASD_ECKD_CCW_RCD;
8328c2ecf20Sopenharmony_ci	ccw->flags = 0;
8338c2ecf20Sopenharmony_ci	ccw->cda = (__u32)(addr_t)rcd_buffer;
8348c2ecf20Sopenharmony_ci	ccw->count = DASD_ECKD_RCD_DATA_SIZE;
8358c2ecf20Sopenharmony_ci	cqr->magic = DASD_ECKD_MAGIC;
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci	cqr->startdev = device;
8388c2ecf20Sopenharmony_ci	cqr->memdev = device;
8398c2ecf20Sopenharmony_ci	cqr->block = NULL;
8408c2ecf20Sopenharmony_ci	cqr->expires = 10*HZ;
8418c2ecf20Sopenharmony_ci	cqr->lpm = lpm;
8428c2ecf20Sopenharmony_ci	cqr->retries = 256;
8438c2ecf20Sopenharmony_ci	cqr->buildclk = get_tod_clock();
8448c2ecf20Sopenharmony_ci	cqr->status = DASD_CQR_FILLED;
8458c2ecf20Sopenharmony_ci	set_bit(DASD_CQR_VERIFY_PATH, &cqr->flags);
8468c2ecf20Sopenharmony_ci}
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ci/*
8498c2ecf20Sopenharmony_ci * Wakeup helper for read_conf
8508c2ecf20Sopenharmony_ci * if the cqr is not done and needs some error recovery
8518c2ecf20Sopenharmony_ci * the buffer has to be re-initialized with the EBCDIC "V1.0"
8528c2ecf20Sopenharmony_ci * to show support for virtual device SNEQ
8538c2ecf20Sopenharmony_ci */
8548c2ecf20Sopenharmony_cistatic void read_conf_cb(struct dasd_ccw_req *cqr, void *data)
8558c2ecf20Sopenharmony_ci{
8568c2ecf20Sopenharmony_ci	struct ccw1 *ccw;
8578c2ecf20Sopenharmony_ci	__u8 *rcd_buffer;
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_ci	if (cqr->status !=  DASD_CQR_DONE) {
8608c2ecf20Sopenharmony_ci		ccw = cqr->cpaddr;
8618c2ecf20Sopenharmony_ci		rcd_buffer = (__u8 *)((addr_t) ccw->cda);
8628c2ecf20Sopenharmony_ci		memset(rcd_buffer, 0, sizeof(*rcd_buffer));
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_ci		rcd_buffer[0] = 0xE5;
8658c2ecf20Sopenharmony_ci		rcd_buffer[1] = 0xF1;
8668c2ecf20Sopenharmony_ci		rcd_buffer[2] = 0x4B;
8678c2ecf20Sopenharmony_ci		rcd_buffer[3] = 0xF0;
8688c2ecf20Sopenharmony_ci	}
8698c2ecf20Sopenharmony_ci	dasd_wakeup_cb(cqr, data);
8708c2ecf20Sopenharmony_ci}
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_cistatic int dasd_eckd_read_conf_immediately(struct dasd_device *device,
8738c2ecf20Sopenharmony_ci					   struct dasd_ccw_req *cqr,
8748c2ecf20Sopenharmony_ci					   __u8 *rcd_buffer,
8758c2ecf20Sopenharmony_ci					   __u8 lpm)
8768c2ecf20Sopenharmony_ci{
8778c2ecf20Sopenharmony_ci	struct ciw *ciw;
8788c2ecf20Sopenharmony_ci	int rc;
8798c2ecf20Sopenharmony_ci	/*
8808c2ecf20Sopenharmony_ci	 * sanity check: scan for RCD command in extended SenseID data
8818c2ecf20Sopenharmony_ci	 * some devices do not support RCD
8828c2ecf20Sopenharmony_ci	 */
8838c2ecf20Sopenharmony_ci	ciw = ccw_device_get_ciw(device->cdev, CIW_TYPE_RCD);
8848c2ecf20Sopenharmony_ci	if (!ciw || ciw->cmd != DASD_ECKD_CCW_RCD)
8858c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_ci	dasd_eckd_fill_rcd_cqr(device, cqr, rcd_buffer, lpm);
8888c2ecf20Sopenharmony_ci	clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
8898c2ecf20Sopenharmony_ci	set_bit(DASD_CQR_ALLOW_SLOCK, &cqr->flags);
8908c2ecf20Sopenharmony_ci	cqr->retries = 5;
8918c2ecf20Sopenharmony_ci	cqr->callback = read_conf_cb;
8928c2ecf20Sopenharmony_ci	rc = dasd_sleep_on_immediatly(cqr);
8938c2ecf20Sopenharmony_ci	return rc;
8948c2ecf20Sopenharmony_ci}
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_cistatic int dasd_eckd_read_conf_lpm(struct dasd_device *device,
8978c2ecf20Sopenharmony_ci				   void **rcd_buffer,
8988c2ecf20Sopenharmony_ci				   int *rcd_buffer_size, __u8 lpm)
8998c2ecf20Sopenharmony_ci{
9008c2ecf20Sopenharmony_ci	struct ciw *ciw;
9018c2ecf20Sopenharmony_ci	char *rcd_buf = NULL;
9028c2ecf20Sopenharmony_ci	int ret;
9038c2ecf20Sopenharmony_ci	struct dasd_ccw_req *cqr;
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_ci	/*
9068c2ecf20Sopenharmony_ci	 * sanity check: scan for RCD command in extended SenseID data
9078c2ecf20Sopenharmony_ci	 * some devices do not support RCD
9088c2ecf20Sopenharmony_ci	 */
9098c2ecf20Sopenharmony_ci	ciw = ccw_device_get_ciw(device->cdev, CIW_TYPE_RCD);
9108c2ecf20Sopenharmony_ci	if (!ciw || ciw->cmd != DASD_ECKD_CCW_RCD) {
9118c2ecf20Sopenharmony_ci		ret = -EOPNOTSUPP;
9128c2ecf20Sopenharmony_ci		goto out_error;
9138c2ecf20Sopenharmony_ci	}
9148c2ecf20Sopenharmony_ci	rcd_buf = kzalloc(DASD_ECKD_RCD_DATA_SIZE, GFP_KERNEL | GFP_DMA);
9158c2ecf20Sopenharmony_ci	if (!rcd_buf) {
9168c2ecf20Sopenharmony_ci		ret = -ENOMEM;
9178c2ecf20Sopenharmony_ci		goto out_error;
9188c2ecf20Sopenharmony_ci	}
9198c2ecf20Sopenharmony_ci	cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* RCD */,
9208c2ecf20Sopenharmony_ci				   0, /* use rcd_buf as data ara */
9218c2ecf20Sopenharmony_ci				   device, NULL);
9228c2ecf20Sopenharmony_ci	if (IS_ERR(cqr)) {
9238c2ecf20Sopenharmony_ci		DBF_DEV_EVENT(DBF_WARNING, device, "%s",
9248c2ecf20Sopenharmony_ci			      "Could not allocate RCD request");
9258c2ecf20Sopenharmony_ci		ret = -ENOMEM;
9268c2ecf20Sopenharmony_ci		goto out_error;
9278c2ecf20Sopenharmony_ci	}
9288c2ecf20Sopenharmony_ci	dasd_eckd_fill_rcd_cqr(device, cqr, rcd_buf, lpm);
9298c2ecf20Sopenharmony_ci	cqr->callback = read_conf_cb;
9308c2ecf20Sopenharmony_ci	ret = dasd_sleep_on(cqr);
9318c2ecf20Sopenharmony_ci	/*
9328c2ecf20Sopenharmony_ci	 * on success we update the user input parms
9338c2ecf20Sopenharmony_ci	 */
9348c2ecf20Sopenharmony_ci	dasd_sfree_request(cqr, cqr->memdev);
9358c2ecf20Sopenharmony_ci	if (ret)
9368c2ecf20Sopenharmony_ci		goto out_error;
9378c2ecf20Sopenharmony_ci
9388c2ecf20Sopenharmony_ci	*rcd_buffer_size = DASD_ECKD_RCD_DATA_SIZE;
9398c2ecf20Sopenharmony_ci	*rcd_buffer = rcd_buf;
9408c2ecf20Sopenharmony_ci	return 0;
9418c2ecf20Sopenharmony_ciout_error:
9428c2ecf20Sopenharmony_ci	kfree(rcd_buf);
9438c2ecf20Sopenharmony_ci	*rcd_buffer = NULL;
9448c2ecf20Sopenharmony_ci	*rcd_buffer_size = 0;
9458c2ecf20Sopenharmony_ci	return ret;
9468c2ecf20Sopenharmony_ci}
9478c2ecf20Sopenharmony_ci
9488c2ecf20Sopenharmony_cistatic int dasd_eckd_identify_conf_parts(struct dasd_eckd_private *private)
9498c2ecf20Sopenharmony_ci{
9508c2ecf20Sopenharmony_ci
9518c2ecf20Sopenharmony_ci	struct dasd_sneq *sneq;
9528c2ecf20Sopenharmony_ci	int i, count;
9538c2ecf20Sopenharmony_ci
9548c2ecf20Sopenharmony_ci	private->ned = NULL;
9558c2ecf20Sopenharmony_ci	private->sneq = NULL;
9568c2ecf20Sopenharmony_ci	private->vdsneq = NULL;
9578c2ecf20Sopenharmony_ci	private->gneq = NULL;
9588c2ecf20Sopenharmony_ci	count = private->conf_len / sizeof(struct dasd_sneq);
9598c2ecf20Sopenharmony_ci	sneq = (struct dasd_sneq *)private->conf_data;
9608c2ecf20Sopenharmony_ci	for (i = 0; i < count; ++i) {
9618c2ecf20Sopenharmony_ci		if (sneq->flags.identifier == 1 && sneq->format == 1)
9628c2ecf20Sopenharmony_ci			private->sneq = sneq;
9638c2ecf20Sopenharmony_ci		else if (sneq->flags.identifier == 1 && sneq->format == 4)
9648c2ecf20Sopenharmony_ci			private->vdsneq = (struct vd_sneq *)sneq;
9658c2ecf20Sopenharmony_ci		else if (sneq->flags.identifier == 2)
9668c2ecf20Sopenharmony_ci			private->gneq = (struct dasd_gneq *)sneq;
9678c2ecf20Sopenharmony_ci		else if (sneq->flags.identifier == 3 && sneq->res1 == 1)
9688c2ecf20Sopenharmony_ci			private->ned = (struct dasd_ned *)sneq;
9698c2ecf20Sopenharmony_ci		sneq++;
9708c2ecf20Sopenharmony_ci	}
9718c2ecf20Sopenharmony_ci	if (!private->ned || !private->gneq) {
9728c2ecf20Sopenharmony_ci		private->ned = NULL;
9738c2ecf20Sopenharmony_ci		private->sneq = NULL;
9748c2ecf20Sopenharmony_ci		private->vdsneq = NULL;
9758c2ecf20Sopenharmony_ci		private->gneq = NULL;
9768c2ecf20Sopenharmony_ci		return -EINVAL;
9778c2ecf20Sopenharmony_ci	}
9788c2ecf20Sopenharmony_ci	return 0;
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_ci};
9818c2ecf20Sopenharmony_ci
9828c2ecf20Sopenharmony_cistatic unsigned char dasd_eckd_path_access(void *conf_data, int conf_len)
9838c2ecf20Sopenharmony_ci{
9848c2ecf20Sopenharmony_ci	struct dasd_gneq *gneq;
9858c2ecf20Sopenharmony_ci	int i, count, found;
9868c2ecf20Sopenharmony_ci
9878c2ecf20Sopenharmony_ci	count = conf_len / sizeof(*gneq);
9888c2ecf20Sopenharmony_ci	gneq = (struct dasd_gneq *)conf_data;
9898c2ecf20Sopenharmony_ci	found = 0;
9908c2ecf20Sopenharmony_ci	for (i = 0; i < count; ++i) {
9918c2ecf20Sopenharmony_ci		if (gneq->flags.identifier == 2) {
9928c2ecf20Sopenharmony_ci			found = 1;
9938c2ecf20Sopenharmony_ci			break;
9948c2ecf20Sopenharmony_ci		}
9958c2ecf20Sopenharmony_ci		gneq++;
9968c2ecf20Sopenharmony_ci	}
9978c2ecf20Sopenharmony_ci	if (found)
9988c2ecf20Sopenharmony_ci		return ((char *)gneq)[18] & 0x07;
9998c2ecf20Sopenharmony_ci	else
10008c2ecf20Sopenharmony_ci		return 0;
10018c2ecf20Sopenharmony_ci}
10028c2ecf20Sopenharmony_ci
10038c2ecf20Sopenharmony_cistatic void dasd_eckd_clear_conf_data(struct dasd_device *device)
10048c2ecf20Sopenharmony_ci{
10058c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
10068c2ecf20Sopenharmony_ci	int i;
10078c2ecf20Sopenharmony_ci
10088c2ecf20Sopenharmony_ci	private->conf_data = NULL;
10098c2ecf20Sopenharmony_ci	private->conf_len = 0;
10108c2ecf20Sopenharmony_ci	for (i = 0; i < 8; i++) {
10118c2ecf20Sopenharmony_ci		kfree(device->path[i].conf_data);
10128c2ecf20Sopenharmony_ci		device->path[i].conf_data = NULL;
10138c2ecf20Sopenharmony_ci		device->path[i].cssid = 0;
10148c2ecf20Sopenharmony_ci		device->path[i].ssid = 0;
10158c2ecf20Sopenharmony_ci		device->path[i].chpid = 0;
10168c2ecf20Sopenharmony_ci	}
10178c2ecf20Sopenharmony_ci}
10188c2ecf20Sopenharmony_ci
10198c2ecf20Sopenharmony_ci
10208c2ecf20Sopenharmony_cistatic int dasd_eckd_read_conf(struct dasd_device *device)
10218c2ecf20Sopenharmony_ci{
10228c2ecf20Sopenharmony_ci	void *conf_data;
10238c2ecf20Sopenharmony_ci	int conf_len, conf_data_saved;
10248c2ecf20Sopenharmony_ci	int rc, path_err, pos;
10258c2ecf20Sopenharmony_ci	__u8 lpm, opm;
10268c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private, path_private;
10278c2ecf20Sopenharmony_ci	struct dasd_uid *uid;
10288c2ecf20Sopenharmony_ci	char print_path_uid[60], print_device_uid[60];
10298c2ecf20Sopenharmony_ci	struct channel_path_desc_fmt0 *chp_desc;
10308c2ecf20Sopenharmony_ci	struct subchannel_id sch_id;
10318c2ecf20Sopenharmony_ci
10328c2ecf20Sopenharmony_ci	private = device->private;
10338c2ecf20Sopenharmony_ci	opm = ccw_device_get_path_mask(device->cdev);
10348c2ecf20Sopenharmony_ci	ccw_device_get_schid(device->cdev, &sch_id);
10358c2ecf20Sopenharmony_ci	conf_data_saved = 0;
10368c2ecf20Sopenharmony_ci	path_err = 0;
10378c2ecf20Sopenharmony_ci	/* get configuration data per operational path */
10388c2ecf20Sopenharmony_ci	for (lpm = 0x80; lpm; lpm>>= 1) {
10398c2ecf20Sopenharmony_ci		if (!(lpm & opm))
10408c2ecf20Sopenharmony_ci			continue;
10418c2ecf20Sopenharmony_ci		rc = dasd_eckd_read_conf_lpm(device, &conf_data,
10428c2ecf20Sopenharmony_ci					     &conf_len, lpm);
10438c2ecf20Sopenharmony_ci		if (rc && rc != -EOPNOTSUPP) {	/* -EOPNOTSUPP is ok */
10448c2ecf20Sopenharmony_ci			DBF_EVENT_DEVID(DBF_WARNING, device->cdev,
10458c2ecf20Sopenharmony_ci					"Read configuration data returned "
10468c2ecf20Sopenharmony_ci					"error %d", rc);
10478c2ecf20Sopenharmony_ci			return rc;
10488c2ecf20Sopenharmony_ci		}
10498c2ecf20Sopenharmony_ci		if (conf_data == NULL) {
10508c2ecf20Sopenharmony_ci			DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s",
10518c2ecf20Sopenharmony_ci					"No configuration data "
10528c2ecf20Sopenharmony_ci					"retrieved");
10538c2ecf20Sopenharmony_ci			/* no further analysis possible */
10548c2ecf20Sopenharmony_ci			dasd_path_add_opm(device, opm);
10558c2ecf20Sopenharmony_ci			continue;	/* no error */
10568c2ecf20Sopenharmony_ci		}
10578c2ecf20Sopenharmony_ci		/* save first valid configuration data */
10588c2ecf20Sopenharmony_ci		if (!conf_data_saved) {
10598c2ecf20Sopenharmony_ci			/* initially clear previously stored conf_data */
10608c2ecf20Sopenharmony_ci			dasd_eckd_clear_conf_data(device);
10618c2ecf20Sopenharmony_ci			private->conf_data = conf_data;
10628c2ecf20Sopenharmony_ci			private->conf_len = conf_len;
10638c2ecf20Sopenharmony_ci			if (dasd_eckd_identify_conf_parts(private)) {
10648c2ecf20Sopenharmony_ci				private->conf_data = NULL;
10658c2ecf20Sopenharmony_ci				private->conf_len = 0;
10668c2ecf20Sopenharmony_ci				kfree(conf_data);
10678c2ecf20Sopenharmony_ci				continue;
10688c2ecf20Sopenharmony_ci			}
10698c2ecf20Sopenharmony_ci			pos = pathmask_to_pos(lpm);
10708c2ecf20Sopenharmony_ci			/* store per path conf_data */
10718c2ecf20Sopenharmony_ci			device->path[pos].conf_data = conf_data;
10728c2ecf20Sopenharmony_ci			device->path[pos].cssid = sch_id.cssid;
10738c2ecf20Sopenharmony_ci			device->path[pos].ssid = sch_id.ssid;
10748c2ecf20Sopenharmony_ci			chp_desc = ccw_device_get_chp_desc(device->cdev, pos);
10758c2ecf20Sopenharmony_ci			if (chp_desc)
10768c2ecf20Sopenharmony_ci				device->path[pos].chpid = chp_desc->chpid;
10778c2ecf20Sopenharmony_ci			kfree(chp_desc);
10788c2ecf20Sopenharmony_ci			/*
10798c2ecf20Sopenharmony_ci			 * build device UID that other path data
10808c2ecf20Sopenharmony_ci			 * can be compared to it
10818c2ecf20Sopenharmony_ci			 */
10828c2ecf20Sopenharmony_ci			dasd_eckd_generate_uid(device);
10838c2ecf20Sopenharmony_ci			conf_data_saved++;
10848c2ecf20Sopenharmony_ci		} else {
10858c2ecf20Sopenharmony_ci			path_private.conf_data = conf_data;
10868c2ecf20Sopenharmony_ci			path_private.conf_len = DASD_ECKD_RCD_DATA_SIZE;
10878c2ecf20Sopenharmony_ci			if (dasd_eckd_identify_conf_parts(
10888c2ecf20Sopenharmony_ci				    &path_private)) {
10898c2ecf20Sopenharmony_ci				path_private.conf_data = NULL;
10908c2ecf20Sopenharmony_ci				path_private.conf_len = 0;
10918c2ecf20Sopenharmony_ci				kfree(conf_data);
10928c2ecf20Sopenharmony_ci				continue;
10938c2ecf20Sopenharmony_ci			}
10948c2ecf20Sopenharmony_ci			if (dasd_eckd_compare_path_uid(
10958c2ecf20Sopenharmony_ci				    device, &path_private)) {
10968c2ecf20Sopenharmony_ci				uid = &path_private.uid;
10978c2ecf20Sopenharmony_ci				if (strlen(uid->vduit) > 0)
10988c2ecf20Sopenharmony_ci					snprintf(print_path_uid,
10998c2ecf20Sopenharmony_ci						 sizeof(print_path_uid),
11008c2ecf20Sopenharmony_ci						 "%s.%s.%04x.%02x.%s",
11018c2ecf20Sopenharmony_ci						 uid->vendor, uid->serial,
11028c2ecf20Sopenharmony_ci						 uid->ssid, uid->real_unit_addr,
11038c2ecf20Sopenharmony_ci						 uid->vduit);
11048c2ecf20Sopenharmony_ci				else
11058c2ecf20Sopenharmony_ci					snprintf(print_path_uid,
11068c2ecf20Sopenharmony_ci						 sizeof(print_path_uid),
11078c2ecf20Sopenharmony_ci						 "%s.%s.%04x.%02x",
11088c2ecf20Sopenharmony_ci						 uid->vendor, uid->serial,
11098c2ecf20Sopenharmony_ci						 uid->ssid,
11108c2ecf20Sopenharmony_ci						 uid->real_unit_addr);
11118c2ecf20Sopenharmony_ci				uid = &private->uid;
11128c2ecf20Sopenharmony_ci				if (strlen(uid->vduit) > 0)
11138c2ecf20Sopenharmony_ci					snprintf(print_device_uid,
11148c2ecf20Sopenharmony_ci						 sizeof(print_device_uid),
11158c2ecf20Sopenharmony_ci						 "%s.%s.%04x.%02x.%s",
11168c2ecf20Sopenharmony_ci						 uid->vendor, uid->serial,
11178c2ecf20Sopenharmony_ci						 uid->ssid, uid->real_unit_addr,
11188c2ecf20Sopenharmony_ci						 uid->vduit);
11198c2ecf20Sopenharmony_ci				else
11208c2ecf20Sopenharmony_ci					snprintf(print_device_uid,
11218c2ecf20Sopenharmony_ci						 sizeof(print_device_uid),
11228c2ecf20Sopenharmony_ci						 "%s.%s.%04x.%02x",
11238c2ecf20Sopenharmony_ci						 uid->vendor, uid->serial,
11248c2ecf20Sopenharmony_ci						 uid->ssid,
11258c2ecf20Sopenharmony_ci						 uid->real_unit_addr);
11268c2ecf20Sopenharmony_ci				dev_err(&device->cdev->dev,
11278c2ecf20Sopenharmony_ci					"Not all channel paths lead to "
11288c2ecf20Sopenharmony_ci					"the same device, path %02X leads to "
11298c2ecf20Sopenharmony_ci					"device %s instead of %s\n", lpm,
11308c2ecf20Sopenharmony_ci					print_path_uid, print_device_uid);
11318c2ecf20Sopenharmony_ci				path_err = -EINVAL;
11328c2ecf20Sopenharmony_ci				dasd_path_add_cablepm(device, lpm);
11338c2ecf20Sopenharmony_ci				continue;
11348c2ecf20Sopenharmony_ci			}
11358c2ecf20Sopenharmony_ci			pos = pathmask_to_pos(lpm);
11368c2ecf20Sopenharmony_ci			/* store per path conf_data */
11378c2ecf20Sopenharmony_ci			device->path[pos].conf_data = conf_data;
11388c2ecf20Sopenharmony_ci			device->path[pos].cssid = sch_id.cssid;
11398c2ecf20Sopenharmony_ci			device->path[pos].ssid = sch_id.ssid;
11408c2ecf20Sopenharmony_ci			chp_desc = ccw_device_get_chp_desc(device->cdev, pos);
11418c2ecf20Sopenharmony_ci			if (chp_desc)
11428c2ecf20Sopenharmony_ci				device->path[pos].chpid = chp_desc->chpid;
11438c2ecf20Sopenharmony_ci			kfree(chp_desc);
11448c2ecf20Sopenharmony_ci			path_private.conf_data = NULL;
11458c2ecf20Sopenharmony_ci			path_private.conf_len = 0;
11468c2ecf20Sopenharmony_ci		}
11478c2ecf20Sopenharmony_ci		switch (dasd_eckd_path_access(conf_data, conf_len)) {
11488c2ecf20Sopenharmony_ci		case 0x02:
11498c2ecf20Sopenharmony_ci			dasd_path_add_nppm(device, lpm);
11508c2ecf20Sopenharmony_ci			break;
11518c2ecf20Sopenharmony_ci		case 0x03:
11528c2ecf20Sopenharmony_ci			dasd_path_add_ppm(device, lpm);
11538c2ecf20Sopenharmony_ci			break;
11548c2ecf20Sopenharmony_ci		}
11558c2ecf20Sopenharmony_ci		if (!dasd_path_get_opm(device)) {
11568c2ecf20Sopenharmony_ci			dasd_path_set_opm(device, lpm);
11578c2ecf20Sopenharmony_ci			dasd_generic_path_operational(device);
11588c2ecf20Sopenharmony_ci		} else {
11598c2ecf20Sopenharmony_ci			dasd_path_add_opm(device, lpm);
11608c2ecf20Sopenharmony_ci		}
11618c2ecf20Sopenharmony_ci	}
11628c2ecf20Sopenharmony_ci
11638c2ecf20Sopenharmony_ci	return path_err;
11648c2ecf20Sopenharmony_ci}
11658c2ecf20Sopenharmony_ci
11668c2ecf20Sopenharmony_cistatic u32 get_fcx_max_data(struct dasd_device *device)
11678c2ecf20Sopenharmony_ci{
11688c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
11698c2ecf20Sopenharmony_ci	int fcx_in_css, fcx_in_gneq, fcx_in_features;
11708c2ecf20Sopenharmony_ci	unsigned int mdc;
11718c2ecf20Sopenharmony_ci	int tpm;
11728c2ecf20Sopenharmony_ci
11738c2ecf20Sopenharmony_ci	if (dasd_nofcx)
11748c2ecf20Sopenharmony_ci		return 0;
11758c2ecf20Sopenharmony_ci	/* is transport mode supported? */
11768c2ecf20Sopenharmony_ci	fcx_in_css = css_general_characteristics.fcx;
11778c2ecf20Sopenharmony_ci	fcx_in_gneq = private->gneq->reserved2[7] & 0x04;
11788c2ecf20Sopenharmony_ci	fcx_in_features = private->features.feature[40] & 0x80;
11798c2ecf20Sopenharmony_ci	tpm = fcx_in_css && fcx_in_gneq && fcx_in_features;
11808c2ecf20Sopenharmony_ci
11818c2ecf20Sopenharmony_ci	if (!tpm)
11828c2ecf20Sopenharmony_ci		return 0;
11838c2ecf20Sopenharmony_ci
11848c2ecf20Sopenharmony_ci	mdc = ccw_device_get_mdc(device->cdev, 0);
11858c2ecf20Sopenharmony_ci	if (mdc == 0) {
11868c2ecf20Sopenharmony_ci		dev_warn(&device->cdev->dev, "Detecting the maximum supported data size for zHPF requests failed\n");
11878c2ecf20Sopenharmony_ci		return 0;
11888c2ecf20Sopenharmony_ci	} else {
11898c2ecf20Sopenharmony_ci		return (u32)mdc * FCX_MAX_DATA_FACTOR;
11908c2ecf20Sopenharmony_ci	}
11918c2ecf20Sopenharmony_ci}
11928c2ecf20Sopenharmony_ci
11938c2ecf20Sopenharmony_cistatic int verify_fcx_max_data(struct dasd_device *device, __u8 lpm)
11948c2ecf20Sopenharmony_ci{
11958c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
11968c2ecf20Sopenharmony_ci	unsigned int mdc;
11978c2ecf20Sopenharmony_ci	u32 fcx_max_data;
11988c2ecf20Sopenharmony_ci
11998c2ecf20Sopenharmony_ci	if (private->fcx_max_data) {
12008c2ecf20Sopenharmony_ci		mdc = ccw_device_get_mdc(device->cdev, lpm);
12018c2ecf20Sopenharmony_ci		if (mdc == 0) {
12028c2ecf20Sopenharmony_ci			dev_warn(&device->cdev->dev,
12038c2ecf20Sopenharmony_ci				 "Detecting the maximum data size for zHPF "
12048c2ecf20Sopenharmony_ci				 "requests failed (rc=%d) for a new path %x\n",
12058c2ecf20Sopenharmony_ci				 mdc, lpm);
12068c2ecf20Sopenharmony_ci			return mdc;
12078c2ecf20Sopenharmony_ci		}
12088c2ecf20Sopenharmony_ci		fcx_max_data = (u32)mdc * FCX_MAX_DATA_FACTOR;
12098c2ecf20Sopenharmony_ci		if (fcx_max_data < private->fcx_max_data) {
12108c2ecf20Sopenharmony_ci			dev_warn(&device->cdev->dev,
12118c2ecf20Sopenharmony_ci				 "The maximum data size for zHPF requests %u "
12128c2ecf20Sopenharmony_ci				 "on a new path %x is below the active maximum "
12138c2ecf20Sopenharmony_ci				 "%u\n", fcx_max_data, lpm,
12148c2ecf20Sopenharmony_ci				 private->fcx_max_data);
12158c2ecf20Sopenharmony_ci			return -EACCES;
12168c2ecf20Sopenharmony_ci		}
12178c2ecf20Sopenharmony_ci	}
12188c2ecf20Sopenharmony_ci	return 0;
12198c2ecf20Sopenharmony_ci}
12208c2ecf20Sopenharmony_ci
12218c2ecf20Sopenharmony_cistatic int rebuild_device_uid(struct dasd_device *device,
12228c2ecf20Sopenharmony_ci			      struct pe_handler_work_data *data)
12238c2ecf20Sopenharmony_ci{
12248c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
12258c2ecf20Sopenharmony_ci	__u8 lpm, opm = dasd_path_get_opm(device);
12268c2ecf20Sopenharmony_ci	int rc = -ENODEV;
12278c2ecf20Sopenharmony_ci
12288c2ecf20Sopenharmony_ci	for (lpm = 0x80; lpm; lpm >>= 1) {
12298c2ecf20Sopenharmony_ci		if (!(lpm & opm))
12308c2ecf20Sopenharmony_ci			continue;
12318c2ecf20Sopenharmony_ci		memset(&data->rcd_buffer, 0, sizeof(data->rcd_buffer));
12328c2ecf20Sopenharmony_ci		memset(&data->cqr, 0, sizeof(data->cqr));
12338c2ecf20Sopenharmony_ci		data->cqr.cpaddr = &data->ccw;
12348c2ecf20Sopenharmony_ci		rc = dasd_eckd_read_conf_immediately(device, &data->cqr,
12358c2ecf20Sopenharmony_ci						     data->rcd_buffer,
12368c2ecf20Sopenharmony_ci						     lpm);
12378c2ecf20Sopenharmony_ci
12388c2ecf20Sopenharmony_ci		if (rc) {
12398c2ecf20Sopenharmony_ci			if (rc == -EOPNOTSUPP) /* -EOPNOTSUPP is ok */
12408c2ecf20Sopenharmony_ci				continue;
12418c2ecf20Sopenharmony_ci			DBF_EVENT_DEVID(DBF_WARNING, device->cdev,
12428c2ecf20Sopenharmony_ci					"Read configuration data "
12438c2ecf20Sopenharmony_ci					"returned error %d", rc);
12448c2ecf20Sopenharmony_ci			break;
12458c2ecf20Sopenharmony_ci		}
12468c2ecf20Sopenharmony_ci		memcpy(private->conf_data, data->rcd_buffer,
12478c2ecf20Sopenharmony_ci		       DASD_ECKD_RCD_DATA_SIZE);
12488c2ecf20Sopenharmony_ci		if (dasd_eckd_identify_conf_parts(private)) {
12498c2ecf20Sopenharmony_ci			rc = -ENODEV;
12508c2ecf20Sopenharmony_ci		} else /* first valid path is enough */
12518c2ecf20Sopenharmony_ci			break;
12528c2ecf20Sopenharmony_ci	}
12538c2ecf20Sopenharmony_ci
12548c2ecf20Sopenharmony_ci	if (!rc)
12558c2ecf20Sopenharmony_ci		rc = dasd_eckd_generate_uid(device);
12568c2ecf20Sopenharmony_ci
12578c2ecf20Sopenharmony_ci	return rc;
12588c2ecf20Sopenharmony_ci}
12598c2ecf20Sopenharmony_ci
12608c2ecf20Sopenharmony_cistatic void dasd_eckd_path_available_action(struct dasd_device *device,
12618c2ecf20Sopenharmony_ci					    struct pe_handler_work_data *data)
12628c2ecf20Sopenharmony_ci{
12638c2ecf20Sopenharmony_ci	struct dasd_eckd_private path_private;
12648c2ecf20Sopenharmony_ci	struct dasd_uid *uid;
12658c2ecf20Sopenharmony_ci	__u8 path_rcd_buf[DASD_ECKD_RCD_DATA_SIZE];
12668c2ecf20Sopenharmony_ci	__u8 lpm, opm, npm, ppm, epm, hpfpm, cablepm;
12678c2ecf20Sopenharmony_ci	unsigned long flags;
12688c2ecf20Sopenharmony_ci	char print_uid[60];
12698c2ecf20Sopenharmony_ci	int rc;
12708c2ecf20Sopenharmony_ci
12718c2ecf20Sopenharmony_ci	opm = 0;
12728c2ecf20Sopenharmony_ci	npm = 0;
12738c2ecf20Sopenharmony_ci	ppm = 0;
12748c2ecf20Sopenharmony_ci	epm = 0;
12758c2ecf20Sopenharmony_ci	hpfpm = 0;
12768c2ecf20Sopenharmony_ci	cablepm = 0;
12778c2ecf20Sopenharmony_ci
12788c2ecf20Sopenharmony_ci	for (lpm = 0x80; lpm; lpm >>= 1) {
12798c2ecf20Sopenharmony_ci		if (!(lpm & data->tbvpm))
12808c2ecf20Sopenharmony_ci			continue;
12818c2ecf20Sopenharmony_ci		memset(&data->rcd_buffer, 0, sizeof(data->rcd_buffer));
12828c2ecf20Sopenharmony_ci		memset(&data->cqr, 0, sizeof(data->cqr));
12838c2ecf20Sopenharmony_ci		data->cqr.cpaddr = &data->ccw;
12848c2ecf20Sopenharmony_ci		rc = dasd_eckd_read_conf_immediately(device, &data->cqr,
12858c2ecf20Sopenharmony_ci						     data->rcd_buffer,
12868c2ecf20Sopenharmony_ci						     lpm);
12878c2ecf20Sopenharmony_ci		if (!rc) {
12888c2ecf20Sopenharmony_ci			switch (dasd_eckd_path_access(data->rcd_buffer,
12898c2ecf20Sopenharmony_ci						      DASD_ECKD_RCD_DATA_SIZE)
12908c2ecf20Sopenharmony_ci				) {
12918c2ecf20Sopenharmony_ci			case 0x02:
12928c2ecf20Sopenharmony_ci				npm |= lpm;
12938c2ecf20Sopenharmony_ci				break;
12948c2ecf20Sopenharmony_ci			case 0x03:
12958c2ecf20Sopenharmony_ci				ppm |= lpm;
12968c2ecf20Sopenharmony_ci				break;
12978c2ecf20Sopenharmony_ci			}
12988c2ecf20Sopenharmony_ci			opm |= lpm;
12998c2ecf20Sopenharmony_ci		} else if (rc == -EOPNOTSUPP) {
13008c2ecf20Sopenharmony_ci			DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s",
13018c2ecf20Sopenharmony_ci					"path verification: No configuration "
13028c2ecf20Sopenharmony_ci					"data retrieved");
13038c2ecf20Sopenharmony_ci			opm |= lpm;
13048c2ecf20Sopenharmony_ci		} else if (rc == -EAGAIN) {
13058c2ecf20Sopenharmony_ci			DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s",
13068c2ecf20Sopenharmony_ci					"path verification: device is stopped,"
13078c2ecf20Sopenharmony_ci					" try again later");
13088c2ecf20Sopenharmony_ci			epm |= lpm;
13098c2ecf20Sopenharmony_ci		} else {
13108c2ecf20Sopenharmony_ci			dev_warn(&device->cdev->dev,
13118c2ecf20Sopenharmony_ci				 "Reading device feature codes failed "
13128c2ecf20Sopenharmony_ci				 "(rc=%d) for new path %x\n", rc, lpm);
13138c2ecf20Sopenharmony_ci			continue;
13148c2ecf20Sopenharmony_ci		}
13158c2ecf20Sopenharmony_ci		if (verify_fcx_max_data(device, lpm)) {
13168c2ecf20Sopenharmony_ci			opm &= ~lpm;
13178c2ecf20Sopenharmony_ci			npm &= ~lpm;
13188c2ecf20Sopenharmony_ci			ppm &= ~lpm;
13198c2ecf20Sopenharmony_ci			hpfpm |= lpm;
13208c2ecf20Sopenharmony_ci			continue;
13218c2ecf20Sopenharmony_ci		}
13228c2ecf20Sopenharmony_ci
13238c2ecf20Sopenharmony_ci		/*
13248c2ecf20Sopenharmony_ci		 * save conf_data for comparison after
13258c2ecf20Sopenharmony_ci		 * rebuild_device_uid may have changed
13268c2ecf20Sopenharmony_ci		 * the original data
13278c2ecf20Sopenharmony_ci		 */
13288c2ecf20Sopenharmony_ci		memcpy(&path_rcd_buf, data->rcd_buffer,
13298c2ecf20Sopenharmony_ci		       DASD_ECKD_RCD_DATA_SIZE);
13308c2ecf20Sopenharmony_ci		path_private.conf_data = (void *) &path_rcd_buf;
13318c2ecf20Sopenharmony_ci		path_private.conf_len = DASD_ECKD_RCD_DATA_SIZE;
13328c2ecf20Sopenharmony_ci		if (dasd_eckd_identify_conf_parts(&path_private)) {
13338c2ecf20Sopenharmony_ci			path_private.conf_data = NULL;
13348c2ecf20Sopenharmony_ci			path_private.conf_len = 0;
13358c2ecf20Sopenharmony_ci			continue;
13368c2ecf20Sopenharmony_ci		}
13378c2ecf20Sopenharmony_ci
13388c2ecf20Sopenharmony_ci		/*
13398c2ecf20Sopenharmony_ci		 * compare path UID with device UID only if at least
13408c2ecf20Sopenharmony_ci		 * one valid path is left
13418c2ecf20Sopenharmony_ci		 * in other case the device UID may have changed and
13428c2ecf20Sopenharmony_ci		 * the first working path UID will be used as device UID
13438c2ecf20Sopenharmony_ci		 */
13448c2ecf20Sopenharmony_ci		if (dasd_path_get_opm(device) &&
13458c2ecf20Sopenharmony_ci		    dasd_eckd_compare_path_uid(device, &path_private)) {
13468c2ecf20Sopenharmony_ci			/*
13478c2ecf20Sopenharmony_ci			 * the comparison was not successful
13488c2ecf20Sopenharmony_ci			 * rebuild the device UID with at least one
13498c2ecf20Sopenharmony_ci			 * known path in case a z/VM hyperswap command
13508c2ecf20Sopenharmony_ci			 * has changed the device
13518c2ecf20Sopenharmony_ci			 *
13528c2ecf20Sopenharmony_ci			 * after this compare again
13538c2ecf20Sopenharmony_ci			 *
13548c2ecf20Sopenharmony_ci			 * if either the rebuild or the recompare fails
13558c2ecf20Sopenharmony_ci			 * the path can not be used
13568c2ecf20Sopenharmony_ci			 */
13578c2ecf20Sopenharmony_ci			if (rebuild_device_uid(device, data) ||
13588c2ecf20Sopenharmony_ci			    dasd_eckd_compare_path_uid(
13598c2ecf20Sopenharmony_ci				    device, &path_private)) {
13608c2ecf20Sopenharmony_ci				uid = &path_private.uid;
13618c2ecf20Sopenharmony_ci				if (strlen(uid->vduit) > 0)
13628c2ecf20Sopenharmony_ci					snprintf(print_uid, sizeof(print_uid),
13638c2ecf20Sopenharmony_ci						 "%s.%s.%04x.%02x.%s",
13648c2ecf20Sopenharmony_ci						 uid->vendor, uid->serial,
13658c2ecf20Sopenharmony_ci						 uid->ssid, uid->real_unit_addr,
13668c2ecf20Sopenharmony_ci						 uid->vduit);
13678c2ecf20Sopenharmony_ci				else
13688c2ecf20Sopenharmony_ci					snprintf(print_uid, sizeof(print_uid),
13698c2ecf20Sopenharmony_ci						 "%s.%s.%04x.%02x",
13708c2ecf20Sopenharmony_ci						 uid->vendor, uid->serial,
13718c2ecf20Sopenharmony_ci						 uid->ssid,
13728c2ecf20Sopenharmony_ci						 uid->real_unit_addr);
13738c2ecf20Sopenharmony_ci				dev_err(&device->cdev->dev,
13748c2ecf20Sopenharmony_ci					"The newly added channel path %02X "
13758c2ecf20Sopenharmony_ci					"will not be used because it leads "
13768c2ecf20Sopenharmony_ci					"to a different device %s\n",
13778c2ecf20Sopenharmony_ci					lpm, print_uid);
13788c2ecf20Sopenharmony_ci				opm &= ~lpm;
13798c2ecf20Sopenharmony_ci				npm &= ~lpm;
13808c2ecf20Sopenharmony_ci				ppm &= ~lpm;
13818c2ecf20Sopenharmony_ci				cablepm |= lpm;
13828c2ecf20Sopenharmony_ci				continue;
13838c2ecf20Sopenharmony_ci			}
13848c2ecf20Sopenharmony_ci		}
13858c2ecf20Sopenharmony_ci
13868c2ecf20Sopenharmony_ci		/*
13878c2ecf20Sopenharmony_ci		 * There is a small chance that a path is lost again between
13888c2ecf20Sopenharmony_ci		 * above path verification and the following modification of
13898c2ecf20Sopenharmony_ci		 * the device opm mask. We could avoid that race here by using
13908c2ecf20Sopenharmony_ci		 * yet another path mask, but we rather deal with this unlikely
13918c2ecf20Sopenharmony_ci		 * situation in dasd_start_IO.
13928c2ecf20Sopenharmony_ci		 */
13938c2ecf20Sopenharmony_ci		spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
13948c2ecf20Sopenharmony_ci		if (!dasd_path_get_opm(device) && opm) {
13958c2ecf20Sopenharmony_ci			dasd_path_set_opm(device, opm);
13968c2ecf20Sopenharmony_ci			dasd_generic_path_operational(device);
13978c2ecf20Sopenharmony_ci		} else {
13988c2ecf20Sopenharmony_ci			dasd_path_add_opm(device, opm);
13998c2ecf20Sopenharmony_ci		}
14008c2ecf20Sopenharmony_ci		dasd_path_add_nppm(device, npm);
14018c2ecf20Sopenharmony_ci		dasd_path_add_ppm(device, ppm);
14028c2ecf20Sopenharmony_ci		dasd_path_add_tbvpm(device, epm);
14038c2ecf20Sopenharmony_ci		dasd_path_add_cablepm(device, cablepm);
14048c2ecf20Sopenharmony_ci		dasd_path_add_nohpfpm(device, hpfpm);
14058c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
14068c2ecf20Sopenharmony_ci	}
14078c2ecf20Sopenharmony_ci}
14088c2ecf20Sopenharmony_ci
14098c2ecf20Sopenharmony_cistatic void do_pe_handler_work(struct work_struct *work)
14108c2ecf20Sopenharmony_ci{
14118c2ecf20Sopenharmony_ci	struct pe_handler_work_data *data;
14128c2ecf20Sopenharmony_ci	struct dasd_device *device;
14138c2ecf20Sopenharmony_ci
14148c2ecf20Sopenharmony_ci	data = container_of(work, struct pe_handler_work_data, worker);
14158c2ecf20Sopenharmony_ci	device = data->device;
14168c2ecf20Sopenharmony_ci
14178c2ecf20Sopenharmony_ci	/* delay path verification until device was resumed */
14188c2ecf20Sopenharmony_ci	if (test_bit(DASD_FLAG_SUSPENDED, &device->flags)) {
14198c2ecf20Sopenharmony_ci		schedule_work(work);
14208c2ecf20Sopenharmony_ci		return;
14218c2ecf20Sopenharmony_ci	}
14228c2ecf20Sopenharmony_ci	/* check if path verification already running and delay if so */
14238c2ecf20Sopenharmony_ci	if (test_and_set_bit(DASD_FLAG_PATH_VERIFY, &device->flags)) {
14248c2ecf20Sopenharmony_ci		schedule_work(work);
14258c2ecf20Sopenharmony_ci		return;
14268c2ecf20Sopenharmony_ci	}
14278c2ecf20Sopenharmony_ci
14288c2ecf20Sopenharmony_ci	dasd_eckd_path_available_action(device, data);
14298c2ecf20Sopenharmony_ci
14308c2ecf20Sopenharmony_ci	clear_bit(DASD_FLAG_PATH_VERIFY, &device->flags);
14318c2ecf20Sopenharmony_ci	dasd_put_device(device);
14328c2ecf20Sopenharmony_ci	if (data->isglobal)
14338c2ecf20Sopenharmony_ci		mutex_unlock(&dasd_pe_handler_mutex);
14348c2ecf20Sopenharmony_ci	else
14358c2ecf20Sopenharmony_ci		kfree(data);
14368c2ecf20Sopenharmony_ci}
14378c2ecf20Sopenharmony_ci
14388c2ecf20Sopenharmony_cistatic int dasd_eckd_pe_handler(struct dasd_device *device, __u8 lpm)
14398c2ecf20Sopenharmony_ci{
14408c2ecf20Sopenharmony_ci	struct pe_handler_work_data *data;
14418c2ecf20Sopenharmony_ci
14428c2ecf20Sopenharmony_ci	data = kmalloc(sizeof(*data), GFP_ATOMIC | GFP_DMA);
14438c2ecf20Sopenharmony_ci	if (!data) {
14448c2ecf20Sopenharmony_ci		if (mutex_trylock(&dasd_pe_handler_mutex)) {
14458c2ecf20Sopenharmony_ci			data = pe_handler_worker;
14468c2ecf20Sopenharmony_ci			data->isglobal = 1;
14478c2ecf20Sopenharmony_ci		} else {
14488c2ecf20Sopenharmony_ci			return -ENOMEM;
14498c2ecf20Sopenharmony_ci		}
14508c2ecf20Sopenharmony_ci	} else {
14518c2ecf20Sopenharmony_ci		memset(data, 0, sizeof(*data));
14528c2ecf20Sopenharmony_ci		data->isglobal = 0;
14538c2ecf20Sopenharmony_ci	}
14548c2ecf20Sopenharmony_ci	INIT_WORK(&data->worker, do_pe_handler_work);
14558c2ecf20Sopenharmony_ci	dasd_get_device(device);
14568c2ecf20Sopenharmony_ci	data->device = device;
14578c2ecf20Sopenharmony_ci	data->tbvpm = lpm;
14588c2ecf20Sopenharmony_ci	schedule_work(&data->worker);
14598c2ecf20Sopenharmony_ci	return 0;
14608c2ecf20Sopenharmony_ci}
14618c2ecf20Sopenharmony_ci
14628c2ecf20Sopenharmony_cistatic void dasd_eckd_reset_path(struct dasd_device *device, __u8 pm)
14638c2ecf20Sopenharmony_ci{
14648c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
14658c2ecf20Sopenharmony_ci	unsigned long flags;
14668c2ecf20Sopenharmony_ci
14678c2ecf20Sopenharmony_ci	if (!private->fcx_max_data)
14688c2ecf20Sopenharmony_ci		private->fcx_max_data = get_fcx_max_data(device);
14698c2ecf20Sopenharmony_ci	spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
14708c2ecf20Sopenharmony_ci	dasd_path_set_tbvpm(device, pm ? : dasd_path_get_notoperpm(device));
14718c2ecf20Sopenharmony_ci	dasd_schedule_device_bh(device);
14728c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
14738c2ecf20Sopenharmony_ci}
14748c2ecf20Sopenharmony_ci
14758c2ecf20Sopenharmony_cistatic int dasd_eckd_read_features(struct dasd_device *device)
14768c2ecf20Sopenharmony_ci{
14778c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
14788c2ecf20Sopenharmony_ci	struct dasd_psf_prssd_data *prssdp;
14798c2ecf20Sopenharmony_ci	struct dasd_rssd_features *features;
14808c2ecf20Sopenharmony_ci	struct dasd_ccw_req *cqr;
14818c2ecf20Sopenharmony_ci	struct ccw1 *ccw;
14828c2ecf20Sopenharmony_ci	int rc;
14838c2ecf20Sopenharmony_ci
14848c2ecf20Sopenharmony_ci	memset(&private->features, 0, sizeof(struct dasd_rssd_features));
14858c2ecf20Sopenharmony_ci	cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */	+ 1 /* RSSD */,
14868c2ecf20Sopenharmony_ci				   (sizeof(struct dasd_psf_prssd_data) +
14878c2ecf20Sopenharmony_ci				    sizeof(struct dasd_rssd_features)),
14888c2ecf20Sopenharmony_ci				   device, NULL);
14898c2ecf20Sopenharmony_ci	if (IS_ERR(cqr)) {
14908c2ecf20Sopenharmony_ci		DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", "Could not "
14918c2ecf20Sopenharmony_ci				"allocate initialization request");
14928c2ecf20Sopenharmony_ci		return PTR_ERR(cqr);
14938c2ecf20Sopenharmony_ci	}
14948c2ecf20Sopenharmony_ci	cqr->startdev = device;
14958c2ecf20Sopenharmony_ci	cqr->memdev = device;
14968c2ecf20Sopenharmony_ci	cqr->block = NULL;
14978c2ecf20Sopenharmony_ci	cqr->retries = 256;
14988c2ecf20Sopenharmony_ci	cqr->expires = 10 * HZ;
14998c2ecf20Sopenharmony_ci
15008c2ecf20Sopenharmony_ci	/* Prepare for Read Subsystem Data */
15018c2ecf20Sopenharmony_ci	prssdp = (struct dasd_psf_prssd_data *) cqr->data;
15028c2ecf20Sopenharmony_ci	memset(prssdp, 0, sizeof(struct dasd_psf_prssd_data));
15038c2ecf20Sopenharmony_ci	prssdp->order = PSF_ORDER_PRSSD;
15048c2ecf20Sopenharmony_ci	prssdp->suborder = 0x41;	/* Read Feature Codes */
15058c2ecf20Sopenharmony_ci	/* all other bytes of prssdp must be zero */
15068c2ecf20Sopenharmony_ci
15078c2ecf20Sopenharmony_ci	ccw = cqr->cpaddr;
15088c2ecf20Sopenharmony_ci	ccw->cmd_code = DASD_ECKD_CCW_PSF;
15098c2ecf20Sopenharmony_ci	ccw->count = sizeof(struct dasd_psf_prssd_data);
15108c2ecf20Sopenharmony_ci	ccw->flags |= CCW_FLAG_CC;
15118c2ecf20Sopenharmony_ci	ccw->cda = (__u32)(addr_t) prssdp;
15128c2ecf20Sopenharmony_ci
15138c2ecf20Sopenharmony_ci	/* Read Subsystem Data - feature codes */
15148c2ecf20Sopenharmony_ci	features = (struct dasd_rssd_features *) (prssdp + 1);
15158c2ecf20Sopenharmony_ci	memset(features, 0, sizeof(struct dasd_rssd_features));
15168c2ecf20Sopenharmony_ci
15178c2ecf20Sopenharmony_ci	ccw++;
15188c2ecf20Sopenharmony_ci	ccw->cmd_code = DASD_ECKD_CCW_RSSD;
15198c2ecf20Sopenharmony_ci	ccw->count = sizeof(struct dasd_rssd_features);
15208c2ecf20Sopenharmony_ci	ccw->cda = (__u32)(addr_t) features;
15218c2ecf20Sopenharmony_ci
15228c2ecf20Sopenharmony_ci	cqr->buildclk = get_tod_clock();
15238c2ecf20Sopenharmony_ci	cqr->status = DASD_CQR_FILLED;
15248c2ecf20Sopenharmony_ci	rc = dasd_sleep_on(cqr);
15258c2ecf20Sopenharmony_ci	if (rc == 0) {
15268c2ecf20Sopenharmony_ci		prssdp = (struct dasd_psf_prssd_data *) cqr->data;
15278c2ecf20Sopenharmony_ci		features = (struct dasd_rssd_features *) (prssdp + 1);
15288c2ecf20Sopenharmony_ci		memcpy(&private->features, features,
15298c2ecf20Sopenharmony_ci		       sizeof(struct dasd_rssd_features));
15308c2ecf20Sopenharmony_ci	} else
15318c2ecf20Sopenharmony_ci		dev_warn(&device->cdev->dev, "Reading device feature codes"
15328c2ecf20Sopenharmony_ci			 " failed with rc=%d\n", rc);
15338c2ecf20Sopenharmony_ci	dasd_sfree_request(cqr, cqr->memdev);
15348c2ecf20Sopenharmony_ci	return rc;
15358c2ecf20Sopenharmony_ci}
15368c2ecf20Sopenharmony_ci
15378c2ecf20Sopenharmony_ci/* Read Volume Information - Volume Storage Query */
15388c2ecf20Sopenharmony_cistatic int dasd_eckd_read_vol_info(struct dasd_device *device)
15398c2ecf20Sopenharmony_ci{
15408c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
15418c2ecf20Sopenharmony_ci	struct dasd_psf_prssd_data *prssdp;
15428c2ecf20Sopenharmony_ci	struct dasd_rssd_vsq *vsq;
15438c2ecf20Sopenharmony_ci	struct dasd_ccw_req *cqr;
15448c2ecf20Sopenharmony_ci	struct ccw1 *ccw;
15458c2ecf20Sopenharmony_ci	int useglobal;
15468c2ecf20Sopenharmony_ci	int rc;
15478c2ecf20Sopenharmony_ci
15488c2ecf20Sopenharmony_ci	/* This command cannot be executed on an alias device */
15498c2ecf20Sopenharmony_ci	if (private->uid.type == UA_BASE_PAV_ALIAS ||
15508c2ecf20Sopenharmony_ci	    private->uid.type == UA_HYPER_PAV_ALIAS)
15518c2ecf20Sopenharmony_ci		return 0;
15528c2ecf20Sopenharmony_ci
15538c2ecf20Sopenharmony_ci	useglobal = 0;
15548c2ecf20Sopenharmony_ci	cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 2 /* PSF + RSSD */,
15558c2ecf20Sopenharmony_ci				   sizeof(*prssdp) + sizeof(*vsq), device, NULL);
15568c2ecf20Sopenharmony_ci	if (IS_ERR(cqr)) {
15578c2ecf20Sopenharmony_ci		DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s",
15588c2ecf20Sopenharmony_ci				"Could not allocate initialization request");
15598c2ecf20Sopenharmony_ci		mutex_lock(&dasd_vol_info_mutex);
15608c2ecf20Sopenharmony_ci		useglobal = 1;
15618c2ecf20Sopenharmony_ci		cqr = &dasd_vol_info_req->cqr;
15628c2ecf20Sopenharmony_ci		memset(cqr, 0, sizeof(*cqr));
15638c2ecf20Sopenharmony_ci		memset(dasd_vol_info_req, 0, sizeof(*dasd_vol_info_req));
15648c2ecf20Sopenharmony_ci		cqr->cpaddr = &dasd_vol_info_req->ccw;
15658c2ecf20Sopenharmony_ci		cqr->data = &dasd_vol_info_req->data;
15668c2ecf20Sopenharmony_ci		cqr->magic = DASD_ECKD_MAGIC;
15678c2ecf20Sopenharmony_ci	}
15688c2ecf20Sopenharmony_ci
15698c2ecf20Sopenharmony_ci	/* Prepare for Read Subsystem Data */
15708c2ecf20Sopenharmony_ci	prssdp = cqr->data;
15718c2ecf20Sopenharmony_ci	prssdp->order = PSF_ORDER_PRSSD;
15728c2ecf20Sopenharmony_ci	prssdp->suborder = PSF_SUBORDER_VSQ;	/* Volume Storage Query */
15738c2ecf20Sopenharmony_ci	prssdp->lss = private->ned->ID;
15748c2ecf20Sopenharmony_ci	prssdp->volume = private->ned->unit_addr;
15758c2ecf20Sopenharmony_ci
15768c2ecf20Sopenharmony_ci	ccw = cqr->cpaddr;
15778c2ecf20Sopenharmony_ci	ccw->cmd_code = DASD_ECKD_CCW_PSF;
15788c2ecf20Sopenharmony_ci	ccw->count = sizeof(*prssdp);
15798c2ecf20Sopenharmony_ci	ccw->flags |= CCW_FLAG_CC;
15808c2ecf20Sopenharmony_ci	ccw->cda = (__u32)(addr_t)prssdp;
15818c2ecf20Sopenharmony_ci
15828c2ecf20Sopenharmony_ci	/* Read Subsystem Data - Volume Storage Query */
15838c2ecf20Sopenharmony_ci	vsq = (struct dasd_rssd_vsq *)(prssdp + 1);
15848c2ecf20Sopenharmony_ci	memset(vsq, 0, sizeof(*vsq));
15858c2ecf20Sopenharmony_ci
15868c2ecf20Sopenharmony_ci	ccw++;
15878c2ecf20Sopenharmony_ci	ccw->cmd_code = DASD_ECKD_CCW_RSSD;
15888c2ecf20Sopenharmony_ci	ccw->count = sizeof(*vsq);
15898c2ecf20Sopenharmony_ci	ccw->flags |= CCW_FLAG_SLI;
15908c2ecf20Sopenharmony_ci	ccw->cda = (__u32)(addr_t)vsq;
15918c2ecf20Sopenharmony_ci
15928c2ecf20Sopenharmony_ci	cqr->buildclk = get_tod_clock();
15938c2ecf20Sopenharmony_ci	cqr->status = DASD_CQR_FILLED;
15948c2ecf20Sopenharmony_ci	cqr->startdev = device;
15958c2ecf20Sopenharmony_ci	cqr->memdev = device;
15968c2ecf20Sopenharmony_ci	cqr->block = NULL;
15978c2ecf20Sopenharmony_ci	cqr->retries = 256;
15988c2ecf20Sopenharmony_ci	cqr->expires = device->default_expires * HZ;
15998c2ecf20Sopenharmony_ci	/* The command might not be supported. Suppress the error output */
16008c2ecf20Sopenharmony_ci	__set_bit(DASD_CQR_SUPPRESS_CR, &cqr->flags);
16018c2ecf20Sopenharmony_ci
16028c2ecf20Sopenharmony_ci	rc = dasd_sleep_on_interruptible(cqr);
16038c2ecf20Sopenharmony_ci	if (rc == 0) {
16048c2ecf20Sopenharmony_ci		memcpy(&private->vsq, vsq, sizeof(*vsq));
16058c2ecf20Sopenharmony_ci	} else {
16068c2ecf20Sopenharmony_ci		DBF_EVENT_DEVID(DBF_WARNING, device->cdev,
16078c2ecf20Sopenharmony_ci				"Reading the volume storage information failed with rc=%d", rc);
16088c2ecf20Sopenharmony_ci	}
16098c2ecf20Sopenharmony_ci
16108c2ecf20Sopenharmony_ci	if (useglobal)
16118c2ecf20Sopenharmony_ci		mutex_unlock(&dasd_vol_info_mutex);
16128c2ecf20Sopenharmony_ci	else
16138c2ecf20Sopenharmony_ci		dasd_sfree_request(cqr, cqr->memdev);
16148c2ecf20Sopenharmony_ci
16158c2ecf20Sopenharmony_ci	return rc;
16168c2ecf20Sopenharmony_ci}
16178c2ecf20Sopenharmony_ci
16188c2ecf20Sopenharmony_cistatic int dasd_eckd_is_ese(struct dasd_device *device)
16198c2ecf20Sopenharmony_ci{
16208c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
16218c2ecf20Sopenharmony_ci
16228c2ecf20Sopenharmony_ci	return private->vsq.vol_info.ese;
16238c2ecf20Sopenharmony_ci}
16248c2ecf20Sopenharmony_ci
16258c2ecf20Sopenharmony_cistatic int dasd_eckd_ext_pool_id(struct dasd_device *device)
16268c2ecf20Sopenharmony_ci{
16278c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
16288c2ecf20Sopenharmony_ci
16298c2ecf20Sopenharmony_ci	return private->vsq.extent_pool_id;
16308c2ecf20Sopenharmony_ci}
16318c2ecf20Sopenharmony_ci
16328c2ecf20Sopenharmony_ci/*
16338c2ecf20Sopenharmony_ci * This value represents the total amount of available space. As more space is
16348c2ecf20Sopenharmony_ci * allocated by ESE volumes, this value will decrease.
16358c2ecf20Sopenharmony_ci * The data for this value is therefore updated on any call.
16368c2ecf20Sopenharmony_ci */
16378c2ecf20Sopenharmony_cistatic int dasd_eckd_space_configured(struct dasd_device *device)
16388c2ecf20Sopenharmony_ci{
16398c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
16408c2ecf20Sopenharmony_ci	int rc;
16418c2ecf20Sopenharmony_ci
16428c2ecf20Sopenharmony_ci	rc = dasd_eckd_read_vol_info(device);
16438c2ecf20Sopenharmony_ci
16448c2ecf20Sopenharmony_ci	return rc ? : private->vsq.space_configured;
16458c2ecf20Sopenharmony_ci}
16468c2ecf20Sopenharmony_ci
16478c2ecf20Sopenharmony_ci/*
16488c2ecf20Sopenharmony_ci * The value of space allocated by an ESE volume may have changed and is
16498c2ecf20Sopenharmony_ci * therefore updated on any call.
16508c2ecf20Sopenharmony_ci */
16518c2ecf20Sopenharmony_cistatic int dasd_eckd_space_allocated(struct dasd_device *device)
16528c2ecf20Sopenharmony_ci{
16538c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
16548c2ecf20Sopenharmony_ci	int rc;
16558c2ecf20Sopenharmony_ci
16568c2ecf20Sopenharmony_ci	rc = dasd_eckd_read_vol_info(device);
16578c2ecf20Sopenharmony_ci
16588c2ecf20Sopenharmony_ci	return rc ? : private->vsq.space_allocated;
16598c2ecf20Sopenharmony_ci}
16608c2ecf20Sopenharmony_ci
16618c2ecf20Sopenharmony_cistatic int dasd_eckd_logical_capacity(struct dasd_device *device)
16628c2ecf20Sopenharmony_ci{
16638c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
16648c2ecf20Sopenharmony_ci
16658c2ecf20Sopenharmony_ci	return private->vsq.logical_capacity;
16668c2ecf20Sopenharmony_ci}
16678c2ecf20Sopenharmony_ci
16688c2ecf20Sopenharmony_cistatic void dasd_eckd_ext_pool_exhaust_work(struct work_struct *work)
16698c2ecf20Sopenharmony_ci{
16708c2ecf20Sopenharmony_ci	struct ext_pool_exhaust_work_data *data;
16718c2ecf20Sopenharmony_ci	struct dasd_device *device;
16728c2ecf20Sopenharmony_ci	struct dasd_device *base;
16738c2ecf20Sopenharmony_ci
16748c2ecf20Sopenharmony_ci	data = container_of(work, struct ext_pool_exhaust_work_data, worker);
16758c2ecf20Sopenharmony_ci	device = data->device;
16768c2ecf20Sopenharmony_ci	base = data->base;
16778c2ecf20Sopenharmony_ci
16788c2ecf20Sopenharmony_ci	if (!base)
16798c2ecf20Sopenharmony_ci		base = device;
16808c2ecf20Sopenharmony_ci	if (dasd_eckd_space_configured(base) != 0) {
16818c2ecf20Sopenharmony_ci		dasd_generic_space_avail(device);
16828c2ecf20Sopenharmony_ci	} else {
16838c2ecf20Sopenharmony_ci		dev_warn(&device->cdev->dev, "No space left in the extent pool\n");
16848c2ecf20Sopenharmony_ci		DBF_DEV_EVENT(DBF_WARNING, device, "%s", "out of space");
16858c2ecf20Sopenharmony_ci	}
16868c2ecf20Sopenharmony_ci
16878c2ecf20Sopenharmony_ci	dasd_put_device(device);
16888c2ecf20Sopenharmony_ci	kfree(data);
16898c2ecf20Sopenharmony_ci}
16908c2ecf20Sopenharmony_ci
16918c2ecf20Sopenharmony_cistatic int dasd_eckd_ext_pool_exhaust(struct dasd_device *device,
16928c2ecf20Sopenharmony_ci				      struct dasd_ccw_req *cqr)
16938c2ecf20Sopenharmony_ci{
16948c2ecf20Sopenharmony_ci	struct ext_pool_exhaust_work_data *data;
16958c2ecf20Sopenharmony_ci
16968c2ecf20Sopenharmony_ci	data = kzalloc(sizeof(*data), GFP_ATOMIC);
16978c2ecf20Sopenharmony_ci	if (!data)
16988c2ecf20Sopenharmony_ci		return -ENOMEM;
16998c2ecf20Sopenharmony_ci	INIT_WORK(&data->worker, dasd_eckd_ext_pool_exhaust_work);
17008c2ecf20Sopenharmony_ci	dasd_get_device(device);
17018c2ecf20Sopenharmony_ci	data->device = device;
17028c2ecf20Sopenharmony_ci
17038c2ecf20Sopenharmony_ci	if (cqr->block)
17048c2ecf20Sopenharmony_ci		data->base = cqr->block->base;
17058c2ecf20Sopenharmony_ci	else if (cqr->basedev)
17068c2ecf20Sopenharmony_ci		data->base = cqr->basedev;
17078c2ecf20Sopenharmony_ci	else
17088c2ecf20Sopenharmony_ci		data->base = NULL;
17098c2ecf20Sopenharmony_ci
17108c2ecf20Sopenharmony_ci	schedule_work(&data->worker);
17118c2ecf20Sopenharmony_ci
17128c2ecf20Sopenharmony_ci	return 0;
17138c2ecf20Sopenharmony_ci}
17148c2ecf20Sopenharmony_ci
17158c2ecf20Sopenharmony_cistatic void dasd_eckd_cpy_ext_pool_data(struct dasd_device *device,
17168c2ecf20Sopenharmony_ci					struct dasd_rssd_lcq *lcq)
17178c2ecf20Sopenharmony_ci{
17188c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
17198c2ecf20Sopenharmony_ci	int pool_id = dasd_eckd_ext_pool_id(device);
17208c2ecf20Sopenharmony_ci	struct dasd_ext_pool_sum eps;
17218c2ecf20Sopenharmony_ci	int i;
17228c2ecf20Sopenharmony_ci
17238c2ecf20Sopenharmony_ci	for (i = 0; i < lcq->pool_count; i++) {
17248c2ecf20Sopenharmony_ci		eps = lcq->ext_pool_sum[i];
17258c2ecf20Sopenharmony_ci		if (eps.pool_id == pool_id) {
17268c2ecf20Sopenharmony_ci			memcpy(&private->eps, &eps,
17278c2ecf20Sopenharmony_ci			       sizeof(struct dasd_ext_pool_sum));
17288c2ecf20Sopenharmony_ci		}
17298c2ecf20Sopenharmony_ci	}
17308c2ecf20Sopenharmony_ci}
17318c2ecf20Sopenharmony_ci
17328c2ecf20Sopenharmony_ci/* Read Extent Pool Information - Logical Configuration Query */
17338c2ecf20Sopenharmony_cistatic int dasd_eckd_read_ext_pool_info(struct dasd_device *device)
17348c2ecf20Sopenharmony_ci{
17358c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
17368c2ecf20Sopenharmony_ci	struct dasd_psf_prssd_data *prssdp;
17378c2ecf20Sopenharmony_ci	struct dasd_rssd_lcq *lcq;
17388c2ecf20Sopenharmony_ci	struct dasd_ccw_req *cqr;
17398c2ecf20Sopenharmony_ci	struct ccw1 *ccw;
17408c2ecf20Sopenharmony_ci	int rc;
17418c2ecf20Sopenharmony_ci
17428c2ecf20Sopenharmony_ci	/* This command cannot be executed on an alias device */
17438c2ecf20Sopenharmony_ci	if (private->uid.type == UA_BASE_PAV_ALIAS ||
17448c2ecf20Sopenharmony_ci	    private->uid.type == UA_HYPER_PAV_ALIAS)
17458c2ecf20Sopenharmony_ci		return 0;
17468c2ecf20Sopenharmony_ci
17478c2ecf20Sopenharmony_ci	cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 2 /* PSF + RSSD */,
17488c2ecf20Sopenharmony_ci				   sizeof(*prssdp) + sizeof(*lcq), device, NULL);
17498c2ecf20Sopenharmony_ci	if (IS_ERR(cqr)) {
17508c2ecf20Sopenharmony_ci		DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s",
17518c2ecf20Sopenharmony_ci				"Could not allocate initialization request");
17528c2ecf20Sopenharmony_ci		return PTR_ERR(cqr);
17538c2ecf20Sopenharmony_ci	}
17548c2ecf20Sopenharmony_ci
17558c2ecf20Sopenharmony_ci	/* Prepare for Read Subsystem Data */
17568c2ecf20Sopenharmony_ci	prssdp = cqr->data;
17578c2ecf20Sopenharmony_ci	memset(prssdp, 0, sizeof(*prssdp));
17588c2ecf20Sopenharmony_ci	prssdp->order = PSF_ORDER_PRSSD;
17598c2ecf20Sopenharmony_ci	prssdp->suborder = PSF_SUBORDER_LCQ;	/* Logical Configuration Query */
17608c2ecf20Sopenharmony_ci
17618c2ecf20Sopenharmony_ci	ccw = cqr->cpaddr;
17628c2ecf20Sopenharmony_ci	ccw->cmd_code = DASD_ECKD_CCW_PSF;
17638c2ecf20Sopenharmony_ci	ccw->count = sizeof(*prssdp);
17648c2ecf20Sopenharmony_ci	ccw->flags |= CCW_FLAG_CC;
17658c2ecf20Sopenharmony_ci	ccw->cda = (__u32)(addr_t)prssdp;
17668c2ecf20Sopenharmony_ci
17678c2ecf20Sopenharmony_ci	lcq = (struct dasd_rssd_lcq *)(prssdp + 1);
17688c2ecf20Sopenharmony_ci	memset(lcq, 0, sizeof(*lcq));
17698c2ecf20Sopenharmony_ci
17708c2ecf20Sopenharmony_ci	ccw++;
17718c2ecf20Sopenharmony_ci	ccw->cmd_code = DASD_ECKD_CCW_RSSD;
17728c2ecf20Sopenharmony_ci	ccw->count = sizeof(*lcq);
17738c2ecf20Sopenharmony_ci	ccw->flags |= CCW_FLAG_SLI;
17748c2ecf20Sopenharmony_ci	ccw->cda = (__u32)(addr_t)lcq;
17758c2ecf20Sopenharmony_ci
17768c2ecf20Sopenharmony_ci	cqr->buildclk = get_tod_clock();
17778c2ecf20Sopenharmony_ci	cqr->status = DASD_CQR_FILLED;
17788c2ecf20Sopenharmony_ci	cqr->startdev = device;
17798c2ecf20Sopenharmony_ci	cqr->memdev = device;
17808c2ecf20Sopenharmony_ci	cqr->block = NULL;
17818c2ecf20Sopenharmony_ci	cqr->retries = 256;
17828c2ecf20Sopenharmony_ci	cqr->expires = device->default_expires * HZ;
17838c2ecf20Sopenharmony_ci	/* The command might not be supported. Suppress the error output */
17848c2ecf20Sopenharmony_ci	__set_bit(DASD_CQR_SUPPRESS_CR, &cqr->flags);
17858c2ecf20Sopenharmony_ci
17868c2ecf20Sopenharmony_ci	rc = dasd_sleep_on_interruptible(cqr);
17878c2ecf20Sopenharmony_ci	if (rc == 0) {
17888c2ecf20Sopenharmony_ci		dasd_eckd_cpy_ext_pool_data(device, lcq);
17898c2ecf20Sopenharmony_ci	} else {
17908c2ecf20Sopenharmony_ci		DBF_EVENT_DEVID(DBF_WARNING, device->cdev,
17918c2ecf20Sopenharmony_ci				"Reading the logical configuration failed with rc=%d", rc);
17928c2ecf20Sopenharmony_ci	}
17938c2ecf20Sopenharmony_ci
17948c2ecf20Sopenharmony_ci	dasd_sfree_request(cqr, cqr->memdev);
17958c2ecf20Sopenharmony_ci
17968c2ecf20Sopenharmony_ci	return rc;
17978c2ecf20Sopenharmony_ci}
17988c2ecf20Sopenharmony_ci
17998c2ecf20Sopenharmony_ci/*
18008c2ecf20Sopenharmony_ci * Depending on the device type, the extent size is specified either as
18018c2ecf20Sopenharmony_ci * cylinders per extent (CKD) or size per extent (FBA)
18028c2ecf20Sopenharmony_ci * A 1GB size corresponds to 1113cyl, and 16MB to 21cyl.
18038c2ecf20Sopenharmony_ci */
18048c2ecf20Sopenharmony_cistatic int dasd_eckd_ext_size(struct dasd_device *device)
18058c2ecf20Sopenharmony_ci{
18068c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
18078c2ecf20Sopenharmony_ci	struct dasd_ext_pool_sum eps = private->eps;
18088c2ecf20Sopenharmony_ci
18098c2ecf20Sopenharmony_ci	if (!eps.flags.extent_size_valid)
18108c2ecf20Sopenharmony_ci		return 0;
18118c2ecf20Sopenharmony_ci	if (eps.extent_size.size_1G)
18128c2ecf20Sopenharmony_ci		return 1113;
18138c2ecf20Sopenharmony_ci	if (eps.extent_size.size_16M)
18148c2ecf20Sopenharmony_ci		return 21;
18158c2ecf20Sopenharmony_ci
18168c2ecf20Sopenharmony_ci	return 0;
18178c2ecf20Sopenharmony_ci}
18188c2ecf20Sopenharmony_ci
18198c2ecf20Sopenharmony_cistatic int dasd_eckd_ext_pool_warn_thrshld(struct dasd_device *device)
18208c2ecf20Sopenharmony_ci{
18218c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
18228c2ecf20Sopenharmony_ci
18238c2ecf20Sopenharmony_ci	return private->eps.warn_thrshld;
18248c2ecf20Sopenharmony_ci}
18258c2ecf20Sopenharmony_ci
18268c2ecf20Sopenharmony_cistatic int dasd_eckd_ext_pool_cap_at_warnlevel(struct dasd_device *device)
18278c2ecf20Sopenharmony_ci{
18288c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
18298c2ecf20Sopenharmony_ci
18308c2ecf20Sopenharmony_ci	return private->eps.flags.capacity_at_warnlevel;
18318c2ecf20Sopenharmony_ci}
18328c2ecf20Sopenharmony_ci
18338c2ecf20Sopenharmony_ci/*
18348c2ecf20Sopenharmony_ci * Extent Pool out of space
18358c2ecf20Sopenharmony_ci */
18368c2ecf20Sopenharmony_cistatic int dasd_eckd_ext_pool_oos(struct dasd_device *device)
18378c2ecf20Sopenharmony_ci{
18388c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
18398c2ecf20Sopenharmony_ci
18408c2ecf20Sopenharmony_ci	return private->eps.flags.pool_oos;
18418c2ecf20Sopenharmony_ci}
18428c2ecf20Sopenharmony_ci
18438c2ecf20Sopenharmony_ci/*
18448c2ecf20Sopenharmony_ci * Build CP for Perform Subsystem Function - SSC.
18458c2ecf20Sopenharmony_ci */
18468c2ecf20Sopenharmony_cistatic struct dasd_ccw_req *dasd_eckd_build_psf_ssc(struct dasd_device *device,
18478c2ecf20Sopenharmony_ci						    int enable_pav)
18488c2ecf20Sopenharmony_ci{
18498c2ecf20Sopenharmony_ci	struct dasd_ccw_req *cqr;
18508c2ecf20Sopenharmony_ci	struct dasd_psf_ssc_data *psf_ssc_data;
18518c2ecf20Sopenharmony_ci	struct ccw1 *ccw;
18528c2ecf20Sopenharmony_ci
18538c2ecf20Sopenharmony_ci	cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */ ,
18548c2ecf20Sopenharmony_ci				  sizeof(struct dasd_psf_ssc_data),
18558c2ecf20Sopenharmony_ci				   device, NULL);
18568c2ecf20Sopenharmony_ci
18578c2ecf20Sopenharmony_ci	if (IS_ERR(cqr)) {
18588c2ecf20Sopenharmony_ci		DBF_DEV_EVENT(DBF_WARNING, device, "%s",
18598c2ecf20Sopenharmony_ci			   "Could not allocate PSF-SSC request");
18608c2ecf20Sopenharmony_ci		return cqr;
18618c2ecf20Sopenharmony_ci	}
18628c2ecf20Sopenharmony_ci	psf_ssc_data = (struct dasd_psf_ssc_data *)cqr->data;
18638c2ecf20Sopenharmony_ci	psf_ssc_data->order = PSF_ORDER_SSC;
18648c2ecf20Sopenharmony_ci	psf_ssc_data->suborder = 0xc0;
18658c2ecf20Sopenharmony_ci	if (enable_pav) {
18668c2ecf20Sopenharmony_ci		psf_ssc_data->suborder |= 0x08;
18678c2ecf20Sopenharmony_ci		psf_ssc_data->reserved[0] = 0x88;
18688c2ecf20Sopenharmony_ci	}
18698c2ecf20Sopenharmony_ci	ccw = cqr->cpaddr;
18708c2ecf20Sopenharmony_ci	ccw->cmd_code = DASD_ECKD_CCW_PSF;
18718c2ecf20Sopenharmony_ci	ccw->cda = (__u32)(addr_t)psf_ssc_data;
18728c2ecf20Sopenharmony_ci	ccw->count = 66;
18738c2ecf20Sopenharmony_ci
18748c2ecf20Sopenharmony_ci	cqr->startdev = device;
18758c2ecf20Sopenharmony_ci	cqr->memdev = device;
18768c2ecf20Sopenharmony_ci	cqr->block = NULL;
18778c2ecf20Sopenharmony_ci	cqr->retries = 256;
18788c2ecf20Sopenharmony_ci	cqr->expires = 10*HZ;
18798c2ecf20Sopenharmony_ci	cqr->buildclk = get_tod_clock();
18808c2ecf20Sopenharmony_ci	cqr->status = DASD_CQR_FILLED;
18818c2ecf20Sopenharmony_ci	return cqr;
18828c2ecf20Sopenharmony_ci}
18838c2ecf20Sopenharmony_ci
18848c2ecf20Sopenharmony_ci/*
18858c2ecf20Sopenharmony_ci * Perform Subsystem Function.
18868c2ecf20Sopenharmony_ci * It is necessary to trigger CIO for channel revalidation since this
18878c2ecf20Sopenharmony_ci * call might change behaviour of DASD devices.
18888c2ecf20Sopenharmony_ci */
18898c2ecf20Sopenharmony_cistatic int
18908c2ecf20Sopenharmony_cidasd_eckd_psf_ssc(struct dasd_device *device, int enable_pav,
18918c2ecf20Sopenharmony_ci		  unsigned long flags)
18928c2ecf20Sopenharmony_ci{
18938c2ecf20Sopenharmony_ci	struct dasd_ccw_req *cqr;
18948c2ecf20Sopenharmony_ci	int rc;
18958c2ecf20Sopenharmony_ci
18968c2ecf20Sopenharmony_ci	cqr = dasd_eckd_build_psf_ssc(device, enable_pav);
18978c2ecf20Sopenharmony_ci	if (IS_ERR(cqr))
18988c2ecf20Sopenharmony_ci		return PTR_ERR(cqr);
18998c2ecf20Sopenharmony_ci
19008c2ecf20Sopenharmony_ci	/*
19018c2ecf20Sopenharmony_ci	 * set flags e.g. turn on failfast, to prevent blocking
19028c2ecf20Sopenharmony_ci	 * the calling function should handle failed requests
19038c2ecf20Sopenharmony_ci	 */
19048c2ecf20Sopenharmony_ci	cqr->flags |= flags;
19058c2ecf20Sopenharmony_ci
19068c2ecf20Sopenharmony_ci	rc = dasd_sleep_on(cqr);
19078c2ecf20Sopenharmony_ci	if (!rc)
19088c2ecf20Sopenharmony_ci		/* trigger CIO to reprobe devices */
19098c2ecf20Sopenharmony_ci		css_schedule_reprobe();
19108c2ecf20Sopenharmony_ci	else if (cqr->intrc == -EAGAIN)
19118c2ecf20Sopenharmony_ci		rc = -EAGAIN;
19128c2ecf20Sopenharmony_ci
19138c2ecf20Sopenharmony_ci	dasd_sfree_request(cqr, cqr->memdev);
19148c2ecf20Sopenharmony_ci	return rc;
19158c2ecf20Sopenharmony_ci}
19168c2ecf20Sopenharmony_ci
19178c2ecf20Sopenharmony_ci/*
19188c2ecf20Sopenharmony_ci * Valide storage server of current device.
19198c2ecf20Sopenharmony_ci */
19208c2ecf20Sopenharmony_cistatic int dasd_eckd_validate_server(struct dasd_device *device,
19218c2ecf20Sopenharmony_ci				     unsigned long flags)
19228c2ecf20Sopenharmony_ci{
19238c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
19248c2ecf20Sopenharmony_ci	int enable_pav, rc;
19258c2ecf20Sopenharmony_ci
19268c2ecf20Sopenharmony_ci	if (private->uid.type == UA_BASE_PAV_ALIAS ||
19278c2ecf20Sopenharmony_ci	    private->uid.type == UA_HYPER_PAV_ALIAS)
19288c2ecf20Sopenharmony_ci		return 0;
19298c2ecf20Sopenharmony_ci	if (dasd_nopav || MACHINE_IS_VM)
19308c2ecf20Sopenharmony_ci		enable_pav = 0;
19318c2ecf20Sopenharmony_ci	else
19328c2ecf20Sopenharmony_ci		enable_pav = 1;
19338c2ecf20Sopenharmony_ci	rc = dasd_eckd_psf_ssc(device, enable_pav, flags);
19348c2ecf20Sopenharmony_ci
19358c2ecf20Sopenharmony_ci	/* may be requested feature is not available on server,
19368c2ecf20Sopenharmony_ci	 * therefore just report error and go ahead */
19378c2ecf20Sopenharmony_ci	DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "PSF-SSC for SSID %04x "
19388c2ecf20Sopenharmony_ci			"returned rc=%d", private->uid.ssid, rc);
19398c2ecf20Sopenharmony_ci	return rc;
19408c2ecf20Sopenharmony_ci}
19418c2ecf20Sopenharmony_ci
19428c2ecf20Sopenharmony_ci/*
19438c2ecf20Sopenharmony_ci * worker to do a validate server in case of a lost pathgroup
19448c2ecf20Sopenharmony_ci */
19458c2ecf20Sopenharmony_cistatic void dasd_eckd_do_validate_server(struct work_struct *work)
19468c2ecf20Sopenharmony_ci{
19478c2ecf20Sopenharmony_ci	struct dasd_device *device = container_of(work, struct dasd_device,
19488c2ecf20Sopenharmony_ci						  kick_validate);
19498c2ecf20Sopenharmony_ci	unsigned long flags = 0;
19508c2ecf20Sopenharmony_ci
19518c2ecf20Sopenharmony_ci	set_bit(DASD_CQR_FLAGS_FAILFAST, &flags);
19528c2ecf20Sopenharmony_ci	if (dasd_eckd_validate_server(device, flags)
19538c2ecf20Sopenharmony_ci	    == -EAGAIN) {
19548c2ecf20Sopenharmony_ci		/* schedule worker again if failed */
19558c2ecf20Sopenharmony_ci		schedule_work(&device->kick_validate);
19568c2ecf20Sopenharmony_ci		return;
19578c2ecf20Sopenharmony_ci	}
19588c2ecf20Sopenharmony_ci
19598c2ecf20Sopenharmony_ci	dasd_put_device(device);
19608c2ecf20Sopenharmony_ci}
19618c2ecf20Sopenharmony_ci
19628c2ecf20Sopenharmony_cistatic void dasd_eckd_kick_validate_server(struct dasd_device *device)
19638c2ecf20Sopenharmony_ci{
19648c2ecf20Sopenharmony_ci	dasd_get_device(device);
19658c2ecf20Sopenharmony_ci	/* exit if device not online or in offline processing */
19668c2ecf20Sopenharmony_ci	if (test_bit(DASD_FLAG_OFFLINE, &device->flags) ||
19678c2ecf20Sopenharmony_ci	   device->state < DASD_STATE_ONLINE) {
19688c2ecf20Sopenharmony_ci		dasd_put_device(device);
19698c2ecf20Sopenharmony_ci		return;
19708c2ecf20Sopenharmony_ci	}
19718c2ecf20Sopenharmony_ci	/* queue call to do_validate_server to the kernel event daemon. */
19728c2ecf20Sopenharmony_ci	if (!schedule_work(&device->kick_validate))
19738c2ecf20Sopenharmony_ci		dasd_put_device(device);
19748c2ecf20Sopenharmony_ci}
19758c2ecf20Sopenharmony_ci
19768c2ecf20Sopenharmony_ci/*
19778c2ecf20Sopenharmony_ci * Check device characteristics.
19788c2ecf20Sopenharmony_ci * If the device is accessible using ECKD discipline, the device is enabled.
19798c2ecf20Sopenharmony_ci */
19808c2ecf20Sopenharmony_cistatic int
19818c2ecf20Sopenharmony_cidasd_eckd_check_characteristics(struct dasd_device *device)
19828c2ecf20Sopenharmony_ci{
19838c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
19848c2ecf20Sopenharmony_ci	struct dasd_block *block;
19858c2ecf20Sopenharmony_ci	struct dasd_uid temp_uid;
19868c2ecf20Sopenharmony_ci	int rc, i;
19878c2ecf20Sopenharmony_ci	int readonly;
19888c2ecf20Sopenharmony_ci	unsigned long value;
19898c2ecf20Sopenharmony_ci
19908c2ecf20Sopenharmony_ci	/* setup work queue for validate server*/
19918c2ecf20Sopenharmony_ci	INIT_WORK(&device->kick_validate, dasd_eckd_do_validate_server);
19928c2ecf20Sopenharmony_ci	/* setup work queue for summary unit check */
19938c2ecf20Sopenharmony_ci	INIT_WORK(&device->suc_work, dasd_alias_handle_summary_unit_check);
19948c2ecf20Sopenharmony_ci
19958c2ecf20Sopenharmony_ci	if (!ccw_device_is_pathgroup(device->cdev)) {
19968c2ecf20Sopenharmony_ci		dev_warn(&device->cdev->dev,
19978c2ecf20Sopenharmony_ci			 "A channel path group could not be established\n");
19988c2ecf20Sopenharmony_ci		return -EIO;
19998c2ecf20Sopenharmony_ci	}
20008c2ecf20Sopenharmony_ci	if (!ccw_device_is_multipath(device->cdev)) {
20018c2ecf20Sopenharmony_ci		dev_info(&device->cdev->dev,
20028c2ecf20Sopenharmony_ci			 "The DASD is not operating in multipath mode\n");
20038c2ecf20Sopenharmony_ci	}
20048c2ecf20Sopenharmony_ci	if (!private) {
20058c2ecf20Sopenharmony_ci		private = kzalloc(sizeof(*private), GFP_KERNEL | GFP_DMA);
20068c2ecf20Sopenharmony_ci		if (!private) {
20078c2ecf20Sopenharmony_ci			dev_warn(&device->cdev->dev,
20088c2ecf20Sopenharmony_ci				 "Allocating memory for private DASD data "
20098c2ecf20Sopenharmony_ci				 "failed\n");
20108c2ecf20Sopenharmony_ci			return -ENOMEM;
20118c2ecf20Sopenharmony_ci		}
20128c2ecf20Sopenharmony_ci		device->private = private;
20138c2ecf20Sopenharmony_ci	} else {
20148c2ecf20Sopenharmony_ci		memset(private, 0, sizeof(*private));
20158c2ecf20Sopenharmony_ci	}
20168c2ecf20Sopenharmony_ci	/* Invalidate status of initial analysis. */
20178c2ecf20Sopenharmony_ci	private->init_cqr_status = -1;
20188c2ecf20Sopenharmony_ci	/* Set default cache operations. */
20198c2ecf20Sopenharmony_ci	private->attrib.operation = DASD_NORMAL_CACHE;
20208c2ecf20Sopenharmony_ci	private->attrib.nr_cyl = 0;
20218c2ecf20Sopenharmony_ci
20228c2ecf20Sopenharmony_ci	/* Read Configuration Data */
20238c2ecf20Sopenharmony_ci	rc = dasd_eckd_read_conf(device);
20248c2ecf20Sopenharmony_ci	if (rc)
20258c2ecf20Sopenharmony_ci		goto out_err1;
20268c2ecf20Sopenharmony_ci
20278c2ecf20Sopenharmony_ci	/* set some default values */
20288c2ecf20Sopenharmony_ci	device->default_expires = DASD_EXPIRES;
20298c2ecf20Sopenharmony_ci	device->default_retries = DASD_RETRIES;
20308c2ecf20Sopenharmony_ci	device->path_thrhld = DASD_ECKD_PATH_THRHLD;
20318c2ecf20Sopenharmony_ci	device->path_interval = DASD_ECKD_PATH_INTERVAL;
20328c2ecf20Sopenharmony_ci
20338c2ecf20Sopenharmony_ci	if (private->gneq) {
20348c2ecf20Sopenharmony_ci		value = 1;
20358c2ecf20Sopenharmony_ci		for (i = 0; i < private->gneq->timeout.value; i++)
20368c2ecf20Sopenharmony_ci			value = 10 * value;
20378c2ecf20Sopenharmony_ci		value = value * private->gneq->timeout.number;
20388c2ecf20Sopenharmony_ci		/* do not accept useless values */
20398c2ecf20Sopenharmony_ci		if (value != 0 && value <= DASD_EXPIRES_MAX)
20408c2ecf20Sopenharmony_ci			device->default_expires = value;
20418c2ecf20Sopenharmony_ci	}
20428c2ecf20Sopenharmony_ci
20438c2ecf20Sopenharmony_ci	dasd_eckd_get_uid(device, &temp_uid);
20448c2ecf20Sopenharmony_ci	if (temp_uid.type == UA_BASE_DEVICE) {
20458c2ecf20Sopenharmony_ci		block = dasd_alloc_block();
20468c2ecf20Sopenharmony_ci		if (IS_ERR(block)) {
20478c2ecf20Sopenharmony_ci			DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s",
20488c2ecf20Sopenharmony_ci					"could not allocate dasd "
20498c2ecf20Sopenharmony_ci					"block structure");
20508c2ecf20Sopenharmony_ci			rc = PTR_ERR(block);
20518c2ecf20Sopenharmony_ci			goto out_err1;
20528c2ecf20Sopenharmony_ci		}
20538c2ecf20Sopenharmony_ci		device->block = block;
20548c2ecf20Sopenharmony_ci		block->base = device;
20558c2ecf20Sopenharmony_ci	}
20568c2ecf20Sopenharmony_ci
20578c2ecf20Sopenharmony_ci	/* register lcu with alias handling, enable PAV */
20588c2ecf20Sopenharmony_ci	rc = dasd_alias_make_device_known_to_lcu(device);
20598c2ecf20Sopenharmony_ci	if (rc)
20608c2ecf20Sopenharmony_ci		goto out_err2;
20618c2ecf20Sopenharmony_ci
20628c2ecf20Sopenharmony_ci	dasd_eckd_validate_server(device, 0);
20638c2ecf20Sopenharmony_ci
20648c2ecf20Sopenharmony_ci	/* device may report different configuration data after LCU setup */
20658c2ecf20Sopenharmony_ci	rc = dasd_eckd_read_conf(device);
20668c2ecf20Sopenharmony_ci	if (rc)
20678c2ecf20Sopenharmony_ci		goto out_err3;
20688c2ecf20Sopenharmony_ci
20698c2ecf20Sopenharmony_ci	/* Read Feature Codes */
20708c2ecf20Sopenharmony_ci	dasd_eckd_read_features(device);
20718c2ecf20Sopenharmony_ci
20728c2ecf20Sopenharmony_ci	/* Read Volume Information */
20738c2ecf20Sopenharmony_ci	dasd_eckd_read_vol_info(device);
20748c2ecf20Sopenharmony_ci
20758c2ecf20Sopenharmony_ci	/* Read Extent Pool Information */
20768c2ecf20Sopenharmony_ci	dasd_eckd_read_ext_pool_info(device);
20778c2ecf20Sopenharmony_ci
20788c2ecf20Sopenharmony_ci	/* Read Device Characteristics */
20798c2ecf20Sopenharmony_ci	rc = dasd_generic_read_dev_chars(device, DASD_ECKD_MAGIC,
20808c2ecf20Sopenharmony_ci					 &private->rdc_data, 64);
20818c2ecf20Sopenharmony_ci	if (rc) {
20828c2ecf20Sopenharmony_ci		DBF_EVENT_DEVID(DBF_WARNING, device->cdev,
20838c2ecf20Sopenharmony_ci				"Read device characteristic failed, rc=%d", rc);
20848c2ecf20Sopenharmony_ci		goto out_err3;
20858c2ecf20Sopenharmony_ci	}
20868c2ecf20Sopenharmony_ci
20878c2ecf20Sopenharmony_ci	if ((device->features & DASD_FEATURE_USERAW) &&
20888c2ecf20Sopenharmony_ci	    !(private->rdc_data.facilities.RT_in_LR)) {
20898c2ecf20Sopenharmony_ci		dev_err(&device->cdev->dev, "The storage server does not "
20908c2ecf20Sopenharmony_ci			"support raw-track access\n");
20918c2ecf20Sopenharmony_ci		rc = -EINVAL;
20928c2ecf20Sopenharmony_ci		goto out_err3;
20938c2ecf20Sopenharmony_ci	}
20948c2ecf20Sopenharmony_ci
20958c2ecf20Sopenharmony_ci	/* find the valid cylinder size */
20968c2ecf20Sopenharmony_ci	if (private->rdc_data.no_cyl == LV_COMPAT_CYL &&
20978c2ecf20Sopenharmony_ci	    private->rdc_data.long_no_cyl)
20988c2ecf20Sopenharmony_ci		private->real_cyl = private->rdc_data.long_no_cyl;
20998c2ecf20Sopenharmony_ci	else
21008c2ecf20Sopenharmony_ci		private->real_cyl = private->rdc_data.no_cyl;
21018c2ecf20Sopenharmony_ci
21028c2ecf20Sopenharmony_ci	private->fcx_max_data = get_fcx_max_data(device);
21038c2ecf20Sopenharmony_ci
21048c2ecf20Sopenharmony_ci	readonly = dasd_device_is_ro(device);
21058c2ecf20Sopenharmony_ci	if (readonly)
21068c2ecf20Sopenharmony_ci		set_bit(DASD_FLAG_DEVICE_RO, &device->flags);
21078c2ecf20Sopenharmony_ci
21088c2ecf20Sopenharmony_ci	dev_info(&device->cdev->dev, "New DASD %04X/%02X (CU %04X/%02X) "
21098c2ecf20Sopenharmony_ci		 "with %d cylinders, %d heads, %d sectors%s\n",
21108c2ecf20Sopenharmony_ci		 private->rdc_data.dev_type,
21118c2ecf20Sopenharmony_ci		 private->rdc_data.dev_model,
21128c2ecf20Sopenharmony_ci		 private->rdc_data.cu_type,
21138c2ecf20Sopenharmony_ci		 private->rdc_data.cu_model.model,
21148c2ecf20Sopenharmony_ci		 private->real_cyl,
21158c2ecf20Sopenharmony_ci		 private->rdc_data.trk_per_cyl,
21168c2ecf20Sopenharmony_ci		 private->rdc_data.sec_per_trk,
21178c2ecf20Sopenharmony_ci		 readonly ? ", read-only device" : "");
21188c2ecf20Sopenharmony_ci	return 0;
21198c2ecf20Sopenharmony_ci
21208c2ecf20Sopenharmony_ciout_err3:
21218c2ecf20Sopenharmony_ci	dasd_alias_disconnect_device_from_lcu(device);
21228c2ecf20Sopenharmony_ciout_err2:
21238c2ecf20Sopenharmony_ci	dasd_free_block(device->block);
21248c2ecf20Sopenharmony_ci	device->block = NULL;
21258c2ecf20Sopenharmony_ciout_err1:
21268c2ecf20Sopenharmony_ci	dasd_eckd_clear_conf_data(device);
21278c2ecf20Sopenharmony_ci	kfree(device->private);
21288c2ecf20Sopenharmony_ci	device->private = NULL;
21298c2ecf20Sopenharmony_ci	return rc;
21308c2ecf20Sopenharmony_ci}
21318c2ecf20Sopenharmony_ci
21328c2ecf20Sopenharmony_cistatic void dasd_eckd_uncheck_device(struct dasd_device *device)
21338c2ecf20Sopenharmony_ci{
21348c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
21358c2ecf20Sopenharmony_ci
21368c2ecf20Sopenharmony_ci	if (!private)
21378c2ecf20Sopenharmony_ci		return;
21388c2ecf20Sopenharmony_ci
21398c2ecf20Sopenharmony_ci	dasd_alias_disconnect_device_from_lcu(device);
21408c2ecf20Sopenharmony_ci	private->ned = NULL;
21418c2ecf20Sopenharmony_ci	private->sneq = NULL;
21428c2ecf20Sopenharmony_ci	private->vdsneq = NULL;
21438c2ecf20Sopenharmony_ci	private->gneq = NULL;
21448c2ecf20Sopenharmony_ci	dasd_eckd_clear_conf_data(device);
21458c2ecf20Sopenharmony_ci}
21468c2ecf20Sopenharmony_ci
21478c2ecf20Sopenharmony_cistatic struct dasd_ccw_req *
21488c2ecf20Sopenharmony_cidasd_eckd_analysis_ccw(struct dasd_device *device)
21498c2ecf20Sopenharmony_ci{
21508c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
21518c2ecf20Sopenharmony_ci	struct eckd_count *count_data;
21528c2ecf20Sopenharmony_ci	struct LO_eckd_data *LO_data;
21538c2ecf20Sopenharmony_ci	struct dasd_ccw_req *cqr;
21548c2ecf20Sopenharmony_ci	struct ccw1 *ccw;
21558c2ecf20Sopenharmony_ci	int cplength, datasize;
21568c2ecf20Sopenharmony_ci	int i;
21578c2ecf20Sopenharmony_ci
21588c2ecf20Sopenharmony_ci	cplength = 8;
21598c2ecf20Sopenharmony_ci	datasize = sizeof(struct DE_eckd_data) + 2*sizeof(struct LO_eckd_data);
21608c2ecf20Sopenharmony_ci	cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, cplength, datasize, device,
21618c2ecf20Sopenharmony_ci				   NULL);
21628c2ecf20Sopenharmony_ci	if (IS_ERR(cqr))
21638c2ecf20Sopenharmony_ci		return cqr;
21648c2ecf20Sopenharmony_ci	ccw = cqr->cpaddr;
21658c2ecf20Sopenharmony_ci	/* Define extent for the first 2 tracks. */
21668c2ecf20Sopenharmony_ci	define_extent(ccw++, cqr->data, 0, 1,
21678c2ecf20Sopenharmony_ci		      DASD_ECKD_CCW_READ_COUNT, device, 0);
21688c2ecf20Sopenharmony_ci	LO_data = cqr->data + sizeof(struct DE_eckd_data);
21698c2ecf20Sopenharmony_ci	/* Locate record for the first 4 records on track 0. */
21708c2ecf20Sopenharmony_ci	ccw[-1].flags |= CCW_FLAG_CC;
21718c2ecf20Sopenharmony_ci	locate_record(ccw++, LO_data++, 0, 0, 4,
21728c2ecf20Sopenharmony_ci		      DASD_ECKD_CCW_READ_COUNT, device, 0);
21738c2ecf20Sopenharmony_ci
21748c2ecf20Sopenharmony_ci	count_data = private->count_area;
21758c2ecf20Sopenharmony_ci	for (i = 0; i < 4; i++) {
21768c2ecf20Sopenharmony_ci		ccw[-1].flags |= CCW_FLAG_CC;
21778c2ecf20Sopenharmony_ci		ccw->cmd_code = DASD_ECKD_CCW_READ_COUNT;
21788c2ecf20Sopenharmony_ci		ccw->flags = 0;
21798c2ecf20Sopenharmony_ci		ccw->count = 8;
21808c2ecf20Sopenharmony_ci		ccw->cda = (__u32)(addr_t) count_data;
21818c2ecf20Sopenharmony_ci		ccw++;
21828c2ecf20Sopenharmony_ci		count_data++;
21838c2ecf20Sopenharmony_ci	}
21848c2ecf20Sopenharmony_ci
21858c2ecf20Sopenharmony_ci	/* Locate record for the first record on track 1. */
21868c2ecf20Sopenharmony_ci	ccw[-1].flags |= CCW_FLAG_CC;
21878c2ecf20Sopenharmony_ci	locate_record(ccw++, LO_data++, 1, 0, 1,
21888c2ecf20Sopenharmony_ci		      DASD_ECKD_CCW_READ_COUNT, device, 0);
21898c2ecf20Sopenharmony_ci	/* Read count ccw. */
21908c2ecf20Sopenharmony_ci	ccw[-1].flags |= CCW_FLAG_CC;
21918c2ecf20Sopenharmony_ci	ccw->cmd_code = DASD_ECKD_CCW_READ_COUNT;
21928c2ecf20Sopenharmony_ci	ccw->flags = 0;
21938c2ecf20Sopenharmony_ci	ccw->count = 8;
21948c2ecf20Sopenharmony_ci	ccw->cda = (__u32)(addr_t) count_data;
21958c2ecf20Sopenharmony_ci
21968c2ecf20Sopenharmony_ci	cqr->block = NULL;
21978c2ecf20Sopenharmony_ci	cqr->startdev = device;
21988c2ecf20Sopenharmony_ci	cqr->memdev = device;
21998c2ecf20Sopenharmony_ci	cqr->retries = 255;
22008c2ecf20Sopenharmony_ci	cqr->buildclk = get_tod_clock();
22018c2ecf20Sopenharmony_ci	cqr->status = DASD_CQR_FILLED;
22028c2ecf20Sopenharmony_ci	/* Set flags to suppress output for expected errors */
22038c2ecf20Sopenharmony_ci	set_bit(DASD_CQR_SUPPRESS_NRF, &cqr->flags);
22048c2ecf20Sopenharmony_ci
22058c2ecf20Sopenharmony_ci	return cqr;
22068c2ecf20Sopenharmony_ci}
22078c2ecf20Sopenharmony_ci
22088c2ecf20Sopenharmony_ci/* differentiate between 'no record found' and any other error */
22098c2ecf20Sopenharmony_cistatic int dasd_eckd_analysis_evaluation(struct dasd_ccw_req *init_cqr)
22108c2ecf20Sopenharmony_ci{
22118c2ecf20Sopenharmony_ci	char *sense;
22128c2ecf20Sopenharmony_ci	if (init_cqr->status == DASD_CQR_DONE)
22138c2ecf20Sopenharmony_ci		return INIT_CQR_OK;
22148c2ecf20Sopenharmony_ci	else if (init_cqr->status == DASD_CQR_NEED_ERP ||
22158c2ecf20Sopenharmony_ci		 init_cqr->status == DASD_CQR_FAILED) {
22168c2ecf20Sopenharmony_ci		sense = dasd_get_sense(&init_cqr->irb);
22178c2ecf20Sopenharmony_ci		if (sense && (sense[1] & SNS1_NO_REC_FOUND))
22188c2ecf20Sopenharmony_ci			return INIT_CQR_UNFORMATTED;
22198c2ecf20Sopenharmony_ci		else
22208c2ecf20Sopenharmony_ci			return INIT_CQR_ERROR;
22218c2ecf20Sopenharmony_ci	} else
22228c2ecf20Sopenharmony_ci		return INIT_CQR_ERROR;
22238c2ecf20Sopenharmony_ci}
22248c2ecf20Sopenharmony_ci
22258c2ecf20Sopenharmony_ci/*
22268c2ecf20Sopenharmony_ci * This is the callback function for the init_analysis cqr. It saves
22278c2ecf20Sopenharmony_ci * the status of the initial analysis ccw before it frees it and kicks
22288c2ecf20Sopenharmony_ci * the device to continue the startup sequence. This will call
22298c2ecf20Sopenharmony_ci * dasd_eckd_do_analysis again (if the devices has not been marked
22308c2ecf20Sopenharmony_ci * for deletion in the meantime).
22318c2ecf20Sopenharmony_ci */
22328c2ecf20Sopenharmony_cistatic void dasd_eckd_analysis_callback(struct dasd_ccw_req *init_cqr,
22338c2ecf20Sopenharmony_ci					void *data)
22348c2ecf20Sopenharmony_ci{
22358c2ecf20Sopenharmony_ci	struct dasd_device *device = init_cqr->startdev;
22368c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
22378c2ecf20Sopenharmony_ci
22388c2ecf20Sopenharmony_ci	private->init_cqr_status = dasd_eckd_analysis_evaluation(init_cqr);
22398c2ecf20Sopenharmony_ci	dasd_sfree_request(init_cqr, device);
22408c2ecf20Sopenharmony_ci	dasd_kick_device(device);
22418c2ecf20Sopenharmony_ci}
22428c2ecf20Sopenharmony_ci
22438c2ecf20Sopenharmony_cistatic int dasd_eckd_start_analysis(struct dasd_block *block)
22448c2ecf20Sopenharmony_ci{
22458c2ecf20Sopenharmony_ci	struct dasd_ccw_req *init_cqr;
22468c2ecf20Sopenharmony_ci
22478c2ecf20Sopenharmony_ci	init_cqr = dasd_eckd_analysis_ccw(block->base);
22488c2ecf20Sopenharmony_ci	if (IS_ERR(init_cqr))
22498c2ecf20Sopenharmony_ci		return PTR_ERR(init_cqr);
22508c2ecf20Sopenharmony_ci	init_cqr->callback = dasd_eckd_analysis_callback;
22518c2ecf20Sopenharmony_ci	init_cqr->callback_data = NULL;
22528c2ecf20Sopenharmony_ci	init_cqr->expires = 5*HZ;
22538c2ecf20Sopenharmony_ci	/* first try without ERP, so we can later handle unformatted
22548c2ecf20Sopenharmony_ci	 * devices as special case
22558c2ecf20Sopenharmony_ci	 */
22568c2ecf20Sopenharmony_ci	clear_bit(DASD_CQR_FLAGS_USE_ERP, &init_cqr->flags);
22578c2ecf20Sopenharmony_ci	init_cqr->retries = 0;
22588c2ecf20Sopenharmony_ci	dasd_add_request_head(init_cqr);
22598c2ecf20Sopenharmony_ci	return -EAGAIN;
22608c2ecf20Sopenharmony_ci}
22618c2ecf20Sopenharmony_ci
22628c2ecf20Sopenharmony_cistatic int dasd_eckd_end_analysis(struct dasd_block *block)
22638c2ecf20Sopenharmony_ci{
22648c2ecf20Sopenharmony_ci	struct dasd_device *device = block->base;
22658c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
22668c2ecf20Sopenharmony_ci	struct eckd_count *count_area;
22678c2ecf20Sopenharmony_ci	unsigned int sb, blk_per_trk;
22688c2ecf20Sopenharmony_ci	int status, i;
22698c2ecf20Sopenharmony_ci	struct dasd_ccw_req *init_cqr;
22708c2ecf20Sopenharmony_ci
22718c2ecf20Sopenharmony_ci	status = private->init_cqr_status;
22728c2ecf20Sopenharmony_ci	private->init_cqr_status = -1;
22738c2ecf20Sopenharmony_ci	if (status == INIT_CQR_ERROR) {
22748c2ecf20Sopenharmony_ci		/* try again, this time with full ERP */
22758c2ecf20Sopenharmony_ci		init_cqr = dasd_eckd_analysis_ccw(device);
22768c2ecf20Sopenharmony_ci		dasd_sleep_on(init_cqr);
22778c2ecf20Sopenharmony_ci		status = dasd_eckd_analysis_evaluation(init_cqr);
22788c2ecf20Sopenharmony_ci		dasd_sfree_request(init_cqr, device);
22798c2ecf20Sopenharmony_ci	}
22808c2ecf20Sopenharmony_ci
22818c2ecf20Sopenharmony_ci	if (device->features & DASD_FEATURE_USERAW) {
22828c2ecf20Sopenharmony_ci		block->bp_block = DASD_RAW_BLOCKSIZE;
22838c2ecf20Sopenharmony_ci		blk_per_trk = DASD_RAW_BLOCK_PER_TRACK;
22848c2ecf20Sopenharmony_ci		block->s2b_shift = 3;
22858c2ecf20Sopenharmony_ci		goto raw;
22868c2ecf20Sopenharmony_ci	}
22878c2ecf20Sopenharmony_ci
22888c2ecf20Sopenharmony_ci	if (status == INIT_CQR_UNFORMATTED) {
22898c2ecf20Sopenharmony_ci		dev_warn(&device->cdev->dev, "The DASD is not formatted\n");
22908c2ecf20Sopenharmony_ci		return -EMEDIUMTYPE;
22918c2ecf20Sopenharmony_ci	} else if (status == INIT_CQR_ERROR) {
22928c2ecf20Sopenharmony_ci		dev_err(&device->cdev->dev,
22938c2ecf20Sopenharmony_ci			"Detecting the DASD disk layout failed because "
22948c2ecf20Sopenharmony_ci			"of an I/O error\n");
22958c2ecf20Sopenharmony_ci		return -EIO;
22968c2ecf20Sopenharmony_ci	}
22978c2ecf20Sopenharmony_ci
22988c2ecf20Sopenharmony_ci	private->uses_cdl = 1;
22998c2ecf20Sopenharmony_ci	/* Check Track 0 for Compatible Disk Layout */
23008c2ecf20Sopenharmony_ci	count_area = NULL;
23018c2ecf20Sopenharmony_ci	for (i = 0; i < 3; i++) {
23028c2ecf20Sopenharmony_ci		if (private->count_area[i].kl != 4 ||
23038c2ecf20Sopenharmony_ci		    private->count_area[i].dl != dasd_eckd_cdl_reclen(i) - 4 ||
23048c2ecf20Sopenharmony_ci		    private->count_area[i].cyl != 0 ||
23058c2ecf20Sopenharmony_ci		    private->count_area[i].head != count_area_head[i] ||
23068c2ecf20Sopenharmony_ci		    private->count_area[i].record != count_area_rec[i]) {
23078c2ecf20Sopenharmony_ci			private->uses_cdl = 0;
23088c2ecf20Sopenharmony_ci			break;
23098c2ecf20Sopenharmony_ci		}
23108c2ecf20Sopenharmony_ci	}
23118c2ecf20Sopenharmony_ci	if (i == 3)
23128c2ecf20Sopenharmony_ci		count_area = &private->count_area[3];
23138c2ecf20Sopenharmony_ci
23148c2ecf20Sopenharmony_ci	if (private->uses_cdl == 0) {
23158c2ecf20Sopenharmony_ci		for (i = 0; i < 5; i++) {
23168c2ecf20Sopenharmony_ci			if ((private->count_area[i].kl != 0) ||
23178c2ecf20Sopenharmony_ci			    (private->count_area[i].dl !=
23188c2ecf20Sopenharmony_ci			     private->count_area[0].dl) ||
23198c2ecf20Sopenharmony_ci			    private->count_area[i].cyl !=  0 ||
23208c2ecf20Sopenharmony_ci			    private->count_area[i].head != count_area_head[i] ||
23218c2ecf20Sopenharmony_ci			    private->count_area[i].record != count_area_rec[i])
23228c2ecf20Sopenharmony_ci				break;
23238c2ecf20Sopenharmony_ci		}
23248c2ecf20Sopenharmony_ci		if (i == 5)
23258c2ecf20Sopenharmony_ci			count_area = &private->count_area[0];
23268c2ecf20Sopenharmony_ci	} else {
23278c2ecf20Sopenharmony_ci		if (private->count_area[3].record == 1)
23288c2ecf20Sopenharmony_ci			dev_warn(&device->cdev->dev,
23298c2ecf20Sopenharmony_ci				 "Track 0 has no records following the VTOC\n");
23308c2ecf20Sopenharmony_ci	}
23318c2ecf20Sopenharmony_ci
23328c2ecf20Sopenharmony_ci	if (count_area != NULL && count_area->kl == 0) {
23338c2ecf20Sopenharmony_ci		/* we found notthing violating our disk layout */
23348c2ecf20Sopenharmony_ci		if (dasd_check_blocksize(count_area->dl) == 0)
23358c2ecf20Sopenharmony_ci			block->bp_block = count_area->dl;
23368c2ecf20Sopenharmony_ci	}
23378c2ecf20Sopenharmony_ci	if (block->bp_block == 0) {
23388c2ecf20Sopenharmony_ci		dev_warn(&device->cdev->dev,
23398c2ecf20Sopenharmony_ci			 "The disk layout of the DASD is not supported\n");
23408c2ecf20Sopenharmony_ci		return -EMEDIUMTYPE;
23418c2ecf20Sopenharmony_ci	}
23428c2ecf20Sopenharmony_ci	block->s2b_shift = 0;	/* bits to shift 512 to get a block */
23438c2ecf20Sopenharmony_ci	for (sb = 512; sb < block->bp_block; sb = sb << 1)
23448c2ecf20Sopenharmony_ci		block->s2b_shift++;
23458c2ecf20Sopenharmony_ci
23468c2ecf20Sopenharmony_ci	blk_per_trk = recs_per_track(&private->rdc_data, 0, block->bp_block);
23478c2ecf20Sopenharmony_ci
23488c2ecf20Sopenharmony_ciraw:
23498c2ecf20Sopenharmony_ci	block->blocks = ((unsigned long) private->real_cyl *
23508c2ecf20Sopenharmony_ci			  private->rdc_data.trk_per_cyl *
23518c2ecf20Sopenharmony_ci			  blk_per_trk);
23528c2ecf20Sopenharmony_ci
23538c2ecf20Sopenharmony_ci	dev_info(&device->cdev->dev,
23548c2ecf20Sopenharmony_ci		 "DASD with %u KB/block, %lu KB total size, %u KB/track, "
23558c2ecf20Sopenharmony_ci		 "%s\n", (block->bp_block >> 10),
23568c2ecf20Sopenharmony_ci		 (((unsigned long) private->real_cyl *
23578c2ecf20Sopenharmony_ci		   private->rdc_data.trk_per_cyl *
23588c2ecf20Sopenharmony_ci		   blk_per_trk * (block->bp_block >> 9)) >> 1),
23598c2ecf20Sopenharmony_ci		 ((blk_per_trk * block->bp_block) >> 10),
23608c2ecf20Sopenharmony_ci		 private->uses_cdl ?
23618c2ecf20Sopenharmony_ci		 "compatible disk layout" : "linux disk layout");
23628c2ecf20Sopenharmony_ci
23638c2ecf20Sopenharmony_ci	return 0;
23648c2ecf20Sopenharmony_ci}
23658c2ecf20Sopenharmony_ci
23668c2ecf20Sopenharmony_cistatic int dasd_eckd_do_analysis(struct dasd_block *block)
23678c2ecf20Sopenharmony_ci{
23688c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private = block->base->private;
23698c2ecf20Sopenharmony_ci
23708c2ecf20Sopenharmony_ci	if (private->init_cqr_status < 0)
23718c2ecf20Sopenharmony_ci		return dasd_eckd_start_analysis(block);
23728c2ecf20Sopenharmony_ci	else
23738c2ecf20Sopenharmony_ci		return dasd_eckd_end_analysis(block);
23748c2ecf20Sopenharmony_ci}
23758c2ecf20Sopenharmony_ci
23768c2ecf20Sopenharmony_cistatic int dasd_eckd_basic_to_ready(struct dasd_device *device)
23778c2ecf20Sopenharmony_ci{
23788c2ecf20Sopenharmony_ci	return dasd_alias_add_device(device);
23798c2ecf20Sopenharmony_ci};
23808c2ecf20Sopenharmony_ci
23818c2ecf20Sopenharmony_cistatic int dasd_eckd_online_to_ready(struct dasd_device *device)
23828c2ecf20Sopenharmony_ci{
23838c2ecf20Sopenharmony_ci	if (cancel_work_sync(&device->reload_device))
23848c2ecf20Sopenharmony_ci		dasd_put_device(device);
23858c2ecf20Sopenharmony_ci	if (cancel_work_sync(&device->kick_validate))
23868c2ecf20Sopenharmony_ci		dasd_put_device(device);
23878c2ecf20Sopenharmony_ci
23888c2ecf20Sopenharmony_ci	return 0;
23898c2ecf20Sopenharmony_ci};
23908c2ecf20Sopenharmony_ci
23918c2ecf20Sopenharmony_cistatic int dasd_eckd_basic_to_known(struct dasd_device *device)
23928c2ecf20Sopenharmony_ci{
23938c2ecf20Sopenharmony_ci	return dasd_alias_remove_device(device);
23948c2ecf20Sopenharmony_ci};
23958c2ecf20Sopenharmony_ci
23968c2ecf20Sopenharmony_cistatic int
23978c2ecf20Sopenharmony_cidasd_eckd_fill_geometry(struct dasd_block *block, struct hd_geometry *geo)
23988c2ecf20Sopenharmony_ci{
23998c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private = block->base->private;
24008c2ecf20Sopenharmony_ci
24018c2ecf20Sopenharmony_ci	if (dasd_check_blocksize(block->bp_block) == 0) {
24028c2ecf20Sopenharmony_ci		geo->sectors = recs_per_track(&private->rdc_data,
24038c2ecf20Sopenharmony_ci					      0, block->bp_block);
24048c2ecf20Sopenharmony_ci	}
24058c2ecf20Sopenharmony_ci	geo->cylinders = private->rdc_data.no_cyl;
24068c2ecf20Sopenharmony_ci	geo->heads = private->rdc_data.trk_per_cyl;
24078c2ecf20Sopenharmony_ci	return 0;
24088c2ecf20Sopenharmony_ci}
24098c2ecf20Sopenharmony_ci
24108c2ecf20Sopenharmony_ci/*
24118c2ecf20Sopenharmony_ci * Build the TCW request for the format check
24128c2ecf20Sopenharmony_ci */
24138c2ecf20Sopenharmony_cistatic struct dasd_ccw_req *
24148c2ecf20Sopenharmony_cidasd_eckd_build_check_tcw(struct dasd_device *base, struct format_data_t *fdata,
24158c2ecf20Sopenharmony_ci			  int enable_pav, struct eckd_count *fmt_buffer,
24168c2ecf20Sopenharmony_ci			  int rpt)
24178c2ecf20Sopenharmony_ci{
24188c2ecf20Sopenharmony_ci	struct dasd_eckd_private *start_priv;
24198c2ecf20Sopenharmony_ci	struct dasd_device *startdev = NULL;
24208c2ecf20Sopenharmony_ci	struct tidaw *last_tidaw = NULL;
24218c2ecf20Sopenharmony_ci	struct dasd_ccw_req *cqr;
24228c2ecf20Sopenharmony_ci	struct itcw *itcw;
24238c2ecf20Sopenharmony_ci	int itcw_size;
24248c2ecf20Sopenharmony_ci	int count;
24258c2ecf20Sopenharmony_ci	int rc;
24268c2ecf20Sopenharmony_ci	int i;
24278c2ecf20Sopenharmony_ci
24288c2ecf20Sopenharmony_ci	if (enable_pav)
24298c2ecf20Sopenharmony_ci		startdev = dasd_alias_get_start_dev(base);
24308c2ecf20Sopenharmony_ci
24318c2ecf20Sopenharmony_ci	if (!startdev)
24328c2ecf20Sopenharmony_ci		startdev = base;
24338c2ecf20Sopenharmony_ci
24348c2ecf20Sopenharmony_ci	start_priv = startdev->private;
24358c2ecf20Sopenharmony_ci
24368c2ecf20Sopenharmony_ci	count = rpt * (fdata->stop_unit - fdata->start_unit + 1);
24378c2ecf20Sopenharmony_ci
24388c2ecf20Sopenharmony_ci	/*
24398c2ecf20Sopenharmony_ci	 * we're adding 'count' amount of tidaw to the itcw.
24408c2ecf20Sopenharmony_ci	 * calculate the corresponding itcw_size
24418c2ecf20Sopenharmony_ci	 */
24428c2ecf20Sopenharmony_ci	itcw_size = itcw_calc_size(0, count, 0);
24438c2ecf20Sopenharmony_ci
24448c2ecf20Sopenharmony_ci	cqr = dasd_fmalloc_request(DASD_ECKD_MAGIC, 0, itcw_size, startdev);
24458c2ecf20Sopenharmony_ci	if (IS_ERR(cqr))
24468c2ecf20Sopenharmony_ci		return cqr;
24478c2ecf20Sopenharmony_ci
24488c2ecf20Sopenharmony_ci	start_priv->count++;
24498c2ecf20Sopenharmony_ci
24508c2ecf20Sopenharmony_ci	itcw = itcw_init(cqr->data, itcw_size, ITCW_OP_READ, 0, count, 0);
24518c2ecf20Sopenharmony_ci	if (IS_ERR(itcw)) {
24528c2ecf20Sopenharmony_ci		rc = -EINVAL;
24538c2ecf20Sopenharmony_ci		goto out_err;
24548c2ecf20Sopenharmony_ci	}
24558c2ecf20Sopenharmony_ci
24568c2ecf20Sopenharmony_ci	cqr->cpaddr = itcw_get_tcw(itcw);
24578c2ecf20Sopenharmony_ci	rc = prepare_itcw(itcw, fdata->start_unit, fdata->stop_unit,
24588c2ecf20Sopenharmony_ci			  DASD_ECKD_CCW_READ_COUNT_MT, base, startdev, 0, count,
24598c2ecf20Sopenharmony_ci			  sizeof(struct eckd_count),
24608c2ecf20Sopenharmony_ci			  count * sizeof(struct eckd_count), 0, rpt);
24618c2ecf20Sopenharmony_ci	if (rc)
24628c2ecf20Sopenharmony_ci		goto out_err;
24638c2ecf20Sopenharmony_ci
24648c2ecf20Sopenharmony_ci	for (i = 0; i < count; i++) {
24658c2ecf20Sopenharmony_ci		last_tidaw = itcw_add_tidaw(itcw, 0, fmt_buffer++,
24668c2ecf20Sopenharmony_ci					    sizeof(struct eckd_count));
24678c2ecf20Sopenharmony_ci		if (IS_ERR(last_tidaw)) {
24688c2ecf20Sopenharmony_ci			rc = -EINVAL;
24698c2ecf20Sopenharmony_ci			goto out_err;
24708c2ecf20Sopenharmony_ci		}
24718c2ecf20Sopenharmony_ci	}
24728c2ecf20Sopenharmony_ci
24738c2ecf20Sopenharmony_ci	last_tidaw->flags |= TIDAW_FLAGS_LAST;
24748c2ecf20Sopenharmony_ci	itcw_finalize(itcw);
24758c2ecf20Sopenharmony_ci
24768c2ecf20Sopenharmony_ci	cqr->cpmode = 1;
24778c2ecf20Sopenharmony_ci	cqr->startdev = startdev;
24788c2ecf20Sopenharmony_ci	cqr->memdev = startdev;
24798c2ecf20Sopenharmony_ci	cqr->basedev = base;
24808c2ecf20Sopenharmony_ci	cqr->retries = startdev->default_retries;
24818c2ecf20Sopenharmony_ci	cqr->expires = startdev->default_expires * HZ;
24828c2ecf20Sopenharmony_ci	cqr->buildclk = get_tod_clock();
24838c2ecf20Sopenharmony_ci	cqr->status = DASD_CQR_FILLED;
24848c2ecf20Sopenharmony_ci	/* Set flags to suppress output for expected errors */
24858c2ecf20Sopenharmony_ci	set_bit(DASD_CQR_SUPPRESS_FP, &cqr->flags);
24868c2ecf20Sopenharmony_ci	set_bit(DASD_CQR_SUPPRESS_IL, &cqr->flags);
24878c2ecf20Sopenharmony_ci
24888c2ecf20Sopenharmony_ci	return cqr;
24898c2ecf20Sopenharmony_ci
24908c2ecf20Sopenharmony_ciout_err:
24918c2ecf20Sopenharmony_ci	dasd_sfree_request(cqr, startdev);
24928c2ecf20Sopenharmony_ci
24938c2ecf20Sopenharmony_ci	return ERR_PTR(rc);
24948c2ecf20Sopenharmony_ci}
24958c2ecf20Sopenharmony_ci
24968c2ecf20Sopenharmony_ci/*
24978c2ecf20Sopenharmony_ci * Build the CCW request for the format check
24988c2ecf20Sopenharmony_ci */
24998c2ecf20Sopenharmony_cistatic struct dasd_ccw_req *
25008c2ecf20Sopenharmony_cidasd_eckd_build_check(struct dasd_device *base, struct format_data_t *fdata,
25018c2ecf20Sopenharmony_ci		      int enable_pav, struct eckd_count *fmt_buffer, int rpt)
25028c2ecf20Sopenharmony_ci{
25038c2ecf20Sopenharmony_ci	struct dasd_eckd_private *start_priv;
25048c2ecf20Sopenharmony_ci	struct dasd_eckd_private *base_priv;
25058c2ecf20Sopenharmony_ci	struct dasd_device *startdev = NULL;
25068c2ecf20Sopenharmony_ci	struct dasd_ccw_req *cqr;
25078c2ecf20Sopenharmony_ci	struct ccw1 *ccw;
25088c2ecf20Sopenharmony_ci	void *data;
25098c2ecf20Sopenharmony_ci	int cplength, datasize;
25108c2ecf20Sopenharmony_ci	int use_prefix;
25118c2ecf20Sopenharmony_ci	int count;
25128c2ecf20Sopenharmony_ci	int i;
25138c2ecf20Sopenharmony_ci
25148c2ecf20Sopenharmony_ci	if (enable_pav)
25158c2ecf20Sopenharmony_ci		startdev = dasd_alias_get_start_dev(base);
25168c2ecf20Sopenharmony_ci
25178c2ecf20Sopenharmony_ci	if (!startdev)
25188c2ecf20Sopenharmony_ci		startdev = base;
25198c2ecf20Sopenharmony_ci
25208c2ecf20Sopenharmony_ci	start_priv = startdev->private;
25218c2ecf20Sopenharmony_ci	base_priv = base->private;
25228c2ecf20Sopenharmony_ci
25238c2ecf20Sopenharmony_ci	count = rpt * (fdata->stop_unit - fdata->start_unit + 1);
25248c2ecf20Sopenharmony_ci
25258c2ecf20Sopenharmony_ci	use_prefix = base_priv->features.feature[8] & 0x01;
25268c2ecf20Sopenharmony_ci
25278c2ecf20Sopenharmony_ci	if (use_prefix) {
25288c2ecf20Sopenharmony_ci		cplength = 1;
25298c2ecf20Sopenharmony_ci		datasize = sizeof(struct PFX_eckd_data);
25308c2ecf20Sopenharmony_ci	} else {
25318c2ecf20Sopenharmony_ci		cplength = 2;
25328c2ecf20Sopenharmony_ci		datasize = sizeof(struct DE_eckd_data) +
25338c2ecf20Sopenharmony_ci			sizeof(struct LO_eckd_data);
25348c2ecf20Sopenharmony_ci	}
25358c2ecf20Sopenharmony_ci	cplength += count;
25368c2ecf20Sopenharmony_ci
25378c2ecf20Sopenharmony_ci	cqr = dasd_fmalloc_request(DASD_ECKD_MAGIC, cplength, datasize, startdev);
25388c2ecf20Sopenharmony_ci	if (IS_ERR(cqr))
25398c2ecf20Sopenharmony_ci		return cqr;
25408c2ecf20Sopenharmony_ci
25418c2ecf20Sopenharmony_ci	start_priv->count++;
25428c2ecf20Sopenharmony_ci	data = cqr->data;
25438c2ecf20Sopenharmony_ci	ccw = cqr->cpaddr;
25448c2ecf20Sopenharmony_ci
25458c2ecf20Sopenharmony_ci	if (use_prefix) {
25468c2ecf20Sopenharmony_ci		prefix_LRE(ccw++, data, fdata->start_unit, fdata->stop_unit,
25478c2ecf20Sopenharmony_ci			   DASD_ECKD_CCW_READ_COUNT, base, startdev, 1, 0,
25488c2ecf20Sopenharmony_ci			   count, 0, 0);
25498c2ecf20Sopenharmony_ci	} else {
25508c2ecf20Sopenharmony_ci		define_extent(ccw++, data, fdata->start_unit, fdata->stop_unit,
25518c2ecf20Sopenharmony_ci			      DASD_ECKD_CCW_READ_COUNT, startdev, 0);
25528c2ecf20Sopenharmony_ci
25538c2ecf20Sopenharmony_ci		data += sizeof(struct DE_eckd_data);
25548c2ecf20Sopenharmony_ci		ccw[-1].flags |= CCW_FLAG_CC;
25558c2ecf20Sopenharmony_ci
25568c2ecf20Sopenharmony_ci		locate_record(ccw++, data, fdata->start_unit, 0, count,
25578c2ecf20Sopenharmony_ci			      DASD_ECKD_CCW_READ_COUNT, base, 0);
25588c2ecf20Sopenharmony_ci	}
25598c2ecf20Sopenharmony_ci
25608c2ecf20Sopenharmony_ci	for (i = 0; i < count; i++) {
25618c2ecf20Sopenharmony_ci		ccw[-1].flags |= CCW_FLAG_CC;
25628c2ecf20Sopenharmony_ci		ccw->cmd_code = DASD_ECKD_CCW_READ_COUNT;
25638c2ecf20Sopenharmony_ci		ccw->flags = CCW_FLAG_SLI;
25648c2ecf20Sopenharmony_ci		ccw->count = 8;
25658c2ecf20Sopenharmony_ci		ccw->cda = (__u32)(addr_t) fmt_buffer;
25668c2ecf20Sopenharmony_ci		ccw++;
25678c2ecf20Sopenharmony_ci		fmt_buffer++;
25688c2ecf20Sopenharmony_ci	}
25698c2ecf20Sopenharmony_ci
25708c2ecf20Sopenharmony_ci	cqr->startdev = startdev;
25718c2ecf20Sopenharmony_ci	cqr->memdev = startdev;
25728c2ecf20Sopenharmony_ci	cqr->basedev = base;
25738c2ecf20Sopenharmony_ci	cqr->retries = DASD_RETRIES;
25748c2ecf20Sopenharmony_ci	cqr->expires = startdev->default_expires * HZ;
25758c2ecf20Sopenharmony_ci	cqr->buildclk = get_tod_clock();
25768c2ecf20Sopenharmony_ci	cqr->status = DASD_CQR_FILLED;
25778c2ecf20Sopenharmony_ci	/* Set flags to suppress output for expected errors */
25788c2ecf20Sopenharmony_ci	set_bit(DASD_CQR_SUPPRESS_NRF, &cqr->flags);
25798c2ecf20Sopenharmony_ci
25808c2ecf20Sopenharmony_ci	return cqr;
25818c2ecf20Sopenharmony_ci}
25828c2ecf20Sopenharmony_ci
25838c2ecf20Sopenharmony_cistatic struct dasd_ccw_req *
25848c2ecf20Sopenharmony_cidasd_eckd_build_format(struct dasd_device *base, struct dasd_device *startdev,
25858c2ecf20Sopenharmony_ci		       struct format_data_t *fdata, int enable_pav)
25868c2ecf20Sopenharmony_ci{
25878c2ecf20Sopenharmony_ci	struct dasd_eckd_private *base_priv;
25888c2ecf20Sopenharmony_ci	struct dasd_eckd_private *start_priv;
25898c2ecf20Sopenharmony_ci	struct dasd_ccw_req *fcp;
25908c2ecf20Sopenharmony_ci	struct eckd_count *ect;
25918c2ecf20Sopenharmony_ci	struct ch_t address;
25928c2ecf20Sopenharmony_ci	struct ccw1 *ccw;
25938c2ecf20Sopenharmony_ci	void *data;
25948c2ecf20Sopenharmony_ci	int rpt;
25958c2ecf20Sopenharmony_ci	int cplength, datasize;
25968c2ecf20Sopenharmony_ci	int i, j;
25978c2ecf20Sopenharmony_ci	int intensity = 0;
25988c2ecf20Sopenharmony_ci	int r0_perm;
25998c2ecf20Sopenharmony_ci	int nr_tracks;
26008c2ecf20Sopenharmony_ci	int use_prefix;
26018c2ecf20Sopenharmony_ci
26028c2ecf20Sopenharmony_ci	if (enable_pav)
26038c2ecf20Sopenharmony_ci		startdev = dasd_alias_get_start_dev(base);
26048c2ecf20Sopenharmony_ci
26058c2ecf20Sopenharmony_ci	if (!startdev)
26068c2ecf20Sopenharmony_ci		startdev = base;
26078c2ecf20Sopenharmony_ci
26088c2ecf20Sopenharmony_ci	start_priv = startdev->private;
26098c2ecf20Sopenharmony_ci	base_priv = base->private;
26108c2ecf20Sopenharmony_ci
26118c2ecf20Sopenharmony_ci	rpt = recs_per_track(&base_priv->rdc_data, 0, fdata->blksize);
26128c2ecf20Sopenharmony_ci
26138c2ecf20Sopenharmony_ci	nr_tracks = fdata->stop_unit - fdata->start_unit + 1;
26148c2ecf20Sopenharmony_ci
26158c2ecf20Sopenharmony_ci	/*
26168c2ecf20Sopenharmony_ci	 * fdata->intensity is a bit string that tells us what to do:
26178c2ecf20Sopenharmony_ci	 *   Bit 0: write record zero
26188c2ecf20Sopenharmony_ci	 *   Bit 1: write home address, currently not supported
26198c2ecf20Sopenharmony_ci	 *   Bit 2: invalidate tracks
26208c2ecf20Sopenharmony_ci	 *   Bit 3: use OS/390 compatible disk layout (cdl)
26218c2ecf20Sopenharmony_ci	 *   Bit 4: do not allow storage subsystem to modify record zero
26228c2ecf20Sopenharmony_ci	 * Only some bit combinations do make sense.
26238c2ecf20Sopenharmony_ci	 */
26248c2ecf20Sopenharmony_ci	if (fdata->intensity & 0x10) {
26258c2ecf20Sopenharmony_ci		r0_perm = 0;
26268c2ecf20Sopenharmony_ci		intensity = fdata->intensity & ~0x10;
26278c2ecf20Sopenharmony_ci	} else {
26288c2ecf20Sopenharmony_ci		r0_perm = 1;
26298c2ecf20Sopenharmony_ci		intensity = fdata->intensity;
26308c2ecf20Sopenharmony_ci	}
26318c2ecf20Sopenharmony_ci
26328c2ecf20Sopenharmony_ci	use_prefix = base_priv->features.feature[8] & 0x01;
26338c2ecf20Sopenharmony_ci
26348c2ecf20Sopenharmony_ci	switch (intensity) {
26358c2ecf20Sopenharmony_ci	case 0x00:	/* Normal format */
26368c2ecf20Sopenharmony_ci	case 0x08:	/* Normal format, use cdl. */
26378c2ecf20Sopenharmony_ci		cplength = 2 + (rpt*nr_tracks);
26388c2ecf20Sopenharmony_ci		if (use_prefix)
26398c2ecf20Sopenharmony_ci			datasize = sizeof(struct PFX_eckd_data) +
26408c2ecf20Sopenharmony_ci				sizeof(struct LO_eckd_data) +
26418c2ecf20Sopenharmony_ci				rpt * nr_tracks * sizeof(struct eckd_count);
26428c2ecf20Sopenharmony_ci		else
26438c2ecf20Sopenharmony_ci			datasize = sizeof(struct DE_eckd_data) +
26448c2ecf20Sopenharmony_ci				sizeof(struct LO_eckd_data) +
26458c2ecf20Sopenharmony_ci				rpt * nr_tracks * sizeof(struct eckd_count);
26468c2ecf20Sopenharmony_ci		break;
26478c2ecf20Sopenharmony_ci	case 0x01:	/* Write record zero and format track. */
26488c2ecf20Sopenharmony_ci	case 0x09:	/* Write record zero and format track, use cdl. */
26498c2ecf20Sopenharmony_ci		cplength = 2 + rpt * nr_tracks;
26508c2ecf20Sopenharmony_ci		if (use_prefix)
26518c2ecf20Sopenharmony_ci			datasize = sizeof(struct PFX_eckd_data) +
26528c2ecf20Sopenharmony_ci				sizeof(struct LO_eckd_data) +
26538c2ecf20Sopenharmony_ci				sizeof(struct eckd_count) +
26548c2ecf20Sopenharmony_ci				rpt * nr_tracks * sizeof(struct eckd_count);
26558c2ecf20Sopenharmony_ci		else
26568c2ecf20Sopenharmony_ci			datasize = sizeof(struct DE_eckd_data) +
26578c2ecf20Sopenharmony_ci				sizeof(struct LO_eckd_data) +
26588c2ecf20Sopenharmony_ci				sizeof(struct eckd_count) +
26598c2ecf20Sopenharmony_ci				rpt * nr_tracks * sizeof(struct eckd_count);
26608c2ecf20Sopenharmony_ci		break;
26618c2ecf20Sopenharmony_ci	case 0x04:	/* Invalidate track. */
26628c2ecf20Sopenharmony_ci	case 0x0c:	/* Invalidate track, use cdl. */
26638c2ecf20Sopenharmony_ci		cplength = 3;
26648c2ecf20Sopenharmony_ci		if (use_prefix)
26658c2ecf20Sopenharmony_ci			datasize = sizeof(struct PFX_eckd_data) +
26668c2ecf20Sopenharmony_ci				sizeof(struct LO_eckd_data) +
26678c2ecf20Sopenharmony_ci				sizeof(struct eckd_count);
26688c2ecf20Sopenharmony_ci		else
26698c2ecf20Sopenharmony_ci			datasize = sizeof(struct DE_eckd_data) +
26708c2ecf20Sopenharmony_ci				sizeof(struct LO_eckd_data) +
26718c2ecf20Sopenharmony_ci				sizeof(struct eckd_count);
26728c2ecf20Sopenharmony_ci		break;
26738c2ecf20Sopenharmony_ci	default:
26748c2ecf20Sopenharmony_ci		dev_warn(&startdev->cdev->dev,
26758c2ecf20Sopenharmony_ci			 "An I/O control call used incorrect flags 0x%x\n",
26768c2ecf20Sopenharmony_ci			 fdata->intensity);
26778c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
26788c2ecf20Sopenharmony_ci	}
26798c2ecf20Sopenharmony_ci
26808c2ecf20Sopenharmony_ci	fcp = dasd_fmalloc_request(DASD_ECKD_MAGIC, cplength, datasize, startdev);
26818c2ecf20Sopenharmony_ci	if (IS_ERR(fcp))
26828c2ecf20Sopenharmony_ci		return fcp;
26838c2ecf20Sopenharmony_ci
26848c2ecf20Sopenharmony_ci	start_priv->count++;
26858c2ecf20Sopenharmony_ci	data = fcp->data;
26868c2ecf20Sopenharmony_ci	ccw = fcp->cpaddr;
26878c2ecf20Sopenharmony_ci
26888c2ecf20Sopenharmony_ci	switch (intensity & ~0x08) {
26898c2ecf20Sopenharmony_ci	case 0x00: /* Normal format. */
26908c2ecf20Sopenharmony_ci		if (use_prefix) {
26918c2ecf20Sopenharmony_ci			prefix(ccw++, (struct PFX_eckd_data *) data,
26928c2ecf20Sopenharmony_ci			       fdata->start_unit, fdata->stop_unit,
26938c2ecf20Sopenharmony_ci			       DASD_ECKD_CCW_WRITE_CKD, base, startdev);
26948c2ecf20Sopenharmony_ci			/* grant subsystem permission to format R0 */
26958c2ecf20Sopenharmony_ci			if (r0_perm)
26968c2ecf20Sopenharmony_ci				((struct PFX_eckd_data *)data)
26978c2ecf20Sopenharmony_ci					->define_extent.ga_extended |= 0x04;
26988c2ecf20Sopenharmony_ci			data += sizeof(struct PFX_eckd_data);
26998c2ecf20Sopenharmony_ci		} else {
27008c2ecf20Sopenharmony_ci			define_extent(ccw++, (struct DE_eckd_data *) data,
27018c2ecf20Sopenharmony_ci				      fdata->start_unit, fdata->stop_unit,
27028c2ecf20Sopenharmony_ci				      DASD_ECKD_CCW_WRITE_CKD, startdev, 0);
27038c2ecf20Sopenharmony_ci			/* grant subsystem permission to format R0 */
27048c2ecf20Sopenharmony_ci			if (r0_perm)
27058c2ecf20Sopenharmony_ci				((struct DE_eckd_data *) data)
27068c2ecf20Sopenharmony_ci					->ga_extended |= 0x04;
27078c2ecf20Sopenharmony_ci			data += sizeof(struct DE_eckd_data);
27088c2ecf20Sopenharmony_ci		}
27098c2ecf20Sopenharmony_ci		ccw[-1].flags |= CCW_FLAG_CC;
27108c2ecf20Sopenharmony_ci		locate_record(ccw++, (struct LO_eckd_data *) data,
27118c2ecf20Sopenharmony_ci			      fdata->start_unit, 0, rpt*nr_tracks,
27128c2ecf20Sopenharmony_ci			      DASD_ECKD_CCW_WRITE_CKD, base,
27138c2ecf20Sopenharmony_ci			      fdata->blksize);
27148c2ecf20Sopenharmony_ci		data += sizeof(struct LO_eckd_data);
27158c2ecf20Sopenharmony_ci		break;
27168c2ecf20Sopenharmony_ci	case 0x01: /* Write record zero + format track. */
27178c2ecf20Sopenharmony_ci		if (use_prefix) {
27188c2ecf20Sopenharmony_ci			prefix(ccw++, (struct PFX_eckd_data *) data,
27198c2ecf20Sopenharmony_ci			       fdata->start_unit, fdata->stop_unit,
27208c2ecf20Sopenharmony_ci			       DASD_ECKD_CCW_WRITE_RECORD_ZERO,
27218c2ecf20Sopenharmony_ci			       base, startdev);
27228c2ecf20Sopenharmony_ci			data += sizeof(struct PFX_eckd_data);
27238c2ecf20Sopenharmony_ci		} else {
27248c2ecf20Sopenharmony_ci			define_extent(ccw++, (struct DE_eckd_data *) data,
27258c2ecf20Sopenharmony_ci			       fdata->start_unit, fdata->stop_unit,
27268c2ecf20Sopenharmony_ci			       DASD_ECKD_CCW_WRITE_RECORD_ZERO, startdev, 0);
27278c2ecf20Sopenharmony_ci			data += sizeof(struct DE_eckd_data);
27288c2ecf20Sopenharmony_ci		}
27298c2ecf20Sopenharmony_ci		ccw[-1].flags |= CCW_FLAG_CC;
27308c2ecf20Sopenharmony_ci		locate_record(ccw++, (struct LO_eckd_data *) data,
27318c2ecf20Sopenharmony_ci			      fdata->start_unit, 0, rpt * nr_tracks + 1,
27328c2ecf20Sopenharmony_ci			      DASD_ECKD_CCW_WRITE_RECORD_ZERO, base,
27338c2ecf20Sopenharmony_ci			      base->block->bp_block);
27348c2ecf20Sopenharmony_ci		data += sizeof(struct LO_eckd_data);
27358c2ecf20Sopenharmony_ci		break;
27368c2ecf20Sopenharmony_ci	case 0x04: /* Invalidate track. */
27378c2ecf20Sopenharmony_ci		if (use_prefix) {
27388c2ecf20Sopenharmony_ci			prefix(ccw++, (struct PFX_eckd_data *) data,
27398c2ecf20Sopenharmony_ci			       fdata->start_unit, fdata->stop_unit,
27408c2ecf20Sopenharmony_ci			       DASD_ECKD_CCW_WRITE_CKD, base, startdev);
27418c2ecf20Sopenharmony_ci			data += sizeof(struct PFX_eckd_data);
27428c2ecf20Sopenharmony_ci		} else {
27438c2ecf20Sopenharmony_ci			define_extent(ccw++, (struct DE_eckd_data *) data,
27448c2ecf20Sopenharmony_ci			       fdata->start_unit, fdata->stop_unit,
27458c2ecf20Sopenharmony_ci			       DASD_ECKD_CCW_WRITE_CKD, startdev, 0);
27468c2ecf20Sopenharmony_ci			data += sizeof(struct DE_eckd_data);
27478c2ecf20Sopenharmony_ci		}
27488c2ecf20Sopenharmony_ci		ccw[-1].flags |= CCW_FLAG_CC;
27498c2ecf20Sopenharmony_ci		locate_record(ccw++, (struct LO_eckd_data *) data,
27508c2ecf20Sopenharmony_ci			      fdata->start_unit, 0, 1,
27518c2ecf20Sopenharmony_ci			      DASD_ECKD_CCW_WRITE_CKD, base, 8);
27528c2ecf20Sopenharmony_ci		data += sizeof(struct LO_eckd_data);
27538c2ecf20Sopenharmony_ci		break;
27548c2ecf20Sopenharmony_ci	}
27558c2ecf20Sopenharmony_ci
27568c2ecf20Sopenharmony_ci	for (j = 0; j < nr_tracks; j++) {
27578c2ecf20Sopenharmony_ci		/* calculate cylinder and head for the current track */
27588c2ecf20Sopenharmony_ci		set_ch_t(&address,
27598c2ecf20Sopenharmony_ci			 (fdata->start_unit + j) /
27608c2ecf20Sopenharmony_ci			 base_priv->rdc_data.trk_per_cyl,
27618c2ecf20Sopenharmony_ci			 (fdata->start_unit + j) %
27628c2ecf20Sopenharmony_ci			 base_priv->rdc_data.trk_per_cyl);
27638c2ecf20Sopenharmony_ci		if (intensity & 0x01) {	/* write record zero */
27648c2ecf20Sopenharmony_ci			ect = (struct eckd_count *) data;
27658c2ecf20Sopenharmony_ci			data += sizeof(struct eckd_count);
27668c2ecf20Sopenharmony_ci			ect->cyl = address.cyl;
27678c2ecf20Sopenharmony_ci			ect->head = address.head;
27688c2ecf20Sopenharmony_ci			ect->record = 0;
27698c2ecf20Sopenharmony_ci			ect->kl = 0;
27708c2ecf20Sopenharmony_ci			ect->dl = 8;
27718c2ecf20Sopenharmony_ci			ccw[-1].flags |= CCW_FLAG_CC;
27728c2ecf20Sopenharmony_ci			ccw->cmd_code = DASD_ECKD_CCW_WRITE_RECORD_ZERO;
27738c2ecf20Sopenharmony_ci			ccw->flags = CCW_FLAG_SLI;
27748c2ecf20Sopenharmony_ci			ccw->count = 8;
27758c2ecf20Sopenharmony_ci			ccw->cda = (__u32)(addr_t) ect;
27768c2ecf20Sopenharmony_ci			ccw++;
27778c2ecf20Sopenharmony_ci		}
27788c2ecf20Sopenharmony_ci		if ((intensity & ~0x08) & 0x04) {	/* erase track */
27798c2ecf20Sopenharmony_ci			ect = (struct eckd_count *) data;
27808c2ecf20Sopenharmony_ci			data += sizeof(struct eckd_count);
27818c2ecf20Sopenharmony_ci			ect->cyl = address.cyl;
27828c2ecf20Sopenharmony_ci			ect->head = address.head;
27838c2ecf20Sopenharmony_ci			ect->record = 1;
27848c2ecf20Sopenharmony_ci			ect->kl = 0;
27858c2ecf20Sopenharmony_ci			ect->dl = 0;
27868c2ecf20Sopenharmony_ci			ccw[-1].flags |= CCW_FLAG_CC;
27878c2ecf20Sopenharmony_ci			ccw->cmd_code = DASD_ECKD_CCW_WRITE_CKD;
27888c2ecf20Sopenharmony_ci			ccw->flags = CCW_FLAG_SLI;
27898c2ecf20Sopenharmony_ci			ccw->count = 8;
27908c2ecf20Sopenharmony_ci			ccw->cda = (__u32)(addr_t) ect;
27918c2ecf20Sopenharmony_ci		} else {		/* write remaining records */
27928c2ecf20Sopenharmony_ci			for (i = 0; i < rpt; i++) {
27938c2ecf20Sopenharmony_ci				ect = (struct eckd_count *) data;
27948c2ecf20Sopenharmony_ci				data += sizeof(struct eckd_count);
27958c2ecf20Sopenharmony_ci				ect->cyl = address.cyl;
27968c2ecf20Sopenharmony_ci				ect->head = address.head;
27978c2ecf20Sopenharmony_ci				ect->record = i + 1;
27988c2ecf20Sopenharmony_ci				ect->kl = 0;
27998c2ecf20Sopenharmony_ci				ect->dl = fdata->blksize;
28008c2ecf20Sopenharmony_ci				/*
28018c2ecf20Sopenharmony_ci				 * Check for special tracks 0-1
28028c2ecf20Sopenharmony_ci				 * when formatting CDL
28038c2ecf20Sopenharmony_ci				 */
28048c2ecf20Sopenharmony_ci				if ((intensity & 0x08) &&
28058c2ecf20Sopenharmony_ci				    address.cyl == 0 && address.head == 0) {
28068c2ecf20Sopenharmony_ci					if (i < 3) {
28078c2ecf20Sopenharmony_ci						ect->kl = 4;
28088c2ecf20Sopenharmony_ci						ect->dl = sizes_trk0[i] - 4;
28098c2ecf20Sopenharmony_ci					}
28108c2ecf20Sopenharmony_ci				}
28118c2ecf20Sopenharmony_ci				if ((intensity & 0x08) &&
28128c2ecf20Sopenharmony_ci				    address.cyl == 0 && address.head == 1) {
28138c2ecf20Sopenharmony_ci					ect->kl = 44;
28148c2ecf20Sopenharmony_ci					ect->dl = LABEL_SIZE - 44;
28158c2ecf20Sopenharmony_ci				}
28168c2ecf20Sopenharmony_ci				ccw[-1].flags |= CCW_FLAG_CC;
28178c2ecf20Sopenharmony_ci				if (i != 0 || j == 0)
28188c2ecf20Sopenharmony_ci					ccw->cmd_code =
28198c2ecf20Sopenharmony_ci						DASD_ECKD_CCW_WRITE_CKD;
28208c2ecf20Sopenharmony_ci				else
28218c2ecf20Sopenharmony_ci					ccw->cmd_code =
28228c2ecf20Sopenharmony_ci						DASD_ECKD_CCW_WRITE_CKD_MT;
28238c2ecf20Sopenharmony_ci				ccw->flags = CCW_FLAG_SLI;
28248c2ecf20Sopenharmony_ci				ccw->count = 8;
28258c2ecf20Sopenharmony_ci				ccw->cda = (__u32)(addr_t) ect;
28268c2ecf20Sopenharmony_ci				ccw++;
28278c2ecf20Sopenharmony_ci			}
28288c2ecf20Sopenharmony_ci		}
28298c2ecf20Sopenharmony_ci	}
28308c2ecf20Sopenharmony_ci
28318c2ecf20Sopenharmony_ci	fcp->startdev = startdev;
28328c2ecf20Sopenharmony_ci	fcp->memdev = startdev;
28338c2ecf20Sopenharmony_ci	fcp->basedev = base;
28348c2ecf20Sopenharmony_ci	fcp->retries = 256;
28358c2ecf20Sopenharmony_ci	fcp->expires = startdev->default_expires * HZ;
28368c2ecf20Sopenharmony_ci	fcp->buildclk = get_tod_clock();
28378c2ecf20Sopenharmony_ci	fcp->status = DASD_CQR_FILLED;
28388c2ecf20Sopenharmony_ci
28398c2ecf20Sopenharmony_ci	return fcp;
28408c2ecf20Sopenharmony_ci}
28418c2ecf20Sopenharmony_ci
28428c2ecf20Sopenharmony_ci/*
28438c2ecf20Sopenharmony_ci * Wrapper function to build a CCW request depending on input data
28448c2ecf20Sopenharmony_ci */
28458c2ecf20Sopenharmony_cistatic struct dasd_ccw_req *
28468c2ecf20Sopenharmony_cidasd_eckd_format_build_ccw_req(struct dasd_device *base,
28478c2ecf20Sopenharmony_ci			       struct format_data_t *fdata, int enable_pav,
28488c2ecf20Sopenharmony_ci			       int tpm, struct eckd_count *fmt_buffer, int rpt)
28498c2ecf20Sopenharmony_ci{
28508c2ecf20Sopenharmony_ci	struct dasd_ccw_req *ccw_req;
28518c2ecf20Sopenharmony_ci
28528c2ecf20Sopenharmony_ci	if (!fmt_buffer) {
28538c2ecf20Sopenharmony_ci		ccw_req = dasd_eckd_build_format(base, NULL, fdata, enable_pav);
28548c2ecf20Sopenharmony_ci	} else {
28558c2ecf20Sopenharmony_ci		if (tpm)
28568c2ecf20Sopenharmony_ci			ccw_req = dasd_eckd_build_check_tcw(base, fdata,
28578c2ecf20Sopenharmony_ci							    enable_pav,
28588c2ecf20Sopenharmony_ci							    fmt_buffer, rpt);
28598c2ecf20Sopenharmony_ci		else
28608c2ecf20Sopenharmony_ci			ccw_req = dasd_eckd_build_check(base, fdata, enable_pav,
28618c2ecf20Sopenharmony_ci							fmt_buffer, rpt);
28628c2ecf20Sopenharmony_ci	}
28638c2ecf20Sopenharmony_ci
28648c2ecf20Sopenharmony_ci	return ccw_req;
28658c2ecf20Sopenharmony_ci}
28668c2ecf20Sopenharmony_ci
28678c2ecf20Sopenharmony_ci/*
28688c2ecf20Sopenharmony_ci * Sanity checks on format_data
28698c2ecf20Sopenharmony_ci */
28708c2ecf20Sopenharmony_cistatic int dasd_eckd_format_sanity_checks(struct dasd_device *base,
28718c2ecf20Sopenharmony_ci					  struct format_data_t *fdata)
28728c2ecf20Sopenharmony_ci{
28738c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private = base->private;
28748c2ecf20Sopenharmony_ci
28758c2ecf20Sopenharmony_ci	if (fdata->start_unit >=
28768c2ecf20Sopenharmony_ci	    (private->real_cyl * private->rdc_data.trk_per_cyl)) {
28778c2ecf20Sopenharmony_ci		dev_warn(&base->cdev->dev,
28788c2ecf20Sopenharmony_ci			 "Start track number %u used in formatting is too big\n",
28798c2ecf20Sopenharmony_ci			 fdata->start_unit);
28808c2ecf20Sopenharmony_ci		return -EINVAL;
28818c2ecf20Sopenharmony_ci	}
28828c2ecf20Sopenharmony_ci	if (fdata->stop_unit >=
28838c2ecf20Sopenharmony_ci	    (private->real_cyl * private->rdc_data.trk_per_cyl)) {
28848c2ecf20Sopenharmony_ci		dev_warn(&base->cdev->dev,
28858c2ecf20Sopenharmony_ci			 "Stop track number %u used in formatting is too big\n",
28868c2ecf20Sopenharmony_ci			 fdata->stop_unit);
28878c2ecf20Sopenharmony_ci		return -EINVAL;
28888c2ecf20Sopenharmony_ci	}
28898c2ecf20Sopenharmony_ci	if (fdata->start_unit > fdata->stop_unit) {
28908c2ecf20Sopenharmony_ci		dev_warn(&base->cdev->dev,
28918c2ecf20Sopenharmony_ci			 "Start track %u used in formatting exceeds end track\n",
28928c2ecf20Sopenharmony_ci			 fdata->start_unit);
28938c2ecf20Sopenharmony_ci		return -EINVAL;
28948c2ecf20Sopenharmony_ci	}
28958c2ecf20Sopenharmony_ci	if (dasd_check_blocksize(fdata->blksize) != 0) {
28968c2ecf20Sopenharmony_ci		dev_warn(&base->cdev->dev,
28978c2ecf20Sopenharmony_ci			 "The DASD cannot be formatted with block size %u\n",
28988c2ecf20Sopenharmony_ci			 fdata->blksize);
28998c2ecf20Sopenharmony_ci		return -EINVAL;
29008c2ecf20Sopenharmony_ci	}
29018c2ecf20Sopenharmony_ci	return 0;
29028c2ecf20Sopenharmony_ci}
29038c2ecf20Sopenharmony_ci
29048c2ecf20Sopenharmony_ci/*
29058c2ecf20Sopenharmony_ci * This function will process format_data originally coming from an IOCTL
29068c2ecf20Sopenharmony_ci */
29078c2ecf20Sopenharmony_cistatic int dasd_eckd_format_process_data(struct dasd_device *base,
29088c2ecf20Sopenharmony_ci					 struct format_data_t *fdata,
29098c2ecf20Sopenharmony_ci					 int enable_pav, int tpm,
29108c2ecf20Sopenharmony_ci					 struct eckd_count *fmt_buffer, int rpt,
29118c2ecf20Sopenharmony_ci					 struct irb *irb)
29128c2ecf20Sopenharmony_ci{
29138c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private = base->private;
29148c2ecf20Sopenharmony_ci	struct dasd_ccw_req *cqr, *n;
29158c2ecf20Sopenharmony_ci	struct list_head format_queue;
29168c2ecf20Sopenharmony_ci	struct dasd_device *device;
29178c2ecf20Sopenharmony_ci	char *sense = NULL;
29188c2ecf20Sopenharmony_ci	int old_start, old_stop, format_step;
29198c2ecf20Sopenharmony_ci	int step, retry;
29208c2ecf20Sopenharmony_ci	int rc;
29218c2ecf20Sopenharmony_ci
29228c2ecf20Sopenharmony_ci	rc = dasd_eckd_format_sanity_checks(base, fdata);
29238c2ecf20Sopenharmony_ci	if (rc)
29248c2ecf20Sopenharmony_ci		return rc;
29258c2ecf20Sopenharmony_ci
29268c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&format_queue);
29278c2ecf20Sopenharmony_ci
29288c2ecf20Sopenharmony_ci	old_start = fdata->start_unit;
29298c2ecf20Sopenharmony_ci	old_stop = fdata->stop_unit;
29308c2ecf20Sopenharmony_ci
29318c2ecf20Sopenharmony_ci	if (!tpm && fmt_buffer != NULL) {
29328c2ecf20Sopenharmony_ci		/* Command Mode / Format Check */
29338c2ecf20Sopenharmony_ci		format_step = 1;
29348c2ecf20Sopenharmony_ci	} else if (tpm && fmt_buffer != NULL) {
29358c2ecf20Sopenharmony_ci		/* Transport Mode / Format Check */
29368c2ecf20Sopenharmony_ci		format_step = DASD_CQR_MAX_CCW / rpt;
29378c2ecf20Sopenharmony_ci	} else {
29388c2ecf20Sopenharmony_ci		/* Normal Formatting */
29398c2ecf20Sopenharmony_ci		format_step = DASD_CQR_MAX_CCW /
29408c2ecf20Sopenharmony_ci			recs_per_track(&private->rdc_data, 0, fdata->blksize);
29418c2ecf20Sopenharmony_ci	}
29428c2ecf20Sopenharmony_ci
29438c2ecf20Sopenharmony_ci	do {
29448c2ecf20Sopenharmony_ci		retry = 0;
29458c2ecf20Sopenharmony_ci		while (fdata->start_unit <= old_stop) {
29468c2ecf20Sopenharmony_ci			step = fdata->stop_unit - fdata->start_unit + 1;
29478c2ecf20Sopenharmony_ci			if (step > format_step) {
29488c2ecf20Sopenharmony_ci				fdata->stop_unit =
29498c2ecf20Sopenharmony_ci					fdata->start_unit + format_step - 1;
29508c2ecf20Sopenharmony_ci			}
29518c2ecf20Sopenharmony_ci
29528c2ecf20Sopenharmony_ci			cqr = dasd_eckd_format_build_ccw_req(base, fdata,
29538c2ecf20Sopenharmony_ci							     enable_pav, tpm,
29548c2ecf20Sopenharmony_ci							     fmt_buffer, rpt);
29558c2ecf20Sopenharmony_ci			if (IS_ERR(cqr)) {
29568c2ecf20Sopenharmony_ci				rc = PTR_ERR(cqr);
29578c2ecf20Sopenharmony_ci				if (rc == -ENOMEM) {
29588c2ecf20Sopenharmony_ci					if (list_empty(&format_queue))
29598c2ecf20Sopenharmony_ci						goto out;
29608c2ecf20Sopenharmony_ci					/*
29618c2ecf20Sopenharmony_ci					 * not enough memory available, start
29628c2ecf20Sopenharmony_ci					 * requests retry after first requests
29638c2ecf20Sopenharmony_ci					 * were finished
29648c2ecf20Sopenharmony_ci					 */
29658c2ecf20Sopenharmony_ci					retry = 1;
29668c2ecf20Sopenharmony_ci					break;
29678c2ecf20Sopenharmony_ci				}
29688c2ecf20Sopenharmony_ci				goto out_err;
29698c2ecf20Sopenharmony_ci			}
29708c2ecf20Sopenharmony_ci			list_add_tail(&cqr->blocklist, &format_queue);
29718c2ecf20Sopenharmony_ci
29728c2ecf20Sopenharmony_ci			if (fmt_buffer) {
29738c2ecf20Sopenharmony_ci				step = fdata->stop_unit - fdata->start_unit + 1;
29748c2ecf20Sopenharmony_ci				fmt_buffer += rpt * step;
29758c2ecf20Sopenharmony_ci			}
29768c2ecf20Sopenharmony_ci			fdata->start_unit = fdata->stop_unit + 1;
29778c2ecf20Sopenharmony_ci			fdata->stop_unit = old_stop;
29788c2ecf20Sopenharmony_ci		}
29798c2ecf20Sopenharmony_ci
29808c2ecf20Sopenharmony_ci		rc = dasd_sleep_on_queue(&format_queue);
29818c2ecf20Sopenharmony_ci
29828c2ecf20Sopenharmony_ciout_err:
29838c2ecf20Sopenharmony_ci		list_for_each_entry_safe(cqr, n, &format_queue, blocklist) {
29848c2ecf20Sopenharmony_ci			device = cqr->startdev;
29858c2ecf20Sopenharmony_ci			private = device->private;
29868c2ecf20Sopenharmony_ci
29878c2ecf20Sopenharmony_ci			if (cqr->status == DASD_CQR_FAILED) {
29888c2ecf20Sopenharmony_ci				/*
29898c2ecf20Sopenharmony_ci				 * Only get sense data if called by format
29908c2ecf20Sopenharmony_ci				 * check
29918c2ecf20Sopenharmony_ci				 */
29928c2ecf20Sopenharmony_ci				if (fmt_buffer && irb) {
29938c2ecf20Sopenharmony_ci					sense = dasd_get_sense(&cqr->irb);
29948c2ecf20Sopenharmony_ci					memcpy(irb, &cqr->irb, sizeof(*irb));
29958c2ecf20Sopenharmony_ci				}
29968c2ecf20Sopenharmony_ci				rc = -EIO;
29978c2ecf20Sopenharmony_ci			}
29988c2ecf20Sopenharmony_ci			list_del_init(&cqr->blocklist);
29998c2ecf20Sopenharmony_ci			dasd_ffree_request(cqr, device);
30008c2ecf20Sopenharmony_ci			private->count--;
30018c2ecf20Sopenharmony_ci		}
30028c2ecf20Sopenharmony_ci
30038c2ecf20Sopenharmony_ci		if (rc && rc != -EIO)
30048c2ecf20Sopenharmony_ci			goto out;
30058c2ecf20Sopenharmony_ci		if (rc == -EIO) {
30068c2ecf20Sopenharmony_ci			/*
30078c2ecf20Sopenharmony_ci			 * In case fewer than the expected records are on the
30088c2ecf20Sopenharmony_ci			 * track, we will most likely get a 'No Record Found'
30098c2ecf20Sopenharmony_ci			 * error (in command mode) or a 'File Protected' error
30108c2ecf20Sopenharmony_ci			 * (in transport mode). Those particular cases shouldn't
30118c2ecf20Sopenharmony_ci			 * pass the -EIO to the IOCTL, therefore reset the rc
30128c2ecf20Sopenharmony_ci			 * and continue.
30138c2ecf20Sopenharmony_ci			 */
30148c2ecf20Sopenharmony_ci			if (sense &&
30158c2ecf20Sopenharmony_ci			    (sense[1] & SNS1_NO_REC_FOUND ||
30168c2ecf20Sopenharmony_ci			     sense[1] & SNS1_FILE_PROTECTED))
30178c2ecf20Sopenharmony_ci				retry = 1;
30188c2ecf20Sopenharmony_ci			else
30198c2ecf20Sopenharmony_ci				goto out;
30208c2ecf20Sopenharmony_ci		}
30218c2ecf20Sopenharmony_ci
30228c2ecf20Sopenharmony_ci	} while (retry);
30238c2ecf20Sopenharmony_ci
30248c2ecf20Sopenharmony_ciout:
30258c2ecf20Sopenharmony_ci	fdata->start_unit = old_start;
30268c2ecf20Sopenharmony_ci	fdata->stop_unit = old_stop;
30278c2ecf20Sopenharmony_ci
30288c2ecf20Sopenharmony_ci	return rc;
30298c2ecf20Sopenharmony_ci}
30308c2ecf20Sopenharmony_ci
30318c2ecf20Sopenharmony_cistatic int dasd_eckd_format_device(struct dasd_device *base,
30328c2ecf20Sopenharmony_ci				   struct format_data_t *fdata, int enable_pav)
30338c2ecf20Sopenharmony_ci{
30348c2ecf20Sopenharmony_ci	return dasd_eckd_format_process_data(base, fdata, enable_pav, 0, NULL,
30358c2ecf20Sopenharmony_ci					     0, NULL);
30368c2ecf20Sopenharmony_ci}
30378c2ecf20Sopenharmony_ci
30388c2ecf20Sopenharmony_cistatic bool test_and_set_format_track(struct dasd_format_entry *to_format,
30398c2ecf20Sopenharmony_ci				      struct dasd_ccw_req *cqr)
30408c2ecf20Sopenharmony_ci{
30418c2ecf20Sopenharmony_ci	struct dasd_block *block = cqr->block;
30428c2ecf20Sopenharmony_ci	struct dasd_format_entry *format;
30438c2ecf20Sopenharmony_ci	unsigned long flags;
30448c2ecf20Sopenharmony_ci	bool rc = false;
30458c2ecf20Sopenharmony_ci
30468c2ecf20Sopenharmony_ci	spin_lock_irqsave(&block->format_lock, flags);
30478c2ecf20Sopenharmony_ci	if (cqr->trkcount != atomic_read(&block->trkcount)) {
30488c2ecf20Sopenharmony_ci		/*
30498c2ecf20Sopenharmony_ci		 * The number of formatted tracks has changed after request
30508c2ecf20Sopenharmony_ci		 * start and we can not tell if the current track was involved.
30518c2ecf20Sopenharmony_ci		 * To avoid data corruption treat it as if the current track is
30528c2ecf20Sopenharmony_ci		 * involved
30538c2ecf20Sopenharmony_ci		 */
30548c2ecf20Sopenharmony_ci		rc = true;
30558c2ecf20Sopenharmony_ci		goto out;
30568c2ecf20Sopenharmony_ci	}
30578c2ecf20Sopenharmony_ci	list_for_each_entry(format, &block->format_list, list) {
30588c2ecf20Sopenharmony_ci		if (format->track == to_format->track) {
30598c2ecf20Sopenharmony_ci			rc = true;
30608c2ecf20Sopenharmony_ci			goto out;
30618c2ecf20Sopenharmony_ci		}
30628c2ecf20Sopenharmony_ci	}
30638c2ecf20Sopenharmony_ci	list_add_tail(&to_format->list, &block->format_list);
30648c2ecf20Sopenharmony_ci
30658c2ecf20Sopenharmony_ciout:
30668c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&block->format_lock, flags);
30678c2ecf20Sopenharmony_ci	return rc;
30688c2ecf20Sopenharmony_ci}
30698c2ecf20Sopenharmony_ci
30708c2ecf20Sopenharmony_cistatic void clear_format_track(struct dasd_format_entry *format,
30718c2ecf20Sopenharmony_ci			      struct dasd_block *block)
30728c2ecf20Sopenharmony_ci{
30738c2ecf20Sopenharmony_ci	unsigned long flags;
30748c2ecf20Sopenharmony_ci
30758c2ecf20Sopenharmony_ci	spin_lock_irqsave(&block->format_lock, flags);
30768c2ecf20Sopenharmony_ci	atomic_inc(&block->trkcount);
30778c2ecf20Sopenharmony_ci	list_del_init(&format->list);
30788c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&block->format_lock, flags);
30798c2ecf20Sopenharmony_ci}
30808c2ecf20Sopenharmony_ci
30818c2ecf20Sopenharmony_ci/*
30828c2ecf20Sopenharmony_ci * Callback function to free ESE format requests.
30838c2ecf20Sopenharmony_ci */
30848c2ecf20Sopenharmony_cistatic void dasd_eckd_ese_format_cb(struct dasd_ccw_req *cqr, void *data)
30858c2ecf20Sopenharmony_ci{
30868c2ecf20Sopenharmony_ci	struct dasd_device *device = cqr->startdev;
30878c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
30888c2ecf20Sopenharmony_ci	struct dasd_format_entry *format = data;
30898c2ecf20Sopenharmony_ci
30908c2ecf20Sopenharmony_ci	clear_format_track(format, cqr->basedev->block);
30918c2ecf20Sopenharmony_ci	private->count--;
30928c2ecf20Sopenharmony_ci	dasd_ffree_request(cqr, device);
30938c2ecf20Sopenharmony_ci}
30948c2ecf20Sopenharmony_ci
30958c2ecf20Sopenharmony_cistatic struct dasd_ccw_req *
30968c2ecf20Sopenharmony_cidasd_eckd_ese_format(struct dasd_device *startdev, struct dasd_ccw_req *cqr,
30978c2ecf20Sopenharmony_ci		     struct irb *irb)
30988c2ecf20Sopenharmony_ci{
30998c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private;
31008c2ecf20Sopenharmony_ci	struct dasd_format_entry *format;
31018c2ecf20Sopenharmony_ci	struct format_data_t fdata;
31028c2ecf20Sopenharmony_ci	unsigned int recs_per_trk;
31038c2ecf20Sopenharmony_ci	struct dasd_ccw_req *fcqr;
31048c2ecf20Sopenharmony_ci	struct dasd_device *base;
31058c2ecf20Sopenharmony_ci	struct dasd_block *block;
31068c2ecf20Sopenharmony_ci	unsigned int blksize;
31078c2ecf20Sopenharmony_ci	struct request *req;
31088c2ecf20Sopenharmony_ci	sector_t first_trk;
31098c2ecf20Sopenharmony_ci	sector_t last_trk;
31108c2ecf20Sopenharmony_ci	sector_t curr_trk;
31118c2ecf20Sopenharmony_ci	int rc;
31128c2ecf20Sopenharmony_ci
31138c2ecf20Sopenharmony_ci	req = dasd_get_callback_data(cqr);
31148c2ecf20Sopenharmony_ci	block = cqr->block;
31158c2ecf20Sopenharmony_ci	base = block->base;
31168c2ecf20Sopenharmony_ci	private = base->private;
31178c2ecf20Sopenharmony_ci	blksize = block->bp_block;
31188c2ecf20Sopenharmony_ci	recs_per_trk = recs_per_track(&private->rdc_data, 0, blksize);
31198c2ecf20Sopenharmony_ci	format = &startdev->format_entry;
31208c2ecf20Sopenharmony_ci
31218c2ecf20Sopenharmony_ci	first_trk = blk_rq_pos(req) >> block->s2b_shift;
31228c2ecf20Sopenharmony_ci	sector_div(first_trk, recs_per_trk);
31238c2ecf20Sopenharmony_ci	last_trk =
31248c2ecf20Sopenharmony_ci		(blk_rq_pos(req) + blk_rq_sectors(req) - 1) >> block->s2b_shift;
31258c2ecf20Sopenharmony_ci	sector_div(last_trk, recs_per_trk);
31268c2ecf20Sopenharmony_ci	rc = dasd_eckd_track_from_irb(irb, base, &curr_trk);
31278c2ecf20Sopenharmony_ci	if (rc)
31288c2ecf20Sopenharmony_ci		return ERR_PTR(rc);
31298c2ecf20Sopenharmony_ci
31308c2ecf20Sopenharmony_ci	if (curr_trk < first_trk || curr_trk > last_trk) {
31318c2ecf20Sopenharmony_ci		DBF_DEV_EVENT(DBF_WARNING, startdev,
31328c2ecf20Sopenharmony_ci			      "ESE error track %llu not within range %llu - %llu\n",
31338c2ecf20Sopenharmony_ci			      curr_trk, first_trk, last_trk);
31348c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
31358c2ecf20Sopenharmony_ci	}
31368c2ecf20Sopenharmony_ci	format->track = curr_trk;
31378c2ecf20Sopenharmony_ci	/* test if track is already in formatting by another thread */
31388c2ecf20Sopenharmony_ci	if (test_and_set_format_track(format, cqr)) {
31398c2ecf20Sopenharmony_ci		/* this is no real error so do not count down retries */
31408c2ecf20Sopenharmony_ci		cqr->retries++;
31418c2ecf20Sopenharmony_ci		return ERR_PTR(-EEXIST);
31428c2ecf20Sopenharmony_ci	}
31438c2ecf20Sopenharmony_ci
31448c2ecf20Sopenharmony_ci	fdata.start_unit = curr_trk;
31458c2ecf20Sopenharmony_ci	fdata.stop_unit = curr_trk;
31468c2ecf20Sopenharmony_ci	fdata.blksize = blksize;
31478c2ecf20Sopenharmony_ci	fdata.intensity = private->uses_cdl ? DASD_FMT_INT_COMPAT : 0;
31488c2ecf20Sopenharmony_ci
31498c2ecf20Sopenharmony_ci	rc = dasd_eckd_format_sanity_checks(base, &fdata);
31508c2ecf20Sopenharmony_ci	if (rc)
31518c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
31528c2ecf20Sopenharmony_ci
31538c2ecf20Sopenharmony_ci	/*
31548c2ecf20Sopenharmony_ci	 * We're building the request with PAV disabled as we're reusing
31558c2ecf20Sopenharmony_ci	 * the former startdev.
31568c2ecf20Sopenharmony_ci	 */
31578c2ecf20Sopenharmony_ci	fcqr = dasd_eckd_build_format(base, startdev, &fdata, 0);
31588c2ecf20Sopenharmony_ci	if (IS_ERR(fcqr))
31598c2ecf20Sopenharmony_ci		return fcqr;
31608c2ecf20Sopenharmony_ci
31618c2ecf20Sopenharmony_ci	fcqr->callback = dasd_eckd_ese_format_cb;
31628c2ecf20Sopenharmony_ci	fcqr->callback_data = (void *) format;
31638c2ecf20Sopenharmony_ci
31648c2ecf20Sopenharmony_ci	return fcqr;
31658c2ecf20Sopenharmony_ci}
31668c2ecf20Sopenharmony_ci
31678c2ecf20Sopenharmony_ci/*
31688c2ecf20Sopenharmony_ci * When data is read from an unformatted area of an ESE volume, this function
31698c2ecf20Sopenharmony_ci * returns zeroed data and thereby mimics a read of zero data.
31708c2ecf20Sopenharmony_ci *
31718c2ecf20Sopenharmony_ci * The first unformatted track is the one that got the NRF error, the address is
31728c2ecf20Sopenharmony_ci * encoded in the sense data.
31738c2ecf20Sopenharmony_ci *
31748c2ecf20Sopenharmony_ci * All tracks before have returned valid data and should not be touched.
31758c2ecf20Sopenharmony_ci * All tracks after the unformatted track might be formatted or not. This is
31768c2ecf20Sopenharmony_ci * currently not known, remember the processed data and return the remainder of
31778c2ecf20Sopenharmony_ci * the request to the blocklayer in __dasd_cleanup_cqr().
31788c2ecf20Sopenharmony_ci */
31798c2ecf20Sopenharmony_cistatic int dasd_eckd_ese_read(struct dasd_ccw_req *cqr, struct irb *irb)
31808c2ecf20Sopenharmony_ci{
31818c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private;
31828c2ecf20Sopenharmony_ci	sector_t first_trk, last_trk;
31838c2ecf20Sopenharmony_ci	sector_t first_blk, last_blk;
31848c2ecf20Sopenharmony_ci	unsigned int blksize, off;
31858c2ecf20Sopenharmony_ci	unsigned int recs_per_trk;
31868c2ecf20Sopenharmony_ci	struct dasd_device *base;
31878c2ecf20Sopenharmony_ci	struct req_iterator iter;
31888c2ecf20Sopenharmony_ci	struct dasd_block *block;
31898c2ecf20Sopenharmony_ci	unsigned int skip_block;
31908c2ecf20Sopenharmony_ci	unsigned int blk_count;
31918c2ecf20Sopenharmony_ci	struct request *req;
31928c2ecf20Sopenharmony_ci	struct bio_vec bv;
31938c2ecf20Sopenharmony_ci	sector_t curr_trk;
31948c2ecf20Sopenharmony_ci	sector_t end_blk;
31958c2ecf20Sopenharmony_ci	char *dst;
31968c2ecf20Sopenharmony_ci	int rc;
31978c2ecf20Sopenharmony_ci
31988c2ecf20Sopenharmony_ci	req = (struct request *) cqr->callback_data;
31998c2ecf20Sopenharmony_ci	base = cqr->block->base;
32008c2ecf20Sopenharmony_ci	blksize = base->block->bp_block;
32018c2ecf20Sopenharmony_ci	block =  cqr->block;
32028c2ecf20Sopenharmony_ci	private = base->private;
32038c2ecf20Sopenharmony_ci	skip_block = 0;
32048c2ecf20Sopenharmony_ci	blk_count = 0;
32058c2ecf20Sopenharmony_ci
32068c2ecf20Sopenharmony_ci	recs_per_trk = recs_per_track(&private->rdc_data, 0, blksize);
32078c2ecf20Sopenharmony_ci	first_trk = first_blk = blk_rq_pos(req) >> block->s2b_shift;
32088c2ecf20Sopenharmony_ci	sector_div(first_trk, recs_per_trk);
32098c2ecf20Sopenharmony_ci	last_trk = last_blk =
32108c2ecf20Sopenharmony_ci		(blk_rq_pos(req) + blk_rq_sectors(req) - 1) >> block->s2b_shift;
32118c2ecf20Sopenharmony_ci	sector_div(last_trk, recs_per_trk);
32128c2ecf20Sopenharmony_ci	rc = dasd_eckd_track_from_irb(irb, base, &curr_trk);
32138c2ecf20Sopenharmony_ci	if (rc)
32148c2ecf20Sopenharmony_ci		return rc;
32158c2ecf20Sopenharmony_ci
32168c2ecf20Sopenharmony_ci	/* sanity check if the current track from sense data is valid */
32178c2ecf20Sopenharmony_ci	if (curr_trk < first_trk || curr_trk > last_trk) {
32188c2ecf20Sopenharmony_ci		DBF_DEV_EVENT(DBF_WARNING, base,
32198c2ecf20Sopenharmony_ci			      "ESE error track %llu not within range %llu - %llu\n",
32208c2ecf20Sopenharmony_ci			      curr_trk, first_trk, last_trk);
32218c2ecf20Sopenharmony_ci		return -EINVAL;
32228c2ecf20Sopenharmony_ci	}
32238c2ecf20Sopenharmony_ci
32248c2ecf20Sopenharmony_ci	/*
32258c2ecf20Sopenharmony_ci	 * if not the first track got the NRF error we have to skip over valid
32268c2ecf20Sopenharmony_ci	 * blocks
32278c2ecf20Sopenharmony_ci	 */
32288c2ecf20Sopenharmony_ci	if (curr_trk != first_trk)
32298c2ecf20Sopenharmony_ci		skip_block = curr_trk * recs_per_trk - first_blk;
32308c2ecf20Sopenharmony_ci
32318c2ecf20Sopenharmony_ci	/* we have no information beyond the current track */
32328c2ecf20Sopenharmony_ci	end_blk = (curr_trk + 1) * recs_per_trk;
32338c2ecf20Sopenharmony_ci
32348c2ecf20Sopenharmony_ci	rq_for_each_segment(bv, req, iter) {
32358c2ecf20Sopenharmony_ci		dst = page_address(bv.bv_page) + bv.bv_offset;
32368c2ecf20Sopenharmony_ci		for (off = 0; off < bv.bv_len; off += blksize) {
32378c2ecf20Sopenharmony_ci			if (first_blk + blk_count >= end_blk) {
32388c2ecf20Sopenharmony_ci				cqr->proc_bytes = blk_count * blksize;
32398c2ecf20Sopenharmony_ci				return 0;
32408c2ecf20Sopenharmony_ci			}
32418c2ecf20Sopenharmony_ci			if (dst && !skip_block)
32428c2ecf20Sopenharmony_ci				memset(dst, 0, blksize);
32438c2ecf20Sopenharmony_ci			else
32448c2ecf20Sopenharmony_ci				skip_block--;
32458c2ecf20Sopenharmony_ci			dst += blksize;
32468c2ecf20Sopenharmony_ci			blk_count++;
32478c2ecf20Sopenharmony_ci		}
32488c2ecf20Sopenharmony_ci	}
32498c2ecf20Sopenharmony_ci	return 0;
32508c2ecf20Sopenharmony_ci}
32518c2ecf20Sopenharmony_ci
32528c2ecf20Sopenharmony_ci/*
32538c2ecf20Sopenharmony_ci * Helper function to count consecutive records of a single track.
32548c2ecf20Sopenharmony_ci */
32558c2ecf20Sopenharmony_cistatic int dasd_eckd_count_records(struct eckd_count *fmt_buffer, int start,
32568c2ecf20Sopenharmony_ci				   int max)
32578c2ecf20Sopenharmony_ci{
32588c2ecf20Sopenharmony_ci	int head;
32598c2ecf20Sopenharmony_ci	int i;
32608c2ecf20Sopenharmony_ci
32618c2ecf20Sopenharmony_ci	head = fmt_buffer[start].head;
32628c2ecf20Sopenharmony_ci
32638c2ecf20Sopenharmony_ci	/*
32648c2ecf20Sopenharmony_ci	 * There are 3 conditions where we stop counting:
32658c2ecf20Sopenharmony_ci	 * - if data reoccurs (same head and record may reoccur), which may
32668c2ecf20Sopenharmony_ci	 *   happen due to the way DASD_ECKD_CCW_READ_COUNT works
32678c2ecf20Sopenharmony_ci	 * - when the head changes, because we're iterating over several tracks
32688c2ecf20Sopenharmony_ci	 *   then (DASD_ECKD_CCW_READ_COUNT_MT)
32698c2ecf20Sopenharmony_ci	 * - when we've reached the end of sensible data in the buffer (the
32708c2ecf20Sopenharmony_ci	 *   record will be 0 then)
32718c2ecf20Sopenharmony_ci	 */
32728c2ecf20Sopenharmony_ci	for (i = start; i < max; i++) {
32738c2ecf20Sopenharmony_ci		if (i > start) {
32748c2ecf20Sopenharmony_ci			if ((fmt_buffer[i].head == head &&
32758c2ecf20Sopenharmony_ci			    fmt_buffer[i].record == 1) ||
32768c2ecf20Sopenharmony_ci			    fmt_buffer[i].head != head ||
32778c2ecf20Sopenharmony_ci			    fmt_buffer[i].record == 0)
32788c2ecf20Sopenharmony_ci				break;
32798c2ecf20Sopenharmony_ci		}
32808c2ecf20Sopenharmony_ci	}
32818c2ecf20Sopenharmony_ci
32828c2ecf20Sopenharmony_ci	return i - start;
32838c2ecf20Sopenharmony_ci}
32848c2ecf20Sopenharmony_ci
32858c2ecf20Sopenharmony_ci/*
32868c2ecf20Sopenharmony_ci * Evaluate a given range of tracks. Data like number of records, blocksize,
32878c2ecf20Sopenharmony_ci * record ids, and key length are compared with expected data.
32888c2ecf20Sopenharmony_ci *
32898c2ecf20Sopenharmony_ci * If a mismatch occurs, the corresponding error bit is set, as well as
32908c2ecf20Sopenharmony_ci * additional information, depending on the error.
32918c2ecf20Sopenharmony_ci */
32928c2ecf20Sopenharmony_cistatic void dasd_eckd_format_evaluate_tracks(struct eckd_count *fmt_buffer,
32938c2ecf20Sopenharmony_ci					     struct format_check_t *cdata,
32948c2ecf20Sopenharmony_ci					     int rpt_max, int rpt_exp,
32958c2ecf20Sopenharmony_ci					     int trk_per_cyl, int tpm)
32968c2ecf20Sopenharmony_ci{
32978c2ecf20Sopenharmony_ci	struct ch_t geo;
32988c2ecf20Sopenharmony_ci	int max_entries;
32998c2ecf20Sopenharmony_ci	int count = 0;
33008c2ecf20Sopenharmony_ci	int trkcount;
33018c2ecf20Sopenharmony_ci	int blksize;
33028c2ecf20Sopenharmony_ci	int pos = 0;
33038c2ecf20Sopenharmony_ci	int i, j;
33048c2ecf20Sopenharmony_ci	int kl;
33058c2ecf20Sopenharmony_ci
33068c2ecf20Sopenharmony_ci	trkcount = cdata->expect.stop_unit - cdata->expect.start_unit + 1;
33078c2ecf20Sopenharmony_ci	max_entries = trkcount * rpt_max;
33088c2ecf20Sopenharmony_ci
33098c2ecf20Sopenharmony_ci	for (i = cdata->expect.start_unit; i <= cdata->expect.stop_unit; i++) {
33108c2ecf20Sopenharmony_ci		/* Calculate the correct next starting position in the buffer */
33118c2ecf20Sopenharmony_ci		if (tpm) {
33128c2ecf20Sopenharmony_ci			while (fmt_buffer[pos].record == 0 &&
33138c2ecf20Sopenharmony_ci			       fmt_buffer[pos].dl == 0) {
33148c2ecf20Sopenharmony_ci				if (pos++ > max_entries)
33158c2ecf20Sopenharmony_ci					break;
33168c2ecf20Sopenharmony_ci			}
33178c2ecf20Sopenharmony_ci		} else {
33188c2ecf20Sopenharmony_ci			if (i != cdata->expect.start_unit)
33198c2ecf20Sopenharmony_ci				pos += rpt_max - count;
33208c2ecf20Sopenharmony_ci		}
33218c2ecf20Sopenharmony_ci
33228c2ecf20Sopenharmony_ci		/* Calculate the expected geo values for the current track */
33238c2ecf20Sopenharmony_ci		set_ch_t(&geo, i / trk_per_cyl, i % trk_per_cyl);
33248c2ecf20Sopenharmony_ci
33258c2ecf20Sopenharmony_ci		/* Count and check number of records */
33268c2ecf20Sopenharmony_ci		count = dasd_eckd_count_records(fmt_buffer, pos, pos + rpt_max);
33278c2ecf20Sopenharmony_ci
33288c2ecf20Sopenharmony_ci		if (count < rpt_exp) {
33298c2ecf20Sopenharmony_ci			cdata->result = DASD_FMT_ERR_TOO_FEW_RECORDS;
33308c2ecf20Sopenharmony_ci			break;
33318c2ecf20Sopenharmony_ci		}
33328c2ecf20Sopenharmony_ci		if (count > rpt_exp) {
33338c2ecf20Sopenharmony_ci			cdata->result = DASD_FMT_ERR_TOO_MANY_RECORDS;
33348c2ecf20Sopenharmony_ci			break;
33358c2ecf20Sopenharmony_ci		}
33368c2ecf20Sopenharmony_ci
33378c2ecf20Sopenharmony_ci		for (j = 0; j < count; j++, pos++) {
33388c2ecf20Sopenharmony_ci			blksize = cdata->expect.blksize;
33398c2ecf20Sopenharmony_ci			kl = 0;
33408c2ecf20Sopenharmony_ci
33418c2ecf20Sopenharmony_ci			/*
33428c2ecf20Sopenharmony_ci			 * Set special values when checking CDL formatted
33438c2ecf20Sopenharmony_ci			 * devices.
33448c2ecf20Sopenharmony_ci			 */
33458c2ecf20Sopenharmony_ci			if ((cdata->expect.intensity & 0x08) &&
33468c2ecf20Sopenharmony_ci			    geo.cyl == 0 && geo.head == 0) {
33478c2ecf20Sopenharmony_ci				if (j < 3) {
33488c2ecf20Sopenharmony_ci					blksize = sizes_trk0[j] - 4;
33498c2ecf20Sopenharmony_ci					kl = 4;
33508c2ecf20Sopenharmony_ci				}
33518c2ecf20Sopenharmony_ci			}
33528c2ecf20Sopenharmony_ci			if ((cdata->expect.intensity & 0x08) &&
33538c2ecf20Sopenharmony_ci			    geo.cyl == 0 && geo.head == 1) {
33548c2ecf20Sopenharmony_ci				blksize = LABEL_SIZE - 44;
33558c2ecf20Sopenharmony_ci				kl = 44;
33568c2ecf20Sopenharmony_ci			}
33578c2ecf20Sopenharmony_ci
33588c2ecf20Sopenharmony_ci			/* Check blocksize */
33598c2ecf20Sopenharmony_ci			if (fmt_buffer[pos].dl != blksize) {
33608c2ecf20Sopenharmony_ci				cdata->result = DASD_FMT_ERR_BLKSIZE;
33618c2ecf20Sopenharmony_ci				goto out;
33628c2ecf20Sopenharmony_ci			}
33638c2ecf20Sopenharmony_ci			/* Check if key length is 0 */
33648c2ecf20Sopenharmony_ci			if (fmt_buffer[pos].kl != kl) {
33658c2ecf20Sopenharmony_ci				cdata->result = DASD_FMT_ERR_KEY_LENGTH;
33668c2ecf20Sopenharmony_ci				goto out;
33678c2ecf20Sopenharmony_ci			}
33688c2ecf20Sopenharmony_ci			/* Check if record_id is correct */
33698c2ecf20Sopenharmony_ci			if (fmt_buffer[pos].cyl != geo.cyl ||
33708c2ecf20Sopenharmony_ci			    fmt_buffer[pos].head != geo.head ||
33718c2ecf20Sopenharmony_ci			    fmt_buffer[pos].record != (j + 1)) {
33728c2ecf20Sopenharmony_ci				cdata->result = DASD_FMT_ERR_RECORD_ID;
33738c2ecf20Sopenharmony_ci				goto out;
33748c2ecf20Sopenharmony_ci			}
33758c2ecf20Sopenharmony_ci		}
33768c2ecf20Sopenharmony_ci	}
33778c2ecf20Sopenharmony_ci
33788c2ecf20Sopenharmony_ciout:
33798c2ecf20Sopenharmony_ci	/*
33808c2ecf20Sopenharmony_ci	 * In case of no errors, we need to decrease by one
33818c2ecf20Sopenharmony_ci	 * to get the correct positions.
33828c2ecf20Sopenharmony_ci	 */
33838c2ecf20Sopenharmony_ci	if (!cdata->result) {
33848c2ecf20Sopenharmony_ci		i--;
33858c2ecf20Sopenharmony_ci		pos--;
33868c2ecf20Sopenharmony_ci	}
33878c2ecf20Sopenharmony_ci
33888c2ecf20Sopenharmony_ci	cdata->unit = i;
33898c2ecf20Sopenharmony_ci	cdata->num_records = count;
33908c2ecf20Sopenharmony_ci	cdata->rec = fmt_buffer[pos].record;
33918c2ecf20Sopenharmony_ci	cdata->blksize = fmt_buffer[pos].dl;
33928c2ecf20Sopenharmony_ci	cdata->key_length = fmt_buffer[pos].kl;
33938c2ecf20Sopenharmony_ci}
33948c2ecf20Sopenharmony_ci
33958c2ecf20Sopenharmony_ci/*
33968c2ecf20Sopenharmony_ci * Check the format of a range of tracks of a DASD.
33978c2ecf20Sopenharmony_ci */
33988c2ecf20Sopenharmony_cistatic int dasd_eckd_check_device_format(struct dasd_device *base,
33998c2ecf20Sopenharmony_ci					 struct format_check_t *cdata,
34008c2ecf20Sopenharmony_ci					 int enable_pav)
34018c2ecf20Sopenharmony_ci{
34028c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private = base->private;
34038c2ecf20Sopenharmony_ci	struct eckd_count *fmt_buffer;
34048c2ecf20Sopenharmony_ci	struct irb irb;
34058c2ecf20Sopenharmony_ci	int rpt_max, rpt_exp;
34068c2ecf20Sopenharmony_ci	int fmt_buffer_size;
34078c2ecf20Sopenharmony_ci	int trk_per_cyl;
34088c2ecf20Sopenharmony_ci	int trkcount;
34098c2ecf20Sopenharmony_ci	int tpm = 0;
34108c2ecf20Sopenharmony_ci	int rc;
34118c2ecf20Sopenharmony_ci
34128c2ecf20Sopenharmony_ci	trk_per_cyl = private->rdc_data.trk_per_cyl;
34138c2ecf20Sopenharmony_ci
34148c2ecf20Sopenharmony_ci	/* Get maximum and expected amount of records per track */
34158c2ecf20Sopenharmony_ci	rpt_max = recs_per_track(&private->rdc_data, 0, 512) + 1;
34168c2ecf20Sopenharmony_ci	rpt_exp = recs_per_track(&private->rdc_data, 0, cdata->expect.blksize);
34178c2ecf20Sopenharmony_ci
34188c2ecf20Sopenharmony_ci	trkcount = cdata->expect.stop_unit - cdata->expect.start_unit + 1;
34198c2ecf20Sopenharmony_ci	fmt_buffer_size = trkcount * rpt_max * sizeof(struct eckd_count);
34208c2ecf20Sopenharmony_ci
34218c2ecf20Sopenharmony_ci	fmt_buffer = kzalloc(fmt_buffer_size, GFP_KERNEL | GFP_DMA);
34228c2ecf20Sopenharmony_ci	if (!fmt_buffer)
34238c2ecf20Sopenharmony_ci		return -ENOMEM;
34248c2ecf20Sopenharmony_ci
34258c2ecf20Sopenharmony_ci	/*
34268c2ecf20Sopenharmony_ci	 * A certain FICON feature subset is needed to operate in transport
34278c2ecf20Sopenharmony_ci	 * mode. Additionally, the support for transport mode is implicitly
34288c2ecf20Sopenharmony_ci	 * checked by comparing the buffer size with fcx_max_data. As long as
34298c2ecf20Sopenharmony_ci	 * the buffer size is smaller we can operate in transport mode and
34308c2ecf20Sopenharmony_ci	 * process multiple tracks. If not, only one track at once is being
34318c2ecf20Sopenharmony_ci	 * processed using command mode.
34328c2ecf20Sopenharmony_ci	 */
34338c2ecf20Sopenharmony_ci	if ((private->features.feature[40] & 0x04) &&
34348c2ecf20Sopenharmony_ci	    fmt_buffer_size <= private->fcx_max_data)
34358c2ecf20Sopenharmony_ci		tpm = 1;
34368c2ecf20Sopenharmony_ci
34378c2ecf20Sopenharmony_ci	rc = dasd_eckd_format_process_data(base, &cdata->expect, enable_pav,
34388c2ecf20Sopenharmony_ci					   tpm, fmt_buffer, rpt_max, &irb);
34398c2ecf20Sopenharmony_ci	if (rc && rc != -EIO)
34408c2ecf20Sopenharmony_ci		goto out;
34418c2ecf20Sopenharmony_ci	if (rc == -EIO) {
34428c2ecf20Sopenharmony_ci		/*
34438c2ecf20Sopenharmony_ci		 * If our first attempt with transport mode enabled comes back
34448c2ecf20Sopenharmony_ci		 * with an incorrect length error, we're going to retry the
34458c2ecf20Sopenharmony_ci		 * check with command mode.
34468c2ecf20Sopenharmony_ci		 */
34478c2ecf20Sopenharmony_ci		if (tpm && scsw_cstat(&irb.scsw) == 0x40) {
34488c2ecf20Sopenharmony_ci			tpm = 0;
34498c2ecf20Sopenharmony_ci			rc = dasd_eckd_format_process_data(base, &cdata->expect,
34508c2ecf20Sopenharmony_ci							   enable_pav, tpm,
34518c2ecf20Sopenharmony_ci							   fmt_buffer, rpt_max,
34528c2ecf20Sopenharmony_ci							   &irb);
34538c2ecf20Sopenharmony_ci			if (rc)
34548c2ecf20Sopenharmony_ci				goto out;
34558c2ecf20Sopenharmony_ci		} else {
34568c2ecf20Sopenharmony_ci			goto out;
34578c2ecf20Sopenharmony_ci		}
34588c2ecf20Sopenharmony_ci	}
34598c2ecf20Sopenharmony_ci
34608c2ecf20Sopenharmony_ci	dasd_eckd_format_evaluate_tracks(fmt_buffer, cdata, rpt_max, rpt_exp,
34618c2ecf20Sopenharmony_ci					 trk_per_cyl, tpm);
34628c2ecf20Sopenharmony_ci
34638c2ecf20Sopenharmony_ciout:
34648c2ecf20Sopenharmony_ci	kfree(fmt_buffer);
34658c2ecf20Sopenharmony_ci
34668c2ecf20Sopenharmony_ci	return rc;
34678c2ecf20Sopenharmony_ci}
34688c2ecf20Sopenharmony_ci
34698c2ecf20Sopenharmony_cistatic void dasd_eckd_handle_terminated_request(struct dasd_ccw_req *cqr)
34708c2ecf20Sopenharmony_ci{
34718c2ecf20Sopenharmony_ci	if (cqr->retries < 0) {
34728c2ecf20Sopenharmony_ci		cqr->status = DASD_CQR_FAILED;
34738c2ecf20Sopenharmony_ci		return;
34748c2ecf20Sopenharmony_ci	}
34758c2ecf20Sopenharmony_ci	cqr->status = DASD_CQR_FILLED;
34768c2ecf20Sopenharmony_ci	if (cqr->block && (cqr->startdev != cqr->block->base)) {
34778c2ecf20Sopenharmony_ci		dasd_eckd_reset_ccw_to_base_io(cqr);
34788c2ecf20Sopenharmony_ci		cqr->startdev = cqr->block->base;
34798c2ecf20Sopenharmony_ci		cqr->lpm = dasd_path_get_opm(cqr->block->base);
34808c2ecf20Sopenharmony_ci	}
34818c2ecf20Sopenharmony_ci};
34828c2ecf20Sopenharmony_ci
34838c2ecf20Sopenharmony_cistatic dasd_erp_fn_t
34848c2ecf20Sopenharmony_cidasd_eckd_erp_action(struct dasd_ccw_req * cqr)
34858c2ecf20Sopenharmony_ci{
34868c2ecf20Sopenharmony_ci	struct dasd_device *device = (struct dasd_device *) cqr->startdev;
34878c2ecf20Sopenharmony_ci	struct ccw_device *cdev = device->cdev;
34888c2ecf20Sopenharmony_ci
34898c2ecf20Sopenharmony_ci	switch (cdev->id.cu_type) {
34908c2ecf20Sopenharmony_ci	case 0x3990:
34918c2ecf20Sopenharmony_ci	case 0x2105:
34928c2ecf20Sopenharmony_ci	case 0x2107:
34938c2ecf20Sopenharmony_ci	case 0x1750:
34948c2ecf20Sopenharmony_ci		return dasd_3990_erp_action;
34958c2ecf20Sopenharmony_ci	case 0x9343:
34968c2ecf20Sopenharmony_ci	case 0x3880:
34978c2ecf20Sopenharmony_ci	default:
34988c2ecf20Sopenharmony_ci		return dasd_default_erp_action;
34998c2ecf20Sopenharmony_ci	}
35008c2ecf20Sopenharmony_ci}
35018c2ecf20Sopenharmony_ci
35028c2ecf20Sopenharmony_cistatic dasd_erp_fn_t
35038c2ecf20Sopenharmony_cidasd_eckd_erp_postaction(struct dasd_ccw_req * cqr)
35048c2ecf20Sopenharmony_ci{
35058c2ecf20Sopenharmony_ci	return dasd_default_erp_postaction;
35068c2ecf20Sopenharmony_ci}
35078c2ecf20Sopenharmony_ci
35088c2ecf20Sopenharmony_cistatic void dasd_eckd_check_for_device_change(struct dasd_device *device,
35098c2ecf20Sopenharmony_ci					      struct dasd_ccw_req *cqr,
35108c2ecf20Sopenharmony_ci					      struct irb *irb)
35118c2ecf20Sopenharmony_ci{
35128c2ecf20Sopenharmony_ci	char mask;
35138c2ecf20Sopenharmony_ci	char *sense = NULL;
35148c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
35158c2ecf20Sopenharmony_ci
35168c2ecf20Sopenharmony_ci	/* first of all check for state change pending interrupt */
35178c2ecf20Sopenharmony_ci	mask = DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP;
35188c2ecf20Sopenharmony_ci	if ((scsw_dstat(&irb->scsw) & mask) == mask) {
35198c2ecf20Sopenharmony_ci		/*
35208c2ecf20Sopenharmony_ci		 * for alias only, not in offline processing
35218c2ecf20Sopenharmony_ci		 * and only if not suspended
35228c2ecf20Sopenharmony_ci		 */
35238c2ecf20Sopenharmony_ci		if (!device->block && private->lcu &&
35248c2ecf20Sopenharmony_ci		    device->state == DASD_STATE_ONLINE &&
35258c2ecf20Sopenharmony_ci		    !test_bit(DASD_FLAG_OFFLINE, &device->flags) &&
35268c2ecf20Sopenharmony_ci		    !test_bit(DASD_FLAG_SUSPENDED, &device->flags)) {
35278c2ecf20Sopenharmony_ci			/* schedule worker to reload device */
35288c2ecf20Sopenharmony_ci			dasd_reload_device(device);
35298c2ecf20Sopenharmony_ci		}
35308c2ecf20Sopenharmony_ci		dasd_generic_handle_state_change(device);
35318c2ecf20Sopenharmony_ci		return;
35328c2ecf20Sopenharmony_ci	}
35338c2ecf20Sopenharmony_ci
35348c2ecf20Sopenharmony_ci	sense = dasd_get_sense(irb);
35358c2ecf20Sopenharmony_ci	if (!sense)
35368c2ecf20Sopenharmony_ci		return;
35378c2ecf20Sopenharmony_ci
35388c2ecf20Sopenharmony_ci	/* summary unit check */
35398c2ecf20Sopenharmony_ci	if ((sense[27] & DASD_SENSE_BIT_0) && (sense[7] == 0x0D) &&
35408c2ecf20Sopenharmony_ci	    (scsw_dstat(&irb->scsw) & DEV_STAT_UNIT_CHECK)) {
35418c2ecf20Sopenharmony_ci		if (test_and_set_bit(DASD_FLAG_SUC, &device->flags)) {
35428c2ecf20Sopenharmony_ci			DBF_DEV_EVENT(DBF_WARNING, device, "%s",
35438c2ecf20Sopenharmony_ci				      "eckd suc: device already notified");
35448c2ecf20Sopenharmony_ci			return;
35458c2ecf20Sopenharmony_ci		}
35468c2ecf20Sopenharmony_ci		sense = dasd_get_sense(irb);
35478c2ecf20Sopenharmony_ci		if (!sense) {
35488c2ecf20Sopenharmony_ci			DBF_DEV_EVENT(DBF_WARNING, device, "%s",
35498c2ecf20Sopenharmony_ci				      "eckd suc: no reason code available");
35508c2ecf20Sopenharmony_ci			clear_bit(DASD_FLAG_SUC, &device->flags);
35518c2ecf20Sopenharmony_ci			return;
35528c2ecf20Sopenharmony_ci
35538c2ecf20Sopenharmony_ci		}
35548c2ecf20Sopenharmony_ci		private->suc_reason = sense[8];
35558c2ecf20Sopenharmony_ci		DBF_DEV_EVENT(DBF_NOTICE, device, "%s %x",
35568c2ecf20Sopenharmony_ci			      "eckd handle summary unit check: reason",
35578c2ecf20Sopenharmony_ci			      private->suc_reason);
35588c2ecf20Sopenharmony_ci		dasd_get_device(device);
35598c2ecf20Sopenharmony_ci		if (!schedule_work(&device->suc_work))
35608c2ecf20Sopenharmony_ci			dasd_put_device(device);
35618c2ecf20Sopenharmony_ci
35628c2ecf20Sopenharmony_ci		return;
35638c2ecf20Sopenharmony_ci	}
35648c2ecf20Sopenharmony_ci
35658c2ecf20Sopenharmony_ci	/* service information message SIM */
35668c2ecf20Sopenharmony_ci	if (!cqr && !(sense[27] & DASD_SENSE_BIT_0) &&
35678c2ecf20Sopenharmony_ci	    ((sense[6] & DASD_SIM_SENSE) == DASD_SIM_SENSE)) {
35688c2ecf20Sopenharmony_ci		dasd_3990_erp_handle_sim(device, sense);
35698c2ecf20Sopenharmony_ci		return;
35708c2ecf20Sopenharmony_ci	}
35718c2ecf20Sopenharmony_ci
35728c2ecf20Sopenharmony_ci	/* loss of device reservation is handled via base devices only
35738c2ecf20Sopenharmony_ci	 * as alias devices may be used with several bases
35748c2ecf20Sopenharmony_ci	 */
35758c2ecf20Sopenharmony_ci	if (device->block && (sense[27] & DASD_SENSE_BIT_0) &&
35768c2ecf20Sopenharmony_ci	    (sense[7] == 0x3F) &&
35778c2ecf20Sopenharmony_ci	    (scsw_dstat(&irb->scsw) & DEV_STAT_UNIT_CHECK) &&
35788c2ecf20Sopenharmony_ci	    test_bit(DASD_FLAG_IS_RESERVED, &device->flags)) {
35798c2ecf20Sopenharmony_ci		if (device->features & DASD_FEATURE_FAILONSLCK)
35808c2ecf20Sopenharmony_ci			set_bit(DASD_FLAG_LOCK_STOLEN, &device->flags);
35818c2ecf20Sopenharmony_ci		clear_bit(DASD_FLAG_IS_RESERVED, &device->flags);
35828c2ecf20Sopenharmony_ci		dev_err(&device->cdev->dev,
35838c2ecf20Sopenharmony_ci			"The device reservation was lost\n");
35848c2ecf20Sopenharmony_ci	}
35858c2ecf20Sopenharmony_ci}
35868c2ecf20Sopenharmony_ci
35878c2ecf20Sopenharmony_cistatic int dasd_eckd_ras_sanity_checks(struct dasd_device *device,
35888c2ecf20Sopenharmony_ci				       unsigned int first_trk,
35898c2ecf20Sopenharmony_ci				       unsigned int last_trk)
35908c2ecf20Sopenharmony_ci{
35918c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
35928c2ecf20Sopenharmony_ci	unsigned int trks_per_vol;
35938c2ecf20Sopenharmony_ci	int rc = 0;
35948c2ecf20Sopenharmony_ci
35958c2ecf20Sopenharmony_ci	trks_per_vol = private->real_cyl * private->rdc_data.trk_per_cyl;
35968c2ecf20Sopenharmony_ci
35978c2ecf20Sopenharmony_ci	if (first_trk >= trks_per_vol) {
35988c2ecf20Sopenharmony_ci		dev_warn(&device->cdev->dev,
35998c2ecf20Sopenharmony_ci			 "Start track number %u used in the space release command is too big\n",
36008c2ecf20Sopenharmony_ci			 first_trk);
36018c2ecf20Sopenharmony_ci		rc = -EINVAL;
36028c2ecf20Sopenharmony_ci	} else if (last_trk >= trks_per_vol) {
36038c2ecf20Sopenharmony_ci		dev_warn(&device->cdev->dev,
36048c2ecf20Sopenharmony_ci			 "Stop track number %u used in the space release command is too big\n",
36058c2ecf20Sopenharmony_ci			 last_trk);
36068c2ecf20Sopenharmony_ci		rc = -EINVAL;
36078c2ecf20Sopenharmony_ci	} else if (first_trk > last_trk) {
36088c2ecf20Sopenharmony_ci		dev_warn(&device->cdev->dev,
36098c2ecf20Sopenharmony_ci			 "Start track %u used in the space release command exceeds the end track\n",
36108c2ecf20Sopenharmony_ci			 first_trk);
36118c2ecf20Sopenharmony_ci		rc = -EINVAL;
36128c2ecf20Sopenharmony_ci	}
36138c2ecf20Sopenharmony_ci	return rc;
36148c2ecf20Sopenharmony_ci}
36158c2ecf20Sopenharmony_ci
36168c2ecf20Sopenharmony_ci/*
36178c2ecf20Sopenharmony_ci * Helper function to count the amount of involved extents within a given range
36188c2ecf20Sopenharmony_ci * with extent alignment in mind.
36198c2ecf20Sopenharmony_ci */
36208c2ecf20Sopenharmony_cistatic int count_exts(unsigned int from, unsigned int to, int trks_per_ext)
36218c2ecf20Sopenharmony_ci{
36228c2ecf20Sopenharmony_ci	int cur_pos = 0;
36238c2ecf20Sopenharmony_ci	int count = 0;
36248c2ecf20Sopenharmony_ci	int tmp;
36258c2ecf20Sopenharmony_ci
36268c2ecf20Sopenharmony_ci	if (from == to)
36278c2ecf20Sopenharmony_ci		return 1;
36288c2ecf20Sopenharmony_ci
36298c2ecf20Sopenharmony_ci	/* Count first partial extent */
36308c2ecf20Sopenharmony_ci	if (from % trks_per_ext != 0) {
36318c2ecf20Sopenharmony_ci		tmp = from + trks_per_ext - (from % trks_per_ext) - 1;
36328c2ecf20Sopenharmony_ci		if (tmp > to)
36338c2ecf20Sopenharmony_ci			tmp = to;
36348c2ecf20Sopenharmony_ci		cur_pos = tmp - from + 1;
36358c2ecf20Sopenharmony_ci		count++;
36368c2ecf20Sopenharmony_ci	}
36378c2ecf20Sopenharmony_ci	/* Count full extents */
36388c2ecf20Sopenharmony_ci	if (to - (from + cur_pos) + 1 >= trks_per_ext) {
36398c2ecf20Sopenharmony_ci		tmp = to - ((to - trks_per_ext + 1) % trks_per_ext);
36408c2ecf20Sopenharmony_ci		count += (tmp - (from + cur_pos) + 1) / trks_per_ext;
36418c2ecf20Sopenharmony_ci		cur_pos = tmp;
36428c2ecf20Sopenharmony_ci	}
36438c2ecf20Sopenharmony_ci	/* Count last partial extent */
36448c2ecf20Sopenharmony_ci	if (cur_pos < to)
36458c2ecf20Sopenharmony_ci		count++;
36468c2ecf20Sopenharmony_ci
36478c2ecf20Sopenharmony_ci	return count;
36488c2ecf20Sopenharmony_ci}
36498c2ecf20Sopenharmony_ci
36508c2ecf20Sopenharmony_ci/*
36518c2ecf20Sopenharmony_ci * Release allocated space for a given range or an entire volume.
36528c2ecf20Sopenharmony_ci */
36538c2ecf20Sopenharmony_cistatic struct dasd_ccw_req *
36548c2ecf20Sopenharmony_cidasd_eckd_dso_ras(struct dasd_device *device, struct dasd_block *block,
36558c2ecf20Sopenharmony_ci		  struct request *req, unsigned int first_trk,
36568c2ecf20Sopenharmony_ci		  unsigned int last_trk, int by_extent)
36578c2ecf20Sopenharmony_ci{
36588c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
36598c2ecf20Sopenharmony_ci	struct dasd_dso_ras_ext_range *ras_range;
36608c2ecf20Sopenharmony_ci	struct dasd_rssd_features *features;
36618c2ecf20Sopenharmony_ci	struct dasd_dso_ras_data *ras_data;
36628c2ecf20Sopenharmony_ci	u16 heads, beg_head, end_head;
36638c2ecf20Sopenharmony_ci	int cur_to_trk, cur_from_trk;
36648c2ecf20Sopenharmony_ci	struct dasd_ccw_req *cqr;
36658c2ecf20Sopenharmony_ci	u32 beg_cyl, end_cyl;
36668c2ecf20Sopenharmony_ci	struct ccw1 *ccw;
36678c2ecf20Sopenharmony_ci	int trks_per_ext;
36688c2ecf20Sopenharmony_ci	size_t ras_size;
36698c2ecf20Sopenharmony_ci	size_t size;
36708c2ecf20Sopenharmony_ci	int nr_exts;
36718c2ecf20Sopenharmony_ci	void *rq;
36728c2ecf20Sopenharmony_ci	int i;
36738c2ecf20Sopenharmony_ci
36748c2ecf20Sopenharmony_ci	if (dasd_eckd_ras_sanity_checks(device, first_trk, last_trk))
36758c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
36768c2ecf20Sopenharmony_ci
36778c2ecf20Sopenharmony_ci	rq = req ? blk_mq_rq_to_pdu(req) : NULL;
36788c2ecf20Sopenharmony_ci
36798c2ecf20Sopenharmony_ci	features = &private->features;
36808c2ecf20Sopenharmony_ci
36818c2ecf20Sopenharmony_ci	trks_per_ext = dasd_eckd_ext_size(device) * private->rdc_data.trk_per_cyl;
36828c2ecf20Sopenharmony_ci	nr_exts = 0;
36838c2ecf20Sopenharmony_ci	if (by_extent)
36848c2ecf20Sopenharmony_ci		nr_exts = count_exts(first_trk, last_trk, trks_per_ext);
36858c2ecf20Sopenharmony_ci	ras_size = sizeof(*ras_data);
36868c2ecf20Sopenharmony_ci	size = ras_size + (nr_exts * sizeof(*ras_range));
36878c2ecf20Sopenharmony_ci
36888c2ecf20Sopenharmony_ci	cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1, size, device, rq);
36898c2ecf20Sopenharmony_ci	if (IS_ERR(cqr)) {
36908c2ecf20Sopenharmony_ci		DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s",
36918c2ecf20Sopenharmony_ci				"Could not allocate RAS request");
36928c2ecf20Sopenharmony_ci		return cqr;
36938c2ecf20Sopenharmony_ci	}
36948c2ecf20Sopenharmony_ci
36958c2ecf20Sopenharmony_ci	ras_data = cqr->data;
36968c2ecf20Sopenharmony_ci	memset(ras_data, 0, size);
36978c2ecf20Sopenharmony_ci
36988c2ecf20Sopenharmony_ci	ras_data->order = DSO_ORDER_RAS;
36998c2ecf20Sopenharmony_ci	ras_data->flags.vol_type = 0; /* CKD volume */
37008c2ecf20Sopenharmony_ci	/* Release specified extents or entire volume */
37018c2ecf20Sopenharmony_ci	ras_data->op_flags.by_extent = by_extent;
37028c2ecf20Sopenharmony_ci	/*
37038c2ecf20Sopenharmony_ci	 * This bit guarantees initialisation of tracks within an extent that is
37048c2ecf20Sopenharmony_ci	 * not fully specified, but is only supported with a certain feature
37058c2ecf20Sopenharmony_ci	 * subset.
37068c2ecf20Sopenharmony_ci	 */
37078c2ecf20Sopenharmony_ci	ras_data->op_flags.guarantee_init = !!(features->feature[56] & 0x01);
37088c2ecf20Sopenharmony_ci	ras_data->lss = private->ned->ID;
37098c2ecf20Sopenharmony_ci	ras_data->dev_addr = private->ned->unit_addr;
37108c2ecf20Sopenharmony_ci	ras_data->nr_exts = nr_exts;
37118c2ecf20Sopenharmony_ci
37128c2ecf20Sopenharmony_ci	if (by_extent) {
37138c2ecf20Sopenharmony_ci		heads = private->rdc_data.trk_per_cyl;
37148c2ecf20Sopenharmony_ci		cur_from_trk = first_trk;
37158c2ecf20Sopenharmony_ci		cur_to_trk = first_trk + trks_per_ext -
37168c2ecf20Sopenharmony_ci			(first_trk % trks_per_ext) - 1;
37178c2ecf20Sopenharmony_ci		if (cur_to_trk > last_trk)
37188c2ecf20Sopenharmony_ci			cur_to_trk = last_trk;
37198c2ecf20Sopenharmony_ci		ras_range = (struct dasd_dso_ras_ext_range *)(cqr->data + ras_size);
37208c2ecf20Sopenharmony_ci
37218c2ecf20Sopenharmony_ci		for (i = 0; i < nr_exts; i++) {
37228c2ecf20Sopenharmony_ci			beg_cyl = cur_from_trk / heads;
37238c2ecf20Sopenharmony_ci			beg_head = cur_from_trk % heads;
37248c2ecf20Sopenharmony_ci			end_cyl = cur_to_trk / heads;
37258c2ecf20Sopenharmony_ci			end_head = cur_to_trk % heads;
37268c2ecf20Sopenharmony_ci
37278c2ecf20Sopenharmony_ci			set_ch_t(&ras_range->beg_ext, beg_cyl, beg_head);
37288c2ecf20Sopenharmony_ci			set_ch_t(&ras_range->end_ext, end_cyl, end_head);
37298c2ecf20Sopenharmony_ci
37308c2ecf20Sopenharmony_ci			cur_from_trk = cur_to_trk + 1;
37318c2ecf20Sopenharmony_ci			cur_to_trk = cur_from_trk + trks_per_ext - 1;
37328c2ecf20Sopenharmony_ci			if (cur_to_trk > last_trk)
37338c2ecf20Sopenharmony_ci				cur_to_trk = last_trk;
37348c2ecf20Sopenharmony_ci			ras_range++;
37358c2ecf20Sopenharmony_ci		}
37368c2ecf20Sopenharmony_ci	}
37378c2ecf20Sopenharmony_ci
37388c2ecf20Sopenharmony_ci	ccw = cqr->cpaddr;
37398c2ecf20Sopenharmony_ci	ccw->cda = (__u32)(addr_t)cqr->data;
37408c2ecf20Sopenharmony_ci	ccw->cmd_code = DASD_ECKD_CCW_DSO;
37418c2ecf20Sopenharmony_ci	ccw->count = size;
37428c2ecf20Sopenharmony_ci
37438c2ecf20Sopenharmony_ci	cqr->startdev = device;
37448c2ecf20Sopenharmony_ci	cqr->memdev = device;
37458c2ecf20Sopenharmony_ci	cqr->block = block;
37468c2ecf20Sopenharmony_ci	cqr->retries = 256;
37478c2ecf20Sopenharmony_ci	cqr->expires = device->default_expires * HZ;
37488c2ecf20Sopenharmony_ci	cqr->buildclk = get_tod_clock();
37498c2ecf20Sopenharmony_ci	cqr->status = DASD_CQR_FILLED;
37508c2ecf20Sopenharmony_ci
37518c2ecf20Sopenharmony_ci	return cqr;
37528c2ecf20Sopenharmony_ci}
37538c2ecf20Sopenharmony_ci
37548c2ecf20Sopenharmony_cistatic int dasd_eckd_release_space_full(struct dasd_device *device)
37558c2ecf20Sopenharmony_ci{
37568c2ecf20Sopenharmony_ci	struct dasd_ccw_req *cqr;
37578c2ecf20Sopenharmony_ci	int rc;
37588c2ecf20Sopenharmony_ci
37598c2ecf20Sopenharmony_ci	cqr = dasd_eckd_dso_ras(device, NULL, NULL, 0, 0, 0);
37608c2ecf20Sopenharmony_ci	if (IS_ERR(cqr))
37618c2ecf20Sopenharmony_ci		return PTR_ERR(cqr);
37628c2ecf20Sopenharmony_ci
37638c2ecf20Sopenharmony_ci	rc = dasd_sleep_on_interruptible(cqr);
37648c2ecf20Sopenharmony_ci
37658c2ecf20Sopenharmony_ci	dasd_sfree_request(cqr, cqr->memdev);
37668c2ecf20Sopenharmony_ci
37678c2ecf20Sopenharmony_ci	return rc;
37688c2ecf20Sopenharmony_ci}
37698c2ecf20Sopenharmony_ci
37708c2ecf20Sopenharmony_cistatic int dasd_eckd_release_space_trks(struct dasd_device *device,
37718c2ecf20Sopenharmony_ci					unsigned int from, unsigned int to)
37728c2ecf20Sopenharmony_ci{
37738c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
37748c2ecf20Sopenharmony_ci	struct dasd_block *block = device->block;
37758c2ecf20Sopenharmony_ci	struct dasd_ccw_req *cqr, *n;
37768c2ecf20Sopenharmony_ci	struct list_head ras_queue;
37778c2ecf20Sopenharmony_ci	unsigned int device_exts;
37788c2ecf20Sopenharmony_ci	int trks_per_ext;
37798c2ecf20Sopenharmony_ci	int stop, step;
37808c2ecf20Sopenharmony_ci	int cur_pos;
37818c2ecf20Sopenharmony_ci	int rc = 0;
37828c2ecf20Sopenharmony_ci	int retry;
37838c2ecf20Sopenharmony_ci
37848c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&ras_queue);
37858c2ecf20Sopenharmony_ci
37868c2ecf20Sopenharmony_ci	device_exts = private->real_cyl / dasd_eckd_ext_size(device);
37878c2ecf20Sopenharmony_ci	trks_per_ext = dasd_eckd_ext_size(device) * private->rdc_data.trk_per_cyl;
37888c2ecf20Sopenharmony_ci
37898c2ecf20Sopenharmony_ci	/* Make sure device limits are not exceeded */
37908c2ecf20Sopenharmony_ci	step = trks_per_ext * min(device_exts, DASD_ECKD_RAS_EXTS_MAX);
37918c2ecf20Sopenharmony_ci	cur_pos = from;
37928c2ecf20Sopenharmony_ci
37938c2ecf20Sopenharmony_ci	do {
37948c2ecf20Sopenharmony_ci		retry = 0;
37958c2ecf20Sopenharmony_ci		while (cur_pos < to) {
37968c2ecf20Sopenharmony_ci			stop = cur_pos + step -
37978c2ecf20Sopenharmony_ci				((cur_pos + step) % trks_per_ext) - 1;
37988c2ecf20Sopenharmony_ci			if (stop > to)
37998c2ecf20Sopenharmony_ci				stop = to;
38008c2ecf20Sopenharmony_ci
38018c2ecf20Sopenharmony_ci			cqr = dasd_eckd_dso_ras(device, NULL, NULL, cur_pos, stop, 1);
38028c2ecf20Sopenharmony_ci			if (IS_ERR(cqr)) {
38038c2ecf20Sopenharmony_ci				rc = PTR_ERR(cqr);
38048c2ecf20Sopenharmony_ci				if (rc == -ENOMEM) {
38058c2ecf20Sopenharmony_ci					if (list_empty(&ras_queue))
38068c2ecf20Sopenharmony_ci						goto out;
38078c2ecf20Sopenharmony_ci					retry = 1;
38088c2ecf20Sopenharmony_ci					break;
38098c2ecf20Sopenharmony_ci				}
38108c2ecf20Sopenharmony_ci				goto err_out;
38118c2ecf20Sopenharmony_ci			}
38128c2ecf20Sopenharmony_ci
38138c2ecf20Sopenharmony_ci			spin_lock_irq(&block->queue_lock);
38148c2ecf20Sopenharmony_ci			list_add_tail(&cqr->blocklist, &ras_queue);
38158c2ecf20Sopenharmony_ci			spin_unlock_irq(&block->queue_lock);
38168c2ecf20Sopenharmony_ci			cur_pos = stop + 1;
38178c2ecf20Sopenharmony_ci		}
38188c2ecf20Sopenharmony_ci
38198c2ecf20Sopenharmony_ci		rc = dasd_sleep_on_queue_interruptible(&ras_queue);
38208c2ecf20Sopenharmony_ci
38218c2ecf20Sopenharmony_cierr_out:
38228c2ecf20Sopenharmony_ci		list_for_each_entry_safe(cqr, n, &ras_queue, blocklist) {
38238c2ecf20Sopenharmony_ci			device = cqr->startdev;
38248c2ecf20Sopenharmony_ci			private = device->private;
38258c2ecf20Sopenharmony_ci
38268c2ecf20Sopenharmony_ci			spin_lock_irq(&block->queue_lock);
38278c2ecf20Sopenharmony_ci			list_del_init(&cqr->blocklist);
38288c2ecf20Sopenharmony_ci			spin_unlock_irq(&block->queue_lock);
38298c2ecf20Sopenharmony_ci			dasd_sfree_request(cqr, device);
38308c2ecf20Sopenharmony_ci			private->count--;
38318c2ecf20Sopenharmony_ci		}
38328c2ecf20Sopenharmony_ci	} while (retry);
38338c2ecf20Sopenharmony_ci
38348c2ecf20Sopenharmony_ciout:
38358c2ecf20Sopenharmony_ci	return rc;
38368c2ecf20Sopenharmony_ci}
38378c2ecf20Sopenharmony_ci
38388c2ecf20Sopenharmony_cistatic int dasd_eckd_release_space(struct dasd_device *device,
38398c2ecf20Sopenharmony_ci				   struct format_data_t *rdata)
38408c2ecf20Sopenharmony_ci{
38418c2ecf20Sopenharmony_ci	if (rdata->intensity & DASD_FMT_INT_ESE_FULL)
38428c2ecf20Sopenharmony_ci		return dasd_eckd_release_space_full(device);
38438c2ecf20Sopenharmony_ci	else if (rdata->intensity == 0)
38448c2ecf20Sopenharmony_ci		return dasd_eckd_release_space_trks(device, rdata->start_unit,
38458c2ecf20Sopenharmony_ci						    rdata->stop_unit);
38468c2ecf20Sopenharmony_ci	else
38478c2ecf20Sopenharmony_ci		return -EINVAL;
38488c2ecf20Sopenharmony_ci}
38498c2ecf20Sopenharmony_ci
38508c2ecf20Sopenharmony_cistatic struct dasd_ccw_req *dasd_eckd_build_cp_cmd_single(
38518c2ecf20Sopenharmony_ci					       struct dasd_device *startdev,
38528c2ecf20Sopenharmony_ci					       struct dasd_block *block,
38538c2ecf20Sopenharmony_ci					       struct request *req,
38548c2ecf20Sopenharmony_ci					       sector_t first_rec,
38558c2ecf20Sopenharmony_ci					       sector_t last_rec,
38568c2ecf20Sopenharmony_ci					       sector_t first_trk,
38578c2ecf20Sopenharmony_ci					       sector_t last_trk,
38588c2ecf20Sopenharmony_ci					       unsigned int first_offs,
38598c2ecf20Sopenharmony_ci					       unsigned int last_offs,
38608c2ecf20Sopenharmony_ci					       unsigned int blk_per_trk,
38618c2ecf20Sopenharmony_ci					       unsigned int blksize)
38628c2ecf20Sopenharmony_ci{
38638c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private;
38648c2ecf20Sopenharmony_ci	unsigned long *idaws;
38658c2ecf20Sopenharmony_ci	struct LO_eckd_data *LO_data;
38668c2ecf20Sopenharmony_ci	struct dasd_ccw_req *cqr;
38678c2ecf20Sopenharmony_ci	struct ccw1 *ccw;
38688c2ecf20Sopenharmony_ci	struct req_iterator iter;
38698c2ecf20Sopenharmony_ci	struct bio_vec bv;
38708c2ecf20Sopenharmony_ci	char *dst;
38718c2ecf20Sopenharmony_ci	unsigned int off;
38728c2ecf20Sopenharmony_ci	int count, cidaw, cplength, datasize;
38738c2ecf20Sopenharmony_ci	sector_t recid;
38748c2ecf20Sopenharmony_ci	unsigned char cmd, rcmd;
38758c2ecf20Sopenharmony_ci	int use_prefix;
38768c2ecf20Sopenharmony_ci	struct dasd_device *basedev;
38778c2ecf20Sopenharmony_ci
38788c2ecf20Sopenharmony_ci	basedev = block->base;
38798c2ecf20Sopenharmony_ci	private = basedev->private;
38808c2ecf20Sopenharmony_ci	if (rq_data_dir(req) == READ)
38818c2ecf20Sopenharmony_ci		cmd = DASD_ECKD_CCW_READ_MT;
38828c2ecf20Sopenharmony_ci	else if (rq_data_dir(req) == WRITE)
38838c2ecf20Sopenharmony_ci		cmd = DASD_ECKD_CCW_WRITE_MT;
38848c2ecf20Sopenharmony_ci	else
38858c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
38868c2ecf20Sopenharmony_ci
38878c2ecf20Sopenharmony_ci	/* Check struct bio and count the number of blocks for the request. */
38888c2ecf20Sopenharmony_ci	count = 0;
38898c2ecf20Sopenharmony_ci	cidaw = 0;
38908c2ecf20Sopenharmony_ci	rq_for_each_segment(bv, req, iter) {
38918c2ecf20Sopenharmony_ci		if (bv.bv_len & (blksize - 1))
38928c2ecf20Sopenharmony_ci			/* Eckd can only do full blocks. */
38938c2ecf20Sopenharmony_ci			return ERR_PTR(-EINVAL);
38948c2ecf20Sopenharmony_ci		count += bv.bv_len >> (block->s2b_shift + 9);
38958c2ecf20Sopenharmony_ci		if (idal_is_needed (page_address(bv.bv_page), bv.bv_len))
38968c2ecf20Sopenharmony_ci			cidaw += bv.bv_len >> (block->s2b_shift + 9);
38978c2ecf20Sopenharmony_ci	}
38988c2ecf20Sopenharmony_ci	/* Paranoia. */
38998c2ecf20Sopenharmony_ci	if (count != last_rec - first_rec + 1)
39008c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
39018c2ecf20Sopenharmony_ci
39028c2ecf20Sopenharmony_ci	/* use the prefix command if available */
39038c2ecf20Sopenharmony_ci	use_prefix = private->features.feature[8] & 0x01;
39048c2ecf20Sopenharmony_ci	if (use_prefix) {
39058c2ecf20Sopenharmony_ci		/* 1x prefix + number of blocks */
39068c2ecf20Sopenharmony_ci		cplength = 2 + count;
39078c2ecf20Sopenharmony_ci		/* 1x prefix + cidaws*sizeof(long) */
39088c2ecf20Sopenharmony_ci		datasize = sizeof(struct PFX_eckd_data) +
39098c2ecf20Sopenharmony_ci			sizeof(struct LO_eckd_data) +
39108c2ecf20Sopenharmony_ci			cidaw * sizeof(unsigned long);
39118c2ecf20Sopenharmony_ci	} else {
39128c2ecf20Sopenharmony_ci		/* 1x define extent + 1x locate record + number of blocks */
39138c2ecf20Sopenharmony_ci		cplength = 2 + count;
39148c2ecf20Sopenharmony_ci		/* 1x define extent + 1x locate record + cidaws*sizeof(long) */
39158c2ecf20Sopenharmony_ci		datasize = sizeof(struct DE_eckd_data) +
39168c2ecf20Sopenharmony_ci			sizeof(struct LO_eckd_data) +
39178c2ecf20Sopenharmony_ci			cidaw * sizeof(unsigned long);
39188c2ecf20Sopenharmony_ci	}
39198c2ecf20Sopenharmony_ci	/* Find out the number of additional locate record ccws for cdl. */
39208c2ecf20Sopenharmony_ci	if (private->uses_cdl && first_rec < 2*blk_per_trk) {
39218c2ecf20Sopenharmony_ci		if (last_rec >= 2*blk_per_trk)
39228c2ecf20Sopenharmony_ci			count = 2*blk_per_trk - first_rec;
39238c2ecf20Sopenharmony_ci		cplength += count;
39248c2ecf20Sopenharmony_ci		datasize += count*sizeof(struct LO_eckd_data);
39258c2ecf20Sopenharmony_ci	}
39268c2ecf20Sopenharmony_ci	/* Allocate the ccw request. */
39278c2ecf20Sopenharmony_ci	cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, cplength, datasize,
39288c2ecf20Sopenharmony_ci				   startdev, blk_mq_rq_to_pdu(req));
39298c2ecf20Sopenharmony_ci	if (IS_ERR(cqr))
39308c2ecf20Sopenharmony_ci		return cqr;
39318c2ecf20Sopenharmony_ci	ccw = cqr->cpaddr;
39328c2ecf20Sopenharmony_ci	/* First ccw is define extent or prefix. */
39338c2ecf20Sopenharmony_ci	if (use_prefix) {
39348c2ecf20Sopenharmony_ci		if (prefix(ccw++, cqr->data, first_trk,
39358c2ecf20Sopenharmony_ci			   last_trk, cmd, basedev, startdev) == -EAGAIN) {
39368c2ecf20Sopenharmony_ci			/* Clock not in sync and XRC is enabled.
39378c2ecf20Sopenharmony_ci			 * Try again later.
39388c2ecf20Sopenharmony_ci			 */
39398c2ecf20Sopenharmony_ci			dasd_sfree_request(cqr, startdev);
39408c2ecf20Sopenharmony_ci			return ERR_PTR(-EAGAIN);
39418c2ecf20Sopenharmony_ci		}
39428c2ecf20Sopenharmony_ci		idaws = (unsigned long *) (cqr->data +
39438c2ecf20Sopenharmony_ci					   sizeof(struct PFX_eckd_data));
39448c2ecf20Sopenharmony_ci	} else {
39458c2ecf20Sopenharmony_ci		if (define_extent(ccw++, cqr->data, first_trk,
39468c2ecf20Sopenharmony_ci				  last_trk, cmd, basedev, 0) == -EAGAIN) {
39478c2ecf20Sopenharmony_ci			/* Clock not in sync and XRC is enabled.
39488c2ecf20Sopenharmony_ci			 * Try again later.
39498c2ecf20Sopenharmony_ci			 */
39508c2ecf20Sopenharmony_ci			dasd_sfree_request(cqr, startdev);
39518c2ecf20Sopenharmony_ci			return ERR_PTR(-EAGAIN);
39528c2ecf20Sopenharmony_ci		}
39538c2ecf20Sopenharmony_ci		idaws = (unsigned long *) (cqr->data +
39548c2ecf20Sopenharmony_ci					   sizeof(struct DE_eckd_data));
39558c2ecf20Sopenharmony_ci	}
39568c2ecf20Sopenharmony_ci	/* Build locate_record+read/write/ccws. */
39578c2ecf20Sopenharmony_ci	LO_data = (struct LO_eckd_data *) (idaws + cidaw);
39588c2ecf20Sopenharmony_ci	recid = first_rec;
39598c2ecf20Sopenharmony_ci	if (private->uses_cdl == 0 || recid > 2*blk_per_trk) {
39608c2ecf20Sopenharmony_ci		/* Only standard blocks so there is just one locate record. */
39618c2ecf20Sopenharmony_ci		ccw[-1].flags |= CCW_FLAG_CC;
39628c2ecf20Sopenharmony_ci		locate_record(ccw++, LO_data++, first_trk, first_offs + 1,
39638c2ecf20Sopenharmony_ci			      last_rec - recid + 1, cmd, basedev, blksize);
39648c2ecf20Sopenharmony_ci	}
39658c2ecf20Sopenharmony_ci	rq_for_each_segment(bv, req, iter) {
39668c2ecf20Sopenharmony_ci		dst = page_address(bv.bv_page) + bv.bv_offset;
39678c2ecf20Sopenharmony_ci		if (dasd_page_cache) {
39688c2ecf20Sopenharmony_ci			char *copy = kmem_cache_alloc(dasd_page_cache,
39698c2ecf20Sopenharmony_ci						      GFP_DMA | __GFP_NOWARN);
39708c2ecf20Sopenharmony_ci			if (copy && rq_data_dir(req) == WRITE)
39718c2ecf20Sopenharmony_ci				memcpy(copy + bv.bv_offset, dst, bv.bv_len);
39728c2ecf20Sopenharmony_ci			if (copy)
39738c2ecf20Sopenharmony_ci				dst = copy + bv.bv_offset;
39748c2ecf20Sopenharmony_ci		}
39758c2ecf20Sopenharmony_ci		for (off = 0; off < bv.bv_len; off += blksize) {
39768c2ecf20Sopenharmony_ci			sector_t trkid = recid;
39778c2ecf20Sopenharmony_ci			unsigned int recoffs = sector_div(trkid, blk_per_trk);
39788c2ecf20Sopenharmony_ci			rcmd = cmd;
39798c2ecf20Sopenharmony_ci			count = blksize;
39808c2ecf20Sopenharmony_ci			/* Locate record for cdl special block ? */
39818c2ecf20Sopenharmony_ci			if (private->uses_cdl && recid < 2*blk_per_trk) {
39828c2ecf20Sopenharmony_ci				if (dasd_eckd_cdl_special(blk_per_trk, recid)){
39838c2ecf20Sopenharmony_ci					rcmd |= 0x8;
39848c2ecf20Sopenharmony_ci					count = dasd_eckd_cdl_reclen(recid);
39858c2ecf20Sopenharmony_ci					if (count < blksize &&
39868c2ecf20Sopenharmony_ci					    rq_data_dir(req) == READ)
39878c2ecf20Sopenharmony_ci						memset(dst + count, 0xe5,
39888c2ecf20Sopenharmony_ci						       blksize - count);
39898c2ecf20Sopenharmony_ci				}
39908c2ecf20Sopenharmony_ci				ccw[-1].flags |= CCW_FLAG_CC;
39918c2ecf20Sopenharmony_ci				locate_record(ccw++, LO_data++,
39928c2ecf20Sopenharmony_ci					      trkid, recoffs + 1,
39938c2ecf20Sopenharmony_ci					      1, rcmd, basedev, count);
39948c2ecf20Sopenharmony_ci			}
39958c2ecf20Sopenharmony_ci			/* Locate record for standard blocks ? */
39968c2ecf20Sopenharmony_ci			if (private->uses_cdl && recid == 2*blk_per_trk) {
39978c2ecf20Sopenharmony_ci				ccw[-1].flags |= CCW_FLAG_CC;
39988c2ecf20Sopenharmony_ci				locate_record(ccw++, LO_data++,
39998c2ecf20Sopenharmony_ci					      trkid, recoffs + 1,
40008c2ecf20Sopenharmony_ci					      last_rec - recid + 1,
40018c2ecf20Sopenharmony_ci					      cmd, basedev, count);
40028c2ecf20Sopenharmony_ci			}
40038c2ecf20Sopenharmony_ci			/* Read/write ccw. */
40048c2ecf20Sopenharmony_ci			ccw[-1].flags |= CCW_FLAG_CC;
40058c2ecf20Sopenharmony_ci			ccw->cmd_code = rcmd;
40068c2ecf20Sopenharmony_ci			ccw->count = count;
40078c2ecf20Sopenharmony_ci			if (idal_is_needed(dst, blksize)) {
40088c2ecf20Sopenharmony_ci				ccw->cda = (__u32)(addr_t) idaws;
40098c2ecf20Sopenharmony_ci				ccw->flags = CCW_FLAG_IDA;
40108c2ecf20Sopenharmony_ci				idaws = idal_create_words(idaws, dst, blksize);
40118c2ecf20Sopenharmony_ci			} else {
40128c2ecf20Sopenharmony_ci				ccw->cda = (__u32)(addr_t) dst;
40138c2ecf20Sopenharmony_ci				ccw->flags = 0;
40148c2ecf20Sopenharmony_ci			}
40158c2ecf20Sopenharmony_ci			ccw++;
40168c2ecf20Sopenharmony_ci			dst += blksize;
40178c2ecf20Sopenharmony_ci			recid++;
40188c2ecf20Sopenharmony_ci		}
40198c2ecf20Sopenharmony_ci	}
40208c2ecf20Sopenharmony_ci	if (blk_noretry_request(req) ||
40218c2ecf20Sopenharmony_ci	    block->base->features & DASD_FEATURE_FAILFAST)
40228c2ecf20Sopenharmony_ci		set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags);
40238c2ecf20Sopenharmony_ci	cqr->startdev = startdev;
40248c2ecf20Sopenharmony_ci	cqr->memdev = startdev;
40258c2ecf20Sopenharmony_ci	cqr->block = block;
40268c2ecf20Sopenharmony_ci	cqr->expires = startdev->default_expires * HZ;	/* default 5 minutes */
40278c2ecf20Sopenharmony_ci	cqr->lpm = dasd_path_get_ppm(startdev);
40288c2ecf20Sopenharmony_ci	cqr->retries = startdev->default_retries;
40298c2ecf20Sopenharmony_ci	cqr->buildclk = get_tod_clock();
40308c2ecf20Sopenharmony_ci	cqr->status = DASD_CQR_FILLED;
40318c2ecf20Sopenharmony_ci
40328c2ecf20Sopenharmony_ci	/* Set flags to suppress output for expected errors */
40338c2ecf20Sopenharmony_ci	if (dasd_eckd_is_ese(basedev)) {
40348c2ecf20Sopenharmony_ci		set_bit(DASD_CQR_SUPPRESS_FP, &cqr->flags);
40358c2ecf20Sopenharmony_ci		set_bit(DASD_CQR_SUPPRESS_IL, &cqr->flags);
40368c2ecf20Sopenharmony_ci		set_bit(DASD_CQR_SUPPRESS_NRF, &cqr->flags);
40378c2ecf20Sopenharmony_ci	}
40388c2ecf20Sopenharmony_ci
40398c2ecf20Sopenharmony_ci	return cqr;
40408c2ecf20Sopenharmony_ci}
40418c2ecf20Sopenharmony_ci
40428c2ecf20Sopenharmony_cistatic struct dasd_ccw_req *dasd_eckd_build_cp_cmd_track(
40438c2ecf20Sopenharmony_ci					       struct dasd_device *startdev,
40448c2ecf20Sopenharmony_ci					       struct dasd_block *block,
40458c2ecf20Sopenharmony_ci					       struct request *req,
40468c2ecf20Sopenharmony_ci					       sector_t first_rec,
40478c2ecf20Sopenharmony_ci					       sector_t last_rec,
40488c2ecf20Sopenharmony_ci					       sector_t first_trk,
40498c2ecf20Sopenharmony_ci					       sector_t last_trk,
40508c2ecf20Sopenharmony_ci					       unsigned int first_offs,
40518c2ecf20Sopenharmony_ci					       unsigned int last_offs,
40528c2ecf20Sopenharmony_ci					       unsigned int blk_per_trk,
40538c2ecf20Sopenharmony_ci					       unsigned int blksize)
40548c2ecf20Sopenharmony_ci{
40558c2ecf20Sopenharmony_ci	unsigned long *idaws;
40568c2ecf20Sopenharmony_ci	struct dasd_ccw_req *cqr;
40578c2ecf20Sopenharmony_ci	struct ccw1 *ccw;
40588c2ecf20Sopenharmony_ci	struct req_iterator iter;
40598c2ecf20Sopenharmony_ci	struct bio_vec bv;
40608c2ecf20Sopenharmony_ci	char *dst, *idaw_dst;
40618c2ecf20Sopenharmony_ci	unsigned int cidaw, cplength, datasize;
40628c2ecf20Sopenharmony_ci	unsigned int tlf;
40638c2ecf20Sopenharmony_ci	sector_t recid;
40648c2ecf20Sopenharmony_ci	unsigned char cmd;
40658c2ecf20Sopenharmony_ci	struct dasd_device *basedev;
40668c2ecf20Sopenharmony_ci	unsigned int trkcount, count, count_to_trk_end;
40678c2ecf20Sopenharmony_ci	unsigned int idaw_len, seg_len, part_len, len_to_track_end;
40688c2ecf20Sopenharmony_ci	unsigned char new_track, end_idaw;
40698c2ecf20Sopenharmony_ci	sector_t trkid;
40708c2ecf20Sopenharmony_ci	unsigned int recoffs;
40718c2ecf20Sopenharmony_ci
40728c2ecf20Sopenharmony_ci	basedev = block->base;
40738c2ecf20Sopenharmony_ci	if (rq_data_dir(req) == READ)
40748c2ecf20Sopenharmony_ci		cmd = DASD_ECKD_CCW_READ_TRACK_DATA;
40758c2ecf20Sopenharmony_ci	else if (rq_data_dir(req) == WRITE)
40768c2ecf20Sopenharmony_ci		cmd = DASD_ECKD_CCW_WRITE_TRACK_DATA;
40778c2ecf20Sopenharmony_ci	else
40788c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
40798c2ecf20Sopenharmony_ci
40808c2ecf20Sopenharmony_ci	/* Track based I/O needs IDAWs for each page, and not just for
40818c2ecf20Sopenharmony_ci	 * 64 bit addresses. We need additional idals for pages
40828c2ecf20Sopenharmony_ci	 * that get filled from two tracks, so we use the number
40838c2ecf20Sopenharmony_ci	 * of records as upper limit.
40848c2ecf20Sopenharmony_ci	 */
40858c2ecf20Sopenharmony_ci	cidaw = last_rec - first_rec + 1;
40868c2ecf20Sopenharmony_ci	trkcount = last_trk - first_trk + 1;
40878c2ecf20Sopenharmony_ci
40888c2ecf20Sopenharmony_ci	/* 1x prefix + one read/write ccw per track */
40898c2ecf20Sopenharmony_ci	cplength = 1 + trkcount;
40908c2ecf20Sopenharmony_ci
40918c2ecf20Sopenharmony_ci	datasize = sizeof(struct PFX_eckd_data) + cidaw * sizeof(unsigned long);
40928c2ecf20Sopenharmony_ci
40938c2ecf20Sopenharmony_ci	/* Allocate the ccw request. */
40948c2ecf20Sopenharmony_ci	cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, cplength, datasize,
40958c2ecf20Sopenharmony_ci				   startdev, blk_mq_rq_to_pdu(req));
40968c2ecf20Sopenharmony_ci	if (IS_ERR(cqr))
40978c2ecf20Sopenharmony_ci		return cqr;
40988c2ecf20Sopenharmony_ci	ccw = cqr->cpaddr;
40998c2ecf20Sopenharmony_ci	/* transfer length factor: how many bytes to read from the last track */
41008c2ecf20Sopenharmony_ci	if (first_trk == last_trk)
41018c2ecf20Sopenharmony_ci		tlf = last_offs - first_offs + 1;
41028c2ecf20Sopenharmony_ci	else
41038c2ecf20Sopenharmony_ci		tlf = last_offs + 1;
41048c2ecf20Sopenharmony_ci	tlf *= blksize;
41058c2ecf20Sopenharmony_ci
41068c2ecf20Sopenharmony_ci	if (prefix_LRE(ccw++, cqr->data, first_trk,
41078c2ecf20Sopenharmony_ci		       last_trk, cmd, basedev, startdev,
41088c2ecf20Sopenharmony_ci		       1 /* format */, first_offs + 1,
41098c2ecf20Sopenharmony_ci		       trkcount, blksize,
41108c2ecf20Sopenharmony_ci		       tlf) == -EAGAIN) {
41118c2ecf20Sopenharmony_ci		/* Clock not in sync and XRC is enabled.
41128c2ecf20Sopenharmony_ci		 * Try again later.
41138c2ecf20Sopenharmony_ci		 */
41148c2ecf20Sopenharmony_ci		dasd_sfree_request(cqr, startdev);
41158c2ecf20Sopenharmony_ci		return ERR_PTR(-EAGAIN);
41168c2ecf20Sopenharmony_ci	}
41178c2ecf20Sopenharmony_ci
41188c2ecf20Sopenharmony_ci	/*
41198c2ecf20Sopenharmony_ci	 * The translation of request into ccw programs must meet the
41208c2ecf20Sopenharmony_ci	 * following conditions:
41218c2ecf20Sopenharmony_ci	 * - all idaws but the first and the last must address full pages
41228c2ecf20Sopenharmony_ci	 *   (or 2K blocks on 31-bit)
41238c2ecf20Sopenharmony_ci	 * - the scope of a ccw and it's idal ends with the track boundaries
41248c2ecf20Sopenharmony_ci	 */
41258c2ecf20Sopenharmony_ci	idaws = (unsigned long *) (cqr->data + sizeof(struct PFX_eckd_data));
41268c2ecf20Sopenharmony_ci	recid = first_rec;
41278c2ecf20Sopenharmony_ci	new_track = 1;
41288c2ecf20Sopenharmony_ci	end_idaw = 0;
41298c2ecf20Sopenharmony_ci	len_to_track_end = 0;
41308c2ecf20Sopenharmony_ci	idaw_dst = NULL;
41318c2ecf20Sopenharmony_ci	idaw_len = 0;
41328c2ecf20Sopenharmony_ci	rq_for_each_segment(bv, req, iter) {
41338c2ecf20Sopenharmony_ci		dst = page_address(bv.bv_page) + bv.bv_offset;
41348c2ecf20Sopenharmony_ci		seg_len = bv.bv_len;
41358c2ecf20Sopenharmony_ci		while (seg_len) {
41368c2ecf20Sopenharmony_ci			if (new_track) {
41378c2ecf20Sopenharmony_ci				trkid = recid;
41388c2ecf20Sopenharmony_ci				recoffs = sector_div(trkid, blk_per_trk);
41398c2ecf20Sopenharmony_ci				count_to_trk_end = blk_per_trk - recoffs;
41408c2ecf20Sopenharmony_ci				count = min((last_rec - recid + 1),
41418c2ecf20Sopenharmony_ci					    (sector_t)count_to_trk_end);
41428c2ecf20Sopenharmony_ci				len_to_track_end = count * blksize;
41438c2ecf20Sopenharmony_ci				ccw[-1].flags |= CCW_FLAG_CC;
41448c2ecf20Sopenharmony_ci				ccw->cmd_code = cmd;
41458c2ecf20Sopenharmony_ci				ccw->count = len_to_track_end;
41468c2ecf20Sopenharmony_ci				ccw->cda = (__u32)(addr_t)idaws;
41478c2ecf20Sopenharmony_ci				ccw->flags = CCW_FLAG_IDA;
41488c2ecf20Sopenharmony_ci				ccw++;
41498c2ecf20Sopenharmony_ci				recid += count;
41508c2ecf20Sopenharmony_ci				new_track = 0;
41518c2ecf20Sopenharmony_ci				/* first idaw for a ccw may start anywhere */
41528c2ecf20Sopenharmony_ci				if (!idaw_dst)
41538c2ecf20Sopenharmony_ci					idaw_dst = dst;
41548c2ecf20Sopenharmony_ci			}
41558c2ecf20Sopenharmony_ci			/* If we start a new idaw, we must make sure that it
41568c2ecf20Sopenharmony_ci			 * starts on an IDA_BLOCK_SIZE boundary.
41578c2ecf20Sopenharmony_ci			 * If we continue an idaw, we must make sure that the
41588c2ecf20Sopenharmony_ci			 * current segment begins where the so far accumulated
41598c2ecf20Sopenharmony_ci			 * idaw ends
41608c2ecf20Sopenharmony_ci			 */
41618c2ecf20Sopenharmony_ci			if (!idaw_dst) {
41628c2ecf20Sopenharmony_ci				if (__pa(dst) & (IDA_BLOCK_SIZE-1)) {
41638c2ecf20Sopenharmony_ci					dasd_sfree_request(cqr, startdev);
41648c2ecf20Sopenharmony_ci					return ERR_PTR(-ERANGE);
41658c2ecf20Sopenharmony_ci				} else
41668c2ecf20Sopenharmony_ci					idaw_dst = dst;
41678c2ecf20Sopenharmony_ci			}
41688c2ecf20Sopenharmony_ci			if ((idaw_dst + idaw_len) != dst) {
41698c2ecf20Sopenharmony_ci				dasd_sfree_request(cqr, startdev);
41708c2ecf20Sopenharmony_ci				return ERR_PTR(-ERANGE);
41718c2ecf20Sopenharmony_ci			}
41728c2ecf20Sopenharmony_ci			part_len = min(seg_len, len_to_track_end);
41738c2ecf20Sopenharmony_ci			seg_len -= part_len;
41748c2ecf20Sopenharmony_ci			dst += part_len;
41758c2ecf20Sopenharmony_ci			idaw_len += part_len;
41768c2ecf20Sopenharmony_ci			len_to_track_end -= part_len;
41778c2ecf20Sopenharmony_ci			/* collected memory area ends on an IDA_BLOCK border,
41788c2ecf20Sopenharmony_ci			 * -> create an idaw
41798c2ecf20Sopenharmony_ci			 * idal_create_words will handle cases where idaw_len
41808c2ecf20Sopenharmony_ci			 * is larger then IDA_BLOCK_SIZE
41818c2ecf20Sopenharmony_ci			 */
41828c2ecf20Sopenharmony_ci			if (!(__pa(idaw_dst + idaw_len) & (IDA_BLOCK_SIZE-1)))
41838c2ecf20Sopenharmony_ci				end_idaw = 1;
41848c2ecf20Sopenharmony_ci			/* We also need to end the idaw at track end */
41858c2ecf20Sopenharmony_ci			if (!len_to_track_end) {
41868c2ecf20Sopenharmony_ci				new_track = 1;
41878c2ecf20Sopenharmony_ci				end_idaw = 1;
41888c2ecf20Sopenharmony_ci			}
41898c2ecf20Sopenharmony_ci			if (end_idaw) {
41908c2ecf20Sopenharmony_ci				idaws = idal_create_words(idaws, idaw_dst,
41918c2ecf20Sopenharmony_ci							  idaw_len);
41928c2ecf20Sopenharmony_ci				idaw_dst = NULL;
41938c2ecf20Sopenharmony_ci				idaw_len = 0;
41948c2ecf20Sopenharmony_ci				end_idaw = 0;
41958c2ecf20Sopenharmony_ci			}
41968c2ecf20Sopenharmony_ci		}
41978c2ecf20Sopenharmony_ci	}
41988c2ecf20Sopenharmony_ci
41998c2ecf20Sopenharmony_ci	if (blk_noretry_request(req) ||
42008c2ecf20Sopenharmony_ci	    block->base->features & DASD_FEATURE_FAILFAST)
42018c2ecf20Sopenharmony_ci		set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags);
42028c2ecf20Sopenharmony_ci	cqr->startdev = startdev;
42038c2ecf20Sopenharmony_ci	cqr->memdev = startdev;
42048c2ecf20Sopenharmony_ci	cqr->block = block;
42058c2ecf20Sopenharmony_ci	cqr->expires = startdev->default_expires * HZ;	/* default 5 minutes */
42068c2ecf20Sopenharmony_ci	cqr->lpm = dasd_path_get_ppm(startdev);
42078c2ecf20Sopenharmony_ci	cqr->retries = startdev->default_retries;
42088c2ecf20Sopenharmony_ci	cqr->buildclk = get_tod_clock();
42098c2ecf20Sopenharmony_ci	cqr->status = DASD_CQR_FILLED;
42108c2ecf20Sopenharmony_ci
42118c2ecf20Sopenharmony_ci	/* Set flags to suppress output for expected errors */
42128c2ecf20Sopenharmony_ci	if (dasd_eckd_is_ese(basedev))
42138c2ecf20Sopenharmony_ci		set_bit(DASD_CQR_SUPPRESS_NRF, &cqr->flags);
42148c2ecf20Sopenharmony_ci
42158c2ecf20Sopenharmony_ci	return cqr;
42168c2ecf20Sopenharmony_ci}
42178c2ecf20Sopenharmony_ci
42188c2ecf20Sopenharmony_cistatic int prepare_itcw(struct itcw *itcw,
42198c2ecf20Sopenharmony_ci			unsigned int trk, unsigned int totrk, int cmd,
42208c2ecf20Sopenharmony_ci			struct dasd_device *basedev,
42218c2ecf20Sopenharmony_ci			struct dasd_device *startdev,
42228c2ecf20Sopenharmony_ci			unsigned int rec_on_trk, int count,
42238c2ecf20Sopenharmony_ci			unsigned int blksize,
42248c2ecf20Sopenharmony_ci			unsigned int total_data_size,
42258c2ecf20Sopenharmony_ci			unsigned int tlf,
42268c2ecf20Sopenharmony_ci			unsigned int blk_per_trk)
42278c2ecf20Sopenharmony_ci{
42288c2ecf20Sopenharmony_ci	struct PFX_eckd_data pfxdata;
42298c2ecf20Sopenharmony_ci	struct dasd_eckd_private *basepriv, *startpriv;
42308c2ecf20Sopenharmony_ci	struct DE_eckd_data *dedata;
42318c2ecf20Sopenharmony_ci	struct LRE_eckd_data *lredata;
42328c2ecf20Sopenharmony_ci	struct dcw *dcw;
42338c2ecf20Sopenharmony_ci
42348c2ecf20Sopenharmony_ci	u32 begcyl, endcyl;
42358c2ecf20Sopenharmony_ci	u16 heads, beghead, endhead;
42368c2ecf20Sopenharmony_ci	u8 pfx_cmd;
42378c2ecf20Sopenharmony_ci
42388c2ecf20Sopenharmony_ci	int rc = 0;
42398c2ecf20Sopenharmony_ci	int sector = 0;
42408c2ecf20Sopenharmony_ci	int dn, d;
42418c2ecf20Sopenharmony_ci
42428c2ecf20Sopenharmony_ci
42438c2ecf20Sopenharmony_ci	/* setup prefix data */
42448c2ecf20Sopenharmony_ci	basepriv = basedev->private;
42458c2ecf20Sopenharmony_ci	startpriv = startdev->private;
42468c2ecf20Sopenharmony_ci	dedata = &pfxdata.define_extent;
42478c2ecf20Sopenharmony_ci	lredata = &pfxdata.locate_record;
42488c2ecf20Sopenharmony_ci
42498c2ecf20Sopenharmony_ci	memset(&pfxdata, 0, sizeof(pfxdata));
42508c2ecf20Sopenharmony_ci	pfxdata.format = 1; /* PFX with LRE */
42518c2ecf20Sopenharmony_ci	pfxdata.base_address = basepriv->ned->unit_addr;
42528c2ecf20Sopenharmony_ci	pfxdata.base_lss = basepriv->ned->ID;
42538c2ecf20Sopenharmony_ci	pfxdata.validity.define_extent = 1;
42548c2ecf20Sopenharmony_ci
42558c2ecf20Sopenharmony_ci	/* private uid is kept up to date, conf_data may be outdated */
42568c2ecf20Sopenharmony_ci	if (startpriv->uid.type == UA_BASE_PAV_ALIAS)
42578c2ecf20Sopenharmony_ci		pfxdata.validity.verify_base = 1;
42588c2ecf20Sopenharmony_ci
42598c2ecf20Sopenharmony_ci	if (startpriv->uid.type == UA_HYPER_PAV_ALIAS) {
42608c2ecf20Sopenharmony_ci		pfxdata.validity.verify_base = 1;
42618c2ecf20Sopenharmony_ci		pfxdata.validity.hyper_pav = 1;
42628c2ecf20Sopenharmony_ci	}
42638c2ecf20Sopenharmony_ci
42648c2ecf20Sopenharmony_ci	switch (cmd) {
42658c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_READ_TRACK_DATA:
42668c2ecf20Sopenharmony_ci		dedata->mask.perm = 0x1;
42678c2ecf20Sopenharmony_ci		dedata->attributes.operation = basepriv->attrib.operation;
42688c2ecf20Sopenharmony_ci		dedata->blk_size = blksize;
42698c2ecf20Sopenharmony_ci		dedata->ga_extended |= 0x42;
42708c2ecf20Sopenharmony_ci		lredata->operation.orientation = 0x0;
42718c2ecf20Sopenharmony_ci		lredata->operation.operation = 0x0C;
42728c2ecf20Sopenharmony_ci		lredata->auxiliary.check_bytes = 0x01;
42738c2ecf20Sopenharmony_ci		pfx_cmd = DASD_ECKD_CCW_PFX_READ;
42748c2ecf20Sopenharmony_ci		break;
42758c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_WRITE_TRACK_DATA:
42768c2ecf20Sopenharmony_ci		dedata->mask.perm = 0x02;
42778c2ecf20Sopenharmony_ci		dedata->attributes.operation = basepriv->attrib.operation;
42788c2ecf20Sopenharmony_ci		dedata->blk_size = blksize;
42798c2ecf20Sopenharmony_ci		rc = set_timestamp(NULL, dedata, basedev);
42808c2ecf20Sopenharmony_ci		dedata->ga_extended |= 0x42;
42818c2ecf20Sopenharmony_ci		lredata->operation.orientation = 0x0;
42828c2ecf20Sopenharmony_ci		lredata->operation.operation = 0x3F;
42838c2ecf20Sopenharmony_ci		lredata->extended_operation = 0x23;
42848c2ecf20Sopenharmony_ci		lredata->auxiliary.check_bytes = 0x2;
42858c2ecf20Sopenharmony_ci		/*
42868c2ecf20Sopenharmony_ci		 * If XRC is supported the System Time Stamp is set. The
42878c2ecf20Sopenharmony_ci		 * validity of the time stamp must be reflected in the prefix
42888c2ecf20Sopenharmony_ci		 * data as well.
42898c2ecf20Sopenharmony_ci		 */
42908c2ecf20Sopenharmony_ci		if (dedata->ga_extended & 0x08 && dedata->ga_extended & 0x02)
42918c2ecf20Sopenharmony_ci			pfxdata.validity.time_stamp = 1; /* 'Time Stamp Valid' */
42928c2ecf20Sopenharmony_ci		pfx_cmd = DASD_ECKD_CCW_PFX;
42938c2ecf20Sopenharmony_ci		break;
42948c2ecf20Sopenharmony_ci	case DASD_ECKD_CCW_READ_COUNT_MT:
42958c2ecf20Sopenharmony_ci		dedata->mask.perm = 0x1;
42968c2ecf20Sopenharmony_ci		dedata->attributes.operation = DASD_BYPASS_CACHE;
42978c2ecf20Sopenharmony_ci		dedata->ga_extended |= 0x42;
42988c2ecf20Sopenharmony_ci		dedata->blk_size = blksize;
42998c2ecf20Sopenharmony_ci		lredata->operation.orientation = 0x2;
43008c2ecf20Sopenharmony_ci		lredata->operation.operation = 0x16;
43018c2ecf20Sopenharmony_ci		lredata->auxiliary.check_bytes = 0x01;
43028c2ecf20Sopenharmony_ci		pfx_cmd = DASD_ECKD_CCW_PFX_READ;
43038c2ecf20Sopenharmony_ci		break;
43048c2ecf20Sopenharmony_ci	default:
43058c2ecf20Sopenharmony_ci		DBF_DEV_EVENT(DBF_ERR, basedev,
43068c2ecf20Sopenharmony_ci			      "prepare itcw, unknown opcode 0x%x", cmd);
43078c2ecf20Sopenharmony_ci		BUG();
43088c2ecf20Sopenharmony_ci		break;
43098c2ecf20Sopenharmony_ci	}
43108c2ecf20Sopenharmony_ci	if (rc)
43118c2ecf20Sopenharmony_ci		return rc;
43128c2ecf20Sopenharmony_ci
43138c2ecf20Sopenharmony_ci	dedata->attributes.mode = 0x3;	/* ECKD */
43148c2ecf20Sopenharmony_ci
43158c2ecf20Sopenharmony_ci	heads = basepriv->rdc_data.trk_per_cyl;
43168c2ecf20Sopenharmony_ci	begcyl = trk / heads;
43178c2ecf20Sopenharmony_ci	beghead = trk % heads;
43188c2ecf20Sopenharmony_ci	endcyl = totrk / heads;
43198c2ecf20Sopenharmony_ci	endhead = totrk % heads;
43208c2ecf20Sopenharmony_ci
43218c2ecf20Sopenharmony_ci	/* check for sequential prestage - enhance cylinder range */
43228c2ecf20Sopenharmony_ci	if (dedata->attributes.operation == DASD_SEQ_PRESTAGE ||
43238c2ecf20Sopenharmony_ci	    dedata->attributes.operation == DASD_SEQ_ACCESS) {
43248c2ecf20Sopenharmony_ci
43258c2ecf20Sopenharmony_ci		if (endcyl + basepriv->attrib.nr_cyl < basepriv->real_cyl)
43268c2ecf20Sopenharmony_ci			endcyl += basepriv->attrib.nr_cyl;
43278c2ecf20Sopenharmony_ci		else
43288c2ecf20Sopenharmony_ci			endcyl = (basepriv->real_cyl - 1);
43298c2ecf20Sopenharmony_ci	}
43308c2ecf20Sopenharmony_ci
43318c2ecf20Sopenharmony_ci	set_ch_t(&dedata->beg_ext, begcyl, beghead);
43328c2ecf20Sopenharmony_ci	set_ch_t(&dedata->end_ext, endcyl, endhead);
43338c2ecf20Sopenharmony_ci
43348c2ecf20Sopenharmony_ci	dedata->ep_format = 0x20; /* records per track is valid */
43358c2ecf20Sopenharmony_ci	dedata->ep_rec_per_track = blk_per_trk;
43368c2ecf20Sopenharmony_ci
43378c2ecf20Sopenharmony_ci	if (rec_on_trk) {
43388c2ecf20Sopenharmony_ci		switch (basepriv->rdc_data.dev_type) {
43398c2ecf20Sopenharmony_ci		case 0x3390:
43408c2ecf20Sopenharmony_ci			dn = ceil_quot(blksize + 6, 232);
43418c2ecf20Sopenharmony_ci			d = 9 + ceil_quot(blksize + 6 * (dn + 1), 34);
43428c2ecf20Sopenharmony_ci			sector = (49 + (rec_on_trk - 1) * (10 + d)) / 8;
43438c2ecf20Sopenharmony_ci			break;
43448c2ecf20Sopenharmony_ci		case 0x3380:
43458c2ecf20Sopenharmony_ci			d = 7 + ceil_quot(blksize + 12, 32);
43468c2ecf20Sopenharmony_ci			sector = (39 + (rec_on_trk - 1) * (8 + d)) / 7;
43478c2ecf20Sopenharmony_ci			break;
43488c2ecf20Sopenharmony_ci		}
43498c2ecf20Sopenharmony_ci	}
43508c2ecf20Sopenharmony_ci
43518c2ecf20Sopenharmony_ci	if (cmd == DASD_ECKD_CCW_READ_COUNT_MT) {
43528c2ecf20Sopenharmony_ci		lredata->auxiliary.length_valid = 0;
43538c2ecf20Sopenharmony_ci		lredata->auxiliary.length_scope = 0;
43548c2ecf20Sopenharmony_ci		lredata->sector = 0xff;
43558c2ecf20Sopenharmony_ci	} else {
43568c2ecf20Sopenharmony_ci		lredata->auxiliary.length_valid = 1;
43578c2ecf20Sopenharmony_ci		lredata->auxiliary.length_scope = 1;
43588c2ecf20Sopenharmony_ci		lredata->sector = sector;
43598c2ecf20Sopenharmony_ci	}
43608c2ecf20Sopenharmony_ci	lredata->auxiliary.imbedded_ccw_valid = 1;
43618c2ecf20Sopenharmony_ci	lredata->length = tlf;
43628c2ecf20Sopenharmony_ci	lredata->imbedded_ccw = cmd;
43638c2ecf20Sopenharmony_ci	lredata->count = count;
43648c2ecf20Sopenharmony_ci	set_ch_t(&lredata->seek_addr, begcyl, beghead);
43658c2ecf20Sopenharmony_ci	lredata->search_arg.cyl = lredata->seek_addr.cyl;
43668c2ecf20Sopenharmony_ci	lredata->search_arg.head = lredata->seek_addr.head;
43678c2ecf20Sopenharmony_ci	lredata->search_arg.record = rec_on_trk;
43688c2ecf20Sopenharmony_ci
43698c2ecf20Sopenharmony_ci	dcw = itcw_add_dcw(itcw, pfx_cmd, 0,
43708c2ecf20Sopenharmony_ci		     &pfxdata, sizeof(pfxdata), total_data_size);
43718c2ecf20Sopenharmony_ci	return PTR_ERR_OR_ZERO(dcw);
43728c2ecf20Sopenharmony_ci}
43738c2ecf20Sopenharmony_ci
43748c2ecf20Sopenharmony_cistatic struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track(
43758c2ecf20Sopenharmony_ci					       struct dasd_device *startdev,
43768c2ecf20Sopenharmony_ci					       struct dasd_block *block,
43778c2ecf20Sopenharmony_ci					       struct request *req,
43788c2ecf20Sopenharmony_ci					       sector_t first_rec,
43798c2ecf20Sopenharmony_ci					       sector_t last_rec,
43808c2ecf20Sopenharmony_ci					       sector_t first_trk,
43818c2ecf20Sopenharmony_ci					       sector_t last_trk,
43828c2ecf20Sopenharmony_ci					       unsigned int first_offs,
43838c2ecf20Sopenharmony_ci					       unsigned int last_offs,
43848c2ecf20Sopenharmony_ci					       unsigned int blk_per_trk,
43858c2ecf20Sopenharmony_ci					       unsigned int blksize)
43868c2ecf20Sopenharmony_ci{
43878c2ecf20Sopenharmony_ci	struct dasd_ccw_req *cqr;
43888c2ecf20Sopenharmony_ci	struct req_iterator iter;
43898c2ecf20Sopenharmony_ci	struct bio_vec bv;
43908c2ecf20Sopenharmony_ci	char *dst;
43918c2ecf20Sopenharmony_ci	unsigned int trkcount, ctidaw;
43928c2ecf20Sopenharmony_ci	unsigned char cmd;
43938c2ecf20Sopenharmony_ci	struct dasd_device *basedev;
43948c2ecf20Sopenharmony_ci	unsigned int tlf;
43958c2ecf20Sopenharmony_ci	struct itcw *itcw;
43968c2ecf20Sopenharmony_ci	struct tidaw *last_tidaw = NULL;
43978c2ecf20Sopenharmony_ci	int itcw_op;
43988c2ecf20Sopenharmony_ci	size_t itcw_size;
43998c2ecf20Sopenharmony_ci	u8 tidaw_flags;
44008c2ecf20Sopenharmony_ci	unsigned int seg_len, part_len, len_to_track_end;
44018c2ecf20Sopenharmony_ci	unsigned char new_track;
44028c2ecf20Sopenharmony_ci	sector_t recid, trkid;
44038c2ecf20Sopenharmony_ci	unsigned int offs;
44048c2ecf20Sopenharmony_ci	unsigned int count, count_to_trk_end;
44058c2ecf20Sopenharmony_ci	int ret;
44068c2ecf20Sopenharmony_ci
44078c2ecf20Sopenharmony_ci	basedev = block->base;
44088c2ecf20Sopenharmony_ci	if (rq_data_dir(req) == READ) {
44098c2ecf20Sopenharmony_ci		cmd = DASD_ECKD_CCW_READ_TRACK_DATA;
44108c2ecf20Sopenharmony_ci		itcw_op = ITCW_OP_READ;
44118c2ecf20Sopenharmony_ci	} else if (rq_data_dir(req) == WRITE) {
44128c2ecf20Sopenharmony_ci		cmd = DASD_ECKD_CCW_WRITE_TRACK_DATA;
44138c2ecf20Sopenharmony_ci		itcw_op = ITCW_OP_WRITE;
44148c2ecf20Sopenharmony_ci	} else
44158c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
44168c2ecf20Sopenharmony_ci
44178c2ecf20Sopenharmony_ci	/* trackbased I/O needs address all memory via TIDAWs,
44188c2ecf20Sopenharmony_ci	 * not just for 64 bit addresses. This allows us to map
44198c2ecf20Sopenharmony_ci	 * each segment directly to one tidaw.
44208c2ecf20Sopenharmony_ci	 * In the case of write requests, additional tidaws may
44218c2ecf20Sopenharmony_ci	 * be needed when a segment crosses a track boundary.
44228c2ecf20Sopenharmony_ci	 */
44238c2ecf20Sopenharmony_ci	trkcount = last_trk - first_trk + 1;
44248c2ecf20Sopenharmony_ci	ctidaw = 0;
44258c2ecf20Sopenharmony_ci	rq_for_each_segment(bv, req, iter) {
44268c2ecf20Sopenharmony_ci		++ctidaw;
44278c2ecf20Sopenharmony_ci	}
44288c2ecf20Sopenharmony_ci	if (rq_data_dir(req) == WRITE)
44298c2ecf20Sopenharmony_ci		ctidaw += (last_trk - first_trk);
44308c2ecf20Sopenharmony_ci
44318c2ecf20Sopenharmony_ci	/* Allocate the ccw request. */
44328c2ecf20Sopenharmony_ci	itcw_size = itcw_calc_size(0, ctidaw, 0);
44338c2ecf20Sopenharmony_ci	cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 0, itcw_size, startdev,
44348c2ecf20Sopenharmony_ci				   blk_mq_rq_to_pdu(req));
44358c2ecf20Sopenharmony_ci	if (IS_ERR(cqr))
44368c2ecf20Sopenharmony_ci		return cqr;
44378c2ecf20Sopenharmony_ci
44388c2ecf20Sopenharmony_ci	/* transfer length factor: how many bytes to read from the last track */
44398c2ecf20Sopenharmony_ci	if (first_trk == last_trk)
44408c2ecf20Sopenharmony_ci		tlf = last_offs - first_offs + 1;
44418c2ecf20Sopenharmony_ci	else
44428c2ecf20Sopenharmony_ci		tlf = last_offs + 1;
44438c2ecf20Sopenharmony_ci	tlf *= blksize;
44448c2ecf20Sopenharmony_ci
44458c2ecf20Sopenharmony_ci	itcw = itcw_init(cqr->data, itcw_size, itcw_op, 0, ctidaw, 0);
44468c2ecf20Sopenharmony_ci	if (IS_ERR(itcw)) {
44478c2ecf20Sopenharmony_ci		ret = -EINVAL;
44488c2ecf20Sopenharmony_ci		goto out_error;
44498c2ecf20Sopenharmony_ci	}
44508c2ecf20Sopenharmony_ci	cqr->cpaddr = itcw_get_tcw(itcw);
44518c2ecf20Sopenharmony_ci	if (prepare_itcw(itcw, first_trk, last_trk,
44528c2ecf20Sopenharmony_ci			 cmd, basedev, startdev,
44538c2ecf20Sopenharmony_ci			 first_offs + 1,
44548c2ecf20Sopenharmony_ci			 trkcount, blksize,
44558c2ecf20Sopenharmony_ci			 (last_rec - first_rec + 1) * blksize,
44568c2ecf20Sopenharmony_ci			 tlf, blk_per_trk) == -EAGAIN) {
44578c2ecf20Sopenharmony_ci		/* Clock not in sync and XRC is enabled.
44588c2ecf20Sopenharmony_ci		 * Try again later.
44598c2ecf20Sopenharmony_ci		 */
44608c2ecf20Sopenharmony_ci		ret = -EAGAIN;
44618c2ecf20Sopenharmony_ci		goto out_error;
44628c2ecf20Sopenharmony_ci	}
44638c2ecf20Sopenharmony_ci	len_to_track_end = 0;
44648c2ecf20Sopenharmony_ci	/*
44658c2ecf20Sopenharmony_ci	 * A tidaw can address 4k of memory, but must not cross page boundaries
44668c2ecf20Sopenharmony_ci	 * We can let the block layer handle this by setting
44678c2ecf20Sopenharmony_ci	 * blk_queue_segment_boundary to page boundaries and
44688c2ecf20Sopenharmony_ci	 * blk_max_segment_size to page size when setting up the request queue.
44698c2ecf20Sopenharmony_ci	 * For write requests, a TIDAW must not cross track boundaries, because
44708c2ecf20Sopenharmony_ci	 * we have to set the CBC flag on the last tidaw for each track.
44718c2ecf20Sopenharmony_ci	 */
44728c2ecf20Sopenharmony_ci	if (rq_data_dir(req) == WRITE) {
44738c2ecf20Sopenharmony_ci		new_track = 1;
44748c2ecf20Sopenharmony_ci		recid = first_rec;
44758c2ecf20Sopenharmony_ci		rq_for_each_segment(bv, req, iter) {
44768c2ecf20Sopenharmony_ci			dst = page_address(bv.bv_page) + bv.bv_offset;
44778c2ecf20Sopenharmony_ci			seg_len = bv.bv_len;
44788c2ecf20Sopenharmony_ci			while (seg_len) {
44798c2ecf20Sopenharmony_ci				if (new_track) {
44808c2ecf20Sopenharmony_ci					trkid = recid;
44818c2ecf20Sopenharmony_ci					offs = sector_div(trkid, blk_per_trk);
44828c2ecf20Sopenharmony_ci					count_to_trk_end = blk_per_trk - offs;
44838c2ecf20Sopenharmony_ci					count = min((last_rec - recid + 1),
44848c2ecf20Sopenharmony_ci						    (sector_t)count_to_trk_end);
44858c2ecf20Sopenharmony_ci					len_to_track_end = count * blksize;
44868c2ecf20Sopenharmony_ci					recid += count;
44878c2ecf20Sopenharmony_ci					new_track = 0;
44888c2ecf20Sopenharmony_ci				}
44898c2ecf20Sopenharmony_ci				part_len = min(seg_len, len_to_track_end);
44908c2ecf20Sopenharmony_ci				seg_len -= part_len;
44918c2ecf20Sopenharmony_ci				len_to_track_end -= part_len;
44928c2ecf20Sopenharmony_ci				/* We need to end the tidaw at track end */
44938c2ecf20Sopenharmony_ci				if (!len_to_track_end) {
44948c2ecf20Sopenharmony_ci					new_track = 1;
44958c2ecf20Sopenharmony_ci					tidaw_flags = TIDAW_FLAGS_INSERT_CBC;
44968c2ecf20Sopenharmony_ci				} else
44978c2ecf20Sopenharmony_ci					tidaw_flags = 0;
44988c2ecf20Sopenharmony_ci				last_tidaw = itcw_add_tidaw(itcw, tidaw_flags,
44998c2ecf20Sopenharmony_ci							    dst, part_len);
45008c2ecf20Sopenharmony_ci				if (IS_ERR(last_tidaw)) {
45018c2ecf20Sopenharmony_ci					ret = -EINVAL;
45028c2ecf20Sopenharmony_ci					goto out_error;
45038c2ecf20Sopenharmony_ci				}
45048c2ecf20Sopenharmony_ci				dst += part_len;
45058c2ecf20Sopenharmony_ci			}
45068c2ecf20Sopenharmony_ci		}
45078c2ecf20Sopenharmony_ci	} else {
45088c2ecf20Sopenharmony_ci		rq_for_each_segment(bv, req, iter) {
45098c2ecf20Sopenharmony_ci			dst = page_address(bv.bv_page) + bv.bv_offset;
45108c2ecf20Sopenharmony_ci			last_tidaw = itcw_add_tidaw(itcw, 0x00,
45118c2ecf20Sopenharmony_ci						    dst, bv.bv_len);
45128c2ecf20Sopenharmony_ci			if (IS_ERR(last_tidaw)) {
45138c2ecf20Sopenharmony_ci				ret = -EINVAL;
45148c2ecf20Sopenharmony_ci				goto out_error;
45158c2ecf20Sopenharmony_ci			}
45168c2ecf20Sopenharmony_ci		}
45178c2ecf20Sopenharmony_ci	}
45188c2ecf20Sopenharmony_ci	last_tidaw->flags |= TIDAW_FLAGS_LAST;
45198c2ecf20Sopenharmony_ci	last_tidaw->flags &= ~TIDAW_FLAGS_INSERT_CBC;
45208c2ecf20Sopenharmony_ci	itcw_finalize(itcw);
45218c2ecf20Sopenharmony_ci
45228c2ecf20Sopenharmony_ci	if (blk_noretry_request(req) ||
45238c2ecf20Sopenharmony_ci	    block->base->features & DASD_FEATURE_FAILFAST)
45248c2ecf20Sopenharmony_ci		set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags);
45258c2ecf20Sopenharmony_ci	cqr->cpmode = 1;
45268c2ecf20Sopenharmony_ci	cqr->startdev = startdev;
45278c2ecf20Sopenharmony_ci	cqr->memdev = startdev;
45288c2ecf20Sopenharmony_ci	cqr->block = block;
45298c2ecf20Sopenharmony_ci	cqr->expires = startdev->default_expires * HZ;	/* default 5 minutes */
45308c2ecf20Sopenharmony_ci	cqr->lpm = dasd_path_get_ppm(startdev);
45318c2ecf20Sopenharmony_ci	cqr->retries = startdev->default_retries;
45328c2ecf20Sopenharmony_ci	cqr->buildclk = get_tod_clock();
45338c2ecf20Sopenharmony_ci	cqr->status = DASD_CQR_FILLED;
45348c2ecf20Sopenharmony_ci
45358c2ecf20Sopenharmony_ci	/* Set flags to suppress output for expected errors */
45368c2ecf20Sopenharmony_ci	if (dasd_eckd_is_ese(basedev)) {
45378c2ecf20Sopenharmony_ci		set_bit(DASD_CQR_SUPPRESS_FP, &cqr->flags);
45388c2ecf20Sopenharmony_ci		set_bit(DASD_CQR_SUPPRESS_IL, &cqr->flags);
45398c2ecf20Sopenharmony_ci		set_bit(DASD_CQR_SUPPRESS_NRF, &cqr->flags);
45408c2ecf20Sopenharmony_ci	}
45418c2ecf20Sopenharmony_ci
45428c2ecf20Sopenharmony_ci	return cqr;
45438c2ecf20Sopenharmony_ciout_error:
45448c2ecf20Sopenharmony_ci	dasd_sfree_request(cqr, startdev);
45458c2ecf20Sopenharmony_ci	return ERR_PTR(ret);
45468c2ecf20Sopenharmony_ci}
45478c2ecf20Sopenharmony_ci
45488c2ecf20Sopenharmony_cistatic struct dasd_ccw_req *dasd_eckd_build_cp(struct dasd_device *startdev,
45498c2ecf20Sopenharmony_ci					       struct dasd_block *block,
45508c2ecf20Sopenharmony_ci					       struct request *req)
45518c2ecf20Sopenharmony_ci{
45528c2ecf20Sopenharmony_ci	int cmdrtd, cmdwtd;
45538c2ecf20Sopenharmony_ci	int use_prefix;
45548c2ecf20Sopenharmony_ci	int fcx_multitrack;
45558c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private;
45568c2ecf20Sopenharmony_ci	struct dasd_device *basedev;
45578c2ecf20Sopenharmony_ci	sector_t first_rec, last_rec;
45588c2ecf20Sopenharmony_ci	sector_t first_trk, last_trk;
45598c2ecf20Sopenharmony_ci	unsigned int first_offs, last_offs;
45608c2ecf20Sopenharmony_ci	unsigned int blk_per_trk, blksize;
45618c2ecf20Sopenharmony_ci	int cdlspecial;
45628c2ecf20Sopenharmony_ci	unsigned int data_size;
45638c2ecf20Sopenharmony_ci	struct dasd_ccw_req *cqr;
45648c2ecf20Sopenharmony_ci
45658c2ecf20Sopenharmony_ci	basedev = block->base;
45668c2ecf20Sopenharmony_ci	private = basedev->private;
45678c2ecf20Sopenharmony_ci
45688c2ecf20Sopenharmony_ci	/* Calculate number of blocks/records per track. */
45698c2ecf20Sopenharmony_ci	blksize = block->bp_block;
45708c2ecf20Sopenharmony_ci	blk_per_trk = recs_per_track(&private->rdc_data, 0, blksize);
45718c2ecf20Sopenharmony_ci	if (blk_per_trk == 0)
45728c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
45738c2ecf20Sopenharmony_ci	/* Calculate record id of first and last block. */
45748c2ecf20Sopenharmony_ci	first_rec = first_trk = blk_rq_pos(req) >> block->s2b_shift;
45758c2ecf20Sopenharmony_ci	first_offs = sector_div(first_trk, blk_per_trk);
45768c2ecf20Sopenharmony_ci	last_rec = last_trk =
45778c2ecf20Sopenharmony_ci		(blk_rq_pos(req) + blk_rq_sectors(req) - 1) >> block->s2b_shift;
45788c2ecf20Sopenharmony_ci	last_offs = sector_div(last_trk, blk_per_trk);
45798c2ecf20Sopenharmony_ci	cdlspecial = (private->uses_cdl && first_rec < 2*blk_per_trk);
45808c2ecf20Sopenharmony_ci
45818c2ecf20Sopenharmony_ci	fcx_multitrack = private->features.feature[40] & 0x20;
45828c2ecf20Sopenharmony_ci	data_size = blk_rq_bytes(req);
45838c2ecf20Sopenharmony_ci	if (data_size % blksize)
45848c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
45858c2ecf20Sopenharmony_ci	/* tpm write request add CBC data on each track boundary */
45868c2ecf20Sopenharmony_ci	if (rq_data_dir(req) == WRITE)
45878c2ecf20Sopenharmony_ci		data_size += (last_trk - first_trk) * 4;
45888c2ecf20Sopenharmony_ci
45898c2ecf20Sopenharmony_ci	/* is read track data and write track data in command mode supported? */
45908c2ecf20Sopenharmony_ci	cmdrtd = private->features.feature[9] & 0x20;
45918c2ecf20Sopenharmony_ci	cmdwtd = private->features.feature[12] & 0x40;
45928c2ecf20Sopenharmony_ci	use_prefix = private->features.feature[8] & 0x01;
45938c2ecf20Sopenharmony_ci
45948c2ecf20Sopenharmony_ci	cqr = NULL;
45958c2ecf20Sopenharmony_ci	if (cdlspecial || dasd_page_cache) {
45968c2ecf20Sopenharmony_ci		/* do nothing, just fall through to the cmd mode single case */
45978c2ecf20Sopenharmony_ci	} else if ((data_size <= private->fcx_max_data)
45988c2ecf20Sopenharmony_ci		   && (fcx_multitrack || (first_trk == last_trk))) {
45998c2ecf20Sopenharmony_ci		cqr = dasd_eckd_build_cp_tpm_track(startdev, block, req,
46008c2ecf20Sopenharmony_ci						    first_rec, last_rec,
46018c2ecf20Sopenharmony_ci						    first_trk, last_trk,
46028c2ecf20Sopenharmony_ci						    first_offs, last_offs,
46038c2ecf20Sopenharmony_ci						    blk_per_trk, blksize);
46048c2ecf20Sopenharmony_ci		if (IS_ERR(cqr) && (PTR_ERR(cqr) != -EAGAIN) &&
46058c2ecf20Sopenharmony_ci		    (PTR_ERR(cqr) != -ENOMEM))
46068c2ecf20Sopenharmony_ci			cqr = NULL;
46078c2ecf20Sopenharmony_ci	} else if (use_prefix &&
46088c2ecf20Sopenharmony_ci		   (((rq_data_dir(req) == READ) && cmdrtd) ||
46098c2ecf20Sopenharmony_ci		    ((rq_data_dir(req) == WRITE) && cmdwtd))) {
46108c2ecf20Sopenharmony_ci		cqr = dasd_eckd_build_cp_cmd_track(startdev, block, req,
46118c2ecf20Sopenharmony_ci						   first_rec, last_rec,
46128c2ecf20Sopenharmony_ci						   first_trk, last_trk,
46138c2ecf20Sopenharmony_ci						   first_offs, last_offs,
46148c2ecf20Sopenharmony_ci						   blk_per_trk, blksize);
46158c2ecf20Sopenharmony_ci		if (IS_ERR(cqr) && (PTR_ERR(cqr) != -EAGAIN) &&
46168c2ecf20Sopenharmony_ci		    (PTR_ERR(cqr) != -ENOMEM))
46178c2ecf20Sopenharmony_ci			cqr = NULL;
46188c2ecf20Sopenharmony_ci	}
46198c2ecf20Sopenharmony_ci	if (!cqr)
46208c2ecf20Sopenharmony_ci		cqr = dasd_eckd_build_cp_cmd_single(startdev, block, req,
46218c2ecf20Sopenharmony_ci						    first_rec, last_rec,
46228c2ecf20Sopenharmony_ci						    first_trk, last_trk,
46238c2ecf20Sopenharmony_ci						    first_offs, last_offs,
46248c2ecf20Sopenharmony_ci						    blk_per_trk, blksize);
46258c2ecf20Sopenharmony_ci	return cqr;
46268c2ecf20Sopenharmony_ci}
46278c2ecf20Sopenharmony_ci
46288c2ecf20Sopenharmony_cistatic struct dasd_ccw_req *dasd_eckd_build_cp_raw(struct dasd_device *startdev,
46298c2ecf20Sopenharmony_ci						   struct dasd_block *block,
46308c2ecf20Sopenharmony_ci						   struct request *req)
46318c2ecf20Sopenharmony_ci{
46328c2ecf20Sopenharmony_ci	sector_t start_padding_sectors, end_sector_offset, end_padding_sectors;
46338c2ecf20Sopenharmony_ci	unsigned int seg_len, len_to_track_end;
46348c2ecf20Sopenharmony_ci	unsigned int cidaw, cplength, datasize;
46358c2ecf20Sopenharmony_ci	sector_t first_trk, last_trk, sectors;
46368c2ecf20Sopenharmony_ci	struct dasd_eckd_private *base_priv;
46378c2ecf20Sopenharmony_ci	struct dasd_device *basedev;
46388c2ecf20Sopenharmony_ci	struct req_iterator iter;
46398c2ecf20Sopenharmony_ci	struct dasd_ccw_req *cqr;
46408c2ecf20Sopenharmony_ci	unsigned int trkcount;
46418c2ecf20Sopenharmony_ci	unsigned long *idaws;
46428c2ecf20Sopenharmony_ci	unsigned int size;
46438c2ecf20Sopenharmony_ci	unsigned char cmd;
46448c2ecf20Sopenharmony_ci	struct bio_vec bv;
46458c2ecf20Sopenharmony_ci	struct ccw1 *ccw;
46468c2ecf20Sopenharmony_ci	int use_prefix;
46478c2ecf20Sopenharmony_ci	void *data;
46488c2ecf20Sopenharmony_ci	char *dst;
46498c2ecf20Sopenharmony_ci
46508c2ecf20Sopenharmony_ci	/*
46518c2ecf20Sopenharmony_ci	 * raw track access needs to be mutiple of 64k and on 64k boundary
46528c2ecf20Sopenharmony_ci	 * For read requests we can fix an incorrect alignment by padding
46538c2ecf20Sopenharmony_ci	 * the request with dummy pages.
46548c2ecf20Sopenharmony_ci	 */
46558c2ecf20Sopenharmony_ci	start_padding_sectors = blk_rq_pos(req) % DASD_RAW_SECTORS_PER_TRACK;
46568c2ecf20Sopenharmony_ci	end_sector_offset = (blk_rq_pos(req) + blk_rq_sectors(req)) %
46578c2ecf20Sopenharmony_ci		DASD_RAW_SECTORS_PER_TRACK;
46588c2ecf20Sopenharmony_ci	end_padding_sectors = (DASD_RAW_SECTORS_PER_TRACK - end_sector_offset) %
46598c2ecf20Sopenharmony_ci		DASD_RAW_SECTORS_PER_TRACK;
46608c2ecf20Sopenharmony_ci	basedev = block->base;
46618c2ecf20Sopenharmony_ci	if ((start_padding_sectors || end_padding_sectors) &&
46628c2ecf20Sopenharmony_ci	    (rq_data_dir(req) == WRITE)) {
46638c2ecf20Sopenharmony_ci		DBF_DEV_EVENT(DBF_ERR, basedev,
46648c2ecf20Sopenharmony_ci			      "raw write not track aligned (%llu,%llu) req %p",
46658c2ecf20Sopenharmony_ci			      start_padding_sectors, end_padding_sectors, req);
46668c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
46678c2ecf20Sopenharmony_ci	}
46688c2ecf20Sopenharmony_ci
46698c2ecf20Sopenharmony_ci	first_trk = blk_rq_pos(req) / DASD_RAW_SECTORS_PER_TRACK;
46708c2ecf20Sopenharmony_ci	last_trk = (blk_rq_pos(req) + blk_rq_sectors(req) - 1) /
46718c2ecf20Sopenharmony_ci		DASD_RAW_SECTORS_PER_TRACK;
46728c2ecf20Sopenharmony_ci	trkcount = last_trk - first_trk + 1;
46738c2ecf20Sopenharmony_ci
46748c2ecf20Sopenharmony_ci	if (rq_data_dir(req) == READ)
46758c2ecf20Sopenharmony_ci		cmd = DASD_ECKD_CCW_READ_TRACK;
46768c2ecf20Sopenharmony_ci	else if (rq_data_dir(req) == WRITE)
46778c2ecf20Sopenharmony_ci		cmd = DASD_ECKD_CCW_WRITE_FULL_TRACK;
46788c2ecf20Sopenharmony_ci	else
46798c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
46808c2ecf20Sopenharmony_ci
46818c2ecf20Sopenharmony_ci	/*
46828c2ecf20Sopenharmony_ci	 * Raw track based I/O needs IDAWs for each page,
46838c2ecf20Sopenharmony_ci	 * and not just for 64 bit addresses.
46848c2ecf20Sopenharmony_ci	 */
46858c2ecf20Sopenharmony_ci	cidaw = trkcount * DASD_RAW_BLOCK_PER_TRACK;
46868c2ecf20Sopenharmony_ci
46878c2ecf20Sopenharmony_ci	/*
46888c2ecf20Sopenharmony_ci	 * struct PFX_eckd_data and struct LRE_eckd_data can have up to 2 bytes
46898c2ecf20Sopenharmony_ci	 * of extended parameter. This is needed for write full track.
46908c2ecf20Sopenharmony_ci	 */
46918c2ecf20Sopenharmony_ci	base_priv = basedev->private;
46928c2ecf20Sopenharmony_ci	use_prefix = base_priv->features.feature[8] & 0x01;
46938c2ecf20Sopenharmony_ci	if (use_prefix) {
46948c2ecf20Sopenharmony_ci		cplength = 1 + trkcount;
46958c2ecf20Sopenharmony_ci		size = sizeof(struct PFX_eckd_data) + 2;
46968c2ecf20Sopenharmony_ci	} else {
46978c2ecf20Sopenharmony_ci		cplength = 2 + trkcount;
46988c2ecf20Sopenharmony_ci		size = sizeof(struct DE_eckd_data) +
46998c2ecf20Sopenharmony_ci			sizeof(struct LRE_eckd_data) + 2;
47008c2ecf20Sopenharmony_ci	}
47018c2ecf20Sopenharmony_ci	size = ALIGN(size, 8);
47028c2ecf20Sopenharmony_ci
47038c2ecf20Sopenharmony_ci	datasize = size + cidaw * sizeof(unsigned long);
47048c2ecf20Sopenharmony_ci
47058c2ecf20Sopenharmony_ci	/* Allocate the ccw request. */
47068c2ecf20Sopenharmony_ci	cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, cplength,
47078c2ecf20Sopenharmony_ci				   datasize, startdev, blk_mq_rq_to_pdu(req));
47088c2ecf20Sopenharmony_ci	if (IS_ERR(cqr))
47098c2ecf20Sopenharmony_ci		return cqr;
47108c2ecf20Sopenharmony_ci
47118c2ecf20Sopenharmony_ci	ccw = cqr->cpaddr;
47128c2ecf20Sopenharmony_ci	data = cqr->data;
47138c2ecf20Sopenharmony_ci
47148c2ecf20Sopenharmony_ci	if (use_prefix) {
47158c2ecf20Sopenharmony_ci		prefix_LRE(ccw++, data, first_trk, last_trk, cmd, basedev,
47168c2ecf20Sopenharmony_ci			   startdev, 1, 0, trkcount, 0, 0);
47178c2ecf20Sopenharmony_ci	} else {
47188c2ecf20Sopenharmony_ci		define_extent(ccw++, data, first_trk, last_trk, cmd, basedev, 0);
47198c2ecf20Sopenharmony_ci		ccw[-1].flags |= CCW_FLAG_CC;
47208c2ecf20Sopenharmony_ci
47218c2ecf20Sopenharmony_ci		data += sizeof(struct DE_eckd_data);
47228c2ecf20Sopenharmony_ci		locate_record_ext(ccw++, data, first_trk, 0,
47238c2ecf20Sopenharmony_ci				  trkcount, cmd, basedev, 0, 0);
47248c2ecf20Sopenharmony_ci	}
47258c2ecf20Sopenharmony_ci
47268c2ecf20Sopenharmony_ci	idaws = (unsigned long *)(cqr->data + size);
47278c2ecf20Sopenharmony_ci	len_to_track_end = 0;
47288c2ecf20Sopenharmony_ci	if (start_padding_sectors) {
47298c2ecf20Sopenharmony_ci		ccw[-1].flags |= CCW_FLAG_CC;
47308c2ecf20Sopenharmony_ci		ccw->cmd_code = cmd;
47318c2ecf20Sopenharmony_ci		/* maximum 3390 track size */
47328c2ecf20Sopenharmony_ci		ccw->count = 57326;
47338c2ecf20Sopenharmony_ci		/* 64k map to one track */
47348c2ecf20Sopenharmony_ci		len_to_track_end = 65536 - start_padding_sectors * 512;
47358c2ecf20Sopenharmony_ci		ccw->cda = (__u32)(addr_t)idaws;
47368c2ecf20Sopenharmony_ci		ccw->flags |= CCW_FLAG_IDA;
47378c2ecf20Sopenharmony_ci		ccw->flags |= CCW_FLAG_SLI;
47388c2ecf20Sopenharmony_ci		ccw++;
47398c2ecf20Sopenharmony_ci		for (sectors = 0; sectors < start_padding_sectors; sectors += 8)
47408c2ecf20Sopenharmony_ci			idaws = idal_create_words(idaws, rawpadpage, PAGE_SIZE);
47418c2ecf20Sopenharmony_ci	}
47428c2ecf20Sopenharmony_ci	rq_for_each_segment(bv, req, iter) {
47438c2ecf20Sopenharmony_ci		dst = page_address(bv.bv_page) + bv.bv_offset;
47448c2ecf20Sopenharmony_ci		seg_len = bv.bv_len;
47458c2ecf20Sopenharmony_ci		if (cmd == DASD_ECKD_CCW_READ_TRACK)
47468c2ecf20Sopenharmony_ci			memset(dst, 0, seg_len);
47478c2ecf20Sopenharmony_ci		if (!len_to_track_end) {
47488c2ecf20Sopenharmony_ci			ccw[-1].flags |= CCW_FLAG_CC;
47498c2ecf20Sopenharmony_ci			ccw->cmd_code = cmd;
47508c2ecf20Sopenharmony_ci			/* maximum 3390 track size */
47518c2ecf20Sopenharmony_ci			ccw->count = 57326;
47528c2ecf20Sopenharmony_ci			/* 64k map to one track */
47538c2ecf20Sopenharmony_ci			len_to_track_end = 65536;
47548c2ecf20Sopenharmony_ci			ccw->cda = (__u32)(addr_t)idaws;
47558c2ecf20Sopenharmony_ci			ccw->flags |= CCW_FLAG_IDA;
47568c2ecf20Sopenharmony_ci			ccw->flags |= CCW_FLAG_SLI;
47578c2ecf20Sopenharmony_ci			ccw++;
47588c2ecf20Sopenharmony_ci		}
47598c2ecf20Sopenharmony_ci		len_to_track_end -= seg_len;
47608c2ecf20Sopenharmony_ci		idaws = idal_create_words(idaws, dst, seg_len);
47618c2ecf20Sopenharmony_ci	}
47628c2ecf20Sopenharmony_ci	for (sectors = 0; sectors < end_padding_sectors; sectors += 8)
47638c2ecf20Sopenharmony_ci		idaws = idal_create_words(idaws, rawpadpage, PAGE_SIZE);
47648c2ecf20Sopenharmony_ci	if (blk_noretry_request(req) ||
47658c2ecf20Sopenharmony_ci	    block->base->features & DASD_FEATURE_FAILFAST)
47668c2ecf20Sopenharmony_ci		set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags);
47678c2ecf20Sopenharmony_ci	cqr->startdev = startdev;
47688c2ecf20Sopenharmony_ci	cqr->memdev = startdev;
47698c2ecf20Sopenharmony_ci	cqr->block = block;
47708c2ecf20Sopenharmony_ci	cqr->expires = startdev->default_expires * HZ;
47718c2ecf20Sopenharmony_ci	cqr->lpm = dasd_path_get_ppm(startdev);
47728c2ecf20Sopenharmony_ci	cqr->retries = startdev->default_retries;
47738c2ecf20Sopenharmony_ci	cqr->buildclk = get_tod_clock();
47748c2ecf20Sopenharmony_ci	cqr->status = DASD_CQR_FILLED;
47758c2ecf20Sopenharmony_ci
47768c2ecf20Sopenharmony_ci	return cqr;
47778c2ecf20Sopenharmony_ci}
47788c2ecf20Sopenharmony_ci
47798c2ecf20Sopenharmony_ci
47808c2ecf20Sopenharmony_cistatic int
47818c2ecf20Sopenharmony_cidasd_eckd_free_cp(struct dasd_ccw_req *cqr, struct request *req)
47828c2ecf20Sopenharmony_ci{
47838c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private;
47848c2ecf20Sopenharmony_ci	struct ccw1 *ccw;
47858c2ecf20Sopenharmony_ci	struct req_iterator iter;
47868c2ecf20Sopenharmony_ci	struct bio_vec bv;
47878c2ecf20Sopenharmony_ci	char *dst, *cda;
47888c2ecf20Sopenharmony_ci	unsigned int blksize, blk_per_trk, off;
47898c2ecf20Sopenharmony_ci	sector_t recid;
47908c2ecf20Sopenharmony_ci	int status;
47918c2ecf20Sopenharmony_ci
47928c2ecf20Sopenharmony_ci	if (!dasd_page_cache)
47938c2ecf20Sopenharmony_ci		goto out;
47948c2ecf20Sopenharmony_ci	private = cqr->block->base->private;
47958c2ecf20Sopenharmony_ci	blksize = cqr->block->bp_block;
47968c2ecf20Sopenharmony_ci	blk_per_trk = recs_per_track(&private->rdc_data, 0, blksize);
47978c2ecf20Sopenharmony_ci	recid = blk_rq_pos(req) >> cqr->block->s2b_shift;
47988c2ecf20Sopenharmony_ci	ccw = cqr->cpaddr;
47998c2ecf20Sopenharmony_ci	/* Skip over define extent & locate record. */
48008c2ecf20Sopenharmony_ci	ccw++;
48018c2ecf20Sopenharmony_ci	if (private->uses_cdl == 0 || recid > 2*blk_per_trk)
48028c2ecf20Sopenharmony_ci		ccw++;
48038c2ecf20Sopenharmony_ci	rq_for_each_segment(bv, req, iter) {
48048c2ecf20Sopenharmony_ci		dst = page_address(bv.bv_page) + bv.bv_offset;
48058c2ecf20Sopenharmony_ci		for (off = 0; off < bv.bv_len; off += blksize) {
48068c2ecf20Sopenharmony_ci			/* Skip locate record. */
48078c2ecf20Sopenharmony_ci			if (private->uses_cdl && recid <= 2*blk_per_trk)
48088c2ecf20Sopenharmony_ci				ccw++;
48098c2ecf20Sopenharmony_ci			if (dst) {
48108c2ecf20Sopenharmony_ci				if (ccw->flags & CCW_FLAG_IDA)
48118c2ecf20Sopenharmony_ci					cda = *((char **)((addr_t) ccw->cda));
48128c2ecf20Sopenharmony_ci				else
48138c2ecf20Sopenharmony_ci					cda = (char *)((addr_t) ccw->cda);
48148c2ecf20Sopenharmony_ci				if (dst != cda) {
48158c2ecf20Sopenharmony_ci					if (rq_data_dir(req) == READ)
48168c2ecf20Sopenharmony_ci						memcpy(dst, cda, bv.bv_len);
48178c2ecf20Sopenharmony_ci					kmem_cache_free(dasd_page_cache,
48188c2ecf20Sopenharmony_ci					    (void *)((addr_t)cda & PAGE_MASK));
48198c2ecf20Sopenharmony_ci				}
48208c2ecf20Sopenharmony_ci				dst = NULL;
48218c2ecf20Sopenharmony_ci			}
48228c2ecf20Sopenharmony_ci			ccw++;
48238c2ecf20Sopenharmony_ci			recid++;
48248c2ecf20Sopenharmony_ci		}
48258c2ecf20Sopenharmony_ci	}
48268c2ecf20Sopenharmony_ciout:
48278c2ecf20Sopenharmony_ci	status = cqr->status == DASD_CQR_DONE;
48288c2ecf20Sopenharmony_ci	dasd_sfree_request(cqr, cqr->memdev);
48298c2ecf20Sopenharmony_ci	return status;
48308c2ecf20Sopenharmony_ci}
48318c2ecf20Sopenharmony_ci
48328c2ecf20Sopenharmony_ci/*
48338c2ecf20Sopenharmony_ci * Modify ccw/tcw in cqr so it can be started on a base device.
48348c2ecf20Sopenharmony_ci *
48358c2ecf20Sopenharmony_ci * Note that this is not enough to restart the cqr!
48368c2ecf20Sopenharmony_ci * Either reset cqr->startdev as well (summary unit check handling)
48378c2ecf20Sopenharmony_ci * or restart via separate cqr (as in ERP handling).
48388c2ecf20Sopenharmony_ci */
48398c2ecf20Sopenharmony_civoid dasd_eckd_reset_ccw_to_base_io(struct dasd_ccw_req *cqr)
48408c2ecf20Sopenharmony_ci{
48418c2ecf20Sopenharmony_ci	struct ccw1 *ccw;
48428c2ecf20Sopenharmony_ci	struct PFX_eckd_data *pfxdata;
48438c2ecf20Sopenharmony_ci	struct tcw *tcw;
48448c2ecf20Sopenharmony_ci	struct tccb *tccb;
48458c2ecf20Sopenharmony_ci	struct dcw *dcw;
48468c2ecf20Sopenharmony_ci
48478c2ecf20Sopenharmony_ci	if (cqr->cpmode == 1) {
48488c2ecf20Sopenharmony_ci		tcw = cqr->cpaddr;
48498c2ecf20Sopenharmony_ci		tccb = tcw_get_tccb(tcw);
48508c2ecf20Sopenharmony_ci		dcw = (struct dcw *)&tccb->tca[0];
48518c2ecf20Sopenharmony_ci		pfxdata = (struct PFX_eckd_data *)&dcw->cd[0];
48528c2ecf20Sopenharmony_ci		pfxdata->validity.verify_base = 0;
48538c2ecf20Sopenharmony_ci		pfxdata->validity.hyper_pav = 0;
48548c2ecf20Sopenharmony_ci	} else {
48558c2ecf20Sopenharmony_ci		ccw = cqr->cpaddr;
48568c2ecf20Sopenharmony_ci		pfxdata = cqr->data;
48578c2ecf20Sopenharmony_ci		if (ccw->cmd_code == DASD_ECKD_CCW_PFX) {
48588c2ecf20Sopenharmony_ci			pfxdata->validity.verify_base = 0;
48598c2ecf20Sopenharmony_ci			pfxdata->validity.hyper_pav = 0;
48608c2ecf20Sopenharmony_ci		}
48618c2ecf20Sopenharmony_ci	}
48628c2ecf20Sopenharmony_ci}
48638c2ecf20Sopenharmony_ci
48648c2ecf20Sopenharmony_ci#define DASD_ECKD_CHANQ_MAX_SIZE 4
48658c2ecf20Sopenharmony_ci
48668c2ecf20Sopenharmony_cistatic struct dasd_ccw_req *dasd_eckd_build_alias_cp(struct dasd_device *base,
48678c2ecf20Sopenharmony_ci						     struct dasd_block *block,
48688c2ecf20Sopenharmony_ci						     struct request *req)
48698c2ecf20Sopenharmony_ci{
48708c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private;
48718c2ecf20Sopenharmony_ci	struct dasd_device *startdev;
48728c2ecf20Sopenharmony_ci	unsigned long flags;
48738c2ecf20Sopenharmony_ci	struct dasd_ccw_req *cqr;
48748c2ecf20Sopenharmony_ci
48758c2ecf20Sopenharmony_ci	startdev = dasd_alias_get_start_dev(base);
48768c2ecf20Sopenharmony_ci	if (!startdev)
48778c2ecf20Sopenharmony_ci		startdev = base;
48788c2ecf20Sopenharmony_ci	private = startdev->private;
48798c2ecf20Sopenharmony_ci	if (private->count >= DASD_ECKD_CHANQ_MAX_SIZE)
48808c2ecf20Sopenharmony_ci		return ERR_PTR(-EBUSY);
48818c2ecf20Sopenharmony_ci
48828c2ecf20Sopenharmony_ci	spin_lock_irqsave(get_ccwdev_lock(startdev->cdev), flags);
48838c2ecf20Sopenharmony_ci	private->count++;
48848c2ecf20Sopenharmony_ci	if ((base->features & DASD_FEATURE_USERAW))
48858c2ecf20Sopenharmony_ci		cqr = dasd_eckd_build_cp_raw(startdev, block, req);
48868c2ecf20Sopenharmony_ci	else
48878c2ecf20Sopenharmony_ci		cqr = dasd_eckd_build_cp(startdev, block, req);
48888c2ecf20Sopenharmony_ci	if (IS_ERR(cqr))
48898c2ecf20Sopenharmony_ci		private->count--;
48908c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(get_ccwdev_lock(startdev->cdev), flags);
48918c2ecf20Sopenharmony_ci	return cqr;
48928c2ecf20Sopenharmony_ci}
48938c2ecf20Sopenharmony_ci
48948c2ecf20Sopenharmony_cistatic int dasd_eckd_free_alias_cp(struct dasd_ccw_req *cqr,
48958c2ecf20Sopenharmony_ci				   struct request *req)
48968c2ecf20Sopenharmony_ci{
48978c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private;
48988c2ecf20Sopenharmony_ci	unsigned long flags;
48998c2ecf20Sopenharmony_ci
49008c2ecf20Sopenharmony_ci	spin_lock_irqsave(get_ccwdev_lock(cqr->memdev->cdev), flags);
49018c2ecf20Sopenharmony_ci	private = cqr->memdev->private;
49028c2ecf20Sopenharmony_ci	private->count--;
49038c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(get_ccwdev_lock(cqr->memdev->cdev), flags);
49048c2ecf20Sopenharmony_ci	return dasd_eckd_free_cp(cqr, req);
49058c2ecf20Sopenharmony_ci}
49068c2ecf20Sopenharmony_ci
49078c2ecf20Sopenharmony_cistatic int
49088c2ecf20Sopenharmony_cidasd_eckd_fill_info(struct dasd_device * device,
49098c2ecf20Sopenharmony_ci		    struct dasd_information2_t * info)
49108c2ecf20Sopenharmony_ci{
49118c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
49128c2ecf20Sopenharmony_ci
49138c2ecf20Sopenharmony_ci	info->label_block = 2;
49148c2ecf20Sopenharmony_ci	info->FBA_layout = private->uses_cdl ? 0 : 1;
49158c2ecf20Sopenharmony_ci	info->format = private->uses_cdl ? DASD_FORMAT_CDL : DASD_FORMAT_LDL;
49168c2ecf20Sopenharmony_ci	info->characteristics_size = sizeof(private->rdc_data);
49178c2ecf20Sopenharmony_ci	memcpy(info->characteristics, &private->rdc_data,
49188c2ecf20Sopenharmony_ci	       sizeof(private->rdc_data));
49198c2ecf20Sopenharmony_ci	info->confdata_size = min((unsigned long)private->conf_len,
49208c2ecf20Sopenharmony_ci				  sizeof(info->configuration_data));
49218c2ecf20Sopenharmony_ci	memcpy(info->configuration_data, private->conf_data,
49228c2ecf20Sopenharmony_ci	       info->confdata_size);
49238c2ecf20Sopenharmony_ci	return 0;
49248c2ecf20Sopenharmony_ci}
49258c2ecf20Sopenharmony_ci
49268c2ecf20Sopenharmony_ci/*
49278c2ecf20Sopenharmony_ci * SECTION: ioctl functions for eckd devices.
49288c2ecf20Sopenharmony_ci */
49298c2ecf20Sopenharmony_ci
49308c2ecf20Sopenharmony_ci/*
49318c2ecf20Sopenharmony_ci * Release device ioctl.
49328c2ecf20Sopenharmony_ci * Buils a channel programm to releases a prior reserved
49338c2ecf20Sopenharmony_ci * (see dasd_eckd_reserve) device.
49348c2ecf20Sopenharmony_ci */
49358c2ecf20Sopenharmony_cistatic int
49368c2ecf20Sopenharmony_cidasd_eckd_release(struct dasd_device *device)
49378c2ecf20Sopenharmony_ci{
49388c2ecf20Sopenharmony_ci	struct dasd_ccw_req *cqr;
49398c2ecf20Sopenharmony_ci	int rc;
49408c2ecf20Sopenharmony_ci	struct ccw1 *ccw;
49418c2ecf20Sopenharmony_ci	int useglobal;
49428c2ecf20Sopenharmony_ci
49438c2ecf20Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
49448c2ecf20Sopenharmony_ci		return -EACCES;
49458c2ecf20Sopenharmony_ci
49468c2ecf20Sopenharmony_ci	useglobal = 0;
49478c2ecf20Sopenharmony_ci	cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1, 32, device, NULL);
49488c2ecf20Sopenharmony_ci	if (IS_ERR(cqr)) {
49498c2ecf20Sopenharmony_ci		mutex_lock(&dasd_reserve_mutex);
49508c2ecf20Sopenharmony_ci		useglobal = 1;
49518c2ecf20Sopenharmony_ci		cqr = &dasd_reserve_req->cqr;
49528c2ecf20Sopenharmony_ci		memset(cqr, 0, sizeof(*cqr));
49538c2ecf20Sopenharmony_ci		memset(&dasd_reserve_req->ccw, 0,
49548c2ecf20Sopenharmony_ci		       sizeof(dasd_reserve_req->ccw));
49558c2ecf20Sopenharmony_ci		cqr->cpaddr = &dasd_reserve_req->ccw;
49568c2ecf20Sopenharmony_ci		cqr->data = &dasd_reserve_req->data;
49578c2ecf20Sopenharmony_ci		cqr->magic = DASD_ECKD_MAGIC;
49588c2ecf20Sopenharmony_ci	}
49598c2ecf20Sopenharmony_ci	ccw = cqr->cpaddr;
49608c2ecf20Sopenharmony_ci	ccw->cmd_code = DASD_ECKD_CCW_RELEASE;
49618c2ecf20Sopenharmony_ci	ccw->flags |= CCW_FLAG_SLI;
49628c2ecf20Sopenharmony_ci	ccw->count = 32;
49638c2ecf20Sopenharmony_ci	ccw->cda = (__u32)(addr_t) cqr->data;
49648c2ecf20Sopenharmony_ci	cqr->startdev = device;
49658c2ecf20Sopenharmony_ci	cqr->memdev = device;
49668c2ecf20Sopenharmony_ci	clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
49678c2ecf20Sopenharmony_ci	set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags);
49688c2ecf20Sopenharmony_ci	cqr->retries = 2;	/* set retry counter to enable basic ERP */
49698c2ecf20Sopenharmony_ci	cqr->expires = 2 * HZ;
49708c2ecf20Sopenharmony_ci	cqr->buildclk = get_tod_clock();
49718c2ecf20Sopenharmony_ci	cqr->status = DASD_CQR_FILLED;
49728c2ecf20Sopenharmony_ci
49738c2ecf20Sopenharmony_ci	rc = dasd_sleep_on_immediatly(cqr);
49748c2ecf20Sopenharmony_ci	if (!rc)
49758c2ecf20Sopenharmony_ci		clear_bit(DASD_FLAG_IS_RESERVED, &device->flags);
49768c2ecf20Sopenharmony_ci
49778c2ecf20Sopenharmony_ci	if (useglobal)
49788c2ecf20Sopenharmony_ci		mutex_unlock(&dasd_reserve_mutex);
49798c2ecf20Sopenharmony_ci	else
49808c2ecf20Sopenharmony_ci		dasd_sfree_request(cqr, cqr->memdev);
49818c2ecf20Sopenharmony_ci	return rc;
49828c2ecf20Sopenharmony_ci}
49838c2ecf20Sopenharmony_ci
49848c2ecf20Sopenharmony_ci/*
49858c2ecf20Sopenharmony_ci * Reserve device ioctl.
49868c2ecf20Sopenharmony_ci * Options are set to 'synchronous wait for interrupt' and
49878c2ecf20Sopenharmony_ci * 'timeout the request'. This leads to a terminate IO if
49888c2ecf20Sopenharmony_ci * the interrupt is outstanding for a certain time.
49898c2ecf20Sopenharmony_ci */
49908c2ecf20Sopenharmony_cistatic int
49918c2ecf20Sopenharmony_cidasd_eckd_reserve(struct dasd_device *device)
49928c2ecf20Sopenharmony_ci{
49938c2ecf20Sopenharmony_ci	struct dasd_ccw_req *cqr;
49948c2ecf20Sopenharmony_ci	int rc;
49958c2ecf20Sopenharmony_ci	struct ccw1 *ccw;
49968c2ecf20Sopenharmony_ci	int useglobal;
49978c2ecf20Sopenharmony_ci
49988c2ecf20Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
49998c2ecf20Sopenharmony_ci		return -EACCES;
50008c2ecf20Sopenharmony_ci
50018c2ecf20Sopenharmony_ci	useglobal = 0;
50028c2ecf20Sopenharmony_ci	cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1, 32, device, NULL);
50038c2ecf20Sopenharmony_ci	if (IS_ERR(cqr)) {
50048c2ecf20Sopenharmony_ci		mutex_lock(&dasd_reserve_mutex);
50058c2ecf20Sopenharmony_ci		useglobal = 1;
50068c2ecf20Sopenharmony_ci		cqr = &dasd_reserve_req->cqr;
50078c2ecf20Sopenharmony_ci		memset(cqr, 0, sizeof(*cqr));
50088c2ecf20Sopenharmony_ci		memset(&dasd_reserve_req->ccw, 0,
50098c2ecf20Sopenharmony_ci		       sizeof(dasd_reserve_req->ccw));
50108c2ecf20Sopenharmony_ci		cqr->cpaddr = &dasd_reserve_req->ccw;
50118c2ecf20Sopenharmony_ci		cqr->data = &dasd_reserve_req->data;
50128c2ecf20Sopenharmony_ci		cqr->magic = DASD_ECKD_MAGIC;
50138c2ecf20Sopenharmony_ci	}
50148c2ecf20Sopenharmony_ci	ccw = cqr->cpaddr;
50158c2ecf20Sopenharmony_ci	ccw->cmd_code = DASD_ECKD_CCW_RESERVE;
50168c2ecf20Sopenharmony_ci	ccw->flags |= CCW_FLAG_SLI;
50178c2ecf20Sopenharmony_ci	ccw->count = 32;
50188c2ecf20Sopenharmony_ci	ccw->cda = (__u32)(addr_t) cqr->data;
50198c2ecf20Sopenharmony_ci	cqr->startdev = device;
50208c2ecf20Sopenharmony_ci	cqr->memdev = device;
50218c2ecf20Sopenharmony_ci	clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
50228c2ecf20Sopenharmony_ci	set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags);
50238c2ecf20Sopenharmony_ci	cqr->retries = 2;	/* set retry counter to enable basic ERP */
50248c2ecf20Sopenharmony_ci	cqr->expires = 2 * HZ;
50258c2ecf20Sopenharmony_ci	cqr->buildclk = get_tod_clock();
50268c2ecf20Sopenharmony_ci	cqr->status = DASD_CQR_FILLED;
50278c2ecf20Sopenharmony_ci
50288c2ecf20Sopenharmony_ci	rc = dasd_sleep_on_immediatly(cqr);
50298c2ecf20Sopenharmony_ci	if (!rc)
50308c2ecf20Sopenharmony_ci		set_bit(DASD_FLAG_IS_RESERVED, &device->flags);
50318c2ecf20Sopenharmony_ci
50328c2ecf20Sopenharmony_ci	if (useglobal)
50338c2ecf20Sopenharmony_ci		mutex_unlock(&dasd_reserve_mutex);
50348c2ecf20Sopenharmony_ci	else
50358c2ecf20Sopenharmony_ci		dasd_sfree_request(cqr, cqr->memdev);
50368c2ecf20Sopenharmony_ci	return rc;
50378c2ecf20Sopenharmony_ci}
50388c2ecf20Sopenharmony_ci
50398c2ecf20Sopenharmony_ci/*
50408c2ecf20Sopenharmony_ci * Steal lock ioctl - unconditional reserve device.
50418c2ecf20Sopenharmony_ci * Buils a channel programm to break a device's reservation.
50428c2ecf20Sopenharmony_ci * (unconditional reserve)
50438c2ecf20Sopenharmony_ci */
50448c2ecf20Sopenharmony_cistatic int
50458c2ecf20Sopenharmony_cidasd_eckd_steal_lock(struct dasd_device *device)
50468c2ecf20Sopenharmony_ci{
50478c2ecf20Sopenharmony_ci	struct dasd_ccw_req *cqr;
50488c2ecf20Sopenharmony_ci	int rc;
50498c2ecf20Sopenharmony_ci	struct ccw1 *ccw;
50508c2ecf20Sopenharmony_ci	int useglobal;
50518c2ecf20Sopenharmony_ci
50528c2ecf20Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
50538c2ecf20Sopenharmony_ci		return -EACCES;
50548c2ecf20Sopenharmony_ci
50558c2ecf20Sopenharmony_ci	useglobal = 0;
50568c2ecf20Sopenharmony_ci	cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1, 32, device, NULL);
50578c2ecf20Sopenharmony_ci	if (IS_ERR(cqr)) {
50588c2ecf20Sopenharmony_ci		mutex_lock(&dasd_reserve_mutex);
50598c2ecf20Sopenharmony_ci		useglobal = 1;
50608c2ecf20Sopenharmony_ci		cqr = &dasd_reserve_req->cqr;
50618c2ecf20Sopenharmony_ci		memset(cqr, 0, sizeof(*cqr));
50628c2ecf20Sopenharmony_ci		memset(&dasd_reserve_req->ccw, 0,
50638c2ecf20Sopenharmony_ci		       sizeof(dasd_reserve_req->ccw));
50648c2ecf20Sopenharmony_ci		cqr->cpaddr = &dasd_reserve_req->ccw;
50658c2ecf20Sopenharmony_ci		cqr->data = &dasd_reserve_req->data;
50668c2ecf20Sopenharmony_ci		cqr->magic = DASD_ECKD_MAGIC;
50678c2ecf20Sopenharmony_ci	}
50688c2ecf20Sopenharmony_ci	ccw = cqr->cpaddr;
50698c2ecf20Sopenharmony_ci	ccw->cmd_code = DASD_ECKD_CCW_SLCK;
50708c2ecf20Sopenharmony_ci	ccw->flags |= CCW_FLAG_SLI;
50718c2ecf20Sopenharmony_ci	ccw->count = 32;
50728c2ecf20Sopenharmony_ci	ccw->cda = (__u32)(addr_t) cqr->data;
50738c2ecf20Sopenharmony_ci	cqr->startdev = device;
50748c2ecf20Sopenharmony_ci	cqr->memdev = device;
50758c2ecf20Sopenharmony_ci	clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
50768c2ecf20Sopenharmony_ci	set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags);
50778c2ecf20Sopenharmony_ci	cqr->retries = 2;	/* set retry counter to enable basic ERP */
50788c2ecf20Sopenharmony_ci	cqr->expires = 2 * HZ;
50798c2ecf20Sopenharmony_ci	cqr->buildclk = get_tod_clock();
50808c2ecf20Sopenharmony_ci	cqr->status = DASD_CQR_FILLED;
50818c2ecf20Sopenharmony_ci
50828c2ecf20Sopenharmony_ci	rc = dasd_sleep_on_immediatly(cqr);
50838c2ecf20Sopenharmony_ci	if (!rc)
50848c2ecf20Sopenharmony_ci		set_bit(DASD_FLAG_IS_RESERVED, &device->flags);
50858c2ecf20Sopenharmony_ci
50868c2ecf20Sopenharmony_ci	if (useglobal)
50878c2ecf20Sopenharmony_ci		mutex_unlock(&dasd_reserve_mutex);
50888c2ecf20Sopenharmony_ci	else
50898c2ecf20Sopenharmony_ci		dasd_sfree_request(cqr, cqr->memdev);
50908c2ecf20Sopenharmony_ci	return rc;
50918c2ecf20Sopenharmony_ci}
50928c2ecf20Sopenharmony_ci
50938c2ecf20Sopenharmony_ci/*
50948c2ecf20Sopenharmony_ci * SNID - Sense Path Group ID
50958c2ecf20Sopenharmony_ci * This ioctl may be used in situations where I/O is stalled due to
50968c2ecf20Sopenharmony_ci * a reserve, so if the normal dasd_smalloc_request fails, we use the
50978c2ecf20Sopenharmony_ci * preallocated dasd_reserve_req.
50988c2ecf20Sopenharmony_ci */
50998c2ecf20Sopenharmony_cistatic int dasd_eckd_snid(struct dasd_device *device,
51008c2ecf20Sopenharmony_ci			  void __user *argp)
51018c2ecf20Sopenharmony_ci{
51028c2ecf20Sopenharmony_ci	struct dasd_ccw_req *cqr;
51038c2ecf20Sopenharmony_ci	int rc;
51048c2ecf20Sopenharmony_ci	struct ccw1 *ccw;
51058c2ecf20Sopenharmony_ci	int useglobal;
51068c2ecf20Sopenharmony_ci	struct dasd_snid_ioctl_data usrparm;
51078c2ecf20Sopenharmony_ci
51088c2ecf20Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
51098c2ecf20Sopenharmony_ci		return -EACCES;
51108c2ecf20Sopenharmony_ci
51118c2ecf20Sopenharmony_ci	if (copy_from_user(&usrparm, argp, sizeof(usrparm)))
51128c2ecf20Sopenharmony_ci		return -EFAULT;
51138c2ecf20Sopenharmony_ci
51148c2ecf20Sopenharmony_ci	useglobal = 0;
51158c2ecf20Sopenharmony_ci	cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1,
51168c2ecf20Sopenharmony_ci				   sizeof(struct dasd_snid_data), device,
51178c2ecf20Sopenharmony_ci				   NULL);
51188c2ecf20Sopenharmony_ci	if (IS_ERR(cqr)) {
51198c2ecf20Sopenharmony_ci		mutex_lock(&dasd_reserve_mutex);
51208c2ecf20Sopenharmony_ci		useglobal = 1;
51218c2ecf20Sopenharmony_ci		cqr = &dasd_reserve_req->cqr;
51228c2ecf20Sopenharmony_ci		memset(cqr, 0, sizeof(*cqr));
51238c2ecf20Sopenharmony_ci		memset(&dasd_reserve_req->ccw, 0,
51248c2ecf20Sopenharmony_ci		       sizeof(dasd_reserve_req->ccw));
51258c2ecf20Sopenharmony_ci		cqr->cpaddr = &dasd_reserve_req->ccw;
51268c2ecf20Sopenharmony_ci		cqr->data = &dasd_reserve_req->data;
51278c2ecf20Sopenharmony_ci		cqr->magic = DASD_ECKD_MAGIC;
51288c2ecf20Sopenharmony_ci	}
51298c2ecf20Sopenharmony_ci	ccw = cqr->cpaddr;
51308c2ecf20Sopenharmony_ci	ccw->cmd_code = DASD_ECKD_CCW_SNID;
51318c2ecf20Sopenharmony_ci	ccw->flags |= CCW_FLAG_SLI;
51328c2ecf20Sopenharmony_ci	ccw->count = 12;
51338c2ecf20Sopenharmony_ci	ccw->cda = (__u32)(addr_t) cqr->data;
51348c2ecf20Sopenharmony_ci	cqr->startdev = device;
51358c2ecf20Sopenharmony_ci	cqr->memdev = device;
51368c2ecf20Sopenharmony_ci	clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
51378c2ecf20Sopenharmony_ci	set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags);
51388c2ecf20Sopenharmony_ci	set_bit(DASD_CQR_ALLOW_SLOCK, &cqr->flags);
51398c2ecf20Sopenharmony_ci	cqr->retries = 5;
51408c2ecf20Sopenharmony_ci	cqr->expires = 10 * HZ;
51418c2ecf20Sopenharmony_ci	cqr->buildclk = get_tod_clock();
51428c2ecf20Sopenharmony_ci	cqr->status = DASD_CQR_FILLED;
51438c2ecf20Sopenharmony_ci	cqr->lpm = usrparm.path_mask;
51448c2ecf20Sopenharmony_ci
51458c2ecf20Sopenharmony_ci	rc = dasd_sleep_on_immediatly(cqr);
51468c2ecf20Sopenharmony_ci	/* verify that I/O processing didn't modify the path mask */
51478c2ecf20Sopenharmony_ci	if (!rc && usrparm.path_mask && (cqr->lpm != usrparm.path_mask))
51488c2ecf20Sopenharmony_ci		rc = -EIO;
51498c2ecf20Sopenharmony_ci	if (!rc) {
51508c2ecf20Sopenharmony_ci		usrparm.data = *((struct dasd_snid_data *)cqr->data);
51518c2ecf20Sopenharmony_ci		if (copy_to_user(argp, &usrparm, sizeof(usrparm)))
51528c2ecf20Sopenharmony_ci			rc = -EFAULT;
51538c2ecf20Sopenharmony_ci	}
51548c2ecf20Sopenharmony_ci
51558c2ecf20Sopenharmony_ci	if (useglobal)
51568c2ecf20Sopenharmony_ci		mutex_unlock(&dasd_reserve_mutex);
51578c2ecf20Sopenharmony_ci	else
51588c2ecf20Sopenharmony_ci		dasd_sfree_request(cqr, cqr->memdev);
51598c2ecf20Sopenharmony_ci	return rc;
51608c2ecf20Sopenharmony_ci}
51618c2ecf20Sopenharmony_ci
51628c2ecf20Sopenharmony_ci/*
51638c2ecf20Sopenharmony_ci * Read performance statistics
51648c2ecf20Sopenharmony_ci */
51658c2ecf20Sopenharmony_cistatic int
51668c2ecf20Sopenharmony_cidasd_eckd_performance(struct dasd_device *device, void __user *argp)
51678c2ecf20Sopenharmony_ci{
51688c2ecf20Sopenharmony_ci	struct dasd_psf_prssd_data *prssdp;
51698c2ecf20Sopenharmony_ci	struct dasd_rssd_perf_stats_t *stats;
51708c2ecf20Sopenharmony_ci	struct dasd_ccw_req *cqr;
51718c2ecf20Sopenharmony_ci	struct ccw1 *ccw;
51728c2ecf20Sopenharmony_ci	int rc;
51738c2ecf20Sopenharmony_ci
51748c2ecf20Sopenharmony_ci	cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */  + 1 /* RSSD */,
51758c2ecf20Sopenharmony_ci				   (sizeof(struct dasd_psf_prssd_data) +
51768c2ecf20Sopenharmony_ci				    sizeof(struct dasd_rssd_perf_stats_t)),
51778c2ecf20Sopenharmony_ci				   device, NULL);
51788c2ecf20Sopenharmony_ci	if (IS_ERR(cqr)) {
51798c2ecf20Sopenharmony_ci		DBF_DEV_EVENT(DBF_WARNING, device, "%s",
51808c2ecf20Sopenharmony_ci			    "Could not allocate initialization request");
51818c2ecf20Sopenharmony_ci		return PTR_ERR(cqr);
51828c2ecf20Sopenharmony_ci	}
51838c2ecf20Sopenharmony_ci	cqr->startdev = device;
51848c2ecf20Sopenharmony_ci	cqr->memdev = device;
51858c2ecf20Sopenharmony_ci	cqr->retries = 0;
51868c2ecf20Sopenharmony_ci	clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
51878c2ecf20Sopenharmony_ci	cqr->expires = 10 * HZ;
51888c2ecf20Sopenharmony_ci
51898c2ecf20Sopenharmony_ci	/* Prepare for Read Subsystem Data */
51908c2ecf20Sopenharmony_ci	prssdp = (struct dasd_psf_prssd_data *) cqr->data;
51918c2ecf20Sopenharmony_ci	memset(prssdp, 0, sizeof(struct dasd_psf_prssd_data));
51928c2ecf20Sopenharmony_ci	prssdp->order = PSF_ORDER_PRSSD;
51938c2ecf20Sopenharmony_ci	prssdp->suborder = 0x01;	/* Performance Statistics */
51948c2ecf20Sopenharmony_ci	prssdp->varies[1] = 0x01;	/* Perf Statistics for the Subsystem */
51958c2ecf20Sopenharmony_ci
51968c2ecf20Sopenharmony_ci	ccw = cqr->cpaddr;
51978c2ecf20Sopenharmony_ci	ccw->cmd_code = DASD_ECKD_CCW_PSF;
51988c2ecf20Sopenharmony_ci	ccw->count = sizeof(struct dasd_psf_prssd_data);
51998c2ecf20Sopenharmony_ci	ccw->flags |= CCW_FLAG_CC;
52008c2ecf20Sopenharmony_ci	ccw->cda = (__u32)(addr_t) prssdp;
52018c2ecf20Sopenharmony_ci
52028c2ecf20Sopenharmony_ci	/* Read Subsystem Data - Performance Statistics */
52038c2ecf20Sopenharmony_ci	stats = (struct dasd_rssd_perf_stats_t *) (prssdp + 1);
52048c2ecf20Sopenharmony_ci	memset(stats, 0, sizeof(struct dasd_rssd_perf_stats_t));
52058c2ecf20Sopenharmony_ci
52068c2ecf20Sopenharmony_ci	ccw++;
52078c2ecf20Sopenharmony_ci	ccw->cmd_code = DASD_ECKD_CCW_RSSD;
52088c2ecf20Sopenharmony_ci	ccw->count = sizeof(struct dasd_rssd_perf_stats_t);
52098c2ecf20Sopenharmony_ci	ccw->cda = (__u32)(addr_t) stats;
52108c2ecf20Sopenharmony_ci
52118c2ecf20Sopenharmony_ci	cqr->buildclk = get_tod_clock();
52128c2ecf20Sopenharmony_ci	cqr->status = DASD_CQR_FILLED;
52138c2ecf20Sopenharmony_ci	rc = dasd_sleep_on(cqr);
52148c2ecf20Sopenharmony_ci	if (rc == 0) {
52158c2ecf20Sopenharmony_ci		prssdp = (struct dasd_psf_prssd_data *) cqr->data;
52168c2ecf20Sopenharmony_ci		stats = (struct dasd_rssd_perf_stats_t *) (prssdp + 1);
52178c2ecf20Sopenharmony_ci		if (copy_to_user(argp, stats,
52188c2ecf20Sopenharmony_ci				 sizeof(struct dasd_rssd_perf_stats_t)))
52198c2ecf20Sopenharmony_ci			rc = -EFAULT;
52208c2ecf20Sopenharmony_ci	}
52218c2ecf20Sopenharmony_ci	dasd_sfree_request(cqr, cqr->memdev);
52228c2ecf20Sopenharmony_ci	return rc;
52238c2ecf20Sopenharmony_ci}
52248c2ecf20Sopenharmony_ci
52258c2ecf20Sopenharmony_ci/*
52268c2ecf20Sopenharmony_ci * Get attributes (cache operations)
52278c2ecf20Sopenharmony_ci * Returnes the cache attributes used in Define Extend (DE).
52288c2ecf20Sopenharmony_ci */
52298c2ecf20Sopenharmony_cistatic int
52308c2ecf20Sopenharmony_cidasd_eckd_get_attrib(struct dasd_device *device, void __user *argp)
52318c2ecf20Sopenharmony_ci{
52328c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
52338c2ecf20Sopenharmony_ci	struct attrib_data_t attrib = private->attrib;
52348c2ecf20Sopenharmony_ci	int rc;
52358c2ecf20Sopenharmony_ci
52368c2ecf20Sopenharmony_ci        if (!capable(CAP_SYS_ADMIN))
52378c2ecf20Sopenharmony_ci                return -EACCES;
52388c2ecf20Sopenharmony_ci	if (!argp)
52398c2ecf20Sopenharmony_ci                return -EINVAL;
52408c2ecf20Sopenharmony_ci
52418c2ecf20Sopenharmony_ci	rc = 0;
52428c2ecf20Sopenharmony_ci	if (copy_to_user(argp, (long *) &attrib,
52438c2ecf20Sopenharmony_ci			 sizeof(struct attrib_data_t)))
52448c2ecf20Sopenharmony_ci		rc = -EFAULT;
52458c2ecf20Sopenharmony_ci
52468c2ecf20Sopenharmony_ci	return rc;
52478c2ecf20Sopenharmony_ci}
52488c2ecf20Sopenharmony_ci
52498c2ecf20Sopenharmony_ci/*
52508c2ecf20Sopenharmony_ci * Set attributes (cache operations)
52518c2ecf20Sopenharmony_ci * Stores the attributes for cache operation to be used in Define Extend (DE).
52528c2ecf20Sopenharmony_ci */
52538c2ecf20Sopenharmony_cistatic int
52548c2ecf20Sopenharmony_cidasd_eckd_set_attrib(struct dasd_device *device, void __user *argp)
52558c2ecf20Sopenharmony_ci{
52568c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
52578c2ecf20Sopenharmony_ci	struct attrib_data_t attrib;
52588c2ecf20Sopenharmony_ci
52598c2ecf20Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
52608c2ecf20Sopenharmony_ci		return -EACCES;
52618c2ecf20Sopenharmony_ci	if (!argp)
52628c2ecf20Sopenharmony_ci		return -EINVAL;
52638c2ecf20Sopenharmony_ci
52648c2ecf20Sopenharmony_ci	if (copy_from_user(&attrib, argp, sizeof(struct attrib_data_t)))
52658c2ecf20Sopenharmony_ci		return -EFAULT;
52668c2ecf20Sopenharmony_ci	private->attrib = attrib;
52678c2ecf20Sopenharmony_ci
52688c2ecf20Sopenharmony_ci	dev_info(&device->cdev->dev,
52698c2ecf20Sopenharmony_ci		 "The DASD cache mode was set to %x (%i cylinder prestage)\n",
52708c2ecf20Sopenharmony_ci		 private->attrib.operation, private->attrib.nr_cyl);
52718c2ecf20Sopenharmony_ci	return 0;
52728c2ecf20Sopenharmony_ci}
52738c2ecf20Sopenharmony_ci
52748c2ecf20Sopenharmony_ci/*
52758c2ecf20Sopenharmony_ci * Issue syscall I/O to EMC Symmetrix array.
52768c2ecf20Sopenharmony_ci * CCWs are PSF and RSSD
52778c2ecf20Sopenharmony_ci */
52788c2ecf20Sopenharmony_cistatic int dasd_symm_io(struct dasd_device *device, void __user *argp)
52798c2ecf20Sopenharmony_ci{
52808c2ecf20Sopenharmony_ci	struct dasd_symmio_parms usrparm;
52818c2ecf20Sopenharmony_ci	char *psf_data, *rssd_result;
52828c2ecf20Sopenharmony_ci	struct dasd_ccw_req *cqr;
52838c2ecf20Sopenharmony_ci	struct ccw1 *ccw;
52848c2ecf20Sopenharmony_ci	char psf0, psf1;
52858c2ecf20Sopenharmony_ci	int rc;
52868c2ecf20Sopenharmony_ci
52878c2ecf20Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RAWIO))
52888c2ecf20Sopenharmony_ci		return -EACCES;
52898c2ecf20Sopenharmony_ci	psf0 = psf1 = 0;
52908c2ecf20Sopenharmony_ci
52918c2ecf20Sopenharmony_ci	/* Copy parms from caller */
52928c2ecf20Sopenharmony_ci	rc = -EFAULT;
52938c2ecf20Sopenharmony_ci	if (copy_from_user(&usrparm, argp, sizeof(usrparm)))
52948c2ecf20Sopenharmony_ci		goto out;
52958c2ecf20Sopenharmony_ci	if (is_compat_task()) {
52968c2ecf20Sopenharmony_ci		/* Make sure pointers are sane even on 31 bit. */
52978c2ecf20Sopenharmony_ci		rc = -EINVAL;
52988c2ecf20Sopenharmony_ci		if ((usrparm.psf_data >> 32) != 0)
52998c2ecf20Sopenharmony_ci			goto out;
53008c2ecf20Sopenharmony_ci		if ((usrparm.rssd_result >> 32) != 0)
53018c2ecf20Sopenharmony_ci			goto out;
53028c2ecf20Sopenharmony_ci		usrparm.psf_data &= 0x7fffffffULL;
53038c2ecf20Sopenharmony_ci		usrparm.rssd_result &= 0x7fffffffULL;
53048c2ecf20Sopenharmony_ci	}
53058c2ecf20Sopenharmony_ci	/* at least 2 bytes are accessed and should be allocated */
53068c2ecf20Sopenharmony_ci	if (usrparm.psf_data_len < 2) {
53078c2ecf20Sopenharmony_ci		DBF_DEV_EVENT(DBF_WARNING, device,
53088c2ecf20Sopenharmony_ci			      "Symmetrix ioctl invalid data length %d",
53098c2ecf20Sopenharmony_ci			      usrparm.psf_data_len);
53108c2ecf20Sopenharmony_ci		rc = -EINVAL;
53118c2ecf20Sopenharmony_ci		goto out;
53128c2ecf20Sopenharmony_ci	}
53138c2ecf20Sopenharmony_ci	/* alloc I/O data area */
53148c2ecf20Sopenharmony_ci	psf_data = kzalloc(usrparm.psf_data_len, GFP_KERNEL | GFP_DMA);
53158c2ecf20Sopenharmony_ci	rssd_result = kzalloc(usrparm.rssd_result_len, GFP_KERNEL | GFP_DMA);
53168c2ecf20Sopenharmony_ci	if (!psf_data || !rssd_result) {
53178c2ecf20Sopenharmony_ci		rc = -ENOMEM;
53188c2ecf20Sopenharmony_ci		goto out_free;
53198c2ecf20Sopenharmony_ci	}
53208c2ecf20Sopenharmony_ci
53218c2ecf20Sopenharmony_ci	/* get syscall header from user space */
53228c2ecf20Sopenharmony_ci	rc = -EFAULT;
53238c2ecf20Sopenharmony_ci	if (copy_from_user(psf_data,
53248c2ecf20Sopenharmony_ci			   (void __user *)(unsigned long) usrparm.psf_data,
53258c2ecf20Sopenharmony_ci			   usrparm.psf_data_len))
53268c2ecf20Sopenharmony_ci		goto out_free;
53278c2ecf20Sopenharmony_ci	psf0 = psf_data[0];
53288c2ecf20Sopenharmony_ci	psf1 = psf_data[1];
53298c2ecf20Sopenharmony_ci
53308c2ecf20Sopenharmony_ci	/* setup CCWs for PSF + RSSD */
53318c2ecf20Sopenharmony_ci	cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 2, 0, device, NULL);
53328c2ecf20Sopenharmony_ci	if (IS_ERR(cqr)) {
53338c2ecf20Sopenharmony_ci		DBF_DEV_EVENT(DBF_WARNING, device, "%s",
53348c2ecf20Sopenharmony_ci			"Could not allocate initialization request");
53358c2ecf20Sopenharmony_ci		rc = PTR_ERR(cqr);
53368c2ecf20Sopenharmony_ci		goto out_free;
53378c2ecf20Sopenharmony_ci	}
53388c2ecf20Sopenharmony_ci
53398c2ecf20Sopenharmony_ci	cqr->startdev = device;
53408c2ecf20Sopenharmony_ci	cqr->memdev = device;
53418c2ecf20Sopenharmony_ci	cqr->retries = 3;
53428c2ecf20Sopenharmony_ci	cqr->expires = 10 * HZ;
53438c2ecf20Sopenharmony_ci	cqr->buildclk = get_tod_clock();
53448c2ecf20Sopenharmony_ci	cqr->status = DASD_CQR_FILLED;
53458c2ecf20Sopenharmony_ci
53468c2ecf20Sopenharmony_ci	/* Build the ccws */
53478c2ecf20Sopenharmony_ci	ccw = cqr->cpaddr;
53488c2ecf20Sopenharmony_ci
53498c2ecf20Sopenharmony_ci	/* PSF ccw */
53508c2ecf20Sopenharmony_ci	ccw->cmd_code = DASD_ECKD_CCW_PSF;
53518c2ecf20Sopenharmony_ci	ccw->count = usrparm.psf_data_len;
53528c2ecf20Sopenharmony_ci	ccw->flags |= CCW_FLAG_CC;
53538c2ecf20Sopenharmony_ci	ccw->cda = (__u32)(addr_t) psf_data;
53548c2ecf20Sopenharmony_ci
53558c2ecf20Sopenharmony_ci	ccw++;
53568c2ecf20Sopenharmony_ci
53578c2ecf20Sopenharmony_ci	/* RSSD ccw  */
53588c2ecf20Sopenharmony_ci	ccw->cmd_code = DASD_ECKD_CCW_RSSD;
53598c2ecf20Sopenharmony_ci	ccw->count = usrparm.rssd_result_len;
53608c2ecf20Sopenharmony_ci	ccw->flags = CCW_FLAG_SLI ;
53618c2ecf20Sopenharmony_ci	ccw->cda = (__u32)(addr_t) rssd_result;
53628c2ecf20Sopenharmony_ci
53638c2ecf20Sopenharmony_ci	rc = dasd_sleep_on(cqr);
53648c2ecf20Sopenharmony_ci	if (rc)
53658c2ecf20Sopenharmony_ci		goto out_sfree;
53668c2ecf20Sopenharmony_ci
53678c2ecf20Sopenharmony_ci	rc = -EFAULT;
53688c2ecf20Sopenharmony_ci	if (copy_to_user((void __user *)(unsigned long) usrparm.rssd_result,
53698c2ecf20Sopenharmony_ci			   rssd_result, usrparm.rssd_result_len))
53708c2ecf20Sopenharmony_ci		goto out_sfree;
53718c2ecf20Sopenharmony_ci	rc = 0;
53728c2ecf20Sopenharmony_ci
53738c2ecf20Sopenharmony_ciout_sfree:
53748c2ecf20Sopenharmony_ci	dasd_sfree_request(cqr, cqr->memdev);
53758c2ecf20Sopenharmony_ciout_free:
53768c2ecf20Sopenharmony_ci	kfree(rssd_result);
53778c2ecf20Sopenharmony_ci	kfree(psf_data);
53788c2ecf20Sopenharmony_ciout:
53798c2ecf20Sopenharmony_ci	DBF_DEV_EVENT(DBF_WARNING, device,
53808c2ecf20Sopenharmony_ci		      "Symmetrix ioctl (0x%02x 0x%02x): rc=%d",
53818c2ecf20Sopenharmony_ci		      (int) psf0, (int) psf1, rc);
53828c2ecf20Sopenharmony_ci	return rc;
53838c2ecf20Sopenharmony_ci}
53848c2ecf20Sopenharmony_ci
53858c2ecf20Sopenharmony_cistatic int
53868c2ecf20Sopenharmony_cidasd_eckd_ioctl(struct dasd_block *block, unsigned int cmd, void __user *argp)
53878c2ecf20Sopenharmony_ci{
53888c2ecf20Sopenharmony_ci	struct dasd_device *device = block->base;
53898c2ecf20Sopenharmony_ci
53908c2ecf20Sopenharmony_ci	switch (cmd) {
53918c2ecf20Sopenharmony_ci	case BIODASDGATTR:
53928c2ecf20Sopenharmony_ci		return dasd_eckd_get_attrib(device, argp);
53938c2ecf20Sopenharmony_ci	case BIODASDSATTR:
53948c2ecf20Sopenharmony_ci		return dasd_eckd_set_attrib(device, argp);
53958c2ecf20Sopenharmony_ci	case BIODASDPSRD:
53968c2ecf20Sopenharmony_ci		return dasd_eckd_performance(device, argp);
53978c2ecf20Sopenharmony_ci	case BIODASDRLSE:
53988c2ecf20Sopenharmony_ci		return dasd_eckd_release(device);
53998c2ecf20Sopenharmony_ci	case BIODASDRSRV:
54008c2ecf20Sopenharmony_ci		return dasd_eckd_reserve(device);
54018c2ecf20Sopenharmony_ci	case BIODASDSLCK:
54028c2ecf20Sopenharmony_ci		return dasd_eckd_steal_lock(device);
54038c2ecf20Sopenharmony_ci	case BIODASDSNID:
54048c2ecf20Sopenharmony_ci		return dasd_eckd_snid(device, argp);
54058c2ecf20Sopenharmony_ci	case BIODASDSYMMIO:
54068c2ecf20Sopenharmony_ci		return dasd_symm_io(device, argp);
54078c2ecf20Sopenharmony_ci	default:
54088c2ecf20Sopenharmony_ci		return -ENOTTY;
54098c2ecf20Sopenharmony_ci	}
54108c2ecf20Sopenharmony_ci}
54118c2ecf20Sopenharmony_ci
54128c2ecf20Sopenharmony_ci/*
54138c2ecf20Sopenharmony_ci * Dump the range of CCWs into 'page' buffer
54148c2ecf20Sopenharmony_ci * and return number of printed chars.
54158c2ecf20Sopenharmony_ci */
54168c2ecf20Sopenharmony_cistatic int
54178c2ecf20Sopenharmony_cidasd_eckd_dump_ccw_range(struct ccw1 *from, struct ccw1 *to, char *page)
54188c2ecf20Sopenharmony_ci{
54198c2ecf20Sopenharmony_ci	int len, count;
54208c2ecf20Sopenharmony_ci	char *datap;
54218c2ecf20Sopenharmony_ci
54228c2ecf20Sopenharmony_ci	len = 0;
54238c2ecf20Sopenharmony_ci	while (from <= to) {
54248c2ecf20Sopenharmony_ci		len += sprintf(page + len, PRINTK_HEADER
54258c2ecf20Sopenharmony_ci			       " CCW %p: %08X %08X DAT:",
54268c2ecf20Sopenharmony_ci			       from, ((int *) from)[0], ((int *) from)[1]);
54278c2ecf20Sopenharmony_ci
54288c2ecf20Sopenharmony_ci		/* get pointer to data (consider IDALs) */
54298c2ecf20Sopenharmony_ci		if (from->flags & CCW_FLAG_IDA)
54308c2ecf20Sopenharmony_ci			datap = (char *) *((addr_t *) (addr_t) from->cda);
54318c2ecf20Sopenharmony_ci		else
54328c2ecf20Sopenharmony_ci			datap = (char *) ((addr_t) from->cda);
54338c2ecf20Sopenharmony_ci
54348c2ecf20Sopenharmony_ci		/* dump data (max 32 bytes) */
54358c2ecf20Sopenharmony_ci		for (count = 0; count < from->count && count < 32; count++) {
54368c2ecf20Sopenharmony_ci			if (count % 8 == 0) len += sprintf(page + len, " ");
54378c2ecf20Sopenharmony_ci			if (count % 4 == 0) len += sprintf(page + len, " ");
54388c2ecf20Sopenharmony_ci			len += sprintf(page + len, "%02x", datap[count]);
54398c2ecf20Sopenharmony_ci		}
54408c2ecf20Sopenharmony_ci		len += sprintf(page + len, "\n");
54418c2ecf20Sopenharmony_ci		from++;
54428c2ecf20Sopenharmony_ci	}
54438c2ecf20Sopenharmony_ci	return len;
54448c2ecf20Sopenharmony_ci}
54458c2ecf20Sopenharmony_ci
54468c2ecf20Sopenharmony_cistatic void
54478c2ecf20Sopenharmony_cidasd_eckd_dump_sense_dbf(struct dasd_device *device, struct irb *irb,
54488c2ecf20Sopenharmony_ci			 char *reason)
54498c2ecf20Sopenharmony_ci{
54508c2ecf20Sopenharmony_ci	u64 *sense;
54518c2ecf20Sopenharmony_ci	u64 *stat;
54528c2ecf20Sopenharmony_ci
54538c2ecf20Sopenharmony_ci	sense = (u64 *) dasd_get_sense(irb);
54548c2ecf20Sopenharmony_ci	stat = (u64 *) &irb->scsw;
54558c2ecf20Sopenharmony_ci	if (sense) {
54568c2ecf20Sopenharmony_ci		DBF_DEV_EVENT(DBF_EMERG, device, "%s: %016llx %08x : "
54578c2ecf20Sopenharmony_ci			      "%016llx %016llx %016llx %016llx",
54588c2ecf20Sopenharmony_ci			      reason, *stat, *((u32 *) (stat + 1)),
54598c2ecf20Sopenharmony_ci			      sense[0], sense[1], sense[2], sense[3]);
54608c2ecf20Sopenharmony_ci	} else {
54618c2ecf20Sopenharmony_ci		DBF_DEV_EVENT(DBF_EMERG, device, "%s: %016llx %08x : %s",
54628c2ecf20Sopenharmony_ci			      reason, *stat, *((u32 *) (stat + 1)),
54638c2ecf20Sopenharmony_ci			      "NO VALID SENSE");
54648c2ecf20Sopenharmony_ci	}
54658c2ecf20Sopenharmony_ci}
54668c2ecf20Sopenharmony_ci
54678c2ecf20Sopenharmony_ci/*
54688c2ecf20Sopenharmony_ci * Print sense data and related channel program.
54698c2ecf20Sopenharmony_ci * Parts are printed because printk buffer is only 1024 bytes.
54708c2ecf20Sopenharmony_ci */
54718c2ecf20Sopenharmony_cistatic void dasd_eckd_dump_sense_ccw(struct dasd_device *device,
54728c2ecf20Sopenharmony_ci				 struct dasd_ccw_req *req, struct irb *irb)
54738c2ecf20Sopenharmony_ci{
54748c2ecf20Sopenharmony_ci	char *page;
54758c2ecf20Sopenharmony_ci	struct ccw1 *first, *last, *fail, *from, *to;
54768c2ecf20Sopenharmony_ci	int len, sl, sct;
54778c2ecf20Sopenharmony_ci
54788c2ecf20Sopenharmony_ci	page = (char *) get_zeroed_page(GFP_ATOMIC);
54798c2ecf20Sopenharmony_ci	if (page == NULL) {
54808c2ecf20Sopenharmony_ci		DBF_DEV_EVENT(DBF_WARNING, device, "%s",
54818c2ecf20Sopenharmony_ci			      "No memory to dump sense data\n");
54828c2ecf20Sopenharmony_ci		return;
54838c2ecf20Sopenharmony_ci	}
54848c2ecf20Sopenharmony_ci	/* dump the sense data */
54858c2ecf20Sopenharmony_ci	len = sprintf(page, PRINTK_HEADER
54868c2ecf20Sopenharmony_ci		      " I/O status report for device %s:\n",
54878c2ecf20Sopenharmony_ci		      dev_name(&device->cdev->dev));
54888c2ecf20Sopenharmony_ci	len += sprintf(page + len, PRINTK_HEADER
54898c2ecf20Sopenharmony_ci		       " in req: %p CC:%02X FC:%02X AC:%02X SC:%02X DS:%02X "
54908c2ecf20Sopenharmony_ci		       "CS:%02X RC:%d\n",
54918c2ecf20Sopenharmony_ci		       req, scsw_cc(&irb->scsw), scsw_fctl(&irb->scsw),
54928c2ecf20Sopenharmony_ci		       scsw_actl(&irb->scsw), scsw_stctl(&irb->scsw),
54938c2ecf20Sopenharmony_ci		       scsw_dstat(&irb->scsw), scsw_cstat(&irb->scsw),
54948c2ecf20Sopenharmony_ci		       req ? req->intrc : 0);
54958c2ecf20Sopenharmony_ci	len += sprintf(page + len, PRINTK_HEADER
54968c2ecf20Sopenharmony_ci		       " device %s: Failing CCW: %p\n",
54978c2ecf20Sopenharmony_ci		       dev_name(&device->cdev->dev),
54988c2ecf20Sopenharmony_ci		       (void *) (addr_t) irb->scsw.cmd.cpa);
54998c2ecf20Sopenharmony_ci	if (irb->esw.esw0.erw.cons) {
55008c2ecf20Sopenharmony_ci		for (sl = 0; sl < 4; sl++) {
55018c2ecf20Sopenharmony_ci			len += sprintf(page + len, PRINTK_HEADER
55028c2ecf20Sopenharmony_ci				       " Sense(hex) %2d-%2d:",
55038c2ecf20Sopenharmony_ci				       (8 * sl), ((8 * sl) + 7));
55048c2ecf20Sopenharmony_ci
55058c2ecf20Sopenharmony_ci			for (sct = 0; sct < 8; sct++) {
55068c2ecf20Sopenharmony_ci				len += sprintf(page + len, " %02x",
55078c2ecf20Sopenharmony_ci					       irb->ecw[8 * sl + sct]);
55088c2ecf20Sopenharmony_ci			}
55098c2ecf20Sopenharmony_ci			len += sprintf(page + len, "\n");
55108c2ecf20Sopenharmony_ci		}
55118c2ecf20Sopenharmony_ci
55128c2ecf20Sopenharmony_ci		if (irb->ecw[27] & DASD_SENSE_BIT_0) {
55138c2ecf20Sopenharmony_ci			/* 24 Byte Sense Data */
55148c2ecf20Sopenharmony_ci			sprintf(page + len, PRINTK_HEADER
55158c2ecf20Sopenharmony_ci				" 24 Byte: %x MSG %x, "
55168c2ecf20Sopenharmony_ci				"%s MSGb to SYSOP\n",
55178c2ecf20Sopenharmony_ci				irb->ecw[7] >> 4, irb->ecw[7] & 0x0f,
55188c2ecf20Sopenharmony_ci				irb->ecw[1] & 0x10 ? "" : "no");
55198c2ecf20Sopenharmony_ci		} else {
55208c2ecf20Sopenharmony_ci			/* 32 Byte Sense Data */
55218c2ecf20Sopenharmony_ci			sprintf(page + len, PRINTK_HEADER
55228c2ecf20Sopenharmony_ci				" 32 Byte: Format: %x "
55238c2ecf20Sopenharmony_ci				"Exception class %x\n",
55248c2ecf20Sopenharmony_ci				irb->ecw[6] & 0x0f, irb->ecw[22] >> 4);
55258c2ecf20Sopenharmony_ci		}
55268c2ecf20Sopenharmony_ci	} else {
55278c2ecf20Sopenharmony_ci		sprintf(page + len, PRINTK_HEADER
55288c2ecf20Sopenharmony_ci			" SORRY - NO VALID SENSE AVAILABLE\n");
55298c2ecf20Sopenharmony_ci	}
55308c2ecf20Sopenharmony_ci	printk(KERN_ERR "%s", page);
55318c2ecf20Sopenharmony_ci
55328c2ecf20Sopenharmony_ci	if (req) {
55338c2ecf20Sopenharmony_ci		/* req == NULL for unsolicited interrupts */
55348c2ecf20Sopenharmony_ci		/* dump the Channel Program (max 140 Bytes per line) */
55358c2ecf20Sopenharmony_ci		/* Count CCW and print first CCWs (maximum 1024 % 140 = 7) */
55368c2ecf20Sopenharmony_ci		first = req->cpaddr;
55378c2ecf20Sopenharmony_ci		for (last = first; last->flags & (CCW_FLAG_CC | CCW_FLAG_DC); last++);
55388c2ecf20Sopenharmony_ci		to = min(first + 6, last);
55398c2ecf20Sopenharmony_ci		len = sprintf(page, PRINTK_HEADER
55408c2ecf20Sopenharmony_ci			      " Related CP in req: %p\n", req);
55418c2ecf20Sopenharmony_ci		dasd_eckd_dump_ccw_range(first, to, page + len);
55428c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s", page);
55438c2ecf20Sopenharmony_ci
55448c2ecf20Sopenharmony_ci		/* print failing CCW area (maximum 4) */
55458c2ecf20Sopenharmony_ci		/* scsw->cda is either valid or zero  */
55468c2ecf20Sopenharmony_ci		len = 0;
55478c2ecf20Sopenharmony_ci		from = ++to;
55488c2ecf20Sopenharmony_ci		fail = (struct ccw1 *)(addr_t)
55498c2ecf20Sopenharmony_ci				irb->scsw.cmd.cpa; /* failing CCW */
55508c2ecf20Sopenharmony_ci		if (from <  fail - 2) {
55518c2ecf20Sopenharmony_ci			from = fail - 2;     /* there is a gap - print header */
55528c2ecf20Sopenharmony_ci			len += sprintf(page, PRINTK_HEADER "......\n");
55538c2ecf20Sopenharmony_ci		}
55548c2ecf20Sopenharmony_ci		to = min(fail + 1, last);
55558c2ecf20Sopenharmony_ci		len += dasd_eckd_dump_ccw_range(from, to, page + len);
55568c2ecf20Sopenharmony_ci
55578c2ecf20Sopenharmony_ci		/* print last CCWs (maximum 2) */
55588c2ecf20Sopenharmony_ci		from = max(from, ++to);
55598c2ecf20Sopenharmony_ci		if (from < last - 1) {
55608c2ecf20Sopenharmony_ci			from = last - 1;     /* there is a gap - print header */
55618c2ecf20Sopenharmony_ci			len += sprintf(page + len, PRINTK_HEADER "......\n");
55628c2ecf20Sopenharmony_ci		}
55638c2ecf20Sopenharmony_ci		len += dasd_eckd_dump_ccw_range(from, last, page + len);
55648c2ecf20Sopenharmony_ci		if (len > 0)
55658c2ecf20Sopenharmony_ci			printk(KERN_ERR "%s", page);
55668c2ecf20Sopenharmony_ci	}
55678c2ecf20Sopenharmony_ci	free_page((unsigned long) page);
55688c2ecf20Sopenharmony_ci}
55698c2ecf20Sopenharmony_ci
55708c2ecf20Sopenharmony_ci
55718c2ecf20Sopenharmony_ci/*
55728c2ecf20Sopenharmony_ci * Print sense data from a tcw.
55738c2ecf20Sopenharmony_ci */
55748c2ecf20Sopenharmony_cistatic void dasd_eckd_dump_sense_tcw(struct dasd_device *device,
55758c2ecf20Sopenharmony_ci				 struct dasd_ccw_req *req, struct irb *irb)
55768c2ecf20Sopenharmony_ci{
55778c2ecf20Sopenharmony_ci	char *page;
55788c2ecf20Sopenharmony_ci	int len, sl, sct, residual;
55798c2ecf20Sopenharmony_ci	struct tsb *tsb;
55808c2ecf20Sopenharmony_ci	u8 *sense, *rcq;
55818c2ecf20Sopenharmony_ci
55828c2ecf20Sopenharmony_ci	page = (char *) get_zeroed_page(GFP_ATOMIC);
55838c2ecf20Sopenharmony_ci	if (page == NULL) {
55848c2ecf20Sopenharmony_ci		DBF_DEV_EVENT(DBF_WARNING, device, " %s",
55858c2ecf20Sopenharmony_ci			    "No memory to dump sense data");
55868c2ecf20Sopenharmony_ci		return;
55878c2ecf20Sopenharmony_ci	}
55888c2ecf20Sopenharmony_ci	/* dump the sense data */
55898c2ecf20Sopenharmony_ci	len = sprintf(page, PRINTK_HEADER
55908c2ecf20Sopenharmony_ci		      " I/O status report for device %s:\n",
55918c2ecf20Sopenharmony_ci		      dev_name(&device->cdev->dev));
55928c2ecf20Sopenharmony_ci	len += sprintf(page + len, PRINTK_HEADER
55938c2ecf20Sopenharmony_ci		       " in req: %p CC:%02X FC:%02X AC:%02X SC:%02X DS:%02X "
55948c2ecf20Sopenharmony_ci		       "CS:%02X fcxs:%02X schxs:%02X RC:%d\n",
55958c2ecf20Sopenharmony_ci		       req, scsw_cc(&irb->scsw), scsw_fctl(&irb->scsw),
55968c2ecf20Sopenharmony_ci		       scsw_actl(&irb->scsw), scsw_stctl(&irb->scsw),
55978c2ecf20Sopenharmony_ci		       scsw_dstat(&irb->scsw), scsw_cstat(&irb->scsw),
55988c2ecf20Sopenharmony_ci		       irb->scsw.tm.fcxs,
55998c2ecf20Sopenharmony_ci		       (irb->scsw.tm.ifob << 7) | irb->scsw.tm.sesq,
56008c2ecf20Sopenharmony_ci		       req ? req->intrc : 0);
56018c2ecf20Sopenharmony_ci	len += sprintf(page + len, PRINTK_HEADER
56028c2ecf20Sopenharmony_ci		       " device %s: Failing TCW: %p\n",
56038c2ecf20Sopenharmony_ci		       dev_name(&device->cdev->dev),
56048c2ecf20Sopenharmony_ci		       (void *) (addr_t) irb->scsw.tm.tcw);
56058c2ecf20Sopenharmony_ci
56068c2ecf20Sopenharmony_ci	tsb = NULL;
56078c2ecf20Sopenharmony_ci	sense = NULL;
56088c2ecf20Sopenharmony_ci	if (irb->scsw.tm.tcw && (irb->scsw.tm.fcxs & 0x01))
56098c2ecf20Sopenharmony_ci		tsb = tcw_get_tsb(
56108c2ecf20Sopenharmony_ci			(struct tcw *)(unsigned long)irb->scsw.tm.tcw);
56118c2ecf20Sopenharmony_ci
56128c2ecf20Sopenharmony_ci	if (tsb) {
56138c2ecf20Sopenharmony_ci		len += sprintf(page + len, PRINTK_HEADER
56148c2ecf20Sopenharmony_ci			       " tsb->length %d\n", tsb->length);
56158c2ecf20Sopenharmony_ci		len += sprintf(page + len, PRINTK_HEADER
56168c2ecf20Sopenharmony_ci			       " tsb->flags %x\n", tsb->flags);
56178c2ecf20Sopenharmony_ci		len += sprintf(page + len, PRINTK_HEADER
56188c2ecf20Sopenharmony_ci			       " tsb->dcw_offset %d\n", tsb->dcw_offset);
56198c2ecf20Sopenharmony_ci		len += sprintf(page + len, PRINTK_HEADER
56208c2ecf20Sopenharmony_ci			       " tsb->count %d\n", tsb->count);
56218c2ecf20Sopenharmony_ci		residual = tsb->count - 28;
56228c2ecf20Sopenharmony_ci		len += sprintf(page + len, PRINTK_HEADER
56238c2ecf20Sopenharmony_ci			       " residual %d\n", residual);
56248c2ecf20Sopenharmony_ci
56258c2ecf20Sopenharmony_ci		switch (tsb->flags & 0x07) {
56268c2ecf20Sopenharmony_ci		case 1:	/* tsa_iostat */
56278c2ecf20Sopenharmony_ci			len += sprintf(page + len, PRINTK_HEADER
56288c2ecf20Sopenharmony_ci			       " tsb->tsa.iostat.dev_time %d\n",
56298c2ecf20Sopenharmony_ci				       tsb->tsa.iostat.dev_time);
56308c2ecf20Sopenharmony_ci			len += sprintf(page + len, PRINTK_HEADER
56318c2ecf20Sopenharmony_ci			       " tsb->tsa.iostat.def_time %d\n",
56328c2ecf20Sopenharmony_ci				       tsb->tsa.iostat.def_time);
56338c2ecf20Sopenharmony_ci			len += sprintf(page + len, PRINTK_HEADER
56348c2ecf20Sopenharmony_ci			       " tsb->tsa.iostat.queue_time %d\n",
56358c2ecf20Sopenharmony_ci				       tsb->tsa.iostat.queue_time);
56368c2ecf20Sopenharmony_ci			len += sprintf(page + len, PRINTK_HEADER
56378c2ecf20Sopenharmony_ci			       " tsb->tsa.iostat.dev_busy_time %d\n",
56388c2ecf20Sopenharmony_ci				       tsb->tsa.iostat.dev_busy_time);
56398c2ecf20Sopenharmony_ci			len += sprintf(page + len, PRINTK_HEADER
56408c2ecf20Sopenharmony_ci			       " tsb->tsa.iostat.dev_act_time %d\n",
56418c2ecf20Sopenharmony_ci				       tsb->tsa.iostat.dev_act_time);
56428c2ecf20Sopenharmony_ci			sense = tsb->tsa.iostat.sense;
56438c2ecf20Sopenharmony_ci			break;
56448c2ecf20Sopenharmony_ci		case 2: /* ts_ddpc */
56458c2ecf20Sopenharmony_ci			len += sprintf(page + len, PRINTK_HEADER
56468c2ecf20Sopenharmony_ci			       " tsb->tsa.ddpc.rc %d\n", tsb->tsa.ddpc.rc);
56478c2ecf20Sopenharmony_ci			for (sl = 0; sl < 2; sl++) {
56488c2ecf20Sopenharmony_ci				len += sprintf(page + len, PRINTK_HEADER
56498c2ecf20Sopenharmony_ci					       " tsb->tsa.ddpc.rcq %2d-%2d: ",
56508c2ecf20Sopenharmony_ci					       (8 * sl), ((8 * sl) + 7));
56518c2ecf20Sopenharmony_ci				rcq = tsb->tsa.ddpc.rcq;
56528c2ecf20Sopenharmony_ci				for (sct = 0; sct < 8; sct++) {
56538c2ecf20Sopenharmony_ci					len += sprintf(page + len, " %02x",
56548c2ecf20Sopenharmony_ci						       rcq[8 * sl + sct]);
56558c2ecf20Sopenharmony_ci				}
56568c2ecf20Sopenharmony_ci				len += sprintf(page + len, "\n");
56578c2ecf20Sopenharmony_ci			}
56588c2ecf20Sopenharmony_ci			sense = tsb->tsa.ddpc.sense;
56598c2ecf20Sopenharmony_ci			break;
56608c2ecf20Sopenharmony_ci		case 3: /* tsa_intrg */
56618c2ecf20Sopenharmony_ci			len += sprintf(page + len, PRINTK_HEADER
56628c2ecf20Sopenharmony_ci				      " tsb->tsa.intrg.: not supported yet\n");
56638c2ecf20Sopenharmony_ci			break;
56648c2ecf20Sopenharmony_ci		}
56658c2ecf20Sopenharmony_ci
56668c2ecf20Sopenharmony_ci		if (sense) {
56678c2ecf20Sopenharmony_ci			for (sl = 0; sl < 4; sl++) {
56688c2ecf20Sopenharmony_ci				len += sprintf(page + len, PRINTK_HEADER
56698c2ecf20Sopenharmony_ci					       " Sense(hex) %2d-%2d:",
56708c2ecf20Sopenharmony_ci					       (8 * sl), ((8 * sl) + 7));
56718c2ecf20Sopenharmony_ci				for (sct = 0; sct < 8; sct++) {
56728c2ecf20Sopenharmony_ci					len += sprintf(page + len, " %02x",
56738c2ecf20Sopenharmony_ci						       sense[8 * sl + sct]);
56748c2ecf20Sopenharmony_ci				}
56758c2ecf20Sopenharmony_ci				len += sprintf(page + len, "\n");
56768c2ecf20Sopenharmony_ci			}
56778c2ecf20Sopenharmony_ci
56788c2ecf20Sopenharmony_ci			if (sense[27] & DASD_SENSE_BIT_0) {
56798c2ecf20Sopenharmony_ci				/* 24 Byte Sense Data */
56808c2ecf20Sopenharmony_ci				sprintf(page + len, PRINTK_HEADER
56818c2ecf20Sopenharmony_ci					" 24 Byte: %x MSG %x, "
56828c2ecf20Sopenharmony_ci					"%s MSGb to SYSOP\n",
56838c2ecf20Sopenharmony_ci					sense[7] >> 4, sense[7] & 0x0f,
56848c2ecf20Sopenharmony_ci					sense[1] & 0x10 ? "" : "no");
56858c2ecf20Sopenharmony_ci			} else {
56868c2ecf20Sopenharmony_ci				/* 32 Byte Sense Data */
56878c2ecf20Sopenharmony_ci				sprintf(page + len, PRINTK_HEADER
56888c2ecf20Sopenharmony_ci					" 32 Byte: Format: %x "
56898c2ecf20Sopenharmony_ci					"Exception class %x\n",
56908c2ecf20Sopenharmony_ci					sense[6] & 0x0f, sense[22] >> 4);
56918c2ecf20Sopenharmony_ci			}
56928c2ecf20Sopenharmony_ci		} else {
56938c2ecf20Sopenharmony_ci			sprintf(page + len, PRINTK_HEADER
56948c2ecf20Sopenharmony_ci				" SORRY - NO VALID SENSE AVAILABLE\n");
56958c2ecf20Sopenharmony_ci		}
56968c2ecf20Sopenharmony_ci	} else {
56978c2ecf20Sopenharmony_ci		sprintf(page + len, PRINTK_HEADER
56988c2ecf20Sopenharmony_ci			" SORRY - NO TSB DATA AVAILABLE\n");
56998c2ecf20Sopenharmony_ci	}
57008c2ecf20Sopenharmony_ci	printk(KERN_ERR "%s", page);
57018c2ecf20Sopenharmony_ci	free_page((unsigned long) page);
57028c2ecf20Sopenharmony_ci}
57038c2ecf20Sopenharmony_ci
57048c2ecf20Sopenharmony_cistatic void dasd_eckd_dump_sense(struct dasd_device *device,
57058c2ecf20Sopenharmony_ci				 struct dasd_ccw_req *req, struct irb *irb)
57068c2ecf20Sopenharmony_ci{
57078c2ecf20Sopenharmony_ci	u8 *sense = dasd_get_sense(irb);
57088c2ecf20Sopenharmony_ci
57098c2ecf20Sopenharmony_ci	if (scsw_is_tm(&irb->scsw)) {
57108c2ecf20Sopenharmony_ci		/*
57118c2ecf20Sopenharmony_ci		 * In some cases the 'File Protected' or 'Incorrect Length'
57128c2ecf20Sopenharmony_ci		 * error might be expected and log messages shouldn't be written
57138c2ecf20Sopenharmony_ci		 * then. Check if the according suppress bit is set.
57148c2ecf20Sopenharmony_ci		 */
57158c2ecf20Sopenharmony_ci		if (sense && (sense[1] & SNS1_FILE_PROTECTED) &&
57168c2ecf20Sopenharmony_ci		    test_bit(DASD_CQR_SUPPRESS_FP, &req->flags))
57178c2ecf20Sopenharmony_ci			return;
57188c2ecf20Sopenharmony_ci		if (scsw_cstat(&irb->scsw) == 0x40 &&
57198c2ecf20Sopenharmony_ci		    test_bit(DASD_CQR_SUPPRESS_IL, &req->flags))
57208c2ecf20Sopenharmony_ci			return;
57218c2ecf20Sopenharmony_ci
57228c2ecf20Sopenharmony_ci		dasd_eckd_dump_sense_tcw(device, req, irb);
57238c2ecf20Sopenharmony_ci	} else {
57248c2ecf20Sopenharmony_ci		/*
57258c2ecf20Sopenharmony_ci		 * In some cases the 'Command Reject' or 'No Record Found'
57268c2ecf20Sopenharmony_ci		 * error might be expected and log messages shouldn't be
57278c2ecf20Sopenharmony_ci		 * written then. Check if the according suppress bit is set.
57288c2ecf20Sopenharmony_ci		 */
57298c2ecf20Sopenharmony_ci		if (sense && sense[0] & SNS0_CMD_REJECT &&
57308c2ecf20Sopenharmony_ci		    test_bit(DASD_CQR_SUPPRESS_CR, &req->flags))
57318c2ecf20Sopenharmony_ci			return;
57328c2ecf20Sopenharmony_ci
57338c2ecf20Sopenharmony_ci		if (sense && sense[1] & SNS1_NO_REC_FOUND &&
57348c2ecf20Sopenharmony_ci		    test_bit(DASD_CQR_SUPPRESS_NRF, &req->flags))
57358c2ecf20Sopenharmony_ci			return;
57368c2ecf20Sopenharmony_ci
57378c2ecf20Sopenharmony_ci		dasd_eckd_dump_sense_ccw(device, req, irb);
57388c2ecf20Sopenharmony_ci	}
57398c2ecf20Sopenharmony_ci}
57408c2ecf20Sopenharmony_ci
57418c2ecf20Sopenharmony_cistatic int dasd_eckd_pm_freeze(struct dasd_device *device)
57428c2ecf20Sopenharmony_ci{
57438c2ecf20Sopenharmony_ci	/*
57448c2ecf20Sopenharmony_ci	 * the device should be disconnected from our LCU structure
57458c2ecf20Sopenharmony_ci	 * on restore we will reconnect it and reread LCU specific
57468c2ecf20Sopenharmony_ci	 * information like PAV support that might have changed
57478c2ecf20Sopenharmony_ci	 */
57488c2ecf20Sopenharmony_ci	dasd_alias_remove_device(device);
57498c2ecf20Sopenharmony_ci	dasd_alias_disconnect_device_from_lcu(device);
57508c2ecf20Sopenharmony_ci
57518c2ecf20Sopenharmony_ci	return 0;
57528c2ecf20Sopenharmony_ci}
57538c2ecf20Sopenharmony_ci
57548c2ecf20Sopenharmony_cistatic int dasd_eckd_restore_device(struct dasd_device *device)
57558c2ecf20Sopenharmony_ci{
57568c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
57578c2ecf20Sopenharmony_ci	struct dasd_eckd_characteristics temp_rdc_data;
57588c2ecf20Sopenharmony_ci	int rc;
57598c2ecf20Sopenharmony_ci	struct dasd_uid temp_uid;
57608c2ecf20Sopenharmony_ci	unsigned long flags;
57618c2ecf20Sopenharmony_ci	unsigned long cqr_flags = 0;
57628c2ecf20Sopenharmony_ci
57638c2ecf20Sopenharmony_ci	/* Read Configuration Data */
57648c2ecf20Sopenharmony_ci	rc = dasd_eckd_read_conf(device);
57658c2ecf20Sopenharmony_ci	if (rc) {
57668c2ecf20Sopenharmony_ci		DBF_EVENT_DEVID(DBF_WARNING, device->cdev,
57678c2ecf20Sopenharmony_ci				"Read configuration data failed, rc=%d", rc);
57688c2ecf20Sopenharmony_ci		goto out_err;
57698c2ecf20Sopenharmony_ci	}
57708c2ecf20Sopenharmony_ci
57718c2ecf20Sopenharmony_ci	dasd_eckd_get_uid(device, &temp_uid);
57728c2ecf20Sopenharmony_ci	/* Generate device unique id */
57738c2ecf20Sopenharmony_ci	rc = dasd_eckd_generate_uid(device);
57748c2ecf20Sopenharmony_ci	spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
57758c2ecf20Sopenharmony_ci	if (memcmp(&private->uid, &temp_uid, sizeof(struct dasd_uid)) != 0)
57768c2ecf20Sopenharmony_ci		dev_err(&device->cdev->dev, "The UID of the DASD has "
57778c2ecf20Sopenharmony_ci			"changed\n");
57788c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
57798c2ecf20Sopenharmony_ci	if (rc)
57808c2ecf20Sopenharmony_ci		goto out_err;
57818c2ecf20Sopenharmony_ci
57828c2ecf20Sopenharmony_ci	/* register lcu with alias handling, enable PAV if this is a new lcu */
57838c2ecf20Sopenharmony_ci	rc = dasd_alias_make_device_known_to_lcu(device);
57848c2ecf20Sopenharmony_ci	if (rc)
57858c2ecf20Sopenharmony_ci		goto out_err;
57868c2ecf20Sopenharmony_ci
57878c2ecf20Sopenharmony_ci	set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr_flags);
57888c2ecf20Sopenharmony_ci	dasd_eckd_validate_server(device, cqr_flags);
57898c2ecf20Sopenharmony_ci
57908c2ecf20Sopenharmony_ci	/* RE-Read Configuration Data */
57918c2ecf20Sopenharmony_ci	rc = dasd_eckd_read_conf(device);
57928c2ecf20Sopenharmony_ci	if (rc) {
57938c2ecf20Sopenharmony_ci		DBF_EVENT_DEVID(DBF_WARNING, device->cdev,
57948c2ecf20Sopenharmony_ci			"Read configuration data failed, rc=%d", rc);
57958c2ecf20Sopenharmony_ci		goto out_err2;
57968c2ecf20Sopenharmony_ci	}
57978c2ecf20Sopenharmony_ci
57988c2ecf20Sopenharmony_ci	/* Read Feature Codes */
57998c2ecf20Sopenharmony_ci	dasd_eckd_read_features(device);
58008c2ecf20Sopenharmony_ci
58018c2ecf20Sopenharmony_ci	/* Read Volume Information */
58028c2ecf20Sopenharmony_ci	dasd_eckd_read_vol_info(device);
58038c2ecf20Sopenharmony_ci
58048c2ecf20Sopenharmony_ci	/* Read Extent Pool Information */
58058c2ecf20Sopenharmony_ci	dasd_eckd_read_ext_pool_info(device);
58068c2ecf20Sopenharmony_ci
58078c2ecf20Sopenharmony_ci	/* Read Device Characteristics */
58088c2ecf20Sopenharmony_ci	rc = dasd_generic_read_dev_chars(device, DASD_ECKD_MAGIC,
58098c2ecf20Sopenharmony_ci					 &temp_rdc_data, 64);
58108c2ecf20Sopenharmony_ci	if (rc) {
58118c2ecf20Sopenharmony_ci		DBF_EVENT_DEVID(DBF_WARNING, device->cdev,
58128c2ecf20Sopenharmony_ci				"Read device characteristic failed, rc=%d", rc);
58138c2ecf20Sopenharmony_ci		goto out_err2;
58148c2ecf20Sopenharmony_ci	}
58158c2ecf20Sopenharmony_ci	spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
58168c2ecf20Sopenharmony_ci	memcpy(&private->rdc_data, &temp_rdc_data, sizeof(temp_rdc_data));
58178c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
58188c2ecf20Sopenharmony_ci
58198c2ecf20Sopenharmony_ci	/* add device to alias management */
58208c2ecf20Sopenharmony_ci	dasd_alias_add_device(device);
58218c2ecf20Sopenharmony_ci
58228c2ecf20Sopenharmony_ci	return 0;
58238c2ecf20Sopenharmony_ci
58248c2ecf20Sopenharmony_ciout_err2:
58258c2ecf20Sopenharmony_ci	dasd_alias_disconnect_device_from_lcu(device);
58268c2ecf20Sopenharmony_ciout_err:
58278c2ecf20Sopenharmony_ci	return -1;
58288c2ecf20Sopenharmony_ci}
58298c2ecf20Sopenharmony_ci
58308c2ecf20Sopenharmony_cistatic int dasd_eckd_reload_device(struct dasd_device *device)
58318c2ecf20Sopenharmony_ci{
58328c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
58338c2ecf20Sopenharmony_ci	int rc, old_base;
58348c2ecf20Sopenharmony_ci	char print_uid[60];
58358c2ecf20Sopenharmony_ci	struct dasd_uid uid;
58368c2ecf20Sopenharmony_ci	unsigned long flags;
58378c2ecf20Sopenharmony_ci
58388c2ecf20Sopenharmony_ci	/*
58398c2ecf20Sopenharmony_ci	 * remove device from alias handling to prevent new requests
58408c2ecf20Sopenharmony_ci	 * from being scheduled on the wrong alias device
58418c2ecf20Sopenharmony_ci	 */
58428c2ecf20Sopenharmony_ci	dasd_alias_remove_device(device);
58438c2ecf20Sopenharmony_ci
58448c2ecf20Sopenharmony_ci	spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
58458c2ecf20Sopenharmony_ci	old_base = private->uid.base_unit_addr;
58468c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
58478c2ecf20Sopenharmony_ci
58488c2ecf20Sopenharmony_ci	/* Read Configuration Data */
58498c2ecf20Sopenharmony_ci	rc = dasd_eckd_read_conf(device);
58508c2ecf20Sopenharmony_ci	if (rc)
58518c2ecf20Sopenharmony_ci		goto out_err;
58528c2ecf20Sopenharmony_ci
58538c2ecf20Sopenharmony_ci	rc = dasd_eckd_generate_uid(device);
58548c2ecf20Sopenharmony_ci	if (rc)
58558c2ecf20Sopenharmony_ci		goto out_err;
58568c2ecf20Sopenharmony_ci	/*
58578c2ecf20Sopenharmony_ci	 * update unit address configuration and
58588c2ecf20Sopenharmony_ci	 * add device to alias management
58598c2ecf20Sopenharmony_ci	 */
58608c2ecf20Sopenharmony_ci	dasd_alias_update_add_device(device);
58618c2ecf20Sopenharmony_ci
58628c2ecf20Sopenharmony_ci	dasd_eckd_get_uid(device, &uid);
58638c2ecf20Sopenharmony_ci
58648c2ecf20Sopenharmony_ci	if (old_base != uid.base_unit_addr) {
58658c2ecf20Sopenharmony_ci		if (strlen(uid.vduit) > 0)
58668c2ecf20Sopenharmony_ci			snprintf(print_uid, sizeof(print_uid),
58678c2ecf20Sopenharmony_ci				 "%s.%s.%04x.%02x.%s", uid.vendor, uid.serial,
58688c2ecf20Sopenharmony_ci				 uid.ssid, uid.base_unit_addr, uid.vduit);
58698c2ecf20Sopenharmony_ci		else
58708c2ecf20Sopenharmony_ci			snprintf(print_uid, sizeof(print_uid),
58718c2ecf20Sopenharmony_ci				 "%s.%s.%04x.%02x", uid.vendor, uid.serial,
58728c2ecf20Sopenharmony_ci				 uid.ssid, uid.base_unit_addr);
58738c2ecf20Sopenharmony_ci
58748c2ecf20Sopenharmony_ci		dev_info(&device->cdev->dev,
58758c2ecf20Sopenharmony_ci			 "An Alias device was reassigned to a new base device "
58768c2ecf20Sopenharmony_ci			 "with UID: %s\n", print_uid);
58778c2ecf20Sopenharmony_ci	}
58788c2ecf20Sopenharmony_ci	return 0;
58798c2ecf20Sopenharmony_ci
58808c2ecf20Sopenharmony_ciout_err:
58818c2ecf20Sopenharmony_ci	return -1;
58828c2ecf20Sopenharmony_ci}
58838c2ecf20Sopenharmony_ci
58848c2ecf20Sopenharmony_cistatic int dasd_eckd_read_message_buffer(struct dasd_device *device,
58858c2ecf20Sopenharmony_ci					 struct dasd_rssd_messages *messages,
58868c2ecf20Sopenharmony_ci					 __u8 lpum)
58878c2ecf20Sopenharmony_ci{
58888c2ecf20Sopenharmony_ci	struct dasd_rssd_messages *message_buf;
58898c2ecf20Sopenharmony_ci	struct dasd_psf_prssd_data *prssdp;
58908c2ecf20Sopenharmony_ci	struct dasd_ccw_req *cqr;
58918c2ecf20Sopenharmony_ci	struct ccw1 *ccw;
58928c2ecf20Sopenharmony_ci	int rc;
58938c2ecf20Sopenharmony_ci
58948c2ecf20Sopenharmony_ci	cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */	+ 1 /* RSSD */,
58958c2ecf20Sopenharmony_ci				   (sizeof(struct dasd_psf_prssd_data) +
58968c2ecf20Sopenharmony_ci				    sizeof(struct dasd_rssd_messages)),
58978c2ecf20Sopenharmony_ci				   device, NULL);
58988c2ecf20Sopenharmony_ci	if (IS_ERR(cqr)) {
58998c2ecf20Sopenharmony_ci		DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s",
59008c2ecf20Sopenharmony_ci				"Could not allocate read message buffer request");
59018c2ecf20Sopenharmony_ci		return PTR_ERR(cqr);
59028c2ecf20Sopenharmony_ci	}
59038c2ecf20Sopenharmony_ci
59048c2ecf20Sopenharmony_ci	cqr->lpm = lpum;
59058c2ecf20Sopenharmony_ciretry:
59068c2ecf20Sopenharmony_ci	cqr->startdev = device;
59078c2ecf20Sopenharmony_ci	cqr->memdev = device;
59088c2ecf20Sopenharmony_ci	cqr->block = NULL;
59098c2ecf20Sopenharmony_ci	cqr->expires = 10 * HZ;
59108c2ecf20Sopenharmony_ci	set_bit(DASD_CQR_VERIFY_PATH, &cqr->flags);
59118c2ecf20Sopenharmony_ci	/* dasd_sleep_on_immediatly does not do complex error
59128c2ecf20Sopenharmony_ci	 * recovery so clear erp flag and set retry counter to
59138c2ecf20Sopenharmony_ci	 * do basic erp */
59148c2ecf20Sopenharmony_ci	clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
59158c2ecf20Sopenharmony_ci	cqr->retries = 256;
59168c2ecf20Sopenharmony_ci
59178c2ecf20Sopenharmony_ci	/* Prepare for Read Subsystem Data */
59188c2ecf20Sopenharmony_ci	prssdp = (struct dasd_psf_prssd_data *) cqr->data;
59198c2ecf20Sopenharmony_ci	memset(prssdp, 0, sizeof(struct dasd_psf_prssd_data));
59208c2ecf20Sopenharmony_ci	prssdp->order = PSF_ORDER_PRSSD;
59218c2ecf20Sopenharmony_ci	prssdp->suborder = 0x03;	/* Message Buffer */
59228c2ecf20Sopenharmony_ci	/* all other bytes of prssdp must be zero */
59238c2ecf20Sopenharmony_ci
59248c2ecf20Sopenharmony_ci	ccw = cqr->cpaddr;
59258c2ecf20Sopenharmony_ci	ccw->cmd_code = DASD_ECKD_CCW_PSF;
59268c2ecf20Sopenharmony_ci	ccw->count = sizeof(struct dasd_psf_prssd_data);
59278c2ecf20Sopenharmony_ci	ccw->flags |= CCW_FLAG_CC;
59288c2ecf20Sopenharmony_ci	ccw->flags |= CCW_FLAG_SLI;
59298c2ecf20Sopenharmony_ci	ccw->cda = (__u32)(addr_t) prssdp;
59308c2ecf20Sopenharmony_ci
59318c2ecf20Sopenharmony_ci	/* Read Subsystem Data - message buffer */
59328c2ecf20Sopenharmony_ci	message_buf = (struct dasd_rssd_messages *) (prssdp + 1);
59338c2ecf20Sopenharmony_ci	memset(message_buf, 0, sizeof(struct dasd_rssd_messages));
59348c2ecf20Sopenharmony_ci
59358c2ecf20Sopenharmony_ci	ccw++;
59368c2ecf20Sopenharmony_ci	ccw->cmd_code = DASD_ECKD_CCW_RSSD;
59378c2ecf20Sopenharmony_ci	ccw->count = sizeof(struct dasd_rssd_messages);
59388c2ecf20Sopenharmony_ci	ccw->flags |= CCW_FLAG_SLI;
59398c2ecf20Sopenharmony_ci	ccw->cda = (__u32)(addr_t) message_buf;
59408c2ecf20Sopenharmony_ci
59418c2ecf20Sopenharmony_ci	cqr->buildclk = get_tod_clock();
59428c2ecf20Sopenharmony_ci	cqr->status = DASD_CQR_FILLED;
59438c2ecf20Sopenharmony_ci	rc = dasd_sleep_on_immediatly(cqr);
59448c2ecf20Sopenharmony_ci	if (rc == 0) {
59458c2ecf20Sopenharmony_ci		prssdp = (struct dasd_psf_prssd_data *) cqr->data;
59468c2ecf20Sopenharmony_ci		message_buf = (struct dasd_rssd_messages *)
59478c2ecf20Sopenharmony_ci			(prssdp + 1);
59488c2ecf20Sopenharmony_ci		memcpy(messages, message_buf,
59498c2ecf20Sopenharmony_ci		       sizeof(struct dasd_rssd_messages));
59508c2ecf20Sopenharmony_ci	} else if (cqr->lpm) {
59518c2ecf20Sopenharmony_ci		/*
59528c2ecf20Sopenharmony_ci		 * on z/VM we might not be able to do I/O on the requested path
59538c2ecf20Sopenharmony_ci		 * but instead we get the required information on any path
59548c2ecf20Sopenharmony_ci		 * so retry with open path mask
59558c2ecf20Sopenharmony_ci		 */
59568c2ecf20Sopenharmony_ci		cqr->lpm = 0;
59578c2ecf20Sopenharmony_ci		goto retry;
59588c2ecf20Sopenharmony_ci	} else
59598c2ecf20Sopenharmony_ci		DBF_EVENT_DEVID(DBF_WARNING, device->cdev,
59608c2ecf20Sopenharmony_ci				"Reading messages failed with rc=%d\n"
59618c2ecf20Sopenharmony_ci				, rc);
59628c2ecf20Sopenharmony_ci	dasd_sfree_request(cqr, cqr->memdev);
59638c2ecf20Sopenharmony_ci	return rc;
59648c2ecf20Sopenharmony_ci}
59658c2ecf20Sopenharmony_ci
59668c2ecf20Sopenharmony_cistatic int dasd_eckd_query_host_access(struct dasd_device *device,
59678c2ecf20Sopenharmony_ci				       struct dasd_psf_query_host_access *data)
59688c2ecf20Sopenharmony_ci{
59698c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
59708c2ecf20Sopenharmony_ci	struct dasd_psf_query_host_access *host_access;
59718c2ecf20Sopenharmony_ci	struct dasd_psf_prssd_data *prssdp;
59728c2ecf20Sopenharmony_ci	struct dasd_ccw_req *cqr;
59738c2ecf20Sopenharmony_ci	struct ccw1 *ccw;
59748c2ecf20Sopenharmony_ci	int rc;
59758c2ecf20Sopenharmony_ci
59768c2ecf20Sopenharmony_ci	/* not available for HYPER PAV alias devices */
59778c2ecf20Sopenharmony_ci	if (!device->block && private->lcu->pav == HYPER_PAV)
59788c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
59798c2ecf20Sopenharmony_ci
59808c2ecf20Sopenharmony_ci	/* may not be supported by the storage server */
59818c2ecf20Sopenharmony_ci	if (!(private->features.feature[14] & 0x80))
59828c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
59838c2ecf20Sopenharmony_ci
59848c2ecf20Sopenharmony_ci	cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */	+ 1 /* RSSD */,
59858c2ecf20Sopenharmony_ci				   sizeof(struct dasd_psf_prssd_data) + 1,
59868c2ecf20Sopenharmony_ci				   device, NULL);
59878c2ecf20Sopenharmony_ci	if (IS_ERR(cqr)) {
59888c2ecf20Sopenharmony_ci		DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s",
59898c2ecf20Sopenharmony_ci				"Could not allocate read message buffer request");
59908c2ecf20Sopenharmony_ci		return PTR_ERR(cqr);
59918c2ecf20Sopenharmony_ci	}
59928c2ecf20Sopenharmony_ci	host_access = kzalloc(sizeof(*host_access), GFP_KERNEL | GFP_DMA);
59938c2ecf20Sopenharmony_ci	if (!host_access) {
59948c2ecf20Sopenharmony_ci		dasd_sfree_request(cqr, device);
59958c2ecf20Sopenharmony_ci		DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s",
59968c2ecf20Sopenharmony_ci				"Could not allocate host_access buffer");
59978c2ecf20Sopenharmony_ci		return -ENOMEM;
59988c2ecf20Sopenharmony_ci	}
59998c2ecf20Sopenharmony_ci	cqr->startdev = device;
60008c2ecf20Sopenharmony_ci	cqr->memdev = device;
60018c2ecf20Sopenharmony_ci	cqr->block = NULL;
60028c2ecf20Sopenharmony_ci	cqr->retries = 256;
60038c2ecf20Sopenharmony_ci	cqr->expires = 10 * HZ;
60048c2ecf20Sopenharmony_ci
60058c2ecf20Sopenharmony_ci	/* Prepare for Read Subsystem Data */
60068c2ecf20Sopenharmony_ci	prssdp = (struct dasd_psf_prssd_data *) cqr->data;
60078c2ecf20Sopenharmony_ci	memset(prssdp, 0, sizeof(struct dasd_psf_prssd_data));
60088c2ecf20Sopenharmony_ci	prssdp->order = PSF_ORDER_PRSSD;
60098c2ecf20Sopenharmony_ci	prssdp->suborder = PSF_SUBORDER_QHA;	/* query host access */
60108c2ecf20Sopenharmony_ci	/* LSS and Volume that will be queried */
60118c2ecf20Sopenharmony_ci	prssdp->lss = private->ned->ID;
60128c2ecf20Sopenharmony_ci	prssdp->volume = private->ned->unit_addr;
60138c2ecf20Sopenharmony_ci	/* all other bytes of prssdp must be zero */
60148c2ecf20Sopenharmony_ci
60158c2ecf20Sopenharmony_ci	ccw = cqr->cpaddr;
60168c2ecf20Sopenharmony_ci	ccw->cmd_code = DASD_ECKD_CCW_PSF;
60178c2ecf20Sopenharmony_ci	ccw->count = sizeof(struct dasd_psf_prssd_data);
60188c2ecf20Sopenharmony_ci	ccw->flags |= CCW_FLAG_CC;
60198c2ecf20Sopenharmony_ci	ccw->flags |= CCW_FLAG_SLI;
60208c2ecf20Sopenharmony_ci	ccw->cda = (__u32)(addr_t) prssdp;
60218c2ecf20Sopenharmony_ci
60228c2ecf20Sopenharmony_ci	/* Read Subsystem Data - query host access */
60238c2ecf20Sopenharmony_ci	ccw++;
60248c2ecf20Sopenharmony_ci	ccw->cmd_code = DASD_ECKD_CCW_RSSD;
60258c2ecf20Sopenharmony_ci	ccw->count = sizeof(struct dasd_psf_query_host_access);
60268c2ecf20Sopenharmony_ci	ccw->flags |= CCW_FLAG_SLI;
60278c2ecf20Sopenharmony_ci	ccw->cda = (__u32)(addr_t) host_access;
60288c2ecf20Sopenharmony_ci
60298c2ecf20Sopenharmony_ci	cqr->buildclk = get_tod_clock();
60308c2ecf20Sopenharmony_ci	cqr->status = DASD_CQR_FILLED;
60318c2ecf20Sopenharmony_ci	/* the command might not be supported, suppress error message */
60328c2ecf20Sopenharmony_ci	__set_bit(DASD_CQR_SUPPRESS_CR, &cqr->flags);
60338c2ecf20Sopenharmony_ci	rc = dasd_sleep_on_interruptible(cqr);
60348c2ecf20Sopenharmony_ci	if (rc == 0) {
60358c2ecf20Sopenharmony_ci		*data = *host_access;
60368c2ecf20Sopenharmony_ci	} else {
60378c2ecf20Sopenharmony_ci		DBF_EVENT_DEVID(DBF_WARNING, device->cdev,
60388c2ecf20Sopenharmony_ci				"Reading host access data failed with rc=%d\n",
60398c2ecf20Sopenharmony_ci				rc);
60408c2ecf20Sopenharmony_ci		rc = -EOPNOTSUPP;
60418c2ecf20Sopenharmony_ci	}
60428c2ecf20Sopenharmony_ci
60438c2ecf20Sopenharmony_ci	dasd_sfree_request(cqr, cqr->memdev);
60448c2ecf20Sopenharmony_ci	kfree(host_access);
60458c2ecf20Sopenharmony_ci	return rc;
60468c2ecf20Sopenharmony_ci}
60478c2ecf20Sopenharmony_ci/*
60488c2ecf20Sopenharmony_ci * return number of grouped devices
60498c2ecf20Sopenharmony_ci */
60508c2ecf20Sopenharmony_cistatic int dasd_eckd_host_access_count(struct dasd_device *device)
60518c2ecf20Sopenharmony_ci{
60528c2ecf20Sopenharmony_ci	struct dasd_psf_query_host_access *access;
60538c2ecf20Sopenharmony_ci	struct dasd_ckd_path_group_entry *entry;
60548c2ecf20Sopenharmony_ci	struct dasd_ckd_host_information *info;
60558c2ecf20Sopenharmony_ci	int count = 0;
60568c2ecf20Sopenharmony_ci	int rc, i;
60578c2ecf20Sopenharmony_ci
60588c2ecf20Sopenharmony_ci	access = kzalloc(sizeof(*access), GFP_NOIO);
60598c2ecf20Sopenharmony_ci	if (!access) {
60608c2ecf20Sopenharmony_ci		DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s",
60618c2ecf20Sopenharmony_ci				"Could not allocate access buffer");
60628c2ecf20Sopenharmony_ci		return -ENOMEM;
60638c2ecf20Sopenharmony_ci	}
60648c2ecf20Sopenharmony_ci	rc = dasd_eckd_query_host_access(device, access);
60658c2ecf20Sopenharmony_ci	if (rc) {
60668c2ecf20Sopenharmony_ci		kfree(access);
60678c2ecf20Sopenharmony_ci		return rc;
60688c2ecf20Sopenharmony_ci	}
60698c2ecf20Sopenharmony_ci
60708c2ecf20Sopenharmony_ci	info = (struct dasd_ckd_host_information *)
60718c2ecf20Sopenharmony_ci		access->host_access_information;
60728c2ecf20Sopenharmony_ci	for (i = 0; i < info->entry_count; i++) {
60738c2ecf20Sopenharmony_ci		entry = (struct dasd_ckd_path_group_entry *)
60748c2ecf20Sopenharmony_ci			(info->entry + i * info->entry_size);
60758c2ecf20Sopenharmony_ci		if (entry->status_flags & DASD_ECKD_PG_GROUPED)
60768c2ecf20Sopenharmony_ci			count++;
60778c2ecf20Sopenharmony_ci	}
60788c2ecf20Sopenharmony_ci
60798c2ecf20Sopenharmony_ci	kfree(access);
60808c2ecf20Sopenharmony_ci	return count;
60818c2ecf20Sopenharmony_ci}
60828c2ecf20Sopenharmony_ci
60838c2ecf20Sopenharmony_ci/*
60848c2ecf20Sopenharmony_ci * write host access information to a sequential file
60858c2ecf20Sopenharmony_ci */
60868c2ecf20Sopenharmony_cistatic int dasd_hosts_print(struct dasd_device *device, struct seq_file *m)
60878c2ecf20Sopenharmony_ci{
60888c2ecf20Sopenharmony_ci	struct dasd_psf_query_host_access *access;
60898c2ecf20Sopenharmony_ci	struct dasd_ckd_path_group_entry *entry;
60908c2ecf20Sopenharmony_ci	struct dasd_ckd_host_information *info;
60918c2ecf20Sopenharmony_ci	char sysplex[9] = "";
60928c2ecf20Sopenharmony_ci	int rc, i;
60938c2ecf20Sopenharmony_ci
60948c2ecf20Sopenharmony_ci	access = kzalloc(sizeof(*access), GFP_NOIO);
60958c2ecf20Sopenharmony_ci	if (!access) {
60968c2ecf20Sopenharmony_ci		DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s",
60978c2ecf20Sopenharmony_ci				"Could not allocate access buffer");
60988c2ecf20Sopenharmony_ci		return -ENOMEM;
60998c2ecf20Sopenharmony_ci	}
61008c2ecf20Sopenharmony_ci	rc = dasd_eckd_query_host_access(device, access);
61018c2ecf20Sopenharmony_ci	if (rc) {
61028c2ecf20Sopenharmony_ci		kfree(access);
61038c2ecf20Sopenharmony_ci		return rc;
61048c2ecf20Sopenharmony_ci	}
61058c2ecf20Sopenharmony_ci
61068c2ecf20Sopenharmony_ci	info = (struct dasd_ckd_host_information *)
61078c2ecf20Sopenharmony_ci		access->host_access_information;
61088c2ecf20Sopenharmony_ci	for (i = 0; i < info->entry_count; i++) {
61098c2ecf20Sopenharmony_ci		entry = (struct dasd_ckd_path_group_entry *)
61108c2ecf20Sopenharmony_ci			(info->entry + i * info->entry_size);
61118c2ecf20Sopenharmony_ci		/* PGID */
61128c2ecf20Sopenharmony_ci		seq_printf(m, "pgid %*phN\n", 11, entry->pgid);
61138c2ecf20Sopenharmony_ci		/* FLAGS */
61148c2ecf20Sopenharmony_ci		seq_printf(m, "status_flags %02x\n", entry->status_flags);
61158c2ecf20Sopenharmony_ci		/* SYSPLEX NAME */
61168c2ecf20Sopenharmony_ci		memcpy(&sysplex, &entry->sysplex_name, sizeof(sysplex) - 1);
61178c2ecf20Sopenharmony_ci		EBCASC(sysplex, sizeof(sysplex));
61188c2ecf20Sopenharmony_ci		seq_printf(m, "sysplex_name %8s\n", sysplex);
61198c2ecf20Sopenharmony_ci		/* SUPPORTED CYLINDER */
61208c2ecf20Sopenharmony_ci		seq_printf(m, "supported_cylinder %d\n", entry->cylinder);
61218c2ecf20Sopenharmony_ci		/* TIMESTAMP */
61228c2ecf20Sopenharmony_ci		seq_printf(m, "timestamp %lu\n", (unsigned long)
61238c2ecf20Sopenharmony_ci			   entry->timestamp);
61248c2ecf20Sopenharmony_ci	}
61258c2ecf20Sopenharmony_ci	kfree(access);
61268c2ecf20Sopenharmony_ci
61278c2ecf20Sopenharmony_ci	return 0;
61288c2ecf20Sopenharmony_ci}
61298c2ecf20Sopenharmony_ci
61308c2ecf20Sopenharmony_ci/*
61318c2ecf20Sopenharmony_ci * Perform Subsystem Function - CUIR response
61328c2ecf20Sopenharmony_ci */
61338c2ecf20Sopenharmony_cistatic int
61348c2ecf20Sopenharmony_cidasd_eckd_psf_cuir_response(struct dasd_device *device, int response,
61358c2ecf20Sopenharmony_ci			    __u32 message_id, __u8 lpum)
61368c2ecf20Sopenharmony_ci{
61378c2ecf20Sopenharmony_ci	struct dasd_psf_cuir_response *psf_cuir;
61388c2ecf20Sopenharmony_ci	int pos = pathmask_to_pos(lpum);
61398c2ecf20Sopenharmony_ci	struct dasd_ccw_req *cqr;
61408c2ecf20Sopenharmony_ci	struct ccw1 *ccw;
61418c2ecf20Sopenharmony_ci	int rc;
61428c2ecf20Sopenharmony_ci
61438c2ecf20Sopenharmony_ci	cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */ ,
61448c2ecf20Sopenharmony_ci				   sizeof(struct dasd_psf_cuir_response),
61458c2ecf20Sopenharmony_ci				   device, NULL);
61468c2ecf20Sopenharmony_ci
61478c2ecf20Sopenharmony_ci	if (IS_ERR(cqr)) {
61488c2ecf20Sopenharmony_ci		DBF_DEV_EVENT(DBF_WARNING, device, "%s",
61498c2ecf20Sopenharmony_ci			   "Could not allocate PSF-CUIR request");
61508c2ecf20Sopenharmony_ci		return PTR_ERR(cqr);
61518c2ecf20Sopenharmony_ci	}
61528c2ecf20Sopenharmony_ci
61538c2ecf20Sopenharmony_ci	psf_cuir = (struct dasd_psf_cuir_response *)cqr->data;
61548c2ecf20Sopenharmony_ci	psf_cuir->order = PSF_ORDER_CUIR_RESPONSE;
61558c2ecf20Sopenharmony_ci	psf_cuir->cc = response;
61568c2ecf20Sopenharmony_ci	psf_cuir->chpid = device->path[pos].chpid;
61578c2ecf20Sopenharmony_ci	psf_cuir->message_id = message_id;
61588c2ecf20Sopenharmony_ci	psf_cuir->cssid = device->path[pos].cssid;
61598c2ecf20Sopenharmony_ci	psf_cuir->ssid = device->path[pos].ssid;
61608c2ecf20Sopenharmony_ci	ccw = cqr->cpaddr;
61618c2ecf20Sopenharmony_ci	ccw->cmd_code = DASD_ECKD_CCW_PSF;
61628c2ecf20Sopenharmony_ci	ccw->cda = (__u32)(addr_t)psf_cuir;
61638c2ecf20Sopenharmony_ci	ccw->flags = CCW_FLAG_SLI;
61648c2ecf20Sopenharmony_ci	ccw->count = sizeof(struct dasd_psf_cuir_response);
61658c2ecf20Sopenharmony_ci
61668c2ecf20Sopenharmony_ci	cqr->startdev = device;
61678c2ecf20Sopenharmony_ci	cqr->memdev = device;
61688c2ecf20Sopenharmony_ci	cqr->block = NULL;
61698c2ecf20Sopenharmony_ci	cqr->retries = 256;
61708c2ecf20Sopenharmony_ci	cqr->expires = 10*HZ;
61718c2ecf20Sopenharmony_ci	cqr->buildclk = get_tod_clock();
61728c2ecf20Sopenharmony_ci	cqr->status = DASD_CQR_FILLED;
61738c2ecf20Sopenharmony_ci	set_bit(DASD_CQR_VERIFY_PATH, &cqr->flags);
61748c2ecf20Sopenharmony_ci
61758c2ecf20Sopenharmony_ci	rc = dasd_sleep_on(cqr);
61768c2ecf20Sopenharmony_ci
61778c2ecf20Sopenharmony_ci	dasd_sfree_request(cqr, cqr->memdev);
61788c2ecf20Sopenharmony_ci	return rc;
61798c2ecf20Sopenharmony_ci}
61808c2ecf20Sopenharmony_ci
61818c2ecf20Sopenharmony_ci/*
61828c2ecf20Sopenharmony_ci * return configuration data that is referenced by record selector
61838c2ecf20Sopenharmony_ci * if a record selector is specified or per default return the
61848c2ecf20Sopenharmony_ci * conf_data pointer for the path specified by lpum
61858c2ecf20Sopenharmony_ci */
61868c2ecf20Sopenharmony_cistatic struct dasd_conf_data *dasd_eckd_get_ref_conf(struct dasd_device *device,
61878c2ecf20Sopenharmony_ci						     __u8 lpum,
61888c2ecf20Sopenharmony_ci						     struct dasd_cuir_message *cuir)
61898c2ecf20Sopenharmony_ci{
61908c2ecf20Sopenharmony_ci	struct dasd_conf_data *conf_data;
61918c2ecf20Sopenharmony_ci	int path, pos;
61928c2ecf20Sopenharmony_ci
61938c2ecf20Sopenharmony_ci	if (cuir->record_selector == 0)
61948c2ecf20Sopenharmony_ci		goto out;
61958c2ecf20Sopenharmony_ci	for (path = 0x80, pos = 0; path; path >>= 1, pos++) {
61968c2ecf20Sopenharmony_ci		conf_data = device->path[pos].conf_data;
61978c2ecf20Sopenharmony_ci		if (conf_data->gneq.record_selector ==
61988c2ecf20Sopenharmony_ci		    cuir->record_selector)
61998c2ecf20Sopenharmony_ci			return conf_data;
62008c2ecf20Sopenharmony_ci	}
62018c2ecf20Sopenharmony_ciout:
62028c2ecf20Sopenharmony_ci	return device->path[pathmask_to_pos(lpum)].conf_data;
62038c2ecf20Sopenharmony_ci}
62048c2ecf20Sopenharmony_ci
62058c2ecf20Sopenharmony_ci/*
62068c2ecf20Sopenharmony_ci * This function determines the scope of a reconfiguration request by
62078c2ecf20Sopenharmony_ci * analysing the path and device selection data provided in the CUIR request.
62088c2ecf20Sopenharmony_ci * Returns a path mask containing CUIR affected paths for the give device.
62098c2ecf20Sopenharmony_ci *
62108c2ecf20Sopenharmony_ci * If the CUIR request does not contain the required information return the
62118c2ecf20Sopenharmony_ci * path mask of the path the attention message for the CUIR request was reveived
62128c2ecf20Sopenharmony_ci * on.
62138c2ecf20Sopenharmony_ci */
62148c2ecf20Sopenharmony_cistatic int dasd_eckd_cuir_scope(struct dasd_device *device, __u8 lpum,
62158c2ecf20Sopenharmony_ci				struct dasd_cuir_message *cuir)
62168c2ecf20Sopenharmony_ci{
62178c2ecf20Sopenharmony_ci	struct dasd_conf_data *ref_conf_data;
62188c2ecf20Sopenharmony_ci	unsigned long bitmask = 0, mask = 0;
62198c2ecf20Sopenharmony_ci	struct dasd_conf_data *conf_data;
62208c2ecf20Sopenharmony_ci	unsigned int pos, path;
62218c2ecf20Sopenharmony_ci	char *ref_gneq, *gneq;
62228c2ecf20Sopenharmony_ci	char *ref_ned, *ned;
62238c2ecf20Sopenharmony_ci	int tbcpm = 0;
62248c2ecf20Sopenharmony_ci
62258c2ecf20Sopenharmony_ci	/* if CUIR request does not specify the scope use the path
62268c2ecf20Sopenharmony_ci	   the attention message was presented on */
62278c2ecf20Sopenharmony_ci	if (!cuir->ned_map ||
62288c2ecf20Sopenharmony_ci	    !(cuir->neq_map[0] | cuir->neq_map[1] | cuir->neq_map[2]))
62298c2ecf20Sopenharmony_ci		return lpum;
62308c2ecf20Sopenharmony_ci
62318c2ecf20Sopenharmony_ci	/* get reference conf data */
62328c2ecf20Sopenharmony_ci	ref_conf_data = dasd_eckd_get_ref_conf(device, lpum, cuir);
62338c2ecf20Sopenharmony_ci	/* reference ned is determined by ned_map field */
62348c2ecf20Sopenharmony_ci	pos = 8 - ffs(cuir->ned_map);
62358c2ecf20Sopenharmony_ci	ref_ned = (char *)&ref_conf_data->neds[pos];
62368c2ecf20Sopenharmony_ci	ref_gneq = (char *)&ref_conf_data->gneq;
62378c2ecf20Sopenharmony_ci	/* transfer 24 bit neq_map to mask */
62388c2ecf20Sopenharmony_ci	mask = cuir->neq_map[2];
62398c2ecf20Sopenharmony_ci	mask |= cuir->neq_map[1] << 8;
62408c2ecf20Sopenharmony_ci	mask |= cuir->neq_map[0] << 16;
62418c2ecf20Sopenharmony_ci
62428c2ecf20Sopenharmony_ci	for (path = 0; path < 8; path++) {
62438c2ecf20Sopenharmony_ci		/* initialise data per path */
62448c2ecf20Sopenharmony_ci		bitmask = mask;
62458c2ecf20Sopenharmony_ci		conf_data = device->path[path].conf_data;
62468c2ecf20Sopenharmony_ci		pos = 8 - ffs(cuir->ned_map);
62478c2ecf20Sopenharmony_ci		ned = (char *) &conf_data->neds[pos];
62488c2ecf20Sopenharmony_ci		/* compare reference ned and per path ned */
62498c2ecf20Sopenharmony_ci		if (memcmp(ref_ned, ned, sizeof(*ned)) != 0)
62508c2ecf20Sopenharmony_ci			continue;
62518c2ecf20Sopenharmony_ci		gneq = (char *)&conf_data->gneq;
62528c2ecf20Sopenharmony_ci		/* compare reference gneq and per_path gneq under
62538c2ecf20Sopenharmony_ci		   24 bit mask where mask bit 0 equals byte 7 of
62548c2ecf20Sopenharmony_ci		   the gneq and mask bit 24 equals byte 31 */
62558c2ecf20Sopenharmony_ci		while (bitmask) {
62568c2ecf20Sopenharmony_ci			pos = ffs(bitmask) - 1;
62578c2ecf20Sopenharmony_ci			if (memcmp(&ref_gneq[31 - pos], &gneq[31 - pos], 1)
62588c2ecf20Sopenharmony_ci			    != 0)
62598c2ecf20Sopenharmony_ci				break;
62608c2ecf20Sopenharmony_ci			clear_bit(pos, &bitmask);
62618c2ecf20Sopenharmony_ci		}
62628c2ecf20Sopenharmony_ci		if (bitmask)
62638c2ecf20Sopenharmony_ci			continue;
62648c2ecf20Sopenharmony_ci		/* device and path match the reference values
62658c2ecf20Sopenharmony_ci		   add path to CUIR scope */
62668c2ecf20Sopenharmony_ci		tbcpm |= 0x80 >> path;
62678c2ecf20Sopenharmony_ci	}
62688c2ecf20Sopenharmony_ci	return tbcpm;
62698c2ecf20Sopenharmony_ci}
62708c2ecf20Sopenharmony_ci
62718c2ecf20Sopenharmony_cistatic void dasd_eckd_cuir_notify_user(struct dasd_device *device,
62728c2ecf20Sopenharmony_ci				       unsigned long paths, int action)
62738c2ecf20Sopenharmony_ci{
62748c2ecf20Sopenharmony_ci	int pos;
62758c2ecf20Sopenharmony_ci
62768c2ecf20Sopenharmony_ci	while (paths) {
62778c2ecf20Sopenharmony_ci		/* get position of bit in mask */
62788c2ecf20Sopenharmony_ci		pos = 8 - ffs(paths);
62798c2ecf20Sopenharmony_ci		/* get channel path descriptor from this position */
62808c2ecf20Sopenharmony_ci		if (action == CUIR_QUIESCE)
62818c2ecf20Sopenharmony_ci			pr_warn("Service on the storage server caused path %x.%02x to go offline",
62828c2ecf20Sopenharmony_ci				device->path[pos].cssid,
62838c2ecf20Sopenharmony_ci				device->path[pos].chpid);
62848c2ecf20Sopenharmony_ci		else if (action == CUIR_RESUME)
62858c2ecf20Sopenharmony_ci			pr_info("Path %x.%02x is back online after service on the storage server",
62868c2ecf20Sopenharmony_ci				device->path[pos].cssid,
62878c2ecf20Sopenharmony_ci				device->path[pos].chpid);
62888c2ecf20Sopenharmony_ci		clear_bit(7 - pos, &paths);
62898c2ecf20Sopenharmony_ci	}
62908c2ecf20Sopenharmony_ci}
62918c2ecf20Sopenharmony_ci
62928c2ecf20Sopenharmony_cistatic int dasd_eckd_cuir_remove_path(struct dasd_device *device, __u8 lpum,
62938c2ecf20Sopenharmony_ci				      struct dasd_cuir_message *cuir)
62948c2ecf20Sopenharmony_ci{
62958c2ecf20Sopenharmony_ci	unsigned long tbcpm;
62968c2ecf20Sopenharmony_ci
62978c2ecf20Sopenharmony_ci	tbcpm = dasd_eckd_cuir_scope(device, lpum, cuir);
62988c2ecf20Sopenharmony_ci	/* nothing to do if path is not in use */
62998c2ecf20Sopenharmony_ci	if (!(dasd_path_get_opm(device) & tbcpm))
63008c2ecf20Sopenharmony_ci		return 0;
63018c2ecf20Sopenharmony_ci	if (!(dasd_path_get_opm(device) & ~tbcpm)) {
63028c2ecf20Sopenharmony_ci		/* no path would be left if the CUIR action is taken
63038c2ecf20Sopenharmony_ci		   return error */
63048c2ecf20Sopenharmony_ci		return -EINVAL;
63058c2ecf20Sopenharmony_ci	}
63068c2ecf20Sopenharmony_ci	/* remove device from operational path mask */
63078c2ecf20Sopenharmony_ci	dasd_path_remove_opm(device, tbcpm);
63088c2ecf20Sopenharmony_ci	dasd_path_add_cuirpm(device, tbcpm);
63098c2ecf20Sopenharmony_ci	return tbcpm;
63108c2ecf20Sopenharmony_ci}
63118c2ecf20Sopenharmony_ci
63128c2ecf20Sopenharmony_ci/*
63138c2ecf20Sopenharmony_ci * walk through all devices and build a path mask to quiesce them
63148c2ecf20Sopenharmony_ci * return an error if the last path to a device would be removed
63158c2ecf20Sopenharmony_ci *
63168c2ecf20Sopenharmony_ci * if only part of the devices are quiesced and an error
63178c2ecf20Sopenharmony_ci * occurs no onlining necessary, the storage server will
63188c2ecf20Sopenharmony_ci * notify the already set offline devices again
63198c2ecf20Sopenharmony_ci */
63208c2ecf20Sopenharmony_cistatic int dasd_eckd_cuir_quiesce(struct dasd_device *device, __u8 lpum,
63218c2ecf20Sopenharmony_ci				  struct dasd_cuir_message *cuir)
63228c2ecf20Sopenharmony_ci{
63238c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
63248c2ecf20Sopenharmony_ci	struct alias_pav_group *pavgroup, *tempgroup;
63258c2ecf20Sopenharmony_ci	struct dasd_device *dev, *n;
63268c2ecf20Sopenharmony_ci	unsigned long paths = 0;
63278c2ecf20Sopenharmony_ci	unsigned long flags;
63288c2ecf20Sopenharmony_ci	int tbcpm;
63298c2ecf20Sopenharmony_ci
63308c2ecf20Sopenharmony_ci	/* active devices */
63318c2ecf20Sopenharmony_ci	list_for_each_entry_safe(dev, n, &private->lcu->active_devices,
63328c2ecf20Sopenharmony_ci				 alias_list) {
63338c2ecf20Sopenharmony_ci		spin_lock_irqsave(get_ccwdev_lock(dev->cdev), flags);
63348c2ecf20Sopenharmony_ci		tbcpm = dasd_eckd_cuir_remove_path(dev, lpum, cuir);
63358c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(get_ccwdev_lock(dev->cdev), flags);
63368c2ecf20Sopenharmony_ci		if (tbcpm < 0)
63378c2ecf20Sopenharmony_ci			goto out_err;
63388c2ecf20Sopenharmony_ci		paths |= tbcpm;
63398c2ecf20Sopenharmony_ci	}
63408c2ecf20Sopenharmony_ci	/* inactive devices */
63418c2ecf20Sopenharmony_ci	list_for_each_entry_safe(dev, n, &private->lcu->inactive_devices,
63428c2ecf20Sopenharmony_ci				 alias_list) {
63438c2ecf20Sopenharmony_ci		spin_lock_irqsave(get_ccwdev_lock(dev->cdev), flags);
63448c2ecf20Sopenharmony_ci		tbcpm = dasd_eckd_cuir_remove_path(dev, lpum, cuir);
63458c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(get_ccwdev_lock(dev->cdev), flags);
63468c2ecf20Sopenharmony_ci		if (tbcpm < 0)
63478c2ecf20Sopenharmony_ci			goto out_err;
63488c2ecf20Sopenharmony_ci		paths |= tbcpm;
63498c2ecf20Sopenharmony_ci	}
63508c2ecf20Sopenharmony_ci	/* devices in PAV groups */
63518c2ecf20Sopenharmony_ci	list_for_each_entry_safe(pavgroup, tempgroup,
63528c2ecf20Sopenharmony_ci				 &private->lcu->grouplist, group) {
63538c2ecf20Sopenharmony_ci		list_for_each_entry_safe(dev, n, &pavgroup->baselist,
63548c2ecf20Sopenharmony_ci					 alias_list) {
63558c2ecf20Sopenharmony_ci			spin_lock_irqsave(get_ccwdev_lock(dev->cdev), flags);
63568c2ecf20Sopenharmony_ci			tbcpm = dasd_eckd_cuir_remove_path(dev, lpum, cuir);
63578c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(
63588c2ecf20Sopenharmony_ci				get_ccwdev_lock(dev->cdev), flags);
63598c2ecf20Sopenharmony_ci			if (tbcpm < 0)
63608c2ecf20Sopenharmony_ci				goto out_err;
63618c2ecf20Sopenharmony_ci			paths |= tbcpm;
63628c2ecf20Sopenharmony_ci		}
63638c2ecf20Sopenharmony_ci		list_for_each_entry_safe(dev, n, &pavgroup->aliaslist,
63648c2ecf20Sopenharmony_ci					 alias_list) {
63658c2ecf20Sopenharmony_ci			spin_lock_irqsave(get_ccwdev_lock(dev->cdev), flags);
63668c2ecf20Sopenharmony_ci			tbcpm = dasd_eckd_cuir_remove_path(dev, lpum, cuir);
63678c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(
63688c2ecf20Sopenharmony_ci				get_ccwdev_lock(dev->cdev), flags);
63698c2ecf20Sopenharmony_ci			if (tbcpm < 0)
63708c2ecf20Sopenharmony_ci				goto out_err;
63718c2ecf20Sopenharmony_ci			paths |= tbcpm;
63728c2ecf20Sopenharmony_ci		}
63738c2ecf20Sopenharmony_ci	}
63748c2ecf20Sopenharmony_ci	/* notify user about all paths affected by CUIR action */
63758c2ecf20Sopenharmony_ci	dasd_eckd_cuir_notify_user(device, paths, CUIR_QUIESCE);
63768c2ecf20Sopenharmony_ci	return 0;
63778c2ecf20Sopenharmony_ciout_err:
63788c2ecf20Sopenharmony_ci	return tbcpm;
63798c2ecf20Sopenharmony_ci}
63808c2ecf20Sopenharmony_ci
63818c2ecf20Sopenharmony_cistatic int dasd_eckd_cuir_resume(struct dasd_device *device, __u8 lpum,
63828c2ecf20Sopenharmony_ci				 struct dasd_cuir_message *cuir)
63838c2ecf20Sopenharmony_ci{
63848c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
63858c2ecf20Sopenharmony_ci	struct alias_pav_group *pavgroup, *tempgroup;
63868c2ecf20Sopenharmony_ci	struct dasd_device *dev, *n;
63878c2ecf20Sopenharmony_ci	unsigned long paths = 0;
63888c2ecf20Sopenharmony_ci	int tbcpm;
63898c2ecf20Sopenharmony_ci
63908c2ecf20Sopenharmony_ci	/*
63918c2ecf20Sopenharmony_ci	 * the path may have been added through a generic path event before
63928c2ecf20Sopenharmony_ci	 * only trigger path verification if the path is not already in use
63938c2ecf20Sopenharmony_ci	 */
63948c2ecf20Sopenharmony_ci	list_for_each_entry_safe(dev, n,
63958c2ecf20Sopenharmony_ci				 &private->lcu->active_devices,
63968c2ecf20Sopenharmony_ci				 alias_list) {
63978c2ecf20Sopenharmony_ci		tbcpm = dasd_eckd_cuir_scope(dev, lpum, cuir);
63988c2ecf20Sopenharmony_ci		paths |= tbcpm;
63998c2ecf20Sopenharmony_ci		if (!(dasd_path_get_opm(dev) & tbcpm)) {
64008c2ecf20Sopenharmony_ci			dasd_path_add_tbvpm(dev, tbcpm);
64018c2ecf20Sopenharmony_ci			dasd_schedule_device_bh(dev);
64028c2ecf20Sopenharmony_ci		}
64038c2ecf20Sopenharmony_ci	}
64048c2ecf20Sopenharmony_ci	list_for_each_entry_safe(dev, n,
64058c2ecf20Sopenharmony_ci				 &private->lcu->inactive_devices,
64068c2ecf20Sopenharmony_ci				 alias_list) {
64078c2ecf20Sopenharmony_ci		tbcpm = dasd_eckd_cuir_scope(dev, lpum, cuir);
64088c2ecf20Sopenharmony_ci		paths |= tbcpm;
64098c2ecf20Sopenharmony_ci		if (!(dasd_path_get_opm(dev) & tbcpm)) {
64108c2ecf20Sopenharmony_ci			dasd_path_add_tbvpm(dev, tbcpm);
64118c2ecf20Sopenharmony_ci			dasd_schedule_device_bh(dev);
64128c2ecf20Sopenharmony_ci		}
64138c2ecf20Sopenharmony_ci	}
64148c2ecf20Sopenharmony_ci	/* devices in PAV groups */
64158c2ecf20Sopenharmony_ci	list_for_each_entry_safe(pavgroup, tempgroup,
64168c2ecf20Sopenharmony_ci				 &private->lcu->grouplist,
64178c2ecf20Sopenharmony_ci				 group) {
64188c2ecf20Sopenharmony_ci		list_for_each_entry_safe(dev, n,
64198c2ecf20Sopenharmony_ci					 &pavgroup->baselist,
64208c2ecf20Sopenharmony_ci					 alias_list) {
64218c2ecf20Sopenharmony_ci			tbcpm = dasd_eckd_cuir_scope(dev, lpum, cuir);
64228c2ecf20Sopenharmony_ci			paths |= tbcpm;
64238c2ecf20Sopenharmony_ci			if (!(dasd_path_get_opm(dev) & tbcpm)) {
64248c2ecf20Sopenharmony_ci				dasd_path_add_tbvpm(dev, tbcpm);
64258c2ecf20Sopenharmony_ci				dasd_schedule_device_bh(dev);
64268c2ecf20Sopenharmony_ci			}
64278c2ecf20Sopenharmony_ci		}
64288c2ecf20Sopenharmony_ci		list_for_each_entry_safe(dev, n,
64298c2ecf20Sopenharmony_ci					 &pavgroup->aliaslist,
64308c2ecf20Sopenharmony_ci					 alias_list) {
64318c2ecf20Sopenharmony_ci			tbcpm = dasd_eckd_cuir_scope(dev, lpum, cuir);
64328c2ecf20Sopenharmony_ci			paths |= tbcpm;
64338c2ecf20Sopenharmony_ci			if (!(dasd_path_get_opm(dev) & tbcpm)) {
64348c2ecf20Sopenharmony_ci				dasd_path_add_tbvpm(dev, tbcpm);
64358c2ecf20Sopenharmony_ci				dasd_schedule_device_bh(dev);
64368c2ecf20Sopenharmony_ci			}
64378c2ecf20Sopenharmony_ci		}
64388c2ecf20Sopenharmony_ci	}
64398c2ecf20Sopenharmony_ci	/* notify user about all paths affected by CUIR action */
64408c2ecf20Sopenharmony_ci	dasd_eckd_cuir_notify_user(device, paths, CUIR_RESUME);
64418c2ecf20Sopenharmony_ci	return 0;
64428c2ecf20Sopenharmony_ci}
64438c2ecf20Sopenharmony_ci
64448c2ecf20Sopenharmony_cistatic void dasd_eckd_handle_cuir(struct dasd_device *device, void *messages,
64458c2ecf20Sopenharmony_ci				 __u8 lpum)
64468c2ecf20Sopenharmony_ci{
64478c2ecf20Sopenharmony_ci	struct dasd_cuir_message *cuir = messages;
64488c2ecf20Sopenharmony_ci	int response;
64498c2ecf20Sopenharmony_ci
64508c2ecf20Sopenharmony_ci	DBF_DEV_EVENT(DBF_WARNING, device,
64518c2ecf20Sopenharmony_ci		      "CUIR request: %016llx %016llx %016llx %08x",
64528c2ecf20Sopenharmony_ci		      ((u64 *)cuir)[0], ((u64 *)cuir)[1], ((u64 *)cuir)[2],
64538c2ecf20Sopenharmony_ci		      ((u32 *)cuir)[3]);
64548c2ecf20Sopenharmony_ci
64558c2ecf20Sopenharmony_ci	if (cuir->code == CUIR_QUIESCE) {
64568c2ecf20Sopenharmony_ci		/* quiesce */
64578c2ecf20Sopenharmony_ci		if (dasd_eckd_cuir_quiesce(device, lpum, cuir))
64588c2ecf20Sopenharmony_ci			response = PSF_CUIR_LAST_PATH;
64598c2ecf20Sopenharmony_ci		else
64608c2ecf20Sopenharmony_ci			response = PSF_CUIR_COMPLETED;
64618c2ecf20Sopenharmony_ci	} else if (cuir->code == CUIR_RESUME) {
64628c2ecf20Sopenharmony_ci		/* resume */
64638c2ecf20Sopenharmony_ci		dasd_eckd_cuir_resume(device, lpum, cuir);
64648c2ecf20Sopenharmony_ci		response = PSF_CUIR_COMPLETED;
64658c2ecf20Sopenharmony_ci	} else
64668c2ecf20Sopenharmony_ci		response = PSF_CUIR_NOT_SUPPORTED;
64678c2ecf20Sopenharmony_ci
64688c2ecf20Sopenharmony_ci	dasd_eckd_psf_cuir_response(device, response,
64698c2ecf20Sopenharmony_ci				    cuir->message_id, lpum);
64708c2ecf20Sopenharmony_ci	DBF_DEV_EVENT(DBF_WARNING, device,
64718c2ecf20Sopenharmony_ci		      "CUIR response: %d on message ID %08x", response,
64728c2ecf20Sopenharmony_ci		      cuir->message_id);
64738c2ecf20Sopenharmony_ci	/* to make sure there is no attention left schedule work again */
64748c2ecf20Sopenharmony_ci	device->discipline->check_attention(device, lpum);
64758c2ecf20Sopenharmony_ci}
64768c2ecf20Sopenharmony_ci
64778c2ecf20Sopenharmony_cistatic void dasd_eckd_oos_resume(struct dasd_device *device)
64788c2ecf20Sopenharmony_ci{
64798c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
64808c2ecf20Sopenharmony_ci	struct alias_pav_group *pavgroup, *tempgroup;
64818c2ecf20Sopenharmony_ci	struct dasd_device *dev, *n;
64828c2ecf20Sopenharmony_ci	unsigned long flags;
64838c2ecf20Sopenharmony_ci
64848c2ecf20Sopenharmony_ci	spin_lock_irqsave(&private->lcu->lock, flags);
64858c2ecf20Sopenharmony_ci	list_for_each_entry_safe(dev, n, &private->lcu->active_devices,
64868c2ecf20Sopenharmony_ci				 alias_list) {
64878c2ecf20Sopenharmony_ci		if (dev->stopped & DASD_STOPPED_NOSPC)
64888c2ecf20Sopenharmony_ci			dasd_generic_space_avail(dev);
64898c2ecf20Sopenharmony_ci	}
64908c2ecf20Sopenharmony_ci	list_for_each_entry_safe(dev, n, &private->lcu->inactive_devices,
64918c2ecf20Sopenharmony_ci				 alias_list) {
64928c2ecf20Sopenharmony_ci		if (dev->stopped & DASD_STOPPED_NOSPC)
64938c2ecf20Sopenharmony_ci			dasd_generic_space_avail(dev);
64948c2ecf20Sopenharmony_ci	}
64958c2ecf20Sopenharmony_ci	/* devices in PAV groups */
64968c2ecf20Sopenharmony_ci	list_for_each_entry_safe(pavgroup, tempgroup,
64978c2ecf20Sopenharmony_ci				 &private->lcu->grouplist,
64988c2ecf20Sopenharmony_ci				 group) {
64998c2ecf20Sopenharmony_ci		list_for_each_entry_safe(dev, n, &pavgroup->baselist,
65008c2ecf20Sopenharmony_ci					 alias_list) {
65018c2ecf20Sopenharmony_ci			if (dev->stopped & DASD_STOPPED_NOSPC)
65028c2ecf20Sopenharmony_ci				dasd_generic_space_avail(dev);
65038c2ecf20Sopenharmony_ci		}
65048c2ecf20Sopenharmony_ci		list_for_each_entry_safe(dev, n, &pavgroup->aliaslist,
65058c2ecf20Sopenharmony_ci					 alias_list) {
65068c2ecf20Sopenharmony_ci			if (dev->stopped & DASD_STOPPED_NOSPC)
65078c2ecf20Sopenharmony_ci				dasd_generic_space_avail(dev);
65088c2ecf20Sopenharmony_ci		}
65098c2ecf20Sopenharmony_ci	}
65108c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&private->lcu->lock, flags);
65118c2ecf20Sopenharmony_ci}
65128c2ecf20Sopenharmony_ci
65138c2ecf20Sopenharmony_cistatic void dasd_eckd_handle_oos(struct dasd_device *device, void *messages,
65148c2ecf20Sopenharmony_ci				 __u8 lpum)
65158c2ecf20Sopenharmony_ci{
65168c2ecf20Sopenharmony_ci	struct dasd_oos_message *oos = messages;
65178c2ecf20Sopenharmony_ci
65188c2ecf20Sopenharmony_ci	switch (oos->code) {
65198c2ecf20Sopenharmony_ci	case REPO_WARN:
65208c2ecf20Sopenharmony_ci	case POOL_WARN:
65218c2ecf20Sopenharmony_ci		dev_warn(&device->cdev->dev,
65228c2ecf20Sopenharmony_ci			 "Extent pool usage has reached a critical value\n");
65238c2ecf20Sopenharmony_ci		dasd_eckd_oos_resume(device);
65248c2ecf20Sopenharmony_ci		break;
65258c2ecf20Sopenharmony_ci	case REPO_EXHAUST:
65268c2ecf20Sopenharmony_ci	case POOL_EXHAUST:
65278c2ecf20Sopenharmony_ci		dev_warn(&device->cdev->dev,
65288c2ecf20Sopenharmony_ci			 "Extent pool is exhausted\n");
65298c2ecf20Sopenharmony_ci		break;
65308c2ecf20Sopenharmony_ci	case REPO_RELIEVE:
65318c2ecf20Sopenharmony_ci	case POOL_RELIEVE:
65328c2ecf20Sopenharmony_ci		dev_info(&device->cdev->dev,
65338c2ecf20Sopenharmony_ci			 "Extent pool physical space constraint has been relieved\n");
65348c2ecf20Sopenharmony_ci		break;
65358c2ecf20Sopenharmony_ci	}
65368c2ecf20Sopenharmony_ci
65378c2ecf20Sopenharmony_ci	/* In any case, update related data */
65388c2ecf20Sopenharmony_ci	dasd_eckd_read_ext_pool_info(device);
65398c2ecf20Sopenharmony_ci
65408c2ecf20Sopenharmony_ci	/* to make sure there is no attention left schedule work again */
65418c2ecf20Sopenharmony_ci	device->discipline->check_attention(device, lpum);
65428c2ecf20Sopenharmony_ci}
65438c2ecf20Sopenharmony_ci
65448c2ecf20Sopenharmony_cistatic void dasd_eckd_check_attention_work(struct work_struct *work)
65458c2ecf20Sopenharmony_ci{
65468c2ecf20Sopenharmony_ci	struct check_attention_work_data *data;
65478c2ecf20Sopenharmony_ci	struct dasd_rssd_messages *messages;
65488c2ecf20Sopenharmony_ci	struct dasd_device *device;
65498c2ecf20Sopenharmony_ci	int rc;
65508c2ecf20Sopenharmony_ci
65518c2ecf20Sopenharmony_ci	data = container_of(work, struct check_attention_work_data, worker);
65528c2ecf20Sopenharmony_ci	device = data->device;
65538c2ecf20Sopenharmony_ci	messages = kzalloc(sizeof(*messages), GFP_KERNEL);
65548c2ecf20Sopenharmony_ci	if (!messages) {
65558c2ecf20Sopenharmony_ci		DBF_DEV_EVENT(DBF_WARNING, device, "%s",
65568c2ecf20Sopenharmony_ci			      "Could not allocate attention message buffer");
65578c2ecf20Sopenharmony_ci		goto out;
65588c2ecf20Sopenharmony_ci	}
65598c2ecf20Sopenharmony_ci	rc = dasd_eckd_read_message_buffer(device, messages, data->lpum);
65608c2ecf20Sopenharmony_ci	if (rc)
65618c2ecf20Sopenharmony_ci		goto out;
65628c2ecf20Sopenharmony_ci
65638c2ecf20Sopenharmony_ci	if (messages->length == ATTENTION_LENGTH_CUIR &&
65648c2ecf20Sopenharmony_ci	    messages->format == ATTENTION_FORMAT_CUIR)
65658c2ecf20Sopenharmony_ci		dasd_eckd_handle_cuir(device, messages, data->lpum);
65668c2ecf20Sopenharmony_ci	if (messages->length == ATTENTION_LENGTH_OOS &&
65678c2ecf20Sopenharmony_ci	    messages->format == ATTENTION_FORMAT_OOS)
65688c2ecf20Sopenharmony_ci		dasd_eckd_handle_oos(device, messages, data->lpum);
65698c2ecf20Sopenharmony_ci
65708c2ecf20Sopenharmony_ciout:
65718c2ecf20Sopenharmony_ci	dasd_put_device(device);
65728c2ecf20Sopenharmony_ci	kfree(messages);
65738c2ecf20Sopenharmony_ci	kfree(data);
65748c2ecf20Sopenharmony_ci}
65758c2ecf20Sopenharmony_ci
65768c2ecf20Sopenharmony_cistatic int dasd_eckd_check_attention(struct dasd_device *device, __u8 lpum)
65778c2ecf20Sopenharmony_ci{
65788c2ecf20Sopenharmony_ci	struct check_attention_work_data *data;
65798c2ecf20Sopenharmony_ci
65808c2ecf20Sopenharmony_ci	data = kzalloc(sizeof(*data), GFP_ATOMIC);
65818c2ecf20Sopenharmony_ci	if (!data)
65828c2ecf20Sopenharmony_ci		return -ENOMEM;
65838c2ecf20Sopenharmony_ci	INIT_WORK(&data->worker, dasd_eckd_check_attention_work);
65848c2ecf20Sopenharmony_ci	dasd_get_device(device);
65858c2ecf20Sopenharmony_ci	data->device = device;
65868c2ecf20Sopenharmony_ci	data->lpum = lpum;
65878c2ecf20Sopenharmony_ci	schedule_work(&data->worker);
65888c2ecf20Sopenharmony_ci	return 0;
65898c2ecf20Sopenharmony_ci}
65908c2ecf20Sopenharmony_ci
65918c2ecf20Sopenharmony_cistatic int dasd_eckd_disable_hpf_path(struct dasd_device *device, __u8 lpum)
65928c2ecf20Sopenharmony_ci{
65938c2ecf20Sopenharmony_ci	if (~lpum & dasd_path_get_opm(device)) {
65948c2ecf20Sopenharmony_ci		dasd_path_add_nohpfpm(device, lpum);
65958c2ecf20Sopenharmony_ci		dasd_path_remove_opm(device, lpum);
65968c2ecf20Sopenharmony_ci		dev_err(&device->cdev->dev,
65978c2ecf20Sopenharmony_ci			"Channel path %02X lost HPF functionality and is disabled\n",
65988c2ecf20Sopenharmony_ci			lpum);
65998c2ecf20Sopenharmony_ci		return 1;
66008c2ecf20Sopenharmony_ci	}
66018c2ecf20Sopenharmony_ci	return 0;
66028c2ecf20Sopenharmony_ci}
66038c2ecf20Sopenharmony_ci
66048c2ecf20Sopenharmony_cistatic void dasd_eckd_disable_hpf_device(struct dasd_device *device)
66058c2ecf20Sopenharmony_ci{
66068c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
66078c2ecf20Sopenharmony_ci
66088c2ecf20Sopenharmony_ci	dev_err(&device->cdev->dev,
66098c2ecf20Sopenharmony_ci		"High Performance FICON disabled\n");
66108c2ecf20Sopenharmony_ci	private->fcx_max_data = 0;
66118c2ecf20Sopenharmony_ci}
66128c2ecf20Sopenharmony_ci
66138c2ecf20Sopenharmony_cistatic int dasd_eckd_hpf_enabled(struct dasd_device *device)
66148c2ecf20Sopenharmony_ci{
66158c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
66168c2ecf20Sopenharmony_ci
66178c2ecf20Sopenharmony_ci	return private->fcx_max_data ? 1 : 0;
66188c2ecf20Sopenharmony_ci}
66198c2ecf20Sopenharmony_ci
66208c2ecf20Sopenharmony_cistatic void dasd_eckd_handle_hpf_error(struct dasd_device *device,
66218c2ecf20Sopenharmony_ci				       struct irb *irb)
66228c2ecf20Sopenharmony_ci{
66238c2ecf20Sopenharmony_ci	struct dasd_eckd_private *private = device->private;
66248c2ecf20Sopenharmony_ci
66258c2ecf20Sopenharmony_ci	if (!private->fcx_max_data) {
66268c2ecf20Sopenharmony_ci		/* sanity check for no HPF, the error makes no sense */
66278c2ecf20Sopenharmony_ci		DBF_DEV_EVENT(DBF_WARNING, device, "%s",
66288c2ecf20Sopenharmony_ci			      "Trying to disable HPF for a non HPF device");
66298c2ecf20Sopenharmony_ci		return;
66308c2ecf20Sopenharmony_ci	}
66318c2ecf20Sopenharmony_ci	if (irb->scsw.tm.sesq == SCSW_SESQ_DEV_NOFCX) {
66328c2ecf20Sopenharmony_ci		dasd_eckd_disable_hpf_device(device);
66338c2ecf20Sopenharmony_ci	} else if (irb->scsw.tm.sesq == SCSW_SESQ_PATH_NOFCX) {
66348c2ecf20Sopenharmony_ci		if (dasd_eckd_disable_hpf_path(device, irb->esw.esw1.lpum))
66358c2ecf20Sopenharmony_ci			return;
66368c2ecf20Sopenharmony_ci		dasd_eckd_disable_hpf_device(device);
66378c2ecf20Sopenharmony_ci		dasd_path_set_tbvpm(device,
66388c2ecf20Sopenharmony_ci				  dasd_path_get_hpfpm(device));
66398c2ecf20Sopenharmony_ci	}
66408c2ecf20Sopenharmony_ci	/*
66418c2ecf20Sopenharmony_ci	 * prevent that any new I/O ist started on the device and schedule a
66428c2ecf20Sopenharmony_ci	 * requeue of existing requests
66438c2ecf20Sopenharmony_ci	 */
66448c2ecf20Sopenharmony_ci	dasd_device_set_stop_bits(device, DASD_STOPPED_NOT_ACC);
66458c2ecf20Sopenharmony_ci	dasd_schedule_requeue(device);
66468c2ecf20Sopenharmony_ci}
66478c2ecf20Sopenharmony_ci
66488c2ecf20Sopenharmony_ci/*
66498c2ecf20Sopenharmony_ci * Initialize block layer request queue.
66508c2ecf20Sopenharmony_ci */
66518c2ecf20Sopenharmony_cistatic void dasd_eckd_setup_blk_queue(struct dasd_block *block)
66528c2ecf20Sopenharmony_ci{
66538c2ecf20Sopenharmony_ci	unsigned int logical_block_size = block->bp_block;
66548c2ecf20Sopenharmony_ci	struct request_queue *q = block->request_queue;
66558c2ecf20Sopenharmony_ci	struct dasd_device *device = block->base;
66568c2ecf20Sopenharmony_ci	int max;
66578c2ecf20Sopenharmony_ci
66588c2ecf20Sopenharmony_ci	if (device->features & DASD_FEATURE_USERAW) {
66598c2ecf20Sopenharmony_ci		/*
66608c2ecf20Sopenharmony_ci		 * the max_blocks value for raw_track access is 256
66618c2ecf20Sopenharmony_ci		 * it is higher than the native ECKD value because we
66628c2ecf20Sopenharmony_ci		 * only need one ccw per track
66638c2ecf20Sopenharmony_ci		 * so the max_hw_sectors are
66648c2ecf20Sopenharmony_ci		 * 2048 x 512B = 1024kB = 16 tracks
66658c2ecf20Sopenharmony_ci		 */
66668c2ecf20Sopenharmony_ci		max = DASD_ECKD_MAX_BLOCKS_RAW << block->s2b_shift;
66678c2ecf20Sopenharmony_ci	} else {
66688c2ecf20Sopenharmony_ci		max = DASD_ECKD_MAX_BLOCKS << block->s2b_shift;
66698c2ecf20Sopenharmony_ci	}
66708c2ecf20Sopenharmony_ci	blk_queue_flag_set(QUEUE_FLAG_NONROT, q);
66718c2ecf20Sopenharmony_ci	q->limits.max_dev_sectors = max;
66728c2ecf20Sopenharmony_ci	blk_queue_logical_block_size(q, logical_block_size);
66738c2ecf20Sopenharmony_ci	blk_queue_max_hw_sectors(q, max);
66748c2ecf20Sopenharmony_ci	blk_queue_max_segments(q, USHRT_MAX);
66758c2ecf20Sopenharmony_ci	/* With page sized segments each segment can be translated into one idaw/tidaw */
66768c2ecf20Sopenharmony_ci	blk_queue_max_segment_size(q, PAGE_SIZE);
66778c2ecf20Sopenharmony_ci	blk_queue_segment_boundary(q, PAGE_SIZE - 1);
66788c2ecf20Sopenharmony_ci}
66798c2ecf20Sopenharmony_ci
66808c2ecf20Sopenharmony_cistatic struct ccw_driver dasd_eckd_driver = {
66818c2ecf20Sopenharmony_ci	.driver = {
66828c2ecf20Sopenharmony_ci		.name	= "dasd-eckd",
66838c2ecf20Sopenharmony_ci		.owner	= THIS_MODULE,
66848c2ecf20Sopenharmony_ci	},
66858c2ecf20Sopenharmony_ci	.ids	     = dasd_eckd_ids,
66868c2ecf20Sopenharmony_ci	.probe	     = dasd_eckd_probe,
66878c2ecf20Sopenharmony_ci	.remove      = dasd_generic_remove,
66888c2ecf20Sopenharmony_ci	.set_offline = dasd_generic_set_offline,
66898c2ecf20Sopenharmony_ci	.set_online  = dasd_eckd_set_online,
66908c2ecf20Sopenharmony_ci	.notify      = dasd_generic_notify,
66918c2ecf20Sopenharmony_ci	.path_event  = dasd_generic_path_event,
66928c2ecf20Sopenharmony_ci	.shutdown    = dasd_generic_shutdown,
66938c2ecf20Sopenharmony_ci	.freeze      = dasd_generic_pm_freeze,
66948c2ecf20Sopenharmony_ci	.thaw	     = dasd_generic_restore_device,
66958c2ecf20Sopenharmony_ci	.restore     = dasd_generic_restore_device,
66968c2ecf20Sopenharmony_ci	.uc_handler  = dasd_generic_uc_handler,
66978c2ecf20Sopenharmony_ci	.int_class   = IRQIO_DAS,
66988c2ecf20Sopenharmony_ci};
66998c2ecf20Sopenharmony_ci
67008c2ecf20Sopenharmony_cistatic struct dasd_discipline dasd_eckd_discipline = {
67018c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
67028c2ecf20Sopenharmony_ci	.name = "ECKD",
67038c2ecf20Sopenharmony_ci	.ebcname = "ECKD",
67048c2ecf20Sopenharmony_ci	.check_device = dasd_eckd_check_characteristics,
67058c2ecf20Sopenharmony_ci	.uncheck_device = dasd_eckd_uncheck_device,
67068c2ecf20Sopenharmony_ci	.do_analysis = dasd_eckd_do_analysis,
67078c2ecf20Sopenharmony_ci	.pe_handler = dasd_eckd_pe_handler,
67088c2ecf20Sopenharmony_ci	.basic_to_ready = dasd_eckd_basic_to_ready,
67098c2ecf20Sopenharmony_ci	.online_to_ready = dasd_eckd_online_to_ready,
67108c2ecf20Sopenharmony_ci	.basic_to_known = dasd_eckd_basic_to_known,
67118c2ecf20Sopenharmony_ci	.setup_blk_queue = dasd_eckd_setup_blk_queue,
67128c2ecf20Sopenharmony_ci	.fill_geometry = dasd_eckd_fill_geometry,
67138c2ecf20Sopenharmony_ci	.start_IO = dasd_start_IO,
67148c2ecf20Sopenharmony_ci	.term_IO = dasd_term_IO,
67158c2ecf20Sopenharmony_ci	.handle_terminated_request = dasd_eckd_handle_terminated_request,
67168c2ecf20Sopenharmony_ci	.format_device = dasd_eckd_format_device,
67178c2ecf20Sopenharmony_ci	.check_device_format = dasd_eckd_check_device_format,
67188c2ecf20Sopenharmony_ci	.erp_action = dasd_eckd_erp_action,
67198c2ecf20Sopenharmony_ci	.erp_postaction = dasd_eckd_erp_postaction,
67208c2ecf20Sopenharmony_ci	.check_for_device_change = dasd_eckd_check_for_device_change,
67218c2ecf20Sopenharmony_ci	.build_cp = dasd_eckd_build_alias_cp,
67228c2ecf20Sopenharmony_ci	.free_cp = dasd_eckd_free_alias_cp,
67238c2ecf20Sopenharmony_ci	.dump_sense = dasd_eckd_dump_sense,
67248c2ecf20Sopenharmony_ci	.dump_sense_dbf = dasd_eckd_dump_sense_dbf,
67258c2ecf20Sopenharmony_ci	.fill_info = dasd_eckd_fill_info,
67268c2ecf20Sopenharmony_ci	.ioctl = dasd_eckd_ioctl,
67278c2ecf20Sopenharmony_ci	.freeze = dasd_eckd_pm_freeze,
67288c2ecf20Sopenharmony_ci	.restore = dasd_eckd_restore_device,
67298c2ecf20Sopenharmony_ci	.reload = dasd_eckd_reload_device,
67308c2ecf20Sopenharmony_ci	.get_uid = dasd_eckd_get_uid,
67318c2ecf20Sopenharmony_ci	.kick_validate = dasd_eckd_kick_validate_server,
67328c2ecf20Sopenharmony_ci	.check_attention = dasd_eckd_check_attention,
67338c2ecf20Sopenharmony_ci	.host_access_count = dasd_eckd_host_access_count,
67348c2ecf20Sopenharmony_ci	.hosts_print = dasd_hosts_print,
67358c2ecf20Sopenharmony_ci	.handle_hpf_error = dasd_eckd_handle_hpf_error,
67368c2ecf20Sopenharmony_ci	.disable_hpf = dasd_eckd_disable_hpf_device,
67378c2ecf20Sopenharmony_ci	.hpf_enabled = dasd_eckd_hpf_enabled,
67388c2ecf20Sopenharmony_ci	.reset_path = dasd_eckd_reset_path,
67398c2ecf20Sopenharmony_ci	.is_ese = dasd_eckd_is_ese,
67408c2ecf20Sopenharmony_ci	.space_allocated = dasd_eckd_space_allocated,
67418c2ecf20Sopenharmony_ci	.space_configured = dasd_eckd_space_configured,
67428c2ecf20Sopenharmony_ci	.logical_capacity = dasd_eckd_logical_capacity,
67438c2ecf20Sopenharmony_ci	.release_space = dasd_eckd_release_space,
67448c2ecf20Sopenharmony_ci	.ext_pool_id = dasd_eckd_ext_pool_id,
67458c2ecf20Sopenharmony_ci	.ext_size = dasd_eckd_ext_size,
67468c2ecf20Sopenharmony_ci	.ext_pool_cap_at_warnlevel = dasd_eckd_ext_pool_cap_at_warnlevel,
67478c2ecf20Sopenharmony_ci	.ext_pool_warn_thrshld = dasd_eckd_ext_pool_warn_thrshld,
67488c2ecf20Sopenharmony_ci	.ext_pool_oos = dasd_eckd_ext_pool_oos,
67498c2ecf20Sopenharmony_ci	.ext_pool_exhaust = dasd_eckd_ext_pool_exhaust,
67508c2ecf20Sopenharmony_ci	.ese_format = dasd_eckd_ese_format,
67518c2ecf20Sopenharmony_ci	.ese_read = dasd_eckd_ese_read,
67528c2ecf20Sopenharmony_ci};
67538c2ecf20Sopenharmony_ci
67548c2ecf20Sopenharmony_cistatic int __init
67558c2ecf20Sopenharmony_cidasd_eckd_init(void)
67568c2ecf20Sopenharmony_ci{
67578c2ecf20Sopenharmony_ci	int ret;
67588c2ecf20Sopenharmony_ci
67598c2ecf20Sopenharmony_ci	ASCEBC(dasd_eckd_discipline.ebcname, 4);
67608c2ecf20Sopenharmony_ci	dasd_reserve_req = kmalloc(sizeof(*dasd_reserve_req),
67618c2ecf20Sopenharmony_ci				   GFP_KERNEL | GFP_DMA);
67628c2ecf20Sopenharmony_ci	if (!dasd_reserve_req)
67638c2ecf20Sopenharmony_ci		return -ENOMEM;
67648c2ecf20Sopenharmony_ci	dasd_vol_info_req = kmalloc(sizeof(*dasd_vol_info_req),
67658c2ecf20Sopenharmony_ci				    GFP_KERNEL | GFP_DMA);
67668c2ecf20Sopenharmony_ci	if (!dasd_vol_info_req) {
67678c2ecf20Sopenharmony_ci		kfree(dasd_reserve_req);
67688c2ecf20Sopenharmony_ci		return -ENOMEM;
67698c2ecf20Sopenharmony_ci	}
67708c2ecf20Sopenharmony_ci	pe_handler_worker = kmalloc(sizeof(*pe_handler_worker),
67718c2ecf20Sopenharmony_ci				    GFP_KERNEL | GFP_DMA);
67728c2ecf20Sopenharmony_ci	if (!pe_handler_worker) {
67738c2ecf20Sopenharmony_ci		kfree(dasd_reserve_req);
67748c2ecf20Sopenharmony_ci		kfree(dasd_vol_info_req);
67758c2ecf20Sopenharmony_ci		return -ENOMEM;
67768c2ecf20Sopenharmony_ci	}
67778c2ecf20Sopenharmony_ci	rawpadpage = (void *)__get_free_page(GFP_KERNEL);
67788c2ecf20Sopenharmony_ci	if (!rawpadpage) {
67798c2ecf20Sopenharmony_ci		kfree(pe_handler_worker);
67808c2ecf20Sopenharmony_ci		kfree(dasd_reserve_req);
67818c2ecf20Sopenharmony_ci		kfree(dasd_vol_info_req);
67828c2ecf20Sopenharmony_ci		return -ENOMEM;
67838c2ecf20Sopenharmony_ci	}
67848c2ecf20Sopenharmony_ci	ret = ccw_driver_register(&dasd_eckd_driver);
67858c2ecf20Sopenharmony_ci	if (!ret)
67868c2ecf20Sopenharmony_ci		wait_for_device_probe();
67878c2ecf20Sopenharmony_ci	else {
67888c2ecf20Sopenharmony_ci		kfree(pe_handler_worker);
67898c2ecf20Sopenharmony_ci		kfree(dasd_reserve_req);
67908c2ecf20Sopenharmony_ci		kfree(dasd_vol_info_req);
67918c2ecf20Sopenharmony_ci		free_page((unsigned long)rawpadpage);
67928c2ecf20Sopenharmony_ci	}
67938c2ecf20Sopenharmony_ci	return ret;
67948c2ecf20Sopenharmony_ci}
67958c2ecf20Sopenharmony_ci
67968c2ecf20Sopenharmony_cistatic void __exit
67978c2ecf20Sopenharmony_cidasd_eckd_cleanup(void)
67988c2ecf20Sopenharmony_ci{
67998c2ecf20Sopenharmony_ci	ccw_driver_unregister(&dasd_eckd_driver);
68008c2ecf20Sopenharmony_ci	kfree(pe_handler_worker);
68018c2ecf20Sopenharmony_ci	kfree(dasd_reserve_req);
68028c2ecf20Sopenharmony_ci	free_page((unsigned long)rawpadpage);
68038c2ecf20Sopenharmony_ci}
68048c2ecf20Sopenharmony_ci
68058c2ecf20Sopenharmony_cimodule_init(dasd_eckd_init);
68068c2ecf20Sopenharmony_cimodule_exit(dasd_eckd_cleanup);
6807