1 /* $NetBSD: ld_nvme.c,v 1.1 2016/05/01 10:21:02 nonaka Exp $ */ 2 3 /*- 4 * Copyright (C) 2016 NONAKA Kimihiro <nonaka@netbsd.org> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include <sys/cdefs.h> 29 __KERNEL_RCSID(0, "$NetBSD: ld_nvme.c,v 1.1 2016/05/01 10:21:02 nonaka Exp $"); 30 31 #include <sys/param.h> 32 #include <sys/systm.h> 33 #include <sys/kernel.h> 34 #include <sys/device.h> 35 #include <sys/buf.h> 36 #include <sys/disk.h> 37 #include <sys/kmem.h> 38 39 #include <dev/ldvar.h> 40 #include <dev/ic/nvmereg.h> 41 #include <dev/ic/nvmevar.h> 42 43 struct ld_nvme_softc { 44 struct ld_softc sc_ld; 45 struct nvme_softc *sc_nvme; 46 47 uint16_t sc_nsid; 48 }; 49 50 static int ld_nvme_match(device_t, cfdata_t, void *); 51 static void ld_nvme_attach(device_t, device_t, void *); 52 static int ld_nvme_detach(device_t, int); 53 54 CFATTACH_DECL_NEW(ld_nvme, sizeof(struct ld_nvme_softc), 55 ld_nvme_match, ld_nvme_attach, ld_nvme_detach, NULL); 56 57 static int ld_nvme_start(struct ld_softc *, struct buf *); 58 static int ld_nvme_dump(struct ld_softc *, void *, int, int); 59 static int ld_nvme_flush(struct ld_softc *, int); 60 61 static int ld_nvme_dobio(struct ld_nvme_softc *, void *, int, daddr_t, 62 int, struct buf *); 63 static void ld_nvme_biodone(struct nvme_ns_context *); 64 static void ld_nvme_syncdone(struct nvme_ns_context *); 65 66 67 static int 68 ld_nvme_match(device_t parent, cfdata_t match, void *aux) 69 { 70 struct nvme_attach_args *naa = aux; 71 72 if (naa->naa_nsid == 0) 73 return 0; 74 75 return 1; 76 } 77 78 static void 79 ld_nvme_attach(device_t parent, device_t self, void *aux) 80 { 81 struct ld_nvme_softc *sc = device_private(self); 82 struct ld_softc *ld = &sc->sc_ld; 83 struct nvme_softc *nsc = device_private(parent); 84 struct nvme_attach_args *naa = aux; 85 struct nvme_namespace *ns; 86 struct nvm_namespace_format *f; 87 uint64_t nsze; 88 int error; 89 90 ld->sc_dv = self; 91 sc->sc_nvme = nsc; 92 sc->sc_nsid = naa->naa_nsid; 93 94 aprint_naive("\n"); 95 aprint_normal("\n"); 96 97 error = nvme_ns_identify(sc->sc_nvme, sc->sc_nsid); 98 if (error) { 99 aprint_error_dev(self, "couldn't identify namespace\n"); 100 return; 101 } 102 103 ns = nvme_ns_get(sc->sc_nvme, sc->sc_nsid); 104 KASSERT(ns); 105 nsze = lemtoh64(&ns->ident->nsze); 106 f = &ns->ident->lbaf[NVME_ID_NS_FLBAS(ns->ident->flbas)]; 107 108 ld->sc_secsize = 1 << f->lbads; 109 ld->sc_secperunit = nsze; 110 ld->sc_maxxfer = MAXPHYS; 111 ld->sc_maxqueuecnt = naa->naa_qentries; 112 ld->sc_start = ld_nvme_start; 113 ld->sc_dump = ld_nvme_dump; 114 ld->sc_flush = ld_nvme_flush; 115 ld->sc_flags = LDF_ENABLED; 116 ldattach(ld); 117 } 118 119 static int 120 ld_nvme_detach(device_t self, int flags) 121 { 122 struct ld_nvme_softc *sc = device_private(self); 123 struct ld_softc *ld = &sc->sc_ld; 124 int rv; 125 126 if ((rv = ldbegindetach(ld, flags)) != 0) 127 return rv; 128 ldenddetach(ld); 129 130 nvme_ns_free(sc->sc_nvme, sc->sc_nsid); 131 132 return 0; 133 } 134 135 static int 136 ld_nvme_start(struct ld_softc *ld, struct buf *bp) 137 { 138 struct ld_nvme_softc *sc = device_private(ld->sc_dv); 139 140 return ld_nvme_dobio(sc, bp->b_data, bp->b_bcount, bp->b_rawblkno, 141 BUF_ISWRITE(bp), bp); 142 } 143 144 static int 145 ld_nvme_dump(struct ld_softc *ld, void *data, int blkno, int blkcnt) 146 { 147 struct ld_nvme_softc *sc = device_private(ld->sc_dv); 148 149 return ld_nvme_dobio(sc, data, blkcnt * ld->sc_secsize, blkno, 1, NULL); 150 } 151 152 static int 153 ld_nvme_dobio(struct ld_nvme_softc *sc, void *data, int datasize, daddr_t blkno, 154 int dowrite, struct buf *bp) 155 { 156 struct nvme_ns_context *ctx; 157 int error; 158 int s; 159 160 ctx = nvme_ns_get_ctx(bp != NULL ? PR_WAITOK : PR_NOWAIT); 161 ctx->nnc_cookie = sc; 162 ctx->nnc_nsid = sc->sc_nsid; 163 ctx->nnc_done = ld_nvme_biodone; 164 ctx->nnc_buf = bp; 165 ctx->nnc_data = data; 166 ctx->nnc_datasize = datasize; 167 ctx->nnc_secsize = sc->sc_ld.sc_secsize; 168 ctx->nnc_blkno = blkno; 169 ctx->nnc_flags = dowrite ? 0 : NVME_NS_CTX_F_READ; 170 if (bp == NULL) { 171 SET(ctx->nnc_flags, NVME_NS_CTX_F_POLL); 172 s = splbio(); 173 } 174 error = nvme_ns_dobio(sc->sc_nvme, ctx); 175 if (bp == NULL) { 176 splx(s); 177 } 178 179 return error; 180 } 181 182 static void 183 ld_nvme_biodone(struct nvme_ns_context *ctx) 184 { 185 struct ld_nvme_softc *sc = ctx->nnc_cookie; 186 struct buf *bp = ctx->nnc_buf; 187 int status = NVME_CQE_SC(ctx->nnc_status); 188 189 if (bp != NULL) { 190 if (status != NVME_CQE_SC_SUCCESS) { 191 bp->b_error = EIO; 192 bp->b_resid = bp->b_bcount; 193 aprint_error_dev(sc->sc_ld.sc_dv, "I/O error\n"); 194 } else { 195 bp->b_resid = 0; 196 } 197 lddone(&sc->sc_ld, bp); 198 } else { 199 if (status != NVME_CQE_SC_SUCCESS) { 200 aprint_error_dev(sc->sc_ld.sc_dv, "I/O error\n"); 201 } 202 } 203 nvme_ns_put_ctx(ctx); 204 } 205 206 static int 207 ld_nvme_flush(struct ld_softc *ld, int flags) 208 { 209 struct ld_nvme_softc *sc = device_private(ld->sc_dv); 210 struct nvme_ns_context *ctx; 211 int error; 212 int s; 213 214 ctx = nvme_ns_get_ctx((flags & LDFL_POLL) ? PR_NOWAIT : PR_WAITOK); 215 ctx->nnc_cookie = sc; 216 ctx->nnc_nsid = sc->sc_nsid; 217 ctx->nnc_done = ld_nvme_syncdone; 218 ctx->nnc_flags = 0; 219 if (flags & LDFL_POLL) { 220 SET(ctx->nnc_flags, NVME_NS_CTX_F_POLL); 221 s = splbio(); 222 } 223 error = nvme_ns_sync(sc->sc_nvme, ctx); 224 if (flags & LDFL_POLL) { 225 splx(s); 226 } 227 228 return error; 229 } 230 231 static void 232 ld_nvme_syncdone(struct nvme_ns_context *ctx) 233 { 234 235 nvme_ns_put_ctx(ctx); 236 } 237