blob: 1cd2c7825fa1cc60fafbc1f628c02b38eb270298 [file] [log] [blame]
#include "NVMeDrive.hpp"
#include "NVMeError.hpp"
#include "NVMeSubsys.hpp"
#include <iostream>
namespace CommonErr = sdbusplus::xyz::openbmc_project::Common::Error;
NVMeDrive::NVMeDrive(boost::asio::io_context& io,
const std::shared_ptr<sdbusplus::asio::connection>& conn,
const std::string& path,
std::weak_ptr<NVMeSubsystem> subsys) :
DriveBase(dynamic_cast<sdbusplus::bus_t&>(*conn), path.c_str()),
DriveErase(dynamic_cast<sdbusplus::bus_t&>(*conn), path.c_str()),
Asset(dynamic_cast<sdbusplus::bus_t&>(*conn), path.c_str()), path(path),
sanitizeTimer(io), io(io), subsys(std::move(subsys))
{
DriveBase::emit_added();
DriveErase::emit_added();
Asset::emit_added();
}
NVMeDrive::~NVMeDrive()
{
sanitizeTimer.cancel();
Asset::emit_removed();
DriveErase::emit_removed();
DriveBase::emit_removed();
}
// DriveErase.Erase method handler
void NVMeDrive::erase(EraseAction action)
{
std::shared_ptr<NVMeSubsystem> s = subsys.lock();
if (!s)
{
throw std::runtime_error("Sanitize called while shutting down");
}
NVMeSanitizeParams params(action);
if (eraseInProgress())
{
// Already running
if (params == sanitizeParams)
{
return;
}
// TODO: (b/375054188) Exception handling to be refactored
// NOLINTNEXTLINE(cert-err09-cpp,cert-err60-cpp,cert-err61-cpp,misc-throw-by-value-catch-by-reference)
throw *makeLibNVMeError(
"sanitize already in progress with different parameters",
std::make_shared<CommonErr::Unavailable>());
}
sanitizeParams = params;
// Clear properties
erasePercentage(0.0);
errorName("");
errorDescription("");
// eraseInProgress() should always be set last.
eraseInProgress(true);
s->startSanitize(params, [self{shared_from_this()}](const nvme_ex_ptr& ex) {
if (ex)
{
// Update properties with the submission failure
self->erasePercentage(0.0);
self->errorName(ex->name());
self->errorDescription(ex->description());
self->eraseInProgress(false);
}
else
{
// start the timer
self->sanitizePoll();
}
});
}
void NVMeDrive::sanitizePoll()
{
if (sanitizePollPending)
{
return;
}
sanitizePollPending = true;
sanitizeTimer.expires_after(std::chrono::seconds(sanitizePollIntervalSecs));
sanitizeTimer.async_wait(
[weak{weak_from_this()}](boost::system::error_code ec) {
if (ec == boost::asio::error::operation_aborted)
{
return;
}
auto self = weak.lock();
if (!self)
{
return;
}
std::shared_ptr<NVMeSubsystem> s = self->subsys.lock();
if (!s)
{
return;
}
self->sanitizePollPending = false;
if (self->eraseInProgress())
{
s->sanitizeStatus([self](const nvme_ex_ptr& ex, bool inProgress,
bool failed, bool completed,
uint16_t sstat, uint16_t sprog,
uint32_t scdw10) {
(void)sstat;
(void)sprog;
(void)scdw10;
if (ex)
{
std::cerr << "Error returned reading sanitize log: " << ex
<< '\n';
}
else
{
if (completed)
{
self->erasePercentage(0.0);
self->eraseInProgress(false);
}
else if (failed)
{
self->erasePercentage(0.0);
self->eraseInProgress(false);
self->errorName(
CommonErr::DeviceOperationFailed::errName);
self->errorDescription("Sanitize operation failed");
}
else if (inProgress)
{
self->erasePercentage(100.0 * sprog / 0x10000);
}
}
if (self->eraseInProgress())
{
self->sanitizePoll();
}
});
}
});
}
NVMeSanitizeParams::NVMeSanitizeParams(EraseAction sanact) :
sanact(sanact), passes(1), pattern(0x0), patternInvert(false)
{}
enum nvme_sanitize_sanact NVMeSanitizeParams::nvmeAction() const
{
switch (sanact)
{
case EraseAction::BlockErase:
return NVME_SANITIZE_SANACT_START_BLOCK_ERASE;
case EraseAction::CryptoErase:
return NVME_SANITIZE_SANACT_START_CRYPTO_ERASE;
case EraseAction::Overwrite:
return NVME_SANITIZE_SANACT_START_OVERWRITE;
}
throw std::logic_error("unreachable");
}
bool NVMeSanitizeParams::matchesDword10(uint32_t dword10) const
{
uint32_t ownDword10 = 0;
// reconstruct the dword10 sent by libnvme
ownDword10 |= (uint32_t)nvmeAction();
ownDword10 |= (((uint32_t)patternInvert) << 8);
ownDword10 |= (((uint32_t)passes & 0xf) << 4);
return dword10 == ownDword10;
}