brintos

brintos / linux-shallow public Read only

0
0
Text · 21.9 KiB · a221679 Raw
813 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>4 * Bugreports.to..: <Linux390@de.ibm.com>5 * Copyright IBM Corp. 1999, 20096 */7 8#define KMSG_COMPONENT "dasd-fba"9 10#include <linux/stddef.h>11#include <linux/kernel.h>12#include <asm/debug.h>13 14#include <linux/slab.h>15#include <linux/hdreg.h>	/* HDIO_GETGEO			    */16#include <linux/bio.h>17#include <linux/module.h>18#include <linux/init.h>19#include <linux/io.h>20 21#include <asm/idals.h>22#include <asm/ebcdic.h>23#include <asm/ccwdev.h>24 25#include "dasd_int.h"26#include "dasd_fba.h"27 28#define FBA_DEFAULT_RETRIES 3229 30#define DASD_FBA_CCW_WRITE 0x4131#define DASD_FBA_CCW_READ 0x4232#define DASD_FBA_CCW_LOCATE 0x4333#define DASD_FBA_CCW_DEFINE_EXTENT 0x6334 35MODULE_DESCRIPTION("S/390 DASD FBA Disks device driver");36MODULE_LICENSE("GPL");37 38static struct dasd_discipline dasd_fba_discipline;39static void *dasd_fba_zero_page;40 41struct dasd_fba_private {42	struct dasd_fba_characteristics rdc_data;43};44 45static struct ccw_device_id dasd_fba_ids[] = {46	{ CCW_DEVICE_DEVTYPE (0x6310, 0, 0x9336, 0), .driver_info = 0x1},47	{ CCW_DEVICE_DEVTYPE (0x3880, 0, 0x3370, 0), .driver_info = 0x2},48	{ /* end of list */ },49};50 51MODULE_DEVICE_TABLE(ccw, dasd_fba_ids);52 53static int54dasd_fba_set_online(struct ccw_device *cdev)55{56	return dasd_generic_set_online(cdev, &dasd_fba_discipline);57}58 59static struct ccw_driver dasd_fba_driver = {60	.driver = {61		.name	= "dasd-fba",62		.owner	= THIS_MODULE,63		.dev_groups = dasd_dev_groups,64	},65	.ids         = dasd_fba_ids,66	.probe       = dasd_generic_probe,67	.remove      = dasd_generic_remove,68	.set_offline = dasd_generic_set_offline,69	.set_online  = dasd_fba_set_online,70	.notify      = dasd_generic_notify,71	.path_event  = dasd_generic_path_event,72	.int_class   = IRQIO_DAS,73};74 75static void76define_extent(struct ccw1 * ccw, struct DE_fba_data *data, int rw,77	      int blksize, int beg, int nr)78{79	ccw->cmd_code = DASD_FBA_CCW_DEFINE_EXTENT;80	ccw->flags = 0;81	ccw->count = 16;82	ccw->cda = virt_to_dma32(data);83	memset(data, 0, sizeof (struct DE_fba_data));84	if (rw == WRITE)85		(data->mask).perm = 0x0;86	else if (rw == READ)87		(data->mask).perm = 0x1;88	else89		data->mask.perm = 0x2;90	data->blk_size = blksize;91	data->ext_loc = beg;92	data->ext_end = nr - 1;93}94 95static void96locate_record(struct ccw1 * ccw, struct LO_fba_data *data, int rw,97	      int block_nr, int block_ct)98{99	ccw->cmd_code = DASD_FBA_CCW_LOCATE;100	ccw->flags = 0;101	ccw->count = 8;102	ccw->cda = virt_to_dma32(data);103	memset(data, 0, sizeof (struct LO_fba_data));104	if (rw == WRITE)105		data->operation.cmd = 0x5;106	else if (rw == READ)107		data->operation.cmd = 0x6;108	else109		data->operation.cmd = 0x8;110	data->blk_nr = block_nr;111	data->blk_ct = block_ct;112}113 114static int115dasd_fba_check_characteristics(struct dasd_device *device)116{117	struct dasd_fba_private *private = device->private;118	struct ccw_device *cdev = device->cdev;119	struct dasd_block *block;120	int readonly, rc;121 122	if (!private) {123		private = kzalloc(sizeof(*private), GFP_KERNEL | GFP_DMA);124		if (!private) {125			dev_warn(&device->cdev->dev,126				 "Allocating memory for private DASD "127				 "data failed\n");128			return -ENOMEM;129		}130		device->private = private;131	} else {132		memset(private, 0, sizeof(*private));133	}134	block = dasd_alloc_block();135	if (IS_ERR(block)) {136		DBF_EVENT_DEVID(DBF_WARNING, cdev, "%s", "could not allocate "137				"dasd block structure");138		device->private = NULL;139		kfree(private);140		return PTR_ERR(block);141	}142	device->block = block;143	block->base = device;144 145	/* Read Device Characteristics */146	rc = dasd_generic_read_dev_chars(device, DASD_FBA_MAGIC,147					 &private->rdc_data, 32);148	if (rc) {149		DBF_EVENT_DEVID(DBF_WARNING, cdev, "Read device "150				"characteristics returned error %d", rc);151		device->block = NULL;152		dasd_free_block(block);153		device->private = NULL;154		kfree(private);155		return rc;156	}157 158	device->default_expires = DASD_EXPIRES;159	device->default_retries = FBA_DEFAULT_RETRIES;160	dasd_path_set_opm(device, LPM_ANYPATH);161 162	readonly = dasd_device_is_ro(device);163	if (readonly)164		set_bit(DASD_FLAG_DEVICE_RO, &device->flags);165 166	/* FBA supports discard, set the according feature bit */167	dasd_set_feature(cdev, DASD_FEATURE_DISCARD, 1);168 169	dev_info(&device->cdev->dev,170		 "New FBA DASD %04X/%02X (CU %04X/%02X) with %d MB "171		 "and %d B/blk%s\n",172		 cdev->id.dev_type,173		 cdev->id.dev_model,174		 cdev->id.cu_type,175		 cdev->id.cu_model,176		 ((private->rdc_data.blk_bdsa *177		   (private->rdc_data.blk_size >> 9)) >> 11),178		 private->rdc_data.blk_size,179		 readonly ? ", read-only device" : "");180	return 0;181}182 183static int dasd_fba_do_analysis(struct dasd_block *block)184{185	struct dasd_fba_private *private = block->base->private;186	int sb, rc;187 188	rc = dasd_check_blocksize(private->rdc_data.blk_size);189	if (rc) {190		DBF_DEV_EVENT(DBF_WARNING, block->base, "unknown blocksize %d",191			    private->rdc_data.blk_size);192		return rc;193	}194	block->blocks = private->rdc_data.blk_bdsa;195	block->bp_block = private->rdc_data.blk_size;196	block->s2b_shift = 0;	/* bits to shift 512 to get a block */197	for (sb = 512; sb < private->rdc_data.blk_size; sb = sb << 1)198		block->s2b_shift++;199	return 0;200}201 202static int dasd_fba_fill_geometry(struct dasd_block *block,203				  struct hd_geometry *geo)204{205	if (dasd_check_blocksize(block->bp_block) != 0)206		return -EINVAL;207	geo->cylinders = (block->blocks << block->s2b_shift) >> 10;208	geo->heads = 16;209	geo->sectors = 128 >> block->s2b_shift;210	return 0;211}212 213static dasd_erp_fn_t214dasd_fba_erp_action(struct dasd_ccw_req * cqr)215{216	return dasd_default_erp_action;217}218 219static dasd_erp_fn_t220dasd_fba_erp_postaction(struct dasd_ccw_req * cqr)221{222	if (cqr->function == dasd_default_erp_action)223		return dasd_default_erp_postaction;224 225	DBF_DEV_EVENT(DBF_WARNING, cqr->startdev, "unknown ERP action %p",226		    cqr->function);227	return NULL;228}229 230static void dasd_fba_check_for_device_change(struct dasd_device *device,231					     struct dasd_ccw_req *cqr,232					     struct irb *irb)233{234	char mask;235 236	/* first of all check for state change pending interrupt */237	mask = DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP;238	if ((irb->scsw.cmd.dstat & mask) == mask)239		dasd_generic_handle_state_change(device);240};241 242 243/*244 * Builds a CCW with no data payload245 */246static void ccw_write_no_data(struct ccw1 *ccw)247{248	ccw->cmd_code = DASD_FBA_CCW_WRITE;249	ccw->flags |= CCW_FLAG_SLI;250	ccw->count = 0;251}252 253/*254 * Builds a CCW that writes only zeroes.255 */256static void ccw_write_zero(struct ccw1 *ccw, int count)257{258	ccw->cmd_code = DASD_FBA_CCW_WRITE;259	ccw->flags |= CCW_FLAG_SLI;260	ccw->count = count;261	ccw->cda = virt_to_dma32(dasd_fba_zero_page);262}263 264/*265 * Helper function to count the amount of necessary CCWs within a given range266 * with 4k alignment and command chaining in mind.267 */268static int count_ccws(sector_t first_rec, sector_t last_rec,269		      unsigned int blocks_per_page)270{271	sector_t wz_stop = 0, d_stop = 0;272	int cur_pos = 0;273	int count = 0;274 275	if (first_rec % blocks_per_page != 0) {276		wz_stop = first_rec + blocks_per_page -277			(first_rec % blocks_per_page) - 1;278		if (wz_stop > last_rec)279			wz_stop = last_rec;280		cur_pos = wz_stop - first_rec + 1;281		count++;282	}283 284	if (last_rec - (first_rec + cur_pos) + 1 >= blocks_per_page) {285		if ((last_rec - blocks_per_page + 1) % blocks_per_page != 0)286			d_stop = last_rec - ((last_rec - blocks_per_page + 1) %287					     blocks_per_page);288		else289			d_stop = last_rec;290 291		cur_pos += d_stop - (first_rec + cur_pos) + 1;292		count++;293	}294 295	if (cur_pos == 0 || first_rec + cur_pos - 1 < last_rec)296		count++;297 298	return count;299}300 301/*302 * This function builds a CCW request for block layer discard requests.303 * Each page in the z/VM hypervisor that represents certain records of an FBA304 * device will be padded with zeros. This is a special behaviour of the WRITE305 * command which is triggered when no data payload is added to the CCW.306 *307 * Note: Due to issues in some z/VM versions, we can't fully utilise this308 * special behaviour. We have to keep a 4k (or 8 block) alignment in mind to309 * work around those issues and write actual zeroes to the unaligned parts in310 * the request. This workaround might be removed in the future.311 */312static struct dasd_ccw_req *dasd_fba_build_cp_discard(313						struct dasd_device *memdev,314						struct dasd_block *block,315						struct request *req)316{317	struct LO_fba_data *LO_data;318	struct dasd_ccw_req *cqr;319	struct ccw1 *ccw;320 321	sector_t wz_stop = 0, d_stop = 0;322	sector_t first_rec, last_rec;323 324	unsigned int blksize = block->bp_block;325	unsigned int blocks_per_page;326	int wz_count = 0;327	int d_count = 0;328	int cur_pos = 0; /* Current position within the extent */329	int count = 0;330	int cplength;331	int datasize;332	int nr_ccws;333 334	first_rec = blk_rq_pos(req) >> block->s2b_shift;335	last_rec =336		(blk_rq_pos(req) + blk_rq_sectors(req) - 1) >> block->s2b_shift;337	count = last_rec - first_rec + 1;338 339	blocks_per_page = BLOCKS_PER_PAGE(blksize);340	nr_ccws = count_ccws(first_rec, last_rec, blocks_per_page);341 342	/* define extent + nr_ccws * locate record + nr_ccws * single CCW */343	cplength = 1 + 2 * nr_ccws;344	datasize = sizeof(struct DE_fba_data) +345		nr_ccws * (sizeof(struct LO_fba_data) + sizeof(struct ccw1));346 347	cqr = dasd_smalloc_request(DASD_FBA_MAGIC, cplength, datasize, memdev,348				   blk_mq_rq_to_pdu(req));349	if (IS_ERR(cqr))350		return cqr;351 352	ccw = cqr->cpaddr;353 354	define_extent(ccw++, cqr->data, WRITE, blksize, first_rec, count);355	LO_data = cqr->data + sizeof(struct DE_fba_data);356 357	/* First part is not aligned. Calculate range to write zeroes. */358	if (first_rec % blocks_per_page != 0) {359		wz_stop = first_rec + blocks_per_page -360			(first_rec % blocks_per_page) - 1;361		if (wz_stop > last_rec)362			wz_stop = last_rec;363		wz_count = wz_stop - first_rec + 1;364 365		ccw[-1].flags |= CCW_FLAG_CC;366		locate_record(ccw++, LO_data++, WRITE, cur_pos, wz_count);367 368		ccw[-1].flags |= CCW_FLAG_CC;369		ccw_write_zero(ccw++, wz_count * blksize);370 371		cur_pos = wz_count;372	}373 374	/* We can do proper discard when we've got at least blocks_per_page blocks. */375	if (last_rec - (first_rec + cur_pos) + 1 >= blocks_per_page) {376		/* is last record at page boundary? */377		if ((last_rec - blocks_per_page + 1) % blocks_per_page != 0)378			d_stop = last_rec - ((last_rec - blocks_per_page + 1) %379					     blocks_per_page);380		else381			d_stop = last_rec;382 383		d_count = d_stop - (first_rec + cur_pos) + 1;384 385		ccw[-1].flags |= CCW_FLAG_CC;386		locate_record(ccw++, LO_data++, WRITE, cur_pos, d_count);387 388		ccw[-1].flags |= CCW_FLAG_CC;389		ccw_write_no_data(ccw++);390 391		cur_pos += d_count;392	}393 394	/* We might still have some bits left which need to be zeroed. */395	if (cur_pos == 0 || first_rec + cur_pos - 1 < last_rec) {396		if (d_stop != 0)397			wz_count = last_rec - d_stop;398		else if (wz_stop != 0)399			wz_count = last_rec - wz_stop;400		else401			wz_count = count;402 403		ccw[-1].flags |= CCW_FLAG_CC;404		locate_record(ccw++, LO_data++, WRITE, cur_pos, wz_count);405 406		ccw[-1].flags |= CCW_FLAG_CC;407		ccw_write_zero(ccw++, wz_count * blksize);408	}409 410	if (blk_noretry_request(req) ||411	    block->base->features & DASD_FEATURE_FAILFAST)412		set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags);413 414	cqr->startdev = memdev;415	cqr->memdev = memdev;416	cqr->block = block;417	cqr->expires = memdev->default_expires * HZ;	/* default 5 minutes */418	cqr->retries = memdev->default_retries;419	cqr->buildclk = get_tod_clock();420	cqr->status = DASD_CQR_FILLED;421 422	return cqr;423}424 425static struct dasd_ccw_req *dasd_fba_build_cp_regular(426						struct dasd_device *memdev,427						struct dasd_block *block,428						struct request *req)429{430	struct dasd_fba_private *private = block->base->private;431	dma64_t *idaws;432	struct LO_fba_data *LO_data;433	struct dasd_ccw_req *cqr;434	struct ccw1 *ccw;435	struct req_iterator iter;436	struct bio_vec bv;437	char *dst;438	int count, cidaw, cplength, datasize;439	sector_t recid, first_rec, last_rec;440	unsigned int blksize, off;441	unsigned char cmd;442 443	if (rq_data_dir(req) == READ) {444		cmd = DASD_FBA_CCW_READ;445	} else if (rq_data_dir(req) == WRITE) {446		cmd = DASD_FBA_CCW_WRITE;447	} else448		return ERR_PTR(-EINVAL);449	blksize = block->bp_block;450	/* Calculate record id of first and last block. */451	first_rec = blk_rq_pos(req) >> block->s2b_shift;452	last_rec =453		(blk_rq_pos(req) + blk_rq_sectors(req) - 1) >> block->s2b_shift;454	/* Check struct bio and count the number of blocks for the request. */455	count = 0;456	cidaw = 0;457	rq_for_each_segment(bv, req, iter) {458		if (bv.bv_len & (blksize - 1))459			/* Fba can only do full blocks. */460			return ERR_PTR(-EINVAL);461		count += bv.bv_len >> (block->s2b_shift + 9);462		if (idal_is_needed (page_address(bv.bv_page), bv.bv_len))463			cidaw += bv.bv_len / blksize;464	}465	/* Paranoia. */466	if (count != last_rec - first_rec + 1)467		return ERR_PTR(-EINVAL);468	/* 1x define extent + 1x locate record + number of blocks */469	cplength = 2 + count;470	/* 1x define extent + 1x locate record */471	datasize = sizeof(struct DE_fba_data) + sizeof(struct LO_fba_data) +472		cidaw * sizeof(unsigned long);473	/*474	 * Find out number of additional locate record ccws if the device475	 * can't do data chaining.476	 */477	if (private->rdc_data.mode.bits.data_chain == 0) {478		cplength += count - 1;479		datasize += (count - 1)*sizeof(struct LO_fba_data);480	}481	/* Allocate the ccw request. */482	cqr = dasd_smalloc_request(DASD_FBA_MAGIC, cplength, datasize, memdev,483				   blk_mq_rq_to_pdu(req));484	if (IS_ERR(cqr))485		return cqr;486	ccw = cqr->cpaddr;487	/* First ccw is define extent. */488	define_extent(ccw++, cqr->data, rq_data_dir(req),489		      block->bp_block, blk_rq_pos(req), blk_rq_sectors(req));490	/* Build locate_record + read/write ccws. */491	idaws = (dma64_t *)(cqr->data + sizeof(struct DE_fba_data));492	LO_data = (struct LO_fba_data *) (idaws + cidaw);493	/* Locate record for all blocks for smart devices. */494	if (private->rdc_data.mode.bits.data_chain != 0) {495		ccw[-1].flags |= CCW_FLAG_CC;496		locate_record(ccw++, LO_data++, rq_data_dir(req), 0, count);497	}498	recid = first_rec;499	rq_for_each_segment(bv, req, iter) {500		dst = bvec_virt(&bv);501		if (dasd_page_cache) {502			char *copy = kmem_cache_alloc(dasd_page_cache,503						      GFP_DMA | __GFP_NOWARN);504			if (copy && rq_data_dir(req) == WRITE)505				memcpy(copy + bv.bv_offset, dst, bv.bv_len);506			if (copy)507				dst = copy + bv.bv_offset;508		}509		for (off = 0; off < bv.bv_len; off += blksize) {510			/* Locate record for stupid devices. */511			if (private->rdc_data.mode.bits.data_chain == 0) {512				ccw[-1].flags |= CCW_FLAG_CC;513				locate_record(ccw, LO_data++,514					      rq_data_dir(req),515					      recid - first_rec, 1);516				ccw->flags = CCW_FLAG_CC;517				ccw++;518			} else {519				if (recid > first_rec)520					ccw[-1].flags |= CCW_FLAG_DC;521				else522					ccw[-1].flags |= CCW_FLAG_CC;523			}524			ccw->cmd_code = cmd;525			ccw->count = block->bp_block;526			if (idal_is_needed(dst, blksize)) {527				ccw->cda = virt_to_dma32(idaws);528				ccw->flags = CCW_FLAG_IDA;529				idaws = idal_create_words(idaws, dst, blksize);530			} else {531				ccw->cda = virt_to_dma32(dst);532				ccw->flags = 0;533			}534			ccw++;535			dst += blksize;536			recid++;537		}538	}539	if (blk_noretry_request(req) ||540	    block->base->features & DASD_FEATURE_FAILFAST)541		set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags);542	cqr->startdev = memdev;543	cqr->memdev = memdev;544	cqr->block = block;545	cqr->expires = memdev->default_expires * HZ;	/* default 5 minutes */546	cqr->retries = memdev->default_retries;547	cqr->buildclk = get_tod_clock();548	cqr->status = DASD_CQR_FILLED;549	return cqr;550}551 552static struct dasd_ccw_req *dasd_fba_build_cp(struct dasd_device *memdev,553					      struct dasd_block *block,554					      struct request *req)555{556	if (req_op(req) == REQ_OP_DISCARD || req_op(req) == REQ_OP_WRITE_ZEROES)557		return dasd_fba_build_cp_discard(memdev, block, req);558	else559		return dasd_fba_build_cp_regular(memdev, block, req);560}561 562static int563dasd_fba_free_cp(struct dasd_ccw_req *cqr, struct request *req)564{565	struct dasd_fba_private *private = cqr->block->base->private;566	struct ccw1 *ccw;567	struct req_iterator iter;568	struct bio_vec bv;569	char *dst, *cda;570	unsigned int blksize, off;571	int status;572 573	if (!dasd_page_cache)574		goto out;575	blksize = cqr->block->bp_block;576	ccw = cqr->cpaddr;577	/* Skip over define extent & locate record. */578	ccw++;579	if (private->rdc_data.mode.bits.data_chain != 0)580		ccw++;581	rq_for_each_segment(bv, req, iter) {582		dst = bvec_virt(&bv);583		for (off = 0; off < bv.bv_len; off += blksize) {584			/* Skip locate record. */585			if (private->rdc_data.mode.bits.data_chain == 0)586				ccw++;587			if (dst) {588				if (ccw->flags & CCW_FLAG_IDA)589					cda = dma64_to_virt(*((dma64_t *)dma32_to_virt(ccw->cda)));590				else591					cda = dma32_to_virt(ccw->cda);592				if (dst != cda) {593					if (rq_data_dir(req) == READ)594						memcpy(dst, cda, bv.bv_len);595					kmem_cache_free(dasd_page_cache,596					    (void *)((addr_t)cda & PAGE_MASK));597				}598				dst = NULL;599			}600			ccw++;601		}602	}603out:604	status = cqr->status == DASD_CQR_DONE;605	dasd_sfree_request(cqr, cqr->memdev);606	return status;607}608 609static void dasd_fba_handle_terminated_request(struct dasd_ccw_req *cqr)610{611	if (cqr->retries < 0)612		cqr->status = DASD_CQR_FAILED;613	else614		cqr->status = DASD_CQR_FILLED;615};616 617static int618dasd_fba_fill_info(struct dasd_device * device,619		   struct dasd_information2_t * info)620{621	struct dasd_fba_private *private = device->private;622 623	info->label_block = 1;624	info->FBA_layout = 1;625	info->format = DASD_FORMAT_LDL;626	info->characteristics_size = sizeof(private->rdc_data);627	memcpy(info->characteristics, &private->rdc_data,628	       sizeof(private->rdc_data));629	info->confdata_size = 0;630	return 0;631}632 633static void634dasd_fba_dump_sense_dbf(struct dasd_device *device, struct irb *irb,635			char *reason)636{637	u64 *sense;638 639	sense = (u64 *) dasd_get_sense(irb);640	if (sense) {641		DBF_DEV_EVENT(DBF_EMERG, device,642			      "%s: %s %02x%02x%02x %016llx %016llx %016llx "643			      "%016llx", reason,644			      scsw_is_tm(&irb->scsw) ? "t" : "c",645			      scsw_cc(&irb->scsw), scsw_cstat(&irb->scsw),646			      scsw_dstat(&irb->scsw), sense[0], sense[1],647			      sense[2], sense[3]);648	} else {649		DBF_DEV_EVENT(DBF_EMERG, device, "%s",650			      "SORRY - NO VALID SENSE AVAILABLE\n");651	}652}653 654 655static void656dasd_fba_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req,657		    struct irb *irb)658{659	struct ccw1 *act, *end, *last;660	int len, sl, sct, count;661	struct device *dev;662	char *page;663 664	dev = &device->cdev->dev;665 666	page = (char *) get_zeroed_page(GFP_ATOMIC);667	if (page == NULL) {668		DBF_DEV_EVENT(DBF_WARNING, device, "%s",669			      "No memory to dump sense data");670		return;671	}672	len = sprintf(page, "I/O status report:\n");673	len += sprintf(page + len, "in req: %px CS: 0x%02X DS: 0x%02X\n",674		       req, irb->scsw.cmd.cstat, irb->scsw.cmd.dstat);675	len += sprintf(page + len, "Failing CCW: %px\n",676		       (void *)(u64)dma32_to_u32(irb->scsw.cmd.cpa));677	if (irb->esw.esw0.erw.cons) {678		for (sl = 0; sl < 4; sl++) {679			len += sprintf(page + len, "Sense(hex) %2d-%2d:",680				       (8 * sl), ((8 * sl) + 7));681 682			for (sct = 0; sct < 8; sct++) {683				len += sprintf(page + len, " %02x",684					       irb->ecw[8 * sl + sct]);685			}686			len += sprintf(page + len, "\n");687		}688	} else {689		len += sprintf(page + len, "SORRY - NO VALID SENSE AVAILABLE\n");690	}691	dev_err(dev, "%s", page);692 693	/* dump the Channel Program */694	/* print first CCWs (maximum 8) */695	act = req->cpaddr;696	for (last = act; last->flags & (CCW_FLAG_CC | CCW_FLAG_DC); last++);697	end = min(act + 8, last);698	len = sprintf(page, "Related CP in req: %px\n", req);699	while (act <= end) {700		len += sprintf(page + len, "CCW %px: %08X %08X DAT:",701			       act, ((int *) act)[0], ((int *) act)[1]);702		for (count = 0; count < 32 && count < act->count;703		     count += sizeof(int))704			len += sprintf(page + len, " %08X",705				       ((int *)dma32_to_virt(act->cda))706				       [(count>>2)]);707		len += sprintf(page + len, "\n");708		act++;709	}710	dev_err(dev, "%s", page);711 712	/* print failing CCW area */713	len = 0;714	if (act < ((struct ccw1 *)dma32_to_virt(irb->scsw.cmd.cpa)) - 2) {715		act = ((struct ccw1 *)dma32_to_virt(irb->scsw.cmd.cpa)) - 2;716		len += sprintf(page + len, "......\n");717	}718	end = min((struct ccw1 *)dma32_to_virt(irb->scsw.cmd.cpa) + 2, last);719	while (act <= end) {720		len += sprintf(page + len, "CCW %px: %08X %08X DAT:",721			       act, ((int *) act)[0], ((int *) act)[1]);722		for (count = 0; count < 32 && count < act->count;723		     count += sizeof(int))724			len += sprintf(page + len, " %08X",725				       ((int *)dma32_to_virt(act->cda))726				       [(count>>2)]);727		len += sprintf(page + len, "\n");728		act++;729	}730 731	/* print last CCWs */732	if (act <  last - 2) {733		act = last - 2;734		len += sprintf(page + len, "......\n");735	}736	while (act <= last) {737		len += sprintf(page + len, "CCW %px: %08X %08X DAT:",738			       act, ((int *) act)[0], ((int *) act)[1]);739		for (count = 0; count < 32 && count < act->count;740		     count += sizeof(int))741			len += sprintf(page + len, " %08X",742				       ((int *)dma32_to_virt(act->cda))743				       [(count>>2)]);744		len += sprintf(page + len, "\n");745		act++;746	}747	if (len > 0)748		dev_err(dev, "%s", page);749	free_page((unsigned long) page);750}751 752static unsigned int dasd_fba_max_sectors(struct dasd_block *block)753{754	return DASD_FBA_MAX_BLOCKS << block->s2b_shift;755}756 757static int dasd_fba_pe_handler(struct dasd_device *device,758			       __u8 tbvpm, __u8 fcsecpm)759{760	return dasd_generic_verify_path(device, tbvpm);761}762 763static struct dasd_discipline dasd_fba_discipline = {764	.owner = THIS_MODULE,765	.name = "FBA ",766	.ebcname = "FBA ",767	.has_discard = true,768	.check_device = dasd_fba_check_characteristics,769	.do_analysis = dasd_fba_do_analysis,770	.pe_handler = dasd_fba_pe_handler,771	.max_sectors = dasd_fba_max_sectors,772	.fill_geometry = dasd_fba_fill_geometry,773	.start_IO = dasd_start_IO,774	.term_IO = dasd_term_IO,775	.handle_terminated_request = dasd_fba_handle_terminated_request,776	.erp_action = dasd_fba_erp_action,777	.erp_postaction = dasd_fba_erp_postaction,778	.check_for_device_change = dasd_fba_check_for_device_change,779	.build_cp = dasd_fba_build_cp,780	.free_cp = dasd_fba_free_cp,781	.dump_sense = dasd_fba_dump_sense,782	.dump_sense_dbf = dasd_fba_dump_sense_dbf,783	.fill_info = dasd_fba_fill_info,784};785 786static int __init787dasd_fba_init(void)788{789	int ret;790 791	ASCEBC(dasd_fba_discipline.ebcname, 4);792 793	dasd_fba_zero_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);794	if (!dasd_fba_zero_page)795		return -ENOMEM;796 797	ret = ccw_driver_register(&dasd_fba_driver);798	if (!ret)799		wait_for_device_probe();800 801	return ret;802}803 804static void __exit805dasd_fba_cleanup(void)806{807	ccw_driver_unregister(&dasd_fba_driver);808	free_page((unsigned long)dasd_fba_zero_page);809}810 811module_init(dasd_fba_init);812module_exit(dasd_fba_cleanup);813