aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/s390/cio/chsc.c
diff options
context:
space:
mode:
authorCornelia Huck <cornelia.huck@de.ibm.com>2006-03-24 06:15:14 -0500
committerLinus Torvalds <torvalds@g5.osdl.org>2006-03-24 10:33:16 -0500
commit495a5b45ac33b8fe2c49780fdbcc8014cb6d6ddc (patch)
treeb456ebc9e8f53edd726c903401b5e40bbb9562d5 /drivers/s390/cio/chsc.c
parentdc06010c62da773321258df1d8a1708a3158e29d (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/cio/chsc.c')
-rw-r--r--drivers/s390/cio/chsc.c454
1 files changed, 432 insertions, 22 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 */
876static ssize_t
877chp_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
897static 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
907static void
908chp_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
929static ssize_t
930chp_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
948static 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
958static void
959chsc_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
965static int
966chsc_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
981static void
982chsc_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
993static int
994chsc_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;
1007cleanup:
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
1017static 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
1087int
1088chsc_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 */
880static ssize_t 1134static ssize_t
@@ -925,9 +1179,39 @@ chp_type_show(struct device *dev, struct device_attribute *attr, char *buf)
925 1179
926static DEVICE_ATTR(type, 0444, chp_type_show, NULL); 1180static DEVICE_ATTR(type, 0444, chp_type_show, NULL);
927 1181
1182static ssize_t
1183chp_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
1194static DEVICE_ATTR(cmg, 0444, chp_cmg_show, NULL);
1195
1196static ssize_t
1197chp_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
1208static DEVICE_ATTR(shared, 0444, chp_shared_show, NULL);
1209
928static struct attribute * chp_attrs[] = { 1210static 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
1291static void
1292chsc_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
1320static int
1321chsc_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 }
1391out:
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;
1052out_free: 1466out_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) {