dasd_eckd.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,562 行 · 第 1/3 页

C
1,562
字号
	struct eckd_count *count_data;	struct LO_eckd_data *LO_data;	struct dasd_ccw_req *cqr;	struct ccw1 *ccw;	int cplength, datasize;	int i;	private = (struct dasd_eckd_private *) device->private;	cplength = 8;	datasize = sizeof(struct DE_eckd_data) + 2*sizeof(struct LO_eckd_data);	cqr = dasd_smalloc_request(dasd_eckd_discipline.name,				   cplength, datasize, device);	if (IS_ERR(cqr))		return cqr;	ccw = cqr->cpaddr;	/* Define extent for the first 3 tracks. */	define_extent(ccw++, cqr->data, 0, 2,		      DASD_ECKD_CCW_READ_COUNT, device);	LO_data = cqr->data + sizeof (struct DE_eckd_data);	/* Locate record for the first 4 records on track 0. */	ccw[-1].flags |= CCW_FLAG_CC;	locate_record(ccw++, LO_data++, 0, 0, 4,		      DASD_ECKD_CCW_READ_COUNT, device, 0);	count_data = private->count_area;	for (i = 0; i < 4; i++) {		ccw[-1].flags |= CCW_FLAG_CC;		ccw->cmd_code = DASD_ECKD_CCW_READ_COUNT;		ccw->flags = 0;		ccw->count = 8;		ccw->cda = (__u32)(addr_t) count_data;		ccw++;		count_data++;	}	/* Locate record for the first record on track 2. */	ccw[-1].flags |= CCW_FLAG_CC;	locate_record(ccw++, LO_data++, 2, 0, 1,		      DASD_ECKD_CCW_READ_COUNT, device, 0);	/* Read count ccw. */	ccw[-1].flags |= CCW_FLAG_CC;	ccw->cmd_code = DASD_ECKD_CCW_READ_COUNT;	ccw->flags = 0;	ccw->count = 8;	ccw->cda = (__u32)(addr_t) count_data;	cqr->device = device;	cqr->retries = 0;	cqr->buildclk = get_clock();	cqr->status = DASD_CQR_FILLED;	return cqr;}/* * This is the callback function for the init_analysis cqr. It saves * the status of the initial analysis ccw before it frees it and kicks * the device to continue the startup sequence. This will call * dasd_eckd_do_analysis again (if the devices has not been marked * for deletion in the meantime). */static voiddasd_eckd_analysis_callback(struct dasd_ccw_req *init_cqr, void *data){	struct dasd_eckd_private *private;	struct dasd_device *device;	device = init_cqr->device;	private = (struct dasd_eckd_private *) device->private;	private->init_cqr_status = init_cqr->status;	dasd_sfree_request(init_cqr, device);	dasd_kick_device(device);}static intdasd_eckd_start_analysis(struct dasd_device *device){	struct dasd_eckd_private *private;	struct dasd_ccw_req *init_cqr;	private = (struct dasd_eckd_private *) device->private;	init_cqr = dasd_eckd_analysis_ccw(device);	if (IS_ERR(init_cqr))		return PTR_ERR(init_cqr);	init_cqr->callback = dasd_eckd_analysis_callback;	init_cqr->callback_data = NULL;	init_cqr->expires = 5*HZ;	dasd_add_request_head(init_cqr);	return -EAGAIN;}static intdasd_eckd_end_analysis(struct dasd_device *device){	struct dasd_eckd_private *private;	struct eckd_count *count_area;	unsigned int sb, blk_per_trk;	int status, i;	private = (struct dasd_eckd_private *) device->private;	status = private->init_cqr_status;	private->init_cqr_status = -1;	if (status != DASD_CQR_DONE) {		DEV_MESSAGE(KERN_WARNING, device, "%s",			    "volume analysis returned unformatted disk");		return -EMEDIUMTYPE;	}	private->uses_cdl = 1;	/* Calculate number of blocks/records per track. */	blk_per_trk = recs_per_track(&private->rdc_data, 0, device->bp_block);	/* Check Track 0 for Compatible Disk Layout */	count_area = NULL;	for (i = 0; i < 3; i++) {		if (private->count_area[i].kl != 4 ||		    private->count_area[i].dl != dasd_eckd_cdl_reclen(i) - 4) {			private->uses_cdl = 0;			break;		}	}	if (i == 3)		count_area = &private->count_area[4];	if (private->uses_cdl == 0) {		for (i = 0; i < 5; i++) {			if ((private->count_area[i].kl != 0) ||			    (private->count_area[i].dl !=			     private->count_area[0].dl))				break;		}		if (i == 5)			count_area = &private->count_area[0];	} else {		if (private->count_area[3].record == 1)			DEV_MESSAGE(KERN_WARNING, device, "%s",				    "Trk 0: no records after VTOC!");	}	if (count_area != NULL && count_area->kl == 0) {		/* we found notthing violating our disk layout */		if (dasd_check_blocksize(count_area->dl) == 0)			device->bp_block = count_area->dl;	}	if (device->bp_block == 0) {		DEV_MESSAGE(KERN_WARNING, device, "%s",			    "Volume has incompatible disk layout");		return -EMEDIUMTYPE;	}	device->s2b_shift = 0;	/* bits to shift 512 to get a block */	for (sb = 512; sb < device->bp_block; sb = sb << 1)		device->s2b_shift++;	blk_per_trk = recs_per_track(&private->rdc_data, 0, device->bp_block);	device->blocks = (private->rdc_data.no_cyl *			  private->rdc_data.trk_per_cyl *			  blk_per_trk);	DEV_MESSAGE(KERN_INFO, device,		    "(%dkB blks): %dkB at %dkB/trk %s",		    (device->bp_block >> 10),		    ((private->rdc_data.no_cyl *		      private->rdc_data.trk_per_cyl *		      blk_per_trk * (device->bp_block >> 9)) >> 1),		    ((blk_per_trk * device->bp_block) >> 10), 		    private->uses_cdl ?		    "compatible disk layout" : "linux disk layout");	return 0;}static intdasd_eckd_do_analysis(struct dasd_device *device){	struct dasd_eckd_private *private;	private = (struct dasd_eckd_private *) device->private;	if (private->init_cqr_status < 0)		return dasd_eckd_start_analysis(device);	else		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) {		MESSAGE(KERN_WARNING, "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:		MESSAGE(KERN_WARNING, "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:		MESSAGE(KERN_WARNING, "%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:		/* Return dasd_9343_erp_action; */	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 bio *bio;	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;	int i;	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_bio(bio, req) {		bio_for_each_segment(bv, bio, i) {			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_ARCH_S390X)			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. */	define_extent(ccw++, cqr->data, first_trk, last_trk, cmd, device);	/* 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_bio(bio, req) bio_for_each_segment(bv, bio, i) {		dst = page_address(bv->bv_page) + 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;

⌨️ 快捷键说明

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