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