Path: blob/main/sys/cam/ctl/ctl_backend_ramdisk.c
101202 views
/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 2003, 2008 Silicon Graphics International Corp.4* Copyright (c) 2012 The FreeBSD Foundation5* Copyright (c) 2014-2017 Alexander Motin <[email protected]>6* All rights reserved.7*8* Portions of this software were developed by Edward Tomasz Napierala9* under sponsorship from the FreeBSD Foundation.10*11* Redistribution and use in source and binary forms, with or without12* modification, are permitted provided that the following conditions13* are met:14* 1. Redistributions of source code must retain the above copyright15* notice, this list of conditions, and the following disclaimer,16* without modification.17* 2. Redistributions in binary form must reproduce at minimum a disclaimer18* substantially similar to the "NO WARRANTY" disclaimer below19* ("Disclaimer") and any redistribution must be conditioned upon20* including a substantially similar Disclaimer requirement for further21* binary redistribution.22*23* NO WARRANTY24* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS25* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT26* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR27* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT28* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL29* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS30* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)31* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,32* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING33* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE34* POSSIBILITY OF SUCH DAMAGES.35*36* $Id: //depot/users/kenm/FreeBSD-test2/sys/cam/ctl/ctl_backend_ramdisk.c#3 $37*/38/*39* CAM Target Layer black hole and RAM disk backend.40*41* Author: Ken Merry <[email protected]>42*/4344#include <sys/param.h>45#include <sys/systm.h>46#include <sys/kernel.h>47#include <sys/condvar.h>48#include <sys/types.h>49#include <sys/limits.h>50#include <sys/lock.h>51#include <sys/mutex.h>52#include <sys/malloc.h>53#include <sys/sx.h>54#include <sys/taskqueue.h>55#include <sys/time.h>56#include <sys/queue.h>57#include <sys/conf.h>58#include <sys/ioccom.h>59#include <sys/module.h>60#include <sys/sysctl.h>61#include <sys/nv.h>62#include <sys/dnv.h>6364#include <cam/scsi/scsi_all.h>65#include <cam/scsi/scsi_da.h>66#include <cam/ctl/ctl_io.h>67#include <cam/ctl/ctl.h>68#include <cam/ctl/ctl_util.h>69#include <cam/ctl/ctl_backend.h>70#include <cam/ctl/ctl_debug.h>71#include <cam/ctl/ctl_ioctl.h>72#include <cam/ctl/ctl_ha.h>73#include <cam/ctl/ctl_private.h>74#include <cam/ctl/ctl_error.h>7576#define PRIV(io) \77((struct ctl_ptr_len_flags *)&(io)->io_hdr.ctl_private[CTL_PRIV_BACKEND])78#define ARGS(io) \79((struct ctl_lba_len_flags *)&(io)->io_hdr.ctl_private[CTL_PRIV_LBA_LEN])8081#define PPP (PAGE_SIZE / sizeof(uint8_t **))82#ifdef __LP64__83#define PPPS (PAGE_SHIFT - 3)84#else85#define PPPS (PAGE_SHIFT - 2)86#endif87#define SGPP (PAGE_SIZE / sizeof(struct ctl_sg_entry))8889#define P_UNMAPPED NULL /* Page is unmapped. */90#define P_ANCHORED ((void *)(uintptr_t)1) /* Page is anchored. */9192typedef enum {93GP_READ, /* Return data page or zero page. */94GP_WRITE, /* Return data page, try allocate if none. */95GP_ANCHOR, /* Return data page, try anchor if none. */96GP_OTHER, /* Return what present, do not allocate/anchor. */97} getpage_op_t;9899typedef enum {100CTL_BE_RAMDISK_LUN_UNCONFIGURED = 0x01,101CTL_BE_RAMDISK_LUN_WAITING = 0x04102} ctl_be_ramdisk_lun_flags;103104struct ctl_be_ramdisk_lun {105struct ctl_be_lun cbe_lun; /* Must be first element. */106struct ctl_lun_create_params params;107int indir;108uint8_t **pages;109uint8_t *zero_page;110struct sx page_lock;111u_int pblocksize;112u_int pblockmul;113uint64_t size_bytes;114uint64_t size_blocks;115uint64_t cap_bytes;116uint64_t cap_used;117struct ctl_be_ramdisk_softc *softc;118ctl_be_ramdisk_lun_flags flags;119SLIST_ENTRY(ctl_be_ramdisk_lun) links;120struct taskqueue *io_taskqueue;121struct task io_task;122STAILQ_HEAD(, ctl_io_hdr) cont_queue;123struct mtx_padalign queue_lock;124};125126struct ctl_be_ramdisk_softc {127struct sx modify_lock;128struct mtx lock;129int num_luns;130SLIST_HEAD(, ctl_be_ramdisk_lun) lun_list;131};132133static struct ctl_be_ramdisk_softc rd_softc;134135static int ctl_backend_ramdisk_init(void);136static int ctl_backend_ramdisk_shutdown(void);137static int ctl_backend_ramdisk_move_done(union ctl_io *io, bool samethr);138static void ctl_backend_ramdisk_compare(union ctl_io *io);139static void ctl_backend_ramdisk_rw(union ctl_io *io);140static int ctl_backend_ramdisk_submit(union ctl_io *io);141static void ctl_backend_ramdisk_worker(void *context, int pending);142static int ctl_backend_ramdisk_config_read(union ctl_io *io);143static int ctl_backend_ramdisk_config_write(union ctl_io *io);144static uint64_t ctl_backend_ramdisk_lun_attr(struct ctl_be_lun *cbe_lun, const char *attrname);145static int ctl_backend_ramdisk_ioctl(struct cdev *dev, u_long cmd,146caddr_t addr, int flag, struct thread *td);147static int ctl_backend_ramdisk_rm(struct ctl_be_ramdisk_softc *softc,148struct ctl_lun_req *req);149static int ctl_backend_ramdisk_create(struct ctl_be_ramdisk_softc *softc,150struct ctl_lun_req *req);151static int ctl_backend_ramdisk_modify(struct ctl_be_ramdisk_softc *softc,152struct ctl_lun_req *req);153static void ctl_backend_ramdisk_lun_shutdown(struct ctl_be_lun *cbe_lun);154155static struct ctl_backend_driver ctl_be_ramdisk_driver =156{157.name = "ramdisk",158.flags = CTL_BE_FLAG_HAS_CONFIG,159.init = ctl_backend_ramdisk_init,160.shutdown = ctl_backend_ramdisk_shutdown,161.data_submit = ctl_backend_ramdisk_submit,162.config_read = ctl_backend_ramdisk_config_read,163.config_write = ctl_backend_ramdisk_config_write,164.ioctl = ctl_backend_ramdisk_ioctl,165.lun_attr = ctl_backend_ramdisk_lun_attr,166};167168MALLOC_DEFINE(M_RAMDISK, "ctlramdisk", "Memory used for CTL RAMdisk");169CTL_BACKEND_DECLARE(cbr, ctl_be_ramdisk_driver);170171static int172ctl_backend_ramdisk_init(void)173{174struct ctl_be_ramdisk_softc *softc = &rd_softc;175176memset(softc, 0, sizeof(*softc));177sx_init(&softc->modify_lock, "ctlrammod");178mtx_init(&softc->lock, "ctlram", NULL, MTX_DEF);179SLIST_INIT(&softc->lun_list);180return (0);181}182183static int184ctl_backend_ramdisk_shutdown(void)185{186struct ctl_be_ramdisk_softc *softc = &rd_softc;187struct ctl_be_ramdisk_lun *lun;188189mtx_lock(&softc->lock);190while ((lun = SLIST_FIRST(&softc->lun_list)) != NULL) {191SLIST_REMOVE_HEAD(&softc->lun_list, links);192softc->num_luns--;193/*194* Drop our lock here. Since ctl_remove_lun() can call195* back into us, this could potentially lead to a recursive196* lock of the same mutex, which would cause a hang.197*/198mtx_unlock(&softc->lock);199ctl_remove_lun(&lun->cbe_lun);200mtx_lock(&softc->lock);201}202mtx_unlock(&softc->lock);203mtx_destroy(&softc->lock);204sx_destroy(&softc->modify_lock);205return (0);206}207208static uint8_t *209ctl_backend_ramdisk_getpage(struct ctl_be_ramdisk_lun *be_lun, off_t pn,210getpage_op_t op)211{212uint8_t **p, ***pp;213off_t i;214int s;215216if (be_lun->cap_bytes == 0) {217switch (op) {218case GP_READ:219return (be_lun->zero_page);220case GP_WRITE:221return ((uint8_t *)be_lun->pages);222case GP_ANCHOR:223return (P_ANCHORED);224default:225return (P_UNMAPPED);226}227}228if (op == GP_WRITE || op == GP_ANCHOR) {229sx_xlock(&be_lun->page_lock);230pp = &be_lun->pages;231for (s = (be_lun->indir - 1) * PPPS; s >= 0; s -= PPPS) {232if (*pp == NULL) {233*pp = malloc(PAGE_SIZE, M_RAMDISK,234M_WAITOK|M_ZERO);235}236i = pn >> s;237pp = (uint8_t ***)&(*pp)[i];238pn -= i << s;239}240if (*pp == P_UNMAPPED && be_lun->cap_used < be_lun->cap_bytes) {241if (op == GP_WRITE) {242*pp = malloc(be_lun->pblocksize, M_RAMDISK,243M_WAITOK|M_ZERO);244} else245*pp = P_ANCHORED;246be_lun->cap_used += be_lun->pblocksize;247} else if (*pp == P_ANCHORED && op == GP_WRITE) {248*pp = malloc(be_lun->pblocksize, M_RAMDISK,249M_WAITOK|M_ZERO);250}251sx_xunlock(&be_lun->page_lock);252return ((uint8_t *)*pp);253} else {254sx_slock(&be_lun->page_lock);255p = be_lun->pages;256for (s = (be_lun->indir - 1) * PPPS; s >= 0; s -= PPPS) {257if (p == NULL)258break;259i = pn >> s;260p = (uint8_t **)p[i];261pn -= i << s;262}263sx_sunlock(&be_lun->page_lock);264if ((p == P_UNMAPPED || p == P_ANCHORED) && op == GP_READ)265return (be_lun->zero_page);266return ((uint8_t *)p);267}268};269270static void271ctl_backend_ramdisk_unmappage(struct ctl_be_ramdisk_lun *be_lun, off_t pn)272{273uint8_t ***pp;274off_t i;275int s;276277if (be_lun->cap_bytes == 0)278return;279sx_xlock(&be_lun->page_lock);280pp = &be_lun->pages;281for (s = (be_lun->indir - 1) * PPPS; s >= 0; s -= PPPS) {282if (*pp == NULL)283goto noindir;284i = pn >> s;285pp = (uint8_t ***)&(*pp)[i];286pn -= i << s;287}288if (*pp == P_ANCHORED) {289be_lun->cap_used -= be_lun->pblocksize;290*pp = P_UNMAPPED;291} else if (*pp != P_UNMAPPED) {292free(*pp, M_RAMDISK);293be_lun->cap_used -= be_lun->pblocksize;294*pp = P_UNMAPPED;295}296noindir:297sx_xunlock(&be_lun->page_lock);298};299300static void301ctl_backend_ramdisk_anchorpage(struct ctl_be_ramdisk_lun *be_lun, off_t pn)302{303uint8_t ***pp;304off_t i;305int s;306307if (be_lun->cap_bytes == 0)308return;309sx_xlock(&be_lun->page_lock);310pp = &be_lun->pages;311for (s = (be_lun->indir - 1) * PPPS; s >= 0; s -= PPPS) {312if (*pp == NULL)313goto noindir;314i = pn >> s;315pp = (uint8_t ***)&(*pp)[i];316pn -= i << s;317}318if (*pp == P_UNMAPPED && be_lun->cap_used < be_lun->cap_bytes) {319be_lun->cap_used += be_lun->pblocksize;320*pp = P_ANCHORED;321} else if (*pp != P_ANCHORED) {322free(*pp, M_RAMDISK);323*pp = P_ANCHORED;324}325noindir:326sx_xunlock(&be_lun->page_lock);327};328329static void330ctl_backend_ramdisk_freeallpages(uint8_t **p, int indir)331{332int i;333334if (p == NULL)335return;336if (indir == 0) {337free(p, M_RAMDISK);338return;339}340for (i = 0; i < PPP; i++) {341if (p[i] == NULL)342continue;343ctl_backend_ramdisk_freeallpages((uint8_t **)p[i], indir - 1);344}345free(p, M_RAMDISK);346};347348static size_t349cmp(uint8_t *a, uint8_t *b, size_t size)350{351size_t i;352353for (i = 0; i < size; i++) {354if (a[i] != b[i])355break;356}357return (i);358}359360static int361ctl_backend_ramdisk_cmp(union ctl_io *io)362{363struct ctl_be_lun *cbe_lun = CTL_BACKEND_LUN(io);364struct ctl_be_ramdisk_lun *be_lun = (struct ctl_be_ramdisk_lun *)cbe_lun;365uint8_t *page;366uint64_t lba;367u_int lbaoff, lbas, res, off;368369lbas = ctl_kern_data_len(io) / cbe_lun->blocksize;370lba = ARGS(io)->lba + PRIV(io)->len - lbas;371off = 0;372for (; lbas > 0; lbas--, lba++) {373page = ctl_backend_ramdisk_getpage(be_lun,374lba >> cbe_lun->pblockexp, GP_READ);375lbaoff = lba & ~(UINT_MAX << cbe_lun->pblockexp);376page += lbaoff * cbe_lun->blocksize;377res = cmp(ctl_kern_data_ptr(io) + off, page,378cbe_lun->blocksize);379off += res;380if (res < cbe_lun->blocksize)381break;382}383free(io->scsiio.kern_data_ptr, M_RAMDISK);384if (lbas > 0) {385off += ctl_kern_rel_offset(io) - ctl_kern_data_len(io);386ctl_io_set_compare_failure(io, off);387return (1);388}389return (0);390}391392static int393ctl_backend_ramdisk_move_done(union ctl_io *io, bool samethr)394{395struct ctl_be_ramdisk_lun *be_lun =396(struct ctl_be_ramdisk_lun *)CTL_BACKEND_LUN(io);397398CTL_DEBUG_PRINT(("ctl_backend_ramdisk_move_done\n"));399if (ctl_kern_sg_entries(io) > 0)400free(ctl_kern_data_ptr(io), M_RAMDISK);401ctl_add_kern_rel_offset(io, ctl_kern_data_len(io));402if ((io->io_hdr.flags & CTL_FLAG_ABORT) == 0 &&403(io->io_hdr.status & CTL_STATUS_MASK) == CTL_STATUS_NONE) {404if (ARGS(io)->flags & CTL_LLF_COMPARE) {405/* We have data block ready for comparison. */406if (ctl_backend_ramdisk_cmp(io))407goto done;408}409if (ARGS(io)->len > PRIV(io)->len) {410mtx_lock(&be_lun->queue_lock);411STAILQ_INSERT_TAIL(&be_lun->cont_queue,412&io->io_hdr, links);413mtx_unlock(&be_lun->queue_lock);414taskqueue_enqueue(be_lun->io_taskqueue,415&be_lun->io_task);416return (0);417}418ctl_io_set_success(io);419}420done:421ctl_data_submit_done(io);422return(0);423}424425static void426ctl_backend_ramdisk_compare(union ctl_io *io)427{428struct ctl_be_lun *cbe_lun = CTL_BACKEND_LUN(io);429u_int lbas, len;430431lbas = ARGS(io)->len - PRIV(io)->len;432lbas = MIN(lbas, 131072 / cbe_lun->blocksize);433len = lbas * cbe_lun->blocksize;434435ctl_set_be_move_done(io, ctl_backend_ramdisk_move_done);436ctl_set_kern_data_ptr(io, malloc(len, M_RAMDISK, M_WAITOK));437ctl_set_kern_data_len(io, len);438ctl_set_kern_sg_entries(io, 0);439io->io_hdr.flags |= CTL_FLAG_ALLOCATED;440PRIV(io)->len += lbas;441ctl_datamove(io);442}443444static void445ctl_backend_ramdisk_rw(union ctl_io *io)446{447struct ctl_be_lun *cbe_lun = CTL_BACKEND_LUN(io);448struct ctl_be_ramdisk_lun *be_lun = (struct ctl_be_ramdisk_lun *)cbe_lun;449struct ctl_sg_entry *sg_entries;450uint8_t *page;451uint64_t lba;452u_int i, len, lbaoff, lbas, sgs, off;453getpage_op_t op;454455lba = ARGS(io)->lba + PRIV(io)->len;456lbaoff = lba & ~(UINT_MAX << cbe_lun->pblockexp);457lbas = ARGS(io)->len - PRIV(io)->len;458lbas = MIN(lbas, (SGPP << cbe_lun->pblockexp) - lbaoff);459sgs = (lbas + lbaoff + be_lun->pblockmul - 1) >> cbe_lun->pblockexp;460off = lbaoff * cbe_lun->blocksize;461op = (ARGS(io)->flags & CTL_LLF_WRITE) ? GP_WRITE : GP_READ;462if (sgs > 1) {463sg_entries = malloc(sizeof(struct ctl_sg_entry) * sgs,464M_RAMDISK, M_WAITOK);465ctl_set_kern_data_ptr(io, sg_entries);466len = lbas * cbe_lun->blocksize;467for (i = 0; i < sgs; i++) {468page = ctl_backend_ramdisk_getpage(be_lun,469(lba >> cbe_lun->pblockexp) + i, op);470if (page == P_UNMAPPED || page == P_ANCHORED) {471free(sg_entries, M_RAMDISK);472nospc:473ctl_io_set_space_alloc_fail(io);474ctl_data_submit_done(io);475return;476}477sg_entries[i].addr = page + off;478sg_entries[i].len = MIN(len, be_lun->pblocksize - off);479len -= sg_entries[i].len;480off = 0;481}482} else {483page = ctl_backend_ramdisk_getpage(be_lun,484lba >> cbe_lun->pblockexp, op);485if (page == P_UNMAPPED || page == P_ANCHORED)486goto nospc;487sgs = 0;488ctl_set_kern_data_ptr(io, page + off);489}490491ctl_set_be_move_done(io, ctl_backend_ramdisk_move_done);492ctl_set_kern_data_len(io, lbas * cbe_lun->blocksize);493ctl_set_kern_sg_entries(io, sgs);494io->io_hdr.flags |= CTL_FLAG_ALLOCATED;495PRIV(io)->len += lbas;496if ((ARGS(io)->flags & CTL_LLF_READ) &&497ARGS(io)->len <= PRIV(io)->len) {498ctl_io_set_success(io);499if (cbe_lun->serseq >= CTL_LUN_SERSEQ_SOFT)500ctl_serseq_done(io);501}502ctl_datamove(io);503}504505static int506ctl_backend_ramdisk_submit(union ctl_io *io)507{508struct ctl_lba_len_flags *lbalen = ARGS(io);509510if (lbalen->flags & CTL_LLF_VERIFY) {511ctl_io_set_success(io);512ctl_data_submit_done(io);513return (CTL_RETVAL_COMPLETE);514}515PRIV(io)->len = 0;516if (lbalen->flags & CTL_LLF_COMPARE)517ctl_backend_ramdisk_compare(io);518else519ctl_backend_ramdisk_rw(io);520return (CTL_RETVAL_COMPLETE);521}522523static void524ctl_backend_ramdisk_worker(void *context, int pending)525{526struct ctl_be_ramdisk_lun *be_lun;527union ctl_io *io;528529be_lun = (struct ctl_be_ramdisk_lun *)context;530mtx_lock(&be_lun->queue_lock);531for (;;) {532io = (union ctl_io *)STAILQ_FIRST(&be_lun->cont_queue);533if (io != NULL) {534STAILQ_REMOVE_HEAD(&be_lun->cont_queue, links);535mtx_unlock(&be_lun->queue_lock);536if (ARGS(io)->flags & CTL_LLF_COMPARE)537ctl_backend_ramdisk_compare(io);538else539ctl_backend_ramdisk_rw(io);540mtx_lock(&be_lun->queue_lock);541continue;542}543544/*545* If we get here, there is no work left in the queues, so546* just break out and let the task queue go to sleep.547*/548break;549}550mtx_unlock(&be_lun->queue_lock);551}552553static int554ctl_backend_ramdisk_gls(union ctl_io *io)555{556struct ctl_be_lun *cbe_lun = CTL_BACKEND_LUN(io);557struct ctl_be_ramdisk_lun *be_lun = (struct ctl_be_ramdisk_lun *)cbe_lun;558struct scsi_get_lba_status_data *data;559uint8_t *page;560u_int lbaoff;561562data = (struct scsi_get_lba_status_data *)io->scsiio.kern_data_ptr;563scsi_u64to8b(ARGS(io)->lba, data->descr[0].addr);564lbaoff = ARGS(io)->lba & ~(UINT_MAX << cbe_lun->pblockexp);565scsi_ulto4b(be_lun->pblockmul - lbaoff, data->descr[0].length);566page = ctl_backend_ramdisk_getpage(be_lun,567ARGS(io)->lba >> cbe_lun->pblockexp, GP_OTHER);568if (page == P_UNMAPPED)569data->descr[0].status = 1;570else if (page == P_ANCHORED)571data->descr[0].status = 2;572else573data->descr[0].status = 0;574ctl_config_read_done(io);575return (CTL_RETVAL_COMPLETE);576}577578static int579ctl_backend_ramdisk_scsi_config_read(union ctl_io *io)580{581int retval = 0;582583switch (io->scsiio.cdb[0]) {584case SERVICE_ACTION_IN:585if (io->scsiio.cdb[1] == SGLS_SERVICE_ACTION) {586retval = ctl_backend_ramdisk_gls(io);587break;588}589ctl_set_invalid_field(&io->scsiio,590/*sks_valid*/ 1,591/*command*/ 1,592/*field*/ 1,593/*bit_valid*/ 1,594/*bit*/ 4);595ctl_config_read_done(io);596retval = CTL_RETVAL_COMPLETE;597break;598default:599ctl_set_invalid_opcode(&io->scsiio);600ctl_config_read_done(io);601retval = CTL_RETVAL_COMPLETE;602break;603}604return (retval);605}606607static int608ramdisk_namespace_data(union ctl_io *io)609{610struct ctl_be_lun *cbe_lun = CTL_BACKEND_LUN(io);611struct ctl_be_ramdisk_lun *be_lun = (struct ctl_be_ramdisk_lun *)cbe_lun;612struct nvme_namespace_data *nsdata;613614if (io->nvmeio.kern_data_len != sizeof(struct nvme_namespace_data) ||615io->nvmeio.kern_sg_entries != 0)616return (CTL_RETVAL_ERROR);617618nsdata = (struct nvme_namespace_data *)io->nvmeio.kern_data_ptr;619memset(nsdata, 0, sizeof(*nsdata));620nsdata->nsze = htole64(be_lun->size_blocks);621nsdata->ncap = htole64(be_lun->cap_bytes / cbe_lun->blocksize);622nsdata->nuse = htole64(be_lun->cap_used / cbe_lun->blocksize);623nsdata->nsfeat = NVMEM(NVME_NS_DATA_NSFEAT_THIN_PROV) |624NVMEM(NVME_NS_DATA_NSFEAT_DEALLOC);625nsdata->nlbaf = 1 - 1;626nsdata->dlfeat = NVMEM(NVME_NS_DATA_DLFEAT_DWZ) |627NVMEF(NVME_NS_DATA_DLFEAT_READ, NVME_NS_DATA_DLFEAT_READ_00);628nsdata->flbas = NVMEF(NVME_NS_DATA_FLBAS_FORMAT, 0);629nsdata->lbaf[0] = NVMEF(NVME_NS_DATA_LBAF_LBADS,630ffs(cbe_lun->blocksize) - 1);631632ctl_lun_nsdata_ids(cbe_lun, nsdata);633ctl_config_read_done(io);634return (CTL_RETVAL_COMPLETE);635}636637static int638ramdisk_nvme_ids(union ctl_io *io)639{640struct ctl_be_lun *cbe_lun = CTL_BACKEND_LUN(io);641642if (io->nvmeio.kern_data_len != 4096 || io->nvmeio.kern_sg_entries != 0)643return (CTL_RETVAL_ERROR);644645ctl_lun_nvme_ids(cbe_lun, io->nvmeio.kern_data_ptr);646ctl_config_read_done(io);647return (CTL_RETVAL_COMPLETE);648}649650static int651ctl_backend_ramdisk_nvme_config_read(union ctl_io *io)652{653switch (io->nvmeio.cmd.opc) {654case NVME_OPC_IDENTIFY:655{656uint8_t cns;657658cns = le32toh(io->nvmeio.cmd.cdw10) & 0xff;659switch (cns) {660case 0:661return (ramdisk_namespace_data(io));662case 3:663return (ramdisk_nvme_ids(io));664default:665ctl_nvme_set_invalid_field(&io->nvmeio);666ctl_config_read_done(io);667return (CTL_RETVAL_COMPLETE);668}669}670default:671ctl_nvme_set_invalid_opcode(&io->nvmeio);672ctl_config_read_done(io);673return (CTL_RETVAL_COMPLETE);674}675}676677static int678ctl_backend_ramdisk_config_read(union ctl_io *io)679{680switch (io->io_hdr.io_type) {681case CTL_IO_SCSI:682return (ctl_backend_ramdisk_scsi_config_read(io));683case CTL_IO_NVME_ADMIN:684return (ctl_backend_ramdisk_nvme_config_read(io));685default:686__assert_unreachable();687}688}689690static void691ctl_backend_ramdisk_delete(struct ctl_be_lun *cbe_lun, off_t lba, off_t len,692int anchor)693{694struct ctl_be_ramdisk_lun *be_lun = (struct ctl_be_ramdisk_lun *)cbe_lun;695uint8_t *page;696uint64_t p, lp;697u_int lbaoff;698getpage_op_t op = anchor ? GP_ANCHOR : GP_OTHER;699700/* Partially zero first partial page. */701p = lba >> cbe_lun->pblockexp;702lbaoff = lba & ~(UINT_MAX << cbe_lun->pblockexp);703if (lbaoff != 0) {704page = ctl_backend_ramdisk_getpage(be_lun, p, op);705if (page != P_UNMAPPED && page != P_ANCHORED) {706memset(page + lbaoff * cbe_lun->blocksize, 0,707min(len, be_lun->pblockmul - lbaoff) *708cbe_lun->blocksize);709}710p++;711}712713/* Partially zero last partial page. */714lp = (lba + len) >> cbe_lun->pblockexp;715lbaoff = (lba + len) & ~(UINT_MAX << cbe_lun->pblockexp);716if (p <= lp && lbaoff != 0) {717page = ctl_backend_ramdisk_getpage(be_lun, lp, op);718if (page != P_UNMAPPED && page != P_ANCHORED)719memset(page, 0, lbaoff * cbe_lun->blocksize);720}721722/* Delete remaining full pages. */723if (anchor) {724for (; p < lp; p++)725ctl_backend_ramdisk_anchorpage(be_lun, p);726} else {727for (; p < lp; p++)728ctl_backend_ramdisk_unmappage(be_lun, p);729}730}731732static void733ctl_backend_ramdisk_ws(union ctl_io *io)734{735struct ctl_be_lun *cbe_lun = CTL_BACKEND_LUN(io);736struct ctl_be_ramdisk_lun *be_lun = (struct ctl_be_ramdisk_lun *)cbe_lun;737struct ctl_lba_len_flags *lbalen = ARGS(io);738uint8_t *page;739uint64_t lba;740u_int lbaoff, lbas;741742CTL_IO_ASSERT(io, SCSI);743744if (lbalen->flags & ~(SWS_LBDATA | SWS_UNMAP | SWS_ANCHOR | SWS_NDOB)) {745ctl_set_invalid_field(&io->scsiio,746/*sks_valid*/ 1,747/*command*/ 1,748/*field*/ 1,749/*bit_valid*/ 0,750/*bit*/ 0);751ctl_config_write_done(io);752return;753}754if (lbalen->flags & SWS_UNMAP) {755ctl_backend_ramdisk_delete(cbe_lun, lbalen->lba, lbalen->len,756(lbalen->flags & SWS_ANCHOR) != 0);757ctl_set_success(&io->scsiio);758ctl_config_write_done(io);759return;760}761762for (lba = lbalen->lba, lbas = lbalen->len; lbas > 0; lba++, lbas--) {763page = ctl_backend_ramdisk_getpage(be_lun,764lba >> cbe_lun->pblockexp, GP_WRITE);765if (page == P_UNMAPPED || page == P_ANCHORED) {766ctl_set_space_alloc_fail(&io->scsiio);767ctl_data_submit_done(io);768return;769}770lbaoff = lba & ~(UINT_MAX << cbe_lun->pblockexp);771page += lbaoff * cbe_lun->blocksize;772if (lbalen->flags & SWS_NDOB) {773memset(page, 0, cbe_lun->blocksize);774} else {775memcpy(page, io->scsiio.kern_data_ptr,776cbe_lun->blocksize);777}778if (lbalen->flags & SWS_LBDATA)779scsi_ulto4b(lba, page);780}781ctl_set_success(&io->scsiio);782ctl_config_write_done(io);783}784785static void786ctl_backend_ramdisk_unmap(union ctl_io *io)787{788struct ctl_be_lun *cbe_lun = CTL_BACKEND_LUN(io);789struct ctl_ptr_len_flags *ptrlen = (struct ctl_ptr_len_flags *)ARGS(io);790struct scsi_unmap_desc *buf, *end;791792CTL_IO_ASSERT(io, SCSI);793794if ((ptrlen->flags & ~SU_ANCHOR) != 0) {795ctl_set_invalid_field(&io->scsiio,796/*sks_valid*/ 0,797/*command*/ 0,798/*field*/ 0,799/*bit_valid*/ 0,800/*bit*/ 0);801ctl_config_write_done(io);802return;803}804805buf = (struct scsi_unmap_desc *)ptrlen->ptr;806end = buf + ptrlen->len / sizeof(*buf);807for (; buf < end; buf++) {808ctl_backend_ramdisk_delete(cbe_lun,809scsi_8btou64(buf->lba), scsi_4btoul(buf->length),810(ptrlen->flags & SU_ANCHOR) != 0);811}812813ctl_set_success(&io->scsiio);814ctl_config_write_done(io);815}816817static int818ctl_backend_ramdisk_scsi_config_write(union ctl_io *io)819{820struct ctl_be_lun *cbe_lun = CTL_BACKEND_LUN(io);821int retval = 0;822823switch (io->scsiio.cdb[0]) {824case SYNCHRONIZE_CACHE:825case SYNCHRONIZE_CACHE_16:826/* We have no cache to flush. */827ctl_set_success(&io->scsiio);828ctl_config_write_done(io);829break;830case START_STOP_UNIT: {831struct scsi_start_stop_unit *cdb;832833cdb = (struct scsi_start_stop_unit *)io->scsiio.cdb;834if ((cdb->how & SSS_PC_MASK) != 0) {835ctl_set_success(&io->scsiio);836ctl_config_write_done(io);837break;838}839if (cdb->how & SSS_START) {840if (cdb->how & SSS_LOEJ)841ctl_lun_has_media(cbe_lun);842ctl_start_lun(cbe_lun);843} else {844ctl_stop_lun(cbe_lun);845if (cdb->how & SSS_LOEJ)846ctl_lun_ejected(cbe_lun);847}848ctl_set_success(&io->scsiio);849ctl_config_write_done(io);850break;851}852case PREVENT_ALLOW:853ctl_set_success(&io->scsiio);854ctl_config_write_done(io);855break;856case WRITE_SAME_10:857case WRITE_SAME_16:858ctl_backend_ramdisk_ws(io);859break;860case UNMAP:861ctl_backend_ramdisk_unmap(io);862break;863default:864ctl_set_invalid_opcode(&io->scsiio);865ctl_config_write_done(io);866retval = CTL_RETVAL_COMPLETE;867break;868}869870return (retval);871}872873static void874ctl_backend_ramdisk_wu(union ctl_io *io)875{876struct ctl_be_lun *cbe_lun = CTL_BACKEND_LUN(io);877struct ctl_lba_len_flags *lbalen = ARGS(io);878879CTL_IO_ASSERT(io, NVME);880881/*882* XXX: Not quite right as reads will return zeroes rather883* than failing.884*/885ctl_backend_ramdisk_delete(cbe_lun, lbalen->lba, lbalen->len, 1);886ctl_nvme_set_success(&io->nvmeio);887ctl_config_write_done(io);888}889890static void891ctl_backend_ramdisk_wz(union ctl_io *io)892{893struct ctl_be_lun *cbe_lun = CTL_BACKEND_LUN(io);894struct ctl_be_ramdisk_lun *be_lun = (struct ctl_be_ramdisk_lun *)cbe_lun;895struct ctl_lba_len_flags *lbalen = ARGS(io);896uint8_t *page;897uint64_t lba;898u_int lbaoff, lbas;899900CTL_IO_ASSERT(io, NVME);901902if ((le32toh(io->nvmeio.cmd.cdw12) & (1U << 25)) != 0) {903ctl_backend_ramdisk_delete(cbe_lun, lbalen->lba, lbalen->len,9040);905ctl_nvme_set_success(&io->nvmeio);906ctl_config_write_done(io);907return;908}909910for (lba = lbalen->lba, lbas = lbalen->len; lbas > 0; lba++, lbas--) {911page = ctl_backend_ramdisk_getpage(be_lun,912lba >> cbe_lun->pblockexp, GP_WRITE);913if (page == P_UNMAPPED || page == P_ANCHORED) {914ctl_nvme_set_space_alloc_fail(&io->nvmeio);915ctl_data_submit_done(io);916return;917}918lbaoff = lba & ~(UINT_MAX << cbe_lun->pblockexp);919page += lbaoff * cbe_lun->blocksize;920memset(page, 0, cbe_lun->blocksize);921}922ctl_nvme_set_success(&io->nvmeio);923ctl_config_write_done(io);924}925926static void927ctl_backend_ramdisk_dsm(union ctl_io *io)928{929struct ctl_be_lun *cbe_lun = CTL_BACKEND_LUN(io);930struct nvme_dsm_range *r;931uint64_t lba;932uint32_t num_blocks;933u_int i, ranges;934935CTL_IO_ASSERT(io, NVME);936937ranges = le32toh(io->nvmeio.cmd.cdw10) & 0xff;938r = (struct nvme_dsm_range *)io->nvmeio.kern_data_ptr;939for (i = 0; i < ranges; i++) {940lba = le64toh(r[i].starting_lba);941num_blocks = le32toh(r[i].length);942if ((le32toh(r[i].attributes) & (1U << 2)) != 0)943ctl_backend_ramdisk_delete(cbe_lun, lba, num_blocks, 0);944}945946ctl_nvme_set_success(&io->nvmeio);947ctl_config_write_done(io);948}949950static int951ctl_backend_ramdisk_nvme_config_write(union ctl_io *io)952{953switch (io->nvmeio.cmd.opc) {954case NVME_OPC_FLUSH:955/* We have no cache to flush. */956ctl_nvme_set_success(&io->nvmeio);957ctl_config_write_done(io);958break;959case NVME_OPC_WRITE_UNCORRECTABLE:960ctl_backend_ramdisk_wu(io);961break;962case NVME_OPC_WRITE_ZEROES:963ctl_backend_ramdisk_wz(io);964break;965case NVME_OPC_DATASET_MANAGEMENT:966ctl_backend_ramdisk_dsm(io);967break;968default:969ctl_nvme_set_invalid_opcode(&io->nvmeio);970ctl_config_write_done(io);971break;972}973return (CTL_RETVAL_COMPLETE);974}975976static int977ctl_backend_ramdisk_config_write(union ctl_io *io)978{979switch (io->io_hdr.io_type) {980case CTL_IO_SCSI:981return (ctl_backend_ramdisk_scsi_config_write(io));982case CTL_IO_NVME:983return (ctl_backend_ramdisk_nvme_config_write(io));984default:985__assert_unreachable();986}987}988989static uint64_t990ctl_backend_ramdisk_lun_attr(struct ctl_be_lun *cbe_lun, const char *attrname)991{992struct ctl_be_ramdisk_lun *be_lun = (struct ctl_be_ramdisk_lun *)cbe_lun;993uint64_t val;994995val = UINT64_MAX;996if (be_lun->cap_bytes == 0)997return (val);998sx_slock(&be_lun->page_lock);999if (strcmp(attrname, "blocksused") == 0) {1000val = be_lun->cap_used / be_lun->cbe_lun.blocksize;1001} else if (strcmp(attrname, "blocksavail") == 0) {1002val = (be_lun->cap_bytes - be_lun->cap_used) /1003be_lun->cbe_lun.blocksize;1004}1005sx_sunlock(&be_lun->page_lock);1006return (val);1007}10081009static int1010ctl_backend_ramdisk_ioctl(struct cdev *dev, u_long cmd, caddr_t addr,1011int flag, struct thread *td)1012{1013struct ctl_be_ramdisk_softc *softc = &rd_softc;1014struct ctl_lun_req *lun_req;1015int retval;10161017retval = 0;1018switch (cmd) {1019case CTL_LUN_REQ:1020lun_req = (struct ctl_lun_req *)addr;1021switch (lun_req->reqtype) {1022case CTL_LUNREQ_CREATE:1023retval = ctl_backend_ramdisk_create(softc, lun_req);1024break;1025case CTL_LUNREQ_RM:1026retval = ctl_backend_ramdisk_rm(softc, lun_req);1027break;1028case CTL_LUNREQ_MODIFY:1029retval = ctl_backend_ramdisk_modify(softc, lun_req);1030break;1031default:1032lun_req->status = CTL_LUN_ERROR;1033snprintf(lun_req->error_str, sizeof(lun_req->error_str),1034"%s: invalid LUN request type %d", __func__,1035lun_req->reqtype);1036break;1037}1038break;1039default:1040retval = ENOTTY;1041break;1042}10431044return (retval);1045}10461047static int1048ctl_backend_ramdisk_rm(struct ctl_be_ramdisk_softc *softc,1049struct ctl_lun_req *req)1050{1051struct ctl_be_ramdisk_lun *be_lun;1052struct ctl_lun_rm_params *params;1053int retval;10541055params = &req->reqdata.rm;1056sx_xlock(&softc->modify_lock);1057mtx_lock(&softc->lock);1058SLIST_FOREACH(be_lun, &softc->lun_list, links) {1059if (be_lun->cbe_lun.lun_id == params->lun_id) {1060SLIST_REMOVE(&softc->lun_list, be_lun,1061ctl_be_ramdisk_lun, links);1062softc->num_luns--;1063break;1064}1065}1066mtx_unlock(&softc->lock);1067sx_xunlock(&softc->modify_lock);1068if (be_lun == NULL) {1069snprintf(req->error_str, sizeof(req->error_str),1070"%s: LUN %u is not managed by the ramdisk backend",1071__func__, params->lun_id);1072goto bailout_error;1073}10741075/*1076* Set the waiting flag before we invalidate the LUN. Our shutdown1077* routine can be called any time after we invalidate the LUN,1078* and can be called from our context.1079*1080* This tells the shutdown routine that we're waiting, or we're1081* going to wait for the shutdown to happen.1082*/1083mtx_lock(&softc->lock);1084be_lun->flags |= CTL_BE_RAMDISK_LUN_WAITING;1085mtx_unlock(&softc->lock);10861087retval = ctl_remove_lun(&be_lun->cbe_lun);1088if (retval != 0) {1089snprintf(req->error_str, sizeof(req->error_str),1090"%s: error %d returned from ctl_remove_lun() for "1091"LUN %d", __func__, retval, params->lun_id);1092mtx_lock(&softc->lock);1093be_lun->flags &= ~CTL_BE_RAMDISK_LUN_WAITING;1094mtx_unlock(&softc->lock);1095goto bailout_error;1096}10971098mtx_lock(&softc->lock);1099while ((be_lun->flags & CTL_BE_RAMDISK_LUN_UNCONFIGURED) == 0) {1100retval = msleep(be_lun, &softc->lock, PCATCH, "ctlramrm", 0);1101if (retval == EINTR)1102break;1103}1104be_lun->flags &= ~CTL_BE_RAMDISK_LUN_WAITING;1105if (be_lun->flags & CTL_BE_RAMDISK_LUN_UNCONFIGURED) {1106mtx_unlock(&softc->lock);1107free(be_lun, M_RAMDISK);1108} else {1109mtx_unlock(&softc->lock);1110return (EINTR);1111}11121113req->status = CTL_LUN_OK;1114return (retval);11151116bailout_error:1117req->status = CTL_LUN_ERROR;1118return (0);1119}11201121static int1122ctl_backend_ramdisk_create(struct ctl_be_ramdisk_softc *softc,1123struct ctl_lun_req *req)1124{1125struct ctl_be_ramdisk_lun *be_lun;1126struct ctl_be_lun *cbe_lun;1127struct ctl_lun_create_params *params;1128const char *value;1129char tmpstr[32];1130uint64_t t;1131int retval;11321133retval = 0;1134params = &req->reqdata.create;11351136be_lun = malloc(sizeof(*be_lun), M_RAMDISK, M_ZERO | M_WAITOK);1137cbe_lun = &be_lun->cbe_lun;1138cbe_lun->options = nvlist_clone(req->args_nvl);1139be_lun->params = req->reqdata.create;1140be_lun->softc = softc;11411142if (params->flags & CTL_LUN_FLAG_DEV_TYPE)1143cbe_lun->lun_type = params->device_type;1144else1145cbe_lun->lun_type = T_DIRECT;1146be_lun->flags = 0;1147cbe_lun->flags = 0;1148value = dnvlist_get_string(cbe_lun->options, "ha_role", NULL);1149if (value != NULL) {1150if (strcmp(value, "primary") == 0)1151cbe_lun->flags |= CTL_LUN_FLAG_PRIMARY;1152} else if (control_softc->flags & CTL_FLAG_ACTIVE_SHELF)1153cbe_lun->flags |= CTL_LUN_FLAG_PRIMARY;11541155be_lun->pblocksize = PAGE_SIZE;1156value = dnvlist_get_string(cbe_lun->options, "pblocksize", NULL);1157if (value != NULL) {1158ctl_expand_number(value, &t);1159be_lun->pblocksize = t;1160}1161if (be_lun->pblocksize < 512 || be_lun->pblocksize > 131072) {1162snprintf(req->error_str, sizeof(req->error_str),1163"%s: unsupported pblocksize %u", __func__,1164be_lun->pblocksize);1165goto bailout_error;1166}11671168if (cbe_lun->lun_type == T_DIRECT ||1169cbe_lun->lun_type == T_CDROM) {1170if (params->blocksize_bytes != 0)1171cbe_lun->blocksize = params->blocksize_bytes;1172else if (cbe_lun->lun_type == T_CDROM)1173cbe_lun->blocksize = 2048;1174else1175cbe_lun->blocksize = 512;1176be_lun->pblockmul = be_lun->pblocksize / cbe_lun->blocksize;1177if (be_lun->pblockmul < 1 || !powerof2(be_lun->pblockmul)) {1178snprintf(req->error_str, sizeof(req->error_str),1179"%s: pblocksize %u not exp2 of blocksize %u",1180__func__,1181be_lun->pblocksize, cbe_lun->blocksize);1182goto bailout_error;1183}1184if (params->lun_size_bytes < cbe_lun->blocksize) {1185snprintf(req->error_str, sizeof(req->error_str),1186"%s: LUN size %ju < blocksize %u", __func__,1187params->lun_size_bytes, cbe_lun->blocksize);1188goto bailout_error;1189}1190be_lun->size_blocks = params->lun_size_bytes / cbe_lun->blocksize;1191be_lun->size_bytes = be_lun->size_blocks * cbe_lun->blocksize;1192be_lun->indir = 0;1193t = be_lun->size_bytes / be_lun->pblocksize;1194while (t > 1) {1195t /= PPP;1196be_lun->indir++;1197}1198cbe_lun->maxlba = be_lun->size_blocks - 1;1199cbe_lun->pblockexp = fls(be_lun->pblockmul) - 1;1200cbe_lun->pblockoff = 0;1201cbe_lun->ublockexp = cbe_lun->pblockexp;1202cbe_lun->ublockoff = 0;1203cbe_lun->atomicblock = be_lun->pblocksize;1204cbe_lun->opttxferlen = SGPP * be_lun->pblocksize;1205value = dnvlist_get_string(cbe_lun->options, "capacity", NULL);1206if (value != NULL)1207ctl_expand_number(value, &be_lun->cap_bytes);1208} else {1209be_lun->pblockmul = 1;1210cbe_lun->pblockexp = 0;1211}12121213/* Tell the user the blocksize we ended up using */1214params->blocksize_bytes = cbe_lun->blocksize;1215params->lun_size_bytes = be_lun->size_bytes;12161217value = dnvlist_get_string(cbe_lun->options, "unmap", NULL);1218if (value == NULL || strcmp(value, "off") != 0)1219cbe_lun->flags |= CTL_LUN_FLAG_UNMAP;1220value = dnvlist_get_string(cbe_lun->options, "readonly", NULL);1221if (value != NULL) {1222if (strcmp(value, "on") == 0)1223cbe_lun->flags |= CTL_LUN_FLAG_READONLY;1224} else if (cbe_lun->lun_type != T_DIRECT)1225cbe_lun->flags |= CTL_LUN_FLAG_READONLY;1226cbe_lun->serseq = CTL_LUN_SERSEQ_OFF;1227value = dnvlist_get_string(cbe_lun->options, "serseq", NULL);1228if (value != NULL && strcmp(value, "on") == 0)1229cbe_lun->serseq = CTL_LUN_SERSEQ_ON;1230else if (value != NULL && strcmp(value, "read") == 0)1231cbe_lun->serseq = CTL_LUN_SERSEQ_READ;1232else if (value != NULL && strcmp(value, "soft") == 0)1233cbe_lun->serseq = CTL_LUN_SERSEQ_SOFT;1234else if (value != NULL && strcmp(value, "off") == 0)1235cbe_lun->serseq = CTL_LUN_SERSEQ_OFF;12361237if (params->flags & CTL_LUN_FLAG_ID_REQ) {1238cbe_lun->req_lun_id = params->req_lun_id;1239cbe_lun->flags |= CTL_LUN_FLAG_ID_REQ;1240} else1241cbe_lun->req_lun_id = 0;12421243cbe_lun->lun_shutdown = ctl_backend_ramdisk_lun_shutdown;1244cbe_lun->be = &ctl_be_ramdisk_driver;1245if ((params->flags & CTL_LUN_FLAG_SERIAL_NUM) == 0) {1246snprintf(tmpstr, sizeof(tmpstr), "MYSERIAL%04d",1247softc->num_luns);1248strncpy((char *)cbe_lun->serial_num, tmpstr,1249MIN(sizeof(cbe_lun->serial_num), sizeof(tmpstr)));12501251/* Tell the user what we used for a serial number */1252strncpy((char *)params->serial_num, tmpstr,1253MIN(sizeof(params->serial_num), sizeof(tmpstr)));1254} else {1255strncpy((char *)cbe_lun->serial_num, params->serial_num,1256MIN(sizeof(cbe_lun->serial_num),1257sizeof(params->serial_num)));1258}1259if ((params->flags & CTL_LUN_FLAG_DEVID) == 0) {1260snprintf(tmpstr, sizeof(tmpstr), "MYDEVID%04d", softc->num_luns);1261strncpy((char *)cbe_lun->device_id, tmpstr,1262MIN(sizeof(cbe_lun->device_id), sizeof(tmpstr)));12631264/* Tell the user what we used for a device ID */1265strncpy((char *)params->device_id, tmpstr,1266MIN(sizeof(params->device_id), sizeof(tmpstr)));1267} else {1268strncpy((char *)cbe_lun->device_id, params->device_id,1269MIN(sizeof(cbe_lun->device_id),1270sizeof(params->device_id)));1271}12721273STAILQ_INIT(&be_lun->cont_queue);1274sx_init(&be_lun->page_lock, "ctlram page");1275if (be_lun->cap_bytes == 0) {1276be_lun->indir = 0;1277be_lun->pages = malloc(be_lun->pblocksize, M_RAMDISK, M_WAITOK);1278}1279be_lun->zero_page = malloc(be_lun->pblocksize, M_RAMDISK,1280M_WAITOK|M_ZERO);1281mtx_init(&be_lun->queue_lock, "ctlram queue", NULL, MTX_DEF);1282TASK_INIT(&be_lun->io_task, /*priority*/0, ctl_backend_ramdisk_worker,1283be_lun);12841285be_lun->io_taskqueue = taskqueue_create("ctlramtq", M_WAITOK,1286taskqueue_thread_enqueue, /*context*/&be_lun->io_taskqueue);1287if (be_lun->io_taskqueue == NULL) {1288snprintf(req->error_str, sizeof(req->error_str),1289"%s: Unable to create taskqueue", __func__);1290goto bailout_error;1291}12921293retval = taskqueue_start_threads_in_proc(&be_lun->io_taskqueue,1294/*num threads*/1,1295/*priority*/PUSER,1296/*proc*/control_softc->ctl_proc,1297/*thread name*/"ramdisk");1298if (retval != 0)1299goto bailout_error;13001301retval = ctl_add_lun(&be_lun->cbe_lun);1302if (retval != 0) {1303snprintf(req->error_str, sizeof(req->error_str),1304"%s: ctl_add_lun() returned error %d, see dmesg for "1305"details", __func__, retval);1306retval = 0;1307goto bailout_error;1308}13091310mtx_lock(&softc->lock);1311softc->num_luns++;1312SLIST_INSERT_HEAD(&softc->lun_list, be_lun, links);1313mtx_unlock(&softc->lock);13141315params->req_lun_id = cbe_lun->lun_id;13161317req->status = CTL_LUN_OK;1318return (retval);13191320bailout_error:1321req->status = CTL_LUN_ERROR;1322if (be_lun != NULL) {1323if (be_lun->io_taskqueue != NULL)1324taskqueue_free(be_lun->io_taskqueue);1325nvlist_destroy(cbe_lun->options);1326free(be_lun->zero_page, M_RAMDISK);1327ctl_backend_ramdisk_freeallpages(be_lun->pages, be_lun->indir);1328sx_destroy(&be_lun->page_lock);1329mtx_destroy(&be_lun->queue_lock);1330free(be_lun, M_RAMDISK);1331}1332return (retval);1333}13341335static int1336ctl_backend_ramdisk_modify(struct ctl_be_ramdisk_softc *softc,1337struct ctl_lun_req *req)1338{1339struct ctl_be_ramdisk_lun *be_lun;1340struct ctl_be_lun *cbe_lun;1341struct ctl_lun_modify_params *params;1342const char *value;1343uint32_t blocksize;1344int wasprim;13451346params = &req->reqdata.modify;1347sx_xlock(&softc->modify_lock);1348mtx_lock(&softc->lock);1349SLIST_FOREACH(be_lun, &softc->lun_list, links) {1350if (be_lun->cbe_lun.lun_id == params->lun_id)1351break;1352}1353mtx_unlock(&softc->lock);1354if (be_lun == NULL) {1355snprintf(req->error_str, sizeof(req->error_str),1356"%s: LUN %u is not managed by the ramdisk backend",1357__func__, params->lun_id);1358goto bailout_error;1359}1360cbe_lun = &be_lun->cbe_lun;13611362if (params->lun_size_bytes != 0)1363be_lun->params.lun_size_bytes = params->lun_size_bytes;13641365if (req->args_nvl != NULL) {1366nvlist_destroy(cbe_lun->options);1367cbe_lun->options = nvlist_clone(req->args_nvl);1368}13691370wasprim = (cbe_lun->flags & CTL_LUN_FLAG_PRIMARY);1371value = dnvlist_get_string(cbe_lun->options, "ha_role", NULL);1372if (value != NULL) {1373if (strcmp(value, "primary") == 0)1374cbe_lun->flags |= CTL_LUN_FLAG_PRIMARY;1375else1376cbe_lun->flags &= ~CTL_LUN_FLAG_PRIMARY;1377} else if (control_softc->flags & CTL_FLAG_ACTIVE_SHELF)1378cbe_lun->flags |= CTL_LUN_FLAG_PRIMARY;1379else1380cbe_lun->flags &= ~CTL_LUN_FLAG_PRIMARY;1381if (wasprim != (cbe_lun->flags & CTL_LUN_FLAG_PRIMARY)) {1382if (cbe_lun->flags & CTL_LUN_FLAG_PRIMARY)1383ctl_lun_primary(cbe_lun);1384else1385ctl_lun_secondary(cbe_lun);1386}13871388blocksize = be_lun->cbe_lun.blocksize;1389if (be_lun->params.lun_size_bytes < blocksize) {1390snprintf(req->error_str, sizeof(req->error_str),1391"%s: LUN size %ju < blocksize %u", __func__,1392be_lun->params.lun_size_bytes, blocksize);1393goto bailout_error;1394}1395be_lun->size_blocks = be_lun->params.lun_size_bytes / blocksize;1396be_lun->size_bytes = be_lun->size_blocks * blocksize;1397be_lun->cbe_lun.maxlba = be_lun->size_blocks - 1;1398ctl_lun_capacity_changed(&be_lun->cbe_lun);13991400/* Tell the user the exact size we ended up using */1401params->lun_size_bytes = be_lun->size_bytes;14021403sx_xunlock(&softc->modify_lock);1404req->status = CTL_LUN_OK;1405return (0);14061407bailout_error:1408sx_xunlock(&softc->modify_lock);1409req->status = CTL_LUN_ERROR;1410return (0);1411}14121413static void1414ctl_backend_ramdisk_lun_shutdown(struct ctl_be_lun *cbe_lun)1415{1416struct ctl_be_ramdisk_lun *be_lun = (struct ctl_be_ramdisk_lun *)cbe_lun;1417struct ctl_be_ramdisk_softc *softc = be_lun->softc;14181419taskqueue_drain_all(be_lun->io_taskqueue);1420taskqueue_free(be_lun->io_taskqueue);1421nvlist_destroy(be_lun->cbe_lun.options);1422free(be_lun->zero_page, M_RAMDISK);1423ctl_backend_ramdisk_freeallpages(be_lun->pages, be_lun->indir);1424sx_destroy(&be_lun->page_lock);1425mtx_destroy(&be_lun->queue_lock);14261427mtx_lock(&softc->lock);1428be_lun->flags |= CTL_BE_RAMDISK_LUN_UNCONFIGURED;1429if (be_lun->flags & CTL_BE_RAMDISK_LUN_WAITING)1430wakeup(be_lun);1431else1432free(be_lun, M_RAMDISK);1433mtx_unlock(&softc->lock);1434}143514361437