1235537Sgber/*- 2235537Sgber * Copyright (C) 2009-2012 Semihalf 3235537Sgber * All rights reserved. 4235537Sgber * 5235537Sgber * Redistribution and use in source and binary forms, with or without 6235537Sgber * modification, are permitted provided that the following conditions 7235537Sgber * are met: 8235537Sgber * 1. Redistributions of source code must retain the above copyright 9235537Sgber * notice, this list of conditions and the following disclaimer. 10235537Sgber * 2. Redistributions in binary form must reproduce the above copyright 11235537Sgber * notice, this list of conditions and the following disclaimer in the 12235537Sgber * documentation and/or other materials provided with the distribution. 13235537Sgber * 14235537Sgber * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15235537Sgber * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16235537Sgber * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17235537Sgber * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18235537Sgber * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19235537Sgber * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20235537Sgber * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21235537Sgber * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22235537Sgber * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23235537Sgber * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24235537Sgber * SUCH DAMAGE. 25235537Sgber */ 26235537Sgber 27235537Sgber#include <sys/cdefs.h> 28235537Sgber__FBSDID("$FreeBSD$"); 29235537Sgber 30235537Sgber#include <sys/param.h> 31235537Sgber#include <sys/types.h> 32235537Sgber#include <sys/systm.h> 33235537Sgber#include <sys/malloc.h> 34235537Sgber#include <sys/queue.h> 35235537Sgber#include <sys/fcntl.h> 36235537Sgber#include <sys/proc.h> 37235537Sgber#include <sys/namei.h> 38235537Sgber#include <sys/lock.h> 39235537Sgber#include <sys/vnode.h> 40235537Sgber#include <sys/mount.h> 41235537Sgber 42235537Sgber#include <dev/nand/nandsim_chip.h> 43235537Sgber#include <dev/nand/nandsim_swap.h> 44235537Sgber 45235537Sgberstatic int init_block_state(struct chip_swap *); 46235537Sgberstatic void destroy_block_state(struct chip_swap *); 47235537Sgber 48235537Sgberstatic int create_buffers(struct chip_swap *); 49235537Sgberstatic void destroy_buffers(struct chip_swap *); 50235537Sgber 51235537Sgberstatic int swap_file_open(struct chip_swap *, const char *); 52235537Sgberstatic void swap_file_close(struct chip_swap *); 53235537Sgberstatic int swap_file_write(struct chip_swap *, struct block_state *); 54235537Sgberstatic int swap_file_read(struct chip_swap *, struct block_state *); 55235537Sgber 56235537Sgber#define CHIP_SWAP_CMODE 0600 57235537Sgber#define CHIP_SWAP_BLOCKSPACES 2 58235537Sgber 59235537Sgberstatic int 60235537Sgberinit_block_state(struct chip_swap *swap) 61235537Sgber{ 62235537Sgber struct block_state *blk_state; 63235537Sgber int i; 64235537Sgber 65235537Sgber if (swap == NULL) 66235537Sgber return (-1); 67235537Sgber 68235537Sgber blk_state = malloc(swap->nof_blks * sizeof(struct block_state), 69235537Sgber M_NANDSIM, M_WAITOK | M_ZERO); 70235537Sgber 71235537Sgber for (i = 0; i < swap->nof_blks; i++) 72235537Sgber blk_state[i].offset = 0xffffffff; 73235537Sgber 74235537Sgber swap->blk_state = blk_state; 75235537Sgber 76235537Sgber return (0); 77235537Sgber} 78235537Sgber 79235537Sgberstatic void 80235537Sgberdestroy_block_state(struct chip_swap *swap) 81235537Sgber{ 82235537Sgber 83235537Sgber if (swap == NULL) 84235537Sgber return; 85235537Sgber 86235537Sgber if (swap->blk_state != NULL) 87235537Sgber free(swap->blk_state, M_NANDSIM); 88235537Sgber} 89235537Sgber 90235537Sgberstatic int 91235537Sgbercreate_buffers(struct chip_swap *swap) 92235537Sgber{ 93235537Sgber struct block_space *block_space; 94235537Sgber void *block; 95235537Sgber int i; 96235537Sgber 97235537Sgber for (i = 0; i < CHIP_SWAP_BLOCKSPACES; i++) { 98235537Sgber block_space = malloc(sizeof(*block_space), M_NANDSIM, M_WAITOK); 99235537Sgber block = malloc(swap->blk_size, M_NANDSIM, M_WAITOK); 100235537Sgber block_space->blk_ptr = block; 101235537Sgber SLIST_INSERT_HEAD(&swap->free_bs, block_space, free_link); 102235537Sgber nand_debug(NDBG_SIM,"created blk_space %p[%p]\n", block_space, 103235537Sgber block); 104235537Sgber } 105235537Sgber 106235537Sgber if (i == 0) 107235537Sgber return (-1); 108235537Sgber 109235537Sgber return (0); 110235537Sgber} 111235537Sgber 112235537Sgberstatic void 113235537Sgberdestroy_buffers(struct chip_swap *swap) 114235537Sgber{ 115235537Sgber struct block_space *blk_space; 116235537Sgber 117235537Sgber if (swap == NULL) 118235537Sgber return; 119235537Sgber 120235537Sgber blk_space = SLIST_FIRST(&swap->free_bs); 121235537Sgber while (blk_space) { 122235537Sgber SLIST_REMOVE_HEAD(&swap->free_bs, free_link); 123235537Sgber nand_debug(NDBG_SIM,"destroyed blk_space %p[%p]\n", 124235537Sgber blk_space, blk_space->blk_ptr); 125235537Sgber free(blk_space->blk_ptr, M_NANDSIM); 126235537Sgber free(blk_space, M_NANDSIM); 127235537Sgber blk_space = SLIST_FIRST(&swap->free_bs); 128235537Sgber } 129235537Sgber 130235537Sgber blk_space = STAILQ_FIRST(&swap->used_bs); 131235537Sgber while (blk_space) { 132235537Sgber STAILQ_REMOVE_HEAD(&swap->used_bs, used_link); 133235537Sgber nand_debug(NDBG_SIM,"destroyed blk_space %p[%p]\n", 134235537Sgber blk_space, blk_space->blk_ptr); 135235537Sgber free(blk_space->blk_ptr, M_NANDSIM); 136235537Sgber free(blk_space, M_NANDSIM); 137235537Sgber blk_space = STAILQ_FIRST(&swap->used_bs); 138235537Sgber } 139235537Sgber} 140235537Sgber 141235537Sgberstatic int 142235537Sgberswap_file_open(struct chip_swap *swap, const char *swap_file) 143235537Sgber{ 144235537Sgber struct nameidata nd; 145241896Skib int flags, error; 146235537Sgber 147241896Skib NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_SYSSPACE, swap_file, 148235537Sgber curthread); 149235537Sgber 150235537Sgber flags = FWRITE | FREAD | O_NOFOLLOW | O_CREAT | O_TRUNC; 151235537Sgber 152235537Sgber error = vn_open(&nd, &flags, CHIP_SWAP_CMODE, NULL); 153235537Sgber if (error) { 154235537Sgber nand_debug(NDBG_SIM,"Cannot create swap file %s", swap_file); 155235537Sgber NDFREE(&nd, NDF_ONLY_PNBUF); 156235537Sgber return (error); 157235537Sgber } 158235537Sgber 159235537Sgber swap->swap_cred = crhold(curthread->td_ucred); 160235537Sgber NDFREE(&nd, NDF_ONLY_PNBUF); 161235537Sgber 162235537Sgber /* We just unlock so we hold a reference */ 163235537Sgber VOP_UNLOCK(nd.ni_vp, 0); 164235537Sgber 165235537Sgber swap->swap_vp = nd.ni_vp; 166235537Sgber 167235537Sgber return (0); 168235537Sgber} 169235537Sgber 170235537Sgberstatic void 171235537Sgberswap_file_close(struct chip_swap *swap) 172235537Sgber{ 173235537Sgber 174235537Sgber if (swap == NULL) 175235537Sgber return; 176235537Sgber 177235537Sgber if (swap->swap_vp == NULL) 178235537Sgber return; 179235537Sgber 180235537Sgber vn_close(swap->swap_vp, FWRITE, swap->swap_cred, curthread); 181235537Sgber crfree(swap->swap_cred); 182235537Sgber} 183235537Sgber 184235537Sgberstatic int 185235537Sgberswap_file_write(struct chip_swap *swap, struct block_state *blk_state) 186235537Sgber{ 187235537Sgber struct block_space *blk_space; 188235537Sgber struct thread *td; 189235537Sgber struct mount *mp; 190235537Sgber struct vnode *vp; 191235537Sgber struct uio auio; 192235537Sgber struct iovec aiov; 193235537Sgber 194235537Sgber if (swap == NULL || blk_state == NULL) 195235537Sgber return (-1); 196235537Sgber 197235537Sgber blk_space = blk_state->blk_sp; 198235537Sgber if (blk_state->offset == -1) { 199235537Sgber blk_state->offset = swap->swap_offset; 200235537Sgber swap->swap_offset += swap->blk_size; 201235537Sgber } 202235537Sgber 203235537Sgber nand_debug(NDBG_SIM,"saving %p[%p] at %x\n", 204235537Sgber blk_space, blk_space->blk_ptr, blk_state->offset); 205235537Sgber 206235537Sgber bzero(&aiov, sizeof(aiov)); 207235537Sgber bzero(&auio, sizeof(auio)); 208235537Sgber 209235537Sgber aiov.iov_base = blk_space->blk_ptr; 210235537Sgber aiov.iov_len = swap->blk_size; 211235537Sgber td = curthread; 212235537Sgber vp = swap->swap_vp; 213235537Sgber 214235537Sgber auio.uio_iov = &aiov; 215235537Sgber auio.uio_offset = blk_state->offset; 216235537Sgber auio.uio_segflg = UIO_SYSSPACE; 217235537Sgber auio.uio_rw = UIO_WRITE; 218235537Sgber auio.uio_iovcnt = 1; 219235537Sgber auio.uio_resid = swap->blk_size; 220235537Sgber auio.uio_td = td; 221235537Sgber 222235537Sgber vn_start_write(vp, &mp, V_WAIT); 223235537Sgber vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 224235537Sgber VOP_WRITE(vp, &auio, IO_UNIT, swap->swap_cred); 225235537Sgber VOP_UNLOCK(vp, 0); 226235537Sgber vn_finished_write(mp); 227235537Sgber 228235537Sgber return (0); 229235537Sgber} 230235537Sgber 231235537Sgberstatic int 232235537Sgberswap_file_read(struct chip_swap *swap, struct block_state *blk_state) 233235537Sgber{ 234235537Sgber struct block_space *blk_space; 235235537Sgber struct thread *td; 236235537Sgber struct vnode *vp; 237235537Sgber struct uio auio; 238235537Sgber struct iovec aiov; 239235537Sgber 240235537Sgber if (swap == NULL || blk_state == NULL) 241235537Sgber return (-1); 242235537Sgber 243235537Sgber blk_space = blk_state->blk_sp; 244235537Sgber 245235537Sgber nand_debug(NDBG_SIM,"restore %p[%p] at %x\n", 246235537Sgber blk_space, blk_space->blk_ptr, blk_state->offset); 247235537Sgber 248235537Sgber bzero(&aiov, sizeof(aiov)); 249235537Sgber bzero(&auio, sizeof(auio)); 250235537Sgber 251235537Sgber aiov.iov_base = blk_space->blk_ptr; 252235537Sgber aiov.iov_len = swap->blk_size; 253235537Sgber td = curthread; 254235537Sgber vp = swap->swap_vp; 255235537Sgber 256235537Sgber auio.uio_iov = &aiov; 257235537Sgber auio.uio_offset = blk_state->offset; 258235537Sgber auio.uio_segflg = UIO_SYSSPACE; 259235537Sgber auio.uio_rw = UIO_READ; 260235537Sgber auio.uio_iovcnt = 1; 261235537Sgber auio.uio_resid = swap->blk_size; 262235537Sgber auio.uio_td = td; 263235537Sgber 264235537Sgber vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 265235537Sgber VOP_READ(vp, &auio, 0, swap->swap_cred); 266235537Sgber VOP_UNLOCK(vp, 0); 267235537Sgber 268235537Sgber return (0); 269235537Sgber} 270235537Sgber 271235537Sgberstruct chip_swap * 272235537Sgbernandsim_swap_init(const char *swap_file, uint32_t nof_blks, uint32_t blk_size) 273235537Sgber{ 274235537Sgber struct chip_swap *swap; 275235537Sgber int err = 0; 276235537Sgber 277235537Sgber if ((swap_file == NULL) || (nof_blks == 0) || (blk_size == 0)) 278235537Sgber return (NULL); 279235537Sgber 280235537Sgber swap = malloc(sizeof(*swap), M_NANDSIM, M_WAITOK | M_ZERO); 281235537Sgber 282235537Sgber SLIST_INIT(&swap->free_bs); 283235537Sgber STAILQ_INIT(&swap->used_bs); 284235537Sgber swap->blk_size = blk_size; 285235537Sgber swap->nof_blks = nof_blks; 286235537Sgber 287235537Sgber err = init_block_state(swap); 288235537Sgber if (err) { 289235537Sgber nandsim_swap_destroy(swap); 290235537Sgber return (NULL); 291235537Sgber } 292235537Sgber 293235537Sgber err = create_buffers(swap); 294235537Sgber if (err) { 295235537Sgber nandsim_swap_destroy(swap); 296235537Sgber return (NULL); 297235537Sgber } 298235537Sgber 299235537Sgber err = swap_file_open(swap, swap_file); 300235537Sgber if (err) { 301235537Sgber nandsim_swap_destroy(swap); 302235537Sgber return (NULL); 303235537Sgber } 304235537Sgber 305235537Sgber return (swap); 306235537Sgber} 307235537Sgber 308235537Sgbervoid 309235537Sgbernandsim_swap_destroy(struct chip_swap *swap) 310235537Sgber{ 311235537Sgber 312235537Sgber if (swap == NULL) 313235537Sgber return; 314235537Sgber 315235537Sgber destroy_block_state(swap); 316235537Sgber destroy_buffers(swap); 317235537Sgber swap_file_close(swap); 318235537Sgber free(swap, M_NANDSIM); 319235537Sgber} 320235537Sgber 321235537Sgberstruct block_space * 322235537Sgberget_bs(struct chip_swap *swap, uint32_t block, uint8_t writing) 323235537Sgber{ 324235537Sgber struct block_state *blk_state, *old_blk_state = NULL; 325235537Sgber struct block_space *blk_space; 326235537Sgber 327235537Sgber if (swap == NULL || (block >= swap->nof_blks)) 328235537Sgber return (NULL); 329235537Sgber 330235537Sgber blk_state = &swap->blk_state[block]; 331235537Sgber nand_debug(NDBG_SIM,"blk_state %x\n", blk_state->status); 332235537Sgber 333235537Sgber if (blk_state->status & BLOCK_ALLOCATED) { 334235537Sgber blk_space = blk_state->blk_sp; 335235537Sgber } else { 336235537Sgber blk_space = SLIST_FIRST(&swap->free_bs); 337235537Sgber if (blk_space) { 338235537Sgber SLIST_REMOVE_HEAD(&swap->free_bs, free_link); 339235537Sgber STAILQ_INSERT_TAIL(&swap->used_bs, blk_space, 340235537Sgber used_link); 341235537Sgber } else { 342235537Sgber blk_space = STAILQ_FIRST(&swap->used_bs); 343235537Sgber old_blk_state = blk_space->blk_state; 344235537Sgber STAILQ_REMOVE_HEAD(&swap->used_bs, used_link); 345235537Sgber STAILQ_INSERT_TAIL(&swap->used_bs, blk_space, 346235537Sgber used_link); 347235537Sgber if (old_blk_state->status & BLOCK_DIRTY) { 348235537Sgber swap_file_write(swap, old_blk_state); 349235537Sgber old_blk_state->status &= ~BLOCK_DIRTY; 350235537Sgber old_blk_state->status |= BLOCK_SWAPPED; 351235537Sgber } 352235537Sgber } 353235537Sgber } 354235537Sgber 355235537Sgber if (blk_space == NULL) 356235537Sgber return (NULL); 357235537Sgber 358235537Sgber if (old_blk_state != NULL) { 359235537Sgber old_blk_state->status &= ~BLOCK_ALLOCATED; 360235537Sgber old_blk_state->blk_sp = NULL; 361235537Sgber } 362235537Sgber 363235537Sgber blk_state->blk_sp = blk_space; 364235537Sgber blk_space->blk_state = blk_state; 365235537Sgber 366235537Sgber if (!(blk_state->status & BLOCK_ALLOCATED)) { 367235537Sgber if (blk_state->status & BLOCK_SWAPPED) 368235537Sgber swap_file_read(swap, blk_state); 369235537Sgber else 370235537Sgber memset(blk_space->blk_ptr, 0xff, swap->blk_size); 371235537Sgber blk_state->status |= BLOCK_ALLOCATED; 372235537Sgber } 373235537Sgber 374235537Sgber if (writing) 375235537Sgber blk_state->status |= BLOCK_DIRTY; 376235537Sgber 377235537Sgber nand_debug(NDBG_SIM,"get_bs returned %p[%p] state %x\n", blk_space, 378235537Sgber blk_space->blk_ptr, blk_state->status); 379235537Sgber 380235537Sgber return (blk_space); 381235537Sgber} 382