dasd_eckd.c

来自「linux 内核源代码」· C语言 代码 · 共 1,829 行 · 第 1/4 页

C
1,829
字号
		return dasd_eckd_end_analysis(device);}static intdasd_eckd_fill_geometry(struct dasd_device *device, struct hd_geometry *geo){	struct dasd_eckd_private *private;	private = (struct dasd_eckd_private *) device->private;	if (dasd_check_blocksize(device->bp_block) == 0) {		geo->sectors = recs_per_track(&private->rdc_data,					      0, device->bp_block);	}	geo->cylinders = private->rdc_data.no_cyl;	geo->heads = private->rdc_data.trk_per_cyl;	return 0;}static struct dasd_ccw_req *dasd_eckd_format_device(struct dasd_device * device,			struct format_data_t * fdata){	struct dasd_eckd_private *private;	struct dasd_ccw_req *fcp;	struct eckd_count *ect;	struct ccw1 *ccw;	void *data;	int rpt, cyl, head;	int cplength, datasize;	int i;	private = (struct dasd_eckd_private *) device->private;	rpt = recs_per_track(&private->rdc_data, 0, fdata->blksize);	cyl = fdata->start_unit / private->rdc_data.trk_per_cyl;	head = fdata->start_unit % private->rdc_data.trk_per_cyl;	/* Sanity checks. */	if (fdata->start_unit >=	    (private->rdc_data.no_cyl * private->rdc_data.trk_per_cyl)) {		DEV_MESSAGE(KERN_INFO, device, "Track no %d too big!",			    fdata->start_unit);		return ERR_PTR(-EINVAL);	}	if (fdata->start_unit > fdata->stop_unit) {		DEV_MESSAGE(KERN_INFO, device, "Track %d reached! ending.",			    fdata->start_unit);		return ERR_PTR(-EINVAL);	}	if (dasd_check_blocksize(fdata->blksize) != 0) {		DEV_MESSAGE(KERN_WARNING, device,			    "Invalid blocksize %d...terminating!",			    fdata->blksize);		return ERR_PTR(-EINVAL);	}	/*	 * fdata->intensity is a bit string that tells us what to do:	 *   Bit 0: write record zero	 *   Bit 1: write home address, currently not supported	 *   Bit 2: invalidate tracks	 *   Bit 3: use OS/390 compatible disk layout (cdl)	 * Only some bit combinations do make sense.	 */	switch (fdata->intensity) {	case 0x00:	/* Normal format */	case 0x08:	/* Normal format, use cdl. */		cplength = 2 + rpt;		datasize = sizeof(struct DE_eckd_data) +			sizeof(struct LO_eckd_data) +			rpt * sizeof(struct eckd_count);		break;	case 0x01:	/* Write record zero and format track. */	case 0x09:	/* Write record zero and format track, use cdl. */		cplength = 3 + rpt;		datasize = sizeof(struct DE_eckd_data) +			sizeof(struct LO_eckd_data) +			sizeof(struct eckd_count) +			rpt * sizeof(struct eckd_count);		break;	case 0x04:	/* Invalidate track. */	case 0x0c:	/* Invalidate track, use cdl. */		cplength = 3;		datasize = sizeof(struct DE_eckd_data) +			sizeof(struct LO_eckd_data) +			sizeof(struct eckd_count);		break;	default:		DEV_MESSAGE(KERN_WARNING, device, "Invalid flags 0x%x.",			    fdata->intensity);		return ERR_PTR(-EINVAL);	}	/* Allocate the format ccw request. */	fcp = dasd_smalloc_request(dasd_eckd_discipline.name,				   cplength, datasize, device);	if (IS_ERR(fcp))		return fcp;	data = fcp->data;	ccw = fcp->cpaddr;	switch (fdata->intensity & ~0x08) {	case 0x00: /* Normal format. */		define_extent(ccw++, (struct DE_eckd_data *) data,			      fdata->start_unit, fdata->start_unit,			      DASD_ECKD_CCW_WRITE_CKD, device);		data += sizeof(struct DE_eckd_data);		ccw[-1].flags |= CCW_FLAG_CC;		locate_record(ccw++, (struct LO_eckd_data *) data,			      fdata->start_unit, 0, rpt,			      DASD_ECKD_CCW_WRITE_CKD, device,			      fdata->blksize);		data += sizeof(struct LO_eckd_data);		break;	case 0x01: /* Write record zero + format track. */		define_extent(ccw++, (struct DE_eckd_data *) data,			      fdata->start_unit, fdata->start_unit,			      DASD_ECKD_CCW_WRITE_RECORD_ZERO,			      device);		data += sizeof(struct DE_eckd_data);		ccw[-1].flags |= CCW_FLAG_CC;		locate_record(ccw++, (struct LO_eckd_data *) data,			      fdata->start_unit, 0, rpt + 1,			      DASD_ECKD_CCW_WRITE_RECORD_ZERO, device,			      device->bp_block);		data += sizeof(struct LO_eckd_data);		break;	case 0x04: /* Invalidate track. */		define_extent(ccw++, (struct DE_eckd_data *) data,			      fdata->start_unit, fdata->start_unit,			      DASD_ECKD_CCW_WRITE_CKD, device);		data += sizeof(struct DE_eckd_data);		ccw[-1].flags |= CCW_FLAG_CC;		locate_record(ccw++, (struct LO_eckd_data *) data,			      fdata->start_unit, 0, 1,			      DASD_ECKD_CCW_WRITE_CKD, device, 8);		data += sizeof(struct LO_eckd_data);		break;	}	if (fdata->intensity & 0x01) {	/* write record zero */		ect = (struct eckd_count *) data;		data += sizeof(struct eckd_count);		ect->cyl = cyl;		ect->head = head;		ect->record = 0;		ect->kl = 0;		ect->dl = 8;		ccw[-1].flags |= CCW_FLAG_CC;		ccw->cmd_code = DASD_ECKD_CCW_WRITE_RECORD_ZERO;		ccw->flags = CCW_FLAG_SLI;		ccw->count = 8;		ccw->cda = (__u32)(addr_t) ect;		ccw++;	}	if ((fdata->intensity & ~0x08) & 0x04) {	/* erase track */		ect = (struct eckd_count *) data;		data += sizeof(struct eckd_count);		ect->cyl = cyl;		ect->head = head;		ect->record = 1;		ect->kl = 0;		ect->dl = 0;		ccw[-1].flags |= CCW_FLAG_CC;		ccw->cmd_code = DASD_ECKD_CCW_WRITE_CKD;		ccw->flags = CCW_FLAG_SLI;		ccw->count = 8;		ccw->cda = (__u32)(addr_t) ect;	} else {		/* write remaining records */		for (i = 0; i < rpt; i++) {			ect = (struct eckd_count *) data;			data += sizeof(struct eckd_count);			ect->cyl = cyl;			ect->head = head;			ect->record = i + 1;			ect->kl = 0;			ect->dl = fdata->blksize;			/* Check for special tracks 0-1 when formatting CDL */			if ((fdata->intensity & 0x08) &&			    fdata->start_unit == 0) {				if (i < 3) {					ect->kl = 4;					ect->dl = sizes_trk0[i] - 4;				}			}			if ((fdata->intensity & 0x08) &&			    fdata->start_unit == 1) {				ect->kl = 44;				ect->dl = LABEL_SIZE - 44;			}			ccw[-1].flags |= CCW_FLAG_CC;			ccw->cmd_code = DASD_ECKD_CCW_WRITE_CKD;			ccw->flags = CCW_FLAG_SLI;			ccw->count = 8;			ccw->cda = (__u32)(addr_t) ect;			ccw++;		}	}	fcp->device = device;	fcp->retries = 2;	/* set retry counter to enable ERP */	fcp->buildclk = get_clock();	fcp->status = DASD_CQR_FILLED;	return fcp;}static dasd_era_tdasd_eckd_examine_error(struct dasd_ccw_req * cqr, struct irb * irb){	struct dasd_device *device = (struct dasd_device *) cqr->device;	struct ccw_device *cdev = device->cdev;	if (irb->scsw.cstat == 0x00 &&	    irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END))		return dasd_era_none;	switch (cdev->id.cu_type) {	case 0x3990:	case 0x2105:	case 0x2107:	case 0x1750:		return dasd_3990_erp_examine(cqr, irb);	case 0x9343:		return dasd_9343_erp_examine(cqr, irb);	case 0x3880:	default:		DEV_MESSAGE(KERN_WARNING, device, "%s",			    "default (unknown CU type) - RECOVERABLE return");		return dasd_era_recover;	}}static dasd_erp_fn_tdasd_eckd_erp_action(struct dasd_ccw_req * cqr){	struct dasd_device *device = (struct dasd_device *) cqr->device;	struct ccw_device *cdev = device->cdev;	switch (cdev->id.cu_type) {	case 0x3990:	case 0x2105:	case 0x2107:	case 0x1750:		return dasd_3990_erp_action;	case 0x9343:	case 0x3880:	default:		return dasd_default_erp_action;	}}static dasd_erp_fn_tdasd_eckd_erp_postaction(struct dasd_ccw_req * cqr){	return dasd_default_erp_postaction;}static struct dasd_ccw_req *dasd_eckd_build_cp(struct dasd_device * device, struct request *req){	struct dasd_eckd_private *private;	unsigned long *idaws;	struct LO_eckd_data *LO_data;	struct dasd_ccw_req *cqr;	struct ccw1 *ccw;	struct req_iterator iter;	struct bio_vec *bv;	char *dst;	unsigned int blksize, blk_per_trk, off;	int count, cidaw, cplength, datasize;	sector_t recid, first_rec, last_rec;	sector_t first_trk, last_trk;	unsigned int first_offs, last_offs;	unsigned char cmd, rcmd;	private = (struct dasd_eckd_private *) device->private;	if (rq_data_dir(req) == READ)		cmd = DASD_ECKD_CCW_READ_MT;	else if (rq_data_dir(req) == WRITE)		cmd = DASD_ECKD_CCW_WRITE_MT;	else		return ERR_PTR(-EINVAL);	/* Calculate number of blocks/records per track. */	blksize = device->bp_block;	blk_per_trk = recs_per_track(&private->rdc_data, 0, blksize);	/* Calculate record id of first and last block. */	first_rec = first_trk = req->sector >> device->s2b_shift;	first_offs = sector_div(first_trk, blk_per_trk);	last_rec = last_trk =		(req->sector + req->nr_sectors - 1) >> device->s2b_shift;	last_offs = sector_div(last_trk, blk_per_trk);	/* Check struct bio and count the number of blocks for the request. */	count = 0;	cidaw = 0;	rq_for_each_segment(bv, req, iter) {		if (bv->bv_len & (blksize - 1))			/* Eckd can only do full blocks. */			return ERR_PTR(-EINVAL);		count += bv->bv_len >> (device->s2b_shift + 9);#if defined(CONFIG_64BIT)		if (idal_is_needed (page_address(bv->bv_page), bv->bv_len))			cidaw += bv->bv_len >> (device->s2b_shift + 9);#endif	}	/* Paranoia. */	if (count != last_rec - first_rec + 1)		return ERR_PTR(-EINVAL);	/* 1x define extent + 1x locate record + number of blocks */	cplength = 2 + count;	/* 1x define extent + 1x locate record + cidaws*sizeof(long) */	datasize = sizeof(struct DE_eckd_data) + sizeof(struct LO_eckd_data) +		cidaw * sizeof(unsigned long);	/* Find out the number of additional locate record ccws for cdl. */	if (private->uses_cdl && first_rec < 2*blk_per_trk) {		if (last_rec >= 2*blk_per_trk)			count = 2*blk_per_trk - first_rec;		cplength += count;		datasize += count*sizeof(struct LO_eckd_data);	}	/* Allocate the ccw request. */	cqr = dasd_smalloc_request(dasd_eckd_discipline.name,				   cplength, datasize, device);	if (IS_ERR(cqr))		return cqr;	ccw = cqr->cpaddr;	/* First ccw is define extent. */	if (define_extent(ccw++, cqr->data, first_trk,			  last_trk, cmd, device) == -EAGAIN) {		/* Clock not in sync and XRC is enabled. Try again later. */		dasd_sfree_request(cqr, device);		return ERR_PTR(-EAGAIN);	}	/* Build locate_record+read/write/ccws. */	idaws = (unsigned long *) (cqr->data + sizeof(struct DE_eckd_data));	LO_data = (struct LO_eckd_data *) (idaws + cidaw);	recid = first_rec;	if (private->uses_cdl == 0 || recid > 2*blk_per_trk) {		/* Only standard blocks so there is just one locate record. */		ccw[-1].flags |= CCW_FLAG_CC;		locate_record(ccw++, LO_data++, first_trk, first_offs + 1,			      last_rec - recid + 1, cmd, device, blksize);	}	rq_for_each_segment(bv, req, iter) {		dst = page_address(bv->bv_page) + bv->bv_offset;		if (dasd_page_cache) {			char *copy = kmem_cache_alloc(dasd_page_cache,						      GFP_DMA | __GFP_NOWARN);			if (copy && rq_data_dir(req) == WRITE)				memcpy(copy + bv->bv_offset, dst, bv->bv_len);			if (copy)				dst = copy + bv->bv_offset;		}		for (off = 0; off < bv->bv_len; off += blksize) {			sector_t trkid = recid;			unsigned int recoffs = sector_div(trkid, blk_per_trk);			rcmd = cmd;			count = blksize;			/* Locate record for cdl special block ? */			if (private->uses_cdl && recid < 2*blk_per_trk) {				if (dasd_eckd_cdl_special(blk_per_trk, recid)){					rcmd |= 0x8;					count = dasd_eckd_cdl_reclen(recid);					if (count < blksize &&					    rq_data_dir(req) == READ)						memset(dst + count, 0xe5,						       blksize - count);				}				ccw[-1].flags |= CCW_FLAG_CC;				locate_record(ccw++, LO_data++,					      trkid, recoffs + 1,					      1, rcmd, device, count);			}			/* Locate record for standard blocks ? */			if (private->uses_cdl && recid == 2*blk_per_trk) {				ccw[-1].flags |= CCW_FLAG_CC;				locate_record(ccw++, LO_data++,					      trkid, recoffs + 1,					      last_rec - recid + 1,					      cmd, device, count);			}			/* Read/write ccw. */			ccw[-1].flags |= CCW_FLAG_CC;			ccw->cmd_code = rcmd;			ccw->count = count;			if (idal_is_needed(dst, blksize)) {				ccw->cda = (__u32)(addr_t) idaws;				ccw->flags = CCW_FLAG_IDA;				idaws = idal_create_words(idaws, dst, blksize);			} else {				ccw->cda = (__u32)(addr_t) dst;				ccw->flags = 0;			}			ccw++;			dst += blksize;			recid++;		}	}	if (req->cmd_flags & REQ_FAILFAST)		set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags);	cqr->device = device;	cqr->expires = 5 * 60 * HZ;	/* 5 minutes */	cqr->lpm = private->path_data.ppm;	cqr->retries = 256;	cqr->buildclk = get_clock();	cqr->status = DASD_CQR_FILLED;	return cqr;}static intdasd_eckd_free_cp(struct dasd_ccw_req *cqr, struct request *req){	struct dasd_eckd_private *private;	struct ccw1 *ccw;	struct req_iterator iter;	struct bio_vec *bv;	char *dst, *cda;	unsigned int blksize, blk_per_trk, off;	sector_t recid;	int status;	if (!dasd_page_cache)		goto out;	private = (struct dasd_eckd_private *) cqr->device->private;	blksize = cqr->device->bp_block;	blk_per_trk = recs_per_track(&private->rdc_data, 0, blksize);	recid = req->sector >> cqr->device->s2b_shift;	ccw = cqr->cpaddr;	/* Skip over define extent & locate record. */	ccw++;	if (private->uses_cdl == 0 || recid > 2*blk_per_trk)		ccw++;	rq_for_each_segment(bv, req, iter) {		dst = page_address(bv->bv_page) + bv->bv_offset;		for (off = 0; off < bv->bv_len; off += blksize) {			/* Skip locate record. */			if (private->uses_cdl && recid <= 2*blk_per_trk)				ccw++;			if (dst) {				if (ccw->flags & CCW_FLAG_IDA)					cda = *((char **)((addr_t) ccw->cda));				else					cda = (char *)((addr_t) ccw->cda);				if (dst != cda) {					if (rq_data_dir(req) == READ)						memcpy(dst, cda, bv->bv_len);					kmem_cache_free(dasd_page_cache,					    (void *)((addr_t)cda & PAGE_MASK));				}				dst = NULL;			}			ccw++;			recid++;		}	}out:	status = cqr->status == DASD_CQR_DONE;	dasd_sfree_request(cqr, cqr->device);	return status;}static int

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?