OpenPOWER: Add support for Hostboot dump.
Hostboot dump is a dump created during the boot failure
on OpenPOWER based systems.
The dump get extracted from Self Boot Engine(SBE) with
the openpower-dump-manager application.
That application will call into phosphor-dump-manager
to create a dump with the data collected from each
SBE in the system.
Role of phosphor-debug-collector hostboot dump manager
- Create the hostboot dump package directory
- Create hostboot dump entry
- Implement notify interface to get notified when the
collection is completed
- Once notified collect each file from the predefined
location and package it into a dump.
- Update the hostboot dump entry with details once
dump packaging is completed.
Tested:
- Create a dump entry with busctl
- create a dummy file in search path
- Call notify with busctl
Signed-off-by: Dhruvaraj Subhshchandran <dhruvaraj@in.ibm.com>
Change-Id: I2dab3b7ca976ed33184f770722cca00b1b02c075
diff --git a/dump-extensions.hpp b/dump-extensions.hpp
index ec22258..56fb740 100644
--- a/dump-extensions.hpp
+++ b/dump-extensions.hpp
@@ -1,4 +1,5 @@
#include "dump_manager.hpp"
+#include "dump_utils.hpp"
#include <memory>
#include <vector>
@@ -16,6 +17,7 @@
* @param[out] dumpMgrList - list dump manager objects.
*
*/
-void loadExtensions(sdbusplus::bus_t& bus, DumpManagerList& dumpMgrList);
+void loadExtensions(sdbusplus::bus::bus& bus, const EventPtr& event,
+ DumpManagerList& dumpMgrList);
} // namespace dump
} // namespace phosphor
diff --git a/dump-extensions/default/default.cpp b/dump-extensions/default/default.cpp
index 0de4e09..00c2bfe 100644
--- a/dump-extensions/default/default.cpp
+++ b/dump-extensions/default/default.cpp
@@ -4,7 +4,8 @@
{
namespace dump
{
-void loadExtensions(sdbusplus::bus_t&, DumpManagerList&)
+void loadExtensions(sdbusplus::bus::bus&, const phosphor::dump::EventPtr&,
+ DumpManagerList&)
{}
} // namespace dump
} // namespace phosphor
diff --git a/dump-extensions/openpower-dumps/dump-extensions.cpp b/dump-extensions/openpower-dumps/dump-extensions.cpp
index 45864ca..3b91842 100644
--- a/dump-extensions/openpower-dumps/dump-extensions.cpp
+++ b/dump-extensions/openpower-dumps/dump-extensions.cpp
@@ -4,21 +4,52 @@
#include "dump-extensions/openpower-dumps/openpower_dumps_config.h"
+#include "com/ibm/Dump/Entry/Hardware/server.hpp"
+#include "com/ibm/Dump/Entry/Hostboot/server.hpp"
+#include "com/ibm/Dump/Entry/SBE/server.hpp"
+#include "dump_manager_hostdump.hpp"
#include "dump_manager_resource.hpp"
#include "dump_manager_system.hpp"
+#include "dump_utils.hpp"
+#include "host_dump_entry.hpp"
+
+#include <fmt/core.h>
+
+#include <phosphor-logging/log.hpp>
namespace phosphor
{
namespace dump
{
-void loadExtensions(sdbusplus::bus_t& bus, DumpManagerList& dumpList)
+using namespace phosphor::logging;
+void loadExtensions(sdbusplus::bus::bus& bus,
+ const phosphor::dump::EventPtr& event,
+ DumpManagerList& dumpList)
{
-
dumpList.push_back(std::make_unique<openpower::dump::system::Manager>(
bus, SYSTEM_DUMP_OBJPATH, SYSTEM_DUMP_OBJ_ENTRY));
dumpList.push_back(std::make_unique<openpower::dump::resource::Manager>(
bus, RESOURCE_DUMP_OBJPATH, RESOURCE_DUMP_OBJ_ENTRY));
+
+ try
+ {
+ std::filesystem::create_directories(HOSTBOOT_DUMP_PATH);
+ }
+ catch (std::exception& e)
+ {
+ log<level::ERR>(
+ fmt::format("Failed to create hostboot dump directory({})",
+ HOSTBOOT_DUMP_PATH)
+ .c_str());
+ throw std::runtime_error("Failed to create hostboot dump directory");
+ }
+ dumpList.push_back(std::make_unique<openpower::dump::hostdump::Manager<
+ sdbusplus::com::ibm::Dump::Entry::server::Hostboot>>(
+ bus, event, HOSTBOOT_DUMP_OBJPATH, HOSTBOOT_DUMP_OBJ_ENTRY,
+ HOSTBOOT_DUMP_PATH, "hbdump", HOSTBOOT_DUMP_TMP_FILE_DIR,
+ HOSTBOOT_DUMP_MAX_SIZE, HOSTBOOT_DUMP_MIN_SPACE_REQD,
+ HOSTBOOT_DUMP_TOTAL_SIZE));
}
} // namespace dump
} // namespace phosphor
diff --git a/dump-extensions/openpower-dumps/dump_manager_hostdump.hpp b/dump-extensions/openpower-dumps/dump_manager_hostdump.hpp
new file mode 100644
index 0000000..95fe8ed
--- /dev/null
+++ b/dump-extensions/openpower-dumps/dump_manager_hostdump.hpp
@@ -0,0 +1,315 @@
+#pragma once
+
+#include "dump-extensions/openpower-dumps/openpower_dumps_config.h"
+
+#include "dump_manager_bmcstored.hpp"
+#include "dump_utils.hpp"
+#include "host_dump_entry.hpp"
+#include "op_dump_util.hpp"
+#include "watch.hpp"
+#include "xyz/openbmc_project/Common/error.hpp"
+#include "xyz/openbmc_project/Dump/Create/error.hpp"
+#include "xyz/openbmc_project/Dump/NewDump/server.hpp"
+
+#include <fmt/core.h>
+#include <sys/inotify.h>
+#include <unistd.h>
+
+#include <com/ibm/Dump/Create/server.hpp>
+#include <phosphor-logging/elog-errors.hpp>
+#include <phosphor-logging/elog.hpp>
+#include <sdeventplus/exception.hpp>
+#include <sdeventplus/source/base.hpp>
+#include <sdeventplus/source/child.hpp>
+#include <xyz/openbmc_project/Dump/Create/server.hpp>
+
+#include <ctime>
+#include <filesystem>
+#include <regex>
+
+namespace openpower
+{
+namespace dump
+{
+namespace hostdump
+{
+
+using namespace sdbusplus::xyz::openbmc_project::Common::Error;
+using namespace phosphor::logging;
+
+constexpr auto INVALID_DUMP_SIZE = 0;
+constexpr auto HOST_DUMP_COMMON_FILENAME_PART =
+ "_([0-9]+)_([0-9]+).([a-zA-Z0-9]+)";
+
+using CreateIface = sdbusplus::server::object::object<
+ sdbusplus::xyz::openbmc_project::Dump::server::Create,
+ sdbusplus::com::ibm::Dump::server::Create,
+ sdbusplus::xyz::openbmc_project::Dump::server::NewDump>;
+
+using UserMap = phosphor::dump::inotify::UserMap;
+
+using Watch = phosphor::dump::inotify::Watch;
+using ::sdeventplus::source::Child;
+
+using originatorTypes = sdbusplus::xyz::openbmc_project::Common::server::
+ OriginatedBy::OriginatorTypes;
+
+/** @class Manager
+ * @brief Host Dump manager implementation.
+ * @details A concrete implementation for the
+ * xyz.openbmc_project.Dump.Create
+ * com::ibm::Dump::Create and
+ * xyz::openbmc_project::Dump::NewDump D-Bus APIs
+ */
+template <typename T>
+class Manager :
+ virtual public CreateIface,
+ public phosphor::dump::bmc_stored::Manager
+{
+ public:
+ Manager() = delete;
+ Manager(const Manager&) = default;
+ Manager& operator=(const Manager&) = delete;
+ Manager(Manager&&) = delete;
+ Manager& operator=(Manager&&) = delete;
+ virtual ~Manager() = default;
+
+ /** @brief Constructor to put object onto bus at a dbus path.
+ * @param[in] bus - Bus to attach to.
+ * @param[in] event - Dump manager sd_event loop.
+ * @param[in] path - Path to attach at.
+ * @param[in] baseEntryPath - Base path for dump entry.
+ * @param[in] filePath - Path where the dumps are stored.
+ * @param[in] dumpNamePrefix - Prefix to the dump filename
+ * @param[in] dumpTempFileDir - Temporary location of dump files
+ * @param[in] maxDumpSize - Maximum allowed size of dump file
+ * @param[in] minDumpSize - Minimum size of a usable dump
+ * @param[in] allocatedSize - Total allocated space for the dump.
+ */
+ Manager(sdbusplus::bus::bus& bus, const phosphor::dump::EventPtr& event,
+ const char* path, const std::string& baseEntryPath,
+ const char* filePath, const std::string dumpNamePrefix,
+ const std::string dumpTempFileDir, const uint64_t maxDumpSize,
+ const uint64_t minDumpSize, const uint64_t allocatedSize) :
+ CreateIface(bus, path),
+ phosphor::dump::bmc_stored::Manager(
+ bus, event, path, baseEntryPath, filePath,
+ dumpNamePrefix + HOST_DUMP_COMMON_FILENAME_PART, maxDumpSize,
+ minDumpSize, allocatedSize),
+ dumpNamePrefix(dumpNamePrefix), dumpTempFileDir(dumpTempFileDir)
+ {}
+
+ /** @brief Implementation for CreateDump
+ * Method to create a host dump entry when user requests for a
+ * new host dump
+ *
+ * @return object_path - The object path of the new dump entry.
+ */
+ sdbusplus::message::object_path
+ createDump(phosphor::dump::DumpCreateParams params) override
+ {
+ using InvalidArgument =
+ sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument;
+ using Argument = xyz::openbmc_project::Common::InvalidArgument;
+ if (!params.empty())
+ {
+ log<level::ERR>(fmt::format("Dump type({}) accepts no additional "
+ "parameters, number of parameters({})",
+ dumpNamePrefix, params.size())
+ .c_str());
+ elog<InvalidArgument>(
+ Argument::ARGUMENT_NAME("NO_PARAMETERS_NEEDED"),
+ Argument::ARGUMENT_VALUE("INVALID_PARAMETERS"));
+ }
+
+ // Check dump policy
+ util::isOPDumpsEnabled();
+
+ auto size = getAllowedSize();
+
+ uint32_t id = ++lastEntryId;
+
+ // Entry Object path.
+ auto objPath =
+ std::filesystem::path(baseEntryPath) / std::to_string(id);
+
+ log<level::INFO>(fmt::format("Create dump type({}) with id({}) "
+ "available space: ({}) kilobytes",
+ dumpNamePrefix, id, size)
+ .c_str());
+
+ std::time_t timeStamp = std::time(nullptr);
+ createEntry(id, objPath, timeStamp, 0, std::string(),
+ phosphor::dump::OperationStatus::InProgress, std::string(),
+ originatorTypes::Internal);
+
+ return objPath.string();
+ }
+
+ /** @brief Notify the host dump manager about creation of a new dump.
+ * @param[in] dumpId - Id from the source of the dump.
+ * @param[in] size - Size of the dump.
+ */
+ void notify(uint32_t dumpId, uint64_t) override
+ {
+ try
+ {
+ captureDump(dumpId);
+ }
+ catch (std::exception& e)
+ {
+ log<level::ERR>(
+ fmt::format("Failed to package dump({}): id({}) errorMsg({})",
+ dumpNamePrefix, dumpId, e.what())
+ .c_str());
+ throw std::runtime_error("Failed to package dump");
+ }
+ }
+ /** @brief Create a Dump Entry Object
+ * @param[in] id - Id of the dump
+ * @param[in] objPath - Object path to attach to
+ * @param[in] ms - Dump creation timestamp since the epoch.
+ * @param[in] fileSize - Dump file size in bytes.
+ * @param[in] file - Name of dump file.
+ * @param[in] status - status of the dump.
+ * @param[in] originatorId - Id of the originator of the dump
+ * @param[in] originatorType - Originator type
+ */
+
+ virtual void createEntry(const uint32_t id, const std::string objPath,
+ const uint64_t ms, uint64_t fileSize,
+ const std::filesystem::path& file,
+ phosphor::dump::OperationStatus status,
+ std::string originatorId,
+ originatorTypes originatorType) override
+ {
+ try
+ {
+ entries.insert(std::make_pair(
+ id, std::make_unique<openpower::dump::hostdump::Entry<T>>(
+ bus, objPath.c_str(), id, ms, fileSize, file, status,
+ originatorId, originatorType, *this)));
+ }
+ catch (const std::invalid_argument& e)
+ {
+ log<level::ERR>(fmt::format("Error in creating host dump entry, "
+ "errormsg({}), OBJECTPATH({}), ID({})",
+ e.what(), objPath.c_str(), id)
+ .c_str());
+ throw std::runtime_error("Error in creating host dump entry");
+ }
+ }
+
+ private:
+ std::string dumpNamePrefix;
+ std::string dumpTempFileDir;
+
+ void captureDump(uint32_t dumpId)
+ {
+ std::string idStr;
+ try
+ {
+ idStr = std::to_string(dumpId);
+ }
+ catch (std::exception& e)
+ {
+ log<level::ERR>("Dump capture: Error converting idto string");
+ throw std::runtime_error(
+ "Dump capture: Error converting dump id to string");
+ }
+
+ // Get Dump size.
+ // TODO #ibm-openbmc/issues/3061
+ // Dump request will be rejected if there is not enough space for
+ // one complete dump, change this behavior to crate a partial dump
+ // with available space.
+ auto size = getAllowedSize();
+
+ auto dumpTempPath = std::filesystem::path(dumpTempFileDir) / idStr;
+
+ pid_t pid = fork();
+ if (pid == 0)
+ {
+ std::filesystem::path dumpPath(dumpDir);
+ dumpPath /= idStr;
+ execl("/usr/bin/opdreport", "opdreport", "-d", dumpPath.c_str(),
+ "-i", idStr.c_str(), "-s", std::to_string(size).c_str(), "-q",
+ "-v", "-p", dumpTempPath.c_str(), "-n",
+ dumpNamePrefix.c_str(), nullptr);
+
+ // opdreport script execution is failed.
+ auto error = errno;
+ log<level::ERR>(
+ fmt::format(
+ "Dump capture: Error occurred during "
+ "opdreport function execution, errno({}), dumpPrefix({}), "
+ "dumpPath({}), dumpSourcePath({}), allowedSize({})",
+ error, dumpNamePrefix.c_str(), dumpPath.c_str(),
+ dumpTempPath.c_str(), size)
+ .c_str());
+ throw std::runtime_error("Dump capture: Error occured during "
+ "opdreport script execution");
+ }
+ else if (pid > 0)
+ {
+ phosphor::dump::Entry* dumpEntry = NULL;
+ auto dumpIt = entries.find(dumpId);
+ if (dumpIt != entries.end())
+ {
+ dumpEntry = dumpIt->second.get();
+ }
+ Child::Callback callback = [this, dumpEntry,
+ pid](Child&, const siginfo_t* si) {
+ // Set progress as failed if packaging return error
+ if (si->si_status != 0)
+ {
+ log<level::ERR>("Dump packaging failed");
+ if (dumpEntry != nullptr)
+ {
+ reinterpret_cast<phosphor::dump::Entry*>(dumpEntry)
+ ->status(phosphor::dump::OperationStatus::Failed);
+ }
+ }
+ else
+ {
+ log<level::INFO>("Dump packaging completed");
+ }
+ this->childPtrMap.erase(pid);
+ };
+ try
+ {
+ childPtrMap.emplace(
+ pid, std::make_unique<Child>(eventLoop.get(), pid,
+ WEXITED | WSTOPPED,
+ std::move(callback)));
+ }
+ catch (const sdeventplus::SdEventError& ex)
+ {
+ // Failed to add to event loop
+ log<level::ERR>(
+ fmt::format("Dump capture: Error occurred during "
+ "the sdeventplus::source::Child ex({})",
+ ex.what())
+ .c_str());
+ throw std::runtime_error(
+ "Dump capture: Error occurred during the "
+ "sdeventplus::source::Child creation");
+ }
+ }
+ else
+ {
+ auto error = errno;
+ log<level::ERR>(
+ fmt::format(
+ "Dump capture: Error occurred during fork, errno({})",
+ error)
+ .c_str());
+ throw std::runtime_error(
+ "Dump capture: Error occurred during fork");
+ }
+ }
+};
+
+} // namespace hostdump
+} // namespace dump
+} // namespace openpower
diff --git a/dump-extensions/openpower-dumps/host_dump_entry.hpp b/dump-extensions/openpower-dumps/host_dump_entry.hpp
new file mode 100644
index 0000000..fe2b839
--- /dev/null
+++ b/dump-extensions/openpower-dumps/host_dump_entry.hpp
@@ -0,0 +1,75 @@
+#pragma once
+
+#include "bmcstored_dump_entry.hpp"
+
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/server/object.hpp>
+
+#include <filesystem>
+
+namespace openpower
+{
+namespace dump
+{
+namespace hostdump
+{
+
+using originatorTypes = sdbusplus::xyz::openbmc_project::Common::server::
+ OriginatedBy::OriginatorTypes;
+
+template <typename T>
+using ServerObject = typename sdbusplus::server::object_t<T>;
+
+template <typename T>
+using EntryIfaces = sdbusplus::server::object_t<T>;
+
+/** @class Entry
+ * @brief Host Dump Entry implementation.
+ * @details A concrete implementation for the
+ * host dump type DBus API
+ */
+template <typename T>
+class Entry :
+ virtual public EntryIfaces<T>,
+ virtual public phosphor::dump::bmc_stored::Entry
+{
+ public:
+ Entry() = delete;
+ Entry(const Entry&) = delete;
+ Entry& operator=(const Entry&) = delete;
+ Entry(Entry&&) = delete;
+ Entry& operator=(Entry&&) = delete;
+ ~Entry() = default;
+
+ /** @brief Constructor for the Dump Entry Object
+ * @param[in] bus - Bus to attach to.
+ * @param[in] objPath - Object path to attach to
+ * @param[in] dumpId - Dump id.
+ * @param[in] timeStamp - Dump creation timestamp
+ * since the epoch.
+ * @param[in] fileSize - Dump file size in bytes.
+ * @param[in] file - Name of dump file.
+ * @param[in] status - status of the dump.
+ * @param[in] originatorId - Id of the originator of the dump
+ * @param[in] originatorType - Originator type
+ * @param[in] parent - The dump entry's parent.
+ */
+ Entry(sdbusplus::bus_t& bus, const std::string& objPath, uint32_t dumpId,
+ uint64_t timeStamp, uint64_t fileSize,
+ const std::filesystem::path& file,
+ phosphor::dump::OperationStatus status, std::string originatorId,
+ originatorTypes originatorType, phosphor::dump::Manager& parent) :
+ EntryIfaces<T>(bus, objPath.c_str(),
+ EntryIfaces<T>::action::emit_object_added),
+ phosphor::dump::bmc_stored::Entry(bus, objPath.c_str(), dumpId,
+ timeStamp, fileSize, file, status,
+ originatorId, originatorType, parent)
+ {
+ // Emit deferred signal.
+ this->openpower::dump::hostdump::EntryIfaces<T>::emit_object_added();
+ }
+};
+
+} // namespace hostdump
+} // namespace dump
+} // namespace openpower
diff --git a/dump-extensions/openpower-dumps/meson.build b/dump-extensions/openpower-dumps/meson.build
index c367421..ac971a4 100644
--- a/dump-extensions/openpower-dumps/meson.build
+++ b/dump-extensions/openpower-dumps/meson.build
@@ -19,6 +19,30 @@
opconf_data.set_quoted('RESOURCE_DUMP_OBJ_ENTRY', get_option('RESOURCE_DUMP_OBJ_ENTRY'),
description : 'The resource dump entry D-Bus object path'
)
+
+
+opconf_data.set_quoted('HOSTBOOT_DUMP_OBJPATH', get_option('HOSTBOOT_DUMP_OBJPATH'),
+ description : 'The Hostboot dump manager D-Bus path'
+ )
+opconf_data.set_quoted('HOSTBOOT_DUMP_OBJ_ENTRY', get_option('HOSTBOOT_DUMP_OBJ_ENTRY'),
+ description : 'The hostboot dump entry D-Bus object path'
+ )
+opconf_data.set_quoted('HOSTBOOT_DUMP_TMP_FILE_DIR', get_option('HOSTBOOT_DUMP_TMP_FILE_DIR'),
+ description : 'Directory where hostboot dump pieces are stored for packaging'
+ )
+opconf_data.set_quoted('HOSTBOOT_DUMP_PATH', get_option('HOSTBOOT_DUMP_PATH'),
+ description : 'Directory where Hostboot dumps are placed'
+ )
+opconf_data.set('HOSTBOOT_DUMP_MAX_SIZE', get_option('HOSTBOOT_DUMP_MAX_SIZE'),
+ description : 'Maximum size of one Hostboot dump in kilo bytes'
+ )
+opconf_data.set('HOSTBOOT_DUMP_MIN_SPACE_REQD', get_option('HOSTBOOT_DUMP_MIN_SPACE_REQD'),
+ description : 'Minimum space required for one Hostboot dump in kilo bytes'
+ )
+opconf_data.set('HOSTBOOT_DUMP_TOTAL_SIZE', get_option('HOSTBOOT_DUMP_TOTAL_SIZE'),
+ description : 'Total size of the dump in kilo bytes'
+ )
+
configure_file(configuration : opconf_data,
output : 'openpower_dumps_config.h'
)
diff --git a/dump_manager_bmcstored.hpp b/dump_manager_bmcstored.hpp
index 1ea17d4..9c3cf28 100644
--- a/dump_manager_bmcstored.hpp
+++ b/dump_manager_bmcstored.hpp
@@ -8,13 +8,13 @@
#include <filesystem>
#include <map>
-
namespace phosphor
{
namespace dump
{
namespace bmc_stored
{
+using namespace phosphor::logging;
using UserMap = phosphor::dump::inotify::UserMap;
using Watch = phosphor::dump::inotify::Watch;
diff --git a/dump_manager_main.cpp b/dump_manager_main.cpp
index 402cea3..380134d 100644
--- a/dump_manager_main.cpp
+++ b/dump_manager_main.cpp
@@ -85,7 +85,7 @@
FAULTLOG_DUMP_PATH);
dumpMgrList.push_back(std::move(faultLogMgr));
- phosphor::dump::loadExtensions(bus, dumpMgrList);
+ phosphor::dump::loadExtensions(bus, eventP, dumpMgrList);
// Restore dbus objects of all dumps
for (auto& dmpMgr : dumpMgrList)
diff --git a/meson_options.txt b/meson_options.txt
index 0fe8e95..96eb650 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -132,3 +132,37 @@
value : '/xyz/openbmc_project/dump/resource/entry',
description : 'The resource dump entry D-Bus object path'
)
+
+# Hostboot dump options
+
+option('HOSTBOOT_DUMP_OBJPATH', type : 'string',
+ value : '/xyz/openbmc_project/dump/hostboot',
+ description : 'The hostboot dump manager D-Bus object path'
+ )
+option('HOSTBOOT_DUMP_OBJ_ENTRY', type : 'string',
+ value : '/xyz/openbmc_project/dump/hostboot/entry',
+ description : 'The Hostboot dump entry D-Bus object path'
+ )
+# This value needs to be set from recipe as a common value with
+# packaging application.
+option('HOSTBOOT_DUMP_TMP_FILE_DIR', type : 'string',
+ value : '/tmp/openpower-dumps/hostboot',
+ description : 'Directory where hostboot dump pieces are stored for packaging'
+ )
+option('HOSTBOOT_DUMP_PATH', type : 'string',
+ value : '/var/lib/phosphor-debug-collector/hostbootdump/',
+ description : 'Directory where Hostboot dumps are placed'
+ )
+option('HOSTBOOT_DUMP_MAX_SIZE', type : 'integer',
+ value : 102400,
+ description : 'Maximum size of one Hostboot dump in kilo bytes'
+ )
+option('HOSTBOOT_DUMP_MIN_SPACE_REQD', type : 'integer',
+ value : 81920,
+ description : 'Minimum space required for one Hostboot dump in kilo bytes'
+ )
+option('HOSTBOOT_DUMP_TOTAL_SIZE', type : 'integer',
+ value : 409600,
+ description : 'Total size of the dump in kilo bytes'
+ )
+