xref: /freebsd/sys/cam/ata/ata_da.c (revision 55e0987aea18d0b97fc0444c7678cbca1c7761d0)
152c9ce25SScott Long /*-
252c9ce25SScott Long  * Copyright (c) 2009 Alexander Motin <mav@FreeBSD.org>
352c9ce25SScott Long  * All rights reserved.
452c9ce25SScott Long  *
552c9ce25SScott Long  * Redistribution and use in source and binary forms, with or without
652c9ce25SScott Long  * modification, are permitted provided that the following conditions
752c9ce25SScott Long  * are met:
852c9ce25SScott Long  * 1. Redistributions of source code must retain the above copyright
952c9ce25SScott Long  *    notice, this list of conditions and the following disclaimer,
1052c9ce25SScott Long  *    without modification, immediately at the beginning of the file.
1152c9ce25SScott Long  * 2. Redistributions in binary form must reproduce the above copyright
1252c9ce25SScott Long  *    notice, this list of conditions and the following disclaimer in the
1352c9ce25SScott Long  *    documentation and/or other materials provided with the distribution.
1452c9ce25SScott Long  *
1552c9ce25SScott Long  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1652c9ce25SScott Long  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1752c9ce25SScott Long  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1852c9ce25SScott Long  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1952c9ce25SScott Long  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2052c9ce25SScott Long  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2152c9ce25SScott Long  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2252c9ce25SScott Long  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2352c9ce25SScott Long  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2452c9ce25SScott Long  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2552c9ce25SScott Long  */
2652c9ce25SScott Long 
2752c9ce25SScott Long #include <sys/cdefs.h>
2852c9ce25SScott Long __FBSDID("$FreeBSD$");
2952c9ce25SScott Long 
30e3a6d3a4SAlexander Motin #include "opt_ada.h"
31e3a6d3a4SAlexander Motin 
3252c9ce25SScott Long #include <sys/param.h>
3352c9ce25SScott Long 
3452c9ce25SScott Long #ifdef _KERNEL
3552c9ce25SScott Long #include <sys/systm.h>
3652c9ce25SScott Long #include <sys/kernel.h>
3752c9ce25SScott Long #include <sys/bio.h>
3852c9ce25SScott Long #include <sys/sysctl.h>
3952c9ce25SScott Long #include <sys/taskqueue.h>
4052c9ce25SScott Long #include <sys/lock.h>
4152c9ce25SScott Long #include <sys/mutex.h>
4252c9ce25SScott Long #include <sys/conf.h>
4352c9ce25SScott Long #include <sys/devicestat.h>
4452c9ce25SScott Long #include <sys/eventhandler.h>
4552c9ce25SScott Long #include <sys/malloc.h>
4652c9ce25SScott Long #include <sys/cons.h>
472f87dfb0SAlexander Motin #include <sys/proc.h>
48fd104c15SRebecca Cran #include <sys/reboot.h>
4952c9ce25SScott Long #include <geom/geom_disk.h>
5052c9ce25SScott Long #endif /* _KERNEL */
5152c9ce25SScott Long 
5252c9ce25SScott Long #ifndef _KERNEL
5352c9ce25SScott Long #include <stdio.h>
5452c9ce25SScott Long #include <string.h>
5552c9ce25SScott Long #endif /* _KERNEL */
5652c9ce25SScott Long 
5752c9ce25SScott Long #include <cam/cam.h>
5852c9ce25SScott Long #include <cam/cam_ccb.h>
5952c9ce25SScott Long #include <cam/cam_periph.h>
6052c9ce25SScott Long #include <cam/cam_xpt_periph.h>
6152c9ce25SScott Long #include <cam/cam_sim.h>
62a6e0c5daSWarner Losh #include <cam/cam_iosched.h>
6352c9ce25SScott Long 
6452c9ce25SScott Long #include <cam/ata/ata_all.h>
6552c9ce25SScott Long 
664461491bSMarius Strobl #include <machine/md_var.h>	/* geometry translation */
674461491bSMarius Strobl 
6852c9ce25SScott Long #ifdef _KERNEL
6952c9ce25SScott Long 
7052c9ce25SScott Long #define ATA_MAX_28BIT_LBA               268435455UL
7152c9ce25SScott Long 
72a6e0c5daSWarner Losh extern int iosched_debug;
73a6e0c5daSWarner Losh 
7452c9ce25SScott Long typedef enum {
751ed6aaf9SAlexander Motin 	ADA_STATE_RAHEAD,
76f513d14cSAlexander Motin 	ADA_STATE_WCACHE,
771e637ba6SAlexander Motin 	ADA_STATE_NORMAL
7852c9ce25SScott Long } ada_state;
7952c9ce25SScott Long 
8052c9ce25SScott Long typedef enum {
812e1eb332SMarius Strobl 	ADA_FLAG_CAN_48BIT	= 0x0002,
822e1eb332SMarius Strobl 	ADA_FLAG_CAN_FLUSHCACHE	= 0x0004,
832e1eb332SMarius Strobl 	ADA_FLAG_CAN_NCQ	= 0x0008,
842e1eb332SMarius Strobl 	ADA_FLAG_CAN_DMA	= 0x0010,
852e1eb332SMarius Strobl 	ADA_FLAG_NEED_OTAG	= 0x0020,
86030844d1SAlexander Motin 	ADA_FLAG_WAS_OTAG	= 0x0040,
872e1eb332SMarius Strobl 	ADA_FLAG_CAN_TRIM	= 0x0080,
882e1eb332SMarius Strobl 	ADA_FLAG_OPEN		= 0x0100,
892e1eb332SMarius Strobl 	ADA_FLAG_SCTX_INIT	= 0x0200,
902e1eb332SMarius Strobl 	ADA_FLAG_CAN_CFA        = 0x0400,
912e1eb332SMarius Strobl 	ADA_FLAG_CAN_POWERMGT   = 0x0800,
9269114bc0SAlexander Motin 	ADA_FLAG_CAN_DMA48	= 0x1000,
93a6e0c5daSWarner Losh 	ADA_FLAG_DIRTY		= 0x2000,
94a6e0c5daSWarner Losh 	ADA_FLAG_CAN_NCQ_TRIM	= 0x4000,	/* CAN_TRIM also set */
95a6e0c5daSWarner Losh 	ADA_FLAG_PIM_CAN_NCQ_TRIM = 0x8000
9652c9ce25SScott Long } ada_flags;
9752c9ce25SScott Long 
9852c9ce25SScott Long typedef enum {
99d3a460d3SAlexander Motin 	ADA_Q_NONE		= 0x00,
100d3a460d3SAlexander Motin 	ADA_Q_4K		= 0x01,
101a6e0c5daSWarner Losh 	ADA_Q_NCQ_TRIM_BROKEN	= 0x02,
10252c9ce25SScott Long } ada_quirks;
10352c9ce25SScott Long 
1046fb5c84eSSteven Hartland #define ADA_Q_BIT_STRING	\
1056fb5c84eSSteven Hartland 	"\020"			\
106a6e0c5daSWarner Losh 	"\0014K"		\
107a6e0c5daSWarner Losh 	"\002NCQ_TRIM_BROKEN"
1086fb5c84eSSteven Hartland 
10952c9ce25SScott Long typedef enum {
1101ed6aaf9SAlexander Motin 	ADA_CCB_RAHEAD		= 0x01,
1111ed6aaf9SAlexander Motin 	ADA_CCB_WCACHE		= 0x02,
11252c9ce25SScott Long 	ADA_CCB_BUFFER_IO	= 0x03,
11352c9ce25SScott Long 	ADA_CCB_DUMP		= 0x05,
1141c80ec0aSAlexander Motin 	ADA_CCB_TRIM		= 0x06,
11552c9ce25SScott Long 	ADA_CCB_TYPE_MASK	= 0x0F,
11652c9ce25SScott Long } ada_ccb_state;
11752c9ce25SScott Long 
11852c9ce25SScott Long /* Offsets into our private area for storing information */
11952c9ce25SScott Long #define ccb_state	ppriv_field0
12052c9ce25SScott Long #define ccb_bp		ppriv_ptr1
12152c9ce25SScott Long 
122a6e0c5daSWarner Losh typedef enum {
123a6e0c5daSWarner Losh 	ADA_DELETE_NONE,
124a6e0c5daSWarner Losh 	ADA_DELETE_DISABLE,
125a6e0c5daSWarner Losh 	ADA_DELETE_CFA_ERASE,
126a6e0c5daSWarner Losh 	ADA_DELETE_DSM_TRIM,
127a6e0c5daSWarner Losh 	ADA_DELETE_NCQ_DSM_TRIM,
128a6e0c5daSWarner Losh 	ADA_DELETE_MIN = ADA_DELETE_CFA_ERASE,
129a6e0c5daSWarner Losh 	ADA_DELETE_MAX = ADA_DELETE_NCQ_DSM_TRIM,
130a6e0c5daSWarner Losh } ada_delete_methods;
131a6e0c5daSWarner Losh 
132a6e0c5daSWarner Losh static const char *ada_delete_method_names[] =
133a6e0c5daSWarner Losh     { "NONE", "DISABLE", "CFA_ERASE", "DSM_TRIM", "NCQ_DSM_TRIM" };
134a6e0c5daSWarner Losh #if 0
135a6e0c5daSWarner Losh static const char *ada_delete_method_desc[] =
136a6e0c5daSWarner Losh     { "NONE", "DISABLED", "CFA Erase", "DSM Trim", "DSM Trim via NCQ" };
137a6e0c5daSWarner Losh #endif
138a6e0c5daSWarner Losh 
13952c9ce25SScott Long struct disk_params {
14052c9ce25SScott Long 	u_int8_t  heads;
14152c9ce25SScott Long 	u_int8_t  secs_per_track;
142c1bd46c2SAlexander Motin 	u_int32_t cylinders;
143c1bd46c2SAlexander Motin 	u_int32_t secsize;	/* Number of bytes/logical sector */
144c1bd46c2SAlexander Motin 	u_int64_t sectors;	/* Total number sectors */
14552c9ce25SScott Long };
14652c9ce25SScott Long 
1471524677aSAlexander Motin #define TRIM_MAX_BLOCKS	8
148c213c551SSteven Hartland #define TRIM_MAX_RANGES	(TRIM_MAX_BLOCKS * ATA_DSM_BLK_RANGES)
1491c80ec0aSAlexander Motin struct trim_request {
150c213c551SSteven Hartland 	uint8_t		data[TRIM_MAX_RANGES * ATA_DSM_RANGE_SIZE];
1512030b294SAlexander Motin 	TAILQ_HEAD(, bio) bps;
1521c80ec0aSAlexander Motin };
1531c80ec0aSAlexander Motin 
15452c9ce25SScott Long struct ada_softc {
155a6e0c5daSWarner Losh 	struct   cam_iosched_softc *cam_iosched;
156030844d1SAlexander Motin 	int	 outstanding_cmds;	/* Number of active commands */
157030844d1SAlexander Motin 	int	 refcount;		/* Active xpt_action() calls */
15852c9ce25SScott Long 	ada_state state;
15952c9ce25SScott Long 	ada_flags flags;
16052c9ce25SScott Long 	ada_quirks quirks;
161a6e0c5daSWarner Losh 	ada_delete_methods delete_method;
1621c80ec0aSAlexander Motin 	int	 trim_max_ranges;
1631ed6aaf9SAlexander Motin 	int	 read_ahead;
164e3a6d3a4SAlexander Motin 	int	 write_cache;
165a6e0c5daSWarner Losh 	int	 unmappedio;
166a6e0c5daSWarner Losh 	int	 rotating;
167e3a6d3a4SAlexander Motin #ifdef ADA_TEST_FAILURE
168e3a6d3a4SAlexander Motin 	int      force_read_error;
169e3a6d3a4SAlexander Motin 	int      force_write_error;
170e3a6d3a4SAlexander Motin 	int      periodic_read_error;
171e3a6d3a4SAlexander Motin 	int      periodic_read_count;
172e3a6d3a4SAlexander Motin #endif
17352c9ce25SScott Long 	struct	 disk_params params;
17452c9ce25SScott Long 	struct	 disk *disk;
17552c9ce25SScott Long 	struct task		sysctl_task;
17652c9ce25SScott Long 	struct sysctl_ctx_list	sysctl_ctx;
17752c9ce25SScott Long 	struct sysctl_oid	*sysctl_tree;
17852c9ce25SScott Long 	struct callout		sendordered_c;
1791c80ec0aSAlexander Motin 	struct trim_request	trim_req;
180a6e0c5daSWarner Losh #ifdef CAM_IO_STATS
181a6e0c5daSWarner Losh 	struct sysctl_ctx_list	sysctl_stats_ctx;
182a6e0c5daSWarner Losh 	struct sysctl_oid	*sysctl_stats_tree;
183a6e0c5daSWarner Losh 	u_int	timeouts;
184a6e0c5daSWarner Losh 	u_int	errors;
185a6e0c5daSWarner Losh 	u_int	invalidations;
186a6e0c5daSWarner Losh #endif
18752c9ce25SScott Long };
18852c9ce25SScott Long 
18952c9ce25SScott Long struct ada_quirk_entry {
19052c9ce25SScott Long 	struct scsi_inquiry_pattern inq_pat;
19152c9ce25SScott Long 	ada_quirks quirks;
19252c9ce25SScott Long };
19352c9ce25SScott Long 
19430a4094fSAlexander Motin static struct ada_quirk_entry ada_quirk_table[] =
19530a4094fSAlexander Motin {
19630a4094fSAlexander Motin 	{
197d3a460d3SAlexander Motin 		/* Hitachi Advanced Format (4k) drives */
198d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "Hitachi H??????????E3*", "*" },
199d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
200d3a460d3SAlexander Motin 	},
201d3a460d3SAlexander Motin 	{
202d3a460d3SAlexander Motin 		/* Samsung Advanced Format (4k) drives */
203643d1826SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "SAMSUNG HD155UI*", "*" },
204643d1826SAlexander Motin 		/*quirks*/ADA_Q_4K
205643d1826SAlexander Motin 	},
206643d1826SAlexander Motin 	{
207643d1826SAlexander Motin 		/* Samsung Advanced Format (4k) drives */
208d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "SAMSUNG HD204UI*", "*" },
209d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
210d3a460d3SAlexander Motin 	},
211d3a460d3SAlexander Motin 	{
212d3a460d3SAlexander Motin 		/* Seagate Barracuda Green Advanced Format (4k) drives */
213d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "ST????DL*", "*" },
214d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
215d3a460d3SAlexander Motin 	},
216d3a460d3SAlexander Motin 	{
217643d1826SAlexander Motin 		/* Seagate Barracuda Advanced Format (4k) drives */
218643d1826SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "ST???DM*", "*" },
219643d1826SAlexander Motin 		/*quirks*/ADA_Q_4K
220643d1826SAlexander Motin 	},
221643d1826SAlexander Motin 	{
222643d1826SAlexander Motin 		/* Seagate Barracuda Advanced Format (4k) drives */
223643d1826SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "ST????DM*", "*" },
224643d1826SAlexander Motin 		/*quirks*/ADA_Q_4K
225643d1826SAlexander Motin 	},
226643d1826SAlexander Motin 	{
227d3a460d3SAlexander Motin 		/* Seagate Momentus Advanced Format (4k) drives */
228d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "ST9500423AS*", "*" },
229d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
230d3a460d3SAlexander Motin 	},
231d3a460d3SAlexander Motin 	{
232d3a460d3SAlexander Motin 		/* Seagate Momentus Advanced Format (4k) drives */
233d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "ST9500424AS*", "*" },
234d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
235d3a460d3SAlexander Motin 	},
236d3a460d3SAlexander Motin 	{
237d3a460d3SAlexander Motin 		/* Seagate Momentus Advanced Format (4k) drives */
238643d1826SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "ST9640423AS*", "*" },
239643d1826SAlexander Motin 		/*quirks*/ADA_Q_4K
240643d1826SAlexander Motin 	},
241643d1826SAlexander Motin 	{
242643d1826SAlexander Motin 		/* Seagate Momentus Advanced Format (4k) drives */
243643d1826SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "ST9640424AS*", "*" },
244643d1826SAlexander Motin 		/*quirks*/ADA_Q_4K
245643d1826SAlexander Motin 	},
246643d1826SAlexander Motin 	{
247643d1826SAlexander Motin 		/* Seagate Momentus Advanced Format (4k) drives */
248d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "ST9750420AS*", "*" },
249d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
250d3a460d3SAlexander Motin 	},
251d3a460d3SAlexander Motin 	{
252d3a460d3SAlexander Motin 		/* Seagate Momentus Advanced Format (4k) drives */
253d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "ST9750422AS*", "*" },
254d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
255d3a460d3SAlexander Motin 	},
256d3a460d3SAlexander Motin 	{
257643d1826SAlexander Motin 		/* Seagate Momentus Advanced Format (4k) drives */
258643d1826SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "ST9750423AS*", "*" },
259643d1826SAlexander Motin 		/*quirks*/ADA_Q_4K
260643d1826SAlexander Motin 	},
261643d1826SAlexander Motin 	{
262d3a460d3SAlexander Motin 		/* Seagate Momentus Thin Advanced Format (4k) drives */
263d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "ST???LT*", "*" },
264d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
265d3a460d3SAlexander Motin 	},
266d3a460d3SAlexander Motin 	{
2679073a96aSEitan Adler 		/* WDC Caviar Red Advanced Format (4k) drives */
2689073a96aSEitan Adler 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "WDC WD????CX*", "*" },
2699073a96aSEitan Adler 		/*quirks*/ADA_Q_4K
2709073a96aSEitan Adler 	},
2719073a96aSEitan Adler 	{
272d3a460d3SAlexander Motin 		/* WDC Caviar Green Advanced Format (4k) drives */
273d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "WDC WD????RS*", "*" },
274d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
275d3a460d3SAlexander Motin 	},
276d3a460d3SAlexander Motin 	{
2779073a96aSEitan Adler 		/* WDC Caviar Green/Red Advanced Format (4k) drives */
278d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "WDC WD????RX*", "*" },
279d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
280d3a460d3SAlexander Motin 	},
281d3a460d3SAlexander Motin 	{
2829073a96aSEitan Adler 		/* WDC Caviar Red Advanced Format (4k) drives */
2839073a96aSEitan Adler 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "WDC WD??????CX*", "*" },
2849073a96aSEitan Adler 		/*quirks*/ADA_Q_4K
2859073a96aSEitan Adler 	},
2869073a96aSEitan Adler 	{
2879073a96aSEitan Adler 		/* WDC Caviar Black Advanced Format (4k) drives */
2889073a96aSEitan Adler 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "WDC WD??????EX*", "*" },
2899073a96aSEitan Adler 		/*quirks*/ADA_Q_4K
2909073a96aSEitan Adler 	},
2919073a96aSEitan Adler 	{
292d3a460d3SAlexander Motin 		/* WDC Caviar Green Advanced Format (4k) drives */
293d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "WDC WD??????RS*", "*" },
294d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
295d3a460d3SAlexander Motin 	},
296d3a460d3SAlexander Motin 	{
297d3a460d3SAlexander Motin 		/* WDC Caviar Green Advanced Format (4k) drives */
298d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "WDC WD??????RX*", "*" },
299d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
300d3a460d3SAlexander Motin 	},
301d3a460d3SAlexander Motin 	{
302d3a460d3SAlexander Motin 		/* WDC Scorpio Black Advanced Format (4k) drives */
303d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "WDC WD???PKT*", "*" },
304d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
305d3a460d3SAlexander Motin 	},
306d3a460d3SAlexander Motin 	{
307d3a460d3SAlexander Motin 		/* WDC Scorpio Black Advanced Format (4k) drives */
308d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "WDC WD?????PKT*", "*" },
309d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
310d3a460d3SAlexander Motin 	},
311d3a460d3SAlexander Motin 	{
312d3a460d3SAlexander Motin 		/* WDC Scorpio Blue Advanced Format (4k) drives */
313d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "WDC WD???PVT*", "*" },
314d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
315d3a460d3SAlexander Motin 	},
316d3a460d3SAlexander Motin 	{
317d3a460d3SAlexander Motin 		/* WDC Scorpio Blue Advanced Format (4k) drives */
318d3a460d3SAlexander Motin 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "WDC WD?????PVT*", "*" },
319d3a460d3SAlexander Motin 		/*quirks*/ADA_Q_4K
320d3a460d3SAlexander Motin 	},
32132fe0ef7SSteven Hartland 	/* SSDs */
322d3a460d3SAlexander Motin 	{
3239d3334e1SEitan Adler 		/*
3249d3334e1SEitan Adler 		 * Corsair Force 2 SSDs
3259d3334e1SEitan Adler 		 * 4k optimised & trim only works in 4k requests + 4k aligned
3269d3334e1SEitan Adler 		 */
3279d3334e1SEitan Adler 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "Corsair CSSD-F*", "*" },
3289d3334e1SEitan Adler 		/*quirks*/ADA_Q_4K
3299d3334e1SEitan Adler 	},
3309d3334e1SEitan Adler 	{
3319d3334e1SEitan Adler 		/*
3329d3334e1SEitan Adler 		 * Corsair Force 3 SSDs
3339d3334e1SEitan Adler 		 * 4k optimised & trim only works in 4k requests + 4k aligned
3349d3334e1SEitan Adler 		 */
3359d3334e1SEitan Adler 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "Corsair Force 3*", "*" },
3369d3334e1SEitan Adler 		/*quirks*/ADA_Q_4K
3379d3334e1SEitan Adler 	},
3389d3334e1SEitan Adler 	{
3399d3334e1SEitan Adler 		/*
340d85805b2SSteven Hartland 		 * Corsair Neutron GTX SSDs
341d85805b2SSteven Hartland 		 * 4k optimised & trim only works in 4k requests + 4k aligned
342d85805b2SSteven Hartland 		 */
343d85805b2SSteven Hartland 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "Corsair Neutron GTX*", "*" },
344d85805b2SSteven Hartland 		/*quirks*/ADA_Q_4K
345d85805b2SSteven Hartland 	},
346d85805b2SSteven Hartland 	{
347d85805b2SSteven Hartland 		/*
348dc98c62fSSteven Hartland 		 * Corsair Force GT & GS SSDs
3499d3334e1SEitan Adler 		 * 4k optimised & trim only works in 4k requests + 4k aligned
3509d3334e1SEitan Adler 		 */
351dc98c62fSSteven Hartland 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "Corsair Force G*", "*" },
3529d3334e1SEitan Adler 		/*quirks*/ADA_Q_4K
3539d3334e1SEitan Adler 	},
3549d3334e1SEitan Adler 	{
3559d3334e1SEitan Adler 		/*
35632fe0ef7SSteven Hartland 		 * Crucial M4 SSDs
3579d3334e1SEitan Adler 		 * 4k optimised & trim only works in 4k requests + 4k aligned
3589d3334e1SEitan Adler 		 */
35932fe0ef7SSteven Hartland 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "M4-CT???M4SSD2*", "*" },
3609d3334e1SEitan Adler 		/*quirks*/ADA_Q_4K
3619d3334e1SEitan Adler 	},
3629d3334e1SEitan Adler 	{
3639d3334e1SEitan Adler 		/*
364555bb680SWarner Losh 		 * Crucial M500 SSDs MU07 firmware
365555bb680SWarner Losh 		 * NCQ Trim works
366a6e0c5daSWarner Losh 		 */
367555bb680SWarner Losh 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "Crucial CT*M500*", "MU07" },
368a6e0c5daSWarner Losh 		/*quirks*/0
369a6e0c5daSWarner Losh 	},
370a6e0c5daSWarner Losh 	{
371a6e0c5daSWarner Losh 		/*
372a6e0c5daSWarner Losh 		 * Crucial M500 SSDs all other firmware
373a6e0c5daSWarner Losh 		 * NCQ Trim doesn't work
374a6e0c5daSWarner Losh 		 */
375a6e0c5daSWarner Losh 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "Crucial CT*M500*", "*" },
376a6e0c5daSWarner Losh 		/*quirks*/ADA_Q_NCQ_TRIM_BROKEN
377a6e0c5daSWarner Losh 	},
378a6e0c5daSWarner Losh 	{
379a6e0c5daSWarner Losh 		/*
380a6e0c5daSWarner Losh 		 * Crucial M550 SSDs
381a6e0c5daSWarner Losh 		 * NCQ Trim doesn't work, but only on MU01 firmware
382a6e0c5daSWarner Losh 		 */
383a6e0c5daSWarner Losh 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "Crucial CT*M550*", "MU01" },
384a6e0c5daSWarner Losh 		/*quirks*/ADA_Q_NCQ_TRIM_BROKEN
385a6e0c5daSWarner Losh 	},
386a6e0c5daSWarner Losh 	{
387a6e0c5daSWarner Losh 		/*
388a6e0c5daSWarner Losh 		 * Crucial MX100 SSDs
389a6e0c5daSWarner Losh 		 * NCQ Trim doesn't work, but only on MU01 firmware
390a6e0c5daSWarner Losh 		 */
391a6e0c5daSWarner Losh 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "Crucial CT*MX100*", "MU01" },
392a6e0c5daSWarner Losh 		/*quirks*/ADA_Q_NCQ_TRIM_BROKEN
393a6e0c5daSWarner Losh 	},
394a6e0c5daSWarner Losh 	{
395a6e0c5daSWarner Losh 		/*
3969d3334e1SEitan Adler 		 * Crucial RealSSD C300 SSDs
3979d3334e1SEitan Adler 		 * 4k optimised
3989d3334e1SEitan Adler 		 */
3999d3334e1SEitan Adler 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "C300-CTFDDAC???MAG*",
4009d3334e1SEitan Adler 		"*" }, /*quirks*/ADA_Q_4K
4019d3334e1SEitan Adler 	},
4029d3334e1SEitan Adler 	{
4039d3334e1SEitan Adler 		/*
404555bb680SWarner Losh 		 * FCCT M500 SSDs
405555bb680SWarner Losh 		 * NCQ Trim doesn't work
406555bb680SWarner Losh 		 */
407555bb680SWarner Losh 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "FCCT*M500*", "*" },
408555bb680SWarner Losh 		/*quirks*/ADA_Q_NCQ_TRIM_BROKEN
409555bb680SWarner Losh 	},
410555bb680SWarner Losh 	{
411555bb680SWarner Losh 		/*
412883db1c1SEitan Adler 		 * Intel 320 Series SSDs
413883db1c1SEitan Adler 		 * 4k optimised & trim only works in 4k requests + 4k aligned
414883db1c1SEitan Adler 		 */
415883db1c1SEitan Adler 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "INTEL SSDSA2CW*", "*" },
416883db1c1SEitan Adler 		/*quirks*/ADA_Q_4K
417883db1c1SEitan Adler 	},
418883db1c1SEitan Adler 	{
419883db1c1SEitan Adler 		/*
4209d3334e1SEitan Adler 		 * Intel 330 Series SSDs
4219d3334e1SEitan Adler 		 * 4k optimised & trim only works in 4k requests + 4k aligned
4229d3334e1SEitan Adler 		 */
42332fe0ef7SSteven Hartland 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "INTEL SSDSC2CT*", "*" },
4249d3334e1SEitan Adler 		/*quirks*/ADA_Q_4K
4259d3334e1SEitan Adler 	},
4269d3334e1SEitan Adler 	{
4279d3334e1SEitan Adler 		/*
428883db1c1SEitan Adler 		 * Intel 510 Series SSDs
429883db1c1SEitan Adler 		 * 4k optimised & trim only works in 4k requests + 4k aligned
430883db1c1SEitan Adler 		 */
431883db1c1SEitan Adler 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "INTEL SSDSC2MH*", "*" },
432883db1c1SEitan Adler 		/*quirks*/ADA_Q_4K
433883db1c1SEitan Adler 	},
434883db1c1SEitan Adler 	{
435883db1c1SEitan Adler 		/*
43632fe0ef7SSteven Hartland 		 * Intel 520 Series SSDs
4379d3334e1SEitan Adler 		 * 4k optimised & trim only works in 4k requests + 4k aligned
4389d3334e1SEitan Adler 		 */
43932fe0ef7SSteven Hartland 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "INTEL SSDSC2BW*", "*" },
44032fe0ef7SSteven Hartland 		/*quirks*/ADA_Q_4K
44132fe0ef7SSteven Hartland 	},
44232fe0ef7SSteven Hartland 	{
44332fe0ef7SSteven Hartland 		/*
444dce643c8SSteven Hartland 		 * Intel X25-M Series SSDs
445dce643c8SSteven Hartland 		 * 4k optimised & trim only works in 4k requests + 4k aligned
446dce643c8SSteven Hartland 		 */
447dce643c8SSteven Hartland 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "INTEL SSDSA2M*", "*" },
448dce643c8SSteven Hartland 		/*quirks*/ADA_Q_4K
449dce643c8SSteven Hartland 	},
450dce643c8SSteven Hartland 	{
451dce643c8SSteven Hartland 		/*
45232fe0ef7SSteven Hartland 		 * Kingston E100 Series SSDs
45332fe0ef7SSteven Hartland 		 * 4k optimised & trim only works in 4k requests + 4k aligned
45432fe0ef7SSteven Hartland 		 */
45532fe0ef7SSteven Hartland 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "KINGSTON SE100S3*", "*" },
4569d3334e1SEitan Adler 		/*quirks*/ADA_Q_4K
4579d3334e1SEitan Adler 	},
4589d3334e1SEitan Adler 	{
4599d3334e1SEitan Adler 		/*
4609d3334e1SEitan Adler 		 * Kingston HyperX 3k SSDs
4619d3334e1SEitan Adler 		 * 4k optimised & trim only works in 4k requests + 4k aligned
4629d3334e1SEitan Adler 		 */
4639d3334e1SEitan Adler 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "KINGSTON SH103S3*", "*" },
4649d3334e1SEitan Adler 		/*quirks*/ADA_Q_4K
4659d3334e1SEitan Adler 	},
4669d3334e1SEitan Adler 	{
46732fe0ef7SSteven Hartland 		/*
468dce643c8SSteven Hartland 		 * Marvell SSDs (entry taken from OpenSolaris)
469dce643c8SSteven Hartland 		 * 4k optimised & trim only works in 4k requests + 4k aligned
470dce643c8SSteven Hartland 		 */
471dce643c8SSteven Hartland 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "MARVELL SD88SA02*", "*" },
472dce643c8SSteven Hartland 		/*quirks*/ADA_Q_4K
473dce643c8SSteven Hartland 	},
474dce643c8SSteven Hartland 	{
475dce643c8SSteven Hartland 		/*
476555bb680SWarner Losh 		 * Micron M500 SSDs firmware MU07
477a6e0c5daSWarner Losh 		 * NCQ Trim works?
478a6e0c5daSWarner Losh 		 */
479555bb680SWarner Losh 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "Micron M500*", "MU07" },
480a6e0c5daSWarner Losh 		/*quirks*/0
481a6e0c5daSWarner Losh 	},
482a6e0c5daSWarner Losh 	{
483a6e0c5daSWarner Losh 		/*
484a6e0c5daSWarner Losh 		 * Micron M500 SSDs all other firmware
485a6e0c5daSWarner Losh 		 * NCQ Trim doesn't work
486a6e0c5daSWarner Losh 		 */
487a6e0c5daSWarner Losh 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "Micron M500*", "*" },
488a6e0c5daSWarner Losh 		/*quirks*/ADA_Q_NCQ_TRIM_BROKEN
489a6e0c5daSWarner Losh 	},
490a6e0c5daSWarner Losh 	{
491a6e0c5daSWarner Losh 		/*
492a6e0c5daSWarner Losh 		 * Micron M5[15]0 SSDs
493a6e0c5daSWarner Losh 		 * NCQ Trim doesn't work, but only MU01 firmware
494a6e0c5daSWarner Losh 		 */
495a6e0c5daSWarner Losh 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "Micron M5[15]0*", "MU01" },
496a6e0c5daSWarner Losh 		/*quirks*/ADA_Q_NCQ_TRIM_BROKEN
497a6e0c5daSWarner Losh 	},
498a6e0c5daSWarner Losh 	{
499a6e0c5daSWarner Losh 		/*
500dce643c8SSteven Hartland 		 * OCZ Agility 2 SSDs
501dce643c8SSteven Hartland 		 * 4k optimised & trim only works in 4k requests + 4k aligned
502dce643c8SSteven Hartland 		 */
503dce643c8SSteven Hartland 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "OCZ-AGILITY2*", "*" },
504dce643c8SSteven Hartland 		/*quirks*/ADA_Q_4K
505dce643c8SSteven Hartland 	},
506dce643c8SSteven Hartland 	{
507dce643c8SSteven Hartland 		/*
50832fe0ef7SSteven Hartland 		 * OCZ Agility 3 SSDs
50932fe0ef7SSteven Hartland 		 * 4k optimised & trim only works in 4k requests + 4k aligned
51032fe0ef7SSteven Hartland 		 */
51132fe0ef7SSteven Hartland 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "OCZ-AGILITY3*", "*" },
51232fe0ef7SSteven Hartland 		/*quirks*/ADA_Q_4K
51332fe0ef7SSteven Hartland 	},
51432fe0ef7SSteven Hartland 	{
51532fe0ef7SSteven Hartland 		/*
51632fe0ef7SSteven Hartland 		 * OCZ Deneva R Series SSDs
51732fe0ef7SSteven Hartland 		 * 4k optimised & trim only works in 4k requests + 4k aligned
51832fe0ef7SSteven Hartland 		 */
51932fe0ef7SSteven Hartland 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "DENRSTE251M45*", "*" },
52032fe0ef7SSteven Hartland 		/*quirks*/ADA_Q_4K
52132fe0ef7SSteven Hartland 	},
52232fe0ef7SSteven Hartland 	{
52332fe0ef7SSteven Hartland 		/*
52432fe0ef7SSteven Hartland 		 * OCZ Vertex 2 SSDs (inc pro series)
52532fe0ef7SSteven Hartland 		 * 4k optimised & trim only works in 4k requests + 4k aligned
52632fe0ef7SSteven Hartland 		 */
52732fe0ef7SSteven Hartland 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "OCZ?VERTEX2*", "*" },
52832fe0ef7SSteven Hartland 		/*quirks*/ADA_Q_4K
52932fe0ef7SSteven Hartland 	},
53032fe0ef7SSteven Hartland 	{
53132fe0ef7SSteven Hartland 		/*
53232fe0ef7SSteven Hartland 		 * OCZ Vertex 3 SSDs
53332fe0ef7SSteven Hartland 		 * 4k optimised & trim only works in 4k requests + 4k aligned
53432fe0ef7SSteven Hartland 		 */
53532fe0ef7SSteven Hartland 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "OCZ-VERTEX3*", "*" },
53632fe0ef7SSteven Hartland 		/*quirks*/ADA_Q_4K
53732fe0ef7SSteven Hartland 	},
53832fe0ef7SSteven Hartland 	{
53932fe0ef7SSteven Hartland 		/*
5407f1c7787SSteven Hartland 		 * OCZ Vertex 4 SSDs
5417f1c7787SSteven Hartland 		 * 4k optimised & trim only works in 4k requests + 4k aligned
5427f1c7787SSteven Hartland 		 */
5437f1c7787SSteven Hartland 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "OCZ-VERTEX4*", "*" },
5447f1c7787SSteven Hartland 		/*quirks*/ADA_Q_4K
5457f1c7787SSteven Hartland 	},
5467f1c7787SSteven Hartland 	{
5477f1c7787SSteven Hartland 		/*
54832fe0ef7SSteven Hartland 		 * Samsung 830 Series SSDs
549a6e0c5daSWarner Losh 		 * 4k optimised, NCQ TRIM Broken (normal TRIM is fine)
55032fe0ef7SSteven Hartland 		 */
55132fe0ef7SSteven Hartland 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "SAMSUNG SSD 830 Series*", "*" },
552a6e0c5daSWarner Losh 		/*quirks*/ADA_Q_4K | ADA_Q_NCQ_TRIM_BROKEN
55332fe0ef7SSteven Hartland 	},
55432fe0ef7SSteven Hartland 	{
55532fe0ef7SSteven Hartland 		/*
556dc98c62fSSteven Hartland 		 * Samsung 840 SSDs
557a6e0c5daSWarner Losh 		 * 4k optimised, NCQ TRIM Broken (normal TRIM is fine)
558dc98c62fSSteven Hartland 		 */
559dc98c62fSSteven Hartland 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "Samsung SSD 840*", "*" },
560a6e0c5daSWarner Losh 		/*quirks*/ADA_Q_4K | ADA_Q_NCQ_TRIM_BROKEN
561dc98c62fSSteven Hartland 	},
562dc98c62fSSteven Hartland 	{
563dc98c62fSSteven Hartland 		/*
564e3a21bd1SGeorge V. Neville-Neil 		 * Samsung 850 SSDs
565a6e0c5daSWarner Losh 		 * 4k optimised, NCQ TRIM broken (normal TRIM fine)
566e3a21bd1SGeorge V. Neville-Neil 		 */
567e3a21bd1SGeorge V. Neville-Neil 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "Samsung SSD 850*", "*" },
568a6e0c5daSWarner Losh 		/*quirks*/ADA_Q_4K | ADA_Q_NCQ_TRIM_BROKEN
569e3a21bd1SGeorge V. Neville-Neil 	},
5705f91863aSSean Bruno 	{
5715f91863aSSean Bruno 		/*
572b93ecd35SWarner Losh 		 * Samsung SM863 Series SSDs (MZ7KM*)
573b93ecd35SWarner Losh 		 * 4k optimised, NCQ believed to be working
574b93ecd35SWarner Losh 		 */
575b93ecd35SWarner Losh 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "SAMSUNG MZ7KM*", "*" },
576b93ecd35SWarner Losh 		/*quirks*/ADA_Q_4K
577b93ecd35SWarner Losh 	},
578b93ecd35SWarner Losh 	{
579b93ecd35SWarner Losh 		/*
580eae90da9SJean-Sébastien Pédron 		 * Samsung 843T Series SSDs (MZ7WD*)
581eae90da9SJean-Sébastien Pédron 		 * Samsung PM851 Series SSDs (MZ7TE*)
582eae90da9SJean-Sébastien Pédron 		 * Samsung PM853T Series SSDs (MZ7GE*)
583b93ecd35SWarner Losh 		 * 4k optimised, NCQ believed to be broken since these are
584b93ecd35SWarner Losh 		 * appear to be built with the same controllers as the 840/850.
585323e0f6dSSean Bruno 		 */
586eae90da9SJean-Sébastien Pédron 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "SAMSUNG MZ7*", "*" },
587b93ecd35SWarner Losh 		/*quirks*/ADA_Q_4K | ADA_Q_NCQ_TRIM_BROKEN
588844b7984SSean Bruno 	},
589844b7984SSean Bruno 	{
590844b7984SSean Bruno 		/*
591bf95d6a6SWarner Losh 		 * Samsung PM851 Series SSDs Dell OEM
592bf95d6a6SWarner Losh 		 * device model          "SAMSUNG SSD PM851 mSATA 256GB"
593bf95d6a6SWarner Losh 		 * 4k optimised, NCQ broken
594bf95d6a6SWarner Losh 		 */
595bf95d6a6SWarner Losh 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "SAMSUNG SSD PM851*", "*" },
596bf95d6a6SWarner Losh 		/*quirks*/ADA_Q_4K | ADA_Q_NCQ_TRIM_BROKEN
597bf95d6a6SWarner Losh 	},
598bf95d6a6SWarner Losh 	{
599bf95d6a6SWarner Losh 		/*
60032fe0ef7SSteven Hartland 		 * SuperTalent TeraDrive CT SSDs
60132fe0ef7SSteven Hartland 		 * 4k optimised & trim only works in 4k requests + 4k aligned
60232fe0ef7SSteven Hartland 		 */
60332fe0ef7SSteven Hartland 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "FTM??CT25H*", "*" },
60432fe0ef7SSteven Hartland 		/*quirks*/ADA_Q_4K
60532fe0ef7SSteven Hartland 	},
60632fe0ef7SSteven Hartland 	{
60732fe0ef7SSteven Hartland 		/*
60832fe0ef7SSteven Hartland 		 * XceedIOPS SATA SSDs
60932fe0ef7SSteven Hartland 		 * 4k optimised
61032fe0ef7SSteven Hartland 		 */
61132fe0ef7SSteven Hartland 		{ T_DIRECT, SIP_MEDIA_FIXED, "*", "SG9XCS2D*", "*" },
61232fe0ef7SSteven Hartland 		/*quirks*/ADA_Q_4K
61332fe0ef7SSteven Hartland 	},
61432fe0ef7SSteven Hartland 	{
61530a4094fSAlexander Motin 		/* Default */
61630a4094fSAlexander Motin 		{
61730a4094fSAlexander Motin 		  T_ANY, SIP_MEDIA_REMOVABLE|SIP_MEDIA_FIXED,
61830a4094fSAlexander Motin 		  /*vendor*/"*", /*product*/"*", /*revision*/"*"
61930a4094fSAlexander Motin 		},
62030a4094fSAlexander Motin 		/*quirks*/0
62130a4094fSAlexander Motin 	},
62230a4094fSAlexander Motin };
62352c9ce25SScott Long 
62452c9ce25SScott Long static	disk_strategy_t	adastrategy;
62552c9ce25SScott Long static	dumper_t	adadump;
62652c9ce25SScott Long static	periph_init_t	adainit;
62752c9ce25SScott Long static	void		adaasync(void *callback_arg, u_int32_t code,
62852c9ce25SScott Long 				struct cam_path *path, void *arg);
62952c9ce25SScott Long static	void		adasysctlinit(void *context, int pending);
63052c9ce25SScott Long static	periph_ctor_t	adaregister;
63152c9ce25SScott Long static	periph_dtor_t	adacleanup;
63252c9ce25SScott Long static	periph_start_t	adastart;
63352c9ce25SScott Long static	periph_oninv_t	adaoninvalidate;
63452c9ce25SScott Long static	void		adadone(struct cam_periph *periph,
63552c9ce25SScott Long 			       union ccb *done_ccb);
63652c9ce25SScott Long static  int		adaerror(union ccb *ccb, u_int32_t cam_flags,
63752c9ce25SScott Long 				u_int32_t sense_flags);
638c1bd46c2SAlexander Motin static void		adagetparams(struct cam_periph *periph,
63952c9ce25SScott Long 				struct ccb_getdev *cgd);
64052c9ce25SScott Long static timeout_t	adasendorderedtag;
64152c9ce25SScott Long static void		adashutdown(void *arg, int howto);
642c3d0d168SAlexander Motin static void		adasuspend(void *arg);
643c3d0d168SAlexander Motin static void		adaresume(void *arg);
64452c9ce25SScott Long 
6450d307e09SAlexander Motin #ifndef	ADA_DEFAULT_LEGACY_ALIASES
6460d307e09SAlexander Motin #define	ADA_DEFAULT_LEGACY_ALIASES	1
6470d307e09SAlexander Motin #endif
6480d307e09SAlexander Motin 
64952c9ce25SScott Long #ifndef ADA_DEFAULT_TIMEOUT
65052c9ce25SScott Long #define ADA_DEFAULT_TIMEOUT 30	/* Timeout in seconds */
65152c9ce25SScott Long #endif
65252c9ce25SScott Long 
65352c9ce25SScott Long #ifndef	ADA_DEFAULT_RETRY
65452c9ce25SScott Long #define	ADA_DEFAULT_RETRY	4
65552c9ce25SScott Long #endif
65652c9ce25SScott Long 
65752c9ce25SScott Long #ifndef	ADA_DEFAULT_SEND_ORDERED
65852c9ce25SScott Long #define	ADA_DEFAULT_SEND_ORDERED	1
65952c9ce25SScott Long #endif
66052c9ce25SScott Long 
661fd104c15SRebecca Cran #ifndef	ADA_DEFAULT_SPINDOWN_SHUTDOWN
662fd104c15SRebecca Cran #define	ADA_DEFAULT_SPINDOWN_SHUTDOWN	1
663fd104c15SRebecca Cran #endif
664fd104c15SRebecca Cran 
665c3d0d168SAlexander Motin #ifndef	ADA_DEFAULT_SPINDOWN_SUSPEND
666c3d0d168SAlexander Motin #define	ADA_DEFAULT_SPINDOWN_SUSPEND	1
667c3d0d168SAlexander Motin #endif
668c3d0d168SAlexander Motin 
6691ed6aaf9SAlexander Motin #ifndef	ADA_DEFAULT_READ_AHEAD
6701ed6aaf9SAlexander Motin #define	ADA_DEFAULT_READ_AHEAD	1
6711ed6aaf9SAlexander Motin #endif
6721ed6aaf9SAlexander Motin 
673f513d14cSAlexander Motin #ifndef	ADA_DEFAULT_WRITE_CACHE
674f513d14cSAlexander Motin #define	ADA_DEFAULT_WRITE_CACHE	1
675f513d14cSAlexander Motin #endif
676f513d14cSAlexander Motin 
6771ed6aaf9SAlexander Motin #define	ADA_RA	(softc->read_ahead >= 0 ? \
6781ed6aaf9SAlexander Motin 		 softc->read_ahead : ada_read_ahead)
6791ed6aaf9SAlexander Motin #define	ADA_WC	(softc->write_cache >= 0 ? \
6801ed6aaf9SAlexander Motin 		 softc->write_cache : ada_write_cache)
6811ed6aaf9SAlexander Motin 
6824461491bSMarius Strobl /*
6834461491bSMarius Strobl  * Most platforms map firmware geometry to actual, but some don't.  If
6844461491bSMarius Strobl  * not overridden, default to nothing.
6854461491bSMarius Strobl  */
6864461491bSMarius Strobl #ifndef ata_disk_firmware_geom_adjust
6874461491bSMarius Strobl #define	ata_disk_firmware_geom_adjust(disk)
6884461491bSMarius Strobl #endif
68952c9ce25SScott Long 
69052c9ce25SScott Long static int ada_retry_count = ADA_DEFAULT_RETRY;
69152c9ce25SScott Long static int ada_default_timeout = ADA_DEFAULT_TIMEOUT;
69252c9ce25SScott Long static int ada_send_ordered = ADA_DEFAULT_SEND_ORDERED;
693fd104c15SRebecca Cran static int ada_spindown_shutdown = ADA_DEFAULT_SPINDOWN_SHUTDOWN;
694c3d0d168SAlexander Motin static int ada_spindown_suspend = ADA_DEFAULT_SPINDOWN_SUSPEND;
6951ed6aaf9SAlexander Motin static int ada_read_ahead = ADA_DEFAULT_READ_AHEAD;
696f513d14cSAlexander Motin static int ada_write_cache = ADA_DEFAULT_WRITE_CACHE;
69752c9ce25SScott Long 
6986472ac3dSEd Schouten static SYSCTL_NODE(_kern_cam, OID_AUTO, ada, CTLFLAG_RD, 0,
69952c9ce25SScott Long             "CAM Direct Access Disk driver");
700af3b2549SHans Petter Selasky SYSCTL_INT(_kern_cam_ada, OID_AUTO, retry_count, CTLFLAG_RWTUN,
70152c9ce25SScott Long            &ada_retry_count, 0, "Normal I/O retry count");
702af3b2549SHans Petter Selasky SYSCTL_INT(_kern_cam_ada, OID_AUTO, default_timeout, CTLFLAG_RWTUN,
70352c9ce25SScott Long            &ada_default_timeout, 0, "Normal I/O timeout (in seconds)");
704af3b2549SHans Petter Selasky SYSCTL_INT(_kern_cam_ada, OID_AUTO, send_ordered, CTLFLAG_RWTUN,
70552c9ce25SScott Long            &ada_send_ordered, 0, "Send Ordered Tags");
706af3b2549SHans Petter Selasky SYSCTL_INT(_kern_cam_ada, OID_AUTO, spindown_shutdown, CTLFLAG_RWTUN,
707fd104c15SRebecca Cran            &ada_spindown_shutdown, 0, "Spin down upon shutdown");
708af3b2549SHans Petter Selasky SYSCTL_INT(_kern_cam_ada, OID_AUTO, spindown_suspend, CTLFLAG_RWTUN,
709c3d0d168SAlexander Motin            &ada_spindown_suspend, 0, "Spin down upon suspend");
710af3b2549SHans Petter Selasky SYSCTL_INT(_kern_cam_ada, OID_AUTO, read_ahead, CTLFLAG_RWTUN,
7111ed6aaf9SAlexander Motin            &ada_read_ahead, 0, "Enable disk read-ahead");
712af3b2549SHans Petter Selasky SYSCTL_INT(_kern_cam_ada, OID_AUTO, write_cache, CTLFLAG_RWTUN,
713f513d14cSAlexander Motin            &ada_write_cache, 0, "Enable disk write cache");
71452c9ce25SScott Long 
71552c9ce25SScott Long /*
71652c9ce25SScott Long  * ADA_ORDEREDTAG_INTERVAL determines how often, relative
71752c9ce25SScott Long  * to the default timeout, we check to see whether an ordered
71852c9ce25SScott Long  * tagged transaction is appropriate to prevent simple tag
71952c9ce25SScott Long  * starvation.  Since we'd like to ensure that there is at least
72052c9ce25SScott Long  * 1/2 of the timeout length left for a starved transaction to
72152c9ce25SScott Long  * complete after we've sent an ordered tag, we must poll at least
72252c9ce25SScott Long  * four times in every timeout period.  This takes care of the worst
72352c9ce25SScott Long  * case where a starved transaction starts during an interval that
72452c9ce25SScott Long  * meets the requirement "don't send an ordered tag" test so it takes
72552c9ce25SScott Long  * us two intervals to determine that a tag must be sent.
72652c9ce25SScott Long  */
72752c9ce25SScott Long #ifndef ADA_ORDEREDTAG_INTERVAL
72852c9ce25SScott Long #define ADA_ORDEREDTAG_INTERVAL 4
72952c9ce25SScott Long #endif
73052c9ce25SScott Long 
73152c9ce25SScott Long static struct periph_driver adadriver =
73252c9ce25SScott Long {
73352c9ce25SScott Long 	adainit, "ada",
73452c9ce25SScott Long 	TAILQ_HEAD_INITIALIZER(adadriver.units), /* generation */ 0
73552c9ce25SScott Long };
73652c9ce25SScott Long 
737a6e0c5daSWarner Losh static int adadeletemethodsysctl(SYSCTL_HANDLER_ARGS);
738a6e0c5daSWarner Losh 
73952c9ce25SScott Long PERIPHDRIVER_DECLARE(ada, adadriver);
74052c9ce25SScott Long 
74152c9ce25SScott Long static int
74252c9ce25SScott Long adaopen(struct disk *dp)
74352c9ce25SScott Long {
74452c9ce25SScott Long 	struct cam_periph *periph;
74552c9ce25SScott Long 	struct ada_softc *softc;
74652c9ce25SScott Long 	int error;
74752c9ce25SScott Long 
74852c9ce25SScott Long 	periph = (struct cam_periph *)dp->d_drv1;
74952c9ce25SScott Long 	if (cam_periph_acquire(periph) != CAM_REQ_CMP) {
75052c9ce25SScott Long 		return(ENXIO);
75152c9ce25SScott Long 	}
75252c9ce25SScott Long 
75352c9ce25SScott Long 	cam_periph_lock(periph);
75452c9ce25SScott Long 	if ((error = cam_periph_hold(periph, PRIBIO|PCATCH)) != 0) {
75552c9ce25SScott Long 		cam_periph_unlock(periph);
75652c9ce25SScott Long 		cam_periph_release(periph);
75752c9ce25SScott Long 		return (error);
75852c9ce25SScott Long 	}
75952c9ce25SScott Long 
760fddde2b8SAlexander Motin 	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE | CAM_DEBUG_PERIPH,
761fddde2b8SAlexander Motin 	    ("adaopen\n"));
76252c9ce25SScott Long 
7637338ef1aSAlexander Motin 	softc = (struct ada_softc *)periph->softc;
7647338ef1aSAlexander Motin 	softc->flags |= ADA_FLAG_OPEN;
76552c9ce25SScott Long 
76652c9ce25SScott Long 	cam_periph_unhold(periph);
76752c9ce25SScott Long 	cam_periph_unlock(periph);
76852c9ce25SScott Long 	return (0);
76952c9ce25SScott Long }
77052c9ce25SScott Long 
77152c9ce25SScott Long static int
77252c9ce25SScott Long adaclose(struct disk *dp)
77352c9ce25SScott Long {
77452c9ce25SScott Long 	struct	cam_periph *periph;
77552c9ce25SScott Long 	struct	ada_softc *softc;
77652c9ce25SScott Long 	union ccb *ccb;
77769114bc0SAlexander Motin 	int error;
77852c9ce25SScott Long 
77952c9ce25SScott Long 	periph = (struct cam_periph *)dp->d_drv1;
78052c9ce25SScott Long 	softc = (struct ada_softc *)periph->softc;
781227d67aaSAlexander Motin 	cam_periph_lock(periph);
782fddde2b8SAlexander Motin 
783fddde2b8SAlexander Motin 	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE | CAM_DEBUG_PERIPH,
784fddde2b8SAlexander Motin 	    ("adaclose\n"));
785fddde2b8SAlexander Motin 
78652c9ce25SScott Long 	/* We only sync the cache if the drive is capable of it. */
78769114bc0SAlexander Motin 	if ((softc->flags & ADA_FLAG_DIRTY) != 0 &&
78869114bc0SAlexander Motin 	    (softc->flags & ADA_FLAG_CAN_FLUSHCACHE) != 0 &&
789227d67aaSAlexander Motin 	    (periph->flags & CAM_PERIPH_INVALID) == 0 &&
790227d67aaSAlexander Motin 	    cam_periph_hold(periph, PRIBIO) == 0) {
79152c9ce25SScott Long 
792bbfa4aa1SAlexander Motin 		ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL);
79352c9ce25SScott Long 		cam_fill_ataio(&ccb->ataio,
79452c9ce25SScott Long 				    1,
79552c9ce25SScott Long 				    adadone,
79652c9ce25SScott Long 				    CAM_DIR_NONE,
79752c9ce25SScott Long 				    0,
79852c9ce25SScott Long 				    NULL,
79952c9ce25SScott Long 				    0,
80052c9ce25SScott Long 				    ada_default_timeout*1000);
80152c9ce25SScott Long 
80252c9ce25SScott Long 		if (softc->flags & ADA_FLAG_CAN_48BIT)
80352c9ce25SScott Long 			ata_48bit_cmd(&ccb->ataio, ATA_FLUSHCACHE48, 0, 0, 0);
80452c9ce25SScott Long 		else
8057606b445SAlexander Motin 			ata_28bit_cmd(&ccb->ataio, ATA_FLUSHCACHE, 0, 0, 0);
80669114bc0SAlexander Motin 		error = cam_periph_runccb(ccb, adaerror, /*cam_flags*/0,
80746f118feSAlexander Motin 		    /*sense_flags*/0, softc->disk->d_devstat);
80852c9ce25SScott Long 
80969114bc0SAlexander Motin 		if (error != 0)
81052c9ce25SScott Long 			xpt_print(periph->path, "Synchronize cache failed\n");
81169114bc0SAlexander Motin 		else
81269114bc0SAlexander Motin 			softc->flags &= ~ADA_FLAG_DIRTY;
81352c9ce25SScott Long 		xpt_release_ccb(ccb);
814227d67aaSAlexander Motin 		cam_periph_unhold(periph);
81552c9ce25SScott Long 	}
81652c9ce25SScott Long 
81752c9ce25SScott Long 	softc->flags &= ~ADA_FLAG_OPEN;
818227d67aaSAlexander Motin 
819227d67aaSAlexander Motin 	while (softc->refcount != 0)
820227d67aaSAlexander Motin 		cam_periph_sleep(periph, &softc->refcount, PRIBIO, "adaclose", 1);
82152c9ce25SScott Long 	cam_periph_unlock(periph);
82252c9ce25SScott Long 	cam_periph_release(periph);
82352c9ce25SScott Long 	return (0);
82452c9ce25SScott Long }
82552c9ce25SScott Long 
8261c80ec0aSAlexander Motin static void
8271c80ec0aSAlexander Motin adaschedule(struct cam_periph *periph)
8281c80ec0aSAlexander Motin {
8291c80ec0aSAlexander Motin 	struct ada_softc *softc = (struct ada_softc *)periph->softc;
8301c80ec0aSAlexander Motin 
8316bf435dcSAlexander Motin 	if (softc->state != ADA_STATE_NORMAL)
8326bf435dcSAlexander Motin 		return;
8336bf435dcSAlexander Motin 
834a6e0c5daSWarner Losh 	cam_iosched_schedule(softc->cam_iosched, periph);
8351c80ec0aSAlexander Motin }
8361c80ec0aSAlexander Motin 
83752c9ce25SScott Long /*
83852c9ce25SScott Long  * Actually translate the requested transfer into one the physical driver
83952c9ce25SScott Long  * can understand.  The transfer is described by a buf and will include
84052c9ce25SScott Long  * only one physical transfer.
84152c9ce25SScott Long  */
84252c9ce25SScott Long static void
84352c9ce25SScott Long adastrategy(struct bio *bp)
84452c9ce25SScott Long {
84552c9ce25SScott Long 	struct cam_periph *periph;
84652c9ce25SScott Long 	struct ada_softc *softc;
84752c9ce25SScott Long 
84852c9ce25SScott Long 	periph = (struct cam_periph *)bp->bio_disk->d_drv1;
84952c9ce25SScott Long 	softc = (struct ada_softc *)periph->softc;
85052c9ce25SScott Long 
85152c9ce25SScott Long 	cam_periph_lock(periph);
85252c9ce25SScott Long 
853fddde2b8SAlexander Motin 	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("adastrategy(%p)\n", bp));
854fddde2b8SAlexander Motin 
85552c9ce25SScott Long 	/*
85652c9ce25SScott Long 	 * If the device has been made invalid, error out
85752c9ce25SScott Long 	 */
8587338ef1aSAlexander Motin 	if ((periph->flags & CAM_PERIPH_INVALID) != 0) {
85952c9ce25SScott Long 		cam_periph_unlock(periph);
86052c9ce25SScott Long 		biofinish(bp, NULL, ENXIO);
86152c9ce25SScott Long 		return;
86252c9ce25SScott Long 	}
86352c9ce25SScott Long 
86452c9ce25SScott Long 	/*
86552c9ce25SScott Long 	 * Place it in the queue of disk activities for this disk
86652c9ce25SScott Long 	 */
867a6e0c5daSWarner Losh 	cam_iosched_queue_work(softc->cam_iosched, bp);
86852c9ce25SScott Long 
86952c9ce25SScott Long 	/*
87052c9ce25SScott Long 	 * Schedule ourselves for performing the work.
87152c9ce25SScott Long 	 */
8721c80ec0aSAlexander Motin 	adaschedule(periph);
87352c9ce25SScott Long 	cam_periph_unlock(periph);
87452c9ce25SScott Long 
87552c9ce25SScott Long 	return;
87652c9ce25SScott Long }
87752c9ce25SScott Long 
87852c9ce25SScott Long static int
87952c9ce25SScott Long adadump(void *arg, void *virtual, vm_offset_t physical, off_t offset, size_t length)
88052c9ce25SScott Long {
88152c9ce25SScott Long 	struct	    cam_periph *periph;
88252c9ce25SScott Long 	struct	    ada_softc *softc;
88352c9ce25SScott Long 	u_int	    secsize;
88452c9ce25SScott Long 	union	    ccb ccb;
88552c9ce25SScott Long 	struct	    disk *dp;
88652c9ce25SScott Long 	uint64_t    lba;
88752c9ce25SScott Long 	uint16_t    count;
8880191d9b3SAlexander Motin 	int	    error = 0;
88952c9ce25SScott Long 
89052c9ce25SScott Long 	dp = arg;
89152c9ce25SScott Long 	periph = dp->d_drv1;
89252c9ce25SScott Long 	softc = (struct ada_softc *)periph->softc;
89352c9ce25SScott Long 	cam_periph_lock(periph);
89452c9ce25SScott Long 	secsize = softc->params.secsize;
89552c9ce25SScott Long 	lba = offset / secsize;
89652c9ce25SScott Long 	count = length / secsize;
89752c9ce25SScott Long 
8987338ef1aSAlexander Motin 	if ((periph->flags & CAM_PERIPH_INVALID) != 0) {
89952c9ce25SScott Long 		cam_periph_unlock(periph);
90052c9ce25SScott Long 		return (ENXIO);
90152c9ce25SScott Long 	}
90252c9ce25SScott Long 
90352c9ce25SScott Long 	if (length > 0) {
904bbfa4aa1SAlexander Motin 		xpt_setup_ccb(&ccb.ccb_h, periph->path, CAM_PRIORITY_NORMAL);
90552c9ce25SScott Long 		ccb.ccb_h.ccb_state = ADA_CCB_DUMP;
90652c9ce25SScott Long 		cam_fill_ataio(&ccb.ataio,
90752c9ce25SScott Long 		    0,
90852c9ce25SScott Long 		    adadone,
90952c9ce25SScott Long 		    CAM_DIR_OUT,
91052c9ce25SScott Long 		    0,
91152c9ce25SScott Long 		    (u_int8_t *) virtual,
91252c9ce25SScott Long 		    length,
91352c9ce25SScott Long 		    ada_default_timeout*1000);
91452c9ce25SScott Long 		if ((softc->flags & ADA_FLAG_CAN_48BIT) &&
91552c9ce25SScott Long 		    (lba + count >= ATA_MAX_28BIT_LBA ||
91652c9ce25SScott Long 		    count >= 256)) {
91752c9ce25SScott Long 			ata_48bit_cmd(&ccb.ataio, ATA_WRITE_DMA48,
91852c9ce25SScott Long 			    0, lba, count);
91952c9ce25SScott Long 		} else {
9207606b445SAlexander Motin 			ata_28bit_cmd(&ccb.ataio, ATA_WRITE_DMA,
92152c9ce25SScott Long 			    0, lba, count);
92252c9ce25SScott Long 		}
92352c9ce25SScott Long 		xpt_polled_action(&ccb);
92452c9ce25SScott Long 
9250191d9b3SAlexander Motin 		error = cam_periph_error(&ccb,
9260191d9b3SAlexander Motin 		    0, SF_NO_RECOVERY | SF_NO_RETRY, NULL);
9270191d9b3SAlexander Motin 		if ((ccb.ccb_h.status & CAM_DEV_QFRZN) != 0)
9280191d9b3SAlexander Motin 			cam_release_devq(ccb.ccb_h.path, /*relsim_flags*/0,
9290191d9b3SAlexander Motin 			    /*reduction*/0, /*timeout*/0, /*getcount_only*/0);
9300191d9b3SAlexander Motin 		if (error != 0)
93152c9ce25SScott Long 			printf("Aborting dump due to I/O error.\n");
9320191d9b3SAlexander Motin 
93352c9ce25SScott Long 		cam_periph_unlock(periph);
9340191d9b3SAlexander Motin 		return (error);
93552c9ce25SScott Long 	}
93652c9ce25SScott Long 
93752c9ce25SScott Long 	if (softc->flags & ADA_FLAG_CAN_FLUSHCACHE) {
938bbfa4aa1SAlexander Motin 		xpt_setup_ccb(&ccb.ccb_h, periph->path, CAM_PRIORITY_NORMAL);
93952c9ce25SScott Long 
94086ddf15eSWarner Losh 		/*
941da908789SEnji Cooper 		 * Tell the drive to flush its internal cache. if we
94286ddf15eSWarner Losh 		 * can't flush in 5s we have big problems. No need to
94386ddf15eSWarner Losh 		 * wait the default 60s to detect problems.
94486ddf15eSWarner Losh 		 */
94552c9ce25SScott Long 		ccb.ccb_h.ccb_state = ADA_CCB_DUMP;
94652c9ce25SScott Long 		cam_fill_ataio(&ccb.ataio,
9470191d9b3SAlexander Motin 				    0,
94852c9ce25SScott Long 				    adadone,
94952c9ce25SScott Long 				    CAM_DIR_NONE,
95052c9ce25SScott Long 				    0,
95152c9ce25SScott Long 				    NULL,
95252c9ce25SScott Long 				    0,
953a6e0c5daSWarner Losh 				    5*1000);
95452c9ce25SScott Long 
95552c9ce25SScott Long 		if (softc->flags & ADA_FLAG_CAN_48BIT)
95652c9ce25SScott Long 			ata_48bit_cmd(&ccb.ataio, ATA_FLUSHCACHE48, 0, 0, 0);
95752c9ce25SScott Long 		else
9587606b445SAlexander Motin 			ata_28bit_cmd(&ccb.ataio, ATA_FLUSHCACHE, 0, 0, 0);
95952c9ce25SScott Long 		xpt_polled_action(&ccb);
96052c9ce25SScott Long 
9610191d9b3SAlexander Motin 		error = cam_periph_error(&ccb,
9620191d9b3SAlexander Motin 		    0, SF_NO_RECOVERY | SF_NO_RETRY, NULL);
96352c9ce25SScott Long 		if ((ccb.ccb_h.status & CAM_DEV_QFRZN) != 0)
9640191d9b3SAlexander Motin 			cam_release_devq(ccb.ccb_h.path, /*relsim_flags*/0,
9650191d9b3SAlexander Motin 			    /*reduction*/0, /*timeout*/0, /*getcount_only*/0);
9660191d9b3SAlexander Motin 		if (error != 0)
9670191d9b3SAlexander Motin 			xpt_print(periph->path, "Synchronize cache failed\n");
96852c9ce25SScott Long 	}
96952c9ce25SScott Long 	cam_periph_unlock(periph);
9700191d9b3SAlexander Motin 	return (error);
97152c9ce25SScott Long }
97252c9ce25SScott Long 
97352c9ce25SScott Long static void
97452c9ce25SScott Long adainit(void)
97552c9ce25SScott Long {
97652c9ce25SScott Long 	cam_status status;
97752c9ce25SScott Long 
97852c9ce25SScott Long 	/*
97952c9ce25SScott Long 	 * Install a global async callback.  This callback will
98052c9ce25SScott Long 	 * receive async callbacks like "new device found".
98152c9ce25SScott Long 	 */
98252c9ce25SScott Long 	status = xpt_register_async(AC_FOUND_DEVICE, adaasync, NULL, NULL);
98352c9ce25SScott Long 
98452c9ce25SScott Long 	if (status != CAM_REQ_CMP) {
98552c9ce25SScott Long 		printf("ada: Failed to attach master async callback "
98652c9ce25SScott Long 		       "due to status 0x%x!\n", status);
98752c9ce25SScott Long 	} else if (ada_send_ordered) {
98852c9ce25SScott Long 
989c3d0d168SAlexander Motin 		/* Register our event handlers */
990c3d0d168SAlexander Motin 		if ((EVENTHANDLER_REGISTER(power_suspend, adasuspend,
991c3d0d168SAlexander Motin 					   NULL, EVENTHANDLER_PRI_LAST)) == NULL)
992c3d0d168SAlexander Motin 		    printf("adainit: power event registration failed!\n");
993c3d0d168SAlexander Motin 		if ((EVENTHANDLER_REGISTER(power_resume, adaresume,
994c3d0d168SAlexander Motin 					   NULL, EVENTHANDLER_PRI_LAST)) == NULL)
995c3d0d168SAlexander Motin 		    printf("adainit: power event registration failed!\n");
99652c9ce25SScott Long 		if ((EVENTHANDLER_REGISTER(shutdown_post_sync, adashutdown,
99752c9ce25SScott Long 					   NULL, SHUTDOWN_PRI_DEFAULT)) == NULL)
99852c9ce25SScott Long 		    printf("adainit: shutdown event registration failed!\n");
99952c9ce25SScott Long 	}
100052c9ce25SScott Long }
100152c9ce25SScott Long 
10020ba1e4d0SKenneth D. Merry /*
10030ba1e4d0SKenneth D. Merry  * Callback from GEOM, called when it has finished cleaning up its
10040ba1e4d0SKenneth D. Merry  * resources.
10050ba1e4d0SKenneth D. Merry  */
10060ba1e4d0SKenneth D. Merry static void
10070ba1e4d0SKenneth D. Merry adadiskgonecb(struct disk *dp)
10080ba1e4d0SKenneth D. Merry {
10090ba1e4d0SKenneth D. Merry 	struct cam_periph *periph;
10100ba1e4d0SKenneth D. Merry 
10110ba1e4d0SKenneth D. Merry 	periph = (struct cam_periph *)dp->d_drv1;
10120ba1e4d0SKenneth D. Merry 
10130ba1e4d0SKenneth D. Merry 	cam_periph_release(periph);
10140ba1e4d0SKenneth D. Merry }
10150ba1e4d0SKenneth D. Merry 
101652c9ce25SScott Long static void
101752c9ce25SScott Long adaoninvalidate(struct cam_periph *periph)
101852c9ce25SScott Long {
101952c9ce25SScott Long 	struct ada_softc *softc;
102052c9ce25SScott Long 
102152c9ce25SScott Long 	softc = (struct ada_softc *)periph->softc;
102252c9ce25SScott Long 
102352c9ce25SScott Long 	/*
102452c9ce25SScott Long 	 * De-register any async callbacks.
102552c9ce25SScott Long 	 */
102652c9ce25SScott Long 	xpt_register_async(0, adaasync, periph, periph->path);
1027a6e0c5daSWarner Losh #ifdef CAM_IO_STATS
1028a6e0c5daSWarner Losh 	softc->invalidations++;
1029a6e0c5daSWarner Losh #endif
103052c9ce25SScott Long 
103152c9ce25SScott Long 	/*
103252c9ce25SScott Long 	 * Return all queued I/O with ENXIO.
103352c9ce25SScott Long 	 * XXX Handle any transactions queued to the card
103452c9ce25SScott Long 	 *     with XPT_ABORT_CCB.
103552c9ce25SScott Long 	 */
1036a6e0c5daSWarner Losh 	cam_iosched_flush(softc->cam_iosched, NULL, ENXIO);
103752c9ce25SScott Long 
103852c9ce25SScott Long 	disk_gone(softc->disk);
103952c9ce25SScott Long }
104052c9ce25SScott Long 
104152c9ce25SScott Long static void
104252c9ce25SScott Long adacleanup(struct cam_periph *periph)
104352c9ce25SScott Long {
104452c9ce25SScott Long 	struct ada_softc *softc;
104552c9ce25SScott Long 
104652c9ce25SScott Long 	softc = (struct ada_softc *)periph->softc;
104752c9ce25SScott Long 
104852c9ce25SScott Long 	cam_periph_unlock(periph);
104952c9ce25SScott Long 
1050a6e0c5daSWarner Losh 	cam_iosched_fini(softc->cam_iosched);
1051a6e0c5daSWarner Losh 
105252c9ce25SScott Long 	/*
105352c9ce25SScott Long 	 * If we can't free the sysctl tree, oh well...
105452c9ce25SScott Long 	 */
1055a6e0c5daSWarner Losh 	if ((softc->flags & ADA_FLAG_SCTX_INIT) != 0) {
1056a6e0c5daSWarner Losh #ifdef CAM_IO_STATS
1057a6e0c5daSWarner Losh 		if (sysctl_ctx_free(&softc->sysctl_stats_ctx) != 0)
1058a6e0c5daSWarner Losh 			xpt_print(periph->path,
1059a6e0c5daSWarner Losh 			    "can't remove sysctl stats context\n");
1060a6e0c5daSWarner Losh #endif
1061a6e0c5daSWarner Losh 		if (sysctl_ctx_free(&softc->sysctl_ctx) != 0)
1062a6e0c5daSWarner Losh 			xpt_print(periph->path,
1063a6e0c5daSWarner Losh 			    "can't remove sysctl context\n");
106452c9ce25SScott Long 	}
106552c9ce25SScott Long 
106652c9ce25SScott Long 	disk_destroy(softc->disk);
106752c9ce25SScott Long 	callout_drain(&softc->sendordered_c);
106852c9ce25SScott Long 	free(softc, M_DEVBUF);
106952c9ce25SScott Long 	cam_periph_lock(periph);
107052c9ce25SScott Long }
107152c9ce25SScott Long 
107252c9ce25SScott Long static void
1073a6e0c5daSWarner Losh adasetdeletemethod(struct ada_softc *softc)
1074a6e0c5daSWarner Losh {
1075a6e0c5daSWarner Losh 
1076a6e0c5daSWarner Losh 	if (softc->flags & ADA_FLAG_CAN_NCQ_TRIM)
1077a6e0c5daSWarner Losh 		softc->delete_method = ADA_DELETE_NCQ_DSM_TRIM;
1078a6e0c5daSWarner Losh 	else if (softc->flags & ADA_FLAG_CAN_TRIM)
1079a6e0c5daSWarner Losh 		softc->delete_method = ADA_DELETE_DSM_TRIM;
1080a6e0c5daSWarner Losh 	else if ((softc->flags & ADA_FLAG_CAN_CFA) && !(softc->flags & ADA_FLAG_CAN_48BIT))
1081a6e0c5daSWarner Losh 		softc->delete_method = ADA_DELETE_CFA_ERASE;
1082a6e0c5daSWarner Losh 	else
1083a6e0c5daSWarner Losh 		softc->delete_method = ADA_DELETE_NONE;
1084a6e0c5daSWarner Losh }
1085a6e0c5daSWarner Losh 
1086a6e0c5daSWarner Losh static void
108752c9ce25SScott Long adaasync(void *callback_arg, u_int32_t code,
108852c9ce25SScott Long 	struct cam_path *path, void *arg)
108952c9ce25SScott Long {
1090581b2e78SAlexander Motin 	struct ccb_getdev cgd;
109152c9ce25SScott Long 	struct cam_periph *periph;
1092f513d14cSAlexander Motin 	struct ada_softc *softc;
109352c9ce25SScott Long 
109452c9ce25SScott Long 	periph = (struct cam_periph *)callback_arg;
109552c9ce25SScott Long 	switch (code) {
109652c9ce25SScott Long 	case AC_FOUND_DEVICE:
109752c9ce25SScott Long 	{
109852c9ce25SScott Long 		struct ccb_getdev *cgd;
109952c9ce25SScott Long 		cam_status status;
110052c9ce25SScott Long 
110152c9ce25SScott Long 		cgd = (struct ccb_getdev *)arg;
110252c9ce25SScott Long 		if (cgd == NULL)
110352c9ce25SScott Long 			break;
110452c9ce25SScott Long 
110552c9ce25SScott Long 		if (cgd->protocol != PROTO_ATA)
110652c9ce25SScott Long 			break;
110752c9ce25SScott Long 
110852c9ce25SScott Long 		/*
110952c9ce25SScott Long 		 * Allocate a peripheral instance for
111052c9ce25SScott Long 		 * this device and start the probe
111152c9ce25SScott Long 		 * process.
111252c9ce25SScott Long 		 */
111352c9ce25SScott Long 		status = cam_periph_alloc(adaregister, adaoninvalidate,
111452c9ce25SScott Long 					  adacleanup, adastart,
111552c9ce25SScott Long 					  "ada", CAM_PERIPH_BIO,
1116227d67aaSAlexander Motin 					  path, adaasync,
111752c9ce25SScott Long 					  AC_FOUND_DEVICE, cgd);
111852c9ce25SScott Long 
111952c9ce25SScott Long 		if (status != CAM_REQ_CMP
112052c9ce25SScott Long 		 && status != CAM_REQ_INPROG)
112152c9ce25SScott Long 			printf("adaasync: Unable to attach to new device "
112252c9ce25SScott Long 				"due to status 0x%x\n", status);
112352c9ce25SScott Long 		break;
112452c9ce25SScott Long 	}
1125581b2e78SAlexander Motin 	case AC_GETDEV_CHANGED:
1126581b2e78SAlexander Motin 	{
1127581b2e78SAlexander Motin 		softc = (struct ada_softc *)periph->softc;
1128581b2e78SAlexander Motin 		xpt_setup_ccb(&cgd.ccb_h, periph->path, CAM_PRIORITY_NORMAL);
1129581b2e78SAlexander Motin 		cgd.ccb_h.func_code = XPT_GDEV_TYPE;
1130581b2e78SAlexander Motin 		xpt_action((union ccb *)&cgd);
1131581b2e78SAlexander Motin 
1132581b2e78SAlexander Motin 		if ((cgd.ident_data.capabilities1 & ATA_SUPPORT_DMA) &&
1133581b2e78SAlexander Motin 		    (cgd.inq_flags & SID_DMA))
1134581b2e78SAlexander Motin 			softc->flags |= ADA_FLAG_CAN_DMA;
1135581b2e78SAlexander Motin 		else
1136581b2e78SAlexander Motin 			softc->flags &= ~ADA_FLAG_CAN_DMA;
11372e1eb332SMarius Strobl 		if (cgd.ident_data.support.command2 & ATA_SUPPORT_ADDRESS48) {
11382e1eb332SMarius Strobl 			softc->flags |= ADA_FLAG_CAN_48BIT;
11392e1eb332SMarius Strobl 			if (cgd.inq_flags & SID_DMA48)
11402e1eb332SMarius Strobl 				softc->flags |= ADA_FLAG_CAN_DMA48;
11412e1eb332SMarius Strobl 			else
11422e1eb332SMarius Strobl 				softc->flags &= ~ADA_FLAG_CAN_DMA48;
11432e1eb332SMarius Strobl 		} else
11442e1eb332SMarius Strobl 			softc->flags &= ~(ADA_FLAG_CAN_48BIT |
11452e1eb332SMarius Strobl 			    ADA_FLAG_CAN_DMA48);
1146581b2e78SAlexander Motin 		if ((cgd.ident_data.satacapabilities & ATA_SUPPORT_NCQ) &&
1147581b2e78SAlexander Motin 		    (cgd.inq_flags & SID_DMA) && (cgd.inq_flags & SID_CmdQue))
1148581b2e78SAlexander Motin 			softc->flags |= ADA_FLAG_CAN_NCQ;
1149581b2e78SAlexander Motin 		else
1150581b2e78SAlexander Motin 			softc->flags &= ~ADA_FLAG_CAN_NCQ;
1151a6e0c5daSWarner Losh 
1152581b2e78SAlexander Motin 		if ((cgd.ident_data.support_dsm & ATA_SUPPORT_DSM_TRIM) &&
1153a6e0c5daSWarner Losh 		    (cgd.inq_flags & SID_DMA)) {
1154581b2e78SAlexander Motin 			softc->flags |= ADA_FLAG_CAN_TRIM;
1155a6e0c5daSWarner Losh 			/*
1156a6e0c5daSWarner Losh 			 * If we can do RCVSND_FPDMA_QUEUED commands, we may be able to do
1157a6e0c5daSWarner Losh 			 * NCQ trims, if we support trims at all. We also need support from
1158a6e0c5daSWarner Losh 			 * the sim do do things properly. Perhaps we should look at log 13
1159a6e0c5daSWarner Losh 			 * dword 0 bit 0 and dword 1 bit 0 are set too...
1160a6e0c5daSWarner Losh 			 */
1161a6e0c5daSWarner Losh 			if ((softc->quirks & ADA_Q_NCQ_TRIM_BROKEN) == 0 &&
1162a6e0c5daSWarner Losh 			    (softc->flags & ADA_FLAG_PIM_CAN_NCQ_TRIM) != 0 &&
1163a6e0c5daSWarner Losh 			    (cgd.ident_data.satacapabilities2 & ATA_SUPPORT_RCVSND_FPDMA_QUEUED) != 0 &&
1164a6e0c5daSWarner Losh 			    (softc->flags & ADA_FLAG_CAN_TRIM) != 0)
1165a6e0c5daSWarner Losh 				softc->flags |= ADA_FLAG_CAN_NCQ_TRIM;
1166581b2e78SAlexander Motin 			else
1167a6e0c5daSWarner Losh 				softc->flags &= ~ADA_FLAG_CAN_NCQ_TRIM;
1168a6e0c5daSWarner Losh 		} else
1169a6e0c5daSWarner Losh 			softc->flags &= ~(ADA_FLAG_CAN_TRIM | ADA_FLAG_CAN_NCQ_TRIM);
1170a6e0c5daSWarner Losh 		adasetdeletemethod(softc);
1171581b2e78SAlexander Motin 
1172581b2e78SAlexander Motin 		cam_periph_async(periph, code, path, arg);
1173581b2e78SAlexander Motin 		break;
1174581b2e78SAlexander Motin 	}
11753089bb2eSAlexander Motin 	case AC_ADVINFO_CHANGED:
11763089bb2eSAlexander Motin 	{
11773089bb2eSAlexander Motin 		uintptr_t buftype;
11783089bb2eSAlexander Motin 
11793089bb2eSAlexander Motin 		buftype = (uintptr_t)arg;
11803089bb2eSAlexander Motin 		if (buftype == CDAI_TYPE_PHYS_PATH) {
11813089bb2eSAlexander Motin 			struct ada_softc *softc;
11823089bb2eSAlexander Motin 
11833089bb2eSAlexander Motin 			softc = periph->softc;
11843089bb2eSAlexander Motin 			disk_attr_changed(softc->disk, "GEOM::physpath",
11853089bb2eSAlexander Motin 					  M_NOWAIT);
11863089bb2eSAlexander Motin 		}
11873089bb2eSAlexander Motin 		break;
11883089bb2eSAlexander Motin 	}
1189f513d14cSAlexander Motin 	case AC_SENT_BDR:
1190f513d14cSAlexander Motin 	case AC_BUS_RESET:
1191f513d14cSAlexander Motin 	{
1192f513d14cSAlexander Motin 		softc = (struct ada_softc *)periph->softc;
1193f513d14cSAlexander Motin 		cam_periph_async(periph, code, path, arg);
1194f513d14cSAlexander Motin 		if (softc->state != ADA_STATE_NORMAL)
1195f513d14cSAlexander Motin 			break;
1196e3a6d3a4SAlexander Motin 		xpt_setup_ccb(&cgd.ccb_h, periph->path, CAM_PRIORITY_NORMAL);
1197f513d14cSAlexander Motin 		cgd.ccb_h.func_code = XPT_GDEV_TYPE;
1198f513d14cSAlexander Motin 		xpt_action((union ccb *)&cgd);
11991ed6aaf9SAlexander Motin 		if (ADA_RA >= 0 &&
12001ed6aaf9SAlexander Motin 		    cgd.ident_data.support.command1 & ATA_SUPPORT_LOOKAHEAD)
12011ed6aaf9SAlexander Motin 			softc->state = ADA_STATE_RAHEAD;
12021ed6aaf9SAlexander Motin 		else if (ADA_WC >= 0 &&
12031ed6aaf9SAlexander Motin 		    cgd.ident_data.support.command1 & ATA_SUPPORT_WRITECACHE)
1204f513d14cSAlexander Motin 			softc->state = ADA_STATE_WCACHE;
12051ed6aaf9SAlexander Motin 		else
12061ed6aaf9SAlexander Motin 		    break;
1207227d67aaSAlexander Motin 		if (cam_periph_acquire(periph) != CAM_REQ_CMP)
1208227d67aaSAlexander Motin 			softc->state = ADA_STATE_NORMAL;
1209227d67aaSAlexander Motin 		else
1210f513d14cSAlexander Motin 			xpt_schedule(periph, CAM_PRIORITY_DEV);
1211f513d14cSAlexander Motin 	}
121252c9ce25SScott Long 	default:
121352c9ce25SScott Long 		cam_periph_async(periph, code, path, arg);
121452c9ce25SScott Long 		break;
121552c9ce25SScott Long 	}
121652c9ce25SScott Long }
121752c9ce25SScott Long 
121852c9ce25SScott Long static void
121952c9ce25SScott Long adasysctlinit(void *context, int pending)
122052c9ce25SScott Long {
122152c9ce25SScott Long 	struct cam_periph *periph;
122252c9ce25SScott Long 	struct ada_softc *softc;
122352c9ce25SScott Long 	char tmpstr[80], tmpstr2[80];
122452c9ce25SScott Long 
122552c9ce25SScott Long 	periph = (struct cam_periph *)context;
1226e3a6d3a4SAlexander Motin 
1227e3a6d3a4SAlexander Motin 	/* periph was held for us when this task was enqueued */
12287338ef1aSAlexander Motin 	if ((periph->flags & CAM_PERIPH_INVALID) != 0) {
1229e3a6d3a4SAlexander Motin 		cam_periph_release(periph);
123052c9ce25SScott Long 		return;
1231e3a6d3a4SAlexander Motin 	}
123252c9ce25SScott Long 
123352c9ce25SScott Long 	softc = (struct ada_softc *)periph->softc;
123452c9ce25SScott Long 	snprintf(tmpstr, sizeof(tmpstr), "CAM ADA unit %d", periph->unit_number);
123552c9ce25SScott Long 	snprintf(tmpstr2, sizeof(tmpstr2), "%d", periph->unit_number);
123652c9ce25SScott Long 
123752c9ce25SScott Long 	sysctl_ctx_init(&softc->sysctl_ctx);
123852c9ce25SScott Long 	softc->flags |= ADA_FLAG_SCTX_INIT;
123952c9ce25SScott Long 	softc->sysctl_tree = SYSCTL_ADD_NODE(&softc->sysctl_ctx,
124052c9ce25SScott Long 		SYSCTL_STATIC_CHILDREN(_kern_cam_ada), OID_AUTO, tmpstr2,
124152c9ce25SScott Long 		CTLFLAG_RD, 0, tmpstr);
124252c9ce25SScott Long 	if (softc->sysctl_tree == NULL) {
124352c9ce25SScott Long 		printf("adasysctlinit: unable to allocate sysctl tree\n");
124452c9ce25SScott Long 		cam_periph_release(periph);
124552c9ce25SScott Long 		return;
124652c9ce25SScott Long 	}
124752c9ce25SScott Long 
1248a6e0c5daSWarner Losh 	SYSCTL_ADD_PROC(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree),
1249a6e0c5daSWarner Losh 		OID_AUTO, "delete_method", CTLTYPE_STRING | CTLFLAG_RW,
1250a6e0c5daSWarner Losh 		softc, 0, adadeletemethodsysctl, "A",
1251a6e0c5daSWarner Losh 		"BIO_DELETE execution method");
1252e3a6d3a4SAlexander Motin 	SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree),
12531ed6aaf9SAlexander Motin 		OID_AUTO, "read_ahead", CTLFLAG_RW | CTLFLAG_MPSAFE,
12541ed6aaf9SAlexander Motin 		&softc->read_ahead, 0, "Enable disk read ahead.");
12551ed6aaf9SAlexander Motin 	SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree),
1256e3a6d3a4SAlexander Motin 		OID_AUTO, "write_cache", CTLFLAG_RW | CTLFLAG_MPSAFE,
1257e3a6d3a4SAlexander Motin 		&softc->write_cache, 0, "Enable disk write cache.");
12585f83aee5SSteven Hartland 	SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree),
1259a6e0c5daSWarner Losh 		OID_AUTO, "unmapped_io", CTLFLAG_RD | CTLFLAG_MPSAFE,
1260a6e0c5daSWarner Losh 		&softc->unmappedio, 0, "Unmapped I/O leaf");
1261a6e0c5daSWarner Losh 	SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree),
1262a6e0c5daSWarner Losh 		OID_AUTO, "rotating", CTLFLAG_RD | CTLFLAG_MPSAFE,
1263a6e0c5daSWarner Losh 		&softc->rotating, 0, "Rotating media");
1264e3a6d3a4SAlexander Motin #ifdef ADA_TEST_FAILURE
1265e3a6d3a4SAlexander Motin 	/*
1266e3a6d3a4SAlexander Motin 	 * Add a 'door bell' sysctl which allows one to set it from userland
1267e3a6d3a4SAlexander Motin 	 * and cause something bad to happen.  For the moment, we only allow
1268e3a6d3a4SAlexander Motin 	 * whacking the next read or write.
1269e3a6d3a4SAlexander Motin 	 */
1270e3a6d3a4SAlexander Motin 	SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree),
1271e3a6d3a4SAlexander Motin 		OID_AUTO, "force_read_error", CTLFLAG_RW | CTLFLAG_MPSAFE,
1272e3a6d3a4SAlexander Motin 		&softc->force_read_error, 0,
1273e3a6d3a4SAlexander Motin 		"Force a read error for the next N reads.");
1274e3a6d3a4SAlexander Motin 	SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree),
1275e3a6d3a4SAlexander Motin 		OID_AUTO, "force_write_error", CTLFLAG_RW | CTLFLAG_MPSAFE,
1276e3a6d3a4SAlexander Motin 		&softc->force_write_error, 0,
1277e3a6d3a4SAlexander Motin 		"Force a write error for the next N writes.");
1278e3a6d3a4SAlexander Motin 	SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree),
1279e3a6d3a4SAlexander Motin 		OID_AUTO, "periodic_read_error", CTLFLAG_RW | CTLFLAG_MPSAFE,
1280e3a6d3a4SAlexander Motin 		&softc->periodic_read_error, 0,
1281e3a6d3a4SAlexander Motin 		"Force a read error every N reads (don't set too low).");
1282e3a6d3a4SAlexander Motin #endif
1283a6e0c5daSWarner Losh 
1284a6e0c5daSWarner Losh #ifdef CAM_IO_STATS
1285a6e0c5daSWarner Losh 	softc->sysctl_stats_tree = SYSCTL_ADD_NODE(&softc->sysctl_stats_ctx,
1286a6e0c5daSWarner Losh 		SYSCTL_CHILDREN(softc->sysctl_tree), OID_AUTO, "stats",
1287a6e0c5daSWarner Losh 		CTLFLAG_RD, 0, "Statistics");
1288a6e0c5daSWarner Losh 	SYSCTL_ADD_INT(&softc->sysctl_stats_ctx,
1289a6e0c5daSWarner Losh 		SYSCTL_CHILDREN(softc->sysctl_stats_tree),
1290a6e0c5daSWarner Losh 		OID_AUTO, "timeouts", CTLFLAG_RD | CTLFLAG_MPSAFE,
1291a6e0c5daSWarner Losh 		&softc->timeouts, 0,
1292a6e0c5daSWarner Losh 		"Device timeouts reported by the SIM");
1293a6e0c5daSWarner Losh 	SYSCTL_ADD_INT(&softc->sysctl_stats_ctx,
1294a6e0c5daSWarner Losh 		SYSCTL_CHILDREN(softc->sysctl_stats_tree),
1295a6e0c5daSWarner Losh 		OID_AUTO, "errors", CTLFLAG_RD | CTLFLAG_MPSAFE,
1296a6e0c5daSWarner Losh 		&softc->errors, 0,
1297a6e0c5daSWarner Losh 		"Transport errors reported by the SIM.");
1298a6e0c5daSWarner Losh 	SYSCTL_ADD_INT(&softc->sysctl_stats_ctx,
1299a6e0c5daSWarner Losh 		SYSCTL_CHILDREN(softc->sysctl_stats_tree),
1300a6e0c5daSWarner Losh 		OID_AUTO, "pack_invalidations", CTLFLAG_RD | CTLFLAG_MPSAFE,
1301a6e0c5daSWarner Losh 		&softc->invalidations, 0,
1302a6e0c5daSWarner Losh 		"Device pack invalidations.");
1303a6e0c5daSWarner Losh #endif
1304a6e0c5daSWarner Losh 
1305a6e0c5daSWarner Losh 	cam_iosched_sysctl_init(softc->cam_iosched, &softc->sysctl_ctx,
1306a6e0c5daSWarner Losh 	    softc->sysctl_tree);
1307a6e0c5daSWarner Losh 
130852c9ce25SScott Long 	cam_periph_release(periph);
130952c9ce25SScott Long }
131052c9ce25SScott Long 
1311416494d7SJustin T. Gibbs static int
1312416494d7SJustin T. Gibbs adagetattr(struct bio *bp)
1313416494d7SJustin T. Gibbs {
13146884b662SAlexander Motin 	int ret;
1315416494d7SJustin T. Gibbs 	struct cam_periph *periph;
1316416494d7SJustin T. Gibbs 
1317416494d7SJustin T. Gibbs 	periph = (struct cam_periph *)bp->bio_disk->d_drv1;
13186884b662SAlexander Motin 	cam_periph_lock(periph);
1319416494d7SJustin T. Gibbs 	ret = xpt_getattr(bp->bio_data, bp->bio_length, bp->bio_attribute,
1320416494d7SJustin T. Gibbs 	    periph->path);
13216884b662SAlexander Motin 	cam_periph_unlock(periph);
1322416494d7SJustin T. Gibbs 	if (ret == 0)
1323416494d7SJustin T. Gibbs 		bp->bio_completed = bp->bio_length;
1324416494d7SJustin T. Gibbs 	return ret;
1325416494d7SJustin T. Gibbs }
1326416494d7SJustin T. Gibbs 
1327a6e0c5daSWarner Losh static int
1328a6e0c5daSWarner Losh adadeletemethodsysctl(SYSCTL_HANDLER_ARGS)
1329a6e0c5daSWarner Losh {
1330a6e0c5daSWarner Losh 	char buf[16];
1331a6e0c5daSWarner Losh 	const char *p;
1332a6e0c5daSWarner Losh 	struct ada_softc *softc;
1333a6e0c5daSWarner Losh 	int i, error, value, methods;
1334a6e0c5daSWarner Losh 
1335a6e0c5daSWarner Losh 	softc = (struct ada_softc *)arg1;
1336a6e0c5daSWarner Losh 
1337a6e0c5daSWarner Losh 	value = softc->delete_method;
1338a6e0c5daSWarner Losh 	if (value < 0 || value > ADA_DELETE_MAX)
1339a6e0c5daSWarner Losh 		p = "UNKNOWN";
1340a6e0c5daSWarner Losh 	else
1341a6e0c5daSWarner Losh 		p = ada_delete_method_names[value];
1342a6e0c5daSWarner Losh 	strncpy(buf, p, sizeof(buf));
1343a6e0c5daSWarner Losh 	error = sysctl_handle_string(oidp, buf, sizeof(buf), req);
1344a6e0c5daSWarner Losh 	if (error != 0 || req->newptr == NULL)
1345a6e0c5daSWarner Losh 		return (error);
1346a6e0c5daSWarner Losh 	methods = 1 << ADA_DELETE_DISABLE;
1347a6e0c5daSWarner Losh 	if ((softc->flags & ADA_FLAG_CAN_CFA) &&
1348a6e0c5daSWarner Losh 	    !(softc->flags & ADA_FLAG_CAN_48BIT))
1349a6e0c5daSWarner Losh 		methods |= 1 << ADA_DELETE_CFA_ERASE;
1350a6e0c5daSWarner Losh 	if (softc->flags & ADA_FLAG_CAN_TRIM)
1351a6e0c5daSWarner Losh 		methods |= 1 << ADA_DELETE_DSM_TRIM;
1352a6e0c5daSWarner Losh 	if (softc->flags & ADA_FLAG_CAN_NCQ_TRIM)
1353a6e0c5daSWarner Losh 		methods |= 1 << ADA_DELETE_NCQ_DSM_TRIM;
1354a6e0c5daSWarner Losh 	for (i = 0; i <= ADA_DELETE_MAX; i++) {
1355a6e0c5daSWarner Losh 		if (!(methods & (1 << i)) ||
1356a6e0c5daSWarner Losh 		    strcmp(buf, ada_delete_method_names[i]) != 0)
1357a6e0c5daSWarner Losh 			continue;
1358a6e0c5daSWarner Losh 		softc->delete_method = i;
1359a6e0c5daSWarner Losh 		return (0);
1360a6e0c5daSWarner Losh 	}
1361a6e0c5daSWarner Losh 	return (EINVAL);
1362a6e0c5daSWarner Losh }
1363a6e0c5daSWarner Losh 
136452c9ce25SScott Long static cam_status
136552c9ce25SScott Long adaregister(struct cam_periph *periph, void *arg)
136652c9ce25SScott Long {
136752c9ce25SScott Long 	struct ada_softc *softc;
136852c9ce25SScott Long 	struct ccb_pathinq cpi;
136952c9ce25SScott Long 	struct ccb_getdev *cgd;
13704a3760baSAlexander Motin 	char   announce_buf[80];
137152c9ce25SScott Long 	struct disk_params *dp;
137252c9ce25SScott Long 	caddr_t match;
137352c9ce25SScott Long 	u_int maxio;
13744a3760baSAlexander Motin 	int quirks;
137552c9ce25SScott Long 
137652c9ce25SScott Long 	cgd = (struct ccb_getdev *)arg;
137752c9ce25SScott Long 	if (cgd == NULL) {
137852c9ce25SScott Long 		printf("adaregister: no getdev CCB, can't register device\n");
137952c9ce25SScott Long 		return(CAM_REQ_CMP_ERR);
138052c9ce25SScott Long 	}
138152c9ce25SScott Long 
138252c9ce25SScott Long 	softc = (struct ada_softc *)malloc(sizeof(*softc), M_DEVBUF,
138352c9ce25SScott Long 	    M_NOWAIT|M_ZERO);
138452c9ce25SScott Long 
138552c9ce25SScott Long 	if (softc == NULL) {
138652c9ce25SScott Long 		printf("adaregister: Unable to probe new device. "
138752c9ce25SScott Long 		    "Unable to allocate softc\n");
138852c9ce25SScott Long 		return(CAM_REQ_CMP_ERR);
138952c9ce25SScott Long 	}
139052c9ce25SScott Long 
1391a6e0c5daSWarner Losh 	if (cam_iosched_init(&softc->cam_iosched, periph) != 0) {
1392a6e0c5daSWarner Losh 		printf("adaregister: Unable to probe new device. "
1393a6e0c5daSWarner Losh 		       "Unable to allocate iosched memory\n");
1394a6e0c5daSWarner Losh 		return(CAM_REQ_CMP_ERR);
1395a6e0c5daSWarner Losh 	}
139652c9ce25SScott Long 
1397581b2e78SAlexander Motin 	if ((cgd->ident_data.capabilities1 & ATA_SUPPORT_DMA) &&
1398cf2b9a5fSAlexander Motin 	    (cgd->inq_flags & SID_DMA))
139946f118feSAlexander Motin 		softc->flags |= ADA_FLAG_CAN_DMA;
14002e1eb332SMarius Strobl 	if (cgd->ident_data.support.command2 & ATA_SUPPORT_ADDRESS48) {
140152c9ce25SScott Long 		softc->flags |= ADA_FLAG_CAN_48BIT;
14022e1eb332SMarius Strobl 		if (cgd->inq_flags & SID_DMA48)
14032e1eb332SMarius Strobl 			softc->flags |= ADA_FLAG_CAN_DMA48;
14042e1eb332SMarius Strobl 	}
140552c9ce25SScott Long 	if (cgd->ident_data.support.command2 & ATA_SUPPORT_FLUSHCACHE)
140652c9ce25SScott Long 		softc->flags |= ADA_FLAG_CAN_FLUSHCACHE;
1407fd104c15SRebecca Cran 	if (cgd->ident_data.support.command1 & ATA_SUPPORT_POWERMGT)
1408fd104c15SRebecca Cran 		softc->flags |= ADA_FLAG_CAN_POWERMGT;
1409581b2e78SAlexander Motin 	if ((cgd->ident_data.satacapabilities & ATA_SUPPORT_NCQ) &&
1410cf2b9a5fSAlexander Motin 	    (cgd->inq_flags & SID_DMA) && (cgd->inq_flags & SID_CmdQue))
141152c9ce25SScott Long 		softc->flags |= ADA_FLAG_CAN_NCQ;
1412581b2e78SAlexander Motin 	if ((cgd->ident_data.support_dsm & ATA_SUPPORT_DSM_TRIM) &&
1413581b2e78SAlexander Motin 	    (cgd->inq_flags & SID_DMA)) {
14141c80ec0aSAlexander Motin 		softc->flags |= ADA_FLAG_CAN_TRIM;
14151c80ec0aSAlexander Motin 		softc->trim_max_ranges = TRIM_MAX_RANGES;
14161c80ec0aSAlexander Motin 		if (cgd->ident_data.max_dsm_blocks != 0) {
14171c80ec0aSAlexander Motin 			softc->trim_max_ranges =
1418c213c551SSteven Hartland 			    min(cgd->ident_data.max_dsm_blocks *
1419c213c551SSteven Hartland 				ATA_DSM_BLK_RANGES, softc->trim_max_ranges);
14201c80ec0aSAlexander Motin 		}
14211c80ec0aSAlexander Motin 	}
14221c80ec0aSAlexander Motin 	if (cgd->ident_data.support.command2 & ATA_SUPPORT_CFA)
14231c80ec0aSAlexander Motin 		softc->flags |= ADA_FLAG_CAN_CFA;
142452c9ce25SScott Long 
1425a6e0c5daSWarner Losh 	adasetdeletemethod(softc);
1426a6e0c5daSWarner Losh 
142752c9ce25SScott Long 	periph->softc = softc;
142852c9ce25SScott Long 
142952c9ce25SScott Long 	/*
143052c9ce25SScott Long 	 * See if this device has any quirks.
143152c9ce25SScott Long 	 */
143230a4094fSAlexander Motin 	match = cam_quirkmatch((caddr_t)&cgd->ident_data,
143330a4094fSAlexander Motin 			       (caddr_t)ada_quirk_table,
1434323b076eSPedro F. Giffuni 			       nitems(ada_quirk_table),
143530a4094fSAlexander Motin 			       sizeof(*ada_quirk_table), ata_identify_match);
143652c9ce25SScott Long 	if (match != NULL)
143752c9ce25SScott Long 		softc->quirks = ((struct ada_quirk_entry *)match)->quirks;
143852c9ce25SScott Long 	else
143952c9ce25SScott Long 		softc->quirks = ADA_Q_NONE;
144052c9ce25SScott Long 
144152c9ce25SScott Long 	bzero(&cpi, sizeof(cpi));
144283c5d981SAlexander Motin 	xpt_setup_ccb(&cpi.ccb_h, periph->path, CAM_PRIORITY_NONE);
144352c9ce25SScott Long 	cpi.ccb_h.func_code = XPT_PATH_INQ;
144452c9ce25SScott Long 	xpt_action((union ccb *)&cpi);
144552c9ce25SScott Long 
144652c9ce25SScott Long 	TASK_INIT(&softc->sysctl_task, 0, adasysctlinit, periph);
144752c9ce25SScott Long 
144852c9ce25SScott Long 	/*
144952c9ce25SScott Long 	 * Register this media as a disk
145052c9ce25SScott Long 	 */
1451781338b6SAlexander Motin 	(void)cam_periph_hold(periph, PRIBIO);
1452edec59d9SAlexander Motin 	cam_periph_unlock(periph);
1453d3a460d3SAlexander Motin 	snprintf(announce_buf, sizeof(announce_buf),
1454d3a460d3SAlexander Motin 	    "kern.cam.ada.%d.quirks", periph->unit_number);
1455d3a460d3SAlexander Motin 	quirks = softc->quirks;
1456d3a460d3SAlexander Motin 	TUNABLE_INT_FETCH(announce_buf, &quirks);
1457d3a460d3SAlexander Motin 	softc->quirks = quirks;
14581ed6aaf9SAlexander Motin 	softc->read_ahead = -1;
14591ed6aaf9SAlexander Motin 	snprintf(announce_buf, sizeof(announce_buf),
14601ed6aaf9SAlexander Motin 	    "kern.cam.ada.%d.read_ahead", periph->unit_number);
14611ed6aaf9SAlexander Motin 	TUNABLE_INT_FETCH(announce_buf, &softc->read_ahead);
1462781338b6SAlexander Motin 	softc->write_cache = -1;
1463781338b6SAlexander Motin 	snprintf(announce_buf, sizeof(announce_buf),
1464781338b6SAlexander Motin 	    "kern.cam.ada.%d.write_cache", periph->unit_number);
1465781338b6SAlexander Motin 	TUNABLE_INT_FETCH(announce_buf, &softc->write_cache);
146662cc3a63SSteven Hartland 	/* Disable queue sorting for non-rotational media by default. */
1467a6e0c5daSWarner Losh 	if (cgd->ident_data.media_rotation_rate == ATA_RATE_NON_ROTATING) {
1468a6e0c5daSWarner Losh 		softc->rotating = 0;
1469a6e0c5daSWarner Losh 	} else {
1470a6e0c5daSWarner Losh 		softc->rotating = 1;
1471a6e0c5daSWarner Losh 	}
1472a6e0c5daSWarner Losh 	cam_iosched_set_sort_queue(softc->cam_iosched,  softc->rotating ? -1 : 0);
1473c1bd46c2SAlexander Motin 	adagetparams(periph, cgd);
147452c9ce25SScott Long 	softc->disk = disk_alloc();
1475c28078e9SSteven Hartland 	softc->disk->d_rotation_rate = cgd->ident_data.media_rotation_rate;
1476b8b6b5d3SAlexander Motin 	softc->disk->d_devstat = devstat_new_entry(periph->periph_name,
1477b8b6b5d3SAlexander Motin 			  periph->unit_number, softc->params.secsize,
1478b8b6b5d3SAlexander Motin 			  DEVSTAT_ALL_SUPPORTED,
1479b8b6b5d3SAlexander Motin 			  DEVSTAT_TYPE_DIRECT |
1480b8b6b5d3SAlexander Motin 			  XPORT_DEVSTAT_TYPE(cpi.transport),
1481b8b6b5d3SAlexander Motin 			  DEVSTAT_PRIORITY_DISK);
148252c9ce25SScott Long 	softc->disk->d_open = adaopen;
148352c9ce25SScott Long 	softc->disk->d_close = adaclose;
148452c9ce25SScott Long 	softc->disk->d_strategy = adastrategy;
1485416494d7SJustin T. Gibbs 	softc->disk->d_getattr = adagetattr;
148652c9ce25SScott Long 	softc->disk->d_dump = adadump;
14870ba1e4d0SKenneth D. Merry 	softc->disk->d_gone = adadiskgonecb;
148852c9ce25SScott Long 	softc->disk->d_name = "ada";
148952c9ce25SScott Long 	softc->disk->d_drv1 = periph;
149052c9ce25SScott Long 	maxio = cpi.maxio;		/* Honor max I/O size of SIM */
149152c9ce25SScott Long 	if (maxio == 0)
149252c9ce25SScott Long 		maxio = DFLTPHYS;	/* traditional default */
149352c9ce25SScott Long 	else if (maxio > MAXPHYS)
149452c9ce25SScott Long 		maxio = MAXPHYS;	/* for safety */
14951c80ec0aSAlexander Motin 	if (softc->flags & ADA_FLAG_CAN_48BIT)
1496c1bd46c2SAlexander Motin 		maxio = min(maxio, 65536 * softc->params.secsize);
149752c9ce25SScott Long 	else					/* 28bit ATA command limit */
1498c1bd46c2SAlexander Motin 		maxio = min(maxio, 256 * softc->params.secsize);
149952c9ce25SScott Long 	softc->disk->d_maxsize = maxio;
150052c9ce25SScott Long 	softc->disk->d_unit = periph->unit_number;
150140ea77a0SAlexander Motin 	softc->disk->d_flags = DISKFLAG_DIRECT_COMPLETION;
150252c9ce25SScott Long 	if (softc->flags & ADA_FLAG_CAN_FLUSHCACHE)
150352c9ce25SScott Long 		softc->disk->d_flags |= DISKFLAG_CANFLUSHCACHE;
1504c213c551SSteven Hartland 	if (softc->flags & ADA_FLAG_CAN_TRIM) {
15051c80ec0aSAlexander Motin 		softc->disk->d_flags |= DISKFLAG_CANDELETE;
15069fe9ba5bSSteven Hartland 		softc->disk->d_delmaxsize = softc->params.secsize *
15079fe9ba5bSSteven Hartland 					    ATA_DSM_RANGE_MAX *
15089fe9ba5bSSteven Hartland 					    softc->trim_max_ranges;
1509c213c551SSteven Hartland 	} else if ((softc->flags & ADA_FLAG_CAN_CFA) &&
1510c213c551SSteven Hartland 	    !(softc->flags & ADA_FLAG_CAN_48BIT)) {
1511c213c551SSteven Hartland 		softc->disk->d_flags |= DISKFLAG_CANDELETE;
15129fe9ba5bSSteven Hartland 		softc->disk->d_delmaxsize = 256 * softc->params.secsize;
15139fe9ba5bSSteven Hartland 	} else
15149fe9ba5bSSteven Hartland 		softc->disk->d_delmaxsize = maxio;
1515a6e0c5daSWarner Losh 	if ((cpi.hba_misc & PIM_UNMAPPED) != 0) {
1516abc1e60eSKonstantin Belousov 		softc->disk->d_flags |= DISKFLAG_UNMAPPED_BIO;
1517a6e0c5daSWarner Losh 		softc->unmappedio = 1;
1518a6e0c5daSWarner Losh 	}
1519a6e0c5daSWarner Losh 	/*
1520a6e0c5daSWarner Losh 	 * If we can do RCVSND_FPDMA_QUEUED commands, we may be able to do
1521a6e0c5daSWarner Losh 	 * NCQ trims, if we support trims at all. We also need support from
1522a6e0c5daSWarner Losh 	 * the sim do do things properly. Perhaps we should look at log 13
1523a6e0c5daSWarner Losh 	 * dword 0 bit 0 and dword 1 bit 0 are set too...
1524a6e0c5daSWarner Losh 	 */
1525916d57dfSWarner Losh 	if (cpi.hba_misc & PIM_ATA_EXT)
1526a6e0c5daSWarner Losh 		softc->flags |= ADA_FLAG_PIM_CAN_NCQ_TRIM;
1527a6e0c5daSWarner Losh 	if ((softc->quirks & ADA_Q_NCQ_TRIM_BROKEN) == 0 &&
1528a6e0c5daSWarner Losh 	    (softc->flags & ADA_FLAG_PIM_CAN_NCQ_TRIM) != 0 &&
1529a6e0c5daSWarner Losh 	    (cgd->ident_data.satacapabilities2 & ATA_SUPPORT_RCVSND_FPDMA_QUEUED) != 0 &&
1530a6e0c5daSWarner Losh 	    (softc->flags & ADA_FLAG_CAN_TRIM) != 0)
1531a6e0c5daSWarner Losh 		softc->flags |= ADA_FLAG_CAN_NCQ_TRIM;
153265cb6238SNathan Whitehorn 	strlcpy(softc->disk->d_descr, cgd->ident_data.model,
153365cb6238SNathan Whitehorn 	    MIN(sizeof(softc->disk->d_descr), sizeof(cgd->ident_data.model)));
1534d50aaa6dSAndriy Gapon 	strlcpy(softc->disk->d_ident, cgd->ident_data.serial,
1535d50aaa6dSAndriy Gapon 	    MIN(sizeof(softc->disk->d_ident), sizeof(cgd->ident_data.serial)));
15368edcf694SAlexander Motin 	softc->disk->d_hba_vendor = cpi.hba_vendor;
15378edcf694SAlexander Motin 	softc->disk->d_hba_device = cpi.hba_device;
15388edcf694SAlexander Motin 	softc->disk->d_hba_subvendor = cpi.hba_subvendor;
15398edcf694SAlexander Motin 	softc->disk->d_hba_subdevice = cpi.hba_subdevice;
154052c9ce25SScott Long 
154152c9ce25SScott Long 	softc->disk->d_sectorsize = softc->params.secsize;
1542c1bd46c2SAlexander Motin 	softc->disk->d_mediasize = (off_t)softc->params.sectors *
1543c1bd46c2SAlexander Motin 	    softc->params.secsize;
1544ce8332d4SAlexander Motin 	if (ata_physical_sector_size(&cgd->ident_data) !=
1545ce8332d4SAlexander Motin 	    softc->params.secsize) {
1546ce8332d4SAlexander Motin 		softc->disk->d_stripesize =
1547ce8332d4SAlexander Motin 		    ata_physical_sector_size(&cgd->ident_data);
1548ce8332d4SAlexander Motin 		softc->disk->d_stripeoffset = (softc->disk->d_stripesize -
1549ce8332d4SAlexander Motin 		    ata_logical_sector_offset(&cgd->ident_data)) %
1550ce8332d4SAlexander Motin 		    softc->disk->d_stripesize;
1551d3a460d3SAlexander Motin 	} else if (softc->quirks & ADA_Q_4K) {
1552d3a460d3SAlexander Motin 		softc->disk->d_stripesize = 4096;
1553d3a460d3SAlexander Motin 		softc->disk->d_stripeoffset = 0;
1554ce8332d4SAlexander Motin 	}
155552c9ce25SScott Long 	softc->disk->d_fwsectors = softc->params.secs_per_track;
155652c9ce25SScott Long 	softc->disk->d_fwheads = softc->params.heads;
15574461491bSMarius Strobl 	ata_disk_firmware_geom_adjust(softc->disk);
1558a6e0c5daSWarner Losh 	adasetdeletemethod(softc);
155952c9ce25SScott Long 
15600ba1e4d0SKenneth D. Merry 	/*
15610ba1e4d0SKenneth D. Merry 	 * Acquire a reference to the periph before we register with GEOM.
15620ba1e4d0SKenneth D. Merry 	 * We'll release this reference once GEOM calls us back (via
15630ba1e4d0SKenneth D. Merry 	 * adadiskgonecb()) telling us that our provider has been freed.
15640ba1e4d0SKenneth D. Merry 	 */
15650ba1e4d0SKenneth D. Merry 	if (cam_periph_acquire(periph) != CAM_REQ_CMP) {
15660ba1e4d0SKenneth D. Merry 		xpt_print(periph->path, "%s: lost periph during "
15670ba1e4d0SKenneth D. Merry 			  "registration!\n", __func__);
15680ba1e4d0SKenneth D. Merry 		cam_periph_lock(periph);
15690ba1e4d0SKenneth D. Merry 		return (CAM_REQ_CMP_ERR);
15700ba1e4d0SKenneth D. Merry 	}
157152c9ce25SScott Long 	disk_create(softc->disk, DISK_VERSION);
1572edec59d9SAlexander Motin 	cam_periph_lock(periph);
1573781338b6SAlexander Motin 	cam_periph_unhold(periph);
157452c9ce25SScott Long 
157552c9ce25SScott Long 	dp = &softc->params;
157652c9ce25SScott Long 	snprintf(announce_buf, sizeof(announce_buf),
157768546995SAlexander Motin 	    "%juMB (%ju %u byte sectors)",
157868546995SAlexander Motin 	    ((uintmax_t)dp->secsize * dp->sectors) / (1024 * 1024),
157968546995SAlexander Motin 	    (uintmax_t)dp->sectors, dp->secsize);
158052c9ce25SScott Long 	xpt_announce_periph(periph, announce_buf);
15816fb5c84eSSteven Hartland 	xpt_announce_quirks(periph, softc->quirks, ADA_Q_BIT_STRING);
1582e3a6d3a4SAlexander Motin 
1583e3a6d3a4SAlexander Motin 	/*
1584e3a6d3a4SAlexander Motin 	 * Create our sysctl variables, now that we know
1585e3a6d3a4SAlexander Motin 	 * we have successfully attached.
1586e3a6d3a4SAlexander Motin 	 */
1587227d67aaSAlexander Motin 	if (cam_periph_acquire(periph) == CAM_REQ_CMP)
1588e3a6d3a4SAlexander Motin 		taskqueue_enqueue(taskqueue_thread, &softc->sysctl_task);
1589e3a6d3a4SAlexander Motin 
159052c9ce25SScott Long 	/*
159152c9ce25SScott Long 	 * Add async callbacks for bus reset and
159252c9ce25SScott Long 	 * bus device reset calls.  I don't bother
159352c9ce25SScott Long 	 * checking if this fails as, in most cases,
159452c9ce25SScott Long 	 * the system will function just fine without
159552c9ce25SScott Long 	 * them and the only alternative would be to
159652c9ce25SScott Long 	 * not attach the device on failure.
159752c9ce25SScott Long 	 */
15983089bb2eSAlexander Motin 	xpt_register_async(AC_SENT_BDR | AC_BUS_RESET | AC_LOST_DEVICE |
1599581b2e78SAlexander Motin 	    AC_GETDEV_CHANGED | AC_ADVINFO_CHANGED,
1600581b2e78SAlexander Motin 	    adaasync, periph, periph->path);
160152c9ce25SScott Long 
160252c9ce25SScott Long 	/*
160352c9ce25SScott Long 	 * Schedule a periodic event to occasionally send an
160452c9ce25SScott Long 	 * ordered tag to a device.
160552c9ce25SScott Long 	 */
1606227d67aaSAlexander Motin 	callout_init_mtx(&softc->sendordered_c, cam_periph_mtx(periph), 0);
160752c9ce25SScott Long 	callout_reset(&softc->sendordered_c,
160847bb9643SAlexander Motin 	    (ada_default_timeout * hz) / ADA_ORDEREDTAG_INTERVAL,
160952c9ce25SScott Long 	    adasendorderedtag, softc);
161052c9ce25SScott Long 
16111ed6aaf9SAlexander Motin 	if (ADA_RA >= 0 &&
16121ed6aaf9SAlexander Motin 	    cgd->ident_data.support.command1 & ATA_SUPPORT_LOOKAHEAD) {
16131ed6aaf9SAlexander Motin 		softc->state = ADA_STATE_RAHEAD;
16141ed6aaf9SAlexander Motin 	} else if (ADA_WC >= 0 &&
1615f513d14cSAlexander Motin 	    cgd->ident_data.support.command1 & ATA_SUPPORT_WRITECACHE) {
1616f513d14cSAlexander Motin 		softc->state = ADA_STATE_WCACHE;
1617227d67aaSAlexander Motin 	} else {
1618f513d14cSAlexander Motin 		softc->state = ADA_STATE_NORMAL;
1619227d67aaSAlexander Motin 		return(CAM_REQ_CMP);
1620227d67aaSAlexander Motin 	}
1621227d67aaSAlexander Motin 	if (cam_periph_acquire(periph) != CAM_REQ_CMP)
1622227d67aaSAlexander Motin 		softc->state = ADA_STATE_NORMAL;
1623227d67aaSAlexander Motin 	else
1624227d67aaSAlexander Motin 		xpt_schedule(periph, CAM_PRIORITY_DEV);
162552c9ce25SScott Long 	return(CAM_REQ_CMP);
162652c9ce25SScott Long }
162752c9ce25SScott Long 
1628a6e0c5daSWarner Losh static int
1629a6e0c5daSWarner Losh ada_dsmtrim_req_create(struct ada_softc *softc, struct bio *bp, struct trim_request *req)
163052c9ce25SScott Long {
163137ddbd16SAlexander Motin 	uint64_t lastlba = (uint64_t)-1;
16322030b294SAlexander Motin 	int c, lastcount = 0, off, ranges = 0;
163352c9ce25SScott Long 
16341c80ec0aSAlexander Motin 	bzero(req, sizeof(*req));
16352030b294SAlexander Motin 	TAILQ_INIT(&req->bps);
16361c80ec0aSAlexander Motin 	do {
16377ddad071SWarner Losh 		uint64_t lba = bp->bio_pblkno;
16387ddad071SWarner Losh 		int count = bp->bio_bcount / softc->params.secsize;
16391c80ec0aSAlexander Motin 
164037ddbd16SAlexander Motin 		/* Try to extend the previous range. */
164137ddbd16SAlexander Motin 		if (lba == lastlba) {
1642c213c551SSteven Hartland 			c = min(count, ATA_DSM_RANGE_MAX - lastcount);
164337ddbd16SAlexander Motin 			lastcount += c;
1644c213c551SSteven Hartland 			off = (ranges - 1) * ATA_DSM_RANGE_SIZE;
164537ddbd16SAlexander Motin 			req->data[off + 6] = lastcount & 0xff;
164637ddbd16SAlexander Motin 			req->data[off + 7] =
164737ddbd16SAlexander Motin 				(lastcount >> 8) & 0xff;
164837ddbd16SAlexander Motin 			count -= c;
164937ddbd16SAlexander Motin 			lba += c;
165037ddbd16SAlexander Motin 		}
165137ddbd16SAlexander Motin 
165237ddbd16SAlexander Motin 		while (count > 0) {
1653c213c551SSteven Hartland 			c = min(count, ATA_DSM_RANGE_MAX);
1654c213c551SSteven Hartland 			off = ranges * ATA_DSM_RANGE_SIZE;
16551c80ec0aSAlexander Motin 			req->data[off + 0] = lba & 0xff;
16561c80ec0aSAlexander Motin 			req->data[off + 1] = (lba >> 8) & 0xff;
16571c80ec0aSAlexander Motin 			req->data[off + 2] = (lba >> 16) & 0xff;
16581c80ec0aSAlexander Motin 			req->data[off + 3] = (lba >> 24) & 0xff;
16591c80ec0aSAlexander Motin 			req->data[off + 4] = (lba >> 32) & 0xff;
16601c80ec0aSAlexander Motin 			req->data[off + 5] = (lba >> 40) & 0xff;
16611c80ec0aSAlexander Motin 			req->data[off + 6] = c & 0xff;
16621c80ec0aSAlexander Motin 			req->data[off + 7] = (c >> 8) & 0xff;
16631c80ec0aSAlexander Motin 			lba += c;
16641c80ec0aSAlexander Motin 			count -= c;
166537ddbd16SAlexander Motin 			lastcount = c;
16661c80ec0aSAlexander Motin 			ranges++;
1667c213c551SSteven Hartland 			/*
1668c213c551SSteven Hartland 			 * Its the caller's responsibility to ensure the
1669c213c551SSteven Hartland 			 * request will fit so we don't need to check for
1670c213c551SSteven Hartland 			 * overrun here
1671c213c551SSteven Hartland 			 */
16721c80ec0aSAlexander Motin 		}
167337ddbd16SAlexander Motin 		lastlba = lba;
16747ddad071SWarner Losh 		TAILQ_INSERT_TAIL(&req->bps, bp, bio_queue);
1675a6e0c5daSWarner Losh 
1676a6e0c5daSWarner Losh 		bp = cam_iosched_next_trim(softc->cam_iosched);
1677a6e0c5daSWarner Losh 		if (bp == NULL)
16781c80ec0aSAlexander Motin 			break;
1679a6e0c5daSWarner Losh 		if (bp->bio_bcount / softc->params.secsize >
1680a6e0c5daSWarner Losh 		    (softc->trim_max_ranges - ranges) * ATA_DSM_RANGE_MAX) {
1681a6e0c5daSWarner Losh 			cam_iosched_put_back_trim(softc->cam_iosched, bp);
1682a6e0c5daSWarner Losh 			break;
1683a6e0c5daSWarner Losh 		}
16841c80ec0aSAlexander Motin 	} while (1);
1685a6e0c5daSWarner Losh 
1686a6e0c5daSWarner Losh 	return (ranges);
1687a6e0c5daSWarner Losh }
1688a6e0c5daSWarner Losh 
1689a6e0c5daSWarner Losh static void
1690a6e0c5daSWarner Losh ada_dsmtrim(struct ada_softc *softc, struct bio *bp, struct ccb_ataio *ataio)
1691a6e0c5daSWarner Losh {
1692a6e0c5daSWarner Losh 	struct trim_request *req = &softc->trim_req;
1693a6e0c5daSWarner Losh 	int ranges;
1694a6e0c5daSWarner Losh 
1695a6e0c5daSWarner Losh 	ranges = ada_dsmtrim_req_create(softc, bp, req);
16961c80ec0aSAlexander Motin 	cam_fill_ataio(ataio,
16971c80ec0aSAlexander Motin 	    ada_retry_count,
16981c80ec0aSAlexander Motin 	    adadone,
16991c80ec0aSAlexander Motin 	    CAM_DIR_OUT,
17001c80ec0aSAlexander Motin 	    0,
17011c80ec0aSAlexander Motin 	    req->data,
1702*55e0987aSPedro F. Giffuni 	    howmany(ranges, ATA_DSM_BLK_RANGES) * ATA_DSM_BLK_SIZE,
17031c80ec0aSAlexander Motin 	    ada_default_timeout * 1000);
17041c80ec0aSAlexander Motin 	ata_48bit_cmd(ataio, ATA_DATA_SET_MANAGEMENT,
1705*55e0987aSPedro F. Giffuni 	    ATA_DSM_TRIM, 0, howmany(ranges, ATA_DSM_BLK_RANGES));
17067ddad071SWarner Losh }
17077ddad071SWarner Losh 
17087ddad071SWarner Losh static void
1709a6e0c5daSWarner Losh ada_ncq_dsmtrim(struct ada_softc *softc, struct bio *bp, struct ccb_ataio *ataio)
1710a6e0c5daSWarner Losh {
1711a6e0c5daSWarner Losh 	struct trim_request *req = &softc->trim_req;
1712a6e0c5daSWarner Losh 	int ranges;
1713a6e0c5daSWarner Losh 
1714a6e0c5daSWarner Losh 	ranges = ada_dsmtrim_req_create(softc, bp, req);
1715a6e0c5daSWarner Losh 	cam_fill_ataio(ataio,
1716a6e0c5daSWarner Losh 	    ada_retry_count,
1717a6e0c5daSWarner Losh 	    adadone,
1718a6e0c5daSWarner Losh 	    CAM_DIR_OUT,
1719a6e0c5daSWarner Losh 	    0,
1720a6e0c5daSWarner Losh 	    req->data,
1721*55e0987aSPedro F. Giffuni 	    howmany(ranges, ATA_DSM_BLK_RANGES) * ATA_DSM_BLK_SIZE,
1722a6e0c5daSWarner Losh 	    ada_default_timeout * 1000);
1723a6e0c5daSWarner Losh 	ata_ncq_cmd(ataio,
1724a6e0c5daSWarner Losh 	    ATA_SEND_FPDMA_QUEUED,
1725a6e0c5daSWarner Losh 	    0,
1726*55e0987aSPedro F. Giffuni 	    howmany(ranges, ATA_DSM_BLK_RANGES));
1727a6e0c5daSWarner Losh 	ataio->cmd.sector_count_exp = ATA_SFPDMA_DSM;
1728916d57dfSWarner Losh 	ataio->ata_flags |= ATA_FLAG_AUX;
1729916d57dfSWarner Losh 	ataio->aux = 1;
1730a6e0c5daSWarner Losh }
1731a6e0c5daSWarner Losh 
1732a6e0c5daSWarner Losh static void
17337ddad071SWarner Losh ada_cfaerase(struct ada_softc *softc, struct bio *bp, struct ccb_ataio *ataio)
17347ddad071SWarner Losh {
1735467298f5SSteven Hartland 	struct trim_request *req = &softc->trim_req;
17367ddad071SWarner Losh 	uint64_t lba = bp->bio_pblkno;
17377ddad071SWarner Losh 	uint16_t count = bp->bio_bcount / softc->params.secsize;
17387ddad071SWarner Losh 
1739467298f5SSteven Hartland 	bzero(req, sizeof(*req));
1740467298f5SSteven Hartland 	TAILQ_INIT(&req->bps);
1741467298f5SSteven Hartland 	TAILQ_INSERT_TAIL(&req->bps, bp, bio_queue);
1742467298f5SSteven Hartland 
17437ddad071SWarner Losh 	cam_fill_ataio(ataio,
17447ddad071SWarner Losh 	    ada_retry_count,
17457ddad071SWarner Losh 	    adadone,
17467ddad071SWarner Losh 	    CAM_DIR_NONE,
17477ddad071SWarner Losh 	    0,
17487ddad071SWarner Losh 	    NULL,
17497ddad071SWarner Losh 	    0,
17507ddad071SWarner Losh 	    ada_default_timeout*1000);
17517ddad071SWarner Losh 
17527ddad071SWarner Losh 	if (count >= 256)
17537ddad071SWarner Losh 		count = 0;
17547ddad071SWarner Losh 	ata_28bit_cmd(ataio, ATA_CFA_ERASE, 0, lba, count);
17557ddad071SWarner Losh }
17567ddad071SWarner Losh 
17577ddad071SWarner Losh static void
17587ddad071SWarner Losh adastart(struct cam_periph *periph, union ccb *start_ccb)
17597ddad071SWarner Losh {
17607ddad071SWarner Losh 	struct ada_softc *softc = (struct ada_softc *)periph->softc;
17617ddad071SWarner Losh 	struct ccb_ataio *ataio = &start_ccb->ataio;
17627ddad071SWarner Losh 
17637ddad071SWarner Losh 	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("adastart\n"));
17647ddad071SWarner Losh 
17657ddad071SWarner Losh 	switch (softc->state) {
17667ddad071SWarner Losh 	case ADA_STATE_NORMAL:
17677ddad071SWarner Losh 	{
17687ddad071SWarner Losh 		struct bio *bp;
17697ddad071SWarner Losh 		u_int8_t tag_code;
17707ddad071SWarner Losh 
1771a6e0c5daSWarner Losh 		bp = cam_iosched_next_bio(softc->cam_iosched);
17721c80ec0aSAlexander Motin 		if (bp == NULL) {
17731c80ec0aSAlexander Motin 			xpt_release_ccb(start_ccb);
17741c80ec0aSAlexander Motin 			break;
17751c80ec0aSAlexander Motin 		}
177652c9ce25SScott Long 
1777a6e0c5daSWarner Losh 		if ((bp->bio_flags & BIO_ORDERED) != 0 ||
1778a6e0c5daSWarner Losh 		    (bp->bio_cmd != BIO_DELETE && (softc->flags & ADA_FLAG_NEED_OTAG) != 0)) {
177952c9ce25SScott Long 			softc->flags &= ~ADA_FLAG_NEED_OTAG;
1780030844d1SAlexander Motin 			softc->flags |= ADA_FLAG_WAS_OTAG;
178146f118feSAlexander Motin 			tag_code = 0;
178252c9ce25SScott Long 		} else {
178346f118feSAlexander Motin 			tag_code = 1;
178452c9ce25SScott Long 		}
178552c9ce25SScott Long 		switch (bp->bio_cmd) {
178652c9ce25SScott Long 		case BIO_WRITE:
178769114bc0SAlexander Motin 		case BIO_READ:
178852c9ce25SScott Long 		{
178952c9ce25SScott Long 			uint64_t lba = bp->bio_pblkno;
179052c9ce25SScott Long 			uint16_t count = bp->bio_bcount / softc->params.secsize;
1791a9934668SKenneth D. Merry 			void *data_ptr;
1792a9934668SKenneth D. Merry 			int rw_op;
1793a9934668SKenneth D. Merry 
1794a9934668SKenneth D. Merry 			if (bp->bio_cmd == BIO_WRITE) {
1795a9934668SKenneth D. Merry 				softc->flags |= ADA_FLAG_DIRTY;
1796a9934668SKenneth D. Merry 				rw_op = CAM_DIR_OUT;
1797a9934668SKenneth D. Merry 			} else {
1798a9934668SKenneth D. Merry 				rw_op = CAM_DIR_IN;
1799a9934668SKenneth D. Merry 			}
1800a9934668SKenneth D. Merry 
1801a9934668SKenneth D. Merry 			data_ptr = bp->bio_data;
1802a9934668SKenneth D. Merry 			if ((bp->bio_flags & (BIO_UNMAPPED|BIO_VLIST)) != 0) {
1803a9934668SKenneth D. Merry 				rw_op |= CAM_DATA_BIO;
1804a9934668SKenneth D. Merry 				data_ptr = bp;
1805a9934668SKenneth D. Merry 			}
1806a9934668SKenneth D. Merry 
1807e3a6d3a4SAlexander Motin #ifdef ADA_TEST_FAILURE
1808e3a6d3a4SAlexander Motin 			int fail = 0;
180952c9ce25SScott Long 
1810e3a6d3a4SAlexander Motin 			/*
1811e3a6d3a4SAlexander Motin 			 * Support the failure ioctls.  If the command is a
1812e3a6d3a4SAlexander Motin 			 * read, and there are pending forced read errors, or
1813e3a6d3a4SAlexander Motin 			 * if a write and pending write errors, then fail this
1814e3a6d3a4SAlexander Motin 			 * operation with EIO.  This is useful for testing
1815e3a6d3a4SAlexander Motin 			 * purposes.  Also, support having every Nth read fail.
1816e3a6d3a4SAlexander Motin 			 *
1817e3a6d3a4SAlexander Motin 			 * This is a rather blunt tool.
1818e3a6d3a4SAlexander Motin 			 */
1819e3a6d3a4SAlexander Motin 			if (bp->bio_cmd == BIO_READ) {
1820e3a6d3a4SAlexander Motin 				if (softc->force_read_error) {
1821e3a6d3a4SAlexander Motin 					softc->force_read_error--;
1822e3a6d3a4SAlexander Motin 					fail = 1;
1823e3a6d3a4SAlexander Motin 				}
1824e3a6d3a4SAlexander Motin 				if (softc->periodic_read_error > 0) {
1825e3a6d3a4SAlexander Motin 					if (++softc->periodic_read_count >=
1826e3a6d3a4SAlexander Motin 					    softc->periodic_read_error) {
1827e3a6d3a4SAlexander Motin 						softc->periodic_read_count = 0;
1828e3a6d3a4SAlexander Motin 						fail = 1;
1829e3a6d3a4SAlexander Motin 					}
1830e3a6d3a4SAlexander Motin 				}
1831e3a6d3a4SAlexander Motin 			} else {
1832e3a6d3a4SAlexander Motin 				if (softc->force_write_error) {
1833e3a6d3a4SAlexander Motin 					softc->force_write_error--;
1834e3a6d3a4SAlexander Motin 					fail = 1;
1835e3a6d3a4SAlexander Motin 				}
1836e3a6d3a4SAlexander Motin 			}
1837e3a6d3a4SAlexander Motin 			if (fail) {
18384beec135SAlexander Motin 				biofinish(bp, NULL, EIO);
1839e3a6d3a4SAlexander Motin 				xpt_release_ccb(start_ccb);
1840e3a6d3a4SAlexander Motin 				adaschedule(periph);
1841e3a6d3a4SAlexander Motin 				return;
1842e3a6d3a4SAlexander Motin 			}
1843e3a6d3a4SAlexander Motin #endif
1844abc1e60eSKonstantin Belousov 			KASSERT((bp->bio_flags & BIO_UNMAPPED) == 0 ||
1845abc1e60eSKonstantin Belousov 			    round_page(bp->bio_bcount + bp->bio_ma_offset) /
1846abc1e60eSKonstantin Belousov 			    PAGE_SIZE == bp->bio_ma_n,
1847abc1e60eSKonstantin Belousov 			    ("Short bio %p", bp));
184852c9ce25SScott Long 			cam_fill_ataio(ataio,
184952c9ce25SScott Long 			    ada_retry_count,
185052c9ce25SScott Long 			    adadone,
1851a9934668SKenneth D. Merry 			    rw_op,
1852e4cc6558SWarner Losh 			    0,
1853a9934668SKenneth D. Merry 			    data_ptr,
185452c9ce25SScott Long 			    bp->bio_bcount,
185552c9ce25SScott Long 			    ada_default_timeout*1000);
185652c9ce25SScott Long 
185746f118feSAlexander Motin 			if ((softc->flags & ADA_FLAG_CAN_NCQ) && tag_code) {
185852c9ce25SScott Long 				if (bp->bio_cmd == BIO_READ) {
185952c9ce25SScott Long 					ata_ncq_cmd(ataio, ATA_READ_FPDMA_QUEUED,
186052c9ce25SScott Long 					    lba, count);
186152c9ce25SScott Long 				} else {
186252c9ce25SScott Long 					ata_ncq_cmd(ataio, ATA_WRITE_FPDMA_QUEUED,
186352c9ce25SScott Long 					    lba, count);
186452c9ce25SScott Long 				}
186552c9ce25SScott Long 			} else if ((softc->flags & ADA_FLAG_CAN_48BIT) &&
186652c9ce25SScott Long 			    (lba + count >= ATA_MAX_28BIT_LBA ||
186746f118feSAlexander Motin 			    count > 256)) {
18682e1eb332SMarius Strobl 				if (softc->flags & ADA_FLAG_CAN_DMA48) {
186952c9ce25SScott Long 					if (bp->bio_cmd == BIO_READ) {
187052c9ce25SScott Long 						ata_48bit_cmd(ataio, ATA_READ_DMA48,
187152c9ce25SScott Long 						    0, lba, count);
187252c9ce25SScott Long 					} else {
187352c9ce25SScott Long 						ata_48bit_cmd(ataio, ATA_WRITE_DMA48,
187452c9ce25SScott Long 						    0, lba, count);
187552c9ce25SScott Long 					}
187652c9ce25SScott Long 				} else {
187752c9ce25SScott Long 					if (bp->bio_cmd == BIO_READ) {
187846f118feSAlexander Motin 						ata_48bit_cmd(ataio, ATA_READ_MUL48,
187946f118feSAlexander Motin 						    0, lba, count);
188046f118feSAlexander Motin 					} else {
188146f118feSAlexander Motin 						ata_48bit_cmd(ataio, ATA_WRITE_MUL48,
188246f118feSAlexander Motin 						    0, lba, count);
188346f118feSAlexander Motin 					}
188446f118feSAlexander Motin 				}
188546f118feSAlexander Motin 			} else {
188646f118feSAlexander Motin 				if (count == 256)
188746f118feSAlexander Motin 					count = 0;
188846f118feSAlexander Motin 				if (softc->flags & ADA_FLAG_CAN_DMA) {
188946f118feSAlexander Motin 					if (bp->bio_cmd == BIO_READ) {
18907606b445SAlexander Motin 						ata_28bit_cmd(ataio, ATA_READ_DMA,
189152c9ce25SScott Long 						    0, lba, count);
189252c9ce25SScott Long 					} else {
18937606b445SAlexander Motin 						ata_28bit_cmd(ataio, ATA_WRITE_DMA,
189452c9ce25SScott Long 						    0, lba, count);
189552c9ce25SScott Long 					}
189646f118feSAlexander Motin 				} else {
189746f118feSAlexander Motin 					if (bp->bio_cmd == BIO_READ) {
189846f118feSAlexander Motin 						ata_28bit_cmd(ataio, ATA_READ_MUL,
189946f118feSAlexander Motin 						    0, lba, count);
190046f118feSAlexander Motin 					} else {
190146f118feSAlexander Motin 						ata_28bit_cmd(ataio, ATA_WRITE_MUL,
190246f118feSAlexander Motin 						    0, lba, count);
190346f118feSAlexander Motin 					}
190446f118feSAlexander Motin 				}
190552c9ce25SScott Long 			}
190652c9ce25SScott Long 			break;
19071c80ec0aSAlexander Motin 		}
1908a6e0c5daSWarner Losh 		case BIO_DELETE:
1909a6e0c5daSWarner Losh 			switch (softc->delete_method) {
1910a6e0c5daSWarner Losh 			case ADA_DELETE_NCQ_DSM_TRIM:
1911a6e0c5daSWarner Losh 				ada_ncq_dsmtrim(softc, bp, ataio);
1912a6e0c5daSWarner Losh 				break;
1913a6e0c5daSWarner Losh 			case ADA_DELETE_DSM_TRIM:
1914a6e0c5daSWarner Losh 				ada_dsmtrim(softc, bp, ataio);
1915a6e0c5daSWarner Losh 				break;
1916a6e0c5daSWarner Losh 			case ADA_DELETE_CFA_ERASE:
1917a6e0c5daSWarner Losh 				ada_cfaerase(softc, bp, ataio);
1918a6e0c5daSWarner Losh 				break;
1919a6e0c5daSWarner Losh 			default:
1920a6e0c5daSWarner Losh 				biofinish(bp, NULL, EOPNOTSUPP);
1921a6e0c5daSWarner Losh 				xpt_release_ccb(start_ccb);
1922a6e0c5daSWarner Losh 				adaschedule(periph);
1923a6e0c5daSWarner Losh 				return;
1924a6e0c5daSWarner Losh 			}
1925a6e0c5daSWarner Losh 			start_ccb->ccb_h.ccb_state = ADA_CCB_TRIM;
1926a6e0c5daSWarner Losh 			start_ccb->ccb_h.flags |= CAM_UNLOCKED;
1927a6e0c5daSWarner Losh 			cam_iosched_submit_trim(softc->cam_iosched);
1928a6e0c5daSWarner Losh 			goto out;
192952c9ce25SScott Long 		case BIO_FLUSH:
193052c9ce25SScott Long 			cam_fill_ataio(ataio,
193152c9ce25SScott Long 			    1,
193252c9ce25SScott Long 			    adadone,
193352c9ce25SScott Long 			    CAM_DIR_NONE,
193446f118feSAlexander Motin 			    0,
193552c9ce25SScott Long 			    NULL,
193652c9ce25SScott Long 			    0,
193752c9ce25SScott Long 			    ada_default_timeout*1000);
193852c9ce25SScott Long 
193952c9ce25SScott Long 			if (softc->flags & ADA_FLAG_CAN_48BIT)
194052c9ce25SScott Long 				ata_48bit_cmd(ataio, ATA_FLUSHCACHE48, 0, 0, 0);
194152c9ce25SScott Long 			else
19427606b445SAlexander Motin 				ata_28bit_cmd(ataio, ATA_FLUSHCACHE, 0, 0, 0);
194352c9ce25SScott Long 			break;
194452c9ce25SScott Long 		}
194552c9ce25SScott Long 		start_ccb->ccb_h.ccb_state = ADA_CCB_BUFFER_IO;
1946227d67aaSAlexander Motin 		start_ccb->ccb_h.flags |= CAM_UNLOCKED;
19471c80ec0aSAlexander Motin out:
194852c9ce25SScott Long 		start_ccb->ccb_h.ccb_bp = bp;
1949c1bd46c2SAlexander Motin 		softc->outstanding_cmds++;
1950227d67aaSAlexander Motin 		softc->refcount++;
1951227d67aaSAlexander Motin 		cam_periph_unlock(periph);
195252c9ce25SScott Long 		xpt_action(start_ccb);
1953227d67aaSAlexander Motin 		cam_periph_lock(periph);
1954227d67aaSAlexander Motin 		softc->refcount--;
195552c9ce25SScott Long 
19561c80ec0aSAlexander Motin 		/* May have more work to do, so ensure we stay scheduled */
19571c80ec0aSAlexander Motin 		adaschedule(periph);
195852c9ce25SScott Long 		break;
195952c9ce25SScott Long 	}
19601ed6aaf9SAlexander Motin 	case ADA_STATE_RAHEAD:
1961f513d14cSAlexander Motin 	case ADA_STATE_WCACHE:
1962f513d14cSAlexander Motin 	{
1963f513d14cSAlexander Motin 		cam_fill_ataio(ataio,
1964f513d14cSAlexander Motin 		    1,
1965f513d14cSAlexander Motin 		    adadone,
1966f513d14cSAlexander Motin 		    CAM_DIR_NONE,
1967f513d14cSAlexander Motin 		    0,
1968f513d14cSAlexander Motin 		    NULL,
1969f513d14cSAlexander Motin 		    0,
1970f513d14cSAlexander Motin 		    ada_default_timeout*1000);
1971f513d14cSAlexander Motin 
19721ed6aaf9SAlexander Motin 		if (softc->state == ADA_STATE_RAHEAD) {
19731ed6aaf9SAlexander Motin 			ata_28bit_cmd(ataio, ATA_SETFEATURES, ADA_RA ?
19741ed6aaf9SAlexander Motin 			    ATA_SF_ENAB_RCACHE : ATA_SF_DIS_RCACHE, 0, 0);
19751ed6aaf9SAlexander Motin 			start_ccb->ccb_h.ccb_state = ADA_CCB_RAHEAD;
19761ed6aaf9SAlexander Motin 		} else {
19771ed6aaf9SAlexander Motin 			ata_28bit_cmd(ataio, ATA_SETFEATURES, ADA_WC ?
1978f513d14cSAlexander Motin 			    ATA_SF_ENAB_WCACHE : ATA_SF_DIS_WCACHE, 0, 0);
1979f513d14cSAlexander Motin 			start_ccb->ccb_h.ccb_state = ADA_CCB_WCACHE;
19801ed6aaf9SAlexander Motin 		}
1981cccf4220SAlexander Motin 		start_ccb->ccb_h.flags |= CAM_DEV_QFREEZE;
1982f513d14cSAlexander Motin 		xpt_action(start_ccb);
1983f513d14cSAlexander Motin 		break;
1984f513d14cSAlexander Motin 	}
198552c9ce25SScott Long 	}
198652c9ce25SScott Long }
198752c9ce25SScott Long 
198852c9ce25SScott Long static void
198952c9ce25SScott Long adadone(struct cam_periph *periph, union ccb *done_ccb)
199052c9ce25SScott Long {
199152c9ce25SScott Long 	struct ada_softc *softc;
199252c9ce25SScott Long 	struct ccb_ataio *ataio;
19931ed6aaf9SAlexander Motin 	struct ccb_getdev *cgd;
1994cccf4220SAlexander Motin 	struct cam_path *path;
19957651b989SAlexander Motin 	int state;
199652c9ce25SScott Long 
199752c9ce25SScott Long 	softc = (struct ada_softc *)periph->softc;
199852c9ce25SScott Long 	ataio = &done_ccb->ataio;
1999cccf4220SAlexander Motin 	path = done_ccb->ccb_h.path;
2000fddde2b8SAlexander Motin 
2001cccf4220SAlexander Motin 	CAM_DEBUG(path, CAM_DEBUG_TRACE, ("adadone\n"));
2002fddde2b8SAlexander Motin 
20037651b989SAlexander Motin 	state = ataio->ccb_h.ccb_state & ADA_CCB_TYPE_MASK;
20047651b989SAlexander Motin 	switch (state) {
200552c9ce25SScott Long 	case ADA_CCB_BUFFER_IO:
20061c80ec0aSAlexander Motin 	case ADA_CCB_TRIM:
200752c9ce25SScott Long 	{
200852c9ce25SScott Long 		struct bio *bp;
200952c9ce25SScott Long 		int error;
201052c9ce25SScott Long 
2011227d67aaSAlexander Motin 		cam_periph_lock(periph);
2012a6e0c5daSWarner Losh 		bp = (struct bio *)done_ccb->ccb_h.ccb_bp;
20137651b989SAlexander Motin 		if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
201446f118feSAlexander Motin 			error = adaerror(done_ccb, 0, 0);
201552c9ce25SScott Long 			if (error == ERESTART) {
201646f118feSAlexander Motin 				/* A retry was scheduled, so just return. */
2017227d67aaSAlexander Motin 				cam_periph_unlock(periph);
201852c9ce25SScott Long 				return;
201952c9ce25SScott Long 			}
202052c9ce25SScott Long 			if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
2021cccf4220SAlexander Motin 				cam_release_devq(path,
202252c9ce25SScott Long 						 /*relsim_flags*/0,
202352c9ce25SScott Long 						 /*reduction*/0,
202452c9ce25SScott Long 						 /*timeout*/0,
202552c9ce25SScott Long 						 /*getcount_only*/0);
2026a6e0c5daSWarner Losh 			/*
2027a6e0c5daSWarner Losh 			 * If we get an error on an NCQ DSM TRIM, fall back
2028a6e0c5daSWarner Losh 			 * to a non-NCQ DSM TRIM forever. Please note that if
2029a6e0c5daSWarner Losh 			 * CAN_NCQ_TRIM is set, CAN_TRIM is necessarily set too.
2030a6e0c5daSWarner Losh 			 * However, for this one trim, we treat it as advisory
2031a6e0c5daSWarner Losh 			 * and return success up the stack.
2032a6e0c5daSWarner Losh 			 */
2033a6e0c5daSWarner Losh 			if (state == ADA_CCB_TRIM &&
2034a6e0c5daSWarner Losh 			    error != 0 &&
2035a6e0c5daSWarner Losh 			    (softc->flags & ADA_FLAG_CAN_NCQ_TRIM) != 0) {
2036a6e0c5daSWarner Losh 				softc->flags &= ~ADA_FLAG_CAN_NCQ_TRIM;
2037a6e0c5daSWarner Losh 				error = 0;
2038a6e0c5daSWarner Losh 				adasetdeletemethod(softc);
2039a6e0c5daSWarner Losh 			}
204052c9ce25SScott Long 		} else {
204152c9ce25SScott Long 			if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
204252c9ce25SScott Long 				panic("REQ_CMP with QFRZN");
20437651b989SAlexander Motin 			error = 0;
20447651b989SAlexander Motin 		}
20457651b989SAlexander Motin 		bp->bio_error = error;
20467651b989SAlexander Motin 		if (error != 0) {
20477651b989SAlexander Motin 			bp->bio_resid = bp->bio_bcount;
20487651b989SAlexander Motin 			bp->bio_flags |= BIO_ERROR;
20497651b989SAlexander Motin 		} else {
20507651b989SAlexander Motin 			if (state == ADA_CCB_TRIM)
20517651b989SAlexander Motin 				bp->bio_resid = 0;
20527651b989SAlexander Motin 			else
205352c9ce25SScott Long 				bp->bio_resid = ataio->resid;
20547651b989SAlexander Motin 			if (bp->bio_resid > 0)
205552c9ce25SScott Long 				bp->bio_flags |= BIO_ERROR;
205652c9ce25SScott Long 		}
205752c9ce25SScott Long 		softc->outstanding_cmds--;
205852c9ce25SScott Long 		if (softc->outstanding_cmds == 0)
2059030844d1SAlexander Motin 			softc->flags |= ADA_FLAG_WAS_OTAG;
2060a6e0c5daSWarner Losh 
2061a6e0c5daSWarner Losh 		cam_iosched_bio_complete(softc->cam_iosched, bp, done_ccb);
2062227d67aaSAlexander Motin 		xpt_release_ccb(done_ccb);
20637651b989SAlexander Motin 		if (state == ADA_CCB_TRIM) {
20642030b294SAlexander Motin 			TAILQ_HEAD(, bio) queue;
20652030b294SAlexander Motin 			struct bio *bp1;
206652c9ce25SScott Long 
20672030b294SAlexander Motin 			TAILQ_INIT(&queue);
20682030b294SAlexander Motin 			TAILQ_CONCAT(&queue, &softc->trim_req.bps, bio_queue);
20690ac66574SWarner Losh 			/*
20700ac66574SWarner Losh 			 * Normally, the xpt_release_ccb() above would make sure
20710ac66574SWarner Losh 			 * that when we have more work to do, that work would
20720ac66574SWarner Losh 			 * get kicked off. However, we specifically keep
20730ac66574SWarner Losh 			 * trim_running set to 0 before the call above to allow
20740ac66574SWarner Losh 			 * other I/O to progress when many BIO_DELETE requests
20750ac66574SWarner Losh 			 * are pushed down. We set trim_running to 0 and call
20760ac66574SWarner Losh 			 * daschedule again so that we don't stall if there are
20770ac66574SWarner Losh 			 * no other I/Os pending apart from BIO_DELETEs.
20780ac66574SWarner Losh 			 */
2079a6e0c5daSWarner Losh 			cam_iosched_trim_done(softc->cam_iosched);
2080227d67aaSAlexander Motin 			adaschedule(periph);
2081227d67aaSAlexander Motin 			cam_periph_unlock(periph);
20822030b294SAlexander Motin 			while ((bp1 = TAILQ_FIRST(&queue)) != NULL) {
20832030b294SAlexander Motin 				TAILQ_REMOVE(&queue, bp1, bio_queue);
20842030b294SAlexander Motin 				bp1->bio_error = error;
20852030b294SAlexander Motin 				if (error != 0) {
20861c80ec0aSAlexander Motin 					bp1->bio_flags |= BIO_ERROR;
20877651b989SAlexander Motin 					bp1->bio_resid = bp1->bio_bcount;
20887651b989SAlexander Motin 				} else
20897651b989SAlexander Motin 					bp1->bio_resid = 0;
20901c80ec0aSAlexander Motin 				biodone(bp1);
20911c80ec0aSAlexander Motin 			}
2092227d67aaSAlexander Motin 		} else {
2093a6e0c5daSWarner Losh 			adaschedule(periph);
2094227d67aaSAlexander Motin 			cam_periph_unlock(periph);
209552c9ce25SScott Long 			biodone(bp);
2096227d67aaSAlexander Motin 		}
2097227d67aaSAlexander Motin 		return;
209852c9ce25SScott Long 	}
20991ed6aaf9SAlexander Motin 	case ADA_CCB_RAHEAD:
21001ed6aaf9SAlexander Motin 	{
21011ed6aaf9SAlexander Motin 		if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
21021ed6aaf9SAlexander Motin 			if (adaerror(done_ccb, 0, 0) == ERESTART) {
2103cccf4220SAlexander Motin out:
2104cccf4220SAlexander Motin 				/* Drop freeze taken due to CAM_DEV_QFREEZE */
2105cccf4220SAlexander Motin 				cam_release_devq(path, 0, 0, 0, FALSE);
21061ed6aaf9SAlexander Motin 				return;
21071ed6aaf9SAlexander Motin 			} else if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) {
2108cccf4220SAlexander Motin 				cam_release_devq(path,
21091ed6aaf9SAlexander Motin 				    /*relsim_flags*/0,
21101ed6aaf9SAlexander Motin 				    /*reduction*/0,
21111ed6aaf9SAlexander Motin 				    /*timeout*/0,
21121ed6aaf9SAlexander Motin 				    /*getcount_only*/0);
21131ed6aaf9SAlexander Motin 			}
21141ed6aaf9SAlexander Motin 		}
21151ed6aaf9SAlexander Motin 
21161ed6aaf9SAlexander Motin 		/*
21171ed6aaf9SAlexander Motin 		 * Since our peripheral may be invalidated by an error
21181ed6aaf9SAlexander Motin 		 * above or an external event, we must release our CCB
21191ed6aaf9SAlexander Motin 		 * before releasing the reference on the peripheral.
21201ed6aaf9SAlexander Motin 		 * The peripheral will only go away once the last reference
21211ed6aaf9SAlexander Motin 		 * is removed, and we need it around for the CCB release
21221ed6aaf9SAlexander Motin 		 * operation.
21231ed6aaf9SAlexander Motin 		 */
21241ed6aaf9SAlexander Motin 		cgd = (struct ccb_getdev *)done_ccb;
2125cccf4220SAlexander Motin 		xpt_setup_ccb(&cgd->ccb_h, path, CAM_PRIORITY_NORMAL);
21261ed6aaf9SAlexander Motin 		cgd->ccb_h.func_code = XPT_GDEV_TYPE;
21271ed6aaf9SAlexander Motin 		xpt_action((union ccb *)cgd);
21281ed6aaf9SAlexander Motin 		if (ADA_WC >= 0 &&
21291ed6aaf9SAlexander Motin 		    cgd->ident_data.support.command1 & ATA_SUPPORT_WRITECACHE) {
21301ed6aaf9SAlexander Motin 			softc->state = ADA_STATE_WCACHE;
21311ed6aaf9SAlexander Motin 			xpt_release_ccb(done_ccb);
21321ed6aaf9SAlexander Motin 			xpt_schedule(periph, CAM_PRIORITY_DEV);
2133cccf4220SAlexander Motin 			goto out;
21341ed6aaf9SAlexander Motin 		}
21351ed6aaf9SAlexander Motin 		softc->state = ADA_STATE_NORMAL;
21361ed6aaf9SAlexander Motin 		xpt_release_ccb(done_ccb);
2137cccf4220SAlexander Motin 		/* Drop freeze taken due to CAM_DEV_QFREEZE */
2138cccf4220SAlexander Motin 		cam_release_devq(path, 0, 0, 0, FALSE);
21391ed6aaf9SAlexander Motin 		adaschedule(periph);
21401ed6aaf9SAlexander Motin 		cam_periph_release_locked(periph);
21411ed6aaf9SAlexander Motin 		return;
21421ed6aaf9SAlexander Motin 	}
2143f513d14cSAlexander Motin 	case ADA_CCB_WCACHE:
2144f513d14cSAlexander Motin 	{
2145f513d14cSAlexander Motin 		if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
2146f513d14cSAlexander Motin 			if (adaerror(done_ccb, 0, 0) == ERESTART) {
2147cccf4220SAlexander Motin 				goto out;
2148f513d14cSAlexander Motin 			} else if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) {
2149cccf4220SAlexander Motin 				cam_release_devq(path,
2150f513d14cSAlexander Motin 				    /*relsim_flags*/0,
2151f513d14cSAlexander Motin 				    /*reduction*/0,
2152f513d14cSAlexander Motin 				    /*timeout*/0,
2153f513d14cSAlexander Motin 				    /*getcount_only*/0);
2154f513d14cSAlexander Motin 			}
2155f513d14cSAlexander Motin 		}
2156f513d14cSAlexander Motin 
2157f513d14cSAlexander Motin 		softc->state = ADA_STATE_NORMAL;
2158f513d14cSAlexander Motin 		/*
2159f513d14cSAlexander Motin 		 * Since our peripheral may be invalidated by an error
2160f513d14cSAlexander Motin 		 * above or an external event, we must release our CCB
2161f513d14cSAlexander Motin 		 * before releasing the reference on the peripheral.
2162f513d14cSAlexander Motin 		 * The peripheral will only go away once the last reference
2163f513d14cSAlexander Motin 		 * is removed, and we need it around for the CCB release
2164f513d14cSAlexander Motin 		 * operation.
2165f513d14cSAlexander Motin 		 */
2166f513d14cSAlexander Motin 		xpt_release_ccb(done_ccb);
2167cccf4220SAlexander Motin 		/* Drop freeze taken due to CAM_DEV_QFREEZE */
2168cccf4220SAlexander Motin 		cam_release_devq(path, 0, 0, 0, FALSE);
2169f513d14cSAlexander Motin 		adaschedule(periph);
2170f513d14cSAlexander Motin 		cam_periph_release_locked(periph);
2171f513d14cSAlexander Motin 		return;
2172f513d14cSAlexander Motin 	}
217352c9ce25SScott Long 	case ADA_CCB_DUMP:
217452c9ce25SScott Long 		/* No-op.  We're polling */
217552c9ce25SScott Long 		return;
217652c9ce25SScott Long 	default:
217752c9ce25SScott Long 		break;
217852c9ce25SScott Long 	}
217952c9ce25SScott Long 	xpt_release_ccb(done_ccb);
218052c9ce25SScott Long }
218152c9ce25SScott Long 
218252c9ce25SScott Long static int
218352c9ce25SScott Long adaerror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags)
218452c9ce25SScott Long {
2185acfc9b68SWarner Losh #ifdef CAM_IO_STATS
2186a6e0c5daSWarner Losh 	struct ada_softc *softc;
2187a6e0c5daSWarner Losh 	struct cam_periph *periph;
2188a6e0c5daSWarner Losh 
2189a6e0c5daSWarner Losh 	periph = xpt_path_periph(ccb->ccb_h.path);
2190a6e0c5daSWarner Losh 	softc = (struct ada_softc *)periph->softc;
2191a6e0c5daSWarner Losh 
2192a6e0c5daSWarner Losh 	switch (ccb->ccb_h.status & CAM_STATUS_MASK) {
2193a6e0c5daSWarner Losh 	case CAM_CMD_TIMEOUT:
2194a6e0c5daSWarner Losh 		softc->timeouts++;
2195a6e0c5daSWarner Losh 		break;
2196a6e0c5daSWarner Losh 	case CAM_REQ_ABORTED:
2197a6e0c5daSWarner Losh 	case CAM_REQ_CMP_ERR:
2198a6e0c5daSWarner Losh 	case CAM_REQ_TERMIO:
2199a6e0c5daSWarner Losh 	case CAM_UNREC_HBA_ERROR:
2200a6e0c5daSWarner Losh 	case CAM_DATA_RUN_ERR:
2201a6e0c5daSWarner Losh 	case CAM_ATA_STATUS_ERROR:
2202a6e0c5daSWarner Losh 		softc->errors++;
2203a6e0c5daSWarner Losh 		break;
2204a6e0c5daSWarner Losh 	default:
2205a6e0c5daSWarner Losh 		break;
2206a6e0c5daSWarner Losh 	}
2207acfc9b68SWarner Losh #endif
220852c9ce25SScott Long 
2209a9b8edb1SAlexander Motin 	return(cam_periph_error(ccb, cam_flags, sense_flags, NULL));
221052c9ce25SScott Long }
221152c9ce25SScott Long 
221252c9ce25SScott Long static void
2213c1bd46c2SAlexander Motin adagetparams(struct cam_periph *periph, struct ccb_getdev *cgd)
221452c9ce25SScott Long {
221552c9ce25SScott Long 	struct ada_softc *softc = (struct ada_softc *)periph->softc;
221652c9ce25SScott Long 	struct disk_params *dp = &softc->params;
221752c9ce25SScott Long 	u_int64_t lbasize48;
221852c9ce25SScott Long 	u_int32_t lbasize;
221952c9ce25SScott Long 
2220c1bd46c2SAlexander Motin 	dp->secsize = ata_logical_sector_size(&cgd->ident_data);
222152c9ce25SScott Long 	if ((cgd->ident_data.atavalid & ATA_FLAG_54_58) &&
222252c9ce25SScott Long 		cgd->ident_data.current_heads && cgd->ident_data.current_sectors) {
222352c9ce25SScott Long 		dp->heads = cgd->ident_data.current_heads;
222452c9ce25SScott Long 		dp->secs_per_track = cgd->ident_data.current_sectors;
222552c9ce25SScott Long 		dp->cylinders = cgd->ident_data.cylinders;
222652c9ce25SScott Long 		dp->sectors = (u_int32_t)cgd->ident_data.current_size_1 |
222752c9ce25SScott Long 			  ((u_int32_t)cgd->ident_data.current_size_2 << 16);
222852c9ce25SScott Long 	} else {
222952c9ce25SScott Long 		dp->heads = cgd->ident_data.heads;
223052c9ce25SScott Long 		dp->secs_per_track = cgd->ident_data.sectors;
223152c9ce25SScott Long 		dp->cylinders = cgd->ident_data.cylinders;
223252c9ce25SScott Long 		dp->sectors = cgd->ident_data.cylinders * dp->heads * dp->secs_per_track;
223352c9ce25SScott Long 	}
223452c9ce25SScott Long 	lbasize = (u_int32_t)cgd->ident_data.lba_size_1 |
223552c9ce25SScott Long 		  ((u_int32_t)cgd->ident_data.lba_size_2 << 16);
223652c9ce25SScott Long 
223752c9ce25SScott Long 	/* use the 28bit LBA size if valid or bigger than the CHS mapping */
223852c9ce25SScott Long 	if (cgd->ident_data.cylinders == 16383 || dp->sectors < lbasize)
223952c9ce25SScott Long 		dp->sectors = lbasize;
224052c9ce25SScott Long 
224152c9ce25SScott Long 	/* use the 48bit LBA size if valid */
224252c9ce25SScott Long 	lbasize48 = ((u_int64_t)cgd->ident_data.lba_size48_1) |
224352c9ce25SScott Long 		    ((u_int64_t)cgd->ident_data.lba_size48_2 << 16) |
224452c9ce25SScott Long 		    ((u_int64_t)cgd->ident_data.lba_size48_3 << 32) |
224552c9ce25SScott Long 		    ((u_int64_t)cgd->ident_data.lba_size48_4 << 48);
224652c9ce25SScott Long 	if ((cgd->ident_data.support.command2 & ATA_SUPPORT_ADDRESS48) &&
224752c9ce25SScott Long 	    lbasize48 > ATA_MAX_28BIT_LBA)
224852c9ce25SScott Long 		dp->sectors = lbasize48;
224952c9ce25SScott Long }
225052c9ce25SScott Long 
225152c9ce25SScott Long static void
225252c9ce25SScott Long adasendorderedtag(void *arg)
225352c9ce25SScott Long {
225452c9ce25SScott Long 	struct ada_softc *softc = arg;
225552c9ce25SScott Long 
225652c9ce25SScott Long 	if (ada_send_ordered) {
2257030844d1SAlexander Motin 		if (softc->outstanding_cmds > 0) {
2258030844d1SAlexander Motin 			if ((softc->flags & ADA_FLAG_WAS_OTAG) == 0)
225952c9ce25SScott Long 				softc->flags |= ADA_FLAG_NEED_OTAG;
2260030844d1SAlexander Motin 			softc->flags &= ~ADA_FLAG_WAS_OTAG;
226152c9ce25SScott Long 		}
226252c9ce25SScott Long 	}
226352c9ce25SScott Long 	/* Queue us up again */
226452c9ce25SScott Long 	callout_reset(&softc->sendordered_c,
226547bb9643SAlexander Motin 	    (ada_default_timeout * hz) / ADA_ORDEREDTAG_INTERVAL,
226652c9ce25SScott Long 	    adasendorderedtag, softc);
226752c9ce25SScott Long }
226852c9ce25SScott Long 
226952c9ce25SScott Long /*
227052c9ce25SScott Long  * Step through all ADA peripheral drivers, and if the device is still open,
227152c9ce25SScott Long  * sync the disk cache to physical media.
227252c9ce25SScott Long  */
227352c9ce25SScott Long static void
2274c3d0d168SAlexander Motin adaflush(void)
227552c9ce25SScott Long {
227652c9ce25SScott Long 	struct cam_periph *periph;
227752c9ce25SScott Long 	struct ada_softc *softc;
227809cfadbeSAlexander Motin 	union ccb *ccb;
22790191d9b3SAlexander Motin 	int error;
228052c9ce25SScott Long 
2281f371c9e2SAlexander Motin 	CAM_PERIPH_FOREACH(periph, &adadriver) {
228252c9ce25SScott Long 		softc = (struct ada_softc *)periph->softc;
22832f87dfb0SAlexander Motin 		if (SCHEDULER_STOPPED()) {
22842f87dfb0SAlexander Motin 			/* If we paniced with the lock held, do not recurse. */
22852f87dfb0SAlexander Motin 			if (!cam_periph_owned(periph) &&
22862f87dfb0SAlexander Motin 			    (softc->flags & ADA_FLAG_OPEN)) {
22872f87dfb0SAlexander Motin 				adadump(softc->disk, NULL, 0, 0, 0);
22882f87dfb0SAlexander Motin 			}
22892f87dfb0SAlexander Motin 			continue;
22902f87dfb0SAlexander Motin 		}
22912f87dfb0SAlexander Motin 		cam_periph_lock(periph);
229252c9ce25SScott Long 		/*
229352c9ce25SScott Long 		 * We only sync the cache if the drive is still open, and
229452c9ce25SScott Long 		 * if the drive is capable of it..
229552c9ce25SScott Long 		 */
229652c9ce25SScott Long 		if (((softc->flags & ADA_FLAG_OPEN) == 0) ||
229752c9ce25SScott Long 		    (softc->flags & ADA_FLAG_CAN_FLUSHCACHE) == 0) {
229852c9ce25SScott Long 			cam_periph_unlock(periph);
229952c9ce25SScott Long 			continue;
230052c9ce25SScott Long 		}
230152c9ce25SScott Long 
230209cfadbeSAlexander Motin 		ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL);
230309cfadbeSAlexander Motin 		cam_fill_ataio(&ccb->ataio,
23040191d9b3SAlexander Motin 				    0,
230552c9ce25SScott Long 				    adadone,
230652c9ce25SScott Long 				    CAM_DIR_NONE,
230752c9ce25SScott Long 				    0,
230852c9ce25SScott Long 				    NULL,
230952c9ce25SScott Long 				    0,
231052c9ce25SScott Long 				    ada_default_timeout*1000);
231152c9ce25SScott Long 		if (softc->flags & ADA_FLAG_CAN_48BIT)
231209cfadbeSAlexander Motin 			ata_48bit_cmd(&ccb->ataio, ATA_FLUSHCACHE48, 0, 0, 0);
231352c9ce25SScott Long 		else
231409cfadbeSAlexander Motin 			ata_28bit_cmd(&ccb->ataio, ATA_FLUSHCACHE, 0, 0, 0);
231552c9ce25SScott Long 
231609cfadbeSAlexander Motin 		error = cam_periph_runccb(ccb, adaerror, /*cam_flags*/0,
231709cfadbeSAlexander Motin 		    /*sense_flags*/ SF_NO_RECOVERY | SF_NO_RETRY,
231809cfadbeSAlexander Motin 		    softc->disk->d_devstat);
23190191d9b3SAlexander Motin 		if (error != 0)
23200191d9b3SAlexander Motin 			xpt_print(periph->path, "Synchronize cache failed\n");
2321d6794b70SAlexander Motin 		xpt_release_ccb(ccb);
232252c9ce25SScott Long 		cam_periph_unlock(periph);
232352c9ce25SScott Long 	}
2324c3d0d168SAlexander Motin }
2325fd104c15SRebecca Cran 
2326c3d0d168SAlexander Motin static void
2327c3d0d168SAlexander Motin adaspindown(uint8_t cmd, int flags)
2328c3d0d168SAlexander Motin {
2329c3d0d168SAlexander Motin 	struct cam_periph *periph;
2330c3d0d168SAlexander Motin 	struct ada_softc *softc;
233109cfadbeSAlexander Motin 	union ccb *ccb;
23320191d9b3SAlexander Motin 	int error;
2333fd104c15SRebecca Cran 
2334f371c9e2SAlexander Motin 	CAM_PERIPH_FOREACH(periph, &adadriver) {
2335fd104c15SRebecca Cran 		/* If we paniced with lock held - not recurse here. */
2336fd104c15SRebecca Cran 		if (cam_periph_owned(periph))
2337fd104c15SRebecca Cran 			continue;
2338fd104c15SRebecca Cran 		cam_periph_lock(periph);
2339fd104c15SRebecca Cran 		softc = (struct ada_softc *)periph->softc;
2340fd104c15SRebecca Cran 		/*
2341fd104c15SRebecca Cran 		 * We only spin-down the drive if it is capable of it..
2342fd104c15SRebecca Cran 		 */
2343fd104c15SRebecca Cran 		if ((softc->flags & ADA_FLAG_CAN_POWERMGT) == 0) {
2344fd104c15SRebecca Cran 			cam_periph_unlock(periph);
2345fd104c15SRebecca Cran 			continue;
2346fd104c15SRebecca Cran 		}
2347fd104c15SRebecca Cran 
2348fd104c15SRebecca Cran 		if (bootverbose)
2349fd104c15SRebecca Cran 			xpt_print(periph->path, "spin-down\n");
2350fd104c15SRebecca Cran 
235109cfadbeSAlexander Motin 		ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL);
235209cfadbeSAlexander Motin 		cam_fill_ataio(&ccb->ataio,
23530191d9b3SAlexander Motin 				    0,
2354fd104c15SRebecca Cran 				    adadone,
2355c3d0d168SAlexander Motin 				    CAM_DIR_NONE | flags,
2356fd104c15SRebecca Cran 				    0,
2357fd104c15SRebecca Cran 				    NULL,
2358fd104c15SRebecca Cran 				    0,
2359fd104c15SRebecca Cran 				    ada_default_timeout*1000);
236009cfadbeSAlexander Motin 		ata_28bit_cmd(&ccb->ataio, cmd, 0, 0, 0);
2361fd104c15SRebecca Cran 
236209cfadbeSAlexander Motin 		error = cam_periph_runccb(ccb, adaerror, /*cam_flags*/0,
236309cfadbeSAlexander Motin 		    /*sense_flags*/ SF_NO_RECOVERY | SF_NO_RETRY,
236409cfadbeSAlexander Motin 		    softc->disk->d_devstat);
23650191d9b3SAlexander Motin 		if (error != 0)
23660191d9b3SAlexander Motin 			xpt_print(periph->path, "Spin-down disk failed\n");
2367d6794b70SAlexander Motin 		xpt_release_ccb(ccb);
2368fd104c15SRebecca Cran 		cam_periph_unlock(periph);
2369fd104c15SRebecca Cran 	}
237052c9ce25SScott Long }
237152c9ce25SScott Long 
2372c3d0d168SAlexander Motin static void
2373c3d0d168SAlexander Motin adashutdown(void *arg, int howto)
2374c3d0d168SAlexander Motin {
2375c3d0d168SAlexander Motin 
2376c3d0d168SAlexander Motin 	adaflush();
2377c3d0d168SAlexander Motin 	if (ada_spindown_shutdown != 0 &&
2378c3d0d168SAlexander Motin 	    (howto & (RB_HALT | RB_POWEROFF)) != 0)
2379c3d0d168SAlexander Motin 		adaspindown(ATA_STANDBY_IMMEDIATE, 0);
2380c3d0d168SAlexander Motin }
2381c3d0d168SAlexander Motin 
2382c3d0d168SAlexander Motin static void
2383c3d0d168SAlexander Motin adasuspend(void *arg)
2384c3d0d168SAlexander Motin {
2385c3d0d168SAlexander Motin 
2386c3d0d168SAlexander Motin 	adaflush();
2387c3d0d168SAlexander Motin 	if (ada_spindown_suspend != 0)
2388c3d0d168SAlexander Motin 		adaspindown(ATA_SLEEP, CAM_DEV_QFREEZE);
2389c3d0d168SAlexander Motin }
2390c3d0d168SAlexander Motin 
2391c3d0d168SAlexander Motin static void
2392c3d0d168SAlexander Motin adaresume(void *arg)
2393c3d0d168SAlexander Motin {
2394c3d0d168SAlexander Motin 	struct cam_periph *periph;
2395c3d0d168SAlexander Motin 	struct ada_softc *softc;
2396c3d0d168SAlexander Motin 
2397c3d0d168SAlexander Motin 	if (ada_spindown_suspend == 0)
2398c3d0d168SAlexander Motin 		return;
2399c3d0d168SAlexander Motin 
2400f371c9e2SAlexander Motin 	CAM_PERIPH_FOREACH(periph, &adadriver) {
2401c3d0d168SAlexander Motin 		cam_periph_lock(periph);
2402c3d0d168SAlexander Motin 		softc = (struct ada_softc *)periph->softc;
2403c3d0d168SAlexander Motin 		/*
2404c3d0d168SAlexander Motin 		 * We only spin-down the drive if it is capable of it..
2405c3d0d168SAlexander Motin 		 */
2406c3d0d168SAlexander Motin 		if ((softc->flags & ADA_FLAG_CAN_POWERMGT) == 0) {
2407c3d0d168SAlexander Motin 			cam_periph_unlock(periph);
2408c3d0d168SAlexander Motin 			continue;
2409c3d0d168SAlexander Motin 		}
2410c3d0d168SAlexander Motin 
2411c3d0d168SAlexander Motin 		if (bootverbose)
2412c3d0d168SAlexander Motin 			xpt_print(periph->path, "resume\n");
2413c3d0d168SAlexander Motin 
2414c3d0d168SAlexander Motin 		/*
2415c3d0d168SAlexander Motin 		 * Drop freeze taken due to CAM_DEV_QFREEZE flag set on
2416c3d0d168SAlexander Motin 		 * sleep request.
2417c3d0d168SAlexander Motin 		 */
2418c3d0d168SAlexander Motin 		cam_release_devq(periph->path,
2419c3d0d168SAlexander Motin 			 /*relsim_flags*/0,
2420c3d0d168SAlexander Motin 			 /*openings*/0,
2421c3d0d168SAlexander Motin 			 /*timeout*/0,
2422c3d0d168SAlexander Motin 			 /*getcount_only*/0);
2423c3d0d168SAlexander Motin 
2424c3d0d168SAlexander Motin 		cam_periph_unlock(periph);
2425c3d0d168SAlexander Motin 	}
2426c3d0d168SAlexander Motin }
2427c3d0d168SAlexander Motin 
242852c9ce25SScott Long #endif /* _KERNEL */
2429