blob: 2764288ed4bdd838f33b0d52fb1bad9be189e2ef [file] [log] [blame]
#include "NVMeError.hpp"
#include <libnvme-mi.h>
#include <xyz/openbmc_project/Common/Device/error.hpp>
#include <xyz/openbmc_project/Common/error.hpp>
namespace CommonErr = sdbusplus::xyz::openbmc_project::Common::Error;
NVMeSdBusPlusError::NVMeSdBusPlusError(std::string_view desc) : desc(desc)
{
init();
}
NVMeSdBusPlusError::NVMeSdBusPlusError(
std::shared_ptr<sdbusplus::exception_t> specific) :
specific(std::move(specific)) // NOLINT
{
init();
}
NVMeSdBusPlusError::NVMeSdBusPlusError(
std::string_view desc, std::shared_ptr<sdbusplus::exception_t> specific) :
desc(desc), specific(std::move(specific)) // NOLINT
{
init();
}
void NVMeSdBusPlusError::init()
{
whatMsg = std::string(name()) + ": " + description(); // NOLINT
}
const char* NVMeSdBusPlusError::name() const noexcept
{
if (specific)
{
return specific->name();
}
return CommonErr::InternalFailure().name();
}
const char* NVMeSdBusPlusError::description() const noexcept
{
if (!desc.empty())
{
return desc.c_str();
}
if (specific)
{
return specific->description();
}
return "nvmesensor internal error";
}
const char* NVMeSdBusPlusError::what() const noexcept
{
return whatMsg.c_str();
}
int NVMeSdBusPlusError::get_errno() const noexcept
{
if (specific)
{
return specific->get_errno();
}
// arbitrary, sdbusplus method return ignores this errno
return EIO;
}
void NVMeSdBusPlusError::print(std::ostream& o) const
{
o << description();
}
/* Converts a subset of known status codes to dbus enums */
static void translateLibNVMe(int val, std::string& desc,
std::shared_ptr<sdbusplus::exception_t>& specific)
{
uint16_t sc = nvme_status_code(val);
uint16_t sct = nvme_status_code_type(val);
switch (sct)
{
case NVME_SCT_GENERIC:
switch (sc)
{
case NVME_SC_INVALID_FIELD:
specific = std::make_shared<CommonErr::InvalidArgument>();
break;
case NVME_SC_CAP_EXCEEDED:
specific = std::make_shared<CommonErr::TooManyResources>();
break;
case NVME_SC_SANITIZE_IN_PROGRESS:
specific = std::make_shared<CommonErr::Unavailable>();
break;
default:
specific =
std::make_shared<CommonErr::DeviceOperationFailed>();
}
break;
case NVME_SCT_CMD_SPECIFIC:
switch (sc)
{
case NVME_SC_INVALID_FORMAT:
specific = std::make_shared<CommonErr::InvalidArgument>();
break;
case NVME_SC_INSUFFICIENT_CAP:
case NVME_SC_NS_INSUFFICIENT_CAP:
case NVME_SC_NS_ID_UNAVAILABLE:
case NVME_SC_NS_ATTACHMENT_LIMIT_EXCEEDED:
specific = std::make_shared<CommonErr::TooManyResources>();
break;
case NVME_SC_FW_NEEDS_SUBSYS_RESET:
case NVME_SC_FW_NEEDS_RESET:
specific = std::make_shared<CommonErr::Unavailable>();
break;
default:
specific =
std::make_shared<CommonErr::DeviceOperationFailed>();
}
break;
default:
specific = std::make_shared<CommonErr::DeviceOperationFailed>();
}
// always return the description from libnvme
std::ostringstream s;
s << "NVMe: " << nvme_status_to_string(val, false) << " (SCT " << sct
<< " SC 0x" << std::hex << sc << ")";
desc = s.str();
}
static void
translateLibNVMeMI(int val, std::string& desc,
std::shared_ptr<sdbusplus::exception_t>& specific)
{
switch (val)
{
case NVME_MI_RESP_INVALID_PARAM:
specific = std::make_shared<CommonErr::InvalidArgument>();
break;
case NVME_MI_RESP_SANITIZE_IN_PROGRESS:
specific = std::make_shared<CommonErr::Unavailable>();
break;
// INVALID_CMD_SIZE is returned by some drives
case NVME_MI_RESP_INVALID_OPCODE:
case NVME_MI_RESP_INVALID_CMD_SIZE:
specific = std::make_shared<CommonErr::UnsupportedRequest>();
break;
default:
specific = std::make_shared<CommonErr::DeviceOperationFailed>();
}
// always return the description from libnvme
std::ostringstream s;
s << "NVMe MI: " << nvme_mi_status_to_string(val) << " (MI status 0x"
<< std::hex << val << ")";
desc = s.str();
}
nvme_ex_ptr makeLibNVMeError(const std::error_code& err, int nvmeStatus,
const char* methodName)
{
// TODO: possibly remove method_name argument
(void)methodName;
if (nvmeStatus < 0)
{
auto desc = std::string("libnvme error: ") + err.message();
std::cerr << methodName << ":" << desc << '\n';
return std::make_shared<NVMeSdBusPlusError>(desc);
}
if (nvmeStatus > 0)
{
int val = static_cast<int>(nvme_status_get_value(nvmeStatus));
int ty = static_cast<int>(nvme_status_get_type(nvmeStatus));
std::string desc;
std::shared_ptr<sdbusplus::exception_t> specific;
switch (ty)
{
case NVME_STATUS_TYPE_NVME:
translateLibNVMe(val, desc, specific);
break;
case NVME_STATUS_TYPE_MI:
translateLibNVMeMI(val, desc, specific);
break;
default:
std::cerr << "Unknown libnvme error status " << nvmeStatus
<< '\n';
desc = "Unknown libnvme error";
}
std::cerr << methodName << ":" << desc << '\n';
return std::make_shared<NVMeSdBusPlusError>(desc, specific);
}
// No Error
return nullptr;
}
nvme_ex_ptr makeLibNVMeError(int nvmeErrno, int nvmeStatus,
const char* methodName)
{
auto err = std::make_error_code(static_cast<std::errc>(nvmeErrno));
return makeLibNVMeError(err, nvmeStatus, methodName);
}
nvme_ex_ptr makeLibNVMeError(std::string_view msg)
{
return std::make_shared<NVMeSdBusPlusError>(msg);
}
nvme_ex_ptr
makeLibNVMeError(std::string_view desc,
const std::shared_ptr<sdbusplus::exception_t>& specific)
{
return std::make_shared<NVMeSdBusPlusError>(desc, specific);
}
/* Throws an appropriate error type for the given status from libnvme,
* or returns normally if nvme_status == 0 */
void checkLibNVMeError(const std::error_code& err, int nvmeStatus,
const char* methodName)
{
auto e = makeLibNVMeError(err, nvmeStatus, methodName);
if (e)
{
// 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 *e;
}
}
std::ostream& operator<<(std::ostream& o, const nvme_ex_ptr& ex)
{
if (ex)
{
ex->print(o);
}
else
{
o << "(null nvme_ex_ptr)";
}
return o;
}