dbus-sensors: Refresh the nvmesensor patches

Adding new features for nvmesensor:
* warm reboot
** health poll based enumeration
** host power signal handling
* nvme telemetry log chuck fetch

Tested:
* Warm reboot: b/267654194#comment20 ~ comment#22
* telemetry log: as part of nvme CBTRM: b/267654194#comment23

Google-Bug-Id: 267654194
Google-Bug-Id: 289134789
Change-Id: I984347cbfe2907565d7d2da41ef2766020d13255
Signed-off-by: Hao Jiang <jianghao@google.com>
diff --git a/recipes-phosphor/sensors/dbus-sensors/0001-nvmesensor-rename-the-files.patch b/recipes-phosphor/sensors/dbus-sensors/0001-nvmesensor-rename-the-files.patch
new file mode 100644
index 0000000..9dbde16
--- /dev/null
+++ b/recipes-phosphor/sensors/dbus-sensors/0001-nvmesensor-rename-the-files.patch
@@ -0,0 +1,99 @@
+From abe940aab4c575f542facb0b165db3387f334d14 Mon Sep 17 00:00:00 2001
+From: Hao Jiang <jianghao@google.com>
+Date: Tue, 9 May 2023 01:25:12 +0000
+Subject: [PATCH 01/44] nvmesensor: rename the files
+
+Get prepared for the code refactor of supporting both NVMe Basic and
+NVMe-Mi protocol.
+
+The NVMeContext is renamed to NVMeIntf which will be the common
+interface for both protocols.
+
+Context is removed from the naming and its functinality will be moved
+into NVMe subsystem, which provide the full OOB management beyond the
+thermal reading. The naming will be more consitant with the nvme spec.
+
+NVMeBasicContext is also renamed to NVMeBasic which is the
+implementation of NVMeIntf for NVMe Basic protocol.
+
+Signed-off-by: Hao Jiang <jianghao@google.com>
+Change-Id: I76a8544297e87a4e8f2e4483bd09dd9e3889d4c5
+---
+ src/{NVMeBasicContext.cpp => NVMeBasic.cpp} | 2 +-
+ src/{NVMeBasicContext.hpp => NVMeBasic.hpp} | 2 +-
+ src/{NVMeContext.hpp => NVMeIntf.hpp}       | 0
+ src/NVMeSensorMain.cpp                      | 4 ++--
+ src/meson.build                             | 7 +++++--
+ 5 files changed, 9 insertions(+), 6 deletions(-)
+ rename src/{NVMeBasicContext.cpp => NVMeBasic.cpp} (99%)
+ rename src/{NVMeBasicContext.hpp => NVMeBasic.hpp} (98%)
+ rename src/{NVMeContext.hpp => NVMeIntf.hpp} (100%)
+
+diff --git a/src/NVMeBasicContext.cpp b/src/NVMeBasic.cpp
+similarity index 99%
+rename from src/NVMeBasicContext.cpp
+rename to src/NVMeBasic.cpp
+index 0bc2252..29407e8 100644
+--- a/src/NVMeBasicContext.cpp
++++ b/src/NVMeBasic.cpp
+@@ -1,4 +1,4 @@
+-#include "NVMeBasicContext.hpp"
++#include "NVMeBasic.hpp"
+ 
+ #include <endian.h>
+ #include <sys/ioctl.h>
+diff --git a/src/NVMeBasicContext.hpp b/src/NVMeBasic.hpp
+similarity index 98%
+rename from src/NVMeBasicContext.hpp
+rename to src/NVMeBasic.hpp
+index 52b6a09..a443d8c 100644
+--- a/src/NVMeBasicContext.hpp
++++ b/src/NVMeBasic.hpp
+@@ -1,6 +1,6 @@
+ #pragma once
+ 
+-#include "NVMeContext.hpp"
++#include "NVMeIntf.hpp"
+ 
+ #include <boost/asio/io_context.hpp>
+ #include <boost/asio/posix/stream_descriptor.hpp>
+diff --git a/src/NVMeContext.hpp b/src/NVMeIntf.hpp
+similarity index 100%
+rename from src/NVMeContext.hpp
+rename to src/NVMeIntf.hpp
+diff --git a/src/NVMeSensorMain.cpp b/src/NVMeSensorMain.cpp
+index ef1abbe..8854662 100644
+--- a/src/NVMeSensorMain.cpp
++++ b/src/NVMeSensorMain.cpp
+@@ -14,8 +14,8 @@
+ // limitations under the License.
+ */
+ 
+-#include "NVMeBasicContext.hpp"
+-#include "NVMeContext.hpp"
++#include "NVMeBasic.hpp"
++#include "NVMeIntf.hpp"
+ #include "NVMeSensor.hpp"
+ 
+ #include <boost/asio/steady_timer.hpp>
+diff --git a/src/meson.build b/src/meson.build
+index 665517a..093ae1f 100644
+--- a/src/meson.build
++++ b/src/meson.build
+@@ -186,8 +186,11 @@ if get_option('mcu').enabled()
+ endif
+ 
+ if get_option('nvme').enabled()
+-    nvme_srcs = files('NVMeSensorMain.cpp', 'NVMeSensor.cpp')
+-    nvme_srcs += files('NVMeBasicContext.cpp')
++    nvme_srcs = [
++        'NVMeSensorMain.cpp',
++        'NVMeSensor.cpp',
++        'NVMeBasic.cpp'
++    ]
+ 
+     nvme_deps = [ default_deps, i2c, thresholds_dep, utils_dep, threads ]
+ 
+-- 
+2.34.1
+
diff --git a/recipes-phosphor/sensors/dbus-sensors/0001-nvme-sensor-refactor-the-code.patch b/recipes-phosphor/sensors/dbus-sensors/0002-nvme-sensor-refactor-the-code.patch
similarity index 89%
rename from recipes-phosphor/sensors/dbus-sensors/0001-nvme-sensor-refactor-the-code.patch
rename to recipes-phosphor/sensors/dbus-sensors/0002-nvme-sensor-refactor-the-code.patch
index c31605e..2887ac3 100644
--- a/recipes-phosphor/sensors/dbus-sensors/0001-nvme-sensor-refactor-the-code.patch
+++ b/recipes-phosphor/sensors/dbus-sensors/0002-nvme-sensor-refactor-the-code.patch
@@ -1,11 +1,11 @@
-From 2d40f5d5454621f313e07ee25243c4ba961bd831 Mon Sep 17 00:00:00 2001
+From 9ceb1c6b68ef8123344729b7c2bf6b1eab7d5831 Mon Sep 17 00:00:00 2001
 From: Hao Jiang <jianghao@google.com>
 Date: Mon, 29 Aug 2022 17:21:06 +0000
-Subject: [PATCH 01/34] nvme sensor: refactor the code
+Subject: [PATCH 02/44] nvme sensor: refactor the code
 
 Refactor the code in order to adapt to the hierachy of the NVMe
 subsystem exposed by NVMe-MI spec. The new design is documented at:
-https://github.com/openbmc/docs/blob/628472ab74a0803ed7d726e6a95b00e0767790b9/designs/nvmed.md
+https://gerrit.openbmc.org/plugins/gitiles/openbmc/docs/+/10f2d00fbcfb97cfb02a1ee4005d82f237783668/designs/nvmed.md
 
 The refactor follows the design pattern of MVC (Model-View-Controller).
 
@@ -20,9 +20,6 @@
 together. It schedules the data retrieve from Model and update the
 corresponding View in the heirachy.
 
-Undefine `BOOST_ASIO_NO_DEPRECATED` temporarily until all nvme mi
-patches are merged. An all-in-one fix will be applied.
-
 Tested:
 
 Nuvoton with Samsung PM1733V5TLC
@@ -123,36 +120,25 @@
 Signed-off-by: Hao Jiang <jianghao@google.com>
 Change-Id: Ib99f3c1ab6d65533cb80804abd119f0d4a9f0f8a
 ---
- src/{NVMeBasicContext.cpp => NVMeBasic.cpp} | 193 ++++++++---------
- src/NVMeBasic.hpp                           |  50 +++++
- src/NVMeBasicContext.hpp                    |  48 -----
- src/NVMeContext.hpp                         | 109 ----------
- src/NVMeIntf.hpp                            |  43 ++++
- src/NVMeSensor.cpp                          |   9 +-
- src/NVMeSensor.hpp                          |   4 +-
- src/NVMeSensorMain.cpp                      | 219 +++++++++-----------
- src/NVMeSubsys.cpp                          | 215 +++++++++++++++++++
- src/NVMeSubsys.hpp                          |  70 +++++++
- src/meson.build                             |  11 +-
- 11 files changed, 569 insertions(+), 402 deletions(-)
- rename src/{NVMeBasicContext.cpp => NVMeBasic.cpp} (71%)
- create mode 100644 src/NVMeBasic.hpp
- delete mode 100644 src/NVMeBasicContext.hpp
- delete mode 100644 src/NVMeContext.hpp
- create mode 100644 src/NVMeIntf.hpp
+ src/NVMeBasic.cpp      | 192 ++++++++++++++++-------------------
+ src/NVMeBasic.hpp      |  64 ++++++------
+ src/NVMeIntf.hpp       | 149 ++++++++++++---------------
+ src/NVMeSensor.cpp     |   9 +-
+ src/NVMeSensor.hpp     |   4 +-
+ src/NVMeSensorMain.cpp | 212 +++++++++++++++++----------------------
+ src/NVMeSubsys.cpp     | 223 +++++++++++++++++++++++++++++++++++++++++
+ src/NVMeSubsys.hpp     |  69 +++++++++++++
+ src/meson.build        |   6 +-
+ 9 files changed, 573 insertions(+), 355 deletions(-)
  create mode 100644 src/NVMeSubsys.cpp
  create mode 100644 src/NVMeSubsys.hpp
 
-diff --git a/src/NVMeBasicContext.cpp b/src/NVMeBasic.cpp
-similarity index 71%
-rename from src/NVMeBasicContext.cpp
-rename to src/NVMeBasic.cpp
-index 0bc2252..b2cb44e 100644
---- a/src/NVMeBasicContext.cpp
+diff --git a/src/NVMeBasic.cpp b/src/NVMeBasic.cpp
+index 29407e8..8390452 100644
+--- a/src/NVMeBasic.cpp
 +++ b/src/NVMeBasic.cpp
 @@ -1,20 +1,15 @@
--#include "NVMeBasicContext.hpp"
-+#include "NVMeBasic.hpp"
+ #include "NVMeBasic.hpp"
  
  #include <endian.h>
 -#include <sys/ioctl.h>
@@ -323,7 +309,7 @@
              return;
          }
  
-@@ -336,81 +366,30 @@ void NVMeBasicContext::readAndProcessNVMeSensor()
+@@ -336,81 +366,31 @@ void NVMeBasicContext::readAndProcessNVMeSensor()
              /* Deserialise the response */
              response->consume(1); /* Drop the length byte */
              std::istream is(response.get());
@@ -379,6 +365,7 @@
 -        {
 -            self->readAndProcessNVMeSensor();
 +                data->erase(data->begin());
++                // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
 +                cb({}, reinterpret_cast<DriveStatus*>(data->data()));
 +            });
          }
@@ -427,87 +414,38 @@
 -    sensor->updateValue(value);
 -}
 diff --git a/src/NVMeBasic.hpp b/src/NVMeBasic.hpp
-new file mode 100644
-index 0000000..0a54a55
---- /dev/null
+index a443d8c..3be3051 100644
+--- a/src/NVMeBasic.hpp
 +++ b/src/NVMeBasic.hpp
-@@ -0,0 +1,50 @@
-+#pragma once
-+
+@@ -1,48 +1,50 @@
+ #pragma once
+ 
 +#include "FileHandle.hpp"
-+#include "NVMeIntf.hpp"
-+
-+#include <boost/asio.hpp>
-+#include <boost/asio/posix/stream_descriptor.hpp>
-+
-+#include <thread>
-+
-+class NVMeBasicIO
-+{
-+  public:
-+    NVMeBasicIO(
-+        boost::asio::io_context& io,
-+        std::function<ssize_t(FileHandle& in, FileHandle& out)>&& procFunc);
-+    boost::asio::posix::stream_descriptor reqStream;
-+    boost::asio::posix::stream_descriptor respStream;
-+
-+  private:
-+    std::jthread thread;
-+};
-+
-+// NVMe Basic Management Command
-+// NVMe MI Spec Appendix A.
-+class NVMeBasic :
-+    public NVMeBasicIntf,
-+    public std::enable_shared_from_this<NVMeBasic>
-+{
-+  public:
-+    NVMeBasic(boost::asio::io_context& io, int bus, int addr);
-+
-+    int getBus() const override
-+    {
-+        return bus;
-+    }
-+    int getAddr() const override
-+    {
-+        return addr;
-+    }
-+    void getStatus(std::function<void(const std::error_code&, DriveStatus*)>&&
-+                       cb) override;
-+    ~NVMeBasic() override = default;
-+
-+  private:
-+    boost::asio::io_context& io;
-+    int bus;
-+    int addr;
-+    std::shared_ptr<NVMeBasicIO> basicIO;
-+};
-diff --git a/src/NVMeBasicContext.hpp b/src/NVMeBasicContext.hpp
-deleted file mode 100644
-index 52b6a09..0000000
---- a/src/NVMeBasicContext.hpp
-+++ /dev/null
-@@ -1,48 +0,0 @@
--#pragma once
--
--#include "NVMeContext.hpp"
--
+ #include "NVMeIntf.hpp"
+ 
 -#include <boost/asio/io_context.hpp>
--#include <boost/asio/posix/stream_descriptor.hpp>
--
--#include <thread>
--
++#include <boost/asio.hpp>
+ #include <boost/asio/posix/stream_descriptor.hpp>
+ 
+ #include <thread>
+ 
 -class NVMeBasicContext : public NVMeContext
--{
--  public:
++class NVMeBasicIO
+ {
+   public:
 -    NVMeBasicContext(boost::asio::io_context& io, int rootBus);
 -    ~NVMeBasicContext() override = default;
 -    void pollNVMeDevices() override;
 -    void readAndProcessNVMeSensor() override;
 -    void processResponse(std::shared_ptr<NVMeSensor>& sensor, void* msg,
 -                         size_t len) override;
--
--  private:
++    NVMeBasicIO(
++        boost::asio::io_context& io,
++        std::function<ssize_t(FileHandle& in, FileHandle& out)>&& procFunc);
++    boost::asio::posix::stream_descriptor reqStream;
++    boost::asio::posix::stream_descriptor respStream;
+ 
+   private:
 -    NVMeBasicContext(boost::asio::io_context& io, int rootBus, int cmdOut,
 -                     int streamIn, int streamOut, int cmdIn);
 -    boost::asio::io_context& io;
@@ -521,57 +459,97 @@
 -    // on the pipes associated with reqStream and respStream. Rather than trying
 -    // to force a stop, rely on read()/write() failures from closed pipes to
 -    // coerce it to exit and thus allow completion of the join().
--    std::jthread thread;
--
+     std::jthread thread;
++};
+ 
 -    // Destruction of the stream descriptors has the effect of issuing cancel(),
 -    // destroying the closure of the callback where we might be carrying
 -    // weak_ptrs to `this`.
 -    // https://www.boost.org/doc/libs/1_79_0/doc/html/boost_asio/reference/posix__basic_descriptor/_basic_descriptor.html
 -    boost::asio::posix::stream_descriptor reqStream;
 -    boost::asio::posix::stream_descriptor respStream;
--
++// NVMe Basic Management Command
++// NVMe MI Spec Appendix A.
++class NVMeBasic :
++    public NVMeBasicIntf,
++    public std::enable_shared_from_this<NVMeBasic>
++{
++  public:
++    NVMeBasic(boost::asio::io_context& io, int bus, int addr);
+ 
 -    enum
--    {
++    int getBus() const override
++    {
++        return bus;
++    }
++    int getAddress() const override
+     {
 -        NVME_MI_BASIC_SFLGS_DRIVE_NOT_READY = 0x40,
 -        NVME_MI_BASIC_SFLGS_DRIVE_FUNCTIONAL = 0x20,
 -    };
--};
-diff --git a/src/NVMeContext.hpp b/src/NVMeContext.hpp
-deleted file mode 100644
-index 14e38a1..0000000
---- a/src/NVMeContext.hpp
-+++ /dev/null
-@@ -1,109 +0,0 @@
--#pragma once
++        return addr;
++    }
++    void getStatus(std::function<void(const std::error_code&, DriveStatus*)>&&
++                       cb) override;
++    ~NVMeBasic() override = default;
++
++  private:
++    boost::asio::io_context& io;
++    int bus;
++    int addr;
++    std::shared_ptr<NVMeBasicIO> basicIO;
+ };
+diff --git a/src/NVMeIntf.hpp b/src/NVMeIntf.hpp
+index 14e38a1..6699bae 100644
+--- a/src/NVMeIntf.hpp
++++ b/src/NVMeIntf.hpp
+@@ -1,109 +1,88 @@
+ #pragma once
 -
 -#include "NVMeSensor.hpp"
 -
 -#include <boost/asio/io_context.hpp>
 -#include <boost/asio/steady_timer.hpp>
 -
--#include <memory>
++#include <functional>
+ #include <memory>
 -#include <stdexcept>
 -
 -class NVMeContext : public std::enable_shared_from_this<NVMeContext>
--{
--  public:
++#include <variant>
++
++class NVMeBasicIntf;
++/**
++ * @brief a container class to hold smart ptr to NVMe Basic or NVMe MI
++ * implementation instance
++ */
++class NVMeIntf
+ {
+   public:
 -    NVMeContext(boost::asio::io_context& io, int rootBus) :
 -        scanTimer(io), rootBus(rootBus), pollCursor(sensors.end())
--    {
++    enum class Protocol
+     {
 -        if (rootBus < 0)
 -        {
 -            throw std::invalid_argument(
 -                "Invalid root bus: Bus ID must not be negative");
 -        }
 -    }
--
++        NVMeBasic,
++        NVMeMI,
++    };
+ 
 -    virtual ~NVMeContext()
 -    {
 -        scanTimer.cancel();
 -    }
--
++    NVMeIntf() = default;
+ 
 -    void addSensor(const std::shared_ptr<NVMeSensor>& sensor)
--    {
++    template <class IntfImpl, class... Args>
++    static NVMeIntf create(Args&&... args)
+     {
 -        sensors.emplace_back(sensor);
 -    }
 -
@@ -579,20 +557,27 @@
 -        getSensorAtPath(const std::string& path)
 -    {
 -        for (auto& sensor : sensors)
--        {
++        NVMeIntf nvmeIntf;
++        if constexpr (std::is_base_of_v<NVMeBasicIntf, IntfImpl>)
+         {
 -            if (sensor->configurationPath == path)
 -            {
 -                return sensor;
 -            }
--        }
++            nvmeIntf.interface =
++                std::make_shared<IntfImpl>(std::forward<Args>(args)...);
++            return nvmeIntf;
+         }
 -
 -        return std::nullopt;
--    }
--
++        throw std::runtime_error("Unsupported NVMe interface");
+     }
+ 
 -    // Post-condition: The sensor list does not contain the provided sensor
 -    // Post-condition: pollCursor is a valid iterator for the sensor list
 -    void removeSensor(const std::shared_ptr<NVMeSensor>& sensor)
--    {
++    auto getInferface()
+     {
 -        // Locate the sensor that we're removing in the sensor list
 -        auto found = std::find(sensors.begin(), sensors.end(), sensor);
 -
@@ -613,67 +598,46 @@
 -        }
 -
 -        // We're actively polling the sensor list
--
++        return interface;
++    }
+ 
 -        // If we're not polling the specific sensor that has been removed, then
 -        // remove the sensor
 -        if (*pollCursor != *found)
--        {
++    Protocol getProtocol()
++    {
++        if (std::holds_alternative<std::shared_ptr<NVMeBasicIntf>>(interface))
+         {
 -            sensors.erase(found);
 -            return;
--        }
++            return Protocol::NVMeBasic;
+         }
 -
 -        // We're polling the sensor that is being removed
 -
 -        // Remove the sensor and update the poll cursor so the cursor remains
 -        // valid
 -        pollCursor = sensors.erase(found);
--    }
--
++        throw std::runtime_error("uninitiated NVMeIntf");
+     }
+ 
 -    virtual void close()
--    {
--        scanTimer.cancel();
--    }
--
--    virtual void pollNVMeDevices() = 0;
--
--    virtual void readAndProcessNVMeSensor() = 0;
--
--    virtual void processResponse(std::shared_ptr<NVMeSensor>& sensor, void* msg,
--                                 size_t len) = 0;
--
--  protected:
--    boost::asio::steady_timer scanTimer;
--    int rootBus; // Root bus for this drive
--    std::list<std::shared_ptr<NVMeSensor>> sensors;
--    std::list<std::shared_ptr<NVMeSensor>>::iterator pollCursor;
--};
--
--using NVMEMap = boost::container::flat_map<int, std::shared_ptr<NVMeContext>>;
--
--NVMEMap& getNVMEMap(void);
-diff --git a/src/NVMeIntf.hpp b/src/NVMeIntf.hpp
-new file mode 100644
-index 0000000..1531eb6
---- /dev/null
-+++ b/src/NVMeIntf.hpp
-@@ -0,0 +1,43 @@
-+#pragma once
-+#include <functional>
-+#include <memory>
-+
-+class NVMeIntf
-+{
-+  public:
-+    NVMeIntf() = default;
-+    virtual ~NVMeIntf() = default;
++  private:
++    std::variant<std::shared_ptr<NVMeBasicIntf>> interface;
 +};
 +
-+// Interface to get information via NVMe MI Basic CMD protocol.
-+class NVMeBasicIntf : public NVMeIntf
++/**
++ * @brief Interface to get information via NVMe MI Basic CMD protocol.
++ *
++ * Can be used for implementation or mockup
++ */
++class NVMeBasicIntf
 +{
 +  public:
 +    struct DriveStatus
-+    {
+     {
+-        scanTimer.cancel();
+-    }
 +        uint8_t SmartWarnings;
 +        uint8_t Temp;
 +        uint8_t DriveLifeUsed;
@@ -686,20 +650,33 @@
 +        NVME_MI_BASIC_SFLGS_DRIVE_NOT_READY = 0x40,
 +        NVME_MI_BASIC_SFLGS_DRIVE_FUNCTIONAL = 0x20,
 +    };
-+
+ 
+-    virtual void pollNVMeDevices() = 0;
 +    NVMeBasicIntf() = default;
-+
+ 
+-    virtual void readAndProcessNVMeSensor() = 0;
 +    // The i2c bus number
 +    virtual int getBus() const = 0;
 +    // The i2c address for NVMe Basic
-+    virtual int getAddr() const = 0;
-+
++    virtual int getAddress() const = 0;
+ 
+-    virtual void processResponse(std::shared_ptr<NVMeSensor>& sensor, void* msg,
+-                                 size_t len) = 0;
 +    // Get NVMe drive status, data address is from 00h~07h
 +    virtual void getStatus(
 +        std::function<void(const std::error_code&, DriveStatus*)>&& cb) = 0;
-+
-+    ~NVMeBasicIntf() override = default;
-+};
+ 
+-  protected:
+-    boost::asio::steady_timer scanTimer;
+-    int rootBus; // Root bus for this drive
+-    std::list<std::shared_ptr<NVMeSensor>> sensors;
+-    std::list<std::shared_ptr<NVMeSensor>>::iterator pollCursor;
++    virtual ~NVMeBasicIntf() = default;
+ };
+-
+-using NVMEMap = boost::container::flat_map<int, std::shared_ptr<NVMeContext>>;
+-
+-NVMEMap& getNVMEMap(void);
 diff --git a/src/NVMeSensor.cpp b/src/NVMeSensor.cpp
 index e11fae7..3bd743f 100644
 --- a/src/NVMeSensor.cpp
@@ -746,19 +723,17 @@
      const unsigned int scanDelayTicks = 5 * 60;
      sdbusplus::asio::object_server& objServer;
 diff --git a/src/NVMeSensorMain.cpp b/src/NVMeSensorMain.cpp
-index ef1abbe..0a72a7a 100644
+index 8854662..01d997b 100644
 --- a/src/NVMeSensorMain.cpp
 +++ b/src/NVMeSensorMain.cpp
-@@ -14,21 +14,16 @@
- // limitations under the License.
+@@ -15,20 +15,16 @@
  */
  
--#include "NVMeBasicContext.hpp"
--#include "NVMeContext.hpp"
+ #include "NVMeBasic.hpp"
+-#include "NVMeIntf.hpp"
 -#include "NVMeSensor.hpp"
--
-+#include "NVMeBasic.hpp"
 +#include "NVMeSubsys.hpp"
+ 
  #include <boost/asio/steady_timer.hpp>
  
  #include <optional>
@@ -776,7 +751,7 @@
  
  static std::optional<int>
      extractBusNumber(const std::string& path,
-@@ -44,91 +39,67 @@ static std::optional<int>
+@@ -44,91 +40,68 @@ static std::optional<int>
      return std::visit(VariantToIntVisitor(), findBus->second);
  }
  
@@ -812,14 +787,10 @@
 +    extractName(const std::string& path, const SensorBaseConfigMap& properties)
  {
 -    if (!busNumber)
-+    auto findName = properties.find("Name");
-+    if (findName == properties.end())
-     {
-+        std::cerr << "could not determine configuration name for " << path
-+                  << "\n";
-         return std::nullopt;
-     }
- 
+-    {
+-        return std::nullopt;
+-    }
+-
 -    std::filesystem::path muxPath = deriveRootBusPath(*busNumber);
 -
 -    if (!std::filesystem::is_symlink(muxPath))
@@ -830,11 +801,15 @@
 -    std::string rootName = std::filesystem::read_symlink(muxPath).filename();
 -    size_t dash = rootName.find('-');
 -    if (dash == std::string::npos)
--    {
++    auto findName = properties.find("Name");
++    if (findName == properties.end())
+     {
 -        std::cerr << "Error finding root bus for " << rootName << "\n";
--        return std::nullopt;
--    }
--
++        std::cerr << "could not determine configuration name for " << path
++                  << "\n";
+         return std::nullopt;
+     }
+ 
 -    return std::stoi(rootName.substr(0, dash));
 +    return std::get<std::string>(findName->second);
  }
@@ -865,9 +840,8 @@
  }
  
 -static void handleSensorConfigurations(
--    boost::asio::io_context& io, sdbusplus::asio::object_server& objectServer,
 +static void handleConfigurations(
-+    boost::asio::io_service& io, sdbusplus::asio::object_server& objectServer,
+     boost::asio::io_context& io, sdbusplus::asio::object_server& objectServer,
      std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
 -    const ManagedObjectType& sensorConfigurations)
 +    const ManagedObjectType& nvmeConfigurations)
@@ -891,15 +865,15 @@
 +    for (const auto& [interfacePath, configData] : nvmeConfigurations)
      {
          // find base configuration
--        auto sensorBase =
+         auto sensorBase =
 -            sensorData.find(configInterfaceName(NVMeSensor::sensorType));
 -        if (sensorBase == sensorData.end())
-+        auto sensorBase = configData.find(configInterfaceName(NVMeSubsystem::sensorType));
++            configData.find(configInterfaceName(NVMeSubsystem::sensorType));
 +        if (sensorBase == configData.end())
          {
              continue;
          }
-@@ -136,65 +107,68 @@ static void handleSensorConfigurations(
+@@ -136,65 +109,68 @@ static void handleSensorConfigurations(
          const SensorBaseConfigMap& sensorConfig = sensorBase->second;
          std::optional<int> busNumber =
              extractBusNumber(interfacePath, sensorConfig);
@@ -956,12 +930,12 @@
 +            }
 +            try
 +            {
-+                std::shared_ptr<NVMeIntf> nvmeBasic{
-+                    new NVMeBasic(io, *busNumber, *address)};
++                NVMeIntf nvmeBasic =
++                    NVMeIntf::create<NVMeBasic>(io, *busNumber, *address);
 +
 +                auto nvmeSubsys = std::make_shared<NVMeSubsystem>(
 +                    io, objectServer, dbusConnection, interfacePath,
-+                    *sensorName, configData, nvmeBasic);
++                    *sensorName, configData, std::move(nvmeBasic));
 +                nvmeSubsysMap.emplace(interfacePath, nvmeSubsys);
 +                nvmeSubsys->start();
 +            }
@@ -984,7 +958,7 @@
 -                   sdbusplus::asio::object_server& objectServer,
 -                   std::shared_ptr<sdbusplus::asio::connection>& dbusConnection)
 +void createNVMeSubsystems(
-+    boost::asio::io_service& io, sdbusplus::asio::object_server& objectServer,
++    boost::asio::io_context& io, sdbusplus::asio::object_server& objectServer,
 +    std::shared_ptr<sdbusplus::asio::connection>& dbusConnection)
  {
  
@@ -1007,7 +981,7 @@
  {
      if (message.is_method_error())
      {
-@@ -207,36 +181,33 @@ static void interfaceRemoved(sdbusplus::message_t& message, NVMEMap& contexts)
+@@ -207,36 +183,32 @@ static void interfaceRemoved(sdbusplus::message_t& message, NVMEMap& contexts)
  
      message.read(path, interfaces);
  
@@ -1051,7 +1025,6 @@
 +    systemBus->request_name("xyz.openbmc_project.NVMe");
      sdbusplus::asio::object_server objectServer(systemBus, true);
      objectServer.add_manager("/xyz/openbmc_project/sensors");
-+    objectServer.add_manager("/xyz/openbmc_project/sensors");
  
 -    boost::asio::post(io,
 -                      [&]() { createSensors(io, objectServer, systemBus); });
@@ -1059,7 +1032,7 @@
  
      boost::asio::steady_timer filterTimer(io);
      std::function<void(sdbusplus::message_t&)> eventHandler =
-@@ -256,7 +227,7 @@ int main()
+@@ -256,7 +228,7 @@ int main()
                  return;
              }
  
@@ -1068,7 +1041,7 @@
          });
      };
  
-@@ -272,7 +243,7 @@ int main()
+@@ -272,7 +244,7 @@ int main()
          "type='signal',member='InterfacesRemoved',arg0path='" +
              std::string(inventoryPath) + "/'",
          [](sdbusplus::message_t& msg) {
@@ -1079,10 +1052,10 @@
      setupManufacturingModeMatch(*systemBus);
 diff --git a/src/NVMeSubsys.cpp b/src/NVMeSubsys.cpp
 new file mode 100644
-index 0000000..0f02d16
+index 0000000..e4cc768
 --- /dev/null
 +++ b/src/NVMeSubsys.cpp
-@@ -0,0 +1,215 @@
+@@ -0,0 +1,223 @@
 +#include "NVMeSubsys.hpp"
 +
 +#include "Thresholds.hpp"
@@ -1155,21 +1128,26 @@
 +    return reading;
 +}
 +
-+NVMeSubsystem::NVMeSubsystem(boost::asio::io_context& io,
-+                             sdbusplus::asio::object_server& objServer,
++NVMeSubsystem::NVMeSubsystem(boost::asio::io_context& asio,
++                             sdbusplus::asio::object_server& server,
 +                             std::shared_ptr<sdbusplus::asio::connection> conn,
 +                             const std::string& path, const std::string& name,
-+                             const SensorData& configData,
-+                             const std::shared_ptr<NVMeIntf>& intf) :
-+    io(io),
-+    objServer(objServer), conn(conn), path(path), name(name), nvmeIntf(intf),
-+    ctempTimer(io)
++                             const SensorData& configData, NVMeIntf intf) :
++    io(asio),
++    objServer(server), conn(conn), path(path), name(name),
++    nvmeIntf(std::move(intf)), ctempTimer(io)
 +{
-+    if (!intf)
++    NVMeIntf::Protocol protocol{NVMeIntf::Protocol::NVMeBasic};
++    try
++    {
++        protocol = nvmeIntf.getProtocol();
++    }
++    catch (const std::runtime_error&)
 +    {
 +        throw std::runtime_error("NVMe interface is null");
 +    }
-+    if (dynamic_cast<NVMeBasicIntf*>(nvmeIntf.get()) != nullptr)
++
++    if (protocol == NVMeIntf::Protocol::NVMeBasic)
 +    {
 +        std::optional<std::string> sensorName = createSensorNameFromPath(path);
 +        if (!sensorName)
@@ -1199,8 +1177,11 @@
 +void NVMeSubsystem::start()
 +{
 +    // start to poll value for CTEMP sensor.
-+    if (auto intf = std::dynamic_pointer_cast<NVMeBasicIntf>(nvmeIntf))
++    if (nvmeIntf.getProtocol() == NVMeIntf::Protocol::NVMeBasic)
++
 +    {
++        auto intf =
++            std::get<std::shared_ptr<NVMeBasicIntf>>(nvmeIntf.getInferface());
 +        ctemp_fetcher_t<NVMeBasicIntf::DriveStatus*> dataFether =
 +            [intf](std::function<void(const std::error_code&,
 +                                      NVMeBasicIntf::DriveStatus*)>&& cb) {
@@ -1300,10 +1281,10 @@
 +}
 diff --git a/src/NVMeSubsys.hpp b/src/NVMeSubsys.hpp
 new file mode 100644
-index 0000000..fa741b7
+index 0000000..9fdbeb2
 --- /dev/null
 +++ b/src/NVMeSubsys.hpp
-@@ -0,0 +1,70 @@
+@@ -0,0 +1,69 @@
 +#include "NVMeBasic.hpp"
 +#include "NVMeSensor.hpp"
 +#include "Utils.hpp"
@@ -1317,8 +1298,7 @@
 +                  sdbusplus::asio::object_server& objServer,
 +                  std::shared_ptr<sdbusplus::asio::connection> conn,
 +                  const std::string& path, const std::string& name,
-+                  const SensorData& configData,
-+                  const std::shared_ptr<NVMeIntf>& intf);
++                  const SensorData& configData, NVMeIntf intf);
 +
 +    void start();
 +
@@ -1334,13 +1314,13 @@
 +    std::string path;
 +    std::string name;
 +
-+    std::shared_ptr<NVMeIntf> nvmeIntf;
++    NVMeIntf nvmeIntf;
 +
 +    /* thermal sensor for the subsystem */
 +    std::optional<NVMeSensor> ctemp;
 +    boost::asio::steady_timer ctempTimer;
 +
-+    // Function type for fetching ctemp which incaplucated in a structure of T.
++    // Function type for fetching ctemp which encaplucated in a structure of T.
 +    // The fetcher function take a callback as input to process the result.
 +    template <class T>
 +    using ctemp_fetcher_t =
@@ -1375,22 +1355,18 @@
 +    };
 +};
 diff --git a/src/meson.build b/src/meson.build
-index 665517a..90a3c6f 100644
+index 093ae1f..ceace5f 100644
 --- a/src/meson.build
 +++ b/src/meson.build
-@@ -186,16 +186,19 @@ if get_option('mcu').enabled()
- endif
- 
- if get_option('nvme').enabled()
--    nvme_srcs = files('NVMeSensorMain.cpp', 'NVMeSensor.cpp')
--    nvme_srcs += files('NVMeBasicContext.cpp')
--
-+    nvme_srcs = [
-+        'NVMeSensorMain.cpp',
-+        'NVMeSensor.cpp',
+@@ -189,16 +189,16 @@ if get_option('nvme').enabled()
+     nvme_srcs = [
+         'NVMeSensorMain.cpp',
+         'NVMeSensor.cpp',
+-        'NVMeBasic.cpp'
 +        'NVMeBasic.cpp',
 +        'NVMeSubsys.cpp'
-+    ]
+     ]
+-
      nvme_deps = [ default_deps, i2c, thresholds_dep, utils_dep, threads ]
  
      executable(
@@ -1398,7 +1374,7 @@
          sources: nvme_srcs,
          dependencies: nvme_deps,
 -        cpp_args: uring_args,
-+        cpp_args: [uring_args, '-frtti', '-UBOOST_ASIO_NO_DEPRECATED'], 
++        cpp_args: [uring_args, '-UBOOST_ASIO_NO_DEPRECATED'], 
          install: true,
      )
  endif
diff --git a/recipes-phosphor/sensors/dbus-sensors/0002-NVMe-sensor-add-Storage-and-Drive-interface.patch b/recipes-phosphor/sensors/dbus-sensors/0003-NVMe-sensor-add-Storage-and-Drive-interface.patch
similarity index 84%
rename from recipes-phosphor/sensors/dbus-sensors/0002-NVMe-sensor-add-Storage-and-Drive-interface.patch
rename to recipes-phosphor/sensors/dbus-sensors/0003-NVMe-sensor-add-Storage-and-Drive-interface.patch
index ba713ca..7d5f578 100644
--- a/recipes-phosphor/sensors/dbus-sensors/0002-NVMe-sensor-add-Storage-and-Drive-interface.patch
+++ b/recipes-phosphor/sensors/dbus-sensors/0003-NVMe-sensor-add-Storage-and-Drive-interface.patch
@@ -1,7 +1,7 @@
-From 97215d05ee8506cc2f4dc85ae701307bfb887ed1 Mon Sep 17 00:00:00 2001
+From e6011f504b61528918e61d24141af2f8a67b09ed Mon Sep 17 00:00:00 2001
 From: Hao Jiang <jianghao@google.com>
 Date: Thu, 8 Sep 2022 16:43:59 -0700
-Subject: [PATCH 02/34] NVMe sensor: add Storage and Drive interface
+Subject: [PATCH 03/44] NVMe sensor: add Storage and Drive interface
 
 Add xyz.openbmc_project.Inventory.Item.Drive and
 xyz.openbmc_project.Inventory.Item.Storage interfaces to the nvme
@@ -14,12 +14,12 @@
 Change-Id: Iaa2615f519f5a95dd0cdf3678ca1774b5285f469
 ---
  src/NVMeDrive.hpp      | 16 ++++++++++++++++
- src/NVMeSensorMain.cpp |  2 +-
+ src/NVMeSensorMain.cpp |  1 +
  src/NVMeStorage.hpp    | 17 +++++++++++++++++
  src/NVMeSubsys.cpp     | 37 ++++++++++++++++++++++++++++++++++++-
  src/NVMeSubsys.hpp     | 12 ++++++++++++
  src/meson.build        |  7 ++++++-
