mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
Compare commits
437 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bea3180836 | |||
| 1ffd767977 | |||
| a4e396fdd7 | |||
| 972d4fe0a5 | |||
| 148460c235 | |||
| 8c2f6790fe | |||
| 044272ea25 | |||
| 7d67dc8f1c | |||
| 71461d80bf | |||
| 9eac2788d7 | |||
| 6ff8202fa9 | |||
| acf30de415 | |||
| a766411306 | |||
| cbcab61c65 | |||
| c3d179f06e | |||
| f42250ca9b | |||
| 7217903ce4 | |||
| 708abbf380 | |||
| 28dbec093b | |||
| daf02f75b1 | |||
| 71b0460d11 | |||
| ff62f4d425 | |||
| 53c523ab19 | |||
| 2285b9527b | |||
| b4648ec4bd | |||
| c6207db8fc | |||
| 1bd73c68f9 | |||
| 47016441f0 | |||
| eeed384a51 | |||
| cd5d6a5004 | |||
| e4733002da | |||
| 15158ee0ca | |||
| dc73c77d73 | |||
| 56529261db | |||
| fcb1f7efd1 | |||
| 72b5e8b313 | |||
| 225964875c | |||
| e580190074 | |||
| 8ef593904d | |||
| 51b9504720 | |||
| e9d0e267c5 | |||
| b028d4c254 | |||
| a6f844d642 | |||
| 7f059bef13 | |||
| 4543d23dcf | |||
| 12f7dbbb3d | |||
| f7c048d0eb | |||
| 80e54ac888 | |||
| 26148740df | |||
| 3ab06d77fb | |||
| 386392690d | |||
| 72df42ea4e | |||
| 6c0f429805 | |||
| 0466760ec1 | |||
| 8ddc31ea77 | |||
| 60f862b21c | |||
| a66f6bb075 | |||
| 2b015183aa | |||
| b4142a9966 | |||
| c1d08e1380 | |||
| 85c9a49e63 | |||
| 4939531fe5 | |||
| 84abb27077 | |||
| 6826316350 | |||
| b95a3c24b8 | |||
| 1f8496aa92 | |||
| 9aa59bfc4b | |||
| fa111404cb | |||
| 5094354c39 | |||
| 844058bf4d | |||
| dc49b4fcf0 | |||
| 647e8b5c37 | |||
| 1ae4fd18fe | |||
| 561883b1d8 | |||
| 953af2cc67 | |||
| 8f37000137 | |||
| ba7bf2f6d3 | |||
| 4a8e52a019 | |||
| 446ace09ad | |||
| 012b842652 | |||
| fed12a71cf | |||
| 5cd9ad13e5 | |||
| f2d5b649e1 | |||
| a957e3efd6 | |||
| 463189bf60 | |||
| aa2e8e1caa | |||
| 7bc15537c8 | |||
| 901f082f1a | |||
| cd2647ab78 | |||
| 48bcfc3366 | |||
| 57f3527799 | |||
| f51dc05f7f | |||
| 3f9ead5731 | |||
| b4e4cd6cd5 | |||
| e97c85bb80 | |||
| c6443cf977 | |||
| 6a439e6771 | |||
| 2134aff491 | |||
| b41ac66f36 | |||
| 9aa368af54 | |||
| 2e6cf81c2a | |||
| ae27066c15 | |||
| 47fcc71f96 | |||
| 5ac171d224 | |||
| 67dbf094b2 | |||
| 3669fd4d8a | |||
| b01ef782f0 | |||
| a177ae2439 | |||
| 13fef6dbe0 | |||
| 508253c666 | |||
| 773d01e674 | |||
| 2da206f5a6 | |||
| bf71e3bb1f | |||
| 6f7982dae5 | |||
| f64b4087dd | |||
| a739584d6f | |||
| cce5f510c3 | |||
| 2515832469 | |||
| 6c5c5f7e55 | |||
| 736c69379d | |||
| 130880159a | |||
| d7b1cbad8b | |||
| a58460b5c7 | |||
| acdce2a7a9 | |||
| 0981df62c0 | |||
| 4e28f6749b | |||
| 5f08033bb8 | |||
| 8231ec5f38 | |||
| 9e62005818 | |||
| a0dba6bfdb | |||
| ebc3384b9f | |||
| e9bf141264 | |||
| d8b8114e6f | |||
| c4e4b926b4 | |||
| 6324f62da7 | |||
| 3ade063ea2 | |||
| 92a3b30323 | |||
| 673ff375d9 | |||
| 4e2ebe0370 | |||
| 78b053326c | |||
| ab06a5d88a | |||
| a0a71efde0 | |||
| b4b2202ff5 | |||
| a26f61c4fe | |||
| a28b396d9e | |||
| 0012eaf68e | |||
| c22d282e67 | |||
| 071522f7d2 | |||
| ae7217806b | |||
| 9f6150db34 | |||
| b39bdfbf6e | |||
| 71fe58111c | |||
| 5e46632767 | |||
| 1bd64f5b0f | |||
| d311506dc4 | |||
| 08e4520a4f | |||
| 46fd42af0c | |||
| a8b0d7ace8 | |||
| a29b3c8997 | |||
| 27dce3317a | |||
| 4382901daa | |||
| bcbb6fcae6 | |||
| b16a6d4fe1 | |||
| 0d15a07c0a | |||
| 5394cd363c | |||
| 0906293cd8 | |||
| a9e13a2d69 | |||
| ee2c6d4160 | |||
| 455ad953a9 | |||
| 02a0d12988 | |||
| be47b51890 | |||
| 2677a4182d | |||
| 812df795c3 | |||
| a0d6edf03c | |||
| 81920f7cfd | |||
| 40dd63e2ac | |||
| a8579a0727 | |||
| 2ab27bdaf2 | |||
| 03fef264bd | |||
| 927a48e607 | |||
| fbcb3f384a | |||
| c7fe21b019 | |||
| 50a4740443 | |||
| 164d4817ec | |||
| abec41f514 | |||
| fcb503ea3c | |||
| 2eea614bbf | |||
| 3a13cb43f2 | |||
| e8fca95592 | |||
| b5604850ab | |||
| 18efac6306 | |||
| 18f8db2225 | |||
| f1a13749df | |||
| b5da337d1e | |||
| a67888f36a | |||
| e1f174ae22 | |||
| 7503d88b3b | |||
| 43039943cd | |||
| b2e02d202a | |||
| 1faa5d2957 | |||
| d62e075450 | |||
| 983233730c | |||
| ad024c06e1 | |||
| e2843232d5 | |||
| b79d17d233 | |||
| 11b401b599 | |||
| 69c5ee2e52 | |||
| 3ee625d74f | |||
| 832268ecb0 | |||
| 5fb6434548 | |||
| 5894e3f700 | |||
| e384f06014 | |||
| 1627c6a39f | |||
| bb8f17a7da | |||
| 8b3d3dd4bd | |||
| a5c04e5746 | |||
| c5ae1a41da | |||
| 970c697983 | |||
| cff4b0384f | |||
| 724f800dc5 | |||
| 4cdfe91ca9 | |||
| a44db0f367 | |||
| 03ddcd6267 | |||
| 5c468a220e | |||
| 4ba65eaa74 | |||
| 58cae4fecd | |||
| 9b84de4b0a | |||
| e679a88d88 | |||
| 6acf6ed76d | |||
| e7abf256f6 | |||
| 0d407904dd | |||
| 2238eb820f | |||
| 81013f0c70 | |||
| 697c5dc405 | |||
| e9deec4c62 | |||
| d60b7770fd | |||
| 70925f9aff | |||
| ef88fac874 | |||
| 4bd16ba8ea | |||
| f6a7da141c | |||
| 1bbd34e500 | |||
| c2f8988444 | |||
| d2990db15f | |||
| fd3d79335a | |||
| 07b3837e68 | |||
| 13a1227a02 | |||
| 52118a8d02 | |||
| ee89b850fe | |||
| 5160f41079 | |||
| 5b0115fdb4 | |||
| ae305b5158 | |||
| 04407d24fc | |||
| bcb8ea09d1 | |||
| 88619b6dba | |||
| e127b89624 | |||
| 3591b94c42 | |||
| 2a29a28f78 | |||
| cb831aea44 | |||
| 04be704083 | |||
| 1bcfcd3253 | |||
| 2dbaf9f991 | |||
| 2ea20094d0 | |||
| a80c14d72c | |||
| 10518da618 | |||
| e9d1e168b2 | |||
| 7567506cca | |||
| 524017ac06 | |||
| 15774dbe32 | |||
| f247850ff0 | |||
| 800ef7cb13 | |||
| 48cf3b3809 | |||
| 99c8c27302 | |||
| a5c3af1361 | |||
| 353c23a157 | |||
| d371ba0db8 | |||
| 8308143008 | |||
| d8871197ea | |||
| df338a3bac | |||
| 1005e7be7e | |||
| afb14cba7a | |||
| 1e450e6f7b | |||
| 696a007b62 | |||
| ea5f3f4a1c | |||
| 6afd5e97d1 | |||
| b1da8bbbe9 | |||
| 6ece595532 | |||
| 40d4aa38e9 | |||
| c58cfd1ec1 | |||
| 02086f26d2 | |||
| 7a2666d727 | |||
| 2df532c1fa | |||
| 969a439aaa | |||
| f8331a4b8e | |||
| 895dc80580 | |||
| d93645d690 | |||
| c3bb6307b8 | |||
| 6208c0626a | |||
| 3949c62f6f | |||
| 8c6c2ef30e | |||
| 12c84469fd | |||
| 0a8e726d05 | |||
| 533df585fd | |||
| c4eb2d5fba | |||
| 35029ad010 | |||
| 296079f70a | |||
| 4f1c73d592 | |||
| 1fb859729e | |||
| bbeb0f8445 | |||
| 2ae44cb794 | |||
| a0b8033088 | |||
| 9cc74839b0 | |||
| b7b7002855 | |||
| 55e428a4d7 | |||
| cb09223a56 | |||
| 28db3523d0 | |||
| f15e32cdb1 | |||
| 094057e265 | |||
| 025a2198a4 | |||
| 2b938b78aa | |||
| de36fc5259 | |||
| e3e5b6422a | |||
| 38343be0fa | |||
| 957e612720 | |||
| 1d2316caef | |||
| 64539853ca | |||
| 686c331b69 | |||
| 01551f98d5 | |||
| 0e2c5907e0 | |||
| 68ff79e4c3 | |||
| 5b96d86538 | |||
| 4043137cd7 | |||
| 735d8f5493 | |||
| 28efa7d120 | |||
| ebde75eeda | |||
| a645ec0fc0 | |||
| 761a185938 | |||
| 890b15d158 | |||
| 07677668a3 | |||
| fde914fd1c | |||
| 35c97a07aa | |||
| 45e3f016f4 | |||
| 38b520ab4c | |||
| 3b6920271e | |||
| a3146409da | |||
| 76aa8e4a52 | |||
| f5e6f4a8cb | |||
| 80cb1553a3 | |||
| 706f59b9d9 | |||
| 02cc902b90 | |||
| ff2fb42868 | |||
| 451bf7fbc6 | |||
| d2a4a8b0f3 | |||
| fe5753d34d | |||
| fe187eb3d3 | |||
| 4a8afd4ed6 | |||
| 74e6ebe1df | |||
| ec83da3a39 | |||
| c11b0cdcaa | |||
| 4f3cba406e | |||
| c55ac2c016 | |||
| 4eab80ef44 | |||
| 41ddd35bc1 | |||
| bdc44cafb6 | |||
| 46583b4db6 | |||
| a2359dbe5f | |||
| 52b6e57743 | |||
| b1997c7aac | |||
| c56aea0fa9 | |||
| e24629b977 | |||
| 268dcebe23 | |||
| 030c0054b1 | |||
| 154914ee7a | |||
| 56fecf1832 | |||
| 081499f355 | |||
| 26c34a49b1 | |||
| e70cde2c5d | |||
| fff83fc116 | |||
| 5a7d2a58c8 | |||
| 89d25559e0 | |||
| 49901079d4 | |||
| 317384b5c9 | |||
| 8db32a8cb9 | |||
| 4cf73238fc | |||
| 27120f2824 | |||
| 8f32a60c22 | |||
| 0fa2f9acf7 | |||
| 2ca7db75d3 | |||
| 02aaff72fa | |||
| 8723dbce04 | |||
| c5d566a4da | |||
| d90344a2d5 | |||
| 71a667fbf0 | |||
| d2611b472b | |||
| 34482d67c0 | |||
| b77523ab4d | |||
| 802e5f6607 | |||
| 5e5d49264f | |||
| 3e818c53b2 | |||
| 9100d6f40e | |||
| 8bcb93673e | |||
| 21fe08ad23 | |||
| f8d3e5069a | |||
| 9fb2568134 | |||
| a38d82e8f9 | |||
| 8c9cc6e12a | |||
| ce8383e5ac | |||
| 9696b4c9b5 | |||
| 466ceb02a1 | |||
| f6ab7f6cda | |||
| 568caa0501 | |||
| 2ece47ee1a | |||
| 0fafbcf3bb | |||
| 7244785279 | |||
| cdf716a7a9 | |||
| 529ad4000e | |||
| 0772c0f01b | |||
| 27a562da0a | |||
| cd532cb6cd | |||
| 80b1306102 | |||
| 4b0403a963 | |||
| 1d9e7b614c | |||
| 44de579311 | |||
| 4fb18a1e7b | |||
| 59206b88d8 | |||
| 7a64b88396 | |||
| b7807d0195 | |||
| 2f436d6ee0 | |||
| 302cd4ed7e | |||
| 69526998f0 | |||
| a65bc51cf4 | |||
| eb1fd47a85 | |||
| c4918fe713 | |||
| d23558f1d1 | |||
| 3d79a542d4 | |||
| b6bdd7744a | |||
| 3f3f063c98 | |||
| 78d63f2333 |
@@ -0,0 +1,21 @@
|
|||||||
|
# Check https://editorconfig.org for details
|
||||||
|
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
charset = utf-8
|
||||||
|
indent_style = tab
|
||||||
|
tab_width = 8
|
||||||
|
|
||||||
|
[*.sh]
|
||||||
|
shell_variant = bash # used by `shfmt`
|
||||||
|
|
||||||
|
[*.y{a,}ml]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
[*.py]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
+30
-10
@@ -5,14 +5,25 @@
|
|||||||
*.a
|
*.a
|
||||||
*.o.d
|
*.o.d
|
||||||
|
|
||||||
|
# ctags files
|
||||||
|
tags
|
||||||
|
TAGS
|
||||||
|
|
||||||
|
# Ignore coverage data
|
||||||
|
*.gcda
|
||||||
|
*.gcno
|
||||||
|
|
||||||
#
|
#
|
||||||
# Ignore generated executables and other generated files
|
# Ignore generated executables and other generated files
|
||||||
#
|
#
|
||||||
|
ap_tools/ap-check
|
||||||
cmsfs-fuse/cmsfs-fuse
|
cmsfs-fuse/cmsfs-fuse
|
||||||
cpacfstats/cpacfstats
|
cpacfstats/cpacfstats
|
||||||
cpacfstats/cpacfstatsd
|
cpacfstats/cpacfstatsd
|
||||||
cpumf/chcpumf
|
cpumf/chcpumf
|
||||||
cpumf/lscpumf
|
cpumf/lscpumf
|
||||||
|
cpumf/lshwc
|
||||||
|
cpumf/pai
|
||||||
cpuplugd/cpuplugd
|
cpuplugd/cpuplugd
|
||||||
dasdfmt/dasdfmt
|
dasdfmt/dasdfmt
|
||||||
dasdinfo/dasdinfo
|
dasdinfo/dasdinfo
|
||||||
@@ -20,6 +31,8 @@ dasdview/dasdview
|
|||||||
dump2tar/src/dump2tar
|
dump2tar/src/dump2tar
|
||||||
fdasd/fdasd
|
fdasd/fdasd
|
||||||
hmcdrvfs/hmcdrvfs
|
hmcdrvfs/hmcdrvfs
|
||||||
|
hsavmcore/check-dep-fuse
|
||||||
|
hsavmcore/hsavmcore
|
||||||
hyptop/hyptop
|
hyptop/hyptop
|
||||||
ip_watcher/xcec-bridge
|
ip_watcher/xcec-bridge
|
||||||
ipl_tools/chreipl
|
ipl_tools/chreipl
|
||||||
@@ -34,27 +47,29 @@ iucvterm/src/iucvconn
|
|||||||
iucvterm/src/iucvtty
|
iucvterm/src/iucvtty
|
||||||
iucvterm/src/ttyrun
|
iucvterm/src/ttyrun
|
||||||
iucvterm/test/test_afiucv
|
iucvterm/test/test_afiucv
|
||||||
|
libap/check-dep-lock
|
||||||
|
libap/check-dep-json
|
||||||
libekmfweb/check-dep-libekmfweb
|
libekmfweb/check-dep-libekmfweb
|
||||||
libekmfweb/detect-openssl-version.dep
|
libekmfweb/detect-openssl-version.dep
|
||||||
libekmfweb/libekmfweb.so
|
libekmfweb/libekmfweb.so
|
||||||
libekmfweb/libekmfweb.so.1
|
libekmfweb/libekmfweb.so.1
|
||||||
libekmfweb/libekmfweb.so.1.0
|
libekmfweb/libekmfweb.so.1.0
|
||||||
libutil/util_base_example
|
libkmipclient/check-dep-libkmipclient
|
||||||
libutil/util_file_example
|
libkmipclient/detect-openssl-version.dep
|
||||||
libutil/util_libc_example
|
libkmipclient/libkmipclient.so
|
||||||
libutil/util_opt_command_example
|
libkmipclient/libkmipclient.so.1
|
||||||
libutil/util_opt_example
|
libkmipclient/libkmipclient.so.1.0
|
||||||
libutil/util_panic_example
|
libseckey/check-dep-libseckey
|
||||||
libutil/util_path_example
|
libseckey/detect-openssl-version.dep
|
||||||
libutil/util_prg_example
|
libutil/*_example
|
||||||
libutil/util_rec_example
|
|
||||||
libutil/util_scandir_example
|
|
||||||
libvmcp/vmcp_example
|
libvmcp/vmcp_example
|
||||||
libzds/libzds.a
|
libzds/libzds.a
|
||||||
lsstp/lsstp
|
lsstp/lsstp
|
||||||
mon_tools/mon_fsstatd
|
mon_tools/mon_fsstatd
|
||||||
mon_tools/mon_procd
|
mon_tools/mon_procd
|
||||||
osasnmpd/osasnmpd
|
osasnmpd/osasnmpd
|
||||||
|
pvattest/src/pvattest
|
||||||
|
pvattest/tools/exchange_info
|
||||||
qetharp/qetharp
|
qetharp/qetharp
|
||||||
qethqoat/qethqoat
|
qethqoat/qethqoat
|
||||||
systemd/cpacfstatsd.service
|
systemd/cpacfstatsd.service
|
||||||
@@ -94,13 +109,18 @@ zipl/boot/*.bin
|
|||||||
zipl/boot/*.exec
|
zipl/boot/*.exec
|
||||||
zipl/boot/data.h
|
zipl/boot/data.h
|
||||||
zipl/src/chreipl_helper.device-mapper
|
zipl/src/chreipl_helper.device-mapper
|
||||||
|
zdev/src/zdev_id
|
||||||
zipl/src/zipl
|
zipl/src/zipl
|
||||||
|
zipl/src/zipl-editenv
|
||||||
zipl/src/zipl_helper.device-mapper
|
zipl/src/zipl_helper.device-mapper
|
||||||
zkey/check-dep-zkey
|
zkey/check-dep-zkey
|
||||||
zkey/check-dep-zkey-cryptsetup
|
zkey/check-dep-zkey-cryptsetup
|
||||||
zkey/detect-libcryptsetup.dep
|
zkey/detect-libcryptsetup.dep
|
||||||
zkey/ekmfweb/libekmfweb.dep
|
zkey/ekmfweb/libekmfweb.dep
|
||||||
zkey/ekmfweb/zkey-ekmfweb.so
|
zkey/ekmfweb/zkey-ekmfweb.so
|
||||||
|
zkey/kmip/libkmipclient.dep
|
||||||
|
zkey/kmip/zkey-kmip.so
|
||||||
zkey/zkey
|
zkey/zkey
|
||||||
zkey/zkey-cryptsetup
|
zkey/zkey-cryptsetup
|
||||||
zpcictl/zpcictl
|
zpcictl/zpcictl
|
||||||
|
**/.detect-openssl.dep.c
|
||||||
|
|||||||
+14
-2
@@ -2,12 +2,14 @@ List of all individuals having contributed content to s390-tools
|
|||||||
----------------------------------------------------------------
|
----------------------------------------------------------------
|
||||||
|
|
||||||
- Alexander Egorenkov
|
- Alexander Egorenkov
|
||||||
|
- Alexandra Winter
|
||||||
- Alexey Ishchuk
|
- Alexey Ishchuk
|
||||||
- Andreas Herrmann
|
- Andreas Herrmann
|
||||||
- Andre Wild
|
- André Wild
|
||||||
- Antoinette Kaschner
|
- Antoinette Kaschner
|
||||||
- Arnd Bergmann
|
- Arnd Bergmann
|
||||||
- Axel Wirbser
|
- Axel Wirbser
|
||||||
|
- Balint Reczey
|
||||||
- Benjamin Block
|
- Benjamin Block
|
||||||
- Brian C. Lane
|
- Brian C. Lane
|
||||||
- Carsten Otte
|
- Carsten Otte
|
||||||
@@ -18,6 +20,7 @@ List of all individuals having contributed content to s390-tools
|
|||||||
- Clemens von Mann
|
- Clemens von Mann
|
||||||
- Colin Walters
|
- Colin Walters
|
||||||
- Dan Horak
|
- Dan Horak
|
||||||
|
- Dan Horák
|
||||||
- Despina Papadopoulou
|
- Despina Papadopoulou
|
||||||
- Dimitri John Ledkov
|
- Dimitri John Ledkov
|
||||||
- Eberhard Pasch
|
- Eberhard Pasch
|
||||||
@@ -27,15 +30,18 @@ List of all individuals having contributed content to s390-tools
|
|||||||
- Erwin Vicari
|
- Erwin Vicari
|
||||||
- Eugene Crosser
|
- Eugene Crosser
|
||||||
- Eugene Dvurechenski
|
- Eugene Dvurechenski
|
||||||
|
- Fabrice Fontaine
|
||||||
- Farhan Ali
|
- Farhan Ali
|
||||||
- Fedor Loshakov
|
- Fedor Loshakov
|
||||||
- Felix Beck
|
- Felix Beck
|
||||||
- Frank Blaschka
|
- Frank Blaschka
|
||||||
|
- Frank Heimes
|
||||||
- Frank Munzert
|
- Frank Munzert
|
||||||
- Frank Pavlic
|
- Frank Pavlic
|
||||||
- Fritz Elfert
|
- Fritz Elfert
|
||||||
- Gerald Schaefer
|
- Gerald Schaefer
|
||||||
- Gerhard Tonn
|
- Gerhard Tonn
|
||||||
|
- Graham Inggs
|
||||||
- Guevenc Guelce
|
- Guevenc Guelce
|
||||||
- Hannes Reinecke
|
- Hannes Reinecke
|
||||||
- Hans-Joachim Picht
|
- Hans-Joachim Picht
|
||||||
@@ -50,13 +56,14 @@ List of all individuals having contributed content to s390-tools
|
|||||||
- Ingo Franzki
|
- Ingo Franzki
|
||||||
- Ingo Tuchscherer
|
- Ingo Tuchscherer
|
||||||
- Jan Glauber
|
- Jan Glauber
|
||||||
- Jan Hoeppner
|
- Jan Höppner
|
||||||
- Jan Willeke
|
- Jan Willeke
|
||||||
- Jason J. Herne
|
- Jason J. Herne
|
||||||
- Javier Martinez Canillas
|
- Javier Martinez Canillas
|
||||||
- Jean-Baptiste Joret
|
- Jean-Baptiste Joret
|
||||||
- Jens Remus
|
- Jens Remus
|
||||||
- Jochen Roehrig
|
- Jochen Roehrig
|
||||||
|
- Joern Siglen
|
||||||
- Juergen Christ
|
- Juergen Christ
|
||||||
- Julian Wiedmann
|
- Julian Wiedmann
|
||||||
- Karsten Graul
|
- Karsten Graul
|
||||||
@@ -64,14 +71,17 @@ List of all individuals having contributed content to s390-tools
|
|||||||
- Klaus-Dieter Wacker
|
- Klaus-Dieter Wacker
|
||||||
- Lakhvich Dmitriy
|
- Lakhvich Dmitriy
|
||||||
- Marc Hartmayer
|
- Marc Hartmayer
|
||||||
|
- Mario Held
|
||||||
- Mark Dettinger
|
- Mark Dettinger
|
||||||
- Mark Post
|
- Mark Post
|
||||||
- Martin Kammerer
|
- Martin Kammerer
|
||||||
- Martin Peschke
|
- Martin Peschke
|
||||||
- Martin Petermann
|
- Martin Petermann
|
||||||
- Martin Schwidefsky
|
- Martin Schwidefsky
|
||||||
|
- Matthew Rosato
|
||||||
- Maxim Shchetynin
|
- Maxim Shchetynin
|
||||||
- Melissa Howland
|
- Melissa Howland
|
||||||
|
- Mete Durlu
|
||||||
- Michael Ernst
|
- Michael Ernst
|
||||||
- Michael Holzheu
|
- Michael Holzheu
|
||||||
- Michael Mueller
|
- Michael Mueller
|
||||||
@@ -99,6 +109,7 @@ List of all individuals having contributed content to s390-tools
|
|||||||
- Stefan Raspl
|
- Stefan Raspl
|
||||||
- Stefan Reimbold
|
- Stefan Reimbold
|
||||||
- Stefan Weinhuber
|
- Stefan Weinhuber
|
||||||
|
- Steffen Eiden
|
||||||
- Steffen Maier
|
- Steffen Maier
|
||||||
- Steffen Thoss
|
- Steffen Thoss
|
||||||
- Susanne Wintenberger
|
- Susanne Wintenberger
|
||||||
@@ -107,6 +118,7 @@ List of all individuals having contributed content to s390-tools
|
|||||||
- Swen Schillig
|
- Swen Schillig
|
||||||
- Taraka R. Bodireddy
|
- Taraka R. Bodireddy
|
||||||
- Thomas Heidrich
|
- Thomas Heidrich
|
||||||
|
- Thomas Huth
|
||||||
- Thomas Richter
|
- Thomas Richter
|
||||||
- Thomas Spatzier
|
- Thomas Spatzier
|
||||||
- Thomas Weber
|
- Thomas Weber
|
||||||
|
|||||||
+189
@@ -1,5 +1,194 @@
|
|||||||
Release history for s390-tools (MIT version)
|
Release history for s390-tools (MIT version)
|
||||||
--------------------------------------------
|
--------------------------------------------
|
||||||
|
|
||||||
|
* __v2.23.0 (2022-08-18)__
|
||||||
|
|
||||||
|
For Linux kernel version: 5.19
|
||||||
|
|
||||||
|
Changes of existing tools:
|
||||||
|
- Makefile: use common Make definition for DRACUTDIR
|
||||||
|
- Makefile: use common Make definition for UDEVDIR and UDEVRULESDIR
|
||||||
|
- cpacfstats: Add PAI and hotplug support
|
||||||
|
- cpumf/pai: Omit file write progress information
|
||||||
|
- dbginfo.sh: Get more details on lspci command
|
||||||
|
- dumpconf: Prevent running the service in containers
|
||||||
|
- libcpumf: Detect PMU named pai_ext
|
||||||
|
- pvattest: Improve error reporting and logging
|
||||||
|
- zdev: Add some --type ap examples to manpages
|
||||||
|
- zkey: Use default benchmarked Argon2i with LUKS2
|
||||||
|
|
||||||
|
Bug Fixes:
|
||||||
|
- dbginfo.sh: Fix accidental ftrace buffer shrinkage/free
|
||||||
|
- genprotimg: Fix BIO_reset() returncode handling
|
||||||
|
- libpv: Fix dependency checking
|
||||||
|
- pvattest: Fix dependency checking
|
||||||
|
- zipl: Fix segmentation fault when no parmline is provided
|
||||||
|
|
||||||
|
|
||||||
|
* __v2.22.0 (2022-06-20)__
|
||||||
|
|
||||||
|
For Linux kernel version: 5.18
|
||||||
|
|
||||||
|
Add new tools / libraries:
|
||||||
|
- ap_tools: Introduce ap_tools and the ap-check tool
|
||||||
|
- cpumf/pai: Add Processor Activity Instrumentation tool
|
||||||
|
- libpv: New library for PV tools
|
||||||
|
- pvattest: Add new tool to create, perform, and verify attestation measurements
|
||||||
|
- zipl/zdump: Add Next Gen Dump (NGDump) support
|
||||||
|
|
||||||
|
Changes of existing tools:
|
||||||
|
- Move man pages to System commands section (lscpumf, lshwc, pai, dbginfo.sh, zfcpdbf, zipl-switch-to-blscfg)
|
||||||
|
- README.md: Add 70-chreipl-fcp-mpath.rules to the list of udev rule descriptions
|
||||||
|
- Remove SysV related daemon scripts (cpacfstatsd, cpuplugd, mon_statd)
|
||||||
|
- genprotimg: Move man page to section 1 for user commands
|
||||||
|
- hyptop: increase initial update interval
|
||||||
|
- libseckey: Adapt keymgmt_match() implementation to OpenSSL
|
||||||
|
- libutil: Add util_exit_code
|
||||||
|
- libutil: Introduce util_udev
|
||||||
|
- zdev: Introduce the ap device type
|
||||||
|
- zipl-editenv: Add zIPL multienvironment support
|
||||||
|
- zipl: Implement sorting BLS entries by versions
|
||||||
|
- zkey: Add initramfs hook
|
||||||
|
|
||||||
|
Bug Fixes:
|
||||||
|
- cmsfs-fuse: Fix enabling of hard_remove option
|
||||||
|
- s390-tools: Fix typos that were detected by lintian as 'typo-in-manual-page'
|
||||||
|
- zkey-kmip: Fix possible use after free
|
||||||
|
- zkey: Fix EP11 host library version checking
|
||||||
|
- zkey_kmip: Setup ext-lib once the APQNs have been configured
|
||||||
|
|
||||||
|
* __v2.21.0 (2022-04-20)__
|
||||||
|
|
||||||
|
For Linux kernel version: 5.17
|
||||||
|
|
||||||
|
Add new tools / libraries:
|
||||||
|
- libcpumf: Create library libcpumf for CPU Measurement functions
|
||||||
|
|
||||||
|
Changes of existing tools:
|
||||||
|
- chreipl-fcp-mpath: bundle a pre-cooked version of the manpage for build
|
||||||
|
environments without access to `pandoc`
|
||||||
|
- dbginfo.sh: Add multipath info to map paths to FC addressing and prio group
|
||||||
|
- dbginfo.sh: Collect config files of systemd-modules-load.service
|
||||||
|
- dbginfo.sh: Sort list of environment variables for readability
|
||||||
|
- dbginfo.sh: Replace "which" by builtin command "type"
|
||||||
|
- dbginfo.sh: Rework script formatting (indents, order)
|
||||||
|
- dbginfo.sh: Update sysfs collection (excludes, messages)
|
||||||
|
- genprotimg: Add Protected Virtualization (PV) dump support
|
||||||
|
- genprotimg: Remove DigiCert root CA pinning
|
||||||
|
- lszcrypt: Add CEX8S support
|
||||||
|
- zcryptctl: Add control domain handling
|
||||||
|
- zcryptstats: Add CEX8 support
|
||||||
|
- zipl: Allow optional entries that are left out when files are missing
|
||||||
|
- zipl: make IPL sections defined with BLS to inherit a target field
|
||||||
|
- zpcictl: Add option to trigger firmware reset
|
||||||
|
|
||||||
|
Bug Fixes:
|
||||||
|
- cpictl: Handle excessive kernel version numbers
|
||||||
|
- dbginfo.sh: Collect all places where modprobe.d config files could exist
|
||||||
|
- fdasd: Fix endless menu loop on EOF
|
||||||
|
- zdump/dfi: Fix segfault due to double free
|
||||||
|
- zdump: Fix /dev/mem reading
|
||||||
|
- zpcictl: Fix race of SCLP reset and Linux recovery
|
||||||
|
|
||||||
|
* __v2.20.0 (2022-02-04)__
|
||||||
|
|
||||||
|
For Linux kernel version: 5.16
|
||||||
|
|
||||||
|
Add new tools / libraries:
|
||||||
|
- Add EditorConfig configuration
|
||||||
|
|
||||||
|
Changes of existing tools:
|
||||||
|
- s390-tools switches to Fuse 3 as Fuse 2 is deprecated.
|
||||||
|
Affected tools: cmsfs, hmcdrvfs, hsavmcore, zdsfs, zdump
|
||||||
|
- chreipl-fcp-mpath: don't compress the manpage before installing it
|
||||||
|
- cpictl: Report extended version information
|
||||||
|
- genprotimg: Add extended kernel command line support
|
||||||
|
- zdev: modify the lsblk output parser in lszdev
|
||||||
|
- zipl: Add support for longer kernel command lines (now supports up to 64k length)
|
||||||
|
|
||||||
|
Bug Fixes:
|
||||||
|
- cpictl: Suppress messages for unwritable sysfs files
|
||||||
|
- dbginfo.sh: Fix missing syslog for step create_package
|
||||||
|
- lshwc: Fix CPU list parameter setup for device driver
|
||||||
|
- zdev: Check for errors when removing a devtype setting
|
||||||
|
- zdev: Fix path resolution for multi-mount point file systems
|
||||||
|
|
||||||
|
* __v2.19.0 (2021-11-10)__
|
||||||
|
|
||||||
|
For Linux kernel version: 5.15
|
||||||
|
|
||||||
|
Add new tools / libraries:
|
||||||
|
- chreipl-fcp-mpath: New toolset that uses multipath information to change
|
||||||
|
the configured FCP re-IPL path on detecting issues with the current path
|
||||||
|
|
||||||
|
Changes of existing tools:
|
||||||
|
- dbginfo.sh: Add retry timeout and remove possible blocking "blockdev --report"
|
||||||
|
- dbginfo.sh: Collect config- and debug-data for chreipl-fcp-mpath
|
||||||
|
- hsci: Add support for multiple MAC addresses
|
||||||
|
|
||||||
|
Bug Fixes:
|
||||||
|
- lshwc: Fix compile error for gcc <8.1
|
||||||
|
- zdump: Various clean-ups and fixes
|
||||||
|
- ziomon: Correct throughput calculation in ziorep_printers
|
||||||
|
- zipl: Fix segmentation fault when setting stage3_parms
|
||||||
|
|
||||||
|
* __v2.18.0 (2021-10-01)__
|
||||||
|
|
||||||
|
For Linux kernel version: 5.14
|
||||||
|
|
||||||
|
Add new tools:
|
||||||
|
- scripts: Add tool for parsing sclp s390dbf logs
|
||||||
|
- zdev: Add udev rule helper tool
|
||||||
|
- zipl-editenv: Add tool to operate with zIPL environment installed in the boot record
|
||||||
|
|
||||||
|
Changes of existing tools:
|
||||||
|
- Makefile: Fix order of build of libraries for parallel builds
|
||||||
|
- dbginfo.sh: Add collection in area of timedate, coredump and --check option
|
||||||
|
- dbginfo.sh: Add exception on dump2tar for /sys/kernel/mm/page_idle/bitmap
|
||||||
|
- dbginfo.sh: Cleanup of outdated sections and general code rework
|
||||||
|
- dbginfo.sh: Collect zipl boot menu entries from boot loader specification
|
||||||
|
- lszcrypt: Add support for vfio-ap status field
|
||||||
|
- lszcrypt: Improved output for deconfig cards and queues
|
||||||
|
- lszfcp: Add linkdown case to host marker of extended output
|
||||||
|
- zdev: Add auto-config for PCI and crypto devices
|
||||||
|
- zdump: Introduce multi-level message logging
|
||||||
|
- zipl: Add support for environment block interpretation
|
||||||
|
- zkey-cryptsetup: Support LUKS2 volumes with integrity support enabled
|
||||||
|
|
||||||
|
Bug Fixes:
|
||||||
|
- hsavmcore: Avoid recompilation of overlay during install step
|
||||||
|
- libkmipclient: Fix parsing of hex values for XML and JSON encoding
|
||||||
|
- vmur/vmur.cpp: Fix error handling on transfer failure
|
||||||
|
- zdump: Lots of smaller fixes across the board
|
||||||
|
|
||||||
|
* __v2.17.0 (2021-07-07)__
|
||||||
|
|
||||||
|
For Linux kernel version: 5.12 / 5.13
|
||||||
|
|
||||||
|
Add new tools / libraries:
|
||||||
|
- hsavmcore: New utility to make the dump process with kdump more efficient
|
||||||
|
- libkmipclient: Add KMIP client shared library
|
||||||
|
- libseckey: Add a secure key library
|
||||||
|
- lshwc: New tool to extract and list complete counter sets
|
||||||
|
|
||||||
|
Changes of existing tools:
|
||||||
|
- genprotimg: Add '--(enable|disable)-pckmo' options
|
||||||
|
- genprotimg: Add OpenSSL 3.0 support
|
||||||
|
- genprotimg: Change plaintext control flags defaults so PCKMO functions are allowed
|
||||||
|
- libutil: Introduce multi-level message logging (util_log)
|
||||||
|
- libutil: Introduce util_arch module
|
||||||
|
- udev/dasd: Change DASD udev-rule to set none scheduler
|
||||||
|
- zdsfs: Add transparent codepage conversion
|
||||||
|
- zkey: Add support for KMIP-based key management systems
|
||||||
|
|
||||||
|
Bug Fixes:
|
||||||
|
- ttyrun-getty: Avoid conflicts with serial-getty@
|
||||||
|
- dbginfo: add /proc/kallsyms - refresh zVM, lscpu - fix WORKARCHIVE handling
|
||||||
|
- dbginfo: add KVM data collection for server and guest - fix lszdev
|
||||||
|
- genprotimg: Add missing return values in error paths
|
||||||
|
- zkey: Fix conversion of CCA DATA keys to CCA CIPHER keys
|
||||||
|
- znetconf: avoid conflict with "chzdev -e"
|
||||||
|
|
||||||
* __v2.16.0 (2021-02-19)__
|
* __v2.16.0 (2021-02-19)__
|
||||||
|
|
||||||
For Linux kernel version: 5.10 / 5.11
|
For Linux kernel version: 5.10 / 5.11
|
||||||
|
|||||||
@@ -3,15 +3,22 @@ ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/ar
|
|||||||
# Include common definitions
|
# Include common definitions
|
||||||
include common.mak
|
include common.mak
|
||||||
|
|
||||||
LIB_DIRS = libvtoc libutil libzds libdasd libvmdump libccw libvmcp libekmfweb
|
#
|
||||||
|
# BASELIBS: Libraries that have no dependency to other libraries in s390-tools
|
||||||
|
# LIBS: Libraries that can have a dependency to base libraries
|
||||||
|
# TOOLS: Tools that can have a dependency to base libraries or libraries
|
||||||
|
#
|
||||||
|
BASELIB_DIRS = libutil libseckey
|
||||||
|
LIB_DIRS = libvtoc libzds libdasd libvmdump libccw libvmcp libekmfweb \
|
||||||
|
libkmipclient libcpumf libap libpv
|
||||||
TOOL_DIRS = zipl zdump fdasd dasdfmt dasdview tunedasd \
|
TOOL_DIRS = zipl zdump fdasd dasdfmt dasdview tunedasd \
|
||||||
tape390 osasnmpd qetharp ip_watcher qethconf scripts zconf \
|
tape390 osasnmpd qetharp ip_watcher qethconf scripts zconf \
|
||||||
vmconvert vmcp man mon_tools dasdinfo vmur cpuplugd ipl_tools \
|
vmconvert vmcp man mon_tools dasdinfo vmur cpuplugd ipl_tools \
|
||||||
ziomon iucvterm hyptop cmsfs-fuse qethqoat zfcpdump zdsfs cpumf \
|
ziomon iucvterm hyptop cmsfs-fuse qethqoat zfcpdump zdsfs cpumf \
|
||||||
systemd hmcdrvfs cpacfstats zdev dump2tar zkey netboot etc zpcictl \
|
systemd hmcdrvfs cpacfstats zdev dump2tar zkey netboot etc zpcictl \
|
||||||
genprotimg lsstp hsci
|
genprotimg lsstp hsci hsavmcore chreipl-fcp-mpath ap_tools pvattest
|
||||||
|
|
||||||
SUB_DIRS = $(LIB_DIRS) $(TOOL_DIRS)
|
SUB_DIRS = $(BASELIB_DIRS) $(LIB_DIRS) $(TOOL_DIRS)
|
||||||
|
|
||||||
all: $(TOOL_DIRS)
|
all: $(TOOL_DIRS)
|
||||||
clean: $(TOOL_DIRS)
|
clean: $(TOOL_DIRS)
|
||||||
@@ -25,9 +32,10 @@ MAKECMDGOALS = all
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
#
|
#
|
||||||
# We have to build the libraries before the tools are built. Otherwise
|
# We have to build the base libraries before the other libraries are built,
|
||||||
# the tools would trigger parallel "make -C" builds for libraries in
|
# and then build the other libraries before the tools are built. Otherwise the
|
||||||
# case of "make -j".
|
# other libraries and tools would trigger parallel "make -C" builds for the
|
||||||
|
# base libraries and the other libraries in case of "make -j".
|
||||||
#
|
#
|
||||||
# MAKECMDGOALS contains the list of goals, e.g. "clean all". We use
|
# MAKECMDGOALS contains the list of goals, e.g. "clean all". We use
|
||||||
# "foreach" to generate a ";" separated list of "make -C <target>".
|
# "foreach" to generate a ";" separated list of "make -C <target>".
|
||||||
@@ -44,7 +52,12 @@ $(TOOL_DIRS): $(LIB_DIRS)
|
|||||||
$(MAKE) -C $@ TOPDIR=$(TOPDIR) ARCH=$(ARCH) $(goal) ;)
|
$(MAKE) -C $@ TOPDIR=$(TOPDIR) ARCH=$(ARCH) $(goal) ;)
|
||||||
.PHONY: $(TOOL_DIRS)
|
.PHONY: $(TOOL_DIRS)
|
||||||
|
|
||||||
$(LIB_DIRS):
|
$(LIB_DIRS): $(BASELIB_DIRS)
|
||||||
$(foreach goal,$(MAKECMDGOALS), \
|
$(foreach goal,$(MAKECMDGOALS), \
|
||||||
$(MAKE) -C $@ TOPDIR=$(TOPDIR) ARCH=$(ARCH) $(goal) ;)
|
$(MAKE) -C $@ TOPDIR=$(TOPDIR) ARCH=$(ARCH) $(goal) ;)
|
||||||
.PHONY: $(LIB_DIRS)
|
.PHONY: $(LIB_DIRS)
|
||||||
|
|
||||||
|
$(BASELIB_DIRS):
|
||||||
|
$(foreach goal,$(MAKECMDGOALS), \
|
||||||
|
$(MAKE) -C $@ TOPDIR=$(TOPDIR) ARCH=$(ARCH) $(goal) ;)
|
||||||
|
.PHONY: $(BASELIB_DIRS)
|
||||||
|
|||||||
@@ -33,6 +33,9 @@ Package contents
|
|||||||
* genprotimg:
|
* genprotimg:
|
||||||
Create a protected virtualization image.
|
Create a protected virtualization image.
|
||||||
|
|
||||||
|
* pvattest:
|
||||||
|
Create, perform, and verify protected virtualization attestation measurements.
|
||||||
|
|
||||||
* udev rules:
|
* udev rules:
|
||||||
- 59-dasd.rules: rules for unique DASD device nodes created in /dev/disk/.
|
- 59-dasd.rules: rules for unique DASD device nodes created in /dev/disk/.
|
||||||
- 57-osasnmpd.rules: udev rules for osasnmpd.
|
- 57-osasnmpd.rules: udev rules for osasnmpd.
|
||||||
@@ -40,6 +43,9 @@ Package contents
|
|||||||
- 40-z90crypt.rules: udev rules for z90crypt driver
|
- 40-z90crypt.rules: udev rules for z90crypt driver
|
||||||
- 90-cpi.rules: udev rule to update Control-Program-Information when KVM is
|
- 90-cpi.rules: udev rule to update Control-Program-Information when KVM is
|
||||||
used.
|
used.
|
||||||
|
- 70-chreipl-fcp-mpath.rules: udev rules to monitor multipath events for
|
||||||
|
re-IPL path failover and to adjust the re-IPL
|
||||||
|
device in case needed.
|
||||||
|
|
||||||
* systemd units:
|
* systemd units:
|
||||||
- cpi.service: Unit to apply CPI settings
|
- cpi.service: Unit to apply CPI settings
|
||||||
@@ -50,10 +56,10 @@ Package contents
|
|||||||
- ttyrun-getty@.service: Instance unit to manage ttyrun
|
- ttyrun-getty@.service: Instance unit to manage ttyrun
|
||||||
|
|
||||||
* zipl:
|
* zipl:
|
||||||
Make DASDs or tapes bootable for system IPL or system dump.
|
Make DASDs, SCSIs, NVMes or tapes bootable for system IPL or system dump.
|
||||||
|
|
||||||
* zgetdump:
|
* zgetdump:
|
||||||
Retrieve system dumps from either tapes or DASDs.
|
Retrieve system dumps from either tapes, DASDs, SCSIs or NVMes.
|
||||||
|
|
||||||
* qetharp:
|
* qetharp:
|
||||||
Read and flush the ARP cache on OSA Express network cards.
|
Read and flush the ARP cache on OSA Express network cards.
|
||||||
@@ -109,6 +115,9 @@ Package contents
|
|||||||
feature. Those traces are filtered with the zfcpdbf script, i.e. merge
|
feature. Those traces are filtered with the zfcpdbf script, i.e. merge
|
||||||
several traces, make it more readable etc.
|
several traces, make it more readable etc.
|
||||||
|
|
||||||
|
* sclpdbf:
|
||||||
|
Display debug data for the sclp kernel component.
|
||||||
|
|
||||||
* scsi_logging_level:
|
* scsi_logging_level:
|
||||||
Create, get or set the logging level for the SCSI logging facility.
|
Create, get or set the logging level for the SCSI logging facility.
|
||||||
|
|
||||||
@@ -223,7 +232,8 @@ Package contents
|
|||||||
* CPU-measurement facilities (CPU-MF) tools:
|
* CPU-measurement facilities (CPU-MF) tools:
|
||||||
Use the lscpumf tool to display information about the CPU-measurement
|
Use the lscpumf tool to display information about the CPU-measurement
|
||||||
counter and sampling facilities. Use the chcpumf tool to control the
|
counter and sampling facilities. Use the chcpumf tool to control the
|
||||||
sampling facility support.
|
sampling facility support. Use lshwc to extract complete counter sets from
|
||||||
|
the CPU Measurement Facilities.
|
||||||
|
|
||||||
* cpacfstats:
|
* cpacfstats:
|
||||||
The cpacfstats tools provide a client/server application set to monitor
|
The cpacfstats tools provide a client/server application set to monitor
|
||||||
@@ -249,9 +259,31 @@ Package contents
|
|||||||
Management Foundation - Web Edition, and is used to manage keys in an
|
Management Foundation - Web Edition, and is used to manage keys in an
|
||||||
enterprise.
|
enterprise.
|
||||||
|
|
||||||
|
* libkmipclient:
|
||||||
|
A shared library that provides an KMIP client to communicate with an KMIP
|
||||||
|
server. KMIP stands for Key Management Interoperability Protocol, and is an
|
||||||
|
extensible communication protocol that defines message formats for the
|
||||||
|
manipulation of cryptographic keys on a key management server.
|
||||||
|
|
||||||
* hsci:
|
* hsci:
|
||||||
Manage HiperSockets Converged Interfaces (HSCI).
|
Manage HiperSockets Converged Interfaces (HSCI).
|
||||||
|
|
||||||
|
* hsavmcore:
|
||||||
|
hsavmcore is designed to make the dump process with kdump more efficient.
|
||||||
|
With hsavmcore, the HSA memory that contains a part of the production
|
||||||
|
kernel's memory can be released early in the process. Depending on the size
|
||||||
|
of the production kernel's memory, writing the dump to persistent storage
|
||||||
|
can be time consuming and prevent the HSA memory from being reused
|
||||||
|
by other LPARs.
|
||||||
|
|
||||||
|
* chreipl-fcp-mpath:
|
||||||
|
Use multipath information to change the configured FCP re-IPL path on
|
||||||
|
detecting issues with the current path.
|
||||||
|
|
||||||
|
* ap-check:
|
||||||
|
A utility called by mdevctl to assist in managing vfio_ap-passthrough
|
||||||
|
devices.
|
||||||
|
|
||||||
For more information refer to the following publications:
|
For more information refer to the following publications:
|
||||||
|
|
||||||
* "Device Drivers, Features, and Commands" chapter "Useful Linux commands"
|
* "Device Drivers, Features, and Commands" chapter "Useful Linux commands"
|
||||||
@@ -270,25 +302,33 @@ build options:
|
|||||||
|
|
||||||
| __LIBRARY__ | __BUILD OPTION__ | __TOOLS__ |
|
| __LIBRARY__ | __BUILD OPTION__ | __TOOLS__ |
|
||||||
|----------------|:------------------:|:-------------------------------------:|
|
|----------------|:------------------:|:-------------------------------------:|
|
||||||
| fuse | `HAVE_FUSE` | cmsfs-fuse, zdsfs, hmcdrvfs, zgetdump |
|
| fuse3 | `HAVE_FUSE` | cmsfs-fuse, zdsfs, hmcdrvfs, zgetdump,|
|
||||||
|
| | | hsavmcore |
|
||||||
| zlib | `HAVE_ZLIB` | zgetdump, dump2tar |
|
| zlib | `HAVE_ZLIB` | zgetdump, dump2tar |
|
||||||
| ncurses | `HAVE_NCURSES` | hyptop |
|
| ncurses | `HAVE_NCURSES` | hyptop |
|
||||||
| pfm | `HAVE_PFM` | cpacfstats |
|
|
||||||
| net-snmp | `HAVE_SNMP` | osasnmpd |
|
| net-snmp | `HAVE_SNMP` | osasnmpd |
|
||||||
| glibc-static | `HAVE_LIBC_STATIC` | zfcpdump |
|
| glibc-static | `HAVE_LIBC_STATIC` | zfcpdump |
|
||||||
| openssl | `HAVE_OPENSSL` | genprotimg, zkey, libekmfweb |
|
| openssl | `HAVE_OPENSSL` | genprotimg, zkey, libekmfweb, |
|
||||||
|
| | | libkmipclient, pvattest |
|
||||||
| cryptsetup | `HAVE_CRYPTSETUP2` | zkey-cryptsetup |
|
| cryptsetup | `HAVE_CRYPTSETUP2` | zkey-cryptsetup |
|
||||||
| json-c | `HAVE_JSONC` | zkey-cryptsetup, libekmfweb |
|
| json-c | `HAVE_JSONC` | zkey-cryptsetup, libekmfweb, |
|
||||||
| glib2 | `HAVE_GLIB2` | genprotimg |
|
| | | libkmipclient |
|
||||||
| libcurl | `HAVE_LIBCURL` | genprotimg, libekmfweb |
|
| glib2 | `HAVE_GLIB2` | genprotimg, pvattest |
|
||||||
|
| libcurl | `HAVE_LIBCURL` | genprotimg, libekmfweb, libkmipclient,|
|
||||||
|
| | | pvattest |
|
||||||
|
| libxml2 | `HAVE_LIBXML2` | libkmipclient |
|
||||||
|
| systemd | `HAVE_SYSTEMD` | hsavmcore |
|
||||||
|
| liblockfile | `HAVE_LOCKFILE` | ap-check |
|
||||||
|
| libudev | `HAVE_LIBUDEV` | cpacfstatsd |
|
||||||
|
|
||||||
This table lists additional build or install options:
|
This table lists additional build or install options:
|
||||||
|
|
||||||
| __COMPONENT__ | __OPTION__ | __TOOLS__ |
|
| __COMPONENT__ | __OPTION__ | __TOOLS__ |
|
||||||
|------------------|:----------------------------:|:--------------:|
|
|------------------|:----------------------------:|:------------------------:|
|
||||||
| dracut | `HAVE_DRACUT` | zdev |
|
| dracut | `HAVE_DRACUT` | zdev, chreipl-fcp-mpath, |
|
||||||
| initramfs-tools | `HAVE_INITRAMFS` | zdev |
|
| | | zipl |
|
||||||
| | `ZDEV_ALWAYS_UPDATE_INITRD` | zdev |
|
| initramfs-tools | `HAVE_INITRAMFS` | zdev, zipl |
|
||||||
|
| | `ZDEV_ALWAYS_UPDATE_INITRD` | zdev |
|
||||||
|
|
||||||
The s390-tools build process uses "pkg-config" if available and hard-coded
|
The s390-tools build process uses "pkg-config" if available and hard-coded
|
||||||
compiler and linker options otherwise.
|
compiler and linker options otherwise.
|
||||||
@@ -310,6 +350,16 @@ the different tools are provided:
|
|||||||
|
|
||||||
The runtime requirements are: openssl-libs (>= 1.1.0) and glib2.
|
The runtime requirements are: openssl-libs (>= 1.1.0) and glib2.
|
||||||
|
|
||||||
|
* pvattest:
|
||||||
|
For building pvattest you need OpenSSL version 1.1.1 or newer
|
||||||
|
installed (openssl-devel.rpm). Also required is glib2.56 or newer
|
||||||
|
(glib2-devel.rpm) and libcurl.
|
||||||
|
Tip: you may skip the pvattest build by adding
|
||||||
|
`HAVE_OPENSSL=0`, `HAVE_LIBCURL=0`, or `HAVE_GLIB2=0`.
|
||||||
|
|
||||||
|
The runtime requirements are: openssl-libs (>= 1.1.1) and
|
||||||
|
glib2.56 or newer.
|
||||||
|
|
||||||
* osasnmpd:
|
* osasnmpd:
|
||||||
You need at least the NET-SNMP 5.1.x package (net-snmp-devel.rpm)
|
You need at least the NET-SNMP 5.1.x package (net-snmp-devel.rpm)
|
||||||
installed, before building the osasnmpd subagent.
|
installed, before building the osasnmpd subagent.
|
||||||
@@ -328,12 +378,12 @@ the different tools are provided:
|
|||||||
|
|
||||||
* cmsfs-fuse/zdsfs/hmcdrvfs/zgetdump:
|
* cmsfs-fuse/zdsfs/hmcdrvfs/zgetdump:
|
||||||
The tools cmsfs-fuse, zdsfs, hmcdrvfs, and zgetdump depend on FUSE.
|
The tools cmsfs-fuse, zdsfs, hmcdrvfs, and zgetdump depend on FUSE.
|
||||||
FUSE is provided by installing the fuse and libfuse packages and by a
|
FUSE is provided by installing the fuse3 and libfuse3 packages and by a
|
||||||
kernel compiled with `CONFIG_FUSE_FS`. For compiling the s390-tools package
|
kernel compiled with `CONFIG_FUSE_FS`. For compiling the s390-tools package
|
||||||
the fuse-devel package is required.
|
the fuse3-devel package is required.
|
||||||
The cmsfs-fuse tool requires FUSE version 2.8.1 or newer for full
|
The cmsfs-fuse tool requires FUSE version 3.0 or newer for full
|
||||||
functionality.
|
functionality.
|
||||||
For further information about FUSE see: http://fuse.sourceforge.net
|
For further information about FUSE see: https://github.com/libfuse/libfuse
|
||||||
|
|
||||||
* hyptop:
|
* hyptop:
|
||||||
The ncurses-devel package is required to build hyptop.
|
The ncurses-devel package is required to build hyptop.
|
||||||
@@ -353,14 +403,13 @@ the different tools are provided:
|
|||||||
As of s390-tools-1.13.0, the minimum required kernel level is 2.6.38.
|
As of s390-tools-1.13.0, the minimum required kernel level is 2.6.38.
|
||||||
|
|
||||||
* cpacfstats:
|
* cpacfstats:
|
||||||
For building the cpacfstats tools you need libpfm version 4 or
|
For building the cpacfstats tools you need libudev installed
|
||||||
newer installed (libpfm-devel.rpm). Tip: you may skip the cpacfstats
|
(systemd-devel.rpm). Tip: you may skip the cpacfstats build by
|
||||||
build by adding `HAVE_PFN=0` to the make invocation. To run the
|
adding `HAVE_LIBUDEV=0` to the make invocation. To run the
|
||||||
cpacfstats daemon the kernel needs to have performance
|
cpacfstats daemon the kernel needs to have performance events
|
||||||
events enabled (check for `CONFIG_PERF_EVENTS=y`) and you need libpfm
|
enabled (check for `CONFIG_PERF_EVENTS=y`). A new group 'cpacfstats'
|
||||||
version 4 or newer installed. A new group 'cpacfstats' needs to be
|
needs to be created and all users intending to use the tool should
|
||||||
created and all users intending to use the tool should be added to
|
be added to this group.
|
||||||
this group.
|
|
||||||
|
|
||||||
* zdev:
|
* zdev:
|
||||||
Depending on the boot loader and initial RAM-disk mechanism used by a
|
Depending on the boot loader and initial RAM-disk mechanism used by a
|
||||||
@@ -427,3 +476,37 @@ the different tools are provided:
|
|||||||
(libcurl-devel.rpm).
|
(libcurl-devel.rpm).
|
||||||
Tip: you may skip the libekmfweb build by adding `HAVE_OPENSSL=0`,
|
Tip: you may skip the libekmfweb build by adding `HAVE_OPENSSL=0`,
|
||||||
`HAVE_JSONC=0`, or `HAVE_LIBCURL=0` to the make invocation.
|
`HAVE_JSONC=0`, or `HAVE_LIBCURL=0` to the make invocation.
|
||||||
|
|
||||||
|
* hsavmcore:
|
||||||
|
For building the hsavmcore tool you need fuse version 3.0 and optionally
|
||||||
|
systemd which is enabled by default, to disable systemd support,
|
||||||
|
add `HAVE_SYSTEMD=0` to the make invocation.
|
||||||
|
Tip: you may skip the hsavmcore build by adding `HAVE_FUSE=0`
|
||||||
|
to the make invocation.
|
||||||
|
|
||||||
|
* libkmipclient:
|
||||||
|
For building the libkmipclient shared library you need openssl version 1.1.1
|
||||||
|
or newer installed (openssl-devel.rpm). Also required are json-c version 0.13
|
||||||
|
or newer (json-c-devel.rpm), libxml2 version 2.9.10 or newer
|
||||||
|
(libxml2-devel.rpm), and libcurl version 7.59 or newer (libcurl-devel.rpm).
|
||||||
|
Tip: you may skip the libkmipclient build by adding `HAVE_OPENSSL=0`,
|
||||||
|
`HAVE_JSONC=0`, `HAVE_LIBXML2=0`, or `HAVE_LIBCURL=0` to the make invocation.
|
||||||
|
|
||||||
|
* chreipl-fcp-mpath:
|
||||||
|
For a complete list and documentation of the requirements, installation and
|
||||||
|
uninstallation, please see
|
||||||
|
[chreipl-fcp-mpath/README.md](chreipl-fcp-mpath/README.md).
|
||||||
|
|
||||||
|
Summarized: chreipl-fcp-mpath requires GNU Bash, GNU Core Utilities,
|
||||||
|
util-linux, udev, and multipath-tools. When using `HAVE_DRACUT=1` with the
|
||||||
|
make invocation, it also requires dracut. When using `ENABLE_DOC=1` with the
|
||||||
|
make invocation to build a fresh man page (instead of using the pre-cooked
|
||||||
|
version) and render the README.md as HTML, make further requires pandoc and
|
||||||
|
GNU awk for the build process.
|
||||||
|
|
||||||
|
* ap-check:
|
||||||
|
For building the ap-check mdevctl callout utility you need liblockfile
|
||||||
|
version 1.14 or newer installed (liblockfile-devel.rpm). Also required is
|
||||||
|
json-c version 0.13 or newer (json-c-devel.rpm).
|
||||||
|
Tip: you may skip ap-check build by adding `HAVE_LOCKFILE=0` or `HAVE_JSONC=0`
|
||||||
|
to the make invocation.
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
include ../common.mak
|
||||||
|
|
||||||
|
MDEVCTL_DIR = /etc/mdevctl.d/
|
||||||
|
MDEVCTL_SCRIPTS = /etc/mdevctl.d/scripts.d/
|
||||||
|
MDEVCTL_CALLOUTS = /etc/mdevctl.d/scripts.d/callouts/
|
||||||
|
|
||||||
|
libs = $(rootdir)/libap/libap.a \
|
||||||
|
$(rootdir)/libutil/libutil.a
|
||||||
|
|
||||||
|
ifeq (${HAVE_LOCKFILE},0)
|
||||||
|
all:
|
||||||
|
$(SKIP) HAVE_LOCKFILE=0
|
||||||
|
|
||||||
|
install:
|
||||||
|
$(SKIP) HAVE_LOCKFILE=0
|
||||||
|
|
||||||
|
else ifeq (${HAVE_JSONC},0)
|
||||||
|
all:
|
||||||
|
$(SKIP) HAVE_JSONC=0
|
||||||
|
|
||||||
|
install:
|
||||||
|
$(SKIP) HAVE_JSONC=0
|
||||||
|
else
|
||||||
|
LDLIBS += -llockfile -ljson-c
|
||||||
|
|
||||||
|
all: ap-check
|
||||||
|
|
||||||
|
ap-check: ap-check.o $(libs)
|
||||||
|
|
||||||
|
install: all
|
||||||
|
@if [ ! -d $(DESTDIR)$(MDEVCTL_CALLOUTS) ]; then \
|
||||||
|
mkdir -p $(DESTDIR)$(MDEVCTL_CALLOUTS); \
|
||||||
|
chown $(OWNER).$(GROUP) $(DESTDIR)$(MDEVCTL_DIR); \
|
||||||
|
chown $(OWNER).$(GROUP) $(DESTDIR)$(MDEVCTL_SCRIPTS); \
|
||||||
|
chown $(OWNER).$(GROUP) $(DESTDIR)$(MDEVCTL_CALLOUTS); \
|
||||||
|
chmod 755 $(DESTDIR)$(MDEVCTL_DIR); \
|
||||||
|
chmod 755 $(DESTDIR)$(MDEVCTL_SCRIPTS); \
|
||||||
|
chmod 755 $(DESTDIR)$(MDEVCTL_CALLOUTS); \
|
||||||
|
fi; \
|
||||||
|
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 ap-check \
|
||||||
|
$(DESTDIR)$(MDEVCTL_CALLOUTS)
|
||||||
|
endif
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.o *~ ap-check core
|
||||||
|
|
||||||
|
.PHONY: all install clean
|
||||||
@@ -0,0 +1,911 @@
|
|||||||
|
/*
|
||||||
|
* ap-check - Validate vfio-ap mediated device configuration changes
|
||||||
|
*
|
||||||
|
* This tool in intended to be driven via the callout API of the mdevctl
|
||||||
|
* utility (https://github.com/mdevctl/mdevctl/)
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp. 2022
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <err.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "lib/ap.h"
|
||||||
|
#include "lib/util_base.h"
|
||||||
|
#include "lib/util_libc.h"
|
||||||
|
#include "lib/util_opt.h"
|
||||||
|
#include "lib/util_path.h"
|
||||||
|
|
||||||
|
#include "ap-check.h"
|
||||||
|
|
||||||
|
static const struct mdevctl_action mdevctl_action_table[NUM_MDEVCTL_ACTIONS] = {
|
||||||
|
{MDEVCTL_ACTION_DEFINE, "define"},
|
||||||
|
{MDEVCTL_ACTION_LIST, "list"},
|
||||||
|
{MDEVCTL_ACTION_MODIFY, "modify"},
|
||||||
|
{MDEVCTL_ACTION_START, "start"},
|
||||||
|
{MDEVCTL_ACTION_STOP, "stop"},
|
||||||
|
{MDEVCTL_ACTION_TYPES, "types"},
|
||||||
|
{MDEVCTL_ACTION_UNDEFINE, "undefine"},
|
||||||
|
{MDEVCTL_ACTION_ATTRIBUTES, "attributes"}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct mdevctl_event mdevctl_event_table[NUM_MDEVCTL_EVENTS] = {
|
||||||
|
{MDEVCTL_EVENT_PRE, "pre"},
|
||||||
|
{MDEVCTL_EVENT_POST, "post"},
|
||||||
|
{MDEVCTL_EVENT_GET, "get"}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert mdevctl action string to an enumerated value
|
||||||
|
*/
|
||||||
|
static enum mdevctl_action_id validate_action(char *action)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < NUM_MDEVCTL_ACTIONS; i++) {
|
||||||
|
if (strcmp(action, mdevctl_action_table[i].action) == 0)
|
||||||
|
return mdevctl_action_table[i].id;
|
||||||
|
}
|
||||||
|
|
||||||
|
return MDEVCTL_ACTION_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert mdevctl event string to an enumerated value
|
||||||
|
*/
|
||||||
|
static enum mdevctl_event_id validate_event(char *event)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < NUM_MDEVCTL_EVENTS; i++) {
|
||||||
|
if (strcmp(event, mdevctl_event_table[i].event) == 0)
|
||||||
|
return mdevctl_event_table[i].id;
|
||||||
|
}
|
||||||
|
|
||||||
|
return MDEVCTL_EVENT_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct util_opt opt_vec[] = {
|
||||||
|
UTIL_OPT_SECTION("DEVICE"),
|
||||||
|
{
|
||||||
|
.option = { "e", required_argument, NULL, 'e' },
|
||||||
|
.argument = "EVENT",
|
||||||
|
.desc = "The type of callout being issued",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "a", required_argument, NULL, 'a' },
|
||||||
|
.argument = "ACTION",
|
||||||
|
.desc = "The action being performed on the specified device",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "s", required_argument, NULL, 's' },
|
||||||
|
.argument = "STATE",
|
||||||
|
.desc = "The state of the associated mdevctl command",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "u", required_argument, NULL, 'u' },
|
||||||
|
.argument = "UUID",
|
||||||
|
.desc = "Universally Unique ID for the mediated device",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "p", required_argument, NULL, 'p' },
|
||||||
|
.argument = "PDEV",
|
||||||
|
.desc = "Parent device name, e.g. matrix",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "t", required_argument, NULL, 't' },
|
||||||
|
.argument = "TYPE",
|
||||||
|
.desc = "Mediated device type, e.g. vfio_ap-passthrough",
|
||||||
|
},
|
||||||
|
UTIL_OPT_END
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize the ap_check anchor struct.
|
||||||
|
*/
|
||||||
|
static void ap_check_init(struct ap_check_anchor *anc)
|
||||||
|
{
|
||||||
|
anc->uuid = anc->parent = anc->type = NULL;
|
||||||
|
anc->dev = vfio_ap_device_new();
|
||||||
|
anc->cleanup_lock = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Free memory of ap_check anchor struct.
|
||||||
|
*/
|
||||||
|
static void ap_check_cleanup(struct ap_check_anchor *anc)
|
||||||
|
{
|
||||||
|
if (anc->uuid)
|
||||||
|
free(anc->uuid);
|
||||||
|
if (anc->parent)
|
||||||
|
free(anc->parent);
|
||||||
|
if (anc->type)
|
||||||
|
free(anc->type);
|
||||||
|
if (anc->dev)
|
||||||
|
vfio_ap_device_free(anc->dev);
|
||||||
|
if (anc->cleanup_lock)
|
||||||
|
ap_release_lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Exit ap_check
|
||||||
|
*/
|
||||||
|
static void __noreturn ap_check_exit(struct ap_check_anchor *anc, int rc)
|
||||||
|
{
|
||||||
|
ap_check_cleanup(anc);
|
||||||
|
exit(rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* parses the command line
|
||||||
|
*/
|
||||||
|
static void ap_check_parse(struct ap_check_anchor *anc,
|
||||||
|
int argc, char *argv[])
|
||||||
|
{
|
||||||
|
bool action = false, event = false, state = false, bad_opts = false;
|
||||||
|
int opt;
|
||||||
|
|
||||||
|
util_opt_init(opt_vec, NULL);
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
opt = util_opt_getopt_long(argc, argv);
|
||||||
|
if (opt == -1)
|
||||||
|
break;
|
||||||
|
switch (opt) {
|
||||||
|
case 'e':
|
||||||
|
if (event) {
|
||||||
|
bad_opts = true;
|
||||||
|
} else {
|
||||||
|
anc->event = validate_event(optarg);
|
||||||
|
event = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'a':
|
||||||
|
if (action) {
|
||||||
|
bad_opts = true;
|
||||||
|
} else {
|
||||||
|
anc->action = validate_action(optarg);
|
||||||
|
action = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
if (state) {
|
||||||
|
bad_opts = true;
|
||||||
|
} else {
|
||||||
|
/* Ignore the state */
|
||||||
|
state = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'u':
|
||||||
|
if (anc->uuid)
|
||||||
|
bad_opts = true;
|
||||||
|
else
|
||||||
|
anc->uuid = util_strdup(optarg);
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
if (anc->parent)
|
||||||
|
bad_opts = true;
|
||||||
|
else
|
||||||
|
anc->parent = util_strdup(optarg);
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
if (anc->type)
|
||||||
|
bad_opts = true;
|
||||||
|
else
|
||||||
|
anc->type = util_strdup(optarg);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "Unknown operand\n");
|
||||||
|
ap_check_exit(anc, EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make sure we got all expected input values */
|
||||||
|
if (!(action && event && state && anc->uuid && anc->parent &&
|
||||||
|
anc->type) || bad_opts) {
|
||||||
|
fprintf(stderr, "Duplicate or missing operand\n");
|
||||||
|
ap_check_exit(anc, EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check for invalid UUID */
|
||||||
|
if (!is_valid_uuid(anc->uuid)) {
|
||||||
|
fprintf(stderr, "Invalid UUID specified\n");
|
||||||
|
ap_check_exit(anc, EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
anc->dev->uuid = util_strdup(anc->uuid);
|
||||||
|
|
||||||
|
/* Check for valid type */
|
||||||
|
if (strcmp(anc->type, VFIO_AP_TYPE) != 0)
|
||||||
|
ap_check_exit(anc, APC_EXIT_UNKNOWN_TYPE);
|
||||||
|
|
||||||
|
/* Check for invalid parent - currently only 'matrix' supported */
|
||||||
|
if (strcmp(anc->parent, "matrix") != 0) {
|
||||||
|
fprintf(stderr, "Invalid parent specified\n");
|
||||||
|
ap_check_exit(anc, EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Call a function for each entry in a directory:
|
||||||
|
* int callback(const char *abs_path, const char *rel_path, void *data)
|
||||||
|
* Continues for all entries in the directory regardless of callback return
|
||||||
|
* code. Will return 0 or, if one or more callbacks failed, the first nonzero
|
||||||
|
* rc received.
|
||||||
|
*/
|
||||||
|
static int path_for_each(const char *path,
|
||||||
|
int (*callback)(const char *, const char *, void *),
|
||||||
|
void *data)
|
||||||
|
{
|
||||||
|
struct dirent *de;
|
||||||
|
int rc = 0;
|
||||||
|
int r = 0;
|
||||||
|
DIR *dir;
|
||||||
|
char *p;
|
||||||
|
|
||||||
|
dir = opendir(path);
|
||||||
|
if (!dir)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
while ((de = readdir(dir))) {
|
||||||
|
if (strcmp(de->d_name, ".") == 0 ||
|
||||||
|
strcmp(de->d_name, "..") == 0)
|
||||||
|
continue;
|
||||||
|
util_asprintf(&p, "%s/%s", path, de->d_name);
|
||||||
|
r = callback(p, de->d_name, data);
|
||||||
|
/* Save first nonzero return code for caller */
|
||||||
|
if (rc == 0 && r != 0)
|
||||||
|
rc = r;
|
||||||
|
free(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
closedir(dir);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Report an error message when the specified configuration will conflict
|
||||||
|
* with an existing device
|
||||||
|
*/
|
||||||
|
static void conflict_error(const char *uuid, unsigned int a, unsigned int d,
|
||||||
|
bool persistent)
|
||||||
|
{
|
||||||
|
if (uuid) {
|
||||||
|
if (persistent) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"APQN %u.%u is defined for autostart by %s\n",
|
||||||
|
a, d, uuid);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "APQN %u.%u already in use by %s\n",
|
||||||
|
a, d, uuid);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (persistent) {
|
||||||
|
fprintf(stderr, "AQPN %u.%u is not defined for "
|
||||||
|
"vfio_ap-passthrough use by the persistent "
|
||||||
|
"ap bus mask settings\n", a, d);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "AQPN %u.%u is not allowed for "
|
||||||
|
"vfio_ap-passthrough use by the active ap "
|
||||||
|
"bus mask settings\n", a, d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compare the list of adapters and domains for two devices, reporting error
|
||||||
|
* messages for any conflicts that occur. A conflict occurs when both devices
|
||||||
|
* have the same adapter + domain pair.
|
||||||
|
* The function below takes advantage of the fact that the lists are known to
|
||||||
|
* be sorted in numeric order; therefore we can use this information to run
|
||||||
|
* the lists in parallel rather than always starting from the beginning.
|
||||||
|
*/
|
||||||
|
static int find_apqn_conflicts(const char *uuid,
|
||||||
|
struct util_list *adapters,
|
||||||
|
struct util_list *domains,
|
||||||
|
struct util_list *adapters2,
|
||||||
|
struct util_list *domains2,
|
||||||
|
bool persistent)
|
||||||
|
{
|
||||||
|
struct vfio_ap_node *a, *a2, *d, *d2;
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
/* Checks for conflicts with the device */
|
||||||
|
a = util_list_start(adapters);
|
||||||
|
a2 = util_list_start(adapters2);
|
||||||
|
while ((a != NULL) && (a2 != NULL)) {
|
||||||
|
if (a->id == a2->id) {
|
||||||
|
d = util_list_start(domains);
|
||||||
|
d2 = util_list_start(domains2);
|
||||||
|
while ((d != NULL) && (d2 != NULL)) {
|
||||||
|
if (d->id == d2->id) {
|
||||||
|
/* Report error, look for more */
|
||||||
|
conflict_error(uuid, a->id, d->id,
|
||||||
|
persistent);
|
||||||
|
rc = -1;
|
||||||
|
d = util_list_next(domains, d);
|
||||||
|
d2 = util_list_next(domains2, d2);
|
||||||
|
} else if (d->id > d2->id) {
|
||||||
|
d2 = util_list_next(domains2, d2);
|
||||||
|
} else {
|
||||||
|
d = util_list_next(domains, d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
a = util_list_next(adapters, a);
|
||||||
|
a2 = util_list_next(adapters2, a2);
|
||||||
|
} else if (a->id > a2->id) {
|
||||||
|
a2 = util_list_next(adapters2, a2);
|
||||||
|
} else {
|
||||||
|
a = util_list_next(adapters, a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the provided path maps to a valid vfio-ap device configuration,
|
||||||
|
* determine if its current configuration will conflict with the proposed
|
||||||
|
* changes.
|
||||||
|
*/
|
||||||
|
static int check_other_mdev_cfg_cb(const char *path,
|
||||||
|
const char *filename,
|
||||||
|
void *data)
|
||||||
|
{
|
||||||
|
struct other_mdev_cb_data *cbdata = data;
|
||||||
|
struct vfio_ap_device *dev = cbdata->dev;
|
||||||
|
struct vfio_ap_device *dev2 = NULL;
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
/* Skip anything that isn't an mdev config */
|
||||||
|
if (!is_valid_uuid(filename))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
/* Skip if this is the input device */
|
||||||
|
if (strcasecmp(cbdata->uuid, filename) == 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
/* Read the device config */
|
||||||
|
dev2 = vfio_ap_device_new();
|
||||||
|
if (vfio_ap_read_device_config(path, dev2) != 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
/* If wrong device type, skip */
|
||||||
|
if (strcmp(dev2->type, VFIO_AP_TYPE) != 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
/* If not AUTO device, skip */
|
||||||
|
if (dev2->manual)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
/* Perform mdev-to-mdev apqn conflict analysis */
|
||||||
|
rc = find_apqn_conflicts(filename, dev->adapters, dev->domains,
|
||||||
|
dev2->adapters, dev2->domains, true);
|
||||||
|
|
||||||
|
out:
|
||||||
|
if (dev2 != NULL)
|
||||||
|
vfio_ap_device_free(dev2);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Perform conflict analysis against all other vfio-ap persistent
|
||||||
|
* configurations.
|
||||||
|
*/
|
||||||
|
int check_other_mdevs_cfg(struct ap_check_anchor *anc)
|
||||||
|
{
|
||||||
|
struct other_mdev_cb_data cb_data;
|
||||||
|
|
||||||
|
if (!util_path_is_dir(VFIO_AP_CONFIG_PATH))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
cb_data.uuid = anc->uuid;
|
||||||
|
cb_data.dev = anc->dev;
|
||||||
|
|
||||||
|
return path_for_each(VFIO_AP_CONFIG_PATH, check_other_mdev_cfg_cb,
|
||||||
|
&cb_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the provided path maps to a valid device, determine if its current
|
||||||
|
* configuration will conflict with the proposed changes.
|
||||||
|
*/
|
||||||
|
static int check_other_mdev_sysfs_cb(const char *path, const char *filename,
|
||||||
|
void *data)
|
||||||
|
{
|
||||||
|
struct other_mdev_cb_data *cbdata = data;
|
||||||
|
struct vfio_ap_device *dev = cbdata->dev;
|
||||||
|
struct vfio_ap_device *dev2;
|
||||||
|
char *matrix_path;
|
||||||
|
char buf[80];
|
||||||
|
int rc = 0;
|
||||||
|
FILE *f;
|
||||||
|
|
||||||
|
if (!is_valid_uuid(filename) || path == NULL ||
|
||||||
|
strcasecmp(filename, cbdata->uuid) == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
dev2 = vfio_ap_device_new();
|
||||||
|
matrix_path = path_get_vfio_ap_attr(filename, "matrix");
|
||||||
|
f = fopen(matrix_path, "r");
|
||||||
|
while (fgets(buf, sizeof(buf), f))
|
||||||
|
vfio_ap_parse_matrix(dev2, buf);
|
||||||
|
vfio_ap_sort_matrix_results(dev2);
|
||||||
|
fclose(f);
|
||||||
|
free(matrix_path);
|
||||||
|
|
||||||
|
/* Look for conflicts between target device and this device */
|
||||||
|
rc = find_apqn_conflicts(filename, dev->adapters, dev->domains,
|
||||||
|
dev2->adapters, dev2->domains, false);
|
||||||
|
|
||||||
|
vfio_ap_device_free(dev2);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Run conflict analysis against all other active vfio-ap devices */
|
||||||
|
static int check_other_mdevs_sysfs(struct ap_check_anchor *anc)
|
||||||
|
{
|
||||||
|
struct other_mdev_cb_data cb_data;
|
||||||
|
char *root;
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
cb_data.uuid = anc->uuid;
|
||||||
|
cb_data.dev = anc->dev;
|
||||||
|
|
||||||
|
root = path_get_vfio_ap_mdev("");
|
||||||
|
if (util_path_is_dir(root))
|
||||||
|
rc = path_for_each(root, check_other_mdev_sysfs_cb, &cb_data);
|
||||||
|
|
||||||
|
free(root);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Determine if there are any conflicts between the specified device and
|
||||||
|
* the active apmask/aqmask settings. This is done by treating the masks
|
||||||
|
* as a temporary vfio_ap_device with all of the associated APQNs owned by
|
||||||
|
* the system.
|
||||||
|
*/
|
||||||
|
static int check_sysfs_mask_conflicts(struct ap_check_anchor *anc)
|
||||||
|
{
|
||||||
|
struct vfio_ap_device *sysdev = vfio_ap_device_new();
|
||||||
|
char *apmask = util_zalloc(AP_MASK_SIZE);
|
||||||
|
char *aqmask = util_zalloc(AP_MASK_SIZE);
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
if (ap_read_sysfs_masks(apmask, aqmask, AP_MASK_SIZE) != 0) {
|
||||||
|
fprintf(stderr, "Error reading system AP settings\n");
|
||||||
|
rc = -1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Convert the masks to a device with the associated APQNs */
|
||||||
|
ap_mask_to_list(apmask, sysdev->adapters);
|
||||||
|
ap_mask_to_list(aqmask, sysdev->domains);
|
||||||
|
|
||||||
|
/* Perform conflict analysis */
|
||||||
|
rc = find_apqn_conflicts(NULL, anc->dev->adapters,
|
||||||
|
anc->dev->domains, sysdev->adapters,
|
||||||
|
sysdev->domains, false);
|
||||||
|
out:
|
||||||
|
free(apmask);
|
||||||
|
free(aqmask);
|
||||||
|
vfio_ap_device_free(sysdev);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Determine if there are any conflicts between the specified device and
|
||||||
|
* the apmask/aqmask settings stored in udev. This is done by treating
|
||||||
|
* the masks as a temporary vfio_ap_device with all of the associated
|
||||||
|
* AQPNs owned by the system.
|
||||||
|
*/
|
||||||
|
static int check_cfg_mask_conflicts(struct ap_check_anchor *anc)
|
||||||
|
{
|
||||||
|
struct vfio_ap_device *sysdev = vfio_ap_device_new();
|
||||||
|
char *apmask = util_zalloc(AP_MASK_SIZE);
|
||||||
|
char *aqmask = util_zalloc(AP_MASK_SIZE);
|
||||||
|
bool read_ap = false, read_aq = false;
|
||||||
|
char *path;
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
path = path_get_ap_udev();
|
||||||
|
if (!ap_read_udev_masks(path, apmask, aqmask, &read_ap, &read_aq)) {
|
||||||
|
fprintf(stderr, "Error reading system AP settings\n");
|
||||||
|
rc = -1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Convert the masks to a device with the associated APQNs */
|
||||||
|
ap_mask_to_list(apmask, sysdev->adapters);
|
||||||
|
ap_mask_to_list(aqmask, sysdev->domains);
|
||||||
|
|
||||||
|
/* Perform conflict analysis */
|
||||||
|
rc = find_apqn_conflicts(NULL, anc->dev->adapters,
|
||||||
|
anc->dev->domains, sysdev->adapters,
|
||||||
|
sysdev->domains, true);
|
||||||
|
out:
|
||||||
|
free(apmask);
|
||||||
|
free(aqmask);
|
||||||
|
free(path);
|
||||||
|
vfio_ap_device_free(sysdev);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Subroutine to handle checking shared between DEFINE and MODIFY actions. */
|
||||||
|
static int ap_check_changes(struct ap_check_anchor *anc)
|
||||||
|
{
|
||||||
|
int rc = 0, rc2;
|
||||||
|
|
||||||
|
rc = ap_get_lock_callout();
|
||||||
|
if (rc) {
|
||||||
|
fprintf(stderr, "Failed to acquire configuration lock %d\n",
|
||||||
|
rc);
|
||||||
|
rc = -1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
anc->cleanup_lock = true;
|
||||||
|
|
||||||
|
if (vfio_ap_read_device_config(NULL, anc->dev) != 0) {
|
||||||
|
fprintf(stderr, "Failed to read device config\n");
|
||||||
|
rc = -1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(anc->dev->type, anc->type) != 0) {
|
||||||
|
fprintf(stderr, "Invalid mdev_type: %s\n", anc->dev->type);
|
||||||
|
rc = -1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!anc->dev->manual) {
|
||||||
|
/* Check against all other AUTO config files */
|
||||||
|
rc = check_other_mdevs_cfg(anc);
|
||||||
|
/* Check against the system UDEV rule for apmask/aqmask */
|
||||||
|
rc2 = check_cfg_mask_conflicts(anc);
|
||||||
|
/* If either hit an error, reflect this */
|
||||||
|
rc = rc != 0 ? rc : rc2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If successful, lock must remain held until post callout */
|
||||||
|
if (rc == 0)
|
||||||
|
anc->cleanup_lock = false;
|
||||||
|
|
||||||
|
out:
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Determine if defining the specified device is a valid operation.
|
||||||
|
* mdevctl can reach us for a DEFINE under the following circumstances:
|
||||||
|
* 1) the device does not exist
|
||||||
|
* 2) the device is active but does not have a config file, so this action
|
||||||
|
* would be to generate a config file based upon the active device.
|
||||||
|
* DEFINE has no effect on an active device (if one exists) it only creates
|
||||||
|
* the configuration file. The config file might be empty or may have various
|
||||||
|
* attributes if being fed by --jsonfile or an active device.
|
||||||
|
*/
|
||||||
|
static int ap_check_handle_define(struct ap_check_anchor *anc)
|
||||||
|
{
|
||||||
|
char *path = path_get_vfio_ap_mdev_config(anc->uuid);
|
||||||
|
|
||||||
|
if (util_path_is_readable(path)) {
|
||||||
|
fprintf(stderr, "Config already exists\n");
|
||||||
|
free(path);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(path);
|
||||||
|
|
||||||
|
return ap_check_changes(anc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Determine if modifying the specified device is a valid operation.
|
||||||
|
* mdevctl can reach us for a MODIFY under the following circumstances:
|
||||||
|
* 1) Modifying a MANUAL device
|
||||||
|
* 2) Modifying an AUTO device
|
||||||
|
* In the case of MANUAL, we don't take any action because changes made via
|
||||||
|
* MODIFY don't take affect on the active mdev until a STOP/START cycle.
|
||||||
|
* In the case of AUTO, we must compare the contents of the proposed device
|
||||||
|
* with the contents of stashed AUTO mdev configurations + the system.
|
||||||
|
*/
|
||||||
|
static int ap_check_handle_modify(struct ap_check_anchor *anc)
|
||||||
|
{
|
||||||
|
char *path = path_get_vfio_ap_mdev_config(anc->uuid);
|
||||||
|
FILE *fd = fopen(path, "r");
|
||||||
|
|
||||||
|
/* Determine if a base config file already exists for UUID */
|
||||||
|
free(path);
|
||||||
|
if (fd == NULL) {
|
||||||
|
fprintf(stderr, "Config doesn't exist\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
fclose(fd);
|
||||||
|
|
||||||
|
return ap_check_changes(anc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Determine if starting the specified device is a valid operation.
|
||||||
|
* mdevctl can reach us for a START under the following circumstances:
|
||||||
|
* 1) STARTing a MANUAL device
|
||||||
|
* 1a) Where the MANUAL device is defined (has a config file)
|
||||||
|
* 1b) Where the MANUAL device is NOT defined (no config file).
|
||||||
|
* For vfio-ap this case provides an mdev with no adapters/domains.
|
||||||
|
* 1c) Where the MANUAL device is NOT defined but a full configuration is
|
||||||
|
* provided via --jsonfile
|
||||||
|
* 2) STARTing an AUTO device
|
||||||
|
* 2a) Where the AUTO device is defined (has a config file)
|
||||||
|
* 2b) Where the AUTO device is NOT defined but a full configuration is
|
||||||
|
* provided via --jsonfile
|
||||||
|
* In each case, we must compare the proposed device with the contents of
|
||||||
|
* active mdevs + the system.
|
||||||
|
*/
|
||||||
|
static int ap_check_handle_start(struct ap_check_anchor *anc)
|
||||||
|
{
|
||||||
|
int rc = 0, rc2;
|
||||||
|
|
||||||
|
/* Can only start a device if vfio_ap is built-in or loaded */
|
||||||
|
if (!util_path_is_dir(VFIO_AP_PATH)) {
|
||||||
|
fprintf(stderr, "vfio_ap module is not loaded\n");
|
||||||
|
ap_check_exit(anc, EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = ap_get_lock_callout();
|
||||||
|
if (rc) {
|
||||||
|
fprintf(stderr, "Failed to acquire configuration lock %d\n",
|
||||||
|
rc);
|
||||||
|
rc = -1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
anc->cleanup_lock = true;
|
||||||
|
|
||||||
|
if (vfio_ap_read_device_config(NULL, anc->dev) != 0) {
|
||||||
|
fprintf(stderr, "Failed to read device config\n");
|
||||||
|
rc = -1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(anc->dev->type, anc->type) != 0) {
|
||||||
|
fprintf(stderr, "Invalid mdev_type: %s\n", anc->dev->type);
|
||||||
|
rc = -1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check against all other active vfio-ap devices */
|
||||||
|
rc = check_other_mdevs_sysfs(anc);
|
||||||
|
/* Check against the system sysfs values for apmask/aqmask */
|
||||||
|
rc2 = check_sysfs_mask_conflicts(anc);
|
||||||
|
/* If either hit an error, reflect this */
|
||||||
|
rc = rc != 0 ? rc : rc2;
|
||||||
|
|
||||||
|
/* If successful, lock must remain held until post callout */
|
||||||
|
if (rc == 0)
|
||||||
|
anc->cleanup_lock = false;
|
||||||
|
|
||||||
|
out:
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Acquire the appropriate serialization so that the specified device can be
|
||||||
|
* STOPped.
|
||||||
|
*/
|
||||||
|
static int ap_check_handle_stop(void)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = ap_get_lock_callout();
|
||||||
|
if (rc) {
|
||||||
|
fprintf(stderr, "Failed to acquire configuration lock %d\n",
|
||||||
|
rc);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The lock must remain held until post callout */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Determine if UNDEFINEing the specified device is a valid operation.
|
||||||
|
* mdevctl can reach us for an UNDEFINE under the following circumstances:
|
||||||
|
* 1) UNDEFINEing an active device
|
||||||
|
* 2) UNDEFINEing an inactive device
|
||||||
|
* UNDEFINE has no effect on the active device, it only removes the config
|
||||||
|
* file.
|
||||||
|
*/
|
||||||
|
static int ap_check_handle_undefine(struct ap_check_anchor *anc)
|
||||||
|
{
|
||||||
|
char *path = path_get_vfio_ap_mdev_config(anc->uuid);
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
rc = ap_get_lock_callout();
|
||||||
|
if (rc) {
|
||||||
|
fprintf(stderr, "Failed to acquire configuration lock %d\n",
|
||||||
|
rc);
|
||||||
|
rc = -1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
anc->cleanup_lock = true;
|
||||||
|
|
||||||
|
if (vfio_ap_read_device_config(path, anc->dev) != 0) {
|
||||||
|
fprintf(stderr, "Failed to read device config\n");
|
||||||
|
rc = -1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(anc->dev->type, anc->type) != 0) {
|
||||||
|
fprintf(stderr, "Invalid mdev_type: %s\n", anc->dev->type);
|
||||||
|
rc = -1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Success: lock must remain held until post callout */
|
||||||
|
anc->cleanup_lock = false;
|
||||||
|
|
||||||
|
out:
|
||||||
|
free(path);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For callouts where the "pre" callout would have acquired the lock, it is
|
||||||
|
* now safe to remove the lock as all changes have been committed.
|
||||||
|
*/
|
||||||
|
static int ap_check_handle_post(void)
|
||||||
|
{
|
||||||
|
return ap_release_lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* For the specified device, print the attributes to stdout in JSON format */
|
||||||
|
static int ap_check_handle_get_attributes(struct ap_check_anchor *anc)
|
||||||
|
{
|
||||||
|
struct vfio_ap_device *dev = anc->dev;
|
||||||
|
struct vfio_ap_node *node;
|
||||||
|
bool has_attr = false;
|
||||||
|
char buf[80];
|
||||||
|
char *path;
|
||||||
|
FILE *f;
|
||||||
|
|
||||||
|
path = path_get_vfio_ap_attr(anc->uuid, "matrix");
|
||||||
|
f = fopen(path, "r");
|
||||||
|
while (fgets(buf, sizeof(buf), f))
|
||||||
|
vfio_ap_parse_matrix(dev, buf);
|
||||||
|
vfio_ap_sort_matrix_results(dev);
|
||||||
|
fclose(f);
|
||||||
|
free(path);
|
||||||
|
|
||||||
|
path = path_get_vfio_ap_attr(anc->uuid, "control_domains");
|
||||||
|
f = fopen(path, "r");
|
||||||
|
while (fgets(buf, sizeof(buf), f))
|
||||||
|
vfio_ap_parse_control(dev, buf);
|
||||||
|
fclose(f);
|
||||||
|
free(path);
|
||||||
|
|
||||||
|
printf("[{");
|
||||||
|
|
||||||
|
if (!util_list_is_empty(dev->adapters)) {
|
||||||
|
util_list_iterate(dev->adapters, node) {
|
||||||
|
if (has_attr)
|
||||||
|
printf("},{");
|
||||||
|
printf("\"assign_adapter\": \"%u\"", node->id);
|
||||||
|
has_attr = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!util_list_is_empty(dev->domains)) {
|
||||||
|
util_list_iterate(dev->domains, node) {
|
||||||
|
if (has_attr)
|
||||||
|
printf("},{");
|
||||||
|
printf("\"assign_domain\": \"%u\"", node->id);
|
||||||
|
has_attr = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!util_list_is_empty(dev->controls)) {
|
||||||
|
util_list_iterate(dev->controls, node) {
|
||||||
|
if (has_attr)
|
||||||
|
printf("},{");
|
||||||
|
printf("\"assign_control_domain\": \"%u\"", node->id);
|
||||||
|
has_attr = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("}]\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Determine which mdevctl action is being checked and handle accordingly.
|
||||||
|
*/
|
||||||
|
static int ap_check_handle_action(struct ap_check_anchor *anc)
|
||||||
|
{
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
switch (anc->event) {
|
||||||
|
case MDEVCTL_EVENT_PRE:
|
||||||
|
switch (anc->action) {
|
||||||
|
case MDEVCTL_ACTION_DEFINE:
|
||||||
|
rc = ap_check_handle_define(anc);
|
||||||
|
break;
|
||||||
|
case MDEVCTL_ACTION_MODIFY:
|
||||||
|
rc = ap_check_handle_modify(anc);
|
||||||
|
break;
|
||||||
|
case MDEVCTL_ACTION_START:
|
||||||
|
rc = ap_check_handle_start(anc);
|
||||||
|
break;
|
||||||
|
case MDEVCTL_ACTION_STOP:
|
||||||
|
rc = ap_check_handle_stop();
|
||||||
|
break;
|
||||||
|
case MDEVCTL_ACTION_UNDEFINE:
|
||||||
|
rc = ap_check_handle_undefine(anc);
|
||||||
|
break;
|
||||||
|
case MDEVCTL_ACTION_LIST:
|
||||||
|
case MDEVCTL_ACTION_TYPES:
|
||||||
|
default:
|
||||||
|
/* Ignore some actions including unknown ones */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MDEVCTL_EVENT_POST:
|
||||||
|
switch (anc->action) {
|
||||||
|
case MDEVCTL_ACTION_DEFINE:
|
||||||
|
case MDEVCTL_ACTION_MODIFY:
|
||||||
|
case MDEVCTL_ACTION_START:
|
||||||
|
case MDEVCTL_ACTION_STOP:
|
||||||
|
case MDEVCTL_ACTION_UNDEFINE:
|
||||||
|
ap_check_handle_post();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* Ignore other post events */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MDEVCTL_EVENT_GET:
|
||||||
|
switch (anc->action) {
|
||||||
|
case MDEVCTL_ACTION_ATTRIBUTES:
|
||||||
|
rc = ap_check_handle_get_attributes(anc);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* Ignore some actions including unknown ones */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* Ignore any unknown events */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
struct ap_check_anchor anchor;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
ap_check_init(&anchor);
|
||||||
|
|
||||||
|
ap_check_parse(&anchor, argc, argv);
|
||||||
|
|
||||||
|
rc = ap_check_handle_action(&anchor);
|
||||||
|
|
||||||
|
ap_check_exit(&anchor, rc);
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* ap-check - Validate vfio-ap mediated device configuration changes
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp. 2022
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef AP_CHECK_H
|
||||||
|
#define AP_CHECK_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* List of all of the supported mdevctl actions
|
||||||
|
*/
|
||||||
|
enum mdevctl_action_id {
|
||||||
|
MDEVCTL_ACTION_DEFINE = 0,
|
||||||
|
MDEVCTL_ACTION_LIST,
|
||||||
|
MDEVCTL_ACTION_MODIFY,
|
||||||
|
MDEVCTL_ACTION_START,
|
||||||
|
MDEVCTL_ACTION_STOP,
|
||||||
|
MDEVCTL_ACTION_TYPES,
|
||||||
|
MDEVCTL_ACTION_UNDEFINE,
|
||||||
|
MDEVCTL_ACTION_ATTRIBUTES,
|
||||||
|
/* UNKNOWN must always be the last in the list */
|
||||||
|
MDEVCTL_ACTION_UNKNOWN,
|
||||||
|
};
|
||||||
|
#define NUM_MDEVCTL_ACTIONS MDEVCTL_ACTION_UNKNOWN
|
||||||
|
|
||||||
|
struct mdevctl_action {
|
||||||
|
enum mdevctl_action_id id;
|
||||||
|
const char action[32];
|
||||||
|
};
|
||||||
|
|
||||||
|
enum mdevctl_event_id {
|
||||||
|
MDEVCTL_EVENT_PRE = 0,
|
||||||
|
MDEVCTL_EVENT_POST,
|
||||||
|
MDEVCTL_EVENT_GET,
|
||||||
|
MDEVCTL_EVENT_UNKNOWN,
|
||||||
|
};
|
||||||
|
#define NUM_MDEVCTL_EVENTS MDEVCTL_EVENT_UNKNOWN
|
||||||
|
|
||||||
|
struct mdevctl_event {
|
||||||
|
enum mdevctl_event_id id;
|
||||||
|
const char event[32];
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ap-check special exit codes */
|
||||||
|
#define APC_EXIT_UNKNOWN_TYPE 2
|
||||||
|
|
||||||
|
struct ap_check_anchor {
|
||||||
|
enum mdevctl_event_id event;
|
||||||
|
enum mdevctl_action_id action;
|
||||||
|
char *uuid;
|
||||||
|
char *parent;
|
||||||
|
char *type;
|
||||||
|
struct vfio_ap_device *dev;
|
||||||
|
/* Active Masks */
|
||||||
|
char apmask[80];
|
||||||
|
char aqmask[80];
|
||||||
|
/* Persistent Masks */
|
||||||
|
char p_apmask[80];
|
||||||
|
char p_aqmask[80];
|
||||||
|
bool cleanup_lock;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct other_mdev_cb_data {
|
||||||
|
const char *uuid;
|
||||||
|
struct vfio_ap_device *dev;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* AP_CHECK_H */
|
||||||
+2
-2
@@ -32,7 +32,7 @@ Usage: $cmd [-t LAST_GIT_TAG]
|
|||||||
Add all new authors since last release to the $authors_file file. Without
|
Add all new authors since last release to the $authors_file file. Without
|
||||||
the -t option the tool uses the last available git tag as last release.
|
the -t option the tool uses the last available git tag as last release.
|
||||||
|
|
||||||
The tool should be called form the s390-tools master branch.
|
The tool runs on the s390-tools master branch.
|
||||||
|
|
||||||
OPTIONS
|
OPTIONS
|
||||||
-t, --tag LAST_GIT_TAG Use git tag LAST_GIT_TAG as starting point
|
-t, --tag LAST_GIT_TAG Use git tag LAST_GIT_TAG as starting point
|
||||||
@@ -116,7 +116,7 @@ echo "$cmd: Adding new authors since tag: $release_tag_last"
|
|||||||
# Print authors list without header
|
# Print authors list without header
|
||||||
tail -n +4 $authors_file_path
|
tail -n +4 $authors_file_path
|
||||||
# Add the new authors
|
# Add the new authors
|
||||||
git log --format="- %an" $release_tag_last..
|
git log --format="- %an" $release_tag_last..master
|
||||||
# Then sort everything and remove duplicates
|
# Then sort everything and remove duplicates
|
||||||
} | sort | uniq > $authors_file_tmp
|
} | sort | uniq > $authors_file_tmp
|
||||||
# Create new AUTHORS file with header ...
|
# Create new AUTHORS file with header ...
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
8e604dab39577678bc9f24bfe5e2ee35354b2f4b1759edd16093157b1ecce9d3 README.md
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
# build artifacts
|
||||||
|
/chreipl-fcp-mpath-common.sh
|
||||||
|
/chreipl-fcp-mpath-is-ipl-tgt
|
||||||
|
/chreipl-fcp-mpath-is-ipl-vol
|
||||||
|
/chreipl-fcp-mpath-is-reipl-zfcp
|
||||||
|
/chreipl-fcp-mpath-record-volume-identifier
|
||||||
|
/chreipl-fcp-mpath-try-change-ipl-path
|
||||||
|
/dracut/dracut.conf.d/70-chreipl-fcp-mpath.conf
|
||||||
|
/README.html
|
||||||
|
/README.pdf
|
||||||
|
/chreipl-fcp-mpath.md
|
||||||
@@ -0,0 +1,274 @@
|
|||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
#
|
||||||
|
# chreipl-fcp-mpath: use multipath information to change FCP IPL target
|
||||||
|
# (C) Copyright IBM Corp. 2021
|
||||||
|
#
|
||||||
|
# Uses the following system-utilities (and shell-builtins):
|
||||||
|
# Utilities list in GNU Make Conventions:
|
||||||
|
# https://www.gnu.org/software/make/manual/make.html#Utilities-in-Makefiles
|
||||||
|
# Those necessary for sourced Makefiles:
|
||||||
|
# - ../common.mak
|
||||||
|
# - chreipl-fcp-mpath.mak
|
||||||
|
# bash:
|
||||||
|
# - bash
|
||||||
|
# GNU coreutils:
|
||||||
|
# - sha256sum
|
||||||
|
# If $(ENABLE_DOC) is `1`:
|
||||||
|
# GNU awk:
|
||||||
|
# - gawk
|
||||||
|
|
||||||
|
override SHELL := /bin/bash
|
||||||
|
override .SHELLFLAGS := -O globstar -O nullglob -O extglob -c
|
||||||
|
|
||||||
|
# Include common s390-tools definitions
|
||||||
|
include ../common.mak
|
||||||
|
|
||||||
|
# Include common chreipl-fcp-mpath definitions
|
||||||
|
include chreipl-fcp-mpath.mak
|
||||||
|
|
||||||
|
# Local setting: .make.config
|
||||||
|
# You may create a file named like this in the same directory as this
|
||||||
|
# Makefile, and customize the build this way (e.g. re-define variables
|
||||||
|
# set in `chreipl-fcp-mpath.mak`, or define a `CHREIPLZFCPMP_POST_INSTALL`
|
||||||
|
# that is automatically called after each installation)
|
||||||
|
ifneq ($(wildcard .make.config),)
|
||||||
|
include $(wildcard .make.config)
|
||||||
|
endif
|
||||||
|
|
||||||
|
#
|
||||||
|
## Build
|
||||||
|
#
|
||||||
|
|
||||||
|
.PHONY: chreipl-fcp-mpath chreipl-fcp-mpath-clean
|
||||||
|
chreipl-fcp-mpath:
|
||||||
|
chreipl-fcp-mpath-clean:
|
||||||
|
all: chreipl-fcp-mpath
|
||||||
|
clean: chreipl-fcp-mpath-clean
|
||||||
|
|
||||||
|
# common function used in the helper scripts
|
||||||
|
CHREIPL_FCP_MPATH_COMMON := \
|
||||||
|
chreipl-fcp-mpath-common.sh
|
||||||
|
CHREIPL_FCP_MPATH_UDEV_HELPER := \
|
||||||
|
chreipl-fcp-mpath-is-ipl-tgt \
|
||||||
|
chreipl-fcp-mpath-is-ipl-vol \
|
||||||
|
chreipl-fcp-mpath-is-reipl-zfcp \
|
||||||
|
chreipl-fcp-mpath-record-volume-identifier \
|
||||||
|
chreipl-fcp-mpath-try-change-ipl-path
|
||||||
|
|
||||||
|
$(CHREIPL_FCP_MPATH_UDEV_HELPER) $(CHREIPL_FCP_MPATH_COMMON): $(MAKEFILE_LIST)
|
||||||
|
$(CHREIPL_FCP_MPATH_UDEV_HELPER) $(CHREIPL_FCP_MPATH_COMMON): % : %.in
|
||||||
|
$(call chreiplzfcpmp-sed-buildvar-replace,$(<),$(@))
|
||||||
|
chmod a+x $(@)
|
||||||
|
|
||||||
|
$(CHREIPL_FCP_MPATH_UDEV_HELPER): $(CHREIPL_FCP_MPATH_COMMON)
|
||||||
|
|
||||||
|
.PHONY: chreipl-fcp-mpath-udev-helper-clean
|
||||||
|
chreipl-fcp-mpath-udev-helper-clean:
|
||||||
|
rm -f $(CHREIPL_FCP_MPATH_UDEV_HELPER) $(CHREIPL_FCP_MPATH_COMMON)
|
||||||
|
|
||||||
|
udev/rules.d/70-chreipl-fcp-mpath.rules: $(CHREIPL_FCP_MPATH_UDEV_HELPER)
|
||||||
|
|
||||||
|
chreipl-fcp-mpath: udev/rules.d/70-chreipl-fcp-mpath.rules
|
||||||
|
chreipl-fcp-mpath-clean: chreipl-fcp-mpath-udev-helper-clean
|
||||||
|
|
||||||
|
dracut/dracut.conf.d/70-chreipl-fcp-mpath.conf: $(MAKEFILE_LIST)
|
||||||
|
dracut/dracut.conf.d/70-chreipl-fcp-mpath.conf: % : %.in
|
||||||
|
$(call chreiplzfcpmp-sed-buildvar-replace,$(<),$(@))
|
||||||
|
|
||||||
|
.PHONY: chreipl-fcp-mpath-dracut-clean
|
||||||
|
chreipl-fcp-mpath-dracut-clean:
|
||||||
|
rm -f dracut/dracut.conf.d/70-chreipl-fcp-mpath.conf
|
||||||
|
|
||||||
|
chreipl-fcp-mpath: dracut/dracut.conf.d/70-chreipl-fcp-mpath.conf
|
||||||
|
chreipl-fcp-mpath-clean: chreipl-fcp-mpath-dracut-clean
|
||||||
|
|
||||||
|
ifeq ($(ENABLE_DOC),1)
|
||||||
|
|
||||||
|
.PHONY: chreipl-fcp-mpath-doc
|
||||||
|
chreipl-fcp-mpath-doc: README.html chreipl-fcp-mpath.7
|
||||||
|
|
||||||
|
ifeq ($(ENABLE_DOC_PDF),1)
|
||||||
|
chreipl-fcp-mpath-doc: README.pdf
|
||||||
|
|
||||||
|
README.pdf: PANDOCFLAGS += -M title="chreipl-fcp-mpath" -M subtitle="README"
|
||||||
|
endif
|
||||||
|
|
||||||
|
README.html: PANDOCFLAGS += -M title="chreipl-fcp-mpath" -M subtitle="README"
|
||||||
|
|
||||||
|
chreiplzfcpmp-doc-man-meta = \
|
||||||
|
-M title="CHREIPL-FCP-MPATH" \
|
||||||
|
-V header="Administrator Manual" \
|
||||||
|
-V section="7" \
|
||||||
|
-V footer="s390-tools $(S390_TOOLS_RELEASE)"
|
||||||
|
|
||||||
|
chreipl-fcp-mpath.7: PANDOCFLAGS += $(chreiplzfcpmp-doc-man-meta)
|
||||||
|
chreipl-fcp-mpath.7: .chreipl-fcp-mpath.7.cksum
|
||||||
|
|
||||||
|
.INTERMEDIATE: chreipl-fcp-mpath.md
|
||||||
|
chreipl-fcp-mpath.md: README.md
|
||||||
|
gawk -- '/NOT-IN-MAN \{/,/NOT-IN-MAN \}/ { next } { print }' $(<) > $(@)
|
||||||
|
|
||||||
|
.chreipl-fcp-mpath.7.cksum: README.md
|
||||||
|
sha256sum $(<) > $(@)
|
||||||
|
|
||||||
|
.PHONY: chreipl-fcp-mpath-doc-clean
|
||||||
|
chreipl-fcp-mpath-doc-clean:
|
||||||
|
rm -f README.html README.pdf chreipl-fcp-mpath.md
|
||||||
|
|
||||||
|
chreipl-fcp-mpath: chreipl-fcp-mpath-doc
|
||||||
|
chreipl-fcp-mpath-clean: chreipl-fcp-mpath-doc-clean
|
||||||
|
|
||||||
|
else # $(ENABLE_DOC) != 1
|
||||||
|
|
||||||
|
# We bundle a pre-cooked man page with the source-code so that distributions
|
||||||
|
# don't need `pandoc` in order to be able to ship the man page. As of this
|
||||||
|
# writing multiple distributions don't have a packaged version of it.
|
||||||
|
#
|
||||||
|
# In order to remember to regenerate this pre-cooked version whenever the
|
||||||
|
# README.md is changed, we also generate a checksum of the README.md, bundle
|
||||||
|
# that as well, and compare that whenever `make` is called. This way, the
|
||||||
|
# freshness of the man page can be checked, even if `pandoc` is not available,
|
||||||
|
# or ENABLE_DOC disabled.
|
||||||
|
|
||||||
|
MANPAGE_FRESH := $(shell sha256sum --check .chreipl-fcp-mpath.7.cksum >/dev/null && echo 1 || echo 0)
|
||||||
|
ifeq ($(MANPAGE_FRESH),0)
|
||||||
|
$(warning chreipl-fcp-mpath.7 is outdated, please regenerate it by calling `make ENABLE_DOC=1`)
|
||||||
|
endif
|
||||||
|
|
||||||
|
endif # $(ENABLE_DOC) == 1
|
||||||
|
|
||||||
|
#
|
||||||
|
## Install
|
||||||
|
#
|
||||||
|
|
||||||
|
.PHONY: chreipl-fcp-mpath-install
|
||||||
|
# The content of `CHREIPLZFCPMP_POST_INSTALL` (bash script) is automatically
|
||||||
|
# called *after* installing chreipl-fcp-mpath during `make install`. If not
|
||||||
|
# defined (the default), nothing happens. You may define this on the make
|
||||||
|
# command line, or by creating a `.make.config` and defining the variable in
|
||||||
|
# there.
|
||||||
|
chreipl-fcp-mpath-install:
|
||||||
|
$(CHREIPLZFCPMP_POST_INSTALL)
|
||||||
|
|
||||||
|
install: chreipl-fcp-mpath-install
|
||||||
|
|
||||||
|
# install udev rules
|
||||||
|
INSTDIRS += $(UDEVRULESDIR)
|
||||||
|
$(DESTDIR)$(UDEVRULESDIR): install_dirs
|
||||||
|
|
||||||
|
.PHONY: chreipl-fcp-mpath-install-udev-rules
|
||||||
|
chreipl-fcp-mpath-install-udev-rules: | $(DESTDIR)$(UDEVRULESDIR)
|
||||||
|
chreipl-fcp-mpath-install-udev-rules: udev/rules.d/70-chreipl-fcp-mpath.rules
|
||||||
|
$(INSTALL_DATA) -t $(DESTDIR)$(UDEVRULESDIR) \
|
||||||
|
udev/rules.d/70-chreipl-fcp-mpath.rules
|
||||||
|
|
||||||
|
chreipl-fcp-mpath-install: chreipl-fcp-mpath-install-udev-rules
|
||||||
|
|
||||||
|
# install udev helper programs
|
||||||
|
INSTDIRS += $(UDEVDIR)
|
||||||
|
$(DESTDIR)$(UDEVDIR): install_dirs
|
||||||
|
|
||||||
|
.PHONY: chreipl-fcp-mpath-install-udev-helper
|
||||||
|
chreipl-fcp-mpath-install-udev-helper: | $(DESTDIR)$(UDEVDIR)
|
||||||
|
chreipl-fcp-mpath-install-udev-helper: $(CHREIPL_FCP_MPATH_UDEV_HELPER)
|
||||||
|
$(INSTALL_EXEC) -t $(DESTDIR)$(UDEVDIR) $(CHREIPL_FCP_MPATH_UDEV_HELPER)
|
||||||
|
|
||||||
|
chreipl-fcp-mpath-install: chreipl-fcp-mpath-install-udev-helper
|
||||||
|
|
||||||
|
# install common library files
|
||||||
|
INSTDIRS += $(CHREIPLZFCPMPDIR)
|
||||||
|
$(DESTDIR)$(CHREIPLZFCPMPDIR): install_dirs
|
||||||
|
|
||||||
|
.PHONY: chreipl-fcp-mpath-install-libfiles
|
||||||
|
chreipl-fcp-mpath-install-libfiles: | $(DESTDIR)$(CHREIPLZFCPMPDIR)
|
||||||
|
chreipl-fcp-mpath-install-libfiles: $(CHREIPL_FCP_MPATH_COMMON)
|
||||||
|
$(INSTALL_DATA) -t $(DESTDIR)$(CHREIPLZFCPMPDIR) \
|
||||||
|
$(CHREIPL_FCP_MPATH_COMMON)
|
||||||
|
|
||||||
|
chreipl-fcp-mpath-install: chreipl-fcp-mpath-install-libfiles
|
||||||
|
|
||||||
|
ifeq ($(HAVE_DRACUT),1)
|
||||||
|
|
||||||
|
# install dracut config files
|
||||||
|
INSTDIRS += $(DRACUTCONFDIR)
|
||||||
|
$(DESTDIR)$(DRACUTCONFDIR): install_dirs
|
||||||
|
|
||||||
|
.PHONY: chreipl-fcp-mpath-install-dracut-config
|
||||||
|
chreipl-fcp-mpath-install-dracut-config: | $(DESTDIR)$(DRACUTCONFDIR)
|
||||||
|
chreipl-fcp-mpath-install-dracut-config: dracut/dracut.conf.d/70-chreipl-fcp-mpath.conf
|
||||||
|
$(INSTALL_DATA) -t $(DESTDIR)$(DRACUTCONFDIR) \
|
||||||
|
dracut/dracut.conf.d/70-chreipl-fcp-mpath.conf
|
||||||
|
|
||||||
|
chreipl-fcp-mpath-install: chreipl-fcp-mpath-install-dracut-config
|
||||||
|
|
||||||
|
endif
|
||||||
|
|
||||||
|
# chreipl-fcp-mpath: install man page
|
||||||
|
INSTDIRS += $(MANDIR)
|
||||||
|
$(DESTDIR)$(MANDIR)/man7: install_dirs
|
||||||
|
|
||||||
|
.PHONY: chreipl-fcp-mpath-install-man-page
|
||||||
|
chreipl-fcp-mpath-install-man-page: | $(DESTDIR)$(MANDIR)/man7
|
||||||
|
chreipl-fcp-mpath-install-man-page: chreipl-fcp-mpath.7
|
||||||
|
$(INSTALL_DATA) -t $(DESTDIR)$(MANDIR)/man7 \
|
||||||
|
chreipl-fcp-mpath.7
|
||||||
|
|
||||||
|
chreipl-fcp-mpath-install: chreipl-fcp-mpath-install-man-page
|
||||||
|
|
||||||
|
#
|
||||||
|
## Utility
|
||||||
|
#
|
||||||
|
|
||||||
|
# Utilities for the debug feature of chreipl-fcp-mpath-common.sh.
|
||||||
|
#
|
||||||
|
# When `chreipl-fcp-mpath` is built with D=1 (default is D=0), each run
|
||||||
|
# of one of the helper scripts will create a debug log in $(DEBUGOUTDIR)
|
||||||
|
# (default: /run/udev) which among other things contains the complete shell
|
||||||
|
# trace of that script run, with some added information that would not be
|
||||||
|
# inspectable otherwise with just the trace.
|
||||||
|
#
|
||||||
|
# There is currently no way of enabling/disabling this at runtime.
|
||||||
|
#
|
||||||
|
# chreipl-fcp-mpath-common.sh defines debug log file name as
|
||||||
|
# "chreiplzfcpmp-${debug_trace_tag}-${SEQNUM:-0}.XXXXXXXXXX" where SEQNUM is a
|
||||||
|
# udev rule environment variable and each X is replaced with some [[:alnum:]]
|
||||||
|
# by mktemp. For a definition of `debug_trace_tag`, please see the comments
|
||||||
|
# in the source.
|
||||||
|
#
|
||||||
|
# The following targets can be used for some simple access and filtering of the
|
||||||
|
# logs during development.
|
||||||
|
|
||||||
|
DEBUG_LOG_GLOB = $(DEBUGOUTDIR)/chreiplzfcpmp-[[:digit:]][[:digit:]][[:alpha:]][[:alpha:]][[:alpha:]][[:alpha:]]-+([[:digit:]]).[[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]]
|
||||||
|
|
||||||
|
# display all debug log files on the system
|
||||||
|
.PHONY: chreipl-fcp-mpath-debug-logs
|
||||||
|
chreipl-fcp-mpath-debug-logs:
|
||||||
|
@ls -1d $(DEBUG_LOG_GLOB)
|
||||||
|
|
||||||
|
# display only debug log files of script runs that exited with status 0 (= good)
|
||||||
|
#
|
||||||
|
# XXX: `sed -n -e '$p'` is used instead of `tail -n1` to prevent an other
|
||||||
|
# dependency just for that (both invocations do the same thing).
|
||||||
|
.PHONY: chreipl-fcp-mpath-debug-logs-filter-good
|
||||||
|
chreipl-fcp-mpath-debug-logs-filter-good:
|
||||||
|
@for lg in $(DEBUG_LOG_GLOB); do \
|
||||||
|
sed -e '/^+ trap_exit$$/,/^+ trap - EXIT$$/d' "$${lg}" \
|
||||||
|
| sed -n -e '$$p' \
|
||||||
|
| grep -q -e '^+ exit 0$$' || continue; \
|
||||||
|
ls -d "$${lg}"; \
|
||||||
|
done
|
||||||
|
|
||||||
|
# display only debug log files of script runs that didn't exit with status 0
|
||||||
|
# (= bad)
|
||||||
|
.PHONY: chreipl-fcp-mpath-debug-logs-filter-bad
|
||||||
|
chreipl-fcp-mpath-debug-logs-filter-bad:
|
||||||
|
@for lg in $(DEBUG_LOG_GLOB); do \
|
||||||
|
sed -e '/^+ trap_exit$$/,/^+ trap - EXIT$$/d' "$${lg}" \
|
||||||
|
| sed -n -e '$$p' \
|
||||||
|
| grep -q -e '^+ exit 0$$' && continue; \
|
||||||
|
ls -d "$${lg}"; \
|
||||||
|
done
|
||||||
|
|
||||||
|
.PHONY: chreipl-fcp-mpath-debug-logs-clean
|
||||||
|
chreipl-fcp-mpath-debug-logs-clean:
|
||||||
|
rm -f $(DEBUG_LOG_GLOB)
|
||||||
@@ -0,0 +1,270 @@
|
|||||||
|
<!-- markdown documentation: https://github.github.com/gfm/ -->
|
||||||
|
|
||||||
|
NAME
|
||||||
|
====
|
||||||
|
|
||||||
|
chreipl-fcp-mpath - use multipath information for re-IPL path failover on a
|
||||||
|
running Linux instance
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
===========
|
||||||
|
|
||||||
|
The IPL process of Linux on Z or LinuxONE from an FCP-attached SCSI volume uses
|
||||||
|
exactly one path to the volume. If this path is unavailable, the IPL fails.
|
||||||
|
|
||||||
|
The **chreipl-fcp-mpath** toolset monitors **udev** events about paths to the
|
||||||
|
re-IPL volume. If the currently configured re-IPL path becomes unavailable, the
|
||||||
|
toolset checks for operational paths to the same volume. If available, it
|
||||||
|
reconfigures the re-IPL settings to use an operational path.
|
||||||
|
|
||||||
|
Thus, re-IPL from an FCP-attached SCSI volume can be successful despite path
|
||||||
|
failures on a running Linux instance if at least one path to the re-IPL volume
|
||||||
|
remains operational.
|
||||||
|
|
||||||
|
**Chreipl-fcp-mpath** requires **udev**, **multipathd** and **dm-multipath**.
|
||||||
|
Once installed, the toolset runs automatically and autonomously. No user
|
||||||
|
intervention is possible or required.
|
||||||
|
|
||||||
|
Other than installing the toolset, there is no user interface for
|
||||||
|
**chreipl-fcp-mpath**.
|
||||||
|
|
||||||
|
Requirements
|
||||||
|
------------
|
||||||
|
|
||||||
|
The **chreipl-fcp-mpath** tool has the following requirements on the
|
||||||
|
Linux instance that is being monitored:
|
||||||
|
|
||||||
|
- The Linux instance must have started successfully, during IPL.
|
||||||
|
|
||||||
|
- The running Linux instance must use **dm-multipath** and **multipathd** for
|
||||||
|
the configured re-IPL volume - a volume that contains a zipl boot record
|
||||||
|
and has one of its paths used in the re-IPL configuration.
|
||||||
|
|
||||||
|
- **udev** must run.
|
||||||
|
|
||||||
|
- The toolset must observe at least one event about the configured re-IPL
|
||||||
|
path. Examples for such events are: the SCSI disk comes online, or a path
|
||||||
|
of the corresponding multipath device goes down or comes back online.
|
||||||
|
|
||||||
|
- The WWID of the re-IPL volume must not change while the Linux instance is
|
||||||
|
running.
|
||||||
|
|
||||||
|
- When the configured re-IPL path becomes unavailable while the Linux
|
||||||
|
instance is running, at least one operational path to the re-IPL volume
|
||||||
|
must be available, or must become available. If no such path is available
|
||||||
|
when the Linux instance is rebooted, the re-IPL path is not changed.
|
||||||
|
|
||||||
|
- The tool assumes that any manually reconfigured re-IPL device is valid and
|
||||||
|
operational.
|
||||||
|
|
||||||
|
The tool treats a newly configured re-IPL device like the initially
|
||||||
|
configured re-IPL device. In particular, if the newly configured re-IPL
|
||||||
|
device fulfills the requirements of the tool, re-IPL path failover takes
|
||||||
|
place if the configured re-IPL path becomes unavailable.
|
||||||
|
|
||||||
|
Caution with Manual Changes to the Configured re-IPL Target
|
||||||
|
-----------------------------------------------------------
|
||||||
|
|
||||||
|
**chreipl-fcp-mpath** is designed to accept operator-inititated changes of the
|
||||||
|
re-IPL device. However, concurrent changes by the operator and tool driven
|
||||||
|
changes can result in the operator change being overwritten.
|
||||||
|
|
||||||
|
To avoid this problem, change the re-IPL device only during steady-state
|
||||||
|
operations, when no path events happen. Alternatively, make sure that no events
|
||||||
|
are processed while you change the device. See [EXAMPLES](#examples) for one
|
||||||
|
way to suspend event processing.
|
||||||
|
|
||||||
|
MESSAGES
|
||||||
|
========
|
||||||
|
|
||||||
|
During monitoring and event processing, **chreipl-fcp-mpath** writes messages
|
||||||
|
to the syslog.
|
||||||
|
|
||||||
|
When the configured re-IPL path is changed to a different path to the same
|
||||||
|
volume (priority *daemon.notice*):
|
||||||
|
|
||||||
|
> Changed re-IPL path to: \<device-bus-id\>:\<wwpn\>:\<lun\>.
|
||||||
|
|
||||||
|
When a path event indicates that the last available path has become
|
||||||
|
non-operational (priority *daemon.alert*):
|
||||||
|
|
||||||
|
> The re-IPL device cannot be changed because no operational path to the
|
||||||
|
> re-IPL volume remains. The next re-IPL might fail unless you re-attach or
|
||||||
|
> enable at least one valid path to the re-IPL volume.
|
||||||
|
|
||||||
|
When changing the configured re-IPL device failed because of an error with the
|
||||||
|
used Linux kernel interface (priority *daemon.crit*):
|
||||||
|
|
||||||
|
> Changing the re-IPL device failed. The current re-IPL settings might be
|
||||||
|
> inconsistent. Check and correct the settings (see the README.md of
|
||||||
|
> chreipl-fcp-mpath) to make sure that the current re-IPL device is valid.
|
||||||
|
|
||||||
|
A failure to change the re-IPL device can indicate an inconsistent setting that
|
||||||
|
cannot be corrected automatically by **chreipl-fcp-mpath**. As a result, the
|
||||||
|
next re-IPL might fail or might not use the intended re-IPL device.
|
||||||
|
|
||||||
|
You can use the following tools to check and correct the current settings:
|
||||||
|
|
||||||
|
- **lsreipl** to confirm that the intended re-IPL device is configured;
|
||||||
|
- **chreipl** to change the re-IPL device;
|
||||||
|
- **lszfcp** to inspect the state of available paths to the re-IPL device.
|
||||||
|
|
||||||
|
<!-- NOT-IN-MAN { -->
|
||||||
|
|
||||||
|
SOFTWARE REQUIREMENTS
|
||||||
|
=====================
|
||||||
|
|
||||||
|
**chreipl-fcp-mpath** integrates into s390-tools's build and install
|
||||||
|
infrastructure. Use **make** to build it. No explicit dependency management is
|
||||||
|
in place, but the toolset has some software dependencies besides the
|
||||||
|
requirements in section [Requirements](#requirements):
|
||||||
|
|
||||||
|
- GNU Bash;
|
||||||
|
- GNU Core Utilities (mktemp, readlink, sync, truncate, sha256sum);
|
||||||
|
- util-linux (flock, hexdump, logger);
|
||||||
|
- udev / systemd-udev;
|
||||||
|
- multipath-tools.
|
||||||
|
|
||||||
|
To make use of the optional dracut configuration you need: dracut.
|
||||||
|
|
||||||
|
To build a fresh version of the documentation (man page) you need:
|
||||||
|
|
||||||
|
- pandoc;
|
||||||
|
- GNU Core Utilities (date);
|
||||||
|
- GNU awk;
|
||||||
|
|
||||||
|
otherwise the pre-cooked version shipped with the source will be used.
|
||||||
|
|
||||||
|
INSTALLATION
|
||||||
|
============
|
||||||
|
|
||||||
|
If your distribution includes a packaged version of **chreipl-fcp-mpath**,
|
||||||
|
either as a separate package or as part of a **s390-tools** package, install
|
||||||
|
that package. Otherwise, you can either install it from source as part of
|
||||||
|
**s390-tools** or separately.
|
||||||
|
|
||||||
|
To install **chreipl-fcp-mpath** as part of **s390-tools**, use **make** on the
|
||||||
|
top-level directory of your **s390-tools** distribution. Installing the entire
|
||||||
|
distribution might overwrite other already installed tools.
|
||||||
|
|
||||||
|
To install the tool separately, change into the **chreipl-fcp-mpath**
|
||||||
|
directory, and use **make** there.
|
||||||
|
|
||||||
|
You need *root* privileges to install the tool into the root file system.
|
||||||
|
|
||||||
|
Calling **make** runs the build steps. Calling **make install** runs the build
|
||||||
|
steps and copies the resulting components to their final destination.
|
||||||
|
**s390-tools** offers more options and targets to customize the build (see
|
||||||
|
**make help**).
|
||||||
|
|
||||||
|
**chreipl-fcp-mpath** has the following optional build options:
|
||||||
|
|
||||||
|
| Option | Values | Default | Effect
|
||||||
|
| :----- | :----: | :-----: | :-----
|
||||||
|
| HAVE_DRACUT | 0, 1 | 0 | Install a dracut configuration file that includes **chreipl-fcp-mpath** in the initial ramdisks built with **dracut**.
|
||||||
|
| ENABLE_DOC | 0, 1 | 0 | Build a fresh version of the man page for **chreipl-fcp-mpath**.
|
||||||
|
|
||||||
|
Specify any options as arguments for both the **make** and **make install**
|
||||||
|
command as shown in the following example:
|
||||||
|
|
||||||
|
~ # cd chreipl-fcp-mpath/
|
||||||
|
~ # make HAVE_DRACUT=1 ENABLE_DOC=1
|
||||||
|
~ # make HAVE_DRACUT=1 ENABLE_DOC=1 install
|
||||||
|
|
||||||
|
After the installation, reload the udev rules database:
|
||||||
|
|
||||||
|
~ # udevadm control --reload
|
||||||
|
|
||||||
|
*The toolset is now active on your running Linux instance.*
|
||||||
|
|
||||||
|
If you use the *HAVE_DRACUT=1* option, also rebuild your
|
||||||
|
initial ramdisk, to immediately include the toolset instead of
|
||||||
|
waiting for the next kernel update.
|
||||||
|
|
||||||
|
How to rebuild the initial ramdisk and the naming scheme for the
|
||||||
|
resulting file or files depends on your distribution.
|
||||||
|
The following example applies to Fedora and to Red Hat Enterprise Linux:
|
||||||
|
|
||||||
|
~ # dracut --force /boot/initramfs-"$(uname -r)".img "$(uname -r)"
|
||||||
|
|
||||||
|
For SUSE Linux Enterprise Server run for example:
|
||||||
|
|
||||||
|
~ # dracut --hostonly --force /boot/initrd-"$(uname -r)" "$(uname -r)"
|
||||||
|
|
||||||
|
These commands replace the initial ramdisk for the currently running kernel.
|
||||||
|
If your distribution uses **zipl** as its boot loader, run **zipl** to refresh
|
||||||
|
the boot record to find the new initial ramdisk.
|
||||||
|
|
||||||
|
~ # zipl
|
||||||
|
|
||||||
|
With dracut enabled, **make install** deploys the following files to these
|
||||||
|
default locations:
|
||||||
|
|
||||||
|
/usr/lib/chreipl-fcp-mpath/chreipl-fcp-mpath-common.sh
|
||||||
|
/usr/lib/dracut/dracut.conf.d/70-chreipl-fcp-mpath.conf
|
||||||
|
/usr/lib/udev/chreipl-fcp-mpath-is-ipl-tgt
|
||||||
|
/usr/lib/udev/chreipl-fcp-mpath-is-ipl-vol
|
||||||
|
/usr/lib/udev/chreipl-fcp-mpath-is-reipl-zfcp
|
||||||
|
/usr/lib/udev/chreipl-fcp-mpath-record-volume-identifier
|
||||||
|
/usr/lib/udev/chreipl-fcp-mpath-try-change-ipl-path
|
||||||
|
/usr/lib/udev/rules.d/70-chreipl-fcp-mpath.rules
|
||||||
|
/usr/share/man/man7/chreipl-fcp-mpath.7
|
||||||
|
|
||||||
|
UNINSTALL
|
||||||
|
=========
|
||||||
|
|
||||||
|
If your distribution includes a separately from **s390-tools** packaged version
|
||||||
|
of **chreipl-fcp-mpath**, uninstall that package.
|
||||||
|
|
||||||
|
For installations without distribution packaging, you cannot uninstall
|
||||||
|
**chreipl-fcp-mpath** with **make**.
|
||||||
|
|
||||||
|
Instead, remove the toolset by deleting the installed files as listed in
|
||||||
|
[INSTALLATION](#installation)), reload the udev rules database, and rebuild all
|
||||||
|
modified initial ramdisks as described in [INSTALLATION](#installation)).
|
||||||
|
|
||||||
|
<!-- NOT-IN-MAN } -->
|
||||||
|
|
||||||
|
EXAMPLES
|
||||||
|
========
|
||||||
|
|
||||||
|
Manual Changes to the Configured re-IPL Device
|
||||||
|
----------------------------------------------
|
||||||
|
|
||||||
|
As outlined in [DESCRIPTION](#description), be cautious when manually changing
|
||||||
|
the configured re-IPL device. Assure that your reconfiguration actions do not
|
||||||
|
collide with concurrent automatic event processing by **chreipl-fcp-mpath**.
|
||||||
|
You can avoid such collisions, by stopping event processing, making your
|
||||||
|
changes, and then re-enabling event processing. You need *root* privileges for
|
||||||
|
running the commands in the following example:
|
||||||
|
|
||||||
|
~ # udevadm settle
|
||||||
|
~ # udevadm control --stop-exec-queue
|
||||||
|
~ # chreipl ...
|
||||||
|
~ # udevadm control --start-exec-queue
|
||||||
|
|
||||||
|
Listing messages with journalctl
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
If your Linux instance includes **journalctl**, use the following command to
|
||||||
|
list all messages that are issued by **chreipl-fcp-mpath**:
|
||||||
|
|
||||||
|
~ # journalctl -t chreipl-fcp-mpath
|
||||||
|
|
||||||
|
To list only messages that were issued since the last IPL, use this command:
|
||||||
|
|
||||||
|
~ # journalctl -t chreipl-fcp-mpath -b
|
||||||
|
|
||||||
|
REPORTING BUGS
|
||||||
|
==============
|
||||||
|
|
||||||
|
Use the **Issues** functionality on GitHub to report any bugs in
|
||||||
|
**chreipl-fcp-mpath**:
|
||||||
|
[s390-tools Issues](<https://github.com/ibm-s390-linux/s390-tools/issues> "Link to the s390-tools Issues page").
|
||||||
|
|
||||||
|
SEE ALSO
|
||||||
|
========
|
||||||
|
|
||||||
|
**chreipl**(8), **dracut**(8), **journalctl**(1), **lsreipl(8)**,
|
||||||
|
**lszfcp**(8), **multipath**(8), **multipathd**(8), **udev**(7),
|
||||||
|
**udevadm**(8), **zipl**(8)
|
||||||
@@ -0,0 +1,446 @@
|
|||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
#
|
||||||
|
# chreipl-fcp-mpath: use multipath information to change FCP IPL target
|
||||||
|
# (C) Copyright IBM Corp. 2021
|
||||||
|
#
|
||||||
|
# Uses the following system-utilities (and shell-builtins):
|
||||||
|
# GNU coreutils:
|
||||||
|
# - mktemp
|
||||||
|
# - readlink
|
||||||
|
# - sync
|
||||||
|
# util-linux:
|
||||||
|
# - flock
|
||||||
|
# - logger
|
||||||
|
|
||||||
|
# Makes use of udev event environment variables:
|
||||||
|
# SEQNUM
|
||||||
|
|
||||||
|
# (1) expand failed globs to an empty string
|
||||||
|
# (2) extended pattern matching to strip leading/trailing whitespaces
|
||||||
|
shopt -s nullglob extglob
|
||||||
|
# (1) don't overwrite existing files using redirects (e.g.: `>`)
|
||||||
|
set -o noclobber
|
||||||
|
|
||||||
|
# make sure any state files created are only writeable by the owning user
|
||||||
|
umask 027
|
||||||
|
|
||||||
|
# create log if DEBUG is enabled (with Make: D=1)
|
||||||
|
#
|
||||||
|
# Each script importing this library and expecting a debug log to be created
|
||||||
|
# must declare a *trace tag* in a variable `debug_trace_tag`. This is used as
|
||||||
|
# identifier in the log file name. The format is:
|
||||||
|
#
|
||||||
|
# [[:digit:]][[:digit:]][[:alpha:]][[:alpha:]][[:alpha:]][[:alpha:]]
|
||||||
|
# \ /\ /
|
||||||
|
# --------\ /-------- -------------------\ /-------------------
|
||||||
|
# \/ \/
|
||||||
|
# relative position of some unique abbreviation for the script
|
||||||
|
# execution in the name, excluding any common prefix
|
||||||
|
# udev rules
|
||||||
|
if '@DEBUG@' && [ -v debug_trace_tag ] && tlg="$(
|
||||||
|
mktemp -p '@debugoutdir@' \
|
||||||
|
"chreiplzfcpmp-${debug_trace_tag}-${SEQNUM:-0}.XXXXXXXXXX" \
|
||||||
|
2>/dev/null)"
|
||||||
|
then
|
||||||
|
readonly tlg
|
||||||
|
exec >|"${tlg}" 2>&1
|
||||||
|
set -x
|
||||||
|
set
|
||||||
|
else
|
||||||
|
unset tlg
|
||||||
|
fi
|
||||||
|
|
||||||
|
declare -gr ID_FILE='@chreiplzfcpmp-id-file@'
|
||||||
|
declare -gr FW_LOCK_FILE='@chreiplzfcpmp-fwlock-file@'
|
||||||
|
|
||||||
|
declare -gA TRAP_EXIT_FN=()
|
||||||
|
declare -gf trap_exit 1>/dev/null
|
||||||
|
function trap_exit() {
|
||||||
|
local fn
|
||||||
|
|
||||||
|
for fn in "${TRAP_EXIT_FN[@]}"; do
|
||||||
|
"${fn}"
|
||||||
|
done
|
||||||
|
|
||||||
|
trap - EXIT
|
||||||
|
}
|
||||||
|
trap trap_exit EXIT
|
||||||
|
|
||||||
|
# Output variables:
|
||||||
|
# id_file_unlock_exclusive_create() - call to unlock when finished with
|
||||||
|
# critical section
|
||||||
|
#
|
||||||
|
# XXX: `id_file_lock_*` can't be taken recursively
|
||||||
|
function id_file_lock_exclusive_create() {
|
||||||
|
declare -g ID_FILE_LOCK=""
|
||||||
|
|
||||||
|
# prevent concurrent file creation
|
||||||
|
#
|
||||||
|
# First, open the file defined in ${ID_FILE} for writing; this will
|
||||||
|
# succeed and create the file only if it doesn't exist already. If the
|
||||||
|
# file already exist, the first open attempt will fail and we fall
|
||||||
|
# back to opening it only for reading; this will always succeed if the
|
||||||
|
# file already exists (the reason why the first attempty failed). In
|
||||||
|
# both cases store the corresponding file descriptor in
|
||||||
|
# ${ID_FILE_LOCK}.
|
||||||
|
#
|
||||||
|
# XXX: This should be race free.
|
||||||
|
# open() with O_EXCL... is atomic (we set `noclobber` as shell
|
||||||
|
# option); at least as long as we talk about a local FS.
|
||||||
|
if ! { exec {ID_FILE_LOCK}>"${ID_FILE}"; } 2>/dev/null; then
|
||||||
|
{ exec {ID_FILE_LOCK}<"${ID_FILE}"; } 2>/dev/null \
|
||||||
|
|| return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
declare -gf id_file_unlock_exclusive_create 1>/dev/null
|
||||||
|
function id_file_unlock_exclusive_create() {
|
||||||
|
if [ -v ID_FILE_LOCK ]; then
|
||||||
|
sync "${ID_FILE}" 2>/dev/null
|
||||||
|
# release file and implicitly the lock, if taken
|
||||||
|
exec {ID_FILE_LOCK}>&-
|
||||||
|
unset ID_FILE_LOCK
|
||||||
|
fi
|
||||||
|
|
||||||
|
unset "TRAP_EXIT_FN[id_file_unlock_exclusive_create]"
|
||||||
|
}
|
||||||
|
TRAP_EXIT_FN+=(
|
||||||
|
[id_file_unlock_exclusive_create]=id_file_unlock_exclusive_create
|
||||||
|
)
|
||||||
|
|
||||||
|
flock --exclusive --timeout 5 "${ID_FILE_LOCK}" || return 2
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Output variables:
|
||||||
|
# id_file_unlock_exclusive_no_create() - call to unlock when finished with
|
||||||
|
# critical section
|
||||||
|
#
|
||||||
|
# XXX: `id_file_lock_*` can't be taken recursively
|
||||||
|
function id_file_lock_exclusive_no_create() {
|
||||||
|
declare -g ID_FILE_LOCK=""
|
||||||
|
|
||||||
|
# Open the file defined in ${ID_FILE} for reading, and store the
|
||||||
|
# corresponding file descriptor in ${ID_FILE_LOCK}.
|
||||||
|
#
|
||||||
|
# XXX: return code is used in `chreipl-fcp-mpath-try-change-ipl-path`
|
||||||
|
{ exec {ID_FILE_LOCK}<"${ID_FILE}"; } 2>/dev/null || return 1
|
||||||
|
|
||||||
|
declare -gf id_file_unlock_exclusive_no_create 1>/dev/null
|
||||||
|
function id_file_unlock_exclusive_no_create() {
|
||||||
|
if [ -v ID_FILE_LOCK ]; then
|
||||||
|
sync "${ID_FILE}" 2>/dev/null
|
||||||
|
# release file and implicitly the lock, if taken
|
||||||
|
exec {ID_FILE_LOCK}<&-
|
||||||
|
unset ID_FILE_LOCK
|
||||||
|
fi
|
||||||
|
|
||||||
|
unset "TRAP_EXIT_FN[id_file_unlock_exclusive_no_create]"
|
||||||
|
}
|
||||||
|
TRAP_EXIT_FN+=(
|
||||||
|
[id_file_unlock_exclusive_no_create]=id_file_unlock_exclusive_no_create
|
||||||
|
)
|
||||||
|
|
||||||
|
flock --exclusive --timeout 5 "${ID_FILE_LOCK}" || return 2
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Output variables:
|
||||||
|
# id_file_unlock_shared_no_create() - call to unlock when finished with
|
||||||
|
# critical section
|
||||||
|
#
|
||||||
|
# XXX: `id_file_lock_*` can't be taken recursively
|
||||||
|
function id_file_lock_shared_no_create() {
|
||||||
|
declare -g ID_FILE_LOCK=""
|
||||||
|
|
||||||
|
# Open the file defined in ${ID_FILE} for reading, and store the
|
||||||
|
# corresponding file descriptor in ${ID_FILE_LOCK}.
|
||||||
|
{ exec {ID_FILE_LOCK}<"${ID_FILE}"; } 2>/dev/null || return 1
|
||||||
|
|
||||||
|
declare -gf id_file_unlock_shared_no_create 1>/dev/null
|
||||||
|
function id_file_unlock_shared_no_create() {
|
||||||
|
if [ -v ID_FILE_LOCK ]; then
|
||||||
|
# release file and implicitly the lock, if taken
|
||||||
|
exec {ID_FILE_LOCK}<&-
|
||||||
|
unset ID_FILE_LOCK
|
||||||
|
fi
|
||||||
|
|
||||||
|
unset "TRAP_EXIT_FN[id_file_unlock_shared_no_create]"
|
||||||
|
}
|
||||||
|
TRAP_EXIT_FN+=(
|
||||||
|
[id_file_unlock_shared_no_create]=id_file_unlock_shared_no_create
|
||||||
|
)
|
||||||
|
|
||||||
|
flock --shared --timeout 5 "${ID_FILE_LOCK}" || return 2
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Output variables:
|
||||||
|
# firmware_unlock_exclusive() - call to unlock when finished with critical section
|
||||||
|
#
|
||||||
|
# XXX: `firmware_lock_*` can't be taken recursively
|
||||||
|
function firmware_lock_exclusive() {
|
||||||
|
declare -g FIRMWARE_LOCK=""
|
||||||
|
|
||||||
|
# Open the file defined in ${FW_LOCK_FILE} for reading, and store the
|
||||||
|
# corresponding file descriptor in ${FIRMWARE_LOCK} (it doesn't matter
|
||||||
|
# whether this is a normal file or directory). This file descriptor
|
||||||
|
# will only be used for locking - not for actual I/O.
|
||||||
|
{ exec {FIRMWARE_LOCK}<"${FW_LOCK_FILE}"; } 2>/dev/null || return 1
|
||||||
|
|
||||||
|
declare -gf firmware_unlock_exclusive 1>/dev/null
|
||||||
|
function firmware_unlock_exclusive() {
|
||||||
|
if [ -v FIRMWARE_LOCK ]; then
|
||||||
|
# release file and implicitly the lock, if taken
|
||||||
|
exec {FIRMWARE_LOCK}<&-
|
||||||
|
unset FIRMWARE_LOCK
|
||||||
|
fi
|
||||||
|
|
||||||
|
unset "TRAP_EXIT_FN[firmware_unlock_exclusive]"
|
||||||
|
}
|
||||||
|
TRAP_EXIT_FN+=([firmware_unlock_exclusive]=firmware_unlock_exclusive)
|
||||||
|
|
||||||
|
flock --exclusive --timeout 5 "${FIRMWARE_LOCK}" || return 2
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Output variables:
|
||||||
|
# firmware_unlock_shared() - call to unlock when finished with critical section
|
||||||
|
#
|
||||||
|
# XXX: `firmware_lock_*` can't be taken recursively
|
||||||
|
function firmware_lock_shared() {
|
||||||
|
declare -g FIRMWARE_LOCK=""
|
||||||
|
|
||||||
|
# Open the file defined in ${FW_LOCK_FILE} for reading, and store the
|
||||||
|
# corresponding file descriptor in ${FIRMWARE_LOCK} (it doesn't matter
|
||||||
|
# whether this is a normal file or directory). This file descriptor
|
||||||
|
# will only be used for locking - not for actual I/O.
|
||||||
|
{ exec {FIRMWARE_LOCK}<"${FW_LOCK_FILE}"; } 2>/dev/null || return 1
|
||||||
|
|
||||||
|
declare -gf firmware_unlock_shared 1>/dev/null
|
||||||
|
function firmware_unlock_shared() {
|
||||||
|
if [ -v FIRMWARE_LOCK ]; then
|
||||||
|
# release file and implicitly the lock, if taken
|
||||||
|
exec {FIRMWARE_LOCK}<&-
|
||||||
|
unset FIRMWARE_LOCK
|
||||||
|
fi
|
||||||
|
|
||||||
|
unset "TRAP_EXIT_FN[firmware_unlock_shared]"
|
||||||
|
}
|
||||||
|
TRAP_EXIT_FN+=([firmware_unlock_shared]=firmware_unlock_shared)
|
||||||
|
|
||||||
|
flock --shared --timeout 5 "${FIRMWARE_LOCK}" || return 2
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Output variables:
|
||||||
|
# IPL_TYPE
|
||||||
|
# IPL_BUSID
|
||||||
|
# IPL_WWPN
|
||||||
|
# IPL_LUN
|
||||||
|
function firmware_get_ipl_information() {
|
||||||
|
declare -g IPL_TYPE="" IPL_BUSID="" IPL_WWPN="" IPL_LUN=""
|
||||||
|
|
||||||
|
# Take lock so we don't see any intermediate state from other helpers
|
||||||
|
# running in parallel
|
||||||
|
firmware_lock_shared || return 5
|
||||||
|
|
||||||
|
{ read -r IPL_TYPE _ < /sys/firmware/reipl/reipl_type; } 2>/dev/null \
|
||||||
|
|| return 1
|
||||||
|
{ read -r IPL_BUSID _ < /sys/firmware/reipl/fcp/device; } 2>/dev/null \
|
||||||
|
|| return 2
|
||||||
|
{ read -r IPL_WWPN _ < /sys/firmware/reipl/fcp/wwpn; } 2>/dev/null \
|
||||||
|
|| return 3
|
||||||
|
{ read -r IPL_LUN _ < /sys/firmware/reipl/fcp/lun; } 2>/dev/null \
|
||||||
|
|| return 4
|
||||||
|
|
||||||
|
firmware_unlock_shared
|
||||||
|
|
||||||
|
# show read values in debug log if enabled
|
||||||
|
if '@DEBUG@'; then
|
||||||
|
declare -p IPL_TYPE IPL_BUSID IPL_WWPN IPL_LUN 1>&2
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Input:
|
||||||
|
# 1: absolute canonical path to the scsi device in sysfs, e.g.:
|
||||||
|
# /sys/devices/css0/0.0.0014/0.0.1700/host1/rport-1:0-0/target1:0:0/1:0:0:1075789848
|
||||||
|
# Output variables:
|
||||||
|
# SDEV_LUN
|
||||||
|
function sdev_get_lun() {
|
||||||
|
local sdev="${1}" sdev_lun_str
|
||||||
|
# bash uses `intmax_t` as width for integer variables, and glibc
|
||||||
|
# defines this either as `long int` on 64 bit systems, or
|
||||||
|
# `long long int` on other.
|
||||||
|
local -i sdev_lun=0 fcp_lun=0
|
||||||
|
|
||||||
|
sdev_lun_str="${sdev##*:}"
|
||||||
|
# e.g.: 1075789848
|
||||||
|
[[ "${sdev_lun_str}" == +([[:digit:]]) ]] || return 1
|
||||||
|
# "cast" to integer
|
||||||
|
sdev_lun="${sdev_lun_str}"
|
||||||
|
|
||||||
|
# convert the Linux integer LUN format to the hexadecimal 64 bit T10
|
||||||
|
# LUN representation format used by many s390x interfaces
|
||||||
|
(( fcp_lun = (((sdev_lun >> 0) & 0xffff) << 48)
|
||||||
|
| (((sdev_lun >> 16) & 0xffff) << 32)
|
||||||
|
| (((sdev_lun >> 32) & 0xffff) << 16)
|
||||||
|
| (((sdev_lun >> 48) & 0xffff) << 0) ))
|
||||||
|
# the '0x' prefix is part of the length
|
||||||
|
printf -v SDEV_LUN "%#018llx" "${fcp_lun}"
|
||||||
|
|
||||||
|
# show read values in debug log if enabled
|
||||||
|
if '@DEBUG@'; then
|
||||||
|
declare -p SDEV_LUN 1>&2
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Input:
|
||||||
|
# 1: path to the scsi device in sysfs, e.g.:
|
||||||
|
# /sys/devices/css0/0.0.0014/0.0.1700/host1/rport-1:0-0/target1:0:0/1:0:0:1075789848
|
||||||
|
# , or a symlink pointing to the scsi device, e.g.:
|
||||||
|
# /sys/class/block/sds/device
|
||||||
|
# Output variables:
|
||||||
|
# SDEV_BUSID
|
||||||
|
# SDEV_WWPN
|
||||||
|
# SDEV_LUN
|
||||||
|
function sdev_get_fcp_addressing() {
|
||||||
|
local sdev="${1}" fcp_lun rport rport_wwpn zfcp_dev
|
||||||
|
|
||||||
|
declare -g SDEV_BUSID="" SDEV_WWPN="" SDEV_LUN=""
|
||||||
|
|
||||||
|
sdev="$(readlink -se "${sdev}")" || return 1
|
||||||
|
|
||||||
|
# get the LUN for this SDEV
|
||||||
|
#
|
||||||
|
# sets ${SDEV_LUN}
|
||||||
|
sdev_get_lun "${sdev}" || return 2
|
||||||
|
|
||||||
|
# get the WWPN of the remote port this SDEV is attached to
|
||||||
|
printf -v rport "%s" "${sdev}"/../../fc_remote_ports/rport-*:*-*
|
||||||
|
# e.g.: /sys/devices/css0/0.0.0016/0.0.1740/host0/rport-0:0-1/fc_remote_ports/rport-0:0-1
|
||||||
|
[ "${rport}" != "" ] || return 3
|
||||||
|
|
||||||
|
# XXX: This works even if the rport is currently in a bad
|
||||||
|
# state, so e.g. when it has just gone down because of a
|
||||||
|
# cable pull.
|
||||||
|
{ read -r rport_wwpn _ < "${rport}"/port_name; } 2>/dev/null \
|
||||||
|
|| return 4
|
||||||
|
|
||||||
|
# The Linux kernel doesn't guarantee the same format as in
|
||||||
|
# /sys/firmware/..., so make sure it is the one we expect.
|
||||||
|
[[ "${rport_wwpn}" =~ ^0x[[:xdigit:]]{1,16}$ ]] || return 5
|
||||||
|
# the '0x' prefix is part of the length
|
||||||
|
printf -v rport_wwpn "%#018llx" "${rport_wwpn}"
|
||||||
|
|
||||||
|
# get the Device Bus-ID of the device via which this SDEV is attached
|
||||||
|
zfcp_dev="$(readlink -se "${sdev}"/../../../..)" || return 6
|
||||||
|
# e.g.: /sys/devices/css0/0.0.0016/0.0.1740
|
||||||
|
zfcp_dev="${zfcp_dev##*/}"
|
||||||
|
|
||||||
|
# shellcheck disable=2034
|
||||||
|
SDEV_BUSID="${zfcp_dev}"
|
||||||
|
# shellcheck disable=2034
|
||||||
|
SDEV_WWPN="${rport_wwpn}"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Input:
|
||||||
|
# 1: path to the scsi device in sysfs
|
||||||
|
# Output variables:
|
||||||
|
# SDEV_WWID
|
||||||
|
function sdev_get_wwid() {
|
||||||
|
local sdev="${1}"
|
||||||
|
local -a wwid
|
||||||
|
|
||||||
|
declare -g SDEV_WWID=""
|
||||||
|
|
||||||
|
# read the volume identifier without stripping any content
|
||||||
|
#
|
||||||
|
# XXX: we can read the WWID file, even if the SDEV is currently not
|
||||||
|
# operational (e.g.: due to the path has gone away), as long as
|
||||||
|
# the VPD PG 83 is still cached in the kernel; and the page gets
|
||||||
|
# only released on SDEV device release.
|
||||||
|
{ readarray -d "" -t wwid < "${sdev}"/wwid; } 2>/dev/null \
|
||||||
|
|| return 1
|
||||||
|
if '@DEBUG@'; then declare -p wwid 1>&2; fi
|
||||||
|
|
||||||
|
# test whether we read something
|
||||||
|
#
|
||||||
|
# This strips all leading spaces from the beginning of the read WWID
|
||||||
|
# (until the first non-space or NUL character), and checks whether the
|
||||||
|
# result is empty.
|
||||||
|
# Hence, we return early if the WWID consists of only whitespace.
|
||||||
|
#
|
||||||
|
# XXX: there could be unexpected characters in the returned ID.
|
||||||
|
# `scsi_id` from the udev helpers sanitizes the strings it reads
|
||||||
|
# from the devices, so they can be used in environment variables
|
||||||
|
# without much danger.
|
||||||
|
# But we don't export anything here, so it should be fine.
|
||||||
|
[ "${wwid[0]/#*([[:space:]])}" != "" ] || return 2
|
||||||
|
|
||||||
|
# shellcheck disable=2034
|
||||||
|
SDEV_WWID="${wwid[0]}"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Input:
|
||||||
|
# 1: path to the scsi device in sysfs
|
||||||
|
# Return Value:
|
||||||
|
# == 0: SDEV referenced by `1` in good state
|
||||||
|
# != 0: otherwise
|
||||||
|
function sdev_test_path_state() {
|
||||||
|
local sdev="${1}" state zfcp_failed zfcp_in_recovery rport port_state
|
||||||
|
|
||||||
|
sdev="$(readlink -se "${sdev}")" || return 1
|
||||||
|
|
||||||
|
{ read -r state _ < "${sdev}"/state; } 2>/dev/null || return 2
|
||||||
|
{ read -r zfcp_failed _ < "${sdev}"/zfcp_failed; } 2>/dev/null \
|
||||||
|
|| return 3
|
||||||
|
{ read -r zfcp_in_recovery _ < "${sdev}"/zfcp_in_recovery; } 2>/dev/null \
|
||||||
|
|| return 4
|
||||||
|
|
||||||
|
printf -v rport "%s" "${sdev}"/../../fc_remote_ports/rport-*:*-*
|
||||||
|
# e.g.: /sys/devices/css0/0.0.0016/0.0.1740/host0/rport-0:0-1/fc_remote_ports/rport-0:0-1
|
||||||
|
[ "${rport}" != "" ] || return 5
|
||||||
|
{ read -r port_state _ < "${rport}"/port_state; } 2>/dev/null \
|
||||||
|
|| return 6
|
||||||
|
|
||||||
|
if '@DEBUG@'; then
|
||||||
|
declare -p state zfcp_failed zfcp_in_recovery port_state 1>&2
|
||||||
|
fi
|
||||||
|
|
||||||
|
[ "${state}" = "running" ] || return 7
|
||||||
|
[ "${zfcp_failed}" = "0" ] || return 8
|
||||||
|
[ "${zfcp_in_recovery}" = "0" ] || return 9
|
||||||
|
{ [ "${port_state}" = "Online" ] \
|
||||||
|
|| [ "${port_state}" = "Marginal" ]; } || return 10
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Input:
|
||||||
|
# *: all input parameters are used as quoted message
|
||||||
|
function log_note() {
|
||||||
|
logger -p 'daemon.notice' -t 'chreipl-fcp-mpath' "${*}" &>/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
# Input:
|
||||||
|
# *: all input parameters are used as quoted message
|
||||||
|
function log_crit() {
|
||||||
|
logger -p 'daemon.crit' -t 'chreipl-fcp-mpath' "${*}" &>/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
# Input:
|
||||||
|
# *: all input parameters are used as quoted message
|
||||||
|
function log_alert() {
|
||||||
|
logger -p 'daemon.alert' -t 'chreipl-fcp-mpath' "${*}" &>/dev/null
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
#
|
||||||
|
# chreipl-fcp-mpath: use multipath information to change FCP IPL target
|
||||||
|
# (C) Copyright IBM Corp. 2021
|
||||||
|
#
|
||||||
|
# Uses the following system-utilities (and shell-builtins):
|
||||||
|
# Those necessary for sourced library:
|
||||||
|
# - chreipl-fcp-mpath-common.sh
|
||||||
|
|
||||||
|
# Find out whether the device in udev event environment variable ${DEVPATH}
|
||||||
|
# represents the device we want to re-IPL from. We do this by comparing
|
||||||
|
# Device-Bus-ID/Target-WWPN/LUN of the individual SDEVs to the parameters set
|
||||||
|
# in `/sys/firmware/reipl/fcp/`.
|
||||||
|
#
|
||||||
|
# Makes use of udev event environment variables:
|
||||||
|
# DM_UUID
|
||||||
|
# SUBSYSTEM
|
||||||
|
# DEVPATH
|
||||||
|
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
declare -gr debug_trace_tag=05iilt
|
||||||
|
# shellcheck disable=SC1091
|
||||||
|
source '@chreiplzfcpmp-lib@' || exit 127
|
||||||
|
|
||||||
|
firmware_get_ipl_information || exit 1
|
||||||
|
|
||||||
|
if [[ "${DM_UUID}" == mpath-* ]]; then
|
||||||
|
# Assume Multipath Device Mapper Device;
|
||||||
|
# e.g.: DEVPATH = /devices/virtual/block/dm-0
|
||||||
|
declare sdev
|
||||||
|
|
||||||
|
# depends on `nullglob` from `chreipl-fcp-mpath-common.sh`
|
||||||
|
for sdev in /sys/"${DEVPATH}"/slaves/sd*/device; do
|
||||||
|
sdev_get_fcp_addressing "${sdev}" || continue
|
||||||
|
[ "${SDEV_LUN}" = "${IPL_LUN}" ] || continue
|
||||||
|
[ "${SDEV_WWPN}" = "${IPL_WWPN}" ] || continue
|
||||||
|
[ "${SDEV_BUSID}" = "${IPL_BUSID}" ] || continue
|
||||||
|
|
||||||
|
exit 0
|
||||||
|
done
|
||||||
|
unset sdev
|
||||||
|
|
||||||
|
elif [ "${SUBSYSTEM}" = block ]; then
|
||||||
|
# Assume SCSI Disk;
|
||||||
|
# e.g.: DEVPATH = /devices/css0/0.0.0014/0.0.1700/host0/rport-0:0-0/target0:0:0/0:0:0:1074806808/block/sds
|
||||||
|
|
||||||
|
sdev_get_fcp_addressing /sys/"${DEVPATH}"/device || exit 2
|
||||||
|
[ "${SDEV_LUN}" = "${IPL_LUN}" ] || exit 3
|
||||||
|
[ "${SDEV_WWPN}" = "${IPL_WWPN}" ] || exit 4
|
||||||
|
[ "${SDEV_BUSID}" = "${IPL_BUSID}" ] || exit 5
|
||||||
|
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit 6
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
#
|
||||||
|
# chreipl-fcp-mpath: use multipath information to change FCP IPL target
|
||||||
|
# (C) Copyright IBM Corp. 2021
|
||||||
|
#
|
||||||
|
# Uses the following system-utilities (and shell-builtins):
|
||||||
|
# Those necessary for sourced library:
|
||||||
|
# - chreipl-fcp-mpath-common.sh
|
||||||
|
|
||||||
|
# Find out whether the device in environment variable ${DEVPATH} represents the
|
||||||
|
# _volume_ that we IPL'ed from. We do this by comparing its WWID to the one
|
||||||
|
# recorded in `@chreiplzfcpmp-id-file@`.
|
||||||
|
#
|
||||||
|
# Makes use of udev event environment variables:
|
||||||
|
# DM_UUID
|
||||||
|
# SUBSYSTEM
|
||||||
|
# DEVPATH
|
||||||
|
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
declare -gr debug_trace_tag=11iilv
|
||||||
|
# shellcheck disable=SC1091
|
||||||
|
source '@chreiplzfcpmp-lib@' || exit 127
|
||||||
|
|
||||||
|
function id_file_read_ipl_information() {
|
||||||
|
local -a records
|
||||||
|
|
||||||
|
declare -g REC_WWID="" REC_BUSID="" REC_WWPN="" REC_LUN=""
|
||||||
|
|
||||||
|
# lock file before reading ID, so we don't see any intermediate state
|
||||||
|
id_file_lock_shared_no_create || return 1
|
||||||
|
|
||||||
|
{ readarray -d "" -t -u "${ID_FILE_LOCK}" records; } 2>/dev/null \
|
||||||
|
|| return 2
|
||||||
|
if '@DEBUG@'; then declare -p records 1>&2; fi
|
||||||
|
|
||||||
|
id_file_unlock_shared_no_create
|
||||||
|
|
||||||
|
[ "${#records[@]}" = "4" ] || return 3
|
||||||
|
# check that none of the array fields contains whitespace only
|
||||||
|
[ "${records[0]/#*([[:space:]])}" != "" ] || return 4
|
||||||
|
[ "${records[1]/#*([[:space:]])}" != "" ] || return 5
|
||||||
|
[ "${records[2]/#*([[:space:]])}" != "" ] || return 6
|
||||||
|
[ "${records[3]/#*([[:space:]])}" != "" ] || return 7
|
||||||
|
|
||||||
|
REC_WWID="${records[0]}"
|
||||||
|
REC_BUSID="${records[1]}"
|
||||||
|
REC_WWPN="${records[2]}"
|
||||||
|
REC_LUN="${records[3]}"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
id_file_read_ipl_information || exit 1
|
||||||
|
|
||||||
|
if [[ "${DM_UUID}" == mpath-* ]]; then
|
||||||
|
# Assume Multipath Device Mapper Device;
|
||||||
|
# e.g.: DEVPATH = /devices/virtual/block/dm-0
|
||||||
|
declare sdev found=false
|
||||||
|
|
||||||
|
for sdev in /sys/"${DEVPATH}"/slaves/sd*/device; do
|
||||||
|
if sdev_get_wwid "${sdev}"; then
|
||||||
|
found=true
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
unset sdev
|
||||||
|
|
||||||
|
"${found}" || exit 2
|
||||||
|
|
||||||
|
elif [ "${SUBSYSTEM}" = block ]; then
|
||||||
|
# Assume SCSI Disk;
|
||||||
|
# e.g.: DEVPATH = /devices/css0/0.0.0014/0.0.1700/host0/rport-0:0-0/target0:0:0/0:0:0:1074806808/block/sds
|
||||||
|
|
||||||
|
sdev_get_wwid /sys/"${DEVPATH}"/device || exit 3
|
||||||
|
fi
|
||||||
|
|
||||||
|
# set by `sdev_get_wwid` and `id_file_read_ipl_information`
|
||||||
|
[ "${SDEV_WWID}" = "${REC_WWID}" ] || exit 4
|
||||||
|
|
||||||
|
firmware_get_ipl_information || exit 5
|
||||||
|
# set by `firmware_get_ipl_information` and `id_file_read_ipl_information`
|
||||||
|
[ "${IPL_BUSID}" = "${REC_BUSID}" ] || exit 6
|
||||||
|
[ "${IPL_WWPN}" = "${REC_WWPN}" ] || exit 7
|
||||||
|
[ "${IPL_LUN}" = "${REC_LUN}" ] || exit 8
|
||||||
|
|
||||||
|
exit 0
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
#
|
||||||
|
# chreipl-fcp-mpath: use multipath information to change FCP IPL target
|
||||||
|
# (C) Copyright IBM Corp. 2021
|
||||||
|
#
|
||||||
|
# Uses the following system-utilities (and shell-builtins):
|
||||||
|
# Those necessary for sourced library:
|
||||||
|
# - chreipl-fcp-mpath-common.sh
|
||||||
|
|
||||||
|
# Find out whether ReIPL is gonna happen from a SCSI volume attached via zFCP
|
||||||
|
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
declare -gr debug_trace_tag=00iriz
|
||||||
|
# shellcheck disable=SC1091
|
||||||
|
source '@chreiplzfcpmp-lib@' || exit 127
|
||||||
|
|
||||||
|
declare reipl_type
|
||||||
|
|
||||||
|
{ read -r reipl_type _ < /sys/firmware/reipl/reipl_type; } 2>/dev/null || exit 1
|
||||||
|
if '@DEBUG@'; then declare -p reipl_type 1>&2; fi
|
||||||
|
|
||||||
|
[ "${reipl_type}" = "fcp" ] || exit 2
|
||||||
|
|
||||||
|
exit 0
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
#
|
||||||
|
# chreipl-fcp-mpath: use multipath information to change FCP IPL target
|
||||||
|
# (C) Copyright IBM Corp. 2021
|
||||||
|
#
|
||||||
|
# Uses the following system-utilities (and shell-builtins):
|
||||||
|
# Those necessary for sourced library:
|
||||||
|
# - chreipl-fcp-mpath-common.sh
|
||||||
|
# GNU coreutils:
|
||||||
|
# - truncate
|
||||||
|
# util-linux:
|
||||||
|
# - hexdump
|
||||||
|
|
||||||
|
# Record the identification of the volume we want to re-IPL from
|
||||||
|
#
|
||||||
|
# Makes use of udev event environment variables:
|
||||||
|
# DM_UUID
|
||||||
|
# SUBSYSTEM
|
||||||
|
# DEVPATH
|
||||||
|
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
declare -gr debug_trace_tag=10rvid
|
||||||
|
# shellcheck disable=SC1091
|
||||||
|
source '@chreiplzfcpmp-lib@' || exit 127
|
||||||
|
|
||||||
|
function id_file_record_ipl_information() {
|
||||||
|
local sdev_wwid="${1}" ipl_busid="${2}" ipl_wwpn="${3}" ipl_lun="${4}"
|
||||||
|
|
||||||
|
# lock file before writing ID, so noone sees any intermediate state
|
||||||
|
id_file_lock_exclusive_create || return 1
|
||||||
|
|
||||||
|
# reset ID without removing the file (necessary for the locking to work
|
||||||
|
# properly, since the FD we use for locking is on this file/inode)
|
||||||
|
truncate --no-create --size=0 "${ID_FILE}" || return 3
|
||||||
|
echo -ne "${sdev_wwid}\x00${ipl_busid}\x00${ipl_wwpn}\x00${ipl_lun}\x00" \
|
||||||
|
>>"${ID_FILE}" || return 4
|
||||||
|
|
||||||
|
if '@DEBUG@'; then hexdump -vC "${ID_FILE}" 1>&2; fi
|
||||||
|
|
||||||
|
id_file_unlock_exclusive_create
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ "${DM_UUID}" == mpath-* ]]; then
|
||||||
|
# Assume Multipath Device Mapper Device;
|
||||||
|
# e.g.: DEVPATH = /devices/virtual/block/dm-0
|
||||||
|
declare sdev
|
||||||
|
|
||||||
|
for sdev in /sys/"${DEVPATH}"/slaves/sd*/device; do
|
||||||
|
if sdev_get_wwid "${sdev}"; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
unset sdev
|
||||||
|
|
||||||
|
elif [ "${SUBSYSTEM}" = block ]; then
|
||||||
|
# Assume SCSI Disk;
|
||||||
|
# e.g.: DEVPATH = /devices/css0/0.0.0014/0.0.1700/host0/rport-0:0-0/target0:0:0/0:0:0:1074806808/block/sds
|
||||||
|
|
||||||
|
sdev_get_wwid /sys/"${DEVPATH}"/device
|
||||||
|
fi
|
||||||
|
# shellcheck disable=SC2153
|
||||||
|
[ "${SDEV_WWID}" != "" ] || exit 1
|
||||||
|
|
||||||
|
firmware_get_ipl_information || exit 2
|
||||||
|
# shellcheck disable=SC2153
|
||||||
|
id_file_record_ipl_information \
|
||||||
|
"${SDEV_WWID}" "${IPL_BUSID}" "${IPL_WWPN}" "${IPL_LUN}" \
|
||||||
|
|| exit 3
|
||||||
|
|
||||||
|
exit 0
|
||||||
@@ -0,0 +1,158 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
#
|
||||||
|
# chreipl-fcp-mpath: use multipath information to change FCP IPL target
|
||||||
|
# (C) Copyright IBM Corp. 2021
|
||||||
|
#
|
||||||
|
# Uses the following system-utilities (and shell-builtins):
|
||||||
|
# Those necessary for sourced library:
|
||||||
|
# - chreipl-fcp-mpath-common.sh
|
||||||
|
# GNU coreutils:
|
||||||
|
# - truncate
|
||||||
|
# util-linux:
|
||||||
|
# - hexdump
|
||||||
|
|
||||||
|
# Try to change the current re-IPL target to a dfferent, operational path to
|
||||||
|
# the same volume.
|
||||||
|
#
|
||||||
|
# Makes use of udev event environment variables:
|
||||||
|
# DM_UUID
|
||||||
|
# SUBSYSTEM
|
||||||
|
# DEVPATH
|
||||||
|
# CHREIPL_FCP_MPATH_IS_TGT
|
||||||
|
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
declare -gr debug_trace_tag=15tcip
|
||||||
|
# shellcheck disable=SC1091
|
||||||
|
source '@chreiplzfcpmp-lib@' || exit 127
|
||||||
|
|
||||||
|
function apply_ipl_information() {
|
||||||
|
local sdev_wwid="${1}"
|
||||||
|
local sdev_busid="${2}" sdev_wwpn="${3}" sdev_lun="${4}"
|
||||||
|
local ipl_type="${5}" ipl_busid="${6}" ipl_wwpn="${7}" ipl_lun="${8}"
|
||||||
|
local -a records
|
||||||
|
local try_update_id_file=true
|
||||||
|
|
||||||
|
[ "${ipl_type}" = "fcp" ] || return 1
|
||||||
|
|
||||||
|
[[ "${sdev_busid}" =~ ^[[:xdigit:]]{1,3}\.[[:xdigit:]]\.[[:xdigit:]]{1,4}$ ]] \
|
||||||
|
|| return 2
|
||||||
|
[[ "${sdev_wwpn}" =~ ^0x[[:xdigit:]]{16}$ ]] || return 3
|
||||||
|
[[ "${sdev_lun}" =~ ^0x[[:xdigit:]]{16}$ ]] || return 4
|
||||||
|
|
||||||
|
# After updating the firmware re-IPL information below we also try to
|
||||||
|
# update the information stored in the ID file (necessary, so it
|
||||||
|
# contains the correct Device-Bus-ID/WWPN/LUN after the update). For
|
||||||
|
# the update of the ID file we try to grab an exclusive lock, so there
|
||||||
|
# are no overlapping reads/writes.
|
||||||
|
#
|
||||||
|
# In case we can't get the lock because the ID file is missing, but we
|
||||||
|
# have a direct TGT match, we may still try to change the re-IPL
|
||||||
|
# information, but skip the ID file update.
|
||||||
|
#
|
||||||
|
# "direct match" means, the event subject is either the SDEV that is
|
||||||
|
# currently set as re-IPL target, or it is the dm-multipath device that
|
||||||
|
# currently contains the re-IPL target.
|
||||||
|
if ! id_file_lock_exclusive_no_create; then
|
||||||
|
# rc == 1 --> could not read ${ID_FILE}
|
||||||
|
[ "${PIPESTATUS[0]}" -eq 1 ] || return 5
|
||||||
|
# if true, we are dealing with a direct TGT match
|
||||||
|
[ "${CHREIPL_FCP_MPATH_IS_TGT}" = "true" ] || return 6
|
||||||
|
try_update_id_file=false
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If we have a direct match (see in the comment above), we know
|
||||||
|
# that we have a path to the current re-IPL volume - no matter of the
|
||||||
|
# WWID. Otherwise, we got here by comparing the WWID of the event
|
||||||
|
# subject with the one recorded in the ID file; in this case we try to
|
||||||
|
# make sure the information is still up-to-date.
|
||||||
|
if [ "${CHREIPL_FCP_MPATH_IS_TGT}" != "true" ]; then
|
||||||
|
# last bail to make sure we don't overwrite user choices..
|
||||||
|
#
|
||||||
|
# XXX: this will *NOT* prevent the race completely, but at least
|
||||||
|
# make it less likely
|
||||||
|
{ readarray -d "" -t -u "${ID_FILE_LOCK}" records; } 2>/dev/null \
|
||||||
|
|| return 7
|
||||||
|
if '@DEBUG@'; then declare -p records 1>&2; fi
|
||||||
|
|
||||||
|
[ "${#records[@]}" = "4" ] || return 8
|
||||||
|
[ "${records[0]}" = "${sdev_wwid}" ] || return 9
|
||||||
|
[ "${records[1]}" = "${ipl_busid}" ] || return 10
|
||||||
|
[ "${records[2]}" = "${ipl_wwpn}" ] || return 11
|
||||||
|
[ "${records[3]}" = "${ipl_lun}" ] || return 12
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Take lock so we don't see any intermediate state from other helpers
|
||||||
|
# running in parallel
|
||||||
|
firmware_lock_exclusive || return 13
|
||||||
|
|
||||||
|
if ! { echo "${sdev_busid}" >| /sys/firmware/reipl/fcp/device \
|
||||||
|
&& echo "${sdev_wwpn}" >| /sys/firmware/reipl/fcp/wwpn \
|
||||||
|
&& echo "${sdev_lun}" >| /sys/firmware/reipl/fcp/lun; };
|
||||||
|
then
|
||||||
|
log_alert "Changing the re-IPL device failed. The current re-IPL settings might be inconsistent. Check and correct the settings (see the README.md of chreipl-fcp-mpath) to make sure that the current re-IPL device is valid."
|
||||||
|
return 14
|
||||||
|
fi
|
||||||
|
|
||||||
|
firmware_unlock_exclusive
|
||||||
|
|
||||||
|
if [ "${sdev_busid}" != "${ipl_busid}" ] \
|
||||||
|
|| [ "${sdev_wwpn}" != "${ipl_wwpn}" ] \
|
||||||
|
|| [ "${sdev_lun}" != "${ipl_lun}" ]; then
|
||||||
|
log_note "Changed re-IPL path to: ${sdev_busid}:${sdev_wwpn}:${sdev_lun}."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Try to update the information in the ID file if we have gotten the
|
||||||
|
# lock for it.
|
||||||
|
if ${try_update_id_file}; then
|
||||||
|
# reset ID without removing the file
|
||||||
|
truncate --no-create --size=0 "${ID_FILE}" || return 15
|
||||||
|
echo -ne "${sdev_wwid}\x00${sdev_busid}\x00${sdev_wwpn}\x00${sdev_lun}\x00" \
|
||||||
|
>>"${ID_FILE}" || return 16
|
||||||
|
|
||||||
|
id_file_unlock_exclusive_no_create
|
||||||
|
|
||||||
|
if '@DEBUG@'; then hexdump -vC "${ID_FILE}" 1>&2; fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
declare -g SDEV=""
|
||||||
|
if [[ "${DM_UUID}" == mpath-* ]]; then
|
||||||
|
# Assume Multipath Device Mapper Device;
|
||||||
|
# e.g.: DEVPATH = /devices/virtual/block/dm-0
|
||||||
|
|
||||||
|
for sdev in /sys/"${DEVPATH}"/slaves/sd*/device; do
|
||||||
|
if sdev_test_path_state "${sdev}"; then
|
||||||
|
SDEV="${sdev}"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# No path of the multipath-device that represents the IPL volume is
|
||||||
|
# online.
|
||||||
|
if [ "${SDEV}" = "" ]; then
|
||||||
|
log_crit "The re-IPL device cannot be changed because no operational path to the re-IPL volume remains. The next re-IPL might fail unless you re-attach or enable at least one valid path to the re-IPL volume."
|
||||||
|
fi
|
||||||
|
|
||||||
|
elif [ "${SUBSYSTEM}" = block ]; then
|
||||||
|
# Assume SCSI Disk;
|
||||||
|
# e.g.: DEVPATH = /devices/css0/0.0.0014/0.0.1700/host0/rport-0:0-0/target0:0:0/0:0:0:1074806808/block/sds
|
||||||
|
if sdev_test_path_state /sys/"${DEVPATH}"/device; then
|
||||||
|
SDEV=/sys/"${DEVPATH}"/device
|
||||||
|
fi
|
||||||
|
|
||||||
|
fi
|
||||||
|
[ "${SDEV}" != "" ] || exit 0
|
||||||
|
|
||||||
|
sdev_get_wwid "${SDEV}" || exit 0
|
||||||
|
sdev_get_fcp_addressing "${SDEV}" || exit 0
|
||||||
|
firmware_get_ipl_information || exit 0
|
||||||
|
|
||||||
|
# shellcheck disable=SC2153
|
||||||
|
apply_ipl_information \
|
||||||
|
"${SDEV_WWID}" "${SDEV_BUSID}" "${SDEV_WWPN}" "${SDEV_LUN}" \
|
||||||
|
"${IPL_TYPE}" "${IPL_BUSID}" "${IPL_WWPN}" "${IPL_LUN}" || exit 0
|
||||||
|
|
||||||
|
exit 0
|
||||||
@@ -0,0 +1,183 @@
|
|||||||
|
.\" Automatically generated by Pandoc 2.16.1
|
||||||
|
.\"
|
||||||
|
.TH "CHREIPL-FCP-MPATH" "7" "2022-02-24" "s390-tools 2.20.0-build-20220224" "Administrator Manual"
|
||||||
|
.hy
|
||||||
|
.SH NAME
|
||||||
|
.PP
|
||||||
|
chreipl-fcp-mpath - use multipath information for re-IPL path failover
|
||||||
|
on a running Linux instance
|
||||||
|
.SH DESCRIPTION
|
||||||
|
.PP
|
||||||
|
The IPL process of Linux on Z or LinuxONE from an FCP-attached SCSI
|
||||||
|
volume uses exactly one path to the volume.
|
||||||
|
If this path is unavailable, the IPL fails.
|
||||||
|
.PP
|
||||||
|
The \f[B]chreipl-fcp-mpath\f[R] toolset monitors \f[B]udev\f[R] events
|
||||||
|
about paths to the re-IPL volume.
|
||||||
|
If the currently configured re-IPL path becomes unavailable, the toolset
|
||||||
|
checks for operational paths to the same volume.
|
||||||
|
If available, it reconfigures the re-IPL settings to use an operational
|
||||||
|
path.
|
||||||
|
.PP
|
||||||
|
Thus, re-IPL from an FCP-attached SCSI volume can be successful despite
|
||||||
|
path failures on a running Linux instance if at least one path to the
|
||||||
|
re-IPL volume remains operational.
|
||||||
|
.PP
|
||||||
|
\f[B]Chreipl-fcp-mpath\f[R] requires \f[B]udev\f[R],
|
||||||
|
\f[B]multipathd\f[R] and \f[B]dm-multipath\f[R].
|
||||||
|
Once installed, the toolset runs automatically and autonomously.
|
||||||
|
No user intervention is possible or required.
|
||||||
|
.PP
|
||||||
|
Other than installing the toolset, there is no user interface for
|
||||||
|
\f[B]chreipl-fcp-mpath\f[R].
|
||||||
|
.SS Requirements
|
||||||
|
.PP
|
||||||
|
The \f[B]chreipl-fcp-mpath\f[R] tool has the following requirements on
|
||||||
|
the Linux instance that is being monitored:
|
||||||
|
.IP \[bu] 2
|
||||||
|
The Linux instance must have started successfully, during IPL.
|
||||||
|
.IP \[bu] 2
|
||||||
|
The running Linux instance must use \f[B]dm-multipath\f[R] and
|
||||||
|
\f[B]multipathd\f[R] for the configured re-IPL volume - a volume that
|
||||||
|
contains a zipl boot record and has one of its paths used in the re-IPL
|
||||||
|
configuration.
|
||||||
|
.IP \[bu] 2
|
||||||
|
\f[B]udev\f[R] must run.
|
||||||
|
.IP \[bu] 2
|
||||||
|
The toolset must observe at least one event about the configured re-IPL
|
||||||
|
path.
|
||||||
|
Examples for such events are: the SCSI disk comes online, or a path of
|
||||||
|
the corresponding multipath device goes down or comes back online.
|
||||||
|
.RS 2
|
||||||
|
.IP \[bu] 2
|
||||||
|
The WWID of the re-IPL volume must not change while the Linux instance
|
||||||
|
is running.
|
||||||
|
.RE
|
||||||
|
.IP \[bu] 2
|
||||||
|
When the configured re-IPL path becomes unavailable while the Linux
|
||||||
|
instance is running, at least one operational path to the re-IPL volume
|
||||||
|
must be available, or must become available.
|
||||||
|
If no such path is available when the Linux instance is rebooted, the
|
||||||
|
re-IPL path is not changed.
|
||||||
|
.IP \[bu] 2
|
||||||
|
The tool assumes that any manually reconfigured re-IPL device is valid
|
||||||
|
and operational.
|
||||||
|
.RS 2
|
||||||
|
.PP
|
||||||
|
The tool treats a newly configured re-IPL device like the initially
|
||||||
|
configured re-IPL device.
|
||||||
|
In particular, if the newly configured re-IPL device fulfills the
|
||||||
|
requirements of the tool, re-IPL path failover takes place if the
|
||||||
|
configured re-IPL path becomes unavailable.
|
||||||
|
.RE
|
||||||
|
.SS Caution with Manual Changes to the Configured re-IPL Target
|
||||||
|
.PP
|
||||||
|
\f[B]chreipl-fcp-mpath\f[R] is designed to accept operator-inititated
|
||||||
|
changes of the re-IPL device.
|
||||||
|
However, concurrent changes by the operator and tool driven changes can
|
||||||
|
result in the operator change being overwritten.
|
||||||
|
.PP
|
||||||
|
To avoid this problem, change the re-IPL device only during steady-state
|
||||||
|
operations, when no path events happen.
|
||||||
|
Alternatively, make sure that no events are processed while you change
|
||||||
|
the device.
|
||||||
|
See EXAMPLES for one way to suspend event processing.
|
||||||
|
.SH MESSAGES
|
||||||
|
.PP
|
||||||
|
During monitoring and event processing, \f[B]chreipl-fcp-mpath\f[R]
|
||||||
|
writes messages to the syslog.
|
||||||
|
.PP
|
||||||
|
When the configured re-IPL path is changed to a different path to the
|
||||||
|
same volume (priority \f[I]daemon.notice\f[R]):
|
||||||
|
.RS
|
||||||
|
.PP
|
||||||
|
Changed re-IPL path to: <device-bus-id>:<wwpn>:<lun>.
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
When a path event indicates that the last available path has become
|
||||||
|
non-operational (priority \f[I]daemon.alert\f[R]):
|
||||||
|
.RS
|
||||||
|
.PP
|
||||||
|
The re-IPL device cannot be changed because no operational path to the
|
||||||
|
re-IPL volume remains.
|
||||||
|
The next re-IPL might fail unless you re-attach or enable at least one
|
||||||
|
valid path to the re-IPL volume.
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
When changing the configured re-IPL device failed because of an error
|
||||||
|
with the used Linux kernel interface (priority \f[I]daemon.crit\f[R]):
|
||||||
|
.RS
|
||||||
|
.PP
|
||||||
|
Changing the re-IPL device failed.
|
||||||
|
The current re-IPL settings might be inconsistent.
|
||||||
|
Check and correct the settings (see the README.md of chreipl-fcp-mpath)
|
||||||
|
to make sure that the current re-IPL device is valid.
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
A failure to change the re-IPL device can indicate an inconsistent
|
||||||
|
setting that cannot be corrected automatically by
|
||||||
|
\f[B]chreipl-fcp-mpath\f[R].
|
||||||
|
As a result, the next re-IPL might fail or might not use the intended
|
||||||
|
re-IPL device.
|
||||||
|
.PP
|
||||||
|
You can use the following tools to check and correct the current
|
||||||
|
settings:
|
||||||
|
.IP \[bu] 2
|
||||||
|
\f[B]lsreipl\f[R] to confirm that the intended re-IPL device is
|
||||||
|
configured;
|
||||||
|
.IP \[bu] 2
|
||||||
|
\f[B]chreipl\f[R] to change the re-IPL device;
|
||||||
|
.IP \[bu] 2
|
||||||
|
\f[B]lszfcp\f[R] to inspect the state of available paths to the re-IPL
|
||||||
|
device.
|
||||||
|
.SH EXAMPLES
|
||||||
|
.SS Manual Changes to the Configured re-IPL Device
|
||||||
|
.PP
|
||||||
|
As outlined in DESCRIPTION, be cautious when manually changing the
|
||||||
|
configured re-IPL device.
|
||||||
|
Assure that your reconfiguration actions do not collide with concurrent
|
||||||
|
automatic event processing by \f[B]chreipl-fcp-mpath\f[R].
|
||||||
|
You can avoid such collisions, by stopping event processing, making your
|
||||||
|
changes, and then re-enabling event processing.
|
||||||
|
You need \f[I]root\f[R] privileges for running the commands in the
|
||||||
|
following example:
|
||||||
|
.IP
|
||||||
|
.nf
|
||||||
|
\f[C]
|
||||||
|
\[ti] # udevadm settle
|
||||||
|
\[ti] # udevadm control --stop-exec-queue
|
||||||
|
\[ti] # chreipl ...
|
||||||
|
\[ti] # udevadm control --start-exec-queue
|
||||||
|
\f[R]
|
||||||
|
.fi
|
||||||
|
.SS Listing messages with journalctl
|
||||||
|
.PP
|
||||||
|
If your Linux instance includes \f[B]journalctl\f[R], use the following
|
||||||
|
command to list all messages that are issued by
|
||||||
|
\f[B]chreipl-fcp-mpath\f[R]:
|
||||||
|
.IP
|
||||||
|
.nf
|
||||||
|
\f[C]
|
||||||
|
\[ti] # journalctl -t chreipl-fcp-mpath
|
||||||
|
\f[R]
|
||||||
|
.fi
|
||||||
|
.PP
|
||||||
|
To list only messages that were issued since the last IPL, use this
|
||||||
|
command:
|
||||||
|
.IP
|
||||||
|
.nf
|
||||||
|
\f[C]
|
||||||
|
\[ti] # journalctl -t chreipl-fcp-mpath -b
|
||||||
|
\f[R]
|
||||||
|
.fi
|
||||||
|
.SH REPORTING BUGS
|
||||||
|
.PP
|
||||||
|
Use the \f[B]Issues\f[R] functionality on GitHub to report any bugs in
|
||||||
|
\f[B]chreipl-fcp-mpath\f[R]: s390-tools
|
||||||
|
Issues (https://github.com/ibm-s390-linux/s390-tools/issues).
|
||||||
|
.SH SEE ALSO
|
||||||
|
.PP
|
||||||
|
\f[B]chreipl\f[R](8), \f[B]dracut\f[R](8), \f[B]journalctl\f[R](1),
|
||||||
|
\f[B]lsreipl(8)\f[R], \f[B]lszfcp\f[R](8), \f[B]multipath\f[R](8),
|
||||||
|
\f[B]multipathd\f[R](8), \f[B]udev\f[R](7), \f[B]udevadm\f[R](8),
|
||||||
|
\f[B]zipl\f[R](8)
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
#
|
||||||
|
# chreipl-fcp-mpath: use multipath information to change FCP IPL target
|
||||||
|
# (C) Copyright IBM Corp. 2021
|
||||||
|
#
|
||||||
|
# Uses the following system-utilities (and shell-builtins):
|
||||||
|
# Utilities list in GNU Make Conventions:
|
||||||
|
# https://www.gnu.org/software/make/manual/make.html#Utilities-in-Makefiles
|
||||||
|
# GNU coreutils:
|
||||||
|
# - mktemp
|
||||||
|
# If $(ENABLE_DOC) is `1`:
|
||||||
|
# Pandoc:
|
||||||
|
# - pandoc
|
||||||
|
# GNU coreutils:
|
||||||
|
# - date
|
||||||
|
|
||||||
|
#
|
||||||
|
## Paths and Build Variables
|
||||||
|
#
|
||||||
|
|
||||||
|
# Install the configuration file for dracut, to automatically pull in the
|
||||||
|
# toolset into the initial ramdisk, when built with it.
|
||||||
|
HAVE_DRACUT = 0
|
||||||
|
|
||||||
|
# Build documentation; requires: Pandoc
|
||||||
|
ENABLE_DOC = 0
|
||||||
|
|
||||||
|
# https://www.gnu.org/software/make/manual/make.html#Directory-Variables
|
||||||
|
CHREIPLZFCPMPDIR = $(USRLIBDIR)/chreipl-fcp-mpath
|
||||||
|
UDEVRUNDIR = /run/udev
|
||||||
|
DEBUGOUTDIR = $(UDEVRUNDIR)
|
||||||
|
|
||||||
|
INSTALL_EXEC = $(INSTALL) -g $(GROUP) -o $(OWNER) --preserve-timestamps
|
||||||
|
INSTALL_DATA = $(INSTALL_EXEC) --mode=0644
|
||||||
|
|
||||||
|
# used for data exchange and synchronization across the different helpers
|
||||||
|
chreiplzfcpmp-id-file = $(UDEVRUNDIR)/chreiplzfcpmp-ipl-volume-id
|
||||||
|
# file used to implement mutual exclusion when accessing firmware IPL info:
|
||||||
|
# - this should be something that is (practically) always available, so we
|
||||||
|
# dont have to worry about fallbacks or error-handling;
|
||||||
|
# - at the same time, it should not be used by anything else with flock(2) to
|
||||||
|
# hold a lock for long periods.
|
||||||
|
chreiplzfcpmp-fwlock-file = /sys/firmware/reipl
|
||||||
|
|
||||||
|
.DELETE_ON_ERROR:
|
||||||
|
|
||||||
|
# export build-time definitions to the scripts/built-components
|
||||||
|
define chreiplzfcpmp-sed-buildvar-replace =
|
||||||
|
tmpout=$$(mktemp -p ./ .make.tmp.XXXXXXXXXXXXXXXX) && { \
|
||||||
|
$(SED) -E \
|
||||||
|
-e 's|@DEBUG@|$(if $(filter 1,$(D)),true,false)|g' \
|
||||||
|
-e 's|@chreiplzfcpmp-id-file@|$(chreiplzfcpmp-id-file)|g' \
|
||||||
|
-e 's|@chreiplzfcpmp-fwlock-file@|$(chreiplzfcpmp-fwlock-file)|g' \
|
||||||
|
-e 's|@chreiplzfcpmp-lib@|$(CHREIPLZFCPMPDIR)/chreipl-fcp-mpath-common.sh|g' \
|
||||||
|
-e 's|@debugoutdir@|$(DEBUGOUTDIR)|g' \
|
||||||
|
-e 's|@udevdir@|$(UDEVDIR)|g' \
|
||||||
|
-e 's|@udevrulesdir@|$(UDEVRULESDIR)|g' \
|
||||||
|
$(1) > $${tmpout} \
|
||||||
|
&& mv $${tmpout} $(2) \
|
||||||
|
|| { rm $${tmpout}; false; } \
|
||||||
|
; }
|
||||||
|
endef
|
||||||
|
|
||||||
|
.PHONY: clean-mk-temp
|
||||||
|
clean: clean-mk-temp
|
||||||
|
clean-mk-temp:
|
||||||
|
rm -f .make.tmp.[[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]]
|
||||||
|
|
||||||
|
# Definitions for generating documentation when $(ENABLE_DOC) is set to `1`
|
||||||
|
|
||||||
|
PANDOCFLAGS = --fail-if-warnings
|
||||||
|
ALL_PANDOCFLAGS = --preserve-tabs --tab-stop=8 --strip-comments \
|
||||||
|
--standalone --self-contained \
|
||||||
|
-M date="$(shell date +'%Y-%m-%d')" \
|
||||||
|
$(PANDOCFLAGS)
|
||||||
|
|
||||||
|
$(eval $(call cmd_define, PANDOC," PANDOC ",pandoc))
|
||||||
|
|
||||||
|
%.html : ALL_PANDOCFLAGS += -t html
|
||||||
|
%.html : %.md
|
||||||
|
$(PANDOC) $(ALL_PANDOCFLAGS) -f gfm -o $(@) $(<)
|
||||||
|
|
||||||
|
%.pdf : ALL_PANDOCFLAGS += -t latex --toc
|
||||||
|
%.pdf : %.md
|
||||||
|
$(PANDOC) $(ALL_PANDOCFLAGS) -f gfm -o $(@) $(<)
|
||||||
|
|
||||||
|
%.7 : ALL_PANDOCFLAGS += -t man
|
||||||
|
%.7 : %.md
|
||||||
|
$(PANDOC) $(ALL_PANDOCFLAGS) -f gfm -o $(@) $(<)
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
#
|
||||||
|
# chreipl-fcp-mpath: use multipath information to change FCP IPL target
|
||||||
|
# (C) Copyright IBM Corp. 2021
|
||||||
|
|
||||||
|
add_dracutmodules+=" bash multipath udev-rules "
|
||||||
|
|
||||||
|
install_items+=" @udevrulesdir@/70-chreipl-fcp-mpath.rules "
|
||||||
|
install_items+=" @chreiplzfcpmp-lib@ "
|
||||||
|
install_items+=" @udevdir@/chreipl-fcp-mpath-is-ipl-tgt "
|
||||||
|
install_items+=" @udevdir@/chreipl-fcp-mpath-is-ipl-vol "
|
||||||
|
install_items+=" @udevdir@/chreipl-fcp-mpath-is-reipl-zfcp "
|
||||||
|
install_items+=" @udevdir@/chreipl-fcp-mpath-record-volume-identifier "
|
||||||
|
install_items+=" @udevdir@/chreipl-fcp-mpath-try-change-ipl-path "
|
||||||
|
|
||||||
|
install_items+=" flock hexdump logger mktemp readlink sync truncate "
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
#
|
||||||
|
# chreipl-fcp-mpath: use multipath information to change FCP IPL target
|
||||||
|
# (C) Copyright IBM Corp. 2021
|
||||||
|
|
||||||
|
# Did the event affect a multipath or scsi disk device?
|
||||||
|
ACTION=="change", KERNEL=="dm-[0-9]*", SUBSYSTEM=="block", \
|
||||||
|
ENV{DM_UUID}=="mpath-*", ENV{DM_ACTION}=="PATH_FAILED", \
|
||||||
|
GOTO="chreipl_fcp_mpath_path_change"
|
||||||
|
ACTION=="change", KERNEL=="dm-[0-9]*", SUBSYSTEM=="block", \
|
||||||
|
ENV{DM_UUID}=="mpath-*", ENV{DM_ACTION}=="PATH_REINSTATED", \
|
||||||
|
GOTO="chreipl_fcp_mpath_path_change"
|
||||||
|
ACTION=="add", KERNEL=="sd[a-z]*", SUBSYSTEM=="block", \
|
||||||
|
GOTO="chreipl_fcp_mpath_path_change"
|
||||||
|
GOTO="chreipl_fcp_mpath_end"
|
||||||
|
LABEL="chreipl_fcp_mpath_path_change"
|
||||||
|
|
||||||
|
# Is this system IPL'ed (IOW, are we on s390x)? And do we ReIPL via zFCP?
|
||||||
|
#
|
||||||
|
# udev(7): If no absolute path is given, the program is expected to live
|
||||||
|
# in /usr/lib/udev; otherwise, the absolute path must be
|
||||||
|
# specified.
|
||||||
|
TEST!="/sys/firmware/ipl", GOTO="chreipl_fcp_mpath_end"
|
||||||
|
PROGRAM!="chreipl-fcp-mpath-is-reipl-zfcp", GOTO="chreipl_fcp_mpath_end"
|
||||||
|
|
||||||
|
# Consider the following scenarios.
|
||||||
|
# Either:
|
||||||
|
#
|
||||||
|
# (A) We recognized a new SCSI Disk. This might represent:
|
||||||
|
# (a) the path we want to ReIPL from;
|
||||||
|
# (b) an alternative path to the volume we want to ReIPL from;
|
||||||
|
# (c) a path to some unrelated volume.
|
||||||
|
#
|
||||||
|
# Or:
|
||||||
|
#
|
||||||
|
# (B) We recognized a PATH_ event for a multipath device. This might represent:
|
||||||
|
# the path we want to ReIPL from:
|
||||||
|
# (a) went away;
|
||||||
|
# (b) came back online;
|
||||||
|
# an alternative path to the volume we want to ReIPL from:
|
||||||
|
# (c) went away;
|
||||||
|
# (d) came back online;
|
||||||
|
# (e) some unrelated multipath device saw an event.
|
||||||
|
|
||||||
|
# Test whether the affected device is, or contains, the current IPL target.
|
||||||
|
#
|
||||||
|
# This covers scenarios:
|
||||||
|
# (A) (a),
|
||||||
|
# (B) (a)/(b)/(c)/(d)
|
||||||
|
PROGRAM!="chreipl-fcp-mpath-is-ipl-tgt", \
|
||||||
|
ENV{CHREIPL_FCP_MPATH_IS_TGT}="false", \
|
||||||
|
GOTO="chreipl_fcp_mpath_not_direct_match"
|
||||||
|
ENV{CHREIPL_FCP_MPATH_IS_TGT}="true"
|
||||||
|
|
||||||
|
# Record the WWID, Device-Bus-ID, Remote WWPN, and LUN of the ReIPL target
|
||||||
|
# (see `chreipl-fcp-mpath-is-ipl-vol` for usecases). This information
|
||||||
|
# might change, depending on whether the machine operator changes the ReIPL
|
||||||
|
# target to a different volume.
|
||||||
|
#
|
||||||
|
# XXX: Because the kernel doesn't generate any events upon changing of
|
||||||
|
# the ReIPL target, the chreipl-fcp-mpath toolset can't take any
|
||||||
|
# actions until the next path event for the new target is
|
||||||
|
# generated. Following that, we assume that when the machine
|
||||||
|
# operator changes the ReIPL target, the new target is reachable
|
||||||
|
# and in a good state at this point in time.
|
||||||
|
PROGRAM!="chreipl-fcp-mpath-record-volume-identifier", \
|
||||||
|
GOTO="chreipl_fcp_mpath_try_change_ipl_path"
|
||||||
|
GOTO="chreipl_fcp_mpath_try_change_ipl_path"
|
||||||
|
|
||||||
|
# If the even subject is not a direct match (not the sdev that is the current
|
||||||
|
# ReIPL target, and not a mpath device that contains the current ReIPL target)
|
||||||
|
LABEL="chreipl_fcp_mpath_not_direct_match"
|
||||||
|
|
||||||
|
# While this sdev/mpath device doesn't directly correspond to the path
|
||||||
|
# currently set as ReIPL target, it might still point to the same volume.
|
||||||
|
#
|
||||||
|
# For mpath devices this can happen if the original ReIPL target is completely
|
||||||
|
# gone from the machine, and so there is no way we can successfully, directly
|
||||||
|
# compare the ReIPL parameters to the sdevs of the mpath device.
|
||||||
|
#
|
||||||
|
# For cases like these we recorded the volume identifier, which we now can
|
||||||
|
# compare, and so still decide whether we are addressing the correct volume.
|
||||||
|
#
|
||||||
|
# This covers scenarios:
|
||||||
|
# (A) (b)/(c),
|
||||||
|
# (B) (c)/(d)/(e)
|
||||||
|
#
|
||||||
|
# XXX: we recorded WWID, Device-Bus-ID, Remote WWPN, LUN of the ReIPL target at
|
||||||
|
# the time; if the latter three don't match the current ReIPL setting
|
||||||
|
# anymore, we have to assume that someone changed the ReIPL target
|
||||||
|
# manually, and we cannot use the WWID anymore since we can't possibly
|
||||||
|
# know whether that stayed the same when the change was done.
|
||||||
|
PROGRAM!="chreipl-fcp-mpath-is-ipl-vol", GOTO="chreipl_fcp_mpath_end"
|
||||||
|
|
||||||
|
# We are here because of scenarios:
|
||||||
|
# (A) (a)/(b),
|
||||||
|
# (B) (a)/(b)/(c)/(d)
|
||||||
|
LABEL="chreipl_fcp_mpath_try_change_ipl_path"
|
||||||
|
RUN{program}+="chreipl-fcp-mpath-try-change-ipl-path"
|
||||||
|
|
||||||
|
LABEL="chreipl_fcp_mpath_end"
|
||||||
+7
-6
@@ -16,17 +16,18 @@ check_dep:
|
|||||||
$(call check_dep, \
|
$(call check_dep, \
|
||||||
"cmsfs-fuse", \
|
"cmsfs-fuse", \
|
||||||
"fuse.h", \
|
"fuse.h", \
|
||||||
"fuse-devel or libfuse-dev", \
|
"fuse3-devel or libfuse3-dev", \
|
||||||
"HAVE_FUSE=0")
|
"HAVE_FUSE=0", \
|
||||||
|
"-DFUSE_USE_VERSION=30")
|
||||||
|
|
||||||
all: check_dep cmsfs-fuse
|
all: check_dep cmsfs-fuse
|
||||||
|
|
||||||
ifneq ($(shell sh -c 'command -v pkg-config'),)
|
ifneq ($(shell sh -c 'command -v pkg-config'),)
|
||||||
FUSE_CFLAGS = $(shell pkg-config --silence-errors --cflags fuse)
|
FUSE_CFLAGS = $(shell pkg-config --silence-errors --cflags fuse3)
|
||||||
FUSE_LDLIBS = $(shell pkg-config --silence-errors --libs fuse)
|
FUSE_LDLIBS = $(shell pkg-config --silence-errors --libs fuse3)
|
||||||
else
|
else
|
||||||
FUSE_CFLAGS = -D_FILE_OFFSET_BITS=64 -I/usr/include/fuse
|
FUSE_CFLAGS = -D_FILE_OFFSET_BITS=64 -I/usr/include/fuse3
|
||||||
FUSE_LDLIBS = -lfuse
|
FUSE_LDLIBS = -lfuse3
|
||||||
endif
|
endif
|
||||||
ALL_CFLAGS += -DHAVE_SETXATTR $(FUSE_CFLAGS)
|
ALL_CFLAGS += -DHAVE_SETXATTR $(FUSE_CFLAGS)
|
||||||
LDLIBS += $(FUSE_LDLIBS) -lm
|
LDLIBS += $(FUSE_LDLIBS) -lm
|
||||||
|
|||||||
@@ -80,9 +80,6 @@ Allow access by other users
|
|||||||
\fB\-o\fR allow_root
|
\fB\-o\fR allow_root
|
||||||
Allow access by root
|
Allow access by root
|
||||||
.TP
|
.TP
|
||||||
\fB\-o\fR nonempty
|
|
||||||
Allow mounts over non\-empty file/dir
|
|
||||||
.TP
|
|
||||||
\fB\-o\fR default_permissions
|
\fB\-o\fR default_permissions
|
||||||
Enable permission checking by kernel
|
Enable permission checking by kernel
|
||||||
.TP
|
.TP
|
||||||
|
|||||||
+33
-11
@@ -9,7 +9,7 @@
|
|||||||
* it under the terms of the MIT license. See LICENSE for details.
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define FUSE_USE_VERSION 26
|
#define FUSE_USE_VERSION 30
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
@@ -1516,7 +1516,7 @@ static void walk_dir_block(struct fst_entry *fst, struct walk_file *walk,
|
|||||||
decode_edf_name(file, fst->name, fst->type);
|
decode_edf_name(file, fst->name, fst->type);
|
||||||
if (!file_unlinked(file)) {
|
if (!file_unlinked(file)) {
|
||||||
cache_fst_addr(walk->addr, file);
|
cache_fst_addr(walk->addr, file);
|
||||||
walk->filler(walk->buf, file, NULL, 0);
|
walk->filler(walk->buf, file, NULL, 0, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1728,8 +1728,11 @@ static off_t get_file_size_logical(struct fst_entry *fst)
|
|||||||
return total;
|
return total;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int cmsfs_getattr(const char *path, struct stat *stbuf)
|
static int cmsfs_getattr(const char *path, struct stat *stbuf,
|
||||||
|
struct fuse_file_info *fi)
|
||||||
{
|
{
|
||||||
|
(void) fi;
|
||||||
|
|
||||||
int mask = (cmsfs.allow_other) ? 0444 : 0440;
|
int mask = (cmsfs.allow_other) ? 0444 : 0440;
|
||||||
struct fst_entry fst;
|
struct fst_entry fst;
|
||||||
|
|
||||||
@@ -1783,13 +1786,15 @@ static int cmsfs_getattr(const char *path, struct stat *stbuf)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int cmsfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
|
static int cmsfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
|
||||||
off_t offset, struct fuse_file_info *fi)
|
off_t offset, struct fuse_file_info *fi,
|
||||||
|
enum fuse_readdir_flags flags)
|
||||||
{
|
{
|
||||||
struct walk_file walk;
|
struct walk_file walk;
|
||||||
struct fst_entry fst;
|
struct fst_entry fst;
|
||||||
|
|
||||||
(void) offset;
|
(void) offset;
|
||||||
(void) fi;
|
(void) fi;
|
||||||
|
(void) flags;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Offset is ignored and 0 passed to the filler fn so the whole
|
* Offset is ignored and 0 passed to the filler fn so the whole
|
||||||
@@ -1800,8 +1805,8 @@ static int cmsfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
|
|||||||
if (strcmp(path, "/") != 0)
|
if (strcmp(path, "/") != 0)
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
|
|
||||||
filler(buf, ".", NULL, 0);
|
filler(buf, ".", NULL, 0, 0);
|
||||||
filler(buf, "..", NULL, 0);
|
filler(buf, "..", NULL, 0, 0);
|
||||||
|
|
||||||
memset(&walk, 0, sizeof(walk));
|
memset(&walk, 0, sizeof(walk));
|
||||||
/* readdir is possible without open so fi->fh is not set */
|
/* readdir is possible without open so fi->fh is not set */
|
||||||
@@ -2683,13 +2688,16 @@ static int cmsfs_statfs(const char *path, struct statvfs *buf)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int cmsfs_utimens(const char *path, const struct timespec ts[2])
|
static int cmsfs_utimens(const char *path, const struct timespec ts[2],
|
||||||
|
struct fuse_file_info *fi)
|
||||||
{
|
{
|
||||||
struct fst_entry fst;
|
struct fst_entry fst;
|
||||||
off_t fst_addr;
|
off_t fst_addr;
|
||||||
struct tm tm;
|
struct tm tm;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
|
(void) fi;
|
||||||
|
|
||||||
if (cmsfs.readonly)
|
if (cmsfs.readonly)
|
||||||
return -EACCES;
|
return -EACCES;
|
||||||
|
|
||||||
@@ -2825,7 +2833,8 @@ error:
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int cmsfs_rename(const char *path, const char *new_path)
|
static int cmsfs_rename(const char *path, const char *new_path,
|
||||||
|
unsigned int flags)
|
||||||
{
|
{
|
||||||
struct fst_entry fst, fst_new;
|
struct fst_entry fst, fst_new;
|
||||||
off_t fst_addr, fst_addr_new;
|
off_t fst_addr, fst_addr_new;
|
||||||
@@ -2835,6 +2844,8 @@ static int cmsfs_rename(const char *path, const char *new_path)
|
|||||||
struct file *f;
|
struct file *f;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
|
(void) flags;
|
||||||
|
|
||||||
if (cmsfs.readonly)
|
if (cmsfs.readonly)
|
||||||
return -EACCES;
|
return -EACCES;
|
||||||
|
|
||||||
@@ -3232,13 +3243,16 @@ static void update_fst(struct file *f, off_t addr)
|
|||||||
unhide_null_blocks(f);
|
unhide_null_blocks(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int cmsfs_truncate(const char *path, off_t size)
|
static int cmsfs_truncate(const char *path, off_t size,
|
||||||
|
struct fuse_file_info *fi)
|
||||||
{
|
{
|
||||||
struct fst_entry fst;
|
struct fst_entry fst;
|
||||||
off_t fst_addr, len;
|
off_t fst_addr, len;
|
||||||
struct file *f;
|
struct file *f;
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
|
|
||||||
|
(void) fi;
|
||||||
|
|
||||||
if (cmsfs.readonly)
|
if (cmsfs.readonly)
|
||||||
return -EROFS;
|
return -EROFS;
|
||||||
|
|
||||||
@@ -4478,6 +4492,15 @@ static void destroy_file_object(struct file *f)
|
|||||||
free(f);
|
free(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void *cmsfs_oper_init(struct fuse_conn_info *conn, struct fuse_config *cfg)
|
||||||
|
{
|
||||||
|
(void)conn;
|
||||||
|
|
||||||
|
/* force immediate file removal */
|
||||||
|
cfg->hard_remove = 1;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static struct file_operations fops_fixed = {
|
static struct file_operations fops_fixed = {
|
||||||
.cache_data = cache_file_fixed,
|
.cache_data = cache_file_fixed,
|
||||||
.write_data = extend_block_fixed,
|
.write_data = extend_block_fixed,
|
||||||
@@ -4493,6 +4516,7 @@ static struct file_operations fops_variable = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static struct fuse_operations cmsfs_oper = {
|
static struct fuse_operations cmsfs_oper = {
|
||||||
|
.init = cmsfs_oper_init,
|
||||||
.getattr = cmsfs_getattr,
|
.getattr = cmsfs_getattr,
|
||||||
.statfs = cmsfs_statfs,
|
.statfs = cmsfs_statfs,
|
||||||
.readdir = cmsfs_readdir,
|
.readdir = cmsfs_readdir,
|
||||||
@@ -4669,8 +4693,6 @@ int main(int argc, char *argv[])
|
|||||||
fuse_opt_add_arg(&args, "-oro");
|
fuse_opt_add_arg(&args, "-oro");
|
||||||
/* force single threaded mode which requires no locking */
|
/* force single threaded mode which requires no locking */
|
||||||
fuse_opt_add_arg(&args, "-s");
|
fuse_opt_add_arg(&args, "-s");
|
||||||
/* force immediate file removal */
|
|
||||||
fuse_opt_add_arg(&args, "-ohard_remove");
|
|
||||||
|
|
||||||
if (cmsfs.mode == BINARY_MODE &&
|
if (cmsfs.mode == BINARY_MODE &&
|
||||||
(cmsfs.codepage_from != NULL || cmsfs.codepage_to != NULL))
|
(cmsfs.codepage_from != NULL || cmsfs.codepage_to != NULL))
|
||||||
|
|||||||
+49
-13
@@ -5,7 +5,7 @@ COMMON_INCLUDED = true
|
|||||||
# The variable "DISTRELEASE" should be overwritten in rpm spec files with:
|
# The variable "DISTRELEASE" should be overwritten in rpm spec files with:
|
||||||
# "make DISTRELEASE=%{release}" and "make install DISTRELEASE=%{release}"
|
# "make DISTRELEASE=%{release}" and "make install DISTRELEASE=%{release}"
|
||||||
VERSION = 2
|
VERSION = 2
|
||||||
RELEASE = 16
|
RELEASE = 23
|
||||||
PATCHLEVEL = 0
|
PATCHLEVEL = 0
|
||||||
DISTRELEASE = build-$(shell date +%Y%m%d)
|
DISTRELEASE = build-$(shell date +%Y%m%d)
|
||||||
S390_TOOLS_RELEASE = $(VERSION).$(RELEASE).$(PATCHLEVEL)-$(DISTRELEASE)
|
S390_TOOLS_RELEASE = $(VERSION).$(RELEASE).$(PATCHLEVEL)-$(DISTRELEASE)
|
||||||
@@ -59,11 +59,8 @@ define cmd_define
|
|||||||
endef
|
endef
|
||||||
|
|
||||||
$(eval $(call cmd_define, AS," AS ",$(CROSS_COMPILE)as))
|
$(eval $(call cmd_define, AS," AS ",$(CROSS_COMPILE)as))
|
||||||
$(eval $(call cmd_define, LINK," LINK ",$(CROSS_COMPILE)gcc))
|
|
||||||
$(eval $(call cmd_define, LD," LD ",$(CROSS_COMPILE)ld))
|
|
||||||
$(eval $(call cmd_define, CC," CC ",$(CROSS_COMPILE)gcc))
|
$(eval $(call cmd_define, CC," CC ",$(CROSS_COMPILE)gcc))
|
||||||
$(eval $(call cmd_define, HOSTCC," HOSTCC ",gcc))
|
$(eval $(call cmd_define, HOSTCC," HOSTCC ",gcc))
|
||||||
$(eval $(call cmd_define, LINKXX," LINKXX ",$(CROSS_COMPILE)g++))
|
|
||||||
$(eval $(call cmd_define, CXX," CXX ",$(CROSS_COMPILE)g++))
|
$(eval $(call cmd_define, CXX," CXX ",$(CROSS_COMPILE)g++))
|
||||||
$(eval $(call cmd_define, CPP," CPP ",$(CROSS_COMPILE)gcc -E))
|
$(eval $(call cmd_define, CPP," CPP ",$(CROSS_COMPILE)gcc -E))
|
||||||
$(eval $(call cmd_define, AR," AR ",$(CROSS_COMPILE)ar))
|
$(eval $(call cmd_define, AR," AR ",$(CROSS_COMPILE)ar))
|
||||||
@@ -84,6 +81,8 @@ CHECKTOOL = $(call echocmd," CHECK ",/$@)$(CHECK_SILENT)
|
|||||||
SKIP = echo " SKIP $(call reldir) due to"
|
SKIP = echo " SKIP $(call reldir) due to"
|
||||||
|
|
||||||
INSTALL = install
|
INSTALL = install
|
||||||
|
LINK = $(CC)
|
||||||
|
LINKXX = $(CXX)
|
||||||
CP = cp
|
CP = cp
|
||||||
ifneq ("${V}","1")
|
ifneq ("${V}","1")
|
||||||
MAKEFLAGS += --quiet
|
MAKEFLAGS += --quiet
|
||||||
@@ -92,13 +91,12 @@ ifneq ("${V}","1")
|
|||||||
else
|
else
|
||||||
echocmd=
|
echocmd=
|
||||||
endif
|
endif
|
||||||
|
DEFAULT_CFLAGS = -g -fstack-protector-all -W -Wall -Wformat-security
|
||||||
ifeq ("${W}","1")
|
ifeq ("${W}","1")
|
||||||
DEFAULT_CFLAGS = -g -rdynamic -fstack-protector-all -W -Wall -Wformat-security -Wextra
|
DEFAULT_CFLAGS += -Wextra -Wshadow -Wundef -Wuninitialized -Wdouble-promotion -Wconversion
|
||||||
else
|
|
||||||
DEFAULT_CFLAGS = -g -rdynamic -fstack-protector-all -W -Wall -Wformat-security
|
|
||||||
endif
|
endif
|
||||||
ifeq ("${D}","1")
|
ifeq ("${D}","1")
|
||||||
DEFAULT_CFLAGS += -Og
|
DEFAULT_CFLAGS += -Og -g3 -ggdb3
|
||||||
else
|
else
|
||||||
DEFAULT_CFLAGS += -O3
|
DEFAULT_CFLAGS += -O3
|
||||||
endif
|
endif
|
||||||
@@ -111,6 +109,20 @@ ifeq ("${ASAN}","1")
|
|||||||
DEFAULT_LDFLAGS += -fsanitize=address
|
DEFAULT_LDFLAGS += -fsanitize=address
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
#
|
||||||
|
# Check for header prerequisite
|
||||||
|
#
|
||||||
|
# $1: Name of include file to check
|
||||||
|
# $2: Additional compiler & linker options (optional)
|
||||||
|
#
|
||||||
|
# Returns "yes" on success and nothing otherwise
|
||||||
|
#
|
||||||
|
define check_header_prereq
|
||||||
|
$(shell printf "#include <%s>\n int main(void) {return 0;}\n" $1 | \
|
||||||
|
( $(CC) $(filter-out --coverage, $(ALL_CFLAGS)) $(ALL_CPPFLAGS) \
|
||||||
|
$2 -o /dev/null -xc - ) >/dev/null 2>&1 && echo -n yes)
|
||||||
|
endef
|
||||||
|
|
||||||
#
|
#
|
||||||
# Check for build dependency
|
# Check for build dependency
|
||||||
#
|
#
|
||||||
@@ -121,7 +133,7 @@ endif
|
|||||||
# $5: Additional compiler & linker options (optional)
|
# $5: Additional compiler & linker options (optional)
|
||||||
#
|
#
|
||||||
check_dep=\
|
check_dep=\
|
||||||
printf "\#include <%s>\n int main(void) {return 0;}" $2 | ( $(CC) $(filter-out --coverage, $(ALL_CFLAGS)) $(ALL_CPPFLAGS) $5 -o /dev/null -xc - ) > /dev/null 2>&1; \
|
printf "\#include <%s>\n int main(void) {return 0;}\n" $2 | ( $(CC) $(filter-out --coverage, $(ALL_CFLAGS)) $(ALL_CPPFLAGS) $5 -o /dev/null -xc - ) > /dev/null 2>&1; \
|
||||||
if [ $$? != 0 ]; \
|
if [ $$? != 0 ]; \
|
||||||
then \
|
then \
|
||||||
printf " REQCHK %s (%s)\n" $1 $2; \
|
printf " REQCHK %s (%s)\n" $1 $2; \
|
||||||
@@ -168,6 +180,7 @@ USRSBINDIR = $(INSTALLDIR)/usr/sbin
|
|||||||
USRBINDIR = $(INSTALLDIR)/usr/bin
|
USRBINDIR = $(INSTALLDIR)/usr/bin
|
||||||
BINDIR = $(INSTALLDIR)/sbin
|
BINDIR = $(INSTALLDIR)/sbin
|
||||||
LIBDIR = $(INSTALLDIR)/lib
|
LIBDIR = $(INSTALLDIR)/lib
|
||||||
|
USRLIBDIR = $(INSTALLDIR)/usr/lib
|
||||||
USRLIB64DIR = $(INSTALLDIR)/usr/lib64
|
USRLIB64DIR = $(INSTALLDIR)/usr/lib64
|
||||||
SYSCONFDIR = $(INSTALLDIR)/etc
|
SYSCONFDIR = $(INSTALLDIR)/etc
|
||||||
MANDIR = $(INSTALLDIR)/usr/share/man
|
MANDIR = $(INSTALLDIR)/usr/share/man
|
||||||
@@ -180,6 +193,11 @@ ZFCPDUMP_DIR = $(TOOLS_LIBDIR)/zfcpdump
|
|||||||
SYSTEMDSYSTEMUNITDIR =
|
SYSTEMDSYSTEMUNITDIR =
|
||||||
USRINCLUDEDIR = $(INSTALLDIR)/usr/include
|
USRINCLUDEDIR = $(INSTALLDIR)/usr/include
|
||||||
ZKEYKMSPLUGINDIR = $(USRLIB64DIR)/zkey
|
ZKEYKMSPLUGINDIR = $(USRLIB64DIR)/zkey
|
||||||
|
UDEVDIR = $(USRLIBDIR)/udev
|
||||||
|
UDEVRULESDIR = $(UDEVDIR)/rules.d
|
||||||
|
DRACUTDIR = $(USRLIBDIR)/dracut
|
||||||
|
DRACUTCONFDIR = $(DRACUTDIR)/dracut.conf.d
|
||||||
|
DRACUTMODDIR = $(DRACUTDIR)/modules.d
|
||||||
|
|
||||||
ifeq ($(LIBDIR),$(INSTALLDIR)/lib)
|
ifeq ($(LIBDIR),$(INSTALLDIR)/lib)
|
||||||
SOINSTALLDIR = $(USRLIB64DIR)
|
SOINSTALLDIR = $(USRLIB64DIR)
|
||||||
@@ -192,10 +210,10 @@ INSTDIRS = $(USRSBINDIR) $(USRBINDIR) $(BINDIR) $(LIBDIR) $(MANDIR) \
|
|||||||
$(TOOLS_LIBDIR) $(TOOLS_DATADIR) \
|
$(TOOLS_LIBDIR) $(TOOLS_DATADIR) \
|
||||||
$(ZFCPDUMP_DIR) $(SYSTEMDSYSTEMUNITDIR) \
|
$(ZFCPDUMP_DIR) $(SYSTEMDSYSTEMUNITDIR) \
|
||||||
$(USRLIB64DIR) $(USRINCLUDEDIR) $(ZKEYKMSPLUGINDIR) \
|
$(USRLIB64DIR) $(USRINCLUDEDIR) $(ZKEYKMSPLUGINDIR) \
|
||||||
$(SOINSTALLDIR)
|
$(SOINSTALLDIR) $(USRLIBDIR)
|
||||||
OWNER = $(shell id -un)
|
OWNER = $(shell id -un)
|
||||||
GROUP = $(shell id -gn)
|
GROUP = $(shell id -gn)
|
||||||
export INSTALLDIR BINDIR LIBDIR USRLIB64DIR MANDIR OWNER GROUP
|
export INSTALLDIR BINDIR LIBDIR USRLIBDIR USRLIB64DIR MANDIR OWNER GROUP
|
||||||
|
|
||||||
# Special defines for zfcpdump
|
# Special defines for zfcpdump
|
||||||
ZFCPDUMP_IMAGE = zfcpdump-image
|
ZFCPDUMP_IMAGE = zfcpdump-image
|
||||||
@@ -237,11 +255,9 @@ export AS LD CC CPP AR NM STRIP OBJCOPY OBJDUMP INSTALL CFLAGS CXXFLAGS \
|
|||||||
|
|
||||||
ifneq ($(shell $(CC_SILENT) -dumpspecs 2>/dev/null | grep -e '[^f]no-pie'),)
|
ifneq ($(shell $(CC_SILENT) -dumpspecs 2>/dev/null | grep -e '[^f]no-pie'),)
|
||||||
NO_PIE_CFLAGS := -fno-pie
|
NO_PIE_CFLAGS := -fno-pie
|
||||||
NO_PIE_LINKFLAGS := -no-pie
|
|
||||||
NO_PIE_LDFLAGS := -no-pie
|
NO_PIE_LDFLAGS := -no-pie
|
||||||
else
|
else
|
||||||
NO_PIE_CFLAGS :=
|
NO_PIE_CFLAGS :=
|
||||||
NO_PIE_LINKFLAGS :=
|
|
||||||
NO_PIE_LDFLAGS :=
|
NO_PIE_LDFLAGS :=
|
||||||
endif
|
endif
|
||||||
|
|
||||||
@@ -356,10 +372,30 @@ $(rootdir)/libvmcp/libvmcp.a: $(rootdir)/libvmcp
|
|||||||
$(MAKE) -C $(rootdir)/libvmcp/ libvmcp.a
|
$(MAKE) -C $(rootdir)/libvmcp/ libvmcp.a
|
||||||
.PHONY: $(rootdir)/libvmcp
|
.PHONY: $(rootdir)/libvmcp
|
||||||
|
|
||||||
|
$(rootdir)/libcpumf/libcpumf.a: $(rootdir)/libcpumf
|
||||||
|
$(MAKE) -C $(rootdir)/libcpumf/ libcpumf.a
|
||||||
|
.PHONY: $(rootdir)/libcpumf
|
||||||
|
|
||||||
$(rootdir)/libekmfweb/libekmfweb.so: $(rootdir)/libekmfweb
|
$(rootdir)/libekmfweb/libekmfweb.so: $(rootdir)/libekmfweb
|
||||||
$(MAKE) -C $(rootdir)/libekmfweb/ libekmfweb.so
|
$(MAKE) -C $(rootdir)/libekmfweb/ libekmfweb.so
|
||||||
.PHONY: $(rootdir)/libekmfweb
|
.PHONY: $(rootdir)/libekmfweb
|
||||||
|
|
||||||
|
$(rootdir)/libseckey/libseckey.a: $(rootdir)/libseckey
|
||||||
|
$(MAKE) -C $(rootdir)/libseckey/ libseckey.a
|
||||||
|
.PHONY: $(rootdir)/libseckey
|
||||||
|
|
||||||
|
$(rootdir)/libkmipclient/libkmipclient.so: $(rootdir)/libkmipclient
|
||||||
|
$(MAKE) -C $(rootdir)/libkmipclient/ libkmipclient.so
|
||||||
|
.PHONY: $(rootdir)/libkmipclient
|
||||||
|
|
||||||
|
$(rootdir)/libap/libap.a: $(rootdir)/libap
|
||||||
|
$(MAKE) -C $(rootdir)/libap/ libap.a
|
||||||
|
.PHONY: $(rootdir)/libap
|
||||||
|
|
||||||
|
$(rootdir)/libpv/libpv.a: $(rootdir)/libpv
|
||||||
|
$(MAKE) -C $(rootdir)/libpv libpv.a
|
||||||
|
.PHONY: $(rootdir)/libpv
|
||||||
|
|
||||||
$(rootdir)/zipl/boot/data.o:
|
$(rootdir)/zipl/boot/data.o:
|
||||||
$(MAKE) -C $(rootdir)/zipl/boot/ data.o
|
$(MAKE) -C $(rootdir)/zipl/boot/ data.o
|
||||||
|
|
||||||
|
|||||||
+21
-2
@@ -1,11 +1,28 @@
|
|||||||
include ../common.mak
|
include ../common.mak
|
||||||
|
|
||||||
|
ifeq (${HAVE_LIBUDEV},0)
|
||||||
|
|
||||||
|
all:
|
||||||
|
$(SKIP) HAVE_LIBUDEV=0
|
||||||
|
|
||||||
|
install:
|
||||||
|
$(SKIP) HAVE_LIBUDEV=0
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
check_dep:
|
||||||
|
$(call check_dep, \
|
||||||
|
"cpacfstatsd", \
|
||||||
|
"libudev.h", \
|
||||||
|
"systemd-devel", \
|
||||||
|
"HAVE_LIBUDEV=0")
|
||||||
|
|
||||||
ALL_CPPFLAGS += -DVERSION=$(VERSION)
|
ALL_CPPFLAGS += -DVERSION=$(VERSION)
|
||||||
|
|
||||||
all: cpacfstats cpacfstatsd
|
all: check_dep cpacfstats cpacfstatsd
|
||||||
|
|
||||||
cpacfstatsd: cpacfstatsd.o stats_sock.o perf_crypto.o
|
cpacfstatsd: cpacfstatsd.o stats_sock.o perf_crypto.o
|
||||||
$(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@
|
$(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -ludev -lpthread -o $@
|
||||||
|
|
||||||
cpacfstats: cpacfstats.o stats_sock.o
|
cpacfstats: cpacfstats.o stats_sock.o
|
||||||
$(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@
|
$(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@
|
||||||
@@ -16,6 +33,8 @@ install: all
|
|||||||
$(INSTALL) -m 644 cpacfstatsd.8 $(DESTDIR)$(MANDIR)/man8
|
$(INSTALL) -m 644 cpacfstatsd.8 $(DESTDIR)$(MANDIR)/man8
|
||||||
$(INSTALL) -m 644 cpacfstats.1 $(DESTDIR)$(MANDIR)/man1
|
$(INSTALL) -m 644 cpacfstats.1 $(DESTDIR)$(MANDIR)/man1
|
||||||
|
|
||||||
|
endif
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f *.o *~ cpacfstatsd cpacfstats
|
rm -f *.o *~ cpacfstatsd cpacfstats
|
||||||
|
|
||||||
|
|||||||
+370
-11
@@ -1,6 +1,6 @@
|
|||||||
.\" cpacfstats.1
|
.\" cpacfstats.1
|
||||||
.\"
|
.\"
|
||||||
.\" Copyright IBM Corp. 2015, 2020
|
.\" Copyright IBM Corp. 2015, 2022
|
||||||
.\" s390-tools is free software; you can redistribute it and/or modify
|
.\" s390-tools is free software; you can redistribute it and/or modify
|
||||||
.\" it under the terms of the MIT license. See LICENSE for details.
|
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||||
.\"
|
.\"
|
||||||
@@ -32,7 +32,9 @@ cpacfstats \- enable, disable and display CPACF statistical data
|
|||||||
.RB ]
|
.RB ]
|
||||||
.RB [ \-p | \-\-print
|
.RB [ \-p | \-\-print
|
||||||
.I counter
|
.I counter
|
||||||
|
.RB [ \-n | \-\-nonzero]
|
||||||
.RB ]
|
.RB ]
|
||||||
|
.RB [ \-j | \-\-json ]
|
||||||
.
|
.
|
||||||
.SH DESCRIPTION
|
.SH DESCRIPTION
|
||||||
The cpacfstats client application interacts with the cpacfstatsd daemon and
|
The cpacfstats client application interacts with the cpacfstatsd daemon and
|
||||||
@@ -44,12 +46,347 @@ All counters are initially disabled and must be switched on to measure
|
|||||||
CPACF activities of the system. There is a slight performance penalty with
|
CPACF activities of the system. There is a slight performance penalty with
|
||||||
CPACF counters enabled.
|
CPACF counters enabled.
|
||||||
|
|
||||||
Note: All cryptographic counters are bound to physical CPUs. If you use
|
CPACF activity counters come in two flavors: CPU-MF and PAI. CPU-MF
|
||||||
dedicated CPUs for your LPAR, you obtain only your CPACF activities. If
|
counters are only available on LPARs and have to be authorized. If
|
||||||
your CPUs are shared with other LPARs, their cryptographic activities might
|
they are available, the counters
|
||||||
distort the results. CPACF performance counters are available on LPARs
|
.B des,
|
||||||
only. For security reasons only members of the group \fIcpacfstats\fR are
|
.B aes,
|
||||||
allowed to run the cpacfstats client application.
|
.B sha,
|
||||||
|
.B rng,
|
||||||
|
and
|
||||||
|
.B ecc
|
||||||
|
are made available. These counters can individually be activated,
|
||||||
|
reset, printed, or deactivated. PAI counters are a lot more detailed.
|
||||||
|
The user interface only offers the counters
|
||||||
|
.B pai_user
|
||||||
|
and
|
||||||
|
.B pai_kernel
|
||||||
|
to count CPACF usage in user-space or kernel-space. When printing
|
||||||
|
these counters, detailed counters are shown. The detailed names are:
|
||||||
|
.RS
|
||||||
|
.IP \(bu
|
||||||
|
KM DES,
|
||||||
|
.IP \(bu
|
||||||
|
KM 2key TDES,
|
||||||
|
.IP \(bu
|
||||||
|
KM TDES,
|
||||||
|
.IP \(bu
|
||||||
|
KM DES protected key,
|
||||||
|
.IP \(bu
|
||||||
|
KM 2key TDES protected key,
|
||||||
|
.IP \(bu
|
||||||
|
KM TDES protected key,
|
||||||
|
.IP \(bu
|
||||||
|
KM AES 128bit,
|
||||||
|
.IP \(bu
|
||||||
|
KM AES 192bit,
|
||||||
|
.IP \(bu
|
||||||
|
KM AES 256bit,
|
||||||
|
.IP \(bu
|
||||||
|
KM AES 128bit protected key,
|
||||||
|
.IP \(bu
|
||||||
|
KM AES 192bit protected key,
|
||||||
|
.IP \(bu
|
||||||
|
KM AES 256bit protected key,
|
||||||
|
.IP \(bu
|
||||||
|
KM AES-XTS 128bit,
|
||||||
|
.IP \(bu
|
||||||
|
KM AES-XTS 256bit,
|
||||||
|
.IP \(bu
|
||||||
|
KM AES-XTS 128bit protected key,
|
||||||
|
.IP \(bu
|
||||||
|
KM AES-XTS 256bit protected key,
|
||||||
|
.IP \(bu
|
||||||
|
KMC DES,
|
||||||
|
.IP \(bu
|
||||||
|
KMC 2key TDES,
|
||||||
|
.IP \(bu
|
||||||
|
KMC TDES,
|
||||||
|
.IP \(bu
|
||||||
|
KMC DES protected key,
|
||||||
|
.IP \(bu
|
||||||
|
KMC 2key TDES protected key,
|
||||||
|
.IP \(bu
|
||||||
|
KMC TDES protected key,
|
||||||
|
.IP \(bu
|
||||||
|
KMC AES 128bit,
|
||||||
|
.IP \(bu
|
||||||
|
KMC AES 192bit,
|
||||||
|
.IP \(bu
|
||||||
|
KMC AES 256bit,
|
||||||
|
.IP \(bu
|
||||||
|
KMC AES 128bit protected key,
|
||||||
|
.IP \(bu
|
||||||
|
KMC AES 192bit protected key,
|
||||||
|
.IP \(bu
|
||||||
|
KMC AES 256bit protected key,
|
||||||
|
.IP \(bu
|
||||||
|
KMC PRNG,
|
||||||
|
.IP \(bu
|
||||||
|
KMA AES 128bit,
|
||||||
|
.IP \(bu
|
||||||
|
KMA AES 192bit,
|
||||||
|
.IP \(bu
|
||||||
|
KMA AES 256bit,
|
||||||
|
.IP \(bu
|
||||||
|
KMA AES 128bit protected key,
|
||||||
|
.IP \(bu
|
||||||
|
KMA AES 192bit protected key,
|
||||||
|
.IP \(bu
|
||||||
|
KMA AES 256bit protected key,
|
||||||
|
.IP \(bu
|
||||||
|
KMF DES,
|
||||||
|
.IP \(bu
|
||||||
|
KMF 2key TDES,
|
||||||
|
.IP \(bu
|
||||||
|
KMF TDES,
|
||||||
|
.IP \(bu
|
||||||
|
KMF DES protected key,
|
||||||
|
.IP \(bu
|
||||||
|
KMF 2key TDES protected key,
|
||||||
|
.IP \(bu
|
||||||
|
KMF TDES protected key,
|
||||||
|
.IP \(bu
|
||||||
|
KMF AES 128bit,
|
||||||
|
.IP \(bu
|
||||||
|
KMF AES 192bit,
|
||||||
|
.IP \(bu
|
||||||
|
KMF AES 256bit,
|
||||||
|
.IP \(bu
|
||||||
|
KMF AES 128bit protected key,
|
||||||
|
.IP \(bu
|
||||||
|
KMF AES 192bit protected key,
|
||||||
|
.IP \(bu
|
||||||
|
KMF AES 256bit protected key,
|
||||||
|
.IP \(bu
|
||||||
|
KMCTR DES,
|
||||||
|
.IP \(bu
|
||||||
|
KMCTR 2key TDES,
|
||||||
|
.IP \(bu
|
||||||
|
KMCTR TDES,
|
||||||
|
.IP \(bu
|
||||||
|
KMCTR DES protected key,
|
||||||
|
.IP \(bu
|
||||||
|
KMCTR 2key TDES protected key,
|
||||||
|
.IP \(bu
|
||||||
|
KMCTR TDES protected key,
|
||||||
|
.IP \(bu
|
||||||
|
KMCTR AES 128bit,
|
||||||
|
.IP \(bu
|
||||||
|
KMCTR AES 192bit,
|
||||||
|
.IP \(bu
|
||||||
|
KMCTR AES 256bit,
|
||||||
|
.IP \(bu
|
||||||
|
KMCTR AES 128bit protected key,
|
||||||
|
.IP \(bu
|
||||||
|
KMCTR AES 192bit protected key,
|
||||||
|
.IP \(bu
|
||||||
|
KMCTR AES 256bit protected key,
|
||||||
|
.IP \(bu
|
||||||
|
KMO DES,
|
||||||
|
.IP \(bu
|
||||||
|
KMO 2key TDES,
|
||||||
|
.IP \(bu
|
||||||
|
KMO TDES,
|
||||||
|
.IP \(bu
|
||||||
|
KMO DES protected key,
|
||||||
|
.IP \(bu
|
||||||
|
KMO 2key TDES protected key,
|
||||||
|
.IP \(bu
|
||||||
|
KMO TDES protected key,
|
||||||
|
.IP \(bu
|
||||||
|
KMO AES 128bit,
|
||||||
|
.IP \(bu
|
||||||
|
KMO AES 192bit,
|
||||||
|
.IP \(bu
|
||||||
|
KMO AES 256bit,
|
||||||
|
.IP \(bu
|
||||||
|
KMO AES 128bit protected key,
|
||||||
|
.IP \(bu
|
||||||
|
KMO AES 192bit protected key,
|
||||||
|
.IP \(bu
|
||||||
|
KMO AES 256bit protected key,
|
||||||
|
.IP \(bu
|
||||||
|
KIMD SHA1,
|
||||||
|
.IP \(bu
|
||||||
|
KIMD SHA256,
|
||||||
|
.IP \(bu
|
||||||
|
KIMD SHA512,
|
||||||
|
.IP \(bu
|
||||||
|
KIMD SHA3-224,
|
||||||
|
.IP \(bu
|
||||||
|
KIMD SHA3-256,
|
||||||
|
.IP \(bu
|
||||||
|
KIMD SHA3-384,
|
||||||
|
.IP \(bu
|
||||||
|
KIMD SHA3-512,
|
||||||
|
.IP \(bu
|
||||||
|
KIMD SHAKE 128,
|
||||||
|
.IP \(bu
|
||||||
|
KIMD SHAKE 256,
|
||||||
|
.IP \(bu
|
||||||
|
KIMD GHASH,
|
||||||
|
.IP \(bu
|
||||||
|
KLMD SHA1,
|
||||||
|
.IP \(bu
|
||||||
|
KLMD SHA256,
|
||||||
|
.IP \(bu
|
||||||
|
KLMD SHA512,
|
||||||
|
.IP \(bu
|
||||||
|
KLMD SHA3-224,
|
||||||
|
.IP \(bu
|
||||||
|
KLMD SHA3-256,
|
||||||
|
.IP \(bu
|
||||||
|
KLMD SHA3-384,
|
||||||
|
.IP \(bu
|
||||||
|
KLMD SHA3-512,
|
||||||
|
.IP \(bu
|
||||||
|
KLMD SHAKE 128,
|
||||||
|
.IP \(bu
|
||||||
|
KLMD SHAKE 256,
|
||||||
|
.IP \(bu
|
||||||
|
KMAC DES,
|
||||||
|
.IP \(bu
|
||||||
|
KMAC 2key TDES,
|
||||||
|
.IP \(bu
|
||||||
|
KMAC TDES,
|
||||||
|
.IP \(bu
|
||||||
|
KMAC DES protected key,
|
||||||
|
.IP \(bu
|
||||||
|
KMAC 2key TDES protected key,
|
||||||
|
.IP \(bu
|
||||||
|
KMAC TDES protected key,
|
||||||
|
.IP \(bu
|
||||||
|
KMAC AES 128bit,
|
||||||
|
.IP \(bu
|
||||||
|
KMAC AES 192bit,
|
||||||
|
.IP \(bu
|
||||||
|
KMAC AES 256bit,
|
||||||
|
.IP \(bu
|
||||||
|
KMAC AES 128bit protected key,
|
||||||
|
.IP \(bu
|
||||||
|
KMAC AES 192bit protected key,
|
||||||
|
.IP \(bu
|
||||||
|
KMAC AES 256bit protected key,
|
||||||
|
.IP \(bu
|
||||||
|
PCC Last Block CMAC DES,
|
||||||
|
.IP \(bu
|
||||||
|
PCC Last Block CMAC 2key TDES,
|
||||||
|
.IP \(bu
|
||||||
|
PCC Last Block CMAC TDES,
|
||||||
|
.IP \(bu
|
||||||
|
PCC Last Block CMAC DES protected key,
|
||||||
|
.IP \(bu
|
||||||
|
PCC Last Block CMAC 2key TDES protected key,
|
||||||
|
.IP \(bu
|
||||||
|
PCC Last Block CMAC TDES protected key,
|
||||||
|
.IP \(bu
|
||||||
|
PCC Last Block CMAC AES 128bit,
|
||||||
|
.IP \(bu
|
||||||
|
PCC Last Block CMAC AES 192bit,
|
||||||
|
.IP \(bu
|
||||||
|
PCC Last Block CMAC AES 256bit,
|
||||||
|
.IP \(bu
|
||||||
|
PCC Last Block CMAC AES 128bit protected key,
|
||||||
|
.IP \(bu
|
||||||
|
PCC Last Block CMAC AES 192bit protected key,
|
||||||
|
.IP \(bu
|
||||||
|
PCC Last Block CMAC AES 256bit protected key,
|
||||||
|
.IP \(bu
|
||||||
|
PCC XTS Parameter AES 128bit,
|
||||||
|
.IP \(bu
|
||||||
|
PCC XTS Parameter AES 256bit,
|
||||||
|
.IP \(bu
|
||||||
|
PCC XTS Parameter AES 128bit protected key,
|
||||||
|
.IP \(bu
|
||||||
|
PCC XTS Parameter AES 256bit protected key,
|
||||||
|
.IP \(bu
|
||||||
|
PCC Scalar Mult P256,
|
||||||
|
.IP \(bu
|
||||||
|
PCC Scalar Mult P384,
|
||||||
|
.IP \(bu
|
||||||
|
PCC Scalar Mult P521,
|
||||||
|
.IP \(bu
|
||||||
|
PCC Scalar Mult Ed25519,
|
||||||
|
.IP \(bu
|
||||||
|
PCC Scalar Mult Ed448,
|
||||||
|
.IP \(bu
|
||||||
|
PCC Scalar Mult X25519,
|
||||||
|
.IP \(bu
|
||||||
|
PCC Scalar Mult X448,
|
||||||
|
.IP \(bu
|
||||||
|
PRNO SHA512 DRNG,
|
||||||
|
.IP \(bu
|
||||||
|
PRNO TRNG Query Ratio,
|
||||||
|
.IP \(bu
|
||||||
|
PRNO TRNG,
|
||||||
|
.IP \(bu
|
||||||
|
KDSA ECDSA Verify P256,
|
||||||
|
.IP \(bu
|
||||||
|
KDSA ECDSA Verify P384,
|
||||||
|
.IP \(bu
|
||||||
|
KDSA ECDSA Verify P521,
|
||||||
|
.IP \(bu
|
||||||
|
KDSA ECDSA Sign P256,
|
||||||
|
.IP \(bu
|
||||||
|
KDSA ECDSA Sign P384,
|
||||||
|
.IP \(bu
|
||||||
|
KDSA ECDSA Sign P521,
|
||||||
|
.IP \(bu
|
||||||
|
KDSA ECDSA Sign P256 protected key,
|
||||||
|
.IP \(bu
|
||||||
|
KDSA ECDSA Sign P384 protected key,
|
||||||
|
.IP \(bu
|
||||||
|
KDSA ECDSA Sign P521 protected key,
|
||||||
|
.IP \(bu
|
||||||
|
KDSA EdDSA Verify Ed25519,
|
||||||
|
.IP \(bu
|
||||||
|
KDSA EdDSA Verify Ed448,
|
||||||
|
.IP \(bu
|
||||||
|
KDSA EdDSA Sign Ed25519,
|
||||||
|
.IP \(bu
|
||||||
|
KDSA EdDSA Sign Ed448,
|
||||||
|
.IP \(bu
|
||||||
|
KDSA EdDSA Sign Ed25519 protected key,
|
||||||
|
.IP \(bu
|
||||||
|
KDSA EdDSA Sign Ed448 protected key,
|
||||||
|
.IP \(bu
|
||||||
|
PCKMO DES,
|
||||||
|
.IP \(bu
|
||||||
|
PCKMO 2key TDES,
|
||||||
|
.IP \(bu
|
||||||
|
PCMKO TDES,
|
||||||
|
.IP \(bu
|
||||||
|
PCKMO AES 128bit,
|
||||||
|
.IP \(bu
|
||||||
|
PCKMO AES 192bit,
|
||||||
|
.IP \(bu
|
||||||
|
PCMKO AES 256bit,
|
||||||
|
.IP \(bu
|
||||||
|
PCKMO ECC P256,
|
||||||
|
.IP \(bu
|
||||||
|
PCKMO ECC P384,
|
||||||
|
.IP \(bu
|
||||||
|
PCKMO ECC P521,
|
||||||
|
.IP \(bu
|
||||||
|
PCKMO ECC Ed25519,
|
||||||
|
.IP \(bu
|
||||||
|
PCKMO ECC Ed448,
|
||||||
|
.IP \(bu
|
||||||
|
Reserved 1, and
|
||||||
|
.IP \(bu
|
||||||
|
Reserved 2.
|
||||||
|
.RE
|
||||||
|
Note that the counters starting with PCKMO and Reserved are only
|
||||||
|
available in the pai_kernel set. Also note that the counters are
|
||||||
|
designed to count successful operations. In the case of KMA this
|
||||||
|
means only complete GCM operations including final hashing are
|
||||||
|
counted.
|
||||||
|
|
||||||
|
Note: CPU-MF based CPACF performance counters are available on LPARs
|
||||||
|
only. PAI counters are available on all hypervisors. For security
|
||||||
|
reasons only members of the group \fIcpacfstats\fR are allowed to run
|
||||||
|
the cpacfstats client application.
|
||||||
|
|
||||||
Example usage scenario:
|
Example usage scenario:
|
||||||
.P
|
.P
|
||||||
@@ -101,12 +438,34 @@ argument can be one of: \fBdes\fR, \fBaes\fR, \fBsha\fR, \fBprng\fR,
|
|||||||
\fBecc\fR, or \fBall\fR. If the counter argument is omitted, all
|
\fBecc\fR, or \fBall\fR. If the counter argument is omitted, all
|
||||||
performance counters are reset to 0.
|
performance counters are reset to 0.
|
||||||
.TP
|
.TP
|
||||||
\fB\-p\fR or \fB\-\-print\fR [counter]
|
\fB\-p\fR or \fB\-\-print\fR [\fB\-n\fR or \fB\-\-nonzero\fR] [counter]
|
||||||
Display the value of one or all CPACF performance counters. The
|
Display the value of one or all CPACF performance counters. The
|
||||||
optional counter argument can be one of: \fBdes\fR, \fBaes\fR,
|
optional counter argument can be one of: \fBdes\fR, \fBaes\fR,
|
||||||
\fBsha\fR, \fBprng\fR, \fBecc\fR, or \fBall\fR. If the counter
|
\fBsha\fR, \fBprng\fR, \fBecc\fR, \fBpai_user\fR, \fBpai_kernel\fR, or
|
||||||
argument is omitted or if there is no argument, all performance
|
\fBall\fR. If the counter argument is omitted or if there is no
|
||||||
counters are displayed.
|
argument, all performance counters are displayed. If the optional
|
||||||
|
\fB\-n\fR or \fB\-\-nonzero\fR argument is given, then only PAI
|
||||||
|
counters that have a non-zero value are printed.
|
||||||
|
.TP
|
||||||
|
\fB\-j\fR or \fB\-\-json\fR
|
||||||
|
Display all activated counters in JSON format. The JSON contains an
|
||||||
|
array of counter objects. Each object contains the property
|
||||||
|
.B counter
|
||||||
|
specifying either a CPU-MF counter of one of the detailed PAI counter.
|
||||||
|
Additional properties include
|
||||||
|
.B error
|
||||||
|
an error number if the counter could not be read,
|
||||||
|
.B value
|
||||||
|
the counter value if the counter could be read,
|
||||||
|
.B space
|
||||||
|
for PAI counters to specify
|
||||||
|
.B user
|
||||||
|
or
|
||||||
|
.B kernel
|
||||||
|
space counter set, and
|
||||||
|
.B counterid
|
||||||
|
for PAI counters to specify the PAI counter number as specified in the
|
||||||
|
Principles of Operation.
|
||||||
.TP
|
.TP
|
||||||
The default command is --print all.
|
The default command is --print all.
|
||||||
.
|
.
|
||||||
|
|||||||
+366
-40
@@ -3,7 +3,7 @@
|
|||||||
*
|
*
|
||||||
* cpacfstats client implementation
|
* cpacfstats client implementation
|
||||||
*
|
*
|
||||||
* Copyright IBM Corp. 2015, 2020
|
* Copyright IBM Corp. 2015, 2022
|
||||||
*
|
*
|
||||||
* s390-tools is free software; you can redistribute it and/or modify
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the MIT license. See LICENSE for details.
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
@@ -39,17 +39,187 @@ static const char *const usage =
|
|||||||
"\t-d, --disable [counter] Disable one or all counters\n"
|
"\t-d, --disable [counter] Disable one or all counters\n"
|
||||||
"\t-r, --reset [counter] Reset one or all counter values\n"
|
"\t-r, --reset [counter] Reset one or all counter values\n"
|
||||||
"\t-p, --print [counter] Print one or all counter values\n"
|
"\t-p, --print [counter] Print one or all counter values\n"
|
||||||
"\tcounter can be: 'aes' 'des' 'rng' 'sha' 'ecc' or 'all'\n";
|
"\t-n, --nonzero Print all PAI counters\n"
|
||||||
|
"\t-j, --json Print all counter values in JSON format\n"
|
||||||
|
"\tcounter can be: 'aes' 'des' 'rng' 'sha' 'ecc'\n"
|
||||||
|
"\t 'pai_user' 'pai_kernel' or 'all'\n";
|
||||||
|
|
||||||
static const char *const counter_str[] = {
|
static const char *const counter_str[] = {
|
||||||
[DES_FUNCTIONS] = "des",
|
[DES_FUNCTIONS] = "des",
|
||||||
[AES_FUNCTIONS] = "aes",
|
[AES_FUNCTIONS] = "aes",
|
||||||
[SHA_FUNCTIONS] = "sha",
|
[SHA_FUNCTIONS] = "sha",
|
||||||
[PRNG_FUNCTIONS] = "rng",
|
[PRNG_FUNCTIONS] = "rng",
|
||||||
[ECC_FUNCTIONS] = "ecc",
|
[ECC_FUNCTIONS] = "ecc",
|
||||||
[ALL_COUNTER] = "all"
|
[ALL_COUNTER] = "all",
|
||||||
|
[PAI_USER] = "pai_user",
|
||||||
|
[PAI_KERNEL] = "pai_kernel"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Strings for the pai counter details. Note that this is 0-based
|
||||||
|
* while PoP is 1-based.
|
||||||
|
*/
|
||||||
|
static const char *const pai_str[] = {
|
||||||
|
[ 0] = "KM DES",
|
||||||
|
[ 1] = "KM 2key TDES",
|
||||||
|
[ 2] = "KM TDES",
|
||||||
|
[ 3] = "KM DES protected key",
|
||||||
|
[ 4] = "KM 2key TDES protected key",
|
||||||
|
[ 5] = "KM TDES protected key",
|
||||||
|
[ 6] = "KM AES 128bit",
|
||||||
|
[ 7] = "KM AES 192bit",
|
||||||
|
[ 8] = "KM AES 256bit",
|
||||||
|
[ 9] = "KM AES 128bit protected key",
|
||||||
|
[ 10] = "KM AES 192bit protected key",
|
||||||
|
[ 11] = "KM AES 256bit protected key",
|
||||||
|
[ 12] = "KM AES-XTS 128bit",
|
||||||
|
[ 13] = "KM AES-XTS 256bit",
|
||||||
|
[ 14] = "KM AES-XTS 128bit protected key",
|
||||||
|
[ 15] = "KM AES-XTS 256bit protected key",
|
||||||
|
[ 16] = "KMC DES",
|
||||||
|
[ 17] = "KMC 2key TDES",
|
||||||
|
[ 18] = "KMC TDES",
|
||||||
|
[ 19] = "KMC DES protected key",
|
||||||
|
[ 20] = "KMC 2key TDES protected key",
|
||||||
|
[ 21] = "KMC TDES protected key",
|
||||||
|
[ 22] = "KMC AES 128bit",
|
||||||
|
[ 23] = "KMC AES 192bit",
|
||||||
|
[ 24] = "KMC AES 256bit",
|
||||||
|
[ 25] = "KMC AES 128bit protected key",
|
||||||
|
[ 26] = "KMC AES 192bit protected key",
|
||||||
|
[ 27] = "KMC AES 256bit protected key",
|
||||||
|
[ 28] = "KMC PRNG",
|
||||||
|
[ 29] = "KMA AES 128bit",
|
||||||
|
[ 30] = "KMA AES 192bit",
|
||||||
|
[ 31] = "KMA AES 256bit",
|
||||||
|
[ 32] = "KMA AES 128bit protected key",
|
||||||
|
[ 33] = "KMA AES 192bit protected key",
|
||||||
|
[ 34] = "KMA AES 256bit protected key",
|
||||||
|
[ 35] = "KMF DES",
|
||||||
|
[ 36] = "KMF 2key TDES",
|
||||||
|
[ 37] = "KMF TDES",
|
||||||
|
[ 38] = "KMF DES protected key",
|
||||||
|
[ 39] = "KMF 2key TDES protected key",
|
||||||
|
[ 40] = "KMF TDES protected key",
|
||||||
|
[ 41] = "KMF AES 128bit",
|
||||||
|
[ 42] = "KMF AES 192bit",
|
||||||
|
[ 43] = "KMF AES 256bit",
|
||||||
|
[ 44] = "KMF AES 128bit protected key",
|
||||||
|
[ 45] = "KMF AES 192bit protected key",
|
||||||
|
[ 46] = "KMF AES 256bit protected key",
|
||||||
|
[ 47] = "KMCTR DES",
|
||||||
|
[ 48] = "KMCTR 2key TDES",
|
||||||
|
[ 49] = "KMCTR TDES",
|
||||||
|
[ 50] = "KMCTR DES protected key",
|
||||||
|
[ 51] = "KMCTR 2key TDES protected key",
|
||||||
|
[ 52] = "KMCTR TDES protected key",
|
||||||
|
[ 53] = "KMCTR AES 128bit",
|
||||||
|
[ 54] = "KMCTR AES 192bit",
|
||||||
|
[ 55] = "KMCTR AES 256bit",
|
||||||
|
[ 56] = "KMCTR AES 128bit protected key",
|
||||||
|
[ 57] = "KMCTR AES 192bit protected key",
|
||||||
|
[ 58] = "KMCTR AES 256bit protected key",
|
||||||
|
[ 59] = "KMO DES",
|
||||||
|
[ 60] = "KMO 2key TDES",
|
||||||
|
[ 61] = "KMO TDES",
|
||||||
|
[ 62] = "KMO DES protected key",
|
||||||
|
[ 63] = "KMO 2key TDES protected key",
|
||||||
|
[ 64] = "KMO TDES protected key",
|
||||||
|
[ 65] = "KMO AES 128bit",
|
||||||
|
[ 66] = "KMO AES 192bit",
|
||||||
|
[ 67] = "KMO AES 256bit",
|
||||||
|
[ 68] = "KMO AES 128bit protected key",
|
||||||
|
[ 69] = "KMO AES 192bit protected key",
|
||||||
|
[ 70] = "KMO AES 256bit protected key",
|
||||||
|
[ 71] = "KIMD SHA1",
|
||||||
|
[ 72] = "KIMD SHA256",
|
||||||
|
[ 73] = "KIMD SHA512",
|
||||||
|
[ 74] = "KIMD SHA3-224",
|
||||||
|
[ 75] = "KIMD SHA3-256",
|
||||||
|
[ 76] = "KIMD SHA3-384",
|
||||||
|
[ 77] = "KIMD SHA3-512",
|
||||||
|
[ 78] = "KIMD SHAKE 128",
|
||||||
|
[ 79] = "KIMD SHAKE 256",
|
||||||
|
[ 80] = "KIMD GHASH",
|
||||||
|
[ 81] = "KLMD SHA1",
|
||||||
|
[ 82] = "KLMD SHA256",
|
||||||
|
[ 83] = "KLMD SHA512",
|
||||||
|
[ 84] = "KLMD SHA3-224",
|
||||||
|
[ 85] = "KLMD SHA3-256",
|
||||||
|
[ 86] = "KLMD SHA3-384",
|
||||||
|
[ 87] = "KLMD SHA3-512",
|
||||||
|
[ 88] = "KLMD SHAKE 128",
|
||||||
|
[ 89] = "KLMD SHAKE 256",
|
||||||
|
[ 90] = "KMAC DES",
|
||||||
|
[ 91] = "KMAC 2key TDES",
|
||||||
|
[ 92] = "KMAC TDES",
|
||||||
|
[ 93] = "KMAC DES protected key",
|
||||||
|
[ 94] = "KMAC 2key TDES protected key",
|
||||||
|
[ 95] = "KMAC TDES protected key",
|
||||||
|
[ 96] = "KMAC AES 128bit",
|
||||||
|
[ 97] = "KMAC AES 192bit",
|
||||||
|
[ 98] = "KMAC AES 256bit",
|
||||||
|
[ 99] = "KMAC AES 128bit protected key",
|
||||||
|
[100] = "KMAC AES 192bit protected key",
|
||||||
|
[101] = "KMAC AES 256bit protected key",
|
||||||
|
[102] = "PCC Last Block CMAC DES",
|
||||||
|
[103] = "PCC Last Block CMAC 2key TDES",
|
||||||
|
[104] = "PCC Last Block CMAC TDES",
|
||||||
|
[105] = "PCC Last Block CMAC DES protected key",
|
||||||
|
[106] = "PCC Last Block CMAC 2key TDES protected key",
|
||||||
|
[107] = "PCC Last Block CMAC TDES protected key",
|
||||||
|
[108] = "PCC Last Block CMAC AES 128bit",
|
||||||
|
[109] = "PCC Last Block CMAC AES 192bit",
|
||||||
|
[110] = "PCC Last Block CMAC AES 256bit",
|
||||||
|
[111] = "PCC Last Block CMAC AES 128bit protected key",
|
||||||
|
[112] = "PCC Last Block CMAC AES 192bit protected key",
|
||||||
|
[113] = "PCC Last Block CMAC AES 256bit protected key",
|
||||||
|
[114] = "PCC XTS Parameter AES 128bit",
|
||||||
|
[115] = "PCC XTS Parameter AES 256bit",
|
||||||
|
[116] = "PCC XTS Parameter AES 128bit protected key",
|
||||||
|
[117] = "PCC XTS Parameter AES 256bit protected key",
|
||||||
|
[118] = "PCC Scalar Mult P256",
|
||||||
|
[119] = "PCC Scalar Mult P384",
|
||||||
|
[120] = "PCC Scalar Mult P521",
|
||||||
|
[121] = "PCC Scalar Mult Ed25519",
|
||||||
|
[122] = "PCC Scalar Mult Ed448",
|
||||||
|
[123] = "PCC Scalar Mult X25519",
|
||||||
|
[124] = "PCC Scalar Mult X448",
|
||||||
|
[125] = "PRNO SHA512 DRNG",
|
||||||
|
[126] = "PRNO TRNG Query Ratio",
|
||||||
|
[127] = "PRNO TRNG",
|
||||||
|
[128] = "KDSA ECDSA Verify P256",
|
||||||
|
[129] = "KDSA ECDSA Verify P384",
|
||||||
|
[130] = "KDSA ECDSA Verify P521",
|
||||||
|
[131] = "KDSA ECDSA Sign P256",
|
||||||
|
[132] = "KDSA ECDSA Sign P384",
|
||||||
|
[133] = "KDSA ECDSA Sign P521",
|
||||||
|
[134] = "KDSA ECDSA Sign P256 protected key",
|
||||||
|
[135] = "KDSA ECDSA Sign P384 protected key",
|
||||||
|
[136] = "KDSA ECDSA Sign P521 protected key",
|
||||||
|
[137] = "KDSA EdDSA Verify Ed25519",
|
||||||
|
[138] = "KDSA EdDSA Verify Ed448",
|
||||||
|
[139] = "KDSA EdDSA Sign Ed25519",
|
||||||
|
[140] = "KDSA EdDSA Sign Ed448",
|
||||||
|
[141] = "KDSA EdDSA Sign Ed25519 protected key",
|
||||||
|
[142] = "KDSA EdDSA Sign Ed448 protected key",
|
||||||
|
[143] = "PCKMO DES",
|
||||||
|
[144] = "PCKMO 2key TDES",
|
||||||
|
[145] = "PCMKO TDES",
|
||||||
|
[146] = "PCKMO AES 128bit",
|
||||||
|
[147] = "PCKMO AES 192bit",
|
||||||
|
[148] = "PCMKO AES 256bit",
|
||||||
|
[149] = "PCKMO ECC P256",
|
||||||
|
[150] = "PCKMO ECC P384",
|
||||||
|
[151] = "PCKMO ECC P521",
|
||||||
|
[152] = "PCKMO ECC Ed25519",
|
||||||
|
[153] = "PCKMO ECC Ed448",
|
||||||
|
[154] = "Reserved 1",
|
||||||
|
[155] = "Reserved 2"
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static int paiprintnonzero;
|
||||||
|
|
||||||
|
|
||||||
static int send_query(int s, enum cmd_e cmd, enum ctr_e ctr)
|
static int send_query(int s, enum cmd_e cmd, enum ctr_e ctr)
|
||||||
{
|
{
|
||||||
@@ -62,7 +232,7 @@ static int send_query(int s, enum cmd_e cmd, enum ctr_e ctr)
|
|||||||
m.query.m_ctr = ctr;
|
m.query.m_ctr = ctr;
|
||||||
m.query.m_cmd = cmd;
|
m.query.m_cmd = cmd;
|
||||||
|
|
||||||
return send_msg(s, &m);
|
return send_msg(s, &m, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -71,7 +241,7 @@ static int recv_answer(int s, int *ctr, int *state, uint64_t *value)
|
|||||||
struct msg m;
|
struct msg m;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
rc = recv_msg(s, &m);
|
rc = recv_msg(s, &m, 0);
|
||||||
if (rc == 0) {
|
if (rc == 0) {
|
||||||
if (m.head.m_ver != VERSION) {
|
if (m.head.m_ver != VERSION) {
|
||||||
eprint("Received msg with wrong version %d != %d\n",
|
eprint("Received msg with wrong version %d != %d\n",
|
||||||
@@ -92,9 +262,139 @@ static int recv_answer(int s, int *ctr, int *state, uint64_t *value)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void print_answer(int ctr, int state, uint64_t value)
|
static void printjsonsep(void)
|
||||||
{
|
{
|
||||||
if (state < 0)
|
static const char *jsonsep = "";
|
||||||
|
|
||||||
|
fputs(jsonsep, stdout);
|
||||||
|
jsonsep = ",";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void json_print_virtual_counter_answer(int s, int ctr,
|
||||||
|
int state, uint64_t value)
|
||||||
|
{
|
||||||
|
int paictr = 0, paistate = 0, ec;
|
||||||
|
uint64_t i, paivalue = 0, maxnum;
|
||||||
|
const char *space;
|
||||||
|
|
||||||
|
switch (ctr) {
|
||||||
|
case HOTPLUG_DETECTED:
|
||||||
|
printjsonsep();
|
||||||
|
printf("{\"counter\":\"hotplug detected\",");
|
||||||
|
if (state < 0)
|
||||||
|
printf("\"error\":%d}", state);
|
||||||
|
else
|
||||||
|
printf("\"value\":%d}", !!value);
|
||||||
|
return;
|
||||||
|
case PAI_USER:
|
||||||
|
maxnum = NUM_PAI_USER;
|
||||||
|
space = "user";
|
||||||
|
break;
|
||||||
|
case PAI_KERNEL:
|
||||||
|
maxnum = NUM_PAI_KERNEL;
|
||||||
|
space = "kernel";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* Here, we have validated the PAI counter retrieved, but not
|
||||||
|
* yet printed. */
|
||||||
|
if (state != ENABLED)
|
||||||
|
return;
|
||||||
|
if (value > maxnum) {
|
||||||
|
eprint("Incompatible versions detected!\n");
|
||||||
|
eprint("Expected %"PRIu64" counter space for %s, but got %"PRIu64"\n",
|
||||||
|
maxnum, space, value);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
for (i = 0; i < value; ++i) {
|
||||||
|
ec = recv_answer(s, &paictr, &paistate, &paivalue);
|
||||||
|
if (ec < 0 || paistate < 0) {
|
||||||
|
eprint("Error on receiving answer message from daemon\n");
|
||||||
|
/* No more data for this virtual event after error. */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (paictr > NUM_PAI_KERNEL) {
|
||||||
|
eprint("Pai counter number too big: %d\n", paictr);
|
||||||
|
} else {
|
||||||
|
printjsonsep();
|
||||||
|
printf("{\"counter\":\"%s\",\"space\":\"%s\",\"counterid\":%d,",
|
||||||
|
pai_str[paictr], space, paictr + 1);
|
||||||
|
if (paistate < 0) {
|
||||||
|
printf("\"error\":%d}", paistate);
|
||||||
|
/* Protocol does not send furter counters. */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
printf("\"value\":%"PRIu64"}", paivalue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void print_virtual_counter_answer(int s,
|
||||||
|
int ctr, int state, uint64_t value)
|
||||||
|
{
|
||||||
|
static const char *const states[] = {
|
||||||
|
[DISABLED] = "disabled",
|
||||||
|
[ENABLED] = "enabled",
|
||||||
|
[UNSUPPORTED] = "unsupported"
|
||||||
|
};
|
||||||
|
int paictr = 0, paistate = 0, ec;
|
||||||
|
uint64_t i, paivalue = 0, maxnum;
|
||||||
|
const char *ctrstr;
|
||||||
|
|
||||||
|
switch (ctr) {
|
||||||
|
case HOTPLUG_DETECTED:
|
||||||
|
if (state >= 0 && value > 0)
|
||||||
|
printf(" hotplug detected\n");
|
||||||
|
return;
|
||||||
|
case PAI_USER:
|
||||||
|
maxnum = NUM_PAI_USER;
|
||||||
|
ctrstr = "pai_user";
|
||||||
|
break;
|
||||||
|
case PAI_KERNEL:
|
||||||
|
maxnum = NUM_PAI_KERNEL;
|
||||||
|
ctrstr = "pai_kernel";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* Here, we have validated the PAI counter retrieved, but not
|
||||||
|
* yet printed. */
|
||||||
|
if (state < 0 || state > UNSUPPORTED) {
|
||||||
|
printf(" %11s: error state %d\n", ctrstr, state);
|
||||||
|
/* No details follow if counter in error state. */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
printf(" %-11s: %s\n", ctrstr, states[state]);
|
||||||
|
if (state != ENABLED)
|
||||||
|
return;
|
||||||
|
if (value > maxnum) {
|
||||||
|
eprint("Incompatible versions detected!\n");
|
||||||
|
eprint("Expected %"PRIu64" counters for %s, but got %"PRIu64"\n",
|
||||||
|
maxnum, ctrstr, value);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
for (i = 0; i < value; ++i) {
|
||||||
|
ec = recv_answer(s, &paictr, &paistate, &paivalue);
|
||||||
|
if (ec < 0 || paistate < 0) {
|
||||||
|
eprint("Error on receiving answer message from daemon\n");
|
||||||
|
/* No more data for this virtual event after error. */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (paictr > NUM_PAI_KERNEL)
|
||||||
|
eprint("Pai counter number too big: %d\n", paictr);
|
||||||
|
else if (!paiprintnonzero || paivalue > 0)
|
||||||
|
printf(" %-45s: %"PRIu64"\n", pai_str[paictr], paivalue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_answer(int s, int ctr, int state, uint64_t value)
|
||||||
|
{
|
||||||
|
if (ctr > ALL_COUNTER)
|
||||||
|
print_virtual_counter_answer(s, ctr, state, value);
|
||||||
|
else if (state < 0)
|
||||||
printf(" %s counter: error state %d\n",
|
printf(" %s counter: error state %d\n",
|
||||||
counter_str[ctr], state);
|
counter_str[ctr], state);
|
||||||
else if (state == DISABLED)
|
else if (state == DISABLED)
|
||||||
@@ -106,6 +406,22 @@ static void print_answer(int ctr, int state, uint64_t value)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void json_print_answer(int s, int ctr, int state, uint64_t value)
|
||||||
|
{
|
||||||
|
if (ctr > ALL_COUNTER) {
|
||||||
|
json_print_virtual_counter_answer(s, ctr, state, value);
|
||||||
|
} else if (state < 0) {
|
||||||
|
printjsonsep();
|
||||||
|
printf("{\"counter\":\"%s\",", counter_str[ctr]);
|
||||||
|
printf("\"error\":%d}\n", state);
|
||||||
|
} else if (state == ENABLED) {
|
||||||
|
printjsonsep();
|
||||||
|
printf("{\"counter\":\"%s\",", counter_str[ctr]);
|
||||||
|
printf("\"value\":%"PRIu64"}", value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int eprint(const char *format, ...)
|
int eprint(const char *format, ...)
|
||||||
{
|
{
|
||||||
char buf[1024];
|
char buf[1024];
|
||||||
@@ -128,7 +444,7 @@ int main(int argc, char *argv[])
|
|||||||
{
|
{
|
||||||
enum ctr_e ctr = ALL_COUNTER;
|
enum ctr_e ctr = ALL_COUNTER;
|
||||||
enum cmd_e cmd = PRINT;
|
enum cmd_e cmd = PRINT;
|
||||||
int i, j, s, state;
|
int i, j, s, state, num, json = 0;
|
||||||
uint64_t value;
|
uint64_t value;
|
||||||
|
|
||||||
if (argc > 1) {
|
if (argc > 1) {
|
||||||
@@ -140,98 +456,108 @@ int main(int argc, char *argv[])
|
|||||||
{ "disable", 0, NULL, 'd' },
|
{ "disable", 0, NULL, 'd' },
|
||||||
{ "reset", 0, NULL, 'r' },
|
{ "reset", 0, NULL, 'r' },
|
||||||
{ "print", 0, NULL, 'p' },
|
{ "print", 0, NULL, 'p' },
|
||||||
|
{ "nonzero", 0, NULL, 'n' },
|
||||||
|
{ "json", 0, NULL, 'j' },
|
||||||
{ NULL, 0, NULL, 0 } };
|
{ NULL, 0, NULL, 0 } };
|
||||||
while (1) {
|
while (1) {
|
||||||
opt = getopt_long(argc, argv,
|
opt = getopt_long(argc, argv,
|
||||||
"hvedrp", long_opts, &idx);
|
"hvedrpnj", long_opts, &idx);
|
||||||
if (opt == -1)
|
if (opt == -1)
|
||||||
break; /* no more arguments */
|
break; /* no more arguments */
|
||||||
switch (opt) {
|
switch (opt) {
|
||||||
case 'h':
|
case 'h':
|
||||||
printf(usage, name);
|
printf(usage, name);
|
||||||
exit(0);
|
return 0;
|
||||||
break;
|
|
||||||
case 'v':
|
case 'v':
|
||||||
printf("%s: Linux on System z CPACF Crypto Activity Counters Client\n"
|
printf("%s: Linux on System z CPACF Crypto Activity Counters Client\n"
|
||||||
"Version %s\n%s\n",
|
"Version %s\n%s\n",
|
||||||
name, RELEASE_STRING, COPYRIGHT);
|
name, RELEASE_STRING, COPYRIGHT);
|
||||||
exit(0);
|
return 0;
|
||||||
break;
|
|
||||||
case 'e':
|
case 'e':
|
||||||
cmd = ENABLE;
|
cmd = ENABLE;
|
||||||
|
json = 0;
|
||||||
break;
|
break;
|
||||||
case 'd':
|
case 'd':
|
||||||
cmd = DISABLE;
|
cmd = DISABLE;
|
||||||
|
json = 0;
|
||||||
break;
|
break;
|
||||||
case 'r':
|
case 'r':
|
||||||
cmd = RESET;
|
cmd = RESET;
|
||||||
break;
|
break;
|
||||||
case 'p':
|
case 'p':
|
||||||
cmd = PRINT;
|
cmd = PRINT;
|
||||||
|
json = 0;
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
paiprintnonzero = 1;
|
||||||
|
break;
|
||||||
|
case 'j':
|
||||||
|
cmd = PRINT;
|
||||||
|
json = 1;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
eprint("Invalid argument, try -h or --help for more information\n");
|
eprint("Invalid argument, try -h or --help for more information\n");
|
||||||
exit(1);
|
return EXIT_FAILURE;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* there may be an optional counter argument */
|
/* there may be an optional counter argument */
|
||||||
if (optind > 0 && optind < argc) {
|
if (optind > 0 && optind < argc) {
|
||||||
for (i = 0; i <= ALL_COUNTER; i++)
|
for (i = 0; i < NUM_COUNTER; i++)
|
||||||
if (strcmp(argv[optind], counter_str[i]) == 0)
|
if (strcmp(argv[optind], counter_str[i]) == 0)
|
||||||
break;
|
break;
|
||||||
if (i > ALL_COUNTER) {
|
if (i >= NUM_COUNTER) {
|
||||||
eprint("Unknown counter '%s'\n", argv[optind]);
|
eprint("Unknown counter '%s'\n", argv[optind]);
|
||||||
exit(1);
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
ctr = (enum ctr_e) i;
|
ctr = (enum ctr_e) i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (json)
|
||||||
|
ctr = ALL_COUNTER;
|
||||||
|
|
||||||
/* try to open and connect socket to the cpacfstatsd daemon */
|
/* try to open and connect socket to the cpacfstatsd daemon */
|
||||||
s = open_socket(CLIENT);
|
s = open_socket(CLIENT);
|
||||||
if (s < 0) {
|
if (s < 0) {
|
||||||
eprint("Can't connect to daemon\n");
|
eprint("Can't connect to daemon\n");
|
||||||
exit(1);
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* send query */
|
/* send query */
|
||||||
if (send_query(s, cmd, ctr) != 0) {
|
if (send_query(s, cmd, ctr) != 0) {
|
||||||
eprint("Error on sending query message to daemon\n");
|
eprint("Error on sending query message to daemon\n");
|
||||||
close(s);
|
close(s);
|
||||||
exit(1);
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ctr == ALL_COUNTER) {
|
if (ctr == ALL_COUNTER) {
|
||||||
for (i = 0; i < ALL_COUNTER; i++) {
|
/* The -1 is for ALL_COUNTER which is not sent, +1 for
|
||||||
/* receive answer */
|
* hotplug state. */
|
||||||
if (recv_answer(s, &j, &state, &value) != 0) {
|
num = NUM_COUNTER - 1 + 1;
|
||||||
eprint("Error on receiving answer message from daemon\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
if (state < 0) {
|
|
||||||
eprint("Received bad status code %d from daemon\n",
|
|
||||||
state);
|
|
||||||
close(s);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
print_answer(j, state, value);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
|
/* +1 for hotplug state */
|
||||||
|
num = 1 + 1;
|
||||||
|
}
|
||||||
|
if (json)
|
||||||
|
putchar('[');
|
||||||
|
for (i = 0; i < num; i++) {
|
||||||
/* receive answer */
|
/* receive answer */
|
||||||
if (recv_answer(s, &j, &state, &value) != 0) {
|
if (recv_answer(s, &j, &state, &value) != 0) {
|
||||||
eprint("Error on receiving answer message from daemon\n");
|
eprint("Error on receiving answer message from daemon\n");
|
||||||
close(s);
|
return EXIT_FAILURE;
|
||||||
exit(1);
|
|
||||||
}
|
}
|
||||||
if (state < 0) {
|
if (state < 0) {
|
||||||
eprint("Received bad status code %d from daemon\n",
|
eprint("Received bad status code %d from daemon\n",
|
||||||
state);
|
state);
|
||||||
close(s);
|
close(s);
|
||||||
exit(1);
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
print_answer(j, state, value);
|
if (json)
|
||||||
|
json_print_answer(s, j, state, value);
|
||||||
|
else
|
||||||
|
print_answer(s, j, state, value);
|
||||||
}
|
}
|
||||||
|
if (json)
|
||||||
|
putchar(']');
|
||||||
|
|
||||||
/* close connection */
|
/* close connection */
|
||||||
close(s);
|
close(s);
|
||||||
|
|||||||
+30
-9
@@ -3,7 +3,7 @@
|
|||||||
*
|
*
|
||||||
* common function prototypes and definitions
|
* common function prototypes and definitions
|
||||||
*
|
*
|
||||||
* Copyright IBM Corp. 2015, 2020
|
* Copyright IBM Corp. 2015, 2022
|
||||||
*
|
*
|
||||||
* s390-tools is free software; you can redistribute it and/or modify
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the MIT license. See LICENSE for details.
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
@@ -14,13 +14,27 @@
|
|||||||
|
|
||||||
#include "lib/zt_common.h"
|
#include "lib/zt_common.h"
|
||||||
|
|
||||||
#define COPYRIGHT "Copyright IBM Corp. 2015, 2020"
|
#define COPYRIGHT "Copyright IBM Corp. 2015, 2022"
|
||||||
|
|
||||||
|
#define DEFAULT_SEND_TIMEOUT (30 * 1000)
|
||||||
|
#define DEFAULT_RECV_TIMEOUT (30 * 1000)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Number of PAI counters for user space. This excludes PCKMO since
|
||||||
|
* this instruction is privileged.
|
||||||
|
*/
|
||||||
|
#define NUM_PAI_USER 143
|
||||||
|
/* Number of PAI counters for kernel space. Contains all counters. */
|
||||||
|
#define NUM_PAI_KERNEL 156
|
||||||
|
|
||||||
int eprint(const char *format, ...);
|
int eprint(const char *format, ...);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Counter names
|
* Counter names
|
||||||
* ALL_COUNTER must always be the last member of the enum
|
* ALL_COUNTER specifies the number of physical counters. Virtual
|
||||||
|
* counters might be added afterwards. NUM_COUNTER is the last
|
||||||
|
* managed counter (i.e., a counter that can be activated, reset,
|
||||||
|
* deactivated).
|
||||||
*/
|
*/
|
||||||
enum ctr_e {
|
enum ctr_e {
|
||||||
DES_FUNCTIONS = 0,
|
DES_FUNCTIONS = 0,
|
||||||
@@ -28,7 +42,11 @@ enum ctr_e {
|
|||||||
SHA_FUNCTIONS,
|
SHA_FUNCTIONS,
|
||||||
PRNG_FUNCTIONS,
|
PRNG_FUNCTIONS,
|
||||||
ECC_FUNCTIONS,
|
ECC_FUNCTIONS,
|
||||||
ALL_COUNTER
|
ALL_COUNTER,
|
||||||
|
PAI_USER,
|
||||||
|
PAI_KERNEL,
|
||||||
|
NUM_COUNTER,
|
||||||
|
HOTPLUG_DETECTED = 0xffff
|
||||||
};
|
};
|
||||||
|
|
||||||
enum type_e {
|
enum type_e {
|
||||||
@@ -50,7 +68,7 @@ enum state_e {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* query send from clent to daemon
|
* query send from client to daemon
|
||||||
* Consist of:
|
* Consist of:
|
||||||
* enum counter
|
* enum counter
|
||||||
* enum command
|
* enum command
|
||||||
@@ -63,7 +81,7 @@ struct msg_query {
|
|||||||
/*
|
/*
|
||||||
* answer send from daemon to client
|
* answer send from daemon to client
|
||||||
* Consist of:
|
* Consist of:
|
||||||
* enum counter
|
* enum counter or PAI counter number if following PAI_USER or PAI_KERNEL
|
||||||
* status code: < 0 error, 0 disabled, > 0 enabled
|
* status code: < 0 error, 0 disabled, > 0 enabled
|
||||||
* counter value
|
* counter value
|
||||||
*/
|
*/
|
||||||
@@ -99,17 +117,20 @@ struct msg {
|
|||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
int open_socket(int mode);
|
int open_socket(int mode);
|
||||||
int send_msg(int sfd, struct msg *m);
|
int send_msg(int sfd, struct msg *m, int timeout);
|
||||||
int recv_msg(int sfd, struct msg *m);
|
int recv_msg(int sfd, struct msg *m, int timeout);
|
||||||
|
|
||||||
/* perf_crypto.c */
|
/* perf_crypto.c */
|
||||||
|
|
||||||
int perf_init(void);
|
int perf_init(void);
|
||||||
|
void perf_stop(void);
|
||||||
void perf_close(void);
|
void perf_close(void);
|
||||||
int perf_enable_ctr(enum ctr_e ctr);
|
int perf_enable_ctr(enum ctr_e ctr);
|
||||||
int perf_disable_ctr(enum ctr_e ctr);
|
int perf_disable_ctr(enum ctr_e ctr);
|
||||||
int perf_reset_ctr(enum ctr_e ctr);
|
int perf_reset_ctr(enum ctr_e ctr, uint64_t *value);
|
||||||
int perf_read_ctr(enum ctr_e ctr, uint64_t *value);
|
int perf_read_ctr(enum ctr_e ctr, uint64_t *value);
|
||||||
int perf_ecc_supported(void);
|
int perf_ecc_supported(void);
|
||||||
|
int perf_ctr_state(enum ctr_e ctr);
|
||||||
|
int perf_read_pai_ctr(unsigned int ctrnum, int user, uint64_t *value);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+124
-62
@@ -3,7 +3,7 @@
|
|||||||
*
|
*
|
||||||
* cpacfstatsd daemon implementation
|
* cpacfstatsd daemon implementation
|
||||||
*
|
*
|
||||||
* Copyright IBM Corp. 2015, 2017
|
* Copyright IBM Corp. 2015, 2022
|
||||||
*
|
*
|
||||||
* s390-tools is free software; you can redistribute it and/or modify
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the MIT license. See LICENSE for details.
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
@@ -28,6 +28,7 @@
|
|||||||
#include "lib/zt_common.h"
|
#include "lib/zt_common.h"
|
||||||
#include "cpacfstats.h"
|
#include "cpacfstats.h"
|
||||||
|
|
||||||
|
static volatile int stopsig;
|
||||||
|
|
||||||
static const char *const name = "cpacfstatsd";
|
static const char *const name = "cpacfstatsd";
|
||||||
|
|
||||||
@@ -43,15 +44,12 @@ static const char *const usage =
|
|||||||
|
|
||||||
static int daemonized;
|
static int daemonized;
|
||||||
|
|
||||||
static int ctr_state[ALL_COUNTER];
|
|
||||||
|
|
||||||
|
|
||||||
static int recv_query(int s, enum ctr_e *ctr, enum cmd_e *cmd)
|
static int recv_query(int s, enum ctr_e *ctr, enum cmd_e *cmd)
|
||||||
{
|
{
|
||||||
struct msg m;
|
struct msg m;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
rc = recv_msg(s, &m);
|
rc = recv_msg(s, &m, DEFAULT_RECV_TIMEOUT);
|
||||||
if (rc == 0) {
|
if (rc == 0) {
|
||||||
if (m.head.m_ver != VERSION) {
|
if (m.head.m_ver != VERSION) {
|
||||||
eprint("Received msg with wrong version %d != %d\n",
|
eprint("Received msg with wrong version %d != %d\n",
|
||||||
@@ -83,38 +81,82 @@ static int send_answer(int s, int ctr, int state, uint64_t value)
|
|||||||
m.answer.m_state = state;
|
m.answer.m_state = state;
|
||||||
m.answer.m_value = value;
|
m.answer.m_value = value;
|
||||||
|
|
||||||
return send_msg(s, &m);
|
return send_msg(s, &m, DEFAULT_SEND_TIMEOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Print according to protocol for PAI:
|
||||||
|
* - first the state and the number of PAI counters that follow
|
||||||
|
* - if state is ENABLED:
|
||||||
|
* - for each PAI counter the value with state ENABLED
|
||||||
|
* Note that the PAI counters are 0-based, not 1 based as in PoP!
|
||||||
|
* Sending ends with the first error.
|
||||||
|
*/
|
||||||
|
static int do_send_pai(int s, int user)
|
||||||
|
{
|
||||||
|
int ctr, state, i, maxctr, rc = 0;
|
||||||
|
uint64_t value;
|
||||||
|
|
||||||
|
if (user) {
|
||||||
|
ctr = PAI_USER;
|
||||||
|
maxctr = NUM_PAI_USER;
|
||||||
|
} else {
|
||||||
|
ctr = PAI_KERNEL;
|
||||||
|
maxctr = NUM_PAI_KERNEL;
|
||||||
|
}
|
||||||
|
state = perf_ctr_state(ctr);
|
||||||
|
if (state != ENABLED)
|
||||||
|
return rc;
|
||||||
|
for (i = 0; i < maxctr; ++i) {
|
||||||
|
rc = perf_read_pai_ctr(i, user, &value);
|
||||||
|
if (rc != 0) {
|
||||||
|
send_answer(s, i, rc, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
send_answer(s, i, state, value);
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int do_enable(int s, enum ctr_e ctr)
|
static int do_enable(int s, enum ctr_e ctr)
|
||||||
{
|
{
|
||||||
uint64_t value;
|
uint64_t value = 0;
|
||||||
int i, rc = 0;
|
int i, rc = 0;
|
||||||
|
int state;
|
||||||
|
|
||||||
for (i = 0; i < ALL_COUNTER; i++) {
|
for (i = 0; i < NUM_COUNTER; i++) {
|
||||||
|
if (i == ALL_COUNTER)
|
||||||
|
continue;
|
||||||
if (i == (int) ctr || ctr == ALL_COUNTER) {
|
if (i == (int) ctr || ctr == ALL_COUNTER) {
|
||||||
if (ctr_state[i] == DISABLED) {
|
state = perf_ctr_state(i);
|
||||||
|
if (state == DISABLED) {
|
||||||
rc = perf_enable_ctr(i);
|
rc = perf_enable_ctr(i);
|
||||||
if (rc != 0) {
|
if (rc != 0) {
|
||||||
send_answer(s, i, rc, 0);
|
send_answer(s, i, rc, 0);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
ctr_state[i] = ENABLED;
|
state = ENABLED;
|
||||||
}
|
}
|
||||||
if (ctr_state[i] == UNSUPPORTED) {
|
if (state != UNSUPPORTED) {
|
||||||
send_answer(s, i, UNSUPPORTED, 0);
|
|
||||||
} else {
|
|
||||||
rc = perf_read_ctr(i, &value);
|
rc = perf_read_ctr(i, &value);
|
||||||
if (rc != 0) {
|
if (rc != 0) {
|
||||||
send_answer(s, i, rc, 0);
|
send_answer(s, i, rc, 0);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
send_answer(s, i, ENABLED, value);
|
|
||||||
}
|
}
|
||||||
|
send_answer(s, i, state, value);
|
||||||
|
if (i == PAI_USER)
|
||||||
|
rc = do_send_pai(s, 1);
|
||||||
|
if (i == PAI_KERNEL)
|
||||||
|
rc = do_send_pai(s, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (rc == 0) {
|
||||||
|
rc = perf_read_ctr(HOTPLUG_DETECTED, &value);
|
||||||
|
send_answer(s, HOTPLUG_DETECTED, rc, value);
|
||||||
|
}
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,67 +164,90 @@ static int do_enable(int s, enum ctr_e ctr)
|
|||||||
static int do_disable(int s, enum ctr_e ctr)
|
static int do_disable(int s, enum ctr_e ctr)
|
||||||
{
|
{
|
||||||
int i, rc = 0;
|
int i, rc = 0;
|
||||||
|
uint64_t value;
|
||||||
|
|
||||||
for (i = 0; i < ALL_COUNTER; i++) {
|
for (i = 0; i < NUM_COUNTER; i++) {
|
||||||
|
if (i == ALL_COUNTER)
|
||||||
|
continue;
|
||||||
if (i == (int) ctr || ctr == ALL_COUNTER) {
|
if (i == (int) ctr || ctr == ALL_COUNTER) {
|
||||||
if (ctr_state[i] == ENABLED) {
|
if (perf_ctr_state(i) == ENABLED) {
|
||||||
rc = perf_disable_ctr(i);
|
rc = perf_disable_ctr(i);
|
||||||
if (rc != 0) {
|
if (rc != 0) {
|
||||||
send_answer(s, i, rc, 0);
|
send_answer(s, i, rc, 0);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
ctr_state[i] = 0;
|
|
||||||
}
|
}
|
||||||
send_answer(s, i, ctr_state[i], 0);
|
send_answer(s, i, perf_ctr_state(i), 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (rc == 0) {
|
||||||
|
rc = perf_read_ctr(HOTPLUG_DETECTED, &value);
|
||||||
|
send_answer(s, HOTPLUG_DETECTED, rc, value);
|
||||||
|
}
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int do_reset(int s, enum ctr_e ctr)
|
static int do_reset(int s, enum ctr_e ctr)
|
||||||
{
|
{
|
||||||
int i, rc = 0;
|
int i, rc = 0, state;
|
||||||
|
uint64_t value;
|
||||||
|
|
||||||
for (i = 0; i < ALL_COUNTER; i++) {
|
for (i = 0; i < NUM_COUNTER; i++) {
|
||||||
|
if (i == ALL_COUNTER)
|
||||||
|
continue;
|
||||||
if (i == (int) ctr || ctr == ALL_COUNTER) {
|
if (i == (int) ctr || ctr == ALL_COUNTER) {
|
||||||
if (ctr_state[i] == ENABLED) {
|
state = perf_ctr_state(i);
|
||||||
rc = perf_reset_ctr(i);
|
if (state == ENABLED) {
|
||||||
|
rc = perf_reset_ctr(i, &value);
|
||||||
if (rc != 0) {
|
if (rc != 0) {
|
||||||
send_answer(s, i, rc, 0);
|
send_answer(s, i, rc, 0);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
send_answer(s, i, ENABLED, 0);
|
|
||||||
} else {
|
|
||||||
send_answer(s, i, ctr_state[i], 0);
|
|
||||||
}
|
}
|
||||||
|
send_answer(s, i, state, value);
|
||||||
|
if (i == PAI_USER)
|
||||||
|
rc = do_send_pai(s, 1);
|
||||||
|
if (i == PAI_KERNEL)
|
||||||
|
rc = do_send_pai(s, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (rc == 0) {
|
||||||
|
rc = perf_read_ctr(HOTPLUG_DETECTED, &value);
|
||||||
|
send_answer(s, HOTPLUG_DETECTED, rc, value);
|
||||||
|
}
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int do_print(int s, enum ctr_e ctr)
|
static int do_print(int s, enum ctr_e ctr)
|
||||||
{
|
{
|
||||||
int i, rc = 0;
|
int i, rc = 0, state;
|
||||||
uint64_t value;
|
uint64_t value = 0;
|
||||||
|
|
||||||
for (i = 0; i < ALL_COUNTER; i++) {
|
for (i = 0; i < NUM_COUNTER; i++) {
|
||||||
|
if (i == ALL_COUNTER)
|
||||||
|
continue;
|
||||||
if (i == (int) ctr || ctr == ALL_COUNTER) {
|
if (i == (int) ctr || ctr == ALL_COUNTER) {
|
||||||
if (ctr_state[i] == ENABLED) {
|
state = perf_ctr_state(i);
|
||||||
|
if (state == ENABLED) {
|
||||||
rc = perf_read_ctr(i, &value);
|
rc = perf_read_ctr(i, &value);
|
||||||
if (rc != 0) {
|
if (rc != 0) {
|
||||||
send_answer(s, i, rc, 0);
|
send_answer(s, i, rc, 0);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
send_answer(s, i, ENABLED, value);
|
|
||||||
} else {
|
|
||||||
send_answer(s, i, ctr_state[i], 0);
|
|
||||||
}
|
}
|
||||||
|
send_answer(s, i, state, value);
|
||||||
|
if (i == PAI_USER)
|
||||||
|
rc = do_send_pai(s, 1);
|
||||||
|
if (i == PAI_KERNEL)
|
||||||
|
rc = do_send_pai(s, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (rc == 0) {
|
||||||
|
rc = perf_read_ctr(HOTPLUG_DETECTED, &value);
|
||||||
|
send_answer(s, HOTPLUG_DETECTED, rc, value);
|
||||||
|
}
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -324,18 +389,8 @@ static void remove_pidfile(void)
|
|||||||
|
|
||||||
void signalhandler(int sig)
|
void signalhandler(int sig)
|
||||||
{
|
{
|
||||||
if (sig == SIGTERM)
|
perf_stop();
|
||||||
eprint("Caught signal SIGTERM, terminating...\n");
|
stopsig = sig;
|
||||||
else if (sig == SIGINT)
|
|
||||||
eprint("Caught signal SIGINT, terminating...\n");
|
|
||||||
else
|
|
||||||
eprint("Caught signal %d, terminating...\n", sig);
|
|
||||||
|
|
||||||
remove_sock();
|
|
||||||
perf_close();
|
|
||||||
remove_pidfile();
|
|
||||||
|
|
||||||
exit(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -381,7 +436,7 @@ int main(int argc, char *argv[])
|
|||||||
switch (opt) {
|
switch (opt) {
|
||||||
case 'h':
|
case 'h':
|
||||||
printf(usage, name);
|
printf(usage, name);
|
||||||
exit(0);
|
return 0;
|
||||||
case 'f':
|
case 'f':
|
||||||
foreground = 1;
|
foreground = 1;
|
||||||
break;
|
break;
|
||||||
@@ -389,40 +444,37 @@ int main(int argc, char *argv[])
|
|||||||
printf("%s: Linux on System z CPACF Crypto Activity Counters Daemon\n"
|
printf("%s: Linux on System z CPACF Crypto Activity Counters Daemon\n"
|
||||||
"Version %s\n%s\n",
|
"Version %s\n%s\n",
|
||||||
name, RELEASE_STRING, COPYRIGHT);
|
name, RELEASE_STRING, COPYRIGHT);
|
||||||
exit(0);
|
return 0;
|
||||||
default:
|
default:
|
||||||
printf("%s: Invalid argument, try -h or --help for more information\n",
|
printf("%s: Invalid argument, try -h or --help for more information\n",
|
||||||
name);
|
name);
|
||||||
exit(1);
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (check_pidfile() != 0) {
|
if (check_pidfile() != 0) {
|
||||||
eprint("Stalled pid file or daemon allready running, terminating\n");
|
eprint("Stalled pid file or daemon allready running, terminating\n");
|
||||||
exit(1);
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!foreground) {
|
if (!foreground) {
|
||||||
if (become_daemon() != 0) {
|
if (become_daemon() != 0) {
|
||||||
eprint("Couldn't daemonize\n");
|
eprint("Couldn't daemonize\n");
|
||||||
exit(1);
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (perf_init() != 0) {
|
if (perf_init() != 0) {
|
||||||
eprint("Couldn't initialize perf lib\n");
|
eprint("Couldn't initialize perf lib\n");
|
||||||
exit(1);
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
atexit(perf_close);
|
atexit(perf_close);
|
||||||
|
|
||||||
if (!perf_ecc_supported())
|
|
||||||
ctr_state[ECC_FUNCTIONS] = UNSUPPORTED;
|
|
||||||
|
|
||||||
sfd = open_socket(SERVER);
|
sfd = open_socket(SERVER);
|
||||||
if (sfd < 0) {
|
if (sfd < 0) {
|
||||||
eprint("Couldn't initialize server socket\n");
|
eprint("Couldn't initialize server socket\n");
|
||||||
exit(1);
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
atexit(remove_sock);
|
atexit(remove_sock);
|
||||||
|
|
||||||
@@ -432,17 +484,19 @@ int main(int argc, char *argv[])
|
|||||||
if (sigaction(SIGINT, &act, 0) != 0) {
|
if (sigaction(SIGINT, &act, 0) != 0) {
|
||||||
eprint("Couldn't establish signal handler for SIGINT, errno=%d [%s]\n",
|
eprint("Couldn't establish signal handler for SIGINT, errno=%d [%s]\n",
|
||||||
errno, strerror(errno));
|
errno, strerror(errno));
|
||||||
exit(1);
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
if (sigaction(SIGTERM, &act, 0) != 0) {
|
if (sigaction(SIGTERM, &act, 0) != 0) {
|
||||||
eprint("Couldn't establish signal handler for SIGTERM, errno=%d [%s]\n",
|
eprint("Couldn't establish signal handler for SIGTERM, errno=%d [%s]\n",
|
||||||
errno, strerror(errno));
|
errno, strerror(errno));
|
||||||
exit(1);
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
/* Ignore SIGPIPE such that we see EPIPE as return from write. */
|
||||||
|
signal(SIGPIPE, SIG_IGN);
|
||||||
|
|
||||||
eprint("Running\n");
|
eprint("Running\n");
|
||||||
|
|
||||||
while (1) {
|
while (!stopsig) {
|
||||||
enum ctr_e ctr;
|
enum ctr_e ctr;
|
||||||
enum cmd_e cmd;
|
enum cmd_e cmd;
|
||||||
int s;
|
int s;
|
||||||
@@ -453,7 +507,7 @@ int main(int argc, char *argv[])
|
|||||||
continue;
|
continue;
|
||||||
eprint("Accept() failure, errno=%d [%s]\n",
|
eprint("Accept() failure, errno=%d [%s]\n",
|
||||||
errno, strerror(errno));
|
errno, strerror(errno));
|
||||||
exit(1);
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = recv_query(s, &ctr, &cmd);
|
rc = recv_query(s, &ctr, &cmd);
|
||||||
@@ -480,5 +534,13 @@ cleanup:
|
|||||||
close(s);
|
close(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (stopsig == SIGTERM)
|
||||||
|
eprint("Caught signal SIGTERM, terminating...\n");
|
||||||
|
else if (stopsig == SIGINT)
|
||||||
|
eprint("Caught signal SIGINT, terminating...\n");
|
||||||
|
else
|
||||||
|
eprint("Caught signal %d, terminating...\n", stopsig);
|
||||||
|
remove_pidfile();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
+615
-124
@@ -3,7 +3,7 @@
|
|||||||
*
|
*
|
||||||
* low level perf functions
|
* low level perf functions
|
||||||
*
|
*
|
||||||
* Copyright IBM Corp. 2015, 2020
|
* Copyright IBM Corp. 2015, 2022
|
||||||
*
|
*
|
||||||
* s390-tools is free software; you can redistribute it and/or modify
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the MIT license. See LICENSE for details.
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
@@ -16,14 +16,21 @@
|
|||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <linux/perf_event.h>
|
#include <linux/perf_event.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <signal.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/resource.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <libudev.h>
|
||||||
|
|
||||||
#include "cpacfstats.h"
|
#include "cpacfstats.h"
|
||||||
|
#include "../include/lib/zt_common.h"
|
||||||
|
|
||||||
/* correlation between counter and perf counter string */
|
/* correlation between counter and perf counter string */
|
||||||
static const struct {
|
static const struct {
|
||||||
@@ -38,25 +45,83 @@ static const struct {
|
|||||||
{"cpum_cf", "ECC_FUNCTION_COUNT", ECC_FUNCTIONS}
|
{"cpum_cf", "ECC_FUNCTION_COUNT", ECC_FUNCTIONS}
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
static struct pmf_data {
|
||||||
* We need one filedescriptor per CPU per counter.
|
int pmutype;
|
||||||
* So perf_init builds this:
|
int eventid;
|
||||||
*
|
} pmf_counter_data[ALL_COUNTER];
|
||||||
* ctr_fds - is an array of pointers to file descriptor arrays.
|
|
||||||
* Each file descriptor array has space for number of logical CPUs + 1
|
|
||||||
* filedescriptors (int values). The last element of each file descriptor
|
|
||||||
* array is always 0, assuming there will never appear a filedescriptor
|
|
||||||
* with value 0:
|
|
||||||
*
|
|
||||||
* ctr_fds:
|
|
||||||
* ctr_fds[0] -> [file descriptor 0] [fd1] ... [fd cpus-1][0]
|
|
||||||
* ctr_fds[1] -> [file descriptor 0] [fd1] ... [fd cpus-1][0]
|
|
||||||
* ...
|
|
||||||
* ctr_fds[ALL_COUNTER-1] -> [file descriptor 0] [fd1] ... [fd cpus-1][0]
|
|
||||||
*/
|
|
||||||
static int *ctr_fds[ALL_COUNTER];
|
|
||||||
|
|
||||||
static int ecc_supported;
|
struct percpucounter {
|
||||||
|
int ctr_fds[ALL_COUNTER];
|
||||||
|
int pai_user[NUM_PAI_USER];
|
||||||
|
int pai_kernel[NUM_PAI_KERNEL];
|
||||||
|
unsigned int cpunum;
|
||||||
|
struct percpucounter *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct percpucounter *root;
|
||||||
|
pthread_mutex_t rootmux = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
static volatile int hotplugdetected;
|
||||||
|
static unsigned int enabledcounter;
|
||||||
|
pthread_t hotplugthread;
|
||||||
|
|
||||||
|
#define foreachcpu(PCPU) if (pthread_mutex_lock(&rootmux)) return -1; \
|
||||||
|
for ((PCPU) = root; (PCPU) != NULL; (PCPU) = (PCPU)->next)
|
||||||
|
#define endforeachcpu() pthread_mutex_unlock(&rootmux)
|
||||||
|
|
||||||
|
static int ctr_state[NUM_COUNTER];
|
||||||
|
|
||||||
|
static volatile int stoprequested;
|
||||||
|
|
||||||
|
static int paipmutype, paipmueventstart;
|
||||||
|
|
||||||
|
static struct percpucounter *allocpercpucounter(unsigned int cpunum)
|
||||||
|
{
|
||||||
|
struct percpucounter *ppc;
|
||||||
|
|
||||||
|
ppc = malloc(sizeof(struct percpucounter));
|
||||||
|
if (ppc) {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < ALL_COUNTER; ++i)
|
||||||
|
ppc->ctr_fds[i] = -1;
|
||||||
|
for (i = 0; i < NUM_PAI_USER; ++i)
|
||||||
|
ppc->pai_user[i] = -1;
|
||||||
|
for (i = 0; i < NUM_PAI_KERNEL; ++i)
|
||||||
|
ppc->pai_kernel[i] = -1;
|
||||||
|
ppc->cpunum = cpunum;
|
||||||
|
ppc->next = NULL;
|
||||||
|
}
|
||||||
|
return ppc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void freepercpucounter(struct percpucounter *pcpu)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < ALL_COUNTER; ++i)
|
||||||
|
(void)close(pcpu->ctr_fds[i]);
|
||||||
|
for (i = 0; i < NUM_PAI_USER; ++i)
|
||||||
|
(void)close(pcpu->pai_user[i]);
|
||||||
|
for (i = 0; i < NUM_PAI_KERNEL; ++i)
|
||||||
|
(void)close(pcpu->pai_kernel[i]);
|
||||||
|
free(pcpu);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct percpucounter *findcpu(unsigned int cpunum, int unlinkflag)
|
||||||
|
{
|
||||||
|
struct percpucounter **prev = &root, *walk = root;
|
||||||
|
|
||||||
|
while (walk) {
|
||||||
|
if (walk->cpunum == cpunum) {
|
||||||
|
if (unlinkflag)
|
||||||
|
*prev = walk->next;
|
||||||
|
return walk;
|
||||||
|
}
|
||||||
|
prev = &(walk->next);
|
||||||
|
walk = walk->next;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid,
|
static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid,
|
||||||
int cpu, int group_fd, unsigned long flags)
|
int cpu, int group_fd, unsigned long flags)
|
||||||
@@ -85,12 +150,43 @@ static int perf_counter_supported(const char *pmu, const char *counter)
|
|||||||
return !access(buf, R_OK);
|
return !access(buf, R_OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int perf_event_encode(struct perf_event_attr *attr,
|
static int cpumf_authorized(void)
|
||||||
|
{
|
||||||
|
unsigned vermin, vermax, auth;
|
||||||
|
int res = 0, found = 0;
|
||||||
|
size_t linesize = 0;
|
||||||
|
char *line = NULL;
|
||||||
|
FILE *f;
|
||||||
|
|
||||||
|
f = fopen("/proc/service_levels", "r");
|
||||||
|
if (f == NULL) {
|
||||||
|
eprint("Failed to open /proc/service_levels (%d:%s)\n",
|
||||||
|
errno, strerror(errno));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
while (getline(&line, &linesize, f) >= 0) {
|
||||||
|
if (sscanf(line,
|
||||||
|
"CPU-MF: Counter facility: version=%d.%d authorization=%x",
|
||||||
|
&vermin, &vermax, &auth) == 3) {
|
||||||
|
if (auth & 0x8)
|
||||||
|
res = 1;
|
||||||
|
else
|
||||||
|
eprint("CPU-MF counters not authorized.\n");
|
||||||
|
found = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found)
|
||||||
|
eprint("CPU-MF counters not available.\n");
|
||||||
|
free(line);
|
||||||
|
fclose(f);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int perf_event_encode(int *pmutype, int *eventid,
|
||||||
const char *pmu, const char *event)
|
const char *pmu, const char *event)
|
||||||
{
|
{
|
||||||
FILE *f;
|
FILE *f;
|
||||||
int eventid;
|
|
||||||
int pmutype;
|
|
||||||
char buf[PATH_MAX];
|
char buf[PATH_MAX];
|
||||||
|
|
||||||
if (snprintf(buf, PATH_MAX, "/sys/bus/event_source/devices/%s/events/%s",
|
if (snprintf(buf, PATH_MAX, "/sys/bus/event_source/devices/%s/events/%s",
|
||||||
@@ -104,7 +200,7 @@ static int perf_event_encode(struct perf_event_attr *attr,
|
|||||||
errno, strerror(errno));
|
errno, strerror(errno));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (fscanf(f, "event=0x%x\n", &eventid) != 1) {
|
if (fscanf(f, "event=0x%x\n", eventid) != 1) {
|
||||||
fclose(f);
|
fclose(f);
|
||||||
eprint("Event file %s has invalid format\n", buf);
|
eprint("Event file %s has invalid format\n", buf);
|
||||||
return -1;
|
return -1;
|
||||||
@@ -121,21 +217,257 @@ static int perf_event_encode(struct perf_event_attr *attr,
|
|||||||
errno, strerror(errno));
|
errno, strerror(errno));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (fscanf(f, "%d\n", &pmutype) != 1) {
|
if (fscanf(f, "%d\n", pmutype) != 1) {
|
||||||
fclose(f);
|
fclose(f);
|
||||||
eprint("Type file %s has invalid format\n", buf);
|
eprint("Type file %s has invalid format\n", buf);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
attr->type = pmutype;
|
|
||||||
attr->config = eventid;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int activatecpu(unsigned int cpu)
|
||||||
|
{
|
||||||
|
struct perf_event_attr pfm_event;
|
||||||
|
struct percpucounter *ppc;
|
||||||
|
int fd, i, rc = 0;
|
||||||
|
|
||||||
|
ppc = allocpercpucounter(cpu);
|
||||||
|
if (ppc == NULL) {
|
||||||
|
eprint("Failed to allocate per cpu counter data");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (pthread_mutex_lock(&rootmux)) {
|
||||||
|
freepercpucounter(ppc);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
/* activate CPU-MF */
|
||||||
|
for (i = 0; i < ALL_COUNTER; ++i) {
|
||||||
|
if (ctr_state[i] == UNSUPPORTED)
|
||||||
|
continue;
|
||||||
|
memset(&pfm_event, 0, sizeof(pfm_event));
|
||||||
|
pfm_event.size = sizeof(pfm_event);
|
||||||
|
pfm_event.type = pmf_counter_data[i].pmutype;
|
||||||
|
pfm_event.config = pmf_counter_data[i].eventid;
|
||||||
|
|
||||||
|
/* fetch file descriptor for this perf event
|
||||||
|
* the counter event should start disabled
|
||||||
|
*/
|
||||||
|
pfm_event.disabled = ctr_state[i] == DISABLED;
|
||||||
|
fd = perf_event_open(
|
||||||
|
&pfm_event,
|
||||||
|
-1, /* pid -1 means all processes */
|
||||||
|
cpu,
|
||||||
|
-1, /* group filedescriptor */
|
||||||
|
0); /* flags */
|
||||||
|
if (fd < 0) {
|
||||||
|
eprint("Perf_event_open() failed with errno=%d [%s]\n",
|
||||||
|
errno, strerror(errno));
|
||||||
|
rc = -1;
|
||||||
|
ctr_state[i] = UNSUPPORTED;
|
||||||
|
} else {
|
||||||
|
ppc->ctr_fds[i] = fd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* activate pai_user and pai_kernel */
|
||||||
|
/* invariant:
|
||||||
|
(ctr_state[PAI_USER] == UNSUPPORTED) ==
|
||||||
|
(ctr_state[PAI_KERNEL] == UNSUPPORTED) */
|
||||||
|
if (ctr_state[PAI_USER] != UNSUPPORTED) {
|
||||||
|
for (i = 1; i <= NUM_PAI_USER; ++i) {
|
||||||
|
memset(&pfm_event, 0, sizeof(pfm_event));
|
||||||
|
pfm_event.size = sizeof(pfm_event);
|
||||||
|
pfm_event.type = paipmutype;
|
||||||
|
pfm_event.config = paipmueventstart + i;
|
||||||
|
pfm_event.exclude_kernel = 1;
|
||||||
|
pfm_event.exclude_user = 0;
|
||||||
|
pfm_event.disabled = ctr_state[PAI_USER] == DISABLED;
|
||||||
|
fd = perf_event_open(&pfm_event, -1, cpu, -1, 0);
|
||||||
|
if (fd < 0) {
|
||||||
|
eprint("Perf_event_open() failed with errno=%d [%s]\n",
|
||||||
|
errno, strerror(errno));
|
||||||
|
rc = -1;
|
||||||
|
ctr_state[PAI_USER] = UNSUPPORTED;
|
||||||
|
ctr_state[PAI_KERNEL] = UNSUPPORTED;
|
||||||
|
goto outevents;
|
||||||
|
} else {
|
||||||
|
ppc->pai_user[i - 1] = fd;
|
||||||
|
}
|
||||||
|
pfm_event.exclude_kernel = 0;
|
||||||
|
pfm_event.exclude_user = 1;
|
||||||
|
pfm_event.disabled = ctr_state[PAI_KERNEL] == DISABLED;
|
||||||
|
fd = perf_event_open(&pfm_event, -1, cpu, -1, 0);
|
||||||
|
if (fd < 0) {
|
||||||
|
eprint("Perf_event_open() failed with errno=%d [%s]\n",
|
||||||
|
errno, strerror(errno));
|
||||||
|
rc = -1;
|
||||||
|
ctr_state[PAI_USER] = UNSUPPORTED;
|
||||||
|
ctr_state[PAI_KERNEL] = UNSUPPORTED;
|
||||||
|
goto outevents;
|
||||||
|
} else {
|
||||||
|
ppc->pai_kernel[i - 1] = fd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (; i <= NUM_PAI_KERNEL; ++i) {
|
||||||
|
memset(&pfm_event, 0, sizeof(pfm_event));
|
||||||
|
pfm_event.size = sizeof(pfm_event);
|
||||||
|
pfm_event.type = paipmutype;
|
||||||
|
pfm_event.config = paipmueventstart + i;
|
||||||
|
pfm_event.exclude_kernel = 0;
|
||||||
|
pfm_event.exclude_user = 1;
|
||||||
|
pfm_event.disabled = ctr_state[PAI_KERNEL] == DISABLED;
|
||||||
|
fd = perf_event_open(&pfm_event, -1, cpu, -1, 0);
|
||||||
|
if (fd < 0) {
|
||||||
|
eprint("Perf_event_open() failed with errno=%d [%s]\n",
|
||||||
|
errno, strerror(errno));
|
||||||
|
rc = -1;
|
||||||
|
ctr_state[PAI_USER] = UNSUPPORTED;
|
||||||
|
ctr_state[PAI_KERNEL] = UNSUPPORTED;
|
||||||
|
goto outevents;
|
||||||
|
} else {
|
||||||
|
ppc->pai_kernel[i - 1] = fd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
outevents:
|
||||||
|
ppc->next = root;
|
||||||
|
root = ppc;
|
||||||
|
if (enabledcounter)
|
||||||
|
hotplugdetected = 1;
|
||||||
|
pthread_mutex_unlock(&rootmux);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void deactivatecpu(unsigned int cpunum)
|
||||||
|
{
|
||||||
|
struct percpucounter *pcpu;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (pthread_mutex_lock(&rootmux))
|
||||||
|
return;
|
||||||
|
pcpu = findcpu(cpunum, 1);
|
||||||
|
if (pcpu != NULL) {
|
||||||
|
for (i = 0; i < ALL_COUNTER; ++i)
|
||||||
|
(void)close(pcpu->ctr_fds[i]);
|
||||||
|
for (i = 0; i < NUM_PAI_USER; ++i)
|
||||||
|
(void)close(pcpu->pai_user[i]);
|
||||||
|
for (i = 0; i < NUM_PAI_KERNEL; ++i)
|
||||||
|
(void)close(pcpu->pai_kernel[i]);
|
||||||
|
free(pcpu);
|
||||||
|
if (enabledcounter)
|
||||||
|
hotplugdetected = 1;
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&rootmux);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int addallcpus(void)
|
||||||
|
{
|
||||||
|
unsigned int start, end;
|
||||||
|
int scanned, rc = 0;
|
||||||
|
FILE *fp;
|
||||||
|
|
||||||
|
/* comma separated list of intervals */
|
||||||
|
if ((fp = fopen("/sys/devices/system/cpu/online", "r")) == NULL) {
|
||||||
|
eprint("Failed to get online cpus (%d:%s)\n",
|
||||||
|
errno, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!feof(fp)) {
|
||||||
|
/* scan all intervals of online cpus */
|
||||||
|
scanned = fscanf(fp, "%u-%u", &start, &end);
|
||||||
|
/* take care of singleton intervals */
|
||||||
|
if (scanned == 1)
|
||||||
|
end = start;
|
||||||
|
for (; start <= end; ++start) {
|
||||||
|
if (activatecpu(start)) {
|
||||||
|
rc = -1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Skip comma separator */
|
||||||
|
(void)fgetc(fp);
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
fclose(fp);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int perf_load_counter_data(void)
|
||||||
|
{
|
||||||
|
int i, res = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < ALL_COUNTER; ++i) {
|
||||||
|
if (ctr_state[i] != UNSUPPORTED)
|
||||||
|
res |= perf_event_encode(&pmf_counter_data[i].pmutype,
|
||||||
|
&pmf_counter_data[i].eventid,
|
||||||
|
pmf_counter_name[i].pmu,
|
||||||
|
pmf_counter_name[i].pfm_name);
|
||||||
|
}
|
||||||
|
if (ctr_state[PAI_USER] != UNSUPPORTED)
|
||||||
|
res |= perf_event_encode(&paipmutype, &paipmueventstart,
|
||||||
|
"pai_crypto", "CRYPTO_ALL");
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *hotplughandler(void *UNUSED(unused))
|
||||||
|
{
|
||||||
|
struct udev *hotplug;
|
||||||
|
struct udev_monitor *monitor;
|
||||||
|
struct pollfd item;
|
||||||
|
|
||||||
|
hotplug = udev_new();
|
||||||
|
if (!hotplug) {
|
||||||
|
eprint("Failed to create hotplug device\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
monitor = udev_monitor_new_from_netlink(hotplug, "udev");
|
||||||
|
udev_monitor_filter_add_match_subsystem_devtype(monitor, "cpu", NULL);
|
||||||
|
udev_monitor_enable_receiving(monitor);
|
||||||
|
item.fd = udev_monitor_get_fd(monitor);
|
||||||
|
item.events = POLLIN;
|
||||||
|
item.revents = 0;
|
||||||
|
while (!stoprequested) {
|
||||||
|
struct udev_device *dev;
|
||||||
|
const char *path, *action;
|
||||||
|
unsigned int cpunum;
|
||||||
|
int rc, on, off;
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
rc = poll(&item, 1, -1);
|
||||||
|
if (rc == -1) {
|
||||||
|
if (errno == EINTR)
|
||||||
|
continue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
dev = udev_monitor_receive_device(monitor);
|
||||||
|
if (dev == NULL)
|
||||||
|
continue;
|
||||||
|
action = udev_device_get_action(dev);
|
||||||
|
if (action == NULL)
|
||||||
|
continue;
|
||||||
|
off = strcmp(action, "offline") == 0;
|
||||||
|
on = strcmp(action, "online") == 0;
|
||||||
|
if (!on && !off)
|
||||||
|
continue;
|
||||||
|
path = udev_device_get_devpath(dev);
|
||||||
|
if (sscanf(path, "/devices/system/cpu/cpu%u", &cpunum) != 1)
|
||||||
|
continue;
|
||||||
|
if (on && activatecpu(cpunum))
|
||||||
|
eprint("Failed to attach to hotplugged CPU %u\n", cpunum);
|
||||||
|
if (off)
|
||||||
|
deactivatecpu(cpunum);
|
||||||
|
}
|
||||||
|
udev_monitor_unref(monitor);
|
||||||
|
udev_unref(hotplug);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
int perf_init(void)
|
int perf_init(void)
|
||||||
{
|
{
|
||||||
int i, cpus, ctr, cpu, *fds;
|
int ecc_supported, i, num;
|
||||||
|
unsigned long maxfd;
|
||||||
memset(ctr_fds, 0, sizeof(ctr_fds));
|
struct rlimit rlim;
|
||||||
|
FILE *f;
|
||||||
|
|
||||||
/* initialize performance monitoring library */
|
/* initialize performance monitoring library */
|
||||||
if (!perf_supported()) {
|
if (!perf_supported()) {
|
||||||
@@ -143,99 +475,105 @@ int perf_init(void)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* We currently support all cpumf counters plus two virtual
|
||||||
|
* counters for PAI. */
|
||||||
|
num = ALL_COUNTER + 2;
|
||||||
/* Check if ECC is supported on current hardware */
|
/* Check if ECC is supported on current hardware */
|
||||||
ecc_supported = perf_counter_supported("cpum_cf", "ECC_FUNCTION_COUNT");
|
ecc_supported = perf_counter_supported("cpum_cf", "ECC_FUNCTION_COUNT");
|
||||||
|
|
||||||
/* get number of logical processors */
|
if (!cpumf_authorized()) {
|
||||||
cpus = sysconf(_SC_NPROCESSORS_ONLN);
|
for (i = 0; i < ALL_COUNTER; ++i)
|
||||||
|
ctr_state[i] = UNSUPPORTED;
|
||||||
/* for each counter */
|
num -= ALL_COUNTER;
|
||||||
for (ctr = 0; ctr < ALL_COUNTER; ctr++) {
|
} else if (!ecc_supported) {
|
||||||
|
ctr_state[ECC_FUNCTIONS] = UNSUPPORTED;
|
||||||
/* Skip ECC counters completely if unsupported */
|
--num;
|
||||||
if (ctr == ECC_FUNCTIONS && !ecc_supported)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* allocate an array of ints to store for each CPU
|
|
||||||
* one filedescriptor + a terminating 0
|
|
||||||
*/
|
|
||||||
fds = (int *) calloc(sizeof(int), cpus+1);
|
|
||||||
if (!fds) {
|
|
||||||
eprint("Malloc() of %d byte failed, errno=%d [%s]\n",
|
|
||||||
(int)(sizeof(int) * (cpus+1)),
|
|
||||||
errno, strerror(errno));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
ctr_fds[ctr] = fds;
|
|
||||||
|
|
||||||
/* search for the counter's corresponding pfm name */
|
|
||||||
for (i = ALL_COUNTER-1; i >= 0; i--)
|
|
||||||
if ((int) pmf_counter_name[i].ctr == ctr)
|
|
||||||
break;
|
|
||||||
if (i < 0) {
|
|
||||||
eprint("Pfm ctr name not found for counter %d, please adjust pmf_counter_name[] in %s\n",
|
|
||||||
ctr, __FILE__);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (cpu = 0; cpu < cpus; cpu++) {
|
|
||||||
struct perf_event_attr pfm_event;
|
|
||||||
int fd;
|
|
||||||
|
|
||||||
memset(&pfm_event, 0, sizeof(pfm_event));
|
|
||||||
pfm_event.size = sizeof(pfm_event);
|
|
||||||
if (perf_event_encode(&pfm_event,
|
|
||||||
pmf_counter_name[i].pmu,
|
|
||||||
pmf_counter_name[i].pfm_name)) {
|
|
||||||
eprint("Failed to initialize counter %s for pmu %s\n",
|
|
||||||
pmf_counter_name[i].pfm_name,
|
|
||||||
pmf_counter_name[i].pmu);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* fetch file descriptor for this perf event
|
|
||||||
* the counter event should start disabled
|
|
||||||
*/
|
|
||||||
pfm_event.disabled = 1;
|
|
||||||
fd = perf_event_open(
|
|
||||||
&pfm_event,
|
|
||||||
-1, /* pid -1 means all processes */
|
|
||||||
cpu,
|
|
||||||
-1, /* group filedescriptor */
|
|
||||||
0); /* flags */
|
|
||||||
if (fd < 0) {
|
|
||||||
eprint("Perf_event_open() failed with errno=%d [%s]\n",
|
|
||||||
errno, strerror(errno));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
fds[cpu] = fd;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
if (!perf_counter_supported("pai_crypto", "CRYPTO_ALL")) {
|
||||||
|
ctr_state[PAI_USER] = UNSUPPORTED;
|
||||||
|
ctr_state[PAI_KERNEL] = UNSUPPORTED;
|
||||||
|
num -= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (num == 0) {
|
||||||
|
eprint("No crypto counters supported!\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (perf_load_counter_data())
|
||||||
|
return -1;
|
||||||
|
/* We have to adjust the number of FDs possible since we might
|
||||||
|
* need more than 1024 (the typical soft limit) */
|
||||||
|
f = fopen("/proc/sys/fs/nr_open", "r");
|
||||||
|
if (f == NULL) {
|
||||||
|
eprint("fopen failed for /proc/sys/fs/nr_open with errno=%d [%s]\n",
|
||||||
|
errno, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (fscanf(f, "%lu", &maxfd) != 1) {
|
||||||
|
fclose(f);
|
||||||
|
eprint("Failed to parse /proc/sys/fs/nr_open\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
fclose(f);
|
||||||
|
rlim.rlim_cur = maxfd;
|
||||||
|
rlim.rlim_max = maxfd;
|
||||||
|
if (setrlimit(RLIMIT_NOFILE, &rlim) == -1) {
|
||||||
|
eprint("setrlimit failed with errno=%d [%s]\n",
|
||||||
|
errno, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pthread_create(&hotplugthread, NULL, hotplughandler, NULL)) {
|
||||||
|
eprint("Failed to start hotplug handler thread\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return addallcpus();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void perf_stop(void)
|
||||||
|
{
|
||||||
|
stoprequested = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void perf_close(void)
|
void perf_close(void)
|
||||||
{
|
{
|
||||||
int ctr, *fds;
|
struct percpucounter *walk, *next;
|
||||||
|
|
||||||
for (ctr = 0; ctr < ALL_COUNTER; ctr++) {
|
pthread_kill(hotplugthread, SIGINT);
|
||||||
for (fds = ctr_fds[ctr]; fds && *fds; fds++) {
|
pthread_join(hotplugthread, NULL);
|
||||||
close(*fds);
|
walk = root;
|
||||||
*fds = 0;
|
while (walk) {
|
||||||
}
|
next = walk->next;
|
||||||
free(ctr_fds[ctr]);
|
freepercpucounter(walk);
|
||||||
ctr_fds[ctr] = NULL;
|
walk = next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int enable_array(int *arr, int size)
|
||||||
|
{
|
||||||
|
int i, ec, rc = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < size; ++i) {
|
||||||
|
ec = ioctl(arr[i], PERF_EVENT_IOC_ENABLE, 0);
|
||||||
|
if (ec < 0) {
|
||||||
|
eprint("Ioctl(PERF_EVENT_IOC_ENABLE) failed with errno=%d [%s]\n",
|
||||||
|
errno, strerror(errno));
|
||||||
|
rc = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int perf_enable_ctr(enum ctr_e ctr)
|
int perf_enable_ctr(enum ctr_e ctr)
|
||||||
{
|
{
|
||||||
int *fds, ec, rc = 0;
|
struct percpucounter *pcpu;
|
||||||
|
int ec, rc = 0;
|
||||||
|
|
||||||
if (ctr == ALL_COUNTER) {
|
if (ctr == ALL_COUNTER) {
|
||||||
for (ctr = 0; ctr < ALL_COUNTER; ctr++) {
|
for (ctr = 0; ctr < ALL_COUNTER; ctr++) {
|
||||||
@@ -243,24 +581,64 @@ int perf_enable_ctr(enum ctr_e ctr)
|
|||||||
if (rc != 0)
|
if (rc != 0)
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
} else {
|
} else if (ctr < ALL_COUNTER) {
|
||||||
for (fds = ctr_fds[ctr]; fds && *fds; fds++) {
|
foreachcpu(pcpu) {
|
||||||
ec = ioctl(*fds, PERF_EVENT_IOC_ENABLE, 0);
|
ec = ioctl(pcpu->ctr_fds[ctr], PERF_EVENT_IOC_ENABLE, 0);
|
||||||
if (ec < 0) {
|
if (ec < 0) {
|
||||||
eprint("Ioctl(PERF_EVENT_IOC_ENABLE) failed with errno=%d [%s]\n",
|
eprint("Ioctl(PERF_EVENT_IOC_ENABLE) failed with errno=%d [%s]\n",
|
||||||
errno, strerror(errno));
|
errno, strerror(errno));
|
||||||
rc = -1;
|
rc = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ctr_state[ctr] = ENABLED;
|
||||||
|
++enabledcounter;
|
||||||
|
endforeachcpu();
|
||||||
|
} else if (ctr == PAI_USER) {
|
||||||
|
foreachcpu(pcpu) {
|
||||||
|
ec = enable_array(pcpu->pai_user, NUM_PAI_USER);
|
||||||
|
if (ec < 0)
|
||||||
|
rc = -1;
|
||||||
|
}
|
||||||
|
ctr_state[ctr] = ENABLED;
|
||||||
|
++enabledcounter;
|
||||||
|
endforeachcpu();
|
||||||
|
} else if (ctr == PAI_KERNEL) {
|
||||||
|
foreachcpu(pcpu) {
|
||||||
|
ec = enable_array(pcpu->pai_kernel, NUM_PAI_KERNEL);
|
||||||
|
if (ec < 0)
|
||||||
|
rc = -1;
|
||||||
|
}
|
||||||
|
ctr_state[ctr] = ENABLED;
|
||||||
|
++enabledcounter;
|
||||||
|
endforeachcpu();
|
||||||
|
} else {
|
||||||
|
rc = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int disable_array(int *arr, int size)
|
||||||
|
{
|
||||||
|
int i, ec, rc = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < size; ++i) {
|
||||||
|
ec = ioctl(arr[i], PERF_EVENT_IOC_DISABLE, 0);
|
||||||
|
if (ec < 0) {
|
||||||
|
eprint("Ioctl(PERF_EVENT_IOC_DISABLE) failed with errno=%d [%s]\n",
|
||||||
|
errno, strerror(errno));
|
||||||
|
rc = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int perf_disable_ctr(enum ctr_e ctr)
|
int perf_disable_ctr(enum ctr_e ctr)
|
||||||
{
|
{
|
||||||
int *fds, ec, rc = 0;
|
struct percpucounter *pcpu;
|
||||||
|
int ec, rc = 0;
|
||||||
|
|
||||||
if (ctr == ALL_COUNTER) {
|
if (ctr == ALL_COUNTER) {
|
||||||
for (ctr = 0; ctr < ALL_COUNTER; ctr++) {
|
for (ctr = 0; ctr < ALL_COUNTER; ctr++) {
|
||||||
@@ -268,70 +646,183 @@ int perf_disable_ctr(enum ctr_e ctr)
|
|||||||
if (rc != 0)
|
if (rc != 0)
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
} else {
|
} else if (ctr < ALL_COUNTER) {
|
||||||
for (fds = ctr_fds[ctr]; fds && *fds; fds++) {
|
foreachcpu(pcpu) {
|
||||||
ec = ioctl(*fds, PERF_EVENT_IOC_DISABLE, 0);
|
ec = ioctl(pcpu->ctr_fds[ctr], PERF_EVENT_IOC_DISABLE, 0);
|
||||||
if (ec < 0) {
|
if (ec < 0) {
|
||||||
eprint("Ioctl(PERF_EVENT_IOC_DISABLE) failed with errno=%d [%s]\n",
|
eprint("Ioctl(PERF_EVENT_IOC_DISABLE) failed with errno=%d [%s]\n",
|
||||||
errno, strerror(errno));
|
errno, strerror(errno));
|
||||||
rc = -1;
|
rc = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ctr_state[ctr] = DISABLED;
|
||||||
|
--enabledcounter;
|
||||||
|
if (enabledcounter == 0)
|
||||||
|
hotplugdetected = 0;
|
||||||
|
endforeachcpu();
|
||||||
|
} else if (ctr == PAI_USER) {
|
||||||
|
foreachcpu(pcpu) {
|
||||||
|
ec = disable_array(pcpu->pai_user, NUM_PAI_USER);
|
||||||
|
if (ec < 0)
|
||||||
|
rc = -1;
|
||||||
|
}
|
||||||
|
ctr_state[ctr] = DISABLED;
|
||||||
|
--enabledcounter;
|
||||||
|
if (enabledcounter == 0)
|
||||||
|
hotplugdetected = 0;
|
||||||
|
endforeachcpu();
|
||||||
|
} else if (ctr == PAI_KERNEL) {
|
||||||
|
foreachcpu(pcpu) {
|
||||||
|
ec = disable_array(pcpu->pai_kernel, NUM_PAI_KERNEL);
|
||||||
|
if (ec < 0)
|
||||||
|
rc = -1;
|
||||||
|
}
|
||||||
|
ctr_state[ctr] = DISABLED;
|
||||||
|
--enabledcounter;
|
||||||
|
if (enabledcounter == 0)
|
||||||
|
hotplugdetected = 0;
|
||||||
|
endforeachcpu();
|
||||||
|
} else {
|
||||||
|
rc = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int perf_reset_ctr(enum ctr_e ctr)
|
static int reset_array(int *arr, int size)
|
||||||
{
|
{
|
||||||
int *fds, ec, rc = 0;
|
int ec, rc = 0, i;
|
||||||
|
|
||||||
|
for (i = 0; i < size; ++i) {
|
||||||
|
ec = ioctl(arr[i], PERF_EVENT_IOC_RESET, 0);
|
||||||
|
if (ec < 0) {
|
||||||
|
eprint("Ioctl(PERF_EVENT_IOC_RESET) failed with errno=%d [%s]\n",
|
||||||
|
errno, strerror(errno));
|
||||||
|
rc = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int perf_reset_ctr(enum ctr_e ctr, uint64_t *value)
|
||||||
|
{
|
||||||
|
struct percpucounter *pcpu;
|
||||||
|
int ec, rc = 0;
|
||||||
|
|
||||||
if (ctr == ALL_COUNTER) {
|
if (ctr == ALL_COUNTER) {
|
||||||
for (ctr = 0; ctr < ALL_COUNTER; ctr++) {
|
for (ctr = 0; ctr < ALL_COUNTER; ctr++) {
|
||||||
rc = perf_reset_ctr(ctr);
|
rc = perf_reset_ctr(ctr, value);
|
||||||
if (rc != 0)
|
if (rc != 0)
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
} else {
|
} else if (ctr < ALL_COUNTER) {
|
||||||
for (fds = ctr_fds[ctr]; fds && *fds; fds++) {
|
foreachcpu(pcpu) {
|
||||||
ec = ioctl(*fds, PERF_EVENT_IOC_RESET, 0);
|
ec = ioctl(pcpu->ctr_fds[ctr], PERF_EVENT_IOC_RESET, 0);
|
||||||
if (ec < 0) {
|
if (ec < 0) {
|
||||||
eprint("Ioctl(PERF_EVENT_IOC_RESET) failed with errno=%d [%s]\n",
|
eprint("Ioctl(PERF_EVENT_IOC_RESET) failed with errno=%d [%s]\n",
|
||||||
errno, strerror(errno));
|
errno, strerror(errno));
|
||||||
rc = -1;
|
rc = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
endforeachcpu();
|
||||||
|
} else if (ctr == PAI_USER) {
|
||||||
|
foreachcpu(pcpu) {
|
||||||
|
ec = reset_array(pcpu->pai_user, NUM_PAI_USER);
|
||||||
|
if (ec < 0)
|
||||||
|
rc = -1;
|
||||||
|
}
|
||||||
|
endforeachcpu();
|
||||||
|
} else if (ctr == PAI_KERNEL) {
|
||||||
|
foreachcpu(pcpu) {
|
||||||
|
ec = reset_array(pcpu->pai_kernel, NUM_PAI_KERNEL);
|
||||||
|
if (ec < 0)
|
||||||
|
rc = -1;
|
||||||
|
}
|
||||||
|
endforeachcpu();
|
||||||
|
} else {
|
||||||
|
rc = -1;
|
||||||
}
|
}
|
||||||
|
if (rc == 0)
|
||||||
|
rc = perf_read_ctr(ctr, value);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int perf_read_ctr(enum ctr_e ctr, uint64_t *value)
|
int perf_read_ctr(enum ctr_e ctr, uint64_t *value)
|
||||||
{
|
{
|
||||||
int *fds, ec, rc = -1;
|
struct percpucounter *pcpu;
|
||||||
|
int ec, rc = 0;
|
||||||
uint64_t val;
|
uint64_t val;
|
||||||
|
|
||||||
if (!value)
|
if (!value)
|
||||||
return -1;
|
return -1;
|
||||||
|
if (ctr == HOTPLUG_DETECTED) {
|
||||||
|
*value = hotplugdetected;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (ctr == PAI_USER) {
|
||||||
|
*value = NUM_PAI_USER;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (ctr == PAI_KERNEL) {
|
||||||
|
*value = NUM_PAI_KERNEL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (ctr >= ALL_COUNTER)
|
||||||
|
return -1;
|
||||||
*value = 0;
|
*value = 0;
|
||||||
|
|
||||||
for (fds = ctr_fds[ctr]; fds && *fds; fds++) {
|
foreachcpu(pcpu) {
|
||||||
ec = read(*fds, &val, sizeof(val));
|
ec = read(pcpu->ctr_fds[ctr], &val, sizeof(val));
|
||||||
if (ec != sizeof(val)) {
|
if (ec != sizeof(val)) {
|
||||||
eprint("Read() on perf file descriptor failed with errno=%d [%s]\n",
|
eprint("Read() on perf file descriptor failed with errno=%d [%s]\n",
|
||||||
errno, strerror(errno));
|
errno, strerror(errno));
|
||||||
|
rc = -1;
|
||||||
} else {
|
} else {
|
||||||
*value += val;
|
*value += val;
|
||||||
rc = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
endforeachcpu();
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
int perf_ecc_supported(void)
|
int perf_ecc_supported(void)
|
||||||
{
|
{
|
||||||
return ecc_supported;
|
return ctr_state[ECC_FUNCTIONS] != UNSUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
int perf_ctr_state(enum ctr_e ctr) {
|
||||||
|
if (ctr < NUM_COUNTER)
|
||||||
|
return ctr_state[ctr];
|
||||||
|
return UNSUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int perf_read_pai_ctr(unsigned int ctrnum, int user, uint64_t *value)
|
||||||
|
{
|
||||||
|
struct percpucounter *pcpu;
|
||||||
|
unsigned int maxctr;
|
||||||
|
int *arr, ec, rc = 0;
|
||||||
|
uint64_t val;
|
||||||
|
|
||||||
|
*value = 0;
|
||||||
|
maxctr = user ? NUM_PAI_USER : NUM_PAI_KERNEL;
|
||||||
|
if (ctrnum >= maxctr)
|
||||||
|
return -1;
|
||||||
|
foreachcpu(pcpu) {
|
||||||
|
arr = user ? pcpu->pai_user : pcpu->pai_kernel;
|
||||||
|
ec = read(arr[ctrnum], &val, sizeof(val));
|
||||||
|
if (ec != sizeof(val)) {
|
||||||
|
eprint("Read() on perf file descriptor failed with errno=%d [%s]\n",
|
||||||
|
errno, strerror(errno));
|
||||||
|
rc = -1;
|
||||||
|
} else {
|
||||||
|
*value += val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
endforeachcpu();
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
|
|||||||
+52
-6
@@ -3,7 +3,7 @@
|
|||||||
*
|
*
|
||||||
* basic socket and receive/send functions
|
* basic socket and receive/send functions
|
||||||
*
|
*
|
||||||
* Copyright IBM Corp. 2015, 2017
|
* Copyright IBM Corp. 2015, 2022
|
||||||
*
|
*
|
||||||
* s390-tools is free software; you can redistribute it and/or modify
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the MIT license. See LICENSE for details.
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
#include <endian.h>
|
#include <endian.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <grp.h>
|
#include <grp.h>
|
||||||
|
#include <poll.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
@@ -148,7 +149,51 @@ static int __read(int fd, void *buf, int buflen)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int send_msg(int sfd, struct msg *m)
|
static int __timedwrite(int fd, const void *buf, int buflen, int timeout)
|
||||||
|
{
|
||||||
|
struct pollfd pfd = { .fd = fd, .events = POLLOUT };
|
||||||
|
int i = 0, n;
|
||||||
|
|
||||||
|
while (poll(&pfd, 1, timeout) == 1) {
|
||||||
|
n = write(fd, buf + i, buflen - i);
|
||||||
|
if (n < 0) {
|
||||||
|
if (errno == EINTR)
|
||||||
|
continue;
|
||||||
|
else
|
||||||
|
return n;
|
||||||
|
} else if (n == 0) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
i += n;
|
||||||
|
if (buflen == i)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __timedread(int fd, void *buf, int buflen, int timeout)
|
||||||
|
{
|
||||||
|
struct pollfd pfd = { .fd = fd, .events = POLLIN };
|
||||||
|
int i = 0, n;
|
||||||
|
|
||||||
|
while (poll(&pfd, 1, timeout) == 1) {
|
||||||
|
n = read(fd, buf + i, buflen - i);
|
||||||
|
if (n < 0) {
|
||||||
|
if (errno == EINTR)
|
||||||
|
continue;
|
||||||
|
else
|
||||||
|
return n;
|
||||||
|
} else if (n == 0) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
i += n;
|
||||||
|
if (buflen == i)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int send_msg(int sfd, struct msg *m, int timeout)
|
||||||
{
|
{
|
||||||
int n, len;
|
int n, len;
|
||||||
|
|
||||||
@@ -166,7 +211,7 @@ int send_msg(int sfd, struct msg *m)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
n = __write(sfd, m, len);
|
n = timeout ? __timedwrite(sfd, m, len, timeout) : __write(sfd, m, len);
|
||||||
if (n != len) {
|
if (n != len) {
|
||||||
eprint("Write() error: write()=%d expected %d, errno=%d [%s]\n",
|
eprint("Write() error: write()=%d expected %d, errno=%d [%s]\n",
|
||||||
n, len, errno, strerror(errno));
|
n, len, errno, strerror(errno));
|
||||||
@@ -176,12 +221,12 @@ int send_msg(int sfd, struct msg *m)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int recv_msg(int sfd, struct msg *m)
|
int recv_msg(int sfd, struct msg *m, int timeout)
|
||||||
{
|
{
|
||||||
int n, len;
|
int n, len;
|
||||||
|
|
||||||
len = sizeof(m->head);
|
len = sizeof(m->head);
|
||||||
n = __read(sfd, m, len);
|
n = timeout ? __timedread(sfd, m, len, timeout) : __read(sfd, m, len);
|
||||||
if (n != len) {
|
if (n != len) {
|
||||||
eprint("Recv() error: read()=%d expected %d, errno=%d [%s]\n",
|
eprint("Recv() error: read()=%d expected %d, errno=%d [%s]\n",
|
||||||
n, len, errno, strerror(errno));
|
n, len, errno, strerror(errno));
|
||||||
@@ -200,7 +245,8 @@ int recv_msg(int sfd, struct msg *m)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
n = __read(sfd, ((char *)m) + sizeof(m->head), len);
|
n = timeout ? __timedread(sfd, ((char *)m) + sizeof(m->head), len, timeout) :
|
||||||
|
__read(sfd, ((char *)m) + sizeof(m->head), len);
|
||||||
if (n != len) {
|
if (n != len) {
|
||||||
eprint("Recv() error: recv()=%d expected %d, errno=%d [%s]\n",
|
eprint("Recv() error: recv()=%d expected %d, errno=%d [%s]\n",
|
||||||
n, len, errno, strerror(errno));
|
n, len, errno, strerror(errno));
|
||||||
|
|||||||
+7
-5
@@ -1,14 +1,16 @@
|
|||||||
include ../common.mak
|
include ../common.mak
|
||||||
|
|
||||||
BIN_FILES = lscpumf chcpumf
|
BIN_FILES = lscpumf chcpumf lshwc pai
|
||||||
MAN_FILES = lscpumf.1 chcpumf.8
|
MAN_FILES = lscpumf.8 chcpumf.8 lshwc.8 pai.8
|
||||||
|
|
||||||
all: $(BIN_FILES)
|
all: $(BIN_FILES)
|
||||||
|
|
||||||
libs = $(rootdir)/libutil/libutil.a
|
libs = $(rootdir)/libcpumf/libcpumf.a $(rootdir)/libutil/libutil.a
|
||||||
|
|
||||||
lscpumf: lscpumf.o $(libs)
|
lscpumf: lscpumf.o $(libs)
|
||||||
chcpumf: chcpumf.o $(libs)
|
chcpumf: chcpumf.o $(libs)
|
||||||
|
lshwc: lshwc.o $(libs)
|
||||||
|
pai: pai.o $(libs)
|
||||||
|
|
||||||
install: all install-man
|
install: all install-man
|
||||||
$(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man8
|
$(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man8
|
||||||
@@ -21,8 +23,8 @@ clean:
|
|||||||
|
|
||||||
install-man:
|
install-man:
|
||||||
for man in $(MAN_FILES); do \
|
for man in $(MAN_FILES); do \
|
||||||
msection=`echo $$man |sed 's/.*\.\([1-9]\)$$/man\1/'` ; \
|
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 man/$$man \
|
||||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 -D man/$$man $(DESTDIR)$(MANDIR)/$$msection/$$man ; \
|
$(DESTDIR)$(MANDIR)/man8 ; \
|
||||||
done
|
done
|
||||||
|
|
||||||
.PHONY: all install clean
|
.PHONY: all install clean
|
||||||
|
|||||||
+35
-77
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* chcpumf - Change CPU Measurement Facility Characteristics
|
* chcpumf - Change CPU Measurement Facility Characteristics
|
||||||
*
|
*
|
||||||
* Copyright IBM Corp. 2020
|
* Copyright IBM Corp. 2020, 2022
|
||||||
*
|
*
|
||||||
* s390-tools is free software; you can redistribute it and/or modify
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the MIT license. See LICENSE for details.
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
@@ -20,9 +20,9 @@
|
|||||||
#include "lib/util_prg.h"
|
#include "lib/util_prg.h"
|
||||||
#include "lib/util_base.h"
|
#include "lib/util_base.h"
|
||||||
|
|
||||||
#include "defines.h"
|
#include "lib/libcpumf.h"
|
||||||
|
|
||||||
static int verbose;
|
static unsigned int verbose;
|
||||||
static unsigned long min_sdb, max_sdb;
|
static unsigned long min_sdb, max_sdb;
|
||||||
|
|
||||||
static struct util_opt opt_vec[] = {
|
static struct util_opt opt_vec[] = {
|
||||||
@@ -85,62 +85,31 @@ static long parse_buffersize(char *string)
|
|||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int read_sfb(unsigned long *min, unsigned long *max)
|
static int write_sfb(unsigned int min, unsigned int max)
|
||||||
{
|
|
||||||
unsigned long cur_min_sdb, cur_max_sdb;
|
|
||||||
int rc = EXIT_SUCCESS;
|
|
||||||
FILE *fp;
|
|
||||||
|
|
||||||
if (geteuid()) {
|
|
||||||
fprintf(stderr, "Error: Must run as root\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
fp = fopen(PERF_SFB_SIZE, "r");
|
|
||||||
if (fp == NULL) {
|
|
||||||
linux_error(PERF_SFB_SIZE);
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
if (fscanf(fp, "%ld,%ld", &cur_min_sdb, &cur_max_sdb) != 2) {
|
|
||||||
fprintf(stderr, "Error: Can not parse file " PERF_SFB_SIZE
|
|
||||||
"\n");
|
|
||||||
rc = EXIT_FAILURE;
|
|
||||||
} else {
|
|
||||||
if (*min == 0)
|
|
||||||
*min = cur_min_sdb;
|
|
||||||
if (*max == 0)
|
|
||||||
*max = cur_max_sdb;
|
|
||||||
}
|
|
||||||
fclose(fp);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int write_sfb(unsigned long min, unsigned long max)
|
|
||||||
{
|
{
|
||||||
int rc = EXIT_SUCCESS;
|
int rc = EXIT_SUCCESS;
|
||||||
char text[64];
|
char text[64];
|
||||||
size_t len;
|
size_t len;
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
|
|
||||||
fp = fopen(PERF_SFB_SIZE, "w");
|
fp = fopen(S390_CPUMSF_BUFFERSZ, "w");
|
||||||
if (fp == NULL) {
|
if (!fp)
|
||||||
linux_error(PERF_SFB_SIZE);
|
err(EXIT_FAILURE, S390_CPUMSF_BUFFERSZ);
|
||||||
return EXIT_FAILURE;
|
snprintf(text, sizeof(text), "%u,%u", min, max);
|
||||||
}
|
|
||||||
snprintf(text, sizeof text, "%ld,%ld", min, max);
|
|
||||||
len = strlen(text) + 1;
|
len = strlen(text) + 1;
|
||||||
if (fwrite(text, 1, len, fp) != len) {
|
if (fwrite(text, 1, len, fp) != len) {
|
||||||
linux_error(PERF_SFB_SIZE);
|
warn(S390_CPUMSF_BUFFERSZ);
|
||||||
rc = EXIT_FAILURE;
|
rc = EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
if (fclose(fp)) {
|
if (fclose(fp)) {
|
||||||
linux_error(PERF_SFB_SIZE);
|
warn(S390_CPUMSF_BUFFERSZ);
|
||||||
rc = EXIT_FAILURE;
|
rc = EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
if (verbose && rc != EXIT_FAILURE)
|
if (verbose && rc != EXIT_FAILURE)
|
||||||
fprintf(stderr, "Sampling buffer sizes:\n"
|
warnx("Sampling buffer sizes:\n"
|
||||||
" Minimum:%7ld sample-data-blocks\n"
|
" Minimum:%7d sample-data-blocks\n"
|
||||||
" Maximum:%7ld sample-data-blocks\n",
|
" Maximum:%7d sample-data-blocks\n",
|
||||||
min, max);
|
min, max);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,61 +129,50 @@ static int parse_args(int argc, char **argv)
|
|||||||
exit(EXIT_SUCCESS);
|
exit(EXIT_SUCCESS);
|
||||||
case 'x':
|
case 'x':
|
||||||
new = parse_buffersize(optarg);
|
new = parse_buffersize(optarg);
|
||||||
if (new < 1) {
|
if (new < 1)
|
||||||
fprintf(stderr, "The specified number(s)"
|
errx(EXIT_FAILURE,
|
||||||
" are not valid\n");
|
"The specified number(s) are not valid");
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
max_sdb = new;
|
max_sdb = new;
|
||||||
action = 1;
|
action = 1;
|
||||||
break;
|
break;
|
||||||
case 'm':
|
case 'm':
|
||||||
new = parse_buffersize(optarg);
|
new = parse_buffersize(optarg);
|
||||||
if (new < 1) {
|
if (new < 1)
|
||||||
fprintf(stderr, "The specified number(s)"
|
errx(EXIT_FAILURE,
|
||||||
" are not valid\n");
|
"The specified number(s) are not valid");
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
min_sdb = new;
|
min_sdb = new;
|
||||||
action = 1;
|
action = 1;
|
||||||
break;
|
break;
|
||||||
case 'V':
|
case 'V':
|
||||||
verbose = 1;
|
verbose = 1;
|
||||||
break;
|
break;
|
||||||
case '?':
|
default:
|
||||||
fprintf(stderr, "One or more options are not valid\n");
|
util_opt_print_parse_error(opt, argv);
|
||||||
fprintf(stderr, "Try 'chcpumf --help' for more"
|
|
||||||
" information\n");
|
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!action) {
|
if (!action)
|
||||||
fprintf(stderr, "You must specify a valid option\n");
|
errx(EXIT_FAILURE, "You must specify a valid option");
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
return action;
|
return action;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
int ret = EXIT_FAILURE;
|
unsigned long my_min, my_max;
|
||||||
struct stat sbuf;
|
|
||||||
|
|
||||||
util_prg_init(&prg);
|
util_prg_init(&prg);
|
||||||
util_opt_init(opt_vec, NULL);
|
util_opt_init(opt_vec, NULL);
|
||||||
|
|
||||||
parse_args(argc, argv);
|
parse_args(argc, argv);
|
||||||
if (stat(PERF_PATH PERF_SF, &sbuf) != 0) {
|
if (geteuid())
|
||||||
fprintf(stderr,
|
errx(EXIT_FAILURE, "Must run as root");
|
||||||
"No CPU-measurement sampling facility detected\n");
|
if (!libcpumf_have_sfb())
|
||||||
return ret;
|
errx(EXIT_FAILURE,
|
||||||
}
|
"No CPU-measurement sampling facility detected");
|
||||||
if (read_sfb(&min_sdb, &max_sdb))
|
libcpumf_sfb_info(&my_min, &my_max);
|
||||||
return ret;
|
if (!min_sdb)
|
||||||
if (min_sdb >= max_sdb) {
|
min_sdb = my_min;
|
||||||
fprintf(stderr, "The specified maximum must be greater "
|
if (!max_sdb)
|
||||||
"than the minimum\n");
|
max_sdb = my_max;
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
return write_sfb(min_sdb, max_sdb);
|
return write_sfb(min_sdb, max_sdb);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
/*
|
|
||||||
* Defines for CPU Measurement Facility Characteristics
|
|
||||||
*
|
|
||||||
* Copyright IBM Corp. 2020
|
|
||||||
*
|
|
||||||
* s390-tools is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the MIT license. See LICENSE for details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef DEFINES_H
|
|
||||||
#define DEFINES_H
|
|
||||||
|
|
||||||
#define PERF_SFB_SIZE "/sys/module/kernel/parameters/cpum_sfb_size"
|
|
||||||
#define PERF_PATH "/sys/bus/event_source/devices/"
|
|
||||||
#define PERF_SF "cpum_sf"
|
|
||||||
#define PERF_CF "cpum_cf"
|
|
||||||
|
|
||||||
static inline void linux_error(const char *message)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Error: %s: %s\n", message, strerror(errno));
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
+660
-224
File diff suppressed because it is too large
Load Diff
+703
@@ -0,0 +1,703 @@
|
|||||||
|
/* Copyright IBM Corp. 2021
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* CPU Measurements counter facility counter sets can be extracted by a
|
||||||
|
* device driver accessible by opening device /dev/hwctr.
|
||||||
|
* This program extracts complete counter set using this device.
|
||||||
|
* Counter sets are per CPU, the interface allows to specify counter sets
|
||||||
|
* for individual CPUs. The supported flags are executed from left to
|
||||||
|
* right, the first error encountered stops the execution of the program.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <err.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <linux/limits.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/user.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "lib/util_opt.h"
|
||||||
|
#include "lib/util_prg.h"
|
||||||
|
#include "lib/util_base.h"
|
||||||
|
#include "lib/util_path.h"
|
||||||
|
#include "lib/util_scandir.h"
|
||||||
|
#include "lib/util_libc.h"
|
||||||
|
#include "lib/util_file.h"
|
||||||
|
#include "lib/libcpumf.h"
|
||||||
|
|
||||||
|
#include "lshwc.h"
|
||||||
|
|
||||||
|
#define CPUS_ONLINE "/sys/devices/system/cpu/online"
|
||||||
|
#define CPUS_POSSIBLE "/sys/devices/system/cpu/possible"
|
||||||
|
#define CPUS_KERNELMAX "/sys/devices/system/cpu/kernel_max"
|
||||||
|
#define MAXCTRS 512
|
||||||
|
#define IOCTLSLEEP 60U
|
||||||
|
|
||||||
|
static unsigned int read_interval = IOCTLSLEEP;
|
||||||
|
static int cfvn, csvn, authorization;
|
||||||
|
static unsigned long loop_count = 1;
|
||||||
|
static unsigned char *ioctlbuffer;
|
||||||
|
static bool allcpu;
|
||||||
|
|
||||||
|
static unsigned int max_possible_cpus; /* No of possible CPUs */
|
||||||
|
static struct ctrname { /* List of defined counters */
|
||||||
|
char *name; /* Counter name */
|
||||||
|
bool hitcnt; /* Counter number read from ioctl() */
|
||||||
|
unsigned long total; /* Total counter value */
|
||||||
|
unsigned long *ccv; /* Per CPU counter value */
|
||||||
|
} ctrname[MAXCTRS];
|
||||||
|
|
||||||
|
static bool read_counternames(void)
|
||||||
|
{
|
||||||
|
struct dirent **namelist = NULL;
|
||||||
|
int i, ctr = 0, count = 0;
|
||||||
|
char *path, *ctrpath;
|
||||||
|
|
||||||
|
path = util_path_sysfs("/bus/event_source/devices/cpum_cf/events/");
|
||||||
|
count = util_scandir(&namelist, alphasort, path, "[^.]");
|
||||||
|
if (count <= 0) {
|
||||||
|
warnx("Cannot open %s", path);
|
||||||
|
free(path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (i = 0; i < count && ctr >= 0; i++) {
|
||||||
|
util_asprintf(&ctrpath, "%s/%s", path, namelist[i]->d_name);
|
||||||
|
if (util_file_read_va(ctrpath, "event=%x", &ctr) == 1)
|
||||||
|
ctrname[ctr].name = util_strdup(namelist[i]->d_name);
|
||||||
|
else
|
||||||
|
warnx("Cannot parse %s", ctrpath);
|
||||||
|
free(ctrpath);
|
||||||
|
}
|
||||||
|
util_scandir_free(namelist, count);
|
||||||
|
free(path);
|
||||||
|
return ctr < 0 ? false : true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void free_counternames(void)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < ARRAY_SIZE(ctrname); ++i) {
|
||||||
|
free(ctrname[i].name);
|
||||||
|
free(ctrname[i].ccv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct check_result {
|
||||||
|
bool cpu_pos; /* CPU Number possible */
|
||||||
|
bool cpu_req; /* CPU Number requested */
|
||||||
|
bool cpu_hit; /* CPU Number received */
|
||||||
|
unsigned char sets_req; /* Counters sets requested */
|
||||||
|
unsigned char sets_hit; /* Counters sets received */
|
||||||
|
} *check;
|
||||||
|
|
||||||
|
static bool check_set(unsigned long a, unsigned long b, unsigned long sets)
|
||||||
|
{
|
||||||
|
if (a > b)
|
||||||
|
return false;
|
||||||
|
for (; a <= b; ++a) {
|
||||||
|
if (a >= max_possible_cpus || !check[a].cpu_pos)
|
||||||
|
return false;
|
||||||
|
check[a].cpu_req = true;
|
||||||
|
check[a].sets_req = sets;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Functions to parse command line parameters
|
||||||
|
* Convert a number from ascii to int.
|
||||||
|
*/
|
||||||
|
static unsigned long getnumber(char *word, char stopchar)
|
||||||
|
{
|
||||||
|
unsigned long no;
|
||||||
|
char *endp;
|
||||||
|
|
||||||
|
no = strtoul(word, &endp, 0);
|
||||||
|
if (*endp != stopchar)
|
||||||
|
errx(EXIT_FAILURE, "Invalid parameter %s", word);
|
||||||
|
return no;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read file to get all online CPUs */
|
||||||
|
static bool get_cpus(char *file, char *buf, size_t bufsz)
|
||||||
|
{
|
||||||
|
char fmt[16];
|
||||||
|
FILE *slp;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
slp = fopen(file, "r");
|
||||||
|
if (!slp) {
|
||||||
|
warnx("Cannot open %s", file);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
snprintf(fmt, sizeof(fmt), "%%%zus", bufsz - 1);
|
||||||
|
rc = fscanf(slp, fmt, buf);
|
||||||
|
fclose(slp);
|
||||||
|
if (rc != 1)
|
||||||
|
warnx("Cannot parse %s", file);
|
||||||
|
return rc == 1 ? true : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse counter set specification */
|
||||||
|
static unsigned long parse_ctrset(char *cp)
|
||||||
|
{
|
||||||
|
unsigned long x = 0;
|
||||||
|
|
||||||
|
for (; *cp; ++cp) {
|
||||||
|
switch (tolower(*cp)) {
|
||||||
|
case 'b':
|
||||||
|
x |= S390_HWCTR_BASIC;
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
x |= S390_HWCTR_CRYPTO;
|
||||||
|
break;
|
||||||
|
case 'e':
|
||||||
|
x |= S390_HWCTR_EXT;
|
||||||
|
break;
|
||||||
|
case 'm':
|
||||||
|
x |= S390_HWCTR_MT_DIAG;
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
case 'u':
|
||||||
|
x |= S390_HWCTR_USER;
|
||||||
|
break;
|
||||||
|
case 'a':
|
||||||
|
x |= S390_HWCTR_ALL;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
errx(EXIT_FAILURE,
|
||||||
|
"Invalid counter set specification '%c'", *cp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *show_ctrset(unsigned long set)
|
||||||
|
{
|
||||||
|
static char text[16];
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
if (set & S390_HWCTR_BASIC)
|
||||||
|
text[i++] = 'B';
|
||||||
|
if (set & S390_HWCTR_CRYPTO)
|
||||||
|
text[i++] = 'C';
|
||||||
|
if (set & S390_HWCTR_EXT)
|
||||||
|
text[i++] = 'E';
|
||||||
|
if (set & S390_HWCTR_MT_DIAG)
|
||||||
|
text[i++] = 'M';
|
||||||
|
if (set & S390_HWCTR_USER)
|
||||||
|
text[i++] = 'U';
|
||||||
|
text[i] = '\0';
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse CPU list and counter sets */
|
||||||
|
static void parse_cpulist(char *parm, struct s390_hwctr_start *start)
|
||||||
|
{
|
||||||
|
uint64_t *words = start->cpumask;
|
||||||
|
unsigned int i, no_a, no_b;
|
||||||
|
cpu_set_t cpulist;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
CPU_ZERO(&cpulist);
|
||||||
|
start->data_bytes = 0;
|
||||||
|
start->counter_sets = S390_HWCTR_ALL; /* Default all counter sets */
|
||||||
|
|
||||||
|
if (parm) { /* CPU list with optional counter set */
|
||||||
|
char *cp = strchr(parm, ':');
|
||||||
|
|
||||||
|
if (cp) { /* Handle counter set */
|
||||||
|
*cp = '\0';
|
||||||
|
start->counter_sets = parse_ctrset(++cp);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strlen(parm) > 0) /* Handle CPU list */
|
||||||
|
rc = libcpumf_cpuset(parm, &cpulist);
|
||||||
|
else
|
||||||
|
rc = libcpumf_cpuset_fn(S390_CPUS_ONLINE, &cpulist);
|
||||||
|
if (rc)
|
||||||
|
errx(EXIT_FAILURE, "Cannot use CPU list %s", parm);
|
||||||
|
} else { /* No CPU list and no counter sets */
|
||||||
|
rc = libcpumf_cpuset_fn(S390_CPUS_ONLINE, &cpulist);
|
||||||
|
if (rc)
|
||||||
|
err(EXIT_FAILURE, "Cannot read file " S390_CPUS_ONLINE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check with authorized counter sets */
|
||||||
|
if ((start->counter_sets & authorization) != start->counter_sets) {
|
||||||
|
unsigned int noton = ~(start->counter_sets & authorization);
|
||||||
|
|
||||||
|
start->counter_sets &= authorization;
|
||||||
|
if (!start->counter_sets)
|
||||||
|
errx(EXIT_FAILURE, "No counter sets are authorized");
|
||||||
|
warnx("One or more counter sets are not authorized: %s",
|
||||||
|
show_ctrset(noton));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (rc = 0; rc < CPU_SETSIZE; ++rc)
|
||||||
|
if (CPU_ISSET(rc, &cpulist))
|
||||||
|
if (!check_set(rc, rc, start->counter_sets))
|
||||||
|
errx(EXIT_FAILURE, "Invalid CPU %d", rc);
|
||||||
|
|
||||||
|
/* Convert the CPU list to a bitmask for kernel cpumask_t */
|
||||||
|
for (i = 0, no_b = 0; i < max_possible_cpus; ++i) {
|
||||||
|
if (check[i].cpu_req) {
|
||||||
|
no_a = i % LONG_BIT;
|
||||||
|
no_b = i / LONG_BIT;
|
||||||
|
words[no_b] |= 1ULL << no_a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* no_b is highest used index */
|
||||||
|
start->cpumask_len = (no_b + 1) * CHAR_BIT;
|
||||||
|
start->version = S390_HWCTR_START_VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool check_setpossible(void)
|
||||||
|
{
|
||||||
|
char *cp, *parm, *tokens[16]; /* Used to parse command line params */
|
||||||
|
unsigned long i, no_a, no_b;
|
||||||
|
char cpubuf[1024];
|
||||||
|
|
||||||
|
if (!get_cpus(CPUS_KERNELMAX, cpubuf, sizeof(cpubuf)))
|
||||||
|
return false;
|
||||||
|
max_possible_cpus = getnumber(cpubuf, '\0') + 1;
|
||||||
|
check = calloc(max_possible_cpus, sizeof(*check));
|
||||||
|
if (!check)
|
||||||
|
err(EXIT_FAILURE, "Maximum CPUs %u", max_possible_cpus);
|
||||||
|
if (!get_cpus(CPUS_POSSIBLE, cpubuf, sizeof(cpubuf))) {
|
||||||
|
free(check);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
parm = cpubuf;
|
||||||
|
for (i = 0; i < ARRAY_SIZE(tokens) && (tokens[i] = strtok(parm, ","));
|
||||||
|
++i, parm = NULL) {
|
||||||
|
cp = strchr(tokens[i], '-');
|
||||||
|
if (cp) { /* Range */
|
||||||
|
no_a = getnumber(tokens[i], *cp);
|
||||||
|
no_b = getnumber(++cp, '\0');
|
||||||
|
} else {
|
||||||
|
no_b = getnumber(tokens[i], '\0');
|
||||||
|
no_a = no_b;
|
||||||
|
}
|
||||||
|
for (; no_a <= no_b; ++no_a)
|
||||||
|
check[no_a].cpu_pos = true;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void show_header(void)
|
||||||
|
{
|
||||||
|
static bool header;
|
||||||
|
bool comma = false;
|
||||||
|
|
||||||
|
if (header)
|
||||||
|
return; /* Printed already */
|
||||||
|
printf("Date,Time,CPU,"); /* Print counter name and number */
|
||||||
|
for (size_t i = 0; i < ARRAY_SIZE(ctrname); ++i) {
|
||||||
|
if (!ctrname[i].hitcnt)
|
||||||
|
continue;
|
||||||
|
if (comma)
|
||||||
|
putchar(',');
|
||||||
|
printf("%s(%ld)", ctrname[i].name ?: "Counter", i);
|
||||||
|
comma = true;
|
||||||
|
}
|
||||||
|
putchar('\n');
|
||||||
|
header = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void line(char *header)
|
||||||
|
{
|
||||||
|
bool comma;
|
||||||
|
|
||||||
|
show_header();
|
||||||
|
if (allcpu) {
|
||||||
|
for (unsigned int h = 0; h < max_possible_cpus; ++h) {
|
||||||
|
char txt[16];
|
||||||
|
|
||||||
|
if (!check[h].cpu_hit)
|
||||||
|
continue;
|
||||||
|
comma = false;
|
||||||
|
snprintf(txt, sizeof(txt), "CPU%d,", h);
|
||||||
|
printf("%s%s", header, txt);
|
||||||
|
for (size_t i = 0; i < ARRAY_SIZE(ctrname); ++i) {
|
||||||
|
if (!ctrname[i].hitcnt)
|
||||||
|
continue;
|
||||||
|
if (comma)
|
||||||
|
putchar(',');
|
||||||
|
printf("%ld", ctrname[i].ccv[h]);
|
||||||
|
comma = true;
|
||||||
|
}
|
||||||
|
putchar('\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Print total count of all CPUs */
|
||||||
|
printf("%sTotal,", header);
|
||||||
|
comma = false;
|
||||||
|
for (size_t i = 0; i < ARRAY_SIZE(ctrname); ++i) {
|
||||||
|
if (!ctrname[i].hitcnt)
|
||||||
|
continue;
|
||||||
|
if (comma)
|
||||||
|
putchar(',');
|
||||||
|
printf("%ld", ctrname[i].total);
|
||||||
|
comma = true;
|
||||||
|
}
|
||||||
|
putchar('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
static void show(void)
|
||||||
|
{
|
||||||
|
time_t now = time(NULL);
|
||||||
|
struct tm *now_tm;
|
||||||
|
char now_text[32];
|
||||||
|
|
||||||
|
now_tm = localtime(&now);
|
||||||
|
strftime(now_text, sizeof(now_text), "%F,%T,", now_tm);
|
||||||
|
line(now_text);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return Counter set size numbers (in counters) */
|
||||||
|
static unsigned int ctrset_size(int set)
|
||||||
|
{
|
||||||
|
switch (set) {
|
||||||
|
case S390_HWCTR_BASIC:
|
||||||
|
return 6;
|
||||||
|
case S390_HWCTR_USER:
|
||||||
|
return (cfvn == 1) ? 6 : 2;
|
||||||
|
case S390_HWCTR_CRYPTO:
|
||||||
|
return (csvn <= 5) ? 16 : 20;
|
||||||
|
case S390_HWCTR_EXT:
|
||||||
|
switch (csvn) {
|
||||||
|
case 1: return 32;
|
||||||
|
case 2: return 48;
|
||||||
|
case 3:
|
||||||
|
case 4:
|
||||||
|
case 5: return 128;
|
||||||
|
}
|
||||||
|
return 160;
|
||||||
|
case S390_HWCTR_MT_DIAG:
|
||||||
|
switch (csvn) {
|
||||||
|
case 1:
|
||||||
|
case 2:
|
||||||
|
case 3: return 0;
|
||||||
|
}
|
||||||
|
return 48;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return counter set offset numbers */
|
||||||
|
static int ctrset_offset(int set)
|
||||||
|
{
|
||||||
|
switch (set) {
|
||||||
|
case S390_HWCTR_BASIC:
|
||||||
|
return 0;
|
||||||
|
case S390_HWCTR_USER:
|
||||||
|
return 32;
|
||||||
|
case S390_HWCTR_CRYPTO:
|
||||||
|
return 64;
|
||||||
|
case S390_HWCTR_EXT:
|
||||||
|
return 128;
|
||||||
|
case S390_HWCTR_MT_DIAG:
|
||||||
|
return 448;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool set_and_size_ok(struct s390_hwctr_setdata *p)
|
||||||
|
{
|
||||||
|
switch (p->set) {
|
||||||
|
case S390_HWCTR_BASIC:
|
||||||
|
case S390_HWCTR_USER:
|
||||||
|
case S390_HWCTR_CRYPTO:
|
||||||
|
case S390_HWCTR_EXT:
|
||||||
|
case S390_HWCTR_MT_DIAG:
|
||||||
|
return p->no_cnts == ctrset_size(p->set);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool add_countervalue(size_t idx, unsigned int cpu, unsigned long value)
|
||||||
|
{
|
||||||
|
if (idx >= ARRAY_SIZE(ctrname)) {
|
||||||
|
warnx("Invalid counter number %zu", idx);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (cpu >= max_possible_cpus) {
|
||||||
|
warnx("Invalid CPU number %d", cpu);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!ctrname[idx].ccv) /* Unknown counter */
|
||||||
|
ctrname[idx].ccv = calloc(max_possible_cpus,
|
||||||
|
sizeof(unsigned long));
|
||||||
|
if (ctrname[idx].ccv)
|
||||||
|
ctrname[idx].ccv[cpu] += value;
|
||||||
|
ctrname[idx].total += value;
|
||||||
|
ctrname[idx].hitcnt = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_read(struct s390_hwctr_read *read)
|
||||||
|
{
|
||||||
|
void *base = &read->data;
|
||||||
|
size_t offset = 0;
|
||||||
|
|
||||||
|
/* Clear previous hit counters */
|
||||||
|
for (unsigned int i = 0; i < max_possible_cpus; ++i) {
|
||||||
|
check[i].sets_hit = 0;
|
||||||
|
check[i].cpu_hit = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Iterate over all CPUs */
|
||||||
|
for (unsigned int i = 0; i < read->no_cpus; ++i) {
|
||||||
|
struct s390_hwctr_cpudata *cp = base + offset;
|
||||||
|
|
||||||
|
check[cp->cpu_nr].cpu_hit = true;
|
||||||
|
check[cp->cpu_nr].sets_hit = 0;
|
||||||
|
|
||||||
|
offset += sizeof(cp->cpu_nr) + sizeof(cp->no_sets);
|
||||||
|
/* Iterate over all counter sets */
|
||||||
|
for (unsigned int j = 0; j < cp->no_sets; ++j) {
|
||||||
|
struct s390_hwctr_setdata *sp = base + offset;
|
||||||
|
|
||||||
|
check[cp->cpu_nr].sets_hit |= sp->set;
|
||||||
|
offset += sizeof(sp->set) + sizeof(sp->no_cnts);
|
||||||
|
if (!set_and_size_ok(sp)) {
|
||||||
|
warnx("CPU %d inconsistent set %d size %d",
|
||||||
|
cp->cpu_nr, sp->set, sp->no_cnts);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
/* Iterate over all counters in each set */
|
||||||
|
for (unsigned int k = 0; k < sp->no_cnts; ++k) {
|
||||||
|
uint64_t value;
|
||||||
|
void *addr = base + offset;
|
||||||
|
size_t idx = ctrset_offset(sp->set) + k;
|
||||||
|
|
||||||
|
memcpy(&value, addr, sizeof(value));
|
||||||
|
offset += sizeof(value);
|
||||||
|
if (!add_countervalue(idx, cp->cpu_nr, value))
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
show();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_open(void)
|
||||||
|
{
|
||||||
|
int fd = open(S390_HWCTR_DEVICE, O_RDWR);
|
||||||
|
|
||||||
|
if (fd < 0)
|
||||||
|
warn(S390_HWCTR_DEVICE);
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_stop(int ioctlfd)
|
||||||
|
{
|
||||||
|
int rc = ioctl(ioctlfd, S390_HWCTR_STOP, 0);
|
||||||
|
|
||||||
|
if (rc < 0)
|
||||||
|
warn("ioctl S390_HWCTR_STOP");
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_start(int ioctlfd, struct s390_hwctr_start *start)
|
||||||
|
{
|
||||||
|
int rc = ioctl(ioctlfd, S390_HWCTR_START, start);
|
||||||
|
|
||||||
|
if (rc < 0)
|
||||||
|
warn("ioctl S390_HWCTR_START");
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_read(int ioctlfd)
|
||||||
|
{
|
||||||
|
size_t ioctlbuffer_len = PAGE_SIZE * max_possible_cpus +
|
||||||
|
sizeof(struct s390_hwctr_read);
|
||||||
|
struct s390_hwctr_read *read;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
if (!ioctlbuffer) {
|
||||||
|
ioctlbuffer = malloc(ioctlbuffer_len);
|
||||||
|
if (!ioctlbuffer) {
|
||||||
|
warn("ioctl S390_HWCTR_START");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
read = (struct s390_hwctr_read *)ioctlbuffer;
|
||||||
|
rc = ioctl(ioctlfd, S390_HWCTR_READ, read);
|
||||||
|
if (!rc)
|
||||||
|
rc = test_read(read);
|
||||||
|
else
|
||||||
|
warn("ioctl S390_HWCTR_READ");
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void do_sleep(void)
|
||||||
|
{
|
||||||
|
struct timespec req = {
|
||||||
|
.tv_sec = read_interval,
|
||||||
|
.tv_nsec = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
nanosleep(&req, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Execute commands and report first error */
|
||||||
|
static int do_it(char *s)
|
||||||
|
{
|
||||||
|
struct s390_hwctr_start start;
|
||||||
|
int ioctlfd;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
memset(&start, 0, sizeof(start));
|
||||||
|
rc = max_possible_cpus / sizeof(uint64_t);
|
||||||
|
start.cpumask = alloca(max_possible_cpus / sizeof(uint64_t));
|
||||||
|
memset(start.cpumask, 0, rc);
|
||||||
|
parse_cpulist(s, &start);
|
||||||
|
errno = 0;
|
||||||
|
ioctlfd = do_open();
|
||||||
|
if (ioctlfd < 0)
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
|
||||||
|
rc = do_start(ioctlfd, &start);
|
||||||
|
if (rc < 0) {
|
||||||
|
close(ioctlfd);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned long i = 0; !rc && i < loop_count; ++i) {
|
||||||
|
rc = do_read(ioctlfd);
|
||||||
|
if (rc) {
|
||||||
|
close(ioctlfd);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
if (read_interval && i + 1 < loop_count)
|
||||||
|
do_sleep();
|
||||||
|
}
|
||||||
|
rc = do_stop(ioctlfd);
|
||||||
|
close(ioctlfd);
|
||||||
|
return rc ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct util_opt opt_vec[] = {
|
||||||
|
UTIL_OPT_SECTION("OPTIONS"),
|
||||||
|
{
|
||||||
|
.option = { "all", no_argument, NULL, 'a' },
|
||||||
|
.desc = "Displays all CPUs in output"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "loop", required_argument, NULL, 'l' },
|
||||||
|
.argument = "NUMBER",
|
||||||
|
.desc = "Specifies loop count for next read"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "interval", required_argument, NULL, 'i' },
|
||||||
|
.argument = "NUMBER",
|
||||||
|
.desc = "Specifies interval between read operations (seconds)"
|
||||||
|
},
|
||||||
|
UTIL_OPT_HELP,
|
||||||
|
UTIL_OPT_VERSION,
|
||||||
|
UTIL_OPT_END
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct util_prg prg = {
|
||||||
|
.desc = "Read CPU Measurement facility counter sets",
|
||||||
|
.copyright_vec = {
|
||||||
|
{
|
||||||
|
.owner = "IBM Corp.",
|
||||||
|
.pub_first = 2021,
|
||||||
|
.pub_last = 2021,
|
||||||
|
},
|
||||||
|
UTIL_PRG_COPYRIGHT_END
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Check for hardware support and exit if not available */
|
||||||
|
static void have_support(void)
|
||||||
|
{
|
||||||
|
struct stat statbuf;
|
||||||
|
|
||||||
|
if (stat(S390_HWCTR_DEVICE, &statbuf) == -1)
|
||||||
|
errx(EXIT_FAILURE,
|
||||||
|
"No support for CPU Measurement Counter set facility");
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
char *slash;
|
||||||
|
int ch;
|
||||||
|
|
||||||
|
util_prg_init(&prg);
|
||||||
|
util_opt_init(opt_vec, NULL);
|
||||||
|
|
||||||
|
while ((ch = util_opt_getopt_long(argc, argv)) != -1) {
|
||||||
|
switch (ch) {
|
||||||
|
default:
|
||||||
|
util_opt_print_parse_error(ch, argv);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
case 'h':
|
||||||
|
util_prg_print_help();
|
||||||
|
util_opt_print_help();
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
case 'v':
|
||||||
|
util_prg_print_version();
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
case 'l':
|
||||||
|
errno = 0;
|
||||||
|
loop_count = strtoul(optarg, &slash, 0);
|
||||||
|
if (errno || *slash)
|
||||||
|
errx(EXIT_FAILURE, "Invalid argument for -%c",
|
||||||
|
ch);
|
||||||
|
break;
|
||||||
|
case 'i':
|
||||||
|
errno = 0;
|
||||||
|
read_interval = (unsigned int)strtoul(optarg, &slash, 0);
|
||||||
|
if (errno || *slash)
|
||||||
|
errx(EXIT_FAILURE, "Invalid argument for -%c", ch);
|
||||||
|
break;
|
||||||
|
case 'a':
|
||||||
|
allcpu = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
have_support();
|
||||||
|
if (!libcpumf_cpumcf_info(&cfvn, &csvn, &authorization))
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
if (!check_setpossible())
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
if (!read_counternames()) {
|
||||||
|
free(check);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (optind >= argc) {
|
||||||
|
ch = do_it(NULL);
|
||||||
|
} else {
|
||||||
|
while (optind < argc) {
|
||||||
|
ch = do_it(argv[optind++]);
|
||||||
|
if (ch)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free_counternames();
|
||||||
|
free(check);
|
||||||
|
return ch;
|
||||||
|
}
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
/* Copyright IBM Corp. 2021
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CPU Measurement counter facility application for device driver.
|
||||||
|
*
|
||||||
|
* Ioctl system call definitions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LSHWC_H
|
||||||
|
#define LSHWC_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
|
||||||
|
enum {
|
||||||
|
S390_HWCTR_BASIC = 0x2, /* BASIC counter set */
|
||||||
|
S390_HWCTR_USER = 0x4, /* Problem-State Counter Set */
|
||||||
|
S390_HWCTR_CRYPTO = 0x8, /* Crypto-Activity Counter Set */
|
||||||
|
S390_HWCTR_EXT = 0x1, /* Extended Counter Set */
|
||||||
|
S390_HWCTR_MT_DIAG = 0x20, /* MT-diagnostic Counter Set */
|
||||||
|
S390_HWCTR_ALL = S390_HWCTR_BASIC | S390_HWCTR_USER |
|
||||||
|
S390_HWCTR_CRYPTO | S390_HWCTR_EXT |
|
||||||
|
S390_HWCTR_MT_DIAG
|
||||||
|
};
|
||||||
|
|
||||||
|
/* The ioctl(..., S390_HWCTR_READ, ...) is the only subcommand which returns
|
||||||
|
* data. It requires member data_bytes to be positive and indicates the
|
||||||
|
* maximum amount of data available to store counter set data. The other
|
||||||
|
* ioctl() subcommands do not use this member and it should be set to zero.
|
||||||
|
*
|
||||||
|
* The cpuset data is flattened using the following scheme, stored in member
|
||||||
|
* data:
|
||||||
|
*
|
||||||
|
* 0x0 0x8 0xc 0x10 0x14 0x18 0x20 0x28 0xU-1
|
||||||
|
* +---------+-----+---------+-----+---------+-----+-----+------+------+
|
||||||
|
* | no_cpus | cpu | no_sets | set | no_cnts | cv1 | cv2 | .... | cv_n |
|
||||||
|
* +---------+-----+---------+-----+---------+-----+-----+------+------+
|
||||||
|
*
|
||||||
|
* 0xU 0xU+4 0xU+8 0xU+10 0xV-1
|
||||||
|
* +-----+---------+-----+-----+------+------+
|
||||||
|
* | set | no_cnts | cv1 | cv2 | .... | cv_n |
|
||||||
|
* +-----+---------+-----+-----+------+------+
|
||||||
|
*
|
||||||
|
* 0xV 0xV+4 0xV+8 0xV+c
|
||||||
|
* +-----+---------+-----+---------+-----+-----+------+------+
|
||||||
|
* | cpu | no_sets | set | no_cnts | cv1 | cv2 | .... | cv_n |
|
||||||
|
* +-----+---------+-----+---------+-----+-----+------+------+
|
||||||
|
*
|
||||||
|
* U and V denote arbitrary hexadezimal addresses.
|
||||||
|
* In fact the first int represents the number of CPUs data was extracted
|
||||||
|
* from. This is followed by CPU number and number of counter sets extracted.
|
||||||
|
* Both are two integer values. This is followed by the set number and number
|
||||||
|
* of counters extracted. Both are two integer values. This is followed by
|
||||||
|
* the counter values, each element is eight bytes in size.
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct s390_hwctr_start { /* Set CPUs to operate on */
|
||||||
|
uint64_t version; /* Version of interface */
|
||||||
|
uint64_t data_bytes; /* # of bytes required */
|
||||||
|
uint64_t cpumask_len; /* Length of CPU mask in bytes */
|
||||||
|
uint64_t *cpumask; /* Pointer to CPU mask */
|
||||||
|
uint64_t counter_sets; /* Bit mask of counter set to get */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct s390_hwctr_setdata { /* Counter set data */
|
||||||
|
uint32_t set; /* Counter set number */
|
||||||
|
uint32_t no_cnts; /* # of counters stored in cv[] */
|
||||||
|
uint64_t cv[0]; /* Counter values (variable length) */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct s390_hwctr_cpudata { /* Counter set data per CPU */
|
||||||
|
uint32_t cpu_nr; /* Counter set number */
|
||||||
|
uint32_t no_sets; /* # of counters sets in data[] */
|
||||||
|
struct s390_hwctr_setdata data[0];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct s390_hwctr_read { /* Structure to get all ctr sets */
|
||||||
|
uint64_t no_cpus; /* Total # of CPUs data taken from */
|
||||||
|
struct s390_hwctr_cpudata data[0];
|
||||||
|
};
|
||||||
|
|
||||||
|
#define S390_HWCTR_MAGIC 'C' /* Random magic # for ioctls */
|
||||||
|
#define S390_HWCTR_START _IOWR(S390_HWCTR_MAGIC, 1, struct s390_hwctr_start)
|
||||||
|
#define S390_HWCTR_STOP _IO(S390_HWCTR_MAGIC, 2)
|
||||||
|
#define S390_HWCTR_READ _IOWR(S390_HWCTR_MAGIC, 3, struct s390_hwctr_read)
|
||||||
|
|
||||||
|
#define S390_HWCTR_START_VERSION 1 /* Version # s390_hwctr_start */
|
||||||
|
#define S390_HWCTR_DEVICE "/dev/hwctr" /* Device name */
|
||||||
|
#endif
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
\" lscpumf.1
|
\" lscpumf.8
|
||||||
.\"
|
.\"
|
||||||
.\"
|
.\"
|
||||||
.\" Copyright IBM Corp. 2014, 2020
|
.\" Copyright IBM Corp. 2014, 2020
|
||||||
.\" s390-tools is free software; you can redistribute it and/or modify
|
.\" s390-tools is free software; you can redistribute it and/or modify
|
||||||
.\" it under the terms of the MIT license. See LICENSE for details.
|
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||||
.\" ----------------------------------------------------------------------
|
.\" ----------------------------------------------------------------------
|
||||||
.TH lscpumf "1" "May 2020" "s390-tools" "CPU-MF management programs"
|
.TH lscpumf "8" "May 2022" "s390-tools" "CPU-MF management programs"
|
||||||
.
|
.
|
||||||
.ds c \fBlscpumf\fP
|
.ds c \fBlscpumf\fP
|
||||||
.
|
.
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
.\" lshwc.8
|
||||||
|
.\"
|
||||||
|
.\"
|
||||||
|
.\" Copyright IBM Corp. 2021
|
||||||
|
.\" s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
.\" ----------------------------------------------------------------------
|
||||||
|
.ds c \fBlshwc\fP
|
||||||
|
.
|
||||||
|
.TH \*c "8" "May 2022" "s390-tools" "CPU-MF management programs"
|
||||||
|
.
|
||||||
|
.SH NAME
|
||||||
|
\*c \- extract CPU Measurement Facilities counter sets
|
||||||
|
.
|
||||||
|
.SH SYNOPSIS
|
||||||
|
\*c
|
||||||
|
.RB [ \-a ]
|
||||||
|
.RB [ \-l
|
||||||
|
.IR count ]
|
||||||
|
.RB [ \-i
|
||||||
|
.IR interval ]
|
||||||
|
\fR[\fIcpulist\fR][:\fIsets\fR]\fP
|
||||||
|
.br
|
||||||
|
\*c
|
||||||
|
.BR \-h | \-\-help
|
||||||
|
.br
|
||||||
|
\*c
|
||||||
|
.BR \-v | \-\-version
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.SH DESCRIPTION
|
||||||
|
The \*c command extracts complete counter sets from the CPU
|
||||||
|
Measurement Facilities for Linux on Z.
|
||||||
|
Counter sets can be specified and extracted for individual CPUs.
|
||||||
|
The output is a comma-separated values file.
|
||||||
|
Each line starts with a timestamp and the CPU number,
|
||||||
|
followed by the extracted counter values.
|
||||||
|
.
|
||||||
|
.SH OPTIONS
|
||||||
|
.TP
|
||||||
|
.BR \-h ", " \-\-help
|
||||||
|
Displays help information, then exits.
|
||||||
|
.
|
||||||
|
.TP
|
||||||
|
.BR \-v ", " \-\-version
|
||||||
|
Displays version information, then exits.
|
||||||
|
.
|
||||||
|
.TP
|
||||||
|
.BR \-a ", " \-\-allcpu
|
||||||
|
Displays counter values from each CPU.
|
||||||
|
The default is a total summary line of all counters from all CPUs.
|
||||||
|
.
|
||||||
|
.TP
|
||||||
|
.BR \-i ", " \-\-interval \fI\ seconds\fP
|
||||||
|
Specifies a time interval, in seconds,
|
||||||
|
that the command waits between read operations.
|
||||||
|
The default is 60 seconds.
|
||||||
|
.
|
||||||
|
.TP
|
||||||
|
.BR \-l ", " \-\-loop \fI\ count\fP
|
||||||
|
Performs the specified number of read operations.
|
||||||
|
.
|
||||||
|
.TP
|
||||||
|
\fR[\fIcpulist\fR][:\fIsets\fR]\fP
|
||||||
|
A comma-separated list of CPUs.
|
||||||
|
Each CPU can optionally be followed by characters that specify the counter set.
|
||||||
|
See below for details.
|
||||||
|
.
|
||||||
|
.SS "CPU List and counter-set specification"
|
||||||
|
In the comma-separated list of CPUs,
|
||||||
|
each element is a CPU or a range of CPUs.
|
||||||
|
By default, \*c lists all CPUs.
|
||||||
|
.P
|
||||||
|
The CPU list can be followed by an optional list
|
||||||
|
of characters that specify the counter sets to be extracted,
|
||||||
|
preceded by a colon.
|
||||||
|
The characters can be upper or lower case.
|
||||||
|
By default, all counter sets are used.
|
||||||
|
.IP b
|
||||||
|
Include the basic counter set.
|
||||||
|
.IP c
|
||||||
|
Include the crypto counter set.
|
||||||
|
.IP e
|
||||||
|
Include the extended counter set.
|
||||||
|
.IP m
|
||||||
|
Include the MT_Diagnostic counter set.
|
||||||
|
.IP p|u
|
||||||
|
Include the problem counter set.
|
||||||
|
.IP a
|
||||||
|
Include all known counter sets (default).
|
||||||
|
.SH "Concurrency with perf tool"
|
||||||
|
The \*c tool and the linux
|
||||||
|
.B perf
|
||||||
|
tool use the same hardware and cannot be used concurrently.
|
||||||
|
Both tools print an error message and abort when they
|
||||||
|
detect this situation.
|
||||||
|
.SH "EXAMPLES"
|
||||||
|
The first example enables the basic and problem counter sets on CPU 0 and 1.
|
||||||
|
Two read operations are performed and a summary line is printed for each
|
||||||
|
read operation.
|
||||||
|
.sp 1
|
||||||
|
.nf
|
||||||
|
.ft CW
|
||||||
|
# lshwc -l2 0-1:BP
|
||||||
|
Date,Time,CPU,CPU_CYCLES(0),INSTRUCTIONS(1),L1I_DIR_WRITES(2),L1I_PENALTY_CYCLES(3),L1D_DIR_WRITES(4),
|
||||||
|
L1D_PENALTY_CYCLES(5),PROBLEM_STATE_CPU_CYCLES(32),PROBLEM_STATE_INSTRUCTIONS(33)
|
||||||
|
2021-04-01,11:50:32,Total,125422,39421,304,13953,454,
|
||||||
|
97489,0,0
|
||||||
|
2021-04-01,11:51:32,Total,68074231,16386850,194028,21382384,317227,
|
||||||
|
104503489,777383,14198
|
||||||
|
.ft
|
||||||
|
.fi
|
||||||
|
.sp 1
|
||||||
|
This example shows the counter values of the problem state counter set
|
||||||
|
per CPU. CPU 0 and CPU 1 is selected.
|
||||||
|
.nf
|
||||||
|
.ft CW
|
||||||
|
.sp 1
|
||||||
|
# lshwc -l3 -a 0-1:P
|
||||||
|
Date,Time,CPU,PROBLEM_STATE_CPU_CYCLES(32),PROBLEM_STATE_INSTRUCTIONS(33)
|
||||||
|
2021-04-01,11:54:47,CPU0,0,0
|
||||||
|
2021-04-01,11:54:47,CPU1,0,0
|
||||||
|
2021-04-01,11:54:47,Total,0,0
|
||||||
|
2021-04-01,11:55:47,CPU0,818775,14198
|
||||||
|
2021-04-01,11:55:47,CPU1,125689,1306
|
||||||
|
2021-04-01,11:55:47,Total,944464,15504
|
||||||
|
2021-04-01,11:56:47,CPU0,3207071426,1489122591
|
||||||
|
2021-04-01,11:56:47,CPU1,3225092021,1489278312
|
||||||
|
2021-04-01,11:56:47,Total,6432163447,2978400903
|
||||||
|
.ft
|
||||||
|
.fi
|
||||||
|
.SH "SEE ALSO"
|
||||||
|
.BR lscpumf (8)
|
||||||
|
.BR chcpumf (8)
|
||||||
+268
@@ -0,0 +1,268 @@
|
|||||||
|
.\" pai.8
|
||||||
|
.\"
|
||||||
|
.\"
|
||||||
|
.\" Copyright IBM Corp. 2022
|
||||||
|
.\" s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
.\" ----------------------------------------------------------------------
|
||||||
|
.ds c \fBpai\fP
|
||||||
|
.
|
||||||
|
.TH \*c "8" "May 2022" "s390-tools" "PAI Management Programs"
|
||||||
|
.
|
||||||
|
.SH NAME
|
||||||
|
\*c \- record and report Processor Activity Instrumentation (PAI) counters
|
||||||
|
.
|
||||||
|
.SH SYNOPSIS
|
||||||
|
\*c
|
||||||
|
.RB [ \-V ][ \-m | \-\-mapsize
|
||||||
|
.IR size ]
|
||||||
|
.RB [ \-i | \-\-interval
|
||||||
|
.IR ms ]
|
||||||
|
.BR \-c | \-\-crypto [ \fIcpulist ][: \fIdata\fR "] [" \fIloops\fP ]
|
||||||
|
.br
|
||||||
|
\*c
|
||||||
|
.RB [ \-V ][ \-m | \-\-mapsize
|
||||||
|
.IR size ]
|
||||||
|
.RB [ \-i | \-\-interval
|
||||||
|
.IR ms ]
|
||||||
|
.BR \-n | \-\-nnpa [ \fIcpulist ][: \fIdata\fR "] [" \fIloops\fP ]
|
||||||
|
.br
|
||||||
|
\*c
|
||||||
|
.RB [ \-V ][ \-H | \-\-humantime ][ \-S | \-\-summary "] " \-r | \-\-report " [" \fIfiles\fP ]
|
||||||
|
.br
|
||||||
|
\*c
|
||||||
|
.BR \-h | \-\-help
|
||||||
|
.br
|
||||||
|
\*c
|
||||||
|
.BR \-v | \-\-version
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.SH DESCRIPTION
|
||||||
|
\*c
|
||||||
|
counters tally calls for specific CPU instructions.
|
||||||
|
The \*c command records PAI counters in a ring buffer.
|
||||||
|
\*c can record counter data for all CPUs or for selected CPUs.
|
||||||
|
The main command options are
|
||||||
|
.B \-c
|
||||||
|
for recording cryptographic CPU instructions,
|
||||||
|
.B \-n
|
||||||
|
for recording NNPA CPU instructions
|
||||||
|
and
|
||||||
|
.B \-r
|
||||||
|
for reporting.
|
||||||
|
If all three options are omitted, option
|
||||||
|
.B \-r
|
||||||
|
is assumed and a message is printed.
|
||||||
|
Recording stores data, by CPU, in files
|
||||||
|
.I paicrypto.<XXX>,
|
||||||
|
for option
|
||||||
|
.B \-c
|
||||||
|
or
|
||||||
|
.I painnpa.<XXX>,
|
||||||
|
for option
|
||||||
|
.B \-n
|
||||||
|
where <XXX> specifies the CPU number with leading
|
||||||
|
zeros.
|
||||||
|
The files are created in the working directory,
|
||||||
|
existing files are overwritten.
|
||||||
|
Reporting evaluates files that are created by recording.
|
||||||
|
|
||||||
|
.SH OPTIONS
|
||||||
|
.TP
|
||||||
|
.BR \-c ", " \-\-crypto "\fR[\fIcpulist\fR][:\fIdata\fR]"
|
||||||
|
Records data for all (default) or a specified list of CPUs.
|
||||||
|
The CPU list is a comma-separated list of CPU numbers and ranges.
|
||||||
|
In a range, a hyphen separates the first CPU number
|
||||||
|
from the last CPU number.
|
||||||
|
By default \*c lists all CPUs.
|
||||||
|
.RS
|
||||||
|
The optional data specification
|
||||||
|
follows the colon
|
||||||
|
and determines additional collection of data.
|
||||||
|
The specification consists of alphabetic
|
||||||
|
characters that can be upper or lower case:
|
||||||
|
.IP c|C
|
||||||
|
Include task rename system calls
|
||||||
|
.B exec
|
||||||
|
and
|
||||||
|
.BR prctl .
|
||||||
|
.IP f|F
|
||||||
|
Include task creation and deletion system calls
|
||||||
|
.B fork
|
||||||
|
and
|
||||||
|
.BR exit .
|
||||||
|
.IP s|S
|
||||||
|
Include context switch records created by the kernel scheduler.
|
||||||
|
.IP k|K
|
||||||
|
Include only PAI cryptographic counters changed during system call
|
||||||
|
execution.
|
||||||
|
.IP u|U
|
||||||
|
Include only PAI cryptographic counters changed during user space
|
||||||
|
execution.
|
||||||
|
.RE
|
||||||
|
.
|
||||||
|
.TP
|
||||||
|
.BR \-n ", " \-\-nnpa "\fR[\fIcpulist\fR][:\fIdata\fR]"
|
||||||
|
Records data for all (default) or a specified list of CPUs.
|
||||||
|
The CPU list is a comma-separated list of CPU numbers and ranges.
|
||||||
|
In a range, a hyphen separates the first CPU number
|
||||||
|
from the last CPU number.
|
||||||
|
By default \*c lists all CPUs.
|
||||||
|
.RS
|
||||||
|
The optional data specification
|
||||||
|
follows the colon
|
||||||
|
and determines additional collection of data.
|
||||||
|
The specification consists of alphabetic
|
||||||
|
characters that can be upper or lower case:
|
||||||
|
.IP c|C
|
||||||
|
Include task rename system calls
|
||||||
|
.B exec
|
||||||
|
and
|
||||||
|
.BR prctl .
|
||||||
|
.IP f|F
|
||||||
|
Include task creation and deletion system calls
|
||||||
|
.B fork
|
||||||
|
and
|
||||||
|
.BR exit .
|
||||||
|
.IP s|S
|
||||||
|
Include context switch records created by the kernel scheduler.
|
||||||
|
.RE
|
||||||
|
.
|
||||||
|
.TP
|
||||||
|
.BR \-r ", " \-\-report
|
||||||
|
Generates a report from the specified files.
|
||||||
|
Files is a list of blank-separated file names.
|
||||||
|
If no files are specified,
|
||||||
|
uses all files named
|
||||||
|
.I paicrypto.<X>,
|
||||||
|
in the working directory, where
|
||||||
|
.I <X>
|
||||||
|
is a CPU number that
|
||||||
|
identifies the CPU for which the data was recorded.
|
||||||
|
.
|
||||||
|
.TP
|
||||||
|
.BR \-h ", " \-\-help
|
||||||
|
Displays help information, then exits.
|
||||||
|
.
|
||||||
|
.TP
|
||||||
|
.BR \-v ", " \-\-version
|
||||||
|
Displays version information, then exits.
|
||||||
|
.
|
||||||
|
.TP
|
||||||
|
.BR \-V ", " \-\-verbose
|
||||||
|
Displays the following information during reporting:
|
||||||
|
the file name and the hexadecimal offset for each sample header.
|
||||||
|
Displays the following information during recording:
|
||||||
|
the read position in the ring buffer,
|
||||||
|
the file name, and the hexadecimal offset for each sample header.
|
||||||
|
.
|
||||||
|
.TP
|
||||||
|
.BR \-H ", " \-\-humantime
|
||||||
|
Changes the time stamp format for sample entries from a hexadecimal
|
||||||
|
number to a human readable <seconds>.<nanoseconds> format.
|
||||||
|
The time stamp specifies the elapsed time since Linux was booted.
|
||||||
|
.
|
||||||
|
.TP
|
||||||
|
.BR \-S ", " \-\-summary
|
||||||
|
Prints a summary of counter names and
|
||||||
|
counter numbers with non-zero values.
|
||||||
|
The summary is the last line in the command output.
|
||||||
|
It shows the sum of the counter values of all processed files.
|
||||||
|
.
|
||||||
|
.TP
|
||||||
|
.BR \-i ", " \-\-interval "\ ms"
|
||||||
|
Specifies the waiting time,
|
||||||
|
in milliseconds,
|
||||||
|
between ring buffer read operations during recording.
|
||||||
|
The default is 1000 milliseconds.
|
||||||
|
Argument
|
||||||
|
.B loops
|
||||||
|
is an integer that specifies the number of
|
||||||
|
read operations during recording. The default is 1.
|
||||||
|
.
|
||||||
|
.TP
|
||||||
|
.BR \-m ", " \-\-mapsize "\ size"
|
||||||
|
Specifies the size of the ring buffer
|
||||||
|
that holds the collected data.
|
||||||
|
The value specifies the number of 4 KB pages
|
||||||
|
to be allocated and must be a power of 2.
|
||||||
|
The default size is 512 pages.
|
||||||
|
The ring buffer is created with the
|
||||||
|
.IR mmap (2)
|
||||||
|
system call.
|
||||||
|
.
|
||||||
|
.SH ARGUMENT
|
||||||
|
The command line options determine how command line
|
||||||
|
arguments are interpreted.
|
||||||
|
For option
|
||||||
|
.B \-c
|
||||||
|
to start recording, the argument specifies
|
||||||
|
the number of read operations.
|
||||||
|
If omitted, the default is one.
|
||||||
|
For option
|
||||||
|
.B \-r
|
||||||
|
to start reporting, the argument specifies
|
||||||
|
the file names to be read.
|
||||||
|
If omitted all files in the current directory
|
||||||
|
with file name
|
||||||
|
.I paicryto.<X>
|
||||||
|
are read.
|
||||||
|
.SH "Concurrency with perf tool"
|
||||||
|
The following concurrency restrictions apply
|
||||||
|
for \*c and the
|
||||||
|
.B perf
|
||||||
|
tool.
|
||||||
|
Both tools use the
|
||||||
|
.IR perf_event_open ()
|
||||||
|
system call and use the same device driver.
|
||||||
|
The device driver supports limited concurrency:
|
||||||
|
.IP Counting:
|
||||||
|
Counting pai events can run in parallel.
|
||||||
|
.IP Sampling:
|
||||||
|
Only one sampling event pai_crypto/CRYPTO_ALL can be active at any one time.
|
||||||
|
If a sampling event is active, no pai counting event can be active.
|
||||||
|
Both tools stop with an error message if they detect a collision
|
||||||
|
reported by the device driver.
|
||||||
|
.SH "EXAMPLES"
|
||||||
|
This example collects cryptographic counter on CPU 0.
|
||||||
|
The program runs for 10 seconds
|
||||||
|
(10 intervals of 1000 milliseconds).
|
||||||
|
.sp 1
|
||||||
|
.nf
|
||||||
|
.ft CW
|
||||||
|
# \*c -c0 10
|
||||||
|
.ft R
|
||||||
|
.fi
|
||||||
|
.sp 1
|
||||||
|
This example displays the data
|
||||||
|
that is collected in the first example.
|
||||||
|
Each data line shows the time stamp in jiffies,
|
||||||
|
followed by the CPU number,
|
||||||
|
the event number,
|
||||||
|
process and thread identifier (pid/tid) separated by slash (/),
|
||||||
|
and a comma-separated list of counter number, colon (:),
|
||||||
|
and the counter value n hexadecimal notation.
|
||||||
|
.sp 1
|
||||||
|
.nf
|
||||||
|
.ft CW
|
||||||
|
# \*c -r
|
||||||
|
0x62a668f2fa 0 event 4096 sample pid 4956/4956 9:0xa7,73:0x8,74:0x18
|
||||||
|
0x6319c75653 0 event 4096 sample pid 4972/4972 32:0x1
|
||||||
|
0x6319e2ddee 0 event 4096 sample pid 4972/4972 32:0x1
|
||||||
|
0x631d3e44f5 0 event 4096 sample pid 4972/4972 32:0x2
|
||||||
|
0x631d4cfc2e 0 event 4096 sample pid 4972/4972 32:0x3
|
||||||
|
0x631d529fdd 0 event 4096 sample pid 4972/4972 32:0x2
|
||||||
|
0x631d84cfa0 0 event 4096 sample pid 4972/4972 73:0x1
|
||||||
|
0x636e9826bc 0 event 4096 sample pid 4984/4984 9:0xa7,73:0x8,74:0x18
|
||||||
|
0x636f81a137 0 event 4096 sample pid 4984/4984 9:0x2,74:0x7
|
||||||
|
0x6378026e54 0 event 4096 sample pid 4984/4984 73:0x2
|
||||||
|
0x637bcdc8da 0 event 4096 sample pid 4984/4984 73:0x2
|
||||||
|
0x637bd426cd 0 event 4096 sample pid 4984/4984 73:0x6
|
||||||
|
0x637c503384 0 event 4096 sample pid 4984/4984 73:0x2
|
||||||
|
0x64991d83ba 0 event 4096 sample pid 5026/5026 9:0xb0,73:0x8,74:0x18
|
||||||
|
#
|
||||||
|
.ft R
|
||||||
|
.fi
|
||||||
|
.sp 1
|
||||||
|
.SH "SEE ALSO"
|
||||||
|
perf(1)
|
||||||
+1125
File diff suppressed because it is too large
Load Diff
+105
@@ -0,0 +1,105 @@
|
|||||||
|
/*
|
||||||
|
* pai - Extract CPU Processor Activity Instrumentation (PAI) facility data.
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp. 2022
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PAI_H
|
||||||
|
#define PAI_H
|
||||||
|
|
||||||
|
#define S390_EVT_PAI_CRYPTO 0x1000
|
||||||
|
|
||||||
|
enum { /* Event attribute specifications */
|
||||||
|
S390_EVTATTR_CTX_SWITCH = 1,
|
||||||
|
S390_EVTATTR_COMM = 2,
|
||||||
|
S390_EVTATTR_FORK = 4,
|
||||||
|
S390_EVTATTR_USERSPACE = 8,
|
||||||
|
S390_EVTATTR_KERNELSPACE = 16
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pai_event {
|
||||||
|
struct util_list_node node; /* List node */
|
||||||
|
struct perf_event_attr attr; /* Perf_event_open(2) attr */
|
||||||
|
int fd; /* Perf event file descriptor */
|
||||||
|
void *map_addr; /* Sampling data mapping address */
|
||||||
|
size_t map_size; /* Sampling size mapping size */
|
||||||
|
char file_name[16]; /* File name of sampled data */
|
||||||
|
int file_fd; /* Map data output file descriptor */
|
||||||
|
int cpu; /* Perf_event_open(2) CPU */
|
||||||
|
unsigned long flags; /* Perf_event_open(2) flags */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pai_event_out { /* Output for CRYPTO_ALL event */
|
||||||
|
__u32 type; /* Header type, see PERF_RECORD_xxx */
|
||||||
|
__u16 misc; /* Header misc, value depends on type */
|
||||||
|
__u64 time; /* Time stamp valid for all entries */
|
||||||
|
__u32 cpu; /* CPU number valid for all entries */
|
||||||
|
union {
|
||||||
|
/* Fields from PERF_RECORD_FORK|EXIT */
|
||||||
|
struct {
|
||||||
|
__u32 pid, ppid;
|
||||||
|
__u32 tid, ptid;
|
||||||
|
__u64 time;
|
||||||
|
} s_fork;
|
||||||
|
/* Fields from PERF_RECORD_COMM */
|
||||||
|
struct {
|
||||||
|
__u32 pid, tid;
|
||||||
|
__u8 cmd[16];
|
||||||
|
} s_comm;
|
||||||
|
/* Fields from PERF_RECORD_SWITCH_CPU_WIDE */
|
||||||
|
struct {
|
||||||
|
__u32 next_prev_pid, next_prev_tid;
|
||||||
|
} s_cs;
|
||||||
|
/* Fields from PERF_RECORD_LOST_SAMPLES */
|
||||||
|
struct {
|
||||||
|
__u64 lost;
|
||||||
|
} s_lost;
|
||||||
|
/* Relevant fields from PERF_RECORD_SAMPLE, time and cpu
|
||||||
|
* are stored above
|
||||||
|
*/
|
||||||
|
struct {
|
||||||
|
__u64 ip;
|
||||||
|
__u64 period;
|
||||||
|
__u32 pid, tid;
|
||||||
|
} s_sample;
|
||||||
|
/* Fields from PERF_RECORD_[UN]THROTTLE */
|
||||||
|
struct {
|
||||||
|
__u64 time;
|
||||||
|
__u64 id;
|
||||||
|
__u64 stream_id;
|
||||||
|
} s_throttle;
|
||||||
|
} u;
|
||||||
|
/* Information on last context switch out */
|
||||||
|
struct cs_switch {
|
||||||
|
unsigned char valid;
|
||||||
|
__u32 topid, totid;
|
||||||
|
__u32 frompid, fromtid;
|
||||||
|
} cs_switch;
|
||||||
|
void *raw; /* Pointer to key/value array for crypto counters */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct data_pos { /* Perf event mapped ring buffer */
|
||||||
|
__u64 data_head; /* Head in the data section */
|
||||||
|
__u64 data_tail; /* User-space written tail */
|
||||||
|
__u64 data_offset; /* Where the buffer starts */
|
||||||
|
__u64 data_size; /* Data buffer size */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct event_name { /* Event list for number to name xlate */
|
||||||
|
char *name; /* Event name */
|
||||||
|
__u64 config; /* Event config value */
|
||||||
|
__u64 total; /* Total counter value */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pmu_events { /* Event list for PMU number to name xlate */
|
||||||
|
struct util_list_node node; /* List node */
|
||||||
|
char *name; /* PMU name */
|
||||||
|
int type; /* PMU type */
|
||||||
|
int lstlen; /* # of entries in lst */
|
||||||
|
unsigned long base; /* Base event number */
|
||||||
|
struct event_name *lst; /* List of event names */
|
||||||
|
};
|
||||||
|
#endif /* PAI_H */
|
||||||
+2
-2
@@ -1107,8 +1107,8 @@ static void dasdfmt_write_labels(volume_label_t *vlabel,
|
|||||||
} else {
|
} else {
|
||||||
vlabel->ldl_version = 0xf2; /* EBCDIC '2' */
|
vlabel->ldl_version = 0xf2; /* EBCDIC '2' */
|
||||||
vlabel->formatted_blocks = cylinders * heads * geo.sectors;
|
vlabel->formatted_blocks = cylinders * heads * geo.sectors;
|
||||||
rc = write(fd, &vlabel->vollbl, (sizeof(*vlabel)
|
rc = write(fd, (void *)vlabel + sizeof(vlabel->volkey),
|
||||||
- sizeof(vlabel->volkey)));
|
(sizeof(*vlabel) - sizeof(vlabel->volkey)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (((rc != sizeof(*vlabel) - sizeof(vlabel->formatted_blocks)) &&
|
if (((rc != sizeof(*vlabel) - sizeof(vlabel->formatted_blocks)) &&
|
||||||
|
|||||||
+1
-1
@@ -47,7 +47,7 @@ Print DASD uid without z/VM minidisk token
|
|||||||
|
|
||||||
z/VM may provide an additional token that can be used to distinguish
|
z/VM may provide an additional token that can be used to distinguish
|
||||||
between different minidisks (see --extended-uid option). To remain
|
between different minidisks (see --extended-uid option). To remain
|
||||||
compatibile with systems that were installed on older Linux or z/VM
|
compatible with systems that were installed on older Linux or z/VM
|
||||||
levels, the -u option will print the uid excluding any z/VM-provided
|
levels, the -u option will print the uid excluding any z/VM-provided
|
||||||
minidisk token.
|
minidisk token.
|
||||||
|
|
||||||
|
|||||||
+5
-6
@@ -29,6 +29,7 @@
|
|||||||
#include "lib/dasd_sys.h"
|
#include "lib/dasd_sys.h"
|
||||||
#include "lib/libzds.h"
|
#include "lib/libzds.h"
|
||||||
#include "lib/util_base.h"
|
#include "lib/util_base.h"
|
||||||
|
#include "lib/util_libc.h"
|
||||||
#include "lib/util_opt.h"
|
#include "lib/util_opt.h"
|
||||||
#include "lib/util_prg.h"
|
#include "lib/util_prg.h"
|
||||||
#include "lib/util_sys.h"
|
#include "lib/util_sys.h"
|
||||||
@@ -1788,10 +1789,8 @@ dasdview_print_format1(unsigned int size, unsigned char *dumpstr)
|
|||||||
for (i = 0; i < size; i++) {
|
for (i = 0; i < size; i++) {
|
||||||
if ((i / 16) * 16 == i) {
|
if ((i / 16) * 16 == i) {
|
||||||
printf("\n| ");
|
printf("\n| ");
|
||||||
strncpy(asc, (char *)dumpstr + i, 16);
|
util_strlcpy(asc, (char *)dumpstr + i, 16);
|
||||||
strncpy(ebc, (char *)dumpstr + i, 16);
|
util_strlcpy(ebc, (char *)dumpstr + i, 16);
|
||||||
asc[16] = '\0';
|
|
||||||
ebc[16] = '\0';
|
|
||||||
}
|
}
|
||||||
printf("%02X", dumpstr[i]);
|
printf("%02X", dumpstr[i]);
|
||||||
if (((i + 1) / 4) * 4 == i + 1)
|
if (((i + 1) / 4) * 4 == i + 1)
|
||||||
@@ -1822,8 +1821,8 @@ dasdview_print_format2(unsigned int size, unsigned char *dumpstr,
|
|||||||
begin + (unsigned long long)i,
|
begin + (unsigned long long)i,
|
||||||
begin + (unsigned long long)i);
|
begin + (unsigned long long)i);
|
||||||
|
|
||||||
strncpy(asc, (char *)dumpstr + i, 8);
|
util_strlcpy(asc, (char *)dumpstr + i, 8);
|
||||||
strncpy(ebc, (char *)dumpstr + i, 8);
|
util_strlcpy(ebc, (char *)dumpstr + i, 8);
|
||||||
}
|
}
|
||||||
printf("%02X", dumpstr[i]);
|
printf("%02X", dumpstr[i]);
|
||||||
if (((i + 1) / 4) * 4 == i + 1)
|
if (((i + 1) / 4) * 4 == i + 1)
|
||||||
|
|||||||
@@ -110,6 +110,7 @@ static struct util_opt dump2tar_opts[] = {
|
|||||||
{
|
{
|
||||||
.option = { "file-timeout", required_argument, NULL, 'T' },
|
.option = { "file-timeout", required_argument, NULL, 'T' },
|
||||||
.desc = "Stop reading file after SEC seconds",
|
.desc = "Stop reading file after SEC seconds",
|
||||||
|
.argument = "SEC",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.option = { "file-max-size", required_argument, NULL, 'M' },
|
.option = { "file-max-size", required_argument, NULL, 'M' },
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
# Example configuration for hsavmcore
|
||||||
|
# See hsavmcore.conf(8) for documentation
|
||||||
|
|
||||||
|
# 0 - ERROR
|
||||||
|
# 1 - WARN
|
||||||
|
# 2 - INFO
|
||||||
|
# 3 - DEBUG
|
||||||
|
# 4 - TRACE
|
||||||
|
#verbose = 0
|
||||||
|
|
||||||
|
#workdir = /var/crash
|
||||||
|
|
||||||
|
#mount_debugfs = 0
|
||||||
|
|
||||||
|
#use_hsa_mem = 0
|
||||||
|
|
||||||
|
#hsa_size = -1
|
||||||
|
#release_hsa = 1
|
||||||
|
|
||||||
|
#bind_mount_vmcore = 1
|
||||||
|
|
||||||
|
#swap = /dev/disk/by-uuid/3cf6630b-4c4d-49ac-a0ae-0f5484cb5721
|
||||||
|
#swap = /swap.img
|
||||||
|
|
||||||
|
#fuse_debug = 0
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
### BEGIN INIT INFO
|
|
||||||
# Provides: cpacfstatsd
|
|
||||||
# Required-Start: $local_fs
|
|
||||||
# Required-Stop: $local_fs
|
|
||||||
# Should-Start:
|
|
||||||
# Should-Stop:
|
|
||||||
# Default-Start: 2 3 5
|
|
||||||
# Default-Stop: 0 1 6
|
|
||||||
# Short-Description: Start the cpacfstatsd daemon for Linux on System z
|
|
||||||
# Description: CPACF statistics collection daemon process for Linux on System z
|
|
||||||
### END INIT INFO
|
|
||||||
|
|
||||||
DAEMON=cpacfstatsd
|
|
||||||
DAEMON_PATH=/usr/sbin/cpacfstatsd
|
|
||||||
RUN_PID_FILE=/run/cpacfstatsd.pid
|
|
||||||
RETVAL=0
|
|
||||||
OPTIONS=""
|
|
||||||
|
|
||||||
# source function library
|
|
||||||
. /lib/lsb/init-functions
|
|
||||||
|
|
||||||
start()
|
|
||||||
{
|
|
||||||
if [ ! -f $RUN_PID_FILE ]; then
|
|
||||||
echo -n $"Starting $DAEMON:"
|
|
||||||
$DAEMON_PATH $OPTIONS
|
|
||||||
if [ $? == "0" ]; then
|
|
||||||
log_success_msg
|
|
||||||
else
|
|
||||||
log_failure_msg
|
|
||||||
fi
|
|
||||||
echo
|
|
||||||
else
|
|
||||||
echo "$DAEMON (pid $(cat $RUN_PID_FILE)) is already running..."
|
|
||||||
echo
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
stop()
|
|
||||||
{
|
|
||||||
echo -n $"Stopping $DAEMON:"
|
|
||||||
if [ -f $RUN_PID_FILE ]; then
|
|
||||||
killproc $DAEMON_PATH -TERM
|
|
||||||
log_success_msg
|
|
||||||
rm -f $RUN_PID_FILE
|
|
||||||
else
|
|
||||||
log_failure_msg
|
|
||||||
fi
|
|
||||||
echo
|
|
||||||
}
|
|
||||||
|
|
||||||
restart() {
|
|
||||||
stop
|
|
||||||
sleep 1
|
|
||||||
start
|
|
||||||
}
|
|
||||||
|
|
||||||
status()
|
|
||||||
{
|
|
||||||
if [ ! -f $RUN_PID_FILE ]; then
|
|
||||||
echo "$DAEMON is not running."
|
|
||||||
echo
|
|
||||||
else
|
|
||||||
echo "$DAEMON (pid $(cat $RUN_PID_FILE)) is running."
|
|
||||||
echo
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# How are we called?
|
|
||||||
case "$1" in
|
|
||||||
start)
|
|
||||||
start
|
|
||||||
;;
|
|
||||||
stop)
|
|
||||||
stop
|
|
||||||
;;
|
|
||||||
status)
|
|
||||||
status
|
|
||||||
;;
|
|
||||||
restart)
|
|
||||||
restart
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo "Usage: $DAEMON {start|stop|status|restart}"
|
|
||||||
RETVAL=1
|
|
||||||
esac
|
|
||||||
|
|
||||||
exit $RETVAL
|
|
||||||
@@ -1,116 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
### BEGIN INIT INFO
|
|
||||||
# Provides: cpuplugd
|
|
||||||
# Required-Start: $local_fs $remote_fs
|
|
||||||
# Required-Stop: $local_fs $remote_fs
|
|
||||||
# Should-Start:
|
|
||||||
# Should-Stop:
|
|
||||||
# Default-Start: 2 3 5
|
|
||||||
# Default-Stop: 0 1 6
|
|
||||||
# Short-Description: Start the cpu hotplug daemon for Linux on System z
|
|
||||||
# Description: Starts the cpuplugd. It uses the configuration
|
|
||||||
# file /etc/cpuplugd.conf
|
|
||||||
### END INIT INFO
|
|
||||||
|
|
||||||
# chkconfig: 235 01 99
|
|
||||||
|
|
||||||
DAEMON=cpuplugd
|
|
||||||
DAEMON_PATH=/usr/sbin/cpuplugd
|
|
||||||
CONFIG_FILE=/etc/cpuplugd.conf
|
|
||||||
RUN_PID_FILE=/run/cpuplugd.pid
|
|
||||||
RETVAL=0
|
|
||||||
OPTIONS="-c $CONFIG_FILE"
|
|
||||||
|
|
||||||
# source function library
|
|
||||||
. /lib/lsb/init-functions
|
|
||||||
|
|
||||||
start()
|
|
||||||
{
|
|
||||||
if [ ! -f $RUN_PID_FILE ]; then
|
|
||||||
echo -n $"Starting $DAEMON:"
|
|
||||||
$DAEMON_PATH $OPTIONS
|
|
||||||
if [ $? == "0" ]; then
|
|
||||||
log_success_msg
|
|
||||||
else
|
|
||||||
log_failure_msg
|
|
||||||
fi
|
|
||||||
echo
|
|
||||||
else
|
|
||||||
echo "$DAEMON (pid $(cat $RUN_PID_FILE)) is already running..."
|
|
||||||
echo
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
stop()
|
|
||||||
{
|
|
||||||
echo -n $"Stopping $DAEMON:"
|
|
||||||
if [ -f $RUN_PID_FILE ]; then
|
|
||||||
killproc $DAEMON_PATH -TERM
|
|
||||||
log_success_msg
|
|
||||||
rm -f $RUN_PID_FILE
|
|
||||||
else
|
|
||||||
log_failure_msg
|
|
||||||
fi
|
|
||||||
echo
|
|
||||||
}
|
|
||||||
|
|
||||||
restart() {
|
|
||||||
stop
|
|
||||||
#
|
|
||||||
# We have to wait 2-3 seconds here. When the daemon is stopped it takes
|
|
||||||
# the time we sleep to reactivate cpus. If we restart to fast and
|
|
||||||
# cpuplugd wasn't able to restore some settings we may get a undesired
|
|
||||||
# online cpu count after cpuplugd shutdown
|
|
||||||
#
|
|
||||||
sleep 4
|
|
||||||
start
|
|
||||||
}
|
|
||||||
|
|
||||||
status()
|
|
||||||
{
|
|
||||||
if [ ! -f $RUN_PID_FILE ]; then
|
|
||||||
echo "$DAEMON is not running."
|
|
||||||
echo
|
|
||||||
else
|
|
||||||
echo "$DAEMON (pid $(cat $RUN_PID_FILE), options: $OPTIONS) is running."
|
|
||||||
echo
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
reload()
|
|
||||||
{
|
|
||||||
echo -n $"Reloading $DAEMON: "
|
|
||||||
if [ -f $RUN_PID_FILE ]; then
|
|
||||||
killproc $DAEMON_PATH -HUP
|
|
||||||
log_success_msg
|
|
||||||
else
|
|
||||||
log_failure_msg
|
|
||||||
fi
|
|
||||||
RETVAL=$?
|
|
||||||
echo
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# How are we called?
|
|
||||||
case "$1" in
|
|
||||||
start)
|
|
||||||
start
|
|
||||||
;;
|
|
||||||
stop)
|
|
||||||
stop
|
|
||||||
;;
|
|
||||||
status)
|
|
||||||
status
|
|
||||||
;;
|
|
||||||
restart)
|
|
||||||
restart
|
|
||||||
;;
|
|
||||||
reload|force-reload)
|
|
||||||
reload
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo "Usage: $DAEMON {start|stop|status|restart|reload}"
|
|
||||||
RETVAL=1
|
|
||||||
esac
|
|
||||||
|
|
||||||
exit $RETVAL
|
|
||||||
@@ -1,161 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
### BEGIN INIT INFO
|
|
||||||
# Provides: mon_statd
|
|
||||||
# Required-Start: $local_fs $remote_fs
|
|
||||||
# Required-Stop: $local_fs $remote_fs
|
|
||||||
# Should-Start:
|
|
||||||
# Should-Stop:
|
|
||||||
# Default-Start: 2 3 5
|
|
||||||
# Default-Stop: 0 1 6
|
|
||||||
# Short-Description: Configure the mon_fsstatd and mon_procd daemons.
|
|
||||||
# Description: Configures the mon_fsstatd and mon_procd daemons. It uses the
|
|
||||||
# configuration file /etc/sysconfig/mon_statd.
|
|
||||||
### END INIT INFO
|
|
||||||
|
|
||||||
# chkconfig: 235 01 99
|
|
||||||
|
|
||||||
DAEMON=mon_statd
|
|
||||||
FSSTATD=mon_fsstatd
|
|
||||||
PROCD=mon_procd
|
|
||||||
FSSTATD_PATH=/usr/sbin/$FSSTATD
|
|
||||||
PROCD_PATH=/usr/sbin/$PROCD
|
|
||||||
CONFIG_FILE=/etc/sysconfig/$DAEMON
|
|
||||||
FSSTATD_PID_FILE=/run/$FSSTATD.pid
|
|
||||||
PROCD_PID_FILE=/run/$PROCD.pid
|
|
||||||
|
|
||||||
# source function library
|
|
||||||
. /lib/lsb/init-functions
|
|
||||||
|
|
||||||
# Source config file
|
|
||||||
if [ -f $CONFIG_FILE ]; then
|
|
||||||
. $CONFIG_FILE
|
|
||||||
fi
|
|
||||||
|
|
||||||
UDEVSETTLE=/usr/bin/udevadm
|
|
||||||
if [ ! -e $UDEVSETTLE ]
|
|
||||||
then
|
|
||||||
UDEVSETTLE=/sbin/udevsettle
|
|
||||||
UDEVSETTLE_CALL="$UDEVSETTLE --timeout=10"
|
|
||||||
else
|
|
||||||
UDEVSETTLE_CALL="$UDEVSETTLE settle --timeout=10"
|
|
||||||
fi
|
|
||||||
|
|
||||||
load_kernel_module()
|
|
||||||
{
|
|
||||||
if [ ! -e /dev/monwriter ]; then
|
|
||||||
echo "Loading monwriter module..."
|
|
||||||
modprobe monwriter 2>&1
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
if [ -e $UDEVSETTLE ]
|
|
||||||
then
|
|
||||||
$UDEVSETTLE_CALL
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
start_daemon()
|
|
||||||
{
|
|
||||||
local daemon_name=$1
|
|
||||||
local daemon_interval=$2
|
|
||||||
local daemon_pid_file=$3
|
|
||||||
local daemon_path=$4
|
|
||||||
|
|
||||||
if [ ! -f $daemon_pid_file ]; then
|
|
||||||
load_kernel_module
|
|
||||||
echo -n "Starting $daemon_name:"
|
|
||||||
$daemon_path -i $daemon_interval
|
|
||||||
if [ $? -eq 0 ]; then
|
|
||||||
log_success_msg
|
|
||||||
else
|
|
||||||
log_failure_msg
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "$daemon_name (pid $(cat $daemon_pid_file)) is already running..."
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
start()
|
|
||||||
{
|
|
||||||
if [ "$FSSTAT" = "yes" ]; then
|
|
||||||
start_daemon $FSSTATD $FSSTAT_INTERVAL $FSSTATD_PID_FILE \
|
|
||||||
$FSSTATD_PATH
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$PROC" = "yes" ]; then
|
|
||||||
start_daemon $PROCD $PROC_INTERVAL $PROCD_PID_FILE \
|
|
||||||
$PROCD_PATH
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
stop_daemon()
|
|
||||||
{
|
|
||||||
local daemon_name=$1
|
|
||||||
local daemon_pid_file=$2
|
|
||||||
local daemon_path=$3
|
|
||||||
|
|
||||||
echo -n "Stopping $daemon_name:"
|
|
||||||
if [ -f $daemon_pid_file ]; then
|
|
||||||
killproc $daemon_path -TERM
|
|
||||||
log_success_msg
|
|
||||||
rm -f $daemon_pid_file
|
|
||||||
else
|
|
||||||
log_failure_msg
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
stop()
|
|
||||||
{
|
|
||||||
if [ "$FSSTAT" = "yes" ]; then
|
|
||||||
stop_daemon $FSSTATD $FSSTATD_PID_FILE $FSSTATD_PATH
|
|
||||||
fi
|
|
||||||
if [ "$PROC" = "yes" ]; then
|
|
||||||
stop_daemon $PROCD $PROCD_PID_FILE $PROCD_PATH
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
restart() {
|
|
||||||
stop
|
|
||||||
start
|
|
||||||
}
|
|
||||||
|
|
||||||
status_daemon()
|
|
||||||
{
|
|
||||||
local daemon_name=$1
|
|
||||||
local daemon_pid_file=$2
|
|
||||||
local daemon_interval=$3
|
|
||||||
|
|
||||||
if [ ! -f $daemon_pid_file ]; then
|
|
||||||
echo "$daemon_name is not running."
|
|
||||||
else
|
|
||||||
echo "$daemon_name (pid $(cat $daemon_pid_file), interval: $daemon_interval) is running."
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
status()
|
|
||||||
{
|
|
||||||
status_daemon $FSSTATD $FSSTATD_PID_FILE $FSSTAT_INTERVAL
|
|
||||||
status_daemon $PROCD $PROCD_PID_FILE $PROC_INTERVAL
|
|
||||||
}
|
|
||||||
|
|
||||||
# How are we called?
|
|
||||||
case "$1" in
|
|
||||||
start)
|
|
||||||
start
|
|
||||||
;;
|
|
||||||
stop)
|
|
||||||
stop
|
|
||||||
;;
|
|
||||||
status)
|
|
||||||
status
|
|
||||||
;;
|
|
||||||
restart|reload|force-reload)
|
|
||||||
restart
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo "Usage: $DAEMON {start|stop|status|restart|reload}"
|
|
||||||
exit 1
|
|
||||||
esac
|
|
||||||
|
|
||||||
exit 0
|
|
||||||
@@ -26,10 +26,10 @@ KERNEL=="dasd*[0-9]", ENV{ID_XUID}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env
|
|||||||
|
|
||||||
LABEL="dasd_symlinks_end"
|
LABEL="dasd_symlinks_end"
|
||||||
|
|
||||||
# on device add set request queue scheduler to deadline
|
# on device add set request queue scheduler to none
|
||||||
SUBSYSTEM!="block", GOTO="sched_end"
|
SUBSYSTEM!="block", GOTO="sched_end"
|
||||||
|
|
||||||
ACTION!="change", GOTO="sched_end"
|
ACTION!="change", GOTO="sched_end"
|
||||||
KERNEL=="dasd*[!0-9]", TEST=="queue/scheduler", ATTR{queue/scheduler}="deadline"
|
KERNEL=="dasd*[!0-9]", TEST=="queue/scheduler", ATTR{queue/scheduler}="none"
|
||||||
|
|
||||||
LABEL="sched_end"
|
LABEL="sched_end"
|
||||||
|
|||||||
+3
-1
@@ -408,8 +408,10 @@ static int read_line(void)
|
|||||||
{
|
{
|
||||||
bzero(line_buffer, LINE_LENGTH);
|
bzero(line_buffer, LINE_LENGTH);
|
||||||
line_ptr = line_buffer;
|
line_ptr = line_buffer;
|
||||||
if (!fgets(line_buffer, LINE_LENGTH, stdin))
|
if (!fgets(line_buffer, LINE_LENGTH, stdin)) {
|
||||||
|
clearerr(stdin);
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
while (*line_ptr && !isgraph(*line_ptr))
|
while (*line_ptr && !isgraph(*line_ptr))
|
||||||
line_ptr++;
|
line_ptr++;
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,8 @@ ALL_CFLAGS := $(NO_PIE_CFLAGS) -Os -g \
|
|||||||
-fno-delete-null-pointer-checks -fno-stack-protector \
|
-fno-delete-null-pointer-checks -fno-stack-protector \
|
||||||
-fexec-charset=IBM1047 -m64 -mpacked-stack \
|
-fexec-charset=IBM1047 -m64 -mpacked-stack \
|
||||||
-mstack-size=4096 -mstack-guard=128 -msoft-float \
|
-mstack-size=4096 -mstack-guard=128 -msoft-float \
|
||||||
-Wall -Wformat-security -Wextra -Werror
|
-Wall -Wformat-security -Wextra -Werror \
|
||||||
|
-Wno-array-bounds
|
||||||
|
|
||||||
FILES := stage3a.bin stage3b.bin stage3b_reloc.bin
|
FILES := stage3a.bin stage3b.bin stage3b_reloc.bin
|
||||||
|
|
||||||
@@ -74,11 +75,11 @@ stage3b_reloc.elf:
|
|||||||
|
|
||||||
%.elf: %.o
|
%.elf: %.o
|
||||||
case $* in \
|
case $* in \
|
||||||
stage3a) SFLAGS="$(NO_PIE_LINKFLAGS) -nostdlib -Wl,-T,stage3a.lds";; \
|
stage3a) SFLAGS="-Wl,-T,stage3a.lds";; \
|
||||||
stage3b) SFLAGS="$(NO_PIE_LINKFLAGS) -nostdlib -Wl,-T,stage3b.lds";; \
|
stage3b) SFLAGS="-Wl,-T,stage3b.lds";; \
|
||||||
stage3b_reloc) SFLAGS="$(NO_PIE_LINKFLAGS) -nostdlib -Wl,-estage3b_reloc_start,-Ttext,0";; \
|
stage3b_reloc) SFLAGS="-Wl,-estage3b_reloc_start,-Ttext,0";; \
|
||||||
esac; \
|
esac; \
|
||||||
$(LINK) $$SFLAGS -m64 $(filter %.o, $^) -o $@
|
$(LINK) $$SFLAGS $(NO_PIE_LDFLAGS) -m64 -static -nostdlib $(filter %.o, $^) -o $@
|
||||||
@chmod a-x $@
|
@chmod a-x $@
|
||||||
|
|
||||||
%.bin: %.elf
|
%.bin: %.elf
|
||||||
|
|||||||
@@ -28,13 +28,23 @@ static inline void __noreturn load_psw(struct psw_t psw)
|
|||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static unsigned long get_kernel_cmdline_size(void)
|
||||||
|
{
|
||||||
|
unsigned long size = *(volatile unsigned long *)MAX_COMMAND_LINE_SIZE;
|
||||||
|
|
||||||
|
if (size != 0)
|
||||||
|
return size;
|
||||||
|
|
||||||
|
return LEGACY_COMMAND_LINE_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
void __noreturn start(void)
|
void __noreturn start(void)
|
||||||
{
|
{
|
||||||
volatile struct stage3b_args *args = &loader_parms;
|
volatile struct stage3b_args *args = &loader_parms;
|
||||||
volatile struct memblob *kernel = &args->kernel;
|
volatile struct memblob *kernel = &args->kernel;
|
||||||
volatile struct memblob *cmdline = &args->cmdline;
|
volatile struct memblob *cmdline = &args->cmdline;
|
||||||
volatile struct memblob *initrd = &args->initrd;
|
volatile struct memblob *initrd = &args->initrd;
|
||||||
volatile struct psw_t psw = args->psw;
|
struct psw_t psw = args->psw;
|
||||||
|
|
||||||
/* set up ASCII and line-mode */
|
/* set up ASCII and line-mode */
|
||||||
sclp_setup(SCLP_LINE_ASCII_INIT);
|
sclp_setup(SCLP_LINE_ASCII_INIT);
|
||||||
@@ -42,14 +52,14 @@ void __noreturn start(void)
|
|||||||
if (kernel->size < IMAGE_LOAD_ADDRESS)
|
if (kernel->size < IMAGE_LOAD_ADDRESS)
|
||||||
panic(EINTERNAL, "Invalid kernel\n");
|
panic(EINTERNAL, "Invalid kernel\n");
|
||||||
|
|
||||||
if (cmdline->size > COMMAND_LINE_SIZE)
|
|
||||||
panic(EINTERNAL, "Command line is too large\n");
|
|
||||||
|
|
||||||
/* move the kernel and cut the kernel header */
|
/* move the kernel and cut the kernel header */
|
||||||
memmove((void *)IMAGE_LOAD_ADDRESS,
|
memmove((void *)IMAGE_LOAD_ADDRESS,
|
||||||
(void *)(kernel->src + IMAGE_LOAD_ADDRESS),
|
(void *)(kernel->src + IMAGE_LOAD_ADDRESS),
|
||||||
kernel->size - IMAGE_LOAD_ADDRESS);
|
kernel->size - IMAGE_LOAD_ADDRESS);
|
||||||
|
|
||||||
|
if (cmdline->size > get_kernel_cmdline_size())
|
||||||
|
panic(EINTERNAL, "Command line is too large\n");
|
||||||
|
|
||||||
/* move the kernel cmdline */
|
/* move the kernel cmdline */
|
||||||
memmove((void *)COMMAND_LINE,
|
memmove((void *)COMMAND_LINE,
|
||||||
(void *)cmdline->src,
|
(void *)cmdline->src,
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ include ../../common.mak
|
|||||||
all:
|
all:
|
||||||
|
|
||||||
install:
|
install:
|
||||||
$(INSTALL) -d -m 755 $(DESTDIR)$(MANDIR)/man8
|
$(INSTALL) -d -m 755 $(DESTDIR)$(MANDIR)/man1
|
||||||
$(INSTALL) -m 644 -c genprotimg.8 $(DESTDIR)$(MANDIR)/man8
|
$(INSTALL) -m 644 -c genprotimg.1 $(DESTDIR)$(MANDIR)/man1
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
.\" s390-tools is free software; you can redistribute it and/or modify
|
.\" s390-tools is free software; you can redistribute it and/or modify
|
||||||
.\" it under the terms of the MIT license. See LICENSE for details.
|
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||||
.\"
|
.\"
|
||||||
.TH GENPROTIMG 8 "November 2020" "s390-tools"
|
.TH GENPROTIMG 1 "May 2022" "s390-tools"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
genprotimg \- Create a protected virtualization image
|
genprotimg \- Create a protected virtualization image
|
||||||
|
|
||||||
@@ -62,7 +62,8 @@ host.
|
|||||||
Specifies the certificate that is used to establish a chain of trust
|
Specifies the certificate that is used to establish a chain of trust
|
||||||
for the verification of the host-key documents. Specify this option
|
for the verification of the host-key documents. Specify this option
|
||||||
twice to specify the IBM Z signing key and the intermediate CA
|
twice to specify the IBM Z signing key and the intermediate CA
|
||||||
certificate (signed by the root CA). Required.
|
certificate (signed by the root CA).
|
||||||
|
Required. Ignored if \fB--no-verify\fP is specified.
|
||||||
.TP
|
.TP
|
||||||
\fB\-o\fR, \fB\-\-output\fR=\fI\,OUTPUT_FILE\/\fR
|
\fB\-o\fR, \fB\-\-output\fR=\fI\,OUTPUT_FILE\/\fR
|
||||||
Specifies the output file. Required.
|
Specifies the output file. Required.
|
||||||
@@ -87,31 +88,54 @@ CRLs. Optional.
|
|||||||
.TP
|
.TP
|
||||||
\fB\-\-root\-ca\fR=\fI\,FILE\/\fR
|
\fB\-\-root\-ca\fR=\fI\,FILE\/\fR
|
||||||
Specifies the root CA certificate for the verification. If omitted,
|
Specifies the root CA certificate for the verification. If omitted,
|
||||||
the DigiCert root CA certificate installed on the system is used. Use
|
the system wide root CAs installed on the system is used. Use
|
||||||
this only if you trust the specified certificate. Optional.
|
this only if you trust the specified certificate. Optional.
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-no-verify\fR
|
\fB\-\-no-verify\fR
|
||||||
Do not require the host-key documents to be valid. For testing
|
Does not require the host-key documents to be valid.
|
||||||
purposes, do not use for a production image. Optional.
|
Do not use for a production image unless you verified
|
||||||
|
the host-key document before. Optional.
|
||||||
|
.TP
|
||||||
|
\fB\-\-comm\-key\fR=\fI\,FILE\/\fR
|
||||||
|
Specifies the encryption key you want to use for the PV guest dump. Use a
|
||||||
|
secure, random, plaintext AES-256 GCM key. Optional.
|
||||||
|
.TP
|
||||||
|
\fB\-\-enable\-dump\fR
|
||||||
|
Enable PV guest dumps. Requires the \fB\-\-comm-key\fR option. Optional.
|
||||||
|
.TP
|
||||||
|
\fB\-\-disable\-dump\fR
|
||||||
|
Disable PV guest dumps. This is the default. Optional.
|
||||||
|
.TP
|
||||||
|
\fB\-\-enable\-pckmo\fR
|
||||||
|
Enable the support for the DEA, TDEA, AES, and ECC PCKMO key encryption
|
||||||
|
functions. This is the default. Optional.
|
||||||
|
.TP
|
||||||
|
\fB\-\-disable\-pckmo\fR
|
||||||
|
Disable the support for the DEA, TDEA, AES, and ECC PCKMO key encryption
|
||||||
|
functions. Optional.
|
||||||
.TP
|
.TP
|
||||||
\fB\-v\fR, \fB\-\-version\fR
|
\fB\-v\fR, \fB\-\-version\fR
|
||||||
Prints version information, then exits.
|
Prints version information, then exits.
|
||||||
|
|
||||||
.SH EXAMPLE
|
.SH EXAMPLES
|
||||||
|
|
||||||
|
These are examples to generate a protected virtualization image in
|
||||||
|
\fI\,/boot/vmlinuz.pv\/\fR, using the kernel file \fI\,vmlinuz\/\fR, the
|
||||||
|
initrd in \fI\,initramfs\/\fR, the kernel parameters contained in
|
||||||
|
\fI\,parmfile\/\fR, the intermediate CA in \fI\,DigiCertCA.crt\/\fR, the
|
||||||
|
IBM Z signing key in \fI\,ibm-z-host-key-signing.crt\/\fR, and the
|
||||||
|
host-key document in \fI\,host_key.crt\/\fR. An AES-256 GCM key is stored in
|
||||||
|
\fI\,comm-key\/\fR, which is used for the PV guest dump support in the second
|
||||||
|
example.
|
||||||
|
|
||||||
|
Generate a protected virtualization image:
|
||||||
|
|
||||||
.PP
|
.PP
|
||||||
Generate a protected virtualization image in
|
.B genprotimg \-i \fI\,vmlinuz\/\fR \-r \fI\,initramfs\/\fR \-p \fI\,parmfile\/\fR \-k \fI\,host_key.crt\/\fR \-C \fI\,ibm-z-host-key-signing.crt\/\fR \-C \fI\,DigiCertCA.crt\fR \-o \fI\,/boot/vmlinuz.pv\/\fR
|
||||||
\fI\,/boot/vmlinuz.pv\/\fR, using the kernel file \fI\,vmlinuz\/\fR,
|
|
||||||
the initrd in \fI\,initramfs\/\fR, the kernel parameters contained in
|
Generate a protected virtualization image with PV guest dump support:
|
||||||
\fI\,parmfile\/\fR, the intermediate CA in \fI\,DigiCertCA.crt\/\fR,
|
|
||||||
the IBM Z signing key in \fI\,ibm-z-host-key-signing.crt\/\fR, and the
|
|
||||||
host-key document in \fI\,host_key.crt\/\fR:
|
|
||||||
.PP
|
|
||||||
.Vb 1
|
|
||||||
.EX
|
|
||||||
\& genprotimg \-i \fI\,vmlinuz\/\fR \-r \fI\,initramfs\/\fR \-p \fI\,parmfile\/\fR \-k \fI\,host_key.crt\/\fR \-C \fI\,ibm-z-host-key-signing.crt\/\fR \-C \fI\,DigiCertCA.crt \-o \fI\,/boot/vmlinuz.pv\/\fR
|
|
||||||
.EE
|
|
||||||
.Ve
|
|
||||||
.PP
|
.PP
|
||||||
|
.B genprotimg \-i \fI\,vmlinuz\/\fR \-r \fI\,initramfs\/\fR \-p \fI\,parmfile\/\fR \-k \fI\,host_key.crt\/\fR \-C \fI\,ibm-z-host-key-signing.crt\/\fR \-C \fI\,DigiCertCA.crt\fR \-o \fI\,/boot/vmlinuz.pv\/\fR \-\-enable\-dump \-\-comm\-key \fI\,comm-key\fR
|
||||||
|
|
||||||
.SH NOTES
|
.SH NOTES
|
||||||
.IP "1." 4
|
.IP "1." 4
|
||||||
@@ -23,6 +23,7 @@ BODY_FILE=$(mktemp)
|
|||||||
ISSUER_DN_FILE=$(mktemp)
|
ISSUER_DN_FILE=$(mktemp)
|
||||||
SUBJECT_DN_FILE=$(mktemp)
|
SUBJECT_DN_FILE=$(mktemp)
|
||||||
DEF_ISSUER_DN_FILE=$(mktemp)
|
DEF_ISSUER_DN_FILE=$(mktemp)
|
||||||
|
CANONICAL_ISSUER_DN_FILE=$(mktemp)
|
||||||
CRL_SERIAL_FILE=$(mktemp)
|
CRL_SERIAL_FILE=$(mktemp)
|
||||||
|
|
||||||
# Cleanup on exit
|
# Cleanup on exit
|
||||||
@@ -30,7 +31,7 @@ cleanup()
|
|||||||
{
|
{
|
||||||
rm -f $ISSUER_PUBKEY_FILE $SIGNATURE_FILE $BODY_FILE \
|
rm -f $ISSUER_PUBKEY_FILE $SIGNATURE_FILE $BODY_FILE \
|
||||||
$ISSUER_DN_FILE $SUBJECT_DN_FILE $DEF_ISSUER_DN_FILE \
|
$ISSUER_DN_FILE $SUBJECT_DN_FILE $DEF_ISSUER_DN_FILE \
|
||||||
$CRL_SERIAL_FILE
|
$CANONICAL_ISSUER_DN_FILE $CRL_SERIAL_FILE
|
||||||
}
|
}
|
||||||
trap cleanup EXIT
|
trap cleanup EXIT
|
||||||
|
|
||||||
@@ -47,13 +48,21 @@ set -e
|
|||||||
usage()
|
usage()
|
||||||
{
|
{
|
||||||
cat <<-EOF
|
cat <<-EOF
|
||||||
Usage: `basename $1` host-key-doc signing-key-cert [-c CA-cert] [-r CRL]
|
Usage: `basename $1` [-d] [-c CA-cert] [-r CRL] host-key-doc signing-key-cert
|
||||||
|
|
||||||
Verify an IBM Secure Execution host key document against
|
Verify an IBM Secure Execution host key document against
|
||||||
a signing key.
|
a signing key.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-d disable default issuer check of host-key-doc
|
||||||
|
-c CA-cert trusted CA certificate
|
||||||
|
-r CRL list of revoked host-key-docs
|
||||||
|
|
||||||
Note that in order to have the full trust chain verified
|
Note that in order to have the full trust chain verified
|
||||||
it is necessary to provide the issueing CA's certificate.
|
it is necessary to provide the issuing CA's certificate.
|
||||||
|
The default issuer check may be disabled if a non-default
|
||||||
|
signing key certificate needs to be verified against the
|
||||||
|
CA certificate.
|
||||||
|
|
||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
@@ -121,20 +130,34 @@ default_issuer()
|
|||||||
commonName = International Business Machines Corporation
|
commonName = International Business Machines Corporation
|
||||||
countryName = US
|
countryName = US
|
||||||
localityName = Poughkeepsie
|
localityName = Poughkeepsie
|
||||||
organizationalUnitName = IBM Z Host Key Signing Service
|
organizationalUnitName = Key Signing Service
|
||||||
organizationName = International Business Machines Corporation
|
organizationName = International Business Machines Corporation
|
||||||
stateOrProvinceName = New York
|
stateOrProvinceName = New York
|
||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
verify_issuer_files()
|
# As organizationalUnitName can have an arbitrary prefix but must
|
||||||
|
# end with "Key Signing Service" let's normalize the OU name by
|
||||||
|
# stripping off the prefix
|
||||||
|
verify_default_issuer()
|
||||||
{
|
{
|
||||||
default_issuer > $DEF_ISSUER_DN_FILE
|
default_issuer > $DEF_ISSUER_DN_FILE
|
||||||
|
|
||||||
if ! diff $ISSUER_DN_FILE $DEF_ISSUER_DN_FILE
|
sed "s/\(^[ ]*organizationalUnitName[ ]*=[ ]*\).*\(Key Signing Service$\)/\1\2/" \
|
||||||
|
$ISSUER_DN_FILE > $CANONICAL_ISSUER_DN_FILE
|
||||||
|
|
||||||
|
if ! diff $CANONICAL_ISSUER_DN_FILE $DEF_ISSUER_DN_FILE
|
||||||
then
|
then
|
||||||
echo Incorrect default issuer >&2 && exit 1
|
echo Incorrect default issuer >&2 && exit 1
|
||||||
fi
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
verify_issuer_files()
|
||||||
|
{
|
||||||
|
if [ $1 -eq 1 ]
|
||||||
|
then
|
||||||
|
verify_default_issuer
|
||||||
|
fi
|
||||||
|
|
||||||
if diff $ISSUER_DN_FILE $SUBJECT_DN_FILE
|
if diff $ISSUER_DN_FILE $SUBJECT_DN_FILE
|
||||||
then
|
then
|
||||||
@@ -196,14 +219,16 @@ check_file()
|
|||||||
# check args
|
# check args
|
||||||
CRL_FILE=
|
CRL_FILE=
|
||||||
CA_FILE=
|
CA_FILE=
|
||||||
|
CHECK_DEFAULT_ISSUER=1
|
||||||
|
|
||||||
args=$(getopt -qu "r:c:h" $*)
|
args=$(getopt -qu "dr:c:h" $*)
|
||||||
if [ $? = 0 ]
|
if [ $? = 0 ]
|
||||||
then
|
then
|
||||||
set -- $args
|
set -- $args
|
||||||
while [ $1 != "" ]
|
while [ $1 != "" ]
|
||||||
do
|
do
|
||||||
case $1 in
|
case $1 in
|
||||||
|
-d) CHECK_DEFAULT_ISSUER=0; shift;;
|
||||||
-r) CRL_FILE=$2; shift 2;;
|
-r) CRL_FILE=$2; shift 2;;
|
||||||
-c) CA_FILE=$2; shift 2;;
|
-c) CA_FILE=$2; shift 2;;
|
||||||
-h) usage $0; exit 0;;
|
-h) usage $0; exit 0;;
|
||||||
@@ -244,7 +269,7 @@ exit 1
|
|||||||
# Verify the issuer
|
# Verify the issuer
|
||||||
canonical_dn x509 $HKD_FILE issuer $ISSUER_DN_FILE
|
canonical_dn x509 $HKD_FILE issuer $ISSUER_DN_FILE
|
||||||
canonical_dn x509 $HKSK_FILE subject $SUBJECT_DN_FILE
|
canonical_dn x509 $HKSK_FILE subject $SUBJECT_DN_FILE
|
||||||
verify_issuer_files
|
verify_issuer_files $CHECK_DEFAULT_ISSUER
|
||||||
|
|
||||||
# Verify dates
|
# Verify dates
|
||||||
verify_dates $(cert_time $HKD_FILE startdate) $(cert_time $HKD_FILE enddate)
|
verify_dates $(cert_time $HKD_FILE startdate) $(cert_time $HKD_FILE enddate)
|
||||||
@@ -261,7 +286,7 @@ then
|
|||||||
echo -n "CRL "
|
echo -n "CRL "
|
||||||
canonical_dn crl $CRL_FILE issuer $ISSUER_DN_FILE
|
canonical_dn crl $CRL_FILE issuer $ISSUER_DN_FILE
|
||||||
canonical_dn x509 $HKSK_FILE subject $SUBJECT_DN_FILE
|
canonical_dn x509 $HKSK_FILE subject $SUBJECT_DN_FILE
|
||||||
verify_issuer_files
|
verify_issuer_files $CHECK_DEFAULT_ISSUER
|
||||||
|
|
||||||
verify_dates $(crl_time $CRL_FILE lastupdate) $(crl_time $CRL_FILE nextupdate) 'CRL'
|
verify_dates $(crl_time $CRL_FILE lastupdate) $(crl_time $CRL_FILE nextupdate) 'CRL'
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ $(bin_PROGRAM)_OBJS := $($(bin_PROGRAM)_SRCS:.c=.o)
|
|||||||
|
|
||||||
ALL_CFLAGS += -std=gnu11 -DPKGDATADIR=$(PKGDATADIR) \
|
ALL_CFLAGS += -std=gnu11 -DPKGDATADIR=$(PKGDATADIR) \
|
||||||
$(GLIB2_CFLAGS) $(LIBCRYPTO_CFLAGS) $(LIBCURL_CFLAGS) \
|
$(GLIB2_CFLAGS) $(LIBCRYPTO_CFLAGS) $(LIBCURL_CFLAGS) \
|
||||||
|
-DOPENSSL_API_COMPAT=0x10100000L \
|
||||||
$(WARNINGS) \
|
$(WARNINGS) \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
ALL_CPPFLAGS += $(INCLUDE_PARMS)
|
ALL_CPPFLAGS += $(INCLUDE_PARMS)
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
/*
|
||||||
|
* Copyright IBM Corp. 2020, 2022
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef COMMON_H
|
#ifndef COMMON_H
|
||||||
#define COMMON_H
|
#define COMMON_H
|
||||||
|
|
||||||
|
|||||||
@@ -29,9 +29,6 @@
|
|||||||
*/
|
*/
|
||||||
#define PV_CERTS_SECURITY_LEVEL 2
|
#define PV_CERTS_SECURITY_LEVEL 2
|
||||||
|
|
||||||
/* SKID for DigiCert Assured ID Root CA */
|
|
||||||
#define DIGICERT_ASSURED_ID_ROOT_CA_SKID "45EBA2AFF492CB82312D518BA7A7219DF36DC80F"
|
|
||||||
|
|
||||||
union ecdh_pub_key {
|
union ecdh_pub_key {
|
||||||
struct {
|
struct {
|
||||||
uint8_t x[80];
|
uint8_t x[80];
|
||||||
|
|||||||
@@ -24,8 +24,15 @@
|
|||||||
#define PV_MAGIC_NUMBER 0x49424d5365634578ULL
|
#define PV_MAGIC_NUMBER 0x49424d5365634578ULL
|
||||||
#define PV_VERSION_1 0x00000100U
|
#define PV_VERSION_1 0x00000100U
|
||||||
|
|
||||||
/* prevent Ultravisor decryption during unpack operation */
|
/* Internal helper macro */
|
||||||
#define PV_CFLAG_NO_DECRYPTION 0x10000000ULL
|
#define __PV_BIT(nr) (1ULL << (63 - (nr)))
|
||||||
|
|
||||||
|
/* Plaintext control flags */
|
||||||
|
#define PV_PCF_ALLOW_DUMPING __PV_BIT(34) /* dumping of the configuration is allowed */
|
||||||
|
#define PV_PCF_NO_DECRYPTION __PV_BIT(35) /* prevent Ultravisor decryption during unpack operation */
|
||||||
|
#define PV_PCF_PCKMO_DEA_TDEA __PV_BIT(56) /* PCKMO encrypt-DEA/TDEA-key functions allowed */
|
||||||
|
#define PV_PCF_PCKMO_AES __PV_BIT(57) /* PCKMO encrypt-AES-key functions allowed */
|
||||||
|
#define PV_PCF_PCKM_ECC __PV_BIT(58) /* PCKMO encrypt-ECC-key functions allowed */
|
||||||
|
|
||||||
/* maxima for the PV version 1 */
|
/* maxima for the PV version 1 */
|
||||||
#define PV_V1_IPIB_MAX_SIZE PAGE_SIZE
|
#define PV_V1_IPIB_MAX_SIZE PAGE_SIZE
|
||||||
|
|||||||
+93
-14
@@ -64,6 +64,17 @@ static gint pv_args_validate_options(PvArgs *args, GError **err)
|
|||||||
{
|
{
|
||||||
PvComponentType KERNEL = PV_COMP_TYPE_KERNEL;
|
PvComponentType KERNEL = PV_COMP_TYPE_KERNEL;
|
||||||
|
|
||||||
|
/* Check for mutually exclusive arguments */
|
||||||
|
if (args->pcf && !(args->allow_pckmo == PV_NOT_SET &&
|
||||||
|
args->allow_dump == PV_NOT_SET)) {
|
||||||
|
g_set_error(
|
||||||
|
err, PV_PARSE_ERROR, PV_PARSE_ERROR_SYNTAX,
|
||||||
|
_("The '--x-pcf' option cannot be used with the '--(enable|disable)-pckmo' or"
|
||||||
|
" '--(enable|disable)-dump' flags.\nUse 'genprotimg --help' for more information"));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check for unused arguments */
|
||||||
if (args->unused_values->len > 0) {
|
if (args->unused_values->len > 0) {
|
||||||
g_autofree gchar *unused = NULL;
|
g_autofree gchar *unused = NULL;
|
||||||
|
|
||||||
@@ -81,6 +92,14 @@ static gint pv_args_validate_options(PvArgs *args, GError **err)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Check for mandatory arguments */
|
||||||
|
if (args->allow_dump == PV_TRUE && !args->cust_comm_key_path) {
|
||||||
|
g_set_error(err, PV_PARSE_ERROR, PR_PARSE_ERROR_MISSING_ARGUMENT,
|
||||||
|
_("Option '--allow-dump' requires the '--comm-key' option.\nUse 'genprotimg "
|
||||||
|
"--help' for more information"));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (!args->output_path) {
|
if (!args->output_path) {
|
||||||
g_set_error(err, PV_PARSE_ERROR, PR_PARSE_ERROR_MISSING_ARGUMENT,
|
g_set_error(err, PV_PARSE_ERROR, PR_PARSE_ERROR_MISSING_ARGUMENT,
|
||||||
_("Option '--output' is required.\nUse 'genprotimg --help' for more information"));
|
_("Option '--output' is required.\nUse 'genprotimg --help' for more information"));
|
||||||
@@ -104,7 +123,7 @@ static gint pv_args_validate_options(PvArgs *args, GError **err)
|
|||||||
g_strv_length(args->untrusted_cert_paths) == 0)) {
|
g_strv_length(args->untrusted_cert_paths) == 0)) {
|
||||||
g_set_error(
|
g_set_error(
|
||||||
err, PV_PARSE_ERROR, PR_PARSE_ERROR_MISSING_ARGUMENT,
|
err, PV_PARSE_ERROR, PR_PARSE_ERROR_MISSING_ARGUMENT,
|
||||||
_("Either specify the IBM Z signing key and (DigiCert) intermediate CA certificate\n"
|
_("Either specify the IBM Z signing key and intermediate CA certificate\n"
|
||||||
"by using the '--cert' option, or use the '--no-verify' flag to disable the\n"
|
"by using the '--cert' option, or use the '--no-verify' flag to disable the\n"
|
||||||
"host-key document verification completely (at your own risk)."));
|
"host-key document verification completely (at your own risk)."));
|
||||||
return -1;
|
return -1;
|
||||||
@@ -148,14 +167,14 @@ static gboolean cb_set_string_option(const gchar *option, const gchar *value,
|
|||||||
{
|
{
|
||||||
gchar **args_option = NULL;
|
gchar **args_option = NULL;
|
||||||
|
|
||||||
|
if (g_str_equal(option, "--comm-key"))
|
||||||
|
args_option = &args->cust_comm_key_path;
|
||||||
if (g_str_equal(option, "--root-ca"))
|
if (g_str_equal(option, "--root-ca"))
|
||||||
args_option = &args->root_ca_path;
|
args_option = &args->root_ca_path;
|
||||||
if (g_str_equal(option, "-o") || g_str_equal(option, "--output"))
|
if (g_str_equal(option, "-o") || g_str_equal(option, "--output"))
|
||||||
args_option = &args->output_path;
|
args_option = &args->output_path;
|
||||||
if (g_str_equal(option, "--x-comp-key"))
|
if (g_str_equal(option, "--x-comp-key"))
|
||||||
args_option = &args->xts_key_path;
|
args_option = &args->xts_key_path;
|
||||||
if (g_str_equal(option, "--x-comm-key"))
|
|
||||||
args_option = &args->cust_comm_key_path;
|
|
||||||
if (g_str_equal(option, "--x-header-key"))
|
if (g_str_equal(option, "--x-header-key"))
|
||||||
args_option = &args->cust_root_key_path;
|
args_option = &args->cust_root_key_path;
|
||||||
if (g_str_equal(option, "--x-pcf"))
|
if (g_str_equal(option, "--x-pcf"))
|
||||||
@@ -197,8 +216,51 @@ static gboolean cb_remaining_values(const gchar *option G_GNUC_UNUSED,
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define MUT_EXCL_BOOL_FLAG_CB_NAME(FLAG, VALUE) (cb_##FLAG##_##VALUE)
|
||||||
|
#define DEFINE_MUT_EXCL_BOOL_FLAG_CB(FLAG, VALUE) \
|
||||||
|
static gboolean MUT_EXCL_BOOL_FLAG_CB_NAME(FLAG, VALUE)( \
|
||||||
|
const gchar *option G_GNUC_UNUSED, const gchar *value G_GNUC_UNUSED, \
|
||||||
|
PvArgs *args, GError **err) \
|
||||||
|
{ \
|
||||||
|
if (!(args->allow_##FLAG == PV_NOT_SET || \
|
||||||
|
args->allow_##FLAG == VALUE)) { \
|
||||||
|
g_set_error(err, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, \
|
||||||
|
"'--enable-" #FLAG "' and '--disable-" #FLAG \
|
||||||
|
"' are mutually exclusive"); \
|
||||||
|
return FALSE; \
|
||||||
|
} \
|
||||||
|
args->allow_##FLAG = VALUE; \
|
||||||
|
return TRUE; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define DEFINE_MUT_EXCL_BOOL_FLAG_CBS(FLAG) \
|
||||||
|
DEFINE_MUT_EXCL_BOOL_FLAG_CB(FLAG, PV_TRUE) \
|
||||||
|
DEFINE_MUT_EXCL_BOOL_FLAG_CB(FLAG, PV_FALSE)
|
||||||
|
|
||||||
|
#define MUT_EXCL_BOOL_FLAG(FLAG, ENABLE_DESC, DISABLE_DESC) \
|
||||||
|
{ \
|
||||||
|
.long_name = "enable-" #FLAG, \
|
||||||
|
.short_name = 0, \
|
||||||
|
.flags = G_OPTION_FLAG_NO_ARG, \
|
||||||
|
.arg = G_OPTION_ARG_CALLBACK, \
|
||||||
|
.arg_data = MUT_EXCL_BOOL_FLAG_CB_NAME(FLAG, PV_TRUE), \
|
||||||
|
.description = ENABLE_DESC, \
|
||||||
|
}, \
|
||||||
|
{ \
|
||||||
|
.long_name = "disable-" #FLAG, \
|
||||||
|
.short_name = 0, \
|
||||||
|
.flags = G_OPTION_FLAG_NO_ARG, \
|
||||||
|
.arg = G_OPTION_ARG_CALLBACK, \
|
||||||
|
.arg_data = MUT_EXCL_BOOL_FLAG_CB_NAME(FLAG, PV_FALSE), \
|
||||||
|
.description = DISABLE_DESC, \
|
||||||
|
}
|
||||||
|
|
||||||
#define INDENT " "
|
#define INDENT " "
|
||||||
|
|
||||||
|
/* Define the callbacks for mutually exclusive command line flags */
|
||||||
|
DEFINE_MUT_EXCL_BOOL_FLAG_CBS(dump)
|
||||||
|
DEFINE_MUT_EXCL_BOOL_FLAG_CBS(pckmo)
|
||||||
|
|
||||||
gint pv_args_parse_options(PvArgs *args, gint *argc, gchar **argv[],
|
gint pv_args_parse_options(PvArgs *args, gint *argc, gchar **argv[],
|
||||||
GError **err)
|
GError **err)
|
||||||
{
|
{
|
||||||
@@ -218,7 +280,8 @@ gint pv_args_parse_options(PvArgs *args, gint *argc, gchar **argv[],
|
|||||||
.arg_data = &args->host_keys,
|
.arg_data = &args->host_keys,
|
||||||
.description =
|
.description =
|
||||||
_("FILE specifies a host-key document. At least\n" INDENT
|
_("FILE specifies a host-key document. At least\n" INDENT
|
||||||
"one is required."),
|
"one is required Specify this option multiple times\n" INDENT
|
||||||
|
"to enable the image to run on more than one host."),
|
||||||
.arg_description = _("FILE") },
|
.arg_description = _("FILE") },
|
||||||
{ .long_name = "cert",
|
{ .long_name = "cert",
|
||||||
.short_name = 'C',
|
.short_name = 'C',
|
||||||
@@ -262,6 +325,28 @@ gint pv_args_parse_options(PvArgs *args, gint *argc, gchar **argv[],
|
|||||||
.description = _("Use the kernel parameters stored in PARMFILE\n" INDENT
|
.description = _("Use the kernel parameters stored in PARMFILE\n" INDENT
|
||||||
"(optional)."),
|
"(optional)."),
|
||||||
.arg_description = _("PARMFILE") },
|
.arg_description = _("PARMFILE") },
|
||||||
|
MUT_EXCL_BOOL_FLAG(
|
||||||
|
dump,
|
||||||
|
_("Enable PV guest dumps (optional). This option\n" INDENT
|
||||||
|
"requires the '--comm-key' option."),
|
||||||
|
_("Disable PV guest dumps (default) (optional).")),
|
||||||
|
MUT_EXCL_BOOL_FLAG(
|
||||||
|
pckmo,
|
||||||
|
_("Enable the support for the DEA, TDEA, AES, and\n" INDENT
|
||||||
|
"ECC PCKMO key encryption functions (default)\n" INDENT
|
||||||
|
"(optional)."),
|
||||||
|
_("Disable the support for the DEA, TDEA, AES, and\n" INDENT
|
||||||
|
"ECC PCKMO key encryption functions (optional).")),
|
||||||
|
{ .long_name = "comm-key",
|
||||||
|
.short_name = 0,
|
||||||
|
.flags = G_OPTION_FLAG_FILENAME,
|
||||||
|
.arg = G_OPTION_ARG_CALLBACK,
|
||||||
|
.arg_data = cb_set_string_option,
|
||||||
|
.description = _(
|
||||||
|
"FILE contains the key with which you encrypt\n" INDENT
|
||||||
|
"the PV guest dump (optional). Required by\n" INDENT
|
||||||
|
"the '--enable-dump' option."),
|
||||||
|
.arg_description = _("FILE") },
|
||||||
{ .long_name = "crl",
|
{ .long_name = "crl",
|
||||||
.short_name = 0,
|
.short_name = 0,
|
||||||
.flags = G_OPTION_FLAG_NONE,
|
.flags = G_OPTION_FLAG_NONE,
|
||||||
@@ -320,15 +405,6 @@ gint pv_args_parse_options(PvArgs *args, gint *argc, gchar **argv[],
|
|||||||
};
|
};
|
||||||
|
|
||||||
GOptionEntry x_entries[] = {
|
GOptionEntry x_entries[] = {
|
||||||
{ .long_name = "x-comm-key",
|
|
||||||
.short_name = 0,
|
|
||||||
.flags = G_OPTION_FLAG_FILENAME,
|
|
||||||
.arg = G_OPTION_ARG_CALLBACK,
|
|
||||||
.arg_data = cb_set_string_option,
|
|
||||||
.description = _(
|
|
||||||
"Use FILE as the customer communication key.\n" INDENT
|
|
||||||
"Optional; default: auto-generated."),
|
|
||||||
.arg_description = _("FILE") },
|
|
||||||
{ .long_name = "x-comp-key",
|
{ .long_name = "x-comp-key",
|
||||||
.short_name = 0,
|
.short_name = 0,
|
||||||
.flags = G_OPTION_FLAG_FILENAME,
|
.flags = G_OPTION_FLAG_FILENAME,
|
||||||
@@ -357,7 +433,8 @@ gint pv_args_parse_options(PvArgs *args, gint *argc, gchar **argv[],
|
|||||||
.description =
|
.description =
|
||||||
_("Specify the plaintext control flags\n" INDENT
|
_("Specify the plaintext control flags\n" INDENT
|
||||||
"as a hexadecimal value.\n" INDENT
|
"as a hexadecimal value.\n" INDENT
|
||||||
"Optional; default: '0x0'."),
|
"Optional; mutually exclusive with\n" INDENT
|
||||||
|
"'--(enable|disable)-pckmo'; default: '0xe0'."),
|
||||||
.arg_description = _("VALUE") },
|
.arg_description = _("VALUE") },
|
||||||
{ .long_name = "x-psw",
|
{ .long_name = "x-psw",
|
||||||
.short_name = 0,
|
.short_name = 0,
|
||||||
@@ -410,6 +487,8 @@ PvArgs *pv_args_new(void)
|
|||||||
g_autoptr(PvArgs) args = g_new0(PvArgs, 1);
|
g_autoptr(PvArgs) args = g_new0(PvArgs, 1);
|
||||||
|
|
||||||
args->unused_values = g_ptr_array_new_with_free_func(g_free);
|
args->unused_values = g_ptr_array_new_with_free_func(g_free);
|
||||||
|
args->allow_dump = PV_NOT_SET;
|
||||||
|
args->allow_pckmo = PV_NOT_SET;
|
||||||
return g_steal_pointer(&args);
|
return g_steal_pointer(&args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,12 +22,20 @@ typedef struct pv_arg {
|
|||||||
PvArg *pv_arg_new(PvComponentType type, const gchar *path);
|
PvArg *pv_arg_new(PvComponentType type, const gchar *path);
|
||||||
void pv_arg_free(PvArg *arg);
|
void pv_arg_free(PvArg *arg);
|
||||||
|
|
||||||
|
typedef enum pv_tristate {
|
||||||
|
PV_NOT_SET = 0,
|
||||||
|
PV_TRUE,
|
||||||
|
PV_FALSE,
|
||||||
|
} PvTristate;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
gint log_level;
|
gint log_level;
|
||||||
gint no_verify;
|
gint no_verify;
|
||||||
gboolean offline;
|
gboolean offline;
|
||||||
gchar *pcf;
|
gchar *pcf;
|
||||||
gchar *scf;
|
gchar *scf;
|
||||||
|
PvTristate allow_dump;
|
||||||
|
PvTristate allow_pckmo;
|
||||||
gchar *psw_addr; /* PSW address which will be used for the start of
|
gchar *psw_addr; /* PSW address which will be used for the start of
|
||||||
* the actual component (e.g. Linux kernel)
|
* the actual component (e.g. Linux kernel)
|
||||||
*/
|
*/
|
||||||
|
|||||||
+17
-30
@@ -73,12 +73,12 @@ PvComponent *pv_component_new_file(PvComponentType type, const gchar *path,
|
|||||||
return pv_component_new(type, size, DATA_FILE, (void **)&file, err);
|
return pv_component_new(type, size, DATA_FILE, (void **)&file, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
PvComponent *pv_component_new_buf(PvComponentType type, const Buffer *buf,
|
PvComponent *pv_component_new_buf(PvComponentType type, const PvBuffer *buf,
|
||||||
GError **err)
|
GError **err)
|
||||||
{
|
{
|
||||||
g_assert(buf);
|
g_assert(buf);
|
||||||
|
|
||||||
g_autoptr(Buffer) dup_buf = buffer_dup(buf, FALSE);
|
g_autoptr(PvBuffer) dup_buf = pv_buffer_dup(buf, FALSE);
|
||||||
return pv_component_new(type, buf->size, DATA_BUFFER, (void **)&dup_buf,
|
return pv_component_new(type, buf->size, DATA_BUFFER, (void **)&dup_buf,
|
||||||
err);
|
err);
|
||||||
}
|
}
|
||||||
@@ -90,7 +90,7 @@ void pv_component_free(PvComponent *component)
|
|||||||
|
|
||||||
switch ((PvComponentDataType)component->d_type) {
|
switch ((PvComponentDataType)component->d_type) {
|
||||||
case DATA_BUFFER:
|
case DATA_BUFFER:
|
||||||
buffer_clear(&component->buf);
|
pv_buffer_clear(&component->buf);
|
||||||
break;
|
break;
|
||||||
case DATA_FILE:
|
case DATA_FILE:
|
||||||
comp_file_free(component->file);
|
comp_file_free(component->file);
|
||||||
@@ -162,21 +162,21 @@ gint pv_component_align_and_encrypt(PvComponent *component, const gchar *tmp_pat
|
|||||||
|
|
||||||
switch ((PvComponentDataType)component->d_type) {
|
switch ((PvComponentDataType)component->d_type) {
|
||||||
case DATA_BUFFER: {
|
case DATA_BUFFER: {
|
||||||
g_autoptr(Buffer) enc_buf = NULL;
|
g_autoptr(PvBuffer) enc_buf = NULL;
|
||||||
|
|
||||||
if (!(IS_PAGE_ALIGNED(pv_component_size(component)))) {
|
if (!(IS_PAGE_ALIGNED(pv_component_size(component)))) {
|
||||||
g_autoptr(Buffer) new = NULL;
|
g_autoptr(PvBuffer) new = NULL;
|
||||||
|
|
||||||
/* create a page aligned copy */
|
/* create a page aligned copy */
|
||||||
new = buffer_dup(component->buf, TRUE);
|
new = pv_buffer_dup(component->buf, TRUE);
|
||||||
buffer_clear(&component->buf);
|
pv_buffer_clear(&component->buf);
|
||||||
component->buf = g_steal_pointer(&new);
|
component->buf = g_steal_pointer(&new);
|
||||||
}
|
}
|
||||||
enc_buf = encrypt_buf(parms, component->buf, err);
|
enc_buf = encrypt_buf(parms, component->buf, err);
|
||||||
if (!enc_buf)
|
if (!enc_buf)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
buffer_clear(&component->buf);
|
pv_buffer_clear(&component->buf);
|
||||||
component->buf = g_steal_pointer(&enc_buf);
|
component->buf = g_steal_pointer(&enc_buf);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -220,10 +220,10 @@ gint pv_component_align(PvComponent *component, const gchar *tmp_path,
|
|||||||
|
|
||||||
switch (component->d_type) {
|
switch (component->d_type) {
|
||||||
case DATA_BUFFER: {
|
case DATA_BUFFER: {
|
||||||
g_autoptr(Buffer) buf = NULL;
|
g_autoptr(PvBuffer) buf = NULL;
|
||||||
|
|
||||||
buf = buffer_dup(component->buf, TRUE);
|
buf = pv_buffer_dup(component->buf, TRUE);
|
||||||
buffer_clear(&component->buf);
|
pv_buffer_clear(&component->buf);
|
||||||
component->buf = g_steal_pointer(&buf);
|
component->buf = g_steal_pointer(&buf);
|
||||||
return 0;
|
return 0;
|
||||||
} break;
|
} break;
|
||||||
@@ -248,19 +248,6 @@ gint pv_component_align(PvComponent *component, const gchar *tmp_path,
|
|||||||
g_assert_not_reached();
|
g_assert_not_reached();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Convert uint64_t address to byte array */
|
|
||||||
static void uint64_to_uint8_buf(uint8_t dst[8], uint64_t addr)
|
|
||||||
{
|
|
||||||
uint8_t *p = (uint8_t *)&addr;
|
|
||||||
|
|
||||||
g_assert(dst);
|
|
||||||
|
|
||||||
for (gint i = 0; i < 8; i++) {
|
|
||||||
/* cppcheck-suppress objectIndex */
|
|
||||||
dst[i] = p[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t pv_component_update_ald(const PvComponent *comp, EVP_MD_CTX *ctx,
|
int64_t pv_component_update_ald(const PvComponent *comp, EVP_MD_CTX *ctx,
|
||||||
GError **err)
|
GError **err)
|
||||||
{
|
{
|
||||||
@@ -273,11 +260,8 @@ int64_t pv_component_update_ald(const PvComponent *comp, EVP_MD_CTX *ctx,
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
uint64_t cur_be = GUINT64_TO_BE(cur);
|
uint64_t cur_be = GUINT64_TO_BE(cur);
|
||||||
uint8_t addr_buf[8];
|
|
||||||
|
|
||||||
uint64_to_uint8_buf(addr_buf, cur_be);
|
if (EVP_DigestUpdate(ctx, &cur_be, sizeof(cur_be)) != 1) {
|
||||||
|
|
||||||
if (EVP_DigestUpdate(ctx, addr_buf, sizeof(addr_buf)) != 1) {
|
|
||||||
g_set_error(err, PV_CRYPTO_ERROR,
|
g_set_error(err, PV_CRYPTO_ERROR,
|
||||||
PV_CRYPTO_ERROR_INTERNAL,
|
PV_CRYPTO_ERROR_INTERNAL,
|
||||||
_("EVP_DigestUpdate failed"));
|
_("EVP_DigestUpdate failed"));
|
||||||
@@ -301,7 +285,7 @@ int64_t pv_component_update_pld(const PvComponent *comp, EVP_MD_CTX *ctx,
|
|||||||
|
|
||||||
switch (comp->d_type) {
|
switch (comp->d_type) {
|
||||||
case DATA_BUFFER: {
|
case DATA_BUFFER: {
|
||||||
const Buffer *buf = comp->buf;
|
const PvBuffer *buf = comp->buf;
|
||||||
|
|
||||||
g_assert(buf->size <= INT64_MAX);
|
g_assert(buf->size <= INT64_MAX);
|
||||||
g_assert(buf->size == size);
|
g_assert(buf->size == size);
|
||||||
@@ -383,6 +367,7 @@ int64_t pv_component_update_tld(const PvComponent *comp, EVP_MD_CTX *ctx,
|
|||||||
g_set_error(err, PV_CRYPTO_ERROR,
|
g_set_error(err, PV_CRYPTO_ERROR,
|
||||||
PV_CRYPTO_ERROR_INTERNAL,
|
PV_CRYPTO_ERROR_INTERNAL,
|
||||||
_("BN_bin2bn failed"));
|
_("BN_bin2bn failed"));
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint64_t cur = 0; cur < size; cur += PAGE_SIZE) {
|
for (uint64_t cur = 0; cur < size; cur += PAGE_SIZE) {
|
||||||
@@ -395,6 +380,7 @@ int64_t pv_component_update_tld(const PvComponent *comp, EVP_MD_CTX *ctx,
|
|||||||
g_set_error(err, PV_CRYPTO_ERROR,
|
g_set_error(err, PV_CRYPTO_ERROR,
|
||||||
PV_CRYPTO_ERROR_INTERNAL,
|
PV_CRYPTO_ERROR_INTERNAL,
|
||||||
_("BN_bn2binpad failed"));
|
_("BN_bn2binpad failed"));
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (EVP_DigestUpdate(ctx, tmp, sizeof(tmp)) != 1) {
|
if (EVP_DigestUpdate(ctx, tmp, sizeof(tmp)) != 1) {
|
||||||
@@ -409,6 +395,7 @@ int64_t pv_component_update_tld(const PvComponent *comp, EVP_MD_CTX *ctx,
|
|||||||
g_set_error(err, PV_CRYPTO_ERROR,
|
g_set_error(err, PV_CRYPTO_ERROR,
|
||||||
PV_CRYPTO_ERROR_INTERNAL,
|
PV_CRYPTO_ERROR_INTERNAL,
|
||||||
_("BN_add_word failed"));
|
_("BN_add_word failed"));
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
nep++;
|
nep++;
|
||||||
@@ -425,7 +412,7 @@ gint pv_component_write(const PvComponent *component, FILE *f, GError **err)
|
|||||||
|
|
||||||
switch (component->d_type) {
|
switch (component->d_type) {
|
||||||
case DATA_BUFFER: {
|
case DATA_BUFFER: {
|
||||||
const Buffer *buf = component->buf;
|
const PvBuffer *buf = component->buf;
|
||||||
|
|
||||||
if (seek_and_write_buffer(f, buf, offset, err) < 0)
|
if (seek_and_write_buffer(f, buf, offset, err) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ typedef struct {
|
|||||||
gint d_type; /* PvComponentDataType */
|
gint d_type; /* PvComponentDataType */
|
||||||
union {
|
union {
|
||||||
struct comp_file *file;
|
struct comp_file *file;
|
||||||
Buffer *buf;
|
PvBuffer *buf;
|
||||||
void *data;
|
void *data;
|
||||||
};
|
};
|
||||||
uint64_t src_addr;
|
uint64_t src_addr;
|
||||||
@@ -51,7 +51,7 @@ typedef struct {
|
|||||||
|
|
||||||
PvComponent *pv_component_new_file(PvComponentType type, const gchar *path,
|
PvComponent *pv_component_new_file(PvComponentType type, const gchar *path,
|
||||||
GError **err);
|
GError **err);
|
||||||
PvComponent *pv_component_new_buf(PvComponentType type, const Buffer *buf,
|
PvComponent *pv_component_new_buf(PvComponentType type, const PvBuffer *buf,
|
||||||
GError **err);
|
GError **err);
|
||||||
void pv_component_free(PvComponent *component);
|
void pv_component_free(PvComponent *component);
|
||||||
gint pv_component_type(const PvComponent *component);
|
gint pv_component_type(const PvComponent *component);
|
||||||
|
|||||||
@@ -210,13 +210,13 @@ GSList *pv_img_comps_get_comps(const PvImgComps *comps)
|
|||||||
return comps->comps;
|
return comps->comps;
|
||||||
}
|
}
|
||||||
|
|
||||||
gint pv_img_comps_finalize(PvImgComps *comps, Buffer **pld_digest,
|
gint pv_img_comps_finalize(PvImgComps *comps, PvBuffer **pld_digest,
|
||||||
Buffer **ald_digest, Buffer **tld_digest,
|
PvBuffer **ald_digest, PvBuffer **tld_digest,
|
||||||
uint64_t *nep, GError **err)
|
uint64_t *nep, GError **err)
|
||||||
{
|
{
|
||||||
g_autoptr(Buffer) tmp_pld_digest = NULL;
|
g_autoptr(PvBuffer) tmp_pld_digest = NULL;
|
||||||
g_autoptr(Buffer) tmp_ald_digest = NULL;
|
g_autoptr(PvBuffer) tmp_ald_digest = NULL;
|
||||||
g_autoptr(Buffer) tmp_tld_digest = NULL;
|
g_autoptr(PvBuffer) tmp_tld_digest = NULL;
|
||||||
|
|
||||||
comps->finalized = TRUE;
|
comps->finalized = TRUE;
|
||||||
for (GSList *iterator = comps->comps; iterator; iterator = iterator->next) {
|
for (GSList *iterator = comps->comps; iterator; iterator = iterator->next) {
|
||||||
|
|||||||
@@ -32,8 +32,8 @@ gint pv_img_comps_add_component(PvImgComps *comps, PvComponent **comp,
|
|||||||
GError **err);
|
GError **err);
|
||||||
PvComponent *pv_img_comps_get_nth_comp(PvImgComps *comps, guint n);
|
PvComponent *pv_img_comps_get_nth_comp(PvImgComps *comps, guint n);
|
||||||
gint pv_img_comps_set_offset(PvImgComps *comps, gsize offset, GError **err);
|
gint pv_img_comps_set_offset(PvImgComps *comps, gsize offset, GError **err);
|
||||||
gint pv_img_comps_finalize(PvImgComps *comps, Buffer **pld_digest,
|
gint pv_img_comps_finalize(PvImgComps *comps, PvBuffer **pld_digest,
|
||||||
Buffer **ald_digest, Buffer **tld_digest,
|
PvBuffer **ald_digest, PvBuffer **tld_digest,
|
||||||
uint64_t *nep, GError **err);
|
uint64_t *nep, GError **err);
|
||||||
void pv_img_comps_free(PvImgComps *comps);
|
void pv_img_comps_free(PvImgComps *comps);
|
||||||
|
|
||||||
|
|||||||
+13
-13
@@ -41,7 +41,7 @@ uint32_t pv_hdr_size(const PvHdr *hdr)
|
|||||||
|
|
||||||
gboolean pv_hdr_uses_encryption(const PvHdr *hdr)
|
gboolean pv_hdr_uses_encryption(const PvHdr *hdr)
|
||||||
{
|
{
|
||||||
return !(GUINT64_FROM_BE(hdr->head.pcf) & PV_CFLAG_NO_DECRYPTION);
|
return !(GUINT64_FROM_BE(hdr->head.pcf) & PV_PCF_NO_DECRYPTION);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t pv_hdr_enc_size(const PvHdr *hdr)
|
uint64_t pv_hdr_enc_size(const PvHdr *hdr)
|
||||||
@@ -76,17 +76,17 @@ uint64_t pv_hdr_get_nks(const PvHdr *hdr)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* In-place modification of ``buf`` */
|
/* In-place modification of ``buf`` */
|
||||||
static gint pv_hdr_encrypt(const PvHdr *hdr, const PvImage *img, Buffer *buf,
|
static gint pv_hdr_encrypt(const PvHdr *hdr, const PvImage *img, PvBuffer *buf,
|
||||||
GError **err)
|
GError **err)
|
||||||
{
|
{
|
||||||
uint32_t hdr_len = pv_hdr_size(hdr);
|
uint32_t hdr_len = pv_hdr_size(hdr);
|
||||||
uint32_t aad_len = pv_hdr_aad_size(hdr);
|
uint32_t aad_len = pv_hdr_aad_size(hdr);
|
||||||
guint tag_len = pv_hdr_tag_size(hdr);
|
guint tag_len = pv_hdr_tag_size(hdr);
|
||||||
uint32_t enc_len = pv_hdr_enc_size_casted(hdr);
|
uint32_t enc_len = pv_hdr_enc_size_casted(hdr);
|
||||||
const Buffer aad_part = { .data = buf->data, .size = aad_len };
|
const PvBuffer aad_part = { .data = buf->data, .size = aad_len };
|
||||||
Buffer enc_part = { .data = (uint8_t *)buf->data + aad_len,
|
PvBuffer enc_part = { .data = (uint8_t *)buf->data + aad_len,
|
||||||
.size = enc_len };
|
.size = enc_len };
|
||||||
Buffer tag_part = { .data = (uint8_t *)buf->data + hdr_len - tag_len,
|
PvBuffer tag_part = { .data = (uint8_t *)buf->data + hdr_len - tag_len,
|
||||||
.size = tag_len };
|
.size = tag_len };
|
||||||
struct cipher_parms parms;
|
struct cipher_parms parms;
|
||||||
int64_t c_len;
|
int64_t c_len;
|
||||||
@@ -119,9 +119,9 @@ static gint pv_hdr_aad_init(PvHdr *hdr, const PvImage *img, GError **err)
|
|||||||
g_autofree union ecdh_pub_key *cust_pub_key = NULL;
|
g_autofree union ecdh_pub_key *cust_pub_key = NULL;
|
||||||
struct pv_hdr_key_slot *hdr_slot = hdr->slots;
|
struct pv_hdr_key_slot *hdr_slot = hdr->slots;
|
||||||
struct pv_hdr_head *head = &hdr->head;
|
struct pv_hdr_head *head = &hdr->head;
|
||||||
g_autoptr(Buffer) pld = NULL;
|
g_autoptr(PvBuffer) pld = NULL;
|
||||||
g_autoptr(Buffer) ald = NULL;
|
g_autoptr(PvBuffer) ald = NULL;
|
||||||
g_autoptr(Buffer) tld = NULL;
|
g_autoptr(PvBuffer) tld = NULL;
|
||||||
uint64_t nep = 0;
|
uint64_t nep = 0;
|
||||||
|
|
||||||
g_assert(sizeof(head->iv) == img->gcm_iv->size);
|
g_assert(sizeof(head->iv) == img->gcm_iv->size);
|
||||||
@@ -250,7 +250,7 @@ PvHdr *pv_hdr_new(const PvImage *img, GError **err)
|
|||||||
return g_steal_pointer(&ret);
|
return g_steal_pointer(&ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pv_hdr_memcpy(const PvHdr *hdr, const Buffer *dst)
|
static void pv_hdr_memcpy(const PvHdr *hdr, const PvBuffer *dst)
|
||||||
{
|
{
|
||||||
uint64_t nks = pv_hdr_get_nks(hdr);
|
uint64_t nks = pv_hdr_get_nks(hdr);
|
||||||
uint8_t *data;
|
uint8_t *data;
|
||||||
@@ -270,13 +270,13 @@ static void pv_hdr_memcpy(const PvHdr *hdr, const Buffer *dst)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Buffer *pv_hdr_serialize(const PvHdr *hdr, const PvImage *img,
|
PvBuffer *pv_hdr_serialize(const PvHdr *hdr, const PvImage *img,
|
||||||
enum PvCryptoMode mode, GError **err)
|
enum PvCryptoMode mode, GError **err)
|
||||||
{
|
{
|
||||||
uint32_t hdr_size = pv_hdr_size(hdr);
|
uint32_t hdr_size = pv_hdr_size(hdr);
|
||||||
g_autoptr(Buffer) ret = NULL;
|
g_autoptr(PvBuffer) ret = NULL;
|
||||||
|
|
||||||
ret = buffer_alloc(hdr_size);
|
ret = pv_buffer_alloc(hdr_size);
|
||||||
pv_hdr_memcpy(hdr, ret);
|
pv_hdr_memcpy(hdr, ret);
|
||||||
|
|
||||||
if (mode == PV_ENCRYPT) {
|
if (mode == PV_ENCRYPT) {
|
||||||
|
|||||||
@@ -23,8 +23,8 @@
|
|||||||
PvHdr *pv_hdr_new(const PvImage *img, GError **err);
|
PvHdr *pv_hdr_new(const PvImage *img, GError **err);
|
||||||
void pv_hdr_free(PvHdr *hdr);
|
void pv_hdr_free(PvHdr *hdr);
|
||||||
G_GNUC_UNUSED gboolean pv_hdr_uses_encryption(const PvHdr *hdr);
|
G_GNUC_UNUSED gboolean pv_hdr_uses_encryption(const PvHdr *hdr);
|
||||||
Buffer *pv_hdr_serialize(const PvHdr *hdr, const PvImage *img,
|
PvBuffer *pv_hdr_serialize(const PvHdr *hdr, const PvImage *img,
|
||||||
enum PvCryptoMode mode, GError **err);
|
enum PvCryptoMode mode, GError **err);
|
||||||
uint32_t pv_hdr_size(const PvHdr *hdr);
|
uint32_t pv_hdr_size(const PvHdr *hdr);
|
||||||
uint32_t pv_hdr_aad_size(const PvHdr *hdr);
|
uint32_t pv_hdr_aad_size(const PvHdr *hdr);
|
||||||
uint64_t pv_hdr_enc_size(const PvHdr *hdr);
|
uint64_t pv_hdr_enc_size(const PvHdr *hdr);
|
||||||
|
|||||||
@@ -56,12 +56,12 @@ static gint pv_img_prepare_component(const PvImage *img, PvComponent *comp,
|
|||||||
GError **err)
|
GError **err)
|
||||||
{
|
{
|
||||||
struct cipher_parms parms = { 0 };
|
struct cipher_parms parms = { 0 };
|
||||||
g_autoptr(Buffer) tweak = NULL;
|
g_autoptr(PvBuffer) tweak = NULL;
|
||||||
prepare_func func = NULL;
|
prepare_func func = NULL;
|
||||||
void *opaque = NULL;
|
void *opaque = NULL;
|
||||||
gint rc;
|
gint rc;
|
||||||
|
|
||||||
if (img->pcf & PV_CFLAG_NO_DECRYPTION) {
|
if (img->pcf & PV_PCF_NO_DECRYPTION) {
|
||||||
/* we only need to align the components */
|
/* we only need to align the components */
|
||||||
func = pv_component_align;
|
func = pv_component_align;
|
||||||
opaque = NULL;
|
opaque = NULL;
|
||||||
@@ -76,7 +76,7 @@ static gint pv_img_prepare_component(const PvImage *img, PvComponent *comp,
|
|||||||
EVP_CIPHER_iv_length(cipher));
|
EVP_CIPHER_iv_length(cipher));
|
||||||
g_assert(img->xts_key->size <= UINT_MAX);
|
g_assert(img->xts_key->size <= UINT_MAX);
|
||||||
|
|
||||||
tweak = buffer_alloc(sizeof(comp->tweak.data));
|
tweak = pv_buffer_alloc(sizeof(comp->tweak.data));
|
||||||
memcpy(tweak->data, comp->tweak.data, tweak->size);
|
memcpy(tweak->data, comp->tweak.data, tweak->size);
|
||||||
func = pv_component_align_and_encrypt;
|
func = pv_component_align_and_encrypt;
|
||||||
parms.cipher = cipher;
|
parms.cipher = cipher;
|
||||||
@@ -93,11 +93,11 @@ static gint pv_img_prepare_component(const PvImage *img, PvComponent *comp,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Buffer *pv_img_read_key(const gchar *path, guint key_size,
|
static PvBuffer *pv_img_read_key(const gchar *path, guint key_size,
|
||||||
GError **err)
|
GError **err)
|
||||||
{
|
{
|
||||||
g_autoptr(Buffer) tmp_ret = NULL;
|
g_autoptr(PvBuffer) tmp_ret = NULL;
|
||||||
Buffer *ret = NULL;
|
PvBuffer *ret = NULL;
|
||||||
gsize bytes_read;
|
gsize bytes_read;
|
||||||
FILE *f = NULL;
|
FILE *f = NULL;
|
||||||
gsize size;
|
gsize size;
|
||||||
@@ -116,7 +116,7 @@ static Buffer *pv_img_read_key(const gchar *path, guint key_size,
|
|||||||
if (!f)
|
if (!f)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
tmp_ret = buffer_alloc(size);
|
tmp_ret = pv_buffer_alloc(size);
|
||||||
if (file_read(f, tmp_ret->data, 1, tmp_ret->size, &bytes_read, err) < 0)
|
if (file_read(f, tmp_ret->data, 1, tmp_ret->size, &bytes_read, err) < 0)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
@@ -160,8 +160,8 @@ static HostKeyList *pv_img_get_host_keys(GSList *host_keys_with_path, gint nid,
|
|||||||
return g_steal_pointer(&ret);
|
return g_steal_pointer(&ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Buffer *pv_img_get_key(const EVP_CIPHER *cipher, const gchar *path,
|
static PvBuffer *pv_img_get_key(const EVP_CIPHER *cipher, const gchar *path,
|
||||||
GError **err)
|
GError **err)
|
||||||
{
|
{
|
||||||
gint key_len = EVP_CIPHER_key_length(cipher);
|
gint key_len = EVP_CIPHER_key_length(cipher);
|
||||||
|
|
||||||
@@ -173,8 +173,8 @@ static Buffer *pv_img_get_key(const EVP_CIPHER *cipher, const gchar *path,
|
|||||||
return generate_aes_key((guint)key_len, err);
|
return generate_aes_key((guint)key_len, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Buffer *pv_img_get_iv(const EVP_CIPHER *cipher, const gchar *path,
|
static PvBuffer *pv_img_get_iv(const EVP_CIPHER *cipher, const gchar *path,
|
||||||
GError **err)
|
GError **err)
|
||||||
{
|
{
|
||||||
gint iv_len = EVP_CIPHER_iv_length(cipher);
|
gint iv_len = EVP_CIPHER_iv_length(cipher);
|
||||||
|
|
||||||
@@ -229,7 +229,9 @@ static gint pv_img_set_psw_addr(PvImage *img, const gchar *psw_addr_s,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static gint pv_img_set_control_flags(PvImage *img, const gchar *pcf_s,
|
static gint pv_img_set_control_flags(PvImage *img, const gchar *pcf_s,
|
||||||
const gchar *scf_s, GError **err)
|
const gchar *scf_s,
|
||||||
|
PvTristate allow_dump,
|
||||||
|
PvTristate allow_pckmo, GError **err)
|
||||||
{
|
{
|
||||||
uint64_t flags;
|
uint64_t flags;
|
||||||
|
|
||||||
@@ -247,6 +249,16 @@ static gint pv_img_set_control_flags(PvImage *img, const gchar *pcf_s,
|
|||||||
img->scf = flags;
|
img->scf = flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (allow_dump == PV_TRUE)
|
||||||
|
img->pcf |= PV_PCF_ALLOW_DUMPING;
|
||||||
|
else if (allow_dump == PV_FALSE)
|
||||||
|
img->pcf &= ~PV_PCF_ALLOW_DUMPING;
|
||||||
|
|
||||||
|
if (allow_pckmo == PV_TRUE)
|
||||||
|
img->pcf |= PV_PCF_PCKM_ECC | PV_PCF_PCKMO_AES | PV_PCF_PCKMO_DEA_TDEA;
|
||||||
|
else if (allow_pckmo == PV_FALSE)
|
||||||
|
img->pcf &= ~(PV_PCF_PCKM_ECC | PV_PCF_PCKMO_AES | PV_PCF_PCKMO_DEA_TDEA);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -299,9 +311,10 @@ static gint pv_img_hostkey_verify(GSList *host_key_certs,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Load all untrusted certificates (e.g. IBM Z signing key and
|
/* Load all untrusted certificates (e.g. IBM Z signing key and
|
||||||
* DigiCert intermediate CA) that are required to establish a chain of
|
* intermediate CA) that are required to establish a chain of trust
|
||||||
* trust starting from the host-key document up to the root CA (if not
|
* starting from the host-key document up to the root CA (if not
|
||||||
* otherwise specified that's the DigiCert Assured ID Root CA).
|
* otherwise specified that can be one of the system wide installed
|
||||||
|
* root CAs, e.g. DigiCert).
|
||||||
*/
|
*/
|
||||||
untrusted_certs_with_path = load_certificates(untrusted_cert_paths, err);
|
untrusted_certs_with_path = load_certificates(untrusted_cert_paths, err);
|
||||||
if (!untrusted_certs_with_path)
|
if (!untrusted_certs_with_path)
|
||||||
@@ -336,9 +349,8 @@ static gint pv_img_hostkey_verify(GSList *host_key_certs,
|
|||||||
* For this we must check:
|
* For this we must check:
|
||||||
*
|
*
|
||||||
* 1. Can a chain of trust be established ending in a root CA
|
* 1. Can a chain of trust be established ending in a root CA
|
||||||
* 2. Is the correct root CA ued? It has either to be the
|
* 2. Is the correct root CA used? It has either to be a system CA
|
||||||
* 'DigiCert Assured ID Root CA' or the root CA specified via
|
* or the root CA specified via command line.
|
||||||
* command line.
|
|
||||||
*/
|
*/
|
||||||
for (gint i = 0; i < sk_X509_num(ibm_signing_certs); ++i) {
|
for (gint i = 0; i < sk_X509_num(ibm_signing_certs); ++i) {
|
||||||
X509 *ibm_signing_cert = sk_X509_value(ibm_signing_certs, i);
|
X509 *ibm_signing_cert = sk_X509_value(ibm_signing_certs, i);
|
||||||
@@ -359,17 +371,12 @@ static gint pv_img_hostkey_verify(GSList *host_key_certs,
|
|||||||
if (verify_cert(ibm_signing_cert, ctx, err) < 0)
|
if (verify_cert(ibm_signing_cert, ctx, err) < 0)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
/* Verify the build chain of trust chain. If the user passes a
|
/* If there is a chain of trust using either the provided root
|
||||||
* trusted root CA on the command line then the check for the
|
* CA on the command line or a system wide trusted root CA.
|
||||||
* Subject Key Identifier (SKID) is skipped, otherwise let's
|
|
||||||
* check if the SKID meets our expectation.
|
|
||||||
*/
|
*/
|
||||||
if (!root_ca_path &&
|
if (check_chain_parameters(X509_STORE_CTX_get0_chain(ctx),
|
||||||
check_chain_parameters(X509_STORE_CTX_get0_chain(ctx),
|
err) < 0)
|
||||||
get_digicert_assured_id_root_ca_skid(),
|
|
||||||
err) < 0) {
|
|
||||||
goto error;
|
goto error;
|
||||||
}
|
|
||||||
|
|
||||||
ibm_signing_crls = store_ctx_find_valid_crls(ctx, ibm_signing_cert, err);
|
ibm_signing_crls = store_ctx_find_valid_crls(ctx, ibm_signing_cert, err);
|
||||||
if (!ibm_signing_crls) {
|
if (!ibm_signing_crls) {
|
||||||
@@ -485,23 +492,23 @@ static void pv_hdr_key_slot_free(PvHdrKeySlot *slot)
|
|||||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(PvHdrKeySlot, pv_hdr_key_slot_free)
|
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(PvHdrKeySlot, pv_hdr_key_slot_free)
|
||||||
|
|
||||||
static PvHdrKeySlot *pv_hdr_key_slot_new(const EVP_CIPHER *gcm_cipher,
|
static PvHdrKeySlot *pv_hdr_key_slot_new(const EVP_CIPHER *gcm_cipher,
|
||||||
const Buffer *cust_root_key,
|
const PvBuffer *cust_root_key,
|
||||||
EVP_PKEY *cust_key, EVP_PKEY *host_key,
|
EVP_PKEY *cust_key, EVP_PKEY *host_key,
|
||||||
GError **err)
|
GError **err)
|
||||||
{
|
{
|
||||||
g_autoptr(PvHdrKeySlot) ret = g_new0(PvHdrKeySlot, 1);
|
g_autoptr(PvHdrKeySlot) ret = g_new0(PvHdrKeySlot, 1);
|
||||||
g_autofree union ecdh_pub_key *pub = NULL;
|
g_autofree union ecdh_pub_key *pub = NULL;
|
||||||
g_autoptr(Buffer) exchange_key = NULL;
|
g_autoptr(PvBuffer) exchange_key = NULL;
|
||||||
g_autoptr(Buffer) digest_key = NULL;
|
g_autoptr(PvBuffer) digest_key = NULL;
|
||||||
g_autoptr(Buffer) iv = NULL;
|
g_autoptr(PvBuffer) iv = NULL;
|
||||||
Buffer pub_buf;
|
PvBuffer pub_buf;
|
||||||
/* No AAD data is used */
|
/* No AAD data is used */
|
||||||
Buffer aad = { .data = NULL, .size = 0 };
|
PvBuffer aad = { .data = NULL, .size = 0 };
|
||||||
/* Set the output buffers for the encrypted data and the
|
/* Set the output buffers for the encrypted data and the
|
||||||
* generated GCM tag
|
* generated GCM tag
|
||||||
*/
|
*/
|
||||||
Buffer enc = { .data = ret->wrapped_key, .size = sizeof(ret->wrapped_key) };
|
PvBuffer enc = { .data = ret->wrapped_key, .size = sizeof(ret->wrapped_key) };
|
||||||
Buffer tag = { .data = ret->tag, .size = sizeof(ret->tag) };
|
PvBuffer tag = { .data = ret->tag, .size = sizeof(ret->tag) };
|
||||||
struct cipher_parms parms;
|
struct cipher_parms parms;
|
||||||
int64_t c_len = 0;
|
int64_t c_len = 0;
|
||||||
|
|
||||||
@@ -530,7 +537,7 @@ static PvHdrKeySlot *pv_hdr_key_slot_new(const EVP_CIPHER *gcm_cipher,
|
|||||||
g_assert(exchange_key->size == (guint)EVP_CIPHER_key_length(gcm_cipher));
|
g_assert(exchange_key->size == (guint)EVP_CIPHER_key_length(gcm_cipher));
|
||||||
|
|
||||||
/* create zero IV */
|
/* create zero IV */
|
||||||
iv = buffer_alloc((guint)EVP_CIPHER_iv_length(gcm_cipher));
|
iv = pv_buffer_alloc((guint)EVP_CIPHER_iv_length(gcm_cipher));
|
||||||
parms.iv_or_tweak = iv;
|
parms.iv_or_tweak = iv;
|
||||||
parms.key = exchange_key;
|
parms.key = exchange_key;
|
||||||
parms.cipher = gcm_cipher;
|
parms.cipher = gcm_cipher;
|
||||||
@@ -583,12 +590,13 @@ PvImage *pv_img_new(PvArgs *args, const gchar *stage3a_path, GError **err)
|
|||||||
g_warning(_("host-key document verification is disabled. Your workload is not secured."));
|
g_warning(_("host-key document verification is disabled. Your workload is not secured."));
|
||||||
|
|
||||||
if (args->root_ca_path)
|
if (args->root_ca_path)
|
||||||
g_warning(_("A different root CA than the default DigiCert root CA is selected. Ensure that this root CA is trusted."));
|
g_warning(_("The root CA is selected through the command line. Ensure that this root CA is trusted."));
|
||||||
|
|
||||||
ret->comps = pv_img_comps_new(EVP_sha512(), EVP_sha512(), EVP_sha512(), err);
|
ret->comps = pv_img_comps_new(EVP_sha512(), EVP_sha512(), EVP_sha512(), err);
|
||||||
if (!ret->comps)
|
if (!ret->comps)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
ret->pcf = PV_PCF_PCKMO_AES | PV_PCF_PCKMO_DEA_TDEA | PV_PCF_PCKM_ECC;
|
||||||
ret->cust_comm_cipher = EVP_aes_256_gcm();
|
ret->cust_comm_cipher = EVP_aes_256_gcm();
|
||||||
ret->gcm_cipher = EVP_aes_256_gcm();
|
ret->gcm_cipher = EVP_aes_256_gcm();
|
||||||
ret->initial_psw.addr = DEFAULT_INITIAL_PSW_ADDR;
|
ret->initial_psw.addr = DEFAULT_INITIAL_PSW_ADDR;
|
||||||
@@ -602,7 +610,9 @@ PvImage *pv_img_new(PvArgs *args, const gchar *stage3a_path, GError **err)
|
|||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
/* set the control flags: PCF and SCF */
|
/* set the control flags: PCF and SCF */
|
||||||
if (pv_img_set_control_flags(ret, args->pcf, args->scf, err) < 0)
|
if (pv_img_set_control_flags(ret, args->pcf, args->scf,
|
||||||
|
args->allow_dump, args->allow_pckmo,
|
||||||
|
err) < 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
/* read in the keys */
|
/* read in the keys */
|
||||||
@@ -637,13 +647,13 @@ void pv_img_free(PvImage *img)
|
|||||||
g_slist_free_full(img->key_slots, (GDestroyNotify)pv_hdr_key_slot_free);
|
g_slist_free_full(img->key_slots, (GDestroyNotify)pv_hdr_key_slot_free);
|
||||||
g_slist_free_full(img->host_pub_keys, (GDestroyNotify)EVP_PKEY_free);
|
g_slist_free_full(img->host_pub_keys, (GDestroyNotify)EVP_PKEY_free);
|
||||||
EVP_PKEY_free(img->cust_pub_priv_key);
|
EVP_PKEY_free(img->cust_pub_priv_key);
|
||||||
buffer_clear(&img->stage3a);
|
pv_buffer_clear(&img->stage3a);
|
||||||
pv_img_comps_free(img->comps);
|
pv_img_comps_free(img->comps);
|
||||||
g_free(img->tmp_dir);
|
g_free(img->tmp_dir);
|
||||||
buffer_free(img->xts_key);
|
pv_buffer_free(img->xts_key);
|
||||||
buffer_free(img->cust_root_key);
|
pv_buffer_free(img->cust_root_key);
|
||||||
buffer_free(img->gcm_iv);
|
pv_buffer_free(img->gcm_iv);
|
||||||
buffer_free(img->cust_comm_key);
|
pv_buffer_free(img->cust_comm_key);
|
||||||
g_free(img);
|
g_free(img);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -684,13 +694,13 @@ gint pv_img_add_component(PvImage *img, const PvArg *arg, GError **err)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
gint pv_img_calc_pld_ald_tld_nep(const PvImage *img, Buffer **pld, Buffer **ald,
|
gint pv_img_calc_pld_ald_tld_nep(const PvImage *img, PvBuffer **pld, PvBuffer **ald,
|
||||||
Buffer **tld, uint64_t *nep, GError **err)
|
PvBuffer **tld, uint64_t *nep, GError **err)
|
||||||
{
|
{
|
||||||
return pv_img_comps_finalize(img->comps, pld, ald, tld, nep, err);
|
return pv_img_comps_finalize(img->comps, pld, ald, tld, nep, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
static gint pv_img_build_stage3b(PvImage *img, Buffer *stage3b, GError **err)
|
static gint pv_img_build_stage3b(PvImage *img, PvBuffer *stage3b, GError **err)
|
||||||
{
|
{
|
||||||
g_autofree struct stage3b_args *args = NULL;
|
g_autofree struct stage3b_args *args = NULL;
|
||||||
|
|
||||||
@@ -708,7 +718,7 @@ static gint pv_img_build_stage3b(PvImage *img, Buffer *stage3b, GError **err)
|
|||||||
gint pv_img_add_stage3b_comp(PvImage *img, const gchar *path, GError **err)
|
gint pv_img_add_stage3b_comp(PvImage *img, const gchar *path, GError **err)
|
||||||
{
|
{
|
||||||
g_autoptr(PvComponent) comp = NULL;
|
g_autoptr(PvComponent) comp = NULL;
|
||||||
g_autoptr(Buffer) stage3b = NULL;
|
g_autoptr(PvBuffer) stage3b = NULL;
|
||||||
|
|
||||||
stage3b = stage3b_getblob(path, err);
|
stage3b = stage3b_getblob(path, err);
|
||||||
if (!stage3b)
|
if (!stage3b)
|
||||||
@@ -825,7 +835,7 @@ static gint get_stage3a_data_size(const PvImage *img, gsize *data_size,
|
|||||||
|
|
||||||
gint pv_img_load_and_set_stage3a(PvImage *img, const gchar *path, GError **err)
|
gint pv_img_load_and_set_stage3a(PvImage *img, const gchar *path, GError **err)
|
||||||
{
|
{
|
||||||
g_autoptr(Buffer) stage3a = NULL;
|
g_autoptr(PvBuffer) stage3a = NULL;
|
||||||
gsize bin_size, data_size = 0;
|
gsize bin_size, data_size = 0;
|
||||||
|
|
||||||
if (get_stage3a_data_size(img, &data_size, err) < 0)
|
if (get_stage3a_data_size(img, &data_size, err) < 0)
|
||||||
@@ -845,8 +855,8 @@ gint pv_img_load_and_set_stage3a(PvImage *img, const gchar *path, GError **err)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Creates the PV IPIB and sets the stage3a arguments */
|
/* Creates the PV IPIB and sets the stage3a arguments */
|
||||||
static gint pv_img_build_stage3a(Buffer *stage3a, gsize stage3a_bin_size,
|
static gint pv_img_build_stage3a(PvBuffer *stage3a, gsize stage3a_bin_size,
|
||||||
GSList *comps, const Buffer *hdr, GError **err)
|
GSList *comps, const PvBuffer *hdr, GError **err)
|
||||||
{
|
{
|
||||||
g_autofree struct ipl_parameter_block *ipib = NULL;
|
g_autofree struct ipl_parameter_block *ipib = NULL;
|
||||||
|
|
||||||
@@ -866,9 +876,9 @@ static gint pv_img_build_stage3a(Buffer *stage3a, gsize stage3a_bin_size,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Creates the actual PV header (serialized and AES-GCM encrypted) */
|
/* Creates the actual PV header (serialized and AES-GCM encrypted) */
|
||||||
static Buffer *pv_img_create_pv_hdr(PvImage *img, GError **err)
|
static PvBuffer *pv_img_create_pv_hdr(PvImage *img, GError **err)
|
||||||
{
|
{
|
||||||
g_autoptr(Buffer) hdr_buf = NULL;
|
g_autoptr(PvBuffer) hdr_buf = NULL;
|
||||||
g_autoptr(PvHdr) hdr = NULL;
|
g_autoptr(PvHdr) hdr = NULL;
|
||||||
|
|
||||||
hdr = pv_hdr_new(img, err);
|
hdr = pv_hdr_new(img, err);
|
||||||
@@ -887,7 +897,7 @@ static Buffer *pv_img_create_pv_hdr(PvImage *img, GError **err)
|
|||||||
*/
|
*/
|
||||||
gint pv_img_finalize(PvImage *pv, const gchar *stage3b_path, GError **err)
|
gint pv_img_finalize(PvImage *pv, const gchar *stage3b_path, GError **err)
|
||||||
{
|
{
|
||||||
g_autoptr(Buffer) hdr = NULL;
|
g_autoptr(PvBuffer) hdr = NULL;
|
||||||
|
|
||||||
/* load stage3b template into memory and add it to the list of
|
/* load stage3b template into memory and add it to the list of
|
||||||
* components. This must be done before calling
|
* components. This must be done before calling
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
gchar *tmp_dir; /* directory used for temporary files */
|
gchar *tmp_dir; /* directory used for temporary files */
|
||||||
Buffer *stage3a; /* stage3a containing IPIB and PV header */
|
PvBuffer *stage3a; /* stage3a containing IPIB and PV header */
|
||||||
gsize stage3a_bin_size; /* size of stage3a.bin */
|
gsize stage3a_bin_size; /* size of stage3a.bin */
|
||||||
struct psw_t stage3a_psw; /* (short) PSW that is written to
|
struct psw_t stage3a_psw; /* (short) PSW that is written to
|
||||||
* location 0 of the created image
|
* location 0 of the created image
|
||||||
@@ -35,15 +35,15 @@ typedef struct {
|
|||||||
GSList *host_pub_keys; /* public host keys */
|
GSList *host_pub_keys; /* public host keys */
|
||||||
gint nid; /* Elliptic Curve used for the key derivation */
|
gint nid; /* Elliptic Curve used for the key derivation */
|
||||||
/* keys and cipher used for the AES-GCM encryption */
|
/* keys and cipher used for the AES-GCM encryption */
|
||||||
Buffer *cust_root_key;
|
PvBuffer *cust_root_key;
|
||||||
Buffer *gcm_iv;
|
PvBuffer *gcm_iv;
|
||||||
const EVP_CIPHER *gcm_cipher;
|
const EVP_CIPHER *gcm_cipher;
|
||||||
/* Information for the IPIB and PV header */
|
/* Information for the IPIB and PV header */
|
||||||
uint64_t pcf;
|
uint64_t pcf;
|
||||||
uint64_t scf;
|
uint64_t scf;
|
||||||
Buffer *cust_comm_key;
|
PvBuffer *cust_comm_key;
|
||||||
const EVP_CIPHER *cust_comm_cipher;
|
const EVP_CIPHER *cust_comm_cipher;
|
||||||
Buffer *xts_key;
|
PvBuffer *xts_key;
|
||||||
const EVP_CIPHER *xts_cipher;
|
const EVP_CIPHER *xts_cipher;
|
||||||
GSList *key_slots;
|
GSList *key_slots;
|
||||||
GSList *optional_items;
|
GSList *optional_items;
|
||||||
@@ -54,8 +54,8 @@ PvImage *pv_img_new(PvArgs *args, const gchar *stage3a_path, GError **err);
|
|||||||
void pv_img_free(PvImage *img);
|
void pv_img_free(PvImage *img);
|
||||||
gint pv_img_add_component(PvImage *img, const PvArg *arg, GError **err);
|
gint pv_img_add_component(PvImage *img, const PvArg *arg, GError **err);
|
||||||
gint pv_img_finalize(PvImage *img, const gchar *stage3b_path, GError **err);
|
gint pv_img_finalize(PvImage *img, const gchar *stage3b_path, GError **err);
|
||||||
gint pv_img_calc_pld_ald_tld_nep(const PvImage *img, Buffer **pld, Buffer **ald,
|
gint pv_img_calc_pld_ald_tld_nep(const PvImage *img, PvBuffer **pld, PvBuffer **ald,
|
||||||
Buffer **tld, uint64_t *nep, GError **err);
|
PvBuffer **tld, uint64_t *nep, GError **err);
|
||||||
gint pv_img_load_and_set_stage3a(PvImage *img, const gchar *path, GError **err);
|
gint pv_img_load_and_set_stage3a(PvImage *img, const gchar *path, GError **err);
|
||||||
const PvComponent *pv_img_get_stage3b_comp(const PvImage *img, GError **err);
|
const PvComponent *pv_img_get_stage3b_comp(const PvImage *img, GError **err);
|
||||||
gint pv_img_add_stage3b_comp(PvImage *img, const gchar *path, GError **err);
|
gint pv_img_add_stage3b_comp(PvImage *img, const gchar *path, GError **err);
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ uint64_t pv_ipib_get_size(uint32_t num_comp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static gint pv_ipib_init(IplParameterBlock *ipib, GSList *comps,
|
static gint pv_ipib_init(IplParameterBlock *ipib, GSList *comps,
|
||||||
const Buffer *hdr)
|
const PvBuffer *hdr)
|
||||||
{
|
{
|
||||||
g_assert(sizeof(struct ipl_pl_hdr) <= UINT32_MAX);
|
g_assert(sizeof(struct ipl_pl_hdr) <= UINT32_MAX);
|
||||||
g_assert(sizeof(struct ipl_pb0_pv_comp) <= UINT32_MAX);
|
g_assert(sizeof(struct ipl_pb0_pv_comp) <= UINT32_MAX);
|
||||||
@@ -100,7 +100,7 @@ static gint pv_ipib_init(IplParameterBlock *ipib, GSList *comps,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
IplParameterBlock *pv_ipib_new(GSList *comps, const Buffer *hdr, GError **err)
|
IplParameterBlock *pv_ipib_new(GSList *comps, const PvBuffer *hdr, GError **err)
|
||||||
{
|
{
|
||||||
uint64_t ipib_size = pv_ipib_get_size(g_slist_length(comps));
|
uint64_t ipib_size = pv_ipib_get_size(g_slist_length(comps));
|
||||||
g_autoptr(IplParameterBlock) ret = NULL;
|
g_autoptr(IplParameterBlock) ret = NULL;
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
typedef struct ipl_parameter_block IplParameterBlock;
|
typedef struct ipl_parameter_block IplParameterBlock;
|
||||||
|
|
||||||
uint64_t pv_ipib_get_size(uint32_t num_comp);
|
uint64_t pv_ipib_get_size(uint32_t num_comp);
|
||||||
IplParameterBlock *pv_ipib_new(GSList *comps, const Buffer *hdr, GError **err);
|
IplParameterBlock *pv_ipib_new(GSList *comps, const PvBuffer *hdr, GError **err);
|
||||||
void pv_ipib_free(IplParameterBlock *ipib);
|
void pv_ipib_free(IplParameterBlock *ipib);
|
||||||
|
|
||||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(IplParameterBlock, pv_ipib_free)
|
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(IplParameterBlock, pv_ipib_free)
|
||||||
|
|||||||
@@ -24,12 +24,12 @@
|
|||||||
((struct stage3a_args *)((uint64_t)data_ptr + loader_size - \
|
((struct stage3a_args *)((uint64_t)data_ptr + loader_size - \
|
||||||
sizeof(struct stage3a_args)))
|
sizeof(struct stage3a_args)))
|
||||||
|
|
||||||
static Buffer *loader_getblob(const gchar *filename, gsize *loader_size,
|
static PvBuffer *loader_getblob(const gchar *filename, gsize *loader_size,
|
||||||
gsize args_size, gsize data_size,
|
gsize args_size, gsize data_size,
|
||||||
gboolean data_aligned, GError **err)
|
gboolean data_aligned, GError **err)
|
||||||
{
|
{
|
||||||
g_autoptr(GMappedFile) mapped_file = NULL;
|
g_autoptr(GMappedFile) mapped_file = NULL;
|
||||||
g_autoptr(Buffer) ret = NULL;
|
g_autoptr(PvBuffer) ret = NULL;
|
||||||
gsize size, tmp_loader_size;
|
gsize size, tmp_loader_size;
|
||||||
gchar *loader_data;
|
gchar *loader_data;
|
||||||
|
|
||||||
@@ -60,7 +60,7 @@ static Buffer *loader_getblob(const gchar *filename, gsize *loader_size,
|
|||||||
size = (data_aligned ? PAGE_ALIGN(tmp_loader_size) : tmp_loader_size) +
|
size = (data_aligned ? PAGE_ALIGN(tmp_loader_size) : tmp_loader_size) +
|
||||||
data_size;
|
data_size;
|
||||||
|
|
||||||
ret = buffer_alloc(size);
|
ret = pv_buffer_alloc(size);
|
||||||
|
|
||||||
/* copy the loader "template" */
|
/* copy the loader "template" */
|
||||||
memcpy(ret->data, loader_data, tmp_loader_size);
|
memcpy(ret->data, loader_data, tmp_loader_size);
|
||||||
@@ -71,8 +71,8 @@ static Buffer *loader_getblob(const gchar *filename, gsize *loader_size,
|
|||||||
return g_steal_pointer(&ret);
|
return g_steal_pointer(&ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
Buffer *stage3a_getblob(const gchar *filename, gsize *loader_size,
|
PvBuffer *stage3a_getblob(const gchar *filename, gsize *loader_size,
|
||||||
gsize data_size, GError **err)
|
gsize data_size, GError **err)
|
||||||
{
|
{
|
||||||
return loader_getblob(filename, loader_size,
|
return loader_getblob(filename, loader_size,
|
||||||
sizeof(struct stage3a_args), data_size, TRUE,
|
sizeof(struct stage3a_args), data_size, TRUE,
|
||||||
@@ -83,8 +83,8 @@ Buffer *stage3a_getblob(const gchar *filename, gsize *loader_size,
|
|||||||
/* Set the right offsets and sizes in the stage3a template + add
|
/* Set the right offsets and sizes in the stage3a template + add
|
||||||
* the IPIB block with the PV header
|
* the IPIB block with the PV header
|
||||||
*/
|
*/
|
||||||
static gint stage3a_set_data(Buffer *loader, gsize loader_size,
|
static gint stage3a_set_data(PvBuffer *loader, gsize loader_size,
|
||||||
const Buffer *hdr, struct ipl_parameter_block *ipib,
|
const PvBuffer *hdr, struct ipl_parameter_block *ipib,
|
||||||
GError **err)
|
GError **err)
|
||||||
{
|
{
|
||||||
uint32_t ipib_size = GUINT32_FROM_BE(ipib->hdr.len);
|
uint32_t ipib_size = GUINT32_FROM_BE(ipib->hdr.len);
|
||||||
@@ -126,15 +126,15 @@ static gint stage3a_set_data(Buffer *loader, gsize loader_size,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
gint build_stage3a(Buffer *loader, gsize loader_size, const Buffer *hdr,
|
gint build_stage3a(PvBuffer *loader, gsize loader_size, const PvBuffer *hdr,
|
||||||
struct ipl_parameter_block *ipib, GError **err)
|
struct ipl_parameter_block *ipib, GError **err)
|
||||||
{
|
{
|
||||||
return stage3a_set_data(loader, loader_size, hdr, ipib, err);
|
return stage3a_set_data(loader, loader_size, hdr, ipib, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
Buffer *stage3b_getblob(const gchar *filename, GError **err)
|
PvBuffer *stage3b_getblob(const gchar *filename, GError **err)
|
||||||
{
|
{
|
||||||
g_autoptr(Buffer) ret = NULL;
|
g_autoptr(PvBuffer) ret = NULL;
|
||||||
gsize rb_size;
|
gsize rb_size;
|
||||||
|
|
||||||
ret = loader_getblob(filename, &rb_size, sizeof(struct stage3b_args), 0,
|
ret = loader_getblob(filename, &rb_size, sizeof(struct stage3b_args), 0,
|
||||||
@@ -146,7 +146,7 @@ Buffer *stage3b_getblob(const gchar *filename, GError **err)
|
|||||||
return g_steal_pointer(&ret);
|
return g_steal_pointer(&ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
void build_stage3b(Buffer *stage3b, const struct stage3b_args *args)
|
void build_stage3b(PvBuffer *stage3b, const struct stage3b_args *args)
|
||||||
{
|
{
|
||||||
g_assert(stage3b->size > sizeof(*args));
|
g_assert(stage3b->size > sizeof(*args));
|
||||||
|
|
||||||
|
|||||||
@@ -19,12 +19,12 @@
|
|||||||
#include "boot/stage3b.h"
|
#include "boot/stage3b.h"
|
||||||
#include "utils/buffer.h"
|
#include "utils/buffer.h"
|
||||||
|
|
||||||
Buffer *stage3a_getblob(const gchar *filename, gsize *loader_size,
|
PvBuffer *stage3a_getblob(const gchar *filename, gsize *loader_size,
|
||||||
gsize data_size, GError **err);
|
gsize data_size, GError **err);
|
||||||
gint build_stage3a(Buffer *dc, gsize dc_size, const Buffer *hdr,
|
gint build_stage3a(PvBuffer *dc, gsize dc_size, const PvBuffer *hdr,
|
||||||
struct ipl_parameter_block *ipib, GError **err);
|
struct ipl_parameter_block *ipib, GError **err);
|
||||||
Buffer *stage3b_getblob(const gchar *filename, GError **err);
|
PvBuffer *stage3b_getblob(const gchar *filename, GError **err);
|
||||||
void build_stage3b(Buffer *stage3b, const struct stage3b_args *args);
|
void build_stage3b(PvBuffer *stage3b, const struct stage3b_args *args);
|
||||||
void memblob_init(struct memblob *arg, uint64_t src, uint64_t size);
|
void memblob_init(struct memblob *arg, uint64_t src, uint64_t size);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -17,18 +17,18 @@
|
|||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "file_utils.h"
|
#include "file_utils.h"
|
||||||
|
|
||||||
Buffer *buffer_alloc(gsize size)
|
PvBuffer *pv_buffer_alloc(gsize size)
|
||||||
{
|
{
|
||||||
Buffer *ret = g_new0(Buffer, 1);
|
PvBuffer *ret = g_new0(PvBuffer, 1);
|
||||||
|
|
||||||
ret->data = g_malloc0(size);
|
ret->data = g_malloc0(size);
|
||||||
ret->size = size;
|
ret->size = size;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
Buffer *buffer_dup(const Buffer *buf, gboolean page_aligned)
|
PvBuffer *pv_buffer_dup(const PvBuffer *buf, gboolean page_aligned)
|
||||||
{
|
{
|
||||||
Buffer *ret;
|
PvBuffer *ret;
|
||||||
gsize size;
|
gsize size;
|
||||||
|
|
||||||
if (!buf)
|
if (!buf)
|
||||||
@@ -38,19 +38,19 @@ Buffer *buffer_dup(const Buffer *buf, gboolean page_aligned)
|
|||||||
if (page_aligned)
|
if (page_aligned)
|
||||||
size = PAGE_ALIGN(size);
|
size = PAGE_ALIGN(size);
|
||||||
|
|
||||||
ret = buffer_alloc(size);
|
ret = pv_buffer_alloc(size);
|
||||||
|
|
||||||
/* content will be 0-right-padded */
|
/* content will be 0-right-padded */
|
||||||
memcpy(ret->data, buf->data, buf->size);
|
memcpy(ret->data, buf->data, buf->size);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
gint buffer_write(const Buffer *buf, FILE *file, GError **err)
|
gint pv_buffer_write(const PvBuffer *buf, FILE *file, GError **err)
|
||||||
{
|
{
|
||||||
return file_write(file, buf->data, buf->size, 1, NULL, err);
|
return file_write(file, buf->data, buf->size, 1, NULL, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
void buffer_free(Buffer *buf)
|
void pv_buffer_free(PvBuffer *buf)
|
||||||
{
|
{
|
||||||
if (!buf)
|
if (!buf)
|
||||||
return;
|
return;
|
||||||
@@ -59,11 +59,11 @@ void buffer_free(Buffer *buf)
|
|||||||
g_free(buf);
|
g_free(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
void buffer_clear(Buffer **buf)
|
void pv_buffer_clear(PvBuffer **buf)
|
||||||
{
|
{
|
||||||
if (!buf || !*buf)
|
if (!buf || !*buf)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
buffer_free(*buf);
|
pv_buffer_free(*buf);
|
||||||
*buf = NULL;
|
*buf = NULL;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,17 +15,17 @@
|
|||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
typedef struct Buffer {
|
typedef struct PvBuffer {
|
||||||
void *data;
|
void *data;
|
||||||
gsize size; /* in bytes */
|
gsize size; /* in bytes */
|
||||||
} Buffer;
|
} PvBuffer;
|
||||||
|
|
||||||
Buffer *buffer_alloc(gsize size);
|
PvBuffer *pv_buffer_alloc(gsize size);
|
||||||
void buffer_free(Buffer *buf);
|
void pv_buffer_free(PvBuffer *buf);
|
||||||
void buffer_clear(Buffer **buf);
|
void pv_buffer_clear(PvBuffer **buf);
|
||||||
gint buffer_write(const Buffer *buf, FILE *file, GError **err);
|
gint pv_buffer_write(const PvBuffer *buf, FILE *file, GError **err);
|
||||||
Buffer *buffer_dup(const Buffer *buf, gboolean page_aligned);
|
PvBuffer *pv_buffer_dup(const PvBuffer *buf, gboolean page_aligned);
|
||||||
|
|
||||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(Buffer, buffer_free)
|
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(PvBuffer, pv_buffer_free)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
|
|
||||||
#include "buffer.h"
|
#include "buffer.h"
|
||||||
#include "curl.h"
|
#include "curl.h"
|
||||||
|
#include "openssl_compat.h"
|
||||||
#include "crypto.h"
|
#include "crypto.h"
|
||||||
|
|
||||||
#define DEFINE_GSLIST_MAP(t2, t1) \
|
#define DEFINE_GSLIST_MAP(t2, t1) \
|
||||||
@@ -89,15 +90,15 @@ EVP_MD_CTX *digest_ctx_new(const EVP_MD *md, GError **err)
|
|||||||
return g_steal_pointer(&ctx);
|
return g_steal_pointer(&ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
Buffer *digest_ctx_finalize(EVP_MD_CTX *ctx, GError **err)
|
PvBuffer *digest_ctx_finalize(EVP_MD_CTX *ctx, GError **err)
|
||||||
{
|
{
|
||||||
gint md_size = EVP_MD_size(EVP_MD_CTX_md(ctx));
|
gint md_size = EVP_MD_size(EVP_MD_CTX_md(ctx));
|
||||||
g_autoptr(Buffer) ret = NULL;
|
g_autoptr(PvBuffer) ret = NULL;
|
||||||
guint digest_size;
|
guint digest_size;
|
||||||
|
|
||||||
g_assert(md_size > 0);
|
g_assert(md_size > 0);
|
||||||
|
|
||||||
ret = buffer_alloc((guint)md_size);
|
ret = pv_buffer_alloc((guint)md_size);
|
||||||
if (EVP_DigestFinal_ex(ctx, ret->data, &digest_size) != 1) {
|
if (EVP_DigestFinal_ex(ctx, ret->data, &digest_size) != 1) {
|
||||||
g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL,
|
g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL,
|
||||||
_("EVP_DigestFinal_ex failed"));
|
_("EVP_DigestFinal_ex failed"));
|
||||||
@@ -110,10 +111,10 @@ Buffer *digest_ctx_finalize(EVP_MD_CTX *ctx, GError **err)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Returns the digest of @buf using the hash algorithm @md */
|
/* Returns the digest of @buf using the hash algorithm @md */
|
||||||
static Buffer *digest_buffer(const EVP_MD *md, const Buffer *buf, GError **err)
|
static PvBuffer *digest_buffer(const EVP_MD *md, const PvBuffer *buf, GError **err)
|
||||||
{
|
{
|
||||||
g_autoptr(EVP_MD_CTX) md_ctx = NULL;
|
g_autoptr(EVP_MD_CTX) md_ctx = NULL;
|
||||||
g_autoptr(Buffer) ret = NULL;
|
g_autoptr(PvBuffer) ret = NULL;
|
||||||
g_assert(buf);
|
g_assert(buf);
|
||||||
|
|
||||||
md_ctx = digest_ctx_new(md, err);
|
md_ctx = digest_ctx_new(md, err);
|
||||||
@@ -134,9 +135,9 @@ static Buffer *digest_buffer(const EVP_MD *md, const Buffer *buf, GError **err)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Returns the SHA256 digest of @buf */
|
/* Returns the SHA256 digest of @buf */
|
||||||
Buffer *sha256_buffer(const Buffer *buf, GError **err)
|
PvBuffer *sha256_buffer(const PvBuffer *buf, GError **err)
|
||||||
{
|
{
|
||||||
g_autoptr(Buffer) ret = NULL;
|
g_autoptr(PvBuffer) ret = NULL;
|
||||||
|
|
||||||
ret = digest_buffer(EVP_sha256(), buf, err);
|
ret = digest_buffer(EVP_sha256(), buf, err);
|
||||||
if (!ret)
|
if (!ret)
|
||||||
@@ -207,10 +208,10 @@ union ecdh_pub_key *evp_pkey_to_ecdh_pub_key(EVP_PKEY *key, GError **err)
|
|||||||
return g_steal_pointer(&ret);
|
return g_steal_pointer(&ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Buffer *derive_key(EVP_PKEY *cust, EVP_PKEY *host, GError **err)
|
static PvBuffer *derive_key(EVP_PKEY *cust, EVP_PKEY *host, GError **err)
|
||||||
{
|
{
|
||||||
g_autoptr(EVP_PKEY_CTX) ctx = NULL;
|
g_autoptr(EVP_PKEY_CTX) ctx = NULL;
|
||||||
g_autoptr(Buffer) ret = NULL;
|
g_autoptr(PvBuffer) ret = NULL;
|
||||||
gsize key_size;
|
gsize key_size;
|
||||||
|
|
||||||
ctx = EVP_PKEY_CTX_new(cust, NULL);
|
ctx = EVP_PKEY_CTX_new(cust, NULL);
|
||||||
@@ -236,7 +237,7 @@ static Buffer *derive_key(EVP_PKEY *cust, EVP_PKEY *host, GError **err)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = buffer_alloc(key_size);
|
ret = pv_buffer_alloc(key_size);
|
||||||
if (EVP_PKEY_derive(ctx, ret->data, &key_size) != 1) {
|
if (EVP_PKEY_derive(ctx, ret->data, &key_size) != 1) {
|
||||||
g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_DERIVE,
|
g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_DERIVE,
|
||||||
_("Key derivation failed"));
|
_("Key derivation failed"));
|
||||||
@@ -247,11 +248,11 @@ static Buffer *derive_key(EVP_PKEY *cust, EVP_PKEY *host, GError **err)
|
|||||||
return g_steal_pointer(&ret);
|
return g_steal_pointer(&ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
Buffer *compute_exchange_key(EVP_PKEY *cust, EVP_PKEY *host, GError **err)
|
PvBuffer *compute_exchange_key(EVP_PKEY *cust, EVP_PKEY *host, GError **err)
|
||||||
{
|
{
|
||||||
g_autoptr(Buffer) raw = buffer_alloc(70);
|
g_autoptr(PvBuffer) raw = pv_buffer_alloc(70);
|
||||||
g_autoptr(Buffer) ret = NULL;
|
g_autoptr(PvBuffer) ret = NULL;
|
||||||
g_autoptr(Buffer) key = NULL;
|
g_autoptr(PvBuffer) key = NULL;
|
||||||
guchar *data;
|
guchar *data;
|
||||||
|
|
||||||
key = derive_key(cust, host, err);
|
key = derive_key(cust, host, err);
|
||||||
@@ -290,10 +291,10 @@ gint generate_tweak(union tweak *tweak, uint16_t i, GError **err)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Buffer *generate_rand_data(guint size, const gchar *err_msg,
|
static PvBuffer *generate_rand_data(guint size, const gchar *err_msg,
|
||||||
GError **err)
|
GError **err)
|
||||||
{
|
{
|
||||||
g_autoptr(Buffer) buf = buffer_alloc(size);
|
g_autoptr(PvBuffer) buf = pv_buffer_alloc(size);
|
||||||
|
|
||||||
g_assert(size <= INT_MAX);
|
g_assert(size <= INT_MAX);
|
||||||
|
|
||||||
@@ -307,14 +308,14 @@ static Buffer *generate_rand_data(guint size, const gchar *err_msg,
|
|||||||
return g_steal_pointer(&buf);
|
return g_steal_pointer(&buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
Buffer *generate_aes_iv(guint size, GError **err)
|
PvBuffer *generate_aes_iv(guint size, GError **err)
|
||||||
{
|
{
|
||||||
return generate_rand_data(size,
|
return generate_rand_data(size,
|
||||||
_("Generating a IV failed because the required amount of random data is not available"),
|
_("Generating a IV failed because the required amount of random data is not available"),
|
||||||
err);
|
err);
|
||||||
}
|
}
|
||||||
|
|
||||||
Buffer *generate_aes_key(guint size, GError **err)
|
PvBuffer *generate_aes_key(guint size, GError **err)
|
||||||
{
|
{
|
||||||
return generate_rand_data(size,
|
return generate_rand_data(size,
|
||||||
_("Generating a key failed because the required amount of random data is not available"),
|
_("Generating a key failed because the required amount of random data is not available"),
|
||||||
@@ -439,11 +440,15 @@ static int check_signature_algo_match(const EVP_PKEY *pkey, const X509 *subject,
|
|||||||
|
|
||||||
static X509_CRL *load_crl_from_bio(BIO *bio)
|
static X509_CRL *load_crl_from_bio(BIO *bio)
|
||||||
{
|
{
|
||||||
g_autoptr(X509_CRL) crl = PEM_read_bio_X509_CRL(bio, NULL, 0, NULL);
|
g_autoptr(X509_CRL) crl = PEM_read_bio_X509_CRL(bio, NULL, NULL, NULL);
|
||||||
|
gint rc;
|
||||||
|
|
||||||
if (crl)
|
if (crl)
|
||||||
return g_steal_pointer(&crl);
|
return g_steal_pointer(&crl);
|
||||||
ERR_clear_error();
|
ERR_clear_error();
|
||||||
BIO_reset(bio);
|
rc = BIO_reset(bio);
|
||||||
|
if (rc != 1 && !(rc == 0 && BIO_method_type(bio) == BIO_TYPE_FILE))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
/* maybe the CRL is stored in DER format */
|
/* maybe the CRL is stored in DER format */
|
||||||
crl = d2i_X509_CRL_bio(bio, NULL);
|
crl = d2i_X509_CRL_bio(bio, NULL);
|
||||||
@@ -514,6 +519,7 @@ X509 *load_cert_from_file(const char *path, GError **err)
|
|||||||
{
|
{
|
||||||
g_autoptr(BIO) bio = bio_read_from_file(path);
|
g_autoptr(BIO) bio = bio_read_from_file(path);
|
||||||
g_autoptr(X509) cert = NULL;
|
g_autoptr(X509) cert = NULL;
|
||||||
|
gint rc;
|
||||||
|
|
||||||
if (!bio) {
|
if (!bio) {
|
||||||
g_set_error(err, PV_CRYPTO_ERROR,
|
g_set_error(err, PV_CRYPTO_ERROR,
|
||||||
@@ -526,7 +532,12 @@ X509 *load_cert_from_file(const char *path, GError **err)
|
|||||||
if (cert)
|
if (cert)
|
||||||
return g_steal_pointer(&cert);
|
return g_steal_pointer(&cert);
|
||||||
ERR_clear_error();
|
ERR_clear_error();
|
||||||
BIO_reset(bio);
|
rc = BIO_reset(bio);
|
||||||
|
if (rc != 1 && !(rc == 0 && BIO_method_type(bio) == BIO_TYPE_FILE)) {
|
||||||
|
g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_READ_CERTIFICATE,
|
||||||
|
_("unable to load certificate: '%s'"), path);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/* maybe the certificate is stored in DER format */
|
/* maybe the certificate is stored in DER format */
|
||||||
cert = d2i_X509_bio(bio, NULL);
|
cert = d2i_X509_bio(bio, NULL);
|
||||||
@@ -1068,8 +1079,8 @@ int store_set_verify_param(X509_STORE *store, GError **err)
|
|||||||
g_abort();
|
g_abort();
|
||||||
|
|
||||||
/* The maximum depth level of the chain of trust for the verification of
|
/* The maximum depth level of the chain of trust for the verification of
|
||||||
* the IBM Z signing key is 2, i.e. IBM Z signing key -> (DigiCert)
|
* the IBM Z signing key is 2, i.e. IBM Z signing key -> intermediate CA
|
||||||
* intermediate CA -> (DigiCert) root CA
|
* -> root CA
|
||||||
*/
|
*/
|
||||||
X509_VERIFY_PARAM_set_depth(param, 2);
|
X509_VERIFY_PARAM_set_depth(param, 2);
|
||||||
|
|
||||||
@@ -1256,46 +1267,38 @@ static int security_level_to_bits(int level)
|
|||||||
return security_bits[level];
|
return security_bits[level];
|
||||||
}
|
}
|
||||||
|
|
||||||
static ASN1_OCTET_STRING *digicert_assured_id_root_ca;
|
|
||||||
|
|
||||||
const ASN1_OCTET_STRING *get_digicert_assured_id_root_ca_skid(void)
|
|
||||||
{
|
|
||||||
pv_crypto_init();
|
|
||||||
return digicert_assured_id_root_ca;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Used for the caching of the downloaded CRLs */
|
/* Used for the caching of the downloaded CRLs */
|
||||||
static GHashTable *cached_crls;
|
static GHashTable *cached_crls;
|
||||||
|
|
||||||
void pv_crypto_init(void)
|
void pv_crypto_init(void)
|
||||||
{
|
{
|
||||||
if (digicert_assured_id_root_ca)
|
if (cached_crls)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
cached_crls = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
|
cached_crls = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
|
||||||
(GDestroyNotify)X509_CRL_free);
|
(GDestroyNotify)X509_CRL_free);
|
||||||
digicert_assured_id_root_ca = s2i_ASN1_OCTET_STRING(
|
|
||||||
NULL, NULL, DIGICERT_ASSURED_ID_ROOT_CA_SKID);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void pv_crypto_cleanup(void)
|
void pv_crypto_cleanup(void)
|
||||||
{
|
{
|
||||||
if (!digicert_assured_id_root_ca)
|
if (!cached_crls)
|
||||||
return;
|
return;
|
||||||
g_clear_pointer(&cached_crls, g_hash_table_destroy);
|
g_clear_pointer(&cached_crls, g_hash_table_destroy);
|
||||||
g_clear_pointer(&digicert_assured_id_root_ca, ASN1_OCTET_STRING_free);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gint check_chain_parameters(const STACK_OF_X509 *chain,
|
gint check_chain_parameters(const STACK_OF_X509 *chain,
|
||||||
const ASN1_OCTET_STRING *skid, GError **err)
|
GError **err)
|
||||||
{
|
{
|
||||||
const ASN1_OCTET_STRING *ca_skid = NULL;
|
const X509_NAME *ca_x509_subject = NULL;
|
||||||
|
g_autofree gchar *ca_subject = NULL;
|
||||||
gint len = sk_X509_num(chain);
|
gint len = sk_X509_num(chain);
|
||||||
X509 *ca = NULL;
|
X509 *ca = NULL;
|
||||||
|
|
||||||
g_assert(skid);
|
|
||||||
/* at least one root and one leaf certificate must be defined */
|
/* at least one root and one leaf certificate must be defined */
|
||||||
g_assert(len >= 2);
|
if (len < 2) {
|
||||||
|
g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL,
|
||||||
|
_("there must be at least on root and one leaf certificate in the chain of trust"));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
/* get the root certificate of the chain of trust */
|
/* get the root certificate of the chain of trust */
|
||||||
ca = sk_X509_value(chain, len - 1);
|
ca = sk_X509_value(chain, len - 1);
|
||||||
@@ -1305,19 +1308,21 @@ gint check_chain_parameters(const STACK_OF_X509 *chain,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ca_skid = X509_get0_subject_key_id(ca);
|
ca_x509_subject = X509_get_subject_name(ca);
|
||||||
if (!ca_skid) {
|
if (!ca_x509_subject) {
|
||||||
g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_MALFORMED_ROOT_CA,
|
g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL,
|
||||||
_("malformed root certificate"));
|
_("subject of the root CA cannot be retrieved"));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ASN1_STRING_cmp(ca_skid, skid) != 0) {
|
ca_subject = X509_NAME_oneline(ca_x509_subject, NULL, 0);
|
||||||
g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_WRONG_CA_USED,
|
if (!ca_subject) {
|
||||||
_("expecting DigiCert root CA to be used"));
|
g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL,
|
||||||
|
_("subject name of the root CA cannot be retrieved"));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
g_info("Root CA used: '%s'", ca_subject);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1428,7 +1433,7 @@ static const char *get_first_dp_url(DIST_POINT *dp)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean insert_crl(X509_NAME *name, X509_CRL *crl)
|
static gboolean insert_crl(const X509_NAME *name, X509_CRL *crl)
|
||||||
{
|
{
|
||||||
g_autofree gchar *key = NULL;
|
g_autofree gchar *key = NULL;
|
||||||
|
|
||||||
@@ -1443,7 +1448,7 @@ static gboolean insert_crl(X509_NAME *name, X509_CRL *crl)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Caller is responsible for free'ing */
|
/* Caller is responsible for free'ing */
|
||||||
static X509_CRL *lookup_crl(X509_NAME *name)
|
static X509_CRL *lookup_crl(const X509_NAME *name)
|
||||||
{
|
{
|
||||||
g_autoptr(X509_CRL) crl = NULL;
|
g_autoptr(X509_CRL) crl = NULL;
|
||||||
g_autofree gchar *key = NULL;
|
g_autofree gchar *key = NULL;
|
||||||
@@ -1463,7 +1468,7 @@ static X509_CRL *lookup_crl(X509_NAME *name)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Returns empty stack if no CRL downloaded. */
|
/* Returns empty stack if no CRL downloaded. */
|
||||||
static STACK_OF_X509_CRL *crls_download_cb(X509_STORE_CTX *ctx, X509_NAME *nm)
|
static STACK_OF_X509_CRL *crls_download_cb(const X509_STORE_CTX *ctx, const X509_NAME *nm)
|
||||||
{
|
{
|
||||||
g_autoptr(STACK_OF_X509_CRL) crls = NULL;
|
g_autoptr(STACK_OF_X509_CRL) crls = NULL;
|
||||||
g_autoptr(X509_CRL) crl = NULL;
|
g_autoptr(X509_CRL) crl = NULL;
|
||||||
@@ -1473,7 +1478,7 @@ static STACK_OF_X509_CRL *crls_download_cb(X509_STORE_CTX *ctx, X509_NAME *nm)
|
|||||||
crls = sk_X509_CRL_new_null();
|
crls = sk_X509_CRL_new_null();
|
||||||
if (!crls)
|
if (!crls)
|
||||||
g_abort();
|
g_abort();
|
||||||
cert = X509_STORE_CTX_get_current_cert(ctx);
|
cert = Pv_X509_STORE_CTX_get_current_cert(ctx);
|
||||||
if (!cert)
|
if (!cert)
|
||||||
return g_steal_pointer(&crls);
|
return g_steal_pointer(&crls);
|
||||||
g_assert(X509_NAME_cmp(X509_get_issuer_name(cert), nm) == 0);
|
g_assert(X509_NAME_cmp(X509_get_issuer_name(cert), nm) == 0);
|
||||||
@@ -1517,19 +1522,19 @@ void STACK_OF_X509_CRL_free(STACK_OF_X509_CRL *stack)
|
|||||||
/* Downloaded CRLs have a higher precedence than the CRLs specified on the
|
/* Downloaded CRLs have a higher precedence than the CRLs specified on the
|
||||||
* command line.
|
* command line.
|
||||||
*/
|
*/
|
||||||
static STACK_OF_X509_CRL *crls_cb(X509_STORE_CTX *ctx, X509_NAME *nm)
|
static STACK_OF_X509_CRL *crls_cb(const X509_STORE_CTX *ctx, const X509_NAME *nm)
|
||||||
{
|
{
|
||||||
g_autoptr(STACK_OF_X509_CRL) crls = crls_download_cb(ctx, nm);
|
g_autoptr(STACK_OF_X509_CRL) crls = crls_download_cb(ctx, nm);
|
||||||
|
|
||||||
if (sk_X509_CRL_num(crls) > 0)
|
if (sk_X509_CRL_num(crls) > 0)
|
||||||
return g_steal_pointer(&crls);
|
return g_steal_pointer(&crls);
|
||||||
return X509_STORE_CTX_get1_crls(ctx, nm);
|
return Pv_X509_STORE_CTX_get1_crls(ctx, nm);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set up CRL lookup with download support */
|
/* Set up CRL lookup with download support */
|
||||||
void store_setup_crl_download(X509_STORE *st)
|
void store_setup_crl_download(X509_STORE *st)
|
||||||
{
|
{
|
||||||
X509_STORE_set_lookup_crls(st, crls_cb);
|
Pv_X509_STORE_set_lookup_crls(st, crls_cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Download a CRL using the URI specified in the distribution @crldp */
|
/* Download a CRL using the URI specified in the distribution @crldp */
|
||||||
@@ -1645,8 +1650,8 @@ gint verify_host_key(X509 *host_key, GSList *issuer_pairs,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!(verify_flags & X509_V_FLAG_NO_CHECK_TIME)) {
|
if (!(verify_flags & X509_V_FLAG_NO_CHECK_TIME)) {
|
||||||
const ASN1_TIME *last = X509_get_notBefore(host_key);
|
const ASN1_TIME *last = X509_get0_notBefore(host_key);
|
||||||
const ASN1_TIME *next = X509_get_notAfter(host_key);
|
const ASN1_TIME *next = X509_get0_notAfter(host_key);
|
||||||
|
|
||||||
if (!last || !next || check_validity_period(last, next)) {
|
if (!last || !next || check_validity_period(last, next)) {
|
||||||
g_set_error(err, PV_CRYPTO_ERROR,
|
g_set_error(err, PV_CRYPTO_ERROR,
|
||||||
@@ -1756,8 +1761,8 @@ static gint __encrypt_decrypt_bio(const struct cipher_parms *parms, BIO *b_in,
|
|||||||
gint cipher_block_size = EVP_CIPHER_block_size(cipher);
|
gint cipher_block_size = EVP_CIPHER_block_size(cipher);
|
||||||
guchar in_buf[PAGE_SIZE],
|
guchar in_buf[PAGE_SIZE],
|
||||||
out_buf[PAGE_SIZE + (guint)cipher_block_size];
|
out_buf[PAGE_SIZE + (guint)cipher_block_size];
|
||||||
const Buffer *key = parms->key;
|
const PvBuffer *key = parms->key;
|
||||||
const Buffer *tweak = parms->iv_or_tweak;
|
const PvBuffer *tweak = parms->iv_or_tweak;
|
||||||
g_autofree guchar *tmp_tweak = NULL;
|
g_autofree guchar *tmp_tweak = NULL;
|
||||||
gint out_len, tweak_size;
|
gint out_len, tweak_size;
|
||||||
gsize tmp_size_in = 0, tmp_size_out = 0;
|
gsize tmp_size_in = 0, tmp_size_out = 0;
|
||||||
@@ -1847,6 +1852,7 @@ static gint __encrypt_decrypt_bio(const struct cipher_parms *parms, BIO *b_in,
|
|||||||
g_set_error(err, PV_CRYPTO_ERROR,
|
g_set_error(err, PV_CRYPTO_ERROR,
|
||||||
PV_CRYPTO_ERROR_INTERNAL,
|
PV_CRYPTO_ERROR_INTERNAL,
|
||||||
_("BN_add_word failed"));
|
_("BN_add_word failed"));
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
g_assert(BN_num_bytes(tweak_num) > 0);
|
g_assert(BN_num_bytes(tweak_num) > 0);
|
||||||
g_assert(BN_num_bytes(tweak_num) <= tweak_size);
|
g_assert(BN_num_bytes(tweak_num) <= tweak_size);
|
||||||
@@ -1855,6 +1861,7 @@ static gint __encrypt_decrypt_bio(const struct cipher_parms *parms, BIO *b_in,
|
|||||||
g_set_error(err, PV_CRYPTO_ERROR,
|
g_set_error(err, PV_CRYPTO_ERROR,
|
||||||
PV_CRYPTO_ERROR_INTERNAL,
|
PV_CRYPTO_ERROR_INTERNAL,
|
||||||
_("BN_bn2binpad failed"));
|
_("BN_bn2binpad failed"));
|
||||||
|
return -1;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* set new tweak */
|
/* set new tweak */
|
||||||
@@ -1895,11 +1902,11 @@ static gint __encrypt_decrypt_bio(const struct cipher_parms *parms, BIO *b_in,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Buffer *__encrypt_decrypt_buffer(const struct cipher_parms *parms,
|
static PvBuffer *__encrypt_decrypt_buffer(const struct cipher_parms *parms,
|
||||||
const Buffer *in, gboolean encrypt,
|
const PvBuffer *in, gboolean encrypt,
|
||||||
GError **err)
|
GError **err)
|
||||||
{
|
{
|
||||||
g_autoptr(Buffer) ret = NULL;
|
g_autoptr(PvBuffer) ret = NULL;
|
||||||
g_autoptr(BIO) b_out = NULL;
|
g_autoptr(BIO) b_out = NULL;
|
||||||
g_autoptr(BIO) b_in = NULL;
|
g_autoptr(BIO) b_in = NULL;
|
||||||
gsize in_size, out_size;
|
gsize in_size, out_size;
|
||||||
@@ -1927,19 +1934,19 @@ static Buffer *__encrypt_decrypt_buffer(const struct cipher_parms *parms,
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = buffer_alloc((unsigned long)data_size);
|
ret = pv_buffer_alloc((unsigned long)data_size);
|
||||||
memcpy(ret->data, data, ret->size);
|
memcpy(ret->data, data, ret->size);
|
||||||
return g_steal_pointer(&ret);
|
return g_steal_pointer(&ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
Buffer *encrypt_buf(const struct cipher_parms *parms, const Buffer *in,
|
PvBuffer *encrypt_buf(const struct cipher_parms *parms, const PvBuffer *in,
|
||||||
GError **err)
|
GError **err)
|
||||||
{
|
{
|
||||||
return __encrypt_decrypt_buffer(parms, in, TRUE, err);
|
return __encrypt_decrypt_buffer(parms, in, TRUE, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
Buffer *decrypt_buf(const struct cipher_parms *parms, const Buffer *in,
|
PvBuffer *decrypt_buf(const struct cipher_parms *parms, const PvBuffer *in,
|
||||||
GError **err)
|
GError **err)
|
||||||
{
|
{
|
||||||
return __encrypt_decrypt_buffer(parms, in, FALSE, err);
|
return __encrypt_decrypt_buffer(parms, in, FALSE, err);
|
||||||
}
|
}
|
||||||
@@ -1993,16 +2000,16 @@ G_GNUC_UNUSED static gint decrypt_file(const struct cipher_parms *parms,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* GCM mode uses (zero-)padding */
|
/* GCM mode uses (zero-)padding */
|
||||||
static int64_t gcm_encrypt_decrypt(const Buffer *in, const Buffer *aad,
|
static int64_t gcm_encrypt_decrypt(const PvBuffer *in, const PvBuffer *aad,
|
||||||
const struct cipher_parms *parms,
|
const struct cipher_parms *parms,
|
||||||
Buffer *out, Buffer *tag,
|
PvBuffer *out, PvBuffer *tag,
|
||||||
enum PvCryptoMode mode, GError **err)
|
enum PvCryptoMode mode, GError **err)
|
||||||
{
|
{
|
||||||
g_autoptr(EVP_CIPHER_CTX) ctx = NULL;
|
g_autoptr(EVP_CIPHER_CTX) ctx = NULL;
|
||||||
const EVP_CIPHER *cipher = parms->cipher;
|
const EVP_CIPHER *cipher = parms->cipher;
|
||||||
const Buffer *iv = parms->iv_or_tweak;
|
const PvBuffer *iv = parms->iv_or_tweak;
|
||||||
gboolean encrypt = mode == PV_ENCRYPT;
|
gboolean encrypt = mode == PV_ENCRYPT;
|
||||||
const Buffer *key = parms->key;
|
const PvBuffer *key = parms->key;
|
||||||
int64_t ret = -1;
|
int64_t ret = -1;
|
||||||
gint len = -1;
|
gint len = -1;
|
||||||
|
|
||||||
@@ -2097,8 +2104,8 @@ static int64_t gcm_encrypt_decrypt(const Buffer *in, const Buffer *aad,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t gcm_encrypt(const Buffer *in, const Buffer *aad,
|
int64_t gcm_encrypt(const PvBuffer *in, const PvBuffer *aad,
|
||||||
const struct cipher_parms *parms, Buffer *out, Buffer *tag,
|
const struct cipher_parms *parms, PvBuffer *out, PvBuffer *tag,
|
||||||
GError **err)
|
GError **err)
|
||||||
{
|
{
|
||||||
return gcm_encrypt_decrypt(in, aad, parms, out, tag, PV_ENCRYPT, err);
|
return gcm_encrypt_decrypt(in, aad, parms, out, tag, PV_ENCRYPT, err);
|
||||||
|
|||||||
@@ -117,15 +117,14 @@ union tweak {
|
|||||||
|
|
||||||
struct cipher_parms {
|
struct cipher_parms {
|
||||||
const EVP_CIPHER *cipher;
|
const EVP_CIPHER *cipher;
|
||||||
const Buffer *key;
|
const PvBuffer *key;
|
||||||
const Buffer *iv_or_tweak;
|
const PvBuffer *iv_or_tweak;
|
||||||
};
|
};
|
||||||
|
|
||||||
int check_crl_valid_for_cert(X509_CRL *crl, X509 *cert,
|
int check_crl_valid_for_cert(X509_CRL *crl, X509 *cert,
|
||||||
gint verify_flags, GError **err);
|
gint verify_flags, GError **err);
|
||||||
void pv_crypto_init(void);
|
void pv_crypto_init(void);
|
||||||
void pv_crypto_cleanup(void);
|
void pv_crypto_cleanup(void);
|
||||||
const ASN1_OCTET_STRING *get_digicert_assured_id_root_ca_skid(void);
|
|
||||||
gint verify_host_key(X509 *host_key, GSList *issuer_pairs,
|
gint verify_host_key(X509 *host_key, GSList *issuer_pairs,
|
||||||
gint verify_flags, int level, GError **err);
|
gint verify_flags, int level, GError **err);
|
||||||
X509 *load_cert_from_file(const char *path, GError **err);
|
X509 *load_cert_from_file(const char *path, GError **err);
|
||||||
@@ -138,8 +137,7 @@ X509_STORE *store_setup(const gchar *root_ca_path,
|
|||||||
int store_set_verify_param(X509_STORE *store, GError **err);
|
int store_set_verify_param(X509_STORE *store, GError **err);
|
||||||
X509_CRL *load_crl_by_cert(X509 *cert, GError **err);
|
X509_CRL *load_crl_by_cert(X509 *cert, GError **err);
|
||||||
STACK_OF_X509_CRL *try_load_crls_by_certs(GSList *certs_with_path);
|
STACK_OF_X509_CRL *try_load_crls_by_certs(GSList *certs_with_path);
|
||||||
gint check_chain_parameters(const STACK_OF_X509 *chain,
|
gint check_chain_parameters(const STACK_OF_X509 *chain, GError **err);
|
||||||
const ASN1_OCTET_STRING *skid, GError **err);
|
|
||||||
X509_NAME *c2b_name(const X509_NAME *name);
|
X509_NAME *c2b_name(const X509_NAME *name);
|
||||||
|
|
||||||
STACK_OF_X509 *delete_ibm_signing_certs(STACK_OF_X509 *certs);
|
STACK_OF_X509 *delete_ibm_signing_certs(STACK_OF_X509 *certs);
|
||||||
@@ -152,24 +150,24 @@ X509_CRL *get_first_valid_crl(X509_STORE_CTX *ctx, X509 *cert, GError **err);
|
|||||||
void store_setup_crl_download(X509_STORE *st);
|
void store_setup_crl_download(X509_STORE *st);
|
||||||
EVP_PKEY *read_ec_pubkey_cert(X509 *cert, gint nid, GError **err);
|
EVP_PKEY *read_ec_pubkey_cert(X509 *cert, gint nid, GError **err);
|
||||||
|
|
||||||
Buffer *compute_exchange_key(EVP_PKEY *cust, EVP_PKEY *host, GError **err);
|
PvBuffer *compute_exchange_key(EVP_PKEY *cust, EVP_PKEY *host, GError **err);
|
||||||
Buffer *generate_aes_key(guint size, GError **err);
|
PvBuffer *generate_aes_key(guint size, GError **err);
|
||||||
Buffer *generate_aes_iv(guint size, GError **err);
|
PvBuffer *generate_aes_iv(guint size, GError **err);
|
||||||
EVP_PKEY *generate_ec_key(gint nid, GError **err);
|
EVP_PKEY *generate_ec_key(gint nid, GError **err);
|
||||||
gint generate_tweak(union tweak *tweak, uint16_t i, GError **err);
|
gint generate_tweak(union tweak *tweak, uint16_t i, GError **err);
|
||||||
union ecdh_pub_key *evp_pkey_to_ecdh_pub_key(EVP_PKEY *key, GError **err);
|
union ecdh_pub_key *evp_pkey_to_ecdh_pub_key(EVP_PKEY *key, GError **err);
|
||||||
EVP_MD_CTX *digest_ctx_new(const EVP_MD *md, GError **err);
|
EVP_MD_CTX *digest_ctx_new(const EVP_MD *md, GError **err);
|
||||||
Buffer *digest_ctx_finalize(EVP_MD_CTX *ctx, GError **err);
|
PvBuffer *digest_ctx_finalize(EVP_MD_CTX *ctx, GError **err);
|
||||||
Buffer *sha256_buffer(const Buffer *buf, GError **err);
|
PvBuffer *sha256_buffer(const PvBuffer *buf, GError **err);
|
||||||
int64_t gcm_encrypt(const Buffer *in, const Buffer *aad,
|
int64_t gcm_encrypt(const PvBuffer *in, const PvBuffer *aad,
|
||||||
const struct cipher_parms *parms, Buffer *out,
|
const struct cipher_parms *parms, PvBuffer *out,
|
||||||
Buffer *tag, GError **err);
|
PvBuffer *tag, GError **err);
|
||||||
gint encrypt_file(const struct cipher_parms *parms, const gchar *in_path,
|
gint encrypt_file(const struct cipher_parms *parms, const gchar *in_path,
|
||||||
const gchar *path_out, gsize *in_size, gsize *out_size,
|
const gchar *path_out, gsize *in_size, gsize *out_size,
|
||||||
GError **err);
|
GError **err);
|
||||||
Buffer *encrypt_buf(const struct cipher_parms *parms, const Buffer *in,
|
PvBuffer *encrypt_buf(const struct cipher_parms *parms, const PvBuffer *in,
|
||||||
GError **err);
|
GError **err);
|
||||||
G_GNUC_UNUSED Buffer *decrypt_buf(const struct cipher_parms *parms,
|
G_GNUC_UNUSED PvBuffer *decrypt_buf(const struct cipher_parms *parms,
|
||||||
const Buffer *in, GError **err);
|
const PvBuffer *in, GError **err);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -171,13 +171,13 @@ err:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
gint seek_and_write_buffer(FILE *o, const Buffer *buf, uint64_t offset,
|
gint seek_and_write_buffer(FILE *o, const PvBuffer *buf, uint64_t offset,
|
||||||
GError **err)
|
GError **err)
|
||||||
{
|
{
|
||||||
if (file_seek(o, offset, err) < 0)
|
if (file_seek(o, offset, err) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (buffer_write(buf, o, err) < 0)
|
if (pv_buffer_write(buf, o, err) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ gint file_write(FILE *out, const void *ptr, gsize size, gsize count,
|
|||||||
gsize *count_written, GError **err);
|
gsize *count_written, GError **err);
|
||||||
gint pad_file_right(const gchar *path_out, const gchar *path_in,
|
gint pad_file_right(const gchar *path_out, const gchar *path_in,
|
||||||
gsize *size_out, guint padding, GError **err);
|
gsize *size_out, guint padding, GError **err);
|
||||||
gint seek_and_write_buffer(FILE *out, const Buffer *buf, uint64_t offset,
|
gint seek_and_write_buffer(FILE *out, const PvBuffer *buf, uint64_t offset,
|
||||||
GError **err);
|
GError **err);
|
||||||
gint seek_and_write_file(FILE *o, const CompFile *ifile, uint64_t offset,
|
gint seek_and_write_file(FILE *o, const CompFile *ifile, uint64_t offset,
|
||||||
GError **err);
|
GError **err);
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* OpenSSL compatibility utils
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp. 2021
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PV_UTILS_OPENSSL_COMPAT_H
|
||||||
|
#define PV_UTILS_OPENSSL_COMPAT_H
|
||||||
|
|
||||||
|
#include <openssl/opensslv.h>
|
||||||
|
#include <openssl/x509.h>
|
||||||
|
#include <openssl/x509_vfy.h>
|
||||||
|
|
||||||
|
#if OPENSSL_VERSION_NUMBER < 0x30000000L
|
||||||
|
#define Pv_X509_STORE_CTX_get_current_cert(ctx) \
|
||||||
|
X509_STORE_CTX_get_current_cert((X509_STORE_CTX *)(ctx))
|
||||||
|
#define Pv_X509_STORE_CTX_get1_crls(ctx, nm) \
|
||||||
|
X509_STORE_CTX_get1_crls((X509_STORE_CTX *)(ctx), (X509_NAME *)(nm))
|
||||||
|
#define Pv_X509_STORE_set_lookup_crls(st, cb) \
|
||||||
|
X509_STORE_set_lookup_crls(st, (X509_STORE_CTX_lookup_crls_fn)(cb))
|
||||||
|
#else
|
||||||
|
#define Pv_X509_STORE_CTX_get_current_cert(ctx) \
|
||||||
|
X509_STORE_CTX_get_current_cert(ctx)
|
||||||
|
#define Pv_X509_STORE_CTX_get1_crls(ctx, nm) \
|
||||||
|
X509_STORE_CTX_get1_crls(ctx, nm)
|
||||||
|
#define Pv_X509_STORE_set_lookup_crls(st, cb) \
|
||||||
|
X509_STORE_set_lookup_crls(st, cb)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
+6
-6
@@ -16,17 +16,17 @@ check_dep:
|
|||||||
$(call check_dep, \
|
$(call check_dep, \
|
||||||
"hmcdrvfs", \
|
"hmcdrvfs", \
|
||||||
"fuse.h", \
|
"fuse.h", \
|
||||||
"fuse-devel or libfuse-dev", \
|
"fuse3-devel or libfuse3-dev", \
|
||||||
"HAVE_FUSE=0")
|
"HAVE_FUSE=0")
|
||||||
|
|
||||||
ifneq ($(shell sh -c 'command -v pkg-config'),)
|
ifneq ($(shell sh -c 'command -v pkg-config'),)
|
||||||
FUSE_CFLAGS = $(shell pkg-config --silence-errors --cflags fuse)
|
FUSE_CFLAGS = $(shell pkg-config --silence-errors --cflags fuse3)
|
||||||
FUSE_LDLIBS = $(shell pkg-config --silence-errors --libs fuse)
|
FUSE_LDLIBS = $(shell pkg-config --silence-errors --libs fuse3)
|
||||||
else
|
else
|
||||||
FUSE_CFLAGS = -D_FILE_OFFSET_BITS=64 -I/usr/include/fuse
|
FUSE_CFLAGS = -D_FILE_OFFSET_BITS=64 -I/usr/include/fuse3
|
||||||
FUSE_LDLIBS = -lfuse
|
FUSE_LDLIBS = -lfuse3
|
||||||
endif
|
endif
|
||||||
ALL_CFLAGS += -DFUSE_USE_VERSION=26 -D_LARGEFILE_SOURCE $(FUSE_CFLAGS)
|
ALL_CFLAGS += -DFUSE_USE_VERSION=30 -D_LARGEFILE_SOURCE $(FUSE_CFLAGS)
|
||||||
LDLIBS += $(FUSE_LDLIBS) -lpthread -lrt -ldl -lm
|
LDLIBS += $(FUSE_LDLIBS) -lpthread -lrt -ldl -lm
|
||||||
|
|
||||||
OBJECTS = hmcdrvfs.o
|
OBJECTS = hmcdrvfs.o
|
||||||
|
|||||||
@@ -111,9 +111,6 @@ allow access by other users
|
|||||||
.B -o allow_root
|
.B -o allow_root
|
||||||
allow access by root
|
allow access by root
|
||||||
.TP
|
.TP
|
||||||
.B -o nonempty
|
|
||||||
allow mounts over non-empty file/dir
|
|
||||||
.TP
|
|
||||||
.B -o default_permissions
|
.B -o default_permissions
|
||||||
enable permission checking by kernel
|
enable permission checking by kernel
|
||||||
.TP
|
.TP
|
||||||
|
|||||||
+9
-6
@@ -990,7 +990,7 @@ static int hmcdrv_cache_dir(const char *dir, fuse_fill_dir_t filler, void *buf)
|
|||||||
hmcdrv_cache_refresh(path, &st, symlink);
|
hmcdrv_cache_refresh(path, &st, symlink);
|
||||||
|
|
||||||
if ((filler != NULL) &&
|
if ((filler != NULL) &&
|
||||||
(filler(buf, fname, &st, 0) != 0))
|
(filler(buf, fname, &st, 0, 0) != 0))
|
||||||
filler = NULL; /* stop filling */
|
filler = NULL; /* stop filling */
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
strftime(symlink, sizeof(symlink),
|
strftime(symlink, sizeof(symlink),
|
||||||
@@ -1109,7 +1109,8 @@ static struct hmcdrv_fuse_file *hmcdrv_file_get(const char *path)
|
|||||||
*
|
*
|
||||||
* Note: The most important function which FUSE calls (very often).
|
* Note: The most important function which FUSE calls (very often).
|
||||||
*/
|
*/
|
||||||
static int hmcdrv_fuse_getattr(const char *path, struct stat *stbuf)
|
static int hmcdrv_fuse_getattr(const char *path, struct stat *stbuf,
|
||||||
|
struct fuse_file_info *UNUSED(fi))
|
||||||
{
|
{
|
||||||
struct hmcdrv_fuse_file *fp;
|
struct hmcdrv_fuse_file *fp;
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
@@ -1175,12 +1176,13 @@ static int hmcdrv_fuse_opendir(const char *UNUSED(path),
|
|||||||
*/
|
*/
|
||||||
static int hmcdrv_fuse_readdir(const char *path, void *buf,
|
static int hmcdrv_fuse_readdir(const char *path, void *buf,
|
||||||
fuse_fill_dir_t filler, off_t UNUSED(offset),
|
fuse_fill_dir_t filler, off_t UNUSED(offset),
|
||||||
struct fuse_file_info *UNUSED(fi))
|
struct fuse_file_info *UNUSED(fi),
|
||||||
|
enum fuse_readdir_flags UNUSED(flags))
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
filler(buf, ".", NULL, 0);
|
filler(buf, ".", NULL, 0, 0);
|
||||||
filler(buf, "..", NULL, 0);
|
filler(buf, "..", NULL, 0, 0);
|
||||||
|
|
||||||
pthread_mutex_lock(&hmcdrv_ctx.mutex);
|
pthread_mutex_lock(&hmcdrv_ctx.mutex);
|
||||||
ret = hmcdrv_cache_dir(path, filler, buf);
|
ret = hmcdrv_cache_dir(path, filler, buf);
|
||||||
@@ -1232,7 +1234,8 @@ static int hmcdrv_fuse_read(const char *path, char *buf, size_t size,
|
|||||||
* Return: value to be passed in the private_data field of fuse_context to
|
* Return: value to be passed in the private_data field of fuse_context to
|
||||||
* all file operations and as a parameter to the destroy() method
|
* all file operations and as a parameter to the destroy() method
|
||||||
*/
|
*/
|
||||||
static void *hmcdrv_fuse_init(struct fuse_conn_info *UNUSED(conn))
|
static void *hmcdrv_fuse_init(struct fuse_conn_info *UNUSED(conn),
|
||||||
|
struct fuse_config *UNUSED(cfg))
|
||||||
{
|
{
|
||||||
pthread_mutexattr_t attr;
|
pthread_mutexattr_t attr;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
#
|
||||||
|
# Copyright IBM Corp. 2021
|
||||||
|
#
|
||||||
|
# s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
#
|
||||||
|
|
||||||
|
include ../common.mak
|
||||||
|
|
||||||
|
ALL_CPPFLAGS += -D_FILE_OFFSET_BITS=64
|
||||||
|
|
||||||
|
ifeq (${HAVE_FUSE},0)
|
||||||
|
|
||||||
|
all:
|
||||||
|
$(SKIP) HAVE_FUSE=0
|
||||||
|
|
||||||
|
install:
|
||||||
|
$(SKIP) HAVE_FUSE=0
|
||||||
|
|
||||||
|
else # HAVE_FUSE
|
||||||
|
|
||||||
|
#
|
||||||
|
# FUSE
|
||||||
|
#
|
||||||
|
ifneq ($(shell sh -c 'command -v pkg-config'),)
|
||||||
|
FUSE_CFLAGS = $(shell pkg-config --silence-errors --cflags fuse3)
|
||||||
|
FUSE_LDLIBS = $(shell pkg-config --silence-errors --libs fuse3)
|
||||||
|
else
|
||||||
|
FUSE_CFLAGS = -I/usr/include/fuse3
|
||||||
|
FUSE_LDLIBS = -lfuse3
|
||||||
|
endif
|
||||||
|
|
||||||
|
#
|
||||||
|
# systemd
|
||||||
|
#
|
||||||
|
ifneq (${HAVE_SYSTEMD},0)
|
||||||
|
ifeq ($(call check_header_prereq,"systemd/sd-daemon.h"),yes)
|
||||||
|
ifneq ($(shell sh -c 'command -v pkg-config'),)
|
||||||
|
SYSTEMD_CFLAGS = $(shell pkg-config --silence-errors --cflags libsystemd)
|
||||||
|
SYSTEMD_LDLIBS = $(shell pkg-config --silence-errors --libs libsystemd)
|
||||||
|
else
|
||||||
|
SYSTEMD_CFLAGS =
|
||||||
|
SYSTEMD_LDLIBS = -lsystemd
|
||||||
|
endif
|
||||||
|
ALL_CPPFLAGS += -DHAVE_SYSTEMD
|
||||||
|
else
|
||||||
|
$(warning "systemd support disabled")
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
ALL_CFLAGS += $(FUSE_CFLAGS) $(SYSTEMD_CFLAGS)
|
||||||
|
LDLIBS += $(FUSE_LDLIBS) $(SYSTEMD_LDLIBS) -lpthread
|
||||||
|
|
||||||
|
sources := $(wildcard *.c)
|
||||||
|
objects := $(patsubst %.c,%.o,$(sources))
|
||||||
|
|
||||||
|
libs = $(rootdir)/libutil/libutil.a
|
||||||
|
|
||||||
|
all: hsavmcore
|
||||||
|
|
||||||
|
hsavmcore: $(objects) $(libs)
|
||||||
|
$(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@
|
||||||
|
|
||||||
|
overlay.o: check-dep-fuse overlay.c overlay.h
|
||||||
|
|
||||||
|
check-dep-fuse:
|
||||||
|
$(call check_dep, \
|
||||||
|
"hsavmcore", \
|
||||||
|
"fuse.h", \
|
||||||
|
"fuse3-devel or libfuse3-dev", \
|
||||||
|
"HAVE_FUSE=0", \
|
||||||
|
"-DFUSE_USE_VERSION=30")
|
||||||
|
touch check-dep-fuse
|
||||||
|
|
||||||
|
install: all
|
||||||
|
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 hsavmcore \
|
||||||
|
$(DESTDIR)$(USRSBINDIR)
|
||||||
|
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 man/hsavmcore.8 \
|
||||||
|
$(DESTDIR)$(MANDIR)/man8
|
||||||
|
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 man/hsavmcore.conf.5 \
|
||||||
|
$(DESTDIR)$(MANDIR)/man5
|
||||||
|
|
||||||
|
endif # HAVE_FUSE
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f hsavmcore $(objects) check-dep-fuse
|
||||||
|
|
||||||
|
.PHONY: all install clean
|
||||||
@@ -0,0 +1,219 @@
|
|||||||
|
/*
|
||||||
|
* Copyright IBM Corp. 2021
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <limits.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "lib/zt_common.h"
|
||||||
|
#include "lib/util_opt.h"
|
||||||
|
#include "lib/util_prg.h"
|
||||||
|
#include "lib/util_log.h"
|
||||||
|
|
||||||
|
#include "cmdline_options.h"
|
||||||
|
|
||||||
|
static const struct util_prg prg = {
|
||||||
|
.desc = "hsavmcore is designed to make the dump process with kdump more "
|
||||||
|
"efficient. The HSA memory contains a part of the production "
|
||||||
|
"kernel's memory. Use hsavmcore to cache this information and "
|
||||||
|
"release HSA memory early in the process.",
|
||||||
|
.copyright_vec = {
|
||||||
|
{
|
||||||
|
.owner = "IBM Corp.",
|
||||||
|
.pub_first = 2021,
|
||||||
|
.pub_last = 2021,
|
||||||
|
},
|
||||||
|
UTIL_PRG_COPYRIGHT_END
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct util_opt opt_vec[] = {
|
||||||
|
UTIL_OPT_SECTION("CONFIGURATION"),
|
||||||
|
{
|
||||||
|
.option = { "config", required_argument, NULL, 'c' },
|
||||||
|
.argument = "CONFIGFILE",
|
||||||
|
.desc = "Path to the configuration file.\n"
|
||||||
|
"Default: no configuration file is used",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "vmcore", required_argument, NULL, 'C' },
|
||||||
|
.argument = "VMCOREFILE",
|
||||||
|
.desc = "Path to the vmcore file.\n"
|
||||||
|
"Default: " PROC_VMCORE,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "hsa", required_argument, NULL, 'H' },
|
||||||
|
.argument = "ZCOREHSAFILE",
|
||||||
|
.desc = "Path to the zcore HSA file.\n"
|
||||||
|
"Default: " ZCORE_HSA,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "workdir", required_argument, NULL, 'W' },
|
||||||
|
.argument = "WORKDIR",
|
||||||
|
.desc = "Path to the work directory where temporary files can be "
|
||||||
|
"stored.\nDefault: " WORKDIR,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "bmvmcore", required_argument, NULL, 'B' },
|
||||||
|
.argument = "VMCOREFILE",
|
||||||
|
.desc = "Path to the target of the bind mount for the vmcore "
|
||||||
|
"replacement.\nDefault: " PROC_VMCORE,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "swap", required_argument, NULL, 'S' },
|
||||||
|
.argument = "PATH",
|
||||||
|
.desc = "Path to a swap device or file. The specified swap "
|
||||||
|
"device or file must exist and have the proper swap "
|
||||||
|
"format.\nDefault: no swap device or file is activated",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "hsasize", required_argument, NULL, 'T' },
|
||||||
|
.argument = "HSASIZE",
|
||||||
|
.desc = "HSA size in bytes.\n"
|
||||||
|
"Default: -1 (read from the zcore HSA file)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "dbgfsmnt", no_argument, NULL, 'D' },
|
||||||
|
.desc = "Mount the debug file system.\n"
|
||||||
|
"Default: the debug file system is not mounted",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "hsamem", no_argument, NULL, 'F' },
|
||||||
|
.desc = "Cache the HSA memory in regular memory.\n"
|
||||||
|
"Default: the HSA memory is cached as a file within "
|
||||||
|
"WORKDIR",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "norelhsa", no_argument, NULL, 'R' },
|
||||||
|
.desc = "Do NOT release the HSA memory after caching.\n"
|
||||||
|
"Default: the HSA memory is released",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "nobindmnt", no_argument, NULL, 'N' },
|
||||||
|
.desc = "Do NOT replace the system's vmcore.\n"
|
||||||
|
"Default: the system's vmcore is replaced",
|
||||||
|
},
|
||||||
|
UTIL_OPT_SECTION("LOGGING"),
|
||||||
|
{
|
||||||
|
.option = { "verbose", no_argument, NULL, 'V' },
|
||||||
|
.desc = "Print verbose messages to stdout. Repeat this option "
|
||||||
|
"for increased verbosity from just error messages to "
|
||||||
|
"also include warning, information, debug, and trace "
|
||||||
|
"messages. This option is intended for debugging",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "fusedbg", no_argument, NULL, 'G' },
|
||||||
|
.desc = "Enable FUSE debugging.\n"
|
||||||
|
"Default: FUSE debugging is disabled",
|
||||||
|
},
|
||||||
|
UTIL_OPT_SECTION("GENERAL OPTIONS"),
|
||||||
|
UTIL_OPT_HELP,
|
||||||
|
UTIL_OPT_VERSION,
|
||||||
|
UTIL_OPT_END
|
||||||
|
};
|
||||||
|
|
||||||
|
void parse_cmdline_options(int argc, char *argv[], struct config *config)
|
||||||
|
{
|
||||||
|
int opt, ret;
|
||||||
|
|
||||||
|
util_prg_init(&prg);
|
||||||
|
util_opt_init(opt_vec, NULL);
|
||||||
|
|
||||||
|
/* Parse given command-line config */
|
||||||
|
while (1) {
|
||||||
|
opt = util_opt_getopt_long(argc, argv);
|
||||||
|
if (opt == -1)
|
||||||
|
break;
|
||||||
|
|
||||||
|
switch (opt) {
|
||||||
|
case 'h':
|
||||||
|
util_prg_print_help();
|
||||||
|
util_opt_print_help();
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
case 'v':
|
||||||
|
util_prg_print_version();
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
case 'V':
|
||||||
|
config->verbose++;
|
||||||
|
util_log_set_level(config->verbose);
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
ret = update_config_from_file(optarg, config);
|
||||||
|
if (ret < 0)
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
util_log_set_level(config->verbose);
|
||||||
|
break;
|
||||||
|
case 'C':
|
||||||
|
strncpy(config->vmcore_path, optarg,
|
||||||
|
sizeof(config->vmcore_path) - 1);
|
||||||
|
/* Ensure null termination */
|
||||||
|
config->vmcore_path[sizeof(config->vmcore_path) - 1] =
|
||||||
|
'\0';
|
||||||
|
break;
|
||||||
|
case 'H':
|
||||||
|
strncpy(config->zcore_hsa_path, optarg,
|
||||||
|
sizeof(config->zcore_hsa_path) - 1);
|
||||||
|
/* Ensure null termination */
|
||||||
|
config->zcore_hsa_path[sizeof(config->zcore_hsa_path) -
|
||||||
|
1] = '\0';
|
||||||
|
break;
|
||||||
|
case 'W':
|
||||||
|
strncpy(config->workdir_path, optarg,
|
||||||
|
sizeof(config->workdir_path) - 1);
|
||||||
|
/* Ensure null termination */
|
||||||
|
config->workdir_path[sizeof(config->workdir_path) - 1] =
|
||||||
|
'\0';
|
||||||
|
break;
|
||||||
|
case 'B':
|
||||||
|
strncpy(config->bind_mount_vmcore_path, optarg,
|
||||||
|
sizeof(config->bind_mount_vmcore_path) - 1);
|
||||||
|
/* Ensure null termination */
|
||||||
|
config->bind_mount_vmcore_path
|
||||||
|
[sizeof(config->bind_mount_vmcore_path) - 1] =
|
||||||
|
'\0';
|
||||||
|
break;
|
||||||
|
case 'S':
|
||||||
|
strncpy(config->swap, optarg, sizeof(config->swap) - 1);
|
||||||
|
/* Ensure null termination */
|
||||||
|
config->swap[sizeof(config->swap) - 1] = '\0';
|
||||||
|
break;
|
||||||
|
case 'T': {
|
||||||
|
char *endptr;
|
||||||
|
long hsa_size = strtol(optarg, &endptr, 0);
|
||||||
|
|
||||||
|
if (*endptr != '\0' || hsa_size < -1 ||
|
||||||
|
hsa_size > INT_MAX) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"The given HSA size is invalid.\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
config->hsa_size = hsa_size;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'D':
|
||||||
|
config->mount_debugfs = true;
|
||||||
|
break;
|
||||||
|
case 'F':
|
||||||
|
config->use_hsa_mem = true;
|
||||||
|
break;
|
||||||
|
case 'R':
|
||||||
|
config->release_hsa = false;
|
||||||
|
break;
|
||||||
|
case 'N':
|
||||||
|
config->bind_mount_vmcore = false;
|
||||||
|
break;
|
||||||
|
case 'G':
|
||||||
|
config->fuse_debug = true;
|
||||||
|
break;
|
||||||
|
case '?':
|
||||||
|
default:
|
||||||
|
util_opt_print_parse_error(opt, argv);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* Copyright IBM Corp. 2021
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _HSAVMCORE_CMDLINE_OPTIONS_H
|
||||||
|
#define _HSAVMCORE_CMDLINE_OPTIONS_H
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parses the given command-line options and adjusts the application's
|
||||||
|
* configuration accordingly.
|
||||||
|
*/
|
||||||
|
void parse_cmdline_options(int argc, char *argv[], struct config *config);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* Copyright IBM Corp. 2021
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _HSAVMCORE_COMMON_H
|
||||||
|
#define _HSAVMCORE_COMMON_H
|
||||||
|
|
||||||
|
#define NAME "hsavmcore"
|
||||||
|
|
||||||
|
#define DEBUGFS_MOUNT_POINT "/sys/kernel/debug"
|
||||||
|
|
||||||
|
#define ZCORE_HSA DEBUGFS_MOUNT_POINT "/zcore/hsa"
|
||||||
|
|
||||||
|
#define VMCORE_FILE "vmcore"
|
||||||
|
|
||||||
|
#define PROC_VMCORE "/proc/" VMCORE_FILE
|
||||||
|
|
||||||
|
#define WORKDIR "/var/crash"
|
||||||
|
|
||||||
|
#define HSA_CACHE_FILE NAME "-hsa-cache.bin"
|
||||||
|
|
||||||
|
#define OVERLAY_MOUNT_POINT "/tmp/" NAME "-overlay/"
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,242 @@
|
|||||||
|
/*
|
||||||
|
* Copyright IBM Corp. 2021
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <limits.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#include "lib/util_libc.h"
|
||||||
|
#include "lib/util_log.h"
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#define CONFIG_LINE_MAX_SIZE 1024
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Supported configuration parameters
|
||||||
|
*/
|
||||||
|
#define CONFIG_VERBOSE "verbose"
|
||||||
|
#define CONFIG_WORKDIR "workdir"
|
||||||
|
#define CONFIG_HSA_SIZE "hsa_size"
|
||||||
|
#define CONFIG_MOUNT_DEBUGFS "mount_debugfs"
|
||||||
|
#define CONFIG_USE_HSA_MEM "use_hsa_mem"
|
||||||
|
#define CONFIG_RELEASE_HSA "release_hsa"
|
||||||
|
#define CONFIG_BIND_MOUNT_VMCORE "bind_mount_vmcore"
|
||||||
|
#define CONFIG_FUSE_DEBUG "fuse_debug"
|
||||||
|
#define CONFIG_SWAP "swap"
|
||||||
|
|
||||||
|
static char *get_value_str(char *line)
|
||||||
|
{
|
||||||
|
char *ptr = strchr(line, '=');
|
||||||
|
|
||||||
|
if (!ptr)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return util_strstrip(ptr + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_bool(char *line, int linenum, const char *name, bool *value)
|
||||||
|
{
|
||||||
|
const char *value_str;
|
||||||
|
unsigned long value_num;
|
||||||
|
char *endptr;
|
||||||
|
|
||||||
|
value_str = get_value_str(line);
|
||||||
|
if (!value_str) {
|
||||||
|
util_log_print(UTIL_LOG_ERROR,
|
||||||
|
"config line %d: value expected for %s\n",
|
||||||
|
linenum, name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
util_log_print(UTIL_LOG_DEBUG, "config parse bool: %s\n", value_str);
|
||||||
|
|
||||||
|
value_num = strtoul(value_str, &endptr, 0);
|
||||||
|
if (*endptr != '\0' || (value_num != 0 && value_num != 1)) {
|
||||||
|
util_log_print(UTIL_LOG_ERROR,
|
||||||
|
"config line %d: invalid value for %s\n",
|
||||||
|
linenum, name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*value = value_num;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_int(char *line, int linenum, const char *name, int min_value,
|
||||||
|
int max_value, int *value)
|
||||||
|
{
|
||||||
|
const char *value_str;
|
||||||
|
long value_num;
|
||||||
|
char *endptr;
|
||||||
|
|
||||||
|
value_str = get_value_str(line);
|
||||||
|
if (!value_str) {
|
||||||
|
util_log_print(UTIL_LOG_ERROR,
|
||||||
|
"config line %d: value expected for %s\n",
|
||||||
|
linenum, name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
util_log_print(UTIL_LOG_DEBUG, "config parse int: %s\n", value_str);
|
||||||
|
|
||||||
|
value_num = strtol(value_str, &endptr, 0);
|
||||||
|
if (*endptr != '\0' || value_num < min_value || value_num > max_value) {
|
||||||
|
util_log_print(UTIL_LOG_ERROR,
|
||||||
|
"config line %d: invalid value for %s\n",
|
||||||
|
linenum, name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*value = value_num;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_str(char *line, int linenum, const char *name, int min_size,
|
||||||
|
int max_size, char *value)
|
||||||
|
{
|
||||||
|
const char *value_str;
|
||||||
|
int size;
|
||||||
|
|
||||||
|
value_str = get_value_str(line);
|
||||||
|
if (!value_str) {
|
||||||
|
util_log_print(UTIL_LOG_ERROR,
|
||||||
|
"config line %d: value expected for %s\n",
|
||||||
|
linenum, name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
util_log_print(UTIL_LOG_DEBUG, "config parse string: %s\n", value_str);
|
||||||
|
|
||||||
|
size = strlen(value_str);
|
||||||
|
if ((min_size >= 0 && size < min_size) || size > max_size) {
|
||||||
|
util_log_print(UTIL_LOG_ERROR,
|
||||||
|
"config line %d: invalid value for %s\n",
|
||||||
|
linenum, name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
strncpy(value, value_str, max_size);
|
||||||
|
/* Ensure null termination */
|
||||||
|
value[max_size] = '\0';
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_line(char *line, int linenum, struct config *config)
|
||||||
|
{
|
||||||
|
if (strncmp(line, CONFIG_VERBOSE, strlen(CONFIG_VERBOSE)) == 0) {
|
||||||
|
if (parse_int(line, linenum, CONFIG_VERBOSE, UTIL_LOG_ERROR,
|
||||||
|
UTIL_LOG_TRACE, &config->verbose))
|
||||||
|
return -1;
|
||||||
|
} else if (strncmp(line, CONFIG_WORKDIR, strlen(CONFIG_WORKDIR)) == 0) {
|
||||||
|
if (parse_str(line, linenum, CONFIG_WORKDIR, 0,
|
||||||
|
sizeof(config->workdir_path) - 1,
|
||||||
|
config->workdir_path))
|
||||||
|
return -1;
|
||||||
|
} else if (strncmp(line, CONFIG_HSA_SIZE, strlen(CONFIG_HSA_SIZE)) ==
|
||||||
|
0) {
|
||||||
|
if (parse_int(line, linenum, CONFIG_HSA_SIZE, -1, INT_MAX,
|
||||||
|
&config->hsa_size))
|
||||||
|
return -1;
|
||||||
|
} else if (strncmp(line, CONFIG_MOUNT_DEBUGFS,
|
||||||
|
strlen(CONFIG_MOUNT_DEBUGFS)) == 0) {
|
||||||
|
if (parse_bool(line, linenum, CONFIG_MOUNT_DEBUGFS,
|
||||||
|
&config->mount_debugfs))
|
||||||
|
return -1;
|
||||||
|
} else if (strncmp(line, CONFIG_USE_HSA_MEM,
|
||||||
|
strlen(CONFIG_USE_HSA_MEM)) == 0) {
|
||||||
|
if (parse_bool(line, linenum, CONFIG_USE_HSA_MEM,
|
||||||
|
&config->use_hsa_mem))
|
||||||
|
return -1;
|
||||||
|
} else if (strncmp(line, CONFIG_RELEASE_HSA,
|
||||||
|
strlen(CONFIG_RELEASE_HSA)) == 0) {
|
||||||
|
if (parse_bool(line, linenum, CONFIG_RELEASE_HSA,
|
||||||
|
&config->release_hsa))
|
||||||
|
return -1;
|
||||||
|
} else if (strncmp(line, CONFIG_BIND_MOUNT_VMCORE,
|
||||||
|
strlen(CONFIG_BIND_MOUNT_VMCORE)) == 0) {
|
||||||
|
if (parse_bool(line, linenum, CONFIG_BIND_MOUNT_VMCORE,
|
||||||
|
&config->bind_mount_vmcore))
|
||||||
|
return -1;
|
||||||
|
} else if (strncmp(line, CONFIG_FUSE_DEBUG,
|
||||||
|
strlen(CONFIG_FUSE_DEBUG)) == 0) {
|
||||||
|
if (parse_bool(line, linenum, CONFIG_FUSE_DEBUG,
|
||||||
|
&config->fuse_debug))
|
||||||
|
return -1;
|
||||||
|
} else if (strncmp(line, CONFIG_SWAP, strlen(CONFIG_SWAP)) == 0) {
|
||||||
|
if (parse_str(line, linenum, CONFIG_SWAP, 0,
|
||||||
|
sizeof(config->swap) - 1, config->swap))
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
util_log_print(UTIL_LOG_ERROR,
|
||||||
|
"config line %d: unknown configuration '%s'\n",
|
||||||
|
linenum, line);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void init_config(struct config *config)
|
||||||
|
{
|
||||||
|
memset(config, 0, sizeof(struct config));
|
||||||
|
strncpy(config->vmcore_path, PROC_VMCORE,
|
||||||
|
sizeof(config->vmcore_path) - 1);
|
||||||
|
strncpy(config->zcore_hsa_path, ZCORE_HSA,
|
||||||
|
sizeof(config->zcore_hsa_path) - 1);
|
||||||
|
strncpy(config->workdir_path, WORKDIR,
|
||||||
|
sizeof(config->workdir_path) - 1);
|
||||||
|
strncpy(config->bind_mount_vmcore_path, PROC_VMCORE,
|
||||||
|
sizeof(config->bind_mount_vmcore_path) - 1);
|
||||||
|
config->hsa_size = -1;
|
||||||
|
config->mount_debugfs = false;
|
||||||
|
config->use_hsa_mem = false;
|
||||||
|
config->release_hsa = true;
|
||||||
|
config->bind_mount_vmcore = true;
|
||||||
|
config->fuse_debug = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int update_config_from_file(const char *config_path, struct config *config)
|
||||||
|
{
|
||||||
|
char line[CONFIG_LINE_MAX_SIZE], *ptr;
|
||||||
|
int ret = 0, linenum;
|
||||||
|
FILE *fp;
|
||||||
|
|
||||||
|
fp = fopen(config_path, "r");
|
||||||
|
if (!fp) {
|
||||||
|
util_log_print(UTIL_LOG_ERROR, "Couldn't open config file %s\n",
|
||||||
|
config_path);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read the given configuration file linewise and parse parameters */
|
||||||
|
linenum = 0;
|
||||||
|
while (fgets(line, sizeof(line), fp)) {
|
||||||
|
linenum++;
|
||||||
|
|
||||||
|
ptr = util_strstrip(line);
|
||||||
|
/* Skip empty or comment lines */
|
||||||
|
if (ptr[0] == '\0' || ptr[0] == '#')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
util_log_print(UTIL_LOG_DEBUG, "config line %d: %s\n", linenum,
|
||||||
|
ptr);
|
||||||
|
|
||||||
|
ret = parse_line(ptr, linenum, config);
|
||||||
|
if (ret < 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* Copyright IBM Corp. 2021
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _HSAVMCORE_CONFIG_H
|
||||||
|
#define _HSAVMCORE_CONFIG_H
|
||||||
|
|
||||||
|
#include <limits.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This represents the application's configuration.
|
||||||
|
*/
|
||||||
|
struct config {
|
||||||
|
/* Log message level */
|
||||||
|
int verbose;
|
||||||
|
/* Path to the system's vmcore file */
|
||||||
|
char vmcore_path[PATH_MAX];
|
||||||
|
/* Path to the system's zcore hsa file */
|
||||||
|
char zcore_hsa_path[PATH_MAX];
|
||||||
|
/*
|
||||||
|
* Path to a directory where the application could create temporary
|
||||||
|
* files.
|
||||||
|
*/
|
||||||
|
char workdir_path[PATH_MAX];
|
||||||
|
/* Path to a bind-mount target for vmcore Overlay */
|
||||||
|
char bind_mount_vmcore_path[PATH_MAX];
|
||||||
|
/* Path to a swap device/file */
|
||||||
|
char swap[PATH_MAX];
|
||||||
|
/* HSA memory size */
|
||||||
|
int hsa_size;
|
||||||
|
/* Indicates whether the debugfs shall be mounted */
|
||||||
|
bool mount_debugfs;
|
||||||
|
/* Indicates whether the HSA memory file reader shall be used */
|
||||||
|
bool use_hsa_mem;
|
||||||
|
/* Indicates whether the HSA memory shall be released after caching */
|
||||||
|
bool release_hsa;
|
||||||
|
/* Indicates whether a bind-mount of vmcore Proxy shall be enabled */
|
||||||
|
bool bind_mount_vmcore;
|
||||||
|
/* Indicates whether the FUSE debug messages shall be enabled */
|
||||||
|
bool fuse_debug;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initializes the application's configuration to its default values.
|
||||||
|
*/
|
||||||
|
void init_config(struct config *config);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Updates the application's configuration from the given configuration file.
|
||||||
|
*/
|
||||||
|
int update_config_from_file(const char *config_path, struct config *config);
|
||||||
|
|
||||||
|
#endif
|
||||||
+172
@@ -0,0 +1,172 @@
|
|||||||
|
/*
|
||||||
|
* Copyright IBM Corp. 2021
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <elf.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "lib/util_file.h"
|
||||||
|
#include "lib/util_log.h"
|
||||||
|
|
||||||
|
#include "hsa.h"
|
||||||
|
|
||||||
|
long get_hsa_size(const char *zcore_hsa_path)
|
||||||
|
{
|
||||||
|
long size;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
util_log_print(UTIL_LOG_DEBUG, "Reading HSA memory size from %s\n",
|
||||||
|
zcore_hsa_path);
|
||||||
|
|
||||||
|
/* Read HSA size */
|
||||||
|
ret = util_file_read_l(&size, 16, zcore_hsa_path);
|
||||||
|
if (ret < 0) {
|
||||||
|
util_log_print(UTIL_LOG_ERROR, "File read failed (%s)\n",
|
||||||
|
strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
long get_hsa_vmcore_offset(const char *vmcore_path)
|
||||||
|
{
|
||||||
|
Elf64_Ehdr elf_hdr;
|
||||||
|
int fd = -1, n, i;
|
||||||
|
long offset = -1;
|
||||||
|
|
||||||
|
util_log_print(UTIL_LOG_DEBUG,
|
||||||
|
"Reading HSA memory offset from vmcore %s\n",
|
||||||
|
vmcore_path);
|
||||||
|
|
||||||
|
/* Open vmcore file */
|
||||||
|
fd = open(vmcore_path, O_RDONLY);
|
||||||
|
if (fd < 0) {
|
||||||
|
util_log_print(UTIL_LOG_ERROR, "open syscall failed (%s)\n",
|
||||||
|
strerror(errno));
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
util_log_print(UTIL_LOG_DEBUG, "Reading vmcore ELF header\n");
|
||||||
|
|
||||||
|
/* Read ELF header */
|
||||||
|
n = read(fd, &elf_hdr, sizeof(Elf64_Ehdr));
|
||||||
|
if (n < 0) {
|
||||||
|
util_log_print(UTIL_LOG_ERROR, "read syscall failed (%s)\n",
|
||||||
|
strerror(errno));
|
||||||
|
goto fail;
|
||||||
|
} else if (n != sizeof(Elf64_Ehdr)) {
|
||||||
|
util_log_print(UTIL_LOG_ERROR,
|
||||||
|
"read syscall read less data than expected (%s)\n",
|
||||||
|
strerror(errno));
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Verify ELF header */
|
||||||
|
if ((memcmp(elf_hdr.e_ident, ELFMAG, SELFMAG) != 0) ||
|
||||||
|
elf_hdr.e_type != ET_CORE || elf_hdr.e_machine != EM_S390 ||
|
||||||
|
elf_hdr.e_ident[EI_CLASS] != ELFCLASS64) {
|
||||||
|
util_log_print(UTIL_LOG_ERROR, "Invalid vmcore ELF header\n");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
util_log_print(UTIL_LOG_DEBUG,
|
||||||
|
"Reading vmcore ELF program header(s)\n");
|
||||||
|
|
||||||
|
/* Read ELF program header(s) */
|
||||||
|
n = lseek(fd, elf_hdr.e_phoff, SEEK_SET);
|
||||||
|
if (n < 0) {
|
||||||
|
util_log_print(UTIL_LOG_ERROR, "lseek syscall failed (%s)\n",
|
||||||
|
strerror(errno));
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Go through all ELF program headers and find one
|
||||||
|
* that starts at physical/virtual address 0x0.
|
||||||
|
*/
|
||||||
|
for (i = 0; i < elf_hdr.e_phnum; i++) {
|
||||||
|
Elf64_Phdr elf_phdr;
|
||||||
|
|
||||||
|
util_log_print(UTIL_LOG_DEBUG,
|
||||||
|
"Reading vmcore ELF program header #%d\n", i);
|
||||||
|
|
||||||
|
n = read(fd, &elf_phdr, sizeof(Elf64_Phdr));
|
||||||
|
if (n < 0) {
|
||||||
|
util_log_print(UTIL_LOG_ERROR,
|
||||||
|
"read syscall failed (%s)\n",
|
||||||
|
strerror(errno));
|
||||||
|
goto fail;
|
||||||
|
} else if (n != sizeof(Elf64_Phdr)) {
|
||||||
|
util_log_print(UTIL_LOG_ERROR,
|
||||||
|
"read syscall read less data than expected (%s)\n",
|
||||||
|
strerror(errno));
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
util_log_print(UTIL_LOG_DEBUG,
|
||||||
|
"vmcore ELF program segment #%d: type=%lx vaddr=%lx paddr=%lx offset=%lx\n",
|
||||||
|
i, elf_phdr.p_type, elf_phdr.p_vaddr,
|
||||||
|
elf_phdr.p_paddr, elf_phdr.p_offset);
|
||||||
|
|
||||||
|
/* HSA memory starts at physical/virtual address 0x0 */
|
||||||
|
if (elf_phdr.p_type == PT_LOAD && elf_phdr.p_vaddr == 0 &&
|
||||||
|
elf_phdr.p_paddr == 0) {
|
||||||
|
offset = elf_phdr.p_offset;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (offset < 0) {
|
||||||
|
util_log_print(UTIL_LOG_ERROR,
|
||||||
|
"Couldn't find HSA memory offset in vmcore\n");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
util_log_print(UTIL_LOG_DEBUG, "HSA memory vmcore offset %lx\n",
|
||||||
|
offset);
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
return offset;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
if (fd >= 0)
|
||||||
|
close(fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int release_hsa(const char *zcore_hsa_path)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
util_log_print(UTIL_LOG_INFO, "Release HSA memory via %s\n",
|
||||||
|
zcore_hsa_path);
|
||||||
|
|
||||||
|
/* Release HSA memory */
|
||||||
|
ret = util_file_write_s("0", zcore_hsa_path);
|
||||||
|
if (ret < 0) {
|
||||||
|
util_log_print(UTIL_LOG_ERROR, "File write failed (%s)\n",
|
||||||
|
strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Verify that HSA memory has been released */
|
||||||
|
if (get_hsa_size(zcore_hsa_path) > 0) {
|
||||||
|
util_log_print(UTIL_LOG_ERROR, "HSA memory release failed\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
util_log_print(UTIL_LOG_INFO, "HSA memory successfully released\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* Copyright IBM Corp. 2021
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _HSAVMCORE_HSA_H
|
||||||
|
#define _HSAVMCORE_HSA_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The interface to a HSA memory reader.
|
||||||
|
* This interface must be implemented by a concrete HSA memory reader.
|
||||||
|
*/
|
||||||
|
struct hsa_reader {
|
||||||
|
/* Total HSA memory size */
|
||||||
|
long hsa_size;
|
||||||
|
/* Offset of HSA memory in /proc/vmcore */
|
||||||
|
long hsa_vmcore_offset;
|
||||||
|
/* Destroys a concrete HSA memory reader */
|
||||||
|
void (*destroy)(struct hsa_reader *self);
|
||||||
|
/* Reads a HSA memory block given by offset and size */
|
||||||
|
int (*read_at)(struct hsa_reader *self, long offset, void *buf,
|
||||||
|
int size);
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline long hsa_get_size(struct hsa_reader *self)
|
||||||
|
{
|
||||||
|
return self->hsa_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline long hsa_get_vmcore_offset(struct hsa_reader *self)
|
||||||
|
{
|
||||||
|
return self->hsa_vmcore_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void destroy_hsa_reader(struct hsa_reader *self)
|
||||||
|
{
|
||||||
|
self->destroy(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int read_hsa_at(struct hsa_reader *self, long offset, void *buf,
|
||||||
|
int size)
|
||||||
|
{
|
||||||
|
return self->read_at(self, offset, buf, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reads total HSA memory size from /sys/kernel/debug/zcore/hsa.
|
||||||
|
*/
|
||||||
|
long get_hsa_size(const char *zcore_hsa_path);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the offset of HSA memory in /proc/vmcore.
|
||||||
|
*/
|
||||||
|
long get_hsa_vmcore_offset(const char *vmcore_path);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Releases HSA memory.
|
||||||
|
*/
|
||||||
|
int release_hsa(const char *zcore_hsa_path);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns a pointer to the enclosing struct which contains
|
||||||
|
* the variable pointed to by the given pointer as a member.
|
||||||
|
*/
|
||||||
|
#define container_of(ptr, type, member) \
|
||||||
|
({ \
|
||||||
|
const typeof(((type *)NULL)->member) *mptr = (ptr); \
|
||||||
|
(type *)((char *)mptr - offsetof(type, member)); \
|
||||||
|
})
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,236 @@
|
|||||||
|
/*
|
||||||
|
* Copyright IBM Corp. 2021
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <limits.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "lib/zt_common.h"
|
||||||
|
#include "lib/util_log.h"
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include "hsa.h"
|
||||||
|
#include "hsa_file.h"
|
||||||
|
|
||||||
|
struct hsa_file_reader {
|
||||||
|
struct hsa_reader super;
|
||||||
|
/* Temporary file containing a copy of the HSA memory */
|
||||||
|
int fd;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void destroy(struct hsa_reader *super)
|
||||||
|
{
|
||||||
|
struct hsa_file_reader *self =
|
||||||
|
container_of(super, struct hsa_file_reader, super);
|
||||||
|
|
||||||
|
close(self->fd);
|
||||||
|
free(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int read_at(struct hsa_reader *super, long offset, void *buf, int size)
|
||||||
|
{
|
||||||
|
struct hsa_file_reader *self =
|
||||||
|
container_of(super, struct hsa_file_reader, super);
|
||||||
|
long n, nread = 0;
|
||||||
|
|
||||||
|
util_log_print(UTIL_LOG_DEBUG, "HSA file read: offset=%lx size=%x\n",
|
||||||
|
offset, size);
|
||||||
|
|
||||||
|
/* Validate given offset */
|
||||||
|
if (offset >= super->hsa_size)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Validate given size */
|
||||||
|
size = MIN(super->hsa_size - offset, size);
|
||||||
|
|
||||||
|
n = lseek(self->fd, offset, SEEK_SET);
|
||||||
|
if (n < 0) {
|
||||||
|
util_log_print(UTIL_LOG_ERROR, "lseek syscall failed (%s)\n",
|
||||||
|
strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (size) {
|
||||||
|
n = read(self->fd, buf + nread, size);
|
||||||
|
if (n < 0) {
|
||||||
|
util_log_print(UTIL_LOG_ERROR,
|
||||||
|
"read syscall failed (%s)\n",
|
||||||
|
strerror(errno));
|
||||||
|
return -1;
|
||||||
|
} else if (n == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
nread += n;
|
||||||
|
size -= n;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nread;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int copy_hsa_to_file(const char *vmcore_path, const char *workdir_path,
|
||||||
|
long size, long offset)
|
||||||
|
{
|
||||||
|
int fd_in = -1, fd_out = -1;
|
||||||
|
char cache_file_path[PATH_MAX];
|
||||||
|
long n;
|
||||||
|
|
||||||
|
snprintf(cache_file_path, sizeof(cache_file_path), "%s/%s",
|
||||||
|
workdir_path, HSA_CACHE_FILE);
|
||||||
|
|
||||||
|
util_log_print(UTIL_LOG_DEBUG,
|
||||||
|
"Copy HSA memory from vmcore %s to cache file %s\n",
|
||||||
|
vmcore_path, cache_file_path);
|
||||||
|
|
||||||
|
/* Open vmcore file */
|
||||||
|
fd_in = open(vmcore_path, O_RDONLY);
|
||||||
|
if (fd_in < 0) {
|
||||||
|
util_log_print(UTIL_LOG_ERROR, "open syscall failed (%s)\n",
|
||||||
|
strerror(errno));
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Open cache file */
|
||||||
|
fd_out = open(cache_file_path, O_RDWR | O_CREAT | O_TRUNC, 0644);
|
||||||
|
if (fd_out < 0) {
|
||||||
|
util_log_print(UTIL_LOG_ERROR, "open syscall failed (%s)\n",
|
||||||
|
strerror(errno));
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Unlink cache file to auto-delete it on close */
|
||||||
|
n = unlink(cache_file_path);
|
||||||
|
if (n < 0) {
|
||||||
|
util_log_print(UTIL_LOG_ERROR, "unlink syscall failed (%s)\n",
|
||||||
|
strerror(errno));
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy HSA memory to cache file */
|
||||||
|
n = lseek(fd_in, offset, SEEK_SET);
|
||||||
|
if (n < 0) {
|
||||||
|
util_log_print(UTIL_LOG_ERROR, "lseek syscall failed (%s)\n",
|
||||||
|
strerror(errno));
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy HSA memory chunkwise to the temporary file */
|
||||||
|
while (size) {
|
||||||
|
char buf[1024];
|
||||||
|
long nread, nwrite;
|
||||||
|
|
||||||
|
/* Read a chunk from vmcore */
|
||||||
|
nread = MIN((long)sizeof(buf), size);
|
||||||
|
|
||||||
|
n = read(fd_in, buf, nread);
|
||||||
|
if (n < 0) {
|
||||||
|
util_log_print(UTIL_LOG_ERROR,
|
||||||
|
"read syscall failed (%s)\n",
|
||||||
|
strerror(errno));
|
||||||
|
goto fail;
|
||||||
|
} else if (n == 0) {
|
||||||
|
util_log_print(UTIL_LOG_ERROR,
|
||||||
|
"read syscall read less data than expected\n");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write a chunk to cache file */
|
||||||
|
nwrite = n;
|
||||||
|
n = write(fd_out, buf, nwrite);
|
||||||
|
if (n < 0) {
|
||||||
|
util_log_print(UTIL_LOG_ERROR,
|
||||||
|
"write syscall failed (%s)\n",
|
||||||
|
strerror(errno));
|
||||||
|
goto fail;
|
||||||
|
} else if (n != nwrite) {
|
||||||
|
util_log_print(UTIL_LOG_ERROR,
|
||||||
|
"write syscall wrote less data than expected\n");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
size -= n;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reset cache file position */
|
||||||
|
n = lseek(fd_out, 0, SEEK_SET);
|
||||||
|
if (n < 0) {
|
||||||
|
util_log_print(UTIL_LOG_ERROR, "lseek syscall failed (%s)\n",
|
||||||
|
strerror(errno));
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
close(fd_in);
|
||||||
|
|
||||||
|
return fd_out;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
if (fd_in >= 0)
|
||||||
|
close(fd_in);
|
||||||
|
if (fd_out >= 0)
|
||||||
|
close(fd_out);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct hsa_reader *make_hsa_file_reader(const char *zcore_hsa_path,
|
||||||
|
const char *vmcore_path,
|
||||||
|
const char *workdir_path, long hsa_size,
|
||||||
|
bool release_hsa_flag)
|
||||||
|
{
|
||||||
|
struct hsa_file_reader *self;
|
||||||
|
long hsa_vmcore_offset;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
/* Calculate HSA size if not given by user */
|
||||||
|
if (hsa_size < 0) {
|
||||||
|
hsa_size = get_hsa_size(zcore_hsa_path);
|
||||||
|
if (hsa_size <= 0)
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
hsa_vmcore_offset = get_hsa_vmcore_offset(vmcore_path);
|
||||||
|
if (hsa_vmcore_offset < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
util_log_print(UTIL_LOG_INFO, "HSA: size=%lx vmcore offset=%lx\n",
|
||||||
|
hsa_size, hsa_vmcore_offset);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Store the whole HSA memory from /proc/vmcore to a temporary file
|
||||||
|
* before releasing HSA.
|
||||||
|
*/
|
||||||
|
fd = copy_hsa_to_file(vmcore_path, workdir_path, hsa_size,
|
||||||
|
hsa_vmcore_offset);
|
||||||
|
if (fd < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (release_hsa_flag) {
|
||||||
|
if (release_hsa(zcore_hsa_path)) {
|
||||||
|
close(fd);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self = malloc(sizeof(struct hsa_file_reader));
|
||||||
|
if (!self) {
|
||||||
|
util_log_print(UTIL_LOG_ERROR, "malloc failed\n");
|
||||||
|
close(fd);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
self->super.hsa_size = hsa_size;
|
||||||
|
self->super.hsa_vmcore_offset = hsa_vmcore_offset;
|
||||||
|
self->super.destroy = destroy;
|
||||||
|
self->super.read_at = read_at;
|
||||||
|
self->fd = fd;
|
||||||
|
|
||||||
|
return &self->super;
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* Copyright IBM Corp. 2021
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _HSAVMCORE_HSA_FILE_H
|
||||||
|
#define _HSAVMCORE_HSA_FILE_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "hsa.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This concrete HSA memory reader copies the whole HSA memory from /proc/vmcore
|
||||||
|
* to a temporary file.
|
||||||
|
* In order for it to work, the system must provide enough file storage.
|
||||||
|
* The advantage of this reader is that it doesn't require extra memory for
|
||||||
|
* caching.
|
||||||
|
*/
|
||||||
|
struct hsa_reader *make_hsa_file_reader(const char *zcore_hsa_path,
|
||||||
|
const char *vmcore_path,
|
||||||
|
const char *workdir_path, long hsa_size,
|
||||||
|
bool release_hsa_flag);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,151 @@
|
|||||||
|
/*
|
||||||
|
* Copyright IBM Corp. 2021
|
||||||
|
*
|
||||||
|
* s390-tools is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "lib/zt_common.h"
|
||||||
|
#include "lib/util_log.h"
|
||||||
|
|
||||||
|
#include "hsa.h"
|
||||||
|
#include "hsa_mem.h"
|
||||||
|
|
||||||
|
struct hsa_mem_reader {
|
||||||
|
struct hsa_reader super;
|
||||||
|
unsigned char cache[];
|
||||||
|
};
|
||||||
|
|
||||||
|
static void destroy(struct hsa_reader *super)
|
||||||
|
{
|
||||||
|
struct hsa_mem_reader *self =
|
||||||
|
container_of(super, struct hsa_mem_reader, super);
|
||||||
|
|
||||||
|
free(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int read_at(struct hsa_reader *super, long offset, void *buf, int size)
|
||||||
|
{
|
||||||
|
struct hsa_mem_reader *self =
|
||||||
|
container_of(super, struct hsa_mem_reader, super);
|
||||||
|
|
||||||
|
util_log_print(UTIL_LOG_DEBUG, "HSA file read: offset=%lx size=%x\n",
|
||||||
|
offset, size);
|
||||||
|
|
||||||
|
/* Validate given offset */
|
||||||
|
if (offset >= super->hsa_size)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Validate given size */
|
||||||
|
size = MIN(super->hsa_size - offset, size);
|
||||||
|
|
||||||
|
memcpy(buf, self->cache + offset, size);
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int read_hsa(const char *vmcore_path, long offset, void *buf, int size)
|
||||||
|
{
|
||||||
|
long n, nread = 0;
|
||||||
|
int fd = -1;
|
||||||
|
|
||||||
|
util_log_print(UTIL_LOG_DEBUG, "Read HSA memory from vmcore %s\n",
|
||||||
|
vmcore_path);
|
||||||
|
|
||||||
|
/* Open vmcore file */
|
||||||
|
fd = open(vmcore_path, O_RDONLY);
|
||||||
|
if (fd < 0) {
|
||||||
|
util_log_print(UTIL_LOG_ERROR, "open syscall failed (%s)\n",
|
||||||
|
strerror(errno));
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
n = lseek(fd, offset, SEEK_SET);
|
||||||
|
if (n < 0) {
|
||||||
|
util_log_print(UTIL_LOG_ERROR, "lseek syscall failed (%s)\n",
|
||||||
|
strerror(errno));
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read HSA memory */
|
||||||
|
while (size) {
|
||||||
|
n = read(fd, buf + nread, size);
|
||||||
|
if (n < 0) {
|
||||||
|
util_log_print(UTIL_LOG_ERROR,
|
||||||
|
"read syscall failed (%s)\n",
|
||||||
|
strerror(errno));
|
||||||
|
goto fail;
|
||||||
|
} else if (n == 0) {
|
||||||
|
util_log_print(UTIL_LOG_ERROR,
|
||||||
|
"read syscall read less data than expected\n");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
nread += n;
|
||||||
|
size -= n;
|
||||||
|
}
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
if (fd >= 0)
|
||||||
|
close(fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct hsa_reader *make_hsa_mem_reader(const char *zcore_hsa_path,
|
||||||
|
const char *vmcore_path, long hsa_size,
|
||||||
|
bool release_hsa_flag)
|
||||||
|
{
|
||||||
|
struct hsa_mem_reader *self;
|
||||||
|
long hsa_vmcore_offset;
|
||||||
|
|
||||||
|
/* Calculate HSA size if not given by user */
|
||||||
|
if (hsa_size < 0) {
|
||||||
|
hsa_size = get_hsa_size(zcore_hsa_path);
|
||||||
|
if (hsa_size <= 0)
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
hsa_vmcore_offset = get_hsa_vmcore_offset(vmcore_path);
|
||||||
|
if (hsa_vmcore_offset < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
util_log_print(UTIL_LOG_INFO, "HSA: size=%lx vmcore offset=%lx\n",
|
||||||
|
hsa_size, hsa_vmcore_offset);
|
||||||
|
|
||||||
|
self = malloc(sizeof(struct hsa_mem_reader) + hsa_size);
|
||||||
|
if (!self) {
|
||||||
|
util_log_print(UTIL_LOG_ERROR, "malloc failed\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Cache the whole HSA memory from /proc/vmcore before releasing HSA */
|
||||||
|
if (read_hsa(vmcore_path, hsa_vmcore_offset, self->cache, hsa_size)) {
|
||||||
|
free(self);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (release_hsa_flag) {
|
||||||
|
if (release_hsa(zcore_hsa_path)) {
|
||||||
|
free(self);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self->super.hsa_size = hsa_size;
|
||||||
|
self->super.hsa_vmcore_offset = hsa_vmcore_offset;
|
||||||
|
self->super.destroy = destroy;
|
||||||
|
self->super.read_at = read_at;
|
||||||
|
|
||||||
|
return &self->super;
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user