dasd_eckd.c
来自「优龙2410linux2.6.8内核源代码」· C语言 代码 · 共 1,550 行 · 第 1/3 页
C
1,550 行
/* * File...........: linux/drivers/s390/block/dasd_eckd.c * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com> * Horst Hummel <Horst.Hummel@de.ibm.com> * Carsten Otte <Cotte@de.ibm.com> * Martin Schwidefsky <schwidefsky@de.ibm.com> * Bugreports.to..: <Linux390@de.ibm.com> * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000 * * $Revision: 1.57 $ */#include <linux/config.h>#include <linux/stddef.h>#include <linux/kernel.h>#include <linux/slab.h>#include <linux/hdreg.h> /* HDIO_GETGEO */#include <linux/bio.h>#include <linux/module.h>#include <linux/init.h>#include <asm/debug.h>#include <asm/idals.h>#include <asm/ebcdic.h>#include <asm/io.h>#include <asm/todclk.h>#include <asm/uaccess.h>#include <asm/ccwdev.h>#include "dasd_int.h"#include "dasd_eckd.h"#ifdef PRINTK_HEADER#undef PRINTK_HEADER#endif /* PRINTK_HEADER */#define PRINTK_HEADER "dasd(eckd):"#define ECKD_C0(i) (i->home_bytes)#define ECKD_F(i) (i->formula)#define ECKD_F1(i) (ECKD_F(i)==0x01?(i->factors.f_0x01.f1):(i->factors.f_0x02.f1))#define ECKD_F2(i) (ECKD_F(i)==0x01?(i->factors.f_0x01.f2):(i->factors.f_0x02.f2))#define ECKD_F3(i) (ECKD_F(i)==0x01?(i->factors.f_0x01.f3):(i->factors.f_0x02.f3))#define ECKD_F4(i) (ECKD_F(i)==0x02?(i->factors.f_0x02.f4):0)#define ECKD_F5(i) (ECKD_F(i)==0x02?(i->factors.f_0x02.f5):0)#define ECKD_F6(i) (i->factor6)#define ECKD_F7(i) (i->factor7)#define ECKD_F8(i) (i->factor8)MODULE_LICENSE("GPL");static struct dasd_discipline dasd_eckd_discipline;struct dasd_eckd_private { struct dasd_eckd_characteristics rdc_data; struct dasd_eckd_confdata conf_data; struct eckd_count count_area[5]; int init_cqr_status; int uses_cdl; struct attrib_data_t attrib; /* e.g. cache operations */};/* The ccw bus type uses this table to find devices that it sends to * dasd_eckd_probe */static struct ccw_device_id dasd_eckd_ids[] = { { CCW_DEVICE_DEVTYPE (0x3990, 0, 0x3390, 0), driver_info: 0x1}, { CCW_DEVICE_DEVTYPE (0x2105, 0, 0x3390, 0), driver_info: 0x2}, { CCW_DEVICE_DEVTYPE (0x3880, 0, 0x3390, 0), driver_info: 0x3}, { CCW_DEVICE_DEVTYPE (0x3990, 0, 0x3380, 0), driver_info: 0x4}, { CCW_DEVICE_DEVTYPE (0x2105, 0, 0x3380, 0), driver_info: 0x5}, { CCW_DEVICE_DEVTYPE (0x9343, 0, 0x9345, 0), driver_info: 0x6}, { /* end of list */ },};MODULE_DEVICE_TABLE(ccw, dasd_eckd_ids);static struct ccw_driver dasd_eckd_driver; /* see below *//* initial attempt at a probe function. this can be simplified once * the other detection code is gone */static intdasd_eckd_probe (struct ccw_device *cdev){ int ret; ret = dasd_generic_probe (cdev, &dasd_eckd_discipline); if (ret) return ret; ccw_device_set_options(cdev, CCWDEV_DO_PATHGROUP | CCWDEV_ALLOW_FORCE); return 0;}static intdasd_eckd_set_online(struct ccw_device *cdev){ return dasd_generic_set_online (cdev, &dasd_eckd_discipline);}static struct ccw_driver dasd_eckd_driver = { .name = "dasd-eckd", .owner = THIS_MODULE, .ids = dasd_eckd_ids, .probe = dasd_eckd_probe, .remove = dasd_generic_remove, .set_offline = dasd_generic_set_offline, .set_online = dasd_eckd_set_online, .notify = dasd_generic_notify,};static const int sizes_trk0[] = { 28, 148, 84 };#define LABEL_SIZE 140static inline unsigned intround_up_multiple(unsigned int no, unsigned int mult){ int rem = no % mult; return (rem ? no - rem + mult : no);}static inline unsigned intceil_quot(unsigned int d1, unsigned int d2){ return (d1 + (d2 - 1)) / d2;}static inline intbytes_per_record(struct dasd_eckd_characteristics *rdc, int kl, int dl){ unsigned int fl1, fl2, int1, int2; int bpr; switch (rdc->formula) { case 0x01: fl1 = round_up_multiple(ECKD_F2(rdc) + dl, ECKD_F1(rdc)); fl2 = round_up_multiple(kl ? ECKD_F2(rdc) + kl : 0, ECKD_F1(rdc)); bpr = fl1 + fl2; break; case 0x02: int1 = ceil_quot(dl + ECKD_F6(rdc), ECKD_F5(rdc) << 1); int2 = ceil_quot(kl + ECKD_F6(rdc), ECKD_F5(rdc) << 1); fl1 = round_up_multiple(ECKD_F1(rdc) * ECKD_F2(rdc) + dl + ECKD_F6(rdc) + ECKD_F4(rdc) * int1, ECKD_F1(rdc)); fl2 = round_up_multiple(ECKD_F1(rdc) * ECKD_F3(rdc) + kl + ECKD_F6(rdc) + ECKD_F4(rdc) * int2, ECKD_F1(rdc)); bpr = fl1 + fl2; break; default: MESSAGE(KERN_ERR, "unknown formula%d", rdc->formula); bpr = 0; break; } return bpr;}static inline unsigned intbytes_per_track(struct dasd_eckd_characteristics *rdc){ return *(unsigned int *) (rdc->byte_per_track) >> 8;}static inline unsigned intrecs_per_track(struct dasd_eckd_characteristics * rdc, unsigned int kl, unsigned int dl){ int dn, kn; switch (rdc->dev_type) { case 0x3380: if (kl) return 1499 / (15 + 7 + ceil_quot(kl + 12, 32) + ceil_quot(dl + 12, 32)); else return 1499 / (15 + ceil_quot(dl + 12, 32)); case 0x3390: dn = ceil_quot(dl + 6, 232) + 1; if (kl) { kn = ceil_quot(kl + 6, 232) + 1; return 1729 / (10 + 9 + ceil_quot(kl + 6 * kn, 34) + 9 + ceil_quot(dl + 6 * dn, 34)); } else return 1729 / (10 + 9 + ceil_quot(dl + 6 * dn, 34)); case 0x9345: dn = ceil_quot(dl + 6, 232) + 1; if (kl) { kn = ceil_quot(kl + 6, 232) + 1; return 1420 / (18 + 7 + ceil_quot(kl + 6 * kn, 34) + ceil_quot(dl + 6 * dn, 34)); } else return 1420 / (18 + 7 + ceil_quot(dl + 6 * dn, 34)); } return 0;}static inline voidcheck_XRC (struct ccw1 *de_ccw, struct DE_eckd_data *data, struct dasd_device *device){ struct dasd_eckd_private *private; private = (struct dasd_eckd_private *) device->private; /* switch on System Time Stamp - needed for XRC Support */ if (private->rdc_data.facilities.XRC_supported) { data->ga_extended |= 0x08; /* switch on 'Time Stamp Valid' */ data->ga_extended |= 0x02; /* switch on 'Extended Parameter' */ data->ep_sys_time = get_clock (); de_ccw->count = sizeof (struct DE_eckd_data); de_ccw->flags |= CCW_FLAG_SLI; } return;} /* end check_XRC */static inline voiddefine_extent(struct ccw1 * ccw, struct DE_eckd_data * data, int trk, int totrk, int cmd, struct dasd_device * device){ struct dasd_eckd_private *private; struct ch_t geo, beg, end; private = (struct dasd_eckd_private *) device->private; ccw->cmd_code = DASD_ECKD_CCW_DEFINE_EXTENT; ccw->flags = 0; ccw->count = 16; ccw->cda = (__u32) __pa(data); memset(data, 0, sizeof (struct DE_eckd_data)); switch (cmd) { case DASD_ECKD_CCW_READ_HOME_ADDRESS: case DASD_ECKD_CCW_READ_RECORD_ZERO: case DASD_ECKD_CCW_READ: case DASD_ECKD_CCW_READ_MT: case DASD_ECKD_CCW_READ_CKD: case DASD_ECKD_CCW_READ_CKD_MT: case DASD_ECKD_CCW_READ_KD: case DASD_ECKD_CCW_READ_KD_MT: case DASD_ECKD_CCW_READ_COUNT: data->mask.perm = 0x1; data->attributes.operation = private->attrib.operation; break; case DASD_ECKD_CCW_WRITE: case DASD_ECKD_CCW_WRITE_MT: case DASD_ECKD_CCW_WRITE_KD: case DASD_ECKD_CCW_WRITE_KD_MT: data->mask.perm = 0x02; data->attributes.operation = private->attrib.operation; check_XRC (ccw, data, device); break; case DASD_ECKD_CCW_WRITE_CKD: case DASD_ECKD_CCW_WRITE_CKD_MT: data->attributes.operation = DASD_BYPASS_CACHE; check_XRC (ccw, data, device); break; case DASD_ECKD_CCW_ERASE: case DASD_ECKD_CCW_WRITE_HOME_ADDRESS: case DASD_ECKD_CCW_WRITE_RECORD_ZERO: data->mask.perm = 0x3; data->mask.auth = 0x1; data->attributes.operation = DASD_BYPASS_CACHE; check_XRC (ccw, data, device); break; default: MESSAGE(KERN_ERR, "unknown opcode 0x%x", cmd); break; } data->attributes.mode = 0x3; /* ECKD */ if (private->rdc_data.cu_type == 0x2105 && !(private->uses_cdl && trk < 2)) data->ga_extended |= 0x40; geo.cyl = private->rdc_data.no_cyl; geo.head = private->rdc_data.trk_per_cyl; beg.cyl = trk / geo.head; beg.head = trk % geo.head; end.cyl = totrk / geo.head; end.head = totrk % geo.head; /* check for sequential prestage - enhance cylinder range */ if (data->attributes.operation == DASD_SEQ_PRESTAGE || data->attributes.operation == DASD_SEQ_ACCESS) { if (end.cyl + private->attrib.nr_cyl < geo.cyl) end.cyl += private->attrib.nr_cyl; else end.cyl = (geo.cyl - 1); } data->beg_ext.cyl = beg.cyl; data->beg_ext.head = beg.head; data->end_ext.cyl = end.cyl; data->end_ext.head = end.head;}static inline voidlocate_record(struct ccw1 *ccw, struct LO_eckd_data *data, int trk, int rec_on_trk, int no_rec, int cmd, struct dasd_device * device, int reclen){ struct dasd_eckd_private *private; int sector; int dn, d; private = (struct dasd_eckd_private *) device->private; DBF_EVENT(DBF_INFO, "Locate: trk %d, rec %d, no_rec %d, cmd %d, reclen %d", trk, rec_on_trk, no_rec, cmd, reclen); ccw->cmd_code = DASD_ECKD_CCW_LOCATE_RECORD; ccw->flags = 0; ccw->count = 16; ccw->cda = (__u32) __pa(data); memset(data, 0, sizeof (struct LO_eckd_data)); sector = 0; if (rec_on_trk) { switch (private->rdc_data.dev_type) { case 0x3390: dn = ceil_quot(reclen + 6, 232); d = 9 + ceil_quot(reclen + 6 * (dn + 1), 34); sector = (49 + (rec_on_trk - 1) * (10 + d)) / 8; break; case 0x3380: d = 7 + ceil_quot(reclen + 12, 32); sector = (39 + (rec_on_trk - 1) * (8 + d)) / 7; break; } } data->sector = sector; data->count = no_rec; switch (cmd) { case DASD_ECKD_CCW_WRITE_HOME_ADDRESS: data->operation.orientation = 0x3; data->operation.operation = 0x03; break; case DASD_ECKD_CCW_READ_HOME_ADDRESS: data->operation.orientation = 0x3; data->operation.operation = 0x16; break; case DASD_ECKD_CCW_WRITE_RECORD_ZERO: data->operation.orientation = 0x1; data->operation.operation = 0x03; data->count++; break; case DASD_ECKD_CCW_READ_RECORD_ZERO: data->operation.orientation = 0x3; data->operation.operation = 0x16; data->count++; break; case DASD_ECKD_CCW_WRITE: case DASD_ECKD_CCW_WRITE_MT: case DASD_ECKD_CCW_WRITE_KD: case DASD_ECKD_CCW_WRITE_KD_MT: data->auxiliary.last_bytes_used = 0x1; data->length = reclen; data->operation.operation = 0x01; break; case DASD_ECKD_CCW_WRITE_CKD: case DASD_ECKD_CCW_WRITE_CKD_MT: data->auxiliary.last_bytes_used = 0x1; data->length = reclen; data->operation.operation = 0x03; break; case DASD_ECKD_CCW_READ: case DASD_ECKD_CCW_READ_MT: case DASD_ECKD_CCW_READ_KD: case DASD_ECKD_CCW_READ_KD_MT: data->auxiliary.last_bytes_used = 0x1; data->length = reclen; data->operation.operation = 0x06; break; case DASD_ECKD_CCW_READ_CKD: case DASD_ECKD_CCW_READ_CKD_MT: data->auxiliary.last_bytes_used = 0x1; data->length = reclen; data->operation.operation = 0x16; break; case DASD_ECKD_CCW_READ_COUNT: data->operation.operation = 0x06; break; case DASD_ECKD_CCW_ERASE: data->length = reclen; data->auxiliary.last_bytes_used = 0x1; data->operation.operation = 0x0b; break; default: MESSAGE(KERN_ERR, "unknown opcode 0x%x", cmd); } data->seek_addr.cyl = data->search_arg.cyl = trk / private->rdc_data.trk_per_cyl; data->seek_addr.head = data->search_arg.head = trk % private->rdc_data.trk_per_cyl; data->search_arg.record = rec_on_trk;}/* * Returns 1 if the block is one of the special blocks that needs * to get read/written with the KD variant of the command. * That is DASD_ECKD_READ_KD_MT instead of DASD_ECKD_READ_MT and * DASD_ECKD_WRITE_KD_MT instead of DASD_ECKD_WRITE_MT. * Luckily the KD variants differ only by one bit (0x08) from the * normal variant. So don't wonder about code like: * if (dasd_eckd_cdl_special(blk_per_trk, recid)) * ccw->cmd_code |= 0x8; */static inline intdasd_eckd_cdl_special(int blk_per_trk, int recid){ if (recid < 3) return 1; if (recid < blk_per_trk) return 0; if (recid < 2 * blk_per_trk) return 1; return 0;}/* * Returns the record size for the special blocks of the cdl format. * Only returns something useful if dasd_eckd_cdl_special is true * for the recid. */static inline intdasd_eckd_cdl_reclen(int recid){ if (recid < 3) return sizes_trk0[recid]; return LABEL_SIZE;}static intdasd_eckd_check_characteristics(struct dasd_device *device){ struct dasd_eckd_private *private; void *rdc_data; void *conf_data; int conf_len; int rc; private = (struct dasd_eckd_private *) device->private; if (private == NULL) { private = kmalloc(sizeof(struct dasd_eckd_private), GFP_KERNEL | GFP_DMA); if (private == NULL) { MESSAGE(KERN_WARNING, "%s", "memory allocation failed for private data"); return -ENOMEM; } device->private = (void *) private; } /* Invalidate status of initial analysis. */ private->init_cqr_status = -1; /* Set default cache operations. */ private->attrib.operation = DASD_NORMAL_CACHE; private->attrib.nr_cyl = 0; /* Read Device Characteristics */ rdc_data = (void *) &(private->rdc_data); rc = read_dev_chars(device->cdev, &rdc_data, 64); if (rc) { MESSAGE(KERN_WARNING, "Read device characteristics returned error %d", rc); return rc; } DEV_MESSAGE(KERN_INFO, device, "%04X/%02X(CU:%04X/%02X) Cyl:%d Head:%d Sec:%d", private->rdc_data.dev_type, private->rdc_data.dev_model, private->rdc_data.cu_type, private->rdc_data.cu_model.model, private->rdc_data.no_cyl, private->rdc_data.trk_per_cyl, private->rdc_data.sec_per_trk); /* Read Configuration Data */ rc = read_conf_data(device->cdev, &conf_data, &conf_len); if (rc && rc != -EOPNOTSUPP) { /* -EOPNOTSUPP is ok */ MESSAGE(KERN_WARNING, "Read configuration data returned error %d", rc); return rc; } if (conf_data == NULL) { MESSAGE(KERN_WARNING, "%s", "No configuration data retrieved"); return 0; /* no errror */ } if (conf_len != sizeof (struct dasd_eckd_confdata)) { MESSAGE(KERN_WARNING, "sizes of configuration data mismatch" "%d (read) vs %ld (expected)", conf_len, sizeof (struct dasd_eckd_confdata)); return 0; /* no errror */ } memcpy(&private->conf_data, conf_data, sizeof (struct dasd_eckd_confdata)); return 0;}static struct dasd_ccw_req *dasd_eckd_analysis_ccw(struct dasd_device *device){ struct dasd_eckd_private *private; struct eckd_count *count_data; struct LO_eckd_data *LO_data; struct dasd_ccw_req *cqr; struct ccw1 *ccw;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?