1229997Sken/*- 2229997Sken * Copyright (c) 2003, 2008 Silicon Graphics International Corp. 3232604Strasz * Copyright (c) 2012 The FreeBSD Foundation 4313371Smav * Copyright (c) 2014-2017 Alexander Motin <mav@FreeBSD.org> 5229997Sken * All rights reserved. 6229997Sken * 7232604Strasz * Portions of this software were developed by Edward Tomasz Napierala 8232604Strasz * under sponsorship from the FreeBSD Foundation. 9232604Strasz * 10229997Sken * Redistribution and use in source and binary forms, with or without 11229997Sken * modification, are permitted provided that the following conditions 12229997Sken * are met: 13229997Sken * 1. Redistributions of source code must retain the above copyright 14229997Sken * notice, this list of conditions, and the following disclaimer, 15229997Sken * without modification. 16229997Sken * 2. Redistributions in binary form must reproduce at minimum a disclaimer 17229997Sken * substantially similar to the "NO WARRANTY" disclaimer below 18229997Sken * ("Disclaimer") and any redistribution must be conditioned upon 19229997Sken * including a substantially similar Disclaimer requirement for further 20229997Sken * binary redistribution. 21229997Sken * 22229997Sken * NO WARRANTY 23229997Sken * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24229997Sken * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25229997Sken * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR 26229997Sken * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 27229997Sken * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28229997Sken * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29229997Sken * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30229997Sken * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 31229997Sken * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 32229997Sken * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 33229997Sken * POSSIBILITY OF SUCH DAMAGES. 34229997Sken * 35229997Sken * $Id: //depot/users/kenm/FreeBSD-test2/sys/cam/ctl/ctl_backend_ramdisk.c#3 $ 36229997Sken */ 37229997Sken/* 38313371Smav * CAM Target Layer black hole and RAM disk backend. 39229997Sken * 40229997Sken * Author: Ken Merry <ken@FreeBSD.org> 41229997Sken */ 42229997Sken 43229997Sken#include <sys/cdefs.h> 44229997Sken__FBSDID("$FreeBSD: stable/10/sys/cam/ctl/ctl_backend_ramdisk.c 314026 2017-02-21 05:13:16Z mav $"); 45229997Sken 46229997Sken#include <sys/param.h> 47229997Sken#include <sys/systm.h> 48229997Sken#include <sys/kernel.h> 49229997Sken#include <sys/condvar.h> 50229997Sken#include <sys/types.h> 51313371Smav#include <sys/limits.h> 52229997Sken#include <sys/lock.h> 53229997Sken#include <sys/mutex.h> 54229997Sken#include <sys/malloc.h> 55313371Smav#include <sys/sx.h> 56265642Smav#include <sys/taskqueue.h> 57229997Sken#include <sys/time.h> 58229997Sken#include <sys/queue.h> 59229997Sken#include <sys/conf.h> 60229997Sken#include <sys/ioccom.h> 61229997Sken#include <sys/module.h> 62288732Smav#include <sys/sysctl.h> 63229997Sken 64229997Sken#include <cam/scsi/scsi_all.h> 65288732Smav#include <cam/scsi/scsi_da.h> 66229997Sken#include <cam/ctl/ctl_io.h> 67229997Sken#include <cam/ctl/ctl.h> 68229997Sken#include <cam/ctl/ctl_util.h> 69229997Sken#include <cam/ctl/ctl_backend.h> 70229997Sken#include <cam/ctl/ctl_debug.h> 71229997Sken#include <cam/ctl/ctl_ioctl.h> 72288732Smav#include <cam/ctl/ctl_ha.h> 73288732Smav#include <cam/ctl/ctl_private.h> 74229997Sken#include <cam/ctl/ctl_error.h> 75229997Sken 76313371Smav#define PRIV(io) \ 77313371Smav ((struct ctl_ptr_len_flags *)&(io)->io_hdr.ctl_private[CTL_PRIV_BACKEND]) 78313371Smav#define ARGS(io) \ 79313371Smav ((struct ctl_lba_len_flags *)&(io)->io_hdr.ctl_private[CTL_PRIV_LBA_LEN]) 80313371Smav 81313371Smav#define PPP (PAGE_SIZE / sizeof(uint8_t **)) 82313371Smav#ifdef __LP64__ 83313371Smav#define PPPS (PAGE_SHIFT - 3) 84313371Smav#else 85313371Smav#define PPPS (PAGE_SHIFT - 2) 86313371Smav#endif 87313371Smav#define SGPP (PAGE_SIZE / sizeof(struct ctl_sg_entry)) 88313371Smav 89313371Smav#define P_UNMAPPED NULL /* Page is unmapped. */ 90313371Smav#define P_ANCHORED ((void *)(uintptr_t)1) /* Page is anchored. */ 91313371Smav 92229997Skentypedef enum { 93313371Smav GP_READ, /* Return data page or zero page. */ 94313371Smav GP_WRITE, /* Return data page, try allocate if none. */ 95313371Smav GP_ANCHOR, /* Return data page, try anchor if none. */ 96313371Smav GP_OTHER, /* Return what present, do not allocate/anchor. */ 97313371Smav} getpage_op_t; 98313371Smav 99313371Smavtypedef enum { 100229997Sken CTL_BE_RAMDISK_LUN_UNCONFIGURED = 0x01, 101229997Sken CTL_BE_RAMDISK_LUN_CONFIG_ERR = 0x02, 102229997Sken CTL_BE_RAMDISK_LUN_WAITING = 0x04 103229997Sken} ctl_be_ramdisk_lun_flags; 104229997Sken 105229997Skenstruct ctl_be_ramdisk_lun { 106288728Smav struct ctl_lun_create_params params; 107313371Smav char lunname[32]; 108313371Smav int indir; 109313371Smav uint8_t **pages; 110313371Smav uint8_t *zero_page; 111313371Smav struct sx page_lock; 112313371Smav u_int pblocksize; 113313371Smav u_int pblockmul; 114313371Smav uint64_t size_bytes; 115313371Smav uint64_t size_blocks; 116313371Smav uint64_t cap_bytes; 117313371Smav uint64_t cap_used; 118229997Sken struct ctl_be_ramdisk_softc *softc; 119229997Sken ctl_be_ramdisk_lun_flags flags; 120229997Sken STAILQ_ENTRY(ctl_be_ramdisk_lun) links; 121313371Smav struct ctl_be_lun cbe_lun; 122313371Smav struct taskqueue *io_taskqueue; 123313371Smav struct task io_task; 124265642Smav STAILQ_HEAD(, ctl_io_hdr) cont_queue; 125313371Smav struct mtx_padalign queue_lock; 126229997Sken}; 127229997Sken 128229997Skenstruct ctl_be_ramdisk_softc { 129229997Sken struct mtx lock; 130229997Sken int num_luns; 131229997Sken STAILQ_HEAD(, ctl_be_ramdisk_lun) lun_list; 132229997Sken}; 133229997Sken 134229997Skenstatic struct ctl_be_ramdisk_softc rd_softc; 135288732Smavextern struct ctl_softc *control_softc; 136229997Sken 137313369Smavstatic int ctl_backend_ramdisk_init(void); 138313369Smavstatic int ctl_backend_ramdisk_shutdown(void); 139229997Skenstatic int ctl_backend_ramdisk_move_done(union ctl_io *io); 140313371Smavstatic void ctl_backend_ramdisk_compare(union ctl_io *io); 141313371Smavstatic void ctl_backend_ramdisk_rw(union ctl_io *io); 142229997Skenstatic int ctl_backend_ramdisk_submit(union ctl_io *io); 143313371Smavstatic void ctl_backend_ramdisk_worker(void *context, int pending); 144313371Smavstatic int ctl_backend_ramdisk_config_read(union ctl_io *io); 145313371Smavstatic int ctl_backend_ramdisk_config_write(union ctl_io *io); 146313371Smavstatic uint64_t ctl_backend_ramdisk_lun_attr(void *be_lun, const char *attrname); 147229997Skenstatic int ctl_backend_ramdisk_ioctl(struct cdev *dev, u_long cmd, 148229997Sken caddr_t addr, int flag, struct thread *td); 149229997Skenstatic int ctl_backend_ramdisk_rm(struct ctl_be_ramdisk_softc *softc, 150229997Sken struct ctl_lun_req *req); 151229997Skenstatic int ctl_backend_ramdisk_create(struct ctl_be_ramdisk_softc *softc, 152288727Smav struct ctl_lun_req *req); 153232604Straszstatic int ctl_backend_ramdisk_modify(struct ctl_be_ramdisk_softc *softc, 154232604Strasz struct ctl_lun_req *req); 155229997Skenstatic void ctl_backend_ramdisk_lun_shutdown(void *be_lun); 156229997Skenstatic void ctl_backend_ramdisk_lun_config_status(void *be_lun, 157229997Sken ctl_lun_config_status status); 158229997Sken 159229997Skenstatic struct ctl_backend_driver ctl_be_ramdisk_driver = 160229997Sken{ 161230334Sken .name = "ramdisk", 162230334Sken .flags = CTL_BE_FLAG_HAS_CONFIG, 163230334Sken .init = ctl_backend_ramdisk_init, 164313369Smav .shutdown = ctl_backend_ramdisk_shutdown, 165230334Sken .data_submit = ctl_backend_ramdisk_submit, 166230334Sken .data_move_done = ctl_backend_ramdisk_move_done, 167230334Sken .config_read = ctl_backend_ramdisk_config_read, 168230334Sken .config_write = ctl_backend_ramdisk_config_write, 169313371Smav .ioctl = ctl_backend_ramdisk_ioctl, 170313371Smav .lun_attr = ctl_backend_ramdisk_lun_attr, 171229997Sken}; 172229997Sken 173229997SkenMALLOC_DEFINE(M_RAMDISK, "ramdisk", "Memory used for CTL RAMdisk"); 174229997SkenCTL_BACKEND_DECLARE(cbr, ctl_be_ramdisk_driver); 175229997Sken 176313371Smavstatic int 177229997Skenctl_backend_ramdisk_init(void) 178229997Sken{ 179288799Smav struct ctl_be_ramdisk_softc *softc = &rd_softc; 180229997Sken 181229997Sken memset(softc, 0, sizeof(*softc)); 182268549Smav mtx_init(&softc->lock, "ctlramdisk", NULL, MTX_DEF); 183229997Sken STAILQ_INIT(&softc->lun_list); 184229997Sken return (0); 185229997Sken} 186229997Sken 187313369Smavstatic int 188229997Skenctl_backend_ramdisk_shutdown(void) 189229997Sken{ 190288799Smav struct ctl_be_ramdisk_softc *softc = &rd_softc; 191229997Sken struct ctl_be_ramdisk_lun *lun, *next_lun; 192229997Sken 193229997Sken mtx_lock(&softc->lock); 194288818Smav STAILQ_FOREACH_SAFE(lun, &softc->lun_list, links, next_lun) { 195229997Sken /* 196229997Sken * Drop our lock here. Since ctl_invalidate_lun() can call 197229997Sken * back into us, this could potentially lead to a recursive 198229997Sken * lock of the same mutex, which would cause a hang. 199229997Sken */ 200229997Sken mtx_unlock(&softc->lock); 201288727Smav ctl_disable_lun(&lun->cbe_lun); 202288727Smav ctl_invalidate_lun(&lun->cbe_lun); 203229997Sken mtx_lock(&softc->lock); 204229997Sken } 205229997Sken mtx_unlock(&softc->lock); 206313369Smav mtx_destroy(&softc->lock); 207313369Smav return (0); 208229997Sken} 209229997Sken 210313371Smavstatic uint8_t * 211313371Smavctl_backend_ramdisk_getpage(struct ctl_be_ramdisk_lun *be_lun, off_t pn, 212313371Smav getpage_op_t op) 213313371Smav{ 214313371Smav uint8_t **p, ***pp; 215313371Smav off_t i; 216313371Smav int s; 217313371Smav 218313371Smav if (be_lun->cap_bytes == 0) { 219313371Smav switch (op) { 220313371Smav case GP_READ: 221313371Smav return (be_lun->zero_page); 222313371Smav case GP_WRITE: 223313371Smav return ((uint8_t *)be_lun->pages); 224313371Smav case GP_ANCHOR: 225313371Smav return (P_ANCHORED); 226313371Smav default: 227313371Smav return (P_UNMAPPED); 228313371Smav } 229313371Smav } 230313371Smav if (op == GP_WRITE || op == GP_ANCHOR) { 231313371Smav sx_xlock(&be_lun->page_lock); 232313371Smav pp = &be_lun->pages; 233313371Smav for (s = (be_lun->indir - 1) * PPPS; s >= 0; s -= PPPS) { 234313371Smav if (*pp == NULL) { 235313371Smav *pp = malloc(PAGE_SIZE, M_RAMDISK, 236313371Smav M_WAITOK|M_ZERO); 237313371Smav } 238313371Smav i = pn >> s; 239313371Smav pp = (uint8_t ***)&(*pp)[i]; 240313371Smav pn -= i << s; 241313371Smav } 242313371Smav if (*pp == P_UNMAPPED && be_lun->cap_used < be_lun->cap_bytes) { 243313371Smav if (op == GP_WRITE) { 244313371Smav *pp = malloc(be_lun->pblocksize, M_RAMDISK, 245313371Smav M_WAITOK|M_ZERO); 246313371Smav } else 247313371Smav *pp = P_ANCHORED; 248313371Smav be_lun->cap_used += be_lun->pblocksize; 249313371Smav } else if (*pp == P_ANCHORED && op == GP_WRITE) { 250313371Smav *pp = malloc(be_lun->pblocksize, M_RAMDISK, 251313371Smav M_WAITOK|M_ZERO); 252313371Smav } 253313371Smav sx_xunlock(&be_lun->page_lock); 254313371Smav return ((uint8_t *)*pp); 255313371Smav } else { 256313371Smav sx_slock(&be_lun->page_lock); 257313371Smav p = be_lun->pages; 258313371Smav for (s = (be_lun->indir - 1) * PPPS; s >= 0; s -= PPPS) { 259313371Smav if (p == NULL) 260313371Smav break; 261313371Smav i = pn >> s; 262313371Smav p = (uint8_t **)p[i]; 263313371Smav pn -= i << s; 264313371Smav } 265313371Smav sx_sunlock(&be_lun->page_lock); 266313371Smav if ((p == P_UNMAPPED || p == P_ANCHORED) && op == GP_READ) 267313371Smav return (be_lun->zero_page); 268313371Smav return ((uint8_t *)p); 269313371Smav } 270313371Smav}; 271313371Smav 272313371Smavstatic void 273313371Smavctl_backend_ramdisk_unmappage(struct ctl_be_ramdisk_lun *be_lun, off_t pn) 274313371Smav{ 275313371Smav uint8_t ***pp; 276313371Smav off_t i; 277313371Smav int s; 278313371Smav 279313371Smav if (be_lun->cap_bytes == 0) 280313371Smav return; 281313371Smav sx_xlock(&be_lun->page_lock); 282313371Smav pp = &be_lun->pages; 283313371Smav for (s = (be_lun->indir - 1) * PPPS; s >= 0; s -= PPPS) { 284313371Smav if (*pp == NULL) 285313371Smav goto noindir; 286313371Smav i = pn >> s; 287313371Smav pp = (uint8_t ***)&(*pp)[i]; 288313371Smav pn -= i << s; 289313371Smav } 290313371Smav if (*pp == P_ANCHORED) { 291313371Smav be_lun->cap_used -= be_lun->pblocksize; 292313371Smav *pp = P_UNMAPPED; 293313371Smav } else if (*pp != P_UNMAPPED) { 294313371Smav free(*pp, M_RAMDISK); 295313371Smav be_lun->cap_used -= be_lun->pblocksize; 296313371Smav *pp = P_UNMAPPED; 297313371Smav } 298313371Smavnoindir: 299313371Smav sx_xunlock(&be_lun->page_lock); 300313371Smav}; 301313371Smav 302313371Smavstatic void 303313371Smavctl_backend_ramdisk_anchorpage(struct ctl_be_ramdisk_lun *be_lun, off_t pn) 304313371Smav{ 305313371Smav uint8_t ***pp; 306313371Smav off_t i; 307313371Smav int s; 308313371Smav 309313371Smav if (be_lun->cap_bytes == 0) 310313371Smav return; 311313371Smav sx_xlock(&be_lun->page_lock); 312313371Smav pp = &be_lun->pages; 313313371Smav for (s = (be_lun->indir - 1) * PPPS; s >= 0; s -= PPPS) { 314313371Smav if (*pp == NULL) 315313371Smav goto noindir; 316313371Smav i = pn >> s; 317313371Smav pp = (uint8_t ***)&(*pp)[i]; 318313371Smav pn -= i << s; 319313371Smav } 320313371Smav if (*pp == P_UNMAPPED && be_lun->cap_used < be_lun->cap_bytes) { 321313371Smav be_lun->cap_used += be_lun->pblocksize; 322313371Smav *pp = P_ANCHORED; 323313371Smav } else if (*pp != P_ANCHORED) { 324313371Smav free(*pp, M_RAMDISK); 325313371Smav *pp = P_ANCHORED; 326313371Smav } 327313371Smavnoindir: 328313371Smav sx_xunlock(&be_lun->page_lock); 329313371Smav}; 330313371Smav 331313371Smavstatic void 332313371Smavctl_backend_ramdisk_freeallpages(uint8_t **p, int indir) 333313371Smav{ 334313371Smav int i; 335313371Smav 336313371Smav if (p == NULL) 337313371Smav return; 338313371Smav if (indir == 0) { 339313371Smav free(p, M_RAMDISK); 340313371Smav return; 341313371Smav } 342313371Smav for (i = 0; i < PPP; i++) { 343313371Smav if (p[i] == NULL) 344313371Smav continue; 345313371Smav ctl_backend_ramdisk_freeallpages((uint8_t **)p[i], indir - 1); 346313371Smav } 347313371Smav free(p, M_RAMDISK); 348313371Smav}; 349313371Smav 350313371Smavstatic size_t 351313371Smavcmp(uint8_t *a, uint8_t *b, size_t size) 352313371Smav{ 353313371Smav size_t i; 354313371Smav 355313371Smav for (i = 0; i < size; i++) { 356313371Smav if (a[i] != b[i]) 357313371Smav break; 358313371Smav } 359313371Smav return (i); 360313371Smav} 361313371Smav 362229997Skenstatic int 363313371Smavctl_backend_ramdisk_cmp(union ctl_io *io) 364313371Smav{ 365313371Smav struct ctl_be_lun *cbe_lun = CTL_BACKEND_LUN(io); 366313371Smav struct ctl_be_ramdisk_lun *be_lun = cbe_lun->be_lun; 367313371Smav uint8_t *page; 368313371Smav uint8_t info[8]; 369313371Smav uint64_t lba; 370313371Smav u_int lbaoff, lbas, res, off; 371313371Smav 372313371Smav lbas = io->scsiio.kern_data_len / cbe_lun->blocksize; 373313371Smav lba = ARGS(io)->lba + PRIV(io)->len - lbas; 374313371Smav off = 0; 375313371Smav for (; lbas > 0; lbas--, lba++) { 376313371Smav page = ctl_backend_ramdisk_getpage(be_lun, 377313371Smav lba >> cbe_lun->pblockexp, GP_READ); 378313371Smav lbaoff = lba & ~(UINT_MAX << cbe_lun->pblockexp); 379313371Smav page += lbaoff * cbe_lun->blocksize; 380313371Smav res = cmp(io->scsiio.kern_data_ptr + off, page, 381313371Smav cbe_lun->blocksize); 382313371Smav off += res; 383313371Smav if (res < cbe_lun->blocksize) 384313371Smav break; 385313371Smav } 386313371Smav if (lbas > 0) { 387313371Smav off += io->scsiio.kern_rel_offset - io->scsiio.kern_data_len; 388313371Smav scsi_u64to8b(off, info); 389313371Smav ctl_set_sense(&io->scsiio, /*current_error*/ 1, 390313371Smav /*sense_key*/ SSD_KEY_MISCOMPARE, 391313371Smav /*asc*/ 0x1D, /*ascq*/ 0x00, 392313371Smav /*type*/ SSD_ELEM_INFO, 393313371Smav /*size*/ sizeof(info), /*data*/ &info, 394313371Smav /*type*/ SSD_ELEM_NONE); 395313371Smav return (1); 396313371Smav } 397313371Smav return (0); 398313371Smav} 399313371Smav 400313371Smavstatic int 401229997Skenctl_backend_ramdisk_move_done(union ctl_io *io) 402229997Sken{ 403313371Smav struct ctl_be_lun *cbe_lun = CTL_BACKEND_LUN(io); 404313371Smav struct ctl_be_ramdisk_lun *be_lun = cbe_lun->be_lun; 405229997Sken#ifdef CTL_TIME_IO 406229997Sken struct bintime cur_bt; 407229997Sken#endif 408229997Sken 409229997Sken CTL_DEBUG_PRINT(("ctl_backend_ramdisk_move_done\n")); 410265642Smav#ifdef CTL_TIME_IO 411288798Smav getbinuptime(&cur_bt); 412265642Smav bintime_sub(&cur_bt, &io->io_hdr.dma_start_bt); 413265642Smav bintime_add(&io->io_hdr.dma_bt, &cur_bt); 414288798Smav#endif 415265642Smav io->io_hdr.num_dmas++; 416265642Smav if (io->scsiio.kern_sg_entries > 0) 417265642Smav free(io->scsiio.kern_data_ptr, M_RAMDISK); 418265642Smav io->scsiio.kern_rel_offset += io->scsiio.kern_data_len; 419275881Smav if (io->io_hdr.flags & CTL_FLAG_ABORT) { 420275881Smav ; 421313365Smav } else if (io->io_hdr.port_status != 0 && 422313365Smav ((io->io_hdr.status & CTL_STATUS_MASK) == CTL_STATUS_NONE || 423313365Smav (io->io_hdr.status & CTL_STATUS_MASK) == CTL_SUCCESS)) { 424313365Smav ctl_set_internal_failure(&io->scsiio, /*sks_valid*/ 1, 425313365Smav /*retry_count*/ io->io_hdr.port_status); 426313365Smav } else if (io->scsiio.kern_data_resid != 0 && 427313365Smav (io->io_hdr.flags & CTL_FLAG_DATA_MASK) == CTL_FLAG_DATA_OUT && 428313365Smav ((io->io_hdr.status & CTL_STATUS_MASK) == CTL_STATUS_NONE || 429313365Smav (io->io_hdr.status & CTL_STATUS_MASK) == CTL_SUCCESS)) { 430313365Smav ctl_set_invalid_field_ciu(&io->scsiio); 431275881Smav } else if ((io->io_hdr.port_status == 0) && 432275881Smav ((io->io_hdr.status & CTL_STATUS_MASK) == CTL_STATUS_NONE)) { 433313371Smav if (ARGS(io)->flags & CTL_LLF_COMPARE) { 434313371Smav /* We have data block ready for comparison. */ 435313371Smav if (ctl_backend_ramdisk_cmp(io)) 436313371Smav goto done; 437313371Smav } 438313371Smav if (ARGS(io)->len > PRIV(io)->len) { 439268549Smav mtx_lock(&be_lun->queue_lock); 440265642Smav STAILQ_INSERT_TAIL(&be_lun->cont_queue, 441265642Smav &io->io_hdr, links); 442268549Smav mtx_unlock(&be_lun->queue_lock); 443265642Smav taskqueue_enqueue(be_lun->io_taskqueue, 444265642Smav &be_lun->io_task); 445265642Smav return (0); 446265642Smav } 447275879Smav ctl_set_success(&io->scsiio); 448229997Sken } 449313371Smavdone: 450268151Smav ctl_data_submit_done(io); 451229997Sken return(0); 452229997Sken} 453229997Sken 454313371Smavstatic void 455313371Smavctl_backend_ramdisk_compare(union ctl_io *io) 456229997Sken{ 457313371Smav struct ctl_be_lun *cbe_lun = CTL_BACKEND_LUN(io); 458313371Smav u_int lbas, len; 459229997Sken 460313371Smav lbas = ARGS(io)->len - PRIV(io)->len; 461313371Smav lbas = MIN(lbas, 131072 / cbe_lun->blocksize); 462313371Smav len = lbas * cbe_lun->blocksize; 463313371Smav 464313371Smav io->scsiio.be_move_done = ctl_backend_ramdisk_move_done; 465313371Smav io->scsiio.kern_data_ptr = malloc(len, M_RAMDISK, M_WAITOK); 466313371Smav io->scsiio.kern_data_len = len; 467313371Smav io->scsiio.kern_sg_entries = 0; 468313371Smav io->io_hdr.flags |= CTL_FLAG_ALLOCATED; 469313371Smav PRIV(io)->len += lbas; 470313371Smav#ifdef CTL_TIME_IO 471313371Smav getbinuptime(&io->io_hdr.dma_start_bt); 472313371Smav#endif 473313371Smav ctl_datamove(io); 474265642Smav} 475229997Sken 476265642Smavstatic void 477313371Smavctl_backend_ramdisk_rw(union ctl_io *io) 478265642Smav{ 479313371Smav struct ctl_be_lun *cbe_lun = CTL_BACKEND_LUN(io); 480313371Smav struct ctl_be_ramdisk_lun *be_lun = cbe_lun->be_lun; 481265642Smav struct ctl_sg_entry *sg_entries; 482313371Smav uint8_t *page; 483313371Smav uint64_t lba; 484313371Smav u_int i, len, lbaoff, lbas, sgs, off; 485313371Smav getpage_op_t op; 486229997Sken 487313371Smav lba = ARGS(io)->lba + PRIV(io)->len; 488313371Smav lbaoff = lba & ~(UINT_MAX << cbe_lun->pblockexp); 489313371Smav lbas = ARGS(io)->len - PRIV(io)->len; 490313371Smav lbas = MIN(lbas, (SGPP << cbe_lun->pblockexp) - lbaoff); 491313371Smav sgs = (lbas + lbaoff + be_lun->pblockmul - 1) >> cbe_lun->pblockexp; 492313371Smav off = lbaoff * cbe_lun->blocksize; 493313371Smav op = (ARGS(io)->flags & CTL_LLF_WRITE) ? GP_WRITE : GP_READ; 494313371Smav if (sgs > 1) { 495229997Sken io->scsiio.kern_data_ptr = malloc(sizeof(struct ctl_sg_entry) * 496313371Smav sgs, M_RAMDISK, M_WAITOK); 497229997Sken sg_entries = (struct ctl_sg_entry *)io->scsiio.kern_data_ptr; 498313371Smav len = lbas * cbe_lun->blocksize; 499313371Smav for (i = 0; i < sgs; i++) { 500313371Smav page = ctl_backend_ramdisk_getpage(be_lun, 501313371Smav (lba >> cbe_lun->pblockexp) + i, op); 502313371Smav if (page == P_UNMAPPED || page == P_ANCHORED) { 503313371Smav free(io->scsiio.kern_data_ptr, M_RAMDISK); 504313371Smavnospc: 505313371Smav ctl_set_space_alloc_fail(&io->scsiio); 506313371Smav ctl_data_submit_done(io); 507313371Smav return; 508313371Smav } 509313371Smav sg_entries[i].addr = page + off; 510313371Smav sg_entries[i].len = MIN(len, be_lun->pblocksize - off); 511313371Smav len -= sg_entries[i].len; 512313371Smav off = 0; 513229997Sken } 514229997Sken } else { 515313371Smav page = ctl_backend_ramdisk_getpage(be_lun, 516313371Smav lba >> cbe_lun->pblockexp, op); 517313371Smav if (page == P_UNMAPPED || page == P_ANCHORED) 518313371Smav goto nospc; 519313371Smav sgs = 0; 520313371Smav io->scsiio.kern_data_ptr = page + off; 521265642Smav } 522229997Sken 523268148Smav io->scsiio.be_move_done = ctl_backend_ramdisk_move_done; 524313371Smav io->scsiio.kern_data_len = lbas * cbe_lun->blocksize; 525313371Smav io->scsiio.kern_sg_entries = sgs; 526265642Smav io->io_hdr.flags |= CTL_FLAG_ALLOCATED; 527313371Smav PRIV(io)->len += lbas; 528229997Sken#ifdef CTL_TIME_IO 529288798Smav getbinuptime(&io->io_hdr.dma_start_bt); 530229997Sken#endif 531229997Sken ctl_datamove(io); 532265642Smav} 533229997Sken 534313371Smavstatic int 535313371Smavctl_backend_ramdisk_submit(union ctl_io *io) 536313371Smav{ 537313371Smav struct ctl_lba_len_flags *lbalen = ARGS(io); 538313371Smav 539313371Smav if (lbalen->flags & CTL_LLF_VERIFY) { 540313371Smav ctl_set_success(&io->scsiio); 541313371Smav ctl_data_submit_done(io); 542313371Smav return (CTL_RETVAL_COMPLETE); 543313371Smav } 544313371Smav PRIV(io)->len = 0; 545313371Smav if (lbalen->flags & CTL_LLF_COMPARE) 546313371Smav ctl_backend_ramdisk_compare(io); 547313371Smav else 548313371Smav ctl_backend_ramdisk_rw(io); 549313371Smav return (CTL_RETVAL_COMPLETE); 550313371Smav} 551313371Smav 552265642Smavstatic void 553265642Smavctl_backend_ramdisk_worker(void *context, int pending) 554265642Smav{ 555265642Smav struct ctl_be_ramdisk_lun *be_lun; 556265642Smav union ctl_io *io; 557265642Smav 558265642Smav be_lun = (struct ctl_be_ramdisk_lun *)context; 559268549Smav mtx_lock(&be_lun->queue_lock); 560265642Smav for (;;) { 561265642Smav io = (union ctl_io *)STAILQ_FIRST(&be_lun->cont_queue); 562265642Smav if (io != NULL) { 563265642Smav STAILQ_REMOVE(&be_lun->cont_queue, &io->io_hdr, 564265642Smav ctl_io_hdr, links); 565268549Smav mtx_unlock(&be_lun->queue_lock); 566313371Smav if (ARGS(io)->flags & CTL_LLF_COMPARE) 567313371Smav ctl_backend_ramdisk_compare(io); 568313371Smav else 569313371Smav ctl_backend_ramdisk_rw(io); 570268549Smav mtx_lock(&be_lun->queue_lock); 571265642Smav continue; 572265642Smav } 573265642Smav 574265642Smav /* 575265642Smav * If we get here, there is no work left in the queues, so 576265642Smav * just break out and let the task queue go to sleep. 577265642Smav */ 578265642Smav break; 579265642Smav } 580268549Smav mtx_unlock(&be_lun->queue_lock); 581229997Sken} 582229997Sken 583229997Skenstatic int 584313371Smavctl_backend_ramdisk_gls(union ctl_io *io) 585313371Smav{ 586313371Smav struct ctl_be_lun *cbe_lun = CTL_BACKEND_LUN(io); 587313371Smav struct ctl_be_ramdisk_lun *be_lun = cbe_lun->be_lun; 588313371Smav struct scsi_get_lba_status_data *data; 589313371Smav uint8_t *page; 590313371Smav u_int lbaoff; 591313371Smav 592313371Smav data = (struct scsi_get_lba_status_data *)io->scsiio.kern_data_ptr; 593313371Smav scsi_u64to8b(ARGS(io)->lba, data->descr[0].addr); 594313371Smav lbaoff = ARGS(io)->lba & ~(UINT_MAX << cbe_lun->pblockexp); 595313371Smav scsi_ulto4b(be_lun->pblockmul - lbaoff, data->descr[0].length); 596313371Smav page = ctl_backend_ramdisk_getpage(be_lun, 597313371Smav ARGS(io)->lba >> cbe_lun->pblockexp, GP_OTHER); 598313371Smav if (page == P_UNMAPPED) 599313371Smav data->descr[0].status = 1; 600313371Smav else if (page == P_ANCHORED) 601313371Smav data->descr[0].status = 2; 602313371Smav else 603313371Smav data->descr[0].status = 0; 604313371Smav ctl_config_read_done(io); 605313371Smav return (CTL_RETVAL_COMPLETE); 606313371Smav} 607313371Smav 608313371Smavstatic int 609313371Smavctl_backend_ramdisk_config_read(union ctl_io *io) 610313371Smav{ 611313371Smav int retval = 0; 612313371Smav 613313371Smav switch (io->scsiio.cdb[0]) { 614313371Smav case SERVICE_ACTION_IN: 615313371Smav if (io->scsiio.cdb[1] == SGLS_SERVICE_ACTION) { 616313371Smav retval = ctl_backend_ramdisk_gls(io); 617313371Smav break; 618313371Smav } 619313371Smav ctl_set_invalid_field(&io->scsiio, 620313371Smav /*sks_valid*/ 1, 621313371Smav /*command*/ 1, 622313371Smav /*field*/ 1, 623313371Smav /*bit_valid*/ 1, 624313371Smav /*bit*/ 4); 625313371Smav ctl_config_read_done(io); 626313371Smav retval = CTL_RETVAL_COMPLETE; 627313371Smav break; 628313371Smav default: 629313371Smav ctl_set_invalid_opcode(&io->scsiio); 630313371Smav ctl_config_read_done(io); 631313371Smav retval = CTL_RETVAL_COMPLETE; 632313371Smav break; 633313371Smav } 634313371Smav return (retval); 635313371Smav} 636313371Smav 637313371Smavstatic void 638313371Smavctl_backend_ramdisk_delete(struct ctl_be_lun *cbe_lun, off_t lba, off_t len, 639313371Smav int anchor) 640313371Smav{ 641313371Smav struct ctl_be_ramdisk_lun *be_lun = cbe_lun->be_lun; 642313371Smav uint8_t *page; 643313371Smav uint64_t p, lp; 644313371Smav u_int lbaoff; 645313371Smav getpage_op_t op = anchor ? GP_ANCHOR : GP_OTHER; 646313371Smav 647313371Smav /* Partially zero first partial page. */ 648313371Smav p = lba >> cbe_lun->pblockexp; 649313371Smav lbaoff = lba & ~(UINT_MAX << cbe_lun->pblockexp); 650313371Smav if (lbaoff != 0) { 651313371Smav page = ctl_backend_ramdisk_getpage(be_lun, p, op); 652313371Smav if (page != P_UNMAPPED && page != P_ANCHORED) { 653313371Smav memset(page + lbaoff * cbe_lun->blocksize, 0, 654313371Smav min(len, be_lun->pblockmul - lbaoff) * 655313371Smav cbe_lun->blocksize); 656313371Smav } 657313371Smav p++; 658313371Smav } 659313371Smav 660313371Smav /* Partially zero last partial page. */ 661313371Smav lp = (lba + len) >> cbe_lun->pblockexp; 662313371Smav lbaoff = (lba + len) & ~(UINT_MAX << cbe_lun->pblockexp); 663313371Smav if (p <= lp && lbaoff != 0) { 664313371Smav page = ctl_backend_ramdisk_getpage(be_lun, lp, op); 665313371Smav if (page != P_UNMAPPED && page != P_ANCHORED) 666313371Smav memset(page, 0, lbaoff * cbe_lun->blocksize); 667313371Smav } 668313371Smav 669313371Smav /* Delete remaining full pages. */ 670313371Smav if (anchor) { 671313371Smav for (; p < lp; p++) 672313371Smav ctl_backend_ramdisk_anchorpage(be_lun, p); 673313371Smav } else { 674313371Smav for (; p < lp; p++) 675313371Smav ctl_backend_ramdisk_unmappage(be_lun, p); 676313371Smav } 677313371Smav} 678313371Smav 679313371Smavstatic void 680313371Smavctl_backend_ramdisk_ws(union ctl_io *io) 681313371Smav{ 682313371Smav struct ctl_be_lun *cbe_lun = CTL_BACKEND_LUN(io); 683313371Smav struct ctl_be_ramdisk_lun *be_lun = cbe_lun->be_lun; 684313371Smav struct ctl_lba_len_flags *lbalen = ARGS(io); 685313371Smav uint8_t *page; 686313371Smav uint64_t lba; 687313371Smav u_int lbaoff, lbas; 688313371Smav 689313371Smav if (lbalen->flags & ~(SWS_LBDATA | SWS_UNMAP | SWS_ANCHOR | SWS_NDOB)) { 690313371Smav ctl_set_invalid_field(&io->scsiio, 691313371Smav /*sks_valid*/ 1, 692313371Smav /*command*/ 1, 693313371Smav /*field*/ 1, 694313371Smav /*bit_valid*/ 0, 695313371Smav /*bit*/ 0); 696313371Smav ctl_config_write_done(io); 697313371Smav return; 698313371Smav } 699313371Smav if (lbalen->flags & SWS_UNMAP) { 700313371Smav ctl_backend_ramdisk_delete(cbe_lun, lbalen->lba, lbalen->len, 701313371Smav (lbalen->flags & SWS_ANCHOR) != 0); 702313371Smav ctl_set_success(&io->scsiio); 703313371Smav ctl_config_write_done(io); 704313371Smav return; 705313371Smav } 706313371Smav 707313371Smav for (lba = lbalen->lba, lbas = lbalen->len; lbas > 0; lba++, lbas--) { 708313371Smav page = ctl_backend_ramdisk_getpage(be_lun, 709313371Smav lba >> cbe_lun->pblockexp, GP_WRITE); 710313371Smav if (page == P_UNMAPPED || page == P_ANCHORED) { 711313371Smav ctl_set_space_alloc_fail(&io->scsiio); 712313371Smav ctl_data_submit_done(io); 713313371Smav return; 714313371Smav } 715313371Smav lbaoff = lba & ~(UINT_MAX << cbe_lun->pblockexp); 716313371Smav page += lbaoff * cbe_lun->blocksize; 717313371Smav if (lbalen->flags & SWS_NDOB) { 718313371Smav memset(page, 0, cbe_lun->blocksize); 719313371Smav } else { 720313371Smav memcpy(page, io->scsiio.kern_data_ptr, 721313371Smav cbe_lun->blocksize); 722313371Smav } 723313371Smav if (lbalen->flags & SWS_LBDATA) 724313371Smav scsi_ulto4b(lba, page); 725313371Smav } 726313371Smav ctl_set_success(&io->scsiio); 727313371Smav ctl_config_write_done(io); 728313371Smav} 729313371Smav 730313371Smavstatic void 731313371Smavctl_backend_ramdisk_unmap(union ctl_io *io) 732313371Smav{ 733313371Smav struct ctl_be_lun *cbe_lun = CTL_BACKEND_LUN(io); 734313371Smav struct ctl_ptr_len_flags *ptrlen = (struct ctl_ptr_len_flags *)ARGS(io); 735313371Smav struct scsi_unmap_desc *buf, *end; 736313371Smav 737313371Smav if ((ptrlen->flags & ~SU_ANCHOR) != 0) { 738313371Smav ctl_set_invalid_field(&io->scsiio, 739313371Smav /*sks_valid*/ 0, 740313371Smav /*command*/ 0, 741313371Smav /*field*/ 0, 742313371Smav /*bit_valid*/ 0, 743313371Smav /*bit*/ 0); 744313371Smav ctl_config_write_done(io); 745313371Smav return; 746313371Smav } 747313371Smav 748313371Smav buf = (struct scsi_unmap_desc *)ptrlen->ptr; 749313371Smav end = buf + ptrlen->len / sizeof(*buf); 750313371Smav for (; buf < end; buf++) { 751313371Smav ctl_backend_ramdisk_delete(cbe_lun, 752313371Smav scsi_8btou64(buf->lba), scsi_4btoul(buf->length), 753313371Smav (ptrlen->flags & SU_ANCHOR) != 0); 754313371Smav } 755313371Smav 756313371Smav ctl_set_success(&io->scsiio); 757313371Smav ctl_config_write_done(io); 758313371Smav} 759313371Smav 760313371Smavstatic int 761313371Smavctl_backend_ramdisk_config_write(union ctl_io *io) 762313371Smav{ 763313371Smav struct ctl_be_lun *cbe_lun = CTL_BACKEND_LUN(io); 764313371Smav int retval = 0; 765313371Smav 766313371Smav switch (io->scsiio.cdb[0]) { 767313371Smav case SYNCHRONIZE_CACHE: 768313371Smav case SYNCHRONIZE_CACHE_16: 769313371Smav /* We have no cache to flush. */ 770313371Smav ctl_set_success(&io->scsiio); 771313371Smav ctl_config_write_done(io); 772313371Smav break; 773313371Smav case START_STOP_UNIT: { 774313371Smav struct scsi_start_stop_unit *cdb; 775313371Smav 776313371Smav cdb = (struct scsi_start_stop_unit *)io->scsiio.cdb; 777313371Smav if ((cdb->how & SSS_PC_MASK) != 0) { 778313371Smav ctl_set_success(&io->scsiio); 779313371Smav ctl_config_write_done(io); 780313371Smav break; 781313371Smav } 782313371Smav if (cdb->how & SSS_START) { 783313371Smav if (cdb->how & SSS_LOEJ) 784313371Smav ctl_lun_has_media(cbe_lun); 785313371Smav ctl_start_lun(cbe_lun); 786313371Smav } else { 787313371Smav ctl_stop_lun(cbe_lun); 788313371Smav if (cdb->how & SSS_LOEJ) 789313371Smav ctl_lun_ejected(cbe_lun); 790313371Smav } 791313371Smav ctl_set_success(&io->scsiio); 792313371Smav ctl_config_write_done(io); 793313371Smav break; 794313371Smav } 795313371Smav case PREVENT_ALLOW: 796313371Smav ctl_set_success(&io->scsiio); 797313371Smav ctl_config_write_done(io); 798313371Smav break; 799313371Smav case WRITE_SAME_10: 800313371Smav case WRITE_SAME_16: 801313371Smav ctl_backend_ramdisk_ws(io); 802313371Smav break; 803313371Smav case UNMAP: 804313371Smav ctl_backend_ramdisk_unmap(io); 805313371Smav break; 806313371Smav default: 807313371Smav ctl_set_invalid_opcode(&io->scsiio); 808313371Smav ctl_config_write_done(io); 809313371Smav retval = CTL_RETVAL_COMPLETE; 810313371Smav break; 811313371Smav } 812313371Smav 813313371Smav return (retval); 814313371Smav} 815313371Smav 816313371Smavstatic uint64_t 817313371Smavctl_backend_ramdisk_lun_attr(void *arg, const char *attrname) 818313371Smav{ 819313371Smav struct ctl_be_ramdisk_lun *be_lun = arg; 820313371Smav uint64_t val; 821313371Smav 822313371Smav val = UINT64_MAX; 823313371Smav if (be_lun->cap_bytes == 0) 824313371Smav return (val); 825313371Smav sx_slock(&be_lun->page_lock); 826313371Smav if (strcmp(attrname, "blocksused") == 0) { 827313371Smav val = be_lun->cap_used / be_lun->cbe_lun.blocksize; 828313371Smav } else if (strcmp(attrname, "blocksavail") == 0) { 829313371Smav val = (be_lun->cap_bytes - be_lun->cap_used) / 830313371Smav be_lun->cbe_lun.blocksize; 831313371Smav } 832313371Smav sx_sunlock(&be_lun->page_lock); 833313371Smav return (val); 834313371Smav} 835313371Smav 836313371Smavstatic int 837229997Skenctl_backend_ramdisk_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, 838229997Sken int flag, struct thread *td) 839229997Sken{ 840288799Smav struct ctl_be_ramdisk_softc *softc = &rd_softc; 841288799Smav struct ctl_lun_req *lun_req; 842229997Sken int retval; 843229997Sken 844229997Sken retval = 0; 845229997Sken switch (cmd) { 846288799Smav case CTL_LUN_REQ: 847229997Sken lun_req = (struct ctl_lun_req *)addr; 848229997Sken switch (lun_req->reqtype) { 849229997Sken case CTL_LUNREQ_CREATE: 850288727Smav retval = ctl_backend_ramdisk_create(softc, lun_req); 851229997Sken break; 852229997Sken case CTL_LUNREQ_RM: 853229997Sken retval = ctl_backend_ramdisk_rm(softc, lun_req); 854229997Sken break; 855232604Strasz case CTL_LUNREQ_MODIFY: 856232604Strasz retval = ctl_backend_ramdisk_modify(softc, lun_req); 857232604Strasz break; 858229997Sken default: 859229997Sken lun_req->status = CTL_LUN_ERROR; 860229997Sken snprintf(lun_req->error_str, sizeof(lun_req->error_str), 861229997Sken "%s: invalid LUN request type %d", __func__, 862229997Sken lun_req->reqtype); 863229997Sken break; 864229997Sken } 865229997Sken break; 866229997Sken default: 867229997Sken retval = ENOTTY; 868229997Sken break; 869229997Sken } 870229997Sken 871229997Sken return (retval); 872229997Sken} 873229997Sken 874229997Skenstatic int 875229997Skenctl_backend_ramdisk_rm(struct ctl_be_ramdisk_softc *softc, 876229997Sken struct ctl_lun_req *req) 877229997Sken{ 878229997Sken struct ctl_be_ramdisk_lun *be_lun; 879229997Sken struct ctl_lun_rm_params *params; 880229997Sken int retval; 881229997Sken 882229997Sken params = &req->reqdata.rm; 883229997Sken mtx_lock(&softc->lock); 884229997Sken STAILQ_FOREACH(be_lun, &softc->lun_list, links) { 885288727Smav if (be_lun->cbe_lun.lun_id == params->lun_id) 886229997Sken break; 887229997Sken } 888229997Sken mtx_unlock(&softc->lock); 889229997Sken if (be_lun == NULL) { 890229997Sken snprintf(req->error_str, sizeof(req->error_str), 891229997Sken "%s: LUN %u is not managed by the ramdisk backend", 892229997Sken __func__, params->lun_id); 893229997Sken goto bailout_error; 894229997Sken } 895229997Sken 896288727Smav retval = ctl_disable_lun(&be_lun->cbe_lun); 897229997Sken if (retval != 0) { 898229997Sken snprintf(req->error_str, sizeof(req->error_str), 899229997Sken "%s: error %d returned from ctl_disable_lun() for " 900229997Sken "LUN %d", __func__, retval, params->lun_id); 901229997Sken goto bailout_error; 902229997Sken } 903229997Sken 904229997Sken /* 905229997Sken * Set the waiting flag before we invalidate the LUN. Our shutdown 906229997Sken * routine can be called any time after we invalidate the LUN, 907229997Sken * and can be called from our context. 908229997Sken * 909229997Sken * This tells the shutdown routine that we're waiting, or we're 910229997Sken * going to wait for the shutdown to happen. 911229997Sken */ 912229997Sken mtx_lock(&softc->lock); 913229997Sken be_lun->flags |= CTL_BE_RAMDISK_LUN_WAITING; 914229997Sken mtx_unlock(&softc->lock); 915229997Sken 916288727Smav retval = ctl_invalidate_lun(&be_lun->cbe_lun); 917229997Sken if (retval != 0) { 918229997Sken snprintf(req->error_str, sizeof(req->error_str), 919229997Sken "%s: error %d returned from ctl_invalidate_lun() for " 920229997Sken "LUN %d", __func__, retval, params->lun_id); 921252569Smav mtx_lock(&softc->lock); 922252569Smav be_lun->flags &= ~CTL_BE_RAMDISK_LUN_WAITING; 923252569Smav mtx_unlock(&softc->lock); 924229997Sken goto bailout_error; 925229997Sken } 926229997Sken 927229997Sken mtx_lock(&softc->lock); 928229997Sken while ((be_lun->flags & CTL_BE_RAMDISK_LUN_UNCONFIGURED) == 0) { 929229997Sken retval = msleep(be_lun, &softc->lock, PCATCH, "ctlram", 0); 930288799Smav if (retval == EINTR) 931229997Sken break; 932229997Sken } 933229997Sken be_lun->flags &= ~CTL_BE_RAMDISK_LUN_WAITING; 934229997Sken 935229997Sken /* 936229997Sken * We only remove this LUN from the list and free it (below) if 937229997Sken * retval == 0. If the user interrupted the wait, we just bail out 938229997Sken * without actually freeing the LUN. We let the shutdown routine 939229997Sken * free the LUN if that happens. 940229997Sken */ 941229997Sken if (retval == 0) { 942229997Sken STAILQ_REMOVE(&softc->lun_list, be_lun, ctl_be_ramdisk_lun, 943229997Sken links); 944229997Sken softc->num_luns--; 945229997Sken } 946229997Sken 947229997Sken mtx_unlock(&softc->lock); 948229997Sken 949265642Smav if (retval == 0) { 950288734Smav taskqueue_drain_all(be_lun->io_taskqueue); 951265642Smav taskqueue_free(be_lun->io_taskqueue); 952288727Smav ctl_free_opts(&be_lun->cbe_lun.options); 953313371Smav free(be_lun->zero_page, M_RAMDISK); 954313371Smav ctl_backend_ramdisk_freeallpages(be_lun->pages, be_lun->indir); 955313371Smav sx_destroy(&be_lun->page_lock); 956268549Smav mtx_destroy(&be_lun->queue_lock); 957229997Sken free(be_lun, M_RAMDISK); 958265642Smav } 959229997Sken 960229997Sken req->status = CTL_LUN_OK; 961229997Sken return (retval); 962229997Sken 963229997Skenbailout_error: 964229997Sken req->status = CTL_LUN_ERROR; 965229997Sken return (0); 966229997Sken} 967229997Sken 968229997Skenstatic int 969229997Skenctl_backend_ramdisk_create(struct ctl_be_ramdisk_softc *softc, 970288727Smav struct ctl_lun_req *req) 971229997Sken{ 972229997Sken struct ctl_be_ramdisk_lun *be_lun; 973288727Smav struct ctl_be_lun *cbe_lun; 974229997Sken struct ctl_lun_create_params *params; 975268143Smav char *value; 976229997Sken char tmpstr[32]; 977313371Smav uint64_t t; 978288727Smav int retval; 979229997Sken 980229997Sken retval = 0; 981229997Sken params = &req->reqdata.create; 982229997Sken 983288727Smav be_lun = malloc(sizeof(*be_lun), M_RAMDISK, M_ZERO | M_WAITOK); 984288727Smav cbe_lun = &be_lun->cbe_lun; 985288727Smav cbe_lun->be_lun = be_lun; 986288728Smav be_lun->params = req->reqdata.create; 987288727Smav be_lun->softc = softc; 988265642Smav sprintf(be_lun->lunname, "cram%d", softc->num_luns); 989288727Smav ctl_init_opts(&cbe_lun->options, req->num_be_args, req->kern_be_args); 990229997Sken 991229997Sken if (params->flags & CTL_LUN_FLAG_DEV_TYPE) 992288727Smav cbe_lun->lun_type = params->device_type; 993229997Sken else 994288727Smav cbe_lun->lun_type = T_DIRECT; 995288727Smav be_lun->flags = CTL_BE_RAMDISK_LUN_UNCONFIGURED; 996288732Smav cbe_lun->flags = 0; 997288732Smav value = ctl_get_opt(&cbe_lun->options, "ha_role"); 998288732Smav if (value != NULL) { 999288732Smav if (strcmp(value, "primary") == 0) 1000288732Smav cbe_lun->flags |= CTL_LUN_FLAG_PRIMARY; 1001288732Smav } else if (control_softc->flags & CTL_FLAG_ACTIVE_SHELF) 1002288732Smav cbe_lun->flags |= CTL_LUN_FLAG_PRIMARY; 1003229997Sken 1004313371Smav be_lun->pblocksize = PAGE_SIZE; 1005313371Smav value = ctl_get_opt(&cbe_lun->options, "pblocksize"); 1006313371Smav if (value != NULL) { 1007313371Smav ctl_expand_number(value, &t); 1008313371Smav be_lun->pblocksize = t; 1009313371Smav } 1010313371Smav if (be_lun->pblocksize < 512 || be_lun->pblocksize > 131072) { 1011313371Smav snprintf(req->error_str, sizeof(req->error_str), 1012313371Smav "%s: unsupported pblocksize %u", __func__, 1013313371Smav be_lun->pblocksize); 1014313371Smav goto bailout_error; 1015313371Smav } 1016313371Smav 1017288810Smav if (cbe_lun->lun_type == T_DIRECT || 1018288810Smav cbe_lun->lun_type == T_CDROM) { 1019288727Smav if (params->blocksize_bytes != 0) 1020288727Smav cbe_lun->blocksize = params->blocksize_bytes; 1021288810Smav else if (cbe_lun->lun_type == T_CDROM) 1022288810Smav cbe_lun->blocksize = 2048; 1023288727Smav else 1024288727Smav cbe_lun->blocksize = 512; 1025313371Smav be_lun->pblockmul = be_lun->pblocksize / cbe_lun->blocksize; 1026313371Smav if (be_lun->pblockmul < 1 || !powerof2(be_lun->pblockmul)) { 1027313371Smav snprintf(req->error_str, sizeof(req->error_str), 1028313371Smav "%s: pblocksize %u not exp2 of blocksize %u", 1029313371Smav __func__, 1030313371Smav be_lun->pblocksize, cbe_lun->blocksize); 1031313371Smav goto bailout_error; 1032313371Smav } 1033288727Smav if (params->lun_size_bytes < cbe_lun->blocksize) { 1034229997Sken snprintf(req->error_str, sizeof(req->error_str), 1035229997Sken "%s: LUN size %ju < blocksize %u", __func__, 1036288727Smav params->lun_size_bytes, cbe_lun->blocksize); 1037229997Sken goto bailout_error; 1038229997Sken } 1039288727Smav be_lun->size_blocks = params->lun_size_bytes / cbe_lun->blocksize; 1040288727Smav be_lun->size_bytes = be_lun->size_blocks * cbe_lun->blocksize; 1041313371Smav be_lun->indir = 0; 1042313371Smav t = be_lun->size_bytes / be_lun->pblocksize; 1043313371Smav while (t > 1) { 1044313371Smav t /= PPP; 1045313371Smav be_lun->indir++; 1046313371Smav } 1047288727Smav cbe_lun->maxlba = be_lun->size_blocks - 1; 1048313371Smav cbe_lun->pblockexp = fls(be_lun->pblockmul) - 1; 1049313371Smav cbe_lun->pblockoff = 0; 1050313371Smav cbe_lun->ublockexp = cbe_lun->pblockexp; 1051313371Smav cbe_lun->ublockoff = 0; 1052313371Smav cbe_lun->atomicblock = be_lun->pblocksize; 1053313371Smav cbe_lun->opttxferlen = SGPP * be_lun->pblocksize; 1054313371Smav value = ctl_get_opt(&cbe_lun->options, "capacity"); 1055313371Smav if (value != NULL) 1056313371Smav ctl_expand_number(value, &be_lun->cap_bytes); 1057313371Smav } else { 1058313371Smav be_lun->pblockmul = 1; 1059313371Smav cbe_lun->pblockexp = 0; 1060229997Sken } 1061229997Sken 1062229997Sken /* Tell the user the blocksize we ended up using */ 1063288727Smav params->blocksize_bytes = cbe_lun->blocksize; 1064229997Sken params->lun_size_bytes = be_lun->size_bytes; 1065229997Sken 1066288727Smav value = ctl_get_opt(&cbe_lun->options, "unmap"); 1067313371Smav if (value == NULL || strcmp(value, "off") != 0) 1068288727Smav cbe_lun->flags |= CTL_LUN_FLAG_UNMAP; 1069288727Smav value = ctl_get_opt(&cbe_lun->options, "readonly"); 1070288810Smav if (value != NULL) { 1071288810Smav if (strcmp(value, "on") == 0) 1072288810Smav cbe_lun->flags |= CTL_LUN_FLAG_READONLY; 1073288810Smav } else if (cbe_lun->lun_type != T_DIRECT) 1074288727Smav cbe_lun->flags |= CTL_LUN_FLAG_READONLY; 1075288727Smav cbe_lun->serseq = CTL_LUN_SERSEQ_OFF; 1076288727Smav value = ctl_get_opt(&cbe_lun->options, "serseq"); 1077288727Smav if (value != NULL && strcmp(value, "on") == 0) 1078288727Smav cbe_lun->serseq = CTL_LUN_SERSEQ_ON; 1079288727Smav else if (value != NULL && strcmp(value, "read") == 0) 1080288727Smav cbe_lun->serseq = CTL_LUN_SERSEQ_READ; 1081288727Smav else if (value != NULL && strcmp(value, "off") == 0) 1082288727Smav cbe_lun->serseq = CTL_LUN_SERSEQ_OFF; 1083254759Strasz 1084229997Sken if (params->flags & CTL_LUN_FLAG_ID_REQ) { 1085288727Smav cbe_lun->req_lun_id = params->req_lun_id; 1086288727Smav cbe_lun->flags |= CTL_LUN_FLAG_ID_REQ; 1087229997Sken } else 1088288727Smav cbe_lun->req_lun_id = 0; 1089229997Sken 1090288727Smav cbe_lun->lun_shutdown = ctl_backend_ramdisk_lun_shutdown; 1091288727Smav cbe_lun->lun_config_status = ctl_backend_ramdisk_lun_config_status; 1092288727Smav cbe_lun->be = &ctl_be_ramdisk_driver; 1093229997Sken if ((params->flags & CTL_LUN_FLAG_SERIAL_NUM) == 0) { 1094229997Sken snprintf(tmpstr, sizeof(tmpstr), "MYSERIAL%4d", 1095229997Sken softc->num_luns); 1096288727Smav strncpy((char *)cbe_lun->serial_num, tmpstr, 1097288727Smav MIN(sizeof(cbe_lun->serial_num), sizeof(tmpstr))); 1098229997Sken 1099229997Sken /* Tell the user what we used for a serial number */ 1100229997Sken strncpy((char *)params->serial_num, tmpstr, 1101276616Smav MIN(sizeof(params->serial_num), sizeof(tmpstr))); 1102229997Sken } else { 1103288727Smav strncpy((char *)cbe_lun->serial_num, params->serial_num, 1104288727Smav MIN(sizeof(cbe_lun->serial_num), 1105276616Smav sizeof(params->serial_num))); 1106229997Sken } 1107229997Sken if ((params->flags & CTL_LUN_FLAG_DEVID) == 0) { 1108229997Sken snprintf(tmpstr, sizeof(tmpstr), "MYDEVID%4d", softc->num_luns); 1109288727Smav strncpy((char *)cbe_lun->device_id, tmpstr, 1110288727Smav MIN(sizeof(cbe_lun->device_id), sizeof(tmpstr))); 1111229997Sken 1112229997Sken /* Tell the user what we used for a device ID */ 1113229997Sken strncpy((char *)params->device_id, tmpstr, 1114276616Smav MIN(sizeof(params->device_id), sizeof(tmpstr))); 1115229997Sken } else { 1116288727Smav strncpy((char *)cbe_lun->device_id, params->device_id, 1117288727Smav MIN(sizeof(cbe_lun->device_id), 1118276616Smav sizeof(params->device_id))); 1119229997Sken } 1120229997Sken 1121265642Smav STAILQ_INIT(&be_lun->cont_queue); 1122313371Smav sx_init(&be_lun->page_lock, "cram page lock"); 1123314026Smav if (be_lun->cap_bytes == 0) { 1124314026Smav be_lun->indir = 0; 1125313371Smav be_lun->pages = malloc(be_lun->pblocksize, M_RAMDISK, M_WAITOK); 1126314026Smav } 1127313371Smav be_lun->zero_page = malloc(be_lun->pblocksize, M_RAMDISK, 1128313371Smav M_WAITOK|M_ZERO); 1129268549Smav mtx_init(&be_lun->queue_lock, "cram queue lock", NULL, MTX_DEF); 1130265642Smav TASK_INIT(&be_lun->io_task, /*priority*/0, ctl_backend_ramdisk_worker, 1131265642Smav be_lun); 1132265642Smav 1133265642Smav be_lun->io_taskqueue = taskqueue_create(be_lun->lunname, M_WAITOK, 1134265642Smav taskqueue_thread_enqueue, /*context*/&be_lun->io_taskqueue); 1135265642Smav if (be_lun->io_taskqueue == NULL) { 1136265642Smav snprintf(req->error_str, sizeof(req->error_str), 1137265642Smav "%s: Unable to create taskqueue", __func__); 1138265642Smav goto bailout_error; 1139265642Smav } 1140265642Smav 1141265642Smav retval = taskqueue_start_threads(&be_lun->io_taskqueue, 1142265642Smav /*num threads*/1, 1143265642Smav /*priority*/PWAIT, 1144265642Smav /*thread name*/ 1145265642Smav "%s taskq", be_lun->lunname); 1146265642Smav if (retval != 0) 1147265642Smav goto bailout_error; 1148265642Smav 1149229997Sken mtx_lock(&softc->lock); 1150229997Sken softc->num_luns++; 1151229997Sken STAILQ_INSERT_TAIL(&softc->lun_list, be_lun, links); 1152229997Sken mtx_unlock(&softc->lock); 1153229997Sken 1154288727Smav retval = ctl_add_lun(&be_lun->cbe_lun); 1155229997Sken if (retval != 0) { 1156229997Sken mtx_lock(&softc->lock); 1157229997Sken STAILQ_REMOVE(&softc->lun_list, be_lun, ctl_be_ramdisk_lun, 1158229997Sken links); 1159229997Sken softc->num_luns--; 1160229997Sken mtx_unlock(&softc->lock); 1161229997Sken snprintf(req->error_str, sizeof(req->error_str), 1162229997Sken "%s: ctl_add_lun() returned error %d, see dmesg for " 1163229997Sken "details", __func__, retval); 1164229997Sken retval = 0; 1165229997Sken goto bailout_error; 1166229997Sken } 1167229997Sken 1168229997Sken mtx_lock(&softc->lock); 1169229997Sken 1170229997Sken /* 1171229997Sken * Tell the config_status routine that we're waiting so it won't 1172229997Sken * clean up the LUN in the event of an error. 1173229997Sken */ 1174229997Sken be_lun->flags |= CTL_BE_RAMDISK_LUN_WAITING; 1175229997Sken 1176229997Sken while (be_lun->flags & CTL_BE_RAMDISK_LUN_UNCONFIGURED) { 1177229997Sken retval = msleep(be_lun, &softc->lock, PCATCH, "ctlram", 0); 1178229997Sken if (retval == EINTR) 1179229997Sken break; 1180229997Sken } 1181229997Sken be_lun->flags &= ~CTL_BE_RAMDISK_LUN_WAITING; 1182229997Sken 1183229997Sken if (be_lun->flags & CTL_BE_RAMDISK_LUN_CONFIG_ERR) { 1184229997Sken snprintf(req->error_str, sizeof(req->error_str), 1185229997Sken "%s: LUN configuration error, see dmesg for details", 1186229997Sken __func__); 1187229997Sken STAILQ_REMOVE(&softc->lun_list, be_lun, ctl_be_ramdisk_lun, 1188229997Sken links); 1189229997Sken softc->num_luns--; 1190229997Sken mtx_unlock(&softc->lock); 1191229997Sken goto bailout_error; 1192229997Sken } else { 1193288727Smav params->req_lun_id = cbe_lun->lun_id; 1194229997Sken } 1195229997Sken mtx_unlock(&softc->lock); 1196229997Sken 1197229997Sken req->status = CTL_LUN_OK; 1198229997Sken return (retval); 1199229997Sken 1200229997Skenbailout_error: 1201229997Sken req->status = CTL_LUN_ERROR; 1202265642Smav if (be_lun != NULL) { 1203313371Smav if (be_lun->io_taskqueue != NULL) 1204265642Smav taskqueue_free(be_lun->io_taskqueue); 1205288727Smav ctl_free_opts(&cbe_lun->options); 1206313371Smav free(be_lun->zero_page, M_RAMDISK); 1207313371Smav ctl_backend_ramdisk_freeallpages(be_lun->pages, be_lun->indir); 1208313371Smav sx_destroy(&be_lun->page_lock); 1209268549Smav mtx_destroy(&be_lun->queue_lock); 1210265642Smav free(be_lun, M_RAMDISK); 1211265642Smav } 1212229997Sken return (retval); 1213229997Sken} 1214229997Sken 1215232604Straszstatic int 1216232604Straszctl_backend_ramdisk_modify(struct ctl_be_ramdisk_softc *softc, 1217232604Strasz struct ctl_lun_req *req) 1218232604Strasz{ 1219232604Strasz struct ctl_be_ramdisk_lun *be_lun; 1220288728Smav struct ctl_be_lun *cbe_lun; 1221232604Strasz struct ctl_lun_modify_params *params; 1222288732Smav char *value; 1223232604Strasz uint32_t blocksize; 1224288732Smav int wasprim; 1225232604Strasz 1226232604Strasz params = &req->reqdata.modify; 1227232604Strasz 1228232604Strasz mtx_lock(&softc->lock); 1229232604Strasz STAILQ_FOREACH(be_lun, &softc->lun_list, links) { 1230288727Smav if (be_lun->cbe_lun.lun_id == params->lun_id) 1231232604Strasz break; 1232232604Strasz } 1233232604Strasz mtx_unlock(&softc->lock); 1234232604Strasz if (be_lun == NULL) { 1235232604Strasz snprintf(req->error_str, sizeof(req->error_str), 1236232604Strasz "%s: LUN %u is not managed by the ramdisk backend", 1237232604Strasz __func__, params->lun_id); 1238232604Strasz goto bailout_error; 1239232604Strasz } 1240288728Smav cbe_lun = &be_lun->cbe_lun; 1241232604Strasz 1242288728Smav if (params->lun_size_bytes != 0) 1243288728Smav be_lun->params.lun_size_bytes = params->lun_size_bytes; 1244288728Smav ctl_update_opts(&cbe_lun->options, req->num_be_args, req->kern_be_args); 1245288732Smav 1246288732Smav wasprim = (cbe_lun->flags & CTL_LUN_FLAG_PRIMARY); 1247288732Smav value = ctl_get_opt(&cbe_lun->options, "ha_role"); 1248288732Smav if (value != NULL) { 1249288732Smav if (strcmp(value, "primary") == 0) 1250288732Smav cbe_lun->flags |= CTL_LUN_FLAG_PRIMARY; 1251288732Smav else 1252288732Smav cbe_lun->flags &= ~CTL_LUN_FLAG_PRIMARY; 1253288732Smav } else if (control_softc->flags & CTL_FLAG_ACTIVE_SHELF) 1254288732Smav cbe_lun->flags |= CTL_LUN_FLAG_PRIMARY; 1255288732Smav else 1256288732Smav cbe_lun->flags &= ~CTL_LUN_FLAG_PRIMARY; 1257288732Smav if (wasprim != (cbe_lun->flags & CTL_LUN_FLAG_PRIMARY)) { 1258288732Smav if (cbe_lun->flags & CTL_LUN_FLAG_PRIMARY) 1259288732Smav ctl_lun_primary(cbe_lun); 1260288732Smav else 1261288732Smav ctl_lun_secondary(cbe_lun); 1262288732Smav } 1263288732Smav 1264288727Smav blocksize = be_lun->cbe_lun.blocksize; 1265288728Smav if (be_lun->params.lun_size_bytes < blocksize) { 1266232604Strasz snprintf(req->error_str, sizeof(req->error_str), 1267232604Strasz "%s: LUN size %ju < blocksize %u", __func__, 1268288728Smav be_lun->params.lun_size_bytes, blocksize); 1269232604Strasz goto bailout_error; 1270232604Strasz } 1271288728Smav be_lun->size_blocks = be_lun->params.lun_size_bytes / blocksize; 1272232604Strasz be_lun->size_bytes = be_lun->size_blocks * blocksize; 1273288727Smav be_lun->cbe_lun.maxlba = be_lun->size_blocks - 1; 1274288727Smav ctl_lun_capacity_changed(&be_lun->cbe_lun); 1275232604Strasz 1276232604Strasz /* Tell the user the exact size we ended up using */ 1277232604Strasz params->lun_size_bytes = be_lun->size_bytes; 1278232604Strasz 1279232604Strasz req->status = CTL_LUN_OK; 1280232604Strasz return (0); 1281232604Strasz 1282232604Straszbailout_error: 1283232604Strasz req->status = CTL_LUN_ERROR; 1284232604Strasz return (0); 1285232604Strasz} 1286232604Strasz 1287229997Skenstatic void 1288229997Skenctl_backend_ramdisk_lun_shutdown(void *be_lun) 1289229997Sken{ 1290229997Sken struct ctl_be_ramdisk_lun *lun; 1291229997Sken struct ctl_be_ramdisk_softc *softc; 1292229997Sken int do_free; 1293229997Sken 1294229997Sken lun = (struct ctl_be_ramdisk_lun *)be_lun; 1295229997Sken softc = lun->softc; 1296229997Sken do_free = 0; 1297229997Sken 1298229997Sken mtx_lock(&softc->lock); 1299229997Sken lun->flags |= CTL_BE_RAMDISK_LUN_UNCONFIGURED; 1300229997Sken if (lun->flags & CTL_BE_RAMDISK_LUN_WAITING) { 1301229997Sken wakeup(lun); 1302229997Sken } else { 1303269151Smav STAILQ_REMOVE(&softc->lun_list, lun, ctl_be_ramdisk_lun, 1304229997Sken links); 1305229997Sken softc->num_luns--; 1306229997Sken do_free = 1; 1307229997Sken } 1308229997Sken mtx_unlock(&softc->lock); 1309229997Sken 1310229997Sken if (do_free != 0) 1311229997Sken free(be_lun, M_RAMDISK); 1312229997Sken} 1313229997Sken 1314229997Skenstatic void 1315229997Skenctl_backend_ramdisk_lun_config_status(void *be_lun, 1316229997Sken ctl_lun_config_status status) 1317229997Sken{ 1318229997Sken struct ctl_be_ramdisk_lun *lun; 1319229997Sken struct ctl_be_ramdisk_softc *softc; 1320229997Sken 1321229997Sken lun = (struct ctl_be_ramdisk_lun *)be_lun; 1322229997Sken softc = lun->softc; 1323229997Sken 1324229997Sken if (status == CTL_LUN_CONFIG_OK) { 1325229997Sken mtx_lock(&softc->lock); 1326229997Sken lun->flags &= ~CTL_BE_RAMDISK_LUN_UNCONFIGURED; 1327229997Sken if (lun->flags & CTL_BE_RAMDISK_LUN_WAITING) 1328229997Sken wakeup(lun); 1329229997Sken mtx_unlock(&softc->lock); 1330229997Sken 1331229997Sken /* 1332229997Sken * We successfully added the LUN, attempt to enable it. 1333229997Sken */ 1334288727Smav if (ctl_enable_lun(&lun->cbe_lun) != 0) { 1335229997Sken printf("%s: ctl_enable_lun() failed!\n", __func__); 1336288727Smav if (ctl_invalidate_lun(&lun->cbe_lun) != 0) { 1337229997Sken printf("%s: ctl_invalidate_lun() failed!\n", 1338229997Sken __func__); 1339229997Sken } 1340229997Sken } 1341229997Sken 1342229997Sken return; 1343229997Sken } 1344229997Sken 1345229997Sken 1346229997Sken mtx_lock(&softc->lock); 1347229997Sken lun->flags &= ~CTL_BE_RAMDISK_LUN_UNCONFIGURED; 1348229997Sken 1349229997Sken /* 1350229997Sken * If we have a user waiting, let him handle the cleanup. If not, 1351229997Sken * clean things up here. 1352229997Sken */ 1353229997Sken if (lun->flags & CTL_BE_RAMDISK_LUN_WAITING) { 1354229997Sken lun->flags |= CTL_BE_RAMDISK_LUN_CONFIG_ERR; 1355229997Sken wakeup(lun); 1356229997Sken } else { 1357229997Sken STAILQ_REMOVE(&softc->lun_list, lun, ctl_be_ramdisk_lun, 1358229997Sken links); 1359229997Sken softc->num_luns--; 1360229997Sken free(lun, M_RAMDISK); 1361229997Sken } 1362229997Sken mtx_unlock(&softc->lock); 1363229997Sken} 1364