- 6 files changed, 88 insertions(+), 3 deletions(-)
+ 6 files changed, 88 insertions(+), 2 deletions(-)
  create mode 100644 src/NVMeDrive.hpp
  create mode 100644 src/NVMeStorage.hpp
 
@@ -46,14 +46,13 @@
 +    }
 +};
 diff --git a/src/NVMeSensorMain.cpp b/src/NVMeSensorMain.cpp
-index 0a72a7a..4e5cfc9 100644
+index 01d997b..a7214a4 100644
 --- a/src/NVMeSensorMain.cpp
 +++ b/src/NVMeSensorMain.cpp
-@@ -205,7 +205,7 @@ int main()
+@@ -207,6 +207,7 @@ int main()
      systemBus->request_name("xyz.openbmc_project.NVMe");
      sdbusplus::asio::object_server objectServer(systemBus, true);
      objectServer.add_manager("/xyz/openbmc_project/sensors");
--    objectServer.add_manager("/xyz/openbmc_project/sensors");
 +    objectServer.add_manager("/xyz/openbmc_project/inventory");
  
      io.post([&]() { createNVMeSubsystems(io, objectServer, systemBus); });
@@ -82,7 +81,7 @@
 +    }
 +};
 diff --git a/src/NVMeSubsys.cpp b/src/NVMeSubsys.cpp
-index 0f02d16..d0e41a5 100644
+index e4cc768..bdce4a2 100644
 --- a/src/NVMeSubsys.cpp
 +++ b/src/NVMeSubsys.cpp
 @@ -2,6 +2,8 @@
@@ -117,25 +116,27 @@
  // get temporature from a NVMe Basic reading.
  static double getTemperatureReading(int8_t reading)
  {
-@@ -78,12 +96,16 @@ NVMeSubsystem::NVMeSubsystem(boost::asio::io_context& io,
-                              const std::shared_ptr<NVMeIntf>& intf) :
-     io(io),
-     objServer(objServer), conn(conn), path(path), name(name), nvmeIntf(intf),
--    ctempTimer(io)
-+    ctempTimer(io),
+@@ -77,7 +95,9 @@ NVMeSubsystem::NVMeSubsystem(boost::asio::io_context& asio,
+                              const SensorData& configData, NVMeIntf intf) :
+     io(asio),
+     objServer(server), conn(conn), path(path), name(name),
+-    nvmeIntf(std::move(intf)), ctempTimer(io)
++    nvmeIntf(std::move(intf)), ctempTimer(io),
 +    storage(*dynamic_cast<sdbusplus::bus_t*>(conn.get()), path.c_str()),
 +    drive(*dynamic_cast<sdbusplus::bus_t*>(conn.get()), path.c_str())
  {
-     if (!intf)
-     {
+     NVMeIntf::Protocol protocol{NVMeIntf::Protocol::NVMeBasic};
+     try
+@@ -89,6 +109,8 @@ NVMeSubsystem::NVMeSubsystem(boost::asio::io_context& asio,
          throw std::runtime_error("NVMe interface is null");
      }
+ 
 +    
 +    // initiate the common interfaces (thermal sensor, Drive and Storage)
-     if (dynamic_cast<NVMeBasicIntf*>(nvmeIntf.get()) != nullptr)
+     if (protocol == NVMeIntf::Protocol::NVMeBasic)
      {
          std::optional<std::string> sensorName = createSensorNameFromPath(path);
-@@ -104,6 +126,17 @@ NVMeSubsystem::NVMeSubsystem(boost::asio::io_context& io,
+@@ -109,6 +131,17 @@ NVMeSubsystem::NVMeSubsystem(boost::asio::io_context& asio,
  
          ctemp.emplace(objServer, io, conn, *sensorName,
                        std::move(sensorThresholds), path);
@@ -153,7 +154,7 @@
      }
      else
      {
-@@ -131,6 +164,8 @@ void NVMeSubsystem::start()
+@@ -139,6 +172,8 @@ void NVMeSubsystem::start()
          };
          pollCtemp(dataFether, dataParser);
      }
@@ -163,7 +164,7 @@
  
  template <class T>
 diff --git a/src/NVMeSubsys.hpp b/src/NVMeSubsys.hpp
-index fa741b7..f4b2cc2 100644
+index 9fdbeb2..3e6f1a5 100644
 --- a/src/NVMeSubsys.hpp
 +++ b/src/NVMeSubsys.hpp
 @@ -1,5 +1,7 @@
@@ -174,7 +175,7 @@
  #include "Utils.hpp"
  
  class NVMeSubsystem : public std::enable_shared_from_this<NVMeSubsystem>
-@@ -49,6 +51,16 @@ class NVMeSubsystem : public std::enable_shared_from_this<NVMeSubsystem>
+@@ -48,6 +50,16 @@ class NVMeSubsystem : public std::enable_shared_from_this<NVMeSubsystem>
      void pollCtemp(const ctemp_fetcher_t<T>& dataFetcher,
                     const ctemp_parser_t<T>& dataParser);
  
@@ -192,7 +193,7 @@
      // It should contain only static function and using binding to the
      // NVMeSubsystem instances. The detail is defined to claim the accessibility
 diff --git a/src/meson.build b/src/meson.build
-index 90a3c6f..788cc6b 100644
+index ceace5f..2ec80ce 100644
 --- a/src/meson.build
 +++ b/src/meson.build
 @@ -192,7 +192,12 @@ if get_option('nvme').enabled()
diff --git a/recipes-phosphor/sensors/dbus-sensors/0003-nvmesensor-Add-NVMe-MI-protocol-and-controller.patch b/recipes-phosphor/sensors/dbus-sensors/0004-nvmesensor-Add-NVMe-MI-protocol-and-controller.patch
similarity index 86%
rename from recipes-phosphor/sensors/dbus-sensors/0003-nvmesensor-Add-NVMe-MI-protocol-and-controller.patch
rename to recipes-phosphor/sensors/dbus-sensors/0004-nvmesensor-Add-NVMe-MI-protocol-and-controller.patch
index aca0cd7..08e31ec 100644
--- a/recipes-phosphor/sensors/dbus-sensors/0003-nvmesensor-Add-NVMe-MI-protocol-and-controller.patch
+++ b/recipes-phosphor/sensors/dbus-sensors/0004-nvmesensor-Add-NVMe-MI-protocol-and-controller.patch
@@ -1,7 +1,7 @@
-From 4dd8224171dcde2f8efe912236eab9b54ac2f2af Mon Sep 17 00:00:00 2001
+From 5cea63d013d451b7de9f0662a5a9952b9c5c3e36 Mon Sep 17 00:00:00 2001
 From: Hao Jiang <jianghao@google.com>
 Date: Fri, 16 Sep 2022 18:52:55 -0700
-Subject: [PATCH 03/34] nvmesensor: Add NVMe-MI protocol and controller
+Subject: [PATCH 04/44] nvmesensor: Add NVMe-MI protocol and controller
 
 NVMeMiIntf will expose NVMe-MI and NVMe-MI admin interface in the async
 style. NVMeMi class is a implementation of the NVMeMiIntf. It is based
@@ -20,15 +20,15 @@
 Change-Id: I2b8d172ed826ca0798b472a8c0bb95c8677e60bb
 ---
  src/NVMeController.hpp   |  47 +++++++++
- src/NVMeIntf.hpp         |  16 +++
+ src/NVMeIntf.hpp         |  35 ++++++-
  src/NVMeMi.cpp           | 212 +++++++++++++++++++++++++++++++++++++++
  src/NVMeMi.hpp           |  48 +++++++++
- src/NVMeSensorMain.cpp   |  29 ++++++
- src/NVMeSubsys.cpp       |  38 ++++++-
+ src/NVMeSensorMain.cpp   |  28 ++++++
+ src/NVMeSubsys.cpp       |  39 ++++++-
  src/NVMeSubsys.hpp       |   4 +
  src/meson.build          |  20 +++-
  subprojects/libnvme.wrap |   6 ++
- 9 files changed, 414 insertions(+), 6 deletions(-)
+ 9 files changed, 432 insertions(+), 7 deletions(-)
  create mode 100644 src/NVMeController.hpp
  create mode 100644 src/NVMeMi.cpp
  create mode 100644 src/NVMeMi.hpp
@@ -88,22 +88,62 @@
 +    nvme_mi_ctrl_t nvmeCtrl;
 +};
 diff --git a/src/NVMeIntf.hpp b/src/NVMeIntf.hpp
-index 1531eb6..0fed5fd 100644
+index 6699bae..cb0e0a7 100644
 --- a/src/NVMeIntf.hpp
 +++ b/src/NVMeIntf.hpp
-@@ -1,4 +1,6 @@
+@@ -1,9 +1,12 @@
  #pragma once
 +#include <libnvme-mi.h>
 +
  #include <functional>
  #include <memory>
+ #include <variant>
  
-@@ -41,3 +43,17 @@ class NVMeBasicIntf : public NVMeIntf
+ class NVMeBasicIntf;
++class NVMeMiIntf;
+ /**
+  * @brief a container class to hold smart ptr to NVMe Basic or NVMe MI
+  * implementation instance
+@@ -29,6 +32,14 @@ class NVMeIntf
+                 std::make_shared<IntfImpl>(std::forward<Args>(args)...);
+             return nvmeIntf;
+         }
++
++        if constexpr (std::is_base_of_v<NVMeMiIntf, IntfImpl>)
++        {
++            nvmeIntf.interface =
++                std::make_shared<IntfImpl>(std::forward<Args>(args)...);
++            return nvmeIntf;
++        }
++
+         throw std::runtime_error("Unsupported NVMe interface");
+     }
  
-     ~NVMeBasicIntf() override = default;
+@@ -43,11 +54,17 @@ class NVMeIntf
+         {
+             return Protocol::NVMeBasic;
+         }
++        else if (std::holds_alternative<std::shared_ptr<NVMeMiIntf>>(interface))
++        {
++            return Protocol::NVMeMI;
++        }
++
+         throw std::runtime_error("uninitiated NVMeIntf");
+     }
+ 
+   private:
+-    std::variant<std::shared_ptr<NVMeBasicIntf>> interface;
++    std::variant<std::shared_ptr<NVMeBasicIntf>, std::shared_ptr<NVMeMiIntf>>
++        interface;
+ };
+ 
+ /**
+@@ -86,3 +103,19 @@ class NVMeBasicIntf
+ 
+     virtual ~NVMeBasicIntf() = default;
  };
 +
-+class NVMeMiIntf : public NVMeIntf
++class NVMeMiIntf
 +{
 +  public:
 +    virtual int getNID() const = 0;
@@ -115,6 +155,8 @@
 +        miScanCtrl(std::function<void(const std::error_code&,
 +                                      const std::vector<nvme_mi_ctrl_t>&)>
 +                       cb) = 0;
++
++    virtual ~NVMeMiIntf() = default;
 +};
 diff --git a/src/NVMeMi.cpp b/src/NVMeMi.cpp
 new file mode 100644
@@ -389,20 +431,18 @@
 +    void post(std::function<void(void)>&& func);
 +};
 diff --git a/src/NVMeSensorMain.cpp b/src/NVMeSensorMain.cpp
-index 4e5cfc9..d85dac7 100644
+index a7214a4..d0e5274 100644
 --- a/src/NVMeSensorMain.cpp
 +++ b/src/NVMeSensorMain.cpp
-@@ -15,7 +15,9 @@
+@@ -15,6 +15,7 @@
  */
  
  #include "NVMeBasic.hpp"
 +#include "NVMeMi.hpp"
  #include "NVMeSubsys.hpp"
-+
- #include <boost/asio/steady_timer.hpp>
  
- #include <optional>
-@@ -150,6 +152,33 @@ static void handleConfigurations(
+ #include <boost/asio/steady_timer.hpp>
+@@ -152,6 +153,33 @@ static void handleConfigurations(
                  continue;
              }
          }
@@ -415,13 +455,13 @@
 +            }
 +            try
 +            {
-+                std::shared_ptr<NVMeIntf> nvmeMi{new NVMeMi(
++                NVMeIntf nvmeMi = NVMeIntf::create<NVMeMi>(
 +                    io, dynamic_cast<sdbusplus::bus_t&>(*dbusConnection),
-+                    *busNumber, *address)};
++                    *busNumber, *address);
 +
 +                auto nvmeSubsys = std::make_shared<NVMeSubsystem>(
 +                    io, objectServer, dbusConnection, interfacePath,
-+                    *sensorName, configData, nvmeMi);
++                    *sensorName, configData, std::move(nvmeMi));
 +                nvmeSubsysMap.emplace(interfacePath, nvmeSubsys);
 +                nvmeSubsys->start();
 +            }
@@ -437,29 +477,30 @@
  }
  
 diff --git a/src/NVMeSubsys.cpp b/src/NVMeSubsys.cpp
-index d0e41a5..7900295 100644
+index bdce4a2..0ab0653 100644
 --- a/src/NVMeSubsys.cpp
 +++ b/src/NVMeSubsys.cpp
-@@ -104,9 +104,10 @@ NVMeSubsystem::NVMeSubsystem(boost::asio::io_context& io,
-     {
+@@ -109,9 +109,9 @@ NVMeSubsystem::NVMeSubsystem(boost::asio::io_context& asio,
          throw std::runtime_error("NVMe interface is null");
      }
+ 
 -    
-+
      // initiate the common interfaces (thermal sensor, Drive and Storage)
--    if (dynamic_cast<NVMeBasicIntf*>(nvmeIntf.get()) != nullptr)
-+    if (dynamic_cast<NVMeBasicIntf*>(nvmeIntf.get()) != nullptr ||
-+        dynamic_cast<NVMeMiIntf*>(nvmeIntf.get()) != nullptr)
+-    if (protocol == NVMeIntf::Protocol::NVMeBasic)
++    if (protocol == NVMeIntf::Protocol::NVMeBasic ||
++        protocol == NVMeIntf::Protocol::NVMeMI)
      {
          std::optional<std::string> sensorName = createSensorNameFromPath(path);
          if (!sensorName)
-@@ -146,6 +147,39 @@ NVMeSubsystem::NVMeSubsystem(boost::asio::io_context& io,
+@@ -151,6 +151,41 @@ NVMeSubsystem::NVMeSubsystem(boost::asio::io_context& asio,
  
  void NVMeSubsystem::start()
  {
 +    // add controllers for the subsystem
-+    if (auto nvme = std::dynamic_pointer_cast<NVMeMiIntf>(nvmeIntf))
++    if (nvmeIntf.getProtocol() == NVMeIntf::Protocol::NVMeMI)
 +    {
++        auto nvme =
++            std::get<std::shared_ptr<NVMeMiIntf>>(nvmeIntf.getInferface());
 +        nvme->miScanCtrl(
 +            [self{shared_from_this()},
 +             nvme](const std::error_code& ec,
@@ -491,10 +532,10 @@
 +    }
 +
      // start to poll value for CTEMP sensor.
-     if (auto intf = std::dynamic_pointer_cast<NVMeBasicIntf>(nvmeIntf))
-     {
+     if (nvmeIntf.getProtocol() == NVMeIntf::Protocol::NVMeBasic)
+ 
 diff --git a/src/NVMeSubsys.hpp b/src/NVMeSubsys.hpp
-index f4b2cc2..1d51cec 100644
+index 3e6f1a5..0a3e8ce 100644
 --- a/src/NVMeSubsys.hpp
 +++ b/src/NVMeSubsys.hpp
 @@ -1,4 +1,5 @@
@@ -503,7 +544,7 @@
  #include "NVMeDrive.hpp"
  #include "NVMeSensor.hpp"
  #include "NVMeStorage.hpp"
-@@ -61,6 +62,9 @@ class NVMeSubsystem : public std::enable_shared_from_this<NVMeSubsystem>
+@@ -60,6 +61,9 @@ class NVMeSubsystem : public std::enable_shared_from_this<NVMeSubsystem>
      */
      NVMeDrive drive;
  
@@ -514,7 +555,7 @@
      // It should contain only static function and using binding to the
      // NVMeSubsystem instances. The detail is defined to claim the accessibility
 diff --git a/src/meson.build b/src/meson.build
-index 788cc6b..9f37585 100644
+index 2ec80ce..4565c01 100644
 --- a/src/meson.build
 +++ b/src/meson.build
 @@ -190,13 +190,25 @@ if get_option('nvme').enabled()
diff --git a/recipes-phosphor/sensors/dbus-sensors/0004-Enable-ctemp-sensor-for-nvme-mi.patch b/recipes-phosphor/sensors/dbus-sensors/0005-Enable-ctemp-sensor-for-nvme-mi.patch
similarity index 63%
rename from recipes-phosphor/sensors/dbus-sensors/0004-Enable-ctemp-sensor-for-nvme-mi.patch
rename to recipes-phosphor/sensors/dbus-sensors/0005-Enable-ctemp-sensor-for-nvme-mi.patch
index 50a4656..fa9900a 100644
--- a/recipes-phosphor/sensors/dbus-sensors/0004-Enable-ctemp-sensor-for-nvme-mi.patch
+++ b/recipes-phosphor/sensors/dbus-sensors/0005-Enable-ctemp-sensor-for-nvme-mi.patch
@@ -1,26 +1,37 @@
-From 80a8495e87cba923ffddb19ffe370c07c11c79ab Mon Sep 17 00:00:00 2001
+From 59880f9f6a4277f5e9b2fdb27ded379532cb0d88 Mon Sep 17 00:00:00 2001
 From: Hao Jiang <jianghao@google.com>
 Date: Wed, 21 Sep 2022 05:49:20 +0000
-Subject: [PATCH 04/34] Enable ctemp sensor for nvme mi
+Subject: [PATCH 05/44] Enable ctemp sensor for nvme mi
 
 polling ctemp from miSubsystemHealthStatusPoll and update the sensor
 
 Signed-off-by: Hao Jiang <jianghao@google.com>
 Change-Id: I03601646db8a66d4b7ff687185a4c7fb7d4c6f8e
 ---
- src/NVMeSubsys.cpp | 22 ++++++++++++++++++++++
- 1 file changed, 22 insertions(+)
+ src/NVMeSubsys.cpp | 26 +++++++++++++++++++++++++-
+ 1 file changed, 25 insertions(+), 1 deletion(-)
 
 diff --git a/src/NVMeSubsys.cpp b/src/NVMeSubsys.cpp
-index 7900295..57668fa 100644
+index 0ab0653..f420282 100644
 --- a/src/NVMeSubsys.cpp
 +++ b/src/NVMeSubsys.cpp
-@@ -198,6 +198,28 @@ void NVMeSubsystem::start()
+@@ -188,7 +188,6 @@ void NVMeSubsystem::start()
+ 
+     // start to poll value for CTEMP sensor.
+     if (nvmeIntf.getProtocol() == NVMeIntf::Protocol::NVMeBasic)
+-
+     {
+         auto intf =
+             std::get<std::shared_ptr<NVMeBasicIntf>>(nvmeIntf.getInferface());
+@@ -207,6 +206,31 @@ void NVMeSubsystem::start()
          };
          pollCtemp(dataFether, dataParser);
      }
-+    else if (auto intf = std::dynamic_pointer_cast<NVMeMiIntf>(nvmeIntf))
++    else if (nvmeIntf.getProtocol() == NVMeIntf::Protocol::NVMeMI)
 +    {
++        auto intf =
++            std::get<std::shared_ptr<NVMeMiIntf>>(nvmeIntf.getInferface());
++
 +        ctemp_fetcher_t<nvme_mi_nvm_ss_health_status*>
 +            dataFether =
 +                [intf](
diff --git a/recipes-phosphor/sensors/dbus-sensors/0005-nvmesensor-Add-Identify-and-cntrl-association.patch b/recipes-phosphor/sensors/dbus-sensors/0006-nvmesensor-Add-Identify-and-cntrl-association.patch
similarity index 95%
rename from recipes-phosphor/sensors/dbus-sensors/0005-nvmesensor-Add-Identify-and-cntrl-association.patch
rename to recipes-phosphor/sensors/dbus-sensors/0006-nvmesensor-Add-Identify-and-cntrl-association.patch
index 061cbae..75f7328 100644
--- a/recipes-phosphor/sensors/dbus-sensors/0005-nvmesensor-Add-Identify-and-cntrl-association.patch
+++ b/recipes-phosphor/sensors/dbus-sensors/0006-nvmesensor-Add-Identify-and-cntrl-association.patch
@@ -1,7 +1,7 @@
-From fe3d552e865877e326b03fd02bc1cda26f0cd8b4 Mon Sep 17 00:00:00 2001
+From 0d88ee217fadfcdd1583fa662cc07754024ab5c6 Mon Sep 17 00:00:00 2001
 From: Hao Jiang <jianghao@google.com>
 Date: Fri, 16 Sep 2022 19:26:38 -0700
-Subject: [PATCH 05/34] nvmesensor: Add Identify and cntrl association
+Subject: [PATCH 06/44] nvmesensor: Add Identify and cntrl association
 
 Add Identify to NVMeMi. The NVMeSubsys will identify the primary
 controllers and their corresponding secondary controllers. It will
@@ -12,12 +12,12 @@
 ---
  src/NVMeController.cpp | 29 ++++++++++++++
  src/NVMeController.hpp | 14 +++++++
- src/NVMeIntf.hpp       |  6 +++
+ src/NVMeIntf.hpp       |  7 ++++
  src/NVMeMi.cpp         | 86 ++++++++++++++++++++++++++++++++++++++++++
  src/NVMeMi.hpp         |  4 ++
  src/NVMeSubsys.cpp     | 85 ++++++++++++++++++++++++++++++++++++++---
  src/meson.build        |  9 +++--
- 7 files changed, 223 insertions(+), 10 deletions(-)
+ 7 files changed, 224 insertions(+), 10 deletions(-)
  create mode 100644 src/NVMeController.cpp
 
 diff --git a/src/NVMeController.cpp b/src/NVMeController.cpp
@@ -86,7 +86,7 @@
 +    std::shared_ptr<sdbusplus::asio::dbus_interface> secAssoc;
  };
 diff --git a/src/NVMeIntf.hpp b/src/NVMeIntf.hpp
-index 0fed5fd..389289b 100644
+index cb0e0a7..b929ca4 100644
 --- a/src/NVMeIntf.hpp
 +++ b/src/NVMeIntf.hpp
 @@ -3,6 +3,7 @@
@@ -94,13 +94,14 @@
  #include <functional>
  #include <memory>
 +#include <span>
+ #include <variant>
  
- class NVMeIntf
- {
-@@ -56,4 +57,9 @@ class NVMeMiIntf : public NVMeIntf
-         miScanCtrl(std::function<void(const std::error_code&,
-                                       const std::vector<nvme_mi_ctrl_t>&)>
+ class NVMeBasicIntf;
+@@ -118,4 +119,10 @@ class NVMeMiIntf
                         cb) = 0;
+ 
+     virtual ~NVMeMiIntf() = default;
++
 +    virtual void adminIdentify(
 +        nvme_mi_ctrl_t ctrl, nvme_identify_cns cns, uint32_t nsid,
 +        uint16_t cntid,
@@ -217,10 +218,10 @@
    private:
      static nvme_root_t nvmeRoot;
 diff --git a/src/NVMeSubsys.cpp b/src/NVMeSubsys.cpp
-index 57668fa..b49743f 100644
+index f420282..426c46d 100644
 --- a/src/NVMeSubsys.cpp
 +++ b/src/NVMeSubsys.cpp
-@@ -162,21 +162,94 @@ void NVMeSubsystem::start()
+@@ -168,21 +168,94 @@ void NVMeSubsystem::start()
                  return;
              }
  
@@ -322,7 +323,7 @@
      }
  
 diff --git a/src/meson.build b/src/meson.build
-index 9f37585..740fdd5 100644
+index 4565c01..a98fb4f 100644
 --- a/src/meson.build
 +++ b/src/meson.build
 @@ -186,13 +186,14 @@ if get_option('mcu').enabled()
diff --git a/recipes-phosphor/sensors/dbus-sensors/0006-Add-NVMeAdmin-intf-with-GetLogPage.patch b/recipes-phosphor/sensors/dbus-sensors/0007-Add-NVMeAdmin-intf-with-GetLogPage.patch
similarity index 98%
rename from recipes-phosphor/sensors/dbus-sensors/0006-Add-NVMeAdmin-intf-with-GetLogPage.patch
rename to recipes-phosphor/sensors/dbus-sensors/0007-Add-NVMeAdmin-intf-with-GetLogPage.patch
index e681fdb..13ed473 100644
--- a/recipes-phosphor/sensors/dbus-sensors/0006-Add-NVMeAdmin-intf-with-GetLogPage.patch
+++ b/recipes-phosphor/sensors/dbus-sensors/0007-Add-NVMeAdmin-intf-with-GetLogPage.patch
@@ -1,7 +1,7 @@
-From 9df967c6ea11a48371336aa78ac967b6eae03ea2 Mon Sep 17 00:00:00 2001
+From 8c190e17acc59dc51fce5f613638fdd7182e9503 Mon Sep 17 00:00:00 2001
 From: Hao Jiang <jianghao@google.com>
 Date: Tue, 20 Sep 2022 18:36:16 +0000
-Subject: [PATCH 06/34] Add NVMeAdmin intf with GetLogPage
+Subject: [PATCH 07/44] Add NVMeAdmin intf with GetLogPage
 
 xyz.openbmc_project.Nvme.NVMeAdmin interface is used to NVMe spec ADMIN
 API to the NVMe devices.
@@ -135,10 +135,10 @@
 +    std::shared_ptr<sdbusplus::asio::dbus_interface> adminIntf;
  };
 diff --git a/src/NVMeIntf.hpp b/src/NVMeIntf.hpp
-index 389289b..0080b3b 100644
+index b929ca4..07b1df6 100644
 --- a/src/NVMeIntf.hpp
 +++ b/src/NVMeIntf.hpp
-@@ -62,4 +62,9 @@ class NVMeMiIntf : public NVMeIntf
+@@ -125,4 +125,9 @@ class NVMeMiIntf
          uint16_t cntid,
          std::function<void(const std::error_code&, std::span<uint8_t>)>&&
              cb) = 0;
diff --git a/recipes-phosphor/sensors/dbus-sensors/0007-Add-more-logs-to-NVMe-daemon.patch b/recipes-phosphor/sensors/dbus-sensors/0008-Add-more-logs-to-NVMe-daemon.patch
similarity index 99%
rename from recipes-phosphor/sensors/dbus-sensors/0007-Add-more-logs-to-NVMe-daemon.patch
rename to recipes-phosphor/sensors/dbus-sensors/0008-Add-more-logs-to-NVMe-daemon.patch
index f2837dc..21cf49b 100644
--- a/recipes-phosphor/sensors/dbus-sensors/0007-Add-more-logs-to-NVMe-daemon.patch
+++ b/recipes-phosphor/sensors/dbus-sensors/0008-Add-more-logs-to-NVMe-daemon.patch
@@ -1,7 +1,7 @@
-From 68db54ddb4a01d87c1a70d959c260f7e990f633b Mon Sep 17 00:00:00 2001
+From ac7745c38854c78697bcb5374c0734bdc921dfad Mon Sep 17 00:00:00 2001
 From: Hao Jiang <jianghao@google.com>
 Date: Mon, 26 Sep 2022 06:07:46 +0000
-Subject: [PATCH 07/34] Add more logs to NVMe daemon
+Subject: [PATCH 08/44] Add more logs to NVMe daemon
 
 The following logs are now supported via GetLogPage:
 * NVME_LOG_LID_ERROR
diff --git a/recipes-phosphor/sensors/dbus-sensors/0008-Add-Identify-method-to-NVMeAdmin-interface.patch b/recipes-phosphor/sensors/dbus-sensors/0009-Add-Identify-method-to-NVMeAdmin-interface.patch
similarity index 93%
rename from recipes-phosphor/sensors/dbus-sensors/0008-Add-Identify-method-to-NVMeAdmin-interface.patch
rename to recipes-phosphor/sensors/dbus-sensors/0009-Add-Identify-method-to-NVMeAdmin-interface.patch
index bfa3b85..5ca69ac 100644
--- a/recipes-phosphor/sensors/dbus-sensors/0008-Add-Identify-method-to-NVMeAdmin-interface.patch
+++ b/recipes-phosphor/sensors/dbus-sensors/0009-Add-Identify-method-to-NVMeAdmin-interface.patch
@@ -1,7 +1,7 @@
-From 3c9556e7a6c93af2665cb3face5e4bf01a6c7ff8 Mon Sep 17 00:00:00 2001
+From 7bf1e5702c953531e1a1aaa22519216a12c6b4c4 Mon Sep 17 00:00:00 2001
 From: Hao Jiang <jianghao@google.com>
 Date: Thu, 29 Sep 2022 18:47:09 +0000
-Subject: [PATCH 08/34] Add Identify method to NVMeAdmin interface
+Subject: [PATCH 09/44] Add Identify method to NVMeAdmin interface
 
 This method is used to send NVMe admin command `Identify` to the target
 controller. It will return a fd to read the raw data field of the
diff --git a/recipes-phosphor/sensors/dbus-sensors/0009-nvmesensor-add-adminXfer-for-MI-interface.patch b/recipes-phosphor/sensors/dbus-sensors/0010-nvmesensor-add-adminXfer-for-MI-interface.patch
similarity index 96%
rename from recipes-phosphor/sensors/dbus-sensors/0009-nvmesensor-add-adminXfer-for-MI-interface.patch
rename to recipes-phosphor/sensors/dbus-sensors/0010-nvmesensor-add-adminXfer-for-MI-interface.patch
index 9bf521e..142e524 100644
--- a/recipes-phosphor/sensors/dbus-sensors/0009-nvmesensor-add-adminXfer-for-MI-interface.patch
+++ b/recipes-phosphor/sensors/dbus-sensors/0010-nvmesensor-add-adminXfer-for-MI-interface.patch
@@ -1,7 +1,7 @@
-From 83360adff6ffe99e383f514a3b890898549f344e Mon Sep 17 00:00:00 2001
+From 900fb7de218c3d25b5ecf8859970ee42a27f0938 Mon Sep 17 00:00:00 2001
 From: Hao Jiang <jianghao@google.com>
 Date: Tue, 27 Sep 2022 14:09:24 -0700
-Subject: [PATCH 09/34] nvmesensor: add adminXfer for MI interface
+Subject: [PATCH 10/44] nvmesensor: add adminXfer for MI interface
 
 The adminXfer is used to transfer arbitrary NVMe-MI admin commands. It
 can be used for any vendor specify commands.
@@ -15,10 +15,10 @@
  3 files changed, 113 insertions(+)
 
 diff --git a/src/NVMeIntf.hpp b/src/NVMeIntf.hpp
-index 0080b3b..9e7b3a4 100644
+index 07b1df6..0a745f1 100644
 --- a/src/NVMeIntf.hpp
 +++ b/src/NVMeIntf.hpp
-@@ -67,4 +67,39 @@ class NVMeMiIntf : public NVMeIntf
+@@ -130,4 +130,39 @@ class NVMeMiIntf
          uint8_t lsp, uint16_t lsi,
          std::function<void(const std::error_code&, std::span<uint8_t>)>&&
              cb) = 0;
diff --git a/recipes-phosphor/sensors/dbus-sensors/0010-nvmesensor-handle-libnvme-mi-status-return-code.patch b/recipes-phosphor/sensors/dbus-sensors/0011-nvmesensor-handle-libnvme-mi-status-return-code.patch
similarity index 98%
rename from recipes-phosphor/sensors/dbus-sensors/0010-nvmesensor-handle-libnvme-mi-status-return-code.patch
rename to recipes-phosphor/sensors/dbus-sensors/0011-nvmesensor-handle-libnvme-mi-status-return-code.patch
index 660af01..de0c592 100644
--- a/recipes-phosphor/sensors/dbus-sensors/0010-nvmesensor-handle-libnvme-mi-status-return-code.patch
+++ b/recipes-phosphor/sensors/dbus-sensors/0011-nvmesensor-handle-libnvme-mi-status-return-code.patch
@@ -1,7 +1,7 @@
-From 41c8ceecc86d720635b18ecf03af60a9294caabb Mon Sep 17 00:00:00 2001
+From 1b5cb7d4950e914a41c6462243cf4da5267dc467 Mon Sep 17 00:00:00 2001
 From: Hao Jiang <jianghao@google.com>
 Date: Wed, 28 Sep 2022 18:23:29 +0000
-Subject: [PATCH 10/34] nvmesensor: handle libnvme-mi status return code
+Subject: [PATCH 11/44] nvmesensor: handle libnvme-mi status return code
 
 libnvme-mi return status by returning a positive rc. Previously it only
 handles the negitive rc with errno.
@@ -14,11 +14,11 @@
  2 files changed, 257 insertions(+), 178 deletions(-)
 
 diff --git a/src/NVMeIntf.hpp b/src/NVMeIntf.hpp
-index 9e7b3a4..d34ac3e 100644
+index 0a745f1..a8cbfad 100644
 --- a/src/NVMeIntf.hpp
 +++ b/src/NVMeIntf.hpp
-@@ -48,6 +48,54 @@ class NVMeBasicIntf : public NVMeIntf
- class NVMeMiIntf : public NVMeIntf
+@@ -108,6 +108,54 @@ class NVMeBasicIntf
+ class NVMeMiIntf
  {
    public:
 +    constexpr static std::string_view statusToString(nvme_mi_resp_status status)
diff --git a/recipes-phosphor/sensors/dbus-sensors/0011-nvmesensor-handle-the-broken-pipe-signal.patch b/recipes-phosphor/sensors/dbus-sensors/0012-nvmesensor-handle-the-broken-pipe-signal.patch
similarity index 84%
rename from recipes-phosphor/sensors/dbus-sensors/0011-nvmesensor-handle-the-broken-pipe-signal.patch
rename to recipes-phosphor/sensors/dbus-sensors/0012-nvmesensor-handle-the-broken-pipe-signal.patch
index 290bf37..3aa375e 100644
--- a/recipes-phosphor/sensors/dbus-sensors/0011-nvmesensor-handle-the-broken-pipe-signal.patch
+++ b/recipes-phosphor/sensors/dbus-sensors/0012-nvmesensor-handle-the-broken-pipe-signal.patch
@@ -1,7 +1,7 @@
-From bd0fc8a47f7ab381d4368d9ea68ab1bfc45bd43e Mon Sep 17 00:00:00 2001
+From d8203b2a8907a44c874ecab4f35c0efd31c4e3de Mon Sep 17 00:00:00 2001
 From: Hao Jiang <jianghao@google.com>
 Date: Mon, 28 Nov 2022 23:28:16 +0000
-Subject: [PATCH 11/34] nvmesensor: handle the broken pipe signal
+Subject: [PATCH 12/44] nvmesensor: handle the broken pipe signal
 
 The NVMe controller uses pipe to transfer raw data. The pipe could
 be closed by the client. It should not be considered as an error.
@@ -13,10 +13,10 @@
  1 file changed, 9 insertions(+)
 
 diff --git a/src/NVMeSensorMain.cpp b/src/NVMeSensorMain.cpp
-index d85dac7..d04d3e5 100644
+index d0e5274..16c9901 100644
 --- a/src/NVMeSensorMain.cpp
 +++ b/src/NVMeSensorMain.cpp
-@@ -276,5 +276,14 @@ int main()
+@@ -277,5 +277,14 @@ int main()
          });
  
      setupManufacturingModeMatch(*systemBus);
diff --git a/recipes-phosphor/sensors/dbus-sensors/0012-nvmesensor-Add-FirmwareCommit-to-nvme-daemon.patch b/recipes-phosphor/sensors/dbus-sensors/0013-nvmesensor-Add-FirmwareCommit-to-nvme-daemon.patch
similarity index 97%
rename from recipes-phosphor/sensors/dbus-sensors/0012-nvmesensor-Add-FirmwareCommit-to-nvme-daemon.patch
rename to recipes-phosphor/sensors/dbus-sensors/0013-nvmesensor-Add-FirmwareCommit-to-nvme-daemon.patch
index 47c1dd4..c7968be 100644
--- a/recipes-phosphor/sensors/dbus-sensors/0012-nvmesensor-Add-FirmwareCommit-to-nvme-daemon.patch
+++ b/recipes-phosphor/sensors/dbus-sensors/0013-nvmesensor-Add-FirmwareCommit-to-nvme-daemon.patch
@@ -1,7 +1,7 @@
-From 9695c31c9d0c9261cdaafaad0027c3c5a80cd95e Mon Sep 17 00:00:00 2001
+From 643ff1844fef8ff814d3f9ee33bc4afc14534ce5 Mon Sep 17 00:00:00 2001
 From: Hao Jiang <jianghao@google.com>
 Date: Fri, 21 Oct 2022 23:25:15 +0000
-Subject: [PATCH 12/34] nvmesensor: Add FirmwareCommit to nvme daemon
+Subject: [PATCH 13/44] nvmesensor: Add FirmwareCommit to nvme daemon
 
 The FirmwareCommit is to send nvme_mi_admin_fw_commit cmd to nvme
 device. The attribute "FirmwareCommitStatus" show the progress stauts of
@@ -161,10 +161,10 @@
 +    FwCommitStatus commitStatus;
  };
 diff --git a/src/NVMeIntf.hpp b/src/NVMeIntf.hpp
-index d34ac3e..e9060ab 100644
+index a8cbfad..7c639c6 100644
 --- a/src/NVMeIntf.hpp
 +++ b/src/NVMeIntf.hpp
-@@ -115,6 +115,10 @@ class NVMeMiIntf : public NVMeIntf
+@@ -178,6 +178,10 @@ class NVMeMiIntf
          uint8_t lsp, uint16_t lsi,
          std::function<void(const std::error_code&, std::span<uint8_t>)>&&
              cb) = 0;
diff --git a/recipes-phosphor/sensors/dbus-sensors/0013-nvmesensor-Using-generated-controller-server.patch b/recipes-phosphor/sensors/dbus-sensors/0014-nvmesensor-Using-generated-controller-server.patch
similarity index 97%
rename from recipes-phosphor/sensors/dbus-sensors/0013-nvmesensor-Using-generated-controller-server.patch
rename to recipes-phosphor/sensors/dbus-sensors/0014-nvmesensor-Using-generated-controller-server.patch
index 9bfb26b..f7e729b 100644
--- a/recipes-phosphor/sensors/dbus-sensors/0013-nvmesensor-Using-generated-controller-server.patch
+++ b/recipes-phosphor/sensors/dbus-sensors/0014-nvmesensor-Using-generated-controller-server.patch
@@ -1,9 +1,9 @@
-From 4ed719b22cf47f677412e2ca40cc10e69dc9dba3 Mon Sep 17 00:00:00 2001
+From a9eeb97934b9afce0c3572040813fee7a5c0930d Mon Sep 17 00:00:00 2001
 From: Hao Jiang <jianghao@google.com>
 Date: Tue, 29 Nov 2022 01:48:52 +0000
-Subject: [PATCH 13/34] nvmesensor: Using generated controller server
+Subject: [PATCH 14/44] nvmesensor: Using generated controller server
 
-libnvme-vu include the yaml file for the definition of
+libnvme-vu includes the yaml file for the definition of
 xyz.openbmc_project.NVMe.NVMeAdmin. The server.hpp and server.cpp
 will be gerenated from the yaml. Move the functionility from dynamic
 insertion of sdbusplus::asio to the inheretence of the generated
@@ -13,8 +13,8 @@
 * a more clear structure by distributing the functions into the
   overloading instead of a bit chunck of constructor.
 * DBus interface sharing across the server and the client.
-* Get prepared for the future plug-in style functions by
-  splitting the Plug-in class out of NVMeController.
+* Get prepared for the future plug-in style knuckle functions by
+  splitting the knuckle class out of NVMeController.
 
 Signed-off-by: Hao Jiang <jianghao@google.com>
 Change-Id: I13d6f6b639f892faf27ee1f3170a21c8793be3a6
@@ -419,7 +419,7 @@
 +                             bool bpid) override;
  };
 diff --git a/src/meson.build b/src/meson.build
-index 740fdd5..5214ebd 100644
+index a98fb4f..6d822d5 100644
 --- a/src/meson.build
 +++ b/src/meson.build
 @@ -207,9 +207,16 @@ if get_option('nvme').enabled()
diff --git a/recipes-phosphor/sensors/dbus-sensors/0014-nvmesensor-Add-NVMePlugin.patch b/recipes-phosphor/sensors/dbus-sensors/0015-nvmesensor-Add-NVMePlugin.patch
similarity index 95%
rename from recipes-phosphor/sensors/dbus-sensors/0014-nvmesensor-Add-NVMePlugin.patch
rename to recipes-phosphor/sensors/dbus-sensors/0015-nvmesensor-Add-NVMePlugin.patch
index 9872f30..e458bfb 100644
--- a/recipes-phosphor/sensors/dbus-sensors/0014-nvmesensor-Add-NVMePlugin.patch
+++ b/recipes-phosphor/sensors/dbus-sensors/0015-nvmesensor-Add-NVMePlugin.patch
@@ -1,7 +1,7 @@
-From 9e3da3bb6496d7745caac537a8d117210667d549 Mon Sep 17 00:00:00 2001
+From 1c0ca7d33b1a749c4f40c0c09dffec006e364a60 Mon Sep 17 00:00:00 2001
 From: Hao Jiang <jianghao@google.com>
 Date: Mon, 28 Nov 2022 23:54:58 +0000
-Subject: [PATCH 14/34] nvmesensor: Add NVMePlugin
+Subject: [PATCH 15/44] nvmesensor: Add NVMePlugin
 
 The NVmePlugin is used to define vendor unique commands or fields(such
 as VU LogPage ID).
@@ -200,7 +200,7 @@
 +    std::shared_ptr<NVMeController> nvmeController;
 +};
 diff --git a/src/NVMeSubsys.cpp b/src/NVMeSubsys.cpp
