From f2c543b4ccad3b9f8871d952cddf66b3b438595b Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 2 Jul 2021 22:49:35 +0200 Subject: Merging upstream version 1.14. Signed-off-by: Daniel Baumann --- .github/workflows/c-cpp.yml | 18 + .gitignore | 3 + Documentation/cmds-main.txt | 35 +- Documentation/nvme-admin-passthru.1 | 4 +- Documentation/nvme-ana-log.1 | 4 +- Documentation/nvme-attach-ns.1 | 4 +- Documentation/nvme-changed-ns-list-log.1 | 4 +- Documentation/nvme-compare.1 | 4 +- Documentation/nvme-connect-all.1 | 14 +- Documentation/nvme-connect-all.html | 10 +- Documentation/nvme-connect-all.txt | 5 +- Documentation/nvme-connect.1 | 10 +- Documentation/nvme-connect.html | 8 +- Documentation/nvme-connect.txt | 1 + Documentation/nvme-copy.1 | 141 + Documentation/nvme-copy.html | 989 ++++++ Documentation/nvme-copy.txt | 111 + Documentation/nvme-create-ns.1 | 10 +- Documentation/nvme-create-ns.html | 27 +- Documentation/nvme-create-ns.txt | 6 + Documentation/nvme-delete-ns.1 | 4 +- Documentation/nvme-dera-stat.1 | 4 +- Documentation/nvme-detach-ns.1 | 4 +- Documentation/nvme-device-self-test.1 | 4 +- Documentation/nvme-dir-receive.1 | 4 +- Documentation/nvme-dir-send.1 | 4 +- Documentation/nvme-disconnect-all.1 | 4 +- Documentation/nvme-disconnect.1 | 4 +- Documentation/nvme-discover.1 | 10 +- Documentation/nvme-discover.html | 8 +- Documentation/nvme-discover.txt | 13 + Documentation/nvme-dsm.1 | 4 +- Documentation/nvme-effects-log.1 | 4 +- Documentation/nvme-endurance-event-agg-log.1 | 112 + Documentation/nvme-endurance-event-agg-log.html | 858 +++++ Documentation/nvme-endurance-event-agg-log.txt | 66 + Documentation/nvme-endurance-log.1 | 4 +- Documentation/nvme-endurance-log.html | 2 +- Documentation/nvme-error-log.1 | 4 +- Documentation/nvme-flush.1 | 4 +- Documentation/nvme-format.1 | 4 +- Documentation/nvme-fw-commit.1 | 4 +- Documentation/nvme-fw-download.1 | 4 +- Documentation/nvme-fw-log.1 | 4 +- Documentation/nvme-gen-hostnqn.1 | 4 +- Documentation/nvme-get-feature.1 | 4 +- Documentation/nvme-get-log.1 | 4 +- Documentation/nvme-get-ns-id.1 | 4 +- Documentation/nvme-get-ns-id.html | 14 +- Documentation/nvme-get-property.1 | 4 +- Documentation/nvme-help.1 | 4 +- Documentation/nvme-huawei-id-ctrl.1 | 4 +- Documentation/nvme-huawei-list.1 | 4 +- Documentation/nvme-id-ctrl.1 | 4 +- Documentation/nvme-id-iocs.1 | 72 + Documentation/nvme-id-iocs.html | 817 +++++ Documentation/nvme-id-iocs.txt | 39 + Documentation/nvme-id-ns.1 | 4 +- Documentation/nvme-id-nvmset.1 | 4 +- Documentation/nvme-intel-id-ctrl.1 | 4 +- Documentation/nvme-intel-internal-log.1 | 4 +- Documentation/nvme-intel-lat-stats.1 | 4 +- Documentation/nvme-intel-market-name.1 | 4 +- Documentation/nvme-intel-smart-log-add.1 | 4 +- Documentation/nvme-intel-temp-stats.1 | 4 +- Documentation/nvme-intel-temp-stats.html | 14 +- Documentation/nvme-io-passthru.1 | 4 +- Documentation/nvme-lba-status-log.1 | 105 + Documentation/nvme-lba-status-log.html | 838 +++++ Documentation/nvme-lba-status-log.txt | 54 + Documentation/nvme-list-ctrl.1 | 4 +- Documentation/nvme-list-ns.1 | 10 +- Documentation/nvme-list-ns.html | 20 +- Documentation/nvme-list-ns.txt | 6 + Documentation/nvme-list-subsys.1 | 4 +- Documentation/nvme-list.1 | 4 +- Documentation/nvme-lnvm-create.1 | 4 +- Documentation/nvme-lnvm-diag-bbtbl.1 | 4 +- Documentation/nvme-lnvm-diag-set-bbtbl.1 | 4 +- Documentation/nvme-lnvm-factory.1 | 4 +- Documentation/nvme-lnvm-id-ns.1 | 4 +- Documentation/nvme-lnvm-info.1 | 4 +- Documentation/nvme-lnvm-init.1 | 4 +- Documentation/nvme-lnvm-list.1 | 4 +- Documentation/nvme-lnvm-remove.1 | 4 +- Documentation/nvme-micron-clear-pcie-errors.1 | 71 + Documentation/nvme-micron-clear-pcie-errors.html | 803 +++++ Documentation/nvme-micron-clear-pcie-errors.txt | 39 + Documentation/nvme-micron-internal-log.1 | 74 + Documentation/nvme-micron-internal-log.html | 818 +++++ Documentation/nvme-micron-internal-log.txt | 43 + Documentation/nvme-micron-nand-stats.1 | 71 + Documentation/nvme-micron-nand-stats.html | 804 +++++ Documentation/nvme-micron-nand-stats.txt | 40 + Documentation/nvme-micron-pcie-stats.1 | 71 + Documentation/nvme-micron-pcie-stats.html | 804 +++++ Documentation/nvme-micron-pcie-stats.txt | 40 + Documentation/nvme-micron-selective-download.1 | 139 + Documentation/nvme-micron-selective-download.html | 874 ++++++ Documentation/nvme-micron-selective-download.txt | 63 + Documentation/nvme-micron-temperature-stats.1 | 71 + Documentation/nvme-micron-temperature-stats.html | 804 +++++ Documentation/nvme-micron-temperature-stats.txt | 40 + Documentation/nvme-netapp-ontapdevices.1 | 4 +- Documentation/nvme-netapp-smdevices.1 | 4 +- Documentation/nvme-ns-descs.1 | 4 +- Documentation/nvme-ns-rescan.1 | 4 +- Documentation/nvme-ns-rescan.html | 1256 +++++--- Documentation/nvme-nvm-id-ctrl.1 | 98 + Documentation/nvme-nvm-id-ctrl.html | 828 +++++ Documentation/nvme-nvm-id-ctrl.txt | 49 + Documentation/nvme-persistent-event-log.1 | 118 + Documentation/nvme-persistent-event-log.html | 874 ++++++ Documentation/nvme-persistent-event-log.txt | 76 + Documentation/nvme-pred-lat-event-agg-log.1 | 117 + Documentation/nvme-pred-lat-event-agg-log.html | 870 ++++++ Documentation/nvme-pred-lat-event-agg-log.txt | 72 + Documentation/nvme-predictable-lat-log.1 | 112 + Documentation/nvme-predictable-lat-log.html | 857 +++++ Documentation/nvme-predictable-lat-log.txt | 66 + Documentation/nvme-read.1 | 4 +- Documentation/nvme-reset.1 | 4 +- Documentation/nvme-resv-acquire.1 | 6 +- Documentation/nvme-resv-acquire.html | 2 +- Documentation/nvme-resv-acquire.txt | 2 +- Documentation/nvme-resv-notif-log.1 | 98 + Documentation/nvme-resv-notif-log.html | 829 +++++ Documentation/nvme-resv-notif-log.txt | 51 + Documentation/nvme-resv-register.1 | 4 +- Documentation/nvme-resv-release.1 | 6 +- Documentation/nvme-resv-release.html | 2 +- Documentation/nvme-resv-release.txt | 2 +- Documentation/nvme-resv-report.1 | 4 +- Documentation/nvme-resv-report.html | 7 +- Documentation/nvme-rpmb.1 | 326 ++ Documentation/nvme-rpmb.html | 1008 ++++++ Documentation/nvme-rpmb.txt | 150 + Documentation/nvme-sanitize-log.1 | 15 +- Documentation/nvme-sanitize-log.html | 15 +- Documentation/nvme-sanitize-log.txt | 6 +- Documentation/nvme-sanitize.1 | 4 +- Documentation/nvme-security-recv.1 | 4 +- Documentation/nvme-security-send.1 | 4 +- Documentation/nvme-self-test-log.1 | 16 +- Documentation/nvme-self-test-log.html | 30 +- Documentation/nvme-self-test-log.txt | 9 +- Documentation/nvme-set-feature.1 | 4 +- Documentation/nvme-set-property.1 | 4 +- Documentation/nvme-show-hostnqn.1 | 4 +- Documentation/nvme-show-regs.1 | 4 +- Documentation/nvme-smart-log.1 | 4 +- Documentation/nvme-smart-log.html | 14 +- Documentation/nvme-subsystem-reset.1 | 4 +- Documentation/nvme-telemetry-log.1 | 4 +- .../nvme-toshiba-clear-pcie-correctable-errors.1 | 4 +- Documentation/nvme-toshiba-vs-internal-log.1 | 4 +- Documentation/nvme-toshiba-vs-smart-add-log.1 | 4 +- Documentation/nvme-transcend-badblock.1 | 4 +- Documentation/nvme-transcend-healthvalue.1 | 4 +- .../nvme-virtium-save-smart-to-vtview-log.1 | 4 +- Documentation/nvme-virtium-show-identify.1 | 4 +- Documentation/nvme-wdc-cap-diag.1 | 4 +- Documentation/nvme-wdc-capabilities.1 | 68 + Documentation/nvme-wdc-capabilities.html | 796 +++++ Documentation/nvme-wdc-capabilities.txt | 32 + Documentation/nvme-wdc-clear-assert-dump.1 | 4 +- Documentation/nvme-wdc-clear-fw-activate-history.1 | 4 +- .../nvme-wdc-clear-pcie-correctable-errors.1 | 4 +- Documentation/nvme-wdc-cloud-SSD-plugin-version.1 | 68 + .../nvme-wdc-cloud-SSD-plugin-version.html | 793 +++++ .../nvme-wdc-cloud-SSD-plugin-version.txt | 33 + Documentation/nvme-wdc-drive-essentials.1 | 4 +- Documentation/nvme-wdc-drive-log.1 | 4 +- Documentation/nvme-wdc-drive-resize.1 | 4 +- Documentation/nvme-wdc-enc-get-log.1 | 104 + Documentation/nvme-wdc-enc-get-log.html | 839 +++++ Documentation/nvme-wdc-enc-get-log.txt | 54 + Documentation/nvme-wdc-get-crash-dump.1 | 4 +- Documentation/nvme-wdc-get-drive-status.1 | 4 +- Documentation/nvme-wdc-get-pfail-dump.1 | 4 +- Documentation/nvme-wdc-id-ctrl.1 | 4 +- Documentation/nvme-wdc-log-page-directory.1 | 4 +- Documentation/nvme-wdc-namespace-resize.1 | 4 +- Documentation/nvme-wdc-purge-monitor.1 | 4 +- Documentation/nvme-wdc-purge.1 | 4 +- Documentation/nvme-wdc-vs-drive-info.1 | 4 +- .../nvme-wdc-vs-error-reason-identifier.1 | 4 +- Documentation/nvme-wdc-vs-fw-activate-history.1 | 4 +- Documentation/nvme-wdc-vs-internal-log.1 | 6 +- Documentation/nvme-wdc-vs-internal-log.html | 8 +- Documentation/nvme-wdc-vs-internal-log.txt | 30 +- Documentation/nvme-wdc-vs-nand-stats.1 | 4 +- Documentation/nvme-wdc-vs-nand-stats.html | 12 +- Documentation/nvme-wdc-vs-smart-add-log.1 | 421 +-- Documentation/nvme-wdc-vs-smart-add-log.html | 335 +- Documentation/nvme-wdc-vs-smart-add-log.txt | 226 +- .../nvme-wdc-vs-telemetry-controller-option.1 | 4 +- Documentation/nvme-wdc-vs-temperature-stats.1 | 179 ++ Documentation/nvme-wdc-vs-temperature-stats.html | 864 +++++ Documentation/nvme-wdc-vs-temperature-stats.txt | 78 + Documentation/nvme-write-uncor.1 | 4 +- Documentation/nvme-write-zeroes.1 | 4 +- Documentation/nvme-write.1 | 4 +- Documentation/nvme-zns-changed-zone-list.1 | 105 + Documentation/nvme-zns-changed-zone-list.html | 840 +++++ Documentation/nvme-zns-changed-zone-list.txt | 54 + Documentation/nvme-zns-close-zone.1 | 84 + Documentation/nvme-zns-close-zone.html | 841 +++++ Documentation/nvme-zns-close-zone.txt | 49 + Documentation/nvme-zns-finish-zone.1 | 84 + Documentation/nvme-zns-finish-zone.html | 842 +++++ Documentation/nvme-zns-finish-zone.txt | 50 + Documentation/nvme-zns-id-ctrl.1 | 98 + Documentation/nvme-zns-id-ctrl.html | 828 +++++ Documentation/nvme-zns-id-ctrl.txt | 50 + Documentation/nvme-zns-id-ns.1 | 110 + Documentation/nvme-zns-id-ns.html | 854 +++++ Documentation/nvme-zns-id-ns.txt | 62 + Documentation/nvme-zns-offline-zone.1 | 84 + Documentation/nvme-zns-offline-zone.html | 841 +++++ Documentation/nvme-zns-offline-zone.txt | 49 + Documentation/nvme-zns-open-zone.1 | 84 + Documentation/nvme-zns-open-zone.html | 841 +++++ Documentation/nvme-zns-open-zone.txt | 49 + Documentation/nvme-zns-report-zones.1 | 198 ++ Documentation/nvme-zns-report-zones.html | 964 ++++++ Documentation/nvme-zns-report-zones.txt | 102 + Documentation/nvme-zns-reset-zone.1 | 84 + Documentation/nvme-zns-reset-zone.html | 842 +++++ Documentation/nvme-zns-reset-zone.txt | 50 + Documentation/nvme-zns-set-zone-desc.1 | 82 + Documentation/nvme-zns-set-zone-desc.html | 842 +++++ Documentation/nvme-zns-set-zone-desc.txt | 50 + Documentation/nvme-zns-zone-append.1 | 133 + Documentation/nvme-zns-zone-append.html | 947 ++++++ Documentation/nvme-zns-zone-append.txt | 96 + Documentation/nvme-zns-zone-mgmt-recv.1 | 121 + Documentation/nvme-zns-zone-mgmt-recv.html | 889 ++++++ Documentation/nvme-zns-zone-mgmt-recv.txt | 78 + Documentation/nvme-zns-zone-mgmt-send.1 | 126 + Documentation/nvme-zns-zone-mgmt-send.html | 899 ++++++ Documentation/nvme-zns-zone-mgmt-send.txt | 77 + Documentation/nvme.1 | 63 +- Documentation/nvme.html | 92 +- Makefile | 33 +- NVME-VERSION-GEN | 2 +- README.md | 7 + common.h | 3 + completions/_nvme | 149 +- completions/bash-nvme-completion.sh | 54 +- fabrics.c | 744 +++-- fabrics.h | 42 + linux/nvme.h | 509 ++- nvme-builtin.h | 11 + nvme-filters.c | 16 + nvme-ioctl.c | 314 +- nvme-ioctl.h | 74 +- nvme-print.c | 1802 ++++++++++- nvme-print.h | 56 +- nvme-rpmb.c | 1001 ++++++ nvme-status.c | 37 +- nvme-topology.c | 259 +- nvme.c | 1449 +++++++-- nvme.h | 43 +- nvme.spec.in | 4 +- .../dracut-conf/70-nvmf-autoconnect.conf.in | 2 +- plugins/amzn/amzn-nvme.c | 59 + plugins/amzn/amzn-nvme.h | 17 + plugins/dera/dera-nvme.c | 3 +- plugins/huawei/huawei-nvme.c | 3 +- plugins/intel/intel-nvme.c | 513 ++- plugins/intel/intel-nvme.h | 1 + plugins/memblaze/memblaze-nvme.c | 781 +++-- plugins/memblaze/memblaze-nvme.h | 13 +- plugins/memblaze/memblaze-utils.h | 63 + plugins/micron/micron-nvme.c | 3293 ++++++++++++------- plugins/micron/micron-nvme.h | 10 + plugins/netapp/netapp-nvme.c | 13 +- plugins/nvidia/nvidia-nvme.c | 58 + plugins/nvidia/nvidia-nvme.h | 17 + plugins/scaleflux/sfx-nvme.c | 377 ++- plugins/scaleflux/sfx-nvme.h | 4 +- plugins/seagate/seagate-nvme.c | 33 +- plugins/shannon/shannon-nvme.c | 7 +- plugins/toshiba/toshiba-nvme.c | 2 +- plugins/virtium/virtium-nvme.c | 6 +- plugins/wdc/wdc-nvme.c | 3301 +++++++++++++++++--- plugins/wdc/wdc-nvme.h | 5 + plugins/wdc/wdc-utils.c | 15 + plugins/wdc/wdc-utils.h | 1 + plugins/ymtc/ymtc-nvme.c | 148 + plugins/ymtc/ymtc-nvme.h | 24 + plugins/ymtc/ymtc-utils.h | 80 + plugins/zns/zns.c | 923 ++++++ plugins/zns/zns.h | 30 + scripts/gen-hostnqn.sh | 6 +- tests/nvme_get_features_test.py | 7 +- util/argconfig.c | 31 +- util/argconfig.h | 4 +- util/cleanup.c | 4 + util/cleanup.h | 18 + util/json.c | 26 +- util/json.h | 17 +- util/log.c | 90 + util/log.h | 34 + 305 files changed, 49236 insertions(+), 4494 deletions(-) create mode 100644 .github/workflows/c-cpp.yml create mode 100644 Documentation/nvme-copy.1 create mode 100644 Documentation/nvme-copy.html create mode 100644 Documentation/nvme-copy.txt create mode 100644 Documentation/nvme-endurance-event-agg-log.1 create mode 100644 Documentation/nvme-endurance-event-agg-log.html create mode 100644 Documentation/nvme-endurance-event-agg-log.txt create mode 100644 Documentation/nvme-id-iocs.1 create mode 100644 Documentation/nvme-id-iocs.html create mode 100644 Documentation/nvme-id-iocs.txt create mode 100644 Documentation/nvme-lba-status-log.1 create mode 100644 Documentation/nvme-lba-status-log.html create mode 100644 Documentation/nvme-lba-status-log.txt create mode 100644 Documentation/nvme-micron-clear-pcie-errors.1 create mode 100644 Documentation/nvme-micron-clear-pcie-errors.html create mode 100644 Documentation/nvme-micron-clear-pcie-errors.txt create mode 100644 Documentation/nvme-micron-internal-log.1 create mode 100644 Documentation/nvme-micron-internal-log.html create mode 100644 Documentation/nvme-micron-internal-log.txt create mode 100644 Documentation/nvme-micron-nand-stats.1 create mode 100644 Documentation/nvme-micron-nand-stats.html create mode 100644 Documentation/nvme-micron-nand-stats.txt create mode 100644 Documentation/nvme-micron-pcie-stats.1 create mode 100644 Documentation/nvme-micron-pcie-stats.html create mode 100644 Documentation/nvme-micron-pcie-stats.txt create mode 100644 Documentation/nvme-micron-selective-download.1 create mode 100644 Documentation/nvme-micron-selective-download.html create mode 100644 Documentation/nvme-micron-selective-download.txt create mode 100644 Documentation/nvme-micron-temperature-stats.1 create mode 100644 Documentation/nvme-micron-temperature-stats.html create mode 100644 Documentation/nvme-micron-temperature-stats.txt create mode 100644 Documentation/nvme-nvm-id-ctrl.1 create mode 100644 Documentation/nvme-nvm-id-ctrl.html create mode 100644 Documentation/nvme-nvm-id-ctrl.txt create mode 100644 Documentation/nvme-persistent-event-log.1 create mode 100644 Documentation/nvme-persistent-event-log.html create mode 100644 Documentation/nvme-persistent-event-log.txt create mode 100644 Documentation/nvme-pred-lat-event-agg-log.1 create mode 100644 Documentation/nvme-pred-lat-event-agg-log.html create mode 100644 Documentation/nvme-pred-lat-event-agg-log.txt create mode 100644 Documentation/nvme-predictable-lat-log.1 create mode 100644 Documentation/nvme-predictable-lat-log.html create mode 100644 Documentation/nvme-predictable-lat-log.txt create mode 100644 Documentation/nvme-resv-notif-log.1 create mode 100644 Documentation/nvme-resv-notif-log.html create mode 100644 Documentation/nvme-resv-notif-log.txt create mode 100644 Documentation/nvme-rpmb.1 create mode 100644 Documentation/nvme-rpmb.html create mode 100644 Documentation/nvme-rpmb.txt create mode 100644 Documentation/nvme-wdc-capabilities.1 create mode 100644 Documentation/nvme-wdc-capabilities.html create mode 100644 Documentation/nvme-wdc-capabilities.txt create mode 100644 Documentation/nvme-wdc-cloud-SSD-plugin-version.1 create mode 100644 Documentation/nvme-wdc-cloud-SSD-plugin-version.html create mode 100644 Documentation/nvme-wdc-cloud-SSD-plugin-version.txt create mode 100644 Documentation/nvme-wdc-enc-get-log.1 create mode 100644 Documentation/nvme-wdc-enc-get-log.html create mode 100644 Documentation/nvme-wdc-enc-get-log.txt create mode 100644 Documentation/nvme-wdc-vs-temperature-stats.1 create mode 100644 Documentation/nvme-wdc-vs-temperature-stats.html create mode 100644 Documentation/nvme-wdc-vs-temperature-stats.txt create mode 100644 Documentation/nvme-zns-changed-zone-list.1 create mode 100644 Documentation/nvme-zns-changed-zone-list.html create mode 100644 Documentation/nvme-zns-changed-zone-list.txt create mode 100644 Documentation/nvme-zns-close-zone.1 create mode 100644 Documentation/nvme-zns-close-zone.html create mode 100644 Documentation/nvme-zns-close-zone.txt create mode 100644 Documentation/nvme-zns-finish-zone.1 create mode 100644 Documentation/nvme-zns-finish-zone.html create mode 100644 Documentation/nvme-zns-finish-zone.txt create mode 100644 Documentation/nvme-zns-id-ctrl.1 create mode 100644 Documentation/nvme-zns-id-ctrl.html create mode 100644 Documentation/nvme-zns-id-ctrl.txt create mode 100644 Documentation/nvme-zns-id-ns.1 create mode 100644 Documentation/nvme-zns-id-ns.html create mode 100644 Documentation/nvme-zns-id-ns.txt create mode 100644 Documentation/nvme-zns-offline-zone.1 create mode 100644 Documentation/nvme-zns-offline-zone.html create mode 100644 Documentation/nvme-zns-offline-zone.txt create mode 100644 Documentation/nvme-zns-open-zone.1 create mode 100644 Documentation/nvme-zns-open-zone.html create mode 100644 Documentation/nvme-zns-open-zone.txt create mode 100644 Documentation/nvme-zns-report-zones.1 create mode 100644 Documentation/nvme-zns-report-zones.html create mode 100644 Documentation/nvme-zns-report-zones.txt create mode 100644 Documentation/nvme-zns-reset-zone.1 create mode 100644 Documentation/nvme-zns-reset-zone.html create mode 100644 Documentation/nvme-zns-reset-zone.txt create mode 100644 Documentation/nvme-zns-set-zone-desc.1 create mode 100644 Documentation/nvme-zns-set-zone-desc.html create mode 100644 Documentation/nvme-zns-set-zone-desc.txt create mode 100644 Documentation/nvme-zns-zone-append.1 create mode 100644 Documentation/nvme-zns-zone-append.html create mode 100644 Documentation/nvme-zns-zone-append.txt create mode 100644 Documentation/nvme-zns-zone-mgmt-recv.1 create mode 100644 Documentation/nvme-zns-zone-mgmt-recv.html create mode 100644 Documentation/nvme-zns-zone-mgmt-recv.txt create mode 100644 Documentation/nvme-zns-zone-mgmt-send.1 create mode 100644 Documentation/nvme-zns-zone-mgmt-send.html create mode 100644 Documentation/nvme-zns-zone-mgmt-send.txt mode change 100644 => 100755 nvme-print.c create mode 100644 nvme-rpmb.c create mode 100644 plugins/amzn/amzn-nvme.c create mode 100644 plugins/amzn/amzn-nvme.h create mode 100644 plugins/nvidia/nvidia-nvme.c create mode 100644 plugins/nvidia/nvidia-nvme.h create mode 100644 plugins/ymtc/ymtc-nvme.c create mode 100644 plugins/ymtc/ymtc-nvme.h create mode 100644 plugins/ymtc/ymtc-utils.h create mode 100644 plugins/zns/zns.c create mode 100644 plugins/zns/zns.h create mode 100644 util/cleanup.c create mode 100644 util/cleanup.h create mode 100644 util/log.c create mode 100644 util/log.h diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml new file mode 100644 index 0000000..d2f94e9 --- /dev/null +++ b/.github/workflows/c-cpp.yml @@ -0,0 +1,18 @@ +name: C/C++ CI + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: make + run: sudo apt-get install gcc-10-powerpc* && make clean && make && make clean && make LD=powerpc64le-linux-gnu-ld CC=powerpc64le-linux-gnu-gcc-10 CFLAGS='-O2 -g -Wall -Wformat-security -Werror -m64 -mcpu=power8 -mtune=power8 -I -I/usr/powerpc64-linux-gnu/include/' + diff --git a/.gitignore b/.gitignore index b0efd0b..8e5ae00 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,9 @@ a.out *~ *.swp NVME-VERSION-FILE +nvme.spec +nvme-*.tar.gz +version cscope.* diff --git a/Documentation/cmds-main.txt b/Documentation/cmds-main.txt index 824ca69..46df03d 100644 --- a/Documentation/cmds-main.txt +++ b/Documentation/cmds-main.txt @@ -17,7 +17,10 @@ linknvme:nvme-format[1]:: Format namespace(s) linknvme:nvme-fw-activate[1]:: - F/W Activate + F/W Activate (in old version < 1.2) + +linknvme:nvme-fw-commit[1]:: + F/W Commit (in > 1.2) linknvme:nvme-fw-download[1]:: F/W Download @@ -34,15 +37,24 @@ linknvme:nvme-get-log[1]:: linknvme:nvme-telemetry-log[1]:: Telemetry Host-Initiated Log +linknvme:nvme-changed-ns-list-log[1]:: + Retrieve Changed Namespace List Log + linknvme:nvme-smart-log[1]:: Retrieve Smart Log +linknvme:nvme-ana-log[1]:: + Retreive ANA(Asymmetric Namespace Access) Log + linknvme:nvme-endurance-log[1]:: Retrieve endurance Log linknvme:nvme-effects-log[1]:: Retrieve effects Log +linknvme:nvme-self-test-log[1]:: + Retrieve Device Self-test Log + linknvme:nvme-get-ns-id[1]:: Retrieve namespace identifier @@ -55,6 +67,12 @@ linknvme:nvme-id-ctrl[1]:: linknvme:nvme-id-ns[1]:: Identify Namespace +linknvme:nvme-id-nvmset[1]:: + Identify NVM Set List + +linknvme:nvme-id-iocs[1]:: + Identify I/O Command Set + linknvme:nvme-create-ns[1]:: Create a new namespace @@ -82,6 +100,15 @@ linknvme:nvme-list[1]:: linknvme:nvme-list-ctrl[1]:: List controller in NVMe subsystem +linknvme:nvme-list-subsys[1]:: + List NVMe subsystems + +linknvme:nvme-reset[1]:: + Reset a NVMe controller + +linknvme:nvme-device-self-test[1]:: + Issue Device Self-test Command + linknvme:nvme-read[1]:: Issue IO Read Command @@ -112,6 +139,12 @@ linknvme:nvme-security-recv[1]:: linknvme:nvme-security-send[1]:: Security Send +linknvme:nvme-dsm[1]:: + Issue Data Set Management Command + +linknvme:nvme-copy[1]:: + Issue Simple Copy Command + linknvme:nvme-set-feature[1]:: Set Feature diff --git a/Documentation/nvme-admin-passthru.1 b/Documentation/nvme-admin-passthru.1 index f6d247f..286709f 100644 --- a/Documentation/nvme-admin-passthru.1 +++ b/Documentation/nvme-admin-passthru.1 @@ -2,12 +2,12 @@ .\" Title: nvme-admin-passthru .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-ADMIN\-PASSTHR" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-ADMIN\-PASSTHR" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff --git a/Documentation/nvme-ana-log.1 b/Documentation/nvme-ana-log.1 index 862f42d..5ba4cc3 100644 --- a/Documentation/nvme-ana-log.1 +++ b/Documentation/nvme-ana-log.1 @@ -2,12 +2,12 @@ .\" Title: nvme-ana-log .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-ANA\-LOG" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-ANA\-LOG" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff --git a/Documentation/nvme-attach-ns.1 b/Documentation/nvme-attach-ns.1 index 0af22ae..37f63a8 100644 --- a/Documentation/nvme-attach-ns.1 +++ b/Documentation/nvme-attach-ns.1 @@ -2,12 +2,12 @@ .\" Title: nvme-attach-ns .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-ATTACH\-NS" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-ATTACH\-NS" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff --git a/Documentation/nvme-changed-ns-list-log.1 b/Documentation/nvme-changed-ns-list-log.1 index 13dda55..97372fd 100644 --- a/Documentation/nvme-changed-ns-list-log.1 +++ b/Documentation/nvme-changed-ns-list-log.1 @@ -2,12 +2,12 @@ .\" Title: nvme-changed-ns-list-log .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-CHANGED\-NS\-L" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-CHANGED\-NS\-L" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff --git a/Documentation/nvme-compare.1 b/Documentation/nvme-compare.1 index 0f1c6ff..0638190 100644 --- a/Documentation/nvme-compare.1 +++ b/Documentation/nvme-compare.1 @@ -2,12 +2,12 @@ .\" Title: nvme-compare .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-COMPARE" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-COMPARE" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff --git a/Documentation/nvme-connect-all.1 b/Documentation/nvme-connect-all.1 index f829935..a53a017 100644 --- a/Documentation/nvme-connect-all.1 +++ b/Documentation/nvme-connect-all.1 @@ -2,12 +2,12 @@ .\" Title: nvme-connect-all .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/29/2020 +.\" Date: 01/20/2021 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-CONNECT\-ALL" "1" "04/29/2020" "NVMe" "NVMe Manual" +.TH "NVME\-CONNECT\-ALL" "1" "01/20/2021" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -51,7 +51,7 @@ nvme-connect-all \- Discover and Connect to Fabrics controllers\&. [\-\-queue\-size=<#> | \-Q <#>] [\-\-matching | \-m] [\-\-persistent | \-p] - [\-\-quiet | \-q] + [\-\-quiet | \-S] .fi .SH "DESCRIPTION" .sp @@ -72,6 +72,7 @@ allbox tab(:); lt lt lt lt lt lt +lt lt lt lt. T{ Value @@ -90,6 +91,11 @@ T}:T{ The network fabric is a Fibre Channel network\&. T} T{ +tcp +T}:T{ +The network fabric is a TCP/IP network\&. +T} +T{ loop T}:T{ Connect to a NVMe over Fabrics target on the local host @@ -187,7 +193,7 @@ If a traddr was specified on the command line or in the configuration file, only Don\(cqt remove the discovery controller after retrieving the discovery log page\&. .RE .PP -\-q, \-\-quiet +\-S, \-\-quiet .RS 4 Suppress error messages\&. .RE diff --git a/Documentation/nvme-connect-all.html b/Documentation/nvme-connect-all.html index b5f22f7..f779c7b 100644 --- a/Documentation/nvme-connect-all.html +++ b/Documentation/nvme-connect-all.html @@ -768,7 +768,7 @@ nvme-connect-all(1) Manual Page [--queue-size=<#> | -Q <#>] [--matching | -m] [--persistent | -p] - [--quiet | -q] + [--quiet | -S]
@@ -825,6 +825,10 @@ cellspacing="0" cellpadding="4">

WIP The network fabric is a Fibre Channel network.

+

tcp

+

The network fabric is a TCP/IP network.

+ +

loop

Connect to a NVMe over Fabrics target on the local host

@@ -1044,7 +1048,7 @@ cellspacing="0" cellpadding="4">

--q +-S
--quiet @@ -1111,7 +1115,7 @@ nvme-connect(1)

diff --git a/Documentation/nvme-connect-all.txt b/Documentation/nvme-connect-all.txt index 63d3c8f..820dd6c 100644 --- a/Documentation/nvme-connect-all.txt +++ b/Documentation/nvme-connect-all.txt @@ -27,7 +27,7 @@ SYNOPSIS [--queue-size=<#> | -Q <#>] [--matching | -m] [--persistent | -p] - [--quiet | -q] + [--quiet | -S] DESCRIPTION ----------- @@ -58,6 +58,7 @@ OPTIONS |Value|Definition |rdma|The network fabric is an rdma network (RoCE, iWARP, Infiniband, basic rdma, etc) |fc |*WIP* The network fabric is a Fibre Channel network. +|tcp |The network fabric is a TCP/IP network. |loop|Connect to a NVMe over Fabrics target on the local host |================= @@ -153,7 +154,7 @@ OPTIONS Don't remove the discovery controller after retrieving the discovery log page. --q:: +-S:: --quiet:: Suppress error messages. diff --git a/Documentation/nvme-connect.1 b/Documentation/nvme-connect.1 index 9170d01..64d18bb 100644 --- a/Documentation/nvme-connect.1 +++ b/Documentation/nvme-connect.1 @@ -2,12 +2,12 @@ .\" Title: nvme-connect .\" Author: [see the "AUTHORS" section] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-CONNECT" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-CONNECT" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -65,6 +65,7 @@ allbox tab(:); lt lt lt lt lt lt +lt lt lt lt. T{ Value @@ -83,6 +84,11 @@ T}:T{ The network fabric is a Fibre Channel network\&. T} T{ +tcp +T}:T{ +The network fabric is a TCP/IP network\&. +T} +T{ loop T}:T{ Connect to a NVMe over Fabrics target on the local host diff --git a/Documentation/nvme-connect.html b/Documentation/nvme-connect.html index 3387333..1f523dc 100644 --- a/Documentation/nvme-connect.html +++ b/Documentation/nvme-connect.html @@ -4,7 +4,7 @@ - + nvme-connect(1) + + + + +
+
+

SYNOPSIS

+
+
+
nvme-copy <device> [--sdlba=<sdlba> | -d <sdlba>]
+                        [--blocks=<nlb-list,> | -b <nlb-list,>]
+                        [--slbs=<slbas,> | -s <slbas,>]
+                        [--limited-retry | -l]
+                        [--force-unit-access | -f]
+                        [--prinfow=<prinfow> | -p <prinfow>]
+                        [--prinfor=<prinfor> | -P <prinfor>]
+                        [--ref-tag=<reftag> | -r <reftag>]
+                        [--expected-ref-tags=<reftag,> | -R <reftag,>]
+                        [--app-tag=<apptag> | -a <apptag>]
+                        [--expected-app-tags=<apptag,> | -A <apptag,>]
+                        [--app-mask=<appmask> | -m <appmask>]
+                        [--expected-app-masks=<appmask,> | -M <appmask,>]
+                        [--dir-type=<type> | -T <type>]
+                        [--dir-spec=<spec> | -S <spec>]
+                        [--format=<entry-format> | -F <entry-format>]
+
+
+
+
+
+

DESCRIPTION

+
+

The Copy command is used by the host to copy data from one or more source +logical block ranges to a single consecutive destination logical block range.

+
+
+
+

OPTIONS

+
+
+
+--sdlba=<sdlba> +
+
+-d <sdlba> +
+
+

+ 64-bit addr of first destination logical block +

+
+
+--blocks=<nlb-list,> +
+
+-b <nlb-list,> +
+
+

+ Comma separated list of the number of blocks in each range +

+
+
+--slbs=<slbas,> +
+
+-s <slbas,> +
+
+

+ Comma separated list of the starting blocks in each range +

+
+
+--limited-retry +
+
+-l +
+
+

+ Sets the limited retry flag. +

+
+
+--force-unit-access +
+
+-f +
+
+

+ Set the force-unit access flag. +

+
+
+--prinfow=<prinfow> +
+
+-p <prinfow> +
+
+

+ Protection Information field write definition. +

+
+
+--prinfor=<prinfor> +
+
+-P <prinfor> +
+
+

+ Protection Information field read definition. +

+
+
+--ref-tag=<reftag> +
+
+-r <reftag> +
+
+

+ initial lba reference tag. +

+
+
+--expected-ref-tags=<reftag,> +
+
+-R <reftag,> +
+
+

+ expected lba reference tags (comma-separated list). +

+
+
+--app-tag=<apptag> +
+
+-a <apptag> +
+
+

+ lba app tag +

+
+
+--expected-app-tags=<apptag,> +
+
+-A <apptag,> +
+
+

+ expected lba app tags (comma-separated list) +

+
+
+--app-mask=<appmask> +
+
+-m <appmask> +
+
+

+ lba tag mask +

+
+
+--expected-app-masks=<appmask,> +
+
+-M <appmask,> +
+
+

+ expected lba tag masks (comma-separated list) +

+
+
+--dir-type=<type> +
+
+-T <type> +
+
+

+ Optional directive type. The nvme-cli only enforces the value + be in the defined range for the directive type, though the NVMe + specifcation (1.3a) defines only one directive, 01h, for write + stream idenfiers. +

+
+
+--dir-spec=<spec> +
+
+-S <spec> +
+
+

+ Optional field for directive specifics. When used with + write streams, this value is defined to be the write stream + identifier. The nvme-cli will not validate the stream requested + is within the controller’s capabilities. +

+
+
+--format=<entry-format> +
+
+-F <entry-format> +
+
+

+ source range entry format +

+
+
+
+
+
+

EXAMPLES

+
+

No examples yet.

+
+
+
+

NVME

+
+

Part of the nvme-user suite

+
+
+
+

+ + + diff --git a/Documentation/nvme-copy.txt b/Documentation/nvme-copy.txt new file mode 100644 index 0000000..d6452ec --- /dev/null +++ b/Documentation/nvme-copy.txt @@ -0,0 +1,111 @@ +nvme-copy(1) +============ + +NAME +---- +nvme-copy - Send an NVMe Simple Copy command, provide results + +SYNOPSIS +-------- +[verse] +'nvme-copy' [--sdlba= | -d ] + [--blocks= | -b ] + [--slbs= | -s ] + [--limited-retry | -l] + [--force-unit-access | -f] + [--prinfow= | -p ] + [--prinfor= | -P ] + [--ref-tag= | -r ] + [--expected-ref-tags= | -R ] + [--app-tag= | -a ] + [--expected-app-tags= | -A ] + [--app-mask= | -m ] + [--expected-app-masks= | -M ] + [--dir-type= | -T ] + [--dir-spec= | -S ] + [--format= | -F ] + +DESCRIPTION +----------- +The Copy command is used by the host to copy data from one or more source +logical block ranges to a single consecutive destination logical block range. + +OPTIONS +------- +--sdlba=:: +-d :: + 64-bit addr of first destination logical block + +--blocks=:: +-b :: + Comma separated list of the number of blocks in each range + +--slbs=:: +-s :: + Comma separated list of the starting blocks in each range + +--limited-retry:: +-l:: + Sets the limited retry flag. + +--force-unit-access:: +-f:: + Set the force-unit access flag. + +--prinfow=:: +-p :: + Protection Information field write definition. + +--prinfor=:: +-P :: + Protection Information field read definition. + +--ref-tag=:: +-r :: + initial lba reference tag. + +--expected-ref-tags=:: +-R :: + expected lba reference tags (comma-separated list). + +--app-tag=:: +-a :: + lba app tag + +--expected-app-tags=:: +-A :: + expected lba app tags (comma-separated list) + +--app-mask=:: +-m :: + lba tag mask + +--expected-app-masks=:: +-M :: + expected lba tag masks (comma-separated list) + +--dir-type=:: +-T :: + Optional directive type. The nvme-cli only enforces the value + be in the defined range for the directive type, though the NVMe + specifcation (1.3a) defines only one directive, 01h, for write + stream idenfiers. + +--dir-spec=:: +-S :: + Optional field for directive specifics. When used with + write streams, this value is defined to be the write stream + identifier. The nvme-cli will not validate the stream requested + is within the controller's capabilities. + +--format=:: +-F :: + source range entry format + +EXAMPLES +-------- +No examples yet. + +NVME +---- +Part of the nvme-user suite diff --git a/Documentation/nvme-create-ns.1 b/Documentation/nvme-create-ns.1 index 7f358c1..7a61f79 100644 --- a/Documentation/nvme-create-ns.1 +++ b/Documentation/nvme-create-ns.1 @@ -2,12 +2,12 @@ .\" Title: nvme-create-ns .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-CREATE\-NS" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-CREATE\-NS" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -39,6 +39,7 @@ nvme-create-ns \- Send NVMe Namespace management command to create namespace, re [\-\-nmic= | \-m ] [\-\-anagrp\-id= | \-a ] [\-\-nvmset\-id= | \-i ] + [\-\-csi= | \-y ] [\-\-block\-size= | \-b ] [\-\-timeout= | \-t ] DESCRIPTION @@ -90,6 +91,11 @@ ANA Gorup Identifier\&. If this value is 0h specifies that the controller determ This field specifies the identifier of the NVM Set\&. .RE .PP +\-y , \-\-csi= +.RS 4 +This field specifies the identifier of command set\&. if not issued, NVM Command Set will be selected\&. +.RE +.PP \-b, \-\-block\-size .RS 4 Target block size the new namespace should be formatted as\&. Potential FLBAS values will be values will be scanned and the lowest numbered will be selected for the create\-ns operation\&. Conflicts with \-\-flbas argument\&. diff --git a/Documentation/nvme-create-ns.html b/Documentation/nvme-create-ns.html index 1ecc66e..6d31241 100644 --- a/Documentation/nvme-create-ns.html +++ b/Documentation/nvme-create-ns.html @@ -1,9 +1,10 @@ + - + nvme-create-ns(1) + + + + +
+
+

SYNOPSIS

+
+
+
nvme endurance-event-agg-log <device> [--log-entries=<log_entries> | -e <log_entries>]
+                        [--rae | -r] [--raw-binary | -b]
+                        [--output-format=<fmt> | -o <fmt>]
+
+
+
+
+
+

DESCRIPTION

+
+

Retrieves the NVMe Endurance Event Aggregate log page from an NVMe device and +provides the returned structure.

+

The <device> parameter is mandatory and may be either the NVMe character +device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1).

+

On success, the returned endurance event agg log structure may be returned +in one of several ways depending on the option flags; the structure may parsed +by the program and printed in a readable format, the raw buffer may be +printed to stdout for another program to parse, or reported in json format.

+
+
+
+

OPTIONS

+
+
+
+-e <log_entries> +
+
+--log-entries=<log_entries> +
+
+

+ Retrieve the Endurance Group Event Aggregate Log pending entries. + This argument is mandatory and its success may depend on the device’s + statistics to provide this log For More details see NVM Express 1.4 Spec. + Section 5.14.1.15. The maximum number of log entries supported is 2044 + for the device. +

+
+
+-r +
+
+--rae +
+
+

+ Retain an Asynchronous Event. +

+
+
+-o <format> +
+
+--output-format=<format> +
+
+

+ Set the reporting format to normal, json, or + binary. Only one output format can be used at a time. +

+
+
+
+
+
+

EXAMPLES

+
+
    +
  • +

    +Print the Endurance log page in a human readable format: +

    +
    +
    +
    # nvme endurance-event-agg-log /dev/nvme0
    +
    +
  • +
  • +

    +Print the raw Endurance log to a file: +

    +
    +
    +
    # nvme endurance-event-agg-log /dev/nvme0 --output=binary > endurance_event_agg_log.raw
    +
    +

    It is probably a bad idea to not redirect stdout when using this mode.

    +
  • +
+
+
+
+

NVME

+
+

Part of the nvme-user suite

+
+
+
+

+ + + diff --git a/Documentation/nvme-endurance-event-agg-log.txt b/Documentation/nvme-endurance-event-agg-log.txt new file mode 100644 index 0000000..69701e3 --- /dev/null +++ b/Documentation/nvme-endurance-event-agg-log.txt @@ -0,0 +1,66 @@ +nvme-endurance-event-agg-log(1) +=============================== + +NAME +---- +nvme-endurance-event-agg-log - Send NVMe Endurance log page request, returns result and log + +SYNOPSIS +-------- +[verse] +'nvme endurance-event-agg-log' [--log-entries= | -e ] + [--rae | -r] [--raw-binary | -b] + [--output-format= | -o ] + +DESCRIPTION +----------- +Retrieves the NVMe Endurance Event Aggregate log page from an NVMe device and +provides the returned structure. + +The parameter is mandatory and may be either the NVMe character +device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1). + +On success, the returned endurance event agg log structure may be returned +in one of several ways depending on the option flags; the structure may parsed +by the program and printed in a readable format, the raw buffer may be +printed to stdout for another program to parse, or reported in json format. + +OPTIONS +------- +-e :: +--log-entries=:: + Retrieve the Endurance Group Event Aggregate Log pending entries. + This argument is mandatory and its success may depend on the device's + statistics to provide this log For More details see NVM Express 1.4 Spec. + Section 5.14.1.15. The maximum number of log entries supported is 2044 + for the device. + +-r:: +--rae:: + Retain an Asynchronous Event. + +-o :: +--output-format=:: + Set the reporting format to 'normal', 'json', or + 'binary'. Only one output format can be used at a time. + +EXAMPLES +-------- +* Print the Endurance log page in a human readable format: ++ +------------ +# nvme endurance-event-agg-log /dev/nvme0 +------------ ++ + +* Print the raw Endurance log to a file: ++ +------------ +# nvme endurance-event-agg-log /dev/nvme0 --output=binary > endurance_event_agg_log.raw +------------ ++ +It is probably a bad idea to not redirect stdout when using this mode. + +NVME +---- +Part of the nvme-user suite \ No newline at end of file diff --git a/Documentation/nvme-endurance-log.1 b/Documentation/nvme-endurance-log.1 index 067a7a3..5e3170d 100644 --- a/Documentation/nvme-endurance-log.1 +++ b/Documentation/nvme-endurance-log.1 @@ -2,12 +2,12 @@ .\" Title: nvme-endurance-log .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-ENDURANCE\-LOG" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-ENDURANCE\-LOG" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff --git a/Documentation/nvme-endurance-log.html b/Documentation/nvme-endurance-log.html index 42fe267..809edee 100644 --- a/Documentation/nvme-endurance-log.html +++ b/Documentation/nvme-endurance-log.html @@ -834,7 +834,7 @@ Print the raw Endurance log to a file: diff --git a/Documentation/nvme-error-log.1 b/Documentation/nvme-error-log.1 index e116ad4..4428aee 100644 --- a/Documentation/nvme-error-log.1 +++ b/Documentation/nvme-error-log.1 @@ -2,12 +2,12 @@ .\" Title: nvme-error-log .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-ERROR\-LOG" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-ERROR\-LOG" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff --git a/Documentation/nvme-flush.1 b/Documentation/nvme-flush.1 index 3c4ddeb..1ee4223 100644 --- a/Documentation/nvme-flush.1 +++ b/Documentation/nvme-flush.1 @@ -2,12 +2,12 @@ .\" Title: nvme-flush .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-FLUSH" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-FLUSH" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff --git a/Documentation/nvme-format.1 b/Documentation/nvme-format.1 index b47c53e..dad63a6 100644 --- a/Documentation/nvme-format.1 +++ b/Documentation/nvme-format.1 @@ -2,12 +2,12 @@ .\" Title: nvme-format .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-FORMAT" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-FORMAT" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff --git a/Documentation/nvme-fw-commit.1 b/Documentation/nvme-fw-commit.1 index 2a965a7..178e546 100644 --- a/Documentation/nvme-fw-commit.1 +++ b/Documentation/nvme-fw-commit.1 @@ -2,12 +2,12 @@ .\" Title: nvme-fw-commit .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-FW\-COMMIT" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-FW\-COMMIT" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff --git a/Documentation/nvme-fw-download.1 b/Documentation/nvme-fw-download.1 index 0fd360b..d1126da 100644 --- a/Documentation/nvme-fw-download.1 +++ b/Documentation/nvme-fw-download.1 @@ -2,12 +2,12 @@ .\" Title: nvme-fw-download .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-FW\-DOWNLOAD" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-FW\-DOWNLOAD" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff --git a/Documentation/nvme-fw-log.1 b/Documentation/nvme-fw-log.1 index f96bcdc..1d1da08 100644 --- a/Documentation/nvme-fw-log.1 +++ b/Documentation/nvme-fw-log.1 @@ -2,12 +2,12 @@ .\" Title: nvme-fw-log .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-FW\-LOG" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-FW\-LOG" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff --git a/Documentation/nvme-gen-hostnqn.1 b/Documentation/nvme-gen-hostnqn.1 index 255dcca..9074dcb 100644 --- a/Documentation/nvme-gen-hostnqn.1 +++ b/Documentation/nvme-gen-hostnqn.1 @@ -2,12 +2,12 @@ .\" Title: nvme-gen-hostnqn .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-GEN\-HOSTNQN" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-GEN\-HOSTNQN" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff --git a/Documentation/nvme-get-feature.1 b/Documentation/nvme-get-feature.1 index 1bc451f..399d1d2 100644 --- a/Documentation/nvme-get-feature.1 +++ b/Documentation/nvme-get-feature.1 @@ -2,12 +2,12 @@ .\" Title: nvme-get-feature .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-GET\-FEATURE" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-GET\-FEATURE" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff --git a/Documentation/nvme-get-log.1 b/Documentation/nvme-get-log.1 index 742a791..98f81a8 100644 --- a/Documentation/nvme-get-log.1 +++ b/Documentation/nvme-get-log.1 @@ -2,12 +2,12 @@ .\" Title: nvme-get-log .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-GET\-LOG" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-GET\-LOG" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff --git a/Documentation/nvme-get-ns-id.1 b/Documentation/nvme-get-ns-id.1 index f7e69e4..6987f3f 100644 --- a/Documentation/nvme-get-ns-id.1 +++ b/Documentation/nvme-get-ns-id.1 @@ -2,12 +2,12 @@ .\" Title: nvme-get-ns-id .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-GET\-NS\-ID" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-GET\-NS\-ID" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff --git a/Documentation/nvme-get-ns-id.html b/Documentation/nvme-get-ns-id.html index bd02929..6fd2329 100644 --- a/Documentation/nvme-get-ns-id.html +++ b/Documentation/nvme-get-ns-id.html @@ -1,9 +1,10 @@ + - + nvme-get-ns-id(1) + + + + +
+
+

SYNOPSIS

+
+
+
nvme id-iocs <device> [--controller-id=<cntid> | -c <cntid>]
+
+
+
+
+
+

DESCRIPTION

+
+

For the NVMe device given, send an identify command and return the Identify I/O +Command Set data structure.

+

The <device> parameter is mandatory and may be either the NVMe character +device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1).

+
+
+
+

OPTIONS

+
+
+
+-c <cntid> +
+
+--controller-id=<cntid> +
+
+

+ Retrieve the identify I/O Command set data structure for the given + cntid. If this value is not given, cntid will be 0xffff. +

+
+
+
+
+
+

EXAMPLES

+
+
    +
  • +

    +Have the program interpret the returned buffer and display the known +fields in a human readable format: +

    +
    +
    +
    # nvme id-iocs /dev/nvme0
    +
    +
  • +
+
+
+
+

NVME

+
+

Part of the nvme-user suite

+
+
+
+

+ + + diff --git a/Documentation/nvme-id-iocs.txt b/Documentation/nvme-id-iocs.txt new file mode 100644 index 0000000..9e53207 --- /dev/null +++ b/Documentation/nvme-id-iocs.txt @@ -0,0 +1,39 @@ +nvme-id-iocs(1) +=============== + +NAME +---- +nvme-id-iocs - Send NVMe Identify I/O Command Set, return result and structure + +SYNOPSIS +-------- +[verse] +'nvme id-iocs' [--controller-id= | -c ] + +DESCRIPTION +----------- +For the NVMe device given, send an identify command and return the Identify I/O +Command Set data structure. + +The parameter is mandatory and may be either the NVMe character +device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1). + +OPTIONS +------- +-c :: +--controller-id=:: + Retrieve the identify I/O Command set data structure for the given + cntid. If this value is not given, cntid will be 0xffff. + +EXAMPLES +-------- +* Have the program interpret the returned buffer and display the known +fields in a human readable format: ++ +------------ +# nvme id-iocs /dev/nvme0 +------------ + +NVME +---- +Part of the nvme-user suite diff --git a/Documentation/nvme-id-ns.1 b/Documentation/nvme-id-ns.1 index 55094c9..de73c08 100644 --- a/Documentation/nvme-id-ns.1 +++ b/Documentation/nvme-id-ns.1 @@ -2,12 +2,12 @@ .\" Title: nvme-id-ns .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-ID\-NS" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-ID\-NS" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff --git a/Documentation/nvme-id-nvmset.1 b/Documentation/nvme-id-nvmset.1 index bcec988..f29f45a 100644 --- a/Documentation/nvme-id-nvmset.1 +++ b/Documentation/nvme-id-nvmset.1 @@ -2,12 +2,12 @@ .\" Title: nvme-id-nvmset .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-ID\-NVMSET" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-ID\-NVMSET" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff --git a/Documentation/nvme-intel-id-ctrl.1 b/Documentation/nvme-intel-id-ctrl.1 index 9e90231..4605de0 100644 --- a/Documentation/nvme-intel-id-ctrl.1 +++ b/Documentation/nvme-intel-id-ctrl.1 @@ -2,12 +2,12 @@ .\" Title: nvme-intel-id-ctrl .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-INTEL\-ID\-CTR" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-INTEL\-ID\-CTR" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff --git a/Documentation/nvme-intel-internal-log.1 b/Documentation/nvme-intel-internal-log.1 index fdefc66..22e193d 100644 --- a/Documentation/nvme-intel-internal-log.1 +++ b/Documentation/nvme-intel-internal-log.1 @@ -2,12 +2,12 @@ .\" Title: nvme-intel-internal-log .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-INTEL\-INTERNA" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-INTEL\-INTERNA" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff --git a/Documentation/nvme-intel-lat-stats.1 b/Documentation/nvme-intel-lat-stats.1 index a936e9a..577c625 100644 --- a/Documentation/nvme-intel-lat-stats.1 +++ b/Documentation/nvme-intel-lat-stats.1 @@ -2,12 +2,12 @@ .\" Title: nvme-intel-lat-stats .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-INTEL\-LAT\-ST" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-INTEL\-LAT\-ST" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff --git a/Documentation/nvme-intel-market-name.1 b/Documentation/nvme-intel-market-name.1 index a19a844..92e2d3e 100644 --- a/Documentation/nvme-intel-market-name.1 +++ b/Documentation/nvme-intel-market-name.1 @@ -2,12 +2,12 @@ .\" Title: nvme-intel-market-name .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-INTEL\-MARKET\" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-INTEL\-MARKET\" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff --git a/Documentation/nvme-intel-smart-log-add.1 b/Documentation/nvme-intel-smart-log-add.1 index 94eac80..de28a8e 100644 --- a/Documentation/nvme-intel-smart-log-add.1 +++ b/Documentation/nvme-intel-smart-log-add.1 @@ -2,12 +2,12 @@ .\" Title: nvme-intel-smart-log-add .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-INTEL\-SMART\-" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-INTEL\-SMART\-" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff --git a/Documentation/nvme-intel-temp-stats.1 b/Documentation/nvme-intel-temp-stats.1 index aaab8fa..90cb356 100644 --- a/Documentation/nvme-intel-temp-stats.1 +++ b/Documentation/nvme-intel-temp-stats.1 @@ -2,12 +2,12 @@ .\" Title: nvme-intel-temp-stats .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-INTEL\-TEMP\-S" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-INTEL\-TEMP\-S" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff --git a/Documentation/nvme-intel-temp-stats.html b/Documentation/nvme-intel-temp-stats.html index 2005af0..b97b8b4 100644 --- a/Documentation/nvme-intel-temp-stats.html +++ b/Documentation/nvme-intel-temp-stats.html @@ -1,9 +1,10 @@ + - + nvme-intel-temp-stats(1) + + + + +
+
+

SYNOPSIS

+
+
+
nvme lba-status-log <device> [--rae | -r] [--output-format=<fmt> | -o <fmt>]
+
+
+
+
+
+

DESCRIPTION

+
+

Retrieves the NVMe LBA Status Log Page from an NVMe device and provides +the returned structure.

+

The <device> parameter is mandatory and may be either the NVMe character +device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1).

+

On success, the returned LBA Status Log Page structure may be returned +in one of several ways depending on the option flags; the structure may +parsed by the program and printed in a readable format or the raw buffer +may be printed to stdout for another program to parse.

+
+
+
+

OPTIONS

+
+
+
+-r +
+
+--rae +
+
+

+ Retain an Asynchronous Event. +

+
+
+-o <format> +
+
+--output-format=<format> +
+
+

+ Set the reporting format to normal, json, or binary. + Only one output format can be used at a time. +

+
+
+
+
+
+

EXAMPLES

+
+
    +
  • +

    +Print the LBA Status Log page in a normal readable format: +

    +
    +
    +
    # nvme lba-status-log /dev/nvme0
    +
    +
  • +
  • +

    +Show the output in json format +

    +
    +
    +
    # nvme lba-status-log /dev/nvme0 -o json
    ++
    +
    +NVME
    +
    +
  • +
+

Part of the nvme-user suite

+
+
+
+

+ + + diff --git a/Documentation/nvme-lba-status-log.txt b/Documentation/nvme-lba-status-log.txt new file mode 100644 index 0000000..0d888f7 --- /dev/null +++ b/Documentation/nvme-lba-status-log.txt @@ -0,0 +1,54 @@ +nvme-lba-status-log(1) +====================== + +NAME +---- +nvme-lba-status-log - Send LBA Status Log Page request returns result and log + +SYNOPSIS +-------- +[verse] +'nvme lba-status-log' [--rae | -r] [--output-format= | -o ] + +DESCRIPTION +----------- +Retrieves the NVMe LBA Status Log Page from an NVMe device and provides +the returned structure. + +The parameter is mandatory and may be either the NVMe character +device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1). + +On success, the returned LBA Status Log Page structure may be returned +in one of several ways depending on the option flags; the structure may +parsed by the program and printed in a readable format or the raw buffer +may be printed to stdout for another program to parse. + +OPTIONS +------- +-r:: +--rae:: + Retain an Asynchronous Event. + +-o :: +--output-format=:: + Set the reporting format to 'normal', 'json', or 'binary'. + Only one output format can be used at a time. + +EXAMPLES +-------- +* Print the LBA Status Log page in a normal readable format: ++ +------------ +# nvme lba-status-log /dev/nvme0 +------------ ++ + +* Show the output in json format ++ +------------ +# nvme lba-status-log /dev/nvme0 -o json ++ + +NVME +---- +Part of the nvme-user suite \ No newline at end of file diff --git a/Documentation/nvme-list-ctrl.1 b/Documentation/nvme-list-ctrl.1 index 516f1e8..ce64471 100644 --- a/Documentation/nvme-list-ctrl.1 +++ b/Documentation/nvme-list-ctrl.1 @@ -2,12 +2,12 @@ .\" Title: nvme-id-ns .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-ID\-NS" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-ID\-NS" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff --git a/Documentation/nvme-list-ns.1 b/Documentation/nvme-list-ns.1 index 3297d39..084eb3e 100644 --- a/Documentation/nvme-list-ns.1 +++ b/Documentation/nvme-list-ns.1 @@ -2,12 +2,12 @@ .\" Title: nvme-id-ns .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-ID\-NS" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-ID\-NS" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -33,6 +33,7 @@ nvme-list-ns \- Send NVMe Identify List Namespaces, return result and structure .sp .nf \fInvme list\-ns\fR [\-\-namespace\-id= | \-n ] + [\-\-csi= | \-y ] [\-\-all | \-a] .fi .SH "DESCRIPTION" @@ -49,6 +50,11 @@ On success, the namespace array is printed for each index and nsid for a valid n Retrieve the identify list structure starting with the given nsid\&. .RE .PP +\-y , \-\-csi= +.RS 4 +If this value is given, retrieve the identify list structure associated with the speicified I/O command set\&. +.RE +.PP \-a, \-\-all .RS 4 Retrieve the identify list structure for all namespaces in the subsystem, whether attached or inactive\&. diff --git a/Documentation/nvme-list-ns.html b/Documentation/nvme-list-ns.html index 45737bb..f88f3e8 100644 --- a/Documentation/nvme-list-ns.html +++ b/Documentation/nvme-list-ns.html @@ -1,9 +1,10 @@ + - + nvme-id-ns(1) + + + + +
+
+

NAME

+
+

nvme-micron-clear-pcie-errors - Clears correctable PCIe correctable errors of given Micron device

+
+
+
+

SYNOPSIS

+
+
+
nvme micron clear-pcie-correctable-errors <device>
+
+
+
+
+
+

DESCRIPTION

+
+

This command clears corretable pcie errors for the specified Micron device.

+

The <device> parameter is mandatory and may be either the NVMe +character device (ex: /dev/nvme0), or a namespace block device (ex: +/dev/nvme0n1).

+

This will only work on Micron devices devices of model numbers 54XX. Support for new +devices may be added subsequently.

+
+
+
+

OPTIONS

+
+

None

+
+
+
+

EXAMPLES

+
+
    +
  • +

    +Retrieve NAND statistics information +

    +
    +
    +
    # nvme micron clear-pcie-correctable-errors /dev/nvme0
    +
    +
  • +
+
+
+
+

NVME

+
+

Part of the nvme-user suite

+
+
+
+

+ + + diff --git a/Documentation/nvme-micron-clear-pcie-errors.txt b/Documentation/nvme-micron-clear-pcie-errors.txt new file mode 100644 index 0000000..d3afccf --- /dev/null +++ b/Documentation/nvme-micron-clear-pcie-errors.txt @@ -0,0 +1,39 @@ +nvme-micron-clear-pcie-errors(1) +=============================== + +NAME +---- +nvme-micron-clear-pcie-errors - Clears correctable PCIe correctable errors of given Micron device + +SYNOPSIS +-------- +[verse] +'nvme micron clear-pcie-correctable-errors' + +DESCRIPTION +----------- +This command clears corretable pcie errors for the specified Micron device. + +The parameter is mandatory and may be either the NVMe +character device (ex: /dev/nvme0), or a namespace block device (ex: +/dev/nvme0n1). + +This will only work on Micron devices devices of model numbers 54XX. Support for new +devices may be added subsequently. + +OPTIONS +------- +None + +EXAMPLES +-------- +* Retrieve NAND statistics information ++ +------------ +# nvme micron clear-pcie-correctable-errors /dev/nvme0 + +------------ + +NVME +---- +Part of the nvme-user suite diff --git a/Documentation/nvme-micron-internal-log.1 b/Documentation/nvme-micron-internal-log.1 new file mode 100644 index 0000000..4fb909d --- /dev/null +++ b/Documentation/nvme-micron-internal-log.1 @@ -0,0 +1,74 @@ +'\" t +.\" Title: nvme-micron-internal-log +.\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] +.\" Generator: DocBook XSL Stylesheets vsnapshot +.\" Date: 10/20/2020 +.\" Manual: NVMe Manual +.\" Source: NVMe +.\" Language: English +.\" +.TH "NVME\-MICRON\-INTERN" "1" "10/20/2020" "NVMe" "NVMe Manual" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +nvme-micron-internal-log \- Retrieve Micron device\*(Aqs internal logs and save to given zip file\&. +.SH "SYNOPSIS" +.sp +.nf +\fInvme micron vs\-internal\-log\fR [\-\-package=, \-p ] +.fi +.SH "DESCRIPTION" +.sp +For the given NVMe device, sends the Micron vendor specific device commands to retrieve various logs (in binary format) and compresses them and saves into speficied zip file\&. These vendor unique logs can be analysed with Micron Technical support team for any device specific issues +.sp +The parameter is mandatory and may be either the NVMe character device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1)\&. +.sp +This will only work on Micron devices devices of model numbers 9200 and 54XX\&. Support for new devices may be added subsequently\&. Results for any other device are undefined\&. +.SH "OPTIONS" +.PP +\-l , \-\-package= +.RS 4 +name of the file (with \&.zip extension) to save the device logs +.RE +.SH "EXAMPLES" +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Gets the logs from the device and saves to micron_logs\&.zip file +.sp +.if n \{\ +.RS 4 +.\} +.nf +# nvme micron vs\-internal\-log /dev/nvme0 \-\-package=micron_logs\&.zip +.fi +.if n \{\ +.RE +.\} +.RE +.SH "NVME" +.sp +Part of the nvme\-user suite diff --git a/Documentation/nvme-micron-internal-log.html b/Documentation/nvme-micron-internal-log.html new file mode 100644 index 0000000..324092d --- /dev/null +++ b/Documentation/nvme-micron-internal-log.html @@ -0,0 +1,818 @@ + + + + + + +nvme-micron-internal-log(1) + + + + + +
+
+

NAME

+
+

nvme-micron-internal-log - Retrieve Micron device’s internal logs and save to given zip file.

+
+
+
+

SYNOPSIS

+
+
+
nvme micron vs-internal-log <device> [--package=<FILE>, -p <FILE>]
+
+
+
+
+
+

DESCRIPTION

+
+

For the given NVMe device, sends the Micron vendor specific device commands to retrieve +various logs (in binary format) and compresses them and saves into speficied zip file. +These vendor unique logs can be analysed with Micron Technical support team for any device +specific issues

+

The <device> parameter is mandatory and may be either the NVMe +character device (ex: /dev/nvme0), or a namespace block device (ex: +/dev/nvme0n1).

+

This will only work on Micron devices devices of model numbers 9200 and 54XX. Support +for new devices may be added subsequently. Results for any other device are undefined.

+
+
+
+

OPTIONS

+
+
+
+-l <FILE> +
+
+--package=<FILE> +
+
+

+ name of the file (with .zip extension) to save the device logs +

+
+
+
+
+
+

EXAMPLES

+
+
    +
  • +

    +Gets the logs from the device and saves to micron_logs.zip file +

    +
    +
    +
    # nvme micron vs-internal-log /dev/nvme0 --package=micron_logs.zip
    +
    +
  • +
+
+
+
+

NVME

+
+

Part of the nvme-user suite

+
+
+
+

+ + + diff --git a/Documentation/nvme-micron-internal-log.txt b/Documentation/nvme-micron-internal-log.txt new file mode 100644 index 0000000..66fb90f --- /dev/null +++ b/Documentation/nvme-micron-internal-log.txt @@ -0,0 +1,43 @@ +nvme-micron-internal-log(1) +========================== + +NAME +---- +nvme-micron-internal-log - Retrieve Micron device's internal logs and save to given zip file. + +SYNOPSIS +-------- +[verse] +'nvme micron vs-internal-log' [--package=, -p ] + +DESCRIPTION +----------- +For the given NVMe device, sends the Micron vendor specific device commands to retrieve +various logs (in binary format) and compresses them and saves into speficied zip file. +These vendor unique logs can be analysed with Micron Technical support team for any device +specific issues + +The parameter is mandatory and may be either the NVMe +character device (ex: /dev/nvme0), or a namespace block device (ex: +/dev/nvme0n1). + +This will only work on Micron devices devices of model numbers 9200 and 54XX. Support +for new devices may be added subsequently. Results for any other device are undefined. + +OPTIONS +------- +-l :: +--package=:: + name of the file (with .zip extension) to save the device logs + +EXAMPLES +-------- +* Gets the logs from the device and saves to micron_logs.zip file ++ +------------ +# nvme micron vs-internal-log /dev/nvme0 --package=micron_logs.zip + +------------ +NVME +---- +Part of the nvme-user suite diff --git a/Documentation/nvme-micron-nand-stats.1 b/Documentation/nvme-micron-nand-stats.1 new file mode 100644 index 0000000..313e7d2 --- /dev/null +++ b/Documentation/nvme-micron-nand-stats.1 @@ -0,0 +1,71 @@ +'\" t +.\" Title: nvme-micron-nand-stats +.\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] +.\" Generator: DocBook XSL Stylesheets vsnapshot +.\" Date: 10/20/2020 +.\" Manual: NVMe Manual +.\" Source: NVMe +.\" Language: English +.\" +.TH "NVME\-MICRON\-NAND\-" "1" "10/20/2020" "NVMe" "NVMe Manual" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +nvme-micron-nand-stats \- Retrieves NAND statistics of given micron device +.SH "SYNOPSIS" +.sp +.nf +\fInvme micron vs\-nand\-stats\fR +.fi +.SH "DESCRIPTION" +.sp +This command prints NAND information (Total bytes written, Bad block count and Erase failures etc) for the given micron device\&. +.sp +The parameter is mandatory and may be either the NVMe character device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1)\&. +.sp +This will only work on Micron devices devices of model numbers 54XX\&. Support for new devices may be added subsequently\&. +.SH "OPTIONS" +.sp +None +.SH "EXAMPLES" +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Retrieve NAND statistics information +.sp +.if n \{\ +.RS 4 +.\} +.nf +# nvme micron vs\-nand\-stats /dev/nvme0 +.fi +.if n \{\ +.RE +.\} +.RE +.SH "NVME" +.sp +Part of the nvme\-user suite diff --git a/Documentation/nvme-micron-nand-stats.html b/Documentation/nvme-micron-nand-stats.html new file mode 100644 index 0000000..175dfd3 --- /dev/null +++ b/Documentation/nvme-micron-nand-stats.html @@ -0,0 +1,804 @@ + + + + + + +nvme-micron-nand-stats(1) + + + + + +
+
+

NAME

+
+

nvme-micron-nand-stats - Retrieves NAND statistics of given micron device

+
+
+
+

SYNOPSIS

+
+
+
nvme micron vs-nand-stats <device>
+
+
+
+
+
+

DESCRIPTION

+
+

This command prints NAND information (Total bytes written, Bad block count and +Erase failures etc) for the given micron device.

+

The <device> parameter is mandatory and may be either the NVMe +character device (ex: /dev/nvme0), or a namespace block device (ex: +/dev/nvme0n1).

+

This will only work on Micron devices devices of model numbers 54XX. Support for +new devices may be added subsequently.

+
+
+
+

OPTIONS

+
+

None

+
+
+
+

EXAMPLES

+
+
    +
  • +

    +Retrieve NAND statistics information +

    +
    +
    +
    # nvme micron vs-nand-stats /dev/nvme0
    +
    +
  • +
+
+
+
+

NVME

+
+

Part of the nvme-user suite

+
+
+
+

+ + + diff --git a/Documentation/nvme-micron-nand-stats.txt b/Documentation/nvme-micron-nand-stats.txt new file mode 100644 index 0000000..68e211e --- /dev/null +++ b/Documentation/nvme-micron-nand-stats.txt @@ -0,0 +1,40 @@ +nvme-micron-nand-stats(1) +========================= + +NAME +---- +nvme-micron-nand-stats - Retrieves NAND statistics of given micron device + +SYNOPSIS +-------- +[verse] +'nvme micron vs-nand-stats' + +DESCRIPTION +----------- +This command prints NAND information (Total bytes written, Bad block count and +Erase failures etc) for the given micron device. + +The parameter is mandatory and may be either the NVMe +character device (ex: /dev/nvme0), or a namespace block device (ex: +/dev/nvme0n1). + +This will only work on Micron devices devices of model numbers 54XX. Support for +new devices may be added subsequently. + +OPTIONS +------- +None + +EXAMPLES +-------- +* Retrieve NAND statistics information ++ +------------ +# nvme micron vs-nand-stats /dev/nvme0 + +------------ + +NVME +---- +Part of the nvme-user suite diff --git a/Documentation/nvme-micron-pcie-stats.1 b/Documentation/nvme-micron-pcie-stats.1 new file mode 100644 index 0000000..3070c7e --- /dev/null +++ b/Documentation/nvme-micron-pcie-stats.1 @@ -0,0 +1,71 @@ +'\" t +.\" Title: nvme-micron-pcie-stats +.\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] +.\" Generator: DocBook XSL Stylesheets vsnapshot +.\" Date: 10/20/2020 +.\" Manual: NVMe Manual +.\" Source: NVMe +.\" Language: English +.\" +.TH "NVME\-MICRON\-PCIE\-" "1" "10/20/2020" "NVMe" "NVMe Manual" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +nvme-micron-pcie-stats \- Retrieves pcie error statistics for given micron device +.SH "SYNOPSIS" +.sp +.nf +\fInvme micron vs\-pcie\-stats\fR +.fi +.SH "DESCRIPTION" +.sp +This command prints pcie correctable and uncorrectable error information for the given micron device\&. +.sp +The parameter is mandatory and may be either the NVMe character device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1)\&. +.sp +This will only work on Micron devices devices of model numbers 54XX\&. Support for new devices may be added subsequently\&. +.SH "OPTIONS" +.sp +None +.SH "EXAMPLES" +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Retrieve PCIe error information +.sp +.if n \{\ +.RS 4 +.\} +.nf +# nvme micron vs\-pcie\-stats /dev/nvme0 +.fi +.if n \{\ +.RE +.\} +.RE +.SH "NVME" +.sp +Part of the nvme\-user suite diff --git a/Documentation/nvme-micron-pcie-stats.html b/Documentation/nvme-micron-pcie-stats.html new file mode 100644 index 0000000..c27f4bb --- /dev/null +++ b/Documentation/nvme-micron-pcie-stats.html @@ -0,0 +1,804 @@ + + + + + + +nvme-micron-pcie-stats(1) + + + + + +
+
+

NAME

+
+

nvme-micron-pcie-stats - Retrieves pcie error statistics for given micron device

+
+
+
+

SYNOPSIS

+
+
+
nvme micron vs-pcie-stats <device>
+
+
+
+
+
+

DESCRIPTION

+
+

This command prints pcie correctable and uncorrectable error information for the +given micron device.

+

The <device> parameter is mandatory and may be either the NVMe +character device (ex: /dev/nvme0), or a namespace block device (ex: +/dev/nvme0n1).

+

This will only work on Micron devices devices of model numbers 54XX. Support for +new devices may be added subsequently.

+
+
+
+

OPTIONS

+
+

None

+
+
+
+

EXAMPLES

+
+
    +
  • +

    +Retrieve PCIe error information +

    +
    +
    +
    # nvme micron vs-pcie-stats /dev/nvme0
    +
    +
  • +
+
+
+
+

NVME

+
+

Part of the nvme-user suite

+
+
+
+

+ + + diff --git a/Documentation/nvme-micron-pcie-stats.txt b/Documentation/nvme-micron-pcie-stats.txt new file mode 100644 index 0000000..f58257a --- /dev/null +++ b/Documentation/nvme-micron-pcie-stats.txt @@ -0,0 +1,40 @@ +nvme-micron-pcie-stats(1) +========================= + +NAME +---- +nvme-micron-pcie-stats - Retrieves pcie error statistics for given micron device + +SYNOPSIS +-------- +[verse] +'nvme micron vs-pcie-stats' + +DESCRIPTION +----------- +This command prints pcie correctable and uncorrectable error information for the +given micron device. + +The parameter is mandatory and may be either the NVMe +character device (ex: /dev/nvme0), or a namespace block device (ex: +/dev/nvme0n1). + +This will only work on Micron devices devices of model numbers 54XX. Support for +new devices may be added subsequently. + +OPTIONS +------- +None + +EXAMPLES +-------- +* Retrieve PCIe error information ++ +------------ +# nvme micron vs-pcie-stats /dev/nvme0 + +------------ + +NVME +---- +Part of the nvme-user suite diff --git a/Documentation/nvme-micron-selective-download.1 b/Documentation/nvme-micron-selective-download.1 new file mode 100644 index 0000000..fd521b5 --- /dev/null +++ b/Documentation/nvme-micron-selective-download.1 @@ -0,0 +1,139 @@ +'\" t +.\" Title: nvme-micron-selective-download +.\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] +.\" Generator: DocBook XSL Stylesheets vsnapshot +.\" Date: 10/20/2020 +.\" Manual: NVMe Manual +.\" Source: NVMe +.\" Language: English +.\" +.TH "NVME\-MICRON\-SELECT" "1" "10/20/2020" "NVMe" "NVMe Manual" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +nvme-micron-selective-download \- Performs selective firmware download that allows user to select which firmware binary to update for 9200 devices\&. This requires power cycle once the update completes\&. +.SH "SYNOPSIS" +.sp +.nf +\fInvme micron selective\-download\fR [\-\-fw=, \-f ] [\-\-select=, \-s ] +.fi +.SH "DESCRIPTION" +.sp +This command uses micron vendor specific nvme commands to download given firmware image to the specified 9200 device to update selected or all portions of firmware image\&. +.sp +The parameter is mandatory and may be either the NVMe character device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1)\&. +.sp +This will only work on Micron devices devices of model number 9200\&. Support for new devices may be added subsequently\&. Results for any other device are undefined\&. +.SH "OPTIONS" +.PP +\-f , \-\-fw= +.RS 4 +name of the firmware image file +.RE +.PP +\-s , \-\-select= +.RS 4 +flag that has following values +.RE +.PP +OOB +.RS 4 +This updates the OOB and main firmware\en" +.RE +.PP +EEP +.RS 4 +This updates the eeprom and main firmware\en" +.RE +.PP +ALL +.RS 4 +This updates the eeprom, OOB, and main firmware"; +.RE +.SH "EXAMPLES" +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Update OOB and main firmware +.sp +.if n \{\ +.RS 4 +.\} +.nf +# nvme micron selective\-download /dev/nvme0 \-\-fw=firmware_bin \-\-select=OOB +# nvme micron selective\-download /dev/nvme0 \-f firmware_bin \-s OOB +.fi +.if n \{\ +.RE +.\} +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Update OOB and main firmware +.sp +.if n \{\ +.RS 4 +.\} +.nf +# nvme micron selective\-download /dev/nvme0 \-\-fw=firmware_bin \-\-select=EEP +# nvme micron selective\-download /dev/nvme0 \-f firmware_bin \-\-s EEP +.fi +.if n \{\ +.RE +.\} +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Update eeprom, OOB and main firmware +.sp +.if n \{\ +.RS 4 +.\} +.nf +# nvme micron selective\-download /dev/nvme0 \-\-fw=firmware_bin \-\-select=ALL +# nvme micron selective\-download /dev/nvme0 \-f firmware_bin \-\-s ALL +.fi +.if n \{\ +.RE +.\} +.RE +.SH "NVME" +.sp +Part of the nvme\-user suite diff --git a/Documentation/nvme-micron-selective-download.html b/Documentation/nvme-micron-selective-download.html new file mode 100644 index 0000000..7281f48 --- /dev/null +++ b/Documentation/nvme-micron-selective-download.html @@ -0,0 +1,874 @@ + + + + + + +nvme-micron-selective-download(1) + + + + + +
+
+

NAME

+
+

nvme-micron-selective-download - Performs selective firmware download that allows user +to select which firmware binary to update for 9200 devices. This requires power cycle +once the update completes.

+
+
+
+

SYNOPSIS

+
+
+
nvme micron selective-download <device> [--fw=<FILE>, -f <FILE>] [--select=<flag>, -s <flag>]
+
+
+
+
+
+

DESCRIPTION

+
+

This command uses micron vendor specific nvme commands to download given firmware image to the +specified 9200 device to update selected or all portions of firmware image.

+

The <device> parameter is mandatory and may be either the NVMe +character device (ex: /dev/nvme0), or a namespace block device (ex: +/dev/nvme0n1).

+

This will only work on Micron devices devices of model number 9200. Support for new devices +may be added subsequently. Results for any other device are undefined.

+
+
+
+

OPTIONS

+
+
+
+-f <FILE> +
+
+--fw=<FILE> +
+
+

+ name of the firmware image file +

+
+
+-s <flag> +
+
+--select=<flag> +
+
+

+ flag that has following values +

+
+
+OOB +
+
+

+This updates the OOB and main firmware\n" +

+
+
+EEP +
+
+

+This updates the eeprom and main firmware\n" +

+
+
+ALL +
+
+

+This updates the eeprom, OOB, and main firmware"; +

+
+
+
+
+
+

EXAMPLES

+
+
    +
  • +

    +Update OOB and main firmware +

    +
    +
    +
    # nvme micron selective-download /dev/nvme0 --fw=firmware_bin --select=OOB
    +# nvme micron selective-download /dev/nvme0 -f firmware_bin -s OOB
    +
    +
  • +
  • +

    +Update OOB and main firmware +

    +
    +
    +
    # nvme micron selective-download /dev/nvme0 --fw=firmware_bin --select=EEP
    +# nvme micron selective-download /dev/nvme0 -f firmware_bin --s EEP
    +
    +
  • +
  • +

    +Update eeprom, OOB and main firmware +

    +
    +
    +
    # nvme micron selective-download /dev/nvme0 --fw=firmware_bin --select=ALL
    +# nvme micron selective-download /dev/nvme0 -f firmware_bin --s ALL
    +
    +
  • +
+
+
+
+

NVME

+
+

Part of the nvme-user suite

+
+
+
+

+ + + diff --git a/Documentation/nvme-micron-selective-download.txt b/Documentation/nvme-micron-selective-download.txt new file mode 100644 index 0000000..5fb11d7 --- /dev/null +++ b/Documentation/nvme-micron-selective-download.txt @@ -0,0 +1,63 @@ +nvme-micron-selective-download(1) +================================= + +NAME +---- +nvme-micron-selective-download - Performs selective firmware download that allows user +to select which firmware binary to update for 9200 devices. This requires power cycle +once the update completes. + +SYNOPSIS +-------- +[verse] +'nvme micron selective-download' [--fw=, -f ] [--select=, -s ] + +DESCRIPTION +----------- +This command uses micron vendor specific nvme commands to download given firmware image to the +specified 9200 device to update selected or all portions of firmware image. + +The parameter is mandatory and may be either the NVMe +character device (ex: /dev/nvme0), or a namespace block device (ex: +/dev/nvme0n1). + +This will only work on Micron devices devices of model number 9200. Support for new devices +may be added subsequently. Results for any other device are undefined. + +OPTIONS +------- +-f :: +--fw=:: + name of the firmware image file +-s :: +--select=:: + flag that has following values + OOB:: This updates the OOB and main firmware\n" + EEP:: This updates the eeprom and main firmware\n" + ALL:: This updates the eeprom, OOB, and main firmware"; + +EXAMPLES +-------- +* Update OOB and main firmware ++ +------------ +# nvme micron selective-download /dev/nvme0 --fw=firmware_bin --select=OOB +# nvme micron selective-download /dev/nvme0 -f firmware_bin -s OOB + +------------ +* Update OOB and main firmware ++ +------------ +# nvme micron selective-download /dev/nvme0 --fw=firmware_bin --select=EEP +# nvme micron selective-download /dev/nvme0 -f firmware_bin --s EEP +------------ +* Update eeprom, OOB and main firmware ++ +------------ +# nvme micron selective-download /dev/nvme0 --fw=firmware_bin --select=ALL +# nvme micron selective-download /dev/nvme0 -f firmware_bin --s ALL +------------ + +NVME +---- +Part of the nvme-user suite diff --git a/Documentation/nvme-micron-temperature-stats.1 b/Documentation/nvme-micron-temperature-stats.1 new file mode 100644 index 0000000..71297d1 --- /dev/null +++ b/Documentation/nvme-micron-temperature-stats.1 @@ -0,0 +1,71 @@ +'\" t +.\" Title: nvme-micron-temperarature-stats +.\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] +.\" Generator: DocBook XSL Stylesheets vsnapshot +.\" Date: 10/20/2020 +.\" Manual: NVMe Manual +.\" Source: NVMe +.\" Language: English +.\" +.TH "NVME\-MICRON\-TEMPER" "1" "10/20/2020" "NVMe" "NVMe Manual" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +nvme-micron-temperature-stats \- Retrieves temperature information of given micron device +.SH "SYNOPSIS" +.sp +.nf +\fInvme micron vs\-temperature\-stats\fR +.fi +.SH "DESCRIPTION" +.sp +This command prints temperature information (composite temperature and number of active temperature sensors) for the given micron device\&. +.sp +The parameter is mandatory and may be either the NVMe character device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1)\&. +.sp +This will only work on Micron devices devices of model numbers 54XX\&. Support for new devices may be added subsequently\&. +.SH "OPTIONS" +.sp +None +.SH "EXAMPLES" +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Retrieve temperature information +.sp +.if n \{\ +.RS 4 +.\} +.nf +# nvme micron vs\-temperature\-stats /dev/nvme0 +.fi +.if n \{\ +.RE +.\} +.RE +.SH "NVME" +.sp +Part of the nvme\-user suite diff --git a/Documentation/nvme-micron-temperature-stats.html b/Documentation/nvme-micron-temperature-stats.html new file mode 100644 index 0000000..6701cc2 --- /dev/null +++ b/Documentation/nvme-micron-temperature-stats.html @@ -0,0 +1,804 @@ + + + + + + +nvme-micron-temperarature-stats(1) + + + + + +
+
+

NAME

+
+

nvme-micron-temperature-stats - Retrieves temperature information of given micron device

+
+
+
+

SYNOPSIS

+
+
+
nvme micron vs-temperature-stats <device>
+
+
+
+
+
+

DESCRIPTION

+
+

This command prints temperature information (composite temperature and number of active +temperature sensors) for the given micron device.

+

The <device> parameter is mandatory and may be either the NVMe +character device (ex: /dev/nvme0), or a namespace block device (ex: +/dev/nvme0n1).

+

This will only work on Micron devices devices of model numbers 54XX. Support for new +devices may be added subsequently.

+
+
+
+

OPTIONS

+
+

None

+
+
+
+

EXAMPLES

+
+
    +
  • +

    +Retrieve temperature information +

    +
    +
    +
    # nvme micron vs-temperature-stats /dev/nvme0
    +
    +
  • +
+
+
+
+

NVME

+
+

Part of the nvme-user suite

+
+
+
+

+ + + diff --git a/Documentation/nvme-micron-temperature-stats.txt b/Documentation/nvme-micron-temperature-stats.txt new file mode 100644 index 0000000..43061c0 --- /dev/null +++ b/Documentation/nvme-micron-temperature-stats.txt @@ -0,0 +1,40 @@ +nvme-micron-temperarature-stats(1) +================================== + +NAME +---- +nvme-micron-temperature-stats - Retrieves temperature information of given micron device + +SYNOPSIS +-------- +[verse] +'nvme micron vs-temperature-stats' + +DESCRIPTION +----------- +This command prints temperature information (composite temperature and number of active +temperature sensors) for the given micron device. + +The parameter is mandatory and may be either the NVMe +character device (ex: /dev/nvme0), or a namespace block device (ex: +/dev/nvme0n1). + +This will only work on Micron devices devices of model numbers 54XX. Support for new +devices may be added subsequently. + +OPTIONS +------- +None + +EXAMPLES +-------- +* Retrieve temperature information ++ +------------ +# nvme micron vs-temperature-stats /dev/nvme0 + +------------ + +NVME +---- +Part of the nvme-user suite diff --git a/Documentation/nvme-netapp-ontapdevices.1 b/Documentation/nvme-netapp-ontapdevices.1 index 83bc5cb..7a0fdc0 100644 --- a/Documentation/nvme-netapp-ontapdevices.1 +++ b/Documentation/nvme-netapp-ontapdevices.1 @@ -2,12 +2,12 @@ .\" Title: nvme-netapp-ontapdevices .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-NETAPP\-ONTAPD" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-NETAPP\-ONTAPD" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff --git a/Documentation/nvme-netapp-smdevices.1 b/Documentation/nvme-netapp-smdevices.1 index 78622ea..b0d052a 100644 --- a/Documentation/nvme-netapp-smdevices.1 +++ b/Documentation/nvme-netapp-smdevices.1 @@ -2,12 +2,12 @@ .\" Title: nvme-netapp-smdevices .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-NETAPP\-SMDEVI" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-NETAPP\-SMDEVI" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff --git a/Documentation/nvme-ns-descs.1 b/Documentation/nvme-ns-descs.1 index cddc126..907a081 100644 --- a/Documentation/nvme-ns-descs.1 +++ b/Documentation/nvme-ns-descs.1 @@ -2,12 +2,12 @@ .\" Title: nvme-ns-descs .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-NS\-DESCS" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-NS\-DESCS" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff --git a/Documentation/nvme-ns-rescan.1 b/Documentation/nvme-ns-rescan.1 index 5fcf6e8..8be4772 100644 --- a/Documentation/nvme-ns-rescan.1 +++ b/Documentation/nvme-ns-rescan.1 @@ -2,12 +2,12 @@ .\" Title: nvme-ns-rescan .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-NS\-RESCAN" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-NS\-RESCAN" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff --git a/Documentation/nvme-ns-rescan.html b/Documentation/nvme-ns-rescan.html index f4b0346..5e94762 100644 --- a/Documentation/nvme-ns-rescan.html +++ b/Documentation/nvme-ns-rescan.html @@ -1,455 +1,801 @@ - - - - - -nvme-ns-rescan(1) - - - - -

SYNOPSIS

-
-
-
nvme ns-rescan <device>
-
-
-
-

DESCRIPTION

-
-

Requests NVMe controller rescans the namespaces. The <device> param is mandatory and must -be an NVMe character device (ex: /dev/nvme0).

-
-

OPTIONS

-
-

None

-
-

EXAMPLES

-
-
    -
  • -

    -Rescans the nvme namespaces. -

    -
    -
    -
    # nvme ns-rescan /dev/nvme0
    -
    -
  • -
-
-

NVME

-
-

Part of the nvme-user suite

-
- - - + + + + + + +nvme-ns-rescan(1) + + + + + +
+
+

SYNOPSIS

+
+
+
nvme ns-rescan <device>
+
+
+
+
+
+

DESCRIPTION

+
+

Requests NVMe controller rescans the namespaces. The <device> param is mandatory and must +be an NVMe character device (ex: /dev/nvme0).

+
+
+
+

OPTIONS

+
+

None

+
+
+
+

EXAMPLES

+
+
    +
  • +

    +Rescans the nvme namespaces. +

    +
    +
    +
    # nvme ns-rescan /dev/nvme0
    +
    +
  • +
+
+
+
+

NVME

+
+

Part of the nvme-user suite

+
+
+
+

+ + + diff --git a/Documentation/nvme-nvm-id-ctrl.1 b/Documentation/nvme-nvm-id-ctrl.1 new file mode 100644 index 0000000..4d1c634 --- /dev/null +++ b/Documentation/nvme-nvm-id-ctrl.1 @@ -0,0 +1,98 @@ +'\" t +.\" Title: nvme-nvm-id-ctrl +.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] +.\" Generator: DocBook XSL Stylesheets v1.79.1 +.\" Date: 02/18/2021 +.\" Manual: NVMe Manual +.\" Source: NVMe +.\" Language: English +.\" +.TH "NVME\-NVM\-ID\-CTRL" "1" "02/18/2021" "NVMe" "NVMe Manual" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +nvme-nvm-id-ctrl \- Send NVMe Identify Controller, return NVM command set structure +.SH "SYNOPSIS" +.sp +.nf +\fInvme nvm\-id\-ctrl\fR [\-o | \-\-output\-format=] +.fi +.SH "DESCRIPTION" +.sp +For the NVMe device given, sends the NVM command set\(cqs identify controller command and provides the result and returned structure\&. +.sp +The parameter is mandatory and may be either the NVMe character device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1)\&. +.sp +On success, the data structure returned by the device will be decoded and displayed in one of several ways\&. +.SH "OPTIONS" +.PP +\-o , \-\-output\-format= +.RS 4 +Set the reporting format to +\fInormal\fR, +\fIjson\fR, or +\fIbinary\fR\&. Only one output format can be used at a time\&. +.RE +.SH "EXAMPLES" +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Has the program interpret the returned buffer and display the known fields in a human readable format: +.sp +.if n \{\ +.RS 4 +.\} +.nf +# nvme nvm\-id\-ctrl /dev/nvme0 +.fi +.if n \{\ +.RE +.\} +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Show the output in json format +.sp +.if n \{\ +.RS 4 +.\} +.nf +# nvme nvm\-id\-ctrl /dev/nvme0 \-o json +.fi +.if n \{\ +.RE +.\} +.RE +.SH "NVME" +.sp +Part of nvme\-cli diff --git a/Documentation/nvme-nvm-id-ctrl.html b/Documentation/nvme-nvm-id-ctrl.html new file mode 100644 index 0000000..fa08be0 --- /dev/null +++ b/Documentation/nvme-nvm-id-ctrl.html @@ -0,0 +1,828 @@ + + + + + + +nvme-nvm-id-ctrl(1) + + + + + +
+
+

SYNOPSIS

+
+
+
nvme nvm-id-ctrl <device> [-o <fmt> | --output-format=<fmt>]
+
+
+
+
+
+

DESCRIPTION

+
+

For the NVMe device given, sends the NVM command set’s identify controller +command and provides the result and returned structure.

+

The <device> parameter is mandatory and may be either the NVMe character +device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1).

+

On success, the data structure returned by the device will be decoded and +displayed in one of several ways.

+
+
+
+

OPTIONS

+
+
+
+-o <format> +
+
+--output-format=<format> +
+
+

+ Set the reporting format to normal, json, or + binary. Only one output format can be used at a time. +

+
+
+
+
+
+

EXAMPLES

+
+
    +
  • +

    +Has the program interpret the returned buffer and display the known +fields in a human readable format: +

    +
    +
    +
    # nvme nvm-id-ctrl /dev/nvme0
    +
    +
  • +
  • +

    +Show the output in json format +

    +
    +
    +
    # nvme nvm-id-ctrl /dev/nvme0 -o json
    +
    +
  • +
+
+
+
+

NVME

+
+

Part of nvme-cli

+
+
+
+

+ + + diff --git a/Documentation/nvme-nvm-id-ctrl.txt b/Documentation/nvme-nvm-id-ctrl.txt new file mode 100644 index 0000000..fb4a237 --- /dev/null +++ b/Documentation/nvme-nvm-id-ctrl.txt @@ -0,0 +1,49 @@ +nvme-nvm-id-ctrl(1) +=================== + +NAME +---- +nvme-nvm-id-ctrl - Send NVMe Identify Controller, return NVM command set structure + +SYNOPSIS +-------- +[verse] +'nvme nvm-id-ctrl' [-o | --output-format=] + +DESCRIPTION +----------- +For the NVMe device given, sends the NVM command set's identify controller +command and provides the result and returned structure. + +The parameter is mandatory and may be either the NVMe character +device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1). + +On success, the data structure returned by the device will be decoded and +displayed in one of several ways. + +OPTIONS +------- +-o :: +--output-format=:: + Set the reporting format to 'normal', 'json', or + 'binary'. Only one output format can be used at a time. + +EXAMPLES +-------- +* Has the program interpret the returned buffer and display the known +fields in a human readable format: ++ +------------ +# nvme nvm-id-ctrl /dev/nvme0 +------------ ++ + +* Show the output in json format ++ +------------ +# nvme nvm-id-ctrl /dev/nvme0 -o json +------------ + +NVME +---- +Part of nvme-cli diff --git a/Documentation/nvme-persistent-event-log.1 b/Documentation/nvme-persistent-event-log.1 new file mode 100644 index 0000000..5d74346 --- /dev/null +++ b/Documentation/nvme-persistent-event-log.1 @@ -0,0 +1,118 @@ +'\" t +.\" Title: persistent-event-log +.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] +.\" Generator: DocBook XSL Stylesheets v1.79.1 +.\" Date: 01/11/2021 +.\" Manual: NVMe Manual +.\" Source: NVMe +.\" Language: English +.\" +.TH "PERSISTENT\-EVENT\-L" "1" "01/11/2021" "NVMe" "NVMe Manual" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +nvme-persistent-event-log \- Send NVMe persistent event log page request, returns result and log +.SH "SYNOPSIS" +.sp +.nf +\fInvme persistent\-event\-log\fR [\-\-action= | \-a ] + [\-\-log\-len= | \-l ] + [\-\-raw\-binary | \-b] + [\-\-output\-format= | \-o ] +.fi +.SH "DESCRIPTION" +.sp +Retrieves the NVMe persistent event log page from an NVMe device and provides the returned structure\&. +.sp +The parameter is mandatory and may be either the NVMe character device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1)\&. +.sp +On success, the returned persistent event log structure may be returned in one of several ways depending on the option flags; the structure may parsed by the program and printed in a readable format or the raw buffer may be printed to stdout for another program to parse\&. +.SH "OPTIONS" +.PP +\-a , \-\-action= +.RS 4 +While try to retrieve this log action the controller shall take during processing this persistent log page command\&. This mandatory field, based on the value issued it may Read Log Data, Establish Context and Read Log Data or Release Context can occur\&. For More details see NVM Express 1\&.4 Spec\&. Section 5\&.14\&.1\&.13 Persistent Event Log (Log Identifier 0Dh) +.RE +.PP +\-l , \-\-log\-len= +.RS 4 +Allocates a buffer of bytes size and requests this many bytes be returned in the constructed NVMe command\&. This param is mandatory\&. If given is 0 and action is 0, it will read the Total Log Length(TLL) of the page\&. +.RE +.PP +\-b, \-\-raw\-binary +.RS 4 +Print the raw persistent event log buffer to stdout\&. +.RE +.PP +\-o , \-\-output\-format= +.RS 4 +Set the reporting format to +\fInormal\fR, +\fIjson\fR, or +\fIbinary\fR\&. Only one output format can be used at a time\&. +.RE +.SH "EXAMPLES" +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Print the persistent event log page in a human readable format: +.sp +.if n \{\ +.RS 4 +.\} +.nf +# nvme persistent\-event\-log /dev/nvme0 +.fi +.if n \{\ +.RE +.\} +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Print the raw persistent event log to a file: +.sp +.if n \{\ +.RS 4 +.\} +.nf +# nvme persistent\-event\-log /dev/nvme0 \-\-raw\-binary > persistent_log\&.raw +.fi +.if n \{\ +.RE +.\} +.sp +It is probably a bad idea to not redirect stdout when using this mode\&. +.RE +.SH "NVME" +.sp +Part of the nvme\-user suite diff --git a/Documentation/nvme-persistent-event-log.html b/Documentation/nvme-persistent-event-log.html new file mode 100644 index 0000000..44a6499 --- /dev/null +++ b/Documentation/nvme-persistent-event-log.html @@ -0,0 +1,874 @@ + + + + + + +persistent-event-log(1) + + + + + +
+
+

SYNOPSIS

+
+
+
nvme persistent-event-log <device> [--action=<action> | -a <action>]
+            [--log-len=<log-len> | -l <log-len>]
+            [--raw-binary | -b]
+            [--output-format=<fmt> | -o <fmt>]
+
+
+
+
+
+

DESCRIPTION

+
+

Retrieves the NVMe persistent event log page from an NVMe device +and provides the returned structure.

+

The <device> parameter is mandatory and may be either the NVMe character +device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1).

+

On success, the returned persistent event log structure may be returned +in one of several ways depending on the option flags; the structure may +parsed by the program and printed in a readable format or the raw buffer +may be printed to stdout for another program to parse.

+
+
+
+

OPTIONS

+
+
+
+-a <action> +
+
+--action=<action> +
+
+

+ While try to retrieve this log action the controller shall take + during processing this persistent log page command. This mandatory + field, based on the value issued it may Read Log Data, Establish + Context and Read Log Data or Release Context can occur. For More + details see NVM Express 1.4 Spec. Section 5.14.1.13 Persistent + Event Log (Log Identifier 0Dh) +

+
+
+-l <log-len> +
+
+--log-len=<log-len> +
+
+

+ Allocates a buffer of <log-len> bytes size and requests this + many bytes be returned in the constructed NVMe command. This + param is mandatory. If <log-len> given is 0 and action is 0, + it will read the Total Log Length(TLL) of the page. +

+
+
+-b +
+
+--raw-binary +
+
+

+ Print the raw persistent event log buffer to stdout. +

+
+
+-o <format> +
+
+--output-format=<format> +
+
+

+ Set the reporting format to normal, json, or binary. + Only one output format can be used at a time. +

+
+
+
+
+
+

EXAMPLES

+
+
    +
  • +

    +Print the persistent event log page in a human readable format: +

    +
    +
    +
    # nvme persistent-event-log /dev/nvme0
    +
    +
  • +
  • +

    +Print the raw persistent event log to a file: +

    +
    +
    +
    # nvme persistent-event-log /dev/nvme0 --raw-binary > persistent_log.raw
    +
    +

    It is probably a bad idea to not redirect stdout when using this mode.

    +
  • +
+
+
+
+

NVME

+
+

Part of the nvme-user suite

+
+
+
+

+ + + diff --git a/Documentation/nvme-persistent-event-log.txt b/Documentation/nvme-persistent-event-log.txt new file mode 100644 index 0000000..833491f --- /dev/null +++ b/Documentation/nvme-persistent-event-log.txt @@ -0,0 +1,76 @@ +persistent-event-log(1) +======================= + +NAME +---- +nvme-persistent-event-log - Send NVMe persistent event log page request, +returns result and log + +SYNOPSIS +-------- +[verse] +'nvme persistent-event-log' [--action= | -a ] + [--log-len= | -l ] + [--raw-binary | -b] + [--output-format= | -o ] + +DESCRIPTION +----------- +Retrieves the NVMe persistent event log page from an NVMe device +and provides the returned structure. + +The parameter is mandatory and may be either the NVMe character +device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1). + +On success, the returned persistent event log structure may be returned +in one of several ways depending on the option flags; the structure may +parsed by the program and printed in a readable format or the raw buffer +may be printed to stdout for another program to parse. + +OPTIONS +------- +-a :: +--action=:: + While try to retrieve this log action the controller shall take + during processing this persistent log page command. This mandatory + field, based on the value issued it may Read Log Data, Establish + Context and Read Log Data or Release Context can occur. For More + details see NVM Express 1.4 Spec. Section 5.14.1.13 Persistent + Event Log (Log Identifier 0Dh) + +-l :: +--log-len=:: + Allocates a buffer of bytes size and requests this + many bytes be returned in the constructed NVMe command. This + param is mandatory. If given is 0 and action is 0, + it will read the Total Log Length(TLL) of the page. + +-b:: +--raw-binary:: + Print the raw persistent event log buffer to stdout. + +-o :: +--output-format=:: + Set the reporting format to 'normal', 'json', or 'binary'. + Only one output format can be used at a time. + +EXAMPLES +-------- +* Print the persistent event log page in a human readable format: ++ +------------ +# nvme persistent-event-log /dev/nvme0 +------------ ++ + +* Print the raw persistent event log to a file: ++ +------------ +# nvme persistent-event-log /dev/nvme0 --raw-binary > persistent_log.raw +------------ ++ +It is probably a bad idea to not redirect stdout when using this mode. + +NVME +---- +Part of the nvme-user suite diff --git a/Documentation/nvme-pred-lat-event-agg-log.1 b/Documentation/nvme-pred-lat-event-agg-log.1 new file mode 100644 index 0000000..69785cb --- /dev/null +++ b/Documentation/nvme-pred-lat-event-agg-log.1 @@ -0,0 +1,117 @@ +'\" t +.\" Title: nvme-pred-lat-event-agg-log +.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] +.\" Generator: DocBook XSL Stylesheets v1.79.1 +.\" Date: 01/12/2021 +.\" Manual: NVMe Manual +.\" Source: NVMe +.\" Language: English +.\" +.TH "NVME\-PRED\-LAT\-EVE" "1" "01/12/2021" "NVMe" "NVMe Manual" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +nvme-pred-lat-event-agg-log \- Send Predictable Latency Event Aggregate Log Page request, returns result and log +.SH "SYNOPSIS" +.sp +.nf +\fInvme pred\-lat\-event\-agg\-log\fR [\-\-log\-entries= | \-e ] + [\-\-rae | \-r] [\-\-raw\-binary | \-b] + [\-\-output\-format= | \-o ] +.fi +.SH "DESCRIPTION" +.sp +Retrieves the NVMe Predictable Latency Event Aggregate Log Page from an NVMe device and provides the returned structure\&. +.sp +The parameter is mandatory and may be either the NVMe character device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1)\&. +.sp +On success, the returned Predictable Latency Event Aggregate Log Page structure may be returned in one ofseveral ways depending on the option flags; the structure may parsed by the program and printed in a readable format or the raw buffer may be printed to stdout for another program to parse\&. +.SH "OPTIONS" +.PP +\-e , \-\-log\-entries= +.RS 4 +Retrieve the Predictable Latency Event Aggregate Log pending entries\&. This argument is mandatory and its success may depend on the device\(cqs statistics to provide this log For More details see NVM Express 1\&.4 Spec\&. Section 5\&.14\&.1\&.11\&. The maximum number of log entries supported is 2044 for the device\&. +.RE +.PP +\-r, \-\-rae +.RS 4 +Retain an Asynchronous Event\&. +.RE +.PP +\-b, \-\-raw\-binary +.RS 4 +Print the raw Predectible Latency Event Aggregate log buffer to stdout\&. +.RE +.PP +\-o , \-\-output\-format= +.RS 4 +Set the reporting format to +\fInormal\fR, +\fIjson\fR, or +\fIbinary\fR\&. Only one output format can be used at a time\&. +.RE +.SH "EXAMPLES" +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Print the Predictable Latency Event Aggregate Log page in a human readable format: +.sp +.if n \{\ +.RS 4 +.\} +.nf +# nvme pred\-lat\-event\-agg\-log /dev/nvme0 +.fi +.if n \{\ +.RE +.\} +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Print the raw Predectible Latency Event Aggregate log to a file: +.sp +.if n \{\ +.RS 4 +.\} +.nf +# nvme pred\-lat\-event\-agg\-log /dev/nvme0 \-\-raw\-binary > pred_lat_agg_log\&.raw +.fi +.if n \{\ +.RE +.\} +.sp +It is probably a bad idea to not redirect stdout when using this mode\&. +.RE +.SH "NVME" +.sp +Part of the nvme\-user suite diff --git a/Documentation/nvme-pred-lat-event-agg-log.html b/Documentation/nvme-pred-lat-event-agg-log.html new file mode 100644 index 0000000..42c370f --- /dev/null +++ b/Documentation/nvme-pred-lat-event-agg-log.html @@ -0,0 +1,870 @@ + + + + + + +nvme-pred-lat-event-agg-log(1) + + + + + +
+
+

SYNOPSIS

+
+
+
nvme pred-lat-event-agg-log <device> [--log-entries=<log_entries> | -e <log_entries>]
+                        [--rae | -r] [--raw-binary | -b]
+                        [--output-format=<fmt> | -o <fmt>]
+
+
+
+
+
+

DESCRIPTION

+
+

Retrieves the NVMe Predictable Latency Event Aggregate Log Page from an +NVMe device and provides the returned structure.

+

The <device> parameter is mandatory and may be either the NVMe character +device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1).

+

On success, the returned Predictable Latency Event Aggregate Log +Page structure may be returned in one ofseveral ways depending on +the option flags; the structure may parsed by the program and printed +in a readable format or the raw buffer may be printed to stdout for +another program to parse.

+
+
+
+

OPTIONS

+
+
+
+-e <log_entries> +
+
+--log-entries=<log_entries> +
+
+

+ Retrieve the Predictable Latency Event Aggregate Log pending entries. + This argument is mandatory and its success may depend on the device’s + statistics to provide this log For More details see NVM Express 1.4 Spec. + Section 5.14.1.11. The maximum number of log entries supported is 2044 + for the device. +

+
+
+-r +
+
+--rae +
+
+

+ Retain an Asynchronous Event. +

+
+
+-b +
+
+--raw-binary +
+
+

+ Print the raw Predectible Latency Event Aggregate log buffer to stdout. +

+
+
+-o <format> +
+
+--output-format=<format> +
+
+

+ Set the reporting format to normal, json, or binary. + Only one output format can be used at a time. +

+
+
+
+
+
+

EXAMPLES

+
+
    +
  • +

    +Print the Predictable Latency Event Aggregate Log page in a human readable format: +

    +
    +
    +
    # nvme pred-lat-event-agg-log /dev/nvme0
    +
    +
  • +
  • +

    +Print the raw Predectible Latency Event Aggregate log to a file: +

    +
    +
    +
    # nvme pred-lat-event-agg-log /dev/nvme0 --raw-binary > pred_lat_agg_log.raw
    +
    +

    It is probably a bad idea to not redirect stdout when using this mode.

    +
  • +
+
+
+
+

NVME

+
+

Part of the nvme-user suite

+
+
+
+

+ + + diff --git a/Documentation/nvme-pred-lat-event-agg-log.txt b/Documentation/nvme-pred-lat-event-agg-log.txt new file mode 100644 index 0000000..d7d998b --- /dev/null +++ b/Documentation/nvme-pred-lat-event-agg-log.txt @@ -0,0 +1,72 @@ +nvme-pred-lat-event-agg-log(1) +============================== + +NAME +---- +nvme-pred-lat-event-agg-log - Send Predictable Latency Event Aggregate Log +Page request, returns result and log + +SYNOPSIS +-------- +[verse] +'nvme pred-lat-event-agg-log' [--log-entries= | -e ] + [--rae | -r] [--raw-binary | -b] + [--output-format= | -o ] + +DESCRIPTION +----------- +Retrieves the NVMe Predictable Latency Event Aggregate Log Page from an +NVMe device and provides the returned structure. + +The parameter is mandatory and may be either the NVMe character +device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1). + +On success, the returned Predictable Latency Event Aggregate Log +Page structure may be returned in one ofseveral ways depending on +the option flags; the structure may parsed by the program and printed +in a readable format or the raw buffer may be printed to stdout for +another program to parse. + +OPTIONS +------- +-e :: +--log-entries=:: + Retrieve the Predictable Latency Event Aggregate Log pending entries. + This argument is mandatory and its success may depend on the device's + statistics to provide this log For More details see NVM Express 1.4 Spec. + Section 5.14.1.11. The maximum number of log entries supported is 2044 + for the device. + +-r:: +--rae:: + Retain an Asynchronous Event. + +-b:: +--raw-binary:: + Print the raw Predectible Latency Event Aggregate log buffer to stdout. + +-o :: +--output-format=:: + Set the reporting format to 'normal', 'json', or 'binary'. + Only one output format can be used at a time. + +EXAMPLES +-------- +* Print the Predictable Latency Event Aggregate Log page in a human readable format: ++ +------------ +# nvme pred-lat-event-agg-log /dev/nvme0 +------------ ++ + +* Print the raw Predectible Latency Event Aggregate log to a file: ++ +------------ +# nvme pred-lat-event-agg-log /dev/nvme0 --raw-binary > pred_lat_agg_log.raw +------------ ++ +It is probably a bad idea to not redirect stdout when using this mode. + +NVME +---- +Part of the nvme-user suite diff --git a/Documentation/nvme-predictable-lat-log.1 b/Documentation/nvme-predictable-lat-log.1 new file mode 100644 index 0000000..41dd16c --- /dev/null +++ b/Documentation/nvme-predictable-lat-log.1 @@ -0,0 +1,112 @@ +'\" t +.\" Title: nvme-predictable-lat-log +.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] +.\" Generator: DocBook XSL Stylesheets v1.79.1 +.\" Date: 01/13/2021 +.\" Manual: NVMe Manual +.\" Source: NVMe +.\" Language: English +.\" +.TH "NVME\-PREDICTABLE\-L" "1" "01/13/2021" "NVMe" "NVMe Manual" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +nvme-predictable-lat-log \- Send Predectible latency per NVM set log page request, returns result and log +.SH "SYNOPSIS" +.sp +.nf +\fInvme predictable\-lat\-log\fR [\-\-nvmset\-id= | \-i ] + [\-\-raw\-binary | \-b] + [\-\-output\-format= | \-o ] +.fi +.SH "DESCRIPTION" +.sp +Retrieves the NVMe Predectible latency per NVM set log page from an NVMe device and provides the returned structure\&. +.sp +The parameter is mandatory and may be either the NVMe character device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1)\&. +.sp +On success, the returned Predectible latency per NVM set log structure may be returned in one ofseveral ways depending on the option flags; the structure may parsed by the program and printed in a readable format or the raw buffer may be printed to stdout for another program to parse\&. +.SH "OPTIONS" +.PP +\-i , \-\-nvmset\-id= +.RS 4 +Retrieve the Predectible latency per NVM set log for the given nvmset id\&. This argument is mandatory and its success may depend on the device\(cqs statistics to provide this log For More details see NVM Express 1\&.4 Spec\&. Section 5\&.14\&.1\&.10\&. The default nvmset id to use is 1 for the device\&. +.RE +.PP +\-b, \-\-raw\-binary +.RS 4 +Print the raw Predectible latency per NVM set log buffer to stdout\&. +.RE +.PP +\-o , \-\-output\-format= +.RS 4 +Set the reporting format to +\fInormal\fR, +\fIjson\fR, or +\fIbinary\fR\&. Only one output format can be used at a time\&. +.RE +.SH "EXAMPLES" +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Print the Predectible latency per NVM set log page in a human readable format: +.sp +.if n \{\ +.RS 4 +.\} +.nf +# nvme predictable\-lat\-log /dev/nvme0 +.fi +.if n \{\ +.RE +.\} +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Print the raw Predectible latency per NVM set log to a file: +.sp +.if n \{\ +.RS 4 +.\} +.nf +# nvme predictable\-lat\-log /dev/nvme0 \-\-raw\-binary > nvmset_log\&.raw +.fi +.if n \{\ +.RE +.\} +.sp +It is probably a bad idea to not redirect stdout when using this mode\&. +.RE +.SH "NVME" +.sp +Part of the nvme\-user suite diff --git a/Documentation/nvme-predictable-lat-log.html b/Documentation/nvme-predictable-lat-log.html new file mode 100644 index 0000000..42dfda8 --- /dev/null +++ b/Documentation/nvme-predictable-lat-log.html @@ -0,0 +1,857 @@ + + + + + + +nvme-predictable-lat-log(1) + + + + + +
+
+

SYNOPSIS

+
+
+
nvme predictable-lat-log <device> [--nvmset-id=<nvmset_id> | -i <nvmset_id>]
+                        [--raw-binary | -b]
+                        [--output-format=<fmt> | -o <fmt>]
+
+
+
+
+
+

DESCRIPTION

+
+

Retrieves the NVMe Predectible latency per NVM set log page from an NVMe device +and provides the returned structure.

+

The <device> parameter is mandatory and may be either the NVMe character +device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1).

+

On success, the returned Predectible latency per NVM set log structure +may be returned in one ofseveral ways depending on the option flags; the +structure may parsed by the program and printed in a readable format or +the raw buffer may be printed to stdout for another program to parse.

+
+
+
+

OPTIONS

+
+
+
+-i <nvmset_id> +
+
+--nvmset-id=<nvmset_id> +
+
+

+ Retrieve the Predectible latency per NVM set log for the given nvmset id. + This argument is mandatory and its success may depend on the device’s + statistics to provide this log For More details see NVM Express 1.4 Spec. + Section 5.14.1.10. The default nvmset id to use is 1 for the device. +

+
+
+-b +
+
+--raw-binary +
+
+

+ Print the raw Predectible latency per NVM set log buffer to stdout. +

+
+
+-o <format> +
+
+--output-format=<format> +
+
+

+ Set the reporting format to normal, json, or + binary. Only one output format can be used at a time. +

+
+
+
+
+
+

EXAMPLES

+
+
    +
  • +

    +Print the Predectible latency per NVM set log page in a human readable format: +

    +
    +
    +
    # nvme predictable-lat-log /dev/nvme0
    +
    +
  • +
  • +

    +Print the raw Predectible latency per NVM set log to a file: +

    +
    +
    +
    # nvme predictable-lat-log /dev/nvme0 --raw-binary > nvmset_log.raw
    +
    +

    It is probably a bad idea to not redirect stdout when using this mode.

    +
  • +
+
+
+
+

NVME

+
+

Part of the nvme-user suite

+
+
+
+

+ + + diff --git a/Documentation/nvme-predictable-lat-log.txt b/Documentation/nvme-predictable-lat-log.txt new file mode 100644 index 0000000..b4d7335 --- /dev/null +++ b/Documentation/nvme-predictable-lat-log.txt @@ -0,0 +1,66 @@ +nvme-predictable-lat-log(1) +=========================== + +NAME +---- +nvme-predictable-lat-log - Send Predectible latency per NVM set log page request, +returns result and log + +SYNOPSIS +-------- +[verse] +'nvme predictable-lat-log' [--nvmset-id= | -i ] + [--raw-binary | -b] + [--output-format= | -o ] + +DESCRIPTION +----------- +Retrieves the NVMe Predectible latency per NVM set log page from an NVMe device +and provides the returned structure. + +The parameter is mandatory and may be either the NVMe character +device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1). + +On success, the returned Predectible latency per NVM set log structure +may be returned in one ofseveral ways depending on the option flags; the +structure may parsed by the program and printed in a readable format or +the raw buffer may be printed to stdout for another program to parse. + +OPTIONS +------- +-i :: +--nvmset-id=:: + Retrieve the Predectible latency per NVM set log for the given nvmset id. + This argument is mandatory and its success may depend on the device's + statistics to provide this log For More details see NVM Express 1.4 Spec. + Section 5.14.1.10. The default nvmset id to use is 1 for the device. + +-b:: +--raw-binary:: + Print the raw Predectible latency per NVM set log buffer to stdout. + +-o :: +--output-format=:: + Set the reporting format to 'normal', 'json', or + 'binary'. Only one output format can be used at a time. + +EXAMPLES +-------- +* Print the Predectible latency per NVM set log page in a human readable format: ++ +------------ +# nvme predictable-lat-log /dev/nvme0 +------------ ++ + +* Print the raw Predectible latency per NVM set log to a file: ++ +------------ +# nvme predictable-lat-log /dev/nvme0 --raw-binary > nvmset_log.raw +------------ ++ +It is probably a bad idea to not redirect stdout when using this mode. + +NVME +---- +Part of the nvme-user suite diff --git a/Documentation/nvme-read.1 b/Documentation/nvme-read.1 index ae7faf6..7ba8d6a 100644 --- a/Documentation/nvme-read.1 +++ b/Documentation/nvme-read.1 @@ -2,12 +2,12 @@ .\" Title: nvme-read .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-READ" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-READ" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff --git a/Documentation/nvme-reset.1 b/Documentation/nvme-reset.1 index 7b8f3de..3d153e2 100644 --- a/Documentation/nvme-reset.1 +++ b/Documentation/nvme-reset.1 @@ -2,12 +2,12 @@ .\" Title: nvme-reset .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-RESET" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-RESET" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff --git a/Documentation/nvme-resv-acquire.1 b/Documentation/nvme-resv-acquire.1 index f96d127..1088479 100644 --- a/Documentation/nvme-resv-acquire.1 +++ b/Documentation/nvme-resv-acquire.1 @@ -2,12 +2,12 @@ .\" Title: nvme-resv-acquire .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-RESV\-ACQUIRE" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-RESV\-ACQUIRE" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -59,7 +59,7 @@ Current Reservation Key: The field specifies the current reservation key associa Preempt Reservation Key: If the Reservation Acquire Action is set to 001b (i\&.e\&., Preempt) or 010b (i\&.e\&., Preempt and Abort), then this field specifies the reservation key to be unregistered from the namespace\&. For all other Reservation Acquire Action values, this field is reserved\&. .RE .PP -\-t , \-\-rtyep= +\-t , \-\-rtype= .RS 4 Reservation Type: This field specifies the type of reservation to be created\&. .TS diff --git a/Documentation/nvme-resv-acquire.html b/Documentation/nvme-resv-acquire.html index 17b3d94..fabd8fd 100644 --- a/Documentation/nvme-resv-acquire.html +++ b/Documentation/nvme-resv-acquire.html @@ -815,7 +815,7 @@ reservation held on a namespace.

-t <rtype>
---rtyep=<rtype> +--rtype=<rtype>

diff --git a/Documentation/nvme-resv-acquire.txt b/Documentation/nvme-resv-acquire.txt index 41cbf8f..1b6e110 100644 --- a/Documentation/nvme-resv-acquire.txt +++ b/Documentation/nvme-resv-acquire.txt @@ -44,7 +44,7 @@ OPTIONS this field is reserved. -t :: ---rtyep=:: +--rtype=:: Reservation Type: This field specifies the type of reservation to be created. + diff --git a/Documentation/nvme-resv-notif-log.1 b/Documentation/nvme-resv-notif-log.1 new file mode 100644 index 0000000..ec0aca2 --- /dev/null +++ b/Documentation/nvme-resv-notif-log.1 @@ -0,0 +1,98 @@ +'\" t +.\" Title: nvme-resv-notif-log +.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] +.\" Generator: DocBook XSL Stylesheets v1.79.1 +.\" Date: 03/09/2021 +.\" Manual: NVMe Manual +.\" Source: NVMe +.\" Language: English +.\" +.TH "NVME\-RESV\-NOTIF\-L" "1" "03/09/2021" "NVMe" "NVMe Manual" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +nvme-resv-notif-log \- Send NVMe Reservation Notification log page request, return result and log +.SH "SYNOPSIS" +.sp +.nf +\fInvme resv\-notif\-log\fR [\-\-output\-format= | \-o ] +.fi +.SH "DESCRIPTION" +.sp +Retrieves NVMe Reservation Notification log page from an NVMe device and provides the returned structure\&. +.sp +The parameter is mandatory and may be either the NVMe character device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1)\&. +.sp +On success, the returned Reservation Notification log structure may be returned in one of several ways depending on the option flags; the structure may parsed by the program and printed in a readable format or the raw buffer may be printed to stdout for another program to parse\&. +.SH "OPTIONS" +.PP +\-o , \-\-output\-format= +.RS 4 +Set the reporting format to +\fInormal\fR, +\fIjson\fR, or +\fIbinary\fR\&. Only one output format can be used at a time\&. +.RE +.SH "EXAMPLES" +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Get the Reservation Notification log and print it in a human readable format: +.sp +.if n \{\ +.RS 4 +.\} +.nf +# nvme resv\-notif\-log /dev/nvme0 +.fi +.if n \{\ +.RE +.\} +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Print the output in json format: +.sp +.if n \{\ +.RS 4 +.\} +.nf +# nvme resv\-notif\-log /dev/nvme0 \-o json +.fi +.if n \{\ +.RE +.\} +.RE +.SH "NVME" +.sp +Part of the nvme\-user suite diff --git a/Documentation/nvme-resv-notif-log.html b/Documentation/nvme-resv-notif-log.html new file mode 100644 index 0000000..ebac7e1 --- /dev/null +++ b/Documentation/nvme-resv-notif-log.html @@ -0,0 +1,829 @@ + + + + + + +nvme-resv-notif-log(1) + + + + +

+
+
+

SYNOPSIS

+
+
+
nvme resv-notif-log <device> [--output-format=<fmt> | -o <fmt>]
+
+
+
+
+
+

DESCRIPTION

+
+

Retrieves NVMe Reservation Notification log page from an NVMe device and +provides the returned structure.

+

The <device> parameter is mandatory and may be either the NVMe character +device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1).

+

On success, the returned Reservation Notification log structure may be +returned in one of several ways depending on the option flags; the structure +may parsed by the program and printed in a readable format or the raw buffer +may be printed to stdout for another program to parse.

+
+
+
+

OPTIONS

+
+
+
+-o <format> +
+
+--output-format=<format> +
+
+

+ Set the reporting format to normal, json, or + binary. Only one output format can be used at a time. +

+
+
+
+
+
+

EXAMPLES

+
+
    +
  • +

    +Get the Reservation Notification log and print it in a human readable format: +

    +
    +
    +
    # nvme resv-notif-log /dev/nvme0
    +
    +
  • +
  • +

    +Print the output in json format: +

    +
    +
    +
    # nvme resv-notif-log /dev/nvme0 -o json
    +
    +
  • +
+
+
+
+

NVME

+
+

Part of the nvme-user suite

+
+
+
+

+ + + diff --git a/Documentation/nvme-resv-notif-log.txt b/Documentation/nvme-resv-notif-log.txt new file mode 100644 index 0000000..a9c5cdd --- /dev/null +++ b/Documentation/nvme-resv-notif-log.txt @@ -0,0 +1,51 @@ +nvme-resv-notif-log(1) +====================== + +NAME +---- +nvme-resv-notif-log - Send NVMe Reservation Notification log page request, + return result and log + +SYNOPSIS +-------- +[verse] +'nvme resv-notif-log' [--output-format= | -o ] + +DESCRIPTION +----------- +Retrieves NVMe Reservation Notification log page from an NVMe device and +provides the returned structure. + +The parameter is mandatory and may be either the NVMe character +device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1). + +On success, the returned Reservation Notification log structure may be +returned in one of several ways depending on the option flags; the structure +may parsed by the program and printed in a readable format or the raw buffer +may be printed to stdout for another program to parse. + +OPTIONS +------- +-o :: +--output-format=:: + Set the reporting format to 'normal', 'json', or + 'binary'. Only one output format can be used at a time. + +EXAMPLES +-------- +* Get the Reservation Notification log and print it in a human readable format: ++ +------------ +# nvme resv-notif-log /dev/nvme0 +------------ ++ + +* Print the output in json format: ++ +------------ +# nvme resv-notif-log /dev/nvme0 -o json +------------ + +NVME +---- +Part of the nvme-user suite diff --git a/Documentation/nvme-resv-register.1 b/Documentation/nvme-resv-register.1 index 4a50513..48b391f 100644 --- a/Documentation/nvme-resv-register.1 +++ b/Documentation/nvme-resv-register.1 @@ -2,12 +2,12 @@ .\" Title: nvme-resv-register .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-RESV\-REGISTER" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-RESV\-REGISTER" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff --git a/Documentation/nvme-resv-release.1 b/Documentation/nvme-resv-release.1 index 1817e51..5ade7f5 100644 --- a/Documentation/nvme-resv-release.1 +++ b/Documentation/nvme-resv-release.1 @@ -2,12 +2,12 @@ .\" Title: nvme-resv-release .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-RESV\-RELEASE" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-RESV\-RELEASE" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -53,7 +53,7 @@ Override the nsid field\&. If using the admin character device, this parameter i Current Reservation Key: If the Reservation Register Action is 001b (i\&.e\&., Unregister Reservation Key) or 010b (i\&.e\&., Replace Reservation Key), then this field contains the current reservation key associated with the host\&. For all other Reservation Register Action values, this field is reserved\&. The controller ignores the value of this field when the Ignore Existing Key (IEKEY) bit is set to \(oq1\(cq\&. .RE .PP -\-t , \-\-rtyep= +\-t , \-\-rtype= .RS 4 Reservation Type: This field specifies the type of reservation to be created\&. .TS diff --git a/Documentation/nvme-resv-release.html b/Documentation/nvme-resv-release.html index 884dc78..094c89a 100644 --- a/Documentation/nvme-resv-release.html +++ b/Documentation/nvme-resv-release.html @@ -802,7 +802,7 @@ held on a namespace.

-t <rtype>
---rtyep=<rtype> +--rtype=<rtype>

diff --git a/Documentation/nvme-resv-release.txt b/Documentation/nvme-resv-release.txt index a6a895e..6eb0b43 100644 --- a/Documentation/nvme-resv-release.txt +++ b/Documentation/nvme-resv-release.txt @@ -37,7 +37,7 @@ OPTIONS bit is set to ‘1’. -t :: ---rtyep=:: +--rtype=:: Reservation Type: This field specifies the type of reservation to be created. + diff --git a/Documentation/nvme-resv-report.1 b/Documentation/nvme-resv-report.1 index 7242f3a..f9f1fa8 100644 --- a/Documentation/nvme-resv-report.1 +++ b/Documentation/nvme-resv-report.1 @@ -2,12 +2,12 @@ .\" Title: nvme-resv-report .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-RESV\-REPORT" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-RESV\-REPORT" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff --git a/Documentation/nvme-resv-report.html b/Documentation/nvme-resv-report.html index d34e6da..83655ae 100644 --- a/Documentation/nvme-resv-report.html +++ b/Documentation/nvme-resv-report.html @@ -1,9 +1,10 @@ + - + nvme-resv-report(1) + + + +

+
+
+

SYNOPSIS

+
+
+
nvme rpmb <device> [--cmd=<command> | -c <command>]
+                    [--msgfile=<data-file> | -f <data-file>]
+                    [--keyfile=<key-file> | -g <key-file>]
+                    [--key=<key> | -k <key>]
+                    [--msg=<data> | -d <data>]
+                    [--address=<offset> | -o <offset>]
+                    [--blocks=<512 byte sectors> | -b <sectors> ]
+                    [--target=<target-id> | -t <id> ]
+
+
+
+
+
+

DESCRIPTION

+
+

For the NVMe device given, send an nvme rpmb command and provide the results.

+

The <device> parameter is mandatory and NVMe character device (ex: /dev/nvme0) +must be specified. If the given device supports RPMB targets, command given +with --cmd or -c option shall be sent to the controller. If given NVMe device +doesn’t support RPMB targets, a message indicating the same shall be printed +along with controller register values related RPMB.

+
+
+
+

OPTIONS

+
+
+
+-c <command> +
+
+--cmd=<command> +
+
+

+ RPMB command to be sent to the device. It can be one of the following +

+
+
+
info          - print information regarding supported RPMB targets and
+                access and total sizes. No further arguments are required
+
+
+
+
program-key   - program 'key' specified with -k option or key read from
+                file specified with --keyfile option to the specified
+                RPMB target given with --target or -t options. As per
+                spec, this is one time action which can't be undone.
+
+
+
+
read-couter   - Read 'write counter' of specified RPMB target. The
+                counter value read is printed onto STDOUT
+
+
+
+
read-config   - Read 512 bytes of device configuration block data of
+                specified RPMB target of the NVMe device. The data read
+                is written to input file specified with --msgfile or -f
+                option.
+write-config  - Write 512 byes of device configuration block data
+                from file specified by --msgfile or -f options to the
+                RPMB target specified with --target or -t options.
+
+
+
+
read-data     - Supports authenticated data reading from specified
+                RPMB target (--target or -t option) at given offset
+                specified with --address or -o option, using key
+                specified using --keyfile or -k options. --blocks or
+                -o option should be given to read the amount of data
+                to be read in 512 byte blocks.
+
+
+
+
write-data    - Supports authenticated data writting to specified RPMB
+                target (--target or -t option) at given offset
+                specified with --address or -o option, using key
+                specified using --keyfile or -k options. --blocks or
+                -o option should be given to indicate amount of data
+                to be written in 512 byte blocks.
+
+
+
+
For data transfer (read/write) commands, if the specified size is not
+within the total size supported by a target, the request is failed
+nvme-rpmb without sending it to device. RPMB target 0 is used as the
+default target if --target or -t is not specified. 0x0 is used as the
+default address if no -address or -o option is specified,
+
+
+
+-t <target> +
+
+--target=<target> +
+
+

+ RPMB target id. This should be one of the supported RPMB targets as + reported by info command. If nothing is given, default of 0 is used + as RPMB target. +

+
+
+-k <key> +
+
+--key=<key> +
+
+-g <key-file> +
+
+--keyfile=<key-file> +
+
+

+ Authentication key to be used for read/write commands. This should have + been already programmed by program-key command for given target. Key + can be specified on command line using --key or -k options. Key can + also be specified using file argument specified with --keyfile or -g + options. +

+
+
+-f <data-file> +
+
+--msgfile=<data-file> +
+
+

+ Name of the file to be used for data transfer commands (read or write). + For read command, if an existing file is specified, it will be appended. +

+
+
+-d <data> +
+
+--msg=<data> +
+
+

+ These options provide the data on the command line itself. +

+
+
+-o <offset> +
+
+--address=<offset> +
+
+

+ The address (in 512 byte sector offset from 0) to be used for data + trasnfer commands (read or write) for a specified RPMB target. +

+
+
+-b +
+
+--blocks=<sectors> +
+
+

+ The size in 512 byte sectors to be used for data trasnfer commands + (read or write) for a specified RPMB target. +

+
+
+
+
+
+

EXAMPLES

+
+
    +
  • +

    +Print RPMB support information of an NVMe device +

    +
    +
    +
    # nvme rpmb /dev/nvme0 --cmd=info
    +
    +
  • +
  • +

    +Program SecreteKey as authentication key for target 1 +

    +
    +
    +
    # nvme rpmb /dev/nvme0 --cmd=program-key -key='SecretKey' --target=1
    +
    +
  • +
  • +

    +Read current write counter of RPMB target 0 +

    +
    +
    +
    # nvme rpmb /dev/nvme0 --cmd=read-counter --target=0
    +
    +
  • +
  • +

    +Read configuration data block of target 2 into config.bin file +

    +
    +
    +
    # nvme rpmb /dev/nvme0 --cmd=read-config --target=2 -f config.bin
    +
    +
  • +
  • +

    +Write 200 blocks of (512 bytes) from input.bin onto target 0 +

    +
    +
    +
    # nvme rpmb /dev/nvme0 -c write-data -t 0 -f input.bin -b 200 -k 'SecreteKey'
    +
    +
  • +
  • +

    +Read 200 blocks of (512 bytes) from target 2, at offset 0x100 and save the +

    +
  • +
  • +

    +data onto output.bin +

    +
    +
    +
    # nvme rpmb /dev/nvme0 -c read-data -t 2 -f out.bin -b 200 -o 0x100
    +
    +
  • +
+
+
+
+

NVME

+
+

Part of the nvme-user suite

+
+
+
+

+ + + diff --git a/Documentation/nvme-rpmb.txt b/Documentation/nvme-rpmb.txt new file mode 100644 index 0000000..f9b371a --- /dev/null +++ b/Documentation/nvme-rpmb.txt @@ -0,0 +1,150 @@ +nvme-rpmb(1) +============== + +NAME +---- +nvme-rpmb - Send RPMB commands to an NVMe device + +SYNOPSIS +-------- +[verse] +'nvme rpmb' [--cmd= | -c ] + [--msgfile= | -f ] + [--keyfile= | -g ] + [--key= | -k ] + [--msg= | -d ] + [--address= | -o ] + [--blocks=<512 byte sectors> | -b ] + [--target= | -t ] + +DESCRIPTION +----------- +For the NVMe device given, send an nvme rpmb command and provide the results. + +The parameter is mandatory and NVMe character device (ex: /dev/nvme0) +must be specified. If the given device supports RPMB targets, command given +with --cmd or -c option shall be sent to the controller. If given NVMe device +doesn't support RPMB targets, a message indicating the same shall be printed +along with controller register values related RPMB. + +OPTIONS +------- +-c :: +--cmd=:: + RPMB command to be sent to the device. It can be one of the following + + info - print information regarding supported RPMB targets and + access and total sizes. No further arguments are required + + program-key - program 'key' specified with -k option or key read from + file specified with --keyfile option to the specified + RPMB target given with --target or -t options. As per + spec, this is one time action which can't be undone. + + read-couter - Read 'write counter' of specified RPMB target. The + counter value read is printed onto STDOUT + + read-config - Read 512 bytes of device configuration block data of + specified RPMB target of the NVMe device. The data read + is written to input file specified with --msgfile or -f + option. + write-config - Write 512 byes of device configuration block data + from file specified by --msgfile or -f options to the + RPMB target specified with --target or -t options. + + read-data - Supports authenticated data reading from specified + RPMB target (--target or -t option) at given offset + specified with --address or -o option, using key + specified using --keyfile or -k options. --blocks or + -o option should be given to read the amount of data + to be read in 512 byte blocks. + + write-data - Supports authenticated data writting to specified RPMB + target (--target or -t option) at given offset + specified with --address or -o option, using key + specified using --keyfile or -k options. --blocks or + -o option should be given to indicate amount of data + to be written in 512 byte blocks. + + For data transfer (read/write) commands, if the specified size is not + within the total size supported by a target, the request is failed + nvme-rpmb without sending it to device. RPMB target 0 is used as the + default target if --target or -t is not specified. 0x0 is used as the + default address if no -address or -o option is specified, + +-t :: +--target=:: + RPMB target id. This should be one of the supported RPMB targets as + reported by 'info' command. If nothing is given, default of 0 is used + as RPMB target. + +-k :: +--key=:: +-g :: +--keyfile=:: + Authentication key to be used for read/write commands. This should have + been already programmed by 'program-key' command for given target. Key + can be specified on command line using --key or -k options. Key can + also be specified using file argument specified with --keyfile or -g + options. + +-f :: +--msgfile=:: + Name of the file to be used for data transfer commands (read or write). + For read command, if an existing file is specified, it will be appended. + +-d :: +--msg=:: + These options provide the data on the command line itself. +-o :: +--address=:: + The address (in 512 byte sector offset from 0) to be used for data + trasnfer commands (read or write) for a specified RPMB target. +-b:: +--blocks=:: + The size in 512 byte sectors to be used for data trasnfer commands + (read or write) for a specified RPMB target. + +EXAMPLES +-------- +* Print RPMB support information of an NVMe device ++ +----------- +# nvme rpmb /dev/nvme0 --cmd=info +----------- ++ +* Program 'SecreteKey' as authentication key for target 1 ++ +------------ +# nvme rpmb /dev/nvme0 --cmd=program-key -key='SecretKey' --target=1 +------------ ++ +* Read current write counter of RPMB target 0 ++ +------------ +# nvme rpmb /dev/nvme0 --cmd=read-counter --target=0 +------------ ++ +* Read configuration data block of target 2 into config.bin file ++ +------------ +# nvme rpmb /dev/nvme0 --cmd=read-config --target=2 -f config.bin +------------ ++ +* Write 200 blocks of (512 bytes) from input.bin onto target 0 ++ +------------ +# nvme rpmb /dev/nvme0 -c write-data -t 0 -f input.bin -b 200 -k 'SecreteKey' +------------ ++ +* Read 200 blocks of (512 bytes) from target 2, at offset 0x100 and save the +* data onto output.bin ++ +------------ +# nvme rpmb /dev/nvme0 -c read-data -t 2 -f out.bin -b 200 -o 0x100 +------------ + +NVME +---- +Part of the nvme-user suite + diff --git a/Documentation/nvme-sanitize-log.1 b/Documentation/nvme-sanitize-log.1 index 72787c7..1163e2d 100644 --- a/Documentation/nvme-sanitize-log.1 +++ b/Documentation/nvme-sanitize-log.1 @@ -1,13 +1,13 @@ '\" t .\" Title: nvme-sanitize-log -.\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] -.\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] +.\" Generator: DocBook XSL Stylesheets v1.79.1 +.\" Date: 04/05/2021 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-SANITIZE\-LOG" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-SANITIZE\-LOG" "1" "04/05/2021" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -32,7 +32,7 @@ nvme-sanitize-log \- Send NVMe sanitize\-log Command, return result .SH "SYNOPSIS" .sp .nf -\fInvme sanitize\-log\fR [\-\-output\-format= | \-o ] +\fInvme sanitize\-log\fR [\-\-rae | \-r] [\-\-output\-format= | \-o ] [\-\-human\-readable | \-H] [\-\-raw\-binary | \-b] .fi @@ -100,6 +100,11 @@ Sanitize Progress \- percentage complete On success it returns 0, error code otherwise\&. .SH "OPTIONS" .PP +\-r, \-\-rae +.RS 4 +Retain an Asynchronous Event\&. +.RE +.PP \-o , \-\-output\-format= .RS 4 Set the reporting format to diff --git a/Documentation/nvme-sanitize-log.html b/Documentation/nvme-sanitize-log.html index d369a10..63c8db6 100644 --- a/Documentation/nvme-sanitize-log.html +++ b/Documentation/nvme-sanitize-log.html @@ -749,7 +749,7 @@ nvme-sanitize-log(1) Manual Page

SYNOPSIS

-
nvme sanitize-log <device> [--output-format=<fmt> | -o <fmt>]
+
nvme sanitize-log <device> [--rae | -r] [--output-format=<fmt> | -o <fmt>]
                              [--human-readable | -H]
                              [--raw-binary | -b]
@@ -816,6 +816,17 @@ If cleared to 0, then non-volatile storage in the NVM subsystem has been written
+-r +
+
+--rae +
+
+

+ Retain an Asynchronous Event. +

+
+
-o <format>
@@ -881,7 +892,7 @@ Has the program issue Sanitize-log Command : diff --git a/Documentation/nvme-sanitize-log.txt b/Documentation/nvme-sanitize-log.txt index 3c3b73e..3c2d43e 100644 --- a/Documentation/nvme-sanitize-log.txt +++ b/Documentation/nvme-sanitize-log.txt @@ -8,7 +8,7 @@ nvme-sanitize-log - Send NVMe sanitize-log Command, return result SYNOPSIS -------- [verse] -'nvme sanitize-log' [--output-format= | -o ] +'nvme sanitize-log' [--rae | -r] [--output-format= | -o ] [--human-readable | -H] [--raw-binary | -b] @@ -54,6 +54,10 @@ On success it returns 0, error code otherwise. OPTIONS ------- +-r:: +--rae:: + Retain an Asynchronous Event. + -o :: --output-format=:: Set the reporting format to 'normal', 'json', or diff --git a/Documentation/nvme-sanitize.1 b/Documentation/nvme-sanitize.1 index 85d2bd4..709992e 100644 --- a/Documentation/nvme-sanitize.1 +++ b/Documentation/nvme-sanitize.1 @@ -2,12 +2,12 @@ .\" Title: nvme-sanitize .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-SANITIZE" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-SANITIZE" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff --git a/Documentation/nvme-security-recv.1 b/Documentation/nvme-security-recv.1 index df47abb..eef260a 100644 --- a/Documentation/nvme-security-recv.1 +++ b/Documentation/nvme-security-recv.1 @@ -2,12 +2,12 @@ .\" Title: nvme-security-recv .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-SECURITY\-RECV" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-SECURITY\-RECV" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff --git a/Documentation/nvme-security-send.1 b/Documentation/nvme-security-send.1 index 919464c..81e4854 100644 --- a/Documentation/nvme-security-send.1 +++ b/Documentation/nvme-security-send.1 @@ -2,12 +2,12 @@ .\" Title: nvme-security-send .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-SECURITY\-SEND" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-SECURITY\-SEND" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff --git a/Documentation/nvme-self-test-log.1 b/Documentation/nvme-self-test-log.1 index caff6ff..e00e289 100644 --- a/Documentation/nvme-self-test-log.1 +++ b/Documentation/nvme-self-test-log.1 @@ -1,13 +1,13 @@ '\" t .\" Title: nvme-self-test-log -.\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] -.\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] +.\" Generator: DocBook XSL Stylesheets v1.79.1 +.\" Date: 01/23/2021 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-SELF\-TEST\-LO" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-SELF\-TEST\-LO" "1" "01/23/2021" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -32,7 +32,8 @@ nvme-self-test-log \- Retrieve the log information initited by device\-self\-tes .SH "SYNOPSIS" .sp .nf -\fInvme self\-test\fR\-log [\-\-output\-format= | \-o ] +\fInvme self\-test\fR\-log [\-\-log\-entries= | \-e ] + [\-\-output\-format= | \-o ] .fi .SH "DESCRIPTION" .sp @@ -45,6 +46,11 @@ On success, the returned log structure may be returned in one of several ways de By default the log is printed out in the normal readable format\&. .SH "OPTION" .PP +\-e , \-\-log\-entries= +.RS 4 +Specifies how many DST log entries the program should request from the device\&. This must be at least one, and shouldn\(cqt exceed the 20 entries\&. Defaults to 20 DST log entries\&. +.RE +.PP \-o , \-\-output\-format= .RS 4 Set the reporting format to diff --git a/Documentation/nvme-self-test-log.html b/Documentation/nvme-self-test-log.html index 7f02a76..3b24a26 100644 --- a/Documentation/nvme-self-test-log.html +++ b/Documentation/nvme-self-test-log.html @@ -1,9 +1,10 @@ + - + nvme-self-test-log(1) + + + + +
+
+

SYNOPSIS

+
+
+
nvme wdc capabilities <device>
+
+
+
+
+
+

DESCRIPTION

+
+

For the NVMe device given, displays list of commands and support status.

+

The <device> parameter is mandatory NVMe character device (ex: /dev/nvme0).

+

On success it returns 0, error code otherwise.

+
+
+
+

EXAMPLES

+
+
    +
  • +

    +Displays the capabilities for the device: +

    +
    +
    +
    # nvme wdc capabilities /dev/nvme0
    +
    +
  • +
+
+
+
+

NVME

+
+

Part of the nvme-user suite.

+
+
+
+

+ + + diff --git a/Documentation/nvme-wdc-capabilities.txt b/Documentation/nvme-wdc-capabilities.txt new file mode 100644 index 0000000..859bb5f --- /dev/null +++ b/Documentation/nvme-wdc-capabilities.txt @@ -0,0 +1,32 @@ +nvme-wdc-capabilities(1) +======================== + +NAME +---- +nvme-wdc-capabilities - Display WDC plugin command capabilities + +SYNOPSIS +-------- +[verse] +'nvme wdc capabilities' + +DESCRIPTION +----------- + +For the NVMe device given, displays list of commands and support status. + +The parameter is mandatory NVMe character device (ex: /dev/nvme0). + +On success it returns 0, error code otherwise. + +EXAMPLES +-------- +* Displays the capabilities for the device: ++ +------------ +# nvme wdc capabilities /dev/nvme0 +------------ + +NVME +---- +Part of the nvme-user suite. diff --git a/Documentation/nvme-wdc-clear-assert-dump.1 b/Documentation/nvme-wdc-clear-assert-dump.1 index 1f1569e..85e7dca 100644 --- a/Documentation/nvme-wdc-clear-assert-dump.1 +++ b/Documentation/nvme-wdc-clear-assert-dump.1 @@ -2,12 +2,12 @@ .\" Title: nvme-wdc-clear-assert-dump .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-WDC\-CLEAR\-AS" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-WDC\-CLEAR\-AS" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff --git a/Documentation/nvme-wdc-clear-fw-activate-history.1 b/Documentation/nvme-wdc-clear-fw-activate-history.1 index 0a0a999..3dd36b2 100644 --- a/Documentation/nvme-wdc-clear-fw-activate-history.1 +++ b/Documentation/nvme-wdc-clear-fw-activate-history.1 @@ -2,12 +2,12 @@ .\" Title: nvme-wdc-clear-fw-activate-history .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-WDC\-CLEAR\-FW" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-WDC\-CLEAR\-FW" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff --git a/Documentation/nvme-wdc-clear-pcie-correctable-errors.1 b/Documentation/nvme-wdc-clear-pcie-correctable-errors.1 index 1f45364..f819679 100644 --- a/Documentation/nvme-wdc-clear-pcie-correctable-errors.1 +++ b/Documentation/nvme-wdc-clear-pcie-correctable-errors.1 @@ -2,12 +2,12 @@ .\" Title: nvme-wdc-clear-pcie-correctable-errors .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-WDC\-CLEAR\-PC" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-WDC\-CLEAR\-PC" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff --git a/Documentation/nvme-wdc-cloud-SSD-plugin-version.1 b/Documentation/nvme-wdc-cloud-SSD-plugin-version.1 new file mode 100644 index 0000000..3ac3bc1 --- /dev/null +++ b/Documentation/nvme-wdc-cloud-SSD-plugin-version.1 @@ -0,0 +1,68 @@ +'\" t +.\" Title: nvme-wdc-cloud-SSD-plugin-version +.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] +.\" Generator: DocBook XSL Stylesheets v1.78.1 +.\" Date: 03/01/2021 +.\" Manual: NVMe Manual +.\" Source: NVMe +.\" Language: English +.\" +.TH "NVME\-WDC\-CLOUD\-SS" "1" "03/01/2021" "NVMe" "NVMe Manual" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +nvme-wdc-cloud-SSD-plugin-version \- Display WDC plugin Cloud SSD Plugin Version +.SH "SYNOPSIS" +.sp +.nf +\fInvme wdc cloud\-SSD\-plugin\-version\fR +.fi +.SH "DESCRIPTION" +.sp +For the NVMe device given, this command displays the current Cloud SSD Plugin Version (if supported by the device)\&. +.sp +The parameter is mandatory NVMe character device (ex: /dev/nvme0)\&. +.sp +On success it returns 0, error code otherwise\&. +.SH "EXAMPLES" +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Displays the cloud ssd plugin version for the device: +.sp +.if n \{\ +.RS 4 +.\} +.nf +# nvme wdc cloud\-SSD\-plugin\-version /dev/nvme0 +.fi +.if n \{\ +.RE +.\} +.RE +.SH "NVME" +.sp +Part of the nvme\-user suite\&. diff --git a/Documentation/nvme-wdc-cloud-SSD-plugin-version.html b/Documentation/nvme-wdc-cloud-SSD-plugin-version.html new file mode 100644 index 0000000..dd686f8 --- /dev/null +++ b/Documentation/nvme-wdc-cloud-SSD-plugin-version.html @@ -0,0 +1,793 @@ + + + + + +nvme-wdc-cloud-SSD-plugin-version(1) + + + + + +
+
+

SYNOPSIS

+
+
+
nvme wdc cloud-SSD-plugin-version <device>
+
+
+
+
+
+

DESCRIPTION

+
+

For the NVMe device given, this command displays the current Cloud SSD +Plugin Version (if supported by the device).

+

The <device> parameter is mandatory NVMe character device (ex: /dev/nvme0).

+

On success it returns 0, error code otherwise.

+
+
+
+

EXAMPLES

+
+
    +
  • +

    +Displays the cloud ssd plugin version for the device: +

    +
    +
    +
    # nvme wdc cloud-SSD-plugin-version /dev/nvme0
    +
    +
  • +
+
+
+
+

NVME

+
+

Part of the nvme-user suite.

+
+
+
+

+ + + diff --git a/Documentation/nvme-wdc-cloud-SSD-plugin-version.txt b/Documentation/nvme-wdc-cloud-SSD-plugin-version.txt new file mode 100644 index 0000000..3fed5cd --- /dev/null +++ b/Documentation/nvme-wdc-cloud-SSD-plugin-version.txt @@ -0,0 +1,33 @@ +nvme-wdc-cloud-SSD-plugin-version(1) +==================================== + +NAME +---- +nvme-wdc-cloud-SSD-plugin-version - Display WDC plugin Cloud SSD Plugin Version + +SYNOPSIS +-------- +[verse] +'nvme wdc cloud-SSD-plugin-version' + +DESCRIPTION +----------- + +For the NVMe device given, this command displays the current Cloud SSD +Plugin Version (if supported by the device). + +The parameter is mandatory NVMe character device (ex: /dev/nvme0). + +On success it returns 0, error code otherwise. + +EXAMPLES +-------- +* Displays the cloud ssd plugin version for the device: ++ +------------ +# nvme wdc cloud-SSD-plugin-version /dev/nvme0 +------------ + +NVME +---- +Part of the nvme-user suite. diff --git a/Documentation/nvme-wdc-drive-essentials.1 b/Documentation/nvme-wdc-drive-essentials.1 index e0a2902..e6e1b57 100644 --- a/Documentation/nvme-wdc-drive-essentials.1 +++ b/Documentation/nvme-wdc-drive-essentials.1 @@ -2,12 +2,12 @@ .\" Title: nvme-wdc-drive-essentials .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-WDC\-DRIVE\-ES" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-WDC\-DRIVE\-ES" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff --git a/Documentation/nvme-wdc-drive-log.1 b/Documentation/nvme-wdc-drive-log.1 index 4ce6955..5ed478b 100644 --- a/Documentation/nvme-wdc-drive-log.1 +++ b/Documentation/nvme-wdc-drive-log.1 @@ -2,12 +2,12 @@ .\" Title: nvme-wdc-drive-log .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-WDC\-DRIVE\-LO" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-WDC\-DRIVE\-LO" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff --git a/Documentation/nvme-wdc-drive-resize.1 b/Documentation/nvme-wdc-drive-resize.1 index 0949d70..6bd64f4 100644 --- a/Documentation/nvme-wdc-drive-resize.1 +++ b/Documentation/nvme-wdc-drive-resize.1 @@ -2,12 +2,12 @@ .\" Title: nvme-wdc-drive-resize .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-WDC\-DRIVE\-RE" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-WDC\-DRIVE\-RE" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff --git a/Documentation/nvme-wdc-enc-get-log.1 b/Documentation/nvme-wdc-enc-get-log.1 new file mode 100644 index 0000000..69a4f3e --- /dev/null +++ b/Documentation/nvme-wdc-enc-get-log.1 @@ -0,0 +1,104 @@ +'\" t +.\" Title: nvme-wdc-enc-get-log +.\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] +.\" Generator: DocBook XSL Stylesheets vsnapshot +.\" Date: 10/20/2020 +.\" Manual: NVMe Manual +.\" Source: NVMe +.\" Language: English +.\" +.TH "NVME\-WDC\-ENC\-GET\" "1" "10/20/2020" "NVMe" "NVMe Manual" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +nvme-wdc-enc-get-log \- Send NVMe WDC enc\-get\-log Vendor Unique Command, return result\&. +.SH "SYNOPSIS" +.sp +.nf +\fInvme wdc enc\-get\-log\fR [\-\-log\-id=, \-l ] [\-\-output\-file=, \-o ] [\-\-transfer\-size=, \-s ] +.fi +.SH "DESCRIPTION" +.sp +For the NVMe device given, send a Vendor Unique WDC enc\-get\-log command and output the Enclosure logs\&. +.sp +The parameter is mandatory and may be either the NVMe character device (ex: /dev/nvme0)\&. +.sp +The \-\-log\-id=, \-l parameter is mandatory and may be either 0xd1, 0xd2, 0xd3, 0xd4, 0xe2 and 0xe4\&. +.sp +This will only work on WDC devices supporting this feature\&. Results for any other device are undefined\&. +.sp +On success it returns the enclosure log data, error code otherwise\&. +.SH "OPTIONS" +.PP +\-o , \-\-output\-file= +.RS 4 +Output file pathname +.RE +.PP +\-s , \-\-transfer\-size= +.RS 4 +Data retrieval transfer size, maximum transfer size should be 0x2000 (decimal 8192) +.RE +.SH "EXAMPLES" +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Gets the enclosure log from the device based on the log id(0xd1) with transfer size(0x2000) and saves to defined file in current directory: +.sp +.if n \{\ +.RS 4 +.\} +.nf +# nvme wdc enc\-get\-log /dev/nvme0 \-l 0xd1 \-o d1_log\&.bin \-s 0x2000 +.fi +.if n \{\ +.RE +.\} +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Gets the enclosure log from the device based on the log id(0xd2) with default transfer size(0x1000) and saves to defined file in current directory: +.sp +.if n \{\ +.RS 4 +.\} +.nf +# nvme wdc enc\-get\-log /dev/nvme0 \-l 0xd2 \-o d1_log\&.bin +.fi +.if n \{\ +.RE +.\} +.RE +.SH "NVME" +.sp +Part of the nvme\-user suite\&. diff --git a/Documentation/nvme-wdc-enc-get-log.html b/Documentation/nvme-wdc-enc-get-log.html new file mode 100644 index 0000000..b806f3a --- /dev/null +++ b/Documentation/nvme-wdc-enc-get-log.html @@ -0,0 +1,839 @@ + + + + + + +nvme-wdc-enc-get-log(1) + + + + + +
+
+

SYNOPSIS

+
+
+
nvme wdc enc-get-log <device> [--log-id=<NUM>, -l <NUM>] [--output-file=<FILE>, -o <FILE>] [--transfer-size=<SIZE>, -s <SIZE>]
+
+
+
+
+
+

DESCRIPTION

+
+

For the NVMe device given, send a Vendor Unique WDC enc-get-log command and +output the Enclosure logs.

+

The <device> parameter is mandatory and may be either the NVMe character +device (ex: /dev/nvme0).

+

The --log-id=<NUM>, -l <NUM> parameter is mandatory and may be either 0xd1, 0xd2, 0xd3, 0xd4, 0xe2 and 0xe4.

+

This will only work on WDC devices supporting this feature. +Results for any other device are undefined.

+

On success it returns the enclosure log data, error code otherwise.

+
+
+
+

OPTIONS

+
+
+
+-o <FILE> +
+
+--output-file=<FILE> +
+
+

+ Output file pathname +

+
+
+-s <SIZE> +
+
+--transfer-size=<NUM> +
+
+

+ Data retrieval transfer size, maximum transfer size should be 0x2000 (decimal 8192) +

+
+
+
+
+
+

EXAMPLES

+
+
    +
  • +

    +Gets the enclosure log from the device based on the log id(0xd1) with transfer size(0x2000) and saves to defined file in current directory: +

    +
    +
    +
    # nvme wdc enc-get-log /dev/nvme0 -l 0xd1 -o d1_log.bin -s 0x2000
    +
    +
  • +
  • +

    +Gets the enclosure log from the device based on the log id(0xd2) with default transfer size(0x1000) and saves to defined file in current directory: +

    +
    +
    +
    # nvme wdc enc-get-log /dev/nvme0 -l 0xd2 -o d1_log.bin
    +
    +
  • +
+
+
+
+

NVME

+
+

Part of the nvme-user suite.

+
+
+
+

+ + + diff --git a/Documentation/nvme-wdc-enc-get-log.txt b/Documentation/nvme-wdc-enc-get-log.txt new file mode 100644 index 0000000..ae93a7a --- /dev/null +++ b/Documentation/nvme-wdc-enc-get-log.txt @@ -0,0 +1,54 @@ +nvme-wdc-enc-get-log(1) +======================= + +NAME +---- +nvme-wdc-enc-get-log - Send NVMe WDC enc-get-log Vendor Unique Command, return result. + +SYNOPSIS +-------- +[verse] +'nvme wdc enc-get-log' [--log-id=, -l ] [--output-file=, -o ] [--transfer-size=, -s ] + +DESCRIPTION +----------- + +For the NVMe device given, send a Vendor Unique WDC enc-get-log command and +output the Enclosure logs. + +The parameter is mandatory and may be either the NVMe character +device (ex: /dev/nvme0). + +The --log-id=, -l parameter is mandatory and may be either 0xd1, 0xd2, 0xd3, 0xd4, 0xe2 and 0xe4. + +This will only work on WDC devices supporting this feature. +Results for any other device are undefined. + +On success it returns the enclosure log data, error code otherwise. + +OPTIONS +------- +-o :: +--output-file=:: + Output file pathname + +-s :: +--transfer-size=:: + Data retrieval transfer size, maximum transfer size should be 0x2000 (decimal 8192) + +EXAMPLES +-------- +* Gets the enclosure log from the device based on the log id(0xd1) with transfer size(0x2000) and saves to defined file in current directory: ++ +------------ +# nvme wdc enc-get-log /dev/nvme0 -l 0xd1 -o d1_log.bin -s 0x2000 +------------ +* Gets the enclosure log from the device based on the log id(0xd2) with default transfer size(0x1000) and saves to defined file in current directory: ++ +------------ +# nvme wdc enc-get-log /dev/nvme0 -l 0xd2 -o d1_log.bin +------------ + +NVME +---- +Part of the nvme-user suite. diff --git a/Documentation/nvme-wdc-get-crash-dump.1 b/Documentation/nvme-wdc-get-crash-dump.1 index 152643f..8e6a880 100644 --- a/Documentation/nvme-wdc-get-crash-dump.1 +++ b/Documentation/nvme-wdc-get-crash-dump.1 @@ -2,12 +2,12 @@ .\" Title: nvme-wdc-get-crash-dump .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-WDC\-GET\-CRAS" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-WDC\-GET\-CRAS" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff --git a/Documentation/nvme-wdc-get-drive-status.1 b/Documentation/nvme-wdc-get-drive-status.1 index b19520d..f360e85 100644 --- a/Documentation/nvme-wdc-get-drive-status.1 +++ b/Documentation/nvme-wdc-get-drive-status.1 @@ -2,12 +2,12 @@ .\" Title: nvme-wdc-get-drive-status .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-WDC\-GET\-DRIV" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-WDC\-GET\-DRIV" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff --git a/Documentation/nvme-wdc-get-pfail-dump.1 b/Documentation/nvme-wdc-get-pfail-dump.1 index 230ce79..a96febd 100644 --- a/Documentation/nvme-wdc-get-pfail-dump.1 +++ b/Documentation/nvme-wdc-get-pfail-dump.1 @@ -2,12 +2,12 @@ .\" Title: nvme-wdc-get-pfail-dump .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-WDC\-GET\-PFAI" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-WDC\-GET\-PFAI" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff --git a/Documentation/nvme-wdc-id-ctrl.1 b/Documentation/nvme-wdc-id-ctrl.1 index 220d953..71aaa71 100644 --- a/Documentation/nvme-wdc-id-ctrl.1 +++ b/Documentation/nvme-wdc-id-ctrl.1 @@ -2,12 +2,12 @@ .\" Title: nvme-wdc-id-ctrl .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-WDC\-ID\-CTRL" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-WDC\-ID\-CTRL" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff --git a/Documentation/nvme-wdc-log-page-directory.1 b/Documentation/nvme-wdc-log-page-directory.1 index 0d407ba..611f65e 100644 --- a/Documentation/nvme-wdc-log-page-directory.1 +++ b/Documentation/nvme-wdc-log-page-directory.1 @@ -2,12 +2,12 @@ .\" Title: nvme-wdc-log-page-directory .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-WDC\-LOG\-PAGE" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-WDC\-LOG\-PAGE" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff --git a/Documentation/nvme-wdc-namespace-resize.1 b/Documentation/nvme-wdc-namespace-resize.1 index ea0235a..c159d2b 100644 --- a/Documentation/nvme-wdc-namespace-resize.1 +++ b/Documentation/nvme-wdc-namespace-resize.1 @@ -2,12 +2,12 @@ .\" Title: nvme-wdc-namespace-resize .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-WDC\-NAMESPACE" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-WDC\-NAMESPACE" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff --git a/Documentation/nvme-wdc-purge-monitor.1 b/Documentation/nvme-wdc-purge-monitor.1 index 3df192a..ffb7fd8 100644 --- a/Documentation/nvme-wdc-purge-monitor.1 +++ b/Documentation/nvme-wdc-purge-monitor.1 @@ -2,12 +2,12 @@ .\" Title: nvme-wdc-purge-monitor .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-WDC\-PURGE\-MO" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-WDC\-PURGE\-MO" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff --git a/Documentation/nvme-wdc-purge.1 b/Documentation/nvme-wdc-purge.1 index 748f78d..506714a 100644 --- a/Documentation/nvme-wdc-purge.1 +++ b/Documentation/nvme-wdc-purge.1 @@ -2,12 +2,12 @@ .\" Title: nvme-wdc-purge .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-WDC\-PURGE" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-WDC\-PURGE" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff --git a/Documentation/nvme-wdc-vs-drive-info.1 b/Documentation/nvme-wdc-vs-drive-info.1 index 4f5dd1c..9ca8396 100644 --- a/Documentation/nvme-wdc-vs-drive-info.1 +++ b/Documentation/nvme-wdc-vs-drive-info.1 @@ -2,12 +2,12 @@ .\" Title: nvme-wdc-vs-drive-info .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-WDC\-VS\-DRIVE" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-WDC\-VS\-DRIVE" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff --git a/Documentation/nvme-wdc-vs-error-reason-identifier.1 b/Documentation/nvme-wdc-vs-error-reason-identifier.1 index 2770ce3..e8779bf 100644 --- a/Documentation/nvme-wdc-vs-error-reason-identifier.1 +++ b/Documentation/nvme-wdc-vs-error-reason-identifier.1 @@ -2,12 +2,12 @@ .\" Title: nvme-wdc-vs-error-reason-identifier .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-WDC\-VS\-ERROR" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-WDC\-VS\-ERROR" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff --git a/Documentation/nvme-wdc-vs-fw-activate-history.1 b/Documentation/nvme-wdc-vs-fw-activate-history.1 index 25ce40a..696acf8 100644 --- a/Documentation/nvme-wdc-vs-fw-activate-history.1 +++ b/Documentation/nvme-wdc-vs-fw-activate-history.1 @@ -2,12 +2,12 @@ .\" Title: nvme-wdc-vs-fw-activate-history .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-WDC\-VS\-FW\-A" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-WDC\-VS\-FW\-A" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff --git a/Documentation/nvme-wdc-vs-internal-log.1 b/Documentation/nvme-wdc-vs-internal-log.1 index abb6e94..441777d 100644 --- a/Documentation/nvme-wdc-vs-internal-log.1 +++ b/Documentation/nvme-wdc-vs-internal-log.1 @@ -2,12 +2,12 @@ .\" Title: nvme-wdc-vs-internal-log .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-WDC\-VS\-INTER" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-WDC\-VS\-INTER" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -57,7 +57,7 @@ Transfer size; defaults to 0x10000 (65536 decimal) bytes .PP \-d , \-\-data\-area= .RS 4 -DUI data area to retrieve\&. The DUI data areas from 1 to will be retrieved\&. This parameter is currently only supported on the SN340, SN640, and SN840 devices\&. +DUI data area to retrieve\&. The DUI data areas from 1 to will be retrieved\&. This parameter is currently only supported on the SN340, SN640, SN730, and SN840 devices\&. .RE .PP \-f , \-\-file\-size= diff --git a/Documentation/nvme-wdc-vs-internal-log.html b/Documentation/nvme-wdc-vs-internal-log.html index d07be0e..8919541 100644 --- a/Documentation/nvme-wdc-vs-internal-log.html +++ b/Documentation/nvme-wdc-vs-internal-log.html @@ -4,7 +4,7 @@ - + nvme-wdc-vs-internal-log(1) + + + + +
+
+

SYNOPSIS

+
+
+
nvme wdc vs-temperature-stats <device>
+
+
+
+
+
+

DESCRIPTION

+
+

For the NVMe device given, displays temperature statistics.

+

The <device> parameter is mandatory NVMe character device (ex: /dev/nvme0).

+

This will only work on WDC devices supporting this feature. +Results for any other device are undefined.

+

Expected status and description :-

+
+ +++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Statistic Description

The current composite temperature

device temperature

Warning Composite TEMPerature threshold

temp of overheating

Critical Composite TEMPerature threshold

temp of critical overheating

Device Initiated Thermal Throttling support status

0 = unsupported, 1 = supported

Host Controlled Thermal Management support

0 = unsupported, 1 = supported

Thermal Management Temperature 1 (Light throttle)

temp to start light throttle

Thermal Management Temperature 1 Transition Counter

# times switched into light throttle

Thermal Management Temperature 1 Total Time

# seconds spent in light throttle

Thermal Management Temperature 2 (Heavy throttle)

temp to start heavy throttle

Thermal Management Temperature 2 Transition Counter

# times switched into heavy throttle

Thermal Management Temperature 2 Total Time

# seconds spent in heavy throttle

Thermal Shutdown Threshold

temp of device shutdown

+
+

On success it returns 0, error code otherwise.

+
+
+
+

EXAMPLES

+
+
    +
  • +

    +Displays the temperature stats for the device: +

    +
    +
    +
    # nvme wdc vs-temperature-stats /dev/nvme0
    +
    +
  • +
+
+
+
+

NVME

+
+

Part of the nvme-user suite.

+
+
+
+

+ + + diff --git a/Documentation/nvme-wdc-vs-temperature-stats.txt b/Documentation/nvme-wdc-vs-temperature-stats.txt new file mode 100644 index 0000000..1d000e3 --- /dev/null +++ b/Documentation/nvme-wdc-vs-temperature-stats.txt @@ -0,0 +1,78 @@ +nvme-wdc-vs-temperature-stats(1) +================================ + +NAME +---- +nvme-wdc-vs-temperature-stats - Display temperature-related statistics + +SYNOPSIS +-------- +[verse] +'nvme wdc vs-temperature-stats' + +DESCRIPTION +----------- + +For the NVMe device given, displays temperature statistics. + +The parameter is mandatory NVMe character device (ex: /dev/nvme0). + +This will only work on WDC devices supporting this feature. +Results for any other device are undefined. + +Expected status and description :- + +[cols="2*", options="header"] +|=== +|Statistic |Description + +|The current composite temperature +|device temperature + +|Warning Composite TEMPerature threshold +|temp of overheating + +|Critical Composite TEMPerature threshold +|temp of critical overheating + +|Device Initiated Thermal Throttling support status +|0 = unsupported, 1 = supported + +|Host Controlled Thermal Management support +|0 = unsupported, 1 = supported + +|Thermal Management Temperature 1 (Light throttle) +|temp to start light throttle + +|Thermal Management Temperature 1 Transition Counter +|# times switched into light throttle + +|Thermal Management Temperature 1 Total Time +|# seconds spent in light throttle + +|Thermal Management Temperature 2 (Heavy throttle) +|temp to start heavy throttle + +|Thermal Management Temperature 2 Transition Counter +|# times switched into heavy throttle + +|Thermal Management Temperature 2 Total Time +|# seconds spent in heavy throttle + +|Thermal Shutdown Threshold +|temp of device shutdown +|=== + +On success it returns 0, error code otherwise. + +EXAMPLES +-------- +* Displays the temperature stats for the device: ++ +------------ +# nvme wdc vs-temperature-stats /dev/nvme0 +------------ + +NVME +---- +Part of the nvme-user suite. diff --git a/Documentation/nvme-write-uncor.1 b/Documentation/nvme-write-uncor.1 index 2cc5c88..93e7511 100644 --- a/Documentation/nvme-write-uncor.1 +++ b/Documentation/nvme-write-uncor.1 @@ -2,12 +2,12 @@ .\" Title: nvme-uncor .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-UNCOR" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-UNCOR" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff --git a/Documentation/nvme-write-zeroes.1 b/Documentation/nvme-write-zeroes.1 index c1a5b52..d32e3ca 100644 --- a/Documentation/nvme-write-zeroes.1 +++ b/Documentation/nvme-write-zeroes.1 @@ -2,12 +2,12 @@ .\" Title: nvme-zeroes .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-ZEROES" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-ZEROES" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff --git a/Documentation/nvme-write.1 b/Documentation/nvme-write.1 index 5e9101a..727ac03 100644 --- a/Documentation/nvme-write.1 +++ b/Documentation/nvme-write.1 @@ -2,12 +2,12 @@ .\" Title: nvme-write .\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Date: 10/20/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME\-WRITE" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME\-WRITE" "1" "10/20/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff --git a/Documentation/nvme-zns-changed-zone-list.1 b/Documentation/nvme-zns-changed-zone-list.1 new file mode 100644 index 0000000..c4a96ba --- /dev/null +++ b/Documentation/nvme-zns-changed-zone-list.1 @@ -0,0 +1,105 @@ +'\" t +.\" Title: nvme-zns-changed-zone-list +.\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] +.\" Generator: DocBook XSL Stylesheets vsnapshot +.\" Date: 10/20/2020 +.\" Manual: NVMe Manual +.\" Source: NVMe +.\" Language: English +.\" +.TH "NVME\-ZNS\-CHANGED\-" "1" "10/20/2020" "NVMe" "NVMe Manual" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +nvme-zns-changed-zone-list \- Retrieve Changed Zone log for the given device +.SH "SYNOPSIS" +.sp +.nf +\fInvme zns changed\-zone\-list\fR [\-o | \-\-output\-format=] + [\-\-namespace\-id= | \-n ] + [\-\-rae | \-r] +.fi +.SH "DESCRIPTION" +.sp +For the NVMe device given, requests the namespace\(cqs changed zoned list log and provides the result and returned structure\&. +.sp +The parameter is mandatory and may be either the NVMe character device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1)\&. +.sp +On success, the returned list may be decoded and displayed in one of several ways\&. +.SH "OPTIONS" +.PP +\-o , \-\-output\-format= +.RS 4 +Set the reporting format to +\fInormal\fR, +\fIjson\fR, or +\fIbinary\fR\&. Only one output format can be used at a time\&. +.RE +.PP +\-r, \-\-rae +.RS 4 +Retain an Asynchronous Event\&. +.RE +.SH "EXAMPLES" +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Get the changed zone list log for namespace 1 +.sp +.if n \{\ +.RS 4 +.\} +.nf +# nvme zns changed\-zone\-list /dev/nvme0 \-n 1 +.fi +.if n \{\ +.RE +.\} +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Show the output in json format +.sp +.if n \{\ +.RS 4 +.\} +.nf +# nvme zns changed\-zone\-list /dev/nvme0 \-o json \-n 1 +.fi +.if n \{\ +.RE +.\} +.RE +.SH "NVME" +.sp +Part of nvme\-cli diff --git a/Documentation/nvme-zns-changed-zone-list.html b/Documentation/nvme-zns-changed-zone-list.html new file mode 100644 index 0000000..977c1d6 --- /dev/null +++ b/Documentation/nvme-zns-changed-zone-list.html @@ -0,0 +1,840 @@ + + + + + + +nvme-zns-changed-zone-list(1) + + + + + +
+
+

SYNOPSIS

+
+
+
nvme zns changed-zone-list <device> [-o <fmt> | --output-format=<fmt>]
+                                      [--namespace-id=<NUM> | -n <NUM>]
+                                      [--rae | -r]
+
+
+
+
+
+

DESCRIPTION

+
+

For the NVMe device given, requests the namespace’s changed zoned list log +and provides the result and returned structure.

+

The <device> parameter is mandatory and may be either the NVMe character +device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1).

+

On success, the returned list may be decoded and displayed in one of several +ways.

+
+
+
+

OPTIONS

+
+
+
+-o <format> +
+
+--output-format=<format> +
+
+

+ Set the reporting format to normal, json, or + binary. Only one output format can be used at a time. +

+
+
+-r +
+
+--rae +
+
+

+ Retain an Asynchronous Event. +

+
+
+
+
+
+

EXAMPLES

+
+
    +
  • +

    +Get the changed zone list log for namespace 1 +

    +
    +
    +
    # nvme zns changed-zone-list /dev/nvme0 -n 1
    +
    +
  • +
  • +

    +Show the output in json format +

    +
    +
    +
    # nvme zns changed-zone-list /dev/nvme0 -o json -n 1
    +
    +
  • +
+
+
+
+

NVME

+
+

Part of nvme-cli

+
+
+
+

+ + + diff --git a/Documentation/nvme-zns-changed-zone-list.txt b/Documentation/nvme-zns-changed-zone-list.txt new file mode 100644 index 0000000..9626c05 --- /dev/null +++ b/Documentation/nvme-zns-changed-zone-list.txt @@ -0,0 +1,54 @@ +nvme-zns-changed-zone-list(1) +============================= + +NAME +---- +nvme-zns-changed-zone-list - Retrieve Changed Zone log for the given device + +SYNOPSIS +-------- +[verse] +'nvme zns changed-zone-list' [-o | --output-format=] + [--namespace-id= | -n ] + [--rae | -r] + +DESCRIPTION +----------- +For the NVMe device given, requests the namespace's changed zoned list log +and provides the result and returned structure. + +The parameter is mandatory and may be either the NVMe character +device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1). + +On success, the returned list may be decoded and displayed in one of several +ways. + +OPTIONS +------- +-o :: +--output-format=:: + Set the reporting format to 'normal', 'json', or + 'binary'. Only one output format can be used at a time. + +-r:: +--rae:: + Retain an Asynchronous Event. + +EXAMPLES +-------- +* Get the changed zone list log for namespace 1 ++ +------------ +# nvme zns changed-zone-list /dev/nvme0 -n 1 +------------ ++ + +* Show the output in json format ++ +------------ +# nvme zns changed-zone-list /dev/nvme0 -o json -n 1 +------------ + +NVME +---- +Part of nvme-cli diff --git a/Documentation/nvme-zns-close-zone.1 b/Documentation/nvme-zns-close-zone.1 new file mode 100644 index 0000000..d9624c1 --- /dev/null +++ b/Documentation/nvme-zns-close-zone.1 @@ -0,0 +1,84 @@ +'\" t +.\" Title: nvme-zns-close-zone +.\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] +.\" Generator: DocBook XSL Stylesheets vsnapshot +.\" Date: 10/20/2020 +.\" Manual: NVMe Manual +.\" Source: NVMe +.\" Language: English +.\" +.TH "NVME\-ZNS\-CLOSE\-ZO" "1" "10/20/2020" "NVMe" "NVMe Manual" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +nvme-zns-close-zone \- Closes one or all zones +.SH "SYNOPSIS" +.sp +.nf +\fInvme zns close\-zone nvme zns id\-ctrl\fR [\-\-namespace\-id= | \-n ] + [\-\-start\-lba= | \-s ] + [\-\-select\-all | \-a] +.fi +.SH "DESCRIPTION" +.sp +For the NVMe device given, issues the Zone Management Send command with the "Close Zone" action\&. This will transition the zone to the closed state\&. +.sp +The parameter is mandatory and may be either the NVMe character device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1)\&. +.SH "OPTIONS" +.PP +\-n , \-\-namespace\-id= +.RS 4 +Use the provided namespace id for the command\&. If not provided, the namespace id of the block device will be used\&. If the command is issued to a non\-block device, the parameter is required\&. +.RE +.PP +\-s , \-\-start\-lba= +.RS 4 +The starting LBA of the zone to close\&. +.RE +.PP +\-a, \-\-select\-all +.RS 4 +Select all zones for this action +.RE +.SH "EXAMPLES" +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Close all zones on namespace 1: +.sp +.if n \{\ +.RS 4 +.\} +.nf +# nvme zns close\-zone /dev/nvme0 \-a \-n 1 +.fi +.if n \{\ +.RE +.\} +.RE +.SH "NVME" +.sp +Part of nvme\-cli diff --git a/Documentation/nvme-zns-close-zone.html b/Documentation/nvme-zns-close-zone.html new file mode 100644 index 0000000..d2e297a --- /dev/null +++ b/Documentation/nvme-zns-close-zone.html @@ -0,0 +1,841 @@ + + + + + + +nvme-zns-close-zone(1) + + + + + +
+
+

SYNOPSIS

+
+
+
nvme zns close-zone nvme zns id-ctrl <device> [--namespace-id=<NUM> | -n <NUM>]
+                                                [--start-lba=<LBA> | -s <LBA>]
+                                                [--select-all | -a]
+
+
+
+
+
+

DESCRIPTION

+
+

For the NVMe device given, issues the Zone Management Send command with the +"Close Zone" action. This will transition the zone to the closed state.

+

The <device> parameter is mandatory and may be either the NVMe character +device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1).

+
+
+
+

OPTIONS

+
+
+
+-n <NUM> +
+
+--namespace-id=<NUM> +
+
+

+ Use the provided namespace id for the command. If not provided, the + namespace id of the block device will be used. If the command is issued + to a non-block device, the parameter is required. +

+
+
+-s <lba> +
+
+--start-lba=<lba> +
+
+

+ The starting LBA of the zone to close. +

+
+
+-a +
+
+--select-all +
+
+

+ Select all zones for this action +

+
+
+
+
+
+

EXAMPLES

+
+
    +
  • +

    +Close all zones on namespace 1: +

    +
    +
    +
    # nvme zns close-zone /dev/nvme0 -a -n 1
    +
    +
  • +
+
+
+
+

NVME

+
+

Part of nvme-cli

+
+
+
+

+ + + diff --git a/Documentation/nvme-zns-close-zone.txt b/Documentation/nvme-zns-close-zone.txt new file mode 100644 index 0000000..7b47bf0 --- /dev/null +++ b/Documentation/nvme-zns-close-zone.txt @@ -0,0 +1,49 @@ +nvme-zns-close-zone(1) +====================== + +NAME +---- +nvme-zns-close-zone - Closes one or all zones + +SYNOPSIS +-------- +[verse] +'nvme zns close-zone nvme zns id-ctrl' [--namespace-id= | -n ] + [--start-lba= | -s ] + [--select-all | -a] + +DESCRIPTION +----------- +For the NVMe device given, issues the Zone Management Send command with the +"Close Zone" action. This will transition the zone to the closed state. + +The parameter is mandatory and may be either the NVMe character +device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1). + +OPTIONS +------- +-n :: +--namespace-id=:: + Use the provided namespace id for the command. If not provided, the + namespace id of the block device will be used. If the command is issued + to a non-block device, the parameter is required. + +-s :: +--start-lba=:: + The starting LBA of the zone to close. + +-a:: +--select-all:: + Select all zones for this action + +EXAMPLES +-------- +* Close all zones on namespace 1: ++ +------------ +# nvme zns close-zone /dev/nvme0 -a -n 1 +------------ + +NVME +---- +Part of nvme-cli diff --git a/Documentation/nvme-zns-finish-zone.1 b/Documentation/nvme-zns-finish-zone.1 new file mode 100644 index 0000000..764c683 --- /dev/null +++ b/Documentation/nvme-zns-finish-zone.1 @@ -0,0 +1,84 @@ +'\" t +.\" Title: nvme-zns-finish-zone +.\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] +.\" Generator: DocBook XSL Stylesheets vsnapshot +.\" Date: 10/20/2020 +.\" Manual: NVMe Manual +.\" Source: NVMe +.\" Language: English +.\" +.TH "NVME\-ZNS\-FINISH\-Z" "1" "10/20/2020" "NVMe" "NVMe Manual" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +nvme-zns-finish-zone \- Finishes one or all zones +.SH "SYNOPSIS" +.sp +.nf +\fInvme zns finish\-zone nvme zns id\-ctrl\fR [\-\-namespace\-id= | \-n ] + [\-\-start\-lba= | \-s ] + [\-\-select\-all | \-a] +.fi +.SH "DESCRIPTION" +.sp +For the NVMe device given, issues the Zone Management Send command with the "Finish Zone" action\&. This will transition the zone to the full state on success\&. +.sp +The parameter is mandatory and may be either the NVMe character device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1)\&. +.SH "OPTIONS" +.PP +\-n , \-\-namespace\-id= +.RS 4 +Use the provided namespace id for the command\&. If not provided, the namespace id of the block device will be used\&. If the command is issued to a non\-block device, the parameter is required\&. +.RE +.PP +\-s , \-\-start\-lba= +.RS 4 +The starting LBA of the zone to finish\&. +.RE +.PP +\-a, \-\-select\-all +.RS 4 +Select all zones for this action\&. +.RE +.SH "EXAMPLES" +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Finish all zones on namespace 1: +.sp +.if n \{\ +.RS 4 +.\} +.nf +# nvme zns finish\-zone /dev/nvme0 \-a \-n 1 +.fi +.if n \{\ +.RE +.\} +.RE +.SH "NVME" +.sp +Part of nvme\-cli diff --git a/Documentation/nvme-zns-finish-zone.html b/Documentation/nvme-zns-finish-zone.html new file mode 100644 index 0000000..36b833f --- /dev/null +++ b/Documentation/nvme-zns-finish-zone.html @@ -0,0 +1,842 @@ + + + + + + +nvme-zns-finish-zone(1) + + + + + +
+
+

SYNOPSIS

+
+
+
nvme zns finish-zone nvme zns id-ctrl <device> [--namespace-id=<NUM> | -n <NUM>]
+                                                [--start-lba=<LBA> | -s <LBA>]
+                                                [--select-all | -a]
+
+
+
+
+
+

DESCRIPTION

+
+

For the NVMe device given, issues the Zone Management Send command with the +"Finish Zone" action. This will transition the zone to the full state on +success.

+

The <device> parameter is mandatory and may be either the NVMe character +device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1).

+
+
+
+

OPTIONS

+
+
+
+-n <NUM> +
+
+--namespace-id=<NUM> +
+
+

+ Use the provided namespace id for the command. If not provided, the + namespace id of the block device will be used. If the command is issued + to a non-block device, the parameter is required. +

+
+
+-s <lba> +
+
+--start-lba=<lba> +
+
+

+ The starting LBA of the zone to finish. +

+
+
+-a +
+
+--select-all +
+
+

+ Select all zones for this action. +

+
+
+
+
+
+

EXAMPLES

+
+
    +
  • +

    +Finish all zones on namespace 1: +

    +
    +
    +
    # nvme zns finish-zone /dev/nvme0 -a -n 1
    +
    +
  • +
+
+
+
+

NVME

+
+

Part of nvme-cli

+
+
+
+

+ + + diff --git a/Documentation/nvme-zns-finish-zone.txt b/Documentation/nvme-zns-finish-zone.txt new file mode 100644 index 0000000..9a9a2f0 --- /dev/null +++ b/Documentation/nvme-zns-finish-zone.txt @@ -0,0 +1,50 @@ +nvme-zns-finish-zone(1) +======================= + +NAME +---- +nvme-zns-finish-zone - Finishes one or all zones + +SYNOPSIS +-------- +[verse] +'nvme zns finish-zone nvme zns id-ctrl' [--namespace-id= | -n ] + [--start-lba= | -s ] + [--select-all | -a] + +DESCRIPTION +----------- +For the NVMe device given, issues the Zone Management Send command with the +"Finish Zone" action. This will transition the zone to the full state on +success. + +The parameter is mandatory and may be either the NVMe character +device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1). + +OPTIONS +------- +-n :: +--namespace-id=:: + Use the provided namespace id for the command. If not provided, the + namespace id of the block device will be used. If the command is issued + to a non-block device, the parameter is required. + +-s :: +--start-lba=:: + The starting LBA of the zone to finish. + +-a:: +--select-all:: + Select all zones for this action. + +EXAMPLES +-------- +* Finish all zones on namespace 1: ++ +------------ +# nvme zns finish-zone /dev/nvme0 -a -n 1 +------------ + +NVME +---- +Part of nvme-cli diff --git a/Documentation/nvme-zns-id-ctrl.1 b/Documentation/nvme-zns-id-ctrl.1 new file mode 100644 index 0000000..7b444c5 --- /dev/null +++ b/Documentation/nvme-zns-id-ctrl.1 @@ -0,0 +1,98 @@ +'\" t +.\" Title: nvme-zns-id-ctrl +.\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] +.\" Generator: DocBook XSL Stylesheets vsnapshot +.\" Date: 10/20/2020 +.\" Manual: NVMe Manual +.\" Source: NVMe +.\" Language: English +.\" +.TH "NVME\-ZNS\-ID\-CTRL" "1" "10/20/2020" "NVMe" "NVMe Manual" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +nvme-zns-id-ctrl \- Send NVMe Zoned Command Set Identify Controller, return result and structure +.SH "SYNOPSIS" +.sp +.nf +\fInvme zns id\-ctrl\fR [\-o | \-\-output\-format=] +.fi +.SH "DESCRIPTION" +.sp +For the NVMe device given, sends the zoned command set\(cqs identify controller command and provides the result and returned structure\&. +.sp +The parameter is mandatory and may be either the NVMe character device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1)\&. +.sp +On success, the data structure returned by the device will be decoded and displayed in one of several ways\&. +.SH "OPTIONS" +.PP +\-o , \-\-output\-format= +.RS 4 +Set the reporting format to +\fInormal\fR, +\fIjson\fR, or +\fIbinary\fR\&. Only one output format can be used at a time\&. +.RE +.SH "EXAMPLES" +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Has the program interpret the returned buffer and display the known fields in a human readable format: +.sp +.if n \{\ +.RS 4 +.\} +.nf +# nvme zns id\-ctrl /dev/nvme0 +.fi +.if n \{\ +.RE +.\} +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Show the output in json format +.sp +.if n \{\ +.RS 4 +.\} +.nf +# nvme zns id\-ctrl /dev/nvme0 \-o json +.fi +.if n \{\ +.RE +.\} +.RE +.SH "NVME" +.sp +Part of nvme\-cli diff --git a/Documentation/nvme-zns-id-ctrl.html b/Documentation/nvme-zns-id-ctrl.html new file mode 100644 index 0000000..cc892a5 --- /dev/null +++ b/Documentation/nvme-zns-id-ctrl.html @@ -0,0 +1,828 @@ + + + + + + +nvme-zns-id-ctrl(1) + + + + + +
+
+

SYNOPSIS

+
+
+
nvme zns id-ctrl <device> [-o <fmt> | --output-format=<fmt>]
+
+
+
+
+
+

DESCRIPTION

+
+

For the NVMe device given, sends the zoned command set’s identify controller +command and provides the result and returned structure.

+

The <device> parameter is mandatory and may be either the NVMe character +device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1).

+

On success, the data structure returned by the device will be decoded and +displayed in one of several ways.

+
+
+
+

OPTIONS

+
+
+
+-o <format> +
+
+--output-format=<format> +
+
+

+ Set the reporting format to normal, json, or + binary. Only one output format can be used at a time. +

+
+
+
+
+
+

EXAMPLES

+
+
    +
  • +

    +Has the program interpret the returned buffer and display the known +fields in a human readable format: +

    +
    +
    +
    # nvme zns id-ctrl /dev/nvme0
    +
    +
  • +
  • +

    +Show the output in json format +

    +
    +
    +
    # nvme zns id-ctrl /dev/nvme0 -o json
    +
    +
  • +
+
+
+
+

NVME

+
+

Part of nvme-cli

+
+
+
+

+ + + diff --git a/Documentation/nvme-zns-id-ctrl.txt b/Documentation/nvme-zns-id-ctrl.txt new file mode 100644 index 0000000..e7bd5ba --- /dev/null +++ b/Documentation/nvme-zns-id-ctrl.txt @@ -0,0 +1,50 @@ +nvme-zns-id-ctrl(1) +=================== + +NAME +---- +nvme-zns-id-ctrl - Send NVMe Zoned Command Set Identify Controller, return + result and structure + +SYNOPSIS +-------- +[verse] +'nvme zns id-ctrl' [-o | --output-format=] + +DESCRIPTION +----------- +For the NVMe device given, sends the zoned command set's identify controller +command and provides the result and returned structure. + +The parameter is mandatory and may be either the NVMe character +device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1). + +On success, the data structure returned by the device will be decoded and +displayed in one of several ways. + +OPTIONS +------- +-o :: +--output-format=:: + Set the reporting format to 'normal', 'json', or + 'binary'. Only one output format can be used at a time. + +EXAMPLES +-------- +* Has the program interpret the returned buffer and display the known +fields in a human readable format: ++ +------------ +# nvme zns id-ctrl /dev/nvme0 +------------ ++ + +* Show the output in json format ++ +------------ +# nvme zns id-ctrl /dev/nvme0 -o json +------------ + +NVME +---- +Part of nvme-cli diff --git a/Documentation/nvme-zns-id-ns.1 b/Documentation/nvme-zns-id-ns.1 new file mode 100644 index 0000000..979bea5 --- /dev/null +++ b/Documentation/nvme-zns-id-ns.1 @@ -0,0 +1,110 @@ +'\" t +.\" Title: nvme-zns-id-ns +.\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] +.\" Generator: DocBook XSL Stylesheets vsnapshot +.\" Date: 10/20/2020 +.\" Manual: NVMe Manual +.\" Source: NVMe +.\" Language: English +.\" +.TH "NVME\-ZNS\-ID\-NS" "1" "10/20/2020" "NVMe" "NVMe Manual" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +nvme-zns-id-ns \- Send NVMe Zoned Command Set Identify Controller, return result and structure +.SH "SYNOPSIS" +.sp +.nf +\fInvme zns id\-ns\fR [\-\-namespace\-id= | \-n ] + [\-o | \-\-output\-format=] + [\-v | \-\-verbose] +.fi +.SH "DESCRIPTION" +.sp +For the NVMe device given, sends the zoned command set\(cqs identify namepsace command and provides the result and returned structure\&. +.sp +The parameter is mandatory and may be either the NVMe character device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1)\&. +.sp +On success, the data structure returned by the device will be decoded and displayed in one of several ways\&. +.SH "OPTIONS" +.PP +\-n , \-\-namespace\-id= +.RS 4 +Use the provided namespace id for the command\&. If not provided, the namespace id of the block device will be used\&. If the command is issued to a non\-block device, the parameter is required\&. +.RE +.PP +\-v, \-\-verbose +.RS 4 +Increase the information detail in the output\&. +.RE +.PP +\-o , \-\-output\-format= +.RS 4 +Set the reporting format to +\fInormal\fR, +\fIjson\fR, or +\fIbinary\fR\&. Only one output format can be used at a time\&. +.RE +.SH "EXAMPLES" +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Has the program interpret the returned buffer and display the known fields in a human readable format: +.sp +.if n \{\ +.RS 4 +.\} +.nf +# nvme zns id\-ns /dev/nvme0 \-n 1 +.fi +.if n \{\ +.RE +.\} +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Show the output in json format with extra details +.sp +.if n \{\ +.RS 4 +.\} +.nf +# nvme zns id\-ns /dev/nvme0 \-o json \-v +.fi +.if n \{\ +.RE +.\} +.RE +.SH "NVME" +.sp +Part of nvme\-cli diff --git a/Documentation/nvme-zns-id-ns.html b/Documentation/nvme-zns-id-ns.html new file mode 100644 index 0000000..45393db --- /dev/null +++ b/Documentation/nvme-zns-id-ns.html @@ -0,0 +1,854 @@ + + + + + + +nvme-zns-id-ctrl(1) + + + + + +
+
+

SYNOPSIS

+
+
+
nvme zns id-ctrl <device>  [--namespace-id=<NUM> | -n <NUM>]
+                             [-o <fmt> | --output-format=<fmt>]
+                             [-v | --verbose]
+
+
+
+
+
+

DESCRIPTION

+
+

For the NVMe device given, sends the zoned command set’s identify namepsace +command and provides the result and returned structure.

+

The <device> parameter is mandatory and may be either the NVMe character +device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1).

+

On success, the data structure returned by the device will be decoded and +displayed in one of several ways.

+
+
+
+

OPTIONS

+
+
+
+-n <NUM> +
+
+--namespace-id=<NUM> +
+
+

+ Use the provided namespace id for the command. If not provided, the + namespace id of the block device will be used. If the command is issued + to a non-block device, the parameter is required. +

+
+
+-v +
+
+--verbose +
+
+

+ Increase the information detail in the output. +

+
+
+-o <format> +
+
+--output-format=<format> +
+
+

+ Set the reporting format to normal, json, or + binary. Only one output format can be used at a time. +

+
+
+
+
+
+

EXAMPLES

+
+
    +
  • +

    +Has the program interpret the returned buffer and display the known +fields in a human readable format: +

    +
    +
    +
    # nvme zns id-ns /dev/nvme0 -n 1
    +
    +
  • +
  • +

    +Show the output in json format with extra details +

    +
    +
    +
    # nvme zns id-ctrl /dev/nvme0 -o json -v
    +
    +
  • +
+
+
+
+

NVME

+
+

Part of nvme-cli

+
+
+
+

+ + + diff --git a/Documentation/nvme-zns-id-ns.txt b/Documentation/nvme-zns-id-ns.txt new file mode 100644 index 0000000..a95d975 --- /dev/null +++ b/Documentation/nvme-zns-id-ns.txt @@ -0,0 +1,62 @@ +nvme-zns-id-ns(1) +================= + +NAME +---- +nvme-zns-id-ns - Send NVMe Zoned Command Set Identify Controller, return + result and structure + +SYNOPSIS +-------- +[verse] +'nvme zns id-ns' [--namespace-id= | -n ] + [-o | --output-format=] + [-v | --verbose] + +DESCRIPTION +----------- +For the NVMe device given, sends the zoned command set's identify namepsace +command and provides the result and returned structure. + +The parameter is mandatory and may be either the NVMe character +device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1). + +On success, the data structure returned by the device will be decoded and +displayed in one of several ways. + +OPTIONS +------- +-n :: +--namespace-id=:: + Use the provided namespace id for the command. If not provided, the + namespace id of the block device will be used. If the command is issued + to a non-block device, the parameter is required. + +-v:: +--verbose:: + Increase the information detail in the output. + +-o :: +--output-format=:: + Set the reporting format to 'normal', 'json', or + 'binary'. Only one output format can be used at a time. + +EXAMPLES +-------- +* Has the program interpret the returned buffer and display the known +fields in a human readable format: ++ +------------ +# nvme zns id-ns /dev/nvme0 -n 1 +------------ ++ + +* Show the output in json format with extra details ++ +------------ +# nvme zns id-ns /dev/nvme0 -o json -v +------------ + +NVME +---- +Part of nvme-cli diff --git a/Documentation/nvme-zns-offline-zone.1 b/Documentation/nvme-zns-offline-zone.1 new file mode 100644 index 0000000..9d2d083 --- /dev/null +++ b/Documentation/nvme-zns-offline-zone.1 @@ -0,0 +1,84 @@ +'\" t +.\" Title: nvme-zns-offline-zone +.\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] +.\" Generator: DocBook XSL Stylesheets vsnapshot +.\" Date: 10/20/2020 +.\" Manual: NVMe Manual +.\" Source: NVMe +.\" Language: English +.\" +.TH "NVME\-ZNS\-OFFLINE\-" "1" "10/20/2020" "NVMe" "NVMe Manual" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +nvme-zns-offline-zone \- Offlines one or all zones +.SH "SYNOPSIS" +.sp +.nf +\fInvme zns offline\-zone nvme zns id\-ctrl\fR [\-\-namespace\-id= | \-n ] + [\-\-start\-lba= | \-s ] + [\-\-select\-all | \-a] +.fi +.SH "DESCRIPTION" +.sp +For the NVMe device given, issues the Zone Management Send command with the "Offline Zone" action\&. This will transition the zone to the offlined state\&. +.sp +The parameter is mandatory and may be either the NVMe character device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1)\&. +.SH "OPTIONS" +.PP +\-n , \-\-namespace\-id= +.RS 4 +Use the provided namespace id for the command\&. If not provided, the namespace id of the block device will be used\&. If the command is issued to a non\-block device, the parameter is required\&. +.RE +.PP +\-s , \-\-start\-lba= +.RS 4 +The starting LBA of the zone to offline\&. +.RE +.PP +\-a, \-\-select\-all +.RS 4 +Select all zones for this action +.RE +.SH "EXAMPLES" +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Offline all zones on namespace 1: +.sp +.if n \{\ +.RS 4 +.\} +.nf +# nvme zns offline\-zone /dev/nvme0 \-a \-n 1 +.fi +.if n \{\ +.RE +.\} +.RE +.SH "NVME" +.sp +Part of nvme\-cli diff --git a/Documentation/nvme-zns-offline-zone.html b/Documentation/nvme-zns-offline-zone.html new file mode 100644 index 0000000..ae2da7f --- /dev/null +++ b/Documentation/nvme-zns-offline-zone.html @@ -0,0 +1,841 @@ + + + + + + +nvme-zns-offline-zone(1) + + + + + +
+
+

SYNOPSIS

+
+
+
nvme zns offline-zone nvme zns id-ctrl <device> [--namespace-id=<NUM> | -n <NUM>]
+                                                [--start-lba=<LBA> | -s <LBA>]
+                                                [--select-all | -a]
+
+
+
+
+
+

DESCRIPTION

+
+

For the NVMe device given, issues the Zone Management Send command with the +"Offline Zone" action. This will transition the zone to the offlined state.

+

The <device> parameter is mandatory and may be either the NVMe character +device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1).

+
+
+
+

OPTIONS

+
+
+
+-n <NUM> +
+
+--namespace-id=<NUM> +
+
+

+ Use the provided namespace id for the command. If not provided, the + namespace id of the block device will be used. If the command is issued + to a non-block device, the parameter is required. +

+
+
+-s <lba> +
+
+--start-lba=<lba> +
+
+

+ The starting LBA of the zone to offline. +

+
+
+-a +
+
+--select-all +
+
+

+ Select all zones for this action +

+
+
+
+
+
+

EXAMPLES

+
+
    +
  • +

    +Offline all zones on namespace 1: +

    +
    +
    +
    # nvme zns offline-zone /dev/nvme0 -a -n 1
    +
    +
  • +
+
+
+
+

NVME

+
+

Part of nvme-cli

+
+
+
+

+ + + diff --git a/Documentation/nvme-zns-offline-zone.txt b/Documentation/nvme-zns-offline-zone.txt new file mode 100644 index 0000000..0a35235 --- /dev/null +++ b/Documentation/nvme-zns-offline-zone.txt @@ -0,0 +1,49 @@ +nvme-zns-offline-zone(1) +======================== + +NAME +---- +nvme-zns-offline-zone - Offlines one or all zones + +SYNOPSIS +-------- +[verse] +'nvme zns offline-zone nvme zns id-ctrl' [--namespace-id= | -n ] + [--start-lba= | -s ] + [--select-all | -a] + +DESCRIPTION +----------- +For the NVMe device given, issues the Zone Management Send command with the +"Offline Zone" action. This will transition the zone to the offlined state. + +The parameter is mandatory and may be either the NVMe character +device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1). + +OPTIONS +------- +-n :: +--namespace-id=:: + Use the provided namespace id for the command. If not provided, the + namespace id of the block device will be used. If the command is issued + to a non-block device, the parameter is required. + +-s :: +--start-lba=:: + The starting LBA of the zone to offline. + +-a:: +--select-all:: + Select all zones for this action + +EXAMPLES +-------- +* Offline all zones on namespace 1: ++ +------------ +# nvme zns offline-zone /dev/nvme0 -a -n 1 +------------ + +NVME +---- +Part of nvme-cli diff --git a/Documentation/nvme-zns-open-zone.1 b/Documentation/nvme-zns-open-zone.1 new file mode 100644 index 0000000..8ea78f2 --- /dev/null +++ b/Documentation/nvme-zns-open-zone.1 @@ -0,0 +1,84 @@ +'\" t +.\" Title: nvme-zns-open-zone +.\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] +.\" Generator: DocBook XSL Stylesheets vsnapshot +.\" Date: 10/20/2020 +.\" Manual: NVMe Manual +.\" Source: NVMe +.\" Language: English +.\" +.TH "NVME\-ZNS\-OPEN\-ZON" "1" "10/20/2020" "NVMe" "NVMe Manual" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +nvme-zns-open-zone \- Opens one or all zones +.SH "SYNOPSIS" +.sp +.nf +\fInvme zns open\-zone nvme zns id\-ctrl\fR [\-\-namespace\-id= | \-n ] + [\-\-start\-lba= | \-s ] + [\-\-select\-all | \-a] +.fi +.SH "DESCRIPTION" +.sp +For the NVMe device given, issues the Zone Management Send command with the "Open Zone" action\&. This will transition the zone to the opened state\&. +.sp +The parameter is mandatory and may be either the NVMe character device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1)\&. +.SH "OPTIONS" +.PP +\-n , \-\-namespace\-id= +.RS 4 +Use the provided namespace id for the command\&. If not provided, the namespace id of the block device will be used\&. If the command is issued to a non\-block device, the parameter is required\&. +.RE +.PP +\-s , \-\-start\-lba= +.RS 4 +The starting LBA of the zone to open\&. +.RE +.PP +\-a, \-\-select\-all +.RS 4 +Select all zones for this action +.RE +.SH "EXAMPLES" +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Open the first zone on namespace 1: +.sp +.if n \{\ +.RS 4 +.\} +.nf +# nvme zns open\-zone /dev/nvme0 \-n 1 \-s 0 +.fi +.if n \{\ +.RE +.\} +.RE +.SH "NVME" +.sp +Part of nvme\-cli diff --git a/Documentation/nvme-zns-open-zone.html b/Documentation/nvme-zns-open-zone.html new file mode 100644 index 0000000..5a648b2 --- /dev/null +++ b/Documentation/nvme-zns-open-zone.html @@ -0,0 +1,841 @@ + + + + + + +nvme-zns-open-zone(1) + + + + + +
+
+

SYNOPSIS

+
+
+
nvme zns open-zone nvme zns id-ctrl <device> [--namespace-id=<NUM> | -n <NUM>]
+                                                [--start-lba=<LBA> | -s <LBA>]
+                                                [--select-all | -a]
+
+
+
+
+
+

DESCRIPTION

+
+

For the NVMe device given, issues the Zone Management Send command with the +"Open Zone" action. This will transition the zone to the opened state.

+

The <device> parameter is mandatory and may be either the NVMe character +device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1).

+
+
+
+

OPTIONS

+
+
+
+-n <NUM> +
+
+--namespace-id=<NUM> +
+
+

+ Use the provided namespace id for the command. If not provided, the + namespace id of the block device will be used. If the command is issued + to a non-block device, the parameter is required. +

+
+
+-s <lba> +
+
+--start-lba=<lba> +
+
+

+ The starting LBA of the zone to open. +

+
+
+-a +
+
+--select-all +
+
+

+ Select all zones for this action +

+
+
+
+
+
+

EXAMPLES

+
+
    +
  • +

    +Open the first zone on namespace 1: +

    +
    +
    +
    # nvme zns open-zone /dev/nvme0 -n 1 -s 0
    +
    +
  • +
+
+
+
+

NVME

+
+

Part of nvme-cli

+
+
+
+

+ + + diff --git a/Documentation/nvme-zns-open-zone.txt b/Documentation/nvme-zns-open-zone.txt new file mode 100644 index 0000000..23acf1c --- /dev/null +++ b/Documentation/nvme-zns-open-zone.txt @@ -0,0 +1,49 @@ +nvme-zns-open-zone(1) +====================== + +NAME +---- +nvme-zns-open-zone - Opens one or all zones + +SYNOPSIS +-------- +[verse] +'nvme zns open-zone nvme zns id-ctrl' [--namespace-id= | -n ] + [--start-lba= | -s ] + [--select-all | -a] + +DESCRIPTION +----------- +For the NVMe device given, issues the Zone Management Send command with the +"Open Zone" action. This will transition the zone to the opened state. + +The parameter is mandatory and may be either the NVMe character +device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1). + +OPTIONS +------- +-n :: +--namespace-id=:: + Use the provided namespace id for the command. If not provided, the + namespace id of the block device will be used. If the command is issued + to a non-block device, the parameter is required. + +-s :: +--start-lba=:: + The starting LBA of the zone to open. + +-a:: +--select-all:: + Select all zones for this action + +EXAMPLES +-------- +* Open the first zone on namespace 1: ++ +------------ +# nvme zns open-zone /dev/nvme0 -n 1 -s 0 +------------ + +NVME +---- +Part of nvme-cli diff --git a/Documentation/nvme-zns-report-zones.1 b/Documentation/nvme-zns-report-zones.1 new file mode 100644 index 0000000..a1ec86e --- /dev/null +++ b/Documentation/nvme-zns-report-zones.1 @@ -0,0 +1,198 @@ +'\" t +.\" Title: nvme-zns-report-zones +.\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] +.\" Generator: DocBook XSL Stylesheets vsnapshot +.\" Date: 10/20/2020 +.\" Manual: NVMe Manual +.\" Source: NVMe +.\" Language: English +.\" +.TH "NVME\-ZNS\-REPORT\-Z" "1" "10/20/2020" "NVMe" "NVMe Manual" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +nvme-zns-report-zones \- Retrieve and display the Report Zones data structure +.SH "SYNOPSIS" +.sp +.nf +\fInvme zns report\-zones\fR [\-\-namespace\-id= | \-n ] + [\-\-start\-lba= | \-s ] + [\-\-descs= | \-d ] + [\-\-state= | \-S ] + [\-\-extended | \-e] + [\-\-partial | \-p] + [\-\-verbose | \-v] + [\-\-output\-format= | \-o ] +.fi +.SH "DESCRIPTION" +.sp +For the NVMe device given, sends the Zone Management Receive command with the Zone Receive Action set to either Report Zones or Extended Report Zones, depending on the \fIextended\fR option\&. +.sp +The parameter is mandatory and may be either the NVMe character device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1)\&. +.sp +On success, the data structure returned by the device will be decoded and displayed in one of several ways\&. +.SH "OPTIONS" +.PP +\-n , \-\-namespace\-id= +.RS 4 +Use the provided namespace id for the command\&. If not provided, the namespace id of the block device will be used\&. If the command is issued to a non\-block device, the parameter is required\&. +.RE +.PP +\-s , \-\-start\-lba= +.RS 4 +The starting LBA of the zone to begin the report +.RE +.PP +\-d , \-\-descs= +.RS 4 +The number of descriptors to request in the report\&. +.RE +.PP +\-S , \-\-state= +.RS 4 +The state of zones to request in the report\&. Known values include: +.TS +allbox tab(:); +lt lt +lt lt +lt lt +lt lt +lt lt +lt lt +lt lt +lt lt +lt lt. +T{ +Value +T}:T{ +Definition +T} +T{ +0 +T}:T{ +List all zones (default) +T} +T{ +1 +T}:T{ +Empty State +T} +T{ +2 +T}:T{ +Implicitly Opened State +T} +T{ +3 +T}:T{ +Explicitly Opened State +T} +T{ +4 +T}:T{ +Closed State +T} +T{ +5 +T}:T{ +Full State +T} +T{ +6 +T}:T{ +Read Only State +T} +T{ +7 +T}:T{ +Offline State +T} +.TE +.sp 1 +.RE +.PP +\-e, \-\-extended +.RS 4 +Request to use the Extended Report Zones option\&. The extended data is not decoded\&. +.RE +.PP +\-p, \-\-partial +.RS 4 +If set, the device will return the number of zones that match the state rather than the number of zones returned in the report\&. +.RE +.PP +\-v, \-\-verbose +.RS 4 +Increase the information detail in the output\&. +.RE +.PP +\-o , \-\-output\-format= +.RS 4 +Set the reporting format to +\fInormal\fR, +\fIjson\fR, or +\fIbinary\fR\&. Only one output format can be used at a time\&. +.RE +.SH "EXAMPLES" +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Has the program interpret the report for 16 zones, and display the known fields in a human readable format: +.sp +.if n \{\ +.RS 4 +.\} +.nf +# nvme zns report\-zones /dev/nvme0 \-n 1 \-d 16 +.fi +.if n \{\ +.RE +.\} +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Show the output in json format with extra details +.sp +.if n \{\ +.RS 4 +.\} +.nf +# nvme zns report\-zones /dev/nvme0 \-n 1 \-d 16 \-o json +.fi +.if n \{\ +.RE +.\} +.RE +.SH "NVME" +.sp +Part of nvme\-cli diff --git a/Documentation/nvme-zns-report-zones.html b/Documentation/nvme-zns-report-zones.html new file mode 100644 index 0000000..fbdde5c --- /dev/null +++ b/Documentation/nvme-zns-report-zones.html @@ -0,0 +1,964 @@ + + + + + + +nvme-zns-report-zones(1) + + + + + +
+
+

SYNOPSIS

+
+
+
nvme zns report-zones <device> [--namespace-id=<NUM> | -n <NUM>]
+                                 [--start-lba=<IONUM> | -s <IONUM>]
+                                 [--descs=<NUM> | -d <NUM>]
+                                 [--state=<NUM> | -S <NUM>]
+                                 [--extended | -e]
+                                 [--partial | -p]
+                                 [--verbose | -v]
+                                 [--output-format=<FMT> | -o <FMT>]
+
+
+
+
+
+

DESCRIPTION

+
+

For the NVMe device given, sends the Zone Management Receive command with the +Zone Receive Action set to either Report Zones or Extended Report Zones, +depending on the extended option.

+

The <device> parameter is mandatory and may be either the NVMe character +device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1).

+

On success, the data structure returned by the device will be decoded and +displayed in one of several ways.

+
+
+
+

OPTIONS

+
+
+
+-n <NUM> +
+
+--namespace-id=<NUM> +
+
+

+ Use the provided namespace id for the command. If not provided, the + namespace id of the block device will be used. If the command is issued + to a non-block device, the parameter is required. +

+
+
+-s <lba> +
+
+--start-lba=<lba> +
+
+

+ The starting LBA of the zone to begin the report +

+
+
+-d <NUM> +
+
+--descs=<NUM> +
+
+

+ The number of descriptors to request in the report. +

+
+
+-S <NUM> +
+
+--state=<NUM> +
+
+

+ The state of zones to request in the report. Known values include: +

+
+ +++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Value

Definition

0

List all zones (default)

1

Empty State

2

Implicitly Opened State

3

Explicitly Opened State

4

Closed State

5

Full State

6

Read Only State

7

Offline State

+
+
+
+-e +
+
+--extended +
+
+

+ Request to use the Extended Report Zones option. The extended data is + not decoded. +

+
+
+-p +
+
+--partial +
+
+

+ If set, the device will return the number of zones that match the state + rather than the number of zones returned in the report. +

+
+
+-v +
+
+--verbose +
+
+

+ Increase the information detail in the output. +

+
+
+-o <format> +
+
+--output-format=<format> +
+
+

+ Set the reporting format to normal, json, or + binary. Only one output format can be used at a time. +

+
+
+
+
+
+

EXAMPLES

+
+
    +
  • +

    +Has the program interpret the report for 16 zones, and display the known +fields in a human readable format: +

    +
    +
    +
    # nvme zns report-zones /dev/nvme0 -n 1 -d 16
    +
    +
  • +
  • +

    +Show the output in json format with extra details +

    +
    +
    +
    # nvme zns report-zones /dev/nvme0 -n 1 -d 16 -o json
    +
    +
  • +
+
+
+
+

NVME

+
+

Part of nvme-cli

+
+
+
+

+ + + diff --git a/Documentation/nvme-zns-report-zones.txt b/Documentation/nvme-zns-report-zones.txt new file mode 100644 index 0000000..35d5eda --- /dev/null +++ b/Documentation/nvme-zns-report-zones.txt @@ -0,0 +1,102 @@ +nvme-zns-report-zones(1) +======================== + +NAME +---- +nvme-zns-report-zones - Retrieve and display the Report Zones data structure + +SYNOPSIS +-------- +[verse] +'nvme zns report-zones' [--namespace-id= | -n ] + [--start-lba= | -s ] + [--descs= | -d ] + [--state= | -S ] + [--extended | -e] + [--partial | -p] + [--verbose | -v] + [--output-format= | -o ] + +DESCRIPTION +----------- +For the NVMe device given, sends the Zone Management Receive command with the +Zone Receive Action set to either Report Zones or Extended Report Zones, +depending on the 'extended' option. + +The parameter is mandatory and may be either the NVMe character +device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1). + +On success, the data structure returned by the device will be decoded and +displayed in one of several ways. + +OPTIONS +------- +-n :: +--namespace-id=:: + Use the provided namespace id for the command. If not provided, the + namespace id of the block device will be used. If the command is issued + to a non-block device, the parameter is required. + +-s :: +--start-lba=:: + The starting LBA of the zone to begin the report + +-d :: +--descs=:: + The number of descriptors to request in the report. + +-S :: +--state=:: + The state of zones to request in the report. Known values include: ++ +[] +|================= +|Value|Definition +|0|List all zones (default) +|1|Empty State +|2|Implicitly Opened State +|3|Explicitly Opened State +|4|Closed State +|5|Full State +|6|Read Only State +|7|Offline State +|================= + +-e:: +--extended:: + Request to use the Extended Report Zones option. The extended data is + not decoded. + +-p:: +--partial:: + If set, the device will return the number of zones that match the state + rather than the number of zones returned in the report. + +-v:: +--verbose:: + Increase the information detail in the output. + +-o :: +--output-format=:: + Set the reporting format to 'normal', 'json', or + 'binary'. Only one output format can be used at a time. + +EXAMPLES +-------- +* Has the program interpret the report for 16 zones, and display the known +fields in a human readable format: ++ +------------ +# nvme zns report-zones /dev/nvme0 -n 1 -d 16 +------------ ++ + +* Show the output in json format with extra details ++ +------------ +# nvme zns report-zones /dev/nvme0 -n 1 -d 16 -o json +------------ + +NVME +---- +Part of nvme-cli diff --git a/Documentation/nvme-zns-reset-zone.1 b/Documentation/nvme-zns-reset-zone.1 new file mode 100644 index 0000000..b02d042 --- /dev/null +++ b/Documentation/nvme-zns-reset-zone.1 @@ -0,0 +1,84 @@ +'\" t +.\" Title: nvme-zns-reset-zone +.\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] +.\" Generator: DocBook XSL Stylesheets vsnapshot +.\" Date: 10/20/2020 +.\" Manual: NVMe Manual +.\" Source: NVMe +.\" Language: English +.\" +.TH "NVME\-ZNS\-RESET\-ZO" "1" "10/20/2020" "NVMe" "NVMe Manual" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +nvme-zns-reset-zone \- Resets one or all zones +.SH "SYNOPSIS" +.sp +.nf +\fInvme zns reset\-zone\fR [\-\-namespace\-id= | \-n ] + [\-\-start\-lba= | \-s ] + [\-\-select\-all | \-a] +.fi +.SH "DESCRIPTION" +.sp +For the NVMe device given, issues the Zone Management Send command with the "Reset Zone" action\&. This will transition the zone to the empty state, setting the write pointer for each zone back to the beginning on success\&. +.sp +The parameter is mandatory and may be either the NVMe character device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1)\&. +.SH "OPTIONS" +.PP +\-n , \-\-namespace\-id= +.RS 4 +Use the provided namespace id for the command\&. If not provided, the namespace id of the block device will be used\&. If the command is issued to a non\-block device, the parameter is required\&. +.RE +.PP +\-s , \-\-start\-lba= +.RS 4 +The starting LBA of the zone to reset\&. +.RE +.PP +\-a, \-\-select\-all +.RS 4 +Select all zones for this action +.RE +.SH "EXAMPLES" +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Reset the first zone on namespace 1: +.sp +.if n \{\ +.RS 4 +.\} +.nf +# nvme zns reset\-zone /dev/nvme0 \-n 1 \-s 0 +.fi +.if n \{\ +.RE +.\} +.RE +.SH "NVME" +.sp +Part of nvme\-cli diff --git a/Documentation/nvme-zns-reset-zone.html b/Documentation/nvme-zns-reset-zone.html new file mode 100644 index 0000000..46545b2 --- /dev/null +++ b/Documentation/nvme-zns-reset-zone.html @@ -0,0 +1,842 @@ + + + + + + +nvme-zns-reset-zone(1) + + + + + +
+
+

SYNOPSIS

+
+
+
nvme zns reset-zone <device> [--namespace-id=<NUM> | -n <NUM>]
+                               [--start-lba=<LBA> | -s <LBA>]
+                               [--select-all | -a]
+
+
+
+
+
+

DESCRIPTION

+
+

For the NVMe device given, issues the Zone Management Send command with the +"Reset Zone" action. This will transition the zone to the empty state, setting +the write pointer for each zone back to the beginning on success.

+

The <device> parameter is mandatory and may be either the NVMe character +device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1).

+
+
+
+

OPTIONS

+
+
+
+-n <NUM> +
+
+--namespace-id=<NUM> +
+
+

+ Use the provided namespace id for the command. If not provided, the + namespace id of the block device will be used. If the command is issued + to a non-block device, the parameter is required. +

+
+
+-s <lba> +
+
+--start-lba=<lba> +
+
+

+ The starting LBA of the zone to reset. +

+
+
+-a +
+
+--select-all +
+
+

+ Select all zones for this action +

+
+
+
+
+
+

EXAMPLES

+
+
    +
  • +

    +Reset the first zone on namespace 1: +

    +
    +
    +
    # nvme zns reset-zone /dev/nvme0 -n 1 -s 0
    +
    +
  • +
+
+
+
+

NVME

+
+

Part of nvme-cli

+
+
+
+

+ + + diff --git a/Documentation/nvme-zns-reset-zone.txt b/Documentation/nvme-zns-reset-zone.txt new file mode 100644 index 0000000..a04fd66 --- /dev/null +++ b/Documentation/nvme-zns-reset-zone.txt @@ -0,0 +1,50 @@ +nvme-zns-reset-zone(1) +====================== + +NAME +---- +nvme-zns-reset-zone - Resets one or all zones + +SYNOPSIS +-------- +[verse] +'nvme zns reset-zone' [--namespace-id= | -n ] + [--start-lba= | -s ] + [--select-all | -a] + +DESCRIPTION +----------- +For the NVMe device given, issues the Zone Management Send command with the +"Reset Zone" action. This will transition the zone to the empty state, setting +the write pointer for each zone back to the beginning on success. + +The parameter is mandatory and may be either the NVMe character +device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1). + +OPTIONS +------- +-n :: +--namespace-id=:: + Use the provided namespace id for the command. If not provided, the + namespace id of the block device will be used. If the command is issued + to a non-block device, the parameter is required. + +-s :: +--start-lba=:: + The starting LBA of the zone to reset. + +-a:: +--select-all:: + Select all zones for this action + +EXAMPLES +-------- +* Reset the first zone on namespace 1: ++ +------------ +# nvme zns reset-zone /dev/nvme0 -n 1 -s 0 +------------ + +NVME +---- +Part of nvme-cli diff --git a/Documentation/nvme-zns-set-zone-desc.1 b/Documentation/nvme-zns-set-zone-desc.1 new file mode 100644 index 0000000..a33b06a --- /dev/null +++ b/Documentation/nvme-zns-set-zone-desc.1 @@ -0,0 +1,82 @@ +'\" t +.\" Title: nvme-zns-set-zone-desc +.\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] +.\" Generator: DocBook XSL Stylesheets vsnapshot +.\" Date: 10/20/2020 +.\" Manual: NVMe Manual +.\" Source: NVMe +.\" Language: English +.\" +.TH "NVME\-ZNS\-SET\-ZONE" "1" "10/20/2020" "NVMe" "NVMe Manual" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +nvme-zns-set-zone-desc \- Set extended descriptor data for a zone +.SH "SYNOPSIS" +.sp +.nf +\fInvme zns setzone\-desc\fR [\-\-namespace\-id= | \-n ] + [\-\-start\-lba=, \-s ] + [\-data=, \-d ] +.fi +.SH "DESCRIPTION" +.sp +For the NVMe device given, issues the Zone Management Send command with the Set Zone Descriptor Extenions action\&. The input will default to stdin\&. Alternatively, the data may come from a file that can be specified\&. The data length will automatically be calculated from the zns identify namesapce\&. +.SH "OPTIONS" +.PP +\-n , \-\-namespace\-id= +.RS 4 +Use the provided namespace id for the command\&. If not provided, the namespace id of the block device will be used\&. If the command is issued to a non\-block device, the parameter is required\&. +.RE +.PP +\-s , \-\-start\-lba= +.RS 4 +The starting LBA of the zone to manage send\&. +.RE +.PP +\-d +.RS 4 +Optional file for data (default stdin) +.RE +.SH "EXAMPLES" +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Write "hello world" into the zone descriptor for namespace 1\(cqs first zone (requires device supports a large enough zone extended data) +.sp +.if n \{\ +.RS 4 +.\} +.nf +# echo "hello world" | nvme zns set\-zone\-desc /dev/nvme0 \-n 1 \-s 0 +.fi +.if n \{\ +.RE +.\} +.RE +.SH "NVME" +.sp +Part of nvme\-cli diff --git a/Documentation/nvme-zns-set-zone-desc.html b/Documentation/nvme-zns-set-zone-desc.html new file mode 100644 index 0000000..b69be1f --- /dev/null +++ b/Documentation/nvme-zns-set-zone-desc.html @@ -0,0 +1,842 @@ + + + + + + +nvme-zns-set-zone-desc(1) + + + + + +
+
+

SYNOPSIS

+
+
+
nvme zns setzone-desc <device> [--namespace-id=<NUM> | -n <NUM>]
+                                 [--start-lba=<IONUM>, -s <IONUM>]
+                                 [-data=<FILE>, -d <FILE>]
+
+
+
+
+
+

DESCRIPTION

+
+

For the NVMe device given, issues the Zone Management Send command with the +Set Zone Descriptor Extenions action. The input will default to stdin. +Alternatively, the data may come from a file that can be specified. The data +length will automatically be calculated from the zns identify namesapce.

+
+
+
+

OPTIONS

+
+
+
+-n <NUM> +
+
+--namespace-id=<NUM> +
+
+

+ Use the provided namespace id for the command. If not provided, the + namespace id of the block device will be used. If the command is issued + to a non-block device, the parameter is required. +

+
+
+-s <lba> +
+
+--start-lba=<lba> +
+
+

+ The starting LBA of the zone to manage send. +

+
+
+-d <FILE +
+
+-data=<FILE> +
+
+

+ Optional file for data (default stdin) +

+
+
+
+
+
+

EXAMPLES

+
+
    +
  • +

    +Write "hello world" into the zone descriptor for namespace 1’s first zone + (requires device supports a large enough zone extended data) +

    +
    +
    +
    # echo "hello world" | nvme zns set-zone-desc /dev/nvme0 -n 1 -s 0
    +
    +
  • +
+
+
+
+

NVME

+
+

Part of nvme-cli

+
+
+
+

+ + + diff --git a/Documentation/nvme-zns-set-zone-desc.txt b/Documentation/nvme-zns-set-zone-desc.txt new file mode 100644 index 0000000..5df3ecb --- /dev/null +++ b/Documentation/nvme-zns-set-zone-desc.txt @@ -0,0 +1,50 @@ +nvme-zns-set-zone-desc(1) +========================= + +NAME +---- +nvme-zns-set-zone-desc - Set extended descriptor data for a zone + +SYNOPSIS +-------- +[verse] +'nvme zns setzone-desc' [--namespace-id= | -n ] + [--start-lba=, -s ] + [-data=, -d ] + +DESCRIPTION +----------- +For the NVMe device given, issues the Zone Management Send command with the +Set Zone Descriptor Extenions action. The input will default to stdin. +Alternatively, the data may come from a file that can be specified. The data +length will automatically be calculated from the zns identify namesapce. + +OPTIONS +------- +-n :: +--namespace-id=:: + Use the provided namespace id for the command. If not provided, the + namespace id of the block device will be used. If the command is issued + to a non-block device, the parameter is required. + +-s :: +--start-lba=:: + The starting LBA of the zone to manage send. + +-d :: + Optional file for data (default stdin) + + +EXAMPLES +-------- +* Write "hello world" into the zone descriptor for namespace 1's first zone + (requires device supports a large enough zone extended data) ++ +------------ +# echo "hello world" | nvme zns set-zone-desc /dev/nvme0 -n 1 -s 0 +------------ + +NVME +---- +Part of nvme-cli diff --git a/Documentation/nvme-zns-zone-append.1 b/Documentation/nvme-zns-zone-append.1 new file mode 100644 index 0000000..494e265 --- /dev/null +++ b/Documentation/nvme-zns-zone-append.1 @@ -0,0 +1,133 @@ +'\" t +.\" Title: nvme-zns-zone-append +.\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] +.\" Generator: DocBook XSL Stylesheets vsnapshot +.\" Date: 10/20/2020 +.\" Manual: NVMe Manual +.\" Source: NVMe +.\" Language: English +.\" +.TH "NVME\-ZNS\-ZONE\-APP" "1" "10/20/2020" "NVMe" "NVMe Manual" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +nvme-zns-zone-append \- Send an NVMe write command, provide results +.SH "SYNOPSIS" +.sp +.nf +\fInvme\-zns\-zone\-append\fR [\-\-namespace\-id= | \-n ] + [\-\-zslba= | \-s ] + [\-\-data\-size= | \-z ] + [\-\-metadata\-size= | \-y ] + [\-\-data= | \-d ] + [\-\-metadata= | \-M ] + [\-\-limited\-retry | \-l] + [\-\-force\-unit\-access | \-f] + [\-\-ref\-tag= | \-r ] + [\-\-app\-tag\-mask= | \-m ] + [\-\-app\-tag= | \-a ] + [\-\-prinfo= | \-p ] +.fi +.SH "DESCRIPTION" +.sp +The zone append command writes the logical blocks specified by the command to the medium from the data data buffer provided\&. Will use stdin by default if you don\(cqt provide a file\&. +.sp +On sucess, the program will report the LBA that was assigned to the data for the append operation\&. +.SH "OPTIONS" +.PP +\-n , \-\-namespace\-id= +.RS 4 +Use the provided namespace id for the command\&. If not provided, the namespace id of the block device will be used\&. If the command is issued to a non\-block device, the parameter is required\&. +.RE +.PP +\-s , \-\-zslba=, \-z , \-\-data\-size= +.RS 4 +Size of data, in bytes\&. +.RE +.PP +\-y , \-\-metadata\-size= +.RS 4 +Size of metadata in bytes\&. +.RE +.PP +\-d , \-\-data= +.RS 4 +Data file providing the data to write\&. If none provided, contents are sent from STDIN\&. +.RE +.PP +\-M , \-\-metadata= +.RS 4 +Metadata file, if necessary\&. +.RE +.PP +\-l, \-\-limited\-retry +.RS 4 +Sets the limited retry flag\&. +.RE +.PP +\-f, \-\-force\-unit\-access +.RS 4 +Set the force\-unit access flag\&. +.RE +.PP +\-r , \-\-ref\-tag= +.RS 4 +Optional reftag when used with protection information\&. +.RE +.PP +\-m , \-\-app\-tag\-mask= +.RS 4 +Optional application tag mask when used with protection information\&. +.RE +.PP +\-a , \-\-app\-tag= +.RS 4 +Optional application tag when used with protection information\&. +.RE +.PP +\-p , \-\-prinfo= +.RS 4 +Protection Information field definition\&. +.RE +.SH "EXAMPLES" +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Append the data "hello world" into 4k worth of blocks into the zone starting at block 0 for namespace 1: +.sp +.if n \{\ +.RS 4 +.\} +.nf +# echo "hello world" | nvme zns zone\-append /dev/nvme0 \-n 1 \-s 0 \-z 4k +.fi +.if n \{\ +.RE +.\} +.RE +.SH "NVME" +.sp +Part of the nvme\-user suite diff --git a/Documentation/nvme-zns-zone-append.html b/Documentation/nvme-zns-zone-append.html new file mode 100644 index 0000000..5acc06a --- /dev/null +++ b/Documentation/nvme-zns-zone-append.html @@ -0,0 +1,947 @@ + + + + + + +nvme-zns-zone-append(1) + + + + + +
+
+

SYNOPSIS

+
+
+
nvme-zns-zone-append <device> [--namespace-id=<NUM> | -n <NUM>]
+                                [--zslba=<IONUM> | -s <IONUM>]
+                                [--data-size=<IONUM> | -z <IONUM>]
+                                [--metadata-size=<IONUM> | -y <IONUM>]
+                                [--data=<FILE> | -d <FILE>]
+                                [--metadata=<FILE> | -M <FILE>]
+                                [--limited-retry | -l]
+                                [--force-unit-access | -f]
+                                [--ref-tag=<NUM> | -r <NUM>]
+                                [--app-tag-mask=<NUM> | -m <NUM>]
+                                [--app-tag=<NUM> | -a <NUM>]
+                                [--prinfo=<NUM> | -p <NUM>]
+
+
+
+
+
+

DESCRIPTION

+
+

The zone append command writes the logical blocks specified by the command to +the medium from the data data buffer provided. Will use stdin by default +if you don’t provide a file.

+

On sucess, the program will report the LBA that was assigned to the data for +the append operation.

+
+
+
+

OPTIONS

+
+
+
+-n <NUM> +
+
+--namespace-id=<NUM> +
+
+

+ Use the provided namespace id for the command. If not provided, the + namespace id of the block device will be used. If the command is issued + to a non-block device, the parameter is required. +

+
+
+-s <IONUM> +
+
+--zslba=<IONUM> +
+
+-z <IONUM> +
+
+--data-size=<IONUM> +
+
+

+ Size of data, in bytes. +

+
+
+-y <IONUM> +
+
+--metadata-size=<IONUM> +
+
+

+ Size of metadata in bytes. +

+
+
+-d <FILE> +
+
+--data=<FILE> +
+
+

+ Data file providing the data to write. If none provided, contents are + sent from STDIN. +

+
+
+-M <FILE> +
+
+--metadata=<FILE> +
+
+

+ Metadata file, if necessary. +

+
+
+-l +
+
+--limited-retry +
+
+

+ Sets the limited retry flag. +

+
+
+-f +
+
+--force-unit-access +
+
+

+ Set the force-unit access flag. +

+
+
+-r <NUM> +
+
+--ref-tag=<NUM> +
+
+

+ Optional reftag when used with protection information. +

+
+
+-m <NUM> +
+
+--app-tag-mask=<NUM> +
+
+

+ Optional application tag mask when used with protection information. +

+
+
+-a <NUM> +
+
+--app-tag=<NUM> +
+
+

+ Optional application tag when used with protection information. +

+
+
+-p <NUM> +
+
+--prinfo=<NUM> +
+
+

+ Protection Information field definition. +

+
+
+
+
+
+

EXAMPLES

+
+
    +
  • +

    +Append the data "hello world" into 4k worth of blocks into the zone starting + at block 0 for namespace 1: +

    +
    +
    +
    # echo "hello world" | nvme zns zone-append /dev/nvme0 -n 1 -s 0 -z 4k
    +
    +
  • +
+
+
+
+

NVME

+
+

Part of the nvme-user suite

+
+
+
+

+ + + diff --git a/Documentation/nvme-zns-zone-append.txt b/Documentation/nvme-zns-zone-append.txt new file mode 100644 index 0000000..37242a5 --- /dev/null +++ b/Documentation/nvme-zns-zone-append.txt @@ -0,0 +1,96 @@ +nvme-zns-zone-append(1) +======================= + +NAME +---- +nvme-zns-zone-append - Send an NVMe write command, provide results + +SYNOPSIS +-------- +[verse] +'nvme-zns-zone-append' [--namespace-id= | -n ] + [--zslba= | -s ] + [--data-size= | -z ] + [--metadata-size= | -y ] + [--data= | -d ] + [--metadata= | -M ] + [--limited-retry | -l] + [--force-unit-access | -f] + [--ref-tag= | -r ] + [--app-tag-mask= | -m ] + [--app-tag= | -a ] + [--prinfo= | -p ] + +DESCRIPTION +----------- +The zone append command writes the logical blocks specified by the command to +the medium from the data data buffer provided. Will use stdin by default +if you don't provide a file. + +On sucess, the program will report the LBA that was assigned to the data for +the append operation. + +OPTIONS +------- +-n :: +--namespace-id=:: + Use the provided namespace id for the command. If not provided, the + namespace id of the block device will be used. If the command is issued + to a non-block device, the parameter is required. + +-s :: +--zslba=:: + +-z :: +--data-size=:: + Size of data, in bytes. + +-y :: +--metadata-size=:: + Size of metadata in bytes. + +-d :: +--data=:: + Data file providing the data to write. If none provided, contents are + sent from STDIN. + +-M :: +--metadata=:: + Metadata file, if necessary. + +-l:: +--limited-retry:: + Sets the limited retry flag. + +-f:: +--force-unit-access:: + Set the force-unit access flag. + +-r :: +--ref-tag=:: + Optional reftag when used with protection information. + +-m :: +--app-tag-mask=:: + Optional application tag mask when used with protection information. + +-a :: +--app-tag=:: + Optional application tag when used with protection information. + +-p :: +--prinfo=:: + Protection Information field definition. + +EXAMPLES +-------- +* Append the data "hello world" into 4k worth of blocks into the zone starting + at block 0 for namespace 1: ++ +------------ +# echo "hello world" | nvme zns zone-append /dev/nvme0 -n 1 -s 0 -z 4k +------------ + +NVME +---- +Part of the nvme-user suite diff --git a/Documentation/nvme-zns-zone-mgmt-recv.1 b/Documentation/nvme-zns-zone-mgmt-recv.1 new file mode 100644 index 0000000..26feb54 --- /dev/null +++ b/Documentation/nvme-zns-zone-mgmt-recv.1 @@ -0,0 +1,121 @@ +'\" t +.\" Title: nvme-zns-zone-mgmt-recv +.\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] +.\" Generator: DocBook XSL Stylesheets vsnapshot +.\" Date: 10/20/2020 +.\" Manual: NVMe Manual +.\" Source: NVMe +.\" Language: English +.\" +.TH "NVME\-ZNS\-ZONE\-MGM" "1" "10/20/2020" "NVMe" "NVMe Manual" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +nvme-zns-zone-mgmt-recv \- Zone Management Receive command +.SH "SYNOPSIS" +.sp +.nf +\fInvme zns zone\-mgmt\-recv\fR [\-\-namespace\-id= | \-n ] + [\-\-start\-lba= | \-s ] + [\-\-data\-len=, \-l ] + [\-\-zra=, \-z ] + [\-\-zrasf=, \-a ] + [\-\-zra\-spec\-feat, \-f] + [\-\-output\-format=, \-o ] +.fi +.SH "DESCRIPTION" +.sp +For the NVMe device given, issues the Zone Management Receive command with the requested receive action and additional action specific parameters\&. This is the generic interface provided for forward compatibility as new actions are created that this program isn\(cqt aware of at the time of its development\&. As such, this is a generic command that does not do any additional decoding for specific types of data received\&. This will only report the data as a hex dump, or binary\&. +.SH "OPTIONS" +.PP +\-n , \-\-namespace\-id= +.RS 4 +Use the provided namespace id for the command\&. If not provided, the namespace id of the block device will be used\&. If the command is issued to a non\-block device, the parameter is required\&. +.RE +.PP +\-s , \-\-start\-lba= +.RS 4 +The starting LBA of the zone to manage receive\&. +.RE +.sp +\-\-data\-len= \-l Received data buffer length +.PP +\-z , \-\-zra= +.RS 4 +Zone Receive Action +.RE +.sp +\-a \-\-zrasf= Zone Receive Action Specific field +.PP +\-f, \-\-zra\-spec\-feat +.RS 4 +Enable Zone Receive Action Specific features +.RE +.PP +\-o , \-\-output\-format= +.RS 4 +Output format: normal|json|binary +.RE +.SH "EXAMPLES" +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Hex dump of a report all zones +.sp +.if n \{\ +.RS 4 +.\} +.nf +# nvme zns zone\-mgmt\-recv /dev/nvme0 \-n 1 \-s 0 \-z 0 \-a 0 \-l 4k +.fi +.if n \{\ +.RE +.\} +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Binary dump of a report all zones +.sp +.if n \{\ +.RS 4 +.\} +.nf +# nvme zns zone\-mgmt\-recv /dev/nvme0 \-n 1 \-s 0 \-z 0 \-a 0 \-o \-l 4k binary > report\&.out +.fi +.if n \{\ +.RE +.\} +.RE +.SH "NVME" +.sp +Part of nvme\-cli diff --git a/Documentation/nvme-zns-zone-mgmt-recv.html b/Documentation/nvme-zns-zone-mgmt-recv.html new file mode 100644 index 0000000..74d6a8d --- /dev/null +++ b/Documentation/nvme-zns-zone-mgmt-recv.html @@ -0,0 +1,889 @@ + + + + + + +nvme-zns-zone-mgmt-recv(1) + + + + + +
+
+

SYNOPSIS

+
+
+
nvme zns zone-mgmt-recv <device> [--namespace-id=<NUM> | -n <NUM>]
+                                   [--start-lba=<LBA> | -s <LBA>]
+                                   [--data-len=<IONUM>, -l <IONUM>]
+                                   [--zra=<NUM>, -z <NUM>]
+                                   [--zrasf=<NUM>, -a <NUM>]
+                                   [--zra-spec-feat, -f]
+                                   [--output-format=<FMT>, -o <FMT>]
+
+
+
+
+
+

DESCRIPTION

+
+

For the NVMe device given, issues the Zone Management Receive command with the +requested receive action and additional action specific parameters. This is the +generic interface provided for forward compatibility as new actions are created +that this program isn’t aware of at the time of its development. As such, this +is a generic command that does not do any additional decoding for specific +types of data received. This will only report the data as a hex dump, or +binary.

+
+
+
+

OPTIONS

+
+
+
+-n <NUM> +
+
+--namespace-id=<NUM> +
+
+

+ Use the provided namespace id for the command. If not provided, the + namespace id of the block device will be used. If the command is issued + to a non-block device, the parameter is required. +

+
+
+-s <lba> +
+
+--start-lba=<lba> +
+
+

+ The starting LBA of the zone to manage receive. +

+
+
+

--data-len=<NUM> +-l <NUM> + Received data buffer length

+
+
+-z <NUM> +
+
+--zra=<NUM> +
+
+

+ Zone Receive Action +

+
+
+

-a <NUM> +--zrasf=<NUM> + Zone Receive Action Specific field

+
+
+-f +
+
+--zra-spec-feat +
+
+

+ Enable Zone Receive Action Specific features +

+
+
+-o <FMT> +
+
+--output-format=<FMT> +
+
+

+ Output format: normal|json|binary +

+
+
+
+
+
+

EXAMPLES

+
+
    +
  • +

    +Hex dump of a report all zones +

    +
    +
    +
    # nvme zns zone-mgmt-recv /dev/nvme0 -n 1 -s 0 -z 0 -a 0 -l 4k
    +
    +
  • +
  • +

    +Binary dump of a report all zones +

    +
    +
    +
    # nvme zns zone-mgmt-recv /dev/nvme0 -n 1 -s 0 -z 0 -a 0 -o -l 4k binary > report.out
    +
    +
  • +
+
+
+
+

NVME

+
+

Part of nvme-cli

+
+
+
+

+ + + diff --git a/Documentation/nvme-zns-zone-mgmt-recv.txt b/Documentation/nvme-zns-zone-mgmt-recv.txt new file mode 100644 index 0000000..07cd98e --- /dev/null +++ b/Documentation/nvme-zns-zone-mgmt-recv.txt @@ -0,0 +1,78 @@ +nvme-zns-zone-mgmt-recv(1) +========================== + +NAME +---- +nvme-zns-zone-mgmt-recv - Zone Management Receive command + +SYNOPSIS +-------- +[verse] +'nvme zns zone-mgmt-recv' [--namespace-id= | -n ] + [--start-lba= | -s ] + [--data-len=, -l ] + [--zra=, -z ] + [--zrasf=, -a ] + [--zra-spec-feat, -f] + [--output-format=, -o ] + +DESCRIPTION +----------- +For the NVMe device given, issues the Zone Management Receive command with the +requested receive action and additional action specific parameters. This is the +generic interface provided for forward compatibility as new actions are created +that this program isn't aware of at the time of its development. As such, this +is a generic command that does not do any additional decoding for specific +types of data received. This will only report the data as a hex dump, or +binary. + +OPTIONS +------- +-n :: +--namespace-id=:: + Use the provided namespace id for the command. If not provided, the + namespace id of the block device will be used. If the command is issued + to a non-block device, the parameter is required. + +-s :: +--start-lba=:: + The starting LBA of the zone to manage receive. + +--data-len= +-l + Received data buffer length + +-z :: +--zra=:: + Zone Receive Action + +-a +--zrasf= + Zone Receive Action Specific field + +-f:: +--zra-spec-feat:: + Enable Zone Receive Action Specific features + +-o :: +--output-format=:: + Output format: normal|json|binary + +EXAMPLES +-------- +* Hex dump of a report all zones ++ +------------ +# nvme zns zone-mgmt-recv /dev/nvme0 -n 1 -s 0 -z 0 -a 0 -l 4k +------------ ++ + +* Binary dump of a report all zones ++ +------------ +# nvme zns zone-mgmt-recv /dev/nvme0 -n 1 -s 0 -z 0 -a 0 -o -l 4k binary > report.out +------------ + +NVME +---- +Part of nvme-cli diff --git a/Documentation/nvme-zns-zone-mgmt-send.1 b/Documentation/nvme-zns-zone-mgmt-send.1 new file mode 100644 index 0000000..f74d8a3 --- /dev/null +++ b/Documentation/nvme-zns-zone-mgmt-send.1 @@ -0,0 +1,126 @@ +'\" t +.\" Title: nvme-zns-zone-mgmt-send +.\" Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author] +.\" Generator: DocBook XSL Stylesheets vsnapshot +.\" Date: 10/20/2020 +.\" Manual: NVMe Manual +.\" Source: NVMe +.\" Language: English +.\" +.TH "NVME\-ZNS\-ZONE\-MGM" "1" "10/20/2020" "NVMe" "NVMe Manual" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +nvme-zns-zone-mgmt-send \- Zone Management Send command +.SH "SYNOPSIS" +.sp +.nf +\fInvme zns zone\-mgmt\-send\fR [\-\-namespace\-id= | \-n ] + [\-\-start\-lba=, \-s ] + [\-\-select\-all, \-a] + [\-\-zsa=, \-z ] + [\-\-data\-len=, \-l ] + [\-data=, \-d ] +.fi +.SH "DESCRIPTION" +.sp +For the NVMe device given, issues the Zone Management Send command with the requested send action\&. If the send requires additional data, you may specify the data length\&. If data is required, the input will default to stdin\&. Alternatively, the data may come from a file that can be specified\&. +.SH "OPTIONS" +.PP +\-n , \-\-namespace\-id= +.RS 4 +Use the provided namespace id for the command\&. If not provided, the namespace id of the block device will be used\&. If the command is issued to a non\-block device, the parameter is required\&. +.RE +.PP +\-s , \-\-start\-lba= +.RS 4 +The starting LBA of the zone to manage send\&. +.RE +.PP +\-l , \-\-data\-len= +.RS 4 +Send data buffer length +.RE +.PP +\-\-select\-all, \-a +.RS 4 +Send command to all zones +.RE +.PP +\-z , \-\-zsa= +.RS 4 +Zone send action\&. +.RE +.PP +\-l , \-\-data\-len= +.RS 4 +Buffer length if data required +.RE +.PP +\-d +.RS 4 +Optional file for data (default stdin) +.RE +.SH "EXAMPLES" +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Send a zone management command with action set to 1 (close zone) to namespace 1\(cqs first zone: +.sp +.if n \{\ +.RS 4 +.\} +.nf +# nvme zns zone\-mgmt\-send /dev/nvme0 \-n 1 \-s 0 \-z 1 +.fi +.if n \{\ +.RE +.\} +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Write "hello world" into the zone descriptor for namespace 1\(cqs first zone (requires device supports a large enough zone extended data) +.sp +.if n \{\ +.RS 4 +.\} +.nf +# echo "hello world" | nvme zns zone\-mgmt\-send /dev/nvme0 \-n 1 \-s 0 \-z 0x10 +.fi +.if n \{\ +.RE +.\} +.RE +.SH "NVME" +.sp +Part of nvme\-cli diff --git a/Documentation/nvme-zns-zone-mgmt-send.html b/Documentation/nvme-zns-zone-mgmt-send.html new file mode 100644 index 0000000..6305707 --- /dev/null +++ b/Documentation/nvme-zns-zone-mgmt-send.html @@ -0,0 +1,899 @@ + + + + + + +nvme-zns-zone-mgmt-send(1) + + + + + +
+
+

SYNOPSIS

+
+
+
nvme zns zone-mgmt-send <device> [--namespace-id=<NUM> | -n <NUM>]
+                                   [--start-lba=<IONUM>, -s <IONUM>]
+                                   [--select-all, -a]
+                                   [--zsa=<NUM>, -z <NUM>]
+                                   [--data-len=<IONUM>, -l <IONUM>]
+                                   [-data=<FILE>, -d <FILE>]
+
+
+
+
+
+

DESCRIPTION

+
+

For the NVMe device given, issues the Zone Management Send command with the +requested send action. If the send requires additional data, you may specify +the data length. If data is required, the input will default to stdin. +Alternatively, the data may come from a file that can be specified.

+
+
+
+

OPTIONS

+
+
+
+-n <NUM> +
+
+--namespace-id=<NUM> +
+
+

+ Use the provided namespace id for the command. If not provided, the + namespace id of the block device will be used. If the command is issued + to a non-block device, the parameter is required. +

+
+
+-s <lba> +
+
+--start-lba=<lba> +
+
+

+ The starting LBA of the zone to manage send. +

+
+
+-l <NUM> +
+
+--data-len=<NUM> +
+
+

+ Send data buffer length +

+
+
+--select-all +
+
+-a +
+
+

+ Send command to all zones +

+
+
+-z <NUM> +
+
+--zsa=<NUM> +
+
+

+ Zone send action. +

+
+
+-l <IONUM> +
+
+--data-len=<IONUM> +
+
+

+ Buffer length if data required +

+
+
+-d <FILE +
+
+-data=<FILE> +
+
+

+ Optional file for data (default stdin) +

+
+
+
+
+
+

EXAMPLES

+
+
    +
  • +

    +Send a zone management command with action set to 1 (close zone) to namespace + 1’s first zone: +

    +
    +
    +
    # nvme zns zone-mgmt-send /dev/nvme0 -n 1 -s 0 -z 1
    +
    +
  • +
  • +

    +Write "hello world" into the zone descriptor for namespace 1’s first zone + (requires device supports a large enough zone extended data) +

    +
    +
    +
    # echo "hello world" | nvme zns zone-mgmt-send /dev/nvme0 -n 1 -s 0 -z 0x10
    +
    +
  • +
+
+
+
+

NVME

+
+

Part of nvme-cli

+
+
+
+

+ + + diff --git a/Documentation/nvme-zns-zone-mgmt-send.txt b/Documentation/nvme-zns-zone-mgmt-send.txt new file mode 100644 index 0000000..e33b9a5 --- /dev/null +++ b/Documentation/nvme-zns-zone-mgmt-send.txt @@ -0,0 +1,77 @@ +nvme-zns-zone-mgmt-send(1) +========================== + +NAME +---- +nvme-zns-zone-mgmt-send - Zone Management Send command + +SYNOPSIS +-------- +[verse] +'nvme zns zone-mgmt-send' [--namespace-id= | -n ] + [--start-lba=, -s ] + [--select-all, -a] + [--zsa=, -z ] + [--data-len=, -l ] + [-data=, -d ] + +DESCRIPTION +----------- +For the NVMe device given, issues the Zone Management Send command with the +requested send action. If the send requires additional data, you may specify +the data length. If data is required, the input will default to stdin. +Alternatively, the data may come from a file that can be specified. + +OPTIONS +------- +-n :: +--namespace-id=:: + Use the provided namespace id for the command. If not provided, the + namespace id of the block device will be used. If the command is issued + to a non-block device, the parameter is required. + +-s :: +--start-lba=:: + The starting LBA of the zone to manage send. + +-l :: +--data-len=:: + Send data buffer length + +--select-all:: +-a:: + Send command to all zones + +-z :: +--zsa=:: + Zone send action. + +-l :: +--data-len=:: + Buffer length if data required + +-d :: + Optional file for data (default stdin) + + +EXAMPLES +-------- +* Send a zone management command with action set to 1 (close zone) to namespace + 1's first zone: ++ +------------ +# nvme zns zone-mgmt-send /dev/nvme0 -n 1 -s 0 -z 1 +------------ ++ + +* Write "hello world" into the zone descriptor for namespace 1's first zone + (requires device supports a large enough zone extended data) ++ +------------ +# echo "hello world" | nvme zns zone-mgmt-send /dev/nvme0 -n 1 -s 0 -z 0x10 +------------ + +NVME +---- +Part of nvme-cli diff --git a/Documentation/nvme.1 b/Documentation/nvme.1 index 55dbfc8..295f5d1 100644 --- a/Documentation/nvme.1 +++ b/Documentation/nvme.1 @@ -1,13 +1,13 @@ '\" t .\" Title: nvme .\" Author: [see the "Authors" section] -.\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/24/2020 +.\" Generator: DocBook XSL Stylesheets v1.79.1 +.\" Date: 12/01/2020 .\" Manual: NVMe Manual .\" Source: NVMe .\" Language: English .\" -.TH "NVME" "1" "04/24/2020" "NVMe" "NVMe Manual" +.TH "NVME" "1" "12/01/2020" "NVMe" "NVMe Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -85,7 +85,12 @@ Format namespace(s) .PP \fBnvme-fw-activate\fR(1) .RS 4 -F/W Activate +F/W Activate (in old version < 1\&.2) +.RE +.PP +\fBnvme-fw-commit\fR(1) +.RS 4 +F/W Commit (in > 1\&.2) .RE .PP \fBnvme-fw-download\fR(1) @@ -113,11 +118,21 @@ Generic Get Log Telemetry Host\-Initiated Log .RE .PP +\fBnvme-changed-ns-list-log\fR(1) +.RS 4 +Retrieve Changed Namespace List Log +.RE +.PP \fBnvme-smart-log\fR(1) .RS 4 Retrieve Smart Log .RE .PP +\fBnvme-ana-log\fR(1) +.RS 4 +Retreive ANA(Asymmetric Namespace Access) Log +.RE +.PP \fBnvme-endurance-log\fR(1) .RS 4 Retrieve endurance Log @@ -128,6 +143,11 @@ Retrieve endurance Log Retrieve effects Log .RE .PP +\fBnvme-self-test-log\fR(1) +.RS 4 +Retrieve Device Self\-test Log +.RE +.PP \fBnvme-get-ns-id\fR(1) .RS 4 Retrieve namespace identifier @@ -148,6 +168,16 @@ Identify Controller Identify Namespace .RE .PP +\fBnvme-id-nvmset\fR(1) +.RS 4 +Identify NVM Set List +.RE +.PP +\fBnvme-id-iocs\fR(1) +.RS 4 +Identify I/O Command Set +.RE +.PP \fBnvme-create-ns\fR(1) .RS 4 Create a new namespace @@ -193,6 +223,21 @@ List all nvme controllers List controller in NVMe subsystem .RE .PP +\fBnvme-list-subsys\fR(1) +.RS 4 +List NVMe subsystems +.RE +.PP +\fBnvme-reset\fR(1) +.RS 4 +Reset a NVMe controller +.RE +.PP +\fBnvme-device-self-test\fR(1) +.RS 4 +Issue Device Self\-test Command +.RE +.PP \fBnvme-read\fR(1) .RS 4 Issue IO Read Command @@ -243,6 +288,16 @@ Security Receive Security Send .RE .PP +\fBnvme-dsm\fR(1) +.RS 4 +Issue Data Set Management Command +.RE +.PP +\fBnvme-copy\fR(1) +.RS 4 +Issue Simple Copy Command +.RE +.PP \fBnvme-set-feature\fR(1) .RS 4 Set Feature diff --git a/Documentation/nvme.html b/Documentation/nvme.html index 5f9856c..6153179 100644 --- a/Documentation/nvme.html +++ b/Documentation/nvme.html @@ -839,7 +839,15 @@ available, run "nvme help".

- F/W Activate + F/W Activate (in old version < 1.2) +

+
+
+nvme-fw-commit(1) +
+
+

+ F/W Commit (in > 1.2)

@@ -883,6 +891,14 @@ available, run "nvme help".

+nvme-changed-ns-list-log(1) +
+
+

+ Retrieve Changed Namespace List Log +

+
+
nvme-smart-log(1)
@@ -891,6 +907,14 @@ available, run "nvme help".

+nvme-ana-log(1) +
+
+

+ Retreive ANA(Asymmetric Namespace Access) Log +

+
+
nvme-endurance-log(1)
@@ -907,6 +931,14 @@ available, run "nvme help".

+nvme-self-test-log(1) +
+
+

+ Retrieve Device Self-test Log +

+
+
nvme-get-ns-id(1)
@@ -939,6 +971,22 @@ available, run "nvme help".

+nvme-id-nvmset(1) +
+
+

+ Identify NVM Set List +

+
+
+nvme-id-iocs(1) +
+
+

+ Identify I/O Command Set +

+
+
nvme-create-ns(1)
@@ -1011,6 +1059,30 @@ available, run "nvme help".

+nvme-list-subsys(1) +
+
+

+ List NVMe subsystems +

+
+
+nvme-reset(1) +
+
+

+ Reset a NVMe controller +

+
+
+nvme-device-self-test(1) +
+
+

+ Issue Device Self-test Command +

+
+
nvme-read(1)
@@ -1091,6 +1163,22 @@ available, run "nvme help".

+nvme-dsm(1) +
+
+

+ Issue Data Set Management Command +

+
+
+nvme-copy(1) +
+
+

+ Issue Simple Copy Command +

+
+
nvme-set-feature(1)
@@ -1190,7 +1278,7 @@ NVM-Express Site.

diff --git a/Makefile b/Makefile index 9db121c..86eb7c6 100644 --- a/Makefile +++ b/Makefile @@ -4,6 +4,7 @@ override CPPFLAGS += -D_GNU_SOURCE -D__CHECK_ENDIAN__ LIBUUID = $(shell $(LD) -o /dev/null -luuid >/dev/null 2>&1; echo $$?) LIBHUGETLBFS = $(shell $(LD) -o /dev/null -lhugetlbfs >/dev/null 2>&1; echo $$?) HAVE_SYSTEMD = $(shell pkg-config --exists libsystemd --atleast-version=242; echo $$?) +LIBJSONC = $(shell $(LD) -o /dev/null -ljson-c >/dev/null 2>&1; echo $$?) NVME = nvme INSTALL ?= install DESTDIR = @@ -37,6 +38,11 @@ ifeq ($(HAVE_SYSTEMD),0) override CFLAGS += -DHAVE_SYSTEMD endif +ifeq ($(LIBJSONC), 0) + override LDFLAGS += -ljson-c + override CFLAGS += -DLIBJSONC +endif + RPMBUILD = rpmbuild TAR = tar RM = rm -f @@ -58,14 +64,19 @@ override CFLAGS += -DNVME_VERSION='"$(NVME_VERSION)"' NVME_DPKG_VERSION=1~`lsb_release -sc` -OBJS := nvme-print.o nvme-ioctl.o \ +OBJS := nvme-print.o nvme-ioctl.o nvme-rpmb.o \ nvme-lightnvm.o fabrics.o nvme-models.o plugin.o \ nvme-status.o nvme-filters.o nvme-topology.o -UTIL_OBJS := util/argconfig.o util/suffix.o util/json.o util/parser.o +UTIL_OBJS := util/argconfig.o util/suffix.o util/parser.o \ + util/cleanup.o util/log.o +ifneq ($(LIBJSONC), 0) +override UTIL_OBJS += util/json.o +endif PLUGIN_OBJS := \ plugins/intel/intel-nvme.o \ + plugins/amzn/amzn-nvme.o \ plugins/lnvm/lnvm-nvme.o \ plugins/memblaze/memblaze-nvme.o \ plugins/wdc/wdc-nvme.o \ @@ -77,15 +88,18 @@ PLUGIN_OBJS := \ plugins/seagate/seagate-nvme.o \ plugins/virtium/virtium-nvme.o \ plugins/shannon/shannon-nvme.o \ - plugins/dera/dera-nvme.o \ - plugins/scaleflux/sfx-nvme.o \ - plugins/transcend/transcend-nvme.o + plugins/dera/dera-nvme.o \ + plugins/scaleflux/sfx-nvme.o \ + plugins/transcend/transcend-nvme.o \ + plugins/zns/zns.o \ + plugins/nvidia/nvidia-nvme.o \ + plugins/ymtc/ymtc-nvme.o nvme: nvme.c nvme.h $(OBJS) $(PLUGIN_OBJS) $(UTIL_OBJS) NVME-VERSION-FILE $(QUIET_CC)$(CC) $(CPPFLAGS) $(CFLAGS) $(INC) $< -o $(NVME) $(OBJS) $(PLUGIN_OBJS) $(UTIL_OBJS) $(LDFLAGS) -verify-no-dep: nvme.c nvme.h $(OBJS) NVME-VERSION-FILE - $(QUIET_CC)$(CC) $(CPPFLAGS) $(CFLAGS) $< -o $@ $(OBJS) $(LDFLAGS) +verify-no-dep: nvme.c nvme.h $(OBJS) $(UTIL_OBJS) NVME-VERSION-FILE + $(QUIET_CC)$(CC) $(CPPFLAGS) $(CFLAGS) $(INC) $< -o $@ $(OBJS) $(UTIL_OBJS) $(LDFLAGS) nvme.o: nvme.c nvme.h nvme-print.h nvme-ioctl.h util/argconfig.h util/suffix.h nvme-lightnvm.h fabrics.h $(QUIET_CC)$(CC) $(CPPFLAGS) $(CFLAGS) $(INC) -c $< @@ -229,7 +243,10 @@ deb-light: $(NVME) pkg nvme.control.in dpkg-deb --build nvme-$(NVME_VERSION) rpm: dist - $(RPMBUILD) --define '_libdir ${LIBDIR}' -ta nvme-$(NVME_VERSION).tar.gz + $(RPMBUILD) --define '_prefix $(DESTDIR)$(PREFIX)' \ + --define '_libdir $(DESTDIR)${LIBDIR}' \ + --define '_sysconfdir $(DESTDIR)$(SYSCONFDIR)' \ + -ta nvme-$(NVME_VERSION).tar.gz .PHONY: default doc all clean clobber install-man install-bin install .PHONY: dist pkg dist-orig deb deb-light rpm FORCE test diff --git a/NVME-VERSION-GEN b/NVME-VERSION-GEN index 75b13d1..802bbd3 100755 --- a/NVME-VERSION-GEN +++ b/NVME-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=NVME-VERSION-FILE -DEF_VER=v1.12 +DEF_VER=v1.14 LF=' ' diff --git a/README.md b/README.md index b4d2574..bb0219e 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,13 @@ The development version can be installed from AUR, e.g.: $ yay -S nvme-cli-git +### Debian + +nvme-cli is available in Debian 9 and up. Install it with your favorite +package manager. For example: + + $ sudo apt install nvme-cli + ### Fedora nvme-cli is available in Fedora 23 and up. Install it with your favorite diff --git a/common.h b/common.h index aed2a99..1c214a4 100644 --- a/common.h +++ b/common.h @@ -9,4 +9,7 @@ #define min(x, y) ((x) > (y) ? (y) : (x)) #define max(x, y) ((x) > (y) ? (x) : (y)) +#define __stringify_1(x...) #x +#define __stringify(x...) __stringify_1(x) + #endif diff --git a/completions/_nvme b/completions/_nvme index e076232..9267522 100644 --- a/completions/_nvme +++ b/completions/_nvme @@ -10,17 +10,25 @@ _nvme () { 'id-ctrl:display information about the controller' 'id-ns:display information about the namespace' 'list-ns:identify all namespace(s) attached' + 'id-iocs:display information about I/O command sets' 'create-ns:create a new namespace before attachment' 'delete-ns:delete a detached namespace' 'attach-ns:attach namespace to controller' 'detach-ns:detach namespace from controller' 'list-ctrl:identify all controller(s) attached' + 'nvm-id-ctrl:display information about the nvm command set' 'get-ns-id:get namespace id of opened block device' 'get-log:retrieve any log in raw format' + 'predictable-lat-log:retrieve predictable latency per nvmset log' + 'pred-lat-event-agg-log:retrieve predictable latency event aggregate log' + 'persistent-event-log:retrieve presistent event log' 'fw-log:retrieve fw log' 'smart-log:retrieve SMART log' 'smart-log-add:retrieve additional SMART log' 'error-log:retrieve error log' + 'endurance-event-agg-log:retrieve endurance group event aggregate log' + 'lba-status-log:retrieve lba status log' + 'resv-notif-log: retrieve reservation notification log' 'get-feature:display a controller feature' 'set-feature:set a controller feature and show results' 'format:apply new block format to namespace' @@ -34,6 +42,7 @@ _nvme () { 'resv-register:register reservation on a namespace' 'resv-release:release reservation on a namespace' 'resv-report:report reservation on a namespace' + 'copy:submit a simple copy command' 'flush:submit a flush' 'compare:compare data on device to data elsewhere' 'read:submit a read command' @@ -88,10 +97,30 @@ _nvme () { /dev/nvme':supply a device to use (required)' --namespace-id=':start namespace infos listing with this nsid' -n':alias of --namespace-id' + --csi=':command set identifier' + -y':alias of --csi' ) _arguments '*:: :->subcmds' _describe -t commands "nvme list-ns options" _listns ;; + (id-iocs) + local _idiocs + _idiocs=( + /dev/nvme':supply a device to use (required)' + --controller-id=':show infos for controller ' + -c':alias of --controller-id' + ) + _arguments '*:: :->subcmds' + _describe -t commands "nvme id-iocs options" _idiocs + ;; + nvm-id-ctrl) + local _nvmidctrl + _nvmidctrl=( + /dev/nvme':supply a device to use (required)' + ) + _arguments '*:: :->subcmds' + _describe -t commands "nvme nvm-id-ctrl options" _nvmidctrl + ;; (create-ns) local _createns _createns=( @@ -106,6 +135,8 @@ _nvme () { -d':alias of --dps' --nmic=':multipath and sharing' -n':alias of --nmic' + --csi=':command set identifier' + -y':alias of --csi' ) _arguments '*:: :->subcmds' _describe -t commands "nvme create-ns options" _createns @@ -180,6 +211,46 @@ _nvme () { _arguments '*:: :->subcmds' _describe -t commands "nvme get-log options" _getlog ;; + (persistent-event-log) + local _persistenteventlog + _persistenteventlog=( + /dev/nvme':supply a device to use (required)' + --action=': action the controller shall take for this log page' + -a':alias to --action' + --log-len=':number of bytes to show for requested log' + -l':alias of --log-len' + --raw-binary':dump infos in binary format' + -b':alias of --raw-binary' + ) + _arguments '*:: :->subcmds' + _describe -t commands "persistent-event-log options" _persistenteventlog + ;; + (pred-lat-event-agg-log) + local _predlateventagglog + _predlateventagglog=( + /dev/nvme':supply a device to use (required)' + --log-entries=': Number of pending NVM Set Entries log list' + -e':alias to --log-entries' + --rae': Retain an Asynchronous Event' + -r':alias to --rae + --raw-binary':dump infos in binary format' + -b':alias of --raw-binary' + ) + _arguments '*:: :->subcmds' + _describe -t commands "nvme pred-lat-event-agg-log options" _predlateventagglog + ;; + (predictablelatlog) + local _predictablelatlog + _predictablelatlog=( + /dev/nvme':supply a device to use (required)' + --nvmset-id=': NVM Set Identifier on which log page retrieve info' + -i':alias to --nvmset-id' + --raw-binary':dump infos in binary format' + -b':alias of --raw-binary' + ) + _arguments '*:: :->subcmds' + _describe -t commands "nvme predictable-lat-log options" _predictablelatlog + ;; (fw-log) local _fwlog _fwlog=( @@ -228,6 +299,38 @@ _nvme () { _arguments '*:: :->subcmds' _describe -t commands "nvme error-log options" _errlog ;; + (endurance-event-agg-log) + local _enduranceeventagglog + _enduranceeventagglog=( + /dev/nvme':supply a device to use (required)' + --log-entries=': Number of Endurance Group Event Agg Entries log list' + -e':alias to --log-entries' + --rae': Retain an Asynchronous Event' + -r':alias to --rae + --raw-binary':dump infos in binary format' + -b':alias of --raw-binary' + ) + _arguments '*:: :->subcmds' + _describe -t commands "nvme endurance-event-agg-log options" _enduranceeventagglog + ;; + (lba-status-log) + local _lbastatuslog + _lbastatuslog=( + /dev/nvme':supply a device to use (required)' + --rae': Retain an Asynchronous Event' + -r':alias to --rae + ) + _arguments '*:: :->subcmds' + _describe -t commands "nvme lba-status-log options" _lbastatuslog + ;; + (resv-notif-log) + local _resvnotiflog + _resvnotiflog=( + /dev/nvme':supply a device to use (required)' + ) + _arguments '*:: :->subcmds' + _describe -t commands "nvme resv-notif-log options" _resvnotiflog + ;; (get-feature) local _getf _getf=( @@ -515,6 +618,46 @@ _nvme () { _arguments '*:: :->subcmds' _describe -t commands "nvme resv-register options" _reg ;; + (copy) + local _copy + _copy=( + /dev/nvme':supply a device to use (required)' + --sdlba=':64-bit addr of first destination logical block' + -d':alias of --sdlba' + --slbs=':64-bit addr of first block per range (comma-separated list)' + -s':alias of --slbs' + --blocks=':number of blocks per range (comma-separated list, zeroes-based values)' + -b':alias of --blocks' + --limited-retry':if included, controller should try less hard to retrieve data from media (if not included, all available data recovery means used)' + -l':alias of --limited-retry' + --force-unit-access':if included, the data shall be read from non-volatile media' + -f':alias of --force-unit access' + --prinfow=':protection information and check field (write part)' + -p':alias of --prinfow' + --prinfor=':protection information and check field (read part)' + -P':alias of --prinfor' + --ref-tag=':initial lba reference tag (write part)' + -r':alias of --ref-tag' + --expected-ref-tags=':expected lba reference tags (read part, comma-separated list)' + -R':alias of --expected-ref-tags' + --app-tag=':lba application tag (write part)' + -a':alias of --app-tag' + --expected-app-tags=':expected lba application tags (read part, comma-separated list)' + -A':alias of --expected-app-tags' + --app-tag-mask=':lba application tag mask (write part)' + -m':alias of --app-tag-mask' + --expected-app-tag-masks=':expected lba application tag masks (read part, comma-separated list)' + -M':alias of --expected-app-tag-masks' + --dir-type':directive type (write part)' + -T':alias of --dir-type' + --dir-spec':directive specific (write part)' + -S':alias of --dir-spec' + --format':source range entry format' + -F':alias of --format' + ) + _arguments '*:: :->subcmds' + _describe -t commands "nvme copy options" _copy + ;; (flush) local _flush _flush=( @@ -639,11 +782,13 @@ _nvme () { ;; (help) local _h - _h=( id-ctrl id-ns list-ns create-ns delete-ns attach-ns detach-ns + _h=( id-ctrl id-ns list-ns id-iocs create-ns delete-ns attach-ns detach-ns list-ctrl get-ns-id get-log fw-log smart-log error-log get-feature set-feature format fw-activate fw-download admin-passthru io-passthru security-send security-recv resv-acquire resv-register resv-release - resv-report flush compare read write show-regs + resv-report flush compare read write copy show-regs persistent-event-log + pred-lat-event-agg-log nvm-id-ctrl endurance-event-agg-log lba-status-log + resv-notif-log ) _arguments '*:: :->subcmds' _describe -t commands "help: infos on a specific nvme command, or provide no option to see a synopsis of all nvme commands" _h diff --git a/completions/bash-nvme-completion.sh b/completions/bash-nvme-completion.sh index b7c2aa1..c022b89 100644 --- a/completions/bash-nvme-completion.sh +++ b/completions/bash-nvme-completion.sh @@ -2,16 +2,17 @@ # (unfortunately, bash won't let me add descriptions to cmds) # Kelly Kaoudis kelly.n.kaoudis at intel.com, Aug. 2015 -_cmds="list id-ctrl id-ns list-ns create-ns delete-ns \ - attach-ns detach-ns list-ctrl get-ns-id get-log \ - fw-log smart-log smart-log-add error-log \ - get-feature set-feature format fw-activate \ +_cmds="list id-ctrl id-ns list-ns id-iocs nvm-id-ctrl create-ns delete-ns \ + attach-ns detach-ns list-ctrl get-ns-id get-log persistent-event-log \ + pred-lat-event-agg-log fw-log smart-log smart-log-add error-log \ + predictable-lat-log get-feature set-feature format fw-activate \ fw-download admin-passthru io-passthru security-send \ security-recv resv-acquire resv-register resv-release \ resv-report dsm flush compare read write write-zeroes \ - write-uncor reset subsystem-reset show-regs discover \ + write-uncor copy reset subsystem-reset show-regs discover \ connect-all connect disconnect version help \ - intel lnvm memblaze list-subsys" + intel lnvm memblaze list-subsys endurance-event-agg-log \ + lba-status-log resv-notif-log" nvme_list_opts () { local opts="" @@ -44,11 +45,17 @@ nvme_list_opts () { --force -f --output-format= -o" ;; "list-ns") - opts+=" --namespace-id= -n --al -a" + opts+=" --namespace-id= -n --al -a --csi= -y" + ;; + "id-iocs") + opts+=" --controller-id= -c" + ;; + "nvm-id-ctrl") + opts+=" --output-format= -o" ;; "create-ns") opts+=" --nsze= -s --ncap= -c --flbas= -f \ - --dps= -d --nmic= -n" + --dps= -d --nmic= -n --csi= -y" ;; "delete-ns") opts+=" -namespace-id= -n" @@ -68,6 +75,18 @@ nvme_list_opts () { opts+=" --log-id= -i --log-len= -l --namespace-id= -n \ --raw-binary= -b" ;; + "persistent-event-log") + opts+=" --action= -a --log-len= -l \ + --raw-binary -b --output-format= -o" + ;; + "pred-lat-event-agg-log") + opts+=" --log-entries= -e --rae -r \ + --raw-binary -b --output-format= -o" + ;; + "predictable-lat-log") + opts+=" --nvmset-id= -i --raw-binary -b \ + --output-format= -o" + ;; "fw-log") opts+=" --raw-binary -b --output-format= -o" ;; @@ -82,6 +101,16 @@ nvme_list_opts () { opts+=" --namespace-id= -n --raw-binary -b --log-entries= -e \ --output-format= -o" ;; + "endurance-event-agg-log") + opts+=" --log-entries= -e --rae -r \ + --raw-binary -b --output-format= -o" + ;; + "lba-status-log") + opts+=" --rae -r --output-format= -o" + ;; + "resv-notif-log") + opts+=" --output-format= -o" + ;; "get-feature") opts+=" --namespace-id= -n --feature-id= -f --sel= -s \ --data-len= -l --cdw11= --raw-binary -b \ @@ -145,6 +174,15 @@ nvme_list_opts () { opts+=" --namespace-id= -n --ctx-attrs= -a --blocks= -b\ -slbs= -s --ad -d --idw -w --idr -r --cdw11= -c" ;; + "copy") + opts+=" --sdlba= -d --blocks= -b --slbs= -s \ + --limited-retry -l --force-unit-access -f \ + --prinfow= -p --prinfor= -P \ + --ref-tag= -r --expected-ref-tag= -R \ + --app-tag= -a --expected-app-tag= -A \ + --app-tag-mask= -m --expected-app-tag-mask= -M \ + --dir-type= -T --dir-spec= -S --format= -F" + ;; "flush") opts+=" --namespace-id= -n" ;; diff --git a/fabrics.c b/fabrics.c index 2fec802..7dd0d27 100644 --- a/fabrics.c +++ b/fabrics.c @@ -32,6 +32,8 @@ #include #include #include +#include +#include #include #include @@ -46,6 +48,8 @@ #include "util/argconfig.h" #include "common.h" +#include "util/log.h" +#include "util/cleanup.h" #ifdef HAVE_SYSTEMD #include @@ -54,38 +58,19 @@ #define NVMF_HOSTID_SIZE 36 +/* default to 600 seconds of reconnect attempts before giving up */ +#define NVMF_DEF_CTRL_LOSS_TMO 600 + const char *conarg_nqn = "nqn"; const char *conarg_transport = "transport"; const char *conarg_traddr = "traddr"; const char *conarg_trsvcid = "trsvcid"; const char *conarg_host_traddr = "host_traddr"; -static struct config { - char *nqn; - char *transport; - char *traddr; - char *trsvcid; - char *host_traddr; - char *hostnqn; - char *hostid; - int nr_io_queues; - int nr_write_queues; - int nr_poll_queues; - int queue_size; - int keep_alive_tmo; - int reconnect_delay; - int ctrl_loss_tmo; - int tos; - char *raw; - char *device; - int duplicate_connect; - int disable_sqflow; - int hdr_digest; - int data_digest; - bool persistent; - bool quiet; - bool matching_only; -} cfg = { NULL }; +struct fabrics_config fabrics_cfg = { + .ctrl_loss_tmo = -1, + .output_format = "normal", +}; struct connect_args { char *subsysnqn; @@ -93,9 +78,12 @@ struct connect_args { char *traddr; char *trsvcid; char *host_traddr; + struct connect_args *next; + struct connect_args *tail; }; -#define BUF_SIZE 4096 +struct connect_args *tracked_ctrls; + #define PATH_NVME_FABRICS "/dev/nvme-fabrics" #define PATH_NVMF_DISC "/etc/nvme/discovery.conf" #define PATH_NVMF_HOSTNQN "/etc/nvme/hostnqn" @@ -115,7 +103,7 @@ static const match_table_t opt_tokens = { { OPT_ERR, NULL }, }; -static const char *arg_str(const char * const *strings, +const char *arg_str(const char * const *strings, size_t array_size, size_t idx) { if (idx < array_size && strings[idx]) @@ -123,7 +111,7 @@ static const char *arg_str(const char * const *strings, return "unrecognized"; } -static const char * const trtypes[] = { +const char * const trtypes[] = { [NVMF_TRTYPE_RDMA] = "rdma", [NVMF_TRTYPE_FC] = "fc", [NVMF_TRTYPE_TCP] = "tcp", @@ -214,14 +202,12 @@ static const char *cms_str(__u8 cm) return arg_str(cms, ARRAY_SIZE(cms), cm); } -static int do_discover(char *argstr, bool connect); - /* * parse strings with connect arguments to find a particular field. * If field found, return string containing field value. If field * not found, return an empty string. */ -static char *parse_conn_arg(char *conargs, const char delim, const char *field) +char *parse_conn_arg(const char *conargs, const char delim, const char *field) { char *s, *e; size_t cnt; @@ -265,17 +251,22 @@ empty_field: return strdup("\0"); } -static int ctrl_instance(char *device) +int ctrl_instance(const char *device) { char d[64]; + const char *p; int ret, instance; - device = basename(device); - ret = sscanf(device, "nvme%d", &instance); + p = strrchr(device, '/'); + if (p == NULL) + p = device; + else + p++; + ret = sscanf(p, "nvme%d", &instance); if (ret <= 0) return -EINVAL; if (snprintf(d, sizeof(d), "nvme%d", instance) <= 0 || - strcmp(device, d)) + strcmp(p, d)) return -EINVAL; return instance; } @@ -286,25 +277,54 @@ static int ctrl_instance(char *device) * given. * Return true/false based on whether it matches */ -static bool ctrl_matches_connectargs(char *name, struct connect_args *args) +static bool ctrl_matches_connectargs(const char *name, struct connect_args *args) { struct connect_args cargs; bool found = false; - char *path, *addr; + char *path = NULL, *addr; int ret; + bool persistent = true; ret = asprintf(&path, "%s/%s", SYS_NVME, name); if (ret < 0) return found; addr = nvme_get_ctrl_attr(path, "address"); + if (!addr) { + fprintf(stderr, "nvme_get_ctrl_attr failed\n"); + return found; + } + cargs.subsysnqn = nvme_get_ctrl_attr(path, "subsysnqn"); cargs.transport = nvme_get_ctrl_attr(path, "transport"); cargs.traddr = parse_conn_arg(addr, ' ', conarg_traddr); cargs.trsvcid = parse_conn_arg(addr, ' ', conarg_trsvcid); cargs.host_traddr = parse_conn_arg(addr, ' ', conarg_host_traddr); - if (!strcmp(cargs.subsysnqn, args->subsysnqn) && + if (!strcmp(cargs.subsysnqn, NVME_DISC_SUBSYS_NAME)) { + char *kato_str = nvme_get_ctrl_attr(path, "kato"), *p; + unsigned int kato = 0; + + /* + * When looking up discovery controllers we have to skip + * any non-persistent controllers (ie those with a zero + * kato value). Otherwise the controller will vanish from + * underneath us as they are owned by another program. + * + * On older kernels, the 'kato' attribute isn't present. + * Assume a persistent controller for these installations. + */ + if (kato_str) { + kato = strtoul(kato_str, &p, 0); + if (p == kato_str) + kato = 0; + free(kato_str); + persistent = (kato != 0); + } + } + + if (persistent && + !strcmp(cargs.subsysnqn, args->subsysnqn) && !strcmp(cargs.transport, args->transport) && (!strcmp(cargs.traddr, args->traddr) || !strcmp(args->traddr, "none")) && @@ -319,6 +339,8 @@ static bool ctrl_matches_connectargs(char *name, struct connect_args *args) free(cargs.traddr); free(cargs.trsvcid); free(cargs.host_traddr); + free(addr); + free(path); return found; } @@ -338,7 +360,7 @@ static char *find_ctrl_with_connectargs(struct connect_args *args) n = scandir(SYS_NVME, &devices, scan_ctrls_filter, alphasort); if (n < 0) { - fprintf(stderr, "no NVMe controller(s) detected.\n"); + msg(LOG_ERR, "no NVMe controller(s) detected.\n"); return NULL; } @@ -346,7 +368,7 @@ static char *find_ctrl_with_connectargs(struct connect_args *args) if (ctrl_matches_connectargs(devices[i]->d_name, args)) { devname = strdup(devices[i]->d_name); if (devname == NULL) - fprintf(stderr, "no memory for ctrl name %s\n", + msg(LOG_ERR, "no memory for ctrl name %s\n", devices[i]->d_name); goto cleanup_devices; } @@ -360,6 +382,51 @@ cleanup_devices: return devname; } +static struct connect_args *extract_connect_args(char *argstr) +{ + struct connect_args *cargs; + + cargs = calloc(1, sizeof(*cargs)); + if (!cargs) + return NULL; + cargs->subsysnqn = parse_conn_arg(argstr, ',', conarg_nqn); + cargs->transport = parse_conn_arg(argstr, ',', conarg_transport); + cargs->traddr = parse_conn_arg(argstr, ',', conarg_traddr); + cargs->trsvcid = parse_conn_arg(argstr, ',', conarg_trsvcid); + cargs->host_traddr = parse_conn_arg(argstr, ',', conarg_host_traddr); + return cargs; +} + +static void destruct_connect_args(struct connect_args *cargs) +{ + free(cargs->subsysnqn); + free(cargs->transport); + free(cargs->traddr); + free(cargs->trsvcid); + free(cargs->host_traddr); +} + +static void free_connect_args(struct connect_args *cargs) +{ + destruct_connect_args(cargs); + free(cargs); +} + +static void track_ctrl(char *argstr) +{ + struct connect_args *cargs; + + cargs = extract_connect_args(argstr); + if (!cargs) + return; + + if (!tracked_ctrls) + tracked_ctrls = cargs; + else + tracked_ctrls->tail->next = cargs; + tracked_ctrls->tail = cargs; +} + static int add_ctrl(const char *argstr) { substring_t args[MAX_OPT_ARGS]; @@ -368,7 +435,7 @@ static int add_ctrl(const char *argstr) fd = open(PATH_NVME_FABRICS, O_RDWR); if (fd < 0) { - fprintf(stderr, "Failed to open %s: %s\n", + msg(LOG_ERR, "Failed to open %s: %s\n", PATH_NVME_FABRICS, strerror(errno)); ret = -errno; goto out; @@ -376,8 +443,8 @@ static int add_ctrl(const char *argstr) ret = write(fd, argstr, len); if (ret != len) { - if (errno != EALREADY || !cfg.quiet) - fprintf(stderr, "Failed to write to %s: %s\n", + if (errno != EALREADY) + msg(LOG_NOTICE, "Failed to write to %s: %s\n", PATH_NVME_FABRICS, strerror(errno)); ret = -errno; goto out_close; @@ -385,7 +452,7 @@ static int add_ctrl(const char *argstr) len = read(fd, buf, BUF_SIZE); if (len < 0) { - fprintf(stderr, "Failed to read from %s: %s\n", + msg(LOG_ERR, "Failed to read from %s: %s\n", PATH_NVME_FABRICS, strerror(errno)); ret = -errno; goto out_close; @@ -403,6 +470,7 @@ static int add_ctrl(const char *argstr) if (match_int(args, &token)) goto out_fail; ret = token; + track_ctrl((char *)argstr); goto out_close; default: /* ignore */ @@ -411,7 +479,7 @@ static int add_ctrl(const char *argstr) } out_fail: - fprintf(stderr, "Failed to parse ctrl info for \"%s\"\n", argstr); + msg(LOG_ERR, "Failed to parse ctrl info for \"%s\"\n", argstr); ret = -EINVAL; out_close: close(fd); @@ -426,7 +494,7 @@ static int remove_ctrl_by_path(char *sysfs_path) fd = open(sysfs_path, O_WRONLY); if (fd < 0) { ret = -errno; - fprintf(stderr, "Failed to open %s: %s\n", sysfs_path, + msg(LOG_ERR, "Failed to open %s: %s\n", sysfs_path, strerror(errno)); goto out; } @@ -443,7 +511,7 @@ out: return ret; } -static int remove_ctrl(int instance) +int remove_ctrl(int instance) { char *sysfs_path; int ret; @@ -480,7 +548,7 @@ static int nvmf_get_log_page_discovery(const char *dev_path, fd = open(dev_path, O_RDWR); if (fd < 0) { error = -errno; - fprintf(stderr, "Failed to open %s: %s\n", + msg(LOG_ERR, "Failed to open %s: %s\n", dev_path, strerror(errno)); goto out; } @@ -497,6 +565,7 @@ static int nvmf_get_log_page_discovery(const char *dev_path, */ log = calloc(1, hdr_size); if (!log) { + perror("could not alloc memory for discovery log header"); error = -ENOMEM; goto out_close; } @@ -531,6 +600,7 @@ static int nvmf_get_log_page_discovery(const char *dev_path, /* allocate discovery log pages based on page_hdr->numrec */ log = calloc(1, log_size); if (!log) { + perror("could not alloc memory for discovery log page"); error = -ENOMEM; goto out_close; } @@ -644,15 +714,67 @@ static void print_discovery_log(struct nvmf_disc_rsp_page_hdr *log, int numrec) } } +static void json_discovery_log(struct nvmf_disc_rsp_page_hdr *log, int numrec) +{ + struct json_object *root; + struct json_object *entries; + int i; + + root = json_create_object(); + entries = json_create_array(); + json_object_add_value_uint(root, "genctr", le64_to_cpu(log->genctr)); + json_object_add_value_array(root, "records", entries); + + for (i = 0; i < numrec; i++) { + struct nvmf_disc_rsp_page_entry *e = &log->entries[i]; + struct json_object *entry = json_create_object(); + + json_object_add_value_string(entry, "trtype", + trtype_str(e->trtype)); + json_object_add_value_string(entry, "adrfam", + adrfam_str(e->adrfam)); + json_object_add_value_string(entry, "subtype", + subtype_str(e->subtype)); + json_object_add_value_string(entry,"treq", + treq_str(e->treq)); + json_object_add_value_uint(entry, "portid", e->portid); + json_object_add_value_string(entry, "trsvcid", + e->trsvcid); + json_object_add_value_string(entry, "subnqn", e->subnqn); + json_object_add_value_string(entry, "traddr", e->traddr); + + switch (e->trtype) { + case NVMF_TRTYPE_RDMA: + json_object_add_value_string(entry, "rdma_prtype", + prtype_str(e->tsas.rdma.prtype)); + json_object_add_value_string(entry, "rdma_qptype", + qptype_str(e->tsas.rdma.qptype)); + json_object_add_value_string(entry, "rdma_cms", + cms_str(e->tsas.rdma.cms)); + json_object_add_value_uint(entry, "rdma_pkey", + e->tsas.rdma.pkey); + break; + case NVMF_TRTYPE_TCP: + json_object_add_value_string(entry, "sectype", + sectype_str(e->tsas.tcp.sectype)); + break; + } + json_array_add_value_object(entries, entry); + } + json_print_object(root, NULL); + printf("\n"); + json_free_object(root); +} + static void save_discovery_log(struct nvmf_disc_rsp_page_hdr *log, int numrec) { int fd; int len, ret; - fd = open(cfg.raw, O_CREAT|O_RDWR|O_TRUNC, S_IRUSR|S_IWUSR); + fd = open(fabrics_cfg.raw, O_CREAT|O_RDWR|O_TRUNC, S_IRUSR|S_IWUSR); if (fd < 0) { - fprintf(stderr, "failed to open %s: %s\n", - cfg.raw, strerror(errno)); + msg(LOG_ERR, "failed to open %s: %s\n", + fabrics_cfg.raw, strerror(errno)); return; } @@ -660,10 +782,10 @@ static void save_discovery_log(struct nvmf_disc_rsp_page_hdr *log, int numrec) numrec * sizeof(struct nvmf_disc_rsp_page_entry); ret = write(fd, log, len); if (ret < 0) - fprintf(stderr, "failed to write to %s: %s\n", - cfg.raw, strerror(errno)); + msg(LOG_ERR, "failed to write to %s: %s\n", + fabrics_cfg.raw, strerror(errno)); else - printf("Discovery log is saved to %s\n", cfg.raw); + printf("Discovery log is saved to %s\n", fabrics_cfg.raw); close(fd); } @@ -707,6 +829,18 @@ static char *hostnqn_generate_systemd(void) #endif } +static char *hostnqn_read_dmi(void) +{ + char uuid[16]; + char *ret = NULL; + + if (uuid_from_dmi(uuid) < 0) + return NULL; + if (asprintf(&ret, "nqn.2014-08.org.nvmexpress:uuid:%s", uuid) == -1) + return NULL; + return ret; +} + /* returns an allocated string or NULL */ char *hostnqn_read(void) { @@ -716,6 +850,10 @@ char *hostnqn_read(void) if (ret) return ret; + ret = hostnqn_read_dmi(); + if (ret) + return ret; + ret = hostnqn_generate_systemd(); if (ret) return ret; @@ -725,9 +863,9 @@ char *hostnqn_read(void) static int nvmf_hostnqn_file(void) { - cfg.hostnqn = hostnqn_read(); + fabrics_cfg.hostnqn = hostnqn_read(); - return cfg.hostnqn != NULL; + return fabrics_cfg.hostnqn != NULL; } static int nvmf_hostid_file(void) @@ -743,8 +881,8 @@ static int nvmf_hostid_file(void) if (fgets(hostid, sizeof(hostid), f) == NULL) goto out; - cfg.hostid = strdup(hostid); - if (!cfg.hostid) + fabrics_cfg.hostid = strdup(hostid); + if (!fabrics_cfg.hostid) goto out; ret = true; @@ -787,7 +925,7 @@ add_int_argument(char **argstr, int *max_len, char *arg_str, int arg, } static int -add_argument(char **argstr, int *max_len, char *arg_str, char *arg) +add_argument(char **argstr, int *max_len, char *arg_str, const char *arg) { int len; @@ -802,91 +940,115 @@ add_argument(char **argstr, int *max_len, char *arg_str, char *arg) return 0; } -static int build_options(char *argstr, int max_len, bool discover) +int build_options(char *argstr, int max_len, bool discover) { int len; - if (!cfg.transport) { - fprintf(stderr, "need a transport (-t) argument\n"); + if (!fabrics_cfg.transport) { + msg(LOG_ERR, "need a transport (-t) argument\n"); return -EINVAL; } - if (strncmp(cfg.transport, "loop", 4)) { - if (!cfg.traddr) { - fprintf(stderr, "need a address (-a) argument\n"); + if (strncmp(fabrics_cfg.transport, "loop", 4)) { + if (!fabrics_cfg.traddr) { + msg(LOG_ERR, "need a address (-a) argument\n"); return -EINVAL; } + /* Use the default ctrl loss timeout if unset */ + if (fabrics_cfg.ctrl_loss_tmo == -1) + fabrics_cfg.ctrl_loss_tmo = NVMF_DEF_CTRL_LOSS_TMO; } /* always specify nqn as first arg - this will init the string */ - len = snprintf(argstr, max_len, "nqn=%s", cfg.nqn); + len = snprintf(argstr, max_len, "nqn=%s", fabrics_cfg.nqn); if (len < 0) return -EINVAL; argstr += len; max_len -= len; - if (add_argument(&argstr, &max_len, "transport", cfg.transport) || - add_argument(&argstr, &max_len, "traddr", cfg.traddr) || - add_argument(&argstr, &max_len, "host_traddr", cfg.host_traddr) || - add_argument(&argstr, &max_len, "trsvcid", cfg.trsvcid) || - ((cfg.hostnqn || nvmf_hostnqn_file()) && - add_argument(&argstr, &max_len, "hostnqn", cfg.hostnqn)) || - ((cfg.hostid || nvmf_hostid_file()) && - add_argument(&argstr, &max_len, "hostid", cfg.hostid)) || + if (add_argument(&argstr, &max_len, "transport", fabrics_cfg.transport) || + add_argument(&argstr, &max_len, "traddr", fabrics_cfg.traddr) || + add_argument(&argstr, &max_len, "host_traddr", fabrics_cfg.host_traddr) || + add_argument(&argstr, &max_len, "trsvcid", fabrics_cfg.trsvcid) || + ((fabrics_cfg.hostnqn || nvmf_hostnqn_file()) && + add_argument(&argstr, &max_len, "hostnqn", fabrics_cfg.hostnqn)) || + ((fabrics_cfg.hostid || nvmf_hostid_file()) && + add_argument(&argstr, &max_len, "hostid", fabrics_cfg.hostid)) || (!discover && add_int_argument(&argstr, &max_len, "nr_io_queues", - cfg.nr_io_queues, false)) || + fabrics_cfg.nr_io_queues, false)) || add_int_argument(&argstr, &max_len, "nr_write_queues", - cfg.nr_write_queues, false) || + fabrics_cfg.nr_write_queues, false) || add_int_argument(&argstr, &max_len, "nr_poll_queues", - cfg.nr_poll_queues, false) || + fabrics_cfg.nr_poll_queues, false) || (!discover && add_int_argument(&argstr, &max_len, "queue_size", - cfg.queue_size, false)) || - (!discover && - add_int_argument(&argstr, &max_len, "keep_alive_tmo", - cfg.keep_alive_tmo, false)) || + fabrics_cfg.queue_size, false)) || + add_int_argument(&argstr, &max_len, "keep_alive_tmo", + fabrics_cfg.keep_alive_tmo, false) || add_int_argument(&argstr, &max_len, "reconnect_delay", - cfg.reconnect_delay, false) || - add_int_argument(&argstr, &max_len, "ctrl_loss_tmo", - cfg.ctrl_loss_tmo, false) || + fabrics_cfg.reconnect_delay, false) || + (strncmp(fabrics_cfg.transport, "loop", 4) && + add_int_argument(&argstr, &max_len, "ctrl_loss_tmo", + fabrics_cfg.ctrl_loss_tmo, true)) || add_int_argument(&argstr, &max_len, "tos", - cfg.tos, true) || + fabrics_cfg.tos, true) || add_bool_argument(&argstr, &max_len, "duplicate_connect", - cfg.duplicate_connect) || + fabrics_cfg.duplicate_connect) || add_bool_argument(&argstr, &max_len, "disable_sqflow", - cfg.disable_sqflow) || - add_bool_argument(&argstr, &max_len, "hdr_digest", cfg.hdr_digest) || - add_bool_argument(&argstr, &max_len, "data_digest", cfg.data_digest)) + fabrics_cfg.disable_sqflow) || + add_bool_argument(&argstr, &max_len, "hdr_digest", fabrics_cfg.hdr_digest) || + add_bool_argument(&argstr, &max_len, "data_digest", fabrics_cfg.data_digest)) return -EINVAL; return 0; } -static bool traddr_is_hostname(struct config *cfg) +static void set_discovery_kato(struct fabrics_config *cfg) +{ + /* Set kato to NVMF_DEF_DISC_TMO for persistent controllers */ + if (cfg->persistent && !cfg->keep_alive_tmo) + cfg->keep_alive_tmo = NVMF_DEF_DISC_TMO; + /* Set kato to zero for non-persistent controllers */ + else if (!cfg->persistent && (cfg->keep_alive_tmo > 0)) + cfg->keep_alive_tmo = 0; +} + +static void discovery_trsvcid(struct fabrics_config *fabrics_cfg) +{ + if (!strcmp(fabrics_cfg->transport, "tcp")) { + /* Default port for NVMe/TCP discovery controllers */ + fabrics_cfg->trsvcid = __stringify(NVME_DISC_IP_PORT); + } else if (!strcmp(fabrics_cfg->transport, "rdma")) { + /* Default port for NVMe/RDMA controllers */ + fabrics_cfg->trsvcid = __stringify(NVME_RDMA_IP_PORT); + } +} + +static bool traddr_is_hostname(struct fabrics_config *fabrics_cfg) { char addrstr[NVMF_TRADDR_SIZE]; - if (!cfg->traddr) + if (!fabrics_cfg->traddr || !fabrics_cfg->transport) return false; - if (strcmp(cfg->transport, "tcp") && strcmp(cfg->transport, "rdma")) + if (strcmp(fabrics_cfg->transport, "tcp") && strcmp(fabrics_cfg->transport, "rdma")) return false; - if (inet_pton(AF_INET, cfg->traddr, addrstr) > 0 || - inet_pton(AF_INET6, cfg->traddr, addrstr) > 0) + if (inet_pton(AF_INET, fabrics_cfg->traddr, addrstr) > 0 || + inet_pton(AF_INET6, fabrics_cfg->traddr, addrstr) > 0) return false; return true; } -static int hostname2traddr(struct config *cfg) +static int hostname2traddr(struct fabrics_config *fabrics_cfg) { struct addrinfo *host_info, hints = {.ai_family = AF_UNSPEC}; char addrstr[NVMF_TRADDR_SIZE]; const char *p; int ret; - ret = getaddrinfo(cfg->traddr, NULL, &hints, &host_info); + ret = getaddrinfo(fabrics_cfg->traddr, NULL, &hints, &host_info); if (ret) { - fprintf(stderr, "failed to resolve host %s info\n", cfg->traddr); + msg(LOG_ERR, "failed to resolve host %s info\n", fabrics_cfg->traddr); return ret; } @@ -902,18 +1064,18 @@ static int hostname2traddr(struct config *cfg) addrstr, NVMF_TRADDR_SIZE); break; default: - fprintf(stderr, "unrecognized address family (%d) %s\n", - host_info->ai_family, cfg->traddr); + msg(LOG_ERR, "unrecognized address family (%d) %s\n", + host_info->ai_family, fabrics_cfg->traddr); ret = -EINVAL; goto free_addrinfo; } if (!p) { - fprintf(stderr, "failed to get traddr for %s\n", cfg->traddr); + msg(LOG_ERR, "failed to get traddr for %s\n", fabrics_cfg->traddr); ret = -errno; goto free_addrinfo; } - cfg->traddr = strdup(addrstr); + fabrics_cfg->traddr = strdup(addrstr); free_addrinfo: freeaddrinfo(host_info); @@ -937,7 +1099,7 @@ retry: case NVME_NQN_NVME: break; default: - fprintf(stderr, "skipping unsupported subtype %d\n", + msg(LOG_ERR, "skipping unsupported subtype %d\n", e->subtype); return -EINVAL; } @@ -947,78 +1109,78 @@ retry: return -EINVAL; p += len; - if (cfg.hostnqn && strcmp(cfg.hostnqn, "none")) { - len = sprintf(p, ",hostnqn=%s", cfg.hostnqn); + if (fabrics_cfg.hostnqn && strcmp(fabrics_cfg.hostnqn, "none")) { + len = sprintf(p, ",hostnqn=%s", fabrics_cfg.hostnqn); if (len < 0) return -EINVAL; p += len; } - if (cfg.hostid && strcmp(cfg.hostid, "none")) { - len = sprintf(p, ",hostid=%s", cfg.hostid); + if (fabrics_cfg.hostid && strcmp(fabrics_cfg.hostid, "none")) { + len = sprintf(p, ",hostid=%s", fabrics_cfg.hostid); if (len < 0) return -EINVAL; p += len; } - if (cfg.queue_size && !discover) { - len = sprintf(p, ",queue_size=%d", cfg.queue_size); + if (fabrics_cfg.queue_size && !discover) { + len = sprintf(p, ",queue_size=%d", fabrics_cfg.queue_size); if (len < 0) return -EINVAL; p += len; } - if (cfg.nr_io_queues && !discover) { - len = sprintf(p, ",nr_io_queues=%d", cfg.nr_io_queues); + if (fabrics_cfg.nr_io_queues && !discover) { + len = sprintf(p, ",nr_io_queues=%d", fabrics_cfg.nr_io_queues); if (len < 0) return -EINVAL; p += len; } - if (cfg.nr_write_queues) { - len = sprintf(p, ",nr_write_queues=%d", cfg.nr_write_queues); + if (fabrics_cfg.nr_write_queues) { + len = sprintf(p, ",nr_write_queues=%d", fabrics_cfg.nr_write_queues); if (len < 0) return -EINVAL; p += len; } - if (cfg.nr_poll_queues) { - len = sprintf(p, ",nr_poll_queues=%d", cfg.nr_poll_queues); + if (fabrics_cfg.nr_poll_queues) { + len = sprintf(p, ",nr_poll_queues=%d", fabrics_cfg.nr_poll_queues); if (len < 0) return -EINVAL; p += len; } - if (cfg.host_traddr && strcmp(cfg.host_traddr, "none")) { - len = sprintf(p, ",host_traddr=%s", cfg.host_traddr); + if (fabrics_cfg.host_traddr && strcmp(fabrics_cfg.host_traddr, "none")) { + len = sprintf(p, ",host_traddr=%s", fabrics_cfg.host_traddr); if (len < 0) return -EINVAL; p+= len; } - if (cfg.reconnect_delay) { - len = sprintf(p, ",reconnect_delay=%d", cfg.reconnect_delay); + if (fabrics_cfg.reconnect_delay) { + len = sprintf(p, ",reconnect_delay=%d", fabrics_cfg.reconnect_delay); if (len < 0) return -EINVAL; p += len; } - if (cfg.ctrl_loss_tmo) { - len = sprintf(p, ",ctrl_loss_tmo=%d", cfg.ctrl_loss_tmo); + if ((e->trtype != NVMF_TRTYPE_LOOP) && (fabrics_cfg.ctrl_loss_tmo >= -1)) { + len = sprintf(p, ",ctrl_loss_tmo=%d", fabrics_cfg.ctrl_loss_tmo); if (len < 0) return -EINVAL; p += len; } - if (cfg.tos != -1) { - len = sprintf(p, ",tos=%d", cfg.tos); + if (fabrics_cfg.tos != -1) { + len = sprintf(p, ",tos=%d", fabrics_cfg.tos); if (len < 0) return -EINVAL; p += len; } - if (cfg.keep_alive_tmo) { - len = sprintf(p, ",keep_alive_tmo=%d", cfg.keep_alive_tmo); + if (fabrics_cfg.keep_alive_tmo) { + len = sprintf(p, ",keep_alive_tmo=%d", fabrics_cfg.keep_alive_tmo); if (len < 0) return -EINVAL; p += len; @@ -1026,7 +1188,7 @@ retry: transport = trtype_str(e->trtype); if (!strcmp(transport, "unrecognized")) { - fprintf(stderr, "skipping unsupported transport %d\n", + msg(LOG_ERR, "skipping unsupported transport %d\n", e->trtype); return -EINVAL; } @@ -1036,14 +1198,14 @@ retry: return -EINVAL; p += len; - if (cfg.hdr_digest) { + if (fabrics_cfg.hdr_digest) { len = sprintf(p, ",hdr_digest"); if (len < 0) return -EINVAL; p += len; } - if (cfg.data_digest) { + if (fabrics_cfg.data_digest) { len = sprintf(p, ",data_digest"); if (len < 0) return -EINVAL; @@ -1072,7 +1234,7 @@ retry: p += len; break; default: - fprintf(stderr, "skipping unsupported adrfam\n"); + msg(LOG_ERR, "skipping unsupported adrfam\n"); return -EINVAL; } break; @@ -1087,7 +1249,7 @@ retry: p += len; break; default: - fprintf(stderr, "skipping unsupported adrfam\n"); + msg(LOG_ERR, "skipping unsupported adrfam\n"); return -EINVAL; } break; @@ -1100,11 +1262,17 @@ retry: p += len; } - if (discover) - ret = do_discover(argstr, true); - else + if (discover) { + enum nvme_print_flags flags; + + flags = validate_output_format(fabrics_cfg.output_format); + if (flags < 0) + flags = NORMAL; + ret = do_discover(argstr, true, flags); + } else ret = add_ctrl(argstr); - if (ret == -EINVAL && e->treq & NVMF_TREQ_DISABLE_SQFLOW) { + if (ret == -EINVAL && disable_sqflow && + e->treq & NVMF_TREQ_DISABLE_SQFLOW) { /* disable_sqflow param might not be supported, try without it */ disable_sqflow = false; goto retry; @@ -1112,15 +1280,44 @@ retry: return ret; } +static bool cargs_match_found(struct nvmf_disc_rsp_page_entry *entry) +{ + struct connect_args cargs __cleanup__(destruct_connect_args) = { NULL, }; + struct connect_args *c = tracked_ctrls; + + cargs.traddr = strdup(entry->traddr); + cargs.transport = strdup(trtype_str(entry->trtype)); + cargs.subsysnqn = strdup(entry->subnqn); + cargs.trsvcid = strdup(entry->trsvcid); + cargs.host_traddr = strdup(fabrics_cfg.host_traddr ?: "\0"); + + /* check if we have a match in the discovery recursion */ + while (c) { + if (!strcmp(cargs.subsysnqn, c->subsysnqn) && + !strcmp(cargs.transport, c->transport) && + !strcmp(cargs.traddr, c->traddr) && + !strcmp(cargs.trsvcid, c->trsvcid) && + !strcmp(cargs.host_traddr, c->host_traddr)) + return true; + c = c->next; + } + + /* check if we have a matching existing controller */ + return find_ctrl_with_connectargs(&cargs) != NULL; +} + static bool should_connect(struct nvmf_disc_rsp_page_entry *entry) { int len; - if (!cfg.matching_only || !cfg.traddr) + if (cargs_match_found(entry)) + return false; + + if (!fabrics_cfg.matching_only || !fabrics_cfg.traddr) return true; len = space_strip_len(NVMF_TRADDR_SIZE, entry->traddr); - return !strncmp(cfg.traddr, entry->traddr, len); + return !strncmp(fabrics_cfg.traddr, entry->traddr, len); } static int connect_ctrls(struct nvmf_disc_rsp_page_hdr *log, int numrec) @@ -1143,9 +1340,7 @@ static int connect_ctrls(struct nvmf_disc_rsp_page_hdr *log, int numrec) if (instance == -EALREADY) { const char *traddr = log->entries[i].traddr; - if (!cfg.quiet) - fprintf(stderr, - "traddr=%.*s is already connected\n", + msg(LOG_NOTICE, "traddr=%.*s is already connected\n", space_strip_len(NVMF_TRADDR_SIZE, traddr), traddr); @@ -1169,52 +1364,37 @@ static void nvmf_get_host_identifiers(int ctrl_instance) if (asprintf(&path, "%s/nvme%d", SYS_NVME, ctrl_instance) < 0) return; - cfg.hostnqn = nvme_get_ctrl_attr(path, "hostnqn"); - cfg.hostid = nvme_get_ctrl_attr(path, "hostid"); + fabrics_cfg.hostnqn = nvme_get_ctrl_attr(path, "hostnqn"); + fabrics_cfg.hostid = nvme_get_ctrl_attr(path, "hostid"); } -static int do_discover(char *argstr, bool connect) +static DEFINE_CLEANUP_FUNC(cleanup_log, struct nvmf_disc_rsp_page_hdr *, free); + +int do_discover(char *argstr, bool connect, enum nvme_print_flags flags) { - struct nvmf_disc_rsp_page_hdr *log = NULL; + struct nvmf_disc_rsp_page_hdr *log __cleanup__(cleanup_log) = NULL; char *dev_name; int instance, numrec = 0, ret, err; int status = 0; + struct connect_args *cargs; - if (cfg.device) { - struct connect_args cargs; - - memset(&cargs, 0, sizeof(cargs)); - cargs.subsysnqn = parse_conn_arg(argstr, ',', conarg_nqn); - cargs.transport = parse_conn_arg(argstr, ',', conarg_transport); - cargs.traddr = parse_conn_arg(argstr, ',', conarg_traddr); - cargs.trsvcid = parse_conn_arg(argstr, ',', conarg_trsvcid); - cargs.host_traddr = parse_conn_arg(argstr, ',', conarg_host_traddr); - - /* - * if the cfg.device passed in matches the connect args - * cfg.device is left as-is - * else if there exists a controller that matches the - * connect args - * cfg.device is the matching ctrl name - * else if no ctrl matches the connect args - * cfg.device is set to null. This will attempt to - * create a new ctrl. - * endif - */ - if (!ctrl_matches_connectargs(cfg.device, &cargs)) - cfg.device = find_ctrl_with_connectargs(&cargs); + cargs = extract_connect_args(argstr); + if (!cargs) + return -ENOMEM; - free(cargs.subsysnqn); - free(cargs.transport); - free(cargs.traddr); - free(cargs.trsvcid); - free(cargs.host_traddr); + if (fabrics_cfg.device && + !ctrl_matches_connectargs(fabrics_cfg.device, cargs)) { + free(fabrics_cfg.device); + fabrics_cfg.device = NULL; } + if (!fabrics_cfg.device) + fabrics_cfg.device = find_ctrl_with_connectargs(cargs); + free_connect_args(cargs); - if (!cfg.device) { + if (!fabrics_cfg.device) { instance = add_ctrl(argstr); } else { - instance = ctrl_instance(cfg.device); + instance = ctrl_instance(fabrics_cfg.device); nvmf_get_host_identifiers(instance); } if (instance < 0) @@ -1224,7 +1404,9 @@ static int do_discover(char *argstr, bool connect) return -errno; ret = nvmf_get_log_page_discovery(dev_name, &log, &numrec, &status); free(dev_name); - if (!cfg.device && !cfg.persistent) { + if (fabrics_cfg.persistent) + msg(LOG_NOTICE, "Persistent device: nvme%d\n", instance); + if (!fabrics_cfg.device && !fabrics_cfg.persistent) { err = remove_ctrl(instance); if (err) return err; @@ -1234,18 +1416,20 @@ static int do_discover(char *argstr, bool connect) case DISC_OK: if (connect) ret = connect_ctrls(log, numrec); - else if (cfg.raw) + else if (fabrics_cfg.raw || flags == BINARY) save_discovery_log(log, numrec); + else if (flags == JSON) + json_discovery_log(log, numrec); else print_discovery_log(log, numrec); break; case DISC_GET_NUMRECS: - fprintf(stderr, + msg(LOG_ERR, "Get number of discovery log entries failed.\n"); ret = status; break; case DISC_GET_LOG: - fprintf(stderr, "Get discovery log entries failed.\n"); + msg(LOG_ERR, "Get discovery log entries failed.\n"); ret = status; break; case DISC_NO_LOG: @@ -1257,12 +1441,12 @@ static int do_discover(char *argstr, bool connect) ret = -EAGAIN; break; case DISC_NOT_EQUAL: - fprintf(stderr, + msg(LOG_ERR, "Numrec values of last two get discovery log page not equal\n"); ret = -EBADSLT; break; default: - fprintf(stderr, "Get discovery log page failed: %d\n", ret); + msg(LOG_ERR, "Get discovery log page failed: %d\n", ret); break; } @@ -1273,30 +1457,33 @@ static int discover_from_conf_file(const char *desc, char *argstr, const struct argconfig_commandline_options *opts, bool connect) { FILE *f; - char line[256], *ptr, *args, **argv; + char line[256], *ptr, *all_args, *args, **argv; int argc, err, ret = 0; f = fopen(PATH_NVMF_DISC, "r"); if (f == NULL) { - fprintf(stderr, "No discover params given and no %s conf\n", + msg(LOG_ERR, "No discover params given and no %s\n", PATH_NVMF_DISC); return -EINVAL; } while (fgets(line, sizeof(line), f) != NULL) { + enum nvme_print_flags flags; + if (line[0] == '#' || line[0] == '\n') continue; args = strdup(line); if (!args) { - fprintf(stderr, "failed to strdup args\n"); + msg(LOG_ERR, "failed to strdup args\n"); ret = -ENOMEM; goto out; } + all_args = args; argv = calloc(MAX_DISC_ARGS, BUF_SIZE); if (!argv) { - fprintf(stderr, "failed to allocate argv vector\n"); + msg(LOG_ERR, "failed to allocate argv vector: %m\n"); free(args); ret = -ENOMEM; goto out; @@ -1311,28 +1498,38 @@ static int discover_from_conf_file(const char *desc, char *argstr, if (err) goto free_and_continue; - if (cfg.persistent && !cfg.keep_alive_tmo) - cfg.keep_alive_tmo = NVMF_DEF_DISC_TMO; + if (!fabrics_cfg.transport || !fabrics_cfg.traddr) + goto free_and_continue; + + err = flags = validate_output_format(fabrics_cfg.output_format); + if (err < 0) + goto free_and_continue; + set_discovery_kato(&fabrics_cfg); - if (traddr_is_hostname(&cfg)) { - ret = hostname2traddr(&cfg); + if (traddr_is_hostname(&fabrics_cfg)) { + ret = hostname2traddr(&fabrics_cfg); if (ret) goto out; } + if (!fabrics_cfg.trsvcid) + discovery_trsvcid(&fabrics_cfg); + err = build_options(argstr, BUF_SIZE, true); if (err) { ret = err; goto free_and_continue; } - err = do_discover(argstr, connect); + err = do_discover(argstr, connect, flags); if (err) ret = err; free_and_continue: - free(args); + free(all_args); free(argv); + fabrics_cfg.transport = fabrics_cfg.traddr = + fabrics_cfg.trsvcid = fabrics_cfg.host_traddr = NULL; } out: @@ -1344,59 +1541,78 @@ int fabrics_discover(const char *desc, int argc, char **argv, bool connect) { char argstr[BUF_SIZE]; int ret; + enum nvme_print_flags flags; + bool quiet = false; OPT_ARGS(opts) = { - OPT_LIST("transport", 't', &cfg.transport, "transport type"), - OPT_LIST("traddr", 'a', &cfg.traddr, "transport address"), - OPT_LIST("trsvcid", 's', &cfg.trsvcid, "transport service id (e.g. IP port)"), - OPT_LIST("host-traddr", 'w', &cfg.host_traddr, "host traddr (e.g. FC WWN's)"), - OPT_LIST("hostnqn", 'q', &cfg.hostnqn, "user-defined hostnqn (if default not used)"), - OPT_LIST("hostid", 'I', &cfg.hostid, "user-defined hostid (if default not used)"), - OPT_LIST("raw", 'r', &cfg.raw, "raw output file"), - OPT_LIST("device", 'd', &cfg.device, "use existing discovery controller device"), - OPT_INT("keep-alive-tmo", 'k', &cfg.keep_alive_tmo, "keep alive timeout period in seconds"), - OPT_INT("reconnect-delay", 'c', &cfg.reconnect_delay, "reconnect timeout period in seconds"), - OPT_INT("ctrl-loss-tmo", 'l', &cfg.ctrl_loss_tmo, "controller loss timeout period in seconds"), - OPT_INT("tos", 'T', &cfg.tos, "type of service"), - OPT_FLAG("hdr_digest", 'g', &cfg.hdr_digest, "enable transport protocol header digest (TCP transport)"), - OPT_FLAG("data_digest", 'G', &cfg.data_digest, "enable transport protocol data digest (TCP transport)"), - OPT_INT("nr-io-queues", 'i', &cfg.nr_io_queues, "number of io queues to use (default is core count)"), - OPT_INT("nr-write-queues", 'W', &cfg.nr_write_queues, "number of write queues to use (default 0)"), - OPT_INT("nr-poll-queues", 'P', &cfg.nr_poll_queues, "number of poll queues to use (default 0)"), - OPT_INT("queue-size", 'Q', &cfg.queue_size, "number of io queue elements to use (default 128)"), - OPT_FLAG("persistent", 'p', &cfg.persistent, "persistent discovery connection"), - OPT_FLAG("quiet", 'S', &cfg.quiet, "suppress already connected errors"), - OPT_FLAG("matching", 'm', &cfg.matching_only, "connect only records matching the traddr"), + OPT_LIST("transport", 't', &fabrics_cfg.transport, "transport type"), + OPT_LIST("traddr", 'a', &fabrics_cfg.traddr, "transport address"), + OPT_LIST("trsvcid", 's', &fabrics_cfg.trsvcid, "transport service id (e.g. IP port)"), + OPT_LIST("host-traddr", 'w', &fabrics_cfg.host_traddr, "host traddr (e.g. FC WWN's)"), + OPT_LIST("hostnqn", 'q', &fabrics_cfg.hostnqn, "user-defined hostnqn (if default not used)"), + OPT_LIST("hostid", 'I', &fabrics_cfg.hostid, "user-defined hostid (if default not used)"), + OPT_LIST("raw", 'r', &fabrics_cfg.raw, "raw output file"), + OPT_LIST("device", 'd', &fabrics_cfg.device, "existing discovery controller device"), + OPT_INT("keep-alive-tmo", 'k', &fabrics_cfg.keep_alive_tmo, "keep alive timeout period in seconds"), + OPT_INT("reconnect-delay", 'c', &fabrics_cfg.reconnect_delay, "reconnect timeout period in seconds"), + OPT_INT("ctrl-loss-tmo", 'l', &fabrics_cfg.ctrl_loss_tmo, "controller loss timeout period in seconds"), + OPT_INT("tos", 'T', &fabrics_cfg.tos, "type of service"), + OPT_FLAG("hdr_digest", 'g', &fabrics_cfg.hdr_digest, "enable transport protocol header digest (TCP transport)"), + OPT_FLAG("data_digest", 'G', &fabrics_cfg.data_digest, "enable transport protocol data digest (TCP transport)"), + OPT_INT("nr-io-queues", 'i', &fabrics_cfg.nr_io_queues, "number of io queues to use (default is core count)"), + OPT_INT("nr-write-queues", 'W', &fabrics_cfg.nr_write_queues, "number of write queues to use (default 0)"), + OPT_INT("nr-poll-queues", 'P', &fabrics_cfg.nr_poll_queues, "number of poll queues to use (default 0)"), + OPT_INT("queue-size", 'Q', &fabrics_cfg.queue_size, "number of io queue elements to use (default 128)"), + OPT_FLAG("persistent", 'p', &fabrics_cfg.persistent, "persistent discovery connection"), + OPT_FLAG("quiet", 'S', &quiet, "suppress already connected errors"), + OPT_FLAG("matching", 'm', &fabrics_cfg.matching_only, "connect only records matching the traddr"), + OPT_FMT("output-format", 'o', &fabrics_cfg.output_format, output_format), OPT_END() }; - cfg.tos = -1; + fabrics_cfg.tos = -1; ret = argconfig_parse(argc, argv, desc, opts); if (ret) goto out; - if (cfg.device && !strcmp(cfg.device, "none")) - cfg.device = NULL; + ret = flags = validate_output_format(fabrics_cfg.output_format); + if (ret < 0) + goto out; + if (fabrics_cfg.device && strcmp(fabrics_cfg.device, "none")) { + fabrics_cfg.device = strdup(fabrics_cfg.device); + if (!fabrics_cfg.device) { + ret = -ENOMEM; + goto out; + } + } - cfg.nqn = NVME_DISC_SUBSYS_NAME; + if (quiet) + log_level = LOG_WARNING; - if (!cfg.transport && !cfg.traddr) { + if (fabrics_cfg.device && !strcmp(fabrics_cfg.device, "none")) + fabrics_cfg.device = NULL; + + fabrics_cfg.nqn = NVME_DISC_SUBSYS_NAME; + + if (!fabrics_cfg.transport && !fabrics_cfg.traddr) { ret = discover_from_conf_file(desc, argstr, opts, connect); } else { - if (cfg.persistent && !cfg.keep_alive_tmo) - cfg.keep_alive_tmo = NVMF_DEF_DISC_TMO; + set_discovery_kato(&fabrics_cfg); - if (traddr_is_hostname(&cfg)) { - ret = hostname2traddr(&cfg); + if (traddr_is_hostname(&fabrics_cfg)) { + ret = hostname2traddr(&fabrics_cfg); if (ret) goto out; } + if (!fabrics_cfg.trsvcid) + discovery_trsvcid(&fabrics_cfg); + ret = build_options(argstr, BUF_SIZE, true); if (ret) goto out; - ret = do_discover(argstr, connect); + ret = do_discover(argstr, connect, flags); } out: @@ -1409,35 +1625,35 @@ int fabrics_connect(const char *desc, int argc, char **argv) int instance, ret; OPT_ARGS(opts) = { - OPT_LIST("transport", 't', &cfg.transport, "transport type"), - OPT_LIST("nqn", 'n', &cfg.nqn, "nqn name"), - OPT_LIST("traddr", 'a', &cfg.traddr, "transport address"), - OPT_LIST("trsvcid", 's', &cfg.trsvcid, "transport service id (e.g. IP port)"), - OPT_LIST("host-traddr", 'w', &cfg.host_traddr, "host traddr (e.g. FC WWN's)"), - OPT_LIST("hostnqn", 'q', &cfg.hostnqn, "user-defined hostnqn"), - OPT_LIST("hostid", 'I', &cfg.hostid, "user-defined hostid (if default not used)"), - OPT_INT("nr-io-queues", 'i', &cfg.nr_io_queues, "number of io queues to use (default is core count)"), - OPT_INT("nr-write-queues", 'W', &cfg.nr_write_queues, "number of write queues to use (default 0)"), - OPT_INT("nr-poll-queues", 'P', &cfg.nr_poll_queues, "number of poll queues to use (default 0)"), - OPT_INT("queue-size", 'Q', &cfg.queue_size, "number of io queue elements to use (default 128)"), - OPT_INT("keep-alive-tmo", 'k', &cfg.keep_alive_tmo, "keep alive timeout period in seconds"), - OPT_INT("reconnect-delay", 'c', &cfg.reconnect_delay, "reconnect timeout period in seconds"), - OPT_INT("ctrl-loss-tmo", 'l', &cfg.ctrl_loss_tmo, "controller loss timeout period in seconds"), - OPT_INT("tos", 'T', &cfg.tos, "type of service"), - OPT_FLAG("duplicate-connect", 'D', &cfg.duplicate_connect, "allow duplicate connections between same transport host and subsystem port"), - OPT_FLAG("disable-sqflow", 'd', &cfg.disable_sqflow, "disable controller sq flow control (default false)"), - OPT_FLAG("hdr-digest", 'g', &cfg.hdr_digest, "enable transport protocol header digest (TCP transport)"), - OPT_FLAG("data-digest", 'G', &cfg.data_digest, "enable transport protocol data digest (TCP transport)"), + OPT_LIST("transport", 't', &fabrics_cfg.transport, "transport type"), + OPT_LIST("nqn", 'n', &fabrics_cfg.nqn, "nqn name"), + OPT_LIST("traddr", 'a', &fabrics_cfg.traddr, "transport address"), + OPT_LIST("trsvcid", 's', &fabrics_cfg.trsvcid, "transport service id (e.g. IP port)"), + OPT_LIST("host-traddr", 'w', &fabrics_cfg.host_traddr, "host traddr (e.g. FC WWN's)"), + OPT_LIST("hostnqn", 'q', &fabrics_cfg.hostnqn, "user-defined hostnqn"), + OPT_LIST("hostid", 'I', &fabrics_cfg.hostid, "user-defined hostid (if default not used)"), + OPT_INT("nr-io-queues", 'i', &fabrics_cfg.nr_io_queues, "number of io queues to use (default is core count)"), + OPT_INT("nr-write-queues", 'W', &fabrics_cfg.nr_write_queues, "number of write queues to use (default 0)"), + OPT_INT("nr-poll-queues", 'P', &fabrics_cfg.nr_poll_queues, "number of poll queues to use (default 0)"), + OPT_INT("queue-size", 'Q', &fabrics_cfg.queue_size, "number of io queue elements to use (default 128)"), + OPT_INT("keep-alive-tmo", 'k', &fabrics_cfg.keep_alive_tmo, "keep alive timeout period in seconds"), + OPT_INT("reconnect-delay", 'c', &fabrics_cfg.reconnect_delay, "reconnect timeout period in seconds"), + OPT_INT("ctrl-loss-tmo", 'l', &fabrics_cfg.ctrl_loss_tmo, "controller loss timeout period in seconds"), + OPT_INT("tos", 'T', &fabrics_cfg.tos, "type of service"), + OPT_FLAG("duplicate-connect", 'D', &fabrics_cfg.duplicate_connect, "allow duplicate connections between same transport host and subsystem port"), + OPT_FLAG("disable-sqflow", 'd', &fabrics_cfg.disable_sqflow, "disable controller sq flow control (default false)"), + OPT_FLAG("hdr-digest", 'g', &fabrics_cfg.hdr_digest, "enable transport protocol header digest (TCP transport)"), + OPT_FLAG("data-digest", 'G', &fabrics_cfg.data_digest, "enable transport protocol data digest (TCP transport)"), OPT_END() }; - cfg.tos = -1; + fabrics_cfg.tos = -1; ret = argconfig_parse(argc, argv, desc, opts); if (ret) goto out; - if (traddr_is_hostname(&cfg)) { - ret = hostname2traddr(&cfg); + if (traddr_is_hostname(&fabrics_cfg)) { + ret = hostname2traddr(&fabrics_cfg); if (ret) goto out; } @@ -1446,8 +1662,8 @@ int fabrics_connect(const char *desc, int argc, char **argv) if (ret) goto out; - if (!cfg.nqn) { - fprintf(stderr, "need a -n argument\n"); + if (!fabrics_cfg.nqn) { + msg(LOG_ERR, "need a -n argument\n"); ret = -EINVAL; goto out; } @@ -1472,7 +1688,7 @@ static int scan_sys_nvme_filter(const struct dirent *d) /* * Returns 1 if disconnect occurred, 0 otherwise. */ -static int disconnect_subsys(char *nqn, char *ctrl) +static int disconnect_subsys(const char *nqn, char *ctrl) { char *sysfs_nqn_path = NULL, *sysfs_del_path = NULL; char subsysnqn[NVMF_NQN_SIZE] = {}; @@ -1485,7 +1701,7 @@ static int disconnect_subsys(char *nqn, char *ctrl) fd = open(sysfs_nqn_path, O_RDONLY); if (fd < 0) { - fprintf(stderr, "Failed to open %s: %s\n", + msg(LOG_ERR, "Failed to open %s: %s\n", sysfs_nqn_path, strerror(errno)); goto free; } @@ -1510,7 +1726,7 @@ static int disconnect_subsys(char *nqn, char *ctrl) /* * Returns the number of controllers successfully disconnected. */ -static int disconnect_by_nqn(char *nqn) +static int disconnect_by_nqn(const char *nqn) { struct dirent **devices = NULL; int i, n, ret = 0; @@ -1532,7 +1748,7 @@ static int disconnect_by_nqn(char *nqn) return ret; } -static int disconnect_by_device(char *device) +static int disconnect_by_device(const char *device) { int instance; @@ -1549,8 +1765,8 @@ int fabrics_disconnect(const char *desc, int argc, char **argv) int ret; OPT_ARGS(opts) = { - OPT_LIST("nqn", 'n', &cfg.nqn, nqn), - OPT_LIST("device", 'd', &cfg.device, device), + OPT_LIST("nqn", 'n', &fabrics_cfg.nqn, nqn), + OPT_LIST("device", 'd', &fabrics_cfg.device, device), OPT_END() }; @@ -1558,29 +1774,29 @@ int fabrics_disconnect(const char *desc, int argc, char **argv) if (ret) goto out; - if (!cfg.nqn && !cfg.device) { - fprintf(stderr, "need a -n or -d argument\n"); + if (!fabrics_cfg.nqn && !fabrics_cfg.device) { + msg(LOG_ERR, "need a -n or -d argument\n"); ret = -EINVAL; goto out; } - if (cfg.nqn) { - ret = disconnect_by_nqn(cfg.nqn); + if (fabrics_cfg.nqn) { + ret = disconnect_by_nqn(fabrics_cfg.nqn); if (ret < 0) - fprintf(stderr, "Failed to disconnect by NQN: %s\n", - cfg.nqn); + msg(LOG_ERR, "Failed to disconnect by NQN: %s\n", + fabrics_cfg.nqn); else { - printf("NQN:%s disconnected %d controller(s)\n", cfg.nqn, ret); + printf("NQN:%s disconnected %d controller(s)\n", fabrics_cfg.nqn, ret); ret = 0; } } - if (cfg.device) { - ret = disconnect_by_device(cfg.device); + if (fabrics_cfg.device) { + ret = disconnect_by_device(fabrics_cfg.device); if (ret) - fprintf(stderr, + msg(LOG_ERR, "Failed to disconnect by device name: %s\n", - cfg.device); + fabrics_cfg.device); } out: @@ -1600,9 +1816,9 @@ int fabrics_disconnect_all(const char *desc, int argc, char **argv) if (err) goto out; - err = scan_subsystems(&t, NULL, 0); + err = scan_subsystems(&t, NULL, 0, 0, NULL); if (err) { - fprintf(stderr, "Failed to scan namespaces\n"); + msg(LOG_ERR, "Failed to scan namespaces\n"); goto out; } @@ -1612,7 +1828,7 @@ int fabrics_disconnect_all(const char *desc, int argc, char **argv) for (j = 0; j < s->nr_ctrls; j++) { struct nvme_ctrl *c = &s->ctrls[j]; - if (!strcmp(c->transport, "pcie")) + if (!c->transport || !strcmp(c->transport, "pcie")) continue; err = disconnect_by_device(c->name); if (err) diff --git a/fabrics.h b/fabrics.h index f5b8eaf..41e6a2d 100644 --- a/fabrics.h +++ b/fabrics.h @@ -10,4 +10,46 @@ extern int fabrics_connect(const char *desc, int argc, char **argv); extern int fabrics_disconnect(const char *desc, int argc, char **argv); extern int fabrics_disconnect_all(const char *desc, int argc, char **argv); +/* Symbols used by monitor.c */ + +const char *arg_str(const char * const *strings, size_t array_size, size_t idx); + +struct fabrics_config { + const char *nqn; + const char *transport; + const char *traddr; + const char *trsvcid; + const char *host_traddr; + const char *hostnqn; + const char *hostid; + int nr_io_queues; + int nr_write_queues; + int nr_poll_queues; + int queue_size; + int keep_alive_tmo; + int reconnect_delay; + int ctrl_loss_tmo; + int tos; + const char *raw; + char *device; + int duplicate_connect; + int disable_sqflow; + int hdr_digest; + int data_digest; + bool persistent; + bool matching_only; + const char *output_format; +}; +extern struct fabrics_config fabrics_cfg; + +extern const char *const trtypes[]; + +#define BUF_SIZE 4096 + +int build_options(char *argstr, int max_len, bool discover); +int do_discover(char *argstr, bool connect, enum nvme_print_flags flags); +int ctrl_instance(const char *device); +char *parse_conn_arg(const char *conargs, const char delim, const char *field); +int remove_ctrl(int instance); + #endif diff --git a/linux/nvme.h b/linux/nvme.h index f2c4fdb..4ad09ee 100644 --- a/linux/nvme.h +++ b/linux/nvme.h @@ -70,6 +70,7 @@ static inline uint64_t le64_to_cpu(__le64 x) #define NVME_DISC_SUBSYS_NAME "nqn.2014-08.org.nvmexpress.discovery" #define NVME_RDMA_IP_PORT 4420 +#define NVME_DISC_IP_PORT 8009 #define NVME_NSID_ALL 0xffffffff @@ -139,6 +140,13 @@ enum { NVMF_TCP_SECTYPE_TLS = 1, /* Transport Layer Security */ }; +/* I/O Command Sets + */ +enum { + NVME_IOCS_NVM = 0x00, + NVME_IOCS_ZONED = 0x02, +}; + #define NVME_AQ_DEPTH 32 #define NVME_NR_AEN_COMMANDS 1 #define NVME_AQ_BLK_MQ_DEPTH (NVME_AQ_DEPTH - NVME_NR_AEN_COMMANDS) @@ -172,7 +180,8 @@ enum { NVME_REG_PMRSTS = 0x0e08, /* Persistent Memory Region Status */ NVME_REG_PMREBS = 0x0e0c, /* Persistent Memory Region Elasticity Buffer Size */ NVME_REG_PMRSWTP= 0x0e10, /* Persistent Memory Region Sustained Write Throughput */ - NVME_REG_PMRMSC = 0x0e14, /* Persistent Memory Region Controller Memory Space Control */ + NVME_REG_PMRMSCL= 0x0e14, /* Persistent Memory Region Controller Memory Space Control Lower */ + NVME_REG_PMRMSCU= 0x0e18, /* Persistent Memory Region Controller Memory Space Control Upper*/ NVME_REG_DBS = 0x1000, /* SQ 0 Tail Doorbell */ }; @@ -325,7 +334,7 @@ struct nvme_id_ctrl { __u8 vwc; __le16 awun; __le16 awupf; - __u8 nvscc; + __u8 icsvscc; __u8 nwpc; __le16 acwu; __u8 rsvd534[2]; @@ -337,9 +346,10 @@ struct nvme_id_ctrl { __le32 ioccsz; __le32 iorcsz; __le16 icdoff; - __u8 ctrattr; + __u8 fcatt; __u8 msdbd; - __u8 rsvd1804[244]; + __le16 ofcs; + __u8 rsvd1806[242]; struct nvme_id_power_state psd[32]; __u8 vs[1024]; }; @@ -398,7 +408,10 @@ struct nvme_id_ns { __le16 npdg; __le16 npda; __le16 nows; - __u8 rsvd74[18]; + __le16 mssrl; + __le32 mcl; + __u8 msrc; + __u8 rsvd81[11]; __le32 anagrpid; __u8 rsvd96[3]; __u8 nsattr; @@ -411,19 +424,30 @@ struct nvme_id_ns { __u8 vs[3712]; }; +struct nvme_id_iocs { + __le64 iocs[512]; +}; + enum { NVME_ID_CNS_NS = 0x00, NVME_ID_CNS_CTRL = 0x01, NVME_ID_CNS_NS_ACTIVE_LIST = 0x02, NVME_ID_CNS_NS_DESC_LIST = 0x03, NVME_ID_CNS_NVMSET_LIST = 0x04, + NVME_ID_CNS_CSI_ID_NS = 0x05, + NVME_ID_CNS_CSI_ID_CTRL = 0x06, + NVME_ID_CNS_CSI_NS_ACTIVE_LIST = 0x07, NVME_ID_CNS_NS_PRESENT_LIST = 0x10, NVME_ID_CNS_NS_PRESENT = 0x11, NVME_ID_CNS_CTRL_NS_LIST = 0x12, NVME_ID_CNS_CTRL_LIST = 0x13, + NVME_ID_CNS_PRIMARY_CTRL_CAPS = 0x14, NVME_ID_CNS_SCNDRY_CTRL_LIST = 0x15, NVME_ID_CNS_NS_GRANULARITY = 0x16, NVME_ID_CNS_UUID_LIST = 0x17, + NVME_ID_CNS_CSI_NS_PRESENT_LIST = 0x1a, + NVME_ID_CNS_CSI_NS_PRESENT = 0x1b, + NVME_ID_CNS_CSI = 0x1c, }; enum { @@ -468,11 +492,13 @@ struct nvme_ns_id_desc { #define NVME_NIDT_EUI64_LEN 8 #define NVME_NIDT_NGUID_LEN 16 #define NVME_NIDT_UUID_LEN 16 +#define NVME_NIDT_CSI_LEN 1 enum { NVME_NIDT_EUI64 = 0x01, NVME_NIDT_NGUID = 0x02, NVME_NIDT_UUID = 0x03, + NVME_NIDT_CSI = 0x04, }; #define NVME_MAX_NVMSET 31 @@ -627,12 +653,14 @@ enum { NVME_ST_VALID_SCT = 1 << 2, NVME_ST_VALID_SC = 1 << 3, NVME_ST_REPORTS = 20, + NVME_ST_LOG_ENTRY_SIZE = 28, + NVME_ST_LOG_HEAD_SIZE = 4, }; struct nvme_self_test_log { __u8 crnt_dev_selftest_oprn; __u8 crnt_dev_selftest_compln; - __u8 rsvd[2]; + __u8 rsvd2[2]; struct nvme_self_test_res result[20]; } __attribute__((packed)); @@ -714,6 +742,209 @@ struct nvme_ana_rsp_hdr { __le16 rsvd10[3]; }; +/* persistent event type 02h */ +struct nvme_fw_commit_event { + __le64 old_fw_rev; + __le64 new_fw_rev; + __u8 fw_commit_action; + __u8 fw_slot; + __u8 sct_fw; + __u8 sc_fw; + __le16 vndr_assign_fw_commit_rc; +} __attribute__((packed)); + +/* persistent event type 03h */ +struct nvme_time_stamp_change_event { + __le64 previous_timestamp; + __le64 ml_secs_since_reset; +}; + +/* persistent event type 04h */ +struct nvme_power_on_reset_info_list { + __le16 cid; + __u8 fw_act; + __u8 op_in_prog; + __u8 rsvd4[12]; + __le32 ctrl_power_cycle; + __le64 power_on_ml_seconds; + __le64 ctrl_time_stamp; +} __attribute__((packed)); + +/* persistent event type 05h */ +struct nvme_nss_hw_err_event { + __le16 nss_hw_err_event_code; + __u8 rsvd2[2]; + __u8 *add_hw_err_info; +}; + +/* persistent event type 06h */ +struct nvme_change_ns_event { + __le32 nsmgt_cdw10; + __u8 rsvd4[4]; + __le64 nsze; + __u8 rsvd16[8]; + __le64 nscap; + __u8 flbas; + __u8 dps; + __u8 nmic; + __u8 rsvd35; + __le32 ana_grp_id; + __le16 nvmset_id; + __le16 rsvd42; + __le32 nsid; +}; + +/* persistent event type 07h */ +struct nvme_format_nvm_start_event { + __le32 nsid; + __u8 fna; + __u8 rsvd5[3]; + __le32 format_nvm_cdw10; +}; + +/* persistent event type 08h */ +struct nvme_format_nvm_compln_event { + __le32 nsid; + __u8 smallest_fpi; + __u8 format_nvm_status; + __le16 compln_info; + __le32 status_field; +}; + +/* persistent event type 09h */ +struct nvme_sanitize_start_event { + __le32 sani_cap; + __le32 sani_cdw10; + __le32 sani_cdw11; +}; + +/* persistent event type 0Ah */ +struct nvme_sanitize_compln_event { + __le16 sani_prog; + __le16 sani_status; + __le16 cmpln_info; + __u8 rsvd6[2]; +}; + +/* persistent event type 0Dh */ +struct nvme_thermal_exc_event { + __u8 over_temp; + __u8 threshold; +}; + +/* persistent event entry head */ +struct nvme_persistent_event_entry_head { + __u8 etype; + __u8 etype_rev; + __u8 ehl; + __u8 rsvd3; + __le16 ctrl_id; + __le64 etimestamp; + __u8 rsvd14[6]; + __le16 vsil; + __le16 el; +} __attribute__((packed)); + +/* persistent event log head */ +struct nvme_persistent_event_log_head { + __u8 log_id; + __u8 rsvd1[3]; + __le32 tnev; + __le64 tll; + __u8 log_rev; + __u8 rsvd17; + __le16 head_len; + __le64 timestamp; + __u8 poh[16]; + __le64 pcc; + __le16 vid; + __le16 ssvid; + __u8 sn[20]; + __u8 mn[40]; + __u8 subnqn[256]; + __u8 rsvd372[108]; + __u8 supp_event_bm[32]; +} __attribute__((packed)); + +enum nvme_persistent_event_types { + NVME_SMART_HEALTH_EVENT = 0x01, + NVME_FW_COMMIT_EVENT = 0x02, + NVME_TIMESTAMP_EVENT = 0x03, + NVME_POWER_ON_RESET_EVENT = 0x04, + NVME_NSS_HW_ERROR_EVENT = 0x05, + NVME_CHANGE_NS_EVENT = 0x06, + NVME_FORMAT_START_EVENT = 0x07, + NVME_FORMAT_COMPLETION_EVENT = 0x08, + NVME_SANITIZE_START_EVENT = 0x09, + NVME_SANITIZE_COMPLETION_EVENT = 0x0a, + NVME_THERMAL_EXCURSION_EVENT = 0x0d +}; + +enum nvme_persistent_event_log_actions { + NVME_PEVENT_LOG_READ = 0x0, + NVME_PEVENT_LOG_EST_CTX_AND_READ = 0x1, + NVME_PEVENT_LOG_RELEASE_CTX = 0x2, +}; + +/** + * struct nvme_event_agg_log_page - is common for both + * predictable latency event aggregate log and endurance + * group event aggregate log + * @num_entries: indicates the number of entries in the list. + * @entries: indicates NVMSET ID or ENDURANCE Group ID entries + */ +struct nvme_event_agg_log_page { + __le64 num_entries; + __le16 entries[]; +}; + +struct nvme_predlat_per_nvmset_log_page { + __u8 status; + __u8 rsvd1; + __le16 event_type; + __u8 rsvd4[28]; + __le64 dtwin_rtyp; + __le64 dtwin_wtyp; + __le64 dtwin_timemax; + __le64 ndwin_timemin_high; + __le64 ndwin_timemin_low; + __u8 rsvd72[56]; + __le64 dtwin_restimate; + __le64 dtwin_westimate; + __le64 dtwin_testimate; + __u8 rsvd152[360]; +}; + +struct nvme_lba_status_range_desc { + __le64 rslba; + __le32 rnlb; + __u8 rsvd12[4]; +}; + +struct nvme_lba_status_ns_element { + __le32 neid; + __le32 nlrd; + __u8 ratype; + __u8 rsvd9[7]; +}; + +struct nvme_lba_status_hdr { + __le32 lslplen; + __le32 nlslne; + __le32 estulb; + __u8 rsvd12[2]; + __le16 lsgc; +}; + +struct nvme_resv_notif_log { + __le64 log_page_count; + __u8 resv_notif_log_type; + __u8 num_logs; + __u8 rsvd10[2]; + __le32 nsid; + __u8 rsvd16[48]; +}; + enum { NVME_SMART_CRIT_SPARE = 1 << 0, NVME_SMART_CRIT_TEMPERATURE = 1 << 1, @@ -814,6 +1045,10 @@ enum nvme_opcode { nvme_cmd_resv_report = 0x0e, nvme_cmd_resv_acquire = 0x11, nvme_cmd_resv_release = 0x15, + nvme_cmd_copy = 0x19, + nvme_zns_cmd_mgmt_send = 0x79, + nvme_zns_cmd_mgmt_recv = 0x7a, + nvme_zns_cmd_append = 0x7d, }; /* @@ -919,6 +1154,7 @@ enum { NVME_RW_DSM_LATENCY_LOW = 3 << 4, NVME_RW_DSM_SEQ_REQ = 1 << 6, NVME_RW_DSM_COMPRESSED = 1 << 7, + NVME_RW_PIREMAP = 1 << 9, NVME_RW_PRINFO_PRCHK_REF = 1 << 10, NVME_RW_PRINFO_PRCHK_APP = 1 << 11, NVME_RW_PRINFO_PRCHK_GUARD = 1 << 12, @@ -940,6 +1176,16 @@ struct nvme_dsm_range { __le64 slba; }; +struct nvme_copy_range { + __u8 rsvd0[8]; + __le64 slba; + __le16 nlb; + __u8 rsvd18[6]; + __le32 eilbrt; + __le16 elbatm; + __le16 elbat; +}; + /* Features */ struct nvme_feat_auto_pst { __le64 entries[32]; @@ -989,6 +1235,32 @@ enum { NVME_SQ_PRIO_HIGH = (1 << 1), NVME_SQ_PRIO_MEDIUM = (2 << 1), NVME_SQ_PRIO_LOW = (3 << 1), + NVME_LOG_ERROR = 0x01, + NVME_LOG_SMART = 0x02, + NVME_LOG_FW_SLOT = 0x03, + NVME_LOG_CHANGED_NS = 0x04, + NVME_LOG_CMD_EFFECTS = 0x05, + NVME_LOG_DEVICE_SELF_TEST = 0x06, + NVME_LOG_TELEMETRY_HOST = 0x07, + NVME_LOG_TELEMETRY_CTRL = 0x08, + NVME_LOG_ENDURANCE_GROUP = 0x09, + NVME_LOG_PRELAT_PER_NVMSET = 0x0a, + NVME_LOG_ANA = 0x0c, + NVME_LOG_PRELAT_EVENT_AGG = 0x0b, + NVME_LOG_PERSISTENT_EVENT = 0x0d, + NVME_LOG_LBA_STATUS = 0x0e, + NVME_LOG_ENDURANCE_GROUP_EVENT_AGG = 0x0f, + NVME_LOG_DISC = 0x70, + NVME_LOG_RESERVATION = 0x80, + NVME_LOG_SANITIZE = 0x81, + NVME_LOG_ZONE_CHANGED_LIST = 0xbf, + NVME_FWACT_REPL = (0 << 3), + NVME_FWACT_REPL_ACTV = (1 << 3), + NVME_FWACT_ACTV = (2 << 3), +}; + +enum nvme_feat { + NVME_FEAT_NONE = 0x0, NVME_FEAT_ARBITRATION = 0x01, NVME_FEAT_POWER_MGMT = 0x02, NVME_FEAT_LBA_RANGE = 0x03, @@ -1009,30 +1281,17 @@ enum { NVME_FEAT_RRL = 0x12, NVME_FEAT_PLM_CONFIG = 0x13, NVME_FEAT_PLM_WINDOW = 0x14, + NVME_LBA_STATUS_INFO = 0x15, NVME_FEAT_HOST_BEHAVIOR = 0x16, NVME_FEAT_SANITIZE = 0x17, + NVME_FEAT_ENDURANCE = 0x18, + NVME_FEAT_IOCS_PROFILE = 0x19, NVME_FEAT_SW_PROGRESS = 0x80, NVME_FEAT_HOST_ID = 0x81, NVME_FEAT_RESV_MASK = 0x82, NVME_FEAT_RESV_PERSIST = 0x83, NVME_FEAT_WRITE_PROTECT = 0x84, - NVME_LOG_ERROR = 0x01, - NVME_LOG_SMART = 0x02, - NVME_LOG_FW_SLOT = 0x03, - NVME_LOG_CHANGED_NS = 0x04, - NVME_LOG_CMD_EFFECTS = 0x05, - NVME_LOG_DEVICE_SELF_TEST = 0x06, - NVME_LOG_TELEMETRY_HOST = 0x07, - NVME_LOG_TELEMETRY_CTRL = 0x08, - NVME_LOG_ENDURANCE_GROUP = 0x09, - NVME_LOG_ANA = 0x0c, - NVME_LOG_DISC = 0x70, - NVME_LOG_RESERVATION = 0x80, - NVME_LOG_SANITIZE = 0x81, - NVME_FWACT_REPL = (0 << 3), - NVME_FWACT_REPL_ACTV = (1 << 3), - NVME_FWACT_ACTV = (2 << 3), -}; +} __attribute__ ((__packed__)); enum { NVME_NO_LOG_LSP = 0x0, @@ -1084,6 +1343,7 @@ struct nvme_sanitize_log_page { __le32 est_ovrwrt_time_with_no_deallocate; __le32 est_blk_erase_time_with_no_deallocate; __le32 est_crypto_erase_time_with_no_deallocate; + __u8 rsvd32[480]; }; /* @@ -1224,6 +1484,27 @@ struct nvme_controller_list { __le16 identifier[2047]; }; +struct nvme_primary_ctrl_caps { + __le16 cntlid; /* Controller Identifier */ + __le16 portid; /* Port Identifier */ + __u8 crt; /* Controller Resource Types */ + __u8 rsvd5[27]; + __le32 vqfrt; /* VQ Resources Flexible Total */ + __le32 vqrfa; /* VQ Resources Flexible Assigned */ + __le16 vqrfap; /* VQ Resources Flexible Allocated to Primary */ + __le16 vqprt; /* VQ Resources Private Total */ + __le16 vqfrsm; /* VQ Resources Flexible Secondary Maximum */ + __le16 vqgran; /* VQ Flexible Resource Preferred Granularity */ + __u8 rsvd48[16]; + __le32 vifrt; /* VI Resources Flexible Total */ + __le32 virfa; /* VI Resources Flexible Assigned */ + __u16 virfap; /* VI Resources Flexible Allocated to Primary */ + __u16 viprt; /* VI Resources Private Total */ + __u16 vifrsm; /* VI Resources Flexible Secondary Maximum */ + __u16 vigran; /* VI Flexible Resource Preferred Granularity */ + __u8 rsvd80[4016]; +}; + struct nvme_secondary_controller_entry { __le16 scid; /* Secondary Controller Identifier */ __le16 pcid; /* Primary Controller Identifier */ @@ -1277,6 +1558,7 @@ enum { NVME_SCT_GENERIC = 0x0, NVME_SCT_CMD_SPECIFIC = 0x1, NVME_SCT_MEDIA = 0x2, + NVME_SCT_PATH = 0x3, }; enum { @@ -1316,7 +1598,7 @@ enum { NVME_SC_NS_WRITE_PROTECTED = 0x20, NVME_SC_CMD_INTERRUPTED = 0x21, - NVME_SC_TRANSIENT_TRANSPORT = 0x22, + NVME_SC_TRANSIENT_TRANSPORT = 0x22, NVME_SC_LBA_RANGE = 0x80, NVME_SC_CAP_EXCEEDED = 0x81, @@ -1365,13 +1647,21 @@ enum { NVME_SC_ANA_INVALID_GROUP_ID= 0x124, NVME_SC_ANA_ATTACH_FAIL = 0x125, + /* + * Command Set Specific - Namespace Types commands: + */ + NVME_SC_IOCS_NOT_SUPPORTED = 0x129, + NVME_SC_IOCS_NOT_ENABLED = 0x12A, + NVME_SC_IOCS_COMBINATION_REJECTED = 0x12B, + NVME_SC_INVALID_IOCS = 0x12C, + /* * I/O Command Set Specific - NVM commands: */ NVME_SC_BAD_ATTRIBUTES = 0x180, NVME_SC_INVALID_PI = 0x181, NVME_SC_READ_ONLY = 0x182, - NVME_SC_ONCS_NOT_SUPPORTED = 0x183, + NVME_SC_CMD_SIZE_LIMIT_EXCEEDED = 0x183, /* * I/O Command Set Specific - Fabrics commands: @@ -1385,6 +1675,18 @@ enum { NVME_SC_DISCOVERY_RESTART = 0x190, NVME_SC_AUTH_REQUIRED = 0x191, + /* + * I/O Command Set Specific - Zoned Namespace commands: + */ + NVME_SC_ZONE_BOUNDARY_ERROR = 0x1B8, + NVME_SC_ZONE_IS_FULL = 0x1B9, + NVME_SC_ZONE_IS_READ_ONLY = 0x1BA, + NVME_SC_ZONE_IS_OFFLINE = 0x1BB, + NVME_SC_ZONE_INVALID_WRITE = 0x1BC, + NVME_SC_TOO_MANY_ACTIVE_ZONES = 0x1BD, + NVME_SC_TOO_MANY_OPEN_ZONES = 0x1BE, + NVME_SC_ZONE_INVALID_STATE_TRANSITION = 0x1BF, + /* * Media and Data Integrity Errors: */ @@ -1400,10 +1702,22 @@ enum { /* * Path-related Errors: */ + NVME_SC_INTERNAL_PATH_ERROR = 0x300, NVME_SC_ANA_PERSISTENT_LOSS = 0x301, NVME_SC_ANA_INACCESSIBLE = 0x302, NVME_SC_ANA_TRANSITION = 0x303, + /* + * Controller Detected Path errors + */ + NVME_SC_CTRL_PATHING_ERROR = 0x360, + + /* + * Host Detected Path Errors + */ + NVME_SC_HOST_PATHING_ERROR = 0x370, + NVME_SC_HOST_CMD_ABORT = 0x371, + NVME_SC_CRD = 0x1800, NVME_SC_DNR = 0x4000, }; @@ -1415,4 +1729,149 @@ enum { #define NVME_MINOR(ver) (((ver) >> 8) & 0xff) #define NVME_TERTIARY(ver) ((ver) & 0xff) + +/** + * struct nvme_zns_lbafe - + * zsze: + * zdes: + */ +struct nvme_zns_lbafe { + __le64 zsze; + __u8 zdes; + __u8 rsvd9[7]; +}; + +/** + * struct nvme_zns_id_ns - + * @zoc: + * @ozcs: + * @mar: + * @mor: + * @rrl: + * @frl: + * @lbafe: + * @vs: + */ +struct nvme_zns_id_ns { + __le16 zoc; + __le16 ozcs; + __le32 mar; + __le32 mor; + __le32 rrl; + __le32 frl; + __u8 rsvd20[2796]; + struct nvme_zns_lbafe lbafe[16]; + __u8 rsvd3072[768]; + __u8 vs[256]; +}; + +struct nvme_id_ctrl_nvm { + __u8 vsl; + __u8 wzsl; + __u8 wusl; + __u8 dmrl; + __u32 dmrsl; + __u64 dmsl; + __u8 rsvd16[4080]; +}; + +/** + * struct nvme_zns_id_ctrl - + * @zasl: + */ +struct nvme_zns_id_ctrl { + __u8 zasl; + __u8 rsvd1[4095]; +}; + +#define NVME_ZNS_CHANGED_ZONES_MAX 511 + +/** + * struct nvme_zns_changed_zone_log - ZNS Changed Zone List log + * @nrzid: + * @zid: + */ +struct nvme_zns_changed_zone_log { + __le16 nrzid; + __u8 rsvd2[6]; + __le64 zid[NVME_ZNS_CHANGED_ZONES_MAX]; +}; + +/** + * enum nvme_zns_zt - + */ +enum nvme_zns_zt { + NVME_ZONE_TYPE_SEQWRITE_REQ = 0x2, +}; + +/** + * enum nvme_zns_za - + */ +enum nvme_zns_za { + NVME_ZNS_ZA_ZFC = 1 << 0, + NVME_ZNS_ZA_FZR = 1 << 1, + NVME_ZNS_ZA_RZR = 1 << 2, + NVME_ZNS_ZA_ZDEV = 1 << 7, +}; + +/** + * enum nvme_zns_zs - + */ +enum nvme_zns_zs { + NVME_ZNS_ZS_EMPTY = 0x1, + NVME_ZNS_ZS_IMPL_OPEN = 0x2, + NVME_ZNS_ZS_EXPL_OPEN = 0x3, + NVME_ZNS_ZS_CLOSED = 0x4, + NVME_ZNS_ZS_READ_ONLY = 0xd, + NVME_ZNS_ZS_FULL = 0xe, + NVME_ZNS_ZS_OFFLINE = 0xf, +}; + +/** + * struct nvme_zns_desc - + */ +struct nvme_zns_desc { + __u8 zt; + __u8 zs; + __u8 za; + __u8 rsvd3[5]; + __le64 zcap; + __le64 zslba; + __le64 wp; + __u8 rsvd32[32]; +}; + +/** + * struct nvme_zone_report - + */ +struct nvme_zone_report { + __le64 nr_zones; + __u8 resv8[56]; + struct nvme_zns_desc entries[]; +}; + +enum nvme_zns_send_action { + NVME_ZNS_ZSA_CLOSE = 0x1, + NVME_ZNS_ZSA_FINISH = 0x2, + NVME_ZNS_ZSA_OPEN = 0x3, + NVME_ZNS_ZSA_RESET = 0x4, + NVME_ZNS_ZSA_OFFLINE = 0x5, + NVME_ZNS_ZSA_SET_DESC_EXT = 0x10, +}; + +enum nvme_zns_recv_action { + NVME_ZNS_ZRA_REPORT_ZONES = 0x0, + NVME_ZNS_ZRA_EXTENDED_REPORT_ZONES = 0x1, +}; + +enum nvme_zns_report_options { + NVME_ZNS_ZRAS_REPORT_ALL = 0x0, + NVME_ZNS_ZRAS_REPORT_EMPTY = 0x1, + NVME_ZNS_ZRAS_REPORT_IMPL_OPENED = 0x2, + NVME_ZNS_ZRAS_REPORT_EXPL_OPENED = 0x3, + NVME_ZNS_ZRAS_REPORT_CLOSED = 0x4, + NVME_ZNS_ZRAS_REPORT_FULL = 0x5, + NVME_ZNS_ZRAS_REPORT_READ_ONLY = 0x6, + NVME_ZNS_ZRAS_REPORT_OFFLINE = 0x7, +}; #endif /* _LINUX_NVME_H */ diff --git a/nvme-builtin.h b/nvme-builtin.h index bfb907d..a413d00 100644 --- a/nvme-builtin.h +++ b/nvme-builtin.h @@ -14,10 +14,13 @@ COMMAND_LIST( ENTRY("id-ns-granularity", "Send NVMe Identify Namespace Granularity List, display structure", id_ns_granularity) ENTRY("list-ns", "Send NVMe Identify List, display structure", list_ns) ENTRY("list-ctrl", "Send NVMe Identify Controller List, display structure", list_ctrl) + ENTRY("nvm-id-ctrl", "Send NVMe Identify Controller NVM Command Set, display structure", nvm_id_ctrl) + ENTRY("primary-ctrl-caps", "Send NVMe Identify Primary Controller Capabilities", primary_ctrl_caps) ENTRY("list-secondary", "List Secondary Controllers associated with a Primary Controller", list_secondary_ctrl) ENTRY("ns-descs", "Send NVMe Namespace Descriptor List, display structure", ns_descs) ENTRY("id-nvmset", "Send NVMe Identify NVM Set List, display structure", id_nvmset) ENTRY("id-uuid", "Send NVMe Identify UUID List, display structure", id_uuid) + ENTRY("id-iocs", "Send NVMe Identify I/O Command Set, display structure", id_iocs) ENTRY("create-ns", "Creates a namespace with the provided parameters", create_ns) ENTRY("delete-ns", "Deletes a namespace from the controller", delete_ns) ENTRY("attach-ns", "Attaches a namespace to requested controller(s)", attach_ns) @@ -32,6 +35,12 @@ COMMAND_LIST( ENTRY("error-log", "Retrieve Error Log, show it", get_error_log) ENTRY("effects-log", "Retrieve Command Effects Log, show it", get_effects_log) ENTRY("endurance-log", "Retrieve Endurance Group Log, show it", get_endurance_log) + ENTRY("predictable-lat-log", "Retrieve Predictable Latency per Nvmset Log, show it", get_pred_lat_per_nvmset_log) + ENTRY("pred-lat-event-agg-log", "Retrieve Predictable Latency Event Aggregate Log, show it", get_pred_lat_event_agg_log) + ENTRY("persistent-event-log", "Retrieve Presistent Event Log, show it", get_persistent_event_log) + ENTRY("endurance-event-agg-log", "Retrieve Endurance Group Event Aggregate Log, show it", get_endurance_event_agg_log) + ENTRY("lba-status-log", "Retrieve LBA Status Information Log, show it", get_lba_status_log) + ENTRY("resv-notif-log", "Retrieve Reservation Notification Log, show it", get_resv_notif_log) ENTRY("get-feature", "Get feature and show the resulting value", get_feature) ENTRY("device-self-test", "Perform the necessary tests to observe the performance", device_self_test) ENTRY("self-test-log", "Retrieve the SELF-TEST Log, show it", self_test_log) @@ -51,6 +60,7 @@ COMMAND_LIST( ENTRY("resv-release", "Submit a Reservation Release, return results", resv_release) ENTRY("resv-report", "Submit a Reservation Report, return results", resv_report) ENTRY("dsm", "Submit a Data Set Management command, return results", dsm) + ENTRY("copy", "Submit a Simple Copy command, return results", copy) ENTRY("flush", "Submit a Flush command, return results", flush) ENTRY("compare", "Submit a Compare command, return results", compare) ENTRY("read", "Submit a read command, return results", read_cmd) @@ -74,6 +84,7 @@ COMMAND_LIST( ENTRY("dir-receive", "Submit a Directive Receive command, return results", dir_receive) ENTRY("dir-send", "Submit a Directive Send command, return results", dir_send) ENTRY("virt-mgmt", "Manage Flexible Resources between Primary and Secondary Controller ", virtual_mgmt) + ENTRY("rpmb", "Replay Protection Memory Block commands", rpmb_cmd) ); #endif diff --git a/nvme-filters.c b/nvme-filters.c index a4133f8..17c375f 100644 --- a/nvme-filters.c +++ b/nvme-filters.c @@ -7,6 +7,22 @@ /* global, used for controller specific namespace filter */ int current_index; +int scan_ctrl_namespace_filter(const struct dirent *d) +{ + int c, i, n; + + if (d->d_name[0] == '.') + return 0; + + if (strstr(d->d_name, "nvme")) { + if (sscanf(d->d_name, "nvme%dc%dn%d", &i, &c, &n) == 3) + return 1; + if (sscanf(d->d_name, "nvme%dn%d", &i, &n) == 2) + return 1; + } + return 0; +} + int scan_namespace_filter(const struct dirent *d) { int i, n; diff --git a/nvme-ioctl.c b/nvme-ioctl.c index 39685d6..2801075 100644 --- a/nvme-ioctl.c +++ b/nvme-ioctl.c @@ -145,30 +145,6 @@ int nvme_io(int fd, __u8 opcode, __u64 slba, __u16 nblocks, __u16 control, return ioctl(fd, NVME_IOCTL_SUBMIT_IO, &io); } -int nvme_read(int fd, __u64 slba, __u16 nblocks, __u16 control, __u32 dsmgmt, - __u32 reftag, __u16 apptag, __u16 appmask, void *data, - void *metadata) -{ - return nvme_io(fd, nvme_cmd_read, slba, nblocks, control, dsmgmt, - reftag, apptag, appmask, data, metadata); -} - -int nvme_write(int fd, __u64 slba, __u16 nblocks, __u16 control, __u32 dsmgmt, - __u32 reftag, __u16 apptag, __u16 appmask, void *data, - void *metadata) -{ - return nvme_io(fd, nvme_cmd_write, slba, nblocks, control, dsmgmt, - reftag, apptag, appmask, data, metadata); -} - -int nvme_compare(int fd, __u64 slba, __u16 nblocks, __u16 control, __u32 dsmgmt, - __u32 reftag, __u16 apptag, __u16 appmask, void *data, - void *metadata) -{ - return nvme_io(fd, nvme_cmd_compare, slba, nblocks, control, dsmgmt, - reftag, apptag, appmask, data, metadata); -} - int nvme_verify(int fd, __u32 nsid, __u64 slba, __u16 nblocks, __u16 control, __u32 reftag, __u16 apptag, __u16 appmask) { @@ -251,8 +227,9 @@ int nvme_dsm(int fd, __u32 nsid, __u32 cdw11, struct nvme_dsm_range *dsm, return nvme_submit_io_passthru(fd, &cmd); } -struct nvme_dsm_range *nvme_setup_dsm_range(__u32 *ctx_attrs, __u32 *llbas, - __u64 *slbas, __u16 nr_ranges) +struct nvme_dsm_range *nvme_setup_dsm_range(int *ctx_attrs, int *llbas, + unsigned long long *slbas, + __u16 nr_ranges) { int i; struct nvme_dsm_range *dsm = malloc(nr_ranges * sizeof(*dsm)); @@ -269,6 +246,52 @@ struct nvme_dsm_range *nvme_setup_dsm_range(__u32 *ctx_attrs, __u32 *llbas, return dsm; } +int nvme_copy(int fd, __u32 nsid, struct nvme_copy_range *copy, __u64 sdlba, + __u16 nr, __u8 prinfor, __u8 prinfow, __u8 dtype, __u16 dspec, + __u8 format, int lr, int fua, __u32 ilbrt, __u16 lbatm, + __u16 lbat) +{ + __u32 cdw12 = ((nr - 1) & 0xff) | ((format & 0xf) << 8) | + ((prinfor & 0xf) << 12) | ((dtype & 0xf) << 20) | + ((prinfow & 0xf) << 26) | ((fua & 0x1) << 30) | + ((lr & 0x1) << 31); + + struct nvme_passthru_cmd cmd = { + .opcode = nvme_cmd_copy, + .nsid = nsid, + .addr = (__u64)(uintptr_t)copy, + .data_len = nr * sizeof(*copy), + .cdw10 = sdlba & 0xffffffff, + .cdw11 = sdlba >> 32, + .cdw12 = cdw12, + .cdw13 = (dspec & 0xffff) << 16, + .cdw14 = ilbrt, + .cdw15 = (lbatm << 16) | lbat, + }; + + return nvme_submit_io_passthru(fd, &cmd); +} + +struct nvme_copy_range *nvme_setup_copy_range(int *nlbs, unsigned long long *slbas, + int *eilbrts, int *elbatms, int *elbats, __u16 nr) +{ + struct nvme_copy_range *copy = malloc(nr * sizeof(*copy)); + if (!copy) { + fprintf(stderr, "malloc: %s\n", strerror(errno)); + return NULL; + } + + for (int i = 0; i < nr; i++) { + copy[i].nlb = cpu_to_le16(nlbs[i]); + copy[i].slba = cpu_to_le64(slbas[i]); + copy[i].eilbrt = cpu_to_le32(eilbrts[i]); + copy[i].elbatm = cpu_to_le16(elbatms[i]); + copy[i].elbat = cpu_to_le16(elbats[i]); + } + + return copy; +} + int nvme_resv_acquire(int fd, __u32 nsid, __u8 rtype, __u8 racqa, bool iekey, __u64 crkey, __u64 nrkey) { @@ -365,11 +388,22 @@ int nvme_identify_ns(int fd, __u32 nsid, bool present, void *data) return nvme_identify(fd, nsid, cns, data); } -int nvme_identify_ns_list(int fd, __u32 nsid, bool all, void *data) +int nvme_identify_ns_list_csi(int fd, __u32 nsid, __u8 csi, bool all, void *data) { - int cns = all ? NVME_ID_CNS_NS_PRESENT_LIST : NVME_ID_CNS_NS_ACTIVE_LIST; + int cns; - return nvme_identify(fd, nsid, cns, data); + if (csi) { + cns = all ? NVME_ID_CNS_CSI_NS_PRESENT_LIST : NVME_ID_CNS_CSI_NS_ACTIVE_LIST; + } else { + cns = all ? NVME_ID_CNS_NS_PRESENT_LIST : NVME_ID_CNS_NS_ACTIVE_LIST; + } + + return nvme_identify13(fd, nsid, cns, csi << 24, data); +} + +int nvme_identify_ns_list(int fd, __u32 nsid, bool all, void *data) +{ + return nvme_identify_ns_list_csi(fd, nsid, 0x0, all, data); } int nvme_identify_ctrl_list(int fd, __u32 nsid, __u16 cntid, void *data) @@ -379,6 +413,11 @@ int nvme_identify_ctrl_list(int fd, __u32 nsid, __u16 cntid, void *data) return nvme_identify(fd, nsid, (cntid << 16) | cns, data); } +int nvme_identify_primary_ctrl_caps(int fd, void *data) +{ + return nvme_identify(fd, 0, NVME_ID_CNS_PRIMARY_CTRL_CAPS, data); +} + int nvme_identify_secondary_ctrl_list(int fd, __u32 nsid, __u16 cntid, void *data) { return nvme_identify(fd, nsid, (cntid << 16) | NVME_ID_CNS_SCNDRY_CTRL_LIST, data); @@ -405,6 +444,26 @@ int nvme_identify_uuid(int fd, void *data) return nvme_identify(fd, 0, NVME_ID_CNS_UUID_LIST, data); } +int nvme_identify_ctrl_nvm(int fd, void *data) +{ + return nvme_identify13(fd, 0, NVME_ID_CNS_CSI_ID_CTRL, 0, data); +} + +int nvme_zns_identify_ns(int fd, __u32 nsid, void *data) +{ + return nvme_identify13(fd, nsid, NVME_ID_CNS_CSI_ID_NS, 2 << 24, data); +} + +int nvme_zns_identify_ctrl(int fd, void *data) +{ + return nvme_identify13(fd, 0, NVME_ID_CNS_CSI_ID_CTRL, 2 << 24, data); +} + +int nvme_identify_iocs(int fd, __u16 cntid, void *data) +{ + return nvme_identify(fd, 0, (cntid << 16) | NVME_ID_CNS_CSI, data); +} + int nvme_get_log14(int fd, __u32 nsid, __u8 log_id, __u8 lsp, __u64 lpo, __u16 lsi, bool rae, __u8 uuid_ix, __u32 data_len, void *data) { @@ -436,7 +495,7 @@ int nvme_get_log13(int fd, __u32 nsid, __u8 log_id, __u8 lsp, } int nvme_get_log(int fd, __u32 nsid, __u8 log_id, bool rae, - __u32 data_len, void *data) + __u8 lsp, __u32 data_len, void *data) { __u32 offset = 0, xfer_len = data_len; void *ptr = data; @@ -452,7 +511,7 @@ int nvme_get_log(int fd, __u32 nsid, __u8 log_id, bool rae, if (xfer_len > 4096) xfer_len = 4096; - ret = nvme_get_log13(fd, nsid, log_id, NVME_NO_LOG_LSP, + ret = nvme_get_log13(fd, nsid, log_id, lsp, offset, 0, rae, xfer_len, ptr); if (ret) return ret; @@ -484,20 +543,20 @@ int nvme_get_telemetry_log(int fd, void *lp, int generate_report, int nvme_fw_log(int fd, struct nvme_firmware_log_page *fw_log) { return nvme_get_log(fd, NVME_NSID_ALL, NVME_LOG_FW_SLOT, true, - sizeof(*fw_log), fw_log); + NVME_NO_LOG_LSP, sizeof(*fw_log), fw_log); } int nvme_changed_ns_list_log(int fd, struct nvme_changed_ns_list_log *changed_ns_list_log) { return nvme_get_log(fd, 0, NVME_LOG_CHANGED_NS, true, - sizeof(changed_ns_list_log->log), + NVME_NO_LOG_LSP, sizeof(changed_ns_list_log->log), changed_ns_list_log->log); } int nvme_error_log(int fd, int entries, struct nvme_error_log_page *err_log) { return nvme_get_log(fd, NVME_NSID_ALL, NVME_LOG_ERROR, false, - entries * sizeof(*err_log), err_log); + NVME_NO_LOG_LSP, entries * sizeof(*err_log), err_log); } int nvme_endurance_log(int fd, __u16 group_id, struct nvme_endurance_group_log *endurance_log) @@ -509,7 +568,7 @@ int nvme_endurance_log(int fd, __u16 group_id, struct nvme_endurance_group_log * int nvme_smart_log(int fd, __u32 nsid, struct nvme_smart_log *smart_log) { return nvme_get_log(fd, nsid, NVME_LOG_SMART, false, - sizeof(*smart_log), smart_log); + NVME_NO_LOG_LSP, sizeof(*smart_log), smart_log); } int nvme_ana_log(int fd, void *ana_log, size_t ana_log_len, int rgo) @@ -518,27 +577,72 @@ int nvme_ana_log(int fd, void *ana_log, size_t ana_log_len, int rgo) true, ana_log_len, ana_log); } -int nvme_self_test_log(int fd, __u32 nsid, struct nvme_self_test_log *self_test_log) +int nvme_self_test_log(int fd, __u32 size, struct nvme_self_test_log *self_test_log) { - return nvme_get_log(fd, nsid, NVME_LOG_DEVICE_SELF_TEST, false, - sizeof(*self_test_log), self_test_log); + return nvme_get_log(fd, NVME_NSID_ALL, NVME_LOG_DEVICE_SELF_TEST, false, + NVME_NO_LOG_LSP, size, self_test_log); } int nvme_effects_log(int fd, struct nvme_effects_log_page *effects_log) { return nvme_get_log(fd, NVME_NSID_ALL, NVME_LOG_CMD_EFFECTS, false, - sizeof(*effects_log), effects_log); + NVME_NO_LOG_LSP, sizeof(*effects_log), effects_log); } int nvme_discovery_log(int fd, struct nvmf_disc_rsp_page_hdr *log, __u32 size) { - return nvme_get_log(fd, 0, NVME_LOG_DISC, false, size, log); + return nvme_get_log(fd, 0, NVME_LOG_DISC, false, NVME_NO_LOG_LSP, size, log); } -int nvme_sanitize_log(int fd, struct nvme_sanitize_log_page *sanitize_log) +int nvme_sanitize_log(int fd, bool rae, struct nvme_sanitize_log_page *sanitize_log) { - return nvme_get_log(fd, 0, NVME_LOG_SANITIZE, false, - sizeof(*sanitize_log), sanitize_log); + return nvme_get_log(fd, 0, NVME_LOG_SANITIZE, rae, + NVME_NO_LOG_LSP, sizeof(*sanitize_log), sanitize_log); +} + +int nvme_predictable_latency_per_nvmset_log(int fd, + __u16 nvmset_id, + struct nvme_predlat_per_nvmset_log_page *plpns_log) +{ + return nvme_get_log13(fd, NVME_NSID_ALL, + NVME_LOG_PRELAT_PER_NVMSET, 0, 0, nvmset_id, + false, sizeof(*plpns_log), plpns_log); +} + +int nvme_predictable_latency_event_agg_log(int fd, + void *pea_log, bool rae, __u32 size) +{ + return nvme_get_log(fd, NVME_NSID_ALL, + NVME_LOG_PRELAT_EVENT_AGG, rae, NVME_NO_LOG_LSP, + size, pea_log); +} + +int nvme_persistent_event_log(int fd, __u8 action, __u32 size, + void *pevent_log_info) +{ + return nvme_get_log(fd, NVME_NSID_ALL, NVME_LOG_PERSISTENT_EVENT, + false, action, size, pevent_log_info); +} + +int nvme_endurance_group_event_agg_log(int fd, + void *endurance_log, bool rae, __u32 size) +{ + return nvme_get_log(fd, NVME_NSID_ALL, + NVME_LOG_ENDURANCE_GROUP_EVENT_AGG, rae, NVME_NO_LOG_LSP, + size, endurance_log); +} + +int nvme_lba_status_log(int fd, void *lba_status, bool rae, + __u32 size) +{ + return nvme_get_log(fd, NVME_NSID_ALL, NVME_LOG_LBA_STATUS, + rae, NVME_NO_LOG_LSP, size, lba_status); +} + +int nvme_resv_notif_log(int fd, struct nvme_resv_notif_log *resv) +{ + return nvme_get_log(fd, 0, NVME_LOG_RESERVATION, false, + NVME_NO_LOG_LSP, sizeof(*resv), resv); } int nvme_feature(int fd, __u8 opcode, __u32 nsid, __u32 cdw10, __u32 cdw11, @@ -659,8 +763,8 @@ int nvme_format(int fd, __u32 nsid, __u8 lbaf, __u8 ses, __u8 pi, } int nvme_ns_create(int fd, __u64 nsze, __u64 ncap, __u8 flbas, __u8 dps, - __u8 nmic, __u32 anagrpid, __u16 nvmsetid, __u32 timeout, - __u32 *result) + __u8 nmic, __u32 anagrpid, __u16 nvmsetid, __u8 csi, + __u32 timeout, __u32 *result) { struct nvme_id_ns ns = { .nsze = cpu_to_le64(nsze), @@ -676,6 +780,7 @@ int nvme_ns_create(int fd, __u64 nsze, __u64 ncap, __u8 flbas, __u8 dps, .opcode = nvme_admin_ns_mgmt, .addr = (__u64)(uintptr_t) ((void *)&ns), .cdw10 = 0, + .cdw11 = csi << 24, .data_len = 0x1000, .timeout_ms = timeout, }; @@ -745,7 +850,7 @@ int nvme_fw_commit(int fd, __u8 slot, __u8 action, __u8 bpid) } int nvme_sec_send(int fd, __u32 nsid, __u8 nssf, __u16 spsp, - __u8 secp, __u32 tl, __u32 data_len, void *data, __u32 *result) + __u8 secp, __u32 tl, __u32 data_len, void *data) { struct nvme_admin_cmd cmd = { .opcode = nvme_admin_security_send, @@ -755,16 +860,12 @@ int nvme_sec_send(int fd, __u32 nsid, __u8 nssf, __u16 spsp, .cdw10 = secp << 24 | spsp << 8 | nssf, .cdw11 = tl, }; - int err; - err = nvme_submit_admin_passthru(fd, &cmd); - if (!err && result) - *result = cmd.result; - return err; + return nvme_submit_admin_passthru(fd, &cmd); } int nvme_sec_recv(int fd, __u32 nsid, __u8 nssf, __u16 spsp, - __u8 secp, __u32 al, __u32 data_len, void *data, __u32 *result) + __u8 secp, __u32 al, __u32 data_len, void *data) { struct nvme_admin_cmd cmd = { .opcode = nvme_admin_security_recv, @@ -774,24 +875,23 @@ int nvme_sec_recv(int fd, __u32 nsid, __u8 nssf, __u16 spsp, .addr = (__u64)(uintptr_t) data, .data_len = data_len, }; - int err; - err = nvme_submit_admin_passthru(fd, &cmd); - if (!err && result) - *result = cmd.result; - return err; + return nvme_submit_admin_passthru(fd, &cmd); } -int nvme_get_lba_status(int fd, __u64 slba, __u32 mndw, __u8 atype, __u16 rl, - void *data) +int nvme_get_lba_status(int fd, __u32 namespace_id, __u64 slba, __u32 mndw, + __u8 atype, __u16 rl, void *data, __u32 timeout_ms) { struct nvme_admin_cmd cmd = { .opcode = nvme_admin_get_lba_status, + .nsid = namespace_id, .addr = (__u64)(uintptr_t) data, + .data_len = (mndw + 1) * 4, .cdw10 = slba & 0xffffffff, .cdw11 = slba >> 32, .cdw12 = mndw, .cdw13 = (atype << 24) | rl, + .timeout_ms = timeout_ms, }; return nvme_submit_admin_passthru(fd, &cmd); @@ -851,12 +951,12 @@ int nvme_sanitize(int fd, __u8 sanact, __u8 ause, __u8 owpass, __u8 oipbp, return nvme_submit_admin_passthru(fd, &cmd); } -int nvme_self_test_start(int fd, __u32 nsid, __u32 cdw10) +int nvme_self_test_start(int fd, __u32 nsid, __u8 stc) { struct nvme_admin_cmd cmd = { .opcode = nvme_admin_dev_self_test, .nsid = nsid, - .cdw10 = cdw10, + .cdw10 = stc, }; return nvme_submit_admin_passthru(fd, &cmd); @@ -877,3 +977,95 @@ int nvme_virtual_mgmt(int fd, __u32 cdw10, __u32 cdw11, __u32 *result) return err; } + +int nvme_zns_mgmt_send(int fd, __u32 nsid, __u64 slba, bool select_all, + enum nvme_zns_send_action zsa, __u32 data_len, + void *data) +{ + __u32 cdw10 = slba & 0xffffffff; + __u32 cdw11 = slba >> 32; + __u32 cdw13 = zsa | (!!select_all) << 8; + + struct nvme_passthru_cmd cmd = { + .opcode = nvme_zns_cmd_mgmt_send, + .nsid = nsid, + .cdw10 = cdw10, + .cdw11 = cdw11, + .cdw13 = cdw13, + .addr = (__u64)(uintptr_t)data, + .data_len = data_len, + }; + + return nvme_submit_io_passthru(fd, &cmd); +} + +int nvme_zns_mgmt_recv(int fd, __u32 nsid, __u64 slba, + enum nvme_zns_recv_action zra, __u8 zrasf, + bool zras_feat, __u32 data_len, void *data) +{ + __u32 cdw10 = slba & 0xffffffff; + __u32 cdw11 = slba >> 32; + __u32 cdw12 = (data_len >> 2) - 1; + __u32 cdw13 = zra | zrasf << 8 | zras_feat << 16; + + struct nvme_passthru_cmd cmd = { + .opcode = nvme_zns_cmd_mgmt_recv, + .nsid = nsid, + .cdw10 = cdw10, + .cdw11 = cdw11, + .cdw12 = cdw12, + .cdw13 = cdw13, + .addr = (__u64)(uintptr_t)data, + .data_len = data_len, + }; + + return nvme_submit_io_passthru(fd, &cmd); +} + +int nvme_zns_report_zones(int fd, __u32 nsid, __u64 slba, bool extended, + enum nvme_zns_report_options opts, bool partial, + __u32 data_len, void *data) +{ + enum nvme_zns_recv_action zra; + + if (extended) + zra = NVME_ZNS_ZRA_EXTENDED_REPORT_ZONES; + else + zra = NVME_ZNS_ZRA_REPORT_ZONES; + + return nvme_zns_mgmt_recv(fd, nsid, slba, zra, opts, partial, + data_len, data); +} + +int nvme_zns_append(int fd, __u32 nsid, __u64 zslba, __u16 nlb, __u16 control, + __u32 ilbrt, __u16 lbat, __u16 lbatm, __u32 data_len, + void *data, __u32 metadata_len, void *metadata, + __u64 *result) +{ + __u32 cdw10 = zslba & 0xffffffff; + __u32 cdw11 = zslba >> 32; + __u32 cdw12 = nlb | (control << 16); + __u32 cdw14 = ilbrt; + __u32 cdw15 = lbat | (lbatm << 16); + + struct nvme_passthru_cmd64 cmd = { + .opcode = nvme_zns_cmd_append, + .nsid = nsid, + .cdw10 = cdw10, + .cdw11 = cdw11, + .cdw12 = cdw12, + .cdw14 = cdw14, + .cdw15 = cdw15, + .metadata = (__u64)(uintptr_t)metadata, + .addr = (__u64)(uintptr_t)data, + .metadata_len = metadata_len, + .data_len = data_len, + }; + + int err; + + err = ioctl(fd, NVME_IOCTL_IO64_CMD, &cmd); + if (!err && result) + *result = cmd.result; + return err; +} diff --git a/nvme-ioctl.h b/nvme-ioctl.h index aae4e49..b0184c7 100644 --- a/nvme-ioctl.h +++ b/nvme-ioctl.h @@ -29,18 +29,6 @@ int nvme_io(int fd, __u8 opcode, __u64 slba, __u16 nblocks, __u16 control, __u32 dsmgmt, __u32 reftag, __u16 apptag, __u16 appmask, void *data, void *metadata); -int nvme_read(int fd, __u64 slba, __u16 nblocks, __u16 control, - __u32 dsmgmt, __u32 reftag, __u16 apptag, - __u16 appmask, void *data, void *metadata); - -int nvme_write(int fd, __u64 slba, __u16 nblocks, __u16 control, - __u32 dsmgmt, __u32 reftag, __u16 apptag, - __u16 appmask, void *data, void *metadata); - -int nvme_compare(int fd, __u64 slba, __u16 nblocks, __u16 control, - __u32 dsmgmt, __u32 reftag, __u16 apptag, - __u16 appmask, void *data, void *metadata); - /* NVME_IO_CMD */ int nvme_passthru_io(int fd, __u8 opcode, __u8 flags, __u16 rsvd, __u32 nsid, __u32 cdw2, __u32 cdw3, @@ -61,10 +49,17 @@ int nvme_flush(int fd, __u32 nsid); int nvme_dsm(int fd, __u32 nsid, __u32 cdw11, struct nvme_dsm_range *dsm, __u16 nr_ranges); -struct nvme_dsm_range *nvme_setup_dsm_range(__u32 *ctx_attrs, - __u32 *llbas, __u64 *slbas, +struct nvme_dsm_range *nvme_setup_dsm_range(int *ctx_attrs, int *llbas, + unsigned long long *slbas, __u16 nr_ranges); +int nvme_copy(int fd, __u32 nsid, struct nvme_copy_range *copy, __u64 sdlba, + __u16 nr, __u8 prinfor, __u8 prinfow, __u8 dtype, __u16 dspec, + __u8 format, int lr, int fua, __u32 ilbrt, __u16 lbatm, + __u16 lbat); +struct nvme_copy_range *nvme_setup_copy_range(int *nlbs, unsigned long long *slbas, + int *eilbrts, int *elbatms, int *elbats, __u16 nr); + int nvme_resv_acquire(int fd, __u32 nsid, __u8 rtype, __u8 racqa, bool iekey, __u64 crkey, __u64 nrkey); int nvme_resv_register(int fd, __u32 nsid, __u8 rrega, __u8 cptpl, @@ -79,14 +74,20 @@ int nvme_identify(int fd, __u32 nsid, __u32 cdw10, void *data); int nvme_identify_ctrl(int fd, void *data); int nvme_identify_ns(int fd, __u32 nsid, bool present, void *data); int nvme_identify_ns_list(int fd, __u32 nsid, bool all, void *data); +int nvme_identify_ns_list_csi(int fd, __u32 nsid, __u8 csi, bool all, void *data); int nvme_identify_ctrl_list(int fd, __u32 nsid, __u16 cntid, void *data); int nvme_identify_ns_descs(int fd, __u32 nsid, void *data); int nvme_identify_nvmset(int fd, __u16 nvmset_id, void *data); int nvme_identify_uuid(int fd, void *data); +int nvme_identify_primary_ctrl_caps(int fd, void *data); int nvme_identify_secondary_ctrl_list(int fd, __u32 nsid, __u16 cntid, void *data); int nvme_identify_ns_granularity(int fd, void *data); +int nvme_identify_ctrl_nvm(int fd, void *data); +int nvme_zns_identify_ctrl(int fd, void *data); +int nvme_zns_identify_ns(int fd, __u32 nsid, void *data); +int nvme_identify_iocs(int fd, __u16 cntid, void *data); int nvme_get_log(int fd, __u32 nsid, __u8 log_id, bool rae, - __u32 data_len, void *data); + __u8 lsp, __u32 data_len, void *data); int nvme_get_log14(int fd, __u32 nsid, __u8 log_id, __u8 lsp, __u64 lpo, __u16 group_id, bool rae, __u8 uuid_ix, __u32 data_len, void *data); @@ -103,10 +104,20 @@ int nvme_smart_log(int fd, __u32 nsid, struct nvme_smart_log *smart_log); int nvme_ana_log(int fd, void *ana_log, size_t ana_log_len, int rgo); int nvme_effects_log(int fd, struct nvme_effects_log_page *effects_log); int nvme_discovery_log(int fd, struct nvmf_disc_rsp_page_hdr *log, __u32 size); -int nvme_sanitize_log(int fd, struct nvme_sanitize_log_page *sanitize_log); +int nvme_sanitize_log(int fd, bool rae, struct nvme_sanitize_log_page *sanitize_log); +int nvme_predictable_latency_per_nvmset_log(int fd, + __u16 nvmset_id, struct nvme_predlat_per_nvmset_log_page *plpns_log); +int nvme_predictable_latency_event_agg_log(int fd, void *pea_log, + bool rae, __u32 size); +int nvme_persistent_event_log(int fd, __u8 action, __u32 size, + void *pevent_log_info); +int nvme_endurance_group_event_agg_log(int fd, void *endurance_log, + bool rae, __u32 size); int nvme_endurance_log(int fd, __u16 group_id, struct nvme_endurance_group_log *endurance_log); - +int nvme_lba_status_log(int fd, void *lba_status, bool rae, + __u32 size); +int nvme_resv_notif_log(int fd, struct nvme_resv_notif_log *resv); int nvme_feature(int fd, __u8 opcode, __u32 nsid, __u32 cdw10, __u32 cdw11, __u32 cdw12, __u32 data_len, void *data, __u32 *result); @@ -119,8 +130,8 @@ int nvme_format(int fd, __u32 nsid, __u8 lbaf, __u8 ses, __u8 pi, __u8 pil, __u8 ms, __u32 timeout); int nvme_ns_create(int fd, __u64 nsze, __u64 ncap, __u8 flbas, __u8 dps, - __u8 nmic, __u32 anagrpid, __u16 nvmsetid, __u32 timeout, - __u32 *result); + __u8 nmic, __u32 anagrpid, __u16 nvmsetid, __u8 csi, + __u32 timeout, __u32 *result); int nvme_ns_delete(int fd, __u32 nsid, __u32 timeout); int nvme_ns_attachment(int fd, __u32 nsid, __u16 num_ctrls, @@ -130,16 +141,16 @@ int nvme_fw_download(int fd, __u32 offset, __u32 data_len, void *data); int nvme_fw_commit(int fd, __u8 slot, __u8 action, __u8 bpid); int nvme_sec_send(int fd, __u32 nsid, __u8 nssf, __u16 spsp, - __u8 secp, __u32 tl, __u32 data_len, void *data, __u32 *result); + __u8 secp, __u32 tl, __u32 data_len, void *data); int nvme_sec_recv(int fd, __u32 nsid, __u8 nssf, __u16 spsp, - __u8 secp, __u32 al, __u32 data_len, void *data, __u32 *result); + __u8 secp, __u32 al, __u32 data_len, void *data); int nvme_subsystem_reset(int fd); int nvme_reset_controller(int fd); int nvme_ns_rescan(int fd); -int nvme_get_lba_status(int fd, __u64 slba, __u32 mndw, __u8 atype, __u16 rl, - void *data); +int nvme_get_lba_status(int fd, __u32 namespace_id, __u64 slba, __u32 mndw, + __u8 atype, __u16 rl, void *data, __u32 timeout_ms); int nvme_dir_send(int fd, __u32 nsid, __u16 dspec, __u8 dtype, __u8 doper, __u32 data_len, __u32 dw12, void *data, __u32 *result); int nvme_dir_recv(int fd, __u32 nsid, __u16 dspec, __u8 dtype, __u8 doper, @@ -149,7 +160,22 @@ int nvme_set_property(int fd, int offset, uint64_t value); int nvme_get_property(int fd, int offset, uint64_t *value); int nvme_sanitize(int fd, __u8 sanact, __u8 ause, __u8 owpass, __u8 oipbp, __u8 no_dealloc, __u32 ovrpat); -int nvme_self_test_start(int fd, __u32 nsid, __u32 cdw10); +int nvme_self_test_start(int fd, __u32 nsid, __u8 stc); int nvme_self_test_log(int fd, __u32 nsid, struct nvme_self_test_log *self_test_log); int nvme_virtual_mgmt(int fd, __u32 cdw10, __u32 cdw11, __u32 *result); + +int nvme_zns_mgmt_send(int fd, __u32 nsid, __u64 slba, bool select_all, + enum nvme_zns_send_action zsa, __u32 data_len, + void *data); +int nvme_zns_mgmt_recv(int fd, __u32 nsid, __u64 slba, + enum nvme_zns_recv_action zra, __u8 zrasf, + bool zras_feat, __u32 data_len, void *data); +int nvme_zns_report_zones(int fd, __u32 nsid, __u64 slba, bool extended, + enum nvme_zns_report_options opts, bool partial, + __u32 data_len, void *data); +int nvme_zns_append(int fd, __u32 nsid, __u64 zslba, __u16 nlb, __u16 control, + __u32 ilbrt, __u16 lbat, __u16 lbatm, __u32 data_len, + void *data, __u32 metadata_len, void *metadata, + __u64 *result); + #endif /* _NVME_LIB_H */ diff --git a/nvme-print.c b/nvme-print.c old mode 100644 new mode 100755 index 30fca29..b01a842 --- a/nvme-print.c +++ b/nvme-print.c @@ -3,9 +3,10 @@ #include #include #include +#include +#include "nvme.h" #include "nvme-print.h" -#include "util/json.h" #include "nvme-models.h" #include "util/suffix.h" #include "common.h" @@ -43,7 +44,7 @@ static const char *nvme_ana_state_to_string(enum nvme_ana_state state) return "invalid state"; } -static const char *nvme_cmd_to_string(int admin, __u8 opcode) +const char *nvme_cmd_to_string(int admin, __u8 opcode) { if (admin) { switch (opcode) { @@ -73,6 +74,7 @@ static const char *nvme_cmd_to_string(int admin, __u8 opcode) case nvme_admin_security_send: return "Security Send"; case nvme_admin_security_recv: return "Security Receive"; case nvme_admin_sanitize_nvm: return "Sanitize"; + case nvme_admin_get_lba_status: return "Get LBA Status"; } } else { switch (opcode) { @@ -87,6 +89,8 @@ static const char *nvme_cmd_to_string(int admin, __u8 opcode) case nvme_cmd_resv_report: return "Reservation Report"; case nvme_cmd_resv_acquire: return "Reservation Acquire"; case nvme_cmd_resv_release: return "Reservation Release"; + case nvme_cmd_verify: return "Verify"; + case nvme_cmd_copy: return "Copy"; } } @@ -107,38 +111,29 @@ static const char *fw_to_string(__u64 fw) static const char *get_sanitize_log_sstat_status_str(__u16 status) { - const char *str; - switch (status & NVME_SANITIZE_LOG_STATUS_MASK) { case NVME_SANITIZE_LOG_NEVER_SANITIZED: - str = "NVM Subsystem has never been sanitized."; - break; + return "NVM Subsystem has never been sanitized."; case NVME_SANITIZE_LOG_COMPLETED_SUCCESS: - str = "Most Recent Sanitize Command Completed Successfully."; - break; + return "Most Recent Sanitize Command Completed Successfully."; case NVME_SANITIZE_LOG_IN_PROGESS: - str = "Sanitize in Progress."; - break; + return "Sanitize in Progress."; case NVME_SANITIZE_LOG_COMPLETED_FAILED: - str = "Most Recent Sanitize Command Failed."; - break; + return "Most Recent Sanitize Command Failed."; case NVME_SANITIZE_LOG_ND_COMPLETED_SUCCESS: - str = "Most Recent Sanitize Command (No-Deallocate After Sanitize) Completed Successfully."; - break; + return "Most Recent Sanitize Command (No-Deallocate After Sanitize) Completed Successfully."; default: - str = "Unknown."; + return "Unknown"; } - - return str; } -static void json_nvme_id_ns(struct nvme_id_ns *ns, unsigned int mode) +static void json_nvme_id_ns(struct nvme_id_ns *ns) { char nguid_buf[2 * sizeof(ns->nguid) + 1], eui64_buf[2 * sizeof(ns->eui64) + 1]; char *nguid = nguid_buf, *eui64 = eui64_buf; struct json_object *root; - struct json_array *lbafs; + struct json_object *lbafs; int i; long double nvmcap = int128_to_double(ns->nvmcap); @@ -176,6 +171,10 @@ static void json_nvme_id_ns(struct nvme_id_ns *ns, unsigned int mode) json_object_add_value_int(root, "nows", le16_to_cpu(ns->nows)); } + json_object_add_value_int(root, "mssrl", le16_to_cpu(ns->mssrl)); + json_object_add_value_int(root, "mcl", le32_to_cpu(ns->mcl)); + json_object_add_value_int(root, "msrc", ns->msrc); + json_object_add_value_int(root, "anagrpid", le32_to_cpu(ns->anagrpid)); json_object_add_value_int(root, "endgid", le16_to_cpu(ns->endgid)); @@ -209,11 +208,11 @@ static void json_nvme_id_ns(struct nvme_id_ns *ns, unsigned int mode) json_free_object(root); } -static void json_nvme_id_ctrl(struct nvme_id_ctrl *ctrl, unsigned int mode, +static void json_nvme_id_ctrl(struct nvme_id_ctrl *ctrl, void (*vs)(__u8 *vs, struct json_object *root)) { struct json_object *root; - struct json_array *psds; + struct json_object *psds; long double tnvmcap = int128_to_double(ctrl->tnvmcap); long double unvmcap = int128_to_double(ctrl->unvmcap); @@ -296,7 +295,7 @@ static void json_nvme_id_ctrl(struct nvme_id_ctrl *ctrl, unsigned int mode, json_object_add_value_int(root, "vwc", ctrl->vwc); json_object_add_value_int(root, "awun", le16_to_cpu(ctrl->awun)); json_object_add_value_int(root, "awupf", le16_to_cpu(ctrl->awupf)); - json_object_add_value_int(root, "nvscc", ctrl->nvscc); + json_object_add_value_int(root, "icsvscc", ctrl->icsvscc); json_object_add_value_int(root, "nwpc", ctrl->nwpc); json_object_add_value_int(root, "acwu", le16_to_cpu(ctrl->acwu)); json_object_add_value_int(root, "sgls", le32_to_cpu(ctrl->sgls)); @@ -307,8 +306,9 @@ static void json_nvme_id_ctrl(struct nvme_id_ctrl *ctrl, unsigned int mode, json_object_add_value_int(root, "ioccsz", le32_to_cpu(ctrl->ioccsz)); json_object_add_value_int(root, "iorcsz", le32_to_cpu(ctrl->iorcsz)); json_object_add_value_int(root, "icdoff", le16_to_cpu(ctrl->icdoff)); - json_object_add_value_int(root, "ctrattr", ctrl->ctrattr); + json_object_add_value_int(root, "fcatt", ctrl->fcatt); json_object_add_value_int(root, "msdbd", ctrl->msdbd); + json_object_add_value_int(root, "ofcs", le16_to_cpu(ctrl->ofcs)); psds = json_create_array(); json_object_add_value_array(root, "psds", psds); @@ -353,7 +353,7 @@ static void json_nvme_id_ctrl(struct nvme_id_ctrl *ctrl, unsigned int mode, static void json_error_log(struct nvme_error_log_page *err_log, int entries) { struct json_object *root; - struct json_array *errors; + struct json_object *errors; int i; root = json_create_object(); @@ -370,7 +370,9 @@ static void json_error_log(struct nvme_error_log_page *err_log, int entries) json_object_add_value_int(error, "cmdid", le16_to_cpu(err_log[i].cmdid)); json_object_add_value_int(error, "status_field", - le16_to_cpu(err_log[i].status_field)); + le16_to_cpu(err_log[i].status_field >> 0x1)); + json_object_add_value_int(error, "phase_tag", + le16_to_cpu(err_log[i].status_field & 0x1)); json_object_add_value_int(error, "parm_error_location", le16_to_cpu(err_log[i].parm_error_location)); json_object_add_value_uint(error, "lba", @@ -396,7 +398,7 @@ static void json_nvme_resv_report(struct nvme_reservation_status *status, int bytes, __u32 cdw11) { struct json_object *root; - struct json_array *rcs; + struct json_object *rcs; int i, j, regctl, entries; regctl = status->regctl[0] | (status->regctl[1] << 8); @@ -678,8 +680,8 @@ static void json_ana_log(struct nvme_ana_rsp_hdr *ana_log, const char *devname) int offset = sizeof(struct nvme_ana_rsp_hdr); struct nvme_ana_rsp_hdr *hdr = ana_log; struct nvme_ana_group_desc *ana_desc; - struct json_array *desc_list; - struct json_array *ns_list; + struct json_object *desc_list; + struct json_object *ns_list; struct json_object *desc; struct json_object *nsid; struct json_object *root; @@ -690,7 +692,7 @@ static void json_ana_log(struct nvme_ana_rsp_hdr *ana_log, const char *devname) root = json_create_object(); json_object_add_value_string(root, - "Asynchronous Namespace Access Log for NVMe device", + "Asymmetric Namespace Access Log for NVMe device", devname); json_object_add_value_uint(root, "chgcnt", le64_to_cpu(hdr->chgcnt)); @@ -731,12 +733,13 @@ static void json_ana_log(struct nvme_ana_rsp_hdr *ana_log, const char *devname) json_free_object(root); } -static void json_self_test_log(struct nvme_self_test_log *self_test) +static void json_self_test_log(struct nvme_self_test_log *self_test, __u8 dst_entries) { struct json_object *valid_attrs; struct json_object *root; - struct json_array *valid; + struct json_object *valid; int i; + __u32 num_entries; root = json_create_object(); json_object_add_value_int(root, "Current Device Self-Test Operation", @@ -745,7 +748,8 @@ static void json_self_test_log(struct nvme_self_test_log *self_test) self_test->crnt_dev_selftest_compln); valid = json_create_array(); - for (i = 0; i < NVME_ST_REPORTS; i++) { + num_entries = min(dst_entries, NVME_ST_REPORTS); + for (i = 0; i < num_entries; i++) { valid_attrs = json_create_object(); json_object_add_value_int(valid_attrs, "Self test result", self_test->result[i].dsts & 0xf); @@ -786,26 +790,36 @@ add: static void json_effects_log(struct nvme_effects_log_page *effects_log) { struct json_object *root; + struct json_object *acs; + struct json_object *iocs; unsigned int opcode; char key[128]; __u32 effect; root = json_create_object(); - + acs = json_create_object(); for (opcode = 0; opcode < 256; opcode++) { - sprintf(key, "ACS%d (%s)", opcode, - nvme_cmd_to_string(1, opcode)); effect = le32_to_cpu(effects_log->acs[opcode]); - json_object_add_value_uint(root, key, effect); + if (effect & NVME_CMD_EFFECTS_CSUPP) { + sprintf(key, "ACS_%u (%s)", opcode, + nvme_cmd_to_string(1, opcode)); + json_object_add_value_uint(acs, key, effect); + } } + json_object_add_value_object(root, "admin_cmd_set", acs); + + iocs = json_create_object(); for (opcode = 0; opcode < 256; opcode++) { - sprintf(key, "IOCS%d (%s)", opcode, - nvme_cmd_to_string(0, opcode)); effect = le32_to_cpu(effects_log->iocs[opcode]); - json_object_add_value_uint(root, key, effect); + if (effect & NVME_CMD_EFFECTS_CSUPP) { + sprintf(key, "IOCS_%u (%s)", opcode, + nvme_cmd_to_string(0, opcode)); + json_object_add_value_uint(iocs, key, effect); + } } + json_object_add_value_object(root, "io_cmd_set", iocs); json_print_object(root, NULL); printf("\n"); json_free_object(root); @@ -860,6 +874,915 @@ static void json_sanitize_log(struct nvme_sanitize_log_page *sanitize_log, json_free_object(root); } +void json_predictable_latency_per_nvmset( + struct nvme_predlat_per_nvmset_log_page *plpns_log, + __u16 nvmset_id) +{ + struct json_object *root; + + root = json_create_object(); + json_object_add_value_uint(root, "nvmset_id", + le16_to_cpu(nvmset_id)); + json_object_add_value_uint(root, "status", + plpns_log->status); + json_object_add_value_uint(root, "event_type", + le16_to_cpu(plpns_log->event_type)); + json_object_add_value_uint(root, "dtwin_reads_typical", + le64_to_cpu(plpns_log->dtwin_rtyp)); + json_object_add_value_uint(root, "dtwin_writes_typical", + le64_to_cpu(plpns_log->dtwin_wtyp)); + json_object_add_value_uint(root, "dtwin_time_maximum", + le64_to_cpu(plpns_log->dtwin_timemax)); + json_object_add_value_uint(root, "ndwin_time_minimum_high", + le64_to_cpu(plpns_log->ndwin_timemin_high)); + json_object_add_value_uint(root, "ndwin_time_minimum_low", + le64_to_cpu(plpns_log->ndwin_timemin_low)); + json_object_add_value_uint(root, "dtwin_reads_estimate", + le64_to_cpu(plpns_log->dtwin_restimate)); + json_object_add_value_uint(root, "dtwin_writes_estimate", + le64_to_cpu(plpns_log->dtwin_westimate)); + json_object_add_value_uint(root, "dtwin_time_estimate", + le64_to_cpu(plpns_log->dtwin_testimate)); + + json_print_object(root, NULL); + printf("\n"); + json_free_object(root); +} + +void nvme_show_predictable_latency_per_nvmset( + struct nvme_predlat_per_nvmset_log_page *plpns_log, + __u16 nvmset_id, const char *devname, + enum nvme_print_flags flags) +{ + if (flags & BINARY) + return d_raw((unsigned char *)plpns_log, + sizeof(*plpns_log)); + if (flags & JSON) + return json_predictable_latency_per_nvmset(plpns_log, + nvmset_id); + + printf("Predictable Latency Per NVM Set Log for device: %s\n", + devname); + printf("Predictable Latency Per NVM Set Log for NVM Set ID: %u\n", + le16_to_cpu(nvmset_id)); + printf("Status: %u\n", plpns_log->status); + printf("Event Type: %u\n", + le16_to_cpu(plpns_log->event_type)); + printf("DTWIN Reads Typical: %"PRIu64"\n", + le64_to_cpu(plpns_log->dtwin_rtyp)); + printf("DTWIN Writes Typical: %"PRIu64"\n", + le64_to_cpu(plpns_log->dtwin_wtyp)); + printf("DTWIN Time Maximum: %"PRIu64"\n", + le64_to_cpu(plpns_log->dtwin_timemax)); + printf("NDWIN Time Minimum High: %"PRIu64" \n", + le64_to_cpu(plpns_log->ndwin_timemin_high)); + printf("NDWIN Time Minimum Low: %"PRIu64"\n", + le64_to_cpu(plpns_log->ndwin_timemin_low)); + printf("DTWIN Reads Estimate: %"PRIu64"\n", + le64_to_cpu(plpns_log->dtwin_restimate)); + printf("DTWIN Writes Estimate: %"PRIu64"\n", + le64_to_cpu(plpns_log->dtwin_westimate)); + printf("DTWIN Time Estimate: %"PRIu64"\n\n\n", + le64_to_cpu(plpns_log->dtwin_testimate)); +} + +void json_predictable_latency_event_agg_log( + struct nvme_event_agg_log_page *pea_log, + __u64 log_entries) +{ + struct json_object *root; + struct json_object *valid_attrs; + struct json_object *valid; + __u64 num_iter; + __u64 num_entries; + + root = json_create_object(); + num_entries = le64_to_cpu(pea_log->num_entries); + json_object_add_value_uint(root, "num_entries_avail", + num_entries); + valid = json_create_array(); + + num_iter = min(num_entries, log_entries); + for (int i = 0; i < num_iter; i++) { + valid_attrs = json_create_object(); + json_object_add_value_uint(valid_attrs, "entry", + le16_to_cpu(pea_log->entries[i])); + json_array_add_value_object(valid, valid_attrs); + } + json_object_add_value_array(root, "list_of_entries", valid); + json_print_object(root, NULL); + printf("\n"); + json_free_object(root); +} + +void nvme_show_predictable_latency_event_agg_log( + struct nvme_event_agg_log_page *pea_log, + __u64 log_entries, __u32 size, const char *devname, + enum nvme_print_flags flags) +{ + __u64 num_iter; + __u64 num_entries; + + if (flags & BINARY) + return d_raw((unsigned char *)pea_log, size); + if (flags & JSON) + return json_predictable_latency_event_agg_log(pea_log, + log_entries); + + num_entries = le64_to_cpu(pea_log->num_entries); + printf("Predictable Latency Event Aggregate Log for"\ + " device: %s\n", devname); + + printf("Number of Entries Available: %"PRIu64"\n", + (uint64_t)num_entries); + + num_iter = min(num_entries, log_entries); + for (int i = 0; i < num_iter; i++) { + printf("Entry[%d]: %u\n", i + 1, + le16_to_cpu(pea_log->entries[i])); + } +} + +static const char *nvme_show_nss_hw_error(__u16 error_code) +{ + switch (error_code) { + case 0x01: + return "PCIe Correctable Error"; + case 0x02: + return "PCIe Uncorrectable Non fatal Error"; + case 0x03: + return "PCIe Uncorrectable Fatal Error"; + case 0x04: + return "PCIe Link Status Change"; + case 0x05: + return "PCIe Link Not Active"; + case 0x06: + return "Critical Warning Condition"; + case 0x07: + return "Endurance Group Critical Warning Condition"; + case 0x08: + return "Unsafe Shutdown"; + case 0x09: + return "Controller Fatal Status"; + case 0xA: + return "Media and Data Integrity Status"; + default: + return "Reserved"; + } +} + +void json_persistent_event_log(void *pevent_log_info, __u32 size) +{ + struct json_object *root; + struct json_object *valid_attrs; + struct json_object *valid; + __u32 offset, por_info_len, por_info_list; + __u64 *fw_rev; + char key[128]; + struct nvme_smart_log *smart_event; + struct nvme_fw_commit_event *fw_commit_event; + struct nvme_time_stamp_change_event *ts_change_event; + struct nvme_power_on_reset_info_list *por_event; + struct nvme_nss_hw_err_event *nss_hw_err_event; + struct nvme_change_ns_event *ns_event; + struct nvme_format_nvm_start_event *format_start_event; + struct nvme_format_nvm_compln_event *format_cmpln_event; + struct nvme_sanitize_start_event *sanitize_start_event; + struct nvme_sanitize_compln_event *sanitize_cmpln_event; + struct nvme_thermal_exc_event *thermal_exc_event; + struct nvme_persistent_event_log_head *pevent_log_head; + struct nvme_persistent_event_entry_head *pevent_entry_head; + + root = json_create_object(); + valid = json_create_array(); + + offset = sizeof(*pevent_log_head); + if (size >= offset) { + pevent_log_head = pevent_log_info; + char sn[sizeof(pevent_log_head->sn) + 1], + mn[sizeof(pevent_log_head->mn) + 1], + subnqn[sizeof(pevent_log_head->subnqn) + 1]; + + snprintf(sn, sizeof(sn), "%-.*s", + (int)sizeof(pevent_log_head->sn), pevent_log_head->sn); + snprintf(mn, sizeof(mn), "%-.*s", + (int)sizeof(pevent_log_head->mn), pevent_log_head->mn); + snprintf(subnqn, sizeof(subnqn), "%-.*s", + (int)sizeof(pevent_log_head->subnqn), pevent_log_head->subnqn); + + json_object_add_value_uint(root, "log_id", + pevent_log_head->log_id); + json_object_add_value_uint(root, "total_num_of_events", + le32_to_cpu(pevent_log_head->tnev)); + json_object_add_value_uint(root, "total_log_len", + le64_to_cpu(pevent_log_head->tll)); + json_object_add_value_uint(root, "log_revision", + pevent_log_head->log_rev); + json_object_add_value_uint(root, "log_header_len", + le16_to_cpu(pevent_log_head->head_len)); + json_object_add_value_uint(root, "timestamp", + le64_to_cpu(pevent_log_head->timestamp)); + json_object_add_value_float(root, "power_on_hours", + int128_to_double(pevent_log_head->poh)); + json_object_add_value_uint(root, "power_cycle_count", + le64_to_cpu(pevent_log_head->pcc)); + json_object_add_value_uint(root, "pci_vid", + le16_to_cpu(pevent_log_head->vid)); + json_object_add_value_uint(root, "pci_ssvid", + le16_to_cpu(pevent_log_head->ssvid)); + json_object_add_value_string(root, "sn", sn); + json_object_add_value_string(root, "mn", mn); + json_object_add_value_string(root, "subnqn", subnqn); + for (int i = 0; i < 32; i++) { + if (pevent_log_head->supp_event_bm[i] == 0) + continue; + sprintf(key, "bitmap_%d", i); + json_object_add_value_uint(root, key, + pevent_log_head->supp_event_bm[i]); + } + } else { + printf("No log data can be shown with this log len at least " \ + "512 bytes is required or can be 0 to read the complete "\ + "log page after context established\n"); + return; + } + for (int i = 0; i < le32_to_cpu(pevent_log_head->tnev); i++) { + if (offset + sizeof(*pevent_entry_head) >= size) + break; + + pevent_entry_head = pevent_log_info + offset; + + if ((offset + pevent_entry_head->ehl + 3 + + le16_to_cpu(pevent_entry_head->el)) >= size) + break; + valid_attrs = json_create_object(); + + json_object_add_value_uint(valid_attrs, "event_type", + pevent_entry_head->etype); + json_object_add_value_uint(valid_attrs, "event_type_rev", + pevent_entry_head->etype_rev); + json_object_add_value_uint(valid_attrs, "event_header_len", + pevent_entry_head->ehl); + json_object_add_value_uint(valid_attrs, "ctrl_id", + le16_to_cpu(pevent_entry_head->ctrl_id)); + json_object_add_value_uint(valid_attrs, "event_time_stamp", + le64_to_cpu(pevent_entry_head->etimestamp)); + json_object_add_value_uint(valid_attrs, "vu_info_len", + le16_to_cpu(pevent_entry_head->vsil)); + json_object_add_value_uint(valid_attrs, "event_len", + le16_to_cpu(pevent_entry_head->el)); + + offset += pevent_entry_head->ehl + 3; + + switch (pevent_entry_head->etype) { + case NVME_SMART_HEALTH_EVENT: + smart_event = pevent_log_info + offset; + unsigned int temperature = ((smart_event->temperature[1] << 8) | + smart_event->temperature[0]); + + long double data_units_read = int128_to_double(smart_event->data_units_read); + long double data_units_written = int128_to_double(smart_event->data_units_written); + long double host_read_commands = int128_to_double(smart_event->host_reads); + long double host_write_commands = int128_to_double(smart_event->host_writes); + long double controller_busy_time = int128_to_double(smart_event->ctrl_busy_time); + long double power_cycles = int128_to_double(smart_event->power_cycles); + long double power_on_hours = int128_to_double(smart_event->power_on_hours); + long double unsafe_shutdowns = int128_to_double(smart_event->unsafe_shutdowns); + long double media_errors = int128_to_double(smart_event->media_errors); + long double num_err_log_entries = int128_to_double(smart_event->num_err_log_entries); + json_object_add_value_int(valid_attrs, "critical_warning", + smart_event->critical_warning); + + json_object_add_value_int(valid_attrs, "temperature", + temperature); + json_object_add_value_int(valid_attrs, "avail_spare", + smart_event->avail_spare); + json_object_add_value_int(valid_attrs, "spare_thresh", + smart_event->spare_thresh); + json_object_add_value_int(valid_attrs, "percent_used", + smart_event->percent_used); + json_object_add_value_int(valid_attrs, + "endurance_grp_critical_warning_summary", + smart_event->endu_grp_crit_warn_sumry); + json_object_add_value_float(valid_attrs, "data_units_read", + data_units_read); + json_object_add_value_float(valid_attrs, "data_units_written", + data_units_written); + json_object_add_value_float(valid_attrs, "host_read_commands", + host_read_commands); + json_object_add_value_float(valid_attrs, "host_write_commands", + host_write_commands); + json_object_add_value_float(valid_attrs, "controller_busy_time", + controller_busy_time); + json_object_add_value_float(valid_attrs, "power_cycles", + power_cycles); + json_object_add_value_float(valid_attrs, "power_on_hours", + power_on_hours); + json_object_add_value_float(valid_attrs, "unsafe_shutdowns", + unsafe_shutdowns); + json_object_add_value_float(valid_attrs, "media_errors", + media_errors); + json_object_add_value_float(valid_attrs, "num_err_log_entries", + num_err_log_entries); + json_object_add_value_uint(valid_attrs, "warning_temp_time", + le32_to_cpu(smart_event->warning_temp_time)); + json_object_add_value_uint(valid_attrs, "critical_comp_time", + le32_to_cpu(smart_event->critical_comp_time)); + + for (int c = 0; c < 8; c++) { + __s32 temp = le16_to_cpu(smart_event->temp_sensor[c]); + if (temp == 0) + continue; + sprintf(key, "temperature_sensor_%d",c + 1); + json_object_add_value_int(valid_attrs, key, temp); + } + + json_object_add_value_uint(valid_attrs, "thm_temp1_trans_count", + le32_to_cpu(smart_event->thm_temp1_trans_count)); + json_object_add_value_uint(valid_attrs, "thm_temp2_trans_count", + le32_to_cpu(smart_event->thm_temp2_trans_count)); + json_object_add_value_uint(valid_attrs, "thm_temp1_total_time", + le32_to_cpu(smart_event->thm_temp1_total_time)); + json_object_add_value_uint(valid_attrs, "thm_temp2_total_time", + le32_to_cpu(smart_event->thm_temp2_total_time)); + break; + case NVME_FW_COMMIT_EVENT: + fw_commit_event = pevent_log_info + offset; + json_object_add_value_uint(valid_attrs, "old_fw_rev", + le64_to_cpu(fw_commit_event->old_fw_rev)); + json_object_add_value_uint(valid_attrs, "new_fw_rev", + le64_to_cpu(fw_commit_event->new_fw_rev)); + json_object_add_value_uint(valid_attrs, "fw_commit_action", + fw_commit_event->fw_commit_action); + json_object_add_value_uint(valid_attrs, "fw_slot", + fw_commit_event->fw_slot); + json_object_add_value_uint(valid_attrs, "sct_fw", + fw_commit_event->sct_fw); + json_object_add_value_uint(valid_attrs, "sc_fw", + fw_commit_event->sc_fw); + json_object_add_value_uint(valid_attrs, + "vu_assign_fw_commit_rc", + le16_to_cpu(fw_commit_event->vndr_assign_fw_commit_rc)); + break; + case NVME_TIMESTAMP_EVENT: + ts_change_event = pevent_log_info + offset; + json_object_add_value_uint(valid_attrs, "prev_ts", + le64_to_cpu(ts_change_event->previous_timestamp)); + json_object_add_value_uint(valid_attrs, + "ml_secs_since_reset", + le64_to_cpu(ts_change_event->ml_secs_since_reset)); + break; + case NVME_POWER_ON_RESET_EVENT: + por_info_len = (le16_to_cpu(pevent_entry_head->el) - + le16_to_cpu(pevent_entry_head->vsil) - sizeof(*fw_rev)); + + por_info_list = por_info_len / sizeof(*por_event); + + fw_rev = pevent_log_info + offset; + json_object_add_value_uint(valid_attrs, "fw_rev", + le64_to_cpu(*fw_rev)); + for (int i = 0; i < por_info_list; i++) { + por_event = pevent_log_info + offset + + sizeof(*fw_rev) + i * sizeof(*por_event); + json_object_add_value_uint(valid_attrs, "ctrl_id", + le16_to_cpu(por_event->cid)); + json_object_add_value_uint(valid_attrs, "fw_act", + por_event->fw_act); + json_object_add_value_uint(valid_attrs, "op_in_prog", + por_event->op_in_prog); + json_object_add_value_uint(valid_attrs, "ctrl_power_cycle", + le32_to_cpu(por_event->ctrl_power_cycle)); + json_object_add_value_uint(valid_attrs, "power_on_ml_secs", + le64_to_cpu(por_event->power_on_ml_seconds)); + json_object_add_value_uint(valid_attrs, "ctrl_time_stamp", + le64_to_cpu(por_event->ctrl_time_stamp)); + } + break; + case NVME_NSS_HW_ERROR_EVENT: + nss_hw_err_event = pevent_log_info + offset; + json_object_add_value_uint(valid_attrs, "nss_hw_err_code", + le16_to_cpu(nss_hw_err_event->nss_hw_err_event_code)); + break; + case NVME_CHANGE_NS_EVENT: + ns_event = pevent_log_info + offset; + json_object_add_value_uint(valid_attrs, "nsmgt_cdw10", + le32_to_cpu(ns_event->nsmgt_cdw10)); + json_object_add_value_uint(valid_attrs, "nsze", + le64_to_cpu(ns_event->nsze)); + json_object_add_value_uint(valid_attrs, "nscap", + le64_to_cpu(ns_event->nscap)); + json_object_add_value_uint(valid_attrs, "flbas", + ns_event->flbas); + json_object_add_value_uint(valid_attrs, "dps", + ns_event->dps); + json_object_add_value_uint(valid_attrs, "nmic", + ns_event->nmic); + json_object_add_value_uint(valid_attrs, "ana_grp_id", + le32_to_cpu(ns_event->ana_grp_id)); + json_object_add_value_uint(valid_attrs, "nvmset_id", + le16_to_cpu(ns_event->nvmset_id)); + json_object_add_value_uint(valid_attrs, "nsid", + le32_to_cpu(ns_event->nsid)); + break; + case NVME_FORMAT_START_EVENT: + format_start_event = pevent_log_info + offset; + json_object_add_value_uint(valid_attrs, "nsid", + le32_to_cpu(format_start_event->nsid)); + json_object_add_value_uint(valid_attrs, "fna", + format_start_event->fna); + json_object_add_value_uint(valid_attrs, "format_nvm_cdw10", + le32_to_cpu(format_start_event->format_nvm_cdw10)); + break; + case NVME_FORMAT_COMPLETION_EVENT: + format_cmpln_event = pevent_log_info + offset; + json_object_add_value_uint(valid_attrs, "nsid", + le32_to_cpu(format_cmpln_event->nsid)); + json_object_add_value_uint(valid_attrs, "smallest_fpi", + format_cmpln_event->smallest_fpi); + json_object_add_value_uint(valid_attrs, "format_nvm_status", + format_cmpln_event->format_nvm_status); + json_object_add_value_uint(valid_attrs, "compln_info", + le16_to_cpu(format_cmpln_event->compln_info)); + json_object_add_value_uint(valid_attrs, "status_field", + le32_to_cpu(format_cmpln_event->status_field)); + break; + case NVME_SANITIZE_START_EVENT: + sanitize_start_event = pevent_log_info + offset; + json_object_add_value_uint(valid_attrs, "SANICAP", + le32_to_cpu(sanitize_start_event->sani_cap)); + json_object_add_value_uint(valid_attrs, "sani_cdw10", + le32_to_cpu(sanitize_start_event->sani_cdw10)); + json_object_add_value_uint(valid_attrs, "sani_cdw11", + le32_to_cpu(sanitize_start_event->sani_cdw11)); + break; + case NVME_SANITIZE_COMPLETION_EVENT: + sanitize_cmpln_event = pevent_log_info + offset; + json_object_add_value_uint(valid_attrs, "sani_prog", + le16_to_cpu(sanitize_cmpln_event->sani_prog)); + json_object_add_value_uint(valid_attrs, "sani_status", + le16_to_cpu(sanitize_cmpln_event->sani_status)); + json_object_add_value_uint(valid_attrs, "cmpln_info", + le16_to_cpu(sanitize_cmpln_event->cmpln_info)); + break; + case NVME_THERMAL_EXCURSION_EVENT: + thermal_exc_event = pevent_log_info + offset; + json_object_add_value_uint(valid_attrs, "over_temp", + thermal_exc_event->over_temp); + json_object_add_value_uint(valid_attrs, "threshold", + thermal_exc_event->threshold); + break; + } + + json_array_add_value_object(valid, valid_attrs); + offset += le16_to_cpu(pevent_entry_head->el); + } + + json_object_add_value_array(root, "list_of_event_entries", valid); + json_print_object(root, NULL); + printf("\n"); + json_free_object(root); +} + +void nvme_show_persistent_event_log(void *pevent_log_info, + __u8 action, __u32 size, const char *devname, + enum nvme_print_flags flags) +{ + __u32 offset, por_info_len, por_info_list; + __u64 *fw_rev; + struct nvme_smart_log *smart_event; + struct nvme_fw_commit_event *fw_commit_event; + struct nvme_time_stamp_change_event *ts_change_event; + struct nvme_power_on_reset_info_list *por_event; + struct nvme_nss_hw_err_event *nss_hw_err_event; + struct nvme_change_ns_event *ns_event; + struct nvme_format_nvm_start_event *format_start_event; + struct nvme_format_nvm_compln_event *format_cmpln_event; + struct nvme_sanitize_start_event *sanitize_start_event; + struct nvme_sanitize_compln_event *sanitize_cmpln_event; + struct nvme_thermal_exc_event *thermal_exc_event; + struct nvme_persistent_event_log_head *pevent_log_head; + struct nvme_persistent_event_entry_head *pevent_entry_head; + + if (flags & BINARY) + return d_raw((unsigned char *)pevent_log_info, size); + if (flags & JSON) + return json_persistent_event_log(pevent_log_info, size); + + offset = sizeof(*pevent_log_head); + + printf("Persistent Event Log for device: %s\n", devname); + printf("Action for Persistent Event Log: %u\n", action); + if (size >= offset) { + pevent_log_head = pevent_log_info; + printf("Log Identifier: %u\n", pevent_log_head->log_id); + printf("Total Number of Events: %u\n", + le32_to_cpu(pevent_log_head->tnev)); + printf("Total Log Length : %"PRIu64"\n", + le64_to_cpu(pevent_log_head->tll)); + printf("Log Revision: %u\n", pevent_log_head->log_rev); + printf("Log Header Length: %u\n", pevent_log_head->head_len); + printf("Timestamp: %"PRIu64"\n", + le64_to_cpu(pevent_log_head->timestamp)); + printf("Power On Hours (POH): %'.0Lf\n", + int128_to_double(pevent_log_head->poh)); + printf("Power Cycle Count: %"PRIu64"\n", + le64_to_cpu(pevent_log_head->pcc)); + printf("PCI Vendor ID (VID): %u\n", + le16_to_cpu(pevent_log_head->vid)); + printf("PCI Subsystem Vendor ID (SSVID): %u\n", + le16_to_cpu(pevent_log_head->ssvid)); + printf("Serial Number (SN): %-.*s\n", + (int)sizeof(pevent_log_head->sn), pevent_log_head->sn); + printf("Model Number (MN): %-.*s\n", + (int)sizeof(pevent_log_head->mn), pevent_log_head->mn); + printf("NVM Subsystem NVMe Qualified Name (SUBNQN): %-.*s\n", + (int)sizeof(pevent_log_head->subnqn), + pevent_log_head->subnqn); + printf("Supported Events Bitmap: "); + for (int i = 0; i < 32; i++) { + if (pevent_log_head->supp_event_bm[i] == 0) + continue; + printf("BitMap[%d] is 0x%x\n", i, + pevent_log_head->supp_event_bm[i]); + } + } else { + printf("No log data can be shown with this log len at least " \ + "512 bytes is required or can be 0 to read the complete "\ + "log page after context established\n"); + return; + } + printf("\n"); + printf("\nPersistent Event Entries:\n"); + for (int i = 0; i < le32_to_cpu(pevent_log_head->tnev); i++) { + if (offset + sizeof(*pevent_entry_head) >= size) + break; + + pevent_entry_head = pevent_log_info + offset; + + if ((offset + pevent_entry_head->ehl + 3 + + le16_to_cpu(pevent_entry_head->el)) >= size) + break; + + printf("Event Type: %u\n", pevent_entry_head->etype); + printf("Event Type Revision: %u\n", pevent_entry_head->etype_rev); + printf("Event Header Length: %u\n", pevent_entry_head->ehl); + printf("Controller Identifier: %u\n", + le16_to_cpu(pevent_entry_head->ctrl_id)); + printf("Event Timestamp: %"PRIu64"\n", + le64_to_cpu(pevent_entry_head->etimestamp)); + printf("Vendor Specific Information Length: %u\n", + le16_to_cpu(pevent_entry_head->vsil)); + printf("Event Length: %u\n", le16_to_cpu(pevent_entry_head->el)); + + offset += pevent_entry_head->ehl + 3; + + switch (pevent_entry_head->etype) { + case NVME_SMART_HEALTH_EVENT: + smart_event = pevent_log_info + offset; + printf("Smart Health Event: \n"); + nvme_show_smart_log(smart_event, NVME_NSID_ALL, devname, flags); + break; + case NVME_FW_COMMIT_EVENT: + fw_commit_event = pevent_log_info + offset; + printf("FW Commit Event: \n"); + printf("Old Firmware Revision: %"PRIu64"\n", + le64_to_cpu(fw_commit_event->old_fw_rev)); + printf("New Firmware Revision: %"PRIu64"\n", + le64_to_cpu(fw_commit_event->new_fw_rev)); + printf("FW Commit Action: %u\n", + fw_commit_event->fw_commit_action); + printf("FW Slot: %u\n", fw_commit_event->fw_slot); + printf("Status Code Type for Firmware Commit Command: %u\n", + fw_commit_event->sct_fw); + printf("Status Returned for Firmware Commit Command: %u\n", + fw_commit_event->sc_fw); + printf("Vendor Assigned Firmware Commit Result Code: %u\n", + le16_to_cpu(fw_commit_event->vndr_assign_fw_commit_rc)); + break; + case NVME_TIMESTAMP_EVENT: + ts_change_event = pevent_log_info + offset; + printf("Time Stamp Change Event: \n"); + printf("Previous Timestamp: %"PRIu64"\n", + le64_to_cpu(ts_change_event->previous_timestamp)); + printf("Milliseconds Since Reset: %"PRIu64"\n", + le64_to_cpu(ts_change_event->ml_secs_since_reset)); + break; + case NVME_POWER_ON_RESET_EVENT: + por_info_len = (le16_to_cpu(pevent_entry_head->el) - + le16_to_cpu(pevent_entry_head->vsil) - sizeof(*fw_rev)); + + por_info_list = por_info_len / sizeof(*por_event); + + printf("Power On Reset Event: \n"); + fw_rev = pevent_log_info + offset; + printf("Firmware Revision: %"PRIu64"\n", le64_to_cpu(*fw_rev)); + printf("Reset Information List: \n"); + + for (int i = 0; i < por_info_list; i++) { + por_event = pevent_log_info + offset + + sizeof(*fw_rev) + i * sizeof(*por_event); + printf("Controller ID: %u\n", le16_to_cpu(por_event->cid)); + printf("Firmware Activation: %u\n", + por_event->fw_act); + printf("Operation in Progress: %u\n", + por_event->op_in_prog); + printf("Controller Power Cycle: %u\n", + le32_to_cpu(por_event->ctrl_power_cycle)); + printf("Power on milliseconds: %"PRIu64"\n", + le64_to_cpu(por_event->power_on_ml_seconds)); + printf("Controller Timestamp: %"PRIu64"\n", + le64_to_cpu(por_event->ctrl_time_stamp)); + } + break; + case NVME_NSS_HW_ERROR_EVENT: + nss_hw_err_event = pevent_log_info + offset; + printf("NVM Subsystem Hardware Error Event Code: %u, %s\n", + le16_to_cpu(nss_hw_err_event->nss_hw_err_event_code), + nvme_show_nss_hw_error(nss_hw_err_event->nss_hw_err_event_code)); + break; + case NVME_CHANGE_NS_EVENT: + ns_event = pevent_log_info + offset; + printf("Change Namespace Event: \n"); + printf("Namespace Management CDW10: %u\n", + le32_to_cpu(ns_event->nsmgt_cdw10)); + printf("Namespace Size: %"PRIu64"\n", + le64_to_cpu(ns_event->nsze)); + printf("Namespace Capacity: %"PRIu64"\n", + le64_to_cpu(ns_event->nscap)); + printf("Formatted LBA Size: %u\n", ns_event->flbas); + printf("End-to-end Data Protection Type Settings: %u\n", + ns_event->dps); + printf("Namespace Multi-path I/O and Namespace Sharing" \ + " Capabilities: %u\n", ns_event->nmic); + printf("ANA Group Identifier: %u\n", + le32_to_cpu(ns_event->ana_grp_id)); + printf("NVM Set Identifier: %u\n", le16_to_cpu(ns_event->nvmset_id)); + printf("Namespace ID: %u\n", le32_to_cpu(ns_event->nsid)); + break; + case NVME_FORMAT_START_EVENT: + format_start_event = pevent_log_info + offset; + printf("Format NVM Start Event: \n"); + printf("Namespace Identifier: %u\n", + le32_to_cpu(format_start_event->nsid)); + printf("Format NVM Attributes: %u\n", + format_start_event->fna); + printf("Format NVM CDW10: %u\n", + le32_to_cpu(format_start_event->format_nvm_cdw10)); + break; + case NVME_FORMAT_COMPLETION_EVENT: + format_cmpln_event = pevent_log_info + offset; + printf("Format NVM Completion Event: \n"); + printf("Namespace Identifier: %u\n", + le32_to_cpu(format_cmpln_event->nsid)); + printf("Smallest Format Progress Indicator: %u\n", + format_cmpln_event->smallest_fpi); + printf("Format NVM Status: %u\n", + format_cmpln_event->format_nvm_status); + printf("Completion Information: %u\n", + le16_to_cpu(format_cmpln_event->compln_info)); + printf("Status Field: %u\n", + le32_to_cpu(format_cmpln_event->status_field)); + break; + case NVME_SANITIZE_START_EVENT: + sanitize_start_event = pevent_log_info + offset; + printf("Sanitize Start Event: \n"); + printf("SANICAP: %u\n", sanitize_start_event->sani_cap); + printf("Sanitize CDW10: %u\n", + le32_to_cpu(sanitize_start_event->sani_cdw10)); + printf("Sanitize CDW11: %u\n", + le32_to_cpu(sanitize_start_event->sani_cdw11)); + break; + case NVME_SANITIZE_COMPLETION_EVENT: + sanitize_cmpln_event = pevent_log_info + offset; + printf("Sanitize Completion Event: \n"); + printf("Sanitize Progress: %u\n", + le16_to_cpu(sanitize_cmpln_event->sani_prog)); + printf("Sanitize Status: %u\n", + le16_to_cpu(sanitize_cmpln_event->sani_status)); + printf("Completion Information: %u\n", + le16_to_cpu(sanitize_cmpln_event->cmpln_info)); + break; + case NVME_THERMAL_EXCURSION_EVENT: + thermal_exc_event = pevent_log_info + offset; + printf("Thermal Excursion Event: \n"); + printf("Over Temperature: %u\n", thermal_exc_event->over_temp); + printf("Threshold: %u\n", thermal_exc_event->threshold); + break; + default: + printf("Reserved Event\n\n"); + } + offset += le16_to_cpu(pevent_entry_head->el); + printf("\n"); + } +} + +void json_endurance_group_event_agg_log( + struct nvme_event_agg_log_page *endurance_log, + __u64 log_entries) +{ + struct json_object *root; + struct json_object *valid_attrs; + struct json_object *valid; + + root = json_create_object(); + json_object_add_value_uint(root, "num_entries_avail", + le64_to_cpu(endurance_log->num_entries)); + valid = json_create_array(); + + for (int i = 0; i < log_entries; i++) { + valid_attrs = json_create_object(); + json_object_add_value_uint(valid_attrs, "entry", + le16_to_cpu(endurance_log->entries[i])); + json_array_add_value_object(valid, valid_attrs); + } + json_object_add_value_array(root, "list_of_entries", valid); + json_print_object(root, NULL); + printf("\n"); + json_free_object(root); +} + +void nvme_show_endurance_group_event_agg_log( + struct nvme_event_agg_log_page *endurance_log, + __u64 log_entries, __u32 size, const char *devname, + enum nvme_print_flags flags) +{ + + if (flags & BINARY) + return d_raw((unsigned char *)endurance_log, size); + if (flags & JSON) + return json_endurance_group_event_agg_log(endurance_log, + log_entries); + + printf("Endurance Group Event Aggregate Log for"\ + " device: %s\n", devname); + + printf("Number of Entries Available: %"PRIu64"\n", + le64_to_cpu(endurance_log->num_entries)); + + for (int i = 0; i < log_entries; i++) { + printf("Entry[%d]: %u\n", i + 1, + le16_to_cpu(endurance_log->entries[i])); + } +} + +void json_lba_status_log(void *lba_status) +{ + struct json_object *root; + struct json_object *desc; + struct json_object *element; + struct json_object *desc_list; + struct json_object *elements_list; + struct nvme_lba_status_hdr *hdr; + struct nvme_lba_status_ns_element *ns_element; + struct nvme_lba_status_range_desc *range_desc; + int offset = sizeof(*hdr); + __u32 num_lba_desc, num_elements; + + root = json_create_object(); + hdr = lba_status; + json_object_add_value_uint(root, "lslplen", le32_to_cpu(hdr->lslplen)); + num_elements = le32_to_cpu(hdr->nlslne); + json_object_add_value_uint(root, "nlslne", num_elements); + json_object_add_value_uint(root, "estulb", le32_to_cpu(hdr->estulb)); + json_object_add_value_uint(root, "lsgc", le16_to_cpu(hdr->lsgc)); + + elements_list = json_create_array(); + for (int ele = 0; ele < num_elements; ele++) { + ns_element = lba_status + offset; + element = json_create_object(); + json_object_add_value_uint(element, "neid", + le32_to_cpu(ns_element->neid)); + num_lba_desc = le32_to_cpu(ns_element->nlrd); + json_object_add_value_uint(element, "nlrd", num_lba_desc); + json_object_add_value_uint(element, "ratype", ns_element->ratype); + + offset += sizeof(*ns_element); + desc_list = json_create_array(); + if (num_lba_desc != 0xffffffff) { + for (int i = 0; i < num_lba_desc; i++) { + range_desc = lba_status + offset; + desc = json_create_object(); + json_object_add_value_uint(desc, "rslba", + le64_to_cpu(range_desc->rslba)); + json_object_add_value_uint(desc, "rnlb", + le32_to_cpu(range_desc->rnlb)); + + offset += sizeof(*range_desc); + json_array_add_value_object(desc_list, desc); + } + } else { + printf("Number of LBA Range Descriptors (NLRD) set to %#x for " \ + "NS element %d", num_lba_desc, ele); + } + + json_object_add_value_array(element, "descs", desc_list); + json_array_add_value_object(elements_list, element); + } + + json_object_add_value_array(root, "ns_elements", elements_list); + json_print_object(root, NULL); + printf("\n"); + json_free_object(root); +} + +void nvme_show_lba_status_log(void *lba_status, __u32 size, + const char *devname, enum nvme_print_flags flags) +{ + struct nvme_lba_status_hdr *hdr; + struct nvme_lba_status_ns_element *ns_element; + struct nvme_lba_status_range_desc *range_desc; + int offset = sizeof(*hdr); + __u32 num_lba_desc, num_elements; + + if (flags & BINARY) + return d_raw((unsigned char *)lba_status, size); + if (flags & JSON) + return json_lba_status_log(lba_status); + + hdr = lba_status; + printf("LBA Status Log for device: %s\n", devname); + printf("LBA Status Log Page Length: %"PRIu32"\n", + le32_to_cpu(hdr->lslplen)); + num_elements = le32_to_cpu(hdr->nlslne); + printf("Number of LBA Status Log Namespace Elements: %"PRIu32"\n", + num_elements); + printf("Estimate of Unrecoverable Logical Blocks: %"PRIu32"\n", + le32_to_cpu(hdr->estulb)); + printf("LBA Status Generation Counter: %"PRIu16"\n", le16_to_cpu(hdr->lsgc)); + for (int ele = 0; ele < num_elements; ele++) { + ns_element = lba_status + offset; + printf("Namespace Element Identifier: %"PRIu32"\n", + le32_to_cpu(ns_element->neid)); + num_lba_desc = le32_to_cpu(ns_element->nlrd); + printf("Number of LBA Range Descriptors: %"PRIu32"\n", num_lba_desc); + printf("Recommended Action Type: %u\n", ns_element->ratype); + + offset += sizeof(*ns_element); + if (num_lba_desc != 0xffffffff) { + for (int i = 0; i < num_lba_desc; i++) { + range_desc = lba_status + offset; + printf("RSLBA[%d]: %"PRIu64"\n", i, + le64_to_cpu(range_desc->rslba)); + printf("RNLB[%d]: %"PRIu32"\n", i, + le32_to_cpu(range_desc->rnlb)); + offset += sizeof(*range_desc); + } + } else { + printf("Number of LBA Range Descriptors (NLRD) set to %#x for "\ + "NS element %d\n", num_lba_desc, ele); + } + } +} + +static const char *resv_notif_to_string(__u8 type) +{ + switch (type) { + case 0x1: return "Empty Log Page"; + case 0x2: return "Registration Preempted"; + case 0x3: return "Reservation Released"; + case 0x4: return "Reservation Preempted"; + default: return "Reserved"; + } +} + +static void json_resv_notif_log(struct nvme_resv_notif_log *resv) +{ + struct json_object *root; + + root = json_create_object(); + json_object_add_value_uint(root, "count", + le64_to_cpu(resv->log_page_count)); + json_object_add_value_uint(root, "rn_log_type", + resv->resv_notif_log_type); + json_object_add_value_uint(root, "num_logs", + resv->num_logs); + json_object_add_value_uint(root, "nsid", + le32_to_cpu(resv->nsid)); + + json_print_object(root, NULL); + printf("\n"); + json_free_object(root); +} + +void nvme_show_resv_notif_log(struct nvme_resv_notif_log *resv, + const char *devname, enum nvme_print_flags flags) +{ + if (flags & BINARY) + return d_raw((unsigned char *)resv, sizeof(*resv)); + if (flags & JSON) + return json_resv_notif_log(resv); + + printf("Reservation Notif Log for device: %s\n", devname); + printf("Log Page Count : %"PRIx64"\n", + le64_to_cpu(resv->log_page_count)); + printf("Resv Notif Log Page Type : %u (%s)\n", + resv->resv_notif_log_type, + resv_notif_to_string(resv->resv_notif_log_type)); + printf("Num of Available Log Pages : %u\n", resv->num_logs); + printf("Namespace ID: : %"PRIx32"\n", + le32_to_cpu(resv->nsid)); +} + static void nvme_show_subsystem(struct nvme_subsystem *s) { int i; @@ -869,9 +1792,9 @@ static void nvme_show_subsystem(struct nvme_subsystem *s) for (i = 0; i < s->nr_ctrls; i++) { printf(" +- %s %s %s %s %s\n", s->ctrls[i].name, - s->ctrls[i].transport, - s->ctrls[i].address, - s->ctrls[i].state, + s->ctrls[i].transport ? : "", + s->ctrls[i].address ? : "", + s->ctrls[i].state ? : "", s->ctrls[i].ana_state ? : ""); } } @@ -879,7 +1802,7 @@ static void nvme_show_subsystem(struct nvme_subsystem *s) static void json_print_nvme_subsystem_list(struct nvme_topology *t) { struct json_object *subsystem_attrs, *path_attrs; - struct json_array *subsystems, *paths; + struct json_object *subsystems, *paths; struct json_object *root; int i, j; @@ -904,12 +1827,15 @@ static void json_print_nvme_subsystem_list(struct nvme_topology *t) path_attrs = json_create_object(); json_object_add_value_string(path_attrs, "Name", c->name); - json_object_add_value_string(path_attrs, "Transport", - c->transport); - json_object_add_value_string(path_attrs, "Address", - c->address); - json_object_add_value_string(path_attrs, "State", - c->state); + if (c->transport) + json_object_add_value_string(path_attrs, + "Transport", c->transport); + if (c->address) + json_object_add_value_string(path_attrs, + "Address", c->address); + if (c->state) + json_object_add_value_string(path_attrs, + "State", c->state); if (c->ana_state) json_object_add_value_string(path_attrs, "ANAState", c->ana_state); @@ -946,14 +1872,16 @@ static void nvme_show_registers_cap(struct nvme_bar_cap *cap) "Not Supported"); printf("\tPersistent Memory Region Supported (PMRS): The Persistent Memory Region is %s\n", (cap->rsvd_cmbs_pmrs & 0x01) ? "Supported" : "Not Supported"); - printf("\tMemory Page Size Maximum (MPSMAX): %u bytes\n", + printf("\tMemory Page Size Maximum (MPSMAX): %u bytes\n", 1 << (12 + ((cap->mpsmax_mpsmin & 0xf0) >> 4))); printf("\tMemory Page Size Minimum (MPSMIN): %u bytes\n", 1 << (12 + (cap->mpsmax_mpsmin & 0x0f))); printf("\tBoot Partition Support (BPS): %s\n", (cap->bps_css_nssrs_dstrd & 0x2000) ? "Yes":"No"); printf("\tCommand Sets Supported (CSS): NVM command set is %s\n", - (cap->bps_css_nssrs_dstrd & 0x0020) ? "supported":"not supported"); + (cap->bps_css_nssrs_dstrd & 0x0020) ? "Supported" : "Not Supported"); + printf("\t One or more I/O Command Sets are %s\n", + (cap->bps_css_nssrs_dstrd & 0x0800) ? "Supported" : "Not Supported"); printf("\tNVM Subsystem Reset Supported (NSSRS): %s\n", (cap->bps_css_nssrs_dstrd & 0x0010) ? "Yes":"No"); printf("\tDoorbell Stride (DSTRD): %u bytes\n", @@ -962,7 +1890,7 @@ static void nvme_show_registers_cap(struct nvme_bar_cap *cap) cap->to * 500); printf("\tArbitration Mechanism Supported (AMS): Weighted Round Robin with Urgent Priority Class is %s\n", (cap->ams_cqr & 0x02) ? "supported":"not supported"); - printf("\tContiguous Queues Required (CQR): %s\n", + printf("\tContiguous Queues Required (CQR): %s\n", (cap->ams_cqr & 0x01) ? "Yes":"No"); printf("\tMaximum Queue Entries Supported (MQES): %u\n\n", cap->mqes + 1); @@ -1020,8 +1948,10 @@ static void nvme_show_registers_cc(__u32 cc) nvme_show_registers_cc_ams((cc & 0x00003800) >> NVME_CC_AMS_SHIFT); printf("\tMemory Page Size (MPS): %u bytes\n", 1 << (12 + ((cc & 0x00000780) >> NVME_CC_MPS_SHIFT))); - printf("\tI/O Command Sets Selected (CSS): %s\n", - (cc & 0x00000070) ? "Reserved":"NVM Command Set"); + printf("\tI/O Command Set Selected (CSS): %s\n", + (cc & 0x00000070) == 0x00 ? "NVM Command Set" : + (cc & 0x00000070) == 0x60 ? "All supported I/O Command Sets" : + (cc & 0x00000070) == 0x70 ? "Admin Command Set only" : "Reserved"); printf("\tEnable (EN): %s\n\n", (cc & 0x00000001) ? "Yes":"No"); } @@ -1199,11 +2129,11 @@ static void nvme_show_registers_bpmbl(uint64_t bpmbl) static void nvme_show_registers_cmbmsc(uint64_t cmbmsc) { - printf("\tController Base Address (CBA) : %" PRIx64 "\n", + printf("\tController Base Address (CBA): %" PRIx64 "\n", (cmbmsc & 0xfffffffffffff000) >> 12); printf("\tController Memory Space Enable (CMSE): %" PRIx64 "\n", (cmbmsc & 0x0000000000000002) >> 1); - printf("\tCapabilities Registers Enabled (CRE) : CMBLOC and "\ + printf("\tCapabilities Registers Enabled (CRE): CMBLOC and "\ "CMBSZ registers are%senabled\n\n", (cmbmsc & 0x0000000000000001) ? " " : " NOT "); } @@ -1216,20 +2146,20 @@ static void nvme_show_registers_cmbsts(__u32 cmbsts) static void nvme_show_registers_pmrcap(__u32 pmrcap) { - printf("\tController Memory Space Supported (CMSS) : "\ + printf("\tController Memory Space Supported (CMSS): "\ "Referencing PMR with host supplied addresses is %s\n", ((pmrcap & 0x01000000) >> 24) ? "Supported" : "Not Supported"); - printf("\tPersistent Memory Region Timeout (PMRTO): %x\n", + printf("\tPersistent Memory Region Timeout (PMRTO): %x\n", (pmrcap & 0x00ff0000) >> 16); - printf("\tPersistent Memory Region Write Barrier Mechanisms(PMRWBM): %x\n", + printf("\tPersistent Memory Region Write Barrier Mechanisms (PMRWBM): %x\n", (pmrcap & 0x00003c00) >> 10); - printf("\tPersistent Memory Region Time Units (PMRTU): PMR time unit is %s\n", + printf("\tPersistent Memory Region Time Units (PMRTU): PMR time unit is %s\n", (pmrcap & 0x00000300) >> 8 ? "minutes":"500 milliseconds"); - printf("\tBase Indicator Register (BIR): %x\n", + printf("\tBase Indicator Register (BIR): %x\n", (pmrcap & 0x000000e0) >> 5); - printf("\tWrite Data Support (WDS): Write data to the PMR is %s\n", + printf("\tWrite Data Support (WDS): Write data to the PMR is %s\n", (pmrcap & 0x00000010) ? "supported":"not supported"); - printf("\tRead Data Support (RDS): Read data from the PMR is %s\n", + printf("\tRead Data Support (RDS): Read data from the PMR is %s\n", (pmrcap & 0x00000008) ? "supported":"not supported"); } @@ -1254,14 +2184,14 @@ static void nvme_show_registers_pmrsts(__u32 pmrsts, __u32 pmrctl) { printf("\tController Base Address Invalid (CBAI): %x\n", (pmrsts & 0x00001000) >> 12); - printf("\tHealth Status (HSTS): %s\n", + printf("\tHealth Status (HSTS): %s\n", nvme_register_pmr_hsts_to_string((pmrsts & 0x00000e00) >> 9)); - printf("\tNot Ready (NRDY): "\ + printf("\tNot Ready (NRDY): "\ "The Persistent Memory Region is %s to process "\ "PCI Express memory read and write requests\n", (pmrsts & 0x00000100) == 0 && (pmrctl & 0x00000001) ? "READY":"Not Ready"); - printf("\tError (ERR) : %x\n", (pmrsts & 0x000000ff)); + printf("\tError (ERR): %x\n", (pmrsts & 0x000000ff)); } static const char *nvme_register_pmr_pmrszu_to_string(__u8 pmrszu) @@ -1278,7 +2208,7 @@ static const char *nvme_register_pmr_pmrszu_to_string(__u8 pmrszu) static void nvme_show_registers_pmrebs(__u32 pmrebs) { printf("\tPMR Elasticity Buffer Size Base (PMRWBZ): %x\n", (pmrebs & 0xffffff00) >> 8); - printf("\tRead Bypass Behavior : memory reads not conflicting with memory writes "\ + printf("\tRead Bypass Behavior : memory reads not conflicting with memory writes "\ "in the PMR Elasticity Buffer %s bypass those memory writes\n", (pmrebs & 0x00000010) ? "SHALL":"MAY"); printf("\tPMR Elasticity Buffer Size Units (PMRSZU): %s\n", @@ -1293,12 +2223,18 @@ static void nvme_show_registers_pmrswtp(__u32 pmrswtp) nvme_register_pmr_pmrszu_to_string(pmrswtp & 0x0000000f)); } -static void nvme_show_registers_pmrmsc(uint64_t pmrmsc) +static void nvme_show_registers_pmrmscl(uint32_t pmrmscl) +{ + printf("\tController Base Address (CBA): %#x\n", + (pmrmscl & 0xfffff000) >> 12); + printf("\tController Memory Space Enable (CMSE): %#x\n\n", + (pmrmscl & 0x00000002) >> 1); +} + +static void nvme_show_registers_pmrmscu(uint32_t pmrmscu) { - printf("\tController Base Address (CBA) : %" PRIx64 "\n", - (pmrmsc & 0xfffffffffffff000) >> 12); - printf("\tController Memory Space Enable (CMSE) : %" PRIx64 "\n\n", - (pmrmsc & 0x0000000000000002) >> 1); + printf("\tController Base Address (CBA): %#x\n", + pmrmscu); } static inline uint32_t mmio_read32(void *addr) @@ -1311,16 +2247,21 @@ static inline uint32_t mmio_read32(void *addr) /* Access 64-bit registers as 2 32-bit; Some devices fail 64-bit MMIO. */ static inline __u64 mmio_read64(void *addr) { - __le32 *p = addr; + const volatile __u32 *p = addr; + __u32 low, high; + + low = le32_to_cpu(*p); + high = le32_to_cpu(*(p + 1)); - return le32_to_cpu(*p) | ((uint64_t)le32_to_cpu(*(p + 1)) << 32); + return ((__u64) high << 32) | low; } static void json_ctrl_registers(void *bar) { - uint64_t cap, asq, acq, bpmbl, cmbmsc, pmrmsc; + uint64_t cap, asq, acq, bpmbl, cmbmsc; uint32_t vs, intms, intmc, cc, csts, nssr, aqa, cmbsz, cmbloc, - bpinfo, bprsel, cmbsts, pmrcap, pmrctl, pmrsts, pmrebs, pmrswtp; + bpinfo, bprsel, cmbsts, pmrcap, pmrctl, pmrsts, pmrebs, pmrswtp, + pmrmscl, pmrmscu; struct json_object *root; cap = mmio_read64(bar + NVME_REG_CAP); @@ -1345,7 +2286,8 @@ static void json_ctrl_registers(void *bar) pmrsts = mmio_read32(bar + NVME_REG_PMRSTS); pmrebs = mmio_read32(bar + NVME_REG_PMREBS); pmrswtp = mmio_read32(bar + NVME_REG_PMRSWTP); - pmrmsc = mmio_read64(bar + NVME_REG_PMRMSC); + pmrmscl = mmio_read32(bar + NVME_REG_PMRMSCL); + pmrmscu = mmio_read32(bar + NVME_REG_PMRMSCU); root = json_create_object(); json_object_add_value_uint(root, "cap", cap); @@ -1370,7 +2312,8 @@ static void json_ctrl_registers(void *bar) json_object_add_value_int(root, "pmrsts", pmrsts); json_object_add_value_int(root, "pmrebs", pmrebs); json_object_add_value_int(root, "pmrswtp", pmrswtp); - json_object_add_value_uint(root, "pmrmsc", pmrmsc); + json_object_add_value_uint(root, "pmrmscl", pmrmscl); + json_object_add_value_uint(root, "pmrmscu", pmrmscu); json_print_object(root, NULL); printf("\n"); json_free_object(root); @@ -1378,10 +2321,11 @@ static void json_ctrl_registers(void *bar) void nvme_show_ctrl_registers(void *bar, bool fabrics, enum nvme_print_flags flags) { - const unsigned int reg_size = 0x50; /* 00h to 4Fh */ - uint64_t cap, asq, acq, bpmbl, cmbmsc, pmrmsc; + const unsigned int reg_size = 0x0e1c; /* 0x0000 to 0x0e1b */ + uint64_t cap, asq, acq, bpmbl, cmbmsc; uint32_t vs, intms, intmc, cc, csts, nssr, aqa, cmbsz, cmbloc, bpinfo, - bprsel, cmbsts, pmrcap, pmrctl, pmrsts, pmrebs, pmrswtp; + bprsel, cmbsts, pmrcap, pmrctl, pmrsts, pmrebs, pmrswtp, + pmrmscl, pmrmscu; int human = flags & VERBOSE; if (flags & BINARY) @@ -1411,7 +2355,8 @@ void nvme_show_ctrl_registers(void *bar, bool fabrics, enum nvme_print_flags fla pmrsts = mmio_read32(bar + NVME_REG_PMRSTS); pmrebs = mmio_read32(bar + NVME_REG_PMREBS); pmrswtp = mmio_read32(bar + NVME_REG_PMRSWTP); - pmrmsc = mmio_read64(bar + NVME_REG_PMRMSC); + pmrmscl = mmio_read32(bar + NVME_REG_PMRMSCL); + pmrmscu = mmio_read32(bar + NVME_REG_PMRMSCU); if (human) { if (cap != 0xffffffff) { @@ -1490,8 +2435,11 @@ void nvme_show_ctrl_registers(void *bar, bool fabrics, enum nvme_print_flags fla printf("pmrswtp : %x\n", pmrswtp); nvme_show_registers_pmrswtp(pmrswtp); - printf("pmrmsc : %"PRIx64"\n", pmrmsc); - nvme_show_registers_pmrmsc(pmrmsc); + printf("pmrmscl : %#x\n", pmrmscl); + nvme_show_registers_pmrmscl(pmrmscl); + + printf("pmrmscu : %#x\n", pmrmscu); + nvme_show_registers_pmrmscu(pmrmscu); } } else { if (cap != 0xffffffff) @@ -1522,7 +2470,8 @@ void nvme_show_ctrl_registers(void *bar, bool fabrics, enum nvme_print_flags fla printf("pmrsts : %x\n", pmrsts); printf("pmrebs : %x\n", pmrebs); printf("pmrswtp : %x\n", pmrswtp); - printf("pmrmsc : %"PRIx64"\n", pmrmsc); + printf("pmrmscl : %#x\n", pmrmscl); + printf("pmrmscu : %#x\n", pmrmscu); } } } @@ -1615,7 +2564,7 @@ void nvme_show_relatives(const char *name) free(path); return; } - err = scan_subsystems(&t, subsysnqn, 0); + err = scan_subsystems(&t, subsysnqn, 0, 0, NULL); if (err || t.nr_subsystems != 1) { free(subsysnqn); free(path); @@ -1698,7 +2647,7 @@ void d_raw(unsigned char *buf, unsigned len) { unsigned i; for (i = 0; i < len; i++) - putchar(*(buf+i)); + putchar(*(buf + i)); } void nvme_show_status(__u16 status) @@ -1765,17 +2714,23 @@ static void nvme_show_id_ctrl_cmic(__u8 cmic) static void nvme_show_id_ctrl_oaes(__le32 ctrl_oaes) { __u32 oaes = le32_to_cpu(ctrl_oaes); - __u32 rsvd0 = (oaes & 0xFFFF8000) >> 15; + __u32 rsvd0 = (oaes & 0xF0000000) >> 28; + __u32 zicn = (oaes & 0x08000000) >> 27; + __u32 rsvd1 = (oaes & 0x07FF8000) >> 15; __u32 nace = (oaes & 0x100) >> 8; __u32 fan = (oaes & 0x200) >> 9; __u32 anacn = (oaes & 0x800) >> 11; __u32 plealcn = (oaes & 0x1000) >> 12; __u32 lbasin = (oaes & 0x2000) >> 13; __u32 egealpcn = (oaes & 0x4000) >> 14; - __u32 rsvd1 = oaes & 0xFF; + __u32 rsvd2 = oaes & 0xFF; if (rsvd0) - printf(" [31:10] : %#x\tReserved\n", rsvd0); + printf(" [31:28] : %#x\tReserved\n", rsvd0); + printf("[27:27] : %#x\tZone Descriptor Changed Notices %sSupported\n", + zicn, zicn ? "" : "Not "); + if (rsvd1) + printf(" [26:15] : %#x\tReserved\n", rsvd1); printf("[14:14] : %#x\tEndurance Group Event Aggregate Log Page"\ " Change Notice %sSupported\n", egealpcn, egealpcn ? "" : "Not "); @@ -1790,7 +2745,7 @@ static void nvme_show_id_ctrl_oaes(__le32 ctrl_oaes) fan, fan ? "" : "Not "); printf(" [8:8] : %#x\tNamespace Attribute Changed Event %sSupported\n", nace, nace ? "" : "Not "); - if (rsvd1) + if (rsvd2) printf(" [7:0] : %#x\tReserved\n", rsvd1); printf("\n"); } @@ -1955,7 +2910,7 @@ static void nvme_show_id_ctrl_apsta(__u8 apsta) printf("\n"); } -static void nvme_show_id_ctrl_rpmbs(__le32 ctrl_rpmbs) +void nvme_show_id_ctrl_rpmbs(__le32 ctrl_rpmbs) { __u32 rpmbs = le32_to_cpu(ctrl_rpmbs); __u32 asz = (rpmbs & 0xFF000000) >> 24; @@ -2068,7 +3023,8 @@ static void nvme_show_id_ctrl_cqes(__u8 cqes) static void nvme_show_id_ctrl_oncs(__le16 ctrl_oncs) { __u16 oncs = le16_to_cpu(ctrl_oncs); - __u16 rsvd = (oncs & 0xFF00) >> 8; + __u16 rsvd = (oncs & 0xFE00) >> 9; + __u16 copy = (oncs & 0x100) >> 8; __u16 vrfy = (oncs & 0x80) >> 7; __u16 tmst = (oncs & 0x40) >> 6; __u16 resv = (oncs & 0x20) >> 5; @@ -2079,7 +3035,9 @@ static void nvme_show_id_ctrl_oncs(__le16 ctrl_oncs) __u16 cmp = oncs & 0x1; if (rsvd) - printf(" [15:8] : %#x\tReserved\n", rsvd); + printf(" [15:9] : %#x\tReserved\n", rsvd); + printf(" [8:8] : %#x\tCopy %sSupported\n", + copy, copy ? "" : "Not "); printf(" [7:7] : %#x\tVerify %sSupported\n", vrfy, vrfy ? "" : "Not "); printf(" [6:6] : %#x\tTimestamp %sSupported\n", @@ -2150,10 +3108,10 @@ static void nvme_show_id_ctrl_vwc(__u8 vwc) printf("\n"); } -static void nvme_show_id_ctrl_nvscc(__u8 nvscc) +static void nvme_show_id_ctrl_icsvscc(__u8 icsvscc) { - __u8 rsvd = (nvscc & 0xFE) >> 1; - __u8 fmt = nvscc & 0x1; + __u8 rsvd = (icsvscc & 0xFE) >> 1; + __u8 fmt = icsvscc & 0x1; if (rsvd) printf(" [7:1] : %#x\tReserved\n", rsvd); printf(" [0:0] : %#x\tNVM Vendor Specific Commands uses %s Format\n", @@ -2233,10 +3191,10 @@ static void nvme_show_id_ctrl_sgls(__le32 ctrl_sgls) printf("\n"); } -static void nvme_show_id_ctrl_ctrattr(__u8 ctrattr) +static void nvme_show_id_ctrl_fcatt(__u8 fcatt) { - __u8 rsvd = (ctrattr & 0xFE) >> 1; - __u8 scm = ctrattr & 0x1; + __u8 rsvd = (fcatt & 0xFE) >> 1; + __u8 scm = fcatt & 0x1; if (rsvd) printf(" [7:1] : %#x\tReserved\n", rsvd); printf(" [0:0] : %#x\t%s Controller Model\n", @@ -2244,10 +3202,23 @@ static void nvme_show_id_ctrl_ctrattr(__u8 ctrattr) printf("\n"); } +static void nvme_show_id_ctrl_ofcs(__le16 ofcs) +{ + __u16 rsvd = (ofcs & 0xfffe) >> 1; + __u8 disconn = ofcs & 0x1; + if (rsvd) + printf(" [15:1] : %#x\tReserved\n", rsvd); + printf(" [0:0] : %#x\tDisconnect command %s Supported\n", + disconn, disconn ? "" : "Not"); + printf("\n"); + +} + static void nvme_show_id_ns_nsfeat(__u8 nsfeat) { __u8 rsvd = (nsfeat & 0xE0) >> 5; __u8 ioopt = (nsfeat & 0x10) >> 4; + __u8 uidreuse = (nsfeat & 0x8) >> 3; __u8 dulbe = (nsfeat & 0x4) >> 2; __u8 na = (nsfeat & 0x2) >> 1; __u8 thin = nsfeat & 0x1; @@ -2255,6 +3226,8 @@ static void nvme_show_id_ns_nsfeat(__u8 nsfeat) printf(" [7:5] : %#x\tReserved\n", rsvd); printf(" [4:4] : %#x\tNPWG, NPWA, NPDG, NPDA, and NOWS are %sSupported\n", ioopt, ioopt ? "" : "Not "); + printf(" [3:3] : %#x\tNGUID and EUI64 fields if non-zero, %sReused\n", + uidreuse, uidreuse ? "Never " : ""); printf(" [2:2] : %#x\tDeallocated or Unwritten Logical Block error %sSupported\n", dulbe, dulbe ? "" : "Not "); printf(" [1:1] : %#x\tNamespace uses %s\n", @@ -2344,7 +3317,7 @@ static void nvme_show_id_ns_nmic(__u8 nmic) static void nvme_show_id_ns_rescap(__u8 rescap) { - __u8 rsvd = (rescap & 0x80) >> 7; + __u8 iekr = (rescap & 0x80) >> 7; __u8 eaar = (rescap & 0x40) >> 6; __u8 wear = (rescap & 0x20) >> 5; __u8 earo = (rescap & 0x10) >> 4; @@ -2352,8 +3325,9 @@ static void nvme_show_id_ns_rescap(__u8 rescap) __u8 ea = (rescap & 0x4) >> 2; __u8 we = (rescap & 0x2) >> 1; __u8 ptpl = rescap & 0x1; - if (rsvd) - printf(" [7:7] : %#x\tReserved\n", rsvd); + + printf(" [7:7] : %#x\tIgnore Existing Key - Used as defined in revision %s\n", + iekr, iekr ? "1.3 or later" : "1.2.1 or earlier"); printf(" [6:6] : %#x\tExclusive Access - All Registrants %sSupported\n", eaar, eaar ? "" : "Not "); printf(" [5:5] : %#x\tWrite Exclusive - All Registrants %sSupported\n", @@ -2412,7 +3386,7 @@ void nvme_show_id_ns(struct nvme_id_ns *ns, unsigned int nsid, if (flags & BINARY) return d_raw((unsigned char *)ns, sizeof(*ns)); if (flags & JSON) - return json_nvme_id_ns(ns, flags); + return json_nvme_id_ns(ns); printf("NVME Identify Namespace %d:\n", nsid); printf("nsze : %#"PRIx64"\n", le64_to_cpu(ns->nsze)); @@ -2461,9 +3435,12 @@ void nvme_show_id_ns(struct nvme_id_ns *ns, unsigned int nsid, printf("npda : %u\n", le16_to_cpu(ns->npda)); printf("nows : %u\n", le16_to_cpu(ns->nows)); } + printf("mssrl : %u\n", le16_to_cpu(ns->mssrl)); + printf("mcl : %d\n", le32_to_cpu(ns->mcl)); + printf("msrc : %u\n", ns->msrc); + printf("anagrpid: %u\n", le32_to_cpu(ns->anagrpid)); printf("nsattr : %u\n", ns->nsattr); printf("nvmsetid: %d\n", le16_to_cpu(ns->nvmsetid)); - printf("anagrpid: %u\n", le32_to_cpu(ns->anagrpid)); printf("endgid : %d\n", le16_to_cpu(ns->endgid)); printf("nguid : "); @@ -2492,6 +3469,7 @@ void nvme_show_id_ns(struct nvme_id_ns *ns, unsigned int nsid, ns->lbaf[i].rp, i == (ns->flbas & 0xf) ? "(in use)" : ""); } + if (vs) { printf("vs[]:\n"); d(ns->vs, sizeof(ns->vs), 16, 1); @@ -2512,10 +3490,11 @@ static void json_nvme_id_ns_descs(void *data) #ifdef LIBUUID uuid_t uuid; #endif + __u8 csi; } desc; struct json_object *root; - struct json_array *json_array = NULL; + struct json_object *json_array = NULL; off_t off; int pos, len = 0; @@ -2557,6 +3536,12 @@ static void json_nvme_id_ns_descs(void *data) nidt_name = "uuid"; break; #endif + case NVME_NIDT_CSI: + memcpy(&desc.csi, data + off, sizeof(desc.csi)); + json_str_p += sprintf(json_str_p, "%#x", desc.csi); + len += sizeof(desc.csi); + nidt_name = "csi"; + break; default: /* Skip unnkown types */ len = cur->nidl; @@ -2602,6 +3587,7 @@ void nvme_show_id_ns_descs(void *data, unsigned nsid, enum nvme_print_flags flag #endif __u8 eui64[8]; __u8 nguid[16]; + __u8 csi; if (flags & BINARY) return d_raw((unsigned char *)data, 0x1000); @@ -2640,6 +3626,11 @@ void nvme_show_id_ns_descs(void *data, unsigned nsid, enum nvme_print_flags flag len = sizeof(uuid); break; #endif + case NVME_NIDT_CSI: + memcpy(&csi, data + pos + sizeof(*cur), 1); + printf("csi : %#x\n", csi); + len += sizeof(csi); + break; default: /* Skip unnkown types */ len = cur->nidl; @@ -2716,7 +3707,7 @@ void __nvme_show_id_ctrl(struct nvme_id_ctrl *ctrl, enum nvme_print_flags flags, if (flags & BINARY) return d_raw((unsigned char *)ctrl, sizeof(*ctrl)); else if (flags & JSON) - return json_nvme_id_ctrl(ctrl, flags, vendor_show); + return json_nvme_id_ctrl(ctrl, vendor_show); printf("NVME Identify Controller:\n"); printf("vid : %#x\n", le16_to_cpu(ctrl->vid)); @@ -2824,9 +3815,9 @@ void __nvme_show_id_ctrl(struct nvme_id_ctrl *ctrl, enum nvme_print_flags flags, nvme_show_id_ctrl_vwc(ctrl->vwc); printf("awun : %d\n", le16_to_cpu(ctrl->awun)); printf("awupf : %d\n", le16_to_cpu(ctrl->awupf)); - printf("nvscc : %d\n", ctrl->nvscc); + printf("icsvscc : %d\n", ctrl->icsvscc); if (human) - nvme_show_id_ctrl_nvscc(ctrl->nvscc); + nvme_show_id_ctrl_icsvscc(ctrl->icsvscc); printf("nwpc : %d\n", ctrl->nwpc); if (human) nvme_show_id_ctrl_nwpc(ctrl->nwpc); @@ -2839,10 +3830,13 @@ void __nvme_show_id_ctrl(struct nvme_id_ctrl *ctrl, enum nvme_print_flags flags, printf("ioccsz : %d\n", le32_to_cpu(ctrl->ioccsz)); printf("iorcsz : %d\n", le32_to_cpu(ctrl->iorcsz)); printf("icdoff : %d\n", le16_to_cpu(ctrl->icdoff)); - printf("ctrattr : %#x\n", ctrl->ctrattr); + printf("fcatt : %#x\n", ctrl->fcatt); if (human) - nvme_show_id_ctrl_ctrattr(ctrl->ctrattr); + nvme_show_id_ctrl_fcatt(ctrl->fcatt); printf("msdbd : %d\n", ctrl->msdbd); + printf("ofcs : %d\n", le16_to_cpu(ctrl->ofcs)); + if (human) + nvme_show_id_ctrl_ofcs(ctrl->ofcs); nvme_show_id_ctrl_power(ctrl); if (vendor_show) @@ -2858,10 +3852,292 @@ void nvme_show_id_ctrl(struct nvme_id_ctrl *ctrl, unsigned int mode) __nvme_show_id_ctrl(ctrl, mode, NULL); } +static void json_nvme_id_ctrl_nvm(struct nvme_id_ctrl_nvm *ctrl_nvm) +{ + struct json_object *root; + + root = json_create_object(); + json_object_add_value_uint(root, "vsl", ctrl_nvm->vsl); + json_object_add_value_uint(root, "wzsl", ctrl_nvm->wzsl); + json_object_add_value_uint(root, "wusl", ctrl_nvm->wusl); + json_object_add_value_uint(root, "dmrl", ctrl_nvm->dmrl); + json_object_add_value_uint(root, "dmrsl", ctrl_nvm->dmrsl); + json_object_add_value_uint(root, "dmsl", ctrl_nvm->dmsl); + + json_print_object(root, NULL); + printf("\n"); + json_free_object(root); +} + +void nvme_show_id_ctrl_nvm(struct nvme_id_ctrl_nvm *ctrl_nvm, + enum nvme_print_flags flags) +{ + if (flags & BINARY) + return d_raw((unsigned char *)ctrl_nvm, sizeof(*ctrl_nvm)); + else if (flags & JSON) + return json_nvme_id_ctrl_nvm(ctrl_nvm); + + printf("NVMe Identify Controller NVM:\n"); + printf("vsl : %u\n", ctrl_nvm->vsl); + printf("wzsl : %u\n", ctrl_nvm->wzsl); + printf("wusl : %u\n", ctrl_nvm->wusl); + printf("dmrl : %u\n", ctrl_nvm->dmrl); + printf("dmrsl : %u\n", le32_to_cpu(ctrl_nvm->dmrsl)); + printf("dmsl : %"PRIu64"\n", le64_to_cpu(ctrl_nvm->dmsl)); +} + +static void json_nvme_zns_id_ctrl(struct nvme_zns_id_ctrl *ctrl) +{ + struct json_object *root; + + root = json_create_object(); + json_object_add_value_int(root, "zasl", ctrl->zasl); + + json_print_object(root, NULL); + printf("\n"); + json_free_object(root); +} + +void nvme_show_zns_id_ctrl(struct nvme_zns_id_ctrl *ctrl, unsigned int mode) +{ + if (mode & BINARY) + return d_raw((unsigned char *)ctrl, sizeof(*ctrl)); + else if (mode & JSON) + return json_nvme_zns_id_ctrl(ctrl); + + printf("NVMe ZNS Identify Controller:\n"); + printf("zasl : %u\n", ctrl->zasl); +} + +void json_nvme_zns_id_ns(struct nvme_zns_id_ns *ns, + struct nvme_id_ns *id_ns) +{ + struct json_object *root; + struct json_object *lbafs; + int i; + + root = json_create_object(); + json_object_add_value_int(root, "zoc", le16_to_cpu(ns->zoc)); + json_object_add_value_int(root, "ozcs", le16_to_cpu(ns->ozcs)); + json_object_add_value_int(root, "mar", le32_to_cpu(ns->mar)); + json_object_add_value_int(root, "mor", le32_to_cpu(ns->mor)); + json_object_add_value_int(root, "rrl", le32_to_cpu(ns->rrl)); + json_object_add_value_int(root, "frl", le32_to_cpu(ns->frl)); + + lbafs = json_create_array(); + json_object_add_value_array(root, "lbafe", lbafs); + + for (i = 0; i <= id_ns->nlbaf; i++) { + struct json_object *lbaf = json_create_object(); + + json_object_add_value_int(lbaf, "zsze", + le64_to_cpu(ns->lbafe[i].zsze)); + json_object_add_value_int(lbaf, "zdes", ns->lbafe[i].zdes); + + json_array_add_value_object(lbafs, lbaf); + } + json_print_object(root, NULL); + printf("\n"); + json_free_object(root); +} + +static void show_nvme_id_ns_zoned_zoc(__le16 ns_zoc) +{ + __u16 zoc = le16_to_cpu(ns_zoc); + __u8 rsvd = (zoc & 0xfffc) >> 2; + __u8 ze = (zoc & 0x2) >> 1; + __u8 vzc = zoc & 0x1; + if (rsvd) + printf(" [15:2] : %#x\tReserved\n", rsvd); + printf(" [1:1] : %#x\t Zone Active Excursions: %s\n", + ze, ze ? "Yes (Host support required)" : "No"); + printf(" [0:0] : %#x\t Variable Zone Capacity: %s\n", + vzc, vzc ? "Yes (Host support required)" : "No"); + printf("\n"); +} + +static void show_nvme_id_ns_zoned_ozcs(__le16 ns_ozcs) +{ + __u16 ozcs = le16_to_cpu(ns_ozcs); + __u8 rsvd = (ozcs & 0xfffe) >> 1; + __u8 razb = ozcs & 0x1; + + if (rsvd) + printf(" [15:1] : %#x\tReserved\n", rsvd); + printf(" [0:0] : %#x\t Read Across Zone Boundaries: %s\n", + razb, razb ? "Yes" : "No"); +} + +void nvme_show_zns_id_ns(struct nvme_zns_id_ns *ns, + struct nvme_id_ns *id_ns, unsigned long flags) +{ + int human = flags & VERBOSE, vs = flags & VS; + uint8_t lbaf = id_ns->flbas & NVME_NS_FLBAS_LBA_MASK; + int i; + + if (flags & BINARY) + return d_raw((unsigned char *)ns, sizeof(*ns)); + else if (flags & JSON) + return json_nvme_zns_id_ns(ns, id_ns); + + printf("ZNS Command Set Identify Namespace:\n"); + + if (human) { + printf("zoc : %u\tZone Operation Characteristics\n", le16_to_cpu(ns->zoc)); + show_nvme_id_ns_zoned_zoc(ns->zoc); + } else { + printf("zoc : %u\n", le16_to_cpu(ns->zoc)); + } + + if (human) { + printf("ozcs : %u\tOptional Zoned Command Support\n", le16_to_cpu(ns->ozcs)); + show_nvme_id_ns_zoned_ozcs(ns->ozcs); + } else { + printf("ozcs : %u\n", le16_to_cpu(ns->ozcs)); + } + + if (human) { + if (ns->mar == 0xffffffff) { + printf("mar : No Active Resource Limit\n"); + } else { + printf("mar : %u\tActive Resources\n", le32_to_cpu(ns->mar) + 1); + } + } else { + printf("mar : %#x\n", le32_to_cpu(ns->mar)); + } + + if (human) { + if (ns->mor == 0xffffffff) { + printf("mor : No Open Resource Limit\n"); + } else { + printf("mor : %u\tOpen Resources\n", le32_to_cpu(ns->mor) + 1); + } + } else { + printf("mor : %#x\n", le32_to_cpu(ns->mor)); + } + + if (!le32_to_cpu(ns->rrl) && human) + printf("rrl : Not Reported\n"); + else + printf("rrl : %d\n", le32_to_cpu(ns->rrl)); + + if (!le32_to_cpu(ns->frl) && human) + printf("frl : Not Reported\n"); + else + printf("frl : %d\n", le32_to_cpu(ns->frl)); + + for (i = 0; i <= id_ns->nlbaf; i++){ + if (human) + printf("LBA Format Extension %2d : Zone Size: 0x%"PRIx64" LBAs - " + "Zone Descriptor Extension Size: %-1d bytes%s\n", + i, le64_to_cpu(ns->lbafe[i].zsze), ns->lbafe[i].zdes << 6, + i == lbaf ? " (in use)" : ""); + else + printf("lbafe %2d: zsze:0x%"PRIx64" zdes:%u%s\n", i, + (uint64_t)le64_to_cpu(ns->lbafe[i].zsze), + ns->lbafe[i].zdes, i == lbaf ? " (in use)" : ""); + } + + if (vs) { + printf("vs[] :\n"); + d(ns->vs, sizeof(ns->vs), 16, 1); + } +} + +void nvme_show_zns_changed(struct nvme_zns_changed_zone_log *log, + unsigned long flags) +{ + uint16_t nrzid; + int i; + + if (flags & BINARY) + return d_raw((unsigned char *)log, sizeof(*log)); + + nrzid = le16_to_cpu(log->nrzid); + printf("NVMe Changed Zone List:\n"); + + if (nrzid == 0xFFFF) { + printf("Too many zones have changed to fit into the log. Use report zones for changes.\n"); + return; + } + + printf("nrzid: %u\n", nrzid); + for (i = 0; i < nrzid; i++) + printf("zid %03d: %"PRIu64"\n", i, (uint64_t)le64_to_cpu(log->zid[i])); +} + +char *zone_type_to_string(__u8 cond) +{ + switch (cond) { + case NVME_ZONE_TYPE_SEQWRITE_REQ: + return "SEQWRITE_REQ"; + default: + return "Unknown"; + } +} + +char *zone_state_to_string(__u8 state) +{ + switch (state) { + case NVME_ZNS_ZS_EMPTY: + return "EMPTY"; + case NVME_ZNS_ZS_IMPL_OPEN: + return "IMP_OPENED"; + case NVME_ZNS_ZS_EXPL_OPEN: + return "EXP_OPENED"; + case NVME_ZNS_ZS_CLOSED: + return "CLOSED"; + case NVME_ZNS_ZS_READ_ONLY: + return "READONLY"; + case NVME_ZNS_ZS_FULL: + return "FULL"; + case NVME_ZNS_ZS_OFFLINE: + return "OFFLINE"; + default: + return "Unknown State"; + } +} + +void nvme_show_zns_report_zones(void *report, __u32 descs, + __u8 ext_size, __u32 report_size, unsigned long flags) +{ + struct nvme_zone_report *r = report; + struct nvme_zns_desc *desc; + int i; + + __u64 nr_zones = le64_to_cpu(r->nr_zones); + + if (nr_zones < descs) + descs = nr_zones; + + if (flags & BINARY) + return d_raw((unsigned char *)report, report_size); + + printf("nr_zones: %"PRIu64"\n", (uint64_t)le64_to_cpu(r->nr_zones)); + for (i = 0; i < descs; i++) { + desc = (struct nvme_zns_desc *) + (report + sizeof(*r) + i * (sizeof(*desc) + ext_size)); + printf("SLBA: 0x%-8"PRIx64" WP: 0x%-8"PRIx64" Cap: 0x%-8"PRIx64" State: %-12s Type: %-14s Attrs: 0x%-x\n", + (uint64_t)le64_to_cpu(desc->zslba), (uint64_t)le64_to_cpu(desc->wp), + (uint64_t)le64_to_cpu(desc->zcap), zone_state_to_string(desc->zs >> 4), + zone_type_to_string(desc->zt), desc->za); + + if (ext_size) { + printf("Extension Data: "); + if (desc->za & NVME_ZNS_ZA_ZDEV) { + d((unsigned char *)desc + sizeof(*desc), ext_size, 16, 1); + printf("..\n"); + } else { + printf(" Not valid\n"); + } + } + } +} + static void json_nvme_id_nvmset(struct nvme_id_nvmset *nvmset) { __u32 nent = nvmset->nid; - struct json_array *entries; + struct json_object *entries; struct json_object *root; int i; @@ -2926,12 +4202,83 @@ void nvme_show_id_nvmset(struct nvme_id_nvmset *nvmset, unsigned nvmset_id, } } +static void json_nvme_primary_ctrl_caps(const struct nvme_primary_ctrl_caps *caps) +{ + struct json_object *root; + + root = json_create_object(); + + json_object_add_value_uint(root, "cntlid", le16_to_cpu(caps->cntlid)); + json_object_add_value_uint(root, "portid", le16_to_cpu(caps->portid)); + json_object_add_value_uint(root, "crt", caps->crt); + + json_object_add_value_int(root, "vqfrt", le32_to_cpu(caps->vqfrt)); + json_object_add_value_int(root, "vqrfa", le32_to_cpu(caps->vqrfa)); + json_object_add_value_int(root, "vqrfap", le16_to_cpu(caps->vqrfap)); + json_object_add_value_int(root, "vqprt", le16_to_cpu(caps->vqprt)); + json_object_add_value_int(root, "vqfrsm", le16_to_cpu(caps->vqfrsm)); + json_object_add_value_int(root, "vqgran", le16_to_cpu(caps->vqgran)); + + json_object_add_value_int(root, "vifrt", le32_to_cpu(caps->vifrt)); + json_object_add_value_int(root, "virfa", le32_to_cpu(caps->virfa)); + json_object_add_value_int(root, "virfap", le16_to_cpu(caps->virfap)); + json_object_add_value_int(root, "viprt", le16_to_cpu(caps->viprt)); + json_object_add_value_int(root, "vifrsm", le16_to_cpu(caps->vifrsm)); + json_object_add_value_int(root, "vigran", le16_to_cpu(caps->vigran)); + + json_print_object(root, NULL); + printf("\n"); + json_free_object(root); +} + +static void nvme_show_primary_ctrl_caps_crt(__u8 crt) +{ + __u8 rsvd = (crt & 0xFC) >> 2; + __u8 vi = (crt & 0x2) >> 1; + __u8 vq = crt & 0x1; + + if (rsvd) + printf(" [7:2] : %#x\tReserved\n", rsvd); + printf(" [1:1] %#x\tVI Resources are %ssupported\n", vi, vi ? "" : "not "); + printf(" [0:0] %#x\tVQ Resources are %ssupported\n", vq, vq ? "" : "not "); +} + +void nvme_show_primary_ctrl_caps(const struct nvme_primary_ctrl_caps *caps, + enum nvme_print_flags flags) +{ + int human = flags & VERBOSE; + + if (flags & BINARY) + return d_raw((unsigned char *)caps, sizeof(*caps)); + else if (flags & JSON) + return json_nvme_primary_ctrl_caps(caps); + + printf("NVME Identify Primary Controller Capabilities:\n"); + printf("cntlid : %#x\n", le16_to_cpu(caps->cntlid)); + printf("portid : %#x\n", le16_to_cpu(caps->portid)); + printf("crt : %#x\n", caps->crt); + if (human) + nvme_show_primary_ctrl_caps_crt(caps->crt); + printf("vqfrt : %d\n", le32_to_cpu(caps->vqfrt)); + printf("vqrfa : %d\n", le32_to_cpu(caps->vqrfa)); + printf("vqrfap : %d\n", le16_to_cpu(caps->vqrfap)); + printf("vqprt : %d\n", le16_to_cpu(caps->vqprt)); + printf("vqfrsm : %d\n", le16_to_cpu(caps->vqfrsm)); + printf("vqgran : %d\n", le16_to_cpu(caps->vqgran)); + printf("vifrt : %d\n", le32_to_cpu(caps->vifrt)); + printf("virfa : %d\n", le32_to_cpu(caps->virfa)); + printf("virfap : %d\n", le16_to_cpu(caps->virfap)); + printf("viprt : %d\n", le16_to_cpu(caps->viprt)); + printf("vifrsm : %d\n", le16_to_cpu(caps->vifrsm)); + printf("vigran : %d\n", le16_to_cpu(caps->vigran)); +} + static void json_nvme_list_secondary_ctrl(const struct nvme_secondary_controllers_list *sc_list, __u32 count) { const struct nvme_secondary_controller_entry *sc_entry = &sc_list->sc_entry[0]; __u32 nent = min(sc_list->num, count); - struct json_array *entries; + struct json_object *entries; struct json_object *root; int i; @@ -3011,7 +4358,7 @@ static void json_nvme_id_ns_granularity_list( { int i; struct json_object *root; - struct json_array *entries; + struct json_object *entries; root = json_create_object(); @@ -3067,7 +4414,7 @@ void nvme_show_id_ns_granularity_list(const struct nvme_id_ns_granularity_list * static void json_nvme_id_uuid_list(const struct nvme_id_uuid_list *uuid_list) { struct json_object *root; - struct json_array *entries; + struct json_object *entries; int i; root = json_create_object(); @@ -3140,9 +4487,19 @@ void nvme_show_id_uuid_list(const struct nvme_id_uuid_list *uuid_list, } } +void nvme_show_id_iocs(struct nvme_id_iocs *iocs) +{ + __u16 i; + + for (i = 0; i < 512; i++) + if (iocs->iocs[i]) + printf("I/O Command Set Combination[%u]:%"PRIx64"\n", i, + (uint64_t)le64_to_cpu(iocs->iocs[i])); +} + static const char *nvme_trtype_to_string(__u8 trtype) { - switch(trtype) { + switch (trtype) { case 0: return "The transport type is not indicated or the error "\ "is not transport related."; case 1: return "RDMA Transport error."; @@ -3174,9 +4531,11 @@ void nvme_show_error_log(struct nvme_error_log_page *err_log, int entries, le64_to_cpu(err_log[i].error_count)); printf("sqid : %d\n", err_log[i].sqid); printf("cmdid : %#x\n", err_log[i].cmdid); - printf("status_field : %#x(%s)\n", err_log[i].status_field, + printf("status_field : %#x(%s)\n", err_log[i].status_field >> 0x1, nvme_status_to_string( le16_to_cpu(err_log[i].status_field) >> 1)); + printf("phase_tag : %#x\n", + le16_to_cpu(err_log[i].status_field & 0x1)); printf("parm_err_loc : %#x\n", err_log[i].parm_error_location); printf("lba : %#"PRIx64"\n", @@ -3503,7 +4862,7 @@ void nvme_show_ana_log(struct nvme_ana_rsp_hdr *ana_log, const char *devname, else if (flags & JSON) return json_ana_log(ana_log, devname); - printf("Asynchronous Namespace Access Log for NVMe device: %s\n", + printf("Asymmetric Namespace Access Log for NVMe device: %s\n", devname); printf("ANA LOG HEADER :-\n"); printf("chgcnt : %"PRIu64"\n", @@ -3608,20 +4967,22 @@ static void nvme_show_self_test_result(struct nvme_self_test_res *res, res->vs[0], res->vs[1]); } -void nvme_show_self_test_log(struct nvme_self_test_log *self_test, const char *devname, - enum nvme_print_flags flags) +void nvme_show_self_test_log(struct nvme_self_test_log *self_test, __u8 dst_entries, + __u32 size, const char *devname, enum nvme_print_flags flags) { int i; + __u8 num_entries; if (flags & BINARY) - return d_raw((unsigned char *)self_test, sizeof(*self_test)); + return d_raw((unsigned char *)self_test, size); if (flags & JSON) - return json_self_test_log(self_test); + return json_self_test_log(self_test, dst_entries); printf("Device Self Test Log for NVME device:%s\n", devname); printf("Current operation : %#x\n", self_test->crnt_dev_selftest_oprn); printf("Current Completion : %u%%\n", self_test->crnt_dev_selftest_compln); - for (i = 0; i < NVME_ST_REPORTS; i++) { + num_entries = min(dst_entries, NVME_ST_REPORTS); + for (i = 0; i < num_entries; i++) { printf("Self Test Result[%d]:\n", i); nvme_show_self_test_result(&self_test->result[i], flags); } @@ -3704,9 +5065,10 @@ void nvme_show_sanitize_log(struct nvme_sanitize_log_page *sanitize, le32_to_cpu(sanitize->est_crypto_erase_time_with_no_deallocate)); } -const char *nvme_feature_to_string(int feature) +const char *nvme_feature_to_string(enum nvme_feat feature) { switch (feature) { + case NVME_FEAT_NONE: return "Reserved"; case NVME_FEAT_ARBITRATION: return "Arbitration"; case NVME_FEAT_POWER_MGMT: return "Power Management"; case NVME_FEAT_LBA_RANGE: return "LBA Range Type"; @@ -3725,6 +5087,9 @@ const char *nvme_feature_to_string(int feature) case NVME_FEAT_RRL: return "Read Recovery Level"; case NVME_FEAT_PLM_CONFIG: return "Predicatable Latency Mode Config"; case NVME_FEAT_PLM_WINDOW: return "Predicatable Latency Mode Window"; + case NVME_LBA_STATUS_INFO: return "LBA Status Infomation Attributes"; + case NVME_FEAT_ENDURANCE: return "Enduarance Event Group Configuration"; + case NVME_FEAT_IOCS_PROFILE: return "I/O Command Set Profile"; case NVME_FEAT_SW_PROGRESS: return "Software Progress"; case NVME_FEAT_HOST_ID: return "Host Identifier"; case NVME_FEAT_RESV_MASK: return "Reservation Notification Mask"; @@ -3734,8 +5099,13 @@ const char *nvme_feature_to_string(int feature) case NVME_FEAT_HCTM: return "Host Controlled Thermal Management"; case NVME_FEAT_HOST_BEHAVIOR: return "Host Behavior"; case NVME_FEAT_SANITIZE: return "Sanitize"; - default: return "Unknown"; } + /* + * We don't use the "default:" statement to let the compiler warning if + * some values of the enum nvme_feat are missing in the switch(). + * The following return is acting as the default: statement. + */ + return "Unknown"; } const char *nvme_register_to_string(int reg) @@ -3778,9 +5148,9 @@ void nvme_show_select_result(__u32 result) printf(" Feature is changeable\n"); } -const char *nvme_status_to_string(__u32 status) +const char *nvme_status_to_string(__u16 status) { - switch (status & 0x3ff) { + switch (status & 0x7ff) { case NVME_SC_SUCCESS: return "SUCCESS: The command completed successfully"; case NVME_SC_INVALID_OPCODE: @@ -3839,6 +5209,14 @@ const char *nvme_status_to_string(__u32 status) return "SANITIZE_FAILED: The most recent sanitize operation failed and no recovery actions has been successfully completed"; case NVME_SC_SANITIZE_IN_PROGRESS: return "SANITIZE_IN_PROGRESS: The requested function is prohibited while a sanitize operation is in progress"; + case NVME_SC_IOCS_NOT_SUPPORTED: + return "IOCS_NOT_SUPPORTED: The I/O command set is not supported"; + case NVME_SC_IOCS_NOT_ENABLED: + return "IOCS_NOT_ENABLED: The I/O command set is not enabled"; + case NVME_SC_IOCS_COMBINATION_REJECTED: + return "IOCS_COMBINATION_REJECTED: The I/O command set combination is rejected"; + case NVME_SC_INVALID_IOCS: + return "INVALID_IOCS: the I/O command set is invalid"; case NVME_SC_LBA_RANGE: return "LBA_RANGE: The command references a LBA that exceeds the size of the namespace"; case NVME_SC_NS_WRITE_PROTECTED: @@ -3853,6 +5231,22 @@ const char *nvme_status_to_string(__u32 status) return "RESERVATION_CONFLICT: The command was aborted due to a conflict with a reservation held on the accessed namespace"; case NVME_SC_FORMAT_IN_PROGRESS: return "FORMAT_IN_PROGRESS: A Format NVM command is in progress on the namespace."; + case NVME_SC_ZONE_BOUNDARY_ERROR: + return "ZONE_BOUNDARY_ERROR: Invalid Zone Boundary crossing"; + case NVME_SC_ZONE_IS_FULL: + return "ZONE_IS_FULL: The accessed zone is in ZSF:Full state"; + case NVME_SC_ZONE_IS_READ_ONLY: + return "ZONE_IS_READ_ONLY: The accessed zone is in ZSRO:Read Only state"; + case NVME_SC_ZONE_IS_OFFLINE: + return "ZONE_IS_OFFLINE: The access zone is in ZSO:Offline state"; + case NVME_SC_ZONE_INVALID_WRITE: + return "ZONE_INVALID_WRITE: The write to zone was not at the write pointer offset"; + case NVME_SC_TOO_MANY_ACTIVE_ZONES: + return "TOO_MANY_ACTIVE_ZONES: The controller does not allow additional active zones"; + case NVME_SC_TOO_MANY_OPEN_ZONES: + return "TOO_MANY_OPEN_ZONES: The controller does not allow additional open zones"; + case NVME_SC_ZONE_INVALID_STATE_TRANSITION: + return "INVALID_ZONE_STATE_TRANSITION: The zone state change was invalid"; case NVME_SC_CQ_INVALID: return "CQ_INVALID: The Completion Queue identifier specified in the command does not exist"; case NVME_SC_QID_INVALID: @@ -3927,6 +5321,12 @@ const char *nvme_status_to_string(__u32 status) return "ANA_ATTACH_FAIL: The controller is not attached to the namespace as a result of an ANA condition"; case NVME_SC_BAD_ATTRIBUTES: return "BAD_ATTRIBUTES: Bad attributes were given"; + case NVME_SC_INVALID_PI: + return "INVALID_PROTECION_INFO: The Protection Information Field settings specified in the command are invalid"; + case NVME_SC_READ_ONLY: + return "WRITE_ATTEMPT_READ_ONLY_RANGE: The LBA range specified contains read-only blocks"; + case NVME_SC_CMD_SIZE_LIMIT_EXCEEDED: + return "CMD_SIZE_LIMIT_EXCEEDED: Command size limit exceeded"; case NVME_SC_WRITE_FAULT: return "WRITE_FAULT: The write data could not be committed to the media"; case NVME_SC_READ_ERROR: @@ -3943,12 +5343,20 @@ const char *nvme_status_to_string(__u32 status) return "ACCESS_DENIED: Access to the namespace and/or LBA range is denied due to lack of access rights"; case NVME_SC_UNWRITTEN_BLOCK: return "UNWRITTEN_BLOCK: The command failed due to an attempt to read from an LBA range containing a deallocated or unwritten logical block"; + case NVME_SC_INTERNAL_PATH_ERROR: + return "INTERNAL_PATH_ERROT: The command was not completed as the result of a controller internal error"; case NVME_SC_ANA_PERSISTENT_LOSS: return "ASYMMETRIC_NAMESPACE_ACCESS_PERSISTENT_LOSS: The requested function (e.g., command) is not able to be performed as a result of the relationship between the controller and the namespace being in the ANA Persistent Loss state"; case NVME_SC_ANA_INACCESSIBLE: return "ASYMMETRIC_NAMESPACE_ACCESS_INACCESSIBLE: The requested function (e.g., command) is not able to be performed as a result of the relationship between the controller and the namespace being in the ANA Inaccessible state"; case NVME_SC_ANA_TRANSITION: return "ASYMMETRIC_NAMESPACE_ACCESS_TRANSITION: The requested function (e.g., command) is not able to be performed as a result of the relationship between the controller and the namespace transitioning between Asymmetric Namespace Access states"; + case NVME_SC_CTRL_PATHING_ERROR: + return "CONTROLLER_PATHING_ERROR: A pathing error was detected by the controller"; + case NVME_SC_HOST_PATHING_ERROR: + return "HOST_PATHING_ERROR: A pathing error was detected by the host"; + case NVME_SC_HOST_CMD_ABORT: + return "HOST_COMMAND_ABORT: The command was aborted as a result of host action"; case NVME_SC_CMD_INTERRUPTED: return "CMD_INTERRUPTED: Command processing was interrupted and the controller is unable to successfully complete the command. The host should retry the command."; case NVME_SC_PMR_SAN_PROHIBITED: @@ -3967,7 +5375,7 @@ static const char *nvme_feature_lba_type_to_string(__u8 type) case 3: return "Cache"; case 4: return "Page / Swap file"; default: - if (type>=0x05 && type<=0x7f) + if (type >= 0x05 && type <= 0x7f) return "Reserved"; else return "Vendor Specific"; @@ -4019,8 +5427,7 @@ static const char *nvme_feature_temp_type_to_string(__u8 type) static const char *nvme_feature_temp_sel_to_string(__u8 sel) { - switch (sel) - { + switch (sel) { case 0: return "Composite Temperature"; case 1: return "Temperature Sensor 1"; case 2: return "Temperature Sensor 2"; @@ -4077,13 +5484,13 @@ static void nvme_show_timestamp(struct nvme_timestamp *ts) static void nvme_show_host_mem_buffer(struct nvme_host_mem_buffer *hmb) { printf("\tHost Memory Descriptor List Entry Count (HMDLEC): %u\n", - hmb->hmdlec); + le32_to_cpu(hmb->hmdlec)); printf("\tHost Memory Descriptor List Address (HMDLAU): 0x%x\n", - hmb->hmdlau); + le32_to_cpu(hmb->hmdlau)); printf("\tHost Memory Descriptor List Address (HMDLAL): 0x%x\n", - hmb->hmdlal); + le32_to_cpu(hmb->hmdlal)); printf("\tHost Memory Buffer Size (HSIZE): %u\n", - hmb->hsize); + le32_to_cpu(hmb->hsize)); } static void nvme_directive_show_fields(__u8 dtype, __u8 doper, @@ -4121,6 +5528,8 @@ static void nvme_directive_show_fields(__u8 dtype, __u8 doper, *(__u16 *) (field + 2)); printf("\tNVM Subsystem Streams Open (NSSO): %u\n", *(__u16 *) (field + 4)); + printf("\tNVM Subsystem Stream Capability (NSSC): %u\n", + *(__u16 *) (field + 6)); printf("\tStream Write Size (in unit of LB size) (SWS): %u\n", *(__u32 *) (field + 16)); printf("\tStream Granularity Size (in unit of SWS) (SGS): %u\n", @@ -4183,6 +5592,11 @@ static const char *nvme_plm_window(__u32 plm) } } +void nvme_show_lba_status_info(__u32 result) { + printf("\tLBA Status Information Poll Interval (LSIPI) : %u\n", (result >> 16) & 0xffff); + printf("\tLBA Status Information Report Interval (LSIRI): %u\n", result & 0xffff); +} + static void nvme_show_plm_config(struct nvme_plm_config *plmcfg) { printf("\tEnable Event :%04x\n", le16_to_cpu(plmcfg->enable_event)); @@ -4191,12 +5605,15 @@ static void nvme_show_plm_config(struct nvme_plm_config *plmcfg) printf("\tDTWIN Time Threshold :%"PRIu64"\n", le64_to_cpu(plmcfg->dtwin_time_thresh)); } -void nvme_feature_show_fields(__u32 fid, unsigned int result, unsigned char *buf) +void nvme_feature_show_fields(enum nvme_feat fid, unsigned int result, unsigned char *buf) { __u8 field; uint64_t ull; switch (fid) { + case NVME_FEAT_NONE: + printf("\tFeature Identifier Reserved\n"); + break; case NVME_FEAT_ARBITRATION: printf("\tHigh Priority Weight (HPW): %u\n", ((result & 0xff000000) >> 24) + 1); printf("\tMedium Priority Weight (MPW): %u\n", ((result & 0x00ff0000) >> 16) + 1); @@ -4247,6 +5664,10 @@ void nvme_feature_show_fields(__u32 fid, unsigned int result, unsigned char *buf printf("\tDisable Normal (DN): %s\n", (result & 0x00000001) ? "True":"False"); break; case NVME_FEAT_ASYNC_EVENT: + printf("\tEndurance Group Event Aggregate Log Change Notices: %s\n", ((result & 0x00004000) >> 14) ? "Send async event":"Do not send async event"); + printf("\tLBA Status Information Notices : %s\n", ((result & 0x00002000) >> 13) ? "Send async event":"Do not send async event"); + printf("\tPredictable Latency Event Aggregate Log Change Notices: %s\n", ((result & 0x00001000) >> 12) ? "Send async event":"Do not send async event"); + printf("\tAsymmetric Namespace Access Change Notices: %s\n", ((result & 0x00000800) >> 11) ? "Send async event":"Do not send async event"); printf("\tTelemetry Log Notices : %s\n", ((result & 0x00000400) >> 10) ? "Send async event":"Do not send async event"); printf("\tFirmware Activation Notices : %s\n", ((result & 0x00000200) >> 9) ? "Send async event":"Do not send async event"); printf("\tNamespace Attribute Notices : %s\n", ((result & 0x00000100) >> 8) ? "Send async event":"Do not send async event"); @@ -4257,7 +5678,6 @@ void nvme_feature_show_fields(__u32 fid, unsigned int result, unsigned char *buf nvme_show_auto_pst((struct nvme_auto_pst *)buf); break; case NVME_FEAT_HOST_MEM_BUF: - printf("\tMemory Return (MR): %s\n", ((result & 0x00000002) >> 1) ? "True":"False"); printf("\tEnable Host Memory (EHM): %s\n", (result & 0x00000001) ? "Enabled":"Disabled"); nvme_show_host_mem_buffer((struct nvme_host_mem_buffer *)buf); break; @@ -4271,6 +5691,16 @@ void nvme_feature_show_fields(__u32 fid, unsigned int result, unsigned char *buf case NVME_FEAT_PLM_WINDOW: printf("\tWindow Select: %s", nvme_plm_window(result)); break; + case NVME_LBA_STATUS_INFO: + nvme_show_lba_status_info(result); + break; + case NVME_FEAT_ENDURANCE: + printf("\tEndurance Group Identifier (ENDGID): %u\n", result & 0xffff); + printf("\tEndurance Group Critical Warnings : %u\n", (result >> 16) & 0xff); + break; + case NVME_FEAT_IOCS_PROFILE: + printf("\tI/O Command Set Comination Index(IOCSCI): %u\n", result & 0x1ff); + break; case NVME_FEAT_HOST_ID: ull = buf[7]; ull <<= 8; ull |= buf[6]; ull <<= 8; ull |= buf[5]; ull <<= 8; ull |= buf[4]; ull <<= 8; ull |= buf[3]; ull <<= 8; ull |= buf[2]; ull <<= 8; @@ -4304,6 +5734,12 @@ void nvme_feature_show_fields(__u32 fid, unsigned int result, unsigned char *buf case NVME_FEAT_HOST_BEHAVIOR: printf("\tHost Behavior Support: %s\n", (buf[0] & 0x1) ? "True" : "False"); break; + case NVME_FEAT_SANITIZE: + printf("\tNo-Deallocate Response Mode (NODRM) : %u\n", result & 0x1); + break; + case NVME_FEAT_RRL: + printf("\tRead Recovery Level (RRL): %u\n", result & 0xf); + break; } } @@ -4351,13 +5787,20 @@ static void nvme_show_list_item(struct nvme_namespace *n) const char *l_suffix = suffix_binary_get(&lba); char usage[128]; - char format[128]; + char format[128], path[256]; + struct stat st; + int ret; + + sprintf(path, "%s%s", n->ctrl->path, n->name); + ret = stat(path, &st); + if (ret < 0) + return; sprintf(usage,"%6.2f %2sB / %6.2f %2sB", nuse, u_suffix, nsze, s_suffix); sprintf(format,"%3.0f %2sB + %2d B", (double)lba, l_suffix, le16_to_cpu(n->ns.lbaf[(n->ns.flbas & 0x0f)].ms)); - printf("/dev/%-11s %-*.*s %-*.*s %-9d %-26s %-16s %-.*s\n", n->name, + printf("%-21s %-*.*s %-*.*s %-9d %-26s %-16s %-.*s\n", path, (int)sizeof(n->ctrl->id.sn), (int)sizeof(n->ctrl->id.sn), n->ctrl->id.sn, (int)sizeof(n->ctrl->id.mn), (int)sizeof(n->ctrl->id.mn), n->ctrl->id.mn, n->nsid, usage, format, (int)sizeof(n->ctrl->id.fr), n->ctrl->id.fr); @@ -4367,9 +5810,9 @@ static void nvme_show_simple_list(struct nvme_topology *t) { int i, j, k; - printf("%-16s %-20s %-40s %-9s %-26s %-16s %-8s\n", + printf("%-21s %-20s %-40s %-9s %-26s %-16s %-8s\n", "Node", "SN", "Model", "Namespace", "Usage", "Format", "FW Rev"); - printf("%-.16s %-.20s %-.40s %-.9s %-.26s %-.16s %-.8s\n", dash, dash, + printf("%-.21s %-.20s %-.40s %-.9s %-.26s %-.16s %-.8s\n", dash, dash, dash, dash, dash, dash, dash); for (i = 0; i < t->nr_subsystems; i++) { @@ -4409,16 +5852,28 @@ static void nvme_show_details_ns(struct nvme_namespace *n, bool ctrl) sprintf(format,"%3.0f %2sB + %2d B", (double)lba, l_suffix, le16_to_cpu(n->ns.lbaf[(n->ns.flbas & 0x0f)].ms)); - printf("%-12s %-8x %-26s %-16s ", n->name, n->nsid, usage, format); + printf("%-12s %-8d %-26s %-16s ", n->name, n->nsid, usage, format); if (ctrl) printf("%s", n->ctrl->name); else { struct nvme_subsystem *s = n->ctrl->subsys; - int i; + int i, j; + bool comma = false; - for (i = 0; i < s->nr_ctrls; i++) - printf("%s%s", i ? ", " : "", s->ctrls[i].name); + for (i = 0; i < s->nr_ctrls; i++) { + struct nvme_ctrl *c = &s->ctrls[i]; + + for (j = 0; j < c->nr_namespaces; j++) { + struct nvme_namespace *ns = &c->namespaces[j]; + + if (ns->nsid == n->nsid) { + printf("%s%s", comma ? ", " : "", + c->name); + comma = true; + } + } + } } printf("\n"); } @@ -4456,15 +5911,11 @@ static void nvme_show_detailed_list(struct nvme_topology *t) printf("%-8s %-.20s %-.40s %-.8s %-6s %-14s %-12s ", c->name, c->id.sn, c->id.mn, c->id.fr, - c->transport, c->address, s->name); + c->transport ? : "", c->address ? : "", s->name); for (k = 0; k < c->nr_namespaces; k++) { struct nvme_namespace *n = &c->namespaces[k]; - printf("%s%s", comma ? ", " : "", n->name); - comma = true; - } - for (k = 0; k < s->nr_namespaces; k++) { - struct nvme_namespace *n = &s->namespaces[k]; + printf("%s%s", comma ? ", " : "", n->name); comma = true; } @@ -4479,18 +5930,21 @@ static void nvme_show_detailed_list(struct nvme_topology *t) for (i = 0; i < t->nr_subsystems; i++) { struct nvme_subsystem *s = &t->subsystems[i]; - for (j = 0; j < s->nr_ctrls; j++) { - struct nvme_ctrl *c = &s->ctrls[j]; + if (s->nr_namespaces) { + for (j = 0; j < s->nr_namespaces; j++) { + struct nvme_namespace *n = &s->namespaces[j]; + nvme_show_details_ns(n, false); + } + } else { + for (j = 0; j < s->nr_ctrls; j++) { + struct nvme_ctrl *c = &s->ctrls[j]; - for (k = 0; k < c->nr_namespaces; k++) { - struct nvme_namespace *n = &c->namespaces[k]; - nvme_show_details_ns(n, true); + for (k = 0; k < c->nr_namespaces; k++) { + struct nvme_namespace *n = &c->namespaces[k]; + nvme_show_details_ns(n, true); + } } } - for (j = 0; j < s->nr_namespaces; j++) { - struct nvme_namespace *n = &s->namespaces[j]; - nvme_show_details_ns(n, false); - } } } @@ -4517,7 +5971,7 @@ static void json_detail_list(struct nvme_topology *t) { int i, j, k; struct json_object *root; - struct json_array *devices; + struct json_object *devices; char formatter[41] = { 0 }; root = json_create_object(); @@ -4526,7 +5980,7 @@ static void json_detail_list(struct nvme_topology *t) for (i = 0; i < t->nr_subsystems; i++) { struct nvme_subsystem *s = &t->subsystems[i]; struct json_object *subsys_attrs; - struct json_array *namespaces, *ctrls; + struct json_object *namespaces, *ctrls; subsys_attrs = json_create_object(); json_object_add_value_string(subsys_attrs, "Subsystem", s->name); @@ -4537,12 +5991,15 @@ static void json_detail_list(struct nvme_topology *t) for (j = 0; j < s->nr_ctrls; j++) { struct json_object *ctrl_attrs = json_create_object(); struct nvme_ctrl *c = &s->ctrls[j]; - struct json_array *namespaces; + struct json_object *namespaces; json_object_add_value_string(ctrl_attrs, "Controller", c->name); - json_object_add_value_string(ctrl_attrs, "Transport", c->transport); - json_object_add_value_string(ctrl_attrs, "Address", c->address); - json_object_add_value_string(ctrl_attrs, "State", c->state); + if (c->transport) + json_object_add_value_string(ctrl_attrs, "Transport", c->transport); + if (c->address) + json_object_add_value_string(ctrl_attrs, "Address", c->address); + if (c->state) + json_object_add_value_string(ctrl_attrs, "State", c->state); if (c->hostnqn) json_object_add_value_string(ctrl_attrs, "HostNQN", c->hostnqn); if (c->hostid) @@ -4596,16 +6053,21 @@ static void json_detail_list(struct nvme_topology *t) json_free_object(root); } -static void json_simple_ns(struct nvme_namespace *n, struct json_array *devices) +static void json_simple_ns(struct nvme_namespace *n, struct json_object *devices) { struct json_object *device_attrs; char formatter[41] = { 0 }; double nsze, nuse; - int index = -1; + int ret, index = -1; long long lba; char *devnode; + struct stat st; + + if (asprintf(&devnode, "%s%s", n->ctrl->path, n->name) < 0) + return; - if (asprintf(&devnode, "/dev/%s", n->name) < 0) + ret = stat(devnode, &st); + if (ret < 0) return; device_attrs = json_create_object(); @@ -4629,7 +6091,7 @@ static void json_simple_ns(struct nvme_namespace *n, struct json_array *devices) json_object_add_value_string(device_attrs, "ModelNumber", formatter); - if (index >= 0 && !strcmp(n->ctrl->transport, "pcie")) { + if (index >= 0 && n->ctrl->transport && !strcmp(n->ctrl->transport, "pcie")) { char *product = nvme_product_name(index); json_object_add_value_string(device_attrs, "ProductName", product); @@ -4658,7 +6120,7 @@ static void json_simple_ns(struct nvme_namespace *n, struct json_array *devices) static void json_simple_list(struct nvme_topology *t) { struct json_object *root; - struct json_array *devices; + struct json_object *devices; int i, j, k; root = json_create_object(); diff --git a/nvme-print.h b/nvme-print.h index 2a6f5ee..2d8ed4d 100644 --- a/nvme-print.h +++ b/nvme-print.h @@ -2,7 +2,6 @@ #define NVME_PRINT_H #include "nvme.h" -#include "util/json.h" #include void d(unsigned char *buf, int len, int width, int group); @@ -11,7 +10,7 @@ uint64_t int48_to_long(__u8 *data); void nvme_show_status(__u16 status); void nvme_show_relatives(const char *name); - +const char *nvme_cmd_to_string(int admin, __u8 opcode); void __nvme_show_id_ctrl(struct nvme_id_ctrl *ctrl, unsigned int mode, void (*vendor_show)(__u8 *vs, struct json_object *root)); void nvme_show_id_ctrl(struct nvme_id_ctrl *ctrl, unsigned int mode); @@ -26,8 +25,8 @@ void nvme_show_smart_log(struct nvme_smart_log *smart, unsigned int nsid, const char *devname, enum nvme_print_flags flags); void nvme_show_ana_log(struct nvme_ana_rsp_hdr *ana_log, const char *devname, enum nvme_print_flags flags, size_t len); -void nvme_show_self_test_log(struct nvme_self_test_log *self_test, const char *devname, - enum nvme_print_flags flags); +void nvme_show_self_test_log(struct nvme_self_test_log *self_test, __u8 dst_entries, + __u32 size, const char *devname, enum nvme_print_flags flags); void nvme_show_fw_log(struct nvme_firmware_log_page *fw_log, const char *devname, enum nvme_print_flags flags); void nvme_show_effects_log(struct nvme_effects_log_page *effects, unsigned int flags); @@ -37,6 +36,35 @@ void nvme_show_endurance_log(struct nvme_endurance_group_log *endurance_log, __u16 group_id, const char *devname, enum nvme_print_flags flags); void nvme_show_sanitize_log(struct nvme_sanitize_log_page *sanitize, const char *devname, enum nvme_print_flags flags); +void json_predictable_latency_per_nvmset( + struct nvme_predlat_per_nvmset_log_page *plpns_log, + __u16 nvmset_id); +void nvme_show_predictable_latency_per_nvmset( + struct nvme_predlat_per_nvmset_log_page *plpns_log, + __u16 nvmset_id, const char *devname, enum nvme_print_flags flags); +void json_predictable_latency_event_agg_log( + struct nvme_event_agg_log_page *pea_log, + __u64 log_entries); +void nvme_show_predictable_latency_event_agg_log( + struct nvme_event_agg_log_page *pea_log, + __u64 log_entries, __u32 size, const char *devname, + enum nvme_print_flags flags); +void json_persistent_event_log(void *pevent_log_info, __u32 size); +void nvme_show_persistent_event_log(void *pevent_log_info, + __u8 action, __u32 size, const char *devname, + enum nvme_print_flags flags); +void json_endurance_group_event_agg_log( + struct nvme_event_agg_log_page *endurance_log, + __u64 log_entries); +void nvme_show_endurance_group_event_agg_log( + struct nvme_event_agg_log_page *endurance_log, + __u64 log_entries, __u32 size, const char *devname, + enum nvme_print_flags flags); +void json_lba_status_log(void *lba_status); +void nvme_show_lba_status_log(void *lba_status, __u32 size, + const char *devname, enum nvme_print_flags flags); +void nvme_show_resv_notif_log(struct nvme_resv_notif_log *resv, + const char *devname, enum nvme_print_flags flags); void nvme_show_ctrl_registers(void *bar, bool fabrics, enum nvme_print_flags flags); void nvme_show_single_property(int offset, uint64_t prop, int human); void nvme_show_id_ns_descs(void *data, unsigned nsid, enum nvme_print_flags flags); @@ -47,21 +75,35 @@ void nvme_show_subsystem_list(struct nvme_topology *t, enum nvme_print_flags flags); void nvme_show_id_nvmset(struct nvme_id_nvmset *nvmset, unsigned nvmset_id, enum nvme_print_flags flags); +void nvme_show_primary_ctrl_caps(const struct nvme_primary_ctrl_caps *caps, + enum nvme_print_flags flags); void nvme_show_list_secondary_ctrl(const struct nvme_secondary_controllers_list *sc_list, __u32 count, enum nvme_print_flags flags); void nvme_show_id_ns_granularity_list(const struct nvme_id_ns_granularity_list *glist, enum nvme_print_flags flags); void nvme_show_id_uuid_list(const struct nvme_id_uuid_list *uuid_list, enum nvme_print_flags flags); +void nvme_show_id_iocs(struct nvme_id_iocs *iocs); -void nvme_feature_show_fields(__u32 fid, unsigned int result, unsigned char *buf); +void nvme_feature_show_fields(enum nvme_feat fid, unsigned int result, unsigned char *buf); void nvme_directive_show(__u8 type, __u8 oper, __u16 spec, __u32 nsid, __u32 result, void *buf, __u32 len, enum nvme_print_flags flags); void nvme_show_select_result(__u32 result); +void nvme_show_lba_status_info(__u32 result); + +void nvme_show_zns_id_ctrl(struct nvme_zns_id_ctrl *ctrl, unsigned int mode); +void nvme_show_id_ctrl_nvm(struct nvme_id_ctrl_nvm *ctrl_nvm, + enum nvme_print_flags flags); +void nvme_show_zns_id_ns(struct nvme_zns_id_ns *ns, + struct nvme_id_ns *id_ns, unsigned long flags); +void nvme_show_zns_changed( struct nvme_zns_changed_zone_log *log, + unsigned long flags); +void nvme_show_zns_report_zones(void *report, __u32 descs, + __u8 ext_size, __u32 report_size, unsigned long flags); -const char *nvme_status_to_string(__u32 status); +const char *nvme_status_to_string(__u16 status); const char *nvme_select_to_string(int sel); -const char *nvme_feature_to_string(int feature); +const char *nvme_feature_to_string(enum nvme_feat feature); const char *nvme_register_to_string(int reg); #endif diff --git a/nvme-rpmb.c b/nvme-rpmb.c new file mode 100644 index 0000000..0e11ef3 --- /dev/null +++ b/nvme-rpmb.c @@ -0,0 +1,1001 @@ +/* + * Copyright (C) 2020 Micron Techology Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * nvme-rpmb.c - Implementation of NVMe RPMB support commands in Nvme + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nvme.h" +#include "nvme-print.h" +#include "nvme-ioctl.h" + +#define CREATE_CMD + + +#ifndef AF_ALG +#define AF_ALG 38 +#endif +#ifndef SOL_ALG +#define SOL_ALG 279 +#endif + +#define HMAC_SHA256_ALGO_NAME "hmac(sha256)" +#define MD5_HASH_ALGO_NAME "md5" +#define HMAC_SHA256_HASH_SIZE 32 +#define MD5_HASH_HASH_SIZE 16 + +extern int nvme_show_id_ctrl_rpmbs(unsigned int); +/* + * Utility function to create hash value of given data (with given key) using + * given hash algorithm; this function uses kernel crypto services + */ +unsigned char *create_hash(const char *algo, + int hash_size, + unsigned char *data, + int datalen, + unsigned char *key, + int keylen) +{ + int error, infd, outfd = -1; + unsigned char *hash = NULL; + struct sockaddr_alg provider_sa = { + .salg_family = AF_ALG, + .salg_type = "hash", + .salg_name = { 0 } + }; + + /* copy algorith name */ + memcpy(provider_sa.salg_name, algo, strlen(algo)); + + /* open netlink socket connection to algorigm provider and bind */ + infd = socket(AF_ALG, SOCK_SEQPACKET, 0); + if (infd < 0) { + perror("socket"); + return hash; + } + error = bind(infd, (struct sockaddr *)&provider_sa, sizeof(provider_sa)); + if (error < 0) { + perror("bind"); + goto out; + } + + /* if algorithm requires key, set it first - empty keys not accepted !*/ + if (key != NULL && keylen > 0) { + error = setsockopt(infd, SOL_ALG, ALG_SET_KEY, key, keylen); + if (error < 0) { + perror("setsockopt"); + goto out; + } + } + + /* now send data to hash */ + outfd = accept(infd, NULL, 0); + if (outfd < 0) { + perror("accept"); + goto out; + } + error = send(outfd, data, datalen, 0); + if (error < 0) { + perror("send"); + goto out; + } + + /* read computed hash */ + hash = (unsigned char *)calloc(hash_size, 1); + if (hash == NULL) { + perror("calloc"); + goto out; + } + + error = read(outfd, hash, hash_size); + if (error != hash_size) { + perror("read"); + free(hash); + hash = NULL; + } +out: + if (outfd > 0) close(outfd); + if (infd > 0) close(infd); + + return hash; +} + +/* Function that computes hmac-sha256 hash of given data and key pair. Returns + * byte stream (non-null terminated) upon success, NULL otherwise. + */ +unsigned char * +hmac_sha256(unsigned char *data, int datalen, unsigned char *key, int keylen) +{ + return create_hash(HMAC_SHA256_ALGO_NAME, + HMAC_SHA256_HASH_SIZE, + data, + datalen, + key, + keylen); +} + +/* Function that computes md5 of given buffer - md5 hash is used as nonce + * Returns byte stream (non-null terminated) upon success, NULL otherwise. + */ +unsigned char * +rpmb_md5(unsigned char *data, int datalen) +{ + return create_hash(MD5_HASH_ALGO_NAME, + MD5_HASH_HASH_SIZE, + data, + datalen, + NULL, + 0); +} + +/* Read data from given file into buffer and return its length */ +static int read_file(const char *file, unsigned char **data, unsigned int *len) +{ + struct stat sb; + size_t size; + unsigned char *buf = NULL; + int fd; + int err = -EINVAL; + + if (file == NULL) return err; + + if ((fd = open(file, O_RDONLY)) < 0) { + fprintf(stderr, "Failed to open %s: %s\n", file, strerror(errno)); + return fd; + } + + err = fstat(fd, &sb); + if (err < 0) { + perror("fstat"); + goto out; + } + + size = sb.st_size; + if (posix_memalign((void **)&buf, getpagesize(), size)) { + fprintf(stderr, "No memory for reading file :%s\n", file); + err = -ENOMEM; + goto out; + } + + err = read(fd, buf, size); + if (err < 0) { + err = -errno; + fprintf(stderr, "Failed to read data from file" + " %s with %s\n", file, strerror(errno)); + free(buf); + goto out; + } + err -= size; + *data = buf; + *len = size; +out: + close(fd); + return err; +} + +/* Write given buffer data to specified file */ +static void write_file(unsigned char *data, size_t len, const char *dir, + const char *file, const char *msg) +{ + char temp_folder[PATH_MAX] = { 0 }; + FILE *fp = NULL; + + if (dir != NULL) + sprintf(temp_folder, "%s/%s", dir, file); + else + sprintf(temp_folder, "./%s", file); + + if ((fp = fopen(temp_folder, "ab+")) != NULL) { + if (fwrite(data, 1, len, fp) != len) { + fprintf(stderr, "Failed to write %s data to %s\n", + msg ? msg : "", temp_folder); + } + fclose(fp); + } else { + fprintf(stderr, "Failed to open %s file to write %s\n", + temp_folder, msg ? msg : ""); + } +} + +/* Various definitions used in RPMB related support */ +enum rpmb_request_type { + RPMB_REQ_AUTH_KEY_PROGRAM = 0x0001, + RPMB_REQ_READ_WRITE_CNTR = 0x0002, + RPMB_REQ_AUTH_DATA_WRITE = 0x0003, + RPMB_REQ_AUTH_DATA_READ = 0x0004, + RPMB_REQ_READ_RESULT = 0x0005, + RPMB_REQ_AUTH_DCB_WRITE = 0x0006, + RPMB_REQ_AUTH_DCB_READ = 0x0007 +}; + +enum rpmb_response_type { + RPMB_RSP_AUTH_KEY_PROGRAM = (RPMB_REQ_AUTH_KEY_PROGRAM << 8), + RPMB_RSP_READ_WRITE_CNTR = (RPMB_REQ_READ_WRITE_CNTR << 8), + RPMB_RSP_AUTH_DATA_WRITE = (RPMB_REQ_AUTH_DATA_WRITE << 8), + RPMB_RSP_AUTH_DATA_READ = (RPMB_REQ_AUTH_DATA_READ << 8), + RPMB_RSP_READ_RESULT = (RPMB_REQ_READ_RESULT << 8), + RPMB_RSP_AUTH_DCB_WRITE = (RPMB_REQ_AUTH_DCB_WRITE << 8), + RPMB_RSP_AUTH_DCB_READ = (RPMB_REQ_AUTH_DCB_READ << 8) +}; + +/* RPMB data frame structure */ +#pragma pack(1) +struct rpmb_data_frame_t { + unsigned char pad[191]; + unsigned char mac[32]; + unsigned char target; /* 0-6, should match with NSSF with SS, SR */ + unsigned char nonce[16]; + unsigned int write_counter; + unsigned int address; + unsigned int sectors; + unsigned short result; + unsigned short type; /* req or response */ + unsigned char data[0]; /* in sector count times */ +}; +#pragma pack() + +struct rpmb_config_block_t { + unsigned char bp_enable; + unsigned char bp_lock; + unsigned char rsvd[510]; +}; + +#define RPMB_DATA_FRAME_SIZE 256 +#define RPMB_NVME_SECP 0xEA +#define RPMB_NVME_SPSP 0x0001 + +#define SEND_RPMB_REQ(tgt, size, req) \ +nvme_sec_send(fd, 0, tgt, RPMB_NVME_SPSP, RPMB_NVME_SECP, size, size, \ + (unsigned char *)(req)) + +#define RECV_RPMB_RSP(tgt, size, rsp) \ +nvme_sec_recv(fd, 0, tgt, RPMB_NVME_SPSP, RPMB_NVME_SECP, size, size, \ + (unsigned char *)(rsp)) + +/* Initialize nonce value in rpmb request frame */ +static void rpmb_nonce_init(struct rpmb_data_frame_t *req) +{ + int num = rand(); + unsigned char *hash = rpmb_md5((unsigned char *)&num, sizeof(num)); + if (hash) memcpy(req->nonce, hash, sizeof(req->nonce)); +} + +/* Read key from a given key buffer or key file */ +static unsigned char *read_rpmb_key(char *keystr, char *keyfile, unsigned int *keysize) +{ + unsigned char *keybuf = NULL; + + if (keystr == NULL) { + if (keyfile != NULL) + read_file(keyfile, &keybuf, keysize); + } else if ((keybuf = (unsigned char *)malloc(strlen(keystr))) != NULL) { + *keysize = strlen(keystr); + memcpy(keybuf, keystr, *keysize); + } + + return keybuf; +} + +/* Initialize RPMB request frame with given values */ +static struct rpmb_data_frame_t * +rpmb_request_init(unsigned int req_size, + unsigned short type, + unsigned char target, + unsigned char nonce, + unsigned int addr, + unsigned int sectors, + unsigned char *data, + unsigned short data_offset, + unsigned int data_size) +{ + struct rpmb_data_frame_t *req = NULL; + + if ((req = (struct rpmb_data_frame_t *)calloc(req_size, 1)) == NULL) { + fprintf(stderr, "Memory allocation failed for request 0x%04x\n", + type); + return req; + } + + req->type = type; + req->target = target; + req->address = addr; + req->sectors = sectors; + + if (nonce) rpmb_nonce_init(req); + if (data) memcpy((unsigned char *)req + data_offset, data, data_size); + + return req; +} + +/* Process rpmb response and print appropriate error message */ +static int check_rpmb_response( struct rpmb_data_frame_t *req, + struct rpmb_data_frame_t *rsp, + char *msg) +{ + const char *rpmb_result_string [] = { + "Operation successful", + "General failure", + "Authentication (MAC) failure", + "Counter failure (not matching/incrementing failure)", + "Address failure (out of range or wrong alignment)", + "Write (data/counter/result) failure", + "Read (data/counter/result) failure", + "Authentication key not yet programmed", + "Invalid device configuration block", + "Unknown error" + }; + + /* check error status before comparing nonce and mac */ + if (rsp->result != 0) { + if (rsp->type != ((req->type << 8) & 0xFF00)) { + fprintf(stderr, "%s ! non-matching response 0x%04x for" + " 0x%04x\n", msg, rsp->type, req->type); + } else if ((rsp->result & 0x80) == 0x80) { + fprintf(stderr, "%s ! Expired write-counter !\n", msg); + } else if (rsp->result) { + fprintf(stderr, "%s ! %s\n", msg, + rpmb_result_string[rsp->result & 0x7F]); + } else if (memcmp(req->nonce, rsp->nonce, 16)) { + fprintf(stderr, "%s ! non-matching nonce\n", msg); + } else if (memcmp(req->mac, rsp->mac, 32)) { + fprintf(stderr, "%s ! non-matching MAC\n", msg); + } else if ((req->write_counter + 1) != rsp->write_counter) { + fprintf(stderr, "%s ! out-of-sync write-counters\n", msg); + } + } + + return (int)(rsp->result); +} + +/* send an initialized rpmb request to the controller and read its response + * expected response size give in 'rsp_size'. returns response buffer upon + * successful completion (caller must free), NULL otherwise + */ +static struct rpmb_data_frame_t * +rpmb_read_request(int fd, + struct rpmb_data_frame_t *req, + int req_size, + int rsp_size) +{ + struct rpmb_data_frame_t *rsp = NULL; + unsigned char msg[1024] = { 0 }; + int error; + + sprintf((char *)msg, "RPMB request 0x%04x to target 0x%x", + req->type, req->target); + + error = SEND_RPMB_REQ(req->target, req_size, req); + if (error != 0) { + fprintf(stderr, "%s failed with error = 0x%x\n", + msg, error); + goto error_out; + } + + /* read the result back */ + rsp = (struct rpmb_data_frame_t *)calloc(rsp_size, 1); + if (rsp == NULL) { + fprintf(stderr, "memory alloc failed for %s\n", msg); + goto error_out; + } + + /* Read result of previous request */ + error = RECV_RPMB_RSP(req->target, rsp_size, rsp); + if (error) { + fprintf(stderr, "error 0x%x receiving response for %s\n", + error, msg); + goto error_out; + } + + /* validate response buffer - match target, nonce, and mac */ + error = check_rpmb_response(req, rsp, (char *)msg); + if (error == 0) return rsp; + +error_out: + free(rsp); + return NULL; +} + +/* read current write counter value from controller */ +static int rpmb_read_write_counter(int fd, + unsigned char target, + unsigned int *counter) +{ + int error = -1; + int req_size = sizeof(struct rpmb_data_frame_t); + struct rpmb_data_frame_t *req = NULL; + struct rpmb_data_frame_t *rsp = NULL; + + req = rpmb_request_init(req_size, RPMB_REQ_READ_WRITE_CNTR, + target, 1, 0, 0, NULL, 0, 0); + if (req == NULL) goto out; + if ((rsp = rpmb_read_request(fd, req, req_size, req_size)) == NULL) { + goto out; + } + *counter = rsp->write_counter; + error = 0; + +out: + free(req); + free(rsp); + return error; +} + +/* Read current device configuration block into specified buffer. It also returns + * current write counter value returned as part of response, in case of error it + * returns 0 + */ +static unsigned int rpmb_read_config_block(int fd, unsigned char **config_buf) +{ + int req_size = sizeof(struct rpmb_data_frame_t); + int cfg_size = sizeof(struct rpmb_config_block_t); + int rsp_size = req_size + cfg_size; + + struct rpmb_data_frame_t *req = NULL; + struct rpmb_data_frame_t *rsp = NULL; + struct rpmb_config_block_t *cfg = NULL; + unsigned int retval = 0; + + /* initialize request with nonce, no data on input */ + req = rpmb_request_init(req_size, RPMB_REQ_AUTH_DCB_READ, 0, 1, 0, 1, + 0, 0, 0); + if ((req == NULL) || + (rsp = rpmb_read_request(fd, req, req_size, rsp_size)) == NULL) + { + goto out; + } + + /* copy configuration data to be sent back to caller */ + cfg = (struct rpmb_config_block_t *)calloc(cfg_size, 1); + if (cfg == NULL) { + fprintf(stderr, "failed to allocate RPMB config buffer\n"); + goto out; + } + + memcpy(cfg, rsp->data, cfg_size); + *config_buf = (unsigned char *)cfg; + cfg = NULL; + retval = rsp->write_counter; +out: + free(req); + free(rsp); + free(cfg); + return retval; +} + + +static int rpmb_auth_data_read(int fd, unsigned char target, + unsigned int offset, + unsigned char **msg_buf, + int msg_size, int acc_size) +{ + struct rpmb_data_frame_t *req = NULL; + struct rpmb_data_frame_t *rsp = NULL; + int req_size = sizeof(struct rpmb_data_frame_t); + int chunk_size = (acc_size < msg_size) ? acc_size : msg_size; + int xfer = chunk_size; + unsigned char *bufp = (unsigned char *)malloc(msg_size * 512); + unsigned char *tbufp = bufp; + int data_size, rsp_size; + int error = -1; + + if (bufp == NULL) { + fprintf(stderr, "Failed to allocated memory for read-data req\n"); + goto out; + } + + while (xfer > 0) { + rsp_size = req_size + xfer * 512; + req = rpmb_request_init(req_size, RPMB_REQ_AUTH_DATA_READ, + target, 1, offset, xfer, 0, 0, 0); + if (req == NULL) break; + if ((rsp = rpmb_read_request(fd, req, req_size, rsp_size)) == NULL) + { + fprintf(stderr, "read_request failed\n"); + free(rsp); + goto out; + } + + data_size = rsp->sectors * 512; + memcpy(tbufp, rsp->data, data_size); + offset += rsp->sectors; + tbufp += data_size; + if (offset + chunk_size > msg_size) + xfer = msg_size - offset; + else + xfer = chunk_size; + free(req); + free(rsp); + } + + *msg_buf = bufp; + error = offset; +out: + return error; +} + +/* Implementation of programming authentication key to given RPMB target */ +static int rpmb_program_auth_key(int fd, unsigned char target, + unsigned char *key_buf, int key_size) +{ + int req_size = sizeof(struct rpmb_data_frame_t); + int rsp_size = sizeof(struct rpmb_data_frame_t); + + struct rpmb_data_frame_t *req = NULL; + struct rpmb_data_frame_t *rsp = NULL; + + int err = -ENOMEM; + + req = rpmb_request_init(req_size, RPMB_REQ_AUTH_KEY_PROGRAM, target, + 0, 0, 0, key_buf, (223 - key_size), key_size); + if (req == NULL) { + fprintf(stderr, "failed to allocate request buffer memory\n"); + goto out; + } + + /* send request and read the result first */ + rsp = rpmb_read_request(fd, req, req_size, rsp_size); + if (rsp == NULL || rsp->result != 0) { + goto out; + } + + /* re-use response buffer */ + memset(rsp, 0, rsp_size); + err = RECV_RPMB_RSP(req->target, rsp_size, (unsigned char *)rsp); + if (err != 0) { + err = check_rpmb_response(req, rsp, "Failed to Program Key"); + } +out: + free(req); + free(rsp); + + return err; +} + + +/* Implementation of RPMB authenticated data write command; this function + * transfers msg_size bytes from msg_buf to controller 'addr'. Returns + * number of bytes actually written to, otherwise negetive error code + * on failures. + */ +static int auth_data_write_chunk(int fd, unsigned char tgt, unsigned int addr, + unsigned char *msg_buf, int msg_size, + unsigned char *keybuf, int keysize) +{ + int req_size = sizeof(struct rpmb_data_frame_t) + msg_size; + int rsp_size = sizeof(struct rpmb_data_frame_t); + + struct rpmb_data_frame_t *req = NULL; + struct rpmb_data_frame_t *rsp = NULL; + + unsigned int write_cntr = 0; + unsigned char *mac = NULL; + int error = -ENOMEM; + + /* get current write counter and copy to the request */ + error = rpmb_read_write_counter(fd, tgt, &write_cntr); + if (error != 0) { + fprintf(stderr, "Failed to read write counter for write-data\n"); + goto out; + } + + req = rpmb_request_init(req_size, RPMB_REQ_AUTH_DATA_WRITE, tgt, 0, + addr, (msg_size / 512), msg_buf, + offsetof(struct rpmb_data_frame_t, data), msg_size); + if (req == NULL) { + fprintf(stderr, "Memory alloc failed for write-data command\n"); + goto out; + } + + req->write_counter = write_cntr; + + /* compute HMAC hash */ + mac = hmac_sha256(((unsigned char *)req + 223), req_size - 223, + keybuf, keysize); + if (mac == NULL) { + fprintf(stderr, "failed to compute HMAC-SHA256\n"); + error = -1; + goto out; + } + + memcpy(req->mac, mac, 32); + + /* send the request and get response */ + error = SEND_RPMB_REQ(tgt, req_size, (unsigned char *)req); + if (error != 0) { + fprintf(stderr, "RPMB request 0x%04x for 0x%x, error: %d\n", + req->type, tgt, error); + goto out; + } + + /* send the request to get the result and then request to get the response */ + rsp = (struct rpmb_data_frame_t *)calloc(rsp_size, 1); + rsp->target = req->target; + rsp->type = RPMB_REQ_READ_RESULT; + error = SEND_RPMB_REQ(tgt, rsp_size, (unsigned char *)rsp); + if (error != 0 || rsp->result != 0) { + fprintf(stderr, "Write-data read result 0x%x, error = 0x%x\n", + rsp->result, error); + goto out; + } + + /* Read final response */ + memset(rsp, 0, rsp_size); + error = RECV_RPMB_RSP(tgt, rsp_size, (unsigned char *)rsp); + if (error != 0) + fprintf(stderr, "Auth data write recv error = 0x%x\n", error); + else + error = check_rpmb_response(req, rsp, "Failed to write-data"); +out: + free(req); + free(rsp); + free(mac); + + return error; +} + +/* send the request and get response */ +static int rpmb_auth_data_write(int fd, unsigned char target, + unsigned int addr, int acc_size, + unsigned char *msg_buf, int msg_size, + unsigned char *keybuf, int keysize) +{ + int chunk_size = acc_size < msg_size ? acc_size : msg_size; + int xfer = chunk_size; + int offset = 0; + + while (xfer > 0 ) { + if (auth_data_write_chunk(fd, target, (addr + offset / 512), + msg_buf + offset, xfer, + keybuf, keysize) != 0) + { + /* error writing chunk data */ + break; + } + + offset += xfer; + if (offset + chunk_size > msg_size) + xfer = msg_size - offset; + else + xfer = chunk_size; + } + + return offset; +} + +/* writes given config_block buffer to the drive target 0 */ +static int rpmb_write_config_block(int fd, unsigned char *cfg_buf, + unsigned char *keybuf, int keysize) +{ + int cfg_size = sizeof(struct rpmb_config_block_t); + int rsp_size = sizeof(struct rpmb_data_frame_t); + int req_size = rsp_size + cfg_size; + + struct rpmb_data_frame_t *req = NULL; + struct rpmb_data_frame_t *rsp = NULL; + unsigned char *cfg_buf_read = NULL, *mac = NULL; + unsigned int write_cntr = 0; + int error = -ENOMEM; + + /* initialize request */ + req = rpmb_request_init(req_size, RPMB_REQ_AUTH_DCB_WRITE, 0, 0, 0, 1, + cfg_buf, offsetof(struct rpmb_data_frame_t, data), + cfg_size); + if (req == NULL) { + fprintf(stderr, "failed to allocate rpmb request buffer\n"); + goto out; + } + + /* read config block write_counter from controller */ + write_cntr = rpmb_read_config_block(fd, &cfg_buf_read); + if (cfg_buf_read == NULL) { + fprintf(stderr, "failed to read config block write counter\n"); + error = -EIO; + goto out; + } + + free(cfg_buf_read); + req->write_counter = write_cntr; + mac = hmac_sha256(((unsigned char *)req + 223), req_size - 223, + keybuf, keysize); + if (mac == NULL) { + fprintf(stderr, "failed to compute hmac-sha256 hash\n"); + error = -EINVAL; + goto out; + } + + memcpy(req->mac, mac, sizeof(req->mac)); + + error = SEND_RPMB_REQ(0, req_size, (unsigned char *)req); + if (error != 0) { + fprintf(stderr, "Write-config RPMB request, error = 0x%x\n", + error); + goto out; + } + + /* get response */ + rsp = (struct rpmb_data_frame_t *)calloc(rsp_size, 1); + if (rsp == NULL) { + fprintf(stderr, "failed to allocate response buffer memory\n"); + error = -ENOMEM; + goto out; + } + + /* get result first */ + memset(rsp, 0, rsp_size); + rsp->target = req->target; + rsp->type = RPMB_REQ_READ_RESULT; + /* get the response and validate */ + error = RECV_RPMB_RSP(req->target, rsp_size, rsp); + if (error != 0) { + fprintf(stderr,"Failed getting write-config response\ + error = 0x%x\n", error); + goto out; + } + error = check_rpmb_response(req, rsp, + "Failed to retrieve write-config response"); +out: + free(req); + free(rsp); + free(mac); + + return error; +} + +static bool invalid_xfer_size(int blocks, unsigned int bpsz) +{ + return ((blocks <= 0) || + (blocks * 512) > ((bpsz + 1) * 128 * 1024)); +} + +/* Handling rpmb sub-command */ +int rpmb_cmd_option(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Run RPMB command on the supporting controller"; + const char *msg = "data to be written on write-data or write-config commands"; + const char *mfile = "data file for read/write-data, read/write-config options"; + const char *kfile = "key file that has authentication key to be used"; + const char *target = "RPMB target - numerical value of 0 to 6, default 0"; + const char *address = "Sector offset to read from or write to for an RPMB target, default 0"; + const char *blocks = "Number of 512 blocks to read or write"; + const char *key = "key to be used for authentication"; + const char *opt = "RPMB action - info, program-key, read-counter, write-data, " \ + "read-data, write-config and read-config"; + + struct config { + char *cmd; + char *key; + char *msg; + char *keyfile; + char *msgfile; + int opt; + int address; + int blocks; + char target; + }; + + struct config cfg = { + .cmd = "info", + .key = NULL, + .msg = NULL, + .msgfile = NULL, + .keyfile = NULL, + .opt = 0, + .address = 0, + .blocks = 0, + .target = 0, + }; + + OPT_ARGS(opts) = { + OPT_STRING("cmd", 'c', "command", &cfg.cmd, opt), + OPT_STRING("msgfile", 'f', "FILE", &cfg.msgfile, mfile), + OPT_STRING("keyfile", 'g', "FILE", &cfg.keyfile, kfile), + OPT_STRING("key", 'k', "key", &cfg.key, key), + OPT_STRING("msg", 'd', "data", &cfg.msg, msg), + OPT_UINT("address", 'o', &cfg.address, address), + OPT_UINT("blocks", 'b', &cfg.blocks, blocks), + OPT_UINT("target", 't', &cfg.target, target), + OPT_END() + }; + + unsigned int write_cntr = 0; + unsigned char *key_buf = NULL; + unsigned char *msg_buf = NULL; + unsigned int msg_size = 0; + unsigned int key_size = 0; + int fd = -1, err = -1; + struct nvme_id_ctrl ctrl; + + union ctrl_rpmbs_reg { + struct { + unsigned int num_targets:3; + unsigned int auth_method:3; + unsigned int reserved:10; + unsigned int total_size:8; /* 128K units */ + unsigned int access_size:8; /* in 512 byte count */ + }; + unsigned int rpmbs; + } regs; + + if ((fd = parse_and_open(argc, argv, desc, opts)) < 0) + goto out; + + /* before parsing commands, check if controller supports any RPMB targets */ + err = nvme_identify_ctrl(fd, &ctrl); + if (err) + goto out; + + regs.rpmbs = le32_to_cpu(ctrl.rpmbs); + if (regs.num_targets == 0) { + fprintf(stderr, "No RPMB targets are supported by the drive\n"); + goto out; + } + + /* parse and validate options; default print rpmb support info */ + if (cfg.cmd == 0 || strcmp(cfg.cmd, "info") == 0) { + nvme_show_id_ctrl_rpmbs(regs.rpmbs); + goto out; + } + + if (strcmp(cfg.cmd, "program-key") == 0) + cfg.opt = RPMB_REQ_AUTH_KEY_PROGRAM; + else if (strcmp(cfg.cmd, "read-counter") == 0) + cfg.opt = RPMB_REQ_READ_WRITE_CNTR; + else if (strcmp(cfg.cmd, "write-data") == 0) + cfg.opt = RPMB_REQ_AUTH_DATA_WRITE; + else if (strcmp(cfg.cmd, "read-data") == 0) + cfg.opt = RPMB_REQ_AUTH_DATA_READ; + else if (strcmp(cfg.cmd, "write-config") == 0) + cfg.opt = RPMB_REQ_AUTH_DCB_WRITE; + else if (strcmp(cfg.cmd, "read-config") == 0) + cfg.opt = RPMB_REQ_AUTH_DCB_READ; + else { + fprintf(stderr, "Invalid option %s for rpmb command\n", cfg.cmd); + goto out; + } + + /* input file/data processing */ + if (cfg.opt == RPMB_REQ_AUTH_DCB_WRITE || + cfg.opt == RPMB_REQ_AUTH_DATA_WRITE || + cfg.opt == RPMB_REQ_AUTH_KEY_PROGRAM) + { + key_buf = read_rpmb_key(cfg.key, cfg.keyfile, &key_size); + if (key_buf == NULL) { + fprintf(stderr, "Failed to read key\n"); + goto out; + } + + if (key_size > 223 || key_size <= 0) { + fprintf(stderr, "Invalid key size %d, valid input 1 to 223\n", + key_size); + goto out; + } + + if (cfg.opt == RPMB_REQ_AUTH_DCB_WRITE || + cfg.opt == RPMB_REQ_AUTH_DATA_WRITE) { + if (cfg.msg != NULL) { + msg_size = strlen(cfg.msg); + msg_buf = (unsigned char *)malloc(msg_size); + memcpy(msg_buf, cfg.msg, msg_size); + } else { + err = read_file(cfg.msgfile, &msg_buf, &msg_size); + if (err || msg_size <= 0) { + fprintf(stderr, "Failed to read file %s\n", + cfg.msgfile); + goto out; + } + } + } + } + + switch (cfg.opt) { + case RPMB_REQ_READ_WRITE_CNTR: + err = rpmb_read_write_counter(fd, cfg.target, &write_cntr); + if (err == 0) + printf("Write Counter is: %u\n", write_cntr); + break; + + case RPMB_REQ_AUTH_DCB_READ: + write_cntr = rpmb_read_config_block(fd, &msg_buf); + if (msg_buf == NULL) { + fprintf(stderr, "failed read config blk\n"); + goto out; + } + + /* no output file is given, print the data on stdout */ + if (cfg.msgfile == 0) { + struct rpmb_config_block_t *cfg = + (struct rpmb_config_block_t *)msg_buf; + printf("Boot Parition Protection is %s\n", + ((cfg->bp_enable & 0x1) ? "Enabled" : "Disabled")); + printf("Boot Parition 1 is %s\n", + ((cfg->bp_lock & 0x2) ? "Locked" : "Unlocked")); + printf("Boot Parition 0 is %s\n", + ((cfg->bp_lock & 0x1) ? "Locked" : "Unlocked")); + } else { + printf("Saving received config data to %s file\n", cfg.msgfile); + write_file(msg_buf, sizeof(struct rpmb_config_block_t), NULL, + cfg.msgfile, NULL); + } + err = (write_cntr == 0); + break; + + case RPMB_REQ_AUTH_DATA_READ: + /* check if requested data is beyond what target supports */ + msg_size = cfg.blocks * 512; + if (invalid_xfer_size(cfg.blocks, regs.total_size)) { + fprintf(stderr, "invalid transfer size %d \n", + msg_size); + break; + } + err = rpmb_auth_data_read(fd, cfg.target, cfg.address, + &msg_buf, cfg.blocks, + (regs.access_size + 1)); + if (err > 0 && msg_buf != NULL) { + printf("Writting %d bytes to file %s\n", + err * 512, cfg.msgfile); + write_file(msg_buf, err * 512, NULL, + cfg.msgfile, NULL); + } + break; + + case RPMB_REQ_AUTH_DATA_WRITE: + if (invalid_xfer_size(cfg.blocks, regs.total_size) || + (cfg.blocks * 512) > msg_size) { + fprintf(stderr, "invalid transfer size %d\n", + cfg.blocks * 512); + break; + } else if ((cfg.blocks * 512) < msg_size) { + msg_size = cfg.blocks * 512; + } + err = rpmb_auth_data_write(fd, cfg.target, cfg.address, + ((regs.access_size + 1) * 512), + msg_buf, msg_size, + key_buf, key_size); + + /* print whatever extent of data written to target */ + printf("Written %d sectors out of %d @target(%d):0x%x\n", + err/512, msg_size/512, cfg.target, cfg.address); + break; + + case RPMB_REQ_AUTH_DCB_WRITE: + err = rpmb_write_config_block(fd, msg_buf, key_buf, key_size); + break; + + case RPMB_REQ_AUTH_KEY_PROGRAM: + err = rpmb_program_auth_key(fd, cfg.target, key_buf, key_size); + break; + default: + break; + } + +out: + /* release memory */ + free(key_buf); + free(msg_buf); + + /* close file descriptor */ + if (fd > 0) close(fd); + + return err; +} diff --git a/nvme-status.c b/nvme-status.c index 1b060dc..270eb06 100644 --- a/nvme-status.c +++ b/nvme-status.c @@ -97,7 +97,6 @@ static inline __u8 nvme_cmd_specific_status_to_errno(__u16 status) case NVME_SC_NS_ALREADY_ATTACHED: return EALREADY; case NVME_SC_THIN_PROV_NOT_SUPP: - case NVME_SC_ONCS_NOT_SUPPORTED: return EOPNOTSUPP; case NVME_SC_DEVICE_SELF_TEST_IN_PROGRESS: return EINPROGRESS; @@ -127,6 +126,22 @@ static inline __u8 nvme_fabrics_status_to_errno(__u16 status) return EIO; } +static inline __u8 nvme_path_status_to_errno(__u16 status) +{ + switch (status) { + case NVME_SC_INTERNAL_PATH_ERROR: + case NVME_SC_ANA_PERSISTENT_LOSS: + case NVME_SC_ANA_INACCESSIBLE: + case NVME_SC_ANA_TRANSITION: + case NVME_SC_CTRL_PATHING_ERROR: + case NVME_SC_HOST_PATHING_ERROR: + case NVME_SC_HOST_CMD_ABORT: + return EACCES; + } + + return EIO; +} + /* * nvme_status_to_errno - It converts given status to errno mapped * @status: >= 0 for nvme status field in completion queue entry, @@ -142,8 +157,11 @@ __u8 nvme_status_to_errno(int status, bool fabrics) if (!status) return 0; - if (status < 0) - return ECOMM; + if (status < 0) { + if (errno) + return errno; + return status; + } /* * The actual status code is enough with masking 0xff, but we need to @@ -152,12 +170,17 @@ __u8 nvme_status_to_errno(int status, bool fabrics) status &= 0x7ff; sct = nvme_status_type(status); - if (sct == NVME_SCT_GENERIC) + switch (sct) { + case NVME_SCT_GENERIC: return nvme_generic_status_to_errno(status); - else if (sct == NVME_SCT_CMD_SPECIFIC && !fabrics) - return nvme_cmd_specific_status_to_errno(status); - else if (sct == NVME_SCT_CMD_SPECIFIC && fabrics) + case NVME_SCT_CMD_SPECIFIC: + if (!fabrics) { + return nvme_cmd_specific_status_to_errno(status); + } return nvme_fabrics_status_to_errno(status); + case NVME_SCT_PATH: + return nvme_path_status_to_errno(status); + } /* * Media, integrity related status, and the others will be mapped to diff --git a/nvme-topology.c b/nvme-topology.c index d24ef6b..31cf7f9 100644 --- a/nvme-topology.c +++ b/nvme-topology.c @@ -9,8 +9,14 @@ #include "nvme.h" #include "nvme-ioctl.h" +#ifdef HAVE_SYSTEMD +#include +#define NVME_HOSTNQN_ID SD_ID128_MAKE(c7,f4,61,81,12,be,49,32,8c,83,10,6f,9d,dd,d8,6b) +#endif + static const char *dev = "/dev/"; static const char *subsys_dir = "/sys/class/nvme-subsystem/"; +static void free_ctrl(struct nvme_ctrl *c); char *get_nvme_subsnqn(char *path) { @@ -45,7 +51,7 @@ close_fd: return subsysnqn; } -char *nvme_get_ctrl_attr(char *path, const char *attr) +char *nvme_get_ctrl_attr(const char *path, const char *attr) { char *attrpath, *value; ssize_t ret; @@ -144,7 +150,7 @@ static int scan_namespace(struct nvme_namespace *n) int ret, fd; char *path; - ret = asprintf(&path, "%s%s", dev, n->name); + ret = asprintf(&path, "%s%s", n->ctrl->path, n->name); if (ret < 0) return ret; @@ -152,9 +158,11 @@ static int scan_namespace(struct nvme_namespace *n) if (fd < 0) goto free; - n->nsid = nvme_get_nsid(fd); - if (n->nsid < 0) - goto close_fd; + if (!n->nsid) { + n->nsid = nvme_get_nsid(fd); + if (n->nsid < 0) + goto close_fd; + } ret = nvme_identify_ns(fd, n->nsid, 0, &n->ns); if (ret < 0) @@ -228,6 +236,19 @@ static char *get_nvme_ctrl_path_ana_state(char *path, int nsid) return ana_state; } +static bool ns_attached_to_ctrl(int nsid, struct nvme_ctrl *ctrl) +{ + struct nvme_namespace *n; + int i; + + for (i = 0; i < ctrl->nr_namespaces; i++) { + n = &ctrl->namespaces[i]; + if (nsid == n->nsid) + return true; + } + return false; +} + static int scan_ctrl(struct nvme_ctrl *c, char *p, __u32 ns_instance) { struct nvme_namespace *n; @@ -248,7 +269,7 @@ static int scan_ctrl(struct nvme_ctrl *c, char *p, __u32 ns_instance) if (ns_instance) c->ana_state = get_nvme_ctrl_path_ana_state(path, ns_instance); - ret = scandir(path, &ns, scan_namespace_filter, alphasort); + ret = scandir(path, &ns, scan_ctrl_namespace_filter, alphasort); if (ret == -1) { fprintf(stderr, "Failed to open %s: %s\n", path, strerror(errno)); return errno; @@ -256,11 +277,36 @@ static int scan_ctrl(struct nvme_ctrl *c, char *p, __u32 ns_instance) c->nr_namespaces = ret; c->namespaces = calloc(c->nr_namespaces, sizeof(*n)); - for (i = 0; i < c->nr_namespaces; i++) { - n = &c->namespaces[i]; - n->name = strdup(ns[i]->d_name); - n->ctrl = c; - scan_namespace(n); + if (c->namespaces) { + for (i = 0; i < c->nr_namespaces; i++) { + char *ns_path, nsid[16]; + int ns_fd; + + n = &c->namespaces[i]; + n->name = strdup(ns[i]->d_name); + n->ctrl = c; + ret = asprintf(&ns_path, "%s/%s/nsid", path, n->name); + if (ret < 0) + continue; + ns_fd = open(ns_path, O_RDONLY); + if (ns_fd < 0) { + free(ns_path); + continue; + } + ret = read(ns_fd, nsid, 16); + if (ret < 0) { + close(ns_fd); + free(ns_path); + continue; + } + n->nsid = (unsigned)strtol(nsid, NULL, 10); + scan_namespace(n); + close(ns_fd); + free(ns_path); + } + } else { + i = c->nr_namespaces; + c->nr_namespaces = 0; } while (i--) @@ -268,7 +314,7 @@ static int scan_ctrl(struct nvme_ctrl *c, char *p, __u32 ns_instance) free(ns); free(path); - ret = asprintf(&path, "%s%s", dev, c->name); + ret = asprintf(&path, "%s%s", c->path, c->name); if (ret < 0) return ret; @@ -288,12 +334,12 @@ free: return 0; } -static int scan_subsystem(struct nvme_subsystem *s, __u32 ns_instance) +static int scan_subsystem(struct nvme_subsystem *s, __u32 ns_instance, int nsid) { struct dirent **ctrls, **ns; struct nvme_namespace *n; struct nvme_ctrl *c; - int i, ret; + int i, j = 0, ret; char *path; ret = asprintf(&path, "%s%s", subsys_dir, s->name); @@ -309,11 +355,18 @@ static int scan_subsystem(struct nvme_subsystem *s, __u32 ns_instance) s->nr_ctrls = ret; s->ctrls = calloc(s->nr_ctrls, sizeof(*c)); for (i = 0; i < s->nr_ctrls; i++) { - c = &s->ctrls[i]; + c = &s->ctrls[j]; c->name = strdup(ctrls[i]->d_name); + c->path = strdup(dev); c->subsys = s; scan_ctrl(c, path, ns_instance); + + if (!ns_instance || ns_attached_to_ctrl(nsid, c)) + j++; + else + free_ctrl(c); } + s->nr_ctrls = j; while (i--) free(ctrls[i]); @@ -327,11 +380,16 @@ static int scan_subsystem(struct nvme_subsystem *s, __u32 ns_instance) s->nr_namespaces = ret; s->namespaces = calloc(s->nr_namespaces, sizeof(*n)); - for (i = 0; i < s->nr_namespaces; i++) { - n = &s->namespaces[i]; - n->name = strdup(ns[i]->d_name); - n->ctrl = &s->ctrls[0]; - scan_namespace(n); + if (s->namespaces) { + for (i = 0; i < s->nr_namespaces; i++) { + n = &s->namespaces[i]; + n->name = strdup(ns[i]->d_name); + n->ctrl = &s->ctrls[0]; + scan_namespace(n); + } + } else { + i = s->nr_namespaces; + s->nr_namespaces = 0; } while (i--) @@ -349,7 +407,7 @@ static int verify_legacy_ns(struct nvme_namespace *n) char *path; int ret, fd; - ret = asprintf(&path, "%s%s", dev, n->name); + ret = asprintf(&path, "%s%s", n->ctrl->path, n->name); if (ret < 0) return ret; @@ -388,7 +446,7 @@ static int verify_legacy_ns(struct nvme_namespace *n) * is the controller to nvme0n1 for such older kernels. We will also assume * every controller is its own subsystem. */ -static int legacy_list(struct nvme_topology *t) +static int legacy_list(struct nvme_topology *t, char *dev_dir) { struct nvme_ctrl *c; struct nvme_subsystem *s; @@ -397,10 +455,10 @@ static int legacy_list(struct nvme_topology *t) int ret = 0, fd, i; char *path; - t->nr_subsystems = scandir(dev, &devices, scan_ctrls_filter, alphasort); - if (t->nr_subsystems < 0) { - fprintf(stderr, "no NVMe device(s) detected.\n"); - return t->nr_subsystems; + t->nr_subsystems = scandir(dev_dir, &devices, scan_ctrls_filter, alphasort); + if (t->nr_subsystems == -1) { + fprintf(stderr, "Failed to open %s: %s\n", dev_dir, strerror(errno)); + return errno; } t->subsystems = calloc(t->nr_subsystems, sizeof(*s)); @@ -417,11 +475,18 @@ static int legacy_list(struct nvme_topology *t) c = s->ctrls; c->name = strdup(s->name); sscanf(c->name, "nvme%d", ¤t_index); - c->nr_namespaces = scandir(dev, &namespaces, scan_dev_filter, + c->path = strdup(dev_dir); + c->nr_namespaces = scandir(c->path, &namespaces, scan_dev_filter, alphasort); c->namespaces = calloc(c->nr_namespaces, sizeof(*n)); + if (!c->namespaces) { + while (c->nr_namespaces--) + free(namespaces[c->nr_namespaces]); + free(namespaces); + continue; + } - ret = asprintf(&path, "%s%s", dev, c->name); + ret = asprintf(&path, "%s%s", c->path, c->name); if (ret < 0) continue; ret = 0; @@ -463,6 +528,7 @@ static void free_ctrl(struct nvme_ctrl *c) free(n->name); } free(c->name); + free(c->path); free(c->transport); free(c->address); free(c->state); @@ -488,35 +554,64 @@ static void free_subsystem(struct nvme_subsystem *s) free(s->namespaces); } +static int scan_subsystem_dir(struct nvme_topology *t, char *dev_dir) +{ + struct nvme_topology dev_dir_t = { }; + int ret, i, total_nr_subsystems; + + ret = legacy_list(&dev_dir_t, dev_dir); + if (ret != 0) + return ret; + + total_nr_subsystems = t->nr_subsystems + dev_dir_t.nr_subsystems; + t->subsystems = realloc(t->subsystems, + total_nr_subsystems * sizeof(struct nvme_subsystem)); + for (i = 0; i < dev_dir_t.nr_subsystems; i++){ + t->subsystems[i+t->nr_subsystems] = dev_dir_t.subsystems[i]; + } + t->nr_subsystems = total_nr_subsystems; + + return 0; +} + int scan_subsystems(struct nvme_topology *t, const char *subsysnqn, - __u32 ns_instance) + __u32 ns_instance, int nsid, char *dev_dir) { struct nvme_subsystem *s; struct dirent **subsys; - int i, j = 0; + int ret = 0, i, j = 0; t->nr_subsystems = scandir(subsys_dir, &subsys, scan_subsys_filter, alphasort); - if (t->nr_subsystems < 0) - return legacy_list(t); + if (t->nr_subsystems < 0) { + ret = legacy_list(t, (char *)dev); + if (ret != 0) + return ret; + } else { + + t->subsystems = calloc(t->nr_subsystems, sizeof(*s)); + for (i = 0; i < t->nr_subsystems; i++) { + s = &t->subsystems[j]; + s->name = strdup(subsys[i]->d_name); + scan_subsystem(s, ns_instance, nsid); + + if (!subsysnqn || !strcmp(s->subsysnqn, subsysnqn)) + j++; + else + free_subsystem(s); + } + t->nr_subsystems = j; - t->subsystems = calloc(t->nr_subsystems, sizeof(*s)); - for (i = 0; i < t->nr_subsystems; i++) { - s = &t->subsystems[j]; - s->name = strdup(subsys[i]->d_name); - scan_subsystem(s, ns_instance); + while (i--) + free(subsys[i]); + free(subsys); + } - if (!subsysnqn || !strcmp(s->subsysnqn, subsysnqn)) - j++; - else - free_subsystem(s); + if (dev_dir != NULL && strcmp(dev_dir, "/dev/")) { + ret = scan_subsystem_dir(t, dev_dir); } - t->nr_subsystems = j; - while (i--) - free(subsys[i]); - free(subsys); - return 0; + return ret; } void free_topology(struct nvme_topology *t) @@ -606,3 +701,75 @@ void *mmap_registers(const char *dev) return membase; } +#define PATH_DMI_ENTRIES "/sys/firmware/dmi/entries" + +int uuid_from_dmi(char *system_uuid) +{ + int f; + DIR *d; + struct dirent *de; + char buf[512]; + + system_uuid[0] = '\0'; + d = opendir(PATH_DMI_ENTRIES); + if (!d) + return -ENXIO; + while ((de = readdir(d))) { + char filename[PATH_MAX]; + int len, type; + + if (de->d_name[0] == '.') + continue; + sprintf(filename, "%s/%s/type", PATH_DMI_ENTRIES, de->d_name); + f = open(filename, O_RDONLY); + if (f < 0) + continue; + len = read(f, buf, 512); + close(f); + if (len < 0) + continue; + if (sscanf(buf, "%d", &type) != 1) + continue; + if (type != 1) + continue; + sprintf(filename, "%s/%s/raw", PATH_DMI_ENTRIES, de->d_name); + f = open(filename, O_RDONLY); + if (f < 0) + continue; + len = read(f, buf, 512); + close(f); + if (len < 0) + continue; + /* Sigh. https://en.wikipedia.org/wiki/Overengineering */ + /* DMTF SMBIOS 3.0 Section 7.2.1 System UUID */ + sprintf(system_uuid, + "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-" + "%02x%02x%02x%02x%02x%02x", + (uint8_t)buf[8 + 3], (uint8_t)buf[8 + 2], + (uint8_t)buf[8 + 1], (uint8_t)buf[8 + 0], + (uint8_t)buf[8 + 5], (uint8_t)buf[8 + 4], + (uint8_t)buf[8 + 7], (uint8_t)buf[8 + 6], + (uint8_t)buf[8 + 8], (uint8_t)buf[8 + 9], + (uint8_t)buf[8 + 10], (uint8_t)buf[8 + 11], + (uint8_t)buf[8 + 12], (uint8_t)buf[8 + 13], + (uint8_t)buf[8 + 14], (uint8_t)buf[8 + 15]); + break; + } + closedir(d); + return strlen(system_uuid) ? 0 : -ENXIO; +} + +int uuid_from_systemd(char *systemd_uuid) +{ +#ifdef HAVE_SYSTEMD + sd_id128_t id; + + if (sd_id128_get_machine_app_specific(NVME_HOSTNQN_ID, &id) < 0) + return -ENXIO; + + sprintf(systemd_uuid, SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(id)); + return 0; +#else + return -ENOTSUP; +#endif +} diff --git a/nvme.c b/nvme.c index 107f012..a2075bf 100644 --- a/nvme.c +++ b/nvme.c @@ -47,7 +47,6 @@ #include #include #include -#include #include "common.h" #include "nvme-print.h" @@ -85,7 +84,17 @@ static struct program nvme = { .extensions = &builtin, }; -static const char *output_format = "Output format: normal|json|binary"; +static __u16 nvme_feat_buf_len[0x100] = { + [NVME_FEAT_LBA_RANGE] = 4096, + [NVME_FEAT_AUTO_PST] = 256, + [NVME_FEAT_HOST_MEM_BUF] = 4096, + [NVME_FEAT_HOST_ID] = 8, + [NVME_FEAT_PLM_CONFIG] = 512, + [NVME_FEAT_TIMESTAMP] = 8, + [NVME_FEAT_HOST_BEHAVIOR] = 512 +}; + +const char *output_format = "Output format: normal|json|binary"; static const char *output_format_no_binary = "Output format: normal|json"; static void *__nvme_alloc(size_t len, bool *huge) @@ -100,10 +109,10 @@ static void *__nvme_alloc(size_t len, bool *huge) return NULL; } -#ifdef LIBHUGETLBFS #define HUGE_MIN 0x80000 -static void nvme_free(void *p, bool huge) +#ifdef LIBHUGETLBFS +void nvme_free(void *p, bool huge) { if (huge) free_hugepage_region(p); @@ -111,7 +120,7 @@ static void nvme_free(void *p, bool huge) free(p); } -static void *nvme_alloc(size_t len, bool *huge) +void *nvme_alloc(size_t len, bool *huge) { void *p; @@ -126,17 +135,27 @@ static void *nvme_alloc(size_t len, bool *huge) return p; } #else -static void nvme_free(void *p, bool huge) +void nvme_free(void *p, bool huge) { free(p); } -static void *nvme_alloc(size_t len, bool *huge) +void *nvme_alloc(size_t len, bool *huge) { return __nvme_alloc(len, huge); } #endif +static bool is_chardev(void) +{ + return S_ISCHR(nvme_stat.st_mode); +} + +static bool is_blkdev(void) +{ + return S_ISBLK(nvme_stat.st_mode); +} + static int open_dev(char *dev) { int err, fd; @@ -152,7 +171,7 @@ static int open_dev(char *dev) close(fd); goto perror; } - if (!S_ISCHR(nvme_stat.st_mode) && !S_ISBLK(nvme_stat.st_mode)) { + if (!is_chardev() && !is_blkdev()) { fprintf(stderr, "%s is not a block or character device\n", dev); close(fd); return -ENODEV; @@ -200,7 +219,7 @@ int parse_and_open(int argc, char **argv, const char *desc, return ret; } -enum nvme_print_flags validate_output_format(char *format) +enum nvme_print_flags validate_output_format(const char *format) { if (!format) return -EINVAL; @@ -386,8 +405,7 @@ static int get_telemetry_log(int argc, char **argv, struct command *cmd, struct hdr = malloc(bs); page_log = malloc(bs); if (!hdr || !page_log) { - fprintf(stderr, "Failed to allocate %zu bytes for log: %s\n", - bs, strerror(errno)); + perror("failed to allocate buf for log\n"); err = -ENOMEM; goto free_mem; } @@ -623,7 +641,7 @@ static int get_error_log(int argc, char **argv, struct command *cmd, struct plug cfg.log_entries = min(cfg.log_entries, ctrl.elpe + 1); err_log = calloc(cfg.log_entries, sizeof(struct nvme_error_log_page)); if (!err_log) { - fprintf(stderr, "could not alloc buffer for error log\n"); + perror("could not alloc buffer for error log\n"); err = -ENOMEM; goto close_fd; } @@ -738,6 +756,470 @@ ret: return nvme_status_to_errno(err, false); } +static int get_pred_lat_per_nvmset_log(int argc, char **argv, + struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Retrieve Predictable latency per nvm set log "\ + "page and prints it for the given device in either decoded " \ + "format(default),json or binary."; + const char *nvmset_id = "NVM Set Identifier"; + const char *raw = "use binary output"; + struct nvme_predlat_per_nvmset_log_page plpns_log; + enum nvme_print_flags flags; + int err, fd; + + struct config { + __u16 nvmset_id; + char *output_format; + int raw_binary; + }; + + struct config cfg = { + .nvmset_id = 1, + .output_format = "normal", + }; + + OPT_ARGS(opts) = { + OPT_UINT("nvmset-id", 'i', &cfg.nvmset_id, nvmset_id), + OPT_FMT("output-format", 'o', &cfg.output_format, output_format), + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), + OPT_END() + }; + + err = fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + goto ret; + + err = flags = validate_output_format(cfg.output_format); + if (flags < 0) + goto close_fd; + if (cfg.raw_binary) + flags = BINARY; + + err = nvme_predictable_latency_per_nvmset_log(fd, + cfg.nvmset_id, &plpns_log); + if (!err) + nvme_show_predictable_latency_per_nvmset(&plpns_log, + cfg.nvmset_id, devicename, flags); + else if (err > 0) + nvme_show_status(err); + else + perror("predictable latency per nvm set"); + +close_fd: + close(fd); +ret: + return nvme_status_to_errno(err, false); +} + +static int get_pred_lat_event_agg_log(int argc, char **argv, + struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Retrieve Predictable Latency Event" \ + "Aggregate Log page and prints it, for the given" \ + "device in either decoded format(default)," \ + "json or binary."; + const char *log_entries = "Number of pending NVM Set" \ + "log Entries list"; + const char *rae = "Retain an Asynchronous Event"; + const char *raw = "use binary output"; + void *pea_log; + struct nvme_id_ctrl ctrl; + enum nvme_print_flags flags; + int err, fd; + __u32 log_size; + + struct config { + __u64 log_entries; + bool rae; + char *output_format; + int raw_binary; + }; + + struct config cfg = { + .log_entries = 2044, + .rae = false, + .output_format = "normal", + }; + + OPT_ARGS(opts) = { + OPT_UINT("log-entries", 'e', &cfg.log_entries, log_entries), + OPT_FLAG("rae", 'r', &cfg.rae, rae), + OPT_FMT("output-format", 'o', &cfg.output_format, output_format), + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), + OPT_END() + }; + + err = fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + goto ret; + + err = flags = validate_output_format(cfg.output_format); + if (flags < 0) + goto close_fd; + if (cfg.raw_binary) + flags = BINARY; + + if (!cfg.log_entries) { + fprintf(stderr, "non-zero log-entries is required param\n"); + err = -EINVAL; + goto close_fd; + } + + err = nvme_identify_ctrl(fd, &ctrl); + if (err < 0) { + perror("identify controller"); + goto close_fd; + } else if (err) { + nvme_show_status(err); + goto close_fd; + } + + cfg.log_entries = min(cfg.log_entries, le32_to_cpu(ctrl.nsetidmax)); + log_size = sizeof(__u64) + cfg.log_entries * sizeof(__u16); + pea_log = calloc(log_size, 1); + if (!pea_log) { + perror("could not alloc buffer for predictable " \ + "latency event agggregate log entries\n"); + err = -ENOMEM; + goto close_fd; + } + + err = nvme_predictable_latency_event_agg_log(fd, pea_log, cfg.rae, + log_size); + if (!err) + nvme_show_predictable_latency_event_agg_log(pea_log, cfg.log_entries, + log_size, devicename, flags); + else if (err > 0) + nvme_show_status(err); + else + perror("predictable latency event gggregate log page"); + free(pea_log); + +close_fd: + close(fd); +ret: + return nvme_status_to_errno(err, false); +} + +static int get_persistent_event_log(int argc, char **argv, + struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Retrieve Persistent Event log info for"\ + " the given device in either decoded format(default),"\ + " json or binary."; + const char *action = "action the controller shall take during"\ + " processing this persistent log page command."; + const char *log_len = "number of bytes to retrieve"; + const char *raw = "use binary output"; + void *pevent_log_info; + struct nvme_persistent_event_log_head *pevent_log_head; + enum nvme_print_flags flags; + int err, fd; + bool huge; + + struct config { + __u8 action; + __u32 log_len; + int raw_binary; + char *output_format; + }; + + struct config cfg = { + .action = 0xff, + .log_len = 0, + .output_format = "normal", + }; + + OPT_ARGS(opts) = { + OPT_UINT("action", 'a', &cfg.action, action), + OPT_UINT("log_len", 'l', &cfg.log_len, log_len), + OPT_FMT("output-format", 'o', &cfg.output_format, output_format), + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), + OPT_END() + }; + + err = fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + goto ret; + + err = flags = validate_output_format(cfg.output_format); + if (flags < 0) + goto close_fd; + if (cfg.raw_binary) + flags = BINARY; + + pevent_log_head = calloc(sizeof(*pevent_log_head), 1); + if (!pevent_log_head) { + perror("could not alloc buffer for persistent " \ + "event log header\n"); + err = -ENOMEM; + goto close_fd; + } + + err = nvme_persistent_event_log(fd, cfg.action, + sizeof(*pevent_log_head), pevent_log_head); + if (err < 0) { + perror("persistent event log"); + goto close_fd; + } else if (err) { + nvme_show_status(err); + goto close_fd; + } + + if (cfg.action == NVME_PEVENT_LOG_RELEASE_CTX) { + printf("Releasing Persistent Event Log Context\n"); + goto close_fd; + } + + if (!cfg.log_len && cfg.action != NVME_PEVENT_LOG_EST_CTX_AND_READ) { + cfg.log_len = le64_to_cpu(pevent_log_head->tll); + } else if (!cfg.log_len && cfg.action == NVME_PEVENT_LOG_EST_CTX_AND_READ) { + printf("Establishing Persistent Event Log Context\n"); + goto close_fd; + } + + /* + * if header aleady read with context establish action 0x1, + * action shall not be 0x1 again in the subsequent request, + * until the current context is released by issuing action + * with 0x2, otherwise throws command sequence error, make + * it as zero to read the log page + */ + if (cfg.action == NVME_PEVENT_LOG_EST_CTX_AND_READ) + cfg.action = NVME_PEVENT_LOG_READ; + + pevent_log_info = nvme_alloc(cfg.log_len, &huge); + if (!pevent_log_info) { + perror("could not alloc buffer for persistent event log page\n"); + err = -ENOMEM; + goto close_fd; + } + err = nvme_persistent_event_log(fd, cfg.action, + cfg.log_len, pevent_log_info); + if (!err) + nvme_show_persistent_event_log(pevent_log_info, cfg.action, + cfg.log_len, devicename, flags); + else if (err > 0) + nvme_show_status(err); + else + perror("persistent event log"); + + nvme_free(pevent_log_info, huge); + +close_fd: + free(pevent_log_head); + close(fd); +ret: + return nvme_status_to_errno(err, false); +} + +static int get_endurance_event_agg_log(int argc, char **argv, + struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Retrieve Retrieve Predictable Latency " \ + "Event Aggregate page and prints it, for the given " \ + "device in either decoded format(default), " \ + "json or binary."; + const char *log_entries = "Number of pending Endurance Group " \ + "Event log Entries list"; + const char *rae = "Retain an Asynchronous Event"; + const char *raw = "use binary output"; + void *endurance_log; + struct nvme_id_ctrl ctrl; + enum nvme_print_flags flags; + int err, fd; + __u32 log_size; + + struct config { + __u64 log_entries; + bool rae; + char *output_format; + int raw_binary; + }; + + struct config cfg = { + .log_entries = 2044, + .rae = false, + .output_format = "normal", + }; + + OPT_ARGS(opts) = { + OPT_UINT("log-entries", 'e', &cfg.log_entries, log_entries), + OPT_FLAG("rae", 'r', &cfg.rae, rae), + OPT_FMT("output-format", 'o', &cfg.output_format, output_format), + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), + OPT_END() + }; + + err = fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + goto ret; + + err = flags = validate_output_format(cfg.output_format); + if (flags < 0) + goto close_fd; + if (cfg.raw_binary) + flags = BINARY; + + if (!cfg.log_entries) { + fprintf(stderr, "non-zero log-entries is required param\n"); + err = -EINVAL; + goto close_fd; + } + + err = nvme_identify_ctrl(fd, &ctrl); + if (err < 0) { + perror("identify controller"); + goto close_fd; + } else if (err) { + fprintf(stderr, "could not identify controller\n"); + err = -ENODEV; + goto close_fd; + } + + cfg.log_entries = min(cfg.log_entries, le16_to_cpu(ctrl.endgidmax)); + log_size = sizeof(__u64) + cfg.log_entries * sizeof(__u16); + endurance_log = calloc(log_size, 1); + if (!endurance_log) { + perror("could not alloc buffer for endurance group" \ + " event agggregate log entries\n"); + err = -ENOMEM; + goto close_fd; + } + + err = nvme_endurance_group_event_agg_log(fd, endurance_log, cfg.rae, + log_size); + if (!err) + nvme_show_endurance_group_event_agg_log(endurance_log, cfg.log_entries, + log_size, devicename, flags); + else if (err > 0) + nvme_show_status(err); + else + perror("endurance group event aggregate log page"); + free(endurance_log); + +close_fd: + close(fd); +ret: + return nvme_status_to_errno(err, false); +} + +static int get_lba_status_log(int argc, char **argv, + struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Retrieve Get LBA Status Info Log " \ + "and prints it, for the given device in either " \ + "decoded format(default),json or binary."; + const char *rae = "Retain an Asynchronous Event"; + void *lab_status; + enum nvme_print_flags flags; + int err, fd; + __u32 lslplen; + + struct config { + bool rae; + char *output_format; + }; + + struct config cfg = { + .rae = false, + .output_format = "normal", + }; + + OPT_ARGS(opts) = { + OPT_FLAG("rae", 'r', &cfg.rae, rae), + OPT_FMT("output-format", 'o', &cfg.output_format, output_format), + OPT_END() + }; + + err = fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + goto ret; + + err = flags = validate_output_format(cfg.output_format); + if (flags < 0) + goto close_fd; + + err = nvme_lba_status_log(fd, &lslplen, true, sizeof(__u32)); + if (err < 0) { + perror("lba status log page"); + goto close_fd; + } else if (err) { + nvme_show_status(err); + goto close_fd; + } + + lab_status = calloc(lslplen, 1); + if (!lab_status) { + perror("could not alloc buffer for lba status log"); + err = -ENOMEM; + goto close_fd; + } + + err = nvme_lba_status_log(fd, lab_status, cfg.rae, lslplen); + if (!err) + nvme_show_lba_status_log(lab_status, lslplen, devicename, flags); + else if (err > 0) + nvme_show_status(err); + else + perror("lba status log page"); + free(lab_status); + +close_fd: + close(fd); +ret: + return nvme_status_to_errno(err, false); +} + +static int get_resv_notif_log(int argc, char **argv, + struct command *cmd, struct plugin *plugin) +{ + + const char *desc = "Retrieve Reservation Notification " \ + "log page and prints it, for the given " \ + "device in either decoded format(default), " \ + "json or binary."; + struct nvme_resv_notif_log resv; + enum nvme_print_flags flags; + int err, fd; + + struct config { + char *output_format; + }; + + struct config cfg = { + .output_format = "normal", + }; + + OPT_ARGS(opts) = { + OPT_FMT("output-format", 'o', &cfg.output_format, output_format), + OPT_END() + }; + + err = fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + goto ret; + + err = flags = validate_output_format(cfg.output_format); + if (flags < 0) + goto close_fd; + + err = nvme_resv_notif_log(fd, &resv); + if (!err) + nvme_show_resv_notif_log(&resv, devicename, flags); + else if (err > 0) + nvme_show_status(err); + else + perror("resv notifi log"); + +close_fd: + close(fd); +ret: + return nvme_status_to_errno(err, false); + +} + static int get_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) { const char *desc = "Retrieve desired number of bytes "\ @@ -756,7 +1238,7 @@ static int get_log(int argc, char **argv, struct command *cmd, struct plugin *pl struct config { __u32 namespace_id; - __u32 log_id; + __u8 log_id; __u32 log_len; __u32 aen; __u64 lpo; @@ -768,7 +1250,7 @@ static int get_log(int argc, char **argv, struct command *cmd, struct plugin *pl struct config cfg = { .namespace_id = NVME_NSID_ALL, - .log_id = 0xffffffff, + .log_id = 0xff, .log_len = 0, .lpo = NVME_NO_LOG_LPO, .lsp = NVME_NO_LOG_LSP, @@ -798,12 +1280,6 @@ static int get_log(int argc, char **argv, struct command *cmd, struct plugin *pl cfg.log_id = (cfg.aen >> 16) & 0xff; } - if (cfg.log_id > 0xff) { - fprintf(stderr, "Invalid log identifier: %d. Valid range: 0-255\n", cfg.log_id); - err = -EINVAL; - goto close_fd; - } - if (!cfg.log_len) { fprintf(stderr, "non-zero log-len is required param\n"); err = -EINVAL; @@ -812,9 +1288,8 @@ static int get_log(int argc, char **argv, struct command *cmd, struct plugin *pl log = malloc(cfg.log_len); if (!log) { - fprintf(stderr, "could not alloc buffer for log: %s\n", - strerror(errno)); - err = -EINVAL; + perror("could not alloc buffer for log\n"); + err = -ENOMEM; goto close_fd; } @@ -844,6 +1319,7 @@ ret: static int sanitize_log(int argc, char **argv, struct command *command, struct plugin *plugin) { const char *desc = "Retrieve sanitize log and show it."; + const char *rae = "Retain an Asynchronous Event"; const char *raw = "show log in binary format"; const char *human_readable = "show log in readable format"; struct nvme_sanitize_log_page sanitize_log; @@ -851,16 +1327,19 @@ static int sanitize_log(int argc, char **argv, struct command *command, struct p int fd, err; struct config { + bool rae; int raw_binary; int human_readable; char *output_format; }; struct config cfg = { + .rae = false, .output_format = "normal", }; OPT_ARGS(opts) = { + OPT_FLAG("rae", 'r', &cfg.rae, rae), OPT_FMT("output-format", 'o', &cfg.output_format, output_format), OPT_FLAG("human-readable",'H', &cfg.human_readable, human_readable), OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), @@ -879,7 +1358,7 @@ static int sanitize_log(int argc, char **argv, struct command *command, struct p if (cfg.human_readable) flags |= VERBOSE; - err = nvme_sanitize_log(fd, &sanitize_log); + err = nvme_sanitize_log(fd, cfg.rae, &sanitize_log); if (!err) nvme_show_sanitize_log(&sanitize_log, devicename, flags); else if (err > 0) @@ -932,8 +1411,7 @@ static int list_ctrl(int argc, char **argv, struct command *cmd, struct plugin * for (i = 0; i < (min(num, 2048)); i++) printf("[%4u]:%#x\n", i, le16_to_cpu(cntlist->identifier[i])); - } - else if (err > 0) + } else if (err > 0) nvme_show_status(err); else perror("id controller list"); @@ -950,6 +1428,7 @@ static int list_ns(int argc, char **argv, struct command *cmd, struct plugin *pl const char *desc = "For the specified controller handle, show the "\ "namespace list in the associated NVMe subsystem, optionally starting with a given nsid."; const char *namespace_id = "first nsid returned list should start from"; + const char *csi = "I/O command set identifier"; const char *all = "show all namespaces in the subsystem, whether attached or inactive"; int err, i, fd; __le32 ns_list[1024]; @@ -957,6 +1436,7 @@ static int list_ns(int argc, char **argv, struct command *cmd, struct plugin *pl struct config { __u32 namespace_id; int all; + __u16 csi; }; struct config cfg = { @@ -965,6 +1445,7 @@ static int list_ns(int argc, char **argv, struct command *cmd, struct plugin *pl OPT_ARGS(opts) = { OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_BYTE("csi", 'y', &cfg.csi, csi), OPT_FLAG("all", 'a', &cfg.all, all), OPT_END() }; @@ -979,8 +1460,8 @@ static int list_ns(int argc, char **argv, struct command *cmd, struct plugin *pl goto close_fd; } - err = nvme_identify_ns_list(fd, cfg.namespace_id - 1, !!cfg.all, - ns_list); + err = nvme_identify_ns_list_csi(fd, cfg.namespace_id - 1, cfg.csi, + !!cfg.all, ns_list); if (!err) { for (i = 0; i < 1024; i++) if (ns_list[i]) @@ -1028,13 +1509,9 @@ static int delete_ns(int argc, char **argv, struct command *cmd, struct plugin * goto ret; if (!cfg.namespace_id) { - cfg.namespace_id = nvme_get_nsid(fd); - if (cfg.namespace_id == 0) { - err = -EINVAL; - goto close_fd; - } - else if (cfg.namespace_id < 0) { - err = cfg.namespace_id; + err = cfg.namespace_id = nvme_get_nsid(fd); + if (err < 0) { + perror("get-namespace-id"); goto close_fd; } } @@ -1145,15 +1622,18 @@ static int create_ns(int argc, char **argv, struct command *cmd, struct plugin * "parameters. The next available namespace ID is used for the "\ "create operation. Note that create-ns does not attach the "\ "namespace to a controller, the attach-ns command is needed."; - const char *nsze = "size of ns"; - const char *ncap = "capacity of ns"; - const char *flbas = "FLBA size"; - const char *dps = "data protection capabilities"; - const char *nmic = "multipath and sharing capabilities"; - const char *anagrpid = "ANA Group Identifier"; - const char *nvmsetid = "NVM Set Identifier"; + const char *nsze = "size of ns (NSZE)"; + const char *ncap = "capacity of ns (NCAP)"; + const char *flbas = "Formatted LBA size (FLBAS), if entering this "\ + "value ignore \'block-size\' field"; + const char *dps = "data protection settings (DPS)"; + const char *nmic = "multipath and sharing capabilities (NMIC)"; + const char *anagrpid = "ANA Group Identifier (ANAGRPID)"; + const char *nvmsetid = "NVM Set Identifier (NVMSETID)"; + const char *csi = "command set identifier (CSI)"; const char *timeout = "timeout value, in milliseconds"; - const char *bs = "target block size"; + const char *bs = "target block size, specify only if \'FLBAS\' "\ + "value not entered"; int err = 0, fd, i; struct nvme_id_ns ns; @@ -1169,6 +1649,7 @@ static int create_ns(int argc, char **argv, struct command *cmd, struct plugin * __u16 nvmsetid; __u64 bs; __u32 timeout; + __u8 csi; }; struct config cfg = { @@ -1189,6 +1670,7 @@ static int create_ns(int argc, char **argv, struct command *cmd, struct plugin * OPT_UINT("nvmset-id", 'i', &cfg.nvmsetid, nvmsetid), OPT_SUFFIX("block-size", 'b', &cfg.bs, bs), OPT_UINT("timeout", 't', &cfg.timeout, timeout), + OPT_BYTE("csi", 'y', &cfg.csi, csi), OPT_END() }; @@ -1240,7 +1722,8 @@ static int create_ns(int argc, char **argv, struct command *cmd, struct plugin * } err = nvme_ns_create(fd, cfg.nsze, cfg.ncap, cfg.flbas, cfg.dps, cfg.nmic, - cfg.anagrpid, cfg.nvmsetid, cfg.timeout, &nsid); + cfg.anagrpid, cfg.nvmsetid, cfg.csi, cfg.timeout, + &nsid); if (!err) printf("%s: Success, created nsid:%d\n", cmd->name, nsid); else if (err > 0) @@ -1263,7 +1746,7 @@ static int list_subsys(int argc, char **argv, struct command *cmd, const char *desc = "Retrieve information for subsystems"; const char *verbose = "Increase output verbosity"; __u32 ns_instance = 0; - int err; + int err, nsid = 0; struct config { char *output_format; @@ -1288,7 +1771,7 @@ static int list_subsys(int argc, char **argv, struct command *cmd, devicename = NULL; if (optind < argc) { char path[512]; - int id; + int id, fd; devicename = basename(argv[optind]); if (sscanf(devicename, "nvme%dn%d", &id, &ns_instance) != 2) { @@ -1297,6 +1780,22 @@ static int list_subsys(int argc, char **argv, struct command *cmd, err = -EINVAL; goto ret; } + sprintf(path, "/dev/%s", devicename); + fd = open(path, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "Cannot read nsid from %s\n", + devicename); + err = -EINVAL; + goto ret; + } + nsid = nvme_get_nsid(fd); + close(fd); + if (nsid < 0) { + fprintf(stderr, "Cannot read nsid from %s\n", + devicename); + err = -EINVAL; + goto ret; + } sprintf(path, "/sys/block/%s/device", devicename); subsysnqn = get_nvme_subsnqn(path); if (!subsysnqn) { @@ -1318,7 +1817,7 @@ static int list_subsys(int argc, char **argv, struct command *cmd, if (cfg.verbose) flags |= VERBOSE; - err = scan_subsystems(&t, subsysnqn, ns_instance); + err = scan_subsystems(&t, subsysnqn, ns_instance, nsid, NULL); if (err) { fprintf(stderr, "Failed to scan namespaces\n"); goto free; @@ -1326,8 +1825,7 @@ static int list_subsys(int argc, char **argv, struct command *cmd, nvme_show_subsystem_list(&t, flags); free: free_topology(&t); - if (subsysnqn) - free(subsysnqn); + free(subsysnqn); ret: return nvme_status_to_errno(err, false); } @@ -1335,22 +1833,26 @@ ret: static int list(int argc, char **argv, struct command *cmd, struct plugin *plugin) { const char *desc = "Retrieve basic information for all NVMe namespaces"; + const char *device_dir = "Additional directory to search for devices"; const char *verbose = "Increase output verbosity"; struct nvme_topology t = { }; enum nvme_print_flags flags; int err = 0; struct config { + char *device_dir; char *output_format; int verbose; }; struct config cfg = { + .device_dir = NULL, .output_format = "normal", .verbose = 0, }; OPT_ARGS(opts) = { + OPT_STRING("directory", 'd', "DIR", &cfg.device_dir, device_dir), OPT_FMT("output-format", 'o', &cfg.output_format, output_format_no_binary), OPT_FLAG("verbose", 'v', &cfg.verbose, verbose), OPT_END() @@ -1370,7 +1872,7 @@ static int list(int argc, char **argv, struct command *cmd, struct plugin *plugi if (cfg.verbose) flags |= VERBOSE; - err = scan_subsystems(&t, NULL, 0); + err = scan_subsystems(&t, NULL, 0, 0, cfg.device_dir); if (err) { fprintf(stderr, "Failed to scan namespaces\n"); return err; @@ -1447,6 +1949,50 @@ static int id_ctrl(int argc, char **argv, struct command *cmd, struct plugin *pl return __id_ctrl(argc, argv, cmd, plugin, NULL); } +static int nvm_id_ctrl(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + const char *desc = "Send an Identify Controller NVM Command Set "\ + "command to the given device and report information about "\ + "the specified controller in various formats."; + enum nvme_print_flags flags; + struct nvme_id_ctrl_nvm ctrl_nvm; + int fd, err = -1; + + struct config { + char *output_format; + }; + + struct config cfg = { + .output_format = "normal", + }; + + OPT_ARGS(opts) = { + OPT_FMT("output-format", 'o', &cfg.output_format, output_format), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + goto ret; + + err = flags = validate_output_format(cfg.output_format); + if (flags < 0) + goto close_fd; + + err = nvme_identify_ctrl_nvm(fd, &ctrl_nvm); + if (!err) + nvme_show_id_ctrl_nvm(&ctrl_nvm, flags); + else if (err > 0) + nvme_show_status(err); + else + perror("nvm identify controller"); +close_fd: + close(fd); +ret: + return nvme_status_to_errno(err, false); +} + static int ns_descs(int argc, char **argv, struct command *cmd, struct plugin *plugin) { const char *desc = "Send Namespace Identification Descriptors command to the "\ @@ -1487,9 +2033,9 @@ static int ns_descs(int argc, char **argv, struct command *cmd, struct plugin *p flags = BINARY; if (!cfg.namespace_id) { - cfg.namespace_id = nvme_get_nsid(fd); - if (cfg.namespace_id < 0) { - err = cfg.namespace_id; + err = cfg.namespace_id = nvme_get_nsid(fd); + if (err < 0) { + perror("get-namespace-id"); goto close_fd; } } @@ -1569,15 +2115,9 @@ static int id_ns(int argc, char **argv, struct command *cmd, struct plugin *plug flags |= VERBOSE; if (!cfg.namespace_id) { - cfg.namespace_id = nvme_get_nsid(fd); - if (cfg.namespace_id < 0) { - err = cfg.namespace_id; - goto close_fd; - } - else if (!cfg.namespace_id) { - fprintf(stderr, - "Error: requesting namespace-id from non-block device\n"); - err = -ENOTBLK; + err = cfg.namespace_id = nvme_get_nsid(fd); + if (err < 0) { + perror("get-namespace-id"); goto close_fd; } } @@ -1749,6 +2289,48 @@ close_fd: return err; } +static int id_iocs(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Send an Identify Command Set Data command to the "\ + "given device, returns properties of the specified controller "\ + "in either human-readable or binary format."; + const char *controller_id = "identifier of desired controller"; + struct nvme_id_iocs iocs; + int err, fd; + + struct config { + __u16 cntid; + }; + + struct config cfg = { + .cntid = 0xffff, + }; + + OPT_ARGS(opts) = { + OPT_SHRT("controller-id", 'c', &cfg.cntid, controller_id), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) { + err = fd; + goto ret; + } + + err = nvme_identify_iocs(fd, cfg.cntid, &iocs); + if (!err) { + printf("NVMe Identify I/O Command Set:\n"); + nvme_show_id_iocs(&iocs); + } else if (err > 0) + nvme_show_status(err); + else + perror("NVMe Identify I/O Command Set"); + + close(fd); +ret: + return nvme_status_to_errno(err, false); +} + static int get_ns_id(int argc, char **argv, struct command *cmd, struct plugin *plugin) { int err = 0, nsid, fd; @@ -1795,29 +2377,73 @@ static int virtual_mgmt(int argc, char **argv, struct command *cmd, struct plugi "9h: Secondary Online"; const char *nr = "Number of Controller Resources(NR)"; int fd, err; - __u32 result; + __u32 result, cdw10; + + struct config { + __u16 cntlid; + __u8 rt; + __u8 act; + __u16 nr; + }; + + struct config cfg = { + .cntlid = 0, + .rt = 0, + .act = 0, + .nr = 0, + }; + + OPT_ARGS(opts) = { + OPT_UINT("cntlid", 'c', &cfg.cntlid, cntlid), + OPT_UINT("rt", 'r', &cfg.rt, rt), + OPT_UINT("act", 'a', &cfg.act, act), + OPT_UINT("nr", 'n', &cfg.nr, nr), + OPT_END() + }; + + err = fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + goto ret; + + cdw10 = cfg.act | (cfg.rt << 8) | (cfg.cntlid << 16); + + err = nvme_virtual_mgmt(fd, cdw10, cfg.nr, &result); + if (!err) { + printf("success, Number of Controller Resources Modified "\ + "(NRM):%#x\n", result); + } else if (err > 0) { + nvme_show_status(err); + } else + perror("virt-mgmt"); + + close(fd); +ret: + return nvme_status_to_errno(err, false); +} + +static int primary_ctrl_caps(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Send an Identify Primary Controller Capabilities "\ + "command to the given device and report the information in a "\ + "decoded format (default), json or binary."; + const char *human_readable = "show info in readable format"; + struct nvme_primary_ctrl_caps caps; + + int err, fd; + enum nvme_print_flags flags; struct config { - int cntlid; - int rt; - int act; - __u32 cdw10; - __u32 cdw11; + char *output_format; + int human_readable; }; struct config cfg = { - .cntlid = 0, - .rt = 0, - .act = 0, - .cdw10 = 0, - .cdw11 = 0, + .output_format = "normal", }; OPT_ARGS(opts) = { - OPT_UINT("cntlid", 'c', &cfg.cntlid, cntlid), - OPT_UINT("rt", 'r', &cfg.rt, rt), - OPT_UINT("act", 'a', &cfg.act, act), - OPT_UINT("nr", 'n', &cfg.cdw11, nr), + OPT_FMT("output-format", 'o', &cfg.output_format, output_format), + OPT_FLAG("human-readable", 'H', &cfg.human_readable, human_readable), OPT_END() }; @@ -1825,18 +2451,20 @@ static int virtual_mgmt(int argc, char **argv, struct command *cmd, struct plugi if (fd < 0) goto ret; - cfg.cdw10 = cfg.cntlid << 16; - cfg.cdw10 = cfg.cdw10 | (cfg.rt << 8); - cfg.cdw10 = cfg.cdw10 | cfg.act; + err = flags = validate_output_format(cfg.output_format); + if (flags < 0) + goto close_fd; + if (cfg.human_readable) + flags |= VERBOSE; - err = nvme_virtual_mgmt(fd, cfg.cdw10, cfg.cdw11, &result); - if (!err) { - printf("success, Number of Resources allocated:%#x\n", result); - } else if (err > 0) { + err = nvme_identify_primary_ctrl_caps(fd, &caps); + if (!err) + nvme_show_primary_ctrl_caps(&caps, flags); + else if (err > 0) nvme_show_status(err); - } else - perror("virt-mgmt"); - + else + perror("identify primary controller capabilities"); +close_fd: close(fd); ret: return nvme_status_to_errno(err, false); @@ -1927,17 +2555,17 @@ static int device_self_test(int argc, char **argv, struct command *cmd, struct p struct config { __u32 namespace_id; - __u32 cdw10; + __u8 stc; }; struct config cfg = { .namespace_id = NVME_NSID_ALL, - .cdw10 = 0, + .stc = 0, }; OPT_ARGS(opts) = { OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), - OPT_UINT("self-test-code", 's', &cfg.cdw10, self_test_code), + OPT_UINT("self-test-code", 's', &cfg.stc, self_test_code), OPT_END() }; @@ -1945,12 +2573,14 @@ static int device_self_test(int argc, char **argv, struct command *cmd, struct p if (fd < 0) goto ret; - err = nvme_self_test_start(fd, cfg.namespace_id, cfg.cdw10); + err = nvme_self_test_start(fd, cfg.namespace_id, cfg.stc); if (!err) { - if ((cfg.cdw10 & 0xf) == 0xf) + if (cfg.stc == 0xf) printf("Aborting device self-test operation\n"); - else - printf("Device self-test started\n"); + else if (cfg.stc == 0x2) + printf("Extended Device self-test started\n"); + else if (cfg.stc == 0x1) + printf("Short Device self-test started\n"); } else if (err > 0) { nvme_show_status(err); } else @@ -1966,27 +2596,28 @@ static int self_test_log(int argc, char **argv, struct command *cmd, struct plug const char *desc = "Retrieve the self-test log for the given device and given test "\ "(or optionally a namespace) in either decoded format "\ "(default) or binary."; - const char *namespace_id = "Indicate the namespace from which the self-test "\ - "log has to be obtained"; + const char *dst_entries = "Indicate how many DST log entries to be retrieved, "\ + "by default all the 20 entries will be retrieved"; const char *verbose = "Increase output verbosity"; struct nvme_self_test_log self_test_log; enum nvme_print_flags flags; int err, fd; + __u32 log_size; struct config { - __u32 namespace_id; + __u8 dst_entries; char *output_format; int verbose; }; struct config cfg = { - .namespace_id = NVME_NSID_ALL, + .dst_entries = NVME_ST_REPORTS, .output_format = "normal", }; OPT_ARGS(opts) = { - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_UINT("dst-entries", 'e', &cfg.dst_entries, dst_entries), OPT_FMT("output-format", 'o', &cfg.output_format, output_format), OPT_FLAG("verbose", 'v', &cfg.verbose, verbose), OPT_END() @@ -2002,9 +2633,11 @@ static int self_test_log(int argc, char **argv, struct command *cmd, struct plug if (cfg.verbose) flags |= VERBOSE; - err = nvme_self_test_log(fd, cfg.namespace_id, &self_test_log); + log_size = NVME_ST_LOG_HEAD_SIZE + cfg.dst_entries * NVME_ST_LOG_ENTRY_SIZE; + err = nvme_self_test_log(fd, log_size, &self_test_log); if (!err) - nvme_show_self_test_log(&self_test_log, devicename, flags); + nvme_show_self_test_log(&self_test_log, cfg.dst_entries, log_size, + devicename, flags); else if (err > 0) nvme_show_status(err); else @@ -2039,7 +2672,7 @@ static int get_feature(int argc, char **argv, struct command *cmd, struct plugin struct config { __u32 namespace_id; - __u32 feature_id; + __u8 feature_id; __u8 sel; __u32 cdw11; __u32 data_len; @@ -2048,7 +2681,7 @@ static int get_feature(int argc, char **argv, struct command *cmd, struct plugin }; struct config cfg = { - .namespace_id = 1, + .namespace_id = 0, .feature_id = 0, .sel = 0, .cdw11 = 0, @@ -2070,6 +2703,18 @@ static int get_feature(int argc, char **argv, struct command *cmd, struct plugin if (fd < 0) goto ret; + if (!cfg.namespace_id) { + err = cfg.namespace_id = nvme_get_nsid(fd); + if (err < 0) { + if (errno != ENOTTY) { + perror("get-namespace-id"); + goto close_fd; + } + + cfg.namespace_id = NVME_NSID_ALL; + } + } + if (cfg.sel > 7) { fprintf(stderr, "invalid 'select' param:%d\n", cfg.sel); err = -EINVAL; @@ -2081,32 +2726,11 @@ static int get_feature(int argc, char **argv, struct command *cmd, struct plugin goto close_fd; } - switch (cfg.feature_id) { - case NVME_FEAT_LBA_RANGE: - cfg.data_len = 4096; - break; - case NVME_FEAT_AUTO_PST: - cfg.data_len = 256; - break; - case NVME_FEAT_HOST_MEM_BUF: - cfg.data_len = 4096; - break; - case NVME_FEAT_HOST_ID: - cfg.data_len = 8; - /* check for Extended Host Identifier */ - if (cfg.cdw11 & 0x1) - cfg.data_len = 16; - break; - case NVME_FEAT_PLM_CONFIG: - cfg.data_len = 512; - break; - case NVME_FEAT_TIMESTAMP: - cfg.data_len = 8; - break; - case NVME_FEAT_HOST_BEHAVIOR: - cfg.data_len = 512; - break; - } + cfg.data_len = nvme_feat_buf_len[cfg.feature_id]; + + /* check for Extended Host Identifier */ + if (cfg.feature_id == NVME_FEAT_HOST_ID && (cfg.cdw11 & 0x1)) + cfg.data_len = 16; if (cfg.sel == 3) cfg.data_len = 0; @@ -2140,8 +2764,7 @@ static int get_feature(int argc, char **argv, struct command *cmd, struct plugin } else perror("get-feature"); - if (buf) - free(buf); + free(buf); close_fd: close(fd); @@ -2207,22 +2830,27 @@ static int fw_download(int argc, char **argv, struct command *cmd, struct plugin } fw_size = sb.st_size; - if (fw_size & 0x3) { + if ((fw_size & 0x3) || (fw_size == 0)) { fprintf(stderr, "Invalid size:%d for f/w image\n", fw_size); err = -EINVAL; goto close_fw_fd; } - fw_buf = nvme_alloc(fw_size, &huge); + if (cfg.xfer == 0 || cfg.xfer % 4096) + cfg.xfer = 4096; + + if (cfg.xfer < HUGE_MIN) + fw_buf = __nvme_alloc(fw_size, &huge); + else + fw_buf = nvme_alloc(fw_size, &huge); + if (!fw_buf) { - fprintf(stderr, "No memory for f/w size:%d\n", fw_size); + perror("No memory for f/w size:\n"); err = -ENOMEM; goto close_fw_fd; } buf = fw_buf; - if (cfg.xfer == 0 || cfg.xfer % 4096) - cfg.xfer = 4096; if (read(fw_fd, fw_buf, fw_size) != ((ssize_t)(fw_size))) { err = -errno; fprintf(stderr, "read :%s :%s\n", cfg.fw, strerror(errno)); @@ -2257,9 +2885,9 @@ ret: return nvme_status_to_errno(err, false); } -static char *nvme_fw_status_reset_type(__u32 status) +static char *nvme_fw_status_reset_type(__u16 status) { - switch (status & 0x3ff) { + switch (status & 0x7ff) { case NVME_SC_FW_NEEDS_CONV_RESET: return "conventional"; case NVME_SC_FW_NEEDS_SUBSYS_RESET: return "subsystem"; case NVME_SC_FW_NEEDS_RESET: return "any controller"; @@ -2322,7 +2950,7 @@ static int fw_commit(int argc, char **argv, struct command *cmd, struct plugin * if (err < 0) perror("fw-commit"); else if (err != 0) - switch (err & 0x3ff) { + switch (err & 0x7ff) { case NVME_SC_FW_NEEDS_CONV_RESET: case NVME_SC_FW_NEEDS_SUBSYS_RESET: case NVME_SC_FW_NEEDS_RESET: @@ -2486,8 +3114,8 @@ static int sanitize(int argc, char **argv, struct command *cmd, struct plugin *p } if (cfg.sanact == NVME_SANITIZE_ACT_OVERWRITE) { - if (cfg.owpass > 16) { - fprintf(stderr, "OWPASS out of range [0-16]\n"); + if (cfg.owpass >= 16) { + fprintf(stderr, "OWPASS out of range [0-15]\n"); ret = -EINVAL; goto close_fd; } @@ -2697,8 +3325,8 @@ static int format(int argc, char **argv, struct command *cmd, struct plugin *plu struct nvme_id_ns ns; struct nvme_id_ctrl ctrl; int err, fd, i; + int block_size; __u8 prev_lbaf = 0; - __u8 lbads = 0; struct config { __u32 namespace_id; @@ -2771,13 +3399,11 @@ static int format(int argc, char **argv, struct command *cmd, struct plugin *plu * format of all namespaces. */ cfg.namespace_id = NVME_NSID_ALL; - } else { - if (!cfg.namespace_id) { - cfg.namespace_id = nvme_get_nsid(fd); - if (cfg.namespace_id < 0) { - err = cfg.namespace_id; - goto close_fd; - } + } else if (!cfg.namespace_id) { + err = cfg.namespace_id = nvme_get_nsid(fd); + if (err < 0) { + perror("get-namespace-id"); + goto close_fd; } } @@ -2813,8 +3439,8 @@ static int format(int argc, char **argv, struct command *cmd, struct plugin *plu } if (cfg.lbaf == 0xff) { fprintf(stderr, - "LBAF corresponding to block size %"PRIu64"(LBAF %u) not found\n", - (uint64_t)cfg.bs, lbads); + "LBAF corresponding to given block size %"PRIu64" not found\n", + (uint64_t)cfg.bs); fprintf(stderr, "Please correct block size, or specify LBAF directly\n"); err = -EINVAL; @@ -2822,8 +3448,7 @@ static int format(int argc, char **argv, struct command *cmd, struct plugin *plu } } else if (cfg.lbaf == 0xff) cfg.lbaf = prev_lbaf; - } - else { + } else { if (cfg.lbaf == 0xff) cfg.lbaf = 0; } @@ -2874,13 +3499,38 @@ static int format(int argc, char **argv, struct command *cmd, struct plugin *plu nvme_show_status(err); else { printf("Success formatting namespace:%x\n", cfg.namespace_id); - if (cfg.lbaf != prev_lbaf && ioctl(fd, BLKRRPART) < 0) { - fprintf(stderr, "failed to re-read partition table\n"); - err = -errno; - goto close_fd; - } + if (cfg.lbaf != prev_lbaf){ + if (is_chardev()) { + if(ioctl(fd, NVME_IOCTL_RESCAN) < 0){ + fprintf(stderr, "failed to rescan namespaces\n"); + err = -errno; + goto close_fd; + } + } else { + block_size = 1 << ns.lbaf[cfg.lbaf].ds; + + /* + * If block size has been changed by the format + * command up there, we should notify it to + * kernel blkdev to update its own block size + * to the given one because blkdev will not + * update by itself without re-opening fd. + */ + if (ioctl(fd, BLKBSZSET, &block_size) < 0) { + fprintf(stderr, "failed to set block size to %d\n", + block_size); + err = -errno; + goto close_fd; + } - if (cfg.reset && S_ISCHR(nvme_stat.st_mode)) + if(ioctl(fd, BLKRRPART) < 0) { + fprintf(stderr, "failed to re-read partition table\n"); + err = -errno; + goto close_fd; + } + } + } + if (cfg.reset && is_chardev()) nvme_reset_controller(fd); } @@ -2890,6 +3540,9 @@ ret: return nvme_status_to_errno(err, false); } +#define STRTOUL_AUTO_BASE (0) +#define NVME_FEAT_TIMESTAMP_DATA_SIZE (6) + static int set_feature(int argc, char **argv, struct command *cmd, struct plugin *plugin) { const char *desc = "Modify the saveable or changeable "\ @@ -2912,12 +3565,14 @@ static int set_feature(int argc, char **argv, struct command *cmd, struct plugin __u32 result; void *buf = NULL; int fd, ffd = STDIN_FILENO; + char *endptr = NULL; + uint64_t number = 0; struct config { char *file; __u32 namespace_id; - __u32 feature_id; - __u32 value; + __u8 feature_id; + __u64 value; __u32 cdw12; __u32 data_len; int save; @@ -2935,7 +3590,7 @@ static int set_feature(int argc, char **argv, struct command *cmd, struct plugin OPT_ARGS(opts) = { OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), OPT_UINT("feature-id", 'f', &cfg.feature_id, feature_id), - OPT_UINT("value", 'v', &cfg.value, value), + OPT_LONG("value", 'v', &cfg.value, value), OPT_UINT("cdw12", 'c', &cfg.cdw12, cdw12), OPT_UINT("data-len", 'l', &cfg.data_len, data_len), OPT_FILE("data", 'd', &cfg.file, data), @@ -2947,13 +3602,26 @@ static int set_feature(int argc, char **argv, struct command *cmd, struct plugin if (fd < 0) goto ret; + if (!cfg.namespace_id) { + err = cfg.namespace_id = nvme_get_nsid(fd); + if (err < 0) { + if (errno != ENOTTY) { + perror("get-namespace-id"); + goto close_fd; + } + + cfg.namespace_id = NVME_NSID_ALL; + } + } + if (!cfg.feature_id) { fprintf(stderr, "feature-id required param\n"); err = -EINVAL; goto close_fd; } - if (cfg.feature_id == NVME_FEAT_LBA_RANGE) - cfg.data_len = 4096; + + cfg.data_len = nvme_feat_buf_len[cfg.feature_id]; + if (cfg.data_len) { if (posix_memalign(&buf, getpagesize(), cfg.data_len)) { fprintf(stderr, "can not allocate feature payload\n"); @@ -2964,22 +3632,33 @@ static int set_feature(int argc, char **argv, struct command *cmd, struct plugin } if (buf) { - if (strlen(cfg.file)) { - ffd = open(cfg.file, O_RDONLY); - if (ffd <= 0) { - fprintf(stderr, "Failed to open file %s: %s\n", - cfg.file, strerror(errno)); - err = -EINVAL; - goto free; - } - } - err = read(ffd, (void *)buf, cfg.data_len); - if (err < 0) { - err = -errno; - fprintf(stderr, "failed to read data buffer from input" - " file: %s\n", strerror(errno)); - goto close_ffd; - } + /* if feature ID is 0x0E, get timestamp value by -v option */ + if (NVME_FEAT_TIMESTAMP == cfg.feature_id && cfg.value) { + memcpy(buf, &cfg.value, NVME_FEAT_TIMESTAMP_DATA_SIZE); + } else { + if (strlen(cfg.file)) { + ffd = open(cfg.file, O_RDONLY); + if (ffd <= 0) { + fprintf(stderr, "Failed to open file %s: %s\n", + cfg.file, strerror(errno)); + err = -EINVAL; + goto free; + } + } + err = read(ffd, (void *)buf, cfg.data_len); + if (err < 0) { + err = -errno; + fprintf(stderr, "failed to read data buffer from input" + " file: %s\n", strerror(errno)); + goto close_ffd; + } + /* if feature ID is 0x0E, then change string from file to integer */ + if (NVME_FEAT_TIMESTAMP == cfg.feature_id) { + number = strtoul(buf, &endptr, STRTOUL_AUTO_BASE); + memset(buf, 0, cfg.data_len); + memcpy(buf, &number, NVME_FEAT_TIMESTAMP_DATA_SIZE); + } + } } err = nvme_set_feature(fd, cfg.namespace_id, cfg.feature_id, cfg.value, @@ -2987,8 +3666,12 @@ static int set_feature(int argc, char **argv, struct command *cmd, struct plugin if (err < 0) { perror("set-feature"); } else if (!err) { - printf("set-feature:%02x (%s), value:%#08x\n", cfg.feature_id, - nvme_feature_to_string(cfg.feature_id), cfg.value); + printf("set-feature:%#02x (%s), value:%#08"PRIx64", cdw12:%#08"PRIx32", \ + save:%#x\n", cfg.feature_id, nvme_feature_to_string(cfg.feature_id), + (uint64_t)cfg.value, cfg.cdw12, cfg.save); + if (cfg.feature_id == NVME_LBA_STATUS_INFO) { + nvme_show_lba_status_info(result); + } if (buf) { if (cfg.feature_id == NVME_FEAT_LBA_RANGE) nvme_show_lba_range((struct nvme_lba_range_type *)buf, @@ -3002,8 +3685,7 @@ static int set_feature(int argc, char **argv, struct command *cmd, struct plugin close_ffd: close(ffd); free: - if (buf) - free(buf); + free(buf); close_fd: close(fd); ret: @@ -3027,7 +3709,6 @@ static int sec_send(int argc, char **argv, struct command *cmd, struct plugin *p int err, fd, sec_fd = -1; void *sec_buf; unsigned int sec_size; - __u32 result; struct config { __u32 namespace_id; @@ -3089,13 +3770,13 @@ static int sec_send(int argc, char **argv, struct command *cmd, struct plugin *p } err = nvme_sec_send(fd, cfg.namespace_id, cfg.nssf, cfg.spsp, cfg.secp, - cfg.tl, sec_size, sec_buf, &result); + cfg.tl, sec_size, sec_buf); if (err < 0) perror("security-send"); else if (err != 0) - fprintf(stderr, "NVME Security Send Command Error:%d\n", err); + nvme_show_status(err); else - printf("NVME Security Send Command Success:%d\n", result); + printf("NVME Security Send Command Success\n"); free: free(sec_buf); @@ -3244,15 +3925,13 @@ static int dir_send(int argc, char **argv, struct command *cmd, struct plugin *p else d_raw(buf, cfg.data_len); } - } - else if (err > 0) + } else if (err > 0) nvme_show_status(err); close_ffd: close(ffd); free: - if (buf) - free(buf); + free(buf); close_fd: close(fd); ret: @@ -3292,9 +3971,9 @@ static int write_uncor(int argc, char **argv, struct command *cmd, struct plugin goto ret; if (!cfg.namespace_id) { - cfg.namespace_id = nvme_get_nsid(fd); - if (cfg.namespace_id < 0) { - err = cfg.namespace_id; + err = cfg.namespace_id = nvme_get_nsid(fd); + if (err < 0) { + perror("get-namespace-id"); goto close_fd; } } @@ -3384,9 +4063,9 @@ static int write_zeroes(int argc, char **argv, struct command *cmd, struct plugi if (cfg.deac) control |= NVME_RW_DEAC; if (!cfg.namespace_id) { - cfg.namespace_id = nvme_get_nsid(fd); - if (cfg.namespace_id < 0) { - err = cfg.namespace_id; + err = cfg.namespace_id = nvme_get_nsid(fd); + if (err < 0) { + perror("get-namespace-id"); goto close_fd; } } @@ -3477,16 +4156,16 @@ static int dsm(int argc, char **argv, struct command *cmd, struct plugin *plugin } if (!cfg.namespace_id) { - cfg.namespace_id = nvme_get_nsid(fd); - if (cfg.namespace_id < 0) { - err = cfg.namespace_id; + err = cfg.namespace_id = nvme_get_nsid(fd); + if (err < 0) { + perror("get-namespace-id"); goto close_fd; } } if (!cfg.cdw11) cfg.cdw11 = (cfg.ad << 2) | (cfg.idw << 1) | (cfg.idr << 0); - dsm = nvme_setup_dsm_range((__u32 *)ctx_attrs, (__u32 *)nlbs, (__u64 *)slbas, nr); + dsm = nvme_setup_dsm_range(ctx_attrs, nlbs, slbas, nr); if (!dsm) { fprintf(stderr, "failed to allocate data set payload\n"); err = -ENOMEM; @@ -3501,6 +4180,144 @@ static int dsm(int argc, char **argv, struct command *cmd, struct plugin *plugin else printf("NVMe DSM: success\n"); + free(dsm); + +close_fd: + close(fd); +ret: + return nvme_status_to_errno(err, false); +} + +static int copy(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "The Copy command is used by the host to copy data " + "from one or more source logical block ranges to a " + "single consecutive destination logical block " + "range."; + + const char *d_nsid = "identifier of desired namespace"; + const char *d_sdlba = "64-bit addr of first destination logical block"; + const char *d_slbas = "64-bit addr of first block per range (comma-separated list)"; + const char *d_nlbs = "number of blocks per range (comma-separated list, zeroes-based values)"; + const char *d_lr = "limited retry"; + const char *d_fua = "force unit access"; + const char *d_prinfor = "protection information and check field (read part)"; + const char *d_prinfow = "protection information and check field (write part)"; + const char *d_ilbrt = "initial lba reference tag (write part)"; + const char *d_eilbrts = "expected lba reference tags (read part, comma-separated list)"; + const char *d_lbat = "lba application tag (write part)"; + const char *d_elbats = "expected lba application tags (read part, comma-separated list)"; + const char *d_lbatm = "lba application tag mask (write part)"; + const char *d_elbatms = "expected lba application tag masks (read part, comma-separated list)"; + const char *d_dtype = "directive type (write part)"; + const char *d_dspec = "directive specific (write part)"; + const char *d_format = "source range entry format"; + + int err, fd; + uint16_t nr, nb, ns, nrts, natms, nats; + int nlbs[128] = { 0 }; + unsigned long long slbas[128] = {0,}; + int eilbrts[128] = { 0 }; + int elbatms[128] = { 0 }; + int elbats[128] = { 0 }; + struct nvme_copy_range *copy; + + struct config { + __u32 namespace_id; + __u64 sdlba; + char *nlbs; + char *slbas; + __u32 ilbrt; + char *eilbrts; + __u16 lbatm; + char *elbatms; + __u16 lbat; + char *elbats; + __u8 prinfow; + __u8 prinfor; + int lr; + int fua; + __u8 dtype; + __u16 dspec; + __u8 format; + }; + + struct config cfg = { + .namespace_id = 0, + .nlbs = "", + .slbas = "", + .eilbrts = "", + .elbatms = "", + .elbats = "", + }; + + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, d_nsid), + OPT_SUFFIX("sdlba", 'd', &cfg.sdlba, d_sdlba), + OPT_LIST("slbs", 's', &cfg.slbas, d_slbas), + OPT_LIST("blocks", 'b', &cfg.nlbs, d_nlbs), + OPT_FLAG("limited-retry", 'l', &cfg.lr, d_lr), + OPT_FLAG("force-unit-access", 'f', &cfg.fua, d_fua), + OPT_BYTE("prinfow", 'p', &cfg.prinfow, d_prinfow), + OPT_BYTE("prinfor", 'P', &cfg.prinfor, d_prinfor), + OPT_UINT("ref-tag", 'r', &cfg.ilbrt, d_ilbrt), + OPT_LIST("expected-ref-tags", 'R', &cfg.eilbrts, d_eilbrts), + OPT_SHRT("app-tag", 'a', &cfg.lbat, d_lbat), + OPT_LIST("expected-app-tags", 'A', &cfg.elbats, d_elbats), + OPT_SHRT("app-tag-mask", 'm', &cfg.lbatm, d_lbatm), + OPT_LIST("expected-app-tag-masks", 'M', &cfg.elbatms, d_elbatms), + OPT_BYTE("dir-type", 'T', &cfg.dtype, d_dtype), + OPT_SHRT("dir-spec", 'S', &cfg.dspec, d_dspec), + OPT_BYTE("format", 'F', &cfg.format, d_format), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) { + err = fd; + goto ret; + } + + nb = argconfig_parse_comma_sep_array(cfg.nlbs, nlbs, ARRAY_SIZE(nlbs)); + ns = argconfig_parse_comma_sep_array_long(cfg.slbas, slbas, ARRAY_SIZE(slbas)); + nrts = argconfig_parse_comma_sep_array(cfg.eilbrts, eilbrts, ARRAY_SIZE(eilbrts)); + natms = argconfig_parse_comma_sep_array(cfg.elbatms, elbatms, ARRAY_SIZE(elbatms)); + nats = argconfig_parse_comma_sep_array(cfg.elbats, elbats, ARRAY_SIZE(elbats)); + + nr = max(nb, max(ns, max(nrts, max(natms, nats)))); + if (!nr || nr > 128) { + fprintf(stderr, "invalid range\n"); + err = -EINVAL; + goto close_fd; + } + + if (!cfg.namespace_id) { + err = cfg.namespace_id = nvme_get_nsid(fd); + if (err < 0) { + perror("get-namespace-id"); + goto close_fd; + } + } + + copy = nvme_setup_copy_range(nlbs, slbas, eilbrts, elbatms, elbats, nr); + if (!copy) { + fprintf(stderr, "failed to allocate payload\n"); + err = -ENOMEM; + goto close_fd; + } + + err = nvme_copy(fd, cfg.namespace_id, copy, cfg.sdlba, nr, cfg.prinfor, + cfg.prinfow, cfg.dtype, cfg.dspec, cfg.format, cfg.lr, + cfg.fua, cfg.ilbrt, cfg.lbatm, cfg.lbat); + if (err < 0) + perror("NVMe Copy"); + else if (err != 0) + nvme_show_status(err); + else + printf("NVMe Copy: success\n"); + + free(copy); + close_fd: close(fd); ret: @@ -3535,9 +4352,9 @@ static int flush(int argc, char **argv, struct command *cmd, struct plugin *plug goto ret; if (!cfg.namespace_id) { - cfg.namespace_id = nvme_get_nsid(fd); - if (cfg.namespace_id < 0) { - err = cfg.namespace_id; + err = cfg.namespace_id = nvme_get_nsid(fd); + if (err < 0) { + perror("get-namespace-id"); goto close_fd; } } @@ -3603,9 +4420,9 @@ static int resv_acquire(int argc, char **argv, struct command *cmd, struct plugi goto ret; if (!cfg.namespace_id) { - cfg.namespace_id = nvme_get_nsid(fd); - if (cfg.namespace_id < 0) { - err = cfg.namespace_id; + err = cfg.namespace_id = nvme_get_nsid(fd); + if (err < 0) { + perror("get-namespace-id"); goto close_fd; } } @@ -3675,9 +4492,9 @@ static int resv_register(int argc, char **argv, struct command *cmd, struct plug goto ret; if (!cfg.namespace_id) { - cfg.namespace_id = nvme_get_nsid(fd); - if (cfg.namespace_id < 0) { - err = cfg.namespace_id; + err = cfg.namespace_id = nvme_get_nsid(fd); + if (err < 0) { + perror("get-namespace-id"); goto close_fd; } } @@ -3755,9 +4572,9 @@ static int resv_release(int argc, char **argv, struct command *cmd, struct plugi goto ret; if (!cfg.namespace_id) { - cfg.namespace_id = nvme_get_nsid(fd); - if (cfg.namespace_id < 0) { - err = cfg.namespace_id; + err = cfg.namespace_id = nvme_get_nsid(fd); + if (err < 0) { + perror("get-namespace-id"); goto close_fd; } } @@ -3833,9 +4650,9 @@ static int resv_report(int argc, char **argv, struct command *cmd, struct plugin flags = BINARY; if (!cfg.namespace_id) { - cfg.namespace_id = nvme_get_nsid(fd); - if (cfg.namespace_id < 0) { - err = cfg.namespace_id; + err = cfg.namespace_id = nvme_get_nsid(fd); + if (err < 0) { + perror("get-namespace-id"); goto close_fd; } } @@ -3858,7 +4675,7 @@ static int resv_report(int argc, char **argv, struct command *cmd, struct plugin if (!err) nvme_show_resv_report(status, size, cfg.cdw11, flags); else if (err > 0) - fprintf(stderr, "NVME IO command error:%04x\n", err); + nvme_show_status(err); else perror("reservation report"); free(status); @@ -3868,7 +4685,7 @@ ret: return nvme_status_to_errno(err, false); } -static unsigned long long elapsed_utime(struct timeval start_time, +unsigned long long elapsed_utime(struct timeval start_time, struct timeval end_time) { unsigned long long err = (end_time.tv_sec - start_time.tv_sec) * 1000000 + @@ -3886,10 +4703,12 @@ static int submit_io(int opcode, char *command, const char *desc, int flags = opcode & 1 ? O_RDONLY : O_WRONLY | O_CREAT; int mode = S_IRUSR | S_IWUSR |S_IRGRP | S_IWGRP| S_IROTH; __u16 control = 0; - __u32 dsmgmt = 0; - int phys_sector_size = 0; - long long buffer_size = 0; + __u32 dsmgmt = 0, nsid = 0; + int logical_block_size = 0; + long long buffer_size = 0, mbuffer_size = 0; bool huge; + struct nvme_id_ns ns; + __u8 lba_index, ms = 0; const char *start_block = "64-bit addr of first block to access"; const char *block_count = "number of blocks (zeroes based) on device to access"; @@ -4017,10 +4836,10 @@ static int submit_io(int opcode, char *command, const char *desc, goto close_mfd; } - if (ioctl(fd, BLKPBSZGET, &phys_sector_size) < 0) + if (ioctl(fd, BLKSSZGET, &logical_block_size) < 0) goto close_mfd; - buffer_size = (cfg.block_count + 1) * phys_sector_size; + buffer_size = (cfg.block_count + 1) * logical_block_size; if (cfg.data_size < buffer_size) { fprintf(stderr, "Rounding data size to fit block count (%lld bytes)\n", buffer_size); @@ -4030,20 +4849,41 @@ static int submit_io(int opcode, char *command, const char *desc, buffer = nvme_alloc(buffer_size, &huge); if (!buffer) { - fprintf(stderr, "can not allocate io payload\n"); + perror("can not allocate io payload\n"); err = -ENOMEM; goto close_mfd; } if (cfg.metadata_size) { - mbuffer = malloc(cfg.metadata_size); + err = nsid = nvme_get_nsid(fd); + if (err < 0) { + perror("get-namespace-id"); + goto close_mfd; + } + err = nvme_identify_ns(fd, nsid, false, &ns); + if (err) { + nvme_show_status(err); + goto free_buffer; + } else if (err < 0) { + perror("identify namespace"); + goto free_buffer; + } + lba_index = ns.flbas & NVME_NS_FLBAS_LBA_MASK; + ms = ns.lbaf[lba_index].ms; + mbuffer_size = (cfg.block_count + 1) * ms; + if (ms && cfg.metadata_size < mbuffer_size) { + fprintf(stderr, "Rounding metadata size to fit block count (%lld bytes)\n", + mbuffer_size); + } else { + mbuffer_size = cfg.metadata_size; + } + mbuffer = malloc(mbuffer_size); if (!mbuffer) { - fprintf(stderr, "can not allocate io metadata " - "payload: %s\n", strerror(errno)); + perror("can not allocate buf for io metadata payload\n"); err = -ENOMEM; goto free_buffer; } - memset(mbuffer, 0, cfg.metadata_size); + memset(mbuffer, 0, mbuffer_size); } if ((opcode & 1)) { @@ -4057,7 +4897,7 @@ static int submit_io(int opcode, char *command, const char *desc, } if ((opcode & 1) && cfg.metadata_size) { - err = read(mfd, (void *)mbuffer, cfg.metadata_size); + err = read(mfd, (void *)mbuffer, mbuffer_size); if (err < 0) { err = -errno; fprintf(stderr, "failed to read meta-data buffer from" @@ -4071,7 +4911,6 @@ static int submit_io(int opcode, char *command, const char *desc, printf("flags : %02x\n", 0); printf("control : %04x\n", control); printf("nblocks : %04x\n", cfg.block_count); - printf("rsvd : %04x\n", 0); printf("metadata : %"PRIx64"\n", (uint64_t)(uintptr_t)mbuffer); printf("addr : %"PRIx64"\n", (uint64_t)(uintptr_t)buffer); printf("slba : %"PRIx64"\n", (uint64_t)cfg.start_block); @@ -4100,7 +4939,7 @@ static int submit_io(int opcode, char *command, const char *desc, strerror(errno)); err = -EINVAL; } else if (!(opcode & 1) && cfg.metadata_size && - write(mfd, (void *)mbuffer, cfg.metadata_size) < 0) { + write(mfd, (void *)mbuffer, mbuffer_size) < 0) { fprintf(stderr, "write: %s: failed to write meta-data buffer to output file\n", strerror(errno)); err = -EINVAL; @@ -4109,8 +4948,7 @@ static int submit_io(int opcode, char *command, const char *desc, } free_mbuffer: - if (cfg.metadata_size) - free(mbuffer); + free(mbuffer); free_buffer: nvme_free(buffer, huge); close_mfd: @@ -4215,9 +5053,9 @@ static int verify_cmd(int argc, char **argv, struct command *cmd, struct plugin control |= NVME_RW_FUA; if (!cfg.namespace_id) { - cfg.namespace_id = nvme_get_nsid(fd); - if (cfg.namespace_id < 0) { - err = cfg.namespace_id; + err = cfg.namespace_id = nvme_get_nsid(fd); + if (err < 0) { + perror("get-namespace-id"); goto close_fd; } } @@ -4254,7 +5092,6 @@ static int sec_recv(int argc, char **argv, struct command *cmd, struct plugin *p const char *nssf = "NVMe Security Specific Field"; int err, fd; void *sec_buf = NULL; - __u32 result; struct config { __u32 namespace_id; @@ -4298,16 +5135,14 @@ static int sec_recv(int argc, char **argv, struct command *cmd, struct plugin *p } err = nvme_sec_recv(fd, cfg.namespace_id, cfg.nssf, cfg.spsp, - cfg.secp, cfg.al, cfg.size, sec_buf, &result); + cfg.secp, cfg.al, cfg.size, sec_buf); if (err < 0) perror("security receive"); else if (err != 0) - fprintf(stderr, "NVME Security Receive Command Error:%d\n", - err); + nvme_show_status(err); else { + printf("NVME Security Receive Command Success\n"); if (!cfg.raw_binary) { - printf("NVME Security Receive Command Success:%d\n", - result); d(sec_buf, cfg.size, 16, 1); } else if (cfg.size) d_raw((unsigned char *)sec_buf, cfg.size); @@ -4325,6 +5160,7 @@ static int get_lba_status(int argc, char **argv, struct command *cmd, struct plugin *plugin) { const char *desc = "Information about potentially unrecoverable LBAs."; + const char *namespace_id = "Desired Namespace"; const char *slba = "Starting LBA(SLBA) in 64-bit address of the first"\ " logical block addressed by this command"; const char *mndw = "Maximum Number of Dwords(MNDW) specifies maximum"\ @@ -4334,6 +5170,7 @@ static int get_lba_status(int argc, char **argv, struct command *cmd, " Status Descriptors to return."; const char *rl = "Range Length(RL) specifies the length of the range"\ " of contiguous LBAs beginning at SLBA"; + const char *timeout = "timeout value, in milliseconds"; enum nvme_print_flags flags; unsigned long buf_len; @@ -4341,26 +5178,32 @@ static int get_lba_status(int argc, char **argv, struct command *cmd, void *buf; struct config { + __u32 namespace_id; __u64 slba; __u32 mndw; __u8 atype; __u16 rl; + __u32 timeout; char *output_format; }; struct config cfg = { + .namespace_id = 0, .slba = 0, .mndw = 0, .atype = 0, .rl = 0, + .timeout = 0, .output_format = "normal", }; OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), OPT_SUFFIX("start-lba", 's', &cfg.slba, slba), OPT_UINT("max-dw", 'm', &cfg.mndw, mndw), OPT_BYTE("action", 'a', &cfg.atype, atype), OPT_SHRT("range-len", 'l', &cfg.rl, rl), + OPT_UINT("timeout", 't', &cfg.timeout, timeout), OPT_FMT("output-format", 'o', &cfg.output_format, output_format), OPT_END() }; @@ -4382,16 +5225,17 @@ static int get_lba_status(int argc, char **argv, struct command *cmd, buf_len = (cfg.mndw + 1) * 4; buf = calloc(1, buf_len); if (!buf) { + perror("could not alloc memory for get lba status"); err = -ENOMEM; goto close_fd; } - err = nvme_get_lba_status(fd, cfg.slba, cfg.mndw, cfg.atype, cfg.rl, - buf); + err = nvme_get_lba_status(fd, cfg.namespace_id, cfg.slba, cfg.mndw, + cfg.atype, cfg.rl, buf, cfg.timeout); if (!err) nvme_show_lba_status(buf, buf_len, flags); else if (err > 0) - fprintf(stderr, "NVME command error:%04x\n", err); + nvme_show_status(err); else perror("get lba status"); free(buf); @@ -4518,20 +5362,50 @@ static int dir_receive(int argc, char **argv, struct command *cmd, struct plugin else if (err < 0) perror("dir-receive"); - if (cfg.data_len) - free(buf); + free(buf); close_fd: close(fd); ret: return nvme_status_to_errno(err, false); } -static int passthru(int argc, char **argv, int ioctl_cmd, const char *desc, struct command *cmd) +/* rpmb_cmd_option is defined in nvme-rpmb.c */ +extern int rpmb_cmd_option(int, char **, struct command *, struct plugin *); +static int rpmb_cmd(int argc, char **argv, struct command *cmd, struct plugin *plugin) { + return rpmb_cmd_option(argc, argv, cmd, plugin); +} + +static int passthru(int argc, char **argv, int ioctl_cmd, uint8_t cmd_type, + const char *desc, struct command *cmd) +{ + const char *opcode = "opcode (required)"; + const char *flags = "command flags"; + const char *rsvd = "value for reserved field"; + const char *namespace_id = "desired namespace"; + const char *data_len = "data I/O length (bytes)"; + const char *metadata_len = "metadata seg. length (bytes)"; + const char *timeout = "timeout value, in milliseconds"; + const char *cdw2 = "command dword 2 value"; + const char *cdw3 = "command dword 3 value"; + const char *cdw10 = "command dword 10 value"; + const char *cdw11 = "command dword 11 value"; + const char *cdw12 = "command dword 12 value"; + const char *cdw13 = "command dword 13 value"; + const char *cdw14 = "command dword 14 value"; + const char *cdw15 = "command dword 15 value"; + const char *input = "write/send file (default stdin)"; + const char *raw_binary = "dump output in binary format"; + const char *show = "print command before sending"; + const char *dry = "show command instead of sending"; + const char *re = "set dataflow direction to receive"; + const char *wr = "set dataflow direction to send"; + const char *prefill = "prefill buffers with known byte-value, default 0"; void *data = NULL, *metadata = NULL; int err = 0, wfd = STDIN_FILENO, fd; __u32 result; bool huge; + const char *cmd_name = NULL; struct config { __u8 opcode; @@ -4578,29 +5452,6 @@ static int passthru(int argc, char **argv, int ioctl_cmd, const char *desc, stru .prefill = 0, }; - const char *opcode = "opcode (required)"; - const char *flags = "command flags"; - const char *rsvd = "value for reserved field"; - const char *namespace_id = "desired namespace"; - const char *data_len = "data I/O length (bytes)"; - const char *metadata_len = "metadata seg. length (bytes)"; - const char *timeout = "timeout value, in milliseconds"; - const char *cdw2 = "command dword 2 value"; - const char *cdw3 = "command dword 3 value"; - const char *cdw10 = "command dword 10 value"; - const char *cdw11 = "command dword 11 value"; - const char *cdw12 = "command dword 12 value"; - const char *cdw13 = "command dword 13 value"; - const char *cdw14 = "command dword 14 value"; - const char *cdw15 = "command dword 15 value"; - const char *input = "write/send file (default stdin)"; - const char *raw_binary = "dump output in binary format"; - const char *show = "print command before sending"; - const char *dry = "show command instead of sending"; - const char *re = "set dataflow direction to receive"; - const char *wr = "set dataflow direction to send"; - const char *prefill = "prefill buffers with known byte-value, default 0"; - OPT_ARGS(opts) = { OPT_BYTE("opcode", 'o', &cfg.opcode, opcode), OPT_BYTE("flags", 'f', &cfg.flags, flags), @@ -4631,7 +5482,7 @@ static int passthru(int argc, char **argv, int ioctl_cmd, const char *desc, stru if (fd < 0) goto ret; - if (strlen(cfg.input_file)){ + if (strlen(cfg.input_file)) { wfd = open(cfg.input_file, O_RDONLY, S_IRUSR | S_IRGRP | S_IROTH); if (wfd < 0) { @@ -4644,8 +5495,7 @@ static int passthru(int argc, char **argv, int ioctl_cmd, const char *desc, stru if (cfg.metadata_len) { metadata = malloc(cfg.metadata_len); if (!metadata) { - fprintf(stderr, "can not allocate metadata " - "payload: %s\n", strerror(errno)); + perror("can not allocate metadata payload\n"); err = -ENOMEM; goto close_wfd; } @@ -4654,7 +5504,7 @@ static int passthru(int argc, char **argv, int ioctl_cmd, const char *desc, stru if (cfg.data_len) { data = nvme_alloc(cfg.data_len, &huge); if (!data) { - fprintf(stderr, "can not allocate data payload\n"); + perror("can not allocate data payload\n"); err = -ENOMEM; goto free_metadata; } @@ -4714,19 +5564,21 @@ static int passthru(int argc, char **argv, int ioctl_cmd, const char *desc, stru else if (err) nvme_show_status(err); else { + cmd_name = nvme_cmd_to_string(cmd_type, cfg.opcode); + fprintf(stderr, "%s Command %s is Success and result: 0x%08x\n", + cmd_type ? "Admin": "IO", + strcmp(cmd_name, "Unknown") ? cmd_name: "Vendor Specific", + result); if (!cfg.raw_binary) { - fprintf(stderr, "NVMe command result:%08x\n", result); if (data && cfg.read && !err) d((unsigned char *)data, cfg.data_len, 16, 1); } else if (data && cfg.read) d_raw((unsigned char *)data, cfg.data_len); } free_data: - if (cfg.data_len) - nvme_free(data, huge); + nvme_free(data, huge); free_metadata: - if (cfg.metadata_len) - free(metadata); + free(metadata); close_wfd: if (strlen(cfg.input_file)) close(wfd); @@ -4740,35 +5592,42 @@ static int io_passthru(int argc, char **argv, struct command *cmd, struct plugin { const char *desc = "Send a user-defined IO command to the specified "\ "device via IOCTL passthrough, return results."; - return passthru(argc, argv, NVME_IOCTL_IO_CMD, desc, cmd); + return passthru(argc, argv, NVME_IOCTL_IO_CMD, 0, desc, cmd); } static int admin_passthru(int argc, char **argv, struct command *cmd, struct plugin *plugin) { const char *desc = "Send a user-defined Admin command to the specified "\ "device via IOCTL passthrough, return results."; - return passthru(argc, argv, NVME_IOCTL_ADMIN_CMD, desc, cmd); + return passthru(argc, argv, NVME_IOCTL_ADMIN_CMD, 1, desc, cmd); } -#ifdef LIBUUID static int gen_hostnqn_cmd(int argc, char **argv, struct command *command, struct plugin *plugin) { - uuid_t uuid; + int ret; char uuid_str[37]; /* e.g. 1b4e28ba-2fa1-11d2-883f-0016d3cca427 + \0 */ +#ifdef LIBUUID + uuid_t uuid; +#endif - uuid_generate_random(uuid); - uuid_unparse_lower(uuid, uuid_str); + ret = uuid_from_dmi(uuid_str); + if (ret < 0) + ret = uuid_from_systemd(uuid_str); +#ifdef LIBUUID + if (ret < 0) { + uuid_generate_random(uuid); + uuid_unparse_lower(uuid, uuid_str); + ret = 0; + } +#endif + if (ret < 0) { + fprintf(stderr, "\"%s\" not supported. Install lib uuid and rebuild.\n", + command->name); + return -ENOTSUP; + } printf("nqn.2014-08.org.nvmexpress:uuid:%s\n", uuid_str); return 0; } -#else -static int gen_hostnqn_cmd(int argc, char **argv, struct command *command, struct plugin *plugin) -{ - fprintf(stderr, "\"%s\" not supported. Install lib uuid and rebuild.\n", - command->name); - return -ENOTSUP; -} -#endif static int show_hostnqn_cmd(int argc, char **argv, struct command *command, struct plugin *plugin) { diff --git a/nvme.h b/nvme.h index 017148a..e33094d 100644 --- a/nvme.h +++ b/nvme.h @@ -19,9 +19,35 @@ #include #include #include +#include #include "plugin.h" +#ifdef LIBJSONC +#include + +#define json_create_object(o) json_object_new_object(o) +#define json_create_array(a) json_object_new_array(a) +#define json_free_object(o) json_object_put(o) +#define json_free_array(a) json_object_put(a) +#define json_object_add_value_uint(o, k, v) \ + json_object_object_add(o, k, json_object_new_int(v)) +#define json_object_add_value_int(o, k, v) \ + json_object_object_add(o, k, json_object_new_int(v)) +#define json_object_add_value_float(o, k, v) \ + json_object_object_add(o, k, json_object_new_double(v)) +#define json_object_add_value_string(o, k, v) \ + json_object_object_add(o, k, json_object_new_string(v)) +#define json_object_add_value_array(o, k, v) \ + json_object_object_add(o, k, v) +#define json_object_add_value_object(o, k, v) \ + json_object_object_add(o, k, v) +#define json_array_add_value_object(o, k) \ + json_object_array_add(o, k) +#define json_print_object(o, u) \ + printf("%s", json_object_to_json_string_ext(o, JSON_C_TO_STRING_PRETTY)) +#else #include "util/json.h" +#endif #include "util/argconfig.h" #include "linux/nvme.h" @@ -46,6 +72,7 @@ struct nvme_namespace { struct nvme_ctrl { char *name; + char *path; struct nvme_subsystem *subsys; char *address; @@ -87,14 +114,16 @@ int parse_and_open(int argc, char **argv, const char *desc, const struct argconfig_commandline_options *clo); extern const char *devicename; +extern const char *output_format; -enum nvme_print_flags validate_output_format(char *format); +enum nvme_print_flags validate_output_format(const char *format); int __id_ctrl(int argc, char **argv, struct command *cmd, struct plugin *plugin, void (*vs)(__u8 *vs, struct json_object *root)); char *nvme_char_from_block(char *block); void *mmap_registers(const char *dev); extern int current_index; +int scan_ctrl_namespace_filter(const struct dirent *d); int scan_namespace_filter(const struct dirent *d); int scan_ctrl_paths_filter(const struct dirent *d); int scan_ctrls_filter(const struct dirent *d); @@ -102,9 +131,17 @@ int scan_subsys_filter(const struct dirent *d); int scan_dev_filter(const struct dirent *d); int scan_subsystems(struct nvme_topology *t, const char *subsysnqn, - __u32 ns_instance); + __u32 ns_instance, int nsid, char *dev_dir); void free_topology(struct nvme_topology *t); char *get_nvme_subsnqn(char *path); -char *nvme_get_ctrl_attr(char *path, const char *attr); +char *nvme_get_ctrl_attr(const char *path, const char *attr); +void *nvme_alloc(size_t len, bool *huge); +void nvme_free(void *p, bool huge); + +int uuid_from_dmi(char *uuid); +int uuid_from_systemd(char *uuid); + +unsigned long long elapsed_utime(struct timeval start_time, + struct timeval end_time); #endif /* _NVME_H */ diff --git a/nvme.spec.in b/nvme.spec.in index 74e681e..7154a87 100644 --- a/nvme.spec.in +++ b/nvme.spec.in @@ -7,7 +7,7 @@ Group: Development/Tools URL: https://github.com/linux-nvme/nvme-cli/ Source: nvme-@@VERSION@@.tar.gz Provides: nvme -Requires(post): util-linux +Requires(post): util-linux systemd systemd-udev BuildRoot: %{_tmppath}/%{name}-%{version}-root %description @@ -49,7 +49,7 @@ rm -rf $RPM_BUILD_ROOT %post if [ $1 -eq 1 ]; then # 1 : This package is being installed for the first time if [ ! -s %{_sysconfdir}/nvme/hostnqn ]; then - echo $(nvme gen-hostnqn) > %{_sysconfdir}/nvme/hostnqn + echo $(%{_sbindir}/nvme gen-hostnqn) > %{_sysconfdir}/nvme/hostnqn fi if [ ! -s %{_sysconfdir}/nvme/hostid ]; then uuidgen > %{_sysconfdir}/nvme/hostid diff --git a/nvmf-autoconnect/dracut-conf/70-nvmf-autoconnect.conf.in b/nvmf-autoconnect/dracut-conf/70-nvmf-autoconnect.conf.in index 41ea83c..b32f2c3 100644 --- a/nvmf-autoconnect/dracut-conf/70-nvmf-autoconnect.conf.in +++ b/nvmf-autoconnect/dracut-conf/70-nvmf-autoconnect.conf.in @@ -1 +1 @@ -install_items+="@@UDEVRULESDIR@@/70-nvmf-autoconnect.rules" +install_items+=" @@UDEVRULESDIR@@/70-nvmf-autoconnect.rules " diff --git a/plugins/amzn/amzn-nvme.c b/plugins/amzn/amzn-nvme.c new file mode 100644 index 0000000..47a2d82 --- /dev/null +++ b/plugins/amzn/amzn-nvme.c @@ -0,0 +1,59 @@ +#include +#include +#include +#include +#include +#include + +#include "linux/nvme_ioctl.h" + +#include "common.h" +#include "nvme.h" +#include "nvme-print.h" +#include "nvme-ioctl.h" +#include "plugin.h" + +#include "argconfig.h" +#include "suffix.h" + +#define CREATE_CMD +#include "amzn-nvme.h" + +struct nvme_vu_id_ctrl_field { + __u8 bdev[32]; + __u8 reserved0[992]; +}; + +static void json_amzn_id_ctrl(struct nvme_vu_id_ctrl_field *id, + char *bdev, + struct json_object *root) +{ + json_object_add_value_string(root, "bdev", bdev); +} + +static void amzn_id_ctrl(__u8 *vs, struct json_object *root) +{ + struct nvme_vu_id_ctrl_field* id = (struct nvme_vu_id_ctrl_field *)vs; + + char bdev[32] = { 0 }; + + int len = 0; + while (len < 31) { + if (id->bdev[++len] == ' ') { + break; + } + } + snprintf(bdev, len+1, "%s", id->bdev); + + if (root) { + json_amzn_id_ctrl(id, bdev, root); + return; + } + + printf("bdev : %s\n", bdev); +} + +static int id_ctrl(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + return __id_ctrl(argc, argv, cmd, plugin, amzn_id_ctrl); +} diff --git a/plugins/amzn/amzn-nvme.h b/plugins/amzn/amzn-nvme.h new file mode 100644 index 0000000..9b8d797 --- /dev/null +++ b/plugins/amzn/amzn-nvme.h @@ -0,0 +1,17 @@ +#undef CMD_INC_FILE +#define CMD_INC_FILE plugins/amzn/amzn-nvme + +#if !defined(AMZN_NVME) || defined(CMD_HEADER_MULTI_READ) +#define AMZN_NVME + +#include "cmd.h" + +PLUGIN(NAME("amzn", "Amazon vendor specific extensions"), + COMMAND_LIST( + ENTRY("id-ctrl", "Send NVMe Identify Controller", id_ctrl) + ) +); + +#endif + +#include "define_cmd.h" diff --git a/plugins/dera/dera-nvme.c b/plugins/dera/dera-nvme.c index dbb40cc..be78930 100644 --- a/plugins/dera/dera-nvme.c +++ b/plugins/dera/dera-nvme.c @@ -131,7 +131,8 @@ static int get_status(int argc, char **argv, struct command *cmd, struct plugin if (fd < 0) return fd; - err = nvme_get_log(fd, 0xffffffff, 0xc0, false, sizeof(log), &log); + err = nvme_get_log(fd, 0xffffffff, 0xc0, false, NVME_NO_LOG_LSP, + sizeof(log), &log); if (err) { goto exit; } diff --git a/plugins/huawei/huawei-nvme.c b/plugins/huawei/huawei-nvme.c index 482ea0d..b39d861 100644 --- a/plugins/huawei/huawei-nvme.c +++ b/plugins/huawei/huawei-nvme.c @@ -32,7 +32,6 @@ #include "nvme-print.h" #include "nvme-ioctl.h" #include "plugin.h" -#include "json.h" #include "argconfig.h" #include "suffix.h" @@ -143,7 +142,7 @@ static void huawei_json_print_list_items(struct huawei_list_item *list_items, unsigned len) { struct json_object *root; - struct json_array *devices; + struct json_object *devices; struct json_object *device_attrs; char formatter[128] = { 0 }; int index, i = 0; diff --git a/plugins/intel/intel-nvme.c b/plugins/intel/intel-nvme.c index 8217c46..27b065d 100644 --- a/plugins/intel/intel-nvme.c +++ b/plugins/intel/intel-nvme.c @@ -11,7 +11,6 @@ #include "nvme.h" #include "nvme-print.h" #include "nvme-ioctl.h" -#include "json.h" #include "plugin.h" #include "argconfig.h" @@ -54,6 +53,13 @@ struct nvme_additional_smart_log { struct nvme_additional_smart_log_item pll_lock_loss_cnt; struct nvme_additional_smart_log_item nand_bytes_written; struct nvme_additional_smart_log_item host_bytes_written; + struct nvme_additional_smart_log_item host_ctx_wear_used; + struct nvme_additional_smart_log_item perf_stat_indicator; + struct nvme_additional_smart_log_item re_alloc_sectr_cnt; + struct nvme_additional_smart_log_item soft_ecc_err_rate; + struct nvme_additional_smart_log_item unexp_power_loss; + struct nvme_additional_smart_log_item media_bytes_read; + struct nvme_additional_smart_log_item avail_fw_downgrades; }; struct nvme_vu_id_ctrl_field { /* CDR MR5 */ @@ -218,60 +224,118 @@ static void show_intel_smart_log_jsn(struct nvme_additional_smart_log *smart, json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->host_bytes_written.raw)); json_object_add_value_object(dev_stats, "host_bytes_written", entry_stats); + entry_stats = json_create_object(); + json_object_add_value_int(entry_stats, "normalized", smart->host_ctx_wear_used.norm); + json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->host_ctx_wear_used.raw)); + json_object_add_value_object(dev_stats, "host_ctx_wear_used", entry_stats); + + entry_stats = json_create_object(); + json_object_add_value_int(entry_stats, "normalized", smart->perf_stat_indicator.norm); + json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->perf_stat_indicator.raw)); + json_object_add_value_object(dev_stats, "perf_stat_indicator", entry_stats); + + entry_stats = json_create_object(); + json_object_add_value_int(entry_stats, "normalized", smart->re_alloc_sectr_cnt.norm); + json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->re_alloc_sectr_cnt.raw)); + json_object_add_value_object(dev_stats, "re_alloc_sectr_cnt", entry_stats); + + entry_stats = json_create_object(); + json_object_add_value_int(entry_stats, "normalized", smart->soft_ecc_err_rate.norm); + json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->soft_ecc_err_rate.raw)); + json_object_add_value_object(dev_stats, "soft_ecc_err_rate", entry_stats); + + entry_stats = json_create_object(); + json_object_add_value_int(entry_stats, "normalized", smart->unexp_power_loss.norm); + json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->unexp_power_loss.raw)); + json_object_add_value_object(dev_stats, "unexp_power_loss", entry_stats); + + entry_stats = json_create_object(); + json_object_add_value_int(entry_stats, "normalized", smart->media_bytes_read.norm); + json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->media_bytes_read.raw)); + json_object_add_value_object(dev_stats, "media_bytes_read", entry_stats); + + entry_stats = json_create_object(); + json_object_add_value_int(entry_stats, "normalized", smart->avail_fw_downgrades.norm); + json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->avail_fw_downgrades.raw)); + json_object_add_value_object(dev_stats, "avail_fw_downgrades", entry_stats); + json_object_add_value_object(root, "Device stats", dev_stats); json_print_object(root, NULL); json_free_object(root); } +static char *id_to_key(__u8 id) +{ + switch (id) { + case 0xAB: + return "program_fail_count"; + case 0xAC: + return "erase_fail_count"; + case 0xAD: + return "wear_leveling_count"; + case 0xB8: + return "e2e_error_detect_count"; + case 0xC7: + return "crc_error_count"; + case 0xE2: + return "media_wear_percentage"; + case 0xE3: + return "host_reads"; + case 0xE4: + return "timed_work_load"; + case 0xEA: + return "thermal_throttle_status"; + case 0xF0: + return "retry_buff_overflow_count"; + case 0xF3: + return "pll_lock_loss_counter"; + case 0xF4: + return "nand_bytes_written"; + case 0xF5: + return "host_bytes_written"; + case 0xF6: + return "host_context_wear_used"; + case 0xF7: + return "performance_status_indicator"; + case 0xF8: + return "media_bytes_read"; + case 0xF9: + return "available_fw_downgrades"; + case 0x05: + return "re-allocated_sector_count"; + case 0x0D: + return "soft_ecc_error_rate"; + case 0xAE: + return "unexpected_power_loss"; + default: + return "Invalid ID"; + } +} + +static void print_intel_smart_log_items(struct nvme_additional_smart_log_item *item) +{ + if (!item->key) + return; + + printf("%#x %-45s %3d %"PRIu64"\n", + item->key, id_to_key(item->key), + item->norm, int48_to_long(item->raw)); +} + static void show_intel_smart_log(struct nvme_additional_smart_log *smart, unsigned int nsid, const char *devname) { + struct nvme_additional_smart_log_item *iter = &smart->program_fail_cnt; + int num_items = sizeof(struct nvme_additional_smart_log) / + sizeof(struct nvme_additional_smart_log_item); + printf("Additional Smart Log for NVME device:%s namespace-id:%x\n", devname, nsid); - printf("key normalized raw\n"); - printf("program_fail_count : %3d%% %"PRIu64"\n", - smart->program_fail_cnt.norm, - int48_to_long(smart->program_fail_cnt.raw)); - printf("erase_fail_count : %3d%% %"PRIu64"\n", - smart->erase_fail_cnt.norm, - int48_to_long(smart->erase_fail_cnt.raw)); - printf("wear_leveling : %3d%% min: %u, max: %u, avg: %u\n", - smart->wear_leveling_cnt.norm, - le16_to_cpu(smart->wear_leveling_cnt.wear_level.min), - le16_to_cpu(smart->wear_leveling_cnt.wear_level.max), - le16_to_cpu(smart->wear_leveling_cnt.wear_level.avg)); - printf("end_to_end_error_detection_count: %3d%% %"PRIu64"\n", - smart->e2e_err_cnt.norm, - int48_to_long(smart->e2e_err_cnt.raw)); - printf("crc_error_count : %3d%% %"PRIu64"\n", - smart->crc_err_cnt.norm, - int48_to_long(smart->crc_err_cnt.raw)); - printf("timed_workload_media_wear : %3d%% %.3f%%\n", - smart->timed_workload_media_wear.norm, - ((float)int48_to_long(smart->timed_workload_media_wear.raw)) / 1024); - printf("timed_workload_host_reads : %3d%% %"PRIu64"%%\n", - smart->timed_workload_host_reads.norm, - int48_to_long(smart->timed_workload_host_reads.raw)); - printf("timed_workload_timer : %3d%% %"PRIu64" min\n", - smart->timed_workload_timer.norm, - int48_to_long(smart->timed_workload_timer.raw)); - printf("thermal_throttle_status : %3d%% %u%%, cnt: %u\n", - smart->thermal_throttle_status.norm, - smart->thermal_throttle_status.thermal_throttle.pct, - smart->thermal_throttle_status.thermal_throttle.count); - printf("retry_buffer_overflow_count : %3d%% %"PRIu64"\n", - smart->retry_buffer_overflow_cnt.norm, - int48_to_long(smart->retry_buffer_overflow_cnt.raw)); - printf("pll_lock_loss_count : %3d%% %"PRIu64"\n", - smart->pll_lock_loss_cnt.norm, - int48_to_long(smart->pll_lock_loss_cnt.raw)); - printf("nand_bytes_written : %3d%% sectors: %"PRIu64"\n", - smart->nand_bytes_written.norm, - int48_to_long(smart->nand_bytes_written.raw)); - printf("host_bytes_written : %3d%% sectors: %"PRIu64"\n", - smart->host_bytes_written.norm, - int48_to_long(smart->host_bytes_written.raw)); + printf("ID KEY Normalized Raw\n"); + + for (int i = 0; i < num_items; i++, iter++) + print_intel_smart_log_items(iter); } static int get_additional_smart_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) @@ -307,7 +371,7 @@ static int get_additional_smart_log(int argc, char **argv, struct command *cmd, return fd; err = nvme_get_log(fd, cfg.namespace_id, 0xca, false, - sizeof(smart_log), &smart_log); + NVME_NO_LOG_LSP, sizeof(smart_log), &smart_log); if (!err) { if (cfg.json) show_intel_smart_log_jsn(&smart_log, cfg.namespace_id, devicename); @@ -347,7 +411,7 @@ static int get_market_log(int argc, char **argv, struct command *cmd, struct plu return fd; err = nvme_get_log(fd, NVME_NSID_ALL, 0xdd, false, - sizeof(log), log); + NVME_NO_LOG_LSP, sizeof(log), log); if (!err) { if (!cfg.raw_binary) printf("Intel Marketing Name Log:\n%s\n", log); @@ -409,7 +473,7 @@ static int get_temp_stats_log(int argc, char **argv, struct command *cmd, struct return fd; err = nvme_get_log(fd, NVME_NSID_ALL, 0xc5, false, - sizeof(stats), &stats); + NVME_NO_LOG_LSP, sizeof(stats), &stats); if (!err) { if (!cfg.raw_binary) show_temp_stats(&stats); @@ -427,6 +491,28 @@ struct intel_lat_stats { __u32 data[1216]; }; +struct __attribute__((__packed__)) optane_lat_stats { + __u16 maj; + __u16 min; + __u64 data[9]; +}; + +#define MEDIA_MAJOR_IDX 0 +#define MEDIA_MINOR_IDX 1 +#define MEDIA_MAX_LEN 2 +#define OPTANE_V1000_BUCKET_LEN 8 + +static struct intel_lat_stats stats; +static struct optane_lat_stats v1000_stats; + +struct v1000_thresholds { + __u32 read[OPTANE_V1000_BUCKET_LEN]; + __u32 write[OPTANE_V1000_BUCKET_LEN]; +}; + +static struct v1000_thresholds v1000_bucket; +static __u16 media_version[MEDIA_MAX_LEN] = {0}; + enum FormatUnit { US, MS, @@ -536,6 +622,28 @@ static void show_lat_stats_bucket(struct intel_lat_stats *stats, printf("%-*d\n", COL_WIDTH, stats->data[i]); } +static void show_optane_lat_stats_bucket(struct optane_lat_stats *stats, + __u32 lower_us, enum inf_bound_type start_type, + __u32 upper_us, enum inf_bound_type end_type, int i) +{ + enum FormatUnit fu = S; + char buffer[BUFSIZE]; + + init_buffer(buffer, BUFSIZE); + printf("%-*d", COL_WIDTH, i); + + fu = get_seconds_magnitude(lower_us); + set_unit_string(buffer, lower_us, fu, start_type); + printf("%-*s", COL_WIDTH, buffer); + + fu = get_seconds_magnitude(upper_us); + set_unit_string(buffer, upper_us, fu, end_type); + printf("%-*s", COL_WIDTH, buffer); + + printf("%-*lu\n", COL_WIDTH, (long unsigned int)stats->data[i]); +} + + static void show_lat_stats_linear(struct intel_lat_stats *stats, __u32 start_offset, __u32 end_offset, __u32 bytes_per, __u32 us_step, bool nonzero_print) @@ -622,6 +730,31 @@ static void json_add_bucket(struct intel_lat_stats *stats, json_object_add_value_int(bucket, "value", val); } +static void json_add_bucket_optane(struct json_object *bucket_list, __u32 id, + __u32 lower_us, enum inf_bound_type start_type, + __u32 upper_us, enum inf_bound_type end_type, __u32 val) +{ + char buffer[BUFSIZE]; + struct json_object *bucket = json_create_object(); + + init_buffer(buffer, BUFSIZE); + + json_object_add_value_object(bucket_list, + "bucket", bucket); + json_object_add_value_int(bucket, "id", id); + + set_unit_string(buffer, lower_us, + get_seconds_magnitude(lower_us), start_type); + json_object_add_value_string(bucket, "start", buffer); + + set_unit_string(buffer, upper_us, + get_seconds_magnitude(upper_us), end_type); + json_object_add_value_string(bucket, "end", buffer); + + json_object_add_value_uint(bucket, "value", val); + +} + static void json_lat_stats_linear(struct intel_lat_stats *stats, struct json_object *bucket_list, __u32 start_offset, __u32 end_offset, __u32 bytes_per, @@ -714,32 +847,121 @@ static void show_lat_stats_4_0(struct intel_lat_stats *stats) } } -static void json_lat_stats(struct intel_lat_stats *stats, int write) +static void jason_lat_stats_v1000_0(struct optane_lat_stats *stats, int write) +{ + int i; + struct json_object *root = json_create_object(); + struct json_object *bucket_list = json_create_object(); + + lat_stats_make_json_root(root, bucket_list, write); + + if (write) { + for (i = 0; i < OPTANE_V1000_BUCKET_LEN - 1; i++) + json_add_bucket_optane(bucket_list, i, + v1000_bucket.write[i], NOINF, + v1000_bucket.write[i + 1] - 1, + NOINF, + stats->data[i]); + + json_add_bucket_optane(bucket_list, + OPTANE_V1000_BUCKET_LEN - 1, + v1000_bucket.write[i], + NOINF, + v1000_bucket.write[i], + POSINF, stats->data[i]); + } else { + for (i = 0; i < OPTANE_V1000_BUCKET_LEN - 1; i++) + json_add_bucket_optane(bucket_list, i, + v1000_bucket.read[i], NOINF, + v1000_bucket.read[i + 1] - 1, + NOINF, + stats->data[i]); + + json_add_bucket_optane(bucket_list, + OPTANE_V1000_BUCKET_LEN - 1, + v1000_bucket.read[i], + NOINF, + v1000_bucket.read[i], + POSINF, stats->data[i]); + } + + struct json_object *subroot = json_create_object(); + json_object_add_value_object(root, "Average latency since last reset", subroot); + + json_object_add_value_uint(subroot, "value in us", stats->data[8]); + + json_print_object(root, NULL); + json_free_object(root); + +} + +static void show_lat_stats_v1000_0(struct optane_lat_stats *stats, int write) +{ + int i; + if (write) { + for (i = 0; i < OPTANE_V1000_BUCKET_LEN - 1; i++) + show_optane_lat_stats_bucket(stats, + v1000_bucket.write[i], + NOINF, + v1000_bucket.write[i + 1] -1, + NOINF, i); + + show_optane_lat_stats_bucket(stats, v1000_bucket.write[i], + NOINF, v1000_bucket.write[i], + POSINF, i); + } else { + for (i = 0; i < OPTANE_V1000_BUCKET_LEN - 1; i++) + show_optane_lat_stats_bucket(stats, + v1000_bucket.read[i], + NOINF, + v1000_bucket.read[i + 1] -1, + NOINF, i); + + show_optane_lat_stats_bucket(stats, v1000_bucket.read[i], + NOINF, v1000_bucket.read[i], + POSINF, i); + } + + printf("Average latency since last reset: %lu us\n", (long unsigned int)stats->data[8]); + +} + +static void json_lat_stats(int write) { - switch (stats->maj) { + switch (media_version[MEDIA_MAJOR_IDX]) { case 3: - json_lat_stats_3_0(stats, write); + json_lat_stats_3_0(&stats, write); break; case 4: - switch (stats->min) { + switch (media_version[MEDIA_MINOR_IDX]) { case 0: case 1: case 2: case 3: case 4: case 5: - json_lat_stats_4_0(stats, write); + json_lat_stats_4_0(&stats, write); break; default: - printf(("Unsupported minor revision (%u.%u)\n" - "Defaulting to format for rev4.0"), - stats->maj, stats->min); + printf("Unsupported minor revision (%u.%u)\n", + stats.maj, stats.min); + break; + } + break; + case 1000: + switch (media_version[MEDIA_MINOR_IDX]) { + case 0: + jason_lat_stats_v1000_0(&v1000_stats, write); + break; + default: + printf("Unsupported minor revision (%u.%u)\n", + stats.maj, stats.min); break; } break; default: printf("Unsupported revision (%u.%u)\n", - stats->maj, stats->min); + stats.maj, stats.min); break; } printf("\n"); @@ -752,69 +974,79 @@ static void print_dash_separator(int count) putchar('\n'); } -static void show_lat_stats(struct intel_lat_stats *stats, int write) +static void show_lat_stats(int write) { static const int separator_length = 50; printf("Intel IO %s Command Latency Statistics\n", write ? "Write" : "Read"); printf("Major Revision : %u\nMinor Revision : %u\n", - stats->maj, stats->min); + media_version[MEDIA_MAJOR_IDX], media_version[MEDIA_MINOR_IDX]); print_dash_separator(separator_length); printf("%-12s%-12s%-12s%-20s\n", "Bucket", "Start", "End", "Value"); print_dash_separator(separator_length); - switch (stats->maj) { + switch (media_version[MEDIA_MAJOR_IDX]) { case 3: - show_lat_stats_3_0(stats); + show_lat_stats_3_0(&stats); break; case 4: - switch (stats->min) { + switch (media_version[MEDIA_MINOR_IDX]) { case 0: case 1: case 2: case 3: case 4: case 5: - show_lat_stats_4_0(stats); + show_lat_stats_4_0(&stats); break; default: - printf(("Unsupported minor revision (%u.%u)\n" - "Defaulting to format for rev4.0"), - stats->maj, stats->min); + printf("Unsupported minor revision (%u.%u)\n", + stats.maj, stats.min); + break; + } + break; + case 1000: + switch (media_version[MEDIA_MINOR_IDX]) { + case 0: + show_lat_stats_v1000_0(&v1000_stats, write); + break; + default: + printf("Unsupported minor revision (%u.%u)\n", + stats.maj, stats.min); break; } break; default: printf("Unsupported revision (%u.%u)\n", - stats->maj, stats->min); + stats.maj, stats.min); break; } } static int get_lat_stats_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) { - struct intel_lat_stats stats; - enum nvme_print_flags flags; + int err, fd; const char *desc = "Get Intel Latency Statistics log and show it."; - const char *raw = "dump output in binary format"; + const char *raw = "Dump output in binary format"; + const char *json= "Dump output in json format"; const char *write = "Get write statistics (read default)"; + struct config { - char *output_format; int raw_binary; + int json; int write; }; struct config cfg = { - .output_format = "normal", }; OPT_ARGS(opts) = { - OPT_FLAG("write", 'w', &cfg.write, write), - OPT_FMT("output-format", 'o', &cfg.output_format, "Output format: normal|json|binary"), - OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), + OPT_FLAG("write", 'w', &cfg.write, write), + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), + OPT_FLAG("json", 'j', &cfg.json, json), OPT_END() }; @@ -822,26 +1054,54 @@ static int get_lat_stats_log(int argc, char **argv, struct command *cmd, struct if (fd < 0) return fd; - err = flags = validate_output_format(cfg.output_format); - if (flags < 0) + /* Query maj and minor version first */ + err = nvme_get_log(fd, NVME_NSID_ALL, cfg.write ? 0xc2 : 0xc1, + false, NVME_NO_LOG_LSP, sizeof(media_version), + media_version); + if (err) goto close_fd; - if (cfg.raw_binary) - flags = BINARY; + if (media_version[0] == 1000) { + __u32 thresholds[OPTANE_V1000_BUCKET_LEN] = {0}; + __u32 result; + + err = nvme_get_feature(fd, 0, 0xf7, 0, cfg.write ? 0x1 : 0x0, + sizeof(thresholds), thresholds, &result); + if (err) { + fprintf(stderr, "Quering thresholds failed. NVMe Status:%s(%x)\n", + nvme_status_to_string(err), err); + goto close_fd; + } + + /* Update bucket thresholds to be printed */ + if (cfg.write) { + for (int i = 0; i < OPTANE_V1000_BUCKET_LEN; i++) + v1000_bucket.write[i] = thresholds[i]; + } else { + for (int i = 0; i < OPTANE_V1000_BUCKET_LEN; i++) + v1000_bucket.read[i] = thresholds[i]; + + } + + err = nvme_get_log(fd, NVME_NSID_ALL, cfg.write ? 0xc2 : 0xc1, + false, NVME_NO_LOG_LSP, sizeof(v1000_stats), + &v1000_stats); + } else { + err = nvme_get_log(fd, NVME_NSID_ALL, cfg.write ? 0xc2 : 0xc1, + false, NVME_NO_LOG_LSP, sizeof(stats), + &stats); + } - err = nvme_get_log(fd, NVME_NSID_ALL, cfg.write ? 0xc2 : 0xc1, - false, sizeof(stats), &stats); if (!err) { - if (flags & JSON) - json_lat_stats(&stats, cfg.write); - else if (flags & BINARY) - d_raw((unsigned char *)&stats, sizeof(stats)); + if (cfg.json) + json_lat_stats(cfg.write); + else if (!cfg.raw_binary) + show_lat_stats(cfg.write); else - show_lat_stats(&stats, cfg.write); + d_raw((unsigned char *)&stats, sizeof(stats)); } else if (err > 0) fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(err), err); - close_fd: close(fd); return err; @@ -1314,3 +1574,84 @@ static int enable_lat_stats_tracking(int argc, char **argv, } return fd; } + +static int set_lat_stats_thresholds(int argc, char **argv, + struct command *command, struct plugin *plugin) +{ + int err, fd, num; + const char *desc = "Write Intel Bucket Thresholds for Latency Statistics Tracking"; + const char *bucket_thresholds = "Bucket Threshold List, comma separated list: 0, 10, 20 ..."; + const char *write = "Set write bucket Thresholds for latency tracking (read default)"; + + const __u32 nsid = 0; + const __u8 fid = 0xf7; + const __u32 cdw12 = 0x0; + const __u32 save = 0; + __u32 result; + + struct config { + int write; + char *bucket_thresholds; + }; + + struct config cfg = { + .write = 0, + .bucket_thresholds = "", + }; + + OPT_ARGS(opts) = { + OPT_FLAG("write", 'w', &cfg.write, write), + OPT_LIST("bucket-thresholds", 't', &cfg.bucket_thresholds, + bucket_thresholds), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + + if (fd < 0) + return fd; + + /* Query maj and minor version first to figure out the amount of + * valid buckets a user is allowed to modify. Read or write doesn't + * matter + */ + err = nvme_get_log(fd, NVME_NSID_ALL, 0xc2, + false, NVME_NO_LOG_LSP, sizeof(media_version), + media_version); + if (err) { + fprintf(stderr, "Querying media version failed. NVMe Status:%s(%x)\n", + nvme_status_to_string(err), err); + goto close_fd; + } + + if (media_version[0] == 1000) { + int thresholds[OPTANE_V1000_BUCKET_LEN] = {0}; + num = argconfig_parse_comma_sep_array(cfg.bucket_thresholds, + thresholds, + sizeof(thresholds)); + if (num == -1) { + fprintf(stderr, "ERROR: Bucket list is malformed\n"); + goto close_fd; + + } + + err = nvme_set_feature(fd, nsid, fid, cfg.write ? 0x1 : 0x0, + cdw12, save, OPTANE_V1000_BUCKET_LEN, + thresholds, &result); + + if (err > 0) { + fprintf(stderr, "NVMe Status:%s(%x)\n", + nvme_status_to_string(err), err); + } else if (err < 0) { + perror("Enable latency tracking"); + fprintf(stderr, "Command failed while parsing.\n"); + } + } else { + fprintf(stderr, "Unsupported command\n"); + } + +close_fd: + close(fd); + return err; +} + diff --git a/plugins/intel/intel-nvme.h b/plugins/intel/intel-nvme.h index 335e901..b119004 100644 --- a/plugins/intel/intel-nvme.h +++ b/plugins/intel/intel-nvme.h @@ -11,6 +11,7 @@ PLUGIN(NAME("intel", "Intel vendor specific extensions"), ENTRY("id-ctrl", "Send NVMe Identify Controller", id_ctrl) ENTRY("internal-log", "Retrieve Intel internal firmware log, save it", get_internal_log) ENTRY("lat-stats", "Retrieve Intel IO Latency Statistics log, show it", get_lat_stats_log) + ENTRY("set-bucket-thresholds", "Set Latency Stats Bucket Values, save it", set_lat_stats_thresholds) ENTRY("lat-stats-tracking", "Enable and disable Latency Statistics logging.", enable_lat_stats_tracking) ENTRY("market-name", "Retrieve Intel Marketing Name log, show it", get_market_log) ENTRY("smart-log-add", "Retrieve Intel SMART Log, show it", get_additional_smart_log) diff --git a/plugins/memblaze/memblaze-nvme.c b/plugins/memblaze/memblaze-nvme.c index c75f49c..d330835 100644 --- a/plugins/memblaze/memblaze-nvme.c +++ b/plugins/memblaze/memblaze-nvme.c @@ -3,6 +3,7 @@ #include #include #include +#include #include "linux/nvme_ioctl.h" @@ -19,9 +20,23 @@ #include "memblaze-utils.h" enum { - MB_FEAT_POWER_MGMT = 0xc6, + // feature id + MB_FEAT_POWER_MGMT = 0x02, + MB_FEAT_HIGH_LATENCY = 0xE1, + // log id + GLP_ID_VU_GET_READ_LATENCY_HISTOGRAM = 0xC1, + GLP_ID_VU_GET_WRITE_LATENCY_HISTOGRAM = 0xC2, + GLP_ID_VU_GET_HIGH_LATENCY_LOG = 0xC3, + MB_FEAT_CLEAR_ERRORLOG = 0xF7, }; +#define LOG_PAGE_SIZE (0x1000) +#define DO_PRINT_FLAG (1) +#define NOT_PRINT_FLAG (0) +#define FID_C1_LOG_FILENAME "log_c1.csv" +#define FID_C2_LOG_FILENAME "log_c2.csv" +#define FID_C3_LOG_FILENAME "log_c3.csv" + /* * Return -1 if @fw1 < @fw2 * Return 0 if @fw1 == @fw2 @@ -53,18 +68,26 @@ static int compare_fw_version(const char *fw1, const char *fw2) #define MEMBLAZE_FORMAT (0) #define INTEL_FORMAT (1) -// 2.83 = raisin -#define IS_RAISIN(str) (!strcmp(str, "2.83")) // 2.13 = papaya #define IS_PAPAYA(str) (!strcmp(str, "2.13")) -#define STR_VER_SIZE 5 +// 2.83 = raisin +#define IS_RAISIN(str) (!strcmp(str, "2.83")) +// 2.94 = kumquat +#define IS_KUMQUAT(str) (!strcmp(str, "2.94")) +// 0.60 = loquat +#define IS_LOQUAT(str) (!strcmp(str, "0.60")) + +#define STR_VER_SIZE (5) int getlogpage_format_type(char *fw_ver) { char fw_ver_local[STR_VER_SIZE]; strncpy(fw_ver_local, fw_ver, STR_VER_SIZE); *(fw_ver_local + STR_VER_SIZE - 1) = '\0'; - if ( IS_RAISIN(fw_ver_local) ) + if ( IS_RAISIN(fw_ver_local) + || IS_KUMQUAT(fw_ver_local) + || IS_LOQUAT(fw_ver_local) + ) { return INTEL_FORMAT; } @@ -128,7 +151,7 @@ static __u64 raw_2_u64(const __u8 *buf, size_t len) #define STR17_04 ", min: " #define STR17_05 ", curr: " #define STR18_01 "power_loss_protection" -#define STR19_01 "read_fail" +#define STR19_01 "read_fail_count" #define STR20_01 "thermal_throttle_time" #define STR21_01 "flash_media_error" @@ -380,7 +403,42 @@ static int show_memblaze_smart_log(int fd, __u32 nsid, const char *devname, return err; } -static int get_additional_smart_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) +int parse_params(char *str, int number, ...) +{ + va_list argp; + int *param; + char *c; + int value; + + va_start(argp, number); + + while (number > 0) { + c = strtok(str, ","); + if ( c == NULL) { + printf("No enough parameters. abort...\n"); + exit(EINVAL); + } + + if (isalnum(*c) == 0) { + printf("%s is not a valid number\n", c); + return 1; + } + value = atoi(c); + param = va_arg(argp, int *); + *param = value; + + if (str) { + str = strchr(str, ','); + if (str) { str++; } + } + number--; + } + va_end(argp); + + return 0; +} + +static int mb_get_additional_smart_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) { struct nvme_memblaze_smart_log smart_log; int err, fd; @@ -408,7 +466,7 @@ static int get_additional_smart_log(int argc, char **argv, struct command *cmd, return fd; err = nvme_get_log(fd, cfg.namespace_id, 0xca, false, - sizeof(smart_log), &smart_log); + NVME_NO_LOG_LSP, sizeof(smart_log), &smart_log); if (!err) { if (!cfg.raw_binary) err = show_memblaze_smart_log(fd, cfg.namespace_id, devicename, &smart_log); @@ -423,213 +481,262 @@ static int get_additional_smart_log(int argc, char **argv, struct command *cmd, static char *mb_feature_to_string(int feature) { - switch (feature) { - case MB_FEAT_POWER_MGMT: return "Memblaze power management"; - default: return "Unknown"; - } + switch (feature) { + case MB_FEAT_POWER_MGMT: return "Memblaze power management"; + case MB_FEAT_HIGH_LATENCY: return "Memblaze high latency log"; + case MB_FEAT_CLEAR_ERRORLOG: return "Memblaze clear error log"; + default: return "Unknown"; + } } -static int get_additional_feature(int argc, char **argv, struct command *cmd, struct plugin *plugin) +static int mb_get_powermanager_status(int argc, char **argv, struct command *cmd, struct plugin *plugin) { - const char *desc = "Read operating parameters of the "\ - "specified controller. Operating parameters are grouped "\ - "and identified by Feature Identifiers; each Feature "\ - "Identifier contains one or more attributes that may affect "\ - "behaviour of the feature. Each Feature has three possible "\ - "settings: default, saveable, and current. If a Feature is "\ - "saveable, it may be modified by set-feature. Default values "\ - "are vendor-specific and not changeable. Use set-feature to "\ - "change saveable Features.\n\n"\ - "Available additional feature id:\n"\ - "0xc6: Memblaze power management\n"\ - " (value 0 - 25w, 1 - 20w, 2 - 15w)"; - const char *raw = "show feature in binary format"; - const char *namespace_id = "identifier of desired namespace"; - const char *feature_id = "hexadecimal feature name"; - const char *sel = "[0-3]: curr./default/saved/supp."; - const char *data_len = "buffer len (if) data is returned"; - const char *cdw11 = "dword 11 for interrupt vector config"; - const char *human_readable = "show infos in readable format"; - int err, fd; - __u32 result; - void *buf = NULL; - - struct config { - __u32 namespace_id; - __u32 feature_id; - __u8 sel; - __u32 cdw11; - __u32 data_len; - int raw_binary; - int human_readable; - }; + const char *desc = "Get Memblaze power management ststus\n (value 0 - 25w, 1 - 20w, 2 - 15w)"; + int err, fd; + __u32 result; + __u32 feature_id = MB_FEAT_POWER_MGMT; - struct config cfg = { - .namespace_id = 1, - .feature_id = 0, - .sel = 0, - .cdw11 = 0, - .data_len = 0, - }; + OPT_ARGS(opts) = { + OPT_END() + }; - OPT_ARGS(opts) = { - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), - OPT_UINT("feature-id", 'f', &cfg.feature_id, feature_id), - OPT_BYTE("sel", 's', &cfg.sel, sel), - OPT_UINT("data-len", 'l', &cfg.data_len, data_len), - OPT_UINT("cdw11", 'c', &cfg.cdw11, cdw11), - OPT_FLAG("human-readable", 'H', &cfg.human_readable, human_readable), - OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), - OPT_END() - }; + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) return fd; - fd = parse_and_open(argc, argv, desc, opts); - if (fd < 0) - return fd; + err = nvme_get_feature(fd, 0, feature_id, 0, 0, 0, NULL, &result); + if (err < 0) { + perror("get-feature"); + } + if (!err) { + printf("get-feature:0x%02x (%s), %s value: %#08x\n", feature_id, + mb_feature_to_string(feature_id), + nvme_select_to_string(0), result); + } else if (err > 0) + fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(err), err); + return err; +} - if (cfg.sel > 7) { - fprintf(stderr, "invalid 'select' param:%d\n", cfg.sel); - return EINVAL; - } - if (!cfg.feature_id) { - fprintf(stderr, "feature-id required param\n"); - return EINVAL; - } - if (cfg.data_len) { - if (posix_memalign(&buf, getpagesize(), cfg.data_len)) - exit(ENOMEM); - memset(buf, 0, cfg.data_len); - } +static int mb_set_powermanager_status(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Set Memblaze power management status\n (value 0 - 25w, 1 - 20w, 2 - 15w)"; + const char *value = "new value of feature (required)"; + const char *save = "specifies that the controller shall save the attribute"; + int err, fd; + __u32 result; + + struct config { + __u32 feature_id; + __u32 value; + int save; + }; + + struct config cfg = { + .feature_id = MB_FEAT_POWER_MGMT, + .value = 0, + .save = 0, + }; + + OPT_ARGS(opts) = { + OPT_UINT("value", 'v', &cfg.value, value), + OPT_FLAG("save", 's', &cfg.save, save), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) return fd; + + err = nvme_set_feature(fd, 0, cfg.feature_id, cfg.value, 0, cfg.save, 0, NULL, &result); + if (err < 0) { + perror("set-feature"); + } + if (!err) { + printf("set-feature:%02x (%s), value:%#08x\n", cfg.feature_id, + mb_feature_to_string(cfg.feature_id), cfg.value); + } else if (err > 0) + fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(err), err); - err = nvme_get_feature(fd, cfg.namespace_id, cfg.feature_id, cfg.sel, cfg.cdw11, - cfg.data_len, buf, &result); - if (!err) { - printf("get-feature:0x%02x (%s), %s value: %#08x\n", cfg.feature_id, - mb_feature_to_string(cfg.feature_id), - nvme_select_to_string(cfg.sel), result); - if (cfg.human_readable) - nvme_feature_show_fields(cfg.feature_id, result, buf); - else { - if (buf) { - if (!cfg.raw_binary) - d(buf, cfg.data_len, 16, 1); - else - d_raw(buf, cfg.data_len); - } - } - } else if (err > 0) - fprintf(stderr, "NVMe Status:%s(%x)\n", - nvme_status_to_string(err), err); - if (buf) - free(buf); - return err; + return err; } -static int set_additional_feature(int argc, char **argv, struct command *cmd, struct plugin *plugin) +#define P2MIN (1) +#define P2MAX (5000) +#define MB_FEAT_HIGH_LATENCY_VALUE_SHIFT (15) +static int mb_set_high_latency_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) { - const char *desc = "Modify the saveable or changeable "\ - "current operating parameters of the controller. Operating "\ - "parameters are grouped and identified by Feature "\ - "Identifiers. Feature settings can be applied to the entire "\ - "controller and all associated namespaces, or to only a few "\ - "namespace(s) associated with the controller. Default values "\ - "for each Feature are vendor-specific and may not be modified."\ - "Use get-feature to determine which Features are supported by "\ - "the controller and are saveable/changeable.\n\n"\ - "Available additional feature id:\n"\ - "0xc6: Memblaze power management\n"\ - " (value 0 - 25w, 1 - 20w, 2 - 15w)"; - const char *namespace_id = "desired namespace"; - const char *feature_id = "hex feature name (required)"; - const char *data_len = "buffer length if data required"; - const char *data = "optional file for feature data (default stdin)"; - const char *value = "new value of feature (required)"; - const char *save = "specifies that the controller shall save the attribute"; - int err, fd; - __u32 result; - void *buf = NULL; - int ffd = STDIN_FILENO; + const char *desc = "Set Memblaze high latency log\n"\ + " input parameter p1,p2\n"\ + " p1 value: 0 is disable, 1 is enable\n"\ + " p2 value: 1 .. 5000 ms"; + const char *param = "input parameters"; + int err, fd; + __u32 result; + int param1 = 0, param2 = 0; + + struct config { + __u32 feature_id; + char * param; + __u32 value; + }; + + struct config cfg = { + .feature_id = MB_FEAT_HIGH_LATENCY, + .param = "0,0", + .value = 0, + }; + + OPT_ARGS(opts) = { + OPT_LIST("param", 'p', &cfg.param, param), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) return fd; + + if (parse_params(cfg.param, 2, ¶m1, ¶m2)) { + printf("setfeature: invalid formats %s\n", cfg.param); + exit(EINVAL); + } + if ((param1 == 1) && (param2 < P2MIN || param2 > P2MAX)) { + printf("setfeature: invalid high io latency threshold %d\n", param2); + exit(EINVAL); + } + cfg.value = (param1 << MB_FEAT_HIGH_LATENCY_VALUE_SHIFT) | param2; - struct config { - char *file; - __u32 namespace_id; - __u32 feature_id; - __u32 value; - __u32 data_len; - int save; - }; + err = nvme_set_feature(fd, 0, cfg.feature_id, cfg.value, 0, 0, 0, NULL, &result); + if (err < 0) { + perror("set-feature"); + } + if (!err) { + printf("set-feature:0x%02X (%s), value:%#08x\n", cfg.feature_id, + mb_feature_to_string(cfg.feature_id), cfg.value); + } else if (err > 0) + fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(err), err); - struct config cfg = { - .file = "", - .namespace_id = 0, - .feature_id = 0, - .value = 0, - .data_len = 0, - .save = 0, - }; + return err; +} - OPT_ARGS(opts) = { - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), - OPT_UINT("feature-id", 'f', &cfg.feature_id, feature_id), - OPT_UINT("value", 'v', &cfg.value, value), - OPT_UINT("data-len", 'l', &cfg.data_len, data_len), - OPT_FILE("data", 'd', &cfg.file, data), - OPT_FLAG("save", 's', &cfg.save, save), - OPT_END() - }; +static int glp_high_latency_show_bar(FILE *fdi, int print) +{ + fPRINT_PARAM1("Memblaze High Latency Log\n"); + fPRINT_PARAM1("---------------------------------------------------------------------------------------------\n"); + fPRINT_PARAM1("Timestamp Type QID CID NSID StartLBA NumLBA Latency\n"); + fPRINT_PARAM1("---------------------------------------------------------------------------------------------\n"); + return 0; +} - fd = parse_and_open(argc, argv, desc, opts); - if (fd < 0) - return fd; +/* High latency log page definiton + * Total 32 bytes + */ +typedef struct +{ + __u8 port; + __u8 revision; + __u16 rsvd; + __u8 opcode; + __u8 sqe; + __u16 cid; + __u32 nsid; + __u32 latency; + __u64 sLBA; + __u16 numLBA; + __u16 timestampH; + __u32 timestampL; +} log_page_high_latency_t; /* total 32 bytes */ + +static int find_deadbeef(char *buf) +{ + if (((*(buf + 0) & 0xff) == 0xef) && ((*(buf + 1) & 0xff) == 0xbe) && \ + ((*(buf + 2) & 0xff) == 0xad) && ((*(buf + 3) & 0xff) == 0xde)) + { + return 1; + } + return 0; +} - if (!cfg.feature_id) { - fprintf(stderr, "feature-id required param\n"); - return EINVAL; - } +#define TIME_STR_SIZE (44) +static int glp_high_latency(FILE *fdi, char *buf, int buflen, int print) +{ + log_page_high_latency_t *logEntry; + char string[TIME_STR_SIZE]; + int i, entrySize; + __u64 timestamp; + time_t tt = 0; + struct tm *t = NULL; + int millisec = 0; + + if (find_deadbeef(buf)) return 0; + + entrySize = sizeof(log_page_high_latency_t); + for (i = 0; i < buflen; i += entrySize) + { + logEntry = (log_page_high_latency_t *)(buf + i); - if (cfg.data_len) { - if (posix_memalign(&buf, getpagesize(), cfg.data_len)) - exit(ENOMEM); - memset(buf, 0, cfg.data_len); - } + if (logEntry->latency == 0 && logEntry->revision == 0) + { + return 1; + } + + if (0 == logEntry->timestampH) // generate host time string + { + snprintf(string, sizeof(string), "%d", logEntry->timestampL); + } + else // sort + { + timestamp = logEntry->timestampH - 1; + timestamp = timestamp << 32; + timestamp += logEntry->timestampL; + tt = timestamp / 1000; + millisec = timestamp % 1000; + t = gmtime(&tt); + snprintf(string, sizeof(string), "%4d%02d%02d--%02d:%02d:%02d.%03d UTC", + 1900 + t->tm_year, 1 + t->tm_mon, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec, millisec); + } + + fprintf(fdi, "%-32s %-7x %-6x %-6x %-8x %4x%08x %-8x %-d\n", + string, logEntry->opcode, logEntry->sqe, logEntry->cid, logEntry->nsid, + (__u32)(logEntry->sLBA >> 32), (__u32)logEntry->sLBA, logEntry->numLBA, logEntry->latency); + if (print) + { + printf("%-32s %-7x %-6x %-6x %-8x %4x%08x %-8x %-d\n", + string, logEntry->opcode, logEntry->sqe, logEntry->cid, logEntry->nsid, + (__u32)(logEntry->sLBA >> 32), (__u32)logEntry->sLBA, logEntry->numLBA, logEntry->latency); + } + } + return 1; +} - if (buf) { - if (strlen(cfg.file)) { - ffd = open(cfg.file, O_RDONLY); - if (ffd <= 0) { - fprintf(stderr, "no firmware file provided\n"); - err = EINVAL; - goto free; - } - } - if (read(ffd, (void *)buf, cfg.data_len) < 0) { - fprintf(stderr, "failed to read data buffer from input file\n"); - err = EINVAL; - goto free; - } - } +static int mb_high_latency_log_print(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Get Memblaze high latency log"; + int err, fd; + char buf[LOG_PAGE_SIZE]; + FILE *fdi = NULL; + + fdi = fopen(FID_C3_LOG_FILENAME, "w+"); + OPT_ARGS(opts) = { + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) return fd; + + glp_high_latency_show_bar(fdi, DO_PRINT_FLAG); + err = nvme_get_log(fd, NVME_NSID_ALL, GLP_ID_VU_GET_HIGH_LATENCY_LOG, 0, NVME_NO_LOG_LSP, sizeof(buf), &buf); + + while ( 1) { + if (!glp_high_latency(fdi, buf, LOG_PAGE_SIZE, DO_PRINT_FLAG)) break; + err = nvme_get_log(fd, NVME_NSID_ALL, GLP_ID_VU_GET_HIGH_LATENCY_LOG, 0, NVME_NO_LOG_LSP, sizeof(buf), &buf); + if ( err) { + fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(err), err); + break; + } + } - err = nvme_set_feature(fd, cfg.namespace_id, cfg.feature_id, cfg.value, - 0, cfg.save, cfg.data_len, buf, &result); - if (err < 0) { - perror("set-feature"); - goto free; - } - if (!err) { - printf("set-feature:%02x (%s), value:%#08x\n", cfg.feature_id, - mb_feature_to_string(cfg.feature_id), cfg.value); - if (buf) - d(buf, cfg.data_len, 16, 1); - } else if (err > 0) - fprintf(stderr, "NVMe Status:%s(%x)\n", - nvme_status_to_string(err), err); - -free: - if (buf) - free(buf); - return err; + if (NULL != fdi) fclose(fdi); + return err; } + static int memblaze_fw_commit(int fd, int select) { struct nvme_admin_cmd cmd = { @@ -641,7 +748,7 @@ static int memblaze_fw_commit(int fd, int select) return nvme_submit_admin_passthru(fd, &cmd); } -static int memblaze_selective_download(int argc, char **argv, struct command *cmd, struct plugin *plugin) +static int mb_selective_download(int argc, char **argv, struct command *cmd, struct plugin *plugin) { const char *desc = "This performs a selective firmware download, which allows the user to " @@ -757,3 +864,287 @@ out: return err; } +static void ioLatencyHistogramOutput(FILE *fd, int index, int start, int end, char *unit0, + char *unit1, unsigned int *pHistogram, int print) +{ + int len; + char string[64], subString0[12], subString1[12]; + + len = snprintf(subString0, sizeof(subString0), "%d%s", start, unit0); + if (end != 0x7FFFFFFF) + { + len = snprintf(subString1, sizeof(subString1), "%d%s", end, unit1); + } + else + { + len = snprintf(subString1, sizeof(subString1), "%s", "+INF"); + } + len = snprintf(string, sizeof(string), "%-11d %-11s %-11s %-11u\n", index, subString0, subString1, + pHistogram[index]); + fwrite(string, 1, len, fd); + if (print) + { + printf("%s", string); + } +} + +int io_latency_histogram(char *file, char *buf, int print, int logid) +{ + FILE *fdi = fopen(file, "w+"); + int i, index; + char unit[2][3]; + unsigned int *revision = (unsigned int *)buf; + + if (logid == GLP_ID_VU_GET_READ_LATENCY_HISTOGRAM) + { + fPRINT_PARAM1("Memblaze IO Read Command Latency Histogram\n"); + } + else if (logid == GLP_ID_VU_GET_WRITE_LATENCY_HISTOGRAM) + { + fPRINT_PARAM1("Memblaze IO Write Command Latency Histogram\n"); + } + fPRINT_PARAM2("Major Revision : %d\n", revision[1]); + fPRINT_PARAM2("Minor Revision : %d\n", revision[0]); + buf += 8; + + if (revision[1] == 1 && revision[0] == 0) + { + fPRINT_PARAM1("--------------------------------------------------\n"); + fPRINT_PARAM1("Bucket Start End Value \n"); + fPRINT_PARAM1("--------------------------------------------------\n"); + index = 0; + strcpy(unit[0], "us"); + strcpy(unit[1], "us"); + for (i = 0; i < 32; i++, index++) + { + if (i == 31) + { + strcpy(unit[1], "ms"); + ioLatencyHistogramOutput(fdi, index, i * 32, 1, unit[0], unit[1], (unsigned int *)buf, print); + } + else + { + ioLatencyHistogramOutput(fdi, index, i * 32, (i + 1) * 32, unit[0], unit[1], (unsigned int *)buf, + print); + } + } + + strcpy(unit[0], "ms"); + strcpy(unit[1], "ms"); + for (i = 1; i < 32; i++, index++) + { + ioLatencyHistogramOutput(fdi, index, i, i + 1, unit[0], unit[1], (unsigned int *)buf, print); + } + + for (i = 1; i < 32; i++, index++) + { + if (i == 31) + { + strcpy(unit[1], "s"); + ioLatencyHistogramOutput(fdi, index, i * 32, 1, unit[0], unit[1], (unsigned int *)buf, print); + } + else + { + ioLatencyHistogramOutput(fdi, index, i * 32, (i + 1) * 32, unit[0], unit[1], (unsigned int *)buf, + print); + } + } + + strcpy(unit[0], "s"); + strcpy(unit[1], "s"); + for (i = 1; i < 4; i++, index++) + { + ioLatencyHistogramOutput(fdi, index, i, i + 1, unit[0], unit[1], (unsigned int *)buf, print); + } + + ioLatencyHistogramOutput(fdi, index, i, 0x7FFFFFFF, unit[0], unit[1], (unsigned int *)buf, print); + } + else + { + fPRINT_PARAM1("Unsupported io latency histogram revision\n"); + } + + fclose(fdi); + return 1; +} + +static int mb_lat_stats_log_print(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + char stats[LOG_PAGE_SIZE]; + int err = 0; + int fd; + char f1[] = FID_C1_LOG_FILENAME; + char f2[] = FID_C2_LOG_FILENAME; + + const char *desc = "Get Latency Statistics log and show it."; + const char *write = "Get write statistics (read default)"; + + struct config { + int write; + }; + struct config cfg = { + .write = 0, + }; + + OPT_ARGS(opts) = { + OPT_FLAG("write", 'w', &cfg.write, write), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) return fd; + + err = nvme_get_log(fd, NVME_NSID_ALL, cfg.write ? 0xc2 : 0xc1, false, NVME_NO_LOG_LSP, sizeof(stats), &stats); + if (!err) + io_latency_histogram(cfg.write ? f2 : f1, stats, DO_PRINT_FLAG, + cfg.write ? GLP_ID_VU_GET_WRITE_LATENCY_HISTOGRAM : GLP_ID_VU_GET_READ_LATENCY_HISTOGRAM); + else + fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(err), err); + + close(fd); + return err; +} + +#define OP 0xFC +#define FID 0x68 +static int memblaze_clear_error_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + int err, fd; + char *desc = "Clear Memblaze devices error log."; + + //const char *value = "new value of feature (required)"; + //const char *save = "specifies that the controller shall save the attribute"; + __u32 result; + + struct config { + __u32 feature_id; + __u32 value; + int save; + }; + + struct config cfg = { + .feature_id = 0xf7, + .value = 0x534d0001, + .save = 0, + }; + + OPT_ARGS(opts) = { + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + return fd; + + + + err = nvme_set_feature(fd, 0, cfg.feature_id, cfg.value, 0, cfg.save, 0, NULL, &result); + if (err < 0) { + perror("set-feature"); + } + if (!err) { + printf("set-feature:%02x (%s), value:%#08x\n", cfg.feature_id, mb_feature_to_string(cfg.feature_id), cfg.value); + } else if (err > 0) + fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(err), err); +/* + struct nvme_admin_cmd admin_cmd = { + .opcode = OP, + .cdw10 = FID, + }; + + err = nvme_submit_admin_passthru(fd, &admin_cmd); + + if (!err) { + printf("OP(0x%2X) FID(0x%2X) Clear error log success.\n", OP, FID); + } else { + printf("NVMe Status:%s(%x)\n", nvme_status_to_string(err), err); + }; +*/ + return err; +} + +static int mb_set_lat_stats(int argc, char **argv, + struct command *command, struct plugin *plugin) +{ + int err, fd; + const char *desc = ( + "Enable/Disable Latency Statistics Tracking.\n" + "No argument prints current status."); + const char *enable_desc = "Enable LST"; + const char *disable_desc = "Disable LST"; + const __u32 nsid = 0; + const __u8 fid = 0xe2; + const __u8 sel = 0; + const __u32 cdw11 = 0x0; + const __u32 cdw12 = 0x0; + const __u32 data_len = 32; + const __u32 save = 0; + __u32 result; + void *buf = NULL; + + struct config { + bool enable, disable; + }; + + struct config cfg = { + .enable = false, + .disable = false, + }; + + const struct argconfig_commandline_options command_line_options[] = { + {"enable", 'e', "", CFG_NONE, &cfg.enable, no_argument, enable_desc}, + {"disable", 'd', "", CFG_NONE, &cfg.disable, no_argument, disable_desc}, + {NULL} + }; + + fd = parse_and_open(argc, argv, desc, command_line_options); + + enum Option { + None = -1, + True = 1, + False = 0, + }; + enum Option option = None; + + if (cfg.enable && cfg.disable) + printf("Cannot enable and disable simultaneously."); + else if (cfg.enable || cfg.disable) + option = cfg.enable; + + if (fd < 0) + return fd; + switch (option) { + case None: + err = nvme_get_feature(fd, nsid, fid, sel, cdw11, data_len, buf, + &result); + if (!err) { + printf( + "Latency Statistics Tracking (FID 0x%X) is currently (%i).\n", + fid, result); + } else { + printf("Could not read feature id 0xE2.\n"); + return err; + } + break; + case True: + case False: + err = nvme_set_feature(fd, nsid, fid, option, cdw12, save, + data_len, buf, &result); + if (err > 0) { + fprintf(stderr, "NVMe Status:%s(%x)\n", + nvme_status_to_string(err), err); + } else if (err < 0) { + perror("Enable latency tracking"); + fprintf(stderr, "Command failed while parsing.\n"); + } else { + printf("Successfully set enable bit for FID (0x%X) to %i.\n", + fid, option); + } + break; + default: + printf("%d not supported.\n", option); + return EINVAL; + } + return fd; +} + diff --git a/plugins/memblaze/memblaze-nvme.h b/plugins/memblaze/memblaze-nvme.h index 8f7024d..043d0a8 100644 --- a/plugins/memblaze/memblaze-nvme.h +++ b/plugins/memblaze/memblaze-nvme.h @@ -14,10 +14,15 @@ PLUGIN(NAME("memblaze", "Memblaze vendor specific extensions"), COMMAND_LIST( - ENTRY("smart-log-add", "Retrieve Memblaze SMART Log, show it", get_additional_smart_log) - ENTRY("get-feature-add", "Get Memblaze feature and show the resulting value", get_additional_feature) - ENTRY("set-feature-add", "Set a Memblaze feature and show the resulting value", set_additional_feature) - ENTRY("select-download", "Selective Firmware Download", memblaze_selective_download) + ENTRY("smart-log-add", "Retrieve Memblaze SMART Log, show it", mb_get_additional_smart_log) + ENTRY("get-pm-status", "Get Memblaze Power Manager Status", mb_get_powermanager_status) + ENTRY("set-pm-status", "Set Memblaze Power Manager Status", mb_set_powermanager_status) + ENTRY("select-download", "Selective Firmware Download", mb_selective_download) + ENTRY("lat-stats", "Enable and disable Latency Statistics logging", mb_set_lat_stats) + ENTRY("lat-stats-print", "Retrieve IO Latency Statistics log, show it", mb_lat_stats_log_print) + ENTRY("lat-log", "Set Memblaze High Latency Log", mb_set_high_latency_log) + ENTRY("lat-log-print", "Output Memblaze High Latency Log", mb_high_latency_log_print) + ENTRY("clear-error-log", "Clear error log", memblaze_clear_error_log) ) ); diff --git a/plugins/memblaze/memblaze-utils.h b/plugins/memblaze/memblaze-utils.h index a67ee5e..6fdee39 100644 --- a/plugins/memblaze/memblaze-utils.h +++ b/plugins/memblaze/memblaze-utils.h @@ -160,5 +160,68 @@ struct nvme_p4_smart_log u8 resv[SMART_INFO_NEW_SIZE - sizeof(struct nvme_p4_smart_log_item) * NR_SMART_ITEMS]; }; +// base +#define DD do{ printf("=Memblaze= %s[%d]-%s():\n", __FILE__, __LINE__, __func__); }while(0) +#define DE(str) do{ printf("===ERROR!=== %s[%d]-%s():str=%s\n", __FILE__, __LINE__, __func__, \ + str); }while(0) +// integer +#define DI(i) do{ printf("=Memblaze= %s[%d]-%s():int=%d\n", __FILE__, __LINE__, __func__, \ + (int)i); } while(0) +#define DPI(prompt, i) do{ printf("=Memblaze= %s[%d]-%s():%s=%d\n", __FILE__, __LINE__, __func__, \ + prompt, i); }while(0) +#define DAI(prompt, i, arr, max) do{ printf("=Memblaze= %s[%d]-%s():%s", __FILE__, __LINE__, __func__, prompt); \ + for(i=0;i #include - #define CREATE_CMD #include "micron-nvme.h" -#define min(x, y) ((x) > (y) ? (y) : (x)) -#define SensorCount 2 + +/* Supported Vendor specific feature ids */ +#define MICRON_FEATURE_CLEAR_PCI_CORRECTABLE_ERRORS 0xC3 +#define MICRON_FEATURE_CLEAR_FW_ACTIVATION_HISTORY 0xCE +#define MICRON_FEATURE_TELEMETRY_CONTROL_OPTION 0xCF +#define MICRON_FEATURE_SMBUS_OPTION 0xD5 + +/* Supported Vendor specific log page sizes */ #define C5_log_size (((452 + 16 * 1024) / 4) * 4096) -#define D0_log_size 256 +#define C0_log_size 512 +#define C2_log_size 4096 +#define D0_log_size 512 +#define FB_log_size 512 #define MaxLogChunk 16 * 1024 #define CommonChunkSize 16 * 4096 +#define min(x, y) ((x) > (y) ? (y) : (x)) +#define SensorCount 2 + +/* Plugin version major_number.minor_number.patch */ +static const char *__version_major = "1"; +static const char *__version_minor = "0"; +static const char *__version_patch = "5"; + +/* supported models of micron plugin; new models should be added at the end + * before UNKNOWN_MODEL. Make sure M5410 is first in the list ! + */ +typedef enum { M5410 = 0, M51AX, M51BX, M51CX, M5407, M5411, UNKNOWN_MODEL } eDriveModel; + +#define MICRON_VENDOR_ID 0x1344 + +static char *fvendorid1 = "/sys/class/nvme/nvme%d/device/vendor"; +static char *fvendorid2 = "/sys/class/misc/nvme%d/device/vendor"; +static char *fdeviceid1 = "/sys/class/nvme/nvme%d/device/device"; +static char *fdeviceid2 = "/sys/class/misc/nvme%d/device/device"; +static unsigned short vendor_id; +static unsigned short device_id; + typedef struct _LogPageHeader_t { - unsigned char numDwordsInLogPageHeaderLo; - unsigned char logPageHeaderFormatVersion; - unsigned char logPageId; - unsigned char numDwordsInLogPageHeaderHi; - unsigned int numValidDwordsInPayload; - unsigned int numDwordsInEntireLogPage; + unsigned char numDwordsInLogPageHeaderLo; + unsigned char logPageHeaderFormatVersion; + unsigned char logPageId; + unsigned char numDwordsInLogPageHeaderHi; + unsigned int numValidDwordsInPayload; + unsigned int numDwordsInEntireLogPage; } LogPageHeader_t; -/* - * Useful Helper functions - */ +static void WriteData(__u8 *data, __u32 len, const char *dir, const char *file, const char *msg) +{ + char tempFolder[PATH_MAX] = { 0 }; + FILE *fpOutFile = NULL; + sprintf(tempFolder, "%s/%s", dir, file); + if ((fpOutFile = fopen(tempFolder, "ab+")) != NULL) { + if (fwrite(data, 1, len, fpOutFile) != len) { + printf("Failed to write %s data to %s\n", msg, tempFolder); + } + fclose(fpOutFile); + } else { + printf("Failed to open %s file to write %s\n", tempFolder, msg); + } +} -static int micron_fw_commit(int fd, int select) +static int ReadSysFile(const char *file, unsigned short *id) { - struct nvme_admin_cmd cmd = { - .opcode = nvme_admin_activate_fw, - .cdw10 = 8, - .cdw12 = select, - }; - return ioctl(fd, NVME_IOCTL_ADMIN_CMD, &cmd); + int ret = 0; + char idstr[32] = { '\0' }; + int fd = open(file, O_RDONLY); + + if (fd > 0) { + ret = read(fd, idstr, sizeof(idstr)); + close(fd); + } + + if (fd < 0 || ret < 0) + perror(file); + else + *id = strtol(idstr, NULL, 16); + + return ret; +} +static eDriveModel GetDriveModel(int idx) +{ + eDriveModel eModel = UNKNOWN_MODEL; + char path[512]; + + sprintf(path, fvendorid1, idx); + if (ReadSysFile(path, &vendor_id) < 0) { + sprintf(path, fvendorid2, idx); + ReadSysFile(path, &vendor_id); + } + sprintf(path, fdeviceid1, idx); + if (ReadSysFile(path, &device_id) < 0) { + sprintf(path, fdeviceid2, idx); + ReadSysFile(path, &device_id); + } + if (vendor_id == MICRON_VENDOR_ID) { + switch (device_id) { + case 0x51A0: + case 0x51A1: + case 0x51A2: + eModel = M51AX; + break; + case 0x51B0: + case 0x51B1: + case 0x51B2: + eModel = M51BX; + break; + case 0x51C0: + case 0x51C1: + case 0x51C2: + case 0x51C3: + eModel = M51CX; + break; + case 0x5405: + case 0x5406: + case 0x5407: + eModel = M5407; + break; + case 0x5410: + eModel = M5410; + break; + case 0x5411: + eModel = M5411; + break; + default: + break; + } + } + return eModel; } static int ZipAndRemoveDir(char *strDirName, char *strFileName) { - int err = 0; - char strBuffer[PATH_MAX]; - int nRet; - - sprintf(strBuffer, "zip -r \"%s\" \"%s\" >temp.txt 2>&1", strFileName, - strDirName); - - nRet = system(strBuffer); - - if (nRet < 0) { - printf("Unable to create zip package!\n"); - err = EINVAL; - goto exit_status; - } - - sprintf(strBuffer, "rm -f -R \"%s\" >temp.txt 2>&1", strDirName); - nRet = system(strBuffer); - if (nRet < 0) { - printf("Unable to remove temporary files!\n"); - err = EINVAL; - goto exit_status; - } - - exit_status: - err = system("rm -f temp.txt"); - return err; + int err = 0; + char strBuffer[PATH_MAX]; + int nRet; + bool is_tgz = false; + struct stat sb; + + if (strstr(strFileName, ".tar.gz") || strstr(strFileName, ".tgz")) { + sprintf(strBuffer, "tar -zcf \"%s\" \"%s\"", strFileName, + strDirName); + is_tgz = true; + } else { + sprintf(strBuffer, "zip -r \"%s\" \"%s\" >temp.txt 2>&1", strFileName, + strDirName); + } + + err = EINVAL; + nRet = system(strBuffer); + + /* check if log file is created, if not print error message */ + if (nRet < 0 || (stat(strFileName, &sb) == -1)) { + if (is_tgz) + sprintf(strBuffer, "check if tar and gzip commands are installed"); + else + sprintf(strBuffer, "check if zip command is installed"); + + fprintf(stderr, "Failed to create log data package, %s!\n", strBuffer); + } + + sprintf(strBuffer, "rm -f -R \"%s\" >temp.txt 2>&1", strDirName); + nRet = system(strBuffer); + if (nRet < 0) + printf("Failed to remove temporary files!\n"); + + err = system("rm -f temp.txt"); + return err; } static int SetupDebugDataDirectories(char *strSN, char *strFilePath, - char *strMainDirName, char *strOSDirName, - char *strCtrlDirName) + char *strMainDirName, char *strOSDirName, + char *strCtrlDirName) { - int err = 0; - char strAppend[250]; - struct stat st; - char *fileLocation = NULL; - char *fileName; - int length = 0; - int nIndex = 0; - char *strTemp = NULL; - struct stat dirStat; - int j; - int k = 0; - int i = 0; - - if (strchr(strFilePath, '/') != NULL) { - fileName = strrchr(strFilePath, '\\'); - if (fileName == NULL) { - fileName = strrchr(strFilePath, '/'); - } - - if (fileName != NULL) { - if (!strcmp(fileName, "/")) { - goto exit_status; - } - - while (strFilePath[nIndex] != '\0') { - if ('\\' == strFilePath[nIndex] && '\\' == strFilePath[nIndex + 1]) { - goto exit_status; - } - nIndex++; - } - - length = (int)strlen(strFilePath) - (int)strlen(fileName); - - if (fileName == strFilePath) { - length = 1; - } - - fileLocation = (char *)malloc(length + 1); - strncpy(fileLocation, strFilePath, length); - fileLocation[length] = '\0'; - - while (fileLocation[k] != '\0') { - if (fileLocation[k] == '\\') { - fileLocation[k] = '/'; - } - } - - length = (int)strlen(fileLocation); - - if (':' == fileLocation[length - 1]) { - strTemp = (char *)malloc(length + 2); - strcpy(strTemp, fileLocation); - strcat(strTemp, "/"); - free(fileLocation); - - length = (int)strlen(strTemp); - fileLocation = (char *)malloc(length + 1); - memcpy(fileLocation, strTemp, length + 1); - free(strTemp); - } - - if (stat(fileLocation, &st) != 0) { - free(fileLocation); - goto exit_status; - } - free(fileLocation); - } else { - goto exit_status; - } - } - - nIndex = 0; - for (i = 0; i < (int)strlen(strSN); i++) { - if (strSN[i] != ' ' && strSN[i] != '\n' && strSN[i] != '\t' && strSN[i] != '\r') { - strMainDirName[nIndex++] = strSN[i]; - } - } - strMainDirName[nIndex] = '\0'; - - j = 1; - while (stat(strMainDirName, &dirStat) == 0) { - strMainDirName[nIndex] = '\0'; - sprintf(strAppend, "-%d", j); - strcat(strMainDirName, strAppend); - j++; - } - - mkdir(strMainDirName, 0777); - - if (strOSDirName != NULL) { - sprintf(strOSDirName, "%s/%s", strMainDirName, "OS"); - mkdir(strOSDirName, 0777); - - } - if (strCtrlDirName != NULL) { - sprintf(strCtrlDirName, "%s/%s", strMainDirName, "Controller"); - mkdir(strCtrlDirName, 0777); - - } - - exit_status: - return err; + int err = 0; + char strAppend[250]; + struct stat st; + char *fileLocation = NULL; + char *fileName; + int length = 0; + int nIndex = 0; + char *strTemp = NULL; + struct stat dirStat; + int j; + int k = 0; + int i = 0; + + if (strchr(strFilePath, '/') != NULL) { + fileName = strrchr(strFilePath, '\\'); + if (fileName == NULL) { + fileName = strrchr(strFilePath, '/'); + } + + if (fileName != NULL) { + if (!strcmp(fileName, "/")) { + goto exit_status; + } + + while (strFilePath[nIndex] != '\0') { + if ('\\' == strFilePath[nIndex] && '\\' == strFilePath[nIndex + 1]) { + goto exit_status; + } + nIndex++; + } + + length = (int)strlen(strFilePath) - (int)strlen(fileName); + + if (fileName == strFilePath) { + length = 1; + } + + if ((fileLocation = (char *)malloc(length + 1)) == NULL) { + goto exit_status; + } + strncpy(fileLocation, strFilePath, length); + fileLocation[length] = '\0'; + + while (fileLocation[k] != '\0') { + if (fileLocation[k] == '\\') { + fileLocation[k] = '/'; + } + k++; + } + + length = (int)strlen(fileLocation); + + if (':' == fileLocation[length - 1]) { + if ((strTemp = (char *)malloc(length + 2)) == NULL) { + goto exit_status; + } + strcpy(strTemp, fileLocation); + strcat(strTemp, "/"); + free(fileLocation); + + length = (int)strlen(strTemp); + if ((fileLocation = (char *)malloc(length + 1)) == NULL) { + goto exit_status; + } + + memcpy(fileLocation, strTemp, length + 1); + free(strTemp); + } + + if (stat(fileLocation, &st) != 0) { + free(fileLocation); + goto exit_status; + } + free(fileLocation); + } else { + goto exit_status; + } + } + + nIndex = 0; + for (i = 0; i < (int)strlen(strSN); i++) { + if (strSN[i] != ' ' && strSN[i] != '\n' && strSN[i] != '\t' && strSN[i] != '\r') { + strMainDirName[nIndex++] = strSN[i]; + } + } + strMainDirName[nIndex] = '\0'; + + j = 1; + while (stat(strMainDirName, &dirStat) == 0) { + strMainDirName[nIndex] = '\0'; + sprintf(strAppend, "-%d", j); + strcat(strMainDirName, strAppend); + j++; + } + + mkdir(strMainDirName, 0777); + + if (strOSDirName != NULL) { + sprintf(strOSDirName, "%s/%s", strMainDirName, "OS"); + mkdir(strOSDirName, 0777); + + } + if (strCtrlDirName != NULL) { + sprintf(strCtrlDirName, "%s/%s", strMainDirName, "Controller"); + mkdir(strCtrlDirName, 0777); + + } + +exit_status: + return err; } static int GetLogPageSize(int nFD, unsigned char ucLogID, int *nLogSize) { - int err = 0; - struct nvme_admin_cmd cmd; - unsigned int uiXferDwords = 0; - unsigned char pTmpBuf[CommonChunkSize] = { 0 }; - LogPageHeader_t *pLogHeader = NULL; - - if (ucLogID == 0xC1 || ucLogID == 0xC2 || ucLogID == 0xC4) { - cmd.opcode = 0x02; - cmd.cdw10 = ucLogID; - uiXferDwords = (unsigned int)(CommonChunkSize / 4); - cmd.nsid = 0xFFFFFFFF; - cmd.cdw10 |= ((uiXferDwords - 1) & 0x0000FFFF) << 16; - cmd.data_len = CommonChunkSize; - cmd.addr = (__u64) (uintptr_t) & pTmpBuf; - err = nvme_submit_passthru(nFD, NVME_IOCTL_ADMIN_CMD, &cmd); - if (err == 0) { - pLogHeader = (LogPageHeader_t *) pTmpBuf; - LogPageHeader_t *pLogHeader1 = (LogPageHeader_t *) pLogHeader; - *nLogSize = (int)(pLogHeader1->numDwordsInEntireLogPage) * 4; - } else { - printf ("Getting size of log page : 0x%X failed with %d\n", ucLogID, err); - *nLogSize = 0; - } - } - return err; + int err = 0; + unsigned char pTmpBuf[CommonChunkSize] = { 0 }; + LogPageHeader_t *pLogHeader = NULL; + + if (ucLogID == 0xC1 || ucLogID == 0xC2 || ucLogID == 0xC4) { + err = nvme_get_log(nFD, NVME_NSID_ALL, ucLogID, false, NVME_NO_LOG_LSP, + CommonChunkSize, pTmpBuf); + if (err == 0) { + pLogHeader = (LogPageHeader_t *) pTmpBuf; + LogPageHeader_t *pLogHeader1 = (LogPageHeader_t *) pLogHeader; + *nLogSize = (int)(pLogHeader1->numDwordsInEntireLogPage) * 4; + if (pLogHeader1->logPageHeaderFormatVersion == 0) { + printf ("Unsupported log page format version %d of log page : 0x%X\n", + ucLogID, err); + *nLogSize = 0; + err = -1; + } + } else { + printf ("Getting size of log page : 0x%X failed with %d\n", ucLogID, err); + *nLogSize = 0; + } + } + return err; } static int NVMEGetLogPage(int nFD, unsigned char ucLogID, unsigned char *pBuffer, int nBuffSize) { - int err = 0; - struct nvme_admin_cmd cmd = { 0 }; - unsigned int uiNumDwords = (unsigned int)nBuffSize / sizeof(unsigned int); - unsigned int uiMaxChunk = uiNumDwords; - unsigned int uiNumChunks = 1; - unsigned int uiXferDwords = 0; - unsigned long long ullBytesRead = 0; - unsigned char *pTempPtr = pBuffer; - unsigned char ucOpCode = 0x02; - - if (ullBytesRead == 0 && (ucLogID == 0xE6 || ucLogID == 0xE7)) { - uiMaxChunk = 4096; - } else if (uiMaxChunk > 16 * 1024) { - uiMaxChunk = 16 * 1024; - } - - uiNumChunks = uiNumDwords / uiMaxChunk; - if (uiNumDwords % uiMaxChunk > 0) { - uiNumChunks += 1; - } - - for (unsigned int i = 0; i < uiNumChunks; i++) { - memset(&cmd, 0, sizeof(cmd)); - uiXferDwords = uiMaxChunk; - if (i == uiNumChunks - 1 && uiNumDwords % uiMaxChunk > 0) { - uiXferDwords = uiNumDwords % uiMaxChunk; - } - - cmd.opcode = ucOpCode; - cmd.cdw10 |= ucLogID; - cmd.cdw10 |= ((uiXferDwords - 1) & 0x0000FFFF) << 16; - - if (ucLogID == 0x7) { - cmd.cdw10 |= 0x80; - } - if (ullBytesRead == 0 && (ucLogID == 0xE6 || ucLogID == 0xE7)) { - cmd.cdw11 = 1; - } - if (ullBytesRead > 0 && !(ucLogID == 0xE6 || ucLogID == 0xE7)) { - unsigned long long ullOffset = ullBytesRead; - cmd.cdw12 = ullOffset & 0xFFFFFFFF; - cmd.cdw13 = (ullOffset >> 32) & 0xFFFFFFFF; - } - - cmd.addr = (__u64) (uintptr_t) pTempPtr; - cmd.nsid = 0xFFFFFFFF; - cmd.data_len = uiXferDwords * 4; - err = nvme_submit_passthru(nFD, NVME_IOCTL_ADMIN_CMD, &cmd); - ullBytesRead += uiXferDwords * 4; - pTempPtr = pBuffer + ullBytesRead; - } - - return err; + int err = 0; + struct nvme_admin_cmd cmd = { 0 }; + unsigned int uiNumDwords = (unsigned int)nBuffSize / sizeof(unsigned int); + unsigned int uiMaxChunk = uiNumDwords; + unsigned int uiNumChunks = 1; + unsigned int uiXferDwords = 0; + unsigned long long ullBytesRead = 0; + unsigned char *pTempPtr = pBuffer; + unsigned char ucOpCode = 0x02; + + if (ullBytesRead == 0 && (ucLogID == 0xE6 || ucLogID == 0xE7)) { + uiMaxChunk = 4096; + } else if (uiMaxChunk > 16 * 1024) { + uiMaxChunk = 16 * 1024; + } + + uiNumChunks = uiNumDwords / uiMaxChunk; + if (uiNumDwords % uiMaxChunk > 0) { + uiNumChunks += 1; + } + + for (unsigned int i = 0; i < uiNumChunks; i++) { + memset(&cmd, 0, sizeof(cmd)); + uiXferDwords = uiMaxChunk; + if (i == uiNumChunks - 1 && uiNumDwords % uiMaxChunk > 0) { + uiXferDwords = uiNumDwords % uiMaxChunk; + } + + cmd.opcode = ucOpCode; + cmd.cdw10 |= ucLogID; + cmd.cdw10 |= ((uiXferDwords - 1) & 0x0000FFFF) << 16; + + if (ucLogID == 0x7) { + cmd.cdw10 |= 0x80; + } + if (ullBytesRead == 0 && (ucLogID == 0xE6 || ucLogID == 0xE7)) { + cmd.cdw11 = 1; + } + if (ullBytesRead > 0 && !(ucLogID == 0xE6 || ucLogID == 0xE7)) { + unsigned long long ullOffset = ullBytesRead; + cmd.cdw12 = ullOffset & 0xFFFFFFFF; + cmd.cdw13 = (ullOffset >> 32) & 0xFFFFFFFF; + } + + cmd.addr = (__u64) (uintptr_t) pTempPtr; + cmd.nsid = 0xFFFFFFFF; + cmd.data_len = uiXferDwords * 4; + err = nvme_submit_passthru(nFD, NVME_IOCTL_ADMIN_CMD, &cmd); + ullBytesRead += uiXferDwords * 4; + pTempPtr = pBuffer + ullBytesRead; + } + + return err; } static int NVMEResetLog(int nFD, unsigned char ucLogID, int nBufferSize, - long long llMaxSize) + long long llMaxSize) { - unsigned int *pBuffer = NULL; - int err = 0; + unsigned int *pBuffer = NULL; + int err = 0; - if ((pBuffer = (unsigned int *)calloc(1, nBufferSize)) == NULL) - return err; + if ((pBuffer = (unsigned int *)calloc(1, nBufferSize)) == NULL) + return err; - while (err == 0 && llMaxSize > 0) { - err = NVMEGetLogPage(nFD, ucLogID, (unsigned char *)pBuffer, nBufferSize); - if (err) - return err; + while (err == 0 && llMaxSize > 0) { + err = NVMEGetLogPage(nFD, ucLogID, (unsigned char *)pBuffer, nBufferSize); + if (err) + return err; - if (pBuffer[0] == 0xdeadbeef) - break; + if (pBuffer[0] == 0xdeadbeef) + break; - llMaxSize = llMaxSize - nBufferSize; - } + llMaxSize = llMaxSize - nBufferSize; + } - free(pBuffer); - return err; + free(pBuffer); + return err; } -static int GetCommonLogPage(int nFD, unsigned char ucLogID, unsigned char **pBuffer, int nBuffSize) +static int GetCommonLogPage(int nFD, unsigned char ucLogID, + unsigned char **pBuffer, int nBuffSize) { - struct nvme_admin_cmd cmd; - int err = 0; - unsigned char pTmpBuf[CommonChunkSize] = { 0 }; - unsigned int uiMaxChunk = 0; - unsigned int uiXferDwords = 0; - int nBytesRead = 0; - unsigned char *pTempPtr = NULL; - - uiMaxChunk = CommonChunkSize / 4; - pTempPtr = (unsigned char *)malloc(nBuffSize); - if (!pTempPtr) { - goto exit_status; - } - memset(pTempPtr, 0, nBuffSize); - - while (nBytesRead < nBuffSize) { - int nBytesRemaining = nBuffSize - nBytesRead; - - memset(pTmpBuf, 0, CommonChunkSize); - - uiXferDwords = uiMaxChunk; - memset(&cmd, 0, sizeof(cmd)); - cmd.opcode = 0x02; - cmd.cdw10 |= ucLogID; - cmd.cdw10 |= ((uiXferDwords - 1) & 0x0000FFFF) << 16; - - if (nBytesRead > 0) { - unsigned long long ullOffset = (unsigned long long)nBytesRead; - cmd.cdw12 = ullOffset & 0xFFFFFFFF; - cmd.cdw13 = (ullOffset >> 32) & 0xFFFFFFFF; - } - cmd.nsid = 0xFFFFFFFF; - cmd.data_len = uiXferDwords * 4; - cmd.addr = (__u64) (uintptr_t) pTmpBuf; - - err = nvme_submit_passthru(nFD, NVME_IOCTL_ADMIN_CMD, &cmd); - - if (nBytesRemaining >= (int)(uiMaxChunk * 4)) { - memcpy(&pTempPtr[nBytesRead], pTmpBuf, uiMaxChunk * 4); - } else { - memcpy(&pTempPtr[nBytesRead], pTmpBuf, nBytesRemaining); - } - - nBytesRead += (int)uiXferDwords *4; - } - *pBuffer = pTempPtr; - - exit_status: - return err; + unsigned char *pTempPtr = NULL; + int err = 0; + pTempPtr = (unsigned char *)malloc(nBuffSize); + if (!pTempPtr) { + goto exit_status; + } + memset(pTempPtr, 0, nBuffSize); + err = nvme_get_log(nFD, NVME_NSID_ALL, ucLogID, false, NVME_NO_LOG_LSP, + nBuffSize, pTempPtr); + *pBuffer = pTempPtr; + +exit_status: + return err; } /* * Plugin Commands */ +static int micron_parse_options(int argc, char **argv, const char *desc, + const struct argconfig_commandline_options *opts, eDriveModel *modelp) +{ + int idx = 0; + int fd = parse_and_open(argc, argv, desc, opts); + + if (fd < 0) { + perror("open"); + return -1; + } + + if (modelp) { + sscanf(argv[optind], "/dev/nvme%d", &idx); + *modelp = GetDriveModel(idx); + } + + return fd; +} -static int micron_selective_download(int argc, char **argv, struct command *cmd, struct plugin *plugin) +static int micron_fw_commit(int fd, int select) { - const char *desc = - "This performs a selective firmware download, which allows the user to " - "select which firmware binary to update for 9200 devices. This requires a power cycle once the " - "update completes. The options available are: \n\n" - "OOB - This updates the OOB and main firmware\n" - "EEP - This updates the eeprom and main firmware\n" - "ALL - This updates the eeprom, OOB, and main firmware"; - const char *fw = "firmware file (required)"; - const char *select = "FW Select (e.g., --select=ALL)"; - int xfer = 4096; - void *fw_buf; - int fd, selectNo, fw_fd, fw_size, err, offset = 0; - struct stat sb; - - struct config { - char *fw; - char *select; - }; - - struct config cfg = { - .fw = "", - .select = "\0", - }; - - OPT_ARGS(opts) = { - OPT_STRING("fw", 'f', "FILE", &cfg.fw, fw), - OPT_STRING("select", 's', "flag", &cfg.select, select), - OPT_END() - }; - - fd = parse_and_open(argc, argv, desc, opts); - - if (fd < 0) - return fd; - - if (strlen(cfg.select) != 3) { - fprintf(stderr, "Invalid select flag\n"); - err = EINVAL; - goto out; - } - - for (int i = 0; i < 3; i++) { - cfg.select[i] = toupper(cfg.select[i]); - } - - if (strncmp(cfg.select, "OOB", 3) == 0) { - selectNo = 18; - } else if (strncmp(cfg.select, "EEP", 3) == 0) { - selectNo = 10; - } else if (strncmp(cfg.select, "ALL", 3) == 0) { - selectNo = 26; - } else { - fprintf(stderr, "Invalid select flag\n"); - err = EINVAL; - goto out; - } - - fw_fd = open(cfg.fw, O_RDONLY); - if (fw_fd < 0) { - fprintf(stderr, "no firmware file provided\n"); - err = EINVAL; - goto out; - } - - err = fstat(fw_fd, &sb); - if (err < 0) { - perror("fstat"); - err = errno; - } - - fw_size = sb.st_size; - if (fw_size & 0x3) { - fprintf(stderr, "Invalid size:%d for f/w image\n", fw_size); - err = EINVAL; - goto out; - } - - if (posix_memalign(&fw_buf, getpagesize(), fw_size)) { - fprintf(stderr, "No memory for f/w size:%d\n", fw_size); - err = ENOMEM; - goto out; - } - - if (read(fw_fd, fw_buf, fw_size) != ((ssize_t) (fw_size))) - return EIO; - - while (fw_size > 0) { - xfer = min(xfer, fw_size); - - err = nvme_fw_download(fd, offset, xfer, fw_buf); - if (err < 0) { - perror("fw-download"); - goto out; - } else if (err != 0) { - fprintf(stderr, "NVME Admin command error:%s(%x)\n", - nvme_status_to_string(err), err); - goto out; - } - fw_buf += xfer; - fw_size -= xfer; - offset += xfer; - } - - err = micron_fw_commit(fd, selectNo); - - if (err == 0x10B || err == 0x20B) { - err = 0; - fprintf(stderr, - "Update successful! Please power cycle for changes to take effect\n"); - } - - out: - return err; + struct nvme_admin_cmd cmd = { + .opcode = nvme_admin_activate_fw, + .cdw10 = 8, + .cdw12 = select, + }; + return ioctl(fd, NVME_IOCTL_ADMIN_CMD, &cmd); } -static int micron_temp_stats(int argc, char **argv, struct command *cmd, struct plugin *plugin) +static int micron_selective_download(int argc, char **argv, + struct command *cmd, struct plugin *plugin) { + const char *desc = + "This performs a selective firmware download, which allows the user to " + "select which firmware binary to update for 9200 devices. This requires " + "a power cycle once the update completes. The options available are: \n\n" + "OOB - This updates the OOB and main firmware\n" + "EEP - This updates the eeprom and main firmware\n" + "ALL - This updates the eeprom, OOB, and main firmware"; + const char *fw = "firmware file (required)"; + const char *select = "FW Select (e.g., --select=ALL)"; + int xfer = 4096; + void *fw_buf; + int fd, selectNo, fw_fd, fw_size, err, offset = 0; + struct stat sb; + + struct config { + char *fw; + char *select; + }; + + struct config cfg = { + .fw = "", + .select = "\0", + }; + + OPT_ARGS(opts) = { + OPT_STRING("fw", 'f', "FILE", &cfg.fw, fw), + OPT_STRING("select", 's', "flag", &cfg.select, select), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + + if (fd < 0) + return fd; + + if (strlen(cfg.select) != 3) { + fprintf(stderr, "Invalid select flag\n"); + err = EINVAL; + goto out; + } + + for (int i = 0; i < 3; i++) { + cfg.select[i] = toupper(cfg.select[i]); + } + + if (strncmp(cfg.select, "OOB", 3) == 0) { + selectNo = 18; + } else if (strncmp(cfg.select, "EEP", 3) == 0) { + selectNo = 10; + } else if (strncmp(cfg.select, "ALL", 3) == 0) { + selectNo = 26; + } else { + fprintf(stderr, "Invalid select flag\n"); + err = EINVAL; + goto out; + } + + fw_fd = open(cfg.fw, O_RDONLY); + if (fw_fd < 0) { + fprintf(stderr, "no firmware file provided\n"); + err = EINVAL; + goto out; + } + + err = fstat(fw_fd, &sb); + if (err < 0) { + perror("fstat"); + err = errno; + } + + fw_size = sb.st_size; + if (fw_size & 0x3) { + fprintf(stderr, "Invalid size:%d for f/w image\n", fw_size); + err = EINVAL; + goto out; + } + + if (posix_memalign(&fw_buf, getpagesize(), fw_size)) { + fprintf(stderr, "No memory for f/w size:%d\n", fw_size); + err = ENOMEM; + goto out; + } + + if (read(fw_fd, fw_buf, fw_size) != ((ssize_t) (fw_size))) + return EIO; + + while (fw_size > 0) { + xfer = min(xfer, fw_size); + + err = nvme_fw_download(fd, offset, xfer, fw_buf); + if (err < 0) { + perror("fw-download"); + goto out; + } else if (err != 0) { + fprintf(stderr, "NVME Admin command error:%s(%x)\n", + nvme_status_to_string(err), err); + goto out; + } + fw_buf += xfer; + fw_size -= xfer; + offset += xfer; + } + + err = micron_fw_commit(fd, selectNo); + + if (err == 0x10B || err == 0x20B) { + err = 0; + fprintf(stderr, + "Update successful! Power cycle for changes to take effect\n"); + } + +out: + return err; +} - struct nvme_smart_log smart_log; - unsigned int temperature = 0, i = 0, fd = 0, err = 0; - unsigned int tempSensors[SensorCount] = { 0 }; - const char *desc = "Retrieve Micron temperature info for the given device "; - - OPT_ARGS(opts) = { - OPT_END() - }; - - fd = parse_and_open(argc, argv, desc, opts); - if (fd < 0) { - printf("\nDevice not found \n");; - return -1; - } - - err = nvme_smart_log(fd, 0xffffffff, &smart_log); - if (!err) { - printf("Micron temperature information:\n"); - temperature = ((smart_log.temperature[1] << 8) | smart_log.temperature[0]); - temperature = temperature ? temperature - 273 : 0; - for (i = 0; i < SensorCount; i++) { - tempSensors[i] = le16_to_cpu(smart_log.temp_sensor[i]); - tempSensors[i] = tempSensors[i] ? tempSensors[i] - 273 : 0; - } - printf("%-10s : %u C\n", "Current Composite Temperature", temperature); - for (i = 0; i < SensorCount; i++) { - printf("%-10s%d : %u C\n", "Temperature Sensor #", i + 1, tempSensors[i]); - } - } - return err; +static int micron_smbus_option(int argc, char **argv, + struct command *cmd, struct plugin *plugin) +{ + __u32 result = 0; + __u32 cdw10 = 0; + __u32 cdw11 = 0; + const char *desc = "Enable/Disable/Get status of SMBUS option on controller"; + const char *option = "enable or disable or status"; + const char *value = "1 - hottest component temperature, 0 - composite " + "temperature (default) for enable option, 0 (current), " + "1 (default), 2 (saved) for status options"; + const char *save = "1 - persistent, 0 - non-persistent (default)"; + int err = 0; + int fd = 0; + int fid = MICRON_FEATURE_SMBUS_OPTION; + eDriveModel model = UNKNOWN_MODEL; + + struct { + char *option; + int value; + int save; + int status; + } opt = { + .option = "disable", + .value = 0, + .save = 0, + .status = 0, + }; + + OPT_ARGS(opts) = { + OPT_STRING("option", 'o', "option", &opt.option, option), + OPT_UINT("value", 'v', &opt.value, value), + OPT_UINT("save", 's', &opt.save, save), + OPT_END() + }; + + if ((fd = micron_parse_options(argc, argv, desc, opts, &model)) < 0) + return err; + + if (model != M5407 && model != M5411) { + printf ("This option is not supported for specified drive\n"); + close(fd); + return err; + } + + if (!strcmp(opt.option, "enable")) { + cdw11 = opt.value << 1 | 1; + err = nvme_set_feature(fd, 1, fid, cdw11, 0, opt.save, 0, 0, &result); + if (err == 0) { + printf("successfully enabled SMBus on drive\n"); + } else { + printf("Failed to enabled SMBus on drive\n"); + } + } + else if (!strcmp(opt.option, "status")) { + cdw10 = opt.value; + err = nvme_get_feature(fd, 1, fid, cdw10, 0, 0, 0, &result); + if (err == 0) { + printf("SMBus status on the drive: %s (returns %s temperature) \n", + (result & 1) ? "enabled" : "disabled", + (result & 2) ? "hottest component" : "composite"); + } else { + printf("Failed to retrieve SMBus status on the drive\n"); + } + } + else if (!strcmp(opt.option, "disable")) { + cdw11 = opt.value << 1 | 0; + err = nvme_set_feature(fd, 1, fid, cdw11, 0, opt.save, 0, 0, &result); + if (err == 0) { + printf("Successfully disabled SMBus on drive\n"); + } else { + printf("Failed to disable SMBus on drive\n"); + } + } else { + printf("Invalid option %s, valid values are enable, disable or status\n", + opt.option); + close(fd); + return -1; + } + + close(fd); + return err; } -static int micron_pcie_stats(int argc, char **argv, struct command *cmd, struct plugin *plugin) +static int micron_temp_stats(int argc, char **argv, struct command *cmd, + struct plugin *plugin) { - int err = 0, bus = 0, domain = 0, device = 0, function = 0; - char strTempFile[1024], strTempFile2[1024], command[1024]; - char *businfo = NULL; - char *devicename = NULL; - char tdevice[NAME_MAX] = { 0 }; - ssize_t sLinkSize = 0; - FILE *fp; - char correctable[8] = { 0 }; - char uncorrectable[8] = { 0 }; - char *res; - - if (argc != 2) { - printf("vs-pcie-stats: Invalid argument\n"); - printf("Usage: nvme micron vs-pcie-stats \n\n"); - goto out; - } - if (strstr(argv[optind], "/dev/nvme") && strstr(argv[optind], "n1")) { - devicename = strrchr(argv[optind], '/'); - } else if (strstr(argv[optind], "/dev/nvme")) { - devicename = strrchr(argv[optind], '/'); - sprintf(tdevice, "%s%s", devicename, "n1"); - devicename = tdevice; - } else { - printf("Invalid device specified!\n"); - goto out; - } - sprintf(strTempFile, "/sys/block/%s/device", devicename); - sLinkSize = readlink(strTempFile, strTempFile2, 1024); - if (sLinkSize < 0) { - printf("Unable to read device\n"); - goto out; - } - if (strstr(strTempFile2, "../../nvme")) { - sprintf(strTempFile, "/sys/block/%s/device/device", devicename); - sLinkSize = readlink(strTempFile, strTempFile2, 1024); - if (sLinkSize < 0) { - printf("Unable to read device\n"); - goto out; - } - } - businfo = strrchr(strTempFile2, '/'); - sscanf(businfo, "/%x:%x:%x.%x", &domain, &bus, &device, &function); - sprintf(command, "setpci -s %x:%x.%x ECAP_AER+10.L", bus, device, - function); - fp = popen(command, "r"); - if (fp == NULL) { - printf("Unable to retrieve error count\n"); - goto out; - } - res = fgets(correctable, sizeof(correctable), fp); - if (res == NULL) { - printf("Unable to retrieve error count\n"); - goto out; - } - pclose(fp); - - sprintf(command, "setpci -s %x:%x.%x ECAP_AER+0x4.L", bus, device, - function); - fp = popen(command, "r"); - if (fp == NULL) { - printf("Unable to retrieve error count\n"); - goto out; - } - res = fgets(uncorrectable, sizeof(uncorrectable), fp); - if (res == NULL) { - printf("Unable to retrieve error count\n"); - goto out; - } - pclose(fp); - printf("PCIE Stats:\n"); - printf("Device correctable errors detected: %s\n", correctable); - printf("Device uncorrectable errors detected: %s\n", uncorrectable); - - out: - return err; + + struct nvme_smart_log smart_log; + unsigned int temperature = 0, i = 0, err = 0; + unsigned int tempSensors[SensorCount] = { 0 }; + const char *desc = "Retrieve Micron temperature info for the given device "; + const char *fmt = "output format normal|json"; + struct format { + char *fmt; + }; + struct format cfg = { + .fmt = "normal", + }; + bool is_json = false; + struct json_object *root; + struct json_object *logPages; + int fd; + + OPT_ARGS(opts) = { + OPT_FMT("format", 'f', &cfg.fmt, fmt), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) { + printf("\nDevice not found \n");; + return -1; + } + + if (strcmp(cfg.fmt, "json") == 0) + is_json = true; + + err = nvme_smart_log(fd, 0xffffffff, &smart_log); + if (!err) { + temperature = ((smart_log.temperature[1] << 8) | smart_log.temperature[0]); + temperature = temperature ? temperature - 273 : 0; + for (i = 0; i < SensorCount; i++) { + tempSensors[i] = le16_to_cpu(smart_log.temp_sensor[i]); + tempSensors[i] = tempSensors[i] ? tempSensors[i] - 273 : 0; + } + if (is_json) { + struct json_object *stats = json_create_object(); + char tempstr[64] = { 0 }; + root = json_create_object(); + logPages = json_create_array(); + json_object_add_value_array(root, "Micron temperature information", logPages); + sprintf(tempstr, "%u C", temperature); + json_object_add_value_string(stats, "Current Composite Temperature", tempstr); + for (i = 0; i < SensorCount; i++) { + char sensor_str[256] = { 0 }; + char datastr[64] = { 0 }; + sprintf(sensor_str, "Temperature Sensor #%d", (i + 1)); + sprintf(datastr, "%u C", tempSensors[i]); + json_object_add_value_string(stats, sensor_str, datastr); + } + json_array_add_value_object(logPages, stats); + json_print_object(root, NULL); + printf("\n"); + json_free_object(root); + } else { + printf("Micron temperature information:\n"); + printf("%-10s : %u C\n", "Current Composite Temperature", temperature); + for (i = 0; i < SensorCount; i++) { + printf("%-10s%d : %u C\n", "Temperature Sensor #", i + 1, tempSensors[i]); + } + } + } + return err; } -static int micron_clear_pcie_correctable_errors(int argc, char **argv, - struct command *cmd, - struct plugin *plugin) +static int micron_pcie_stats(int argc, char **argv, + struct command *cmd, struct plugin *plugin) { - int err = 0, bus = 0, domain = 0, device = 0, function = 0; - char strTempFile[1024], strTempFile2[1024], command[1024]; - char *businfo = NULL; - char *devicename = NULL; - char tdevice[PATH_MAX] = { 0 }; - ssize_t sLinkSize = 0; - FILE *fp; - char correctable[8] = { 0 }; - char *res; - - if (argc != 2) { - printf("clear-pcie-correctable-errors: Invalid argument\n"); - printf ("Usage: nvme micron clear-pcie-correctable-errors \n\n"); - goto out; - } - if (strstr(argv[optind], "/dev/nvme") && strstr(argv[optind], "n1")) { - devicename = strrchr(argv[optind], '/'); - } else if (strstr(argv[optind], "/dev/nvme")) { - devicename = strrchr(argv[optind], '/'); - sprintf(tdevice, "%s%s", devicename, "n1"); - devicename = tdevice; - } else { - printf("Invalid device specified!\n"); - goto out; - } - err = snprintf(strTempFile, sizeof(strTempFile), - "/sys/block/%s/device", devicename); - if (err < 0) - goto out; - sLinkSize = readlink(strTempFile, strTempFile2, 1024); - if (sLinkSize < 0) { - printf("Unable to read device\n"); - goto out; - } - if (strstr(strTempFile2, "../../nvme")) { - err = snprintf(strTempFile, sizeof(strTempFile), - "/sys/block/%s/device/device", devicename); - if (err < 0) - goto out; - sLinkSize = readlink(strTempFile, strTempFile2, 1024); - if (sLinkSize < 0) { - printf("Unable to read device\n"); - goto out; - } - } - businfo = strrchr(strTempFile2, '/'); - sscanf(businfo, "/%x:%x:%x.%x", &domain, &bus, &device, &function); - sprintf(command, "setpci -s %x:%x.%x ECAP_AER+0x10.L=0xffffffff", bus, - device, function); - - fp = popen(command, "r"); - if (fp == NULL) { - printf("Unable to clear error count\n"); - goto out; - } - pclose(fp); - - sprintf(command, "setpci -s %x:%x.%x ECAP_AER+0x10.L", bus, device, - function); - fp = popen(command, "r"); - if (fp == NULL) { - printf("Unable to retrieve error count\n"); - goto out; - } - res = fgets(correctable, sizeof(correctable), fp); - if (res == NULL) { - printf("Unable to retrieve error count\n"); - goto out; - } - pclose(fp); - printf("Device correctable errors cleared!\n"); - printf("Device correctable errors detected: %s\n", correctable); - - out: - return err; + int i, fd, err = 0, bus = 0, domain = 0, device = 0, function = 0, ctrlIdx; + char strTempFile[1024], strTempFile2[1024], command[1024]; + char *businfo = NULL; + char *devicename = NULL; + char tdevice[NAME_MAX] = { 0 }; + ssize_t sLinkSize = 0; + FILE *fp; + char correctable[8] = { 0 }; + char uncorrectable[8] = { 0 }; + eDriveModel eModel = UNKNOWN_MODEL; + char *res; + bool is_json = true; + struct format { + char *fmt; + }; + const char *desc = "Retrieve PCIe event counters"; + const char *fmt = "output format json|normal"; + struct format cfg = { + .fmt = "json", + }; + struct { + char *err; + int bit; + int val; + } pcie_correctable_errors[] = { + { "Unsupported Request Error Status (URES)", 20}, + { "ECRC Error Status (ECRCES)", 19}, + { "Malformed TLP Status (MTS)", 18}, + { "Receiver Overflow Status (ROS)", 17}, + { "Unexpected Completion Status (UCS)", 16}, + { "Completer Abort Status (CAS)", 15}, + { "Completion Timeout Stats (CTS)", 14}, + { "Flow Control Protocol Error Status (FCPES)", 13}, + { "Poisoned TLP Status (PTS)", 12}, + { "Data Link Protocol Error Status (DLPES)", 4}, + }, + pcie_uncorrectable_errors[] = { + { "Advisory Non-Fatal Error Status (ANFES)", 13}, + { "Replay Timer Timeout Status (RTS)", 12}, + { "REPLY NUM Rollover Status (RRS)", 8}, + { "Bad DLLP Status (BDS)", 7}, + { "Bad TLP Status (BTS)", 6}, + { "Receiver Error Status (RES)", 0}, + }; + + __u32 correctable_errors; + __u32 uncorrectable_errors; + + OPT_ARGS(opts) = { + OPT_FMT("format", 'f', &cfg.fmt, fmt), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) { + printf("\nDevice not found \n");; + return -1; + } + + /* pull log details based on the model name */ + sscanf(argv[optind], "/dev/nvme%d", &ctrlIdx); + if ((eModel = GetDriveModel(ctrlIdx)) == UNKNOWN_MODEL) { + printf ("Unsupported drive model for vs-pcie-stats command\n"); + close(fd); + goto out; + } + + if (strcmp(cfg.fmt, "normal") == 0) + is_json = false; + + if (strstr(argv[optind], "/dev/nvme") && strstr(argv[optind], "n1")) { + devicename = strrchr(argv[optind], '/'); + } else if (strstr(argv[optind], "/dev/nvme")) { + devicename = strrchr(argv[optind], '/'); + sprintf(tdevice, "%s%s", devicename, "n1"); + devicename = tdevice; + } else { + printf("Invalid device specified!\n"); + goto out; + } + sprintf(strTempFile, "/sys/block/%s/device", devicename); + sLinkSize = readlink(strTempFile, strTempFile2, 1024); + if (sLinkSize < 0) { + err = -errno; + printf("Failed to read device\n"); + goto out; + } + if (strstr(strTempFile2, "../../nvme")) { + sprintf(strTempFile, "/sys/block/%s/device/device", devicename); + sLinkSize = readlink(strTempFile, strTempFile2, 1024); + if (sLinkSize < 0) { + err = -errno; + printf("Failed to read device\n"); + goto out; + } + } + businfo = strrchr(strTempFile2, '/'); + sscanf(businfo, "/%x:%x:%x.%x", &domain, &bus, &device, &function); + sprintf(command, "setpci -s %x:%x.%x ECAP_AER+10.L", bus, device, + function); + fp = popen(command, "r"); + if (fp == NULL) { + printf("Failed to retrieve error count\n"); + goto out; + } + res = fgets(correctable, sizeof(correctable), fp); + if (res == NULL) { + printf("Failed to retrieve error count\n"); + goto out; + } + pclose(fp); + + sprintf(command, "setpci -s %x:%x.%x ECAP_AER+0x4.L", bus, device, + function); + fp = popen(command, "r"); + if (fp == NULL) { + printf("Failed to retrieve error count\n"); + goto out; + } + res = fgets(uncorrectable, sizeof(uncorrectable), fp); + if (res == NULL) { + printf("Failed to retrieve error count\n"); + goto out; + } + pclose(fp); + + correctable_errors = (__u32)strtol(correctable, NULL, 16); + uncorrectable_errors = (__u32)strtol(uncorrectable, NULL, 16); + + if (is_json) { + + struct json_object *root = json_create_object(); + struct json_object *pcieErrors = json_create_array(); + struct json_object *stats = json_create_object(); + + json_object_add_value_array(root, "PCIE Stats", pcieErrors); + for (i = 0; i < sizeof(pcie_correctable_errors) / sizeof(pcie_correctable_errors[0]); i++) { + json_object_add_value_int(stats, pcie_correctable_errors[i].err, + ((correctable_errors >> pcie_correctable_errors[i].bit) & 1)); + } + for (i = 0; i < sizeof(pcie_uncorrectable_errors) / sizeof(pcie_uncorrectable_errors[0]); i++) { + json_object_add_value_int(stats, pcie_uncorrectable_errors[i].err, + ((uncorrectable_errors >> pcie_uncorrectable_errors[i].bit) & 1)); + } + json_array_add_value_object(pcieErrors, stats); + json_print_object(root, NULL); + printf("\n"); + json_free_object(root); + } else if (eModel == M5407 || eModel == M5410) { + for (i = 0; i < sizeof(pcie_correctable_errors) / sizeof(pcie_correctable_errors[0]); i++) { + printf("%-40s : %-1d\n", pcie_correctable_errors[i].err, + ((correctable_errors >> pcie_correctable_errors[i].bit) & 1)); + } + for (i = 0; i < sizeof(pcie_uncorrectable_errors) / sizeof(pcie_uncorrectable_errors[0]); i++) { + printf("%-40s : %-1d\n", pcie_uncorrectable_errors[i].err, + ((uncorrectable_errors >> pcie_uncorrectable_errors[i].bit) & 1)); + } + } else { + printf("PCIE Stats:\n"); + printf("Device correctable errors detected: %s\n", correctable); + printf("Device uncorrectable errors detected: %s\n", uncorrectable); + } + +out: + return err; } -static int micron_nand_stats(int argc, char **argv, struct command *cmd, struct plugin *plugin) +static int micron_clear_pcie_correctable_errors(int argc, char **argv, + struct command *cmd, + struct plugin *plugin) { - const char *desc = "Retrieve Micron NAND stats for the given device "; - unsigned int extSmartLog[64] = { 0 }; - struct nvme_id_ctrl ctrl; - int fd, err; - - OPT_ARGS(opts) = { - OPT_END() - }; - - fd = parse_and_open(argc, argv, desc, opts); - if (fd < 0) { - printf("\nDevice not found \n");; - return -1; - } - - err = nvme_identify_ctrl(fd, &ctrl); - if (err) - goto out; - - err = NVMEGetLogPage(fd, 0xD0, (unsigned char *)extSmartLog, D0_log_size); - if (err) - goto out; - - unsigned long long count = ((unsigned long long)extSmartLog[45] << 32) | extSmartLog[44]; - printf("%-40s : 0x%llx\n", "NAND Writes (Bytes Written)", count); - printf("%-40s : ", "Program Failure Count"); - - unsigned long long count_hi = ((unsigned long long)extSmartLog[39] << 32) | extSmartLog[38]; - unsigned long long count_lo = ((unsigned long long)extSmartLog[37] << 32) | extSmartLog[36]; - if (count_hi != 0) - printf("0x%llx%016llx", count_hi, count_lo); - else - printf("0x%llx\n", count_lo); - - count = ((unsigned long long)extSmartLog[25] << 32) | extSmartLog[24]; - printf("%-40s : 0x%llx\n", "Erase Failures", count); - printf("%-40s : 0x%x\n", "Bad Block Count", extSmartLog[3]); - - count = (unsigned long long)extSmartLog[3] - (count_lo + count); - printf("%-40s : 0x%llx\n", "NAND XOR/RAID Recovery Trigger Events", count); - printf("%-40s : 0x%x\n", "NSZE Change Supported", (ctrl.oacs >> 3) & 0x1); - printf("%-40s : 0x%x\n", "Number of NSZE Modifications", extSmartLog[1]); - out: - close(fd); - return err; + int err = -EINVAL, bus = 0, domain = 0, device = 0, function = 0; + char strTempFile[1024], strTempFile2[1024], command[1024]; + char *businfo = NULL; + char *devicename = NULL; + char tdevice[PATH_MAX] = { 0 }; + ssize_t sLinkSize = 0; + eDriveModel model = UNKNOWN_MODEL; + char correctable[8] = { 0 }; + int fd = -1; + FILE *fp; + char *res; + const char *desc = "Clear PCIe Device Correctable Errors"; + __u32 result = 0; + __u8 fid = MICRON_FEATURE_CLEAR_PCI_CORRECTABLE_ERRORS; + OPT_ARGS(opts) = { + OPT_END() + }; + + if ((fd = micron_parse_options(argc, argv, desc, opts, &model)) < 0) + return err; + + /* For M51CX models, PCIe errors are cleared using 0xC3 feature */ + if (model == M51CX) { + err = nvme_set_feature(fd, 0, fid, (1 << 31), 0, 0, 0, 0, &result); + if (err == 0 && (err = (int)result) == 0) + printf("Device correctable errors cleared!\n"); + else + printf("Error clearing Device correctable errors = 0x%x\n", err); + goto out; + } + + if (strstr(argv[optind], "/dev/nvme") && strstr(argv[optind], "n1")) { + devicename = strrchr(argv[optind], '/'); + } else if (strstr(argv[optind], "/dev/nvme")) { + devicename = strrchr(argv[optind], '/'); + sprintf(tdevice, "%s%s", devicename, "n1"); + devicename = tdevice; + } else { + printf("Invalid device specified!\n"); + goto out; + } + err = snprintf(strTempFile, sizeof(strTempFile), + "/sys/block/%s/device", devicename); + if (err < 0) + goto out; + + sLinkSize = readlink(strTempFile, strTempFile2, 1024); + if (sLinkSize < 0) { + err = -errno; + printf("Failed to read device\n"); + goto out; + } + if (strstr(strTempFile2, "../../nvme")) { + err = snprintf(strTempFile, sizeof(strTempFile), + "/sys/block/%s/device/device", devicename); + if (err < 0) + goto out; + sLinkSize = readlink(strTempFile, strTempFile2, 1024); + if (sLinkSize < 0) { + err = -errno; + printf("Failed to read device\n"); + goto out; + } + } + businfo = strrchr(strTempFile2, '/'); + sscanf(businfo, "/%x:%x:%x.%x", &domain, &bus, &device, &function); + sprintf(command, "setpci -s %x:%x.%x ECAP_AER+0x10.L=0xffffffff", bus, + device, function); + err = -1; + fp = popen(command, "r"); + if (fp == NULL) { + printf("Failed to clear error count\n"); + goto out; + } + pclose(fp); + + sprintf(command, "setpci -s %x:%x.%x ECAP_AER+0x10.L", bus, device, + function); + fp = popen(command, "r"); + if (fp == NULL) { + printf("Failed to retrieve error count\n"); + goto out; + } + res = fgets(correctable, sizeof(correctable), fp); + if (res == NULL) { + printf("Failed to retrieve error count\n"); + goto out; + } + pclose(fp); + printf("Device correctable errors cleared!\n"); + printf("Device correctable errors detected: %s\n", correctable); + err = 0; +out: + if (fd > 0) + close(fd); + return err; } -typedef enum { M5410 = 0, M51AX, M51BX, UNKNOWN_MODEL } eDriveModel; +static struct logpage { + const char *field; + char datastr[128]; +} d0_log_page[] = { + { "NAND Writes (Bytes Written)", { 0 }}, + { "Program Failure Count", { 0 }}, + { "Erase Failures", { 0 }}, + { "Bad Block Count", { 0 }}, + { "NAND XOR/RAID Recovery Trigger Events", { 0 }}, + { "NSZE Change Supported", { 0 }}, + { "Number of NSZE Modifications", { 0 }} +}; + +static void init_d0_log_page(__u8 *buf, __u8 nsze) +{ + unsigned int logD0[D0_log_size/sizeof(int)] = { 0 }; + __u64 count_lo, count_hi, count; -static char *fvendorid1 = "/sys/class/nvme/nvme%d/device/vendor"; -static char *fvendorid2 = "/sys/class/misc/nvme%d/device/vendor"; -static char *fdeviceid1 = "/sys/class/nvme/nvme%d/device/device"; -static char *fdeviceid2 = "/sys/class/misc/nvme%d/device/device"; -static unsigned short vendor_id; -static unsigned short device_id; + memcpy(logD0, buf, sizeof(logD0)); -static int ReadSysFile(const char *file, unsigned short *id) + + count = ((__u64)logD0[45] << 32) | logD0[44]; + sprintf(d0_log_page[0].datastr, "0x%"PRIx64, le64_to_cpu(count)); + + count_hi = ((__u64)logD0[39] << 32) | logD0[38]; + count_lo = ((__u64)logD0[37] << 32) | logD0[36]; + if (count_hi != 0) + sprintf(d0_log_page[1].datastr, "0x%"PRIx64"%016"PRIx64, + le64_to_cpu(count_hi), le64_to_cpu(count_lo)); + else + sprintf(d0_log_page[1].datastr, "0x%"PRIx64, le64_to_cpu(count_lo)); + + count = ((__u64)logD0[25] << 32) | logD0[24]; + sprintf(d0_log_page[2].datastr, "0x%"PRIx64, le64_to_cpu(count)); + + sprintf(d0_log_page[3].datastr, "0x%x", logD0[3]); + + count_lo = ((__u64)logD0[37] << 32) | logD0[36]; + count = ((__u64)logD0[25] << 32) | logD0[24]; + count = (__u64)logD0[3] - (count_lo + count); + sprintf(d0_log_page[4].datastr, "0x%"PRIx64, le64_to_cpu(count)); + + sprintf(d0_log_page[5].datastr, "0x%x", nsze); + sprintf(d0_log_page[6].datastr, "0x%x", logD0[1]); +} + +/* OCP and Vendor specific log data format */ +struct micron_vs_logpage { + char *field; + int size; +} +/* Smart Health Log information as per OCP spec */ +ocp_c0_log_page[] = { + { "Physical Media Units Written", 16 }, + { "Physical Media Units Read", 16 }, + { "Raw Bad User NAND Block Count", 6}, + { "Normalized Bad User NAND Block Count", 2}, + { "Raw Bad System NAND Block Count", 6}, + { "Normalized Bad System NAND Block Count", 2}, + { "XOR Recovery Count", 8}, + { "Uncorrectable Read Error Count", 8}, + { "Soft ECC Error Count", 8}, + { "SSD End to End Detected Counts", 4}, + { "SSD End to End Corrected Errors", 4}, + { "System data % life-used", 1}, + { "Refresh Count", 7}, + { "Maximum User Data Erase Count", 4}, + { "Minimum User Data Erase Count", 4}, + { "Thermal Throttling Count", 1}, + { "Thermal Throttling Status", 1}, + { "Reserved", 6}, + { "PCIe Correctable Error count", 8}, + { "Incomplete Shutdowns", 4}, + { "Reserved", 4}, + { "% Free Blocks", 1}, + { "Reserved", 7}, + { "Capacitor Health", 2}, + { "Reserved", 6}, + { "Unaligned I/O", 8}, + { "Security Version Number", 8}, + { "NUSE", 8}, + { "PLP Start Count", 16}, + { "Endurance Estimate", 16}, + { "Reserved", 302}, + { "Log Page Version", 2}, + { "Log Page GUID", 16}, +}, +/* Vendor Specific Health Log information */ +fb_log_page[] = { + { "Physical Media Units Written - TLC", 16 }, + { "Physical Media Units Written - SLC", 16 }, + { "Normalized Bad User NAND Block Count", 2}, + { "Raw Bad User NAND Block Count", 6}, + { "XOR Recovery Count", 8}, + { "Uncorrectable Read Error Count", 8}, + { "SSD End to End Corrected Errors", 8}, + { "SSD End to End Detected Counts", 4}, + { "SSD End to End Uncorrected Counts", 4}, + { "System data % life-used", 1}, + { "Minimum User Data Erase Count - TLC", 8}, + { "Maximum User Data Erase Count - TLC", 8}, + { "Minimum User Data Erase Count - SLC", 8}, + { "Maximum User Data Erase Count - SLC", 8}, + { "Normalized Program Fail Count", 2}, + { "Raw Program Fail Count", 6}, + { "Normalized Erase Fail Count", 2}, + { "Raw Erase Fail Count", 6}, + { "Pcie Correctable Error Count", 8}, + { "% Free Blocks (User)", 1}, + { "Security Version Number", 8}, + { "% Free Blocks (System)", 1}, + { "Dataset Management (Deallocate) Commands", 16}, + { "Incomplete TRIM Data", 8}, + { "% Age of Completed TRIM", 1}, + { "Background Back-Pressure Gauge", 1}, + { "Soft ECC Error Count", 8}, + { "Refresh Count", 8}, + { "Normalized Bad System NAND Block Count", 2}, + { "Raw Bad System NAND Block Count", 6}, + { "Endurance Estimate", 16}, + { "Thermal Throttling Count", 1}, + { "Thermal Throttling Status", 1}, + { "Unaligned I/O", 8}, + { "Physical Media Units Read", 16}, + { "Reserved", 279}, + { "Log Page Version", 2} +}; + +/* Common function to print Micron VS log pages */ +static void print_micron_vs_logs( + __u8 *buf, /* raw log data */ + struct micron_vs_logpage *log_page, /* format of the data */ + int field_count, /* log field count */ + struct json_object *stats /* json object to add fields */ +) { - int ret = 0; - char idstr[32] = { '\0' }; - int fd = open(file, O_RDONLY); + __u64 lval_lo, lval_hi; + __u32 ival; + __u16 sval; + __u8 cval, lval[8] = { 0 }; + int field, guid_index; + int offset = 0; + + for (field = 0; field < field_count; field++) { + char datastr[1024] = { 0 }; + if (log_page[field].size == 16) { + if (strstr(log_page[field].field, "GUID")) { + char *tmpstr = datastr; + tmpstr += sprintf(datastr, "0x"); + for(guid_index = 0; guid_index < 16; guid_index++) + tmpstr += sprintf(tmpstr, "%01X", buf[offset + guid_index]); + } else { + lval_lo = *((__u64 *)(&buf[offset])); + lval_hi = *((__u64 *)(&buf[offset + 8])); + if (lval_hi) + sprintf(datastr, "0x%"PRIx64"_%"PRIx64"", + le64_to_cpu(lval_hi), le64_to_cpu(lval_lo)); + else + sprintf(datastr, "0x%"PRIx64"", le64_to_cpu(lval_lo)); + } + } else if (log_page[field].size == 8) { + lval_lo = *((__u64 *)(&buf[offset])); + sprintf(datastr, "0x%"PRIx64"", le64_to_cpu(lval_lo)); + } else if (log_page[field].size == 7) { + /* 7 bytes will be in little-endian format, with last byte as MSB */ + memcpy(&lval[0], &buf[offset], 7); + memcpy((void *)&lval_lo, lval, 8); + sprintf(datastr, "0x%"PRIx64"", le64_to_cpu(lval_lo)); + } else if (log_page[field].size == 6) { + ival = *((__u32 *)(&buf[offset])); + sval = *((__u16 *)(&buf[offset + 4])); + lval_lo = (((__u64)sval << 32) | ival); + sprintf(datastr, "0x%"PRIx64"", le64_to_cpu(lval_lo)); + } else if (log_page[field].size == 4) { + ival = *((__u32 *)(&buf[offset])); + sprintf(datastr, "0x%x", le32_to_cpu(ival)); + } else if (log_page[field].size == 2) { + sval = *((__u16 *)(&buf[offset])); + sprintf(datastr, "0x%04x", le16_to_cpu(sval)); + } else if (log_page[field].size == 1) { + cval = buf[offset]; + sprintf(datastr, "0x%02x", cval); + } else { + sprintf(datastr, "0"); + } + offset += log_page[field].size; + /* do not print reserved values */ + if (strstr(log_page[field].field, "Reserved")) + continue; + if (stats != NULL) { + json_object_add_value_string(stats, log_page[field].field, datastr); + } else { + printf("%-40s : %-4s\n", log_page[field].field, datastr); + } + } +} - if (fd > 0) { - ret = read(fd, idstr, sizeof(idstr)); - close(fd); - } +static void print_smart_cloud_health_log(__u8 *buf, bool is_json) +{ + struct json_object *root; + struct json_object *logPages; + struct json_object *stats = NULL; + int field_count = sizeof(ocp_c0_log_page)/sizeof(ocp_c0_log_page[0]); + + if (is_json) { + root = json_create_object(); + stats = json_create_object(); + logPages = json_create_array(); + json_object_add_value_array(root, "OCP SMART Cloud Health Log: 0xC0", + logPages); + } + + print_micron_vs_logs(buf, ocp_c0_log_page, field_count, stats); + + if (is_json) { + json_array_add_value_object(logPages, stats); + json_print_object(root, NULL); + printf("\n"); + json_free_object(root); + } +} - if (fd < 0 || ret < 0) - perror(file); - else - *id = strtol(idstr, NULL, 16); +static void print_nand_stats_fb(__u8 *buf, __u8 *buf2, __u8 nsze, bool is_json) +{ + struct json_object *root; + struct json_object *logPages; + struct json_object *stats = NULL; + int field_count = sizeof(fb_log_page)/sizeof(fb_log_page[0]); + + if (is_json) { + root = json_create_object(); + stats = json_create_object(); + logPages = json_create_array(); + json_object_add_value_array(root, "Extended Smart Log Page : 0xFB", + logPages); + } + + print_micron_vs_logs(buf, fb_log_page, field_count, stats); + + /* print last three entries from D0 log page */ + init_d0_log_page(buf2, nsze); + + if (is_json) { + for (int i = 4; i < 7; i++) { + json_object_add_value_string(stats, + d0_log_page[i].field, + d0_log_page[i].datastr); + } + } else { + for (int i = 4; i < 7; i++) { + printf("%-40s : %s\n", d0_log_page[i].field, d0_log_page[i].datastr); + } + } + + if (is_json) { + json_array_add_value_object(logPages, stats); + json_print_object(root, NULL); + printf("\n"); + json_free_object(root); + } +} - return ret; +static void print_nand_stats_d0(__u8 *buf, __u8 oacs, bool is_json) +{ + init_d0_log_page(buf, oacs); + + if (is_json) { + struct json_object *root = json_create_object(); + struct json_object *stats = json_create_object(); + struct json_object *logPages = json_create_array(); + + json_object_add_value_array(root, + "Extended Smart Log Page : 0xD0", + logPages); + + for (int i = 0; i < 7; i++) { + json_object_add_value_string(stats, + d0_log_page[i].field, + d0_log_page[i].datastr); + } + + json_array_add_value_object(logPages, stats); + json_print_object(root, NULL); + printf("\n"); + json_free_object(root); + } else { + for (int i = 0; i < 7; i++) { + printf("%-40s : %s\n", d0_log_page[i].field, d0_log_page[i].datastr); + } + } } -static eDriveModel GetDriveModel(int idx) +static bool nsze_from_oacs = false; /* read nsze for now from idd[4059] */ + +static int micron_nand_stats(int argc, char **argv, + struct command *cmd, struct plugin *plugin) { - eDriveModel eModel = UNKNOWN_MODEL; - char path[512]; - - sprintf(path, fvendorid1, idx); - if (ReadSysFile(path, &vendor_id) < 0) { - sprintf(path, fvendorid2, idx); - ReadSysFile(path, &vendor_id); - } - sprintf(path, fdeviceid1, idx); - if (ReadSysFile(path, &device_id) < 0) { - sprintf(path, fdeviceid2, idx); - ReadSysFile(path, &device_id); - } - - if (vendor_id == 0x1344) { - switch (device_id) { - case 0x5410: - eModel = M5410; - break; - case 0x51A0: - case 0x51A1: - case 0x51A2: - eModel = M51AX; - break; - case 0x51B0: - case 0x51B1: - case 0x51B2: - eModel = M51BX; - break; - default: - break; - } - } - return eModel; + const char *desc = "Retrieve Micron NAND stats for the given device "; + unsigned int extSmartLog[D0_log_size/sizeof(int)] = { 0 }; + unsigned int logFB[FB_log_size/sizeof(int)] = { 0 }; + eDriveModel eModel = UNKNOWN_MODEL; + struct nvme_id_ctrl ctrl; + int fd, err, ctrlIdx; + __u8 nsze; + bool has_d0_log = true; + bool has_fb_log = false; + bool is_json = true; + struct format { + char *fmt; + }; + const char *fmt = "output format json|normal"; + struct format cfg = { + .fmt = "json", + }; + + OPT_ARGS(opts) = { + OPT_FMT("format", 'f', &cfg.fmt, fmt), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) { + printf("\nDevice not found \n");; + return -1; + } + + if (strcmp(cfg.fmt, "normal") == 0) + is_json = false; + + err = nvme_identify_ctrl(fd, &ctrl); + if (err) + goto out; + + /* pull log details based on the model name */ + sscanf(argv[optind], "/dev/nvme%d", &ctrlIdx); + if ((eModel = GetDriveModel(ctrlIdx)) == UNKNOWN_MODEL) { + printf ("Unsupported drive model for vs-nand-stats command\n"); + close(fd); + goto out; + } + + err = nvme_get_log(fd, NVME_NSID_ALL, 0xD0, false, NVME_NO_LOG_LSP, + D0_log_size, extSmartLog); + has_d0_log = (0 == err); + + /* should check for firmware version if this log is supported or not */ + if (eModel == M5407 || eModel == M5410) { + err = nvme_get_log(fd, NVME_NSID_ALL, 0xFB, false, NVME_NO_LOG_LSP, + FB_log_size, logFB); + has_fb_log = (0 == err); + } + + nsze = (ctrl.vs[987] == 0x12); + if (nsze == 0 && nsze_from_oacs) + nsze = ((ctrl.oacs >> 3) & 0x1); + err = 0; + if (has_fb_log) + print_nand_stats_fb((__u8 *)logFB, (__u8 *)extSmartLog, nsze, is_json); + else if (has_d0_log) + print_nand_stats_d0((__u8 *)extSmartLog, nsze, is_json); + else { + printf("Unable to retrieve extended smart log for the drive\n"); + err = -ENOTTY; + } +out: + close(fd); + return err; } + static void GetDriveInfo(const char *strOSDirName, int nFD, - struct nvme_id_ctrl *ctrlp) + struct nvme_id_ctrl *ctrlp) { - FILE *fpOutFile = NULL; - char tempFile[256] = { 0 }; - char strBuffer[1024] = { 0 }; - char model[41] = { 0 }; - char serial[21] = { 0 }; - char fwrev[9] = { 0 }; - char *strPDir = strdup(strOSDirName); - char *strDest = dirname(strPDir); - - sprintf(tempFile, "%s/%s", strDest, "drive-info.txt"); - fpOutFile = fopen(tempFile, "w+"); - if (!fpOutFile) { - printf("Unable to create %s\n", tempFile); - free(strPDir); - return; - } - - strncpy(model, ctrlp->mn, 40); - strncpy(serial, ctrlp->sn, 20); - strncpy(fwrev, ctrlp->fr, 8); - - sprintf(strBuffer, - "********************\nDrive Info\n********************\n"); - - fprintf(fpOutFile, "%s", strBuffer); - sprintf(strBuffer, - "%-20s : /dev/nvme%d\n%-20s : %s\n%-20s : %-20s\n%-20s : %-20s\n", - "Device Name", nFD, - "Model No", (char *)model, - "Serial No", (char *)serial, "FW-Rev", (char *)fwrev); - - fprintf(fpOutFile, "%s", strBuffer); - - sprintf(strBuffer, - "\n********************\nPCI Info\n********************\n"); - - fprintf(fpOutFile, "%s", strBuffer); - - sprintf(strBuffer, - "%-22s : %04X\n%-22s : %04X\n", - "VendorId", vendor_id, "DeviceId", device_id); - fprintf(fpOutFile, "%s", strBuffer); - fclose(fpOutFile); - free(strPDir); + FILE *fpOutFile = NULL; + char tempFile[256] = { 0 }; + char strBuffer[1024] = { 0 }; + char model[41] = { 0 }; + char serial[21] = { 0 }; + char fwrev[9] = { 0 }; + char *strPDir = strdup(strOSDirName); + char *strDest = dirname(strPDir); + + sprintf(tempFile, "%s/%s", strDest, "drive-info.txt"); + fpOutFile = fopen(tempFile, "w+"); + if (!fpOutFile) { + printf("Failed to create %s\n", tempFile); + free(strPDir); + return; + } + + strncpy(model, ctrlp->mn, 40); + strncpy(serial, ctrlp->sn, 20); + strncpy(fwrev, ctrlp->fr, 8); + + sprintf(strBuffer, + "********************\nDrive Info\n********************\n"); + + fprintf(fpOutFile, "%s", strBuffer); + sprintf(strBuffer, + "%-20s : /dev/nvme%d\n%-20s : %s\n%-20s : %-20s\n%-20s : %-20s\n", + "Device Name", nFD, + "Model No", (char *)model, + "Serial No", (char *)serial, "FW-Rev", (char *)fwrev); + + fprintf(fpOutFile, "%s", strBuffer); + + sprintf(strBuffer, + "\n********************\nPCI Info\n********************\n"); + + fprintf(fpOutFile, "%s", strBuffer); + + sprintf(strBuffer, + "%-22s : %04X\n%-22s : %04X\n", + "VendorId", vendor_id, "DeviceId", device_id); + fprintf(fpOutFile, "%s", strBuffer); + fclose(fpOutFile); + free(strPDir); } static void GetTimestampInfo(const char *strOSDirName) { - char outstr[200]; - time_t t; - struct tm *tmp; - FILE *fpOutFile = NULL; - size_t num; - char tempFolder[256] = { 0 }; - char *strPDir; - char *strDest; - - t = time(NULL); - tmp = localtime(&t); - if (tmp == NULL) - return; - - num = strftime(outstr, sizeof(outstr), "Timestamp (UTC): %a, %d %b %Y %T %z", tmp); - if (num) { - strPDir = strdup(strOSDirName); - strDest = dirname(strPDir); - sprintf(tempFolder, "%s/%s", strDest, "timestamp_info.txt"); - fpOutFile = fopen(tempFolder, "wb"); - if (fwrite(outstr, 1, num, fpOutFile) != num) - printf("Unable to write to %s file!", tempFolder); - if (fpOutFile) - fclose(fpOutFile); - free(strPDir); - } + __u8 outstr[1024]; + time_t t; + struct tm *tmp; + size_t num; + char *strPDir; + char *strDest; + + t = time(NULL); + tmp = localtime(&t); + if (tmp == NULL) + return; + + num = strftime((char *)outstr, sizeof(outstr), + "Timestamp (UTC): %a, %d %b %Y %T %z", tmp); + num += sprintf((char *)(outstr + num), "\nPackage Version: 1.4"); + if (num) { + strPDir = strdup(strOSDirName); + strDest = dirname(strPDir); + WriteData(outstr, num, strDest, "timestamp_info.txt", "timestamp"); + free(strPDir); + } } -static void GetCtrlIDDInfo(const char *strCtrlDirName, struct nvme_id_ctrl *ctrlp) +static void GetCtrlIDDInfo(const char *dir, struct nvme_id_ctrl *ctrlp) { - char tempFolder[PATH_MAX] = { 0 }; - FILE *fpOutFile; - sprintf(tempFolder, "%s/%s", strCtrlDirName, - "nvme_controller_identify_data.bin"); - fpOutFile = fopen(tempFolder, "wb"); - if (fwrite(ctrlp, 1, sizeof(*ctrlp), fpOutFile) != sizeof(*ctrlp)) - printf("Unable to write controller data to %s file!", tempFolder); - if (fpOutFile) - fclose(fpOutFile); + WriteData((__u8*)ctrlp, sizeof(*ctrlp), dir, + "nvme_controller_identify_data.bin", "id-ctrl"); } -static void GetSmartlogData(int fd, const char *strCtrlDirName) +static void GetSmartlogData(int fd, const char *dir) { - char tempFolder[PATH_MAX] = { 0 }; - FILE *fpOutFile = NULL; - struct nvme_smart_log smart_log; - if (nvme_smart_log(fd, -1, &smart_log) == 0) { - sprintf(tempFolder, "%s/%s", strCtrlDirName, "smart_data.bin"); - fpOutFile = fopen(tempFolder, "wb"); - if (fwrite(&smart_log, 1, sizeof(smart_log), fpOutFile) != sizeof(smart_log)) - printf("Unable to write smart log data to %s file!", tempFolder); - if (fpOutFile) - fclose(fpOutFile); - } + struct nvme_smart_log smart_log; + if (nvme_smart_log(fd, -1, &smart_log) == 0) { + WriteData((__u8*)&smart_log, sizeof(smart_log), dir, + "smart_data.bin", "smart log"); + } } -static void GetErrorlogData(int fd, int entries, const char *strCtrlDirName) +static void GetErrorlogData(int fd, int entries, const char *dir) { - char tempFolder[PATH_MAX] = { 0 }; - FILE *fpOutFile = NULL; - int logSize = entries * sizeof(struct nvme_error_log_page); - struct nvme_error_log_page *error_log = (struct nvme_error_log_page *)calloc(1, logSize); - - if (error_log == NULL) - return; - - if (nvme_error_log(fd, entries, error_log) == 0) { - sprintf(tempFolder, "%s/%s", strCtrlDirName, - "error_information_log.bin"); - fpOutFile = fopen(tempFolder, "wb"); - if (fwrite(error_log, 1, logSize, fpOutFile) != logSize) - printf("Unable to write error log to %s file!", tempFolder); - if (fpOutFile) - fclose(fpOutFile); - } - free(error_log); + int logSize = entries * sizeof(struct nvme_error_log_page); + struct nvme_error_log_page *error_log = + (struct nvme_error_log_page *)calloc(1, logSize); + + if (error_log == NULL) + return; + + if (nvme_error_log(fd, entries, error_log) == 0) { + WriteData((__u8*)error_log, logSize, dir, + "error_information_log.bin", "error log"); + } + + free(error_log); } -static void GetNSIDDInfo(int fd, const char *strCtrlDirName, int nsid) +static void GetNSIDDInfo(int fd, const char *dir, int nsid) { - char tempFolder[256] = { 0 }; - char strFileName[PATH_MAX] = { 0 }; - FILE *fpOutFile = NULL; - struct nvme_id_ns ns; - if (nvme_identify_ns(fd, nsid, 0, &ns) == 0) { - sprintf(tempFolder, "identify_namespace_%d_data.bin.bin", nsid); - sprintf(strFileName, "%s/%s", strCtrlDirName, tempFolder); - fpOutFile = fopen(strFileName, "wb"); - if (fwrite(&ns, 1, sizeof(ns), fpOutFile) != sizeof(ns)) - printf("Unable to write controller data to %s file!", tempFolder); - if (fpOutFile) - fclose(fpOutFile); - } + char file[PATH_MAX] = { 0 }; + struct nvme_id_ns ns; + + if (nvme_identify_ns(fd, nsid, 0, &ns) == 0) { + sprintf(file, "identify_namespace_%d_data.bin", nsid); + WriteData((__u8*)&ns, sizeof(ns), dir, file, "id-ns"); + } } static void GetOSConfig(const char *strOSDirName) { - FILE *fpOSConfig = NULL; - char strBuffer[1024], strTemp[1024]; - char strFileName[PATH_MAX]; - int i; - - struct { - char *strcmdHeader; - char *strCommand; - } cmdArray[] = { - { (char *)"SYSTEM INFORMATION", (char *)"uname -a >> %s" }, - { (char *)"LINUX KERNEL MODULE INFORMATION", (char *)"lsmod >> %s" }, - { (char *)"LINUX SYSTEM MEMORY INFORMATION", (char *)"cat /proc/meminfo >> %s" }, - { (char *)"SYSTEM INTERRUPT INFORMATION", (char *)"cat /proc/interrupts >> %s" }, - { (char *)"CPU INFORMATION", (char *)"cat /proc/cpuinfo >> %s" }, - { (char *)"IO MEMORY MAP INFORMATION", (char *)"cat /proc/iomem >> %s" }, - { (char *)"MAJOR NUMBER AND DEVICE GROUP", (char *)"cat /proc/devices >> %s" }, - { (char *)"KERNEL DMESG", (char *)"dmesg >> %s" }, - { (char *)"/VAR/LOG/MESSAGES", (char *)"cat /var/log/messages >> %s" } - }; - - sprintf(strFileName, "%s/%s", strOSDirName, "os_config.txt"); - - for (i = 0; i < 7; i++) { - fpOSConfig = fopen(strFileName, "a+"); - fprintf(fpOSConfig, - "\n\n\n\n%s\n-----------------------------------------------\n", - cmdArray[i].strcmdHeader); - if (NULL != fpOSConfig) { - fclose(fpOSConfig); - fpOSConfig = NULL; - } - strcpy(strTemp, cmdArray[i].strCommand); - sprintf(strBuffer, strTemp, strFileName); - if (system(strBuffer)) - fprintf(stderr, "Failed to send \"%s\"\n", strBuffer); - } + FILE *fpOSConfig = NULL; + char strBuffer[1024], strTemp[1024]; + char strFileName[PATH_MAX]; + int i; + + struct { + char *strcmdHeader; + char *strCommand; + } cmdArray[] = { + { (char *)"SYSTEM INFORMATION", (char *)"uname -a >> %s" }, + { (char *)"LINUX KERNEL MODULE INFORMATION", (char *)"lsmod >> %s" }, + { (char *)"LINUX SYSTEM MEMORY INFORMATION", (char *)"cat /proc/meminfo >> %s" }, + { (char *)"SYSTEM INTERRUPT INFORMATION", (char *)"cat /proc/interrupts >> %s" }, + { (char *)"CPU INFORMATION", (char *)"cat /proc/cpuinfo >> %s" }, + { (char *)"IO MEMORY MAP INFORMATION", (char *)"cat /proc/iomem >> %s" }, + { (char *)"MAJOR NUMBER AND DEVICE GROUP", (char *)"cat /proc/devices >> %s" }, + { (char *)"KERNEL DMESG", (char *)"dmesg >> %s" }, + { (char *)"/VAR/LOG/MESSAGES", (char *)"cat /var/log/messages >> %s" } + }; + + sprintf(strFileName, "%s/%s", strOSDirName, "os_config.txt"); + + for (i = 0; i < 7; i++) { + fpOSConfig = fopen(strFileName, "a+"); + fprintf(fpOSConfig, + "\n\n\n\n%s\n-----------------------------------------------\n", + cmdArray[i].strcmdHeader); + if (NULL != fpOSConfig) { + fclose(fpOSConfig); + fpOSConfig = NULL; + } + strcpy(strTemp, cmdArray[i].strCommand); + sprintf(strBuffer, strTemp, strFileName); + if (system(strBuffer)) + fprintf(stderr, "Failed to send \"%s\"\n", strBuffer); + } } -static int micron_internal_logs(int argc, char **argv, struct command *cmd, - struct plugin *plugin) +static int micron_telemetry_log(int fd, __u8 gen, __u8 type, __u8 **data, + int *logSize, int da) +{ + int err; + unsigned short data_area[4]; + unsigned char ctrl_init = (type == 0x8); + + __u8 *buffer = (unsigned char *)calloc(512, 1); + if (buffer == NULL) + return -1; + err = nvme_get_telemetry_log(fd, buffer, gen, ctrl_init, 512, 0); + if (err != 0) { + fprintf(stderr, "Failed to get telemetry log header for 0x%X\n", type); + if (buffer != NULL) { + free(buffer); + } + return err; + } + + // compute size of the log + data_area[1] = buffer[9] << 16 | buffer[8]; + data_area[2] = buffer[11] << 16 | buffer[10]; + data_area[3] = buffer[13] << 16 | buffer[12]; + data_area[0] = data_area[1] > data_area[2] ? data_area[1] : data_area[2]; + data_area[0] = data_area[3] > data_area[0] ? data_area[3] : data_area[0]; + + if (data_area[da] == 0) { + fprintf(stderr, "Requested telemetry data for 0x%X is empty\n", type); + if (buffer != NULL) { + free(buffer); + buffer = NULL; + } + return -1; + } + + *logSize = data_area[da] * 512; + if ((buffer = (unsigned char *)realloc(buffer, (size_t)(*logSize))) != NULL) { + err = nvme_get_telemetry_log(fd, buffer, gen, ctrl_init, *logSize, 0); + } + + if (err == 0 && buffer != NULL) { + *data = buffer; + } else { + fprintf(stderr, "Failed to get telemetry data for 0x%x\n", type); + if (buffer != NULL) + free(buffer); + } + + return err; +} + +static int GetTelemetryData(int fd, const char *dir) +{ + unsigned char *buffer = NULL; + int i, err, logSize = 0; + char msg[256] = {0}; + struct { + __u8 log; + char *file; + } tmap[] = { + {0x07, "nvme_host_telemetry.bin"}, + {0x08, "nvme_cntrl_telemetry.bin"}, + }; + + for(i = 0; i < (int)(sizeof(tmap)/sizeof(tmap[0])); i++) { + err = micron_telemetry_log(fd, 0, tmap[i].log, &buffer, &logSize, 0); + if (err == 0 && logSize > 0 && buffer != NULL) { + sprintf(msg, "telemetry log: 0x%X", tmap[i].log); + WriteData(buffer, logSize, dir, tmap[i].file, msg); + if (buffer != NULL) + free(buffer); + } + buffer = NULL; + logSize = 0; + } + return err; +} + +static int GetFeatureSettings(int fd, const char *dir) +{ + unsigned char *bufp, buf[4096] = { 0 }; + int i, err, len, errcnt = 0; + __u32 attrVal = 0; + char msg[256] = { 0 }; + + struct features { + int id; + char *file; + } fmap[] = { + {0x01, "nvme_feature_setting_arbitration.bin"}, + {0x02, "nvme_feature_setting_pm.bin"}, + {0x03, "nvme_feature_setting_lba_range_namespace_1.bin"}, + {0x04, "nvme_feature_setting_temp_threshold.bin"}, + {0x05, "nvme_feature_setting_error_recovery.bin"}, + {0x06, "nvme_feature_setting_volatile_write_cache.bin"}, + {0x07, "nvme_feature_setting_num_queues.bin"}, + {0x08, "nvme_feature_setting_interrupt_coalescing.bin"}, + {0x09, "nvme_feature_setting_interrupt_vec_config.bin"}, + {0x0A, "nvme_feature_setting_write_atomicity.bin"}, + {0x0B, "nvme_feature_setting_async_event_config.bin"}, + {0x80, "nvme_feature_setting_sw_progress_marker.bin"}, + }; + + for (i = 0; i < (int)(sizeof(fmap)/sizeof(fmap[0])); i++) { + if (fmap[i].id == 0x03) { + len = 4096; + bufp = (unsigned char *)(&buf[0]); + } else { + len = 0; + bufp = NULL; + } + + err = nvme_get_feature(fd, 1, fmap[i].id, 0, 0x0, len, bufp, &attrVal); + if (err == 0) { + sprintf(msg, "feature: 0x%X", fmap[i].id); + WriteData((__u8*)&attrVal, sizeof(attrVal), dir, fmap[i].file, msg); + if (bufp != NULL) { + WriteData(bufp, len, dir, fmap[i].file, msg); + } + } else { + printf("Failed to retrieve feature 0x%x data !\n", fmap[i].id); + errcnt++; + } + } + return (int)(errcnt == sizeof(fmap)/sizeof(fmap[0])); +} + +static int micron_drive_info(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + const char *desc = "Get drive HW information"; + int fd, err = 0; + struct nvme_id_ctrl ctrl = { 0 }; + OPT_ARGS(opts) = { + OPT_END() + }; + + if ((fd = micron_parse_options(argc, argv, desc, opts, NULL)) < 0) + return err; + + err = nvme_identify_ctrl(fd, &ctrl); + if (err) { + fprintf(stderr, "ERROR : nvme_identify_ctrl() failed with 0x%x\n", err); + return -1; + } + + printf("%u.%u\n", ctrl.vs[820], ctrl.vs[821]); + return 0; +} + +static int micron_cloud_ssd_plugin_version(int argc, char **argv, + struct command *cmd, struct plugin *plugin) +{ + printf("nvme-cli Micron cloud SSD plugin version: %s.%s\n", + __version_major, __version_minor); + return 0; +} + +static int micron_plugin_version(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + printf("nvme-cli Micron plugin version: %s.%s.%s\n", + __version_major, __version_minor, __version_patch); + return 0; +} + +static int micron_logpage_dir(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + printf("This command is not implemented for the drive\n"); + return 0; +} +/* Binary format of firmware activation history entry */ +struct __attribute__((__packed__)) fw_activation_history_entry { + __u8 version; + __u8 length; + __u16 rsvd1; + __le16 valid; + __le64 power_on_hour; + __le64 rsvd2; + __le64 power_cycle_count; + __u8 previous_fw[8]; + __u8 activated_fw[8]; + __u8 slot; + __u8 commit_action_type; + __le16 result; + __u8 rsvd3[14]; +}; + + +/* Binary format for firmware activation history table */ +struct __attribute__((__packed__)) micron_fw_activation_history_table { + __u8 log_page; + __u8 rsvd1[3]; + __le32 num_entries; + struct fw_activation_history_entry entries[20]; + __u8 rsvd2[2790]; + __u16 version; + __u8 GUID[16]; +}; + +/* header to be printed field widths = 10 | 12 | 10 | 11 | 12 | 9 | 9 | 9 */ + +const char *fw_activation_history_table_header = "\ +__________________________________________________________________________________\n\ + | | | | | | | \n\ +Firmware | Power | Power | Previous | New FW | Slot | Commit | Result \n\ +Activation| On Hour | cycle | firmware | activated | number | Action | \n\ +Counter | | count | | | | Type | \n\ +__________|___________|_________|__________|___________|________|________|________\n"; + +static int display_fw_activate_entry ( + int entry_count, + struct fw_activation_history_entry *entry, + char *formatted_entry, + struct json_object *stats +) +{ + time_t timestamp, hours; + char buffer[32]; + __u8 minutes, seconds; + char *ca[] = {"000b", "001b", "010b", "011b"}; + char *ptr = formatted_entry; + int index = 0, entry_size = 82; + + if (entry->version != 1 || entry->length != 64) { + fprintf(stderr, "unsupported entry ! version: %x with length: %d\n", + entry->version, entry->length); + return -EINVAL; + } + + sprintf(ptr, "%d", entry_count); + ptr += 10; + + timestamp = (le64_to_cpu(entry->power_on_hour) & 0x0000FFFFFFFFFFFFUL) / 1000; + hours = timestamp / 3600; + minutes = (timestamp % 3600) / 60; + seconds = (timestamp % 3600) % 60; + sprintf(ptr, "|%"PRIu64":%hhu:%hhu", (uint64_t)hours, minutes, seconds); + ptr += 12; + + sprintf(ptr, "| %"PRIu64, le64_to_cpu(entry->power_cycle_count)); + ptr += 10; + + /* firmware details */ + memset(buffer, 0, sizeof(buffer)); + memcpy(buffer, entry->previous_fw, sizeof(entry->previous_fw)); + sprintf(ptr, "| %s", buffer); + ptr += 11; + + memset(buffer, 0, sizeof(buffer)); + memcpy(buffer, entry->activated_fw, sizeof(entry->activated_fw)); + sprintf(ptr, "| %s", buffer); + ptr += 12; + + /* firmware slot and commit action*/ + sprintf(ptr, "| %d", entry->slot); + ptr += 9; + + if (entry->commit_action_type <= 3) + sprintf(ptr, "| %s", ca[entry->commit_action_type]); + else + sprintf(ptr, "| xxxb"); + ptr += 9; + + /* result */ + if (entry->result) { + sprintf(ptr, "| Fail #%d", entry->result); + } else { + sprintf(ptr, "| pass"); + } + + /* replace all null charecters with spaces */ + ptr = formatted_entry; + while (index < entry_size) { + if (ptr[index] == '\0') + ptr[index] = ' '; + index++; + } + return 0; +} + + +static int micron_fw_activation_history(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + const char *desc = "Retrieve Firmware Activation history of the given drive"; + char formatted_output[100]; + int count = 0; + unsigned int logC2[C2_log_size/sizeof(int)] = { 0 }; + eDriveModel eModel = UNKNOWN_MODEL; + struct nvme_id_ctrl ctrl; + int fd, err, ctrlIdx; + struct format { + char *fmt; + }; + + const char *fmt = "output format normal"; + struct format cfg = { + .fmt = "normal", + }; + + OPT_ARGS(opts) = { + OPT_FMT("format", 'f', &cfg.fmt, fmt), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) { + printf("\nDevice not found \n");; + return -1; + } + + if (strcmp(cfg.fmt, "normal") != 0) { + fprintf (stderr, "only normal format is supported currently\n"); + close(fd); + return -1; + } + + err = nvme_identify_ctrl(fd, &ctrl); + if (err) { + fprintf(stderr, "failed get device identification data, error: %x\n", err); + goto out; + } + + /* check if product supports fw_history log */ + err = -EINVAL; + sscanf(argv[optind], "/dev/nvme%d", &ctrlIdx); + eModel = GetDriveModel(ctrlIdx); + if (eModel != M51CX) { + fprintf(stderr, "Unsupported drive model for vs-fw-activate-history command\n"); + goto out; + } + + err = nvme_get_log(fd, NVME_NSID_ALL, 0xC2, false, NVME_NO_LOG_LSP, C2_log_size, logC2); + if (err) { + fprintf(stderr, "Failed to retrieve fw activation history log, error: %x\n", err); + goto out; + } + + /* check if we have atleast one entry to print */ + struct micron_fw_activation_history_table *table = + (struct micron_fw_activation_history_table *)logC2; + + /* check version and log page */ + if (table->version != 2 || table->log_page != 0xC2) { + fprintf(stderr, "Unsupported fw activation history page: %x, version: %x\n", + table->log_page, table->version); + goto out; + } + + if (table->num_entries == 0) { + fprintf(stderr, "No entries were found in fw activation history log\n"); + goto out; + } + + printf("%s", fw_activation_history_table_header); + for(count = 0; count < table->num_entries; count++) { + memset(formatted_output, '\0', 100); + if (display_fw_activate_entry(count, + &table->entries[count], + formatted_output, NULL) == 0) + { + printf("%s\n", formatted_output); + } + } +out: + if (fd > 0) + close(fd); + return err; +} + +static int micron_error_reason(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + printf("This command is not implemented for the drive\n"); + return 0; +} + +static int micron_ocp_smart_health_logs(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + const char *desc = "Retrieve Micron OCP Smart Health log for the given device "; + unsigned int logC0[C0_log_size/sizeof(int)] = { 0 }; + eDriveModel eModel = UNKNOWN_MODEL; + struct nvme_id_ctrl ctrl; + int fd, err, ctrlIdx; + bool is_json = false; + struct format { + char *fmt; + }; + const char *fmt = "output format normal|json"; + struct format cfg = { + .fmt = "normal", + }; + + OPT_ARGS(opts) = { + OPT_FMT("format", 'f', &cfg.fmt, fmt), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) { + printf("\nDevice not found \n");; + return -1; + } + + if (strcmp(cfg.fmt, "json") == 0) + is_json = true; + + err = nvme_identify_ctrl(fd, &ctrl); + if (err) + goto out; + + /* pull log details based on the model name */ + sscanf(argv[optind], "/dev/nvme%d", &ctrlIdx); + if ((eModel = GetDriveModel(ctrlIdx)) == UNKNOWN_MODEL) { + printf ("Unsupported drive model for vs-smart-add-log commmand\n"); + close(fd); + goto out; + } + + /* should check for firmware version if this log is supported or not */ + if (eModel == M5407 || eModel == M5410) { + err = nvme_get_log(fd, NVME_NSID_ALL, 0xC0, false, NVME_NO_LOG_LSP, + C0_log_size, logC0); + } + if (err < 0) { + printf("Unable to retrieve extended smart log for the drive\n"); + err = -ENOTTY; + } else { + print_smart_cloud_health_log((__u8 *)logC0, is_json); + } +out: + close(fd); + return err; +} + +static int micron_clr_fw_activation_history(int argc, char **argv, + struct command *cmd, struct plugin *plugin) { + const char *desc = "Clear FW activation history"; + int fd, err = 0; + __u32 result = 0; + __u8 fid = MICRON_FEATURE_CLEAR_FW_ACTIVATION_HISTORY; + eDriveModel model = UNKNOWN_MODEL; + OPT_ARGS(opts) = { + OPT_END() + }; + + if ((fd = micron_parse_options(argc, argv, desc, opts, &model)) < 0) + return err; + + if (model != M51CX) { + printf ("This option is not supported for specified drive\n"); + close(fd); + return err; + } + + //err = nvme_set_feature(fd, 1, fid, cdw11, 0, opt.save, 0, 0, &result); + err = nvme_set_feature(fd, 1, fid, 0, 0, 0, 0, 0, &result); + if (err == 0) err = (int)result; + return err; +} - int err = 0; - int fd; - int ctrlIdx; - FILE *fpOutFile = NULL; - char strOSDirName[1024]; - char strCtrlDirName[1024]; - char strMainDirName[256]; - char tempFolder[PATH_MAX] = { 0 }; - unsigned int *puiIDDBuf; - unsigned int uiMask; - struct nvme_id_ctrl ctrl; - char sn[20] = { 0 }; - - struct { - unsigned char ucLogPage; - const char *strFileName; - int nLogSize; - int nMaxSize; - } aVendorLogs[32] = { - { 0xC1, "nvmelog_C1.bin", 0, 0 }, - { 0xC2, "nvmelog_C2.bin", 0, 0 }, - { 0xC4, "nvmelog_C4.bin", 0, 0 }, - { 0xC5, "nvmelog_C5.bin", C5_log_size, 0 }, - { 0xD0, "nvmelog_D0.bin", D0_log_size, 0 }, - { 0xE6, "nvmelog_E6.bin", 0, 0 }, - { 0xE7, "nvmelog_E7.bin", 0, 0 } - }, - aM51XXLogs[] = { - { 0xFB, "nvmelog_FB.bin", 4096, 0 }, /* this should be collected first for M51AX */ - { 0xF7, "nvmelog_F7.bin", 4096, 512 * 1024 }, - { 0xF8, "nvmelog_F8.bin", 4096, 512 * 1024 }, - { 0xF9, "nvmelog_F9.bin", 4096, 200 * 1024 * 1024 }, - { 0xFC, "nvmelog_FC.bin", 4096, 200 * 1024 * 1024 }, - { 0xFD, "nvmelog_FD.bin", 4096, 80 * 1024 * 1024 } - }, - aM51AXLogs[] = { - { 0xD0, "nvmelog_D0.bin", 512, 0 }, - { 0xCA, "nvmelog_CA.bin", 512, 0 }, - { 0xFA, "nvmelog_FA.bin", 4096, 15232 }, - { 0xFE, "nvmelog_FE.bin", 4096, 512 * 1024 }, - { 0xFF, "nvmelog_FF.bin", 4096, 162 * 1024 }, - { 0x04, "changed_namespace_log.bin", 4096, 0 }, - { 0x05, "command_effects_log.bin", 4096, 0 }, - { 0x06, "drive_self_test.bin", 4096, 0 } - }, - aM51BXLogs[] = { - { 0xFA, "nvmelog_FA.bin", 4096, 16376 }, - { 0xFE, "nvmelog_FE.bin", 4096, 256 * 1024 }, - { 0xFF, "nvmelog_FF.bin", 4096, 64 * 1024 }, - { 0xCA, "nvmelog_CA.bin", 512, 1024 } - }; - - eDriveModel eModel; - - const char *desc = "This retrieves the micron debug log package"; - const char *package = "Log output package name (required)"; - unsigned char *dataBuffer = NULL; - int bSize = 0; - int maxSize = 0; - - struct config { - char *package; - }; - - struct config cfg = { - .package = "" - }; - - OPT_ARGS(opts) = { - OPT_STRING("package", 'p', "FILE", &cfg.package, package), - OPT_END() - }; - - fd = parse_and_open(argc, argv, desc, opts); - - if (strlen(cfg.package) == 0) { - printf ("You must specify an output name for the log package. ie --p=logfiles.zip\n"); - goto out; - } - - if (fd < 0) - goto out; - - /* pull log details based on the model name */ - sscanf(argv[optind], "/dev/nvme%d", &ctrlIdx); - if ((eModel = GetDriveModel(ctrlIdx)) == UNKNOWN_MODEL) { - printf ("Unsupported drive model for vs-internal-log collection\n"); - close(fd); - goto out; - } - - printf("Preparing log package. This will take a few seconds...\n"); - err = nvme_identify_ctrl(fd, &ctrl); - if (err) - goto out; - - // trim spaces out of serial number string */ - int i, j = 0; - for (i = 0; i < sizeof(ctrl.sn); i++) { - if (isblank(ctrl.sn[i])) - continue; - sn[j++] = ctrl.sn[i]; - } - sn[j] = '\0'; - strcpy(ctrl.sn, sn); - - SetupDebugDataDirectories(ctrl.sn, cfg.package, strMainDirName, strOSDirName, strCtrlDirName); - - GetTimestampInfo(strOSDirName); - GetCtrlIDDInfo(strCtrlDirName, &ctrl); - GetOSConfig(strOSDirName); - GetDriveInfo(strOSDirName, ctrlIdx, &ctrl); - - for (int i = 1; i <= ctrl.nn; i++) - GetNSIDDInfo(fd, strCtrlDirName, i); - - GetSmartlogData(fd, strCtrlDirName); - GetErrorlogData(fd, ctrl.elpe, strCtrlDirName); - - if (eModel != M5410) { - memcpy(aVendorLogs, aM51XXLogs, sizeof(aM51XXLogs)); - if (eModel == M51AX) - memcpy((char *)aVendorLogs + sizeof(aM51XXLogs), aM51AXLogs, sizeof(aM51AXLogs)); - else - memcpy((char *)aVendorLogs + sizeof(aM51XXLogs), aM51BXLogs, sizeof(aM51BXLogs)); - } - - for (int i = 0; i < (int)(sizeof(aVendorLogs) / sizeof(aVendorLogs[0])) && aVendorLogs[i].ucLogPage != 0; i++) { - err = -1; - switch (aVendorLogs[i].ucLogPage) { - case 0xC1: - case 0xC2: - case 0xC4: - err = GetLogPageSize(fd, aVendorLogs[i].ucLogPage, &bSize); - if (err == 0 && bSize > 0) - err = GetCommonLogPage(fd, aVendorLogs[i].ucLogPage, &dataBuffer, bSize); - break; - - case 0xE6: - case 0xE7: - puiIDDBuf = (unsigned int *)&ctrl; - uiMask = puiIDDBuf[1015]; - if (uiMask == 0 || (aVendorLogs[i].ucLogPage == 0xE6 && uiMask == 2) || (aVendorLogs[i].ucLogPage == 0xE7 - && uiMask == 1)) { - bSize = 0; - } else { - bSize = (int)puiIDDBuf[1015]; - if (bSize % (16 * 1024)) { - bSize += (16 * 1024) - (bSize % (16 * 1024)); - } - } - if (bSize != 0) { - dataBuffer = (unsigned char *)malloc(bSize); - memset(dataBuffer, 0, bSize); - err = NVMEGetLogPage(fd, aVendorLogs[i].ucLogPage, dataBuffer, bSize); - } - break; - - case 0xF7: - case 0xF9: - case 0xFC: - case 0xFD: - if (eModel == M51BX) - (void)NVMEResetLog(fd, aVendorLogs[i].ucLogPage, aVendorLogs[i].nLogSize, aVendorLogs[i].nMaxSize); - default: - bSize = aVendorLogs[i].nLogSize; - dataBuffer = (unsigned char *)malloc(bSize); - memset(dataBuffer, 0, bSize); - err = NVMEGetLogPage(fd, aVendorLogs[i].ucLogPage, dataBuffer, bSize); - maxSize = aVendorLogs[i].nMaxSize - bSize; - while (err == 0 && maxSize > 0 && ((unsigned int *)dataBuffer)[0] != 0xdeadbeef) { - sprintf(tempFolder, "%s/%s", strCtrlDirName, - aVendorLogs[i].strFileName); - fpOutFile = fopen(tempFolder, "ab+"); - if (fwrite(dataBuffer, 1, bSize, fpOutFile) != bSize) { - printf ("Unable to write log to file %s\n!", aVendorLogs[i].strFileName); - } - if (fpOutFile) - fclose(fpOutFile); - err = NVMEGetLogPage(fd, aVendorLogs[i].ucLogPage, dataBuffer, bSize); - if (err || (((unsigned int *)dataBuffer)[0] == 0xdeadbeef)) - break; - maxSize -= bSize; - } - break; - } - - if (err == 0 && dataBuffer != NULL && ((unsigned int *)dataBuffer)[0] != 0xdeadbeef) { - sprintf(tempFolder, "%s/%s", strCtrlDirName, - aVendorLogs[i].strFileName); - fpOutFile = fopen(tempFolder, "ab+"); - if (fwrite(dataBuffer, 1, bSize, fpOutFile) != bSize) { - printf("Unable to write log to file %s\n!", aVendorLogs[i].strFileName); - } - if (fpOutFile) - fclose(fpOutFile); - } - - if (dataBuffer != NULL) { - free(dataBuffer); - dataBuffer = NULL; - } - } - - ZipAndRemoveDir(strMainDirName, cfg.package); - out: - return err; +static int micron_telemetry_cntrl_option(int argc, char **argv, + struct command *cmd, struct plugin *plugin) +{ + int err = 0; + __u32 result = 0; + const char *desc = "Enable or Disable Controller telemetry log generation"; + const char *option = "enable or disable or status"; + const char *select = "select/save values: enable/disable options" + "1 - save (persistent), 0 - non-persistent and for " + "status options: 0 - current, 1 - default, 2-saved"; + int fd = 0; + int fid = MICRON_FEATURE_TELEMETRY_CONTROL_OPTION; + eDriveModel model = UNKNOWN_MODEL; + struct nvme_id_ctrl ctrl = { 0 }; + + struct { + char *option; + int select; + } opt = { + .option = "disable", + .select= 0, + }; + + OPT_ARGS(opts) = { + OPT_STRING("option", 'o', "option", &opt.option, option), + OPT_UINT("select", 's', &opt.select, select), + OPT_END() + }; + + if ((fd = micron_parse_options(argc, argv, desc, opts, &model)) < 0) { + return -1; + } + + err = nvme_identify_ctrl(fd, &ctrl); + if ((ctrl.lpa & 0x8) != 0x8) { + printf("drive doesn't support host/controller generated telemetry logs\n"); + close(fd); + return err; + } + + if (!strcmp(opt.option, "enable")) { + err = nvme_set_feature(fd, 1, fid, 1, 0, (opt.select & 0x1), 0, 0, &result); + if (err == 0) { + printf("successfully set controller telemetry option\n"); + } else { + printf("Failed to set controller telemetry option\n"); + } + } else if (!strcmp(opt.option, "disable")) { + err = nvme_set_feature(fd, 1, fid, 0, 0, (opt.select & 0x1), 0, 0, &result); + if (err == 0) { + printf("successfully disabled controller telemetry option\n"); + } else { + printf("Failed to disable controller telemetry option\n"); + } + } else if (!strcmp(opt.option, "status")) { + opt.select &= 0x3; + err = nvme_get_feature(fd, 1, fid, opt.select, 0, 0, 0, &result); + if (err == 0) { + printf("Controller telemetry option : %s\n", + (result) ? "enabled" : "disabled"); + } else { + printf("Failed to retrieve controller telemetry option\n"); + } + } else { + printf("invalid option %s, valid values are enable,disable or status\n", opt.option); + close(fd); + return -1; + } + + close(fd); + return err; +} + +static int micron_internal_logs(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + int err = -EINVAL; + int fd = 0; + int ctrlIdx, telemetry_option = 0; + char strOSDirName[1024]; + char strCtrlDirName[1024]; + char strMainDirName[256]; + unsigned int *puiIDDBuf; + unsigned int uiMask; + struct nvme_id_ctrl ctrl; + char sn[20] = { 0 }; + char msg[256] = { 0 }; + + struct { + unsigned char ucLogPage; + const char *strFileName; + int nLogSize; + int nMaxSize; + } aVendorLogs[32] = { + { 0x03, "firmware_slot_info_log.bin", 512, 0 }, + { 0xC1, "nvmelog_C1.bin", 0, 0 }, + { 0xC2, "nvmelog_C2.bin", 0, 0 }, + { 0xC4, "nvmelog_C4.bin", 0, 0 }, + { 0xC5, "nvmelog_C5.bin", C5_log_size, 0 }, + { 0xD0, "nvmelog_D0.bin", D0_log_size, 0 }, + { 0xE6, "nvmelog_E6.bin", 0, 0 }, + { 0xE7, "nvmelog_E7.bin", 0, 0 } + }, + aM51XXLogs[] = { + { 0xFB, "nvmelog_FB.bin", 4096, 0 }, /* this should be collected first for M51AX */ + { 0xD0, "nvmelog_D0.bin", 512, 0 }, + { 0x03, "firmware_slot_info_log.bin", 512, 0}, + { 0xF7, "nvmelog_F7.bin", 4096, 512 * 1024 }, + { 0xF8, "nvmelog_F8.bin", 4096, 512 * 1024 }, + { 0xF9, "nvmelog_F9.bin", 4096, 200 * 1024 * 1024 }, + { 0xFC, "nvmelog_FC.bin", 4096, 200 * 1024 * 1024 }, + { 0xFD, "nvmelog_FD.bin", 4096, 80 * 1024 * 1024 } + }, + aM51AXLogs[] = { + { 0xCA, "nvmelog_CA.bin", 512, 0 }, + { 0xFA, "nvmelog_FA.bin", 4096, 15232 }, + { 0xF6, "nvmelog_F6.bin", 4096, 512 * 1024 }, + { 0xFE, "nvmelog_FE.bin", 4096, 512 * 1024 }, + { 0xFF, "nvmelog_FF.bin", 4096, 162 * 1024 }, + { 0x04, "changed_namespace_log.bin", 4096, 0 }, + { 0x05, "command_effects_log.bin", 4096, 0 }, + { 0x06, "drive_self_test.bin", 4096, 0 } + }, + aM51BXLogs[] = { + { 0xFA, "nvmelog_FA.bin", 4096, 16376 }, + { 0xFE, "nvmelog_FE.bin", 4096, 256 * 1024 }, + { 0xFF, "nvmelog_FF.bin", 4096, 64 * 1024 }, + { 0xCA, "nvmelog_CA.bin", 512, 1024 } + }; + + eDriveModel eModel; + + const char *desc = "This retrieves the micron debug log package"; + const char *package = "Log output data file name (required)"; + const char *type = "telemetry log type - host or controller"; + const char *data_area = "telemetry log data area 1, 2 or 3"; + unsigned char *dataBuffer = NULL; + int bSize = 0; + int maxSize = 0; + + struct config { + char *type; + char *package; + int data_area; + int log; + }; + + struct config cfg = { + .type = "", + .package = "", + .data_area = -1, + .log = 0x07, + }; + + OPT_ARGS(opts) = { + OPT_STRING("type", 't', "log type", &cfg.type, type), + OPT_STRING("package", 'p', "FILE", &cfg.package, package), + OPT_UINT("data_area", 'd', &cfg.data_area, data_area), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + + if (fd < 0) + goto out; + + /* if telemetry type is specified, check for data area */ + if (strlen(cfg.type) != 0) { + if (!strcmp(cfg.type, "controller")) { + cfg.log = 0x08; + } else if (strcmp(cfg.type, "host")) { + printf ("telemetry type (host or controller) should be specified i.e. -t=host\n"); + close(fd); + goto out; + } + + if (cfg.data_area <= 0 || cfg.data_area > 3) { + printf ("data area must be selected using -d option ie --d=1,2,3\n"); + close(fd); + goto out; + } + telemetry_option = 1; + } else if (cfg.data_area > 0) { + printf ("data area option is valid only for telemetry option (i.e --type=host|controller)\n"); + close(fd); + goto out; + } + + if (strlen(cfg.package) == 0) { + if (telemetry_option) + printf ("Log data file must be specified. ie -p=logfile.bin\n"); + else + printf ("Log data file must be specified. ie -p=logfile.zip or -p=logfile.tgz|logfile.tar.gz\n"); + goto out; + } + + /* pull log details based on the model name */ + sscanf(argv[optind], "/dev/nvme%d", &ctrlIdx); + if ((eModel = GetDriveModel(ctrlIdx)) == UNKNOWN_MODEL) { + printf ("Unsupported drive model for vs-internal-log collection\n"); + close(fd); + goto out; + } + + err = nvme_identify_ctrl(fd, &ctrl); + if (err) + goto out; + + err = -EINVAL; + if (telemetry_option) { + if ((ctrl.lpa & 0x8) != 0x8) { + printf("telemetry option is not supported for specified drive\n"); + close(fd); + goto out; + } + int logSize = 0; __u8 *buffer = NULL; const char *dir = "."; + err = micron_telemetry_log(fd, 0, cfg.log, &buffer, &logSize, cfg.data_area); + if (err == 0 && logSize > 0 && buffer != NULL) { + sprintf(msg, "telemetry log: 0x%X", cfg.log); + WriteData(buffer, logSize, dir, cfg.package, msg); + free(buffer); + } + close(fd); + goto out; + } + + printf("Preparing log package. This will take a few seconds...\n"); + + // trim spaces out of serial number string */ + int i, j = 0; + for (i = 0; i < sizeof(ctrl.sn); i++) { + if (isblank(ctrl.sn[i])) + continue; + sn[j++] = ctrl.sn[i]; + } + sn[j] = '\0'; + strcpy(ctrl.sn, sn); + + SetupDebugDataDirectories(ctrl.sn, cfg.package, strMainDirName, strOSDirName, strCtrlDirName); + + GetTimestampInfo(strOSDirName); + GetCtrlIDDInfo(strCtrlDirName, &ctrl); + GetOSConfig(strOSDirName); + GetDriveInfo(strOSDirName, ctrlIdx, &ctrl); + + for (int i = 1; i <= ctrl.nn; i++) + GetNSIDDInfo(fd, strCtrlDirName, i); + + GetSmartlogData(fd, strCtrlDirName); + GetErrorlogData(fd, ctrl.elpe, strCtrlDirName); + + // pull if telemetry log data is supported + if ((ctrl.lpa & 0x8) == 0x8) + GetTelemetryData(fd, strCtrlDirName); + + GetFeatureSettings(fd, strCtrlDirName); + + if (eModel != M5410) { + memcpy(aVendorLogs, aM51XXLogs, sizeof(aM51XXLogs)); + if (eModel == M51AX) + memcpy((char *)aVendorLogs + sizeof(aM51XXLogs), aM51AXLogs, sizeof(aM51AXLogs)); + else + memcpy((char *)aVendorLogs + sizeof(aM51XXLogs), aM51BXLogs, sizeof(aM51BXLogs)); + } + + for (int i = 0; i < (int)(sizeof(aVendorLogs) / sizeof(aVendorLogs[0])) && + aVendorLogs[i].ucLogPage != 0; i++) { + err = -1; + switch (aVendorLogs[i].ucLogPage) { + case 0xC1: + case 0xC2: + case 0xC4: + err = GetLogPageSize(fd, aVendorLogs[i].ucLogPage, &bSize); + if (err == 0 && bSize > 0) + err = GetCommonLogPage(fd, aVendorLogs[i].ucLogPage, &dataBuffer, bSize); + break; + + case 0xE6: + case 0xE7: + puiIDDBuf = (unsigned int *)&ctrl; + uiMask = puiIDDBuf[1015]; + if (uiMask == 0 || (aVendorLogs[i].ucLogPage == 0xE6 && uiMask == 2) || + (aVendorLogs[i].ucLogPage == 0xE7 && uiMask == 1)) { + bSize = 0; + } else { + bSize = (int)puiIDDBuf[1023]; + if (bSize % (16 * 1024)) { + bSize += (16 * 1024) - (bSize % (16 * 1024)); + } + } + if (bSize != 0 && (dataBuffer = (unsigned char *)malloc(bSize)) != NULL) { + memset(dataBuffer, 0, bSize); + if (eModel == M5410 || eModel == M5407) + err = NVMEGetLogPage(fd, aVendorLogs[i].ucLogPage, dataBuffer, bSize); + else + err = nvme_get_log(fd, NVME_NSID_ALL, aVendorLogs[i].ucLogPage, + false, NVME_NO_LOG_LSP, bSize, dataBuffer); + } + break; + + case 0xF7: + case 0xF9: + case 0xFC: + case 0xFD: + if (eModel == M51BX) + (void)NVMEResetLog(fd, aVendorLogs[i].ucLogPage, + aVendorLogs[i].nLogSize, aVendorLogs[i].nMaxSize); + default: + bSize = aVendorLogs[i].nLogSize; + dataBuffer = (unsigned char *)malloc(bSize); + if (dataBuffer == NULL) { + break; + } + memset(dataBuffer, 0, bSize); + err = nvme_get_log(fd, NVME_NSID_ALL, aVendorLogs[i].ucLogPage, + false, NVME_NO_LOG_LSP, bSize, dataBuffer); + maxSize = aVendorLogs[i].nMaxSize - bSize; + while (err == 0 && maxSize > 0 && ((unsigned int *)dataBuffer)[0] != 0xdeadbeef) { + sprintf(msg, "log 0x%x", aVendorLogs[i].ucLogPage); + WriteData(dataBuffer, bSize, strCtrlDirName, aVendorLogs[i].strFileName, msg); + err = nvme_get_log(fd, NVME_NSID_ALL, aVendorLogs[i].ucLogPage, + false, NVME_NO_LOG_LSP, bSize, dataBuffer); + if (err || (((unsigned int *)dataBuffer)[0] == 0xdeadbeef)) + break; + maxSize -= bSize; + } + break; + } + + if (err == 0 && dataBuffer != NULL && ((unsigned int *)dataBuffer)[0] != 0xdeadbeef) { + sprintf(msg, "log 0x%x", aVendorLogs[i].ucLogPage); + WriteData(dataBuffer, bSize, strCtrlDirName, aVendorLogs[i].strFileName, msg); + } + + if (dataBuffer != NULL) { + free(dataBuffer); + dataBuffer = NULL; + } + } + + err = ZipAndRemoveDir(strMainDirName, cfg.package); +out: + return err; } diff --git a/plugins/micron/micron-nvme.h b/plugins/micron/micron-nvme.h index add36e4..118f8cd 100644 --- a/plugins/micron/micron-nvme.h +++ b/plugins/micron/micron-nvme.h @@ -12,7 +12,17 @@ PLUGIN(NAME("micron", "Micron vendor specific extensions"), ENTRY("vs-pcie-stats", "Retrieve Micron PCIe error stats", micron_pcie_stats) ENTRY("clear-pcie-correctable-errors", "Clear correctable PCIe errors", micron_clear_pcie_correctable_errors) ENTRY("vs-internal-log", "Retrieve Micron logs", micron_internal_logs) + ENTRY("vs-telemetry-controller-option", "Enable/Disable controller telemetry log generation", micron_telemetry_cntrl_option) ENTRY("vs-nand-stats", "Retrieve NAND Stats", micron_nand_stats) + ENTRY("vs-drive-info", "Retrieve Drive information", micron_drive_info) + ENTRY("plugin-version", "Display plugin version info", micron_plugin_version) + ENTRY("cloud-SSD-plugin-version", "Display plugin version info", micron_cloud_ssd_plugin_version) + ENTRY("log-page-directory", "Retrieve log page directory", micron_logpage_dir) + ENTRY("vs-fw-activate-history", "Display FW activation history", micron_fw_activation_history) + ENTRY("vs-error-reason-identifier", "Retrieve Error reason", micron_error_reason) + ENTRY("vs-smart-add-log", "Retrieve extended SMART data", micron_ocp_smart_health_logs) + ENTRY("clear-fw-activate-history", "Clear FW activation history", micron_clr_fw_activation_history) + ENTRY("vs-smbus-option", "Enable/Disable SMBUS on the drive", micron_smbus_option) ) ); diff --git a/plugins/netapp/netapp-nvme.c b/plugins/netapp/netapp-nvme.c index a942f57..15c3923 100644 --- a/plugins/netapp/netapp-nvme.c +++ b/plugins/netapp/netapp-nvme.c @@ -25,7 +25,6 @@ #include "nvme.h" #include "nvme-ioctl.h" -#include "json.h" #include "suffix.h" @@ -197,7 +196,7 @@ static void netapp_get_ontap_labels(char *vsname, char *nspath, vol_name, "/", ns_name); } -static void netapp_smdevice_json(struct json_array *devices, char *devname, +static void netapp_smdevice_json(struct json_object *devices, char *devname, char *arrayname, char *volname, int nsid, char *nguid, char *ctrl, char *astate, char *size, long long lba, long long nsze) @@ -219,7 +218,7 @@ static void netapp_smdevice_json(struct json_array *devices, char *devname, json_array_add_value_object(devices, device_attrs); } -static void netapp_ontapdevice_json(struct json_array *devices, char *devname, +static void netapp_ontapdevice_json(struct json_object *devices, char *devname, char *vsname, char *nspath, int nsid, char *uuid, char *size, long long lba, long long nsze) { @@ -241,7 +240,7 @@ static void netapp_ontapdevice_json(struct json_array *devices, char *devname, static void netapp_smdevices_print(struct smdevice_info *devices, int count, int format) { struct json_object *root = NULL; - struct json_array *json_devices = NULL; + struct json_object *json_devices = NULL; int i, slta; char array_label[ARRAY_LABEL_LEN / 2 + 1]; char volume_label[VOLUME_LABEL_LEN / 2 + 1]; @@ -266,7 +265,7 @@ static void netapp_smdevices_print(struct smdevice_info *devices, int count, int else if (format == NJSON) { /* prepare for json output */ root = json_create_object(); - json_devices = json_create_array(); + json_devices = json_create_object(); } for (i = 0; i < count; i++) { @@ -304,7 +303,7 @@ static void netapp_ontapdevices_print(struct ontapdevice_info *devices, int count, int format) { struct json_object *root = NULL; - struct json_array *json_devices = NULL; + struct json_object *json_devices = NULL; char vsname[ONTAP_LABEL_LEN] = " "; char nspath[ONTAP_NS_PATHLEN] = " "; long long lba; @@ -332,7 +331,7 @@ static void netapp_ontapdevices_print(struct ontapdevice_info *devices, } else if (format == NJSON) { /* prepare for json output */ root = json_create_object(); - json_devices = json_create_array(); + json_devices = json_create_object(); } for (i = 0; i < count; i++) { diff --git a/plugins/nvidia/nvidia-nvme.c b/plugins/nvidia/nvidia-nvme.c new file mode 100644 index 0000000..cdf51ab --- /dev/null +++ b/plugins/nvidia/nvidia-nvme.c @@ -0,0 +1,58 @@ +#include +#include +#include +#include +#include +#include + +#include "linux/nvme_ioctl.h" + +#include "common.h" +#include "nvme.h" +#include "nvme-print.h" +#include "nvme-ioctl.h" +#include "plugin.h" + +#include "argconfig.h" +#include "suffix.h" + +#define CREATE_CMD +#include "nvidia-nvme.h" + +struct nvme_vu_id_ctrl_field { + __u16 json_rpc_2_0_mjr; + __u16 json_rpc_2_0_mnr; + __u16 json_rpc_2_0_ter; + __u8 reserved0[1018]; +}; + +static void json_nvidia_id_ctrl(struct nvme_vu_id_ctrl_field *id, + char *json_rpc_2_0_ver, struct json_object *root) +{ + json_object_add_value_string(root, "json_rpc_2_0_ver", + json_rpc_2_0_ver); +} + +static void nvidia_id_ctrl(__u8 *vs, struct json_object *root) +{ + struct nvme_vu_id_ctrl_field *id = (struct nvme_vu_id_ctrl_field *)vs; + char json_rpc_2_0_ver[16] = { 0 }; + + snprintf(json_rpc_2_0_ver, sizeof(json_rpc_2_0_ver), "0x%04x%04x%04x", + le16_to_cpu(id->json_rpc_2_0_mjr), + le16_to_cpu(id->json_rpc_2_0_mnr), + le16_to_cpu(id->json_rpc_2_0_ter)); + + if (root) { + json_nvidia_id_ctrl(id, json_rpc_2_0_ver, root); + return; + } + + printf("json_rpc_2_0_ver : %s\n", json_rpc_2_0_ver); +} + +static int id_ctrl(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + return __id_ctrl(argc, argv, cmd, plugin, nvidia_id_ctrl); +} diff --git a/plugins/nvidia/nvidia-nvme.h b/plugins/nvidia/nvidia-nvme.h new file mode 100644 index 0000000..bf562b9 --- /dev/null +++ b/plugins/nvidia/nvidia-nvme.h @@ -0,0 +1,17 @@ +#undef CMD_INC_FILE +#define CMD_INC_FILE plugins/nvidia/nvidia-nvme + +#if !defined(NVIDIA_NVME) || defined(CMD_HEADER_MULTI_READ) +#define NVIDIA_NVME + +#include "cmd.h" + +PLUGIN(NAME("nvidia", "NVIDIA vendor specific extensions"), + COMMAND_LIST( + ENTRY("id-ctrl", "Send NVMe Identify Controller", id_ctrl) + ) +); + +#endif + +#include "define_cmd.h" diff --git a/plugins/scaleflux/sfx-nvme.c b/plugins/scaleflux/sfx-nvme.c index 846ca77..df7f9a3 100644 --- a/plugins/scaleflux/sfx-nvme.c +++ b/plugins/scaleflux/sfx-nvme.c @@ -8,6 +8,8 @@ #include #include #include +#include +#include #include "linux/nvme_ioctl.h" @@ -15,7 +17,6 @@ #include "nvme-print.h" #include "nvme-ioctl.h" #include "nvme-status.h" -#include "json.h" #include "plugin.h" #include "argconfig.h" @@ -28,36 +29,40 @@ #define SECTOR_SHIFT 9 #define SFX_GET_FREESPACE _IOWR('N', 0x240, struct sfx_freespace_ctx) -#define IDEMA_CAP(exp_GB) (((__u64)exp_GB - 50ULL) * 1953504ULL + 97696368ULL) +#define NVME_IOCTL_CLR_CARD _IO('N', 0x47) +#define IDEMA_CAP(exp_GB) (((__u64)exp_GB - 50ULL) * 1953504ULL + 97696368ULL) +#define IDEMA_CAP2GB(exp_sector) (((__u64)exp_sector - 97696368ULL) / 1953504ULL + 50ULL) enum { SFX_LOG_LATENCY_READ_STATS = 0xc1, SFX_LOG_SMART = 0xc2, - SFX_LOG_LATENCY_WRITE_STATS = 0xc3, + SFX_LOG_LATENCY_WRITE_STATS = 0xc3, SFX_LOG_QUAL = 0xc4, SFX_LOG_MISMATCHLBA = 0xc5, SFX_LOG_MEDIA = 0xc6, SFX_LOG_BBT = 0xc7, SFX_LOG_IDENTIFY = 0xcc, SFX_FEAT_ATOMIC = 0x01, + SFX_FEAT_UP_P_CAP = 0xac, + SFX_FEAT_CLR_CARD = 0xdc, }; enum sfx_nvme_admin_opcode { nvme_admin_query_cap_info = 0xd3, nvme_admin_change_cap = 0xd4, - nvme_admin_sfx_set_features = 0xd5, - nvme_admin_sfx_get_features = 0xd6, + nvme_admin_sfx_set_features = 0xd5, + nvme_admin_sfx_get_features = 0xd6, }; struct sfx_freespace_ctx { __u64 free_space; - __u64 phy_cap; /* physical capacity, in unit of sector */ - __u64 phy_space; /* physical space considering OP, in unit of sector */ - __u64 user_space; /* user required space, in unit of sector*/ - __u64 hw_used; /* hw space used in 4K */ - __u64 app_written; /* app data written in 4K */ + __u64 phy_cap; /* physical capacity, in unit of sector */ + __u64 phy_space; /* physical space considering OP, in unit of sector */ + __u64 user_space; /* user required space, in unit of sector*/ + __u64 hw_used; /* hw space used in 4K */ + __u64 app_written; /* app data written in 4K */ }; struct nvme_capacity_info { @@ -66,7 +71,7 @@ struct nvme_capacity_info { __u64 used_space; __u64 free_space; }; -struct __attribute__((packed)) nvme_additional_smart_log_item { +struct __attribute__((packed)) nvme_additional_smart_log_item { uint8_t key; uint8_t _kp[2]; uint8_t norm; @@ -112,11 +117,10 @@ int nvme_change_cap(int fd, __u32 nsid, __u64 capacity) struct nvme_admin_cmd cmd = { .opcode = nvme_admin_change_cap, .nsid = nsid, - .cdw10 = (capacity & 0xffffffff), - .cdw11 = (capacity >> 32), + .cdw10 = (capacity & 0xffffffff), + .cdw11 = (capacity >> 32), }; - return nvme_submit_passthru(fd, NVME_IOCTL_ADMIN_CMD,&cmd); } @@ -267,65 +271,65 @@ static void show_sfx_smart_log(struct nvme_additional_smart_log *smart, unsigned int nsid, const char *devname) { printf("Additional Smart Log for ScaleFlux device:%s namespace-id:%x\n", - devname, nsid); - printf("key normalized raw\n"); - printf("program_fail_count : %3d%% %"PRIu64"\n", - smart->program_fail_cnt.norm, - int48_to_long(smart->program_fail_cnt.raw)); - printf("erase_fail_count : %3d%% %"PRIu64"\n", - smart->erase_fail_cnt.norm, - int48_to_long(smart->erase_fail_cnt.raw)); - printf("wear_leveling : %3d%% min: %u, max: %u, avg: %u\n", - smart->wear_leveling_cnt.norm, - le16_to_cpu(smart->wear_leveling_cnt.wear_level.min), - le16_to_cpu(smart->wear_leveling_cnt.wear_level.max), - le16_to_cpu(smart->wear_leveling_cnt.wear_level.avg)); - printf("end_to_end_error_detection_count: %3d%% %"PRIu64"\n", - smart->e2e_err_cnt.norm, - int48_to_long(smart->e2e_err_cnt.raw)); - printf("crc_error_count : %3d%% %"PRIu64"\n", - smart->crc_err_cnt.norm, - int48_to_long(smart->crc_err_cnt.raw)); - printf("timed_workload_media_wear : %3d%% %.3f%%\n", - smart->timed_workload_media_wear.norm, - ((float)int48_to_long(smart->timed_workload_media_wear.raw)) / 1024); - printf("timed_workload_host_reads : %3d%% %"PRIu64"%%\n", - smart->timed_workload_host_reads.norm, - int48_to_long(smart->timed_workload_host_reads.raw)); - printf("timed_workload_timer : %3d%% %"PRIu64" min\n", - smart->timed_workload_timer.norm, - int48_to_long(smart->timed_workload_timer.raw)); - printf("thermal_throttle_status : %3d%% %u%%, cnt: %u\n", - smart->thermal_throttle_status.norm, - smart->thermal_throttle_status.thermal_throttle.pct, - smart->thermal_throttle_status.thermal_throttle.count); - printf("retry_buffer_overflow_count : %3d%% %"PRIu64"\n", - smart->retry_buffer_overflow_cnt.norm, - int48_to_long(smart->retry_buffer_overflow_cnt.raw)); - printf("pll_lock_loss_count : %3d%% %"PRIu64"\n", - smart->pll_lock_loss_cnt.norm, - int48_to_long(smart->pll_lock_loss_cnt.raw)); - printf("nand_bytes_written : %3d%% sectors: %"PRIu64"\n", - smart->nand_bytes_written.norm, - int48_to_long(smart->nand_bytes_written.raw)); - printf("host_bytes_written : %3d%% sectors: %"PRIu64"\n", - smart->host_bytes_written.norm, - int48_to_long(smart->host_bytes_written.raw)); - printf("raid_recover_cnt : %3d%% %"PRIu64"\n", - smart->raid_recover_cnt.norm, - int48_to_long(smart->raid_recover_cnt.raw)); - printf("read_ecc_cnt : %3d%% %"PRIu64"\n", - smart->read_ecc_cnt.norm, - int48_to_long(smart->read_ecc_cnt.raw)); - printf("prog_timeout_cnt : %3d%% %"PRIu64"\n", - smart->prog_timeout_cnt.norm, - int48_to_long(smart->prog_timeout_cnt.raw)); - printf("erase_timeout_cnt : %3d%% %"PRIu64"\n", - smart->erase_timeout_cnt.norm, - int48_to_long(smart->erase_timeout_cnt.raw)); - printf("read_timeout_cnt : %3d%% %"PRIu64"\n", - smart->read_timeout_cnt.norm, - int48_to_long(smart->read_timeout_cnt.raw)); + devname, nsid); + printf("key normalized raw\n"); + printf("program_fail_count : %3d%% %"PRIu64"\n", + smart->program_fail_cnt.norm, + int48_to_long(smart->program_fail_cnt.raw)); + printf("erase_fail_count : %3d%% %"PRIu64"\n", + smart->erase_fail_cnt.norm, + int48_to_long(smart->erase_fail_cnt.raw)); + printf("wear_leveling : %3d%% min: %u, max: %u, avg: %u\n", + smart->wear_leveling_cnt.norm, + le16_to_cpu(smart->wear_leveling_cnt.wear_level.min), + le16_to_cpu(smart->wear_leveling_cnt.wear_level.max), + le16_to_cpu(smart->wear_leveling_cnt.wear_level.avg)); + printf("end_to_end_error_detection_count: %3d%% %"PRIu64"\n", + smart->e2e_err_cnt.norm, + int48_to_long(smart->e2e_err_cnt.raw)); + printf("crc_error_count : %3d%% %"PRIu64"\n", + smart->crc_err_cnt.norm, + int48_to_long(smart->crc_err_cnt.raw)); + printf("timed_workload_media_wear : %3d%% %.3f%%\n", + smart->timed_workload_media_wear.norm, + ((float)int48_to_long(smart->timed_workload_media_wear.raw)) / 1024); + printf("timed_workload_host_reads : %3d%% %"PRIu64"%%\n", + smart->timed_workload_host_reads.norm, + int48_to_long(smart->timed_workload_host_reads.raw)); + printf("timed_workload_timer : %3d%% %"PRIu64" min\n", + smart->timed_workload_timer.norm, + int48_to_long(smart->timed_workload_timer.raw)); + printf("thermal_throttle_status : %3d%% %u%%, cnt: %u\n", + smart->thermal_throttle_status.norm, + smart->thermal_throttle_status.thermal_throttle.pct, + smart->thermal_throttle_status.thermal_throttle.count); + printf("retry_buffer_overflow_count : %3d%% %"PRIu64"\n", + smart->retry_buffer_overflow_cnt.norm, + int48_to_long(smart->retry_buffer_overflow_cnt.raw)); + printf("pll_lock_loss_count : %3d%% %"PRIu64"\n", + smart->pll_lock_loss_cnt.norm, + int48_to_long(smart->pll_lock_loss_cnt.raw)); + printf("nand_bytes_written : %3d%% sectors: %"PRIu64"\n", + smart->nand_bytes_written.norm, + int48_to_long(smart->nand_bytes_written.raw)); + printf("host_bytes_written : %3d%% sectors: %"PRIu64"\n", + smart->host_bytes_written.norm, + int48_to_long(smart->host_bytes_written.raw)); + printf("raid_recover_cnt : %3d%% %"PRIu64"\n", + smart->raid_recover_cnt.norm, + int48_to_long(smart->raid_recover_cnt.raw)); + printf("read_ecc_cnt : %3d%% %"PRIu64"\n", + smart->read_ecc_cnt.norm, + int48_to_long(smart->read_ecc_cnt.raw)); + printf("prog_timeout_cnt : %3d%% %"PRIu64"\n", + smart->prog_timeout_cnt.norm, + int48_to_long(smart->prog_timeout_cnt.raw)); + printf("erase_timeout_cnt : %3d%% %"PRIu64"\n", + smart->erase_timeout_cnt.norm, + int48_to_long(smart->erase_timeout_cnt.raw)); + printf("read_timeout_cnt : %3d%% %"PRIu64"\n", + smart->read_timeout_cnt.norm, + int48_to_long(smart->read_timeout_cnt.raw)); } static int get_additional_smart_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) @@ -357,8 +361,8 @@ static int get_additional_smart_log(int argc, char **argv, struct command *cmd, fd = parse_and_open(argc, argv, desc, opts); - err = nvme_get_log(fd, cfg.namespace_id, 0xca, false, sizeof(smart_log), - (void *)&smart_log); + err = nvme_get_log(fd, cfg.namespace_id, 0xca, false, NVME_NO_LOG_LSP, + sizeof(smart_log), (void *)&smart_log); if (!err) { if (cfg.json) show_sfx_smart_log_jsn(&smart_log, cfg.namespace_id, devicename); @@ -373,7 +377,6 @@ static int get_additional_smart_log(int argc, char **argv, struct command *cmd, return err; } - struct sfx_lat_stats { __u16 maj; __u16 min; @@ -440,7 +443,8 @@ static int get_lat_stats_log(int argc, char **argv, struct command *cmd, struct fd = parse_and_open(argc, argv, desc, opts); - err = nvme_get_log(fd, 0xffffffff, cfg.write ? 0xc3 : 0xc1, false, sizeof(stats), (void *)&stats); + err = nvme_get_log(fd, 0xffffffff, cfg.write ? 0xc3 : 0xc1, false, NVME_NO_LOG_LSP, + sizeof(stats), (void *)&stats); if (!err) { if (!cfg.raw_binary) show_lat_stats(&stats, cfg.write); @@ -448,7 +452,7 @@ static int get_lat_stats_log(int argc, char **argv, struct command *cmd, struct d_raw((unsigned char *)&stats, sizeof(stats)); } else if (err > 0) fprintf(stderr, "NVMe Status:%s(%x)\n", - nvme_status_to_string(err), err); + nvme_status_to_string(err), err); return err; } @@ -517,16 +521,16 @@ static void bd_table_show(unsigned char *bd_table, __u64 table_size) bb_elem = (__u64 *)(bd_table + 5 * sizeof(__u32)); printf("Bad Block Table \n"); - printf("MF_BB_COUNT: %u\n", mf_bb_count); - printf("GROWN_BB_COUNT: %u\n", grown_bb_count); - printf("TOTAL_BB_COUNT: %u\n", total_bb_count); - printf("REMAP_MFBB_COUNT: %u\n", remap_mfbb_count); - printf("REMAP_GBB_COUNT: %u\n", remap_gbb_count); + printf("MF_BB_COUNT: %u\n", mf_bb_count); + printf("GROWN_BB_COUNT: %u\n", grown_bb_count); + printf("TOTAL_BB_COUNT: %u\n", total_bb_count); + printf("REMAP_MFBB_COUNT: %u\n", remap_mfbb_count); + printf("REMAP_GBB_COUNT: %u\n", remap_gbb_count); printf("REMAP_MFBB_TABLE ["); i = 0; while (bb_elem < elem_end && i < remap_mfbb_count) { - printf(" 0x%llx", *(bb_elem++)); + printf(" 0x%"PRIx64"", (uint64_t)*(bb_elem++)); i++; } printf(" ]\n"); @@ -534,14 +538,14 @@ static void bd_table_show(unsigned char *bd_table, __u64 table_size) printf("REMAP_GBB_TABLE ["); i = 0; while (bb_elem < elem_end && i < remap_gbb_count) { - printf(" 0x%llx",*(bb_elem++)); + printf(" 0x%"PRIx64"", (uint64_t)*(bb_elem++)); i++; } printf(" ]\n"); } /** - * @brief "hooks of sfx get-bad-block" + * @brief "hooks of sfx get-bad-block" * * @param argc * @param argv @@ -592,10 +596,16 @@ static int sfx_get_bad_block(int argc, char **argv, struct command *cmd, struct static void show_cap_info(struct sfx_freespace_ctx *ctx) { - printf("user sectors: %#llx\n", ctx->user_space); - printf("totl physical sectors: %#llx\n", ctx->phy_space); - printf("free physical sectors: %#llx\n", ctx->free_space); - printf("used physical sectors: %#llx\n", ctx->phy_space - ctx->free_space); + + printf("logic capacity:%5lluGB(0x%"PRIx64")\n", + IDEMA_CAP2GB(ctx->user_space), (uint64_t)ctx->user_space); + printf("provisioned capacity:%5lluGB(0x%"PRIx64")\n", + IDEMA_CAP2GB(ctx->phy_space), (uint64_t)ctx->phy_space); + printf("free provisioned capacity:%5lluGB(0x%"PRIx64")\n", + IDEMA_CAP2GB(ctx->free_space), (uint64_t)ctx->free_space); + printf("used provisioned capacity:%5lluGB(0x%"PRIx64")\n", + IDEMA_CAP2GB(ctx->phy_space) - IDEMA_CAP2GB(ctx->free_space), + (uint64_t)(ctx->phy_space - ctx->free_space)); } static int query_cap_info(int argc, char **argv, struct command *cmd, struct plugin *plugin) @@ -631,42 +641,94 @@ static int query_cap_info(int argc, char **argv, struct command *cmd, struct plu return err; } -static int change_cap_mem_check(int fd, __u64 trg_in_4k) +static int change_sanity_check(int fd, __u64 trg_in_4k, int *shrink) { struct sfx_freespace_ctx freespace_ctx = { 0 }; struct sysinfo s_info; __u64 mem_need = 0; __u64 cur_in_4k = 0; + __u64 provisoned_cap_4k = 0; __u32 cnt_ms = 0; + int extend = 0; while (ioctl(fd, SFX_GET_FREESPACE, &freespace_ctx)) { if (cnt_ms++ > 600) {//1min - fprintf(stderr, "vu ioctl fail, errno %d\r\n", errno); return -1; } usleep(100000); } - cur_in_4k = freespace_ctx.user_space >> (SFX_PAGE_SHIFT - SECTOR_SHIFT); - if (cur_in_4k > trg_in_4k) { - return 0; + /* + * capacity illegal check + */ + provisoned_cap_4k = freespace_ctx.phy_space >> + (SFX_PAGE_SHIFT - SECTOR_SHIFT); + if (trg_in_4k < provisoned_cap_4k || + trg_in_4k > ((__u64)provisoned_cap_4k * 4)) { + fprintf(stderr, + "WARNING: Only support 1.0~4.0 x provisoned capacity!\n"); + if (trg_in_4k < provisoned_cap_4k) { + fprintf(stderr, + "WARNING: The target capacity is less than 1.0 x provisioned capacity!\n"); + } else { + fprintf(stderr, + "WARNING: The target capacity is larger than 4.0 x provisioned capacity!\n"); + } + return -1; } - - if (sysinfo(&s_info) < 0) { - printf("change-cap query mem info fail\n"); + if (trg_in_4k > ((__u64)provisoned_cap_4k*4)) { + fprintf(stderr, "WARNING: the target capacity is too large\n"); return -1; } - mem_need = (trg_in_4k - cur_in_4k) * 8; - if (s_info.freeram <= 10 || mem_need > s_info.freeram) { - fprintf(stderr, "WARNING: mem needed is %llu, free mem is %lu\n" - "Insufficient memory, please drop cache or add free memory and retry\n", - mem_need, s_info.freeram); - return -1; + /* + * check whether mem enough if extend + * */ + cur_in_4k = freespace_ctx.user_space >> (SFX_PAGE_SHIFT - SECTOR_SHIFT); + extend = (cur_in_4k <= trg_in_4k); + if (extend) { + if (sysinfo(&s_info) < 0) { + printf("change-cap query mem info fail\n"); + return -1; + } + mem_need = (trg_in_4k - cur_in_4k) * 8; + if (s_info.freeram <= 10 || mem_need > s_info.freeram) { + fprintf(stderr, + "WARNING: Free memory is not enough! " + "Please drop cache or extend more memory and retry\n" + "WARNING: Memory needed is %"PRIu64", free memory is %"PRIu64"\n", + (uint64_t)mem_need, (uint64_t)s_info.freeram); + return -1; + } } + *shrink = !extend; + return 0; } +/** + * @brief prompt and get user confirm input + * + * @param str, prompt string + * + * @return 0, cancled; 1 confirmed + */ +static int sfx_confirm_change(const char *str) +{ + char confirm; + fprintf(stderr, "WARNING: %s.\n" + "Use the force [--force] option to suppress this warning.\n", str); + + fprintf(stderr, "Confirm Y/y, Others cancel:\n"); + confirm = fgetc(stdin); + if (confirm != 'y' && confirm != 'Y') { + fprintf(stderr, "Cancled.\n"); + return 0; + } + fprintf(stderr, "Sending operation ... \n"); + return 1; +} + static int change_cap(int argc, char **argv, struct command *cmd, struct plugin *plugin) { int err = -1, fd; @@ -678,6 +740,8 @@ static int change_cap(int argc, char **argv, struct command *cmd, struct plugin const char *force = "The \"I know what I'm doing\" flag, skip confirmation before sending command"; __u64 cap_in_4k = 0; __u64 cap_in_sec = 0; + int shrink = 0; + struct config { __u64 cap_in_byte; __u32 capacity_in_gb; @@ -694,7 +758,7 @@ static int change_cap(int argc, char **argv, struct command *cmd, struct plugin OPT_ARGS(opts) = { OPT_UINT("cap", 'c', &cfg.capacity_in_gb, cap_gb), - OPT_UINT("cap-byte", 'z', &cfg.cap_in_byte, cap_byte), + OPT_SUFFIX("cap-byte", 'z', &cfg.cap_in_byte, cap_byte), OPT_FLAG("force", 'f', &cfg.force, force), OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), OPT_FLAG("json", 'j', &cfg.json, json), @@ -706,22 +770,21 @@ static int change_cap(int argc, char **argv, struct command *cmd, struct plugin return fd; } - if (!cfg.force) { - fprintf(stderr, "WARNING: Changing capacity may irrevocably delete user data.\n" - "You have 10 seconds to press Ctrl-C to cancel this operation.\n\n" - "Use the force [--force|-f] option to suppress this warning.\n"); - sleep(10); - fprintf(stderr, "Sending operation ... \n"); - } - cap_in_sec = IDEMA_CAP(cfg.capacity_in_gb); cap_in_4k = cap_in_sec >> 3; if (cfg.cap_in_byte) cap_in_4k = cfg.cap_in_byte >> 12; - printf("%dG %lluB %llu 4K\n", - cfg.capacity_in_gb, cfg.cap_in_byte, cap_in_4k); - if (change_cap_mem_check(fd, cap_in_4k)) + printf("%dG %"PRIu64"B %"PRIu64" 4K\n", + cfg.capacity_in_gb, (uint64_t)cfg.cap_in_byte, (uint64_t)cap_in_4k); + + if (change_sanity_check(fd, cap_in_4k, &shrink)) { + printf("ScaleFlux change-capacity: fail\n"); return err; + } + + if (!cfg.force && shrink && !sfx_confirm_change("Changing Cap may irrevocably delete this device's data")) { + return 0; + } err = nvme_change_cap(fd, 0xffffffff, cap_in_4k); if (err < 0) @@ -731,20 +794,54 @@ static int change_cap(int argc, char **argv, struct command *cmd, struct plugin nvme_status_to_string(err), err); else { printf("ScaleFlux change-capacity: success\n"); - if(ioctl(fd, BLKRRPART) < 0) { - fprintf(stderr, "failed to re-read partition table\n"); - err = EFAULT; - } + ioctl(fd, BLKRRPART); } return err; } +static int sfx_verify_chr(int fd) +{ + static struct stat nvme_stat; + int err = fstat(fd, &nvme_stat); + + if (err < 0) { + perror("fstat"); + return errno; + } + if (!S_ISCHR(nvme_stat.st_mode)) { + fprintf(stderr, + "Error: requesting clean card on non-controller handle\n"); + return ENOTBLK; + } + return 0; +} + +static int sfx_clean_card(int fd) +{ + int ret; + + ret = sfx_verify_chr(fd); + if (ret) + return ret; + ret = ioctl(fd, NVME_IOCTL_CLR_CARD); + if (ret) + perror("Ioctl Fail."); + else + printf("ScaleFlux clean card success\n"); + + return ret; +} + char *sfx_feature_to_string(int feature) { switch (feature) { - case SFX_FEAT_ATOMIC: return "ATOMIC"; + case SFX_FEAT_ATOMIC: + return "ATOMIC"; + case SFX_FEAT_UP_P_CAP: + return "UPDATE_PROVISION_CAPACITY"; - default: return "Unknown"; + default: + return "Unknown"; } } @@ -752,27 +849,34 @@ static int sfx_set_feature(int argc, char **argv, struct command *cmd, struct pl { int err = 0, fd; char *desc = "ScaleFlux internal set features\n" - "feature id 1: ATOMIC"; + "feature id 1: ATOMIC\n" + "value 0: Disable atomic write\n" + " 1: Enable atomic write"; const char *value = "new value of feature (required)"; const char *feature_id = "hex feature name (required)"; const char *namespace_id = "desired namespace"; + const char *force = "The \"I know what I'm doing\" flag, skip confirmation before sending command"; + struct nvme_id_ns ns; struct config { __u32 namespace_id; __u32 feature_id; __u32 value; + __u32 force; }; struct config cfg = { .namespace_id = 1, .feature_id = 0, .value = 0, + .force = 0, }; OPT_ARGS(opts) = { OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), OPT_UINT("feature-id", 'f', &cfg.feature_id, feature_id), - OPT_UINT("value", 'v', &cfg.value, value), + OPT_UINT("value", 'v', &cfg.value, value), + OPT_FLAG("force", 's', &cfg.force, force), OPT_END() }; @@ -786,7 +890,17 @@ static int sfx_set_feature(int argc, char **argv, struct command *cmd, struct pl return EINVAL; } - if (cfg.feature_id == SFX_FEAT_ATOMIC) { + if (cfg.feature_id == SFX_FEAT_CLR_CARD) { + /*Warning for clean card*/ + if (!cfg.force && !sfx_confirm_change("Going to clean device's data, confirm umount fs and try again")) { + return 0; + } else { + return sfx_clean_card(fd); + } + + } + + if (cfg.feature_id == SFX_FEAT_ATOMIC && cfg.value != 0) { if (cfg.namespace_id != 0xffffffff) { err = nvme_identify_ns(fd, cfg.namespace_id, 0, &ns); if (err) { @@ -806,14 +920,25 @@ static int sfx_set_feature(int argc, char **argv, struct command *cmd, struct pl return EFAULT; } } + } else if (cfg.feature_id == SFX_FEAT_UP_P_CAP) { + if (cfg.value <= 0) { + fprintf(stderr, "Invalid Param\n"); + return EINVAL; + } + + /*Warning for change pacp by GB*/ + if (!cfg.force && !sfx_confirm_change("Changing physical capacity may irrevocably delete this device's data")) { + return 0; + } } err = nvme_sfx_set_features(fd, cfg.namespace_id, cfg.feature_id, cfg.value); + if (err < 0) { perror("ScaleFlux-set-feature"); return errno; } else if (!err) { - printf("ScaleFlux set-feature:%02x (%s), value:%#08x\n", cfg.feature_id, + printf("ScaleFlux set-feature:%#02x (%s), value:%d\n", cfg.feature_id, sfx_feature_to_string(cfg.feature_id), cfg.value); } else if (err > 0) fprintf(stderr, "NVMe Status:%s(%x)\n", diff --git a/plugins/scaleflux/sfx-nvme.h b/plugins/scaleflux/sfx-nvme.h index daf9c33..8f14501 100644 --- a/plugins/scaleflux/sfx-nvme.h +++ b/plugins/scaleflux/sfx-nvme.h @@ -14,12 +14,10 @@ PLUGIN(NAME("sfx", "ScaleFlux vendor specific extensions"), ENTRY("query-cap", "Query current capacity info", query_cap_info) ENTRY("change-cap", "Dynamic change capacity", change_cap) ENTRY("set-feature", "Set a feature", sfx_set_feature) - ENTRY("get-feature", "get a feature", sfx_get_feature) + ENTRY("get-feature", "Get a feature", sfx_get_feature) ) ); #endif #include "define_cmd.h" - - diff --git a/plugins/seagate/seagate-nvme.c b/plugins/seagate/seagate-nvme.c index 7ba14f8..d5ef32b 100644 --- a/plugins/seagate/seagate-nvme.c +++ b/plugins/seagate/seagate-nvme.c @@ -36,7 +36,6 @@ #include "plugin.h" #include "argconfig.h" #include "suffix.h" -#include "json.h" #define CREATE_CMD @@ -132,7 +131,7 @@ static char *log_pages_supp_print(__u32 pageID) static void json_log_pages_supp(log_page_map *logPageMap) { struct json_object *root; - struct json_array *logPages; + struct json_object *logPages; __u32 i = 0; root = json_create_object(); @@ -177,7 +176,8 @@ static int log_pages_supp(int argc, char **argv, struct command *cmd, }; fd = parse_and_open(argc, argv, desc, opts); - err = nvme_get_log(fd, 1, 0xc5, false, sizeof(logPageMap), &logPageMap); + err = nvme_get_log(fd, 1, 0xc5, false, NVME_NO_LOG_LSP, + sizeof(logPageMap), &logPageMap); if (!err) { if (strcmp(cfg.output_format,"json")) { printf ("Seagate Supported Log-pages count :%d\n", @@ -312,7 +312,7 @@ static char *print_ext_smart_id(__u8 attrId) return "RAIS_ECC_CORRECT_ERR_COUNT"; break; case VS_ATTR_ID_UNCORRECTABLE_RAISE_ERRORS: - return "Uncorrectable read error count";/*VS_ATTR_ID_UNCORRECTABLE_RAISE_ERRORS*/ + return "Uncorrectable RAISE error count";/*VS_ATTR_ID_UNCORRECTABLE_RAISE_ERRORS*/ break; case VS_ATTR_ID_DRIVE_LIFE_PROTECTION_STATUS: return "DRIVE_LIFE_PROTECTION_STATUS"; @@ -493,7 +493,7 @@ static void json_print_smart_log(struct json_object *root, EXTENDED_SMART_INFO_T *ExtdSMARTInfo ) { /*struct json_object *root; */ - struct json_array *lbafs; + struct json_object *lbafs; int index = 0; static __u64 lsbGbErased = 0, msbGbErased = 0, lsbLifWrtToFlash = 0, msbLifWrtToFlash = 0, @@ -650,7 +650,7 @@ static void json_print_smart_log_CF(struct json_object *root, vendor_log_page_CF *pLogPageCF) { /*struct json_object *root;*/ - struct json_array *logPages; + struct json_object *logPages; unsigned int currentTemp, maxTemp; char buf[40]; @@ -715,11 +715,9 @@ static int vs_smart_log(int argc, char **argv, struct command *cmd, struct plugi EXTENDED_SMART_INFO_T ExtdSMARTInfo; vendor_log_page_CF logPageCF; int fd; - struct json_object *root; - struct json_array *lbafs; + struct json_object *root = json_create_object(); + struct json_object *lbafs = json_create_array(); struct json_object *lbafs_ExtSmart, *lbafs_DramSmart; - root = json_create_object(); - lbafs = json_create_array(); const char *desc = "Retrieve Seagate Extended SMART information for the given device "; const char *output_format = "output in binary format"; @@ -741,7 +739,8 @@ static int vs_smart_log(int argc, char **argv, struct command *cmd, struct plugi if (strcmp(cfg.output_format,"json")) printf("Seagate Extended SMART Information :\n"); - err = nvme_get_log(fd, 1, 0xC4, false, sizeof(ExtdSMARTInfo), &ExtdSMARTInfo); + err = nvme_get_log(fd, 1, 0xC4, false, NVME_NO_LOG_LSP, + sizeof(ExtdSMARTInfo), &ExtdSMARTInfo); if (!err) { if (strcmp(cfg.output_format,"json")) { printf("%-39s %-15s %-19s \n", "Description", "Ext-Smart-Id", "Ext-Smart-Value"); @@ -763,7 +762,8 @@ static int vs_smart_log(int argc, char **argv, struct command *cmd, struct plugi * Next get Log Page 0xCF */ - err = nvme_get_log(fd, 1, 0xCF, false, sizeof(logPageCF), &logPageCF); + err = nvme_get_log(fd, 1, 0xCF, false, NVME_NO_LOG_LSP, + sizeof(logPageCF), &logPageCF); if (!err) { if(strcmp(cfg.output_format,"json")) { /*printf("Seagate DRAM Supercap SMART Attributes :\n");*/ @@ -860,7 +860,8 @@ static int temp_stats(int argc, char **argv, struct command *cmd, struct plugin } /* STEP-2 : Get Max temperature form Ext SMART-id 194 */ - err = nvme_get_log(fd, 1, 0xC4, false, sizeof(ExtdSMARTInfo), &ExtdSMARTInfo); + err = nvme_get_log(fd, 1, 0xC4, false, NVME_NO_LOG_LSP, + sizeof(ExtdSMARTInfo), &ExtdSMARTInfo); if (!err) { for(index = 0; index < NUMBER_EXTENDED_SMART_ATTRIBUTES; index++) { if (ExtdSMARTInfo.vendorData[index].AttributeNumber == VS_ATTR_ID_MAX_LIFE_TEMPERATURE) { @@ -882,7 +883,8 @@ static int temp_stats(int argc, char **argv, struct command *cmd, struct plugin fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(err), err); - cf_err = nvme_get_log(fd, 1, 0xCF, false, sizeof(ExtdSMARTInfo), &logPageCF); + cf_err = nvme_get_log(fd, 1, 0xCF, false, NVME_NO_LOG_LSP, + sizeof(ExtdSMARTInfo), &logPageCF); if(!cf_err) { scCurrentTemp = logPageCF.AttrCF.SuperCapCurrentTemperature; @@ -1011,7 +1013,8 @@ static int vs_pcie_error_log(int argc, char **argv, struct command *cmd, struct if(strcmp(cfg.output_format,"json")) printf("Seagate PCIe error counters Information :\n"); - err = nvme_get_log(fd, 1, 0xCB, false, sizeof(pcieErrorLog), &pcieErrorLog); + err = nvme_get_log(fd, 1, 0xCB, false, NVME_NO_LOG_LSP, + sizeof(pcieErrorLog), &pcieErrorLog); if (!err) { if(strcmp(cfg.output_format,"json")) { print_vs_pcie_error_log(pcieErrorLog); diff --git a/plugins/shannon/shannon-nvme.c b/plugins/shannon/shannon-nvme.c index 3aa6f8a..588f4f3 100644 --- a/plugins/shannon/shannon-nvme.c +++ b/plugins/shannon/shannon-nvme.c @@ -11,7 +11,6 @@ #include "nvme.h" #include "nvme-print.h" #include "nvme-ioctl.h" -#include "json.h" #include "plugin.h" #include "argconfig.h" @@ -143,7 +142,7 @@ static int get_additional_smart_log(int argc, char **argv, struct command *cmd, fd = parse_and_open(argc, argv, desc, opts); err = nvme_get_log(fd, cfg.namespace_id, 0xca, false, - sizeof(smart_log), &smart_log); + NVME_NO_LOG_LSP, sizeof(smart_log), &smart_log); if (!err) { if (!cfg.raw_binary) show_shannon_smart_log(&smart_log, cfg.namespace_id, devicename); @@ -182,7 +181,7 @@ static int get_additional_feature(int argc, char **argv, struct command *cmd, st struct config { __u32 namespace_id; - __u32 feature_id; + enum nvme_feat feature_id; __u8 sel; __u32 cdw11; __u32 data_len; @@ -192,7 +191,7 @@ static int get_additional_feature(int argc, char **argv, struct command *cmd, st struct config cfg = { .namespace_id = 1, - .feature_id = 0, + .feature_id = NVME_FEAT_NONE, .sel = 0, .cdw11 = 0, .data_len = 0, diff --git a/plugins/toshiba/toshiba-nvme.c b/plugins/toshiba/toshiba-nvme.c index c067ce8..53d54bc 100644 --- a/plugins/toshiba/toshiba-nvme.c +++ b/plugins/toshiba/toshiba-nvme.c @@ -393,7 +393,7 @@ static int nvme_get_vendor_log(int fd, __u32 namespace_id, int log_page, goto end; } err = nvme_get_log(fd, namespace_id, log_page, false, - log_len, log); + NVME_NO_LOG_LSP, log_len, log); if (err) { fprintf(stderr, "%s: couldn't get log 0x%x\n", __func__, log_page); diff --git a/plugins/virtium/virtium-nvme.c b/plugins/virtium/virtium-nvme.c index 47b0fdc..a194a5f 100644 --- a/plugins/virtium/virtium-nvme.c +++ b/plugins/virtium/virtium-nvme.c @@ -633,7 +633,7 @@ static void vt_parse_detail_identify(const struct nvme_id_ctrl *ctrl) const char *VWCtable[2] = {"0 = a volatile write cache is not present", "1 = a volatile write cache is present"}; - const char *NVSCCtable[2] = {"0 = the format of all NVM Vendor Specific Commands are vendor specific", + const char *ICSVSCCtable[2] = {"0 = the format of all NVM Vendor Specific Commands are vendor specific", "1 = all NVM Vendor Specific Commands use the format defined in NVM Express specification"}; const char *SGLSSubtable[4] = {"00b = SGLs are not supported", @@ -883,11 +883,11 @@ static void vt_parse_detail_identify(const struct nvme_id_ctrl *ctrl) vt_convert_data_buffer_to_hex_string(&buf[528], 2, true, s); printf(" \"Atomic Write Unit Power Fail\":\"%sh\",\n", s); - temp = ctrl->nvscc; + temp = ctrl->icsvscc; printf(" \"NVM Vendor Specific Command Configuration\":{\n"); vt_convert_data_buffer_to_hex_string(&buf[530], 1, true, s); printf(" \"Value\":\"%sh\",\n", s); - vt_build_identify_lv2(temp, 0, 1, NVSCCtable, true); + vt_build_identify_lv2(temp, 0, 1, ICSVSCCtable, true); vt_convert_data_buffer_to_hex_string(&buf[532], 2, true, s); printf(" \"Atomic Compare 0 Write Unit\":\"%sh\",\n", s); diff --git a/plugins/wdc/wdc-nvme.c b/plugins/wdc/wdc-nvme.c index 0cebe3f..f7a5b31 100644 --- a/plugins/wdc/wdc-nvme.c +++ b/plugins/wdc/wdc-nvme.c @@ -19,6 +19,7 @@ * Author: Chaitanya Kulkarni , * Dong Ho , * Jeff Lien + * Brandon Paupore */ #include #include @@ -36,7 +37,7 @@ #include "nvme-print.h" #include "nvme-ioctl.h" #include "plugin.h" -#include "json.h" +#include "nvme-status.h" #include "argconfig.h" #include "suffix.h" @@ -52,6 +53,11 @@ #define WDC_NVME_LOG_SIZE_DATA_LEN 0x08 #define WDC_NVME_LOG_SIZE_HDR_LEN 0x08 +/* Enclosure */ +#define WDC_OPENFLEX_MI_DEVICE_MODEL "OpenFlex" +#define WDC_RESULT_MORE_DATA 0x80000000 +#define WDC_RESULT_NOT_AVAILABLE 0x7FFFFFFF + /* Device Config */ #define WDC_NVME_VID 0x1c58 #define WDC_NVME_VID_2 0x1b96 @@ -66,12 +72,13 @@ #define WDC_NVME_SN640_DEV_ID 0x2400 #define WDC_NVME_SN640_DEV_ID_1 0x2401 #define WDC_NVME_SN640_DEV_ID_2 0x2402 -#define WDC_NVME_SN640_DEV_ID_3 0x2404 -#define WDC_NVME_ZN440_DEV_ID 0x2600 -#define WDC_NVME_SN440_DEV_ID 0x2610 -#define WDC_NVME_SN7GC_DEV_ID 0x2700 -#define WDC_NVME_SN7GC_DEV_ID_1 0x2701 -#define WDC_NVME_SN7GC_DEV_ID_2 0x2702 +#define WDC_NVME_SN640_DEV_ID_3 0x2404 +#define WDC_NVME_ZN540_DEV_ID 0x2600 +#define WDC_NVME_SN540_DEV_ID 0x2610 +#define WDC_NVME_SN650_DEV_ID 0x2700 +#define WDC_NVME_SN650_DEV_ID_1 0x2701 +#define WDC_NVME_SN650_DEV_ID_2 0x2702 +#define WDC_NVME_SN650_DEV_ID_3 0x2720 #define WDC_NVME_SXSLCL_DEV_ID 0x2001 #define WDC_NVME_SN520_DEV_ID 0x5003 #define WDC_NVME_SN520_DEV_ID_1 0x5004 @@ -81,6 +88,8 @@ #define WDC_NVME_SN730B_DEV_ID 0x3714 #define WDC_NVME_SN730B_DEV_ID_1 0x3734 #define WDC_NVME_SN340_DEV_ID 0x500d +#define WDC_NVME_ZN350_DEV_ID 0x5010 +#define WDC_NVME_ZN350_DEV_ID_1 0x5018 #define WDC_DRIVE_CAP_CAP_DIAG 0x0000000000000001 #define WDC_DRIVE_CAP_INTERNAL_LOG 0x0000000000000002 @@ -92,9 +101,9 @@ #define WDC_DRIVE_CAP_CLEAR_PCIE 0x0000000000000080 #define WDC_DRIVE_CAP_RESIZE 0x0000000000000100 #define WDC_DRIVE_CAP_NAND_STATS 0x0000000000000200 -#define WDC_DRIVE_CAP_DRIVE_LOG 0x0000000000000400 -#define WDC_DRIVE_CAP_CRASH_DUMP 0x0000000000000800 -#define WDC_DRIVE_CAP_PFAIL_DUMP 0x0000000000001000 +#define WDC_DRIVE_CAP_DRIVE_LOG 0x0000000000000400 +#define WDC_DRIVE_CAP_CRASH_DUMP 0x0000000000000800 +#define WDC_DRIVE_CAP_PFAIL_DUMP 0x0000000000001000 #define WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY 0x0000000000002000 #define WDC_DRIVE_CAP_CLEAR_FW_ACT_HISTORY 0x0000000000004000 #define WDC_DRVIE_CAP_DISABLE_CTLR_TELE_LOG 0x0000000000008000 @@ -102,14 +111,31 @@ #define WDC_DRIVE_CAP_LOG_PAGE_DIR 0x0000000000020000 #define WDC_DRIVE_CAP_NS_RESIZE 0x0000000000040000 #define WDC_DRIVE_CAP_INFO 0x0000000000080000 +#define WDC_DRIVE_CAP_C0_LOG_PAGE 0x0000000000100000 +#define WDC_DRIVE_CAP_TEMP_STATS 0x0000000000200000 +#define WDC_DRIVE_CAP_VUC_CLEAR_PCIE 0x0000000000400000 +#define WDC_DRIVE_CAP_VU_FID_CLEAR_PCIE 0x0000000000800000 +#define WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY_C2 0x0000000001000000 +#define WDC_DRIVE_CAP_VU_FID_CLEAR_FW_ACT_HISTORY 0x0000000002000000 +#define WDC_DRIVE_CAP_CLOUD_SSD_VERSION 0x0000000004000000 +#define WDC_DRIVE_CAP_PCIE_STATS 0x0000000008000000 +#define WDC_DRIVE_CAP_INFO_2 0x0000000010000000 #define WDC_DRIVE_CAP_DRIVE_ESSENTIALS 0x0000000100000000 #define WDC_DRIVE_CAP_DUI_DATA 0x0000000200000000 #define WDC_SN730B_CAP_VUC_LOG 0x0000000400000000 -#define WDC_DRIVE_CAP_SN340_DUI 0x0000000800000000 -#define WDC_DRIVE_CAP_SMART_LOG_MASK (WDC_DRIVE_CAP_C1_LOG_PAGE | WDC_DRIVE_CAP_CA_LOG_PAGE | \ - WDC_DRIVE_CAP_D0_LOG_PAGE) - +#define WDC_DRIVE_CAP_DUI 0x0000000800000000 +#define WDC_DRIVE_CAP_SMART_LOG_MASK (WDC_DRIVE_CAP_C0_LOG_PAGE | WDC_DRIVE_CAP_C1_LOG_PAGE | \ + WDC_DRIVE_CAP_CA_LOG_PAGE | WDC_DRIVE_CAP_D0_LOG_PAGE) +#define WDC_DRIVE_CAP_CLEAR_PCIE_MASK (WDC_DRIVE_CAP_CLEAR_PCIE | \ + WDC_DRIVE_CAP_VUC_CLEAR_PCIE | \ + WDC_DRIVE_CAP_VU_FID_CLEAR_PCIE) +#define WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY_MASK (WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY | \ + WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY_C2) +#define WDC_DRIVE_CAP_CLEAR_FW_ACT_HISTORY_MASK (WDC_DRIVE_CAP_CLEAR_FW_ACT_HISTORY | \ + WDC_DRIVE_CAP_VU_FID_CLEAR_FW_ACT_HISTORY) +#define WDC_DRIVE_CAP_INTERNAL_LOG_MASK (WDC_DRIVE_CAP_INTERNAL_LOG | WDC_DRIVE_CAP_DUI | \ + WDC_DRIVE_CAP_DUI_DATA | WDC_SN730B_CAP_VUC_LOG) /* SN730 Get Log Capabilities */ #define SN730_NVME_GET_LOG_OPCODE 0xc2 #define SN730_GET_FULL_LOG_LENGTH 0x00080009 @@ -126,9 +152,20 @@ /* Customer ID's */ #define WDC_CUSTOMER_ID_GN 0x0001 #define WDC_CUSTOMER_ID_GD 0x0101 -#define WDC_CUSTOMER_ID_0x1004 0x1004 +#define WDC_CUSTOMER_ID_BD 0x1009 + #define WDC_CUSTOMER_ID_0x1005 0x1005 +#define WDC_CUSTOMER_ID_0x1004 0x1004 +#define WDC_CUSTOMER_ID_0x1008 0x1008 +#define WDC_CUSTOMER_ID_0x1304 0x1304 + +#define WDC_ALL_PAGE_MASK 0xFFFF +#define WDC_C0_PAGE_MASK 0x0001 +#define WDC_C1_PAGE_MASK 0x0002 +#define WDC_CA_PAGE_MASK 0x0004 +#define WDC_D0_PAGE_MASK 0x0008 + /* Drive Resize */ #define WDC_NVME_DRIVE_RESIZE_OPCODE 0xCC #define WDC_NVME_DRIVE_RESIZE_CMD 0x03 @@ -142,6 +179,9 @@ #define WDC_NVME_DRIVE_INFO_CMD 0x22 #define WDC_NVME_DRIVE_INFO_SUBCMD 0x06 +/* VS PCIE Stats */ +#define WDC_NVME_PCIE_STATS_OPCODE 0xD1 + /* Capture Diagnostics */ #define WDC_NVME_CAP_DIAG_HEADER_TOC_SIZE WDC_NVME_LOG_SIZE_DATA_LEN #define WDC_NVME_CAP_DIAG_OPCODE 0xE6 @@ -160,6 +200,7 @@ #define WDC_NVME_DUI_MAX_SECTION_V2 0x26 #define WDC_NVME_DUI_MAX_SECTION_V3 0x23 #define WDC_NVME_DUI_MAX_DATA_AREA 0x05 +#define WDC_NVME_SN730_SECTOR_SIZE 512 /* Telemtery types for vs-internal-log command */ #define WDC_TELEMETRY_TYPE_NONE 0x0 @@ -224,6 +265,7 @@ #define WDC_NVME_CLEAR_FW_ACT_HIST_OPCODE 0xC6 #define WDC_NVME_CLEAR_FW_ACT_HIST_CMD 0x23 #define WDC_NVME_CLEAR_FW_ACT_HIST_SUBCMD 0x05 +#define WDC_NVME_CLEAR_FW_ACT_HIST_VU_FID 0xC1 /* Additional Smart Log */ #define WDC_ADD_LOG_BUF_LEN 0x4000 @@ -233,6 +275,7 @@ /* C2 Log Page */ #define WDC_NVME_GET_DEV_MGMNT_LOG_PAGE_OPCODE 0xC2 +#define WDC_NVME_GET_DEV_MGMNT_LOG_PAGE_OPCODE_C8 0xC8 #define WDC_C2_LOG_BUF_LEN 0x1000 #define WDC_C2_LOG_PAGES_SUPPORTED_ID 0x08 #define WDC_C2_CUSTOMER_ID_ID 0x15 @@ -262,11 +305,20 @@ /* C0 EOL Status Log Page */ #define WDC_NVME_GET_EOL_STATUS_LOG_OPCODE 0xC0 #define WDC_NVME_EOL_STATUS_LOG_LEN 0x200 +#define WDC_NVME_SMART_CLOUD_ATTR_LEN 0x200 + +/* C0 SMART Cloud Attributes Log Page*/ +#define WDC_NVME_GET_SMART_CLOUD_ATTR_LOG_OPCODE 0xC0 /* CB - FW Activate History Log Page */ #define WDC_NVME_GET_FW_ACT_HISTORY_LOG_ID 0xCB #define WDC_FW_ACT_HISTORY_LOG_BUF_LEN 0x3d0 +/* C2 - FW Activation History Log Page */ +#define WDC_NVME_GET_FW_ACT_HISTORY_C2_LOG_ID 0xC2 +#define WDC_FW_ACT_HISTORY_C2_LOG_BUF_LEN 0x1000 +#define WDC_MAX_NUM_ACT_HIST_ENTRIES 20 + /* D0 Smart Log Page */ #define WDC_NVME_GET_VU_SMART_LOG_OPCODE 0xD0 #define WDC_NVME_VU_SMART_LOG_LEN 0x200 @@ -274,13 +326,16 @@ /* Log Page Directory defines */ #define NVME_LOG_PERSISTENT_EVENT 0x0D #define WDC_LOG_ID_C0 0xC0 +#define WDC_LOG_ID_C1 0xC1 #define WDC_LOG_ID_C2 WDC_NVME_GET_DEV_MGMNT_LOG_PAGE_OPCODE #define WDC_LOG_ID_C4 0xC4 #define WDC_LOG_ID_C5 0xC5 #define WDC_LOG_ID_C6 0xC6 +#define WDC_LOG_ID_C8 WDC_NVME_GET_DEV_MGMNT_LOG_PAGE_OPCODE_C8 #define WDC_LOG_ID_CA WDC_NVME_GET_DEVICE_INFO_LOG_OPCODE #define WDC_LOG_ID_CB WDC_NVME_GET_FW_ACT_HISTORY_LOG_ID #define WDC_LOG_ID_D0 WDC_NVME_GET_VU_SMART_LOG_OPCODE +#define WDC_LOG_ID_D1 0xD1 #define WDC_LOG_ID_D6 0xD6 #define WDC_LOG_ID_D7 0xD7 #define WDC_LOG_ID_D8 0xD8 @@ -291,10 +346,11 @@ #define WDC_LOG_ID_FA 0xFA /* Clear PCIe Correctable Errors */ -#define WDC_NVME_CLEAR_PCIE_CORR_OPCODE WDC_NVME_CAP_DIAG_CMD_OPCODE -#define WDC_NVME_CLEAR_PCIE_CORR_CMD 0x22 -#define WDC_NVME_CLEAR_PCIE_CORR_SUBCMD 0x04 - +#define WDC_NVME_CLEAR_PCIE_CORR_OPCODE WDC_NVME_CAP_DIAG_CMD_OPCODE +#define WDC_NVME_CLEAR_PCIE_CORR_CMD 0x22 +#define WDC_NVME_CLEAR_PCIE_CORR_SUBCMD 0x04 +#define WDC_NVME_CLEAR_PCIE_CORR_OPCODE_VUC 0xD2 +#define WDC_NVME_CLEAR_PCIE_CORR_FEATURE_ID 0xC3 /* Clear Assert Dump Status */ #define WDC_NVME_CLEAR_ASSERT_DUMP_OPCODE 0xD8 #define WDC_NVME_CLEAR_ASSERT_DUMP_CMD 0x03 @@ -319,6 +375,8 @@ /* VU Opcodes */ #define WDC_DE_VU_READ_SIZE_OPCODE 0xC0 #define WDC_DE_VU_READ_BUFFER_OPCODE 0xC2 +#define WDC_NVME_ADMIN_ENC_MGMT_SND 0xC9 +#define WDC_NVME_ADMIN_ENC_MGMT_RCV 0xCA #define WDC_DE_FILE_HEADER_SIZE 4 #define WDC_DE_FILE_OFFSET_SIZE 2 @@ -332,6 +390,21 @@ #define WDC_DE_DESTN_SPI 1 #define WDC_DE_DUMPTRACE_DESTINATION 6 +#define NVME_ID_CTRL_MODEL_NUMBER_SIZE 40 +#define NVME_ID_CTRL_SERIAL_NUMBER_SIZE 20 + +/* Enclosure log */ +#define WDC_NVME_ENC_LOG_SIZE_CHUNK 0x1000 +#define WDC_NVME_ENC_NIC_LOG_SIZE 0x400000 + +/* Enclosure nic crash dump get-log id */ +#define WDC_ENC_NIC_CRASH_DUMP_ID_SLOT_1 0xD1 +#define WDC_ENC_NIC_CRASH_DUMP_ID_SLOT_2 0xD2 +#define WDC_ENC_NIC_CRASH_DUMP_ID_SLOT_3 0xD3 +#define WDC_ENC_NIC_CRASH_DUMP_ID_SLOT_4 0xD4 +#define WDC_ENC_CRASH_DUMP_ID 0xE4 +#define WDC_ENC_LOG_DUMP_ID 0xE2 + typedef enum _NVME_FEATURES_SELECT { FS_CURRENT = 0, @@ -392,6 +465,62 @@ typedef enum WDC_DE_TYPE_ALL = 0xFFFFFFF, } WDC_DRIVE_ESSENTIAL_TYPE; +typedef enum +{ + SCAO_PMUW = 0, /* Physical media units written */ + SCAO_PMUR = 16, /* Physical media units read */ + SCAO_BUNBR = 32, /* Bad user nand blocks raw */ + SCAO_BUNBN = 38, /* Bad user nand blocks normalized */ + SCAO_BSNBR = 40, /* Bad system nand blocks raw */ + SCAO_BSNBN = 46, /* Bad system nand blocks normalized */ + SCAO_XRC = 48, /* XOR recovery count */ + SCAO_UREC = 56, /* Uncorrectable read error count */ + SCAO_SEEC = 64, /* Soft ecc error count */ + SCAO_EECE = 72, /* End to end corrected errors */ + SCAO_EEDC = 76, /* End to end detected errors */ + SCAO_SDPU = 80, /* System data percent used */ + SCAO_RFSC = 81, /* Refresh counts */ + SCAO_MXUDEC = 88, /* Max User data erase counts */ + SCAO_MNUDEC = 92, /* Min User data erase counts */ + SCAO_NTTE = 96, /* Number of Thermal throttling events */ + SCAO_CTS = 97, /* Current throttling status */ + SCAO_EVF = 98, /* Errata Version Field */ + SCAO_PVF = 99, /* Point Version Field */ + SCAO_MIVF = 101, /* Minor Version Field */ + SCAO_MAVF = 103, /* Major Version Field */ + SCAO_PCEC = 104, /* PCIe correctable error count */ + SCAO_ICS = 112, /* Incomplete shutdowns */ + SCAO_PFB = 120, /* Percent free blocks */ + SCAO_CPH = 128, /* Capacitor health */ + SCAO_NEV = 130, /* NVMe Errata Version */ + SCAO_UIO = 136, /* Unaligned I/O */ + SCAO_SVN = 144, /* Security Version Number */ + SCAO_NUSE = 152, /* NUSE - Namespace utilization */ + SCAO_PSC = 160, /* PLP start count */ + SCAO_EEST = 176, /* Endurance estimate */ + SCAO_PLRC = 192, /* PCIe Link Retraining Count */ + SCAO_LPV = 494, /* Log page version */ + SCAO_LPG = 496, /* Log page GUID */ +} SMART_CLOUD_ATTRIBUTE_OFFSETS; + +#define WDC_C2_GUID_LENGTH 16 + +static __u8 scao_guid[WDC_C2_GUID_LENGTH] = { 0xC5, 0xAF, 0x10, 0x28, 0xEA, 0xBF, 0xF2, 0xA4, + 0x9C, 0x4F, 0x6F, 0x7C, 0xC9, 0x14, 0xD5, 0xAF }; + +typedef enum +{ + EOL_RBC = 76, /* Realloc Block Count */ + EOL_ECCR = 80, /* ECC Rate */ + EOL_WRA = 84, /* Write Amp */ + EOL_PLR = 88, /* Percent Life Remaining */ + EOL_RSVBC = 92, /* Reserved Block Count */ + EOL_PFC = 96, /* Program Fail Count */ + EOL_EFC = 100, /* Erase Fail Count */ + EOL_RRER = 108, /* Raw Read Error Rate */ +} EOL_LOG_PAGE_C0_OFFSETS; + + typedef struct __attribute__((__packed__)) _WDC_DE_VU_FILE_META_DATA { __u8 fileName[WDC_DE_FILE_NAME_SIZE]; @@ -517,6 +646,12 @@ static int wdc_log_page_directory(int argc, char **argv, struct command *command static int wdc_do_drive_info(int fd, __u32 *result); static int wdc_vs_drive_info(int argc, char **argv, struct command *command, struct plugin *plugin); +static int wdc_vs_temperature_stats(int argc, char **argv, struct command *command, + struct plugin *plugin); +static __u64 wdc_get_enc_drive_capabilities(int fd); +static int wdc_enc_get_nic_log(int fd, __u8 log_id, __u32 xfer_size, __u32 data_len, FILE *out); +static int wdc_enc_submit_move_data(int fd, char *cmd, int len, int xfer_size, FILE *out, int data_id, int cdw14, int cdw15); +static bool get_dev_mgment_cbs_data(int fd, __u8 log_id, void **cbs_data); /* Drive log data size */ struct wdc_log_size { @@ -543,6 +678,14 @@ struct __attribute__((__packed__)) wdc_dui_log_section_v2 { __le64 section_size; }; +/* DUI log header V4 */ +struct wdc_dui_log_section_v4 { + __le16 section_type; + __u8 data_area_id; + __u8 reserved; + __le32 section_size_sectors; +}; + struct wdc_dui_log_hdr { __u8 telemetry_hdr[512]; __le16 hdr_version; @@ -573,6 +716,16 @@ struct __attribute__((__packed__)) wdc_dui_log_hdr_v3 { __u8 log_data[40]; }; +struct __attribute__((__packed__)) wdc_dui_log_hdr_v4 { + __u8 telemetry_hdr[512]; + __u8 hdr_version; + __u8 product_id; + __le16 section_count; + __le32 log_size_sectors; + struct wdc_dui_log_section_v4 log_section[WDC_NVME_DUI_MAX_SECTION]; + __u8 log_data[40]; +}; + /* Purge monitor response */ struct wdc_nvme_purge_monitor_data { __le16 rsvd1; @@ -706,7 +859,57 @@ struct __attribute__((__packed__)) wdc_nand_stats { __le64 nand_rec_trigger_event; __le64 e2e_error_counter; __le64 successful_ns_resize_event; - __u8 rsvd[444]; + __u8 rsvd[442]; + __u16 log_page_version; +}; + +struct __attribute__((__packed__)) wdc_nand_stats_V3 { + __u8 nand_write_tlc[16]; + __u8 nand_write_slc[16]; + __u8 bad_nand_block_count[8]; + __le64 xor_recovery_count; + __le64 uecc_read_error_count; + __u8 ssd_correction_counts[16]; + __u8 percent_life_used; + __le64 user_data_erase_counts[4]; + __u8 program_fail_count[8]; + __u8 erase_fail_count[8]; + __le64 correctable_error_count; + __u8 percent_free_blocks_user; + __le64 security_version_number; + __u8 percent_free_blocks_system; + __u8 trim_completions[25]; + __u8 back_pressure_guage; + __le64 soft_ecc_error_count; + __le64 refresh_count; + __u8 bad_sys_nand_block_count[8]; + __u8 endurance_estimate[16]; + __u8 thermal_throttling_st_ct[2]; + __le64 unaligned_IO; + __u8 physical_media_units[16]; + __u8 reserved[279]; + __u16 log_page_version; +}; + +struct wdc_vs_pcie_stats +{ + __le64 unsupportedRequestErrorCount; + __le64 ecrcErrorStatusCount; + __le64 malformedTlpStatusCount; + __le64 receiverOverflowStatusCount; + __le64 unexpectedCmpltnStatusCount; + __le64 completeAbortStatusCount; + __le64 cmpltnTimoutStatusCount; + __le64 flowControlErrorStatusCount; + __le64 poisonedTlpStatusCount; + __le64 dLinkPrtclErrorStatusCount; + __le64 advsryNFatalErrStatusCount; + __le64 replayTimerToStatusCount; + __le64 replayNumRolloverStCount; + __le64 badDllpStatusCount; + __le64 badTlpStatusCount; + __le64 receiverErrStatusCount; + __u8 reserved1[384]; }; struct wdc_fw_act_history_log_hdr { @@ -731,6 +934,32 @@ struct wdc_fw_act_history_log_entry { __u8 reserved[12]; }; +struct __attribute__((__packed__)) wdc_fw_act_history_log_entry_c2 { + __u8 entry_version_num; + __u8 entry_len; + __le16 reserved; + __le16 fw_act_hist_entries; + __le64 timestamp; + __u8 reserved2[8]; + __le64 power_cycle_count; + __le64 previous_fw_version; + __le64 current_fw_version; + __u8 slot_number; + __u8 commit_action_type; + __le16 result; + __u8 reserved3[14]; +}; + +struct __attribute__((__packed__)) wdc_fw_act_history_log_format_c2 { + __u8 log_identifier; + __u8 reserved[3]; + __le32 num_entries; + struct wdc_fw_act_history_log_entry_c2 entry[20]; + __u8 reserved2[2790]; + __le16 log_page_version; + __u8 log_page_guid[WDC_C2_GUID_LENGTH]; +}; + #define WDC_REASON_INDEX_MAX 16 #define WDC_REASON_ID_ENTRY_LEN 128 #define WDC_REASON_ID_PATH_NAME "/usr/local/nvmecli" @@ -835,15 +1064,63 @@ free_id: return ret; } +static int wdc_get_vendor_id(int fd, uint32_t *vendor_id) +{ + int ret; + struct nvme_id_ctrl ctrl; + + memset(&ctrl, 0, sizeof(struct nvme_id_ctrl)); + ret = nvme_identify_ctrl(fd, &ctrl); + if (ret) { + fprintf(stderr, "ERROR : WDC : nvme_identify_ctrl() failed " + "0x%x\n", ret); + return -1; + } + + *vendor_id = (uint32_t) ctrl.vid; + + return ret; +} + +static bool wdc_check_power_of_2(int num) +{ + return (num && ( !(num & (num-1)))); +} + +static int wdc_get_model_number(int fd, char *model) +{ + int ret,i; + struct nvme_id_ctrl ctrl; + + memset(&ctrl, 0, sizeof(struct nvme_id_ctrl)); + ret = nvme_identify_ctrl(fd, &ctrl); + if (ret) { + fprintf(stderr, "ERROR : WDC : nvme_identify_ctrl() failed " + "0x%x\n", ret); + return -1; + } + + memcpy(model,ctrl.mn,NVME_ID_CTRL_MODEL_NUMBER_SIZE); + /* get rid of the padded spaces */ + i = NVME_ID_CTRL_MODEL_NUMBER_SIZE-1; + while (model[i] == ' ') i--; + model[i+1]=0; + + return ret; +} + static bool wdc_check_device(int fd) { int ret; bool supported; - uint32_t read_device_id, read_vendor_id; + uint32_t read_device_id = -1, read_vendor_id = -1; ret = wdc_get_pci_ids(&read_device_id, &read_vendor_id); - if (ret < 0) - return false; + if (ret < 0) { + /* Use the identify nvme command to get vendor id due to NVMeOF device. */ + if (wdc_get_vendor_id(fd, &read_vendor_id) < 0) + return false; + } supported = false; @@ -858,14 +1135,46 @@ static bool wdc_check_device(int fd) return supported; } +static bool wdc_enc_check_model(int fd) +{ + int ret; + bool supported; + char model[NVME_ID_CTRL_MODEL_NUMBER_SIZE+1]; + + ret = wdc_get_model_number(fd, model); + if (ret < 0) + return false; + + supported = false; + model[NVME_ID_CTRL_MODEL_NUMBER_SIZE] = 0; /* forced termination */ + if (strstr(model,WDC_OPENFLEX_MI_DEVICE_MODEL) != NULL) + supported = true; + else + fprintf(stderr, "ERROR : WDC: unsupported WDC enclosure, Model = %s\n",model); + + return supported; +} + static __u64 wdc_get_drive_capabilities(int fd) { int ret; - uint32_t read_device_id, read_vendor_id; + uint32_t read_device_id = -1, read_vendor_id = -1; __u64 capabilities = 0; + __u8 *data; + __u32 *cust_id; ret = wdc_get_pci_ids(&read_device_id, &read_vendor_id); if (ret < 0) + { + if (wdc_get_vendor_id(fd, &read_vendor_id) < 0) + return capabilities; + } + + /* below check condition is added due in NVMeOF device we dont have device_id so we need to use only vendor_id*/ + if (read_device_id == -1 && read_vendor_id != -1) + { + capabilities = wdc_get_enc_drive_capabilities(fd); return capabilities; + } switch (read_vendor_id) { case WDC_NVME_VID: @@ -912,25 +1221,59 @@ static __u64 wdc_get_drive_capabilities(int fd) { /* FALLTHRU */ case WDC_NVME_SN640_DEV_ID_2: /* FALLTHRU */ - case WDC_NVME_SN640_DEV_ID_3: - /* FALLTHRU */ + case WDC_NVME_SN640_DEV_ID_3: + /* verify the 0xC0 log page is supported */ + if (wdc_nvme_check_supported_log_page(fd, WDC_NVME_GET_EOL_STATUS_LOG_OPCODE) == true) { + capabilities |= WDC_DRIVE_CAP_C0_LOG_PAGE; + } + + capabilities |= (WDC_DRIVE_CAP_CAP_DIAG | WDC_DRIVE_CAP_INTERNAL_LOG | + WDC_DRIVE_CAP_DRIVE_STATUS | WDC_DRIVE_CAP_CLEAR_ASSERT | + WDC_DRIVE_CAP_RESIZE | WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY | + WDC_DRVIE_CAP_DISABLE_CTLR_TELE_LOG | WDC_DRIVE_CAP_REASON_ID | + WDC_DRIVE_CAP_LOG_PAGE_DIR); + + /* verify the 0xCA log page is supported */ + if (wdc_nvme_check_supported_log_page(fd, WDC_NVME_GET_DEVICE_INFO_LOG_OPCODE) == true) + capabilities |= WDC_DRIVE_CAP_CA_LOG_PAGE; + + /* verify the 0xD0 log page is supported */ + if (wdc_nvme_check_supported_log_page(fd, WDC_NVME_GET_VU_SMART_LOG_OPCODE) == true) + capabilities |= WDC_DRIVE_CAP_D0_LOG_PAGE; + + if (!get_dev_mgment_cbs_data(fd, WDC_C2_CUSTOMER_ID_ID, (void*)&data)) { + fprintf(stderr, "%s: ERROR : WDC : 0xC2 Log Page entry ID 0x%x not found\n", __func__, WDC_C2_CUSTOMER_ID_ID); + return -1; + } + + cust_id = (__u32*)data; + + if ((*cust_id == WDC_CUSTOMER_ID_0x1004) || (*cust_id == WDC_CUSTOMER_ID_0x1008) || + (*cust_id == WDC_CUSTOMER_ID_0x1005) || (*cust_id == WDC_CUSTOMER_ID_0x1304)) + capabilities |= (WDC_DRIVE_CAP_VU_FID_CLEAR_FW_ACT_HISTORY | WDC_DRIVE_CAP_VU_FID_CLEAR_PCIE | + WDC_DRIVE_CAP_INFO | WDC_DRIVE_CAP_CLOUD_SSD_VERSION); + else + capabilities |= (WDC_DRIVE_CAP_CLEAR_FW_ACT_HISTORY | WDC_DRIVE_CAP_CLEAR_PCIE); + + break; case WDC_NVME_SN840_DEV_ID: /* FALLTHRU */ case WDC_NVME_SN840_DEV_ID_1: + /* verify the 0xC0 log page is supported */ + if (wdc_nvme_check_supported_log_page(fd, WDC_NVME_GET_EOL_STATUS_LOG_OPCODE) == true) { + capabilities |= WDC_DRIVE_CAP_C0_LOG_PAGE; + } /* FALLTHRU */ - case WDC_NVME_ZN440_DEV_ID: + case WDC_NVME_ZN540_DEV_ID: /* FALLTHRU */ - case WDC_NVME_SN440_DEV_ID: - /* FALLTHRU */ - case WDC_NVME_SN7GC_DEV_ID: - case WDC_NVME_SN7GC_DEV_ID_1: - case WDC_NVME_SN7GC_DEV_ID_2: - capabilities = (WDC_DRIVE_CAP_CAP_DIAG | WDC_DRIVE_CAP_INTERNAL_LOG | + case WDC_NVME_SN540_DEV_ID: + /* FALLTHRU */ + capabilities |= (WDC_DRIVE_CAP_CAP_DIAG | WDC_DRIVE_CAP_INTERNAL_LOG | WDC_DRIVE_CAP_DRIVE_STATUS | WDC_DRIVE_CAP_CLEAR_ASSERT | WDC_DRIVE_CAP_RESIZE | WDC_DRIVE_CAP_CLEAR_PCIE | WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY | WDC_DRIVE_CAP_CLEAR_FW_ACT_HISTORY | WDC_DRVIE_CAP_DISABLE_CTLR_TELE_LOG | WDC_DRIVE_CAP_REASON_ID | - WDC_DRIVE_CAP_LOG_PAGE_DIR | WDC_DRIVE_CAP_INFO); + WDC_DRIVE_CAP_LOG_PAGE_DIR ); /* verify the 0xCA log page is supported */ if (wdc_nvme_check_supported_log_page(fd, WDC_NVME_GET_DEVICE_INFO_LOG_OPCODE) == true) @@ -940,6 +1283,24 @@ static __u64 wdc_get_drive_capabilities(int fd) { if (wdc_nvme_check_supported_log_page(fd, WDC_NVME_GET_VU_SMART_LOG_OPCODE) == true) capabilities |= WDC_DRIVE_CAP_D0_LOG_PAGE; break; + case WDC_NVME_SN650_DEV_ID: + case WDC_NVME_SN650_DEV_ID_1: + case WDC_NVME_SN650_DEV_ID_2: + case WDC_NVME_SN650_DEV_ID_3: + /* verify the 0xC0 log page is supported */ + if (wdc_nvme_check_supported_log_page(fd, WDC_NVME_GET_EOL_STATUS_LOG_OPCODE) == true) { + capabilities |= WDC_DRIVE_CAP_C0_LOG_PAGE; + } + + capabilities |= (WDC_DRIVE_CAP_CAP_DIAG | WDC_DRIVE_CAP_INTERNAL_LOG | + WDC_DRIVE_CAP_DRIVE_STATUS | WDC_DRIVE_CAP_CLEAR_ASSERT | + WDC_DRIVE_CAP_RESIZE | WDC_DRIVE_CAP_VU_FID_CLEAR_PCIE | + WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY | WDC_DRIVE_CAP_VU_FID_CLEAR_FW_ACT_HISTORY | + WDC_DRVIE_CAP_DISABLE_CTLR_TELE_LOG | WDC_DRIVE_CAP_REASON_ID | + WDC_DRIVE_CAP_LOG_PAGE_DIR | WDC_DRIVE_CAP_INFO | + WDC_DRIVE_CAP_CLOUD_SSD_VERSION); + + break; case WDC_NVME_SN730B_DEV_ID: /* FALLTHRU */ case WDC_NVME_SN730B_DEV_ID_1: @@ -965,10 +1326,18 @@ static __u64 wdc_get_drive_capabilities(int fd) { capabilities = WDC_DRIVE_CAP_DUI_DATA | WDC_DRIVE_CAP_NAND_STATS | WDC_DRIVE_CAP_NS_RESIZE; break; case WDC_NVME_SN730A_DEV_ID: - capabilities = WDC_DRIVE_CAP_DUI_DATA | WDC_DRIVE_CAP_NAND_STATS; + capabilities = WDC_DRIVE_CAP_DUI | WDC_DRIVE_CAP_NAND_STATS | WDC_DRIVE_CAP_INFO_2 + | WDC_DRIVE_CAP_TEMP_STATS | WDC_DRIVE_CAP_VUC_CLEAR_PCIE | WDC_DRIVE_CAP_PCIE_STATS; break; case WDC_NVME_SN340_DEV_ID: - capabilities = WDC_DRIVE_CAP_SN340_DUI; + capabilities = WDC_DRIVE_CAP_DUI; + break; + case WDC_NVME_ZN350_DEV_ID: + /* FALLTHRU */ + case WDC_NVME_ZN350_DEV_ID_1: + capabilities = WDC_DRIVE_CAP_DUI_DATA | WDC_DRIVE_CAP_VU_FID_CLEAR_PCIE | WDC_DRIVE_CAP_C0_LOG_PAGE | + WDC_DRIVE_CAP_VU_FID_CLEAR_FW_ACT_HISTORY | WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY_C2 | + WDC_DRIVE_CAP_INFO | WDC_DRIVE_CAP_CLOUD_SSD_VERSION | WDC_DRIVE_CAP_LOG_PAGE_DIR; break; default: capabilities = 0; @@ -981,6 +1350,71 @@ static __u64 wdc_get_drive_capabilities(int fd) { return capabilities; } +static __u64 wdc_get_enc_drive_capabilities(int fd) { + int ret; + uint32_t read_vendor_id; + __u64 capabilities = 0; + __u8 *data; + __u32 *cust_id; + + ret = wdc_get_vendor_id(fd, &read_vendor_id); + if (ret < 0) + return capabilities; + + switch (read_vendor_id) { + case WDC_NVME_VID: + capabilities = (WDC_DRIVE_CAP_CAP_DIAG | WDC_DRIVE_CAP_INTERNAL_LOG | WDC_DRIVE_CAP_CLEAR_PCIE | + WDC_DRIVE_CAP_DRIVE_LOG | WDC_DRIVE_CAP_CRASH_DUMP | WDC_DRIVE_CAP_PFAIL_DUMP); + + /* verify the 0xCA log page is supported */ + if (wdc_nvme_check_supported_log_page(fd, WDC_NVME_GET_DEVICE_INFO_LOG_OPCODE) == true) + capabilities |= WDC_DRIVE_CAP_CA_LOG_PAGE; + + /* verify the 0xC1 log page is supported */ + if (wdc_nvme_check_supported_log_page(fd, WDC_NVME_ADD_LOG_OPCODE) == true) + capabilities |= WDC_DRIVE_CAP_C1_LOG_PAGE; + break; + case WDC_NVME_VID_2: + capabilities = (WDC_DRIVE_CAP_CAP_DIAG | WDC_DRIVE_CAP_INTERNAL_LOG | + WDC_DRIVE_CAP_DRIVE_STATUS | WDC_DRIVE_CAP_CLEAR_ASSERT | + WDC_DRIVE_CAP_RESIZE); + + /* verify the 0xCB log page is supported */ + if (wdc_nvme_check_supported_log_page(fd, WDC_NVME_GET_FW_ACT_HISTORY_LOG_ID) == true) + capabilities |= WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY; + + /* verify the 0xCA log page is supported */ + if (wdc_nvme_check_supported_log_page(fd, WDC_NVME_GET_DEVICE_INFO_LOG_OPCODE) == true) + capabilities |= WDC_DRIVE_CAP_CA_LOG_PAGE; + + /* verify the 0xD0 log page is supported */ + if (wdc_nvme_check_supported_log_page(fd, WDC_NVME_GET_VU_SMART_LOG_OPCODE) == true) + capabilities |= WDC_DRIVE_CAP_D0_LOG_PAGE; + + if (!get_dev_mgment_cbs_data(fd, WDC_C2_CUSTOMER_ID_ID, (void*)&data)) { + fprintf(stderr, "%s: ERROR : WDC : 0xC2 Log Page entry ID 0x%x not found\n", __func__, WDC_C2_CUSTOMER_ID_ID); + return -1; + } + + cust_id = (__u32*)data; + + if ((*cust_id == WDC_CUSTOMER_ID_0x1004) || (*cust_id == WDC_CUSTOMER_ID_0x1008) || + (*cust_id == WDC_CUSTOMER_ID_0x1005) || (*cust_id == WDC_CUSTOMER_ID_0x1304)) + capabilities |= (WDC_DRIVE_CAP_VU_FID_CLEAR_FW_ACT_HISTORY | WDC_DRIVE_CAP_VU_FID_CLEAR_PCIE); + else + capabilities |= (WDC_DRIVE_CAP_CLEAR_FW_ACT_HISTORY | WDC_DRIVE_CAP_CLEAR_PCIE); + + break; + case WDC_NVME_SNDK_VID: + capabilities = WDC_DRIVE_CAP_DRIVE_ESSENTIALS; + break; + default: + capabilities = 0; + } + + return capabilities; +} + static int wdc_get_serial_name(int fd, char *file, size_t len, const char *suffix) { int i; @@ -1060,6 +1494,99 @@ static int wdc_create_log_file(char *file, __u8 *drive_log_data, return 0; } +bool wdc_get_dev_mng_log_entry(__u32 log_length, + __u32 entry_id, + struct wdc_c2_log_page_header* p_log_hdr, + struct wdc_c2_log_subpage_header **p_p_found_log_entry) +{ + __u32 remaining_len = 0; + __u32 log_entry_hdr_size = sizeof(struct wdc_c2_log_subpage_header) - 1; + __u32 log_entry_size = 0; + __u32 size = 0; + bool valid_log; + __u32 current_data_offset = 0; + struct wdc_c2_log_subpage_header *p_next_log_entry = NULL; + + if (*p_p_found_log_entry == NULL) { + fprintf(stderr, "ERROR : WDC - wdc_get_dev_mng_log_entry: No ppLogEntry pointer.\n"); + return false; + } + + *p_p_found_log_entry = NULL; + + /* Ensure log data is large enough for common header */ + if (log_length < sizeof(struct wdc_c2_log_page_header)) { + fprintf(stderr, "ERROR : WDC - wdc_get_dev_mng_log_entry: \ + Buffer is not large enough for the common header. BufSize: 0x%x HdrSize: %"PRIxPTR"\n", + log_length, sizeof(struct wdc_c2_log_page_header)); + return false; + } + + /* Get pointer to first log Entry */ + size = sizeof(struct wdc_c2_log_page_header); + current_data_offset = size; + p_next_log_entry = (struct wdc_c2_log_subpage_header *)((__u8*)p_log_hdr + current_data_offset); + remaining_len = log_length - size; + valid_log = false; + + /* Walk the entire structure. Perform a sanity check to make sure this is a + standard version of the structure. This means making sure each entry looks + valid. But allow for the data to overflow the allocated + buffer (we don't want a false negative because of a FW formatting error) */ + + /* Proceed only if there is at least enough data to read an entry header */ + while (remaining_len >= log_entry_hdr_size) { + /* Get size of the next entry */ + log_entry_size = p_next_log_entry->length; + + /* If log entry size is 0 or the log entry goes past the end + of the data, we must be at the end of the data */ + if ((log_entry_size == 0) || + (log_entry_size > remaining_len)) { + fprintf(stderr, "ERROR : WDC: wdc_get_dev_mng_log_entry: \ + Detected unaligned end of the data. Data Offset: 0x%x \ + Entry Size: 0x%x, Remaining Log Length: 0x%x Entry Id: 0x%x\n", + current_data_offset, log_entry_size, remaining_len, p_next_log_entry->entry_id); + + /* Force the loop to end */ + remaining_len = 0; + } else if ((p_next_log_entry->entry_id == 0) || + (p_next_log_entry->entry_id > 200)) { + /* Invalid entry - fail the search */ + fprintf(stderr, "ERROR : WDC: wdc_get_dev_mng_log_entry: \ + Invalid entry found at offset: 0x%x Entry Size: 0x%x, \ + Remaining Log Length: 0x%x Entry Id: 0x%x\n", + current_data_offset, log_entry_size, remaining_len, p_next_log_entry->entry_id); + + /* Force the loop to end */ + remaining_len = 0; + valid_log = false; + + /* The struture is invalid, so any match that was found is invalid. */ + *p_p_found_log_entry = NULL; + } else { + /* Structure must have at least one valid entry to be considered valid */ + valid_log = true; + if (p_next_log_entry->entry_id == entry_id) { + /* A potential match. */ + *p_p_found_log_entry = p_next_log_entry; + } + + remaining_len -= log_entry_size; + + if (remaining_len > 0) { + /* Increment the offset counter */ + current_data_offset += log_entry_size; + + /* Get the next entry */ + p_next_log_entry = (struct wdc_c2_log_subpage_header *)(((__u8*)p_log_hdr) + current_data_offset); + } + } + } + + return valid_log; +} + static bool get_dev_mgment_cbs_data(int fd, __u8 log_id, void **cbs_data) { int ret = -1; @@ -1068,8 +1595,17 @@ static bool get_dev_mgment_cbs_data(int fd, __u8 log_id, void **cbs_data) struct wdc_c2_log_subpage_header *sph; __u32 length = 0; bool found = false; - + __u8 uuid_ix = 1; + __u8 lid = 0; *cbs_data = NULL; + __u32 device_id, read_vendor_id; + + ret = wdc_get_pci_ids(&device_id, &read_vendor_id); + if(device_id == WDC_NVME_ZN350_DEV_ID || device_id == WDC_NVME_ZN350_DEV_ID_1) { + lid = WDC_NVME_GET_DEV_MGMNT_LOG_PAGE_OPCODE_C8; + uuid_ix = 0; + } else + lid = WDC_NVME_GET_DEV_MGMNT_LOG_PAGE_OPCODE; if ((data = (__u8*) malloc(sizeof (__u8) * WDC_C2_LOG_BUF_LEN)) == NULL) { fprintf(stderr, "ERROR : WDC : malloc : %s\n", strerror(errno)); @@ -1078,10 +1614,9 @@ static bool get_dev_mgment_cbs_data(int fd, __u8 log_id, void **cbs_data) memset(data, 0, sizeof (__u8) * WDC_C2_LOG_BUF_LEN); /* get the log page length */ - ret = nvme_get_log(fd, 0xFFFFFFFF, WDC_NVME_GET_DEV_MGMNT_LOG_PAGE_OPCODE, - false, WDC_C2_LOG_BUF_LEN, data); + ret = nvme_get_log14(fd, 0xFFFFFFFF, lid, NVME_NO_LOG_LSP, 0, 0, false, uuid_ix, WDC_C2_LOG_BUF_LEN, data); if (ret) { - fprintf(stderr, "ERROR : WDC : Unable to get C2 Log Page length, ret = 0x%x\n", ret); + fprintf(stderr, "ERROR : WDC : Unable to get 0x%x Log Page length, ret = 0x%x\n", lid, ret); goto end; } @@ -1097,28 +1632,37 @@ static bool get_dev_mgment_cbs_data(int fd, __u8 log_id, void **cbs_data) } } - ret = nvme_get_log(fd, 0xFFFFFFFF, WDC_NVME_GET_DEV_MGMNT_LOG_PAGE_OPCODE, - false, le32_to_cpu(hdr_ptr->length), data); - /* parse the data until the List of log page ID's is found */ + /* get the log page data */ + ret = nvme_get_log14(fd, 0xFFFFFFFF, lid, NVME_NO_LOG_LSP, 0, 0, false, uuid_ix, le32_to_cpu(hdr_ptr->length), data); if (ret) { - fprintf(stderr, "ERROR : WDC : Unable to read C2 Log Page data, ret = 0x%x\n", ret); + fprintf(stderr, "ERROR : WDC : Unable to read 0x%x Log Page data, ret = 0x%x\n", lid, ret); goto end; } + /* Check the log data to see if the WD version of log page ID's is found */ + length = sizeof(struct wdc_c2_log_page_header); hdr_ptr = (struct wdc_c2_log_page_header *)data; + sph = (struct wdc_c2_log_subpage_header *)(data + length); + found = wdc_get_dev_mng_log_entry(hdr_ptr->length, log_id, hdr_ptr, &sph); - while (length < le32_to_cpu(hdr_ptr->length)) { + if (found) { + *cbs_data = (void *)&sph->data; + } else { + /* not found with uuid = 1 try with uuid = 0 */ + uuid_ix = 0; + /* get the log page data */ + ret = nvme_get_log14(fd, 0xFFFFFFFF, lid, NVME_NO_LOG_LSP, 0, 0, false, uuid_ix, le32_to_cpu(hdr_ptr->length), data); + hdr_ptr = (struct wdc_c2_log_page_header *)data; sph = (struct wdc_c2_log_subpage_header *)(data + length); - - if (le32_to_cpu(sph->entry_id) == log_id) { + found = wdc_get_dev_mng_log_entry(hdr_ptr->length, log_id, hdr_ptr, &sph); + if (found) { *cbs_data = (void *)&sph->data; - found = true; - break; + } else { + /* WD version not found */ + fprintf(stderr, "ERROR : WDC : Unable to find correct version of page 0x%x, entry id = %d\n", lid, log_id); } - length += le32_to_cpu(sph->length); } - end: free(data); return found; @@ -1270,6 +1814,7 @@ static __u32 wdc_dump_dui_data_v2(int fd, __u32 dataLen, __u64 offset, __u8 *dum { int ret; struct nvme_admin_cmd admin_cmd; + __u64 offset_lo, offset_hi; memset(&admin_cmd, 0, sizeof (struct nvme_admin_cmd)); admin_cmd.opcode = WDC_NVME_CAP_DUI_OPCODE; @@ -1277,8 +1822,11 @@ static __u32 wdc_dump_dui_data_v2(int fd, __u32 dataLen, __u64 offset, __u8 *dum admin_cmd.addr = (__u64)(uintptr_t)dump_data; admin_cmd.data_len = dataLen; admin_cmd.cdw10 = ((dataLen >> 2) - 1); - admin_cmd.cdw12 = (__u32)(offset & 0x00000000FFFFFFFF); - admin_cmd.cdw13 = (__u32)(offset >> 32); + offset_lo = offset & 0x00000000FFFFFFFF; + offset_hi = ((offset & 0xFFFFFFFF00000000) >> 32); + admin_cmd.cdw12 = (__u32)offset_lo; + admin_cmd.cdw13 = (__u32)offset_hi; + if (last_xfer) admin_cmd.cdw14 = 0; else @@ -1609,6 +2157,7 @@ static int wdc_do_cap_dui(int fd, char *file, __u32 xfer_size, int data_area, in struct wdc_dui_log_hdr_v3 *log_hdr_v3; __u32 cap_dui_length; __u64 cap_dui_length_v3; + __u64 cap_dui_length_v4; __u8 *dump_data = NULL; __u8 *buffer_addr; __s64 total_size = 0; @@ -1634,62 +2183,51 @@ static int wdc_do_cap_dui(int fd, char *file, __u32 xfer_size, int data_area, in } /* Check the Log Header version */ - if (((log_hdr->hdr_version & 0xFF) == 0x02) || - ((log_hdr->hdr_version & 0xFF) == 0x03)) { /* Process Version 2 or 3 header */ - __s64 log_size = 0; - __u64 curr_data_offset = 0; - __u64 xfer_size_long = (__u64)xfer_size; - - log_hdr_v3 = (struct wdc_dui_log_hdr_v3 *)log_hdr; + if ((log_hdr->hdr_version & 0xFF) == 0x00 || + (log_hdr->hdr_version & 0xFF) == 0x01) { + __s32 log_size = 0; + __u32 curr_data_offset = 0; - cap_dui_length_v3 = le64_to_cpu(log_hdr_v3->log_size); + cap_dui_length = le32_to_cpu(log_hdr->log_size); if (verbose) { - fprintf(stderr, "INFO : WDC : Capture V2 or V3 Device Unit Info log, data area = %d\n", data_area); - - fprintf(stderr, "INFO : WDC : DUI Header Version = 0x%x\n", log_hdr_v3->hdr_version); - if (log_hdr_v3->hdr_version >= 0x03) - fprintf(stderr, "INFO : WDC : DUI Product ID = %c\n", log_hdr_v3->product_id); + fprintf(stderr, "INFO : WDC : Capture V1 Device Unit Info log, data area = %d\n", data_area); + fprintf(stderr, "INFO : WDC : DUI Header Version = 0x%x\n", log_hdr->hdr_version); } - if (cap_dui_length_v3 == 0) { - fprintf(stderr, "INFO : WDC : Capture V2 or V3 Device Unit Info log is empty\n"); + if (cap_dui_length == 0) { + fprintf(stderr, "INFO : WDC : Capture V1 Device Unit Info log is empty\n"); } else { /* parse log header for all sections up to specified data area inclusively */ if (data_area != WDC_NVME_DUI_MAX_DATA_AREA) { - for(j = 0; j < WDC_NVME_DUI_MAX_SECTION_V3; j++) { - if (log_hdr_v3->log_section[j].data_area_id <= data_area && - log_hdr_v3->log_section[j].data_area_id != 0) { - log_size += log_hdr_v3->log_section[j].section_size; + for(j = 0; j < WDC_NVME_DUI_MAX_SECTION; j++) { + if (log_hdr->log_section[j].data_area_id <= data_area && + log_hdr->log_section[j].data_area_id != 0) { + log_size += log_hdr->log_section[j].section_size; if (verbose) - fprintf(stderr, "%s: Data area ID %d : section size 0x%x, total size = 0x%lx\n", - __func__, log_hdr_v3->log_section[j].data_area_id, (unsigned int)log_hdr_v3->log_section[j].section_size, (long unsigned int)log_size); + fprintf(stderr, "%s: Data area ID %d : section size 0x%x, total size = 0x%x\n", + __func__, log_hdr->log_section[j].data_area_id, (unsigned int)log_hdr->log_section[j].section_size, (unsigned int)log_size); + } else { if (verbose) - fprintf(stderr, "%s: break, total size = 0x%lx\n", __func__, (long unsigned int)log_size); + fprintf(stderr, "%s: break, total size = 0x%x\n", __func__, (unsigned int)log_size); break; } } } else - log_size = cap_dui_length_v3; + log_size = cap_dui_length; total_size = log_size; - if (offset >= total_size) { - fprintf(stderr, "%s: INFO : WDC : Offset 0x%"PRIx64" exceeds total size 0x%"PRIx64", no data retrieved\n", - __func__, (uint64_t)offset, (uint64_t)total_size); - goto out; - } - - dump_data = (__u8 *) malloc(sizeof (__u8) * xfer_size_long); + dump_data = (__u8 *) malloc(sizeof (__u8) * xfer_size); if (dump_data == NULL) { - fprintf(stderr, "%s: ERROR : dump data v3 malloc failed : status %s, size = 0x%lx\n", - __func__, strerror(errno), (long unsigned int)xfer_size_long); + fprintf(stderr, "%s: ERROR : dump data V1 malloc failed : status %s, size = 0x%x\n", + __func__, strerror(errno), (unsigned int)xfer_size); ret = -1; goto out; } - memset(dump_data, 0, sizeof (__u8) * xfer_size_long); + memset(dump_data, 0, sizeof (__u8) * xfer_size); output = open(file, O_WRONLY | O_CREAT | O_TRUNC, 0666); if (output < 0) { @@ -1699,36 +2237,140 @@ static int wdc_do_cap_dui(int fd, char *file, __u32 xfer_size, int data_area, in goto free_mem; } - curr_data_offset = 0; - - if (file_size != 0) { - /* Write the DUI data based on the passed in file size */ - if ((offset + file_size) > total_size) - log_size = min((total_size - offset), file_size); - else - log_size = min(total_size, file_size); - - if (verbose) - fprintf(stderr, "%s: INFO : WDC : Offset 0x%"PRIx64", file size 0x%"PRIx64", total size 0x%"PRIx64", log size 0x%"PRIx64"\n", - __func__, (uint64_t)offset, (uint64_t)file_size, (uint64_t)total_size, (uint64_t)log_size); - - curr_data_offset = offset; - + /* write the telemetry and log headers into the dump_file */ + err = write(output, (void *)log_hdr, WDC_NVME_CAP_DUI_HEADER_SIZE); + if (err != WDC_NVME_CAP_DUI_HEADER_SIZE) { + fprintf(stderr, "%s: Failed to flush header data to file!\n", __func__); + goto free_mem; } + log_size -= WDC_NVME_CAP_DUI_HEADER_SIZE; + curr_data_offset = WDC_NVME_CAP_DUI_HEADER_SIZE; i = 0; buffer_addr = dump_data; - for(; log_size > 0; log_size -= xfer_size_long) { - xfer_size_long = min(xfer_size_long, log_size); + for(; log_size > 0; log_size -= xfer_size) { + xfer_size = min(xfer_size, log_size); + + if (log_size <= xfer_size) + last_xfer = true; + + ret = wdc_dump_dui_data(fd, xfer_size, curr_data_offset, buffer_addr, last_xfer); + if (ret != 0) { + fprintf(stderr, "%s: ERROR : WDC : Get chunk %d, size = 0x%"PRIx64", offset = 0x%x, addr = %p\n", + __func__, i, (uint64_t)log_size, curr_data_offset, buffer_addr); + fprintf(stderr, "%s: ERROR : WDC : NVMe Status:%s(%x)\n", __func__, nvme_status_to_string(ret), ret); + break; + } + + /* write the dump data into the file */ + err = write(output, (void *)buffer_addr, xfer_size); + if (err != xfer_size) { + fprintf(stderr, "%s: ERROR : WDC : Failed to flush DUI data to file! chunk %d, err = 0x%x, xfer_size = 0x%x\n", + __func__, i, err, xfer_size); + goto free_mem; + } + + curr_data_offset += xfer_size; + i++; + } + } + } + else if (((log_hdr->hdr_version & 0xFF) == 0x02) || + ((log_hdr->hdr_version & 0xFF) == 0x03)) { /* Process Version 2 or 3 header */ + __s64 log_size = 0; + __u64 curr_data_offset = 0; + __u64 xfer_size_long = (__u64)xfer_size; + + log_hdr_v3 = (struct wdc_dui_log_hdr_v3 *)log_hdr; + + cap_dui_length_v3 = le64_to_cpu(log_hdr_v3->log_size); + + if (verbose) { + fprintf(stderr, "INFO : WDC : Capture V2 or V3 Device Unit Info log, data area = %d\n", data_area); + + fprintf(stderr, "INFO : WDC : DUI Header Version = 0x%x\n", log_hdr_v3->hdr_version); + if ((log_hdr->hdr_version & 0xFF) == 0x03) + fprintf(stderr, "INFO : WDC : DUI Product ID = 0x%x/%c\n", log_hdr_v3->product_id, log_hdr_v3->product_id); + } + + if (cap_dui_length_v3 == 0) { + fprintf(stderr, "INFO : WDC : Capture V2 or V3 Device Unit Info log is empty\n"); + } else { + /* parse log header for all sections up to specified data area inclusively */ + if (data_area != WDC_NVME_DUI_MAX_DATA_AREA) { + for(j = 0; j < WDC_NVME_DUI_MAX_SECTION_V3; j++) { + if (log_hdr_v3->log_section[j].data_area_id <= data_area && + log_hdr_v3->log_section[j].data_area_id != 0) { + log_size += log_hdr_v3->log_section[j].section_size; + if (verbose) + fprintf(stderr, "%s: Data area ID %d : section size 0x%x, total size = 0x%"PRIx64"\n", + __func__, log_hdr_v3->log_section[j].data_area_id, (unsigned int)log_hdr_v3->log_section[j].section_size, (uint64_t)log_size); + } + else { + if (verbose) + fprintf(stderr, "%s: break, total size = 0x%"PRIx64"\n", __func__, (uint64_t)log_size); + break; + } + } + } else + log_size = cap_dui_length_v3; + + total_size = log_size; + + if (offset >= total_size) { + fprintf(stderr, "%s: INFO : WDC : Offset 0x%"PRIx64" exceeds total size 0x%"PRIx64", no data retrieved\n", + __func__, (uint64_t)offset, (uint64_t)total_size); + goto out; + } + + dump_data = (__u8 *) malloc(sizeof (__u8) * xfer_size_long); + if (dump_data == NULL) { + fprintf(stderr, "%s: ERROR : dump data v3 malloc failed : status %s, size = 0x%"PRIx64"\n", + __func__, strerror(errno), (uint64_t)xfer_size_long); + ret = -1; + goto out; + } + memset(dump_data, 0, sizeof (__u8) * xfer_size_long); + + output = open(file, O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (output < 0) { + fprintf(stderr, "%s: Failed to open output file %s: %s!\n", + __func__, file, strerror(errno)); + ret = output; + goto free_mem; + } + + curr_data_offset = 0; + + if (file_size != 0) { + /* Write the DUI data based on the passed in file size */ + if ((offset + file_size) > total_size) + log_size = min((total_size - offset), file_size); + else + log_size = min(total_size, file_size); + + if (verbose) + fprintf(stderr, "%s: INFO : WDC : Offset 0x%"PRIx64", file size 0x%"PRIx64", total size 0x%"PRIx64", log size 0x%"PRIx64"\n", + __func__, (uint64_t)offset, (uint64_t)file_size, (uint64_t)total_size, (uint64_t)log_size); + + curr_data_offset = offset; + + } + + i = 0; + buffer_addr = dump_data; + + for(; log_size > 0; log_size -= xfer_size_long) { + xfer_size_long = min(xfer_size_long, log_size); if (log_size <= xfer_size_long) last_xfer = true; ret = wdc_dump_dui_data_v2(fd, (__u32)xfer_size_long, curr_data_offset, buffer_addr, last_xfer); if (ret != 0) { - fprintf(stderr, "%s: ERROR : WDC : Get chunk %d, size = 0x%lx, offset = 0x%lx, addr = 0x%lx\n", - __func__, i, (long unsigned int)total_size, (long unsigned int)curr_data_offset, (long unsigned int)buffer_addr); + fprintf(stderr, "%s: ERROR : WDC : Get chunk %d, size = 0x%"PRIx64", offset = 0x%"PRIx64", addr = %p\n", + __func__, i, (uint64_t)total_size, (uint64_t)curr_data_offset, buffer_addr); fprintf(stderr, "%s: ERROR : WDC : NVMe Status:%s(%x)\n", __func__, nvme_status_to_string(ret), ret); break; } @@ -1736,8 +2378,8 @@ static int wdc_do_cap_dui(int fd, char *file, __u32 xfer_size, int data_area, in /* write the dump data into the file */ err = write(output, (void *)buffer_addr, xfer_size_long); if (err != xfer_size_long) { - fprintf(stderr, "%s: ERROR : WDC : Failed to flush DUI data to file! chunk %d, err = 0x%x, xfer_size = 0x%lx\n", - __func__, i, err, (long unsigned int)xfer_size_long); + fprintf(stderr, "%s: ERROR : WDC : Failed to flush DUI data to file! chunk %d, err = 0x%x, xfer_size = 0x%"PRIx64"\n", + __func__, i, err, (uint64_t)xfer_size_long); goto free_mem; } @@ -1745,50 +2387,65 @@ static int wdc_do_cap_dui(int fd, char *file, __u32 xfer_size, int data_area, in i++; } } - } else { - __s32 log_size = 0; - __u32 curr_data_offset = 0; + } + else if ((log_hdr->hdr_version & 0xFF) == 0x04) { + __s64 log_size = 0; + __u64 curr_data_offset = 0; + struct wdc_dui_log_hdr_v4 *log_hdr_v4; + log_hdr_v4 = (struct wdc_dui_log_hdr_v4 *)log_hdr; + __s64 xfer_size_long = (__s64)xfer_size; + __s64 section_size_bytes = 0; - cap_dui_length = le32_to_cpu(log_hdr->log_size); + cap_dui_length_v4 = le64_to_cpu(log_hdr_v4->log_size_sectors) * WDC_NVME_SN730_SECTOR_SIZE; if (verbose) { - fprintf(stderr, "INFO : WDC : Capture V1 Device Unit Info log, data area = %d\n", data_area); - fprintf(stderr, "INFO : WDC : DUI Header Version = 0x%x\n", log_hdr->hdr_version); + fprintf(stderr, "INFO : WDC : Capture V4 Device Unit Info log, data area = %d\n", data_area); + fprintf(stderr, "INFO : WDC : DUI Header Version = 0x%x\n", log_hdr_v4->hdr_version); + fprintf(stderr, "INFO : WDC : DUI Product ID = 0x%x/%c\n", log_hdr_v4->product_id, log_hdr_v4->product_id); + fprintf(stderr, "INFO : WDC : DUI log size sectors = 0x%x\n", log_hdr_v4->log_size_sectors); + fprintf(stderr, "INFO : WDC : DUI cap_dui_length = 0x%"PRIx64"\n", (uint64_t)cap_dui_length_v4); } - if (cap_dui_length == 0) { - fprintf(stderr, "INFO : WDC : Capture V1 Device Unit Info log is empty\n"); + if (cap_dui_length_v4 == 0) { + fprintf(stderr, "INFO : WDC : Capture V4 Device Unit Info log is empty\n"); } else { /* parse log header for all sections up to specified data area inclusively */ if (data_area != WDC_NVME_DUI_MAX_DATA_AREA) { for(j = 0; j < WDC_NVME_DUI_MAX_SECTION; j++) { - if (log_hdr->log_section[j].data_area_id <= data_area && - log_hdr->log_section[j].data_area_id != 0) { - log_size += log_hdr->log_section[j].section_size; + if (log_hdr_v4->log_section[j].data_area_id <= data_area && + log_hdr_v4->log_section[j].data_area_id != 0) { + section_size_bytes = ((__s64)log_hdr_v4->log_section[j].section_size_sectors * WDC_NVME_SN730_SECTOR_SIZE); + log_size += section_size_bytes; if (verbose) - fprintf(stderr, "%s: Data area ID %d : section size 0x%x, total size = 0x%x\n", - __func__, log_hdr->log_section[j].data_area_id, (unsigned int)log_hdr->log_section[j].section_size, (unsigned int)log_size); - + fprintf(stderr, "%s: Data area ID %d : section size 0x%x sectors, section size 0x%"PRIx64" bytes, total size = 0x%"PRIx64"\n", + __func__, log_hdr_v4->log_section[j].data_area_id, log_hdr_v4->log_section[j].section_size_sectors, (uint64_t)section_size_bytes, + (uint64_t)log_size); } else { if (verbose) - fprintf(stderr, "%s: break, total size = 0x%x\n", __func__, (unsigned int)log_size); + fprintf(stderr, "%s: break, total size = 0x%"PRIx64"\n", __func__, (uint64_t)log_size); break; } } } else - log_size = cap_dui_length; + log_size = cap_dui_length_v4; total_size = log_size; - dump_data = (__u8 *) malloc(sizeof (__u8) * xfer_size); + if (offset >= total_size) { + fprintf(stderr, "%s: INFO : WDC : Offset 0x%"PRIx64" exceeds total size 0x%"PRIx64", no data retrieved\n", + __func__, (uint64_t)offset, (uint64_t)total_size); + goto out; + } + + dump_data = (__u8 *) malloc(sizeof (__u8) * xfer_size_long); if (dump_data == NULL) { - fprintf(stderr, "%s: ERROR : dump data V1 malloc failed : status %s, size = 0x%x\n", - __func__, strerror(errno), (unsigned int)xfer_size); + fprintf(stderr, "%s: ERROR : dump data V4 malloc failed : status %s, size = 0x%x\n", + __func__, strerror(errno), (unsigned int)xfer_size_long); ret = -1; goto out; } - memset(dump_data, 0, sizeof (__u8) * xfer_size); + memset(dump_data, 0, sizeof (__u8) * xfer_size_long); output = open(file, O_WRONLY | O_CREAT | O_TRUNC, 0666); if (output < 0) { @@ -1798,49 +2455,62 @@ static int wdc_do_cap_dui(int fd, char *file, __u32 xfer_size, int data_area, in goto free_mem; } - /* write the telemetry and log headers into the dump_file */ - err = write(output, (void *)log_hdr, WDC_NVME_CAP_DUI_HEADER_SIZE); - if (err != WDC_NVME_CAP_DUI_HEADER_SIZE) { - fprintf(stderr, "%s: Failed to flush header data to file!\n", __func__); - goto free_mem; + curr_data_offset = 0; + + if (file_size != 0) { + /* Write the DUI data based on the passed in file size */ + if ((offset + file_size) > total_size) + log_size = min((total_size - offset), file_size); + else + log_size = min(total_size, file_size); + + if (verbose) + fprintf(stderr, "%s: INFO : WDC : Offset 0x%"PRIx64", file size 0x%"PRIx64", total size 0x%"PRIx64", log size 0x%"PRIx64"\n", + __func__, (uint64_t)offset, (uint64_t)file_size, (uint64_t)total_size, (uint64_t)log_size); + + curr_data_offset = offset; + } - log_size -= WDC_NVME_CAP_DUI_HEADER_SIZE; - curr_data_offset = WDC_NVME_CAP_DUI_HEADER_SIZE; i = 0; buffer_addr = dump_data; - for(; log_size > 0; log_size -= xfer_size) { - xfer_size = min(xfer_size, log_size); + for(; log_size > 0; log_size -= xfer_size_long) { + xfer_size_long = min(xfer_size_long, log_size); - if (log_size <= xfer_size) + if (log_size <= xfer_size_long) last_xfer = true; - ret = wdc_dump_dui_data(fd, xfer_size, curr_data_offset, buffer_addr, last_xfer); + ret = wdc_dump_dui_data_v2(fd, (__u32)xfer_size_long, curr_data_offset, buffer_addr, last_xfer); if (ret != 0) { - fprintf(stderr, "%s: ERROR : WDC : Get chunk %d, size = 0x%lx, offset = 0x%x, addr = %p\n", - __func__, i, (long unsigned int)log_size, curr_data_offset, buffer_addr); + fprintf(stderr, "%s: ERROR : WDC : Get chunk %d, size = 0x%"PRIx64", offset = 0x%"PRIx64", addr = %p\n", + __func__, i, (uint64_t)log_size, (uint64_t)curr_data_offset, buffer_addr); fprintf(stderr, "%s: ERROR : WDC : NVMe Status:%s(%x)\n", __func__, nvme_status_to_string(ret), ret); break; } /* write the dump data into the file */ - err = write(output, (void *)buffer_addr, xfer_size); - if (err != xfer_size) { - fprintf(stderr, "%s: ERROR : WDC : Failed to flush DUI data to file! chunk %d, err = 0x%x, xfer_size = 0x%x\n", - __func__, i, err, xfer_size); + err = write(output, (void *)buffer_addr, xfer_size_long); + if (err != xfer_size_long) { + fprintf(stderr, "%s: ERROR : WDC : Failed to flush DUI data to file! chunk %d, err = 0x%x, xfer_size_long = 0x%"PRIx64"\n", + __func__, i, err, (uint64_t)xfer_size_long); goto free_mem; } - curr_data_offset += xfer_size; + curr_data_offset += xfer_size_long; i++; } } } + else { + fprintf(stderr, "INFO : WDC : Unsupported header version = 0x%x\n", log_hdr->hdr_version); + goto out; + } + fprintf(stderr, "%s: NVMe Status:%s(%x)\n", __func__, nvme_status_to_string(ret), ret); if (verbose) - fprintf(stderr, "INFO : WDC : Capture Device Unit Info log, length = 0x%lx\n", (long unsigned int)total_size); + fprintf(stderr, "INFO : WDC : Capture Device Unit Info log, length = 0x%"PRIx64"\n", (uint64_t)total_size); free_mem: close(output); @@ -2137,7 +2807,7 @@ static int wdc_vs_internal_fw_log(int argc, char **argv, struct command *command char *desc = "Internal Firmware Log."; char *file = "Output file pathname."; char *size = "Data retrieval transfer size."; - char *data_area = "Data area to retrieve up to. Currently only supported on the SN340, SN640, and SN840 devices."; + char *data_area = "Data area to retrieve up to. Currently only supported on the SN340, SN640, SN730, and SN840 devices."; char *file_size = "Output file size. Currently only supported on the SN340 device."; char *offset = "Output file data offset. Currently only supported on the SN340 device."; char *type = "Telemetry type - NONE, HOST, or CONTROLLER. Currently only supported on the SN640 and SN840 devices."; @@ -2164,7 +2834,7 @@ static int wdc_vs_internal_fw_log(int argc, char **argv, struct command *command struct config cfg = { .file = NULL, .xfer_size = 0x10000, - .data_area = 3, + .data_area = 0, .file_size = 0, .offset = 0, .type = NULL, @@ -2224,13 +2894,18 @@ static int wdc_vs_internal_fw_log(int argc, char **argv, struct command *command snprintf(f + strlen(f), PATH_MAX, "%s", ".bin"); fprintf(stderr, "%s: filename = %s\n", __func__, f); - if (cfg.data_area > 5 || cfg.data_area == 0) { - fprintf(stderr, "ERROR : WDC: Data area must be 1-5\n"); - return -1; + if (cfg.data_area) { + if (cfg.data_area > 5 || cfg.data_area < 1) { + fprintf(stderr, "ERROR : WDC: Data area must be 1-5\n"); + return -1; + } } capabilities = wdc_get_drive_capabilities(fd); if ((capabilities & WDC_DRIVE_CAP_INTERNAL_LOG) == WDC_DRIVE_CAP_INTERNAL_LOG) { + if (cfg.data_area == 0) + cfg.data_area = 3; /* Set the default DA to 3 if not specified */ + if ((cfg.type == NULL) || (!strcmp(cfg.type, "NONE")) || (!strcmp(cfg.type, "none"))) { @@ -2251,7 +2926,11 @@ static int wdc_vs_internal_fw_log(int argc, char **argv, struct command *command return wdc_do_cap_diag(fd, f, xfer_size, telemetry_type, telemetry_data_area); } - if ((capabilities & WDC_DRIVE_CAP_SN340_DUI) == WDC_DRIVE_CAP_SN340_DUI) { + if ((capabilities & WDC_DRIVE_CAP_DUI) == WDC_DRIVE_CAP_DUI) { + if (cfg.data_area == 0) { + cfg.data_area = 1; + } + /* FW requirement - xfer size must be 256k for data area 4 */ if (cfg.data_area >= 4) xfer_size = 0x40000; @@ -2924,119 +3603,112 @@ static void wdc_print_bd_ca_log_normal(void *data) { struct wdc_bd_ca_log_format *bd_data = (struct wdc_bd_ca_log_format *)data; __u64 *raw; - __u16 *word_raw; + __u16 *word_raw1, *word_raw2, *word_raw3; __u32 *dword_raw; __u8 *byte_raw; if (bd_data->field_id == 0x00) { raw = (__u64*)bd_data->raw_value; - printf(" CA Log Page values :- \n"); - printf(" Program fail counts %20"PRIu64"\n", - le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); - printf(" %% Remaining of allowable program fails %3"PRIu8"\n", - bd_data->normalized_value); + printf("Additional Smart Log for NVME device:%s namespace-id:%x\n", + devicename, WDC_DE_GLOBAL_NSID); + printf("key normalized raw\n"); + printf("program_fail_count : %3"PRIu8"%% %"PRIu64"\n", + bd_data->normalized_value, le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); } else { goto invalid_id; } bd_data++; if (bd_data->field_id == 0x01) { raw = (__u64*)bd_data->raw_value; - printf(" Erase fail count %20"PRIu64"\n", - le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); - printf(" %% Remaining of allowable erase fails %3"PRIu8"\n", - bd_data->normalized_value); + printf("erase_fail_count : %3"PRIu8"%% %"PRIu64"\n", + bd_data->normalized_value, le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); } else { goto invalid_id; } bd_data++; if (bd_data->field_id == 0x02) { - word_raw = (__u16*)bd_data->raw_value; - printf(" Min erase cycles %10"PRIu16"\n", - le16_to_cpu(*word_raw)); - word_raw = (__u16*)&bd_data->raw_value[2]; - printf(" Max erase cycles %10"PRIu16"\n", - le16_to_cpu(*word_raw)); - word_raw = (__u16*)&bd_data->raw_value[4]; - printf(" Ave erase cycles %10"PRIu16"\n", - le16_to_cpu(*word_raw)); - printf(" Wear Leveling Normalized %3"PRIu8"\n", - bd_data->normalized_value); - + word_raw1 = (__u16*)bd_data->raw_value; + word_raw2 = (__u16*)&bd_data->raw_value[2]; + word_raw3 = (__u16*)&bd_data->raw_value[4]; + printf("wear_leveling : %3"PRIu8"%% min: %"PRIu16", max: %"PRIu16", avg: %"PRIu16"\n", + bd_data->normalized_value, + le16_to_cpu(*word_raw1), + le16_to_cpu(*word_raw2), + le16_to_cpu(*word_raw3)); } else { goto invalid_id; } bd_data++; if (bd_data->field_id == 0x03) { raw = (__u64*)bd_data->raw_value; - printf(" End to end error detection count %20"PRIu64"\n", - le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); + printf("end_to_end_error_detection_count: %3"PRIu8"%% %"PRIu64"\n", + bd_data->normalized_value, le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); } else { goto invalid_id; } bd_data++; if (bd_data->field_id == 0x04) { raw = (__u64*)bd_data->raw_value; - printf(" Crc error count %20"PRIu64"\n", - le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); + printf("crc_error_count : %3"PRIu8"%% %"PRIu64"\n", + bd_data->normalized_value, le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); } else { goto invalid_id; } bd_data++; if (bd_data->field_id == 0x05) { raw = (__u64*)bd_data->raw_value; - printf(" Timed workload media error %20.3f\n", - safe_div_fp((*raw & 0x00FFFFFFFFFFFFFF), 1024.0)); + printf("timed_workload_media_wear : %3"PRIu8"%% %-.3f%%\n", + bd_data->normalized_value, + safe_div_fp((*raw & 0x00FFFFFFFFFFFFFF), 1024.0)); } else { goto invalid_id; } bd_data++; if (bd_data->field_id == 0x06) { raw = (__u64*)bd_data->raw_value; - printf(" Timed workload host reads %% %3"PRIu64"\n", - le64_to_cpu(*raw & 0x00000000000000FF)); + printf("timed_workload_host_reads : %3"PRIu8"%% %"PRIu64"%%\n", + bd_data->normalized_value, le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); } else { goto invalid_id; } bd_data++; if (bd_data->field_id == 0x07) { raw = (__u64*)bd_data->raw_value; - printf(" Timed workload timer %20"PRIu64"\n", - le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); + printf("timed_workload_timer : %3"PRIu8"%% %"PRIu64"\n", + bd_data->normalized_value, le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); } else { goto invalid_id; } bd_data++; if (bd_data->field_id == 0x08) { byte_raw = (__u8*)bd_data->raw_value; - printf(" Throttle status %% %10"PRIu16"\n", - *byte_raw); dword_raw = (__u32*)&bd_data->raw_value[1]; - printf(" Throttling event counter %10"PRIu16"\n", - le32_to_cpu(*dword_raw)); + printf("thermal_throttle_status : %3"PRIu8"%% %"PRIu16"%%, cnt: %"PRIu16"\n", + bd_data->normalized_value, *byte_raw, le32_to_cpu(*dword_raw)); } else { goto invalid_id; } bd_data++; if (bd_data->field_id == 0x09) { raw = (__u64*)bd_data->raw_value; - printf(" Retry buffer overflow count %20"PRIu64"\n", - le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); + printf("retry_buffer_overflow_count : %3"PRIu8"%% %"PRIu64"\n", + bd_data->normalized_value, le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); } else { goto invalid_id; } bd_data++; if (bd_data->field_id == 0x0A) { raw = (__u64*)bd_data->raw_value; - printf(" Pll lock loss count %20"PRIu64"\n", - le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); + printf("pll_lock_loss_count : %3"PRIu8"%% %"PRIu64"\n", + bd_data->normalized_value, le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); } else { goto invalid_id; } bd_data++; if (bd_data->field_id == 0x0B) { raw = (__u64*)bd_data->raw_value; - printf(" Nand bytes written (32mb) %20.0f\n", - safe_div_fp((*raw & 0x00FFFFFFFFFFFFFF), 0xFFFF)); + printf("nand_bytes_written : %3"PRIu8"%% sectors: %.f\n", + bd_data->normalized_value, safe_div_fp((*raw & 0x00FFFFFFFFFFFFFF), 0xFFFF)); raw = (__u64*)bd_data->raw_value; } else { goto invalid_id; @@ -3044,8 +3716,8 @@ static void wdc_print_bd_ca_log_normal(void *data) bd_data++; if (bd_data->field_id == 0x0C) { raw = (__u64*)bd_data->raw_value; - printf(" Host bytes written (32mb) %20.0f\n", - safe_div_fp((*raw & 0x00FFFFFFFFFFFFFF), 0xFFFF)); + printf("host_bytes_written : %3"PRIu8"%% sectors: %.f\n", + bd_data->normalized_value, safe_div_fp((*raw & 0x00FFFFFFFFFFFFFF), 0xFFFF)); raw = (__u64*)bd_data->raw_value; } else { goto invalid_id; @@ -3073,9 +3745,9 @@ static void wdc_print_bd_ca_log_json(void *data) root = json_create_object(); if (bd_data->field_id == 0x00) { raw = (__u64*)bd_data->raw_value; - json_object_add_value_int(root, "Program fail counts", + json_object_add_value_int(root, "program_fail_count", le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); - json_object_add_value_int(root, "% Remaining of allowable program fails", + json_object_add_value_int(root, "normalized", bd_data->normalized_value); } else { goto invalid_id; @@ -3083,9 +3755,9 @@ static void wdc_print_bd_ca_log_json(void *data) bd_data++; if (bd_data->field_id == 0x01) { raw = (__u64*)bd_data->raw_value; - json_object_add_value_int(root, "Erase fail count", + json_object_add_value_int(root, "erase_fail_count", le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); - json_object_add_value_int(root, "% Remaining of allowable erase fails", + json_object_add_value_int(root, "normalized", bd_data->normalized_value); } else { goto invalid_id; @@ -3093,19 +3765,19 @@ static void wdc_print_bd_ca_log_json(void *data) bd_data++; if (bd_data->field_id == 0x02) { word_raw = (__u16*)bd_data->raw_value; - json_object_add_value_int(root, "Min erase cycles", le16_to_cpu(*word_raw)); + json_object_add_value_int(root, "min", le16_to_cpu(*word_raw)); word_raw = (__u16*)&bd_data->raw_value[2]; - json_object_add_value_int(root, "Max erase cycles", le16_to_cpu(*word_raw)); + json_object_add_value_int(root, "max", le16_to_cpu(*word_raw)); word_raw = (__u16*)&bd_data->raw_value[4]; - json_object_add_value_int(root, "Ave erase cycles", le16_to_cpu(*word_raw)); - json_object_add_value_int(root, "Wear Leveling Normalized", bd_data->normalized_value); + json_object_add_value_int(root, "avg", le16_to_cpu(*word_raw)); + json_object_add_value_int(root, "wear_leveling-normalized", bd_data->normalized_value); } else { goto invalid_id; } bd_data++; if (bd_data->field_id == 0x03) { raw = (__u64*)bd_data->raw_value; - json_object_add_value_int(root, "End to end error detection count", + json_object_add_value_int(root, "end_to_end_error_detection_count", le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); } else { goto invalid_id; @@ -3113,7 +3785,7 @@ static void wdc_print_bd_ca_log_json(void *data) bd_data++; if (bd_data->field_id == 0x04) { raw = (__u64*)bd_data->raw_value; - json_object_add_value_int(root, "Crc error count", + json_object_add_value_int(root, "crc_error_count", le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); } else { goto invalid_id; @@ -3121,7 +3793,7 @@ static void wdc_print_bd_ca_log_json(void *data) bd_data++; if (bd_data->field_id == 0x05) { raw = (__u64*)bd_data->raw_value; - json_object_add_value_float(root, "Timed workload media error", + json_object_add_value_float(root, "timed_workload_media_wear", safe_div_fp((*raw & 0x00FFFFFFFFFFFFFF), 1024.0)); } else { goto invalid_id; @@ -3129,7 +3801,7 @@ static void wdc_print_bd_ca_log_json(void *data) bd_data++; if (bd_data->field_id == 0x06) { raw = (__u64*)bd_data->raw_value; - json_object_add_value_int(root, "Timed workload host reads %", + json_object_add_value_int(root, "timed_workload_host_reads", le64_to_cpu(*raw & 0x00000000000000FF)); } else { goto invalid_id; @@ -3137,7 +3809,7 @@ static void wdc_print_bd_ca_log_json(void *data) bd_data++; if (bd_data->field_id == 0x07) { raw = (__u64*)bd_data->raw_value; - json_object_add_value_int(root, "Timed workload timer", + json_object_add_value_int(root, "timed_workload_timer", le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); } else { goto invalid_id; @@ -3145,16 +3817,16 @@ static void wdc_print_bd_ca_log_json(void *data) bd_data++; if (bd_data->field_id == 0x08) { byte_raw = (__u8*)bd_data->raw_value; - json_object_add_value_int(root, "Throttle status %", *byte_raw); + json_object_add_value_int(root, "thermal_throttle_status", *byte_raw); dword_raw = (__u32*)&bd_data->raw_value[1]; - json_object_add_value_int(root, "Throttling event counter", le32_to_cpu(*dword_raw)); + json_object_add_value_int(root, "cnt", le32_to_cpu(*dword_raw)); } else { goto invalid_id; } bd_data++; if (bd_data->field_id == 0x09) { raw = (__u64*)bd_data->raw_value; - json_object_add_value_int(root, "Retry buffer overflow count", + json_object_add_value_int(root, "retry_buffer_overflow_count", le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); } else { goto invalid_id; @@ -3162,7 +3834,7 @@ static void wdc_print_bd_ca_log_json(void *data) bd_data++; if (bd_data->field_id == 0x0A) { raw = (__u64*)bd_data->raw_value; - json_object_add_value_int(root, "Pll lock loss count", + json_object_add_value_int(root, "pll_lock_loss_count", le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); } else { goto invalid_id; @@ -3170,7 +3842,7 @@ static void wdc_print_bd_ca_log_json(void *data) bd_data++; if (bd_data->field_id == 0x0B) { raw = (__u64*)bd_data->raw_value; - json_object_add_value_float(root, "Nand bytes written (32mb)", + json_object_add_value_float(root, "nand_bytes_written", safe_div_fp((*raw & 0x00FFFFFFFFFFFFFF), 0xFFFF)); } else { goto invalid_id; @@ -3178,7 +3850,7 @@ static void wdc_print_bd_ca_log_json(void *data) bd_data++; if (bd_data->field_id == 0x0C) { raw = (__u64*)bd_data->raw_value; - json_object_add_value_float(root, "Host bytes written (32mb)", + json_object_add_value_float(root, "host_bytes_written", safe_div_fp((*raw & 0x00FFFFFFFFFFFFFF), 0xFFFF)); raw = (__u64*)bd_data->raw_value; } else { @@ -3334,61 +4006,162 @@ static void wdc_get_commit_action_bin(__u8 commit_action_type, char *action_bin) } -static void wdc_print_fw_act_history_log_normal(struct wdc_fw_act_history_log_entry *fw_act_history_entry, - int num_entries) +static void wdc_print_fw_act_history_log_normal(__u8 *data, int num_entries, __u32 cust_id) { int i; char previous_fw[9]; char new_fw[9]; char commit_action_bin[8]; - memset((void *)previous_fw, 0, 9); - memset((void *)new_fw, 0, 9); - memset((void *)commit_action_bin, 0, 8); + char time_str[11]; + __u16 oldestEntryIdx = 0, entryIdx = 0; char *null_fw = "--------"; - - - printf(" Firmware Activate History Log \n"); - printf(" Power on Hour Power Cycle Previous New \n"); - printf(" Entry hh:mm:ss Count Firmware Firmware Slot Action Result \n"); - printf(" ----- -------------- ------------ ---------- ---------- ----- ------ -------\n"); - - for (i = 0; i < num_entries; i++) { - memcpy(previous_fw, (char *)&(fw_act_history_entry->previous_fw_version), 8); - if (strlen((char *)&(fw_act_history_entry->new_fw_version)) > 1) - memcpy(new_fw, (char *)&(fw_act_history_entry->new_fw_version), 8); - else - memcpy(new_fw, null_fw, 8); - - printf("%5"PRIu32"", (uint32_t)le32_to_cpu(fw_act_history_entry->entry_num)); - printf(" "); - printf("%02d:%02d:%02d", (int)(le64_to_cpu(fw_act_history_entry->power_on_seconds)/3600), - (int)((le64_to_cpu(fw_act_history_entry->power_on_seconds)%3600)/60), - (int)(le64_to_cpu(fw_act_history_entry->power_on_seconds)%60)); - printf(" "); - printf("%8"PRIu32"", (uint32_t)le32_to_cpu(fw_act_history_entry->power_cycle_count)); - printf(" "); - printf("%s", (char *)previous_fw); - printf(" "); - printf("%s", (char *)new_fw); - printf(" "); - printf("%2"PRIu8"", (uint8_t)fw_act_history_entry->slot_number); - printf(" "); - wdc_get_commit_action_bin(fw_act_history_entry->commit_action_type,(char *)&commit_action_bin); - printf(" %s", (char *)commit_action_bin); - printf(" "); - if (le16_to_cpu(fw_act_history_entry->result) == 0) - printf("pass"); + memset((void *)time_str, 0, 11); + + if (data[0] == WDC_NVME_GET_FW_ACT_HISTORY_C2_LOG_ID) { + printf(" Firmware Activate History Log \n"); + if (cust_id == WDC_CUSTOMER_ID_0x1005) { + printf(" Power on Hour Power Cycle Previous New \n"); + printf(" Entry hh:mm:ss Count Firmware Firmware Slot Action Result \n"); + printf(" ----- ----------------- ----------------- --------- --------- ----- ------ -------\n"); + } else { + printf(" Power Cycle Previous New \n"); + printf(" Entry Timestamp Count Firmware Firmware Slot Action Result \n"); + printf(" ----- ----------------- ----------------- --------- --------- ----- ------ -------\n"); + } + struct wdc_fw_act_history_log_format_c2 *fw_act_history_entry = (struct wdc_fw_act_history_log_format_c2 *)(data); + + if (num_entries == WDC_MAX_NUM_ACT_HIST_ENTRIES) { + /* find lowest/oldest entry */ + for (i = 0; i < num_entries; i++) { + if (le16_to_cpu(fw_act_history_entry->entry[i].fw_act_hist_entries) > + le16_to_cpu(fw_act_history_entry->entry[i+1].fw_act_hist_entries)) { + oldestEntryIdx = i+1; + break; + } + } + } + if (oldestEntryIdx == WDC_MAX_NUM_ACT_HIST_ENTRIES) + entryIdx = 0; else - printf("fail #%d", (uint16_t)le16_to_cpu(fw_act_history_entry->result)); + entryIdx = oldestEntryIdx; + + for (i = 0; i < num_entries; i++) { + memset((void *)previous_fw, 0, 9); + memset((void *)new_fw, 0, 9); + memset((void *)commit_action_bin, 0, 8); + + memcpy(previous_fw, (char *)&(fw_act_history_entry->entry[entryIdx].previous_fw_version), 8); + if (strlen((char *)&(fw_act_history_entry->entry[entryIdx].current_fw_version)) > 1) + memcpy(new_fw, (char *)&(fw_act_history_entry->entry[entryIdx].current_fw_version), 8); + else + memcpy(new_fw, null_fw, 8); + + printf("%5"PRIu16"", (uint16_t)le16_to_cpu(fw_act_history_entry->entry[entryIdx].fw_act_hist_entries)); + if (cust_id == WDC_CUSTOMER_ID_0x1005) { + printf(" "); + memset((void *)time_str, 0, 9); + sprintf((char *)time_str, "%04d:%02d:%02d", (int)(le64_to_cpu(fw_act_history_entry->entry[entryIdx].timestamp)/3600), + (int)((le64_to_cpu(fw_act_history_entry->entry[entryIdx].timestamp%3600)/60)), + (int)(le64_to_cpu(fw_act_history_entry->entry[entryIdx].timestamp%60))); + + printf("%s", time_str); + printf(" "); + } else { + printf(" "); + uint64_t timestamp = (0x0000FFFFFFFFFFFF & le64_to_cpu(fw_act_history_entry->entry[entryIdx].timestamp)); + printf("%16"PRIu64"", timestamp); + printf(" "); + } - printf("\n"); + printf("%16"PRIu64"", (uint64_t)le64_to_cpu(fw_act_history_entry->entry[entryIdx].power_cycle_count)); + printf(" "); + printf("%s", (char *)previous_fw); + printf(" "); + printf("%s", (char *)new_fw); + printf(" "); + printf("%2"PRIu8"", (uint8_t)fw_act_history_entry->entry[entryIdx].slot_number); + printf(" "); + wdc_get_commit_action_bin(fw_act_history_entry->entry[entryIdx].commit_action_type,(char *)&commit_action_bin); + printf(" %s", (char *)commit_action_bin); + printf(" "); + if (le16_to_cpu(fw_act_history_entry->entry[entryIdx].result) == 0) + printf("pass"); + else + printf("fail #%d", (uint16_t)le16_to_cpu(fw_act_history_entry->entry[entryIdx].result)); + printf("\n"); + + entryIdx++; + if (entryIdx >= WDC_MAX_NUM_ACT_HIST_ENTRIES) + entryIdx = 0; + } + } + else + { + printf(" Firmware Activate History Log \n"); + printf(" Power on Hour Power Cycle Previous New \n"); + printf(" Entry hh:mm:ss Count Firmware Firmware Slot Action Result \n"); + printf(" ----- -------------- -------------------- ---------- ---------- ----- ------ -------\n"); + + struct wdc_fw_act_history_log_entry *fw_act_history_entry = (struct wdc_fw_act_history_log_entry *)(data + sizeof(struct wdc_fw_act_history_log_hdr)); + + if (num_entries == WDC_MAX_NUM_ACT_HIST_ENTRIES) { + /* find lowest/oldest entry */ + for (i = 0; i < num_entries; i++) { + if (le32_to_cpu(fw_act_history_entry[i].entry_num) > le32_to_cpu(fw_act_history_entry[i+1].entry_num)) { + oldestEntryIdx = i+1; + break; + } + } + } - fw_act_history_entry++; + if (oldestEntryIdx == WDC_MAX_NUM_ACT_HIST_ENTRIES) + entryIdx = 0; + else + entryIdx = oldestEntryIdx; + + for (i = 0; i < num_entries; i++) { + memset((void *)previous_fw, 0, 9); + memset((void *)new_fw, 0, 9); + memset((void *)commit_action_bin, 0, 8); + + memcpy(previous_fw, (char *)&(fw_act_history_entry[entryIdx].previous_fw_version), 8); + if (strlen((char *)&(fw_act_history_entry[entryIdx].new_fw_version)) > 1) + memcpy(new_fw, (char *)&(fw_act_history_entry[entryIdx].new_fw_version), 8); + else + memcpy(new_fw, null_fw, 8); + + printf("%5"PRIu32"", (uint32_t)le32_to_cpu(fw_act_history_entry[entryIdx].entry_num)); + printf(" "); + printf("%04d:%02d:%02d", (int)(le64_to_cpu(fw_act_history_entry[entryIdx].power_on_seconds)/3600), + (int)((le64_to_cpu(fw_act_history_entry[entryIdx].power_on_seconds)%3600)/60), + (int)(le64_to_cpu(fw_act_history_entry[entryIdx].power_on_seconds)%60)); + printf(" "); + printf("%16"PRIu32"", (uint32_t)le32_to_cpu(fw_act_history_entry[entryIdx].power_cycle_count)); + printf(" "); + printf("%s", (char *)previous_fw); + printf(" "); + printf("%s", (char *)new_fw); + printf(" "); + printf("%2"PRIu8"", (uint8_t)fw_act_history_entry[entryIdx].slot_number); + printf(" "); + wdc_get_commit_action_bin(fw_act_history_entry[entryIdx].commit_action_type,(char *)&commit_action_bin); + printf(" %s", (char *)commit_action_bin); + printf(" "); + if (le16_to_cpu(fw_act_history_entry[entryIdx].result) == 0) + printf("pass"); + else + printf("fail #%d", (uint16_t)le16_to_cpu(fw_act_history_entry[entryIdx].result)); + + printf("\n"); + + entryIdx++; + if (entryIdx >= WDC_MAX_NUM_ACT_HIST_ENTRIES) + entryIdx = 0; + } } } -static void wdc_print_fw_act_history_log_json(struct wdc_fw_act_history_log_entry *fw_act_history_entry, - int num_entries) +static void wdc_print_fw_act_history_log_json(__u8 *data, int num_entries, __u32 cust_id) { struct json_object *root; int i; @@ -3396,51 +4169,136 @@ static void wdc_print_fw_act_history_log_json(struct wdc_fw_act_history_log_entr char new_fw[9]; char commit_action_bin[8]; char fail_str[32]; - char time_str[9]; + char time_str[11]; memset((void *)previous_fw, 0, 9); memset((void *)new_fw, 0, 9); memset((void *)commit_action_bin, 0, 8); - memset((void *)time_str, 0, 9); + memset((void *)time_str, 0, 11); memset((void *)fail_str, 0, 11); char *null_fw = "--------"; + __u16 oldestEntryIdx = 0, entryIdx = 0; root = json_create_object(); - for (i = 0; i < num_entries; i++) { - memcpy(previous_fw, (char *)&(fw_act_history_entry->previous_fw_version), 8); - if (strlen((char *)&(fw_act_history_entry->new_fw_version)) > 1) - memcpy(new_fw, (char *)&(fw_act_history_entry->new_fw_version), 8); + if(data[0] == WDC_NVME_GET_FW_ACT_HISTORY_C2_LOG_ID) { + struct wdc_fw_act_history_log_format_c2 *fw_act_history_entry = (struct wdc_fw_act_history_log_format_c2 *)(data); + + if (num_entries == WDC_MAX_NUM_ACT_HIST_ENTRIES) { + /* find lowest/oldest entry */ + for (i = 0; i < num_entries; i++) { + if (le16_to_cpu(fw_act_history_entry->entry[i].fw_act_hist_entries) > + le16_to_cpu(fw_act_history_entry->entry[i+1].fw_act_hist_entries)) { + oldestEntryIdx = i+1; + break; + } + } + } + if (oldestEntryIdx == WDC_MAX_NUM_ACT_HIST_ENTRIES) + entryIdx = 0; else - memcpy(new_fw, null_fw, 8); - - json_object_add_value_int(root, "Entry", - le32_to_cpu(fw_act_history_entry->entry_num)); - - sprintf((char *)time_str, "%02d:%02d:%02d", (int)(le64_to_cpu(fw_act_history_entry->power_on_seconds)/3600), - (int)((le64_to_cpu(fw_act_history_entry->power_on_seconds)%3600)/60), - (int)(le64_to_cpu(fw_act_history_entry->power_on_seconds)%60)); - json_object_add_value_string(root, "Power on Hour", time_str); - - json_object_add_value_int(root, "Power Cycle Count", - le32_to_cpu(fw_act_history_entry->power_cycle_count)); - json_object_add_value_string(root, "Previous Firmware", - previous_fw); - json_object_add_value_string(root, "New Firmware", - new_fw); - json_object_add_value_int(root, "Slot", - fw_act_history_entry->slot_number); - - wdc_get_commit_action_bin(fw_act_history_entry->commit_action_type,(char *)&commit_action_bin); - json_object_add_value_string(root, "Action", commit_action_bin); - - if (le16_to_cpu(fw_act_history_entry->result) == 0) - json_object_add_value_string(root, "Result", "pass"); - else { - sprintf((char *)fail_str, "fail #%d", (int)(le16_to_cpu(fw_act_history_entry->result))); - json_object_add_value_string(root, "Result", fail_str); + entryIdx = oldestEntryIdx; + + for (i = 0; i < num_entries; i++) { + memcpy(previous_fw, (char *)&(fw_act_history_entry->entry[entryIdx].previous_fw_version), 8); + if (strlen((char *)&(fw_act_history_entry->entry[entryIdx].current_fw_version)) > 1) + memcpy(new_fw, (char *)&(fw_act_history_entry->entry[entryIdx].current_fw_version), 8); + else + memcpy(new_fw, null_fw, 8); + + json_object_add_value_int(root, "Entry", + le16_to_cpu(fw_act_history_entry->entry[entryIdx].fw_act_hist_entries)); + + if (cust_id == WDC_CUSTOMER_ID_0x1005) { + sprintf((char *)time_str, "%04d:%02d:%02d", (int)(le64_to_cpu(fw_act_history_entry->entry[entryIdx].timestamp)/3600), + (int)((le64_to_cpu(fw_act_history_entry->entry[entryIdx].timestamp%3600)/60)), + (int)(le64_to_cpu(fw_act_history_entry->entry[entryIdx].timestamp%60))); + + json_object_add_value_string(root, "Power on Hour", time_str); + + } else { + uint64_t timestamp = (0x0000FFFFFFFFFFFF & le64_to_cpu(fw_act_history_entry->entry[entryIdx].timestamp)); + json_object_add_value_int(root, "Timestamp", timestamp); + } + + json_object_add_value_int(root, "Power Cycle Count", + le64_to_cpu(fw_act_history_entry->entry[entryIdx].power_cycle_count)); + json_object_add_value_string(root, "Previous Firmware", + previous_fw); + json_object_add_value_string(root, "New Firmware", + new_fw); + json_object_add_value_int(root, "Slot", + fw_act_history_entry->entry[entryIdx].slot_number); + + wdc_get_commit_action_bin(fw_act_history_entry->entry[entryIdx].commit_action_type,(char *)&commit_action_bin); + json_object_add_value_string(root, "Action", commit_action_bin); + + if (le16_to_cpu(fw_act_history_entry->entry[entryIdx].result) == 0) + json_object_add_value_string(root, "Result", "pass"); + else { + sprintf((char *)fail_str, "fail #%d", (int)(le16_to_cpu(fw_act_history_entry->entry[entryIdx].result))); + json_object_add_value_string(root, "Result", fail_str); + } + + entryIdx++; + if (entryIdx >= WDC_MAX_NUM_ACT_HIST_ENTRIES) + entryIdx = 0; + } + } + else { + struct wdc_fw_act_history_log_entry *fw_act_history_entry = (struct wdc_fw_act_history_log_entry *)(data + sizeof(struct wdc_fw_act_history_log_hdr)); + + if (num_entries == WDC_MAX_NUM_ACT_HIST_ENTRIES) { + /* find lowest/oldest entry */ + for (i = 0; i < num_entries; i++) { + if (le32_to_cpu(fw_act_history_entry[i].entry_num) > le32_to_cpu(fw_act_history_entry[i+1].entry_num)) { + oldestEntryIdx = i+1; + break; + } + } } + if (oldestEntryIdx == WDC_MAX_NUM_ACT_HIST_ENTRIES) + entryIdx = 0; + else + entryIdx = oldestEntryIdx; + + for (i = 0; i < num_entries; i++) { + memcpy(previous_fw, (char *)&(fw_act_history_entry[entryIdx].previous_fw_version), 8); + if (strlen((char *)&(fw_act_history_entry[entryIdx].new_fw_version)) > 1) + memcpy(new_fw, (char *)&(fw_act_history_entry[entryIdx].new_fw_version), 8); + else + memcpy(new_fw, null_fw, 8); + + json_object_add_value_int(root, "Entry", + le32_to_cpu(fw_act_history_entry[entryIdx].entry_num)); + + sprintf((char *)time_str, "%04d:%02d:%02d", (int)(le64_to_cpu(fw_act_history_entry[entryIdx].power_on_seconds)/3600), + (int)((le64_to_cpu(fw_act_history_entry[entryIdx].power_on_seconds)%3600)/60), + (int)(le64_to_cpu(fw_act_history_entry[entryIdx].power_on_seconds)%60)); + json_object_add_value_string(root, "Power on Hour", time_str); + + json_object_add_value_int(root, "Power Cycle Count", + le32_to_cpu(fw_act_history_entry[entryIdx].power_cycle_count)); + json_object_add_value_string(root, "Previous Firmware", + previous_fw); + json_object_add_value_string(root, "New Firmware", + new_fw); + json_object_add_value_int(root, "Slot", + fw_act_history_entry[entryIdx].slot_number); + + wdc_get_commit_action_bin(fw_act_history_entry[entryIdx].commit_action_type,(char *)&commit_action_bin); + json_object_add_value_string(root, "Action", commit_action_bin); + + if (le16_to_cpu(fw_act_history_entry[entryIdx].result) == 0) + json_object_add_value_string(root, "Result", "pass"); + else { + sprintf((char *)fail_str, "fail #%d", (int)(le16_to_cpu(fw_act_history_entry[entryIdx].result))); + json_object_add_value_string(root, "Result", fail_str); + } - fw_act_history_entry++; + entryIdx++; + if (entryIdx >= WDC_MAX_NUM_ACT_HIST_ENTRIES) + entryIdx = 0; + } } json_print_object(root, NULL); @@ -3449,7 +4307,432 @@ static void wdc_print_fw_act_history_log_json(struct wdc_fw_act_history_log_entr json_free_object(root); } -static int wdc_print_fb_ca_log(struct wdc_ssd_ca_perf_stats *perf, int fmt) +static void wdc_print_smart_cloud_attr_C0_normal(void *data) +{ + __u8 *log_data = (__u8*)data; + uint16_t smart_log_ver = 0; + + printf(" SMART Cloud Attributes :- \n"); + + printf(" Physical media units written %.0Lf\n", + int128_to_double(&log_data[SCAO_PMUW])); + printf(" Physical media units Read %.0Lf\n", + int128_to_double(&log_data[SCAO_PMUR])); + printf(" Bad user nand blocks - Raw %"PRIu64"\n", + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_BUNBR] & 0x0000FFFFFFFFFFFF)); + printf(" Bad user nand blocks - Normalized %d\n", + (uint16_t)le16_to_cpu(*(uint16_t *)&log_data[SCAO_BUNBN])); + printf(" Bad system nand blocks - Raw %"PRIu64"\n", + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_BSNBR] & 0x0000FFFFFFFFFFFF)); + printf(" Bad system nand blocks - Normalized %d\n", + (uint16_t)le16_to_cpu(*(uint16_t *)&log_data[SCAO_BSNBN])); + printf(" XOR recovery count %"PRIu64"\n", + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_XRC])); + printf(" Uncorrectable read error count %"PRIu64"\n", + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_UREC])); + printf(" Soft ecc error count %"PRIu64"\n", + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_SEEC])); + printf(" End to end corrected errors %"PRIu32"\n", + (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_EECE])); + printf(" End to end detected errors %"PRIu32"\n", + (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_EEDC])); + printf(" System data percent used %d\n", + (__u8)log_data[SCAO_SDPU]); + printf(" Refresh counts %"PRIu64"\n", + (uint64_t)(le64_to_cpu(*(uint64_t *)&log_data[SCAO_RFSC])& 0x00FFFFFFFFFFFFFF)); + printf(" Max User data erase counts %"PRIu32"\n", + (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_MXUDEC])); + printf(" Min User data erase counts %"PRIu32"\n", + (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_MNUDEC])); + printf(" Number of Thermal throttling events %d\n", + (__u8)log_data[SCAO_NTTE]); + printf(" Current throttling status 0x%x\n", + (__u8)log_data[SCAO_CTS]); + printf(" PCIe correctable error count %"PRIu64"\n", + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PCEC])); + printf(" Incomplete shutdowns %"PRIu32"\n", + (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_ICS])); + printf(" Percent free blocks %d\n", + (__u8)log_data[SCAO_PFB]); + printf(" Capacitor health %"PRIu16"\n", + (uint16_t)le16_to_cpu(*(uint16_t *)&log_data[SCAO_CPH])); + printf(" Unaligned I/O %"PRIu64"\n", + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_UIO])); + printf(" Security Version Number %"PRIu64"\n", + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_SVN])); + printf(" NUSE - Namespace utilization %"PRIu64"\n", + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_NUSE])); + printf(" PLP start count %.0Lf\n", + int128_to_double(&log_data[SCAO_PSC])); + printf(" Endurance estimate %.0Lf\n", + int128_to_double(&log_data[SCAO_EEST])); + smart_log_ver = (uint16_t)le16_to_cpu(*(uint16_t *)&log_data[SCAO_LPV]); + printf(" Log page version %"PRIu16"\n",smart_log_ver); + printf(" Log page GUID 0x"); + printf("%lX%lX\n",(uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_LPG + 8]), + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_LPG])); + if(smart_log_ver > 2) { + printf(" Errata Version Field %d\n", + (__u8)log_data[SCAO_EVF]); + printf(" Point Version Field %"PRIu16"\n", + (uint16_t)log_data[SCAO_PVF]); + printf(" Minor Version Field %"PRIu16"\n", + (uint16_t)log_data[SCAO_MIVF]); + printf(" Major Version Field %d\n", + (__u8)log_data[SCAO_MAVF]); + printf(" NVMe Errata Version %d\n", + (__u8)log_data[SCAO_NEV]); + printf(" PCIe Link Retraining Count %"PRIu64"\n", + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PLRC])); + } + printf("\n"); +} + +static void wdc_print_smart_cloud_attr_C0_json(void *data) +{ + __u8 *log_data = (__u8*)data; + struct json_object *root; + uint16_t smart_log_ver = 0; + + root = json_create_object(); + json_object_add_value_float(root, "Physical media units written", + int128_to_double(&log_data[SCAO_PMUW])); + json_object_add_value_int(root, "Physical media units Read", + int128_to_double(&log_data[SCAO_PMUR])); + json_object_add_value_uint(root, "Bad user nand blocks - Raw", + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_BUNBR] & 0x0000FFFFFFFFFFFF)); + json_object_add_value_uint(root, "Bad user nand blocks - Normalized", + (uint16_t)le16_to_cpu(*(uint16_t *)&log_data[SCAO_BUNBN])); + json_object_add_value_uint(root, "Bad system nand blocks - Raw", + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_BSNBR] & 0x0000FFFFFFFFFFFF)); + json_object_add_value_uint(root, "Bad system nand blocks - Normalized", + (uint16_t)le16_to_cpu(*(uint16_t *)&log_data[SCAO_BSNBN])); + json_object_add_value_uint(root, "XOR recovery count", + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_XRC])); + json_object_add_value_uint(root, "Uncorrectable read error count", + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_UREC])); + json_object_add_value_uint(root, "Soft ecc error count", + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_SEEC])); + json_object_add_value_uint(root, "End to end corrected errors", + (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_EECE])); + json_object_add_value_uint(root, "End to end detected errors", + (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_EEDC])); + json_object_add_value_uint(root, "System data percent used", + (__u8)log_data[SCAO_SDPU]); + json_object_add_value_uint(root, "Refresh counts", + (uint64_t)(le64_to_cpu(*(uint64_t *)&log_data[SCAO_RFSC])& 0x00FFFFFFFFFFFFFF)); + json_object_add_value_uint(root, "Max User data erase counts", + (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_MXUDEC])); + json_object_add_value_uint(root, "Min User data erase counts", + (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_MNUDEC])); + json_object_add_value_uint(root, "Number of Thermal throttling events", + (__u8)log_data[SCAO_NTTE]); + json_object_add_value_uint(root, "Current throttling status", + (__u8)log_data[SCAO_CTS]); + json_object_add_value_uint(root, "PCIe correctable error count", + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PCEC])); + json_object_add_value_uint(root, "Incomplete shutdowns", + (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_ICS])); + json_object_add_value_uint(root, "Percent free blocks", + (__u8)log_data[SCAO_PFB]); + json_object_add_value_uint(root, "Capacitor health", + (uint16_t)le16_to_cpu(*(uint16_t *)&log_data[SCAO_CPH])); + json_object_add_value_uint(root, "Unaligned I/O", + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_UIO])); + json_object_add_value_uint(root, "Security Version Number", + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_SVN])); + json_object_add_value_uint(root, "NUSE - Namespace utilization", + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_NUSE])); + json_object_add_value_uint(root, "PLP start count", + int128_to_double(&log_data[SCAO_PSC])); + json_object_add_value_uint(root, "Endurance estimate", + int128_to_double(&log_data[SCAO_EEST])); + smart_log_ver = (uint16_t)le16_to_cpu(*(uint16_t *)&log_data[SCAO_LPV]); + json_object_add_value_uint(root, "Log page version", smart_log_ver); + char guid[40]; + memset((void*)guid, 0, 40); + sprintf((char*)guid, "0x%lX%lX",(uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_LPG + 8]), + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_LPG])); + printf("GUID string:%s", guid); + json_object_add_value_string(root, "Log page GUID", guid); + if(smart_log_ver > 2){ + json_object_add_value_uint(root, "Errata Version Field", + (__u8)log_data[SCAO_EVF]); + json_object_add_value_uint(root, "Point Version Field", + (uint16_t)log_data[SCAO_PVF]); + json_object_add_value_uint(root, "Minor Version Field", + (uint16_t)log_data[SCAO_MIVF]); + json_object_add_value_uint(root, "Major Version Field", + (__u8)log_data[SCAO_MAVF]); + json_object_add_value_uint(root, "NVMe Errata Version", + (__u8)log_data[SCAO_NEV]); + json_object_add_value_uint(root, "PCIe Link Retraining Count", + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PLRC])); + } + json_print_object(root, NULL); + printf("\n"); + json_free_object(root); +} + +static void wdc_print_eol_c0_normal(void *data) +{ + + __u8 *log_data = (__u8*)data; + + printf(" End of Life Log Page 0xC0 :- \n"); + + printf(" Realloc Block Count %"PRIu32"\n", + (uint32_t)le32_to_cpu(log_data[EOL_RBC])); + printf(" ECC Rate %"PRIu32"\n", + (uint32_t)le32_to_cpu(log_data[EOL_ECCR])); + printf(" Write Amp %"PRIu32"\n", + (uint32_t)le32_to_cpu(log_data[EOL_WRA])); + printf(" Percent Life Remaining %"PRIu32"\n", + (uint32_t)le32_to_cpu(log_data[EOL_PLR])); + printf(" Program Fail Count %"PRIu32"\n", + (uint32_t)le32_to_cpu(log_data[EOL_PFC])); + printf(" Erase Fail Count %"PRIu32"\n", + (uint32_t)le32_to_cpu(log_data[EOL_EFC])); + printf(" Raw Read Error Rate %"PRIu32"\n", + (uint32_t)le32_to_cpu(log_data[EOL_RRER])); + +} + +static void wdc_print_eol_c0_json(void *data) +{ + __u8 *log_data = (__u8*)data; + struct json_object *root; + + root = json_create_object(); + + json_object_add_value_uint(root, "Realloc Block Count", + (uint32_t)le32_to_cpu(log_data[EOL_RBC])); + json_object_add_value_uint(root, "ECC Rate", + (uint32_t)le32_to_cpu(log_data[EOL_ECCR])); + json_object_add_value_uint(root, "Write Amp", + (uint32_t)le32_to_cpu(log_data[EOL_WRA])); + json_object_add_value_uint(root, "Percent Life Remaining", + (uint32_t)le32_to_cpu(log_data[EOL_PLR])); + json_object_add_value_uint(root, "Program Fail Count", + (uint32_t)le32_to_cpu(log_data[EOL_PFC])); + json_object_add_value_uint(root, "Erase Fail Count", + (uint32_t)le32_to_cpu(log_data[EOL_EFC])); + json_object_add_value_uint(root, "Raw Read Error Rate", + (uint32_t)le32_to_cpu(log_data[EOL_RRER])); + + json_print_object(root, NULL); + printf("\n"); + json_free_object(root); +} + +static int wdc_print_c0_cloud_attr_log(void *data, int fmt) +{ + if (!data) { + fprintf(stderr, "ERROR : WDC : Invalid buffer to read 0xC0 log\n"); + return -1; + } + switch (fmt) { + case NORMAL: + wdc_print_smart_cloud_attr_C0_normal(data); + break; + case JSON: + wdc_print_smart_cloud_attr_C0_json(data); + break; + } + return 0; +} + +static int wdc_print_c0_eol_log(void *data, int fmt) +{ + if (!data) { + fprintf(stderr, "ERROR : WDC : Invalid buffer to read 0xC0 log\n"); + return -1; + } + switch (fmt) { + case NORMAL: + wdc_print_eol_c0_normal(data); + break; + case JSON: + wdc_print_eol_c0_json(data); + break; + } + return 0; +} + +static int wdc_get_c0_log_page(int fd, char *format, int uuid_index) +{ + int ret = 0; + int fmt = -1; + int i = 0; + __u8 *data; + __u32 *cust_id; + uint32_t device_id, read_vendor_id; + + if (!wdc_check_device(fd)) + return -1; + fmt = validate_output_format(format); + if (fmt < 0) { + fprintf(stderr, "ERROR : WDC : invalid output format\n"); + return fmt; + } + + ret = wdc_get_pci_ids(&device_id, &read_vendor_id); + + switch (device_id) { + + case WDC_NVME_SN640_DEV_ID: + case WDC_NVME_SN640_DEV_ID_1: + case WDC_NVME_SN640_DEV_ID_2: + case WDC_NVME_SN640_DEV_ID_3: + case WDC_NVME_SN840_DEV_ID: + case WDC_NVME_SN840_DEV_ID_1: + if (!get_dev_mgment_cbs_data(fd, WDC_C2_CUSTOMER_ID_ID, (void*)&data)) { + fprintf(stderr, "%s: ERROR : WDC : 0xC2 Log Page entry ID 0x%x not found\n", __func__, WDC_C2_CUSTOMER_ID_ID); + return -1; + } + + cust_id = (__u32*)data; + + if ((*cust_id == WDC_CUSTOMER_ID_0x1004) || (*cust_id == WDC_CUSTOMER_ID_0x1008) || + (*cust_id == WDC_CUSTOMER_ID_0x1005) || (*cust_id == WDC_CUSTOMER_ID_0x1304)) + { + if (uuid_index == 0) + { + if ((data = (__u8*) malloc(sizeof (__u8) * WDC_NVME_SMART_CLOUD_ATTR_LEN)) == NULL) { + fprintf(stderr, "ERROR : WDC : malloc : %s\n", strerror(errno)); + return -1; + } + + /* Get the 0xC0 log data */ + ret = nvme_get_log14(fd, 0xFFFFFFFF, WDC_NVME_GET_EOL_STATUS_LOG_OPCODE, + NVME_NO_LOG_LSP, 0, 0, false, uuid_index, WDC_NVME_SMART_CLOUD_ATTR_LEN, data); + + if (strcmp(format, "json")) + fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret); + + if (ret == 0) { + + /* Verify GUID matches */ + for (i=0; i<16; i++) { + if (scao_guid[i] != data[SCAO_LPG + i]) { + fprintf(stderr, "ERROR : WDC : Unknown GUID in C0 Log Page data\n"); + int j; + fprintf(stderr, "ERROR : WDC : Expected GUID: 0x"); + for (j = 0; j<16; j++) { + fprintf(stderr, "%x", scao_guid[j]); + } + fprintf(stderr, "\nERROR : WDC : Actual GUID: 0x"); + for (j = 0; j<16; j++) { + fprintf(stderr, "%x", data[SCAO_LPG + j]); + } + fprintf(stderr, "\n"); + + ret = -1; + break; + } + } + + if (ret == 0) { + + /* parse the data */ + wdc_print_c0_cloud_attr_log(data, fmt); + } + } else { + fprintf(stderr, "ERROR : WDC : Unable to read C0 Log Page data\n"); + ret = -1; + } + + free(data); + } else if (uuid_index == 1) { + + if ((data = (__u8*) malloc(sizeof (__u8) * WDC_NVME_EOL_STATUS_LOG_LEN)) == NULL) { + fprintf(stderr, "ERROR : WDC : malloc : %s\n", strerror(errno)); + return -1; + } + + /* Get the 0xC0 log data */ + ret = nvme_get_log14(fd, 0xFFFFFFFF, WDC_NVME_GET_EOL_STATUS_LOG_OPCODE, + NVME_NO_LOG_LSP, 0, 0, false, uuid_index, WDC_NVME_EOL_STATUS_LOG_LEN, data); + + if (strcmp(format, "json")) + fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret); + + if (ret == 0) { + /* parse the data */ + wdc_print_c0_eol_log(data, fmt); + } else { + fprintf(stderr, "ERROR : WDC : Unable to read C0 Log Page data\n"); + ret = -1; + } + + free(data); + } else { + fprintf(stderr, "ERROR : WDC : Unknown uuid index\n"); + ret = -1; + } + } + else { + if ((data = (__u8*) malloc(sizeof (__u8) * WDC_NVME_EOL_STATUS_LOG_LEN)) == NULL) { + fprintf(stderr, "ERROR : WDC : malloc : %s\n", strerror(errno)); + return -1; + } + + /* Get the 0xC0 log data */ + ret = nvme_get_log(fd, 0xFFFFFFFF, WDC_NVME_GET_EOL_STATUS_LOG_OPCODE, + false, NVME_NO_LOG_LSP, WDC_NVME_EOL_STATUS_LOG_LEN, data); + + if (strcmp(format, "json")) + fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret); + + if (ret == 0) { + /* parse the data */ + wdc_print_c0_eol_log(data, fmt); + } else { + fprintf(stderr, "ERROR : WDC : Unable to read C0 Log Page data\n"); + ret = -1; + } + + free(data); + } + break; + + case WDC_NVME_ZN350_DEV_ID: + case WDC_NVME_ZN350_DEV_ID_1: + if ((data = (__u8*) malloc(sizeof (__u8) * WDC_NVME_SMART_CLOUD_ATTR_LEN)) == NULL) { + fprintf(stderr, "ERROR : WDC : malloc : %s\n", strerror(errno)); + return -1; + } + + /* Get the 0xC0 log data */ + ret = nvme_get_log(fd, 0xFFFFFFFF, WDC_NVME_GET_SMART_CLOUD_ATTR_LOG_OPCODE, + false, NVME_NO_LOG_LSP, WDC_NVME_SMART_CLOUD_ATTR_LEN, data); + + if (strcmp(format, "json")) + fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret); + + if (ret == 0) { + /* parse the data */ + wdc_print_c0_cloud_attr_log(data, fmt); + } else { + fprintf(stderr, "ERROR : WDC : Unable to read C0 Log Page data\n"); + ret = -1; + } + + free(data); + break; + + default: + fprintf(stderr, "ERROR : WDC : Unknown device id - 0x%x\n", device_id); + + ret = -1; + break; + + } + + return ret; +} + +static int wdc_print_fb_ca_log(struct wdc_ssd_ca_perf_stats *perf, int fmt) { if (!perf) { fprintf(stderr, "ERROR : WDC : Invalid buffer to read perf stats\n"); @@ -3500,21 +4783,19 @@ static int wdc_print_d0_log(struct wdc_ssd_d0_smart_log *perf, int fmt) return 0; } -static int wdc_print_fw_act_history_log(struct wdc_fw_act_history_log_entry *fw_act_history_entries, - int num_entries, - int fmt) +static int wdc_print_fw_act_history_log(__u8 *data, int num_entries, int fmt, __u32 cust_id) { - if (!fw_act_history_entries) { + if (!data) { fprintf(stderr, "ERROR : WDC : Invalid buffer to read fw activate history entries\n"); return -1; } switch (fmt) { case NORMAL: - wdc_print_fw_act_history_log_normal(fw_act_history_entries, num_entries); + wdc_print_fw_act_history_log_normal(data, num_entries, cust_id); break; case JSON: - wdc_print_fw_act_history_log_json(fw_act_history_entries, num_entries); + wdc_print_fw_act_history_log_json(data, num_entries, cust_id); break; } return 0; @@ -3566,7 +4847,7 @@ static int wdc_get_ca_log_page(int fd, char *format) memset(data, 0, sizeof (__u8) * WDC_FB_CA_LOG_BUF_LEN); ret = nvme_get_log(fd, 0xFFFFFFFF, WDC_NVME_GET_DEVICE_INFO_LOG_OPCODE, - false, WDC_FB_CA_LOG_BUF_LEN, data); + false, NVME_NO_LOG_LSP, WDC_FB_CA_LOG_BUF_LEN, data); if (strcmp(format, "json")) fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret); @@ -3580,7 +4861,7 @@ static int wdc_get_ca_log_page(int fd, char *format) } } else { - fprintf(stderr, "ERROR : WDC : Unsupported Customer id, id = %d\n", *cust_id); + fprintf(stderr, "ERROR : WDC : Unsupported Customer id, id = 0x%x\n", *cust_id); return -1; } break; @@ -3588,7 +4869,7 @@ static int wdc_get_ca_log_page(int fd, char *format) case WDC_NVME_SN640_DEV_ID: case WDC_NVME_SN640_DEV_ID_1: case WDC_NVME_SN640_DEV_ID_2: - case WDC_NVME_SN640_DEV_ID_3: + case WDC_NVME_SN640_DEV_ID_3: case WDC_NVME_SN840_DEV_ID: case WDC_NVME_SN840_DEV_ID_1: @@ -3602,7 +4883,7 @@ static int wdc_get_ca_log_page(int fd, char *format) memset(data, 0, sizeof (__u8) * WDC_FB_CA_LOG_BUF_LEN); ret = nvme_get_log(fd, 0xFFFFFFFF, WDC_NVME_GET_DEVICE_INFO_LOG_OPCODE, - false, WDC_FB_CA_LOG_BUF_LEN, data); + false, NVME_NO_LOG_LSP, WDC_FB_CA_LOG_BUF_LEN, data); if (strcmp(format, "json")) fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret); @@ -3614,7 +4895,8 @@ static int wdc_get_ca_log_page(int fd, char *format) fprintf(stderr, "ERROR : WDC : Unable to read CA Log Page data\n"); ret = -1; } - } else if ((*cust_id == WDC_CUSTOMER_ID_GN) || (*cust_id == WDC_CUSTOMER_ID_GD)) { + } else if ((*cust_id == WDC_CUSTOMER_ID_GN) || (*cust_id == WDC_CUSTOMER_ID_GD) || + (*cust_id == WDC_CUSTOMER_ID_BD)) { if ((data = (__u8*) malloc(sizeof (__u8) * WDC_BD_CA_LOG_BUF_LEN)) == NULL) { fprintf(stderr, "ERROR : WDC : malloc : %s\n", strerror(errno)); @@ -3623,7 +4905,7 @@ static int wdc_get_ca_log_page(int fd, char *format) memset(data, 0, sizeof (__u8) * WDC_BD_CA_LOG_BUF_LEN); ret = nvme_get_log(fd, 0xFFFFFFFF, WDC_NVME_GET_DEVICE_INFO_LOG_OPCODE, - false, WDC_BD_CA_LOG_BUF_LEN, data); + false, NVME_NO_LOG_LSP, WDC_BD_CA_LOG_BUF_LEN, data); if (strcmp(format, "json")) fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret); @@ -3638,7 +4920,7 @@ static int wdc_get_ca_log_page(int fd, char *format) break; } else { - fprintf(stderr, "ERROR : WDC : Unsupported Customer id, id = %d\n", *cust_id); + fprintf(stderr, "ERROR : WDC : Unsupported Customer id, id = 0x%x\n", *cust_id); return -1; } break; @@ -3687,7 +4969,7 @@ static int wdc_get_c1_log_page(int fd, char *format, uint8_t interval) memset(data, 0, sizeof (__u8) * WDC_ADD_LOG_BUF_LEN); ret = nvme_get_log(fd, 0x01, WDC_NVME_ADD_LOG_OPCODE, false, - WDC_ADD_LOG_BUF_LEN, data); + NVME_NO_LOG_LSP, WDC_ADD_LOG_BUF_LEN, data); if (strcmp(format, "json")) fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret); if (ret == 0) { @@ -3740,7 +5022,7 @@ static int wdc_get_d0_log_page(int fd, char *format) memset(data, 0, sizeof (__u8) * WDC_NVME_VU_SMART_LOG_LEN); ret = nvme_get_log(fd, 0xFFFFFFFF, WDC_NVME_GET_VU_SMART_LOG_OPCODE, - false, WDC_NVME_VU_SMART_LOG_LEN, data); + false, NVME_NO_LOG_LSP, WDC_NVME_VU_SMART_LOG_LEN, data); if (strcmp(format, "json")) fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret); @@ -3763,23 +5045,33 @@ static int wdc_vs_smart_add_log(int argc, char **argv, struct command *command, const char *desc = "Retrieve additional performance statistics."; const char *interval = "Interval to read the statistics from [1, 15]."; int fd; + const char *log_page_version = "Log Page Version: 0 = vendor, 1 = WDC"; + const char *log_page_mask = "Log Page Mask, comma separated list: 0xC0, 0xC1, 0xCA, 0xD0"; int ret = 0; + int uuid_index = 0; + int page_mask = 0, num, i; + int log_page_list[16]; __u64 capabilities = 0; struct config { uint8_t interval; - int vendor_specific; char *output_format; + __u8 log_page_version; + char *log_page_mask; }; struct config cfg = { .interval = 14, .output_format = "normal", + .log_page_version = 0, + .log_page_mask = "", }; OPT_ARGS(opts) = { - OPT_UINT("interval", 'i', &cfg.interval, interval), - OPT_FMT("output-format", 'o', &cfg.output_format, "Output Format: normal|json"), + OPT_UINT("interval", 'i', &cfg.interval, interval), + OPT_FMT("output-format", 'o', &cfg.output_format, "Output Format: normal|json"), + OPT_BYTE("log-page-version", 'l', &cfg.log_page_version, log_page_version), + OPT_LIST("log-page-mask", 'p', &cfg.log_page_mask, log_page_mask), OPT_END() }; @@ -3787,6 +5079,50 @@ static int wdc_vs_smart_add_log(int argc, char **argv, struct command *command, if (fd < 0) return fd; + if (cfg.log_page_version == 0) { + uuid_index = 0; + } else if (cfg.log_page_version == 1) { + uuid_index = 1; + } else { + fprintf(stderr, "ERROR : WDC: unsupported log page version for this command\n"); + ret = -1; + goto out; + } + + num = argconfig_parse_comma_sep_array(cfg.log_page_mask, log_page_list, 16); + + if (num == -1) { + fprintf(stderr, "ERROR: WDC: log page list is malformed\n"); + ret = -1; + goto out; + } + + if (num == 0) + { + page_mask |= WDC_ALL_PAGE_MASK; + } + else + { + for (i = 0; i < num; i++) + { + if (log_page_list[i] == 0xc0) { + page_mask |= WDC_C0_PAGE_MASK; + } + if (log_page_list[i] == 0xc1) { + page_mask |= WDC_C1_PAGE_MASK; + } + if (log_page_list[i] == 0xca) { + page_mask |= WDC_CA_PAGE_MASK; + } + if (log_page_list[i] == 0xd0) { + page_mask |= WDC_D0_PAGE_MASK; + } + } + } + if (page_mask == 0) + fprintf(stderr, "ERROR : WDC: Unknown log page mask - %s\n", cfg.log_page_mask); + + capabilities = wdc_get_drive_capabilities(fd); if ((capabilities & WDC_DRIVE_CAP_SMART_LOG_MASK) == 0) { @@ -3795,25 +5131,78 @@ static int wdc_vs_smart_add_log(int argc, char **argv, struct command *command, goto out; } - if ((capabilities & (WDC_DRIVE_CAP_CA_LOG_PAGE)) == (WDC_DRIVE_CAP_CA_LOG_PAGE)) { + if (((capabilities & WDC_DRIVE_CAP_C0_LOG_PAGE) == WDC_DRIVE_CAP_C0_LOG_PAGE) && + (page_mask & WDC_C0_PAGE_MASK)) { + /* Get 0xC0 log page if possible. */ + ret = wdc_get_c0_log_page(fd, cfg.output_format, uuid_index); + if (ret) + fprintf(stderr, "ERROR : WDC : Failure reading the C0 Log Page, ret = %d\n", ret); + } + if (((capabilities & (WDC_DRIVE_CAP_CA_LOG_PAGE)) == (WDC_DRIVE_CAP_CA_LOG_PAGE)) && + (page_mask & WDC_CA_PAGE_MASK)) { /* Get the CA Log Page */ ret = wdc_get_ca_log_page(fd, cfg.output_format); if (ret) fprintf(stderr, "ERROR : WDC : Failure reading the CA Log Page, ret = %d\n", ret); } - if ((capabilities & WDC_DRIVE_CAP_C1_LOG_PAGE) == WDC_DRIVE_CAP_C1_LOG_PAGE) { + if (((capabilities & WDC_DRIVE_CAP_C1_LOG_PAGE) == WDC_DRIVE_CAP_C1_LOG_PAGE) && + (page_mask & WDC_C1_PAGE_MASK)) { /* Get the C1 Log Page */ ret = wdc_get_c1_log_page(fd, cfg.output_format, cfg.interval); if (ret) fprintf(stderr, "ERROR : WDC : Failure reading the C1 Log Page, ret = %d\n", ret); } - if ((capabilities & WDC_DRIVE_CAP_D0_LOG_PAGE) == WDC_DRIVE_CAP_D0_LOG_PAGE) { + if (((capabilities & WDC_DRIVE_CAP_D0_LOG_PAGE) == WDC_DRIVE_CAP_D0_LOG_PAGE) && + (page_mask & WDC_D0_PAGE_MASK)) { /* Get the D0 Log Page */ ret = wdc_get_d0_log_page(fd, cfg.output_format); if (ret) fprintf(stderr, "ERROR : WDC : Failure reading the D0 Log Page, ret = %d\n", ret); } + out: + + return ret; +} + +static int wdc_do_clear_pcie_correctable_errors(int fd) +{ + int ret; + struct nvme_passthru_cmd admin_cmd; + + memset(&admin_cmd, 0, sizeof (admin_cmd)); + admin_cmd.opcode = WDC_NVME_CLEAR_PCIE_CORR_OPCODE; + admin_cmd.cdw12 = ((WDC_NVME_CLEAR_PCIE_CORR_SUBCMD << WDC_NVME_SUBCMD_SHIFT) | + WDC_NVME_CLEAR_PCIE_CORR_CMD); + + ret = nvme_submit_admin_passthru(fd, &admin_cmd); + fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret); + return ret; +} + +static int wdc_do_clear_pcie_correctable_errors_vuc(int fd) +{ + int ret; + struct nvme_passthru_cmd admin_cmd; + + memset(&admin_cmd, 0, sizeof (admin_cmd)); + admin_cmd.opcode = WDC_NVME_CLEAR_PCIE_CORR_OPCODE_VUC; + + ret = nvme_submit_admin_passthru(fd, &admin_cmd); + fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret); + return ret; +} + +static int wdc_do_clear_pcie_correctable_errors_fid(int fd) +{ + int ret; + __u32 result; + __u32 value = 1 << 31; /* Bit 31 - clear PCIe correctable count */ + + ret = nvme_set_feature(fd, 0, WDC_NVME_CLEAR_PCIE_CORR_FEATURE_ID, value, + 0, 0, 0, NULL, &result); + + fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret); return ret; } @@ -3823,7 +5212,6 @@ static int wdc_clear_pcie_correctable_errors(int argc, char **argv, struct comma char *desc = "Clear PCIE Correctable Errors."; int fd, ret; __u64 capabilities = 0; - struct nvme_passthru_cmd admin_cmd; OPT_ARGS(opts) = { OPT_END() @@ -3839,28 +5227,32 @@ static int wdc_clear_pcie_correctable_errors(int argc, char **argv, struct comma } capabilities = wdc_get_drive_capabilities(fd); - if ((capabilities & WDC_DRIVE_CAP_CLEAR_PCIE) == 0) { + if ((capabilities & WDC_DRIVE_CAP_CLEAR_PCIE_MASK) == 0) { fprintf(stderr, "ERROR : WDC: unsupported device for this command\n"); ret = -1; goto out; } + + if (capabilities & WDC_DRIVE_CAP_CLEAR_PCIE) { + ret = wdc_do_clear_pcie_correctable_errors(fd); + } + else if (capabilities & WDC_DRIVE_CAP_VUC_CLEAR_PCIE) { + ret = wdc_do_clear_pcie_correctable_errors_vuc(fd); + } + else { + ret = wdc_do_clear_pcie_correctable_errors_fid(fd); + } - memset(&admin_cmd, 0, sizeof (admin_cmd)); - admin_cmd.opcode = WDC_NVME_CLEAR_PCIE_CORR_OPCODE; - admin_cmd.cdw12 = ((WDC_NVME_CLEAR_PCIE_CORR_SUBCMD << WDC_NVME_SUBCMD_SHIFT) | - WDC_NVME_CLEAR_PCIE_CORR_CMD); - - ret = nvme_submit_admin_passthru(fd, &admin_cmd); - fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret); out: return ret; } + static int wdc_drive_status(int argc, char **argv, struct command *command, struct plugin *plugin) { char *desc = "Get Drive Status."; int fd; - int ret = -1; + int ret = 0; __le32 system_eol_state; __le32 user_eol_state; __le32 format_corrupt_reason = cpu_to_le32(0xFFFFFFFF); @@ -4022,7 +5414,6 @@ static int wdc_get_fw_act_history(int fd, char *format) int fmt = -1; __u8 *data; struct wdc_fw_act_history_log_hdr *fw_act_history_hdr; - struct wdc_fw_act_history_log_entry *fw_act_history_entry; if (!wdc_check_device(fd)) return -1; @@ -4047,7 +5438,7 @@ static int wdc_get_fw_act_history(int fd, char *format) memset(data, 0, sizeof (__u8) * WDC_FW_ACT_HISTORY_LOG_BUF_LEN); ret = nvme_get_log(fd, 0xFFFFFFFF, WDC_NVME_GET_FW_ACT_HISTORY_LOG_ID, - false, WDC_FW_ACT_HISTORY_LOG_BUF_LEN, data); + false, NVME_NO_LOG_LSP, WDC_FW_ACT_HISTORY_LOG_BUF_LEN, data); if (strcmp(format, "json")) fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret); @@ -4055,12 +5446,17 @@ static int wdc_get_fw_act_history(int fd, char *format) if (ret == 0) { /* parse the data */ fw_act_history_hdr = (struct wdc_fw_act_history_log_hdr *)(data); - fw_act_history_entry = (struct wdc_fw_act_history_log_entry *)(data + sizeof(struct wdc_fw_act_history_log_hdr)); - if (fw_act_history_hdr->num_entries > 0) - ret = wdc_print_fw_act_history_log(fw_act_history_entry, fw_act_history_hdr->num_entries, fmt); - else - fprintf(stderr, "INFO : WDC : No entries found in FW Activate History Log Page\n"); + if ((fw_act_history_hdr->num_entries > 0) && (fw_act_history_hdr->num_entries <= WDC_MAX_NUM_ACT_HIST_ENTRIES)) + ret = wdc_print_fw_act_history_log(data, fw_act_history_hdr->num_entries, fmt, 0); + else if (fw_act_history_hdr->num_entries == 0) { + fprintf(stderr, "INFO : WDC : No FW Activate History entries found.\n"); + ret = 0; + } + else { + fprintf(stderr, "ERROR : WDC : Invalid number entries found in FW Activate History Log Page - %d\n", fw_act_history_hdr->num_entries); + ret = -1; + } } else { fprintf(stderr, "ERROR : WDC : Unable to read FW Activate History Log Page data\n"); ret = -1; @@ -4070,71 +5466,156 @@ static int wdc_get_fw_act_history(int fd, char *format) return ret; } -static int wdc_vs_fw_activate_history(int argc, char **argv, struct command *command, - struct plugin *plugin) +static int wdc_get_fw_act_history_C2(int fd, char *format) { - int fd; int ret = 0; - __u64 capabilities = 0; - const char *desc = "Retrieve FW activate history table."; + int fmt = -1; + __u8 *data; + __u32 *cust_id; + struct wdc_fw_act_history_log_format_c2 *fw_act_history_log; + __u32 num_entries = 0; + if (!wdc_check_device(fd)) + return -1; - struct config { - char *output_format; - }; + fmt = validate_output_format(format); + if (fmt < 0) { + fprintf(stderr, "ERROR : WDC : invalid output format\n"); + return fmt; + } - struct config cfg = { - .output_format = "normal", - }; + if ((data = (__u8*) malloc(sizeof (__u8) * WDC_FW_ACT_HISTORY_C2_LOG_BUF_LEN)) == NULL) { + fprintf(stderr, "ERROR : WDC : malloc : %s\n", strerror(errno)); + return -1; + } - OPT_ARGS(opts) = { - OPT_FMT("output-format", 'o', &cfg.output_format, "Output Format: normal|json"), - OPT_END() - }; + memset(data, 0, sizeof (__u8) * WDC_FW_ACT_HISTORY_C2_LOG_BUF_LEN); - fd = parse_and_open(argc, argv, desc, opts); + ret = nvme_get_log(fd, 0xFFFFFFFF, WDC_NVME_GET_FW_ACT_HISTORY_C2_LOG_ID, + false, NVME_NO_LOG_LSP, WDC_FW_ACT_HISTORY_C2_LOG_BUF_LEN, data); - if (fd < 0) - return fd; + if (strcmp(format, "json")) + fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret); - capabilities = wdc_get_drive_capabilities(fd); + if (ret == 0) { + /* parse the data */ + fw_act_history_log = (struct wdc_fw_act_history_log_format_c2*)(data); + num_entries = le32_to_cpu(fw_act_history_log->num_entries); - if ((capabilities & WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY) == WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY) { - ret = wdc_get_fw_act_history(fd, cfg.output_format); - if (ret) - fprintf(stderr, "ERROR : WDC : Failure reading the FW Activate History, ret = %d\n", ret); + if ((num_entries > 0) && (num_entries <= WDC_MAX_NUM_ACT_HIST_ENTRIES)) { + /* get the FW customer id */ + if (!get_dev_mgment_cbs_data(fd, WDC_C2_CUSTOMER_ID_ID, (void*)&cust_id)) { + fprintf(stderr, "%s: ERROR : WDC : 0xC2 Log Page entry ID 0x%x not found\n", __func__, WDC_C2_CUSTOMER_ID_ID); + ret = -1; + goto freeData; + } + + ret = wdc_print_fw_act_history_log(data, num_entries, fmt, *cust_id); + } else if (num_entries == 0) { + fprintf(stderr, "INFO : WDC : No FW Activate History entries found.\n"); + ret = 0; + } + else { + fprintf(stderr, "ERROR : WDC : Invalid number entries found in FW Activate History Log Page - %d\n", num_entries); + ret = -1; + } } else { - fprintf(stderr, "ERROR : WDC: unsupported device for this command\n"); + fprintf(stderr, "ERROR : WDC : Unable to read FW Activate History Log Page data\n"); ret = -1; } +freeData: + free(data); return ret; } -static int wdc_clear_fw_activate_history(int argc, char **argv, struct command *command, +static int wdc_vs_fw_activate_history(int argc, char **argv, struct command *command, struct plugin *plugin) { - char *desc = "Clear FW activate history table."; int fd; - int ret = -1; + int ret = 0; __u64 capabilities = 0; - struct nvme_passthru_cmd admin_cmd; + const char *desc = "Retrieve FW activate history table."; + + + struct config { + char *output_format; + }; + + struct config cfg = { + .output_format = "normal", + }; OPT_ARGS(opts) = { + OPT_FMT("output-format", 'o', &cfg.output_format, "Output Format: normal|json"), OPT_END() }; fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) return fd; capabilities = wdc_get_drive_capabilities(fd); - if ((capabilities & WDC_DRIVE_CAP_CLEAR_FW_ACT_HISTORY) != WDC_DRIVE_CAP_CLEAR_FW_ACT_HISTORY) { + if ((capabilities & WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY_MASK) == 0) { fprintf(stderr, "ERROR : WDC: unsupported device for this command\n"); ret = -1; goto out; } + if (capabilities & WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY) { + int uuid_index = 0; + bool c0GuidMatch = false; + __u8 *data; + int i; + + /* check for the GUID in the 0xC0 log page to determine which log page to use to */ + /* to retrieve fw activate history data */ + if ((data = (__u8*) malloc(sizeof (__u8) * WDC_NVME_SMART_CLOUD_ATTR_LEN)) == NULL) { + fprintf(stderr, "ERROR : WDC : malloc : %s\n", strerror(errno)); + return -1; + } + + /* Get the 0xC0 log data */ + ret = nvme_get_log14(fd, 0xFFFFFFFF, WDC_NVME_GET_SMART_CLOUD_ATTR_LOG_OPCODE, + NVME_NO_LOG_LSP, 0, 0, false, uuid_index, WDC_NVME_SMART_CLOUD_ATTR_LEN, data); + + if (ret == 0) { + /* Verify GUID matches */ + for (i=0; i<16; i++) { + if (scao_guid[i] != data[SCAO_LPG + i]) { + c0GuidMatch = false; + break; + } + } + + if (i == 16) { + c0GuidMatch = true; + } + } + + free(data); + if (c0GuidMatch) { + ret = wdc_get_fw_act_history_C2(fd, cfg.output_format); + } + else { + ret = wdc_get_fw_act_history(fd, cfg.output_format); + } + } else { + ret = wdc_get_fw_act_history_C2(fd, cfg.output_format); + } + + if (ret) + fprintf(stderr, "ERROR : WDC : Failure reading the FW Activate History, ret = %d\n", ret); +out: + return ret; +} + +static int wdc_do_clear_fw_activate_history_vuc(int fd) +{ + int ret = -1; + struct nvme_passthru_cmd admin_cmd; + memset(&admin_cmd, 0, sizeof (admin_cmd)); admin_cmd.opcode = WDC_NVME_CLEAR_FW_ACT_HIST_OPCODE; admin_cmd.cdw12 = ((WDC_NVME_CLEAR_FW_ACT_HIST_SUBCMD << WDC_NVME_SUBCMD_SHIFT) | @@ -4143,6 +5624,52 @@ static int wdc_clear_fw_activate_history(int argc, char **argv, struct command * ret = nvme_submit_admin_passthru(fd, &admin_cmd); fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret); + return ret; +} + +static int wdc_do_clear_fw_activate_history_fid(int fd) +{ + int ret = -1; + __u32 result; + __u32 value = 1 << 31; /* Bit 31 - Clear Firmware Update History Log */ + + ret = nvme_set_feature(fd, 0, WDC_NVME_CLEAR_FW_ACT_HIST_VU_FID, value, + 0, 0, 0, NULL, &result); + + fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret); + return ret; +} + +static int wdc_clear_fw_activate_history(int argc, char **argv, struct command *command, + struct plugin *plugin) +{ + char *desc = "Clear FW activate history table."; + int fd; + int ret = -1; + __u64 capabilities = 0; + + OPT_ARGS(opts) = { + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + return fd; + + capabilities = wdc_get_drive_capabilities(fd); + if ((capabilities & WDC_DRIVE_CAP_CLEAR_FW_ACT_HISTORY_MASK) == 0) { + fprintf(stderr, "ERROR : WDC: unsupported device for this command\n"); + ret = -1; + goto out; + } + + if (capabilities & WDC_DRIVE_CAP_CLEAR_FW_ACT_HISTORY) { + ret = wdc_do_clear_fw_activate_history_vuc(fd); + } + else { + ret = wdc_do_clear_fw_activate_history_fid(fd); + } + out: return ret; } @@ -4811,7 +6338,7 @@ static int wdc_do_drive_essentials(int fd, char *dir, char *key) memset(dataBuffer, 0, dataBufferSize); ret = nvme_get_log(fd, WDC_DE_GLOBAL_NSID, deVULogPagesList[vuLogIdx].logPageId, - false, dataBufferSize, dataBuffer); + false, NVME_NO_LOG_LSP, dataBufferSize, dataBuffer); if (ret) { fprintf(stderr, "ERROR : WDC : nvme_get_log() for log page 0x%x failed, ret = %d\n", deVULogPagesList[vuLogIdx].logPageId, ret); @@ -5025,7 +6552,6 @@ static int wdc_do_drive_info(int fd, __u32 *result) return ret; } - static int wdc_drive_resize(int argc, char **argv, struct command *command, struct plugin *plugin) { @@ -5230,13 +6756,16 @@ static const char *nvme_log_id_to_string(__u8 log_id) case NVME_LOG_SANITIZE: return "Sanitize Status Log ID"; case WDC_LOG_ID_C0: return "WDC Vendor Unique Log ID"; + case WDC_LOG_ID_C1: return "WDC Vendor Unique Log ID"; case WDC_LOG_ID_C2: return "WDC Vendor Unique Log ID"; case WDC_LOG_ID_C4: return "WDC Vendor Unique Log ID"; case WDC_LOG_ID_C5: return "WDC Vendor Unique Log ID"; case WDC_LOG_ID_C6: return "WDC Vendor Unique Log ID"; + case WDC_LOG_ID_C8: return "WDC Vendor Unique Log ID"; case WDC_LOG_ID_CA: return "WDC Vendor Unique Log ID"; case WDC_LOG_ID_CB: return "WDC Vendor Unique Log ID"; case WDC_LOG_ID_D0: return "WDC Vendor Unique Log ID"; + case WDC_LOG_ID_D1: return "WDC Vendor Unique Log ID"; case WDC_LOG_ID_D6: return "WDC Vendor Unique Log ID"; case WDC_LOG_ID_D7: return "WDC Vendor Unique Log ID"; case WDC_LOG_ID_D8: return "WDC Vendor Unique Log ID"; @@ -5258,7 +6787,9 @@ static int wdc_log_page_directory(int argc, char **argv, struct command *command int ret = 0; __u64 capabilities = 0; struct wdc_c2_cbs_data *cbs_data = NULL; - int i; + int i; + __u8 log_id = 0; + __u32 device_id, read_vendor_id; struct config { char *output_format; @@ -5288,10 +6819,14 @@ static int wdc_log_page_directory(int argc, char **argv, struct command *command if ((capabilities & WDC_DRIVE_CAP_LOG_PAGE_DIR) == 0) { fprintf(stderr, "ERROR : WDC: unsupported device for this command\n"); ret = -1; - } else { + } + else { + ret = wdc_get_pci_ids(&device_id, &read_vendor_id); + log_id = (device_id == WDC_NVME_ZN350_DEV_ID || device_id == WDC_NVME_ZN350_DEV_ID_1) ? + WDC_NVME_GET_DEV_MGMNT_LOG_PAGE_OPCODE_C8 : WDC_NVME_GET_DEV_MGMNT_LOG_PAGE_OPCODE; /* verify the 0xC2 Device Manageability log page is supported */ - if (wdc_nvme_check_supported_log_page(fd, WDC_NVME_GET_DEV_MGMNT_LOG_PAGE_OPCODE) == false) { - fprintf(stderr, "%s: ERROR : WDC : 0xC2 Log Page not supported\n", __func__); + if (wdc_nvme_check_supported_log_page(fd, log_id) == false) { + fprintf(stderr, "%s: ERROR : WDC : 0x%x Log Page not supported\n", __func__, log_id); ret = -1; goto out; } @@ -5491,51 +7026,349 @@ static int wdc_dump_telemetry_hdr(int fd, int log_id, struct nvme_telemetry_log_ return ret; } -static void wdc_print_nand_stats_normal(struct wdc_nand_stats *data) -{ - printf(" NAND Statistics :- \n"); - printf(" NAND Writes TLC (Bytes) %.0Lf\n", - int128_to_double(data->nand_write_tlc)); - printf(" NAND Writes SLC (Bytes) %.0Lf\n", - int128_to_double(data->nand_write_slc)); - printf(" NAND Program Failures %"PRIu32"\n", - (uint32_t)le32_to_cpu(data->nand_prog_failure)); - printf(" NAND Erase Failures %"PRIu32"\n", - (uint32_t)le32_to_cpu(data->nand_erase_failure)); - printf(" Bad Block Count %"PRIu32"\n", - (uint32_t)le32_to_cpu(data->bad_block_count)); - printf(" NAND XOR/RAID Recovery Trigger Events %"PRIu64"\n", - le64_to_cpu(data->nand_rec_trigger_event)); - printf(" E2E Error Counter %"PRIu64"\n", - le64_to_cpu(data->e2e_error_counter)); - printf(" Number Successful NS Resizing Events %"PRIu64"\n", - le64_to_cpu(data->successful_ns_resize_event)); +static void wdc_print_nand_stats_normal(__u16 version, void *data) +{ + struct wdc_nand_stats *nand_stats = (struct wdc_nand_stats *)(data); + struct wdc_nand_stats_V3 *nand_stats_v3 = (struct wdc_nand_stats_V3 *)(data); + __u64 temp_raw; + __u16 temp_norm; + __u64 *temp_ptr = NULL; + + switch (version) + { + case 0: + printf(" NAND Statistics :- \n"); + printf(" NAND Writes TLC (Bytes) %.0Lf\n", + int128_to_double(nand_stats->nand_write_tlc)); + printf(" NAND Writes SLC (Bytes) %.0Lf\n", + int128_to_double(nand_stats->nand_write_slc)); + printf(" NAND Program Failures %"PRIu32"\n", + (uint32_t)le32_to_cpu(nand_stats->nand_prog_failure)); + printf(" NAND Erase Failures %"PRIu32"\n", + (uint32_t)le32_to_cpu(nand_stats->nand_erase_failure)); + printf(" Bad Block Count %"PRIu32"\n", + (uint32_t)le32_to_cpu(nand_stats->bad_block_count)); + printf(" NAND XOR/RAID Recovery Trigger Events %"PRIu64"\n", + le64_to_cpu(nand_stats->nand_rec_trigger_event)); + printf(" E2E Error Counter %"PRIu64"\n", + le64_to_cpu(nand_stats->e2e_error_counter)); + printf(" Number Successful NS Resizing Events %"PRIu64"\n", + le64_to_cpu(nand_stats->successful_ns_resize_event)); + printf(" log page version %"PRIu16"\n", + le16_to_cpu(nand_stats->log_page_version)); + break; + case 3: + printf(" NAND Statistics V3:- \n"); + printf(" TLC Units Written %.0Lf\n", + int128_to_double(nand_stats_v3->nand_write_tlc)); + printf(" SLC Units Written %.0Lf\n", + int128_to_double(nand_stats_v3->nand_write_slc)); + temp_ptr = (__u64 *)nand_stats_v3->bad_nand_block_count; + temp_norm = (__u16)(*temp_ptr & 0x000000000000FFFF); + temp_raw = ((*temp_ptr & 0xFFFFFFFFFFFF0000) >> 16); + printf(" Bad NAND Blocks Count - Normalized %"PRIu16"\n", + le16_to_cpu(temp_norm)); + printf(" Bad NAND Blocks Count - Raw %"PRIu64"\n", + le64_to_cpu(temp_raw)); + printf(" NAND XOR Recovery count %"PRIu64"\n", + le64_to_cpu(nand_stats_v3->xor_recovery_count)); + printf(" UECC Read Error count %"PRIu64"\n", + le64_to_cpu(nand_stats_v3->uecc_read_error_count)); + printf(" SSD End to End corrected errors %"PRIu64"\n", + le64_to_cpu(nand_stats_v3->ssd_correction_counts[0])); + printf(" SSD End to End detected errors %"PRIu32"\n", + le32_to_cpu(nand_stats_v3->ssd_correction_counts[8])); + printf(" SSD End to End uncorrected E2E errors %"PRIu32"\n", + le32_to_cpu(nand_stats_v3->ssd_correction_counts[12])); + printf(" System data %% life-used %u\n", + nand_stats_v3->percent_life_used); + printf(" User Data Erase Counts - TLC Min %"PRIu64"\n", + le64_to_cpu(nand_stats_v3->user_data_erase_counts[0])); + printf(" User Data Erase Counts - TLC Max %"PRIu64"\n", + le64_to_cpu(nand_stats_v3->user_data_erase_counts[1])); + printf(" User Data Erase Counts - SLC Min %"PRIu64"\n", + le64_to_cpu(nand_stats_v3->user_data_erase_counts[2])); + printf(" User Data Erase Counts - SLC Max %"PRIu64"\n", + le64_to_cpu(nand_stats_v3->user_data_erase_counts[3])); + temp_ptr = (__u64 *)nand_stats_v3->program_fail_count; + temp_norm = (__u16)(*temp_ptr & 0x000000000000FFFF); + temp_raw = ((*temp_ptr & 0xFFFFFFFFFFFF0000) >> 16); + printf(" Program Fail Count - Normalized %"PRIu16"\n", + le16_to_cpu(temp_norm)); + printf(" Program Fail Count - Raw %"PRIu64"\n", + le64_to_cpu(temp_raw)); + temp_ptr = (__u64 *)nand_stats_v3->erase_fail_count; + temp_norm = (__u16)(*temp_ptr & 0x000000000000FFFF); + temp_raw = ((*temp_ptr & 0xFFFFFFFFFFFF0000) >> 16); + printf(" Erase Fail Count - Normalized %"PRIu16"\n", + le16_to_cpu(temp_norm)); + printf(" Erase Fail Count - Raw %"PRIu64"\n", + le64_to_cpu(temp_raw)); + printf(" PCIe Correctable Error Count %"PRIu16"\n", + le16_to_cpu(nand_stats_v3->correctable_error_count)); + printf(" %% Free Blocks (User) %u\n", + nand_stats_v3->percent_free_blocks_user); + printf(" Security Version Number %"PRIu64"\n", + le64_to_cpu(nand_stats_v3->security_version_number)); + printf(" %% Free Blocks (System) %u\n", + nand_stats_v3->percent_free_blocks_system); + printf(" Data Set Management Commands %.0Lf\n", + int128_to_double(nand_stats_v3->trim_completions)); + printf(" Estimate of Incomplete Trim Data %"PRIu64"\n", + le64_to_cpu(nand_stats_v3->trim_completions[16])); + printf(" %% of completed trim %u\n", + nand_stats_v3->trim_completions[24]); + printf(" Background Back-Pressure-Guage %u\n", + nand_stats_v3->back_pressure_guage); + printf(" Soft ECC Error Count %"PRIu64"\n", + le64_to_cpu(nand_stats_v3->soft_ecc_error_count)); + printf(" Refresh Count %"PRIu64"\n", + le64_to_cpu(nand_stats_v3->refresh_count)); + temp_ptr = (__u64 *)nand_stats_v3->bad_sys_nand_block_count; + temp_norm = (__u16)(*temp_ptr & 0x000000000000FFFF); + temp_raw = ((*temp_ptr & 0xFFFFFFFFFFFF0000) >> 16); + printf(" Bad System Nand Block Count - Normalized %"PRIu16"\n", + le16_to_cpu(temp_norm)); + printf(" Bad System Nand Block Count - Raw %"PRIu64"\n", + le64_to_cpu(temp_raw)); + printf(" Endurance Estimate %.0Lf\n", + int128_to_double(nand_stats_v3->endurance_estimate)); + printf(" Thermal Throttling Count %u\n", + nand_stats_v3->thermal_throttling_st_ct[0]); + printf(" Thermal Throttling Status %u\n", + nand_stats_v3->thermal_throttling_st_ct[1]); + printf(" Unaligned I/O %"PRIu64"\n", + le64_to_cpu(nand_stats_v3->unaligned_IO)); + printf(" Physical Media Units Read %.0Lf\n", + int128_to_double(nand_stats_v3->physical_media_units)); + printf(" log page version %"PRIu16"\n", + le16_to_cpu(nand_stats_v3->log_page_version)); + break; + + default: + fprintf(stderr, "WDC: Nand Stats ERROR : Invalid version\n"); + break; + + } } -static void wdc_print_nand_stats_json(struct wdc_nand_stats *data) +static void wdc_print_nand_stats_json(__u16 version, void *data) { + struct wdc_nand_stats *nand_stats = (struct wdc_nand_stats *)(data); + struct wdc_nand_stats_V3 *nand_stats_v3 = (struct wdc_nand_stats_V3 *)(data); struct json_object *root; + root = json_create_object(); + __u64 temp_raw; + __u16 temp_norm; + __u64 *temp_ptr = NULL; + + switch (version) + { + case 0: + + json_object_add_value_float(root, "NAND Writes TLC (Bytes)", + int128_to_double(nand_stats->nand_write_tlc)); + json_object_add_value_float(root, "NAND Writes SLC (Bytes)", + int128_to_double(nand_stats->nand_write_slc)); + json_object_add_value_uint(root, "NAND Program Failures", + le32_to_cpu(nand_stats->nand_prog_failure)); + json_object_add_value_uint(root, "NAND Erase Failures", + le32_to_cpu(nand_stats->nand_erase_failure)); + json_object_add_value_uint(root, "Bad Block Count", + le32_to_cpu(nand_stats->bad_block_count)); + json_object_add_value_uint(root, "NAND XOR/RAID Recovery Trigger Events", + le64_to_cpu(nand_stats->nand_rec_trigger_event)); + json_object_add_value_uint(root, "E2E Error Counter", + le64_to_cpu(nand_stats->e2e_error_counter)); + json_object_add_value_uint(root, "Number Successful NS Resizing Events", + le64_to_cpu(nand_stats->successful_ns_resize_event)); + + json_print_object(root, NULL); + printf("\n"); + break; + + case 3: + + json_object_add_value_float(root, "NAND Writes TLC (Bytes)", + int128_to_double(nand_stats_v3->nand_write_tlc)); + json_object_add_value_float(root, "NAND Writes SLC (Bytes)", + int128_to_double(nand_stats_v3->nand_write_slc)); + temp_ptr = (__u64 *)nand_stats_v3->bad_nand_block_count; + temp_norm = (__u16)(*temp_ptr & 0x000000000000FFFF); + temp_raw = ((*temp_ptr & 0xFFFFFFFFFFFF0000) >> 16); + json_object_add_value_uint(root, "Bad NAND Blocks Count - Normalized", + le16_to_cpu(temp_norm)); + json_object_add_value_uint(root, "Bad NAND Blocks Count - Raw", + le64_to_cpu(temp_raw)); + json_object_add_value_uint(root, "NAND XOR Recovery count", + le64_to_cpu(nand_stats_v3->xor_recovery_count)); + json_object_add_value_uint(root, "UECC Read Error count", + le64_to_cpu(nand_stats_v3->uecc_read_error_count)); + json_object_add_value_uint(root, "SSD End to End corrected errors", + le64_to_cpu(nand_stats_v3->ssd_correction_counts[0])); + json_object_add_value_uint(root, "SSD End to End detected errors", + le32_to_cpu(nand_stats_v3->ssd_correction_counts[8])); + json_object_add_value_uint(root, "SSD End to End uncorrected E2E errors", + le32_to_cpu(nand_stats_v3->ssd_correction_counts[12])); + json_object_add_value_uint(root, "System data % life-used", + nand_stats_v3->percent_life_used); + json_object_add_value_uint(root, "User Data Erase Counts - SLC Min", + le64_to_cpu(nand_stats_v3->user_data_erase_counts[0])); + json_object_add_value_uint(root, "User Data Erase Counts - SLC Max", + le64_to_cpu(nand_stats_v3->user_data_erase_counts[1])); + json_object_add_value_uint(root, "User Data Erase Counts - TLC Min", + le64_to_cpu(nand_stats_v3->user_data_erase_counts[2])); + json_object_add_value_uint(root, "User Data Erase Counts - TLC Max", + le64_to_cpu(nand_stats_v3->user_data_erase_counts[3])); + temp_ptr = (__u64 *)nand_stats_v3->program_fail_count; + temp_norm = (__u16)(*temp_ptr & 0x000000000000FFFF); + temp_raw = ((*temp_ptr & 0xFFFFFFFFFFFF0000) >> 16); + json_object_add_value_uint(root, "Program Fail Count - Normalized", + le16_to_cpu(temp_norm)); + json_object_add_value_uint(root, "Program Fail Count - Raw", + le64_to_cpu(temp_raw)); + temp_ptr = (__u64 *)nand_stats_v3->erase_fail_count; + temp_norm = (__u16)(*temp_ptr & 0x000000000000FFFF); + temp_raw = ((*temp_ptr & 0xFFFFFFFFFFFF0000) >> 16); + json_object_add_value_uint(root, "Erase Fail Count - Normalized", + le16_to_cpu(temp_norm)); + json_object_add_value_uint(root, "Erase Fail Count - Raw", + le64_to_cpu(temp_raw)); + json_object_add_value_uint(root, "PCIe Correctable Error Count", + le16_to_cpu(nand_stats_v3->correctable_error_count)); + json_object_add_value_uint(root, "% Free Blocks (User)", + nand_stats_v3->percent_free_blocks_user); + json_object_add_value_uint(root, "Security Version Number", + le64_to_cpu(nand_stats_v3->security_version_number)); + json_object_add_value_uint(root, "% Free Blocks (System)", + nand_stats_v3->percent_free_blocks_system); + json_object_add_value_float(root, "Data Set Management Commands", + int128_to_double(nand_stats_v3->trim_completions)); + json_object_add_value_uint(root, "Estimate of Incomplete Trim Data", + le64_to_cpu(nand_stats_v3->trim_completions[16])); + json_object_add_value_uint(root, "%% of completed trim", + nand_stats_v3->trim_completions[24]); + json_object_add_value_uint(root, "Background Back-Pressure-Guage", + nand_stats_v3->back_pressure_guage); + json_object_add_value_uint(root, "Soft ECC Error Count", + le64_to_cpu(nand_stats_v3->soft_ecc_error_count)); + json_object_add_value_uint(root, "Refresh Count", + le64_to_cpu(nand_stats_v3->refresh_count)); + temp_ptr = (__u64 *)nand_stats_v3->bad_sys_nand_block_count; + temp_norm = (__u16)(*temp_ptr & 0x000000000000FFFF); + temp_raw = ((*temp_ptr & 0xFFFFFFFFFFFF0000) >> 16); + json_object_add_value_uint(root, "Bad System Nand Block Count - Normalized", + le16_to_cpu(temp_norm)); + json_object_add_value_uint(root, "Bad System Nand Block Count - Raw", + le64_to_cpu(temp_raw)); + json_object_add_value_float(root, "Endurance Estimate", + int128_to_double(nand_stats_v3->endurance_estimate)); + json_object_add_value_uint(root, "Thermal Throttling Status", + nand_stats_v3->thermal_throttling_st_ct[0]); + json_object_add_value_uint(root, "Thermal Throttling Count", + nand_stats_v3->thermal_throttling_st_ct[1]); + json_object_add_value_uint(root, "Unaligned I/O", + le64_to_cpu(nand_stats_v3->unaligned_IO)); + json_object_add_value_float(root, "Physical Media Units Read", + int128_to_double(nand_stats_v3->physical_media_units)); + json_object_add_value_uint(root, "log page version", + le16_to_cpu(nand_stats_v3->log_page_version)); + + json_print_object(root, NULL); + printf("\n"); + break; + + default: + printf("%s: Invalid Stats Version = %d\n", __func__, version); + break; + + } + + json_free_object(root); + +} + +static void wdc_print_pcie_stats_normal(struct wdc_vs_pcie_stats *pcie_stats) +{ + printf(" PCIE Statistics :- \n"); + printf(" Unsupported Request Error Counter %20"PRIu64"\n", + le64_to_cpu(pcie_stats->unsupportedRequestErrorCount)); + printf(" ECRC Error Status Counter %20"PRIu64"\n", + le64_to_cpu(pcie_stats->ecrcErrorStatusCount)); + printf(" Malformed TLP Status Counter %20"PRIu64"\n", + le64_to_cpu(pcie_stats->malformedTlpStatusCount)); + printf(" Receiver Overflow Status Counter %20"PRIu64"\n", + le64_to_cpu(pcie_stats->receiverOverflowStatusCount)); + printf(" Unexpected Completion Status Counter %20"PRIu64"\n", + le64_to_cpu(pcie_stats->unexpectedCmpltnStatusCount)); + printf(" Complete Abort Status Counter %20"PRIu64"\n", + le64_to_cpu(pcie_stats->completeAbortStatusCount)); + printf(" Completion Timeout Status Counter %20"PRIu64"\n", + le64_to_cpu(pcie_stats->cmpltnTimoutStatusCount)); + printf(" Flow Control Error Status Counter %20"PRIu64"\n", + le64_to_cpu(pcie_stats->flowControlErrorStatusCount)); + printf(" Poisoned TLP Status Counter %20"PRIu64"\n", + le64_to_cpu(pcie_stats->poisonedTlpStatusCount)); + printf(" Dlink Protocol Error Status Counter %20"PRIu64"\n", + le64_to_cpu(pcie_stats->dLinkPrtclErrorStatusCount)); + printf(" Advisory Non Fatal Error Status Counter %20"PRIu64"\n", + le64_to_cpu(pcie_stats->advsryNFatalErrStatusCount)); + printf(" Replay Timer TO Status Counter %20"PRIu64"\n", + le64_to_cpu(pcie_stats->replayTimerToStatusCount)); + printf(" Replay Number Rollover Status Counter %20"PRIu64"\n", + le64_to_cpu(pcie_stats->replayNumRolloverStCount)); + printf(" Bad DLLP Status Counter %20"PRIu64"\n", + le64_to_cpu(pcie_stats->badDllpStatusCount)); + printf(" Bad TLP Status Counter %20"PRIu64"\n", + le64_to_cpu(pcie_stats->badTlpStatusCount)); + printf(" Receiver Error Status Counter %20"PRIu64"\n", + le64_to_cpu(pcie_stats->receiverErrStatusCount)); + +} + +static void wdc_print_pcie_stats_json(struct wdc_vs_pcie_stats *pcie_stats) +{ + struct json_object *root; root = json_create_object(); - json_object_add_value_float(root, "NAND Writes TLC (Bytes)", - int128_to_double(data->nand_write_tlc)); - json_object_add_value_float(root, "NAND Writes SLC (Bytes)", - int128_to_double(data->nand_write_slc)); - json_object_add_value_uint(root, "NAND Program Failures", - le32_to_cpu(data->nand_prog_failure)); - json_object_add_value_uint(root, "NAND Erase Failures", - le32_to_cpu(data->nand_erase_failure)); - json_object_add_value_uint(root, "Bad Block Count", - le32_to_cpu(data->bad_block_count)); - json_object_add_value_uint(root, "NAND XOR/RAID Recovery Trigger Events", - le64_to_cpu(data->nand_rec_trigger_event)); - json_object_add_value_uint(root, "E2E Error Counter", - le64_to_cpu(data->e2e_error_counter)); - json_object_add_value_uint(root, "Number Successful NS Resizing Events", - le64_to_cpu(data->successful_ns_resize_event)); + + json_object_add_value_uint(root, "Unsupported Request Error Counter", + le64_to_cpu(pcie_stats->unsupportedRequestErrorCount)); + json_object_add_value_uint(root, "ECRC Error Status Counter", + le64_to_cpu(pcie_stats->ecrcErrorStatusCount)); + json_object_add_value_uint(root, "Malformed TLP Status Counter", + le64_to_cpu(pcie_stats->malformedTlpStatusCount)); + + json_object_add_value_uint(root, "Receiver Overflow Status Counter", + le64_to_cpu(pcie_stats->receiverOverflowStatusCount)); + json_object_add_value_uint(root, "Unexpected Completion Status Counter", + le64_to_cpu(pcie_stats->unexpectedCmpltnStatusCount)); + json_object_add_value_uint(root, "Complete Abort Status Counter", + le64_to_cpu(pcie_stats->completeAbortStatusCount)); + json_object_add_value_uint(root, "Completion Timeout Status Counter", + le64_to_cpu(pcie_stats->cmpltnTimoutStatusCount)); + json_object_add_value_uint(root, "Flow Control Error Status Counter", + le64_to_cpu(pcie_stats->flowControlErrorStatusCount)); + json_object_add_value_uint(root, "Poisoned TLP Status Counter", + le64_to_cpu(pcie_stats->poisonedTlpStatusCount)); + json_object_add_value_uint(root, "Dlink Protocol Error Status Counter", + le64_to_cpu(pcie_stats->dLinkPrtclErrorStatusCount)); + json_object_add_value_uint(root, "Advisory Non Fatal Error Status Counter", + le64_to_cpu(pcie_stats->advsryNFatalErrStatusCount)); + json_object_add_value_uint(root, "Replay Timer TO Status Counter", + le64_to_cpu(pcie_stats->replayTimerToStatusCount)); + json_object_add_value_uint(root, "Replay Number Rollover Status Counter", + le64_to_cpu(pcie_stats->replayNumRolloverStCount)); + json_object_add_value_uint(root, "Bad DLLP Status Counter", + le64_to_cpu(pcie_stats->badDllpStatusCount)); + json_object_add_value_uint(root, "Bad TLP Status Counter", + le64_to_cpu(pcie_stats->badTlpStatusCount)); + json_object_add_value_uint(root, "Receiver Error Status Counter", + le64_to_cpu(pcie_stats->receiverErrStatusCount)); json_print_object(root, NULL); printf("\n"); + json_free_object(root); } @@ -5544,7 +7377,7 @@ static int wdc_do_vs_nand_stats(int fd, char *format) int ret; int fmt = -1; uint8_t *output = NULL; - struct wdc_nand_stats *nand_stats; + __u16 version = 0; if ((output = (uint8_t*)calloc(WDC_NVME_NAND_STATS_SIZE, sizeof(uint8_t))) == NULL) { fprintf(stderr, "ERROR : WDC : calloc : %s\n", strerror(errno)); @@ -5553,7 +7386,7 @@ static int wdc_do_vs_nand_stats(int fd, char *format) } ret = nvme_get_log(fd, 0xFFFFFFFF, WDC_NVME_NAND_STATS_LOG_ID, - false, WDC_NVME_NAND_STATS_SIZE, (void*)output); + false, NVME_NO_LOG_LSP, WDC_NVME_NAND_STATS_SIZE, (void*)output); if (ret) { fprintf(stderr, "ERROR : WDC : %s : Failed to retreive NAND stats\n", __func__); goto out; @@ -5565,14 +7398,15 @@ static int wdc_do_vs_nand_stats(int fd, char *format) goto out; } + version = output[WDC_NVME_NAND_STATS_SIZE - 2]; + /* parse the data */ - nand_stats = (struct wdc_nand_stats *)(output); switch (fmt) { case NORMAL: - wdc_print_nand_stats_normal(nand_stats); + wdc_print_nand_stats_normal(version, output); break; case JSON: - wdc_print_nand_stats_json(nand_stats); + wdc_print_nand_stats_json(version, output); break; } } @@ -5586,6 +7420,7 @@ static int wdc_vs_nand_stats(int argc, char **argv, struct command *command, struct plugin *plugin) { const char *desc = "Retrieve NAND statistics."; + int fd; int ret = 0; __u64 capabilities = 0; @@ -5621,6 +7456,97 @@ static int wdc_vs_nand_stats(int argc, char **argv, struct command *command, return ret; } +static int wdc_do_vs_pcie_stats(int fd, + struct wdc_vs_pcie_stats *pcieStatsPtr) +{ + int ret; + struct nvme_admin_cmd admin_cmd; + int pcie_stats_size = sizeof(struct wdc_vs_pcie_stats); + + memset(&admin_cmd, 0, sizeof (struct nvme_admin_cmd)); + admin_cmd.opcode = WDC_NVME_PCIE_STATS_OPCODE; + admin_cmd.addr = (__u64)(uintptr_t)pcieStatsPtr; + admin_cmd.data_len = pcie_stats_size; + + ret = nvme_submit_admin_passthru(fd, &admin_cmd); + + return ret; +} + +static int wdc_vs_pcie_stats(int argc, char **argv, struct command *command, + struct plugin *plugin) +{ + const char *desc = "Retrieve PCIE statistics."; + + int fd; + int ret = 0; + __u64 capabilities = 0; + int fmt = -1; + struct wdc_vs_pcie_stats *pcieStatsPtr = NULL; + int pcie_stats_size = sizeof(struct wdc_vs_pcie_stats); + bool huge; + + struct config { + char *output_format; + }; + + struct config cfg = { + .output_format = "normal", + }; + + OPT_ARGS(opts) = { + OPT_FMT("output-format", 'o', &cfg.output_format, "Output Format: normal|json"), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + return fd; + + fmt = validate_output_format(cfg.output_format); + if (fmt < 0) { + fprintf(stderr, "ERROR : WDC : invalid output format\n"); + ret = fmt; + goto out; + } + + pcieStatsPtr = nvme_alloc(pcie_stats_size, &huge); + if (pcieStatsPtr == NULL) { + fprintf(stderr, "ERROR : WDC : PCIE Stats alloc : %s\n", strerror(errno)); + ret = -1; + goto out; + } + + memset((void *)pcieStatsPtr, 0, pcie_stats_size); + + capabilities = wdc_get_drive_capabilities(fd); + + if ((capabilities & WDC_DRIVE_CAP_PCIE_STATS) == 0) { + fprintf(stderr, "ERROR : WDC: unsupported device for this command\n"); + ret = -1; + } else { + ret = wdc_do_vs_pcie_stats(fd, pcieStatsPtr); + if (ret) + fprintf(stderr, "ERROR : WDC : Failure reading PCIE statistics, ret = 0x%x\n", ret); + else { + /* parse the data */ + switch (fmt) { + case NORMAL: + wdc_print_pcie_stats_normal(pcieStatsPtr); + break; + case JSON: + wdc_print_pcie_stats_json(pcieStatsPtr); + break; + } + } + } + + nvme_free(pcieStatsPtr, huge); + +out: + return ret; +} + static int wdc_vs_drive_info(int argc, char **argv, struct command *command, struct plugin *plugin) { @@ -5630,8 +7556,24 @@ static int wdc_vs_drive_info(int argc, char **argv, __le32 result; __u16 size; double rev; + struct nvme_id_ctrl ctrl; + char vsData[32] = {0}; + char major_rev = 0, minor_rev = 0; + int fmt = -1; + struct json_object *root = NULL; + char formatter[41] = { 0 }; + char rev_str[8] = { 0 }; + + struct config { + char *output_format; + }; + + struct config cfg = { + .output_format = "normal", + }; OPT_ARGS(opts) = { + OPT_FMT("output-format", 'o', &cfg.output_format, "Output Format: normal|json"), OPT_END() }; @@ -5639,25 +7581,558 @@ static int wdc_vs_drive_info(int argc, char **argv, if (fd < 0) return fd; + fmt = validate_output_format(cfg.output_format); + if (fmt < 0) { + fprintf(stderr, "ERROR : WDC %s invalid output format\n", __func__); + return fmt; + } + + /* get the id ctrl data used to fill in drive info below */ + ret = nvme_identify_ctrl(fd, &ctrl); + + if (ret) { + fprintf(stderr, "ERROR : WDC %s: Identify Controller failed\n", __func__); + return ret; + } + wdc_check_device(fd); capabilities = wdc_get_drive_capabilities(fd); if ((capabilities & WDC_DRIVE_CAP_INFO) == WDC_DRIVE_CAP_INFO) { ret = wdc_do_drive_info(fd, &result); + + if (!ret) { + size = (__u16)((cpu_to_le32(result) & 0xffff0000) >> 16); + rev = (double)(cpu_to_le32(result) & 0x0000ffff); + + if (fmt == NORMAL) { + printf("Drive HW Revison: %4.1f\n", (.1 * rev)); + printf("FTL Unit Size: 0x%x KB\n", size); + printf("Customer SN: %-.*s\n", (int)sizeof(ctrl.sn), &ctrl.sn[0]); + } + else if (fmt == JSON) { + root = json_create_object(); + sprintf(rev_str, "%4.1f", (.1 * rev)); + json_object_add_value_string(root, "Drive HW Revison", rev_str); + + json_object_add_value_int(root, "FTL Unit Size", le16_to_cpu(size)); + wdc_StrFormat(formatter, sizeof(formatter), &ctrl.sn[0], sizeof(ctrl.sn)); + json_object_add_value_string(root, "Customer SN", formatter); + + json_print_object(root, NULL); + printf("\n"); + + json_free_object(root); + } + } + } + else if ((capabilities & WDC_DRIVE_CAP_INFO_2) == WDC_DRIVE_CAP_INFO_2) { + memcpy(vsData, &ctrl.vs[0], 32); + + major_rev = ctrl.sn[12]; + minor_rev = ctrl.sn[13]; + + if (fmt == NORMAL) { + printf("Drive HW Revision: %c.%c \n", major_rev, minor_rev); + printf("Customer SN: %-.*s\n", 14, &ctrl.sn[0]); + } + else if (fmt == JSON) { + root = json_create_object(); + sprintf(rev_str, "%c.%c", major_rev, minor_rev); + json_object_add_value_string(root, "Drive HW Revison", rev_str); + wdc_StrFormat(formatter, sizeof(formatter), &ctrl.sn[0], 14); + json_object_add_value_string(root, "Customer SN", formatter); + + json_print_object(root, NULL); + printf("\n"); + + json_free_object(root); + } } else { fprintf(stderr, "ERROR : WDC: unsupported device for this command\n"); - ret = -1; + return -1; } - if (!ret) { - size = (__u16)((cpu_to_le32(result) & 0xffff0000) >> 16); - rev = (double)(cpu_to_le32(result) & 0x0000ffff); - printf("Drive HW Revison: %4.1f\n", (.1 * rev)); - printf("FTL Unit Size: 0x%x KB\n", size); + fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret); + return ret; +} + +static int wdc_vs_temperature_stats(int argc, char **argv, + struct command *command, struct plugin *plugin) +{ + const char *desc = "Send a vs-temperature-stats command."; + struct nvme_smart_log smart_log; + struct nvme_id_ctrl id_ctrl; + uint64_t capabilities = 0; + __u32 hctm_tmt; + int fd, ret; + int temperature, temp_tmt1, temp_tmt2; + int fmt = -1; + + struct config { + char *output_format; + }; + + struct config cfg = { + .output_format = "normal", + }; + + OPT_ARGS(opts) = { + OPT_FMT("output-format", 'o', &cfg.output_format, "Output Format: normal|json"), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + return fd; + + fmt = validate_output_format(cfg.output_format); + if (fmt < 0) { + fprintf(stderr, "ERROR : WDC : invalid output format\n"); + ret = fmt; + goto END; } + /* check if command is supported */ + wdc_check_device(fd); + capabilities = wdc_get_drive_capabilities(fd); + if ((capabilities & WDC_DRIVE_CAP_TEMP_STATS) != WDC_DRIVE_CAP_TEMP_STATS) { + fprintf(stderr, "ERROR : WDC: unsupported device for this command\n"); + return -1; + } + + /* get the temperature stats or report errors */ + ret = nvme_identify_ctrl(fd, &id_ctrl); + if (ret != 0) + goto END; + ret = nvme_smart_log(fd, NVME_NSID_ALL, &smart_log); + if (ret != 0) + goto END; + + /* convert from Kelvin to degrees Celsius */ + temperature = ((smart_log.temperature[1] << 8) | smart_log.temperature[0]) - 273; + + /* retrieve HCTM Thermal Management Temperatures */ + nvme_get_feature(fd, 0, 0x10, 0, 0, 0, 0, &hctm_tmt); + temp_tmt1 = ((hctm_tmt >> 16) & 0xffff) ? ((hctm_tmt >> 16) & 0xffff) - 273 : 0; + temp_tmt2 = (hctm_tmt & 0xffff) ? (hctm_tmt & 0xffff) - 273 : 0; + + if (fmt == NORMAL) { + /* print the temperature stats */ + printf("Temperature Stats for NVME device:%s namespace-id:%x\n", + devicename, WDC_DE_GLOBAL_NSID); + + printf("Current Composite Temperature : %d °C\n", temperature); + printf("WCTEMP : %"PRIu16" °C\n", id_ctrl.wctemp - 273); + printf("CCTEMP : %"PRIu16" °C\n", id_ctrl.cctemp - 273); + printf("DITT support : 0\n"); + printf("HCTM support : %"PRIu16"\n", id_ctrl.hctma); + + printf("HCTM Light (TMT1) : %"PRIu16" °C\n", temp_tmt1); + printf("TMT1 Transition Counter : %"PRIu32"\n", smart_log.thm_temp1_trans_count); + printf("TMT1 Total Time : %"PRIu32"\n", smart_log.thm_temp1_total_time); + + printf("HCTM Heavy (TMT2) : %"PRIu16" °C\n", temp_tmt2); + printf("TMT2 Transition Counter : %"PRIu32"\n", smart_log.thm_temp2_trans_count); + printf("TMT2 Total Time : %"PRIu32"\n", smart_log.thm_temp2_total_time); + printf("Thermal Shutdown Threshold : 95 °C\n"); + } + else if (fmt == JSON) { + struct json_object *root; + root = json_create_object(); + + json_object_add_value_int(root, "Current Composite Temperature", le32_to_cpu(temperature)); + json_object_add_value_int(root, "WCTEMP", le16_to_cpu(id_ctrl.wctemp - 273)); + json_object_add_value_int(root, "CCTEMP", le16_to_cpu(id_ctrl.cctemp - 273)); + json_object_add_value_int(root, "DITT support", 0); + json_object_add_value_int(root, "HCTM support", le16_to_cpu(id_ctrl.hctma)); + + json_object_add_value_int(root, "HCTM Light (TMT1)", le16_to_cpu(temp_tmt1)); + json_object_add_value_int(root, "TMT1 Transition Counter", le32_to_cpu(smart_log.thm_temp1_trans_count)); + json_object_add_value_int(root, "TMT1 Total Time", le32_to_cpu(smart_log.thm_temp1_total_time)); + + json_object_add_value_int(root, "HCTM Light (TMT2)", le16_to_cpu(temp_tmt2)); + json_object_add_value_int(root, "TMT2 Transition Counter", le32_to_cpu(smart_log.thm_temp2_trans_count)); + json_object_add_value_int(root, "TMT2 Total Time", le32_to_cpu(smart_log.thm_temp2_total_time)); + json_object_add_value_int(root, "Thermal Shutdown Threshold", 95); + + json_print_object(root, NULL); + printf("\n"); + + json_free_object(root); + } + else + printf("%s: Invalid format\n", __func__); + +END: fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret); return ret; } +static int wdc_capabilities(int argc, char **argv, + struct command *command, struct plugin *plugin) +{ + const char *desc = "Send a capabilities command."; + uint64_t capabilities = 0; + int fd; + + OPT_ARGS(opts) = + { + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + return fd; + + /* get capabilities */ + wdc_check_device(fd); + capabilities = wdc_get_drive_capabilities(fd); + + /* print command and supported status */ + printf("WDC Plugin Capabilities for NVME device:%s\n", devicename); + printf("cap-diag : %s\n", + capabilities & WDC_DRIVE_CAP_CAP_DIAG ? "Supported" : "Not Supported"); + printf("drive-log : %s\n", + capabilities & WDC_DRIVE_CAP_DRIVE_LOG ? "Supported" : "Not Supported"); + printf("get-crash-dump : %s\n", + capabilities & WDC_DRIVE_CAP_CRASH_DUMP ? "Supported" : "Not Supported"); + printf("get-pfail-dump : %s\n", + capabilities & WDC_DRIVE_CAP_PFAIL_DUMP ? "Supported" : "Not Supported"); + printf("id-ctrl : Supported\n"); + printf("purge : Supported\n"); + printf("purge-monitor : Supported\n"); + printf("vs-internal-log : %s\n", + capabilities & WDC_DRIVE_CAP_INTERNAL_LOG_MASK ? "Supported" : "Not Supported"); + printf("vs-nand-stats : %s\n", + capabilities & WDC_DRIVE_CAP_NAND_STATS ? "Supported" : "Not Supported"); + printf("vs-smart-add-log : %s\n", + capabilities & WDC_DRIVE_CAP_SMART_LOG_MASK ? "Supported" : "Not Supported"); + printf("--C0 Log Page : %s\n", + capabilities & WDC_DRIVE_CAP_C0_LOG_PAGE ? "Supported" : "Not Supported"); + printf("--C1 Log Page : %s\n", + capabilities & WDC_DRIVE_CAP_C1_LOG_PAGE ? "Supported" : "Not Supported"); + printf("--CA Log Page : %s\n", + capabilities & WDC_DRIVE_CAP_CA_LOG_PAGE ? "Supported" : "Not Supported"); + printf("--D0 Log Page : %s\n", + capabilities & WDC_DRIVE_CAP_D0_LOG_PAGE ? "Supported" : "Not Supported"); + printf("clear-pcie-correctable-errors : %s\n", + capabilities & WDC_DRIVE_CAP_CLEAR_PCIE_MASK ? "Supported" : "Not Supported"); + printf("drive-essentials : %s\n", + capabilities & WDC_DRIVE_CAP_DRIVE_ESSENTIALS ? "Supported" : "Not Supported"); + printf("get-drive-status : %s\n", + capabilities & WDC_DRIVE_CAP_DRIVE_STATUS ? "Supported" : "Not Supported"); + printf("clear-assert-dump : %s\n", + capabilities & WDC_DRIVE_CAP_CLEAR_ASSERT ? "Supported" : "Not Supported"); + printf("drive-resize : %s\n", + capabilities & WDC_DRIVE_CAP_RESIZE ? "Supported" : "Not Supported"); + printf("vs-fw-activate-history : %s\n", + capabilities & WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY_MASK ? "Supported" : "Not Supported"); + printf("clear-fw-activate-history : %s\n", + capabilities & WDC_DRIVE_CAP_CLEAR_FW_ACT_HISTORY_MASK ? "Supported" : "Not Supported"); + printf("vs-telemetry-controller-option: %s\n", + capabilities & WDC_DRVIE_CAP_DISABLE_CTLR_TELE_LOG ? "Supported" : "Not Supported"); + printf("vs-error-reason-identifier : %s\n", + capabilities & WDC_DRIVE_CAP_REASON_ID ? "Supported" : "Not Supported"); + printf("log-page-directory : %s\n", + capabilities & WDC_DRIVE_CAP_LOG_PAGE_DIR ? "Supported" : "Not Supported"); + printf("namespace-resize : %s\n", + capabilities & WDC_DRIVE_CAP_NS_RESIZE ? "Supported" : "Not Supported"); + printf("vs-drive-info : %s\n", + capabilities & (WDC_DRIVE_CAP_INFO | WDC_DRIVE_CAP_INFO_2) ? "Supported" : "Not Supported"); + printf("vs-temperature-stats : %s\n", + capabilities & WDC_DRIVE_CAP_TEMP_STATS ? "Supported" : "Not Supported"); + printf("cloud-SSD-plugin-version : %s\n", + capabilities & WDC_DRIVE_CAP_CLOUD_SSD_VERSION ? "Supported" : "Not Supported"); + printf("vs-pcie-stats : %s\n", + capabilities & WDC_DRIVE_CAP_PCIE_STATS ? "Supported" : "Not Supported"); + printf("capabilities : Supported\n"); + return 0; +} + +static int wdc_cloud_ssd_plugin_version(int argc, char **argv, + struct command *command, struct plugin *plugin) +{ + const char *desc = "Get Cloud SSD Plugin Version command."; + uint64_t capabilities = 0; + int fd; + + OPT_ARGS(opts) = + { + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + return fd; + + /* get capabilities */ + wdc_check_device(fd); + capabilities = wdc_get_drive_capabilities(fd); + + if ((capabilities & WDC_DRIVE_CAP_CLOUD_SSD_VERSION) == WDC_DRIVE_CAP_CLOUD_SSD_VERSION) { + /* print command and supported status */ + printf("WDC Cloud SSD Plugin Version: 1.0\n"); + } else { + fprintf(stderr, "ERROR : WDC: unsupported device for this command\n"); + } + + return 0; +} +static int wdc_enc_get_log(int argc, char **argv, struct command *command, + struct plugin *plugin) +{ + char *desc = "Get Enclosure Log."; + char *file = "Output file pathname."; + char *size = "Data retrieval transfer size."; + char *log = "Enclosure Log Page ID."; + FILE *output_fd; + int xfer_size = 0; + int fd; + int len; + int err; + + struct config { + char *file; + __u32 xfer_size; + __u32 log_id; + }; + + struct config cfg = { + .file = NULL, + .xfer_size = 0, + .log_id = 0xffffffff, + }; + + OPT_ARGS(opts) = { + OPT_FILE("output-file", 'o', &cfg.file, file), + OPT_UINT("transfer-size", 's', &cfg.xfer_size, size), + OPT_UINT("log-id", 'l', &cfg.log_id, log), + OPT_END() + }; + + err = fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) { + goto ret; + } + + if (!wdc_enc_check_model(fd)) { + err = -EINVAL; + goto closed_fd; + } + + if (cfg.log_id > 0xff) { + fprintf(stderr, "Invalid log identifier: %d. Valid 0xd1, 0xd2, 0xd3, 0xd4, 0xe2, 0xe4\n", cfg.log_id); + goto closed_fd; + } + + if (cfg.xfer_size != 0) { + xfer_size = cfg.xfer_size; + if (!wdc_check_power_of_2(cfg.xfer_size)) { + fprintf(stderr, "%s: ERROR : xfer-size (%d) must be a power of 2\n", __func__, cfg.xfer_size); + err = -EINVAL; + goto closed_fd; + } + } + + /* Log IDs are only for specific enclosures */ + if (cfg.log_id) { + xfer_size = (xfer_size) ? xfer_size : WDC_NVME_ENC_LOG_SIZE_CHUNK; + len = cfg.file==NULL?0:strlen(cfg.file); + if (len > 0) { + output_fd = fopen(cfg.file,"wb"); + if (output_fd == 0) { + fprintf(stderr, "%s: ERROR : opening:%s : %s\n", __func__,cfg.file, strerror(errno)); + err = -EINVAL; + goto closed_fd; + } + } else { + output_fd = stdout; + } + if (cfg.log_id == WDC_ENC_NIC_CRASH_DUMP_ID_SLOT_1 || cfg.log_id == WDC_ENC_NIC_CRASH_DUMP_ID_SLOT_2 + || cfg.log_id == WDC_ENC_NIC_CRASH_DUMP_ID_SLOT_3 || cfg.log_id == WDC_ENC_NIC_CRASH_DUMP_ID_SLOT_4) { + fprintf(stderr, "args - sz:%x logid:%x of:%s\n",xfer_size,cfg.log_id,cfg.file); + err = wdc_enc_get_nic_log(fd, cfg.log_id, xfer_size, WDC_NVME_ENC_NIC_LOG_SIZE, output_fd); + } else { + fprintf(stderr, "args - sz:%x logid:%x of:%s\n",xfer_size,cfg.log_id,cfg.file); + err = wdc_enc_submit_move_data(fd, NULL, 0, xfer_size, output_fd, cfg.log_id, 0, 0); + } + + if (err == WDC_RESULT_NOT_AVAILABLE) { + fprintf(stderr, "No Log/Crashdump available\n"); + err = 0; + } else if (err) { + fprintf(stderr, "ERROR:0x%x Failed to collect log-id:%x \n",err, cfg.log_id); + } + } +closed_fd: + close(fd); +ret: + return nvme_status_to_errno(err, false); +} + +static int wdc_enc_submit_move_data(int fd, char *cmd, int len, int xfer_size, FILE *out, int log_id, int cdw14, int cdw15) +{ + struct timespec time; + uint32_t response_size, more; + int err; + int handle; + uint32_t offset = 0; + char *buf; + + buf = (char *)malloc(sizeof(__u8) * xfer_size); + if (buf == NULL) { + fprintf(stderr, "%s: ERROR : malloc : %s\n", __func__, strerror(errno)); + return -1; + } + /* send something no matter what */ + cmd = (len) ? cmd : buf; + len = (len) ? len : 0x20; + + struct nvme_admin_cmd nvme_cmd = { + .opcode = WDC_NVME_ADMIN_ENC_MGMT_SND, + .nsid = 0, + .addr = (__u64)(uintptr_t) cmd, + .data_len = ((len + sizeof(uint32_t) - 1)/sizeof(uint32_t)) * sizeof(uint32_t), + .cdw10 = len, + .cdw12 = log_id, + .cdw13 = 0, + .cdw14 = cdw14, + .cdw15 = cdw15, + }; + + clock_gettime(CLOCK_REALTIME, &time); + srand(time.tv_nsec); + handle = random(); /* Handle to associate send request with receive request */ + nvme_cmd.cdw11 = handle; + +#ifdef WDC_NVME_CLI_DEBUG + unsigned char *d = (unsigned char*) nvme_cmd.addr; + unsigned char *md = (unsigned char*) nvme_cmd.metadata; + printf("NVME_ADMIN_COMMAND:\n" \ + "opcode: 0x%02x, flags: 0x%02x, rsvd: 0x%04x, nsid: 0x%08x, cdw2: 0x%08x, cdw3: 0x%08x, " \ + "metadata_len: 0x%08x, data_len: 0x%08x, cdw10: 0x%08x, cdw11: 0x%08x, cdw12: 0x%08x, " \ + "cdw13: 0x%08x, cdw14: 0x%08x, cdw15: 0x%08x, timeout_ms: 0x%08x, result: 0x%08x, " \ + "metadata: %s, " \ + "data: %s\n", \ + nvme_cmd.opcode, nvme_cmd.flags, nvme_cmd.rsvd1, nvme_cmd.nsid, nvme_cmd.cdw2, nvme_cmd.cdw3, \ + nvme_cmd.metadata_len, nvme_cmd.data_len, nvme_cmd.cdw10, nvme_cmd.cdw11, nvme_cmd.cdw12, \ + nvme_cmd.cdw13, nvme_cmd.cdw14, nvme_cmd.cdw15, nvme_cmd.timeout_ms, nvme_cmd.result, + md, \ + d); +#endif + nvme_cmd.result = 0; + err = nvme_submit_passthru(fd, NVME_IOCTL_ADMIN_CMD, &nvme_cmd); + if (err == NVME_SC_INTERNAL) { + fprintf(stderr, "%s: WARNING : WDC : No log ID:x%x available\n", + __func__, log_id); + } + else if (err != 0) { + fprintf(stderr, "%s: ERROR : WDC : NVMe Snd Mgmt Status:%s(x%x)\n", + __func__, nvme_status_to_string(err), err ); + } else { + if (nvme_cmd.result == WDC_RESULT_NOT_AVAILABLE) + { + free(buf); + return WDC_RESULT_NOT_AVAILABLE; + } + + do { + /* Sent request, now go retrieve response */ + nvme_cmd.flags = 0; + nvme_cmd.opcode = WDC_NVME_ADMIN_ENC_MGMT_RCV; + nvme_cmd.addr = (__u64)(uintptr_t) buf; + nvme_cmd.data_len = xfer_size; + nvme_cmd.cdw10 = xfer_size / sizeof(uint32_t); + nvme_cmd.cdw11 = handle; + nvme_cmd.cdw12 = log_id; + nvme_cmd.cdw13 = offset / sizeof(uint32_t); + nvme_cmd.cdw14 = cdw14; + nvme_cmd.cdw15 = cdw15; + nvme_cmd.result = 0; /* returned result !=0 indicates more data available */ + err = nvme_submit_passthru(fd, NVME_IOCTL_ADMIN_CMD, &nvme_cmd); + if (err != 0) { + more = 0; + fprintf(stderr, "%s: ERROR : WDC : NVMe Rcv Mgmt Status:%s(x%x)\n", + __func__, nvme_status_to_string(err), err); + } else { + more = nvme_cmd.result & WDC_RESULT_MORE_DATA; + response_size = nvme_cmd.result & ~WDC_RESULT_MORE_DATA; + fwrite(buf, response_size, 1, out); + offset += response_size; + if (more && (response_size & (sizeof(uint32_t)-1))) { + fprintf(stderr, "%s: ERROR : WDC : NVMe Rcv Mgmt response size:x%x not LW aligned\n", + __func__, response_size); + } + } + } while (more); + free(buf); + } + + return err; +} + +static int wdc_enc_get_nic_log(int fd, __u8 log_id, __u32 xfer_size, __u32 data_len, FILE *out) +{ + __u8 *dump_data; + __u32 curr_data_offset, curr_data_len; + int i, ret; + struct nvme_admin_cmd admin_cmd; + __u32 dump_length = data_len; + __u32 numd; + __u16 numdu, numdl; + + dump_data = (__u8 *) malloc(sizeof (__u8) * dump_length); + if (dump_data == NULL) { + fprintf(stderr, "%s: ERROR : malloc : %s\n",__func__, strerror(errno)); + return -1; + } + memset(dump_data, 0, sizeof (__u8) * dump_length); + memset(&admin_cmd, 0, sizeof (struct nvme_admin_cmd)); + curr_data_offset = 0; + curr_data_len = xfer_size; + i = 0; + + numd = (curr_data_len >> 2) - 1; + numdu = numd >> 16; + numdl = numd & 0xffff; + admin_cmd.opcode = nvme_admin_get_log_page; + admin_cmd.nsid = curr_data_offset; + admin_cmd.addr = (__u64)(uintptr_t) dump_data; + admin_cmd.data_len = curr_data_len; + admin_cmd.cdw10 = log_id | (numdl << 16); + admin_cmd.cdw11 = numdu; + + while (curr_data_offset < data_len) { +#ifdef WDC_NVME_CLI_DEBUG + fprintf(stderr, "nsid 0x%08x addr 0x%08llx, data_len 0x%08x, cdw10 0x%08x, cdw11 0x%08x, cdw12 0x%08x, cdw13 0x%08x, cdw14 0x%08x \n", admin_cmd.nsid, admin_cmd.addr, admin_cmd.data_len, admin_cmd.cdw10, admin_cmd.cdw11, admin_cmd.cdw12, admin_cmd.cdw13, admin_cmd.cdw14); +#endif + ret = nvme_submit_admin_passthru(fd, &admin_cmd); + if (ret !=0 ) { + fprintf(stderr, "%s: ERROR : WDC : NVMe Status:%s(%x)\n",__func__, nvme_status_to_string(ret), ret); + fprintf(stderr, "%s: ERROR : WDC : Get chunk %d, size = 0x%x, offset = 0x%x, addr = 0x%lx\n", + __func__, i, admin_cmd.data_len, curr_data_offset, (long unsigned int)admin_cmd.addr); + break; + } + + if ((curr_data_offset + xfer_size) <= data_len) + curr_data_len = xfer_size; + else + curr_data_len = data_len - curr_data_offset; /* last transfer */ + + curr_data_offset += curr_data_len; + numd = (curr_data_len >> 2) - 1; + numdu = numd >> 16; + numdl = numd & 0xffff; + admin_cmd.addr = (__u64)(uintptr_t)dump_data + (__u64)curr_data_offset; + admin_cmd.nsid = curr_data_offset; + admin_cmd.data_len = curr_data_len; + admin_cmd.cdw10 = log_id | (numdl << 16); + admin_cmd.cdw11 = numdu; + i++; + } + fwrite(dump_data, data_len, 1, out); + free(dump_data); + return ret; +} diff --git a/plugins/wdc/wdc-nvme.h b/plugins/wdc/wdc-nvme.h index c6b2d3f..de6affa 100644 --- a/plugins/wdc/wdc-nvme.h +++ b/plugins/wdc/wdc-nvme.h @@ -25,11 +25,16 @@ PLUGIN(NAME("wdc", "Western Digital vendor specific extensions"), ENTRY("drive-resize", "WDC Drive Resize", wdc_drive_resize) ENTRY("vs-fw-activate-history", "WDC Get FW Activate History", wdc_vs_fw_activate_history) ENTRY("clear-fw-activate-history", "WDC Clear FW Activate History", wdc_clear_fw_activate_history) + ENTRY("enc-get-log", "WDC Get Enclosure Log", wdc_enc_get_log) ENTRY("vs-telemetry-controller-option", "WDC Enable/Disable Controller Initiated Telemetry Log", wdc_vs_telemetry_controller_option) ENTRY("vs-error-reason-identifier", "WDC Telemetry Reason Identifier", wdc_reason_identifier) ENTRY("log-page-directory", "WDC Get Log Page Directory", wdc_log_page_directory) ENTRY("namespace-resize", "WDC NamespaceDrive Resize", wdc_namespace_resize) ENTRY("vs-drive-info", "WDC Get Drive Info", wdc_vs_drive_info) + ENTRY("vs-temperature-stats", "WDC Get Temperature Stats", wdc_vs_temperature_stats) + ENTRY("capabilities", "WDC Device Capabilities", wdc_capabilities) + ENTRY("cloud-SSD-plugin-version", "WDC Cloud SSD Plugin Version", wdc_cloud_ssd_plugin_version) + ENTRY("vs-pcie-stats", "WDC VS PCIE Statistics", wdc_vs_pcie_stats) ) ); diff --git a/plugins/wdc/wdc-utils.c b/plugins/wdc/wdc-utils.c index 80d9955..2740181 100644 --- a/plugins/wdc/wdc-utils.c +++ b/plugins/wdc/wdc-utils.c @@ -144,3 +144,18 @@ int wdc_UtilsStrCompare(char *pcSrc, char *pcDst) return *pcSrc - *pcDst; } +void wdc_StrFormat(char *formatter, size_t fmt_sz, char *tofmt, size_t tofmtsz) +{ + + fmt_sz = snprintf(formatter,fmt_sz, "%-*.*s", + (int)tofmtsz, (int)tofmtsz, tofmt); + /* trim() the obnoxious trailing white lines */ + while (fmt_sz) { + if (formatter[fmt_sz - 1] != ' ' && formatter[fmt_sz - 1] != '\0') { + formatter[fmt_sz] = '\0'; + break; + } + fmt_sz--; + } +} + diff --git a/plugins/wdc/wdc-utils.h b/plugins/wdc/wdc-utils.h index 251339f..04d4b83 100644 --- a/plugins/wdc/wdc-utils.h +++ b/plugins/wdc/wdc-utils.h @@ -74,4 +74,5 @@ int wdc_UtilsGetTime(PUtilsTimeInfo timeInfo); int wdc_UtilsStrCompare(char *pcSrc, char *pcDst); int wdc_UtilsCreateDir(char *path); int wdc_WriteToFile(char *fileName, char *buffer, unsigned int bufferLen); +void wdc_StrFormat(char *formatter, size_t fmt_sz, char *tofmt, size_t tofmtsz); diff --git a/plugins/ymtc/ymtc-nvme.c b/plugins/ymtc/ymtc-nvme.c new file mode 100644 index 0000000..91e52be --- /dev/null +++ b/plugins/ymtc/ymtc-nvme.c @@ -0,0 +1,148 @@ +#include +#include +#include +#include +#include + +#include "linux/nvme_ioctl.h" + +#include "nvme.h" +#include "nvme-print.h" +#include "nvme-ioctl.h" +#include "plugin.h" + +#include "argconfig.h" +#include "suffix.h" + +#define CREATE_CMD +#include "ymtc-nvme.h" +#include "ymtc-utils.h" + +static void get_ymtc_smart_info(struct nvme_ymtc_smart_log *smart, int index, u8 *nm_val, u8 *raw_val) +{ + memcpy(nm_val, smart->itemArr[index].nmVal, NM_SIZE); + memcpy(raw_val, smart->itemArr[index].rawVal, RAW_SIZE); +} + +static int show_ymtc_smart_log(int fd, __u32 nsid, const char *devname, + struct nvme_ymtc_smart_log *smart) +{ + struct nvme_id_ctrl ctrl; + char fw_ver[10]; + int err = 0; + + u8 *nm = malloc(NM_SIZE * sizeof(u8)); + u8 *raw = malloc(RAW_SIZE * sizeof(u8)); + + err = nvme_identify_ctrl(fd, &ctrl); + if (err) + return err; + + snprintf(fw_ver, sizeof(fw_ver), "%c.%c%c.%c%c%c%c", + ctrl.fr[0], ctrl.fr[1], ctrl.fr[2], ctrl.fr[3], + ctrl.fr[4], ctrl.fr[5], ctrl.fr[6]); + + /* Table Title */ + printf("Additional Smart Log for NVME device:%s namespace-id:%x\n", devname, nsid); + /* Clumn Name*/ + printf("key normalized raw\n"); + /* 00 SI_VD_PROGRAM_FAIL */ + get_ymtc_smart_info(smart, SI_VD_PROGRAM_FAIL, nm, raw); + printf("program_fail_count : %3d%% %"PRIu64"\n", *nm, int48_to_long(raw)); + /* 01 SI_VD_ERASE_FAIL */ + get_ymtc_smart_info(smart, SI_VD_ERASE_FAIL, nm, raw); + printf("erase_fail_count : %3d%% %"PRIu64"\n", *nm, int48_to_long(raw)); + /* 02 SI_VD_WEARLEVELING_COUNT */ + get_ymtc_smart_info(smart, SI_VD_WEARLEVELING_COUNT, nm, raw); + printf("wear_leveling : %3d%% min: %u, max: %u, avg: %u\n", *nm, + *raw, *(raw+2), *(raw+4)); + /* 03 SI_VD_E2E_DECTECTION_COUNT */ + get_ymtc_smart_info(smart, SI_VD_E2E_DECTECTION_COUNT, nm, raw); + printf("end_to_end_error_detection_count: %3d%% %"PRIu64"\n", *nm, int48_to_long(raw)); + /* 04 SI_VD_PCIE_CRC_ERR_COUNT */ + get_ymtc_smart_info(smart, SI_VD_PCIE_CRC_ERR_COUNT, nm, raw); + printf("crc_error_count : %3d%% %"PRIu64"\n", *nm, int48_to_long(raw)); + /* 08 SI_VD_THERMAL_THROTTLE_STATUS */ + get_ymtc_smart_info(smart, SI_VD_THERMAL_THROTTLE_STATUS, nm, raw); + printf("thermal_throttle_status : %3d%% %"PRIu64"%%, cnt: %"PRIu64"\n", *nm, + int48_to_long(raw), int48_to_long(raw+1)); + /* 11 SI_VD_TOTAL_WRITE */ + get_ymtc_smart_info(smart, SI_VD_TOTAL_WRITE, nm, raw); + printf("nand_bytes_written : %3d%% sectors: %"PRIu64"\n", *nm, int48_to_long(raw)); + /* 12 SI_VD_HOST_WRITE */ + get_ymtc_smart_info(smart, SI_VD_HOST_WRITE, nm, raw); + printf("host_bytes_written : %3d%% sectors: %"PRIu64"\n", *nm, int48_to_long(raw)); + /* 14 SI_VD_TOTAL_READ */ + get_ymtc_smart_info(smart, SI_VD_TOTAL_READ, nm, raw); + printf("nand_bytes_read : %3d%% %"PRIu64"\n", *nm, int48_to_long(raw)); + /* 15 SI_VD_TEMPT_SINCE_BORN */ + get_ymtc_smart_info(smart, SI_VD_TEMPT_SINCE_BORN, nm, raw); + printf("tempt_since_born : %3d%% max: %u, min: %u, curr: %u\n", *nm, + *raw, *(raw+2), *(raw+4)); + /* 16 SI_VD_POWER_CONSUMPTION */ + get_ymtc_smart_info(smart, SI_VD_POWER_CONSUMPTION, nm, raw); + printf("power_consumption : %3d%% max: %u, min: %u, curr: %u\n", *nm, + *raw, *(raw+2), *(raw+4)); + /* 17 SI_VD_TEMPT_SINCE_BOOTUP */ + get_ymtc_smart_info(smart, SI_VD_TEMPT_SINCE_BOOTUP, nm, raw); + printf("tempt_since_bootup : %3d%% max: %u, min: %u, curr: %u\n", *nm, *raw, + *(raw+2), *(raw+4)); + /* 18 SI_VD_POWER_LOSS_PROTECTION */ + get_ymtc_smart_info(smart, SI_VD_POWER_LOSS_PROTECTION, nm, raw); + printf("power_loss_protection : %3d%% %"PRIu64"\n", *nm, int48_to_long(raw)); + /* 19 SI_VD_READ_FAIL */ + get_ymtc_smart_info(smart, SI_VD_READ_FAIL, nm, raw); + printf("read_fail : %3d%% %"PRIu64"\n", *nm, int48_to_long(raw)); + /* 20 SI_VD_THERMAL_THROTTLE_TIME */ + get_ymtc_smart_info(smart, SI_VD_THERMAL_THROTTLE_TIME, nm, raw); + printf("thermal_throttle_time : %3d%% %"PRIu64"\n", *nm, int48_to_long(raw)); + /* 21 SI_VD_FLASH_MEDIA_ERROR */ + get_ymtc_smart_info(smart, SI_VD_FLASH_MEDIA_ERROR, nm, raw); + printf("flash_error_media_count : %3d%% %"PRIu64"\n", *nm, int48_to_long(raw)); + + free(nm); + free(raw); + + return err; +} + +static int get_additional_smart_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + struct nvme_ymtc_smart_log smart_log; + int err, fd; + char *desc = "Get Ymtc vendor specific additional smart log (optionally, "\ + "for the specified namespace), and show it."; + const char *namespace = "(optional) desired namespace"; + const char *raw = "dump output in binary format"; + struct config { + __u32 namespace_id; + int raw_binary; + }; + + struct config cfg = { + .namespace_id = NVME_NSID_ALL, + }; + + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace), + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + return fd; + + err = nvme_get_log(fd, cfg.namespace_id, 0xca, false, + NVME_NO_LOG_LSP, sizeof(smart_log), &smart_log); + if (!err) { + if (!cfg.raw_binary) + err = show_ymtc_smart_log(fd, cfg.namespace_id, devicename, &smart_log); + else + d_raw((unsigned char *)&smart_log, sizeof(smart_log)); + } + if (err > 0) + fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(err), err); + + return err; +} diff --git a/plugins/ymtc/ymtc-nvme.h b/plugins/ymtc/ymtc-nvme.h new file mode 100644 index 0000000..739fd37 --- /dev/null +++ b/plugins/ymtc/ymtc-nvme.h @@ -0,0 +1,24 @@ +#undef CMD_INC_FILE +#define CMD_INC_FILE plugins/ymtc/ymtc-nvme + +#if !defined(YMTC_NVME) || defined(CMD_HEADER_MULTI_READ) +#define YMTC_NVME + +#include "cmd.h" +#include "common.h" + +#include +#include +#include +#include + +PLUGIN(NAME("ymtc", "Ymtc vendor specific extensions"), + COMMAND_LIST( + ENTRY("smart-log-add", "Retrieve Ymtc SMART Log, show it", get_additional_smart_log) + ) +); + +#endif + +#include "define_cmd.h" + diff --git a/plugins/ymtc/ymtc-utils.h b/plugins/ymtc/ymtc-utils.h new file mode 100644 index 0000000..700c764 --- /dev/null +++ b/plugins/ymtc/ymtc-utils.h @@ -0,0 +1,80 @@ +#ifndef __YMTC_UTILS_H__ +#define __YMTC_UTILS_H__ + +#define SMART_INFO_SIZE 4096 + +#define ID_SIZE 3 +#define NM_SIZE 2 +#define RAW_SIZE 7 + +typedef unsigned char u8; + +/* Additional smart external ID */ +#define SI_VD_PROGRAM_FAIL_ID 0xAB +#define SI_VD_ERASE_FAIL_ID 0xAC +#define SI_VD_WEARLEVELING_COUNT_ID 0xAD +#define SI_VD_E2E_DECTECTION_COUNT_ID 0xB8 +#define SI_VD_PCIE_CRC_ERR_COUNT_ID 0xC7 +#define SI_VD_TIMED_WORKLOAD_MEDIA_WEAR_ID 0xE2 +#define SI_VD_TIMED_WORKLOAD_HOST_READ_ID 0xE3 +#define SI_VD_TIMED_WORKLOAD_TIMER_ID 0xE4 +#define SI_VD_THERMAL_THROTTLE_STATUS_ID 0xEA +#define SI_VD_RETRY_BUFF_OVERFLOW_COUNT_ID 0xF0 +#define SI_VD_PLL_LOCK_LOSS_COUNT_ID 0xF3 +#define SI_VD_TOTAL_WRITE_ID 0xF4 +#define SI_VD_HOST_WRITE_ID 0xF5 +#define SI_VD_SYSTEM_AREA_LIFE_LEFT_ID 0xF6 +#define SI_VD_TOTAL_READ_ID 0xFA +#define SI_VD_TEMPT_SINCE_BORN_ID 0xE7 +#define SI_VD_POWER_CONSUMPTION_ID 0xE8 +#define SI_VD_TEMPT_SINCE_BOOTUP_ID 0xAF +#define SI_VD_POWER_LOSS_PROTECTION_ID 0xEC +#define SI_VD_READ_FAIL_ID 0xF2 +#define SI_VD_THERMAL_THROTTLE_TIME_ID 0xEB +#define SI_VD_FLASH_MEDIA_ERROR_ID 0xED + +/* Addtional smart internal ID */ +typedef enum +{ + /* smart attr following intel */ + SI_VD_PROGRAM_FAIL = 0, /* 0xAB */ + SI_VD_ERASE_FAIL = 1, /* 0xAC */ + SI_VD_WEARLEVELING_COUNT = 2,/* 0xAD */ + SI_VD_E2E_DECTECTION_COUNT = 3, /* 0xB8 */ + SI_VD_PCIE_CRC_ERR_COUNT = 4, /* 0xC7, 2 port data in one attribute */ + SI_VD_THERMAL_THROTTLE_STATUS = 8, /* 0xEA */ + SI_VD_TOTAL_WRITE = 11, /* 0xF4, unit is 32MiB */ + SI_VD_HOST_WRITE = 12, /* 0xF5, unit is 32MiB */ + SI_VD_TOTAL_READ = 14, /* 0xFA, unit is 32MiB */ + + /* smart attr self defined */ + SI_VD_TEMPT_SINCE_BORN = 15, /* 0xE7 */ + SI_VD_POWER_CONSUMPTION = 16, /* 0xE8 */ + SI_VD_TEMPT_SINCE_BOOTUP = 17, /* 0xAF */ + SI_VD_POWER_LOSS_PROTECTION = 18, /* 0xEC */ + SI_VD_READ_FAIL = 19, /* 0xF2 */ + SI_VD_THERMAL_THROTTLE_TIME = 20, /* 0xEB */ + SI_VD_FLASH_MEDIA_ERROR = 21, /* 0xED */ + NR_SMART_ITEMS, +} si_vendor_smart_item_e; + +// Intel Format +struct nvme_ymtc_smart_log_item +{ + /* Item identifier */ + u8 id[ID_SIZE]; + /* Normalized value or percentage. In the range from 0 to 100. */ + u8 nmVal[NM_SIZE]; + /* raw value */ + u8 rawVal[RAW_SIZE]; +}; + +struct nvme_ymtc_smart_log +{ + struct nvme_ymtc_smart_log_item itemArr[NR_SMART_ITEMS]; + + u8 resv[SMART_INFO_SIZE - sizeof(struct nvme_ymtc_smart_log_item) * NR_SMART_ITEMS]; +}; + +#endif // __YMTC_UTILS_H__ + diff --git a/plugins/zns/zns.c b/plugins/zns/zns.c new file mode 100644 index 0000000..7f6f52e --- /dev/null +++ b/plugins/zns/zns.c @@ -0,0 +1,923 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nvme.h" +#include "nvme-ioctl.h" +#include "nvme-print.h" +#include "nvme-status.h" + +#define CREATE_CMD +#include "zns.h" + +static const char *namespace_id = "Namespace identifier to use"; + +static int id_ctrl(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Send an ZNS specific Identify Controller command to "\ + "the given device and report information about the specified "\ + "controller in various formats."; + + enum nvme_print_flags flags; + struct nvme_zns_id_ctrl ctrl; + int fd, err = -1; + + struct config { + char *output_format; + }; + + struct config cfg = { + .output_format = "normal", + }; + + OPT_ARGS(opts) = { + OPT_FMT("output-format", 'o', &cfg.output_format, output_format), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + return errno; + + err = flags = validate_output_format(cfg.output_format); + if (flags < 0) + goto close_fd; + + err = nvme_zns_identify_ctrl(fd, &ctrl); + if (!err) + nvme_show_zns_id_ctrl(&ctrl, flags); + else if (err > 0) + nvme_show_status(err); + else + perror("zns identify controller"); +close_fd: + close(fd); + return nvme_status_to_errno(err, false); +} + +static int id_ns(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Send an ZNS specific Identify Namespace command to "\ + "the given device and report information about the specified "\ + "namespace in varios formats."; + const char *vendor_specific = "dump binary vendor fields"; + const char *human_readable = "show identify in readable format"; + + enum nvme_print_flags flags; + struct nvme_zns_id_ns ns; + struct nvme_id_ns id_ns; + int fd, err = -1; + + struct config { + char *output_format; + __u32 namespace_id; + int human_readable; + int vendor_specific; + }; + + struct config cfg = { + .output_format = "normal", + }; + + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_FLAG("vendor-specific", 'v', &cfg.vendor_specific, vendor_specific), + OPT_FMT("output-format", 'o', &cfg.output_format, output_format), + OPT_FLAG("human-readable", 'H', &cfg.human_readable, human_readable), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + return errno; + + flags = validate_output_format(cfg.output_format); + if (flags < 0) + goto close_fd; + if (cfg.vendor_specific) + flags |= VS; + if (cfg.human_readable) + flags |= VERBOSE; + + if (!cfg.namespace_id) { + err = cfg.namespace_id = nvme_get_nsid(fd); + if (err < 0) { + perror("get-namespace-id"); + goto close_fd; + } + } + + err = nvme_identify_ns(fd, cfg.namespace_id, false, &id_ns); + if (err) { + nvme_show_status(err); + goto close_fd; + } + + err = nvme_zns_identify_ns(fd, cfg.namespace_id, &ns); + if (!err) + nvme_show_zns_id_ns(&ns, &id_ns, flags); + else if (err > 0) + nvme_show_status(err); + else + perror("zns identify namespace"); +close_fd: + close(fd); + return nvme_status_to_errno(err, false); +} + +static int __zns_mgmt_send(int fd, __u32 namespace_id, __u64 zslba, + bool select_all, enum nvme_zns_send_action zsa, __u32 data_len, void *buf) +{ + int err; + + err = nvme_zns_mgmt_send(fd, namespace_id, zslba, select_all, zsa, + data_len, buf); + close(fd); + return err; +} + +static int zns_mgmt_send(int argc, char **argv, struct command *cmd, struct plugin *plugin, + const char *desc, enum nvme_zns_send_action zsa) +{ + const char *zslba = "starting LBA of the zone for this command"; + const char *select_all = "send command to all zones"; + + int err, fd; + char *command; + + struct config { + __u64 zslba; + __u32 namespace_id; + bool select_all; + }; + + struct config cfg = { + }; + + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_SUFFIX("start-lba", 's', &cfg.zslba, zslba), + OPT_FLAG("select-all", 'a', &cfg.select_all, select_all), + OPT_END() + }; + + err = fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + return errno; + + err = asprintf(&command, "%s-%s", plugin->name, cmd->name); + if (err < 0) + goto close_fd; + + if (!cfg.namespace_id) { + err = cfg.namespace_id = nvme_get_nsid(fd); + if (err < 0) { + perror("get-namespace-id"); + goto free; + } + } + + err = __zns_mgmt_send(fd, cfg.namespace_id, cfg.zslba, + cfg.select_all, zsa, 0, NULL); + if (!err) + printf("%s: Success, action:%d zone:%"PRIx64" all:%d nsid:%d\n", + command, zsa, (uint64_t)cfg.zslba, (int)cfg.select_all, + cfg.namespace_id); + else if (err > 0) + nvme_show_status(err); + else + perror(desc); +free: + free(command); +close_fd: + close(fd); + return nvme_status_to_errno(err, false); +} + +static int get_zdes_bytes(int fd, __u32 nsid) +{ + struct nvme_zns_id_ns ns; + struct nvme_id_ns id_ns; + __u8 lbaf; + int err; + + err = nvme_identify_ns(fd, nsid, false, &id_ns); + if (err > 0){ + nvme_show_status(err); + return err; + } + else if (err < 0){ + perror("identify namespace"); + return err; + } + + err = nvme_zns_identify_ns(fd, nsid, &ns); + if (err > 0){ + nvme_show_status(err); + return err; + } + else if (err < 0){ + perror("zns identify namespace"); + return err; + } + + lbaf = id_ns.flbas & NVME_NS_FLBAS_LBA_MASK; + return ns.lbafe[lbaf].zdes << 6; +} + +static int zone_mgmt_send(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Zone Management Send"; + const char *zslba = "starting LBA of the zone for this command"; + const char *select_all = "send command to all zones"; + const char *zsa = "zone send action"; + const char *data_len = "buffer length if data required"; + const char *data = "optional file for data (default stdin)"; + + int fd, ffd = STDIN_FILENO, err = -1; + void *buf = NULL; + + struct config { + __u64 zslba; + __u32 namespace_id; + bool select_all; + __u8 zsa; + int data_len; + char *file; + }; + + struct config cfg = { + }; + + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_SUFFIX("start-lba", 's', &cfg.zslba, zslba), + OPT_FLAG("select-all", 'a', &cfg.select_all, select_all), + OPT_BYTE("zsa", 'z', &cfg.zsa, zsa), + OPT_UINT("data-len", 'l', &cfg.data_len, data_len), + OPT_FILE("data", 'd', &cfg.file, data), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + return errno; + + if (!cfg.namespace_id) { + err = cfg.namespace_id = nvme_get_nsid(fd); + if (err < 0) { + perror("get-namespace-id"); + goto close_fd; + } + } + + if (!cfg.zsa) { + fprintf(stderr, "zone send action must be specified\n"); + err = -EINVAL; + goto close_fd; + } + + if (cfg.zsa == NVME_ZNS_ZSA_SET_DESC_EXT) { + if(!cfg.data_len) { + cfg.data_len = get_zdes_bytes(fd, cfg.namespace_id); + if (cfg.data_len == 0) { + fprintf(stderr, + "Zone Descriptor Extensions are not supported\n"); + goto close_fd; + } else if (cfg.data_len < 0) { + err = cfg.data_len; + goto close_fd; + } + } + if (posix_memalign(&buf, getpagesize(), cfg.data_len)) { + fprintf(stderr, "can not allocate feature payload\n"); + goto close_fd; + } + memset(buf, 0, cfg.data_len); + + if (cfg.file) { + ffd = open(cfg.file, O_RDONLY); + if (ffd < 0) { + perror(cfg.file); + goto free; + } + } + + err = read(ffd, (void *)buf, cfg.data_len); + if (err < 0) { + perror("read"); + goto close_ffd; + } + } else { + if (cfg.file || cfg.data_len) { + fprintf(stderr, + "data, data_len only valid with set extended descriptor\n"); + err = -EINVAL; + goto close_fd; + } + } + + err = __zns_mgmt_send(fd, cfg.namespace_id, cfg.zslba, cfg.select_all, + cfg.zsa, cfg.data_len, buf); + if (!err) + printf("zone-mgmt-send: Success, action:%d zone:%"PRIx64" " + "all:%d nsid:%d\n", + cfg.zsa, (uint64_t)cfg.zslba, (int)cfg.select_all, + cfg.namespace_id); + else if (err > 0) + nvme_show_status(err); + else + perror("zns zone-mgmt-send"); + +close_ffd: + if (cfg.file) + close(ffd); +free: + free(buf); +close_fd: + close(fd); + return nvme_status_to_errno(err, false); +} + +static int close_zone(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Close zones\n"; + + return zns_mgmt_send(argc, argv, cmd, plugin, desc, NVME_ZNS_ZSA_CLOSE); +} + +static int finish_zone(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Finish zones\n"; + + return zns_mgmt_send(argc, argv, cmd, plugin, desc, NVME_ZNS_ZSA_FINISH); +} + +static int open_zone(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Open zones\n"; + + return zns_mgmt_send(argc, argv, cmd, plugin, desc, NVME_ZNS_ZSA_OPEN); +} + +static int reset_zone(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Reset zones\n"; + + return zns_mgmt_send(argc, argv, cmd, plugin, desc, NVME_ZNS_ZSA_RESET); +} + +static int offline_zone(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Offline zones\n"; + + return zns_mgmt_send(argc, argv, cmd, plugin, desc, NVME_ZNS_ZSA_OFFLINE); +} + +static int set_zone_desc(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Set Zone Descriptor Extension\n"; + const char *zslba = "starting LBA of the zone for this command"; + const char *data = "optional file for zone extention data (default stdin)"; + + int fd, ffd = STDIN_FILENO, err; + void *buf = NULL; + __u32 data_len; + + struct config { + __u64 zslba; + __u32 namespace_id; + char *file; + }; + + struct config cfg = { + }; + + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_SUFFIX("start-lba", 's', &cfg.zslba, zslba), + OPT_FILE("data", 'd', &cfg.file, data), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + return errno; + + if (!cfg.namespace_id) { + err = cfg.namespace_id = nvme_get_nsid(fd); + if (err < 0) { + perror("get-namespace-id"); + goto close_fd; + } + } + + data_len = get_zdes_bytes(fd, cfg.namespace_id); + + if (!data_len) { + fprintf(stderr, + "zone format does not provide descriptor extention\n"); + errno = EINVAL; + err = -1; + goto close_fd; + } + + buf = calloc(1, data_len); + if (!buf) { + perror("could not alloc memory for zone desc"); + err = -ENOMEM; + goto close_fd; + } + + if (cfg.file) { + ffd = open(cfg.file, O_RDONLY); + if (ffd < 0) { + perror(cfg.file); + err = -1; + goto free; + } + } + + err = read(ffd, (void *)buf, data_len); + if (err < 0) { + perror("read"); + goto close_ffd; + } + + err = __zns_mgmt_send(fd, cfg.namespace_id, cfg.zslba, 0, + NVME_ZNS_ZSA_SET_DESC_EXT, data_len, buf); + if (!err) + printf("set-zone-desc: Success, zone:%"PRIx64" nsid:%d\n", + (uint64_t)cfg.zslba, cfg.namespace_id); + else if (err > 0) + nvme_show_status(err); + else + perror("zns set-zone-desc"); +close_ffd: + if (cfg.file) + close(ffd); +free: + free(buf); +close_fd: + close(fd); + return nvme_status_to_errno(err, false); +} + +static int zone_mgmt_recv(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Zone Management Receive"; + const char *zslba = "starting LBA of the zone"; + const char *zra = "Zone Receive Action"; + const char *zrasf = "Zone Receive Action Specific Field(Reporting Options)"; + const char *partial = "Zone Receive Action Specific Features(Partial Report)"; + const char *data_len = "length of data in bytes"; + + enum nvme_print_flags flags; + int fd, err = -1; + void *data = NULL; + + struct config { + char *output_format; + __u64 zslba; + __u32 namespace_id; + __u8 zra; + __u8 zrasf; + bool partial; + __u32 data_len; + }; + + struct config cfg = { + .output_format = "normal", + }; + + OPT_ARGS(opts) = { + OPT_FMT("output-format", 'o', &cfg.output_format, output_format), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_SUFFIX("start-lba", 's', &cfg.zslba, zslba), + OPT_BYTE("zra", 'z', &cfg.zra, zra), + OPT_BYTE("zrasf", 'S', &cfg.zrasf, zrasf), + OPT_FLAG("partial", 'p', &cfg.partial, partial), + OPT_UINT("data-len", 'l', &cfg.data_len, data_len), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + return errno; + + flags = validate_output_format(cfg.output_format); + if (flags < 0) + goto close_fd; + + if (!cfg.namespace_id) { + err = cfg.namespace_id = nvme_get_nsid(fd); + if (err < 0) { + perror("get-namespace-id"); + goto close_fd; + } + } + + if (cfg.zra == NVME_ZNS_ZRA_REPORT_ZONES && !cfg.data_len) { + fprintf(stderr, "error: data len is needed for NVME_ZRA_ZONE_REPORT\n"); + err = -EINVAL; + goto close_fd; + } + if (cfg.data_len) { + data = calloc(1, cfg.data_len); + if (!data) { + perror("could not alloc memory for zone mgmt receive data"); + err = -ENOMEM; + goto close_fd; + } + } + + err = nvme_zns_mgmt_recv(fd, cfg.namespace_id, cfg.zslba, cfg.zra, + cfg.zrasf, cfg.partial, cfg.data_len, data); + if (!err) + printf("zone-mgmt-recv: Success, action:%d zone:%"PRIx64" nsid:%d\n", + cfg.zra, (uint64_t)cfg.zslba, cfg.namespace_id); + else if (err > 0) + nvme_show_status(err); + else + perror("zns zone-mgmt-recv"); + + free(data); +close_fd: + close(fd); + return nvme_status_to_errno(err, false); +} + +static int report_zones(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Retrieve the Report Zones data structure"; + const char *zslba = "starting LBA of the zone"; + const char *num_descs = "number of descriptors to retrieve (default: all of them)"; + const char *state = "state of zones to list"; + const char *ext = "set to use the extended report zones"; + const char *part = "set to use the partial report"; + const char *human_readable = "show report zones in readable format"; + + enum nvme_print_flags flags; + int fd, zdes = 0, err = -1; + __u32 report_size; + void *report; + bool huge = false; + + struct config { + char *output_format; + __u64 zslba; + __u32 namespace_id; + int num_descs; + int state; + int human_readable; + bool extended; + bool partial; + }; + + struct config cfg = { + .output_format = "normal", + .num_descs = -1, + }; + + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_SUFFIX("start-lba", 's', &cfg.zslba, zslba), + OPT_UINT("descs", 'd', &cfg.num_descs, num_descs), + OPT_UINT("state", 'S', &cfg.state, state), + OPT_FMT("output-format", 'o', &cfg.output_format, output_format), + OPT_FLAG("human-readable",'H', &cfg.human_readable, human_readable), + OPT_FLAG("extended", 'e', &cfg.extended, ext), + OPT_FLAG("partial", 'p', &cfg.partial, part), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + return errno; + + flags = validate_output_format(cfg.output_format); + if (flags < 0) + goto close_fd; + if (cfg.human_readable) + flags |= VERBOSE; + + if (!cfg.namespace_id) { + err = cfg.namespace_id = nvme_get_nsid(fd); + if (err < 0) { + perror("get-namespace-id"); + goto close_fd; + } + } + + if (cfg.extended) { + zdes = get_zdes_bytes(fd, cfg.namespace_id); + if (zdes < 0) { + err = zdes; + goto close_fd; + } + } + + if (cfg.num_descs == -1) { + struct nvme_zone_report r; + + err = nvme_zns_report_zones(fd, cfg.namespace_id, 0, + 0, cfg.state, 0, sizeof(r), &r); + if (err > 0) { + nvme_show_status(err); + goto close_fd; + } else if (err < 0) { + perror("zns report-zones"); + goto close_fd; + } + cfg.num_descs = le64_to_cpu(r.nr_zones); + } + + report_size = sizeof(struct nvme_zone_report) + cfg.num_descs * + (sizeof(struct nvme_zns_desc) + cfg.num_descs * zdes); + + report = nvme_alloc(report_size, &huge); + if (!report) { + perror("alloc"); + err = -ENOMEM; + goto close_fd; + } + + err = nvme_zns_report_zones(fd, cfg.namespace_id, cfg.zslba, + cfg.extended, cfg.state, cfg.partial, report_size, report); + if (!err) + nvme_show_zns_report_zones(report, cfg.num_descs, zdes, + report_size, flags); + else if (err > 0) + nvme_show_status(err); + else + perror("zns report-zones"); + + nvme_free(report, huge); +close_fd: + close(fd); + return nvme_status_to_errno(err, false); +} + +static int zone_append(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "The zone append command is used to write to a zone "\ + "using the slba of the zone, and the write will be appended from the "\ + "write pointer of the zone"; + const char *zslba = "starting LBA of the zone"; + const char *data = "file containing data to write"; + const char *metadata = "file with metadata to be written"; + const char *limited_retry = "limit media access attempts"; + const char *fua = "force unit access"; + const char *prinfo = "protection information action and checks field"; + const char *piremap = "protection information remap (for type 1 PI)"; + const char *ref_tag = "reference tag (for end to end PI)"; + const char *lbat = "logical block application tag (for end to end PI)"; + const char *lbatm = "logical block application tag mask (for end to end PI)"; + const char *metadata_size = "size of metadata in bytes"; + const char *data_size = "size of data in bytes"; + const char *latency = "output latency statistics"; + + int err = -1, fd, dfd = STDIN_FILENO, mfd = STDIN_FILENO; + unsigned int lba_size, meta_size; + void *buf = NULL, *mbuf = NULL; + __u16 nblocks, control = 0; + __u64 result; + struct timeval start_time, end_time; + + struct nvme_id_ns ns; + + struct config { + char *data; + char *metadata; + __u64 zslba; + __u64 data_size; + __u64 metadata_size; + int limited_retry; + int fua; + __u32 namespace_id; + __u32 ref_tag; + __u16 lbat; + __u16 lbatm; + __u8 prinfo; + int piremap; + int latency; + }; + + struct config cfg = { + }; + + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_SUFFIX("zslba", 's', &cfg.zslba, zslba), + OPT_SUFFIX("data-size", 'z', &cfg.data_size, data_size), + OPT_SUFFIX("metadata-size", 'y', &cfg.metadata_size, metadata_size), + OPT_FILE("data", 'd', &cfg.data, data), + OPT_FILE("metadata", 'M', &cfg.metadata, metadata), + OPT_FLAG("limited-retry", 'l', &cfg.limited_retry, limited_retry), + OPT_FLAG("force-unit-access", 'f', &cfg.fua, fua), + OPT_UINT("ref-tag", 'r', &cfg.ref_tag, ref_tag), + OPT_SHRT("app-tag-mask", 'm', &cfg.lbatm, lbatm), + OPT_SHRT("app-tag", 'a', &cfg.lbat, lbat), + OPT_BYTE("prinfo", 'p', &cfg.prinfo, prinfo), + OPT_FLAG("piremap", 'P', &cfg.piremap, piremap), + OPT_FLAG("latency", 't', &cfg.latency, latency), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + return errno; + + if (!cfg.data_size) { + fprintf(stderr, "Append size not provided\n"); + errno = EINVAL; + goto close_fd; + } + + if (!cfg.namespace_id) { + err = cfg.namespace_id = nvme_get_nsid(fd); + if (err < 0) { + perror("get-namespace-id"); + goto close_fd; + } + } + + err = nvme_identify_ns(fd, cfg.namespace_id, false, &ns); + if (err) { + nvme_show_status(err); + goto close_fd; + } + + lba_size = 1 << ns.lbaf[(ns.flbas & 0x0f)].ds; + if (cfg.data_size & (lba_size - 1)) { + fprintf(stderr, + "Data size:%#"PRIx64" not aligned to lba size:%#x\n", + (uint64_t)cfg.data_size, lba_size); + errno = EINVAL; + goto close_ns; + } + + meta_size = ns.lbaf[(ns.flbas & 0x0f)].ms; + if (meta_size && !(meta_size == 8 && (cfg.prinfo & 0x8)) && + (!cfg.metadata_size || cfg.metadata_size % meta_size)) { + fprintf(stderr, + "Metadata size:%#"PRIx64" not aligned to metadata size:%#x\n", + (uint64_t)cfg.metadata_size, meta_size); + errno = EINVAL; + goto close_ns; + } + + if (cfg.prinfo > 0xf) { + fprintf(stderr, "Invalid value for prinfo:%#x\n", cfg.prinfo); + errno = EINVAL; + goto close_ns; + } + + if (cfg.data) { + dfd = open(cfg.data, O_RDONLY); + if (dfd < 0) { + perror(cfg.data); + goto close_ns; + } + } + + if (posix_memalign(&buf, getpagesize(), cfg.data_size)) { + fprintf(stderr, "No memory for data size:%"PRIx64"\n", + (uint64_t)cfg.data_size); + goto close_dfd; + } + + memset(buf, 0, cfg.data_size); + err = read(dfd, buf, cfg.data_size); + if (err < 0) { + perror("read-data"); + goto free_data; + } + + if (cfg.metadata) { + mfd = open(cfg.metadata, O_RDONLY); + if (mfd < 0) { + perror(cfg.metadata); + err = -1; + goto close_dfd; + } + } + + if (cfg.metadata_size) { + if (posix_memalign(&mbuf, getpagesize(), meta_size)) { + fprintf(stderr, "No memory for metadata size:%d\n", + meta_size); + err = -1; + goto close_mfd; + } + + memset(mbuf, 0, cfg.metadata_size); + err = read(mfd, mbuf, cfg.metadata_size); + if (err < 0) { + perror("read-metadata"); + goto free_meta; + } + } + + nblocks = (cfg.data_size / lba_size) - 1; + control |= (cfg.prinfo << 10); + if (cfg.limited_retry) + control |= NVME_RW_LR; + if (cfg.fua) + control |= NVME_RW_FUA; + if (cfg.piremap) + control |= NVME_RW_PIREMAP; + + gettimeofday(&start_time, NULL); + err = nvme_zns_append(fd, cfg.namespace_id, cfg.zslba, nblocks, + control, cfg.ref_tag, cfg.lbat, cfg.lbatm, + cfg.data_size, buf, cfg.metadata_size, mbuf, + &result); + gettimeofday(&end_time, NULL); + if (cfg.latency) + printf(" latency: zone append: %llu us\n", + elapsed_utime(start_time, end_time)); + + if (!err) + printf("Success appended data to LBA %"PRIx64"\n", (uint64_t)result); + else if (err > 0) + nvme_show_status(err); + else + perror("zns zone-append"); + +free_meta: + free(mbuf); +close_mfd: + if (cfg.metadata) + close(mfd); +free_data: + free(buf); +close_dfd: + if (cfg.data) + close(dfd); +close_ns: +close_fd: + close(fd); + return nvme_status_to_errno(err, false); +} + +static int changed_zone_list(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Retrieve Changed Zone log for the given device"; + const char *rae = "retain an asynchronous event"; + + struct nvme_zns_changed_zone_log log; + enum nvme_print_flags flags; + int fd, err = -1; + + struct config { + char *output_format; + __u32 namespace_id; + bool rae; + }; + + struct config cfg = { + .output_format = "normal", + }; + + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_FMT("output-format", 'o', &cfg.output_format, output_format), + OPT_FLAG("rae", 'r', &cfg.rae, rae), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + return errno; + + flags = validate_output_format(cfg.output_format); + if (flags < 0) + goto close_fd; + + if (!cfg.namespace_id) { + err = cfg.namespace_id = nvme_get_nsid(fd); + if (err < 0) { + perror("get-namespace-id"); + goto close_fd; + } + } + + err = nvme_get_log(fd, cfg.namespace_id, NVME_LOG_ZONE_CHANGED_LIST, + cfg.rae, NVME_NO_LOG_LSP, sizeof(log), &log); + if (!err) + nvme_show_zns_changed(&log, flags); + else if (err > 0) + nvme_show_status(err); + else + perror("zns changed-zone-list"); + +close_fd: + close(fd); + return nvme_status_to_errno(err, false); +} diff --git a/plugins/zns/zns.h b/plugins/zns/zns.h new file mode 100644 index 0000000..a92de69 --- /dev/null +++ b/plugins/zns/zns.h @@ -0,0 +1,30 @@ +#undef CMD_INC_FILE +#define CMD_INC_FILE plugins/zns/zns + +#if !defined(ZNS_NVME) || defined(CMD_HEADER_MULTI_READ) +#define ZNS_NVME + +#include "cmd.h" + +PLUGIN(NAME("zns", "Zoned Namespace Command Set"), + COMMAND_LIST( + ENTRY("id-ctrl", "Retrieve ZNS controller identification", id_ctrl) + ENTRY("id-ns", "Retrieve ZNS namespace identification", id_ns) + ENTRY("zone-mgmt-recv", "Sends the zone management receive command", zone_mgmt_recv) + ENTRY("zone-mgmt-send", "Sends the zone management send command", zone_mgmt_send) + ENTRY("report-zones", "Retrieve the Report Zones report", report_zones) + ENTRY("close-zone", "Closes one or more zones", close_zone) + ENTRY("finish-zone", "Finishes one or more zones", finish_zone) + ENTRY("open-zone", "Opens one or more zones", open_zone) + ENTRY("reset-zone", "Resets one or more zones", reset_zone) + ENTRY("offline-zone", "Offlines one or more zones", offline_zone) + ENTRY("set-zone-desc", "Attaches zone descriptor extension data", set_zone_desc) + ENTRY("zone-append", "Writes data and metadata (if applicable), appended to the end of the requested zone", zone_append) + ENTRY("changed-zone-list", "Retrieves the changed zone list log", changed_zone_list) + ) +); + +#endif + +#include "define_cmd.h" + diff --git a/scripts/gen-hostnqn.sh b/scripts/gen-hostnqn.sh index d8783cf..5138f0c 100644 --- a/scripts/gen-hostnqn.sh +++ b/scripts/gen-hostnqn.sh @@ -2,7 +2,11 @@ LC_ALL=C -UUID=$(dmidecode -s system-uuid | tr -d '[:space:]') +if [ -e "/proc/device-tree/ibm,partition-uuid" ] ; then + UUID=$(tr -d '\0' < /proc/device-tree/ibm,partition-uuid) +else + UUID=$(dmidecode -s system-uuid | tr -d '[:space:]') +fi if [ -z "$UUID" ] ; then >&2 echo "No UUID found, can't determine hostnqn." diff --git a/tests/nvme_get_features_test.py b/tests/nvme_get_features_test.py index 94ed865..2e407ef 100644 --- a/tests/nvme_get_features_test.py +++ b/tests/nvme_get_features_test.py @@ -54,7 +54,8 @@ class TestNVMeGetMandatoryFeatures(TestNVMe): self.setup_log_dir(self.__class__.__name__) self.feature_id_list = ["0x01", "0x02", "0x04", "0x05", "0x07", "0x08", "0x09", "0x0A", "0x0B"] - get_vector_list_cmd = "cat /proc/interrupts | grep nvme |" \ + device = self.ctrl.split('/')[-1] + get_vector_list_cmd = "grep " + device + "q /proc/interrupts |" \ " cut -d : -f 1 | tr -d ' ' | tr '\n' ' '" proc = subprocess.Popen(get_vector_list_cmd, shell=True, @@ -80,7 +81,7 @@ class TestNVMeGetMandatoryFeatures(TestNVMe): for vector in range(self.vector_list_len): get_feat_cmd = "nvme get-feature " + self.ctrl + \ " --feature-id=" + str(feature_id) + \ - " --cdw11=" + str(vector) + " --cdw11=" + str(vector) + " -H" proc = subprocess.Popen(get_feat_cmd, shell=True, stdout=subprocess.PIPE, @@ -90,7 +91,7 @@ class TestNVMeGetMandatoryFeatures(TestNVMe): assert_equal(proc.wait(), 0) else: get_feat_cmd = "nvme get-feature " + self.ctrl + \ - " --feature-id=" + str(feature_id) + " --feature-id=" + str(feature_id) + " -H" proc = subprocess.Popen(get_feat_cmd, shell=True, stdout=subprocess.PIPE, diff --git a/util/argconfig.c b/util/argconfig.c index f647448..341a049 100644 --- a/util/argconfig.c +++ b/util/argconfig.c @@ -31,13 +31,14 @@ #include "argconfig.h" #include "suffix.h" -#include +#include +#include #include +#include #include #include -#include #include -#include +#include static argconfig_help_func *help_funcs[MAX_HELP_FUNC] = { NULL }; @@ -450,10 +451,11 @@ int argconfig_parse_subopt_string(char *string, char **options, return 0; } -unsigned argconfig_parse_comma_sep_array(char *string, int *val, +int argconfig_parse_comma_sep_array(char *string, int *val, unsigned max_length) { - unsigned ret = 0; + int ret = 0; + unsigned long v; char *tmp; char *p; @@ -464,9 +466,14 @@ unsigned argconfig_parse_comma_sep_array(char *string, int *val, if (!tmp) return 0; - val[ret] = strtol(tmp, &p, 0); + v = strtoul(tmp, &p, 0); if (*p != 0) return -1; + if (v > UINT_MAX) { + fprintf(stderr, "%s out of range\n", tmp); + return -1; + } + val[ret] = v; ret++; while (1) { @@ -478,19 +485,23 @@ unsigned argconfig_parse_comma_sep_array(char *string, int *val, if (ret >= max_length) return -1; - val[ret] = strtol(tmp, &p, 0); - + v = strtoul(tmp, &p, 0); if (*p != 0) return -1; + if (v > UINT_MAX) { + fprintf(stderr, "%s out of range\n", tmp); + return -1; + } + val[ret] = v; ret++; } } -unsigned argconfig_parse_comma_sep_array_long(char *string, +int argconfig_parse_comma_sep_array_long(char *string, unsigned long long *val, unsigned max_length) { - unsigned ret = 0; + int ret = 0; char *tmp; char *p; diff --git a/util/argconfig.h b/util/argconfig.h index 623b832..3147277 100644 --- a/util/argconfig.h +++ b/util/argconfig.h @@ -119,9 +119,9 @@ int argconfig_parse(int argc, char *argv[], const char *program_desc, const struct argconfig_commandline_options *options); int argconfig_parse_subopt_string(char *string, char **options, size_t max_options); -unsigned argconfig_parse_comma_sep_array(char *string, int *ret, +int argconfig_parse_comma_sep_array(char *string, int *ret, unsigned max_length); -unsigned argconfig_parse_comma_sep_array_long(char *string, +int argconfig_parse_comma_sep_array_long(char *string, unsigned long long *ret, unsigned max_length); void argconfig_register_help_func(argconfig_help_func * f); diff --git a/util/cleanup.c b/util/cleanup.c new file mode 100644 index 0000000..0d5d910 --- /dev/null +++ b/util/cleanup.c @@ -0,0 +1,4 @@ +#include +#include "cleanup.h" + +DEFINE_CLEANUP_FUNC(cleanup_charp, char *, free); diff --git a/util/cleanup.h b/util/cleanup.h new file mode 100644 index 0000000..89a4984 --- /dev/null +++ b/util/cleanup.h @@ -0,0 +1,18 @@ +#ifndef __CLEANUP_H +#define __CLEANUP_H + +#define __cleanup__(fn) __attribute__((cleanup(fn))) + +#define DECLARE_CLEANUP_FUNC(name, type) \ + void name(type *__p) + +#define DEFINE_CLEANUP_FUNC(name, type, free_fn)\ +DECLARE_CLEANUP_FUNC(name, type) \ +{ \ + if (*__p) \ + free_fn(*__p); \ +} + +DECLARE_CLEANUP_FUNC(cleanup_charp, char *); + +#endif diff --git a/util/json.c b/util/json.c index 2978410..df0aefb 100644 --- a/util/json.c +++ b/util/json.c @@ -19,9 +19,9 @@ struct json_object *json_create_object(void) return test; } -struct json_array *json_create_array(void) +struct json_object *json_create_array(void) { - void *test = calloc(1, sizeof(struct json_array)); + void *test = calloc(1, sizeof(struct json_object)); if (!test) fail_and_notify(); return test; @@ -146,7 +146,7 @@ static struct json_value *json_create_value_object(struct json_object *obj) return value; } -static struct json_value *json_create_value_array(struct json_array *array) +static struct json_value *json_create_value_array(struct json_object *array) { struct json_value *value = malloc(sizeof(struct json_value)); @@ -173,7 +173,7 @@ void json_free_object(struct json_object *obj) free(obj); } -void json_free_array(struct json_array *array) +void json_free_array(struct json_object *array) { int i; @@ -206,7 +206,7 @@ static void json_free_value(struct json_value *value) free(value); } -static int json_array_add_value(struct json_array *array, struct json_value *value) +static int json_array_add_value(struct json_object *array, struct json_value *value) { struct json_value **values = realloc(array->values, sizeof(struct json_value *) * (array->value_cnt + 1)); @@ -255,7 +255,7 @@ int json_object_add_value_type(struct json_object *obj, const char *name, int ty else if (type == JSON_TYPE_OBJECT) value = json_create_value_object(va_arg(args, struct json_object *)); else - value = json_create_value_array(va_arg(args, struct json_array *)); + value = json_create_value_array(va_arg(args, struct json_object *)); va_end(args); if (!value) @@ -274,8 +274,8 @@ int json_object_add_value_type(struct json_object *obj, const char *name, int ty return 0; } -static void json_print_array(struct json_array *array, void *); -int json_array_add_value_type(struct json_array *array, int type, ...) +static void json_print_array(struct json_object *array, void *); +int json_array_add_value_type(struct json_object *array, int type, ...) { struct json_value *value; va_list args; @@ -293,7 +293,7 @@ int json_array_add_value_type(struct json_array *array, int type, ...) else if (type == JSON_TYPE_OBJECT) value = json_create_value_object(va_arg(args, struct json_object *)); else - value = json_create_value_array(va_arg(args, struct json_array *)); + value = json_create_value_array(va_arg(args, struct json_object *)); va_end(args); if (!value) @@ -309,7 +309,7 @@ int json_array_add_value_type(struct json_array *array, int type, ...) static int json_value_level(struct json_value *value); static int json_pair_level(struct json_pair *pair); -static int json_array_level(struct json_array *array); +static int json_array_level(struct json_object *array); static int json_object_level(struct json_object *object) { if (object->parent == NULL) @@ -322,7 +322,7 @@ static int json_pair_level(struct json_pair *pair) return json_object_level(pair->parent) + 1; } -static int json_array_level(struct json_array *array) +static int json_array_level(struct json_object *array) { return json_value_level(array->parent); } @@ -342,7 +342,7 @@ static void json_print_level(int level, void *out) } static void json_print_pair(struct json_pair *pair, void *); -static void json_print_array(struct json_array *array, void *); +static void json_print_array(struct json_object *array, void *); static void json_print_value(struct json_value *value, void *); void json_print_object(struct json_object *obj, void *out) { @@ -366,7 +366,7 @@ static void json_print_pair(struct json_pair *pair, void *out) json_print_value(pair->value, out); } -static void json_print_array(struct json_array *array, void *out) +static void json_print_array(struct json_object *array, void *out) { int i; diff --git a/util/json.h b/util/json.h index d78d3db..666c78d 100644 --- a/util/json.h +++ b/util/json.h @@ -2,7 +2,6 @@ #define __JSON__H struct json_object; -struct json_array; struct json_pair; #define JSON_TYPE_STRING 0 @@ -21,22 +20,18 @@ struct json_value { long double float_number; char *string; struct json_object *object; - struct json_array *array; + struct json_object *array; }; int parent_type; union { struct json_pair *parent_pair; - struct json_array *parent_array; + struct json_object *parent_array; }; }; -struct json_array { +struct json_object { struct json_value **values; int value_cnt; - struct json_value *parent; -}; - -struct json_object { struct json_pair **pairs; int pair_cnt; struct json_value *parent; @@ -49,10 +44,10 @@ struct json_pair { }; struct json_object *json_create_object(void); -struct json_array *json_create_array(void); +struct json_object *json_create_array(void); void json_free_object(struct json_object *obj); -void json_free_array(struct json_array *array); +void json_free_array(struct json_object *array); int json_object_add_value_type(struct json_object *obj, const char *name, int type, ...); #define json_object_add_value_int(obj, name, val) \ @@ -67,7 +62,7 @@ int json_object_add_value_type(struct json_object *obj, const char *name, int ty json_object_add_value_type((obj), name, JSON_TYPE_OBJECT, (val)) #define json_object_add_value_array(obj, name, val) \ json_object_add_value_type((obj), name, JSON_TYPE_ARRAY, (val)) -int json_array_add_value_type(struct json_array *array, int type, ...); +int json_array_add_value_type(struct json_object *array, int type, ...); #define json_array_add_value_int(obj, val) \ json_array_add_value_type((obj), JSON_TYPE_INTEGER, (val)) #define json_array_add_value_uint(obj, val) \ diff --git a/util/log.c b/util/log.c new file mode 100644 index 0000000..4a22354 --- /dev/null +++ b/util/log.c @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2021 SUSE LLC + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This file implements basic logging functionality. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#define LOG_FUNCNAME 1 +#include "log.h" +#include "cleanup.h" + +#ifndef LOG_CLOCK +#define LOG_CLOCK CLOCK_MONOTONIC +#endif + +int log_level = DEFAULT_LOGLEVEL; +bool log_timestamp; +bool log_pid; + +void __attribute__((format(printf, 3, 4))) +__msg(int lvl, const char *func, const char *format, ...) +{ + va_list ap; + char pidbuf[16]; + char timebuf[32]; + static const char *const formats[] = { + "%s%s%s", + "%s%s%s: ", + "%s<%s>%s ", + "%s<%s> %s: ", + "[%s] %s%s ", + "[%s]%s %s: ", + "[%s] <%s>%s ", + "[%s] <%s> %s: ", + }; + char *header __cleanup__(cleanup_charp) = NULL; + char *message __cleanup__(cleanup_charp) = NULL; + int idx; + + if (lvl > log_level) + return; + + if (log_timestamp) { + struct timespec now; + + clock_gettime(LOG_CLOCK, &now); + snprintf(timebuf, sizeof(timebuf), "%6ld.%06ld", + (long)now.tv_sec, now.tv_nsec / 1000); + } else + *timebuf = '\0'; + + if (log_pid) + snprintf(pidbuf, sizeof(pidbuf), "%ld", (long)getpid()); + else + *pidbuf = '\0'; + + idx = ((log_timestamp ? 1 : 0) << 2) | + ((log_pid ? 1 : 0) << 1) | (func ? 1 : 0); + + if (asprintf(&header, formats[idx], timebuf, pidbuf, func ? func : "") + == -1) + header = NULL; + + va_start(ap, format); + if (vasprintf(&message, format, ap) == -1) + message = NULL; + va_end(ap); + + fprintf(stderr, "%s%s", header ? header : "", + message ? message : ""); + +} diff --git a/util/log.h b/util/log.h new file mode 100644 index 0000000..15107a5 --- /dev/null +++ b/util/log.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2021 Martin Wilck, SUSE LLC + * SPDX-License-Identifier: LGPL-2.1-or-newer + */ +#ifndef _LOG_H +#define _LOG_H + +#ifndef MAX_LOGLEVEL +# define MAX_LOGLEVEL LOG_DEBUG +#endif +#ifndef DEFAULT_LOGLEVEL +# define DEFAULT_LOGLEVEL LOG_NOTICE +#endif + +#if (LOG_FUNCNAME == 1) +#define _log_func __func__ +#else +#define _log_func NULL +#endif + +extern int log_level; +extern bool log_timestamp; +extern bool log_pid; + +void __attribute__((format(printf, 3, 4))) +__msg(int lvl, const char *func, const char *format, ...); + +#define msg(lvl, format, ...) \ + do { \ + if ((lvl) <= MAX_LOGLEVEL) \ + __msg(lvl, _log_func, format, ##__VA_ARGS__); \ + } while (0) + +#endif /* _LOG_H */ -- cgit v1.2.3