162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com> 462306a36Sopenharmony_ci * Based on.......: linux/drivers/s390/block/mdisk.c 562306a36Sopenharmony_ci * ...............: by Hartmunt Penner <hpenner@de.ibm.com> 662306a36Sopenharmony_ci * Bugreports.to..: <Linux390@de.ibm.com> 762306a36Sopenharmony_ci * Copyright IBM Corp. 1999, 2000 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#define KMSG_COMPONENT "dasd" 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/kernel_stat.h> 1462306a36Sopenharmony_ci#include <linux/stddef.h> 1562306a36Sopenharmony_ci#include <linux/kernel.h> 1662306a36Sopenharmony_ci#include <linux/slab.h> 1762306a36Sopenharmony_ci#include <linux/hdreg.h> 1862306a36Sopenharmony_ci#include <linux/bio.h> 1962306a36Sopenharmony_ci#include <linux/module.h> 2062306a36Sopenharmony_ci#include <linux/init.h> 2162306a36Sopenharmony_ci#include <linux/jiffies.h> 2262306a36Sopenharmony_ci#include <asm/asm-extable.h> 2362306a36Sopenharmony_ci#include <asm/dasd.h> 2462306a36Sopenharmony_ci#include <asm/debug.h> 2562306a36Sopenharmony_ci#include <asm/diag.h> 2662306a36Sopenharmony_ci#include <asm/ebcdic.h> 2762306a36Sopenharmony_ci#include <linux/io.h> 2862306a36Sopenharmony_ci#include <asm/irq.h> 2962306a36Sopenharmony_ci#include <asm/vtoc.h> 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#include "dasd_int.h" 3262306a36Sopenharmony_ci#include "dasd_diag.h" 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define PRINTK_HEADER "dasd(diag):" 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/* The maximum number of blocks per request (max_blocks) is dependent on the 3962306a36Sopenharmony_ci * amount of storage that is available in the static I/O buffer for each 4062306a36Sopenharmony_ci * device. Currently each device gets 2 pages. We want to fit two requests 4162306a36Sopenharmony_ci * into the available memory so that we can immediately start the next if one 4262306a36Sopenharmony_ci * finishes. */ 4362306a36Sopenharmony_ci#define DIAG_MAX_BLOCKS (((2 * PAGE_SIZE - sizeof(struct dasd_ccw_req) - \ 4462306a36Sopenharmony_ci sizeof(struct dasd_diag_req)) / \ 4562306a36Sopenharmony_ci sizeof(struct dasd_diag_bio)) / 2) 4662306a36Sopenharmony_ci#define DIAG_MAX_RETRIES 32 4762306a36Sopenharmony_ci#define DIAG_TIMEOUT 50 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic struct dasd_discipline dasd_diag_discipline; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistruct dasd_diag_private { 5262306a36Sopenharmony_ci struct dasd_diag_characteristics rdc_data; 5362306a36Sopenharmony_ci struct dasd_diag_rw_io iob; 5462306a36Sopenharmony_ci struct dasd_diag_init_io iib; 5562306a36Sopenharmony_ci blocknum_t pt_block; 5662306a36Sopenharmony_ci struct ccw_dev_id dev_id; 5762306a36Sopenharmony_ci}; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistruct dasd_diag_req { 6062306a36Sopenharmony_ci unsigned int block_count; 6162306a36Sopenharmony_ci struct dasd_diag_bio bio[]; 6262306a36Sopenharmony_ci}; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic const u8 DASD_DIAG_CMS1[] = { 0xc3, 0xd4, 0xe2, 0xf1 };/* EBCDIC CMS1 */ 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci/* Perform DIAG250 call with block I/O parameter list iob (input and output) 6762306a36Sopenharmony_ci * and function code cmd. 6862306a36Sopenharmony_ci * In case of an exception return 3. Otherwise return result of bitwise OR of 6962306a36Sopenharmony_ci * resulting condition code and DIAG return code. */ 7062306a36Sopenharmony_cistatic inline int __dia250(void *iob, int cmd) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci union register_pair rx = { .even = (unsigned long)iob, }; 7362306a36Sopenharmony_ci typedef union { 7462306a36Sopenharmony_ci struct dasd_diag_init_io init_io; 7562306a36Sopenharmony_ci struct dasd_diag_rw_io rw_io; 7662306a36Sopenharmony_ci } addr_type; 7762306a36Sopenharmony_ci int cc; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci cc = 3; 8062306a36Sopenharmony_ci asm volatile( 8162306a36Sopenharmony_ci " diag %[rx],%[cmd],0x250\n" 8262306a36Sopenharmony_ci "0: ipm %[cc]\n" 8362306a36Sopenharmony_ci " srl %[cc],28\n" 8462306a36Sopenharmony_ci "1:\n" 8562306a36Sopenharmony_ci EX_TABLE(0b,1b) 8662306a36Sopenharmony_ci : [cc] "+&d" (cc), [rx] "+&d" (rx.pair), "+m" (*(addr_type *)iob) 8762306a36Sopenharmony_ci : [cmd] "d" (cmd) 8862306a36Sopenharmony_ci : "cc"); 8962306a36Sopenharmony_ci return cc | rx.odd; 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic inline int dia250(void *iob, int cmd) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci diag_stat_inc(DIAG_STAT_X250); 9562306a36Sopenharmony_ci return __dia250(iob, cmd); 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci/* Initialize block I/O to DIAG device using the specified blocksize and 9962306a36Sopenharmony_ci * block offset. On success, return zero and set end_block to contain the 10062306a36Sopenharmony_ci * number of blocks on the device minus the specified offset. Return non-zero 10162306a36Sopenharmony_ci * otherwise. */ 10262306a36Sopenharmony_cistatic inline int 10362306a36Sopenharmony_cimdsk_init_io(struct dasd_device *device, unsigned int blocksize, 10462306a36Sopenharmony_ci blocknum_t offset, blocknum_t *end_block) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci struct dasd_diag_private *private = device->private; 10762306a36Sopenharmony_ci struct dasd_diag_init_io *iib = &private->iib; 10862306a36Sopenharmony_ci int rc; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci memset(iib, 0, sizeof (struct dasd_diag_init_io)); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci iib->dev_nr = private->dev_id.devno; 11362306a36Sopenharmony_ci iib->block_size = blocksize; 11462306a36Sopenharmony_ci iib->offset = offset; 11562306a36Sopenharmony_ci iib->flaga = DASD_DIAG_FLAGA_DEFAULT; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci rc = dia250(iib, INIT_BIO); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci if ((rc & 3) == 0 && end_block) 12062306a36Sopenharmony_ci *end_block = iib->end_block; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci return rc; 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci/* Remove block I/O environment for device. Return zero on success, non-zero 12662306a36Sopenharmony_ci * otherwise. */ 12762306a36Sopenharmony_cistatic inline int 12862306a36Sopenharmony_cimdsk_term_io(struct dasd_device * device) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci struct dasd_diag_private *private = device->private; 13162306a36Sopenharmony_ci struct dasd_diag_init_io *iib = &private->iib; 13262306a36Sopenharmony_ci int rc; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci memset(iib, 0, sizeof (struct dasd_diag_init_io)); 13562306a36Sopenharmony_ci iib->dev_nr = private->dev_id.devno; 13662306a36Sopenharmony_ci rc = dia250(iib, TERM_BIO); 13762306a36Sopenharmony_ci return rc; 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci/* Error recovery for failed DIAG requests - try to reestablish the DIAG 14162306a36Sopenharmony_ci * environment. */ 14262306a36Sopenharmony_cistatic void 14362306a36Sopenharmony_cidasd_diag_erp(struct dasd_device *device) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci int rc; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci mdsk_term_io(device); 14862306a36Sopenharmony_ci rc = mdsk_init_io(device, device->block->bp_block, 0, NULL); 14962306a36Sopenharmony_ci if (rc == 4) { 15062306a36Sopenharmony_ci if (!(test_and_set_bit(DASD_FLAG_DEVICE_RO, &device->flags))) 15162306a36Sopenharmony_ci pr_warn("%s: The access mode of a DIAG device changed to read-only\n", 15262306a36Sopenharmony_ci dev_name(&device->cdev->dev)); 15362306a36Sopenharmony_ci rc = 0; 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci if (rc) 15662306a36Sopenharmony_ci pr_warn("%s: DIAG ERP failed with rc=%d\n", 15762306a36Sopenharmony_ci dev_name(&device->cdev->dev), rc); 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci/* Start a given request at the device. Return zero on success, non-zero 16162306a36Sopenharmony_ci * otherwise. */ 16262306a36Sopenharmony_cistatic int 16362306a36Sopenharmony_cidasd_start_diag(struct dasd_ccw_req * cqr) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci struct dasd_device *device; 16662306a36Sopenharmony_ci struct dasd_diag_private *private; 16762306a36Sopenharmony_ci struct dasd_diag_req *dreq; 16862306a36Sopenharmony_ci int rc; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci device = cqr->startdev; 17162306a36Sopenharmony_ci if (cqr->retries < 0) { 17262306a36Sopenharmony_ci DBF_DEV_EVENT(DBF_ERR, device, "DIAG start_IO: request %p " 17362306a36Sopenharmony_ci "- no retry left)", cqr); 17462306a36Sopenharmony_ci cqr->status = DASD_CQR_ERROR; 17562306a36Sopenharmony_ci return -EIO; 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci private = device->private; 17862306a36Sopenharmony_ci dreq = cqr->data; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci private->iob.dev_nr = private->dev_id.devno; 18162306a36Sopenharmony_ci private->iob.key = 0; 18262306a36Sopenharmony_ci private->iob.flags = DASD_DIAG_RWFLAG_ASYNC; 18362306a36Sopenharmony_ci private->iob.block_count = dreq->block_count; 18462306a36Sopenharmony_ci private->iob.interrupt_params = (addr_t) cqr; 18562306a36Sopenharmony_ci private->iob.bio_list = dreq->bio; 18662306a36Sopenharmony_ci private->iob.flaga = DASD_DIAG_FLAGA_DEFAULT; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci cqr->startclk = get_tod_clock(); 18962306a36Sopenharmony_ci cqr->starttime = jiffies; 19062306a36Sopenharmony_ci cqr->retries--; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci rc = dia250(&private->iob, RW_BIO); 19362306a36Sopenharmony_ci switch (rc) { 19462306a36Sopenharmony_ci case 0: /* Synchronous I/O finished successfully */ 19562306a36Sopenharmony_ci cqr->stopclk = get_tod_clock(); 19662306a36Sopenharmony_ci cqr->status = DASD_CQR_SUCCESS; 19762306a36Sopenharmony_ci /* Indicate to calling function that only a dasd_schedule_bh() 19862306a36Sopenharmony_ci and no timer is needed */ 19962306a36Sopenharmony_ci rc = -EACCES; 20062306a36Sopenharmony_ci break; 20162306a36Sopenharmony_ci case 8: /* Asynchronous I/O was started */ 20262306a36Sopenharmony_ci cqr->status = DASD_CQR_IN_IO; 20362306a36Sopenharmony_ci rc = 0; 20462306a36Sopenharmony_ci break; 20562306a36Sopenharmony_ci default: /* Error condition */ 20662306a36Sopenharmony_ci cqr->status = DASD_CQR_QUEUED; 20762306a36Sopenharmony_ci DBF_DEV_EVENT(DBF_WARNING, device, "dia250 returned rc=%d", rc); 20862306a36Sopenharmony_ci dasd_diag_erp(device); 20962306a36Sopenharmony_ci rc = -EIO; 21062306a36Sopenharmony_ci break; 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci cqr->intrc = rc; 21362306a36Sopenharmony_ci return rc; 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci/* Terminate given request at the device. */ 21762306a36Sopenharmony_cistatic int 21862306a36Sopenharmony_cidasd_diag_term_IO(struct dasd_ccw_req * cqr) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci struct dasd_device *device; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci device = cqr->startdev; 22362306a36Sopenharmony_ci mdsk_term_io(device); 22462306a36Sopenharmony_ci mdsk_init_io(device, device->block->bp_block, 0, NULL); 22562306a36Sopenharmony_ci cqr->status = DASD_CQR_CLEAR_PENDING; 22662306a36Sopenharmony_ci cqr->stopclk = get_tod_clock(); 22762306a36Sopenharmony_ci dasd_schedule_device_bh(device); 22862306a36Sopenharmony_ci return 0; 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci/* Handle external interruption. */ 23262306a36Sopenharmony_cistatic void dasd_ext_handler(struct ext_code ext_code, 23362306a36Sopenharmony_ci unsigned int param32, unsigned long param64) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci struct dasd_ccw_req *cqr, *next; 23662306a36Sopenharmony_ci struct dasd_device *device; 23762306a36Sopenharmony_ci unsigned long expires; 23862306a36Sopenharmony_ci unsigned long flags; 23962306a36Sopenharmony_ci addr_t ip; 24062306a36Sopenharmony_ci int rc; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci switch (ext_code.subcode >> 8) { 24362306a36Sopenharmony_ci case DASD_DIAG_CODE_31BIT: 24462306a36Sopenharmony_ci ip = (addr_t) param32; 24562306a36Sopenharmony_ci break; 24662306a36Sopenharmony_ci case DASD_DIAG_CODE_64BIT: 24762306a36Sopenharmony_ci ip = (addr_t) param64; 24862306a36Sopenharmony_ci break; 24962306a36Sopenharmony_ci default: 25062306a36Sopenharmony_ci return; 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci inc_irq_stat(IRQEXT_DSD); 25362306a36Sopenharmony_ci if (!ip) { /* no intparm: unsolicited interrupt */ 25462306a36Sopenharmony_ci DBF_EVENT(DBF_NOTICE, "%s", "caught unsolicited " 25562306a36Sopenharmony_ci "interrupt"); 25662306a36Sopenharmony_ci return; 25762306a36Sopenharmony_ci } 25862306a36Sopenharmony_ci cqr = (struct dasd_ccw_req *) ip; 25962306a36Sopenharmony_ci device = (struct dasd_device *) cqr->startdev; 26062306a36Sopenharmony_ci if (strncmp(device->discipline->ebcname, (char *) &cqr->magic, 4)) { 26162306a36Sopenharmony_ci DBF_DEV_EVENT(DBF_WARNING, device, 26262306a36Sopenharmony_ci " magic number of dasd_ccw_req 0x%08X doesn't" 26362306a36Sopenharmony_ci " match discipline 0x%08X", 26462306a36Sopenharmony_ci cqr->magic, *(int *) (&device->discipline->name)); 26562306a36Sopenharmony_ci return; 26662306a36Sopenharmony_ci } 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci /* get irq lock to modify request queue */ 26962306a36Sopenharmony_ci spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci /* Check for a pending clear operation */ 27262306a36Sopenharmony_ci if (cqr->status == DASD_CQR_CLEAR_PENDING) { 27362306a36Sopenharmony_ci cqr->status = DASD_CQR_CLEARED; 27462306a36Sopenharmony_ci dasd_device_clear_timer(device); 27562306a36Sopenharmony_ci dasd_schedule_device_bh(device); 27662306a36Sopenharmony_ci spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); 27762306a36Sopenharmony_ci return; 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci cqr->stopclk = get_tod_clock(); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci expires = 0; 28362306a36Sopenharmony_ci if ((ext_code.subcode & 0xff) == 0) { 28462306a36Sopenharmony_ci cqr->status = DASD_CQR_SUCCESS; 28562306a36Sopenharmony_ci /* Start first request on queue if possible -> fast_io. */ 28662306a36Sopenharmony_ci if (!list_empty(&device->ccw_queue)) { 28762306a36Sopenharmony_ci next = list_entry(device->ccw_queue.next, 28862306a36Sopenharmony_ci struct dasd_ccw_req, devlist); 28962306a36Sopenharmony_ci if (next->status == DASD_CQR_QUEUED) { 29062306a36Sopenharmony_ci rc = dasd_start_diag(next); 29162306a36Sopenharmony_ci if (rc == 0) 29262306a36Sopenharmony_ci expires = next->expires; 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci } else { 29662306a36Sopenharmony_ci cqr->status = DASD_CQR_QUEUED; 29762306a36Sopenharmony_ci DBF_DEV_EVENT(DBF_DEBUG, device, "interrupt status for " 29862306a36Sopenharmony_ci "request %p was %d (%d retries left)", cqr, 29962306a36Sopenharmony_ci ext_code.subcode & 0xff, cqr->retries); 30062306a36Sopenharmony_ci dasd_diag_erp(device); 30162306a36Sopenharmony_ci } 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci if (expires != 0) 30462306a36Sopenharmony_ci dasd_device_set_timer(device, expires); 30562306a36Sopenharmony_ci else 30662306a36Sopenharmony_ci dasd_device_clear_timer(device); 30762306a36Sopenharmony_ci dasd_schedule_device_bh(device); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); 31062306a36Sopenharmony_ci} 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci/* Check whether device can be controlled by DIAG discipline. Return zero on 31362306a36Sopenharmony_ci * success, non-zero otherwise. */ 31462306a36Sopenharmony_cistatic int 31562306a36Sopenharmony_cidasd_diag_check_device(struct dasd_device *device) 31662306a36Sopenharmony_ci{ 31762306a36Sopenharmony_ci struct dasd_diag_private *private = device->private; 31862306a36Sopenharmony_ci struct dasd_diag_characteristics *rdc_data; 31962306a36Sopenharmony_ci struct vtoc_cms_label *label; 32062306a36Sopenharmony_ci struct dasd_block *block; 32162306a36Sopenharmony_ci struct dasd_diag_bio *bio; 32262306a36Sopenharmony_ci unsigned int sb, bsize; 32362306a36Sopenharmony_ci blocknum_t end_block; 32462306a36Sopenharmony_ci int rc; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci if (private == NULL) { 32762306a36Sopenharmony_ci private = kzalloc(sizeof(*private), GFP_KERNEL); 32862306a36Sopenharmony_ci if (private == NULL) { 32962306a36Sopenharmony_ci DBF_DEV_EVENT(DBF_WARNING, device, "%s", 33062306a36Sopenharmony_ci "Allocating memory for private DASD data " 33162306a36Sopenharmony_ci "failed\n"); 33262306a36Sopenharmony_ci return -ENOMEM; 33362306a36Sopenharmony_ci } 33462306a36Sopenharmony_ci ccw_device_get_id(device->cdev, &private->dev_id); 33562306a36Sopenharmony_ci device->private = private; 33662306a36Sopenharmony_ci } 33762306a36Sopenharmony_ci block = dasd_alloc_block(); 33862306a36Sopenharmony_ci if (IS_ERR(block)) { 33962306a36Sopenharmony_ci DBF_DEV_EVENT(DBF_WARNING, device, "%s", 34062306a36Sopenharmony_ci "could not allocate dasd block structure"); 34162306a36Sopenharmony_ci device->private = NULL; 34262306a36Sopenharmony_ci kfree(private); 34362306a36Sopenharmony_ci return PTR_ERR(block); 34462306a36Sopenharmony_ci } 34562306a36Sopenharmony_ci device->block = block; 34662306a36Sopenharmony_ci block->base = device; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci /* Read Device Characteristics */ 34962306a36Sopenharmony_ci rdc_data = &private->rdc_data; 35062306a36Sopenharmony_ci rdc_data->dev_nr = private->dev_id.devno; 35162306a36Sopenharmony_ci rdc_data->rdc_len = sizeof (struct dasd_diag_characteristics); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci rc = diag210((struct diag210 *) rdc_data); 35462306a36Sopenharmony_ci if (rc) { 35562306a36Sopenharmony_ci DBF_DEV_EVENT(DBF_WARNING, device, "failed to retrieve device " 35662306a36Sopenharmony_ci "information (rc=%d)", rc); 35762306a36Sopenharmony_ci rc = -EOPNOTSUPP; 35862306a36Sopenharmony_ci goto out; 35962306a36Sopenharmony_ci } 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci device->default_expires = DIAG_TIMEOUT; 36262306a36Sopenharmony_ci device->default_retries = DIAG_MAX_RETRIES; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci /* Figure out position of label block */ 36562306a36Sopenharmony_ci switch (private->rdc_data.vdev_class) { 36662306a36Sopenharmony_ci case DEV_CLASS_FBA: 36762306a36Sopenharmony_ci private->pt_block = 1; 36862306a36Sopenharmony_ci break; 36962306a36Sopenharmony_ci case DEV_CLASS_ECKD: 37062306a36Sopenharmony_ci private->pt_block = 2; 37162306a36Sopenharmony_ci break; 37262306a36Sopenharmony_ci default: 37362306a36Sopenharmony_ci pr_warn("%s: Device type %d is not supported in DIAG mode\n", 37462306a36Sopenharmony_ci dev_name(&device->cdev->dev), 37562306a36Sopenharmony_ci private->rdc_data.vdev_class); 37662306a36Sopenharmony_ci rc = -EOPNOTSUPP; 37762306a36Sopenharmony_ci goto out; 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci DBF_DEV_EVENT(DBF_INFO, device, 38162306a36Sopenharmony_ci "%04X: %04X on real %04X/%02X", 38262306a36Sopenharmony_ci rdc_data->dev_nr, 38362306a36Sopenharmony_ci rdc_data->vdev_type, 38462306a36Sopenharmony_ci rdc_data->rdev_type, rdc_data->rdev_model); 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci /* terminate all outstanding operations */ 38762306a36Sopenharmony_ci mdsk_term_io(device); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci /* figure out blocksize of device */ 39062306a36Sopenharmony_ci label = (struct vtoc_cms_label *) get_zeroed_page(GFP_KERNEL); 39162306a36Sopenharmony_ci if (label == NULL) { 39262306a36Sopenharmony_ci DBF_DEV_EVENT(DBF_WARNING, device, "%s", 39362306a36Sopenharmony_ci "No memory to allocate initialization request"); 39462306a36Sopenharmony_ci rc = -ENOMEM; 39562306a36Sopenharmony_ci goto out; 39662306a36Sopenharmony_ci } 39762306a36Sopenharmony_ci bio = kzalloc(sizeof(*bio), GFP_KERNEL); 39862306a36Sopenharmony_ci if (bio == NULL) { 39962306a36Sopenharmony_ci DBF_DEV_EVENT(DBF_WARNING, device, "%s", 40062306a36Sopenharmony_ci "No memory to allocate initialization bio"); 40162306a36Sopenharmony_ci rc = -ENOMEM; 40262306a36Sopenharmony_ci goto out_label; 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci rc = 0; 40562306a36Sopenharmony_ci end_block = 0; 40662306a36Sopenharmony_ci /* try all sizes - needed for ECKD devices */ 40762306a36Sopenharmony_ci for (bsize = 512; bsize <= PAGE_SIZE; bsize <<= 1) { 40862306a36Sopenharmony_ci mdsk_init_io(device, bsize, 0, &end_block); 40962306a36Sopenharmony_ci memset(bio, 0, sizeof(*bio)); 41062306a36Sopenharmony_ci bio->type = MDSK_READ_REQ; 41162306a36Sopenharmony_ci bio->block_number = private->pt_block + 1; 41262306a36Sopenharmony_ci bio->buffer = label; 41362306a36Sopenharmony_ci memset(&private->iob, 0, sizeof (struct dasd_diag_rw_io)); 41462306a36Sopenharmony_ci private->iob.dev_nr = rdc_data->dev_nr; 41562306a36Sopenharmony_ci private->iob.key = 0; 41662306a36Sopenharmony_ci private->iob.flags = 0; /* do synchronous io */ 41762306a36Sopenharmony_ci private->iob.block_count = 1; 41862306a36Sopenharmony_ci private->iob.interrupt_params = 0; 41962306a36Sopenharmony_ci private->iob.bio_list = bio; 42062306a36Sopenharmony_ci private->iob.flaga = DASD_DIAG_FLAGA_DEFAULT; 42162306a36Sopenharmony_ci rc = dia250(&private->iob, RW_BIO); 42262306a36Sopenharmony_ci if (rc == 3) { 42362306a36Sopenharmony_ci pr_warn("%s: A 64-bit DIAG call failed\n", 42462306a36Sopenharmony_ci dev_name(&device->cdev->dev)); 42562306a36Sopenharmony_ci rc = -EOPNOTSUPP; 42662306a36Sopenharmony_ci goto out_bio; 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci mdsk_term_io(device); 42962306a36Sopenharmony_ci if (rc == 0) 43062306a36Sopenharmony_ci break; 43162306a36Sopenharmony_ci } 43262306a36Sopenharmony_ci if (bsize > PAGE_SIZE) { 43362306a36Sopenharmony_ci pr_warn("%s: Accessing the DASD failed because of an incorrect format (rc=%d)\n", 43462306a36Sopenharmony_ci dev_name(&device->cdev->dev), rc); 43562306a36Sopenharmony_ci rc = -EIO; 43662306a36Sopenharmony_ci goto out_bio; 43762306a36Sopenharmony_ci } 43862306a36Sopenharmony_ci /* check for label block */ 43962306a36Sopenharmony_ci if (memcmp(label->label_id, DASD_DIAG_CMS1, 44062306a36Sopenharmony_ci sizeof(DASD_DIAG_CMS1)) == 0) { 44162306a36Sopenharmony_ci /* get formatted blocksize from label block */ 44262306a36Sopenharmony_ci bsize = (unsigned int) label->block_size; 44362306a36Sopenharmony_ci block->blocks = (unsigned long) label->block_count; 44462306a36Sopenharmony_ci } else 44562306a36Sopenharmony_ci block->blocks = end_block; 44662306a36Sopenharmony_ci block->bp_block = bsize; 44762306a36Sopenharmony_ci block->s2b_shift = 0; /* bits to shift 512 to get a block */ 44862306a36Sopenharmony_ci for (sb = 512; sb < bsize; sb = sb << 1) 44962306a36Sopenharmony_ci block->s2b_shift++; 45062306a36Sopenharmony_ci rc = mdsk_init_io(device, block->bp_block, 0, NULL); 45162306a36Sopenharmony_ci if (rc && (rc != 4)) { 45262306a36Sopenharmony_ci pr_warn("%s: DIAG initialization failed with rc=%d\n", 45362306a36Sopenharmony_ci dev_name(&device->cdev->dev), rc); 45462306a36Sopenharmony_ci rc = -EIO; 45562306a36Sopenharmony_ci } else { 45662306a36Sopenharmony_ci if (rc == 4) 45762306a36Sopenharmony_ci set_bit(DASD_FLAG_DEVICE_RO, &device->flags); 45862306a36Sopenharmony_ci pr_info("%s: New DASD with %ld byte/block, total size %ld " 45962306a36Sopenharmony_ci "KB%s\n", dev_name(&device->cdev->dev), 46062306a36Sopenharmony_ci (unsigned long) block->bp_block, 46162306a36Sopenharmony_ci (unsigned long) (block->blocks << 46262306a36Sopenharmony_ci block->s2b_shift) >> 1, 46362306a36Sopenharmony_ci (rc == 4) ? ", read-only device" : ""); 46462306a36Sopenharmony_ci rc = 0; 46562306a36Sopenharmony_ci } 46662306a36Sopenharmony_ciout_bio: 46762306a36Sopenharmony_ci kfree(bio); 46862306a36Sopenharmony_ciout_label: 46962306a36Sopenharmony_ci free_page((long) label); 47062306a36Sopenharmony_ciout: 47162306a36Sopenharmony_ci if (rc) { 47262306a36Sopenharmony_ci device->block = NULL; 47362306a36Sopenharmony_ci dasd_free_block(block); 47462306a36Sopenharmony_ci device->private = NULL; 47562306a36Sopenharmony_ci kfree(private); 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci return rc; 47862306a36Sopenharmony_ci} 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci/* Fill in virtual disk geometry for device. Return zero on success, non-zero 48162306a36Sopenharmony_ci * otherwise. */ 48262306a36Sopenharmony_cistatic int 48362306a36Sopenharmony_cidasd_diag_fill_geometry(struct dasd_block *block, struct hd_geometry *geo) 48462306a36Sopenharmony_ci{ 48562306a36Sopenharmony_ci if (dasd_check_blocksize(block->bp_block) != 0) 48662306a36Sopenharmony_ci return -EINVAL; 48762306a36Sopenharmony_ci geo->cylinders = (block->blocks << block->s2b_shift) >> 10; 48862306a36Sopenharmony_ci geo->heads = 16; 48962306a36Sopenharmony_ci geo->sectors = 128 >> block->s2b_shift; 49062306a36Sopenharmony_ci return 0; 49162306a36Sopenharmony_ci} 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_cistatic dasd_erp_fn_t 49462306a36Sopenharmony_cidasd_diag_erp_action(struct dasd_ccw_req * cqr) 49562306a36Sopenharmony_ci{ 49662306a36Sopenharmony_ci return dasd_default_erp_action; 49762306a36Sopenharmony_ci} 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_cistatic dasd_erp_fn_t 50062306a36Sopenharmony_cidasd_diag_erp_postaction(struct dasd_ccw_req * cqr) 50162306a36Sopenharmony_ci{ 50262306a36Sopenharmony_ci return dasd_default_erp_postaction; 50362306a36Sopenharmony_ci} 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci/* Create DASD request from block device request. Return pointer to new 50662306a36Sopenharmony_ci * request on success, ERR_PTR otherwise. */ 50762306a36Sopenharmony_cistatic struct dasd_ccw_req *dasd_diag_build_cp(struct dasd_device *memdev, 50862306a36Sopenharmony_ci struct dasd_block *block, 50962306a36Sopenharmony_ci struct request *req) 51062306a36Sopenharmony_ci{ 51162306a36Sopenharmony_ci struct dasd_ccw_req *cqr; 51262306a36Sopenharmony_ci struct dasd_diag_req *dreq; 51362306a36Sopenharmony_ci struct dasd_diag_bio *dbio; 51462306a36Sopenharmony_ci struct req_iterator iter; 51562306a36Sopenharmony_ci struct bio_vec bv; 51662306a36Sopenharmony_ci char *dst; 51762306a36Sopenharmony_ci unsigned int count; 51862306a36Sopenharmony_ci sector_t recid, first_rec, last_rec; 51962306a36Sopenharmony_ci unsigned int blksize, off; 52062306a36Sopenharmony_ci unsigned char rw_cmd; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci if (rq_data_dir(req) == READ) 52362306a36Sopenharmony_ci rw_cmd = MDSK_READ_REQ; 52462306a36Sopenharmony_ci else if (rq_data_dir(req) == WRITE) 52562306a36Sopenharmony_ci rw_cmd = MDSK_WRITE_REQ; 52662306a36Sopenharmony_ci else 52762306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 52862306a36Sopenharmony_ci blksize = block->bp_block; 52962306a36Sopenharmony_ci /* Calculate record id of first and last block. */ 53062306a36Sopenharmony_ci first_rec = blk_rq_pos(req) >> block->s2b_shift; 53162306a36Sopenharmony_ci last_rec = 53262306a36Sopenharmony_ci (blk_rq_pos(req) + blk_rq_sectors(req) - 1) >> block->s2b_shift; 53362306a36Sopenharmony_ci /* Check struct bio and count the number of blocks for the request. */ 53462306a36Sopenharmony_ci count = 0; 53562306a36Sopenharmony_ci rq_for_each_segment(bv, req, iter) { 53662306a36Sopenharmony_ci if (bv.bv_len & (blksize - 1)) 53762306a36Sopenharmony_ci /* Fba can only do full blocks. */ 53862306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 53962306a36Sopenharmony_ci count += bv.bv_len >> (block->s2b_shift + 9); 54062306a36Sopenharmony_ci } 54162306a36Sopenharmony_ci /* Paranoia. */ 54262306a36Sopenharmony_ci if (count != last_rec - first_rec + 1) 54362306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 54462306a36Sopenharmony_ci /* Build the request */ 54562306a36Sopenharmony_ci cqr = dasd_smalloc_request(DASD_DIAG_MAGIC, 0, struct_size(dreq, bio, count), 54662306a36Sopenharmony_ci memdev, blk_mq_rq_to_pdu(req)); 54762306a36Sopenharmony_ci if (IS_ERR(cqr)) 54862306a36Sopenharmony_ci return cqr; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci dreq = (struct dasd_diag_req *) cqr->data; 55162306a36Sopenharmony_ci dreq->block_count = count; 55262306a36Sopenharmony_ci dbio = dreq->bio; 55362306a36Sopenharmony_ci recid = first_rec; 55462306a36Sopenharmony_ci rq_for_each_segment(bv, req, iter) { 55562306a36Sopenharmony_ci dst = bvec_virt(&bv); 55662306a36Sopenharmony_ci for (off = 0; off < bv.bv_len; off += blksize) { 55762306a36Sopenharmony_ci memset(dbio, 0, sizeof (struct dasd_diag_bio)); 55862306a36Sopenharmony_ci dbio->type = rw_cmd; 55962306a36Sopenharmony_ci dbio->block_number = recid + 1; 56062306a36Sopenharmony_ci dbio->buffer = dst; 56162306a36Sopenharmony_ci dbio++; 56262306a36Sopenharmony_ci dst += blksize; 56362306a36Sopenharmony_ci recid++; 56462306a36Sopenharmony_ci } 56562306a36Sopenharmony_ci } 56662306a36Sopenharmony_ci cqr->retries = memdev->default_retries; 56762306a36Sopenharmony_ci cqr->buildclk = get_tod_clock(); 56862306a36Sopenharmony_ci if (blk_noretry_request(req) || 56962306a36Sopenharmony_ci block->base->features & DASD_FEATURE_FAILFAST) 57062306a36Sopenharmony_ci set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); 57162306a36Sopenharmony_ci cqr->startdev = memdev; 57262306a36Sopenharmony_ci cqr->memdev = memdev; 57362306a36Sopenharmony_ci cqr->block = block; 57462306a36Sopenharmony_ci cqr->expires = memdev->default_expires * HZ; 57562306a36Sopenharmony_ci cqr->status = DASD_CQR_FILLED; 57662306a36Sopenharmony_ci return cqr; 57762306a36Sopenharmony_ci} 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci/* Release DASD request. Return non-zero if request was successful, zero 58062306a36Sopenharmony_ci * otherwise. */ 58162306a36Sopenharmony_cistatic int 58262306a36Sopenharmony_cidasd_diag_free_cp(struct dasd_ccw_req *cqr, struct request *req) 58362306a36Sopenharmony_ci{ 58462306a36Sopenharmony_ci int status; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci status = cqr->status == DASD_CQR_DONE; 58762306a36Sopenharmony_ci dasd_sfree_request(cqr, cqr->memdev); 58862306a36Sopenharmony_ci return status; 58962306a36Sopenharmony_ci} 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_cistatic void dasd_diag_handle_terminated_request(struct dasd_ccw_req *cqr) 59262306a36Sopenharmony_ci{ 59362306a36Sopenharmony_ci if (cqr->retries < 0) 59462306a36Sopenharmony_ci cqr->status = DASD_CQR_FAILED; 59562306a36Sopenharmony_ci else 59662306a36Sopenharmony_ci cqr->status = DASD_CQR_FILLED; 59762306a36Sopenharmony_ci}; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci/* Fill in IOCTL data for device. */ 60062306a36Sopenharmony_cistatic int 60162306a36Sopenharmony_cidasd_diag_fill_info(struct dasd_device * device, 60262306a36Sopenharmony_ci struct dasd_information2_t * info) 60362306a36Sopenharmony_ci{ 60462306a36Sopenharmony_ci struct dasd_diag_private *private = device->private; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci info->label_block = (unsigned int) private->pt_block; 60762306a36Sopenharmony_ci info->FBA_layout = 1; 60862306a36Sopenharmony_ci info->format = DASD_FORMAT_LDL; 60962306a36Sopenharmony_ci info->characteristics_size = sizeof(private->rdc_data); 61062306a36Sopenharmony_ci memcpy(info->characteristics, &private->rdc_data, 61162306a36Sopenharmony_ci sizeof(private->rdc_data)); 61262306a36Sopenharmony_ci info->confdata_size = 0; 61362306a36Sopenharmony_ci return 0; 61462306a36Sopenharmony_ci} 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_cistatic void 61762306a36Sopenharmony_cidasd_diag_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req, 61862306a36Sopenharmony_ci struct irb *stat) 61962306a36Sopenharmony_ci{ 62062306a36Sopenharmony_ci DBF_DEV_EVENT(DBF_WARNING, device, "%s", 62162306a36Sopenharmony_ci "dump sense not available for DIAG data"); 62262306a36Sopenharmony_ci} 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci/* 62562306a36Sopenharmony_ci * Initialize block layer request queue. 62662306a36Sopenharmony_ci */ 62762306a36Sopenharmony_cistatic void dasd_diag_setup_blk_queue(struct dasd_block *block) 62862306a36Sopenharmony_ci{ 62962306a36Sopenharmony_ci unsigned int logical_block_size = block->bp_block; 63062306a36Sopenharmony_ci struct request_queue *q = block->gdp->queue; 63162306a36Sopenharmony_ci int max; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci max = DIAG_MAX_BLOCKS << block->s2b_shift; 63462306a36Sopenharmony_ci blk_queue_flag_set(QUEUE_FLAG_NONROT, q); 63562306a36Sopenharmony_ci q->limits.max_dev_sectors = max; 63662306a36Sopenharmony_ci blk_queue_logical_block_size(q, logical_block_size); 63762306a36Sopenharmony_ci blk_queue_max_hw_sectors(q, max); 63862306a36Sopenharmony_ci blk_queue_max_segments(q, USHRT_MAX); 63962306a36Sopenharmony_ci /* With page sized segments each segment can be translated into one idaw/tidaw */ 64062306a36Sopenharmony_ci blk_queue_max_segment_size(q, PAGE_SIZE); 64162306a36Sopenharmony_ci blk_queue_segment_boundary(q, PAGE_SIZE - 1); 64262306a36Sopenharmony_ci blk_queue_dma_alignment(q, PAGE_SIZE - 1); 64362306a36Sopenharmony_ci} 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_cistatic int dasd_diag_pe_handler(struct dasd_device *device, 64662306a36Sopenharmony_ci __u8 tbvpm, __u8 fcsecpm) 64762306a36Sopenharmony_ci{ 64862306a36Sopenharmony_ci return dasd_generic_verify_path(device, tbvpm); 64962306a36Sopenharmony_ci} 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_cistatic struct dasd_discipline dasd_diag_discipline = { 65262306a36Sopenharmony_ci .owner = THIS_MODULE, 65362306a36Sopenharmony_ci .name = "DIAG", 65462306a36Sopenharmony_ci .ebcname = "DIAG", 65562306a36Sopenharmony_ci .check_device = dasd_diag_check_device, 65662306a36Sopenharmony_ci .pe_handler = dasd_diag_pe_handler, 65762306a36Sopenharmony_ci .fill_geometry = dasd_diag_fill_geometry, 65862306a36Sopenharmony_ci .setup_blk_queue = dasd_diag_setup_blk_queue, 65962306a36Sopenharmony_ci .start_IO = dasd_start_diag, 66062306a36Sopenharmony_ci .term_IO = dasd_diag_term_IO, 66162306a36Sopenharmony_ci .handle_terminated_request = dasd_diag_handle_terminated_request, 66262306a36Sopenharmony_ci .erp_action = dasd_diag_erp_action, 66362306a36Sopenharmony_ci .erp_postaction = dasd_diag_erp_postaction, 66462306a36Sopenharmony_ci .build_cp = dasd_diag_build_cp, 66562306a36Sopenharmony_ci .free_cp = dasd_diag_free_cp, 66662306a36Sopenharmony_ci .dump_sense = dasd_diag_dump_sense, 66762306a36Sopenharmony_ci .fill_info = dasd_diag_fill_info, 66862306a36Sopenharmony_ci}; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_cistatic int __init 67162306a36Sopenharmony_cidasd_diag_init(void) 67262306a36Sopenharmony_ci{ 67362306a36Sopenharmony_ci if (!MACHINE_IS_VM) { 67462306a36Sopenharmony_ci pr_info("Discipline %s cannot be used without z/VM\n", 67562306a36Sopenharmony_ci dasd_diag_discipline.name); 67662306a36Sopenharmony_ci return -ENODEV; 67762306a36Sopenharmony_ci } 67862306a36Sopenharmony_ci ASCEBC(dasd_diag_discipline.ebcname, 4); 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci irq_subclass_register(IRQ_SUBCLASS_SERVICE_SIGNAL); 68162306a36Sopenharmony_ci register_external_irq(EXT_IRQ_CP_SERVICE, dasd_ext_handler); 68262306a36Sopenharmony_ci dasd_diag_discipline_pointer = &dasd_diag_discipline; 68362306a36Sopenharmony_ci return 0; 68462306a36Sopenharmony_ci} 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_cistatic void __exit 68762306a36Sopenharmony_cidasd_diag_cleanup(void) 68862306a36Sopenharmony_ci{ 68962306a36Sopenharmony_ci unregister_external_irq(EXT_IRQ_CP_SERVICE, dasd_ext_handler); 69062306a36Sopenharmony_ci irq_subclass_unregister(IRQ_SUBCLASS_SERVICE_SIGNAL); 69162306a36Sopenharmony_ci dasd_diag_discipline_pointer = NULL; 69262306a36Sopenharmony_ci} 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_cimodule_init(dasd_diag_init); 69562306a36Sopenharmony_cimodule_exit(dasd_diag_cleanup); 696