-index b49743f..7b86fb6 100644
+index 426c46d..15f76e3 100644
 --- a/src/NVMeSubsys.cpp
 +++ b/src/NVMeSubsys.cpp
 @@ -1,5 +1,6 @@
@@ -210,7 +210,7 @@
  #include "Thresholds.hpp"
  
  #include <filesystem>
-@@ -180,11 +181,12 @@ void NVMeSubsystem::start()
+@@ -186,11 +187,12 @@ void NVMeSubsystem::start()
                  std::filesystem::path path = std::filesystem::path(self->path) /
                                               "controllers" /
                                               std::to_string(*index);
@@ -228,7 +228,7 @@
  
                  index++;
              }
-@@ -211,9 +213,9 @@ void NVMeSubsystem::start()
+@@ -217,9 +219,9 @@ void NVMeSubsystem::start()
                      *reinterpret_cast<nvme_secondary_ctrl_list*>(data.data());
  
                  // Remove all associations
@@ -240,7 +240,7 @@
                  }
  
                  if (listHdr.num == 0)
-@@ -246,9 +248,9 @@ void NVMeSubsystem::start()
+@@ -252,9 +254,9 @@ void NVMeSubsystem::start()
                                    << std::endl;
                          break;
                      }
@@ -253,7 +253,7 @@
          });
      }
 diff --git a/src/NVMeSubsys.hpp b/src/NVMeSubsys.hpp
-index 1d51cec..b0c7efa 100644
+index 0a3e8ce..6e5b5f0 100644
 --- a/src/NVMeSubsys.hpp
 +++ b/src/NVMeSubsys.hpp
 @@ -1,6 +1,7 @@
@@ -264,7 +264,7 @@
  #include "NVMeSensor.hpp"
  #include "NVMeStorage.hpp"
  #include "Utils.hpp"
-@@ -62,8 +63,10 @@ class NVMeSubsystem : public std::enable_shared_from_this<NVMeSubsystem>
+@@ -61,8 +62,10 @@ class NVMeSubsystem : public std::enable_shared_from_this<NVMeSubsystem>
      */
      NVMeDrive drive;
  
diff --git a/recipes-phosphor/sensors/dbus-sensors/0015-nvmesensor-add-associations.patch b/recipes-phosphor/sensors/dbus-sensors/0016-nvmesensor-add-associations.patch
similarity index 89%
rename from recipes-phosphor/sensors/dbus-sensors/0015-nvmesensor-add-associations.patch
rename to recipes-phosphor/sensors/dbus-sensors/0016-nvmesensor-add-associations.patch
index 940a773..7d09cb3 100644
--- a/recipes-phosphor/sensors/dbus-sensors/0015-nvmesensor-add-associations.patch
+++ b/recipes-phosphor/sensors/dbus-sensors/0016-nvmesensor-add-associations.patch
@@ -1,7 +1,7 @@
-From 38977ae061e05d1b68da2e313519810c026d53c2 Mon Sep 17 00:00:00 2001
+From ed2364138f22b04e780dfcf49aca78df2f921f5d Mon Sep 17 00:00:00 2001
 From: Willy Tu <wltu@google.com>
 Date: Tue, 6 Dec 2022 14:16:32 -0800
-Subject: [PATCH 15/34] nvmesensor: add associations
+Subject: [PATCH 16/44] nvmesensor: add associations
 
 The BMCWeb uses the associations between
 Chassic/Storage/Drive/StorageController to create the link between
@@ -15,7 +15,7 @@
  2 files changed, 61 insertions(+), 54 deletions(-)
 
 diff --git a/src/NVMeSubsys.cpp b/src/NVMeSubsys.cpp
-index 7b86fb6..0d5af8b 100644
+index 15f76e3..cf364ab 100644
 --- a/src/NVMeSubsys.cpp
 +++ b/src/NVMeSubsys.cpp
 @@ -59,20 +59,23 @@ std::optional<std::string> createSensorNameFromPath(const std::string& path)
@@ -55,14 +55,14 @@
  }
  
  // get temporature from a NVMe Basic reading.
-@@ -107,43 +110,41 @@ NVMeSubsystem::NVMeSubsystem(boost::asio::io_context& io,
+@@ -111,43 +114,41 @@ NVMeSubsystem::NVMeSubsystem(boost::asio::io_context& asio,
      }
  
      // initiate the common interfaces (thermal sensor, Drive and Storage)
--    if (dynamic_cast<NVMeBasicIntf*>(nvmeIntf.get()) != nullptr ||
--        dynamic_cast<NVMeMiIntf*>(nvmeIntf.get()) != nullptr)
-+    if (dynamic_cast<NVMeBasicIntf*>(nvmeIntf.get()) == nullptr &&
-+        dynamic_cast<NVMeMiIntf*>(nvmeIntf.get()) == nullptr)
+-    if (protocol == NVMeIntf::Protocol::NVMeBasic ||
+-        protocol == NVMeIntf::Protocol::NVMeMI)
++    if (protocol != NVMeIntf::Protocol::NVMeBasic &&
++        protocol != NVMeIntf::Protocol::NVMeMI)
      {
 -        std::optional<std::string> sensorName = createSensorNameFromPath(path);
 -        if (!sensorName)
@@ -130,7 +130,7 @@
  }
  
  void NVMeSubsystem::start()
-@@ -160,6 +161,7 @@ void NVMeSubsystem::start()
+@@ -166,6 +167,7 @@ void NVMeSubsystem::start()
                  // TODO: mark the subsystem invalid and reschedule refresh
                  std::cerr << "fail to scan controllers for the nvme subsystem"
                            << (ec ? ": " + ec.message() : "") << std::endl;
@@ -138,7 +138,7 @@
                  return;
              }
  
-@@ -188,8 +190,13 @@ void NVMeSubsystem::start()
+@@ -194,8 +196,13 @@ void NVMeSubsystem::start()
                  self->controllers.insert({*index, {nvmeController, plugin}});
                  nvmeController->start(plugin);
  
@@ -152,10 +152,10 @@
  
              /*
              find primary controller and make association
-@@ -275,16 +282,13 @@ void NVMeSubsystem::start()
-     }
-     else if (auto intf = std::dynamic_pointer_cast<NVMeMiIntf>(nvmeIntf))
-     {
+@@ -286,16 +293,13 @@ void NVMeSubsystem::start()
+         auto intf =
+             std::get<std::shared_ptr<NVMeMiIntf>>(nvmeIntf.getInferface());
+ 
 -        ctemp_fetcher_t<nvme_mi_nvm_ss_health_status*>
 -            dataFether =
 -                [intf](
@@ -175,10 +175,10 @@
              bool df = status->nss & 0x20;
              if (!df)
 diff --git a/src/NVMeSubsys.hpp b/src/NVMeSubsys.hpp
-index b0c7efa..3b654fc 100644
+index 6e5b5f0..0f24380 100644
 --- a/src/NVMeSubsys.hpp
 +++ b/src/NVMeSubsys.hpp
-@@ -86,4 +86,7 @@ class NVMeSubsystem : public std::enable_shared_from_this<NVMeSubsystem>
+@@ -85,4 +85,7 @@ class NVMeSubsystem : public std::enable_shared_from_this<NVMeSubsystem>
                                  ctemp_fetcher_t<T> dataFetcher,
                                  boost::system::error_code errorCode, T data);
      };
diff --git a/recipes-phosphor/sensors/dbus-sensors/0016-nvmesensor-Create-thermal-sensor-in-start.patch b/recipes-phosphor/sensors/dbus-sensors/0017-nvmesensor-Create-thermal-sensor-in-start.patch
similarity index 70%
rename from recipes-phosphor/sensors/dbus-sensors/0016-nvmesensor-Create-thermal-sensor-in-start.patch
rename to recipes-phosphor/sensors/dbus-sensors/0017-nvmesensor-Create-thermal-sensor-in-start.patch
index da1aaa4..9ba89af 100644
--- a/recipes-phosphor/sensors/dbus-sensors/0016-nvmesensor-Create-thermal-sensor-in-start.patch
+++ b/recipes-phosphor/sensors/dbus-sensors/0017-nvmesensor-Create-thermal-sensor-in-start.patch
@@ -1,7 +1,7 @@
-From 68c6f26bd4df63fcfdc5fd142dad21535a05eb1b Mon Sep 17 00:00:00 2001
+From 908941f8a92c2385b4fefd19e982865482da2d7a Mon Sep 17 00:00:00 2001
 From: Hao Jiang <jianghao@google.com>
 Date: Tue, 20 Dec 2022 01:58:11 +0000
-Subject: [PATCH 16/34] nvmesensor: Create thermal sensor in start()
+Subject: [PATCH 17/44] nvmesensor: Create thermal sensor in start()
 
 Move the thermal sensor creation from NVMeSubsys constructor to start().
 This is to align with the other sublayer construction, e.g. Controllers.
@@ -11,32 +11,32 @@
 Change-Id: Iccc45fc0d99777763a67995fb6ca6ce3d0e52f7c
 ---
  src/NVMeSensorMain.cpp |  8 ++++----
- src/NVMeSubsys.cpp     | 40 ++++++++++++++++++++--------------------
- src/NVMeSubsys.hpp     |  3 +--
- 3 files changed, 25 insertions(+), 26 deletions(-)
+ src/NVMeSubsys.cpp     | 41 +++++++++++++++++++++--------------------
+ src/NVMeSubsys.hpp     |  4 ++--
+ 3 files changed, 27 insertions(+), 26 deletions(-)
 
 diff --git a/src/NVMeSensorMain.cpp b/src/NVMeSensorMain.cpp
-index d04d3e5..0656d2c 100644
+index 16c9901..225b05e 100644
 --- a/src/NVMeSensorMain.cpp
 +++ b/src/NVMeSensorMain.cpp
-@@ -140,9 +140,9 @@ static void handleConfigurations(
+@@ -141,9 +141,9 @@ static void handleConfigurations(
  
                  auto nvmeSubsys = std::make_shared<NVMeSubsystem>(
                      io, objectServer, dbusConnection, interfacePath,
--                    *sensorName, configData, nvmeBasic);
-+                    *sensorName, nvmeBasic);
+-                    *sensorName, configData, std::move(nvmeBasic));
++                    *sensorName, std::move(nvmeBasic));
                  nvmeSubsysMap.emplace(interfacePath, nvmeSubsys);
 -                nvmeSubsys->start();
 +                nvmeSubsys->start(configData);
              }
              catch (std::exception& ex)
              {
-@@ -167,9 +167,9 @@ static void handleConfigurations(
+@@ -168,9 +168,9 @@ static void handleConfigurations(
  
                  auto nvmeSubsys = std::make_shared<NVMeSubsystem>(
                      io, objectServer, dbusConnection, interfacePath,
--                    *sensorName, configData, nvmeMi);
-+                    *sensorName, nvmeMi);
+-                    *sensorName, configData, std::move(nvmeMi));
++                    *sensorName, std::move(nvmeMi));
                  nvmeSubsysMap.emplace(interfacePath, nvmeSubsys);
 -                nvmeSubsys->start();
 +                nvmeSubsys->start(configData);
@@ -44,18 +44,19 @@
              catch (std::exception& ex)
              {
 diff --git a/src/NVMeSubsys.cpp b/src/NVMeSubsys.cpp
-index 0d5af8b..f718681 100644
+index cf364ab..17f3daa 100644
 --- a/src/NVMeSubsys.cpp
 +++ b/src/NVMeSubsys.cpp
-@@ -96,7 +96,6 @@ NVMeSubsystem::NVMeSubsystem(boost::asio::io_context& io,
-                              sdbusplus::asio::object_server& objServer,
+@@ -96,7 +96,7 @@ NVMeSubsystem::NVMeSubsystem(boost::asio::io_context& asio,
+                              sdbusplus::asio::object_server& server,
                               std::shared_ptr<sdbusplus::asio::connection> conn,
                               const std::string& path, const std::string& name,
--                             const SensorData& configData,
-                              const std::shared_ptr<NVMeIntf>& intf) :
-     io(io),
-     objServer(objServer), conn(conn), path(path), name(name), nvmeIntf(intf),
-@@ -116,24 +115,6 @@ NVMeSubsystem::NVMeSubsystem(boost::asio::io_context& io,
+-                             const SensorData& configData, NVMeIntf intf) :
++                             NVMeIntf intf) :
+     io(asio),
+     objServer(server), conn(conn), path(path), name(name),
+     nvmeIntf(std::move(intf)), ctempTimer(io),
+@@ -120,24 +120,6 @@ NVMeSubsystem::NVMeSubsystem(boost::asio::io_context& asio,
          throw std::runtime_error("Unsupported NVMe interface");
      }
  
@@ -80,7 +81,7 @@
      /* xyz.openbmc_project.Inventory.Item.Drive */
      drive.protocol(NVMeDrive::DriveProtocol::NVMe);
      drive.type(NVMeDrive::DriveType::SSD);
-@@ -147,7 +128,7 @@ NVMeSubsystem::NVMeSubsystem(boost::asio::io_context& io,
+@@ -151,7 +133,7 @@ NVMeSubsystem::NVMeSubsystem(boost::asio::io_context& asio,
      associations.emplace_back("drive", "storage", path);
  }
  
@@ -88,8 +89,8 @@
 +void NVMeSubsystem::start(const SensorData& configData)
  {
      // add controllers for the subsystem
-     if (auto nvme = std::dynamic_pointer_cast<NVMeMiIntf>(nvmeIntf))
-@@ -262,6 +243,25 @@ void NVMeSubsystem::start()
+     if (nvmeIntf.getProtocol() == NVMeIntf::Protocol::NVMeMI)
+@@ -268,6 +250,25 @@ void NVMeSubsystem::start()
          });
      }
  
@@ -113,18 +114,18 @@
 +                  path);
 +
      // start to poll value for CTEMP sensor.
-     if (auto intf = std::dynamic_pointer_cast<NVMeBasicIntf>(nvmeIntf))
+     if (nvmeIntf.getProtocol() == NVMeIntf::Protocol::NVMeBasic)
      {
 diff --git a/src/NVMeSubsys.hpp b/src/NVMeSubsys.hpp
-index 3b654fc..eb10b47 100644
+index 0f24380..403b5f7 100644
 --- a/src/NVMeSubsys.hpp
 +++ b/src/NVMeSubsys.hpp
-@@ -15,10 +15,9 @@ class NVMeSubsystem : public std::enable_shared_from_this<NVMeSubsystem>
+@@ -15,9 +15,9 @@ class NVMeSubsystem : public std::enable_shared_from_this<NVMeSubsystem>
                    sdbusplus::asio::object_server& objServer,
                    std::shared_ptr<sdbusplus::asio::connection> conn,
                    const std::string& path, const std::string& name,
--                  const SensorData& configData,
-                   const std::shared_ptr<NVMeIntf>& intf);
+-                  const SensorData& configData, NVMeIntf intf);
++                  NVMeIntf intf);
  
 -    void start();
 +    void start(const SensorData& configData);
diff --git a/recipes-phosphor/sensors/dbus-sensors/0017-nvmesensor-Move-temp-sensor-function-to-NVMeUtil.patch b/recipes-phosphor/sensors/dbus-sensors/0018-nvmesensor-Move-temp-sensor-function-to-NVMeUtil.patch
similarity index 93%
rename from recipes-phosphor/sensors/dbus-sensors/0017-nvmesensor-Move-temp-sensor-function-to-NVMeUtil.patch
rename to recipes-phosphor/sensors/dbus-sensors/0018-nvmesensor-Move-temp-sensor-function-to-NVMeUtil.patch
index 8eb320a..30a5575 100644
--- a/recipes-phosphor/sensors/dbus-sensors/0017-nvmesensor-Move-temp-sensor-function-to-NVMeUtil.patch
+++ b/recipes-phosphor/sensors/dbus-sensors/0018-nvmesensor-Move-temp-sensor-function-to-NVMeUtil.patch
@@ -1,7 +1,7 @@
-From 5e59fcefac0a370b7e0e1a4660f9ee2a1260980b Mon Sep 17 00:00:00 2001
+From d5d4f6874189cb526ffcd04ad767891d3e1a6b99 Mon Sep 17 00:00:00 2001
 From: Hao Jiang <jianghao@google.com>
 Date: Thu, 22 Dec 2022 00:48:57 +0000
-Subject: [PATCH 17/34] nvmesensor: Move temp sensor function to NVMeUtil
+Subject: [PATCH 18/44] nvmesensor: Move temp sensor function to NVMeUtil
 
 The temp sensor can not only be used for NVMe subsystem. E.g. Each NVMe
 controller can have its individual cTEMP reporting from controller level
@@ -18,15 +18,13 @@
  create mode 100644 src/NVMeUtil.hpp
 
 diff --git a/src/NVMeSubsys.cpp b/src/NVMeSubsys.cpp
-index f718681..1b0aef5 100644
+index 17f3daa..d6c937d 100644
 --- a/src/NVMeSubsys.cpp
 +++ b/src/NVMeSubsys.cpp
-@@ -3,61 +3,8 @@
- #include "NVMePlugin.hpp"
- #include "Thresholds.hpp"
+@@ -5,60 +5,6 @@
  
--#include <filesystem>
--
+ #include <filesystem>
+ 
 -std::optional<std::string>
 -    extractOneFromTail(std::string::const_reverse_iterator& rbegin,
 -                       const std::string::const_reverse_iterator& rend)
@@ -56,7 +54,7 @@
 -    name.append(rbegin.base(), curr.base());
 -    return {name};
 -}
- 
+-
 -// a path of "/xyz/openbmc_project/inventory/system/board/{prod}/{nvme}" will
 -// generates a sensor name {prod}_{nvme}
 -std::optional<std::string> createSensorNameFromPath(const std::string& path)
@@ -80,19 +78,20 @@
 -    name.append(*nvme);
 -    return name;
 -}
-+#include <filesystem>
- 
+-
  void NVMeSubsystem::createStorageAssociation()
  {
-@@ -99,7 +46,6 @@ NVMeSubsystem::NVMeSubsystem(boost::asio::io_context& io,
-                              const std::shared_ptr<NVMeIntf>& intf) :
-     io(io),
-     objServer(objServer), conn(conn), path(path), name(name), nvmeIntf(intf),
--    ctempTimer(io),
+     auto storageAssociation =
+@@ -99,7 +45,7 @@ NVMeSubsystem::NVMeSubsystem(boost::asio::io_context& asio,
+                              NVMeIntf intf) :
+     io(asio),
+     objServer(server), conn(conn), path(path), name(name),
+-    nvmeIntf(std::move(intf)), ctempTimer(io),
++    nvmeIntf(std::move(intf)),
      storage(*dynamic_cast<sdbusplus::bus_t*>(conn.get()), path.c_str()),
      drive(*dynamic_cast<sdbusplus::bus_t*>(conn.get()), path.c_str())
  {
-@@ -259,8 +205,9 @@ void NVMeSubsystem::start(const SensorData& configData)
+@@ -266,8 +212,9 @@ void NVMeSubsystem::start(const SensorData& configData)
                                   *sensorName);
      }
  
@@ -103,17 +102,17 @@
 +    ctempTimer = std::make_shared<boost::asio::steady_timer>(io);
  
      // start to poll value for CTEMP sensor.
-     if (auto intf = std::dynamic_pointer_cast<NVMeBasicIntf>(nvmeIntf))
-@@ -278,7 +225,7 @@ void NVMeSubsystem::start(const SensorData& configData)
+     if (nvmeIntf.getProtocol() == NVMeIntf::Protocol::NVMeBasic)
+@@ -287,7 +234,7 @@ void NVMeSubsystem::start(const SensorData& configData)
              }
              return {getTemperatureReading(status->Temp)};
          };
 -        pollCtemp(dataFether, dataParser);
 +        pollCtemp(this->ctempTimer, this->ctemp, dataFether, dataParser);
      }
-     else if (auto intf = std::dynamic_pointer_cast<NVMeMiIntf>(nvmeIntf))
+     else if (nvmeIntf.getProtocol() == NVMeIntf::Protocol::NVMeMI)
      {
-@@ -297,89 +244,8 @@ void NVMeSubsystem::start(const SensorData& configData)
+@@ -309,89 +256,8 @@ void NVMeSubsystem::start(const SensorData& configData)
              }
              return {getTemperatureReading(status->ctemp)};
          };
@@ -205,7 +204,7 @@
 -    self->pollCtemp(dataFetcher, dataParser);
 -}
 diff --git a/src/NVMeSubsys.hpp b/src/NVMeSubsys.hpp
-index eb10b47..db9f228 100644
+index 403b5f7..d9c7a78 100644
 --- a/src/NVMeSubsys.hpp
 +++ b/src/NVMeSubsys.hpp
 @@ -1,9 +1,11 @@
@@ -231,13 +230,13 @@
    private:
 @@ -33,24 +35,10 @@ class NVMeSubsystem : public std::enable_shared_from_this<NVMeSubsystem>
  
-     std::shared_ptr<NVMeIntf> nvmeIntf;
+     NVMeIntf nvmeIntf;
  
 -    /* thermal sensor for the subsystem */
 -    std::optional<NVMeSensor> ctemp;
 -    boost::asio::steady_timer ctempTimer;
 -
--    // Function type for fetching ctemp which incaplucated in a structure of T.
+-    // Function type for fetching ctemp which encaplucated in a structure of T.
 -    // The fetcher function take a callback as input to process the result.
 -    template <class T>
 -    using ctemp_fetcher_t =
@@ -285,7 +284,7 @@
  };
 diff --git a/src/NVMeUtil.hpp b/src/NVMeUtil.hpp
 new file mode 100644
-index 0000000..3b6e73a
+index 0000000..16fe291
 --- /dev/null
 +++ b/src/NVMeUtil.hpp
 @@ -0,0 +1,219 @@
@@ -392,7 +391,7 @@
 +
 +// Function to update NVMe temp sensor
 +
-+// Function type for fetching ctemp which incaplucated in a structure of T.
++// Function type for fetching ctemp which encaplucated in a structure of T.
 +// The fetcher function take a callback as input to process the result.
 +template <class T>
 +using ctemp_fetcher_t =
diff --git a/recipes-phosphor/sensors/dbus-sensors/0018-nvmesensor-Change-the-plugin-log-handler.patch b/recipes-phosphor/sensors/dbus-sensors/0019-nvmesensor-Change-the-plugin-log-handler.patch
similarity index 95%
rename from recipes-phosphor/sensors/dbus-sensors/0018-nvmesensor-Change-the-plugin-log-handler.patch
rename to recipes-phosphor/sensors/dbus-sensors/0019-nvmesensor-Change-the-plugin-log-handler.patch
index bec85b0..fb334c3 100644
--- a/recipes-phosphor/sensors/dbus-sensors/0018-nvmesensor-Change-the-plugin-log-handler.patch
+++ b/recipes-phosphor/sensors/dbus-sensors/0019-nvmesensor-Change-the-plugin-log-handler.patch
@@ -1,7 +1,7 @@
-From 2c76fcabed53855b515d33cf80afb8eb82cd9ebd Mon Sep 17 00:00:00 2001
+From c323d7ab2beaaf0342894d79efc3e83196ce2f09 Mon Sep 17 00:00:00 2001
 From: Hao Jiang <jianghao@google.com>
 Date: Thu, 22 Dec 2022 19:35:32 +0000
-Subject: [PATCH 18/34] nvmesensor: Change the plugin log handler
+Subject: [PATCH 19/44] nvmesensor: Change the plugin log handler
 
 Align the logpage handler type definition to the standard NVMe-MI
 logpage function(A.K.A adminGetLogPage). This is because the plugin
diff --git a/recipes-phosphor/sensors/dbus-sensors/0019-nvmesensor-Add-nvme-controller-plugin.patch b/recipes-phosphor/sensors/dbus-sensors/0020-nvmesensor-Add-nvme-controller-plugin.patch
similarity index 94%
rename from recipes-phosphor/sensors/dbus-sensors/0019-nvmesensor-Add-nvme-controller-plugin.patch
rename to recipes-phosphor/sensors/dbus-sensors/0020-nvmesensor-Add-nvme-controller-plugin.patch
index 3c2f1fd..5887d23 100644
--- a/recipes-phosphor/sensors/dbus-sensors/0019-nvmesensor-Add-nvme-controller-plugin.patch
+++ b/recipes-phosphor/sensors/dbus-sensors/0020-nvmesensor-Add-nvme-controller-plugin.patch
@@ -1,7 +1,7 @@
-From f90edf75d2bf6a9d3375fadad5e6ec2e5b3f9913 Mon Sep 17 00:00:00 2001
+From 21d908371df619878d2c2afb303c963e320c81d8 Mon Sep 17 00:00:00 2001
 From: Hao Jiang <jianghao@google.com>
 Date: Tue, 3 Jan 2023 19:45:03 +0000
-Subject: [PATCH 19/34] nvmesensor: Add nvme/controller plugin
+Subject: [PATCH 20/44] nvmesensor: Add nvme/controller plugin
 
 Rename the NVMePlugin to NVMeControllerPlugin. And add NVMePlugin for
 NVMe subsystem.
@@ -21,9 +21,9 @@
  src/NVMeController.cpp |   2 +-
  src/NVMeController.hpp |  20 +++++--
  src/NVMePlugin.hpp     | 122 ++++++++++++++++++++++++++++++++++++-----
- src/NVMeSubsys.cpp     |  58 +++++++++++++++-----
+ src/NVMeSubsys.cpp     |  57 +++++++++++++++----
  src/NVMeSubsys.hpp     |  14 +++--
- 5 files changed, 178 insertions(+), 38 deletions(-)
+ 5 files changed, 178 insertions(+), 37 deletions(-)
 
 diff --git a/src/NVMeController.cpp b/src/NVMeController.cpp
 index 46f84cb..c5333e1 100644
@@ -258,20 +258,12 @@
 +    std::shared_ptr<NVMeSubsystem> subsystem;
 +};
 diff --git a/src/NVMeSubsys.cpp b/src/NVMeSubsys.cpp
-index 1b0aef5..90bad81 100644
+index d6c937d..e45db4f 100644
 --- a/src/NVMeSubsys.cpp
 +++ b/src/NVMeSubsys.cpp
-@@ -3,7 +3,6 @@
- #include "NVMePlugin.hpp"
- #include "Thresholds.hpp"
- 
--
- #include <filesystem>
- 
- void NVMeSubsystem::createStorageAssociation()
-@@ -80,9 +79,9 @@ void NVMeSubsystem::start(const SensorData& configData)
-     if (auto nvme = std::dynamic_pointer_cast<NVMeMiIntf>(nvmeIntf))
-     {
+@@ -87,9 +87,9 @@ void NVMeSubsystem::start(const SensorData& configData)
+         auto nvme =
+             std::get<std::shared_ptr<NVMeMiIntf>>(nvmeIntf.getInferface());
          nvme->miScanCtrl(
 -            [self{shared_from_this()},
 -             nvme](const std::error_code& ec,
@@ -282,7 +274,7 @@
              if (ec || ctrlList.size() == 0)
              {
                  // TODO: mark the subsystem invalid and reschedule refresh
-@@ -110,16 +109,36 @@ void NVMeSubsystem::start(const SensorData& configData)
+@@ -117,16 +117,36 @@ void NVMeSubsystem::start(const SensorData& configData)
                  std::filesystem::path path = std::filesystem::path(self->path) /
                                               "controllers" /
                                               std::to_string(*index);
@@ -328,7 +320,7 @@
  
                  index++;
              }
-@@ -248,4 +267,17 @@ void NVMeSubsystem::start(const SensorData& configData)
+@@ -260,4 +280,17 @@ void NVMeSubsystem::start(const SensorData& configData)
      }
  
      // TODO: start to poll Drive status.
@@ -347,7 +339,7 @@
 +    ctempTimer->cancel();
  }
 diff --git a/src/NVMeSubsys.hpp b/src/NVMeSubsys.hpp
-index db9f228..8af2497 100644
+index d9c7a78..ced3dc1 100644
 --- a/src/NVMeSubsys.hpp
 +++ b/src/NVMeSubsys.hpp
 @@ -2,12 +2,14 @@
@@ -383,7 +375,7 @@
      std::shared_ptr<sdbusplus::asio::connection> conn;
 @@ -35,6 +35,8 @@ class NVMeSubsystem : public std::enable_shared_from_this<NVMeSubsystem>
  
-     std::shared_ptr<NVMeIntf> nvmeIntf;
+     NVMeIntf nvmeIntf;
  
 +    // plugin
 +    std::shared_ptr<NVMePlugin> plugin;
diff --git a/recipes-phosphor/sensors/dbus-sensors/0020-nvmesensor-add-timeout-for-xfer.patch b/recipes-phosphor/sensors/dbus-sensors/0021-nvmesensor-add-timeout-for-xfer.patch
similarity index 95%
rename from recipes-phosphor/sensors/dbus-sensors/0020-nvmesensor-add-timeout-for-xfer.patch
rename to recipes-phosphor/sensors/dbus-sensors/0021-nvmesensor-add-timeout-for-xfer.patch
index ced5672..440d8b4 100644
--- a/recipes-phosphor/sensors/dbus-sensors/0020-nvmesensor-add-timeout-for-xfer.patch
+++ b/recipes-phosphor/sensors/dbus-sensors/0021-nvmesensor-add-timeout-for-xfer.patch
@@ -1,7 +1,7 @@
-From d4b11f261a5a8da134e8a354e31d103de8ffe817 Mon Sep 17 00:00:00 2001
+From 2b330a663d6688096a00d3259a2e3ace2e851021 Mon Sep 17 00:00:00 2001
 From: Hao Jiang <jianghao@google.com>
 Date: Tue, 10 Jan 2023 01:24:00 +0000
-Subject: [PATCH 20/34] nvmesensor: add timeout for xfer
+Subject: [PATCH 21/44] nvmesensor: add timeout for xfer
 
 The xfer which is used by plugins or others may share a different timeout
 setting, since the VU command could take time for processing. So added
@@ -18,10 +18,10 @@
  4 files changed, 14 insertions(+), 6 deletions(-)
 
 diff --git a/src/NVMeIntf.hpp b/src/NVMeIntf.hpp
-index e9060ab..e7ba3cf 100644
+index 7c639c6..e52a700 100644
 --- a/src/NVMeIntf.hpp
 +++ b/src/NVMeIntf.hpp
-@@ -125,6 +125,7 @@ class NVMeMiIntf : public NVMeIntf
+@@ -188,6 +188,7 @@ class NVMeMiIntf
       * @ctrl: controller to send the admin command to
       * @admin_req: request header
       * @data: request data payload
@@ -29,7 +29,7 @@
       * @resp_data_offset: offset into request data to retrieve from controller
       * @cb: callback function after the response received.
       * @ec: error code
-@@ -150,7 +151,7 @@ class NVMeMiIntf : public NVMeIntf
+@@ -213,7 +214,7 @@ class NVMeMiIntf
       */
      virtual void
          adminXfer(nvme_mi_ctrl_t ctrl, const nvme_mi_admin_req_hdr& admin_req,
diff --git a/recipes-phosphor/sensors/dbus-sensors/0021-nvmesensor-segmentation-fault-workaround-and-fix.patch b/recipes-phosphor/sensors/dbus-sensors/0022-nvmesensor-segmentation-fault-workaround-and-fix.patch
similarity index 89%
rename from recipes-phosphor/sensors/dbus-sensors/0021-nvmesensor-segmentation-fault-workaround-and-fix.patch
rename to recipes-phosphor/sensors/dbus-sensors/0022-nvmesensor-segmentation-fault-workaround-and-fix.patch
index 6b3c8fc..0abb888 100644
--- a/recipes-phosphor/sensors/dbus-sensors/0021-nvmesensor-segmentation-fault-workaround-and-fix.patch
+++ b/recipes-phosphor/sensors/dbus-sensors/0022-nvmesensor-segmentation-fault-workaround-and-fix.patch
@@ -1,7 +1,7 @@
-From d02dacecc061ae16457943969885ac677034f294 Mon Sep 17 00:00:00 2001
+From d4a8352c714a94a821928c98063582b881cb6e3e Mon Sep 17 00:00:00 2001
 From: Jinliang Wang <jinliangw@google.com>
 Date: Fri, 20 Jan 2023 11:53:54 -0800
-Subject: [PATCH 21/34] nvmesensor: segmentation fault workaround and fix
+Subject: [PATCH 22/44] nvmesensor: segmentation fault workaround and fix
 
 1) undefine BOOST_ASIO_DISABLE_THREADS and BOOST_ASIO_HAS_IO_URING
    to work around segmentation fault during boost asio mutithreading
@@ -13,8 +13,8 @@
 Change-Id: I25695cb0c2ac0e76b493ed48235370e041287b96
 ---
  src/NVMeMi.cpp  | 24 ++++++++++++------------
- src/meson.build |  3 ++-
- 2 files changed, 14 insertions(+), 13 deletions(-)
+ src/meson.build |  2 +-
+ 2 files changed, 13 insertions(+), 13 deletions(-)
 
 diff --git a/src/NVMeMi.cpp b/src/NVMeMi.cpp
 index c8579a7..dd083b2 100644
@@ -87,16 +87,15 @@
                  });
                  return;
 diff --git a/src/meson.build b/src/meson.build
-index 5214ebd..64200f3 100644
+index 6d822d5..7d16d17 100644
 --- a/src/meson.build
 +++ b/src/meson.build
-@@ -223,7 +223,8 @@ if get_option('nvme').enabled()
+@@ -223,7 +223,7 @@ if get_option('nvme').enabled()
          'nvmesensor',
          sources: nvme_srcs,
          dependencies: nvme_deps,
--        cpp_args: [uring_args, '-frtti', '-UBOOST_ASIO_NO_DEPRECATED'], 
-+        cpp_args: [uring_args, '-frtti', '-UBOOST_ASIO_NO_DEPRECATED',
-+            '-UBOOST_ASIO_DISABLE_THREADS', '-UBOOST_ASIO_HAS_IO_URING'], 
+-        cpp_args: [uring_args, '-UBOOST_ASIO_NO_DEPRECATED'], 
++        cpp_args: uring_args + ['-frtti', '-UBOOST_ASIO_NO_DEPRECATED', '-UBOOST_ASIO_DISABLE_THREADS', '-UBOOST_ASIO_HAS_IO_URING'],
          install: true,
      )
  endif
