X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fs390%2Fblock%2Fdasd_eckd.c;h=7d5a6cee4bd8ff075c4c48ed6edccdb30cefb283;hb=43bc926fffd92024b46cafaf7350d669ba9ca884;hp=df8902364762d87b1b9d5dc6579e4e09e294afca;hpb=6a77f38946aaee1cd85eeec6cf4229b204c15071;p=linux-2.6.git diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index df8902364..7d5a6cee4 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -7,7 +7,6 @@ * Bugreports.to..: * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000 * - * $Revision: 1.68 $ */ #include @@ -56,6 +55,7 @@ static struct dasd_discipline dasd_eckd_discipline; struct dasd_eckd_private { struct dasd_eckd_characteristics rdc_data; struct dasd_eckd_confdata conf_data; + struct dasd_eckd_path path_data; struct eckd_count count_area[5]; int init_cqr_status; int uses_cdl; @@ -446,13 +446,110 @@ dasd_eckd_cdl_reclen(int recid) return LABEL_SIZE; } +/* + * Generate device unique id that specifies the physical device. + */ +static int +dasd_eckd_generate_uid(struct dasd_device *device, struct dasd_uid *uid) +{ + struct dasd_eckd_private *private; + struct dasd_eckd_confdata *confdata; + + private = (struct dasd_eckd_private *) device->private; + if (!private) + return -ENODEV; + confdata = &private->conf_data; + if (!confdata) + return -ENODEV; + + memset(uid, 0, sizeof(struct dasd_uid)); + strncpy(uid->vendor, confdata->ned1.HDA_manufacturer, + sizeof(uid->vendor) - 1); + EBCASC(uid->vendor, sizeof(uid->vendor) - 1); + strncpy(uid->serial, confdata->ned1.HDA_location, + sizeof(uid->serial) - 1); + EBCASC(uid->serial, sizeof(uid->serial) - 1); + uid->ssid = confdata->neq.subsystemID; + if (confdata->ned2.sneq.flags == 0x40) { + uid->alias = 1; + uid->unit_addr = confdata->ned2.sneq.base_unit_addr; + } else + uid->unit_addr = confdata->ned1.unit_addr; + + return 0; +} + +static int +dasd_eckd_read_conf(struct dasd_device *device) +{ + void *conf_data; + int conf_len, conf_data_saved; + int rc; + __u8 lpm; + struct dasd_eckd_private *private; + struct dasd_eckd_path *path_data; + + private = (struct dasd_eckd_private *) device->private; + path_data = (struct dasd_eckd_path *) &private->path_data; + path_data->opm = ccw_device_get_path_mask(device->cdev); + lpm = 0x80; + conf_data_saved = 0; + + /* get configuration data per operational path */ + for (lpm = 0x80; lpm; lpm>>= 1) { + if (lpm & path_data->opm){ + rc = read_conf_data_lpm(device->cdev, &conf_data, + &conf_len, lpm); + 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"); + continue; /* 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)); + kfree(conf_data); + continue; /* no errror */ + } + /* save first valid configuration data */ + if (!conf_data_saved){ + memcpy(&private->conf_data, conf_data, + sizeof (struct dasd_eckd_confdata)); + conf_data_saved++; + } + switch (((char *)conf_data)[242] & 0x07){ + case 0x02: + path_data->npm |= lpm; + break; + case 0x03: + path_data->ppm |= lpm; + break; + } + kfree(conf_data); + } + } + return 0; +} + +/* + * Check device characteristics. + * If the device is accessible using ECKD discipline, the device is enabled. + */ static int dasd_eckd_check_characteristics(struct dasd_device *device) { struct dasd_eckd_private *private; + struct dasd_uid uid; void *rdc_data; - void *conf_data; - int conf_len; int rc; private = (struct dasd_eckd_private *) device->private; @@ -465,6 +562,7 @@ dasd_eckd_check_characteristics(struct dasd_device *device) "data"); return -ENOMEM; } + memset(private, 0, sizeof(struct dasd_eckd_private)); device->private = (void *) private; } /* Invalidate status of initial analysis. */ @@ -475,6 +573,7 @@ dasd_eckd_check_characteristics(struct dasd_device *device) /* Read Device Characteristics */ rdc_data = (void *) &(private->rdc_data); + memset(rdc_data, 0, sizeof(rdc_data)); rc = read_dev_chars(device->cdev, &rdc_data, 64); if (rc) { DEV_MESSAGE(KERN_WARNING, device, @@ -494,30 +593,18 @@ dasd_eckd_check_characteristics(struct dasd_device *device) 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 */ - DEV_MESSAGE(KERN_WARNING, device, - "Read configuration data returned error %d", rc); + rc = dasd_eckd_read_conf (device); + if (rc) return rc; - } - if (conf_data == NULL) { - DEV_MESSAGE(KERN_WARNING, device, "%s", - "No configuration data retrieved"); - return 0; /* no errror */ - } - if (conf_len != sizeof (struct dasd_eckd_confdata)) { - DEV_MESSAGE(KERN_WARNING, device, - "sizes of configuration data mismatch" - "%d (read) vs %ld (expected)", - conf_len, sizeof (struct dasd_eckd_confdata)); - kfree(conf_data); /* allocated by read_conf_data() */ - return 0; /* no errror */ - } - memcpy(&private->conf_data, conf_data, - sizeof (struct dasd_eckd_confdata)); - kfree(conf_data); /* allocated by read_conf_data() */ - return 0; + /* Generate device unique id and register in devmap */ + rc = dasd_eckd_generate_uid(device, &uid); + if (rc) + return rc; + + rc = dasd_set_uid(device->cdev, &uid); + + return rc; } static struct dasd_ccw_req * @@ -1000,7 +1087,7 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req) /* Eckd can only do full blocks. */ return ERR_PTR(-EINVAL); count += bv->bv_len >> (device->s2b_shift + 9); -#if defined(CONFIG_ARCH_S390X) +#if defined(CONFIG_64BIT) if (idal_is_needed (page_address(bv->bv_page), bv->bv_len)) cidaw += bv->bv_len >> (device->s2b_shift + 9); @@ -1060,7 +1147,8 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req) if (dasd_eckd_cdl_special(blk_per_trk, recid)){ rcmd |= 0x8; count = dasd_eckd_cdl_reclen(recid); - if (count < blksize) + if (count < blksize && + rq_data_dir(req) == READ) memset(dst + count, 0xe5, blksize - count); } @@ -1094,9 +1182,11 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req) recid++; } } + if (req->flags & REQ_FAILFAST) + set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); cqr->device = device; cqr->expires = 5 * 60 * HZ; /* 5 minutes */ - cqr->lpm = LPM_ANYPATH; + cqr->lpm = private->path_data.ppm; cqr->retries = 256; cqr->buildclk = get_clock(); cqr->status = DASD_CQR_FILLED; @@ -1184,19 +1274,14 @@ dasd_eckd_fill_info(struct dasd_device * device, * (see dasd_eckd_reserve) device. */ static int -dasd_eckd_release(struct block_device *bdev, int no, long args) +dasd_eckd_release(struct dasd_device *device) { - struct dasd_device *device; struct dasd_ccw_req *cqr; int rc; if (!capable(CAP_SYS_ADMIN)) return -EACCES; - device = bdev->bd_disk->private_data; - if (device == NULL) - return -ENODEV; - cqr = dasd_smalloc_request(dasd_eckd_discipline.name, 1, 32, device); if (IS_ERR(cqr)) { @@ -1210,6 +1295,7 @@ dasd_eckd_release(struct block_device *bdev, int no, long args) cqr->cpaddr->cda = (__u32)(addr_t) cqr->data; cqr->device = device; clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); + set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); cqr->retries = 0; cqr->expires = 2 * HZ; cqr->buildclk = get_clock(); @@ -1228,19 +1314,14 @@ dasd_eckd_release(struct block_device *bdev, int no, long args) * the interrupt is outstanding for a certain time. */ static int -dasd_eckd_reserve(struct block_device *bdev, int no, long args) +dasd_eckd_reserve(struct dasd_device *device) { - struct dasd_device *device; struct dasd_ccw_req *cqr; int rc; if (!capable(CAP_SYS_ADMIN)) return -EACCES; - device = bdev->bd_disk->private_data; - if (device == NULL) - return -ENODEV; - cqr = dasd_smalloc_request(dasd_eckd_discipline.name, 1, 32, device); if (IS_ERR(cqr)) { @@ -1254,6 +1335,7 @@ dasd_eckd_reserve(struct block_device *bdev, int no, long args) cqr->cpaddr->cda = (__u32)(addr_t) cqr->data; cqr->device = device; clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); + set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); cqr->retries = 0; cqr->expires = 2 * HZ; cqr->buildclk = get_clock(); @@ -1271,19 +1353,14 @@ dasd_eckd_reserve(struct block_device *bdev, int no, long args) * (unconditional reserve) */ static int -dasd_eckd_steal_lock(struct block_device *bdev, int no, long args) +dasd_eckd_steal_lock(struct dasd_device *device) { - struct dasd_device *device; struct dasd_ccw_req *cqr; int rc; if (!capable(CAP_SYS_ADMIN)) return -EACCES; - device = bdev->bd_disk->private_data; - if (device == NULL) - return -ENODEV; - cqr = dasd_smalloc_request(dasd_eckd_discipline.name, 1, 32, device); if (IS_ERR(cqr)) { @@ -1297,6 +1374,7 @@ dasd_eckd_steal_lock(struct block_device *bdev, int no, long args) cqr->cpaddr->cda = (__u32)(addr_t) cqr->data; cqr->device = device; clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); + set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); cqr->retries = 0; cqr->expires = 2 * HZ; cqr->buildclk = get_clock(); @@ -1312,19 +1390,14 @@ dasd_eckd_steal_lock(struct block_device *bdev, int no, long args) * Read performance statistics */ static int -dasd_eckd_performance(struct block_device *bdev, int no, long args) +dasd_eckd_performance(struct dasd_device *device, void __user *argp) { - struct dasd_device *device; struct dasd_psf_prssd_data *prssdp; struct dasd_rssd_perf_stats_t *stats; struct dasd_ccw_req *cqr; struct ccw1 *ccw; int rc; - device = bdev->bd_disk->private_data; - if (device == NULL) - return -ENODEV; - cqr = dasd_smalloc_request(dasd_eckd_discipline.name, 1 /* PSF */ + 1 /* RSSD */ , (sizeof (struct dasd_psf_prssd_data) + @@ -1368,8 +1441,9 @@ dasd_eckd_performance(struct block_device *bdev, int no, long args) /* Prepare for Read Subsystem Data */ prssdp = (struct dasd_psf_prssd_data *) cqr->data; stats = (struct dasd_rssd_perf_stats_t *) (prssdp + 1); - rc = copy_to_user((long __user *) args, (long *) stats, - sizeof(struct dasd_rssd_perf_stats_t)); + if (copy_to_user(argp, stats, + sizeof(struct dasd_rssd_perf_stats_t))) + rc = -EFAULT; } dasd_sfree_request(cqr, cqr->device); return rc; @@ -1380,27 +1454,22 @@ dasd_eckd_performance(struct block_device *bdev, int no, long args) * Returnes the cache attributes used in Define Extend (DE). */ static int -dasd_eckd_get_attrib (struct block_device *bdev, int no, long args) +dasd_eckd_get_attrib(struct dasd_device *device, void __user *argp) { - struct dasd_device *device; - struct dasd_eckd_private *private; - struct attrib_data_t attrib; + struct dasd_eckd_private *private = + (struct dasd_eckd_private *)device->private; + struct attrib_data_t attrib = private->attrib; int rc; if (!capable(CAP_SYS_ADMIN)) return -EACCES; - if (!args) + if (!argp) return -EINVAL; - device = bdev->bd_disk->private_data; - if (device == NULL) - return -ENODEV; - - private = (struct dasd_eckd_private *) device->private; - attrib = private->attrib; - - rc = copy_to_user((long __user *) args, (long *) &attrib, - sizeof (struct attrib_data_t)); + rc = 0; + if (copy_to_user(argp, (long *) &attrib, + sizeof (struct attrib_data_t))) + rc = -EFAULT; return rc; } @@ -1410,26 +1479,19 @@ dasd_eckd_get_attrib (struct block_device *bdev, int no, long args) * Stores the attributes for cache operation to be used in Define Extend (DE). */ static int -dasd_eckd_set_attrib(struct block_device *bdev, int no, long args) +dasd_eckd_set_attrib(struct dasd_device *device, void __user *argp) { - struct dasd_device *device; - struct dasd_eckd_private *private; + struct dasd_eckd_private *private = + (struct dasd_eckd_private *)device->private; struct attrib_data_t attrib; if (!capable(CAP_SYS_ADMIN)) return -EACCES; - if (!args) + if (!argp) return -EINVAL; - device = bdev->bd_disk->private_data; - if (device == NULL) - return -ENODEV; - - if (copy_from_user(&attrib, (void __user *) args, - sizeof (struct attrib_data_t))) { + if (copy_from_user(&attrib, argp, sizeof(struct attrib_data_t))) return -EFAULT; - } - private = (struct dasd_eckd_private *) device->private; private->attrib = attrib; DEV_MESSAGE(KERN_INFO, device, @@ -1438,6 +1500,27 @@ dasd_eckd_set_attrib(struct block_device *bdev, int no, long args) return 0; } +static int +dasd_eckd_ioctl(struct dasd_device *device, unsigned int cmd, void __user *argp) +{ + switch (cmd) { + case BIODASDGATTR: + return dasd_eckd_get_attrib(device, argp); + case BIODASDSATTR: + return dasd_eckd_set_attrib(device, argp); + case BIODASDPSRD: + return dasd_eckd_performance(device, argp); + case BIODASDRLSE: + return dasd_eckd_release(device); + case BIODASDRSRV: + return dasd_eckd_reserve(device); + case BIODASDSLCK: + return dasd_eckd_steal_lock(device); + default: + return -ENOIOCTLCMD; + } +} + /* * Print sense data and related channel program. * Parts are printed because printk buffer is only 1024 bytes. @@ -1596,6 +1679,7 @@ static struct dasd_discipline dasd_eckd_discipline = { .free_cp = dasd_eckd_free_cp, .dump_sense = dasd_eckd_dump_sense, .fill_info = dasd_eckd_fill_info, + .ioctl = dasd_eckd_ioctl, }; static int __init @@ -1603,59 +1687,18 @@ dasd_eckd_init(void) { int ret; - dasd_ioctl_no_register(THIS_MODULE, BIODASDGATTR, - dasd_eckd_get_attrib); - dasd_ioctl_no_register(THIS_MODULE, BIODASDSATTR, - dasd_eckd_set_attrib); - dasd_ioctl_no_register(THIS_MODULE, BIODASDPSRD, - dasd_eckd_performance); - dasd_ioctl_no_register(THIS_MODULE, BIODASDRLSE, - dasd_eckd_release); - dasd_ioctl_no_register(THIS_MODULE, BIODASDRSRV, - dasd_eckd_reserve); - dasd_ioctl_no_register(THIS_MODULE, BIODASDSLCK, - dasd_eckd_steal_lock); - ASCEBC(dasd_eckd_discipline.ebcname, 4); ret = ccw_driver_register(&dasd_eckd_driver); - if (ret) { - dasd_ioctl_no_unregister(THIS_MODULE, BIODASDGATTR, - dasd_eckd_get_attrib); - dasd_ioctl_no_unregister(THIS_MODULE, BIODASDSATTR, - dasd_eckd_set_attrib); - dasd_ioctl_no_unregister(THIS_MODULE, BIODASDPSRD, - dasd_eckd_performance); - dasd_ioctl_no_unregister(THIS_MODULE, BIODASDRLSE, - dasd_eckd_release); - dasd_ioctl_no_unregister(THIS_MODULE, BIODASDRSRV, - dasd_eckd_reserve); - dasd_ioctl_no_unregister(THIS_MODULE, BIODASDSLCK, - dasd_eckd_steal_lock); - return ret; - } - - dasd_generic_auto_online(&dasd_eckd_driver); - return 0; + if (!ret) + dasd_generic_auto_online(&dasd_eckd_driver); + return ret; } static void __exit dasd_eckd_cleanup(void) { ccw_driver_unregister(&dasd_eckd_driver); - - dasd_ioctl_no_unregister(THIS_MODULE, BIODASDGATTR, - dasd_eckd_get_attrib); - dasd_ioctl_no_unregister(THIS_MODULE, BIODASDSATTR, - dasd_eckd_set_attrib); - dasd_ioctl_no_unregister(THIS_MODULE, BIODASDPSRD, - dasd_eckd_performance); - dasd_ioctl_no_unregister(THIS_MODULE, BIODASDRLSE, - dasd_eckd_release); - dasd_ioctl_no_unregister(THIS_MODULE, BIODASDRSRV, - dasd_eckd_reserve); - dasd_ioctl_no_unregister(THIS_MODULE, BIODASDSLCK, - dasd_eckd_steal_lock); } module_init(dasd_eckd_init);