xref: /netbsd-src/sys/dev/ic/ld_nvme.c (revision 3ea7aa7b243d6211ac4ad842a9b4ad398771ddef)
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