├── doc ├── Doxyfile.dot ├── Doxyfile └── Makefile ├── uio_memmap ├── Makefile └── uio_memmap.c ├── uio_memmap_platform ├── Makefile └── uio_memmap.c ├── .gitignore ├── test ├── unvme-test ├── Makefile ├── python │ ├── unvme_info.py │ ├── unvme_get_features.py │ ├── unvme_wr_ex.py │ └── unvme.py ├── nvme │ ├── Makefile │ ├── nvme_common.c │ ├── nvme_set_features.c │ ├── nvme_get_features.c │ ├── nvme_identify.c │ └── nvme_get_log_page.c ├── unvme │ ├── Makefile │ ├── unvme_info.c │ ├── unvme_sim_test.c │ ├── unvme_get_features.c │ ├── unvme_get_log_page.c │ ├── unvme_api_test.c │ ├── unvme_mcd_test.c │ ├── unvme_lat_test.c │ ├── unvme_mts_test.c │ └── unvme_wrc.c ├── unvme-benchmark └── unvme-setup ├── LICENSE ├── Makefile.def ├── ioengine ├── Makefile └── unvme_fio.c ├── src ├── Makefile ├── rdtsc.h ├── unvme_lock.h ├── unvme_log.h ├── unvme_vfio.h ├── unvme_log.c ├── unvme.h ├── unvme_core.h ├── unvme.c └── unvme_vfio.c ├── Makefile └── README.md /doc/Doxyfile.dot: -------------------------------------------------------------------------------- 1 | @INCLUDE = Doxyfile 2 | HAVE_DOT = YES 3 | -------------------------------------------------------------------------------- /uio_memmap/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += uio_memmap.o 2 | #uio_memmap-objs += uio_memmap.o 3 | 4 | all: 5 | make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) modules 6 | clean: 7 | make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) clean 8 | -------------------------------------------------------------------------------- /uio_memmap_platform/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += uio_memmap.o 2 | #uio_memmap-objs += uio_memmap.o 3 | 4 | all: 5 | make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) modules 6 | clean: 7 | make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) clean 8 | -------------------------------------------------------------------------------- /doc/Doxyfile: -------------------------------------------------------------------------------- 1 | PROJECT_NAME = "User-space NVME Driver" 2 | INPUT = . ../src 3 | FILE_PATTERNS = *.dox *.h *.c 4 | EXCLUDE_PATTERNS = *.mod.c 5 | MACRO_EXPANSION = NO 6 | BRIEF_MEMBER_DESC = YES 7 | EXTRACT_STATIC = YES 8 | SORT_MEMBER_DOCS = YES 9 | GENERATE_TREEVIEW = YES 10 | GENERATE_HTML = YES 11 | GENERATE_LATEX = NO 12 | CALL_GRAPH = YES 13 | INCLUDE_GRAPH = YES 14 | INCLUDED_BY_GRAPH = NO 15 | DIRECTORY_GRAPH = NO 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.a 3 | *.so 4 | *.pyc 5 | *.*~ 6 | *unvmed 7 | *_test 8 | *_ex 9 | *_fio 10 | *_info 11 | *_identify 12 | *_page 13 | *_features 14 | *_dd 15 | *_read 16 | *_write 17 | *_scan 18 | *_prl 19 | *_topo 20 | *_pecc 21 | *_wrc 22 | *_wrs 23 | *_analyze 24 | *_validate 25 | */out/ 26 | */html/ 27 | fio/gfio 28 | fio/t/axmap 29 | fio/t/fio-btrace2fio 30 | fio/t/fio-dedupe 31 | fio/t/fio-genzipf 32 | fio/t/fio-verify-state 33 | fio/t/gen-rand 34 | fio/t/ieee754 35 | fio/t/lfsr-test 36 | fio/t/stest 37 | 38 | .cproject 39 | .project 40 | .pydevproject 41 | .settings/ 42 | -------------------------------------------------------------------------------- /test/unvme-test: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | PROG=$(basename "$0") 4 | PDIR=$(dirname $(readlink -f $0)) 5 | 6 | [ $# -lt 1 ] && echo "Usage: ${PROG} PCINAME..." && exit 1 7 | 8 | [ ${EUID} -ne 0 ] && echo "${PROG} must be run as root" && exit 1 9 | 10 | # select the executable path 11 | setpx() { 12 | if [ -x "${PDIR}/$1" ]; then 13 | echo "${PDIR}/$1" 14 | else 15 | echo "/usr/local/bin/$(basename $1)" 16 | fi 17 | } 18 | 19 | # echo and execute command 20 | excmd() { 21 | CMD=$(setpx $1) 22 | shift 23 | echo -e "\n\$ ${CMD} $* ($(date))" 24 | eval ${CMD} $* 25 | [ $? -ne 0 ] && exit 1 26 | } 27 | 28 | [ ${EUID} -ne 0 ] && echo "${PROG} must be run as root" && exit 1 29 | 30 | for d in $*; do 31 | excmd unvme-setup bind $d 32 | excmd unvme/unvme_info $d 33 | excmd unvme/unvme_get_features $d 34 | excmd unvme/unvme_sim_test $d 35 | excmd unvme/unvme_api_test $d 36 | excmd unvme/unvme_mts_test $d 37 | excmd unvme/unvme_lat_test $d 38 | 39 | echo -e "\n\$ python ${PDIR}/python/unvme_wr_ex.py $d ($(date))" 40 | python ${PDIR}/python/unvme_wr_ex.py $d 41 | done 42 | 43 | if [ $# -gt 1 ]; then 44 | excmd unvme/unvme_mcd_test $* 45 | fi 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015-2016, Micron Technology, Inc. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of the copyright holder nor the names of its 15 | contributors may be used to endorse or promote products derived 16 | from this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | 30 | -------------------------------------------------------------------------------- /test/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2015-2016, Micron Technology, Inc. 3 | # All rights reserved. 4 | # 5 | # Redistribution and use in source and binary forms, with or without 6 | # modification, are permitted provided that the following conditions are met: 7 | # 8 | # 1. Redistributions of source code must retain the above copyright 9 | # notice, this list of conditions and the following disclaimer. 10 | # 11 | # 2. Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # 3. Neither the name of the copyright holder nor the names of its 16 | # contributors may be used to endorse or promote products derived 17 | # from this software without specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | # 31 | 32 | SUBDIRS = unvme nvme 33 | 34 | all: $(SUBDIRS) 35 | 36 | $(SUBDIRS): 37 | $(MAKE) -C $@ 38 | 39 | lint: 40 | @(for d in $(SUBDIRS); do $(MAKE) -C $$d lint; done) 41 | 42 | clean: 43 | @(for d in $(SUBDIRS); do $(MAKE) -C $$d clean; done) 44 | $(RM) python/*.pyc 45 | 46 | .PHONY: all lint clean $(SUBDIRS) 47 | 48 | -------------------------------------------------------------------------------- /Makefile.def: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2015-2016, Micron Technology, Inc. 3 | # All rights reserved. 4 | # 5 | # Redistribution and use in source and binary forms, with or without 6 | # modification, are permitted provided that the following conditions are met: 7 | # 8 | # 1. Redistributions of source code must retain the above copyright 9 | # notice, this list of conditions and the following disclaimer. 10 | # 11 | # 2. Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # 3. Neither the name of the copyright holder nor the names of its 16 | # contributors may be used to endorse or promote products derived 17 | # from this software without specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | # 31 | 32 | SHELL:=/bin/bash 33 | 34 | # Default compilation flags 35 | CFLAGS?=-O2 36 | CFLAGS+=-mcpu=native 37 | CFLAGS+=-Wall -fPIC 38 | CPPFLAGS+=-D_GNU_SOURCE 39 | 40 | # To turn on debug message logging 41 | #CPPFLAGS+=-DUNVME_DEBUG 42 | 43 | # To build the ioengine modules, specify fio directory where header files are 44 | #FIODIR:=/opt/fio 45 | 46 | -------------------------------------------------------------------------------- /test/python/unvme_info.py: -------------------------------------------------------------------------------- 1 | #!/bin/python 2 | # Example to open a device and print its namespace info. 3 | # 4 | # This program shows 2 ways to invoke Python UNVMe interfaces: 5 | # 1) To use direct functions, specify device with /NSID, e.g. 01:00.0/1 6 | # 2) To use Unvme class, specify device without slash, e.g. 01:00.0 7 | # 8 | 9 | import sys, unvme 10 | from unvme import * 11 | 12 | # Print device info 13 | def print_info(ns): 14 | print("PCI device: %06x" % ns.pci) 15 | print("Namespace: %d (of %d)" % (ns.id, ns.nscount)) 16 | print("Vendor ID: %#x" % ns.vid) 17 | print("Model number: %.40s" % str(ns.mn)) 18 | print("Serial number: %.20s" % str(ns.sn)) 19 | print("FW revision: %.8s" % str(ns.fr)) 20 | print("Block count: %#lx" % ns.blockcount) 21 | print("Page count: %#lx" % ns.pagecount) 22 | print("Block size: %d" % ns.blocksize) 23 | print("Page size : %d" % ns.pagesize) 24 | print("Blocks per page: %d" % ns.nbpp) 25 | print("Max blocks per IO: %d" % ns.maxbpio) 26 | print("Default IO queue count: %d" % ns.qcount) 27 | print("Default IO queue size: %d" % ns.qsize) 28 | print("Max IO queue count: %d" % ns.maxqcount) 29 | print("Max IO queue size: %d" % ns.maxqsize) 30 | 31 | 32 | # Main 33 | try: 34 | pci = sys.argv[1] 35 | except: 36 | print("Usage: python %s PCINAME\n" % sys.argv[0]) 37 | print("1) To invoke functions, specify PCINAME with /NSID, e.g. 01:00.0/1") 38 | print("2) To invoke Unvme class, specify PCINAME without slash, e.g. 01:00.0") 39 | sys.exit(1); 40 | 41 | if ('/' in pci): 42 | print("USING UNVMe FUNCTIONS") 43 | print("=====================") 44 | ns = unvme_open(pci) 45 | if not ns: 46 | sys.exit(1) 47 | print_info(ns.contents) 48 | unvme_close(ns) 49 | else: 50 | print("USING UNVMe CLASS") 51 | print("=================") 52 | unvme = Unvme(pci) 53 | print_info(unvme.info()) 54 | 55 | -------------------------------------------------------------------------------- /uio_memmap_platform/uio_memmap.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | static struct platform_device *pdev = NULL; 9 | static struct uio_info *info = NULL; 10 | static void *mem = NULL; 11 | 12 | 13 | static int __init memmap_init(void) 14 | { 15 | phys_addr_t mem_phys; 16 | 17 | pr_info("uio_memmap: init start\n"); 18 | 19 | pdev = platform_device_register_simple("uio_memmap", 0, NULL, 0); 20 | if (IS_ERR(pdev)) { 21 | pr_err("Failed to register platform device\n"); 22 | return PTR_ERR(pdev); 23 | } 24 | 25 | info = kzalloc(sizeof(struct uio_info), GFP_KERNEL); 26 | if (!info) { 27 | pr_err("Failed to allocate uio_info memory\n"); 28 | return -ENOMEM; 29 | } 30 | 31 | mem = kzalloc(0x100000, GFP_KERNEL); 32 | if (!mem) { 33 | pr_err("Failed to allocate mem memory\n"); 34 | goto free_info; 35 | } 36 | 37 | mem_phys = virt_to_phys(mem); 38 | 39 | pr_info("uio_memmap: Allocated memory:\nVIRT: 0x%016llx\nPHYS: 0x%016llx\n", (phys_addr_t)mem, mem_phys); 40 | 41 | info->name = "memmap"; 42 | info->version = "0.1"; 43 | 44 | info->irq = UIO_IRQ_NONE; 45 | 46 | info->mem[0].name = "mem0"; 47 | info->mem[0].addr = mem_phys; 48 | info->mem[0].internal_addr = mem; 49 | info->mem[0].size = 0x100000; 50 | info->mem[0].memtype = UIO_MEM_PHYS; 51 | 52 | if (uio_register_device(&pdev->dev, info)) { 53 | pr_err("Failed to register UIO device\n"); 54 | goto free_mem; 55 | } 56 | 57 | pr_info("uio_memmap: init done\n"); 58 | 59 | return 0; 60 | 61 | free_mem: 62 | kfree(mem); 63 | free_info: 64 | kfree(info); 65 | return -ENODEV; 66 | } 67 | 68 | static void __exit memmap_exit(void) 69 | { 70 | uio_unregister_device(info); 71 | kfree(info); 72 | kfree(mem); 73 | platform_device_unregister(pdev); 74 | 75 | pr_info("uio_memmap: exited\n"); 76 | } 77 | 78 | 79 | module_init(memmap_init); 80 | module_exit(memmap_exit); 81 | 82 | MODULE_LICENSE("GPL"); 83 | MODULE_AUTHOR("Russell Joyce"); 84 | MODULE_DESCRIPTION("UIO-based Memory Mapping"); 85 | MODULE_VERSION("0.1"); 86 | -------------------------------------------------------------------------------- /doc/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2015-2016, Micron Technology, Inc. 3 | # All rights reserved. 4 | # 5 | # Redistribution and use in source and binary forms, with or without 6 | # modification, are permitted provided that the following conditions are met: 7 | # 8 | # 1. Redistributions of source code must retain the above copyright 9 | # notice, this list of conditions and the following disclaimer. 10 | # 11 | # 2. Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # 3. Neither the name of the copyright holder nor the names of its 16 | # contributors may be used to endorse or promote products derived 17 | # from this software without specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | # 31 | 32 | DOXYGEN := $(shell "command" -v doxygen) 33 | DOT := $(shell "command" -v dot) 34 | 35 | ifdef DOXYGEN 36 | ifdef DOT 37 | DOXYFILE = Doxyfile.dot 38 | else 39 | DOXYFILE = Doxyfile 40 | endif 41 | endif 42 | 43 | all: html/index.html 44 | 45 | html/index.html: $(wildcard ../src/*.c ../src/*.h) 46 | ifdef DOXYGEN 47 | $(DOXYGEN) $(DOXYFILE) > /dev/null 48 | else 49 | @echo "No doxygen" 50 | endif 51 | 52 | lint: clean all 53 | 54 | clean: 55 | $(RM) -r html *.tmp 56 | 57 | .PHONY: all lint clean 58 | 59 | -------------------------------------------------------------------------------- /test/nvme/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2015-2016, Micron Technology, Inc. 3 | # All rights reserved. 4 | # 5 | # Redistribution and use in source and binary forms, with or without 6 | # modification, are permitted provided that the following conditions are met: 7 | # 8 | # 1. Redistributions of source code must retain the above copyright 9 | # notice, this list of conditions and the following disclaimer. 10 | # 11 | # 2. Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # 3. Neither the name of the copyright holder nor the names of its 16 | # contributors may be used to endorse or promote products derived 17 | # from this software without specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | # 31 | 32 | include ../../Makefile.def 33 | 34 | TARGETS = nvme_identify nvme_get_log_page nvme_get_features nvme_set_features 35 | 36 | UNVME_SRC = ../../src 37 | 38 | CPPFLAGS += -I$(UNVME_SRC) 39 | LDLIBS += -lrt 40 | 41 | OBJS = $(addsuffix .o, $(TARGETS)) 42 | 43 | all: $(TARGETS) 44 | 45 | $(TARGETS): $(UNVME_SRC)/libunvme.a 46 | 47 | lint: CFLAGS = -Wall -O3 -D_FORTIFY_SOURCE=2 -DUNVME_DEBUG 48 | lint: clean $(OBJS) 49 | @$(RM) *.o 50 | 51 | clean: 52 | $(RM) $(TARGETS) *.o 53 | 54 | .PHONY: all lint clean 55 | 56 | -------------------------------------------------------------------------------- /test/unvme/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2015-2016, Micron Technology, Inc. 3 | # All rights reserved. 4 | # 5 | # Redistribution and use in source and binary forms, with or without 6 | # modification, are permitted provided that the following conditions are met: 7 | # 8 | # 1. Redistributions of source code must retain the above copyright 9 | # notice, this list of conditions and the following disclaimer. 10 | # 11 | # 2. Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # 3. Neither the name of the copyright holder nor the names of its 16 | # contributors may be used to endorse or promote products derived 17 | # from this software without specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | # 31 | 32 | include ../../Makefile.def 33 | 34 | TARGETS = unvme_sim_test unvme_api_test unvme_mts_test unvme_lat_test \ 35 | unvme_mcd_test unvme_info unvme_wrc \ 36 | unvme_get_log_page unvme_get_features 37 | 38 | UNVME_SRC = ../../src 39 | 40 | CPPFLAGS += -I$(UNVME_SRC) 41 | LDLIBS += -lrt -lpthread 42 | 43 | OBJS = $(addsuffix .o, $(TARGETS)) 44 | 45 | all: $(TARGETS) 46 | 47 | $(TARGETS): $(UNVME_SRC)/libunvme.a 48 | 49 | lint: CFLAGS = -Wall -O3 -D_FORTIFY_SOURCE=2 -DUNVME_DEBUG 50 | lint: clean $(OBJS) 51 | @$(RM) *.o 52 | 53 | clean: 54 | $(RM) $(TARGETS) *.o 55 | 56 | .PHONY: all lint clean 57 | -------------------------------------------------------------------------------- /ioengine/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2015-2016, Micron Technology, Inc. 3 | # All rights reserved. 4 | # 5 | # Redistribution and use in source and binary forms, with or without 6 | # modification, are permitted provided that the following conditions are met: 7 | # 8 | # 1. Redistributions of source code must retain the above copyright 9 | # notice, this list of conditions and the following disclaimer. 10 | # 11 | # 2. Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # 3. Neither the name of the copyright holder nor the names of its 16 | # contributors may be used to endorse or promote products derived 17 | # from this software without specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | # 31 | 32 | include ../Makefile.def 33 | 34 | ifneq ($(wildcard $(FIODIR)/fio.h),) 35 | TARGET := unvme_fio 36 | ifeq ($(shell grep -c io_ops_data $(FIODIR)/fio.h),0) 37 | CPPFLAGS += -Dio_ops_data="io_ops->data" 38 | endif 39 | endif 40 | 41 | UNVME_SRC = ../src 42 | 43 | CPPFLAGS += -I$(UNVME_SRC) -I$(FIODIR) 44 | LDFLAGS += -shared -rdynamic 45 | LDLIBS += -lrt 46 | 47 | OBJS = $(addsuffix .o, $(TARGET)) 48 | 49 | all: $(TARGET) 50 | 51 | $(TARGET): $(UNVME_SRC)/libunvme.a 52 | 53 | lint: CFLAGS = -Wall -O3 -D_FORTIFY_SOURCE=2 -DUNVME_DEBUG 54 | lint: clean $(OBJS) 55 | @$(RM) *.o 56 | 57 | clean: 58 | $(RM) unvme_fio *.o 59 | 60 | .PHONY: all lint clean 61 | 62 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2015-2016, Micron Technology, Inc. 3 | # All rights reserved. 4 | # 5 | # Redistribution and use in source and binary forms, with or without 6 | # modification, are permitted provided that the following conditions are met: 7 | # 8 | # 1. Redistributions of source code must retain the above copyright 9 | # notice, this list of conditions and the following disclaimer. 10 | # 11 | # 2. Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # 3. Neither the name of the copyright holder nor the names of its 16 | # contributors may be used to endorse or promote products derived 17 | # from this software without specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | # 31 | 32 | include ../Makefile.def 33 | 34 | TARGET_LIB = libunvme.a 35 | TARGET_LIBSO = libunvme.so 36 | 37 | INCS = $(wildcard *.h) 38 | SRCS = $(wildcard *.c) 39 | OBJS = $(SRCS:.c=.o) 40 | LDLIBS = -lrt 41 | 42 | all: $(TARGET_LIB) $(TARGET_LIBSO) 43 | 44 | $(OBJS): $(INCS) 45 | 46 | $(TARGET_LIB): $(OBJS) 47 | $(AR) crs $@ $^ 48 | 49 | $(TARGET_LIBSO): $(OBJS) 50 | $(CC) -shared -rdynamic -o $@ $^ -lrt 51 | 52 | %.i: %.c 53 | $(CPP) $(CPPFLAGS) -o $@ $< 54 | 55 | lint: CFLAGS = -Wall -O3 -D_FORTIFY_SOURCE=2 -DUNVME_DEBUG 56 | lint: clean $(OBJS) 57 | @$(RM) *.o 58 | 59 | clean: 60 | $(RM) *.a *.so *.o *.i 61 | @if [ "$(HOME)" = "/root" ]; then $(RM) /dev/shm/unvme*; fi 62 | 63 | .PHONY: all lint clean 64 | 65 | -------------------------------------------------------------------------------- /test/python/unvme_get_features.py: -------------------------------------------------------------------------------- 1 | #!/bin/python 2 | # Example to invoke admin get features command. 3 | # 4 | 5 | import sys, struct, ctypes, unvme 6 | from unvme import * 7 | 8 | # Main 9 | try: 10 | pci = sys.argv[1] 11 | except: 12 | print('Usage: python %s PCINAME\n' % sys.argv[0]) 13 | sys.exit(1) 14 | 15 | features = [ '', 16 | '1) Arbitration:', 17 | '2) Power Management:', 18 | '3) LBA Range Type:', 19 | '4) Temperature Threshold:', 20 | '5) Error Recovery:', 21 | '6) Volatile Write Cache:', 22 | '7) Number of Queues:', 23 | '8) Interrupt Coalescing:', 24 | '9) Interrupt Vector Config:', 25 | '10) Write Atomicity:', 26 | '11) Async Event Config:' 27 | ] 28 | 29 | unvme = Unvme(pci) 30 | buf = unvme.alloc(4096) 31 | res = ctypes.c_uint32() 32 | dw6 = ctypes.c_uint32 * 6 33 | 34 | for fid in range(1, 12): 35 | cdw10_15 = dw6(fid, 0, 0, 0, 0, 0) 36 | err = unvme.cmd(-1, 10, 1, buf, 4096, cdw10_15, ctypes.byref(res)) 37 | 38 | if err: 39 | print('%-30s ' % features[fid]) 40 | elif fid == 1: 41 | arb = struct.unpack('> 16)) 55 | elif fid == 8: 56 | print('%-30s time=%u thr=%u' % (features[fid], res.value & 0xff, (res.value >> 8) & 0xff)) 57 | elif fid == 9: 58 | print('%-30s iv=%u cd=%u' % (features[fid], res.value & 0xffff, (res.value >> 16) & 1)) 59 | elif fid == 10: 60 | print('%-30s dn=%u' % (features[fid], res.value & 1)) 61 | elif fid == 11: 62 | print('%-30s smart=%u' % (features[fid], res.value & 0xff)) 63 | 64 | unvme.free(buf) 65 | unvme.close() 66 | 67 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2015-2016, Micron Technology, Inc. 3 | # All rights reserved. 4 | # 5 | # Redistribution and use in source and binary forms, with or without 6 | # modification, are permitted provided that the following conditions are met: 7 | # 8 | # 1. Redistributions of source code must retain the above copyright 9 | # notice, this list of conditions and the following disclaimer. 10 | # 11 | # 2. Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # 3. Neither the name of the copyright holder nor the names of its 16 | # contributors may be used to endorse or promote products derived 17 | # from this software without specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | # 31 | 32 | INSTALLDIR ?= /usr/local 33 | SUBDIRS := src test ioengine doc 34 | 35 | all: $(SUBDIRS) 36 | 37 | $(SUBDIRS): 38 | $(MAKE) -C $@ 39 | 40 | install: uninstall all 41 | mkdir -p $(INSTALLDIR)/include $(INSTALLDIR)/lib $(INSTALLDIR)/bin 42 | /usr/bin/install -m644 src/unvme{,_log,_nvme,_vfio}.h $(INSTALLDIR)/include 43 | /usr/bin/install -m644 src/libunvme.* $(INSTALLDIR)/lib 44 | /usr/bin/install -m755 test/unvme-setup $(INSTALLDIR)/bin 45 | /usr/bin/install -m755 test/unvme/unvme_{info,wrc} $(INSTALLDIR)/bin 46 | /usr/bin/install -m755 test/unvme/unvme_{sim,api,mts,mcd}_test $(INSTALLDIR)/bin 47 | 48 | uninstall: 49 | $(RM) $(INSTALLDIR)/include/unvme* \ 50 | $(INSTALLDIR)/lib/libunvme* \ 51 | $(INSTALLDIR)/bin/unvme* 52 | 53 | clean lint: 54 | @(for d in $(SUBDIRS); do $(MAKE) -C $$d $@; done) 55 | 56 | .PHONY: all install uninstall lint clean $(SUBDIRS) 57 | 58 | -------------------------------------------------------------------------------- /src/rdtsc.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-2016, Micron Technology, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * 3. Neither the name of the copyright holder nor the names of its 16 | * contributors may be used to endorse or promote products derived 17 | * from this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | /** 33 | * @file 34 | * @brief Read TSC (time stamp counter) functions. 35 | */ 36 | 37 | #ifndef _RDTSC_H 38 | #define _RDTSC_H 39 | 40 | #include 41 | #include 42 | #include 43 | 44 | 45 | /** 46 | * Read CLOCK_MONOTONIC_RAW value. 47 | * @return clock value in ns. 48 | */ 49 | static inline uint64_t rdtsc(void) 50 | { 51 | struct timespec ts; 52 | clock_gettime(CLOCK_MONOTONIC_RAW, &ts); 53 | return ts.tv_sec * 1000000000LL + ts.tv_nsec; 54 | } 55 | 56 | /** 57 | * Get the elapsed time since the specified started time. 58 | * @param tsc started time 59 | * @return number of ns elapsed. 60 | */ 61 | static inline uint64_t rdtsc_elapse(uint64_t tsc) { 62 | int64_t et; 63 | do { 64 | et = rdtsc() - tsc; 65 | } while (et <= 0); 66 | return et; 67 | } 68 | 69 | /** 70 | * Get ns per second. 71 | */ 72 | static inline uint64_t rdtsc_second() 73 | { 74 | return 1000000000LL; 75 | } 76 | 77 | #endif // _RDTSC_H 78 | 79 | -------------------------------------------------------------------------------- /test/unvme/unvme_info.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-2016, Micron Technology, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * 3. Neither the name of the copyright holder nor the names of its 16 | * contributors may be used to endorse or promote products derived 17 | * from this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | /** 33 | * @file 34 | * @brief UNVMe device info. 35 | */ 36 | 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | 43 | #include "unvme.h" 44 | 45 | 46 | int main(int argc, char** argv) 47 | { 48 | const char* prog = strrchr(argv[0], '/'); 49 | prog = prog ? prog + 1 : argv[0]; 50 | if (argc < 2) { 51 | warnx("Usage: %s PCINAME", prog); 52 | exit(1); 53 | } 54 | 55 | const unvme_ns_t* ns = unvme_open(argv[1]); 56 | printf("Namespace: %d (of %d)\n", ns->id, ns->nscount); 57 | printf("Vendor ID: %#x\n", ns->vid); 58 | printf("Model number: %.40s\n", ns->mn); 59 | printf("Serial number: %.20s\n", ns->sn); 60 | printf("FW revision: %.8s\n", ns->fr); 61 | printf("Block count: %#lx\n", ns->blockcount); 62 | printf("Page count: %#lx\n", ns->pagecount); 63 | printf("Block size: %d\n", ns->blocksize); 64 | printf("Page size : %d\n", ns->pagesize); 65 | printf("Blocks per page: %d\n", ns->nbpp); 66 | printf("Max blocks per IO: %d\n", ns->maxbpio); 67 | printf("Default IO queue count: %d\n", ns->qcount); 68 | printf("Default IO queue size: %d\n", ns->qsize); 69 | printf("Max IO queue count: %d\n", ns->maxqcount); 70 | printf("Max IO queue size: %d\n", ns->maxqsize); 71 | unvme_close(ns); 72 | 73 | return 0; 74 | } 75 | 76 | -------------------------------------------------------------------------------- /src/unvme_lock.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-2016, Micron Technology, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * 3. Neither the name of the copyright holder nor the names of its 16 | * contributors may be used to endorse or promote products derived 17 | * from this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | /** 33 | * @file 34 | * @brief UNVMe fast read lock with occasional writes. 35 | */ 36 | 37 | #include 38 | 39 | /// Lock write bit 40 | #define UNVME_LOCKWBIT 0x80000000 41 | 42 | /// Simple read write lock 43 | typedef unsigned unvme_lock_t; 44 | 45 | 46 | /** 47 | * Increment read lock and wait if pending write. 48 | * @param lock lock variable 49 | */ 50 | static inline void unvme_lockr(unvme_lock_t* lock) 51 | { 52 | for (;;) { 53 | if (!(__sync_fetch_and_add(lock, 1) & UNVME_LOCKWBIT)) return; 54 | __sync_fetch_and_sub(lock, 1); 55 | sched_yield(); 56 | } 57 | } 58 | 59 | /** 60 | * Decrement read lock. 61 | * @param lock lock variable 62 | */ 63 | static inline void unvme_unlockr(unvme_lock_t* lock) 64 | { 65 | __sync_fetch_and_sub(lock, 1); 66 | } 67 | 68 | /** 69 | * Acquire write lock and wait for all pending read/write. 70 | * @param lock lock variable 71 | */ 72 | static inline void unvme_lockw(unvme_lock_t* lock) 73 | { 74 | for (;;) { 75 | int val = __sync_fetch_and_or(lock, UNVME_LOCKWBIT); 76 | if (val == 0) return; 77 | sched_yield(); 78 | 79 | // if not pending write then just wait for all reads to clear 80 | if (!(val & UNVME_LOCKWBIT)) { 81 | while (*lock != UNVME_LOCKWBIT) sched_yield(); 82 | return; 83 | } 84 | } 85 | } 86 | 87 | /** 88 | * Clear the write lock. 89 | * @param lock lock variable 90 | */ 91 | static inline void unvme_unlockw(unvme_lock_t* lock) 92 | { 93 | __sync_fetch_and_and(lock, ~UNVME_LOCKWBIT); 94 | } 95 | 96 | -------------------------------------------------------------------------------- /test/nvme/nvme_common.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-2016, Micron Technology, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * 3. Neither the name of the copyright holder nor the names of its 16 | * contributors may be used to endorse or promote products derived 17 | * from this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | /** 33 | * @file 34 | * @brief Common NVMe setup functions. 35 | */ 36 | 37 | #include 38 | #include 39 | 40 | #include "unvme_vfio.h" 41 | #include "unvme_nvme.h" 42 | #include "unvme_log.h" 43 | 44 | static vfio_device_t* vfiodev; 45 | static nvme_device_t* nvmedev; 46 | static vfio_dma_t* adminsq; 47 | static vfio_dma_t* admincq; 48 | 49 | 50 | /** 51 | * NVMe setup. 52 | */ 53 | static void nvme_setup(const char* pciname, int aqsize) 54 | { 55 | int b, d, f; 56 | if (sscanf(pciname, "%x:%x.%x", &b, &d, &f) != 3) { 57 | errx(1, "invalid PCI %s (expect %%x:%%x.%%x format)", pciname); 58 | } 59 | int pci = (b << 16) + (d << 8) + f; 60 | 61 | if (log_open("/dev/shm/unvme.log", "w")) exit(1); 62 | vfiodev = vfio_create(NULL, pci); 63 | if (!vfiodev) errx(1, "vfio_create"); 64 | 65 | nvmedev = nvme_create(NULL, vfiodev->fd); 66 | if (!nvmedev) errx(1, "nvme_create"); 67 | 68 | adminsq = vfio_dma_alloc(vfiodev, aqsize * sizeof(nvme_sq_entry_t)); 69 | if (!adminsq) errx(1, "vfio_dma_alloc"); 70 | admincq = vfio_dma_alloc(vfiodev, aqsize * sizeof(nvme_cq_entry_t)); 71 | if (!admincq) errx(1, "vfio_dma_alloc"); 72 | 73 | if (!nvme_adminq_setup(nvmedev, aqsize, adminsq->buf, adminsq->addr, 74 | admincq->buf, admincq->addr)) { 75 | errx(1, "nvme_setup_adminq"); 76 | } 77 | } 78 | 79 | /** 80 | * NVMe cleanup. 81 | */ 82 | static void nvme_cleanup() 83 | { 84 | vfio_dma_free(adminsq); 85 | vfio_dma_free(admincq); 86 | nvme_delete(nvmedev); 87 | vfio_delete(vfiodev); 88 | } 89 | 90 | -------------------------------------------------------------------------------- /src/unvme_log.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-2016, Micron Technology, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * 3. Neither the name of the copyright holder nor the names of its 16 | * contributors may be used to endorse or promote products derived 17 | * from this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | /** 33 | * @file 34 | * @brief Logging header file. 35 | */ 36 | 37 | #ifndef _UNVME_LOG_H 38 | #define _UNVME_LOG_H 39 | 40 | #include 41 | #include 42 | #include 43 | 44 | /// @cond 45 | 46 | #define INFO(fmt, arg...) log_msg(NULL, fmt "\n", ##arg) 47 | #define INFO_FN(fmt, arg...) log_msg(NULL, "%s " fmt "\n", __func__, ##arg) 48 | #define ERROR(fmt, arg...) log_msg(stderr, "ERROR: %s " fmt "\n", __func__, ##arg) 49 | 50 | #ifdef UNVME_DEBUG 51 | #define DEBUG INFO 52 | #define DEBUG_FN INFO_FN 53 | #define HEX_DUMP hex_dump 54 | #else 55 | #define DEBUG(arg...) 56 | #define DEBUG_FN(arg...) 57 | #define HEX_DUMP(arg...) 58 | #endif 59 | 60 | /// @endcond 61 | 62 | 63 | // Export function 64 | int log_open(const char* filename, const char* mode); 65 | void log_close(); 66 | void log_msg(FILE* ftee, const char* fmt, ...); 67 | 68 | 69 | /** 70 | * Hex dump a data block byte-wise. 71 | * @param buf buffer to read into 72 | * @param len size 73 | */ 74 | static inline void hex_dump(void* buf, int len) 75 | { 76 | unsigned char* b = buf; 77 | int i, k = 0, e = 44, t = 44; 78 | char ss[3906]; 79 | 80 | if (len > 1024) len = 1024; 81 | 82 | for (i = 0; i < len; i++) { 83 | if (!(i & 15)) { 84 | if (i > 0) { 85 | ss[k] = ' '; 86 | ss[t++] = '\n'; 87 | k = t; 88 | } 89 | e = t = k + 44; 90 | k += sprintf(ss+k, " %04x:", i); 91 | } 92 | if (!(i & 3)) ss[k++] = ' '; 93 | k += sprintf(ss+k, "%02x", b[i]); 94 | ss[t++] = isprint(b[i]) ? b[i] : '.'; 95 | } 96 | ss[t] = 0; 97 | for (i = k; i < e; i++) ss[i] = ' '; 98 | INFO("%s", ss); 99 | } 100 | 101 | /** 102 | * Invoke calloc and terminated on failure. 103 | */ 104 | static inline void* zalloc(int size) 105 | { 106 | void* mem = calloc(1, size); 107 | if (!mem) { 108 | ERROR("calloc"); 109 | abort(); 110 | } 111 | return mem; 112 | } 113 | 114 | #endif // _UNVME_LOG_H 115 | 116 | -------------------------------------------------------------------------------- /test/unvme-benchmark: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # This script may be used to run fio uNVMe benchmarks. 3 | # It determines which fio engine to use based on the specified device name. 4 | # 5 | # Usage examples: 6 | # % unvme-benchmark /dev/nvme0n1 # using kernel space NVMe driver 7 | # % unvme-benchmark 05:00.0 # using user space uNVMe driver 8 | 9 | PROG=$(basename $0) 10 | USAGE="Usage: ${PROG} DEVICE_NAME" 11 | 12 | cd $(dirname $0) 13 | : ${OUTDIR=${PWD}/out} 14 | 15 | FIOTEXT="[global] 16 | norandommap=1 17 | thread=1 18 | group_reporting=1 19 | direct=1 20 | verify=0 21 | ioengine=IOENGINE 22 | time_based=1 23 | ramp_time=RAMPTIME 24 | runtime=RUNTIME 25 | bs=BLOCKSIZE 26 | filename=FILENAME 27 | 28 | [test] 29 | rw=RW 30 | numjobs=NUMJOBS 31 | iodepth=IODEPTH" 32 | 33 | : ${FIODIR=$(grep '^FIODIR' ../Makefile.def | sed 's/.*=\s*//')} 34 | : ${RW="randwrite randread"} 35 | : ${NUMJOBS="01 08 16"} 36 | : ${IODEPTH="01 08 16 32"} 37 | : ${RAMPTIME=60} 38 | : ${RUNTIME=120} 39 | : ${BLOCKSIZE=4096} 40 | : ${IOENGINE=""} 41 | 42 | FIO=${FIODIR}/fio 43 | 44 | [ $# -lt 1 ] && echo ${USAGE} && exit 1 45 | [ ${EUID} -ne 0 ] && echo "ERROR: ${PROG} must be run as root" && exit 1 46 | [ ! -x "${FIO}" ] && echo "ERROR: Missing ${FIO}" && exit 1 47 | 48 | for i in $@; do 49 | case $i in 50 | /dev/nvme*) 51 | if [ ! -b $i ]; then 52 | echo "Unknown NVMe device: $i" 53 | exit 1 54 | fi 55 | FILENAME=$i 56 | [ -z "${IOENGINE}" ] && IOENGINE=libaio 57 | DRIVER="nvme" 58 | ;; 59 | 60 | [0-9A-Fa-f][0-9A-Fa-f]:[0-9A-Fa-f][0-9A-Fa-f].[0-9A-Fa-f]*) 61 | pcidev=$(echo $i | cut -d/ -f1) 62 | if [ -z "$(lspci -n | grep ${pcidev}\ 0108:)" ]; then 63 | echo "Device ${pcidev} is not SSD" 64 | exit 1 65 | fi 66 | if [ ! -e /sys/bus/pci/drivers/vfio-pci/0000:${pcidev} ]; then 67 | echo "Device ${pcidev} is not uNVMe enabled" 68 | exit 1 69 | fi 70 | FILENAME=$(echo $i | tr :/ .) 71 | [ -z "${IOENGINE}" ] && IOENGINE=$(readlink -f ../ioengine/unvme_fio) 72 | DRIVER="unvme" 73 | [ -n "$(pgrep unvmed)" ] && DRIVER="unvmed" 74 | ;; 75 | 76 | *) 77 | echo ${USAGE} 78 | exit 1 79 | ;; 80 | esac 81 | done 82 | 83 | mkdir -p ${OUTDIR} 84 | [ $? -ne 0 ] && exit 1 85 | 86 | 87 | # 88 | # Echo and execute a command. 89 | # 90 | excmd() { 91 | echo "# $* ($(date '+%a %D %r'))" 92 | eval $* 93 | [ $? -ne 0 ] && exit 1 94 | echo 95 | #read -p "Press to continue..." 96 | } 97 | 98 | 99 | # 100 | # Start test. 101 | # 102 | for rw in ${RW}; do 103 | for qc in ${NUMJOBS}; do 104 | for qd in ${IODEPTH}; do 105 | OUTNAME=${OUTDIR}/${DRIVER}-${rw}-${qc}-${qd} 106 | OUTFILE=${OUTNAME}.out 107 | FIOFILE=${OUTNAME}.fio 108 | 109 | if [ -e ${OUTFILE} ]; then 110 | if [ -n "$(grep 'Run status' ${OUTFILE})" ]; then 111 | echo -e "Skip existing ${OUTFILE}\n" 112 | continue 113 | else 114 | excmd rm -f ${OUTFILE} 115 | fi 116 | fi 117 | 118 | echo "${FIOTEXT}" | sed -e "s?IOENGINE?${IOENGINE}?g;s?RAMPTIME?${RAMPTIME}?g;s?RUNTIME?${RUNTIME}?g;s?BLOCKSIZE?${BLOCKSIZE}?g;s?FILENAME?${FILENAME}?g;s?RW?${rw}?g;s?NUMJOBS?${qc}?g;s?IODEPTH?${qd}?g" > ${FIOFILE} 119 | 120 | echo "========" 121 | excmd /bin/uname -a | tee -a ${OUTFILE} 122 | excmd cat ${FIOFILE} | tee -a ${OUTFILE} 123 | echo -e "\n========\n" >> ${OUTFILE} 124 | excmd /usr/bin/free -h | tee -a ${OUTFILE} 125 | echo -e "\n========\n" >> ${OUTFILE} 126 | 127 | excmd ${FIO} ${FIOFILE} 2>&1 | tee -a ${OUTFILE} 128 | 129 | if [ -n "$(grep 'Run status' ${OUTFILE})" ]; then 130 | echo -e "\n========\n" >> ${OUTFILE} 131 | excmd /usr/bin/free -h | tee -a ${OUTFILE} 132 | rm ${FIOFILE} 133 | else 134 | echo "### ${PROG} terminated on error ###" 135 | exit 1 136 | fi 137 | done 138 | done 139 | done 140 | 141 | -------------------------------------------------------------------------------- /src/unvme_vfio.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-2016, Micron Technology, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * 3. Neither the name of the copyright holder nor the names of its 16 | * contributors may be used to endorse or promote products derived 17 | * from this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | /** 33 | * @file 34 | * @brief VFIO function support header files. 35 | */ 36 | 37 | #ifndef _UNVME_VFIO_H 38 | #define _UNVME_VFIO_H 39 | 40 | #include 41 | #include 42 | #include 43 | 44 | /// VFIO dma allocation structure 45 | typedef struct _vfio_dma { 46 | void* buf; ///< memory buffer 47 | size_t size; ///< allocated size 48 | __u64 addr; ///< I/O DMA address 49 | struct _vfio_mem* mem; ///< private mem 50 | } vfio_dma_t; 51 | 52 | /// VFIO memory allocation entry 53 | typedef struct _vfio_mem { 54 | struct _vfio_device* dev; ///< device owner 55 | int mmap; ///< mmap indication flag 56 | vfio_dma_t dma; ///< dma mapped memory 57 | size_t size; ///< size 58 | struct _vfio_mem* prev; ///< previous entry 59 | struct _vfio_mem* next; ///< next entry 60 | } vfio_mem_t; 61 | 62 | /// VFIO device structure 63 | typedef struct _vfio_device { 64 | int pci; ///< PCI device number 65 | int fd; ///< device descriptor 66 | int groupfd; ///< group file descriptor 67 | int contfd; ///< container file descriptor 68 | int msixsize; ///< max MSIX table size 69 | int msixnvec; ///< number of enabled MSIX vectors 70 | int pagesize; ///< system page size 71 | int ext; ///< externally allocated flag 72 | __u64 iovabase; ///< IO virtual address base 73 | __u64 iovanext; ///< next IO virtual address to use 74 | __u64 iovamask; ///< max IO virtual address mask 75 | pthread_mutex_t lock; ///< multithreaded lock 76 | vfio_mem_t* memlist; ///< memory allocated list 77 | int uiofd; ///< file descriptor of UIO device 78 | void* uiobuf; ///< UIO buffer pointer 79 | off_t uiobufoff; ///< UIO buffer offset 80 | } vfio_device_t; 81 | 82 | // Export functions 83 | vfio_device_t* vfio_create(vfio_device_t* dev, int pci); 84 | void vfio_delete(vfio_device_t* dev); 85 | void vfio_msix_enable(vfio_device_t* dev, int start, int nvec, __s32* efds); 86 | void vfio_msix_disable(vfio_device_t* dev); 87 | int vfio_mem_free(vfio_mem_t* mem); 88 | vfio_dma_t* vfio_dma_map(vfio_device_t* dev, size_t size, void* pmb); 89 | int vfio_dma_unmap(vfio_dma_t* dma); 90 | vfio_dma_t* vfio_dma_alloc(vfio_device_t* dev, size_t size); 91 | int vfio_dma_free(vfio_dma_t* dma); 92 | 93 | #endif // _UNVME_VFIO_H 94 | 95 | -------------------------------------------------------------------------------- /src/unvme_log.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-2016, Micron Technology, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * 3. Neither the name of the copyright holder nor the names of its 16 | * contributors may be used to endorse or promote products derived 17 | * from this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | /** 33 | * @file 34 | * @brief Logging support routines. 35 | */ 36 | 37 | #include 38 | #include 39 | 40 | #include "unvme_log.h" 41 | 42 | 43 | // Static global variables 44 | static FILE* log_fp = NULL; ///< log file pointer 45 | static int log_count = 0; ///< log open count 46 | static pthread_mutex_t log_lock = PTHREAD_MUTEX_INITIALIZER; ///< log lock 47 | 48 | 49 | /** 50 | * Open log file. Only one log file is supported and thus only the first 51 | * call * will create the log file by its specified name. Subsequent calls 52 | * will only be counted. 53 | * @param name log filename 54 | * @param mode open mode 55 | * @return 0 indicating 56 | */ 57 | int log_open(const char* name, const char* mode) 58 | { 59 | pthread_mutex_lock(&log_lock); 60 | if (!log_fp) { 61 | log_fp = fopen(name, mode); 62 | if (!log_fp) { 63 | perror("log_open"); 64 | pthread_mutex_unlock(&log_lock); 65 | return -1; 66 | } 67 | } 68 | log_count++; 69 | pthread_mutex_unlock(&log_lock); 70 | return 0; 71 | } 72 | 73 | /** 74 | * Close the log file (only the last close will effectively close the file). 75 | */ 76 | void log_close() 77 | { 78 | pthread_mutex_lock(&log_lock); 79 | if (log_count > 0) { 80 | if ((--log_count == 0) && log_fp && log_fp != stdout) { 81 | fclose(log_fp); 82 | log_fp = NULL; 83 | } 84 | } 85 | pthread_mutex_unlock(&log_lock); 86 | } 87 | 88 | /** 89 | * Write a formatted message to log file, if log file is opened. 90 | * If err flag is set then log also to stderr. 91 | * @param ftee additional file to print to 92 | * @param fmt formatted message 93 | */ 94 | void log_msg(FILE* ftee, const char* fmt, ...) 95 | { 96 | va_list args; 97 | 98 | pthread_mutex_lock(&log_lock); 99 | if (log_fp) { 100 | va_start(args, fmt); 101 | if (ftee) { 102 | char s[4096]; 103 | vsnprintf(s, sizeof(s), fmt, args); 104 | fprintf(ftee, "%s", s); 105 | fflush(ftee); 106 | fprintf(log_fp, "%s", s); 107 | fflush(log_fp); 108 | } else { 109 | vfprintf(log_fp, fmt, args); 110 | fflush(log_fp); 111 | } 112 | va_end(args); 113 | } else { 114 | va_start(args, fmt); 115 | if (ftee) { 116 | vfprintf(ftee, fmt, args); 117 | fflush(ftee); 118 | } else { 119 | vfprintf(stdout, fmt, args); 120 | fflush(stdout); 121 | } 122 | va_end(args); 123 | } 124 | pthread_mutex_unlock(&log_lock); 125 | } 126 | 127 | -------------------------------------------------------------------------------- /test/python/unvme_wr_ex.py: -------------------------------------------------------------------------------- 1 | #!/bin/python 2 | # Example to write-read-verify synchronously and asynchronously. 3 | # 4 | 5 | from __future__ import print_function 6 | import os, sys, argparse, random, time, ctypes, unvme 7 | from ctypes import * 8 | 9 | # Write-Read-Verify example test class. 10 | class WRTest: 11 | def __init__(self, pci, ioc, maxnlb): 12 | self.ioc = ioc 13 | self.maxnlb = maxnlb 14 | self.unvme = unvme.Unvme(pci) 15 | self.seed = time.time() 16 | self.ns = self.unvme.info() 17 | self.wpb = self.ns.blocksize / 8 18 | self.wbuf = [] 19 | self.rbuf = [] 20 | for i in range(ioc): 21 | buf = cast(self.unvme.alloc(maxnlb * self.ns.blocksize), POINTER(c_uint64)) 22 | self.wbuf.append(buf) 23 | buf = cast(self.unvme.alloc(maxnlb * self.ns.blocksize), POINTER(c_uint64)) 24 | self.rbuf.append(buf) 25 | 26 | def __del__(self): 27 | for i in range(0, self.ioc): 28 | self.unvme.free(self.wbuf[i]) 29 | self.unvme.free(self.rbuf[i]) 30 | 31 | def error(*fmt, **args): 32 | print(*fmt, file=sys.stderr, **args) 33 | sys.exit(1) 34 | 35 | def fillBuffer(self, buf, lba, nlb): 36 | i = 0 37 | for b in range(nlb): 38 | pat = (lba << 24) | b 39 | for w in range(self.wpb): 40 | buf[i] = pat 41 | i = i + 1 42 | lba = lba + 1 43 | 44 | def verifyBuffer(self, buf, lba, nlb): 45 | i = 0 46 | for b in range(nlb): 47 | pat = (lba << 24) | b 48 | for w in range(self.wpb): 49 | if buf[i] != pat: 50 | self.error('ERROR: data miscompare at lba %#lx' % lba) 51 | i = i + 1 52 | lba = lba + 1 53 | 54 | def pickqbc(self, seed): 55 | random.seed(self.seed + seed) 56 | nlb = random.randint(1, self.maxnlb) 57 | lba = random.randrange(0, self.ns.blockcount - nlb - 1) 58 | q = (lba + nlb) % self.ns.qcount 59 | return q, lba, nlb 60 | 61 | def syncTest(self): 62 | print('Sync test'); 63 | for i in range(self.ioc): 64 | q, lba, nlb = self.pickqbc(i) 65 | print(' write q=%-3d lba=0x%-9lx nlb=%#x' % (q, lba, nlb)) 66 | self.fillBuffer(self.wbuf[i], lba, nlb) 67 | self.unvme.write(q, self.wbuf[i], lba, nlb) 68 | 69 | for i in range(self.ioc): 70 | q, lba, nlb = self.pickqbc(i) 71 | print(' read q=%-3d lba=0x%-9lx nlb=%#x' % (q, lba, nlb)) 72 | self.unvme.read(q, self.rbuf[i], lba, nlb) 73 | self.verifyBuffer(self.rbuf[i], lba, nlb) 74 | 75 | def asyncTest(self): 76 | print('Async test'); 77 | wiod = [] 78 | for i in range(self.ioc): 79 | q, lba, nlb = self.pickqbc(i) 80 | print(' awrite q=%-3d lba=0x%-9lx nlb=%#x' % (q, lba, nlb)) 81 | self.fillBuffer(self.wbuf[i], lba, nlb) 82 | iod = self.unvme.awrite(q, self.wbuf[i], lba, nlb) 83 | wiod.append(iod) 84 | 85 | riod = [] 86 | for i in range(self.ioc): 87 | q, lba, nlb = self.pickqbc(i) 88 | print(' aread q=%-3d lba=0x%-9lx nlb=%#x' % (q, lba, nlb)) 89 | iod = self.unvme.aread(q, self.rbuf[i], lba, nlb) 90 | riod.append(iod) 91 | 92 | for i in range(self.ioc): 93 | q, lba, nlb = self.pickqbc(i) 94 | print(' apoll q=%-3d lba=0x%-9lx nlb=%#x' % (q, lba, nlb)) 95 | if self.unvme.apoll(wiod[i], 30) or self.unvme.apoll(riod[i], 30): 96 | self.error('ERROR: apoll') 97 | self.verifyBuffer(self.rbuf[i], lba, nlb) 98 | 99 | 100 | # Main program. 101 | parser = argparse.ArgumentParser(description='RANDOM WRITE-READ TEST EXAMPLE', add_help=False) 102 | parser.add_argument('--ioc', help='number of IOs per test', type=int, default=8) 103 | parser.add_argument('--nlb', help='max number of blocks per IO', type=int, default=8192) 104 | parser.add_argument('pci', help='PCI device name (as 0a:00.0[/1] format)') 105 | if len(sys.argv) == 1: 106 | parser.print_help() 107 | sys.exit(1) 108 | opts = parser.parse_args() 109 | 110 | print('PYTHON RANDOM WRITE-READ TEST EXAMPLE BEGIN') 111 | ex = WRTest(opts.pci, opts.ioc, opts.nlb) 112 | ex.syncTest() 113 | ex.asyncTest() 114 | print('PYTHON RANDOM WRITE-READ TEST EXAMPLE COMPLETE') 115 | 116 | -------------------------------------------------------------------------------- /test/nvme/nvme_set_features.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-2016, Micron Technology, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * 3. Neither the name of the copyright holder nor the names of its 16 | * contributors may be used to endorse or promote products derived 17 | * from this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | /** 33 | * @file 34 | * @brief Invoke NVMe set features command. 35 | */ 36 | 37 | #include "nvme_common.c" 38 | 39 | 40 | /** 41 | * Main program. 42 | */ 43 | int main(int argc, char* argv[]) 44 | { 45 | const char* usage = "Usage: %s PCINAME NSID FEATURE_ID FEATURE_ARG"; 46 | 47 | if (argc < 5) { 48 | warnx(usage, argv[0]); 49 | exit(1); 50 | } 51 | 52 | char* s = argv[2]; 53 | int nsid = strtol(s, &s, 0); 54 | if (*s) { 55 | warnx(usage, argv[0]); 56 | exit(1); 57 | } 58 | s = argv[3]; 59 | int fid = strtol(s, &s, 0); 60 | if (*s) { 61 | warnx(usage, argv[0]); 62 | exit(1); 63 | } 64 | s = argv[3]; 65 | u32 res = strtol(s, &s, 0); 66 | if (*s) { 67 | warnx(usage, argv[0]); 68 | exit(1); 69 | } 70 | 71 | if (fid < NVME_FEATURE_ARBITRATION || fid > NVME_FEATURE_ASYNC_EVENT || 72 | fid == NVME_FEATURE_LBA_RANGE) 73 | errx(1, "features_id %d not supported", fid); 74 | 75 | nvme_setup(argv[1], 8); 76 | vfio_dma_t* dma = vfio_dma_alloc(vfiodev, sizeof(nvme_feature_lba_data_t)); 77 | if (!dma) errx(1, "vfio_dma_alloc"); 78 | u64 prp1 = dma->addr; 79 | int err = nvme_acmd_get_features(nvmedev, nsid, fid, prp1, 0L, &res); 80 | if (err) errx(1, "set_features %d failed", fid); 81 | 82 | if (fid == NVME_FEATURE_ARBITRATION) { 83 | nvme_feature_arbitration_t* arb = (nvme_feature_arbitration_t*)&res; 84 | printf("1) Arbitration: hpw=%u mpw=%u lpw=%u ab=%u\n", 85 | arb->hpw, arb->mpw, arb->lpw, arb->ab); 86 | } else if (fid == NVME_FEATURE_POWER_MGMT) { 87 | nvme_feature_power_mgmt_t* pm = (nvme_feature_power_mgmt_t*)&res; 88 | printf("2) Power Management: ps=%u\n", pm->ps); 89 | } else if (fid == NVME_FEATURE_TEMP_THRESHOLD) { 90 | nvme_feature_temp_threshold_t* tt = (nvme_feature_temp_threshold_t*)&res; 91 | printf("4) Temperature Threshold: tmpth=%u\n", tt->tmpth); 92 | } else if (fid == NVME_FEATURE_ERROR_RECOVERY) { 93 | nvme_feature_error_recovery_t* er = (nvme_feature_error_recovery_t*)&res; 94 | printf("5) Error Recovery: tler=%u\n", er->tler); 95 | } else if (fid == NVME_FEATURE_WRITE_CACHE) { 96 | nvme_feature_write_cache_t* wc = (nvme_feature_write_cache_t*)&res; 97 | printf("6) Volatile Write Cache: wce=%u\n", wc->wce); 98 | } else if (fid == NVME_FEATURE_NUM_QUEUES) { 99 | nvme_feature_num_queues_t* nq = (nvme_feature_num_queues_t*)&res; 100 | printf("7) Number of Queues: nsq=%u ncq=%u\n", nq->nsq, nq->ncq); 101 | } else if (fid == NVME_FEATURE_INT_COALESCING) { 102 | nvme_feature_int_coalescing_t* intc = (nvme_feature_int_coalescing_t*)&res; 103 | printf("8) Interrupt Coalescing: time=%u thr=%u\n", intc->time, intc->thr); 104 | } else if (fid == NVME_FEATURE_INT_VECTOR) { 105 | nvme_feature_int_vector_t* intv = (nvme_feature_int_vector_t*)&res; 106 | printf("9) Interrupt Vector Config: iv=%u cd=%u\n", intv->iv, intv->cd); 107 | } else if (fid == NVME_FEATURE_WRITE_ATOMICITY) { 108 | nvme_feature_write_atomicity_t* wa = (nvme_feature_write_atomicity_t*)&res; 109 | printf("10) Write Atomicity: dn=%u\n", wa->dn); 110 | } else if (fid == NVME_FEATURE_ASYNC_EVENT) { 111 | nvme_feature_async_event_t* aec = (nvme_feature_async_event_t*)&res; 112 | printf("11) Async Event Config: smart=%u\n", aec->smart); 113 | } 114 | 115 | nvme_cleanup(); 116 | return 0; 117 | } 118 | 119 | -------------------------------------------------------------------------------- /test/unvme/unvme_sim_test.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-2016, Micron Technology, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * 3. Neither the name of the copyright holder nor the names of its 16 | * contributors may be used to endorse or promote products derived 17 | * from this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | /** 33 | * @file 34 | * @brief UNVMe simple write-read-verify test. 35 | */ 36 | 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | 45 | #include "unvme.h" 46 | 47 | 48 | int main(int argc, char** argv) 49 | { 50 | const char* usage = "Usage: %s [OPTION]... PCINAME\n\ 51 | -a LBA use starting LBA (default random)\n\ 52 | -s SIZE data size (default 100M)\n\ 53 | PCINAME PCI device name (as 01:00.0[/1] format)"; 54 | 55 | int opt; 56 | u64 datasize = 100 * 1024 * 1024; 57 | const char* prog = strrchr(argv[0], '/'); 58 | prog = prog ? prog + 1 : argv[0]; 59 | char* endp; 60 | u64 slba = -1L; 61 | 62 | while ((opt = getopt(argc, argv, "s:a:")) != -1) { 63 | switch (opt) { 64 | case 'a': 65 | slba = strtoull(optarg, 0, 0); 66 | break; 67 | case 's': 68 | datasize = strtoull(optarg, &endp, 0); 69 | if (endp) { 70 | int c = tolower(*endp); 71 | if (c == 'k') datasize *= 1024; 72 | else if (c == 'm') datasize *= 1024 * 1024; 73 | else if (c == 'g') datasize *= 1024 * 1024 * 1024; 74 | } 75 | break; 76 | default: 77 | warnx(usage, prog); 78 | exit(1); 79 | } 80 | } 81 | if ((optind + 1) != argc) { 82 | warnx(usage, prog); 83 | exit(1); 84 | } 85 | char* pciname = argv[optind]; 86 | 87 | printf("SIMPLE WRITE-READ-VERIFY TEST BEGIN\n"); 88 | const unvme_ns_t* ns = unvme_open(pciname); 89 | if (!ns) exit(1); 90 | printf("%s qc=%d/%d qs=%d/%d bc=%#lx bs=%d mbio=%d ds=%#lx\n", 91 | ns->device, ns->qcount, ns->maxqcount, ns->qsize, ns->maxqsize, 92 | ns->blockcount, ns->blocksize, ns->maxbpio, datasize); 93 | 94 | void* buf = unvme_alloc(ns, datasize); 95 | if (!buf) errx(1, "unvme_alloc %ld failed", datasize); 96 | time_t tstart = time(0); 97 | u64 nlb = datasize / ns->blocksize; 98 | if (!nlb) nlb = 1; 99 | 100 | if (slba == -1L) { 101 | srandom(time(0)); 102 | slba = (random() % ns->blockcount) - (ns->qcount * nlb); 103 | slba &= ~(ns->nbpp - 1); 104 | if (slba >= ns->blockcount) slba = 0; 105 | } 106 | 107 | u64* p = buf; 108 | u64 wsize = datasize / sizeof(u64); 109 | u64 w; 110 | int q, stat; 111 | 112 | for (q = 0; q < ns->qcount; q++) { 113 | printf("Test q=%-2d lba=%#lx nlb=%#lx\n", q, slba, nlb); 114 | for (w = 0; w < wsize; w++) { 115 | u64 pat = (q << 24) + w; 116 | p[w] = (pat << 32) | (~pat & 0xffffffff); 117 | } 118 | stat = unvme_write(ns, q, p, slba, nlb); 119 | if (stat) 120 | errx(1, "unvme_write failed: slba=%#lx nlb=%#lx stat=%#x", slba, nlb, stat); 121 | for (int i = 0; i < (nlb * ns->blocksize) / sizeof(u64); i++) p[i] = 0; 122 | stat = unvme_read(ns, q, p, slba, nlb); 123 | if (stat) 124 | errx(1, "unvme_read failed: slba=%#lx nlb=%#lx stat=%#x", slba, nlb, stat); 125 | for (w = 0; w < wsize; w++) { 126 | u64 pat = (q << 24) + w; 127 | pat = (pat << 32) | (~pat & 0xffffffff); 128 | if (p[w] != pat) { 129 | w *= sizeof(w); 130 | slba += w / ns->blocksize; 131 | w %= ns->blocksize; 132 | errx(1, "miscompare at lba %#lx offset %#lx", slba, w); 133 | } 134 | } 135 | slba += nlb; 136 | if (slba >= ns->blockcount) slba = 0; 137 | } 138 | 139 | unvme_free(ns, buf); 140 | unvme_close(ns); 141 | 142 | printf("SIMPLE WRITE-READ-VERIFY TEST COMPLETE (%ld secs)\n", time(0) - tstart); 143 | return 0; 144 | } 145 | 146 | -------------------------------------------------------------------------------- /test/nvme/nvme_get_features.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-2016, Micron Technology, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * 3. Neither the name of the copyright holder nor the names of its 16 | * contributors may be used to endorse or promote products derived 17 | * from this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | /** 33 | * @file 34 | * @brief Invoke NVMe get features command. 35 | */ 36 | 37 | #include "nvme_common.c" 38 | 39 | static char* features[] = { 40 | "", 41 | "1) Arbitration:", 42 | "2) Power Management:", 43 | "3) LBA Range Type:", 44 | "4) Temperature Threshold:", 45 | "5) Error Recovery:", 46 | "6) Volatile Write Cache:", 47 | "7) Number of Queues:", 48 | "8) Interrupt Coalescing:", 49 | "9) Interrupt Vector Config:", 50 | "10) Write Atomicity:", 51 | "11) Async Event Config:" 52 | }; 53 | 54 | /** 55 | * Main program. 56 | */ 57 | int main(int argc, char* argv[]) 58 | { 59 | const char* usage = "Usage: %s PCINAME [NSID]"; 60 | 61 | if (argc < 2) { 62 | warnx(usage, argv[0]); 63 | exit(1); 64 | } 65 | 66 | int nsid = 1; 67 | if (argc > 2) { 68 | char* s = argv[2]; 69 | nsid = strtol(s, &s, 0); 70 | if (*s) { 71 | warnx(usage, argv[0]); 72 | exit(1); 73 | } 74 | } 75 | 76 | nvme_setup(argv[1], 8); 77 | vfio_dma_t* dma = vfio_dma_alloc(vfiodev, sizeof(nvme_feature_lba_data_t)); 78 | if (!dma) errx(1, "vfio_dma_alloc"); 79 | u32 res; 80 | 81 | int fid; 82 | for (fid = NVME_FEATURE_ARBITRATION; fid <= NVME_FEATURE_ASYNC_EVENT; fid++) { 83 | int err = nvme_acmd_get_features(nvmedev, nsid, fid, dma->addr, 0L, &res); 84 | 85 | if (err) { 86 | printf("%-30s \n", features[fid]); 87 | } else if (fid == NVME_FEATURE_ARBITRATION) { 88 | nvme_feature_arbitration_t* arb = (nvme_feature_arbitration_t*)&res; 89 | printf("%-30s hpw=%u mpw=%u lpw=%u ab=%u\n", features[fid], 90 | arb->hpw, arb->mpw, arb->lpw, arb->ab); 91 | } else if (fid == NVME_FEATURE_POWER_MGMT) { 92 | nvme_feature_power_mgmt_t* pm = (nvme_feature_power_mgmt_t*)&res; 93 | printf("%-30s ps=%u\n", features[fid], pm->ps); 94 | } else if (fid == NVME_FEATURE_LBA_RANGE) { 95 | nvme_feature_lba_range_t* lbart = (nvme_feature_lba_range_t*)&res; 96 | printf("%-30s num=%u\n", features[fid], lbart->num); 97 | } else if (fid == NVME_FEATURE_TEMP_THRESHOLD) { 98 | nvme_feature_temp_threshold_t* tt = (nvme_feature_temp_threshold_t*)&res; 99 | printf("%-30s tmpth=%u\n", features[fid], tt->tmpth); 100 | } else if (fid == NVME_FEATURE_ERROR_RECOVERY) { 101 | nvme_feature_error_recovery_t* er = (nvme_feature_error_recovery_t*)&res; 102 | printf("%-30s tler=%u\n", features[fid], er->tler); 103 | } else if (fid == NVME_FEATURE_WRITE_CACHE) { 104 | nvme_feature_write_cache_t* wc = (nvme_feature_write_cache_t*)&res; 105 | printf("%-30s wce=%u\n", features[fid], wc->wce); 106 | } else if (fid == NVME_FEATURE_NUM_QUEUES) { 107 | nvme_feature_num_queues_t* nq = (nvme_feature_num_queues_t*)&res; 108 | printf("%-30s nsq=%u ncq=%u\n", features[fid], nq->nsq, nq->ncq); 109 | } else if (fid == NVME_FEATURE_INT_COALESCING) { 110 | nvme_feature_int_coalescing_t* intc = (nvme_feature_int_coalescing_t*)&res; 111 | printf("%-30s time=%u thr=%u\n", features[fid], intc->time, intc->thr); 112 | } else if (fid == NVME_FEATURE_INT_VECTOR) { 113 | nvme_feature_int_vector_t* intv = (nvme_feature_int_vector_t*)&res; 114 | printf("%-30s iv=%u cd=%u\n", features[fid], intv->iv, intv->cd); 115 | } else if (fid == NVME_FEATURE_WRITE_ATOMICITY) { 116 | nvme_feature_write_atomicity_t* wa = (nvme_feature_write_atomicity_t*)&res; 117 | printf("%-30s dn=%u\n", features[fid], wa->dn); 118 | } else if (fid == NVME_FEATURE_ASYNC_EVENT) { 119 | nvme_feature_async_event_t* aec = (nvme_feature_async_event_t*)&res; 120 | printf("%-30s smart=%u\n", features[fid], aec->smart); 121 | } 122 | } 123 | 124 | nvme_cleanup(); 125 | return 0; 126 | } 127 | 128 | -------------------------------------------------------------------------------- /src/unvme.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-2016, Micron Technology, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * 3. Neither the name of the copyright holder nor the names of its 16 | * contributors may be used to endorse or promote products derived 17 | * from this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | /** 33 | * @file 34 | * @brief UNVMe client header file. 35 | */ 36 | 37 | #ifndef _UNVME_H 38 | #define _UNVME_H 39 | 40 | #include 41 | 42 | #ifndef _U_TYPE 43 | #define _U_TYPE ///< bit size data types 44 | typedef int8_t s8; ///< 8-bit signed 45 | typedef int16_t s16; ///< 16-bit signed 46 | typedef int32_t s32; ///< 32-bit signed 47 | typedef int64_t s64; ///< 64-bit signed 48 | typedef uint8_t u8; ///< 8-bit unsigned 49 | typedef uint16_t u16; ///< 16-bit unsigned 50 | typedef uint32_t u32; ///< 32-bit unsigned 51 | typedef uint64_t u64; ///< 64-bit unsigned 52 | #endif // _U_TYPE 53 | 54 | #define UNVME_TIMEOUT 60 ///< default timeout in seconds 55 | #define UNVME_QSIZE 256 ///< default I/O queue size 56 | 57 | /// Namespace attributes structure 58 | typedef struct _unvme_ns { 59 | u32 pci; ///< PCI device id 60 | u16 id; ///< namespace id 61 | u16 vid; ///< vendor id 62 | char device[16]; ///< PCI device name (BB:DD.F/N) 63 | char mn[40]; ///< model number 64 | char sn[20]; ///< serial number 65 | char fr[8]; ///< firmware revision 66 | u64 blockcount; ///< total number of available blocks 67 | u64 pagecount; ///< total number of available pages 68 | u16 blocksize; ///< logical block size 69 | u16 pagesize; ///< page size 70 | u16 blockshift; ///< block size shift value 71 | u16 pageshift; ///< page size shift value 72 | u16 bpshift; ///< block to page shift 73 | u16 nbpp; ///< number of blocks per page 74 | u16 maxbpio; ///< max number of blocks per I/O 75 | u16 maxppio; ///< max number of pages per I/O 76 | u16 maxiopq; ///< max number of I/O submissions per queue 77 | u16 nscount; ///< number of namespaces available 78 | u32 qcount; ///< number of I/O queues 79 | u32 maxqcount; ///< max number of queues supported 80 | u32 qsize; ///< I/O queue size 81 | u32 maxqsize; ///< max queue size supported 82 | void* ses; ///< associated session 83 | } unvme_ns_t; 84 | 85 | /// I/O descriptor (not to be copied and is cleared upon apoll completion) 86 | typedef struct _unvme_iod { 87 | void* buf; ///< data buffer (as submitted) 88 | u64 slba; ///< starting lba (as submitted) 89 | u32 nlb; ///< number of blocks (as submitted) 90 | u32 qid; ///< queue id (as submitted) 91 | u32 opc; ///< op code 92 | u32 id; ///< descriptor id 93 | } *unvme_iod_t; 94 | 95 | // Export functions 96 | const unvme_ns_t* unvme_open(const char* pciname); 97 | const unvme_ns_t* unvme_openq(const char* pciname, int qcount, int qsize); 98 | int unvme_close(const unvme_ns_t* ns); 99 | 100 | void* unvme_alloc(const unvme_ns_t* ns, u64 size); 101 | int unvme_free(const unvme_ns_t* ns, void* buf); 102 | 103 | int unvme_write(const unvme_ns_t* ns, int qid, const void* buf, u64 slba, u32 nlb); 104 | int unvme_read(const unvme_ns_t* ns, int qid, void* buf, u64 slba, u32 nlb); 105 | int unvme_cmd(const unvme_ns_t* ns, int qid, int opc, int nsid, void* buf, u64 bufsz, u32 cdw10_15[6], u32* cqe_cs); 106 | 107 | unvme_iod_t unvme_awrite(const unvme_ns_t* ns, int qid, const void* buf, u64 slba, u32 nlb); 108 | unvme_iod_t unvme_aread(const unvme_ns_t* ns, int qid, void* buf, u64 slba, u32 nlb); 109 | unvme_iod_t unvme_acmd(const unvme_ns_t* ns, int qid, int opc, int nsid, void* buf, u64 bufsz, u32 cdw10_15[6]); 110 | 111 | int unvme_apoll(unvme_iod_t iod, int timeout); 112 | int unvme_apoll_cs(unvme_iod_t iod, int timeout, u32* cqe_cs); 113 | 114 | #endif // _UNVME_H 115 | 116 | -------------------------------------------------------------------------------- /test/nvme/nvme_identify.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-2016, Micron Technology, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * 3. Neither the name of the copyright holder nor the names of its 16 | * contributors may be used to endorse or promote products derived 17 | * from this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | /** 33 | * @file 34 | * @brief Invoke NVMe identify command. 35 | */ 36 | 37 | #include 38 | #include "nvme_common.c" 39 | 40 | 41 | /** 42 | * Print controller info. 43 | */ 44 | void print_controller(void* buf) 45 | { 46 | nvme_identify_ctlr_t* ctlr = buf; 47 | 48 | printf("Identify Controller 0x0\n"); 49 | printf("=======================\n"); 50 | printf("vid : %#x\n", ctlr->vid); 51 | printf("ssvid : %#x\n", ctlr->ssvid); 52 | printf("sn : %.20s\n", ctlr->sn); 53 | printf("mn : %.40s\n", ctlr->mn); 54 | printf("fr : %.8s\n", ctlr->fr); 55 | printf("rab : %#x\n", ctlr->rab); 56 | printf("ieee : %02x%02x%02x\n", ctlr->ieee[0], ctlr->ieee[1], ctlr->ieee[2]); 57 | printf("mic : %#x\n", ctlr->mic); 58 | printf("mdts : %#x\n", ctlr->mdts); 59 | printf("oacs : %#x\n", ctlr->oacs); 60 | printf("acl : %#x\n", ctlr->acl); 61 | printf("aerl : %#x\n", ctlr->aerl); 62 | printf("frmw : %#x\n", ctlr->frmw); 63 | printf("lpa : %#x\n", ctlr->lpa); 64 | printf("elpe : %#x\n", ctlr->elpe); 65 | printf("npss : %#x\n", ctlr->npss); 66 | printf("avscc : %#x\n", ctlr->avscc); 67 | printf("sqes : %#x\n", ctlr->sqes); 68 | printf("cqes : %#x\n", ctlr->cqes); 69 | printf("nn : %#x\n", ctlr->nn); 70 | printf("oncs : %#x\n", ctlr->oncs); 71 | printf("fuses : %#x\n", ctlr->fuses); 72 | printf("fna : %#x\n", ctlr->fna); 73 | printf("vwc : %#x\n", ctlr->vwc); 74 | printf("awun : %#x\n", ctlr->awun); 75 | printf("awupf : %#x\n", ctlr->awupf); 76 | printf("nvscc : %#x\n", ctlr->nvscc); 77 | } 78 | 79 | /** 80 | * Print namespace info. 81 | */ 82 | void print_namespace(void* buf, int nsid) 83 | { 84 | nvme_identify_ns_t* ns = buf; 85 | 86 | printf("\nIdentify Namespace %#x\n", nsid); 87 | printf("======================\n"); 88 | printf("nsze : %#lx\n", ns->nsze); 89 | printf("ncap : %#lx\n", ns->ncap); 90 | printf("nuse : %#lx\n", ns->nuse); 91 | printf("nsfeat : %#x\n", ns->nsfeat); 92 | printf("nlbaf : %#x\n", ns->nlbaf); 93 | printf("flbas : %#x\n", ns->flbas); 94 | printf("mc : %#x\n", ns->mc); 95 | printf("dpc : %#x\n", ns->dpc); 96 | printf("dps : %#x\n", ns->dps); 97 | 98 | int i; 99 | for (i = 0; i <= ns->nlbaf; i++) { 100 | printf("lbaf.%-3d : ms=%-3d lbads=%-3d rp=%-2d %s\n", 101 | i, ns->lbaf[i].ms, ns->lbaf[i].lbads, ns->lbaf[i].rp, 102 | (ns->flbas & 0xf) == i ? "(formatted)" : ""); 103 | } 104 | } 105 | 106 | /** 107 | * Main program. 108 | */ 109 | int main(int argc, char* argv[]) 110 | { 111 | if (argc < 2) { 112 | warnx("Usage: %s PCINAME [NSID]", argv[0]); 113 | exit(1); 114 | } 115 | int nsid = 0; 116 | if (argc > 2) { 117 | nsid = strtol(argv[2], 0, 0); 118 | if (nsid < 1) errx(1, "invalid nsid %#x", nsid); 119 | } 120 | 121 | nvme_setup(argv[1], 8); 122 | vfio_dma_t* dma = vfio_dma_alloc(vfiodev, 16384); 123 | if (!dma) errx(1, "vfio_dma_alloc"); 124 | 125 | if (nvme_acmd_identify(nvmedev, 0, dma->addr, dma->addr + 4096)) 126 | errx(1, "nvme_acmd_identify 0"); 127 | nvme_identify_ctlr_t* ctlr = malloc(sizeof(nvme_identify_ctlr_t)); 128 | memcpy(ctlr, dma->buf, sizeof(nvme_identify_ctlr_t)); 129 | print_controller(ctlr); 130 | 131 | u64 nsaddr = dma->addr + 8192; 132 | void* nsbuf = dma->buf + 8192; 133 | 134 | if (nsid) { 135 | if (nsid > ctlr->nn) 136 | errx(1, "invalid nsid %d", nsid); 137 | if (nvme_acmd_identify(nvmedev, nsid, nsaddr, nsaddr + 4096)) 138 | errx(1, "nvme_acmd_identify %d", nsid); 139 | print_namespace(nsbuf + 8192, nsid); 140 | } else { 141 | for (nsid = 1; nsid <= ctlr->nn; nsid++) { 142 | if (nvme_acmd_identify(nvmedev, nsid, nsaddr, nsaddr + 4096)) 143 | errx(1, "nvme_acmd_identify %d", nsid); 144 | print_namespace(nsbuf + 8192, nsid); 145 | } 146 | } 147 | 148 | free(ctlr); 149 | vfio_dma_free(dma); 150 | nvme_cleanup(); 151 | 152 | return 0; 153 | } 154 | 155 | -------------------------------------------------------------------------------- /test/nvme/nvme_get_log_page.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-2016, Micron Technology, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * 3. Neither the name of the copyright holder nor the names of its 16 | * contributors may be used to endorse or promote products derived 17 | * from this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | /** 33 | * @file 34 | * @brief Invoke NVMe get log page command. 35 | */ 36 | 37 | #include "nvme_common.c" 38 | 39 | /** 40 | * Print log page error info. 41 | */ 42 | void print_error_info(void* buf) 43 | { 44 | nvme_log_page_error_t* err = buf; 45 | 46 | printf("Log Page Error Information\n"); 47 | printf("==========================\n"); 48 | printf("Error Count : %ld\n", err->count); 49 | printf("Submission Queue ID : %d\n", err->sqid); 50 | printf("Command ID : %d\n", err->cid); 51 | printf("Status Field : %#x\n", err->sf); 52 | printf("Parameter Error Location : byte=%d bit=%d\n", err->byte, err->bit); 53 | printf("LBA : %#lx\n", err->lba); 54 | printf("Namespace : %d\n", err->ns); 55 | printf("Vendor Specific : %#x\n", err->vspec); 56 | } 57 | 58 | /** 59 | * Print 128-bit value as hex into designated string dest. 60 | */ 61 | char* hex128(char* dest, u64 val[2]) 62 | { 63 | if (val[0]) sprintf(dest, "%#lx%08lx", val[0], val[1]); 64 | else sprintf(dest, "%ld", val[1]); 65 | return dest; 66 | } 67 | 68 | /** 69 | * Print log page smart health info. 70 | */ 71 | void print_smart_health(void* buf) 72 | { 73 | char s[64]; 74 | nvme_log_page_health_t* smh = buf; 75 | 76 | printf("Log Page SMART / Health Information\n"); 77 | printf("===================================\n"); 78 | printf("Critical Warning : %#x\n", smh->warn); 79 | printf("Temperature (Kelvin) : %d\n", smh->temp); 80 | printf("Available Spare (%%) : %d\n", smh->avspare); 81 | printf("Available Spare Threshold (%%) : %d\n", smh->avsparethresh); 82 | printf("Percentage Used : %d\n", smh->used); 83 | printf("# of Data Units Read : %s\n", hex128(s, smh->dur)); 84 | printf("# of Data Units Written : %s\n", hex128(s, smh->duw)); 85 | printf("# of Host Read Commands : %s\n", hex128(s, smh->hrc)); 86 | printf("# of Host Write Commands : %s\n", hex128(s, smh->hwc)); 87 | printf("Controller Busy Time (mins) : %s\n", hex128(s, smh->cbt)); 88 | printf("# of Power Cycles : %s\n", hex128(s, smh->pcycles)); 89 | printf("# of Power On Hours : %s\n", hex128(s, smh->phours)); 90 | printf("# of Unsafe Shutdowns : %s\n", hex128(s, smh->unsafeshut)); 91 | printf("# of Media Errors : %s\n", hex128(s, smh->merrors)); 92 | printf("# of Error Log Entries : %s\n", hex128(s, smh->errlogs)); 93 | } 94 | 95 | /** 96 | * Print log page firmware slot info. 97 | */ 98 | void print_firmware_slot(void* buf) 99 | { 100 | nvme_log_page_fw_t* fw = buf; 101 | 102 | printf("Log Page Firmware Slot Information\n"); 103 | printf("==================================\n"); 104 | int i; 105 | for (i = 0; i < 7; i++) { 106 | printf("Firmware Revision SLot %d : %.8s %s\n", 107 | i + 1, (char*)&fw->fr[i], fw->afi == (i + 1) ? "(active)" : ""); 108 | } 109 | } 110 | 111 | /** 112 | * Main program. 113 | */ 114 | int main(int argc, char* argv[]) 115 | { 116 | const char* usage = "Usage: %s PCINAME NSID LOG_PAGE_ID\n\ 117 | (LOG_PAGE_ID 1 = error information)\n\ 118 | (LOG_PAGE_ID 2 = SMART / Health information)\n\ 119 | (LOG_PAGE_ID 3 = firmware slot information)"; 120 | 121 | if (argc != 4) { 122 | warnx(usage, argv[0]); 123 | exit(1); 124 | } 125 | 126 | char* s = argv[2]; 127 | int nsid = strtol(s, &s, 0); 128 | int lid = strtol(argv[3], &s, 0); 129 | if (*s || (lid < 1 || lid > 3)) { 130 | warnx(usage, argv[0]); 131 | exit(1); 132 | } 133 | 134 | nvme_setup(argv[1], 8); 135 | vfio_dma_t* dma = vfio_dma_alloc(vfiodev, 8192); 136 | if (!dma) errx(1, "vfio_dma_alloc"); 137 | 138 | int numd = dma->size / sizeof(u32) - 1; 139 | u64 prp1 = dma->addr; 140 | u64 prp2 = dma->addr + 4096; 141 | int err = nvme_acmd_get_log_page(nvmedev, nsid, lid, numd, prp1, prp2); 142 | if (err) errx(1, "nvme_acmd_get_log_page"); 143 | 144 | switch (lid) { 145 | case 1: print_error_info(dma->buf); break; 146 | case 2: print_smart_health(dma->buf); break; 147 | case 3: print_firmware_slot(dma->buf); break; 148 | } 149 | 150 | nvme_cleanup(); 151 | return 0; 152 | } 153 | 154 | -------------------------------------------------------------------------------- /test/unvme/unvme_get_features.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-2016, Micron Technology, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * 3. Neither the name of the copyright holder nor the names of its 16 | * contributors may be used to endorse or promote products derived 17 | * from this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | /** 33 | * @file 34 | * @brief An example using unvme_cmd (generic command) to get features. 35 | */ 36 | 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | #include "unvme.h" 43 | #include "unvme_nvme.h" // for printing get log page structures 44 | 45 | static char* features[] = { 46 | "", 47 | "1) Arbitration:", 48 | "2) Power Management:", 49 | "3) LBA Range Type:", 50 | "4) Temperature Threshold:", 51 | "5) Error Recovery:", 52 | "6) Volatile Write Cache:", 53 | "7) Number of Queues:", 54 | "8) Interrupt Coalescing:", 55 | "9) Interrupt Vector Config:", 56 | "10) Write Atomicity:", 57 | "11) Async Event Config:" 58 | }; 59 | 60 | /** 61 | * Main program. 62 | */ 63 | int main(int argc, char* argv[]) 64 | { 65 | const char* usage = "Usage: %s PCINAME [NSID]"; 66 | 67 | if (argc < 2) { 68 | warnx(usage, argv[0]); 69 | exit(1); 70 | } 71 | 72 | int nsid = 1; 73 | if (argc > 2) { 74 | char* s = argv[2]; 75 | nsid = strtol(s, &s, 0); 76 | if (*s) { 77 | warnx(usage, argv[0]); 78 | exit(1); 79 | } 80 | } 81 | 82 | const unvme_ns_t* ns = unvme_open(argv[1]); 83 | if (!ns) errx(1, "unvme_open"); 84 | u64 bufsz = sizeof(nvme_feature_lba_data_t); 85 | void* buf = unvme_alloc(ns, bufsz); 86 | if (!buf) errx(1, "unvme_alloc"); 87 | 88 | u32 cdw10_15[6] = { 0 }; 89 | u32 res; 90 | int fid; 91 | for (fid = NVME_FEATURE_ARBITRATION; fid <= NVME_FEATURE_ASYNC_EVENT; fid++) { 92 | cdw10_15[0] = fid; 93 | int err = unvme_cmd(ns, -1, NVME_ACMD_GET_FEATURES, nsid, buf, bufsz, cdw10_15, &res); 94 | 95 | if (err) { 96 | printf("%-30s \n", features[fid]); 97 | } else if (fid == NVME_FEATURE_ARBITRATION) { 98 | nvme_feature_arbitration_t* arb = (nvme_feature_arbitration_t*)&res; 99 | printf("%-30s hpw=%u mpw=%u lpw=%u ab=%u\n", features[fid], 100 | arb->hpw, arb->mpw, arb->lpw, arb->ab); 101 | } else if (fid == NVME_FEATURE_POWER_MGMT) { 102 | nvme_feature_power_mgmt_t* pm = (nvme_feature_power_mgmt_t*)&res; 103 | printf("%-30s ps=%u\n", features[fid], pm->ps); 104 | } else if (fid == NVME_FEATURE_LBA_RANGE) { 105 | nvme_feature_lba_range_t* lbart = (nvme_feature_lba_range_t*)&res; 106 | printf("%-30s num=%u\n", features[fid], lbart->num); 107 | } else if (fid == NVME_FEATURE_TEMP_THRESHOLD) { 108 | nvme_feature_temp_threshold_t* tt = (nvme_feature_temp_threshold_t*)&res; 109 | printf("%-30s tmpth=%u\n", features[fid], tt->tmpth); 110 | } else if (fid == NVME_FEATURE_ERROR_RECOVERY) { 111 | nvme_feature_error_recovery_t* er = (nvme_feature_error_recovery_t*)&res; 112 | printf("%-30s tler=%u\n", features[fid], er->tler); 113 | } else if (fid == NVME_FEATURE_WRITE_CACHE) { 114 | nvme_feature_write_cache_t* wc = (nvme_feature_write_cache_t*)&res; 115 | printf("%-30s wce=%u\n", features[fid], wc->wce); 116 | } else if (fid == NVME_FEATURE_NUM_QUEUES) { 117 | nvme_feature_num_queues_t* nq = (nvme_feature_num_queues_t*)&res; 118 | printf("%-30s nsq=%u ncq=%u\n", features[fid], nq->nsq, nq->ncq); 119 | } else if (fid == NVME_FEATURE_INT_COALESCING) { 120 | nvme_feature_int_coalescing_t* intc = (nvme_feature_int_coalescing_t*)&res; 121 | printf("%-30s time=%u thr=%u\n", features[fid], intc->time, intc->thr); 122 | } else if (fid == NVME_FEATURE_INT_VECTOR) { 123 | nvme_feature_int_vector_t* intv = (nvme_feature_int_vector_t*)&res; 124 | printf("%-30s iv=%u cd=%u\n", features[fid], intv->iv, intv->cd); 125 | } else if (fid == NVME_FEATURE_WRITE_ATOMICITY) { 126 | nvme_feature_write_atomicity_t* wa = (nvme_feature_write_atomicity_t*)&res; 127 | printf("%-30s dn=%u\n", features[fid], wa->dn); 128 | } else if (fid == NVME_FEATURE_ASYNC_EVENT) { 129 | nvme_feature_async_event_t* aec = (nvme_feature_async_event_t*)&res; 130 | printf("%-30s smart=%u\n", features[fid], aec->smart); 131 | } 132 | } 133 | 134 | unvme_free(ns, buf); 135 | unvme_close(ns); 136 | return 0; 137 | } 138 | 139 | -------------------------------------------------------------------------------- /test/unvme-setup: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Script to setup UNVMe driver. 3 | 4 | PROG=$(basename "$0") 5 | PDIR=$(dirname $(readlink -f $0)) 6 | 7 | USAGE="Usage: 8 | ${PROG} list 9 | ------------------------------------- 10 | List all NVMe devices binding status. 11 | ------------------------------------- 12 | 13 | ${PROG} bind [DEVICE]... 14 | -------------------------------------------------------- 15 | Bind device(s) to VFIO and enable for UNVMe. 16 | Default to all NVMe devices if no device is specified. 17 | Device name format is %x:%x.%x (as shown in lspci). 18 | -------------------------------------------------------- 19 | 20 | ${PROG} reset [DEVICE]... 21 | --------------------------------------------------------------- 22 | Reset device(s) to kernel space driver. 23 | Default to all NVMe devices if no device is specified. 24 | Device name format is %x:%x.%x (as shown in lspci). 25 | --------------------------------------------------------------- 26 | " 27 | 28 | # 29 | # Function: print usage and exit. 30 | # 31 | myusage() { 32 | echo -e "${USAGE}" 33 | exit 1 34 | } 35 | 36 | # 37 | # Function: print error and exit. 38 | # 39 | errx() { 40 | echo "ERROR: $*" 41 | kill -s TERM ${EXIT_PID} 42 | exit 1 43 | } 44 | 45 | # 46 | # Function: check for VFIO support. 47 | # 48 | vfio_check() { 49 | modprobe vfio-pci 50 | [ $? -ne 0 ] && exit 1 51 | } 52 | 53 | # 54 | # Function: list each NVMe device with its driver binding status. 55 | # 56 | unvme_list() { 57 | for d in $(lspci -n | grep '0108: ' | cut -d' ' -f1); do 58 | m=$(find /sys/bus/pci/drivers -name 0000:$d -printf %h) 59 | 60 | case $m in 61 | */nvme) 62 | m="/dev/$(ls /sys/bus/pci/devices/0000:$d/nvme)" 63 | ;; 64 | 65 | */vfio-pci) 66 | if [ -n "$(pgrep -f "unvmed .*$d")" ]; then 67 | m='UNVMe loaded' 68 | else 69 | m='UNVMe enabled' 70 | fi 71 | ;; 72 | 73 | esac 74 | 75 | echo "$d $(lspci -vs $d | sed '/Subsystem:/!d;s/.*: //') - ($m)" 76 | done 77 | } 78 | 79 | # 80 | # Function: kill a UNVMe driver. 81 | # 82 | unvme_kill() { 83 | k=$(echo $1 | sed 's/^0//') 84 | pkill -9 -f "unvmed .*$k" 85 | 86 | n=0 87 | while true; do 88 | [ -z "$(pgrep -f "unvmed .*$k")" ] && break 89 | sleep 0.1 90 | n=$((n+1)) 91 | [ $n -gt 5 ] && errx "can't kill unvmed $1" 92 | done 93 | rm -f /dev/shm/unvme$(echo $1 | tr -d :.)* 94 | [ $? -ne 0 ] && exit 1 95 | } 96 | 97 | # 98 | # Function: unbind a device from any driver. 99 | # 100 | unvme_unbind() { 101 | unvme_kill $1 102 | f=0000:$1 103 | 104 | if [ -e /sys/bus/pci/devices/$f/driver/unbind ]; then 105 | echo $f > /sys/bus/pci/devices/$f/driver/unbind 106 | [ $? -ne 0 ] && exit 1 107 | fi 108 | 109 | n=0 110 | while true; do 111 | sleep 0.1 112 | [ ! -e /sys/bus/pci/devices/$f/driver ] && break 113 | n=$((n+1)) 114 | if [ $n -gt 20 ]; then 115 | d=$(basename $(readlink -f /sys/bus/pci/devices/$f/driver)) 116 | errx "can't unbind $1 from $d" 117 | fi 118 | done 119 | } 120 | 121 | # 122 | # Function: bind a device to VFIO driver. 123 | # 124 | unvme_bind() { 125 | unvme_kill $1 126 | f=0000:$1 127 | 128 | if [ ! -e /sys/bus/pci/drivers/vfio-pci/$f ]; then 129 | unvme_unbind $1 130 | 131 | if [ -e /sys/bus/pci/drivers/vfio-pci/bind ]; then 132 | echo $(lspci -ns $1 | cut -d' ' -f3 | tr : ' ') > /sys/bus/pci/drivers/vfio-pci/new_id 133 | fi 134 | 135 | n=0 136 | while true; do 137 | sleep 0.1 138 | [ -e /sys/bus/pci/drivers/vfio-pci/$f ] && break 139 | n=$((n+1)) 140 | [ $n -gt 20 ] && errx "can't bind $1 to vfio-pci" 141 | done 142 | fi 143 | } 144 | 145 | # 146 | # Function: bind device to VFIO and load the UNVMe daemon. 147 | # 148 | unvme_load() { 149 | # check to load driver if not already 150 | pgrep -f "unvmed .*$1" 151 | if [ $? -ne 0 ]; then 152 | unvme_bind $1 153 | 154 | tmp=$(mktemp) 155 | if [[ -f ${PDIR}/../src/unvmed && -x ${PDIR}/../src/unvmed ]]; then 156 | ${PDIR}/../src/unvmed $1 &> ${tmp} 157 | elif [[ -f ${PDIR}/unvmed && -x ${PDIR}/unvmed ]]; then 158 | ${PDIR}/unvmed $1 &> ${tmp} 159 | else 160 | echo 'ERROR: unvmed not found' 161 | exit 1 162 | fi 163 | 164 | # wait for status 165 | while [ ! -s ${tmp} ]; do sleep 0.1; done 166 | 167 | if [ -z "$(egrep $1.*loaded ${tmp})" ]; then 168 | cat ${tmp} 169 | rm ${tmp} 170 | exit 1 171 | fi 172 | rm ${tmp} 173 | fi 174 | } 175 | 176 | # 177 | # Main starts here. 178 | # 179 | [ $# -eq 0 ] && myusage 180 | 181 | trap 'exit 1' TERM 182 | export EXIT_PID=$$ 183 | 184 | [ ${EUID} -ne 0 ] && errx "${PROG} must be run as root" 185 | 186 | cmd=$1 187 | shift 188 | 189 | if [ $# -eq 0 ]; then 190 | NVMELIST=$(lspci -n | grep '0108: ' | cut -d' ' -f1) 191 | [ -z "${NVMELIST}" ] && errx "No NVMe device found in system" 192 | else 193 | for d in $*; do 194 | if [[ $d == *:* ]]; then 195 | pci=$(lspci -ns $d | grep '0108: ' | cut -d' ' -f1) 196 | [ -z "${pci}" ] && errx "device $d is not NVMe" 197 | NVMELIST="${NVMELIST} ${pci}" 198 | else 199 | OPTIONS="${OPTIONS} $d" 200 | fi 201 | done 202 | fi 203 | 204 | vfio_check 205 | 206 | case ${cmd} in 207 | list) 208 | ;; 209 | 210 | bind) 211 | for m in ${NVMELIST}; do 212 | unvme_bind $m 213 | done 214 | ;; 215 | 216 | reset) 217 | for m in ${NVMELIST}; do 218 | unvme_unbind $m 219 | done 220 | modprobe -r nvme 221 | modprobe nvme 222 | sleep 3 # wait for NVMe driver to initialize 223 | ;; 224 | 225 | *) 226 | myusage 227 | ;; 228 | 229 | esac 230 | 231 | unvme_list 232 | 233 | -------------------------------------------------------------------------------- /test/unvme/unvme_get_log_page.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-2016, Micron Technology, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * 3. Neither the name of the copyright holder nor the names of its 16 | * contributors may be used to endorse or promote products derived 17 | * from this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | /** 33 | * @file 34 | * @brief An example using unvme_cmd (generic command) to get log page. 35 | */ 36 | 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | #include "unvme.h" 43 | #include "unvme_nvme.h" // for printing get log page structures 44 | 45 | /** 46 | * Print log page error info. 47 | */ 48 | void print_error_info(void* buf) 49 | { 50 | nvme_log_page_error_t* err = buf; 51 | 52 | printf("Log Page Error Information\n"); 53 | printf("==========================\n"); 54 | printf("Error Count : %ld\n", err->count); 55 | printf("Submission Queue ID : %d\n", err->sqid); 56 | printf("Command ID : %d\n", err->cid); 57 | printf("Status Field : %#x\n", err->sf); 58 | printf("Parameter Error Location : byte=%d bit=%d\n", err->byte, err->bit); 59 | printf("LBA : %#lx\n", err->lba); 60 | printf("Namespace : %d\n", err->ns); 61 | printf("Vendor Specific : %#x\n", err->vspec); 62 | } 63 | 64 | /** 65 | * Print 128-bit value as hex into designated string dest. 66 | */ 67 | char* hex128(char* dest, u64 val[2]) 68 | { 69 | if (val[0]) sprintf(dest, "%#lx%08lx", val[0], val[1]); 70 | else sprintf(dest, "%ld", val[1]); 71 | return dest; 72 | } 73 | 74 | /** 75 | * Print log page smart health info. 76 | */ 77 | void print_smart_health(void* buf) 78 | { 79 | char s[64]; 80 | nvme_log_page_health_t* smh = buf; 81 | 82 | printf("Log Page SMART / Health Information\n"); 83 | printf("===================================\n"); 84 | printf("Critical Warning : %#x\n", smh->warn); 85 | printf("Temperature (Kelvin) : %d\n", smh->temp); 86 | printf("Available Spare (%%) : %d\n", smh->avspare); 87 | printf("Available Spare Threshold (%%) : %d\n", smh->avsparethresh); 88 | printf("Percentage Used : %d\n", smh->used); 89 | printf("# of Data Units Read : %s\n", hex128(s, smh->dur)); 90 | printf("# of Data Units Written : %s\n", hex128(s, smh->duw)); 91 | printf("# of Host Read Commands : %s\n", hex128(s, smh->hrc)); 92 | printf("# of Host Write Commands : %s\n", hex128(s, smh->hwc)); 93 | printf("Controller Busy Time (mins) : %s\n", hex128(s, smh->cbt)); 94 | printf("# of Power Cycles : %s\n", hex128(s, smh->pcycles)); 95 | printf("# of Power On Hours : %s\n", hex128(s, smh->phours)); 96 | printf("# of Unsafe Shutdowns : %s\n", hex128(s, smh->unsafeshut)); 97 | printf("# of Media Errors : %s\n", hex128(s, smh->merrors)); 98 | printf("# of Error Log Entries : %s\n", hex128(s, smh->errlogs)); 99 | } 100 | 101 | /** 102 | * Print log page firmware slot info. 103 | */ 104 | void print_firmware_slot(void* buf) 105 | { 106 | nvme_log_page_fw_t* fw = buf; 107 | 108 | printf("Log Page Firmware Slot Information\n"); 109 | printf("==================================\n"); 110 | int i; 111 | for (i = 0; i < 7; i++) { 112 | printf("Firmware Revision SLot %d : %.8s %s\n", 113 | i + 1, (char*)&fw->fr[i], fw->afi == (i + 1) ? "(active)" : ""); 114 | } 115 | } 116 | 117 | /** 118 | * Main program. 119 | */ 120 | int main(int argc, char* argv[]) 121 | { 122 | const char* usage = "Usage: %s PCINAME NSID LOG_PAGE_ID\n\ 123 | (LOG_PAGE_ID 1 = error information)\n\ 124 | (LOG_PAGE_ID 2 = SMART / Health information)\n\ 125 | (LOG_PAGE_ID 3 = firmware slot information)"; 126 | 127 | if (argc != 4) { 128 | warnx(usage, argv[0]); 129 | exit(1); 130 | } 131 | 132 | char* s = argv[2]; 133 | int nsid = strtol(s, &s, 0); 134 | int lid = strtol(argv[3], &s, 0); 135 | if (*s || (lid < 1 || lid > 3)) { 136 | warnx(usage, argv[0]); 137 | exit(1); 138 | } 139 | 140 | const unvme_ns_t* ns = unvme_open(argv[1]); 141 | if (!ns) errx(1, "unvme_open"); 142 | u64 bufsz = 8192; 143 | void* buf = unvme_alloc(ns, bufsz); 144 | if (!buf) errx(1, "unvme_alloc"); 145 | 146 | u32 cdw10_15[6]; 147 | u32 numd = bufsz / sizeof(u32) - 1; 148 | cdw10_15[0] = (numd << 16) + lid; 149 | 150 | if (unvme_cmd(ns, -1, NVME_ACMD_GET_LOG_PAGE, nsid, buf, bufsz, cdw10_15, 0)) 151 | errx(1, "unvme_cmd failed"); 152 | 153 | switch (lid) { 154 | case 1: print_error_info(buf); break; 155 | case 2: print_smart_health(buf); break; 156 | case 3: print_firmware_slot(buf); break; 157 | } 158 | 159 | unvme_free(ns, buf); 160 | unvme_close(ns); 161 | return 0; 162 | } 163 | 164 | -------------------------------------------------------------------------------- /uio_memmap/uio_memmap.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | 15 | #define DYNAMIC_MAP_SIZE 0x40000000 16 | 17 | 18 | struct uio_memmap_platdata { 19 | struct uio_info *uioinfo; 20 | struct platform_device *pdev; 21 | bool open; 22 | }; 23 | 24 | 25 | static int uio_memmap_open(struct uio_info *info, struct inode *inode) 26 | { 27 | int ret = 0; 28 | struct uio_memmap_platdata *priv = info->priv; 29 | struct platform_device *pdev = priv->pdev; 30 | void *mem = NULL; 31 | dma_addr_t mem_phys = 0x40000000; 32 | 33 | if (priv->open) { 34 | dev_err(&pdev->dev, "Device is already open\n"); 35 | return -EBUSY; 36 | } 37 | 38 | mem = dma_zalloc_coherent(&pdev->dev, DYNAMIC_MAP_SIZE, &mem_phys, GFP_KERNEL); 39 | if (!mem) { 40 | dev_err(&pdev->dev, "Failed to allocate mem memory\n"); 41 | return -ENOMEM; 42 | } 43 | dev_info(&pdev->dev, "uio_memmap_open - Allocated DMA mem:\n VIRT: 0x%016llx\n PHYS: 0x%016llx\n", (u64)mem, mem_phys); 44 | 45 | info->mem[0].addr = mem_phys; 46 | info->mem[0].internal_addr = mem; 47 | 48 | priv->open = true; 49 | 50 | return ret; 51 | } 52 | 53 | static int uio_memmap_release(struct uio_info *info, struct inode *inode) 54 | { 55 | struct uio_memmap_platdata *priv = info->priv; 56 | struct platform_device *pdev = priv->pdev; 57 | 58 | dma_free_coherent(&pdev->dev, DYNAMIC_MAP_SIZE, info->mem[0].internal_addr, info->mem[0].addr); 59 | 60 | info->mem[0].addr = 0; 61 | info->mem[0].internal_addr = 0; 62 | 63 | priv->open = false; 64 | 65 | dev_info(&pdev->dev, "uio_memmap_release - Freed DMA mem\n"); 66 | 67 | return 0; 68 | } 69 | 70 | static int uio_memmap_probe(struct platform_device *pdev) 71 | { 72 | struct uio_info *uioinfo = dev_get_platdata(&pdev->dev); 73 | struct uio_memmap_platdata *priv; 74 | int ret = -EINVAL; 75 | // void *mem = NULL; 76 | // dma_addr_t mem_phys; 77 | int err; 78 | 79 | dev_info(&pdev->dev, "probe start\n"); 80 | 81 | if (!pdev->dev.of_node) { 82 | dev_err(&pdev->dev, "No device tree node\n"); 83 | ret = -ENODEV; 84 | goto error0; 85 | } 86 | 87 | uioinfo = devm_kzalloc(&pdev->dev, sizeof(*uioinfo), GFP_KERNEL); 88 | if (!uioinfo) { 89 | dev_err(&pdev->dev, "Failed to allocate uio_info memory\n"); 90 | ret = -ENOMEM; 91 | goto error0; 92 | } 93 | 94 | priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 95 | if (!priv) { 96 | dev_err(&pdev->dev, "unable to kmalloc\n"); 97 | ret = -ENOMEM; 98 | goto error0; 99 | } 100 | 101 | priv->uioinfo = uioinfo; 102 | priv->pdev = pdev; 103 | priv->open = false; 104 | uioinfo->priv = priv; 105 | 106 | /* Initialize reserved memory resources */ 107 | err = of_reserved_mem_device_init(&pdev->dev); 108 | if (err) { 109 | dev_err(&pdev->dev, "Could not get reserved memory: %d\n", err); 110 | ret = -ENOMEM; 111 | goto error0; 112 | } 113 | 114 | /* Allocate memory */ 115 | err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); 116 | if (err) { 117 | dev_err(&pdev->dev, "Could not set coherent mask: %d\n", err); 118 | ret = -ENOMEM; 119 | goto error1; 120 | } 121 | 122 | // mem = dma_alloc_coherent(&pdev->dev, DYNAMIC_MAP_SIZE, &mem_phys, GFP_KERNEL); 123 | // if (!mem) { 124 | // dev_err(&pdev->dev, "Failed to allocate mem memory\n"); 125 | // ret = -ENOMEM; 126 | // goto error1; 127 | // } 128 | // dev_info(&pdev->dev, "Allocated mem:\nVIRT: 0x%016llx\nPHYS: 0x%016llx\n", (u64)mem, mem_phys); 129 | 130 | uioinfo->name = "memmap"; 131 | uioinfo->version = "0.1"; 132 | uioinfo->irq = UIO_IRQ_NONE; 133 | uioinfo->open = uio_memmap_open; 134 | uioinfo->release = uio_memmap_release; 135 | 136 | // uioinfo->mem[0].name = "mem0"; 137 | // uioinfo->mem[0].addr = mem_phys; 138 | // uioinfo->mem[0].internal_addr = mem; 139 | // uioinfo->mem[0].size = DYNAMIC_MAP_SIZE; 140 | // uioinfo->mem[0].memtype = UIO_MEM_PHYS; 141 | 142 | uioinfo->mem[0].name = "mem0"; 143 | uioinfo->mem[0].addr = 0; 144 | uioinfo->mem[0].internal_addr = 0; 145 | uioinfo->mem[0].size = DYNAMIC_MAP_SIZE; 146 | uioinfo->mem[0].memtype = UIO_MEM_PHYS; 147 | 148 | // pm_runtime_enable(&pdev->dev); 149 | 150 | ret = uio_register_device(&pdev->dev, priv->uioinfo); 151 | if (ret) { 152 | dev_err(&pdev->dev, "Failed to register UIO device\n"); 153 | // pm_runtime_disable(&pdev->dev); 154 | goto error2; 155 | } 156 | 157 | platform_set_drvdata(pdev, priv); 158 | 159 | dev_info(&pdev->dev, "probe successful\n"); 160 | 161 | return 0; 162 | 163 | error2: 164 | // dma_free_coherent(&pdev->dev, DYNAMIC_MAP_SIZE, mem, mem_phys); 165 | error1: 166 | of_reserved_mem_device_release(&pdev->dev); 167 | error0: 168 | return ret; 169 | } 170 | 171 | static int uio_memmap_remove(struct platform_device *pdev) 172 | { 173 | struct uio_memmap_platdata *priv = platform_get_drvdata(pdev); 174 | 175 | uio_unregister_device(priv->uioinfo); 176 | // pm_runtime_disable(&pdev->dev); 177 | // dma_free_coherent(&pdev->dev, DYNAMIC_MAP_SIZE, priv->uioinfo->mem[0].internal_addr, priv->uioinfo->mem[0].addr); 178 | of_reserved_mem_device_release(&pdev->dev); 179 | 180 | dev_info(&pdev->dev, "removed\n"); 181 | return 0; 182 | } 183 | 184 | 185 | //static int uio_memmap_runtime_nop(struct device *dev) 186 | //{ 187 | // return 0; 188 | //} 189 | 190 | //static const struct dev_pm_ops uio_memmap_dev_pm_ops = { 191 | // .runtime_suspend = uio_memmap_runtime_nop, 192 | // .runtime_resume = uio_memmap_runtime_nop, 193 | //}; 194 | 195 | static const struct of_device_id uio_memmap_of_match[] = { 196 | { .compatible = "rj,uio-memmap", }, 197 | // { .compatible = "rj,uio-memtest", }, 198 | { /* end of list */ }, 199 | }; 200 | MODULE_DEVICE_TABLE(of, uio_memmap_of_match); 201 | 202 | static struct platform_driver uio_memmap_driver = { 203 | .probe = uio_memmap_probe, 204 | .remove = uio_memmap_remove, 205 | .driver = { 206 | .name = "uio-memmap", 207 | .of_match_table = uio_memmap_of_match, 208 | // .pm = &uio_memmap_dev_pm_ops, 209 | }, 210 | }; 211 | module_platform_driver(uio_memmap_driver); 212 | 213 | 214 | MODULE_LICENSE("GPL"); 215 | MODULE_AUTHOR("Russell Joyce"); 216 | MODULE_DESCRIPTION("UIO-based Memory Test"); 217 | MODULE_VERSION("0.1"); 218 | -------------------------------------------------------------------------------- /test/unvme/unvme_api_test.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-2016, Micron Technology, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * 3. Neither the name of the copyright holder nor the names of its 16 | * contributors may be used to endorse or promote products derived 17 | * from this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | /** 33 | * @file 34 | * @brief UNVMe API test. 35 | */ 36 | 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | 45 | #include "unvme.h" 46 | 47 | /// Print if verbose flag is set 48 | #define VERBOSE(fmt, arg...) if (verbose) printf(fmt, ##arg) 49 | 50 | 51 | /** 52 | * Main. 53 | */ 54 | int main(int argc, char** argv) 55 | { 56 | const char* usage = "Usage: %s [OPTION]... PCINAME\n\ 57 | -v verbose\n\ 58 | -r RATIO max blocks per I/O ratio (default 4)\n\ 59 | PCINAME PCI device name (as 01:00.0[/1] format)"; 60 | 61 | int opt, ratio=4, verbose=0; 62 | const char* prog = strrchr(argv[0], '/'); 63 | prog = prog ? prog + 1 : argv[0]; 64 | 65 | while ((opt = getopt(argc, argv, "r:v")) != -1) { 66 | switch (opt) { 67 | case 'r': 68 | ratio = strtol(optarg, 0, 0); 69 | if (ratio <= 0) errx(1, "r must be > 0"); 70 | break; 71 | case 'v': 72 | verbose = 1; 73 | break; 74 | default: 75 | warnx(usage, prog); 76 | exit(1); 77 | } 78 | } 79 | if ((optind + 1) != argc) { 80 | warnx(usage, prog); 81 | exit(1); 82 | } 83 | char* pciname = argv[optind]; 84 | 85 | printf("API TEST BEGIN\n"); 86 | const unvme_ns_t* ns = unvme_open(pciname); 87 | if (!ns) exit(1); 88 | 89 | // set large number of I/O and size 90 | int maxnlb = ratio * ns->maxbpio; 91 | int iocount = ratio * (ns->qsize - 1); 92 | 93 | printf("%s qc=%d/%d qs=%d/%d bc=%#lx bs=%d maxnlb=%d/%d\n", 94 | ns->device, ns->qcount, ns->maxqcount, ns->qsize, ns->maxqsize, 95 | ns->blockcount, ns->blocksize, maxnlb, ns->maxbpio); 96 | 97 | int q, i, nlb; 98 | u64 slba, size, w, *p; 99 | unvme_iod_t* iod = malloc(iocount * sizeof(unvme_iod_t)); 100 | void** buf = malloc(iocount * sizeof(void*)); 101 | 102 | time_t tstart = time(0); 103 | for (q = 0; q < ns->qcount; q++) { 104 | printf("> Test q=%d ioc=%d\n", q, iocount); 105 | int t = time(0); 106 | 107 | printf("Test alloc\n"); 108 | srandom(t); 109 | for (i = 0; i < iocount; i++) { 110 | nlb = random() % maxnlb + 1; 111 | size = nlb * ns->blocksize; 112 | VERBOSE(" alloc.%-2d %#8x %#lx\n", i, nlb, size); 113 | if (!(buf[i] = unvme_alloc(ns, size))) 114 | errx(1, "alloc.%d failed", i); 115 | } 116 | 117 | printf("Test awrite\n"); 118 | srandom(t); 119 | slba = 0; 120 | for (i = 0; i < iocount; i++) { 121 | nlb = random() % maxnlb + 1; 122 | size = nlb * ns->blocksize / sizeof(u64); 123 | p = buf[i]; 124 | for (w = 0; w < size; w++) p[w] = (w << 32) + i; 125 | VERBOSE(" awrite.%-2d %#8x %p %#lx\n", i, nlb, p, slba); 126 | if (!(iod[i] = unvme_awrite(ns, q, p, slba, nlb))) 127 | errx(1, "awrite.%d failed", i); 128 | slba += nlb; 129 | } 130 | 131 | printf("Test apoll.awrite\n"); 132 | for (i = iocount-1; i >= 0; i--) { 133 | VERBOSE(" apoll.awrite.%-2d\n", i); 134 | if (unvme_apoll(iod[i], UNVME_TIMEOUT)) 135 | errx(1, "apoll_awrite.%d failed", i); 136 | } 137 | 138 | printf("Test aread\n"); 139 | srandom(t); 140 | slba = 0; 141 | for (i = 0; i < iocount; i++) { 142 | nlb = random() % maxnlb + 1; 143 | size = nlb * ns->blocksize; 144 | p = buf[i]; 145 | for (int i = 0; i < size/sizeof(u64); i++) p[i] = 0; 146 | VERBOSE(" aread.%-2d %#8x %p %#lx\n", i, nlb, p, slba); 147 | if (!(iod[i] = unvme_aread(ns, q, p, slba, nlb))) 148 | errx(1, "aread.%d failed", i); 149 | slba += nlb; 150 | } 151 | 152 | printf("Test apoll.aread\n"); 153 | for (i = iocount-1; i >= 0; i--) { 154 | VERBOSE(" apoll.aread.%-2d\n", i); 155 | if (unvme_apoll(iod[i], UNVME_TIMEOUT)) 156 | errx(1, "apoll_aread.%d failed", i); 157 | } 158 | 159 | printf("Test verify\n"); 160 | srandom(t); 161 | slba = 0; 162 | for (i = 0; i < iocount; i++) { 163 | nlb = random() % maxnlb + 1; 164 | size = nlb * ns->blocksize / sizeof(u64); 165 | p = buf[i]; 166 | VERBOSE(" verify.%-2d %#8x %p %#lx\n", i, nlb, p, slba); 167 | for (w = 0; w < size; w++) { 168 | if (p[w] != ((w << 32) + i)) { 169 | w *= sizeof(w); 170 | slba += w / ns->blocksize; 171 | w %= ns->blocksize; 172 | errx(1, "miscompare at lba %#lx offset %#lx", slba, w); 173 | } 174 | } 175 | slba += nlb; 176 | } 177 | 178 | printf("Test free\n"); 179 | for (i = 0; i < iocount; i++) { 180 | VERBOSE(" free.%-2d\n", i); 181 | if (unvme_free(ns, buf[i])) 182 | errx(1, "free.%d failed", i); 183 | } 184 | } 185 | 186 | free(buf); 187 | free(iod); 188 | unvme_close(ns); 189 | 190 | printf("API TEST COMPLETE (%ld secs)\n", time(0) - tstart); 191 | return 0; 192 | } 193 | -------------------------------------------------------------------------------- /src/unvme_core.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-2016, Micron Technology, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * 3. Neither the name of the copyright holder nor the names of its 16 | * contributors may be used to endorse or promote products derived 17 | * from this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | /** 33 | * @file 34 | * @brief uNVMe core header file. 35 | */ 36 | 37 | #ifndef _UNVME_CORE_H 38 | #define _UNVME_CORE_H 39 | 40 | #include 41 | 42 | #include "unvme_log.h" 43 | #include "unvme_vfio.h" 44 | #include "unvme_nvme.h" 45 | #include "unvme_lock.h" 46 | #include "unvme.h" 47 | 48 | /// Doubly linked list add node 49 | #define LIST_ADD(head, node) \ 50 | if ((head) != NULL) { \ 51 | (node)->next = (head); \ 52 | (node)->prev = (head)->prev; \ 53 | (head)->prev->next = (node); \ 54 | (head)->prev = (node); \ 55 | } else { \ 56 | (node)->next = (node)->prev = (node); \ 57 | (head) = (node); \ 58 | } 59 | 60 | /// Doubly linked list remove node 61 | #define LIST_DEL(head, node) \ 62 | if ((node->next) != (node)) { \ 63 | (node)->next->prev = (node)->prev; \ 64 | (node)->prev->next = (node)->next; \ 65 | if ((head) == (node)) (head) = (node)->next; \ 66 | } else { \ 67 | (head) = NULL; \ 68 | } 69 | 70 | /// Log and print an unrecoverable error message and exit 71 | #define FATAL(fmt, arg...) do { ERROR(fmt, ##arg); abort(); } while (0) 72 | 73 | /// Page size 74 | typedef char unvme_page_t[4096]; 75 | 76 | /// IO memory allocation tracking info 77 | typedef struct _unvme_iomem { 78 | vfio_dma_t** map; ///< dynamic array of allocated memory 79 | int size; ///< array size 80 | int count; ///< array count 81 | unvme_lock_t lock; ///< map access lock 82 | } unvme_iomem_t; 83 | 84 | /// IO full descriptor 85 | typedef struct _unvme_desc { 86 | void* buf; ///< buffer 87 | u64 slba; ///< starting lba 88 | u32 nlb; ///< number of blocks 89 | u32 qid; ///< queue id 90 | u32 opc; ///< op code 91 | u32 id; ///< descriptor id 92 | void* sentinel; ///< sentinel check 93 | struct _unvme_queue* q; ///< queue context owner 94 | struct _unvme_desc* prev; ///< previous descriptor node 95 | struct _unvme_desc* next; ///< next descriptor node 96 | int error; ///< error status 97 | int cidcount; ///< number of pending cids 98 | u64 cidmask[]; ///< cid pending bit mask 99 | } unvme_desc_t; 100 | 101 | /// IO queue entry 102 | typedef struct _unvme_queue { 103 | nvme_queue_t* nvmeq; ///< NVMe associated queue 104 | vfio_dma_t* sqdma; ///< submission queue mem 105 | vfio_dma_t* cqdma; ///< completion queue mem 106 | vfio_dma_t* prplist; ///< PRP list 107 | u32 size; ///< queue depth 108 | u16 cid; ///< next cid to check and use 109 | int cidcount; ///< number of pending cids 110 | int desccount; ///< number of pending descriptors 111 | int masksize; ///< bit mask size to allocate 112 | u64* cidmask; ///< cid pending bit mask 113 | unvme_desc_t* desclist; ///< used descriptor list 114 | unvme_desc_t* descfree; ///< free descriptor list 115 | unvme_desc_t* descpend; ///< pending descriptor list 116 | } unvme_queue_t; 117 | 118 | /// Device context 119 | typedef struct _unvme_device { 120 | vfio_device_t vfiodev; ///< VFIO device 121 | nvme_device_t nvmedev; ///< NVMe device 122 | unvme_queue_t adminq; ///< adminq queue 123 | int refcount; ///< reference count 124 | unvme_iomem_t iomem; ///< IO memory tracker 125 | unvme_ns_t ns; ///< controller namespace (id=0) 126 | unvme_queue_t* ioqs; ///< pointer to IO queues 127 | } unvme_device_t; 128 | 129 | /// Session context 130 | typedef struct _unvme_session { 131 | struct _unvme_session* prev; ///< previous session node 132 | struct _unvme_session* next; ///< next session node 133 | unvme_device_t* dev; ///< device context 134 | unvme_ns_t ns; ///< namespace 135 | } unvme_session_t; 136 | 137 | unvme_ns_t* unvme_do_open(int pci, int nsid, int qcount, int qsize); 138 | int unvme_do_close(const unvme_ns_t* ns); 139 | void* unvme_do_alloc(const unvme_ns_t* ns, u64 size); 140 | int unvme_do_free(const unvme_ns_t* ses, void* buf); 141 | int unvme_do_poll(unvme_desc_t* desc, int sec, u32* cqe_cs); 142 | unvme_desc_t* unvme_do_cmd(const unvme_ns_t* ns, int qid, int opc, int nsid, void* buf, u64 bufsz, u32 cdw10_15[6]); 143 | unvme_desc_t* unvme_do_rw(const unvme_ns_t* ns, int qid, int opc, void* buf, u64 slba, u32 nlb); 144 | 145 | #endif // _UNVME_CORE_H 146 | 147 | -------------------------------------------------------------------------------- /test/python/unvme.py: -------------------------------------------------------------------------------- 1 | # UNVMe Python wrapper module 2 | 3 | import os, sys, ctypes 4 | from ctypes import * 5 | 6 | # Namespace attributes structure 7 | class unvme_ns_t(ctypes.Structure): 8 | _fields_ = [ 9 | ("pci", c_uint32), # PCI device id 10 | ("id", c_uint16), # namespace id 11 | ("vid", c_uint16), # vendor id 12 | ("device", c_char*16), # PCI device name (BB:DD.F/N) 13 | ("mn", c_char*40), # model number 14 | ("sn", c_char*20), # serial number 15 | ("fr", c_char*8), # firmware revision 16 | ("blockcount", c_uint64), # total number of available blocks 17 | ("pagecount", c_uint64), # total number of available pages 18 | ("blocksize", c_uint16), # logical block size 19 | ("pagesize", c_uint16), # page size 20 | ("blockshift", c_uint16), # block size shift value 21 | ("pageshift", c_uint16), # page size shift value 22 | ("bpshift", c_uint16), # block to page shift 23 | ("nbpp", c_uint16), # number of blocks per page 24 | ("maxbpio", c_uint16), # max number of blocks per I/O 25 | ("maxppio", c_uint16), # max number of pages per I/O 26 | ("maxiopq", c_uint16), # max number of I/O submissions per queue 27 | ("nscount", c_uint16), # number of namespaces available 28 | ("qcount", c_uint32), # number of I/O queues 29 | ("maxqcount", c_uint32), # max number of queues supported 30 | ("qsize", c_uint32), # I/O queue size 31 | ("maxqsize", c_uint32), # max queue size supported 32 | ("ses", c_void_p) # associated session 33 | ] 34 | 35 | # I/O descriptor structure 36 | class unvme_iod_t(ctypes.Structure): 37 | _fields_ = [ 38 | ("buf", c_void_p), # data buffer (submitted) 39 | ("slba", c_uint64), # starting lba (submitted) 40 | ("nlb", c_uint32), # number of blocks (submitted) 41 | ("qid", c_uint32), # queue id (submitted) 42 | ("opc", c_uint32), # op code 43 | ("id", c_uint32) # descriptor id 44 | ] 45 | 46 | # Load libunvme 47 | libso = os.path.dirname(os.path.realpath(sys.argv[0])) + "/../../src/libunvme.so" 48 | if not os.path.exists(libso): 49 | libso = "./libunvme.so" 50 | if not os.path.exists(libso): 51 | libso = "/usr/local/lib/libunvme.so" 52 | libunvme = cdll.LoadLibrary(libso) 53 | 54 | 55 | # UNVMe library functions 56 | def unvme_open(pci): 57 | fn = libunvme.unvme_open 58 | fn.restype = POINTER(unvme_ns_t) 59 | return fn(pci) 60 | 61 | def unvme_close(ns): 62 | fn = libunvme.unvme_close 63 | fn.argtypes = [POINTER(unvme_ns_t)] 64 | fn.restype = c_int 65 | return fn(ns) 66 | 67 | def unvme_alloc(ns, size): 68 | fn = libunvme.unvme_alloc 69 | fn.argtypes = [POINTER(unvme_ns_t), c_uint64] 70 | fn.restype = c_void_p 71 | return fn(ns, size) 72 | 73 | def unvme_free(ns, buf): 74 | fn = libunvme.unvme_free 75 | fn.argtypes = [POINTER(unvme_ns_t), c_void_p] 76 | fn.restype = c_int 77 | return fn(ns, buf) 78 | 79 | def unvme_write(ns, qid, buf, slba, nlb): 80 | fn = libunvme.unvme_write 81 | fn.argtypes = [POINTER(unvme_ns_t), c_int, c_void_p, c_uint64, c_uint32] 82 | fn.restype = c_int 83 | return fn(ns, qid, buf, slba, nlb) 84 | 85 | def unvme_read(ns, qid, buf, slba, nlb): 86 | fn = libunvme.unvme_read 87 | fn.argtypes = [POINTER(unvme_ns_t), c_int, c_void_p, c_uint64, c_uint32] 88 | fn.restype = c_int 89 | return fn(ns, qid, buf, slba, nlb) 90 | 91 | def unvme_cmd(ns, qid, opc, nsid, buf, bufsz, cdw10_15, cqe_cs): 92 | fn = libunvme.unvme_cmd 93 | fn.argtypes = [POINTER(unvme_ns_t), c_int, c_int, c_int, c_void_p, c_uint64, POINTER(c_uint32), POINTER(c_uint32)] 94 | fn.restype = c_int 95 | return fn(ns, qid, opc, nsid, buf, bufsz, cdw10_15, cqe_cs) 96 | 97 | def unvme_awrite(ns, qid, buf, slba, nlb): 98 | fn = libunvme.unvme_awrite 99 | fn.argtypes = [POINTER(unvme_ns_t), c_int, c_void_p, c_uint64, c_uint32] 100 | fn.restype = POINTER(unvme_iod_t) 101 | return fn(ns, qid, buf, slba, nlb) 102 | 103 | def unvme_aread(ns, qid, buf, slba, nlb): 104 | fn = libunvme.unvme_aread 105 | fn.argtypes = [POINTER(unvme_ns_t), c_int, c_void_p, c_uint64, c_uint32] 106 | fn.restype = POINTER(unvme_iod_t) 107 | return fn(ns, qid, buf, slba, nlb) 108 | 109 | def unvme_acmd(ns, qid, opc, nsid, buf, bufsz, cdw10_15): 110 | fn = libunvme.unvme_acmd 111 | fn.argtypes = [POINTER(unvme_ns_t), c_int, c_int, c_int, c_void_p, c_uint64, POINTER(c_uint32)] 112 | fn.restype = POINTER(unvme_iod_t) 113 | return fn(ns, qid, opc, nsid, buf, bufsz, cdw10_15) 114 | 115 | def unvme_apoll(iod, timeout): 116 | fn = libunvme.unvme_apoll 117 | fn.argtypes = [POINTER(unvme_iod_t), c_int] 118 | fn.restype = c_int 119 | return fn(iod, timeout) 120 | 121 | def unvme_apoll_cs(iod, timeout, cqe_cs): 122 | fn = libunvme.unvme_apoll_cs 123 | fn.argtypes = [POINTER(unvme_iod_t), c_int, POINTER(c_uint32)] 124 | fn.restype = c_int 125 | return fn(iod, timeout, cqe_cs) 126 | 127 | 128 | # UNVMe class 129 | class Unvme(object): 130 | def __init__(self, pci=None): 131 | self.ns = None 132 | self.open(pci) 133 | 134 | def __del__(self): 135 | self.close() 136 | 137 | def open(self, pci): 138 | if self.ns: 139 | raise Exception("already opened") 140 | self.ns = unvme_open(pci) 141 | if not self.ns: 142 | raise Exception("unvme_open error") 143 | 144 | def close(self): 145 | if self.ns: 146 | stat = unvme_close(self.ns) 147 | if stat: 148 | raise Exception("unvme_close error") 149 | self.ns = None 150 | 151 | def info(self): 152 | return self.ns.contents 153 | 154 | def alloc(self, size): 155 | return unvme_alloc(self.ns, size) 156 | 157 | def free(self, buf): 158 | return unvme_free(self.ns, buf) 159 | 160 | def write(self, qid, buf, slba, nlb): 161 | return unvme_write(self.ns, qid, buf, slba, nlb) 162 | 163 | def read(self, qid, buf, slba, nlb): 164 | return unvme_read(self.ns, qid, buf, slba, nlb) 165 | 166 | def cmd(self, qid, opc, nsid, buf, bufsz, cdw10_15, cqe_cs): 167 | return unvme_cmd(self.ns, qid, opc, nsid, buf, bufsz, cdw10_15, cqe_cs) 168 | 169 | def awrite(self, qid, buf, slba, nlb): 170 | return unvme_awrite(self.ns, qid, buf, slba, nlb) 171 | 172 | def aread(self, qid, buf, slba, nlb): 173 | return unvme_aread(self.ns, qid, buf, slba, nlb) 174 | 175 | def acmd(self, qid, opc, nsid, buf, bufsz, cdw10_15): 176 | return unvme_acmd(self.ns, qid, opc, nsid, buf, bufsz, cdw10_15) 177 | 178 | def apoll(self, iod, timeout): 179 | return unvme_apoll(iod, timeout) 180 | 181 | def apoll_cs(self, iod, timeout, cqe_cs): 182 | return unvme_apoll_cs(iod, timeout, cqe_cs) 183 | 184 | -------------------------------------------------------------------------------- /test/unvme/unvme_mcd_test.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-2016, Micron Technology, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * 3. Neither the name of the copyright holder nor the names of its 16 | * contributors may be used to endorse or promote products derived 17 | * from this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | /** 33 | * @file 34 | * @brief UNVMe multiple concurrent devices test. 35 | */ 36 | 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | 48 | #include "unvme.h" 49 | 50 | /// device thread session 51 | typedef struct { 52 | char pciname[16]; ///< PCI name 53 | int pci; ///< PCI device id 54 | int ins; ///< instance (start with 0) 55 | int inscount; ///< instance count 56 | pthread_t thread; ///< session thread 57 | } ses_t; 58 | 59 | // Global variables 60 | static sem_t sm_ready; ///< semaphore for ready 61 | static sem_t sm_start; ///< semaphore for start 62 | static int error; ///< error flag 63 | 64 | 65 | /** 66 | * Thread session per device test. 67 | */ 68 | void* test_session(void* arg) 69 | { 70 | ses_t* ses = arg; 71 | const unvme_ns_t* ns; 72 | 73 | printf("Test device %s started\n", ses->pciname); 74 | sem_post(&sm_ready); 75 | sem_wait(&sm_start); 76 | 77 | if (!(ns = unvme_open(ses->pciname))) exit(1); 78 | printf("%s qc=%d/%d qs=%d/%d bc=%#lx bs=%d mbio=%d\n", 79 | ns->device, ns->qcount, ns->maxqcount, ns->qsize, ns->maxqsize, 80 | ns->blockcount, ns->blocksize, ns->maxbpio); 81 | 82 | u64 datasize = 256*1024*1024; 83 | u64 nlb = datasize >> ns->blockshift; 84 | u64 slba = nlb * ns->id; 85 | u64* wbuf = unvme_alloc(ns, datasize); 86 | u64* rbuf = unvme_alloc(ns, datasize); 87 | if (!wbuf || !rbuf) errx(1, "unvme_alloc %#lx failed", datasize); 88 | 89 | u64 w, wsize = datasize / sizeof(u64); 90 | for (w = 0; w < wsize; w++) { 91 | u64 pat = ses->thread + w; 92 | wbuf[w] = (pat << 32) | (~pat & 0xffffffff); 93 | } 94 | 95 | // for multiple namespace instances, different queues must be used 96 | // so divide up the queues for those instances 97 | int qcount = ns->qcount / ses->inscount; 98 | int q = qcount * ses->ins; 99 | while (!error && qcount--) { 100 | u64 lba = slba + q; 101 | u64 nb = nlb - q; 102 | printf("Test %s q%d lba %#lx nlb %#lx\n", ses->pciname, q, lba, nb); 103 | if (unvme_write(ns, q, wbuf, lba, nb)) { 104 | printf("ERROR: unvme_write %s q%d lba %#lx nlb %#lx\n", 105 | ses->pciname, q, lba, nb); 106 | error = ses->pci; 107 | break; 108 | } 109 | memset(rbuf, 0, datasize); 110 | if (unvme_read(ns, q, rbuf, lba, nb)) { 111 | printf("ERROR: unvme_read %s q%d lba %#lx nlb %#lx\n", 112 | ses->pciname, q, lba, nb); 113 | error = ses->pci; 114 | break; 115 | } 116 | if (memcmp(wbuf, rbuf, nb << ns->blockshift)) { 117 | printf("ERROR: data mismatch %s q%d lba %#lx nlb %#lx\n", 118 | ses->pciname, q, lba, nb); 119 | error = ses->pci; 120 | break; 121 | } 122 | q++; 123 | } 124 | 125 | unvme_free(ns, rbuf); 126 | unvme_free(ns, wbuf); 127 | unvme_close(ns); 128 | 129 | if (!error) 130 | printf("Test device %s completed\n", ses->pciname); 131 | else if (error == ses->pci) 132 | printf("Test device %s failed\n", ses->pciname); 133 | return 0; 134 | } 135 | 136 | /** 137 | * Main program. 138 | */ 139 | int main(int argc, char* argv[]) 140 | { 141 | char* prog = strrchr(argv[0], '/'); 142 | prog = prog ? prog + 1 : argv[0]; 143 | 144 | char usage[256]; 145 | sprintf(usage, "Usage: %s PCINAME PCINAME...\n\n\ 146 | must specified 2 or more devices\n\ 147 | (e.g.: %s 0a:00.0/1 0a:00.0/2 0b:00.0/1)", prog, prog); 148 | 149 | if (argc < 3) { 150 | warnx(usage, prog); 151 | exit(1); 152 | } 153 | 154 | int numses = argc - 1; 155 | ses_t* ses = malloc(numses * sizeof(ses_t)); 156 | int i, k; 157 | for (i = 0; i < numses; i++) { 158 | int b, d, f, n = 1; 159 | if ((sscanf(argv[i+1], "%x:%x.%x/%x", &b, &d, &f, &n) != 4) && 160 | (sscanf(argv[i+1], "%x:%x.%x", &b, &d, &f) != 3)) { 161 | warnx(usage, prog); 162 | exit(1); 163 | } 164 | sprintf(ses[i].pciname, "%02x:%02x.%x/%x", b, d, f, n); 165 | ses[i].pci = (b << 16) | (d << 8) | f; 166 | ses[i].ins = 0; 167 | ses[i].inscount = 1; 168 | } 169 | 170 | // update number of namespace instances in the device list 171 | // first pass to count number of instances 172 | for (i = 0; i < (numses - 1); i++) { 173 | for (k = i + 1; k < numses; k++) { 174 | if (ses[k].ins == 0 && ses[k].pci == ses[i].pci) { 175 | ses[k].ins++; 176 | ses[i].inscount++; 177 | } 178 | } 179 | } 180 | // second pass to update number of instances (applicable for 3+ instances) 181 | for (i = 0; i < (numses - 1); i++) { 182 | for (k = i + 1; k < numses; k++) { 183 | if (ses[k].pci == ses[i].pci) { 184 | ses[k].inscount = ses[i].inscount; 185 | } 186 | } 187 | } 188 | 189 | printf("MULTI-DEVICE TEST BEGIN\n"); 190 | 191 | sem_init(&sm_ready, 0, 0); 192 | sem_init(&sm_start, 0, 0); 193 | time_t tstart = time(0); 194 | 195 | for (i = 0; i < numses; i++) { 196 | pthread_create(&ses[i].thread, 0, test_session, ses+i); 197 | sem_wait(&sm_ready); 198 | } 199 | for (i = 0; i < numses; i++) sem_post(&sm_start); 200 | for (i = 0; i < numses; i++) pthread_join(ses[i].thread, 0); 201 | 202 | sem_destroy(&sm_start); 203 | sem_destroy(&sm_ready); 204 | free(ses); 205 | 206 | printf("MULTI-DEVICE TEST COMPLETE (%ld secs)\n", time(0) - tstart); 207 | return 0; 208 | } 209 | 210 | -------------------------------------------------------------------------------- /src/unvme.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-2016, Micron Technology, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * 3. Neither the name of the copyright holder nor the names of its 16 | * contributors may be used to endorse or promote products derived 17 | * from this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | /** 33 | * @file 34 | * @brief UNVMe client library interface functions. 35 | */ 36 | 37 | #include "unvme_core.h" 38 | 39 | /** 40 | * Open a client session with specified number of IO queues and queue size. 41 | * @param pciname PCI device name (as %x:%x.%x[/NSID] format) 42 | * @param qcount number of io queues 43 | * @param qsize io queue size 44 | * @return namespace pointer or NULL if error. 45 | */ 46 | const unvme_ns_t* unvme_openq(const char* pciname, int qcount, int qsize) 47 | { 48 | if (qcount < 0 || qsize < 0 || qsize == 1) { 49 | ERROR("invalid qcount %d or qsize %d", qcount, qsize); 50 | return NULL; 51 | } 52 | 53 | int b, d, f, nsid = 1; 54 | if ((sscanf(pciname, "%x:%x.%x/%x", &b, &d, &f, &nsid) != 4) && 55 | (sscanf(pciname, "%x:%x.%x", &b, &d, &f) != 3)) { 56 | ERROR("invalid PCI %s (expect %%x:%%x.%%x[/NSID] format)", pciname); 57 | return NULL; 58 | } 59 | int pci = (b << 16) + (d << 8) + f; 60 | 61 | return unvme_do_open(pci, nsid, qcount, qsize); 62 | } 63 | 64 | /** 65 | * Open a client session. 66 | * @param pciname PCI device name (as %x:%x.%x[/NSID] format) 67 | * @return namespace pointer or NULL if error. 68 | */ 69 | const unvme_ns_t* unvme_open(const char* pciname) 70 | { 71 | return unvme_openq(pciname, 0, 0); 72 | } 73 | 74 | /** 75 | * Close a client session and delete its contained io queues. 76 | * @param ns namespace handle 77 | * @return 0 if ok else error code. 78 | */ 79 | int unvme_close(const unvme_ns_t* ns) 80 | { 81 | return unvme_do_close(ns); 82 | } 83 | 84 | /** 85 | * Allocate an I/O buffer associated with a session. 86 | * @param ns namespace handle 87 | * @param size buffer size 88 | * @return the allocated buffer or NULL if failure. 89 | */ 90 | void* unvme_alloc(const unvme_ns_t* ns, u64 size) 91 | { 92 | return unvme_do_alloc(ns, size); 93 | } 94 | 95 | /** 96 | * Free an I/O buffer associated with a session. 97 | * @param ns namespace handle 98 | * @param buf buffer pointer 99 | * @return 0 if ok else -1. 100 | */ 101 | int unvme_free(const unvme_ns_t* ns, void* buf) 102 | { 103 | return unvme_do_free(ns, buf); 104 | } 105 | 106 | /** 107 | * Submit a generic or vendor specific command. 108 | * @param ns namespace handle 109 | * @param qid client queue index (-1 for admin queue) 110 | * @param opc command op code 111 | * @param nsid namespace id 112 | * @param buf data buffer (from unvme_alloc) 113 | * @param bufsz data buffer size 114 | * @param cdw10_15 NVMe command word 10 through 15 115 | * @return descriptor or NULL if failed. 116 | */ 117 | inline unvme_iod_t unvme_acmd(const unvme_ns_t* ns, int qid, int opc, int nsid, 118 | void* buf, u64 bufsz, u32 cdw10_15[6]) 119 | { 120 | return (unvme_iod_t)unvme_do_cmd(ns, qid, opc, nsid, buf, bufsz, cdw10_15); 121 | } 122 | 123 | /** 124 | * Read data from specified logical blocks on device. 125 | * @param ns namespace handle 126 | * @param qid client queue index 127 | * @param buf data buffer (from unvme_alloc) 128 | * @param slba starting logical block 129 | * @param nlb number of logical blocks 130 | * @return I/O descriptor or NULL if failed. 131 | */ 132 | inline unvme_iod_t unvme_aread(const unvme_ns_t* ns, int qid, void* buf, u64 slba, u32 nlb) 133 | { 134 | return (unvme_iod_t)unvme_do_rw(ns, qid, NVME_CMD_READ, buf, slba, nlb); 135 | } 136 | 137 | /** 138 | * Write data to specified logical blocks on device. 139 | * @param ns namespace handle 140 | * @param qid client queue index 141 | * @param buf data buffer (from unvme_alloc) 142 | * @param slba starting logical block 143 | * @param nlb number of logical blocks 144 | * @return I/O descriptor or NULL if failed. 145 | */ 146 | inline unvme_iod_t unvme_awrite(const unvme_ns_t* ns, int qid, 147 | const void* buf, u64 slba, u32 nlb) 148 | { 149 | return (unvme_iod_t)unvme_do_rw(ns, qid, NVME_CMD_WRITE, (void*)buf, slba, nlb); 150 | } 151 | 152 | /** 153 | * Poll for completion status of a previous IO submission. 154 | * If there's no error, the descriptor will be freed. 155 | * @param iod IO descriptor 156 | * @param timeout in seconds 157 | * @return 0 if ok else error status (-1 for timeout). 158 | */ 159 | inline int unvme_apoll(unvme_iod_t iod, int timeout) 160 | { 161 | return unvme_do_poll((unvme_desc_t*)iod, timeout, NULL); 162 | } 163 | 164 | /** 165 | * Poll for completion status of a previous IO submission. 166 | * If there's no error, the descriptor will be freed. 167 | * @param iod IO descriptor 168 | * @param timeout in seconds 169 | * @param cqe_cs CQE command specific DW0 returned 170 | * @return 0 if ok else error status (-1 for timeout). 171 | */ 172 | inline int unvme_apoll_cs(unvme_iod_t iod, int timeout, u32* cqe_cs) 173 | { 174 | return unvme_do_poll((unvme_desc_t*)iod, timeout, cqe_cs); 175 | } 176 | 177 | /** 178 | * Submit a generic or vendor specific command and then poll for completion. 179 | * @param ns namespace handle 180 | * @param qid client queue index (-1 for admin queue) 181 | * @param opc command op code 182 | * @param nsid namespace id 183 | * @param buf data buffer (from unvme_alloc) 184 | * @param bufsz data buffer size 185 | * @param cdw10_15 NVMe command word 10 through 15 186 | * @param cqe_cs CQE command specific DW0 returned 187 | * @return descriptor or NULL if failed. 188 | */ 189 | int unvme_cmd(const unvme_ns_t* ns, int qid, int opc, int nsid, 190 | void* buf, u64 bufsz, u32 cdw10_15[6], u32* cqe_cs) 191 | { 192 | unvme_iod_t iod = unvme_acmd(ns, qid, opc, nsid, buf, bufsz, cdw10_15); 193 | if (iod) { 194 | sched_yield(); 195 | return unvme_apoll_cs(iod, UNVME_TIMEOUT, cqe_cs); 196 | } 197 | return -1; 198 | } 199 | 200 | /** 201 | * Read data from specified logical blocks on device. 202 | * @param ns namespace handle 203 | * @param qid client queue index 204 | * @param buf data buffer (from unvme_alloc) 205 | * @param slba starting logical block 206 | * @param nlb number of logical blocks 207 | * @return 0 if ok else error status. 208 | */ 209 | int unvme_read(const unvme_ns_t* ns, int qid, void* buf, u64 slba, u32 nlb) 210 | { 211 | unvme_iod_t iod = unvme_aread(ns, qid, buf, slba, nlb); 212 | if (iod) { 213 | sched_yield(); 214 | return unvme_apoll(iod, UNVME_TIMEOUT); 215 | } 216 | return -1; 217 | } 218 | 219 | /** 220 | * Write data to specified logical blocks on device. 221 | * @param ns namespace handle 222 | * @param qid client queue index 223 | * @param buf data buffer (from unvme_alloc) 224 | * @param slba starting logical block 225 | * @param nlb number of logical blocks 226 | * @return 0 if ok else error status. 227 | */ 228 | int unvme_write(const unvme_ns_t* ns, int qid, 229 | const void* buf, u64 slba, u32 nlb) 230 | { 231 | unvme_iod_t iod = unvme_awrite(ns, qid, buf, slba, nlb); 232 | if (iod) { 233 | sched_yield(); 234 | return unvme_apoll(iod, UNVME_TIMEOUT); 235 | } 236 | return -1; 237 | } 238 | 239 | -------------------------------------------------------------------------------- /ioengine/unvme_fio.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief UNVMe fio plugin engine. 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include "unvme.h" 11 | #include "config-host.h" 12 | #include "fio.h" 13 | #include "optgroup.h" // since fio 2.4 14 | 15 | 16 | #define TDEBUG(fmt, arg...) //fprintf(stderr, "#%s.%d " fmt "\n", __func__, td->thread_number, ##arg) 17 | #define FATAL(fmt, arg...) do { warnx(fmt, ##arg); abort(); } while (0) 18 | 19 | 20 | /// Context used for thread initialization 21 | typedef struct { 22 | pthread_mutex_t mutex; 23 | const unvme_ns_t* ns; 24 | int ncpus; 25 | } unvme_context_t; 26 | 27 | /// Thread IO completion queue 28 | typedef struct io_u *unvme_iocq_t; 29 | 30 | 31 | // Static variables 32 | static unvme_context_t unvme = { .mutex = PTHREAD_MUTEX_INITIALIZER }; 33 | 34 | 35 | /* 36 | * Clean up UNVMe upon exit. 37 | */ 38 | static void do_unvme_cleanup(void) 39 | { 40 | unvme_close(unvme.ns); 41 | unvme.ns = NULL; 42 | } 43 | 44 | /* 45 | * Initialize UNVMe once. 46 | */ 47 | static void do_unvme_init(struct thread_data *td) 48 | { 49 | TDEBUG("device=%s numjobs=%d iodepth=%d", td->o.filename, td->o.numjobs, td->o.iodepth); 50 | 51 | pthread_mutex_lock(&unvme.mutex); 52 | 53 | if (!unvme.ns) { 54 | char pciname[32]; 55 | int b, d, f, n=1; 56 | sscanf(td->o.filename, "%x.%x.%x.%x", &b, &d, &f, &n); 57 | sprintf(pciname, "%x:%x.%x/%x", b, d, f, n); 58 | unvme.ns = unvme_open(pciname); 59 | 60 | if (!unvme.ns) 61 | FATAL("unvme_open %s failed", pciname); 62 | if (td->o.iodepth >= unvme.ns->qsize) 63 | FATAL("iodepth %d greater than queue size", td->o.iodepth); 64 | 65 | unvme.ncpus = sysconf(_SC_NPROCESSORS_ONLN); 66 | printf("unvme_open %s q=%dx%d ncpus=%d\n", 67 | unvme.ns->device, unvme.ns->qcount, unvme.ns->qsize, unvme.ncpus); 68 | 69 | atexit(do_unvme_cleanup); 70 | } 71 | 72 | if (td->thread_number > unvme.ns->qcount || 73 | td->o.iodepth >= unvme.ns->qsize) { 74 | FATAL("thread %d iodepth %d exceeds UNVMe queue limit %dx%d", 75 | td->thread_number, td->o.iodepth, unvme.ns->qcount, unvme.ns->qsize-1); 76 | } 77 | 78 | pthread_mutex_unlock(&unvme.mutex); 79 | } 80 | 81 | /* 82 | * The ->init() function is called once per thread/process, and should set up 83 | * any structures that this io engine requires to keep track of io. Not 84 | * required. 85 | */ 86 | static int fio_unvme_init(struct thread_data *td) 87 | { 88 | unvme_iocq_t* iocq = calloc(td->o.iodepth, sizeof(void*)); 89 | if (!iocq) return 1; 90 | td->io_ops_data = iocq; 91 | return 0; 92 | } 93 | 94 | /* 95 | * This is paired with the ->init() function and is called when a thread is 96 | * done doing io. Should tear down anything setup by the ->init() function. 97 | * Not required. 98 | */ 99 | static void fio_unvme_cleanup(struct thread_data *td) 100 | { 101 | if (td->io_ops_data) free(td->io_ops_data); 102 | td->io_ops_data = NULL; 103 | } 104 | 105 | /* 106 | * Hook for opening the given file. Unless the engine has special 107 | * needs, it usually just provides generic_file_open() as the handler. 108 | */ 109 | static int fio_unvme_open(struct thread_data *td, struct fio_file *f) 110 | { 111 | TDEBUG(); 112 | return 0; 113 | } 114 | 115 | /* 116 | * Hook for closing a file. See fio_unvme_open(). 117 | */ 118 | static int fio_unvme_close(struct thread_data *td, struct fio_file *f) 119 | { 120 | TDEBUG(); 121 | return 0; 122 | } 123 | 124 | /* 125 | * Allocate IO memory. 126 | */ 127 | static int fio_unvme_iomem_alloc(struct thread_data *td, size_t len) 128 | { 129 | // FIO bug - found cases where this is called before ->get_file_size() 130 | if (!unvme.ns) do_unvme_init(td); 131 | 132 | if (!td->orig_buffer) td->orig_buffer = unvme_alloc(unvme.ns, len); 133 | TDEBUG("%p %#lx", td->orig_buffer, len); 134 | return td->orig_buffer == NULL; 135 | } 136 | 137 | /* 138 | * Free IO memory. 139 | */ 140 | static void fio_unvme_iomem_free(struct thread_data *td) 141 | { 142 | TDEBUG("%p", td->orig_buffer); 143 | if (td->orig_buffer) unvme_free(unvme.ns, td->orig_buffer); 144 | } 145 | 146 | /* 147 | * The ->event() hook is called to match an event number with an io_u. 148 | * After the core has called ->getevents() and it has returned eg 3, 149 | * the ->event() hook must return the 3 events that have completed for 150 | * subsequent calls to ->event() with [0-2]. Required. 151 | */ 152 | static struct io_u* fio_unvme_event(struct thread_data *td, int event) 153 | { 154 | unvme_iocq_t* iocq = td->io_ops_data; 155 | TDEBUG("GET.%d %p", event, iocq[event]->buf); 156 | return iocq[event]; 157 | } 158 | 159 | /* 160 | * The ->getevents() hook is used to reap completion events from an async 161 | * io engine. It returns the number of completed events since the last call, 162 | * which may then be retrieved by calling the ->event() hook with the event 163 | * numbers. Required. 164 | */ 165 | static int fio_unvme_getevents(struct thread_data *td, unsigned int min, 166 | unsigned int max, const struct timespec *t) 167 | { 168 | int i; 169 | struct io_u* io_u; 170 | unvme_iocq_t* iocq = td->io_ops_data; 171 | int ec = 0; 172 | 173 | for (;;) { 174 | io_u_qiter(&td->io_u_all, io_u, i) { 175 | if (io_u->engine_data) { 176 | int stat = unvme_apoll(io_u->engine_data, 0); 177 | if (stat == 0) { 178 | io_u->engine_data = NULL; 179 | TDEBUG("PUT.%d %p (%d %d)", ec, io_u->buf, min, max); 180 | iocq[ec++] = io_u; 181 | if (ec == max) return ec; 182 | } else if (stat == -1) { 183 | if (ec >= min) return ec; 184 | } else { 185 | FATAL("\nunvme_apoll return %#x", stat); 186 | } 187 | } 188 | } 189 | } 190 | 191 | return 0; 192 | } 193 | 194 | 195 | #ifndef _UNVME_MMCC_H 196 | 197 | /* 198 | * The ->queue() hook is responsible for initiating io on the io_u 199 | * being passed in. If the io engine is a synchronous one, io may complete 200 | * before ->queue() returns. Required. 201 | * 202 | * The io engine must transfer in the direction noted by io_u->ddir 203 | * to the buffer pointed to by io_u->xfer_buf for as many bytes as 204 | * io_u->xfer_buflen. Residual data count may be set in io_u->resid 205 | * for a short read/write. 206 | */ 207 | static int fio_unvme_queue(struct thread_data *td, struct io_u *io_u) 208 | { 209 | int q = td->thread_number - 1; 210 | void* buf = io_u->buf; 211 | u64 slba = io_u->offset >> unvme.ns->blockshift; 212 | int nlb = io_u->xfer_buflen >> unvme.ns->blockshift; 213 | 214 | switch (io_u->ddir) { 215 | case DDIR_READ: 216 | TDEBUG("READ q%d %p %#lx %d", q, buf, slba, nlb); 217 | if ((io_u->engine_data = unvme_aread(unvme.ns, q, buf, slba, nlb))) 218 | return FIO_Q_QUEUED; 219 | FATAL("\nunvme_aread q=%d slba=%#lx nlb=%d", q, slba, nlb); 220 | break; 221 | 222 | case DDIR_WRITE: 223 | TDEBUG("WRITE q%d %p %#lx %d", q, buf, slba, nlb); 224 | if ((io_u->engine_data = unvme_awrite(unvme.ns, q, buf, slba, nlb))) 225 | return FIO_Q_QUEUED; 226 | FATAL("\nunvme_awrite q=%d slba=%#lx nlb=%d", q, slba, nlb); 227 | break; 228 | 229 | default: 230 | break; 231 | } 232 | 233 | return FIO_Q_COMPLETED; 234 | } 235 | 236 | /* 237 | * The ->get_file_size() is called once for every job (i.e. numjobs) 238 | * before all other functions. This is called after ->setup() but 239 | * is simpler to initialize here since we only care about the device name 240 | * (given as file_name) and just have to specify the device size. 241 | */ 242 | static int fio_unvme_get_file_size(struct thread_data *td, struct fio_file *f) 243 | { 244 | TDEBUG("file=%s", f->file_name); 245 | if (!fio_file_size_known(f)) { 246 | do_unvme_init(td); 247 | f->filetype = FIO_TYPE_CHAR; 248 | f->real_file_size = unvme.ns->blockcount * unvme.ns->blocksize; 249 | fio_file_set_size_known(f); 250 | } 251 | return 0; 252 | } 253 | 254 | 255 | // Note that the structure is exported, so that fio can get it via 256 | // dlsym(..., "ioengine"); 257 | struct ioengine_ops ioengine = { 258 | .name = "unvme", 259 | .version = FIO_IOOPS_VERSION, 260 | .get_file_size = fio_unvme_get_file_size, 261 | .init = fio_unvme_init, 262 | .cleanup = fio_unvme_cleanup, 263 | .open_file = fio_unvme_open, 264 | .close_file = fio_unvme_close, 265 | .iomem_alloc = fio_unvme_iomem_alloc, 266 | .iomem_free = fio_unvme_iomem_free, 267 | .queue = fio_unvme_queue, 268 | .getevents = fio_unvme_getevents, 269 | .event = fio_unvme_event, 270 | .flags = FIO_NOEXTEND | FIO_RAWIO, 271 | }; 272 | 273 | #endif // _UNVME_MMCC_H 274 | 275 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | UNVMe - A User Space NVMe Driver 2 | ================================ 3 | 4 | UNVMe is a user space NVMe driver developed at Micron Technology. 5 | 6 | **This version of the driver has been modified to work on the ARM64 processors 7 | of the Xilinx Zynq UltraScale+ architecture, rather than UNVMe's original 8 | target of x86-64. Note that instructions and requirements below are for the 9 | original UNVMe project, and have not yet been updated to reflect the changes.** 10 | 11 | The driver in this model is implemented as a library (libunvme.a) that 12 | applications can be linked with. Upon start, an application will first 13 | initialize the NVMe device(s) and then, afterward, it can submit and process 14 | I/O directly from the user space application to the device. 15 | 16 | UNVMe application combines the application and driver into one program and 17 | thus it takes complete ownership of a device upon execution, so an NVMe device 18 | can only be accessed by one application at any given time. However, a UNVMe 19 | application can access multiple devices simultaneously. 20 | Device name is to be specified in PCI format with optional NSID 21 | (e.g. 0a:00.0 or 0a:00.0/1). NSID 1 is assumed if /NSID is omitted. 22 | 23 | UNVMe applications must use the provided APIs instead of the typical system 24 | POSIX APIs to access a device. 25 | 26 | 27 | A design note: UNVMe is designed modularly with independent components 28 | including nvme (unvme_nvme.h) and vfio (unvme_vfio.h), in which the unvme 29 | interface is a module layered on top of the nvme and vfio modules. 30 | 31 | 32 | The programs under test/nvme are built directly on nvme and vfio modules 33 | without dependency on the unvme module. They serve as examples for building 34 | individual NVMe admin commands. 35 | 36 | The programs under test/unvme are examples for developing applications using 37 | the unvme interfaces (defined in unvme.h). Note that vendor specific and NVMe 38 | admin commands may also be built with the provided APIs. 39 | 40 | 41 | 42 | System Requirements 43 | =================== 44 | 45 | UNVMe has a dependency on features provided by the VFIO module in the Linux 46 | kernel (introduced since 3.6). UNVMe code has been built and tested only 47 | with CentOS 6 and 7 running on x86_64 CPU based systems. 48 | 49 | UNVMe requires the following hardware and software support: 50 | 51 | VT-d - CPU must support VT-d (Virtualization Technology for Directed I/O). 52 | Check for Intel product specifications. 53 | VT-d setting is normally found in the BIOS configuration. 54 | 55 | VFIO - Linux OS must have kernel 3.6 or later compiled with the 56 | following configurations: 57 | 58 | CONFIG_IOMMU_API=y 59 | CONFIG_IOMMU_SUPPORT=y 60 | CONFIG_INTEL_IOMMU=y 61 | CONFIG_VFIO=m 62 | CONFIG_VFIO_PCI=m 63 | CONFIG_VFIO_IOMMU_TYPE1=m 64 | 65 | The boot command line must set "intel_iommu=on" argument. 66 | 67 | To verify the system correctly configured with VFIO support, 68 | check that /sys/kernel/iommu_groups directory is not empty but 69 | contains other subdirectories (i.e. group numbers). 70 | 71 | On CentOS 6, which comes with kernel version 2.x (i.e. prior to 3.6), 72 | the user must compile and boot a newer kernel that has the VFIO module. 73 | The user must also copy the header file from the kernel source directory 74 | include/uapi/linux/vfio.h to /usr/include/linux if that is missing. 75 | 76 | UNVMe requires root privilege to access a device. 77 | 78 | 79 | 80 | Build, Run, and Test 81 | ==================== 82 | 83 | To download and install the driver library, run: 84 | 85 | $ git clone https://github.com/MicronSSD/unvme.git 86 | $ make install 87 | 88 | 89 | To setup a device for UNVMe usage (do once before running applications), run: 90 | 91 | $ unvme-setup bind 92 | 93 | By default, all NVMe devices found in the system will be bound to the 94 | VFIO driver enabling them for UNVMe usage. Specific PCI device(s) 95 | may also be specified for binding, e.g. unvme-setup bind 0a:00.0. 96 | 97 | 98 | To reset device(s) to the NVMe kernel space driver, run: 99 | 100 | $ unvme-setup reset 101 | 102 | 103 | For usage help, invoke unvme-setup without argument. 104 | 105 | 106 | To run UNVMe tests, specify the device(s) with command: 107 | 108 | $ test/unvme-test 0a:00.0 0b:00.0 109 | 110 | 111 | The commands under test/nvme may also be invoked individually, e.g.: 112 | 113 | $ test/nvme/nvme_identify 0a:00.0 114 | $ test/nvme/nvme_get_features 0a:00.0 115 | $ test/nvme/nvme_get_log_page 0a:00.0 1 2 116 | ... 117 | 118 | 119 | 120 | Python Support 121 | ============== 122 | 123 | Python dynamic library binding support is provided as unvme.py with examples 124 | under the test/python directory. 125 | 126 | $ python test/python/unvme_info.py 0a:00.0 127 | $ python test/python/unvme_get_features.py 0a:00.0 128 | $ python test/python/unvme_wr_ex.py 0a:00.0 129 | 130 | 131 | Note: For Python scripts that reside elsewhere, you need to copy "libunvme.so" 132 | and "unvme.py" to your working directory. Alternatively, you can run 133 | "make install" and export PYTHONPATH to where unvme.py module is. 134 | 135 | 136 | 137 | I/O Benchmark Tests 138 | =================== 139 | 140 | To run fio benchmark tests against UNVMe: 141 | 142 | 1) Download and compile the fio source code (available on https://github.com/axboe/fio). 143 | 144 | 145 | 2) Edit unvme/Makefile.def and set FIODIR to the compiled fio source 146 | directory (or alternatively export the FIODIR variable). 147 | 148 | 149 | 3) Rerun make to include building the fio engine, since setting FIODIR 150 | will enable ioengine/unvme_fio to be built. 151 | 152 | $ make 153 | 154 | Note that the fio source code is constantly changing, and unvme_fio.c 155 | has been verified to work with the fio versions 2.7 through 2.19 156 | 157 | 158 | 4) Set up for UNVMe driver (if not already): 159 | 160 | $ unvme-setup bind 161 | 162 | 163 | 5) Launch the test script: 164 | 165 | $ test/unvme-benchmark DEVICENAME 166 | 167 | Note the benchmark test, by default, will run random write and read 168 | tests with 1, 4, 8, and 16 threads, and io depth of 1, 4, 8, and 16. 169 | Each test will be run for 120 seconds after a ramp time of 60 seconds. 170 | These default settings can be overridden from the shell command line, e.g.: 171 | 172 | $ RAMPTIME=10 RUNTIME=20 NUMJOBS="1 4" IODEPTH="4 8" test/unvme-benchmark 0a:00.0 173 | 174 | 175 | To run the same tests against the kernel space driver: 176 | 177 | $ unvme-setup reset 178 | 179 | $ test/unvme-benchmark /dev/nvme0n1 180 | 181 | 182 | All the FIO results, by default, will be stored in test/out directory. 183 | 184 | 185 | 186 | Application Programming Interfaces 187 | ================================== 188 | 189 | The UNVMe APIs are designed with application ease of use in mind. 190 | As defined in unvme.h, the following functions are supported: 191 | 192 | unvme_open() - This function must be invoked first to establish a 193 | connection to the specified PCI device. 194 | 195 | unvme_close() - Close a device connection. 196 | 197 | 198 | unvme_alloc() - Allocate an I/O buffer. 199 | 200 | unvme_free() - Free the allocated I/O buffer. 201 | 202 | 203 | unvme_write() - Write the specified number of blocks (nlb) to the 204 | device starting at logical block address (slba). 205 | The buffer must be acquired from unvme_alloc(). 206 | The qid (range from 0 to 1 less than the number of 207 | queues supported by the device) may be used for 208 | thread safe I/O operations. Each queue must only 209 | be accessed by a one thread at any one time. 210 | 211 | unvme_awrite() - Submit a write command to the device asynchronously 212 | and return immediately. The returned descriptor 213 | is used via apoll() or apoll_cs() for completion. 214 | 215 | 216 | unvme_read() - Read from the device (i.e. like unvme_write). 217 | 218 | unvme_aread() - Submit an asynchronous read (i.e. like unvme_awrite). 219 | 220 | 221 | unvme_cmd() - Issue a generic or vendor specific command to 222 | the device. 223 | 224 | unvme_acmd() - Submit a generic or vendor specific command to 225 | the device asynchronously and return immediately. 226 | The returned descriptor is used via apoll() or 227 | apoll_cs() for command completion. 228 | 229 | 230 | unvme_apoll() - Poll an asynchronous read/write for completion. 231 | 232 | unvme_apoll_cs() - Poll an asynchronous read/write for completion with 233 | NVMe command specific DW0 status returned. 234 | 235 | 236 | 237 | Note that a user space filesystem, namely UNFS, has also been developed 238 | at Micron to work with the UNVMe driver. Such available filesystem enables 239 | major applications like MongoDB to work with UNVMe driver. 240 | See https://github.com/MicronSSD/unfs.git for details. 241 | 242 | -------------------------------------------------------------------------------- /test/unvme/unvme_lat_test.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-2016, Micron Technology, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * 3. Neither the name of the copyright holder nor the names of its 16 | * contributors may be used to endorse or promote products derived 17 | * from this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | /** 33 | * @file 34 | * @brief UNVMe I/O latency test. 35 | */ 36 | 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | 48 | #include "unvme.h" 49 | #include "rdtsc.h" 50 | 51 | /// macro to print an io related error message 52 | #define IOERROR(s, p) errx(1, "ERROR: " s " lba=%#lx", (p)->lba) 53 | 54 | /// page structure 55 | typedef struct { 56 | void* buf; ///< IO buffer 57 | u64 lba; ///< lba 58 | unvme_iod_t iod; ///< returned IO descriptor 59 | u64 tsc; ///< tsc time 60 | } lat_page_t; 61 | 62 | // Global variables 63 | static const unvme_ns_t* ns; ///< unvme namespace pointer 64 | static int qcount = 1; ///< queue count 65 | static int qsize = 8; ///< queue size 66 | static int runtime = 15; ///< run time in seconds 67 | static u64 endtsc; ///< end run tsc 68 | static u64 timeout; ///< tsc elapsed timeout 69 | static sem_t sm_ready; ///< semaphore to start thread 70 | static sem_t sm_start; ///< semaphore to start test 71 | static pthread_t* ses; ///< array of thread sessions 72 | static u64 last_lba; ///< last page boundary lba 73 | static u64 ioc; ///< total number of io count 74 | static u64 avg_slat; ///< total submission time 75 | static u64 avg_clat; ///< total completion time 76 | static u64 min_slat; ///< minimum submission time 77 | static u64 max_slat; ///< maximum submission time 78 | static u64 min_clat; ///< minimum completimesn time 79 | static u64 max_clat; ///< maximum completimesn time 80 | 81 | /** 82 | * Submit an io and record the submission latency time. 83 | */ 84 | static void io_submit(int q, int rw, lat_page_t* p) 85 | { 86 | p->lba = random() & ~(ns->nbpp - 1); 87 | if (p->lba > last_lba) p->lba &= last_lba; 88 | 89 | p->tsc = rdtsc(); 90 | if (rw) { 91 | p->iod = unvme_awrite(ns, q, p->buf, p->lba, ns->nbpp); 92 | if (!p->iod) IOERROR("awrite", p); 93 | } else { 94 | p ->iod = unvme_aread(ns, q, p->buf, p->lba, ns->nbpp); 95 | if (!p->iod) IOERROR("aread", p); 96 | } 97 | ioc++; 98 | 99 | u64 ts = rdtsc_elapse((u64)(p->tsc)); 100 | if (min_slat > ts) min_slat = ts; 101 | if (max_slat < ts) max_slat = ts; 102 | avg_slat += ts; 103 | } 104 | 105 | /** 106 | * Queue thread test. 107 | */ 108 | static void* run_thread(void* arg) 109 | { 110 | int rw = (long)arg >> 16; 111 | int q = (long)arg & 0xffff; 112 | int qdepth = qsize - 1; 113 | 114 | u64 lba = (q * qcount * qdepth * ns->nbpp) << 1; 115 | lat_page_t* pages = calloc(ns->maxiopq, sizeof(lat_page_t)); 116 | lat_page_t* p = pages; 117 | int i; 118 | for (i = 0; i < ns->maxiopq; i++) { 119 | p->buf = unvme_alloc(ns, ns->pagesize); 120 | lba += (ns->nbpp << 1); 121 | if (lba > last_lba) lba = i * ns->nbpp; 122 | p->lba = lba; 123 | p++; 124 | } 125 | 126 | sem_post(&sm_ready); 127 | sem_wait(&sm_start); 128 | 129 | for (i = 0; i < qdepth; i++) io_submit(q, rw, pages + i); 130 | 131 | i = 0; 132 | int pending = qdepth; 133 | do { 134 | p = pages + i; 135 | if (p->iod) { 136 | if (unvme_apoll(p->iod, 0) == 0) { 137 | u64 tc = rdtsc_elapse(p->tsc); 138 | if (min_clat > tc) min_clat = tc; 139 | if (max_clat < tc) max_clat = tc; 140 | avg_clat += tc; 141 | 142 | if ((tc + p->tsc) < endtsc) { 143 | io_submit(q, rw, p); 144 | } else { 145 | p->iod = 0; 146 | pending--; 147 | } 148 | } else if ((rdtsc_elapse(p->tsc)) > timeout) { 149 | IOERROR("apoll timeout", p); 150 | } 151 | } 152 | if (++i == ns->maxiopq) i = 0; 153 | } while (pending > 0); 154 | 155 | p = pages; 156 | for (i = 0; i < ns->maxiopq; i++) { 157 | unvme_free(ns, p->buf); 158 | p++; 159 | } 160 | free(pages); 161 | return 0; 162 | } 163 | 164 | 165 | /** 166 | * Run test to spawn one thread for each queue. 167 | */ 168 | void run_test(const char* name, int rw) 169 | { 170 | ioc = 0; 171 | avg_slat = 0; 172 | avg_clat = 0; 173 | min_slat = -1; 174 | max_slat = 0; 175 | min_clat = -1; 176 | max_clat = 0; 177 | 178 | sem_init(&sm_ready, 0, 0); 179 | sem_init(&sm_start, 0, 0); 180 | 181 | u64 tsec = rdtsc_second(); 182 | int q; 183 | for (q = 0; q < qcount; q++) { 184 | long arg = (rw << 16) + q; 185 | pthread_create(&ses[q], 0, run_thread, (void*)arg); 186 | sem_wait(&sm_ready); 187 | } 188 | 189 | sleep(1); 190 | time_t te = time(0); 191 | struct tm* t = localtime(&te); 192 | printf("%s: run test for %d seconds (%02d:%02d:%02d)\n", 193 | name, runtime, t->tm_hour, t->tm_min, t->tm_sec); 194 | endtsc = rdtsc() + (runtime * tsec); 195 | timeout = UNVME_TIMEOUT * tsec; 196 | 197 | for (q = 0; q < qcount; q++) sem_post(&sm_start); 198 | for (q = 0; q < qcount; q++) pthread_join(ses[q], 0); 199 | 200 | u64 utsc = tsec / 1000000; 201 | printf("%s: slat=(%.2f-%.2f %.2f) lat=(%.2f-%.2f %.2f) usecs ioc=%lu\n", 202 | name, (double)min_slat/utsc, (double)max_slat/utsc, 203 | (double)avg_slat/ioc/utsc, (double)min_clat/utsc, 204 | (double)max_clat/utsc, (double)avg_clat/ioc/utsc, ioc); 205 | /* 206 | printf("%s: slat=(%lu-%lu %lu) lat=(%lu-%lu %lu) tscs ioc=%lu\n", 207 | name, min_slat, max_slat, avg_slat/ioc, 208 | min_clat, max_clat, avg_clat/ioc, ioc); 209 | */ 210 | 211 | sem_destroy(&sm_ready); 212 | sem_destroy(&sm_start); 213 | } 214 | 215 | /** 216 | * Main program. 217 | */ 218 | int main(int argc, char* argv[]) 219 | { 220 | const char* usage = "Usage: %s [OPTION]... PCINAME\n\ 221 | -t SECONDS run time in seconds (default 15)\n\ 222 | -q QCOUNT number of queues/threads (default 2)\n\ 223 | -d QDEPTH queue depth (default 8)\n\ 224 | PCINAME PCI device name (as 01:00.0[/1] format)"; 225 | 226 | char* prog = strrchr(argv[0], '/'); 227 | prog = prog ? prog + 1 : argv[0]; 228 | 229 | int opt; 230 | while ((opt = getopt(argc, argv, "t:q:d:")) != -1) { 231 | switch (opt) { 232 | case 't': 233 | runtime = strtol(optarg, 0, 0); 234 | if (runtime <= 0) errx(1, "runtime must be > 0"); 235 | break; 236 | case 'q': 237 | qcount = strtol(optarg, 0, 0); 238 | break; 239 | case 'd': 240 | qsize = strtol(optarg, 0, 0); 241 | break; 242 | default: 243 | warnx(usage, prog); 244 | exit(1); 245 | } 246 | } 247 | if ((optind + 1) != argc) { 248 | warnx(usage, prog); 249 | exit(1); 250 | } 251 | char* pciname = argv[optind]; 252 | 253 | printf("LATENCY TEST BEGIN\n"); 254 | time_t tstart = time(0); 255 | if (!(ns = unvme_open(pciname))) exit(1); 256 | if (qcount <= 0 || qcount > ns->qcount) errx(1, "qcount limit %d", ns->qcount); 257 | if (qsize <= 1 || qsize > ns->qsize) errx(1, "qsize limit %d", ns->qsize); 258 | 259 | last_lba = (ns->blockcount - ns->nbpp) & ~(u64)(ns->nbpp - 1); 260 | if (!qcount) qcount = ns->qcount; 261 | if (!qsize) qsize = ns->qsize; 262 | 263 | printf("%s qc=%d/%d qs=%d/%d bc=%#lx bs=%d mbio=%d\n", 264 | ns->device, qcount, ns->qcount, qsize, ns->qsize, 265 | ns->blockcount, ns->blocksize, ns->maxbpio); 266 | 267 | ses = calloc(qcount, sizeof(pthread_t)); 268 | 269 | run_test("read", 0); 270 | run_test("write", 1); 271 | 272 | free(ses); 273 | unvme_close(ns); 274 | 275 | printf("LATENCY TEST COMPLETE (%ld secs)\n", time(0) - tstart); 276 | return 0; 277 | } 278 | 279 | -------------------------------------------------------------------------------- /test/unvme/unvme_mts_test.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-2016, Micron Technology, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * 3. Neither the name of the copyright holder nor the names of its 16 | * contributors may be used to endorse or promote products derived 17 | * from this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | /** 33 | * @file 34 | * @brief UNVMe multi-threaded/session test. 35 | */ 36 | 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | 48 | #include "unvme.h" 49 | 50 | // Global variables 51 | static int numses = 4; ///< number of thread sessions 52 | static int qcount = 2; ///< number of queues per session 53 | static int maxnlb = 32; ///< maximum number of blocks per IO 54 | static sem_t sm_ready; ///< semaphore for ready 55 | static sem_t sm_start; ///< semaphore for start 56 | static const unvme_ns_t* ns; ///< driver namespace handle 57 | 58 | /// thread session arguments 59 | typedef struct { 60 | int id; ///< session id 61 | int qid; ///< starting queue id 62 | u64 slba; ///< starting lba 63 | } ses_arg_t; 64 | 65 | 66 | /** 67 | * Thread per queue. 68 | */ 69 | void* test_queue(void* arg) 70 | { 71 | ses_arg_t* ses = arg; 72 | u64 slba, wlen, w, *p; 73 | int nlb, l, i; 74 | 75 | printf("Test s%d q%-2d lba %#lx started\n", ses->id, ses->qid, ses->slba); 76 | sem_post(&sm_ready); 77 | sem_wait(&sm_start); 78 | 79 | unvme_iod_t* iod = calloc(ns->qsize, sizeof(unvme_iod_t)); 80 | void** buf = calloc(ns->qsize, sizeof(void*)); 81 | int* buflen = calloc(ns->qsize, sizeof(int)); 82 | 83 | for (l = 0; l < numses; l++) { 84 | // allocate buffers 85 | for (i = 0; i < ns->qsize; i++) { 86 | nlb = random() % maxnlb + 1; 87 | buflen[i] = nlb * ns->blocksize; 88 | if (!(buf[i] = unvme_alloc(ns, buflen[i]))) 89 | errx(1, "alloc.%d.%d.%d failed", ses->id, ses->qid, i); 90 | } 91 | 92 | #ifdef DO_SYNC_WRITE_READ 93 | slba = ses->slba; 94 | for (i = 0; i < ns->qsize; i++) { 95 | nlb = buflen[i] / ns->blocksize; 96 | wlen = buflen[i] / sizeof (u64); 97 | p = buf[i]; 98 | for (w = 0; w < wlen; w++) p[w] = (w << 32) + i; 99 | if (unvme_write(ns, ses->qid, p, slba, nlb)) 100 | errx(1, "write.%d.%d.%d failed", ses->id, ses->qid, i); 101 | bzero(p, buflen[i]); 102 | if (unvme_read(ns, ses->qid, p, slba, nlb)) 103 | errx(1, "read.%d.%d.%d failed", ses->id, ses->qid, i); 104 | for (w = 0; w < wlen; w++) { 105 | if (p[w] != ((w << 32) + i)) 106 | errx(1, "data.%d.%d.%d error", ses->id, ses->qid, i); 107 | } 108 | slba += nlb; 109 | } 110 | #else 111 | // async write 112 | slba = ses->slba; 113 | for (i = 0; i < ns->qsize; i++) { 114 | nlb = buflen[i] / ns->blocksize; 115 | wlen = buflen[i] / sizeof (u64); 116 | p = buf[i]; 117 | for (w = 0; w < wlen; w++) p[w] = (w << 32) + i; 118 | if (!(iod[i] = unvme_awrite(ns, ses->qid, p, slba, nlb))) 119 | errx(1, "awrite.%d.%d.%d failed", ses->id, ses->qid, i); 120 | slba += nlb; 121 | } 122 | 123 | // async poll to complete all writes 124 | for (i = 0; i < ns->qsize; i++) { 125 | if (unvme_apoll(iod[i], UNVME_TIMEOUT)) 126 | errx(1, "apoll.%d.%d.%d failed", ses->id, ses->qid, i); 127 | } 128 | 129 | // do sync read and compare 130 | slba = ses->slba; 131 | for (i = 0; i < ns->qsize; i++) { 132 | nlb = buflen[i] / ns->blocksize; 133 | wlen = buflen[i] / sizeof (u64); 134 | p = buf[i]; 135 | for (int i = 0; i < buflen[i]/sizeof(u64); i++) p[i] = 0; 136 | if (unvme_read(ns, ses->qid, p, slba, nlb)) 137 | errx(1, "read.%d.%d.%d failed", ses->id, ses->qid, i); 138 | for (w = 0; w < wlen; w++) { 139 | if (p[w] != ((w << 32) + i)) 140 | errx(1, "data.%d.%d.%d error", ses->id, ses->qid, i); 141 | } 142 | slba += nlb; 143 | } 144 | #endif 145 | 146 | // free buffers 147 | for (i = 0; i < ns->qsize; i++) { 148 | if (unvme_free(ns, buf[i])) 149 | errx(1, "free failed"); 150 | } 151 | } 152 | 153 | free(buflen); 154 | free(buf); 155 | free(iod); 156 | printf("Test s%d q%-2d lba %#lx completed\n", ses->id, ses->qid, ses->slba); 157 | 158 | return 0; 159 | } 160 | 161 | /** 162 | * Thread per session. 163 | */ 164 | void* test_session(void* arg) 165 | { 166 | int sesid = (long)arg; 167 | int sid = sesid + 1; 168 | 169 | printf("Session %d started\n", sid); 170 | sem_post(&sm_ready); 171 | sem_wait(&sm_start); 172 | 173 | u64 bpq = ns->blockcount / numses / qcount; 174 | pthread_t* sqt = calloc(qcount, sizeof(pthread_t)); 175 | ses_arg_t* sarg = calloc(qcount, sizeof(ses_arg_t)); 176 | 177 | int q; 178 | for (q = 0; q < qcount; q++) { 179 | sarg[q].id = sid; 180 | sarg[q].qid = q + sesid * qcount; 181 | sarg[q].slba = bpq * (sesid * qcount + q); 182 | pthread_create(&sqt[q], 0, test_queue, &sarg[q]); 183 | } 184 | for (q = 0; q < qcount; q++) sem_post(&sm_start); 185 | for (q = 0; q < qcount; q++) pthread_join(sqt[q], 0); 186 | 187 | free(sarg); 188 | free(sqt); 189 | printf("Session %d completed\n", sid); 190 | 191 | return 0; 192 | } 193 | 194 | /** 195 | * Main program. 196 | */ 197 | int main(int argc, char* argv[]) 198 | { 199 | const char* usage = "Usage: %s [OPTION]... PCINAME\n\ 200 | -t THREADS number of thread sessions (default 4)\n\ 201 | -q QCOUNT number of queues per session (default 2)\n\ 202 | -m MAXNLB maximum number of blocks per I/O (default 32)\n\ 203 | PCINAME PCI device name (as 01:00.0[/1] format)"; 204 | 205 | char* prog = strrchr(argv[0], '/'); 206 | prog = prog ? prog + 1 : argv[0]; 207 | 208 | int opt, i; 209 | while ((opt = getopt(argc, argv, "t:q:m:")) != -1) { 210 | switch (opt) { 211 | case 't': 212 | numses = strtol(optarg, 0, 0); 213 | if (numses <= 0) errx(1, "t must be > 0"); 214 | break; 215 | case 'q': 216 | qcount = strtol(optarg, 0, 0); 217 | if (qcount <= 0) errx(1, "t must be > 0"); 218 | break; 219 | case 'm': 220 | maxnlb = strtol(optarg, 0, 0); 221 | if (maxnlb <= 0) errx(1, "m must be > 0"); 222 | break; 223 | default: 224 | warnx(usage, prog); 225 | exit(1); 226 | } 227 | } 228 | if ((optind + 1) != argc) { 229 | warnx(usage, prog); 230 | exit(1); 231 | } 232 | char* pciname = argv[optind]; 233 | 234 | printf("MULTI-SESSION TEST BEGIN\n"); 235 | if (!(ns = unvme_open(pciname))) exit(1); 236 | if ((numses * qcount) > ns->maxqcount) 237 | errx(1, "%d threads %d queues each exceeds limit of %d queues", 238 | numses, qcount, ns->maxqcount); 239 | printf("%s ses=%d qc=%d/%d qs=%d/%d bc=%#lx bs=%d maxnlb=%d/%d\n", 240 | ns->device, numses, qcount, ns->qcount, ns->qsize, ns->maxqsize, 241 | ns->blockcount, ns->blocksize, maxnlb, ns->maxbpio); 242 | 243 | if ((u64)(numses * qcount * ns->qsize * maxnlb) > ns->blockcount) 244 | errx(1, "not enough disk space"); 245 | 246 | sem_init(&sm_ready, 0, 0); 247 | sem_init(&sm_start, 0, 0); 248 | pthread_t* st = calloc(numses * qcount, sizeof(pthread_t)); 249 | 250 | time_t tstart = time(0); 251 | srandom(tstart); 252 | 253 | for (i = 0; i < numses; i++) { 254 | pthread_create(&st[i], 0, test_session, (void*)(long)i); 255 | sem_wait(&sm_ready); 256 | } 257 | for (i = 0; i < numses; i++) sem_post(&sm_start); 258 | for (i = 0; i < numses; i++) pthread_join(st[i], 0); 259 | 260 | sem_destroy(&sm_start); 261 | sem_destroy(&sm_ready); 262 | free(st); 263 | 264 | printf("MULTI-SESSION TEST COMPLETE (%ld secs)\n", time(0) - tstart); 265 | return 0; 266 | } 267 | 268 | -------------------------------------------------------------------------------- /test/unvme/unvme_wrc.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Device read/write utility. 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "unvme.h" 18 | 19 | #define PDEBUG(fmt, arg...) //fprintf(stderr, fmt "\n", ##arg) 20 | 21 | // Global static variables 22 | static const unvme_ns_t* ns; ///< namespace handle 23 | static u32 rw = 0; ///< read-write flag 24 | static u64 startlba = 0; ///< starting LBA 25 | static u64 lbacount = 0; ///< LBA count 26 | static u64 pattern = 0; ///< 64-bit data pattern 27 | static u64 patinc = 0; ///< pattern increment per LBA 28 | static u32 qcount = 16; ///< IO queue count 29 | static u32 qdepth = 64; ///< IO queue depth 30 | static u32 nbpio = 0; ///< number of blocks per IO 31 | static time_t dumptime = 0; ///< interval to display data 32 | static int dump = 0; ///< dump count 33 | static int mismatch = 0; ///< data miscompare flag 34 | static unvme_iod_t* iods; ///< array IO descriptors 35 | static void** iobufs; ///< array of IO buffers 36 | static u64* fixedbuf; ///< fixed data block buffer 37 | 38 | 39 | /* 40 | * Dump buffer content in hex. 41 | */ 42 | static void dumpblock(void* buf, u64 lba) 43 | { 44 | printf("===== LBA 0x%lx =====\n", lba); 45 | u64* p = buf; 46 | u64 w0 = ~*p, w1 = 0, w2 = 0, w3 = 0; 47 | int i, len = ns->blocksize, skip = 0; 48 | for (i = 0; i < len; i += 32) { 49 | if (p[0] != w0 || p[1] != w1 || p[2] != w2 || p[3] != w3) { 50 | printf("%04x: %016lx %016lx %016lx %016lx\n", 51 | i, p[0], p[1], p[2], p[3]); 52 | skip = 0; 53 | } else if (!skip) { 54 | printf("*\n"); 55 | skip = 1; 56 | } 57 | w0 = p[0]; 58 | w1 = p[1]; 59 | w2 = p[2]; 60 | w3 = p[3]; 61 | p += 4; 62 | } 63 | } 64 | 65 | /* 66 | * Submit next IO to a queue. 67 | */ 68 | unvme_iod_t submit(int q, int d, void* buf, u64 lba, u32 nlb) 69 | { 70 | unvme_iod_t iod; 71 | 72 | if (rw == 'w') { 73 | int b, i; 74 | if (patinc) { 75 | u64* pbuf = buf; 76 | int wib = ns->blocksize / sizeof(u64); 77 | for (b = 0; b < nlb; b++) { 78 | u64 p = pattern + ((b + lba - startlba) * patinc); 79 | for (i = 0; i < wib; i++) *pbuf++ = p; 80 | } 81 | } 82 | if (dump) { 83 | void* bbuf = buf; 84 | for (b = 0; b < nlb; b++) { 85 | dumpblock(bbuf, lba + b); 86 | bbuf += ns->blocksize; 87 | if (--dump == 0) break; 88 | } 89 | } 90 | PDEBUG("@W q%d.%d %p %#lx %d", q, d, buf, lba, nlb); 91 | iod = unvme_awrite(ns, q, buf, lba, nlb); 92 | if (!iod) errx(1, "unvme_awrite q=%d lba=%#lx nlb=%#x failed", q, lba, nlb); 93 | } else { 94 | PDEBUG("@R q%d.%d %p %#lx %d", q, d, buf, lba, nlb); 95 | iod = unvme_aread(ns, q, buf, lba, nlb); 96 | if (!iod) errx(1, "unvme_aread q=%d lba=%#lx nlb=%#x failed", q, lba, nlb); 97 | } 98 | return iod; 99 | } 100 | 101 | /* 102 | * Main. 103 | */ 104 | int main(int argc, char** argv) 105 | { 106 | // parsing command options 107 | const char* usage = "Usage: %s [OPTION]... PCINAME\n\ 108 | -w PATTERN write the specified (64-bit) data pattern\n\ 109 | -r PATTERN read and compare against the specified data pattern\n\ 110 | -i PATINC increment data pattern at each LBA (default 0)\n\ 111 | -a LBA starting at LBA (default 0)\n\ 112 | -n COUNT number of blocks to read/write (default to end)\n\ 113 | -q QCOUNT use number of queues for async IO (default 16)\n\ 114 | -d QDEPTH use queue depth for async IO (default 64)\n\ 115 | -m NBPIO use number of blocks per IO (default max support)\n\ 116 | -p INTERVAL print progress with LBA data every INTERVAL seconds\n\ 117 | PCINAME PCI device name (as 01:00.0[/1] format)\n\n\ 118 | either -w or -r must be specified"; 119 | 120 | const char* prog = strrchr(argv[0], '/'); 121 | prog = prog ? prog + 1 : argv[0]; 122 | int opt, b, i; 123 | 124 | while ((opt = getopt(argc, argv, "w:r:i:a:n:q:d:m:p:")) != -1) { 125 | switch (opt) { 126 | case 'w': 127 | case 'r': 128 | rw = opt; 129 | pattern = strtoull(optarg, 0, 0); 130 | break; 131 | case 'i': 132 | patinc = strtoull(optarg, 0, 0); 133 | break; 134 | case 'a': 135 | startlba = strtoull(optarg, 0, 0); 136 | break; 137 | case 'n': 138 | lbacount = strtoull(optarg, 0, 0); 139 | break; 140 | case 'q': 141 | qcount = strtoul(optarg, 0, 0); 142 | break; 143 | case 'd': 144 | qdepth = strtoul(optarg, 0, 0); 145 | break; 146 | case 'm': 147 | nbpio = strtoul(optarg, 0, 0); 148 | break; 149 | case 'p': 150 | dumptime = strtoul(optarg, 0, 0); 151 | dump = 2; 152 | break; 153 | default: 154 | warnx(usage, prog); 155 | exit(1); 156 | } 157 | } 158 | if ((optind + 1) != argc || !rw) { 159 | warnx(usage, prog); 160 | exit(1); 161 | } 162 | char* pciname = argv[optind]; 163 | 164 | // open device and allocate buffer 165 | time_t tstart = time(0); 166 | ns = unvme_open(pciname); 167 | if (!ns) exit(1); 168 | if ((startlba + lbacount) > ns->blockcount) { 169 | unvme_close(ns); 170 | errx(1, "max block count is %#lx", ns->blockcount); 171 | } 172 | if (qcount > ns->qcount || qdepth >= ns->qsize) { 173 | unvme_close(ns); 174 | errx(1, "max qcount=%d qdepth=%d", ns->qcount, ns->qsize-1); 175 | } 176 | if (lbacount == 0) lbacount = ns->blockcount - startlba; 177 | if (nbpio == 0) nbpio = ns->maxbpio; 178 | if (nbpio > ns->maxbpio || (nbpio % ns->nbpp)) { 179 | unvme_close(ns); 180 | errx(1, "invalid nbpio %d", nbpio); 181 | } 182 | 183 | printf("%s qc=%d/%d qd=%d/%d bc=%#lx bs=%d nbpio=%d/%d\n", 184 | ns->device, qcount, ns->qcount, qdepth, ns->qsize-1, 185 | ns->blockcount, ns->blocksize, nbpio, ns->maxbpio); 186 | 187 | int iomax = qcount * qdepth; 188 | iods = calloc(iomax, sizeof(unvme_iod_t)); 189 | iobufs = calloc(iomax, sizeof(void*)); 190 | 191 | int iobufsize = nbpio * ns->blocksize; 192 | for (i = 0; i < iomax; i++) { 193 | iobufs[i] = unvme_alloc(ns, iobufsize); 194 | if (!iobufs[i]) errx(1, "unvme_alloc %#x failed", iobufsize); 195 | } 196 | 197 | int wib = ns->blocksize / sizeof(u64); 198 | if (patinc == 0) { 199 | fixedbuf = malloc(ns->blocksize); 200 | for (i = 0; i < wib; i++) fixedbuf[i] = pattern; 201 | } 202 | 203 | // setup for write and read 204 | if (rw == 'w') { 205 | printf("WRITE lba=%#lx-%#lx pat=%#lx inc=%#lx\n", 206 | startlba, startlba + lbacount - 1, pattern, patinc); 207 | 208 | // if fixed pattern then fill all buffers with the pattern 209 | if (patinc == 0) { 210 | for (i = 0; i < iomax; i++) { 211 | void* buf = iobufs[i]; 212 | for (b = 0; b < nbpio; b++) { 213 | memcpy(buf, fixedbuf, ns->blocksize); 214 | buf += ns->blocksize; 215 | } 216 | } 217 | } 218 | } else { 219 | printf("READ lba=%#lx-%#lx pat=%#lx inc=%#lx\n", 220 | startlba, startlba + lbacount - 1, pattern, patinc); 221 | } 222 | 223 | // submit async IOs until all are completed 224 | u64 submitcount = lbacount; 225 | u64 completecount = lbacount; 226 | u64 nextlba = startlba; 227 | time_t tio = time(0); 228 | time_t dumplast = tio; 229 | int q = 0, d = 0; 230 | 231 | while (completecount > 0) { 232 | int x = q * qdepth + d; 233 | unvme_iod_t iod = iods[x]; 234 | 235 | // check to submit next IO 236 | if (!iod) { 237 | if (submitcount > 0) { 238 | if (!mismatch) { 239 | int nlb = nbpio; 240 | if (nlb > submitcount) nlb = submitcount; 241 | iods[x] = submit(q, d, iobufs[x], nextlba, nlb); 242 | nextlba += nlb; 243 | submitcount -= nlb; 244 | } else { 245 | completecount -= submitcount; 246 | submitcount = 0; 247 | } 248 | } 249 | 250 | // next slot 251 | if (++d >= qdepth) { 252 | d = 0; 253 | if (++q >= qcount) q = 0; 254 | } 255 | continue; 256 | } 257 | 258 | // save iod content as unvme_apoll() will clear it on completion 259 | void* cbuf = iod->buf; 260 | u64 clba = iod->slba; 261 | u32 cnlb = iod->nlb; 262 | 263 | // check IO completion 264 | int stat = unvme_apoll(iod, 0); 265 | if (stat) { 266 | // terminate on error 267 | if (stat != -1) 268 | errx(1, "unvme_apoll error=%#x slba=%#lx nlb=%#x", stat, iod->slba, iod->nlb); 269 | else if ((time(0) - tio) > UNVME_TIMEOUT) 270 | errx(1, "unvme_apoll timeout slba=%#lx nlb=%#x", iod->slba, iod->nlb); 271 | // if no completion go on to next queue 272 | if (++q >= qcount) q = 0; 273 | continue; 274 | } 275 | 276 | // IO completion 277 | PDEBUG("@C q%d.%d %p %#lx %d", q, d, cbuf, clba, cnlb); 278 | completecount -= cnlb; 279 | iods[x] = NULL; 280 | tio = time(0); 281 | if (dumptime && (tio - dumplast) > dumptime) { 282 | dumplast = tio; 283 | dump++; 284 | } 285 | 286 | // compare read result unless (there's already a data mismatch) 287 | if (rw == 'r' && !mismatch) { 288 | // print block contents 289 | if (dump) { 290 | void* bbuf = cbuf; 291 | for (b = 0; b < cnlb; b++) { 292 | dumpblock(bbuf, clba + b); 293 | bbuf += ns->blocksize; 294 | if (--dump == 0) break; 295 | } 296 | } 297 | 298 | // compare read results against data pattern 299 | if (patinc) { 300 | void* bbuf = cbuf; 301 | u64 blba = clba; 302 | for (b = 0; b < cnlb; b++) { 303 | u64 p = pattern + ((blba - startlba) * patinc); 304 | u64* pbuf = bbuf; 305 | for (i = 0; i < wib; i++) { 306 | if (*pbuf != p) { 307 | dumpblock(bbuf, blba); 308 | warnx("ERROR: data mismatch at LBA %#lx " 309 | "offset %#lx exp %#016lx obs %#016lx", 310 | blba, i * sizeof(u64), p, *pbuf); 311 | mismatch++; 312 | break; 313 | } 314 | pbuf++; 315 | } 316 | if (mismatch) break; 317 | bbuf += ns->blocksize; 318 | blba++; 319 | } 320 | } else { 321 | void* bbuf = cbuf; 322 | u64 plba = clba; 323 | for (b = 0; b < cnlb; b++) { 324 | if (memcmp(bbuf, fixedbuf, ns->blocksize)) { 325 | dumpblock(bbuf, plba); 326 | warnx("ERROR: data mismatch at LBA %#lx exp %#016lx", 327 | plba, pattern); 328 | mismatch++; 329 | break; 330 | } 331 | bbuf += ns->blocksize; 332 | plba++; 333 | } 334 | } 335 | } 336 | } 337 | 338 | for (i = 0; i < iomax; i++) unvme_free(ns, iobufs[i]); 339 | free(fixedbuf); 340 | free(iobufs); 341 | free(iods); 342 | unvme_close(ns); 343 | 344 | if (!mismatch) printf("Completion time: %ld seconds\n", time(0) - tstart); 345 | 346 | return mismatch; 347 | } 348 | 349 | -------------------------------------------------------------------------------- /src/unvme_vfio.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-2016, Micron Technology, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * 3. Neither the name of the copyright holder nor the names of its 16 | * contributors may be used to endorse or promote products derived 17 | * from this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | /** 33 | * @file 34 | * @brief VFIO support routines. 35 | */ 36 | 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | 48 | #include "unvme_vfio.h" 49 | #include "unvme_log.h" 50 | 51 | /// Print fatal error and exit 52 | #define FATAL(fmt, arg...) do { ERROR(fmt, ##arg); abort(); } while (0) 53 | 54 | /// Starting device DMA address 55 | #define UIO_BASE 0x40000000 56 | /// Size of UIO buffer/device 57 | #define UIO_SIZE 0x40000000 58 | 59 | /// IRQ index names 60 | const char* vfio_irq_names[] = { "INTX", "MSI", "MSIX", "ERR", "REQ" }; 61 | 62 | 63 | /** 64 | * Read a vfio device. 65 | * @param dev device context 66 | * @param buf buffer to read into 67 | * @param len read size 68 | * @param off offset 69 | */ 70 | static void vfio_read(vfio_device_t* dev, void* buf, size_t len, off_t off) 71 | { 72 | if (pread(dev->fd, buf, len, off) != len) 73 | FATAL("pread(off=%#lx len=%#lx)", off, len); 74 | } 75 | 76 | /** 77 | * Write to vfio device. 78 | * @param dev device context 79 | * @param buf buffer to read into 80 | * @param len read size 81 | * @param off offset 82 | */ 83 | static void vfio_write(vfio_device_t* dev, void* buf, size_t len, off_t off) 84 | { 85 | if (pwrite(dev->fd, buf, len, off) != len) 86 | FATAL("pwrite(off=%#lx len=%#lx)", off, len); 87 | } 88 | 89 | /** 90 | * Allocate VFIO memory. The size will be rounded to page aligned size. 91 | * If pmb is set, it indicates memory has been premapped. 92 | * @param dev device context 93 | * @param size size 94 | * @param pmb premapped buffer 95 | * @return memory structure pointer or NULL if error. 96 | */ 97 | static vfio_mem_t* vfio_mem_alloc(vfio_device_t* dev, size_t size, void* pmb) 98 | { 99 | vfio_mem_t* mem = zalloc(sizeof(*mem)); 100 | mem->size = size; 101 | size_t mask = dev->pagesize - 1; 102 | size = (size + mask) & ~mask; 103 | 104 | if (pmb) { 105 | mem->dma.buf = pmb; 106 | } else { 107 | if (dev->uiobufoff + size > UIO_SIZE) { 108 | ERROR("Out of UIO memory space (next allocation would use %#lx of %#lx)", dev->uiobufoff + size, UIO_SIZE); 109 | free(mem); 110 | return NULL; 111 | } 112 | mem->dma.buf = dev->uiobuf + dev->uiobufoff; 113 | dev->uiobufoff += size; 114 | } 115 | 116 | pthread_mutex_lock(&dev->lock); 117 | mem->dma.size = size; 118 | mem->dma.addr = dev->iovanext; 119 | mem->dma.mem = mem; 120 | mem->dev = dev; 121 | 122 | // add node to the memory list 123 | if (!dev->memlist) { 124 | mem->prev = mem; 125 | mem->next = mem; 126 | dev->memlist = mem; 127 | } else { 128 | mem->prev = dev->memlist->prev; 129 | mem->next = dev->memlist; 130 | dev->memlist->prev->next = mem; 131 | dev->memlist->prev = mem; 132 | } 133 | dev->iovanext += size; 134 | DEBUG_FN("%x %#lx %#lx %#lx %#lx", dev->pci, mem->dma.addr, size, dev->iovanext, dev->uiobufoff); 135 | pthread_mutex_unlock(&dev->lock); 136 | 137 | return mem; 138 | } 139 | 140 | /** 141 | * Free up VFIO memory. 142 | * @param mem memory pointer 143 | * @return 0 if ok else -1. 144 | */ 145 | int vfio_mem_free(vfio_mem_t* mem) 146 | { 147 | vfio_device_t* dev = mem->dev; 148 | 149 | // remove node from memory list 150 | pthread_mutex_lock(&dev->lock); 151 | if (mem->next == dev->memlist) { // If removing last item in list 152 | dev->iovanext -= mem->dma.size; 153 | dev->uiobufoff -= mem->dma.size; 154 | } 155 | if (mem->next == mem) { // If removing only item in list 156 | dev->memlist = NULL; 157 | dev->iovanext = dev->iovabase; 158 | dev->uiobufoff = 0; 159 | } else { // If there are other items in list 160 | mem->next->prev = mem->prev; 161 | mem->prev->next = mem->next; 162 | if (dev->memlist == mem) { // If first item in list 163 | dev->memlist = mem->next; 164 | } 165 | dev->iovanext = dev->memlist->prev->dma.addr + dev->memlist->prev->dma.size; // IOVA next is after last item in list 166 | dev->uiobufoff = dev->iovanext - dev->iovabase; // UIO buffer offset is same as IOVA offset 167 | } 168 | DEBUG_FN("%x %#lx %#lx %#lx %#lx", dev->pci, mem->dma.addr, mem->dma.size, dev->iovanext, dev->uiobufoff); 169 | pthread_mutex_unlock(&dev->lock); 170 | 171 | free(mem); 172 | return 0; 173 | } 174 | 175 | /** 176 | * Map a premapped buffer and return a DMA buffer. 177 | * @param dev device context 178 | * @param size allocation size 179 | * @param pmb premapped buffer 180 | * @return 0 if ok else -1. 181 | */ 182 | vfio_dma_t* vfio_dma_map(vfio_device_t* dev, size_t size, void* pmb) 183 | { 184 | vfio_mem_t* mem = vfio_mem_alloc(dev, size, pmb); 185 | return mem ? &mem->dma : NULL; 186 | } 187 | 188 | /** 189 | * Free a DMA buffer (without unmapping dma->buf). 190 | * @param dma memory pointer 191 | * @return 0 if ok else -1. 192 | */ 193 | int vfio_dma_unmap(vfio_dma_t* dma) 194 | { 195 | return vfio_mem_free(dma->mem); 196 | } 197 | 198 | /** 199 | * Allocate and return a DMA buffer. 200 | * @param dev device context 201 | * @param size allocation size 202 | * @return 0 if ok else -1. 203 | */ 204 | vfio_dma_t* vfio_dma_alloc(vfio_device_t* dev, size_t size) 205 | { 206 | vfio_mem_t* mem = vfio_mem_alloc(dev, size, 0); 207 | return mem ? &mem->dma : NULL; 208 | } 209 | 210 | /** 211 | * Free a DMA buffer. 212 | * @param dma memory pointer 213 | * @return 0 if ok else -1. 214 | */ 215 | int vfio_dma_free(vfio_dma_t* dma) 216 | { 217 | return vfio_mem_free(dma->mem); 218 | } 219 | 220 | /** 221 | * Enable MSIX and map interrupt vectors to VFIO events. 222 | * @param dev device context 223 | * @param start first vector 224 | * @param count number of vectors to enable 225 | * @param efds event file descriptors 226 | */ 227 | void vfio_msix_enable(vfio_device_t* dev, int start, int count, __s32* efds) 228 | { 229 | DEBUG_FN("%x start=%d count=%d", dev->pci, start, count); 230 | 231 | if (dev->msixsize == 0) 232 | FATAL("no MSIX support"); 233 | if ((start + count) > dev->msixsize) 234 | FATAL("MSIX request %d exceeds limit %d", count, dev->msixsize); 235 | if (dev->msixnvec) 236 | FATAL("MSIX is already enabled"); 237 | 238 | // if first time register all vectors else register specified vectors 239 | int len = sizeof(struct vfio_irq_set) + (count * sizeof(__s32)); 240 | struct vfio_irq_set* irqs = zalloc(len); 241 | memcpy(irqs->data, efds, count * sizeof(__s32)); 242 | irqs->argsz = len; 243 | irqs->index = VFIO_PCI_MSIX_IRQ_INDEX; 244 | irqs->flags = VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_TRIGGER; 245 | irqs->start = start; 246 | irqs->count = count; 247 | HEX_DUMP(irqs, len); 248 | 249 | if (ioctl(dev->fd, VFIO_DEVICE_SET_IRQS, irqs)) 250 | FATAL("VFIO_DEVICE_SET_IRQS %d %d: %s", start, count, strerror(errno)); 251 | 252 | dev->msixnvec += count; 253 | free(irqs); 254 | } 255 | 256 | /** 257 | * Disable MSIX interrupt. 258 | * @param dev device context 259 | */ 260 | void vfio_msix_disable(vfio_device_t* dev) 261 | { 262 | if (dev->msixnvec == 0) return; 263 | 264 | struct vfio_irq_set irq_set = { 265 | .argsz = sizeof(irq_set), 266 | .flags = VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_ACTION_TRIGGER, 267 | .index = VFIO_PCI_MSIX_IRQ_INDEX, 268 | .start = 0, 269 | .count = 0, 270 | }; 271 | if (ioctl(dev->fd, VFIO_DEVICE_SET_IRQS, &irq_set)) 272 | FATAL("VFIO_DEVICE_SET_IRQS 0 0: %s", strerror(errno)); 273 | 274 | dev->msixnvec = 0; 275 | } 276 | 277 | /** 278 | * Create a VFIO device context. 279 | * @param dev if NULL then allocate context 280 | * @param pci PCI device id (as %x:%x.%x format) 281 | * @return device context or NULL if failure. 282 | */ 283 | vfio_device_t* vfio_create(vfio_device_t* dev, int pci) 284 | { 285 | // map PCI to vfio device number 286 | int i; 287 | char pciname[64]; 288 | sprintf(pciname, "0000:%02x:%02x.%x", pci >> 16, (pci >> 8) & 0xff, pci & 0xff); 289 | 290 | char path[128]; 291 | sprintf(path, "/sys/bus/pci/devices/%s/iommu_group", pciname); 292 | if ((i = readlink(path, path, sizeof(path))) < 0) 293 | FATAL("No iommu_group associated with device %s", pciname); 294 | path[i] = 0; 295 | sprintf(path, "/dev/vfio/noiommu-%s", &strrchr(path, '/')[1]); 296 | 297 | struct vfio_group_status group_status = { .argsz = sizeof(group_status) }; 298 | struct vfio_device_info dev_info = { .argsz = sizeof(dev_info) }; 299 | 300 | // allocate and initialize device context 301 | if (!dev) dev = zalloc(sizeof(*dev)); 302 | else dev->ext = 1; 303 | dev->pci = pci; 304 | dev->pagesize = sysconf(_SC_PAGESIZE); 305 | dev->iovabase = UIO_BASE; 306 | dev->iovanext = dev->iovabase; 307 | if (pthread_mutex_init(&dev->lock, 0)) return NULL; 308 | 309 | // map vfio context 310 | if ((dev->contfd = open("/dev/vfio/vfio", O_RDWR)) < 0) 311 | FATAL("open /dev/vfio/vfio"); 312 | 313 | if (ioctl(dev->contfd, VFIO_GET_API_VERSION) != VFIO_API_VERSION) 314 | FATAL("ioctl VFIO_GET_API_VERSION"); 315 | 316 | if (ioctl(dev->contfd, VFIO_CHECK_EXTENSION, VFIO_NOIOMMU_IOMMU) == 0) 317 | FATAL("ioctl VFIO_CHECK_EXTENSION"); 318 | 319 | if ((dev->groupfd = open(path, O_RDWR)) < 0) 320 | FATAL("open %s failed", path); 321 | 322 | if (ioctl(dev->groupfd, VFIO_GROUP_GET_STATUS, &group_status) < 0) 323 | FATAL("ioctl VFIO_GROUP_GET_STATUS"); 324 | 325 | if (!(group_status.flags & VFIO_GROUP_FLAGS_VIABLE)) 326 | FATAL("group not viable %#x", group_status.flags); 327 | 328 | if (ioctl(dev->groupfd, VFIO_GROUP_SET_CONTAINER, &dev->contfd) < 0) 329 | FATAL("ioctl VFIO_GROUP_SET_CONTAINER"); 330 | 331 | if (ioctl(dev->contfd, VFIO_SET_IOMMU, VFIO_NOIOMMU_IOMMU) < 0) 332 | FATAL("ioctl VFIO_SET_IOMMU"); 333 | 334 | dev->fd = ioctl(dev->groupfd, VFIO_GROUP_GET_DEVICE_FD, pciname); 335 | if (dev->fd < 0) 336 | FATAL("ioctl VFIO_GROUP_GET_DEVICE_FD"); 337 | 338 | if (ioctl(dev->fd, VFIO_DEVICE_GET_INFO, &dev_info) < 0) 339 | FATAL("ioctl VFIO_DEVICE_GET_INFO"); 340 | 341 | DEBUG_FN("%x flags=%u regions=%u irqs=%u", 342 | pci, dev_info.flags, dev_info.num_regions, dev_info.num_irqs); 343 | 344 | for (i = 0; i < dev_info.num_regions; i++) { 345 | struct vfio_region_info reg = { .argsz = sizeof(reg), .index = i }; 346 | 347 | if (ioctl(dev->fd, VFIO_DEVICE_GET_REGION_INFO, ®)) continue; 348 | 349 | DEBUG_FN("%x region=%d flags=%#x off=%#llx size=%#llx", 350 | pci, reg.index, reg.flags, reg.offset, reg.size); 351 | 352 | if (i == VFIO_PCI_CONFIG_REGION_INDEX) { 353 | __u8 config[256]; 354 | vfio_read(dev, config, sizeof(config), reg.offset); 355 | HEX_DUMP(config, sizeof(config)); 356 | 357 | __u16* vendor = (__u16*)(config + PCI_VENDOR_ID); 358 | __u16* cmd = (__u16*)(config + PCI_COMMAND); 359 | 360 | if (*vendor == 0xffff) 361 | FATAL("device in bad state"); 362 | 363 | *cmd |= PCI_COMMAND_MASTER|PCI_COMMAND_MEMORY|PCI_COMMAND_INTX_DISABLE; 364 | vfio_write(dev, cmd, sizeof(*cmd), reg.offset + PCI_COMMAND); 365 | vfio_read(dev, cmd, sizeof(*cmd), reg.offset + PCI_COMMAND); 366 | 367 | // read MSIX table size 368 | __u8 cap = config[PCI_CAPABILITY_LIST]; 369 | while (cap) { 370 | if (config[cap] == PCI_CAP_ID_MSIX) { 371 | __u16* msixflags = (__u16*)(config + cap + PCI_MSIX_FLAGS); 372 | dev->msixsize = (*msixflags & PCI_MSIX_FLAGS_QSIZE) + 1; 373 | break; 374 | } 375 | cap = config[cap+1]; 376 | } 377 | 378 | DEBUG_FN("%x vendor=%#x cmd=%#x msix=%d device=%#x rev=%d", 379 | pci, *vendor, *cmd, dev->msixsize, 380 | (__u16*)(config + PCI_DEVICE_ID), config[PCI_REVISION_ID]); 381 | } 382 | } 383 | 384 | for (i = 0; i < dev_info.num_irqs; i++) { 385 | struct vfio_irq_info irq = { .argsz = sizeof(irq), .index = i }; 386 | 387 | if (ioctl(dev->fd, VFIO_DEVICE_GET_IRQ_INFO, &irq)) continue; 388 | DEBUG_FN("%x irq=%s count=%d flags=%#x", 389 | pci, vfio_irq_names[i], irq.count, irq.flags); 390 | if (i == VFIO_PCI_MSIX_IRQ_INDEX && irq.count != dev->msixsize) 391 | FATAL("VFIO_DEVICE_GET_IRQ_INFO MSIX count %d != %d", irq.count, dev->msixsize); 392 | } 393 | 394 | // Open and map UIO device as memory buffer 395 | dev->uiofd = open("/dev/uio0", O_RDWR | O_SYNC); 396 | if (dev->uiofd == -1) 397 | FATAL("unable to open /dev/uio0, %d", errno); 398 | dev->uiobuf = mmap(NULL, UIO_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, dev->uiofd, 0); 399 | if (dev->uiobuf == MAP_FAILED) 400 | FATAL("unable to mmap /dev/uio0, %d", errno); 401 | 402 | if (mlock(dev->uiobuf, UIO_SIZE) == -1) 403 | FATAL("unable to mlock, %d", errno); 404 | 405 | return (vfio_device_t*)dev; 406 | } 407 | 408 | /** 409 | * Delete a VFIO device context. 410 | * @param dev device context 411 | */ 412 | void vfio_delete(vfio_device_t* dev) 413 | { 414 | if (!dev) return; 415 | DEBUG_FN("%x", dev->pci); 416 | 417 | // Close and unmap UIO buffer 418 | munlock(dev->uiobuf, UIO_SIZE); 419 | munmap(dev->uiobuf, UIO_SIZE); 420 | close(dev->uiofd); 421 | 422 | // free all memory associated with the device 423 | while (dev->memlist) vfio_mem_free(dev->memlist); 424 | 425 | if (dev->fd) { 426 | close(dev->fd); 427 | dev->fd = 0; 428 | } 429 | if (dev->contfd) { 430 | close(dev->contfd); 431 | dev->contfd = 0; 432 | } 433 | if (dev->groupfd) { 434 | close(dev->groupfd); 435 | dev->groupfd = 0; 436 | } 437 | 438 | pthread_mutex_destroy(&dev->lock); 439 | if (!dev->ext) free(dev); 440 | } 441 | --------------------------------------------------------------------------------