blob: bb544cbf5917b3f6932f0684f23fef1658792b95 [file] [log] [blame]
#include "NVMeController.hpp"
#include "AsioHelper.hpp"
#include "NVMeCacheImpl.hpp"
#include "NVMeError.hpp"
#include "NVMePlugin.hpp"
#include "NVMeSubsys.hpp"
#include <sdbusplus/exception.hpp>
#include <sdbusplus/message/native_types.hpp>
#include <xyz/openbmc_project/Common/File/error.hpp>
#include <xyz/openbmc_project/Common/error.hpp>
#include <cstdio>
#include <filesystem>
#include <fstream>
#include <iostream>
// using sdbusplus::xyz::openbmc_project::Inventory::Item::server::
// StorageController;
using sdbusplus::xyz::openbmc_project::NVMe::server::NVMeAdmin;
using SchedulerClockType = std::chrono::steady_clock;
inline std::optional<std::string> findPluginName(const SensorData& config)
{
// find base configuration
auto sensorBase = config.find(configInterfaceName(nvme::sensorType));
if (sensorBase == config.end())
{
return {};
}
const SensorBaseConfigMap& sensorConfig = sensorBase->second;
auto findPlugin = sensorConfig.find("Plugin");
if (findPlugin == sensorConfig.end())
{
return std::nullopt;
}
return std::get<std::string>(findPlugin->second);
}
std::shared_ptr<NVMeControllerEnabled>
NVMeControllerEnabled::create(NVMeController&& nvmeController)
{
auto self = std::shared_ptr<NVMeControllerEnabled>(
new NVMeControllerEnabled(std::move(nvmeController)));
self->init();
return self;
}
NVMeControllerEnabled::NVMeControllerEnabled(NVMeController&& nvmeController) :
NVMeController(nvmeController),
NVMeAdmin(*this->NVMeController::conn, this->NVMeController::path.c_str(),
{{"FirmwareCommitStatus", {FwCommitStatus::Ready}},
{"FirmwareDownloadStatus", {FwDownloadStatus::Ready}}}),
SoftwareExtVersion(dynamic_cast<sdbusplus::bus_t&>(*conn), path.c_str()),
SoftwareVersion(dynamic_cast<sdbusplus::bus_t&>(*conn), path.c_str())
{}
void NVMeControllerEnabled::init()
{
createAssociation();
passthruInterface =
objServer.add_interface(path, "xyz.openbmc_project.NVMe.Passthru");
passthruInterface->register_method(
"AdminNonDataCmd",
[selfWeak{weak_from_this()}](
boost::asio::yield_context yield, 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) {
auto self = selfWeak.lock();
if (!self)
{
checkLibNVMeError(std::make_error_code(std::errc::no_such_device),
-1, "AdminNonDataCmd");
return std::tuple<uint32_t, uint32_t, uint32_t>{0, 0, 0};
}
if (self->status != Status::Enabled)
{
std::cerr << "Controller has been disabled" << '\n';
throw sdbusplus::xyz::openbmc_project::Common::Error::Unavailable();
}
return self->adminNonDataCmdMethod(std::move(yield), opcode, cdw1, cdw2,
cdw3, cdw10, cdw11, cdw12, cdw13,
cdw14, cdw15);
});
passthruInterface->initialize();
securityInterface = objServer.add_interface(
path, "xyz.openbmc_project.Inventory.Item.StorageControllerSecurity");
securityInterface->register_method(
"SecuritySend", [selfWeak{weak_from_this()}](
boost::asio::yield_context yield, uint8_t proto,
uint16_t protoSpecific, std::vector<uint8_t> data) {
auto self = selfWeak.lock();
if (!self)
{
checkLibNVMeError(std::make_error_code(std::errc::no_such_device),
-1, "SecuritySend");
return;
}
if (self->status != Status::Enabled)
{
std::cerr << "Controller has been disabled" << '\n';
throw sdbusplus::xyz::openbmc_project::Common::Error::Unavailable();
}
return self->securitySendMethod(std::move(yield), proto, protoSpecific,
data);
});
securityInterface->register_method(
"SecurityReceive",
[selfWeak{weak_from_this()}](boost::asio::yield_context yield,
uint8_t proto, uint16_t protoSpecific,
uint32_t transferLength) {
auto self = selfWeak.lock();
if (!self)
{
checkLibNVMeError(std::make_error_code(std::errc::no_such_device),
-1, "SecurityReceive");
return std::vector<uint8_t>{};
}
if (self->status != Status::Enabled)
{
std::cerr << "Controller has been disabled" << '\n';
throw sdbusplus::xyz::openbmc_project::Common::Error::Unavailable();
}
return self->securityReceiveMethod(std::move(yield), proto,
protoSpecific, transferLength);
});
// StorageController interface is implemented manually to allow
// async methods
ctrlInterface = objServer.add_interface(
path, "xyz.openbmc_project.Inventory.Item.StorageController");
ctrlInterface->register_method(
"AttachVolume", [selfWeak{weak_from_this()}](
boost::asio::yield_context yield,
const sdbusplus::message::object_path& volPath) {
auto self = selfWeak.lock();
if (!self)
{
checkLibNVMeError(std::make_error_code(std::errc::no_such_device),
-1, "SecurityReceive");
return;
}
if (self->status != Status::Enabled)
{
std::cerr << "Controller has been disabled" << '\n';
throw sdbusplus::xyz::openbmc_project::Common::Error::Unavailable();
}
return self->attachVolume(std::move(yield), volPath);
});
ctrlInterface->register_method(
"DetachVolume", [selfWeak{weak_from_this()}](
boost::asio::yield_context yield,
const sdbusplus::message::object_path& volPath) {
auto self = selfWeak.lock();
if (!self)
{
checkLibNVMeError(std::make_error_code(std::errc::no_such_device),
-1, "SecurityReceive");
return;
}
if (self->status != Status::Enabled)
{
std::cerr << "Controller has been disabled" << '\n';
throw sdbusplus::xyz::openbmc_project::Common::Error::Unavailable();
}
return self->detachVolume(std::move(yield), volPath);
});
ctrlInterface->initialize();
securityInterface->initialize();
// StorageController::emit_added();
NVMeAdmin::emit_added();
SoftwareExtVersion::emit_added();
SoftwareVersion::emit_added();
}
void NVMeControllerEnabled::start(
const std::shared_ptr<NVMeControllerPlugin>& nvmePlugin)
{
this->NVMeController::start(nvmePlugin);
if (isPrimary)
{
// only to test compilation. need to remove.
this->metricStore.emplace(this->NVMeController::io,
this->NVMeController::conn,
this->NVMeController::path);
auto scheduler = Scheduler<SchedulerClockType>::getScheduler(path);
using IdentifyMetric =
IdentifyMetric<NVME_IDENTIFY_CNS_CTRL, SchedulerClockType>;
using ErrorInfoMetric =
ControllerLogPageMetric<NVME_LOG_LID_ERROR, SchedulerClockType>;
using SMARTMetric =
ControllerLogPageMetric<NVME_LOG_LID_SMART, SchedulerClockType>;
using FwSlotMetric =
ControllerLogPageMetric<NVME_LOG_LID_FW_SLOT, SchedulerClockType>;
using SelfTestMetric =
ControllerLogPageMetric<NVME_LOG_LID_DEVICE_SELF_TEST,
SchedulerClockType>;
using HostTelemetryMetric =
ControllerLogPageMetric<NVME_LOG_LID_TELEMETRY_HOST,
SchedulerClockType>;
// NOLINTBEGIN(modernize-make-shared)
auto identify = std::shared_ptr<IdentifyMetric>(new IdentifyMetric(
this->shared_from_this(), scheduler,
// Metigate solution before Knuckle FW fix for b/404610159
std::chrono::seconds(60)));
auto errorInfo = std::shared_ptr<ErrorInfoMetric>(new ErrorInfoMetric(
this->weak_from_this(), scheduler,
// TODO: configurable interval
std::chrono::duration_cast<SchedulerClockType::duration>(
std::chrono::seconds(60))));
auto smart = std::shared_ptr<SMARTMetric>(new SMARTMetric(
this->weak_from_this(), scheduler,
// TODO: configurable interval
std::chrono::duration_cast<SchedulerClockType::duration>(
std::chrono::seconds(60))));
auto fwSlot = std::shared_ptr<FwSlotMetric>(new FwSlotMetric(
this->weak_from_this(), scheduler,
// TODO: configurable interval
std::chrono::duration_cast<SchedulerClockType::duration>(
std::chrono::seconds(60))));
auto selfTest = std::shared_ptr<SelfTestMetric>(new SelfTestMetric(
this->weak_from_this(), scheduler,
// TODO: configurable interval
std::chrono::duration_cast<SchedulerClockType::duration>(
std::chrono::seconds(60))));
auto pluginName = findPluginName(config);
auto hostTelemetry =
(!pluginName || pluginName != "OCP")
? std::shared_ptr<HostTelemetryMetric>(new HostTelemetryMetric(
this->weak_from_this(), scheduler,
// TODO: configurable interval
std::chrono::duration_cast<SchedulerClockType::duration>(
std::chrono::seconds(900))))
: std::shared_ptr<HostTelemetryMetric>();
// NOLINTEND(modernize-make-shared)
this->metricStore.emplace(
this->NVMeController::io, this->NVMeController::conn,
this->NVMeController::path, std::move(identify),
std::move(errorInfo), std::move(smart), std::move(fwSlot),
std::move(selfTest), std::move(hostTelemetry));
}
status = Status::Enabled;
}
void NVMeControllerEnabled::stop()
{
status = Status::Disabled;
this->metricStore.reset();
}
void NVMeController::createAssociation()
{
assocIntf = objServer.add_interface(path, association::interface);
assocIntf->register_property("Associations", makeAssociation());
assocIntf->initialize();
}
void NVMeController::updateAssociation()
{
if (assocIntf)
{
assocIntf->set_property("Associations", makeAssociation());
}
}
std::vector<Association> NVMeController::makeAssociation() const
{
std::vector<Association> associations;
std::filesystem::path p(path);
auto s = subsys.lock();
if (!s)
{
std::cerr << "makeAssociation() after shutdown\n";
return associations;
}
associations.emplace_back("storage", "storage_controller", s->path);
for (const auto& cntrl : secondaryControllers)
{
associations.emplace_back("secondary", "primary", cntrl);
}
for (uint32_t nsid : s->attachedVolumes(getCntrlId()))
{
auto p = s->volumePath(nsid);
associations.emplace_back("attaching", "attached", p);
}
return associations;
}
sdbusplus::message::unix_fd NVMeControllerEnabled::getLogPage(uint8_t lid,
uint32_t nsid,
uint8_t lsp,
uint16_t lsi)
{
if (status != Status::Enabled)
{
std::cerr << "Controller has been disabled" << '\n';
throw sdbusplus::xyz::openbmc_project::Common::Error::Unavailable();
}
std::array<int, 2> pipe{};
if (::pipe(pipe.data()) < 0)
{
std::cerr << "GetLogPage fails to open pipe: " << std::strerror(errno)
<< '\n';
throw sdbusplus::xyz::openbmc_project::Common::File::Error::Open();
}
// standard NVMe Log IDs
if (lid < uint8_t{0xC0})
{
nvmeIntf->adminGetLogPage(
nvmeCtrl, static_cast<nvme_cmd_get_log_lid>(lid), nsid, lsp, lsi,
[pipe, &io = this->io](const std::error_code& ec,
std::span<uint8_t> data) {
if (ec)
{
std::cerr << "fail to GetLogPage: " << ec.message() << '\n';
close(pipe[1]);
close(pipe[0]);
return;
}
// async write to pipe
auto asyncPipe =
std::make_shared<boost::asio::posix::stream_descriptor>(
io, pipe[1]);
std::weak_ptr<boost::asio::posix::stream_descriptor> weakPipe =
asyncPipe;
auto asyncBuffer = std::make_shared<std::vector<uint8_t>>(
data.begin(), data.end());
boost::asio::async_write(
*asyncPipe, boost::asio::buffer(*asyncBuffer),
[asyncPipe, asyncBuffer](const boost::system::error_code& error,
std::size_t /* transferredSize */) {
if (error)
{
std::cerr
<< "GetLogPage fails to write fd: " << error.message()
<< '\n';
}
});
// set a timer of 5 second for the client to read the telemetry.
auto timer = std::make_shared<boost::asio::steady_timer>(io);
timer->expires_after(std::chrono::seconds(5));
timer->async_wait([timer, fd{pipe[0]}, weakPipe](
const boost::system::error_code& /* ec */) {
auto pipe = weakPipe.lock();
if (pipe)
{
pipe->cancel();
}
close(fd);
});
});
}
// vendor Log IDs
else if (!plugin.expired())
{
auto nvmePlugin = plugin.lock();
auto handler = nvmePlugin->getGetLogPageHandler();
if (handler)
{
std::function<void(const std::error_code&, std::span<uint8_t>)> cb =
[pipe, &io = this->io](std::error_code ec,
std::span<uint8_t> data) {
if (ec)
{
std::cerr << "fail to GetLogPage: " << ec.message() << '\n';
close(pipe[1]);
close(pipe[0]);
return;
}
// async write to pipe
auto asyncPipe =
std::make_shared<boost::asio::posix::stream_descriptor>(
io, pipe[1]);
std::weak_ptr<boost::asio::posix::stream_descriptor> weakPipe =
asyncPipe;
auto asyncBuffer = std::make_shared<std::vector<uint8_t>>(
data.begin(), data.end());
boost::asio::async_write(
*asyncPipe, boost::asio::buffer(*asyncBuffer),
[asyncPipe,
asyncBuffer](const boost::system::error_code& error,
std::size_t /* transferredSize */) {
if (error)
{
std::cerr << "GetLogPage fails to write fd: "
<< error.message() << '\n';
}
});
// set a timer of 5 second for the client to read the telemetry.
auto timer = std::make_shared<boost::asio::steady_timer>(io);
timer->expires_after(std::chrono::seconds(5));
timer->async_wait(
[timer, fd{pipe[0]},
weakPipe](const boost::system::error_code& /* ec */) {
auto pipe = weakPipe.lock();
if (pipe)
{
pipe->cancel();
}
close(fd);
});
};
handler(lid, nsid, lsp, lsi, std::move(cb));
}
else // No VU LogPage handler
{
close(pipe[1]);
close(pipe[0]);
throw sdbusplus::xyz::openbmc_project::Common::Error::
InvalidArgument();
}
}
else // No VU plugin
{
close(pipe[0]);
close(pipe[1]);
throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
}
return sdbusplus::message::unix_fd{pipe[0]};
}
sdbusplus::message::unix_fd
NVMeControllerEnabled::identify(uint8_t cns, uint32_t nsid, uint16_t cntid)
{
if (status != Status::Enabled)
{
std::cerr << "Controller has been disabled" << '\n';
throw sdbusplus::xyz::openbmc_project::Common::Error::Unavailable();
}
std::array<int, 2> pipe{};
if (::pipe(pipe.data()) < 0)
{
std::cerr << "Identify fails to open pipe: " << std::strerror(errno)
<< '\n';
throw sdbusplus::xyz::openbmc_project::Common::File::Error::Open();
}
nvmeIntf->adminIdentify(
nvmeCtrl, static_cast<nvme_identify_cns>(cns), nsid, cntid,
[pipe, &io = this->io](const nvme_ex_ptr& ex, std::span<uint8_t> data) {
if (ex)
{
std::cerr << "fail to Identify: " << ex << '\n';
close(pipe[1]);
close(pipe[0]);
return;
}
// async write to pipe
auto asyncPipe =
std::make_shared<boost::asio::posix::stream_descriptor>(io,
pipe[1]);
std::weak_ptr<boost::asio::posix::stream_descriptor> weakPipe =
asyncPipe;
auto asyncBuffer = std::make_shared<std::vector<uint8_t>>(data.begin(),
data.end());
boost::asio::async_write(
*asyncPipe, boost::asio::buffer(*asyncBuffer),
[asyncPipe, asyncBuffer](const boost::system::error_code& error,
std::size_t /* transferredSize */) {
if (error)
{
std::cerr << "Identify fails to write fd: " << error.message()
<< '\n';
}
});
// set a timer of 5 second for the client to read the telemetry.
auto timer = std::make_shared<boost::asio::steady_timer>(io);
timer->expires_after(std::chrono::seconds(5));
timer->async_wait([timer, fd{pipe[0]}, weakPipe](
const boost::system::error_code& /* ec */) {
auto pipe = weakPipe.lock();
if (pipe)
{
pipe->cancel();
}
close(fd);
});
});
return sdbusplus::message::unix_fd{pipe[0]};
}
NVMeAdmin::FwCommitStatus NVMeControllerEnabled::firmwareCommitStatus(
NVMeAdmin::FwCommitStatus status)
{
auto commitStatus = this->NVMeAdmin::firmwareCommitStatus();
// The function is only allowed to reset the status back to ready
if (status != FwCommitStatus::Ready ||
commitStatus == FwCommitStatus::Ready ||
commitStatus == FwCommitStatus::InProgress)
{
throw sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed{};
}
return this->NVMeAdmin::firmwareCommitStatus(status);
}
void NVMeControllerEnabled::firmwareCommitAsync(uint8_t commitAction,
uint8_t firmwareSlot, bool bpid)
{
auto commitStatus = this->NVMeAdmin::firmwareCommitStatus();
if (commitStatus != FwCommitStatus::Ready)
{
throw sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed();
}
if (status != Status::Enabled)
{
std::cerr << "Controller has been disabled" << '\n';
throw sdbusplus::xyz::openbmc_project::Common::Error::Unavailable();
}
this->NVMeAdmin::firmwareCommitStatus(FwCommitStatus::InProgress);
nvmeIntf->adminFwCommit(
nvmeCtrl, static_cast<nvme_fw_commit_ca>(commitAction & 0b111),
firmwareSlot, bpid,
[self{shared_from_this()}](const std::error_code& ec,
nvme_status_field status) {
if (ec)
{
self->NVMeAdmin::firmwareCommitStatus(FwCommitStatus::Failed);
return;
}
if (status != NVME_SC_SUCCESS)
{
self->NVMeAdmin::firmwareCommitStatus(FwCommitStatus::RequireReset);
return;
}
self->NVMeAdmin::firmwareCommitStatus(FwCommitStatus::Success);
});
}
NVMeAdmin::FwDownloadStatus NVMeControllerEnabled::firmwareDownloadStatus(
NVMeAdmin::FwDownloadStatus status)
{
auto downloadStatus = this->NVMeAdmin::firmwareDownloadStatus();
// The function is only allowed to reset the status back to ready
if (status != FwDownloadStatus::Ready ||
downloadStatus == FwDownloadStatus::Ready ||
downloadStatus == FwDownloadStatus::InProgress)
{
throw sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed{};
}
return this->NVMeAdmin::firmwareDownloadStatus(status);
}
void NVMeControllerEnabled::firmwareDownloadAsync(std::string pathToImage)
{
auto downloadStatus = this->NVMeAdmin::firmwareDownloadStatus();
if (downloadStatus != FwDownloadStatus::Ready)
{
throw sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed();
}
if (std::filesystem::exists(pathToImage))
{
this->NVMeAdmin::firmwareDownloadStatus(FwDownloadStatus::InProgress);
nvmeIntf->adminFwDownload(
nvmeCtrl, pathToImage,
[self{shared_from_this()}](const std::error_code& ec,
nvme_status_field status) {
if (ec || status != NVME_SC_SUCCESS)
{
self->NVMeAdmin::firmwareDownloadStatus(
FwDownloadStatus::Failed);
return;
}
self->NVMeAdmin::firmwareDownloadStatus(FwDownloadStatus::Success);
});
}
else
{
throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument();
}
}
NVMeControllerEnabled::~NVMeControllerEnabled()
{
objServer.remove_interface(securityInterface);
objServer.remove_interface(passthruInterface);
SoftwareVersion::emit_removed();
SoftwareExtVersion::emit_removed();
NVMeAdmin::emit_removed();
// StorageController::emit_removed();
objServer.remove_interface(ctrlInterface);
}
NVMeController::NVMeController(
boost::asio::io_context& io, sdbusplus::asio::object_server& objServer,
std::shared_ptr<sdbusplus::asio::connection> conn, std::string path,
const SensorData& configData, std::shared_ptr<NVMeMiIntf> nvmeIntf,
nvme_mi_ctrl_t ctrl, std::weak_ptr<NVMeSubsystem> subsys) :
isPrimary(true), io(io), objServer(objServer), conn(std::move(conn)),
path(std::move(path)), config(configData), nvmeIntf(std::move(nvmeIntf)),
nvmeCtrl(ctrl), subsys(std::move(subsys))
{}
NVMeController::~NVMeController()
{
objServer.remove_interface(assocIntf);
}
void NVMeController::start(
const std::shared_ptr<NVMeControllerPlugin>& nvmePlugin)
{
plugin = nvmePlugin;
}
void NVMeController::stop() {}
void NVMeController::setSecAssoc(
const std::vector<std::shared_ptr<NVMeController>>& secCntrls)
{
secondaryControllers.clear();
if (secCntrls.empty())
{
return;
}
for (const auto& cntrl : secCntrls)
{
secondaryControllers.push_back(cntrl->path);
}
updateAssociation();
}
void NVMeControllerEnabled::securitySendMethod(boost::asio::yield_context yield,
uint8_t proto,
uint16_t protoSpecific,
std::span<uint8_t> data)
{
if (status != Status::Enabled)
{
std::cerr << "Controller has been disabled" << '\n';
throw sdbusplus::xyz::openbmc_project::Common::Error::Unavailable();
}
using callback_t = void(std::tuple<std::error_code, int>);
auto [err, nvmeStatus] =
boost::asio::async_initiate<boost::asio::yield_context, callback_t>(
[intf{nvmeIntf}, ctrl{nvmeCtrl}, proto, protoSpecific,
&data](auto&& handler) {
auto h = asio_helper::CopyableCallback(
std::forward<decltype(handler)>(handler));
intf->adminSecuritySend(
ctrl, proto, protoSpecific, data,
[h](const std::error_code& err, int nvmeStatus) mutable {
h(std::make_tuple(err, nvmeStatus));
});
},
yield);
// exception must be thrown outside of the async block
checkLibNVMeError(err, nvmeStatus, "SecuritySend");
}
std::vector<uint8_t> NVMeControllerEnabled::securityReceiveMethod(
boost::asio::yield_context yield, uint8_t proto, uint16_t protoSpecific,
uint32_t transferLength)
{
if (status != Status::Enabled)
{
std::cerr << "Controller has been disabled" << '\n';
throw sdbusplus::xyz::openbmc_project::Common::Error::Unavailable();
}
using callback_t =
void(std::tuple<std::error_code, int, std::vector<uint8_t>>);
auto [err, nvmeStatus, data] =
boost::asio::async_initiate<boost::asio::yield_context, callback_t>(
[intf{nvmeIntf}, ctrl{nvmeCtrl}, proto, protoSpecific,
transferLength](auto&& handler) {
auto h = asio_helper::CopyableCallback(
std::forward<decltype(handler)>(handler));
intf->adminSecurityReceive(ctrl, proto, protoSpecific, transferLength,
[h](const std::error_code& err,
int nvmeStatus,
std::span<uint8_t> data) mutable {
std::vector<uint8_t> d(data.begin(), data.end());
h(std::make_tuple(err, nvmeStatus, d));
});
},
yield);
// exception must be thrown outside of the async block
checkLibNVMeError(err, nvmeStatus, "SecurityReceive");
return data;
}
std::tuple<uint32_t, uint32_t, uint32_t>
NVMeControllerEnabled::adminNonDataCmdMethod(
boost::asio::yield_context yield, 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)
{
using callback_t = void(std::tuple<std::error_code, int, uint32_t>);
auto [err, nvmeStatus, completionDw0] =
boost::asio::async_initiate<boost::asio::yield_context, callback_t>(
[intf{nvmeIntf}, ctrl{nvmeCtrl}, opcode, cdw1, cdw2, cdw3, cdw10,
cdw11, cdw12, cdw13, cdw14, cdw15](auto&& handler) {
auto h = asio_helper::CopyableCallback(
std::forward<decltype(handler)>(handler));
intf->adminNonDataCmd(ctrl, opcode, cdw1, cdw2, cdw3, cdw10, cdw11,
cdw12, cdw13, cdw14, cdw15,
[h](const std::error_code& err, int nvmeStatus,
uint32_t completionDw0) mutable {
h(std::make_tuple(err, nvmeStatus, completionDw0));
});
},
yield);
std::cerr << "nvme_status:" << nvmeStatus << ", dw0:" << completionDw0
<< '\n';
if (nvmeStatus < 0)
{
throw sdbusplus::exception::SdBusError(err.value(),
"adminNonDataCmdMethod");
}
// Parse MI status Or MI status from nvme_status
uint32_t miStatus = 0;
uint32_t adminStatus = 0;
if (nvme_status_get_type(nvmeStatus) == NVME_STATUS_TYPE_MI)
{
// there is no Admin status and dw0 if MI layer failed.
miStatus = nvme_status_get_value(nvmeStatus);
adminStatus = 0;
completionDw0 = 0;
}
else
{
miStatus = 0;
adminStatus = nvme_status_get_value(nvmeStatus);
}
return {miStatus, adminStatus, completionDw0};
}
void NVMeControllerEnabled::attachVolume(
boost::asio::yield_context yield,
const sdbusplus::message::object_path& volumePath)
{
if (status != Status::Enabled)
{
std::cerr << "Controller has been disabled" << '\n';
throw sdbusplus::xyz::openbmc_project::Common::Error::Unavailable();
}
uint32_t nsid = 0;
if (auto s = subsys.lock())
{
auto vol = s->getVolume(volumePath);
if (!vol)
{
throw sdbusplus::exception::SdBusError(ENOENT, "attachVolume");
}
nsid = vol->namespaceId();
}
else
{
return;
}
using callback_t = void(std::tuple<std::error_code, int>);
uint16_t ctrlid = getCntrlId();
auto [err, nvmeStatus] =
boost::asio::async_initiate<boost::asio::yield_context, callback_t>(
[intf{nvmeIntf}, ctrl{nvmeCtrl}, ctrlid, nsid](auto&& handler) {
auto h = asio_helper::CopyableCallback(
std::forward<decltype(handler)>(handler));
intf->adminAttachDetachNamespace(
ctrl, ctrlid, nsid, true,
[h](const std::error_code& err, int nvmeStatus) mutable {
h(std::make_tuple(err, nvmeStatus));
});
}, yield);
// exception must be thrown outside of the async block
checkLibNVMeError(err, nvmeStatus, "attachVolume");
if (status == Status::Enabled)
{
if (auto s = subsys.lock())
{
try
{
s->attachCtrlVolume(getCntrlId(), nsid);
}
catch (const std::exception& e)
{
std::cerr << e.what() << '\n';
throw sdbusplus::xyz::openbmc_project::Common::Error::
InternalFailure();
}
}
updateAssociation();
}
}
void NVMeControllerEnabled::detachVolume(
boost::asio::yield_context yield,
const sdbusplus::message::object_path& volumePath)
{
if (status != Status::Enabled)
{
std::cerr << "Controller has been disabled" << '\n';
throw sdbusplus::xyz::openbmc_project::Common::Error::Unavailable();
}
uint32_t nsid = 0;
if (auto s = subsys.lock())
{
auto vol = s->getVolume(volumePath);
if (!vol)
{
throw sdbusplus::exception::SdBusError(ENOENT, "detachVolume");
}
nsid = vol->namespaceId();
}
else
{
return;
}
using callback_t = void(std::tuple<std::error_code, int>);
uint16_t ctrlid = getCntrlId();
auto [err, nvmeStatus] =
boost::asio::async_initiate<boost::asio::yield_context, callback_t>(
[intf{nvmeIntf}, ctrl{nvmeCtrl}, ctrlid, nsid](auto&& handler) {
auto h = asio_helper::CopyableCallback(
std::forward<decltype(handler)>(handler));
intf->adminAttachDetachNamespace(
ctrl, ctrlid, nsid, false,
[h](const std::error_code& err, int nvmeStatus) mutable {
h(std::make_tuple(err, nvmeStatus));
});
}, yield);
// exception must be thrown outside of the async block
checkLibNVMeError(err, nvmeStatus, "detachVolume");
if (status == Status::Enabled)
{
if (auto s = subsys.lock())
{
try
{
s->detachCtrlVolume(getCntrlId(), nsid);
}
catch (const std::exception& e)
{
std::cerr << e.what() << '\n';
throw sdbusplus::xyz::openbmc_project::Common::Error::
InternalFailure();
}
}
updateAssociation();
}
}