From 29c10072c157687c8bce0e9bf63cad87310e71fe Mon Sep 17 00:00:00 2001 From: James Feist Date: Wed, 16 Oct 2019 14:36:58 -0700 Subject: Make hsbp-manager expose all redfish properties Hsbp manager needs to query entity-manager for nvme fields to populate all driver interfaces. This is needed so indexing works correctly. This also exposes all M.2 drive interfaces at the end of the hsbp range. This allows correct numbering. When we find a drive in entity-manager, we backtrack from the virtual bus on the mux, to find out what bus the mux is on, this tells us the hsbp. We then use the virtual bus to identify the channel, this tells us the drive index. Tested: Saw 9 drives with a 1 hsbp and 1 M.2 configuration Change-Id: I8e730a6a4229b3816991c629439ed4639710d114 Signed-off-by: James Feist --- hsbp-manager/src/hsbp_manager.cpp | 212 ++++++++++++++++++++++++-------------- 1 file changed, 133 insertions(+), 79 deletions(-) diff --git a/hsbp-manager/src/hsbp_manager.cpp b/hsbp-manager/src/hsbp_manager.cpp index 83ebdf3..f25f266 100644 --- a/hsbp-manager/src/hsbp_manager.cpp +++ b/hsbp-manager/src/hsbp_manager.cpp @@ -18,7 +18,9 @@ #include #include +#include #include +#include #include #include #include @@ -51,11 +53,20 @@ static std::string zeroPad(const uint8_t val) struct Mux { - Mux(size_t busIn, size_t addressIn) : bus(busIn), address(addressIn) + Mux(size_t busIn, size_t addressIn, size_t channelsIn, size_t indexIn) : + bus(busIn), address(addressIn), channels(channelsIn), index(indexIn) { } size_t bus; size_t address; + size_t channels; + size_t index; + + // to sort in the flat set + bool operator<(const Mux& rhs) const + { + return index < rhs.index; + } }; enum class BlinkPattern : uint8_t @@ -155,58 +166,41 @@ struct Drive itemIface->get_object_path(), "xyz.openbmc_project.State.Drive"); rebuildingIface->register_property("Rebuilding", rebuilding); rebuildingIface->initialize(); + driveIface = + objServer.add_interface(itemIface->get_object_path(), + "xyz.openbmc_project.Inventory.Item.Drive"); + driveIface->initialize(); } virtual ~Drive() { objServer.remove_interface(itemIface); objServer.remove_interface(operationalIface); objServer.remove_interface(rebuildingIface); - objServer.remove_interface(associationIface); + objServer.remove_interface(assetIface); objServer.remove_interface(driveIface); } - void createAssociation(const std::string& path) + void createAsset( + const boost::container::flat_map& data) { - if (associationIface != nullptr) + if (assetIface != nullptr) { return; } - associationIface = objServer.add_interface( + assetIface = objServer.add_interface( itemIface->get_object_path(), - "xyz.openbmc_project.Association.Definitions"); - std::vector associations; - associations.emplace_back("inventory", "drive", path); - associationIface->register_property("Associations", associations); - associationIface->initialize(); - } - - void removeDriveIface() - { - objServer.remove_interface(driveIface); - } - - void createDriveIface() - { - if (associationIface != nullptr || driveIface != nullptr) + "xyz.openbmc_project.Inventory.Decorator.Asset"); + for (const auto& [key, value] : data) { - // this shouldn't be used if we found another provider of the drive - // interface, or if we already created one - return; + assetIface->register_property(key, value); } - driveIface = - objServer.add_interface(itemIface->get_object_path(), - "xyz.openbmc_project.Inventory.Item.Drive"); - driveIface->initialize(); - createAssociation(itemIface->get_object_path()); + assetIface->initialize(); } std::shared_ptr itemIface; std::shared_ptr operationalIface; std::shared_ptr rebuildingIface; - std::shared_ptr associationIface; - - // if something else doesn't expose a driveInterface for this, we need to - // export it ourselves + std::shared_ptr assetIface; std::shared_ptr driveIface; bool isNvme; @@ -220,7 +214,7 @@ struct Backplane bus(busIn), address(addressIn), backplaneIndex(backplaneIndexIn - 1), name(nameIn), timer(std::make_shared(io)), - muxes(std::make_shared>()) + muxes(std::make_shared>()) { } void run() @@ -516,30 +510,28 @@ struct Backplane std::vector drives; std::vector> leds; - std::shared_ptr> muxes; + std::shared_ptr> muxes; }; std::unordered_map backplanes; +std::vector ownerlessDrives; // drives without a backplane -void createDriveInterfaces(size_t referenceCount) +static size_t getDriveCount() { - if (referenceCount != 1) - { - return; - } - for (auto& [name, backplane] : backplanes) + size_t count = 0; + for (const auto& [key, backplane] : backplanes) { - for (Drive& drive : backplane.drives) - { - drive.createDriveIface(); - } + count += backplane.drives.size(); } + return count + ownerlessDrives.size(); } -void updateAssociations() +void updateAssets() { - constexpr const char* driveType = - "xyz.openbmc_project.Inventory.Item.Drive"; + static constexpr const char* nvmeType = + "xyz.openbmc_project.Inventory.Item.NVMe"; + static const std::string assetTag = + "xyz.openbmc_project.Inventory.Decorator.Asset"; conn->async_method_call( [](const boost::system::error_code ec, const GetSubTreeType& subtree) { @@ -548,6 +540,10 @@ void updateAssociations() std::cerr << "Error contacting mapper " << ec.message() << "\n"; return; } + + // drives may get an owner during this, or we might disover more + // drives + ownerlessDrives.clear(); for (const auto& [path, objDict] : subtree) { if (objDict.empty()) @@ -561,48 +557,49 @@ void updateAssociations() { continue; } - std::shared_ptr referenceCount = std::make_shared(); + if (std::find(objDict.begin()->second.begin(), + objDict.begin()->second.end(), + assetTag) == objDict.begin()->second.end()) + { + // no asset tag to associate to + continue; + } + conn->async_method_call( - [path, referenceCount]( - const boost::system::error_code ec2, - const boost::container::flat_map< - std::string, std::variant>& values) { + [path](const boost::system::error_code ec2, + const boost::container::flat_map< + std::string, + std::variant>& values) { if (ec2) { std::cerr << "Error Getting Config " << ec2.message() << " " << __FUNCTION__ << "\n"; - createDriveInterfaces(referenceCount.use_count()); return; } auto findBus = values.find("Bus"); - auto findIndex = values.find("Index"); - if (findBus == values.end() || - findIndex == values.end()) + if (findBus == values.end()) { std::cerr << "Illegal interface at " << path << "\n"; - createDriveInterfaces(referenceCount.use_count()); return; } + // find the mux bus and addr size_t muxBus = static_cast( std::get(findBus->second)); - size_t driveIndex = static_cast( - std::get(findIndex->second)); std::filesystem::path muxPath = "/sys/bus/i2c/devices/i2c-" + std::to_string(muxBus) + "/mux_device"; if (!std::filesystem::is_symlink(muxPath)) { std::cerr << path << " mux does not exist\n"; - createDriveInterfaces(referenceCount.use_count()); return; } - // we should be getting something of the form 7-0052 for - // bus 7 addr 52 + // we should be getting something of the form 7-0052 + // for bus 7 addr 52 std::string fname = std::filesystem::read_symlink(muxPath).filename(); auto findDash = fname.find('-'); @@ -611,7 +608,6 @@ void updateAssociations() findDash + 1 >= fname.size()) { std::cerr << path << " mux path invalid\n"; - createDriveInterfaces(referenceCount.use_count()); return; } @@ -621,9 +617,42 @@ void updateAssociations() size_t bus = static_cast(std::stoi(busStr)); size_t addr = static_cast(std::stoi(muxStr, nullptr, 16)); + size_t muxIndex = 0; + + // find the channel of the mux the drive is on + std::ifstream nameFile("/sys/bus/i2c/devices/i2c-" + + std::to_string(muxBus) + + "/name"); + if (!nameFile) + { + std::cerr << "Unable to open name file of bus " + << muxBus << "\n"; + return; + } + + std::string nameStr; + std::getline(nameFile, nameStr); + + // file is of the form "i2c-4-mux (chan_id 1)", get chan + // assume single digit chan + const std::string prefix = "chan_id "; + size_t findId = nameStr.find(prefix); + if (findId == std::string::npos || + findId + 1 >= nameStr.size()) + { + std::cerr << "Illegal name file on bus " << muxBus + << "\n"; + } + + std::string indexStr = + nameStr.substr(findId + prefix.size(), 1); + + size_t driveIndex = std::stoi(indexStr); + Backplane* parent = nullptr; for (auto& [name, backplane] : backplanes) { + muxIndex = 0; for (const Mux& mux : *(backplane.muxes)) { if (bus == mux.bus && addr == mux.address) @@ -631,37 +660,54 @@ void updateAssociations() parent = &backplane; break; } + muxIndex += mux.channels; } } + boost::container::flat_map + assetInventory; + const std::array assetKeys = { + "PartNumber", "SerialNumber", "Manufacturer", + "Model"}; + for (const auto& [key, value] : values) + { + if (std::find(assetKeys.begin(), assetKeys.end(), + key) == assetKeys.end()) + { + continue; + } + assetInventory[key] = std::get(value); + } + + // assume its a M.2 or something without a hsbp if (parent == nullptr) { - std::cerr << "Failed to find mux at bus " << bus - << ", addr " << addr << "\n"; - createDriveInterfaces(referenceCount.use_count()); + auto& drive = ownerlessDrives.emplace_back( + getDriveCount() + 1, true, true, true, false); + drive.createAsset(assetInventory); return; } + + driveIndex += muxIndex; + if (parent->drives.size() <= driveIndex) { - std::cerr << "Illegal drive index at " << path << " " << driveIndex << "\n"; - createDriveInterfaces(referenceCount.use_count()); return; } + Drive& drive = parent->drives[driveIndex]; - drive.removeDriveIface(); - drive.createAssociation(path); - createDriveInterfaces(referenceCount.use_count()); + drive.createAsset(assetInventory); }, owner, path, "org.freedesktop.DBus.Properties", "GetAll", - "xyz.openbmc_project.Inventory.Item.Drive"); + "" /*all interface items*/); } }, mapper::busName, mapper::path, mapper::interface, mapper::subtree, "/", - 0, std::array{driveType}); + 0, std::array{nvmeType}); } -void populateMuxes(std::shared_ptr> muxes, +void populateMuxes(std::shared_ptr> muxes, std::string& rootPath) { const static std::array muxTypes = { @@ -679,7 +725,8 @@ void populateMuxes(std::shared_ptr> muxes, } std::shared_ptr> callback = std::make_shared>( - []() { updateAssociations(); }); + []() { updateAssets(); }); + size_t index = 0; // as we use a flat map, these are sorted for (const auto& [path, objDict] : subtree) { if (objDict.empty() || objDict.begin()->second.empty()) @@ -708,10 +755,12 @@ void populateMuxes(std::shared_ptr> muxes, } conn->async_method_call( - [path, muxes, callback]( + [path, muxes, callback, index]( const boost::system::error_code ec2, const boost::container::flat_map< - std::string, std::variant>& values) { + std::string, + std::variant>>& + values) { if (ec2) { std::cerr << "Error Getting Config " @@ -721,6 +770,7 @@ void populateMuxes(std::shared_ptr> muxes, } auto findBus = values.find("Bus"); auto findAddress = values.find("Address"); + auto findChannelNames = values.find("ChannelNames"); if (findBus == values.end() || findAddress == values.end()) { @@ -732,7 +782,10 @@ void populateMuxes(std::shared_ptr> muxes, std::get(findBus->second)); size_t address = static_cast( std::get(findAddress->second)); - muxes->emplace_back(bus, address); + std::vector channels = + std::get>( + findChannelNames->second); + muxes->emplace(bus, address, channels.size(), index); if (callback.use_count() == 1) { (*callback)(); @@ -740,6 +793,7 @@ void populateMuxes(std::shared_ptr> muxes, }, owner, path, "org.freedesktop.DBus.Properties", "GetAll", *interface); + index++; } }, mapper::busName, mapper::path, mapper::interface, mapper::subtree, @@ -849,7 +903,7 @@ int main() sdbusplus::bus::match::match drive( *conn, "type='signal',member='PropertiesChanged',arg0='xyz.openbmc_project." - "Inventory.Item.Drive'", + "Inventory.Item.NVMe'", [&callbackTimer](sdbusplus::message::message& message) { callbackTimer.expires_after(std::chrono::seconds(2)); if (message.get_sender() == conn->get_unique_name()) -- cgit v1.2.3