[PATCH] nvmetcli: add support for NVMe passthru target

Nilay Shroff nilay at linux.ibm.com
Wed Jun 11 03:39:29 PDT 2025


This change adds the support for configuring NVMe passthru target.

Signed-off-by: Nilay Shroff <nilay at linux.ibm.com>
---
 nvmet/__init__.py |   2 +-
 nvmet/nvme.py     | 102 ++++++++++++++++++++++++++++++++++++++++++++++
 nvmetcli          |  77 ++++++++++++++++++++++++++++++++++
 3 files changed, 180 insertions(+), 1 deletion(-)

diff --git a/nvmet/__init__.py b/nvmet/__init__.py
index cf172bd..5e2f525 100644
--- a/nvmet/__init__.py
+++ b/nvmet/__init__.py
@@ -1,2 +1,2 @@
-from .nvme import Root, Subsystem, Namespace, Port, Host, Referral, ANAGroup,\
+from .nvme import Root, Subsystem, Namespace, Port, Host, Referral, ANAGroup, Passthru, \
     DEFAULT_SAVE_FILE
diff --git a/nvmet/nvme.py b/nvmet/nvme.py
index 59efdb5..d462ea2 100644
--- a/nvmet/nvme.py
+++ b/nvmet/nvme.py
@@ -462,6 +462,13 @@ class Subsystem(CFSNode):
     namespaces = property(_list_namespaces,
                           doc="Get the list of Namespaces for the Subsystem.")
 
+    def _get_passthru(self):
+        self._check_self()
+        return Passthru(self)
+
+    passthru = property(_get_passthru,
+                        doc="Get the passthru node for the subsystem")
+
     def _list_allowed_hosts(self):
         return [os.path.basename(name)
                 for name in os.listdir("%s/allowed_hosts/" % self._path)]
@@ -510,6 +517,8 @@ class Subsystem(CFSNode):
             Namespace.setup(s, ns, err_func)
         for h in t.get('allowed_hosts', []):
             s.add_allowed_host(h)
+        for pt in t.get('passthru', []):
+            Passthru.setup(s, pt, err_func)
 
         s._setup_attrs(t, err_func)
 
@@ -518,6 +527,7 @@ class Subsystem(CFSNode):
         d['nqn'] = self.nqn
         d['namespaces'] = [ns.dump() for ns in self.namespaces]
         d['allowed_hosts'] = self.allowed_hosts
+        d['passthru'] = [self.passthru.dump()]
         return d
 
 
@@ -629,6 +639,98 @@ class Namespace(CFSNode):
         d['ana_grpid'] = self.grpid
         return d
 
