diff options
author | Cornelia Huck <cornelia.huck@de.ibm.com> | 2006-03-24 06:15:14 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-03-24 10:33:16 -0500 |
commit | 495a5b45ac33b8fe2c49780fdbcc8014cb6d6ddc (patch) | |
tree | b456ebc9e8f53edd726c903401b5e40bbb9562d5 /drivers/s390 | |
parent | dc06010c62da773321258df1d8a1708a3158e29d (diff) |
[PATCH] s390: channel path measurements
Gather extended measurements for channel paths from the channel subsystem and
expose them to userspace via a sysfs attribute.
Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'drivers/s390')
-rw-r--r-- | drivers/s390/cio/chsc.c | 454 | ||||
-rw-r--r-- | drivers/s390/cio/chsc.h | 22 | ||||
-rw-r--r-- | drivers/s390/cio/css.c | 41 | ||||
-rw-r--r-- | drivers/s390/cio/css.h | 6 |
4 files changed, 500 insertions, 23 deletions
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index f4183d660258..8b57fe62a9f9 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c | |||
@@ -98,10 +98,8 @@ chsc_get_sch_desc_irq(struct subchannel *sch, void *page) | |||
98 | 98 | ||
99 | ssd_area = page; | 99 | ssd_area = page; |
100 | 100 | ||
101 | ssd_area->request = (struct chsc_header) { | 101 | ssd_area->request.length = 0x0010; |
102 | .length = 0x0010, | 102 | ssd_area->request.code = 0x0004; |
103 | .code = 0x0004, | ||
104 | }; | ||
105 | 103 | ||
106 | ssd_area->ssid = sch->schid.ssid; | 104 | ssd_area->ssid = sch->schid.ssid; |
107 | ssd_area->f_sch = sch->schid.sch_no; | 105 | ssd_area->f_sch = sch->schid.sch_no; |
@@ -517,10 +515,8 @@ chsc_process_crw(void) | |||
517 | struct device *dev; | 515 | struct device *dev; |
518 | memset(sei_area, 0, sizeof(*sei_area)); | 516 | memset(sei_area, 0, sizeof(*sei_area)); |
519 | memset(&res_data, 0, sizeof(struct res_acc_data)); | 517 | memset(&res_data, 0, sizeof(struct res_acc_data)); |
520 | sei_area->request = (struct chsc_header) { | 518 | sei_area->request.length = 0x0010; |
521 | .length = 0x0010, | 519 | sei_area->request.code = 0x000e; |
522 | .code = 0x000e, | ||
523 | }; | ||
524 | 520 | ||
525 | ccode = chsc(sei_area); | 521 | ccode = chsc(sei_area); |
526 | if (ccode > 0) | 522 | if (ccode > 0) |
@@ -875,6 +871,264 @@ s390_vary_chpid( __u8 chpid, int on) | |||
875 | } | 871 | } |
876 | 872 | ||
877 | /* | 873 | /* |
874 | * Channel measurement related functions | ||
875 | */ | ||
876 | static ssize_t | ||
877 | chp_measurement_chars_read(struct kobject *kobj, char *buf, loff_t off, | ||
878 | size_t count) | ||
879 | { | ||
880 | struct channel_path *chp; | ||
881 | unsigned int size; | ||
882 | |||
883 | chp = to_channelpath(container_of(kobj, struct device, kobj)); | ||
884 | if (!chp->cmg_chars) | ||
885 | return 0; | ||
886 | |||
887 | size = sizeof(struct cmg_chars); | ||
888 | |||
889 | if (off > size) | ||
890 | return 0; | ||
891 | if (off + count > size) | ||
892 | count = size - off; | ||
893 | memcpy(buf, chp->cmg_chars + off, count); | ||
894 | return count; | ||
895 | } | ||
896 | |||
897 | static struct bin_attribute chp_measurement_chars_attr = { | ||
898 | .attr = { | ||
899 | .name = "measurement_chars", | ||
900 | .mode = S_IRUSR, | ||
901 | .owner = THIS_MODULE, | ||
902 | }, | ||
903 | .size = sizeof(struct cmg_chars), | ||
904 | .read = chp_measurement_chars_read, | ||
905 | }; | ||
906 | |||
907 | static void | ||
908 | chp_measurement_copy_block(struct cmg_entry *buf, | ||
909 | struct channel_subsystem *css, int chpid) | ||
910 | { | ||
911 | void *area; | ||
912 | struct cmg_entry *entry, reference_buf; | ||
913 | int idx; | ||
914 | |||
915 | if (chpid < 128) { | ||
916 | area = css->cub_addr1; | ||
917 | idx = chpid; | ||
918 | } else { | ||
919 | area = css->cub_addr2; | ||
920 | idx = chpid - 128; | ||
921 | } | ||
922 | entry = area + (idx * sizeof(struct cmg_entry)); | ||
923 | do { | ||
924 | memcpy(buf, entry, sizeof(*entry)); | ||
925 | memcpy(&reference_buf, entry, sizeof(*entry)); | ||
926 | } while (reference_buf.values[0] != buf->values[0]); | ||
927 | } | ||
928 | |||
929 | static ssize_t | ||
930 | chp_measurement_read(struct kobject *kobj, char *buf, loff_t off, size_t count) | ||
931 | { | ||
932 | struct channel_path *chp; | ||
933 | struct channel_subsystem *css; | ||
934 | unsigned int size; | ||
935 | |||
936 | chp = to_channelpath(container_of(kobj, struct device, kobj)); | ||
937 | css = to_css(chp->dev.parent); | ||
938 | |||
939 | size = sizeof(struct cmg_chars); | ||
940 | |||
941 | /* Only allow single reads. */ | ||
942 | if (off || count < size) | ||
943 | return 0; | ||
944 | chp_measurement_copy_block((struct cmg_entry *)buf, css, chp->id); | ||
945 | return count; | ||
946 | } | ||
947 | |||
948 | static struct bin_attribute chp_measurement_attr = { | ||
949 | .attr = { | ||
950 | .name = "measurement", | ||
951 | .mode = S_IRUSR, | ||
952 | .owner = THIS_MODULE, | ||
953 | }, | ||
954 | .size = sizeof(struct cmg_entry), | ||
955 | .read = chp_measurement_read, | ||
956 | }; | ||
957 | |||
958 | static void | ||
959 | chsc_remove_chp_cmg_attr(struct channel_path *chp) | ||
960 | { | ||
961 | sysfs_remove_bin_file(&chp->dev.kobj, &chp_measurement_chars_attr); | ||
962 | sysfs_remove_bin_file(&chp->dev.kobj, &chp_measurement_attr); | ||
963 | } | ||
964 | |||
965 | static int | ||
966 | chsc_add_chp_cmg_attr(struct channel_path *chp) | ||
967 | { | ||
968 | int ret; | ||
969 | |||
970 | ret = sysfs_create_bin_file(&chp->dev.kobj, | ||
971 | &chp_measurement_chars_attr); | ||
972 | if (ret) | ||
973 | return ret; | ||
974 | ret = sysfs_create_bin_file(&chp->dev.kobj, &chp_measurement_attr); | ||
975 | if (ret) | ||
976 | sysfs_remove_bin_file(&chp->dev.kobj, | ||
977 | &chp_measurement_chars_attr); | ||
978 | return ret; | ||
979 | } | ||
980 | |||
981 | static void | ||
982 | chsc_remove_cmg_attr(struct channel_subsystem *css) | ||
983 | { | ||
984 | int i; | ||
985 | |||
986 | for (i = 0; i <= __MAX_CHPID; i++) { | ||
987 | if (!css->chps[i]) | ||
988 | continue; | ||
989 | chsc_remove_chp_cmg_attr(css->chps[i]); | ||
990 | } | ||
991 | } | ||
992 | |||
993 | static int | ||
994 | chsc_add_cmg_attr(struct channel_subsystem *css) | ||
995 | { | ||
996 | int i, ret; | ||
997 | |||
998 | ret = 0; | ||
999 | for (i = 0; i <= __MAX_CHPID; i++) { | ||
1000 | if (!css->chps[i]) | ||
1001 | continue; | ||
1002 | ret = chsc_add_chp_cmg_attr(css->chps[i]); | ||
1003 | if (ret) | ||
1004 | goto cleanup; | ||
1005 | } | ||
1006 | return ret; | ||
1007 | cleanup: | ||
1008 | for (--i; i >= 0; i--) { | ||
1009 | if (!css->chps[i]) | ||
1010 | continue; | ||
1011 | chsc_remove_chp_cmg_attr(css->chps[i]); | ||
1012 | } | ||
1013 | return ret; | ||
1014 | } | ||
1015 | |||
1016 | |||
1017 | static int | ||
1018 | __chsc_do_secm(struct channel_subsystem *css, int enable, void *page) | ||
1019 | { | ||
1020 | struct { | ||
1021 | struct chsc_header request; | ||
1022 | u32 operation_code : 2; | ||
1023 | u32 : 30; | ||
1024 | u32 key : 4; | ||
1025 | u32 : 28; | ||
1026 | u32 zeroes1; | ||
1027 | u32 cub_addr1; | ||
1028 | u32 zeroes2; | ||
1029 | u32 cub_addr2; | ||
1030 | u32 reserved[13]; | ||
1031 | struct chsc_header response; | ||
1032 | u32 status : 8; | ||
1033 | u32 : 4; | ||
1034 | u32 fmt : 4; | ||
1035 | u32 : 16; | ||
1036 | } *secm_area; | ||
1037 | int ret, ccode; | ||
1038 | |||
1039 | secm_area = page; | ||
1040 | secm_area->request.length = 0x0050; | ||
1041 | secm_area->request.code = 0x0016; | ||
1042 | |||
1043 | secm_area->key = PAGE_DEFAULT_KEY; | ||
1044 | secm_area->cub_addr1 = (u64)(unsigned long)css->cub_addr1; | ||
1045 | secm_area->cub_addr2 = (u64)(unsigned long)css->cub_addr2; | ||
1046 | |||
1047 | secm_area->operation_code = enable ? 0 : 1; | ||
1048 | |||
1049 | ccode = chsc(secm_area); | ||
1050 | if (ccode > 0) | ||
1051 | return (ccode == 3) ? -ENODEV : -EBUSY; | ||
1052 | |||
1053 | switch (secm_area->response.code) { | ||
1054 | case 0x0001: /* Success. */ | ||
1055 | ret = 0; | ||
1056 | break; | ||
1057 | case 0x0003: /* Invalid block. */ | ||
1058 | case 0x0007: /* Invalid format. */ | ||
1059 | case 0x0008: /* Other invalid block. */ | ||
1060 | CIO_CRW_EVENT(2, "Error in chsc request block!\n"); | ||
1061 | ret = -EINVAL; | ||
1062 | break; | ||
1063 | case 0x0004: /* Command not provided in model. */ | ||
1064 | CIO_CRW_EVENT(2, "Model does not provide secm\n"); | ||
1065 | ret = -EOPNOTSUPP; | ||
1066 | break; | ||
1067 | case 0x0102: /* cub adresses incorrect */ | ||
1068 | CIO_CRW_EVENT(2, "Invalid addresses in chsc request block\n"); | ||
1069 | ret = -EINVAL; | ||
1070 | break; | ||
1071 | case 0x0103: /* key error */ | ||
1072 | CIO_CRW_EVENT(2, "Access key error in secm\n"); | ||
1073 | ret = -EINVAL; | ||
1074 | break; | ||
1075 | case 0x0105: /* error while starting */ | ||
1076 | CIO_CRW_EVENT(2, "Error while starting channel measurement\n"); | ||
1077 | ret = -EIO; | ||
1078 | break; | ||
1079 | default: | ||
1080 | CIO_CRW_EVENT(2, "Unknown CHSC response %d\n", | ||
1081 | secm_area->response.code); | ||
1082 | ret = -EIO; | ||
1083 | } | ||
1084 | return ret; | ||
1085 | } | ||
1086 | |||
1087 | int | ||
1088 | chsc_secm(struct channel_subsystem *css, int enable) | ||
1089 | { | ||
1090 | void *secm_area; | ||
1091 | int ret; | ||
1092 | |||
1093 | secm_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); | ||
1094 | if (!secm_area) | ||
1095 | return -ENOMEM; | ||
1096 | |||
1097 | mutex_lock(&css->mutex); | ||
1098 | if (enable && !css->cm_enabled) { | ||
1099 | css->cub_addr1 = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); | ||
1100 | css->cub_addr2 = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); | ||
1101 | if (!css->cub_addr1 || !css->cub_addr2) { | ||
1102 | free_page((unsigned long)css->cub_addr1); | ||
1103 | free_page((unsigned long)css->cub_addr2); | ||
1104 | free_page((unsigned long)secm_area); | ||
1105 | mutex_unlock(&css->mutex); | ||
1106 | return -ENOMEM; | ||
1107 | } | ||
1108 | } | ||
1109 | ret = __chsc_do_secm(css, enable, secm_area); | ||
1110 | if (!ret) { | ||
1111 | css->cm_enabled = enable; | ||
1112 | if (css->cm_enabled) { | ||
1113 | ret = chsc_add_cmg_attr(css); | ||
1114 | if (ret) { | ||
1115 | memset(secm_area, 0, PAGE_SIZE); | ||
1116 | __chsc_do_secm(css, 0, secm_area); | ||
1117 | css->cm_enabled = 0; | ||
1118 | } | ||
1119 | } else | ||
1120 | chsc_remove_cmg_attr(css); | ||
1121 | } | ||
1122 | if (enable && !css->cm_enabled) { | ||
1123 | free_page((unsigned long)css->cub_addr1); | ||
1124 | free_page((unsigned long)css->cub_addr2); | ||
1125 | } | ||
1126 | mutex_unlock(&css->mutex); | ||
1127 | free_page((unsigned long)secm_area); | ||
1128 | return ret; | ||
1129 | } | ||
1130 | |||
1131 | /* | ||
878 | * Files for the channel path entries. | 1132 | * Files for the channel path entries. |
879 | */ | 1133 | */ |
880 | static ssize_t | 1134 | static ssize_t |
@@ -925,9 +1179,39 @@ chp_type_show(struct device *dev, struct device_attribute *attr, char *buf) | |||
925 | 1179 | ||
926 | static DEVICE_ATTR(type, 0444, chp_type_show, NULL); | 1180 | static DEVICE_ATTR(type, 0444, chp_type_show, NULL); |
927 | 1181 | ||
1182 | static ssize_t | ||
1183 | chp_cmg_show(struct device *dev, struct device_attribute *attr, char *buf) | ||
1184 | { | ||
1185 | struct channel_path *chp = to_channelpath(dev); | ||
1186 | |||
1187 | if (!chp) | ||
1188 | return 0; | ||
1189 | if (chp->cmg == -1) /* channel measurements not available */ | ||
1190 | return sprintf(buf, "unknown\n"); | ||
1191 | return sprintf(buf, "%x\n", chp->cmg); | ||
1192 | } | ||
1193 | |||
1194 | static DEVICE_ATTR(cmg, 0444, chp_cmg_show, NULL); | ||
1195 | |||
1196 | static ssize_t | ||
1197 | chp_shared_show(struct device *dev, struct device_attribute *attr, char *buf) | ||
1198 | { | ||
1199 | struct channel_path *chp = to_channelpath(dev); | ||
1200 | |||
1201 | if (!chp) | ||
1202 | return 0; | ||
1203 | if (chp->shared == -1) /* channel measurements not available */ | ||
1204 | return sprintf(buf, "unknown\n"); | ||
1205 | return sprintf(buf, "%x\n", chp->shared); | ||
1206 | } | ||
1207 | |||
1208 | static DEVICE_ATTR(shared, 0444, chp_shared_show, NULL); | ||
1209 | |||
928 | static struct attribute * chp_attrs[] = { | 1210 | static struct attribute * chp_attrs[] = { |
929 | &dev_attr_status.attr, | 1211 | &dev_attr_status.attr, |
930 | &dev_attr_type.attr, | 1212 | &dev_attr_type.attr, |
1213 | &dev_attr_cmg.attr, | ||
1214 | &dev_attr_shared.attr, | ||
931 | NULL, | 1215 | NULL, |
932 | }; | 1216 | }; |
933 | 1217 | ||
@@ -966,10 +1250,8 @@ chsc_determine_channel_path_description(int chpid, | |||
966 | if (!scpd_area) | 1250 | if (!scpd_area) |
967 | return -ENOMEM; | 1251 | return -ENOMEM; |
968 | 1252 | ||
969 | scpd_area->request = (struct chsc_header) { | 1253 | scpd_area->request.length = 0x0010; |
970 | .length = 0x0010, | 1254 | scpd_area->request.code = 0x0002; |
971 | .code = 0x0002, | ||
972 | }; | ||
973 | 1255 | ||
974 | scpd_area->first_chpid = chpid; | 1256 | scpd_area->first_chpid = chpid; |
975 | scpd_area->last_chpid = chpid; | 1257 | scpd_area->last_chpid = chpid; |
@@ -1006,6 +1288,111 @@ out: | |||
1006 | return ret; | 1288 | return ret; |
1007 | } | 1289 | } |
1008 | 1290 | ||
1291 | static void | ||
1292 | chsc_initialize_cmg_chars(struct channel_path *chp, u8 cmcv, | ||
1293 | struct cmg_chars *chars) | ||
1294 | { | ||
1295 | switch (chp->cmg) { | ||
1296 | case 2: | ||
1297 | case 3: | ||
1298 | chp->cmg_chars = kmalloc(sizeof(struct cmg_chars), | ||
1299 | GFP_KERNEL); | ||
1300 | if (chp->cmg_chars) { | ||
1301 | int i, mask; | ||
1302 | struct cmg_chars *cmg_chars; | ||
1303 | |||
1304 | cmg_chars = chp->cmg_chars; | ||
1305 | for (i = 0; i < NR_MEASUREMENT_CHARS; i++) { | ||
1306 | mask = 0x80 >> (i + 3); | ||
1307 | if (cmcv & mask) | ||
1308 | cmg_chars->values[i] = chars->values[i]; | ||
1309 | else | ||
1310 | cmg_chars->values[i] = 0; | ||
1311 | } | ||
1312 | } | ||
1313 | break; | ||
1314 | default: | ||
1315 | /* No cmg-dependent data. */ | ||
1316 | break; | ||
1317 | } | ||
1318 | } | ||
1319 | |||
1320 | static int | ||
1321 | chsc_get_channel_measurement_chars(struct channel_path *chp) | ||
1322 | { | ||
1323 | int ccode, ret; | ||
1324 | |||
1325 | struct { | ||
1326 | struct chsc_header request; | ||
1327 | u32 : 24; | ||
1328 | u32 first_chpid : 8; | ||
1329 | u32 : 24; | ||
1330 | u32 last_chpid : 8; | ||
1331 | u32 zeroes1; | ||
1332 | struct chsc_header response; | ||
1333 | u32 zeroes2; | ||
1334 | u32 not_valid : 1; | ||
1335 | u32 shared : 1; | ||
1336 | u32 : 22; | ||
1337 | u32 chpid : 8; | ||
1338 | u32 cmcv : 5; | ||
1339 | u32 : 11; | ||
1340 | u32 cmgq : 8; | ||
1341 | u32 cmg : 8; | ||
1342 | u32 zeroes3; | ||
1343 | u32 data[NR_MEASUREMENT_CHARS]; | ||
1344 | } *scmc_area; | ||
1345 | |||
1346 | scmc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); | ||
1347 | if (!scmc_area) | ||
1348 | return -ENOMEM; | ||
1349 | |||
1350 | scmc_area->request.length = 0x0010; | ||
1351 | scmc_area->request.code = 0x0022; | ||
1352 | |||
1353 | scmc_area->first_chpid = chp->id; | ||
1354 | scmc_area->last_chpid = chp->id; | ||
1355 | |||
1356 | ccode = chsc(scmc_area); | ||
1357 | if (ccode > 0) { | ||
1358 | ret = (ccode == 3) ? -ENODEV : -EBUSY; | ||
1359 | goto out; | ||
1360 | } | ||
1361 | |||
1362 | switch (scmc_area->response.code) { | ||
1363 | case 0x0001: /* Success. */ | ||
1364 | if (!scmc_area->not_valid) { | ||
1365 | chp->cmg = scmc_area->cmg; | ||
1366 | chp->shared = scmc_area->shared; | ||
1367 | chsc_initialize_cmg_chars(chp, scmc_area->cmcv, | ||
1368 | (struct cmg_chars *) | ||
1369 | &scmc_area->data); | ||
1370 | } else { | ||
1371 | chp->cmg = -1; | ||
1372 | chp->shared = -1; | ||
1373 | } | ||
1374 | ret = 0; | ||
1375 | break; | ||
1376 | case 0x0003: /* Invalid block. */ | ||
1377 | case 0x0007: /* Invalid format. */ | ||
1378 | case 0x0008: /* Invalid bit combination. */ | ||
1379 | CIO_CRW_EVENT(2, "Error in chsc request block!\n"); | ||
1380 | ret = -EINVAL; | ||
1381 | break; | ||
1382 | case 0x0004: /* Command not provided. */ | ||
1383 | CIO_CRW_EVENT(2, "Model does not provide scmc\n"); | ||
1384 | ret = -EOPNOTSUPP; | ||
1385 | break; | ||
1386 | default: | ||
1387 | CIO_CRW_EVENT(2, "Unknown CHSC response %d\n", | ||
1388 | scmc_area->response.code); | ||
1389 | ret = -EIO; | ||
1390 | } | ||
1391 | out: | ||
1392 | free_page((unsigned long)scmc_area); | ||
1393 | return ret; | ||
1394 | } | ||
1395 | |||
1009 | /* | 1396 | /* |
1010 | * Entries for chpids on the system bus. | 1397 | * Entries for chpids on the system bus. |
1011 | * This replaces /proc/chpids. | 1398 | * This replaces /proc/chpids. |
@@ -1034,6 +1421,22 @@ new_channel_path(int chpid) | |||
1034 | ret = chsc_determine_channel_path_description(chpid, &chp->desc); | 1421 | ret = chsc_determine_channel_path_description(chpid, &chp->desc); |
1035 | if (ret) | 1422 | if (ret) |
1036 | goto out_free; | 1423 | goto out_free; |
1424 | /* Get channel-measurement characteristics. */ | ||
1425 | if (css_characteristics_avail && css_chsc_characteristics.scmc | ||
1426 | && css_chsc_characteristics.secm) { | ||
1427 | ret = chsc_get_channel_measurement_chars(chp); | ||
1428 | if (ret) | ||
1429 | goto out_free; | ||
1430 | } else { | ||
1431 | static int msg_done; | ||
1432 | |||
1433 | if (!msg_done) { | ||
1434 | printk(KERN_WARNING "cio: Channel measurements not " | ||
1435 | "available, continuing.\n"); | ||
1436 | msg_done = 1; | ||
1437 | } | ||
1438 | chp->cmg = -1; | ||
1439 | } | ||
1037 | 1440 | ||
1038 | /* make it known to the system */ | 1441 | /* make it known to the system */ |
1039 | ret = device_register(&chp->dev); | 1442 | ret = device_register(&chp->dev); |
@@ -1046,8 +1449,19 @@ new_channel_path(int chpid) | |||
1046 | if (ret) { | 1449 | if (ret) { |
1047 | device_unregister(&chp->dev); | 1450 | device_unregister(&chp->dev); |
1048 | goto out_free; | 1451 | goto out_free; |
1049 | } else | 1452 | } |
1050 | css[0]->chps[chpid] = chp; | 1453 | mutex_lock(&css[0]->mutex); |
1454 | if (css[0]->cm_enabled) { | ||
1455 | ret = chsc_add_chp_cmg_attr(chp); | ||
1456 | if (ret) { | ||
1457 | sysfs_remove_group(&chp->dev.kobj, &chp_attr_group); | ||
1458 | device_unregister(&chp->dev); | ||
1459 | mutex_unlock(&css[0]->mutex); | ||
1460 | goto out_free; | ||
1461 | } | ||
1462 | } | ||
1463 | css[0]->chps[chpid] = chp; | ||
1464 | mutex_unlock(&css[0]->mutex); | ||
1051 | return ret; | 1465 | return ret; |
1052 | out_free: | 1466 | out_free: |
1053 | kfree(chp); | 1467 | kfree(chp); |
@@ -1103,10 +1517,8 @@ chsc_enable_facility(int operation_code) | |||
1103 | sda_area = (void *)get_zeroed_page(GFP_KERNEL|GFP_DMA); | 1517 | sda_area = (void *)get_zeroed_page(GFP_KERNEL|GFP_DMA); |
1104 | if (!sda_area) | 1518 | if (!sda_area) |
1105 | return -ENOMEM; | 1519 | return -ENOMEM; |
1106 | sda_area->request = (struct chsc_header) { | 1520 | sda_area->request.length = 0x0400; |
1107 | .length = 0x0400, | 1521 | sda_area->request.code = 0x0031; |
1108 | .code = 0x0031, | ||
1109 | }; | ||
1110 | sda_area->operation_code = operation_code; | 1522 | sda_area->operation_code = operation_code; |
1111 | 1523 | ||
1112 | ret = chsc(sda_area); | 1524 | ret = chsc(sda_area); |
@@ -1161,10 +1573,8 @@ chsc_determine_css_characteristics(void) | |||
1161 | return -ENOMEM; | 1573 | return -ENOMEM; |
1162 | } | 1574 | } |
1163 | 1575 | ||
1164 | scsc_area->request = (struct chsc_header) { | 1576 | scsc_area->request.length = 0x0010; |
1165 | .length = 0x0010, | 1577 | scsc_area->request.code = 0x0010; |
1166 | .code = 0x0010, | ||
1167 | }; | ||
1168 | 1578 | ||
1169 | result = chsc(scsc_area); | 1579 | result = chsc(scsc_area); |
1170 | if (result) { | 1580 | if (result) { |
diff --git a/drivers/s390/cio/chsc.h b/drivers/s390/cio/chsc.h index 3e75095f35d0..a259245780ae 100644 --- a/drivers/s390/cio/chsc.h +++ b/drivers/s390/cio/chsc.h | |||
@@ -12,6 +12,16 @@ struct chsc_header { | |||
12 | u16 code; | 12 | u16 code; |
13 | }; | 13 | }; |
14 | 14 | ||
15 | #define NR_MEASUREMENT_CHARS 5 | ||
16 | struct cmg_chars { | ||
17 | u32 values[NR_MEASUREMENT_CHARS]; | ||
18 | }; | ||
19 | |||
20 | #define NR_MEASUREMENT_ENTRIES 8 | ||
21 | struct cmg_entry { | ||
22 | u32 values[NR_MEASUREMENT_ENTRIES]; | ||
23 | }; | ||
24 | |||
15 | struct channel_path_desc { | 25 | struct channel_path_desc { |
16 | u8 flags; | 26 | u8 flags; |
17 | u8 lsn; | 27 | u8 lsn; |
@@ -27,6 +37,10 @@ struct channel_path { | |||
27 | int id; | 37 | int id; |
28 | int state; | 38 | int state; |
29 | struct channel_path_desc desc; | 39 | struct channel_path_desc desc; |
40 | /* Channel-measurement related stuff: */ | ||
41 | int cmg; | ||
42 | int shared; | ||
43 | void *cmg_chars; | ||
30 | struct device dev; | 44 | struct device dev; |
31 | }; | 45 | }; |
32 | 46 | ||
@@ -52,7 +66,11 @@ struct css_general_char { | |||
52 | 66 | ||
53 | struct css_chsc_char { | 67 | struct css_chsc_char { |
54 | u64 res; | 68 | u64 res; |
55 | u64 : 43; | 69 | u64 : 20; |
70 | u32 secm : 1; /* bit 84 */ | ||
71 | u32 : 1; | ||
72 | u32 scmc : 1; /* bit 86 */ | ||
73 | u32 : 20; | ||
56 | u32 scssc : 1; /* bit 107 */ | 74 | u32 scssc : 1; /* bit 107 */ |
57 | u32 scsscf : 1; /* bit 108 */ | 75 | u32 scsscf : 1; /* bit 108 */ |
58 | u32 : 19; | 76 | u32 : 19; |
@@ -67,6 +85,8 @@ extern int css_characteristics_avail; | |||
67 | extern void *chsc_get_chp_desc(struct subchannel*, int); | 85 | extern void *chsc_get_chp_desc(struct subchannel*, int); |
68 | 86 | ||
69 | extern int chsc_enable_facility(int); | 87 | extern int chsc_enable_facility(int); |
88 | struct channel_subsystem; | ||
89 | extern int chsc_secm(struct channel_subsystem *, int); | ||
70 | 90 | ||
71 | #define to_channelpath(device) container_of(device, struct channel_path, dev) | 91 | #define to_channelpath(device) container_of(device, struct channel_path, dev) |
72 | 92 | ||
diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index 3c77d65960db..5b304dbacaeb 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c | |||
@@ -452,15 +452,50 @@ channel_subsystem_release(struct device *dev) | |||
452 | struct channel_subsystem *css; | 452 | struct channel_subsystem *css; |
453 | 453 | ||
454 | css = to_css(dev); | 454 | css = to_css(dev); |
455 | mutex_destroy(&css->mutex); | ||
455 | kfree(css); | 456 | kfree(css); |
456 | } | 457 | } |
457 | 458 | ||
459 | static ssize_t | ||
460 | css_cm_enable_show(struct device *dev, struct device_attribute *attr, | ||
461 | char *buf) | ||
462 | { | ||
463 | struct channel_subsystem *css = to_css(dev); | ||
464 | |||
465 | if (!css) | ||
466 | return 0; | ||
467 | return sprintf(buf, "%x\n", css->cm_enabled); | ||
468 | } | ||
469 | |||
470 | static ssize_t | ||
471 | css_cm_enable_store(struct device *dev, struct device_attribute *attr, | ||
472 | const char *buf, size_t count) | ||
473 | { | ||
474 | struct channel_subsystem *css = to_css(dev); | ||
475 | int ret; | ||
476 | |||
477 | switch (buf[0]) { | ||
478 | case '0': | ||
479 | ret = css->cm_enabled ? chsc_secm(css, 0) : 0; | ||
480 | break; | ||
481 | case '1': | ||
482 | ret = css->cm_enabled ? 0 : chsc_secm(css, 1); | ||
483 | break; | ||
484 | default: | ||
485 | ret = -EINVAL; | ||
486 | } | ||
487 | return ret < 0 ? ret : count; | ||
488 | } | ||
489 | |||
490 | static DEVICE_ATTR(cm_enable, 0644, css_cm_enable_show, css_cm_enable_store); | ||
491 | |||
458 | static inline void __init | 492 | static inline void __init |
459 | setup_css(int nr) | 493 | setup_css(int nr) |
460 | { | 494 | { |
461 | u32 tod_high; | 495 | u32 tod_high; |
462 | 496 | ||
463 | memset(css[nr], 0, sizeof(struct channel_subsystem)); | 497 | memset(css[nr], 0, sizeof(struct channel_subsystem)); |
498 | mutex_init(&css[nr]->mutex); | ||
464 | css[nr]->valid = 1; | 499 | css[nr]->valid = 1; |
465 | css[nr]->cssid = nr; | 500 | css[nr]->cssid = nr; |
466 | sprintf(css[nr]->device.bus_id, "css%x", nr); | 501 | sprintf(css[nr]->device.bus_id, "css%x", nr); |
@@ -507,6 +542,9 @@ init_channel_subsystem (void) | |||
507 | ret = device_register(&css[i]->device); | 542 | ret = device_register(&css[i]->device); |
508 | if (ret) | 543 | if (ret) |
509 | goto out_free; | 544 | goto out_free; |
545 | if (css_characteristics_avail && css_chsc_characteristics.secm) | ||
546 | device_create_file(&css[i]->device, | ||
547 | &dev_attr_cm_enable); | ||
510 | } | 548 | } |
511 | css_init_done = 1; | 549 | css_init_done = 1; |
512 | 550 | ||
@@ -519,6 +557,9 @@ out_free: | |||
519 | out_unregister: | 557 | out_unregister: |
520 | while (i > 0) { | 558 | while (i > 0) { |
521 | i--; | 559 | i--; |
560 | if (css_characteristics_avail && css_chsc_characteristics.secm) | ||
561 | device_remove_file(&css[i]->device, | ||
562 | &dev_attr_cm_enable); | ||
522 | device_unregister(&css[i]->device); | 563 | device_unregister(&css[i]->device); |
523 | } | 564 | } |
524 | out_bus: | 565 | out_bus: |
diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h index b6375861cb37..74a257b23383 100644 --- a/drivers/s390/cio/css.h +++ b/drivers/s390/cio/css.h | |||
@@ -1,6 +1,7 @@ | |||
1 | #ifndef _CSS_H | 1 | #ifndef _CSS_H |
2 | #define _CSS_H | 2 | #define _CSS_H |
3 | 3 | ||
4 | #include <linux/mutex.h> | ||
4 | #include <linux/wait.h> | 5 | #include <linux/wait.h> |
5 | #include <linux/workqueue.h> | 6 | #include <linux/workqueue.h> |
6 | 7 | ||
@@ -150,6 +151,11 @@ struct channel_subsystem { | |||
150 | struct channel_path *chps[__MAX_CHPID + 1]; | 151 | struct channel_path *chps[__MAX_CHPID + 1]; |
151 | struct device device; | 152 | struct device device; |
152 | struct pgid global_pgid; | 153 | struct pgid global_pgid; |
154 | struct mutex mutex; | ||
155 | /* channel measurement related */ | ||
156 | int cm_enabled; | ||
157 | void *cub_addr1; | ||
158 | void *cub_addr2; | ||
153 | }; | 159 | }; |
154 | #define to_css(dev) container_of(dev, struct channel_subsystem, device) | 160 | #define to_css(dev) container_of(dev, struct channel_subsystem, device) |
155 | 161 | ||