1*bb0ec6b3SJim Harris /*- 2*bb0ec6b3SJim Harris * Copyright (C) 2012 Intel Corporation 3*bb0ec6b3SJim Harris * All rights reserved. 4*bb0ec6b3SJim Harris * 5*bb0ec6b3SJim Harris * Redistribution and use in source and binary forms, with or without 6*bb0ec6b3SJim Harris * modification, are permitted provided that the following conditions 7*bb0ec6b3SJim Harris * are met: 8*bb0ec6b3SJim Harris * 1. Redistributions of source code must retain the above copyright 9*bb0ec6b3SJim Harris * notice, this list of conditions and the following disclaimer. 10*bb0ec6b3SJim Harris * 2. Redistributions in binary form must reproduce the above copyright 11*bb0ec6b3SJim Harris * notice, this list of conditions and the following disclaimer in the 12*bb0ec6b3SJim Harris * documentation and/or other materials provided with the distribution. 13*bb0ec6b3SJim Harris * 14*bb0ec6b3SJim Harris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15*bb0ec6b3SJim Harris * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16*bb0ec6b3SJim Harris * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17*bb0ec6b3SJim Harris * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18*bb0ec6b3SJim Harris * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19*bb0ec6b3SJim Harris * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20*bb0ec6b3SJim Harris * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21*bb0ec6b3SJim Harris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22*bb0ec6b3SJim Harris * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23*bb0ec6b3SJim Harris * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24*bb0ec6b3SJim Harris * SUCH DAMAGE. 25*bb0ec6b3SJim Harris */ 26*bb0ec6b3SJim Harris 27*bb0ec6b3SJim Harris #include <sys/cdefs.h> 28*bb0ec6b3SJim Harris __FBSDID("$FreeBSD$"); 29*bb0ec6b3SJim Harris 30*bb0ec6b3SJim Harris #include <sys/param.h> 31*bb0ec6b3SJim Harris #include <sys/bio.h> 32*bb0ec6b3SJim Harris #include <sys/kernel.h> 33*bb0ec6b3SJim Harris #include <sys/malloc.h> 34*bb0ec6b3SJim Harris #include <sys/module.h> 35*bb0ec6b3SJim Harris #include <sys/systm.h> 36*bb0ec6b3SJim Harris #include <sys/taskqueue.h> 37*bb0ec6b3SJim Harris 38*bb0ec6b3SJim Harris #include <geom/geom.h> 39*bb0ec6b3SJim Harris #include <geom/geom_disk.h> 40*bb0ec6b3SJim Harris 41*bb0ec6b3SJim Harris #include <dev/nvme/nvme.h> 42*bb0ec6b3SJim Harris 43*bb0ec6b3SJim Harris struct nvd_disk; 44*bb0ec6b3SJim Harris 45*bb0ec6b3SJim Harris static disk_ioctl_t nvd_ioctl; 46*bb0ec6b3SJim Harris static disk_strategy_t nvd_strategy; 47*bb0ec6b3SJim Harris 48*bb0ec6b3SJim Harris static void create_geom_disk(void *, struct nvme_namespace *ns); 49*bb0ec6b3SJim Harris static void destroy_geom_disk(struct nvd_disk *ndisk); 50*bb0ec6b3SJim Harris 51*bb0ec6b3SJim Harris static int nvd_load(void); 52*bb0ec6b3SJim Harris static void nvd_unload(void); 53*bb0ec6b3SJim Harris 54*bb0ec6b3SJim Harris MALLOC_DEFINE(M_NVD, "nvd", "nvd(4) allocations"); 55*bb0ec6b3SJim Harris 56*bb0ec6b3SJim Harris struct nvme_consumer *consumer_handle; 57*bb0ec6b3SJim Harris 58*bb0ec6b3SJim Harris struct nvd_disk { 59*bb0ec6b3SJim Harris 60*bb0ec6b3SJim Harris struct bio_queue_head bioq; 61*bb0ec6b3SJim Harris struct task bioqtask; 62*bb0ec6b3SJim Harris struct mtx bioqlock; 63*bb0ec6b3SJim Harris 64*bb0ec6b3SJim Harris struct disk *disk; 65*bb0ec6b3SJim Harris struct taskqueue *tq; 66*bb0ec6b3SJim Harris struct nvme_namespace *ns; 67*bb0ec6b3SJim Harris 68*bb0ec6b3SJim Harris uint32_t cur_depth; 69*bb0ec6b3SJim Harris 70*bb0ec6b3SJim Harris TAILQ_ENTRY(nvd_disk) tailq; 71*bb0ec6b3SJim Harris }; 72*bb0ec6b3SJim Harris 73*bb0ec6b3SJim Harris TAILQ_HEAD(, nvd_disk) nvd_head; 74*bb0ec6b3SJim Harris 75*bb0ec6b3SJim Harris static int nvd_modevent(module_t mod, int type, void *arg) 76*bb0ec6b3SJim Harris { 77*bb0ec6b3SJim Harris int error = 0; 78*bb0ec6b3SJim Harris 79*bb0ec6b3SJim Harris switch (type) { 80*bb0ec6b3SJim Harris case MOD_LOAD: 81*bb0ec6b3SJim Harris error = nvd_load(); 82*bb0ec6b3SJim Harris break; 83*bb0ec6b3SJim Harris case MOD_UNLOAD: 84*bb0ec6b3SJim Harris nvd_unload(); 85*bb0ec6b3SJim Harris break; 86*bb0ec6b3SJim Harris default: 87*bb0ec6b3SJim Harris break; 88*bb0ec6b3SJim Harris } 89*bb0ec6b3SJim Harris 90*bb0ec6b3SJim Harris return (error); 91*bb0ec6b3SJim Harris } 92*bb0ec6b3SJim Harris 93*bb0ec6b3SJim Harris moduledata_t nvd_mod = { 94*bb0ec6b3SJim Harris "nvd", 95*bb0ec6b3SJim Harris (modeventhand_t)nvd_modevent, 96*bb0ec6b3SJim Harris 0 97*bb0ec6b3SJim Harris }; 98*bb0ec6b3SJim Harris 99*bb0ec6b3SJim Harris DECLARE_MODULE(nvd, nvd_mod, SI_SUB_DRIVERS, SI_ORDER_ANY); 100*bb0ec6b3SJim Harris MODULE_VERSION(nvd, 1); 101*bb0ec6b3SJim Harris MODULE_DEPEND(nvd, nvme, 1, 1, 1); 102*bb0ec6b3SJim Harris 103*bb0ec6b3SJim Harris static int 104*bb0ec6b3SJim Harris nvd_load() 105*bb0ec6b3SJim Harris { 106*bb0ec6b3SJim Harris 107*bb0ec6b3SJim Harris TAILQ_INIT(&nvd_head); 108*bb0ec6b3SJim Harris consumer_handle = nvme_register_consumer(create_geom_disk, NULL); 109*bb0ec6b3SJim Harris 110*bb0ec6b3SJim Harris return (consumer_handle != NULL ? 0 : -1); 111*bb0ec6b3SJim Harris } 112*bb0ec6b3SJim Harris 113*bb0ec6b3SJim Harris static void 114*bb0ec6b3SJim Harris nvd_unload() 115*bb0ec6b3SJim Harris { 116*bb0ec6b3SJim Harris struct nvd_disk *nvd; 117*bb0ec6b3SJim Harris 118*bb0ec6b3SJim Harris while (!TAILQ_EMPTY(&nvd_head)) { 119*bb0ec6b3SJim Harris nvd = TAILQ_FIRST(&nvd_head); 120*bb0ec6b3SJim Harris TAILQ_REMOVE(&nvd_head, nvd, tailq); 121*bb0ec6b3SJim Harris destroy_geom_disk(nvd); 122*bb0ec6b3SJim Harris free(nvd, M_NVD); 123*bb0ec6b3SJim Harris } 124*bb0ec6b3SJim Harris 125*bb0ec6b3SJim Harris nvme_unregister_consumer(consumer_handle); 126*bb0ec6b3SJim Harris } 127*bb0ec6b3SJim Harris 128*bb0ec6b3SJim Harris static void 129*bb0ec6b3SJim Harris nvd_strategy(struct bio *bp) 130*bb0ec6b3SJim Harris { 131*bb0ec6b3SJim Harris struct nvd_disk *ndisk; 132*bb0ec6b3SJim Harris 133*bb0ec6b3SJim Harris ndisk = (struct nvd_disk *)bp->bio_disk->d_drv1; 134*bb0ec6b3SJim Harris 135*bb0ec6b3SJim Harris mtx_lock(&ndisk->bioqlock); 136*bb0ec6b3SJim Harris bioq_insert_tail(&ndisk->bioq, bp); 137*bb0ec6b3SJim Harris mtx_unlock(&ndisk->bioqlock); 138*bb0ec6b3SJim Harris taskqueue_enqueue(ndisk->tq, &ndisk->bioqtask); 139*bb0ec6b3SJim Harris } 140*bb0ec6b3SJim Harris 141*bb0ec6b3SJim Harris static int 142*bb0ec6b3SJim Harris nvd_ioctl(struct disk *ndisk, u_long cmd, void *data, int fflag, 143*bb0ec6b3SJim Harris struct thread *td) 144*bb0ec6b3SJim Harris { 145*bb0ec6b3SJim Harris int ret = 0; 146*bb0ec6b3SJim Harris 147*bb0ec6b3SJim Harris switch (cmd) { 148*bb0ec6b3SJim Harris default: 149*bb0ec6b3SJim Harris ret = EIO; 150*bb0ec6b3SJim Harris } 151*bb0ec6b3SJim Harris 152*bb0ec6b3SJim Harris return (ret); 153*bb0ec6b3SJim Harris } 154*bb0ec6b3SJim Harris 155*bb0ec6b3SJim Harris static void 156*bb0ec6b3SJim Harris nvd_done(void *arg, const struct nvme_completion *status) 157*bb0ec6b3SJim Harris { 158*bb0ec6b3SJim Harris struct bio *bp; 159*bb0ec6b3SJim Harris struct nvd_disk *ndisk; 160*bb0ec6b3SJim Harris 161*bb0ec6b3SJim Harris bp = (struct bio *)arg; 162*bb0ec6b3SJim Harris 163*bb0ec6b3SJim Harris ndisk = bp->bio_disk->d_drv1; 164*bb0ec6b3SJim Harris 165*bb0ec6b3SJim Harris if (atomic_fetchadd_int(&ndisk->cur_depth, -1) == NVME_QD) 166*bb0ec6b3SJim Harris taskqueue_enqueue(ndisk->tq, &ndisk->bioqtask); 167*bb0ec6b3SJim Harris 168*bb0ec6b3SJim Harris /* 169*bb0ec6b3SJim Harris * TODO: add more extensive translation of NVMe status codes 170*bb0ec6b3SJim Harris * to different bio error codes (i.e. EIO, EINVAL, etc.) 171*bb0ec6b3SJim Harris */ 172*bb0ec6b3SJim Harris if (status->sf_sc || status->sf_sct) { 173*bb0ec6b3SJim Harris bp->bio_error = EIO; 174*bb0ec6b3SJim Harris bp->bio_flags |= BIO_ERROR; 175*bb0ec6b3SJim Harris bp->bio_resid = bp->bio_bcount; 176*bb0ec6b3SJim Harris } else 177*bb0ec6b3SJim Harris bp->bio_resid = 0; 178*bb0ec6b3SJim Harris 179*bb0ec6b3SJim Harris biodone(bp); 180*bb0ec6b3SJim Harris } 181*bb0ec6b3SJim Harris 182*bb0ec6b3SJim Harris static void 183*bb0ec6b3SJim Harris nvd_bioq_process(void *arg, int pending) 184*bb0ec6b3SJim Harris { 185*bb0ec6b3SJim Harris struct nvd_disk *ndisk = arg; 186*bb0ec6b3SJim Harris struct bio *bp; 187*bb0ec6b3SJim Harris int err; 188*bb0ec6b3SJim Harris 189*bb0ec6b3SJim Harris for (;;) { 190*bb0ec6b3SJim Harris if (atomic_load_acq_int(&ndisk->cur_depth) >= NVME_QD) 191*bb0ec6b3SJim Harris break; 192*bb0ec6b3SJim Harris 193*bb0ec6b3SJim Harris mtx_lock(&ndisk->bioqlock); 194*bb0ec6b3SJim Harris bp = bioq_takefirst(&ndisk->bioq); 195*bb0ec6b3SJim Harris mtx_unlock(&ndisk->bioqlock); 196*bb0ec6b3SJim Harris if (bp == NULL) 197*bb0ec6b3SJim Harris break; 198*bb0ec6b3SJim Harris 199*bb0ec6b3SJim Harris #ifdef BIO_ORDERED 200*bb0ec6b3SJim Harris /* 201*bb0ec6b3SJim Harris * BIO_ORDERED flag dictates that all outstanding bios 202*bb0ec6b3SJim Harris * must be completed before processing the bio with 203*bb0ec6b3SJim Harris * BIO_ORDERED flag set. 204*bb0ec6b3SJim Harris */ 205*bb0ec6b3SJim Harris if (bp->bio_flags & BIO_ORDERED) { 206*bb0ec6b3SJim Harris while (ndisk->cur_depth > 0) { 207*bb0ec6b3SJim Harris pause("nvd flush", 1); 208*bb0ec6b3SJim Harris } 209*bb0ec6b3SJim Harris } 210*bb0ec6b3SJim Harris #endif 211*bb0ec6b3SJim Harris 212*bb0ec6b3SJim Harris bp->bio_driver1 = NULL; 213*bb0ec6b3SJim Harris atomic_add_acq_int(&ndisk->cur_depth, 1); 214*bb0ec6b3SJim Harris 215*bb0ec6b3SJim Harris err = nvme_ns_bio_process(ndisk->ns, bp, nvd_done); 216*bb0ec6b3SJim Harris 217*bb0ec6b3SJim Harris if (err) { 218*bb0ec6b3SJim Harris atomic_add_acq_int(&ndisk->cur_depth, -1); 219*bb0ec6b3SJim Harris bp->bio_error = EIO; 220*bb0ec6b3SJim Harris bp->bio_flags |= BIO_ERROR; 221*bb0ec6b3SJim Harris bp->bio_resid = bp->bio_bcount; 222*bb0ec6b3SJim Harris biodone(bp); 223*bb0ec6b3SJim Harris } 224*bb0ec6b3SJim Harris 225*bb0ec6b3SJim Harris #ifdef BIO_ORDERED 226*bb0ec6b3SJim Harris /* 227*bb0ec6b3SJim Harris * BIO_ORDERED flag dictates that the bio with BIO_ORDERED 228*bb0ec6b3SJim Harris * flag set must be completed before proceeding with 229*bb0ec6b3SJim Harris * additional bios. 230*bb0ec6b3SJim Harris */ 231*bb0ec6b3SJim Harris if (bp->bio_flags & BIO_ORDERED) { 232*bb0ec6b3SJim Harris while (ndisk->cur_depth > 0) { 233*bb0ec6b3SJim Harris pause("nvd flush", 1); 234*bb0ec6b3SJim Harris } 235*bb0ec6b3SJim Harris } 236*bb0ec6b3SJim Harris #endif 237*bb0ec6b3SJim Harris } 238*bb0ec6b3SJim Harris } 239*bb0ec6b3SJim Harris 240*bb0ec6b3SJim Harris static void 241*bb0ec6b3SJim Harris create_geom_disk(void *arg, struct nvme_namespace *ns) 242*bb0ec6b3SJim Harris { 243*bb0ec6b3SJim Harris struct nvd_disk *ndisk; 244*bb0ec6b3SJim Harris struct disk *disk; 245*bb0ec6b3SJim Harris 246*bb0ec6b3SJim Harris ndisk = malloc(sizeof(struct nvd_disk), M_NVD, M_ZERO | M_NOWAIT); 247*bb0ec6b3SJim Harris 248*bb0ec6b3SJim Harris disk = disk_alloc(); 249*bb0ec6b3SJim Harris disk->d_strategy = nvd_strategy; 250*bb0ec6b3SJim Harris disk->d_ioctl = nvd_ioctl; 251*bb0ec6b3SJim Harris disk->d_name = "nvd"; 252*bb0ec6b3SJim Harris disk->d_drv1 = ndisk; 253*bb0ec6b3SJim Harris 254*bb0ec6b3SJim Harris disk->d_maxsize = nvme_ns_get_max_io_xfer_size(ns); 255*bb0ec6b3SJim Harris disk->d_sectorsize = nvme_ns_get_sector_size(ns); 256*bb0ec6b3SJim Harris disk->d_mediasize = (off_t)nvme_ns_get_size(ns); 257*bb0ec6b3SJim Harris 258*bb0ec6b3SJim Harris if (TAILQ_EMPTY(&nvd_head)) 259*bb0ec6b3SJim Harris disk->d_unit = 0; 260*bb0ec6b3SJim Harris else 261*bb0ec6b3SJim Harris disk->d_unit = TAILQ_FIRST(&nvd_head)->disk->d_unit + 1; 262*bb0ec6b3SJim Harris 263*bb0ec6b3SJim Harris disk->d_flags = 0; 264*bb0ec6b3SJim Harris 265*bb0ec6b3SJim Harris if (nvme_ns_get_flags(ns) & NVME_NS_DEALLOCATE_SUPPORTED) 266*bb0ec6b3SJim Harris disk->d_flags |= DISKFLAG_CANDELETE; 267*bb0ec6b3SJim Harris 268*bb0ec6b3SJim Harris if (nvme_ns_get_flags(ns) & NVME_NS_FLUSH_SUPPORTED) 269*bb0ec6b3SJim Harris disk->d_flags |= DISKFLAG_CANFLUSHCACHE; 270*bb0ec6b3SJim Harris 271*bb0ec6b3SJim Harris strlcpy(disk->d_ident, nvme_ns_get_serial_number(ns), 272*bb0ec6b3SJim Harris sizeof(disk->d_ident)); 273*bb0ec6b3SJim Harris 274*bb0ec6b3SJim Harris #if __FreeBSD_version >= 900034 275*bb0ec6b3SJim Harris strlcpy(disk->d_descr, nvme_ns_get_model_number(ns), 276*bb0ec6b3SJim Harris sizeof(disk->d_descr)); 277*bb0ec6b3SJim Harris #endif 278*bb0ec6b3SJim Harris 279*bb0ec6b3SJim Harris disk_create(disk, DISK_VERSION); 280*bb0ec6b3SJim Harris 281*bb0ec6b3SJim Harris ndisk->ns = ns; 282*bb0ec6b3SJim Harris ndisk->disk = disk; 283*bb0ec6b3SJim Harris ndisk->cur_depth = 0; 284*bb0ec6b3SJim Harris 285*bb0ec6b3SJim Harris mtx_init(&ndisk->bioqlock, "NVD bioq lock", NULL, MTX_DEF); 286*bb0ec6b3SJim Harris bioq_init(&ndisk->bioq); 287*bb0ec6b3SJim Harris 288*bb0ec6b3SJim Harris TASK_INIT(&ndisk->bioqtask, 0, nvd_bioq_process, ndisk); 289*bb0ec6b3SJim Harris ndisk->tq = taskqueue_create("nvd_taskq", M_WAITOK, 290*bb0ec6b3SJim Harris taskqueue_thread_enqueue, &ndisk->tq); 291*bb0ec6b3SJim Harris taskqueue_start_threads(&ndisk->tq, 1, PI_DISK, "nvd taskq"); 292*bb0ec6b3SJim Harris 293*bb0ec6b3SJim Harris TAILQ_INSERT_HEAD(&nvd_head, ndisk, tailq); 294*bb0ec6b3SJim Harris } 295*bb0ec6b3SJim Harris 296*bb0ec6b3SJim Harris static void 297*bb0ec6b3SJim Harris destroy_geom_disk(struct nvd_disk *ndisk) 298*bb0ec6b3SJim Harris { 299*bb0ec6b3SJim Harris struct bio *bp; 300*bb0ec6b3SJim Harris 301*bb0ec6b3SJim Harris taskqueue_free(ndisk->tq); 302*bb0ec6b3SJim Harris disk_destroy(ndisk->disk); 303*bb0ec6b3SJim Harris 304*bb0ec6b3SJim Harris mtx_lock(&ndisk->bioqlock); 305*bb0ec6b3SJim Harris for (;;) { 306*bb0ec6b3SJim Harris bp = bioq_takefirst(&ndisk->bioq); 307*bb0ec6b3SJim Harris if (bp == NULL) 308*bb0ec6b3SJim Harris break; 309*bb0ec6b3SJim Harris bp->bio_error = EIO; 310*bb0ec6b3SJim Harris bp->bio_flags |= BIO_ERROR; 311*bb0ec6b3SJim Harris bp->bio_resid = bp->bio_bcount; 312*bb0ec6b3SJim Harris 313*bb0ec6b3SJim Harris biodone(bp); 314*bb0ec6b3SJim Harris } 315*bb0ec6b3SJim Harris mtx_unlock(&ndisk->bioqlock); 316*bb0ec6b3SJim Harris 317*bb0ec6b3SJim Harris mtx_destroy(&ndisk->bioqlock); 318*bb0ec6b3SJim Harris } 319