diff --git a/recipes-phosphor/sensors/dbus-sensors/0022-nvmesensor-spin-out-the-worker-for-NVMeMi.patch b/recipes-phosphor/sensors/dbus-sensors/0023-nvmesensor-spin-out-the-worker-for-NVMeMi.patch
similarity index 97%
rename from recipes-phosphor/sensors/dbus-sensors/0022-nvmesensor-spin-out-the-worker-for-NVMeMi.patch
rename to recipes-phosphor/sensors/dbus-sensors/0023-nvmesensor-spin-out-the-worker-for-NVMeMi.patch
index 4820b49..2865dd5 100644
--- a/recipes-phosphor/sensors/dbus-sensors/0022-nvmesensor-spin-out-the-worker-for-NVMeMi.patch
+++ b/recipes-phosphor/sensors/dbus-sensors/0023-nvmesensor-spin-out-the-worker-for-NVMeMi.patch
@@ -1,7 +1,7 @@
-From 5e01aaf1ac207acd3c18020d256d690e18bb18ba Mon Sep 17 00:00:00 2001
+From 5eefe6af535cde59d619ece9f31b88c9c541e497 Mon Sep 17 00:00:00 2001
 From: Hao Jiang <jianghao@google.com>
 Date: Mon, 12 Dec 2022 22:51:29 +0000
-Subject: [PATCH 22/34] nvmesensor: spin out the worker for NVMeMi
+Subject: [PATCH 23/44] nvmesensor: spin out the worker for NVMeMi
 
 Created the worker class for NVMeMi. The MCTP endpoint under the same
 i2c bus will share the same worker thread, similar to NVMeBasic.
@@ -22,7 +22,7 @@
  3 files changed, 63 insertions(+), 41 deletions(-)
 
 diff --git a/src/NVMeBasic.cpp b/src/NVMeBasic.cpp
-index b2cb44e..2211088 100644
+index 8390452..3a2d4ca 100644
 --- a/src/NVMeBasic.cpp
 +++ b/src/NVMeBasic.cpp
 @@ -1,5 +1,7 @@
diff --git a/recipes-phosphor/sensors/dbus-sensors/0023-nvmesensor-delay-subsystem-creation.patch b/recipes-phosphor/sensors/dbus-sensors/0024-nvmesensor-delay-subsystem-creation.patch
similarity index 81%
rename from recipes-phosphor/sensors/dbus-sensors/0023-nvmesensor-delay-subsystem-creation.patch
rename to recipes-phosphor/sensors/dbus-sensors/0024-nvmesensor-delay-subsystem-creation.patch
index a0632dd..d2ff1a5 100644
--- a/recipes-phosphor/sensors/dbus-sensors/0023-nvmesensor-delay-subsystem-creation.patch
+++ b/recipes-phosphor/sensors/dbus-sensors/0024-nvmesensor-delay-subsystem-creation.patch
@@ -1,7 +1,7 @@
-From 8fc4305b3d5188170615c7fbbf05704e021b8d17 Mon Sep 17 00:00:00 2001
+From ce2cae25e12c935c637c9f4c0bd426145c99be6f Mon Sep 17 00:00:00 2001
 From: Hao Jiang <jianghao@google.com>
 Date: Thu, 23 Feb 2023 00:55:46 +0000
-Subject: [PATCH 23/34] nvmesensor: delay subsystem creation
+Subject: [PATCH 24/44] nvmesensor: delay subsystem creation
 
 Put the subsystem creation into a second loop. So all NVMeIntf should be
 initiated ahead of the subsystem. It is due to the timing requirement
@@ -10,11 +10,11 @@
 Signed-off-by: Hao Jiang <jianghao@google.com>
 Change-Id: Ia210c143722259486bbd6d23be6eec63438060b4
 ---
- src/NVMeSensorMain.cpp | 63 ++++++++++++++++++++++++++++++++----------
- 1 file changed, 48 insertions(+), 15 deletions(-)
+ src/NVMeSensorMain.cpp | 61 +++++++++++++++++++++++++++++++++---------
+ 1 file changed, 48 insertions(+), 13 deletions(-)
 
 diff --git a/src/NVMeSensorMain.cpp b/src/NVMeSensorMain.cpp
-index 0656d2c..3f8797f 100644
+index 225b05e..3c64eee 100644
 --- a/src/NVMeSensorMain.cpp
 +++ b/src/NVMeSensorMain.cpp
 @@ -96,7 +96,15 @@ static void handleConfigurations(
@@ -30,21 +30,20 @@
 +     * short and should not be delayed by NVMe-MI protocol msg from NVMe
 +     * subsystem.
 +     */
-+    std::map<std::string, std::shared_ptr<NVMeIntf>> nvmeInterfaces;
++    std::map<std::string, NVMeIntf> nvmeInterfaces;
      for (const auto& [interfacePath, configData] : nvmeConfigurations)
      {
          // find base configuration
-@@ -137,16 +145,11 @@ static void handleConfigurations(
-             {
-                 std::shared_ptr<NVMeIntf> nvmeBasic{
-                     new NVMeBasic(io, *busNumber, *address)};
--
+@@ -139,15 +147,11 @@ static void handleConfigurations(
+                 NVMeIntf nvmeBasic =
+                     NVMeIntf::create<NVMeBasic>(io, *busNumber, *address);
+ 
 -                auto nvmeSubsys = std::make_shared<NVMeSubsystem>(
 -                    io, objectServer, dbusConnection, interfacePath,
--                    *sensorName, nvmeBasic);
+-                    *sensorName, std::move(nvmeBasic));
 -                nvmeSubsysMap.emplace(interfacePath, nvmeSubsys);
 -                nvmeSubsys->start(configData);
-+                nvmeInterfaces.emplace(interfacePath, nvmeBasic);
++                nvmeInterfaces.emplace(interfacePath, std::move(nvmeBasic));
              }
              catch (std::exception& ex)
              {
@@ -53,14 +52,13 @@
                            << std::string(interfacePath) << ": " << ex.what()
                            << "\n";
                  continue;
-@@ -164,22 +167,52 @@ static void handleConfigurations(
-                 std::shared_ptr<NVMeIntf> nvmeMi{new NVMeMi(
+@@ -166,21 +170,52 @@ static void handleConfigurations(
                      io, dynamic_cast<sdbusplus::bus_t&>(*dbusConnection),
-                     *busNumber, *address)};
--
+                     *busNumber, *address);
+ 
 -                auto nvmeSubsys = std::make_shared<NVMeSubsystem>(
 -                    io, objectServer, dbusConnection, interfacePath,
--                    *sensorName, nvmeMi);
+-                    *sensorName, std::move(nvmeMi));
 -                nvmeSubsysMap.emplace(interfacePath, nvmeSubsys);
 -                nvmeSubsys->start(configData);
 +                nvmeInterfaces.emplace(interfacePath, nvmeMi);
@@ -75,7 +73,7 @@
              }
          }
      }
-+    
++
 +    for (const auto& [interfacePath, configData] : nvmeConfigurations)
 +    {
 +        // find base configuration
diff --git a/recipes-phosphor/sensors/dbus-sensors/0024-nvmesensor-clean-up-the-association.patch b/recipes-phosphor/sensors/dbus-sensors/0025-nvmesensor-clean-up-the-association.patch
similarity index 93%
rename from recipes-phosphor/sensors/dbus-sensors/0024-nvmesensor-clean-up-the-association.patch
rename to recipes-phosphor/sensors/dbus-sensors/0025-nvmesensor-clean-up-the-association.patch
index 599e1b6..be4a538 100644
--- a/recipes-phosphor/sensors/dbus-sensors/0024-nvmesensor-clean-up-the-association.patch
+++ b/recipes-phosphor/sensors/dbus-sensors/0025-nvmesensor-clean-up-the-association.patch
@@ -1,7 +1,7 @@
-From c6460b06d61e06229061d85041424b7f280594a8 Mon Sep 17 00:00:00 2001
+From d0a812c56d664feb1166c81ed64655c856fb5884 Mon Sep 17 00:00:00 2001
 From: Hao Jiang <jianghao@google.com>
 Date: Fri, 31 Mar 2023 17:32:34 +0000
-Subject: [PATCH 24/34] nvmesensor: clean up the association
+Subject: [PATCH 25/44] nvmesensor: clean up the association
 
 The association now belonges to the child component:
 * subsystem: association to chassis and  between storage/drive
@@ -139,7 +139,7 @@
      // NVMe Plug-in for vendor defined command/field
      std::weak_ptr<NVMeControllerPlugin> plugin;
 diff --git a/src/NVMeSubsys.cpp b/src/NVMeSubsys.cpp
-index 90bad81..6fd78b0 100644
+index e45db4f..68c3bac 100644
 --- a/src/NVMeSubsys.cpp
 +++ b/src/NVMeSubsys.cpp
 @@ -7,21 +7,18 @@
@@ -174,7 +174,7 @@
  }
  
  // get temporature from a NVMe Basic reading.
-@@ -67,10 +64,12 @@ NVMeSubsystem::NVMeSubsystem(boost::asio::io_context& io,
+@@ -73,10 +70,12 @@ NVMeSubsystem::NVMeSubsystem(boost::asio::io_context& asio,
  
      /* xyz.openbmc_project.Inventory.Item.Storage */
      // make association for Drive/Storage/Chassis
@@ -191,7 +191,7 @@
  }
  
  void NVMeSubsystem::start(const SensorData& configData)
-@@ -87,7 +86,7 @@ void NVMeSubsystem::start(const SensorData& configData)
+@@ -95,7 +94,7 @@ void NVMeSubsystem::start(const SensorData& configData)
                  // TODO: mark the subsystem invalid and reschedule refresh
                  std::cerr << "fail to scan controllers for the nvme subsystem"
                            << (ec ? ": " + ec.message() : "") << std::endl;
@@ -200,7 +200,7 @@
                  return;
              }
  
-@@ -121,6 +120,9 @@ void NVMeSubsystem::start(const SensorData& configData)
+@@ -129,6 +128,9 @@ void NVMeSubsystem::start(const SensorData& configData)
                          {*index, {nvmeController, {}}});
                      auto& ctrlPlugin = iter->second.second;
  
@@ -210,7 +210,7 @@
                      // creat controller plugin
                      if (self->plugin)
                      {
-@@ -128,10 +130,6 @@ void NVMeSubsystem::start(const SensorData& configData)
+@@ -136,10 +138,6 @@ void NVMeSubsystem::start(const SensorData& configData)
                              *nvmeController, configData);
                      }
                      nvmeController->start(ctrlPlugin);
@@ -221,7 +221,7 @@
                  }
                  catch (const std::exception& e)
                  {
-@@ -142,7 +140,7 @@ void NVMeSubsystem::start(const SensorData& configData)
+@@ -150,7 +148,7 @@ void NVMeSubsystem::start(const SensorData& configData)
  
                  index++;
              }
