1*3ea7aa7bSmlelstv /* $NetBSD: ld_nvme.c,v 1.25 2022/07/30 12:48:17 mlelstv Exp $ */
28b5163f0Snonaka
38b5163f0Snonaka /*-
48b5163f0Snonaka * Copyright (C) 2016 NONAKA Kimihiro <nonaka@netbsd.org>
58b5163f0Snonaka * All rights reserved.
68b5163f0Snonaka *
78b5163f0Snonaka * Redistribution and use in source and binary forms, with or without
88b5163f0Snonaka * modification, are permitted provided that the following conditions
98b5163f0Snonaka * are met:
108b5163f0Snonaka * 1. Redistributions of source code must retain the above copyright
118b5163f0Snonaka * notice, this list of conditions and the following disclaimer.
128b5163f0Snonaka * 2. Redistributions in binary form must reproduce the above copyright
138b5163f0Snonaka * notice, this list of conditions and the following disclaimer in the
148b5163f0Snonaka * documentation and/or other materials provided with the distribution.
158b5163f0Snonaka *
168b5163f0Snonaka * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
178b5163f0Snonaka * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
188b5163f0Snonaka * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
198b5163f0Snonaka * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
208b5163f0Snonaka * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
218b5163f0Snonaka * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
228b5163f0Snonaka * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
238b5163f0Snonaka * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
248b5163f0Snonaka * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
258b5163f0Snonaka * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
268b5163f0Snonaka */
278b5163f0Snonaka
288b5163f0Snonaka #include <sys/cdefs.h>
29*3ea7aa7bSmlelstv __KERNEL_RCSID(0, "$NetBSD: ld_nvme.c,v 1.25 2022/07/30 12:48:17 mlelstv Exp $");
308b5163f0Snonaka
318b5163f0Snonaka #include <sys/param.h>
328b5163f0Snonaka #include <sys/systm.h>
338b5163f0Snonaka #include <sys/kernel.h>
348b5163f0Snonaka #include <sys/device.h>
358b5163f0Snonaka #include <sys/buf.h>
36e90b5bddSjdolecek #include <sys/bufq.h>
378b5163f0Snonaka #include <sys/disk.h>
388b5163f0Snonaka #include <sys/kmem.h>
39916bdfa5Spgoyette #include <sys/module.h>
408b5163f0Snonaka
418b5163f0Snonaka #include <dev/ldvar.h>
428b5163f0Snonaka #include <dev/ic/nvmereg.h>
438b5163f0Snonaka #include <dev/ic/nvmevar.h>
448b5163f0Snonaka
45916bdfa5Spgoyette #include "ioconf.h"
46916bdfa5Spgoyette
478b5163f0Snonaka struct ld_nvme_softc {
488b5163f0Snonaka struct ld_softc sc_ld;
498b5163f0Snonaka struct nvme_softc *sc_nvme;
508b5163f0Snonaka
518b5163f0Snonaka uint16_t sc_nsid;
528b5163f0Snonaka };
538b5163f0Snonaka
548b5163f0Snonaka static int ld_nvme_match(device_t, cfdata_t, void *);
558b5163f0Snonaka static void ld_nvme_attach(device_t, device_t, void *);
568b5163f0Snonaka static int ld_nvme_detach(device_t, int);
578b5163f0Snonaka
588b5163f0Snonaka CFATTACH_DECL_NEW(ld_nvme, sizeof(struct ld_nvme_softc),
598b5163f0Snonaka ld_nvme_match, ld_nvme_attach, ld_nvme_detach, NULL);
608b5163f0Snonaka
618b5163f0Snonaka static int ld_nvme_start(struct ld_softc *, struct buf *);
628b5163f0Snonaka static int ld_nvme_dump(struct ld_softc *, void *, int, int);
63afe4d516Sjdolecek static int ld_nvme_flush(struct ld_softc *, bool);
645df68754Sjdolecek static int ld_nvme_getcache(struct ld_softc *, int *);
65afe4d516Sjdolecek static int ld_nvme_ioctl(struct ld_softc *, u_long, void *, int32_t, bool);
668b5163f0Snonaka
675df68754Sjdolecek static void ld_nvme_biodone(void *, struct buf *, uint16_t, uint32_t);
688b5163f0Snonaka
698b5163f0Snonaka static int
ld_nvme_match(device_t parent,cfdata_t match,void * aux)708b5163f0Snonaka ld_nvme_match(device_t parent, cfdata_t match, void *aux)
718b5163f0Snonaka {
728b5163f0Snonaka struct nvme_attach_args *naa = aux;
738b5163f0Snonaka
748b5163f0Snonaka if (naa->naa_nsid == 0)
758b5163f0Snonaka return 0;
768b5163f0Snonaka
778b5163f0Snonaka return 1;
788b5163f0Snonaka }
798b5163f0Snonaka
808b5163f0Snonaka static void
ld_nvme_attach(device_t parent,device_t self,void * aux)818b5163f0Snonaka ld_nvme_attach(device_t parent, device_t self, void *aux)
828b5163f0Snonaka {
838b5163f0Snonaka struct ld_nvme_softc *sc = device_private(self);
848b5163f0Snonaka struct ld_softc *ld = &sc->sc_ld;
858b5163f0Snonaka struct nvme_softc *nsc = device_private(parent);
868b5163f0Snonaka struct nvme_attach_args *naa = aux;
878b5163f0Snonaka struct nvme_namespace *ns;
888b5163f0Snonaka struct nvm_namespace_format *f;
898b5163f0Snonaka
908b5163f0Snonaka ld->sc_dv = self;
918b5163f0Snonaka sc->sc_nvme = nsc;
928b5163f0Snonaka sc->sc_nsid = naa->naa_nsid;
938b5163f0Snonaka
948b5163f0Snonaka aprint_naive("\n");
958b5163f0Snonaka aprint_normal("\n");
968b5163f0Snonaka
978b5163f0Snonaka ns = nvme_ns_get(sc->sc_nvme, sc->sc_nsid);
988b5163f0Snonaka KASSERT(ns);
998b5163f0Snonaka
1002f7af14dSkardel f = &ns->ident->lbaf[NVME_ID_NS_FLBAS(ns->ident->flbas)];
1012f7af14dSkardel KASSERT(f->lbads >= 9); /* only valid LBS data sizes allowed here */
1025cd8663bSmlelstv
1038b5163f0Snonaka ld->sc_secsize = 1 << f->lbads;
1046ac76f5bSnonaka ld->sc_secperunit = ns->ident->nsze;
105a2a95605Sjdolecek ld->sc_maxxfer = naa->naa_maxphys;
106a2a95605Sjdolecek ld->sc_maxqueuecnt = naa->naa_qentries;
1078b5163f0Snonaka ld->sc_start = ld_nvme_start;
1088b5163f0Snonaka ld->sc_dump = ld_nvme_dump;
109afe4d516Sjdolecek ld->sc_ioctl = ld_nvme_ioctl;
1105b68a5eeSmlelstv ld->sc_flags = LDF_ENABLED | LDF_NO_RND | LDF_MPSAFE;
111583ae9edSmlelstv ld->sc_typename = kmem_asprintf("%s", naa->naa_typename);
11247d93d33Sjdolecek ldattach(ld, "fcfs");
1138b5163f0Snonaka }
1148b5163f0Snonaka
1158b5163f0Snonaka static int
ld_nvme_detach(device_t self,int flags)1168b5163f0Snonaka ld_nvme_detach(device_t self, int flags)
1178b5163f0Snonaka {
1188b5163f0Snonaka struct ld_nvme_softc *sc = device_private(self);
1198b5163f0Snonaka struct ld_softc *ld = &sc->sc_ld;
1208b5163f0Snonaka int rv;
1218b5163f0Snonaka
1228b5163f0Snonaka if ((rv = ldbegindetach(ld, flags)) != 0)
1238b5163f0Snonaka return rv;
1248b5163f0Snonaka ldenddetach(ld);
1258b5163f0Snonaka
126583ae9edSmlelstv kmem_free(ld->sc_typename, strlen(ld->sc_typename) + 1);
127583ae9edSmlelstv
1288b5163f0Snonaka nvme_ns_free(sc->sc_nvme, sc->sc_nsid);
1298b5163f0Snonaka
1308b5163f0Snonaka return 0;
1318b5163f0Snonaka }
1328b5163f0Snonaka
1338b5163f0Snonaka static int
ld_nvme_start(struct ld_softc * ld,struct buf * bp)1348b5163f0Snonaka ld_nvme_start(struct ld_softc *ld, struct buf *bp)
1358b5163f0Snonaka {
1368b5163f0Snonaka struct ld_nvme_softc *sc = device_private(ld->sc_dv);
1376801660cSjdolecek int flags = BUF_ISWRITE(bp) ? 0 : NVME_NS_CTX_F_READ;
1386801660cSjdolecek
1396801660cSjdolecek if (bp->b_flags & B_MEDIA_FUA)
1406801660cSjdolecek flags |= NVME_NS_CTX_F_FUA;
1418b5163f0Snonaka
142ac7944cbSjdolecek return nvme_ns_dobio(sc->sc_nvme, sc->sc_nsid, sc,
143ac7944cbSjdolecek bp, bp->b_data, bp->b_bcount,
144ac7944cbSjdolecek sc->sc_ld.sc_secsize, bp->b_rawblkno,
1456801660cSjdolecek flags,
146ac7944cbSjdolecek ld_nvme_biodone);
1478b5163f0Snonaka }
1488b5163f0Snonaka
1498b5163f0Snonaka static int
ld_nvme_dump(struct ld_softc * ld,void * data,int blkno,int blkcnt)1508b5163f0Snonaka ld_nvme_dump(struct ld_softc *ld, void *data, int blkno, int blkcnt)
1518b5163f0Snonaka {
1528b5163f0Snonaka struct ld_nvme_softc *sc = device_private(ld->sc_dv);
1538b5163f0Snonaka
154ac7944cbSjdolecek return nvme_ns_dobio(sc->sc_nvme, sc->sc_nsid, sc,
155ac7944cbSjdolecek NULL, data, blkcnt * ld->sc_secsize,
156ac7944cbSjdolecek sc->sc_ld.sc_secsize, blkno,
157ac7944cbSjdolecek NVME_NS_CTX_F_POLL,
158ac7944cbSjdolecek ld_nvme_biodone);
1598b5163f0Snonaka }
1608b5163f0Snonaka
1618b5163f0Snonaka static void
ld_nvme_biodone(void * xc,struct buf * bp,uint16_t cmd_status,uint32_t cdw0)1625df68754Sjdolecek ld_nvme_biodone(void *xc, struct buf *bp, uint16_t cmd_status, uint32_t cdw0)
1638b5163f0Snonaka {
164ac7944cbSjdolecek struct ld_nvme_softc *sc = xc;
165ac7944cbSjdolecek uint16_t status = NVME_CQE_SC(cmd_status);
166ef172b9fSjdolecek
1678b5163f0Snonaka if (bp != NULL) {
1688b5163f0Snonaka if (status != NVME_CQE_SC_SUCCESS) {
1698b5163f0Snonaka bp->b_error = EIO;
1708b5163f0Snonaka bp->b_resid = bp->b_bcount;
171*3ea7aa7bSmlelstv device_printf(sc->sc_ld.sc_dv, "I/O error\n");
1728b5163f0Snonaka } else {
1738b5163f0Snonaka bp->b_resid = 0;
1748b5163f0Snonaka }
1758b5163f0Snonaka lddone(&sc->sc_ld, bp);
1768b5163f0Snonaka } else {
1778b5163f0Snonaka if (status != NVME_CQE_SC_SUCCESS) {
178*3ea7aa7bSmlelstv device_printf(sc->sc_ld.sc_dv, "I/O error\n");
1798b5163f0Snonaka }
1808b5163f0Snonaka }
1818b5163f0Snonaka }
1828b5163f0Snonaka
1838b5163f0Snonaka static int
ld_nvme_flush(struct ld_softc * ld,bool poll)184afe4d516Sjdolecek ld_nvme_flush(struct ld_softc *ld, bool poll)
1858b5163f0Snonaka {
1868b5163f0Snonaka struct ld_nvme_softc *sc = device_private(ld->sc_dv);
1878b5163f0Snonaka
1881bfffd71Sjdolecek return nvme_ns_sync(sc->sc_nvme, sc->sc_nsid,
1891bfffd71Sjdolecek poll ? NVME_NS_CTX_F_POLL : 0);
1905df68754Sjdolecek }
1915df68754Sjdolecek
1925df68754Sjdolecek static int
ld_nvme_getcache(struct ld_softc * ld,int * addr)1935df68754Sjdolecek ld_nvme_getcache(struct ld_softc *ld, int *addr)
1945df68754Sjdolecek {
1955df68754Sjdolecek struct ld_nvme_softc *sc = device_private(ld->sc_dv);
1965df68754Sjdolecek
1971bfffd71Sjdolecek return nvme_admin_getcache(sc->sc_nvme, addr);
1985df68754Sjdolecek }
1995df68754Sjdolecek
200afe4d516Sjdolecek static int
ld_nvme_setcache(struct ld_softc * ld,int addr)201b7a0ea19Sjdolecek ld_nvme_setcache(struct ld_softc *ld, int addr)
202b7a0ea19Sjdolecek {
203b7a0ea19Sjdolecek struct ld_nvme_softc *sc = device_private(ld->sc_dv);
204b7a0ea19Sjdolecek
205b7a0ea19Sjdolecek return nvme_admin_setcache(sc->sc_nvme, addr);
206b7a0ea19Sjdolecek }
207b7a0ea19Sjdolecek
208b7a0ea19Sjdolecek static int
ld_nvme_ioctl(struct ld_softc * ld,u_long cmd,void * addr,int32_t flag,bool poll)209afe4d516Sjdolecek ld_nvme_ioctl(struct ld_softc *ld, u_long cmd, void *addr, int32_t flag, bool poll)
210afe4d516Sjdolecek {
211afe4d516Sjdolecek int error;
212afe4d516Sjdolecek
213afe4d516Sjdolecek switch (cmd) {
214afe4d516Sjdolecek case DIOCCACHESYNC:
215afe4d516Sjdolecek error = ld_nvme_flush(ld, poll);
216afe4d516Sjdolecek break;
217afe4d516Sjdolecek
2185df68754Sjdolecek case DIOCGCACHE:
2195df68754Sjdolecek error = ld_nvme_getcache(ld, (int *)addr);
2205df68754Sjdolecek break;
2215df68754Sjdolecek
222b7a0ea19Sjdolecek case DIOCSCACHE:
223b7a0ea19Sjdolecek error = ld_nvme_setcache(ld, *(int *)addr);
224b7a0ea19Sjdolecek break;
225b7a0ea19Sjdolecek
226afe4d516Sjdolecek default:
227afe4d516Sjdolecek error = EPASSTHROUGH;
228afe4d516Sjdolecek break;
229afe4d516Sjdolecek }
230afe4d516Sjdolecek
231afe4d516Sjdolecek return error;
232afe4d516Sjdolecek }
233afe4d516Sjdolecek
2349d1eb470Spgoyette MODULE(MODULE_CLASS_DRIVER, ld_nvme, "ld,nvme,bufq_fcfs");
235916bdfa5Spgoyette
236916bdfa5Spgoyette #ifdef _MODULE
237916bdfa5Spgoyette /*
238916bdfa5Spgoyette * XXX Don't allow ioconf.c to redefine the "struct cfdriver ld_cd"
239916bdfa5Spgoyette * XXX it will be defined in the common-code module
240916bdfa5Spgoyette */
241916bdfa5Spgoyette #undef CFDRIVER_DECL
242916bdfa5Spgoyette #define CFDRIVER_DECL(name, class, attr)
243916bdfa5Spgoyette #include "ioconf.c"
244916bdfa5Spgoyette #endif
245916bdfa5Spgoyette
246916bdfa5Spgoyette static int
ld_nvme_modcmd(modcmd_t cmd,void * opaque)247916bdfa5Spgoyette ld_nvme_modcmd(modcmd_t cmd, void *opaque)
248916bdfa5Spgoyette {
249916bdfa5Spgoyette #ifdef _MODULE
250916bdfa5Spgoyette /*
251916bdfa5Spgoyette * We ignore the cfdriver_vec[] that ioconf provides, since
252916bdfa5Spgoyette * the cfdrivers are attached already.
253916bdfa5Spgoyette */
254916bdfa5Spgoyette static struct cfdriver * const no_cfdriver_vec[] = { NULL };
255916bdfa5Spgoyette #endif
256916bdfa5Spgoyette int error = 0;
257916bdfa5Spgoyette
258916bdfa5Spgoyette #ifdef _MODULE
259916bdfa5Spgoyette switch (cmd) {
260916bdfa5Spgoyette case MODULE_CMD_INIT:
261916bdfa5Spgoyette error = config_init_component(no_cfdriver_vec,
262916bdfa5Spgoyette cfattach_ioconf_ld_nvme, cfdata_ioconf_ld_nvme);
263916bdfa5Spgoyette break;
264916bdfa5Spgoyette case MODULE_CMD_FINI:
265916bdfa5Spgoyette error = config_fini_component(no_cfdriver_vec,
266916bdfa5Spgoyette cfattach_ioconf_ld_nvme, cfdata_ioconf_ld_nvme);
267916bdfa5Spgoyette break;
268916bdfa5Spgoyette default:
269916bdfa5Spgoyette error = ENOTTY;
270916bdfa5Spgoyette break;
271916bdfa5Spgoyette }
272916bdfa5Spgoyette #endif
273916bdfa5Spgoyette
274916bdfa5Spgoyette return error;
275916bdfa5Spgoyette }
276