xref: /freebsd/sys/cam/nvme/nvme_da.c (revision 3090d5045a1e5663f151ef3f50f3c7db8f9a9e3c)
1baabaca3SWarner Losh /*-
2f24882ecSPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3f24882ecSPedro F. Giffuni  *
452467047SWarner Losh  * Copyright (c) 2015 Netflix, Inc.
5baabaca3SWarner Losh  *
6baabaca3SWarner Losh  * Redistribution and use in source and binary forms, with or without
7baabaca3SWarner Losh  * modification, are permitted provided that the following conditions
8baabaca3SWarner Losh  * are met:
9baabaca3SWarner Losh  * 1. Redistributions of source code must retain the above copyright
10baabaca3SWarner Losh  *    notice, this list of conditions and the following disclaimer,
11baabaca3SWarner Losh  *    without modification, immediately at the beginning of the file.
12baabaca3SWarner Losh  * 2. Redistributions in binary form must reproduce the above copyright
13baabaca3SWarner Losh  *    notice, this list of conditions and the following disclaimer in the
14baabaca3SWarner Losh  *    documentation and/or other materials provided with the distribution.
15baabaca3SWarner Losh  *
16baabaca3SWarner Losh  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17baabaca3SWarner Losh  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18baabaca3SWarner Losh  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19baabaca3SWarner Losh  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20baabaca3SWarner Losh  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21baabaca3SWarner Losh  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22baabaca3SWarner Losh  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23baabaca3SWarner Losh  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24baabaca3SWarner Losh  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25baabaca3SWarner Losh  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26baabaca3SWarner Losh  *
27baabaca3SWarner Losh  * Derived from ata_da.c:
28baabaca3SWarner Losh  * Copyright (c) 2009 Alexander Motin <mav@FreeBSD.org>
29baabaca3SWarner Losh  */
30baabaca3SWarner Losh 
31baabaca3SWarner Losh #include <sys/cdefs.h>
32baabaca3SWarner Losh __FBSDID("$FreeBSD$");
33baabaca3SWarner Losh 
34baabaca3SWarner Losh #include <sys/param.h>
35baabaca3SWarner Losh 
36baabaca3SWarner Losh #ifdef _KERNEL
37baabaca3SWarner Losh #include <sys/systm.h>
38baabaca3SWarner Losh #include <sys/kernel.h>
39baabaca3SWarner Losh #include <sys/bio.h>
40baabaca3SWarner Losh #include <sys/sysctl.h>
41baabaca3SWarner Losh #include <sys/taskqueue.h>
42baabaca3SWarner Losh #include <sys/lock.h>
43baabaca3SWarner Losh #include <sys/mutex.h>
44baabaca3SWarner Losh #include <sys/conf.h>
45baabaca3SWarner Losh #include <sys/devicestat.h>
46baabaca3SWarner Losh #include <sys/eventhandler.h>
47baabaca3SWarner Losh #include <sys/malloc.h>
48baabaca3SWarner Losh #include <sys/cons.h>
49baabaca3SWarner Losh #include <sys/proc.h>
50baabaca3SWarner Losh #include <sys/reboot.h>
5175ce4227SWarner Losh #include <sys/sbuf.h>
5213532153SScott Long #include <geom/geom.h>
53baabaca3SWarner Losh #include <geom/geom_disk.h>
54baabaca3SWarner Losh #endif /* _KERNEL */
55baabaca3SWarner Losh 
56baabaca3SWarner Losh #ifndef _KERNEL
57baabaca3SWarner Losh #include <stdio.h>
58baabaca3SWarner Losh #include <string.h>
59baabaca3SWarner Losh #endif /* _KERNEL */
60baabaca3SWarner Losh 
61baabaca3SWarner Losh #include <cam/cam.h>
62baabaca3SWarner Losh #include <cam/cam_ccb.h>
63baabaca3SWarner Losh #include <cam/cam_periph.h>
64baabaca3SWarner Losh #include <cam/cam_xpt_periph.h>
65baabaca3SWarner Losh #include <cam/cam_sim.h>
66baabaca3SWarner Losh #include <cam/cam_iosched.h>
67baabaca3SWarner Losh 
68baabaca3SWarner Losh #include <cam/nvme/nvme_all.h>
69baabaca3SWarner Losh 
70baabaca3SWarner Losh typedef enum {
71baabaca3SWarner Losh 	NDA_STATE_NORMAL
72baabaca3SWarner Losh } nda_state;
73baabaca3SWarner Losh 
74baabaca3SWarner Losh typedef enum {
75baabaca3SWarner Losh 	NDA_FLAG_OPEN		= 0x0001,
76baabaca3SWarner Losh 	NDA_FLAG_DIRTY		= 0x0002,
77baabaca3SWarner Losh 	NDA_FLAG_SCTX_INIT	= 0x0004,
78baabaca3SWarner Losh } nda_flags;
7975ce4227SWarner Losh #define NDA_FLAG_STRING		\
8075ce4227SWarner Losh 	"\020"			\
8175ce4227SWarner Losh 	"\001OPEN"		\
8275ce4227SWarner Losh 	"\002DIRTY"		\
8375ce4227SWarner Losh 	"\003SCTX_INIT"
84baabaca3SWarner Losh 
85baabaca3SWarner Losh typedef enum {
86baabaca3SWarner Losh 	NDA_Q_4K   = 0x01,
87baabaca3SWarner Losh 	NDA_Q_NONE = 0x00,
88baabaca3SWarner Losh } nda_quirks;
89baabaca3SWarner Losh 
90baabaca3SWarner Losh #define NDA_Q_BIT_STRING	\
91baabaca3SWarner Losh 	"\020"			\
92baabaca3SWarner Losh 	"\001Bit 0"
93baabaca3SWarner Losh 
94baabaca3SWarner Losh typedef enum {
95baabaca3SWarner Losh 	NDA_CCB_BUFFER_IO	= 0x01,
96baabaca3SWarner Losh 	NDA_CCB_DUMP            = 0x02,
97baabaca3SWarner Losh 	NDA_CCB_TRIM            = 0x03,
982446ce7aSWarner Losh 	NDA_CCB_PASS            = 0x04,
99baabaca3SWarner Losh 	NDA_CCB_TYPE_MASK	= 0x0F,
100baabaca3SWarner Losh } nda_ccb_state;
101baabaca3SWarner Losh 
102baabaca3SWarner Losh /* Offsets into our private area for storing information */
103807e94b2SWarner Losh #define ccb_state	ccb_h.ppriv_field0
104807e94b2SWarner Losh #define ccb_bp		ccb_h.ppriv_ptr1	/* For NDA_CCB_BUFFER_IO */
105807e94b2SWarner Losh #define ccb_trim	ccb_h.ppriv_ptr1	/* For NDA_CCB_TRIM */
106baabaca3SWarner Losh 
107baabaca3SWarner Losh struct nda_softc {
108baabaca3SWarner Losh 	struct   cam_iosched_softc *cam_iosched;
109baabaca3SWarner Losh 	int			outstanding_cmds;	/* Number of active commands */
110baabaca3SWarner Losh 	int			refcount;		/* Active xpt_action() calls */
111baabaca3SWarner Losh 	nda_state		state;
112baabaca3SWarner Losh 	nda_flags		flags;
113baabaca3SWarner Losh 	nda_quirks		quirks;
114baabaca3SWarner Losh 	int			unmappedio;
115807e94b2SWarner Losh 	quad_t			deletes;
116baabaca3SWarner Losh 	uint32_t		nsid;			/* Namespace ID for this nda device */
117baabaca3SWarner Losh 	struct disk		*disk;
118baabaca3SWarner Losh 	struct task		sysctl_task;
119baabaca3SWarner Losh 	struct sysctl_ctx_list	sysctl_ctx;
120baabaca3SWarner Losh 	struct sysctl_oid	*sysctl_tree;
121ea657f2cSWarner Losh 	uint64_t		trim_count;
122ea657f2cSWarner Losh 	uint64_t		trim_ranges;
123ea657f2cSWarner Losh 	uint64_t		trim_lbas;
124d38677d2SWarner Losh #ifdef CAM_TEST_FAILURE
125d38677d2SWarner Losh 	int			force_read_error;
126d38677d2SWarner Losh 	int			force_write_error;
127d38677d2SWarner Losh 	int			periodic_read_error;
128d38677d2SWarner Losh 	int			periodic_read_count;
129d38677d2SWarner Losh #endif
130baabaca3SWarner Losh #ifdef CAM_IO_STATS
131baabaca3SWarner Losh 	struct sysctl_ctx_list	sysctl_stats_ctx;
132baabaca3SWarner Losh 	struct sysctl_oid	*sysctl_stats_tree;
133baabaca3SWarner Losh 	u_int			timeouts;
134baabaca3SWarner Losh 	u_int			errors;
135baabaca3SWarner Losh 	u_int			invalidations;
136baabaca3SWarner Losh #endif
137baabaca3SWarner Losh };
138baabaca3SWarner Losh 
139807e94b2SWarner Losh struct nda_trim_request {
14093489854SBrooks Davis 	struct nvme_dsm_range	dsm[NVME_MAX_DSM_TRIM / sizeof(struct nvme_dsm_range)];
141807e94b2SWarner Losh 	TAILQ_HEAD(, bio) bps;
142807e94b2SWarner Losh };
14393489854SBrooks Davis _Static_assert(NVME_MAX_DSM_TRIM % sizeof(struct nvme_dsm_range) == 0,
14493489854SBrooks Davis     "NVME_MAX_DSM_TRIM must be an integral number of ranges");
145807e94b2SWarner Losh 
146baabaca3SWarner Losh /* Need quirk table */
147baabaca3SWarner Losh 
1482446ce7aSWarner Losh static	disk_ioctl_t	ndaioctl;
149baabaca3SWarner Losh static	disk_strategy_t	ndastrategy;
150baabaca3SWarner Losh static	dumper_t	ndadump;
151baabaca3SWarner Losh static	periph_init_t	ndainit;
152baabaca3SWarner Losh static	void		ndaasync(void *callback_arg, u_int32_t code,
153baabaca3SWarner Losh 				struct cam_path *path, void *arg);
154baabaca3SWarner Losh static	void		ndasysctlinit(void *context, int pending);
15575ce4227SWarner Losh static	int		ndaflagssysctl(SYSCTL_HANDLER_ARGS);
156baabaca3SWarner Losh static	periph_ctor_t	ndaregister;
157baabaca3SWarner Losh static	periph_dtor_t	ndacleanup;
158baabaca3SWarner Losh static	periph_start_t	ndastart;
159baabaca3SWarner Losh static	periph_oninv_t	ndaoninvalidate;
160baabaca3SWarner Losh static	void		ndadone(struct cam_periph *periph,
161baabaca3SWarner Losh 			       union ccb *done_ccb);
162baabaca3SWarner Losh static  int		ndaerror(union ccb *ccb, u_int32_t cam_flags,
163baabaca3SWarner Losh 				u_int32_t sense_flags);
164baabaca3SWarner Losh static void		ndashutdown(void *arg, int howto);
165baabaca3SWarner Losh static void		ndasuspend(void *arg);
166baabaca3SWarner Losh 
167baabaca3SWarner Losh #ifndef	NDA_DEFAULT_SEND_ORDERED
168baabaca3SWarner Losh #define	NDA_DEFAULT_SEND_ORDERED	1
169baabaca3SWarner Losh #endif
170baabaca3SWarner Losh #ifndef NDA_DEFAULT_TIMEOUT
171baabaca3SWarner Losh #define NDA_DEFAULT_TIMEOUT 30	/* Timeout in seconds */
172baabaca3SWarner Losh #endif
173baabaca3SWarner Losh #ifndef	NDA_DEFAULT_RETRY
174baabaca3SWarner Losh #define	NDA_DEFAULT_RETRY	4
175baabaca3SWarner Losh #endif
176807e94b2SWarner Losh #ifndef NDA_MAX_TRIM_ENTRIES
177afdbfe1eSWarner Losh #define NDA_MAX_TRIM_ENTRIES  (NVME_MAX_DSM_TRIM / sizeof(struct nvme_dsm_range))/* Number of DSM trims to use, max 256 */
178807e94b2SWarner Losh #endif
179baabaca3SWarner Losh 
1807029da5cSPawel Biernacki static SYSCTL_NODE(_kern_cam, OID_AUTO, nda, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
1816f591d13SWarner Losh     "CAM Direct Access Disk driver");
1826f591d13SWarner Losh 
183baabaca3SWarner Losh //static int nda_retry_count = NDA_DEFAULT_RETRY;
184baabaca3SWarner Losh static int nda_send_ordered = NDA_DEFAULT_SEND_ORDERED;
185baabaca3SWarner Losh static int nda_default_timeout = NDA_DEFAULT_TIMEOUT;
186807e94b2SWarner Losh static int nda_max_trim_entries = NDA_MAX_TRIM_ENTRIES;
18713532153SScott Long static int nda_enable_biospeedup = 1;
1881868c484SWarner Losh static int nda_nvd_compat = 1;
1896f591d13SWarner Losh SYSCTL_INT(_kern_cam_nda, OID_AUTO, max_trim, CTLFLAG_RDTUN,
1906f591d13SWarner Losh     &nda_max_trim_entries, NDA_MAX_TRIM_ENTRIES,
1916f591d13SWarner Losh     "Maximum number of BIO_DELETE to send down as a DSM TRIM.");
19213532153SScott Long SYSCTL_INT(_kern_cam_nda, OID_AUTO, enable_biospeedup, CTLFLAG_RDTUN,
1931868c484SWarner Losh     &nda_enable_biospeedup, 0, "Enable BIO_SPEEDUP processing.");
1941868c484SWarner Losh SYSCTL_INT(_kern_cam_nda, OID_AUTO, nvd_compat, CTLFLAG_RDTUN,
1951868c484SWarner Losh     &nda_nvd_compat, 1, "Enable creation of nvd aliases.");
196baabaca3SWarner Losh 
197baabaca3SWarner Losh /*
198baabaca3SWarner Losh  * All NVMe media is non-rotational, so all nvme device instances
199baabaca3SWarner Losh  * share this to implement the sysctl.
200baabaca3SWarner Losh  */
201baabaca3SWarner Losh static int nda_rotating_media = 0;
202baabaca3SWarner Losh 
203baabaca3SWarner Losh static struct periph_driver ndadriver =
204baabaca3SWarner Losh {
205baabaca3SWarner Losh 	ndainit, "nda",
206baabaca3SWarner Losh 	TAILQ_HEAD_INITIALIZER(ndadriver.units), /* generation */ 0
207baabaca3SWarner Losh };
208baabaca3SWarner Losh 
209baabaca3SWarner Losh PERIPHDRIVER_DECLARE(nda, ndadriver);
210baabaca3SWarner Losh 
211baabaca3SWarner Losh static MALLOC_DEFINE(M_NVMEDA, "nvme_da", "nvme_da buffers");
212baabaca3SWarner Losh 
213baabaca3SWarner Losh /*
214baabaca3SWarner Losh  * nice wrappers. Maybe these belong in nvme_all.c instead of
215baabaca3SWarner Losh  * here, but this is the only place that uses these. Should
216baabaca3SWarner Losh  * we ever grow another NVME periph, we should move them
217baabaca3SWarner Losh  * all there wholesale.
218baabaca3SWarner Losh  */
219baabaca3SWarner Losh 
220baabaca3SWarner Losh static void
221baabaca3SWarner Losh nda_nvme_flush(struct nda_softc *softc, struct ccb_nvmeio *nvmeio)
222baabaca3SWarner Losh {
223baabaca3SWarner Losh 	cam_fill_nvmeio(nvmeio,
224baabaca3SWarner Losh 	    0,			/* retries */
225baabaca3SWarner Losh 	    ndadone,		/* cbfcnp */
226baabaca3SWarner Losh 	    CAM_DIR_NONE,	/* flags */
227baabaca3SWarner Losh 	    NULL,		/* data_ptr */
228baabaca3SWarner Losh 	    0,			/* dxfer_len */
229717bff5dSWarner Losh 	    nda_default_timeout * 1000); /* timeout 30s */
230baabaca3SWarner Losh 	nvme_ns_flush_cmd(&nvmeio->cmd, softc->nsid);
231baabaca3SWarner Losh }
232baabaca3SWarner Losh 
233baabaca3SWarner Losh static void
234baabaca3SWarner Losh nda_nvme_trim(struct nda_softc *softc, struct ccb_nvmeio *nvmeio,
235baabaca3SWarner Losh     void *payload, uint32_t num_ranges)
236baabaca3SWarner Losh {
237baabaca3SWarner Losh 	cam_fill_nvmeio(nvmeio,
238baabaca3SWarner Losh 	    0,			/* retries */
239baabaca3SWarner Losh 	    ndadone,		/* cbfcnp */
240baabaca3SWarner Losh 	    CAM_DIR_OUT,	/* flags */
241baabaca3SWarner Losh 	    payload,		/* data_ptr */
242baabaca3SWarner Losh 	    num_ranges * sizeof(struct nvme_dsm_range), /* dxfer_len */
243717bff5dSWarner Losh 	    nda_default_timeout * 1000); /* timeout 30s */
244baabaca3SWarner Losh 	nvme_ns_trim_cmd(&nvmeio->cmd, softc->nsid, num_ranges);
245baabaca3SWarner Losh }
246baabaca3SWarner Losh 
247baabaca3SWarner Losh static void
248baabaca3SWarner Losh nda_nvme_write(struct nda_softc *softc, struct ccb_nvmeio *nvmeio,
249baabaca3SWarner Losh     void *payload, uint64_t lba, uint32_t len, uint32_t count)
250baabaca3SWarner Losh {
251baabaca3SWarner Losh 	cam_fill_nvmeio(nvmeio,
252baabaca3SWarner Losh 	    0,			/* retries */
253baabaca3SWarner Losh 	    ndadone,		/* cbfcnp */
254baabaca3SWarner Losh 	    CAM_DIR_OUT,	/* flags */
255baabaca3SWarner Losh 	    payload,		/* data_ptr */
256baabaca3SWarner Losh 	    len,		/* dxfer_len */
257717bff5dSWarner Losh 	    nda_default_timeout * 1000); /* timeout 30s */
258baabaca3SWarner Losh 	nvme_ns_write_cmd(&nvmeio->cmd, softc->nsid, lba, count);
259baabaca3SWarner Losh }
260baabaca3SWarner Losh 
261baabaca3SWarner Losh static void
262baabaca3SWarner Losh nda_nvme_rw_bio(struct nda_softc *softc, struct ccb_nvmeio *nvmeio,
263baabaca3SWarner Losh     struct bio *bp, uint32_t rwcmd)
264baabaca3SWarner Losh {
265baabaca3SWarner Losh 	int flags = rwcmd == NVME_OPC_READ ? CAM_DIR_IN : CAM_DIR_OUT;
266baabaca3SWarner Losh 	void *payload;
267baabaca3SWarner Losh 	uint64_t lba;
268baabaca3SWarner Losh 	uint32_t count;
269baabaca3SWarner Losh 
270baabaca3SWarner Losh 	if (bp->bio_flags & BIO_UNMAPPED) {
271baabaca3SWarner Losh 		flags |= CAM_DATA_BIO;
272baabaca3SWarner Losh 		payload = bp;
273baabaca3SWarner Losh 	} else {
274baabaca3SWarner Losh 		payload = bp->bio_data;
275baabaca3SWarner Losh 	}
276baabaca3SWarner Losh 
277baabaca3SWarner Losh 	lba = bp->bio_pblkno;
278baabaca3SWarner Losh 	count = bp->bio_bcount / softc->disk->d_sectorsize;
279baabaca3SWarner Losh 
280baabaca3SWarner Losh 	cam_fill_nvmeio(nvmeio,
281baabaca3SWarner Losh 	    0,			/* retries */
282baabaca3SWarner Losh 	    ndadone,		/* cbfcnp */
283baabaca3SWarner Losh 	    flags,		/* flags */
284baabaca3SWarner Losh 	    payload,		/* data_ptr */
285baabaca3SWarner Losh 	    bp->bio_bcount,	/* dxfer_len */
286717bff5dSWarner Losh 	    nda_default_timeout * 1000); /* timeout 30s */
287baabaca3SWarner Losh 	nvme_ns_rw_cmd(&nvmeio->cmd, rwcmd, softc->nsid, lba, count);
288baabaca3SWarner Losh }
289baabaca3SWarner Losh 
290baabaca3SWarner Losh static int
291baabaca3SWarner Losh ndaopen(struct disk *dp)
292baabaca3SWarner Losh {
293baabaca3SWarner Losh 	struct cam_periph *periph;
294baabaca3SWarner Losh 	struct nda_softc *softc;
295baabaca3SWarner Losh 	int error;
296baabaca3SWarner Losh 
297baabaca3SWarner Losh 	periph = (struct cam_periph *)dp->d_drv1;
29899e7a4adSScott Long 	if (cam_periph_acquire(periph) != 0) {
299baabaca3SWarner Losh 		return(ENXIO);
300baabaca3SWarner Losh 	}
301baabaca3SWarner Losh 
302baabaca3SWarner Losh 	cam_periph_lock(periph);
303baabaca3SWarner Losh 	if ((error = cam_periph_hold(periph, PRIBIO|PCATCH)) != 0) {
304baabaca3SWarner Losh 		cam_periph_unlock(periph);
305baabaca3SWarner Losh 		cam_periph_release(periph);
306baabaca3SWarner Losh 		return (error);
307baabaca3SWarner Losh 	}
308baabaca3SWarner Losh 
309baabaca3SWarner Losh 	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE | CAM_DEBUG_PERIPH,
310baabaca3SWarner Losh 	    ("ndaopen\n"));
311baabaca3SWarner Losh 
312baabaca3SWarner Losh 	softc = (struct nda_softc *)periph->softc;
313baabaca3SWarner Losh 	softc->flags |= NDA_FLAG_OPEN;
314baabaca3SWarner Losh 
315baabaca3SWarner Losh 	cam_periph_unhold(periph);
316baabaca3SWarner Losh 	cam_periph_unlock(periph);
317baabaca3SWarner Losh 	return (0);
318baabaca3SWarner Losh }
319baabaca3SWarner Losh 
320baabaca3SWarner Losh static int
321baabaca3SWarner Losh ndaclose(struct disk *dp)
322baabaca3SWarner Losh {
323baabaca3SWarner Losh 	struct	cam_periph *periph;
324baabaca3SWarner Losh 	struct	nda_softc *softc;
325baabaca3SWarner Losh 	union ccb *ccb;
326baabaca3SWarner Losh 	int error;
327baabaca3SWarner Losh 
328baabaca3SWarner Losh 	periph = (struct cam_periph *)dp->d_drv1;
329baabaca3SWarner Losh 	softc = (struct nda_softc *)periph->softc;
330baabaca3SWarner Losh 	cam_periph_lock(periph);
331baabaca3SWarner Losh 
332baabaca3SWarner Losh 	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE | CAM_DEBUG_PERIPH,
333baabaca3SWarner Losh 	    ("ndaclose\n"));
334baabaca3SWarner Losh 
335baabaca3SWarner Losh 	if ((softc->flags & NDA_FLAG_DIRTY) != 0 &&
336baabaca3SWarner Losh 	    (periph->flags & CAM_PERIPH_INVALID) == 0 &&
337baabaca3SWarner Losh 	    cam_periph_hold(periph, PRIBIO) == 0) {
338baabaca3SWarner Losh 		ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL);
339baabaca3SWarner Losh 		nda_nvme_flush(softc, &ccb->nvmeio);
340baabaca3SWarner Losh 		error = cam_periph_runccb(ccb, ndaerror, /*cam_flags*/0,
341baabaca3SWarner Losh 		    /*sense_flags*/0, softc->disk->d_devstat);
342baabaca3SWarner Losh 
343baabaca3SWarner Losh 		if (error != 0)
344baabaca3SWarner Losh 			xpt_print(periph->path, "Synchronize cache failed\n");
345baabaca3SWarner Losh 		else
346baabaca3SWarner Losh 			softc->flags &= ~NDA_FLAG_DIRTY;
347baabaca3SWarner Losh 		xpt_release_ccb(ccb);
348baabaca3SWarner Losh 		cam_periph_unhold(periph);
349baabaca3SWarner Losh 	}
350baabaca3SWarner Losh 
351baabaca3SWarner Losh 	softc->flags &= ~NDA_FLAG_OPEN;
352baabaca3SWarner Losh 
353baabaca3SWarner Losh 	while (softc->refcount != 0)
354baabaca3SWarner Losh 		cam_periph_sleep(periph, &softc->refcount, PRIBIO, "ndaclose", 1);
355b1988d44SWarner Losh 	KASSERT(softc->outstanding_cmds == 0,
356b1988d44SWarner Losh 	    ("nda %d outstanding commands", softc->outstanding_cmds));
357baabaca3SWarner Losh 	cam_periph_unlock(periph);
358baabaca3SWarner Losh 	cam_periph_release(periph);
359baabaca3SWarner Losh 	return (0);
360baabaca3SWarner Losh }
361baabaca3SWarner Losh 
362baabaca3SWarner Losh static void
363baabaca3SWarner Losh ndaschedule(struct cam_periph *periph)
364baabaca3SWarner Losh {
365baabaca3SWarner Losh 	struct nda_softc *softc = (struct nda_softc *)periph->softc;
366baabaca3SWarner Losh 
367baabaca3SWarner Losh 	if (softc->state != NDA_STATE_NORMAL)
368baabaca3SWarner Losh 		return;
369baabaca3SWarner Losh 
370baabaca3SWarner Losh 	cam_iosched_schedule(softc->cam_iosched, periph);
371baabaca3SWarner Losh }
372baabaca3SWarner Losh 
3732446ce7aSWarner Losh static int
3742446ce7aSWarner Losh ndaioctl(struct disk *dp, u_long cmd, void *data, int fflag,
3752446ce7aSWarner Losh     struct thread *td)
3762446ce7aSWarner Losh {
3772446ce7aSWarner Losh 	struct cam_periph *periph;
3782446ce7aSWarner Losh 
3792446ce7aSWarner Losh 	periph = (struct cam_periph *)dp->d_drv1;
3802446ce7aSWarner Losh 
3812446ce7aSWarner Losh 	switch (cmd) {
3822446ce7aSWarner Losh 	case NVME_IO_TEST:
3832446ce7aSWarner Losh 	case NVME_BIO_TEST:
3842446ce7aSWarner Losh 		/*
3852446ce7aSWarner Losh 		 * These don't map well to the underlying CCBs, so
3862446ce7aSWarner Losh 		 * they are usupported via CAM.
3872446ce7aSWarner Losh 		 */
3882446ce7aSWarner Losh 		return (ENOTTY);
3892446ce7aSWarner Losh 	case NVME_GET_NSID:
3902446ce7aSWarner Losh 	{
3912446ce7aSWarner Losh 		struct nvme_get_nsid *gnsid = (struct nvme_get_nsid *)data;
3922446ce7aSWarner Losh 		struct ccb_pathinq cpi;
3932446ce7aSWarner Losh 
3942446ce7aSWarner Losh 		xpt_path_inq(&cpi, periph->path);
3952446ce7aSWarner Losh 		strncpy(gnsid->cdev, cpi.xport_specific.nvme.dev_name,
3962446ce7aSWarner Losh 		    sizeof(gnsid->cdev));
3972446ce7aSWarner Losh 		gnsid->nsid = cpi.xport_specific.nvme.nsid;
3982446ce7aSWarner Losh 		return (0);
3992446ce7aSWarner Losh 	}
4002446ce7aSWarner Losh 	case NVME_PASSTHROUGH_CMD:
4012446ce7aSWarner Losh 	{
4022446ce7aSWarner Losh 		struct nvme_pt_command *pt;
4032446ce7aSWarner Losh 		union ccb *ccb;
4042446ce7aSWarner Losh 		struct cam_periph_map_info mapinfo;
4052d76f4aaSWarner Losh 		u_int maxmap = dp->d_maxsize;
4062446ce7aSWarner Losh 		int error;
4072446ce7aSWarner Losh 
4082446ce7aSWarner Losh 		/*
4092446ce7aSWarner Losh 		 * Create a NVME_IO CCB to do the passthrough command.
4102446ce7aSWarner Losh 		 */
4112446ce7aSWarner Losh 		pt = (struct nvme_pt_command *)data;
4122446ce7aSWarner Losh 		ccb = xpt_alloc_ccb();
4132446ce7aSWarner Losh 		xpt_setup_ccb(&ccb->ccb_h, periph->path, CAM_PRIORITY_NORMAL);
4142446ce7aSWarner Losh 		ccb->ccb_state = NDA_CCB_PASS;
4152446ce7aSWarner Losh 		cam_fill_nvmeio(&ccb->nvmeio,
4162446ce7aSWarner Losh 		    0,			/* Retries */
4172446ce7aSWarner Losh 		    ndadone,
4182446ce7aSWarner Losh 		    (pt->is_read ? CAM_DIR_IN : CAM_DIR_OUT) | CAM_DATA_VADDR,
4192446ce7aSWarner Losh 		    pt->buf,
4202446ce7aSWarner Losh 		    pt->len,
4212446ce7aSWarner Losh 		    nda_default_timeout * 1000);
4222446ce7aSWarner Losh 		memcpy(&ccb->nvmeio.cmd, &pt->cmd, sizeof(pt->cmd));
4232446ce7aSWarner Losh 
4242446ce7aSWarner Losh 		/*
4252446ce7aSWarner Losh 		 * Wire the user memory in this request for the I/O
4262446ce7aSWarner Losh 		 */
4272446ce7aSWarner Losh 		memset(&mapinfo, 0, sizeof(mapinfo));
4282446ce7aSWarner Losh 		error = cam_periph_mapmem(ccb, &mapinfo, maxmap);
4294f397ed2SWarner Losh 		if (error)
4304f397ed2SWarner Losh 			goto out;
4312446ce7aSWarner Losh 
4322446ce7aSWarner Losh 		/*
4334f397ed2SWarner Losh 		 * Lock the periph and run the command.
4342446ce7aSWarner Losh 		 */
4352446ce7aSWarner Losh 		cam_periph_lock(periph);
4364f397ed2SWarner Losh 		cam_periph_runccb(ccb, NULL, CAM_RETRY_SELTO,
4374f397ed2SWarner Losh 		    SF_RETRY_UA | SF_NO_PRINT, NULL);
4382446ce7aSWarner Losh 
4392446ce7aSWarner Losh 		/*
4402446ce7aSWarner Losh 		 * Tear down mapping and return status.
4412446ce7aSWarner Losh 		 */
4424f397ed2SWarner Losh 		cam_periph_unlock(periph);
4432446ce7aSWarner Losh 		cam_periph_unmapmem(ccb, &mapinfo);
4442446ce7aSWarner Losh 		error = (ccb->ccb_h.status == CAM_REQ_CMP) ? 0 : EIO;
4454f397ed2SWarner Losh out:
4464f397ed2SWarner Losh 		cam_periph_lock(periph);
4472446ce7aSWarner Losh 		xpt_release_ccb(ccb);
4484f397ed2SWarner Losh 		cam_periph_unlock(periph);
4492446ce7aSWarner Losh 		return (error);
4502446ce7aSWarner Losh 	}
4512446ce7aSWarner Losh 	default:
4522446ce7aSWarner Losh 		break;
4532446ce7aSWarner Losh 	}
4542446ce7aSWarner Losh 	return (ENOTTY);
4552446ce7aSWarner Losh }
4562446ce7aSWarner Losh 
457baabaca3SWarner Losh /*
458baabaca3SWarner Losh  * Actually translate the requested transfer into one the physical driver
459baabaca3SWarner Losh  * can understand.  The transfer is described by a buf and will include
460baabaca3SWarner Losh  * only one physical transfer.
461baabaca3SWarner Losh  */
462baabaca3SWarner Losh static void
463baabaca3SWarner Losh ndastrategy(struct bio *bp)
464baabaca3SWarner Losh {
465baabaca3SWarner Losh 	struct cam_periph *periph;
466baabaca3SWarner Losh 	struct nda_softc *softc;
467baabaca3SWarner Losh 
468baabaca3SWarner Losh 	periph = (struct cam_periph *)bp->bio_disk->d_drv1;
469baabaca3SWarner Losh 	softc = (struct nda_softc *)periph->softc;
470baabaca3SWarner Losh 
471baabaca3SWarner Losh 	cam_periph_lock(periph);
472baabaca3SWarner Losh 
473baabaca3SWarner Losh 	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("ndastrategy(%p)\n", bp));
474baabaca3SWarner Losh 
475baabaca3SWarner Losh 	/*
476baabaca3SWarner Losh 	 * If the device has been made invalid, error out
477baabaca3SWarner Losh 	 */
478baabaca3SWarner Losh 	if ((periph->flags & CAM_PERIPH_INVALID) != 0) {
479baabaca3SWarner Losh 		cam_periph_unlock(periph);
480baabaca3SWarner Losh 		biofinish(bp, NULL, ENXIO);
481baabaca3SWarner Losh 		return;
482baabaca3SWarner Losh 	}
483baabaca3SWarner Losh 
484807e94b2SWarner Losh 	if (bp->bio_cmd == BIO_DELETE)
485807e94b2SWarner Losh 		softc->deletes++;
486807e94b2SWarner Losh 
487baabaca3SWarner Losh 	/*
488baabaca3SWarner Losh 	 * Place it in the queue of disk activities for this disk
489baabaca3SWarner Losh 	 */
490baabaca3SWarner Losh 	cam_iosched_queue_work(softc->cam_iosched, bp);
491baabaca3SWarner Losh 
492baabaca3SWarner Losh 	/*
493baabaca3SWarner Losh 	 * Schedule ourselves for performing the work.
494baabaca3SWarner Losh 	 */
495baabaca3SWarner Losh 	ndaschedule(periph);
496baabaca3SWarner Losh 	cam_periph_unlock(periph);
497baabaca3SWarner Losh 
498baabaca3SWarner Losh 	return;
499baabaca3SWarner Losh }
500baabaca3SWarner Losh 
501baabaca3SWarner Losh static int
502baabaca3SWarner Losh ndadump(void *arg, void *virtual, vm_offset_t physical, off_t offset, size_t length)
503baabaca3SWarner Losh {
504baabaca3SWarner Losh 	struct	    cam_periph *periph;
505baabaca3SWarner Losh 	struct	    nda_softc *softc;
506baabaca3SWarner Losh 	u_int	    secsize;
507c4231018SWarner Losh 	struct ccb_nvmeio nvmeio;
508baabaca3SWarner Losh 	struct	    disk *dp;
509baabaca3SWarner Losh 	uint64_t    lba;
510baabaca3SWarner Losh 	uint32_t    count;
511baabaca3SWarner Losh 	int	    error = 0;
512baabaca3SWarner Losh 
513baabaca3SWarner Losh 	dp = arg;
514baabaca3SWarner Losh 	periph = dp->d_drv1;
515baabaca3SWarner Losh 	softc = (struct nda_softc *)periph->softc;
516baabaca3SWarner Losh 	secsize = softc->disk->d_sectorsize;
517baabaca3SWarner Losh 	lba = offset / secsize;
518baabaca3SWarner Losh 	count = length / secsize;
519baabaca3SWarner Losh 
520bff0b56cSScott Long 	if ((periph->flags & CAM_PERIPH_INVALID) != 0)
521baabaca3SWarner Losh 		return (ENXIO);
522baabaca3SWarner Losh 
523fa271a5dSWarner Losh 	/* xpt_get_ccb returns a zero'd allocation for the ccb, mimic that here */
524fa271a5dSWarner Losh 	memset(&nvmeio, 0, sizeof(nvmeio));
525baabaca3SWarner Losh 	if (length > 0) {
526c4231018SWarner Losh 		xpt_setup_ccb(&nvmeio.ccb_h, periph->path, CAM_PRIORITY_NORMAL);
527807e94b2SWarner Losh 		nvmeio.ccb_state = NDA_CCB_DUMP;
528c4231018SWarner Losh 		nda_nvme_write(softc, &nvmeio, virtual, lba, length, count);
5292b31251aSWarner Losh 		error = cam_periph_runccb((union ccb *)&nvmeio, cam_periph_error,
5302b31251aSWarner Losh 		    0, SF_NO_RECOVERY | SF_NO_RETRY, NULL);
531baabaca3SWarner Losh 		if (error != 0)
5322b31251aSWarner Losh 			printf("Aborting dump due to I/O error %d.\n", error);
5332b31251aSWarner Losh 
534baabaca3SWarner Losh 		return (error);
535baabaca3SWarner Losh 	}
536baabaca3SWarner Losh 
537baabaca3SWarner Losh 	/* Flush */
538c4231018SWarner Losh 	xpt_setup_ccb(&nvmeio.ccb_h, periph->path, CAM_PRIORITY_NORMAL);
539baabaca3SWarner Losh 
540807e94b2SWarner Losh 	nvmeio.ccb_state = NDA_CCB_DUMP;
541c4231018SWarner Losh 	nda_nvme_flush(softc, &nvmeio);
5422b31251aSWarner Losh 	error = cam_periph_runccb((union ccb *)&nvmeio, cam_periph_error,
5432b31251aSWarner Losh 	    0, SF_NO_RECOVERY | SF_NO_RETRY, NULL);
544baabaca3SWarner Losh 	if (error != 0)
545baabaca3SWarner Losh 		xpt_print(periph->path, "flush cmd failed\n");
546baabaca3SWarner Losh 	return (error);
547baabaca3SWarner Losh }
548baabaca3SWarner Losh 
549baabaca3SWarner Losh static void
550baabaca3SWarner Losh ndainit(void)
551baabaca3SWarner Losh {
552baabaca3SWarner Losh 	cam_status status;
553baabaca3SWarner Losh 
554baabaca3SWarner Losh 	/*
555baabaca3SWarner Losh 	 * Install a global async callback.  This callback will
556baabaca3SWarner Losh 	 * receive async callbacks like "new device found".
557baabaca3SWarner Losh 	 */
558baabaca3SWarner Losh 	status = xpt_register_async(AC_FOUND_DEVICE, ndaasync, NULL, NULL);
559baabaca3SWarner Losh 
560baabaca3SWarner Losh 	if (status != CAM_REQ_CMP) {
561baabaca3SWarner Losh 		printf("nda: Failed to attach master async callback "
562baabaca3SWarner Losh 		       "due to status 0x%x!\n", status);
563baabaca3SWarner Losh 	} else if (nda_send_ordered) {
564baabaca3SWarner Losh 		/* Register our event handlers */
565baabaca3SWarner Losh 		if ((EVENTHANDLER_REGISTER(power_suspend, ndasuspend,
566baabaca3SWarner Losh 					   NULL, EVENTHANDLER_PRI_LAST)) == NULL)
567baabaca3SWarner Losh 		    printf("ndainit: power event registration failed!\n");
568baabaca3SWarner Losh 		if ((EVENTHANDLER_REGISTER(shutdown_post_sync, ndashutdown,
569baabaca3SWarner Losh 					   NULL, SHUTDOWN_PRI_DEFAULT)) == NULL)
570baabaca3SWarner Losh 		    printf("ndainit: shutdown event registration failed!\n");
571baabaca3SWarner Losh 	}
572baabaca3SWarner Losh }
573baabaca3SWarner Losh 
574baabaca3SWarner Losh /*
575baabaca3SWarner Losh  * Callback from GEOM, called when it has finished cleaning up its
576baabaca3SWarner Losh  * resources.
577baabaca3SWarner Losh  */
578baabaca3SWarner Losh static void
579baabaca3SWarner Losh ndadiskgonecb(struct disk *dp)
580baabaca3SWarner Losh {
581baabaca3SWarner Losh 	struct cam_periph *periph;
582baabaca3SWarner Losh 
583baabaca3SWarner Losh 	periph = (struct cam_periph *)dp->d_drv1;
584baabaca3SWarner Losh 
585baabaca3SWarner Losh 	cam_periph_release(periph);
586baabaca3SWarner Losh }
587baabaca3SWarner Losh 
588baabaca3SWarner Losh static void
589baabaca3SWarner Losh ndaoninvalidate(struct cam_periph *periph)
590baabaca3SWarner Losh {
591baabaca3SWarner Losh 	struct nda_softc *softc;
592baabaca3SWarner Losh 
593baabaca3SWarner Losh 	softc = (struct nda_softc *)periph->softc;
594baabaca3SWarner Losh 
595baabaca3SWarner Losh 	/*
596baabaca3SWarner Losh 	 * De-register any async callbacks.
597baabaca3SWarner Losh 	 */
598baabaca3SWarner Losh 	xpt_register_async(0, ndaasync, periph, periph->path);
599baabaca3SWarner Losh #ifdef CAM_IO_STATS
600baabaca3SWarner Losh 	softc->invalidations++;
601baabaca3SWarner Losh #endif
602baabaca3SWarner Losh 
603baabaca3SWarner Losh 	/*
604baabaca3SWarner Losh 	 * Return all queued I/O with ENXIO.
605baabaca3SWarner Losh 	 * XXX Handle any transactions queued to the card
606baabaca3SWarner Losh 	 *     with XPT_ABORT_CCB.
607baabaca3SWarner Losh 	 */
608baabaca3SWarner Losh 	cam_iosched_flush(softc->cam_iosched, NULL, ENXIO);
609baabaca3SWarner Losh 
610baabaca3SWarner Losh 	disk_gone(softc->disk);
611baabaca3SWarner Losh }
612baabaca3SWarner Losh 
613baabaca3SWarner Losh static void
614baabaca3SWarner Losh ndacleanup(struct cam_periph *periph)
615baabaca3SWarner Losh {
616baabaca3SWarner Losh 	struct nda_softc *softc;
617baabaca3SWarner Losh 
618baabaca3SWarner Losh 	softc = (struct nda_softc *)periph->softc;
619baabaca3SWarner Losh 
620baabaca3SWarner Losh 	cam_periph_unlock(periph);
621baabaca3SWarner Losh 
622baabaca3SWarner Losh 	cam_iosched_fini(softc->cam_iosched);
623baabaca3SWarner Losh 
624baabaca3SWarner Losh 	/*
625baabaca3SWarner Losh 	 * If we can't free the sysctl tree, oh well...
626baabaca3SWarner Losh 	 */
627baabaca3SWarner Losh 	if ((softc->flags & NDA_FLAG_SCTX_INIT) != 0) {
628baabaca3SWarner Losh #ifdef CAM_IO_STATS
629baabaca3SWarner Losh 		if (sysctl_ctx_free(&softc->sysctl_stats_ctx) != 0)
630baabaca3SWarner Losh 			xpt_print(periph->path,
631baabaca3SWarner Losh 			    "can't remove sysctl stats context\n");
632baabaca3SWarner Losh #endif
633baabaca3SWarner Losh 		if (sysctl_ctx_free(&softc->sysctl_ctx) != 0)
634baabaca3SWarner Losh 			xpt_print(periph->path,
635baabaca3SWarner Losh 			    "can't remove sysctl context\n");
636baabaca3SWarner Losh 	}
637baabaca3SWarner Losh 
638baabaca3SWarner Losh 	disk_destroy(softc->disk);
639baabaca3SWarner Losh 	free(softc, M_DEVBUF);
640baabaca3SWarner Losh 	cam_periph_lock(periph);
641baabaca3SWarner Losh }
642baabaca3SWarner Losh 
643baabaca3SWarner Losh static void
644baabaca3SWarner Losh ndaasync(void *callback_arg, u_int32_t code,
645baabaca3SWarner Losh 	struct cam_path *path, void *arg)
646baabaca3SWarner Losh {
647baabaca3SWarner Losh 	struct cam_periph *periph;
648baabaca3SWarner Losh 
649baabaca3SWarner Losh 	periph = (struct cam_periph *)callback_arg;
650baabaca3SWarner Losh 	switch (code) {
651baabaca3SWarner Losh 	case AC_FOUND_DEVICE:
652baabaca3SWarner Losh 	{
653baabaca3SWarner Losh 		struct ccb_getdev *cgd;
654baabaca3SWarner Losh 		cam_status status;
655baabaca3SWarner Losh 
656baabaca3SWarner Losh 		cgd = (struct ccb_getdev *)arg;
657baabaca3SWarner Losh 		if (cgd == NULL)
658baabaca3SWarner Losh 			break;
659baabaca3SWarner Losh 
660baabaca3SWarner Losh 		if (cgd->protocol != PROTO_NVME)
661baabaca3SWarner Losh 			break;
662baabaca3SWarner Losh 
663baabaca3SWarner Losh 		/*
664baabaca3SWarner Losh 		 * Allocate a peripheral instance for
665baabaca3SWarner Losh 		 * this device and start the probe
666baabaca3SWarner Losh 		 * process.
667baabaca3SWarner Losh 		 */
668baabaca3SWarner Losh 		status = cam_periph_alloc(ndaregister, ndaoninvalidate,
669baabaca3SWarner Losh 					  ndacleanup, ndastart,
670baabaca3SWarner Losh 					  "nda", CAM_PERIPH_BIO,
671baabaca3SWarner Losh 					  path, ndaasync,
672baabaca3SWarner Losh 					  AC_FOUND_DEVICE, cgd);
673baabaca3SWarner Losh 
674baabaca3SWarner Losh 		if (status != CAM_REQ_CMP
675baabaca3SWarner Losh 		 && status != CAM_REQ_INPROG)
676baabaca3SWarner Losh 			printf("ndaasync: Unable to attach to new device "
677baabaca3SWarner Losh 				"due to status 0x%x\n", status);
678baabaca3SWarner Losh 		break;
679baabaca3SWarner Losh 	}
680baabaca3SWarner Losh 	case AC_ADVINFO_CHANGED:
681baabaca3SWarner Losh 	{
682baabaca3SWarner Losh 		uintptr_t buftype;
683baabaca3SWarner Losh 
684baabaca3SWarner Losh 		buftype = (uintptr_t)arg;
685baabaca3SWarner Losh 		if (buftype == CDAI_TYPE_PHYS_PATH) {
686baabaca3SWarner Losh 			struct nda_softc *softc;
687baabaca3SWarner Losh 
688baabaca3SWarner Losh 			softc = periph->softc;
689baabaca3SWarner Losh 			disk_attr_changed(softc->disk, "GEOM::physpath",
690baabaca3SWarner Losh 					  M_NOWAIT);
691baabaca3SWarner Losh 		}
692baabaca3SWarner Losh 		break;
693baabaca3SWarner Losh 	}
694baabaca3SWarner Losh 	case AC_LOST_DEVICE:
695baabaca3SWarner Losh 	default:
696baabaca3SWarner Losh 		cam_periph_async(periph, code, path, arg);
697baabaca3SWarner Losh 		break;
698baabaca3SWarner Losh 	}
699baabaca3SWarner Losh }
700baabaca3SWarner Losh 
701baabaca3SWarner Losh static void
702baabaca3SWarner Losh ndasysctlinit(void *context, int pending)
703baabaca3SWarner Losh {
704baabaca3SWarner Losh 	struct cam_periph *periph;
705baabaca3SWarner Losh 	struct nda_softc *softc;
7064d470952SAlexander Motin 	char tmpstr[32], tmpstr2[16];
707baabaca3SWarner Losh 
708baabaca3SWarner Losh 	periph = (struct cam_periph *)context;
709baabaca3SWarner Losh 
710baabaca3SWarner Losh 	/* periph was held for us when this task was enqueued */
711baabaca3SWarner Losh 	if ((periph->flags & CAM_PERIPH_INVALID) != 0) {
712baabaca3SWarner Losh 		cam_periph_release(periph);
713baabaca3SWarner Losh 		return;
714baabaca3SWarner Losh 	}
715baabaca3SWarner Losh 
716baabaca3SWarner Losh 	softc = (struct nda_softc *)periph->softc;
717baabaca3SWarner Losh 	snprintf(tmpstr, sizeof(tmpstr), "CAM NDA unit %d", periph->unit_number);
718baabaca3SWarner Losh 	snprintf(tmpstr2, sizeof(tmpstr2), "%d", periph->unit_number);
719baabaca3SWarner Losh 
720baabaca3SWarner Losh 	sysctl_ctx_init(&softc->sysctl_ctx);
721baabaca3SWarner Losh 	softc->flags |= NDA_FLAG_SCTX_INIT;
7224c484fd2SEd Schouten 	softc->sysctl_tree = SYSCTL_ADD_NODE_WITH_LABEL(&softc->sysctl_ctx,
723baabaca3SWarner Losh 		SYSCTL_STATIC_CHILDREN(_kern_cam_nda), OID_AUTO, tmpstr2,
7247029da5cSPawel Biernacki 		CTLFLAG_RD | CTLFLAG_MPSAFE, 0, tmpstr, "device_index");
725baabaca3SWarner Losh 	if (softc->sysctl_tree == NULL) {
726baabaca3SWarner Losh 		printf("ndasysctlinit: unable to allocate sysctl tree\n");
727baabaca3SWarner Losh 		cam_periph_release(periph);
728baabaca3SWarner Losh 		return;
729baabaca3SWarner Losh 	}
730baabaca3SWarner Losh 
731baabaca3SWarner Losh 	SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree),
732fdfc0a83SWarner Losh 	    OID_AUTO, "unmapped_io", CTLFLAG_RD,
733baabaca3SWarner Losh 	    &softc->unmappedio, 0, "Unmapped I/O leaf");
734baabaca3SWarner Losh 
735807e94b2SWarner Losh 	SYSCTL_ADD_QUAD(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree),
736fdfc0a83SWarner Losh 	    OID_AUTO, "deletes", CTLFLAG_RD,
737807e94b2SWarner Losh 	    &softc->deletes, "Number of BIO_DELETE requests");
738807e94b2SWarner Losh 
739ea657f2cSWarner Losh 	SYSCTL_ADD_UQUAD(&softc->sysctl_ctx,
740ea657f2cSWarner Losh 		SYSCTL_CHILDREN(softc->sysctl_tree), OID_AUTO,
741ea657f2cSWarner Losh 		"trim_count", CTLFLAG_RD, &softc->trim_count,
742ea657f2cSWarner Losh 		"Total number of unmap/dsm commands sent");
743ea657f2cSWarner Losh 	SYSCTL_ADD_UQUAD(&softc->sysctl_ctx,
744ea657f2cSWarner Losh 		SYSCTL_CHILDREN(softc->sysctl_tree), OID_AUTO,
745ea657f2cSWarner Losh 		"trim_ranges", CTLFLAG_RD, &softc->trim_ranges,
746ea657f2cSWarner Losh 		"Total number of ranges in unmap/dsm commands");
747ea657f2cSWarner Losh 	SYSCTL_ADD_UQUAD(&softc->sysctl_ctx,
748ea657f2cSWarner Losh 		SYSCTL_CHILDREN(softc->sysctl_tree), OID_AUTO,
749ea657f2cSWarner Losh 		"trim_lbas", CTLFLAG_RD, &softc->trim_lbas,
750ea657f2cSWarner Losh 		"Total lbas in the unmap/dsm commands sent");
751807e94b2SWarner Losh 
752fdfc0a83SWarner Losh 	SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree),
753fdfc0a83SWarner Losh 	    OID_AUTO, "rotating", CTLFLAG_RD, &nda_rotating_media, 1,
754baabaca3SWarner Losh 	    "Rotating media");
755baabaca3SWarner Losh 
75675ce4227SWarner Losh 	SYSCTL_ADD_PROC(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree),
75775ce4227SWarner Losh 	    OID_AUTO, "flags", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
75875ce4227SWarner Losh 	    softc, 0, ndaflagssysctl, "A",
75975ce4227SWarner Losh 	    "Flags for drive");
76075ce4227SWarner Losh 
761baabaca3SWarner Losh #ifdef CAM_IO_STATS
762baabaca3SWarner Losh 	softc->sysctl_stats_tree = SYSCTL_ADD_NODE(&softc->sysctl_stats_ctx,
763baabaca3SWarner Losh 		SYSCTL_CHILDREN(softc->sysctl_tree), OID_AUTO, "stats",
7647029da5cSPawel Biernacki 		CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "Statistics");
765baabaca3SWarner Losh 	if (softc->sysctl_stats_tree == NULL) {
766baabaca3SWarner Losh 		printf("ndasysctlinit: unable to allocate sysctl tree for stats\n");
767baabaca3SWarner Losh 		cam_periph_release(periph);
768baabaca3SWarner Losh 		return;
769baabaca3SWarner Losh 	}
770baabaca3SWarner Losh 	SYSCTL_ADD_INT(&softc->sysctl_stats_ctx,
771baabaca3SWarner Losh 		SYSCTL_CHILDREN(softc->sysctl_stats_tree),
772fdfc0a83SWarner Losh 		OID_AUTO, "timeouts", CTLFLAG_RD,
773baabaca3SWarner Losh 		&softc->timeouts, 0,
774baabaca3SWarner Losh 		"Device timeouts reported by the SIM");
775baabaca3SWarner Losh 	SYSCTL_ADD_INT(&softc->sysctl_stats_ctx,
776baabaca3SWarner Losh 		SYSCTL_CHILDREN(softc->sysctl_stats_tree),
777fdfc0a83SWarner Losh 		OID_AUTO, "errors", CTLFLAG_RD,
778baabaca3SWarner Losh 		&softc->errors, 0,
779baabaca3SWarner Losh 		"Transport errors reported by the SIM.");
780baabaca3SWarner Losh 	SYSCTL_ADD_INT(&softc->sysctl_stats_ctx,
781baabaca3SWarner Losh 		SYSCTL_CHILDREN(softc->sysctl_stats_tree),
782fdfc0a83SWarner Losh 		OID_AUTO, "pack_invalidations", CTLFLAG_RD,
783baabaca3SWarner Losh 		&softc->invalidations, 0,
784baabaca3SWarner Losh 		"Device pack invalidations.");
785baabaca3SWarner Losh #endif
786baabaca3SWarner Losh 
787d38677d2SWarner Losh #ifdef CAM_TEST_FAILURE
788d38677d2SWarner Losh 	SYSCTL_ADD_PROC(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree),
789d38677d2SWarner Losh 		OID_AUTO, "invalidate", CTLTYPE_U64 | CTLFLAG_RW | CTLFLAG_MPSAFE,
790d38677d2SWarner Losh 		periph, 0, cam_periph_invalidate_sysctl, "I",
791d38677d2SWarner Losh 		"Write 1 to invalidate the drive immediately");
792d38677d2SWarner Losh #endif
793d38677d2SWarner Losh 
794baabaca3SWarner Losh 	cam_iosched_sysctl_init(softc->cam_iosched, &softc->sysctl_ctx,
795baabaca3SWarner Losh 	    softc->sysctl_tree);
796baabaca3SWarner Losh 
797baabaca3SWarner Losh 	cam_periph_release(periph);
798baabaca3SWarner Losh }
799baabaca3SWarner Losh 
800baabaca3SWarner Losh static int
80175ce4227SWarner Losh ndaflagssysctl(SYSCTL_HANDLER_ARGS)
80275ce4227SWarner Losh {
80375ce4227SWarner Losh 	struct sbuf sbuf;
80475ce4227SWarner Losh 	struct nda_softc *softc = arg1;
80575ce4227SWarner Losh 	int error;
80675ce4227SWarner Losh 
80775ce4227SWarner Losh 	sbuf_new_for_sysctl(&sbuf, NULL, 0, req);
80875ce4227SWarner Losh 	if (softc->flags != 0)
80975ce4227SWarner Losh 		sbuf_printf(&sbuf, "0x%b", (unsigned)softc->flags, NDA_FLAG_STRING);
81075ce4227SWarner Losh 	else
81175ce4227SWarner Losh 		sbuf_printf(&sbuf, "0");
81275ce4227SWarner Losh 	error = sbuf_finish(&sbuf);
81375ce4227SWarner Losh 	sbuf_delete(&sbuf);
81475ce4227SWarner Losh 
81575ce4227SWarner Losh 	return (error);
81675ce4227SWarner Losh }
81775ce4227SWarner Losh 
81875ce4227SWarner Losh static int
819baabaca3SWarner Losh ndagetattr(struct bio *bp)
820baabaca3SWarner Losh {
821baabaca3SWarner Losh 	int ret;
822baabaca3SWarner Losh 	struct cam_periph *periph;
823baabaca3SWarner Losh 
82413532153SScott Long 	if (g_handleattr_int(bp, "GEOM::canspeedup", nda_enable_biospeedup))
82513532153SScott Long 		return (EJUSTRETURN);
82613532153SScott Long 
827baabaca3SWarner Losh 	periph = (struct cam_periph *)bp->bio_disk->d_drv1;
828baabaca3SWarner Losh 	cam_periph_lock(periph);
829baabaca3SWarner Losh 	ret = xpt_getattr(bp->bio_data, bp->bio_length, bp->bio_attribute,
830baabaca3SWarner Losh 	    periph->path);
831baabaca3SWarner Losh 	cam_periph_unlock(periph);
832baabaca3SWarner Losh 	if (ret == 0)
833baabaca3SWarner Losh 		bp->bio_completed = bp->bio_length;
834baabaca3SWarner Losh 	return ret;
835baabaca3SWarner Losh }
836baabaca3SWarner Losh 
837baabaca3SWarner Losh static cam_status
838baabaca3SWarner Losh ndaregister(struct cam_periph *periph, void *arg)
839baabaca3SWarner Losh {
840baabaca3SWarner Losh 	struct nda_softc *softc;
841baabaca3SWarner Losh 	struct disk *disk;
842baabaca3SWarner Losh 	struct ccb_pathinq cpi;
843baabaca3SWarner Losh 	const struct nvme_namespace_data *nsd;
844baabaca3SWarner Losh 	const struct nvme_controller_data *cd;
845baabaca3SWarner Losh 	char   announce_buf[80];
8460d787e9bSWojciech Macek 	uint8_t flbas_fmt, lbads, vwc_present;
847baabaca3SWarner Losh 	u_int maxio;
848baabaca3SWarner Losh 	int quirks;
849baabaca3SWarner Losh 
8509f8ed7e4SWarner Losh 	nsd = nvme_get_identify_ns(periph);
8519f8ed7e4SWarner Losh 	cd = nvme_get_identify_cntrl(periph);
852baabaca3SWarner Losh 
853baabaca3SWarner Losh 	softc = (struct nda_softc *)malloc(sizeof(*softc), M_DEVBUF,
854baabaca3SWarner Losh 	    M_NOWAIT | M_ZERO);
855baabaca3SWarner Losh 
856baabaca3SWarner Losh 	if (softc == NULL) {
857baabaca3SWarner Losh 		printf("ndaregister: Unable to probe new device. "
858baabaca3SWarner Losh 		    "Unable to allocate softc\n");
859baabaca3SWarner Losh 		return(CAM_REQ_CMP_ERR);
860baabaca3SWarner Losh 	}
861baabaca3SWarner Losh 
8620028abe6SWarner Losh 	if (cam_iosched_init(&softc->cam_iosched, periph) != 0) {
863baabaca3SWarner Losh 		printf("ndaregister: Unable to probe new device. "
864baabaca3SWarner Losh 		       "Unable to allocate iosched memory\n");
8652e1fccf2SConrad Meyer 		free(softc, M_DEVBUF);
866baabaca3SWarner Losh 		return(CAM_REQ_CMP_ERR);
867baabaca3SWarner Losh 	}
868baabaca3SWarner Losh 
869baabaca3SWarner Losh 	/* ident_data parsing */
870baabaca3SWarner Losh 
871baabaca3SWarner Losh 	periph->softc = softc;
872baabaca3SWarner Losh 	softc->quirks = NDA_Q_NONE;
873762a7f4fSWarner Losh 	xpt_path_inq(&cpi, periph->path);
874baabaca3SWarner Losh 	TASK_INIT(&softc->sysctl_task, 0, ndasysctlinit, periph);
875baabaca3SWarner Losh 
876baabaca3SWarner Losh 	/*
877baabaca3SWarner Losh 	 * The name space ID is the lun, save it for later I/O
878baabaca3SWarner Losh 	 */
8791d6e8110SWarner Losh 	softc->nsid = (uint32_t)xpt_path_lun_id(periph->path);
880baabaca3SWarner Losh 
881baabaca3SWarner Losh 	/*
882baabaca3SWarner Losh 	 * Register this media as a disk
883baabaca3SWarner Losh 	 */
884baabaca3SWarner Losh 	(void)cam_periph_hold(periph, PRIBIO);
885baabaca3SWarner Losh 	cam_periph_unlock(periph);
886baabaca3SWarner Losh 	snprintf(announce_buf, sizeof(announce_buf),
887baabaca3SWarner Losh 	    "kern.cam.nda.%d.quirks", periph->unit_number);
888baabaca3SWarner Losh 	quirks = softc->quirks;
889baabaca3SWarner Losh 	TUNABLE_INT_FETCH(announce_buf, &quirks);
890baabaca3SWarner Losh 	softc->quirks = quirks;
891baabaca3SWarner Losh 	cam_iosched_set_sort_queue(softc->cam_iosched, 0);
892baabaca3SWarner Losh 	softc->disk = disk = disk_alloc();
89317160457SAlexander Motin 	disk->d_rotation_rate = DISK_RR_NON_ROTATING;
894baabaca3SWarner Losh 	disk->d_open = ndaopen;
895baabaca3SWarner Losh 	disk->d_close = ndaclose;
896baabaca3SWarner Losh 	disk->d_strategy = ndastrategy;
8972446ce7aSWarner Losh 	disk->d_ioctl = ndaioctl;
898baabaca3SWarner Losh 	disk->d_getattr = ndagetattr;
899e07ac3f2SJohn Baldwin 	if (cam_sim_pollable(periph->sim))
900baabaca3SWarner Losh 		disk->d_dump = ndadump;
901baabaca3SWarner Losh 	disk->d_gone = ndadiskgonecb;
902baabaca3SWarner Losh 	disk->d_name = "nda";
903baabaca3SWarner Losh 	disk->d_drv1 = periph;
904baabaca3SWarner Losh 	disk->d_unit = periph->unit_number;
905baabaca3SWarner Losh 	maxio = cpi.maxio;		/* Honor max I/O size of SIM */
906baabaca3SWarner Losh 	if (maxio == 0)
907baabaca3SWarner Losh 		maxio = DFLTPHYS;	/* traditional default */
908cd853791SKonstantin Belousov 	else if (maxio > maxphys)
909cd853791SKonstantin Belousov 		maxio = maxphys;	/* for safety */
910baabaca3SWarner Losh 	disk->d_maxsize = maxio;
9110d787e9bSWojciech Macek 	flbas_fmt = (nsd->flbas >> NVME_NS_DATA_FLBAS_FORMAT_SHIFT) &
9120d787e9bSWojciech Macek 		NVME_NS_DATA_FLBAS_FORMAT_MASK;
9130d787e9bSWojciech Macek 	lbads = (nsd->lbaf[flbas_fmt] >> NVME_NS_DATA_LBAF_LBADS_SHIFT) &
9140d787e9bSWojciech Macek 		NVME_NS_DATA_LBAF_LBADS_MASK;
9150d787e9bSWojciech Macek 	disk->d_sectorsize = 1 << lbads;
916baabaca3SWarner Losh 	disk->d_mediasize = (off_t)(disk->d_sectorsize * nsd->nsze);
917baabaca3SWarner Losh 	disk->d_delmaxsize = disk->d_mediasize;
918baabaca3SWarner Losh 	disk->d_flags = DISKFLAG_DIRECT_COMPLETION;
91987b3975eSChuck Tuffli 	if (nvme_ctrlr_has_dataset_mgmt(cd))
920baabaca3SWarner Losh 		disk->d_flags |= DISKFLAG_CANDELETE;
9210d787e9bSWojciech Macek 	vwc_present = (cd->vwc >> NVME_CTRLR_DATA_VWC_PRESENT_SHIFT) &
9220d787e9bSWojciech Macek 		NVME_CTRLR_DATA_VWC_PRESENT_MASK;
9230d787e9bSWojciech Macek 	if (vwc_present)
924baabaca3SWarner Losh 		disk->d_flags |= DISKFLAG_CANFLUSHCACHE;
925baabaca3SWarner Losh 	if ((cpi.hba_misc & PIM_UNMAPPED) != 0) {
926baabaca3SWarner Losh 		disk->d_flags |= DISKFLAG_UNMAPPED_BIO;
927baabaca3SWarner Losh 		softc->unmappedio = 1;
928baabaca3SWarner Losh 	}
929baabaca3SWarner Losh 	/*
930baabaca3SWarner Losh 	 * d_ident and d_descr are both far bigger than the length of either
931baabaca3SWarner Losh 	 *  the serial or model number strings.
932baabaca3SWarner Losh 	 */
933*3090d504SKenneth D. Merry 	cam_strvis_flag(disk->d_descr, cd->mn, NVME_MODEL_NUMBER_LENGTH,
934*3090d504SKenneth D. Merry 	    sizeof(disk->d_descr), CAM_STRVIS_FLAG_NONASCII_SPC);
935*3090d504SKenneth D. Merry 
936*3090d504SKenneth D. Merry 	cam_strvis_flag(disk->d_ident, cd->sn, NVME_SERIAL_NUMBER_LENGTH,
937*3090d504SKenneth D. Merry 	    sizeof(disk->d_ident), CAM_STRVIS_FLAG_NONASCII_SPC);
938*3090d504SKenneth D. Merry 
939baabaca3SWarner Losh 	disk->d_hba_vendor = cpi.hba_vendor;
940baabaca3SWarner Losh 	disk->d_hba_device = cpi.hba_device;
941baabaca3SWarner Losh 	disk->d_hba_subvendor = cpi.hba_subvendor;
942baabaca3SWarner Losh 	disk->d_hba_subdevice = cpi.hba_subdevice;
943b5961be1SEdward Tomasz Napierala 	snprintf(disk->d_attachment, sizeof(disk->d_attachment),
944b5961be1SEdward Tomasz Napierala 	    "%s%d", cpi.dev_name, cpi.unit_number);
9451c7decd4SAlexander Motin 	if (((nsd->nsfeat >> NVME_NS_DATA_NSFEAT_NPVALID_SHIFT) &
9461c7decd4SAlexander Motin 	    NVME_NS_DATA_NSFEAT_NPVALID_MASK) != 0 && nsd->npwg != 0)
9471c7decd4SAlexander Motin 		disk->d_stripesize = ((nsd->npwg + 1) * disk->d_sectorsize);
9481c7decd4SAlexander Motin 	else
9491c7decd4SAlexander Motin 		disk->d_stripesize = nsd->noiob * disk->d_sectorsize;
950baabaca3SWarner Losh 	disk->d_stripeoffset = 0;
951baabaca3SWarner Losh 	disk->d_devstat = devstat_new_entry(periph->periph_name,
952baabaca3SWarner Losh 	    periph->unit_number, disk->d_sectorsize,
953baabaca3SWarner Losh 	    DEVSTAT_ALL_SUPPORTED,
954baabaca3SWarner Losh 	    DEVSTAT_TYPE_DIRECT | XPORT_DEVSTAT_TYPE(cpi.transport),
955baabaca3SWarner Losh 	    DEVSTAT_PRIORITY_DISK);
956d45e1674SWarner Losh 	/*
957d45e1674SWarner Losh 	 * Add alias for older nvd drives to ease transition.
958d45e1674SWarner Losh 	 */
9591868c484SWarner Losh 	if (nda_nvd_compat)
960fd26063fSWarner Losh 		disk_add_alias(disk, "nvd");
961baabaca3SWarner Losh 
962baabaca3SWarner Losh 	/*
963baabaca3SWarner Losh 	 * Acquire a reference to the periph before we register with GEOM.
964baabaca3SWarner Losh 	 * We'll release this reference once GEOM calls us back (via
965baabaca3SWarner Losh 	 * ndadiskgonecb()) telling us that our provider has been freed.
966baabaca3SWarner Losh 	 */
96799e7a4adSScott Long 	if (cam_periph_acquire(periph) != 0) {
968baabaca3SWarner Losh 		xpt_print(periph->path, "%s: lost periph during "
969baabaca3SWarner Losh 			  "registration!\n", __func__);
970baabaca3SWarner Losh 		cam_periph_lock(periph);
971baabaca3SWarner Losh 		return (CAM_REQ_CMP_ERR);
972baabaca3SWarner Losh 	}
973baabaca3SWarner Losh 	disk_create(softc->disk, DISK_VERSION);
974baabaca3SWarner Losh 	cam_periph_lock(periph);
975baabaca3SWarner Losh 	cam_periph_unhold(periph);
976baabaca3SWarner Losh 
977baabaca3SWarner Losh 	snprintf(announce_buf, sizeof(announce_buf),
978baabaca3SWarner Losh 		"%juMB (%ju %u byte sectors)",
979baabaca3SWarner Losh 	    (uintmax_t)((uintmax_t)disk->d_mediasize / (1024*1024)),
980baabaca3SWarner Losh 		(uintmax_t)disk->d_mediasize / disk->d_sectorsize,
981baabaca3SWarner Losh 		disk->d_sectorsize);
982baabaca3SWarner Losh 	xpt_announce_periph(periph, announce_buf);
983baabaca3SWarner Losh 	xpt_announce_quirks(periph, softc->quirks, NDA_Q_BIT_STRING);
984baabaca3SWarner Losh 
985baabaca3SWarner Losh 	/*
986baabaca3SWarner Losh 	 * Create our sysctl variables, now that we know
987baabaca3SWarner Losh 	 * we have successfully attached.
988baabaca3SWarner Losh 	 */
98999e7a4adSScott Long 	if (cam_periph_acquire(periph) == 0)
990baabaca3SWarner Losh 		taskqueue_enqueue(taskqueue_thread, &softc->sysctl_task);
991baabaca3SWarner Losh 
992baabaca3SWarner Losh 	/*
993baabaca3SWarner Losh 	 * Register for device going away and info about the drive
994baabaca3SWarner Losh 	 * changing (though with NVMe, it can't)
995baabaca3SWarner Losh 	 */
996baabaca3SWarner Losh 	xpt_register_async(AC_LOST_DEVICE | AC_ADVINFO_CHANGED,
997baabaca3SWarner Losh 	    ndaasync, periph, periph->path);
998baabaca3SWarner Losh 
999baabaca3SWarner Losh 	softc->state = NDA_STATE_NORMAL;
1000baabaca3SWarner Losh 	return(CAM_REQ_CMP);
1001baabaca3SWarner Losh }
1002baabaca3SWarner Losh 
1003baabaca3SWarner Losh static void
1004baabaca3SWarner Losh ndastart(struct cam_periph *periph, union ccb *start_ccb)
1005baabaca3SWarner Losh {
1006baabaca3SWarner Losh 	struct nda_softc *softc = (struct nda_softc *)periph->softc;
1007baabaca3SWarner Losh 	struct ccb_nvmeio *nvmeio = &start_ccb->nvmeio;
1008baabaca3SWarner Losh 
1009baabaca3SWarner Losh 	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("ndastart\n"));
1010baabaca3SWarner Losh 
1011baabaca3SWarner Losh 	switch (softc->state) {
1012baabaca3SWarner Losh 	case NDA_STATE_NORMAL:
1013baabaca3SWarner Losh 	{
1014baabaca3SWarner Losh 		struct bio *bp;
1015baabaca3SWarner Losh 
1016baabaca3SWarner Losh 		bp = cam_iosched_next_bio(softc->cam_iosched);
1017baabaca3SWarner Losh 		CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("ndastart: bio %p\n", bp));
1018baabaca3SWarner Losh 		if (bp == NULL) {
1019baabaca3SWarner Losh 			xpt_release_ccb(start_ccb);
1020baabaca3SWarner Losh 			break;
1021baabaca3SWarner Losh 		}
1022baabaca3SWarner Losh 
1023baabaca3SWarner Losh 		switch (bp->bio_cmd) {
1024baabaca3SWarner Losh 		case BIO_WRITE:
1025baabaca3SWarner Losh 			softc->flags |= NDA_FLAG_DIRTY;
1026baabaca3SWarner Losh 			/* FALLTHROUGH */
1027baabaca3SWarner Losh 		case BIO_READ:
1028baabaca3SWarner Losh 		{
1029d38677d2SWarner Losh #ifdef CAM_TEST_FAILURE
1030baabaca3SWarner Losh 			int fail = 0;
1031baabaca3SWarner Losh 
1032baabaca3SWarner Losh 			/*
1033baabaca3SWarner Losh 			 * Support the failure ioctls.  If the command is a
1034baabaca3SWarner Losh 			 * read, and there are pending forced read errors, or
1035baabaca3SWarner Losh 			 * if a write and pending write errors, then fail this
1036baabaca3SWarner Losh 			 * operation with EIO.  This is useful for testing
1037baabaca3SWarner Losh 			 * purposes.  Also, support having every Nth read fail.
1038baabaca3SWarner Losh 			 *
1039baabaca3SWarner Losh 			 * This is a rather blunt tool.
1040baabaca3SWarner Losh 			 */
1041baabaca3SWarner Losh 			if (bp->bio_cmd == BIO_READ) {
1042baabaca3SWarner Losh 				if (softc->force_read_error) {
1043baabaca3SWarner Losh 					softc->force_read_error--;
1044baabaca3SWarner Losh 					fail = 1;
1045baabaca3SWarner Losh 				}
1046baabaca3SWarner Losh 				if (softc->periodic_read_error > 0) {
1047baabaca3SWarner Losh 					if (++softc->periodic_read_count >=
1048baabaca3SWarner Losh 					    softc->periodic_read_error) {
1049baabaca3SWarner Losh 						softc->periodic_read_count = 0;
1050baabaca3SWarner Losh 						fail = 1;
1051baabaca3SWarner Losh 					}
1052baabaca3SWarner Losh 				}
1053baabaca3SWarner Losh 			} else {
1054baabaca3SWarner Losh 				if (softc->force_write_error) {
1055baabaca3SWarner Losh 					softc->force_write_error--;
1056baabaca3SWarner Losh 					fail = 1;
1057baabaca3SWarner Losh 				}
1058baabaca3SWarner Losh 			}
1059baabaca3SWarner Losh 			if (fail) {
1060baabaca3SWarner Losh 				biofinish(bp, NULL, EIO);
1061baabaca3SWarner Losh 				xpt_release_ccb(start_ccb);
1062baabaca3SWarner Losh 				ndaschedule(periph);
1063baabaca3SWarner Losh 				return;
1064baabaca3SWarner Losh 			}
1065baabaca3SWarner Losh #endif
1066baabaca3SWarner Losh 			KASSERT((bp->bio_flags & BIO_UNMAPPED) == 0 ||
1067baabaca3SWarner Losh 			    round_page(bp->bio_bcount + bp->bio_ma_offset) /
1068baabaca3SWarner Losh 			    PAGE_SIZE == bp->bio_ma_n,
1069baabaca3SWarner Losh 			    ("Short bio %p", bp));
1070baabaca3SWarner Losh 			nda_nvme_rw_bio(softc, &start_ccb->nvmeio, bp, bp->bio_cmd == BIO_READ ?
1071baabaca3SWarner Losh 			    NVME_OPC_READ : NVME_OPC_WRITE);
1072baabaca3SWarner Losh 			break;
1073baabaca3SWarner Losh 		}
1074baabaca3SWarner Losh 		case BIO_DELETE:
1075baabaca3SWarner Losh 		{
1076807e94b2SWarner Losh 			struct nvme_dsm_range *dsm_range, *dsm_end;
1077807e94b2SWarner Losh 			struct nda_trim_request *trim;
1078807e94b2SWarner Losh 			struct bio *bp1;
1079807e94b2SWarner Losh 			int ents;
1080ea657f2cSWarner Losh 			uint32_t totalcount = 0, ranges = 0;
1081baabaca3SWarner Losh 
1082807e94b2SWarner Losh 			trim = malloc(sizeof(*trim), M_NVMEDA, M_ZERO | M_NOWAIT);
1083807e94b2SWarner Losh 			if (trim == NULL) {
108460b7691dSWarner Losh 				biofinish(bp, NULL, ENOMEM);
108560b7691dSWarner Losh 				xpt_release_ccb(start_ccb);
108660b7691dSWarner Losh 				ndaschedule(periph);
108760b7691dSWarner Losh 				return;
108860b7691dSWarner Losh 			}
1089807e94b2SWarner Losh 			TAILQ_INIT(&trim->bps);
1090807e94b2SWarner Losh 			bp1 = bp;
109193489854SBrooks Davis 			ents = min(nitems(trim->dsm), nda_max_trim_entries);
1092ead4c1b4SWarner Losh 			ents = max(ents, 1);
109393489854SBrooks Davis 			dsm_range = trim->dsm;
1094807e94b2SWarner Losh 			dsm_end = dsm_range + ents;
1095807e94b2SWarner Losh 			do {
1096807e94b2SWarner Losh 				TAILQ_INSERT_TAIL(&trim->bps, bp1, bio_queue);
1097baabaca3SWarner Losh 				dsm_range->length =
1098807e94b2SWarner Losh 				    htole32(bp1->bio_bcount / softc->disk->d_sectorsize);
1099baabaca3SWarner Losh 				dsm_range->starting_lba =
1100afdbfe1eSWarner Losh 				    htole64(bp1->bio_offset / softc->disk->d_sectorsize);
1101ea657f2cSWarner Losh 				ranges++;
1102ea657f2cSWarner Losh 				totalcount += dsm_range->length;
1103807e94b2SWarner Losh 				dsm_range++;
1104807e94b2SWarner Losh 				if (dsm_range >= dsm_end)
1105807e94b2SWarner Losh 					break;
1106807e94b2SWarner Losh 				bp1 = cam_iosched_next_trim(softc->cam_iosched);
1107807e94b2SWarner Losh 				/* XXX -- Could collapse adjacent ranges, but we don't for now */
1108807e94b2SWarner Losh 				/* XXX -- Could limit based on total payload size */
1109807e94b2SWarner Losh 			} while (bp1 != NULL);
1110807e94b2SWarner Losh 			start_ccb->ccb_trim = trim;
111193489854SBrooks Davis 			nda_nvme_trim(softc, &start_ccb->nvmeio, trim->dsm,
111293489854SBrooks Davis 			    dsm_range - trim->dsm);
1113807e94b2SWarner Losh 			start_ccb->ccb_state = NDA_CCB_TRIM;
1114ea657f2cSWarner Losh 			softc->trim_count++;
1115ea657f2cSWarner Losh 			softc->trim_ranges += ranges;
1116ea657f2cSWarner Losh 			softc->trim_lbas += totalcount;
1117851063e1SWarner Losh 			/*
1118851063e1SWarner Losh 			 * Note: We can have multiple TRIMs in flight, so we don't call
1119851063e1SWarner Losh 			 * cam_iosched_submit_trim(softc->cam_iosched);
1120851063e1SWarner Losh 			 * since that forces the I/O scheduler to only schedule one at a time.
1121851063e1SWarner Losh 			 * On NVMe drives, this is a performance disaster.
1122851063e1SWarner Losh 			 */
1123baabaca3SWarner Losh 			goto out;
1124baabaca3SWarner Losh 		}
1125baabaca3SWarner Losh 		case BIO_FLUSH:
1126baabaca3SWarner Losh 			nda_nvme_flush(softc, nvmeio);
1127baabaca3SWarner Losh 			break;
1128d176b803SScott Long 		default:
1129d176b803SScott Long 			biofinish(bp, NULL, EOPNOTSUPP);
1130d176b803SScott Long 			xpt_release_ccb(start_ccb);
1131d176b803SScott Long 			ndaschedule(periph);
1132d176b803SScott Long 			return;
1133baabaca3SWarner Losh 		}
1134807e94b2SWarner Losh 		start_ccb->ccb_state = NDA_CCB_BUFFER_IO;
1135807e94b2SWarner Losh 		start_ccb->ccb_bp = bp;
1136baabaca3SWarner Losh out:
1137807e94b2SWarner Losh 		start_ccb->ccb_h.flags |= CAM_UNLOCKED;
1138baabaca3SWarner Losh 		softc->outstanding_cmds++;
1139b1988d44SWarner Losh 		softc->refcount++;			/* For submission only */
1140baabaca3SWarner Losh 		cam_periph_unlock(periph);
1141baabaca3SWarner Losh 		xpt_action(start_ccb);
1142baabaca3SWarner Losh 		cam_periph_lock(periph);
1143b1988d44SWarner Losh 		softc->refcount--;			/* Submission done */
1144baabaca3SWarner Losh 
1145baabaca3SWarner Losh 		/* May have more work to do, so ensure we stay scheduled */
1146baabaca3SWarner Losh 		ndaschedule(periph);
1147baabaca3SWarner Losh 		break;
1148baabaca3SWarner Losh 		}
1149baabaca3SWarner Losh 	}
1150baabaca3SWarner Losh }
1151baabaca3SWarner Losh 
1152baabaca3SWarner Losh static void
1153baabaca3SWarner Losh ndadone(struct cam_periph *periph, union ccb *done_ccb)
1154baabaca3SWarner Losh {
1155baabaca3SWarner Losh 	struct nda_softc *softc;
1156baabaca3SWarner Losh 	struct ccb_nvmeio *nvmeio = &done_ccb->nvmeio;
1157baabaca3SWarner Losh 	struct cam_path *path;
1158baabaca3SWarner Losh 	int state;
1159baabaca3SWarner Losh 
1160baabaca3SWarner Losh 	softc = (struct nda_softc *)periph->softc;
1161baabaca3SWarner Losh 	path = done_ccb->ccb_h.path;
1162baabaca3SWarner Losh 
1163baabaca3SWarner Losh 	CAM_DEBUG(path, CAM_DEBUG_TRACE, ("ndadone\n"));
1164baabaca3SWarner Losh 
1165807e94b2SWarner Losh 	state = nvmeio->ccb_state & NDA_CCB_TYPE_MASK;
1166baabaca3SWarner Losh 	switch (state) {
1167baabaca3SWarner Losh 	case NDA_CCB_BUFFER_IO:
1168baabaca3SWarner Losh 	case NDA_CCB_TRIM:
1169baabaca3SWarner Losh 	{
1170baabaca3SWarner Losh 		int error;
1171baabaca3SWarner Losh 
1172baabaca3SWarner Losh 		cam_periph_lock(periph);
1173baabaca3SWarner Losh 		if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
1174baabaca3SWarner Losh 			error = ndaerror(done_ccb, 0, 0);
1175baabaca3SWarner Losh 			if (error == ERESTART) {
1176baabaca3SWarner Losh 				/* A retry was scheduled, so just return. */
1177baabaca3SWarner Losh 				cam_periph_unlock(periph);
1178baabaca3SWarner Losh 				return;
1179baabaca3SWarner Losh 			}
1180baabaca3SWarner Losh 			if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
1181baabaca3SWarner Losh 				cam_release_devq(path,
1182baabaca3SWarner Losh 						 /*relsim_flags*/0,
1183baabaca3SWarner Losh 						 /*reduction*/0,
1184baabaca3SWarner Losh 						 /*timeout*/0,
1185baabaca3SWarner Losh 						 /*getcount_only*/0);
1186baabaca3SWarner Losh 		} else {
1187baabaca3SWarner Losh 			if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
1188baabaca3SWarner Losh 				panic("REQ_CMP with QFRZN");
1189baabaca3SWarner Losh 			error = 0;
1190baabaca3SWarner Losh 		}
1191807e94b2SWarner Losh 		if (state == NDA_CCB_BUFFER_IO) {
1192807e94b2SWarner Losh 			struct bio *bp;
1193807e94b2SWarner Losh 
1194807e94b2SWarner Losh 			bp = (struct bio *)done_ccb->ccb_bp;
1195baabaca3SWarner Losh 			bp->bio_error = error;
1196baabaca3SWarner Losh 			if (error != 0) {
1197baabaca3SWarner Losh 				bp->bio_resid = bp->bio_bcount;
1198baabaca3SWarner Losh 				bp->bio_flags |= BIO_ERROR;
1199baabaca3SWarner Losh 			} else {
1200baabaca3SWarner Losh 				bp->bio_resid = 0;
1201baabaca3SWarner Losh 			}
1202baabaca3SWarner Losh 			softc->outstanding_cmds--;
1203baabaca3SWarner Losh 
12049754579bSWarner Losh 			/*
12059754579bSWarner Losh 			 * We need to call cam_iosched before we call biodone so that we
12069754579bSWarner Losh 			 * don't measure any activity that happens in the completion
12079754579bSWarner Losh 			 * routine, which in the case of sendfile can be quite
12089754579bSWarner Losh 			 * extensive.
12099754579bSWarner Losh 			 */
1210baabaca3SWarner Losh 			cam_iosched_bio_complete(softc->cam_iosched, bp, done_ccb);
1211baabaca3SWarner Losh 			xpt_release_ccb(done_ccb);
1212807e94b2SWarner Losh 			ndaschedule(periph);
1213807e94b2SWarner Losh 			cam_periph_unlock(periph);
1214807e94b2SWarner Losh 			biodone(bp);
1215807e94b2SWarner Losh 		} else { /* state == NDA_CCB_TRIM */
1216807e94b2SWarner Losh 			struct nda_trim_request *trim;
1217807e94b2SWarner Losh 			struct bio *bp1, *bp2;
12184d87e271SWarner Losh 			TAILQ_HEAD(, bio) queue;
1219baabaca3SWarner Losh 
1220807e94b2SWarner Losh 			trim = nvmeio->ccb_trim;
1221baabaca3SWarner Losh 			TAILQ_INIT(&queue);
1222807e94b2SWarner Losh 			TAILQ_CONCAT(&queue, &trim->bps, bio_queue);
1223807e94b2SWarner Losh 			free(trim, M_NVMEDA);
1224807e94b2SWarner Losh 
1225851063e1SWarner Losh 			/*
1226851063e1SWarner Losh 			 * Since we can have multiple trims in flight, we don't
1227851063e1SWarner Losh 			 * need to call this here.
1228851063e1SWarner Losh 			 * cam_iosched_trim_done(softc->cam_iosched);
1229851063e1SWarner Losh 			 */
1230807e94b2SWarner Losh 			/*
1231807e94b2SWarner Losh 			 * The the I/O scheduler that we're finishing the I/O
1232807e94b2SWarner Losh 			 * so we can keep book. The first one we pass in the CCB
1233807e94b2SWarner Losh 			 * which has the timing information. The rest we pass in NULL
1234807e94b2SWarner Losh 			 * so we can keep proper counts.
1235807e94b2SWarner Losh 			 */
1236807e94b2SWarner Losh 			bp1 = TAILQ_FIRST(&queue);
1237807e94b2SWarner Losh 			cam_iosched_bio_complete(softc->cam_iosched, bp1, done_ccb);
1238807e94b2SWarner Losh 			xpt_release_ccb(done_ccb);
1239b1988d44SWarner Losh 			softc->outstanding_cmds--;
1240baabaca3SWarner Losh 			ndaschedule(periph);
1241baabaca3SWarner Losh 			cam_periph_unlock(periph);
1242807e94b2SWarner Losh 			while ((bp2 = TAILQ_FIRST(&queue)) != NULL) {
1243807e94b2SWarner Losh 				TAILQ_REMOVE(&queue, bp2, bio_queue);
1244807e94b2SWarner Losh 				bp2->bio_error = error;
1245baabaca3SWarner Losh 				if (error != 0) {
1246807e94b2SWarner Losh 					bp2->bio_flags |= BIO_ERROR;
1247807e94b2SWarner Losh 					bp2->bio_resid = bp1->bio_bcount;
1248baabaca3SWarner Losh 				} else
1249807e94b2SWarner Losh 					bp2->bio_resid = 0;
1250807e94b2SWarner Losh 				if (bp1 != bp2)
1251807e94b2SWarner Losh 					cam_iosched_bio_complete(softc->cam_iosched, bp2, NULL);
1252807e94b2SWarner Losh 				biodone(bp2);
1253baabaca3SWarner Losh 			}
1254baabaca3SWarner Losh 		}
1255baabaca3SWarner Losh 		return;
1256baabaca3SWarner Losh 	}
1257baabaca3SWarner Losh 	case NDA_CCB_DUMP:
1258baabaca3SWarner Losh 		/* No-op.  We're polling */
1259baabaca3SWarner Losh 		return;
12602446ce7aSWarner Losh 	case NDA_CCB_PASS:
1261f8503fdeSWarner Losh 		/* NVME_PASSTHROUGH_CMD runs this CCB and releases it */
12622446ce7aSWarner Losh 		return;
1263baabaca3SWarner Losh 	default:
1264baabaca3SWarner Losh 		break;
1265baabaca3SWarner Losh 	}
1266baabaca3SWarner Losh 	xpt_release_ccb(done_ccb);
1267baabaca3SWarner Losh }
1268baabaca3SWarner Losh 
1269baabaca3SWarner Losh static int
1270baabaca3SWarner Losh ndaerror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags)
1271baabaca3SWarner Losh {
12721bc9ca3bSWarner Losh #ifdef CAM_IO_STATS
12731bc9ca3bSWarner Losh 	struct nda_softc *softc;
12741bc9ca3bSWarner Losh 	struct cam_periph *periph;
12751bc9ca3bSWarner Losh 
12761bc9ca3bSWarner Losh 	periph = xpt_path_periph(ccb->ccb_h.path);
12771bc9ca3bSWarner Losh 	softc = (struct nda_softc *)periph->softc;
12781bc9ca3bSWarner Losh #endif
1279baabaca3SWarner Losh 
1280baabaca3SWarner Losh 	switch (ccb->ccb_h.status & CAM_STATUS_MASK) {
1281baabaca3SWarner Losh 	case CAM_CMD_TIMEOUT:
1282baabaca3SWarner Losh #ifdef CAM_IO_STATS
1283baabaca3SWarner Losh 		softc->timeouts++;
1284baabaca3SWarner Losh #endif
1285baabaca3SWarner Losh 		break;
1286baabaca3SWarner Losh 	case CAM_REQ_ABORTED:
1287baabaca3SWarner Losh 	case CAM_REQ_CMP_ERR:
1288baabaca3SWarner Losh 	case CAM_REQ_TERMIO:
1289baabaca3SWarner Losh 	case CAM_UNREC_HBA_ERROR:
1290baabaca3SWarner Losh 	case CAM_DATA_RUN_ERR:
1291baabaca3SWarner Losh 	case CAM_ATA_STATUS_ERROR:
1292baabaca3SWarner Losh #ifdef CAM_IO_STATS
1293baabaca3SWarner Losh 		softc->errors++;
1294baabaca3SWarner Losh #endif
1295baabaca3SWarner Losh 		break;
1296baabaca3SWarner Losh 	default:
1297baabaca3SWarner Losh 		break;
1298baabaca3SWarner Losh 	}
1299baabaca3SWarner Losh 
1300553484aeSWarner Losh 	return(cam_periph_error(ccb, cam_flags, sense_flags));
1301baabaca3SWarner Losh }
1302baabaca3SWarner Losh 
1303baabaca3SWarner Losh /*
1304baabaca3SWarner Losh  * Step through all NDA peripheral drivers, and if the device is still open,
1305baabaca3SWarner Losh  * sync the disk cache to physical media.
1306baabaca3SWarner Losh  */
1307baabaca3SWarner Losh static void
1308baabaca3SWarner Losh ndaflush(void)
1309baabaca3SWarner Losh {
1310baabaca3SWarner Losh 	struct cam_periph *periph;
1311baabaca3SWarner Losh 	struct nda_softc *softc;
1312baabaca3SWarner Losh 	union ccb *ccb;
1313baabaca3SWarner Losh 	int error;
1314baabaca3SWarner Losh 
1315baabaca3SWarner Losh 	CAM_PERIPH_FOREACH(periph, &ndadriver) {
1316baabaca3SWarner Losh 		softc = (struct nda_softc *)periph->softc;
13179d602e4eSWarner Losh 
1318baabaca3SWarner Losh 		if (SCHEDULER_STOPPED()) {
13199d602e4eSWarner Losh 			/*
13209d602e4eSWarner Losh 			 * If we paniced with the lock held or the periph is not
13219d602e4eSWarner Losh 			 * open, do not recurse.  Otherwise, call ndadump since
13229d602e4eSWarner Losh 			 * that avoids the sleeping cam_periph_getccb does if no
13239d602e4eSWarner Losh 			 * CCBs are available.
13249d602e4eSWarner Losh 			 */
1325baabaca3SWarner Losh 			if (!cam_periph_owned(periph) &&
1326baabaca3SWarner Losh 			    (softc->flags & NDA_FLAG_OPEN)) {
1327baabaca3SWarner Losh 				ndadump(softc->disk, NULL, 0, 0, 0);
1328baabaca3SWarner Losh 			}
1329baabaca3SWarner Losh 			continue;
1330baabaca3SWarner Losh 		}
13319d602e4eSWarner Losh 
1332baabaca3SWarner Losh 		/*
13339d602e4eSWarner Losh 		 * We only sync the cache if the drive is still open
1334baabaca3SWarner Losh 		 */
13359d602e4eSWarner Losh 		cam_periph_lock(periph);
1336baabaca3SWarner Losh 		if ((softc->flags & NDA_FLAG_OPEN) == 0) {
1337baabaca3SWarner Losh 			cam_periph_unlock(periph);
1338baabaca3SWarner Losh 			continue;
1339baabaca3SWarner Losh 		}
1340baabaca3SWarner Losh 
1341baabaca3SWarner Losh 		ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL);
1342baabaca3SWarner Losh 		nda_nvme_flush(softc, &ccb->nvmeio);
1343baabaca3SWarner Losh 		error = cam_periph_runccb(ccb, ndaerror, /*cam_flags*/0,
1344baabaca3SWarner Losh 		    /*sense_flags*/ SF_NO_RECOVERY | SF_NO_RETRY,
1345baabaca3SWarner Losh 		    softc->disk->d_devstat);
1346baabaca3SWarner Losh 		if (error != 0)
1347baabaca3SWarner Losh 			xpt_print(periph->path, "Synchronize cache failed\n");
1348baabaca3SWarner Losh 		xpt_release_ccb(ccb);
1349baabaca3SWarner Losh 		cam_periph_unlock(periph);
1350baabaca3SWarner Losh 	}
1351baabaca3SWarner Losh }
1352baabaca3SWarner Losh 
1353baabaca3SWarner Losh static void
1354baabaca3SWarner Losh ndashutdown(void *arg, int howto)
1355baabaca3SWarner Losh {
1356baabaca3SWarner Losh 
1357baabaca3SWarner Losh 	ndaflush();
1358baabaca3SWarner Losh }
1359baabaca3SWarner Losh 
1360baabaca3SWarner Losh static void
1361baabaca3SWarner Losh ndasuspend(void *arg)
1362baabaca3SWarner Losh {
1363baabaca3SWarner Losh 
1364baabaca3SWarner Losh 	ndaflush();
1365baabaca3SWarner Losh }
1366