diff options
Diffstat (limited to 'drivers/s390/block/dasd_eckd.c')
-rw-r--r-- | drivers/s390/block/dasd_eckd.c | 261 |
1 files changed, 221 insertions, 40 deletions
diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 549443af121c..a1ebf5722ae5 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c | |||
@@ -90,6 +90,18 @@ static struct { | |||
90 | } *dasd_reserve_req; | 90 | } *dasd_reserve_req; |
91 | static DEFINE_MUTEX(dasd_reserve_mutex); | 91 | static DEFINE_MUTEX(dasd_reserve_mutex); |
92 | 92 | ||
93 | /* definitions for the path verification worker */ | ||
94 | struct path_verification_work_data { | ||
95 | struct work_struct worker; | ||
96 | struct dasd_device *device; | ||
97 | struct dasd_ccw_req cqr; | ||
98 | struct ccw1 ccw; | ||
99 | __u8 rcd_buffer[DASD_ECKD_RCD_DATA_SIZE]; | ||
100 | int isglobal; | ||
101 | __u8 tbvpm; | ||
102 | }; | ||
103 | static struct path_verification_work_data *path_verification_worker; | ||
104 | static DEFINE_MUTEX(dasd_path_verification_mutex); | ||
93 | 105 | ||
94 | /* initial attempt at a probe function. this can be simplified once | 106 | /* initial attempt at a probe function. this can be simplified once |
95 | * the other detection code is gone */ | 107 | * the other detection code is gone */ |
@@ -755,26 +767,27 @@ static int dasd_eckd_get_uid(struct dasd_device *device, struct dasd_uid *uid) | |||
755 | return -EINVAL; | 767 | return -EINVAL; |
756 | } | 768 | } |
757 | 769 | ||
758 | static struct dasd_ccw_req *dasd_eckd_build_rcd_lpm(struct dasd_device *device, | 770 | static void dasd_eckd_fill_rcd_cqr(struct dasd_device *device, |
759 | void *rcd_buffer, | 771 | struct dasd_ccw_req *cqr, |
760 | struct ciw *ciw, __u8 lpm) | 772 | __u8 *rcd_buffer, |
773 | __u8 lpm) | ||
761 | { | 774 | { |
762 | struct dasd_ccw_req *cqr; | ||
763 | struct ccw1 *ccw; | 775 | struct ccw1 *ccw; |
764 | 776 | /* | |
765 | cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* RCD */, ciw->count, | 777 | * buffer has to start with EBCDIC "V1.0" to show |
766 | device); | 778 | * support for virtual device SNEQ |
767 | 779 | */ | |
768 | if (IS_ERR(cqr)) { | 780 | rcd_buffer[0] = 0xE5; |
769 | DBF_DEV_EVENT(DBF_WARNING, device, "%s", | 781 | rcd_buffer[1] = 0xF1; |
770 | "Could not allocate RCD request"); | 782 | rcd_buffer[2] = 0x4B; |
771 | return cqr; | 783 | rcd_buffer[3] = 0xF0; |
772 | } | ||
773 | 784 | ||
774 | ccw = cqr->cpaddr; | 785 | ccw = cqr->cpaddr; |
775 | ccw->cmd_code = ciw->cmd; | 786 | ccw->cmd_code = DASD_ECKD_CCW_RCD; |
787 | ccw->flags = 0; | ||
776 | ccw->cda = (__u32)(addr_t)rcd_buffer; | 788 | ccw->cda = (__u32)(addr_t)rcd_buffer; |
777 | ccw->count = ciw->count; | 789 | ccw->count = DASD_ECKD_RCD_DATA_SIZE; |
790 | cqr->magic = DASD_ECKD_MAGIC; | ||
778 | 791 | ||
779 | cqr->startdev = device; | 792 | cqr->startdev = device; |
780 | cqr->memdev = device; | 793 | cqr->memdev = device; |
@@ -784,7 +797,29 @@ static struct dasd_ccw_req *dasd_eckd_build_rcd_lpm(struct dasd_device *device, | |||
784 | cqr->retries = 256; | 797 | cqr->retries = 256; |
785 | cqr->buildclk = get_clock(); | 798 | cqr->buildclk = get_clock(); |
786 | cqr->status = DASD_CQR_FILLED; | 799 | cqr->status = DASD_CQR_FILLED; |
787 | return cqr; | 800 | set_bit(DASD_CQR_VERIFY_PATH, &cqr->flags); |
801 | } | ||
802 | |||
803 | static int dasd_eckd_read_conf_immediately(struct dasd_device *device, | ||
804 | struct dasd_ccw_req *cqr, | ||
805 | __u8 *rcd_buffer, | ||
806 | __u8 lpm) | ||
807 | { | ||
808 | struct ciw *ciw; | ||
809 | int rc; | ||
810 | /* | ||
811 | * sanity check: scan for RCD command in extended SenseID data | ||
812 | * some devices do not support RCD | ||
813 | */ | ||
814 | ciw = ccw_device_get_ciw(device->cdev, CIW_TYPE_RCD); | ||
815 | if (!ciw || ciw->cmd != DASD_ECKD_CCW_RCD) | ||
816 | return -EOPNOTSUPP; | ||
817 | |||
818 | dasd_eckd_fill_rcd_cqr(device, cqr, rcd_buffer, lpm); | ||
819 | clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); | ||
820 | cqr->retries = 5; | ||
821 | rc = dasd_sleep_on_immediatly(cqr); | ||
822 | return rc; | ||
788 | } | 823 | } |
789 | 824 | ||
790 | static int dasd_eckd_read_conf_lpm(struct dasd_device *device, | 825 | static int dasd_eckd_read_conf_lpm(struct dasd_device *device, |
@@ -797,32 +832,29 @@ static int dasd_eckd_read_conf_lpm(struct dasd_device *device, | |||
797 | struct dasd_ccw_req *cqr; | 832 | struct dasd_ccw_req *cqr; |
798 | 833 | ||
799 | /* | 834 | /* |
800 | * scan for RCD command in extended SenseID data | 835 | * sanity check: scan for RCD command in extended SenseID data |
836 | * some devices do not support RCD | ||
801 | */ | 837 | */ |
802 | ciw = ccw_device_get_ciw(device->cdev, CIW_TYPE_RCD); | 838 | ciw = ccw_device_get_ciw(device->cdev, CIW_TYPE_RCD); |
803 | if (!ciw || ciw->cmd == 0) { | 839 | if (!ciw || ciw->cmd != DASD_ECKD_CCW_RCD) { |
804 | ret = -EOPNOTSUPP; | 840 | ret = -EOPNOTSUPP; |
805 | goto out_error; | 841 | goto out_error; |
806 | } | 842 | } |
807 | rcd_buf = kzalloc(ciw->count, GFP_KERNEL | GFP_DMA); | 843 | rcd_buf = kzalloc(DASD_ECKD_RCD_DATA_SIZE, GFP_KERNEL | GFP_DMA); |
808 | if (!rcd_buf) { | 844 | if (!rcd_buf) { |
809 | ret = -ENOMEM; | 845 | ret = -ENOMEM; |
810 | goto out_error; | 846 | goto out_error; |
811 | } | 847 | } |
812 | 848 | cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* RCD */, | |
813 | /* | 849 | 0, /* use rcd_buf as data ara */ |
814 | * buffer has to start with EBCDIC "V1.0" to show | 850 | device); |
815 | * support for virtual device SNEQ | ||
816 | */ | ||
817 | rcd_buf[0] = 0xE5; | ||
818 | rcd_buf[1] = 0xF1; | ||
819 | rcd_buf[2] = 0x4B; | ||
820 | rcd_buf[3] = 0xF0; | ||
821 | cqr = dasd_eckd_build_rcd_lpm(device, rcd_buf, ciw, lpm); | ||
822 | if (IS_ERR(cqr)) { | 851 | if (IS_ERR(cqr)) { |
823 | ret = PTR_ERR(cqr); | 852 | DBF_DEV_EVENT(DBF_WARNING, device, "%s", |
853 | "Could not allocate RCD request"); | ||
854 | ret = -ENOMEM; | ||
824 | goto out_error; | 855 | goto out_error; |
825 | } | 856 | } |
857 | dasd_eckd_fill_rcd_cqr(device, cqr, rcd_buf, lpm); | ||
826 | ret = dasd_sleep_on(cqr); | 858 | ret = dasd_sleep_on(cqr); |
827 | /* | 859 | /* |
828 | * on success we update the user input parms | 860 | * on success we update the user input parms |
@@ -831,7 +863,7 @@ static int dasd_eckd_read_conf_lpm(struct dasd_device *device, | |||
831 | if (ret) | 863 | if (ret) |
832 | goto out_error; | 864 | goto out_error; |
833 | 865 | ||
834 | *rcd_buffer_size = ciw->count; | 866 | *rcd_buffer_size = DASD_ECKD_RCD_DATA_SIZE; |
835 | *rcd_buffer = rcd_buf; | 867 | *rcd_buffer = rcd_buf; |
836 | return 0; | 868 | return 0; |
837 | out_error: | 869 | out_error: |
@@ -901,18 +933,18 @@ static int dasd_eckd_read_conf(struct dasd_device *device) | |||
901 | void *conf_data; | 933 | void *conf_data; |
902 | int conf_len, conf_data_saved; | 934 | int conf_len, conf_data_saved; |
903 | int rc; | 935 | int rc; |
904 | __u8 lpm; | 936 | __u8 lpm, opm; |
905 | struct dasd_eckd_private *private; | 937 | struct dasd_eckd_private *private; |
906 | struct dasd_eckd_path *path_data; | 938 | struct dasd_path *path_data; |
907 | 939 | ||
908 | private = (struct dasd_eckd_private *) device->private; | 940 | private = (struct dasd_eckd_private *) device->private; |
909 | path_data = (struct dasd_eckd_path *) &private->path_data; | 941 | path_data = &device->path_data; |
910 | path_data->opm = ccw_device_get_path_mask(device->cdev); | 942 | opm = ccw_device_get_path_mask(device->cdev); |
911 | lpm = 0x80; | 943 | lpm = 0x80; |
912 | conf_data_saved = 0; | 944 | conf_data_saved = 0; |
913 | /* get configuration data per operational path */ | 945 | /* get configuration data per operational path */ |
914 | for (lpm = 0x80; lpm; lpm>>= 1) { | 946 | for (lpm = 0x80; lpm; lpm>>= 1) { |
915 | if (lpm & path_data->opm){ | 947 | if (lpm & opm) { |
916 | rc = dasd_eckd_read_conf_lpm(device, &conf_data, | 948 | rc = dasd_eckd_read_conf_lpm(device, &conf_data, |
917 | &conf_len, lpm); | 949 | &conf_len, lpm); |
918 | if (rc && rc != -EOPNOTSUPP) { /* -EOPNOTSUPP is ok */ | 950 | if (rc && rc != -EOPNOTSUPP) { /* -EOPNOTSUPP is ok */ |
@@ -925,6 +957,8 @@ static int dasd_eckd_read_conf(struct dasd_device *device) | |||
925 | DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", | 957 | DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", |
926 | "No configuration data " | 958 | "No configuration data " |
927 | "retrieved"); | 959 | "retrieved"); |
960 | /* no further analysis possible */ | ||
961 | path_data->opm |= lpm; | ||
928 | continue; /* no error */ | 962 | continue; /* no error */ |
929 | } | 963 | } |
930 | /* save first valid configuration data */ | 964 | /* save first valid configuration data */ |
@@ -948,6 +982,7 @@ static int dasd_eckd_read_conf(struct dasd_device *device) | |||
948 | path_data->ppm |= lpm; | 982 | path_data->ppm |= lpm; |
949 | break; | 983 | break; |
950 | } | 984 | } |
985 | path_data->opm |= lpm; | ||
951 | if (conf_data != private->conf_data) | 986 | if (conf_data != private->conf_data) |
952 | kfree(conf_data); | 987 | kfree(conf_data); |
953 | } | 988 | } |
@@ -955,6 +990,140 @@ static int dasd_eckd_read_conf(struct dasd_device *device) | |||
955 | return 0; | 990 | return 0; |
956 | } | 991 | } |
957 | 992 | ||
993 | static int verify_fcx_max_data(struct dasd_device *device, __u8 lpm) | ||
994 | { | ||
995 | struct dasd_eckd_private *private; | ||
996 | int mdc; | ||
997 | u32 fcx_max_data; | ||
998 | |||
999 | private = (struct dasd_eckd_private *) device->private; | ||
1000 | if (private->fcx_max_data) { | ||
1001 | mdc = ccw_device_get_mdc(device->cdev, lpm); | ||
1002 | if ((mdc < 0)) { | ||
1003 | dev_warn(&device->cdev->dev, | ||
1004 | "Detecting the maximum data size for zHPF " | ||
1005 | "requests failed (rc=%d) for a new path %x\n", | ||
1006 | mdc, lpm); | ||
1007 | return mdc; | ||
1008 | } | ||
1009 | fcx_max_data = mdc * FCX_MAX_DATA_FACTOR; | ||
1010 | if (fcx_max_data < private->fcx_max_data) { | ||
1011 | dev_warn(&device->cdev->dev, | ||
1012 | "The maximum data size for zHPF requests %u " | ||
1013 | "on a new path %x is below the active maximum " | ||
1014 | "%u\n", fcx_max_data, lpm, | ||
1015 | private->fcx_max_data); | ||
1016 | return -EACCES; | ||
1017 | } | ||
1018 | } | ||
1019 | return 0; | ||
1020 | } | ||
1021 | |||
1022 | static void do_path_verification_work(struct work_struct *work) | ||
1023 | { | ||
1024 | struct path_verification_work_data *data; | ||
1025 | struct dasd_device *device; | ||
1026 | __u8 lpm, opm, npm, ppm, epm; | ||
1027 | unsigned long flags; | ||
1028 | int rc; | ||
1029 | |||
1030 | data = container_of(work, struct path_verification_work_data, worker); | ||
1031 | device = data->device; | ||
1032 | |||
1033 | opm = 0; | ||
1034 | npm = 0; | ||
1035 | ppm = 0; | ||
1036 | epm = 0; | ||
1037 | for (lpm = 0x80; lpm; lpm >>= 1) { | ||
1038 | if (lpm & data->tbvpm) { | ||
1039 | memset(data->rcd_buffer, 0, sizeof(data->rcd_buffer)); | ||
1040 | memset(&data->cqr, 0, sizeof(data->cqr)); | ||
1041 | data->cqr.cpaddr = &data->ccw; | ||
1042 | rc = dasd_eckd_read_conf_immediately(device, &data->cqr, | ||
1043 | data->rcd_buffer, | ||
1044 | lpm); | ||
1045 | if (!rc) { | ||
1046 | switch (dasd_eckd_path_access(data->rcd_buffer, | ||
1047 | DASD_ECKD_RCD_DATA_SIZE)) { | ||
1048 | case 0x02: | ||
1049 | npm |= lpm; | ||
1050 | break; | ||
1051 | case 0x03: | ||
1052 | ppm |= lpm; | ||
1053 | break; | ||
1054 | } | ||
1055 | opm |= lpm; | ||
1056 | } else if (rc == -EOPNOTSUPP) { | ||
1057 | DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", | ||
1058 | "path verification: No configuration " | ||
1059 | "data retrieved"); | ||
1060 | opm |= lpm; | ||
1061 | } else if (rc == -EAGAIN) { | ||
1062 | DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", | ||
1063 | "path verification: device is stopped," | ||
1064 | " try again later"); | ||
1065 | epm |= lpm; | ||
1066 | } else { | ||
1067 | dev_warn(&device->cdev->dev, | ||
1068 | "Reading device feature codes failed " | ||
1069 | "(rc=%d) for new path %x\n", rc, lpm); | ||
1070 | continue; | ||
1071 | } | ||
1072 | if (verify_fcx_max_data(device, lpm)) { | ||
1073 | opm &= ~lpm; | ||
1074 | npm &= ~lpm; | ||
1075 | ppm &= ~lpm; | ||
1076 | } | ||
1077 | } | ||
1078 | } | ||
1079 | /* | ||
1080 | * There is a small chance that a path is lost again between | ||
1081 | * above path verification and the following modification of | ||
1082 | * the device opm mask. We could avoid that race here by using | ||
1083 | * yet another path mask, but we rather deal with this unlikely | ||
1084 | * situation in dasd_start_IO. | ||
1085 | */ | ||
1086 | spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); | ||
1087 | if (!device->path_data.opm && opm) { | ||
1088 | device->path_data.opm = opm; | ||
1089 | dasd_generic_path_operational(device); | ||
1090 | } else | ||
1091 | device->path_data.opm |= opm; | ||
1092 | device->path_data.npm |= npm; | ||
1093 | device->path_data.ppm |= ppm; | ||
1094 | device->path_data.tbvpm |= epm; | ||
1095 | spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); | ||
1096 | |||
1097 | dasd_put_device(device); | ||
1098 | if (data->isglobal) | ||
1099 | mutex_unlock(&dasd_path_verification_mutex); | ||
1100 | else | ||
1101 | kfree(data); | ||
1102 | } | ||
1103 | |||
1104 | static int dasd_eckd_verify_path(struct dasd_device *device, __u8 lpm) | ||
1105 | { | ||
1106 | struct path_verification_work_data *data; | ||
1107 | |||
1108 | data = kmalloc(sizeof(*data), GFP_ATOMIC | GFP_DMA); | ||
1109 | if (!data) { | ||
1110 | if (mutex_trylock(&dasd_path_verification_mutex)) { | ||
1111 | data = path_verification_worker; | ||
1112 | data->isglobal = 1; | ||
1113 | } else | ||
1114 | return -ENOMEM; | ||
1115 | } else { | ||
1116 | memset(data, 0, sizeof(*data)); | ||
1117 | data->isglobal = 0; | ||
1118 | } | ||
1119 | INIT_WORK(&data->worker, do_path_verification_work); | ||
1120 | dasd_get_device(device); | ||
1121 | data->device = device; | ||
1122 | data->tbvpm = lpm; | ||
1123 | schedule_work(&data->worker); | ||
1124 | return 0; | ||
1125 | } | ||
1126 | |||
958 | static int dasd_eckd_read_features(struct dasd_device *device) | 1127 | static int dasd_eckd_read_features(struct dasd_device *device) |
959 | { | 1128 | { |
960 | struct dasd_psf_prssd_data *prssdp; | 1129 | struct dasd_psf_prssd_data *prssdp; |
@@ -1749,6 +1918,7 @@ static void dasd_eckd_handle_terminated_request(struct dasd_ccw_req *cqr) | |||
1749 | if (cqr->block && (cqr->startdev != cqr->block->base)) { | 1918 | if (cqr->block && (cqr->startdev != cqr->block->base)) { |
1750 | dasd_eckd_reset_ccw_to_base_io(cqr); | 1919 | dasd_eckd_reset_ccw_to_base_io(cqr); |
1751 | cqr->startdev = cqr->block->base; | 1920 | cqr->startdev = cqr->block->base; |
1921 | cqr->lpm = cqr->block->base->path_data.opm; | ||
1752 | } | 1922 | } |
1753 | }; | 1923 | }; |
1754 | 1924 | ||
@@ -2017,7 +2187,7 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_single( | |||
2017 | cqr->memdev = startdev; | 2187 | cqr->memdev = startdev; |
2018 | cqr->block = block; | 2188 | cqr->block = block; |
2019 | cqr->expires = startdev->default_expires * HZ; /* default 5 minutes */ | 2189 | cqr->expires = startdev->default_expires * HZ; /* default 5 minutes */ |
2020 | cqr->lpm = private->path_data.ppm; | 2190 | cqr->lpm = startdev->path_data.ppm; |
2021 | cqr->retries = 256; | 2191 | cqr->retries = 256; |
2022 | cqr->buildclk = get_clock(); | 2192 | cqr->buildclk = get_clock(); |
2023 | cqr->status = DASD_CQR_FILLED; | 2193 | cqr->status = DASD_CQR_FILLED; |
@@ -2194,7 +2364,7 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_track( | |||
2194 | cqr->memdev = startdev; | 2364 | cqr->memdev = startdev; |
2195 | cqr->block = block; | 2365 | cqr->block = block; |
2196 | cqr->expires = startdev->default_expires * HZ; /* default 5 minutes */ | 2366 | cqr->expires = startdev->default_expires * HZ; /* default 5 minutes */ |
2197 | cqr->lpm = private->path_data.ppm; | 2367 | cqr->lpm = startdev->path_data.ppm; |
2198 | cqr->retries = 256; | 2368 | cqr->retries = 256; |
2199 | cqr->buildclk = get_clock(); | 2369 | cqr->buildclk = get_clock(); |
2200 | cqr->status = DASD_CQR_FILLED; | 2370 | cqr->status = DASD_CQR_FILLED; |
@@ -2484,7 +2654,7 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track( | |||
2484 | cqr->memdev = startdev; | 2654 | cqr->memdev = startdev; |
2485 | cqr->block = block; | 2655 | cqr->block = block; |
2486 | cqr->expires = startdev->default_expires * HZ; /* default 5 minutes */ | 2656 | cqr->expires = startdev->default_expires * HZ; /* default 5 minutes */ |
2487 | cqr->lpm = private->path_data.ppm; | 2657 | cqr->lpm = startdev->path_data.ppm; |
2488 | cqr->retries = 256; | 2658 | cqr->retries = 256; |
2489 | cqr->buildclk = get_clock(); | 2659 | cqr->buildclk = get_clock(); |
2490 | cqr->status = DASD_CQR_FILLED; | 2660 | cqr->status = DASD_CQR_FILLED; |
@@ -3624,6 +3794,7 @@ static struct ccw_driver dasd_eckd_driver = { | |||
3624 | .set_offline = dasd_generic_set_offline, | 3794 | .set_offline = dasd_generic_set_offline, |
3625 | .set_online = dasd_eckd_set_online, | 3795 | .set_online = dasd_eckd_set_online, |
3626 | .notify = dasd_generic_notify, | 3796 | .notify = dasd_generic_notify, |
3797 | .path_event = dasd_generic_path_event, | ||
3627 | .freeze = dasd_generic_pm_freeze, | 3798 | .freeze = dasd_generic_pm_freeze, |
3628 | .thaw = dasd_generic_restore_device, | 3799 | .thaw = dasd_generic_restore_device, |
3629 | .restore = dasd_generic_restore_device, | 3800 | .restore = dasd_generic_restore_device, |
@@ -3651,6 +3822,7 @@ static struct dasd_discipline dasd_eckd_discipline = { | |||
3651 | .check_device = dasd_eckd_check_characteristics, | 3822 | .check_device = dasd_eckd_check_characteristics, |
3652 | .uncheck_device = dasd_eckd_uncheck_device, | 3823 | .uncheck_device = dasd_eckd_uncheck_device, |
3653 | .do_analysis = dasd_eckd_do_analysis, | 3824 | .do_analysis = dasd_eckd_do_analysis, |
3825 | .verify_path = dasd_eckd_verify_path, | ||
3654 | .ready_to_online = dasd_eckd_ready_to_online, | 3826 | .ready_to_online = dasd_eckd_ready_to_online, |
3655 | .online_to_ready = dasd_eckd_online_to_ready, | 3827 | .online_to_ready = dasd_eckd_online_to_ready, |
3656 | .fill_geometry = dasd_eckd_fill_geometry, | 3828 | .fill_geometry = dasd_eckd_fill_geometry, |
@@ -3683,11 +3855,19 @@ dasd_eckd_init(void) | |||
3683 | GFP_KERNEL | GFP_DMA); | 3855 | GFP_KERNEL | GFP_DMA); |
3684 | if (!dasd_reserve_req) | 3856 | if (!dasd_reserve_req) |
3685 | return -ENOMEM; | 3857 | return -ENOMEM; |
3858 | path_verification_worker = kmalloc(sizeof(*path_verification_worker), | ||
3859 | GFP_KERNEL | GFP_DMA); | ||
3860 | if (!path_verification_worker) { | ||
3861 | kfree(dasd_reserve_req); | ||
3862 | return -ENOMEM; | ||
3863 | } | ||
3686 | ret = ccw_driver_register(&dasd_eckd_driver); | 3864 | ret = ccw_driver_register(&dasd_eckd_driver); |
3687 | if (!ret) | 3865 | if (!ret) |
3688 | wait_for_device_probe(); | 3866 | wait_for_device_probe(); |
3689 | else | 3867 | else { |
3868 | kfree(path_verification_worker); | ||
3690 | kfree(dasd_reserve_req); | 3869 | kfree(dasd_reserve_req); |
3870 | } | ||
3691 | return ret; | 3871 | return ret; |
3692 | } | 3872 | } |
3693 | 3873 | ||
@@ -3695,6 +3875,7 @@ static void __exit | |||
3695 | dasd_eckd_cleanup(void) | 3875 | dasd_eckd_cleanup(void) |
3696 | { | 3876 | { |
3697 | ccw_driver_unregister(&dasd_eckd_driver); | 3877 | ccw_driver_unregister(&dasd_eckd_driver); |
3878 | kfree(path_verification_worker); | ||
3698 | kfree(dasd_reserve_req); | 3879 | kfree(dasd_reserve_req); |
3699 | } | 3880 | } |
3700 | 3881 | ||