| #include "NVMeIntf.hpp" |
| #include "Utils.hpp" |
| |
| #include <boost/asio.hpp> |
| #include <sdbusplus/bus.hpp> |
| |
| #include <cstdint> |
| #include <thread> |
| |
| // A worker thread for calling NVMeMI cmd. |
| class NVMeMiWorker |
| { |
| private: |
| bool workerStop = false; |
| std::mutex workerMtx; |
| std::condition_variable workerCv; |
| boost::asio::io_context workerIO; |
| bool workerIsNotified = false; |
| std::thread thread; |
| |
| public: |
| NVMeMiWorker(); |
| NVMeMiWorker(const NVMeMiWorker&) = delete; |
| ~NVMeMiWorker(); |
| void post(std::function<void(void)>&& func); |
| }; |
| |
| class NVMeMi : public NVMeMiIntf, public std::enable_shared_from_this<NVMeMi> |
| { |
| public: |
| NVMeMi(boost::asio::io_context& io, |
| const std::shared_ptr<sdbusplus::asio::connection>& conn, |
| const std::shared_ptr<const MctpDevice>& device, |
| const std::shared_ptr<NVMeMiWorker>& worker, |
| PowerState readState = PowerState::always); |
| ~NVMeMi() override; |
| |
| bool flushOperations(std::function<void()>&& cb) override; |
| void miSubsystemHealthStatusPoll( |
| std::function<void(const std::error_code&, |
| nvme_mi_nvm_ss_health_status*)>&& cb) override; |
| void miScanCtrl(std::function<void(const std::error_code&, |
| const std::vector<nvme_mi_ctrl_t>&)> |
| cb) override; |
| void adminIdentify( |
| nvme_mi_ctrl_t ctrl, nvme_identify_cns cns, uint32_t nsid, |
| uint16_t cntid, |
| std::function<void(nvme_ex_ptr, std::span<uint8_t>)>&& cb) override; |
| void adminGetLogPage(nvme_mi_ctrl_t ctrl, nvme_cmd_get_log_lid lid, |
| uint32_t nsid, uint8_t lsp, uint16_t lsi, |
| std::function<void(const std::error_code&, |
| std::span<uint8_t>)>&& cb) override; |
| |
| void adminFwCommit( |
| nvme_mi_ctrl_t ctrl, nvme_fw_commit_ca action, uint8_t slot, bool bpid, |
| std::function<void(const std::error_code&, nvme_status_field)>&& cb) |
| override; |
| |
| void adminFwDownload(nvme_mi_ctrl_t ctrl, std::string firmwarefile, |
| std::function<void(const std::error_code&, |
| nvme_status_field)>&& cb) override; |
| |
| void adminXfer(nvme_mi_ctrl_t ctrl, const nvme_mi_admin_req_hdr& adminReq, |
| std::span<uint8_t> data, unsigned int timeoutMs, |
| std::function<void(const std::error_code&, |
| const nvme_mi_admin_resp_hdr&, |
| std::span<uint8_t>)>&& cb) override; |
| |
| void adminSecuritySend(nvme_mi_ctrl_t ctrl, uint8_t proto, |
| uint16_t protoSpecific, std::span<uint8_t> data, |
| std::function<void(const std::error_code&, |
| int nvmeStatus)>&& cb) override; |
| |
| void adminSecurityReceive( |
| nvme_mi_ctrl_t ctrl, uint8_t proto, uint16_t protoSpecific, |
| uint32_t transferLength, |
| std::function<void(const std::error_code&, int nvmeStatus, |
| std::span<uint8_t> data)>&& cb) override; |
| |
| void adminNonDataCmd( |
| nvme_mi_ctrl_t ctrl, uint8_t opcode, uint32_t cdw1, uint32_t cdw2, |
| uint32_t cdw3, uint32_t cdw10, uint32_t cdw11, uint32_t cdw12, |
| uint32_t cdw13, uint32_t cdw14, uint32_t cdw15, |
| std::function<void(const std::error_code&, int nvmeStatus, |
| uint32_t comptionDw0)>&& cb) override; |
| |
| void createNamespace( |
| nvme_mi_ctrl_t ctrl, uint64_t size, size_t lbaFormat, |
| bool metadataAtEnd, std::function<void(nvme_ex_ptr ex)>&& submittedCb, |
| std::function<void(nvme_ex_ptr ex, NVMeNSIdentify newid)>&& finishedCb) |
| override; |
| |
| void adminDeleteNamespace( |
| nvme_mi_ctrl_t ctrl, uint32_t nsid, |
| std::function<void(const std::error_code&, int nvmeStatus)>&& cb) |
| override; |
| |
| void adminListNamespaces( |
| nvme_mi_ctrl_t ctrl, |
| std::function<void(nvme_ex_ptr ex, std::vector<uint32_t> ns)>&& cb) |
| override; |
| |
| void adminAttachDetachNamespace( |
| nvme_mi_ctrl_t ctrl, uint16_t ctrlid, uint32_t nsid, bool attach, |
| std::function<void(const std::error_code&, int nvmeStatus)>&& cb) |
| override; |
| |
| void adminSanitize(nvme_mi_ctrl_t ctrl, enum nvme_sanitize_sanact sanact, |
| uint8_t passes, uint32_t pattern, bool invertPattern, |
| std::function<void(nvme_ex_ptr ex)>&& cb) override; |
| |
| void start(const std::shared_ptr<MctpEndpoint>& ep) override; |
| void stop() override; |
| void recover() override; |
| |
| private: |
| // the transfer size for nvme mi messages. |
| // define in github.com/linux-nvme/libnvme/blob/master/src/nvme/mi.c |
| static constexpr size_t nvmeMiXferSize = 4096; |
| |
| static nvme_root_t nvmeRoot; |
| |
| boost::asio::io_context& io; |
| std::shared_ptr<const MctpDevice> device; |
| |
| // power state |
| std::unique_ptr<PowerCallbackEntry> powerCallback; |
| PowerState readState; |
| |
| /* |
| * A state machine to represent the current status of the MCTP connection. |
| * In Reset state, the MCTP endpoint (EP) is not setup with the device. |
| * In event of successful setup we move from Reset to Configured. If |
| * opening the EP is successful from Configured the status will change to |
| * Initiated. The status will change to Connected once the MTU of local |
| * and device side MTU and frequency is optimized. In an event of connection |
| * EP closure, the status will move back to Reset via Terminating. |
| * |
| * Transitions to the terminal state indicate a logic error. |
| * |
| * stateDiagram |
| * [*] --> Reset |
| * |
| * Reset --> Reset: epReset() |
| * Reset --> [*]: epConnect() |
| * Reset --> [*]: epOptimize() |
| * |
| * Initiated --> Terminating: epReset() |
| * Initiated --> Initiated: epConnect() |
| * Initiated --> Connected: epOptimize() |
| * |
| * Connected --> Terminating: epReset() |
| * Connected --> Connected: epConnect() |
| * Connected --> Connected: epOptimize() |
| * |
| * Terminating --> Reset: Reset close job executes |
| * Terminating --> Terminating: epReset() |
| * Terminating --> Terminating: epConnect() |
| * Terminating --> [*]: epOptimize() |
| */ |
| enum class Status : uint8_t |
| { |
| Reset, |
| Initiated, |
| Connected, |
| Terminating, |
| }; |
| |
| void epReset(); |
| bool epConnect(int lnid, uint8_t leid); |
| void epOptimize(); |
| |
| Status mctpStatus; |
| std::shared_ptr<MctpEndpoint> endpoint; |
| uint16_t mtu; |
| nvme_mi_ep_t nvmeEP; |
| // Handle a start() while in Status::Terminating on entry to Status::Reset. |
| bool restart; |
| std::shared_ptr<boost::asio::steady_timer> optimizeTimer; |
| |
| std::shared_ptr<NVMeMiWorker> worker; |
| void post(std::function<void(void)>&& func); |
| |
| void |
| miConfigureRemoteMCTP(uint8_t port, uint16_t mtu, |
| uint8_t maxSupportedFreq, |
| std::function<void(const std::error_code&)>&& cb); |
| |
| void miConfigureSMBusFrequency( |
| uint8_t portId, uint8_t maxSupportedFreq, |
| std::function<void(const std::error_code&)>&& cb); |
| |
| void miSetMCTPConfiguration( |
| std::function<void(const std::error_code&)>&& cb); |
| |
| void configureLocalRouteMtu( |
| std::function<void(const std::error_code& ec)>&& completed, |
| int retries = 5); |
| |
| std::optional<std::error_code> isEndpointDegraded() const; |
| |
| bool readingStateGood() const |
| { |
| return ::readingStateGood(readState); |
| } |
| |
| std::error_code tryPost(std::function<void(void)>&& func); |
| |
| void adminFwDownloadChunk( |
| nvme_mi_ctrl_t ctrl, const std::string& firmwarefile, size_t size, |
| size_t offset, int attemptCount, |
| std::function<void(const std::error_code&, nvme_status_field)>&& cb); |
| |
| void getTelemetryLogChunk( |
| nvme_mi_ctrl_t ctrl, bool host, uint64_t offset, |
| std::vector<uint8_t>&& data, |
| std::function<void(const std::error_code&, std::span<uint8_t>)>&& cb); |
| |
| static size_t getBlockSize(nvme_mi_ctrl_t ctrl, size_t lbaFormat); |
| }; |