📄 dasd_diag.c
字号:
/* * 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 + -