├── CMakeLists.txt ├── LICENSE.txt ├── README.md ├── core ├── admin.c ├── cmd_args.c ├── core-exec.c ├── core-init.c ├── lightnvm.c ├── nvme_ctrl.c ├── nvmef_ctrl.c ├── ox-memory.c ├── ox-mq-output.c ├── ox-mq.c ├── ox-stats.c └── ox_cmdline.c ├── ftl ├── ftl_common.c ├── lnvm │ ├── lnvm_bbtbl.c │ ├── lnvm_ftl.c │ └── lnvm_ftl.h └── ox-app │ ├── app-cache.c │ ├── app-channels.c │ ├── app-core.c │ ├── app-hmap.c │ ├── app-transaction.c │ ├── block │ ├── non-recoverable │ │ ├── oxb-blkmd-nr.c │ │ └── oxb-chmap-nr.c │ ├── oxb-bbtbl.c │ ├── oxb-blk-md.c │ ├── oxb-ch-map.c │ ├── oxb-ch-prov.c │ ├── oxb-gc.c │ ├── oxb-gl-map.c │ ├── oxb-gl-prov.c │ ├── oxb-lba-io.c │ ├── oxb-log.c │ ├── oxb-ppa-io.c │ └── oxb-recovery.c │ └── ox-app.h ├── fuse ├── fuse-bigfile.c └── fuse-ox-nvme.c ├── host ├── nvme-host-dev.h ├── nvme-host.h └── nvme_host.c ├── include ├── libox.h ├── nvme.h ├── nvmef.h ├── ox-cmdline.h ├── ox-lightnvm.h ├── ox-mq.h └── ox-uatomic.h ├── mmgr ├── file-be │ ├── file-be.c │ └── file-be.h ├── fpga-3.0.1 │ ├── fpga-3_0_1.c │ ├── fpga-3_0_1.h │ ├── nand_dma.c │ └── nand_dma.h ├── mmgr_common.c ├── ocssd-1.2 │ ├── ocssd-1_2.c │ └── ocssd-1_2.h └── volt │ ├── volt.c │ └── volt.h ├── parser ├── fabrics_parser.c ├── lnvm_parser.c └── nvme_parser.c ├── targets ├── ox-ctrl-nvme-filebe.c ├── ox-ctrl-nvme-ocssd.c └── ox-ctrl-nvme-volt.c ├── test ├── test-connect.c ├── test-nvme-rw.c ├── test-nvme-thput-r.c ├── test-nvme-thput-w.c ├── test-ox-mq.c └── test-queue.c └── transport ├── ox-fabrics ├── ox-fabrics.c ├── ox-fabrics.h ├── ox-host.c ├── tcp-client.c ├── tcp-server.c ├── udp-client.c └── udp-server.c └── pci_fpga_3.0.1 ├── pci_fpga_301.c └── pci_fpga_301.h /README.md: -------------------------------------------------------------------------------- 1 | # OX: Computational Storage SSD Controller 2 | 3 | OX is (I) a computational storage solution for high-performance applications, (II) a framework for application-specific FTL development, (III) an NVMe controller that exposes open-channel SSDs as a block device via OX-Block FTL, and (IV) a set of libraries that integrate NVMe over Fabrics and Computational Storage with host applications. 4 | 5 | ``` 6 | RELEASE 7 | 8 | OX 2.7: This release introduces a new media manager (File Backend), enabling the use of files to simulate 9 | flash memory with up to 4 TiB of storage. Additionally, OX 2.7 now supports a FUSE-based file system, 10 | allowing OX media to be mounted as a standard file system. We've also fixed several bugs to enhance 11 | stability and performance :) 12 | 13 | OLD RELEASES 14 | OX 2.6: This release introduces the fully featured FTL OX-Block, which includes a 4KB-granularity 15 | mapping table, garbage collection, write-ahead logging, checkpointing, and recovery. Additionally, 16 | OX 2.6 debuts our NVMe over Fabrics implementation using TCP sockets, with future versions planned 17 | to support RDMA/Infiniband. Another key addition is computational storage support, providing libraries 18 | and interfaces for offloading applications directly to storage devices running OX. OX 2.6 is compatible 19 | with DFC, Broadcom Stingray (ARM platforms), and x86 machines. 20 | 21 | OX 1.4 : The main feature is supporting pblk target (host FTL) by enabling 22 | sector-granularity reads. It allows file systems on top of the DFC namespace. It requires 23 | the linux kernel 4.13 or higher. Please, check the 'pblk: Host-side FTL setup' section. 24 | ``` 25 | 26 | **Documentation:** 27 | - https://github.com/DFC-OpenSource/ox-ctrl/wiki 28 | - [OX PhD Thesis by Ivan Picoli](https://itu.dk/research/dfc-data/IvanPicoli-PhDThesis-08-Jul-19-final.pdf) 29 | 30 | **Publications:** 31 | 32 | [- uFLIP-OC: Understanding Flash I/O Patterns on Open-Channel Solid-State Drives](https://dl.acm.org/citation.cfm?id=3124680.3124741), ApSys '17 33 | 34 | [- Improving CPU I/O Performance via SSD Controller FTL Support for Batched Writes](https://dl.acm.org/citation.cfm?id=3329925), DaMoN '19 35 | 36 | [- LSM Management on Computational Storage](https://dl.acm.org/citation.cfm?id=3329927), DaMoN '19 37 | 38 | [- Programming Storage Controllers with OX](http://nvmw.ucsd.edu/nvmw2019-program/unzip/current/nvmw2019-final54.pdf), NVMW '19 39 | 40 | [- Open-Channel SSD (What is it Good For)](http://cidrdb.org/cidr2020/papers/p17-picoli-cidr20.pdf), CIDR'20 41 | 42 | OX was developed under a project at IT University of Copenhagen. Further work was done for BwTree computational storage at Microsoft Research. Currently, LSM-Tree computational storage techniques are under development at ITU Copenhagen. 43 | 44 | Detailed information can be found here (https://github.com/DFC-OpenSource/ox-ctrl/wiki), latest releases here (https://github.com/DFC-OpenSource/ox-ctrl/releases) and code under development here (https://github.com/DFC-OpenSource/ox-ctrl/tree/for-next). 45 | 46 | 47 | # Installation: 48 | 49 | ``` 50 | Possible Ubuntu packages: 51 | $ sudo apt-get install cmake libreadline6 libreadline6-dev libfuse-dev 52 | 53 | Install OX: 54 | $ git clone https://github.com/DFC-OpenSource/ox-ctrl.git 55 | $ cd ox-ctrl 56 | $ mkdir build 57 | $ cd build 58 | $ mkdir disk 59 | $ cmake -DFILE_FOLDER=./disk -DFILE_GB=<256, 512, 1024, 1536, 2048, 3072, 4096> .. (If you are using File backend, set -DFILE_GB accordingly) 60 | OR 61 | $ cmake -DVOLT_GB=<4,8,16,32,64> .. (If you are using DRAM backend, set -DVOLT_GB accordingly) 62 | $ sudo make install 63 | ``` 64 | 65 | # Localhost Testing: 66 | 67 | ``` 68 | - Open 2 terminals. 69 | First terminal: 70 | $ cd /build 71 | $ ./ox-ctrl-nvme-filebe start 72 | OR 73 | $ ./ox-ctrl-nvme-volt start 74 | Wait until you see OX startup 75 | Try some commands: 76 | > help 77 | > show memory 78 | > show io 79 | > show gc 80 | > show cp 81 | > show mq status 82 | > exit (if you want to close OX) 83 | Second terminal: 84 | $ cd /build 85 | Write something to your OX device: 86 | (Here you are writing BLKS 4K-blocks starting from block SLBA) 87 | $ ./ox-test-nvme-thput-w 88 | (Here you are writing 1000000 4K-blocks starting from block 1) 89 | $ ./ox-test-nvme-thput-w 1 1000000 90 | Read what you wrote: 91 | $ ./ox-test-nvme-thput-r 1 1000000 92 | 93 | You can type 'show io' in the first terminal while running the tests for runtime statistics. 94 | 95 | ``` 96 | 97 | # Internal components: 98 | 99 | ``` 100 | - Open-channel SSD v1.2 media manager 101 | - DRAM media manager (VOLT) 102 | - File BE Media Manager (FILE-BE) 103 | - FUSE File System support 104 | - LightNVM FTL support for raw open-channel SSD access 105 | - OX-App Application-specific FTL Framework 106 | - OX-Block full-fledged FTL (Block device) 107 | - NVMe over PCI controller 108 | - NVMe over Fabrics controller 109 | - TCP-based fabrics implementation 110 | - OX NVMe over fabrics user-space library 111 | - OX-Block NVMe Computational Storage user-space library 112 | ``` 113 | 114 | # OX command line 115 | 116 | $ ox-ctrl-nvme-volt 117 | ``` 118 | Start OX in a specific mode. Commands are listed with --help, as shown below. 119 | ``` 120 | $ ox-ctrl-nvme-volt --help 121 | ``` 122 | *** OX Controller 2.6 - The Ultimate Dragon *** 123 | 124 | The DFC Open-Channel SSD Controller 125 | 126 | Available commands: 127 | start Start controller with standard configuration 128 | debug Start controller and print Admin/IO commands 129 | reset Start controller and reset FTL 130 | WARNING (reset): ALL DATA WILL BE LOST 131 | admin Execute specific tasks within the controller 132 | 133 | Initial release developed by Ivan L. Picoli 134 | 135 | 136 | -?, --help Give this help list 137 | --usage Give a short usage message 138 | -V, --version Print program version 139 | 140 | Report bugs to Ivan L. Picoli . 141 | 142 | ``` 143 | The same commands apply for VOLT media menager (ox-ctrl-volt). 144 | 145 | $ ox-ctrl-nvme-volt admin --help 146 | ``` 147 | Use this command to run specific tasks within the controller. 148 | 149 | Examples: 150 | Show all available Admin tasks: 151 | ox-ctrl admin -l 152 | 153 | Run a specific admin task: 154 | ox-ctrl admin -t 155 | 156 | -l, --list[=list] Show available admin tasks. 157 | -t, --task=admin_task Admin task to be executed. 158 | -?, --help Give this help list 159 | --usage Give a short usage message 160 | -V, --version Print program version 161 | 162 | Mandatory or optional arguments to long options are also mandatory or optional 163 | for any corresponding short options. 164 | 165 | Report bugs to Ivan L. Picoli . 166 | ``` 167 | 168 | $ ox-ctrl-nvme-volt admin -l 169 | ``` 170 | OX Controller ADMIN 171 | Available OX Admin Tasks: 172 | 173 | - 'erase-blk': erase specific blocks. 174 | eg. ox-ctrl admin -t erase-blk 175 | 176 | - 'create-bbt': create or update the bad block table. 177 | eg. ox-ctrl admin -t create-bbt (see below) 178 | ``` 179 | 180 | # Bad block table creation 181 | The bad block table is an essential component of a flash block device, without it you can expect data corruption due 182 | application bad block allocation. OX has an admin command for creating per-channel bad block tables. Please follow the instruction in 'ox-ctrl-test admin -t create-bbt' command. 183 | 184 | When attempting to create a bad block table in a given channel, OX will ask which process you want to use. There are 3 available processes: 185 | ``` 186 | 1 - Full scan (Erase, write full, read full, compare buffers) - RECOMMENDED. Might take a long time 187 | - Backup the channel data before. ALL DATA WILL BE LOST. 188 | 189 | 2 - Fast scan (Only erase the blocks) - Fast creation, but can have omitted bad blocks. 190 | - Backup the channel data before. ALL DATA WILL BE LOST. 191 | 192 | 3 - Emergency table (Creates an empty bad block table without erasing blocks) - Very fast and the channel is not erased. 193 | - If you can't make a backup of your channel, use this option for initializing OX, however, all blocks will be 194 |              marked as good. You should use the DFC in read-only state if you create an emergency bad block table. 195 | ``` 196 | -------------------------------------------------------------------------------- /core/admin.c: -------------------------------------------------------------------------------- 1 | /* OX: Open-Channel NVM Express SSD Controller 2 | * 3 | * - OX Administration Module 4 | * 5 | * Copyright 2016 IT University of Copenhagen 6 | * 7 | * Written by Ivan Luiz Picoli 8 | * 9 | * Licensed under the Apache License, Version 2.0 (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * http://www.apache.org/licenses/LICENSE-2.0 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | extern struct core_struct core; 29 | 30 | static void oxadmin_show_all () 31 | { 32 | printf(" \nAvailable OX Admin Tasks: \n"); 33 | printf("\n - 'erase-blk': erase specific blocks."); 34 | printf("\n eg. ox-ctrl admin -t erase-blk\n"); 35 | printf("\n - 'create-bbt': create or update the bad block table."); 36 | printf("\n eg. ox-ctrl admin -t create-bbt\n\n"); 37 | } 38 | 39 | static int oxadmin_confirm_erase (int n, struct nvm_ppa_addr *ppa) 40 | { 41 | char choice; 42 | printf("\n You asked for erasing %d blocks.\n", n); 43 | printf(" ppa: ch: %d, lun %d, blk: %d\n",ppa->g.ch, ppa->g.lun, ppa->g.blk); 44 | printf(" Are you sure? (Y or N): "); 45 | scanf (" %c", &choice); 46 | 47 | if (choice == 0x59 || choice == 0x79) 48 | return 0; 49 | 50 | return -1; 51 | } 52 | 53 | static int oxadmin_erase_blk (struct nvm_channel *ch, struct nvm_ppa_addr *ppa) 54 | { 55 | int ret, pl; 56 | struct nvm_mmgr_io_cmd *cmd = ox_malloc(sizeof(struct nvm_mmgr_io_cmd), 57 | OX_MEM_ADMIN); 58 | if (!cmd) 59 | return EMEM; 60 | 61 | for (pl = 0; pl < ch->geometry->n_of_planes; pl++) { 62 | memset (cmd, 0, sizeof (struct nvm_mmgr_io_cmd)); 63 | cmd->ppa.ppa = ppa->ppa; 64 | cmd->ppa.g.pl = pl; 65 | cmd->ppa.g.ch = ch->ch_mmgr_id; 66 | cmd->ppa.g.pg = 0; 67 | 68 | ret = ox_submit_sync_io (ch, cmd, NULL, MMGR_ERASE_BLK); 69 | if (ret) 70 | break; 71 | } 72 | ox_free(cmd, OX_MEM_ADMIN); 73 | 74 | return ret; 75 | } 76 | 77 | static int oxadmin_erase_blk_task () 78 | { 79 | int ch_i, lun, blk, ret; 80 | struct nvm_channel *ch; 81 | struct nvm_ppa_addr ppa; 82 | 83 | printf("\n Specific block erasing."); 84 | printf("\n Specify the channel: "); 85 | scanf("%d", &ch_i); 86 | printf(" Specify the LUN: "); 87 | scanf("%d", &lun); 88 | printf(" Specify the block: "); 89 | scanf("%d", &blk); 90 | 91 | if (ch_i < 0 || ch_i >= core.nvm_ch_count) 92 | goto ERR; 93 | 94 | ch = core.nvm_ch[ch_i]; 95 | 96 | if (lun < 0 || lun > ch->geometry->lun_per_ch - 1 || 97 | blk < 0 || blk > ch->geometry->blk_per_lun) 98 | goto ERR; 99 | 100 | ppa.g.ch = ch->ch_mmgr_id; 101 | ppa.g.lun = lun; 102 | ppa.g.blk = blk; 103 | 104 | if (oxadmin_confirm_erase (1, &ppa)) { 105 | printf("\n Erase CANCELED.\n"); 106 | return -1; 107 | } 108 | 109 | printf("\n Erasing...\n"); 110 | 111 | ret = oxadmin_erase_blk (ch, &ppa); 112 | if (ret) { 113 | printf(" Erase FAILED.\n"); 114 | return -1; 115 | } 116 | 117 | printf(" 1 block erased succesfully.\n"); 118 | printf(" [ch: %d, lun: %d, blk: %d]\n\n",ch_i,lun,blk); 119 | 120 | return 0; 121 | ERR: 122 | printf(" Block address out of bounds.\n"); 123 | return -1; 124 | } 125 | 126 | static int oxadmin_create_bbt (int type, struct nvm_channel *ch) 127 | { 128 | struct lnvm_channel *lch; 129 | struct lnvm_bbtbl *bbt; 130 | uint32_t tblks; 131 | int n_pl, ret; 132 | 133 | n_pl = ch->geometry->n_of_planes; 134 | 135 | lch = ox_malloc (sizeof(struct lnvm_channel), OX_MEM_ADMIN); 136 | if (!lch) 137 | return -1; 138 | 139 | tblks = ch->geometry->blk_per_lun * ch->geometry->lun_per_ch * n_pl; 140 | 141 | lch->ch = ch; 142 | 143 | lch->bbtbl = ox_malloc (sizeof(struct lnvm_bbtbl), OX_MEM_ADMIN); 144 | if (!lch->bbtbl) 145 | goto FREE_LCH; 146 | 147 | bbt = lch->bbtbl; 148 | bbt->tbl = ox_malloc (sizeof(uint8_t) * tblks, OX_MEM_ADMIN); 149 | if (!bbt->tbl) 150 | goto FREE_BBTBL; 151 | 152 | memset (bbt->tbl, 0, tblks); 153 | bbt->magic = 0; 154 | bbt->bb_sz = tblks; 155 | 156 | printf("\n [lnvm: Channel %d. Creating bad block table...]", ch->ch_id); 157 | ret = lnvm_bbt_create (lch, bbt, type); 158 | if (ret) goto ERR; 159 | ret = lnvm_flush_bbt (lch); 160 | 161 | ERR: 162 | ox_free(bbt->tbl, OX_MEM_ADMIN); 163 | FREE_BBTBL: 164 | ox_free(bbt, OX_MEM_ADMIN); 165 | FREE_LCH: 166 | ox_free(lch, OX_MEM_ADMIN); 167 | 168 | return ret; 169 | } 170 | 171 | static int oxadmin_confirm_bbt_creation (int type, int ch_i) 172 | { 173 | char choice; 174 | 175 | if (ch_i == core.nvm_ch_count) 176 | printf("\n You are about to create/update the bad block tables on all " 177 | "channels 0-%d.\n", core.nvm_ch_count); 178 | else 179 | printf("\n You are about to create/update the bad block table on " 180 | "channel %d.\n", ch_i); 181 | printf("\n Creation type: "); 182 | switch (type) { 183 | case LNVM_BBT_FULL: 184 | printf ("Full scan (*** ALL DATA WILL BE LOST! *** " 185 | "FULL SCAN MIGHT TAKE A LONG TIME)\n"); 186 | break; 187 | case LNVM_BBT_ERASE: 188 | printf ("Fast scan (*** ALL DATA WILL BE LOST ***)\n"); 189 | break; 190 | case LNVM_BBT_EMERGENCY: 191 | printf ("Emergency table (ALL BLOCKS WILL BE SET AS GOOD)\n"); 192 | break; 193 | default: 194 | return -1; 195 | } 196 | 197 | printf("\n Are you sure? (Y or N): "); 198 | scanf (" %c", &choice); 199 | 200 | if (choice == 0x59 || choice == 0x79) 201 | return 0; 202 | 203 | return -1; 204 | } 205 | 206 | static int oxadmin_create_bbt_task () 207 | { 208 | int type, ch_i, i; 209 | struct nvm_channel *ch; 210 | 211 | printf("\n Bad Block Table creation/update."); 212 | printf("\n Bad block creation types: "); 213 | printf("\n 1 - Full scan (Erase, write full, read full, compare buffers)"); 214 | printf("\n 2 - Fast scan (Only erase the blocks)"); 215 | printf("\n 3 - Emergency table (Creates an empty bad block " 216 | "table without erasing blocks)\n"); 217 | printf("\n Specify the type: "); 218 | scanf("%d", &type); 219 | 220 | if (type < 1 || type > 3) 221 | goto ERR_T; 222 | 223 | printf(" Specify the channel (0-%d or %d for all): ",core.nvm_ch_count - 1, 224 | core.nvm_ch_count); 225 | scanf("%d", &ch_i); 226 | 227 | if (ch_i < 0 || ch_i > core.nvm_ch_count) 228 | goto ERR_CH; 229 | 230 | switch (type) { 231 | case 1: 232 | type = LNVM_BBT_FULL; 233 | break; 234 | case 2: 235 | type = LNVM_BBT_ERASE; 236 | break; 237 | case 3: 238 | type = LNVM_BBT_EMERGENCY; 239 | break; 240 | default: 241 | goto ERR_T; 242 | } 243 | 244 | if (oxadmin_confirm_bbt_creation (type, ch_i)) 245 | goto ERR_CONF; 246 | 247 | i = 0; 248 | while (i < core.nvm_ch_count) { 249 | 250 | if (ch_i != core.nvm_ch_count) 251 | i = ch_i; 252 | 253 | ch = core.nvm_ch[i]; 254 | if (oxadmin_create_bbt (type, ch)) 255 | goto ERR_CR; 256 | 257 | printf ("\n Bad block table SUCCESFULLY created on channel %d.\n", i); 258 | i = (ch_i != core.nvm_ch_count) ? core.nvm_ch_count : i + 1; 259 | } 260 | 261 | return 0; 262 | 263 | ERR_T: 264 | printf("\n Incorrect bad block creation type.\n"); 265 | return -1; 266 | ERR_CH: 267 | printf("\n Channel out of bounds.\n"); 268 | return -1; 269 | ERR_CONF: 270 | printf("\n Bad block table creation CANCELED.\n"); 271 | return -1; 272 | ERR_CR: 273 | printf("\n Bad block table creation FAILED.\n"); 274 | return -1; 275 | } 276 | 277 | int ox_admin_init (struct nvm_init_arg *args) { 278 | printf("\n OX Controller ADMIN\n"); 279 | 280 | if (!ox_mem_create_type ("ADMIN", OX_MEM_ADMIN)) 281 | return -1; 282 | 283 | /* List all available admin tasks */ 284 | if (args->arg_flag & CMDARG_FLAG_L) { 285 | oxadmin_show_all (); 286 | return 0; 287 | } 288 | 289 | /* Run a specific admin task */ 290 | if (args->arg_flag & CMDARG_FLAG_T) { 291 | if (!args->admin_task || strlen(args->admin_task) > CMDARG_LEN) { 292 | printf(" ! Wrong admin task name.\n"); 293 | return -1; 294 | } 295 | if (strcmp(args->admin_task, "erase-blk") == 0) 296 | return oxadmin_erase_blk_task (); 297 | else if (strcmp(args->admin_task, "create-bbt") == 0) { 298 | if (ox_get_ftl_instance (FTL_ID_LNVM)) { 299 | return oxadmin_create_bbt_task (); 300 | } else { 301 | printf (" ! LightNVM FTL is not compiled."); 302 | return -1; 303 | } 304 | } else 305 | printf (" ! Admin task does not exist.\n"); 306 | } 307 | 308 | return -1; 309 | } -------------------------------------------------------------------------------- /core/cmd_args.c: -------------------------------------------------------------------------------- 1 | /* OX: Open-Channel NVM Express SSD Controller 2 | * 3 | * - OX Controller argument parser 4 | * 5 | * Copyright 2016 IT University of Copenhagen 6 | * 7 | * Written by Ivan Luiz Picoli 8 | * 9 | * Licensed under the Apache License, Version 2.0 (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * http://www.apache.org/licenses/LICENSE-2.0 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | extern struct core_struct core; 29 | const char *argp_program_version = OX_LABEL; 30 | const char *argp_program_bug_address = "Ivan L. Picoli "; 31 | 32 | static char doc_global[] = "\n*** OX Controller " OX_VER " - " LABEL " ***\n" 33 | " \n The DFC Open-Channel SSD Controller\n\n" 34 | " Available commands:\n" 35 | " start Start controller with standard configuration\n" 36 | " debug Start controller and print Admin/IO commands\n" 37 | " reset Start controller and reset FTL\n" 38 | " WARNING (reset): ALL DATA WILL BE LOST\n" 39 | " admin Execute specific tasks within the controller\n" 40 | " \n Initial release developed by Ivan L. Picoli \n\n"; 41 | 42 | static char doc_admin[] = 43 | "\nUse this command to run specific tasks within the controller.\n" 44 | "\n Examples:" 45 | "\n Show all available Admin tasks:" 46 | "\n ox-ctrl admin -l\n" 47 | "\n Run a specific admin task:" 48 | "\n ox-ctrl admin -t "; 49 | 50 | static struct argp_option opt_admin[] = { 51 | {"list", 'l', "list", OPTION_ARG_OPTIONAL,"Show available admin tasks."}, 52 | {"task", 't', "admin_task", 0, "Admin task to be executed. "}, 53 | {0} 54 | }; 55 | 56 | static error_t parse_opt_admin(int key, char *arg, struct argp_state *state) 57 | { 58 | struct nvm_init_arg *args = state->input; 59 | 60 | switch (key) { 61 | case 't': 62 | if (!arg || strlen(arg) == 0 || strlen(arg) > CMDARG_LEN) 63 | argp_usage(state); 64 | strcpy(args->admin_task,arg); 65 | args->arg_num++; 66 | args->arg_flag |= CMDARG_FLAG_T; 67 | break; 68 | case 'l': 69 | args->arg_num++; 70 | args->arg_flag |= CMDARG_FLAG_L; 71 | break; 72 | case ARGP_KEY_END: 73 | if (args->arg_num > 1 || args->arg_num == 0) 74 | argp_usage(state); 75 | break; 76 | case ARGP_KEY_ARG: 77 | case ARGP_KEY_NO_ARGS: 78 | case ARGP_KEY_ERROR: 79 | case ARGP_KEY_SUCCESS: 80 | case ARGP_KEY_FINI: 81 | case ARGP_KEY_INIT: 82 | break; 83 | default: 84 | return ARGP_ERR_UNKNOWN; 85 | } 86 | 87 | return 0; 88 | } 89 | 90 | static void cmd_prepare(struct argp_state *state, struct nvm_init_arg *args, 91 | char *cmd, struct argp *argp_cmd) 92 | { 93 | /* Remove the first arg from the parser */ 94 | int argc = state->argc - state->next + 1; 95 | char** argv = &state->argv[state->next - 1]; 96 | char* argv0 = argv[0]; 97 | 98 | argv[0] = ox_malloc(strlen(state->name) + strlen(cmd) + 2, OX_MEM_CMD_ARG); 99 | if(!argv[0]) 100 | argp_failure(state, 1, ENOMEM, 0); 101 | 102 | sprintf(argv[0], "%s %s", state->name, cmd); 103 | 104 | argp_parse(argp_cmd, argc, argv, ARGP_IN_ORDER, &argc, args); 105 | 106 | ox_free(argv[0], OX_MEM_CMD_ARG); 107 | argv[0] = argv0; 108 | state->next += argc - 1; 109 | } 110 | 111 | static struct argp argp_admin = {opt_admin, parse_opt_admin, 0, doc_admin}; 112 | 113 | error_t parse_opt (int key, char *arg, struct argp_state *state) 114 | { 115 | struct nvm_init_arg *args = state->input; 116 | 117 | switch(key) 118 | { 119 | case ARGP_KEY_ARG: 120 | if (strcmp(arg, "start") == 0) 121 | args->cmdtype = CMDARG_START; 122 | else if (strcmp(arg, "debug") == 0) 123 | args->cmdtype = CMDARG_DEBUG; 124 | else if (strcmp(arg, "reset") == 0) 125 | args->cmdtype = CMDARG_RESET; 126 | else if (strcmp(arg, "admin") == 0) { 127 | args->cmdtype = CMDARG_ADMIN; 128 | cmd_prepare(state, args, "admin", &argp_admin); 129 | } 130 | break; 131 | default: 132 | return ARGP_ERR_UNKNOWN; 133 | } 134 | return 0; 135 | } 136 | 137 | void ox_cmdarg_exit (void) 138 | { 139 | ox_free (core.args_global, OX_MEM_CMD_ARG); 140 | } 141 | 142 | static struct argp argp_global={NULL, parse_opt,"ox-ctrl [ [cmd-options]]", 143 | doc_global}; 144 | 145 | int ox_cmdarg_init (int argc, char **argv) 146 | { 147 | if (!ox_mem_create_type ("CMD_ARG", OX_MEM_CMD_ARG)) 148 | return -1; 149 | 150 | core.args_global = ox_malloc (sizeof (struct nvm_init_arg), OX_MEM_CMD_ARG); 151 | if (!core.args_global) 152 | return -1; 153 | 154 | memset (core.args_global, 0, sizeof(struct nvm_init_arg)); 155 | 156 | argp_parse(&argp_global, argc, argv, ARGP_IN_ORDER, NULL, core.args_global); 157 | 158 | switch (core.args_global->cmdtype) 159 | { 160 | case CMDARG_START: 161 | return OX_RUN_MODE; 162 | case CMDARG_DEBUG: 163 | core.debug |= 1 << 0; 164 | return OX_RUN_MODE; 165 | case CMDARG_RESET: 166 | core.reset |= 1 << 0; 167 | return OX_RUN_MODE; 168 | case CMDARG_ADMIN: 169 | if (core.args_global->arg_flag & CMDARG_FLAG_L) 170 | core.nostart = 1; 171 | return OX_ADMIN_MODE; 172 | default: 173 | printf("Invalid command, please use --help to see more info.\n"); 174 | } 175 | 176 | ox_free (core.args_global, OX_MEM_CMD_ARG); 177 | return -1; 178 | } -------------------------------------------------------------------------------- /core/lightnvm.c: -------------------------------------------------------------------------------- 1 | /* OX: Open-Channel NVM Express SSD Controller 2 | * 3 | * - LightNVM NVMe Extension 4 | * 5 | * Copyright 2016 IT University of Copenhagen 6 | * 7 | * Written by Ivan Luiz Picoli 8 | * 9 | * Licensed under the Apache License, Version 2.0 (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * http://www.apache.org/licenses/LICENSE-2.0 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | extern struct core_struct core; 29 | 30 | uint8_t lnvm_dev(NvmeCtrl *n) 31 | { 32 | return (n->lightnvm_ctrl.id_ctrl.ver_id != 0); 33 | } 34 | 35 | void lnvm_set_default(LnvmCtrl *ctrl) 36 | { 37 | struct nvm_mmgr_geometry *g = core.nvm_ch[0]->geometry; 38 | ctrl->id_ctrl.ver_id = LNVM_VER_ID; 39 | ctrl->id_ctrl.dom = LNVM_DOM; 40 | ctrl->id_ctrl.cap = LNVM_CAP; 41 | ctrl->params.sec_size = g->sec_size; 42 | ctrl->params.secs_per_pg = g->sec_per_pg; 43 | ctrl->params.pgs_per_blk = g->pg_per_blk; 44 | ctrl->params.max_sec_per_rq = LNVM_MAX_SEC_RQ; 45 | ctrl->params.mtype = LNVM_MTYPE; 46 | ctrl->params.fmtype = LNVM_FMTYPE; 47 | ctrl->params.num_ch = g->n_of_ch; 48 | ctrl->params.num_lun = g->lun_per_ch; 49 | ctrl->params.num_blk = g->blk_per_lun; 50 | ctrl->params.num_pln = g->n_of_planes; 51 | ctrl->bb_gen_freq = LNVM_BB_GEN_FREQ; 52 | ctrl->err_write = LNVM_ERR_WRITE; 53 | } 54 | 55 | void lnvm_init_id_ctrl (LnvmIdCtrl *ln_id) 56 | { 57 | struct LnvmIdAddrFormat *ppaf = &ln_id->ppaf; 58 | struct nvm_mmgr_geometry *g = core.nvm_ch[0]->geometry; 59 | 60 | ln_id->ver_id = LNVM_VER_ID; 61 | ln_id->vmnt = LNVM_VMNT; 62 | ln_id->cgrps = LNVM_CGRPS; 63 | ln_id->cap = LNVM_CAP; 64 | ln_id->dom = LNVM_DOM; 65 | 66 | ppaf->sect_len = (uint8_t) log2 (g->sec_per_pg); 67 | ppaf->pln_len = (uint8_t) log2 (g->n_of_planes); 68 | ppaf->ch_len = (uint8_t) log2 (g->n_of_ch); 69 | ppaf->lun_len = (uint8_t) log2 (g->lun_per_ch); 70 | ppaf->pg_len = (uint8_t) log2 (g->pg_per_blk); 71 | ppaf->blk_len = (uint8_t) log2 (g->blk_per_lun); 72 | 73 | ppaf->sect_offset = 0; 74 | ppaf->pln_offset += ppaf->sect_len; 75 | ppaf->ch_offset += ppaf->pln_offset + ppaf->pln_len; 76 | ppaf->lun_offset += ppaf->ch_offset + ppaf->ch_len; 77 | ppaf->pg_offset += ppaf->lun_offset + ppaf->lun_len; 78 | ppaf->blk_offset += ppaf->pg_offset + ppaf->pg_len; 79 | } 80 | 81 | void lightnvm_exit(NvmeCtrl *n) 82 | { 83 | 84 | } 85 | 86 | int lnvm_init(NvmeCtrl *n) 87 | { 88 | LnvmCtrl *ln; 89 | LnvmIdGroup *c; 90 | unsigned int i, cid; 91 | uint64_t tot_blks = 0, rsv_blks = 0; 92 | 93 | ln = &n->lightnvm_ctrl; 94 | 95 | if (ln->params.mtype != 0) 96 | log_info(" [lnvm: Only NAND Flash Mem supported at the moment]\n"); 97 | if ((ln->params.num_pln > 4)) 98 | log_info(" [lnvm: Only quad plane mode supported]\n"); 99 | 100 | for (i = 0; i < n->num_namespaces; i++) { 101 | 102 | /* For now we export 1 channel containing all LUNs */ 103 | for (cid = 0; cid < core.nvm_ch_count; cid++){ 104 | tot_blks += core.nvm_ch[cid]->geometry->blk_per_lun * 105 | (core.nvm_ch[cid]->geometry->lun_per_ch & 0xffff); 106 | rsv_blks += core.nvm_ch[cid]->mmgr_rsv; 107 | rsv_blks += core.nvm_ch[cid]->ftl_rsv; 108 | } 109 | 110 | c = &ln->id_ctrl.groups[0]; 111 | c->mtype = ln->params.mtype; 112 | c->fmtype = ln->params.fmtype; 113 | c->num_ch = ln->params.num_ch; 114 | c->num_lun = ln->params.num_lun; 115 | c->num_pln = ln->params.num_pln; 116 | c->num_blk = ln->params.num_blk; 117 | c->num_pg = ln->params.pgs_per_blk; 118 | c->csecs = ln->params.sec_size; 119 | c->fpg_sz = ln->params.sec_size * ln->params.secs_per_pg; 120 | c->sos = core.nvm_ch[0]->geometry->sec_oob_sz; 121 | c->trdt = LNVM_TRDT; 122 | c->trdm = LNVM_TRDM; 123 | c->tprt = LNVM_TPRT; 124 | c->tprm = LNVM_TPRM; 125 | c->tbet = LNVM_TBET; 126 | c->tbem = LNVM_TBEM; 127 | 128 | switch(c->num_pln) { 129 | case 1: 130 | c->mpos = 0x10101; /* single plane */ 131 | break; 132 | case 2: 133 | c->mpos = 0x20202; /* dual plane */ 134 | break; 135 | case 4: 136 | c->mpos = 0x40404; /* quad plane */ 137 | break; 138 | default: 139 | log_info(" [lnvm: Invalid plane mode]\n"); 140 | return -1; 141 | } 142 | 143 | c->cpar = 0; 144 | c->mccap = 1; 145 | 146 | /* calculated values */ 147 | ln->params.sec_per_phys_pl = ln->params.secs_per_pg * c->num_pln; 148 | ln->params.sec_per_blk= ln->params.secs_per_pg * ln->params.pgs_per_blk; 149 | ln->params.sec_per_lun = ln->params.sec_per_blk * c->num_blk; 150 | ln->params.sec_per_log_pl = ln->params.sec_per_lun * c->num_lun; 151 | ln->params.total_secs = ln->params.sec_per_log_pl; 152 | 153 | log_info(" [lnvm: Namespace %d]\n",i); 154 | log_info(" [lnvm: Channels: %d]\n",c->num_ch); 155 | log_info(" [lnvm: LUNs per Channel: %d]\n",c->num_lun); 156 | log_info(" [lnvm: Blocks per LUN: %d]\n",c->num_blk); 157 | log_info(" [lnvm: Pages per Block: %d]\n",c->num_pg); 158 | log_info(" [lnvm: Planes: %d]\n",c->num_pln); 159 | log_info(" [lnvm: Total Blocks: %lu]\n", tot_blks); 160 | log_info(" [lnvm: Total Pages: %lu]\n",c->num_pg * tot_blks 161 | * c->num_pln); 162 | log_info(" [lnvm: Page size: %d bytes]\n",c->fpg_sz); 163 | log_info(" [lnvm: Plane Page size: %d bytes]\n",c->fpg_sz 164 | * c->num_pln); 165 | log_info(" [lnvm: Total: %lu MB]\n",(((c->fpg_sz & 0xffffffff) 166 | / 1024) * c->num_pg * c->num_pln * tot_blks) / 1024); 167 | log_info(" [lnvm: Total Available: %lu MB]\n", 168 | (((c->fpg_sz & 0xffffffff) / 1024) * c->num_pg * c->num_pln * 169 | (tot_blks - rsv_blks)) / 1024); 170 | } 171 | 172 | log_info (" [nvm: LightNVM is registered]\n"); 173 | 174 | return 0; 175 | } -------------------------------------------------------------------------------- /core/ox-memory.c: -------------------------------------------------------------------------------- 1 | /* OX: Open-Channel NVM Express SSD Controller 2 | * 3 | * - OX Memory Control 4 | * 5 | * Copyright 2018 IT University of Copenhagen 6 | * 7 | * Written by Ivan Luiz Picoli 8 | * 9 | * Licensed under the Apache License, Version 2.0 (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * http://www.apache.org/licenses/LICENSE-2.0 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | * 21 | */ 22 | 23 | #ifndef __USE_GNU 24 | #define __USE_GNU 25 | #endif 26 | 27 | #ifndef _GNU_SOURCE 28 | #define _GNU_SOURCE 29 | #endif 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | #define OX_MEM_MAX_TYPES 64 40 | 41 | #define OX_MEM_MALLOC 0 42 | #define OX_MEM_CALLOC 1 43 | 44 | struct ox_mem_node { 45 | void *ptr; 46 | uint64_t size; 47 | }; 48 | 49 | struct ox_mem_type { 50 | char name[32]; 51 | uint16_t id; 52 | volatile uint64_t allocd; 53 | pthread_spinlock_t alloc_spin; 54 | TAILQ_ENTRY(ox_mem_type) entry; 55 | 56 | /* RB tree */ 57 | void *root; 58 | }; 59 | 60 | struct ox_memory { 61 | struct ox_mem_type *types[OX_MEM_MAX_TYPES]; 62 | TAILQ_HEAD(types_list, ox_mem_type) types_head; 63 | }; 64 | 65 | static struct ox_memory ox_mem; 66 | 67 | static void ox_mem_btree_free_act (void *nodep) 68 | { 69 | struct ox_mem_node *datap = (struct ox_mem_node *) nodep; 70 | 71 | log_info ("[mem: Leak detected. ptr: %p, size %lu bytes]", 72 | datap->ptr, datap->size); 73 | free (datap->ptr); 74 | free (datap); 75 | } 76 | 77 | #if OX_MEM_MANAGER 78 | static int ox_mem_btree_compare(const void *new_ptr, const void *tree_ptr) 79 | { 80 | struct ox_mem_node *tree_node = (struct ox_mem_node *) tree_ptr; 81 | struct ox_mem_node *new_node = (struct ox_mem_node *) new_ptr; 82 | 83 | if ( new_node->ptr < tree_node->ptr ) 84 | return -1; 85 | if ( new_node->ptr > tree_node->ptr ) 86 | return 1; 87 | return 0; 88 | } 89 | 90 | static void *ox_alloc (size_t size, uint16_t type, uint8_t fn) 91 | { 92 | void *ptr; 93 | struct ox_mem_node *node; 94 | 95 | if (type >= OX_MEM_MAX_TYPES || !ox_mem.types[type]) 96 | return NULL; 97 | 98 | ptr = (fn == OX_MEM_CALLOC) ? calloc (1, size) : malloc (size); 99 | if (!ptr) 100 | return NULL; 101 | 102 | node = malloc (sizeof (struct ox_mem_node)); 103 | if (!node) { 104 | free (ptr); 105 | return NULL; 106 | } 107 | 108 | node->ptr = ptr; 109 | node->size = size; 110 | 111 | pthread_spin_lock (&ox_mem.types[type]->alloc_spin); 112 | ox_mem.types[type]->allocd += size; 113 | tsearch((void *) node, &ox_mem.types[type]->root, ox_mem_btree_compare); 114 | pthread_spin_unlock (&ox_mem.types[type]->alloc_spin); 115 | 116 | return ptr; 117 | } 118 | 119 | void *ox_calloc (size_t members, size_t size, uint16_t type) 120 | { 121 | return ox_alloc (members * size, type, OX_MEM_CALLOC); 122 | } 123 | 124 | void *ox_malloc (size_t size, uint16_t type) 125 | { 126 | return ox_alloc (size, type, OX_MEM_MALLOC); 127 | } 128 | 129 | void *ox_realloc (void *ptr, size_t size, uint16_t type) 130 | { 131 | struct ox_mem_node **ret; 132 | struct ox_mem_node *node; 133 | void *new; 134 | 135 | if (type >= OX_MEM_MAX_TYPES || !ox_mem.types[type]) 136 | return NULL; 137 | 138 | if (!ptr) 139 | return ox_alloc (size, type, OX_MEM_MALLOC); 140 | 141 | pthread_spin_lock (&ox_mem.types[type]->alloc_spin); 142 | 143 | ret = (struct ox_mem_node **) tfind(&ptr, &ox_mem.types[type]->root, 144 | ox_mem_btree_compare); 145 | if (!ret) { 146 | log_err ("[mem: Realloc pointer not found. ptr: %p, type %d]", ptr, type); 147 | pthread_spin_unlock (&ox_mem.types[type]->alloc_spin); 148 | return NULL; 149 | } 150 | 151 | new = realloc (ptr, size); 152 | if (!new) 153 | return NULL; 154 | 155 | node = *ret; 156 | tdelete(node, &ox_mem.types[type]->root, ox_mem_btree_compare); 157 | node->ptr = new; 158 | tsearch((void *) node, &ox_mem.types[type]->root, ox_mem_btree_compare); 159 | 160 | ox_mem.types[type]->allocd += size - node->size; 161 | node->size = size; 162 | 163 | pthread_spin_unlock (&ox_mem.types[type]->alloc_spin); 164 | 165 | return new; 166 | } 167 | 168 | void *ox_free (void *ptr, uint16_t type) 169 | { 170 | struct ox_mem_node **ret; 171 | struct ox_mem_node *node; 172 | 173 | if (type >= OX_MEM_MAX_TYPES || !ox_mem.types[type]) 174 | return ptr; 175 | 176 | pthread_spin_lock (&ox_mem.types[type]->alloc_spin); 177 | 178 | ret = (struct ox_mem_node **) tfind(&ptr, &ox_mem.types[type]->root, 179 | ox_mem_btree_compare); 180 | if (!ret) { 181 | log_err ("[mem: Double free detected. ptr: %p, type %d]", ptr, type); 182 | pthread_spin_unlock (&ox_mem.types[type]->alloc_spin); 183 | return ptr; 184 | } 185 | 186 | node = *ret; 187 | ox_mem.types[type]->allocd -= node->size; 188 | 189 | free (node->ptr); 190 | 191 | tdelete(node, &ox_mem.types[type]->root, ox_mem_btree_compare); 192 | 193 | free (node); 194 | 195 | pthread_spin_unlock (&ox_mem.types[type]->alloc_spin); 196 | 197 | return NULL; 198 | } 199 | #endif 200 | 201 | struct ox_mem_type *ox_mem_create_type (const char *name, uint16_t type) 202 | { 203 | struct ox_mem_type *memtype; 204 | 205 | if (strlen(name) > 32) { 206 | log_err ("[mem: Type name is too big. max of 32 bytes]"); 207 | return NULL; 208 | } 209 | 210 | if (type >= OX_MEM_MAX_TYPES) { 211 | log_err ("[mem: Invalid Type ID: %d]", type); 212 | return NULL; 213 | } 214 | 215 | if (ox_mem.types[type]) { 216 | log_err ("[mem: Memory Type already created: %d]", type); 217 | return NULL; 218 | } 219 | 220 | memtype = malloc (sizeof (struct ox_mem_type)); 221 | if (!memtype) 222 | return NULL; 223 | 224 | memtype->id = type; 225 | memcpy (memtype->name, name, strlen(name) + 1); 226 | 227 | memtype->allocd = 0; 228 | memtype->root = NULL; 229 | 230 | if (pthread_spin_init (&memtype->alloc_spin, 0)) { 231 | free (memtype); 232 | return NULL; 233 | } 234 | 235 | TAILQ_INSERT_TAIL (&ox_mem.types_head, memtype, entry); 236 | ox_mem.types[type] = memtype; 237 | 238 | return memtype; 239 | } 240 | 241 | static void ox_mem_destroy_type (struct ox_mem_type *type) 242 | { 243 | if (!type || !ox_mem.types[type->id]) 244 | return; 245 | 246 | if (type->allocd) { 247 | log_info ("[mem: Leak was found in %s]", type->name); 248 | //printf ("\n[mem: Leak was found in %s. Check the log.]\n", type->name); 249 | tdestroy (ox_mem.types[type->id]->root,ox_mem_btree_free_act); 250 | type->allocd = 0; 251 | } 252 | 253 | TAILQ_REMOVE (&ox_mem.types_head, type, entry); 254 | ox_mem.types[type->id] = NULL; 255 | pthread_spin_destroy (&type->alloc_spin); 256 | free (type); 257 | } 258 | 259 | uint64_t ox_mem_size (uint16_t type) 260 | { 261 | if (type >= OX_MEM_MAX_TYPES) 262 | return 0; 263 | 264 | return ox_mem.types[type]->allocd; 265 | } 266 | 267 | uint64_t ox_mem_total (void) 268 | { 269 | struct ox_mem_type *memtype; 270 | uint64_t total = 0; 271 | 272 | TAILQ_FOREACH (memtype, &ox_mem.types_head, entry) { 273 | total += ox_mem_size (memtype->id); 274 | } 275 | 276 | return total; 277 | } 278 | 279 | void ox_mem_print_memory (void) 280 | { 281 | struct ox_mem_type *memtype; 282 | uint64_t total = 0; 283 | 284 | TAILQ_FOREACH (memtype, &ox_mem.types_head, entry) { 285 | printf (" %2d - %-18s: %10.5lf MB (%lu bytes)\n", memtype->id, memtype->name, 286 | (double) memtype->allocd / (double) 1024 / (double) 1024, 287 | memtype->allocd); 288 | total += ox_mem_size (memtype->id); 289 | } 290 | printf ("\n Total: %.5lf MB (%lu bytes)\n", 291 | (double) total / (double) 1024 / (double) 1024, total); 292 | } 293 | 294 | void ox_mem_exit (void) 295 | { 296 | struct ox_mem_type *memtype; 297 | 298 | while (!TAILQ_EMPTY(&ox_mem.types_head)) { 299 | memtype = TAILQ_FIRST(&ox_mem.types_head); 300 | ox_mem_destroy_type (memtype); 301 | } 302 | 303 | log_info ("[ox: Memory Management stopped.]"); 304 | } 305 | 306 | int ox_mem_init (void) 307 | { 308 | if (!TAILQ_EMPTY(&ox_mem.types_head)) 309 | return 0; 310 | 311 | TAILQ_INIT (&ox_mem.types_head); 312 | memset (ox_mem.types, 0x0, OX_MEM_MAX_TYPES * sizeof(struct ox_mem_type *)); 313 | 314 | log_info ("[ox: Memory Management started.]"); 315 | 316 | return 0; 317 | } -------------------------------------------------------------------------------- /core/ox-mq-output.c: -------------------------------------------------------------------------------- 1 | /* OX: Open-Channel NVM Express SSD Controller 2 | * 3 | * - Multi-Queue Support for Parallel I/O - Output data and statistics 4 | * 5 | * Copyright 2018 IT University of Copenhagen 6 | * 7 | * Written by Ivan Luiz Picoli 8 | * 9 | * Licensed under the Apache License, Version 2.0 (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * http://www.apache.org/licenses/LICENSE-2.0 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #define OXMQ_OUTPUT_BUF_SZ 8192 36 | 37 | struct oxmq_output *ox_mq_output_init (uint64_t id, const char *name, 38 | uint32_t nodes) 39 | { 40 | FILE *fp; 41 | char filename[80]; 42 | struct stat st = {0}; 43 | struct oxmq_output *output; 44 | uint32_t node_i; 45 | 46 | if (strlen(name) > 40) 47 | return NULL; 48 | 49 | output = ox_malloc (sizeof (struct oxmq_output), OX_MEM_OX_MQ); 50 | if (!output) 51 | return NULL; 52 | 53 | output->id = id; 54 | output->nodes = nodes; 55 | strcpy (output->name, name); 56 | 57 | if (stat("output", &st) == -1) 58 | mkdir("output", S_IRWXO); 59 | 60 | sprintf (filename, "output/%lu_%s.csv", id, name); 61 | fp = fopen(filename, "a"); 62 | if (!fp) 63 | goto FREE_OUT; 64 | 65 | fprintf (fp, "node_sequence;node_id;lba;channel;lun;block;page;" 66 | "plane;sector;start;end;latency;type;is_failed;read_memcmp;bytes\n"); 67 | 68 | fclose(fp); 69 | 70 | output->node_seq = ox_calloc (sizeof(uint64_t), nodes, OX_MEM_OX_MQ); 71 | if (!output->node_seq) 72 | goto FREE_OUT; 73 | 74 | output->queues = ox_malloc (sizeof (struct oxmq_output_tq) * nodes, 75 | OX_MEM_OX_MQ); 76 | if (!output->queues) 77 | goto FREE_NODE; 78 | 79 | for (node_i = 0; node_i < nodes; node_i++) { 80 | output->queues[node_i].rows = ox_malloc (sizeof (struct oxmq_output_row) * 81 | OXMQ_OUTPUT_BUF_SZ, OX_MEM_OX_MQ); 82 | if (!output->queues[node_i].rows) 83 | goto FREE_QUEUE; 84 | output->queues[node_i].row_off = 0; 85 | TAILQ_INIT (&output->queues[node_i].out_head); 86 | } 87 | 88 | if (pthread_mutex_init (&output->file_mutex, 0)) 89 | goto FREE_QUEUE; 90 | 91 | output->sequence = 0; 92 | 93 | return output; 94 | 95 | FREE_QUEUE: 96 | while (node_i) { 97 | node_i--; 98 | ox_free (output->queues[node_i].rows, OX_MEM_OX_MQ); 99 | } 100 | ox_free (output->queues, OX_MEM_OX_MQ); 101 | FREE_NODE: 102 | ox_free (output->node_seq, OX_MEM_OX_MQ); 103 | FREE_OUT: 104 | ox_free (output, OX_MEM_OX_MQ); 105 | return NULL; 106 | } 107 | 108 | void ox_mq_output_exit (struct oxmq_output *output) 109 | { 110 | uint32_t node_i; 111 | 112 | pthread_mutex_destroy (&output->file_mutex); 113 | 114 | for (node_i = 0; node_i < output->nodes; node_i++) 115 | ox_free (output->queues[node_i].rows, OX_MEM_OX_MQ); 116 | 117 | ox_free (output->queues, OX_MEM_OX_MQ); 118 | ox_free (output->node_seq, OX_MEM_OX_MQ); 119 | ox_free (output, OX_MEM_OX_MQ); 120 | } 121 | 122 | static int ox_mq_output_flush_node (struct oxmq_output *output, int node_i) 123 | { 124 | struct oxmq_output_row *row; 125 | char tstart[21], tend[21]; 126 | uint32_t row_i; 127 | FILE *fp; 128 | char filename[80]; 129 | 130 | sprintf (filename, "output/%lu_%s.csv", output->id, output->name); 131 | fp = fopen(filename, "a"); 132 | if (!fp) { 133 | printf (" [ox-mq: ERROR. File not opened.]\n"); 134 | return -1; 135 | } 136 | 137 | for (row_i = 0; row_i < output->queues[node_i].row_off; row_i++) { 138 | 139 | row = &output->queues[node_i].rows[row_i]; 140 | if (!row) { 141 | fclose(fp); 142 | return -1; 143 | } 144 | 145 | row->ulat = (row->tend - row->tstart) / 1000; 146 | sprintf (tstart, "%lu", row->tstart); 147 | sprintf (tend, "%lu", row->tend); 148 | memmove (tstart, tstart+4, 17); 149 | memmove (tend, tend+4, 17); 150 | 151 | if(fprintf (fp, 152 | "%lu;" 153 | "%d;" 154 | "%lu;" 155 | "%d;" 156 | "%d;" 157 | "%d;" 158 | "%d;" 159 | "%d;" 160 | "%d;" 161 | "%s;" 162 | "%s;" 163 | "%lu;" 164 | "%c;" 165 | "%d;" 166 | "%d;" 167 | "%d\n", 168 | row->node_seq, 169 | row->node_id, 170 | row->lba, 171 | row->ch, 172 | row->lun, 173 | row->blk, 174 | row->pg, 175 | row->pl, 176 | row->sec, 177 | tstart, 178 | tend, 179 | row->ulat, 180 | row->type, 181 | row->failed, 182 | row->datacmp, 183 | row->size) < 0) { 184 | printf (" [ox-mq: ERROR. Not possible flushing results.]\n"); 185 | fclose(fp); 186 | return -1; 187 | } 188 | } 189 | 190 | fclose(fp); 191 | return 0; 192 | } 193 | 194 | void ox_mq_output_flush (struct oxmq_output *output) 195 | { 196 | uint32_t node_i; 197 | 198 | pthread_mutex_lock (&output->file_mutex); 199 | 200 | for (node_i = 0; node_i < output->nodes; node_i++) 201 | ox_mq_output_flush_node (output, node_i); 202 | 203 | pthread_mutex_unlock (&output->file_mutex); 204 | } 205 | 206 | struct oxmq_output_row *ox_mq_output_new (struct oxmq_output *output, 207 | int node_id) 208 | { 209 | struct oxmq_output_row *row; 210 | 211 | if (output->queues[node_id].row_off >= OXMQ_OUTPUT_BUF_SZ) { 212 | pthread_mutex_lock (&output->file_mutex); 213 | if (!output->queues[node_id].row_off) { 214 | pthread_mutex_unlock (&output->file_mutex); 215 | goto GET; 216 | } 217 | 218 | ox_mq_output_flush_node (output, node_id); 219 | memset (output->queues[node_id].rows, 0x0, 220 | sizeof (struct oxmq_output_row) * OXMQ_OUTPUT_BUF_SZ); 221 | output->queues[node_id].row_off = 0; 222 | 223 | pthread_mutex_unlock (&output->file_mutex); 224 | } 225 | 226 | GET: 227 | row = &output->queues[node_id].rows[output->queues[node_id].row_off]; 228 | output->queues[node_id].row_off++; 229 | 230 | row->node_id = node_id; 231 | row->node_seq = output->node_seq[node_id]; 232 | output->node_seq[node_id]++; 233 | 234 | return row; 235 | } -------------------------------------------------------------------------------- /ftl/lnvm/lnvm_ftl.h: -------------------------------------------------------------------------------- 1 | /* OX: Open-Channel NVM Express SSD Controller 2 | * 3 | * - LightNVM Flash Translation Layer (header) 4 | * 5 | * Copyright 2017 IT University of Copenhagen 6 | * 7 | * Written by Ivan Luiz Picoli 8 | * 9 | * Licensed under the Apache License, Version 2.0 (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * http://www.apache.org/licenses/LICENSE-2.0 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | */ 21 | 22 | #ifndef FTL_LNVM_H 23 | #define FTL_LNVM_H 24 | 25 | #include 26 | #include 27 | 28 | #define FTL_LNVM_IO_RETRY 0 29 | #define FTL_LNVM_RSV_BLK 1 30 | #define FTL_LNVM_RSV_BLK_COUNT 1 31 | #define FTL_LNVM_MAGIC 0x3c 32 | 33 | enum { 34 | FTL_PGMAP_OFF = 0, 35 | FTL_PGMAP_ON = 1 36 | }; 37 | 38 | #define LNVM_BBT_EMERGENCY 0x0 // Creates the bbt without erasing the channel 39 | #define LNVM_BBT_ERASE 0x1 // Checks for bad blocks only erasing the block 40 | #define LNVM_BBT_FULL 0x2 // Checks for bad blocks erasing the block, 41 | // writing and reading all pages, 42 | // and comparing the buffers 43 | 44 | struct lnvm_page { 45 | 46 | }; 47 | 48 | struct lnvm_bbtbl { 49 | uint32_t rsvd; 50 | uint32_t magic; 51 | uint32_t bb_sz; 52 | uint32_t bb_count; 53 | /* This struct is stored on NVM up to this point, *tbl is not stored */ 54 | uint8_t *tbl; 55 | } __attribute__((packed)); 56 | 57 | struct lnvm_channel { 58 | struct nvm_channel *ch; 59 | struct lnvm_bbtbl *bbtbl; 60 | LIST_ENTRY(lnvm_channel) entry; 61 | }; 62 | 63 | int lnvm_get_bbt_nvm (struct lnvm_channel *); 64 | int lnvm_bbt_create (struct lnvm_channel *, struct lnvm_bbtbl *, uint8_t); 65 | int lnvm_flush_bbt (struct lnvm_channel *); 66 | 67 | #endif /* FTL_LNVM_H */ -------------------------------------------------------------------------------- /ftl/ox-app/app-cache.c: -------------------------------------------------------------------------------- 1 | /* OX: Open-Channel NVM Express SSD Controller 2 | * 3 | * - OX-App Caching Framework 4 | * 5 | * Copyright 2018 IT University of Copenhagen 6 | * 7 | * Written by Ivan Luiz Picoli 8 | * 9 | * Licensed under the Apache License, Version 2.0 (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * http://www.apache.org/licenses/LICENSE-2.0 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #define APP_CACHE_ADDR_FLAG ((1 & AND64) << 63) 29 | 30 | static struct app_channel **ch; 31 | extern uint16_t app_nch; 32 | 33 | /* The caching strategy ensures the entry size matches with the NVM pg size */ 34 | static uint64_t map_ent_per_pg; 35 | 36 | static int app_cache_evict_pg (struct app_cache *cache) 37 | { 38 | struct nvm_ppa_addr old_ppa; 39 | struct app_cache_entry *cache_ent; 40 | 41 | cache_ent = TAILQ_FIRST(&cache->mbu_head); 42 | if (!cache_ent) 43 | return -1; 44 | 45 | pthread_mutex_lock (cache_ent->mutex); 46 | 47 | pthread_spin_lock (&cache->mb_spin); 48 | TAILQ_REMOVE(&cache->mbu_head, cache_ent, u_entry); 49 | cache->nused--; 50 | pthread_spin_unlock (&cache->mb_spin); 51 | 52 | old_ppa.ppa = cache_ent->ppa.ppa; 53 | 54 | if (cache_ent->dirty) { 55 | if (cache->write_fn (cache_ent->buf, cache_ent->up_entry)) { 56 | 57 | pthread_spin_lock (&cache->mb_spin); 58 | TAILQ_INSERT_HEAD(&cache->mbu_head, cache_ent, u_entry); 59 | cache->nused++; 60 | pthread_spin_unlock (&cache->mb_spin); 61 | 62 | pthread_mutex_unlock (cache_ent->mutex); 63 | 64 | return -1; 65 | } 66 | 67 | cache_ent->dirty = 0; 68 | 69 | /* Cache up entry PPA is set after the write completes */ 70 | 71 | /* Invalidate old page PPAs */ 72 | if (old_ppa.ppa) 73 | oxapp()->md->invalidate_fn (ch[old_ppa.g.ch], &old_ppa, 74 | APP_INVALID_PAGE); 75 | } 76 | 77 | cache_ent->up_entry->ppa = cache_ent->ppa.ppa; 78 | cache_ent->ppa.ppa = 0; 79 | cache_ent->up_entry = NULL; 80 | 81 | pthread_mutex_unlock (cache_ent->mutex); 82 | 83 | pthread_spin_lock (&cache->mb_spin); 84 | LIST_INSERT_HEAD (&cache->mbf_head, cache_ent, f_entry); 85 | cache->nfree++; 86 | pthread_spin_unlock (&cache->mb_spin); 87 | 88 | return 0; 89 | } 90 | 91 | static void __app_cache_destroy (struct app_cache *cache) 92 | { 93 | struct app_cache_entry *ent; 94 | 95 | /* Evict all pages in the cache */ 96 | while (!(TAILQ_EMPTY(&cache->mbu_head))) { 97 | ent = TAILQ_FIRST(&cache->mbu_head); 98 | if (ent != NULL) 99 | if (app_cache_evict_pg (cache)) 100 | log_err ("[appnvm (gl_map): ERROR. Cache entry not persisted " 101 | "in NVM. Ch %d\n", cache->id); 102 | } 103 | 104 | /* TODO: Check if any cache entry still remains. Retry I/Os */ 105 | 106 | while (!(LIST_EMPTY(&cache->mbf_head))) { 107 | ent = LIST_FIRST(&cache->mbf_head); 108 | if (ent != NULL) { 109 | LIST_REMOVE(ent, f_entry); 110 | cache->nfree--; 111 | ox_free (ent->buf, cache->ox_mem_id); 112 | } 113 | } 114 | 115 | pthread_spin_destroy (&cache->mb_spin); 116 | ox_free (cache->pg_buf, cache->ox_mem_id); 117 | } 118 | 119 | static int __app_cache_create (struct app_cache *cache, 120 | uint16_t ox_mem_id, uint32_t cache_sz, uint32_t ent_sz) 121 | { 122 | uint32_t pg_i; 123 | 124 | cache->pg_buf = ox_calloc (cache_sz, sizeof(struct app_cache_entry), 125 | ox_mem_id); 126 | if (!cache->pg_buf) 127 | return -1; 128 | 129 | if (pthread_spin_init(&cache->mb_spin, 0)) 130 | goto FREE_BUF; 131 | 132 | cache->ox_mem_id = ox_mem_id; 133 | cache->cache_sz = cache_sz; 134 | cache->cache_ent_sz = ent_sz; 135 | 136 | cache->mbf_head.lh_first = NULL; 137 | LIST_INIT(&cache->mbf_head); 138 | TAILQ_INIT(&cache->mbu_head); 139 | cache->nfree = 0; 140 | cache->nused = 0; 141 | 142 | for (pg_i = 0; pg_i < cache_sz; pg_i++) { 143 | cache->pg_buf[pg_i].dirty = 0; 144 | cache->pg_buf[pg_i].buf_sz = ent_sz; 145 | cache->pg_buf[pg_i].ppa.ppa = 0x0; 146 | cache->pg_buf[pg_i].up_entry = NULL; 147 | cache->pg_buf[pg_i].cache = cache; 148 | 149 | cache->pg_buf[pg_i].buf = ox_malloc (ent_sz, ox_mem_id); 150 | if (!cache->pg_buf[pg_i].buf) 151 | goto FREE_PGS; 152 | 153 | LIST_INSERT_HEAD (&cache->mbf_head, &cache->pg_buf[pg_i], f_entry); 154 | cache->nfree++; 155 | } 156 | 157 | return 0; 158 | 159 | FREE_PGS: 160 | while (pg_i) { 161 | pg_i--; 162 | LIST_REMOVE(&cache->pg_buf[pg_i], f_entry); 163 | cache->nfree--; 164 | ox_free (cache->pg_buf[pg_i].buf, ox_mem_id); 165 | } 166 | pthread_spin_destroy (&cache->mb_spin); 167 | FREE_BUF: 168 | ox_free (cache->pg_buf, ox_mem_id); 169 | return -1; 170 | } 171 | 172 | struct app_cache_entry *app_cache_get (struct app_cache *cache, uint64_t lba) 173 | { 174 | /* TODO: Not implemented yet */ 175 | return NULL; 176 | } 177 | 178 | struct app_cache *app_cache_create (uint16_t ox_mem_id, uint32_t cache_sz, 179 | uint32_t ent_sz, uint16_t instances, 180 | app_cache_rw_fn *read_fn, app_cache_rw_fn *write_fn) 181 | { 182 | struct app_cache *cache; 183 | uint16_t cid; 184 | 185 | cache = ox_malloc (sizeof(struct app_cache) * instances, ox_mem_id); 186 | if (!cache) 187 | return NULL; 188 | 189 | cache->instances = instances; 190 | cache->read_fn = read_fn; 191 | cache->write_fn = write_fn; 192 | 193 | for (cid = 0; cid < instances; cid++) { 194 | if (__app_cache_create (&cache[cid], ox_mem_id, cache_sz, ent_sz)) 195 | goto FREE; 196 | } 197 | 198 | return cache; 199 | 200 | FREE: 201 | while (cid) { 202 | cid--; 203 | __app_cache_destroy (&cache[cid]); 204 | } 205 | ox_free (cache, ox_mem_id); 206 | return NULL; 207 | } 208 | 209 | void app_cache_destroy (struct app_cache *cache) 210 | { 211 | uint16_t cid; 212 | 213 | for (cid = 0; cid < cache->instances; cid++) 214 | __app_cache_destroy (&cache[cid]); 215 | 216 | ox_free (cache, cache->ox_mem_id); 217 | } 218 | 219 | int app_cache_init (void) 220 | { 221 | uint32_t nch; 222 | 223 | ch = ox_malloc (sizeof (struct app_channel *) * app_nch, OX_MEM_OXAPP); 224 | if (!ch) 225 | return -1; 226 | 227 | nch = oxapp()->channels.get_list_fn (ch, app_nch); 228 | if (nch != app_nch) 229 | goto FREE; 230 | 231 | map_ent_per_pg = ch[0]->ch->geometry->pl_pg_size / 232 | sizeof (struct app_hmap_entry); 233 | return 0; 234 | 235 | FREE: 236 | ox_free (ch, OX_MEM_OXAPP); 237 | return -1; 238 | } 239 | 240 | void app_cache_exit (void) 241 | { 242 | ox_free (ch, OX_MEM_OXAPP); 243 | } -------------------------------------------------------------------------------- /ftl/ox-app/block/non-recoverable/oxb-blkmd-nr.c: -------------------------------------------------------------------------------- 1 | /* OX: Open-Channel NVM Express SSD Controller 2 | * 3 | * - Non-Recoverable Block Metadata Management (deprecated) 4 | * 5 | * Copyright 2018 IT University of Copenhagen 6 | * 7 | * Written by Ivan Luiz Picoli 8 | * 9 | * Licensed under the Apache License, Version 2.0 (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * http://www.apache.org/licenses/LICENSE-2.0 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | static int blk_md_create (struct app_channel *lch) 29 | { 30 | int i; 31 | struct app_blk_md_entry *ent; 32 | struct app_blk_md *md = lch->blk_md; 33 | 34 | for (i = 0; i < md->entries; i++) { 35 | ent = ((struct app_blk_md_entry *) md->tbl) + i; 36 | memset (ent, 0x0, sizeof (struct app_blk_md_entry)); 37 | ent->ppa.ppa = 0x0; 38 | ent->ppa.g.ch = lch->ch->ch_id; 39 | ent->ppa.g.lun = i / lch->ch->geometry->blk_per_lun; 40 | ent->ppa.g.blk = i % lch->ch->geometry->blk_per_lun; 41 | } 42 | 43 | return 0; 44 | } 45 | 46 | static int blk_md_load (struct app_channel *lch) 47 | { 48 | int pg; 49 | struct app_blk_md *md = lch->blk_md; 50 | struct nvm_ppa_addr ppa; 51 | 52 | struct nvm_io_data *io = ftl_alloc_pg_io(lch->ch); 53 | if (io == NULL) 54 | return -1; 55 | 56 | /* single page planes might be padded to avoid broken entries */ 57 | uint16_t ent_per_pg = (io->pg_sz / sizeof (struct app_blk_md_entry)) * 58 | io->n_pl; 59 | uint16_t md_pgs = md->entries / ent_per_pg; 60 | if (md->entries % ent_per_pg > 0) 61 | md_pgs++; 62 | 63 | if (md_pgs > io->ch->geometry->pg_per_blk) { 64 | log_err("[ox-app ERR: Ch %d -> Maximum Block Metadata: %d bytes\n", 65 | io->ch->ch_id, io->pg_sz * io->n_pl * io->ch->geometry->pg_per_blk); 66 | goto ERR; 67 | } 68 | 69 | pg = ftl_blk_current_page (lch->ch, io, lch->meta_blk, md_pgs); 70 | if (pg < 0) 71 | goto ERR; 72 | 73 | if (!pg) { 74 | if (ftl_io_rsv_blk (lch->ch, MMGR_ERASE_BLK, NULL, lch->meta_blk, 0)) 75 | goto ERR; 76 | 77 | /* tells the caller that the block is new and must be written */ 78 | md->byte.magic = APP_MAGIC; 79 | goto OUT; 80 | 81 | } else { 82 | 83 | /* load block metadata table from nvm */ 84 | pg -= md_pgs; 85 | ppa.g.pg = pg; 86 | ppa.g.blk = lch->meta_blk; 87 | 88 | if (ftl_nvm_seq_transfer (io, &ppa, md->tbl, md_pgs, ent_per_pg, 89 | md->entries, sizeof(struct app_blk_md_entry), 90 | NVM_TRANS_FROM_NVM, NVM_IO_RESERVED)) 91 | goto ERR; 92 | } 93 | 94 | md->byte.magic = 0; 95 | 96 | OUT: 97 | ftl_free_pg_io(io); 98 | return 0; 99 | 100 | ERR: 101 | ftl_free_pg_io(io); 102 | return -1; 103 | } 104 | 105 | static int blk_md_flush (struct app_channel *lch) 106 | { 107 | int pg; 108 | struct app_blk_md *md = lch->blk_md; 109 | struct nvm_ppa_addr ppa; 110 | 111 | struct nvm_io_data *io = ftl_alloc_pg_io(lch->ch); 112 | if (io == NULL) 113 | return -1; 114 | 115 | /* single page planes might be padded to avoid broken entries */ 116 | uint16_t ent_per_pg = (io->pg_sz / 117 | sizeof (struct app_blk_md_entry)) * io->n_pl; 118 | uint16_t md_pgs = md->entries / ent_per_pg; 119 | if (md->entries % ent_per_pg > 0) 120 | md_pgs++; 121 | 122 | if (md_pgs > io->ch->geometry->pg_per_blk) { 123 | log_err("[ox-app ERR: Ch %d -> Maximum Block Metadata: %d bytes\n", 124 | io->ch->ch_id, io->pg_sz * io->n_pl * io->ch->geometry->pg_per_blk); 125 | goto ERR; 126 | } 127 | 128 | pg = ftl_blk_current_page (lch->ch, io, lch->meta_blk, md_pgs); 129 | if (pg < 0) 130 | goto ERR; 131 | 132 | if (pg >= io->ch->geometry->pg_per_blk - md_pgs) { 133 | if (ftl_io_rsv_blk (lch->ch, MMGR_ERASE_BLK, NULL, lch->meta_blk, 0)) 134 | goto ERR; 135 | pg = 0; 136 | } 137 | 138 | md->byte.magic = APP_MAGIC; 139 | memset (io->buf, 0, io->buf_sz); 140 | 141 | /* set info to OOB area */ 142 | memcpy (io->oob_vec[0], md, 16); 143 | 144 | /* flush the block metadata table to nvm */ 145 | ppa.g.pg = pg; 146 | ppa.g.blk = lch->meta_blk; 147 | 148 | if (ftl_nvm_seq_transfer (io, &ppa, md->tbl, md_pgs, ent_per_pg, 149 | md->entries, sizeof(struct app_blk_md_entry), 150 | NVM_TRANS_TO_NVM, NVM_IO_RESERVED)) 151 | goto ERR; 152 | 153 | ftl_free_pg_io(io); 154 | return 0; 155 | 156 | ERR: 157 | ftl_free_pg_io(io); 158 | return -1; 159 | } 160 | 161 | static void blk_md_mark (struct app_channel *lch, uint64_t addr) 162 | { 163 | 164 | } 165 | 166 | static struct app_blk_md_entry *blk_md_get (struct app_channel *lch, 167 | uint16_t lun) 168 | { 169 | struct app_blk_md *md = lch->blk_md; 170 | size_t lun_sz = md->entry_sz * lch->ch->geometry->blk_per_lun; 171 | 172 | if (!md->tbl) 173 | return NULL; 174 | 175 | return (struct app_blk_md_entry *) (md->tbl + (lun * lun_sz)); 176 | } 177 | 178 | static void blk_md_invalidate (struct app_channel *lch, 179 | struct nvm_ppa_addr *ppa, uint8_t full) 180 | { 181 | uint16_t pl_i; 182 | uint8_t *pg_map, off; 183 | struct app_blk_md_entry *lun; 184 | struct nvm_mmgr_geometry *g = lch->ch->geometry; 185 | uint32_t blk_i; 186 | uint64_t index; 187 | 188 | off = (1 << g->sec_per_pg) - 1; 189 | blk_i = (g->blk_per_lun * ppa->g.lun) + ppa->g.blk; 190 | 191 | lun = oxapp()->md->get_fn (lch, ppa->g.lun); 192 | pg_map = &lun[ppa->g.blk].pg_state[ppa->g.pg * g->n_of_planes]; 193 | 194 | index = blk_i / lch->blk_md->ent_per_pg; 195 | pthread_spin_lock (&lch->blk_md->entry_spin[index]); 196 | 197 | /* If full is > 0, invalidate all sectors in the page */ 198 | if (full) { 199 | for (pl_i = 0; pl_i < g->n_of_planes; pl_i++) 200 | pg_map[pl_i] = off; 201 | lun[ppa->g.blk].invalid_sec += g->sec_per_pl_pg; 202 | } else { 203 | pg_map[ppa->g.pl] |= 1 << ppa->g.sec; 204 | lun[ppa->g.blk].invalid_sec++; 205 | } 206 | 207 | pthread_spin_unlock (&lch->blk_md->entry_spin[index]); 208 | } 209 | 210 | static struct app_global_md nr_md = { 211 | .mod_id = NR_BLK_MD, 212 | .name = "NON-RECOVERABLE-MD", 213 | .create_fn = blk_md_create, 214 | .flush_fn = blk_md_flush, 215 | .load_fn = blk_md_load, 216 | .get_fn = blk_md_get, 217 | .invalidate_fn = blk_md_invalidate, 218 | .mark_fn = blk_md_mark 219 | }; 220 | 221 | void nr_blk_md_register (void) { 222 | app_mod_register (APPMOD_BLK_MD, NR_BLK_MD, &nr_md); 223 | } -------------------------------------------------------------------------------- /ftl/ox-app/block/non-recoverable/oxb-chmap-nr.c: -------------------------------------------------------------------------------- 1 | /* OX: Open-Channel NVM Express SSD Controller 2 | * - Non-recoverable Channel Mapping (deprecated) 3 | * 4 | * Copyright 2018 IT University of Copenhagen 5 | * 6 | * Written by Ivan Luiz Picoli 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | extern uint16_t app_nch; 30 | uint8_t app_map_new; 31 | 32 | static int ch_map_create (struct app_channel *lch) 33 | { 34 | int i; 35 | struct app_map_entry *ent; 36 | struct app_map_md *md = lch->map_md; 37 | 38 | for (i = 0; i < md->entries; i++) { 39 | ent = ((struct app_map_entry *) md->tbl) + i; 40 | memset (ent, 0x0, sizeof (struct app_map_entry)); 41 | ent->lba = (i * app_nch) + lch->app_ch_id; 42 | } 43 | 44 | app_map_new = 1; 45 | 46 | return 0; 47 | } 48 | 49 | static int ch_map_load (struct app_channel *lch) 50 | { 51 | int pg; 52 | struct app_map_md *md = lch->map_md; 53 | struct nvm_ppa_addr ppa; 54 | 55 | struct nvm_io_data *io = ftl_alloc_pg_io (lch->ch); 56 | if (io == NULL) 57 | return -1; 58 | 59 | /* single page planes might be padded to avoid broken entries */ 60 | uint16_t ent_per_pg = (io->pg_sz / sizeof(struct app_map_entry)) * io->n_pl; 61 | uint16_t md_pgs = md->entries / ent_per_pg; 62 | if (md->entries % ent_per_pg > 0) 63 | md_pgs++; 64 | 65 | if (md_pgs > io->ch->geometry->pg_per_blk) { 66 | log_err("[appnvm ERR: Ch %d -> Maximum Mapping Metadata: %d bytes\n", 67 | io->ch->ch_id, io->pg_sz * io->ch->geometry->pg_per_blk); 68 | goto ERR; 69 | } 70 | 71 | pg =ftl_blk_current_page (lch->ch, io, lch->map_blk, md_pgs); 72 | if (pg < 0) 73 | goto ERR; 74 | 75 | if (!pg) { 76 | if (ftl_io_rsv_blk (lch->ch, MMGR_ERASE_BLK, NULL, lch->map_blk, 0)) 77 | goto ERR; 78 | 79 | /* tells the caller that the block is new and must be written */ 80 | md->byte.magic = APP_MAGIC; 81 | goto OUT; 82 | 83 | } else { 84 | 85 | /* load mapping metadata table from nvm */ 86 | pg -= md_pgs; 87 | ppa.g.pg = pg; 88 | ppa.g.blk = lch->map_blk; 89 | 90 | if (ftl_nvm_seq_transfer (io, &ppa, md->tbl, md_pgs, ent_per_pg, 91 | md->entries, sizeof(struct app_map_entry), 92 | NVM_TRANS_FROM_NVM, NVM_IO_RESERVED)) 93 | goto ERR; 94 | } 95 | 96 | md->byte.magic = 0; 97 | 98 | OUT: 99 | ftl_free_pg_io(io); 100 | return 0; 101 | 102 | ERR: 103 | ftl_free_pg_io(io); 104 | return -1; 105 | } 106 | 107 | static int ch_map_flush (struct app_channel *lch) 108 | { 109 | int pg, retry = 0; 110 | struct app_map_md *md = lch->map_md; 111 | struct nvm_ppa_addr ppa; 112 | 113 | struct nvm_io_data *io = ftl_alloc_pg_io(lch->ch); 114 | if (io == NULL) 115 | return -1; 116 | 117 | /* single page planes might be padded to avoid broken entries */ 118 | uint16_t ent_per_pg = (io->pg_sz / sizeof(struct app_map_entry)) * io->n_pl; 119 | uint16_t md_pgs = md->entries / ent_per_pg; 120 | if (md->entries % ent_per_pg > 0) 121 | md_pgs++; 122 | 123 | if (md_pgs > io->ch->geometry->pg_per_blk) { 124 | log_err("[appnvm ERR: Ch %d -> Maximum Mapping Metadata: %d bytes\n", 125 | io->ch->ch_id, io->pg_sz * io->ch->geometry->pg_per_blk); 126 | goto ERR; 127 | } 128 | 129 | pg = ftl_blk_current_page (lch->ch, io, lch->map_blk, md_pgs); 130 | if (pg < 0) 131 | goto ERR; 132 | 133 | ERASE: 134 | if (pg >= io->ch->geometry->pg_per_blk - md_pgs) { 135 | if (ftl_io_rsv_blk (lch->ch, MMGR_ERASE_BLK, NULL, lch->map_blk, 0)) 136 | goto ERR; 137 | pg = 0; 138 | } 139 | 140 | md->byte.magic = APP_MAGIC; 141 | memset (io->buf, 0, io->buf_sz); 142 | 143 | /* set info to OOB area */ 144 | memcpy (io->oob_vec[0], md, sizeof(struct app_map_md)); 145 | 146 | /* flush the mapping metadata table to nvm */ 147 | ppa.g.pg = pg; 148 | ppa.g.blk = lch->map_blk; 149 | 150 | if (ftl_nvm_seq_transfer (io, &ppa, md->tbl, md_pgs, ent_per_pg, 151 | md->entries, sizeof(struct app_map_entry), 152 | NVM_TRANS_TO_NVM, NVM_IO_RESERVED)) { 153 | retry++; 154 | 155 | if (retry > 3) { 156 | goto ERR; 157 | } else { 158 | pg = io->ch->geometry->pg_per_blk; 159 | goto ERASE; 160 | } 161 | } 162 | 163 | ftl_free_pg_io(io); 164 | return 0; 165 | 166 | ERR: 167 | ftl_free_pg_io(io); 168 | return -1; 169 | } 170 | 171 | static void ch_map_mark (struct app_channel *lch, uint64_t addr) 172 | { 173 | 174 | } 175 | 176 | static struct app_map_entry *ch_map_get (struct app_channel *lch, uint32_t off) 177 | { 178 | return (off >= lch->map_md->entries) ? NULL : 179 | ((struct app_map_entry *) lch->map_md->tbl) + off; 180 | } 181 | 182 | static struct app_ch_map nr_ch_map = { 183 | .mod_id = NR_CH_MAP, 184 | .name = "NON-RECOVERABLE-UMAP", 185 | .create_fn = ch_map_create, 186 | .load_fn = ch_map_load, 187 | .flush_fn = ch_map_flush, 188 | .get_fn = ch_map_get, 189 | .mark_fn = ch_map_mark 190 | }; 191 | 192 | void nr_ch_map_register (void) { 193 | app_map_new = 0; 194 | app_mod_register (APPMOD_CH_MAP, NR_CH_MAP, &nr_ch_map); 195 | } -------------------------------------------------------------------------------- /fuse/fuse-bigfile.c: -------------------------------------------------------------------------------- 1 | #define FUSE_USE_VERSION 31 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | 14 | int fuse_ox_init (void); 15 | void fuse_ox_exit (void); 16 | int fuse_ox_write (uint64_t slba, uint64_t size, uint8_t *buf); 17 | int fuse_ox_read (uint64_t slba, uint64_t size, uint8_t *buf); 18 | 19 | static uint64_t ns_size; 20 | 21 | static int do_getattr( const char *path, struct stat *st) 22 | { 23 | st->st_uid = getuid(); 24 | st->st_gid = getgid(); 25 | st->st_atime = time( NULL ); 26 | st->st_mtime = time( NULL ); 27 | 28 | if ( strcmp( path, "/" ) == 0 ) { 29 | st->st_mode = S_IFDIR | 0755; 30 | st->st_nlink = 2; 31 | } else { 32 | st->st_mode = S_IFREG | 0664; 33 | st->st_nlink = 1; 34 | st->st_size = (off_t) ns_size * NVMEH_BLK_SZ; 35 | } 36 | 37 | return 0; 38 | } 39 | 40 | static int do_readdir( const char *path, void *buffer, fuse_fill_dir_t filler, 41 | off_t offset, struct fuse_file_info *fi ) 42 | { 43 | 44 | filler( buffer, ".", NULL, 0 ); 45 | filler( buffer, "..", NULL, 0 ); 46 | 47 | if ( strcmp( path, "/" ) == 0) { 48 | filler( buffer, "ox-full-ns", NULL, 0 ); 49 | } 50 | 51 | return 0; 52 | } 53 | 54 | static int do_read( const char *path, char *buffer, size_t size, off_t offset, 55 | struct fuse_file_info *fi ) 56 | { 57 | int ret; 58 | 59 | ret = fuse_ox_read ((offset / NVMEH_BLK_SZ) + 1, size, (uint8_t *) buffer); 60 | return (!ret) ? size : 0; 61 | } 62 | 63 | static int do_opendir( const char *path, struct fuse_file_info *fi) 64 | { 65 | return 0; 66 | } 67 | 68 | static int do_write( const char *path, const char *buffer, size_t size, 69 | off_t offset, struct fuse_file_info *fi ) 70 | { 71 | int ret; 72 | uint64_t sz; 73 | uint8_t *rbuf; 74 | uint32_t offr; 75 | 76 | /* If size is not aligned, read the block, make any changes, and write it */ 77 | if (size % NVMEH_BLK_SZ != 0) { 78 | rbuf = malloc (NVMEH_BLK_SZ); 79 | if (!rbuf) 80 | return 0; 81 | 82 | ret = fuse_ox_read ((offset / NVMEH_BLK_SZ) + 1, NVMEH_BLK_SZ, rbuf); 83 | if (ret) { 84 | free (rbuf); 85 | return 0; 86 | } 87 | 88 | offr = offset % NVMEH_BLK_SZ; 89 | memcpy (&rbuf[offr], buffer, size); 90 | } else { 91 | rbuf = (uint8_t *) buffer; 92 | } 93 | 94 | sz = (((size - 1) / NVMEH_BLK_SZ) + 1) * NVMEH_BLK_SZ; 95 | 96 | ret = fuse_ox_write ((offset / NVMEH_BLK_SZ) + 1, sz, rbuf); 97 | 98 | return (!ret) ? size : 0; 99 | } 100 | 101 | static int do_open( const char *path, struct fuse_file_info *fi) 102 | { 103 | return 0; 104 | } 105 | 106 | static void *do_init( struct fuse_conn_info *conn) 107 | { 108 | return NULL; 109 | } 110 | 111 | static struct fuse_operations operations = { 112 | .getattr = do_getattr, 113 | .readdir = do_readdir, 114 | .read = do_read, 115 | .write = do_write, 116 | .open = do_open, 117 | .opendir = do_opendir, 118 | .init = do_init, 119 | }; 120 | 121 | int main( int argc, char *argv[] ) 122 | { 123 | struct nvme_id_ns ns_id; 124 | 125 | fuse_ox_init (); 126 | 127 | if (nvmeh_identify_ns (&ns_id, 1)) { 128 | fuse_ox_exit (); 129 | return -1; 130 | } 131 | 132 | ns_size = ns_id.nsze; 133 | 134 | printf ("Welcome to OX BFFS (Big File File System)!\n"); 135 | printf ("It displays a single file representing the whole OX device.\n"); 136 | printf ("Stop this process to unmount the file system.\n"); 137 | 138 | return fuse_main( argc, argv, &operations, NULL ); 139 | } 140 | -------------------------------------------------------------------------------- /fuse/fuse-ox-nvme.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #define NVMEH_NUM_QUEUES 4 10 | #define NVMEH_BLKSZ 4096 11 | 12 | #define OX_FUSE_WRITE_BACK 1 13 | 14 | /* This is an example context that identifies the completion */ 15 | struct nvme_context { 16 | uint32_t id; 17 | uint64_t slba; 18 | uint64_t nlb; 19 | uint16_t done; 20 | uint16_t status; 21 | uint16_t is_write; 22 | uint8_t *buf; 23 | TAILQ_ENTRY (nvme_context) entry; 24 | }; 25 | 26 | struct nvme_context *ctx; 27 | 28 | TAILQ_HEAD (ctx_free, nvme_context) ctx_f; 29 | TAILQ_HEAD (ctx_used, nvme_context) ctx_u; 30 | 31 | static pthread_spinlock_t ctx_spin; 32 | 33 | /* Callback funcion */ 34 | static void nvme_callback (void *c, uint16_t status) 35 | { 36 | struct nvme_context *my_ctx = (struct nvme_context *) c; 37 | uint8_t freem = 0; 38 | 39 | if (OX_FUSE_WRITE_BACK && my_ctx->is_write) 40 | freem = 1; 41 | 42 | my_ctx->status = status; 43 | my_ctx->done = 1; 44 | 45 | if (freem) { 46 | free (my_ctx->buf); 47 | 48 | pthread_spin_lock (&ctx_spin); 49 | TAILQ_REMOVE(&ctx_u, my_ctx, entry); 50 | TAILQ_INSERT_TAIL(&ctx_f, my_ctx, entry); 51 | pthread_spin_unlock (&ctx_spin); 52 | } 53 | } 54 | 55 | int fuse_ox_read (uint64_t slba, uint64_t size, uint8_t *buf) { 56 | struct nvme_context *c; 57 | uint16_t retry = 2000; 58 | int ret; 59 | 60 | RETRY: 61 | /* Get the context */ 62 | pthread_spin_lock (&ctx_spin); 63 | c = TAILQ_FIRST(&ctx_f); 64 | if (!c) { 65 | pthread_spin_unlock (&ctx_spin); 66 | usleep (1000); 67 | retry--; 68 | if (retry) { 69 | goto RETRY; 70 | } else { 71 | printf ("No slots available in the queue.\n"); 72 | return -1; 73 | } 74 | } 75 | TAILQ_REMOVE(&ctx_f, c, entry); 76 | TAILQ_INSERT_TAIL(&ctx_u, c, entry); 77 | pthread_spin_unlock (&ctx_spin); 78 | 79 | /* Set the context */ 80 | c->slba = slba; 81 | c->nlb = size / 4096; 82 | c->done = 0; 83 | c->status = 0; 84 | c->is_write = 0; 85 | 86 | /* Submit the read to OX */ 87 | ret = nvmeh_read (buf, size, slba, nvme_callback, c); 88 | if (ret) { 89 | printf ("Error: I/O not submitted.\n"); 90 | c->done = 1; 91 | c->status = 1; 92 | } 93 | 94 | /* Synchronously wait for completion */ 95 | while (!c->done) 96 | usleep (1); 97 | 98 | ret = c->status; 99 | 100 | /* Free the context */ 101 | pthread_spin_lock (&ctx_spin); 102 | TAILQ_REMOVE(&ctx_u, c, entry); 103 | TAILQ_INSERT_TAIL(&ctx_f, c, entry); 104 | pthread_spin_unlock (&ctx_spin); 105 | 106 | return ret; 107 | } 108 | 109 | int fuse_ox_write (uint64_t slba, uint64_t size, uint8_t *buf) { 110 | struct nvme_context *c; 111 | uint16_t retry = 2000; 112 | int ret, fail = 0; 113 | uint8_t *rbuf; 114 | 115 | RETRY: 116 | /* Get the context */ 117 | pthread_spin_lock (&ctx_spin); 118 | c = TAILQ_FIRST(&ctx_f); 119 | if (!c) { 120 | pthread_spin_unlock (&ctx_spin); 121 | usleep (1000); 122 | retry--; 123 | if (retry) { 124 | goto RETRY; 125 | } else { 126 | printf ("No slots available in the queue.\n"); 127 | return -1; 128 | } 129 | } 130 | TAILQ_REMOVE(&ctx_f, c, entry); 131 | TAILQ_INSERT_TAIL(&ctx_u, c, entry); 132 | pthread_spin_unlock (&ctx_spin); 133 | 134 | /* Set the context */ 135 | c->slba = slba; 136 | c->nlb = size / 4096; 137 | c->done = 0; 138 | c->status = 0; 139 | c->is_write = 1; 140 | 141 | if (OX_FUSE_WRITE_BACK) { 142 | c->buf = malloc (size); 143 | if (!c->buf) { 144 | fail = 1; 145 | rbuf = buf; 146 | } else { 147 | memcpy (c->buf, buf, size); 148 | rbuf = c->buf; 149 | } 150 | } else { 151 | rbuf = buf; 152 | } 153 | 154 | /* Submit the read to OX */ 155 | ret = nvmeh_write (rbuf, size, slba, nvme_callback, c); 156 | if (ret) { 157 | printf ("Error: I/O not submitted.\n"); 158 | c->done = 1; 159 | c->status = 1; 160 | } 161 | 162 | if (OX_FUSE_WRITE_BACK && !fail) { 163 | ret = 0; 164 | } else { 165 | /* Synchronously wait for completion */ 166 | while (!c->done) 167 | usleep (1); 168 | 169 | ret = c->status; 170 | 171 | /* Free the context */ 172 | pthread_spin_lock (&ctx_spin); 173 | TAILQ_REMOVE(&ctx_u, c, entry); 174 | TAILQ_INSERT_TAIL(&ctx_f, c, entry); 175 | pthread_spin_unlock (&ctx_spin); 176 | } 177 | 178 | return ret; 179 | } 180 | 181 | static int fuse_ox_prepare (void) { 182 | ctx = malloc (sizeof (struct nvme_context) * OXF_QUEUE_SIZE); 183 | if (!ctx) 184 | return -1; 185 | 186 | TAILQ_INIT(&ctx_f); 187 | TAILQ_INIT(&ctx_u); 188 | 189 | for (int ent_i = 0; ent_i < OXF_QUEUE_SIZE; ent_i++) { 190 | memset (&ctx[ent_i], 0, sizeof (struct nvme_context)); 191 | ctx[ent_i].id = ent_i; 192 | TAILQ_INSERT_TAIL(&ctx_f, &ctx[ent_i], entry); 193 | } 194 | 195 | if (pthread_spin_init (&ctx_spin, 0)) 196 | goto FREE; 197 | 198 | return 0; 199 | 200 | FREE: 201 | free (ctx); 202 | return -1; 203 | } 204 | 205 | void fuse_ox_exit (void) { 206 | pthread_spin_destroy (&ctx_spin); 207 | free (ctx); 208 | } 209 | 210 | int fuse_ox_init (void) 211 | { 212 | struct nvme_id_ctrl ctrl_id; 213 | struct nvme_id_ns ns_id; 214 | int ret, q_id = 0; 215 | 216 | ret = nvmeh_init (); 217 | if (ret) { 218 | printf ("Failed to initializing NVMe Host.\n"); 219 | return -1; 220 | } 221 | 222 | if (fuse_ox_prepare ()) { 223 | goto ERROR; 224 | } 225 | 226 | nvme_host_add_server_iface (OXF_ADDR_1, OXF_PORT_1); 227 | nvme_host_add_server_iface (OXF_ADDR_2, OXF_PORT_2); 228 | 229 | /* We just have 2 cables for now, for the real network setup */ 230 | #if OXF_FULL_IFACES 231 | nvme_host_add_server_iface (OXF_ADDR_3, OXF_PORT_3); 232 | nvme_host_add_server_iface (OXF_ADDR_4, OXF_PORT_4); 233 | #endif 234 | 235 | /* Create the NVMe queues. One additional queue for the admin queue */ 236 | for (q_id = 0; q_id < NVMEH_NUM_QUEUES + 1; q_id++) { 237 | if (nvme_host_create_queue (q_id)) { 238 | printf ("Failed to creating queue %d.\n", q_id); 239 | goto ERROR_Q; 240 | } 241 | } 242 | 243 | if (nvmeh_identify_ctrl (&ctrl_id)) { 244 | printf ("Identify Controller Failed!\n\n"); 245 | } else { 246 | printf ("Identify Controller Success!\n"); 247 | printf (" Serial Number: %s\n", ctrl_id.sn); 248 | printf (" Model Number : %s\n", ctrl_id.mn); 249 | printf (" Ctrl Name : %s\n\n", ctrl_id.subnqn); 250 | } 251 | 252 | if (nvmeh_identify_ns (&ns_id, 1)) { 253 | printf ("Identify Namespace Failed!\n\n"); 254 | } else { 255 | printf ("Identify Namespace Success!\n"); 256 | printf (" Namespace ID : 1\n"); 257 | printf (" Namespace Size : %lu blocks (%.2f GB)\n\n", ns_id.nsze, 258 | (float) ns_id.nsze * NVMEH_BLK_SZ / 1024 / 1024 / 1024); 259 | } 260 | 261 | return 0; 262 | 263 | /* Closes the application */ 264 | ERROR_Q: 265 | fuse_ox_exit (); 266 | ERROR: 267 | while (q_id) { 268 | q_id--; 269 | nvme_host_destroy_queue (q_id); 270 | } 271 | nvmeh_exit (); 272 | 273 | return -1; 274 | } 275 | -------------------------------------------------------------------------------- /host/nvme-host-dev.h: -------------------------------------------------------------------------------- 1 | /* OX: Open-Channel NVM Express SSD Controller 2 | * 3 | * - NVMe Host Developer Header 4 | * 5 | * Copyright 2018 IT University of Copenhagen 6 | * 7 | * Written by Ivan Luiz Picoli 8 | * 9 | * Licensed under the Apache License, Version 2.0 (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * http://www.apache.org/licenses/LICENSE-2.0 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | * 21 | */ 22 | 23 | #include 24 | 25 | /* Max of NVMe commands per user I/O (each NVMe command is up to 60K in size) */ 26 | #define NVMEH_MAX_CMD_BATCH 260 /* 60K * 260 = 15600K */ 27 | 28 | /* 10 seconds timeout */ 29 | #define NVMEH_RETRY 50000 30 | #define NVMEH_RETRY_DELAY 200 31 | 32 | struct nvmeh_ctx; 33 | struct nvmeh_cmd_status { 34 | uint16_t status; 35 | struct nvmeh_ctx *ctx; 36 | }; 37 | 38 | struct nvme_host; 39 | struct nvmeh_ctx { 40 | uint64_t ctx_id; 41 | void *user_ctx; 42 | oxf_host_callback_fn *user_cb; 43 | struct nvmeh_cmd_status cmd_status[NVMEH_MAX_CMD_BATCH]; 44 | uint32_t n_cmd; 45 | uint32_t completed; 46 | uint32_t failed; 47 | pthread_spinlock_t spin; 48 | TAILQ_ENTRY(nvmeh_ctx) entry; 49 | struct nvme_host *host; 50 | uint8_t is_write; 51 | }; 52 | 53 | struct nvme_cmd_rw { 54 | uint8_t opcode; /* Dword 0 */ 55 | uint8_t fuse : 2; 56 | uint8_t rsvd : 4; 57 | uint8_t psdt : 2; 58 | uint16_t cid; 59 | uint32_t nsid; /* Dword 1 */ 60 | uint64_t rsvd2; /* Dword 2, 3 */ 61 | uint64_t mptr; /* Dword 4, 5 */ 62 | uint64_t prp1; /* Dword 6, 7 */ 63 | uint64_t prp2; /* Dword 8, 9 */ 64 | uint64_t slba; /* Dword 10, 11 */ 65 | uint16_t nlb; /* Dword 12 */ 66 | uint16_t control; 67 | uint32_t dsmgmt; /* Dword 13 */ 68 | uint32_t reftag; /* Dword 14 */ 69 | uint16_t apptag; /* Dword 15 */ 70 | uint16_t appmask; 71 | }; 72 | 73 | struct nvme_cmd_identify { 74 | uint8_t opcode; /* Dword 0 */ 75 | uint8_t fuse : 2; 76 | uint8_t rsvd : 4; 77 | uint8_t psdt : 2; 78 | uint16_t cid; 79 | uint32_t nsid; /* Dword 1 */ 80 | uint64_t rsvd2; /* Dword 2, 3 */ 81 | uint64_t mptr; /* Dword 4, 5 */ 82 | uint64_t prp1; /* Dword 6, 7 */ 83 | uint64_t prp2; /* Dword 8, 9 */ 84 | uint8_t cns; /* Dword 10 */ 85 | uint8_t rsvd3; 86 | uint16_t cntid; 87 | uint16_t nvmsetid; /* Dword 11 */ 88 | uint16_t rsvd4; 89 | uint32_t rsvd5; /* Dword 12 */ 90 | uint32_t rsvd6; /* Dword 13 */ 91 | uint32_t uuid; /* Dword 14 */ 92 | uint32_t rsvd8; /* Dword 15 */ 93 | }; 94 | 95 | struct nvme_host { 96 | /* Write contexts */ 97 | struct nvmeh_ctx *ctxw_ent; 98 | TAILQ_HEAD(ctxw_free, nvmeh_ctx) ctxw_fh; 99 | TAILQ_HEAD(ctxw_used, nvmeh_ctx) ctxw_uh; 100 | pthread_spinlock_t ctxw_spin; 101 | uint32_t ctxw_entries; 102 | 103 | /* Read contexts */ 104 | struct nvmeh_ctx *ctxr_ent; 105 | TAILQ_HEAD(ctxr_free, nvmeh_ctx) ctxr_fh; 106 | TAILQ_HEAD(ctxr_used, nvmeh_ctx) ctxr_uh; 107 | pthread_spinlock_t ctxr_spin; 108 | uint32_t ctxr_entries; 109 | 110 | volatile uint16_t cmdid; 111 | pthread_spinlock_t cmdid_spin; 112 | }; 113 | 114 | int nvmeh_init_ctx_write (struct nvme_host *host, uint32_t entries); 115 | int nvmeh_init_ctx_read (struct nvme_host *host, uint32_t entries); 116 | void nvmeh_exit_ctx_write (struct nvme_host *host); 117 | void nvmeh_exit_ctx_read (struct nvme_host *host); 118 | void nvmeh_ctxr_put (struct nvme_host *host, struct nvmeh_ctx *ctxr); 119 | void nvmeh_ctxw_put (struct nvme_host *host, struct nvmeh_ctx *ctxw); 120 | struct nvmeh_ctx *nvmeh_ctxr_get (struct nvme_host *host); 121 | struct nvmeh_ctx *nvmeh_ctxw_get (struct nvme_host *host); 122 | uint16_t nvmeh_get_cmdid (struct nvme_host *host); 123 | void nvmeh_callback (void *ctx, struct nvme_cqe *cqe); 124 | -------------------------------------------------------------------------------- /host/nvme-host.h: -------------------------------------------------------------------------------- 1 | /* OX: Open-Channel NVM Express SSD Controller 2 | * 3 | * - NVMe Host Interface (header) 4 | * 5 | * Copyright 2018 IT University of Copenhagen 6 | * 7 | * Written by Ivan Luiz Picoli 8 | * 9 | * Licensed under the Apache License, Version 2.0 (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * http://www.apache.org/licenses/LICENSE-2.0 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | * 21 | */ 22 | 23 | #include 24 | 25 | #ifndef NVME_HOST_H 26 | #define NVME_HOST_H 27 | 28 | /* NVMe block size (FOR NOW, ONLY 4096 IS SUPPORTED) */ 29 | #define NVMEH_BLK_SZ 4096 30 | 31 | /* NVMe Identify Controller Data Structure */ 32 | struct nvme_id_ctrl { 33 | uint16_t vid; /* PCI Vendor ID */ 34 | uint16_t ssvid; /* PCI Subsystem Vendor ID */ 35 | uint8_t sn[20]; /* Serial Number */ 36 | uint8_t mn[40]; /* Model Number */ 37 | uint8_t fr[8]; /* Firmware Revision */ 38 | uint8_t rab; /* Recommended Arbitration Burst */ 39 | uint8_t ieee[3]; /* IEEE OUI Identifier */ 40 | uint8_t cmic; /* Controller Multi-Path I/O and Namespace Sharing Cap */ 41 | uint8_t mdts; /* Maximum Data Transfer Size */ 42 | uint16_t cntlid; /* Controller ID */ 43 | uint32_t ver; /* Version */ 44 | uint32_t rtd3r; /* RTD3 Resume Latency */ 45 | uint32_t rtd3e; /* RTD3 Entry Latency */ 46 | uint32_t oaes; /* Optional Asynchronous Events Supported */ 47 | uint32_t ctratt; /* Controller Attributes */ 48 | uint8_t rsv[140]; 49 | uint8_t rsv255[16]; 50 | uint16_t oacs; /* Optional Admin Command Support */ 51 | uint8_t acl; /* Abort Command Limit */ 52 | uint8_t aerl; /* Asynchronous Event Request Limit */ 53 | uint8_t frmw; /* Firmware Updates */ 54 | uint8_t lpa; /* Log Page Attributes */ 55 | uint8_t elpe; /* Error Log Page Entries */ 56 | uint8_t npss; /* Number of Power States Support */ 57 | uint8_t avscc; /* Admin Vendor Specific Command Configuration */ 58 | uint8_t apsta; /* Autonomous Power State Transition Attributes */ 59 | uint16_t wctemp; /* Warning Composite Temperature Threshold */ 60 | uint16_t cctemp; /* Critical Composite Temperature Threshold */ 61 | uint16_t mtfa; /* Maximum Time for Firmware Activation */ 62 | uint32_t hmpre; /* Host Memory Buffer Preferred Size */ 63 | uint32_t hmmin; /* Host Memory Buffer Minimum Size */ 64 | uint8_t tnvmcap[16]; /* Total NVM Capacity */ 65 | uint8_t unvmcap[16]; /* Unallocated NVM Capacity */ 66 | uint32_t rpmbs; /* Replay Protected Memory Block Support */ 67 | uint32_t rsv319; 68 | uint16_t kas; /* Keep Alive Support */ 69 | uint8_t rsv511[190]; /* */ 70 | uint8_t sqes; /* Submission Queue Entry Size */ 71 | uint8_t cqes; /* Completion Queue Entry Size */ 72 | uint16_t maxcmd; /* Maximum Outstanding Commands */ 73 | uint32_t nn; /* Number of Namespaces */ 74 | uint16_t oncs; /* Optional NVM Command Support */ 75 | uint16_t fuses; /* Fused Operation Support */ 76 | uint8_t fna; /* Format NVM Attributes */ 77 | uint8_t vwc; /* Volatile Write Cache */ 78 | uint16_t awun; /* Atomic Write Unit Normal */ 79 | uint16_t awupf; /* Atomic Write Unit Power Fail */ 80 | uint8_t nvscc; /* NVM Vendor Specific Command Configuration */ 81 | uint8_t rsv531; 82 | uint16_t acwu; /* Atomic Compare & Write Unit */ 83 | uint16_t rsv535; 84 | uint32_t sgls; /* SGL Support */ 85 | uint8_t rsv767[228]; 86 | uint8_t subnqn[256]; /* NVM Subsystem NVMe Qualified Name */ 87 | uint8_t rsv1791[768]; 88 | uint8_t fabrics[256]; 89 | NvmePSD psd[32]; /* Power State Descriptors */ 90 | uint8_t vs[1024]; /* Vendor Specific */ 91 | }; 92 | 93 | /* NVMe Identify Namespace Data Structure */ 94 | struct nvme_id_ns { 95 | uint64_t nsze; /* Namespace Size */ 96 | uint64_t ncap; /* Namespace Capacity */ 97 | uint64_t nuse; /* Namespace Utilization */ 98 | uint8_t nsfeat; /* Namespace Features */ 99 | uint8_t nlbaf; /* Number of LBA Formats */ 100 | uint8_t flbas; /* Formatted LBA Size */ 101 | uint8_t mc; /* Metadata Capabilities */ 102 | uint8_t dpc; /* End-to-end Data Protection Capabilities */ 103 | uint8_t dps; /* End-to-end Data Protection Type Settings */ 104 | uint8_t nmic; /* Namespace Multi-path I/O and NS Sharing Cap */ 105 | uint8_t rescap; /* Reservation Capabilities */ 106 | uint8_t fpi; /* Format Progress Indicator */ 107 | uint8_t rsv33; 108 | uint16_t nawun; /* Namespace Atomic Write Unit Normal */ 109 | uint16_t nawupf; /* Namespace Atomic Write Unit Power Fail */ 110 | uint16_t nacwu; /* Namespace Atomic Compare & Write Unit */ 111 | uint16_t nabsn; /* Namespace Atomic Boundary Size Normal */ 112 | uint16_t nabo; /* Namespace Atomic Boundary Offset */ 113 | uint16_t nabspf; /* Namespace Atomic Boundary Size Power Fail */ 114 | uint16_t rsv47; 115 | uint64_t nvmcap[2]; /* NVM Capacity */ 116 | uint8_t rsv103[40]; 117 | uint8_t nguid[16]; /* Namespace Globally Unique Identifier */ 118 | uint8_t eui64[8]; /* IEEE Extended Unique Identifier */ 119 | union { 120 | struct { 121 | uint16_t ms; 122 | uint8_t ds; 123 | uint8_t rp; 124 | } e; 125 | uint32_t val; 126 | } lbaf; /* LBA Format Support */ 127 | uint8_t rsv383[192]; 128 | uint8_t vs[3712]; /* Vendor Specific */ 129 | }; 130 | 131 | /* Prototype for the user defined callback function. Called when the NVMe 132 | * asynchronous functions return. 'ctx' is a pointer to the user data previously 133 | * provided. 'status' is 0 if the call succeeded. A positive value (NVME status) 134 | * is returned in failure. 135 | */ 136 | typedef void (nvme_host_callback_fn) (void *ctx, uint16_t status); 137 | 138 | /* Initialize NVMe host (including the Fabrics) */ 139 | int nvmeh_init (void); 140 | 141 | /* Close the host, freeing all allocated resources */ 142 | void nvmeh_exit (void); 143 | 144 | /** 145 | * Create an end-to-end NVMe over Fabrics queue between host and server. Each 146 | * queue has its own threads and TCP sockets. 147 | * 148 | * @param qid - Queue ID. Use id 0 for admin queue (required). Positive values 149 | * are I/O queues. It is recommended two I/O queues per network 150 | * interface. e.g if you have 2 interfaces, create 1 admin queue 151 | * and 4 I/O queues. A maximum of 64 queues are allowed, however, 152 | * too many queues allocate lots of memory and the system may crash. 153 | * @return - return 0 if the queue is created and connected successfully. A 154 | * positive value is returned if the call fails. 155 | */ 156 | int nvme_host_create_queue (uint16_t qid); 157 | 158 | 159 | /** 160 | * Destroy a queue created by 'nvme_host_create_queue'. All allocated resources 161 | * are freed. 162 | * 163 | * @param qid - Queue ID to be destroyed. 164 | */ 165 | void nvme_host_destroy_queue (uint16_t qid); 166 | 167 | 168 | /** 169 | * Register a server network interface. This function must be called after 170 | * 'nvme_init'. Call this function several times for multiple interfaces. 171 | * 172 | * @param addr - String containing the server IPv4 address. e.g "192.168.0.1". 173 | * @param port - 16-bit integer containing the server port. 174 | * @return - returns 0 in success and a negative value if NVMe is not 175 | * initialized. 176 | */ 177 | int nvme_host_add_server_iface (const char *addr, uint16_t port); 178 | 179 | /** 180 | * Reads data from an OX NVMe device. 181 | * 182 | * @param buf - Memory pointer where data will be read into. 183 | * @param size - Data size to be read starting at 'slba'. 184 | * @param slba - Starting logical block address (each logical block is 185 | * 4096 bytes in size, the only size supported for now). 186 | * @param cb - user defined callback function for command completion. 187 | * @param ctx - user defined context returned by the callback function. 188 | * @return returns 0 if the read has been submitted, or a negative value upon 189 | * failure. 190 | */ 191 | int nvmeh_read (uint8_t *buf, uint64_t size, uint64_t slba, 192 | nvme_host_callback_fn *cb, void *ctx); 193 | 194 | 195 | /** 196 | * Writes data to an OX NVMe device. 197 | * 198 | * @param buf - Memory pointer containing the date to be written. 199 | * @param size - Data size to be written starting at 'slba'. 200 | * @param slba - Starting logical block address (each logical block is 201 | * 4096 bytes in size, the only size supported for now). 202 | * @param cb - user defined callback function for command completion. 203 | * @param ctx - user defined context returned by the callback function. 204 | * @return returns 0 if the write has been submitted, or a negative value upon 205 | * failure. 206 | */ 207 | int nvmeh_write (uint8_t *buf, uint64_t size, uint64_t slba, 208 | nvme_host_callback_fn *cb, void *ctx); 209 | 210 | /** 211 | * Identify controller command. 212 | * 213 | * @param buf - Memory pointer to store the data structure. Must be 4k. 214 | */ 215 | int nvmeh_identify_ctrl (struct nvme_id_ctrl *buf); 216 | 217 | /** 218 | * Identify namespace command. 219 | * 220 | * @param buf - Memory pointer to store the data structure. Must be 4k. 221 | * @param nsid - Namespace ID 222 | */ 223 | int nvmeh_identify_ns (struct nvme_id_ns *buf, uint32_t nsid); 224 | 225 | #endif /* NVME_HOST_H */ 226 | 227 | -------------------------------------------------------------------------------- /include/nvmef.h: -------------------------------------------------------------------------------- 1 | /* OX: Open-Channel NVM Express SSD Controller 2 | * 3 | * - NVM over Fabrics Specification (header) 4 | * 5 | * Copyright 2016 IT University of Copenhagen 6 | * 7 | * Modified by Ivan Luiz Picoli 8 | * This file has been modified from the QEMU project NVMe device. 9 | * 10 | * Licensed under the Apache License, Version 2.0 (the "License"); 11 | * you may not use this file except in compliance with the License. 12 | * You may obtain a copy of the License at 13 | * 14 | * http://www.apache.org/licenses/LICENSE-2.0 15 | * 16 | * Unless required by applicable law or agreed to in writing, software 17 | * distributed under the License is distributed on an "AS IS" BASIS, 18 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | * See the License for the specific language governing permissions and 20 | * limitations under the License. 21 | */ 22 | 23 | #ifndef NVMEF_H 24 | #define NVMEF_H 25 | 26 | /* Timeout 10 sec */ 27 | #define NVMEF_RETRY 50000 28 | #define NVMEF_RETRY_DELAY 200 29 | 30 | #define NVMEF_ICDOFF 16 31 | #define NVMEF_SGL_SZ 256 /* Space for SGL after command in capsule */ 32 | #define NVMEF_DATA_OFF 320 /* Offset where data starts in capsule */ 33 | 34 | enum { 35 | NVMEF_CMD_PROP_SET = 0x00, 36 | NVMEF_CMD_CONNECT = 0x01, 37 | NVMEF_CMD_PROP_GET = 0x04, 38 | NVMEF_CMD_AUTH_SEND = 0x05, 39 | NVMEF_CMD_AUTH_RECV = 0x06 40 | }; 41 | 42 | typedef struct NvmefCmd { 43 | uint8_t opcode; 44 | uint8_t rsvd1; 45 | uint16_t cid; 46 | uint8_t fctype; 47 | uint8_t rsvd2[35]; 48 | uint8_t fabrics[24]; 49 | } NvmefCmd; 50 | 51 | typedef struct NvmefConnect { 52 | uint8_t opcode; 53 | uint8_t rsvd1; 54 | uint16_t cid; 55 | uint8_t fctype; 56 | uint8_t rsvd2[19]; 57 | uint8_t sgl1[16]; 58 | uint16_t recfmt; 59 | uint16_t qid; 60 | uint16_t sqsize; 61 | uint8_t cattr; 62 | uint8_t rsvd3; 63 | uint32_t kato; 64 | uint8_t rsvd4[12]; 65 | } NvmefConnect; 66 | 67 | typedef struct NvmefConnectData { 68 | uint8_t hostid[16]; 69 | uint16_t cntlid; 70 | uint8_t rsvd1[238]; 71 | uint8_t subnqn[256]; 72 | uint8_t hostnqn[256]; 73 | uint8_t rsvd2[256]; 74 | } NvmefConnectData; 75 | 76 | typedef struct NvmefPropSetGet { 77 | uint8_t opcode; 78 | uint8_t rsvd1; 79 | uint16_t cid; 80 | uint8_t fctype; 81 | uint8_t rsvd2[35]; 82 | struct { 83 | uint8_t size : 3; 84 | uint8_t rsvd2 : 5; 85 | } attrib; 86 | uint8_t rsvd3[3]; 87 | uint32_t ofst; 88 | union { 89 | struct { 90 | uint64_t val; 91 | uint64_t rsvd4; 92 | } set; 93 | uint8_t rsvd4[16]; 94 | }; 95 | } NvmefPropSetGet; 96 | 97 | #endif /* NVMEF_H */ -------------------------------------------------------------------------------- /include/ox-cmdline.h: -------------------------------------------------------------------------------- 1 | /* OX: Open-Channel NVM Express SSD Controller 2 | * 3 | * - Command Line 4 | * 5 | * Copyright 2017 IT University of Copenhagen 6 | * 7 | * Written by Frey Alfredsson 8 | * 9 | * Licensed under the Apache License, Version 2.0 (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * http://www.apache.org/licenses/LICENSE-2.0 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | */ 21 | 22 | extern struct core_struct core; 23 | 24 | typedef struct ox_cmd ox_cmd; 25 | typedef int (*ox_cmdline_func_t)(char *line, ox_cmd *cmd); 26 | 27 | typedef struct ox_cmd { 28 | char *command; 29 | struct ox_cmd *next; 30 | ox_cmdline_func_t func; 31 | void *value; 32 | char *short_help; 33 | char *help; 34 | } ox_cmd; 35 | 36 | int cmdline_start_output (char *line, ox_cmd *cmd); 37 | int cmdline_stop_output (char *line, ox_cmd *cmd); 38 | int cmdline_set_debug (char *line, ox_cmd *cmd); 39 | int cmdline_show_debug (char *line, ox_cmd *cmd); 40 | int cmdline_show_mq_status (char *line, ox_cmd *cmd); 41 | int cmdline_show_memory (char *line, ox_cmd *cmd); 42 | int cmdline_show_io (char *line, ox_cmd *cmd); 43 | int cmdline_show_gc (char *line, ox_cmd *cmd); 44 | int cmdline_show_recovery (char *line, ox_cmd *cmd); 45 | int cmdline_show_checkpoint (char *line, ox_cmd *cmd); 46 | int cmdline_show_all (char *line, ox_cmd *cmd); 47 | int cmdline_show_reset (char *line, ox_cmd *cmd); 48 | int cmdline_admin (char *line, ox_cmd *cmd); 49 | int cmdline_exit (char *line, ox_cmd *cmd); 50 | 51 | char **command_completion (const char *text, int start, int end); 52 | char *command_generator (const char *text, int state); 53 | void find_command (char *line, ox_cmd **p_cmd, ox_cmd *p_cmd_list[]); 54 | void remove_trailing_whitespace (char *line); 55 | int is_whitespace (const char *text); 56 | int is_help_cmd (char *line); -------------------------------------------------------------------------------- /include/ox-lightnvm.h: -------------------------------------------------------------------------------- 1 | /* OX: Open-Channel NVM Express SSD Controller 2 | * 3 | * - LightNVM (header) 4 | * 5 | * Copyright 2016 IT University of Copenhagen 6 | * 7 | * Written by Ivan Luiz Picoli 8 | * 9 | * Licensed under the Apache License, Version 2.0 (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * http://www.apache.org/licenses/LICENSE-2.0 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | */ 21 | 22 | #ifndef LIGHTNVM_H 23 | #define LIGHTNVM_H 24 | 25 | #include 26 | 27 | #define LNVM_MAX_SEC_RQ 64 28 | #define LNVM_MTYPE 0 29 | #define LNVM_FMTYPE 0 30 | #define LNVM_VER_ID 1 31 | #define LNVM_DOM 0x0 32 | #define LNVM_CAP 0x3 33 | #define LNVM_READ_L2P 0x1 34 | #define LNVM_BB_GEN_FREQ 0x0 35 | #define LNVM_ERR_WRITE 0x0 36 | #define LNVM_VMNT 0 37 | #define LNVM_CGRPS 1 38 | 39 | #define LNVM_TRDT 1600 40 | #define LNVM_TRDM 1600 41 | #define LNVM_TPRT 800 42 | #define LNVM_TPRM 800 43 | #define LNVM_TBET 2400 44 | #define LNVM_TBEM 2400 45 | 46 | #define LNVM_MAX_GRPS_PR_IDENT (20) 47 | #define LNVM_FEAT_EXT_START 64 48 | #define LNVM_FEAT_EXT_END 127 49 | #define LNVM_PBA_UNMAPPED UINT64_MAX 50 | #define LNVM_LBA_UNMAPPED UINT64_MAX 51 | 52 | enum LnvmAdminCommands { 53 | LNVM_ADM_CMD_IDENTITY = 0xe2, 54 | LNVM_ADM_CMD_GET_L2P_TBL = 0xea, 55 | LNVM_ADM_CMD_GET_BB_TBL = 0xf2, 56 | LNVM_ADM_CMD_SET_BB_TBL = 0xf1, 57 | }; 58 | 59 | enum LnvmDmCommands { 60 | LNVM_CMD_HYBRID_WRITE = 0x81, 61 | LNVM_CMD_HYBRID_READ = 0x02, 62 | LNVM_CMD_PHYS_WRITE = 0x91, 63 | LNVM_CMD_PHYS_READ = 0x92, 64 | LNVM_CMD_ERASE_SYNC = 0x90, 65 | }; 66 | 67 | enum lnvm_bbt_state { 68 | LNVM_BBT_FREE = 0x0, // Block is free AKA good 69 | LNVM_BBT_BAD = 0x1, // Block is bad 70 | LNVM_BBT_GBAD = 0x2, // Block has grown bad 71 | LNVM_BBT_DMRK = 0x4, // Block has been marked by device side 72 | LNVM_BBT_HMRK = 0x8 // Block has been marked by host side 73 | }; 74 | 75 | typedef struct LnvmIdAddrFormat { 76 | uint8_t ch_offset; 77 | uint8_t ch_len; 78 | uint8_t lun_offset; 79 | uint8_t lun_len; 80 | uint8_t pln_offset; 81 | uint8_t pln_len; 82 | uint8_t blk_offset; 83 | uint8_t blk_len; 84 | uint8_t pg_offset; 85 | uint8_t pg_len; 86 | uint8_t sect_offset; 87 | uint8_t sect_len; 88 | uint8_t res[4]; 89 | } LnvmIdAddrFormat; 90 | 91 | typedef struct LnvmIdGroup { 92 | uint8_t mtype; 93 | uint8_t fmtype; 94 | uint16_t res16; 95 | uint8_t num_ch; 96 | uint8_t num_lun; 97 | uint8_t num_pln; 98 | uint8_t rsvd1; 99 | uint16_t num_blk; 100 | uint16_t num_pg; 101 | uint16_t fpg_sz; 102 | uint16_t csecs; 103 | uint16_t sos; 104 | uint16_t rsvd2; 105 | uint32_t trdt; 106 | uint32_t trdm; 107 | uint32_t tprt; 108 | uint32_t tprm; 109 | uint32_t tbet; 110 | uint32_t tbem; 111 | uint32_t mpos; 112 | uint32_t mccap; 113 | uint16_t cpar; 114 | uint8_t res[906]; 115 | } LnvmIdGroup; 116 | 117 | typedef struct LnvmIdCtrl { 118 | uint8_t ver_id; 119 | uint8_t vmnt; 120 | uint8_t cgrps; 121 | uint8_t res; 122 | uint32_t cap; 123 | uint32_t dom; 124 | struct LnvmIdAddrFormat ppaf; 125 | uint8_t resv[228]; 126 | LnvmIdGroup groups[4]; 127 | } LnvmIdCtrl; 128 | 129 | typedef struct LnvmParams { 130 | /* configurable device characteristics */ 131 | uint16_t pgs_per_blk; 132 | uint16_t sec_size; 133 | uint8_t secs_per_pg; 134 | uint8_t max_sec_per_rq; 135 | /* configurable parameters for LnvmIdGroup */ 136 | uint8_t mtype; 137 | uint8_t fmtype; 138 | uint8_t num_ch; 139 | uint8_t num_pln; 140 | uint8_t num_lun; 141 | uint16_t num_blk; 142 | /* calculated values */ 143 | uint32_t sec_per_phys_pl; 144 | uint32_t sec_per_log_pl; 145 | uint32_t sec_per_blk; 146 | uint32_t sec_per_lun; 147 | uint32_t total_secs; 148 | } LnvmParams; 149 | 150 | typedef struct LnvmGetL2PTbl { 151 | uint8_t opcode; 152 | uint8_t flags; 153 | uint16_t cid; 154 | uint32_t nsid; 155 | uint32_t rsvd1[4]; 156 | uint64_t prp1; 157 | uint64_t prp2; 158 | uint64_t slba; 159 | uint32_t nlb; 160 | uint16_t rsvd2[6]; 161 | } LnvmGetL2PTbl; 162 | 163 | typedef struct LnvmGetBBTbl { 164 | uint8_t opcode; 165 | uint8_t flags; 166 | uint16_t cid; 167 | uint32_t nsid; 168 | uint64_t rsvd1[2]; 169 | uint64_t prp1; 170 | uint64_t prp2; 171 | uint64_t spba; 172 | uint32_t rsvd4[4]; // DW15, 14, 13, 12 173 | } LnvmGetBBTbl; 174 | 175 | typedef struct LnvmSetBBTbl { 176 | uint8_t opcode; 177 | uint8_t flags; 178 | uint16_t cid; 179 | uint32_t nsid; 180 | uint64_t rsvd1[2]; 181 | uint64_t prp1; 182 | uint64_t prp2; 183 | uint64_t spba; 184 | uint16_t nlb; 185 | uint8_t value; 186 | uint8_t rsvd3; 187 | uint32_t rsvd4[3]; 188 | } LnvmSetBBTbl; 189 | 190 | typedef struct LnvmBBTbl { 191 | uint8_t tblid[4]; 192 | uint16_t verid; 193 | uint16_t revid; 194 | uint32_t rvsd1; 195 | uint32_t tblks; 196 | uint32_t tfact; 197 | uint32_t tgrown; 198 | uint32_t tdresv; 199 | uint32_t thresv; 200 | uint32_t rsvd2[8]; 201 | uint8_t blk[0]; 202 | } LnvmBBTbl; 203 | 204 | typedef struct LnvmRwCmd { 205 | uint8_t opcode; 206 | uint8_t flags; 207 | uint16_t cid; 208 | uint32_t nsid; 209 | uint64_t rsvd2; 210 | uint64_t metadata; 211 | uint64_t prp1; 212 | uint64_t prp2; 213 | uint64_t spba; 214 | uint16_t nlb; 215 | uint16_t control; 216 | uint32_t dsmgmt; 217 | uint64_t slba; 218 | } LnvmRwCmd; 219 | 220 | typedef struct LnvmCtrl { 221 | LnvmParams params; 222 | LnvmIdCtrl id_ctrl; 223 | uint8_t bb_gen_freq; 224 | uint32_t err_write; 225 | uint32_t err_write_cnt; 226 | } LnvmCtrl; 227 | 228 | typedef struct NvmeCtrl NvmeCtrl; 229 | 230 | void lnvm_set_default (LnvmCtrl *ctrl); 231 | uint8_t lnvm_dev (NvmeCtrl *n); 232 | void lightnvm_exit(NvmeCtrl *n); 233 | int lnvm_init(NvmeCtrl *n); 234 | void lnvm_init_id_ctrl(LnvmIdCtrl *ln_id); 235 | 236 | #endif /* LIGHTNVM_H */ -------------------------------------------------------------------------------- /include/ox-mq.h: -------------------------------------------------------------------------------- 1 | /* OX: Open-Channel NVM Express SSD Controller 2 | * 3 | * - Multi-Queue Support for Parallel I/O (header) 4 | * 5 | * Copyright 2017 IT University of Copenhagen 6 | * 7 | * Written by Ivan Luiz Picoli 8 | * 9 | * Licensed under the Apache License, Version 2.0 (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * http://www.apache.org/licenses/LICENSE-2.0 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | */ 21 | 22 | #ifndef OX_MQ_H 23 | #define OX_MQ_H 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #define OX_MQ_MAX_QUEUES 0x4000 32 | #define OX_MQ_MAX_Q_DEPTH 0x80000 33 | 34 | enum { 35 | OX_MQ_FREE = 1, 36 | OX_MQ_QUEUED, 37 | OX_MQ_WAITING, 38 | OX_MQ_TIMEOUT, 39 | OX_MQ_TIMEOUT_COMPLETED, 40 | OX_MQ_TIMEOUT_BACK 41 | }; 42 | 43 | struct oxmq_output_row; 44 | struct ox_mq_entry { 45 | void *opaque; 46 | uint32_t qid; 47 | uint8_t status; 48 | struct timeval wtime; /* timestamp for timeout */ 49 | uint8_t is_ext; /* if > 0, allocated due timeout */ 50 | TAILQ_ENTRY(ox_mq_entry) entry; 51 | LIST_ENTRY(ox_mq_entry) ext_entry; 52 | pthread_mutex_t entry_mutex; 53 | struct oxmq_output_row *out_row; 54 | }; 55 | 56 | /* Keeps a set of counters related to the multi-queue */ 57 | struct ox_mq_stats { 58 | u_atomic_t sq_free; 59 | u_atomic_t sq_used; 60 | u_atomic_t sq_wait; 61 | u_atomic_t cq_free; 62 | u_atomic_t cq_used; 63 | u_atomic_t ext_list; /* extended entry list size */ 64 | u_atomic_t timeout; /* total timeout entries */ 65 | u_atomic_t to_back; /* timeout entries asked for a late completion */ 66 | }; 67 | 68 | typedef void (ox_mq_sq_fn)(struct ox_mq_entry *); 69 | 70 | /* void * is the pointer to the opaque user entry */ 71 | typedef void (ox_mq_cq_fn)(void *); 72 | 73 | /* void ** is an array of timeout opaque entries, int is the array size */ 74 | typedef void (ox_mq_to_fn)(void **, int); 75 | 76 | /* Fill the statistics rows for file output */ 77 | typedef void (ox_mq_set_output_fn)(struct oxmq_output_row *row, void *opaque); 78 | 79 | struct ox_mq_queue { 80 | pthread_mutex_t sq_free_mutex; 81 | pthread_mutex_t cq_free_mutex; 82 | pthread_mutex_t sq_used_mutex; 83 | pthread_mutex_t cq_used_mutex; 84 | pthread_mutex_t sq_wait_mutex; 85 | struct ox_mq_entry *sq_entries; 86 | struct ox_mq_entry *cq_entries; 87 | TAILQ_HEAD (sq_free_head, ox_mq_entry) sq_free; 88 | TAILQ_HEAD (sq_used_head, ox_mq_entry) sq_used; 89 | TAILQ_HEAD (sq_wait_head, ox_mq_entry) sq_wait; 90 | TAILQ_HEAD (cq_free_head, ox_mq_entry) cq_free; 91 | TAILQ_HEAD (cq_used_head, ox_mq_entry) cq_used; 92 | ox_mq_sq_fn *sq_fn; 93 | ox_mq_cq_fn *cq_fn; 94 | pthread_mutex_t sq_cond_m; 95 | pthread_mutex_t cq_cond_m; 96 | pthread_cond_t sq_cond; 97 | pthread_cond_t cq_cond; 98 | pthread_t sq_tid; 99 | pthread_t cq_tid; 100 | uint8_t running; /* if 0, kill threads */ 101 | struct ox_mq_stats stats; 102 | struct ox_mq *mq; 103 | uint16_t qid; 104 | }; 105 | 106 | #define OX_MQ_TO_COMPLETE (1 << 0) /* Complete request after timeout */ 107 | #define OX_MQ_CPU_AFFINITY (1 << 1) /* Forces all threads to run a specific core */ 108 | 109 | struct oxmq_output_row { 110 | /* Should be set by user in 'ox_mq_set_output_fn' function */ 111 | uint64_t lba; 112 | uint16_t ch; 113 | uint16_t lun; 114 | uint32_t blk; 115 | uint32_t pg; 116 | uint8_t pl; 117 | uint8_t sec; 118 | uint8_t type; 119 | uint8_t failed; 120 | uint8_t datacmp; 121 | uint32_t size; 122 | 123 | /* Filled automatically by ox-mq */ 124 | uint64_t seq; 125 | uint64_t node_seq; 126 | uint16_t node_id; 127 | uint64_t tstart; 128 | uint64_t tend; 129 | uint64_t ulat; 130 | TAILQ_ENTRY(oxmq_output_row) entry; 131 | }; 132 | 133 | struct oxmq_output_tq { 134 | uint64_t row_off; 135 | struct oxmq_output_row *rows; 136 | TAILQ_HEAD(out_list,oxmq_output_row) out_head; 137 | }; 138 | struct oxmq_output { 139 | uint64_t id; 140 | uint32_t nodes; 141 | char name[64]; 142 | pthread_mutex_t file_mutex; 143 | uint64_t sequence; 144 | uint64_t *node_seq; 145 | struct oxmq_output_tq *queues; 146 | }; 147 | 148 | struct ox_mq_config { 149 | char name[40]; 150 | uint32_t n_queues; 151 | uint32_t q_size; 152 | ox_mq_sq_fn *sq_fn; /* submission queue consumer */ 153 | ox_mq_cq_fn *cq_fn; /* completion queue consumer */ 154 | ox_mq_to_fn *to_fn; /* timeout call */ 155 | ox_mq_set_output_fn *output_fn; /* Fill output data */ 156 | uint64_t to_usec; /* timeout in microseconds */ 157 | uint8_t flags; 158 | 159 | /* Used if OX_MQ_CPU_AFFINITY flag is set, max of 64 CPUs */ 160 | uint64_t sq_affinity[OX_MQ_MAX_QUEUES]; 161 | uint64_t cq_affinity[OX_MQ_MAX_QUEUES]; 162 | }; 163 | 164 | struct ox_mq { 165 | LIST_ENTRY(ox_mq) entry; 166 | struct ox_mq_queue *queues; 167 | struct ox_mq_config *config; 168 | pthread_t to_tid; /* timeout thread */ 169 | LIST_HEAD(oxmq_ext, ox_mq_entry) ext_list; /* new allocated entries */ 170 | struct ox_mq_stats stats; 171 | struct oxmq_output *output; 172 | uint8_t stop; /* Set to 1, stop threads */ 173 | }; 174 | 175 | struct ox_mq *ox_mq_init (struct ox_mq_config *); 176 | void ox_mq_destroy (struct ox_mq *); 177 | int ox_mq_submit_req (struct ox_mq *, uint32_t, void *); 178 | int ox_mq_complete_req (struct ox_mq *, struct ox_mq_entry *); 179 | void ox_mq_show_mq (struct ox_mq *); 180 | void ox_mq_show_all (void); 181 | struct ox_mq *ox_mq_get (const char *); 182 | int ox_mq_used_count (struct ox_mq *, uint16_t qid); 183 | int ox_mq_get_status (struct ox_mq *, struct ox_mq_stats *, 184 | uint16_t qid); 185 | void ox_mq_output_start (void); 186 | void ox_mq_output_stop (void); 187 | struct oxmq_output_row *ox_mq_output_new (struct oxmq_output *output, 188 | int node_id); 189 | void ox_mq_output_flush (struct oxmq_output *output); 190 | void ox_mq_output_exit (struct oxmq_output *output); 191 | struct oxmq_output *ox_mq_output_init (uint64_t id, const char *name, 192 | uint32_t nodes); 193 | 194 | #endif /* OX_MQ_H */ -------------------------------------------------------------------------------- /include/ox-uatomic.h: -------------------------------------------------------------------------------- 1 | #ifndef _UATOMIC_H 2 | #define _UATOMIC_H 3 | 4 | /** 5 | * Atomic type. 6 | */ 7 | 8 | typedef struct { 9 | volatile int counter; 10 | } u_atomic_t; 11 | 12 | #define U_ATOMIC_INIT(i) { (i) } 13 | #define U_ATOMIC_INIT_RUNTIME(i) (i) 14 | 15 | /** 16 | * Read atomic variable 17 | * @param v pointer of type atomic_t 18 | * 19 | * Atomically reads the value of @v. 20 | */ 21 | #define u_atomic_read(v) ((v)->counter) 22 | 23 | /** 24 | * Set atomic variable 25 | * @param v pointer of type atomic_t 26 | * @param i required value 27 | */ 28 | #define u_atomic_set(v,i) (((v)->counter) = (i)) 29 | 30 | /** 31 | * Add to the atomic variable 32 | * @param i integer value to add 33 | * @param v pointer of type atomic_t 34 | */ 35 | static inline void u_atomic_add( int i, u_atomic_t *v ) 36 | { 37 | (void)__sync_add_and_fetch(&v->counter, i); 38 | } 39 | 40 | /** 41 | * Subtract the atomic variable 42 | * @param i integer value to subtract 43 | * @param v pointer of type atomic_t 44 | * 45 | * Atomically subtracts @i from @v. 46 | */ 47 | static inline void u_atomic_sub( int i, u_atomic_t *v ) 48 | { 49 | (void)__sync_sub_and_fetch(&v->counter, i); 50 | } 51 | 52 | /** 53 | * Subtract value from variable and test result 54 | * @param i integer value to subtract 55 | * @param v pointer of type atomic_t 56 | * 57 | * Atomically subtracts @i from @v and returns 58 | * true if the result is zero, or false for all 59 | * other cases. 60 | */ 61 | static inline int u_atomic_sub_and_test( int i, u_atomic_t *v ) 62 | { 63 | return !(__sync_sub_and_fetch(&v->counter, i)); 64 | } 65 | 66 | /** 67 | * Increment atomic variable 68 | * @param v pointer of type atomic_t 69 | * 70 | * Atomically increments @v by 1. 71 | */ 72 | static inline void u_atomic_inc( u_atomic_t *v ) 73 | { 74 | (void)__sync_fetch_and_add(&v->counter, 1); 75 | } 76 | 77 | /** 78 | * @brief decrement atomic variable 79 | * @param v: pointer of type atomic_t 80 | * 81 | * Atomically decrements @v by 1. Note that the guaranteed 82 | * useful range of an atomic_t is only 24 bits. 83 | */ 84 | static inline void u_atomic_dec( u_atomic_t *v ) 85 | { 86 | (void)__sync_fetch_and_sub(&v->counter, 1); 87 | } 88 | 89 | /** 90 | * @brief Decrement and test 91 | * @param v pointer of type atomic_t 92 | * 93 | * Atomically decrements @v by 1 and 94 | * returns true if the result is 0, or false for all other 95 | * cases. 96 | */ 97 | static inline int u_atomic_dec_and_test( u_atomic_t *v ) 98 | { 99 | return !(__sync_sub_and_fetch(&v->counter, 1)); 100 | } 101 | 102 | /** 103 | * @brief Increment and test 104 | * @param v pointer of type atomic_t 105 | * 106 | * Atomically increments @v by 1 107 | * and returns true if the result is zero, or false for all 108 | * other cases. 109 | */ 110 | static inline int u_atomic_inc_and_test( u_atomic_t *v ) 111 | { 112 | return !(__sync_add_and_fetch(&v->counter, 1)); 113 | } 114 | 115 | /** 116 | * @brief add and test if negative 117 | * @param v pointer of type atomic_t 118 | * @param i integer value to add 119 | * 120 | * Atomically adds @i to @v and returns true 121 | * if the result is negative, or false when 122 | * result is greater than or equal to zero. 123 | */ 124 | static inline int u_atomic_add_negative( int i, u_atomic_t *v ) 125 | { 126 | return (__sync_add_and_fetch(&v->counter, i) < 0); 127 | } 128 | 129 | #endif /* UATOMIC_H */ 130 | -------------------------------------------------------------------------------- /mmgr/file-be/file-be.h: -------------------------------------------------------------------------------- 1 | /* OX: Open-Channel NVM Express SSD Controller 2 | * 3 | * - File Backend: File backed Media Manager 4 | * 5 | * Copyright 2024 University of Copenhagen 6 | * 7 | * Written by Ivan Luiz Picoli 8 | * 9 | * Licensed under the Apache License, Version 2.0 (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * http://www.apache.org/licenses/LICENSE-2.0 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | */ 21 | 22 | #ifndef FILE_BE_H 23 | #define FILE_BE_H 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | #define FILE_MEM_ERROR 0 30 | #define FILE_MEM_OK 1 31 | #define FILE_SECOND 1000000 /* from u-seconds */ 32 | 33 | #define FILE_CHIP_COUNT 8 34 | #define FILE_VIRTUAL_LUNS 4 35 | #define FILE_BLOCK_COUNT 64 36 | #define FILE_PAGE_COUNT 64 37 | #define FILE_SECTOR_COUNT 4 38 | #define FILE_PLANE_COUNT 2 39 | #define FILE_PAGE_SIZE 0x4000 40 | #define FILE_SECTOR_SIZE 0x1000 41 | #define FILE_SEC_OOB_SIZE 0x10 42 | #define FILE_OOB_SIZE (FILE_SEC_OOB_SIZE * FILE_SECTOR_COUNT) 43 | 44 | #define FILE_DMA_SLOT_CH 32 45 | #define FILE_DMA_SLOT_INDEX (FILE_DMA_SLOT_CH * FILE_CHIP_COUNT) 46 | #define FILE_DMA_READ 0x1 47 | #define FILE_DMA_WRITE 0x2 48 | 49 | /* should be user-defined */ 50 | #define FILE_BLK_LIFE 5000 51 | #define FILE_RSV_BLK 1 52 | 53 | #define FILE_READ_TIME 50 54 | #define FILE_WRITE_TIME 200 55 | #define FILE_ERASE_TIME 1200 56 | 57 | #define FILE_QUEUE_SIZE 2048 58 | #define FILE_QUEUE_TO 48000 59 | 60 | typedef struct FileBEStatus { 61 | uint8_t ready; /* 0x00-busy, 0x01-ready to use */ 62 | uint8_t active; 63 | uint64_t allocated_memory; 64 | } FileBEStatus; 65 | 66 | typedef struct FileBEPage { 67 | uint8_t state; /* 0x00-free, 0x01-alive, 0x02-invalid */ 68 | } FileBEPage; 69 | 70 | typedef struct FileBEBlock { 71 | uint32_t id; 72 | uint16_t life; /* available erases before dying */ 73 | uint32_t file_id; 74 | uint64_t file_offset; 75 | FileBEPage *next_pg; 76 | FileBEPage *pages; 77 | } FileBEBlock; 78 | 79 | typedef struct FileBELun { 80 | FileBEBlock *blk_offset; 81 | } FileBELun; 82 | 83 | typedef struct FileBECh { 84 | FileBELun *lun_offset; 85 | } FileBECh; 86 | 87 | typedef struct FileBECtrl { 88 | FileBEStatus status; 89 | FileBEBlock *blocks; 90 | FileBELun *luns; 91 | FileBECh *channels; 92 | struct ox_mq *mq; 93 | uint8_t *edma; /* emergency DMA buffer for timeout requests */ 94 | } FileBECtrl; 95 | 96 | struct fbe_dma { 97 | uint8_t *virt_addr; 98 | uint32_t prp_index; 99 | uint8_t status; /* nand status */ 100 | }; 101 | 102 | #endif /* FILE_BE_H */ 103 | -------------------------------------------------------------------------------- /mmgr/fpga-3.0.1/fpga-3_0_1.h: -------------------------------------------------------------------------------- 1 | #ifndef DFC_NAND_H 2 | #define DFC_NAND_H 3 | 4 | #include 5 | #include 6 | 7 | #define NAND_PAGE_COUNT 512 8 | #define NAND_SECTOR_COUNT 4 9 | #define NAND_SECTOR_SIZE 0x1000 10 | #define NAND_OOB_SIZE 0x400 11 | #define NAND_EXPOSED_SEC_OOB 0x10 12 | #define NAND_EXPOSED_OOB (NAND_EXPOSED_SEC_OOB * NAND_SECTOR_COUNT) 13 | 14 | /* We assume LUNs * TARGETs for total of LUNs */ 15 | #define NAND_VIRTUAL_LUNS 4 16 | 17 | #define DFCNAND_RESV_BLK 0 18 | #define DFCNAND_RESV_BLK_COUNT 1 19 | 20 | #define DFCNAND_DMA_SLOT_INDEX 64 21 | 22 | #define DFCNAND_RDY_BSY_ON (1 << 3) 23 | #define DFCNAND_RDY_BSY_OFF 0x0 24 | 25 | #define DFCNAND_LS2_DMA_DATA (1 << 0) 26 | #define DFCNAND_LS2_DMA_OOB (1 << 1) 27 | 28 | #define DFCNAND_QUEUE_TO 200000 29 | 30 | enum DFCNAND_COMMAND_ID { 31 | DFCNAND_PAGE_PROG = 0xA, 32 | DFCNAND_PAGE_READ = 0x1E, 33 | DFCNAND_RESET = 0x1, 34 | DFCNAND_READ_STATUS = 0x14, 35 | DFCNAND_BLOCK_ERASE = 0x5, 36 | DFCNAND_CHANGE_WRITE_COL = 0xC, 37 | DFCNAND_SET_FEATURE = 0x9, 38 | }; 39 | 40 | enum { 41 | DFCNAND_READ_IO = 1, 42 | DFCNAND_READ_OOB = 2, 43 | DFCNAND_WRITE = 3, 44 | DFCNAND_READ_STS = 4, 45 | DFCNAND_BAD_BLK = 5, 46 | DFCNAND_OTHERS = 6, 47 | DFCNAND_ERASE = 7, 48 | DFCNAND_END_DESC = 8, 49 | }; 50 | 51 | struct dfcnand_io { 52 | uint8_t *virt_addr; 53 | int prp_index; 54 | uint8_t cmd_type; 55 | struct nvm_mmgr_io_cmd *nvm_mmgr_io; 56 | void *mq_req; 57 | uint8_t rdy_bsy; /* if positive, waits for ready signal */ 58 | uint8_t local_dma; /* if > 0, DMA to LS2 memory */ 59 | }; 60 | 61 | #endif /* DFC_NAND_H */ -------------------------------------------------------------------------------- /mmgr/fpga-3.0.1/nand_dma.h: -------------------------------------------------------------------------------- 1 | /* Modifications from original library: 2 | * M - Modified line 3 | * N - New line 4 | * R - Removed line */ 5 | 6 | #ifndef __NAND_DMA__ 7 | #define __NAND_DMA__ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | /* M */#include"../../include/ox-uatomic.h" 15 | /* N */#include "fpga-3_0_1.h" 16 | 17 | /*Uncomment this to enable Interrupt, the interrupt support is added in 18 | * 03.00.01 iSSD SDK*/ 19 | #define INTR 20 | 21 | 22 | #define TBL_COUNT 8 23 | #define DESC_PER_TBL 8 24 | #define DESC_SIZE 0x40 25 | #define NAND_CSR_OFFSET 0x10 26 | #define NAND_TBL_OFFSET 0x800 27 | #define TBL_SZ_SHIFT 0x1 28 | #define TABLE_SHIFT 0x800 29 | 30 | #define NAND_PAGES_PER_BLOCK 512 31 | /* M */#define NAND_BLOCK_COUNT /*2096*/ 1024 32 | #define NAND_PLANE_COUNT 2 33 | #define NAND_TARGET_COUNT 2 34 | #define NAND_LUN_COUNT 2 35 | #define NAND_CHIP_COUNT 8 36 | #define NAND_PAGE_SIZE 0x4000 37 | 38 | #define SUCCESS 0 39 | #define FAILURE -1 40 | 41 | #define TOTAL_DESC (TBL_COUNT * DESC_PER_TBL) 42 | #define CIRC_INCR(idx, limit) (idx = (idx + 1) % limit) 43 | 44 | #define SET_LEN_TRGT(len,trgt) ((len<<16) | trgt) 45 | 46 | struct timezone zone; 47 | 48 | /* N */ struct dfcnand_io; 49 | 50 | void *io_completer(); 51 | struct list_head* list_head_bad; 52 | 53 | unsigned long long int phy_addr[70]; 54 | uint8_t *virt_addr[70]; 55 | 56 | enum NAND_COMMAND_ID { 57 | PAGE_PROG = 0xA, 58 | PAGE_READ = 0x1E, 59 | RESET = 0x1, 60 | READ_STATUS = 0x15, 61 | BLOCK_ERASE = 0x5, 62 | CHANGE_WRITE_COL = 0xC, 63 | SET_FEATURE = 0x9, 64 | }; 65 | 66 | enum DESC_TRACK_STAT { 67 | TRACK_IDX_FREE = 0, 68 | TRACK_IDX_USED = 1, 69 | }; 70 | 71 | enum { 72 | READ_IO = 1, 73 | READ_OOB = 2, 74 | WRITE = 3, 75 | READ_STS = 4, 76 | BAD_BLK = 5, 77 | OTHERS = 6, 78 | ERASE = 7, 79 | END_DESC = 8, 80 | }; 81 | 82 | typedef struct nand_cmd_struct { 83 | uint32_t row_addr; 84 | uint32_t trgt_col_addr; 85 | uint32_t db_LSB_0; 86 | uint32_t db_MSB_0; 87 | uint32_t db_LSB_1; 88 | uint32_t db_MSB_1; 89 | uint32_t db_LSB_2; 90 | uint32_t db_MSB_2; 91 | uint32_t db_LSB_3; 92 | uint32_t db_MSB_3; 93 | uint32_t len1_len0; 94 | uint32_t len3_len2; 95 | uint32_t oob_LSB; 96 | uint32_t oob_len_MSB; /*contains MSB of oob in 1st 20 bits and 97 | * oob-len in next 12 bits*/ 98 | uint32_t buffer_channel_id; /*unused*/ 99 | uint32_t control_fields; 100 | } nand_cmd_struct; 101 | 102 | typedef struct io_cmd { 103 | uint8_t chip; 104 | uint8_t target; 105 | uint8_t lun; 106 | uint16_t block; 107 | uint16_t page; 108 | uint8_t status; 109 | uint8_t mul_plane; 110 | uint16_t len[5]; 111 | uint16_t col_addr; 112 | uint64_t host_addr[5]; 113 | /* DFC Specific */ 114 | /* N */ struct dfcnand_io dfc_io; 115 | /* N */ struct nand_cmd_struct nand_cmd; 116 | } io_cmd; 117 | 118 | typedef struct table_size_reg { 119 | uint16_t table_size; 120 | uint32_t rsvd : 24; 121 | } tblsz_reg; 122 | 123 | typedef struct DescStatus { 124 | uint8_t *ptr; 125 | uint8_t req_type; 126 | uint8_t page_idx; 127 | void *req_ptr; 128 | uint64_t page_phy_addr; 129 | uint8_t *page_virt_addr; 130 | pthread_mutex_t available; 131 | volatile uint8_t valid; 132 | uint16_t idx; 133 | } DescStat; 134 | 135 | typedef struct nand_descriptor { 136 | uint32_t row_addr; 137 | uint32_t column_addr : 29; 138 | uint32_t target : 3; 139 | uint32_t data_buff0_LSB; 140 | uint32_t data_buff0_MSB; 141 | uint32_t data_buff1_LSB; 142 | uint32_t data_buff1_MSB; 143 | uint32_t data_buff2_LSB; 144 | uint32_t data_buff2_MSB; 145 | uint32_t data_buff3_LSB; 146 | uint32_t data_buff3_MSB; 147 | uint32_t length0 : 16; 148 | uint32_t length1 : 16; 149 | uint32_t length2 : 16; 150 | uint32_t length3 : 16; 151 | uint32_t oob_data_LSB; 152 | uint32_t oob_data_MSB : 20; 153 | uint32_t oob_length : 12; 154 | uint32_t chn_buff_id; 155 | uint32_t dir : 1; 156 | uint32_t no_data : 1; 157 | uint32_t ecc : 1; 158 | uint32_t command : 6; 159 | uint32_t irq_en : 1; 160 | uint32_t hold : 1; 161 | uint32_t dma_cmp : 1; 162 | uint32_t desc_id : 8; 163 | uint32_t OwnedByfpga : 1; 164 | uint32_t rdy_busy : 1; 165 | uint32_t chng_pln : 1; 166 | uint32_t rsvd : 9; 167 | } __attribute__((packed)) nand_descriptor; 168 | 169 | typedef struct csf_reg { 170 | uint32_t dir : 1; 171 | uint32_t no_data : 1; 172 | uint32_t ecc : 1; 173 | uint32_t command : 6; 174 | uint32_t irq_en : 1; 175 | uint32_t hold : 1; 176 | uint32_t dma_cmp : 1; 177 | uint32_t desc_id : 8; 178 | uint32_t OwnedByFpga : 1; 179 | uint32_t rsvd : 11; 180 | } __attribute__((packed)) csf_reg; 181 | 182 | typedef struct csr_reg { 183 | uint8_t desc_id; 184 | uint8_t err_code : 3; 185 | uint8_t start : 1; 186 | uint8_t loop : 1; 187 | uint8_t reset : 1; 188 | uint32_t rsvd : 18; 189 | } csr_reg; 190 | 191 | typedef struct DmaControl { 192 | DescStat DescSt[64]; 193 | uint16_t head_idx; 194 | uint16_t tail_idx; 195 | } Dma_Control; 196 | 197 | typedef struct Desc_Track { 198 | DescStat *DescSt_ptr; 199 | uint8_t is_used; 200 | } Desc_Track; 201 | 202 | static inline int mutex_init(pthread_mutex_t *mutex, 203 | const pthread_mutexattr_t *mutexattr) { 204 | return pthread_mutex_init(mutex, mutexattr); 205 | } 206 | 207 | static inline int mutex_destroy(pthread_mutex_t *mutex) { 208 | return pthread_mutex_destroy(mutex); 209 | } 210 | 211 | static inline int mutex_lock(pthread_mutex_t *mutex) { 212 | return pthread_mutex_lock(mutex); 213 | } 214 | 215 | static inline int mutex_trylock(pthread_mutex_t *mutex) { 216 | return pthread_mutex_trylock(mutex); 217 | } 218 | 219 | static inline int mutex_unlock(pthread_mutex_t *mutex) { 220 | return pthread_mutex_unlock(mutex); 221 | } 222 | 223 | typedef struct blk_status { 224 | uint8_t is_bb; /*Bad block indication byte*/ 225 | uint64_t erase_cnt; /*Erase count*/ 226 | } blk_status_t; 227 | 228 | typedef struct issd_ftl { 229 | uint8_t channel; 230 | uint16_t block; 231 | uint16_t page; 232 | } issd_ftl_t; 233 | 234 | uint8_t make_desc(nand_cmd_struct *cmd_struct, uint8_t tid, 235 | uint8_t req_type, void *req_ptr); 236 | 237 | int nand_page_prog(io_cmd *cmd_buf); 238 | 239 | int nand_page_read(io_cmd *cmd_buf); 240 | 241 | int nand_block_erase(io_cmd *cmd_buf); 242 | 243 | int nand_reset(); 244 | 245 | /* M */int dfcnand_callback(io_cmd *cmd); 246 | 247 | uint8_t nand_dm_init (void); 248 | uint8_t nand_dm_deinit (void); 249 | 250 | #endif -------------------------------------------------------------------------------- /mmgr/mmgr_common.c: -------------------------------------------------------------------------------- 1 | /* OX: Open-Channel NVM Express SSD Controller 2 | * 3 | * - Common Media Manager functions 4 | * 5 | * Copyright 2018 IT University of Copenhagen 6 | * 7 | * Written by Ivan Luiz Picoli 8 | * 9 | * Licensed under the Apache License, Version 2.0 (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * http://www.apache.org/licenses/LICENSE-2.0 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | static int mmgr_io_rsv_blk (struct nvm_channel *ch, uint8_t cmdtype, 27 | void *buf, uint16_t blk, uint16_t pl, uint16_t pg) 28 | { 29 | int ret = -1; 30 | struct nvm_mmgr_io_cmd *cmd = ox_malloc(sizeof(struct nvm_mmgr_io_cmd), 31 | OX_MEM_MMGR); 32 | if (!cmd) 33 | return EMEM; 34 | 35 | memset (cmd, 0, sizeof (struct nvm_mmgr_io_cmd)); 36 | cmd->ppa.g.blk = blk; 37 | cmd->ppa.g.pl = pl; 38 | cmd->ppa.g.ch = ch->ch_mmgr_id; 39 | 40 | /* TODO: Replicate among all LUNs in the channel */ 41 | cmd->ppa.g.lun = 0; 42 | 43 | cmd->ppa.g.pg = pg; 44 | 45 | ret = ox_submit_sync_io (ch, cmd, buf, cmdtype); 46 | 47 | ox_free (cmd, OX_MEM_MMGR); 48 | 49 | return ret; 50 | } 51 | 52 | static int mmgr_read_nvminfo (struct nvm_channel *ch) 53 | { 54 | int ret, pg; 55 | struct nvm_channel ch_a; 56 | void *buf; 57 | uint16_t buf_sz = ch->geometry->pg_size + ch->geometry->sec_oob_sz 58 | * ch->geometry->sec_per_pg; 59 | 60 | buf = ox_calloc(buf_sz * ch->geometry->n_of_planes, 1, OX_MEM_MMGR); 61 | if (!buf) 62 | return EMEM; 63 | 64 | pg = 0; 65 | do { 66 | memset (buf, 0, buf_sz * ch->geometry->n_of_planes); 67 | ret = mmgr_io_rsv_blk (ch, MMGR_READ_PL_PG, buf, 0, 0, pg); 68 | memcpy (&ch_a.nvm_info, buf, sizeof (ch_a.nvm_info)); 69 | 70 | if (ret || ch_a.i.in_use != NVM_CH_IN_USE) 71 | break; 72 | 73 | memcpy (&ch->nvm_info, &ch_a.nvm_info, sizeof (ch_a.nvm_info)); 74 | pg++; 75 | } while (pg < ch->geometry->pg_per_blk); 76 | 77 | if (ret) 78 | goto OUT; 79 | 80 | if (!pg) 81 | ret = mmgr_io_rsv_blk (ch, MMGR_ERASE_BLK, NULL, 0, 0, 0); 82 | 83 | OUT: 84 | ox_free(buf, OX_MEM_MMGR); 85 | return ret; 86 | } 87 | 88 | static int mmgr_flush_nvminfo (struct nvm_channel *ch) 89 | { 90 | int ret, pg; 91 | struct nvm_channel ch_a; 92 | void *buf; 93 | uint16_t buf_sz = ch->geometry->pg_size + ch->geometry->sec_oob_sz 94 | * ch->geometry->sec_per_pg; 95 | 96 | buf = ox_calloc(buf_sz * ch->geometry->n_of_planes, 1, OX_MEM_MMGR); 97 | if (!buf) 98 | return EMEM; 99 | 100 | pg = 0; 101 | do { 102 | memset (buf, 0, buf_sz * ch->geometry->n_of_planes); 103 | ret = mmgr_io_rsv_blk (ch, MMGR_READ_PL_PG, buf, 0, 0, pg); 104 | memcpy (&ch_a.nvm_info, buf, sizeof (ch_a.nvm_info)); 105 | 106 | if (ret || ch_a.i.in_use != NVM_CH_IN_USE) 107 | break; 108 | 109 | pg++; 110 | } while (pg < ch->geometry->pg_per_blk); 111 | 112 | if (ret) 113 | goto OUT; 114 | 115 | if (pg == ch->geometry->pg_per_blk) { 116 | if (mmgr_io_rsv_blk (ch, MMGR_ERASE_BLK, NULL, 0, 0, 0)) 117 | goto OUT; 118 | pg = 0; 119 | } 120 | 121 | memset (buf, 0, buf_sz * ch->geometry->n_of_planes); 122 | memcpy (buf, &ch->nvm_info, sizeof (ch->nvm_info)); 123 | 124 | ret = mmgr_io_rsv_blk (ch, MMGR_WRITE_PL_PG, buf, 0, 0, pg); 125 | if (ret) { 126 | pg = 0; 127 | if (mmgr_io_rsv_blk (ch, MMGR_ERASE_BLK, NULL, 0, 0, 0)) 128 | goto OUT; 129 | ret = mmgr_io_rsv_blk (ch, MMGR_WRITE_PL_PG, buf, 0, 0, pg); 130 | } 131 | 132 | OUT: 133 | ox_free(buf, OX_MEM_MMGR); 134 | return ret; 135 | } 136 | 137 | int mmgr_set_ch_info (struct nvm_channel *ch, uint16_t nc) 138 | { 139 | int i; 140 | 141 | for(i = 0; i < nc; i++) { 142 | if(mmgr_flush_nvminfo (&ch[i])) 143 | return -1; 144 | } 145 | 146 | return 0; 147 | } 148 | 149 | int mmgr_get_ch_info (struct nvm_mmgr *mmgr, struct nvm_channel *ch, 150 | uint16_t nc, uint16_t rsv_blks) 151 | { 152 | int i, n, pl, nsp = 0, trsv; 153 | struct nvm_ppa_addr *ppa; 154 | uint16_t npl; 155 | 156 | for(i = 0; i < nc; i++){ 157 | ch[i].ch_mmgr_id = i; 158 | ch[i].mmgr = mmgr; 159 | ch[i].geometry = mmgr->geometry; 160 | 161 | ch[i].ns_pgs = ch[i].geometry->lun_per_ch * 162 | ch[i].geometry->blk_per_lun * 163 | ch[i].geometry->n_of_planes * 164 | ch[i].geometry->pg_per_blk; 165 | 166 | npl = ch[i].geometry->n_of_planes; 167 | ch[i].mmgr_rsv = rsv_blks; 168 | trsv = ch[i].mmgr_rsv * npl; 169 | ch[i].mmgr_rsv_list = ox_malloc (trsv * sizeof(struct nvm_ppa_addr), 170 | OX_MEM_MMGR); 171 | 172 | if (!ch[i].mmgr_rsv_list) 173 | return EMEM; 174 | 175 | memset (ch[i].mmgr_rsv_list, 0, trsv * sizeof(struct nvm_ppa_addr)); 176 | 177 | for (n = 0; n < ch[i].mmgr_rsv; n++) { 178 | for (pl = 0; pl < npl; pl++) { 179 | ppa = &ch[i].mmgr_rsv_list[npl * n + pl]; 180 | ppa->g.ch = ch[i].ch_mmgr_id; 181 | ppa->g.lun = 0; 182 | ppa->g.blk = n; 183 | ppa->g.pl = pl; 184 | } 185 | } 186 | 187 | ch[i].ftl_rsv = 0; 188 | ch[i].ftl_rsv_list = ox_malloc (sizeof(struct nvm_ppa_addr), 189 | OX_MEM_MMGR); 190 | if (!ch[i].ftl_rsv_list) { 191 | ox_free (ch[i].mmgr_rsv_list, OX_MEM_MMGR); 192 | return EMEM; 193 | } 194 | 195 | if (mmgr_read_nvminfo (&ch[i])) { 196 | ox_free (ch[i].ftl_rsv_list, OX_MEM_MMGR); 197 | ox_free (ch[i].mmgr_rsv_list, OX_MEM_MMGR); 198 | return -1; 199 | } 200 | 201 | if (ch[i].i.in_use != NVM_CH_IN_USE) { 202 | ch[i].i.ns_id = 0x0; 203 | ch[i].i.ns_part = 0x0; 204 | ch[i].i.ftl_id = 0x0; 205 | ch[i].i.in_use = 0x0; 206 | } 207 | 208 | ch[i].tot_bytes = 0; 209 | ch[i].slba = 0; 210 | ch[i].elba = 0; 211 | nsp++; 212 | } 213 | 214 | return 0; 215 | } -------------------------------------------------------------------------------- /mmgr/ocssd-1.2/ocssd-1_2.h: -------------------------------------------------------------------------------- 1 | /* OX: Open-Channel NVM Express SSD Controller 2 | * 3 | * - Open-Channel SSD Media Manager (header) 4 | * 5 | * Copyright (c) 2018, Microsoft Research 6 | * Written by Ivan Luiz Picoli 7 | */ 8 | 9 | #ifndef MMGR_OCSSD_H 10 | #define MMGR_OCSSD_H 11 | 12 | #include 13 | 14 | #define OCSSD_FILE "/dev/nvme0n1\0" 15 | 16 | #define OCSSD_QUEUE_COUNT 128 17 | #define OCSSD_QUEUE_SIZE 2048 18 | #define OCSSD_QUEUE_TO 400000 19 | 20 | #define OCSSD_BLK_BOUND 4 21 | 22 | struct ocssd_ctrl { 23 | char dev_name[13]; 24 | struct nvm_dev *dev; 25 | const struct nvm_geo *geo; 26 | struct nvm_mmgr_geometry mmgr_geo; 27 | struct nvm_mmgr_ops mmgr_ops; 28 | struct nvm_mmgr mmgr; 29 | struct ox_mq *mq; 30 | }; 31 | 32 | #endif /* MMGR_OCSSD_H */ -------------------------------------------------------------------------------- /mmgr/volt/volt.h: -------------------------------------------------------------------------------- 1 | /* OX: Open-Channel NVM Express SSD Controller 2 | * 3 | * - VOLT: Volatile storage and media manager 4 | * 5 | * Copyright 2017 IT University of Copenhagen 6 | * 7 | * Written by Ivan Luiz Picoli 8 | * 9 | * Licensed under the Apache License, Version 2.0 (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * http://www.apache.org/licenses/LICENSE-2.0 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | */ 21 | 22 | #ifndef VOLT_SSD_H 23 | #define VOLT_SSD_H 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | #define VOLT_MEM_ERROR 0 30 | #define VOLT_MEM_OK 1 31 | #define VOLT_SECOND 1000000 /* from u-seconds */ 32 | 33 | #define VOLT_CHIP_COUNT 8 34 | #define VOLT_VIRTUAL_LUNS 4 35 | #define VOLT_BLOCK_COUNT 64 36 | #define VOLT_PAGE_COUNT 64 37 | #define VOLT_SECTOR_COUNT 4 38 | #define VOLT_PLANE_COUNT 2 39 | #define VOLT_PAGE_SIZE 0x4000 40 | #define VOLT_SECTOR_SIZE 0x1000 41 | #define VOLT_SEC_OOB_SIZE 0x10 42 | #define VOLT_OOB_SIZE (VOLT_SEC_OOB_SIZE * VOLT_SECTOR_COUNT) 43 | 44 | #define VOLT_DMA_SLOT_CH 32 45 | #define VOLT_DMA_SLOT_INDEX (VOLT_DMA_SLOT_CH * VOLT_CHIP_COUNT) 46 | #define VOLT_DMA_READ 0x1 47 | #define VOLT_DMA_WRITE 0x2 48 | 49 | /* should be user-defined */ 50 | #define VOLT_BLK_LIFE 5000 51 | #define VOLT_RSV_BLK 1 52 | 53 | #define VOLT_READ_TIME 50 54 | #define VOLT_WRITE_TIME 200 55 | #define VOLT_ERASE_TIME 1200 56 | 57 | #define VOLT_QUEUE_SIZE 2048 58 | #define VOLT_QUEUE_TO 48000 59 | 60 | typedef struct VoltStatus { 61 | uint8_t ready; /* 0x00-busy, 0x01-ready to use */ 62 | uint8_t active; 63 | uint64_t allocated_memory; 64 | } VoltStatus; 65 | 66 | typedef struct VoltPage { 67 | uint8_t state; /* 0x00-free, 0x01-alive, 0x02-invalid */ 68 | uint8_t *data; 69 | } VoltPage; 70 | 71 | typedef struct VoltBlock { 72 | uint16_t id; 73 | uint16_t life; /* available writes before die */ 74 | VoltPage *next_pg; 75 | VoltPage *pages; 76 | } VoltBlock; 77 | 78 | typedef struct VoltLun { 79 | VoltBlock *blk_offset; 80 | } VoltLun; 81 | 82 | typedef struct VoltCh { 83 | VoltLun *lun_offset; 84 | } VoltCh; 85 | 86 | typedef struct VoltCtrl { 87 | VoltStatus status; 88 | VoltBlock *blocks; 89 | VoltLun *luns; 90 | VoltCh *channels; 91 | struct ox_mq *mq; 92 | uint8_t *edma; /* emergency DMA buffer for timeout requests */ 93 | } VoltCtrl; 94 | 95 | struct volt_dma { 96 | uint8_t *virt_addr; 97 | uint32_t prp_index; 98 | uint8_t status; /* nand status */ 99 | }; 100 | 101 | #endif /* VOLT_SSD_H */ -------------------------------------------------------------------------------- /parser/fabrics_parser.c: -------------------------------------------------------------------------------- 1 | /* OX: Open-Channel NVM Express SSD Controller 2 | * 3 | * - NVMe over Fabrics Command Parser 4 | * 5 | * Copyright 2018 IT University of Copenhagen 6 | * 7 | * Written by Ivan Luiz Picoli 8 | * 9 | * Licensed under the Apache License, Version 2.0 (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * http://www.apache.org/licenses/LICENSE-2.0 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #define PARSER_FABRICS_COUNT 1 30 | 31 | extern struct core_struct core; 32 | 33 | static int parser_fabrics_connect (NvmeRequest *req, NvmeCmd *cmd) 34 | { 35 | NvmefConnect *connect = (NvmefConnect *) cmd; 36 | 37 | if (nvmef_create_queue (connect->qid)) { 38 | req->status = 0x4000 | NVME_INTERNAL_DEV_ERROR; 39 | return -1; 40 | } 41 | 42 | return NVME_SUCCESS; 43 | } 44 | 45 | static int parser_fabrics_exec (NvmeRequest *req, NvmeCmd *cmd) 46 | { 47 | NvmefCmd *fcmd = (NvmefCmd *) cmd; 48 | 49 | if (core.debug) 50 | printf ("[FABRICS: cftype: 0x%02x]\n", fcmd->fctype); 51 | 52 | switch (fcmd->fctype) { 53 | case NVMEF_CMD_CONNECT: 54 | return parser_fabrics_connect (req, cmd); 55 | break; 56 | 57 | /* Commands not supported yet */ 58 | case NVMEF_CMD_PROP_SET: 59 | case NVMEF_CMD_PROP_GET: 60 | case NVMEF_CMD_AUTH_SEND: 61 | case NVMEF_CMD_AUTH_RECV: 62 | default: 63 | log_err ("[fabrics: Command %x not supported.]", fcmd->fctype); 64 | req->status = NVME_INVALID_OPCODE; 65 | return -1; 66 | } 67 | } 68 | 69 | static struct nvm_parser_cmd fabrics_cmds[PARSER_FABRICS_COUNT] = { 70 | { 71 | .name = "COMMAND_FABRICS", 72 | .opcode = 0x7f, 73 | .opcode_fn = parser_fabrics_exec, 74 | .queue_type = NVM_CMD_ADMIN 75 | } 76 | }; 77 | 78 | static void parser_fabrics_exit (struct nvm_parser *parser) 79 | { 80 | 81 | } 82 | 83 | static struct nvm_parser parser_fabrics = { 84 | .name = "FABRICS_PARSER", 85 | .cmd_count = PARSER_FABRICS_COUNT, 86 | .exit = parser_fabrics_exit 87 | }; 88 | 89 | int parser_fabrics_init (void) 90 | { 91 | parser_fabrics.cmd = fabrics_cmds; 92 | 93 | return ox_register_parser (&parser_fabrics); 94 | } -------------------------------------------------------------------------------- /parser/lnvm_parser.c: -------------------------------------------------------------------------------- 1 | /* OX: Open-Channel NVM Express SSD Controller 2 | * 3 | * - LightNVM Command Parser 4 | * 5 | * Copyright 2016 IT University of Copenhagen 6 | * 7 | * Written by Ivan Luiz Picoli 8 | * 9 | * Licensed under the Apache License, Version 2.0 (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * http://www.apache.org/licenses/LICENSE-2.0 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #define PARSER_LNVM_COUNT 2 29 | 30 | /*static void lnvm_debug_print_io (struct nvm_ppa_addr *list, uint64_t *prp, 31 | uint64_t *md_prp, uint16_t size, uint64_t dt_sz, uint64_t md_sz) 32 | { 33 | int i; 34 | 35 | printf(" Number of sectors: %d\n", size); 36 | printf(" DMA size: %lu (data) + %lu (meta) = %lu bytes\n", 37 | dt_sz, md_sz, dt_sz + md_sz); 38 | for (i = 0; i < size; i++) 39 | printf (" [ppa(%d): ch: %d, lun: %d, blk: %d, pl: %d, pg: %d, " 40 | "sec: %d] prp: 0x%016lx\n", i, list[i].g.ch, 41 | list[i].g.lun, list[i].g.blk, list[i].g.pl, 42 | list[i].g.pg, list[i].g.sec, prp[i]); 43 | printf (" [meta_prp(0): 0x%016lx\n", md_prp[0]); 44 | 45 | fflush (stdout); 46 | }*/ 47 | 48 | static int parser_lnvm_command1 (NvmeRequest *req, NvmeCmd *cmd) 49 | { 50 | return 0; 51 | } 52 | 53 | static int parser_lnvm_command2 (NvmeRequest *req, NvmeCmd *cmd) 54 | { 55 | return 0; 56 | } 57 | 58 | static struct nvm_parser_cmd lnvm_cmds[PARSER_LNVM_COUNT] = { 59 | { 60 | .name = "COMMAND_1", 61 | .opcode = 0xe2, 62 | .opcode_fn = parser_lnvm_command1, 63 | .queue_type = NVM_CMD_ADMIN 64 | }, 65 | { 66 | .name = "COMMAND_2", 67 | .opcode = 0x91, 68 | .opcode_fn = parser_lnvm_command2, 69 | .queue_type = NVM_CMD_IO 70 | } 71 | }; 72 | 73 | static void parser_lnvm_exit (struct nvm_parser *parser) 74 | { 75 | 76 | } 77 | 78 | static struct nvm_parser parser_lnvm = { 79 | .name = "LNVM_PARSER", 80 | .cmd_count = PARSER_LNVM_COUNT, 81 | .exit = parser_lnvm_exit 82 | }; 83 | 84 | int parser_lnvm_init (void) 85 | { 86 | parser_lnvm.cmd = lnvm_cmds; 87 | 88 | return ox_register_parser (&parser_lnvm); 89 | } -------------------------------------------------------------------------------- /parser/nvme_parser.c: -------------------------------------------------------------------------------- 1 | /* OX: Open-Channel NVM Express SSD Controller 2 | * 3 | * - NVMe Command Parser 4 | * 5 | * Copyright 2018 IT University of Copenhagen 6 | * Copyright 2018 Microsoft Research 7 | * 8 | * Written by Ivan Luiz Picoli 9 | * 10 | * Licensed under the Apache License, Version 2.0 (the "License"); 11 | * you may not use this file except in compliance with the License. 12 | * You may obtain a copy of the License at 13 | * 14 | * http://www.apache.org/licenses/LICENSE-2.0 15 | * 16 | * Unless required by applicable law or agreed to in writing, software 17 | * distributed under the License is distributed on an "AS IS" BASIS, 18 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | * See the License for the specific language governing permissions and 20 | * limitations under the License. 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #define PARSER_NVME_COUNT 5 31 | 32 | extern struct core_struct core; 33 | 34 | /* Bypass FTL queue */ 35 | void ox_ftl_process_cq (void *opaque); 36 | 37 | static void nvme_debug_print_io (NvmeRwCmd *cmd, uint32_t bs, uint64_t dt_sz, 38 | uint64_t md_sz, uint64_t elba, uint64_t *prp) 39 | { 40 | int i; 41 | 42 | printf(" fuse: %d, psdt: %d\n", cmd->fuse, cmd->psdt); 43 | printf(" number of LBAs: %d, bs: %d\n", cmd->nlb + 1, bs); 44 | printf(" DMA size: %lu (data) + %lu (meta) = %lu bytes\n", 45 | dt_sz, md_sz, dt_sz + md_sz); 46 | printf(" starting LBA: %lu, ending LBA: %lu\n", cmd->slba, elba); 47 | printf(" meta_prp: 0x%016lx\n", cmd->mptr); 48 | 49 | for (i = 0; i < cmd->nlb + 1; i++) 50 | printf(" [prp(%d): 0x%016lx\n", i, prp[i]); 51 | 52 | fflush (stdout); 53 | } 54 | 55 | static void nvme_parser_prepare_read (struct nvm_io_cmd *cmd) 56 | { 57 | uint32_t i, pg, nsec; 58 | 59 | cmd->status.status = NVM_IO_PROCESS; 60 | 61 | pg = 0; 62 | nsec = 0; 63 | for (i = 0; i <= cmd->n_sec; i++) { 64 | 65 | /* this is an user IO, so data is transferred to host */ 66 | cmd->mmgr_io[pg].force_sync_data[i] = 0; 67 | 68 | /* create flash pages */ 69 | if ((i == cmd->n_sec) || 70 | (i && (cmd->ppalist[i].ppa == cmd->ppalist[i - 1].ppa)) || 71 | (i && ( cmd->ppalist[i].g.ch != cmd->ppalist[i - 1].g.ch || 72 | cmd->ppalist[i].g.lun != cmd->ppalist[i - 1].g.lun || 73 | cmd->ppalist[i].g.blk != cmd->ppalist[i - 1].g.blk || 74 | cmd->ppalist[i].g.pg != cmd->ppalist[i - 1].g.pg || 75 | cmd->ppalist[i].g.pl != cmd->ppalist[i - 1].g.pl))) { 76 | 77 | cmd->mmgr_io[pg].pg_index = pg; 78 | cmd->mmgr_io[pg].status = NVM_IO_NEW; 79 | cmd->mmgr_io[pg].nvm_io = cmd; 80 | cmd->mmgr_io[pg].pg_sz = nsec * NVME_KERNEL_PG_SIZE; 81 | cmd->mmgr_io[pg].n_sectors = nsec; 82 | cmd->mmgr_io[pg].sec_offset = i - nsec; 83 | cmd->mmgr_io[pg].sync_count = NULL; 84 | cmd->mmgr_io[pg].sync_mutex = NULL; 85 | cmd->mmgr_io[pg].force_sync_md = 1; 86 | 87 | /* No metadata is transfered */ 88 | cmd->md_prp[pg] = 0; 89 | 90 | pg++; 91 | nsec = 0; 92 | } 93 | nsec++; 94 | } 95 | 96 | cmd->status.total_pgs = pg; 97 | } 98 | 99 | static int nvme_parser_read_submit (struct nvm_io_cmd *cmd) 100 | { 101 | int ret; 102 | uint32_t sec_i, pgs; 103 | struct nvm_ppa_addr sec_ppa; 104 | struct nvm_mmgr *mmgr = ox_get_mmgr_instance (); 105 | 106 | pgs = cmd->n_sec / mmgr->geometry->sec_per_pl_pg; 107 | if (cmd->n_sec % mmgr->geometry->sec_per_pl_pg > 0) 108 | pgs++; 109 | 110 | for (sec_i = 0; sec_i < cmd->n_sec; sec_i++) { 111 | 112 | sec_ppa.ppa = oxapp()->gl_map->read_fn (cmd->slba + sec_i); 113 | if (sec_ppa.ppa == AND64) 114 | return 1; 115 | 116 | /* 'cmd->ppalist[sec_i].ppa'is replaced for the PPA */ 117 | cmd->ppalist[sec_i].ppa = sec_ppa.ppa; 118 | 119 | cmd->channel[sec_i] = &mmgr->ch_info[sec_ppa.g.ch]; 120 | } 121 | 122 | nvme_parser_prepare_read (cmd); 123 | 124 | cmd->callback.cb_fn = ox_ftl_process_cq; 125 | cmd->callback.opaque = (void *) cmd; 126 | ret = oxapp()->ppa_io->submit_fn (cmd); 127 | 128 | return ret; 129 | } 130 | 131 | static int parser_nvme_rw (NvmeRequest *req, NvmeCmd *cmd) 132 | { 133 | NvmeRwCmd *rw = (NvmeRwCmd *)cmd; 134 | NvmeNamespace *ns = req->ns; 135 | NvmeCtrl *n = core.nvme_ctrl; 136 | uint64_t keys[16]; 137 | int i; 138 | 139 | uint32_t nlb = rw->nlb + 1; 140 | uint64_t slba = rw->slba; 141 | 142 | const uint64_t elba = slba + nlb; 143 | const uint8_t lba_index = NVME_ID_NS_FLBAS_INDEX(ns->id_ns.flbas); 144 | const uint8_t data_shift = ns->id_ns.lbaf[lba_index].ds; 145 | uint64_t data_size = nlb << data_shift; 146 | 147 | req->nvm_io.status.status = NVM_IO_NEW; 148 | req->is_write = rw->opcode == NVME_CMD_WRITE; 149 | 150 | if (elba > (ns->id_ns.nsze) + 1) 151 | return NVME_LBA_RANGE | NVME_DNR; 152 | 153 | if (n->id_ctrl.mdts && data_size > n->page_size * (1 << n->id_ctrl.mdts)) 154 | return NVME_LBA_RANGE | NVME_DNR; 155 | 156 | if (nlb > 256) 157 | return NVME_INVALID_FIELD | NVME_DNR; 158 | 159 | /* Metadata and End-to-end Data protection are disabled */ 160 | 161 | /* Map PRPs and SGL addresses */ 162 | switch (rw->psdt) { 163 | case CMD_PSDT_PRP: 164 | case CMD_PSDT_RSV: 165 | req->nvm_io.prp[0] = rw->prp1; 166 | 167 | if (nlb == 2) 168 | req->nvm_io.prp[1] = rw->prp2; 169 | else if (nlb > 2) 170 | ox_dma ((void *)(&req->nvm_io.prp[1]), rw->prp2, 171 | (nlb - 1) * sizeof(uint64_t), NVM_DMA_FROM_HOST); 172 | break; 173 | case CMD_PSDT_SGL: 174 | case CMD_PSDT_SGL_MD: 175 | if (nvmef_sgl_to_prp (nlb, &cmd->sgl, req->nvm_io.prp, keys)) 176 | return NVME_INVALID_FIELD; 177 | break; 178 | default: 179 | return NVME_INVALID_FORMAT; 180 | } 181 | 182 | req->slba = slba; 183 | req->meta_size = 0; 184 | req->status = NVME_SUCCESS; 185 | req->nlb = nlb; 186 | req->lba_index = lba_index; 187 | 188 | req->nvm_io.cid = rw->cid; 189 | req->nvm_io.sec_sz = NVME_KERNEL_PG_SIZE; 190 | req->nvm_io.md_sz = 0; 191 | req->nvm_io.cmdtype = (req->is_write) ? MMGR_WRITE_PG : MMGR_READ_PG; 192 | req->nvm_io.n_sec = nlb; 193 | req->nvm_io.req = (void *) req; 194 | req->nvm_io.slba = slba; 195 | 196 | req->nvm_io.status.pg_errors = 0; 197 | req->nvm_io.status.ret_t = 0; 198 | req->nvm_io.status.total_pgs = 0; 199 | req->nvm_io.status.pgs_p = 0; 200 | req->nvm_io.status.pgs_s = 0; 201 | req->nvm_io.status.status = NVM_IO_NEW; 202 | 203 | for (i = 0; i < 8; i++) { 204 | req->nvm_io.status.pg_map[i] = 0; 205 | } 206 | 207 | if (core.debug) 208 | nvme_debug_print_io (rw, req->nvm_io.sec_sz, data_size, 209 | req->nvm_io.md_sz, elba, req->nvm_io.prp); 210 | 211 | if (req->is_write) 212 | return ox_submit_ftl (&req->nvm_io); 213 | else 214 | return (!nvme_parser_read_submit (&req->nvm_io)) ? NVME_NO_COMPLETE : 215 | NVME_CMD_ABORT_REQ; 216 | } 217 | 218 | static int parser_nvme_null (NvmeRequest *req, NvmeCmd *cmd) 219 | { 220 | return NVME_SUCCESS; 221 | } 222 | 223 | static int parser_nvme_identify (NvmeRequest *req, NvmeCmd *cmd) 224 | { 225 | NvmeIdentify *id_cmd = (NvmeIdentify *) cmd; 226 | uint64_t prp[1]; 227 | uint64_t keys[16]; 228 | void *data; 229 | 230 | /* Map PRPs and SGL addresses */ 231 | switch (id_cmd->psdt) { 232 | case CMD_PSDT_PRP: 233 | case CMD_PSDT_RSV: 234 | prp[0] = id_cmd->prp1; 235 | break; 236 | case CMD_PSDT_SGL: 237 | case CMD_PSDT_SGL_MD: 238 | if (nvmef_sgl_to_prp (1, &id_cmd->sgl, prp, keys)) 239 | return NVME_INVALID_FIELD; 240 | break; 241 | default: 242 | return NVME_INVALID_FORMAT; 243 | } 244 | 245 | switch (id_cmd->cns) { 246 | case NVME_CNS_CTRL: 247 | data = (void *)(&core.nvme_ctrl->id_ctrl); 248 | break; 249 | case NVME_CNS_NS: 250 | data = (void *)(&core.nvme_ctrl->namespaces[0].id_ns); 251 | } 252 | 253 | ox_dma (data, prp[0], sizeof (NvmeIdCtrl), NVM_DMA_TO_HOST); 254 | 255 | return NVME_SUCCESS; 256 | } 257 | 258 | static struct nvm_parser_cmd nvme_cmds[PARSER_NVME_COUNT] = { 259 | { 260 | .name = "NVME_WRITE", 261 | .opcode = NVME_CMD_WRITE, 262 | .opcode_fn = parser_nvme_rw, 263 | .queue_type = NVM_CMD_IO 264 | }, 265 | { 266 | .name = "NVME_READ", 267 | .opcode = NVME_CMD_READ, 268 | .opcode_fn = parser_nvme_rw, 269 | .queue_type = NVM_CMD_IO 270 | }, 271 | { 272 | .name = "NVME_WRITE_NULL", 273 | .opcode = NVME_CMD_WRITE_NULL, 274 | .opcode_fn = parser_nvme_null, 275 | .queue_type = NVM_CMD_IO 276 | }, 277 | { 278 | .name = "NVME_READ_NULL", 279 | .opcode = NVME_CMD_READ_NULL, 280 | .opcode_fn = parser_nvme_null, 281 | .queue_type = NVM_CMD_IO 282 | }, 283 | { 284 | .name = "NVME_ADM_IDENTIFY", 285 | .opcode = NVME_ADM_CMD_IDENTIFY, 286 | .opcode_fn = parser_nvme_identify, 287 | .queue_type = NVM_CMD_ADMIN 288 | } 289 | }; 290 | 291 | static void parser_nvme_exit (struct nvm_parser *parser) 292 | { 293 | return; 294 | } 295 | 296 | static struct nvm_parser parser_nvme = { 297 | .name = "NVME_PARSER", 298 | .cmd_count = PARSER_NVME_COUNT, 299 | .exit = parser_nvme_exit 300 | }; 301 | 302 | int parser_nvme_init (void) 303 | { 304 | parser_nvme.cmd = nvme_cmds; 305 | 306 | return ox_register_parser (&parser_nvme); 307 | } 308 | -------------------------------------------------------------------------------- /targets/ox-ctrl-nvme-filebe.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | static void ox_ftl_modules (void) 6 | { 7 | /* Bad block table */ 8 | oxb_bbt_byte_register (); 9 | /* Block meta-data */ 10 | oxb_blk_md_register (); 11 | /* Channel provisioning */ 12 | oxb_ch_prov_register (); 13 | /* Global provisioning */ 14 | oxb_gl_prov_register (); 15 | /* Channel mapping */ 16 | oxb_ch_map_register(); 17 | /* Global mapping */ 18 | oxb_gl_map_register (); 19 | /* Back-end PPA I/O */ 20 | oxb_ppa_io_register (); 21 | /* Front-end LBA I/O */ 22 | oxb_lba_io_register (); 23 | /* Garbage collection */ 24 | oxb_gc_register (); 25 | /* Log Management */ 26 | oxb_log_register (); 27 | /* Recovery */ 28 | oxb_recovery_register (); 29 | } 30 | 31 | int main (int argc, char **argv) 32 | { 33 | ox_add_mmgr (mmgr_filebe_init); 34 | 35 | ox_add_ftl (ftl_lnvm_init); 36 | 37 | ox_add_ftl (ftl_oxapp_init); 38 | ox_set_std_ftl (FTL_ID_OXAPP); 39 | ox_set_std_oxapp (FTL_ID_BLOCK); 40 | ox_ftl_modules (); 41 | 42 | ox_add_parser (parser_nvme_init); 43 | ox_add_parser (parser_fabrics_init); 44 | 45 | ox_add_transport (ox_fabrics_init); 46 | ox_set_std_transport (NVM_TRANSP_FABRICS); 47 | ox_add_net_interface (OXF_ADDR_1, OXF_PORT_1); 48 | ox_add_net_interface (OXF_ADDR_2, OXF_PORT_2); 49 | 50 | /* We just have 2 cables for now, for the real network setup */ 51 | #if OXF_FULL_IFACES 52 | ox_add_net_interface (OXF_ADDR_3, OXF_PORT_3); 53 | ox_add_net_interface (OXF_ADDR_4, OXF_PORT_4); 54 | #endif 55 | 56 | return ox_ctrl_start (argc, argv); 57 | } 58 | -------------------------------------------------------------------------------- /targets/ox-ctrl-nvme-ocssd.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | static void ox_ftl_modules (void) 6 | { 7 | /* Bad block table */ 8 | oxb_bbt_byte_register (); 9 | /* Block meta-data */ 10 | oxb_blk_md_register (); 11 | /* Channel provisioning */ 12 | oxb_ch_prov_register (); 13 | /* Global provisioning */ 14 | oxb_gl_prov_register (); 15 | /* Channel mapping */ 16 | oxb_ch_map_register(); 17 | /* Global mapping */ 18 | oxb_gl_map_register (); 19 | /* Back-end PPA I/O */ 20 | oxb_ppa_io_register (); 21 | /* Front-end LBA I/O */ 22 | oxb_lba_io_register (); 23 | /* Garbage collection */ 24 | oxb_gc_register (); 25 | /* Log Management */ 26 | oxb_log_register (); 27 | /* Recovery */ 28 | oxb_recovery_register (); 29 | } 30 | 31 | int main (int argc, char **argv) 32 | { 33 | ox_add_mmgr (mmgr_ocssd_1_2_init); 34 | 35 | ox_add_ftl (ftl_lnvm_init); 36 | 37 | ox_add_ftl (ftl_oxapp_init); 38 | ox_set_std_ftl (FTL_ID_OXAPP); 39 | ox_set_std_oxapp (FTL_ID_BLOCK); 40 | ox_ftl_modules (); 41 | 42 | ox_add_parser (parser_nvme_init); 43 | ox_add_parser (parser_fabrics_init); 44 | 45 | ox_add_transport (ox_fabrics_init); 46 | ox_set_std_transport (NVM_TRANSP_FABRICS); 47 | ox_add_net_interface (OXF_ADDR_1, OXF_PORT_1); 48 | ox_add_net_interface (OXF_ADDR_2, OXF_PORT_2); 49 | 50 | /* We just have 2 cables for now, for the real network setup */ 51 | #if OXF_FULL_IFACES 52 | ox_add_net_interface (OXF_ADDR_3, OXF_PORT_3); 53 | ox_add_net_interface (OXF_ADDR_4, OXF_PORT_4); 54 | #endif 55 | 56 | return ox_ctrl_start (argc, argv); 57 | } -------------------------------------------------------------------------------- /targets/ox-ctrl-nvme-volt.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | static void ox_ftl_modules (void) 6 | { 7 | /* Bad block table */ 8 | oxb_bbt_byte_register (); 9 | /* Block meta-data */ 10 | oxb_blk_md_register (); 11 | /* Channel provisioning */ 12 | oxb_ch_prov_register (); 13 | /* Global provisioning */ 14 | oxb_gl_prov_register (); 15 | /* Channel mapping */ 16 | oxb_ch_map_register(); 17 | /* Global mapping */ 18 | oxb_gl_map_register (); 19 | /* Back-end PPA I/O */ 20 | oxb_ppa_io_register (); 21 | /* Front-end LBA I/O */ 22 | oxb_lba_io_register (); 23 | /* Garbage collection */ 24 | oxb_gc_register (); 25 | /* Log Management */ 26 | oxb_log_register (); 27 | /* Recovery */ 28 | oxb_recovery_register (); 29 | } 30 | 31 | int main (int argc, char **argv) 32 | { 33 | ox_add_mmgr (mmgr_volt_init_nodisk); 34 | 35 | ox_add_ftl (ftl_lnvm_init); 36 | 37 | ox_add_ftl (ftl_oxapp_init); 38 | ox_set_std_ftl (FTL_ID_OXAPP); 39 | ox_set_std_oxapp (FTL_ID_BLOCK); 40 | ox_ftl_modules (); 41 | 42 | ox_add_parser (parser_nvme_init); 43 | ox_add_parser (parser_fabrics_init); 44 | 45 | ox_add_transport (ox_fabrics_init); 46 | ox_set_std_transport (NVM_TRANSP_FABRICS); 47 | ox_add_net_interface (OXF_ADDR_1, OXF_PORT_1); 48 | ox_add_net_interface (OXF_ADDR_2, OXF_PORT_2); 49 | 50 | /* We just have 2 cables for now, for the real network setup */ 51 | #if OXF_FULL_IFACES 52 | ox_add_net_interface (OXF_ADDR_3, OXF_PORT_3); 53 | ox_add_net_interface (OXF_ADDR_4, OXF_PORT_4); 54 | #endif 55 | 56 | return ox_ctrl_start (argc, argv); 57 | } -------------------------------------------------------------------------------- /test/test-connect.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main (void) 6 | { 7 | int ret; 8 | 9 | ret = oxf_host_init (); 10 | if (ret) { 11 | printf ("Failed to initialize Host Fabrics.\n"); 12 | return -1; 13 | } 14 | 15 | oxf_host_add_server_iface (OXF_ADDR_1, OXF_PORT_1); 16 | 17 | if (oxf_host_create_queue (0)) { 18 | printf ("Failed to creating queue 0.\n"); 19 | oxf_host_exit (); 20 | return -1; 21 | } 22 | 23 | if (oxf_host_create_queue (1)) { 24 | printf ("Failed to creating queue 1.\n"); 25 | oxf_host_exit (); 26 | return -1; 27 | } 28 | 29 | printf ("Connection established -> %s:%d\n", OXF_ADDR_1, OXF_PORT_1); 30 | 31 | oxf_host_exit (); 32 | 33 | printf ("Connection closed -> %s:%d\n", OXF_ADDR_1, OXF_PORT_1); 34 | 35 | return 0; 36 | } 37 | -------------------------------------------------------------------------------- /test/test-nvme-rw.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #define NVMEH_NUM_QUEUES 4 * (OXF_FULL_IFACES + 1) 8 | #define NVMEH_BUF_SIZE 1024 * 1024 * 8 /* 8 MB */ 9 | 10 | #define NVMEH_BLKSZ 4096 11 | 12 | static volatile uint8_t done; 13 | 14 | /* This is an example context that identifies the completion */ 15 | struct nvme_test_context { 16 | uint64_t slba; 17 | uint64_t nlb; 18 | uint8_t is_write; 19 | }; 20 | 21 | void nvme_test_callback (void *ctx, uint16_t status) 22 | { 23 | struct nvme_test_context *my_ctx = (struct nvme_test_context *) ctx; 24 | 25 | printf ("Is write: %d, LBA %lu-%lu, size: %lu KB. Status -> %x\n", 26 | my_ctx->is_write, my_ctx->slba, my_ctx->slba + my_ctx->nlb, 27 | NVMEH_BLK_SZ * my_ctx->nlb / 1024, status); 28 | 29 | done++; 30 | } 31 | 32 | void nvme_test_read (void) 33 | { 34 | int ret; 35 | uint8_t *read_buffer; 36 | uint8_t *write_buffer; 37 | struct nvme_test_context ctx[2]; 38 | uint64_t slba; 39 | 40 | /* An example of write buffer */ 41 | write_buffer = malloc (NVMEH_BUF_SIZE); 42 | if (!write_buffer) { 43 | printf ("Memory allocation error.\n"); 44 | return; 45 | } 46 | memset (write_buffer, 0xca, NVMEH_BUF_SIZE); 47 | 48 | /* An example of read buffer */ 49 | read_buffer = malloc (NVMEH_BUF_SIZE); 50 | if (!read_buffer) { 51 | free (write_buffer); 52 | printf ("Memory allocation error.\n"); 53 | return; 54 | } 55 | 56 | /* Example of SLBA */ 57 | slba = 100; 58 | 59 | /* Sets the example context for write */ 60 | ctx[0].slba = slba; 61 | ctx[0].is_write = 1; 62 | ctx[0].nlb = NVMEH_BUF_SIZE / NVMEH_BLK_SZ; 63 | 64 | /* Submit the read command and define the callback function */ 65 | ret = nvmeh_write (write_buffer, NVMEH_BUF_SIZE, slba, 66 | nvme_test_callback, &ctx[0]); 67 | if (ret) { 68 | printf ("Write has failed.\n"); 69 | done++; 70 | } 71 | 72 | /* Wait until the write returns asynchronously */ 73 | while (done < 1) { 74 | usleep (100); 75 | } 76 | 77 | /* Sets the example for read */ 78 | ctx[1].slba = slba; 79 | ctx[1].is_write = 0; 80 | ctx[1].nlb = NVMEH_BUF_SIZE / NVMEH_BLK_SZ; 81 | 82 | /* Submit the read command and define the callback function */ 83 | ret = nvmeh_read (read_buffer, NVMEH_BUF_SIZE, slba, 84 | nvme_test_callback, &ctx[1]); 85 | if (ret) { 86 | printf ("Read has failed.\n"); 87 | done++; 88 | } 89 | 90 | /* Wait until the read returns asynchronously */ 91 | while (done < 2) { 92 | usleep (100); 93 | } 94 | 95 | if (memcmp (write_buffer, read_buffer, NVMEH_BUF_SIZE)) 96 | printf ("Data is NOT equal.\n"); 97 | else 98 | printf ("Data is equal.\n"); 99 | 100 | free (read_buffer); 101 | free (write_buffer); 102 | } 103 | 104 | int main (void) 105 | { 106 | int ret, q_id; 107 | 108 | ret = nvmeh_init (); 109 | if (ret) { 110 | printf ("Failed to initializing NVMe Host.\n"); 111 | return -1; 112 | } 113 | 114 | nvme_host_add_server_iface (OXF_ADDR_1, OXF_PORT_1); 115 | nvme_host_add_server_iface (OXF_ADDR_2, OXF_PORT_2); 116 | 117 | /* We just have 2 cables for now, for the real network setup */ 118 | #if OXF_FULL_IFACES 119 | nvme_host_add_server_iface (OXF_ADDR_3, OXF_PORT_3); 120 | nvme_host_add_server_iface (OXF_ADDR_4, OXF_PORT_4); 121 | #endif 122 | 123 | /* Create the NVMe queues. One additional queue for the admin queue */ 124 | for (q_id = 0; q_id < NVMEH_NUM_QUEUES + 1; q_id++) { 125 | if (nvme_host_create_queue (q_id)) { 126 | printf ("Failed to creating queue %d.\n", q_id); 127 | goto EXIT; 128 | } 129 | } 130 | 131 | done = 0; 132 | 133 | /* Read */ 134 | nvme_test_read (); 135 | 136 | /* Closes the application */ 137 | EXIT: 138 | while (q_id) { 139 | q_id--; 140 | nvme_host_destroy_queue (q_id); 141 | } 142 | nvmeh_exit (); 143 | 144 | return 0; 145 | } -------------------------------------------------------------------------------- /test/test-nvme-thput-r.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | /* 1 Admin queue + 4 I/O queues */ 9 | #define NVME_TEST_NUM_QUEUES (4 + 1) 10 | 11 | /* Write buffer: 128 MB */ 12 | #define NVME_TEST_BUF_SIZE (1024 * 1024 * 128) 13 | #define NVME_TEST_LBA_BUF (NVME_TEST_BUF_SIZE / NVMEH_BLK_SZ) 14 | 15 | /* I/O write size */ 16 | #define NVME_TEST_IO_SZ (1024 * 256) /* 256 KB */ 17 | #define NVME_TEST_LBA_IO (NVME_TEST_IO_SZ / NVMEH_BLK_SZ) 18 | 19 | /* Number of I/Os */ 20 | #define NVME_TEST_IOS 4000 21 | 22 | /* Offsets containing the LBA for read check */ 23 | #define CHECK_OFFSET1 16 24 | #define CHECK_OFFSET2 1852 25 | #define CHECK_OFFSET3 3740 26 | #define CHECK_OFFSET_LBA1 458 27 | #define CHECK_OFFSET_LBA2 2008 28 | #define CHECK_OFFSET_LBA3 3478 29 | 30 | /* Used to keep track of completed LBAs */ 31 | static volatile uint64_t done, corr; 32 | static pthread_spinlock_t done_spin; 33 | 34 | /* Time and statistics variables */ 35 | static struct timespec ts_s, ts_e, ts_c; 36 | static uint64_t start, end, cur, firstlba, lastlba, iocount, err; 37 | 38 | /* This is an example context that identifies the completion */ 39 | struct nvme_test_context { 40 | uint32_t id; 41 | uint64_t slba; 42 | uint64_t nlb; 43 | uint8_t *buf; 44 | }; 45 | struct nvme_test_context *ctx; 46 | 47 | /* Prints statistics at runtime */ 48 | static void nvme_test_print_runtime (void) 49 | { 50 | double iops, data, mb, sec, divb = 1024, divt = 1000, total_lbas; 51 | uint32_t perc; 52 | 53 | total_lbas = lastlba - firstlba; 54 | perc = (done * 100) / total_lbas; 55 | if (perc > 100) 56 | perc = 100; 57 | data = (double) NVMEH_BLK_SZ * (double) done; 58 | mb = data / divb / divb; 59 | GET_NANOSECONDS(cur,ts_c); 60 | sec = ((double)cur - (double)start) / divt / divt / divt; 61 | iops = (done / NVME_TEST_LBA_IO) / sec; 62 | printf ("\r %d %% - Time %.2lf ms, read: %.2lf MB, thput: %.2lf MB/s, " 63 | "IOPS: %.1f", perc, sec * divt, mb, mb / sec, iops); 64 | } 65 | 66 | /* Callback funcion */ 67 | static void nvme_test_callback (void *ctx, uint16_t status) 68 | { 69 | struct nvme_test_context *my_ctx = (struct nvme_test_context *) ctx; 70 | uint32_t *check1, *check2, *check3, lba, check; 71 | uint64_t *checklba1, *checklba2, *checklba3, checklba, slba; 72 | uint8_t *buf, fail; 73 | 74 | if (status) 75 | printf ("I/O %d failed.\n", my_ctx->id); 76 | 77 | pthread_spin_lock (&done_spin); 78 | done += my_ctx->nlb; 79 | if (status) 80 | err++; 81 | pthread_spin_unlock (&done_spin); 82 | 83 | if (done % (NVME_TEST_LBA_IO * 50) == 0) 84 | nvme_test_print_runtime (); 85 | 86 | slba = my_ctx->slba; 87 | for (lba = 0; lba < my_ctx->nlb; lba++) { 88 | buf = &my_ctx->buf[NVMEH_BLK_SZ * lba]; 89 | 90 | /* Check the relative LBA offsets */ 91 | check1 = (uint32_t *) &buf[CHECK_OFFSET1]; 92 | check2 = (uint32_t *) &buf[CHECK_OFFSET2]; 93 | check3 = (uint32_t *) &buf[CHECK_OFFSET3]; 94 | check = ((uint32_t) slba + lba) % NVME_TEST_LBA_BUF; 95 | if (!check) check = NVME_TEST_LBA_BUF; 96 | 97 | /* Check the absolute lba offsets */ 98 | checklba1 = (uint64_t *) &buf[CHECK_OFFSET_LBA1]; 99 | checklba2 = (uint64_t *) &buf[CHECK_OFFSET_LBA2]; 100 | checklba3 = (uint64_t *) &buf[CHECK_OFFSET_LBA3]; 101 | checklba = slba + (uint64_t) lba; 102 | 103 | fail = 0; 104 | if (*check1 != check || *check2 != check || *check3 != check || 105 | *checklba1 != checklba || *checklba2 != checklba || 106 | *checklba3 != checklba) { 107 | corr++; 108 | fail++; 109 | } 110 | 111 | if (fail) 112 | printf ("\nDATA CORRUPTION -> LBA: %lu, read LBA: %lu/%lu/%lu\n" 113 | " check: %d, read %d/%d/%d\n", 114 | checklba, *checklba1, *checklba2, *checklba3, 115 | check, *check1, *check2, *check3); 116 | } 117 | free (my_ctx->buf); 118 | 119 | if (done == lastlba - firstlba + 1) { 120 | GET_NANOSECONDS(end,ts_e); 121 | done++; 122 | } 123 | } 124 | 125 | /* Prints statistics at the end */ 126 | static void nvme_test_print (void) 127 | { 128 | double iops, data, mb, sec, divb = 1024, divt = 1000; 129 | uint64_t time; 130 | 131 | data = (double) NVMEH_BLK_SZ * (double) done - 1; 132 | mb = data / divb / divb; 133 | time = end - start; 134 | sec = (double) time / divt / divt / divt; 135 | iops = (done - 1) / NVME_TEST_LBA_IO / sec; 136 | printf ("\n\n Time elapsed : %.2lf ms\n", (double) time / divt / divt); 137 | printf ( " Read LBAs : %lu (%lu to %lu) \n", 138 | lastlba - firstlba + 1, firstlba, lastlba); 139 | printf ( " Read data : %.2lf MB\n", mb); 140 | printf ( " Throughput : %.2lf MB/s\n", mb / sec); 141 | printf ( " IOPS : %.1f\n", iops); 142 | printf ( " Block size : %d KB\n", NVMEH_BLK_SZ / 1024); 143 | printf ( " I/O size : %d KB\n", NVME_TEST_IO_SZ / 1024); 144 | printf ( " Issued I/Os : %lu\n", iocount); 145 | printf ( " Failed I/Os : %lu\n\n", err); 146 | if (corr) 147 | printf (" Corrupted LBAs: %lu\n", corr); 148 | } 149 | 150 | /* Prepares the write buffer and spinlocks */ 151 | static int nvme_test_prepare (void) 152 | { 153 | ctx = malloc (iocount * sizeof (struct nvme_test_context)); 154 | if (!ctx) { 155 | printf ("Memory allocation error.\n"); 156 | return -1; 157 | } 158 | 159 | if (pthread_spin_init (&done_spin, 0)) { 160 | free (ctx); 161 | return -1; 162 | } 163 | 164 | return 0; 165 | } 166 | 167 | static void nvme_test_destroy (void) 168 | { 169 | pthread_spin_destroy (&done_spin); 170 | free (ctx); 171 | } 172 | 173 | /* Runs the test */ 174 | void nvme_test_run (void) 175 | { 176 | int it, ret; 177 | uint64_t slba, lbaio, iosz; 178 | 179 | slba = firstlba; 180 | lbaio = NVME_TEST_LBA_IO; 181 | 182 | GET_NANOSECONDS(start,ts_s); 183 | for (it = 0; it < iocount; it++) { 184 | ctx[it].id = it; 185 | ctx[it].slba = slba; 186 | 187 | ctx[it].nlb = (slba + lbaio > lastlba) ? lastlba - slba + 1 : lbaio; 188 | 189 | ctx[it].buf = malloc (NVME_TEST_IO_SZ); 190 | if (!ctx[it].buf) { 191 | ret = -1; 192 | goto CHECK; 193 | } 194 | 195 | iosz = (slba + lbaio > lastlba) ? 196 | ctx[it].nlb * NVMEH_BLK_SZ : NVME_TEST_IO_SZ; 197 | 198 | /* Submit the read command with callback function */ 199 | ret = nvmeh_read (ctx[it].buf, 200 | iosz, slba, nvme_test_callback, &ctx[it]); 201 | CHECK: 202 | if (ret) { 203 | printf ("I/O %d not submitted.\n", it); 204 | pthread_spin_lock (&done_spin); 205 | done += ctx[it].nlb; 206 | pthread_spin_unlock (&done_spin); 207 | } 208 | 209 | /* Increase slba */ 210 | slba += lbaio; 211 | } 212 | 213 | /* Wait until the all I/Os complete */ 214 | while (done <= lastlba - firstlba) { 215 | usleep (500); 216 | } 217 | } 218 | 219 | int main (int argc, char **argv) 220 | { 221 | int ret, q_id; 222 | 223 | /* Get arguments and setup first/last LBA */ 224 | firstlba = 1; 225 | iocount = NVME_TEST_IOS; 226 | if (argc > 1) { 227 | firstlba = atoi(argv[1]); 228 | if (!firstlba) 229 | firstlba = 1; 230 | } 231 | lastlba = firstlba + (NVME_TEST_IOS * NVME_TEST_LBA_IO) - 1; 232 | if (argc > 2) { 233 | lastlba = firstlba + atoi(argv[2]) - 1; 234 | iocount = ((uint64_t) atoi(argv[2]) * (uint64_t) NVMEH_BLK_SZ) / 235 | NVME_TEST_IO_SZ; 236 | if (( atoi(argv[2]) * NVMEH_BLK_SZ) % NVME_TEST_IO_SZ != 0) 237 | iocount++; 238 | } 239 | 240 | /* Initialize OX NVMe Host */ 241 | ret = nvmeh_init (); 242 | if (ret) { 243 | printf ("Failed to initializing NVMe Host.\n"); 244 | return -1; 245 | } 246 | 247 | /* Add a network connections to be used by the Fabrics */ 248 | nvme_host_add_server_iface (OXF_ADDR_1, OXF_PORT_1); 249 | 250 | #if OXF_FULL_IFACES 251 | nvme_host_add_server_iface (OXF_ADDR_2, OXF_PORT_2); 252 | nvme_host_add_server_iface (OXF_ADDR_3, OXF_PORT_3); 253 | nvme_host_add_server_iface (OXF_ADDR_4, OXF_PORT_4); 254 | #endif 255 | 256 | /* Create the NVMe queues. One additional queue for the admin queue */ 257 | for (q_id = 0; q_id < NVME_TEST_NUM_QUEUES; q_id++) { 258 | if (nvme_host_create_queue (q_id)) { 259 | printf ("Failed to creating queue %d.\n", q_id); 260 | goto EXIT; 261 | } 262 | } 263 | 264 | corr = 0; 265 | done = 0; 266 | err = 0; 267 | 268 | nvme_test_prepare (); 269 | nvme_test_run (); 270 | nvme_test_destroy (); 271 | nvme_test_print (); 272 | 273 | /* Closes the application */ 274 | EXIT: 275 | while (q_id) { 276 | q_id--; 277 | nvme_host_destroy_queue (q_id); 278 | } 279 | nvmeh_exit (); 280 | 281 | return 0; 282 | } 283 | 284 | -------------------------------------------------------------------------------- /test/test-nvme-thput-w.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | /* 1 Admin queue + 4 I/O queues */ 9 | #define NVME_TEST_NUM_QUEUES (4 + 1) 10 | 11 | /* Write buffer: 128 MB */ 12 | #define NVME_TEST_BUF_SIZE (1024 * 1024 * 128) 13 | #define NVME_TEST_LBA_BUF (NVME_TEST_BUF_SIZE / NVMEH_BLK_SZ) 14 | 15 | /* I/O write size */ 16 | #define NVME_TEST_IO_SZ (1024 * 256) /* 256 KB */ 17 | #define NVME_TEST_LBA_IO (NVME_TEST_IO_SZ / NVMEH_BLK_SZ) 18 | 19 | /* Number of I/Os */ 20 | #define NVME_TEST_IOS 4000 21 | 22 | /* Offsets containing the LBA for read check */ 23 | #define CHECK_OFFSET1 16 24 | #define CHECK_OFFSET2 1852 25 | #define CHECK_OFFSET3 3740 26 | #define CHECK_OFFSET_LBA1 458 27 | #define CHECK_OFFSET_LBA2 2008 28 | #define CHECK_OFFSET_LBA3 3478 29 | 30 | /* Write buffer */ 31 | static uint8_t *write_buffer; 32 | 33 | /* Used to keep track of completed LBAs */ 34 | static volatile uint64_t done; 35 | static pthread_spinlock_t done_spin; 36 | 37 | /* Time and statistics variables */ 38 | static struct timespec ts_s, ts_e, ts_c; 39 | static uint64_t start, end, cur, firstlba, lastlba, iocount, err; 40 | 41 | /* This is an example context that identifies the completion */ 42 | struct nvme_test_context { 43 | uint32_t id; 44 | uint64_t slba; 45 | uint64_t nlb; 46 | }; 47 | struct nvme_test_context *ctx; 48 | 49 | /* Prints statistics at runtime */ 50 | static void nvme_test_print_runtime (void) 51 | { 52 | double iops, data, mb, sec, divb = 1024, divt = 1000, total_lbas; 53 | uint32_t perc; 54 | 55 | total_lbas = lastlba - firstlba; 56 | perc = (done * 100) / total_lbas; 57 | if (perc > 100) 58 | perc = 100; 59 | data = (double) NVMEH_BLK_SZ * (double) done; 60 | mb = data / divb / divb; 61 | GET_NANOSECONDS(cur,ts_c); 62 | sec = ((double)cur - (double)start) / divt / divt / divt; 63 | iops = (done / NVME_TEST_LBA_IO) / sec; 64 | printf ("\r %d %% - Time %.2lf ms, written: %.2lf MB, thput: %.2lf MB/s, " 65 | "IOPS: %.1f", perc, sec * divt, mb, mb / sec, iops); 66 | } 67 | 68 | /* Callback funcion */ 69 | static void nvme_test_callback (void *ctx, uint16_t status) 70 | { 71 | struct nvme_test_context *my_ctx = (struct nvme_test_context *) ctx; 72 | 73 | pthread_spin_lock (&done_spin); 74 | done += my_ctx->nlb; 75 | if (status) 76 | err++; 77 | pthread_spin_unlock (&done_spin); 78 | 79 | if (done % (NVME_TEST_LBA_IO * 25) == 0) 80 | nvme_test_print_runtime (); 81 | 82 | if (done == lastlba - firstlba + 1) { 83 | GET_NANOSECONDS(end,ts_e); 84 | done++; 85 | } 86 | } 87 | 88 | /* Prints statistics at the end */ 89 | static void nvme_test_print (void) 90 | { 91 | double iops, data, mb, sec, divb = 1024, divt = 1000; 92 | uint64_t time; 93 | 94 | data = (double) NVMEH_BLK_SZ * (double) done - 1; 95 | mb = data / divb / divb; 96 | time = end - start; 97 | sec = (double) time / divt / divt / divt; 98 | iops = (done - 1) / NVME_TEST_LBA_IO / sec; 99 | printf ("\n\n Time elapsed : %.2lf ms\n", (double) time / divt / divt); 100 | printf ( " Written LBAs : %lu (%lu to %lu) \n", 101 | lastlba - firstlba + 1, firstlba, lastlba); 102 | printf ( " Written data : %.2lf MB\n", mb); 103 | printf ( " Throughput : %.2lf MB/s\n", mb / sec); 104 | printf ( " IOPS : %.1f\n", iops); 105 | printf ( " Block size : %d KB\n", NVMEH_BLK_SZ / 1024); 106 | printf ( " I/O size : %d KB\n", NVME_TEST_IO_SZ / 1024); 107 | printf ( " Issued I/Os : %lu\n", iocount); 108 | printf ( " Failed I/Os : %lu\n\n", err); 109 | } 110 | 111 | /* Prepares the write buffer and spinlocks */ 112 | static int nvme_test_prepare (void) 113 | { 114 | uint32_t lba = 1, off = 0; 115 | uint8_t *buf; 116 | 117 | ctx = malloc (iocount * sizeof (struct nvme_test_context)); 118 | if (!ctx) { 119 | printf ("Memory allocation error.\n"); 120 | return -1; 121 | } 122 | 123 | write_buffer = malloc (NVME_TEST_BUF_SIZE + NVME_TEST_IO_SZ); 124 | if (!write_buffer) { 125 | printf ("Memory allocation error.\n"); 126 | free (ctx); 127 | return -1; 128 | } 129 | 130 | while (off < NVME_TEST_BUF_SIZE + NVME_TEST_IO_SZ) { 131 | buf = &write_buffer[off]; 132 | memset (buf, (uint8_t) lba + 7, NVMEH_BLK_SZ); 133 | *((uint32_t *) &buf[CHECK_OFFSET1]) = lba; 134 | *((uint32_t *) &buf[CHECK_OFFSET2]) = lba; 135 | *((uint32_t *) &buf[CHECK_OFFSET3]) = lba; 136 | off += NVMEH_BLK_SZ; 137 | lba = (lba == NVME_TEST_LBA_BUF) ? 1 : lba + 1; 138 | } 139 | 140 | if (pthread_spin_init (&done_spin, 0)) { 141 | free (ctx); 142 | free (write_buffer); 143 | return -1; 144 | } 145 | 146 | return 0; 147 | } 148 | 149 | static void nvme_set_lba_check (uint8_t *buf, uint64_t slba, uint64_t iosz) 150 | { 151 | uint64_t lba, nlba; 152 | uint8_t *bufoff; 153 | 154 | lba = slba; 155 | nlba = iosz / NVMEH_BLK_SZ; 156 | 157 | for (int i = 0; i < nlba; i++) { 158 | bufoff = &buf[i * NVMEH_BLK_SZ]; 159 | *((uint64_t *) &bufoff[CHECK_OFFSET_LBA1]) = lba; 160 | *((uint64_t *) &bufoff[CHECK_OFFSET_LBA2]) = lba; 161 | *((uint64_t *) &bufoff[CHECK_OFFSET_LBA3]) = lba; 162 | lba++; 163 | } 164 | } 165 | 166 | static void nvme_test_destroy (void) 167 | { 168 | pthread_spin_destroy (&done_spin); 169 | free (write_buffer); 170 | free (ctx); 171 | } 172 | 173 | /* Runs the test */ 174 | void nvme_test_run (void) 175 | { 176 | int it, ret; 177 | uint64_t slba, boff, lbaio, iosz; 178 | 179 | slba = firstlba; 180 | lbaio = NVME_TEST_LBA_IO; 181 | 182 | GET_NANOSECONDS(start,ts_s); 183 | for (it = 0; it < iocount; it++) { 184 | ctx[it].id = it; 185 | ctx[it].slba = slba; 186 | 187 | ctx[it].nlb = (slba + lbaio > lastlba) ? lastlba - slba + 1 : lbaio; 188 | 189 | boff = ((slba - 1) % NVME_TEST_LBA_BUF) * NVMEH_BLK_SZ; 190 | iosz = (slba + lbaio > lastlba) ? 191 | ctx[it].nlb * NVMEH_BLK_SZ : NVME_TEST_IO_SZ; 192 | 193 | nvme_set_lba_check (&write_buffer[boff], slba, iosz); 194 | 195 | /* Submit the write command with callback function */ 196 | ret = nvmeh_write (&write_buffer[boff], 197 | iosz, slba, nvme_test_callback, &ctx[it]); 198 | if (ret) { 199 | printf ("I/O %d not submitted.\n", it); 200 | pthread_spin_lock (&done_spin); 201 | done += ctx[it].nlb; 202 | pthread_spin_unlock (&done_spin); 203 | } 204 | 205 | /* Increase slba */ 206 | slba += lbaio; 207 | } 208 | 209 | /* Wait until the all I/Os complete */ 210 | while (done <= lastlba - firstlba) { 211 | usleep (500); 212 | } 213 | } 214 | 215 | int main (int argc, char **argv) 216 | { 217 | int ret, q_id; 218 | 219 | /* Get arguments and setup first/last LBA */ 220 | firstlba = 1; 221 | iocount = NVME_TEST_IOS; 222 | if (argc > 1) { 223 | firstlba = atoi(argv[1]); 224 | if (!firstlba) 225 | firstlba = 1; 226 | } 227 | lastlba = firstlba + (NVME_TEST_IOS * NVME_TEST_LBA_IO) - 1; 228 | if (argc > 2) { 229 | lastlba = firstlba + atoi(argv[2]) - 1; 230 | iocount = ((uint64_t) atoi(argv[2]) * (uint64_t) NVMEH_BLK_SZ) / 231 | NVME_TEST_IO_SZ; 232 | if (( atoi(argv[2]) * NVMEH_BLK_SZ) % NVME_TEST_IO_SZ != 0) 233 | iocount++; 234 | } 235 | 236 | /* Initialize OX NVMe Host */ 237 | ret = nvmeh_init (); 238 | if (ret) { 239 | printf ("Failed to initializing NVMe Host.\n"); 240 | return -1; 241 | } 242 | 243 | /* Add a network connections to be used by the Fabrics */ 244 | nvme_host_add_server_iface (OXF_ADDR_1, OXF_PORT_1); 245 | 246 | #if OXF_FULL_IFACES 247 | nvme_host_add_server_iface (OXF_ADDR_2, OXF_PORT_2); 248 | nvme_host_add_server_iface (OXF_ADDR_3, OXF_PORT_3); 249 | nvme_host_add_server_iface (OXF_ADDR_4, OXF_PORT_4); 250 | #endif 251 | 252 | /* Create the NVMe queues. One additional queue for the admin queue */ 253 | for (q_id = 0; q_id < NVME_TEST_NUM_QUEUES; q_id++) { 254 | if (nvme_host_create_queue (q_id)) { 255 | printf ("Failed to creating queue %d.\n", q_id); 256 | goto EXIT; 257 | } 258 | } 259 | 260 | done = 0; 261 | err = 0; 262 | 263 | nvme_test_prepare (); 264 | nvme_test_run (); 265 | nvme_test_destroy (); 266 | nvme_test_print (); 267 | 268 | /* Closes the application */ 269 | EXIT: 270 | while (q_id) { 271 | q_id--; 272 | nvme_host_destroy_queue (q_id); 273 | } 274 | nvmeh_exit (); 275 | 276 | return 0; 277 | } 278 | -------------------------------------------------------------------------------- /test/test-ox-mq.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define QUEUE_SIZE 512 11 | #define N_QUEUES 4 12 | #define N_CMD (1024 * 1024 * N_QUEUES) 13 | #define N_CORES 8 14 | 15 | struct test_struct { 16 | uint64_t id; 17 | uint32_t queue; 18 | }; 19 | 20 | static struct ox_mq *test_mq; 21 | volatile static uint64_t completed [N_QUEUES]; 22 | 23 | static struct timespec ts, te; 24 | static uint64_t start, end; 25 | 26 | struct test_struct **cmd; 27 | 28 | static void test_process_sq (struct ox_mq_entry *req) 29 | { 30 | RETRY: 31 | if (ox_mq_complete_req (test_mq, req)) 32 | goto RETRY; 33 | } 34 | 35 | static void test_process_cq (void *opaque) 36 | { 37 | struct test_struct *cmd = (struct test_struct *) opaque; 38 | uint32_t qid = cmd->queue; 39 | 40 | free (opaque); 41 | completed[qid]++; 42 | } 43 | 44 | static void test_process_to (void **opaque, int counter) 45 | { 46 | 47 | } 48 | 49 | static void *test_submit_th (void *arg) 50 | { 51 | uint64_t ent_i; 52 | uint64_t count = N_CMD / N_QUEUES; 53 | uint64_t qid = *(int *) arg; 54 | cpu_set_t cpuset; 55 | pthread_t current_thread; 56 | 57 | CPU_ZERO(&cpuset); 58 | CPU_SET(((qid % N_CORES) + N_QUEUES) % N_CORES, &cpuset); 59 | 60 | current_thread = pthread_self(); 61 | pthread_setaffinity_np(current_thread, sizeof(cpu_set_t), &cpuset); 62 | 63 | printf ("Thread %lu set to core %lu\n", qid, 64 | ((qid % N_CORES) + N_QUEUES) % N_CORES); 65 | 66 | for (ent_i = 0; ent_i < count; ent_i++) { 67 | cmd[(count * qid) + ent_i]->queue = qid; 68 | RETRY: 69 | if (ox_mq_submit_req (test_mq, qid, cmd[(count * qid) + ent_i])) 70 | goto RETRY; 71 | } 72 | 73 | return NULL; 74 | } 75 | 76 | int main (void) 77 | { 78 | int ent_i, i; 79 | uint64_t total, qid; 80 | struct ox_mq_config config; 81 | pthread_t th[N_QUEUES]; 82 | int th_qid[N_QUEUES]; 83 | 84 | if (ox_mem_init ()) 85 | return -1; 86 | 87 | if (!ox_mem_create_type ("OX_MQ", OX_MEM_OX_MQ)) 88 | return -1; 89 | 90 | config.n_queues = N_QUEUES; 91 | config.q_size = QUEUE_SIZE; 92 | config.sq_fn = test_process_sq; 93 | config.cq_fn = test_process_cq; 94 | config.to_fn = test_process_to; 95 | config.to_usec = 0; 96 | config.flags = OX_MQ_CPU_AFFINITY; 97 | 98 | /* Set thread affinity*/ 99 | for (qid = 0; qid < N_QUEUES; qid++) { 100 | printf ("Queue %lu - SQ set to core %lu, CQ set to core %lu\n", qid, 101 | (qid % N_CORES), 102 | ((qid % N_CORES) + N_QUEUES) % N_CORES); 103 | 104 | config.sq_affinity[qid] |= ((uint64_t) 1 << (qid % N_CORES)); 105 | config.cq_affinity[qid] |= 106 | ((uint64_t) 1 << (((qid % N_CORES) + N_QUEUES) % N_CORES)); 107 | } 108 | 109 | test_mq = ox_mq_init(&config); 110 | if (!test_mq) 111 | return -1; 112 | 113 | for (ent_i = 0; ent_i < N_QUEUES; ent_i++) 114 | completed[ent_i] = 0; 115 | 116 | GET_NANOSECONDS(start, ts); 117 | 118 | cmd = malloc (sizeof (struct test_struct *) * N_CMD); 119 | if (!cmd) 120 | return -1; 121 | 122 | for (ent_i = 0; ent_i < N_CMD; ent_i++) { 123 | cmd[ent_i] = malloc (sizeof(struct test_struct)); 124 | } 125 | 126 | for (ent_i = 0; ent_i < N_QUEUES; ent_i++) { 127 | th_qid[ent_i] = ent_i; 128 | if (pthread_create (&th[ent_i], NULL, test_submit_th, &th_qid[ent_i])) 129 | return -1; 130 | } 131 | 132 | total = 0; 133 | while (total < N_CMD) { 134 | usleep (10); 135 | total = 0; 136 | for (i = 0; i < N_QUEUES; i++) 137 | total += completed[i]; 138 | } 139 | GET_NANOSECONDS(end, te); 140 | 141 | printf ("\n"); 142 | printf ("Time elapsed; %.3lf ms\n", (float)(end - start) / 1000 / 1000); 143 | printf ("IOPS: %lf\n", (N_CMD / ((float)(end - start) / 1000 / 1000 / 1000))); 144 | 145 | free (cmd); 146 | ox_mq_destroy (test_mq); 147 | ox_mem_exit (); 148 | 149 | return 0; 150 | } -------------------------------------------------------------------------------- /test/test-queue.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define NUM_CMD 100000 9 | #define NUM_QUEUES 4 * (OXF_FULL_IFACES + 1) 10 | 11 | volatile static uint32_t returned; 12 | volatile static uint32_t completed; 13 | static pthread_spinlock_t spin; 14 | 15 | static struct timespec ts_s, ts_e; 16 | static uint64_t start, end, sent, received; 17 | 18 | void test_callback (void *ctx, struct nvme_cqe *cqe) 19 | { 20 | struct nvme_cmd *ncmd = (struct nvme_cmd *) ctx; 21 | double iops, th; 22 | 23 | if (cqe->status) 24 | printf ("Command (%d/0x%x) returned. Status -> %d\n", 25 | cqe->cid, ncmd->opcode, cqe->status); 26 | 27 | pthread_spin_lock (&spin); 28 | if ((cqe->cid == ncmd->cid) && (cqe->status != NVME_HOST_TIMEOUT)) 29 | completed++; 30 | 31 | returned--; 32 | pthread_spin_unlock (&spin); 33 | 34 | if (!returned) { 35 | GET_NANOSECONDS(end,ts_e); 36 | printf ("Time elapsed %lu us. Completed: %d\n", 37 | (end - start) / 1000, completed); 38 | 39 | iops = ( (double)1 * (double)NUM_CMD ) / 40 | ( ( (double)end - (double)start ) / (double) 1000000000 ); 41 | 42 | th = ( (double)1 * ( (sent + received) / (double)1024 /(double)1024 )) / 43 | ( ( (double)end - (double)start ) / (double) 1000000000 ); 44 | 45 | printf ("IOPS: %.2lf, th: %.2lf MB/s\n", iops, th); 46 | printf ("Data sent: %lu MB\n", sent / 1024 / 1024); 47 | printf ("Data rcvd: %lu MB\n", received / 1024 / 1024); 48 | } 49 | } 50 | 51 | int main (void) 52 | { 53 | int ret, i, cmd_i, q_id; 54 | struct nvme_cmd *ncmd[NUM_CMD]; 55 | struct nvme_sgl_desc *desc; 56 | uint8_t buffer[OXF_SQC_MAX_DATA]; 57 | uint8_t *buf_ptr[1] = {&buffer[0]}; 58 | uint32_t buf_sz[1] = {4096 * 15}; 59 | 60 | for (cmd_i = 0; cmd_i < NUM_CMD; cmd_i++) { 61 | ncmd[cmd_i] = calloc (1, sizeof (struct nvme_cmd)); 62 | if (!ncmd[cmd_i]) { 63 | printf ("Failed to allocating memory.\n"); 64 | goto FREE; 65 | } 66 | } 67 | 68 | if (pthread_spin_init (&spin, 0)) 69 | goto FREE; 70 | 71 | ret = oxf_host_init (); 72 | if (ret) { 73 | printf ("Failed to initializing Host Fabrics.\n"); 74 | goto SPIN; 75 | } 76 | 77 | oxf_host_add_server_iface (OXF_ADDR_1, OXF_PORT_1); 78 | oxf_host_add_server_iface (OXF_ADDR_2, OXF_PORT_2); 79 | 80 | /* We just have 2 cables for now, for the real network setup */ 81 | #if OXF_FULL_IFACES 82 | oxf_host_add_server_iface (OXF_ADDR_3, OXF_PORT_3); 83 | oxf_host_add_server_iface (OXF_ADDR_4, OXF_PORT_4); 84 | #endif 85 | 86 | for (q_id = 0; q_id < NUM_QUEUES + 1; q_id++) { 87 | if (oxf_host_create_queue (q_id)) { 88 | printf ("Failed to creating queue %d.\n", q_id); 89 | goto EXIT; 90 | } 91 | } 92 | 93 | printf ("Connected queues: %d + 1 (admin)\n", NUM_QUEUES); 94 | 95 | returned = NUM_CMD; 96 | completed = 0; 97 | sent = 0; 98 | received = 0; 99 | 100 | for (int i2 = 0; i2 < OXF_SQC_MAX_DATA; i2++) 101 | buffer[i2] = (uint8_t) (i2 % 256); 102 | 103 | GET_NANOSECONDS(start,ts_s); 104 | for (i = 0; i < NUM_CMD; i++) { 105 | if (i % 2 == 0) 106 | ncmd[i]->opcode = NVME_CMD_WRITE_NULL; 107 | else 108 | ncmd[i]->opcode = NVME_CMD_READ_NULL; 109 | 110 | desc = oxf_host_alloc_sgl (buf_ptr, buf_sz, 1); 111 | if (!desc) 112 | goto EXIT; 113 | 114 | if (oxf_host_submit_io ((i % NUM_QUEUES) + 1, ncmd[i], desc, 1, 115 | test_callback, ncmd[i])) { 116 | printf ("Failed to submitting command %d.\n", i); 117 | pthread_spin_lock (&spin); 118 | returned--; 119 | pthread_spin_unlock (&spin); 120 | } else { 121 | if (ncmd[i]->opcode == NVME_CMD_WRITE_NULL) { 122 | sent += OXF_FAB_CMD_SZ + NVMEF_SGL_SZ + desc->data.length; 123 | received += OXF_FAB_CQE_SZ; 124 | } else { 125 | sent += OXF_FAB_CMD_SZ + NVMEF_SGL_SZ; 126 | received += OXF_FAB_CQE_SZ + desc->data.length; 127 | } 128 | } 129 | 130 | oxf_host_free_sgl (desc); 131 | } 132 | 133 | printf ("Waiting completion...\n"); 134 | 135 | while (returned) { 136 | usleep (100000); 137 | } 138 | 139 | EXIT: 140 | while (q_id) { 141 | q_id--; 142 | oxf_host_destroy_queue (q_id); 143 | } 144 | oxf_host_exit (); 145 | SPIN: 146 | pthread_spin_destroy (&spin); 147 | FREE: 148 | while (cmd_i) { 149 | cmd_i--; 150 | free (ncmd[cmd_i]); 151 | } 152 | return -1; 153 | } -------------------------------------------------------------------------------- /transport/ox-fabrics/tcp-client.c: -------------------------------------------------------------------------------- 1 | /* OX: Open-Channel NVM Express SSD Controller 2 | * 3 | * - OX NVMe over TCP (client side) 4 | * 5 | * Copyright 2018 IT University of Copenhagen 6 | * 7 | * Written by Ivan Luiz Picoli 8 | * 9 | * Licensed under the Apache License, Version 2.0 (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * http://www.apache.org/licenses/LICENSE-2.0 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | * 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | static uint16_t oxf_tcp_client_process_msg (struct oxf_client_con *con, 35 | uint8_t *buffer, uint8_t *broken, uint16_t *brkb, 36 | int msg_bytes) 37 | { 38 | uint16_t offset = 0, fix = 0, msg_sz, brk_bytes = *brkb; 39 | 40 | if (brk_bytes) { 41 | 42 | if (brk_bytes < 3) { 43 | 44 | if (msg_bytes + brk_bytes < 3) { 45 | memcpy (&broken[brk_bytes], buffer, msg_bytes); 46 | brk_bytes += msg_bytes; 47 | return brk_bytes; 48 | } 49 | 50 | memcpy (&broken[brk_bytes], buffer, 3 - brk_bytes); 51 | offset = fix = 3 - brk_bytes; 52 | msg_bytes -= 3 - brk_bytes; 53 | brk_bytes = 3; 54 | if (!msg_bytes) 55 | return brk_bytes; 56 | } 57 | 58 | msg_sz = ((struct oxf_capsule_sq *) broken)->size; 59 | 60 | if (brk_bytes + msg_bytes < msg_sz) { 61 | memcpy (&broken[brk_bytes], &buffer[offset], msg_bytes); 62 | brk_bytes += msg_bytes; 63 | return brk_bytes; 64 | } 65 | 66 | memcpy (&broken[brk_bytes], &buffer[offset], msg_sz - brk_bytes); 67 | con->recv_fn (msg_sz, (void *) broken); 68 | offset += msg_sz - brk_bytes; 69 | brk_bytes = 0; 70 | } 71 | 72 | msg_bytes += fix; 73 | while (offset < msg_bytes) { 74 | if ( (msg_bytes - offset < 3) || 75 | (msg_bytes - offset < 76 | ((struct oxf_capsule_sq *) &buffer[offset])->size) ) { 77 | memcpy (broken, &buffer[offset], msg_bytes - offset); 78 | brk_bytes = msg_bytes - offset; 79 | offset += msg_bytes - offset; 80 | continue; 81 | } 82 | 83 | msg_sz = ((struct oxf_capsule_sq *) &buffer[offset])->size; 84 | con->recv_fn (msg_sz, (void *) &buffer[offset]); 85 | offset += msg_sz; 86 | } 87 | 88 | return brk_bytes; 89 | } 90 | 91 | static void *oxf_tcp_client_recv (void *arg) 92 | { 93 | ssize_t msg_bytes; 94 | uint16_t brk_bytes = 0; 95 | uint8_t buffer[OXF_MAX_DGRAM + 1]; 96 | uint8_t broken[OXF_MAX_DGRAM + 1]; 97 | struct oxf_client_con *con = (struct oxf_client_con *) arg; 98 | 99 | while (con->running) { 100 | msg_bytes = recv(con->sock_fd , buffer, OXF_MAX_DGRAM, MSG_DONTWAIT); 101 | 102 | if (msg_bytes <= 0) 103 | continue; 104 | 105 | brk_bytes = oxf_tcp_client_process_msg (con, buffer, broken, 106 | &brk_bytes, msg_bytes); 107 | } 108 | 109 | return NULL; 110 | } 111 | 112 | static struct oxf_client_con *oxf_tcp_client_connect (struct oxf_client *client, 113 | uint16_t cid, const char *addr, uint16_t port, oxf_rcv_reply_fn *recv_fn) 114 | { 115 | struct oxf_client_con *con; 116 | unsigned int len; 117 | struct timeval tv; 118 | 119 | if (cid >= OXF_SERVER_MAX_CON) { 120 | printf ("[ox-fabrics: Invalid connection ID: %d]\n", cid); 121 | return NULL; 122 | } 123 | 124 | if (client->connections[cid]) { 125 | printf ("[ox-fabrics: Connection already established: %d]\n", cid); 126 | return NULL; 127 | } 128 | 129 | con = calloc (1, sizeof (struct oxf_client_con)); 130 | if (!con) 131 | return NULL; 132 | 133 | con->cid = cid; 134 | con->client = client; 135 | con->recv_fn = recv_fn; 136 | 137 | if ( (con->sock_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0 ) { 138 | printf ("[ox-fabrics: Socket creation failure]\n"); 139 | free (con); 140 | return NULL; 141 | } 142 | 143 | len = sizeof (struct sockaddr); 144 | con->addr.sin_family = AF_INET; 145 | inet_aton (addr, (struct in_addr *) &con->addr.sin_addr.s_addr); 146 | con->addr.sin_port = htons(port); 147 | 148 | tv.tv_sec = 0; 149 | tv.tv_usec = OXF_RCV_TO; 150 | 151 | if (setsockopt(con->sock_fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0){ 152 | printf ("[ox-fabrics: Socket timeout failure.]\n"); 153 | goto NOT_CONNECTED; 154 | } 155 | 156 | if ( connect(con->sock_fd, (const struct sockaddr *) &con->addr , len) < 0){ 157 | printf ("[ox-fabrics: Socket connection failure.]\n"); 158 | goto NOT_CONNECTED; 159 | } 160 | 161 | con->running = 1; 162 | if (pthread_create(&con->recv_th, NULL, oxf_tcp_client_recv, (void *) con)){ 163 | printf ("[ox-fabrics: Receive reply thread not started.]\n"); 164 | goto NOT_CONNECTED; 165 | } 166 | 167 | client->connections[cid] = con; 168 | client->n_con++; 169 | 170 | return con; 171 | 172 | NOT_CONNECTED: 173 | con->running = 0; 174 | shutdown (con->sock_fd, 0); 175 | close (con->sock_fd); 176 | free (con); 177 | return NULL; 178 | } 179 | 180 | static int oxf_tcp_client_send (struct oxf_client_con *con, uint32_t size, 181 | const void *buf) 182 | { 183 | uint32_t ret; 184 | 185 | ret = send(con->sock_fd, buf, size, 0); 186 | if (ret != size) 187 | return -1; 188 | 189 | return 0; 190 | } 191 | 192 | static void oxf_tcp_client_disconnect (struct oxf_client_con *con) 193 | { 194 | if (con) { 195 | con->running = 0; 196 | pthread_join (con->recv_th, NULL); 197 | shutdown (con->sock_fd, 0); 198 | close (con->sock_fd); 199 | con->client->connections[con->cid] = NULL; 200 | con->client->n_con--; 201 | free (con); 202 | } 203 | } 204 | 205 | void oxf_tcp_client_exit (struct oxf_client *client) 206 | { 207 | free (client); 208 | } 209 | 210 | struct oxf_client_ops oxf_tcp_cli_ops = { 211 | .connect = oxf_tcp_client_connect, 212 | .disconnect = oxf_tcp_client_disconnect, 213 | .send = oxf_tcp_client_send 214 | }; 215 | 216 | struct oxf_client *oxf_tcp_client_init (void) 217 | { 218 | struct oxf_client *client; 219 | 220 | client = calloc (1, sizeof (struct oxf_client)); 221 | if (!client) 222 | return NULL; 223 | 224 | client->ops = &oxf_tcp_cli_ops; 225 | 226 | return client; 227 | } -------------------------------------------------------------------------------- /transport/ox-fabrics/udp-client.c: -------------------------------------------------------------------------------- 1 | /* OX: Open-Channel NVM Express SSD Controller 2 | * 3 | * - OX NVMe over UDP (client side) 4 | * 5 | * Copyright 2018 IT University of Copenhagen 6 | * 7 | * Written by Ivan Luiz Picoli 8 | * 9 | * Licensed under the Apache License, Version 2.0 (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * http://www.apache.org/licenses/LICENSE-2.0 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | * 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | static void *oxf_udp_client_recv (void *arg) 35 | { 36 | int n; 37 | unsigned int len; 38 | uint8_t buf[OXF_MAX_DGRAM + 1]; 39 | struct oxf_client_con *con = (struct oxf_client_con *) arg; 40 | 41 | len = sizeof (struct sockaddr); 42 | while (con->running) { 43 | n = recvfrom(con->sock_fd, (char *) buf, OXF_MAX_DGRAM, 44 | MSG_WAITALL, ( struct sockaddr *) &con->addr, &len); 45 | if (n > 0) 46 | con->recv_fn (n, (void *) buf); 47 | } 48 | 49 | return NULL; 50 | } 51 | 52 | static struct oxf_client_con *oxf_udp_client_connect (struct oxf_client *client, 53 | uint16_t cid, const char *addr, uint16_t port, oxf_rcv_reply_fn *recv_fn) 54 | { 55 | struct oxf_client_con *con; 56 | uint8_t connect[2], ack[2]; 57 | unsigned int len; 58 | struct timeval tv; 59 | int n; 60 | 61 | if (cid >= OXF_SERVER_MAX_CON) { 62 | printf ("[ox-fabrics: Invalid connection ID: %d]", cid); 63 | return NULL; 64 | } 65 | 66 | if (client->connections[cid]) { 67 | printf ("[ox-fabrics: Connection already established: %d]", cid); 68 | return NULL; 69 | } 70 | 71 | con = calloc (1, sizeof (struct oxf_client_con)); 72 | if (!con) 73 | return NULL; 74 | 75 | /* Change port for different connections */ 76 | port = port + cid; 77 | 78 | con->cid = cid; 79 | con->client = client; 80 | con->recv_fn = recv_fn; 81 | 82 | if ( (con->sock_fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) { 83 | free (con); 84 | return NULL; 85 | } 86 | 87 | con->addr.sin_family = AF_INET; 88 | inet_aton (addr, (struct in_addr *) &con->addr.sin_addr.s_addr); 89 | con->addr.sin_port = htons(port); 90 | 91 | /* Check connectivity */ 92 | connect[0] = OXF_CON_BYTE; 93 | len = sizeof (struct sockaddr); 94 | tv.tv_sec = 0; 95 | tv.tv_usec = OXF_RCV_TO; 96 | 97 | if (setsockopt(con->sock_fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0){ 98 | printf ("[ox-fabrics: Socket timeout failure.]\n"); 99 | goto NOT_CONNECTED; 100 | } 101 | 102 | sendto(con->sock_fd, (void *) connect, 1, MSG_CONFIRM, 103 | (const struct sockaddr *) &con->addr, len); 104 | 105 | n = recvfrom(con->sock_fd, (char *) ack, OXF_MAX_DGRAM, 106 | MSG_WAITALL, ( struct sockaddr *) &con->addr, &len); 107 | if (n != 1) { 108 | printf ("[ox-fabrics: Server does not respond.]\n"); 109 | goto NOT_CONNECTED; 110 | } 111 | 112 | if (ack[0] != OXF_ACK_BYTE) { 113 | printf ("[ox-fabrics: Server responded, but byte is incorrect: %x]\n", 114 | ack[0]); 115 | goto NOT_CONNECTED; 116 | } 117 | 118 | con->running = 1; 119 | if (pthread_create(&con->recv_th, NULL, oxf_udp_client_recv, (void *) con)){ 120 | printf ("[ox-fabrics: Receive reply thread not started.]"); 121 | goto NOT_CONNECTED; 122 | } 123 | 124 | client->connections[cid] = con; 125 | client->n_con++; 126 | 127 | return con; 128 | 129 | NOT_CONNECTED: 130 | con->running = 0; 131 | shutdown (con->sock_fd, 0); 132 | close (con->sock_fd); 133 | free (con); 134 | return NULL; 135 | } 136 | 137 | static int oxf_udp_client_send (struct oxf_client_con *con, uint32_t size, 138 | const void *buf) 139 | { 140 | unsigned int len; 141 | uint32_t ret; 142 | 143 | len = sizeof (struct sockaddr); 144 | 145 | ret = sendto(con->sock_fd, buf, size, MSG_CONFIRM, 146 | (const struct sockaddr *) &con->addr, len); 147 | if (ret != size) 148 | return -1; 149 | 150 | return 0; 151 | } 152 | 153 | static void oxf_udp_client_disconnect (struct oxf_client_con *con) 154 | { 155 | if (con) { 156 | con->running = 0; 157 | pthread_join (con->recv_th, NULL); 158 | shutdown (con->sock_fd, 0); 159 | close (con->sock_fd); 160 | con->client->connections[con->cid] = NULL; 161 | con->client->n_con--; 162 | free (con); 163 | } 164 | } 165 | 166 | void oxf_udp_client_exit (struct oxf_client *client) 167 | { 168 | free (client); 169 | } 170 | 171 | struct oxf_client_ops oxf_udp_cli_ops = { 172 | .connect = oxf_udp_client_connect, 173 | .disconnect = oxf_udp_client_disconnect, 174 | .send = oxf_udp_client_send 175 | }; 176 | 177 | struct oxf_client *oxf_udp_client_init (void) 178 | { 179 | struct oxf_client *client; 180 | 181 | client = calloc (1, sizeof (struct oxf_client)); 182 | if (!client) 183 | return NULL; 184 | 185 | client->ops = &oxf_udp_cli_ops; 186 | 187 | return client; 188 | } -------------------------------------------------------------------------------- /transport/ox-fabrics/udp-server.c: -------------------------------------------------------------------------------- 1 | /* OX: Open-Channel NVM Express SSD Controller 2 | * 3 | * - OX NVMe over UDP (server side) 4 | * 5 | * Copyright 2018 IT University of Copenhagen 6 | * 7 | * Written by Ivan Luiz Picoli 8 | * 9 | * Licensed under the Apache License, Version 2.0 (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * http://www.apache.org/licenses/LICENSE-2.0 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | * 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | static struct oxf_server_con *oxf_udp_server_bind (struct oxf_server *server, 36 | uint16_t cid, const char *addr, uint16_t port) 37 | { 38 | struct oxf_server_con *con; 39 | struct timeval tv; 40 | 41 | if (cid > OXF_SERVER_MAX_CON) { 42 | log_err ("[ox-fabrics (bind): Invalid connection ID: %d]", cid); 43 | return NULL; 44 | } 45 | 46 | if (server->connections[cid]) { 47 | log_err ("[ox-fabrics (bind): Connection already established: %d]", cid); 48 | return NULL; 49 | } 50 | 51 | /* Change the port for different connections */ 52 | port = port + cid; 53 | 54 | con = malloc (sizeof (struct oxf_server_con)); 55 | if (!con) 56 | return NULL; 57 | 58 | con->cid = cid; 59 | con->server = server; 60 | con->running = 0; 61 | 62 | if ( (con->sock_fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) { 63 | free (con); 64 | return NULL; 65 | } 66 | 67 | con->addr.sin_family = AF_INET; 68 | inet_aton (addr, (struct in_addr *) &con->addr.sin_addr.s_addr); 69 | con->addr.sin_port = htons(port); 70 | 71 | if ( bind(con->sock_fd, (const struct sockaddr *) &con->addr, 72 | sizeof(con->addr)) < 0 ) 73 | { 74 | log_err ("[ox-fabrics (bind): Socket bind failure.]"); 75 | goto ERR; 76 | } 77 | 78 | /* Set socket timeout */ 79 | tv.tv_sec = 0; 80 | tv.tv_usec = OXF_RCV_TO; 81 | 82 | if (setsockopt(con->sock_fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0){ 83 | log_err ("[ox-fabrics (bind): Socket timeout failure.]"); 84 | goto ERR; 85 | } 86 | 87 | server->connections[cid] = con; 88 | server->n_con++; 89 | 90 | memcpy (con->haddr.addr, addr, 15); 91 | con->haddr.addr[15] = '\0'; 92 | con->haddr.port = port; 93 | 94 | return con; 95 | 96 | ERR: 97 | shutdown (con->sock_fd, 2); 98 | close (con->sock_fd); 99 | free (con); 100 | return NULL; 101 | } 102 | 103 | static void oxf_udp_server_unbind (struct oxf_server_con *con) 104 | { 105 | if (con) { 106 | shutdown (con->sock_fd, 0); 107 | close (con->sock_fd); 108 | con->server->connections[con->cid] = NULL; 109 | con->server->n_con--; 110 | free (con); 111 | } 112 | } 113 | 114 | static void *oxf_udp_server_con_th (void *arg) 115 | { 116 | struct oxf_server_con *con = (struct oxf_server_con *) arg; 117 | struct sockaddr_in client; 118 | uint8_t buffer[OXF_MAX_DGRAM + 1]; 119 | uint8_t ack[2]; 120 | unsigned int len; 121 | int n; 122 | 123 | ack[0] = OXF_ACK_BYTE; 124 | len = sizeof (struct sockaddr); 125 | while (con->running) { 126 | n = recvfrom(con->sock_fd, (char *) buffer, OXF_MAX_DGRAM, 127 | MSG_WAITALL, ( struct sockaddr *) &client, &len); 128 | buffer[n] = '\0'; 129 | 130 | if (n < 0) 131 | continue; 132 | 133 | if (n == 1 && (buffer[0] == OXF_CON_BYTE) ) 134 | goto ACK; 135 | 136 | con->rcv_fn (n, (void *) buffer, (void *) &client); 137 | continue; 138 | 139 | ACK: 140 | /* Send a MSG_CONFIRM to the client to confirm connection */ 141 | if (sendto(con->sock_fd, (char *) ack, 1, MSG_CONFIRM, 142 | (const struct sockaddr *) &client, len) != 1) 143 | log_err ("[ox-fabrics: Connect ACK hasn't been sent.]"); 144 | 145 | } 146 | 147 | log_err ("[ox-fabrics: Connection %d is closed.]", con->cid); 148 | 149 | return NULL; 150 | } 151 | 152 | static int oxf_udp_server_reply(struct oxf_server_con *con, const void *buf, 153 | uint32_t size, void *recv_cli) 154 | { 155 | struct sockaddr *client = (struct sockaddr *) recv_cli; 156 | unsigned int len; 157 | int ret; 158 | 159 | len = sizeof (struct sockaddr); 160 | ret = sendto(con->sock_fd, buf, size, MSG_CONFIRM, 161 | (struct sockaddr *) client, len); 162 | if (ret != size) { 163 | log_err ("[ox-fabrics: Completion reply hasn't been sent. %d]", ret); 164 | return -1; 165 | } 166 | 167 | return 0; 168 | } 169 | 170 | static int oxf_udp_server_con_start (struct oxf_server_con *con, oxf_rcv_fn *fn) 171 | { 172 | con->running = 1; 173 | con->rcv_fn = fn; 174 | 175 | if (pthread_create (&con->tid, NULL, oxf_udp_server_con_th, con)) { 176 | log_err ("[ox-fabrics: Connection not started.]"); 177 | con->running = 0; 178 | return -1; 179 | } 180 | 181 | return 0; 182 | } 183 | 184 | static void oxf_udp_server_con_stop (struct oxf_server_con *con) 185 | { 186 | if (con && con->running) 187 | con->running = 0; 188 | else 189 | return; 190 | 191 | pthread_join (con->tid, NULL); 192 | } 193 | 194 | void oxf_udp_server_exit (struct oxf_server *server) 195 | { 196 | free (server); 197 | } 198 | 199 | struct oxf_server_ops oxf_udp_srv_ops = { 200 | .bind = oxf_udp_server_bind, 201 | .unbind = oxf_udp_server_unbind, 202 | .start = oxf_udp_server_con_start, 203 | .stop = oxf_udp_server_con_stop, 204 | .reply = oxf_udp_server_reply 205 | }; 206 | 207 | struct oxf_server *oxf_udp_server_init (void) 208 | { 209 | struct oxf_server *server; 210 | 211 | server = calloc (1, sizeof (struct oxf_server)); 212 | if (!server) 213 | return NULL; 214 | 215 | server->ops = &oxf_udp_srv_ops; 216 | 217 | log_info ("[ox-fabrics: Protocol -> UDP\n"); 218 | 219 | return server; 220 | } -------------------------------------------------------------------------------- /transport/pci_fpga_3.0.1/pci_fpga_301.h: -------------------------------------------------------------------------------- 1 | #ifndef PCI_DFC_H 2 | #define PCI_DFC_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #define DEV_MEM "/dev/mem" 9 | #define DEV_PPMAP "/dev/ppmap" 10 | #define DEV_UIO0 "/dev/uio0" 11 | #define DEV_UIO1 "/dev/uio8" 12 | 13 | #define PCIE_MAX_BARS 6 14 | #define PCIE_MAX_EPS 2 15 | 16 | /*PCIe3 config space address - According to LS2 DS*/ 17 | #define PCI_CONF_ADDR 0x3600000 18 | 19 | #define HOST_OUTBOUND_ADDR 0x1400000000 /*Outbound iATU Address*/ 20 | #define HOST_OUTBOUND_SIZE 8ULL*1024*1024*1024 /*Outbound iATU size 4GB*/ 21 | 22 | #define DEV_MEM "/dev/mem" 23 | 24 | #define PCI_OFFS_NVME_REGS 0x0000 25 | #define PCI_OFFS_FIFO_ENTRY 0x0001 26 | #define PCI_OFFS_FIFO_COUNT 0x0002 27 | #define PCI_OFFS_FIFO_RESET 0x0003 28 | #define PCI_OFFS_FIFO_IRQ_CSR 0x0004 /*Unused in Target*/ 29 | #define PCI_OFFS_IOSQDBST 0x0005 /*5 to 8: 4 32 bit regs*/ 30 | #define PCI_OFFS_GPIO_CSR 0x0009 31 | 32 | #define PCI_OFFS_DMA_CSR 0x2000 33 | #define PCI_OFFS_ICR 0x2001 /*Present from PCIe2 only*/ 34 | #define PCI_OFFS_DMA_TABLE_SZ 0x2002 35 | #define PCI_OFFS_DMA_TABLE 0x40000 36 | 37 | typedef struct DmaRegs { 38 | uint32_t *csr[2]; 39 | uint32_t *icr[2]; 40 | uint32_t *table_sz[2]; 41 | uint64_t *table[2]; 42 | } DmaRegs; 43 | 44 | struct pci_msix_table { 45 | void *addr_ptr; 46 | void *data_ptr; 47 | void *msix_en_ptr; 48 | }; 49 | 50 | struct pci_msi_table { 51 | void *addr_ptr; 52 | void *data_ptr; 53 | void *msi_en_ptr; 54 | }; 55 | 56 | struct pci_device { 57 | uint8_t totalBars; 58 | struct nvm_memory_region bar[PCIE_MAX_BARS]; 59 | }; 60 | 61 | typedef struct fifo_data { 62 | uint64_t new_val; 63 | uint64_t offset; 64 | } fifo_data; 65 | 66 | struct pci_ctrl { 67 | struct pci_device rc; 68 | struct pci_device dev; 69 | struct nvm_memory_region io_mem; 70 | struct nvm_memory_region msix_mem; 71 | struct pci_msix_table msix; 72 | struct pci_msi_table msi; 73 | uint8_t irq_mode; 74 | int fd_mem; 75 | uint32_t *fifo_reg; 76 | uint32_t *fifo_count; 77 | uint32_t *fifo_msi; 78 | uint32_t *fifo_reset; 79 | uint32_t *iosqdb_bits; 80 | uint32_t *gpio_csr; 81 | uint32_t *icr[2]; 82 | NvmeRegs *nvme_regs; 83 | DmaRegs dma_regs; 84 | }; 85 | 86 | static inline void reset_iosqdb_bits (struct pci_ctrl *pci) 87 | { 88 | /*Clear IOSQDB_STATUS REGISTER*/ 89 | int i = 0; 90 | for(;i<4;i++) memset((void *)(pci->iosqdb_bits + (i * 0x01)), 0, 91 | sizeof(uint32_t)); 92 | } 93 | 94 | static inline void reset_fifo (struct pci_ctrl *pci) 95 | { 96 | /*Clear FIFO*/ 97 | *(pci->fifo_reset) = 0x0; 98 | *(pci->fifo_reset) = 0x1; 99 | *(pci->fifo_reset) = 0x0; 100 | } 101 | 102 | static inline void reset_nvmeregs (struct pci_ctrl *pci) 103 | { 104 | /*Clear NVME registers*/ 105 | *(pci->fifo_reset) = 0x0; 106 | *(pci->fifo_reset) = 0x2; 107 | *(pci->fifo_reset) = 0x0; 108 | } 109 | 110 | #endif /* PCI_DFC_H */ --------------------------------------------------------------------------------