@@ -231,12 +231,12 @@
              /*
              find primary controller and make association
 diff --git a/src/NVMeSubsys.hpp b/src/NVMeSubsys.hpp
-index 8af2497..0c95318 100644
+index ced3dc1..3944fe7 100644
 --- a/src/NVMeSubsys.hpp
 +++ b/src/NVMeSubsys.hpp
 @@ -21,6 +21,8 @@ class NVMeSubsystem : public std::enable_shared_from_this<NVMeSubsystem>
                    const std::string& path, const std::string& name,
-                   const std::shared_ptr<NVMeIntf>& intf);
+                   NVMeIntf intf);
  
 +    ~NVMeSubsystem();
 +
diff --git a/recipes-phosphor/sensors/dbus-sensors/0025-nvmesensor-change-the-controller-init-sequence.patch b/recipes-phosphor/sensors/dbus-sensors/0026-nvmesensor-change-the-controller-init-sequence.patch
similarity index 87%
rename from recipes-phosphor/sensors/dbus-sensors/0025-nvmesensor-change-the-controller-init-sequence.patch
rename to recipes-phosphor/sensors/dbus-sensors/0026-nvmesensor-change-the-controller-init-sequence.patch
index c98b4f9..f39a782 100644
--- a/recipes-phosphor/sensors/dbus-sensors/0025-nvmesensor-change-the-controller-init-sequence.patch
+++ b/recipes-phosphor/sensors/dbus-sensors/0026-nvmesensor-change-the-controller-init-sequence.patch
@@ -1,7 +1,7 @@
-From b0cdd9da9ad7ecc2379c4c79aeb66dac07030e14 Mon Sep 17 00:00:00 2001
+From 56508685439014c303fbc38264465814dc6c6fb6 Mon Sep 17 00:00:00 2001
 From: Hao Jiang <jianghao@google.com>
 Date: Thu, 6 Apr 2023 23:49:17 +0000
-Subject: [PATCH 25/34] nvmesensor: change the controller init sequence
+Subject: [PATCH 26/44] nvmesensor: change the controller init sequence
 
 The start should be the last step for controller initialization process.
 Since the start may depend on the info from intialization.
@@ -13,10 +13,10 @@
  1 file changed, 12 insertions(+), 7 deletions(-)
 
 diff --git a/src/NVMeSubsys.cpp b/src/NVMeSubsys.cpp
-index 6fd78b0..42d9717 100644
+index 68c3bac..90db473 100644
 --- a/src/NVMeSubsys.cpp
 +++ b/src/NVMeSubsys.cpp
-@@ -108,7 +108,7 @@ void NVMeSubsystem::start(const SensorData& configData)
+@@ -116,7 +116,7 @@ void NVMeSubsystem::start(const SensorData& configData)
                  std::filesystem::path path = std::filesystem::path(self->path) /
                                               "controllers" /
                                               std::to_string(*index);
@@ -25,7 +25,7 @@
                  try
                  {
                      auto nvmeController = std::make_shared<NVMeController>(
-@@ -118,18 +118,17 @@ void NVMeSubsystem::start(const SensorData& configData)
+@@ -126,18 +126,17 @@ void NVMeSubsystem::start(const SensorData& configData)
                      // insert the controllers with empty plugin
                      auto [iter, _] = self->controllers.insert(
                          {*index, {nvmeController, {}}});
@@ -49,7 +49,7 @@
                  }
                  catch (const std::exception& e)
                  {
-@@ -202,6 +201,12 @@ void NVMeSubsystem::start(const SensorData& configData)
+@@ -210,6 +209,12 @@ void NVMeSubsystem::start(const SensorData& configData)
                      secCntrls.push_back(findSecondary->second.first);
                  }
                  findPrimary->second.first->setSecAssoc(secCntrls);
diff --git a/recipes-phosphor/sensors/dbus-sensors/0026-nvmesensor-Check-SCS-for-secondary-controllers.patch b/recipes-phosphor/sensors/dbus-sensors/0027-nvmesensor-Check-SCS-for-secondary-controllers.patch
similarity index 97%
rename from recipes-phosphor/sensors/dbus-sensors/0026-nvmesensor-Check-SCS-for-secondary-controllers.patch
rename to recipes-phosphor/sensors/dbus-sensors/0027-nvmesensor-Check-SCS-for-secondary-controllers.patch
index 1736075..fdf9d8d 100644
--- a/recipes-phosphor/sensors/dbus-sensors/0026-nvmesensor-Check-SCS-for-secondary-controllers.patch
+++ b/recipes-phosphor/sensors/dbus-sensors/0027-nvmesensor-Check-SCS-for-secondary-controllers.patch
@@ -1,7 +1,7 @@
-From 0d458922aa59bed85f2dbfe10db4f4d0d380ae57 Mon Sep 17 00:00:00 2001
+From c76bd9e41b07253efa28e034cce42d4d48b756ba Mon Sep 17 00:00:00 2001
 From: Hao Jiang <jianghao@google.com>
 Date: Fri, 7 Apr 2023 23:51:14 +0000
-Subject: [PATCH 26/34] nvmesensor: Check SCS for secondary controllers
+Subject: [PATCH 27/44] nvmesensor: Check SCS for secondary controllers
 
 The Secondary Controller State indicates if the secondary controller has
 been enabled by CC.EN. For disabled controllers, the StorageController
@@ -285,10 +285,10 @@
  
      /** @brief Implementation for GetLogPage
 diff --git a/src/NVMeSubsys.cpp b/src/NVMeSubsys.cpp
-index 42d9717..e9ad699 100644
+index 90db473..aebfd14 100644
 --- a/src/NVMeSubsys.cpp
 +++ b/src/NVMeSubsys.cpp
-@@ -186,6 +186,12 @@ void NVMeSubsystem::start(const SensorData& configData)
+@@ -194,6 +194,12 @@ void NVMeSubsystem::start(const SensorData& configData)
                                << std::endl;
                      return;
                  }
@@ -301,7 +301,7 @@
                  std::vector<std::shared_ptr<NVMeController>> secCntrls;
                  for (int i = 0; i < listHdr.num; i++)
                  {
-@@ -198,9 +204,18 @@ void NVMeSubsystem::start(const SensorData& configData)
+@@ -206,9 +212,18 @@ void NVMeSubsystem::start(const SensorData& configData)
                                    << std::endl;
                          break;
                      }
diff --git a/recipes-phosphor/sensors/dbus-sensors/0033-nvmesensor-Add-toggle-for-single-thread-mode.patch b/recipes-phosphor/sensors/dbus-sensors/0028-nvmesensor-Add-toggle-for-single-thread-mode.patch
similarity index 85%
rename from recipes-phosphor/sensors/dbus-sensors/0033-nvmesensor-Add-toggle-for-single-thread-mode.patch
rename to recipes-phosphor/sensors/dbus-sensors/0028-nvmesensor-Add-toggle-for-single-thread-mode.patch
index 4b5a16d..17a936b 100644
--- a/recipes-phosphor/sensors/dbus-sensors/0033-nvmesensor-Add-toggle-for-single-thread-mode.patch
+++ b/recipes-phosphor/sensors/dbus-sensors/0028-nvmesensor-Add-toggle-for-single-thread-mode.patch
@@ -1,7 +1,7 @@
-From b2254dc06d0c96b3783d4620dd6c14a3e6dad973 Mon Sep 17 00:00:00 2001
+From 0d9a0a81943f30f7d4df23ef9090a6eb2560c98b Mon Sep 17 00:00:00 2001
 From: Hao Jiang <jianghao@google.com>
 Date: Wed, 19 Apr 2023 17:26:09 +0000
-Subject: [PATCH 33/34] nvmesensor: Add toggle for single thread mode
+Subject: [PATCH 28/44] nvmesensor: Add toggle for single thread mode
 
 Add a flag to NVMeMi class to turn off the single thread mode. The
 single thread mode is to aggregate the communicate to the devices under
@@ -20,11 +20,11 @@
  2 files changed, 24 insertions(+), 17 deletions(-)
 
 diff --git a/src/NVMeMi.cpp b/src/NVMeMi.cpp
-index 106413d..f0a2dcf 100644
+index 59ba3a5..28d864e 100644
 --- a/src/NVMeMi.cpp
 +++ b/src/NVMeMi.cpp
-@@ -16,7 +16,7 @@ constexpr size_t maxNVMeMILength = 4096;
- constexpr int tcgDefaultTimeoutMS = 20*1000;
+@@ -13,7 +13,7 @@ std::map<int, std::weak_ptr<NVMeMi::Worker>> NVMeMi::workerMap{};
+ nvme_root_t NVMeMi::nvmeRoot = nvme_mi_create_root(stderr, DEFAULT_LOGLEVEL);
  
  NVMeMi::NVMeMi(boost::asio::io_context& io, sdbusplus::bus_t& dbus, int bus,
 -               int addr) :
@@ -32,7 +32,7 @@
      io(io),
      dbus(dbus)
  {
-@@ -25,11 +25,30 @@ NVMeMi::NVMeMi(boost::asio::io_context& io, sdbusplus::bus_t& dbus, int bus,
+@@ -22,11 +22,30 @@ NVMeMi::NVMeMi(boost::asio::io_context& io, sdbusplus::bus_t& dbus, int bus,
          throw std::runtime_error("invalid NVMe root");
      }
  
@@ -66,7 +66,7 @@
      }
  
      // init mctp ep via mctpd
-@@ -73,18 +92,6 @@ NVMeMi::NVMeMi(boost::asio::io_context& io, sdbusplus::bus_t& dbus, int bus,
+@@ -70,18 +89,6 @@ NVMeMi::NVMeMi(boost::asio::io_context& io, sdbusplus::bus_t& dbus, int bus,
                                   std::to_string(nid) + ":" +
                                   std::to_string(eid));
      }
@@ -86,7 +86,7 @@
  
  NVMeMi::Worker::Worker()
 diff --git a/src/NVMeMi.hpp b/src/NVMeMi.hpp
-index 6a57b6e..2aa6636 100644
+index 3071596..4f1a483 100644
 --- a/src/NVMeMi.hpp
 +++ b/src/NVMeMi.hpp
 @@ -9,7 +9,7 @@ class NVMeMi : public NVMeMiIntf, public std::enable_shared_from_this<NVMeMi>
diff --git a/recipes-phosphor/sensors/dbus-sensors/0029-nvmesensor-Add-Fake-for-NVMeMi.patch b/recipes-phosphor/sensors/dbus-sensors/0029-nvmesensor-Add-Fake-for-NVMeMi.patch
new file mode 100644
index 0000000..504729a
--- /dev/null
+++ b/recipes-phosphor/sensors/dbus-sensors/0029-nvmesensor-Add-Fake-for-NVMeMi.patch
@@ -0,0 +1,470 @@
+From b6689d0733b09d1f7bef9bb673ffa27b43bd88f4 Mon Sep 17 00:00:00 2001
+From: Hao Jiang <jianghao@google.com>
+Date: Wed, 19 Apr 2023 21:30:59 +0000
+Subject: [PATCH 29/44] nvmesensor: Add Fake for NVMeMi
+
+Adding a fake class for NVMeMi implementation. The fake class will
+return predefined nvme response with a 100ms designated delay to mimic
+the transfer and processing time.
+
+Signed-off-by: Hao Jiang <jianghao@google.com>
+Change-Id: I83fb444890cf9faa7006c6cb0d645b9075c8856b
+---
+ src/NVMeMiFake.hpp | 445 +++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 445 insertions(+)
+ create mode 100644 src/NVMeMiFake.hpp
+
+diff --git a/src/NVMeMiFake.hpp b/src/NVMeMiFake.hpp
+new file mode 100644
+index 0000000..05eafed
+--- /dev/null
++++ b/src/NVMeMiFake.hpp
+@@ -0,0 +1,445 @@
++#include "NVMeIntf.hpp"
++
++#include <boost/asio.hpp>
++#include <boost/endian.hpp>
++
++#include <iostream>
++#include <thread>
++
++struct list_node
++{
++    list_node *next, *prev;
++};
++
++struct nvme_mi_ctrl
++{
++    void* ep;
++    __u16 id;
++    list_node ep_entry;
++};
++
++class NVMeMiFake :
++    public NVMeMiIntf,
++    public std::enable_shared_from_this<NVMeMiFake>
++{
++  public:
++    NVMeMiFake(boost::asio::io_context& io) :
++        io(io), valid(true) /*, worker(workerIO.get_executor())*/
++    {
++
++        // start worker thread
++        workerStop = false;
++        thread = std::thread([&io = workerIO, &stop = workerStop,
++                              &mtx = workerMtx, &cv = workerCv]() {
++            std::cerr << "NVMeMiFake worker thread started: " << io.stopped()
++                      << std::endl;
++            // With BOOST_ASIO_DISABLE_THREADS, boost::asio::executor_work_guard
++            // issues null_event across the thread, which caused invalid
++            // invokation. We implement a simple invoke machenism based
++            // std::condition_variable.
++            io.stop();
++            io.restart();
++            while (1)
++            {
++                // mimik the communication delay.
++                std::this_thread::sleep_for(std::chrono::milliseconds(100));
++                io.run();
++                io.restart();
++                std::cerr << "job done" << std::endl;
++                {
++                    std::unique_lock<std::mutex> lock(mtx);
++                    cv.wait(lock);
++                    if (stop)
++                    {
++                        // exhaust all tasks and exit
++                        io.run();
++                        break;
++                    }
++                }
++            }
++            std::cerr << "NVMeMi worker this line should not be reached"
++                      << std::endl;
++        });
++
++        std::cerr << "NVMeMiFake constructor" << std::endl;
++    }
++
++    ~NVMeMiFake() override
++    {
++        if (valid)
++        {
++            std::cerr << "NVMeMiFake destroyer" << std::endl;
++        }
++        else
++        {
++            std::cerr << "NVMeMiFake default destroyer" << std::endl;
++        }
++
++        // close worker
++        workerStop = true;
++        {
++            std::unique_lock<std::mutex> lock(workerMtx);
++            workerCv.notify_all();
++        }
++        thread.join();
++    }
++
++    int getNID() const override
++    {
++        return 0;
++    }
++
++    int getEID() const override
++    {
++        return 0;
++    }
++
++    void miSubsystemHealthStatusPoll(
++        std::function<void(const std::error_code&,
++                           nvme_mi_nvm_ss_health_status*)>&& cb) override
++    {
++        io.post([cb{std::move(cb)}]() {
++            nvme_mi_nvm_ss_health_status status;
++            status.nss = 1 << 5;
++            status.ctemp = 24;
++            cb({}, &status);
++        });
++    }
++
++    void miScanCtrl(std::function<void(const std::error_code&,
++                                       const std::vector<nvme_mi_ctrl_t>&)>
++                        cb) override
++    {
++        if (workerStop)
++        {
++
++            std::cerr << "worker thread for nvme endpoint is stopped"
++                      << std::endl;
++
++            io.post([cb{std::move(cb)}]() {
++                cb(std::make_error_code(std::errc::no_such_device), {});
++            });
++            return;
++        }
++
++        post([self{shared_from_this()}, cb = std::move(cb)] {
++            std::cerr << "libnvme: scan" << std::endl;
++            self->io.post([cb{std::move(cb)}]() {
++                auto ctrl1 = new nvme_mi_ctrl;
++                auto ctrl2 = new nvme_mi_ctrl;
++                auto ctrl3 = new nvme_mi_ctrl;
++                ctrl1->id = 0;
++                ctrl2->id = 1;
++                ctrl3->id = 2;
++                std::vector<nvme_mi_ctrl_t> list{ctrl1, ctrl2, ctrl3};
++                cb({}, list);
++            });
++        });
++    }
++
++    void adminIdentify(
++        nvme_mi_ctrl_t, nvme_identify_cns cns, uint32_t, uint16_t,
++        std::function<void(const std::error_code&, std::span<uint8_t>)>&& cb)
++        override
++    {
++        std::cerr << "identify" << std::endl;
++        post([self{shared_from_this()}, cb = std::move(cb), cns]() {
++            std::cerr << "libnvme: identify" << std::endl;
++            self->io.post([cb{std::move(cb)}, cns]() {
++                std::vector<uint8_t> data;
++                switch (cns)
++                {
++                    case NVME_IDENTIFY_CNS_SECONDARY_CTRL_LIST:
++                    {
++                        nvme_secondary_ctrl_list list;
++                        list.num = 2;
++                        list.sc_entry[0].pcid = 0;
++                        list.sc_entry[0].scid = 1;
++                        list.sc_entry[0].scs = 1;
++                        list.sc_entry[1].pcid = 0;
++                        list.sc_entry[1].scid = 2;
++                        list.sc_entry[1].scs = 0;
++                        data.resize(sizeof(nvme_secondary_ctrl_list));
++                        memcpy(data.data(), &list, data.size());
++                        break;
++                    }
++                    default:
++                        data.resize(NVME_IDENTIFY_DATA_SIZE);
++                }
++                cb({}, std::span<uint8_t>(data.begin(), data.size()));
++            });
++        });
++    }
++
++    void adminGetLogPage(nvme_mi_ctrl_t ctrl, nvme_cmd_get_log_lid lid,
++                         uint32_t nsid, uint8_t lsp, uint16_t lsi,
++                         std::function<void(const std::error_code&,
++                                            std::span<uint8_t>)>&& cb) override
++    {
++        try
++        {
++            post([ctrl, lid, nsid, lsp, lsi, self{shared_from_this()},
++                  cb{std::move(cb)}]() {
++                int rc = 0;
++                std::vector<uint8_t> data;
++                try
++                {
++                    switch (lid)
++                    {
++                        case NVME_LOG_LID_TELEMETRY_HOST:
++                        {
++                            data.resize(sizeof(nvme_telemetry_log));
++                            nvme_telemetry_log& log =
++                                *reinterpret_cast<nvme_telemetry_log*>(
++                                    data.data());
++                            if (lsp == NVME_LOG_TELEM_HOST_LSP_CREATE)
++                            {
++                                log.lpi = 0x07;
++                                if (rc)
++                                {
++                                    std::cerr
++                                        << "failed to create telemetry host log"
++                                        << std::endl;
++                                    throw std::system_error(
++                                        errno, std::generic_category());
++                                }
++                            }
++                            else if (lsp == NVME_LOG_TELEM_HOST_LSP_RETAIN)
++                            {
++                                // nvme rev 1.3 only applies upto Area 3
++                                log.dalb1 = 512;
++                                log.dalb2 = 512;
++                                log.dalb3 = 512;
++                                data.resize(sizeof(nvme_telemetry_log) + 512);
++                                std::string str = "hello world";
++                                for (std::size_t i = 0; i < str.size(); i++)
++                                {
++                                    data[sizeof(nvme_telemetry_log) + i] =
++                                        static_cast<uint8_t>(str[i]);
++                                }
++
++                                if (rc)
++                                {
++                                    std::cerr
++                                        << "failed to retain telemetry host "
++                                           "log full log"
++                                        << std::endl;
++                                    throw std::system_error(
++                                        errno, std::generic_category());
++                                }
++                            }
++                            else
++                            {
++                                throw std::system_error(std::make_error_code(
++                                    std::errc::invalid_argument));
++                            }
++                        }
++                        break;
++                        case NVME_LOG_LID_SMART:
++                        {
++                            data.resize(sizeof(nvme_smart_log));
++                            nvme_smart_log& log =
++                                *reinterpret_cast<nvme_smart_log*>(data.data());
++                            uint8_t temp = 40;
++                            log.temperature[0] = temp;
++                            log.temperature[1] = 1;
++                            break;
++                        }
++
++                        default:
++                        {
++                            std::cerr << "unknown lid for GetLogPage"
++                                      << std::endl;
++                            throw std::system_error(std::make_error_code(
++                                std::errc::invalid_argument));
++                        }
++                    }
++                }
++                catch (const ::std::system_error& e)
++                {
++                    self->io.post(
++                        [cb{std::move(cb)}, ec = e.code()]() { cb(ec, {}); });
++                }
++                self->io.post(
++                    [cb{std::move(cb)}, data{std::move(data)}]() mutable {
++                    std::span<uint8_t> span{data.data(), data.size()};
++                    cb({}, span);
++                });
++            });
++        }
++        catch (const std::runtime_error& e)
++        {
++            std::cerr << e.what() << std::endl;
++            io.post([cb{std::move(cb)}]() {
++                cb(std::make_error_code(std::errc::no_such_device), {});
++            });
++            return;
++        }
++    }
++    void adminFwCommit(
++        nvme_mi_ctrl_t ctrl, nvme_fw_commit_ca action, uint8_t slot, bool bpid,
++        std::function<void(const std::error_code&, nvme_status_field)>&& cb)
++        override
++    {
++        try
++        {
++            nvme_fw_commit_args args;
++            memset(&args, 0, sizeof(args));
++            args.action = action;
++            args.slot = slot;
++            args.bpid = bpid;
++            io.post([ctrl, args, cb{std::move(cb)},
++                     self{shared_from_this()}]() mutable {
++                // int rc = nvme_mi_admin_fw_commit(ctrl, &args);
++                int rc = 1;
++                if (rc < 0)
++                {
++
++                    std::cerr << "fail to subsystem_health_status_poll: "
++                              << std::strerror(errno) << std::endl;
++                    self->io.post([cb{std::move(cb)}]() {
++                        cb(std::make_error_code(static_cast<std::errc>(errno)),
++                           nvme_status_field::NVME_SC_MASK);
++                    });
++                    return;
++                }
++                else if (rc > 0)
++                {
++                    switch (rc & 0x7ff)
++                    {
++                        case NVME_SC_FW_NEEDS_CONV_RESET:
++                        case NVME_SC_FW_NEEDS_SUBSYS_RESET:
++                        case NVME_SC_FW_NEEDS_RESET:
++                            self->io.post([rc, cb{std::move(cb)}]() {
++                                cb({}, static_cast<nvme_status_field>(rc));
++                            });
++                            break;
++                        default:
++                            std::string_view errMsg = statusToString(
++                                static_cast<nvme_mi_resp_status>(rc));
++                            std::cerr
++                                << "fail to subsystem_health_status_poll: "
++                                << errMsg << std::endl;
++                            self->io.post([rc, cb{std::move(cb)}]() {
++                                cb(std::make_error_code(std::errc::bad_message),
++                                   static_cast<nvme_status_field>(rc));
++                            });
++                    }
++                    return;
++                }
++            });
++        }
++        catch (const std::runtime_error& e)
++        {
++            std::cerr << e.what() << std::endl;
++            io.post([cb{std::move(cb)}]() {
++                cb(std::make_error_code(std::errc::no_such_device),
++                   nvme_status_field::NVME_SC_MASK);
++            });
++            return;
++        }
++    }
++
++    void adminXfer(nvme_mi_ctrl_t ctrl, const nvme_mi_admin_req_hdr& admin_req,
++                   std::span<uint8_t> data, unsigned int /*timeout_ms*/,
++                   std::function<void(const std::error_code&,
++                                      const nvme_mi_admin_resp_hdr&,
++                                      std::span<uint8_t>)>&& cb) override
++    {
++        try
++        {
++            std::vector<uint8_t> req(sizeof(nvme_mi_admin_req_hdr) +
++                                     data.size());
++            memcpy(req.data(), &admin_req, sizeof(nvme_mi_admin_req_hdr));
++            memcpy(req.data() + sizeof(nvme_mi_admin_req_hdr), data.data(),
++                   data.size());
++            post([ctrl, req{std::move(req)}, self{shared_from_this()},
++                  cb{std::move(cb)}]() mutable {
++                int rc = 0;
++
++                nvme_mi_admin_req_hdr* reqHeader =
++                    reinterpret_cast<nvme_mi_admin_req_hdr*>(req.data());
++
++                size_t respDataSize =
++                    boost::endian::little_to_native<size_t>(reqHeader->dlen);
++                // off_t respDataOffset =
++                //     boost::endian::little_to_native<off_t>(reqHeader->doff);
++                size_t bufSize = sizeof(nvme_mi_admin_resp_hdr) + respDataSize;
++                std::vector<uint8_t> buf(bufSize);
++                nvme_mi_admin_resp_hdr* respHeader =
++                    reinterpret_cast<nvme_mi_admin_resp_hdr*>(buf.data());
++                (void)respHeader;
++                // rc = nvme_mi_admin_xfer(
++                //     ctrl, reqHeader, req.size() -
++                //     sizeof(nvme_mi_admin_req_hdr), respHeader,
++                //     respDataOffset, &respDataSize);
++
++                if (rc < 0)
++                {
++                    std::cerr << "failed to nvme_mi_admin_xfer" << std::endl;
++                    self->io.post([cb{std::move(cb)}]() {
++                        cb(std::make_error_code(static_cast<std::errc>(errno)),
++                           {}, {});
++                    });
++                    return;
++                }
++                // the MI interface will only consume protocol/io errors
++                // The client will take the reponsibility to deal with nvme-mi
++                // status flag and nvme status field(cwd3). cmd specific return
++                // value (cdw0) is also client's job.
++
++                buf.resize(sizeof(nvme_mi_admin_resp_hdr) + respDataSize);
++                self->io.post(
++                    [cb{std::move(cb)}, data{std::move(buf)}]() mutable {
++                    std::span<uint8_t> span(data.begin() +
++                                                sizeof(nvme_mi_admin_resp_hdr),
++                                            data.end());
++                    nvme_smart_log& log =
++                        *reinterpret_cast<nvme_smart_log*>(span.data());
++                    uint8_t temp = 40;
++                    log.temperature[0] = temp;
++                    log.temperature[1] = 1;
++                    cb({},
++                       *reinterpret_cast<nvme_mi_admin_resp_hdr*>(data.data()),
++                       span);
++                });
++            });
++        }
++        catch (const std::runtime_error& e)
++        {
++            std::cerr << e.what() << std::endl;
++            io.post([cb{std::move(cb)}]() {
++                cb(std::make_error_code(std::errc::no_such_device), {}, {});
++            });
++            return;
++        }
++    }
++
++  private:
++    boost::asio::io_context& io;
++    bool valid = false;
++
++    bool workerStop;
++    std::mutex workerMtx;
++    std::condition_variable workerCv;
++    boost::asio::io_context workerIO;
++    std::thread thread;
++
++    void post(std::function<void(void)>&& func);
++};
++
++void NVMeMiFake::post(std::function<void(void)>&& func)
++{
++    if (!workerStop)
++    {
++        std::unique_lock<std::mutex> lock(workerMtx);
++        if (!workerStop)
++        {
++            std::cerr << "do post" << std::endl;
++            workerIO.post(std::move(func));
++            workerCv.notify_all();
++            return;
++        }
++    }
++    throw std::runtime_error("NVMeMi has been stopped");
++}
+-- 
+2.34.1
+
diff --git a/recipes-phosphor/sensors/dbus-sensors/0030-nvmesensor-add-unit-test.patch b/recipes-phosphor/sensors/dbus-sensors/0030-nvmesensor-add-unit-test.patch
new file mode 100644
index 0000000..49d96fa
--- /dev/null
+++ b/recipes-phosphor/sensors/dbus-sensors/0030-nvmesensor-add-unit-test.patch
@@ -0,0 +1,125 @@
+From 6ca3e2940dc59ce0feac6372abaff71107d8d2d6 Mon Sep 17 00:00:00 2001
+From: Hao Jiang <jianghao@google.com>
+Date: Mon, 17 Apr 2023 21:28:48 +0000
+Subject: [PATCH 30/44] nvmesensor: add unit test
+
+Add a nvme_start_stop unit test for nvme subsystem. The unit test will
+check the correct number of storage controllers are created and
+destructed upon the start and stop.
+
+The test and nvme subsystem requires ObjectMapper to work.
+
+Signed-off-by: Hao Jiang <jianghao@google.com>
+Change-Id: I7e93020a9dc838708a0184df69da9da0da78f9df
+---
+ tests/meson.build      | 17 ++++++++++
+ tests/test_nvme_mi.cpp | 72 ++++++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 89 insertions(+)
+ create mode 100644 tests/test_nvme_mi.cpp
+
+diff --git a/tests/meson.build b/tests/meson.build
+index 28915d6..cc7fd53 100644
+--- a/tests/meson.build
++++ b/tests/meson.build
+@@ -36,3 +36,20 @@ test(
+         include_directories: '../src',
+     )
+ )
++
++if run_command('/usr/bin/bash', '-c', 'busctl tree xyz.openbmc_project.ObjectMapper').returncode() == 0
++    test(
++        'test_nvme',
++        executable(
++            'test_nvme',
++            'test_nvme_mi.cpp',
++            '../src/NVMeSensor.cpp',
++            '../src/NVMeSubsys.cpp',
++            '../src/NVMeController.cpp',
++            cpp_args: uring_args + ['-UBOOST_ASIO_NO_DEPRECATED', '-UBOOST_ASIO_DISABLE_THREADS', '-UBOOST_ASIO_HAS_IO_URING'],
++            dependencies: [ut_deps_list, nvme_deps],
++            implicit_include_directories: false,
++            include_directories: '../src',
++        )
++    )
++endif
+diff --git a/tests/test_nvme_mi.cpp b/tests/test_nvme_mi.cpp
+new file mode 100644
+index 0000000..f8e4a01
+--- /dev/null
++++ b/tests/test_nvme_mi.cpp
+@@ -0,0 +1,72 @@
++#include "NVMeMiFake.hpp"
++#include "NVMeSubsys.hpp"
++
++#include <sdbusplus/asio/connection.hpp>
++
++#include <gtest/gtest.h>
++
++class NVMeTest : public ::testing::Test
++{
++  protected:
++    NVMeTest() :
++        system_bus(std::make_shared<sdbusplus::asio::connection>(io)),
++        object_server(system_bus),
++        subsys(std::make_shared<NVMeSubsystem>(
++            io, object_server, system_bus, subsys_path, "NVMe_1",
++            std::move(NVMeIntf::create<NVMeMiFake>(io))))
++    {}
++    void SetUp() override
++    {
++        system_bus->request_name("xyz.openbmc_project.NVMeTest");
++        subsys->start(SensorData{});
++    }
++
++    static constexpr char subsys_path[] =
++        "/xyz/openbmc_project/inventory/Test_Chassis/Test_NVMe";
++
++    boost::asio::io_service io;
++    std::shared_ptr<sdbusplus::asio::connection> system_bus;
++    sdbusplus::asio::object_server object_server;
++
++    std::shared_ptr<NVMeSubsystem> subsys;
++};
++
++TEST_F(NVMeTest, TestSubsystemStartStop)
++{
++    boost::asio::steady_timer timer(io);
++
++    // wait for subsystem initialization
++    timer.expires_after(std::chrono::seconds(2));
++    timer.async_wait([&](boost::system::error_code) {
++        system_bus->async_method_call(
++            [&, this](boost::system::error_code, const GetSubTreeType& result) {
++            // Only PF and the enabled VF should be listed
++            EXPECT_EQ(result.size(), 2);
++            subsys->stop();
++
++            // wait for storage controller destruction.
++            timer.expires_after(std::chrono::seconds(1));
++            timer.async_wait([&](boost::system::error_code) {
++                system_bus->async_method_call(
++                    [&](boost::system::error_code,
++                        const GetSubTreeType& result) {
++                    // not storage controller should be listed.
++                    EXPECT_EQ(result.size(), 0);
++                    io.stop();
++                    },
++                    "xyz.openbmc_project.ObjectMapper",
++                    "/xyz/openbmc_project/object_mapper",
++                    "xyz.openbmc_project.ObjectMapper", "GetSubTree",
++                    subsys_path, 0,
++                    std::vector<std::string>{"xyz.openbmc_project.Inventory."
++                                             "Item.StorageController"});
++            });
++            },
++            "xyz.openbmc_project.ObjectMapper",
++            "/xyz/openbmc_project/object_mapper",
++            "xyz.openbmc_project.ObjectMapper", "GetSubTree", subsys_path, 0,
++            std::vector<std::string>{
++                "xyz.openbmc_project.Inventory.Item.StorageController"});
++    });
++    io.run();
++}
+-- 
+2.34.1
+
diff --git a/recipes-phosphor/sensors/dbus-sensors/0030-nvmesensor-always-create-host-telemetry-log-page.patch b/recipes-phosphor/sensors/dbus-sensors/0030-nvmesensor-always-create-host-telemetry-log-page.patch
deleted file mode 100644
index 3340496..0000000
--- a/recipes-phosphor/sensors/dbus-sensors/0030-nvmesensor-always-create-host-telemetry-log-page.patch
+++ /dev/null
@@ -1,93 +0,0 @@
-From 7a53e6ef38c992a8ebf5c38ad969430eb5aa2506 Mon Sep 17 00:00:00 2001
-From: Jinliang Wang <jinliangw@google.com>
-Date: Mon, 17 Apr 2023 13:49:16 -0700
-Subject: [PATCH 30/34] nvmesensor: always create host telemetry log page
-
-According to our use case, it's easier for client to get fresh
-host telemetry log page on every request. So we are going to deprecate
-the LSP paremeter in future, and always create host telemetry log.
-
-Change-Id: Ibeafe5f8f511363435b7c9947baaf2153ca12da4
-Signed-off-by: Jinliang Wang <jinliangw@google.com>
----
- src/NVMeMi.cpp | 48 ++++++++++++------------------------------------
- 1 file changed, 12 insertions(+), 36 deletions(-)
-
-diff --git a/src/NVMeMi.cpp b/src/NVMeMi.cpp
-index fabc0b5..8f73aed 100644
---- a/src/NVMeMi.cpp
-+++ b/src/NVMeMi.cpp
-@@ -386,7 +386,7 @@ static int nvme_mi_admin_get_log_telemetry_host_rae(nvme_mi_ctrl_t ctrl,
- 
- // Get Temetery Log header and return the size for hdr + data area (Area 1, 2,
- // 3, or maybe 4)
--int getTelemetryLog(nvme_mi_ctrl_t ctrl, bool host, bool create,
-+static int getTelemetryLog(nvme_mi_ctrl_t ctrl, bool host, bool create,
-                     std::vector<uint8_t>& data)
- {
-     int rc = 0;
-@@ -405,16 +405,16 @@ int getTelemetryLog(nvme_mi_ctrl_t ctrl, bool host, bool create,
-             std::cerr << "failed to create telemetry host log" << std::endl;
-             return rc;
-         }
--        return 0;
-     }
--
--    rc = func(ctrl, false, 0, sizeof(log), &log);
--
--    if (rc)
-+    else
-     {
--        std::cerr << "failed to retain telemetry log for "
--                  << (host ? "host" : "ctrl") << std::endl;
--        return rc;
-+        rc = func(ctrl, false, 0, sizeof(log), &log);
-+        if (rc)
-+        {
-+            std::cerr << "failed to retain telemetry log header for "
-+                    << (host ? "host" : "ctrl") << std::endl;
-+            return rc;
-+        }
-     }
- 
-     long size =
-@@ -551,33 +551,9 @@ void NVMeMi::adminGetLogPage(
-                 // fall through to NVME_LOG_LID_TELEMETRY_CTRL
-                 case NVME_LOG_LID_TELEMETRY_CTRL:
-                 {
--                    bool host = false;
--                    bool create = false;
--                    if (lid == NVME_LOG_LID_TELEMETRY_HOST)
--                    {
--                        host = true;
--                        if (lsp == NVME_LOG_TELEM_HOST_LSP_CREATE)
--                        {
--                            create = true;
--                        }
--                        else if (lsp == NVME_LOG_TELEM_HOST_LSP_RETAIN)
--                        {
--                            create = false;
--                        }
--                        else
--                        {
--                            std::cerr << "invalid lsp for telemetry host log"
--                                      << std::endl;
--                            rc = -1;
--                            errno = EINVAL;
--                            break;
--                        }
--                    }
--                    else
--                    {
--                        host = false;
--                    }
--
-+                    bool host = (lid == NVME_LOG_LID_TELEMETRY_HOST);
-+                    // We always create host telemetry regardless LSP
-+                    bool create = host && true;
-                     rc = getTelemetryLog(ctrl, host, create, data);
-                 }
-                 break;
--- 
-2.34.1
-
diff --git a/recipes-phosphor/sensors/dbus-sensors/0031-nvmesensor-enable-disable-subsys-according-to-DF.patch b/recipes-phosphor/sensors/dbus-sensors/0031-nvmesensor-enable-disable-subsys-according-to-DF.patch
new file mode 100644
index 0000000..0f633f3
--- /dev/null
+++ b/recipes-phosphor/sensors/dbus-sensors/0031-nvmesensor-enable-disable-subsys-according-to-DF.patch
@@ -0,0 +1,593 @@
+From 7c097551eee4daeb6b8b18e10a654a0ab0569de6 Mon Sep 17 00:00:00 2001
+From: Hao Jiang <jianghao@google.com>
+Date: Wed, 19 Apr 2023 00:59:40 +0000
+Subject: [PATCH 31/44] nvmesensor: enable/disable subsys according to DF
+
+NSHDS.NSS.DF(Drive Functional) bit will indicate the NVMe subsystem's
+working status. The NVMeSubsys class should change its functional status
+based on the status bit. It should also remove all the substructure
+(controllers/namspaces, etc) when subsystem is disabled.
+
+Add a mockup upon NVMeMiFake for unit test as well. The unit test mimik
+the process of DF disable and re-enable process.
+
+Signed-off-by: Hao Jiang <jianghao@google.com>
+Change-Id: I907c669eb6cc7f5eafe09c38712c4e681d9efc2d
+---
+ src/NVMeSensorMain.cpp |   4 +-
+ src/NVMeSubsys.cpp     | 112 +++++++++++++++----
+ src/NVMeSubsys.hpp     |  18 ++-
+ tests/meson.build      |   1 +
+ tests/test_nvme_mi.cpp | 245 +++++++++++++++++++++++++++++++++++++++--
+ 5 files changed, 346 insertions(+), 34 deletions(-)
+
+diff --git a/src/NVMeSensorMain.cpp b/src/NVMeSensorMain.cpp
+index 3c64eee..950988d 100644
+--- a/src/NVMeSensorMain.cpp
++++ b/src/NVMeSensorMain.cpp
+@@ -204,9 +204,9 @@ static void handleConfigurations(
+         {
+             auto nvmeSubsys = std::make_shared<NVMeSubsystem>(
+                 io, objectServer, dbusConnection, interfacePath, *sensorName,
+-                std::move(find->second));
++                configData, std::move(find->second));
+             nvmeSubsysMap.emplace(interfacePath, nvmeSubsys);
+-            nvmeSubsys->start(configData);
++            nvmeSubsys->start();
+         }
+         catch (std::exception& ex)
+         {
+diff --git a/src/NVMeSubsys.cpp b/src/NVMeSubsys.cpp
+index aebfd14..10b360d 100644
+--- a/src/NVMeSubsys.cpp
++++ b/src/NVMeSubsys.cpp
+@@ -39,10 +39,10 @@ NVMeSubsystem::NVMeSubsystem(boost::asio::io_context& asio,
+                              sdbusplus::asio::object_server& server,
+                              std::shared_ptr<sdbusplus::asio::connection> conn,
+                              const std::string& path, const std::string& name,
+-                             NVMeIntf intf) :
++                             const SensorData& configData, NVMeIntf intf) :
+     io(asio),
+-    objServer(server), conn(conn), path(path), name(name),
+-    nvmeIntf(std::move(intf)),
++    objServer(server), conn(conn), path(path), name(name), config(configData),
++    nvmeIntf(std::move(intf)), status(Status::Stop),
+     storage(*dynamic_cast<sdbusplus::bus_t*>(conn.get()), path.c_str()),
+     drive(*dynamic_cast<sdbusplus::bus_t*>(conn.get()), path.c_str())
+ {
+@@ -78,23 +78,50 @@ NVMeSubsystem::~NVMeSubsystem()
+     objServer.remove_interface(assocIntf);
+ }
+ 
+-void NVMeSubsystem::start(const SensorData& configData)
++void NVMeSubsystem::markFunctional(bool toggle)
+ {
++    // disable the subsystem
++    if (!toggle)
++    {
++        if (status == Status::Intiatilzing)
++        {
++            throw std::runtime_error(
++                "cannot stop: the subsystem is intiatilzing");
++        }
++        status = Status::Stop;
++        if (plugin)
++        {
++            plugin->stop();
++        }
++        // TODO: the controller should be stopped after controller level polling
++        // is enabled
++
++        controllers.clear();
++        plugin.reset();
++        return;
++    }
++    if (status == Status::Intiatilzing)
++    {
++        throw std::runtime_error("cannot start: the subsystem is intiatilzing");
++    }
++    status = Status::Intiatilzing;
++
+     // add controllers for the subsystem
+     if (nvmeIntf.getProtocol() == NVMeIntf::Protocol::NVMeMI)
+     {
+         auto nvme =
+             std::get<std::shared_ptr<NVMeMiIntf>>(nvmeIntf.getInferface());
+         nvme->miScanCtrl(
+-            [self{shared_from_this()}, nvme,
+-             configData](const std::error_code& ec,
+-                         const std::vector<nvme_mi_ctrl_t>& ctrlList) mutable {
++            [self{shared_from_this()},
++             nvme](const std::error_code& ec,
++                   const std::vector<nvme_mi_ctrl_t>& ctrlList) mutable {
+             if (ec || ctrlList.size() == 0)
+             {
+                 // TODO: mark the subsystem invalid and reschedule refresh
+                 std::cerr << "fail to scan controllers for the nvme subsystem"
+                           << (ec ? ": " + ec.message() : "") << std::endl;
+-                // self->createStorageAssociation();
++                self->status = Status::Stop;
++                self->markFunctional(false);
+                 return;
+             }
+ 
+@@ -132,7 +159,7 @@ void NVMeSubsystem::start(const SensorData& configData)
+                     {
+                         auto& ctrlPlugin = iter->second.second;
+                         ctrlPlugin = self->plugin->createControllerPlugin(
+-                            *nvmeController, configData);
++                            *nvmeController, self->config);
+                     }
+ 
+                     // set StorageController Association
+@@ -165,6 +192,8 @@ void NVMeSubsystem::start(const SensorData& configData)
+                 {
+                     std::cerr << "fail to identify secondary controller list"
+                               << std::endl;
++                    self->status = Status::Stop;
++                    self->markFunctional(false);
+                     return;
+                 }
+                 nvme_secondary_ctrl_list& listHdr =
+@@ -180,6 +209,8 @@ void NVMeSubsystem::start(const SensorData& configData)
+                 {
+                     std::cerr << "empty identify secondary controller list"
+                               << std::endl;
++                    self->status = Status::Stop;
++                    self->markFunctional(false);
+                     return;
+                 }
+ 
+@@ -192,6 +223,8 @@ void NVMeSubsystem::start(const SensorData& configData)
+                     std::cerr << "fail to match primary controller from "
+                                  "identify sencondary cntrl list"
+                               << std::endl;
++                    self->status = Status::Stop;
++                    self->markFunctional(false);
+                     return;
+                 }
+ 
+@@ -230,10 +263,21 @@ void NVMeSubsystem::start(const SensorData& configData)
+                 {
+                     pair.first->start(pair.second);
+                 }
++
++                // start plugin
++                if (self->plugin)
++                {
++                    self->plugin->start();
++                }
++
++                self->status = Status::Start;
+                 });
+         });
+     }
++}
+ 
++void NVMeSubsystem::start()
++{
+     // add thermal sensor for the subsystem
+     std::optional<std::string> sensorName = createSensorNameFromPath(path);
+     if (!sensorName)
+@@ -243,7 +287,7 @@ void NVMeSubsystem::start(const SensorData& configData)
+     }
+ 
+     std::vector<thresholds::Threshold> sensorThresholds;
+-    if (!parseThresholdsFromConfig(configData, sensorThresholds))
++    if (!parseThresholdsFromConfig(config, sensorThresholds))
+     {
+         std::cerr << "error populating thresholds for " << *sensorName << "\n";
+         throw std::runtime_error("error populating thresholds for " +
+@@ -285,30 +329,56 @@ void NVMeSubsystem::start(const SensorData& configData)
+             intf->miSubsystemHealthStatusPoll(std::move(cb));
+         };
+         ctemp_parser_t<nvme_mi_nvm_ss_health_status*> dataParser =
+-            [](nvme_mi_nvm_ss_health_status* status) -> std::optional<double> {
++            [self{shared_from_this()}](
++                nvme_mi_nvm_ss_health_status* status) -> std::optional<double> {
+             // Drive Functional
+             bool df = status->nss & 0x20;
+             if (!df)
+             {
++                // stop the subsystem
++                try
++                {
++                    self->markFunctional(false);
++                    self->ctemp->markFunctional(false);
++                }
++                catch (const std::runtime_error& e)
++                {
++                    std::cerr << e.what() << std::endl;
++                }
+                 return std::nullopt;
+             }
++
++            // restart the subsystem
++            if (self->status == Status::Stop)
++            {
++                self->markFunctional(true);
++                self->ctemp->markFunctional(true);
++            }
+             return {getTemperatureReading(status->ctemp)};
+         };
+         pollCtemp(ctempTimer, ctemp, dataFether, dataParser);
+     }
+-
+-    // TODO: start to poll Drive status.
+-
+-    if (plugin)
+-    {
+-        plugin->start();
+-    }
+ }
+ void NVMeSubsystem::stop()
+ {
+-    if (plugin)
++    ctempTimer->cancel();
++    if (status == Status::Start)
+     {
+-        plugin->stop();
++        std::cerr << "status start" << std::endl;
++        markFunctional(false);
++        ctemp->markFunctional(false);
++    }
++    else if (status == Status::Intiatilzing)
++    {
++        std::cerr << "status init" << std::endl;
++        ctempTimer->expires_after(std::chrono::milliseconds(100));
++        ctempTimer->async_wait(
++            [self{shared_from_this()}](boost::system::error_code ec) {
++            if (ec)
++            {
++                return;
++            }
++            self->stop();
++        });
+     }
+-    ctempTimer->cancel();
+ }
+diff --git a/src/NVMeSubsys.hpp b/src/NVMeSubsys.hpp
+index 3944fe7..e5db58a 100644
+--- a/src/NVMeSubsys.hpp
++++ b/src/NVMeSubsys.hpp
+@@ -19,11 +19,11 @@ class NVMeSubsystem : public std::enable_shared_from_this<NVMeSubsystem>
+                   sdbusplus::asio::object_server& objServer,
+                   std::shared_ptr<sdbusplus::asio::connection> conn,
+                   const std::string& path, const std::string& name,
+-                  NVMeIntf intf);
++                  const SensorData& configData, NVMeIntf intf);
+ 
+     ~NVMeSubsystem();
+ 
+-    void start(const SensorData& configData);
++    void start();
+ 
+     void stop();
+ 
+@@ -34,9 +34,19 @@ class NVMeSubsystem : public std::enable_shared_from_this<NVMeSubsystem>
+     std::shared_ptr<sdbusplus::asio::connection> conn;
+     std::string path;
+     std::string name;
++    SensorData config;
+ 
+     NVMeIntf nvmeIntf;
+ 
++    enum class Status
++    {
++        Stop,
++        Intiatilzing,
++        Start,
++    };
++
++    Status status;
++
+     // plugin
+     std::shared_ptr<NVMePlugin> plugin;
+ 
+@@ -61,4 +71,8 @@ class NVMeSubsystem : public std::enable_shared_from_this<NVMeSubsystem>
+ 
+     std::shared_ptr<sdbusplus::asio::dbus_interface> assocIntf;
+     void createStorageAssociation();
++
++    // make the subsystem functional/functional be enabling/disabling the
++    // storage controller, namespaces and thermal sensors.
++    void markFunctional(bool toggle);
+ };
+diff --git a/tests/meson.build b/tests/meson.build
+index cc7fd53..4195434 100644
+--- a/tests/meson.build
++++ b/tests/meson.build
+@@ -21,6 +21,7 @@ endif
+ 
+ ut_deps_list = [
+     gtest_dep,
++    gmock_dep,
+ ]
+ 
+ ut_deps_list += default_deps
+diff --git a/tests/test_nvme_mi.cpp b/tests/test_nvme_mi.cpp
+index f8e4a01..581f20b 100644
+--- a/tests/test_nvme_mi.cpp
++++ b/tests/test_nvme_mi.cpp
+@@ -3,38 +3,167 @@
+ 
+ #include <sdbusplus/asio/connection.hpp>
+ 
++#include <gmock/gmock.h>
+ #include <gtest/gtest.h>
+ 
++class NVMeMiMock :
++    public NVMeMiIntf,
++    public std::enable_shared_from_this<NVMeMiMock>
++{
++  public:
++    NVMeMiMock(boost::asio::io_service& io) :
++        fake(std::move(std::make_shared<NVMeMiFake>(io)))
++    {
++        ON_CALL(*this, getNID).WillByDefault([]() { return 0; });
++        ON_CALL(*this, getEID).WillByDefault([]() { return 0; });
++        ON_CALL(*this, miSubsystemHealthStatusPoll)
++            .WillByDefault(
++                [this](
++                    std::function<void(const std::error_code&,
++                                       nvme_mi_nvm_ss_health_status*)>&& cb) {
++            return fake->miSubsystemHealthStatusPoll(std::move(cb));
++            });
++        ON_CALL(*this, miScanCtrl)
++            .WillByDefault(
++                [this](
++                    std::function<void(const std::error_code& ec,
++                                       const std::vector<nvme_mi_ctrl_t>& list)>
++                        cb) { return fake->miScanCtrl(std::move(cb)); });
++        ON_CALL(*this, adminIdentify)
++            .WillByDefault(
++                [this](nvme_mi_ctrl_t ctrl, nvme_identify_cns cns,
++                       uint32_t nsid, uint16_t cntid,
++                       std::function<void(const std::error_code&,
++                                          std::span<uint8_t>)>&& cb) {
++            return fake->adminIdentify(ctrl, cns, nsid, cntid, std::move(cb));
++            });
++        ON_CALL(*this, adminGetLogPage)
++            .WillByDefault(
++                [this](nvme_mi_ctrl_t ctrl, nvme_cmd_get_log_lid lid,
++                       uint32_t nsid, uint8_t lsp, uint16_t lsi,
++                       std::function<void(const std::error_code&,
++                                          std::span<uint8_t>)>&& cb) {
++            return fake->adminGetLogPage(ctrl, lid, nsid, lsp, lsi,
++                                         std::move(cb));
++            });
++        ON_CALL(*this, adminFwCommit)
++            .WillByDefault([this](nvme_mi_ctrl_t ctrl, nvme_fw_commit_ca action,
++                                  uint8_t slot, bool bpid,
++                                  std::function<void(const std::error_code&,
++                                                     nvme_status_field)>&& cb) {
++                return fake->adminFwCommit(ctrl, action, slot, bpid,
++                                           std::move(cb));
++            });
++        ON_CALL(*this, adminXfer)
++            .WillByDefault(
++                [this](
++                    nvme_mi_ctrl_t ctrl, const nvme_mi_admin_req_hdr& admin_req,
++                    std::span<uint8_t> data, unsigned int timeout_ms,
++                    std::function<void(const std::error_code& ec,
++                                       const nvme_mi_admin_resp_hdr& admin_resp,
++                                       std::span<uint8_t> resp_data)>&& cb) {
++            return fake->adminXfer(ctrl, admin_req, data, timeout_ms,
++                                   std::move(cb));
++            });
++    }
++
++    MOCK_METHOD(int, getNID, (), (const override));
++    MOCK_METHOD(int, getEID, (), (const override));
++    MOCK_METHOD(void, miSubsystemHealthStatusPoll,
++                (std::function<void(const std::error_code&,
++                                    nvme_mi_nvm_ss_health_status*)> &&),
++                (override));
++    MOCK_METHOD(void, miScanCtrl,
++                (std::function<void(const std::error_code&,
++                                    const std::vector<nvme_mi_ctrl_t>&)>),
++                (override));
++    MOCK_METHOD(
++        void, adminIdentify,
++        (nvme_mi_ctrl_t ctrl, nvme_identify_cns cns, uint32_t nsid,
++         uint16_t cntid,
++         std::function<void(const std::error_code&, std::span<uint8_t>)>&& cb),
++        (override));
++    MOCK_METHOD(
++        void, adminGetLogPage,
++        (nvme_mi_ctrl_t ctrl, nvme_cmd_get_log_lid lid, uint32_t nsid,
++         uint8_t lsp, uint16_t lsi,
++         std::function<void(const std::error_code&, std::span<uint8_t>)>&& cb),
++        (override));
++    MOCK_METHOD(
++        void, adminFwCommit,
++        (nvme_mi_ctrl_t ctrl, nvme_fw_commit_ca action, uint8_t slot, bool bpid,
++         std::function<void(const std::error_code&, nvme_status_field)>&& cb),
++        (override));
++    MOCK_METHOD(void, adminXfer,
++                (nvme_mi_ctrl_t ctrl, const nvme_mi_admin_req_hdr& admin_req,
++                 std::span<uint8_t> data, unsigned int timeout_ms,
++                 std::function<void(const std::error_code& ec,
++                                    const nvme_mi_admin_resp_hdr& admin_resp,
++                                    std::span<uint8_t> resp_data)>&& cb),
++                (override));
++
++    std::shared_ptr<NVMeMiFake> fake;
++};
++
+ class NVMeTest : public ::testing::Test
+ {
+   protected:
+     NVMeTest() :
+-        system_bus(std::make_shared<sdbusplus::asio::connection>(io)),
+-        object_server(system_bus),
+-        subsys(std::make_shared<NVMeSubsystem>(
+-            io, object_server, system_bus, subsys_path, "NVMe_1",
+-            std::move(NVMeIntf::create<NVMeMiFake>(io))))
++        object_server(system_bus), nvme_intf(NVMeIntf::create<NVMeMiMock>(io)),
++        mock(*std::dynamic_pointer_cast<NVMeMiMock>(
++                  std::get<std::shared_ptr<NVMeMiIntf>>(
++                      nvme_intf.getInferface()))
++                  .get()),
++        subsys(std::make_shared<NVMeSubsystem>(io, object_server, system_bus,
++                                               subsys_path, "NVMe_1",
++                                               SensorData{}, nvme_intf))
+     {}
+-    void SetUp() override
++
++    static void SetUpTestSuite()
+     {
++        system_bus =
++            std::make_shared<sdbusplus::asio::connection>(NVMeTest::io);
+         system_bus->request_name("xyz.openbmc_project.NVMeTest");
+-        subsys->start(SensorData{});
++    }
++
++    void SetUp() override
++    {
++        subsys->start();
++    }
++
++    void TearDown() override
++    {
++        io.restart();
+     }
+ 
+     static constexpr char subsys_path[] =
+         "/xyz/openbmc_project/inventory/Test_Chassis/Test_NVMe";
+ 
+-    boost::asio::io_service io;
+-    std::shared_ptr<sdbusplus::asio::connection> system_bus;
++    static boost::asio::io_service io;
++    static std::shared_ptr<sdbusplus::asio::connection> system_bus;
+     sdbusplus::asio::object_server object_server;
+ 
++    NVMeIntf nvme_intf;
++    NVMeMiMock& mock;
+     std::shared_ptr<NVMeSubsystem> subsys;
+ };
+ 
++boost::asio::io_service NVMeTest::io;
++std::shared_ptr<sdbusplus::asio::connection> NVMeTest::system_bus;
++
++/**
++ * @brief Test start and stop function of NVMeSubsystem
++ *
++ */
+ TEST_F(NVMeTest, TestSubsystemStartStop)
+ {
++    using ::testing::AtLeast;
+     boost::asio::steady_timer timer(io);
+ 
++    EXPECT_CALL(mock, miSubsystemHealthStatusPoll).Times(AtLeast(1));
++    EXPECT_CALL(mock, adminIdentify).Times(AtLeast(1));
++    EXPECT_CALL(mock, miScanCtrl).Times(AtLeast(1));
++
+     // wait for subsystem initialization
+     timer.expires_after(std::chrono::seconds(2));
+     timer.async_wait([&](boost::system::error_code) {
+@@ -70,3 +199,101 @@ TEST_F(NVMeTest, TestSubsystemStartStop)
+     });
+     io.run();
+ }
++
++/**
++ * @brief Test NVMeMi return DriveFunctional(NSHDS.NSS.DF) = 0
++ *
++ */
++TEST_F(NVMeTest, TestDriveFunctional)
++{
++
++    using ::testing::AtLeast;
++    boost::asio::steady_timer timer(io);
++
++    EXPECT_CALL(mock, miSubsystemHealthStatusPoll).Times(AtLeast(1));
++    EXPECT_CALL(mock, adminIdentify).Times(AtLeast(1));
++    EXPECT_CALL(mock, miScanCtrl).Times(AtLeast(1));
++
++    // wait for subsystem initialization
++    timer.expires_after(std::chrono::seconds(2));
++    timer.async_wait([&](boost::system::error_code) {
++        system_bus->async_method_call(
++            [&](boost::system::error_code, const GetSubTreeType& result) {
++            // Only PF and the enabled VF should be listed
++            EXPECT_EQ(result.size(), 2);
++
++            // mimik communication error of NVMeMI request
++            ON_CALL(mock, miSubsystemHealthStatusPoll)
++                .WillByDefault(
++                    [&](std::function<void(const std::error_code&,
++                                           nvme_mi_nvm_ss_health_status*)>&&
++                            cb) {
++                std::cerr << "mock health poll" << std::endl;
++                // return status.nss.df = 0
++                return io.post([cb = std::move(cb)]() {
++                    nvme_mi_nvm_ss_health_status status;
++                    status.nss = 0;
++                    cb({}, &status);
++                });
++                });
++
++            // wait for storage controller destruction.
++            timer.expires_after(std::chrono::seconds(2));
++            timer.async_wait([&](boost::system::error_code) {
++                system_bus->async_method_call(
++                    [&](boost::system::error_code,
++                        const GetSubTreeType& result) {
++                    // no storage controller should be listed.
++                    EXPECT_EQ(result.size(), 0);
++
++                    // restart sending DF = 1
++                    ON_CALL(mock, miSubsystemHealthStatusPoll)
++                        .WillByDefault(
++                            [&](std::function<void(
++                                    const std::error_code&,
++                                    nvme_mi_nvm_ss_health_status*)>&& cb) {
++                        return mock.fake->miSubsystemHealthStatusPoll(
++                            std::move(cb));
++                        });
++                    timer.expires_after(std::chrono::seconds(2));
++                    timer.async_wait([&](boost::system::error_code) {
++                        system_bus->async_method_call(
++                            [&](boost::system::error_code,
++                                const GetSubTreeType& result) {
++                            // storage controller should be restored.
++                            EXPECT_EQ(result.size(), 2);
++
++                            subsys->stop();
++                            io.stop();
++                            },
++                            "xyz.openbmc_project.ObjectMapper",
++                            "/xyz/openbmc_project/object_mapper",
++                            "xyz.openbmc_project.ObjectMapper", "GetSubTree",
++                            subsys_path, 0,
++                            std::vector<std::string>{
++                                "xyz.openbmc_project.Inventory."
++                                "Item.StorageController"});
++                    });
++                    },
++                    "xyz.openbmc_project.ObjectMapper",
++                    "/xyz/openbmc_project/object_mapper",
++                    "xyz.openbmc_project.ObjectMapper", "GetSubTree",
++                    subsys_path, 0,
++                    std::vector<std::string>{"xyz.openbmc_project.Inventory."
++                                             "Item.StorageController"});
++            });
++            },
++            "xyz.openbmc_project.ObjectMapper",
++            "/xyz/openbmc_project/object_mapper",
++            "xyz.openbmc_project.ObjectMapper", "GetSubTree", subsys_path, 0,
++            std::vector<std::string>{
++                "xyz.openbmc_project.Inventory.Item.StorageController"});
++    });
++    io.run();
++}
++
++int main(int argc, char** argv)
++{
++    ::testing::InitGoogleTest(&argc, argv);
++    return RUN_ALL_TESTS();
++}
+-- 
+2.34.1
+
diff --git a/recipes-phosphor/sensors/dbus-sensors/0032-nvmesensor-subsystem-poll-supports-absence.patch b/recipes-phosphor/sensors/dbus-sensors/0032-nvmesensor-subsystem-poll-supports-absence.patch
new file mode 100644
index 0000000..7325df9
--- /dev/null
+++ b/recipes-phosphor/sensors/dbus-sensors/0032-nvmesensor-subsystem-poll-supports-absence.patch
@@ -0,0 +1,544 @@
+From f89349b1a54b3e5bb6dcb3ddd027f90543c9a46e Mon Sep 17 00:00:00 2001
+From: Hao Jiang <jianghao@google.com>
+Date: Thu, 20 Apr 2023 20:48:54 +0000
+Subject: [PATCH 32/44] nvmesensor: subsystem poll supports absence
+
+Enhance the polling function to support more symptoms. For example, the
+endpoint absence. The signal comes from NVMeMi class because the
+absence detection was done by transporation layer(mctp or i2c binding),
+instead of the NVMe-MI protocol layer.
+
+The enhancement will be furthur utilized by other symptoms provided by
+NSHDS (NVM Subsystem Health Data Structure), including but not limited
+to SW, CCS.CSTS, CCS.NAC, etc.
+
+Tested: passed the unit test of TestDriveAbsent with increase polling
+rate of 50 ms.
+
+Signed-off-by: Hao Jiang <jianghao@google.com>
+Change-Id: I5efe7c07e07630064b5e6e7fd707f3c286673bba
+---
+ src/NVMeBasic.cpp      |  13 +---
+ src/NVMeIntf.hpp       |   1 +
+ src/NVMeSubsys.cpp     | 146 +++++++++++++++++++++++++++++++++--------
+ src/NVMeSubsys.hpp     |   1 -
+ src/NVMeUtil.hpp       |  92 ++++++++------------------
+ tests/test_nvme_mi.cpp |  95 ++++++++++++++++++++++++++-
+ 6 files changed, 242 insertions(+), 106 deletions(-)
+
+diff --git a/src/NVMeBasic.cpp b/src/NVMeBasic.cpp
+index 3a2d4ca..d18aed4 100644
+--- a/src/NVMeBasic.cpp
++++ b/src/NVMeBasic.cpp
+@@ -342,23 +342,12 @@ void NVMeBasic::getStatus(
+ 
+             /* Process the callback */
+             self->io.post([data, cb{std::move(cb)}]() {
+-                if (data->size() <
+-                    sizeof(DriveStatus) + 1) // The first byte is status flags
++                if (data->size() < sizeof(DriveStatus))
+                 {
+                     cb(std::make_error_code(std::errc::message_size), nullptr);
+                     return;
+                 }
+ 
+-                uint8_t flags = static_cast<uint8_t>(data->front());
+-                if (((flags & NVME_MI_BASIC_SFLGS_DRIVE_NOT_READY) != 0) ||
+-                    ((flags & NVME_MI_BASIC_SFLGS_DRIVE_FUNCTIONAL) == 0))
+-                {
+-                    cb(std::make_error_code(std::errc::no_such_device),
+-                       nullptr);
+-                    return;
+-                }
+-
+-                data->erase(data->begin());
+                 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+                 cb({}, reinterpret_cast<DriveStatus*>(data->data()));
+             });
+diff --git a/src/NVMeIntf.hpp b/src/NVMeIntf.hpp
+index e52a700..f87b536 100644
+--- a/src/NVMeIntf.hpp
++++ b/src/NVMeIntf.hpp
+@@ -78,6 +78,7 @@ class NVMeBasicIntf
+   public:
+     struct DriveStatus
+     {
++        uint8_t Status;
+         uint8_t SmartWarnings;
+         uint8_t Temp;
+         uint8_t DriveLifeUsed;
+diff --git a/src/NVMeSubsys.cpp b/src/NVMeSubsys.cpp
+index 10b360d..c605e30 100644
+--- a/src/NVMeSubsys.cpp
++++ b/src/NVMeSubsys.cpp
+@@ -1,6 +1,7 @@
+ #include "NVMeSubsys.hpp"
+ 
+ #include "NVMePlugin.hpp"
++#include "NVMeUtil.hpp"
+ #include "Thresholds.hpp"
+ 
+ #include <filesystem>
+@@ -303,60 +304,153 @@ void NVMeSubsystem::start()
+     {
+         auto intf =
+             std::get<std::shared_ptr<NVMeBasicIntf>>(nvmeIntf.getInferface());
+-        ctemp_fetcher_t<NVMeBasicIntf::DriveStatus*> dataFether =
+-            [intf](std::function<void(const std::error_code&,
+-                                      NVMeBasicIntf::DriveStatus*)>&& cb) {
++        ctemp_fetch_t<NVMeBasicIntf::DriveStatus*> dataFether =
++            [intf, self{std::move(shared_from_this())}](
++                std::function<void(const std::error_code&,
++                                   NVMeBasicIntf::DriveStatus*)>&& cb) {
++            /* Potentially defer sampling the sensor sensor if it is in error */
++            if (!self->ctemp->sample())
++            {
++                cb(std::make_error_code(std::errc::operation_canceled),
++                   nullptr);
++                return;
++            }
++
+             intf->getStatus(std::move(cb));
+         };
+-        ctemp_parser_t<NVMeBasicIntf::DriveStatus*> dataParser =
+-            [](NVMeBasicIntf::DriveStatus* status) -> std::optional<double> {
++        ctemp_process_t<NVMeBasicIntf::DriveStatus*> dataProcessor =
++            [self{shared_from_this()}](const std::error_code& error,
++                                       NVMeBasicIntf::DriveStatus* status) {
++            // deferred sampling
++            if (error == std::errc::operation_canceled)
++            {
++                return;
++            }
++            // The device is physically absent
++            else if (error == std::errc::no_such_device)
++            {
++                std::cerr << "error reading ctemp from subsystem"
++                          << ", reason:" << error.message() << "\n";
++                self->ctemp->markFunctional(false);
++                self->ctemp->markAvailable(false);
++                return;
++            }
++            // other communication errors
++            else if (error)
++            {
++                std::cerr << "error reading ctemp from subsystem"
++                          << ", reason:" << error.message() << "\n";
++                self->ctemp->incrementError();
++                return;
++            }
++
+             if (status == nullptr)
+             {
+-                return std::nullopt;
++                std::cerr << "empty data returned by data fetcher" << std::endl;
++                self->ctemp->markFunctional(false);
++                return;
+             }
+-            return {getTemperatureReading(status->Temp)};
++
++            uint8_t flags = status->Status;
++            if (((flags & NVMeBasicIntf::StatusFlags::
++                              NVME_MI_BASIC_SFLGS_DRIVE_NOT_READY) != 0) ||
++                ((flags & NVMeBasicIntf::StatusFlags::
++                              NVME_MI_BASIC_SFLGS_DRIVE_FUNCTIONAL) == 0))
++            {
++                self->ctemp->markFunctional(false);
++                return;
++            }
++            self->ctemp->updateValue(getTemperatureReading(status->Temp));
+         };
+-        pollCtemp(this->ctempTimer, this->ctemp, dataFether, dataParser);
++
++        pollCtemp(ctempTimer, std::chrono::seconds(1), dataFether,
++                  dataProcessor);
+     }
+     else if (nvmeIntf.getProtocol() == NVMeIntf::Protocol::NVMeMI)
+     {
+         auto intf =
+             std::get<std::shared_ptr<NVMeMiIntf>>(nvmeIntf.getInferface());
+ 
+-        ctemp_fetcher_t<nvme_mi_nvm_ss_health_status*> dataFether =
+-            [intf](std::function<void(const std::error_code&,
+-                                      nvme_mi_nvm_ss_health_status*)>&& cb) {
++        ctemp_fetch_t<nvme_mi_nvm_ss_health_status*> dataFether =
++            [intf, self{std::move(shared_from_this())}](
++                std::function<void(const std::error_code&,
++                                   nvme_mi_nvm_ss_health_status*)>&& cb) {
++            // do not poll the health status if the subsystem is intiatilzing
++            if (self->status == Status::Intiatilzing)
++            {
++                std::cerr << "subsystem is intiatilzing, cancel the health poll"
++                          << std::endl;
++                cb(std::make_error_code(std::errc::operation_canceled),
++                   nullptr);
++                return;
++            }
+             intf->miSubsystemHealthStatusPoll(std::move(cb));
+         };
+-        ctemp_parser_t<nvme_mi_nvm_ss_health_status*> dataParser =
+-            [self{shared_from_this()}](
+-                nvme_mi_nvm_ss_health_status* status) -> std::optional<double> {
+-            // Drive Functional
+-            bool df = status->nss & 0x20;
+-            if (!df)
++        ctemp_process_t<nvme_mi_nvm_ss_health_status*> dataProcessor =
++            [self{shared_from_this()}](const std::error_code& error,
++                                       nvme_mi_nvm_ss_health_status* status) {
++            if (error == std::errc::operation_canceled ||
++                self->status == Status::Intiatilzing)
+             {
++                // on initialization, the subsystem will not update the status.
++                std::cerr
++                    << "subsystem is intiatilzing, do not process the status"
++                    << std::endl;
++                return;
++            }
++
++            if (error == std::errc::no_such_device)
++            {
++                std::cerr << "error reading ctemp "
++                             "from subsystem"
++                          << ", reason:" << error.message() << "\n";
+                 // stop the subsystem
+-                try
++                self->markFunctional(false);
++                self->ctemp->markFunctional(false);
++                self->ctemp->markAvailable(false);
++
++                return;
++            }
++            else if (error)
++            {
++                std::cerr << "error reading ctemp "
++                             "from subsystem"
++                          << ", reason:" << error.message() << "\n";
++                self->ctemp->incrementError();
++                if (self->ctemp->inError())
+                 {
+                     self->markFunctional(false);
+                     self->ctemp->markFunctional(false);
+                 }
+-                catch (const std::runtime_error& e)
+-                {
+-                    std::cerr << e.what() << std::endl;
+-                }
+-                return std::nullopt;
++                return;
++            }
++
++            // Drive Functional
++            bool df = status->nss & 0x20;
++            if (!df)
++            {
++                // stop the subsystem
++
++                self->markFunctional(false);
++                self->ctemp->markFunctional(false);
++
++                return;
+             }
+ 
+             // restart the subsystem
+             if (self->status == Status::Stop)
+             {
+                 self->markFunctional(true);
+-                self->ctemp->markFunctional(true);
+             }
+-            return {getTemperatureReading(status->ctemp)};
++
++            // TODO: update the drive interface
++
++            self->ctemp->updateValue(getTemperatureReading(status->ctemp));
++            return;
+         };
+-        pollCtemp(ctempTimer, ctemp, dataFether, dataParser);
++
++        pollCtemp(ctempTimer, std::chrono::seconds(1), dataFether,
++                  dataProcessor);
+     }
+ }
+ void NVMeSubsystem::stop()
+diff --git a/src/NVMeSubsys.hpp b/src/NVMeSubsys.hpp
+index e5db58a..6bd8b62 100644
+--- a/src/NVMeSubsys.hpp
++++ b/src/NVMeSubsys.hpp
+@@ -4,7 +4,6 @@
+ #include "NVMeDrive.hpp"
+ #include "NVMeSensor.hpp"
+ #include "NVMeStorage.hpp"
+-#include "NVMeUtil.hpp"
+ #include "Utils.hpp"
+ 
+ class NVMeControllerPlugin;
+diff --git a/src/NVMeUtil.hpp b/src/NVMeUtil.hpp
+index 16fe291..8639113 100644
+--- a/src/NVMeUtil.hpp
++++ b/src/NVMeUtil.hpp
+@@ -7,6 +7,7 @@
+ #include <filesystem>
+ #include <iostream>
+ #include <optional>
++#include <system_error>
+ 
+ inline std::filesystem::path deriveRootBusPath(int busNumber)
+ {
+@@ -104,60 +105,43 @@ inline std::optional<std::string>
+ // Function type for fetching ctemp which encaplucated in a structure of T.
+ // The fetcher function take a callback as input to process the result.
+ template <class T>
+-using ctemp_fetcher_t =
++using ctemp_fetch_t =
+     std::function<void(std::function<void(const std::error_code&, T)>&&)>;
+ 
+-// Function type for parsing ctemp out the structure of type T.
+-// The parser function will return the value of ctemp or nullopt on failure.
++// Function type for processing ctemp out the structure of type T.
++// The process function will update the properties based on input data.
+ template <class T>
+-using ctemp_parser_t = std::function<std::optional<double>(T data)>;
++using ctemp_process_t =
++    std::function<void(const std::error_code& error, T data)>;
+ 
+ template <class T>
+ void pollCtemp(
+     std::shared_ptr<boost::asio::steady_timer> timer,
+-    std::shared_ptr<NVMeSensor> sensor,
++    std::chrono::duration<double, std::milli> delay,
+     const std::function<void(std::function<void(const std::error_code&, T)>&&)>&
+         dataFetcher,
+-    const std::function<std::optional<double>(T data)>& dataParser);
++    const std::function<void(const std::error_code& error, T data)>&
++        dataProcessor);
+ 
+ namespace detail
+ {
+ 
+ template <class T>
+ void updateCtemp(std::shared_ptr<boost::asio::steady_timer> timer,
+-                 std::shared_ptr<NVMeSensor> sensor,
+-                 ctemp_parser_t<T> dataParser, ctemp_fetcher_t<T> dataFetcher,
+-                 const boost::system::error_code error, T data)
++                 std::chrono::duration<double, std::milli> delay,
++                 ctemp_process_t<T> dataProcessor, ctemp_fetch_t<T> dataFetcher,
++                 const std::error_code& error, T data)
+ {
+-    if (error)
+-    {
+-        std::cerr << "error reading ctemp from subsystem"
+-                  << ", reason:" << error.message() << "\n";
+-        sensor->markFunctional(false);
+-        ::pollCtemp(std::move(timer), std::move(sensor), dataFetcher,
+-                    dataParser);
+-        return;
+-    }
+-    auto value = dataParser(data);
+-    if (!value)
+-    {
+-        sensor->incrementError();
+-        ::pollCtemp(std::move(timer), std::move(sensor), dataFetcher,
+-                    dataParser);
+-        return;
+-    }
+-
+-    sensor->updateValue(*value);
+-    ::pollCtemp(std::move(timer), std::move(sensor), dataFetcher, dataParser);
++    dataProcessor(error, data);
++    ::pollCtemp(std::move(timer), std::move(delay), dataFetcher, dataProcessor);
+ }
+ 
+ template <class T>
+ void pollCtemp(std::shared_ptr<boost::asio::steady_timer> timer,
+-               std::shared_ptr<NVMeSensor> sensor,
+-               ctemp_fetcher_t<T> dataFetcher, ctemp_parser_t<T> dataParser,
++               std::chrono::duration<double, std::milli> delay,
++               ctemp_fetch_t<T> dataFetcher, ctemp_process_t<T> dataProcessor,
+                const boost::system::error_code errorCode)
+ {
+-
+     if (errorCode == boost::asio::error::operation_aborted)
+     {
+         return;
+@@ -165,37 +149,13 @@ void pollCtemp(std::shared_ptr<boost::asio::steady_timer> timer,
+     if (errorCode)
+     {
+         std::cerr << errorCode.message() << "\n";
+-        ::pollCtemp(std::move(timer), std::move(sensor), dataFetcher,
+-                    dataParser);
+-        return;
+-    }
+-
+-    if (!sensor)
+-    {
+-        ::pollCtemp(std::move(timer), std::move(sensor), dataFetcher,
+-                    dataParser);
+-        return;
+-    }
+-
+-    if (!sensor->readingStateGood())
+-    {
+-        sensor->markAvailable(false);
+-        sensor->updateValue(std::numeric_limits<double>::quiet_NaN());
+-        ::pollCtemp(std::move(timer), std::move(sensor), dataFetcher,
+-                    dataParser);
+-        return;
+-    }
+-
+-    /* Potentially defer sampling the sensor sensor if it is in error */
+-    if (!sensor->sample())
+-    {
+-        ::pollCtemp(std::move(timer), std::move(sensor), dataFetcher,
+-                    dataParser);
++        ::pollCtemp(std::move(timer), std::move(delay), dataFetcher,
++                    dataProcessor);
+         return;
+     }
+ 
+     dataFetcher(std::bind_front(detail::updateCtemp<T>, std::move(timer),
+-                                std::move(sensor), dataParser, dataFetcher));
++                                std::move(delay), dataProcessor, dataFetcher));
+ }
+ 
+ } // namespace detail
+@@ -203,17 +163,19 @@ void pollCtemp(std::shared_ptr<boost::asio::steady_timer> timer,
+ template <class T>
+ void pollCtemp(
+     std::shared_ptr<boost::asio::steady_timer> timer,
+-    std::shared_ptr<NVMeSensor> sensor,
++    std::chrono::duration<double, std::milli> delay,
+     const std::function<void(std::function<void(const std::error_code&, T)>&&)>&
+         dataFetcher,
+-    const std::function<std::optional<double>(T data)>& dataParser)
++    const std::function<void(const std::error_code& error, T data)>&
++        dataProcessor)
+ {
+-    if (!timer && !sensor)
++    if (!timer)
+     {
+         return;
+     }
+-    timer->expires_from_now(std::chrono::seconds(1));
++    timer->expires_from_now(
++        std::chrono::duration_cast<std::chrono::milliseconds>(delay));
+     timer->async_wait(std::bind_front(detail::pollCtemp<T>, std::move(timer),
+-                                      std::move(sensor), dataFetcher,
+-                                      dataParser));
++                                      std::move(delay), dataFetcher,
++                                      dataProcessor));
+ }
+diff --git a/tests/test_nvme_mi.cpp b/tests/test_nvme_mi.cpp
+index 581f20b..8462d5e 100644
+--- a/tests/test_nvme_mi.cpp
++++ b/tests/test_nvme_mi.cpp
+@@ -228,7 +228,8 @@ TEST_F(NVMeTest, TestDriveFunctional)
+                     [&](std::function<void(const std::error_code&,
+                                            nvme_mi_nvm_ss_health_status*)>&&
+                             cb) {
+-                std::cerr << "mock health poll" << std::endl;
++                std::cerr << "mock device not functional health poll"
++                          << std::endl;
+                 // return status.nss.df = 0
+                 return io.post([cb = std::move(cb)]() {
+                     nvme_mi_nvm_ss_health_status status;
+@@ -264,7 +265,97 @@ TEST_F(NVMeTest, TestDriveFunctional)
+                             EXPECT_EQ(result.size(), 2);
+ 
+                             subsys->stop();
+-                            io.stop();
++                            io.post([&]() { io.stop(); });
++                            },
++                            "xyz.openbmc_project.ObjectMapper",
++                            "/xyz/openbmc_project/object_mapper",
++                            "xyz.openbmc_project.ObjectMapper", "GetSubTree",
++                            subsys_path, 0,
++                            std::vector<std::string>{
++                                "xyz.openbmc_project.Inventory."
++                                "Item.StorageController"});
++                    });
++                    },
++                    "xyz.openbmc_project.ObjectMapper",
++                    "/xyz/openbmc_project/object_mapper",
++                    "xyz.openbmc_project.ObjectMapper", "GetSubTree",
++                    subsys_path, 0,
++                    std::vector<std::string>{"xyz.openbmc_project.Inventory."
++                                             "Item.StorageController"});
++            });
++            },
++            "xyz.openbmc_project.ObjectMapper",
++            "/xyz/openbmc_project/object_mapper",
++            "xyz.openbmc_project.ObjectMapper", "GetSubTree", subsys_path, 0,
++            std::vector<std::string>{
++                "xyz.openbmc_project.Inventory.Item.StorageController"});
++    });
++    io.run();
++}
++
++/**
++ * @brief Test NVMeMi returns Drive is absent (ec = no_such_device)
++ *
++ */
++TEST_F(NVMeTest, TestDriveAbsent)
++{
++    using ::testing::AtLeast;
++    boost::asio::steady_timer timer(io);
++
++    EXPECT_CALL(mock, miSubsystemHealthStatusPoll).Times(AtLeast(1));
++    EXPECT_CALL(mock, adminIdentify).Times(AtLeast(1));
++    EXPECT_CALL(mock, miScanCtrl).Times(AtLeast(1));
++
++    // wait for subsystem initialization
++    timer.expires_after(std::chrono::seconds(2));
++    timer.async_wait([&](boost::system::error_code) {
++        system_bus->async_method_call(
++            [&](boost::system::error_code, const GetSubTreeType& result) {
++            // Only PF and the enabled VF should be listed
++            EXPECT_EQ(result.size(), 2);
++
++            // mimik communication error of NVMeMI request
++            ON_CALL(mock, miSubsystemHealthStatusPoll)
++                .WillByDefault(
++                    [&](std::function<void(const std::error_code&,
++                                           nvme_mi_nvm_ss_health_status*)>&&
++                            cb) {
++                std::cerr << "mock device absent health poll" << std::endl;
++                // return no_such_device
++                return io.post([cb = std::move(cb)]() {
++                    cb(std::make_error_code(std::errc::no_such_device),
++                       nullptr);
++                });
++                });
++
++            // wait for storage controller destruction.
++            timer.expires_after(std::chrono::seconds(2));
++            timer.async_wait([&](boost::system::error_code) {
++                system_bus->async_method_call(
++                    [&](boost::system::error_code,
++                        const GetSubTreeType& result) {
++                    // no storage controller should be listed.
++                    EXPECT_EQ(result.size(), 0);
++
++                    // restart sending normal polling result
++                    ON_CALL(mock, miSubsystemHealthStatusPoll)
++                        .WillByDefault(
++                            [&](std::function<void(
++                                    const std::error_code&,
++                                    nvme_mi_nvm_ss_health_status*)>&& cb) {
++                        return mock.fake->miSubsystemHealthStatusPoll(
++                            std::move(cb));
++                        });
++                    timer.expires_after(std::chrono::seconds(2));
++                    timer.async_wait([&](boost::system::error_code) {
++                        system_bus->async_method_call(
++                            [&](boost::system::error_code,
++                                const GetSubTreeType& result) {
++                            // storage controller should be restored.
++                            EXPECT_EQ(result.size(), 2);
++
++                            subsys->stop();
++                            io.post([&]() { io.stop(); });
+                             },
+                             "xyz.openbmc_project.ObjectMapper",
+                             "/xyz/openbmc_project/object_mapper",
+-- 
+2.34.1
+
diff --git a/recipes-phosphor/sensors/dbus-sensors/0027-Enable-asio-threads-and-boost-coroutine.patch b/recipes-phosphor/sensors/dbus-sensors/0033-Enable-asio-threads-and-boost-coroutine.patch
similarity index 91%
rename from recipes-phosphor/sensors/dbus-sensors/0027-Enable-asio-threads-and-boost-coroutine.patch
rename to recipes-phosphor/sensors/dbus-sensors/0033-Enable-asio-threads-and-boost-coroutine.patch
index 6d18ad3..cf5533b 100644
--- a/recipes-phosphor/sensors/dbus-sensors/0027-Enable-asio-threads-and-boost-coroutine.patch
+++ b/recipes-phosphor/sensors/dbus-sensors/0033-Enable-asio-threads-and-boost-coroutine.patch
@@ -1,7 +1,7 @@
-From af7572953ba58aa5adcb7f77c69594cfdd6453ec Mon Sep 17 00:00:00 2001
+From 6639b4b4e59806b654d468e8e76776366afacf7c Mon Sep 17 00:00:00 2001
 From: Matt Johnston <matt@codeconstruct.com.au>
 Date: Wed, 18 Jan 2023 15:53:54 +0800
-Subject: [PATCH 27/34] Enable asio threads and boost coroutine
+Subject: [PATCH 33/44] Enable asio threads and boost coroutine
 
 ASIO thread support is required to use a worker thread pool,
 coroutines will be used for asynchronous dbus method handlers.
diff --git a/recipes-phosphor/sensors/dbus-sensors/0028-nvmesensor-Split-constructor-for-shared_from_this.patch b/recipes-phosphor/sensors/dbus-sensors/0034-nvmesensor-Split-constructor-for-shared_from_this.patch
similarity index 94%
rename from recipes-phosphor/sensors/dbus-sensors/0028-nvmesensor-Split-constructor-for-shared_from_this.patch
rename to recipes-phosphor/sensors/dbus-sensors/0034-nvmesensor-Split-constructor-for-shared_from_this.patch
index 97121ff..b5ca898 100644
--- a/recipes-phosphor/sensors/dbus-sensors/0028-nvmesensor-Split-constructor-for-shared_from_this.patch
+++ b/recipes-phosphor/sensors/dbus-sensors/0034-nvmesensor-Split-constructor-for-shared_from_this.patch
@@ -1,7 +1,7 @@
-From abcfdf873fce129504fb009b84615e91abeeabe2 Mon Sep 17 00:00:00 2001
+From 005696ecd326eb3982764171db38f892963c943a Mon Sep 17 00:00:00 2001
 From: Matt Johnston <matt@codeconstruct.com.au>
 Date: Wed, 18 Jan 2023 16:06:41 +0800
-Subject: [PATCH 28/34] nvmesensor: Split constructor for shared_from_this
+Subject: [PATCH 34/44] nvmesensor: Split constructor for shared_from_this
 
 This allows using shared_from_this during initialisation
 
@@ -122,10 +122,10 @@
  
      /** @brief Implementation for GetLogPage
 diff --git a/src/NVMeSubsys.cpp b/src/NVMeSubsys.cpp
-index e9ad699..97e879e 100644
+index c605e30..cd10a75 100644
 --- a/src/NVMeSubsys.cpp
 +++ b/src/NVMeSubsys.cpp
-@@ -189,8 +189,8 @@ void NVMeSubsystem::start(const SensorData& configData)
+@@ -231,8 +231,8 @@ void NVMeSubsystem::markFunctional(bool toggle)
  
                  // Enable primary controller since they are required to work
                  auto& primaryController = findPrimary->second.first;
@@ -136,7 +136,7 @@
  
                  std::vector<std::shared_ptr<NVMeController>> secCntrls;
                  for (int i = 0; i < listHdr.num; i++)
-@@ -210,8 +210,8 @@ void NVMeSubsystem::start(const SensorData& configData)
+@@ -252,8 +252,8 @@ void NVMeSubsystem::markFunctional(bool toggle)
                      // Check Secondary Controller State
                      if (listHdr.sc_entry[i].scs != 0)
                      {
diff --git a/recipes-phosphor/sensors/dbus-sensors/0029-nvmesensor-Add-SecuritySend-and-SecurityReceive.patch b/recipes-phosphor/sensors/dbus-sensors/0035-nvmesensor-Add-SecuritySend-and-SecurityReceive.patch
similarity index 87%
rename from recipes-phosphor/sensors/dbus-sensors/0029-nvmesensor-Add-SecuritySend-and-SecurityReceive.patch
rename to recipes-phosphor/sensors/dbus-sensors/0035-nvmesensor-Add-SecuritySend-and-SecurityReceive.patch
index b151659..cce12f5 100644
--- a/recipes-phosphor/sensors/dbus-sensors/0029-nvmesensor-Add-SecuritySend-and-SecurityReceive.patch
+++ b/recipes-phosphor/sensors/dbus-sensors/0035-nvmesensor-Add-SecuritySend-and-SecurityReceive.patch
@@ -1,7 +1,7 @@
-From d902af64fba515d7250ba5ec74924d0e41900d87 Mon Sep 17 00:00:00 2001
+From 5757a885b75fdebdb150530137f49bd3b4c89fb0 Mon Sep 17 00:00:00 2001
 From: Matt Johnston <matt@codeconstruct.com.au>
 Date: Wed, 18 Jan 2023 16:33:24 +0800
-Subject: [PATCH 29/34] nvmesensor: Add SecuritySend and SecurityReceive
+Subject: [PATCH 35/44] nvmesensor: Add SecuritySend and SecurityReceive
 
 The dbus interface is manually implemented to allow
 an asynchronous method handler.
@@ -12,13 +12,13 @@
 Signed-off-by: Matt Johnston <matt@codeconstruct.com.au>
 Change-Id: Ic5acd45b6ade2651927b0abbfde35551e139d996
 ---
- src/AsioHelper.hpp     |  40 ++++++++++++
- src/NVMeController.cpp | 134 +++++++++++++++++++++++++++++++++++++++++
- src/NVMeController.hpp |  14 +++++
- src/NVMeIntf.hpp       |  11 ++++
- src/NVMeMi.cpp         | 102 +++++++++++++++++++++++++++++++
+ src/AsioHelper.hpp     |  40 +++++++++++
+ src/NVMeController.cpp | 147 +++++++++++++++++++++++++++++++++++++++++
+ src/NVMeController.hpp |  14 ++++
+ src/NVMeIntf.hpp       |  11 +++
+ src/NVMeMi.cpp         | 102 ++++++++++++++++++++++++++++
  src/NVMeMi.hpp         |  13 ++++
- 6 files changed, 314 insertions(+)
+ 6 files changed, 327 insertions(+)
  create mode 100644 src/AsioHelper.hpp
 
 diff --git a/src/AsioHelper.hpp b/src/AsioHelper.hpp
@@ -68,7 +68,7 @@
 +
 +} // namespace asio_helper
 diff --git a/src/NVMeController.cpp b/src/NVMeController.cpp
-index cec6664..1c34a58 100644
+index cec6664..5553859 100644
 --- a/src/NVMeController.cpp
 +++ b/src/NVMeController.cpp
 @@ -1,7 +1,9 @@
@@ -81,7 +81,7 @@
  #include <sdbusplus/message/native_types.hpp>
  #include <xyz/openbmc_project/Common/File/error.hpp>
  #include <xyz/openbmc_project/Common/error.hpp>
-@@ -76,6 +78,27 @@ void NVMeControllerEnabled::init()
+@@ -76,6 +78,40 @@ void NVMeControllerEnabled::init()
      assocIntf->register_property("Associations", associations);
      assocIntf->initialize();
  
@@ -90,18 +90,31 @@
 +        path, "xyz.openbmc_project.Inventory.Item.StorageControllerSecurity");
 +    securityInterface->register_method(
 +        "SecuritySend",
-+        [self{shared_from_this()}](boost::asio::yield_context yield,
-+                                   uint8_t proto, uint16_t proto_specific,
-+                                   std::vector<uint8_t> data) {
-+        return self->securitySendMethod(yield, proto, proto_specific, data);
++        [selfWeak{weak_from_this()}](boost::asio::yield_context yield,
++                                     uint8_t proto, uint16_t proto_specific,
++                                     std::vector<uint8_t> data) {
++        if (selfWeak.expired())
++        {
++            checkLibNVMeError(std::make_error_code(std::errc::no_such_device),
++                              -1, "SecuritySend");
++            return;
++        }
++        return selfWeak.lock()->securitySendMethod(yield, proto, proto_specific,
++                                                   data);
 +        });
 +    securityInterface->register_method(
 +        "SecurityReceive",
-+        [self{shared_from_this()}](boost::asio::yield_context yield,
-+                                   uint8_t proto, uint16_t proto_specific,
-+                                   uint32_t transfer_length) {
-+        return self->securityReceiveMethod(yield, proto, proto_specific,
-+                                           transfer_length);
++        [selfWeak{weak_from_this()}](boost::asio::yield_context yield,
++                                     uint8_t proto, uint16_t proto_specific,
++                                     uint32_t transfer_length) {
++        if (selfWeak.expired())
++        {
++            checkLibNVMeError(std::make_error_code(std::errc::no_such_device),
++                              -1, "SecurityReceive");
++            return std::vector<uint8_t>{};
++        }
++        return selfWeak.lock()->securityReceiveMethod(
++            yield, proto, proto_specific, transfer_length);
 +        });
 +
 +    securityInterface->initialize();
@@ -109,7 +122,7 @@
      StorageController::emit_added();
      NVMeAdmin::emit_added();
  }
-@@ -246,6 +269,7 @@ void NVMeControllerEnabled::firmwareCommitAsync(uint8_t commitAction,
+@@ -246,6 +282,7 @@ void NVMeControllerEnabled::firmwareCommitAsync(uint8_t commitAction,
  
  NVMeControllerEnabled::~NVMeControllerEnabled()
  {
@@ -117,7 +130,7 @@
      NVMeAdmin::emit_removed();
      StorageController::emit_removed();
  }
-@@ -321,3 +345,113 @@ void NVMeController::addSubsystemAssociation(const std::string& subsysPath)
+@@ -321,3 +358,113 @@ void NVMeController::addSubsystemAssociation(const std::string& subsysPath)
          assocIntf->set_property("Associations", associations);
      }
  }
@@ -269,10 +282,10 @@
 +
  };
 diff --git a/src/NVMeIntf.hpp b/src/NVMeIntf.hpp
-index e7ba3cf..fc8776d 100644
+index f87b536..3a2fa32 100644
 --- a/src/NVMeIntf.hpp
 +++ b/src/NVMeIntf.hpp
-@@ -120,6 +120,17 @@ class NVMeMiIntf : public NVMeIntf
+@@ -184,6 +184,17 @@ class NVMeMiIntf
                                 std::function<void(const std::error_code&,
                                                    nvme_status_field)>&& cb) = 0;
  
@@ -291,7 +304,7 @@
       * adminXfer() -  Raw admin transfer interface.
       * @ctrl: controller to send the admin command to
 diff --git a/src/NVMeMi.cpp b/src/NVMeMi.cpp
-index 59ba3a5..fabc0b5 100644
+index 28d864e..2cdf1d6 100644
 --- a/src/NVMeMi.cpp
 +++ b/src/NVMeMi.cpp
 @@ -12,6 +12,8 @@ std::map<int, std::weak_ptr<NVMeMi::Worker>> NVMeMi::workerMap{};
@@ -301,9 +314,9 @@
 +constexpr size_t maxNVMeMILength = 4096;
 +
  NVMeMi::NVMeMi(boost::asio::io_context& io, sdbusplus::bus_t& dbus, int bus,
-                int addr) :
+                int addr, bool singleThreadMode) :
      io(io),
-@@ -146,6 +148,21 @@ void NVMeMi::Worker::post(std::function<void(void)>&& func)
+@@ -153,6 +155,21 @@ void NVMeMi::Worker::post(std::function<void(void)>&& func)
      throw std::runtime_error("NVMeMi has been stopped");
  }
  
@@ -325,7 +338,7 @@
  void NVMeMi::miSubsystemHealthStatusPoll(
      std::function<void(const std::error_code&, nvme_mi_nvm_ss_health_status*)>&&
          cb)
-@@ -792,3 +809,88 @@ void NVMeMi::adminFwCommit(
+@@ -799,3 +816,88 @@ void NVMeMi::adminFwCommit(
          return;
      }
  }
@@ -415,7 +428,7 @@
 +    }
 +}
 diff --git a/src/NVMeMi.hpp b/src/NVMeMi.hpp
-index 3071596..6a57b6e 100644
+index 4f1a483..2aa6636 100644
 --- a/src/NVMeMi.hpp
 +++ b/src/NVMeMi.hpp
 @@ -46,6 +46,17 @@ class NVMeMi : public NVMeMiIntf, public std::enable_shared_from_this<NVMeMi>
diff --git a/recipes-phosphor/sensors/dbus-sensors/0031-nvmesensor-set-default-timeout-to-20-seconds-for-Sec.patch b/recipes-phosphor/sensors/dbus-sensors/0036-nvmesensor-set-default-timeout-to-20-seconds-for-Sec.patch
similarity index 86%
rename from recipes-phosphor/sensors/dbus-sensors/0031-nvmesensor-set-default-timeout-to-20-seconds-for-Sec.patch
rename to recipes-phosphor/sensors/dbus-sensors/0036-nvmesensor-set-default-timeout-to-20-seconds-for-Sec.patch
index df23f7a..46e15e3 100644
--- a/recipes-phosphor/sensors/dbus-sensors/0031-nvmesensor-set-default-timeout-to-20-seconds-for-Sec.patch
+++ b/recipes-phosphor/sensors/dbus-sensors/0036-nvmesensor-set-default-timeout-to-20-seconds-for-Sec.patch
@@ -1,7 +1,7 @@
-From 46da5fcb52c7751d0ec8bde97e8f8295af0bdddb Mon Sep 17 00:00:00 2001
+From 132f082370706d00b74e99ebbb78342f7be200a0 Mon Sep 17 00:00:00 2001
 From: Jinliang Wang <jinliangw@google.com>
 Date: Tue, 18 Apr 2023 16:18:37 -0700
-Subject: [PATCH 31/34] nvmesensor: set default timeout to 20 seconds for
+Subject: [PATCH 36/44] nvmesensor: set default timeout to 20 seconds for
  Security Send and Receive
 
 TCG commands (through Security Send and Receive) may take quite long to
@@ -15,7 +15,7 @@
  1 file changed, 9 insertions(+)
 
 diff --git a/src/NVMeMi.cpp b/src/NVMeMi.cpp
-index 8f73aed..106413d 100644
+index 2cdf1d6..6b93191 100644
 --- a/src/NVMeMi.cpp
 +++ b/src/NVMeMi.cpp
 @@ -13,6 +13,7 @@ std::map<int, std::weak_ptr<NVMeMi::Worker>> NVMeMi::workerMap{};
@@ -25,8 +25,8 @@
 +constexpr int tcgDefaultTimeoutMS = 20*1000;
  
  NVMeMi::NVMeMi(boost::asio::io_context& io, sdbusplus::bus_t& dbus, int bus,
-                int addr) :
-@@ -804,7 +805,11 @@ void NVMeMi::adminSecuritySend(
+                int addr, bool singleThreadMode) :
+@@ -835,7 +836,11 @@ void NVMeMi::adminSecuritySend(
          args.data_len = data.size_bytes();
          args.args_size = sizeof(struct nvme_security_send_args);
  
@@ -38,7 +38,7 @@
          self->io.post([cb{std::move(cb)}, nvme_errno{errno}, status]() {
              auto err = std::make_error_code(static_cast<std::errc>(nvme_errno));
              cb(err, status);
-@@ -844,7 +849,11 @@ void NVMeMi::adminSecurityReceive(
+@@ -875,7 +880,11 @@ void NVMeMi::adminSecurityReceive(
          args.data_len = data.size();
          args.args_size = sizeof(struct nvme_security_receive_args);
  
diff --git a/recipes-phosphor/sensors/dbus-sensors/0032-nvmesensor-close-pipe-file-descriptor-before-excepti.patch b/recipes-phosphor/sensors/dbus-sensors/0037-nvmesensor-close-pipe-file-descriptor-before-excepti.patch
similarity index 82%
rename from recipes-phosphor/sensors/dbus-sensors/0032-nvmesensor-close-pipe-file-descriptor-before-excepti.patch
rename to recipes-phosphor/sensors/dbus-sensors/0037-nvmesensor-close-pipe-file-descriptor-before-excepti.patch
index fd36056..1526390 100644
--- a/recipes-phosphor/sensors/dbus-sensors/0032-nvmesensor-close-pipe-file-descriptor-before-excepti.patch
+++ b/recipes-phosphor/sensors/dbus-sensors/0037-nvmesensor-close-pipe-file-descriptor-before-excepti.patch
@@ -1,7 +1,7 @@
-From 0636c36ff896efde95a0f17fcf39b034e0dadfbd Mon Sep 17 00:00:00 2001
+From 5876fc43d78c6210c4e56bc1c87344cf4ab76a9f Mon Sep 17 00:00:00 2001
 From: Jinliang Wang <jinliangw@google.com>
 Date: Tue, 18 Apr 2023 16:24:38 -0700
-Subject: [PATCH 32/34] nvmesensor: close pipe file descriptor before exception
+Subject: [PATCH 37/44] nvmesensor: close pipe file descriptor before exception
 
 Close pipe file descriptors before throwing exception to prevent
 the potential leak of file desctriptor.
@@ -13,10 +13,10 @@
  1 file changed, 4 insertions(+)
 
 diff --git a/src/NVMeController.cpp b/src/NVMeController.cpp
-index 1c34a58..d534532 100644
+index 5553859..1297bb4 100644
 --- a/src/NVMeController.cpp
 +++ b/src/NVMeController.cpp
-@@ -180,12 +180,16 @@ sdbusplus::message::unix_fd NVMeControllerEnabled::getLogPage(uint8_t lid,
+@@ -193,12 +193,16 @@ sdbusplus::message::unix_fd NVMeControllerEnabled::getLogPage(uint8_t lid,
          }
          else // No VU LogPage handler
          {
diff --git a/recipes-phosphor/sensors/dbus-sensors/0038-nvmesensor-add-Mock-and-Fake-for-SecuSend-Recv.patch b/recipes-phosphor/sensors/dbus-sensors/0038-nvmesensor-add-Mock-and-Fake-for-SecuSend-Recv.patch
new file mode 100644
index 0000000..7017620
--- /dev/null
+++ b/recipes-phosphor/sensors/dbus-sensors/0038-nvmesensor-add-Mock-and-Fake-for-SecuSend-Recv.patch
@@ -0,0 +1,78 @@
+From db1a16ddc6aa943c3b71ab529400cb54c418e44b Mon Sep 17 00:00:00 2001
+From: Hao Jiang <jianghao@google.com>
+Date: Wed, 3 May 2023 01:20:14 +0000
+Subject: [PATCH 38/44] nvmesensor: add Mock and Fake for SecuSend/Recv
+
+Signed-off-by: Hao Jiang <jianghao@google.com>
+Change-Id: Ia990447360e39e10932170dc63086b7f301844a5
+---
+ src/NVMeMiFake.hpp     | 19 +++++++++++++++++++
+ tests/test_nvme_mi.cpp | 15 +++++++++++++++
+ 2 files changed, 34 insertions(+)
+
+diff --git a/src/NVMeMiFake.hpp b/src/NVMeMiFake.hpp
+index 05eafed..989649e 100644
+--- a/src/NVMeMiFake.hpp
++++ b/src/NVMeMiFake.hpp
+@@ -415,6 +415,25 @@ class NVMeMiFake :
+         }
+     }
+ 
++    void adminSecuritySend(
++        [[maybe_unused]] nvme_mi_ctrl_t ctrl, [[maybe_unused]] uint8_t proto,
++        [[maybe_unused]] uint16_t proto_specific,
++        [[maybe_unused]] std::span<uint8_t> data,
++        std::function<void(const std::error_code&, int nvme_status)>&& cb)
++        override
++    {
++        cb(std::make_error_code(std::errc::not_supported), 0);
++    }
++    void adminSecurityReceive(
++        [[maybe_unused]] nvme_mi_ctrl_t ctrl, [[maybe_unused]] uint8_t proto,
++        [[maybe_unused]] uint16_t proto_specific,
++        [[maybe_unused]] uint32_t transfer_length,
++        std::function<void(const std::error_code&, int nvme_status,
++                           const std::span<uint8_t> data)>&& cb) override
++    {
++        cb(std::make_error_code(std::errc::not_supported), 0, {});
++    }
++
+   private:
+     boost::asio::io_context& io;
+     bool valid = false;
+diff --git a/tests/test_nvme_mi.cpp b/tests/test_nvme_mi.cpp
+index 8462d5e..ca3bb15 100644
+--- a/tests/test_nvme_mi.cpp
++++ b/tests/test_nvme_mi.cpp
+@@ -65,6 +65,8 @@ class NVMeMiMock :
+             return fake->adminXfer(ctrl, admin_req, data, timeout_ms,
+                                    std::move(cb));
+             });
++        ON_CALL(*this, adminSecuritySend).WillByDefault([]() { return; });
++        ON_CALL(*this, adminSecurityReceive).WillByDefault([]() { return; });
+     }
+ 
+     MOCK_METHOD(int, getNID, (), (const override));
+@@ -102,6 +104,19 @@ class NVMeMiMock :
+                                     std::span<uint8_t> resp_data)>&& cb),
+                 (override));
+ 
++    MOCK_METHOD(
++        void, adminSecuritySend,
++        (nvme_mi_ctrl_t ctrl, uint8_t proto, uint16_t proto_specific,
++         std::span<uint8_t> data,
++         std::function<void(const std::error_code&, int nvme_status)>&& cb),
++        (override));
++    MOCK_METHOD(void, adminSecurityReceive,
++                (nvme_mi_ctrl_t ctrl, uint8_t proto, uint16_t proto_specific,
++                 uint32_t transfer_length,
++                 std::function<void(const std::error_code&, int nvme_status,
++                                    const std::span<uint8_t> data)>&& cb),
++                (override));
++
+     std::shared_ptr<NVMeMiFake> fake;
+ };
+ 
+-- 
+2.34.1
+
diff --git a/recipes-phosphor/sensors/dbus-sensors/0039-utils-add-PowerCallbackEntry-for-PowerMatch.patch b/recipes-phosphor/sensors/dbus-sensors/0039-utils-add-PowerCallbackEntry-for-PowerMatch.patch
new file mode 100644
index 0000000..336e45b
--- /dev/null
+++ b/recipes-phosphor/sensors/dbus-sensors/0039-utils-add-PowerCallbackEntry-for-PowerMatch.patch
@@ -0,0 +1,250 @@
+From 72cd17a9c6c747cf45dc7ca4946c489113c6e2c5 Mon Sep 17 00:00:00 2001
+From: Hao Jiang <jianghao@google.com>
+Date: Mon, 1 May 2023 21:00:27 +0000
+Subject: [PATCH 39/44] utils: add PowerCallbackEntry for PowerMatch
+
+The PowerMatch callback could be different from intances. For example,
+some nvme vendor links the OOB connectivity to inband Host signal,
+nevertheless other vendor has abolute independent inband and oob
+NVMe connectivity. Thus requires mulitple callback funcitons for
+PowerMatch.
+
+PowerCallbackEntry is the class to manage the callback functions to
+PowerMatch. It is created from setupPowerMatchCallback and unregister
+itself upon deconstruction.
+
+This class is not thread-safe so it should be managed within a single
+thread.
+
+Signed-off-by: Hao Jiang <jianghao@google.com>
+Change-Id: Id33a9c18c18168258c3dfda95db6fed56953bd01
+---
+ src/HwmonTempMain.cpp |  2 +-
+ src/Utils.cpp         | 62 +++++++++++++++++++++++++++++++++----------
+ src/Utils.hpp         | 36 ++++++++++++++++++++++++-
+ 3 files changed, 84 insertions(+), 16 deletions(-)
+
+diff --git a/src/HwmonTempMain.cpp b/src/HwmonTempMain.cpp
+index 340bf11..da26196 100644
+--- a/src/HwmonTempMain.cpp
++++ b/src/HwmonTempMain.cpp
+@@ -661,7 +661,7 @@ int main()
+                           &systemBus](PowerState type, bool state) {
+         powerStateChanged(type, state, sensors, io, objectServer, systemBus);
+     };
+-    setupPowerMatchCallback(systemBus, powerCallBack);
++    auto powerCallBackEntry = setupPowerMatchCallback(systemBus, powerCallBack);
+ 
+     boost::asio::post(io, [&]() {
+         createSensors(io, objectServer, sensors, systemBus, nullptr, false);
+diff --git a/src/Utils.cpp b/src/Utils.cpp
+index d659d37..866f05b 100644
+--- a/src/Utils.cpp
++++ b/src/Utils.cpp
+@@ -46,6 +46,8 @@ static std::unique_ptr<sdbusplus::bus::match_t> powerMatch = nullptr;
+ static std::unique_ptr<sdbusplus::bus::match_t> postMatch = nullptr;
+ static std::unique_ptr<sdbusplus::bus::match_t> chassisMatch = nullptr;
+ 
++std::list<PowerCallbackEntry::callback_t> PowerCallbackEntry::list;
++
+ /**
+  * return the contents of a file
+  * @param[in] hwmonFile - the path to the file to read
+@@ -433,17 +435,20 @@ static void
+         chassis::interface, chassis::property);
+ }
+ 
+-void setupPowerMatchCallback(
++std::unique_ptr<PowerCallbackEntry> setupPowerMatchCallback(
+     const std::shared_ptr<sdbusplus::asio::connection>& conn,
+     std::function<void(PowerState type, bool state)>&& hostStatusCallback)
+ {
+     static boost::asio::steady_timer timer(conn->get_io_context());
+     static boost::asio::steady_timer timerChassisOn(conn->get_io_context());
++
++    auto entry =  std::make_unique<PowerCallbackEntry>(std::move(hostStatusCallback));
++
+     // create a match for powergood changes, first time do a method call to
+     // cache the correct value
+     if (powerMatch)
+     {
+-        return;
++        return entry;
+     }
+ 
+     powerMatch = std::make_unique<sdbusplus::bus::match_t>(
+@@ -451,7 +456,7 @@ void setupPowerMatchCallback(
+         "type='signal',interface='" + std::string(properties::interface) +
+             "',path='" + std::string(power::path) + "',arg0='" +
+             std::string(power::interface) + "'",
+-        [hostStatusCallback](sdbusplus::message_t& message) {
++        [](sdbusplus::message_t& message) {
+         std::string objectName;
+         boost::container::flat_map<std::string, std::variant<std::string>>
+             values;
+@@ -465,13 +470,18 @@ void setupPowerMatchCallback(
+             {
+                 timer.cancel();
+                 powerStatusOn = false;
+-                hostStatusCallback(PowerState::on, powerStatusOn);
++                for (const auto& cb : PowerCallbackEntry::list)
++                {
++                    if (cb)
++                    {
++                        cb(PowerState::on, powerStatusOn);
++                    }
++                }
+                 return;
+             }
+             // on comes too quickly
+             timer.expires_after(std::chrono::seconds(10));
+-            timer.async_wait(
+-                [hostStatusCallback](boost::system::error_code ec) {
++            timer.async_wait([](boost::system::error_code ec) {
+                 if (ec == boost::asio::error::operation_aborted)
+                 {
+                     return;
+@@ -482,7 +492,13 @@ void setupPowerMatchCallback(
+                     return;
+                 }
+                 powerStatusOn = true;
+-                hostStatusCallback(PowerState::on, powerStatusOn);
++                for (const auto& cb : PowerCallbackEntry::list)
++                {
++                    if (cb)
++                    {
++                        cb(PowerState::on, powerStatusOn);
++                    }
++                }
+             });
+         }
+         });
+@@ -492,7 +508,7 @@ void setupPowerMatchCallback(
+         "type='signal',interface='" + std::string(properties::interface) +
+             "',path='" + std::string(post::path) + "',arg0='" +
+             std::string(post::interface) + "'",
+-        [hostStatusCallback](sdbusplus::message_t& message) {
++        [](sdbusplus::message_t& message) {
+         std::string objectName;
+         boost::container::flat_map<std::string, std::variant<std::string>>
+             values;
+@@ -504,7 +520,13 @@ void setupPowerMatchCallback(
+             biosHasPost = (value != "Inactive") &&
+                           (value != "xyz.openbmc_project.State.OperatingSystem."
+                                     "Status.OSStatus.Inactive");
+-            hostStatusCallback(PowerState::biosPost, biosHasPost);
++            for (const auto& cb : PowerCallbackEntry::list)
++            {
++                if (cb)
++                {
++                    cb(PowerState::biosPost, biosHasPost);
++                }
++            }
+         }
+         });
+ 
+@@ -513,7 +535,7 @@ void setupPowerMatchCallback(
+         "type='signal',interface='" + std::string(properties::interface) +
+             "',path='" + std::string(chassis::path) + "',arg0='" +
+             std::string(chassis::interface) + "'",
+-        [hostStatusCallback](sdbusplus::message_t& message) {
++        [](sdbusplus::message_t& message) {
+         std::string objectName;
+         boost::container::flat_map<std::string, std::variant<std::string>>
+             values;
+@@ -527,13 +549,18 @@ void setupPowerMatchCallback(
+             {
+                 timerChassisOn.cancel();
+                 chassisStatusOn = false;
+-                hostStatusCallback(PowerState::chassisOn, chassisStatusOn);
++                for (const auto& cb : PowerCallbackEntry::list)
++                {
++                    if (cb)
++                    {
++                        cb(PowerState::chassisOn, chassisStatusOn);
++                    }
++                }
+                 return;
+             }
+             // on comes too quickly
+             timerChassisOn.expires_after(std::chrono::seconds(10));
+-            timerChassisOn.async_wait(
+-                [hostStatusCallback](boost::system::error_code ec) {
++            timerChassisOn.async_wait([](boost::system::error_code ec) {
+                 if (ec == boost::asio::error::operation_aborted)
+                 {
+                     return;
+@@ -544,13 +571,20 @@ void setupPowerMatchCallback(
+                     return;
+                 }
+                 chassisStatusOn = true;
+-                hostStatusCallback(PowerState::chassisOn, chassisStatusOn);
++                for (const auto& cb : PowerCallbackEntry::list)
++                {
++                    if (cb)
++                    {
++                        cb(PowerState::chassisOn, chassisStatusOn);
++                    }
++                }
+             });
+         }
+         });
+     getPowerStatus(conn);
+     getPostStatus(conn);
+     getChassisStatus(conn);
++    return entry;
+ }
+ 
+ void setupPowerMatch(const std::shared_ptr<sdbusplus::asio::connection>& conn)
+diff --git a/src/Utils.hpp b/src/Utils.hpp
+index 444030c..ea4c241 100644
+--- a/src/Utils.hpp
++++ b/src/Utils.hpp
+@@ -71,9 +71,43 @@ bool findFiles(const std::filesystem::path& dirPath,
+ bool isPowerOn(void);
+ bool hasBiosPost(void);
+ bool isChassisOn(void);
+-void setupPowerMatchCallback(
++
++class PowerCallbackEntry;
++
++std::unique_ptr<PowerCallbackEntry> setupPowerMatchCallback(
+     const std::shared_ptr<sdbusplus::asio::connection>& conn,
+     std::function<void(PowerState type, bool state)>&& callback);
++class PowerCallbackEntry
++{
++  public:
++    using callback_t = std::function<void(PowerState type, bool state)>;
++
++    PowerCallbackEntry()
++    {
++        current = list.insert(list.end(), callback_t{});
++    }
++
++    PowerCallbackEntry(const PowerCallbackEntry&) = delete;
++
++    explicit PowerCallbackEntry(callback_t&& cb)
++    {
++        current = list.insert(list.end(), std::move(cb));
++    }
++
++    ~PowerCallbackEntry()
++    {
++        list.erase(current);
++    }
++
++  private:
++    friend std::unique_ptr<PowerCallbackEntry> setupPowerMatchCallback(
++        const std::shared_ptr<sdbusplus::asio::connection>& conn,
++        std::function<void(PowerState type, bool state)>&& callback);
++
++    static std::list<callback_t> list;
++    decltype(list)::iterator current;
++};
++
+ void setupPowerMatch(const std::shared_ptr<sdbusplus::asio::connection>& conn);
+ bool getSensorConfiguration(
+     const std::string& type,
+-- 
+2.34.1
+
diff --git a/recipes-phosphor/sensors/dbus-sensors/0040-nvmesensor-add-PowerState-to-NVMeMi.patch b/recipes-phosphor/sensors/dbus-sensors/0040-nvmesensor-add-PowerState-to-NVMeMi.patch
new file mode 100644
index 0000000..c0f61db
--- /dev/null
+++ b/recipes-phosphor/sensors/dbus-sensors/0040-nvmesensor-add-PowerState-to-NVMeMi.patch
@@ -0,0 +1,347 @@
+From f13c93e13c8676066fed10c2f122c9923077f39e Mon Sep 17 00:00:00 2001
+From: Hao Jiang <jianghao@google.com>
+Date: Mon, 1 May 2023 21:13:20 +0000
+Subject: [PATCH 40/44] nvmesensor: add PowerState to NVMeMi
+
+Some NVMe vendor implementation link the OOB NVMe-MI connectivity to the
+inband signal. For example, there will be MCTP level reset when inband
+PERST is issued.
+
+The proposed solution is to NVMeMi will close the MCTP ep upon
+PowerState down, and re-init the MCTP when PowerState comes back up.
+
+Configuration for PowerState of each NVMeMi MCTP endpoint is via E-M
+configuration of "PowerState". The default value is always, and the
+configu value is case insensitive.
+
+Signed-off-by: Hao Jiang <jianghao@google.com>
+Change-Id: I7e4c84415fb3568226bc51a03279aeec32a5c612
+---
+ src/NVMeMi.cpp         | 111 +++++++++++++++++++++++++++++++++++------
+ src/NVMeMi.hpp         |  31 ++++++++++--
+ src/NVMeSensorMain.cpp |  48 +++++++++++++++++-
+ 3 files changed, 169 insertions(+), 21 deletions(-)
+
+diff --git a/src/NVMeMi.cpp b/src/NVMeMi.cpp
+index 6b93191..4f0ef12 100644
+--- a/src/NVMeMi.cpp
++++ b/src/NVMeMi.cpp
+@@ -15,11 +15,19 @@ nvme_root_t NVMeMi::nvmeRoot = nvme_mi_create_root(stderr, DEFAULT_LOGLEVEL);
+ constexpr size_t maxNVMeMILength = 4096;
+ constexpr int tcgDefaultTimeoutMS = 20*1000;
+ 
+-NVMeMi::NVMeMi(boost::asio::io_context& io, sdbusplus::bus_t& dbus, int bus,
+-               int addr, bool singleThreadMode) :
++NVMeMi::NVMeMi(boost::asio::io_context& io,
++               std::shared_ptr<sdbusplus::asio::connection> conn, int bus,
++               int addr, bool singleThreadMode, PowerState readState) :
+     io(io),
+-    dbus(dbus)
++    conn(conn), dbus(*conn.get()), bus(bus), addr(addr), readState(readState)
+ {
++    // reset to unassigned nid/eid and endpoint
++    nid = -1;
++    eid = 0;
++    mctpPath.erase();
++    nvmeEP = nullptr;
++
++    // set update the worker thread
+     if (!nvmeRoot)
+     {
+         throw std::runtime_error("invalid NVMe root");
+@@ -51,7 +59,38 @@ NVMeMi::NVMeMi(boost::asio::io_context& io, sdbusplus::bus_t& dbus, int bus,
+         worker = std::make_shared<Worker>();
+     }
+ 
++    // setup the power state
++    if (readState == PowerState::on || readState == PowerState::biosPost ||
++        readState == PowerState::chassisOn)
++    {
++        // life time of the callback is binding to the NVMeMi instance, so only
++        // this capture is required.
++        powerCallback = setupPowerMatchCallback(conn, [this](PowerState, bool) {
++            if (::readingStateGood(this->readState))
++            {
++                initMCTP();
++            }
++            else
++            {
++                closeMCTP();
++            }
++        });
++    }
++
++    // TODO: check the powerstate.
++    initMCTP();
++}
++
++void NVMeMi::initMCTP()
++{
++    // already initiated
++    if (isMCTPconnect())
++    {
++        return;
++    }
++
+     // init mctp ep via mctpd
++    std::unique_lock<std::mutex> lock(mctpMtx);
+     int i = 0;
+     for (;; i++)
+     {
+@@ -79,19 +118,58 @@ NVMeMi::NVMeMi(boost::asio::io_context& io, sdbusplus::bus_t& dbus, int bus,
+             }
+             else
+             {
+-                throw std::runtime_error(e.what());
++                nid = -1;
++                eid = 0;
++                mctpPath.erase();
++                nvmeEP = nullptr;
++                std::cerr << "fail to init MCTP endpoint: " << e.what()
++                          << std::endl;
++                return;
+             }
+         }
+     }
+ 
+     // open mctp endpoint
+     nvmeEP = nvme_mi_open_mctp(nvmeRoot, nid, eid);
+-    if (!nvmeEP)
++    if (nvmeEP == nullptr)
++    {
++        nid = -1;
++        eid = 0;
++        // MCTPd won't expect to delete the ep object, just to erase the record
++        // here.
++        mctpPath.erase();
++        nvmeEP = nullptr;
++        std::cerr << "can't open MCTP endpoint "
++                  << std::to_string(nid) + ":" + std::to_string(eid)
++                  << std::endl;
++    }
++}
++
++void NVMeMi::closeMCTP()
++{
++    // MCTP is disconnected
++    if (!isMCTPconnect())
+     {
+-        throw std::runtime_error("can't open MCTP endpoint " +
+-                                 std::to_string(nid) + ":" +
+-                                 std::to_string(eid));
++        return;
+     }
++    // each nvme mi message transaction should take relatively short time
++    // (typically <= 200 ms). So the blocking time should be short
++    std::unique_lock<std::mutex> lock(mctpMtx);
++
++    nvme_mi_close(nvmeEP);
++
++    // Note: No need to remove MCTP ep from MCTPd since the routing table will
++    // re-establish on the next init
++
++    nid = -1;
++    eid = 0;
++    mctpPath.erase();
++    nvmeEP = nullptr;
++}
++
++bool NVMeMi::isMCTPconnect() const
++{
++    return nid >= 0 && !mctpPath.empty() && nvmeEP != nullptr;
+ }
+ 
+ NVMeMi::Worker::Worker()
+@@ -132,13 +210,7 @@ NVMeMi::Worker::~Worker()
+ }
+ NVMeMi::~NVMeMi()
+ {
+-    // close EP
+-    if (nvmeEP)
+-    {
+-        nvme_mi_close(nvmeEP);
+-    }
+-
+-    // TODO: delete mctp ep from mctpd
++    closeMCTP();
+ }
+ 
+ void NVMeMi::Worker::post(std::function<void(void)>&& func)
+@@ -156,6 +228,15 @@ void NVMeMi::Worker::post(std::function<void(void)>&& func)
+     throw std::runtime_error("NVMeMi has been stopped");
+ }
+ 
++void NVMeMi::post(std::function<void(void)>&& func)
++{
++    worker->post(
++        [self{std::move(shared_from_this())}, func{std::move(func)}]() {
++        std::unique_lock<std::mutex> lock(self->mctpMtx);
++        func();
++    });
++}
++
+ // Calls .post(), catching runtime_error and returning an error code on failure.
+ std::error_code NVMeMi::try_post(std::function<void(void)>&& func)
+ {
+diff --git a/src/NVMeMi.hpp b/src/NVMeMi.hpp
+index 2aa6636..db91bfd 100644
+--- a/src/NVMeMi.hpp
++++ b/src/NVMeMi.hpp
+@@ -1,4 +1,5 @@
+ #include "NVMeIntf.hpp"
++#include "Utils.hpp"
+ 
+ #include <boost/asio.hpp>
+ #include <sdbusplus/bus.hpp>
+@@ -8,8 +9,9 @@
+ class NVMeMi : public NVMeMiIntf, public std::enable_shared_from_this<NVMeMi>
+ {
+   public:
+-    NVMeMi(boost::asio::io_context& io, sdbusplus::bus_t& dbus, int bus,
+-           int addr, bool singleThreadMode = false);
++    NVMeMi(boost::asio::io_context& io, std::shared_ptr<sdbusplus::asio::connection> conn, int bus,
++           int addr, bool singleThreadMode = false,
++           PowerState readState = PowerState::always);
+     ~NVMeMi() override;
+ 
+     int getNID() const override
+@@ -65,13 +67,26 @@ class NVMeMi : public NVMeMiIntf, public std::enable_shared_from_this<NVMeMi>
+     static nvme_root_t nvmeRoot;
+ 
+     boost::asio::io_context& io;
++    std::shared_ptr<sdbusplus::asio::connection> conn;
+     sdbusplus::bus_t& dbus;
++
++    // I2C info
++    int bus;
++    int addr;
++
++    // power state
++    std::unique_ptr<PowerCallbackEntry> powerCallback;
++    PowerState readState;
++
++    // mctp connection
+     nvme_mi_ep_t nvmeEP;
+ 
+     int nid;
+     uint8_t eid;
+     std::string mctpPath;
+ 
++    std::mutex mctpMtx;
++
+     // A worker thread for calling NVMeMI cmd.
+     class Worker
+     {
+@@ -98,9 +113,17 @@ class NVMeMi : public NVMeMiIntf, public std::enable_shared_from_this<NVMeMi>
+     static std::map<int, std::weak_ptr<Worker>> workerMap;
+ 
+     std::shared_ptr<Worker> worker;
+-    void post(std::function<void(void)>&& func)
++    void post(std::function<void(void)>&& func);
++
++    void initMCTP();
++
++    void closeMCTP();
++
++    bool isMCTPconnect() const;
++
++    bool readingStateGood() const
+     {
+-        worker->post(std::move(func));
++        return isMCTPconnect() && ::readingStateGood(readState);
+     }
+ 
+     std::error_code try_post(std::function<void(void)>&& func);
+diff --git a/src/NVMeSensorMain.cpp b/src/NVMeSensorMain.cpp
+index 950988d..d6612f6 100644
+--- a/src/NVMeSensorMain.cpp
++++ b/src/NVMeSensorMain.cpp
+@@ -18,6 +18,7 @@
+ #include "NVMeMi.hpp"
+ #include "NVMeSubsys.hpp"
+ 
++#include <boost/algorithm/string.hpp>
+ #include <boost/asio/steady_timer.hpp>
+ 
+ #include <optional>
+@@ -27,6 +28,9 @@
+ using NVMEMap = std::map<std::string, std::shared_ptr<NVMeSubsystem>>;
+ static NVMEMap nvmeSubsysMap;
+ 
++// flag to set a single worker thread for all nvme eps under the same i2c bus
++static bool singleThreadMode = false;
++
+ static std::optional<int>
+     extractBusNumber(const std::string& path,
+                      const SensorBaseConfigMap& properties)
+@@ -81,6 +85,40 @@ static std::optional<std::string>
+     return std::get<std::string>(findProtocol->second);
+ }
+ 
++static PowerState extractPowerState(const std::string& path,
++                                    const SensorBaseConfigMap& properties)
++{
++    auto find = properties.find("PowerState");
++    if (find == properties.end())
++    {
++        std::cerr << "could not determine configuration of PowerState for "
++                  << path << ", using default\n";
++        // default to always
++        return PowerState::always;
++    }
++    auto res = std::get<std::string>(find->second);
++    if (boost::iequals(res, "on"))
++    {
++        return PowerState::on;
++    }
++    else if (boost::iequals(res, "biosPost"))
++    {
++        return PowerState::biosPost;
++    }
++    else if (boost::iequals(res, "always"))
++    {
++        return PowerState::always;
++    }
++    else if (boost::iequals(res, "chassisOn"))
++    {
++        return PowerState::chassisOn;
++    }
++    // default to always
++    std::cerr << "could not determine config value for PowerState for " << path
++              << ", using default\n";
++    return PowerState::always;
++}
++
+ static void handleConfigurations(
+     boost::asio::io_context& io, sdbusplus::asio::object_server& objectServer,
+     std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
+@@ -164,11 +202,15 @@ static void handleConfigurations(
+             {
+                 address.emplace(0x1d);
+             }
++
++            PowerState powerState =
++                extractPowerState(interfacePath, sensorConfig);
++
+             try
+             {
+                 NVMeIntf nvmeMi = NVMeIntf::create<NVMeMi>(
+-                    io, dynamic_cast<sdbusplus::bus_t&>(*dbusConnection),
+-                    *busNumber, *address);
++                    io, dbusConnection, *busNumber, *address, singleThreadMode,
++                    powerState);
+ 
+                 nvmeInterfaces.emplace(interfacePath, nvmeMi);
+             }
+@@ -265,6 +307,8 @@ static void interfaceRemoved(sdbusplus::message_t& message, NVMEMap& subsystems)
+ 
+ int main()
+ {
++    // TODO: set single thread mode according to input parameters
++
+     boost::asio::io_context io;
+     auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
+     systemBus->request_name("xyz.openbmc_project.NVMe");
+-- 
+2.34.1
+
diff --git a/recipes-phosphor/sensors/dbus-sensors/0041-nvmesensor-Add-print-info-to-NVMeMi.patch b/recipes-phosphor/sensors/dbus-sensors/0041-nvmesensor-Add-print-info-to-NVMeMi.patch
new file mode 100644
index 0000000..bac9f1a
--- /dev/null
+++ b/recipes-phosphor/sensors/dbus-sensors/0041-nvmesensor-Add-print-info-to-NVMeMi.patch
@@ -0,0 +1,482 @@
+From 4bc12cf43841e886b813c039eae9281259fdb3e6 Mon Sep 17 00:00:00 2001
+From: Hao Jiang <jianghao@google.com>
+Date: Tue, 2 May 2023 18:41:24 +0000
+Subject: [PATCH 41/44] nvmesensor: Add print info to NVMeMi
+
+The print info will include the MCTP information so it will be easier to
+debug which MCTP ep NVMe-MI node prints these information.
+
+Signed-off-by: Hao Jiang <jianghao@google.com>
+Change-Id: I9be103b53ed76d33c2185323df60310b877ef557
+---
+ src/NVMeMi.cpp | 175 ++++++++++++++++++++++++++++++++++++-------------
+ 1 file changed, 131 insertions(+), 44 deletions(-)
+
+diff --git a/src/NVMeMi.cpp b/src/NVMeMi.cpp
+index 4f0ef12..10d57a3 100644
+--- a/src/NVMeMi.cpp
++++ b/src/NVMeMi.cpp
+@@ -91,6 +91,8 @@ void NVMeMi::initMCTP()
+ 
+     // init mctp ep via mctpd
+     std::unique_lock<std::mutex> lock(mctpMtx);
++    std::cerr << "[bus: " << bus << ", addr: " << addr << "]"
++              << "start MCTP initialization" << std::endl;
+     int i = 0;
+     for (;; i++)
+     {
+@@ -113,7 +115,8 @@ void NVMeMi::initMCTP()
+         {
+             if (i < 5)
+             {
+-                std::cerr << "retry to SetupEndpoint: " << e.what()
++                std::cerr << "[bus: " << bus << ", addr: " << addr << "]"
++                          << "retry to SetupEndpoint: " << e.what()
+                           << std::endl;
+             }
+             else
+@@ -122,7 +125,8 @@ void NVMeMi::initMCTP()
+                 eid = 0;
+                 mctpPath.erase();
+                 nvmeEP = nullptr;
+-                std::cerr << "fail to init MCTP endpoint: " << e.what()
++                std::cerr << "[bus: " << bus << ", addr: " << addr << "]"
++                          << "fail to init MCTP endpoint: " << e.what()
+                           << std::endl;
+                 return;
+             }
+@@ -139,15 +143,18 @@ void NVMeMi::initMCTP()
+         // here.
+         mctpPath.erase();
+         nvmeEP = nullptr;
+-        std::cerr << "can't open MCTP endpoint "
++        std::cerr << "[bus: " << bus << ", addr: " << addr << "]"
++                  << "can't open MCTP endpoint "
+                   << std::to_string(nid) + ":" + std::to_string(eid)
+                   << std::endl;
+     }
++    std::cerr << "[bus: " << bus << ", addr: " << addr << "]"
++              << "finish MCTP initialization.  "
++              << std::to_string(nid) + ":" + std::to_string(eid) << std::endl;
+ }
+ 
+ void NVMeMi::closeMCTP()
+ {
+-    // MCTP is disconnected
+     if (!isMCTPconnect())
+     {
+         return;
+@@ -155,6 +162,8 @@ void NVMeMi::closeMCTP()
+     // each nvme mi message transaction should take relatively short time
+     // (typically <= 200 ms). So the blocking time should be short
+     std::unique_lock<std::mutex> lock(mctpMtx);
++    std::cerr << "[bus: " << bus << ", addr: " << addr << "]"
++              << "start MCTP closure" << std::endl;
+ 
+     nvme_mi_close(nvmeEP);
+ 
+@@ -165,6 +174,8 @@ void NVMeMi::closeMCTP()
+     eid = 0;
+     mctpPath.erase();
+     nvmeEP = nullptr;
++    std::cerr << "[bus: " << bus << ", addr: " << addr << "]"
++              << "finish MCTP closure." << std::endl;
+ }
+ 
+ bool NVMeMi::isMCTPconnect() const
+@@ -246,7 +257,9 @@ std::error_code NVMeMi::try_post(std::function<void(void)>&& func)
+     }
+     catch (const std::runtime_error& e)
+     {
+-        std::cerr << e.what() << std::endl;
++        std::cerr << "[bus: " << bus << ", addr: " << addr
++                  << ", eid: " << static_cast<int>(eid) << "]" << e.what()
++                  << std::endl;
+         return std::make_error_code(std::errc::no_such_device);
+     }
+     return std::error_code();
+@@ -258,7 +271,9 @@ void NVMeMi::miSubsystemHealthStatusPoll(
+ {
+     if (!nvmeEP)
+     {
+-        std::cerr << "nvme endpoint is invalid" << std::endl;
++        std::cerr << "[bus: " << bus << ", addr: " << addr
++                  << ", eid: " << static_cast<int>(eid) << "]"
++                  << "nvme endpoint is invalid" << std::endl;
+ 
+         io.post([cb{std::move(cb)}]() {
+             cb(std::make_error_code(std::errc::no_such_device), nullptr);
+@@ -275,7 +290,9 @@ void NVMeMi::miSubsystemHealthStatusPoll(
+             if (rc < 0)
+             {
+ 
+-                std::cerr << "fail to subsystem_health_status_poll: "
++                std::cerr << "[bus: " << self->bus << ", addr: " << self->addr
++                          << ", eid: " << static_cast<int>(self->eid) << "]"
++                          << "fail to subsystem_health_status_poll: "
+                           << std::strerror(errno) << std::endl;
+                 self->io.post([cb{std::move(cb)}, last_errno{errno}]() {
+                     cb(std::make_error_code(static_cast<std::errc>(last_errno)),
+@@ -287,7 +304,9 @@ void NVMeMi::miSubsystemHealthStatusPoll(
+             {
+                 std::string_view errMsg =
+                     statusToString(static_cast<nvme_mi_resp_status>(rc));
+-                std::cerr << "fail to subsystem_health_status_poll: " << errMsg
++                std::cerr << "[bus: " << self->bus << ", addr: " << self->addr
++                          << ", eid: " << static_cast<int>(self->eid) << "]"
++                          << "fail to subsystem_health_status_poll: " << errMsg
+                           << std::endl;
+                 self->io.post([cb{std::move(cb)}]() {
+                     cb(std::make_error_code(std::errc::bad_message), nullptr);
+@@ -303,7 +322,9 @@ void NVMeMi::miSubsystemHealthStatusPoll(
+     }
+     catch (const std::runtime_error& e)
+     {
+-        std::cerr << e.what() << std::endl;
++        std::cerr << "[bus: " << bus << ", addr: " << addr
++                  << ", eid: " << static_cast<int>(eid) << "]" << e.what()
++                  << std::endl;
+         io.post([cb{std::move(cb)}]() {
+             cb(std::make_error_code(std::errc::no_such_device), {});
+         });
+@@ -331,7 +352,9 @@ void NVMeMi::miScanCtrl(std::function<void(const std::error_code&,
+             int rc = nvme_mi_scan_ep(self->nvmeEP, true);
+             if (rc < 0)
+             {
+-                std::cerr << "fail to scan controllers: "
++                std::cerr << "[bus: " << self->bus << ", addr: " << self->addr
++                          << ", eid: " << static_cast<int>(self->eid) << "]"
++                          << "fail to scan controllers: "
+                           << std::strerror(errno) << std::endl;
+                 self->io.post([cb{std::move(cb)}, last_errno{errno}]() {
+                     cb(std::make_error_code(static_cast<std::errc>(last_errno)), {});
+@@ -342,7 +365,9 @@ void NVMeMi::miScanCtrl(std::function<void(const std::error_code&,
+             {
+                 std::string_view errMsg =
+                     statusToString(static_cast<nvme_mi_resp_status>(rc));
+-                std::cerr << "fail to scan controllers: " << errMsg
++                std::cerr << "[bus: " << self->bus << ", addr: " << self->addr
++                          << ", eid: " << static_cast<int>(self->eid) << "]"
++                          << "fail to scan controllers: " << errMsg
+                           << std::endl;
+                 self->io.post([cb{std::move(cb)}]() {
+                     cb(std::make_error_code(std::errc::bad_message), {});
+@@ -362,7 +387,9 @@ void NVMeMi::miScanCtrl(std::function<void(const std::error_code&,
+     }
+     catch (const std::runtime_error& e)
+     {
+-        std::cerr << e.what() << std::endl;
++        std::cerr << "[bus: " << bus << ", addr: " << addr
++                  << ", eid: " << static_cast<int>(eid) << "]" << e.what()
++                  << std::endl;
+         io.post([cb{std::move(cb)}]() {
+             cb(std::make_error_code(std::errc::no_such_device), {});
+         });
+@@ -431,7 +458,9 @@ void NVMeMi::adminIdentify(
+             }
+             if (rc < 0)
+             {
+-                std::cerr << "fail to do nvme identify: "
++                std::cerr << "[bus: " << self->bus << ", addr: " << self->addr
++                          << ", eid: " << static_cast<int>(self->eid) << "]"
++                          << "fail to do nvme identify: "
+                           << std::strerror(errno) << std::endl;
+                 self->io.post([cb{std::move(cb)}, last_errno{errno}]() {
+                     cb(std::make_error_code(static_cast<std::errc>(last_errno)), {});
+@@ -442,7 +471,9 @@ void NVMeMi::adminIdentify(
+             {
+                 std::string_view errMsg =
+                     statusToString(static_cast<nvme_mi_resp_status>(rc));
+-                std::cerr << "fail to do nvme identify: " << errMsg
++                std::cerr << "[bus: " << self->bus << ", addr: " << self->addr
++                          << ", eid: " << static_cast<int>(self->eid) << "]"
++                          << "fail to do nvme identify: " << errMsg
+                           << std::endl;
+                 self->io.post([cb{std::move(cb)}]() {
+                     cb(std::make_error_code(std::errc::bad_message), {});
+@@ -458,7 +489,9 @@ void NVMeMi::adminIdentify(
+     }
+     catch (const std::runtime_error& e)
+     {
+-        std::cerr << e.what() << std::endl;
++        std::cerr << "[bus: " << bus << ", addr: " << addr
++                  << ", eid: " << static_cast<int>(eid) << "]" << e.what()
++                  << std::endl;
+         io.post([cb{std::move(cb)}]() {
+             cb(std::make_error_code(std::errc::no_such_device), {});
+         });
+@@ -528,7 +561,9 @@ void NVMeMi::adminGetLogPage(
+ {
+     if (!nvmeEP)
+     {
+-        std::cerr << "nvme endpoint is invalid" << std::endl;
++        std::cerr << "[bus: " << bus << ", addr: " << addr
++                  << ", eid: " << static_cast<int>(eid) << "]"
++                  << "nvme endpoint is invalid" << std::endl;
+         io.post([cb{std::move(cb)}]() {
+             cb(std::make_error_code(std::errc::no_such_device), {});
+         });
+@@ -558,7 +593,10 @@ void NVMeMi::adminGetLogPage(
+                     rc = nvme_mi_admin_get_log_error(ctrl, num, false, log);
+                     if (rc)
+                     {
+-                        std::cerr << "fail to get error log" << std::endl;
++                        std::cerr
++                            << "[bus: " << self->bus << ", addr: " << self->addr
++                            << ", eid: " << static_cast<int>(self->eid) << "]"
++                            << "fail to get error log" << std::endl;
+                         break;
+                     }
+                 }
+@@ -571,7 +609,10 @@ void NVMeMi::adminGetLogPage(
+                     rc = nvme_mi_admin_get_log_smart(ctrl, nsid, false, log);
+                     if (rc)
+                     {
+-                        std::cerr << "fail to get smart log" << std::endl;
++                        std::cerr
++                            << "[bus: " << self->bus << ", addr: " << self->addr
++                            << ", eid: " << static_cast<int>(self->eid) << "]"
++                            << "fail to get smart log" << std::endl;
+                         break;
+                     }
+                 }
+@@ -584,7 +625,10 @@ void NVMeMi::adminGetLogPage(
+                     rc = nvme_mi_admin_get_log_fw_slot(ctrl, false, log);
+                     if (rc)
+                     {
+-                        std::cerr << "fail to get firmware slot" << std::endl;
++                        std::cerr
++                            << "[bus: " << self->bus << ", addr: " << self->addr
++                            << ", eid: " << static_cast<int>(self->eid) << "]"
++                            << "fail to get firmware slot" << std::endl;
+                         break;
+                     }
+                 }
+@@ -601,8 +645,11 @@ void NVMeMi::adminGetLogPage(
+                                                            log);
+                     if (rc)
+                     {
+-                        std::cerr << "fail to get cmd supported and effects log"
+-                                  << std::endl;
++                        std::cerr
++                            << "[bus: " << self->bus << ", addr: " << self->addr
++                            << ", eid: " << static_cast<int>(self->eid) << "]"
++                            << "fail to get cmd supported and effects log"
++                            << std::endl;
+                         break;
+                     }
+                 }
+@@ -615,8 +662,10 @@ void NVMeMi::adminGetLogPage(
+                     rc = nvme_mi_admin_get_log_device_self_test(ctrl, log);
+                     if (rc)
+                     {
+-                        std::cerr << "fail to get device self test log"
+-                                  << std::endl;
++                        std::cerr
++                            << "[bus: " << self->bus << ", addr: " << self->addr
++                            << ", eid: " << static_cast<int>(self->eid) << "]"
++                            << "fail to get device self test log" << std::endl;
+                         break;
+                     }
+                 }
+@@ -630,8 +679,11 @@ void NVMeMi::adminGetLogPage(
+                         nvme_mi_admin_get_log_changed_ns_list(ctrl, false, log);
+                     if (rc)
+                     {
+-                        std::cerr << "fail to get changed namespace list"
+-                                  << std::endl;
++                        std::cerr
++                            << "[bus: " << self->bus << ", addr: " << self->addr
++                            << ", eid: " << static_cast<int>(self->eid) << "]"
++                            << "fail to get changed namespace list"
++                            << std::endl;
+                         break;
+                     }
+                 }
+@@ -655,7 +707,10 @@ void NVMeMi::adminGetLogPage(
+                         }
+                         else
+                         {
+-                            std::cerr << "invalid lsp for telemetry host log"
++                            std::cerr << "[bus: " << self->bus
++                                      << ", addr: " << self->addr << ", eid: "
++                                      << static_cast<int>(self->eid) << "]"
++                                      << "invalid lsp for telemetry host log"
+                                       << std::endl;
+                             rc = -1;
+                             errno = EINVAL;
+@@ -681,9 +736,12 @@ void NVMeMi::adminGetLogPage(
+                         nvme_mi_admin_get_log_reservation(ctrl, false, log);
+                     if (rc)
+                     {
+-                        std::cerr << "fail to get reservation "
+-                                     "notification log"
+-                                  << std::endl;
++                        std::cerr
++                            << "[bus: " << self->bus << ", addr: " << self->addr
++                            << ", eid: " << static_cast<int>(self->eid) << "]"
++                            << "fail to get reservation "
++                               "notification log"
++                            << std::endl;
+                         break;
+                     }
+                 }
+@@ -697,15 +755,20 @@ void NVMeMi::adminGetLogPage(
+                     int rc = nvme_mi_admin_get_log_sanitize(ctrl, false, log);
+                     if (rc)
+                     {
+-                        std::cerr << "fail to get sanitize status log"
+-                                  << std::endl;
++                        std::cerr
++                            << "[bus: " << self->bus << ", addr: " << self->addr
++                            << ", eid: " << static_cast<int>(self->eid) << "]"
++                            << "fail to get sanitize status log" << std::endl;
+                         break;
+                     }
+                 }
+                 break;
+                 default:
+                 {
+-                    std::cerr << "unknown lid for GetLogPage" << std::endl;
++                    std::cerr << "[bus: " << self->bus
++                              << ", addr: " << self->addr
++                              << ", eid: " << static_cast<int>(self->eid) << "]"
++                              << "unknown lid for GetLogPage" << std::endl;
+                     rc = -1;
+                     errno = EINVAL;
+                 }
+@@ -713,7 +776,9 @@ void NVMeMi::adminGetLogPage(
+ 
+             if (rc < 0)
+             {
+-                std::cerr << "fail to get log page: " << std::strerror(errno)
++                std::cerr << "[bus: " << self->bus << ", addr: " << self->addr
++                          << ", eid: " << static_cast<int>(self->eid) << "]"
++                          << "fail to get log page: " << std::strerror(errno)
+                           << std::endl;
+                 self->io.post([cb{std::move(cb)}, last_errno{errno}]() {
+                     cb(std::make_error_code(static_cast<std::errc>(last_errno)), {});
+@@ -724,7 +789,9 @@ void NVMeMi::adminGetLogPage(
+             {
+                 std::string_view errMsg =
+                     statusToString(static_cast<nvme_mi_resp_status>(rc));
+-                std::cerr << "fail to get log pag: " << errMsg << std::endl;
++                std::cerr << "[bus: " << self->bus << ", addr: " << self->addr
++                          << ", eid: " << static_cast<int>(self->eid) << "]"
++                          << "fail to get log pag: " << errMsg << std::endl;
+                 self->io.post([cb{std::move(cb)}]() {
+                     cb(std::make_error_code(std::errc::bad_message), {});
+                     return;
+@@ -739,7 +806,9 @@ void NVMeMi::adminGetLogPage(
+     }
+     catch (const std::runtime_error& e)
+     {
+-        std::cerr << "NVMeMi adminGetLogPage throws: " << e.what() << std::endl;
++        std::cerr << "[bus: " << bus << ", addr: " << addr
++                  << ", eid: " << static_cast<int>(eid) << "]"
++                  << "NVMeMi adminGetLogPage throws: " << e.what() << std::endl;
+         io.post([cb{std::move(cb)}]() {
+             cb(std::make_error_code(std::errc::no_such_device), {});
+         });
+@@ -755,7 +824,9 @@ void NVMeMi::adminXfer(
+ {
+     if (!nvmeEP)
+     {
+-        std::cerr << "nvme endpoint is invalid" << std::endl;
++        std::cerr << "[bus: " << bus << ", addr: " << addr
++                  << ", eid: " << static_cast<int>(eid) << "]"
++                  << "nvme endpoint is invalid" << std::endl;
+         io.post([cb{std::move(cb)}]() {
+             cb(std::make_error_code(std::errc::no_such_device), {}, {});
+         });
+@@ -795,7 +866,9 @@ void NVMeMi::adminXfer(
+ 
+             if (rc < 0)
+             {
+-                std::cerr << "failed to nvme_mi_admin_xfer" << std::endl;
++                std::cerr << "[bus: " << self->bus << ", addr: " << self->addr
++                          << ", eid: " << static_cast<int>(self->eid) << "]"
++                          << "failed to nvme_mi_admin_xfer" << std::endl;
+                 self->io.post([cb{std::move(cb)}, last_errno{errno}]() {
+                     cb(std::make_error_code(static_cast<std::errc>(last_errno)), {},
+                        {});
+@@ -818,7 +891,9 @@ void NVMeMi::adminXfer(
+     }
+     catch (const std::runtime_error& e)
+     {
+-        std::cerr << e.what() << std::endl;
++        std::cerr << "[bus: " << bus << ", addr: " << addr
++                  << ", eid: " << static_cast<int>(eid) << "]" << e.what()
++                  << std::endl;
+         io.post([cb{std::move(cb)}]() {
+             cb(std::make_error_code(std::errc::no_such_device), {}, {});
+         });
+@@ -832,7 +907,9 @@ void NVMeMi::adminFwCommit(
+ {
+     if (!nvmeEP)
+     {
+-        std::cerr << "nvme endpoint is invalid" << std::endl;
++        std::cerr << "[bus: " << bus << ", addr: " << addr
++                  << ", eid: " << static_cast<int>(eid) << "]"
++                  << "nvme endpoint is invalid" << std::endl;
+         io.post([cb{std::move(cb)}]() {
+             cb(std::make_error_code(std::errc::no_such_device),
+                nvme_status_field::NVME_SC_MASK);
+@@ -853,7 +930,9 @@ void NVMeMi::adminFwCommit(
+             if (rc < 0)
+             {
+ 
+-                std::cerr << "fail to nvme_mi_admin_fw_commit: "
++                std::cerr << "[bus: " << self->bus << ", addr: " << self->addr
++                          << ", eid: " << static_cast<int>(self->eid) << "]"
++                          << "fail to nvme_mi_admin_fw_commit: "
+                           << std::strerror(errno) << std::endl;
+                 self->io.post([cb{std::move(cb)}, last_errno{errno}]() {
+                     cb(std::make_error_code(static_cast<std::errc>(last_errno)),
+@@ -890,7 +969,9 @@ void NVMeMi::adminFwCommit(
+     }
+     catch (const std::runtime_error& e)
+     {
+-        std::cerr << e.what() << std::endl;
++        std::cerr << "[bus: " << bus << ", addr: " << addr
++                  << ", eid: " << static_cast<int>(eid) << "]" << e.what()
++                  << std::endl;
+         io.post([cb{std::move(cb)}]() {
+             cb(std::make_error_code(std::errc::no_such_device),
+                nvme_status_field::NVME_SC_MASK);
+@@ -929,7 +1010,9 @@ void NVMeMi::adminSecuritySend(
+     });
+     if (post_err)
+     {
+-        std::cerr << "adminSecuritySend post failed: " << post_err << std::endl;
++        std::cerr << "[bus: " << bus << ", addr: " << addr
++                  << ", eid: " << static_cast<int>(eid) << "]"
++                  << "adminSecuritySend post failed: " << post_err << std::endl;
+         io.post([cb{std::move(cb)}, post_err]() { cb(post_err, -1); });
+     }
+ }
+@@ -968,7 +1051,9 @@ void NVMeMi::adminSecurityReceive(
+ 
+         if (args.data_len > maxNVMeMILength)
+         {
+-            std::cerr << "nvme_mi_admin_security_send returned excess data, "
++            std::cerr << "[bus: " << self->bus << ", addr: " << self->addr
++                      << ", eid: " << static_cast<int>(self->eid) << "]"
++                      << "nvme_mi_admin_security_send returned excess data, "
+                       << args.data_len << std::endl;
+             self->io.post([cb]() {
+                 cb(std::make_error_code(std::errc::protocol_error), -1, {});
+@@ -986,7 +1071,9 @@ void NVMeMi::adminSecurityReceive(
+     });
+     if (post_err)
+     {
+-        std::cerr << "adminSecurityReceive post failed: " << post_err
++        std::cerr << "[bus: " << bus << ", addr: " << addr
++                  << ", eid: " << static_cast<int>(eid) << "]"
++                  << "adminSecurityReceive post failed: " << post_err
+                   << std::endl;
+         io.post([cb{std::move(cb)}, post_err]() { cb(post_err, -1, {}); });
+     }
+-- 
+2.34.1
+
diff --git a/recipes-phosphor/sensors/dbus-sensors/0042-nvmesensor-chuck-fetch-for-telemetry-logs.patch b/recipes-phosphor/sensors/dbus-sensors/0042-nvmesensor-chuck-fetch-for-telemetry-logs.patch
new file mode 100644
index 0000000..db3dc27
--- /dev/null
+++ b/recipes-phosphor/sensors/dbus-sensors/0042-nvmesensor-chuck-fetch-for-telemetry-logs.patch
@@ -0,0 +1,294 @@
+From 3d9e8f7a030d6a3b1baa397b391d3c763919ed5d Mon Sep 17 00:00:00 2001
+From: Hao Jiang <jianghao@google.com>
+Date: Fri, 2 Jun 2023 23:29:45 +0000
+Subject: [PATCH 42/44] nvmesensor: chuck fetch for telemetry logs
+
+The telemetry logs could be larger than megabytes so fetch the whole log
+at one shot could block the other transactions on the same bus.
+
+This change slices the transaction into 4kB chucks and repush the
+remaining tasks into the worker thread so other tasks (such as health
+poll) can be scheduled in the middle.
+
+Signed-off-by: Hao Jiang <jianghao@google.com>
+Change-Id: I4f5e88b73cbb850954ea39fde185bb4016b8e25c
+---
+ src/NVMeMi.cpp | 174 +++++++++++++++++++++++++++++++++----------------
+ src/NVMeMi.hpp |   7 +-
+ 2 files changed, 123 insertions(+), 58 deletions(-)
+
+diff --git a/src/NVMeMi.cpp b/src/NVMeMi.cpp
+index 10d57a3..60595e8 100644
+--- a/src/NVMeMi.cpp
++++ b/src/NVMeMi.cpp
+@@ -148,6 +148,9 @@ void NVMeMi::initMCTP()
+                   << std::to_string(nid) + ":" + std::to_string(eid)
+                   << std::endl;
+     }
++    // TODO: make a flag to indicate the next health poll should return
++    // no_such_device error. This is to inform the subsystem that the connected
++    // has been reset or hot-swapped.
+     std::cerr << "[bus: " << bus << ", addr: " << addr << "]"
+               << "finish MCTP initialization.  "
+               << std::to_string(nid) + ":" + std::to_string(eid) << std::endl;
+@@ -508,18 +511,15 @@ static int nvme_mi_admin_get_log_telemetry_host_rae(nvme_mi_ctrl_t ctrl,
+ 
+ // Get Temetery Log header and return the size for hdr + data area (Area 1, 2,
+ // 3, or maybe 4)
+-int getTelemetryLog(nvme_mi_ctrl_t ctrl, bool host, bool create,
+-                    std::vector<uint8_t>& data)
++int getTelemetryLogSize(nvme_mi_ctrl_t ctrl, bool host, uint32_t& size)
+ {
+     int rc = 0;
+-    data.resize(sizeof(nvme_telemetry_log));
+-    nvme_telemetry_log& log =
+-        *reinterpret_cast<nvme_telemetry_log*>(data.data());
++    nvme_telemetry_log log;
+     auto func = host ? nvme_mi_admin_get_log_telemetry_host_rae
+                      : nvme_mi_admin_get_log_telemetry_ctrl;
+ 
+     // Only host telemetry log requires create.
+-    if (host && create)
++    if (host)
+     {
+         rc = nvme_mi_admin_get_log_create_telemetry_host(ctrl, &log);
+         if (rc)
+@@ -527,7 +527,6 @@ int getTelemetryLog(nvme_mi_ctrl_t ctrl, bool host, bool create,
+             std::cerr << "failed to create telemetry host log" << std::endl;
+             return rc;
+         }
+-        return 0;
+     }
+ 
+     rc = func(ctrl, false, 0, sizeof(log), &log);
+@@ -539,19 +538,93 @@ int getTelemetryLog(nvme_mi_ctrl_t ctrl, bool host, bool create,
+         return rc;
+     }
+ 
+-    long size =
+-        static_cast<long>((boost::endian::little_to_native(log.dalb3) + 1)) *
+-        NVME_LOG_TELEM_BLOCK_SIZE;
++    size = static_cast<uint32_t>(
++               (boost::endian::little_to_native(log.dalb3) + 1)) *
++           NVME_LOG_TELEM_BLOCK_SIZE;
++    return rc;
++}
+ 
+-    data.resize(size);
+-    rc = func(ctrl, false, 0, data.size(), data.data());
+-    if (rc)
++void NVMeMi::getTelemetryLogChuck(
++    nvme_mi_ctrl_t ctrl, bool host, uint64_t offset,
++    std::vector<uint8_t>&& data,
++    std::function<void(const std::error_code&, std::span<uint8_t>)>&& cb)
++{
++
++    if (offset >= data.size())
+     {
+-        std::cerr << "failed to get full telemetry log for "
+-                  << (host ? "host" : "ctrl") << std::endl;
+-        return rc;
++
++        std::cerr << "[bus: " << bus << ", addr: " << addr
++                  << ", eid: " << static_cast<int>(eid) << "]"
++                  << "get telemetry log: offset exceed the log size. "
++                  << "offset: " << offset << ", size: " << data.size()
++                  << std::endl;
++        cb(std::make_error_code(std::errc::invalid_argument), {});
++        return;
+     }
+-    return 0;
++
++    post([self{shared_from_this()}, ctrl, host, offset, data{std::move(data)},
++          cb{std::move(cb)}]() mutable {
++        int rc = 0;
++        bool rae = 1;
++        auto func = host ? nvme_mi_admin_get_log_telemetry_host_rae
++                         : nvme_mi_admin_get_log_telemetry_ctrl;
++        uint32_t size = 0;
++
++        // final transaction
++        if (offset + nvme_mi_xfer_size >= data.size())
++        {
++            rae = 0;
++        }
++        size = std::min(static_cast<uint32_t>(nvme_mi_xfer_size),
++                        static_cast<uint32_t>(data.size() - offset));
++
++        rc = func(ctrl, rae, offset, size, data.data() + offset);
++
++        if (rc < 0)
++        {
++            std::cerr << "[bus: " << self->bus << ", addr: " << self->addr
++                      << ", eid: " << static_cast<int>(self->eid) << "]"
++                      << "fail to get chuck for telemetry log: "
++                      << std::strerror(errno) << std::endl;
++            boost::asio::post(self->io,
++                              [cb{std::move(cb)}, last_errno{errno}]() {
++                cb(std::make_error_code(static_cast<std::errc>(last_errno)),
++                   {});
++            });
++            return;
++        }
++        else if (rc > 0)
++        {
++            std::string_view errMsg =
++                statusToString(static_cast<nvme_mi_resp_status>(rc));
++            std::cerr << "[bus: " << self->bus << ", addr: " << self->addr
++                      << ", eid: " << static_cast<int>(self->eid) << "]"
++                      << "fail to get chuck for telemetry log: " << errMsg
++                      << std::endl;
++            boost::asio::post(self->io, [cb{std::move(cb)}]() {
++                cb(std::make_error_code(std::errc::bad_message), {});
++            });
++            return;
++        }
++
++        if (rae == 0)
++        {
++            boost::asio::post(
++                self->io, [cb{std::move(cb)}, data{std::move(data)}]() mutable {
++                    std::span<uint8_t> span{data.data(), data.size()};
++                    cb({}, span);
++                });
++            return;
++        }
++
++        offset += size;
++        boost::asio::post(self->io,
++                          [self, ctrl, host, offset, data{std::move(data)},
++                           cb{std::move(cb)}]() mutable {
++            self->getTelemetryLogChuck(ctrl, host, offset, std::move(data),
++                                       std::move(cb));
++        });
++    });
+ }
+ 
+ void NVMeMi::adminGetLogPage(
+@@ -575,7 +648,7 @@ void NVMeMi::adminGetLogPage(
+         post([ctrl, nsid, lid, lsp, lsi, self{shared_from_this()},
+               cb{std::move(cb)}]() {
+             std::vector<uint8_t> data;
+-
++            std::function<void(void)> logHandler;
+             int rc = 0;
+             switch (lid)
+             {
+@@ -692,37 +765,20 @@ void NVMeMi::adminGetLogPage(
+                 // fall through to NVME_LOG_LID_TELEMETRY_CTRL
+                 case NVME_LOG_LID_TELEMETRY_CTRL:
+                 {
+-                    bool host = false;
+-                    bool create = false;
+-                    if (lid == NVME_LOG_LID_TELEMETRY_HOST)
+-                    {
+-                        host = true;
+-                        if (lsp == NVME_LOG_TELEM_HOST_LSP_CREATE)
+-                        {
+-                            create = true;
+-                        }
+-                        else if (lsp == NVME_LOG_TELEM_HOST_LSP_RETAIN)
+-                        {
+-                            create = false;
+-                        }
+-                        else
+-                        {
+-                            std::cerr << "[bus: " << self->bus
+-                                      << ", addr: " << self->addr << ", eid: "
+-                                      << static_cast<int>(self->eid) << "]"
+-                                      << "invalid lsp for telemetry host log"
+-                                      << std::endl;
+-                            rc = -1;
+-                            errno = EINVAL;
+-                            break;
+-                        }
+-                    }
+-                    else
++                    bool host =
++                        (lid == NVME_LOG_LID_TELEMETRY_HOST) ? true : false;
++
++                    uint32_t size = 0;
++                    rc = getTelemetryLogSize(ctrl, host, size);
++                    if (rc == 0)
+                     {
+-                        host = false;
++                        data.resize(size);
++                        logHandler = [self, ctrl, host, data{std::move(data)},
++                                      cb{std::move(cb)}]() mutable {
++                            self->getTelemetryLogChuck(
++                                ctrl, host, 0, std::move(data), std::move(cb));
++                        };
+                     }
+-
+-                    rc = getTelemetryLog(ctrl, host, create, data);
+                 }
+                 break;
+                 case NVME_LOG_LID_RESERVATION:
+@@ -780,10 +836,9 @@ void NVMeMi::adminGetLogPage(
+                           << ", eid: " << static_cast<int>(self->eid) << "]"
+                           << "fail to get log page: " << std::strerror(errno)
+                           << std::endl;
+-                self->io.post([cb{std::move(cb)}, last_errno{errno}]() {
++                logHandler = [cb{std::move(cb)}, last_errno{errno}]() {
+                     cb(std::make_error_code(static_cast<std::errc>(last_errno)), {});
+-                });
+-                return;
++                };
+             }
+             else if (rc > 0)
+             {
+@@ -792,16 +847,21 @@ void NVMeMi::adminGetLogPage(
+                 std::cerr << "[bus: " << self->bus << ", addr: " << self->addr
+                           << ", eid: " << static_cast<int>(self->eid) << "]"
+                           << "fail to get log pag: " << errMsg << std::endl;
+-                self->io.post([cb{std::move(cb)}]() {
++                logHandler = [cb{std::move(cb)}]() {
+                     cb(std::make_error_code(std::errc::bad_message), {});
+-                    return;
+-                });
++                };
+             }
+ 
+-            self->io.post([cb{std::move(cb)}, data{std::move(data)}]() mutable {
+-                std::span<uint8_t> span{data.data(), data.size()};
+-                cb({}, span);
+-            });
++            if (!logHandler)
++            {
++                logHandler =
++                    [cb{std::move(cb)}, data{std::move(data)}]() mutable {
++                    std::span<uint8_t> span{data.data(), data.size()};
++                    cb({}, span);
++                };
++            }
++            boost::asio::post(self->io, logHandler);
++
+         });
+     }
+     catch (const std::runtime_error& e)
+diff --git a/src/NVMeMi.hpp b/src/NVMeMi.hpp
+index db91bfd..0a86dcf 100644
+--- a/src/NVMeMi.hpp
++++ b/src/NVMeMi.hpp
+@@ -62,7 +62,7 @@ class NVMeMi : public NVMeMiIntf, public std::enable_shared_from_this<NVMeMi>
+   private:
+     // the transfer size for nvme mi messages.
+     // define in github.com/linux-nvme/libnvme/blob/master/src/nvme/mi.c
+-    static constexpr int nvme_mi_xfer_size = 4096;
++    static constexpr size_t nvme_mi_xfer_size = 4096;
+ 
+     static nvme_root_t nvmeRoot;
+ 
+@@ -127,4 +127,9 @@ class NVMeMi : public NVMeMiIntf, public std::enable_shared_from_this<NVMeMi>
+     }
+ 
+     std::error_code try_post(std::function<void(void)>&& func);
++
++    void getTelemetryLogChuck(
++        nvme_mi_ctrl_t ctrl, bool host, uint64_t offset,
++        std::vector<uint8_t>&& data,
++        std::function<void(const std::error_code&, std::span<uint8_t>)>&& cb);
+ };
+-- 
+2.34.1
+
diff --git a/recipes-phosphor/sensors/dbus-sensors/0043-nvmesensor-fix-plugin-issue.patch b/recipes-phosphor/sensors/dbus-sensors/0043-nvmesensor-fix-plugin-issue.patch
new file mode 100644
index 0000000..4e17759
--- /dev/null
+++ b/recipes-phosphor/sensors/dbus-sensors/0043-nvmesensor-fix-plugin-issue.patch
@@ -0,0 +1,32 @@
+From a4eab6069de0aa5764e942e17ed37f9a8f2da1f3 Mon Sep 17 00:00:00 2001
+From: Hao Jiang <jianghao@google.com>
+Date: Fri, 23 Jun 2023 23:50:14 +0000
+Subject: [PATCH 43/44] nvmesensor: fix plugin issue
+
+The plugin should not be reset when the NVMeSubsystem is marked as not
+functional. Given the plugin is only created with NVMeSubsystem, so the
+lifetime of plugin should be same as NVMeSubsystem or start to recycle
+with NVMeSubsystem destructor.
+
+Signed-off-by: Hao Jiang <jianghao@google.com>
+Change-Id: I4b2abc9b534adf9a96747a478f4351b349fa138b
+---
+ src/NVMeSubsys.cpp | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/NVMeSubsys.cpp b/src/NVMeSubsys.cpp
+index cd10a75..2499fb7 100644
+--- a/src/NVMeSubsys.cpp
++++ b/src/NVMeSubsys.cpp
+@@ -98,7 +98,7 @@ void NVMeSubsystem::markFunctional(bool toggle)
+         // is enabled
+ 
+         controllers.clear();
+-        plugin.reset();
++        // plugin.reset();
+         return;
+     }
+     if (status == Status::Intiatilzing)
+-- 
+2.34.1
+
diff --git a/recipes-phosphor/sensors/dbus-sensors/0044-nvmesensor-add-markAvailable-with-adaptive-timer.patch b/recipes-phosphor/sensors/dbus-sensors/0044-nvmesensor-add-markAvailable-with-adaptive-timer.patch
new file mode 100644
index 0000000..54263da
--- /dev/null
+++ b/recipes-phosphor/sensors/dbus-sensors/0044-nvmesensor-add-markAvailable-with-adaptive-timer.patch
@@ -0,0 +1,227 @@
+From e2961d04d4ba202047d1e53fc4b52a3c6483d406 Mon Sep 17 00:00:00 2001
+From: Hao Jiang <jianghao@google.com>
+Date: Sat, 24 Jun 2023 05:29:07 +0000
+Subject: [PATCH 44/44] nvmesensor: add markAvailable with adaptive timer
+
+Added markAvailable() function for NVMeSubsys. When the subsystem health
+poll detects the device absence due to various reasons. The subsystem
+can be marked as unavailable.
+
+Current function during subsystem unavaibility is to reducing the
+polling cadence, because an absent device can no longer report the
+health data and it usually takes time for the communication/NVMe device
+to recovery from absence.
+
+Furthur task for markAvailable() will include a dbus interface to inform
+the status of NVMe device (subsystem).
+
+Signed-off-by: Hao Jiang <jianghao@google.com>
+Change-Id: Ie69b0dee23685d4cf9f712894d396f975c3419b0
+---
+ src/NVMeSubsys.cpp | 67 ++++++++++++++++++++++++++++++++++++++--------
+ src/NVMeSubsys.hpp |  7 +++++
+ 2 files changed, 63 insertions(+), 11 deletions(-)
+
+diff --git a/src/NVMeSubsys.cpp b/src/NVMeSubsys.cpp
+index 2499fb7..63e3426 100644
+--- a/src/NVMeSubsys.cpp
++++ b/src/NVMeSubsys.cpp
+@@ -81,6 +81,16 @@ NVMeSubsystem::~NVMeSubsystem()
+ 
+ void NVMeSubsystem::markFunctional(bool toggle)
+ {
++    if (ctemp)
++    {
++        ctemp->markFunctional(toggle);
++    }
++
++    if (nvmeIntf.getProtocol() == NVMeIntf::Protocol::NVMeBasic)
++    {
++        return;
++    }
++
+     // disable the subsystem
+     if (!toggle)
+     {
+@@ -106,6 +116,7 @@ void NVMeSubsystem::markFunctional(bool toggle)
+         throw std::runtime_error("cannot start: the subsystem is intiatilzing");
+     }
+     status = Status::Intiatilzing;
++    markAvailable(toggle);
+ 
+     // add controllers for the subsystem
+     if (nvmeIntf.getProtocol() == NVMeIntf::Protocol::NVMeMI)
+@@ -123,6 +134,7 @@ void NVMeSubsystem::markFunctional(bool toggle)
+                           << (ec ? ": " + ec.message() : "") << std::endl;
+                 self->status = Status::Stop;
+                 self->markFunctional(false);
++                self->markAvailable(false);
+                 return;
+             }
+ 
+@@ -195,6 +207,7 @@ void NVMeSubsystem::markFunctional(bool toggle)
+                               << std::endl;
+                     self->status = Status::Stop;
+                     self->markFunctional(false);
++                    self->markAvailable(false);
+                     return;
+                 }
+                 nvme_secondary_ctrl_list& listHdr =
+@@ -212,6 +225,7 @@ void NVMeSubsystem::markFunctional(bool toggle)
+                               << std::endl;
+                     self->status = Status::Stop;
+                     self->markFunctional(false);
++                    self->markAvailable(false);
+                     return;
+                 }
+ 
+@@ -226,6 +240,7 @@ void NVMeSubsystem::markFunctional(bool toggle)
+                               << std::endl;
+                     self->status = Status::Stop;
+                     self->markFunctional(false);
++                    self->markAvailable(false);
+                     return;
+                 }
+ 
+@@ -277,6 +292,27 @@ void NVMeSubsystem::markFunctional(bool toggle)
+     }
+ }
+ 
++void NVMeSubsystem::markAvailable(bool toggle)
++{
++    if (ctemp)
++    {
++        ctemp->markAvailable(toggle);
++    }
++
++    if (nvmeIntf.getProtocol() == NVMeIntf::Protocol::NVMeBasic)
++    {
++        return;
++    }
++
++    if (toggle)
++    {
++        // TODO: make the Available interface true
++        UnavailableCount = 0;
++        return;
++    }
++    // TODO: make the Available interface false
++    UnavailableCount = UnavailableMaxCount;
++}
+ void NVMeSubsystem::start()
+ {
+     // add thermal sensor for the subsystem
+@@ -331,8 +367,8 @@ void NVMeSubsystem::start()
+             {
+                 std::cerr << "error reading ctemp from subsystem"
+                           << ", reason:" << error.message() << "\n";
+-                self->ctemp->markFunctional(false);
+-                self->ctemp->markAvailable(false);
++                self->markFunctional(false);
++                self->markAvailable(false);
+                 return;
+             }
+             // other communication errors
+@@ -347,7 +383,7 @@ void NVMeSubsystem::start()
+             if (status == nullptr)
+             {
+                 std::cerr << "empty data returned by data fetcher" << std::endl;
+-                self->ctemp->markFunctional(false);
++                self->markFunctional(false);
+                 return;
+             }
+ 
+@@ -357,7 +393,7 @@ void NVMeSubsystem::start()
+                 ((flags & NVMeBasicIntf::StatusFlags::
+                               NVME_MI_BASIC_SFLGS_DRIVE_FUNCTIONAL) == 0))
+             {
+-                self->ctemp->markFunctional(false);
++                self->markFunctional(false);
+                 return;
+             }
+             self->ctemp->updateValue(getTemperatureReading(status->Temp));
+@@ -375,6 +411,14 @@ void NVMeSubsystem::start()
+             [intf, self{std::move(shared_from_this())}](
+                 std::function<void(const std::error_code&,
+                                    nvme_mi_nvm_ss_health_status*)>&& cb) {
++            // do not poll the health status if subsystem is in cooldown
++            if (self->UnavailableCount > 0)
++            {
++                cb(std::make_error_code(std::errc::operation_canceled),
++                   nullptr);
++                return;
++            }
++
+             // do not poll the health status if the subsystem is intiatilzing
+             if (self->status == Status::Intiatilzing)
+             {
+@@ -389,6 +433,12 @@ void NVMeSubsystem::start()
+         ctemp_process_t<nvme_mi_nvm_ss_health_status*> dataProcessor =
+             [self{shared_from_this()}](const std::error_code& error,
+                                        nvme_mi_nvm_ss_health_status* status) {
++            if (self->UnavailableCount > 0)
++            {
++                self->UnavailableCount--;
++                return;
++            }
++
+             if (error == std::errc::operation_canceled ||
+                 self->status == Status::Intiatilzing)
+             {
+@@ -406,8 +456,7 @@ void NVMeSubsystem::start()
+                           << ", reason:" << error.message() << "\n";
+                 // stop the subsystem
+                 self->markFunctional(false);
+-                self->ctemp->markFunctional(false);
+-                self->ctemp->markAvailable(false);
++                self->markAvailable(false);
+ 
+                 return;
+             }
+@@ -420,7 +469,7 @@ void NVMeSubsystem::start()
+                 if (self->ctemp->inError())
+                 {
+                     self->markFunctional(false);
+-                    self->ctemp->markFunctional(false);
++                    self->markAvailable(false);
+                 }
+                 return;
+             }
+@@ -430,10 +479,7 @@ void NVMeSubsystem::start()
+             if (!df)
+             {
+                 // stop the subsystem
+-
+                 self->markFunctional(false);
+-                self->ctemp->markFunctional(false);
+-
+                 return;
+             }
+ 
+@@ -460,7 +506,6 @@ void NVMeSubsystem::stop()
+     {
+         std::cerr << "status start" << std::endl;
+         markFunctional(false);
+-        ctemp->markFunctional(false);
+     }
+     else if (status == Status::Intiatilzing)
+     {
+diff --git a/src/NVMeSubsys.hpp b/src/NVMeSubsys.hpp
+index 6bd8b62..dd9dbfa 100644
+--- a/src/NVMeSubsys.hpp
++++ b/src/NVMeSubsys.hpp
+@@ -74,4 +74,11 @@ class NVMeSubsystem : public std::enable_shared_from_this<NVMeSubsystem>
+     // make the subsystem functional/functional be enabling/disabling the
+     // storage controller, namespaces and thermal sensors.
+     void markFunctional(bool toggle);
++
++    // mark the availability of the Storage device.
++    void markAvailable(bool toggle);
++
++    // a counter to skip health poll when NVMe subsystem becomes Unavailable
++    unsigned UnavailableCount = 0;
++    static constexpr unsigned UnavailableMaxCount = 60;
+ };
+-- 
+2.34.1
+
diff --git a/recipes-phosphor/sensors/dbus-sensors_%.bbappend b/recipes-phosphor/sensors/dbus-sensors_%.bbappend
index 2324763..e1f0cf0 100644
--- a/recipes-phosphor/sensors/dbus-sensors_%.bbappend
+++ b/recipes-phosphor/sensors/dbus-sensors_%.bbappend
@@ -14,39 +14,50 @@
 
 # gBMC NVMe-MI support begins
 nvmesensor_patches = " \
-  file://0001-nvme-sensor-refactor-the-code.patch \
-  file://0002-NVMe-sensor-add-Storage-and-Drive-interface.patch \
-  file://0003-nvmesensor-Add-NVMe-MI-protocol-and-controller.patch \
-  file://0004-Enable-ctemp-sensor-for-nvme-mi.patch \
-  file://0005-nvmesensor-Add-Identify-and-cntrl-association.patch \
-  file://0006-Add-NVMeAdmin-intf-with-GetLogPage.patch \
-  file://0007-Add-more-logs-to-NVMe-daemon.patch \
-  file://0008-Add-Identify-method-to-NVMeAdmin-interface.patch \
-  file://0009-nvmesensor-add-adminXfer-for-MI-interface.patch \
-  file://0010-nvmesensor-handle-libnvme-mi-status-return-code.patch \
-  file://0011-nvmesensor-handle-the-broken-pipe-signal.patch \
-  file://0012-nvmesensor-Add-FirmwareCommit-to-nvme-daemon.patch \
-  file://0013-nvmesensor-Using-generated-controller-server.patch \
-  file://0014-nvmesensor-Add-NVMePlugin.patch \
-  file://0015-nvmesensor-add-associations.patch \
-  file://0016-nvmesensor-Create-thermal-sensor-in-start.patch \
-  file://0017-nvmesensor-Move-temp-sensor-function-to-NVMeUtil.patch \
-  file://0018-nvmesensor-Change-the-plugin-log-handler.patch \
-  file://0019-nvmesensor-Add-nvme-controller-plugin.patch \
-  file://0020-nvmesensor-add-timeout-for-xfer.patch \
-  file://0021-nvmesensor-segmentation-fault-workaround-and-fix.patch \
-  file://0022-nvmesensor-spin-out-the-worker-for-NVMeMi.patch \
-  file://0023-nvmesensor-delay-subsystem-creation.patch \
-  file://0024-nvmesensor-clean-up-the-association.patch \
-  file://0025-nvmesensor-change-the-controller-init-sequence.patch \
-  file://0026-nvmesensor-Check-SCS-for-secondary-controllers.patch \
-  file://0027-Enable-asio-threads-and-boost-coroutine.patch \
-  file://0028-nvmesensor-Split-constructor-for-shared_from_this.patch \
-  file://0029-nvmesensor-Add-SecuritySend-and-SecurityReceive.patch \
-  file://0030-nvmesensor-always-create-host-telemetry-log-page.patch \
-  file://0031-nvmesensor-set-default-timeout-to-20-seconds-for-Sec.patch \
-  file://0032-nvmesensor-close-pipe-file-descriptor-before-excepti.patch \
-  file://0033-nvmesensor-Add-toggle-for-single-thread-mode.patch \
+  file://0001-nvmesensor-rename-the-files.patch \
+  file://0002-nvme-sensor-refactor-the-code.patch \
+  file://0003-NVMe-sensor-add-Storage-and-Drive-interface.patch \
+  file://0004-nvmesensor-Add-NVMe-MI-protocol-and-controller.patch \
+  file://0005-Enable-ctemp-sensor-for-nvme-mi.patch \
+  file://0006-nvmesensor-Add-Identify-and-cntrl-association.patch \
+  file://0007-Add-NVMeAdmin-intf-with-GetLogPage.patch \
+  file://0008-Add-more-logs-to-NVMe-daemon.patch \
+  file://0009-Add-Identify-method-to-NVMeAdmin-interface.patch \
+  file://0010-nvmesensor-add-adminXfer-for-MI-interface.patch \
+  file://0011-nvmesensor-handle-libnvme-mi-status-return-code.patch \
+  file://0012-nvmesensor-handle-the-broken-pipe-signal.patch \
+  file://0013-nvmesensor-Add-FirmwareCommit-to-nvme-daemon.patch \
+  file://0014-nvmesensor-Using-generated-controller-server.patch \
+  file://0015-nvmesensor-Add-NVMePlugin.patch \
+  file://0016-nvmesensor-add-associations.patch \
+  file://0017-nvmesensor-Create-thermal-sensor-in-start.patch \
+  file://0018-nvmesensor-Move-temp-sensor-function-to-NVMeUtil.patch \
+  file://0019-nvmesensor-Change-the-plugin-log-handler.patch \
+  file://0020-nvmesensor-Add-nvme-controller-plugin.patch \
+  file://0021-nvmesensor-add-timeout-for-xfer.patch \
+  file://0022-nvmesensor-segmentation-fault-workaround-and-fix.patch \
+  file://0023-nvmesensor-spin-out-the-worker-for-NVMeMi.patch \
+  file://0024-nvmesensor-delay-subsystem-creation.patch \
+  file://0025-nvmesensor-clean-up-the-association.patch \
+  file://0026-nvmesensor-change-the-controller-init-sequence.patch \
+  file://0027-nvmesensor-Check-SCS-for-secondary-controllers.patch \
+  file://0028-nvmesensor-Add-toggle-for-single-thread-mode.patch \
+  file://0029-nvmesensor-Add-Fake-for-NVMeMi.patch \
+  file://0030-nvmesensor-add-unit-test.patch \
+  file://0031-nvmesensor-enable-disable-subsys-according-to-DF.patch \
+  file://0032-nvmesensor-subsystem-poll-supports-absence.patch \
+  file://0033-Enable-asio-threads-and-boost-coroutine.patch \
+  file://0034-nvmesensor-Split-constructor-for-shared_from_this.patch \
+  file://0035-nvmesensor-Add-SecuritySend-and-SecurityReceive.patch \
+  file://0036-nvmesensor-set-default-timeout-to-20-seconds-for-Sec.patch \
+  file://0037-nvmesensor-close-pipe-file-descriptor-before-excepti.patch \
+  file://0038-nvmesensor-add-Mock-and-Fake-for-SecuSend-Recv.patch \
+  file://0039-utils-add-PowerCallbackEntry-for-PowerMatch.patch \
+  file://0040-nvmesensor-add-PowerState-to-NVMeMi.patch \
+  file://0041-nvmesensor-Add-print-info-to-NVMeMi.patch \
+  file://0042-nvmesensor-chuck-fetch-for-telemetry-logs.patch \
+  file://0043-nvmesensor-fix-plugin-issue.patch \
+  file://0044-nvmesensor-add-markAvailable-with-adaptive-timer.patch \
 "
 PACKAGECONFIG[nvmesensor] = "-Dnvme=enabled, -Dnvme=disabled, libnvme libnvme-vu"
 SYSTEMD_SERVICE:${PN} += "${@bb.utils.contains('PACKAGECONFIG', 'nvmesensor', \