xref: /freebsd/sys/cam/nvme/nvme_da.c (revision d068ea16e3264c2d62472a8acf794262cfe703dd)
1baabaca3SWarner Losh /*-
2891c6986SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
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
10891c6986SWarner Losh  *    notice, this list of conditions and the following disclaimer.
11baabaca3SWarner Losh  * 2. Redistributions in binary form must reproduce the above copyright
12baabaca3SWarner Losh  *    notice, this list of conditions and the following disclaimer in the
13baabaca3SWarner Losh  *    documentation and/or other materials provided with the distribution.
14baabaca3SWarner Losh  *
15891c6986SWarner Losh  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16891c6986SWarner Losh  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17891c6986SWarner Losh  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18891c6986SWarner Losh  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19891c6986SWarner Losh  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20891c6986SWarner Losh  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21891c6986SWarner Losh  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22891c6986SWarner Losh  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23891c6986SWarner Losh  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24891c6986SWarner Losh  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25891c6986SWarner Losh  * 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/param.h>
32baabaca3SWarner Losh 
33baabaca3SWarner Losh #ifdef _KERNEL
34baabaca3SWarner Losh #include <sys/systm.h>
35baabaca3SWarner Losh #include <sys/kernel.h>
36baabaca3SWarner Losh #include <sys/bio.h>
37baabaca3SWarner Losh #include <sys/sysctl.h>
38baabaca3SWarner Losh #include <sys/taskqueue.h>
39baabaca3SWarner Losh #include <sys/lock.h>
40baabaca3SWarner Losh #include <sys/mutex.h>
41baabaca3SWarner Losh #include <sys/conf.h>
42baabaca3SWarner Losh #include <sys/devicestat.h>
43baabaca3SWarner Losh #include <sys/eventhandler.h>
44baabaca3SWarner Losh #include <sys/malloc.h>
45baabaca3SWarner Losh #include <sys/cons.h>
46baabaca3SWarner Losh #include <sys/proc.h>
47baabaca3SWarner Losh #include <sys/reboot.h>
4875ce4227SWarner Losh #include <sys/sbuf.h>
4913532153SScott Long #include <geom/geom.h>
50baabaca3SWarner Losh #include <geom/geom_disk.h>
51baabaca3SWarner Losh #endif /* _KERNEL */
52baabaca3SWarner Losh 
53baabaca3SWarner Losh #ifndef _KERNEL
54baabaca3SWarner Losh #include <stdio.h>
55baabaca3SWarner Losh #include <string.h>
56baabaca3SWarner Losh #endif /* _KERNEL */
57baabaca3SWarner Losh 
58baabaca3SWarner Losh #include <cam/cam.h>
59baabaca3SWarner Losh #include <cam/cam_ccb.h>
60baabaca3SWarner Losh #include <cam/cam_periph.h>
61baabaca3SWarner Losh #include <cam/cam_xpt_periph.h>
62baabaca3SWarner Losh #include <cam/cam_sim.h>
63baabaca3SWarner Losh #include <cam/cam_iosched.h>
64baabaca3SWarner Losh 
65baabaca3SWarner Losh #include <cam/nvme/nvme_all.h>
66baabaca3SWarner Losh 
67baabaca3SWarner Losh typedef enum {
68baabaca3SWarner Losh 	NDA_STATE_NORMAL
69baabaca3SWarner Losh } nda_state;
70baabaca3SWarner Losh 
71baabaca3SWarner Losh typedef enum {
72baabaca3SWarner Losh 	NDA_FLAG_OPEN		= 0x0001,
73baabaca3SWarner Losh 	NDA_FLAG_DIRTY		= 0x0002,
74baabaca3SWarner Losh 	NDA_FLAG_SCTX_INIT	= 0x0004,
75baabaca3SWarner Losh } nda_flags;
7675ce4227SWarner Losh #define NDA_FLAG_STRING		\
7775ce4227SWarner Losh 	"\020"			\
7875ce4227SWarner Losh 	"\001OPEN"		\
7975ce4227SWarner Losh 	"\002DIRTY"		\
8075ce4227SWarner Losh 	"\003SCTX_INIT"
81baabaca3SWarner Losh 
82baabaca3SWarner Losh typedef enum {
83baabaca3SWarner Losh 	NDA_Q_4K   = 0x01,
84baabaca3SWarner Losh 	NDA_Q_NONE = 0x00,
85baabaca3SWarner Losh } nda_quirks;
86baabaca3SWarner Losh 
87baabaca3SWarner Losh #define NDA_Q_BIT_STRING	\
88baabaca3SWarner Losh 	"\020"			\
89baabaca3SWarner Losh 	"\001Bit 0"
90baabaca3SWarner Losh 
91baabaca3SWarner Losh typedef enum {
92baabaca3SWarner Losh 	NDA_CCB_BUFFER_IO	= 0x01,
93baabaca3SWarner Losh 	NDA_CCB_DUMP            = 0x02,
94baabaca3SWarner Losh 	NDA_CCB_TRIM            = 0x03,
952446ce7aSWarner Losh 	NDA_CCB_PASS            = 0x04,
96baabaca3SWarner Losh 	NDA_CCB_TYPE_MASK	= 0x0F,
97baabaca3SWarner Losh } nda_ccb_state;
98baabaca3SWarner Losh 
99baabaca3SWarner Losh /* Offsets into our private area for storing information */
100807e94b2SWarner Losh #define ccb_state	ccb_h.ppriv_field0
101807e94b2SWarner Losh #define ccb_bp		ccb_h.ppriv_ptr1	/* For NDA_CCB_BUFFER_IO */
102807e94b2SWarner Losh #define ccb_trim	ccb_h.ppriv_ptr1	/* For NDA_CCB_TRIM */
103baabaca3SWarner Losh 
104baabaca3SWarner Losh struct nda_softc {
105baabaca3SWarner Losh 	struct   cam_iosched_softc *cam_iosched;
106baabaca3SWarner Losh 	int			outstanding_cmds;	/* Number of active commands */
107baabaca3SWarner Losh 	int			refcount;		/* Active xpt_action() calls */
108baabaca3SWarner Losh 	nda_state		state;
109baabaca3SWarner Losh 	nda_flags		flags;
110baabaca3SWarner Losh 	nda_quirks		quirks;
111baabaca3SWarner Losh 	int			unmappedio;
112807e94b2SWarner Losh 	quad_t			deletes;
113baabaca3SWarner Losh 	uint32_t		nsid;			/* Namespace ID for this nda device */
114baabaca3SWarner Losh 	struct disk		*disk;
115baabaca3SWarner Losh 	struct task		sysctl_task;
116baabaca3SWarner Losh 	struct sysctl_ctx_list	sysctl_ctx;
117baabaca3SWarner Losh 	struct sysctl_oid	*sysctl_tree;
118ea657f2cSWarner Losh 	uint64_t		trim_count;
119ea657f2cSWarner Losh 	uint64_t		trim_ranges;
120ea657f2cSWarner Losh 	uint64_t		trim_lbas;
121d38677d2SWarner Losh #ifdef CAM_TEST_FAILURE
122d38677d2SWarner Losh 	int			force_read_error;
123d38677d2SWarner Losh 	int			force_write_error;
124d38677d2SWarner Losh 	int			periodic_read_error;
125d38677d2SWarner Losh 	int			periodic_read_count;
126d38677d2SWarner Losh #endif
127baabaca3SWarner Losh #ifdef CAM_IO_STATS
128baabaca3SWarner Losh 	struct sysctl_ctx_list	sysctl_stats_ctx;
129baabaca3SWarner Losh 	struct sysctl_oid	*sysctl_stats_tree;
130baabaca3SWarner Losh 	u_int			timeouts;
131baabaca3SWarner Losh 	u_int			errors;
132baabaca3SWarner Losh 	u_int			invalidations;
133baabaca3SWarner Losh #endif
134baabaca3SWarner Losh };
135baabaca3SWarner Losh 
136807e94b2SWarner Losh struct nda_trim_request {
13793489854SBrooks Davis 	struct nvme_dsm_range	dsm[NVME_MAX_DSM_TRIM / sizeof(struct nvme_dsm_range)];
138807e94b2SWarner Losh 	TAILQ_HEAD(, bio) bps;
139807e94b2SWarner Losh };
14093489854SBrooks Davis _Static_assert(NVME_MAX_DSM_TRIM % sizeof(struct nvme_dsm_range) == 0,
14193489854SBrooks Davis     "NVME_MAX_DSM_TRIM must be an integral number of ranges");
142807e94b2SWarner Losh 
143baabaca3SWarner Losh /* Need quirk table */
144baabaca3SWarner Losh 
1452446ce7aSWarner Losh static	disk_ioctl_t	ndaioctl;
146baabaca3SWarner Losh static	disk_strategy_t	ndastrategy;
147baabaca3SWarner Losh static	dumper_t	ndadump;
148baabaca3SWarner Losh static	periph_init_t	ndainit;
1497f85b11cSWarner Losh static	void		ndaasync(void *callback_arg, uint32_t code,
150baabaca3SWarner Losh 				struct cam_path *path, void *arg);
151baabaca3SWarner Losh static	void		ndasysctlinit(void *context, int pending);
15275ce4227SWarner Losh static	int		ndaflagssysctl(SYSCTL_HANDLER_ARGS);
153baabaca3SWarner Losh static	periph_ctor_t	ndaregister;
154baabaca3SWarner Losh static	periph_dtor_t	ndacleanup;
155baabaca3SWarner Losh static	periph_start_t	ndastart;
156baabaca3SWarner Losh static	periph_oninv_t	ndaoninvalidate;
157baabaca3SWarner Losh static	void		ndadone(struct cam_periph *periph,
158baabaca3SWarner Losh 			       union ccb *done_ccb);
1597f85b11cSWarner Losh static  int		ndaerror(union ccb *ccb, uint32_t cam_flags,
1607f85b11cSWarner Losh 				uint32_t sense_flags);
161baabaca3SWarner Losh static void		ndashutdown(void *arg, int howto);
162baabaca3SWarner Losh static void		ndasuspend(void *arg);
163baabaca3SWarner Losh 
164baabaca3SWarner Losh #ifndef	NDA_DEFAULT_SEND_ORDERED
165baabaca3SWarner Losh #define	NDA_DEFAULT_SEND_ORDERED	1
166baabaca3SWarner Losh #endif
167baabaca3SWarner Losh #ifndef NDA_DEFAULT_TIMEOUT
168baabaca3SWarner Losh #define NDA_DEFAULT_TIMEOUT 30	/* Timeout in seconds */
169baabaca3SWarner Losh #endif
170baabaca3SWarner Losh #ifndef	NDA_DEFAULT_RETRY
171baabaca3SWarner Losh #define	NDA_DEFAULT_RETRY	4
172baabaca3SWarner Losh #endif
173807e94b2SWarner Losh #ifndef NDA_MAX_TRIM_ENTRIES
174afdbfe1eSWarner Losh #define NDA_MAX_TRIM_ENTRIES  (NVME_MAX_DSM_TRIM / sizeof(struct nvme_dsm_range))/* Number of DSM trims to use, max 256 */
175807e94b2SWarner Losh #endif
176baabaca3SWarner Losh 
1777029da5cSPawel Biernacki static SYSCTL_NODE(_kern_cam, OID_AUTO, nda, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
1786f591d13SWarner Losh     "CAM Direct Access Disk driver");
1796f591d13SWarner Losh 
180baabaca3SWarner Losh //static int nda_retry_count = NDA_DEFAULT_RETRY;
181baabaca3SWarner Losh static int nda_send_ordered = NDA_DEFAULT_SEND_ORDERED;
182baabaca3SWarner Losh static int nda_default_timeout = NDA_DEFAULT_TIMEOUT;
183807e94b2SWarner Losh static int nda_max_trim_entries = NDA_MAX_TRIM_ENTRIES;
18413532153SScott Long static int nda_enable_biospeedup = 1;
1851868c484SWarner Losh static int nda_nvd_compat = 1;
1866f591d13SWarner Losh SYSCTL_INT(_kern_cam_nda, OID_AUTO, max_trim, CTLFLAG_RDTUN,
1876f591d13SWarner Losh     &nda_max_trim_entries, NDA_MAX_TRIM_ENTRIES,
1886f591d13SWarner Losh     "Maximum number of BIO_DELETE to send down as a DSM TRIM.");
18913532153SScott Long SYSCTL_INT(_kern_cam_nda, OID_AUTO, enable_biospeedup, CTLFLAG_RDTUN,
1901868c484SWarner Losh     &nda_enable_biospeedup, 0, "Enable BIO_SPEEDUP processing.");
1911868c484SWarner Losh SYSCTL_INT(_kern_cam_nda, OID_AUTO, nvd_compat, CTLFLAG_RDTUN,
1921868c484SWarner Losh     &nda_nvd_compat, 1, "Enable creation of nvd aliases.");
193baabaca3SWarner Losh 
194baabaca3SWarner Losh /*
195baabaca3SWarner Losh  * All NVMe media is non-rotational, so all nvme device instances
196baabaca3SWarner Losh  * share this to implement the sysctl.
197baabaca3SWarner Losh  */
198baabaca3SWarner Losh static int nda_rotating_media = 0;
199baabaca3SWarner Losh 
200baabaca3SWarner Losh static struct periph_driver ndadriver =
201baabaca3SWarner Losh {
202baabaca3SWarner Losh 	ndainit, "nda",
203baabaca3SWarner Losh 	TAILQ_HEAD_INITIALIZER(ndadriver.units), /* generation */ 0
204baabaca3SWarner Losh };
205baabaca3SWarner Losh 
206baabaca3SWarner Losh PERIPHDRIVER_DECLARE(nda, ndadriver);
207baabaca3SWarner Losh 
208baabaca3SWarner Losh static MALLOC_DEFINE(M_NVMEDA, "nvme_da", "nvme_da buffers");
209baabaca3SWarner Losh 
210baabaca3SWarner Losh /*
211baabaca3SWarner Losh  * nice wrappers. Maybe these belong in nvme_all.c instead of
212baabaca3SWarner Losh  * here, but this is the only place that uses these. Should
213baabaca3SWarner Losh  * we ever grow another NVME periph, we should move them
214baabaca3SWarner Losh  * all there wholesale.
215baabaca3SWarner Losh  */
216baabaca3SWarner Losh 
217baabaca3SWarner Losh static void
218baabaca3SWarner Losh nda_nvme_flush(struct nda_softc *softc, struct ccb_nvmeio *nvmeio)
219baabaca3SWarner Losh {
220baabaca3SWarner Losh 	cam_fill_nvmeio(nvmeio,
221baabaca3SWarner Losh 	    0,			/* retries */
222baabaca3SWarner Losh 	    ndadone,		/* cbfcnp */
223baabaca3SWarner Losh 	    CAM_DIR_NONE,	/* flags */
224baabaca3SWarner Losh 	    NULL,		/* data_ptr */
225baabaca3SWarner Losh 	    0,			/* dxfer_len */
226717bff5dSWarner Losh 	    nda_default_timeout * 1000); /* timeout 30s */
227baabaca3SWarner Losh 	nvme_ns_flush_cmd(&nvmeio->cmd, softc->nsid);
228baabaca3SWarner Losh }
229baabaca3SWarner Losh 
230baabaca3SWarner Losh static void
231baabaca3SWarner Losh nda_nvme_trim(struct nda_softc *softc, struct ccb_nvmeio *nvmeio,
232baabaca3SWarner Losh     void *payload, uint32_t num_ranges)
233baabaca3SWarner Losh {
234baabaca3SWarner Losh 	cam_fill_nvmeio(nvmeio,
235baabaca3SWarner Losh 	    0,			/* retries */
236baabaca3SWarner Losh 	    ndadone,		/* cbfcnp */
237baabaca3SWarner Losh 	    CAM_DIR_OUT,	/* flags */
238baabaca3SWarner Losh 	    payload,		/* data_ptr */
239baabaca3SWarner Losh 	    num_ranges * sizeof(struct nvme_dsm_range), /* dxfer_len */
240717bff5dSWarner Losh 	    nda_default_timeout * 1000); /* timeout 30s */
241baabaca3SWarner Losh 	nvme_ns_trim_cmd(&nvmeio->cmd, softc->nsid, num_ranges);
242baabaca3SWarner Losh }
243baabaca3SWarner Losh 
244baabaca3SWarner Losh static void
245baabaca3SWarner Losh nda_nvme_write(struct nda_softc *softc, struct ccb_nvmeio *nvmeio,
246baabaca3SWarner Losh     void *payload, uint64_t lba, uint32_t len, uint32_t count)
247baabaca3SWarner Losh {
248baabaca3SWarner Losh 	cam_fill_nvmeio(nvmeio,
249baabaca3SWarner Losh 	    0,			/* retries */
250baabaca3SWarner Losh 	    ndadone,		/* cbfcnp */
251baabaca3SWarner Losh 	    CAM_DIR_OUT,	/* flags */
252baabaca3SWarner Losh 	    payload,		/* data_ptr */
253baabaca3SWarner Losh 	    len,		/* dxfer_len */
254717bff5dSWarner Losh 	    nda_default_timeout * 1000); /* timeout 30s */
255baabaca3SWarner Losh 	nvme_ns_write_cmd(&nvmeio->cmd, softc->nsid, lba, count);
256baabaca3SWarner Losh }
257baabaca3SWarner Losh 
258baabaca3SWarner Losh static void
259baabaca3SWarner Losh nda_nvme_rw_bio(struct nda_softc *softc, struct ccb_nvmeio *nvmeio,
260baabaca3SWarner Losh     struct bio *bp, uint32_t rwcmd)
261baabaca3SWarner Losh {
262baabaca3SWarner Losh 	int flags = rwcmd == NVME_OPC_READ ? CAM_DIR_IN : CAM_DIR_OUT;
263baabaca3SWarner Losh 	void *payload;
264baabaca3SWarner Losh 	uint64_t lba;
265baabaca3SWarner Losh 	uint32_t count;
266baabaca3SWarner Losh 
267baabaca3SWarner Losh 	if (bp->bio_flags & BIO_UNMAPPED) {
268baabaca3SWarner Losh 		flags |= CAM_DATA_BIO;
269baabaca3SWarner Losh 		payload = bp;
270baabaca3SWarner Losh 	} else {
271baabaca3SWarner Losh 		payload = bp->bio_data;
272baabaca3SWarner Losh 	}
273baabaca3SWarner Losh 
274baabaca3SWarner Losh 	lba = bp->bio_pblkno;
275baabaca3SWarner Losh 	count = bp->bio_bcount / softc->disk->d_sectorsize;
276baabaca3SWarner Losh 
277baabaca3SWarner Losh 	cam_fill_nvmeio(nvmeio,
278baabaca3SWarner Losh 	    0,			/* retries */
279baabaca3SWarner Losh 	    ndadone,		/* cbfcnp */
280baabaca3SWarner Losh 	    flags,		/* flags */
281baabaca3SWarner Losh 	    payload,		/* data_ptr */
282baabaca3SWarner Losh 	    bp->bio_bcount,	/* dxfer_len */
283717bff5dSWarner Losh 	    nda_default_timeout * 1000); /* timeout 30s */
284baabaca3SWarner Losh 	nvme_ns_rw_cmd(&nvmeio->cmd, rwcmd, softc->nsid, lba, count);
285baabaca3SWarner Losh }
286baabaca3SWarner Losh 
287baabaca3SWarner Losh static int
288baabaca3SWarner Losh ndaopen(struct disk *dp)
289baabaca3SWarner Losh {
290baabaca3SWarner Losh 	struct cam_periph *periph;
291baabaca3SWarner Losh 	struct nda_softc *softc;
292baabaca3SWarner Losh 	int error;
293baabaca3SWarner Losh 
294baabaca3SWarner Losh 	periph = (struct cam_periph *)dp->d_drv1;
29599e7a4adSScott Long 	if (cam_periph_acquire(periph) != 0) {
296baabaca3SWarner Losh 		return(ENXIO);
297baabaca3SWarner Losh 	}
298baabaca3SWarner Losh 
299baabaca3SWarner Losh 	cam_periph_lock(periph);
300baabaca3SWarner Losh 	if ((error = cam_periph_hold(periph, PRIBIO|PCATCH)) != 0) {
301baabaca3SWarner Losh 		cam_periph_unlock(periph);
302baabaca3SWarner Losh 		cam_periph_release(periph);
303baabaca3SWarner Losh 		return (error);
304baabaca3SWarner Losh 	}
305baabaca3SWarner Losh 
306baabaca3SWarner Losh 	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE | CAM_DEBUG_PERIPH,
307baabaca3SWarner Losh 	    ("ndaopen\n"));
308baabaca3SWarner Losh 
309baabaca3SWarner Losh 	softc = (struct nda_softc *)periph->softc;
310baabaca3SWarner Losh 	softc->flags |= NDA_FLAG_OPEN;
311baabaca3SWarner Losh 
312baabaca3SWarner Losh 	cam_periph_unhold(periph);
313baabaca3SWarner Losh 	cam_periph_unlock(periph);
314baabaca3SWarner Losh 	return (0);
315baabaca3SWarner Losh }
316baabaca3SWarner Losh 
317baabaca3SWarner Losh static int
318baabaca3SWarner Losh ndaclose(struct disk *dp)
319baabaca3SWarner Losh {
320baabaca3SWarner Losh 	struct	cam_periph *periph;
321baabaca3SWarner Losh 	struct	nda_softc *softc;
322baabaca3SWarner Losh 	union ccb *ccb;
323baabaca3SWarner Losh 	int error;
324baabaca3SWarner Losh 
325baabaca3SWarner Losh 	periph = (struct cam_periph *)dp->d_drv1;
326baabaca3SWarner Losh 	softc = (struct nda_softc *)periph->softc;
327baabaca3SWarner Losh 	cam_periph_lock(periph);
328baabaca3SWarner Losh 
329baabaca3SWarner Losh 	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE | CAM_DEBUG_PERIPH,
330baabaca3SWarner Losh 	    ("ndaclose\n"));
331baabaca3SWarner Losh 
332baabaca3SWarner Losh 	if ((softc->flags & NDA_FLAG_DIRTY) != 0 &&
333baabaca3SWarner Losh 	    (periph->flags & CAM_PERIPH_INVALID) == 0 &&
334baabaca3SWarner Losh 	    cam_periph_hold(periph, PRIBIO) == 0) {
335baabaca3SWarner Losh 		ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL);
336baabaca3SWarner Losh 		nda_nvme_flush(softc, &ccb->nvmeio);
337baabaca3SWarner Losh 		error = cam_periph_runccb(ccb, ndaerror, /*cam_flags*/0,
338baabaca3SWarner Losh 		    /*sense_flags*/0, softc->disk->d_devstat);
339baabaca3SWarner Losh 
340baabaca3SWarner Losh 		if (error != 0)
341baabaca3SWarner Losh 			xpt_print(periph->path, "Synchronize cache failed\n");
342baabaca3SWarner Losh 		else
343baabaca3SWarner Losh 			softc->flags &= ~NDA_FLAG_DIRTY;
344baabaca3SWarner Losh 		xpt_release_ccb(ccb);
345baabaca3SWarner Losh 		cam_periph_unhold(periph);
346baabaca3SWarner Losh 	}
347baabaca3SWarner Losh 
348baabaca3SWarner Losh 	softc->flags &= ~NDA_FLAG_OPEN;
349baabaca3SWarner Losh 
350baabaca3SWarner Losh 	while (softc->refcount != 0)
351baabaca3SWarner Losh 		cam_periph_sleep(periph, &softc->refcount, PRIBIO, "ndaclose", 1);
352b1988d44SWarner Losh 	KASSERT(softc->outstanding_cmds == 0,
353b1988d44SWarner Losh 	    ("nda %d outstanding commands", softc->outstanding_cmds));
354baabaca3SWarner Losh 	cam_periph_unlock(periph);
355baabaca3SWarner Losh 	cam_periph_release(periph);
356baabaca3SWarner Losh 	return (0);
357baabaca3SWarner Losh }
358baabaca3SWarner Losh 
359baabaca3SWarner Losh static void
360baabaca3SWarner Losh ndaschedule(struct cam_periph *periph)
361baabaca3SWarner Losh {
362baabaca3SWarner Losh 	struct nda_softc *softc = (struct nda_softc *)periph->softc;
363baabaca3SWarner Losh 
364baabaca3SWarner Losh 	if (softc->state != NDA_STATE_NORMAL)
365baabaca3SWarner Losh 		return;
366baabaca3SWarner Losh 
367baabaca3SWarner Losh 	cam_iosched_schedule(softc->cam_iosched, periph);
368baabaca3SWarner Losh }
369baabaca3SWarner Losh 
3702446ce7aSWarner Losh static int
3712446ce7aSWarner Losh ndaioctl(struct disk *dp, u_long cmd, void *data, int fflag,
3722446ce7aSWarner Losh     struct thread *td)
3732446ce7aSWarner Losh {
3742446ce7aSWarner Losh 	struct cam_periph *periph;
3752446ce7aSWarner Losh 
3762446ce7aSWarner Losh 	periph = (struct cam_periph *)dp->d_drv1;
3772446ce7aSWarner Losh 
3782446ce7aSWarner Losh 	switch (cmd) {
3792446ce7aSWarner Losh 	case NVME_IO_TEST:
3802446ce7aSWarner Losh 	case NVME_BIO_TEST:
3812446ce7aSWarner Losh 		/*
3822446ce7aSWarner Losh 		 * These don't map well to the underlying CCBs, so
3832446ce7aSWarner Losh 		 * they are usupported via CAM.
3842446ce7aSWarner Losh 		 */
3852446ce7aSWarner Losh 		return (ENOTTY);
3862446ce7aSWarner Losh 	case NVME_GET_NSID:
3872446ce7aSWarner Losh 	{
3882446ce7aSWarner Losh 		struct nvme_get_nsid *gnsid = (struct nvme_get_nsid *)data;
3892446ce7aSWarner Losh 		struct ccb_pathinq cpi;
3902446ce7aSWarner Losh 
3912446ce7aSWarner Losh 		xpt_path_inq(&cpi, periph->path);
3922446ce7aSWarner Losh 		strncpy(gnsid->cdev, cpi.xport_specific.nvme.dev_name,
3932446ce7aSWarner Losh 		    sizeof(gnsid->cdev));
3942446ce7aSWarner Losh 		gnsid->nsid = cpi.xport_specific.nvme.nsid;
3952446ce7aSWarner Losh 		return (0);
3962446ce7aSWarner Losh 	}
3972446ce7aSWarner Losh 	case NVME_PASSTHROUGH_CMD:
3982446ce7aSWarner Losh 	{
3992446ce7aSWarner Losh 		struct nvme_pt_command *pt;
4002446ce7aSWarner Losh 		union ccb *ccb;
4012446ce7aSWarner Losh 		struct cam_periph_map_info mapinfo;
4022d76f4aaSWarner Losh 		u_int maxmap = dp->d_maxsize;
4032446ce7aSWarner Losh 		int error;
4042446ce7aSWarner Losh 
4052446ce7aSWarner Losh 		/*
4062446ce7aSWarner Losh 		 * Create a NVME_IO CCB to do the passthrough command.
4072446ce7aSWarner Losh 		 */
4082446ce7aSWarner Losh 		pt = (struct nvme_pt_command *)data;
4092446ce7aSWarner Losh 		ccb = xpt_alloc_ccb();
4102446ce7aSWarner Losh 		xpt_setup_ccb(&ccb->ccb_h, periph->path, CAM_PRIORITY_NORMAL);
4112446ce7aSWarner Losh 		ccb->ccb_state = NDA_CCB_PASS;
4122446ce7aSWarner Losh 		cam_fill_nvmeio(&ccb->nvmeio,
4132446ce7aSWarner Losh 		    0,			/* Retries */
4142446ce7aSWarner Losh 		    ndadone,
4152446ce7aSWarner Losh 		    (pt->is_read ? CAM_DIR_IN : CAM_DIR_OUT) | CAM_DATA_VADDR,
4162446ce7aSWarner Losh 		    pt->buf,
4172446ce7aSWarner Losh 		    pt->len,
4182446ce7aSWarner Losh 		    nda_default_timeout * 1000);
4192446ce7aSWarner Losh 		memcpy(&ccb->nvmeio.cmd, &pt->cmd, sizeof(pt->cmd));
4202446ce7aSWarner Losh 
4212446ce7aSWarner Losh 		/*
4222446ce7aSWarner Losh 		 * Wire the user memory in this request for the I/O
4232446ce7aSWarner Losh 		 */
4242446ce7aSWarner Losh 		memset(&mapinfo, 0, sizeof(mapinfo));
4252446ce7aSWarner Losh 		error = cam_periph_mapmem(ccb, &mapinfo, maxmap);
4264f397ed2SWarner Losh 		if (error)
4274f397ed2SWarner Losh 			goto out;
4282446ce7aSWarner Losh 
4292446ce7aSWarner Losh 		/*
4304f397ed2SWarner Losh 		 * Lock the periph and run the command.
4312446ce7aSWarner Losh 		 */
4322446ce7aSWarner Losh 		cam_periph_lock(periph);
4334f397ed2SWarner Losh 		cam_periph_runccb(ccb, NULL, CAM_RETRY_SELTO,
4344f397ed2SWarner Losh 		    SF_RETRY_UA | SF_NO_PRINT, NULL);
4352446ce7aSWarner Losh 
4362446ce7aSWarner Losh 		/*
4372446ce7aSWarner Losh 		 * Tear down mapping and return status.
4382446ce7aSWarner Losh 		 */
4394f397ed2SWarner Losh 		cam_periph_unlock(periph);
440*d068ea16SMark Johnston 		error = cam_periph_unmapmem(ccb, &mapinfo);
441*d068ea16SMark Johnston 		if (!cam_ccb_success(ccb))
442*d068ea16SMark Johnston 			error = EIO;
4434f397ed2SWarner Losh out:
4444f397ed2SWarner Losh 		cam_periph_lock(periph);
4452446ce7aSWarner Losh 		xpt_release_ccb(ccb);
4464f397ed2SWarner Losh 		cam_periph_unlock(periph);
4472446ce7aSWarner Losh 		return (error);
4482446ce7aSWarner Losh 	}
4492446ce7aSWarner Losh 	default:
4502446ce7aSWarner Losh 		break;
4512446ce7aSWarner Losh 	}
4522446ce7aSWarner Losh 	return (ENOTTY);
4532446ce7aSWarner Losh }
4542446ce7aSWarner Losh 
455baabaca3SWarner Losh /*
456baabaca3SWarner Losh  * Actually translate the requested transfer into one the physical driver
457baabaca3SWarner Losh  * can understand.  The transfer is described by a buf and will include
458baabaca3SWarner Losh  * only one physical transfer.
459baabaca3SWarner Losh  */
460baabaca3SWarner Losh static void
461baabaca3SWarner Losh ndastrategy(struct bio *bp)
462baabaca3SWarner Losh {
463baabaca3SWarner Losh 	struct cam_periph *periph;
464baabaca3SWarner Losh 	struct nda_softc *softc;
465baabaca3SWarner Losh 
466baabaca3SWarner Losh 	periph = (struct cam_periph *)bp->bio_disk->d_drv1;
467baabaca3SWarner Losh 	softc = (struct nda_softc *)periph->softc;
468baabaca3SWarner Losh 
469baabaca3SWarner Losh 	cam_periph_lock(periph);
470baabaca3SWarner Losh 
471baabaca3SWarner Losh 	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("ndastrategy(%p)\n", bp));
472baabaca3SWarner Losh 
473baabaca3SWarner Losh 	/*
474baabaca3SWarner Losh 	 * If the device has been made invalid, error out
475baabaca3SWarner Losh 	 */
476baabaca3SWarner Losh 	if ((periph->flags & CAM_PERIPH_INVALID) != 0) {
477baabaca3SWarner Losh 		cam_periph_unlock(periph);
478baabaca3SWarner Losh 		biofinish(bp, NULL, ENXIO);
479baabaca3SWarner Losh 		return;
480baabaca3SWarner Losh 	}
481baabaca3SWarner Losh 
482807e94b2SWarner Losh 	if (bp->bio_cmd == BIO_DELETE)
483807e94b2SWarner Losh 		softc->deletes++;
484807e94b2SWarner Losh 
485baabaca3SWarner Losh 	/*
486baabaca3SWarner Losh 	 * Place it in the queue of disk activities for this disk
487baabaca3SWarner Losh 	 */
488baabaca3SWarner Losh 	cam_iosched_queue_work(softc->cam_iosched, bp);
489baabaca3SWarner Losh 
490baabaca3SWarner Losh 	/*
491baabaca3SWarner Losh 	 * Schedule ourselves for performing the work.
492baabaca3SWarner Losh 	 */
493baabaca3SWarner Losh 	ndaschedule(periph);
494baabaca3SWarner Losh 	cam_periph_unlock(periph);
495baabaca3SWarner Losh 
496baabaca3SWarner Losh 	return;
497baabaca3SWarner Losh }
498baabaca3SWarner Losh 
499baabaca3SWarner Losh static int
500489ba222SMitchell Horne ndadump(void *arg, void *virtual, off_t offset, size_t length)
501baabaca3SWarner Losh {
502baabaca3SWarner Losh 	struct	    cam_periph *periph;
503baabaca3SWarner Losh 	struct	    nda_softc *softc;
504baabaca3SWarner Losh 	u_int	    secsize;
505c4231018SWarner Losh 	struct ccb_nvmeio nvmeio;
506baabaca3SWarner Losh 	struct	    disk *dp;
507baabaca3SWarner Losh 	uint64_t    lba;
508baabaca3SWarner Losh 	uint32_t    count;
509baabaca3SWarner Losh 	int	    error = 0;
510baabaca3SWarner Losh 
511baabaca3SWarner Losh 	dp = arg;
512baabaca3SWarner Losh 	periph = dp->d_drv1;
513baabaca3SWarner Losh 	softc = (struct nda_softc *)periph->softc;
514baabaca3SWarner Losh 	secsize = softc->disk->d_sectorsize;
515baabaca3SWarner Losh 	lba = offset / secsize;
516baabaca3SWarner Losh 	count = length / secsize;
517baabaca3SWarner Losh 
518bff0b56cSScott Long 	if ((periph->flags & CAM_PERIPH_INVALID) != 0)
519baabaca3SWarner Losh 		return (ENXIO);
520baabaca3SWarner Losh 
521fa271a5dSWarner Losh 	/* xpt_get_ccb returns a zero'd allocation for the ccb, mimic that here */
522fa271a5dSWarner Losh 	memset(&nvmeio, 0, sizeof(nvmeio));
523baabaca3SWarner Losh 	if (length > 0) {
524c4231018SWarner Losh 		xpt_setup_ccb(&nvmeio.ccb_h, periph->path, CAM_PRIORITY_NORMAL);
525807e94b2SWarner Losh 		nvmeio.ccb_state = NDA_CCB_DUMP;
526c4231018SWarner Losh 		nda_nvme_write(softc, &nvmeio, virtual, lba, length, count);
5272b31251aSWarner Losh 		error = cam_periph_runccb((union ccb *)&nvmeio, cam_periph_error,
5282b31251aSWarner Losh 		    0, SF_NO_RECOVERY | SF_NO_RETRY, NULL);
529baabaca3SWarner Losh 		if (error != 0)
5302b31251aSWarner Losh 			printf("Aborting dump due to I/O error %d.\n", error);
5312b31251aSWarner Losh 
532baabaca3SWarner Losh 		return (error);
533baabaca3SWarner Losh 	}
534baabaca3SWarner Losh 
535baabaca3SWarner Losh 	/* Flush */
536c4231018SWarner Losh 	xpt_setup_ccb(&nvmeio.ccb_h, periph->path, CAM_PRIORITY_NORMAL);
537baabaca3SWarner Losh 
538807e94b2SWarner Losh 	nvmeio.ccb_state = NDA_CCB_DUMP;
539c4231018SWarner Losh 	nda_nvme_flush(softc, &nvmeio);
5402b31251aSWarner Losh 	error = cam_periph_runccb((union ccb *)&nvmeio, cam_periph_error,
5412b31251aSWarner Losh 	    0, SF_NO_RECOVERY | SF_NO_RETRY, NULL);
542baabaca3SWarner Losh 	if (error != 0)
543baabaca3SWarner Losh 		xpt_print(periph->path, "flush cmd failed\n");
544baabaca3SWarner Losh 	return (error);
545baabaca3SWarner Losh }
546baabaca3SWarner Losh 
547baabaca3SWarner Losh static void
548baabaca3SWarner Losh ndainit(void)
549baabaca3SWarner Losh {
550baabaca3SWarner Losh 	cam_status status;
551baabaca3SWarner Losh 
552baabaca3SWarner Losh 	/*
553baabaca3SWarner Losh 	 * Install a global async callback.  This callback will
554baabaca3SWarner Losh 	 * receive async callbacks like "new device found".
555baabaca3SWarner Losh 	 */
556baabaca3SWarner Losh 	status = xpt_register_async(AC_FOUND_DEVICE, ndaasync, NULL, NULL);
557baabaca3SWarner Losh 
558baabaca3SWarner Losh 	if (status != CAM_REQ_CMP) {
559baabaca3SWarner Losh 		printf("nda: Failed to attach master async callback "
560baabaca3SWarner Losh 		       "due to status 0x%x!\n", status);
561baabaca3SWarner Losh 	} else if (nda_send_ordered) {
562baabaca3SWarner Losh 		/* Register our event handlers */
563baabaca3SWarner Losh 		if ((EVENTHANDLER_REGISTER(power_suspend, ndasuspend,
564baabaca3SWarner Losh 					   NULL, EVENTHANDLER_PRI_LAST)) == NULL)
565baabaca3SWarner Losh 		    printf("ndainit: power event registration failed!\n");
566baabaca3SWarner Losh 		if ((EVENTHANDLER_REGISTER(shutdown_post_sync, ndashutdown,
567baabaca3SWarner Losh 					   NULL, SHUTDOWN_PRI_DEFAULT)) == NULL)
568baabaca3SWarner Losh 		    printf("ndainit: shutdown event registration failed!\n");
569baabaca3SWarner Losh 	}
570baabaca3SWarner Losh }
571baabaca3SWarner Losh 
572baabaca3SWarner Losh /*
573baabaca3SWarner Losh  * Callback from GEOM, called when it has finished cleaning up its
574baabaca3SWarner Losh  * resources.
575baabaca3SWarner Losh  */
576baabaca3SWarner Losh static void
577baabaca3SWarner Losh ndadiskgonecb(struct disk *dp)
578baabaca3SWarner Losh {
579baabaca3SWarner Losh 	struct cam_periph *periph;
580baabaca3SWarner Losh 
581baabaca3SWarner Losh 	periph = (struct cam_periph *)dp->d_drv1;
582baabaca3SWarner Losh 
583baabaca3SWarner Losh 	cam_periph_release(periph);
584baabaca3SWarner Losh }
585baabaca3SWarner Losh 
586baabaca3SWarner Losh static void
587baabaca3SWarner Losh ndaoninvalidate(struct cam_periph *periph)
588baabaca3SWarner Losh {
589baabaca3SWarner Losh 	struct nda_softc *softc;
590baabaca3SWarner Losh 
591baabaca3SWarner Losh 	softc = (struct nda_softc *)periph->softc;
592baabaca3SWarner Losh 
593baabaca3SWarner Losh 	/*
594baabaca3SWarner Losh 	 * De-register any async callbacks.
595baabaca3SWarner Losh 	 */
596baabaca3SWarner Losh 	xpt_register_async(0, ndaasync, periph, periph->path);
597baabaca3SWarner Losh #ifdef CAM_IO_STATS
598baabaca3SWarner Losh 	softc->invalidations++;
599baabaca3SWarner Losh #endif
600baabaca3SWarner Losh 
601baabaca3SWarner Losh 	/*
602c08ceddbSWarner Losh 	 * Return all queued I/O with ENXIO. Transactions may be queued up here
603c08ceddbSWarner Losh 	 * for retry (since we are called while there's other transactions
604c08ceddbSWarner Losh 	 * pending). Any requests in the hardware will drain before ndacleanup
605c08ceddbSWarner Losh 	 * is called.
606baabaca3SWarner Losh 	 */
607baabaca3SWarner Losh 	cam_iosched_flush(softc->cam_iosched, NULL, ENXIO);
608baabaca3SWarner Losh 
60948ae3f4fSWarner Losh 	/*
61048ae3f4fSWarner Losh 	 * Tell GEOM that we've gone away, we'll get a callback when it is
61148ae3f4fSWarner Losh 	 * done cleaning up its resources.
61248ae3f4fSWarner Losh 	 */
613baabaca3SWarner Losh 	disk_gone(softc->disk);
614baabaca3SWarner Losh }
615baabaca3SWarner Losh 
616baabaca3SWarner Losh static void
617baabaca3SWarner Losh ndacleanup(struct cam_periph *periph)
618baabaca3SWarner Losh {
619baabaca3SWarner Losh 	struct nda_softc *softc;
620baabaca3SWarner Losh 
621baabaca3SWarner Losh 	softc = (struct nda_softc *)periph->softc;
622baabaca3SWarner Losh 
623baabaca3SWarner Losh 	cam_periph_unlock(periph);
624baabaca3SWarner Losh 
625baabaca3SWarner Losh 	cam_iosched_fini(softc->cam_iosched);
626baabaca3SWarner Losh 
627baabaca3SWarner Losh 	/*
628baabaca3SWarner Losh 	 * If we can't free the sysctl tree, oh well...
629baabaca3SWarner Losh 	 */
630baabaca3SWarner Losh 	if ((softc->flags & NDA_FLAG_SCTX_INIT) != 0) {
631baabaca3SWarner Losh #ifdef CAM_IO_STATS
632baabaca3SWarner Losh 		if (sysctl_ctx_free(&softc->sysctl_stats_ctx) != 0)
633baabaca3SWarner Losh 			xpt_print(periph->path,
634baabaca3SWarner Losh 			    "can't remove sysctl stats context\n");
635baabaca3SWarner Losh #endif
636baabaca3SWarner Losh 		if (sysctl_ctx_free(&softc->sysctl_ctx) != 0)
637baabaca3SWarner Losh 			xpt_print(periph->path,
638baabaca3SWarner Losh 			    "can't remove sysctl context\n");
639baabaca3SWarner Losh 	}
640baabaca3SWarner Losh 
641baabaca3SWarner Losh 	disk_destroy(softc->disk);
642baabaca3SWarner Losh 	free(softc, M_DEVBUF);
643baabaca3SWarner Losh 	cam_periph_lock(periph);
644baabaca3SWarner Losh }
645baabaca3SWarner Losh 
646baabaca3SWarner Losh static void
6477f85b11cSWarner Losh ndaasync(void *callback_arg, uint32_t code,
648baabaca3SWarner Losh 	struct cam_path *path, void *arg)
649baabaca3SWarner Losh {
650baabaca3SWarner Losh 	struct cam_periph *periph;
651baabaca3SWarner Losh 
652baabaca3SWarner Losh 	periph = (struct cam_periph *)callback_arg;
653baabaca3SWarner Losh 	switch (code) {
654baabaca3SWarner Losh 	case AC_FOUND_DEVICE:
655baabaca3SWarner Losh 	{
656baabaca3SWarner Losh 		struct ccb_getdev *cgd;
657baabaca3SWarner Losh 		cam_status status;
658baabaca3SWarner Losh 
659baabaca3SWarner Losh 		cgd = (struct ccb_getdev *)arg;
660baabaca3SWarner Losh 		if (cgd == NULL)
661baabaca3SWarner Losh 			break;
662baabaca3SWarner Losh 
663baabaca3SWarner Losh 		if (cgd->protocol != PROTO_NVME)
664baabaca3SWarner Losh 			break;
665baabaca3SWarner Losh 
666baabaca3SWarner Losh 		/*
667baabaca3SWarner Losh 		 * Allocate a peripheral instance for
668baabaca3SWarner Losh 		 * this device and start the probe
669baabaca3SWarner Losh 		 * process.
670baabaca3SWarner Losh 		 */
671baabaca3SWarner Losh 		status = cam_periph_alloc(ndaregister, ndaoninvalidate,
672baabaca3SWarner Losh 					  ndacleanup, ndastart,
673baabaca3SWarner Losh 					  "nda", CAM_PERIPH_BIO,
674baabaca3SWarner Losh 					  path, ndaasync,
675baabaca3SWarner Losh 					  AC_FOUND_DEVICE, cgd);
676baabaca3SWarner Losh 
677baabaca3SWarner Losh 		if (status != CAM_REQ_CMP
678baabaca3SWarner Losh 		 && status != CAM_REQ_INPROG)
679baabaca3SWarner Losh 			printf("ndaasync: Unable to attach to new device "
680baabaca3SWarner Losh 				"due to status 0x%x\n", status);
681baabaca3SWarner Losh 		break;
682baabaca3SWarner Losh 	}
683baabaca3SWarner Losh 	case AC_ADVINFO_CHANGED:
684baabaca3SWarner Losh 	{
685baabaca3SWarner Losh 		uintptr_t buftype;
686baabaca3SWarner Losh 
687baabaca3SWarner Losh 		buftype = (uintptr_t)arg;
688baabaca3SWarner Losh 		if (buftype == CDAI_TYPE_PHYS_PATH) {
689baabaca3SWarner Losh 			struct nda_softc *softc;
690baabaca3SWarner Losh 
691baabaca3SWarner Losh 			softc = periph->softc;
692baabaca3SWarner Losh 			disk_attr_changed(softc->disk, "GEOM::physpath",
693baabaca3SWarner Losh 					  M_NOWAIT);
694baabaca3SWarner Losh 		}
695baabaca3SWarner Losh 		break;
696baabaca3SWarner Losh 	}
697baabaca3SWarner Losh 	case AC_LOST_DEVICE:
698baabaca3SWarner Losh 	default:
699baabaca3SWarner Losh 		break;
700baabaca3SWarner Losh 	}
701e4b1ae21SWarner Losh 	cam_periph_async(periph, code, path, arg);
702baabaca3SWarner Losh }
703baabaca3SWarner Losh 
704baabaca3SWarner Losh static void
705baabaca3SWarner Losh ndasysctlinit(void *context, int pending)
706baabaca3SWarner Losh {
707baabaca3SWarner Losh 	struct cam_periph *periph;
708baabaca3SWarner Losh 	struct nda_softc *softc;
7094d470952SAlexander Motin 	char tmpstr[32], tmpstr2[16];
710baabaca3SWarner Losh 
711baabaca3SWarner Losh 	periph = (struct cam_periph *)context;
712baabaca3SWarner Losh 
713baabaca3SWarner Losh 	/* periph was held for us when this task was enqueued */
714baabaca3SWarner Losh 	if ((periph->flags & CAM_PERIPH_INVALID) != 0) {
715baabaca3SWarner Losh 		cam_periph_release(periph);
716baabaca3SWarner Losh 		return;
717baabaca3SWarner Losh 	}
718baabaca3SWarner Losh 
719baabaca3SWarner Losh 	softc = (struct nda_softc *)periph->softc;
720baabaca3SWarner Losh 	snprintf(tmpstr, sizeof(tmpstr), "CAM NDA unit %d", periph->unit_number);
721baabaca3SWarner Losh 	snprintf(tmpstr2, sizeof(tmpstr2), "%d", periph->unit_number);
722baabaca3SWarner Losh 
723baabaca3SWarner Losh 	sysctl_ctx_init(&softc->sysctl_ctx);
724baabaca3SWarner Losh 	softc->flags |= NDA_FLAG_SCTX_INIT;
7254c484fd2SEd Schouten 	softc->sysctl_tree = SYSCTL_ADD_NODE_WITH_LABEL(&softc->sysctl_ctx,
726baabaca3SWarner Losh 		SYSCTL_STATIC_CHILDREN(_kern_cam_nda), OID_AUTO, tmpstr2,
7277029da5cSPawel Biernacki 		CTLFLAG_RD | CTLFLAG_MPSAFE, 0, tmpstr, "device_index");
728baabaca3SWarner Losh 	if (softc->sysctl_tree == NULL) {
729baabaca3SWarner Losh 		printf("ndasysctlinit: unable to allocate sysctl tree\n");
730baabaca3SWarner Losh 		cam_periph_release(periph);
731baabaca3SWarner Losh 		return;
732baabaca3SWarner Losh 	}
733baabaca3SWarner Losh 
734baabaca3SWarner Losh 	SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree),
735fdfc0a83SWarner Losh 	    OID_AUTO, "unmapped_io", CTLFLAG_RD,
736baabaca3SWarner Losh 	    &softc->unmappedio, 0, "Unmapped I/O leaf");
737baabaca3SWarner Losh 
738807e94b2SWarner Losh 	SYSCTL_ADD_QUAD(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree),
739fdfc0a83SWarner Losh 	    OID_AUTO, "deletes", CTLFLAG_RD,
740807e94b2SWarner Losh 	    &softc->deletes, "Number of BIO_DELETE requests");
741807e94b2SWarner Losh 
742ea657f2cSWarner Losh 	SYSCTL_ADD_UQUAD(&softc->sysctl_ctx,
743ea657f2cSWarner Losh 		SYSCTL_CHILDREN(softc->sysctl_tree), OID_AUTO,
744ea657f2cSWarner Losh 		"trim_count", CTLFLAG_RD, &softc->trim_count,
745ea657f2cSWarner Losh 		"Total number of unmap/dsm commands sent");
746ea657f2cSWarner Losh 	SYSCTL_ADD_UQUAD(&softc->sysctl_ctx,
747ea657f2cSWarner Losh 		SYSCTL_CHILDREN(softc->sysctl_tree), OID_AUTO,
748ea657f2cSWarner Losh 		"trim_ranges", CTLFLAG_RD, &softc->trim_ranges,
749ea657f2cSWarner Losh 		"Total number of ranges in unmap/dsm commands");
750ea657f2cSWarner Losh 	SYSCTL_ADD_UQUAD(&softc->sysctl_ctx,
751ea657f2cSWarner Losh 		SYSCTL_CHILDREN(softc->sysctl_tree), OID_AUTO,
752ea657f2cSWarner Losh 		"trim_lbas", CTLFLAG_RD, &softc->trim_lbas,
753ea657f2cSWarner Losh 		"Total lbas in the unmap/dsm commands sent");
754807e94b2SWarner Losh 
755fdfc0a83SWarner Losh 	SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree),
756fdfc0a83SWarner Losh 	    OID_AUTO, "rotating", CTLFLAG_RD, &nda_rotating_media, 1,
757baabaca3SWarner Losh 	    "Rotating media");
758baabaca3SWarner Losh 
75975ce4227SWarner Losh 	SYSCTL_ADD_PROC(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree),
76075ce4227SWarner Losh 	    OID_AUTO, "flags", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
76175ce4227SWarner Losh 	    softc, 0, ndaflagssysctl, "A",
76275ce4227SWarner Losh 	    "Flags for drive");
76375ce4227SWarner Losh 
764baabaca3SWarner Losh #ifdef CAM_IO_STATS
765baabaca3SWarner Losh 	softc->sysctl_stats_tree = SYSCTL_ADD_NODE(&softc->sysctl_stats_ctx,
766baabaca3SWarner Losh 		SYSCTL_CHILDREN(softc->sysctl_tree), OID_AUTO, "stats",
7677029da5cSPawel Biernacki 		CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "Statistics");
768baabaca3SWarner Losh 	if (softc->sysctl_stats_tree == NULL) {
769baabaca3SWarner Losh 		printf("ndasysctlinit: unable to allocate sysctl tree for stats\n");
770baabaca3SWarner Losh 		cam_periph_release(periph);
771baabaca3SWarner Losh 		return;
772baabaca3SWarner Losh 	}
773baabaca3SWarner Losh 	SYSCTL_ADD_INT(&softc->sysctl_stats_ctx,
774baabaca3SWarner Losh 		SYSCTL_CHILDREN(softc->sysctl_stats_tree),
775fdfc0a83SWarner Losh 		OID_AUTO, "timeouts", CTLFLAG_RD,
776baabaca3SWarner Losh 		&softc->timeouts, 0,
777baabaca3SWarner Losh 		"Device timeouts reported by the SIM");
778baabaca3SWarner Losh 	SYSCTL_ADD_INT(&softc->sysctl_stats_ctx,
779baabaca3SWarner Losh 		SYSCTL_CHILDREN(softc->sysctl_stats_tree),
780fdfc0a83SWarner Losh 		OID_AUTO, "errors", CTLFLAG_RD,
781baabaca3SWarner Losh 		&softc->errors, 0,
782baabaca3SWarner Losh 		"Transport errors reported by the SIM.");
783baabaca3SWarner Losh 	SYSCTL_ADD_INT(&softc->sysctl_stats_ctx,
784baabaca3SWarner Losh 		SYSCTL_CHILDREN(softc->sysctl_stats_tree),
785fdfc0a83SWarner Losh 		OID_AUTO, "pack_invalidations", CTLFLAG_RD,
786baabaca3SWarner Losh 		&softc->invalidations, 0,
787baabaca3SWarner Losh 		"Device pack invalidations.");
788baabaca3SWarner Losh #endif
789baabaca3SWarner Losh 
790d38677d2SWarner Losh #ifdef CAM_TEST_FAILURE
791d38677d2SWarner Losh 	SYSCTL_ADD_PROC(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree),
792d38677d2SWarner Losh 		OID_AUTO, "invalidate", CTLTYPE_U64 | CTLFLAG_RW | CTLFLAG_MPSAFE,
793d38677d2SWarner Losh 		periph, 0, cam_periph_invalidate_sysctl, "I",
794d38677d2SWarner Losh 		"Write 1 to invalidate the drive immediately");
795d38677d2SWarner Losh #endif
796d38677d2SWarner Losh 
797baabaca3SWarner Losh 	cam_iosched_sysctl_init(softc->cam_iosched, &softc->sysctl_ctx,
798baabaca3SWarner Losh 	    softc->sysctl_tree);
799baabaca3SWarner Losh 
800baabaca3SWarner Losh 	cam_periph_release(periph);
801baabaca3SWarner Losh }
802baabaca3SWarner Losh 
803baabaca3SWarner Losh static int
80475ce4227SWarner Losh ndaflagssysctl(SYSCTL_HANDLER_ARGS)
80575ce4227SWarner Losh {
80675ce4227SWarner Losh 	struct sbuf sbuf;
80775ce4227SWarner Losh 	struct nda_softc *softc = arg1;
80875ce4227SWarner Losh 	int error;
80975ce4227SWarner Losh 
81075ce4227SWarner Losh 	sbuf_new_for_sysctl(&sbuf, NULL, 0, req);
81175ce4227SWarner Losh 	if (softc->flags != 0)
81275ce4227SWarner Losh 		sbuf_printf(&sbuf, "0x%b", (unsigned)softc->flags, NDA_FLAG_STRING);
81375ce4227SWarner Losh 	else
814519b24f0SAlexander Motin 		sbuf_putc(&sbuf, '0');
81575ce4227SWarner Losh 	error = sbuf_finish(&sbuf);
81675ce4227SWarner Losh 	sbuf_delete(&sbuf);
81775ce4227SWarner Losh 
81875ce4227SWarner Losh 	return (error);
81975ce4227SWarner Losh }
82075ce4227SWarner Losh 
82175ce4227SWarner Losh static int
822baabaca3SWarner Losh ndagetattr(struct bio *bp)
823baabaca3SWarner Losh {
824baabaca3SWarner Losh 	int ret;
825baabaca3SWarner Losh 	struct cam_periph *periph;
826baabaca3SWarner Losh 
82713532153SScott Long 	if (g_handleattr_int(bp, "GEOM::canspeedup", nda_enable_biospeedup))
82813532153SScott Long 		return (EJUSTRETURN);
82913532153SScott Long 
830baabaca3SWarner Losh 	periph = (struct cam_periph *)bp->bio_disk->d_drv1;
831baabaca3SWarner Losh 	cam_periph_lock(periph);
832baabaca3SWarner Losh 	ret = xpt_getattr(bp->bio_data, bp->bio_length, bp->bio_attribute,
833baabaca3SWarner Losh 	    periph->path);
834baabaca3SWarner Losh 	cam_periph_unlock(periph);
835baabaca3SWarner Losh 	if (ret == 0)
836baabaca3SWarner Losh 		bp->bio_completed = bp->bio_length;
837baabaca3SWarner Losh 	return ret;
838baabaca3SWarner Losh }
839baabaca3SWarner Losh 
840baabaca3SWarner Losh static cam_status
841baabaca3SWarner Losh ndaregister(struct cam_periph *periph, void *arg)
842baabaca3SWarner Losh {
843baabaca3SWarner Losh 	struct nda_softc *softc;
844baabaca3SWarner Losh 	struct disk *disk;
845baabaca3SWarner Losh 	struct ccb_pathinq cpi;
846baabaca3SWarner Losh 	const struct nvme_namespace_data *nsd;
847baabaca3SWarner Losh 	const struct nvme_controller_data *cd;
848baabaca3SWarner Losh 	char   announce_buf[80];
8490d787e9bSWojciech Macek 	uint8_t flbas_fmt, lbads, vwc_present;
850baabaca3SWarner Losh 	u_int maxio;
851baabaca3SWarner Losh 	int quirks;
852baabaca3SWarner Losh 
8539f8ed7e4SWarner Losh 	nsd = nvme_get_identify_ns(periph);
8549f8ed7e4SWarner Losh 	cd = nvme_get_identify_cntrl(periph);
855baabaca3SWarner Losh 
856baabaca3SWarner Losh 	softc = (struct nda_softc *)malloc(sizeof(*softc), M_DEVBUF,
857baabaca3SWarner Losh 	    M_NOWAIT | M_ZERO);
858baabaca3SWarner Losh 
859baabaca3SWarner Losh 	if (softc == NULL) {
860baabaca3SWarner Losh 		printf("ndaregister: Unable to probe new device. "
861baabaca3SWarner Losh 		    "Unable to allocate softc\n");
862baabaca3SWarner Losh 		return(CAM_REQ_CMP_ERR);
863baabaca3SWarner Losh 	}
864baabaca3SWarner Losh 
8650028abe6SWarner Losh 	if (cam_iosched_init(&softc->cam_iosched, periph) != 0) {
866baabaca3SWarner Losh 		printf("ndaregister: Unable to probe new device. "
867baabaca3SWarner Losh 		       "Unable to allocate iosched memory\n");
8682e1fccf2SConrad Meyer 		free(softc, M_DEVBUF);
869baabaca3SWarner Losh 		return(CAM_REQ_CMP_ERR);
870baabaca3SWarner Losh 	}
871baabaca3SWarner Losh 
872baabaca3SWarner Losh 	/* ident_data parsing */
873baabaca3SWarner Losh 
874baabaca3SWarner Losh 	periph->softc = softc;
875baabaca3SWarner Losh 	softc->quirks = NDA_Q_NONE;
876762a7f4fSWarner Losh 	xpt_path_inq(&cpi, periph->path);
877baabaca3SWarner Losh 	TASK_INIT(&softc->sysctl_task, 0, ndasysctlinit, periph);
878baabaca3SWarner Losh 
879baabaca3SWarner Losh 	/*
880baabaca3SWarner Losh 	 * The name space ID is the lun, save it for later I/O
881baabaca3SWarner Losh 	 */
8821d6e8110SWarner Losh 	softc->nsid = (uint32_t)xpt_path_lun_id(periph->path);
883baabaca3SWarner Losh 
884baabaca3SWarner Losh 	/*
885baabaca3SWarner Losh 	 * Register this media as a disk
886baabaca3SWarner Losh 	 */
88790bcc81bSAlexander Motin 	(void)cam_periph_acquire(periph);
888baabaca3SWarner Losh 	cam_periph_unlock(periph);
889baabaca3SWarner Losh 	snprintf(announce_buf, sizeof(announce_buf),
890baabaca3SWarner Losh 	    "kern.cam.nda.%d.quirks", periph->unit_number);
891baabaca3SWarner Losh 	quirks = softc->quirks;
892baabaca3SWarner Losh 	TUNABLE_INT_FETCH(announce_buf, &quirks);
893baabaca3SWarner Losh 	softc->quirks = quirks;
894baabaca3SWarner Losh 	cam_iosched_set_sort_queue(softc->cam_iosched, 0);
895baabaca3SWarner Losh 	softc->disk = disk = disk_alloc();
89617160457SAlexander Motin 	disk->d_rotation_rate = DISK_RR_NON_ROTATING;
897baabaca3SWarner Losh 	disk->d_open = ndaopen;
898baabaca3SWarner Losh 	disk->d_close = ndaclose;
899baabaca3SWarner Losh 	disk->d_strategy = ndastrategy;
9002446ce7aSWarner Losh 	disk->d_ioctl = ndaioctl;
901baabaca3SWarner Losh 	disk->d_getattr = ndagetattr;
902e07ac3f2SJohn Baldwin 	if (cam_sim_pollable(periph->sim))
903baabaca3SWarner Losh 		disk->d_dump = ndadump;
904baabaca3SWarner Losh 	disk->d_gone = ndadiskgonecb;
905baabaca3SWarner Losh 	disk->d_name = "nda";
906baabaca3SWarner Losh 	disk->d_drv1 = periph;
907baabaca3SWarner Losh 	disk->d_unit = periph->unit_number;
908baabaca3SWarner Losh 	maxio = cpi.maxio;		/* Honor max I/O size of SIM */
909baabaca3SWarner Losh 	if (maxio == 0)
910baabaca3SWarner Losh 		maxio = DFLTPHYS;	/* traditional default */
911cd853791SKonstantin Belousov 	else if (maxio > maxphys)
912cd853791SKonstantin Belousov 		maxio = maxphys;	/* for safety */
913baabaca3SWarner Losh 	disk->d_maxsize = maxio;
9140d787e9bSWojciech Macek 	flbas_fmt = (nsd->flbas >> NVME_NS_DATA_FLBAS_FORMAT_SHIFT) &
9150d787e9bSWojciech Macek 		NVME_NS_DATA_FLBAS_FORMAT_MASK;
9160d787e9bSWojciech Macek 	lbads = (nsd->lbaf[flbas_fmt] >> NVME_NS_DATA_LBAF_LBADS_SHIFT) &
9170d787e9bSWojciech Macek 		NVME_NS_DATA_LBAF_LBADS_MASK;
9180d787e9bSWojciech Macek 	disk->d_sectorsize = 1 << lbads;
919baabaca3SWarner Losh 	disk->d_mediasize = (off_t)(disk->d_sectorsize * nsd->nsze);
920baabaca3SWarner Losh 	disk->d_delmaxsize = disk->d_mediasize;
921baabaca3SWarner Losh 	disk->d_flags = DISKFLAG_DIRECT_COMPLETION;
92287b3975eSChuck Tuffli 	if (nvme_ctrlr_has_dataset_mgmt(cd))
923baabaca3SWarner Losh 		disk->d_flags |= DISKFLAG_CANDELETE;
9240d787e9bSWojciech Macek 	vwc_present = (cd->vwc >> NVME_CTRLR_DATA_VWC_PRESENT_SHIFT) &
9250d787e9bSWojciech Macek 		NVME_CTRLR_DATA_VWC_PRESENT_MASK;
9260d787e9bSWojciech Macek 	if (vwc_present)
927baabaca3SWarner Losh 		disk->d_flags |= DISKFLAG_CANFLUSHCACHE;
928baabaca3SWarner Losh 	if ((cpi.hba_misc & PIM_UNMAPPED) != 0) {
929baabaca3SWarner Losh 		disk->d_flags |= DISKFLAG_UNMAPPED_BIO;
930baabaca3SWarner Losh 		softc->unmappedio = 1;
931baabaca3SWarner Losh 	}
932baabaca3SWarner Losh 	/*
933baabaca3SWarner Losh 	 * d_ident and d_descr are both far bigger than the length of either
934baabaca3SWarner Losh 	 *  the serial or model number strings.
935baabaca3SWarner Losh 	 */
9363090d504SKenneth D. Merry 	cam_strvis_flag(disk->d_descr, cd->mn, NVME_MODEL_NUMBER_LENGTH,
9373090d504SKenneth D. Merry 	    sizeof(disk->d_descr), CAM_STRVIS_FLAG_NONASCII_SPC);
9383090d504SKenneth D. Merry 
9393090d504SKenneth D. Merry 	cam_strvis_flag(disk->d_ident, cd->sn, NVME_SERIAL_NUMBER_LENGTH,
9403090d504SKenneth D. Merry 	    sizeof(disk->d_ident), CAM_STRVIS_FLAG_NONASCII_SPC);
9413090d504SKenneth D. Merry 
942baabaca3SWarner Losh 	disk->d_hba_vendor = cpi.hba_vendor;
943baabaca3SWarner Losh 	disk->d_hba_device = cpi.hba_device;
944baabaca3SWarner Losh 	disk->d_hba_subvendor = cpi.hba_subvendor;
945baabaca3SWarner Losh 	disk->d_hba_subdevice = cpi.hba_subdevice;
946b5961be1SEdward Tomasz Napierala 	snprintf(disk->d_attachment, sizeof(disk->d_attachment),
947b5961be1SEdward Tomasz Napierala 	    "%s%d", cpi.dev_name, cpi.unit_number);
9481c7decd4SAlexander Motin 	if (((nsd->nsfeat >> NVME_NS_DATA_NSFEAT_NPVALID_SHIFT) &
9491c7decd4SAlexander Motin 	    NVME_NS_DATA_NSFEAT_NPVALID_MASK) != 0 && nsd->npwg != 0)
9501c7decd4SAlexander Motin 		disk->d_stripesize = ((nsd->npwg + 1) * disk->d_sectorsize);
9511c7decd4SAlexander Motin 	else
9521c7decd4SAlexander Motin 		disk->d_stripesize = nsd->noiob * disk->d_sectorsize;
953baabaca3SWarner Losh 	disk->d_stripeoffset = 0;
954baabaca3SWarner Losh 	disk->d_devstat = devstat_new_entry(periph->periph_name,
955baabaca3SWarner Losh 	    periph->unit_number, disk->d_sectorsize,
956baabaca3SWarner Losh 	    DEVSTAT_ALL_SUPPORTED,
957baabaca3SWarner Losh 	    DEVSTAT_TYPE_DIRECT | XPORT_DEVSTAT_TYPE(cpi.transport),
958baabaca3SWarner Losh 	    DEVSTAT_PRIORITY_DISK);
959d45e1674SWarner Losh 	/*
960d45e1674SWarner Losh 	 * Add alias for older nvd drives to ease transition.
961d45e1674SWarner Losh 	 */
9621868c484SWarner Losh 	if (nda_nvd_compat)
963fd26063fSWarner Losh 		disk_add_alias(disk, "nvd");
964baabaca3SWarner Losh 
965baabaca3SWarner Losh 	cam_periph_lock(periph);
966baabaca3SWarner Losh 
967baabaca3SWarner Losh 	snprintf(announce_buf, sizeof(announce_buf),
968baabaca3SWarner Losh 		"%juMB (%ju %u byte sectors)",
969baabaca3SWarner Losh 	    (uintmax_t)((uintmax_t)disk->d_mediasize / (1024*1024)),
970baabaca3SWarner Losh 		(uintmax_t)disk->d_mediasize / disk->d_sectorsize,
971baabaca3SWarner Losh 		disk->d_sectorsize);
972baabaca3SWarner Losh 	xpt_announce_periph(periph, announce_buf);
973baabaca3SWarner Losh 	xpt_announce_quirks(periph, softc->quirks, NDA_Q_BIT_STRING);
974baabaca3SWarner Losh 
975baabaca3SWarner Losh 	/*
976baabaca3SWarner Losh 	 * Create our sysctl variables, now that we know
977baabaca3SWarner Losh 	 * we have successfully attached.
978baabaca3SWarner Losh 	 */
97999e7a4adSScott Long 	if (cam_periph_acquire(periph) == 0)
980baabaca3SWarner Losh 		taskqueue_enqueue(taskqueue_thread, &softc->sysctl_task);
981baabaca3SWarner Losh 
982baabaca3SWarner Losh 	/*
983baabaca3SWarner Losh 	 * Register for device going away and info about the drive
984baabaca3SWarner Losh 	 * changing (though with NVMe, it can't)
985baabaca3SWarner Losh 	 */
986baabaca3SWarner Losh 	xpt_register_async(AC_LOST_DEVICE | AC_ADVINFO_CHANGED,
987baabaca3SWarner Losh 	    ndaasync, periph, periph->path);
988baabaca3SWarner Losh 
989baabaca3SWarner Losh 	softc->state = NDA_STATE_NORMAL;
99090bcc81bSAlexander Motin 
99190bcc81bSAlexander Motin 	/*
99290bcc81bSAlexander Motin 	 * We'll release this reference once GEOM calls us back via
99390bcc81bSAlexander Motin 	 * ndadiskgonecb(), telling us that our provider has been freed.
99490bcc81bSAlexander Motin 	 */
99590bcc81bSAlexander Motin 	if (cam_periph_acquire(periph) == 0)
99690bcc81bSAlexander Motin 		disk_create(softc->disk, DISK_VERSION);
99790bcc81bSAlexander Motin 
99890bcc81bSAlexander Motin 	cam_periph_release_locked(periph);
999baabaca3SWarner Losh 	return(CAM_REQ_CMP);
1000baabaca3SWarner Losh }
1001baabaca3SWarner Losh 
1002baabaca3SWarner Losh static void
1003baabaca3SWarner Losh ndastart(struct cam_periph *periph, union ccb *start_ccb)
1004baabaca3SWarner Losh {
1005baabaca3SWarner Losh 	struct nda_softc *softc = (struct nda_softc *)periph->softc;
1006baabaca3SWarner Losh 	struct ccb_nvmeio *nvmeio = &start_ccb->nvmeio;
1007baabaca3SWarner Losh 
1008baabaca3SWarner Losh 	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("ndastart\n"));
1009baabaca3SWarner Losh 
1010baabaca3SWarner Losh 	switch (softc->state) {
1011baabaca3SWarner Losh 	case NDA_STATE_NORMAL:
1012baabaca3SWarner Losh 	{
1013baabaca3SWarner Losh 		struct bio *bp;
1014baabaca3SWarner Losh 
1015baabaca3SWarner Losh 		bp = cam_iosched_next_bio(softc->cam_iosched);
1016baabaca3SWarner Losh 		CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("ndastart: bio %p\n", bp));
1017baabaca3SWarner Losh 		if (bp == NULL) {
1018baabaca3SWarner Losh 			xpt_release_ccb(start_ccb);
1019baabaca3SWarner Losh 			break;
1020baabaca3SWarner Losh 		}
1021baabaca3SWarner Losh 
1022baabaca3SWarner Losh 		switch (bp->bio_cmd) {
1023baabaca3SWarner Losh 		case BIO_WRITE:
1024baabaca3SWarner Losh 			softc->flags |= NDA_FLAG_DIRTY;
1025baabaca3SWarner Losh 			/* FALLTHROUGH */
1026baabaca3SWarner Losh 		case BIO_READ:
1027baabaca3SWarner Losh 		{
1028d38677d2SWarner Losh #ifdef CAM_TEST_FAILURE
1029baabaca3SWarner Losh 			int fail = 0;
1030baabaca3SWarner Losh 
1031baabaca3SWarner Losh 			/*
1032baabaca3SWarner Losh 			 * Support the failure ioctls.  If the command is a
1033baabaca3SWarner Losh 			 * read, and there are pending forced read errors, or
1034baabaca3SWarner Losh 			 * if a write and pending write errors, then fail this
1035baabaca3SWarner Losh 			 * operation with EIO.  This is useful for testing
1036baabaca3SWarner Losh 			 * purposes.  Also, support having every Nth read fail.
1037baabaca3SWarner Losh 			 *
1038baabaca3SWarner Losh 			 * This is a rather blunt tool.
1039baabaca3SWarner Losh 			 */
1040baabaca3SWarner Losh 			if (bp->bio_cmd == BIO_READ) {
1041baabaca3SWarner Losh 				if (softc->force_read_error) {
1042baabaca3SWarner Losh 					softc->force_read_error--;
1043baabaca3SWarner Losh 					fail = 1;
1044baabaca3SWarner Losh 				}
1045baabaca3SWarner Losh 				if (softc->periodic_read_error > 0) {
1046baabaca3SWarner Losh 					if (++softc->periodic_read_count >=
1047baabaca3SWarner Losh 					    softc->periodic_read_error) {
1048baabaca3SWarner Losh 						softc->periodic_read_count = 0;
1049baabaca3SWarner Losh 						fail = 1;
1050baabaca3SWarner Losh 					}
1051baabaca3SWarner Losh 				}
1052baabaca3SWarner Losh 			} else {
1053baabaca3SWarner Losh 				if (softc->force_write_error) {
1054baabaca3SWarner Losh 					softc->force_write_error--;
1055baabaca3SWarner Losh 					fail = 1;
1056baabaca3SWarner Losh 				}
1057baabaca3SWarner Losh 			}
1058baabaca3SWarner Losh 			if (fail) {
1059baabaca3SWarner Losh 				biofinish(bp, NULL, EIO);
1060baabaca3SWarner Losh 				xpt_release_ccb(start_ccb);
1061baabaca3SWarner Losh 				ndaschedule(periph);
1062baabaca3SWarner Losh 				return;
1063baabaca3SWarner Losh 			}
1064baabaca3SWarner Losh #endif
1065baabaca3SWarner Losh 			KASSERT((bp->bio_flags & BIO_UNMAPPED) == 0 ||
1066baabaca3SWarner Losh 			    round_page(bp->bio_bcount + bp->bio_ma_offset) /
1067baabaca3SWarner Losh 			    PAGE_SIZE == bp->bio_ma_n,
1068baabaca3SWarner Losh 			    ("Short bio %p", bp));
1069baabaca3SWarner Losh 			nda_nvme_rw_bio(softc, &start_ccb->nvmeio, bp, bp->bio_cmd == BIO_READ ?
1070baabaca3SWarner Losh 			    NVME_OPC_READ : NVME_OPC_WRITE);
1071baabaca3SWarner Losh 			break;
1072baabaca3SWarner Losh 		}
1073baabaca3SWarner Losh 		case BIO_DELETE:
1074baabaca3SWarner Losh 		{
1075807e94b2SWarner Losh 			struct nvme_dsm_range *dsm_range, *dsm_end;
1076807e94b2SWarner Losh 			struct nda_trim_request *trim;
1077807e94b2SWarner Losh 			struct bio *bp1;
1078807e94b2SWarner Losh 			int ents;
1079ea657f2cSWarner Losh 			uint32_t totalcount = 0, ranges = 0;
1080baabaca3SWarner Losh 
1081807e94b2SWarner Losh 			trim = malloc(sizeof(*trim), M_NVMEDA, M_ZERO | M_NOWAIT);
1082807e94b2SWarner Losh 			if (trim == NULL) {
108360b7691dSWarner Losh 				biofinish(bp, NULL, ENOMEM);
108460b7691dSWarner Losh 				xpt_release_ccb(start_ccb);
108560b7691dSWarner Losh 				ndaschedule(periph);
108660b7691dSWarner Losh 				return;
108760b7691dSWarner Losh 			}
1088807e94b2SWarner Losh 			TAILQ_INIT(&trim->bps);
1089807e94b2SWarner Losh 			bp1 = bp;
109093489854SBrooks Davis 			ents = min(nitems(trim->dsm), nda_max_trim_entries);
1091ead4c1b4SWarner Losh 			ents = max(ents, 1);
109293489854SBrooks Davis 			dsm_range = trim->dsm;
1093807e94b2SWarner Losh 			dsm_end = dsm_range + ents;
1094807e94b2SWarner Losh 			do {
1095807e94b2SWarner Losh 				TAILQ_INSERT_TAIL(&trim->bps, bp1, bio_queue);
1096baabaca3SWarner Losh 				dsm_range->length =
1097807e94b2SWarner Losh 				    htole32(bp1->bio_bcount / softc->disk->d_sectorsize);
1098baabaca3SWarner Losh 				dsm_range->starting_lba =
1099afdbfe1eSWarner Losh 				    htole64(bp1->bio_offset / softc->disk->d_sectorsize);
1100ea657f2cSWarner Losh 				ranges++;
1101ea657f2cSWarner Losh 				totalcount += dsm_range->length;
1102807e94b2SWarner Losh 				dsm_range++;
1103807e94b2SWarner Losh 				if (dsm_range >= dsm_end)
1104807e94b2SWarner Losh 					break;
1105807e94b2SWarner Losh 				bp1 = cam_iosched_next_trim(softc->cam_iosched);
1106807e94b2SWarner Losh 				/* XXX -- Could collapse adjacent ranges, but we don't for now */
1107807e94b2SWarner Losh 				/* XXX -- Could limit based on total payload size */
1108807e94b2SWarner Losh 			} while (bp1 != NULL);
1109807e94b2SWarner Losh 			start_ccb->ccb_trim = trim;
111093489854SBrooks Davis 			nda_nvme_trim(softc, &start_ccb->nvmeio, trim->dsm,
111193489854SBrooks Davis 			    dsm_range - trim->dsm);
1112807e94b2SWarner Losh 			start_ccb->ccb_state = NDA_CCB_TRIM;
1113ea657f2cSWarner Losh 			softc->trim_count++;
1114ea657f2cSWarner Losh 			softc->trim_ranges += ranges;
1115ea657f2cSWarner Losh 			softc->trim_lbas += totalcount;
1116851063e1SWarner Losh 			/*
1117851063e1SWarner Losh 			 * Note: We can have multiple TRIMs in flight, so we don't call
1118851063e1SWarner Losh 			 * cam_iosched_submit_trim(softc->cam_iosched);
1119851063e1SWarner Losh 			 * since that forces the I/O scheduler to only schedule one at a time.
1120851063e1SWarner Losh 			 * On NVMe drives, this is a performance disaster.
1121851063e1SWarner Losh 			 */
1122baabaca3SWarner Losh 			goto out;
1123baabaca3SWarner Losh 		}
1124baabaca3SWarner Losh 		case BIO_FLUSH:
1125baabaca3SWarner Losh 			nda_nvme_flush(softc, nvmeio);
1126baabaca3SWarner Losh 			break;
1127d176b803SScott Long 		default:
1128d176b803SScott Long 			biofinish(bp, NULL, EOPNOTSUPP);
1129d176b803SScott Long 			xpt_release_ccb(start_ccb);
1130d176b803SScott Long 			ndaschedule(periph);
1131d176b803SScott Long 			return;
1132baabaca3SWarner Losh 		}
1133807e94b2SWarner Losh 		start_ccb->ccb_state = NDA_CCB_BUFFER_IO;
1134807e94b2SWarner Losh 		start_ccb->ccb_bp = bp;
1135baabaca3SWarner Losh out:
1136807e94b2SWarner Losh 		start_ccb->ccb_h.flags |= CAM_UNLOCKED;
1137baabaca3SWarner Losh 		softc->outstanding_cmds++;
1138b1988d44SWarner Losh 		softc->refcount++;			/* For submission only */
1139baabaca3SWarner Losh 		cam_periph_unlock(periph);
1140baabaca3SWarner Losh 		xpt_action(start_ccb);
1141baabaca3SWarner Losh 		cam_periph_lock(periph);
1142b1988d44SWarner Losh 		softc->refcount--;			/* Submission done */
1143baabaca3SWarner Losh 
1144baabaca3SWarner Losh 		/* May have more work to do, so ensure we stay scheduled */
1145baabaca3SWarner Losh 		ndaschedule(periph);
1146baabaca3SWarner Losh 		break;
1147baabaca3SWarner Losh 		}
1148baabaca3SWarner Losh 	}
1149baabaca3SWarner Losh }
1150baabaca3SWarner Losh 
1151baabaca3SWarner Losh static void
1152baabaca3SWarner Losh ndadone(struct cam_periph *periph, union ccb *done_ccb)
1153baabaca3SWarner Losh {
1154baabaca3SWarner Losh 	struct nda_softc *softc;
1155baabaca3SWarner Losh 	struct ccb_nvmeio *nvmeio = &done_ccb->nvmeio;
1156baabaca3SWarner Losh 	struct cam_path *path;
1157baabaca3SWarner Losh 	int state;
1158baabaca3SWarner Losh 
1159baabaca3SWarner Losh 	softc = (struct nda_softc *)periph->softc;
1160baabaca3SWarner Losh 	path = done_ccb->ccb_h.path;
1161baabaca3SWarner Losh 
1162baabaca3SWarner Losh 	CAM_DEBUG(path, CAM_DEBUG_TRACE, ("ndadone\n"));
1163baabaca3SWarner Losh 
1164807e94b2SWarner Losh 	state = nvmeio->ccb_state & NDA_CCB_TYPE_MASK;
1165baabaca3SWarner Losh 	switch (state) {
1166baabaca3SWarner Losh 	case NDA_CCB_BUFFER_IO:
1167baabaca3SWarner Losh 	case NDA_CCB_TRIM:
1168baabaca3SWarner Losh 	{
1169baabaca3SWarner Losh 		int error;
1170baabaca3SWarner Losh 
1171baabaca3SWarner Losh 		cam_periph_lock(periph);
1172baabaca3SWarner Losh 		if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
1173baabaca3SWarner Losh 			error = ndaerror(done_ccb, 0, 0);
1174baabaca3SWarner Losh 			if (error == ERESTART) {
1175baabaca3SWarner Losh 				/* A retry was scheduled, so just return. */
1176baabaca3SWarner Losh 				cam_periph_unlock(periph);
1177baabaca3SWarner Losh 				return;
1178baabaca3SWarner Losh 			}
1179baabaca3SWarner Losh 			if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
1180baabaca3SWarner Losh 				cam_release_devq(path,
1181baabaca3SWarner Losh 						 /*relsim_flags*/0,
1182baabaca3SWarner Losh 						 /*reduction*/0,
1183baabaca3SWarner Losh 						 /*timeout*/0,
1184baabaca3SWarner Losh 						 /*getcount_only*/0);
1185baabaca3SWarner Losh 		} else {
1186baabaca3SWarner Losh 			if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
1187baabaca3SWarner Losh 				panic("REQ_CMP with QFRZN");
1188baabaca3SWarner Losh 			error = 0;
1189baabaca3SWarner Losh 		}
1190807e94b2SWarner Losh 		if (state == NDA_CCB_BUFFER_IO) {
1191807e94b2SWarner Losh 			struct bio *bp;
1192807e94b2SWarner Losh 
1193807e94b2SWarner Losh 			bp = (struct bio *)done_ccb->ccb_bp;
1194baabaca3SWarner Losh 			bp->bio_error = error;
1195baabaca3SWarner Losh 			if (error != 0) {
1196baabaca3SWarner Losh 				bp->bio_resid = bp->bio_bcount;
1197baabaca3SWarner Losh 				bp->bio_flags |= BIO_ERROR;
1198baabaca3SWarner Losh 			} else {
1199baabaca3SWarner Losh 				bp->bio_resid = 0;
1200baabaca3SWarner Losh 			}
1201baabaca3SWarner Losh 			softc->outstanding_cmds--;
1202baabaca3SWarner Losh 
12039754579bSWarner Losh 			/*
12049754579bSWarner Losh 			 * We need to call cam_iosched before we call biodone so that we
12059754579bSWarner Losh 			 * don't measure any activity that happens in the completion
12069754579bSWarner Losh 			 * routine, which in the case of sendfile can be quite
12079754579bSWarner Losh 			 * extensive.
12089754579bSWarner Losh 			 */
1209baabaca3SWarner Losh 			cam_iosched_bio_complete(softc->cam_iosched, bp, done_ccb);
1210baabaca3SWarner Losh 			xpt_release_ccb(done_ccb);
1211807e94b2SWarner Losh 			ndaschedule(periph);
1212807e94b2SWarner Losh 			cam_periph_unlock(periph);
1213807e94b2SWarner Losh 			biodone(bp);
1214807e94b2SWarner Losh 		} else { /* state == NDA_CCB_TRIM */
1215807e94b2SWarner Losh 			struct nda_trim_request *trim;
1216807e94b2SWarner Losh 			struct bio *bp1, *bp2;
12174d87e271SWarner Losh 			TAILQ_HEAD(, bio) queue;
1218baabaca3SWarner Losh 
1219807e94b2SWarner Losh 			trim = nvmeio->ccb_trim;
1220baabaca3SWarner Losh 			TAILQ_INIT(&queue);
1221807e94b2SWarner Losh 			TAILQ_CONCAT(&queue, &trim->bps, bio_queue);
1222807e94b2SWarner Losh 			free(trim, M_NVMEDA);
1223807e94b2SWarner Losh 
1224851063e1SWarner Losh 			/*
1225851063e1SWarner Losh 			 * Since we can have multiple trims in flight, we don't
1226851063e1SWarner Losh 			 * need to call this here.
1227851063e1SWarner Losh 			 * cam_iosched_trim_done(softc->cam_iosched);
1228851063e1SWarner Losh 			 */
1229807e94b2SWarner Losh 			/*
1230807e94b2SWarner Losh 			 * The the I/O scheduler that we're finishing the I/O
1231807e94b2SWarner Losh 			 * so we can keep book. The first one we pass in the CCB
1232807e94b2SWarner Losh 			 * which has the timing information. The rest we pass in NULL
1233807e94b2SWarner Losh 			 * so we can keep proper counts.
1234807e94b2SWarner Losh 			 */
1235807e94b2SWarner Losh 			bp1 = TAILQ_FIRST(&queue);
1236807e94b2SWarner Losh 			cam_iosched_bio_complete(softc->cam_iosched, bp1, done_ccb);
1237807e94b2SWarner Losh 			xpt_release_ccb(done_ccb);
1238b1988d44SWarner Losh 			softc->outstanding_cmds--;
1239baabaca3SWarner Losh 			ndaschedule(periph);
1240baabaca3SWarner Losh 			cam_periph_unlock(periph);
1241807e94b2SWarner Losh 			while ((bp2 = TAILQ_FIRST(&queue)) != NULL) {
1242807e94b2SWarner Losh 				TAILQ_REMOVE(&queue, bp2, bio_queue);
1243807e94b2SWarner Losh 				bp2->bio_error = error;
1244baabaca3SWarner Losh 				if (error != 0) {
1245807e94b2SWarner Losh 					bp2->bio_flags |= BIO_ERROR;
1246807e94b2SWarner Losh 					bp2->bio_resid = bp1->bio_bcount;
1247baabaca3SWarner Losh 				} else
1248807e94b2SWarner Losh 					bp2->bio_resid = 0;
1249807e94b2SWarner Losh 				if (bp1 != bp2)
1250807e94b2SWarner Losh 					cam_iosched_bio_complete(softc->cam_iosched, bp2, NULL);
1251807e94b2SWarner Losh 				biodone(bp2);
1252baabaca3SWarner Losh 			}
1253baabaca3SWarner Losh 		}
1254baabaca3SWarner Losh 		return;
1255baabaca3SWarner Losh 	}
1256baabaca3SWarner Losh 	case NDA_CCB_DUMP:
1257baabaca3SWarner Losh 		/* No-op.  We're polling */
1258baabaca3SWarner Losh 		return;
12592446ce7aSWarner Losh 	case NDA_CCB_PASS:
1260f8503fdeSWarner Losh 		/* NVME_PASSTHROUGH_CMD runs this CCB and releases it */
12612446ce7aSWarner Losh 		return;
1262baabaca3SWarner Losh 	default:
1263baabaca3SWarner Losh 		break;
1264baabaca3SWarner Losh 	}
1265baabaca3SWarner Losh 	xpt_release_ccb(done_ccb);
1266baabaca3SWarner Losh }
1267baabaca3SWarner Losh 
1268baabaca3SWarner Losh static int
12697f85b11cSWarner Losh ndaerror(union ccb *ccb, uint32_t cam_flags, uint32_t sense_flags)
1270baabaca3SWarner Losh {
12711bc9ca3bSWarner Losh #ifdef CAM_IO_STATS
12721bc9ca3bSWarner Losh 	struct nda_softc *softc;
12731bc9ca3bSWarner Losh 	struct cam_periph *periph;
12741bc9ca3bSWarner Losh 
12751bc9ca3bSWarner Losh 	periph = xpt_path_periph(ccb->ccb_h.path);
12761bc9ca3bSWarner Losh 	softc = (struct nda_softc *)periph->softc;
12771bc9ca3bSWarner Losh #endif
1278baabaca3SWarner Losh 
1279baabaca3SWarner Losh 	switch (ccb->ccb_h.status & CAM_STATUS_MASK) {
1280baabaca3SWarner Losh 	case CAM_CMD_TIMEOUT:
1281baabaca3SWarner Losh #ifdef CAM_IO_STATS
1282baabaca3SWarner Losh 		softc->timeouts++;
1283baabaca3SWarner Losh #endif
1284baabaca3SWarner Losh 		break;
1285baabaca3SWarner Losh 	case CAM_REQ_CMP_ERR:
1286774ab87cSWarner Losh 	case CAM_NVME_STATUS_ERROR:
1287baabaca3SWarner Losh #ifdef CAM_IO_STATS
1288baabaca3SWarner Losh 		softc->errors++;
1289baabaca3SWarner Losh #endif
1290baabaca3SWarner Losh 		break;
1291baabaca3SWarner Losh 	default:
1292baabaca3SWarner Losh 		break;
1293baabaca3SWarner Losh 	}
1294baabaca3SWarner Losh 
1295553484aeSWarner Losh 	return(cam_periph_error(ccb, cam_flags, sense_flags));
1296baabaca3SWarner Losh }
1297baabaca3SWarner Losh 
1298baabaca3SWarner Losh /*
1299baabaca3SWarner Losh  * Step through all NDA peripheral drivers, and if the device is still open,
1300baabaca3SWarner Losh  * sync the disk cache to physical media.
1301baabaca3SWarner Losh  */
1302baabaca3SWarner Losh static void
1303baabaca3SWarner Losh ndaflush(void)
1304baabaca3SWarner Losh {
1305baabaca3SWarner Losh 	struct cam_periph *periph;
1306baabaca3SWarner Losh 	struct nda_softc *softc;
1307baabaca3SWarner Losh 	union ccb *ccb;
1308baabaca3SWarner Losh 	int error;
1309baabaca3SWarner Losh 
1310baabaca3SWarner Losh 	CAM_PERIPH_FOREACH(periph, &ndadriver) {
1311baabaca3SWarner Losh 		softc = (struct nda_softc *)periph->softc;
13129d602e4eSWarner Losh 
1313baabaca3SWarner Losh 		if (SCHEDULER_STOPPED()) {
13149d602e4eSWarner Losh 			/*
131549dace1dSGordon Bergling 			 * If we panicked with the lock held or the periph is not
13169d602e4eSWarner Losh 			 * open, do not recurse.  Otherwise, call ndadump since
13179d602e4eSWarner Losh 			 * that avoids the sleeping cam_periph_getccb does if no
13189d602e4eSWarner Losh 			 * CCBs are available.
13199d602e4eSWarner Losh 			 */
1320baabaca3SWarner Losh 			if (!cam_periph_owned(periph) &&
1321baabaca3SWarner Losh 			    (softc->flags & NDA_FLAG_OPEN)) {
1322489ba222SMitchell Horne 				ndadump(softc->disk, NULL, 0, 0);
1323baabaca3SWarner Losh 			}
1324baabaca3SWarner Losh 			continue;
1325baabaca3SWarner Losh 		}
13269d602e4eSWarner Losh 
1327baabaca3SWarner Losh 		/*
13289d602e4eSWarner Losh 		 * We only sync the cache if the drive is still open
1329baabaca3SWarner Losh 		 */
13309d602e4eSWarner Losh 		cam_periph_lock(periph);
1331baabaca3SWarner Losh 		if ((softc->flags & NDA_FLAG_OPEN) == 0) {
1332baabaca3SWarner Losh 			cam_periph_unlock(periph);
1333baabaca3SWarner Losh 			continue;
1334baabaca3SWarner Losh 		}
1335baabaca3SWarner Losh 
1336baabaca3SWarner Losh 		ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL);
1337baabaca3SWarner Losh 		nda_nvme_flush(softc, &ccb->nvmeio);
1338baabaca3SWarner Losh 		error = cam_periph_runccb(ccb, ndaerror, /*cam_flags*/0,
1339baabaca3SWarner Losh 		    /*sense_flags*/ SF_NO_RECOVERY | SF_NO_RETRY,
1340baabaca3SWarner Losh 		    softc->disk->d_devstat);
1341baabaca3SWarner Losh 		if (error != 0)
1342baabaca3SWarner Losh 			xpt_print(periph->path, "Synchronize cache failed\n");
1343baabaca3SWarner Losh 		xpt_release_ccb(ccb);
1344baabaca3SWarner Losh 		cam_periph_unlock(periph);
1345baabaca3SWarner Losh 	}
1346baabaca3SWarner Losh }
1347baabaca3SWarner Losh 
1348baabaca3SWarner Losh static void
1349baabaca3SWarner Losh ndashutdown(void *arg, int howto)
1350baabaca3SWarner Losh {
1351baabaca3SWarner Losh 
135238f8addaSAlexander Motin 	if ((howto & RB_NOSYNC) != 0)
135338f8addaSAlexander Motin 		return;
135438f8addaSAlexander Motin 
1355baabaca3SWarner Losh 	ndaflush();
1356baabaca3SWarner Losh }
1357baabaca3SWarner Losh 
1358baabaca3SWarner Losh static void
1359baabaca3SWarner Losh ndasuspend(void *arg)
1360baabaca3SWarner Losh {
1361baabaca3SWarner Losh 
1362baabaca3SWarner Losh 	ndaflush();
1363baabaca3SWarner Losh }
1364