All of lore.kernel.org
 help / color / mirror / Atom feed
From: Terry Bowman <terry.bowman@amd.com>
To: <nvdimm@lists.linux.dev>, <dan.j.williams@intel.com>,
	<dave.jiang@intel.com>, <vishal.l.verma@intel.com>,
	<mav@ixsystems.com>
Cc: <terry.bowman@amd.com>, <linux-cxl@vger.kernel.org>
Subject: [ndctl RFC 1/1] cxl: Script to manage regions during CXL device hotplug remove-reinsert
Date: Mon, 7 Aug 2023 16:36:35 -0500	[thread overview]
Message-ID: <20230807213635.3633907-2-terry.bowman@amd.com> (raw)
In-Reply-To: <20230807213635.3633907-1-terry.bowman@amd.com>

Hotplug removing a CXL device and hotplug reinserting the same device
currently requires manual interaction for managing the device
region. The CXL device region must be manually destroyed before
hotplug removal and manually created after hotplug reinsertion.

Create a script to automatically destroy and recreate the region
during CXL device hotplug remove-reinsert. Save region characteristics to
a file before destroying the region and hotplug remove. Use the region
characteristics stored in the file to recreate the region after
hotplug reinsert.

Signed-off-by: Terry Bowman <terry.bowman@amd.com>
---
 README.txt     | 311 +++++++++++++++++++++++++++++++++++++++++
 cxl-hotplug.py | 366 +++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 677 insertions(+)
 create mode 100644 README.txt
 create mode 100755 cxl-hotplug.py

