nandsim_swap.c revision 235537
1/*- 2 * Copyright (C) 2009-2012 Semihalf 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27#include <sys/cdefs.h> 28__FBSDID("$FreeBSD: head/sys/dev/nand/nandsim_swap.c 235537 2012-05-17 10:11:18Z gber $"); 29 30#include <sys/param.h> 31#include <sys/types.h> 32#include <sys/systm.h> 33#include <sys/malloc.h> 34#include <sys/queue.h> 35#include <sys/fcntl.h> 36#include <sys/proc.h> 37#include <sys/namei.h> 38#include <sys/lock.h> 39#include <sys/vnode.h> 40#include <sys/mount.h> 41 42#include <dev/nand/nandsim_chip.h> 43#include <dev/nand/nandsim_swap.h> 44 45static int init_block_state(struct chip_swap *); 46static void destroy_block_state(struct chip_swap *); 47 48static int create_buffers(struct chip_swap *); 49static void destroy_buffers(struct chip_swap *); 50 51static int swap_file_open(struct chip_swap *, const char *); 52static void swap_file_close(struct chip_swap *); 53static int swap_file_write(struct chip_swap *, struct block_state *); 54static int swap_file_read(struct chip_swap *, struct block_state *); 55 56#define CHIP_SWAP_CMODE 0600 57#define CHIP_SWAP_BLOCKSPACES 2 58 59static int 60init_block_state(struct chip_swap *swap) 61{ 62 struct block_state *blk_state; 63 int i; 64 65 if (swap == NULL) 66 return (-1); 67 68 blk_state = malloc(swap->nof_blks * sizeof(struct block_state), 69 M_NANDSIM, M_WAITOK | M_ZERO); 70 71 for (i = 0; i < swap->nof_blks; i++) 72 blk_state[i].offset = 0xffffffff; 73 74 swap->blk_state = blk_state; 75 76 return (0); 77} 78 79static void 80destroy_block_state(struct chip_swap *swap) 81{ 82 83 if (swap == NULL) 84 return; 85 86 if (swap->blk_state != NULL) 87 free(swap->blk_state, M_NANDSIM); 88} 89 90static int 91create_buffers(struct chip_swap *swap) 92{ 93 struct block_space *block_space; 94 void *block; 95 int i; 96 97 for (i = 0; i < CHIP_SWAP_BLOCKSPACES; i++) { 98 block_space = malloc(sizeof(*block_space), M_NANDSIM, M_WAITOK); 99 block = malloc(swap->blk_size, M_NANDSIM, M_WAITOK); 100 block_space->blk_ptr = block; 101 SLIST_INSERT_HEAD(&swap->free_bs, block_space, free_link); 102 nand_debug(NDBG_SIM,"created blk_space %p[%p]\n", block_space, 103 block); 104 } 105 106 if (i == 0) 107 return (-1); 108 109 return (0); 110} 111 112static void 113destroy_buffers(struct chip_swap *swap) 114{ 115 struct block_space *blk_space; 116 117 if (swap == NULL) 118 return; 119 120 blk_space = SLIST_FIRST(&swap->free_bs); 121 while (blk_space) { 122 SLIST_REMOVE_HEAD(&swap->free_bs, free_link); 123 nand_debug(NDBG_SIM,"destroyed blk_space %p[%p]\n", 124 blk_space, blk_space->blk_ptr); 125 free(blk_space->blk_ptr, M_NANDSIM); 126 free(blk_space, M_NANDSIM); 127 blk_space = SLIST_FIRST(&swap->free_bs); 128 } 129 130 blk_space = STAILQ_FIRST(&swap->used_bs); 131 while (blk_space) { 132 STAILQ_REMOVE_HEAD(&swap->used_bs, used_link); 133 nand_debug(NDBG_SIM,"destroyed blk_space %p[%p]\n", 134 blk_space, blk_space->blk_ptr); 135 free(blk_space->blk_ptr, M_NANDSIM); 136 free(blk_space, M_NANDSIM); 137 blk_space = STAILQ_FIRST(&swap->used_bs); 138 } 139} 140 141static int 142swap_file_open(struct chip_swap *swap, const char *swap_file) 143{ 144 struct nameidata nd; 145 int vfslocked, flags, error; 146 147 NDINIT(&nd, LOOKUP, NOFOLLOW | MPSAFE, UIO_SYSSPACE, swap_file, 148 curthread); 149 150 flags = FWRITE | FREAD | O_NOFOLLOW | O_CREAT | O_TRUNC; 151 152 error = vn_open(&nd, &flags, CHIP_SWAP_CMODE, NULL); 153 if (error) { 154 nand_debug(NDBG_SIM,"Cannot create swap file %s", swap_file); 155 NDFREE(&nd, NDF_ONLY_PNBUF); 156 return (error); 157 } 158 159 swap->swap_cred = crhold(curthread->td_ucred); 160 vfslocked = NDHASGIANT(&nd); 161 NDFREE(&nd, NDF_ONLY_PNBUF); 162 163 /* We just unlock so we hold a reference */ 164 VOP_UNLOCK(nd.ni_vp, 0); 165 VFS_UNLOCK_GIANT(vfslocked); 166 167 swap->swap_vp = nd.ni_vp; 168 169 return (0); 170} 171 172static void 173swap_file_close(struct chip_swap *swap) 174{ 175 176 if (swap == NULL) 177 return; 178 179 if (swap->swap_vp == NULL) 180 return; 181 182 vn_close(swap->swap_vp, FWRITE, swap->swap_cred, curthread); 183 crfree(swap->swap_cred); 184} 185 186static int 187swap_file_write(struct chip_swap *swap, struct block_state *blk_state) 188{ 189 struct block_space *blk_space; 190 struct thread *td; 191 struct mount *mp; 192 struct vnode *vp; 193 struct uio auio; 194 struct iovec aiov; 195 int vfslocked; 196 197 if (swap == NULL || blk_state == NULL) 198 return (-1); 199 200 blk_space = blk_state->blk_sp; 201 if (blk_state->offset == -1) { 202 blk_state->offset = swap->swap_offset; 203 swap->swap_offset += swap->blk_size; 204 } 205 206 nand_debug(NDBG_SIM,"saving %p[%p] at %x\n", 207 blk_space, blk_space->blk_ptr, blk_state->offset); 208 209 bzero(&aiov, sizeof(aiov)); 210 bzero(&auio, sizeof(auio)); 211 212 aiov.iov_base = blk_space->blk_ptr; 213 aiov.iov_len = swap->blk_size; 214 td = curthread; 215 vp = swap->swap_vp; 216 217 auio.uio_iov = &aiov; 218 auio.uio_offset = blk_state->offset; 219 auio.uio_segflg = UIO_SYSSPACE; 220 auio.uio_rw = UIO_WRITE; 221 auio.uio_iovcnt = 1; 222 auio.uio_resid = swap->blk_size; 223 auio.uio_td = td; 224 225 vfslocked = VFS_LOCK_GIANT(vp->v_mount); 226 vn_start_write(vp, &mp, V_WAIT); 227 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 228 VOP_WRITE(vp, &auio, IO_UNIT, swap->swap_cred); 229 VOP_UNLOCK(vp, 0); 230 vn_finished_write(mp); 231 VFS_UNLOCK_GIANT(vfslocked); 232 233 return (0); 234} 235 236static int 237swap_file_read(struct chip_swap *swap, struct block_state *blk_state) 238{ 239 struct block_space *blk_space; 240 struct thread *td; 241 struct vnode *vp; 242 struct uio auio; 243 struct iovec aiov; 244 int vfslocked; 245 246 if (swap == NULL || blk_state == NULL) 247 return (-1); 248 249 blk_space = blk_state->blk_sp; 250 251 nand_debug(NDBG_SIM,"restore %p[%p] at %x\n", 252 blk_space, blk_space->blk_ptr, blk_state->offset); 253 254 bzero(&aiov, sizeof(aiov)); 255 bzero(&auio, sizeof(auio)); 256 257 aiov.iov_base = blk_space->blk_ptr; 258 aiov.iov_len = swap->blk_size; 259 td = curthread; 260 vp = swap->swap_vp; 261 262 auio.uio_iov = &aiov; 263 auio.uio_offset = blk_state->offset; 264 auio.uio_segflg = UIO_SYSSPACE; 265 auio.uio_rw = UIO_READ; 266 auio.uio_iovcnt = 1; 267 auio.uio_resid = swap->blk_size; 268 auio.uio_td = td; 269 270 vfslocked = VFS_LOCK_GIANT(vp->v_mount); 271 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 272 VOP_READ(vp, &auio, 0, swap->swap_cred); 273 VOP_UNLOCK(vp, 0); 274 VFS_UNLOCK_GIANT(vfslocked); 275 276 return (0); 277} 278 279struct chip_swap * 280nandsim_swap_init(const char *swap_file, uint32_t nof_blks, uint32_t blk_size) 281{ 282 struct chip_swap *swap; 283 int err = 0; 284 285 if ((swap_file == NULL) || (nof_blks == 0) || (blk_size == 0)) 286 return (NULL); 287 288 swap = malloc(sizeof(*swap), M_NANDSIM, M_WAITOK | M_ZERO); 289 290 SLIST_INIT(&swap->free_bs); 291 STAILQ_INIT(&swap->used_bs); 292 swap->blk_size = blk_size; 293 swap->nof_blks = nof_blks; 294 295 err = init_block_state(swap); 296 if (err) { 297 nandsim_swap_destroy(swap); 298 return (NULL); 299 } 300 301 err = create_buffers(swap); 302 if (err) { 303 nandsim_swap_destroy(swap); 304 return (NULL); 305 } 306 307 err = swap_file_open(swap, swap_file); 308 if (err) { 309 nandsim_swap_destroy(swap); 310 return (NULL); 311 } 312 313 return (swap); 314} 315 316void 317nandsim_swap_destroy(struct chip_swap *swap) 318{ 319 320 if (swap == NULL) 321 return; 322 323 destroy_block_state(swap); 324 destroy_buffers(swap); 325 swap_file_close(swap); 326 free(swap, M_NANDSIM); 327} 328 329struct block_space * 330get_bs(struct chip_swap *swap, uint32_t block, uint8_t writing) 331{ 332 struct block_state *blk_state, *old_blk_state = NULL; 333 struct block_space *blk_space; 334 335 if (swap == NULL || (block >= swap->nof_blks)) 336 return (NULL); 337 338 blk_state = &swap->blk_state[block]; 339 nand_debug(NDBG_SIM,"blk_state %x\n", blk_state->status); 340 341 if (blk_state->status & BLOCK_ALLOCATED) { 342 blk_space = blk_state->blk_sp; 343 } else { 344 blk_space = SLIST_FIRST(&swap->free_bs); 345 if (blk_space) { 346 SLIST_REMOVE_HEAD(&swap->free_bs, free_link); 347 STAILQ_INSERT_TAIL(&swap->used_bs, blk_space, 348 used_link); 349 } else { 350 blk_space = STAILQ_FIRST(&swap->used_bs); 351 old_blk_state = blk_space->blk_state; 352 STAILQ_REMOVE_HEAD(&swap->used_bs, used_link); 353 STAILQ_INSERT_TAIL(&swap->used_bs, blk_space, 354 used_link); 355 if (old_blk_state->status & BLOCK_DIRTY) { 356 swap_file_write(swap, old_blk_state); 357 old_blk_state->status &= ~BLOCK_DIRTY; 358 old_blk_state->status |= BLOCK_SWAPPED; 359 } 360 } 361 } 362 363 if (blk_space == NULL) 364 return (NULL); 365 366 if (old_blk_state != NULL) { 367 old_blk_state->status &= ~BLOCK_ALLOCATED; 368 old_blk_state->blk_sp = NULL; 369 } 370 371 blk_state->blk_sp = blk_space; 372 blk_space->blk_state = blk_state; 373 374 if (!(blk_state->status & BLOCK_ALLOCATED)) { 375 if (blk_state->status & BLOCK_SWAPPED) 376 swap_file_read(swap, blk_state); 377 else 378 memset(blk_space->blk_ptr, 0xff, swap->blk_size); 379 blk_state->status |= BLOCK_ALLOCATED; 380 } 381 382 if (writing) 383 blk_state->status |= BLOCK_DIRTY; 384 385 nand_debug(NDBG_SIM,"get_bs returned %p[%p] state %x\n", blk_space, 386 blk_space->blk_ptr, blk_state->status); 387 388 return (blk_space); 389} 390