NVMed: Enable primary controller when SC is empty

Tested: Manually tested

Patch Tracking Bug: b/284486810

Change-Id: Iae93dfd392f29c0cb4da719dff38480231fbc05e
Signed-off-by: Muhammad Usama <muhammadusama@google.com>
diff --git a/recipes-phosphor/sensors/dbus-sensors/0047-NVMed-Enable-Primary-controller-when-SC-is-empty.patch b/recipes-phosphor/sensors/dbus-sensors/0047-NVMed-Enable-Primary-controller-when-SC-is-empty.patch
new file mode 100644
index 0000000..19c0eb7
--- /dev/null
+++ b/recipes-phosphor/sensors/dbus-sensors/0047-NVMed-Enable-Primary-controller-when-SC-is-empty.patch
@@ -0,0 +1,221 @@
+From dd139577f132ddbac93d6a4ab6883d390583d3d3 Mon Sep 17 00:00:00 2001
+From: Muhammad Usama <muhammadusama@google.com>
+Date: Thu, 20 Jul 2023 05:23:05 +0000
+Subject: [PATCH] NVMed: Enable Primary controller when SC is empty
+
+NVMe device can have no secondary controller. Currently, if
+the secondary list is empty. The primary controller is not
+enable.
+
+Patch Tracking Bug: b/284486810
+Upstream info / review: https://gerrit.openbmc.org/c/openbmc/dbus-sensors/+/63777
+Upstream-Status: Submitted
+Justification: Depends on other patches
+
+Signed-off-by: Muhammad Usama <muhammadusama@google.com>
+
+%% original patch: 0047-NVMed-Enable-Primary-controller-when-SC-is-empty.patch
+---
+ src/NVMeSubsys.cpp | 144 +++++++++++++++++++++++++--------------------
+ src/NVMeSubsys.hpp |   3 +
+ 2 files changed, 84 insertions(+), 63 deletions(-)
+
+diff --git a/src/NVMeSubsys.cpp b/src/NVMeSubsys.cpp
+index 07370e0..00142f9 100644
+--- a/src/NVMeSubsys.cpp
++++ b/src/NVMeSubsys.cpp
+@@ -99,6 +99,69 @@ NVMeSubsystem::~NVMeSubsystem()
+     objServer.remove_interface(assocIntf);
+ }
+ 
++void NVMeSubsystem::processSecondaryControllerList(nvme_secondary_ctrl_list* secCntlrList)
++{
++    auto findPrimary = controllers.begin();
++    int secCntlrCount = 0;
++    if (secCntlrList != nullptr)
++    {
++        // all sc_entry pointing to a single pcid, so we only check
++        // the first entry.
++        findPrimary = controllers.find(secCntlrList->sc_entry[0].pcid);
++        if (findPrimary == controllers.end())
++        {
++            std::cerr << "fail to match primary controller from "
++                         "identify sencondary cntrl list" << std::endl;
++            status = Status::Stop;
++            markFunctional(false);
++            markAvailable(false);
++            return;
++        }
++        secCntlrCount = secCntlrList->num;
++    }
++
++    // Enable primary controller since they are required to work
++    auto& primaryController = findPrimary->second.first;
++    primaryController =
++        NVMeControllerEnabled::create(std::move(*primaryController.get()));
++
++    std::vector<std::shared_ptr<NVMeController>> secCntrls;
++    for (int i = 0; i < secCntlrCount; i++)
++    {
++        auto findSecondary = controllers.find(secCntlrList->sc_entry[i].scid);
++        if (findSecondary == controllers.end())
++        {
++            std::cerr << "fail to match secondary controller from "
++                         "identify sencondary cntrl list"
++                      << std::endl;
++            break;
++        }
++
++        auto& secondaryController = findSecondary->second.first;
++
++        // Check Secondary Controller State
++        if (secCntlrList->sc_entry[i].scs != 0)
++        {
++            secondaryController = NVMeControllerEnabled::create(
++                std::move(*secondaryController.get()));
++        }
++        secCntrls.push_back(secondaryController);
++    }
++    primaryController->setSecAssoc(secCntrls);
++
++    // start controller
++    for (auto& [_, pair] : controllers)
++    {
++        pair.first->start(pair.second);
++    }
++    // start plugin
++    if (plugin)
++    {
++        plugin->start();
++    }
++    status = Status::Start;
++}
++
+ void NVMeSubsystem::markFunctional(bool toggle)
+ {
+     if (ctemp)
+@@ -214,7 +277,20 @@ void NVMeSubsystem::markFunctional(bool toggle)
+             The controller is SR-IOV, meaning all controllers (within a
+             subsystem) are pointing to a single primary controller. So we
+             only need to do identify on an arbatary controller.
++            If the controller list contains a single controller. Skip
++            identifying the secondary controller list. It will be the primary
++            controller.
+             */
++            if (ctrlList.size() == 1)
++            {
++                // Remove all associations
++                for (const auto& [_, pair] : self->controllers)
++                {
++                    pair.first->setSecAssoc();
++                }
++                self->processSecondaryControllerList(nullptr);
++                return;
++            }
+             auto ctrl = ctrlList.back();
+             nvme->adminIdentify(
+                 ctrl, nvme_identify_cns::NVME_IDENTIFY_CNS_SECONDARY_CTRL_LIST,
+@@ -230,8 +306,8 @@ void NVMeSubsystem::markFunctional(bool toggle)
+                     self->markAvailable(false);
+                     return;
+                 }
+-                nvme_secondary_ctrl_list& listHdr =
+-                    *reinterpret_cast<nvme_secondary_ctrl_list*>(data.data());
++                nvme_secondary_ctrl_list* listHdr =
++                    reinterpret_cast<nvme_secondary_ctrl_list*>(data.data());
+ 
+                 // Remove all associations
+                 for (const auto& [_, pair] : self->controllers)
+@@ -239,7 +315,7 @@ void NVMeSubsystem::markFunctional(bool toggle)
+                     pair.first->setSecAssoc();
+                 }
+ 
+-                if (listHdr.num == 0)
++                if (listHdr->num == 0)
+                 {
+                     std::cerr << "empty identify secondary controller list"
+                               << std::endl;
+@@ -248,66 +324,8 @@ void NVMeSubsystem::markFunctional(bool toggle)
+                     self->markAvailable(false);
+                     return;
+                 }
+-
+-                // all sc_entry pointing to a single pcid, so we only check
+-                // the first entry.
+-                auto findPrimary =
+-                    self->controllers.find(listHdr.sc_entry[0].pcid);
+-                if (findPrimary == self->controllers.end())
+-                {
+-                    std::cerr << "fail to match primary controller from "
+-                                 "identify sencondary cntrl list"
+-                              << std::endl;
+-                    self->status = Status::Stop;
+-                    self->markFunctional(false);
+-                    self->markAvailable(false);
+-                    return;
+-                }
+-
+-                // Enable primary controller since they are required to work
+-                auto& primaryController = findPrimary->second.first;
+-                primaryController = NVMeControllerEnabled::create(
+-                    std::move(*primaryController.get()));
+-
+-                std::vector<std::shared_ptr<NVMeController>> secCntrls;
+-                for (int i = 0; i < listHdr.num; i++)
+-                {
+-                    auto findSecondary =
+-                        self->controllers.find(listHdr.sc_entry[i].scid);
+-                    if (findSecondary == self->controllers.end())
+-                    {
+-                        std::cerr << "fail to match secondary controller from "
+-                                     "identify sencondary cntrl list"
+-                                  << std::endl;
+-                        break;
+-                    }
+-
+-                    auto& secondaryController = findSecondary->second.first;
+-
+-                    // Check Secondary Controller State
+-                    if (listHdr.sc_entry[i].scs != 0)
+-                    {
+-                        secondaryController = NVMeControllerEnabled::create(
+-                            std::move(*secondaryController.get()));
+-                    }
+-                    secCntrls.push_back(secondaryController);
+-                }
+-                primaryController->setSecAssoc(secCntrls);
+-
+-                // start controller
+-                for (auto& [_, pair] : self->controllers)
+-                {
+-                    pair.first->start(pair.second);
+-                }
+-
+-                // start plugin
+-                if (self->plugin)
+-                {
+-                    self->plugin->start();
+-                }
+-
+-                self->status = Status::Start;
+-                });
++                self->processSecondaryControllerList(listHdr);
++            });
+         });
+     }
+ }
+diff --git a/src/NVMeSubsys.hpp b/src/NVMeSubsys.hpp
+index dd9dbfa..a86710f 100644
+--- a/src/NVMeSubsys.hpp
++++ b/src/NVMeSubsys.hpp
+@@ -81,4 +81,7 @@ class NVMeSubsystem : public std::enable_shared_from_this<NVMeSubsystem>
+     // a counter to skip health poll when NVMe subsystem becomes Unavailable
+     unsigned UnavailableCount = 0;
+     static constexpr unsigned UnavailableMaxCount = 60;
++
++    // process Secondary controller and start controllers and the associated Plugin
++    void processSecondaryControllerList(nvme_secondary_ctrl_list* secCntlrList);
+ };
+-- 
+2.41.0.487.g6d72f3e995-goog
+
diff --git a/recipes-phosphor/sensors/dbus-sensors_%.bbappend b/recipes-phosphor/sensors/dbus-sensors_%.bbappend
index 8bed015..7da9773 100644
--- a/recipes-phosphor/sensors/dbus-sensors_%.bbappend
+++ b/recipes-phosphor/sensors/dbus-sensors_%.bbappend
@@ -60,6 +60,7 @@
   file://0044-nvmesensor-add-markAvailable-with-adaptive-timer.patch \
   file://0045-nvmesensor-improve-handling-of-config-change.patch \
   file://0046-nvmesensor-release-resources-when-exit-poll.patch \
+  file://0047-NVMed-Enable-Primary-controller-when-SC-is-empty.patch \
   file://0048-NVMe-Add-the-NVMe-Admin-interface-within-the-repo.patch \
 "
 PACKAGECONFIG[nvmesensor] = "-Dnvme=enabled, -Dnvme=disabled, libnvme"