xref: /freebsd/sys/cam/nvme/nvme_da.c (revision fa271a5d09d6dc12cd0e3838cd92a90daaf5819c)
1baabaca3SWarner Losh /*-
2baabaca3SWarner Losh  * Copyright (c) 2015 Netflix, Inc
3baabaca3SWarner Losh  * All rights reserved.
4baabaca3SWarner Losh  *
5baabaca3SWarner Losh  * Redistribution and use in source and binary forms, with or without
6baabaca3SWarner Losh  * modification, are permitted provided that the following conditions
7baabaca3SWarner Losh  * are met:
8baabaca3SWarner Losh  * 1. Redistributions of source code must retain the above copyright
9baabaca3SWarner Losh  *    notice, this list of conditions and the following disclaimer,
10baabaca3SWarner Losh  *    without modification, immediately at the beginning of the file.
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  *
15baabaca3SWarner Losh  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16baabaca3SWarner Losh  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17baabaca3SWarner Losh  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18baabaca3SWarner Losh  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19baabaca3SWarner Losh  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20baabaca3SWarner Losh  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21baabaca3SWarner Losh  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22baabaca3SWarner Losh  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23baabaca3SWarner Losh  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24baabaca3SWarner Losh  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25baabaca3SWarner Losh  *
26baabaca3SWarner Losh  * Derived from ata_da.c:
27baabaca3SWarner Losh  * Copyright (c) 2009 Alexander Motin <mav@FreeBSD.org>
28baabaca3SWarner Losh  */
29baabaca3SWarner Losh 
30baabaca3SWarner Losh #include <sys/cdefs.h>
31baabaca3SWarner Losh __FBSDID("$FreeBSD$");
32baabaca3SWarner Losh 
33baabaca3SWarner Losh #include <sys/param.h>
34baabaca3SWarner Losh 
35baabaca3SWarner Losh #ifdef _KERNEL
36baabaca3SWarner Losh #include <sys/systm.h>
37baabaca3SWarner Losh #include <sys/kernel.h>
38baabaca3SWarner Losh #include <sys/bio.h>
39baabaca3SWarner Losh #include <sys/sysctl.h>
40baabaca3SWarner Losh #include <sys/taskqueue.h>
41baabaca3SWarner Losh #include <sys/lock.h>
42baabaca3SWarner Losh #include <sys/mutex.h>
43baabaca3SWarner Losh #include <sys/conf.h>
44baabaca3SWarner Losh #include <sys/devicestat.h>
45baabaca3SWarner Losh #include <sys/eventhandler.h>
46baabaca3SWarner Losh #include <sys/malloc.h>
47baabaca3SWarner Losh #include <sys/cons.h>
48baabaca3SWarner Losh #include <sys/proc.h>
49baabaca3SWarner Losh #include <sys/reboot.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;
76baabaca3SWarner Losh 
77baabaca3SWarner Losh typedef enum {
78baabaca3SWarner Losh 	NDA_Q_4K   = 0x01,
79baabaca3SWarner Losh 	NDA_Q_NONE = 0x00,
80baabaca3SWarner Losh } nda_quirks;
81baabaca3SWarner Losh 
82baabaca3SWarner Losh #define NDA_Q_BIT_STRING	\
83baabaca3SWarner Losh 	"\020"			\
84baabaca3SWarner Losh 	"\001Bit 0"
85baabaca3SWarner Losh 
86baabaca3SWarner Losh typedef enum {
87baabaca3SWarner Losh 	NDA_CCB_BUFFER_IO	= 0x01,
88baabaca3SWarner Losh 	NDA_CCB_DUMP            = 0x02,
89baabaca3SWarner Losh 	NDA_CCB_TRIM            = 0x03,
90baabaca3SWarner Losh 	NDA_CCB_TYPE_MASK	= 0x0F,
91baabaca3SWarner Losh } nda_ccb_state;
92baabaca3SWarner Losh 
93baabaca3SWarner Losh /* Offsets into our private area for storing information */
94baabaca3SWarner Losh #define ccb_state	ppriv_field0
95baabaca3SWarner Losh #define ccb_bp		ppriv_ptr1
96baabaca3SWarner Losh 
97baabaca3SWarner Losh struct trim_request {
98baabaca3SWarner Losh 	TAILQ_HEAD(, bio) bps;
99baabaca3SWarner Losh };
100baabaca3SWarner Losh struct nda_softc {
101baabaca3SWarner Losh 	struct   cam_iosched_softc *cam_iosched;
102baabaca3SWarner Losh 	int	 outstanding_cmds;	/* Number of active commands */
103baabaca3SWarner Losh 	int	 refcount;		/* Active xpt_action() calls */
104baabaca3SWarner Losh 	nda_state state;
105baabaca3SWarner Losh 	nda_flags flags;
106baabaca3SWarner Losh 	nda_quirks quirks;
107baabaca3SWarner Losh 	int	 unmappedio;
108baabaca3SWarner Losh 	uint32_t  nsid;			/* Namespace ID for this nda device */
109baabaca3SWarner Losh 	struct disk *disk;
110baabaca3SWarner Losh 	struct task		sysctl_task;
111baabaca3SWarner Losh 	struct sysctl_ctx_list	sysctl_ctx;
112baabaca3SWarner Losh 	struct sysctl_oid	*sysctl_tree;
113baabaca3SWarner Losh 	struct trim_request	trim_req;
114baabaca3SWarner Losh #ifdef CAM_IO_STATS
115baabaca3SWarner Losh 	struct sysctl_ctx_list	sysctl_stats_ctx;
116baabaca3SWarner Losh 	struct sysctl_oid	*sysctl_stats_tree;
117baabaca3SWarner Losh 	u_int	timeouts;
118baabaca3SWarner Losh 	u_int	errors;
119baabaca3SWarner Losh 	u_int	invalidations;
120baabaca3SWarner Losh #endif
121baabaca3SWarner Losh };
122baabaca3SWarner Losh 
123baabaca3SWarner Losh /* Need quirk table */
124baabaca3SWarner Losh 
125baabaca3SWarner Losh static	disk_strategy_t	ndastrategy;
126baabaca3SWarner Losh static	dumper_t	ndadump;
127baabaca3SWarner Losh static	periph_init_t	ndainit;
128baabaca3SWarner Losh static	void		ndaasync(void *callback_arg, u_int32_t code,
129baabaca3SWarner Losh 				struct cam_path *path, void *arg);
130baabaca3SWarner Losh static	void		ndasysctlinit(void *context, int pending);
131baabaca3SWarner Losh static	periph_ctor_t	ndaregister;
132baabaca3SWarner Losh static	periph_dtor_t	ndacleanup;
133baabaca3SWarner Losh static	periph_start_t	ndastart;
134baabaca3SWarner Losh static	periph_oninv_t	ndaoninvalidate;
135baabaca3SWarner Losh static	void		ndadone(struct cam_periph *periph,
136baabaca3SWarner Losh 			       union ccb *done_ccb);
137baabaca3SWarner Losh static  int		ndaerror(union ccb *ccb, u_int32_t cam_flags,
138baabaca3SWarner Losh 				u_int32_t sense_flags);
139baabaca3SWarner Losh static void		ndashutdown(void *arg, int howto);
140baabaca3SWarner Losh static void		ndasuspend(void *arg);
141baabaca3SWarner Losh 
142baabaca3SWarner Losh #ifndef	NDA_DEFAULT_SEND_ORDERED
143baabaca3SWarner Losh #define	NDA_DEFAULT_SEND_ORDERED	1
144baabaca3SWarner Losh #endif
145baabaca3SWarner Losh #ifndef NDA_DEFAULT_TIMEOUT
146baabaca3SWarner Losh #define NDA_DEFAULT_TIMEOUT 30	/* Timeout in seconds */
147baabaca3SWarner Losh #endif
148baabaca3SWarner Losh #ifndef	NDA_DEFAULT_RETRY
149baabaca3SWarner Losh #define	NDA_DEFAULT_RETRY	4
150baabaca3SWarner Losh #endif
151baabaca3SWarner Losh 
152baabaca3SWarner Losh 
153baabaca3SWarner Losh //static int nda_retry_count = NDA_DEFAULT_RETRY;
154baabaca3SWarner Losh static int nda_send_ordered = NDA_DEFAULT_SEND_ORDERED;
155baabaca3SWarner Losh static int nda_default_timeout = NDA_DEFAULT_TIMEOUT;
156baabaca3SWarner Losh 
157baabaca3SWarner Losh /*
158baabaca3SWarner Losh  * All NVMe media is non-rotational, so all nvme device instances
159baabaca3SWarner Losh  * share this to implement the sysctl.
160baabaca3SWarner Losh  */
161baabaca3SWarner Losh static int nda_rotating_media = 0;
162baabaca3SWarner Losh 
163baabaca3SWarner Losh static SYSCTL_NODE(_kern_cam, OID_AUTO, nda, CTLFLAG_RD, 0,
164baabaca3SWarner Losh             "CAM Direct Access Disk driver");
165baabaca3SWarner Losh 
166baabaca3SWarner Losh static struct periph_driver ndadriver =
167baabaca3SWarner Losh {
168baabaca3SWarner Losh 	ndainit, "nda",
169baabaca3SWarner Losh 	TAILQ_HEAD_INITIALIZER(ndadriver.units), /* generation */ 0
170baabaca3SWarner Losh };
171baabaca3SWarner Losh 
172baabaca3SWarner Losh PERIPHDRIVER_DECLARE(nda, ndadriver);
173baabaca3SWarner Losh 
174baabaca3SWarner Losh static MALLOC_DEFINE(M_NVMEDA, "nvme_da", "nvme_da buffers");
175baabaca3SWarner Losh 
176baabaca3SWarner Losh /*
177baabaca3SWarner Losh  * nice wrappers. Maybe these belong in nvme_all.c instead of
178baabaca3SWarner Losh  * here, but this is the only place that uses these. Should
179baabaca3SWarner Losh  * we ever grow another NVME periph, we should move them
180baabaca3SWarner Losh  * all there wholesale.
181baabaca3SWarner Losh  */
182baabaca3SWarner Losh 
183baabaca3SWarner Losh static void
184baabaca3SWarner Losh nda_nvme_flush(struct nda_softc *softc, struct ccb_nvmeio *nvmeio)
185baabaca3SWarner Losh {
186baabaca3SWarner Losh 	cam_fill_nvmeio(nvmeio,
187baabaca3SWarner Losh 	    0,			/* retries */
188baabaca3SWarner Losh 	    ndadone,		/* cbfcnp */
189baabaca3SWarner Losh 	    CAM_DIR_NONE,	/* flags */
190baabaca3SWarner Losh 	    NULL,		/* data_ptr */
191baabaca3SWarner Losh 	    0,			/* dxfer_len */
192717bff5dSWarner Losh 	    nda_default_timeout * 1000); /* timeout 30s */
193baabaca3SWarner Losh 	nvme_ns_flush_cmd(&nvmeio->cmd, softc->nsid);
194baabaca3SWarner Losh }
195baabaca3SWarner Losh 
196baabaca3SWarner Losh static void
197baabaca3SWarner Losh nda_nvme_trim(struct nda_softc *softc, struct ccb_nvmeio *nvmeio,
198baabaca3SWarner Losh     void *payload, uint32_t num_ranges)
199baabaca3SWarner Losh {
200baabaca3SWarner Losh 	cam_fill_nvmeio(nvmeio,
201baabaca3SWarner Losh 	    0,			/* retries */
202baabaca3SWarner Losh 	    ndadone,		/* cbfcnp */
203baabaca3SWarner Losh 	    CAM_DIR_OUT,	/* flags */
204baabaca3SWarner Losh 	    payload,		/* data_ptr */
205baabaca3SWarner Losh 	    num_ranges * sizeof(struct nvme_dsm_range), /* dxfer_len */
206717bff5dSWarner Losh 	    nda_default_timeout * 1000); /* timeout 30s */
207baabaca3SWarner Losh 	nvme_ns_trim_cmd(&nvmeio->cmd, softc->nsid, num_ranges);
208baabaca3SWarner Losh }
209baabaca3SWarner Losh 
210baabaca3SWarner Losh static void
211baabaca3SWarner Losh nda_nvme_write(struct nda_softc *softc, struct ccb_nvmeio *nvmeio,
212baabaca3SWarner Losh     void *payload, uint64_t lba, uint32_t len, uint32_t count)
213baabaca3SWarner Losh {
214baabaca3SWarner Losh 	cam_fill_nvmeio(nvmeio,
215baabaca3SWarner Losh 	    0,			/* retries */
216baabaca3SWarner Losh 	    ndadone,		/* cbfcnp */
217baabaca3SWarner Losh 	    CAM_DIR_OUT,	/* flags */
218baabaca3SWarner Losh 	    payload,		/* data_ptr */
219baabaca3SWarner Losh 	    len,		/* dxfer_len */
220717bff5dSWarner Losh 	    nda_default_timeout * 1000); /* timeout 30s */
221baabaca3SWarner Losh 	nvme_ns_write_cmd(&nvmeio->cmd, softc->nsid, lba, count);
222baabaca3SWarner Losh }
223baabaca3SWarner Losh 
224baabaca3SWarner Losh static void
225baabaca3SWarner Losh nda_nvme_rw_bio(struct nda_softc *softc, struct ccb_nvmeio *nvmeio,
226baabaca3SWarner Losh     struct bio *bp, uint32_t rwcmd)
227baabaca3SWarner Losh {
228baabaca3SWarner Losh 	int flags = rwcmd == NVME_OPC_READ ? CAM_DIR_IN : CAM_DIR_OUT;
229baabaca3SWarner Losh 	void *payload;
230baabaca3SWarner Losh 	uint64_t lba;
231baabaca3SWarner Losh 	uint32_t count;
232baabaca3SWarner Losh 
233baabaca3SWarner Losh 	if (bp->bio_flags & BIO_UNMAPPED) {
234baabaca3SWarner Losh 		flags |= CAM_DATA_BIO;
235baabaca3SWarner Losh 		payload = bp;
236baabaca3SWarner Losh 	} else {
237baabaca3SWarner Losh 		payload = bp->bio_data;
238baabaca3SWarner Losh 	}
239baabaca3SWarner Losh 
240baabaca3SWarner Losh 	lba = bp->bio_pblkno;
241baabaca3SWarner Losh 	count = bp->bio_bcount / softc->disk->d_sectorsize;
242baabaca3SWarner Losh 
243baabaca3SWarner Losh 	cam_fill_nvmeio(nvmeio,
244baabaca3SWarner Losh 	    0,			/* retries */
245baabaca3SWarner Losh 	    ndadone,		/* cbfcnp */
246baabaca3SWarner Losh 	    flags,		/* flags */
247baabaca3SWarner Losh 	    payload,		/* data_ptr */
248baabaca3SWarner Losh 	    bp->bio_bcount,	/* dxfer_len */
249717bff5dSWarner Losh 	    nda_default_timeout * 1000); /* timeout 30s */
250baabaca3SWarner Losh 	nvme_ns_rw_cmd(&nvmeio->cmd, rwcmd, softc->nsid, lba, count);
251baabaca3SWarner Losh }
252baabaca3SWarner Losh 
253baabaca3SWarner Losh static int
254baabaca3SWarner Losh ndaopen(struct disk *dp)
255baabaca3SWarner Losh {
256baabaca3SWarner Losh 	struct cam_periph *periph;
257baabaca3SWarner Losh 	struct nda_softc *softc;
258baabaca3SWarner Losh 	int error;
259baabaca3SWarner Losh 
260baabaca3SWarner Losh 	periph = (struct cam_periph *)dp->d_drv1;
261baabaca3SWarner Losh 	if (cam_periph_acquire(periph) != CAM_REQ_CMP) {
262baabaca3SWarner Losh 		return(ENXIO);
263baabaca3SWarner Losh 	}
264baabaca3SWarner Losh 
265baabaca3SWarner Losh 	cam_periph_lock(periph);
266baabaca3SWarner Losh 	if ((error = cam_periph_hold(periph, PRIBIO|PCATCH)) != 0) {
267baabaca3SWarner Losh 		cam_periph_unlock(periph);
268baabaca3SWarner Losh 		cam_periph_release(periph);
269baabaca3SWarner Losh 		return (error);
270baabaca3SWarner Losh 	}
271baabaca3SWarner Losh 
272baabaca3SWarner Losh 	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE | CAM_DEBUG_PERIPH,
273baabaca3SWarner Losh 	    ("ndaopen\n"));
274baabaca3SWarner Losh 
275baabaca3SWarner Losh 	softc = (struct nda_softc *)periph->softc;
276baabaca3SWarner Losh 	softc->flags |= NDA_FLAG_OPEN;
277baabaca3SWarner Losh 
278baabaca3SWarner Losh 	cam_periph_unhold(periph);
279baabaca3SWarner Losh 	cam_periph_unlock(periph);
280baabaca3SWarner Losh 	return (0);
281baabaca3SWarner Losh }
282baabaca3SWarner Losh 
283baabaca3SWarner Losh static int
284baabaca3SWarner Losh ndaclose(struct disk *dp)
285baabaca3SWarner Losh {
286baabaca3SWarner Losh 	struct	cam_periph *periph;
287baabaca3SWarner Losh 	struct	nda_softc *softc;
288baabaca3SWarner Losh 	union ccb *ccb;
289baabaca3SWarner Losh 	int error;
290baabaca3SWarner Losh 
291baabaca3SWarner Losh 	periph = (struct cam_periph *)dp->d_drv1;
292baabaca3SWarner Losh 	softc = (struct nda_softc *)periph->softc;
293baabaca3SWarner Losh 	cam_periph_lock(periph);
294baabaca3SWarner Losh 
295baabaca3SWarner Losh 	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE | CAM_DEBUG_PERIPH,
296baabaca3SWarner Losh 	    ("ndaclose\n"));
297baabaca3SWarner Losh 
298baabaca3SWarner Losh 	if ((softc->flags & NDA_FLAG_DIRTY) != 0 &&
299baabaca3SWarner Losh 	    (periph->flags & CAM_PERIPH_INVALID) == 0 &&
300baabaca3SWarner Losh 	    cam_periph_hold(periph, PRIBIO) == 0) {
301baabaca3SWarner Losh 
302baabaca3SWarner Losh 		ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL);
303baabaca3SWarner Losh 		nda_nvme_flush(softc, &ccb->nvmeio);
304baabaca3SWarner Losh 		error = cam_periph_runccb(ccb, ndaerror, /*cam_flags*/0,
305baabaca3SWarner Losh 		    /*sense_flags*/0, softc->disk->d_devstat);
306baabaca3SWarner Losh 
307baabaca3SWarner Losh 		if (error != 0)
308baabaca3SWarner Losh 			xpt_print(periph->path, "Synchronize cache failed\n");
309baabaca3SWarner Losh 		else
310baabaca3SWarner Losh 			softc->flags &= ~NDA_FLAG_DIRTY;
311baabaca3SWarner Losh 		xpt_release_ccb(ccb);
312baabaca3SWarner Losh 		cam_periph_unhold(periph);
313baabaca3SWarner Losh 	}
314baabaca3SWarner Losh 
315baabaca3SWarner Losh 	softc->flags &= ~NDA_FLAG_OPEN;
316baabaca3SWarner Losh 
317baabaca3SWarner Losh 	while (softc->refcount != 0)
318baabaca3SWarner Losh 		cam_periph_sleep(periph, &softc->refcount, PRIBIO, "ndaclose", 1);
319baabaca3SWarner Losh 	cam_periph_unlock(periph);
320baabaca3SWarner Losh 	cam_periph_release(periph);
321baabaca3SWarner Losh 	return (0);
322baabaca3SWarner Losh }
323baabaca3SWarner Losh 
324baabaca3SWarner Losh static void
325baabaca3SWarner Losh ndaschedule(struct cam_periph *periph)
326baabaca3SWarner Losh {
327baabaca3SWarner Losh 	struct nda_softc *softc = (struct nda_softc *)periph->softc;
328baabaca3SWarner Losh 
329baabaca3SWarner Losh 	if (softc->state != NDA_STATE_NORMAL)
330baabaca3SWarner Losh 		return;
331baabaca3SWarner Losh 
332baabaca3SWarner Losh 	cam_iosched_schedule(softc->cam_iosched, periph);
333baabaca3SWarner Losh }
334baabaca3SWarner Losh 
335baabaca3SWarner Losh /*
336baabaca3SWarner Losh  * Actually translate the requested transfer into one the physical driver
337baabaca3SWarner Losh  * can understand.  The transfer is described by a buf and will include
338baabaca3SWarner Losh  * only one physical transfer.
339baabaca3SWarner Losh  */
340baabaca3SWarner Losh static void
341baabaca3SWarner Losh ndastrategy(struct bio *bp)
342baabaca3SWarner Losh {
343baabaca3SWarner Losh 	struct cam_periph *periph;
344baabaca3SWarner Losh 	struct nda_softc *softc;
345baabaca3SWarner Losh 
346baabaca3SWarner Losh 	periph = (struct cam_periph *)bp->bio_disk->d_drv1;
347baabaca3SWarner Losh 	softc = (struct nda_softc *)periph->softc;
348baabaca3SWarner Losh 
349baabaca3SWarner Losh 	cam_periph_lock(periph);
350baabaca3SWarner Losh 
351baabaca3SWarner Losh 	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("ndastrategy(%p)\n", bp));
352baabaca3SWarner Losh 
353baabaca3SWarner Losh 	/*
354baabaca3SWarner Losh 	 * If the device has been made invalid, error out
355baabaca3SWarner Losh 	 */
356baabaca3SWarner Losh 	if ((periph->flags & CAM_PERIPH_INVALID) != 0) {
357baabaca3SWarner Losh 		cam_periph_unlock(periph);
358baabaca3SWarner Losh 		biofinish(bp, NULL, ENXIO);
359baabaca3SWarner Losh 		return;
360baabaca3SWarner Losh 	}
361baabaca3SWarner Losh 
362baabaca3SWarner Losh 	/*
363baabaca3SWarner Losh 	 * Place it in the queue of disk activities for this disk
364baabaca3SWarner Losh 	 */
365baabaca3SWarner Losh 	cam_iosched_queue_work(softc->cam_iosched, bp);
366baabaca3SWarner Losh 
367baabaca3SWarner Losh 	/*
368baabaca3SWarner Losh 	 * Schedule ourselves for performing the work.
369baabaca3SWarner Losh 	 */
370baabaca3SWarner Losh 	ndaschedule(periph);
371baabaca3SWarner Losh 	cam_periph_unlock(periph);
372baabaca3SWarner Losh 
373baabaca3SWarner Losh 	return;
374baabaca3SWarner Losh }
375baabaca3SWarner Losh 
376baabaca3SWarner Losh static int
377baabaca3SWarner Losh ndadump(void *arg, void *virtual, vm_offset_t physical, off_t offset, size_t length)
378baabaca3SWarner Losh {
379baabaca3SWarner Losh 	struct	    cam_periph *periph;
380baabaca3SWarner Losh 	struct	    nda_softc *softc;
381baabaca3SWarner Losh 	u_int	    secsize;
382c4231018SWarner Losh 	struct ccb_nvmeio nvmeio;
383baabaca3SWarner Losh 	struct	    disk *dp;
384baabaca3SWarner Losh 	uint64_t    lba;
385baabaca3SWarner Losh 	uint32_t    count;
386baabaca3SWarner Losh 	int	    error = 0;
387baabaca3SWarner Losh 
388baabaca3SWarner Losh 	dp = arg;
389baabaca3SWarner Losh 	periph = dp->d_drv1;
390baabaca3SWarner Losh 	softc = (struct nda_softc *)periph->softc;
391baabaca3SWarner Losh 	cam_periph_lock(periph);
392baabaca3SWarner Losh 	secsize = softc->disk->d_sectorsize;
393baabaca3SWarner Losh 	lba = offset / secsize;
394baabaca3SWarner Losh 	count = length / secsize;
395baabaca3SWarner Losh 
396baabaca3SWarner Losh 	if ((periph->flags & CAM_PERIPH_INVALID) != 0) {
397baabaca3SWarner Losh 		cam_periph_unlock(periph);
398baabaca3SWarner Losh 		return (ENXIO);
399baabaca3SWarner Losh 	}
400baabaca3SWarner Losh 
401*fa271a5dSWarner Losh 	/* xpt_get_ccb returns a zero'd allocation for the ccb, mimic that here */
402*fa271a5dSWarner Losh 	memset(&nvmeio, 0, sizeof(nvmeio));
403baabaca3SWarner Losh 	if (length > 0) {
404c4231018SWarner Losh 		xpt_setup_ccb(&nvmeio.ccb_h, periph->path, CAM_PRIORITY_NORMAL);
405c4231018SWarner Losh 		nvmeio.ccb_h.ccb_state = NDA_CCB_DUMP;
406c4231018SWarner Losh 		nda_nvme_write(softc, &nvmeio, virtual, lba, length, count);
407c4231018SWarner Losh 		xpt_polled_action((union ccb *)&nvmeio);
408baabaca3SWarner Losh 
409c4231018SWarner Losh 		error = cam_periph_error((union ccb *)&nvmeio,
410baabaca3SWarner Losh 		    0, SF_NO_RECOVERY | SF_NO_RETRY, NULL);
411c4231018SWarner Losh 		if ((nvmeio.ccb_h.status & CAM_DEV_QFRZN) != 0)
412c4231018SWarner Losh 			cam_release_devq(nvmeio.ccb_h.path, /*relsim_flags*/0,
413baabaca3SWarner Losh 			    /*reduction*/0, /*timeout*/0, /*getcount_only*/0);
414baabaca3SWarner Losh 		if (error != 0)
415baabaca3SWarner Losh 			printf("Aborting dump due to I/O error.\n");
416baabaca3SWarner Losh 
417baabaca3SWarner Losh 		cam_periph_unlock(periph);
418baabaca3SWarner Losh 		return (error);
419baabaca3SWarner Losh 	}
420baabaca3SWarner Losh 
421baabaca3SWarner Losh 	/* Flush */
422c4231018SWarner Losh 	xpt_setup_ccb(&nvmeio.ccb_h, periph->path, CAM_PRIORITY_NORMAL);
423baabaca3SWarner Losh 
424c4231018SWarner Losh 	nvmeio.ccb_h.ccb_state = NDA_CCB_DUMP;
425c4231018SWarner Losh 	nda_nvme_flush(softc, &nvmeio);
426c4231018SWarner Losh 	xpt_polled_action((union ccb *)&nvmeio);
427baabaca3SWarner Losh 
428c4231018SWarner Losh 	error = cam_periph_error((union ccb *)&nvmeio,
429baabaca3SWarner Losh 	    0, SF_NO_RECOVERY | SF_NO_RETRY, NULL);
430c4231018SWarner Losh 	if ((nvmeio.ccb_h.status & CAM_DEV_QFRZN) != 0)
431c4231018SWarner Losh 		cam_release_devq(nvmeio.ccb_h.path, /*relsim_flags*/0,
432baabaca3SWarner Losh 		    /*reduction*/0, /*timeout*/0, /*getcount_only*/0);
433baabaca3SWarner Losh 	if (error != 0)
434baabaca3SWarner Losh 		xpt_print(periph->path, "flush cmd failed\n");
435baabaca3SWarner Losh 	cam_periph_unlock(periph);
436baabaca3SWarner Losh 	return (error);
437baabaca3SWarner Losh }
438baabaca3SWarner Losh 
439baabaca3SWarner Losh static void
440baabaca3SWarner Losh ndainit(void)
441baabaca3SWarner Losh {
442baabaca3SWarner Losh 	cam_status status;
443baabaca3SWarner Losh 
444baabaca3SWarner Losh 	/*
445baabaca3SWarner Losh 	 * Install a global async callback.  This callback will
446baabaca3SWarner Losh 	 * receive async callbacks like "new device found".
447baabaca3SWarner Losh 	 */
448baabaca3SWarner Losh 	status = xpt_register_async(AC_FOUND_DEVICE, ndaasync, NULL, NULL);
449baabaca3SWarner Losh 
450baabaca3SWarner Losh 	if (status != CAM_REQ_CMP) {
451baabaca3SWarner Losh 		printf("nda: Failed to attach master async callback "
452baabaca3SWarner Losh 		       "due to status 0x%x!\n", status);
453baabaca3SWarner Losh 	} else if (nda_send_ordered) {
454baabaca3SWarner Losh 
455baabaca3SWarner Losh 		/* Register our event handlers */
456baabaca3SWarner Losh 		if ((EVENTHANDLER_REGISTER(power_suspend, ndasuspend,
457baabaca3SWarner Losh 					   NULL, EVENTHANDLER_PRI_LAST)) == NULL)
458baabaca3SWarner Losh 		    printf("ndainit: power event registration failed!\n");
459baabaca3SWarner Losh 		if ((EVENTHANDLER_REGISTER(shutdown_post_sync, ndashutdown,
460baabaca3SWarner Losh 					   NULL, SHUTDOWN_PRI_DEFAULT)) == NULL)
461baabaca3SWarner Losh 		    printf("ndainit: shutdown event registration failed!\n");
462baabaca3SWarner Losh 	}
463baabaca3SWarner Losh }
464baabaca3SWarner Losh 
465baabaca3SWarner Losh /*
466baabaca3SWarner Losh  * Callback from GEOM, called when it has finished cleaning up its
467baabaca3SWarner Losh  * resources.
468baabaca3SWarner Losh  */
469baabaca3SWarner Losh static void
470baabaca3SWarner Losh ndadiskgonecb(struct disk *dp)
471baabaca3SWarner Losh {
472baabaca3SWarner Losh 	struct cam_periph *periph;
473baabaca3SWarner Losh 
474baabaca3SWarner Losh 	periph = (struct cam_periph *)dp->d_drv1;
475baabaca3SWarner Losh 
476baabaca3SWarner Losh 	cam_periph_release(periph);
477baabaca3SWarner Losh }
478baabaca3SWarner Losh 
479baabaca3SWarner Losh static void
480baabaca3SWarner Losh ndaoninvalidate(struct cam_periph *periph)
481baabaca3SWarner Losh {
482baabaca3SWarner Losh 	struct nda_softc *softc;
483baabaca3SWarner Losh 
484baabaca3SWarner Losh 	softc = (struct nda_softc *)periph->softc;
485baabaca3SWarner Losh 
486baabaca3SWarner Losh 	/*
487baabaca3SWarner Losh 	 * De-register any async callbacks.
488baabaca3SWarner Losh 	 */
489baabaca3SWarner Losh 	xpt_register_async(0, ndaasync, periph, periph->path);
490baabaca3SWarner Losh #ifdef CAM_IO_STATS
491baabaca3SWarner Losh 	softc->invalidations++;
492baabaca3SWarner Losh #endif
493baabaca3SWarner Losh 
494baabaca3SWarner Losh 	/*
495baabaca3SWarner Losh 	 * Return all queued I/O with ENXIO.
496baabaca3SWarner Losh 	 * XXX Handle any transactions queued to the card
497baabaca3SWarner Losh 	 *     with XPT_ABORT_CCB.
498baabaca3SWarner Losh 	 */
499baabaca3SWarner Losh 	cam_iosched_flush(softc->cam_iosched, NULL, ENXIO);
500baabaca3SWarner Losh 
501baabaca3SWarner Losh 	disk_gone(softc->disk);
502baabaca3SWarner Losh }
503baabaca3SWarner Losh 
504baabaca3SWarner Losh static void
505baabaca3SWarner Losh ndacleanup(struct cam_periph *periph)
506baabaca3SWarner Losh {
507baabaca3SWarner Losh 	struct nda_softc *softc;
508baabaca3SWarner Losh 
509baabaca3SWarner Losh 	softc = (struct nda_softc *)periph->softc;
510baabaca3SWarner Losh 
511baabaca3SWarner Losh 	cam_periph_unlock(periph);
512baabaca3SWarner Losh 
513baabaca3SWarner Losh 	cam_iosched_fini(softc->cam_iosched);
514baabaca3SWarner Losh 
515baabaca3SWarner Losh 	/*
516baabaca3SWarner Losh 	 * If we can't free the sysctl tree, oh well...
517baabaca3SWarner Losh 	 */
518baabaca3SWarner Losh 	if ((softc->flags & NDA_FLAG_SCTX_INIT) != 0) {
519baabaca3SWarner Losh #ifdef CAM_IO_STATS
520baabaca3SWarner Losh 		if (sysctl_ctx_free(&softc->sysctl_stats_ctx) != 0)
521baabaca3SWarner Losh 			xpt_print(periph->path,
522baabaca3SWarner Losh 			    "can't remove sysctl stats context\n");
523baabaca3SWarner Losh #endif
524baabaca3SWarner Losh 		if (sysctl_ctx_free(&softc->sysctl_ctx) != 0)
525baabaca3SWarner Losh 			xpt_print(periph->path,
526baabaca3SWarner Losh 			    "can't remove sysctl context\n");
527baabaca3SWarner Losh 	}
528baabaca3SWarner Losh 
529baabaca3SWarner Losh 	disk_destroy(softc->disk);
530baabaca3SWarner Losh 	free(softc, M_DEVBUF);
531baabaca3SWarner Losh 	cam_periph_lock(periph);
532baabaca3SWarner Losh }
533baabaca3SWarner Losh 
534baabaca3SWarner Losh static void
535baabaca3SWarner Losh ndaasync(void *callback_arg, u_int32_t code,
536baabaca3SWarner Losh 	struct cam_path *path, void *arg)
537baabaca3SWarner Losh {
538baabaca3SWarner Losh 	struct cam_periph *periph;
539baabaca3SWarner Losh 
540baabaca3SWarner Losh 	periph = (struct cam_periph *)callback_arg;
541baabaca3SWarner Losh 	switch (code) {
542baabaca3SWarner Losh 	case AC_FOUND_DEVICE:
543baabaca3SWarner Losh 	{
544baabaca3SWarner Losh 		struct ccb_getdev *cgd;
545baabaca3SWarner Losh 		cam_status status;
546baabaca3SWarner Losh 
547baabaca3SWarner Losh 		cgd = (struct ccb_getdev *)arg;
548baabaca3SWarner Losh 		if (cgd == NULL)
549baabaca3SWarner Losh 			break;
550baabaca3SWarner Losh 
551baabaca3SWarner Losh 		if (cgd->protocol != PROTO_NVME)
552baabaca3SWarner Losh 			break;
553baabaca3SWarner Losh 
554baabaca3SWarner Losh 		/*
555baabaca3SWarner Losh 		 * Allocate a peripheral instance for
556baabaca3SWarner Losh 		 * this device and start the probe
557baabaca3SWarner Losh 		 * process.
558baabaca3SWarner Losh 		 */
559baabaca3SWarner Losh 		status = cam_periph_alloc(ndaregister, ndaoninvalidate,
560baabaca3SWarner Losh 					  ndacleanup, ndastart,
561baabaca3SWarner Losh 					  "nda", CAM_PERIPH_BIO,
562baabaca3SWarner Losh 					  path, ndaasync,
563baabaca3SWarner Losh 					  AC_FOUND_DEVICE, cgd);
564baabaca3SWarner Losh 
565baabaca3SWarner Losh 		if (status != CAM_REQ_CMP
566baabaca3SWarner Losh 		 && status != CAM_REQ_INPROG)
567baabaca3SWarner Losh 			printf("ndaasync: Unable to attach to new device "
568baabaca3SWarner Losh 				"due to status 0x%x\n", status);
569baabaca3SWarner Losh 		break;
570baabaca3SWarner Losh 	}
571baabaca3SWarner Losh 	case AC_ADVINFO_CHANGED:
572baabaca3SWarner Losh 	{
573baabaca3SWarner Losh 		uintptr_t buftype;
574baabaca3SWarner Losh 
575baabaca3SWarner Losh 		buftype = (uintptr_t)arg;
576baabaca3SWarner Losh 		if (buftype == CDAI_TYPE_PHYS_PATH) {
577baabaca3SWarner Losh 			struct nda_softc *softc;
578baabaca3SWarner Losh 
579baabaca3SWarner Losh 			softc = periph->softc;
580baabaca3SWarner Losh 			disk_attr_changed(softc->disk, "GEOM::physpath",
581baabaca3SWarner Losh 					  M_NOWAIT);
582baabaca3SWarner Losh 		}
583baabaca3SWarner Losh 		break;
584baabaca3SWarner Losh 	}
585baabaca3SWarner Losh 	case AC_LOST_DEVICE:
586baabaca3SWarner Losh 	default:
587baabaca3SWarner Losh 		cam_periph_async(periph, code, path, arg);
588baabaca3SWarner Losh 		break;
589baabaca3SWarner Losh 	}
590baabaca3SWarner Losh }
591baabaca3SWarner Losh 
592baabaca3SWarner Losh static void
593baabaca3SWarner Losh ndasysctlinit(void *context, int pending)
594baabaca3SWarner Losh {
595baabaca3SWarner Losh 	struct cam_periph *periph;
596baabaca3SWarner Losh 	struct nda_softc *softc;
597baabaca3SWarner Losh 	char tmpstr[80], tmpstr2[80];
598baabaca3SWarner Losh 
599baabaca3SWarner Losh 	periph = (struct cam_periph *)context;
600baabaca3SWarner Losh 
601baabaca3SWarner Losh 	/* periph was held for us when this task was enqueued */
602baabaca3SWarner Losh 	if ((periph->flags & CAM_PERIPH_INVALID) != 0) {
603baabaca3SWarner Losh 		cam_periph_release(periph);
604baabaca3SWarner Losh 		return;
605baabaca3SWarner Losh 	}
606baabaca3SWarner Losh 
607baabaca3SWarner Losh 	softc = (struct nda_softc *)periph->softc;
608baabaca3SWarner Losh 	snprintf(tmpstr, sizeof(tmpstr), "CAM NDA unit %d", periph->unit_number);
609baabaca3SWarner Losh 	snprintf(tmpstr2, sizeof(tmpstr2), "%d", periph->unit_number);
610baabaca3SWarner Losh 
611baabaca3SWarner Losh 	sysctl_ctx_init(&softc->sysctl_ctx);
612baabaca3SWarner Losh 	softc->flags |= NDA_FLAG_SCTX_INIT;
6134c484fd2SEd Schouten 	softc->sysctl_tree = SYSCTL_ADD_NODE_WITH_LABEL(&softc->sysctl_ctx,
614baabaca3SWarner Losh 		SYSCTL_STATIC_CHILDREN(_kern_cam_nda), OID_AUTO, tmpstr2,
6154c484fd2SEd Schouten 		CTLFLAG_RD, 0, tmpstr, "device_index");
616baabaca3SWarner Losh 	if (softc->sysctl_tree == NULL) {
617baabaca3SWarner Losh 		printf("ndasysctlinit: unable to allocate sysctl tree\n");
618baabaca3SWarner Losh 		cam_periph_release(periph);
619baabaca3SWarner Losh 		return;
620baabaca3SWarner Losh 	}
621baabaca3SWarner Losh 
622baabaca3SWarner Losh 	SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree),
623baabaca3SWarner Losh 		OID_AUTO, "unmapped_io", CTLFLAG_RD | CTLFLAG_MPSAFE,
624baabaca3SWarner Losh 		&softc->unmappedio, 0, "Unmapped I/O leaf");
625baabaca3SWarner Losh 
626baabaca3SWarner Losh 	SYSCTL_ADD_INT(&softc->sysctl_ctx,
627baabaca3SWarner Losh 		       SYSCTL_CHILDREN(softc->sysctl_tree),
628baabaca3SWarner Losh 		       OID_AUTO,
629baabaca3SWarner Losh 		       "rotating",
630baabaca3SWarner Losh 		       CTLFLAG_RD | CTLFLAG_MPSAFE,
631baabaca3SWarner Losh 		       &nda_rotating_media,
632baabaca3SWarner Losh 		       0,
633baabaca3SWarner Losh 		       "Rotating media");
634baabaca3SWarner Losh 
635baabaca3SWarner Losh #ifdef CAM_IO_STATS
636baabaca3SWarner Losh 	softc->sysctl_stats_tree = SYSCTL_ADD_NODE(&softc->sysctl_stats_ctx,
637baabaca3SWarner Losh 		SYSCTL_CHILDREN(softc->sysctl_tree), OID_AUTO, "stats",
638baabaca3SWarner Losh 		CTLFLAG_RD, 0, "Statistics");
639baabaca3SWarner Losh 	if (softc->sysctl_stats_tree == NULL) {
640baabaca3SWarner Losh 		printf("ndasysctlinit: unable to allocate sysctl tree for stats\n");
641baabaca3SWarner Losh 		cam_periph_release(periph);
642baabaca3SWarner Losh 		return;
643baabaca3SWarner Losh 	}
644baabaca3SWarner Losh 	SYSCTL_ADD_INT(&softc->sysctl_stats_ctx,
645baabaca3SWarner Losh 		SYSCTL_CHILDREN(softc->sysctl_stats_tree),
646baabaca3SWarner Losh 		OID_AUTO, "timeouts", CTLFLAG_RD | CTLFLAG_MPSAFE,
647baabaca3SWarner Losh 		&softc->timeouts, 0,
648baabaca3SWarner Losh 		"Device timeouts reported by the SIM");
649baabaca3SWarner Losh 	SYSCTL_ADD_INT(&softc->sysctl_stats_ctx,
650baabaca3SWarner Losh 		SYSCTL_CHILDREN(softc->sysctl_stats_tree),
651baabaca3SWarner Losh 		OID_AUTO, "errors", CTLFLAG_RD | CTLFLAG_MPSAFE,
652baabaca3SWarner Losh 		&softc->errors, 0,
653baabaca3SWarner Losh 		"Transport errors reported by the SIM.");
654baabaca3SWarner Losh 	SYSCTL_ADD_INT(&softc->sysctl_stats_ctx,
655baabaca3SWarner Losh 		SYSCTL_CHILDREN(softc->sysctl_stats_tree),
656baabaca3SWarner Losh 		OID_AUTO, "pack_invalidations", CTLFLAG_RD | CTLFLAG_MPSAFE,
657baabaca3SWarner Losh 		&softc->invalidations, 0,
658baabaca3SWarner Losh 		"Device pack invalidations.");
659baabaca3SWarner Losh #endif
660baabaca3SWarner Losh 
661baabaca3SWarner Losh 	cam_iosched_sysctl_init(softc->cam_iosched, &softc->sysctl_ctx,
662baabaca3SWarner Losh 	    softc->sysctl_tree);
663baabaca3SWarner Losh 
664baabaca3SWarner Losh 	cam_periph_release(periph);
665baabaca3SWarner Losh }
666baabaca3SWarner Losh 
667baabaca3SWarner Losh static int
668baabaca3SWarner Losh ndagetattr(struct bio *bp)
669baabaca3SWarner Losh {
670baabaca3SWarner Losh 	int ret;
671baabaca3SWarner Losh 	struct cam_periph *periph;
672baabaca3SWarner Losh 
673baabaca3SWarner Losh 	periph = (struct cam_periph *)bp->bio_disk->d_drv1;
674baabaca3SWarner Losh 	cam_periph_lock(periph);
675baabaca3SWarner Losh 	ret = xpt_getattr(bp->bio_data, bp->bio_length, bp->bio_attribute,
676baabaca3SWarner Losh 	    periph->path);
677baabaca3SWarner Losh 	cam_periph_unlock(periph);
678baabaca3SWarner Losh 	if (ret == 0)
679baabaca3SWarner Losh 		bp->bio_completed = bp->bio_length;
680baabaca3SWarner Losh 	return ret;
681baabaca3SWarner Losh }
682baabaca3SWarner Losh 
683baabaca3SWarner Losh static cam_status
684baabaca3SWarner Losh ndaregister(struct cam_periph *periph, void *arg)
685baabaca3SWarner Losh {
686baabaca3SWarner Losh 	struct nda_softc *softc;
687baabaca3SWarner Losh 	struct disk *disk;
688baabaca3SWarner Losh 	struct ccb_pathinq cpi;
689baabaca3SWarner Losh 	const struct nvme_namespace_data *nsd;
690baabaca3SWarner Losh 	const struct nvme_controller_data *cd;
691baabaca3SWarner Losh 	char   announce_buf[80];
692baabaca3SWarner Losh 	u_int maxio;
693baabaca3SWarner Losh 	int quirks;
694baabaca3SWarner Losh 
6959f8ed7e4SWarner Losh 	nsd = nvme_get_identify_ns(periph);
6969f8ed7e4SWarner Losh 	cd = nvme_get_identify_cntrl(periph);
697baabaca3SWarner Losh 
698baabaca3SWarner Losh 	softc = (struct nda_softc *)malloc(sizeof(*softc), M_DEVBUF,
699baabaca3SWarner Losh 	    M_NOWAIT | M_ZERO);
700baabaca3SWarner Losh 
701baabaca3SWarner Losh 	if (softc == NULL) {
702baabaca3SWarner Losh 		printf("ndaregister: Unable to probe new device. "
703baabaca3SWarner Losh 		    "Unable to allocate softc\n");
704baabaca3SWarner Losh 		return(CAM_REQ_CMP_ERR);
705baabaca3SWarner Losh 	}
706baabaca3SWarner Losh 
707baabaca3SWarner Losh 	if (cam_iosched_init(&softc->cam_iosched, periph) != 0) {
708baabaca3SWarner Losh 		printf("ndaregister: Unable to probe new device. "
709baabaca3SWarner Losh 		       "Unable to allocate iosched memory\n");
710baabaca3SWarner Losh 		return(CAM_REQ_CMP_ERR);
711baabaca3SWarner Losh 	}
712baabaca3SWarner Losh 
713baabaca3SWarner Losh 	/* ident_data parsing */
714baabaca3SWarner Losh 
715baabaca3SWarner Losh 	periph->softc = softc;
716baabaca3SWarner Losh 
717baabaca3SWarner Losh 	softc->quirks = NDA_Q_NONE;
718baabaca3SWarner Losh 
719baabaca3SWarner Losh 	bzero(&cpi, sizeof(cpi));
720baabaca3SWarner Losh 	xpt_setup_ccb(&cpi.ccb_h, periph->path, CAM_PRIORITY_NONE);
721baabaca3SWarner Losh 	cpi.ccb_h.func_code = XPT_PATH_INQ;
722baabaca3SWarner Losh 	xpt_action((union ccb *)&cpi);
723baabaca3SWarner Losh 
724baabaca3SWarner Losh 	TASK_INIT(&softc->sysctl_task, 0, ndasysctlinit, periph);
725baabaca3SWarner Losh 
726baabaca3SWarner Losh 	/*
727baabaca3SWarner Losh 	 * The name space ID is the lun, save it for later I/O
728baabaca3SWarner Losh 	 */
7291d6e8110SWarner Losh 	softc->nsid = (uint32_t)xpt_path_lun_id(periph->path);
730baabaca3SWarner Losh 
731baabaca3SWarner Losh 	/*
732baabaca3SWarner Losh 	 * Register this media as a disk
733baabaca3SWarner Losh 	 */
734baabaca3SWarner Losh 	(void)cam_periph_hold(periph, PRIBIO);
735baabaca3SWarner Losh 	cam_periph_unlock(periph);
736baabaca3SWarner Losh 	snprintf(announce_buf, sizeof(announce_buf),
737baabaca3SWarner Losh 	    "kern.cam.nda.%d.quirks", periph->unit_number);
738baabaca3SWarner Losh 	quirks = softc->quirks;
739baabaca3SWarner Losh 	TUNABLE_INT_FETCH(announce_buf, &quirks);
740baabaca3SWarner Losh 	softc->quirks = quirks;
741baabaca3SWarner Losh 	cam_iosched_set_sort_queue(softc->cam_iosched, 0);
742baabaca3SWarner Losh 	softc->disk = disk = disk_alloc();
743baabaca3SWarner Losh 	strlcpy(softc->disk->d_descr, cd->mn,
744baabaca3SWarner Losh 	    MIN(sizeof(softc->disk->d_descr), sizeof(cd->mn)));
745baabaca3SWarner Losh 	strlcpy(softc->disk->d_ident, cd->sn,
746baabaca3SWarner Losh 	    MIN(sizeof(softc->disk->d_ident), sizeof(cd->sn)));
74717160457SAlexander Motin 	disk->d_rotation_rate = DISK_RR_NON_ROTATING;
748baabaca3SWarner Losh 	disk->d_open = ndaopen;
749baabaca3SWarner Losh 	disk->d_close = ndaclose;
750baabaca3SWarner Losh 	disk->d_strategy = ndastrategy;
751baabaca3SWarner Losh 	disk->d_getattr = ndagetattr;
752baabaca3SWarner Losh 	disk->d_dump = ndadump;
753baabaca3SWarner Losh 	disk->d_gone = ndadiskgonecb;
754baabaca3SWarner Losh 	disk->d_name = "nda";
755baabaca3SWarner Losh 	disk->d_drv1 = periph;
756baabaca3SWarner Losh 	disk->d_unit = periph->unit_number;
757baabaca3SWarner Losh 	maxio = cpi.maxio;		/* Honor max I/O size of SIM */
758baabaca3SWarner Losh 	if (maxio == 0)
759baabaca3SWarner Losh 		maxio = DFLTPHYS;	/* traditional default */
760baabaca3SWarner Losh 	else if (maxio > MAXPHYS)
761baabaca3SWarner Losh 		maxio = MAXPHYS;	/* for safety */
762baabaca3SWarner Losh 	disk->d_maxsize = maxio;
763baabaca3SWarner Losh 	disk->d_sectorsize = 1 << nsd->lbaf[nsd->flbas.format].lbads;
764baabaca3SWarner Losh 	disk->d_mediasize = (off_t)(disk->d_sectorsize * nsd->nsze);
765baabaca3SWarner Losh 	disk->d_delmaxsize = disk->d_mediasize;
766baabaca3SWarner Losh 	disk->d_flags = DISKFLAG_DIRECT_COMPLETION;
767baabaca3SWarner Losh //	if (cd->oncs.dsm) // XXX broken?
768baabaca3SWarner Losh 		disk->d_flags |= DISKFLAG_CANDELETE;
769baabaca3SWarner Losh 	if (cd->vwc.present)
770baabaca3SWarner Losh 		disk->d_flags |= DISKFLAG_CANFLUSHCACHE;
771baabaca3SWarner Losh 	if ((cpi.hba_misc & PIM_UNMAPPED) != 0) {
772baabaca3SWarner Losh 		disk->d_flags |= DISKFLAG_UNMAPPED_BIO;
773baabaca3SWarner Losh 		softc->unmappedio = 1;
774baabaca3SWarner Losh 	}
775baabaca3SWarner Losh 	/*
776baabaca3SWarner Losh 	 * d_ident and d_descr are both far bigger than the length of either
777baabaca3SWarner Losh 	 *  the serial or model number strings.
778baabaca3SWarner Losh 	 */
779baabaca3SWarner Losh 	nvme_strvis(disk->d_descr, cd->mn,
780baabaca3SWarner Losh 	    sizeof(disk->d_descr), NVME_MODEL_NUMBER_LENGTH);
781baabaca3SWarner Losh 	nvme_strvis(disk->d_ident, cd->sn,
782baabaca3SWarner Losh 	    sizeof(disk->d_ident), NVME_SERIAL_NUMBER_LENGTH);
783baabaca3SWarner Losh 	disk->d_hba_vendor = cpi.hba_vendor;
784baabaca3SWarner Losh 	disk->d_hba_device = cpi.hba_device;
785baabaca3SWarner Losh 	disk->d_hba_subvendor = cpi.hba_subvendor;
786baabaca3SWarner Losh 	disk->d_hba_subdevice = cpi.hba_subdevice;
787baabaca3SWarner Losh 	disk->d_stripesize = disk->d_sectorsize;
788baabaca3SWarner Losh 	disk->d_stripeoffset = 0;
789baabaca3SWarner Losh 	disk->d_devstat = devstat_new_entry(periph->periph_name,
790baabaca3SWarner Losh 	    periph->unit_number, disk->d_sectorsize,
791baabaca3SWarner Losh 	    DEVSTAT_ALL_SUPPORTED,
792baabaca3SWarner Losh 	    DEVSTAT_TYPE_DIRECT | XPORT_DEVSTAT_TYPE(cpi.transport),
793baabaca3SWarner Losh 	    DEVSTAT_PRIORITY_DISK);
794d45e1674SWarner Losh 	/*
795d45e1674SWarner Losh 	 * Add alias for older nvd drives to ease transition.
796d45e1674SWarner Losh 	 */
797d45e1674SWarner Losh 	disk_add_alias(disk, "nvd");
798baabaca3SWarner Losh 
799baabaca3SWarner Losh 	/*
800baabaca3SWarner Losh 	 * Acquire a reference to the periph before we register with GEOM.
801baabaca3SWarner Losh 	 * We'll release this reference once GEOM calls us back (via
802baabaca3SWarner Losh 	 * ndadiskgonecb()) telling us that our provider has been freed.
803baabaca3SWarner Losh 	 */
804baabaca3SWarner Losh 	if (cam_periph_acquire(periph) != CAM_REQ_CMP) {
805baabaca3SWarner Losh 		xpt_print(periph->path, "%s: lost periph during "
806baabaca3SWarner Losh 			  "registration!\n", __func__);
807baabaca3SWarner Losh 		cam_periph_lock(periph);
808baabaca3SWarner Losh 		return (CAM_REQ_CMP_ERR);
809baabaca3SWarner Losh 	}
810baabaca3SWarner Losh 	disk_create(softc->disk, DISK_VERSION);
811baabaca3SWarner Losh 	cam_periph_lock(periph);
812baabaca3SWarner Losh 	cam_periph_unhold(periph);
813baabaca3SWarner Losh 
814baabaca3SWarner Losh 	snprintf(announce_buf, sizeof(announce_buf),
815baabaca3SWarner Losh 		"%juMB (%ju %u byte sectors)",
816baabaca3SWarner Losh 	    (uintmax_t)((uintmax_t)disk->d_mediasize / (1024*1024)),
817baabaca3SWarner Losh 		(uintmax_t)disk->d_mediasize / disk->d_sectorsize,
818baabaca3SWarner Losh 		disk->d_sectorsize);
819baabaca3SWarner Losh 	xpt_announce_periph(periph, announce_buf);
820baabaca3SWarner Losh 	xpt_announce_quirks(periph, softc->quirks, NDA_Q_BIT_STRING);
821baabaca3SWarner Losh 
822baabaca3SWarner Losh 	/*
823baabaca3SWarner Losh 	 * Create our sysctl variables, now that we know
824baabaca3SWarner Losh 	 * we have successfully attached.
825baabaca3SWarner Losh 	 */
826baabaca3SWarner Losh 	if (cam_periph_acquire(periph) == CAM_REQ_CMP)
827baabaca3SWarner Losh 		taskqueue_enqueue(taskqueue_thread, &softc->sysctl_task);
828baabaca3SWarner Losh 
829baabaca3SWarner Losh 	/*
830baabaca3SWarner Losh 	 * Register for device going away and info about the drive
831baabaca3SWarner Losh 	 * changing (though with NVMe, it can't)
832baabaca3SWarner Losh 	 */
833baabaca3SWarner Losh 	xpt_register_async(AC_LOST_DEVICE | AC_ADVINFO_CHANGED,
834baabaca3SWarner Losh 	    ndaasync, periph, periph->path);
835baabaca3SWarner Losh 
836baabaca3SWarner Losh 	softc->state = NDA_STATE_NORMAL;
837baabaca3SWarner Losh 	return(CAM_REQ_CMP);
838baabaca3SWarner Losh }
839baabaca3SWarner Losh 
840baabaca3SWarner Losh static void
841baabaca3SWarner Losh ndastart(struct cam_periph *periph, union ccb *start_ccb)
842baabaca3SWarner Losh {
843baabaca3SWarner Losh 	struct nda_softc *softc = (struct nda_softc *)periph->softc;
844baabaca3SWarner Losh 	struct ccb_nvmeio *nvmeio = &start_ccb->nvmeio;
845baabaca3SWarner Losh 
846baabaca3SWarner Losh 	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("ndastart\n"));
847baabaca3SWarner Losh 
848baabaca3SWarner Losh 	switch (softc->state) {
849baabaca3SWarner Losh 	case NDA_STATE_NORMAL:
850baabaca3SWarner Losh 	{
851baabaca3SWarner Losh 		struct bio *bp;
852baabaca3SWarner Losh 
853baabaca3SWarner Losh 		bp = cam_iosched_next_bio(softc->cam_iosched);
854baabaca3SWarner Losh 		CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("ndastart: bio %p\n", bp));
855baabaca3SWarner Losh 		if (bp == NULL) {
856baabaca3SWarner Losh 			xpt_release_ccb(start_ccb);
857baabaca3SWarner Losh 			break;
858baabaca3SWarner Losh 		}
859baabaca3SWarner Losh 
860baabaca3SWarner Losh 		switch (bp->bio_cmd) {
861baabaca3SWarner Losh 		case BIO_WRITE:
862baabaca3SWarner Losh 			softc->flags |= NDA_FLAG_DIRTY;
863baabaca3SWarner Losh 			/* FALLTHROUGH */
864baabaca3SWarner Losh 		case BIO_READ:
865baabaca3SWarner Losh 		{
866baabaca3SWarner Losh #ifdef NDA_TEST_FAILURE
867baabaca3SWarner Losh 			int fail = 0;
868baabaca3SWarner Losh 
869baabaca3SWarner Losh 			/*
870baabaca3SWarner Losh 			 * Support the failure ioctls.  If the command is a
871baabaca3SWarner Losh 			 * read, and there are pending forced read errors, or
872baabaca3SWarner Losh 			 * if a write and pending write errors, then fail this
873baabaca3SWarner Losh 			 * operation with EIO.  This is useful for testing
874baabaca3SWarner Losh 			 * purposes.  Also, support having every Nth read fail.
875baabaca3SWarner Losh 			 *
876baabaca3SWarner Losh 			 * This is a rather blunt tool.
877baabaca3SWarner Losh 			 */
878baabaca3SWarner Losh 			if (bp->bio_cmd == BIO_READ) {
879baabaca3SWarner Losh 				if (softc->force_read_error) {
880baabaca3SWarner Losh 					softc->force_read_error--;
881baabaca3SWarner Losh 					fail = 1;
882baabaca3SWarner Losh 				}
883baabaca3SWarner Losh 				if (softc->periodic_read_error > 0) {
884baabaca3SWarner Losh 					if (++softc->periodic_read_count >=
885baabaca3SWarner Losh 					    softc->periodic_read_error) {
886baabaca3SWarner Losh 						softc->periodic_read_count = 0;
887baabaca3SWarner Losh 						fail = 1;
888baabaca3SWarner Losh 					}
889baabaca3SWarner Losh 				}
890baabaca3SWarner Losh 			} else {
891baabaca3SWarner Losh 				if (softc->force_write_error) {
892baabaca3SWarner Losh 					softc->force_write_error--;
893baabaca3SWarner Losh 					fail = 1;
894baabaca3SWarner Losh 				}
895baabaca3SWarner Losh 			}
896baabaca3SWarner Losh 			if (fail) {
897baabaca3SWarner Losh 				biofinish(bp, NULL, EIO);
898baabaca3SWarner Losh 				xpt_release_ccb(start_ccb);
899baabaca3SWarner Losh 				ndaschedule(periph);
900baabaca3SWarner Losh 				return;
901baabaca3SWarner Losh 			}
902baabaca3SWarner Losh #endif
903baabaca3SWarner Losh 			KASSERT((bp->bio_flags & BIO_UNMAPPED) == 0 ||
904baabaca3SWarner Losh 			    round_page(bp->bio_bcount + bp->bio_ma_offset) /
905baabaca3SWarner Losh 			    PAGE_SIZE == bp->bio_ma_n,
906baabaca3SWarner Losh 			    ("Short bio %p", bp));
907baabaca3SWarner Losh 			nda_nvme_rw_bio(softc, &start_ccb->nvmeio, bp, bp->bio_cmd == BIO_READ ?
908baabaca3SWarner Losh 			    NVME_OPC_READ : NVME_OPC_WRITE);
909baabaca3SWarner Losh 			break;
910baabaca3SWarner Losh 		}
911baabaca3SWarner Losh 		case BIO_DELETE:
912baabaca3SWarner Losh 		{
913baabaca3SWarner Losh 			struct nvme_dsm_range *dsm_range;
914baabaca3SWarner Losh 
915baabaca3SWarner Losh 			dsm_range =
916baabaca3SWarner Losh 			    malloc(sizeof(*dsm_range), M_NVMEDA, M_ZERO | M_WAITOK);
917baabaca3SWarner Losh 			dsm_range->length =
918baabaca3SWarner Losh 			    bp->bio_bcount / softc->disk->d_sectorsize;
919baabaca3SWarner Losh 			dsm_range->starting_lba =
920baabaca3SWarner Losh 			    bp->bio_offset / softc->disk->d_sectorsize;
921baabaca3SWarner Losh 			bp->bio_driver2 = dsm_range;
922baabaca3SWarner Losh 			nda_nvme_trim(softc, &start_ccb->nvmeio, dsm_range, 1);
923baabaca3SWarner Losh 			start_ccb->ccb_h.ccb_state = NDA_CCB_TRIM;
924baabaca3SWarner Losh 			start_ccb->ccb_h.flags |= CAM_UNLOCKED;
925851063e1SWarner Losh 			/*
926851063e1SWarner Losh 			 * Note: We can have multiple TRIMs in flight, so we don't call
927851063e1SWarner Losh 			 * cam_iosched_submit_trim(softc->cam_iosched);
928851063e1SWarner Losh 			 * since that forces the I/O scheduler to only schedule one at a time.
929851063e1SWarner Losh 			 * On NVMe drives, this is a performance disaster.
930851063e1SWarner Losh 			 */
931baabaca3SWarner Losh 			goto out;
932baabaca3SWarner Losh 		}
933baabaca3SWarner Losh 		case BIO_FLUSH:
934baabaca3SWarner Losh 			nda_nvme_flush(softc, nvmeio);
935baabaca3SWarner Losh 			break;
936baabaca3SWarner Losh 		}
937baabaca3SWarner Losh 		start_ccb->ccb_h.ccb_state = NDA_CCB_BUFFER_IO;
938baabaca3SWarner Losh 		start_ccb->ccb_h.flags |= CAM_UNLOCKED;
939baabaca3SWarner Losh out:
940baabaca3SWarner Losh 		start_ccb->ccb_h.ccb_bp = bp;
941baabaca3SWarner Losh 		softc->outstanding_cmds++;
942baabaca3SWarner Losh 		softc->refcount++;
943baabaca3SWarner Losh 		cam_periph_unlock(periph);
944baabaca3SWarner Losh 		xpt_action(start_ccb);
945baabaca3SWarner Losh 		cam_periph_lock(periph);
946baabaca3SWarner Losh 		softc->refcount--;
947baabaca3SWarner Losh 
948baabaca3SWarner Losh 		/* May have more work to do, so ensure we stay scheduled */
949baabaca3SWarner Losh 		ndaschedule(periph);
950baabaca3SWarner Losh 		break;
951baabaca3SWarner Losh 		}
952baabaca3SWarner Losh 	}
953baabaca3SWarner Losh }
954baabaca3SWarner Losh 
955baabaca3SWarner Losh static void
956baabaca3SWarner Losh ndadone(struct cam_periph *periph, union ccb *done_ccb)
957baabaca3SWarner Losh {
958baabaca3SWarner Losh 	struct nda_softc *softc;
959baabaca3SWarner Losh 	struct ccb_nvmeio *nvmeio = &done_ccb->nvmeio;
960baabaca3SWarner Losh 	struct cam_path *path;
961baabaca3SWarner Losh 	int state;
962baabaca3SWarner Losh 
963baabaca3SWarner Losh 	softc = (struct nda_softc *)periph->softc;
964baabaca3SWarner Losh 	path = done_ccb->ccb_h.path;
965baabaca3SWarner Losh 
966baabaca3SWarner Losh 	CAM_DEBUG(path, CAM_DEBUG_TRACE, ("ndadone\n"));
967baabaca3SWarner Losh 
968baabaca3SWarner Losh 	state = nvmeio->ccb_h.ccb_state & NDA_CCB_TYPE_MASK;
969baabaca3SWarner Losh 	switch (state) {
970baabaca3SWarner Losh 	case NDA_CCB_BUFFER_IO:
971baabaca3SWarner Losh 	case NDA_CCB_TRIM:
972baabaca3SWarner Losh 	{
973baabaca3SWarner Losh 		struct bio *bp;
974baabaca3SWarner Losh 		int error;
975baabaca3SWarner Losh 
976baabaca3SWarner Losh 		cam_periph_lock(periph);
977baabaca3SWarner Losh 		bp = (struct bio *)done_ccb->ccb_h.ccb_bp;
978baabaca3SWarner Losh 		if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
979baabaca3SWarner Losh 			error = ndaerror(done_ccb, 0, 0);
980baabaca3SWarner Losh 			if (error == ERESTART) {
981baabaca3SWarner Losh 				/* A retry was scheduled, so just return. */
982baabaca3SWarner Losh 				cam_periph_unlock(periph);
983baabaca3SWarner Losh 				return;
984baabaca3SWarner Losh 			}
985baabaca3SWarner Losh 			if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
986baabaca3SWarner Losh 				cam_release_devq(path,
987baabaca3SWarner Losh 						 /*relsim_flags*/0,
988baabaca3SWarner Losh 						 /*reduction*/0,
989baabaca3SWarner Losh 						 /*timeout*/0,
990baabaca3SWarner Losh 						 /*getcount_only*/0);
991baabaca3SWarner Losh 		} else {
992baabaca3SWarner Losh 			if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
993baabaca3SWarner Losh 				panic("REQ_CMP with QFRZN");
994baabaca3SWarner Losh 			error = 0;
995baabaca3SWarner Losh 		}
996baabaca3SWarner Losh 		bp->bio_error = error;
997baabaca3SWarner Losh 		if (error != 0) {
998baabaca3SWarner Losh 			bp->bio_resid = bp->bio_bcount;
999baabaca3SWarner Losh 			bp->bio_flags |= BIO_ERROR;
1000baabaca3SWarner Losh 		} else {
1001baabaca3SWarner Losh 			bp->bio_resid = 0;
1002baabaca3SWarner Losh 		}
1003baabaca3SWarner Losh 		if (state == NDA_CCB_TRIM)
1004baabaca3SWarner Losh 			free(bp->bio_driver2, M_NVMEDA);
1005baabaca3SWarner Losh 		softc->outstanding_cmds--;
1006baabaca3SWarner Losh 
10079754579bSWarner Losh 		/*
10089754579bSWarner Losh 		 * We need to call cam_iosched before we call biodone so that we
10099754579bSWarner Losh 		 * don't measure any activity that happens in the completion
10109754579bSWarner Losh 		 * routine, which in the case of sendfile can be quite
10119754579bSWarner Losh 		 * extensive.
10129754579bSWarner Losh 		 */
1013baabaca3SWarner Losh 		cam_iosched_bio_complete(softc->cam_iosched, bp, done_ccb);
1014baabaca3SWarner Losh 		xpt_release_ccb(done_ccb);
1015baabaca3SWarner Losh 		if (state == NDA_CCB_TRIM) {
1016baabaca3SWarner Losh #ifdef notyet
1017baabaca3SWarner Losh 			TAILQ_HEAD(, bio) queue;
1018baabaca3SWarner Losh 			struct bio *bp1;
1019baabaca3SWarner Losh 
1020baabaca3SWarner Losh 			TAILQ_INIT(&queue);
1021baabaca3SWarner Losh 			TAILQ_CONCAT(&queue, &softc->trim_req.bps, bio_queue);
1022baabaca3SWarner Losh #endif
1023851063e1SWarner Losh 			/*
1024851063e1SWarner Losh 			 * Since we can have multiple trims in flight, we don't
1025851063e1SWarner Losh 			 * need to call this here.
1026851063e1SWarner Losh 			 * cam_iosched_trim_done(softc->cam_iosched);
1027851063e1SWarner Losh 			 */
1028baabaca3SWarner Losh 			ndaschedule(periph);
1029baabaca3SWarner Losh 			cam_periph_unlock(periph);
1030baabaca3SWarner Losh #ifdef notyet
1031baabaca3SWarner Losh /* Not yet collapsing several BIO_DELETE requests into one TRIM */
1032baabaca3SWarner Losh 			while ((bp1 = TAILQ_FIRST(&queue)) != NULL) {
1033baabaca3SWarner Losh 				TAILQ_REMOVE(&queue, bp1, bio_queue);
1034baabaca3SWarner Losh 				bp1->bio_error = error;
1035baabaca3SWarner Losh 				if (error != 0) {
1036baabaca3SWarner Losh 					bp1->bio_flags |= BIO_ERROR;
1037baabaca3SWarner Losh 					bp1->bio_resid = bp1->bio_bcount;
1038baabaca3SWarner Losh 				} else
1039baabaca3SWarner Losh 					bp1->bio_resid = 0;
1040baabaca3SWarner Losh 				biodone(bp1);
1041baabaca3SWarner Losh 			}
1042baabaca3SWarner Losh #else
1043baabaca3SWarner Losh 			biodone(bp);
1044baabaca3SWarner Losh #endif
1045baabaca3SWarner Losh 		} else {
1046baabaca3SWarner Losh 			ndaschedule(periph);
1047baabaca3SWarner Losh 			cam_periph_unlock(periph);
1048baabaca3SWarner Losh 			biodone(bp);
1049baabaca3SWarner Losh 		}
1050baabaca3SWarner Losh 		return;
1051baabaca3SWarner Losh 	}
1052baabaca3SWarner Losh 	case NDA_CCB_DUMP:
1053baabaca3SWarner Losh 		/* No-op.  We're polling */
1054baabaca3SWarner Losh 		return;
1055baabaca3SWarner Losh 	default:
1056baabaca3SWarner Losh 		break;
1057baabaca3SWarner Losh 	}
1058baabaca3SWarner Losh 	xpt_release_ccb(done_ccb);
1059baabaca3SWarner Losh }
1060baabaca3SWarner Losh 
1061baabaca3SWarner Losh static int
1062baabaca3SWarner Losh ndaerror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags)
1063baabaca3SWarner Losh {
1064baabaca3SWarner Losh 	struct nda_softc *softc;
1065baabaca3SWarner Losh 	struct cam_periph *periph;
1066baabaca3SWarner Losh 
1067baabaca3SWarner Losh 	periph = xpt_path_periph(ccb->ccb_h.path);
1068baabaca3SWarner Losh 	softc = (struct nda_softc *)periph->softc;
1069baabaca3SWarner Losh 
1070baabaca3SWarner Losh 	switch (ccb->ccb_h.status & CAM_STATUS_MASK) {
1071baabaca3SWarner Losh 	case CAM_CMD_TIMEOUT:
1072baabaca3SWarner Losh #ifdef CAM_IO_STATS
1073baabaca3SWarner Losh 		softc->timeouts++;
1074baabaca3SWarner Losh #endif
1075baabaca3SWarner Losh 		break;
1076baabaca3SWarner Losh 	case CAM_REQ_ABORTED:
1077baabaca3SWarner Losh 	case CAM_REQ_CMP_ERR:
1078baabaca3SWarner Losh 	case CAM_REQ_TERMIO:
1079baabaca3SWarner Losh 	case CAM_UNREC_HBA_ERROR:
1080baabaca3SWarner Losh 	case CAM_DATA_RUN_ERR:
1081baabaca3SWarner Losh 	case CAM_ATA_STATUS_ERROR:
1082baabaca3SWarner Losh #ifdef CAM_IO_STATS
1083baabaca3SWarner Losh 		softc->errors++;
1084baabaca3SWarner Losh #endif
1085baabaca3SWarner Losh 		break;
1086baabaca3SWarner Losh 	default:
1087baabaca3SWarner Losh 		break;
1088baabaca3SWarner Losh 	}
1089baabaca3SWarner Losh 
1090baabaca3SWarner Losh 	return(cam_periph_error(ccb, cam_flags, sense_flags, NULL));
1091baabaca3SWarner Losh }
1092baabaca3SWarner Losh 
1093baabaca3SWarner Losh /*
1094baabaca3SWarner Losh  * Step through all NDA peripheral drivers, and if the device is still open,
1095baabaca3SWarner Losh  * sync the disk cache to physical media.
1096baabaca3SWarner Losh  */
1097baabaca3SWarner Losh static void
1098baabaca3SWarner Losh ndaflush(void)
1099baabaca3SWarner Losh {
1100baabaca3SWarner Losh 	struct cam_periph *periph;
1101baabaca3SWarner Losh 	struct nda_softc *softc;
1102baabaca3SWarner Losh 	union ccb *ccb;
1103baabaca3SWarner Losh 	int error;
1104baabaca3SWarner Losh 
1105baabaca3SWarner Losh 	CAM_PERIPH_FOREACH(periph, &ndadriver) {
1106baabaca3SWarner Losh 		softc = (struct nda_softc *)periph->softc;
1107baabaca3SWarner Losh 		if (SCHEDULER_STOPPED()) {
1108baabaca3SWarner Losh 			/* If we paniced with the lock held, do not recurse. */
1109baabaca3SWarner Losh 			if (!cam_periph_owned(periph) &&
1110baabaca3SWarner Losh 			    (softc->flags & NDA_FLAG_OPEN)) {
1111baabaca3SWarner Losh 				ndadump(softc->disk, NULL, 0, 0, 0);
1112baabaca3SWarner Losh 			}
1113baabaca3SWarner Losh 			continue;
1114baabaca3SWarner Losh 		}
1115baabaca3SWarner Losh 		cam_periph_lock(periph);
1116baabaca3SWarner Losh 		/*
1117baabaca3SWarner Losh 		 * We only sync the cache if the drive is still open, and
1118baabaca3SWarner Losh 		 * if the drive is capable of it..
1119baabaca3SWarner Losh 		 */
1120baabaca3SWarner Losh 		if ((softc->flags & NDA_FLAG_OPEN) == 0) {
1121baabaca3SWarner Losh 			cam_periph_unlock(periph);
1122baabaca3SWarner Losh 			continue;
1123baabaca3SWarner Losh 		}
1124baabaca3SWarner Losh 
1125baabaca3SWarner Losh 		ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL);
1126baabaca3SWarner Losh 		nda_nvme_flush(softc, &ccb->nvmeio);
1127baabaca3SWarner Losh 		error = cam_periph_runccb(ccb, ndaerror, /*cam_flags*/0,
1128baabaca3SWarner Losh 		    /*sense_flags*/ SF_NO_RECOVERY | SF_NO_RETRY,
1129baabaca3SWarner Losh 		    softc->disk->d_devstat);
1130baabaca3SWarner Losh 		if (error != 0)
1131baabaca3SWarner Losh 			xpt_print(periph->path, "Synchronize cache failed\n");
1132baabaca3SWarner Losh 		xpt_release_ccb(ccb);
1133baabaca3SWarner Losh 		cam_periph_unlock(periph);
1134baabaca3SWarner Losh 	}
1135baabaca3SWarner Losh }
1136baabaca3SWarner Losh 
1137baabaca3SWarner Losh static void
1138baabaca3SWarner Losh ndashutdown(void *arg, int howto)
1139baabaca3SWarner Losh {
1140baabaca3SWarner Losh 
1141baabaca3SWarner Losh 	ndaflush();
1142baabaca3SWarner Losh }
1143baabaca3SWarner Losh 
1144baabaca3SWarner Losh static void
1145baabaca3SWarner Losh ndasuspend(void *arg)
1146baabaca3SWarner Losh {
1147baabaca3SWarner Losh 
1148baabaca3SWarner Losh 	ndaflush();
1149baabaca3SWarner Losh }
1150