diff options
author | Stefan Weinhuber <wein@de.ibm.com> | 2008-08-01 10:39:09 -0400 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2008-08-01 10:39:29 -0400 |
commit | 4abb08c24b5fa7b6ad0807c07077f0f216f6788b (patch) | |
tree | eb907a86ecb4ac1171972460e8440f6815a7a037 /drivers/s390/block/dasd_eckd.c | |
parent | c2bb4e5d497823437f0a11d342024ccdc6ff5b0d (diff) |
[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.c | 147 |
1 files changed, 112 insertions, 35 deletions
diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 3590fdb5b2fd..773b3fe275b2 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, | |||
313 | memset(pfxdata, 0, sizeof(*pfxdata)); | 313 | memset(pfxdata, 0, sizeof(*pfxdata)); |
314 | /* prefix data */ | 314 | /* prefix data */ |
315 | pfxdata->format = 0; | 315 | pfxdata->format = 0; |
316 | pfxdata->base_address = basepriv->conf_data.ned1.unit_addr; | 316 | pfxdata->base_address = basepriv->ned->unit_addr; |
317 | pfxdata->base_lss = basepriv->conf_data.ned1.ID; | 317 | pfxdata->base_lss = basepriv->ned->ID; |
318 | pfxdata->validity.define_extend = 1; | 318 | pfxdata->validity.define_extend = 1; |
319 | 319 | ||
320 | /* private uid is kept up to date, conf_data may be outdated */ | 320 | /* private uid is kept up to date, conf_data may be outdated */ |
@@ -536,36 +536,40 @@ dasd_eckd_cdl_reclen(int recid) | |||
536 | /* | 536 | /* |
537 | * Generate device unique id that specifies the physical device. | 537 | * Generate device unique id that specifies the physical device. |
538 | */ | 538 | */ |
539 | static int | 539 | static int dasd_eckd_generate_uid(struct dasd_device *device, |
540 | dasd_eckd_generate_uid(struct dasd_device *device, struct dasd_uid *uid) | 540 | struct dasd_uid *uid) |
541 | { | 541 | { |
542 | struct dasd_eckd_private *private; | 542 | struct dasd_eckd_private *private; |
543 | struct dasd_eckd_confdata *confdata; | 543 | int count; |
544 | 544 | ||
545 | private = (struct dasd_eckd_private *) device->private; | 545 | private = (struct dasd_eckd_private *) device->private; |
546 | if (!private) | 546 | if (!private) |
547 | return -ENODEV; | 547 | return -ENODEV; |
548 | confdata = &private->conf_data; | 548 | if (!private->ned || !private->gneq) |
549 | if (!confdata) | ||
550 | return -ENODEV; | 549 | return -ENODEV; |
551 | 550 | ||
552 | memset(uid, 0, sizeof(struct dasd_uid)); | 551 | memset(uid, 0, sizeof(struct dasd_uid)); |
553 | memcpy(uid->vendor, confdata->ned1.HDA_manufacturer, | 552 | memcpy(uid->vendor, private->ned->HDA_manufacturer, |
554 | sizeof(uid->vendor) - 1); | 553 | sizeof(uid->vendor) - 1); |
555 | EBCASC(uid->vendor, sizeof(uid->vendor) - 1); | 554 | EBCASC(uid->vendor, sizeof(uid->vendor) - 1); |
556 | memcpy(uid->serial, confdata->ned1.HDA_location, | 555 | memcpy(uid->serial, private->ned->HDA_location, |
557 | sizeof(uid->serial) - 1); | 556 | sizeof(uid->serial) - 1); |
558 | EBCASC(uid->serial, sizeof(uid->serial) - 1); | 557 | EBCASC(uid->serial, sizeof(uid->serial) - 1); |
559 | uid->ssid = confdata->neq.subsystemID; | 558 | uid->ssid = private->gneq->subsystemID; |
560 | uid->real_unit_addr = confdata->ned1.unit_addr; | 559 | uid->real_unit_addr = private->ned->unit_addr;; |
561 | if (confdata->ned2.sneq.flags == 0x40 && | 560 | if (private->sneq) { |
562 | confdata->ned2.sneq.format == 0x0001) { | 561 | uid->type = private->sneq->sua_flags; |
563 | uid->type = confdata->ned2.sneq.sua_flags; | ||
564 | if (uid->type == UA_BASE_PAV_ALIAS) | 562 | if (uid->type == UA_BASE_PAV_ALIAS) |
565 | uid->base_unit_addr = confdata->ned2.sneq.base_unit_addr; | 563 | uid->base_unit_addr = private->sneq->base_unit_addr; |
566 | } else { | 564 | } else { |
567 | uid->type = UA_BASE_DEVICE; | 565 | uid->type = UA_BASE_DEVICE; |
568 | } | 566 | } |
567 | if (private->vdsneq) { | ||
568 | for (count = 0; count < 16; count++) { | ||
569 | sprintf(uid->vduit+2*count, "%02x", | ||
570 | private->vdsneq->uit[count]); | ||
571 | } | ||
572 | } | ||
569 | return 0; | 573 | return 0; |
570 | } | 574 | } |
571 | 575 | ||
@@ -623,6 +627,15 @@ static int dasd_eckd_read_conf_lpm(struct dasd_device *device, | |||
623 | ret = -ENOMEM; | 627 | ret = -ENOMEM; |
624 | goto out_error; | 628 | goto out_error; |
625 | } | 629 | } |
630 | |||
631 | /* | ||
632 | * buffer has to start with EBCDIC "V1.0" to show | ||
633 | * support for virtual device SNEQ | ||
634 | */ | ||
635 | rcd_buf[0] = 0xE5; | ||
636 | rcd_buf[1] = 0xF1; | ||
637 | rcd_buf[2] = 0x4B; | ||
638 | rcd_buf[3] = 0xF0; | ||
626 | cqr = dasd_eckd_build_rcd_lpm(device, rcd_buf, ciw, lpm); | 639 | cqr = dasd_eckd_build_rcd_lpm(device, rcd_buf, ciw, lpm); |
627 | if (IS_ERR(cqr)) { | 640 | if (IS_ERR(cqr)) { |
628 | ret = PTR_ERR(cqr); | 641 | ret = PTR_ERR(cqr); |
@@ -646,8 +659,62 @@ out_error: | |||
646 | return ret; | 659 | return ret; |
647 | } | 660 | } |
648 | 661 | ||
649 | static int | 662 | static int dasd_eckd_identify_conf_parts(struct dasd_eckd_private *private) |
650 | dasd_eckd_read_conf(struct dasd_device *device) | 663 | { |
664 | |||
665 | struct dasd_sneq *sneq; | ||
666 | int i, count; | ||
667 | |||
668 | private->ned = NULL; | ||
669 | private->sneq = NULL; | ||
670 | private->vdsneq = NULL; | ||
671 | private->gneq = NULL; | ||
672 | count = private->conf_len / sizeof(struct dasd_sneq); | ||
673 | sneq = (struct dasd_sneq *)private->conf_data; | ||
674 | for (i = 0; i < count; ++i) { | ||
675 | if (sneq->flags.identifier == 1 && sneq->format == 1) | ||
676 | private->sneq = sneq; | ||
677 | else if (sneq->flags.identifier == 1 && sneq->format == 4) | ||
678 | private->vdsneq = (struct vd_sneq *)sneq; | ||
679 | else if (sneq->flags.identifier == 2) | ||
680 | private->gneq = (struct dasd_gneq *)sneq; | ||
681 | else if (sneq->flags.identifier == 3 && sneq->res1 == 1) | ||
682 | private->ned = (struct dasd_ned *)sneq; | ||
683 | sneq++; | ||
684 | } | ||
685 | if (!private->ned || !private->gneq) { | ||
686 | private->ned = NULL; | ||
687 | private->sneq = NULL; | ||
688 | private->vdsneq = NULL; | ||
689 | private->gneq = NULL; | ||
690 | return -EINVAL; | ||
691 | } | ||
692 | return 0; | ||
693 | |||
694 | }; | ||
695 | |||
696 | static unsigned char dasd_eckd_path_access(void *conf_data, int conf_len) | ||
697 | { | ||
698 | struct dasd_gneq *gneq; | ||
699 | int i, count, found; | ||
700 | |||
701 | count = conf_len / sizeof(*gneq); | ||
702 | gneq = (struct dasd_gneq *)conf_data; | ||
703 | found = 0; | ||
704 | for (i = 0; i < count; ++i) { | ||
705 | if (gneq->flags.identifier == 2) { | ||
706 | found = 1; | ||
707 | break; | ||
708 | } | ||
709 | gneq++; | ||
710 | } | ||
711 | if (found) | ||
712 | return ((char *)gneq)[18] & 0x07; | ||
713 | else | ||
714 | return 0; | ||
715 | } | ||
716 | |||
717 | static int dasd_eckd_read_conf(struct dasd_device *device) | ||
651 | { | 718 | { |
652 | void *conf_data; | 719 | void *conf_data; |
653 | int conf_len, conf_data_saved; | 720 | int conf_len, conf_data_saved; |
@@ -661,7 +728,6 @@ dasd_eckd_read_conf(struct dasd_device *device) | |||
661 | path_data->opm = ccw_device_get_path_mask(device->cdev); | 728 | path_data->opm = ccw_device_get_path_mask(device->cdev); |
662 | lpm = 0x80; | 729 | lpm = 0x80; |
663 | conf_data_saved = 0; | 730 | conf_data_saved = 0; |
664 | |||
665 | /* get configuration data per operational path */ | 731 | /* get configuration data per operational path */ |
666 | for (lpm = 0x80; lpm; lpm>>= 1) { | 732 | for (lpm = 0x80; lpm; lpm>>= 1) { |
667 | if (lpm & path_data->opm){ | 733 | if (lpm & path_data->opm){ |
@@ -678,22 +744,20 @@ dasd_eckd_read_conf(struct dasd_device *device) | |||
678 | "data retrieved"); | 744 | "data retrieved"); |
679 | continue; /* no error */ | 745 | continue; /* no error */ |
680 | } | 746 | } |
681 | if (conf_len != sizeof(struct dasd_eckd_confdata)) { | ||
682 | MESSAGE(KERN_WARNING, | ||
683 | "sizes of configuration data mismatch" | ||
684 | "%d (read) vs %ld (expected)", | ||
685 | conf_len, | ||
686 | sizeof(struct dasd_eckd_confdata)); | ||
687 | kfree(conf_data); | ||
688 | continue; /* no error */ | ||
689 | } | ||
690 | /* save first valid configuration data */ | 747 | /* save first valid configuration data */ |
691 | if (!conf_data_saved){ | 748 | if (!conf_data_saved) { |
692 | memcpy(&private->conf_data, conf_data, | 749 | kfree(private->conf_data); |
693 | sizeof(struct dasd_eckd_confdata)); | 750 | private->conf_data = conf_data; |
751 | private->conf_len = conf_len; | ||
752 | if (dasd_eckd_identify_conf_parts(private)) { | ||
753 | private->conf_data = NULL; | ||
754 | private->conf_len = 0; | ||
755 | kfree(conf_data); | ||
756 | continue; | ||
757 | } | ||
694 | conf_data_saved++; | 758 | conf_data_saved++; |
695 | } | 759 | } |
696 | switch (((char *)conf_data)[242] & 0x07){ | 760 | switch (dasd_eckd_path_access(conf_data, conf_len)) { |
697 | case 0x02: | 761 | case 0x02: |
698 | path_data->npm |= lpm; | 762 | path_data->npm |= lpm; |
699 | break; | 763 | break; |
@@ -701,7 +765,8 @@ dasd_eckd_read_conf(struct dasd_device *device) | |||
701 | path_data->ppm |= lpm; | 765 | path_data->ppm |= lpm; |
702 | break; | 766 | break; |
703 | } | 767 | } |
704 | kfree(conf_data); | 768 | if (conf_data != private->conf_data) |
769 | kfree(conf_data); | ||
705 | } | 770 | } |
706 | } | 771 | } |
707 | return 0; | 772 | return 0; |
@@ -952,6 +1017,7 @@ out_err2: | |||
952 | dasd_free_block(device->block); | 1017 | dasd_free_block(device->block); |
953 | device->block = NULL; | 1018 | device->block = NULL; |
954 | out_err1: | 1019 | out_err1: |
1020 | kfree(private->conf_data); | ||
955 | kfree(device->private); | 1021 | kfree(device->private); |
956 | device->private = NULL; | 1022 | device->private = NULL; |
957 | return rc; | 1023 | return rc; |
@@ -959,7 +1025,17 @@ out_err1: | |||
959 | 1025 | ||
960 | static void dasd_eckd_uncheck_device(struct dasd_device *device) | 1026 | static void dasd_eckd_uncheck_device(struct dasd_device *device) |
961 | { | 1027 | { |
1028 | struct dasd_eckd_private *private; | ||
1029 | |||
1030 | private = (struct dasd_eckd_private *) device->private; | ||
962 | dasd_alias_disconnect_device_from_lcu(device); | 1031 | dasd_alias_disconnect_device_from_lcu(device); |
1032 | private->ned = NULL; | ||
1033 | private->sneq = NULL; | ||
1034 | private->vdsneq = NULL; | ||
1035 | private->gneq = NULL; | ||
1036 | private->conf_len = 0; | ||
1037 | kfree(private->conf_data); | ||
1038 | private->conf_data = NULL; | ||
963 | } | 1039 | } |
964 | 1040 | ||
965 | static struct dasd_ccw_req * | 1041 | static struct dasd_ccw_req * |
@@ -1746,9 +1822,10 @@ dasd_eckd_fill_info(struct dasd_device * device, | |||
1746 | info->characteristics_size = sizeof(struct dasd_eckd_characteristics); | 1822 | info->characteristics_size = sizeof(struct dasd_eckd_characteristics); |
1747 | memcpy(info->characteristics, &private->rdc_data, | 1823 | memcpy(info->characteristics, &private->rdc_data, |
1748 | sizeof(struct dasd_eckd_characteristics)); | 1824 | sizeof(struct dasd_eckd_characteristics)); |
1749 | info->confdata_size = sizeof(struct dasd_eckd_confdata); | 1825 | info->confdata_size = min((unsigned long)private->conf_len, |
1750 | memcpy(info->configuration_data, &private->conf_data, | 1826 | sizeof(info->configuration_data)); |
1751 | sizeof(struct dasd_eckd_confdata)); | 1827 | memcpy(info->configuration_data, private->conf_data, |
1828 | info->confdata_size); | ||
1752 | return 0; | 1829 | return 0; |
1753 | } | 1830 | } |
1754 | 1831 | ||