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$"); 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 flags, error; 146 147 NDINIT(&nd, LOOKUP, NOFOLLOW, 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 NDFREE(&nd, NDF_ONLY_PNBUF); 161 162 /* We just unlock so we hold a reference */ 163 VOP_UNLOCK(nd.ni_vp, 0); 164 165 swap->swap_vp = nd.ni_vp; 166 167 return (0); 168} 169 170static void 171swap_file_close(struct chip_swap *swap) 172{ 173 174 if (swap == NULL) 175 return; 176 177 if (swap->swap_vp == NULL) 178 return; 179 180 vn_close(swap->swap_vp, FWRITE, swap->swap_cred, curthread); 181 crfree(swap->swap_cred); 182} 183 184static int 185swap_file_write(struct chip_swap *swap, struct block_state *blk_state) 186{ 187 struct block_space *blk_space; 188 struct thread *td; 189 struct mount *mp; 190 struct vnode *vp; 191 struct uio auio; 192 struct iovec aiov; 193 194 if (swap == NULL || blk_state == NULL) 195 return (-1); 196 197 blk_space = blk_state->blk_sp; 198 if (blk_state->offset == -1) { 199 blk_state->offset = swap->swap_offset; 200 swap->swap_offset += swap->blk_size; 201 } 202 203 nand_debug(NDBG_SIM,"saving %p[%p] at %x\n", 204 blk_space, blk_space->blk_ptr, blk_state->offset); 205 206 bzero(&aiov, sizeof(aiov)); 207 bzero(&auio, sizeof(auio)); 208 209 aiov.iov_base = blk_space->blk_ptr; 210 aiov.iov_len = swap->blk_size; 211 td = curthread; 212 vp = swap->swap_vp; 213 214 auio.uio_iov = &aiov; 215 auio.uio_offset = blk_state->offset; 216 auio.uio_segflg = UIO_SYSSPACE; 217 auio.uio_rw = UIO_WRITE; 218 auio.uio_iovcnt = 1; 219 auio.uio_resid = swap->blk_size; 220 auio.uio_td = td; 221 222 vn_start_write(vp, &mp, V_WAIT); 223 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 224 VOP_WRITE(vp, &auio, IO_UNIT, swap->swap_cred); 225 VOP_UNLOCK(vp, 0); 226 vn_finished_write(mp); 227 228 return (0); 229} 230 231static int 232swap_file_read(struct chip_swap *swap, struct block_state *blk_state) 233{ 234 struct block_space *blk_space; 235 struct thread *td; 236 struct vnode *vp; 237 struct uio auio; 238 struct iovec aiov; 239 240 if (swap == NULL || blk_state == NULL) 241 return (-1); 242 243 blk_space = blk_state->blk_sp; 244 245 nand_debug(NDBG_SIM,"restore %p[%p] at %x\n", 246 blk_space, blk_space->blk_ptr, blk_state->offset); 247 248 bzero(&aiov, sizeof(aiov)); 249 bzero(&auio, sizeof(auio)); 250 251 aiov.iov_base = blk_space->blk_ptr; 252 aiov.iov_len = swap->blk_size; 253 td = curthread; 254 vp = swap->swap_vp; 255 256 auio.uio_iov = &aiov; 257 auio.uio_offset = blk_state->offset; 258 auio.uio_segflg = UIO_SYSSPACE; 259 auio.uio_rw = UIO_READ; 260 auio.uio_iovcnt = 1; 261 auio.uio_resid = swap->blk_size; 262 auio.uio_td = td; 263 264 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 265 VOP_READ(vp, &auio, 0, swap->swap_cred); 266 VOP_UNLOCK(vp, 0); 267 268 return (0); 269} 270 271struct chip_swap * 272nandsim_swap_init(const char *swap_file, uint32_t nof_blks, uint32_t blk_size) 273{ 274 struct chip_swap *swap; 275 int err = 0; 276 277 if ((swap_file == NULL) || (nof_blks == 0) || (blk_size == 0)) 278 return (NULL); 279 280 swap = malloc(sizeof(*swap), M_NANDSIM, M_WAITOK | M_ZERO); 281 282 SLIST_INIT(&swap->free_bs); 283 STAILQ_INIT(&swap->used_bs); 284 swap->blk_size = blk_size; 285 swap->nof_blks = nof_blks; 286 287 err = init_block_state(swap); 288 if (err) { 289 nandsim_swap_destroy(swap); 290 return (NULL); 291 } 292 293 err = create_buffers(swap); 294 if (err) { 295 nandsim_swap_destroy(swap); 296 return (NULL); 297 } 298 299 err = swap_file_open(swap, swap_file); 300 if (err) { 301 nandsim_swap_destroy(swap); 302 return (NULL); 303 } 304 305 return (swap); 306} 307 308void 309nandsim_swap_destroy(struct chip_swap *swap) 310{ 311 312 if (swap == NULL) 313 return; 314 315 destroy_block_state(swap); 316 destroy_buffers(swap); 317 swap_file_close(swap); 318 free(swap, M_NANDSIM); 319} 320 321struct block_space * 322get_bs(struct chip_swap *swap, uint32_t block, uint8_t writing) 323{ 324 struct block_state *blk_state, *old_blk_state = NULL; 325 struct block_space *blk_space; 326 327 if (swap == NULL || (block >= swap->nof_blks)) 328 return (NULL); 329 330 blk_state = &swap->blk_state[block]; 331 nand_debug(NDBG_SIM,"blk_state %x\n", blk_state->status); 332 333 if (blk_state->status & BLOCK_ALLOCATED) { 334 blk_space = blk_state->blk_sp; 335 } else { 336 blk_space = SLIST_FIRST(&swap->free_bs); 337 if (blk_space) { 338 SLIST_REMOVE_HEAD(&swap->free_bs, free_link); 339 STAILQ_INSERT_TAIL(&swap->used_bs, blk_space, 340 used_link); 341 } else { 342 blk_space = STAILQ_FIRST(&swap->used_bs); 343 old_blk_state = blk_space->blk_state; 344 STAILQ_REMOVE_HEAD(&swap->used_bs, used_link); 345 STAILQ_INSERT_TAIL(&swap->used_bs, blk_space, 346 used_link); 347 if (old_blk_state->status & BLOCK_DIRTY) { 348 swap_file_write(swap, old_blk_state); 349 old_blk_state->status &= ~BLOCK_DIRTY; 350 old_blk_state->status |= BLOCK_SWAPPED; 351 } 352 } 353 } 354 355 if (blk_space == NULL) 356 return (NULL); 357 358 if (old_blk_state != NULL) { 359 old_blk_state->status &= ~BLOCK_ALLOCATED; 360 old_blk_state->blk_sp = NULL; 361 } 362 363 blk_state->blk_sp = blk_space; 364 blk_space->blk_state = blk_state; 365 366 if (!(blk_state->status & BLOCK_ALLOCATED)) { 367 if (blk_state->status & BLOCK_SWAPPED) 368 swap_file_read(swap, blk_state); 369 else 370 memset(blk_space->blk_ptr, 0xff, swap->blk_size); 371 blk_state->status |= BLOCK_ALLOCATED; 372 } 373 374 if (writing) 375 blk_state->status |= BLOCK_DIRTY; 376 377 nand_debug(NDBG_SIM,"get_bs returned %p[%p] state %x\n", blk_space, 378 blk_space->blk_ptr, blk_state->status); 379 380 return (blk_space); 381} 382