+class Passthru(CFSNode):
+    '''
+    This is an interface to a NVMe passthru in ConfigFS.
+    '''
+
+    def __init__(self, subsystem):
+        '''
+        @param subsystem: The parent Subsystem object.
+        @return: A Passthru object.
+        '''
+        super(Passthru, self).__init__()
+        self._path = "%s/passthru" % (subsystem.path)
+        self.attr_groups = ['device']
+
+    def _get_clear_ids(self):
+        self._check_self()
+        path = "%s/clear_ids" % self.path
+        _ids = 0
+        if os.path.isfile(path):
+            with open(path, 'r') as file_fd:
+                _ids = int(file_fd.read().strip())
+        return _ids
+
+    ids = property(_get_clear_ids,
+                   doc = "Get the passthru namespace clear_ids attribute.")
+
+    def set_clear_ids(self, clear):
+        self._check_self()
+        path = "%s/clear_ids" % self.path
+        if os.path.isfile(path):
+            with open(path, 'w') as file_fd:
+                file_fd.write(str(clear))
+
+    def _get_admin_timeout(self):
+        self._check_self()
+        path = "%s/admin_timeout" % self.path
+        _timeout = 0
+        if os.path.isfile(path):
+            with open(path, 'r') as file_fd:
+                _timeout = int(file_fd.read().strip())
+        return _timeout
+
+    admin_timeout = property(_get_admin_timeout,
+                   doc = "Get the passthru admin command timeout.")
+
+    def set_admin_timeout(self, timeout):
+        self._check_self()
+        path = "%s/admin_timeout" % self.path
+        if os.path.isfile(path):
+            with open(path, 'w') as file_fd:
+                file_fd.write(str(timeout))
+
+    def _get_io_timeout(self):
+        self._check_self()
+        path = "%s/io_timeout" % self.path
+        _timeout = 0
+        if os.path.isfile(path):
+            with open(path, 'r') as file_fd:
+                _timeout = int(file_fd.read().strip())
+        return _timeout
+
+    io_timeout = property(_get_io_timeout,
+                   doc = "Get the passthru IO command timeout.")
+
+    def set_io_timeout(self, timeout):
+        self._check_self()
+        path = "%s/io_timeout" % self.path
+        if os.path.isfile(path):
+            with open(path, 'w') as file_fd:
+                file_fd.write(str(timeout))
+
+    @classmethod
+    def setup(cls, subsys, p, err_func):
+        try:
+            pt = Passthru(subsys)
+        except CFSError as e:
+            err_func("Could not create Namespace object: %s" % e)
+            return
+        pt._setup_attrs(p, err_func)
+        if 'clear_ids' in p:
+            pt.set_clear_ids(int(p['clear_ids']))
+        if 'admin_timeout' in p:
+            pt.set_admin_timeout(int(p['admin_timeout']))
+        if 'io_timeout' in p:
+            pt.set_io_timeout(int(p['io_timeout']))
+
+    def dump(self):
+        d = super(Passthru, self).dump()
+        d['clear_ids'] = self.ids
+        d['admin_timeout'] = self.admin_timeout
+        d['io_timeout'] = self.io_timeout
+        return d
 
 class Port(CFSNode):
     '''
diff --git a/nvmetcli b/nvmetcli
index d949891..810a7cd 100755
--- a/nvmetcli
+++ b/nvmetcli
@@ -166,6 +166,7 @@ class UISubsystemNode(UINode):
         self._children = set([])
         UINamespacesNode(self)
         UIAllowedHostsNode(self)
+        UIPassthruNode(self)
 
     def summary(self):
         info = []
@@ -175,6 +176,82 @@ class UISubsystemNode(UINode):
         info.append("serial=" + self.cfnode.get_attr("attr", "serial"))
         return (", ".join(info), True)
 
+class UIPassthruNode(UINode):
+    ui_desc_device = {
+        'path' : ('string', 'Passthru device path')
+    }
+
+    def __init__(self, parent):
+        passthru = nvme.Passthru(parent.cfnode)
+        UINode.__init__(self, 'passthru', parent, passthru)
+
+    def refresh(self):
+        pass
+
+    def ui_command_enable(self):
+        if self.cfnode.get_enable():
+            self.shell.log.info("The passthru is already enabled.")
+        else:
+            try:
+                self.cfnode.set_enable(1)
+                self.shell.log.info("The passthru has been enabled.")
+            except Exception as e:
+                raise configshell.ExecutionError(
+                    "The passthru could not be enabled.")
+
+    def ui_command_disable(self):
+        if not self.cfnode.get_enable():
+            self.shell.log.info("The passthru is already disabled.")
+        else:
+            try:
+                self.cfnode.set_enable(0)
+                self.shell.log.info("The passthru has been disabled.")
+            except Exception as e:
+                raise configshell.ExecutionError(
+                    "The passthru could not be disabled.")
+
+    def ui_command_clear_ids(self, clear):
+        '''
+        If I{clear} is set to non-zero then clears the passthru namespace
+        unique identifiers EUI/GUID/UUID.
+        '''
+        try:
+            self.cfnode.set_clear_ids(clear)
+        except Exception as e:
+            raise configshell.ExecutionError(
+                "Failed to set clear_ids for this passthru target.")
+
+    def ui_command_admin_timeout(self, timeout):
+        '''
+        Sets the timeout of admin passthru command.
+        '''
+        try:
+            self.cfnode.set_admin_timeout(timeout)
+        except Exception as e:
+            raise configshell.ExecutionError(
+                "Failed to set the admin passthru command timeout.")
+
+    def ui_command_io_timeout(self, timeout):
+        '''
+        Sets the timeout of IO passthru command.
+        '''
+        try:
+            self.cfnode.set_io_timeout(timeout)
+        except Exception as e:
+            raise configshell.ExecutionError(
+                "Failed to set the IO passthru command timeout.")
+
+    def summary(self):
+        info = []
+        info.append("path=" + self.cfnode.get_attr("device", "path"))
+        if self.cfnode.ids != 0:
+            info.append("clear_ids=" + str(self.cfnode.ids))
+        if self.cfnode.admin_timeout != 0:
+            info.append("admin_tomeout=" + str(self.cfnode.admin_timeout))
+        if self.cfnode.io_timeout != 0:
+            info.append("io_tomeout=" + str(self.cfnode.io_timeout))
+        info.append("enabled" if self.cfnode.get_enable() else "disabled")
+        return (", ".join(info), True)
 
 class UINamespacesNode(UINode):
     def __init__(self, parent):
-- 
2.49.0




More information about the Linux-nvme mailing list