⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 dasd_diag.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 2 页
字号:
/*  * File...........: linux/drivers/s390/block/dasd_diag.c * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com> * Based on.......: linux/drivers/s390/block/mdisk.c * ...............: by Hartmunt Penner <hpenner@de.ibm.com> * Bugreports.to..: <Linux390@de.ibm.com> * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000 * * $Revision: 1.51 $ */#include <linux/config.h>#include <linux/stddef.h>#include <linux/kernel.h>#include <linux/slab.h>#include <linux/hdreg.h>#include <linux/bio.h>#include <linux/module.h>#include <linux/init.h>#include <linux/jiffies.h>#include <asm/dasd.h>#include <asm/debug.h>#include <asm/ebcdic.h>#include <asm/io.h>#include <asm/s390_ext.h>#include <asm/todclk.h>#include "dasd_int.h"#include "dasd_diag.h"#define PRINTK_HEADER "dasd(diag):"MODULE_LICENSE("GPL");/* The maximum number of blocks per request (max_blocks) is dependent on the * amount of storage that is available in the static I/O buffer for each * device. Currently each device gets 2 pages. We want to fit two requests * into the available memory so that we can immediately start the next if one * finishes. */#define DIAG_MAX_BLOCKS	(((2 * PAGE_SIZE - sizeof(struct dasd_ccw_req) - \			   sizeof(struct dasd_diag_req)) / \		           sizeof(struct dasd_diag_bio)) / 2)#define DIAG_MAX_RETRIES	32#define DIAG_TIMEOUT		50 * HZstruct dasd_discipline dasd_diag_discipline;struct dasd_diag_private {	struct dasd_diag_characteristics rdc_data;	struct dasd_diag_rw_io iob;	struct dasd_diag_init_io iib;	blocknum_t pt_block;};struct dasd_diag_req {	unsigned int block_count;	struct dasd_diag_bio bio[0];};static const u8 DASD_DIAG_CMS1[] = { 0xc3, 0xd4, 0xe2, 0xf1 };/* EBCDIC CMS1 *//* Perform DIAG250 call with block I/O parameter list iob (input and output) * and function code cmd. * In case of an exception return 3. Otherwise return result of bitwise OR of * resulting condition code and DIAG return code. */static __inline__ intdia250(void *iob, int cmd){	typedef union {		struct dasd_diag_init_io init_io;		struct dasd_diag_rw_io rw_io;	} addr_type;	int rc;	__asm__ __volatile__(#ifdef CONFIG_ARCH_S390X		"	lghi	%0,3\n"		"	lgr	0,%3\n"		"	diag	0,%2,0x250\n"		"0:	ipm	%0\n"		"	srl	%0,28\n"		"	or	%0,1\n"		"1:\n"		".section __ex_table,\"a\"\n"		"	.align 8\n"		"	.quad  0b,1b\n"		".previous\n"#else		"	lhi	%0,3\n"		"	lr	0,%3\n"		"	diag	0,%2,0x250\n"		"0:	ipm	%0\n"		"	srl	%0,28\n"		"	or	%0,1\n"		"1:\n"		".section __ex_table,\"a\"\n"		"	.align 4\n"		"	.long 0b,1b\n"		".previous\n"#endif		: "=&d" (rc), "=m" (*(addr_type *) iob)		: "d" (cmd), "d" (iob), "m" (*(addr_type *) iob)		: "0", "1", "cc");	return rc;}/* Initialize block I/O to DIAG device using the specified blocksize and * block offset. On success, return zero and set end_block to contain the * number of blocks on the device minus the specified offset. Return non-zero * otherwise. */static __inline__ intmdsk_init_io(struct dasd_device *device, unsigned int blocksize,	     blocknum_t offset, blocknum_t *end_block){	struct dasd_diag_private *private;	struct dasd_diag_init_io *iib;	int rc;	private = (struct dasd_diag_private *) device->private;	iib = &private->iib;	memset(iib, 0, sizeof (struct dasd_diag_init_io));	iib->dev_nr = _ccw_device_get_device_number(device->cdev);	iib->block_size = blocksize;	iib->offset = offset;	iib->flaga = DASD_DIAG_FLAGA_DEFAULT;	rc = dia250(iib, INIT_BIO);	if ((rc & 3) == 0 && end_block)		*end_block = iib->end_block;	return rc;}/* Remove block I/O environment for device. Return zero on success, non-zero * otherwise. */static __inline__ intmdsk_term_io(struct dasd_device * device){	struct dasd_diag_private *private;	struct dasd_diag_init_io *iib;	int rc;	private = (struct dasd_diag_private *) device->private;	iib = &private->iib;	memset(iib, 0, sizeof (struct dasd_diag_init_io));	iib->dev_nr = _ccw_device_get_device_number(device->cdev);	rc = dia250(iib, TERM_BIO);	return rc;}/* Error recovery for failed DIAG requests - try to reestablish the DIAG * environment. */static voiddasd_diag_erp(struct dasd_device *device){	int rc;	mdsk_term_io(device);	rc = mdsk_init_io(device, device->bp_block, 0, NULL);	if (rc)		DEV_MESSAGE(KERN_WARNING, device, "DIAG ERP unsuccessful, "			    "rc=%d", rc);}/* Start a given request at the device. Return zero on success, non-zero * otherwise. */static intdasd_start_diag(struct dasd_ccw_req * cqr){	struct dasd_device *device;	struct dasd_diag_private *private;	struct dasd_diag_req *dreq;	int rc;	device = cqr->device;	if (cqr->retries < 0) {		DEV_MESSAGE(KERN_WARNING, device, "DIAG start_IO: request %p "			    "- no retry left)", cqr);		cqr->status = DASD_CQR_FAILED;		return -EIO;	}	private = (struct dasd_diag_private *) device->private;	dreq = (struct dasd_diag_req *) cqr->data;	private->iob.dev_nr = _ccw_device_get_device_number(device->cdev);	private->iob.key = 0;	private->iob.flags = DASD_DIAG_RWFLAG_ASYNC;	private->iob.block_count = dreq->block_count;	private->iob.interrupt_params = (addr_t) cqr;	private->iob.bio_list = dreq->bio;	private->iob.flaga = DASD_DIAG_FLAGA_DEFAULT;	cqr->startclk = get_clock();	cqr->starttime = jiffies;	cqr->retries--;	rc = dia250(&private->iob, RW_BIO);	switch (rc) {	case 0: /* Synchronous I/O finished successfully */		cqr->stopclk = get_clock();		cqr->status = DASD_CQR_DONE;		/* Indicate to calling function that only a dasd_schedule_bh()		   and no timer is needed */                rc = -EACCES;		break;	case 8: /* Asynchronous I/O was started */		cqr->status = DASD_CQR_IN_IO;		rc = 0;		break;	default: /* Error condition */		cqr->status = DASD_CQR_QUEUED;		DEV_MESSAGE(KERN_WARNING, device, "dia250 returned rc=%d", rc);		dasd_diag_erp(device);		rc = -EIO;		break;	}	return rc;}/* Terminate given request at the device. */static intdasd_diag_term_IO(struct dasd_ccw_req * cqr){	struct dasd_device *device;	device = cqr->device;	mdsk_term_io(device);	mdsk_init_io(device, device->bp_block, 0, NULL);	cqr->status = DASD_CQR_CLEAR;	cqr->stopclk = get_clock();	dasd_schedule_bh(device);	return 0;}/* Handle external interruption. */static voiddasd_ext_handler(struct pt_regs *regs, __u16 code){	struct dasd_ccw_req *cqr, *next;	struct dasd_device *device;	unsigned long long expires;	unsigned long flags;	u8 int_code, status;	addr_t ip;	int rc;	int_code = *((u8 *) DASD_DIAG_LC_INT_CODE);	status = *((u8 *) DASD_DIAG_LC_INT_STATUS);	switch (int_code) {	case DASD_DIAG_CODE_31BIT:		ip = (addr_t) *((u32 *) DASD_DIAG_LC_INT_PARM_31BIT);		break;	case DASD_DIAG_CODE_64BIT:		ip = (addr_t) *((u64 *) DASD_DIAG_LC_INT_PARM_64BIT);		break;	default:		return;	}	if (!ip) {		/* no intparm: unsolicited interrupt */		MESSAGE(KERN_DEBUG, "%s", "caught unsolicited interrupt");		return;	}	cqr = (struct dasd_ccw_req *) ip;	device = (struct dasd_device *) cqr->device;	if (strncmp(device->discipline->ebcname, (char *) &cqr->magic, 4)) {		DEV_MESSAGE(KERN_WARNING, device,			    " magic number of dasd_ccw_req 0x%08X doesn't"			    " match discipline 0x%08X",			    cqr->magic, *(int *) (&device->discipline->name));		return;	}	/* get irq lock to modify request queue */	spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);	/* Check for a pending clear operation */	if (cqr->status == DASD_CQR_CLEAR) {		cqr->status = DASD_CQR_QUEUED;		dasd_clear_timer(device);		dasd_schedule_bh(device);		spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);		return;	}	cqr->stopclk = get_clock();	expires = 0;	if (status == 0) {		cqr->status = DASD_CQR_DONE;		/* Start first request on queue if possible -> fast_io. */		if (!list_empty(&device->ccw_queue)) {			next = list_entry(device->ccw_queue.next,					  struct dasd_ccw_req, list);			if (next->status == DASD_CQR_QUEUED) {				rc = dasd_start_diag(next);				if (rc == 0)					expires = next->expires;				else if (rc != -EACCES)					DEV_MESSAGE(KERN_WARNING, device, "%s",						    "Interrupt fastpath "						    "failed!");			}		}	} else {		cqr->status = DASD_CQR_QUEUED;		DEV_MESSAGE(KERN_WARNING, device, "interrupt status for "			    "request %p was %d (%d retries left)", cqr, status,			    cqr->retries);		dasd_diag_erp(device);	}	if (expires != 0)		dasd_set_timer(device, expires);	else		dasd_clear_timer(device);	dasd_schedule_bh(device);	spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);

⌨️ 快捷键说明

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