aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/s390/block/dasd_eckd.c
diff options
context:
space:
mode:
authorStefan Weinhuber <wein@de.ibm.com>2008-08-01 16:39:09 +0200
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2008-08-01 16:39:29 +0200
commit4abb08c24b5fa7b6ad0807c07077f0f216f6788b (patch)
treeeb907a86ecb4ac1171972460e8440f6815a7a037 /drivers/s390/block/dasd_eckd.c
parentc2bb4e5d497823437f0a11d342024ccdc6ff5b0d (diff)
downloadlinux-4abb08c24b5fa7b6ad0807c07077f0f216f6788b.tar.gz
[S390] dasd: Add support for enhanced VM UID
When z/VM provides two virtual devices (minidisks) that reside on the same real device, both will receive the configuration data from the real device and thus get the same uid. To fix this problem, z/VM provides an additional configuration data record that allows to distinguish between minidisks. z/VM APAR VM64273 needs be installed so this fix has an effect. Signed-off-by: Stefan Weinhuber <wein@de.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers/s390/block/dasd_eckd.c')
-rw-r--r--drivers/s390/block/dasd_eckd.c147
1 files changed, 112 insertions, 35 deletions
diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c
index 3590fdb5b2fda5..773b3fe275b239 100644
--- a/drivers/s390/block/dasd_eckd.c
+++ b/drivers/s390/block/dasd_eckd.c
@@ -313,8 +313,8 @@ static int prefix(struct ccw1 *ccw, struct PFX_eckd_data *pfxdata, int trk,
memset(pfxdata, 0, sizeof(*pfxdata));
/* prefix data */
pfxdata->format = 0;
- pfxdata->base_address = basepriv->conf_data.ned1.unit_addr;
- pfxdata->base_lss = basepriv->conf_data.ned1.ID;
+ pfxdata->base_address = basepriv->ned->unit_addr;
+ pfxdata->base_lss = basepriv->ned->ID;
pfxdata->validity.define_extend = 1;
/* private uid is kept up to date, conf_data may be outdated */
@@ -536,36 +536,40 @@ dasd_eckd_cdl_reclen(int recid)
/*
* Generate device unique id that specifies the physical device.
*/
-static int
-dasd_eckd_generate_uid(struct dasd_device *device, struct dasd_uid *uid)
+static int dasd_eckd_generate_uid(struct dasd_device *device,
+ struct dasd_uid *uid)
{
struct dasd_eckd_private *private;
- struct dasd_eckd_confdata *confdata;
+ int count;
private = (struct dasd_eckd_private *) device->private;
if (!private)
return -ENODEV;
- confdata = &private->conf_data;
- if (!confdata)
+ if (!private->ned || !private->gneq)
return -ENODEV;
memset(uid, 0, sizeof(struct dasd_uid));
- memcpy(uid->vendor, confdata->ned1.HDA_manufacturer,
+ memcpy(uid->vendor, private->ned->HDA_manufacturer,
sizeof(uid->vendor) - 1);
EBCASC(uid->vendor, sizeof(uid->vendor) - 1);
- memcpy(uid->serial, confdata->ned1.HDA_location,
+ memcpy(uid->serial, private->ned->HDA_location,
sizeof(uid->serial) - 1);
EBCASC(uid->serial, sizeof(uid->serial) - 1);
- uid->ssid = confdata->neq.subsystemID;
- uid->real_unit_addr = confdata->ned1.unit_addr;
- if (confdata->ned2.sneq.flags == 0x40 &&
- confdata->ned2.sneq.format == 0x0001) {
- uid->type = confdata->ned2.sneq.sua_flags;
+ uid->ssid = private->gneq->subsystemID;
+ uid->real_unit_addr = private->ned->unit_addr;;
+ if (private->sneq) {
+ uid->type = private->sneq->sua_flags;
if (uid->type == UA_BASE_PAV_ALIAS)
- uid->base_unit_addr = confdata->ned2.sneq.base_unit_addr;
+ uid->base_unit_addr = private->sneq->base_unit_addr;
} else {
uid->type = UA_BASE_DEVICE;
}
+ if (private->vdsneq) {
+ for (count = 0; count < 16; count++) {
+ sprintf(uid->vduit+2*count, "%02x",
+ private->vdsneq->uit[count]);
+ }
+ }
return 0;
}
@@ -623,6 +627,15 @@ static int dasd_eckd_read_conf_lpm(struct dasd_device *device,
ret = -ENOMEM;
goto out_error;
}
+
+ /*
+ * buffer has to start with EBCDIC "V1.0" to show
+ * support for virtual device SNEQ
+ */
+ rcd_buf[0] = 0xE5;
+ rcd_buf[1] = 0xF1;
+ rcd_buf[2] = 0x4B;
+ rcd_buf[3] = 0xF0;
cqr = dasd_eckd_build_rcd_lpm(device, rcd_buf, ciw, lpm);
if (IS_ERR(cqr)) {
ret = PTR_ERR(cqr);
@@ -646,8 +659,62 @@ out_error:
return ret;
}
-static int
-dasd_eckd_read_conf(struct dasd_device *device)
+static int dasd_eckd_identify_conf_parts(struct dasd_eckd_private *private)
+{
+
+ struct dasd_sneq *sneq;
+ int i, count;
+
+ private->ned = NULL;
+ private->sneq = NULL;
+ private->vdsneq = NULL;
+ private->gneq = NULL;
+ count = private->conf_len / sizeof(struct dasd_sneq);
+ sneq = (struct dasd_sneq *)private->conf_data;
+ for (i = 0; i < count; ++i) {
+ if (sneq->flags.identifier == 1 && sneq->format == 1)
+ private->sneq = sneq;
+ else if (sneq->flags.identifier == 1 && sneq->format == 4)
+ private->vdsneq = (struct vd_sneq *)sneq;
+ else if (sneq->flags.identifier == 2)
+ private->gneq = (struct dasd_gneq *)sneq;
+ else if (sneq->flags.identifier == 3 && sneq->res1 == 1)
+ private->ned = (struct dasd_ned *)sneq;
+ sneq++;
+ }
+ if (!private->ned || !private->gneq) {
+ private->ned = NULL;
+ private->sneq = NULL;
+ private->vdsneq = NULL;
+ private->gneq = NULL;
+ return -EINVAL;
+ }
+ return 0;
+
+};
+
+static unsigned char dasd_eckd_path_access(void *conf_data, int conf_len)
+{
+ struct dasd_gneq *gneq;
+ int i, count, found;
+
+ count = conf_len / sizeof(*gneq);
+ gneq = (struct dasd_gneq *)conf_data;
+ found = 0;
+ for (i = 0; i < count; ++i) {
+ if (gneq->flags.identifier == 2) {
+ found = 1;
+ break;
+ }
+ gneq++;
+ }
+ if (found)
+ return ((char *)gneq)[18] & 0x07;
+ else
+ return 0;
+}
+
+static int dasd_eckd_read_conf(struct dasd_device *device)
{
void *conf_data;
int conf_len, conf_data_saved;
@@ -661,7 +728,6 @@ dasd_eckd_read_conf(struct dasd_device *device)
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){
@@ -678,22 +744,20 @@ dasd_eckd_read_conf(struct dasd_device *device)
"data retrieved");
continue; /* no error */
}
- 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 error */
- }
/* save first valid configuration data */
- if (!conf_data_saved){
- memcpy(&private->conf_data, conf_data,
- sizeof(struct dasd_eckd_confdata));
+ if (!conf_data_saved) {
+ kfree(private->conf_data);
+ private->conf_data = conf_data;
+ private->conf_len = conf_len;
+ if (dasd_eckd_identify_conf_parts(private)) {
+ private->conf_data = NULL;
+ private->conf_len = 0;
+ kfree(conf_data);
+ continue;
+ }
conf_data_saved++;
}
- switch (((char *)conf_data)[242] & 0x07){
+ switch (dasd_eckd_path_access(conf_data, conf_len)) {
case 0x02:
path_data->npm |= lpm;
break;
@@ -701,7 +765,8 @@ dasd_eckd_read_conf(struct dasd_device *device)
path_data->ppm |= lpm;
break;
}
- kfree(conf_data);
+ if (conf_data != private->conf_data)
+ kfree(conf_data);
}
}
return 0;
@@ -952,6 +1017,7 @@ out_err2:
dasd_free_block(device->block);
device->block = NULL;
out_err1:
+ kfree(private->conf_data);
kfree(device->private);
device->private = NULL;
return rc;
@@ -959,7 +1025,17 @@ out_err1:
static void dasd_eckd_uncheck_device(struct dasd_device *device)
{
+ struct dasd_eckd_private *private;
+
+ private = (struct dasd_eckd_private *) device->private;
dasd_alias_disconnect_device_from_lcu(device);
+ private->ned = NULL;
+ private->sneq = NULL;
+ private->vdsneq = NULL;
+ private->gneq = NULL;
+ private->conf_len = 0;
+ kfree(private->conf_data);
+ private->conf_data = NULL;
}
static struct dasd_ccw_req *
@@ -1746,9 +1822,10 @@ dasd_eckd_fill_info(struct dasd_device * device,
info->characteristics_size = sizeof(struct dasd_eckd_characteristics);
memcpy(info->characteristics, &private->rdc_data,
sizeof(struct dasd_eckd_characteristics));
- info->confdata_size = sizeof(struct dasd_eckd_confdata);
- memcpy(info->configuration_data, &private->conf_data,
- sizeof(struct dasd_eckd_confdata));
+ info->confdata_size = min((unsigned long)private->conf_len,
+ sizeof(info->configuration_data));
+ memcpy(info->configuration_data, private->conf_data,
+ info->confdata_size);
return 0;
}