Skip to content

Commit

Permalink
[S390] dasd: Add support for enhanced VM UID
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Stefan Weinhuber authored and Martin Schwidefsky committed Aug 1, 2008
1 parent c2bb4e5 commit 4abb08c
Show file tree
Hide file tree
Showing 5 changed files with 189 additions and 163 deletions.
4 changes: 3 additions & 1 deletion drivers/s390/block/dasd_alias.c
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ static struct alias_pav_group *_find_group(struct alias_lcu *lcu,
else
search_unit_addr = uid->base_unit_addr;
list_for_each_entry(pos, &lcu->grouplist, group) {
if (pos->uid.base_unit_addr == search_unit_addr)
if (pos->uid.base_unit_addr == search_unit_addr &&
!strncmp(pos->uid.vduit, uid->vduit, sizeof(uid->vduit)))
return pos;
};
return NULL;
Expand Down Expand Up @@ -332,6 +333,7 @@ static int _add_device_to_lcu(struct alias_lcu *lcu,
group->uid.base_unit_addr = uid->real_unit_addr;
else
group->uid.base_unit_addr = uid->base_unit_addr;
memcpy(group->uid.vduit, uid->vduit, sizeof(uid->vduit));
INIT_LIST_HEAD(&group->group);
INIT_LIST_HEAD(&group->baselist);
INIT_LIST_HEAD(&group->aliaslist);
Expand Down
16 changes: 13 additions & 3 deletions drivers/s390/block/dasd_devmap.c
Original file line number Diff line number Diff line change
Expand Up @@ -913,7 +913,8 @@ dasd_vendor_show(struct device *dev, struct device_attribute *attr, char *buf)
static DEVICE_ATTR(vendor, 0444, dasd_vendor_show, NULL);

#define UID_STRLEN ( /* vendor */ 3 + 1 + /* serial */ 14 + 1 +\
/* SSID */ 4 + 1 + /* unit addr */ 2 + 1)
/* SSID */ 4 + 1 + /* unit addr */ 2 + 1 +\
/* vduit */ 32 + 1)

static ssize_t
dasd_uid_show(struct device *dev, struct device_attribute *attr, char *buf)
Expand Down Expand Up @@ -945,8 +946,17 @@ dasd_uid_show(struct device *dev, struct device_attribute *attr, char *buf)
sprintf(ua_string, "%02x", uid->real_unit_addr);
break;
}
snprintf(uid_string, sizeof(uid_string), "%s.%s.%04x.%s",
uid->vendor, uid->serial, uid->ssid, ua_string);
if (strlen(uid->vduit) > 0)
snprintf(uid_string, sizeof(uid_string),
"%s.%s.%04x.%s.%s",
uid->vendor, uid->serial,
uid->ssid, ua_string,
uid->vduit);
else
snprintf(uid_string, sizeof(uid_string),
"%s.%s.%04x.%s",
uid->vendor, uid->serial,
uid->ssid, ua_string);
spin_unlock(&dasd_devmap_lock);
return snprintf(buf, PAGE_SIZE, "%s\n", uid_string);
}
Expand Down
147 changes: 112 additions & 35 deletions drivers/s390/block/dasd_eckd.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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);
Expand All @@ -646,8 +659,62 @@ static int dasd_eckd_read_conf_lpm(struct dasd_device *device,
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;
Expand All @@ -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){
Expand All @@ -678,30 +744,29 @@ 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;
case 0x03:
path_data->ppm |= lpm;
break;
}
kfree(conf_data);
if (conf_data != private->conf_data)
kfree(conf_data);
}
}
return 0;
Expand Down Expand Up @@ -952,14 +1017,25 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
dasd_free_block(device->block);
device->block = NULL;
out_err1:
kfree(private->conf_data);
kfree(device->private);
device->private = NULL;
return rc;
}

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 *
Expand Down Expand Up @@ -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;
}

Expand Down
Loading

0 comments on commit 4abb08c

Please sign in to comment.