| #include "NVMeIntf.hpp" |
| #include "Utils.hpp" |
| |
| #include <boost/asio.hpp> |
| #include <sdbusplus/bus.hpp> |
| |
| #include <thread> |
| |
| class NVMeMi : public NVMeMiIntf, public std::enable_shared_from_this<NVMeMi> |
| { |
| public: |
| NVMeMi(boost::asio::io_context& io, |
| std::shared_ptr<sdbusplus::asio::connection> conn, int bus, int addr, |
| bool singleThreadMode = false, |
| PowerState readState = PowerState::always); |
| ~NVMeMi() override; |
| |
| int getNID() const override |
| { |
| return nid; |
| } |
| int getEID() const override |
| { |
| return eid; |
| } |
| 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& admin_req, |
| std::span<uint8_t> data, unsigned int timeout_ms, |
| 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 proto_specific, std::span<uint8_t> data, |
| std::function<void(const std::error_code&, |
| int nvme_status)>&& cb) override; |
| |
| void adminSecurityReceive( |
| nvme_mi_ctrl_t ctrl, uint8_t proto, uint16_t proto_specific, |
| uint32_t transfer_length, |
| std::function<void(const std::error_code&, int nvme_status, |
| 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 nvme_status, |
| uint32_t comption_dw0)>&& cb); |
| |
| void createNamespace( |
| nvme_mi_ctrl_t ctrl, uint64_t size, size_t lba_format, |
| bool metadata_at_end, |
| std::function<void(nvme_ex_ptr ex)>&& submitted_cb, |
| std::function<void(nvme_ex_ptr ex, NVMeNSIdentify newid)>&& finished_cb) |
| override; |
| |
| void adminDeleteNamespace( |
| nvme_mi_ctrl_t ctrl, uint32_t nsid, |
| std::function<void(const std::error_code&, int nvme_status)>&& 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 nvme_status)>&& cb) |
| override; |
| |
| void adminSanitize(nvme_mi_ctrl_t ctrl, enum nvme_sanitize_sanact sanact, |
| uint8_t passes, uint32_t pattern, bool invert_pattern, |
| std::function<void(nvme_ex_ptr ex)>&& cb) override; |
| |
| void start() 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 nvme_mi_xfer_size = 4096; |
| |
| static nvme_root_t nvmeRoot; |
| |
| boost::asio::io_context& io; |
| std::shared_ptr<sdbusplus::asio::connection> conn; |
| sdbusplus::bus_t& dbus; |
| |
| // I2C info |
| int bus; |
| int addr; |
| |
| // power state |
| std::unique_ptr<PowerCallbackEntry> powerCallback; |
| PowerState readState; |
| |
| // mctp connection |
| nvme_mi_ep_t nvmeEP; |
| |
| int nid; |
| uint8_t eid; |
| uint16_t mtu; |
| std::string mctpPath; |
| |
| // A worker thread for calling NVMeMI cmd. |
| class Worker |
| { |
| private: |
| bool workerStop; |
| std::mutex workerMtx; |
| std::condition_variable workerCv; |
| boost::asio::io_context workerIO; |
| bool workerIsNotified = false; |
| std::thread thread; |
| |
| public: |
| Worker(); |
| Worker(const Worker&) = delete; |
| ~Worker(); |
| void post(std::function<void(void)>&& func); |
| }; |
| |
| /* |
| * 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 and opening the EP is successful from |
| * 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. |
| */ |
| |
| enum class Status |
| { |
| Reset, |
| Initiated, |
| Connected, |
| Terminating, |
| }; |
| |
| // Handle a start() while in Status::Terminating on entry to Status::Reset. |
| bool restart; |
| std::shared_ptr<boost::asio::steady_timer> optimizeTimer; |
| |
| Status mctpStatus; |
| |
| // A map from root bus number to the Worker |
| // This map means to reuse the same worker for all NVMe EP under the same |
| // I2C root bus. There is no real physical concurrency among the i2c/mctp |
| // devices on the same bus. Though mctp kernel drive can schedule and |
| // sequencialize the transactions but assigning individual worker thread to |
| // each EP makes no sense. |
| static std::map<int, std::weak_ptr<Worker>> workerMap; |
| |
| std::shared_ptr<Worker> worker; |
| void post(std::function<void(void)>&& func); |
| |
| void stop(); |
| |
| void |
| miConfigureRemoteMCTP(uint8_t port, uint16_t mtu, |
| uint8_t max_supported_freq, |
| std::function<void(const std::error_code&)>&& cb); |
| |
| void miConfigureSMBusFrequency( |
| uint8_t port_id, uint8_t max_supported_freq, |
| std::function<void(const std::error_code&)>&& cb); |
| |
| void miSetMCTPConfiguration( |
| std::function<void(const std::error_code&)>&& cb); |
| |
| int configureLocalRouteMtu(); |
| |
| std::optional<std::error_code> isEndpointDegraded() const; |
| |
| bool readingStateGood() const |
| { |
| return ::readingStateGood(readState); |
| } |
| |
| std::error_code try_post(std::function<void(void)>&& func); |
| |
| void adminFwDownloadChunk( |
| nvme_mi_ctrl_t ctrl, std::string firmwarefile, size_t size, |
| size_t offset, int attempt_count, |
| 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); |
| |
| size_t getBlockSize(nvme_mi_ctrl_t ctrl, size_t lba_format); |
| }; |