diff --git a/README.txt b/README.txt
new file mode 100644
index 0000000..97fa793
--- /dev/null
+++ b/README.txt
@@ -0,0 +1,311 @@
+                          ____________________
+
+                           CXL-HOTPLUG.README
+                          ____________________
+
+
+Table of Contents
+_________________
+
+1. Purpose
+2. Requirements
+.. 1. Kernel - v6.4.0
+.. 2. ndctl - v77 or later
+.. 3. QEMU - v8.0.3 + the following patches:
+.. 4. Python modules:
+.. 5. Additional tool details
+3. Usage
+4. Examples
+.. 1. Swap a device
+.. 2. Manually unplug and plugin a device
+.. 3. Manually unplug and plugin a device (w/ step by step details)
+
+
+
+
+
+1 Purpose
+=========
+
+  Hotplug adding and removing CXL devices requires region management not
+  automatically provided. For instance, if a CXL device is added then a
+  region must be 'created' before the memory can be used. Likewise, a
+  region must be 'destroyed' before a CXL device can be
+  removed. Removing a CXL device before a region is 'destroyed' can
+  result in CXL device data loss or corruption.
+
+  This tool aims to provide region delete and create automation for an
+  existing CXL device. The typical usage is to hotplug remove an
+  existing CXL device and then hotplug readd the same device immediately
+  or at some later time.
+
+  An unplug function is provided that will 'destroy' the region making
+  the device ready for hotplug removal. Note, 'destroying' a PMEM region
+  incurrs no loss of data. 'destroying' a RAM region will lose the
+  region data. This tool saves the region structure information so that
+  the device's region can be created in the future. After the device is
+  hot hotplug added in the future the region information is used to
+  recreate the region. The region information is saved to a default file
+  or can be directed to a specific file provided on the tool comandline.
+
+  This tool provides a swap function that automatically executes the
+  unplug and plugin functions.
+
+  This tool is currently limited to non interleaved devices.
+
+
+2 Requirements
+==============
+
+  Tested using the following:
+
+
+2.1 Kernel - v6.4.0
+~~~~~~~~~~~~~~~~~~~
+
+  To include the following kernel config settings:
+  ,----
+  | scripts/config --enable LIBNVDIMM
+  | scripts/config --enable CONFIG_CXL_BUS
+  | scripts/config --enable CONFIG_CXL_PCI
+  | scripts/config --enable CONFIG_CXL_ACPI
+  | scripts/config --enable CONFIG_CXL_MEM
+  | scripts/config --enable CONFIG_CXL_PMEM
+  | scripts/config --enable CONFIG_CXL_PORT
+  | scripts/config --enable CONFIG_CXL_SUSPEND
+  | scripts/config --enable CONFIG_CXL_REGION
+  | scripts/config --enable CONFIG_CXL_REGION_INVALIDATION_TEST
+  | scripts/config --enable CONFIG_PCIEAER_CXL
+  | scripts/config --enable CONFIG_TRANSPARENT_HUGEPAGE
+  | scripts/config --enable CONFIG_DEV_DAX
+  | scripts/config --enable CONFIG_DEV_DAX_HMEM
+  | scripts/config --enable CONFIG_DEV_DAX_KMEM
+  | scripts/config --enable CONFIG_DEV_DAX_PMEM
+  `----
+  The above configures for statically linked drivers. They could be
+  dynamically linked as modules as well.
+
+
+2.2 ndctl - v77 or later
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+2.3 QEMU - v8.0.3 + the following patches:
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+  20230227163157.6621-1-Jonathan.Cameron@huawei.com
+  20230303150908.27889-1-Jonathan.Cameron@huawei.com
+  <https://lore.kernel.org/linux-cxl/20230303152903.28103-1-Jonathan.Cameron@huawei.com/\#r>
+
+
+2.4 Python modules:
+~~~~~~~~~~~~~~~~~~~
+
+  The following python modules are required: json subprocess os argparse
+  logging
+
+
+2.5 Additional tool details
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+  <https://confluence.amd.com/display/ALK/CXL+QEMU>
+
+
+3 Usage
+=======
+
+  The utility requires one of the sub-commands: plugin,unplug,list,swap.
+
+  Commandline usage information:
+  ,----
+  | usage: cxl-hotplug.py [-h] {plugin,unplug,list,swap} ...
+  | 
+  | positional arguments:
+  | {plugin,unplug,list,swap}
+  | 
+  | options:
+  |   -h, --help            show this help message and exit
+  `----
+
+  Commandline plugin usage information:
+  ,----
+  | usage: cxl-hotplug.py plugin [-h] -m MEMDEV [-c CONFIG_FILE] [-d]
+  | 
+  | options:
+  |   -h, --help      show this help message and exit
+  |   -m MEMDEV       CXL memory device to prepare for unplug
+  |   -c CONFIG_FILE  CXL JSON configuration file to use
+  |   -d, --debug     Enable debugging
+  `----
+
+  Commandline unplug usage information:
+  ,----
+  | usage: cxl-hotplug.py unplug [-h] -m MEMDEV [-c CONFIG_FILE] [-d]
+  | 
+  | options:
+  |   -h, --help      show this help message and exit
+  |   -m MEMDEV       CXL memory device to prepare for unplug
+  |   -c CONFIG_FILE  CXL JSON configuration file to save
+  |   -d, --debug     Enable debugging
+  `----
+
+  Commandline swap usage information:
+  ,----
+  | usage: cxl-hotplug.py swap [-h] -m MEMDEV [-d]
+  | 
+  | options:
+  |   -h, --help   show this help message and exit
+  |   -m MEMDEV    CXL memory device to swap
+  |   -d, --debug  Enable debugging
+  `----
+
+
+4 Examples
+==========
+
+4.1 Swap a device
+~~~~~~~~~~~~~~~~~
+
+  ,----
+  | # cxl create-region -t ram -m  mem0 -d decoder0.0 -w 1 -s 256M
+  | # ./cxl-hotplug.py swap -m mem0
+  | Device 'mem0' can now be safely removed.
+  | Card is ready for removal.
+  | Press any key to continue after card reinsertion.
+  | Region created for 'mem0'.
+  `----
+
+
+4.2 Manually unplug and plugin a device
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+  ,----
+  | # cxl create-region -t ram -m  mem0 -d decoder0.0 -w 1 -s 256M
+  | # ./cxl-hotplug.py unplug -m mem0 -c my-cxl.json
+  | # ./cxl-hotplug.py plugin -m mem0 -c my-cxl.json
+  `----
+
+
+4.3 Manually unplug and plugin a device (w/ step by step details)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+  ,----
+  | # cxl list 
+  | [
+  |   {
+  |     "memdev":"mem0",
+  |     "ram_size":268435456,
+  |     "serial":0,
+  |     "host":"0000:0d:00.0"
+  |   }
+  | ]
+  | 
+  | # ./cxl-hotplug.py list
+  | Device        Region Name        Region Size        Movable
+  | ======        ===========        ===========        =======
+  |   mem0                 NA                 NA             NA
+  | 
+  | # cxl  create-region -t ram -m  mem0 -d decoder0.0 -w 1 -s 256M 
+  | {
+  |   "region":"region0",
+  |   "resource":"0x890000000",
+  |   "size":"256.00 MiB (268.44 MB)",
+  |   "type":"ram",
+  |   "interleave_ways":1,
+  |   "interleave_granularity":256,
+  |   "decode_state":"commit",
+  |   "mappings":[
+  |     {
+  |       "position":0,
+  |       "memdev":"mem0",
+  |       "decoder":"decoder2.0"
+  |     }
+  |   ]
+  | }
+  | cxl region: cmd_create_region: created 1 region
+  | 
+  | # cxl list 
+  | [
+  |   {
+  |     "memdevs":[
+  |       {
+  | 	"memdev":"mem0",
+  | 	"ram_size":268435456,
+  | 	"serial":0,
+  | 	"host":"0000:0d:00.0"
+  |       }
+  |     ]
+  |   },
+  |   {
+  |     "regions":[
+  |       {
+  | 	"region":"region0",
+  | 	"resource":36775657472,
+  | 	"size":268435456,
+  | 	"type":"ram",
+  | 	"interleave_ways":1,
+  | 	"interleave_granularity":256,
+  | 	"decode_state":"commit"
+  |       }
+  |     ]
+  |   }
+  | ]
+  | 
+  | # ./cxl-hotplug.py list
+  | Device        Region Name        Region Size        Movable
+  | ======        ===========        ===========        =======
+  |   mem0            region0          268435456           True
+  | 
+  | # ./cxl-hotplug.py unplug -m mem0 
+  | Device 'mem0' can now be safely removed.
+  | 
+  | # cxl list 
+  | [
+  |   {
+  |     "memdev":"mem0",
+  |     "ram_size":268435456,
+  |     "serial":0,
+  |     "host":"0000:0d:00.0"
+  |   }
+  | ]
+  | 
+  | # ./cxl-hotplug.py list
+  | Device        Region Name        Region Size        Movable
+  | ======        ===========        ===========        =======
+  |   mem0                 NA                 NA             NA
+  | 
+  | # ./cxl-hotplug.py plugin -m mem0 
+  | Region created for 'mem0'.
+  | 
+  | # cxl list 
+  | [
+  |   {
+  |     "memdevs":[
+  |       {
+  | 	"memdev":"mem0",
+  | 	"ram_size":268435456,
+  | 	"serial":0,
+  | 	"host":"0000:0d:00.0"
+  |       }
+  |     ]
+  |   },
+  |   {
+  |     "regions":[
+  |       {
+  | 	"region":"region0",
+  | 	"resource":36775657472,
+  | 	"size":268435456,
+  | 	"type":"ram",
+  | 	"interleave_ways":1,
+  | 	"interleave_granularity":256,
+  | 	"decode_state":"commit"
+  |       }
+  |     ]
+  |   }
+  | ]
+  | 
+  | # ./cxl-hotplug.py list
+  | Device        Region Name        Region Size        Movable
+  | ======        ===========        ===========        =======
+  |   mem0            region0          268435456           True
+  `----
diff --git a/cxl-hotplug.py b/cxl-hotplug.py
new file mode 100755
index 0000000..971e4e7
--- /dev/null
+++ b/cxl-hotplug.py
@@ -0,0 +1,366 @@
+#!/usr/bin/python3
+# SPDX-License-Identifier: LGPL-2.1
+#
+# Copyright (C) 2023, Advanced Micro Devices (AMD). All rights reserved.
+#
+# @Author - Terry Bowman
+#
+# Utility to support CXL hotplug removal and re-insertion. The purpose
+# is to recreate a CXL region during insertion that existed before
+# removal.
+#
+# '--unplug' sub-command option will save the CXL region information to
+# a file before a manual hotplug removal.
+#
+# '--plugin' sub-command after hotplug insertion will recreate a device
+# region using the details from the configuration file.
+#
+# '--swap' sub-command will run the same functionality as '--unplug',
+# then prompt for when the card is plugged in, and then execute the
+# '--plugin' functionality.
+#
+# '--list' sub-command will list the CXL devices and associated regions.
+#
+
+import json
+import subprocess
+import os
+import argparse
+import logging
+import tempfile
+
+class cxl_json:
+
+    cxl_memdev_json = {}
+    decoders_root_json = {}
+    regions_decoder_json = {}
+    daxregion_json = {}
+    daxregion_devices_json = {}
+
+    def cxl_list_memdev(self, memdev):
+        result = subprocess.run(["cxl", "list", "-m", memdev, "-RBMTXEPD"],
+                                capture_output=True, text=True)
+        if result.returncode != 0:
+            print("Error: cxl list command failed for: " + memdev)
+            exit(1)
+
+        result_json = json.loads(result.stdout)
+
+        if not result_json:
+            print("CXL list is empty for: " + memdev)
+            exit(1)
+
+        return(result_json[0])
+
+    # Cache embedded json dictionaries to make more easily accessible.
+    # This helps in later processing .
+    #
+    # If the CXL json dictionary is missing any CXL components than this
+    # implies a CXL device is not configured. In this case exit with an
+    # error and message.
+    def decode_json(self, fatal_error = True):
+        for key in self.cxl_memdev_json:
+            if key.startswith('decoders:root'):
+                self.decoders_root_json = self.cxl_memdev_json[key][0]
+        if self.decoders_root_json == None or \
+           len(self.decoders_root_json) == 0:
+            if fatal_error == True:
+                print("Error: Failed to find decoder root CXL json.")
+                exit(1)
+            else:
+                return
+
+        regions_decoder_key = "regions:" + self.decoders_root_json.get('decoder')
+        if self.decoders_root_json.get(regions_decoder_key) == None:
+            if fatal_error == True:
+                print("Error: Failed to find region decoder in CXL json.")
+                exit(1)
+            else:
+                return
+
+        self.regions_decoder_json = self.decoders_root_json.get(regions_decoder_key)[0]
+        if len(self.regions_decoder_json) == 0:
+            if fatal_error == True:
+                print("Error: Failed to find region decoder in CXL json.")
+                exit(1)
+            else:
+                return
+
+        # PMEM CXL JSON does not include DAX keys searched for below, return early
+        if 'pmem' == self.regions_decoder_json.get('type'):
+            return;
+
+        for key in self.regions_decoder_json:
+            if key.startswith('daxregion'):
+                self.daxregion_json = self.regions_decoder_json[key]
+        if len(self.daxregion_json) == 0:
+            if fatal_error == True:
+                print("Error: Failed to find daxregion in CXL json.")
+                exit(1)
+            else:
+                return
+
+        for key in self.daxregion_json:
+            if key.startswith('devices'):
+                self.daxregion_devices_json = self.daxregion_json[key][0]
+        if len(self.daxregion_devices_json) == 0:
+            if fatal_error == True:
+                print("Error: Failed to find daxregion devices in CXL json.")
+                exit(1)
+            else:
+                return
+
+    def get_block_size_bytes(self):
+        result = subprocess.run(["cat", "/sys/devices/system/memory/block_size_bytes"],
+                                capture_output=True, text=True)
+        if result.returncode != 0:
+            print("Error: cxl destroy-region command failed. rc = " +
+                  str(result.returncode))
+            exit(1)
+        return int(result.stdout, 16)
+
+    def is_region_movable(self):
+
+        # PMEM doesn't have movable concept
+        if 'pmem' == self.regions_decoder_json.get('type'):
+            return True;
+
+        block_size_bytes = self.get_block_size_bytes()
+        resource = self.regions_decoder_json.get('resource')
+        resource_size = self.regions_decoder_json.get('size')
+        start_block = int(resource/block_size_bytes)
+        stop_block = int((resource+resource_size)/block_size_bytes - 1)
+        is_movable = True
+
+        for i in range(0,(stop_block-start_block + 1)):
+            block = start_block + i
+            block_str = ("/sys/devices/system/memory/memory" + str(block) +
+                         "/valid_zones")
+            result = subprocess.run(["cat", block_str],
+                                    capture_output=True, text=True)
+            if result.returncode != 0:
+                print("Error: Block cat command failed. rc = " +
+                      str(result.returncode))
+                exit(1)
+
+            if "Movable" not in result.stdout:
+                is_movable = False
+                break;
+
+        return is_movable
+
+class cxl_unplug(cxl_json):
+
+    def __init__(self, memdev, config_file):
+        # Capture the current CXL (and DAX) configurations. Save
+        # configurations to file for using later with '--plugin'
+        # hot-plug adding the memory device.
+        self.cxl_memdev_json = self.cxl_list_memdev(memdev)
+        self.serialize_json(self.cxl_memdev_json, config_file)
+        self.decode_json()
+
+        if (self.regions_decoder_json.get('interleave_ways')>1):
+            print("Interleaved devices are not supported.")
+            exit(1)
+
+    def serialize_json(self, json_str, filename):
+        with open( filename , "w" ) as write:
+            json.dump(json_str, write)
+
+    def is_dax_memory_offline(self, dax_dev_json):
+        result = subprocess.run(["daxctl", "list"],
+                                capture_output=True, text=True)
+        if result.returncode != 0:
+            print("Error: daxctl offline command failed command. rc = " +
+                  str(result.returncode))
+            exit(1)
+
+        dax_dev_online_mb = json.loads(result.stdout)[0]["online_memblocks"]
+        logging.debug("online_memblocks = " +
+                      str(json.loads(result.stdout)[0]["online_memblocks"]))
+        return dax_dev_online_mb == 0
+
+    def offline_dax_memory(self):
+        # Note: self.daxregion_devices_json['movable'] JSON key is missing
+        # when True (ndctl v77). As a result, use the function region_movable()
+        if self.is_region_movable() == False:
+            print("Error: Entire region memory is not zone movable.")
+            exit(1)
+
+        dax_dev = self.daxregion_devices_json['chardev']
+        result=subprocess.run(["daxctl", "offline-memory", dax_dev],
+                              capture_output=True, text=True)
+        if result.returncode != 0:
+            if self.is_dax_memory_offline(dax_dev) == False:
+                print("Error: daxctl offline command failed. rc = " +
+                      str(result.returncode))
+                exit(1)
+
+    def offline_memory(self):
+        if 'ram' == self.regions_decoder_json.get('type'):
+            self.offline_dax_memory()
+
+    def cxl_destroy_region(self):
+        result = subprocess.run(["cxl", "destroy-region",
+                                 str(self.regions_decoder_json.get('region')),
+                                 "--force"],
+                                capture_output=True, text=True)
+        if result.returncode != 0:
+            print("Error: cxl destroy-region command failed. rc = " +
+                  str(result.returncode))
+            exit(1)
+
+    def unplug(self):
+        unplug_dev.offline_memory()
+        unplug_dev.cxl_destroy_region()
+        print("Device \'" + args.memdev + "\' can now be safely removed.")
+
+class cxl_plugin(cxl_json):
+
+    def __init__(self, config_file):
+        f = open(config_file)
+        self.cxl_memdev_json = json.load(f)
+        self.decode_json()
+
+        if (self.regions_decoder_json.get('interleave_ways')>1):
+            print("Interleaved devices are not supported.")
+            exit(1)
+
+    def cxl_create_region(self, cxl_memdev_json, memdev):
+        type = self.regions_decoder_json.get('type');
+        decoder = self.decoders_root_json.get('decoder')
+        interleave_ways = self.regions_decoder_json.get('interleave_ways')
+        interleave_granularity = self.regions_decoder_json.get('interleave_granularity')
+        size = self.regions_decoder_json.get('size')
+
+        result = subprocess.run(["cxl", "create-region",
+                                 "-m", memdev,
+                                 "-t", type,
+                                 "-d", decoder,
+                                 "-w", str(interleave_ways),
+                                 "-s", str(size),
+                                 "--debug"],
+                                capture_output=True, text=True)
+        if result.returncode!=0:
+            print("Error: cxl destroy-region command failed. rc = " +
+                  str(result.returncode))
+            exit(1)
+
+    def plugin(self, memdev):
+        self.cxl_create_region(self.cxl_memdev_json, memdev)
+        print("Region created for \'" + memdev + "\'.")
+
+class cxl_list(cxl_json):
+
+    cxl_memdevs_json = {}
+
+    def __init__(self):
+        self.cxl_memdevs_json = self.cxl_list_memdevs()
+
+    def display_memdevs(self):
+        if (self.cxl_memdevs_json == None):
+            print("Error: Failed to find memory devices")
+            return
+
+        if (len(self.cxl_memdevs_json) == 0):
+            print("Error: Failed to find memory devices")
+            return
+
+        print("Device        Region Name        Region Size        Movable")
+        print("======        ===========        ===========        =======")
+
+        for memdev in self.cxl_memdevs_json:
+            self.cxl_memdev_json = self.cxl_list_memdev(memdev['memdev'])
+            self.decode_json(fatal_error = False)
+            if len(self.regions_decoder_json) != 0:
+                region = self.regions_decoder_json.get('region');
+                size = self.regions_decoder_json.get('size')
+                is_movable_str = "True"
+                if self.is_region_movable() == 0:
+                        is_movable_str = "False"
+            else:
+                region = 'NA'
+                size = 'NA'
+                is_movable_str = 'NA'
+
+            print("%6s %18s %18s %14s" %
+                  (memdev['memdev'], region, size, is_movable_str))
+
+    def cxl_list_memdevs(self):
+        result = subprocess.run(["cxl", "list", "-M"],
+                                capture_output=True, text=True)
+        if result.returncode != 0:
+            print("Error: cxl list command failed.")
+            exit(1)
+
+        logging.debug("cxl_memdevs_json = " + result.stdout)
+        self.cxl_memdevs_json = json.loads(result.stdout);
+
+        return(self.cxl_memdevs_json)
+
+def init_args():
+    parser = argparse.ArgumentParser()
+    subparsers = parser.add_subparsers(dest='subparser_name', required=True)
+
+    parser_plugin = subparsers.add_parser('plugin')
+    parser_plugin.add_argument('-m', dest='memdev',
+                               help='CXL memory device to prepare for unplug',
+                               required=True)
+    parser_plugin.add_argument('-c', dest='config_file',
+                               help='CXL JSON configuration file to save/use',
+                               required=False, default="cxl.json")
+    parser_plugin.add_argument('-d', '--debug', help='Enable debug logging',
+                               required=False, action='store_true')
+
+    parser_unplug = subparsers.add_parser('unplug')
+    parser_unplug.add_argument('-m', dest='memdev',
+                               help='CXL memory device to prepare for unplug',
+                               required=True)
+    parser_unplug.add_argument('-c', dest='config_file',
+                               help='CXL JSON configuration file to save/use',
+                               required=False, default="cxl.json")
+    parser_unplug.add_argument('-d', '--debug', help='Enable debugging',
+                               required=False, action='store_true')
+
+    parser_list = subparsers.add_parser('list')
+    parser_list.add_argument('-d', '--debug', help='Enable debugging',
+                             required=False, action='store_true')
+
+    parser_swap = subparsers.add_parser('swap')
+    parser_swap.add_argument('-m', dest='memdev',
+                               help='CXL memory device to swap',
+                               required=True)
+    parser_swap.add_argument('-d', '--debug', help='Enable debugging',
+                             required=False, action='store_true')
+
+    return parser.parse_args()
+
+args = init_args()
+if args.debug:
+    logging.basicConfig(level=logging.DEBUG)
+
+logging.debug("args = " + str(args))
+
+if args.subparser_name == 'list':
+    list_dev = cxl_list();
+    list_dev.display_memdevs()
+
+if args.subparser_name == 'unplug':
+    unplug_dev = cxl_unplug(args.memdev, args.config_file);
+    unplug_dev.unplug()
+
+if args.subparser_name == 'plugin':
+    plugin_dev = cxl_plugin(args.config_file);
+    plugin_dev.plugin(args.memdev)
+
+if args.subparser_name == 'swap':
+    f, filename = tempfile.mkstemp()
+    unplug_dev = cxl_unplug(args.memdev, filename);
+    unplug_dev.unplug()
+
+    print("Card is ready for removal.")
+    input("Press any key to continue after card reinsertion.")
+
+    plugin_dev = cxl_plugin(filename);
+    plugin_dev.plugin(args.memdev)
+    os.close(f)
-- 
2.34.1


  reply	other threads:[~2023-08-07 21:36 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-08-07 21:36 [ndctl RFC 0/1] cxl: Script to manage regions during CXL device hotplug remove-reinsert Terry Bowman
2023-08-07 21:36 ` Terry Bowman [this message]
2023-08-08  5:57   ` [ndctl RFC 1/1] " Dan Williams
2023-08-08  5:45 ` [ndctl RFC 0/1] " Dan Williams

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20230807213635.3633907-2-terry.bowman@amd.com \
    --to=terry.bowman@amd.com \
    --cc=dan.j.williams@intel.com \
    --cc=dave.jiang@intel.com \
    --cc=linux-cxl@vger.kernel.org \
    --cc=mav@ixsystems.com \
    --cc=nvdimm@lists.linux.dev \
    --cc=vishal.l.verma@intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.