-
Notifications
You must be signed in to change notification settings - Fork 0
Transition Guide from v1.8 to v1.9
In the release 1.9, we fixed many bugs in earlier design. Most of the fixes keep the back-compatibility very well, but some of them require changes made in user scripts. In order to help users to upgrade to release 1.9, we collected these changes.
Controller was created first, and pcie object was created on controller object.
nvme0 = d.Controller(b'01:00.0')
pcie = d.Pcie(nvme0)Now, we create pcie object first, and controller object is created on pcie object. It is more reasonable.
pcie = d.Pcie('01:00.0')
nvme0 = d.Controller(pcie)v1.9 supports raw pcie device tests, which may not be NVMe devices. And the BDF address now is a string instead of bytes. In v1.9, we can even define the NVMe initialization process in the scripts instead of pynvme driver.
def test_init_nvme_customerized(pcie):
def nvme_init(nvme0):
nvme0[0x14] = 0
while not (nvme0[0x1c]&0x1) == 0: pass
nvme0.init_adminq()
nvme0[0x14] = 0x00460000
nvme0[0x14] = 0x00460001
while not (nvme0[0x1c]&0x1) == 1: pass
nvme0.identify(d.Buffer(4096)).waitdone()
nvme0.init_ns()
nvme0.setfeatures(0x7, cdw11=0x00ff00ff).waitdone()
nvme0.getfeatures(0x7).waitdone()
aerl = nvme0.id_data(259)+1
for i in range(aerl):
nvme0.aer()
nvme0 = d.Controller(pcie, nvme_init_func=nvme_init)AER commands are all managed by SPDK driver, and scripts provide a callback function with the aer fixture.
Now, pynvme bypasses the whole nvme initialization process in SPDK driver. So, pynvme and scripts manage AER commands totally. SPDK driver will not send AER commands nor handle their completions. By default, scripts use the NVMe initialization process provided by pynvme where only one AER command sent. Scripts can also skip pynvme's initialization process and implement NVMe initialization process by its own. Which means scripts can send any number of AER commands and define callback functions for any AER command. Please refer to the example above.
Pcie.reset(), Subsystem.reset() and Subsystem.power_cycle() internally called Controller.reset() in order to make controller accessible after reset and power events.
Scripts are required to call Controller.reset() after these reset and power events.
def test_power_and_reset(pcie, nvme0, subsystem):
nvme0.reset() # controller reset: CC.EN
nvme0.getfeatures(7).waitdone()
pcie.reset() # PCIe reset: hot reset, TS1, TS2
nvme0.reset() # reset controller after pcie reset
nvme0.getfeatures(7).waitdone()
subsystem.reset() # NVMe subsystem reset: NSSR
nvme0.reset() # controller reset: CC.EN
nvme0.getfeatures(7).waitdone()
subsystem.power_cycle(10) # power cycle NVMe device: cold reset
nvme0.reset() # controller reset: CC.EN
nvme0.getfeatures(7).waitdone()
subsystem.poweroff()
subsystem.poweron()
nvme0.reset() # controller reset: CC.EN
nvme0.getfeatures(7).waitdone()We used del to delete objects, as well as its internal data structures. However, Python's GC cannot guarantee the time and the order of object deletion.
We tried many ways to delete internal data structure by del, coping with different ordring. But it made the internal implementation more complex and risky. So, we decided to require scripts explicitly call API to delete internal data structure. Specifically:
pcie = d.Pcie('01:00.0')
nvme0 = d.Controller(pcie)
nvme0n1 = d.Namespace(nvme0)
nvme0n1.close()
qpair = d.Qpair(nvme0)
qpair.delete()
pcie.close()If you use fixture pcie, nvme0n1 and qpair, these fixtures will free resource at the right time, and scripts do not need to explicitly close/delete them. So, it is always recommended to use the fixture whenever it is possible.
Pynvme has to modify internal poweron() and poweroff() code to drive different power module, and re-compile it.
Pynvme provide 2 callback functions in Subsystem objects. Users can define these callback functions in the scripts to driver their own power module.
def poweron():
logging.info("poweron drive")
#... user defined scripts to poweron the power module
def poweroff():
logging.info("poweroff drive")
#... user defined scripts to poweroff the power module
# use user defined poweron/poweroff function
subsystem = d.Subsystem(nvme0, poweron, poweroff)
subsystem.poweroff()
subsystem.poweron()
# use pynvme defined S3/RTC poweron/poweroff
subsystem = d.Subsystem(nvme0, poweron, poweroff)
subsystem.poweroff()
subsystem.poweron()When these 2 callback functions are not provided for a subsystem object, it just use the software-defined S3/RTC method to poweron/poweroff NVMe devices.
Pynvme can verify LBA's CRC32, but it was a global feature enabled by d.config(). When it is enabled, it is enabled on all namespaces.
CRC32 verify function is enabled on a namespace. Even where it is not enabled, CRC32 calculation always works on each LBA write. When we are testing multiple drives, or one drive with a large capacity, the memory consumption is very large to hold the CRC32 of all LBAs. As a mitigation, we can specify a parameter nlba_verify on a namespace, so CRC32 table can be shrink to a smaller LBA region [0, nlba_verify).
def test_ioworker_read_write_mixed_verify(nvme0):
region_end=256*1024*8 # unit is LBA, first 1GB space
nvme0n1 = d.Namespace(nvme0, 1, nlba_verify=region_end)
nvme0n1.verify_enable()
nvme0n1.ioworker(io_size=8,
region_end=region_end,
read_percentage=50,
time=10).start().close()
nvme0n1.close()In the above example, we do read/write mixed test with verify enabled in the first 1GB space. The LBA conflict is detected and cleared in the single ioworker by pynvme, so no data mis-compare would happen in this test.
And do remember to close the namespace!
