* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
*
- * $Revision: 1.68 $
*/
#include <linux/config.h>
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;
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;
"data");
return -ENOMEM;
}
+ memset(private, 0, sizeof(struct dasd_eckd_private));
device->private = (void *) private;
}
/* Invalidate status of initial analysis. */
/* 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,
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 *
/* 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);
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);
}
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;
* (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)) {
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();
* 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)) {
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();
* (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)) {
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();
* 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) +
/* 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;
* 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;
}
* 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,
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.
.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
{
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);