152c9ce25SScott Long /*- 2bec9534dSPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3bec9534dSPedro F. Giffuni * 452c9ce25SScott Long * Copyright (c) 2009 Alexander Motin <mav@FreeBSD.org> 552c9ce25SScott Long * All rights reserved. 652c9ce25SScott Long * 752c9ce25SScott Long * Redistribution and use in source and binary forms, with or without 852c9ce25SScott Long * modification, are permitted provided that the following conditions 952c9ce25SScott Long * are met: 1052c9ce25SScott Long * 1. Redistributions of source code must retain the above copyright 1152c9ce25SScott Long * notice, this list of conditions and the following disclaimer, 1252c9ce25SScott Long * without modification, immediately at the beginning of the file. 1352c9ce25SScott Long * 2. Redistributions in binary form must reproduce the above copyright 1452c9ce25SScott Long * notice, this list of conditions and the following disclaimer in the 1552c9ce25SScott Long * documentation and/or other materials provided with the distribution. 1652c9ce25SScott Long * 1752c9ce25SScott Long * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1852c9ce25SScott Long * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1952c9ce25SScott Long * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2052c9ce25SScott Long * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2152c9ce25SScott Long * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2252c9ce25SScott Long * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2352c9ce25SScott Long * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2452c9ce25SScott Long * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2552c9ce25SScott Long * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2652c9ce25SScott Long * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2752c9ce25SScott Long */ 2852c9ce25SScott Long 2952c9ce25SScott Long #include <sys/cdefs.h> 3052c9ce25SScott Long __FBSDID("$FreeBSD$"); 3152c9ce25SScott Long 32e3a6d3a4SAlexander Motin #include "opt_ada.h" 33e3a6d3a4SAlexander Motin 3452c9ce25SScott Long #include <sys/param.h> 3552c9ce25SScott Long 3652c9ce25SScott Long #ifdef _KERNEL 3752c9ce25SScott Long #include <sys/systm.h> 3852c9ce25SScott Long #include <sys/kernel.h> 3952c9ce25SScott Long #include <sys/bio.h> 4052c9ce25SScott Long #include <sys/sysctl.h> 4152c9ce25SScott Long #include <sys/taskqueue.h> 4252c9ce25SScott Long #include <sys/lock.h> 4352c9ce25SScott Long #include <sys/mutex.h> 4452c9ce25SScott Long #include <sys/conf.h> 4552c9ce25SScott Long #include <sys/devicestat.h> 4652c9ce25SScott Long #include <sys/eventhandler.h> 4752c9ce25SScott Long #include <sys/malloc.h> 489a6844d5SKenneth D. Merry #include <sys/endian.h> 4952c9ce25SScott Long #include <sys/cons.h> 502f87dfb0SAlexander Motin #include <sys/proc.h> 51fd104c15SRebecca Cran #include <sys/reboot.h> 529a6844d5SKenneth D. Merry #include <sys/sbuf.h> 5313532153SScott Long #include <geom/geom.h> 5452c9ce25SScott Long #include <geom/geom_disk.h> 5552c9ce25SScott Long #endif /* _KERNEL */ 5652c9ce25SScott Long 5752c9ce25SScott Long #ifndef _KERNEL 5852c9ce25SScott Long #include <stdio.h> 5952c9ce25SScott Long #include <string.h> 6052c9ce25SScott Long #endif /* _KERNEL */ 6152c9ce25SScott Long 6252c9ce25SScott Long #include <cam/cam.h> 6352c9ce25SScott Long #include <cam/cam_ccb.h> 6452c9ce25SScott Long #include <cam/cam_periph.h> 6552c9ce25SScott Long #include <cam/cam_xpt_periph.h> 669a6844d5SKenneth D. Merry #include <cam/scsi/scsi_all.h> 679a6844d5SKenneth D. Merry #include <cam/scsi/scsi_da.h> 6852c9ce25SScott Long #include <cam/cam_sim.h> 69a6e0c5daSWarner Losh #include <cam/cam_iosched.h> 7052c9ce25SScott Long 7152c9ce25SScott Long #include <cam/ata/ata_all.h> 7252c9ce25SScott Long 7352c9ce25SScott Long #ifdef _KERNEL 7452c9ce25SScott Long 7552c9ce25SScott Long #define ATA_MAX_28BIT_LBA 268435455UL 7652c9ce25SScott Long 77a6e0c5daSWarner Losh extern int iosched_debug; 78a6e0c5daSWarner Losh 7952c9ce25SScott Long typedef enum { 801ed6aaf9SAlexander Motin ADA_STATE_RAHEAD, 81f513d14cSAlexander Motin ADA_STATE_WCACHE, 829a6844d5SKenneth D. Merry ADA_STATE_LOGDIR, 839a6844d5SKenneth D. Merry ADA_STATE_IDDIR, 849a6844d5SKenneth D. Merry ADA_STATE_SUP_CAP, 859a6844d5SKenneth D. Merry ADA_STATE_ZONE, 861e637ba6SAlexander Motin ADA_STATE_NORMAL 8752c9ce25SScott Long } ada_state; 8852c9ce25SScott Long 8952c9ce25SScott Long typedef enum { 909a6844d5SKenneth D. Merry ADA_FLAG_CAN_48BIT = 0x00000002, 919a6844d5SKenneth D. Merry ADA_FLAG_CAN_FLUSHCACHE = 0x00000004, 929a6844d5SKenneth D. Merry ADA_FLAG_CAN_NCQ = 0x00000008, 939a6844d5SKenneth D. Merry ADA_FLAG_CAN_DMA = 0x00000010, 949a6844d5SKenneth D. Merry ADA_FLAG_NEED_OTAG = 0x00000020, 959a6844d5SKenneth D. Merry ADA_FLAG_WAS_OTAG = 0x00000040, 969a6844d5SKenneth D. Merry ADA_FLAG_CAN_TRIM = 0x00000080, 979a6844d5SKenneth D. Merry ADA_FLAG_OPEN = 0x00000100, 989a6844d5SKenneth D. Merry ADA_FLAG_SCTX_INIT = 0x00000200, 999a6844d5SKenneth D. Merry ADA_FLAG_CAN_CFA = 0x00000400, 1009a6844d5SKenneth D. Merry ADA_FLAG_CAN_POWERMGT = 0x00000800, 1019a6844d5SKenneth D. Merry ADA_FLAG_CAN_DMA48 = 0x00001000, 1029a6844d5SKenneth D. Merry ADA_FLAG_CAN_LOG = 0x00002000, 1039a6844d5SKenneth D. Merry ADA_FLAG_CAN_IDLOG = 0x00004000, 1049a6844d5SKenneth D. Merry ADA_FLAG_CAN_SUPCAP = 0x00008000, 1059a6844d5SKenneth D. Merry ADA_FLAG_CAN_ZONE = 0x00010000, 1069a6844d5SKenneth D. Merry ADA_FLAG_CAN_WCACHE = 0x00020000, 1079a6844d5SKenneth D. Merry ADA_FLAG_CAN_RAHEAD = 0x00040000, 1089a6844d5SKenneth D. Merry ADA_FLAG_PROBED = 0x00080000, 1099a6844d5SKenneth D. Merry ADA_FLAG_ANNOUNCED = 0x00100000, 1109a6844d5SKenneth D. Merry ADA_FLAG_DIRTY = 0x00200000, 1119a6844d5SKenneth D. Merry ADA_FLAG_CAN_NCQ_TRIM = 0x00400000, /* CAN_TRIM also set */ 112cf3ff63eSWarner Losh ADA_FLAG_PIM_ATA_EXT = 0x00800000, 11396eb32bfSWarner Losh ADA_FLAG_UNMAPPEDIO = 0x01000000, 11496eb32bfSWarner Losh ADA_FLAG_ROTATING = 0x02000000 11552c9ce25SScott Long } ada_flags; 116aeab0812SWarner Losh #define ADA_FLAG_STRING \ 117aeab0812SWarner Losh "\020" \ 118aeab0812SWarner Losh "\002CAN_48BIT" \ 119aeab0812SWarner Losh "\003CAN_FLUSHCACHE" \ 120aeab0812SWarner Losh "\004CAN_NCQ" \ 121aeab0812SWarner Losh "\005CAN_DMA" \ 122aeab0812SWarner Losh "\006NEED_OTAG" \ 123aeab0812SWarner Losh "\007WAS_OTAG" \ 124aeab0812SWarner Losh "\010CAN_TRIM" \ 125aeab0812SWarner Losh "\011OPEN" \ 126aeab0812SWarner Losh "\012SCTX_INIT" \ 127aeab0812SWarner Losh "\013CAN_CFA" \ 128aeab0812SWarner Losh "\014CAN_POWERMGT" \ 129aeab0812SWarner Losh "\015CAN_DMA48" \ 130aeab0812SWarner Losh "\016CAN_LOG" \ 131aeab0812SWarner Losh "\017CAN_IDLOG" \ 132aeab0812SWarner Losh "\020CAN_SUPCAP" \ 133aeab0812SWarner Losh "\021CAN_ZONE" \ 134aeab0812SWarner Losh "\022CAN_WCACHE" \ 135aeab0812SWarner Losh "\023CAN_RAHEAD" \ 136aeab0812SWarner Losh "\024PROBED" \ 137aeab0812SWarner Losh "\025ANNOUNCED" \ 138aeab0812SWarner Losh "\026DIRTY" \ 139aeab0812SWarner Losh "\027CAN_NCQ_TRIM" \ 140cf3ff63eSWarner Losh "\030PIM_ATA_EXT" \ 14196eb32bfSWarner Losh "\031UNMAPPEDIO" \ 14296eb32bfSWarner Losh "\032ROTATING" 14352c9ce25SScott Long 14452c9ce25SScott Long typedef enum { 145d3a460d3SAlexander Motin ADA_Q_NONE = 0x00, 146d3a460d3SAlexander Motin ADA_Q_4K = 0x01, 147a6e0c5daSWarner Losh ADA_Q_NCQ_TRIM_BROKEN = 0x02, 148600fd98fSKenneth D. Merry ADA_Q_LOG_BROKEN = 0x04, 149fb81f266SOleksandr Tymoshenko ADA_Q_SMR_DM = 0x08, 150a49077d3SWarner Losh ADA_Q_NO_TRIM = 0x10, 151a49077d3SWarner Losh ADA_Q_128KB = 0x20 15252c9ce25SScott Long } ada_quirks; 15352c9ce25SScott Long 1546fb5c84eSSteven Hartland #define ADA_Q_BIT_STRING \ 1556fb5c84eSSteven Hartland "\020" \ 156a6e0c5daSWarner Losh "\0014K" \ 157600fd98fSKenneth D. Merry "\002NCQ_TRIM_BROKEN" \ 158600fd98fSKenneth D. Merry "\003LOG_BROKEN" \ 159fb81f266SOleksandr Tymoshenko "\004SMR_DM" \ 160a49077d3SWarner Losh "\005NO_TRIM" \ 161a49077d3SWarner Losh "\006128KB" 1626fb5c84eSSteven Hartland 16352c9ce25SScott Long typedef enum { 1641ed6aaf9SAlexander Motin ADA_CCB_RAHEAD = 0x01, 1651ed6aaf9SAlexander Motin ADA_CCB_WCACHE = 0x02, 16652c9ce25SScott Long ADA_CCB_BUFFER_IO = 0x03, 16752c9ce25SScott Long ADA_CCB_DUMP = 0x05, 1681c80ec0aSAlexander Motin ADA_CCB_TRIM = 0x06, 1699a6844d5SKenneth D. Merry ADA_CCB_LOGDIR = 0x07, 1709a6844d5SKenneth D. Merry ADA_CCB_IDDIR = 0x08, 1719a6844d5SKenneth D. Merry ADA_CCB_SUP_CAP = 0x09, 1729a6844d5SKenneth D. Merry ADA_CCB_ZONE = 0x0a, 17352c9ce25SScott Long ADA_CCB_TYPE_MASK = 0x0F, 17452c9ce25SScott Long } ada_ccb_state; 17552c9ce25SScott Long 1769a6844d5SKenneth D. Merry typedef enum { 1779a6844d5SKenneth D. Merry ADA_ZONE_NONE = 0x00, 1789a6844d5SKenneth D. Merry ADA_ZONE_DRIVE_MANAGED = 0x01, 1799a6844d5SKenneth D. Merry ADA_ZONE_HOST_AWARE = 0x02, 1809a6844d5SKenneth D. Merry ADA_ZONE_HOST_MANAGED = 0x03 1819a6844d5SKenneth D. Merry } ada_zone_mode; 1829a6844d5SKenneth D. Merry 1839a6844d5SKenneth D. Merry typedef enum { 1849a6844d5SKenneth D. Merry ADA_ZONE_FLAG_RZ_SUP = 0x0001, 1859a6844d5SKenneth D. Merry ADA_ZONE_FLAG_OPEN_SUP = 0x0002, 1869a6844d5SKenneth D. Merry ADA_ZONE_FLAG_CLOSE_SUP = 0x0004, 1879a6844d5SKenneth D. Merry ADA_ZONE_FLAG_FINISH_SUP = 0x0008, 1889a6844d5SKenneth D. Merry ADA_ZONE_FLAG_RWP_SUP = 0x0010, 1899a6844d5SKenneth D. Merry ADA_ZONE_FLAG_SUP_MASK = (ADA_ZONE_FLAG_RZ_SUP | 1909a6844d5SKenneth D. Merry ADA_ZONE_FLAG_OPEN_SUP | 1919a6844d5SKenneth D. Merry ADA_ZONE_FLAG_CLOSE_SUP | 1929a6844d5SKenneth D. Merry ADA_ZONE_FLAG_FINISH_SUP | 1939a6844d5SKenneth D. Merry ADA_ZONE_FLAG_RWP_SUP), 1949a6844d5SKenneth D. Merry ADA_ZONE_FLAG_URSWRZ = 0x0020, 1959a6844d5SKenneth D. Merry ADA_ZONE_FLAG_OPT_SEQ_SET = 0x0040, 1969a6844d5SKenneth D. Merry ADA_ZONE_FLAG_OPT_NONSEQ_SET = 0x0080, 1979a6844d5SKenneth D. Merry ADA_ZONE_FLAG_MAX_SEQ_SET = 0x0100, 1989a6844d5SKenneth D. Merry ADA_ZONE_FLAG_SET_MASK = (ADA_ZONE_FLAG_OPT_SEQ_SET | 1999a6844d5SKenneth D. Merry ADA_ZONE_FLAG_OPT_NONSEQ_SET | 2009a6844d5SKenneth D. Merry ADA_ZONE_FLAG_MAX_SEQ_SET) 2019a6844d5SKenneth D. Merry } ada_zone_flags; 2029a6844d5SKenneth D. Merry 2039a6844d5SKenneth D. Merry static struct ada_zone_desc { 2049a6844d5SKenneth D. Merry ada_zone_flags value; 2059a6844d5SKenneth D. Merry const char *desc; 2069a6844d5SKenneth D. Merry } ada_zone_desc_table[] = { 2079a6844d5SKenneth D. Merry {ADA_ZONE_FLAG_RZ_SUP, "Report Zones" }, 2089a6844d5SKenneth D. Merry {ADA_ZONE_FLAG_OPEN_SUP, "Open" }, 2099a6844d5SKenneth D. Merry {ADA_ZONE_FLAG_CLOSE_SUP, "Close" }, 2109a6844d5SKenneth D. Merry {ADA_ZONE_FLAG_FINISH_SUP, "Finish" }, 2119a6844d5SKenneth D. Merry {ADA_ZONE_FLAG_RWP_SUP, "Reset Write Pointer" }, 2129a6844d5SKenneth D. Merry }; 2139a6844d5SKenneth D. Merry 21452c9ce25SScott Long /* Offsets into our private area for storing information */ 21552c9ce25SScott Long #define ccb_state ppriv_field0 21652c9ce25SScott Long #define ccb_bp ppriv_ptr1 21752c9ce25SScott Long 218a6e0c5daSWarner Losh typedef enum { 219a6e0c5daSWarner Losh ADA_DELETE_NONE, 220a6e0c5daSWarner Losh ADA_DELETE_DISABLE, 221a6e0c5daSWarner Losh ADA_DELETE_CFA_ERASE, 222a6e0c5daSWarner Losh ADA_DELETE_DSM_TRIM, 223a6e0c5daSWarner Losh ADA_DELETE_NCQ_DSM_TRIM, 224a6e0c5daSWarner Losh ADA_DELETE_MIN = ADA_DELETE_CFA_ERASE, 225a6e0c5daSWarner Losh ADA_DELETE_MAX = ADA_DELETE_NCQ_DSM_TRIM, 226a6e0c5daSWarner Losh } ada_delete_methods; 227a6e0c5daSWarner Losh 228a6e0c5daSWarner Losh static const char *ada_delete_method_names[] = 229a6e0c5daSWarner Losh { "NONE", "DISABLE", "CFA_ERASE", "DSM_TRIM", "NCQ_DSM_TRIM" }; 230a6e0c5daSWarner Losh #if 0 231a6e0c5daSWarner Losh static const char *ada_delete_method_desc[] = 232a6e0c5daSWarner Losh { "NONE", "DISABLED", "CFA Erase", "DSM Trim", "DSM Trim via NCQ" }; 233a6e0c5daSWarner Losh #endif 234a6e0c5daSWarner Losh 23552c9ce25SScott Long struct disk_params { 23652c9ce25SScott Long u_int8_t heads; 23752c9ce25SScott Long u_int8_t secs_per_track; 238c1bd46c2SAlexander Motin u_int32_t cylinders; 239c1bd46c2SAlexander Motin u_int32_t secsize; /* Number of bytes/logical sector */ 240c1bd46c2SAlexander Motin u_int64_t sectors; /* Total number sectors */ 24152c9ce25SScott Long }; 24252c9ce25SScott Long 2431524677aSAlexander Motin #define TRIM_MAX_BLOCKS 8 244c213c551SSteven Hartland #define TRIM_MAX_RANGES (TRIM_MAX_BLOCKS * ATA_DSM_BLK_RANGES) 2451c80ec0aSAlexander Motin struct trim_request { 246c213c551SSteven Hartland uint8_t data[TRIM_MAX_RANGES * ATA_DSM_RANGE_SIZE]; 2472030b294SAlexander Motin TAILQ_HEAD(, bio) bps; 2481c80ec0aSAlexander Motin }; 2491c80ec0aSAlexander Motin 25052c9ce25SScott Long struct ada_softc { 251a6e0c5daSWarner Losh struct cam_iosched_softc *cam_iosched; 252030844d1SAlexander Motin int outstanding_cmds; /* Number of active commands */ 253030844d1SAlexander Motin int refcount; /* Active xpt_action() calls */ 25452c9ce25SScott Long ada_state state; 25552c9ce25SScott Long ada_flags flags; 2569a6844d5SKenneth D. Merry ada_zone_mode zone_mode; 2579a6844d5SKenneth D. Merry ada_zone_flags zone_flags; 2589a6844d5SKenneth D. Merry struct ata_gp_log_dir ata_logdir; 2599a6844d5SKenneth D. Merry int valid_logdir_len; 2609a6844d5SKenneth D. Merry struct ata_identify_log_pages ata_iddir; 2619a6844d5SKenneth D. Merry int valid_iddir_len; 2629a6844d5SKenneth D. Merry uint64_t optimal_seq_zones; 2639a6844d5SKenneth D. Merry uint64_t optimal_nonseq_zones; 2649a6844d5SKenneth D. Merry uint64_t max_seq_zones; 26552c9ce25SScott Long ada_quirks quirks; 266a6e0c5daSWarner Losh ada_delete_methods delete_method; 2671c80ec0aSAlexander Motin int trim_max_ranges; 2681ed6aaf9SAlexander Motin int read_ahead; 269e3a6d3a4SAlexander Motin int write_cache; 270d38677d2SWarner Losh #ifdef CAM_TEST_FAILURE 271e3a6d3a4SAlexander Motin int force_read_error; 272e3a6d3a4SAlexander Motin int force_write_error; 273e3a6d3a4SAlexander Motin int periodic_read_error; 274e3a6d3a4SAlexander Motin int periodic_read_count; 275e3a6d3a4SAlexander Motin #endif 27676d843daSAlexander Motin struct ccb_pathinq cpi; 27752c9ce25SScott Long struct disk_params params; 27852c9ce25SScott Long struct disk *disk; 27952c9ce25SScott Long struct task sysctl_task; 28052c9ce25SScott Long struct sysctl_ctx_list sysctl_ctx; 28152c9ce25SScott Long struct sysctl_oid *sysctl_tree; 28252c9ce25SScott Long struct callout sendordered_c; 2831c80ec0aSAlexander Motin struct trim_request trim_req; 284ea657f2cSWarner Losh uint64_t trim_count; 285ea657f2cSWarner Losh uint64_t trim_ranges; 286ea657f2cSWarner Losh uint64_t trim_lbas; 287a6e0c5daSWarner Losh #ifdef CAM_IO_STATS 288a6e0c5daSWarner Losh struct sysctl_ctx_list sysctl_stats_ctx; 289a6e0c5daSWarner Losh struct sysctl_oid *sysctl_stats_tree; 290a6e0c5daSWarner Losh u_int timeouts; 291a6e0c5daSWarner Losh u_int errors; 292a6e0c5daSWarner Losh u_int invalidations; 293a6e0c5daSWarner Losh #endif 2945d01277fSScott Long #define ADA_ANNOUNCETMP_SZ 80 2955d01277fSScott Long char announce_temp[ADA_ANNOUNCETMP_SZ]; 2965d01277fSScott Long #define ADA_ANNOUNCE_SZ 400 2975d01277fSScott Long char announce_buffer[ADA_ANNOUNCE_SZ]; 29852c9ce25SScott Long }; 29952c9ce25SScott Long 3003394d423SEdward Tomasz Napierala static uma_zone_t ada_ccb_zone; 3013394d423SEdward Tomasz Napierala 30252c9ce25SScott Long struct ada_quirk_entry { 30352c9ce25SScott Long struct scsi_inquiry_pattern inq_pat; 30452c9ce25SScott Long ada_quirks quirks; 30552c9ce25SScott Long }; 30652c9ce25SScott Long 30730a4094fSAlexander Motin static struct ada_quirk_entry ada_quirk_table[] = 30830a4094fSAlexander Motin { 30930a4094fSAlexander Motin { 310a49077d3SWarner Losh /* Sandisk X400 */ 311a49077d3SWarner Losh { T_DIRECT, SIP_MEDIA_FIXED, "*", "SanDisk?SD8SB8U1T00*", "X4162000*" }, 312a49077d3SWarner Losh /*quirks*/ADA_Q_128KB 313a49077d3SWarner Losh }, 314a49077d3SWarner Losh { 315d3a460d3SAlexander Motin /* Hitachi Advanced Format (4k) drives */ 316d3a460d3SAlexander Motin { T_DIRECT, SIP_MEDIA_FIXED, "*", "Hitachi H??????????E3*", "*" }, 317d3a460d3SAlexander Motin /*quirks*/ADA_Q_4K 318d3a460d3SAlexander Motin }, 319d3a460d3SAlexander Motin { 320d3a460d3SAlexander Motin /* Samsung Advanced Format (4k) drives */ 321643d1826SAlexander Motin { T_DIRECT, SIP_MEDIA_FIXED, "*", "SAMSUNG HD155UI*", "*" }, 322643d1826SAlexander Motin /*quirks*/ADA_Q_4K 323643d1826SAlexander Motin }, 324643d1826SAlexander Motin { 325643d1826SAlexander Motin /* Samsung Advanced Format (4k) drives */ 326d3a460d3SAlexander Motin { T_DIRECT, SIP_MEDIA_FIXED, "*", "SAMSUNG HD204UI*", "*" }, 327d3a460d3SAlexander Motin /*quirks*/ADA_Q_4K 328d3a460d3SAlexander Motin }, 329d3a460d3SAlexander Motin { 330d3a460d3SAlexander Motin /* Seagate Barracuda Green Advanced Format (4k) drives */ 331d3a460d3SAlexander Motin { T_DIRECT, SIP_MEDIA_FIXED, "*", "ST????DL*", "*" }, 332d3a460d3SAlexander Motin /*quirks*/ADA_Q_4K 333d3a460d3SAlexander Motin }, 334d3a460d3SAlexander Motin { 335643d1826SAlexander Motin /* Seagate Barracuda Advanced Format (4k) drives */ 336643d1826SAlexander Motin { T_DIRECT, SIP_MEDIA_FIXED, "*", "ST???DM*", "*" }, 337643d1826SAlexander Motin /*quirks*/ADA_Q_4K 338643d1826SAlexander Motin }, 339643d1826SAlexander Motin { 340643d1826SAlexander Motin /* Seagate Barracuda Advanced Format (4k) drives */ 341643d1826SAlexander Motin { T_DIRECT, SIP_MEDIA_FIXED, "*", "ST????DM*", "*" }, 342643d1826SAlexander Motin /*quirks*/ADA_Q_4K 343643d1826SAlexander Motin }, 344643d1826SAlexander Motin { 345d3a460d3SAlexander Motin /* Seagate Momentus Advanced Format (4k) drives */ 346d3a460d3SAlexander Motin { T_DIRECT, SIP_MEDIA_FIXED, "*", "ST9500423AS*", "*" }, 347d3a460d3SAlexander Motin /*quirks*/ADA_Q_4K 348d3a460d3SAlexander Motin }, 349d3a460d3SAlexander Motin { 350d3a460d3SAlexander Motin /* Seagate Momentus Advanced Format (4k) drives */ 351d3a460d3SAlexander Motin { T_DIRECT, SIP_MEDIA_FIXED, "*", "ST9500424AS*", "*" }, 352d3a460d3SAlexander Motin /*quirks*/ADA_Q_4K 353d3a460d3SAlexander Motin }, 354d3a460d3SAlexander Motin { 355d3a460d3SAlexander Motin /* Seagate Momentus Advanced Format (4k) drives */ 356643d1826SAlexander Motin { T_DIRECT, SIP_MEDIA_FIXED, "*", "ST9640423AS*", "*" }, 357643d1826SAlexander Motin /*quirks*/ADA_Q_4K 358643d1826SAlexander Motin }, 359643d1826SAlexander Motin { 360643d1826SAlexander Motin /* Seagate Momentus Advanced Format (4k) drives */ 361643d1826SAlexander Motin { T_DIRECT, SIP_MEDIA_FIXED, "*", "ST9640424AS*", "*" }, 362643d1826SAlexander Motin /*quirks*/ADA_Q_4K 363643d1826SAlexander Motin }, 364643d1826SAlexander Motin { 365643d1826SAlexander Motin /* Seagate Momentus Advanced Format (4k) drives */ 366d3a460d3SAlexander Motin { T_DIRECT, SIP_MEDIA_FIXED, "*", "ST9750420AS*", "*" }, 367d3a460d3SAlexander Motin /*quirks*/ADA_Q_4K 368d3a460d3SAlexander Motin }, 369d3a460d3SAlexander Motin { 370d3a460d3SAlexander Motin /* Seagate Momentus Advanced Format (4k) drives */ 371d3a460d3SAlexander Motin { T_DIRECT, SIP_MEDIA_FIXED, "*", "ST9750422AS*", "*" }, 372d3a460d3SAlexander Motin /*quirks*/ADA_Q_4K 373d3a460d3SAlexander Motin }, 374d3a460d3SAlexander Motin { 375643d1826SAlexander Motin /* Seagate Momentus Advanced Format (4k) drives */ 376643d1826SAlexander Motin { T_DIRECT, SIP_MEDIA_FIXED, "*", "ST9750423AS*", "*" }, 377643d1826SAlexander Motin /*quirks*/ADA_Q_4K 378643d1826SAlexander Motin }, 379643d1826SAlexander Motin { 380d3a460d3SAlexander Motin /* Seagate Momentus Thin Advanced Format (4k) drives */ 381d3a460d3SAlexander Motin { T_DIRECT, SIP_MEDIA_FIXED, "*", "ST???LT*", "*" }, 382d3a460d3SAlexander Motin /*quirks*/ADA_Q_4K 383d3a460d3SAlexander Motin }, 384d3a460d3SAlexander Motin { 3859073a96aSEitan Adler /* WDC Caviar Red Advanced Format (4k) drives */ 3869073a96aSEitan Adler { T_DIRECT, SIP_MEDIA_FIXED, "*", "WDC WD????CX*", "*" }, 3879073a96aSEitan Adler /*quirks*/ADA_Q_4K 3889073a96aSEitan Adler }, 3899073a96aSEitan Adler { 390d3a460d3SAlexander Motin /* WDC Caviar Green Advanced Format (4k) drives */ 391d3a460d3SAlexander Motin { T_DIRECT, SIP_MEDIA_FIXED, "*", "WDC WD????RS*", "*" }, 392d3a460d3SAlexander Motin /*quirks*/ADA_Q_4K 393d3a460d3SAlexander Motin }, 394d3a460d3SAlexander Motin { 3959073a96aSEitan Adler /* WDC Caviar Green/Red Advanced Format (4k) drives */ 396d3a460d3SAlexander Motin { T_DIRECT, SIP_MEDIA_FIXED, "*", "WDC WD????RX*", "*" }, 397d3a460d3SAlexander Motin /*quirks*/ADA_Q_4K 398d3a460d3SAlexander Motin }, 399d3a460d3SAlexander Motin { 4009073a96aSEitan Adler /* WDC Caviar Red Advanced Format (4k) drives */ 4019073a96aSEitan Adler { T_DIRECT, SIP_MEDIA_FIXED, "*", "WDC WD??????CX*", "*" }, 4029073a96aSEitan Adler /*quirks*/ADA_Q_4K 4039073a96aSEitan Adler }, 4049073a96aSEitan Adler { 4059073a96aSEitan Adler /* WDC Caviar Black Advanced Format (4k) drives */ 406d734a3dfSEitan Adler { T_DIRECT, SIP_MEDIA_FIXED, "*", "WDC WD????AZEX*", "*" }, 407d734a3dfSEitan Adler /*quirks*/ADA_Q_4K 408d734a3dfSEitan Adler }, 409d734a3dfSEitan Adler { 410d734a3dfSEitan Adler /* WDC Caviar Black Advanced Format (4k) drives */ 411d734a3dfSEitan Adler { T_DIRECT, SIP_MEDIA_FIXED, "*", "WDC WD????FZEX*", "*" }, 4129073a96aSEitan Adler /*quirks*/ADA_Q_4K 4139073a96aSEitan Adler }, 4149073a96aSEitan Adler { 415d3a460d3SAlexander Motin /* WDC Caviar Green Advanced Format (4k) drives */ 416d3a460d3SAlexander Motin { T_DIRECT, SIP_MEDIA_FIXED, "*", "WDC WD??????RS*", "*" }, 417d3a460d3SAlexander Motin /*quirks*/ADA_Q_4K 418d3a460d3SAlexander Motin }, 419d3a460d3SAlexander Motin { 420d3a460d3SAlexander Motin /* WDC Caviar Green Advanced Format (4k) drives */ 421d3a460d3SAlexander Motin { T_DIRECT, SIP_MEDIA_FIXED, "*", "WDC WD??????RX*", "*" }, 422d3a460d3SAlexander Motin /*quirks*/ADA_Q_4K 423d3a460d3SAlexander Motin }, 424d3a460d3SAlexander Motin { 425d3a460d3SAlexander Motin /* WDC Scorpio Black Advanced Format (4k) drives */ 426d3a460d3SAlexander Motin { T_DIRECT, SIP_MEDIA_FIXED, "*", "WDC WD???PKT*", "*" }, 427d3a460d3SAlexander Motin /*quirks*/ADA_Q_4K 428d3a460d3SAlexander Motin }, 429d3a460d3SAlexander Motin { 430d3a460d3SAlexander Motin /* WDC Scorpio Black Advanced Format (4k) drives */ 431d3a460d3SAlexander Motin { T_DIRECT, SIP_MEDIA_FIXED, "*", "WDC WD?????PKT*", "*" }, 432d3a460d3SAlexander Motin /*quirks*/ADA_Q_4K 433d3a460d3SAlexander Motin }, 434d3a460d3SAlexander Motin { 435d3a460d3SAlexander Motin /* WDC Scorpio Blue Advanced Format (4k) drives */ 436d3a460d3SAlexander Motin { T_DIRECT, SIP_MEDIA_FIXED, "*", "WDC WD???PVT*", "*" }, 437d3a460d3SAlexander Motin /*quirks*/ADA_Q_4K 438d3a460d3SAlexander Motin }, 439d3a460d3SAlexander Motin { 440d3a460d3SAlexander Motin /* WDC Scorpio Blue Advanced Format (4k) drives */ 441d3a460d3SAlexander Motin { T_DIRECT, SIP_MEDIA_FIXED, "*", "WDC WD?????PVT*", "*" }, 442d3a460d3SAlexander Motin /*quirks*/ADA_Q_4K 443d3a460d3SAlexander Motin }, 44432fe0ef7SSteven Hartland /* SSDs */ 445d3a460d3SAlexander Motin { 4469d3334e1SEitan Adler /* 4479d3334e1SEitan Adler * Corsair Force 2 SSDs 4489d3334e1SEitan Adler * 4k optimised & trim only works in 4k requests + 4k aligned 4499d3334e1SEitan Adler */ 4509d3334e1SEitan Adler { T_DIRECT, SIP_MEDIA_FIXED, "*", "Corsair CSSD-F*", "*" }, 4519d3334e1SEitan Adler /*quirks*/ADA_Q_4K 4529d3334e1SEitan Adler }, 4539d3334e1SEitan Adler { 4549d3334e1SEitan Adler /* 4559d3334e1SEitan Adler * Corsair Force 3 SSDs 4569d3334e1SEitan Adler * 4k optimised & trim only works in 4k requests + 4k aligned 4579d3334e1SEitan Adler */ 4589d3334e1SEitan Adler { T_DIRECT, SIP_MEDIA_FIXED, "*", "Corsair Force 3*", "*" }, 4599d3334e1SEitan Adler /*quirks*/ADA_Q_4K 4609d3334e1SEitan Adler }, 4619d3334e1SEitan Adler { 4629d3334e1SEitan Adler /* 463d85805b2SSteven Hartland * Corsair Neutron GTX SSDs 464d85805b2SSteven Hartland * 4k optimised & trim only works in 4k requests + 4k aligned 465d85805b2SSteven Hartland */ 466d85805b2SSteven Hartland { T_DIRECT, SIP_MEDIA_FIXED, "*", "Corsair Neutron GTX*", "*" }, 467d85805b2SSteven Hartland /*quirks*/ADA_Q_4K 468d85805b2SSteven Hartland }, 469d85805b2SSteven Hartland { 470d85805b2SSteven Hartland /* 471dc98c62fSSteven Hartland * Corsair Force GT & GS SSDs 4729d3334e1SEitan Adler * 4k optimised & trim only works in 4k requests + 4k aligned 4739d3334e1SEitan Adler */ 474dc98c62fSSteven Hartland { T_DIRECT, SIP_MEDIA_FIXED, "*", "Corsair Force G*", "*" }, 4759d3334e1SEitan Adler /*quirks*/ADA_Q_4K 4769d3334e1SEitan Adler }, 4779d3334e1SEitan Adler { 4789d3334e1SEitan Adler /* 47932fe0ef7SSteven Hartland * Crucial M4 SSDs 4809d3334e1SEitan Adler * 4k optimised & trim only works in 4k requests + 4k aligned 4819d3334e1SEitan Adler */ 48232fe0ef7SSteven Hartland { T_DIRECT, SIP_MEDIA_FIXED, "*", "M4-CT???M4SSD2*", "*" }, 4839d3334e1SEitan Adler /*quirks*/ADA_Q_4K 4849d3334e1SEitan Adler }, 4859d3334e1SEitan Adler { 4869d3334e1SEitan Adler /* 487555bb680SWarner Losh * Crucial M500 SSDs MU07 firmware 488555bb680SWarner Losh * NCQ Trim works 489a6e0c5daSWarner Losh */ 490555bb680SWarner Losh { T_DIRECT, SIP_MEDIA_FIXED, "*", "Crucial CT*M500*", "MU07" }, 491a6e0c5daSWarner Losh /*quirks*/0 492a6e0c5daSWarner Losh }, 493a6e0c5daSWarner Losh { 494a6e0c5daSWarner Losh /* 495a6e0c5daSWarner Losh * Crucial M500 SSDs all other firmware 496a6e0c5daSWarner Losh * NCQ Trim doesn't work 497a6e0c5daSWarner Losh */ 498a6e0c5daSWarner Losh { T_DIRECT, SIP_MEDIA_FIXED, "*", "Crucial CT*M500*", "*" }, 499a6e0c5daSWarner Losh /*quirks*/ADA_Q_NCQ_TRIM_BROKEN 500a6e0c5daSWarner Losh }, 501a6e0c5daSWarner Losh { 502a6e0c5daSWarner Losh /* 503a6e0c5daSWarner Losh * Crucial M550 SSDs 504a6e0c5daSWarner Losh * NCQ Trim doesn't work, but only on MU01 firmware 505a6e0c5daSWarner Losh */ 506a6e0c5daSWarner Losh { T_DIRECT, SIP_MEDIA_FIXED, "*", "Crucial CT*M550*", "MU01" }, 507a6e0c5daSWarner Losh /*quirks*/ADA_Q_NCQ_TRIM_BROKEN 508a6e0c5daSWarner Losh }, 509a6e0c5daSWarner Losh { 510a6e0c5daSWarner Losh /* 511a6e0c5daSWarner Losh * Crucial MX100 SSDs 512a6e0c5daSWarner Losh * NCQ Trim doesn't work, but only on MU01 firmware 513a6e0c5daSWarner Losh */ 514a6e0c5daSWarner Losh { T_DIRECT, SIP_MEDIA_FIXED, "*", "Crucial CT*MX100*", "MU01" }, 515a6e0c5daSWarner Losh /*quirks*/ADA_Q_NCQ_TRIM_BROKEN 516a6e0c5daSWarner Losh }, 517a6e0c5daSWarner Losh { 518a6e0c5daSWarner Losh /* 5199d3334e1SEitan Adler * Crucial RealSSD C300 SSDs 5209d3334e1SEitan Adler * 4k optimised 5219d3334e1SEitan Adler */ 5229d3334e1SEitan Adler { T_DIRECT, SIP_MEDIA_FIXED, "*", "C300-CTFDDAC???MAG*", 5239d3334e1SEitan Adler "*" }, /*quirks*/ADA_Q_4K 5249d3334e1SEitan Adler }, 5259d3334e1SEitan Adler { 5269d3334e1SEitan Adler /* 527555bb680SWarner Losh * FCCT M500 SSDs 528555bb680SWarner Losh * NCQ Trim doesn't work 529555bb680SWarner Losh */ 530555bb680SWarner Losh { T_DIRECT, SIP_MEDIA_FIXED, "*", "FCCT*M500*", "*" }, 531555bb680SWarner Losh /*quirks*/ADA_Q_NCQ_TRIM_BROKEN 532555bb680SWarner Losh }, 533555bb680SWarner Losh { 534555bb680SWarner Losh /* 535883db1c1SEitan Adler * Intel 320 Series SSDs 536883db1c1SEitan Adler * 4k optimised & trim only works in 4k requests + 4k aligned 537883db1c1SEitan Adler */ 538883db1c1SEitan Adler { T_DIRECT, SIP_MEDIA_FIXED, "*", "INTEL SSDSA2CW*", "*" }, 539883db1c1SEitan Adler /*quirks*/ADA_Q_4K 540883db1c1SEitan Adler }, 541883db1c1SEitan Adler { 542883db1c1SEitan Adler /* 5439d3334e1SEitan Adler * Intel 330 Series SSDs 5449d3334e1SEitan Adler * 4k optimised & trim only works in 4k requests + 4k aligned 5459d3334e1SEitan Adler */ 54632fe0ef7SSteven Hartland { T_DIRECT, SIP_MEDIA_FIXED, "*", "INTEL SSDSC2CT*", "*" }, 5479d3334e1SEitan Adler /*quirks*/ADA_Q_4K 5489d3334e1SEitan Adler }, 5499d3334e1SEitan Adler { 5509d3334e1SEitan Adler /* 551883db1c1SEitan Adler * Intel 510 Series SSDs 552883db1c1SEitan Adler * 4k optimised & trim only works in 4k requests + 4k aligned 553883db1c1SEitan Adler */ 554883db1c1SEitan Adler { T_DIRECT, SIP_MEDIA_FIXED, "*", "INTEL SSDSC2MH*", "*" }, 555883db1c1SEitan Adler /*quirks*/ADA_Q_4K 556883db1c1SEitan Adler }, 557883db1c1SEitan Adler { 558883db1c1SEitan Adler /* 55932fe0ef7SSteven Hartland * Intel 520 Series SSDs 5609d3334e1SEitan Adler * 4k optimised & trim only works in 4k requests + 4k aligned 5619d3334e1SEitan Adler */ 56232fe0ef7SSteven Hartland { T_DIRECT, SIP_MEDIA_FIXED, "*", "INTEL SSDSC2BW*", "*" }, 56332fe0ef7SSteven Hartland /*quirks*/ADA_Q_4K 56432fe0ef7SSteven Hartland }, 56532fe0ef7SSteven Hartland { 56632fe0ef7SSteven Hartland /* 56723030355SSean Bruno * Intel S3610 Series SSDs 56823030355SSean Bruno * 4k optimised & trim only works in 4k requests + 4k aligned 56923030355SSean Bruno */ 57023030355SSean Bruno { T_DIRECT, SIP_MEDIA_FIXED, "*", "INTEL SSDSC2BX*", "*" }, 57123030355SSean Bruno /*quirks*/ADA_Q_4K 57223030355SSean Bruno }, 57323030355SSean Bruno { 57423030355SSean Bruno /* 575dce643c8SSteven Hartland * Intel X25-M Series SSDs 576dce643c8SSteven Hartland * 4k optimised & trim only works in 4k requests + 4k aligned 577dce643c8SSteven Hartland */ 578dce643c8SSteven Hartland { T_DIRECT, SIP_MEDIA_FIXED, "*", "INTEL SSDSA2M*", "*" }, 579dce643c8SSteven Hartland /*quirks*/ADA_Q_4K 580dce643c8SSteven Hartland }, 581dce643c8SSteven Hartland { 582dce643c8SSteven Hartland /* 583fb81f266SOleksandr Tymoshenko * KingDian S200 60GB P0921B 584fb81f266SOleksandr Tymoshenko * Trimming crash the SSD 585fb81f266SOleksandr Tymoshenko */ 586fb81f266SOleksandr Tymoshenko { T_DIRECT, SIP_MEDIA_FIXED, "*", "KingDian S200 *", "*" }, 587fb81f266SOleksandr Tymoshenko /*quirks*/ADA_Q_NO_TRIM 588fb81f266SOleksandr Tymoshenko }, 589fb81f266SOleksandr Tymoshenko { 590fb81f266SOleksandr Tymoshenko /* 59132fe0ef7SSteven Hartland * Kingston E100 Series SSDs 59232fe0ef7SSteven Hartland * 4k optimised & trim only works in 4k requests + 4k aligned 59332fe0ef7SSteven Hartland */ 59432fe0ef7SSteven Hartland { T_DIRECT, SIP_MEDIA_FIXED, "*", "KINGSTON SE100S3*", "*" }, 5959d3334e1SEitan Adler /*quirks*/ADA_Q_4K 5969d3334e1SEitan Adler }, 5979d3334e1SEitan Adler { 5989d3334e1SEitan Adler /* 5999d3334e1SEitan Adler * Kingston HyperX 3k SSDs 6009d3334e1SEitan Adler * 4k optimised & trim only works in 4k requests + 4k aligned 6019d3334e1SEitan Adler */ 6029d3334e1SEitan Adler { T_DIRECT, SIP_MEDIA_FIXED, "*", "KINGSTON SH103S3*", "*" }, 6039d3334e1SEitan Adler /*quirks*/ADA_Q_4K 6049d3334e1SEitan Adler }, 6059d3334e1SEitan Adler { 60632fe0ef7SSteven Hartland /* 607dce643c8SSteven Hartland * Marvell SSDs (entry taken from OpenSolaris) 608dce643c8SSteven Hartland * 4k optimised & trim only works in 4k requests + 4k aligned 609dce643c8SSteven Hartland */ 610dce643c8SSteven Hartland { T_DIRECT, SIP_MEDIA_FIXED, "*", "MARVELL SD88SA02*", "*" }, 611dce643c8SSteven Hartland /*quirks*/ADA_Q_4K 612dce643c8SSteven Hartland }, 613dce643c8SSteven Hartland { 614dce643c8SSteven Hartland /* 615555bb680SWarner Losh * Micron M500 SSDs firmware MU07 616a6e0c5daSWarner Losh * NCQ Trim works? 617a6e0c5daSWarner Losh */ 618555bb680SWarner Losh { T_DIRECT, SIP_MEDIA_FIXED, "*", "Micron M500*", "MU07" }, 619a6e0c5daSWarner Losh /*quirks*/0 620a6e0c5daSWarner Losh }, 621a6e0c5daSWarner Losh { 622a6e0c5daSWarner Losh /* 623a6e0c5daSWarner Losh * Micron M500 SSDs all other firmware 624a6e0c5daSWarner Losh * NCQ Trim doesn't work 625a6e0c5daSWarner Losh */ 626a6e0c5daSWarner Losh { T_DIRECT, SIP_MEDIA_FIXED, "*", "Micron M500*", "*" }, 627a6e0c5daSWarner Losh /*quirks*/ADA_Q_NCQ_TRIM_BROKEN 628a6e0c5daSWarner Losh }, 629a6e0c5daSWarner Losh { 630a6e0c5daSWarner Losh /* 631a6e0c5daSWarner Losh * Micron M5[15]0 SSDs 632a6e0c5daSWarner Losh * NCQ Trim doesn't work, but only MU01 firmware 633a6e0c5daSWarner Losh */ 634a6e0c5daSWarner Losh { T_DIRECT, SIP_MEDIA_FIXED, "*", "Micron M5[15]0*", "MU01" }, 635a6e0c5daSWarner Losh /*quirks*/ADA_Q_NCQ_TRIM_BROKEN 636a6e0c5daSWarner Losh }, 637a6e0c5daSWarner Losh { 638a6e0c5daSWarner Losh /* 63923030355SSean Bruno * Micron 5100 SSDs 64023030355SSean Bruno * 4k optimised & trim only works in 4k requests + 4k aligned 64123030355SSean Bruno */ 64223030355SSean Bruno { T_DIRECT, SIP_MEDIA_FIXED, "*", "Micron 5100 MTFDDAK*", "*" }, 64323030355SSean Bruno /*quirks*/ADA_Q_4K 64423030355SSean Bruno }, 64523030355SSean Bruno { 64623030355SSean Bruno /* 647dce643c8SSteven Hartland * OCZ Agility 2 SSDs 648dce643c8SSteven Hartland * 4k optimised & trim only works in 4k requests + 4k aligned 649dce643c8SSteven Hartland */ 650dce643c8SSteven Hartland { T_DIRECT, SIP_MEDIA_FIXED, "*", "OCZ-AGILITY2*", "*" }, 651dce643c8SSteven Hartland /*quirks*/ADA_Q_4K 652dce643c8SSteven Hartland }, 653dce643c8SSteven Hartland { 654dce643c8SSteven Hartland /* 65532fe0ef7SSteven Hartland * OCZ Agility 3 SSDs 65632fe0ef7SSteven Hartland * 4k optimised & trim only works in 4k requests + 4k aligned 65732fe0ef7SSteven Hartland */ 65832fe0ef7SSteven Hartland { T_DIRECT, SIP_MEDIA_FIXED, "*", "OCZ-AGILITY3*", "*" }, 65932fe0ef7SSteven Hartland /*quirks*/ADA_Q_4K 66032fe0ef7SSteven Hartland }, 66132fe0ef7SSteven Hartland { 66232fe0ef7SSteven Hartland /* 66332fe0ef7SSteven Hartland * OCZ Deneva R Series SSDs 66432fe0ef7SSteven Hartland * 4k optimised & trim only works in 4k requests + 4k aligned 66532fe0ef7SSteven Hartland */ 66632fe0ef7SSteven Hartland { T_DIRECT, SIP_MEDIA_FIXED, "*", "DENRSTE251M45*", "*" }, 66732fe0ef7SSteven Hartland /*quirks*/ADA_Q_4K 66832fe0ef7SSteven Hartland }, 66932fe0ef7SSteven Hartland { 67032fe0ef7SSteven Hartland /* 67132fe0ef7SSteven Hartland * OCZ Vertex 2 SSDs (inc pro series) 67232fe0ef7SSteven Hartland * 4k optimised & trim only works in 4k requests + 4k aligned 67332fe0ef7SSteven Hartland */ 67432fe0ef7SSteven Hartland { T_DIRECT, SIP_MEDIA_FIXED, "*", "OCZ?VERTEX2*", "*" }, 67532fe0ef7SSteven Hartland /*quirks*/ADA_Q_4K 67632fe0ef7SSteven Hartland }, 67732fe0ef7SSteven Hartland { 67832fe0ef7SSteven Hartland /* 67932fe0ef7SSteven Hartland * OCZ Vertex 3 SSDs 68032fe0ef7SSteven Hartland * 4k optimised & trim only works in 4k requests + 4k aligned 68132fe0ef7SSteven Hartland */ 68232fe0ef7SSteven Hartland { T_DIRECT, SIP_MEDIA_FIXED, "*", "OCZ-VERTEX3*", "*" }, 68332fe0ef7SSteven Hartland /*quirks*/ADA_Q_4K 68432fe0ef7SSteven Hartland }, 68532fe0ef7SSteven Hartland { 68632fe0ef7SSteven Hartland /* 6877f1c7787SSteven Hartland * OCZ Vertex 4 SSDs 6887f1c7787SSteven Hartland * 4k optimised & trim only works in 4k requests + 4k aligned 6897f1c7787SSteven Hartland */ 6907f1c7787SSteven Hartland { T_DIRECT, SIP_MEDIA_FIXED, "*", "OCZ-VERTEX4*", "*" }, 6917f1c7787SSteven Hartland /*quirks*/ADA_Q_4K 6927f1c7787SSteven Hartland }, 6937f1c7787SSteven Hartland { 6947f1c7787SSteven Hartland /* 695989e632aSSean Bruno * Samsung 750 SSDs 696989e632aSSean Bruno * 4k optimised, NCQ TRIM seems to work 697989e632aSSean Bruno */ 698989e632aSSean Bruno { T_DIRECT, SIP_MEDIA_FIXED, "*", "Samsung SSD 750*", "*" }, 699989e632aSSean Bruno /*quirks*/ADA_Q_4K 700989e632aSSean Bruno }, 701989e632aSSean Bruno { 702989e632aSSean Bruno /* 70332fe0ef7SSteven Hartland * Samsung 830 Series SSDs 704a6e0c5daSWarner Losh * 4k optimised, NCQ TRIM Broken (normal TRIM is fine) 70532fe0ef7SSteven Hartland */ 70632fe0ef7SSteven Hartland { T_DIRECT, SIP_MEDIA_FIXED, "*", "SAMSUNG SSD 830 Series*", "*" }, 707a6e0c5daSWarner Losh /*quirks*/ADA_Q_4K | ADA_Q_NCQ_TRIM_BROKEN 70832fe0ef7SSteven Hartland }, 70932fe0ef7SSteven Hartland { 71032fe0ef7SSteven Hartland /* 711dc98c62fSSteven Hartland * Samsung 840 SSDs 712a6e0c5daSWarner Losh * 4k optimised, NCQ TRIM Broken (normal TRIM is fine) 713dc98c62fSSteven Hartland */ 714dc98c62fSSteven Hartland { T_DIRECT, SIP_MEDIA_FIXED, "*", "Samsung SSD 840*", "*" }, 715a6e0c5daSWarner Losh /*quirks*/ADA_Q_4K | ADA_Q_NCQ_TRIM_BROKEN 716dc98c62fSSteven Hartland }, 717dc98c62fSSteven Hartland { 718dc98c62fSSteven Hartland /* 719d03ae351SSean Bruno * Samsung 845 SSDs 720d03ae351SSean Bruno * 4k optimised, NCQ TRIM Broken (normal TRIM is fine) 721d03ae351SSean Bruno */ 722d03ae351SSean Bruno { T_DIRECT, SIP_MEDIA_FIXED, "*", "Samsung SSD 845*", "*" }, 723d03ae351SSean Bruno /*quirks*/ADA_Q_4K | ADA_Q_NCQ_TRIM_BROKEN 724d03ae351SSean Bruno }, 725d03ae351SSean Bruno { 726d03ae351SSean Bruno /* 727e3a21bd1SGeorge V. Neville-Neil * Samsung 850 SSDs 728a6e0c5daSWarner Losh * 4k optimised, NCQ TRIM broken (normal TRIM fine) 729e3a21bd1SGeorge V. Neville-Neil */ 730e3a21bd1SGeorge V. Neville-Neil { T_DIRECT, SIP_MEDIA_FIXED, "*", "Samsung SSD 850*", "*" }, 731a6e0c5daSWarner Losh /*quirks*/ADA_Q_4K | ADA_Q_NCQ_TRIM_BROKEN 732e3a21bd1SGeorge V. Neville-Neil }, 7335f91863aSSean Bruno { 7345f91863aSSean Bruno /* 735b93ecd35SWarner Losh * Samsung SM863 Series SSDs (MZ7KM*) 736b93ecd35SWarner Losh * 4k optimised, NCQ believed to be working 737b93ecd35SWarner Losh */ 738b93ecd35SWarner Losh { T_DIRECT, SIP_MEDIA_FIXED, "*", "SAMSUNG MZ7KM*", "*" }, 739b93ecd35SWarner Losh /*quirks*/ADA_Q_4K 740b93ecd35SWarner Losh }, 741b93ecd35SWarner Losh { 742b93ecd35SWarner Losh /* 743eae90da9SJean-Sébastien Pédron * Samsung 843T Series SSDs (MZ7WD*) 744eae90da9SJean-Sébastien Pédron * Samsung PM851 Series SSDs (MZ7TE*) 745eae90da9SJean-Sébastien Pédron * Samsung PM853T Series SSDs (MZ7GE*) 746b93ecd35SWarner Losh * 4k optimised, NCQ believed to be broken since these are 747b93ecd35SWarner Losh * appear to be built with the same controllers as the 840/850. 748323e0f6dSSean Bruno */ 749eae90da9SJean-Sébastien Pédron { T_DIRECT, SIP_MEDIA_FIXED, "*", "SAMSUNG MZ7*", "*" }, 750b93ecd35SWarner Losh /*quirks*/ADA_Q_4K | ADA_Q_NCQ_TRIM_BROKEN 751844b7984SSean Bruno }, 752844b7984SSean Bruno { 753844b7984SSean Bruno /* 75416f92ad2SBaptiste Daroussin * Same as for SAMSUNG MZ7* but enable the quirks for SSD 75516f92ad2SBaptiste Daroussin * starting with MZ7* too 75616f92ad2SBaptiste Daroussin */ 75716f92ad2SBaptiste Daroussin { T_DIRECT, SIP_MEDIA_FIXED, "*", "MZ7*", "*" }, 75816f92ad2SBaptiste Daroussin /*quirks*/ADA_Q_4K | ADA_Q_NCQ_TRIM_BROKEN 75916f92ad2SBaptiste Daroussin }, 76016f92ad2SBaptiste Daroussin { 76116f92ad2SBaptiste Daroussin /* 762bf95d6a6SWarner Losh * Samsung PM851 Series SSDs Dell OEM 763bf95d6a6SWarner Losh * device model "SAMSUNG SSD PM851 mSATA 256GB" 764bf95d6a6SWarner Losh * 4k optimised, NCQ broken 765bf95d6a6SWarner Losh */ 766bf95d6a6SWarner Losh { T_DIRECT, SIP_MEDIA_FIXED, "*", "SAMSUNG SSD PM851*", "*" }, 767bf95d6a6SWarner Losh /*quirks*/ADA_Q_4K | ADA_Q_NCQ_TRIM_BROKEN 768bf95d6a6SWarner Losh }, 769bf95d6a6SWarner Losh { 770bf95d6a6SWarner Losh /* 77132fe0ef7SSteven Hartland * SuperTalent TeraDrive CT SSDs 77232fe0ef7SSteven Hartland * 4k optimised & trim only works in 4k requests + 4k aligned 77332fe0ef7SSteven Hartland */ 77432fe0ef7SSteven Hartland { T_DIRECT, SIP_MEDIA_FIXED, "*", "FTM??CT25H*", "*" }, 77532fe0ef7SSteven Hartland /*quirks*/ADA_Q_4K 77632fe0ef7SSteven Hartland }, 77732fe0ef7SSteven Hartland { 77832fe0ef7SSteven Hartland /* 77932fe0ef7SSteven Hartland * XceedIOPS SATA SSDs 78032fe0ef7SSteven Hartland * 4k optimised 78132fe0ef7SSteven Hartland */ 78232fe0ef7SSteven Hartland { T_DIRECT, SIP_MEDIA_FIXED, "*", "SG9XCS2D*", "*" }, 78332fe0ef7SSteven Hartland /*quirks*/ADA_Q_4K 78432fe0ef7SSteven Hartland }, 78532fe0ef7SSteven Hartland { 786600fd98fSKenneth D. Merry /* 787600fd98fSKenneth D. Merry * Samsung drive that doesn't support READ LOG EXT or 788600fd98fSKenneth D. Merry * READ LOG DMA EXT, despite reporting that it does in 789600fd98fSKenneth D. Merry * ATA identify data: 790600fd98fSKenneth D. Merry * SAMSUNG HD200HJ KF100-06 791600fd98fSKenneth D. Merry */ 792600fd98fSKenneth D. Merry { T_DIRECT, SIP_MEDIA_FIXED, "*", "SAMSUNG HD200*", "*" }, 793600fd98fSKenneth D. Merry /*quirks*/ADA_Q_LOG_BROKEN 794600fd98fSKenneth D. Merry }, 795600fd98fSKenneth D. Merry { 796600fd98fSKenneth D. Merry /* 797600fd98fSKenneth D. Merry * Samsung drive that doesn't support READ LOG EXT or 798600fd98fSKenneth D. Merry * READ LOG DMA EXT, despite reporting that it does in 799600fd98fSKenneth D. Merry * ATA identify data: 800600fd98fSKenneth D. Merry * SAMSUNG HD501LJ CR100-10 801600fd98fSKenneth D. Merry */ 802600fd98fSKenneth D. Merry { T_DIRECT, SIP_MEDIA_FIXED, "*", "SAMSUNG HD501*", "*" }, 803600fd98fSKenneth D. Merry /*quirks*/ADA_Q_LOG_BROKEN 804600fd98fSKenneth D. Merry }, 805600fd98fSKenneth D. Merry { 806600fd98fSKenneth D. Merry /* 807600fd98fSKenneth D. Merry * Seagate Lamarr 8TB Shingled Magnetic Recording (SMR) 808600fd98fSKenneth D. Merry * Drive Managed SATA hard drive. This drive doesn't report 809600fd98fSKenneth D. Merry * in firmware that it is a drive managed SMR drive. 810600fd98fSKenneth D. Merry */ 811a4557b05SAlan Somers { T_DIRECT, SIP_MEDIA_FIXED, "*", "ST8000AS000[23]*", "*" }, 812600fd98fSKenneth D. Merry /*quirks*/ADA_Q_SMR_DM 813600fd98fSKenneth D. Merry }, 814600fd98fSKenneth D. Merry { 815ecca2aa5SScott Long /* WD Green SSD */ 816ecca2aa5SScott Long { T_DIRECT, SIP_MEDIA_FIXED, "*", "WDC WDS?????G0*", "*" }, 817ecca2aa5SScott Long /*quirks*/ADA_Q_4K | ADA_Q_NCQ_TRIM_BROKEN 818ecca2aa5SScott Long }, 819ecca2aa5SScott Long { 82030a4094fSAlexander Motin /* Default */ 82130a4094fSAlexander Motin { 82230a4094fSAlexander Motin T_ANY, SIP_MEDIA_REMOVABLE|SIP_MEDIA_FIXED, 82330a4094fSAlexander Motin /*vendor*/"*", /*product*/"*", /*revision*/"*" 82430a4094fSAlexander Motin }, 82530a4094fSAlexander Motin /*quirks*/0 82630a4094fSAlexander Motin }, 82730a4094fSAlexander Motin }; 82852c9ce25SScott Long 82952c9ce25SScott Long static disk_strategy_t adastrategy; 83052c9ce25SScott Long static dumper_t adadump; 83152c9ce25SScott Long static periph_init_t adainit; 8329a6844d5SKenneth D. Merry static void adadiskgonecb(struct disk *dp); 8339a6844d5SKenneth D. Merry static periph_oninv_t adaoninvalidate; 8349a6844d5SKenneth D. Merry static periph_dtor_t adacleanup; 83552c9ce25SScott Long static void adaasync(void *callback_arg, u_int32_t code, 83652c9ce25SScott Long struct cam_path *path, void *arg); 837cf3ff63eSWarner Losh static int adabitsysctl(SYSCTL_HANDLER_ARGS); 838aeab0812SWarner Losh static int adaflagssysctl(SYSCTL_HANDLER_ARGS); 839aeab0812SWarner Losh static int adazonesupsysctl(SYSCTL_HANDLER_ARGS); 84052c9ce25SScott Long static void adasysctlinit(void *context, int pending); 8419a6844d5SKenneth D. Merry static int adagetattr(struct bio *bp); 8429a6844d5SKenneth D. Merry static void adasetflags(struct ada_softc *softc, 8439a6844d5SKenneth D. Merry struct ccb_getdev *cgd); 84476d843daSAlexander Motin static void adasetgeom(struct ada_softc *softc, 84576d843daSAlexander Motin struct ccb_getdev *cgd); 84652c9ce25SScott Long static periph_ctor_t adaregister; 8479a6844d5SKenneth D. Merry static void ada_dsmtrim(struct ada_softc *softc, struct bio *bp, 8489a6844d5SKenneth D. Merry struct ccb_ataio *ataio); 8499a6844d5SKenneth D. Merry static void ada_cfaerase(struct ada_softc *softc, struct bio *bp, 8509a6844d5SKenneth D. Merry struct ccb_ataio *ataio); 8519a6844d5SKenneth D. Merry static int ada_zone_bio_to_ata(int disk_zone_cmd); 8529a6844d5SKenneth D. Merry static int ada_zone_cmd(struct cam_periph *periph, union ccb *ccb, 8539a6844d5SKenneth D. Merry struct bio *bp, int *queue_ccb); 85452c9ce25SScott Long static periph_start_t adastart; 8559a6844d5SKenneth D. Merry static void adaprobedone(struct cam_periph *periph, union ccb *ccb); 8569a6844d5SKenneth D. Merry static void adazonedone(struct cam_periph *periph, union ccb *ccb); 85752c9ce25SScott Long static void adadone(struct cam_periph *periph, 85852c9ce25SScott Long union ccb *done_ccb); 85952c9ce25SScott Long static int adaerror(union ccb *ccb, u_int32_t cam_flags, 86052c9ce25SScott Long u_int32_t sense_flags); 8615773ac11SJohn Baldwin static callout_func_t adasendorderedtag; 86252c9ce25SScott Long static void adashutdown(void *arg, int howto); 863c3d0d168SAlexander Motin static void adasuspend(void *arg); 864c3d0d168SAlexander Motin static void adaresume(void *arg); 86552c9ce25SScott Long 86652c9ce25SScott Long #ifndef ADA_DEFAULT_TIMEOUT 86752c9ce25SScott Long #define ADA_DEFAULT_TIMEOUT 30 /* Timeout in seconds */ 86852c9ce25SScott Long #endif 86952c9ce25SScott Long 87052c9ce25SScott Long #ifndef ADA_DEFAULT_RETRY 87152c9ce25SScott Long #define ADA_DEFAULT_RETRY 4 87252c9ce25SScott Long #endif 87352c9ce25SScott Long 87452c9ce25SScott Long #ifndef ADA_DEFAULT_SEND_ORDERED 87552c9ce25SScott Long #define ADA_DEFAULT_SEND_ORDERED 1 87652c9ce25SScott Long #endif 87752c9ce25SScott Long 878fd104c15SRebecca Cran #ifndef ADA_DEFAULT_SPINDOWN_SHUTDOWN 879fd104c15SRebecca Cran #define ADA_DEFAULT_SPINDOWN_SHUTDOWN 1 880fd104c15SRebecca Cran #endif 881fd104c15SRebecca Cran 882c3d0d168SAlexander Motin #ifndef ADA_DEFAULT_SPINDOWN_SUSPEND 883c3d0d168SAlexander Motin #define ADA_DEFAULT_SPINDOWN_SUSPEND 1 884c3d0d168SAlexander Motin #endif 885c3d0d168SAlexander Motin 8861ed6aaf9SAlexander Motin #ifndef ADA_DEFAULT_READ_AHEAD 8871ed6aaf9SAlexander Motin #define ADA_DEFAULT_READ_AHEAD 1 8881ed6aaf9SAlexander Motin #endif 8891ed6aaf9SAlexander Motin 890f513d14cSAlexander Motin #ifndef ADA_DEFAULT_WRITE_CACHE 891f513d14cSAlexander Motin #define ADA_DEFAULT_WRITE_CACHE 1 892f513d14cSAlexander Motin #endif 893f513d14cSAlexander Motin 8941ed6aaf9SAlexander Motin #define ADA_RA (softc->read_ahead >= 0 ? \ 8951ed6aaf9SAlexander Motin softc->read_ahead : ada_read_ahead) 8961ed6aaf9SAlexander Motin #define ADA_WC (softc->write_cache >= 0 ? \ 8971ed6aaf9SAlexander Motin softc->write_cache : ada_write_cache) 8981ed6aaf9SAlexander Motin 89952c9ce25SScott Long static int ada_retry_count = ADA_DEFAULT_RETRY; 90052c9ce25SScott Long static int ada_default_timeout = ADA_DEFAULT_TIMEOUT; 90152c9ce25SScott Long static int ada_send_ordered = ADA_DEFAULT_SEND_ORDERED; 902fd104c15SRebecca Cran static int ada_spindown_shutdown = ADA_DEFAULT_SPINDOWN_SHUTDOWN; 903c3d0d168SAlexander Motin static int ada_spindown_suspend = ADA_DEFAULT_SPINDOWN_SUSPEND; 9041ed6aaf9SAlexander Motin static int ada_read_ahead = ADA_DEFAULT_READ_AHEAD; 905f513d14cSAlexander Motin static int ada_write_cache = ADA_DEFAULT_WRITE_CACHE; 90613532153SScott Long static int ada_enable_biospeedup = 1; 9076f147a07SEdward Tomasz Napierala static int ada_enable_uma_ccbs = 1; 90852c9ce25SScott Long 9097029da5cSPawel Biernacki static SYSCTL_NODE(_kern_cam, OID_AUTO, ada, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, 91052c9ce25SScott Long "CAM Direct Access Disk driver"); 911af3b2549SHans Petter Selasky SYSCTL_INT(_kern_cam_ada, OID_AUTO, retry_count, CTLFLAG_RWTUN, 91252c9ce25SScott Long &ada_retry_count, 0, "Normal I/O retry count"); 913af3b2549SHans Petter Selasky SYSCTL_INT(_kern_cam_ada, OID_AUTO, default_timeout, CTLFLAG_RWTUN, 91452c9ce25SScott Long &ada_default_timeout, 0, "Normal I/O timeout (in seconds)"); 915af3b2549SHans Petter Selasky SYSCTL_INT(_kern_cam_ada, OID_AUTO, send_ordered, CTLFLAG_RWTUN, 91652c9ce25SScott Long &ada_send_ordered, 0, "Send Ordered Tags"); 917af3b2549SHans Petter Selasky SYSCTL_INT(_kern_cam_ada, OID_AUTO, spindown_shutdown, CTLFLAG_RWTUN, 918fd104c15SRebecca Cran &ada_spindown_shutdown, 0, "Spin down upon shutdown"); 919af3b2549SHans Petter Selasky SYSCTL_INT(_kern_cam_ada, OID_AUTO, spindown_suspend, CTLFLAG_RWTUN, 920c3d0d168SAlexander Motin &ada_spindown_suspend, 0, "Spin down upon suspend"); 921af3b2549SHans Petter Selasky SYSCTL_INT(_kern_cam_ada, OID_AUTO, read_ahead, CTLFLAG_RWTUN, 9221ed6aaf9SAlexander Motin &ada_read_ahead, 0, "Enable disk read-ahead"); 923af3b2549SHans Petter Selasky SYSCTL_INT(_kern_cam_ada, OID_AUTO, write_cache, CTLFLAG_RWTUN, 924f513d14cSAlexander Motin &ada_write_cache, 0, "Enable disk write cache"); 92513532153SScott Long SYSCTL_INT(_kern_cam_ada, OID_AUTO, enable_biospeedup, CTLFLAG_RDTUN, 92613532153SScott Long &ada_enable_biospeedup, 0, "Enable BIO_SPEEDUP processing"); 9273394d423SEdward Tomasz Napierala SYSCTL_INT(_kern_cam_ada, OID_AUTO, enable_uma_ccbs, CTLFLAG_RWTUN, 9283394d423SEdward Tomasz Napierala &ada_enable_uma_ccbs, 0, "Use UMA for CCBs"); 92952c9ce25SScott Long 93052c9ce25SScott Long /* 93152c9ce25SScott Long * ADA_ORDEREDTAG_INTERVAL determines how often, relative 93252c9ce25SScott Long * to the default timeout, we check to see whether an ordered 93352c9ce25SScott Long * tagged transaction is appropriate to prevent simple tag 93452c9ce25SScott Long * starvation. Since we'd like to ensure that there is at least 93552c9ce25SScott Long * 1/2 of the timeout length left for a starved transaction to 93652c9ce25SScott Long * complete after we've sent an ordered tag, we must poll at least 93752c9ce25SScott Long * four times in every timeout period. This takes care of the worst 93852c9ce25SScott Long * case where a starved transaction starts during an interval that 93952c9ce25SScott Long * meets the requirement "don't send an ordered tag" test so it takes 94052c9ce25SScott Long * us two intervals to determine that a tag must be sent. 94152c9ce25SScott Long */ 94252c9ce25SScott Long #ifndef ADA_ORDEREDTAG_INTERVAL 94352c9ce25SScott Long #define ADA_ORDEREDTAG_INTERVAL 4 94452c9ce25SScott Long #endif 94552c9ce25SScott Long 94652c9ce25SScott Long static struct periph_driver adadriver = 94752c9ce25SScott Long { 94852c9ce25SScott Long adainit, "ada", 94952c9ce25SScott Long TAILQ_HEAD_INITIALIZER(adadriver.units), /* generation */ 0 95052c9ce25SScott Long }; 95152c9ce25SScott Long 952a6e0c5daSWarner Losh static int adadeletemethodsysctl(SYSCTL_HANDLER_ARGS); 953a6e0c5daSWarner Losh 95452c9ce25SScott Long PERIPHDRIVER_DECLARE(ada, adadriver); 95552c9ce25SScott Long 9569a6844d5SKenneth D. Merry static MALLOC_DEFINE(M_ATADA, "ata_da", "ata_da buffers"); 9579a6844d5SKenneth D. Merry 95852c9ce25SScott Long static int 95952c9ce25SScott Long adaopen(struct disk *dp) 96052c9ce25SScott Long { 96152c9ce25SScott Long struct cam_periph *periph; 96252c9ce25SScott Long struct ada_softc *softc; 96352c9ce25SScott Long int error; 96452c9ce25SScott Long 96552c9ce25SScott Long periph = (struct cam_periph *)dp->d_drv1; 96699e7a4adSScott Long if (cam_periph_acquire(periph) != 0) { 96752c9ce25SScott Long return(ENXIO); 96852c9ce25SScott Long } 96952c9ce25SScott Long 97052c9ce25SScott Long cam_periph_lock(periph); 97152c9ce25SScott Long if ((error = cam_periph_hold(periph, PRIBIO|PCATCH)) != 0) { 97252c9ce25SScott Long cam_periph_unlock(periph); 97352c9ce25SScott Long cam_periph_release(periph); 97452c9ce25SScott Long return (error); 97552c9ce25SScott Long } 97652c9ce25SScott Long 977fddde2b8SAlexander Motin CAM_DEBUG(periph->path, CAM_DEBUG_TRACE | CAM_DEBUG_PERIPH, 978fddde2b8SAlexander Motin ("adaopen\n")); 97952c9ce25SScott Long 9807338ef1aSAlexander Motin softc = (struct ada_softc *)periph->softc; 9817338ef1aSAlexander Motin softc->flags |= ADA_FLAG_OPEN; 98252c9ce25SScott Long 98352c9ce25SScott Long cam_periph_unhold(periph); 98452c9ce25SScott Long cam_periph_unlock(periph); 98552c9ce25SScott Long return (0); 98652c9ce25SScott Long } 98752c9ce25SScott Long 98852c9ce25SScott Long static int 98952c9ce25SScott Long adaclose(struct disk *dp) 99052c9ce25SScott Long { 99152c9ce25SScott Long struct cam_periph *periph; 99252c9ce25SScott Long struct ada_softc *softc; 99352c9ce25SScott Long union ccb *ccb; 99469114bc0SAlexander Motin int error; 99552c9ce25SScott Long 99652c9ce25SScott Long periph = (struct cam_periph *)dp->d_drv1; 99752c9ce25SScott Long softc = (struct ada_softc *)periph->softc; 998227d67aaSAlexander Motin cam_periph_lock(periph); 999fddde2b8SAlexander Motin 1000fddde2b8SAlexander Motin CAM_DEBUG(periph->path, CAM_DEBUG_TRACE | CAM_DEBUG_PERIPH, 1001fddde2b8SAlexander Motin ("adaclose\n")); 1002fddde2b8SAlexander Motin 100352c9ce25SScott Long /* We only sync the cache if the drive is capable of it. */ 100469114bc0SAlexander Motin if ((softc->flags & ADA_FLAG_DIRTY) != 0 && 100569114bc0SAlexander Motin (softc->flags & ADA_FLAG_CAN_FLUSHCACHE) != 0 && 1006227d67aaSAlexander Motin (periph->flags & CAM_PERIPH_INVALID) == 0 && 1007227d67aaSAlexander Motin cam_periph_hold(periph, PRIBIO) == 0) { 1008bbfa4aa1SAlexander Motin ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL); 100952c9ce25SScott Long cam_fill_ataio(&ccb->ataio, 101052c9ce25SScott Long 1, 1011eed99e75SScott Long NULL, 101252c9ce25SScott Long CAM_DIR_NONE, 101352c9ce25SScott Long 0, 101452c9ce25SScott Long NULL, 101552c9ce25SScott Long 0, 101652c9ce25SScott Long ada_default_timeout*1000); 101752c9ce25SScott Long 101852c9ce25SScott Long if (softc->flags & ADA_FLAG_CAN_48BIT) 101952c9ce25SScott Long ata_48bit_cmd(&ccb->ataio, ATA_FLUSHCACHE48, 0, 0, 0); 102052c9ce25SScott Long else 10217606b445SAlexander Motin ata_28bit_cmd(&ccb->ataio, ATA_FLUSHCACHE, 0, 0, 0); 102269114bc0SAlexander Motin error = cam_periph_runccb(ccb, adaerror, /*cam_flags*/0, 102346f118feSAlexander Motin /*sense_flags*/0, softc->disk->d_devstat); 102452c9ce25SScott Long 102569114bc0SAlexander Motin if (error != 0) 102652c9ce25SScott Long xpt_print(periph->path, "Synchronize cache failed\n"); 102769114bc0SAlexander Motin softc->flags &= ~ADA_FLAG_DIRTY; 102852c9ce25SScott Long xpt_release_ccb(ccb); 1029227d67aaSAlexander Motin cam_periph_unhold(periph); 103052c9ce25SScott Long } 103152c9ce25SScott Long 103252c9ce25SScott Long softc->flags &= ~ADA_FLAG_OPEN; 1033227d67aaSAlexander Motin 1034227d67aaSAlexander Motin while (softc->refcount != 0) 1035227d67aaSAlexander Motin cam_periph_sleep(periph, &softc->refcount, PRIBIO, "adaclose", 1); 103652c9ce25SScott Long cam_periph_unlock(periph); 103752c9ce25SScott Long cam_periph_release(periph); 103852c9ce25SScott Long return (0); 103952c9ce25SScott Long } 104052c9ce25SScott Long 10411c80ec0aSAlexander Motin static void 10421c80ec0aSAlexander Motin adaschedule(struct cam_periph *periph) 10431c80ec0aSAlexander Motin { 10441c80ec0aSAlexander Motin struct ada_softc *softc = (struct ada_softc *)periph->softc; 10451c80ec0aSAlexander Motin 10466bf435dcSAlexander Motin if (softc->state != ADA_STATE_NORMAL) 10476bf435dcSAlexander Motin return; 10486bf435dcSAlexander Motin 1049a6e0c5daSWarner Losh cam_iosched_schedule(softc->cam_iosched, periph); 10501c80ec0aSAlexander Motin } 10511c80ec0aSAlexander Motin 105252c9ce25SScott Long /* 105352c9ce25SScott Long * Actually translate the requested transfer into one the physical driver 105452c9ce25SScott Long * can understand. The transfer is described by a buf and will include 105552c9ce25SScott Long * only one physical transfer. 105652c9ce25SScott Long */ 105752c9ce25SScott Long static void 105852c9ce25SScott Long adastrategy(struct bio *bp) 105952c9ce25SScott Long { 106052c9ce25SScott Long struct cam_periph *periph; 106152c9ce25SScott Long struct ada_softc *softc; 106252c9ce25SScott Long 106352c9ce25SScott Long periph = (struct cam_periph *)bp->bio_disk->d_drv1; 106452c9ce25SScott Long softc = (struct ada_softc *)periph->softc; 106552c9ce25SScott Long 106652c9ce25SScott Long cam_periph_lock(periph); 106752c9ce25SScott Long 1068fddde2b8SAlexander Motin CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("adastrategy(%p)\n", bp)); 1069fddde2b8SAlexander Motin 107052c9ce25SScott Long /* 107152c9ce25SScott Long * If the device has been made invalid, error out 107252c9ce25SScott Long */ 10737338ef1aSAlexander Motin if ((periph->flags & CAM_PERIPH_INVALID) != 0) { 107452c9ce25SScott Long cam_periph_unlock(periph); 107552c9ce25SScott Long biofinish(bp, NULL, ENXIO); 107652c9ce25SScott Long return; 107752c9ce25SScott Long } 107852c9ce25SScott Long 107952c9ce25SScott Long /* 10809a6844d5SKenneth D. Merry * Zone commands must be ordered, because they can depend on the 10819a6844d5SKenneth D. Merry * effects of previously issued commands, and they may affect 10829a6844d5SKenneth D. Merry * commands after them. 10839a6844d5SKenneth D. Merry */ 10849a6844d5SKenneth D. Merry if (bp->bio_cmd == BIO_ZONE) 10859a6844d5SKenneth D. Merry bp->bio_flags |= BIO_ORDERED; 10869a6844d5SKenneth D. Merry 10879a6844d5SKenneth D. Merry /* 108852c9ce25SScott Long * Place it in the queue of disk activities for this disk 108952c9ce25SScott Long */ 1090a6e0c5daSWarner Losh cam_iosched_queue_work(softc->cam_iosched, bp); 109152c9ce25SScott Long 109252c9ce25SScott Long /* 109352c9ce25SScott Long * Schedule ourselves for performing the work. 109452c9ce25SScott Long */ 10951c80ec0aSAlexander Motin adaschedule(periph); 109652c9ce25SScott Long cam_periph_unlock(periph); 109752c9ce25SScott Long 109852c9ce25SScott Long return; 109952c9ce25SScott Long } 110052c9ce25SScott Long 110152c9ce25SScott Long static int 1102489ba222SMitchell Horne adadump(void *arg, void *virtual, off_t offset, size_t length) 110352c9ce25SScott Long { 110452c9ce25SScott Long struct cam_periph *periph; 110552c9ce25SScott Long struct ada_softc *softc; 110652c9ce25SScott Long u_int secsize; 110705972bafSWarner Losh struct ccb_ataio ataio; 110852c9ce25SScott Long struct disk *dp; 110952c9ce25SScott Long uint64_t lba; 111052c9ce25SScott Long uint16_t count; 11110191d9b3SAlexander Motin int error = 0; 111252c9ce25SScott Long 111352c9ce25SScott Long dp = arg; 111452c9ce25SScott Long periph = dp->d_drv1; 111552c9ce25SScott Long softc = (struct ada_softc *)periph->softc; 111652c9ce25SScott Long secsize = softc->params.secsize; 111752c9ce25SScott Long lba = offset / secsize; 111852c9ce25SScott Long count = length / secsize; 111904e814aeSScott Long if ((periph->flags & CAM_PERIPH_INVALID) != 0) 112052c9ce25SScott Long return (ENXIO); 112152c9ce25SScott Long 112205972bafSWarner Losh memset(&ataio, 0, sizeof(ataio)); 112352c9ce25SScott Long if (length > 0) { 112405972bafSWarner Losh xpt_setup_ccb(&ataio.ccb_h, periph->path, CAM_PRIORITY_NORMAL); 112505972bafSWarner Losh ataio.ccb_h.ccb_state = ADA_CCB_DUMP; 112605972bafSWarner Losh cam_fill_ataio(&ataio, 112752c9ce25SScott Long 0, 1128eed99e75SScott Long NULL, 112952c9ce25SScott Long CAM_DIR_OUT, 113052c9ce25SScott Long 0, 113152c9ce25SScott Long (u_int8_t *) virtual, 113252c9ce25SScott Long length, 113352c9ce25SScott Long ada_default_timeout*1000); 113452c9ce25SScott Long if ((softc->flags & ADA_FLAG_CAN_48BIT) && 113552c9ce25SScott Long (lba + count >= ATA_MAX_28BIT_LBA || 113652c9ce25SScott Long count >= 256)) { 113705972bafSWarner Losh ata_48bit_cmd(&ataio, ATA_WRITE_DMA48, 113852c9ce25SScott Long 0, lba, count); 113952c9ce25SScott Long } else { 114005972bafSWarner Losh ata_28bit_cmd(&ataio, ATA_WRITE_DMA, 114152c9ce25SScott Long 0, lba, count); 114252c9ce25SScott Long } 114305972bafSWarner Losh error = cam_periph_runccb((union ccb *)&ataio, adaerror, 11442b31251aSWarner Losh 0, SF_NO_RECOVERY | SF_NO_RETRY, NULL); 11450191d9b3SAlexander Motin if (error != 0) 114652c9ce25SScott Long printf("Aborting dump due to I/O error.\n"); 11470191d9b3SAlexander Motin 11480191d9b3SAlexander Motin return (error); 114952c9ce25SScott Long } 115052c9ce25SScott Long 115152c9ce25SScott Long if (softc->flags & ADA_FLAG_CAN_FLUSHCACHE) { 115205972bafSWarner Losh xpt_setup_ccb(&ataio.ccb_h, periph->path, CAM_PRIORITY_NORMAL); 115352c9ce25SScott Long 115486ddf15eSWarner Losh /* 1155da908789SEnji Cooper * Tell the drive to flush its internal cache. if we 115686ddf15eSWarner Losh * can't flush in 5s we have big problems. No need to 115786ddf15eSWarner Losh * wait the default 60s to detect problems. 115886ddf15eSWarner Losh */ 115905972bafSWarner Losh ataio.ccb_h.ccb_state = ADA_CCB_DUMP; 116005972bafSWarner Losh cam_fill_ataio(&ataio, 11610191d9b3SAlexander Motin 0, 1162eed99e75SScott Long NULL, 116352c9ce25SScott Long CAM_DIR_NONE, 116452c9ce25SScott Long 0, 116552c9ce25SScott Long NULL, 116652c9ce25SScott Long 0, 1167a6e0c5daSWarner Losh 5*1000); 116852c9ce25SScott Long 116952c9ce25SScott Long if (softc->flags & ADA_FLAG_CAN_48BIT) 117005972bafSWarner Losh ata_48bit_cmd(&ataio, ATA_FLUSHCACHE48, 0, 0, 0); 117152c9ce25SScott Long else 117205972bafSWarner Losh ata_28bit_cmd(&ataio, ATA_FLUSHCACHE, 0, 0, 0); 117305972bafSWarner Losh error = cam_periph_runccb((union ccb *)&ataio, adaerror, 11742b31251aSWarner Losh 0, SF_NO_RECOVERY | SF_NO_RETRY, NULL); 11750191d9b3SAlexander Motin if (error != 0) 11760191d9b3SAlexander Motin xpt_print(periph->path, "Synchronize cache failed\n"); 117752c9ce25SScott Long } 11780191d9b3SAlexander Motin return (error); 117952c9ce25SScott Long } 118052c9ce25SScott Long 118152c9ce25SScott Long static void 118252c9ce25SScott Long adainit(void) 118352c9ce25SScott Long { 118452c9ce25SScott Long cam_status status; 118552c9ce25SScott Long 11863394d423SEdward Tomasz Napierala ada_ccb_zone = uma_zcreate("ada_ccb", 11873394d423SEdward Tomasz Napierala sizeof(struct ccb_ataio), NULL, NULL, NULL, NULL, 11883394d423SEdward Tomasz Napierala UMA_ALIGN_PTR, 0); 11893394d423SEdward Tomasz Napierala 119052c9ce25SScott Long /* 119152c9ce25SScott Long * Install a global async callback. This callback will 119252c9ce25SScott Long * receive async callbacks like "new device found". 119352c9ce25SScott Long */ 119452c9ce25SScott Long status = xpt_register_async(AC_FOUND_DEVICE, adaasync, NULL, NULL); 119552c9ce25SScott Long 119652c9ce25SScott Long if (status != CAM_REQ_CMP) { 119752c9ce25SScott Long printf("ada: Failed to attach master async callback " 119852c9ce25SScott Long "due to status 0x%x!\n", status); 119952c9ce25SScott Long } else if (ada_send_ordered) { 1200c3d0d168SAlexander Motin /* Register our event handlers */ 1201c3d0d168SAlexander Motin if ((EVENTHANDLER_REGISTER(power_suspend, adasuspend, 1202c3d0d168SAlexander Motin NULL, EVENTHANDLER_PRI_LAST)) == NULL) 1203c3d0d168SAlexander Motin printf("adainit: power event registration failed!\n"); 1204c3d0d168SAlexander Motin if ((EVENTHANDLER_REGISTER(power_resume, adaresume, 1205c3d0d168SAlexander Motin NULL, EVENTHANDLER_PRI_LAST)) == NULL) 1206c3d0d168SAlexander Motin printf("adainit: power event registration failed!\n"); 120752c9ce25SScott Long if ((EVENTHANDLER_REGISTER(shutdown_post_sync, adashutdown, 120852c9ce25SScott Long NULL, SHUTDOWN_PRI_DEFAULT)) == NULL) 120952c9ce25SScott Long printf("adainit: shutdown event registration failed!\n"); 121052c9ce25SScott Long } 121152c9ce25SScott Long } 121252c9ce25SScott Long 12130ba1e4d0SKenneth D. Merry /* 12140ba1e4d0SKenneth D. Merry * Callback from GEOM, called when it has finished cleaning up its 12150ba1e4d0SKenneth D. Merry * resources. 12160ba1e4d0SKenneth D. Merry */ 12170ba1e4d0SKenneth D. Merry static void 12180ba1e4d0SKenneth D. Merry adadiskgonecb(struct disk *dp) 12190ba1e4d0SKenneth D. Merry { 12200ba1e4d0SKenneth D. Merry struct cam_periph *periph; 12210ba1e4d0SKenneth D. Merry 12220ba1e4d0SKenneth D. Merry periph = (struct cam_periph *)dp->d_drv1; 12230ba1e4d0SKenneth D. Merry 12240ba1e4d0SKenneth D. Merry cam_periph_release(periph); 12250ba1e4d0SKenneth D. Merry } 12260ba1e4d0SKenneth D. Merry 122752c9ce25SScott Long static void 122852c9ce25SScott Long adaoninvalidate(struct cam_periph *periph) 122952c9ce25SScott Long { 123052c9ce25SScott Long struct ada_softc *softc; 123152c9ce25SScott Long 123252c9ce25SScott Long softc = (struct ada_softc *)periph->softc; 123352c9ce25SScott Long 123452c9ce25SScott Long /* 123552c9ce25SScott Long * De-register any async callbacks. 123652c9ce25SScott Long */ 123752c9ce25SScott Long xpt_register_async(0, adaasync, periph, periph->path); 1238a6e0c5daSWarner Losh #ifdef CAM_IO_STATS 1239a6e0c5daSWarner Losh softc->invalidations++; 1240a6e0c5daSWarner Losh #endif 124152c9ce25SScott Long 124252c9ce25SScott Long /* 1243b43cfe71SWarner Losh * Return all queued I/O with ENXIO. Transactions may be queued up here 1244b43cfe71SWarner Losh * for retry (since we are called while there's other transactions 1245b43cfe71SWarner Losh * pending). Any requests in the hardware will drain before ndacleanup 1246b43cfe71SWarner Losh * is called. 124752c9ce25SScott Long */ 1248a6e0c5daSWarner Losh cam_iosched_flush(softc->cam_iosched, NULL, ENXIO); 124952c9ce25SScott Long 125048ae3f4fSWarner Losh /* 125148ae3f4fSWarner Losh * Tell GEOM that we've gone away, we'll get a callback when it is 125248ae3f4fSWarner Losh * done cleaning up its resources. 125348ae3f4fSWarner Losh */ 125452c9ce25SScott Long disk_gone(softc->disk); 125552c9ce25SScott Long } 125652c9ce25SScott Long 125752c9ce25SScott Long static void 125852c9ce25SScott Long adacleanup(struct cam_periph *periph) 125952c9ce25SScott Long { 126052c9ce25SScott Long struct ada_softc *softc; 126152c9ce25SScott Long 126252c9ce25SScott Long softc = (struct ada_softc *)periph->softc; 126352c9ce25SScott Long 126452c9ce25SScott Long cam_periph_unlock(periph); 126552c9ce25SScott Long 1266a6e0c5daSWarner Losh cam_iosched_fini(softc->cam_iosched); 1267a6e0c5daSWarner Losh 126852c9ce25SScott Long /* 126952c9ce25SScott Long * If we can't free the sysctl tree, oh well... 127052c9ce25SScott Long */ 1271a6e0c5daSWarner Losh if ((softc->flags & ADA_FLAG_SCTX_INIT) != 0) { 1272a6e0c5daSWarner Losh #ifdef CAM_IO_STATS 1273a6e0c5daSWarner Losh if (sysctl_ctx_free(&softc->sysctl_stats_ctx) != 0) 1274a6e0c5daSWarner Losh xpt_print(periph->path, 1275a6e0c5daSWarner Losh "can't remove sysctl stats context\n"); 1276a6e0c5daSWarner Losh #endif 1277a6e0c5daSWarner Losh if (sysctl_ctx_free(&softc->sysctl_ctx) != 0) 1278a6e0c5daSWarner Losh xpt_print(periph->path, 1279a6e0c5daSWarner Losh "can't remove sysctl context\n"); 128052c9ce25SScott Long } 128152c9ce25SScott Long 128252c9ce25SScott Long disk_destroy(softc->disk); 128352c9ce25SScott Long callout_drain(&softc->sendordered_c); 128452c9ce25SScott Long free(softc, M_DEVBUF); 128552c9ce25SScott Long cam_periph_lock(periph); 128652c9ce25SScott Long } 128752c9ce25SScott Long 128852c9ce25SScott Long static void 1289a6e0c5daSWarner Losh adasetdeletemethod(struct ada_softc *softc) 1290a6e0c5daSWarner Losh { 1291a6e0c5daSWarner Losh 1292a6e0c5daSWarner Losh if (softc->flags & ADA_FLAG_CAN_NCQ_TRIM) 1293a6e0c5daSWarner Losh softc->delete_method = ADA_DELETE_NCQ_DSM_TRIM; 1294a6e0c5daSWarner Losh else if (softc->flags & ADA_FLAG_CAN_TRIM) 1295a6e0c5daSWarner Losh softc->delete_method = ADA_DELETE_DSM_TRIM; 1296a6e0c5daSWarner Losh else if ((softc->flags & ADA_FLAG_CAN_CFA) && !(softc->flags & ADA_FLAG_CAN_48BIT)) 1297a6e0c5daSWarner Losh softc->delete_method = ADA_DELETE_CFA_ERASE; 1298a6e0c5daSWarner Losh else 1299a6e0c5daSWarner Losh softc->delete_method = ADA_DELETE_NONE; 1300a6e0c5daSWarner Losh } 1301a6e0c5daSWarner Losh 1302a6e0c5daSWarner Losh static void 130352c9ce25SScott Long adaasync(void *callback_arg, u_int32_t code, 130452c9ce25SScott Long struct cam_path *path, void *arg) 130552c9ce25SScott Long { 1306581b2e78SAlexander Motin struct ccb_getdev cgd; 130752c9ce25SScott Long struct cam_periph *periph; 1308f513d14cSAlexander Motin struct ada_softc *softc; 130952c9ce25SScott Long 131052c9ce25SScott Long periph = (struct cam_periph *)callback_arg; 131152c9ce25SScott Long switch (code) { 131252c9ce25SScott Long case AC_FOUND_DEVICE: 131352c9ce25SScott Long { 131452c9ce25SScott Long struct ccb_getdev *cgd; 131552c9ce25SScott Long cam_status status; 131652c9ce25SScott Long 131752c9ce25SScott Long cgd = (struct ccb_getdev *)arg; 131852c9ce25SScott Long if (cgd == NULL) 131952c9ce25SScott Long break; 132052c9ce25SScott Long 132152c9ce25SScott Long if (cgd->protocol != PROTO_ATA) 132252c9ce25SScott Long break; 132352c9ce25SScott Long 132452c9ce25SScott Long /* 132552c9ce25SScott Long * Allocate a peripheral instance for 132652c9ce25SScott Long * this device and start the probe 132752c9ce25SScott Long * process. 132852c9ce25SScott Long */ 132952c9ce25SScott Long status = cam_periph_alloc(adaregister, adaoninvalidate, 133052c9ce25SScott Long adacleanup, adastart, 133152c9ce25SScott Long "ada", CAM_PERIPH_BIO, 1332227d67aaSAlexander Motin path, adaasync, 133352c9ce25SScott Long AC_FOUND_DEVICE, cgd); 133452c9ce25SScott Long 133552c9ce25SScott Long if (status != CAM_REQ_CMP 133652c9ce25SScott Long && status != CAM_REQ_INPROG) 133752c9ce25SScott Long printf("adaasync: Unable to attach to new device " 133852c9ce25SScott Long "due to status 0x%x\n", status); 133952c9ce25SScott Long break; 134052c9ce25SScott Long } 1341581b2e78SAlexander Motin case AC_GETDEV_CHANGED: 1342581b2e78SAlexander Motin { 1343581b2e78SAlexander Motin softc = (struct ada_softc *)periph->softc; 1344ec5325dbSEdward Tomasz Napierala memset(&cgd, 0, sizeof(cgd)); 1345581b2e78SAlexander Motin xpt_setup_ccb(&cgd.ccb_h, periph->path, CAM_PRIORITY_NORMAL); 1346581b2e78SAlexander Motin cgd.ccb_h.func_code = XPT_GDEV_TYPE; 1347581b2e78SAlexander Motin xpt_action((union ccb *)&cgd); 1348581b2e78SAlexander Motin 1349a6e0c5daSWarner Losh /* 135076d843daSAlexander Motin * Update our information based on the new Identify data. 1351a6e0c5daSWarner Losh */ 13529a6844d5SKenneth D. Merry adasetflags(softc, &cgd); 135376d843daSAlexander Motin adasetgeom(softc, &cgd); 135476d843daSAlexander Motin disk_resize(softc->disk, M_NOWAIT); 1355581b2e78SAlexander Motin break; 1356581b2e78SAlexander Motin } 13573089bb2eSAlexander Motin case AC_ADVINFO_CHANGED: 13583089bb2eSAlexander Motin { 13593089bb2eSAlexander Motin uintptr_t buftype; 13603089bb2eSAlexander Motin 13613089bb2eSAlexander Motin buftype = (uintptr_t)arg; 13623089bb2eSAlexander Motin if (buftype == CDAI_TYPE_PHYS_PATH) { 13633089bb2eSAlexander Motin struct ada_softc *softc; 13643089bb2eSAlexander Motin 13653089bb2eSAlexander Motin softc = periph->softc; 13663089bb2eSAlexander Motin disk_attr_changed(softc->disk, "GEOM::physpath", 13673089bb2eSAlexander Motin M_NOWAIT); 13683089bb2eSAlexander Motin } 13693089bb2eSAlexander Motin break; 13703089bb2eSAlexander Motin } 1371f513d14cSAlexander Motin case AC_SENT_BDR: 1372f513d14cSAlexander Motin case AC_BUS_RESET: 1373f513d14cSAlexander Motin { 1374f513d14cSAlexander Motin softc = (struct ada_softc *)periph->softc; 1375f513d14cSAlexander Motin if (softc->state != ADA_STATE_NORMAL) 1376f513d14cSAlexander Motin break; 13779a6844d5SKenneth D. Merry if (ADA_RA >= 0 && softc->flags & ADA_FLAG_CAN_RAHEAD) 13781ed6aaf9SAlexander Motin softc->state = ADA_STATE_RAHEAD; 13791d9aaa86SAlexander Motin else if (ADA_WC >= 0 && softc->flags & ADA_FLAG_CAN_WCACHE) 1380f513d14cSAlexander Motin softc->state = ADA_STATE_WCACHE; 1381600fd98fSKenneth D. Merry else if ((softc->flags & ADA_FLAG_CAN_LOG) 1382600fd98fSKenneth D. Merry && (softc->zone_mode != ADA_ZONE_NONE)) 13839a6844d5SKenneth D. Merry softc->state = ADA_STATE_LOGDIR; 13841ed6aaf9SAlexander Motin else 13851ed6aaf9SAlexander Motin break; 138699e7a4adSScott Long if (cam_periph_acquire(periph) != 0) 1387227d67aaSAlexander Motin softc->state = ADA_STATE_NORMAL; 1388227d67aaSAlexander Motin else 1389f513d14cSAlexander Motin xpt_schedule(periph, CAM_PRIORITY_DEV); 139052c9ce25SScott Long break; 139152c9ce25SScott Long } 1392ae1955cdSWarner Losh default: 1393ae1955cdSWarner Losh break; 1394ae1955cdSWarner Losh } 1395ae1955cdSWarner Losh cam_periph_async(periph, code, path, arg); 139652c9ce25SScott Long } 139752c9ce25SScott Long 13989a6844d5SKenneth D. Merry static int 13999a6844d5SKenneth D. Merry adazonemodesysctl(SYSCTL_HANDLER_ARGS) 14009a6844d5SKenneth D. Merry { 14019a6844d5SKenneth D. Merry char tmpbuf[40]; 14029a6844d5SKenneth D. Merry struct ada_softc *softc; 14039a6844d5SKenneth D. Merry int error; 14049a6844d5SKenneth D. Merry 14059a6844d5SKenneth D. Merry softc = (struct ada_softc *)arg1; 14069a6844d5SKenneth D. Merry 14079a6844d5SKenneth D. Merry switch (softc->zone_mode) { 14089a6844d5SKenneth D. Merry case ADA_ZONE_DRIVE_MANAGED: 14099a6844d5SKenneth D. Merry snprintf(tmpbuf, sizeof(tmpbuf), "Drive Managed"); 14109a6844d5SKenneth D. Merry break; 14119a6844d5SKenneth D. Merry case ADA_ZONE_HOST_AWARE: 14129a6844d5SKenneth D. Merry snprintf(tmpbuf, sizeof(tmpbuf), "Host Aware"); 14139a6844d5SKenneth D. Merry break; 14149a6844d5SKenneth D. Merry case ADA_ZONE_HOST_MANAGED: 14159a6844d5SKenneth D. Merry snprintf(tmpbuf, sizeof(tmpbuf), "Host Managed"); 14169a6844d5SKenneth D. Merry break; 14179a6844d5SKenneth D. Merry case ADA_ZONE_NONE: 14189a6844d5SKenneth D. Merry default: 14199a6844d5SKenneth D. Merry snprintf(tmpbuf, sizeof(tmpbuf), "Not Zoned"); 14209a6844d5SKenneth D. Merry break; 14219a6844d5SKenneth D. Merry } 14229a6844d5SKenneth D. Merry 14239a6844d5SKenneth D. Merry error = sysctl_handle_string(oidp, tmpbuf, sizeof(tmpbuf), req); 14249a6844d5SKenneth D. Merry 14259a6844d5SKenneth D. Merry return (error); 14269a6844d5SKenneth D. Merry } 14279a6844d5SKenneth D. Merry 14289a6844d5SKenneth D. Merry static int 14299a6844d5SKenneth D. Merry adazonesupsysctl(SYSCTL_HANDLER_ARGS) 14309a6844d5SKenneth D. Merry { 14319a6844d5SKenneth D. Merry char tmpbuf[180]; 14329a6844d5SKenneth D. Merry struct ada_softc *softc; 14339a6844d5SKenneth D. Merry struct sbuf sb; 14349a6844d5SKenneth D. Merry int error, first; 14359a6844d5SKenneth D. Merry unsigned int i; 14369a6844d5SKenneth D. Merry 14379a6844d5SKenneth D. Merry softc = (struct ada_softc *)arg1; 14389a6844d5SKenneth D. Merry 14399a6844d5SKenneth D. Merry error = 0; 14409a6844d5SKenneth D. Merry first = 1; 14419a6844d5SKenneth D. Merry sbuf_new(&sb, tmpbuf, sizeof(tmpbuf), 0); 14429a6844d5SKenneth D. Merry 14439a6844d5SKenneth D. Merry for (i = 0; i < sizeof(ada_zone_desc_table) / 14449a6844d5SKenneth D. Merry sizeof(ada_zone_desc_table[0]); i++) { 14459a6844d5SKenneth D. Merry if (softc->zone_flags & ada_zone_desc_table[i].value) { 14469a6844d5SKenneth D. Merry if (first == 0) 14479a6844d5SKenneth D. Merry sbuf_printf(&sb, ", "); 14489a6844d5SKenneth D. Merry else 14499a6844d5SKenneth D. Merry first = 0; 14509a6844d5SKenneth D. Merry sbuf_cat(&sb, ada_zone_desc_table[i].desc); 14519a6844d5SKenneth D. Merry } 14529a6844d5SKenneth D. Merry } 14539a6844d5SKenneth D. Merry 14549a6844d5SKenneth D. Merry if (first == 1) 14559a6844d5SKenneth D. Merry sbuf_printf(&sb, "None"); 14569a6844d5SKenneth D. Merry 14579a6844d5SKenneth D. Merry sbuf_finish(&sb); 14589a6844d5SKenneth D. Merry 14599a6844d5SKenneth D. Merry error = sysctl_handle_string(oidp, sbuf_data(&sb), sbuf_len(&sb), req); 14609a6844d5SKenneth D. Merry 14619a6844d5SKenneth D. Merry return (error); 14629a6844d5SKenneth D. Merry } 14639a6844d5SKenneth D. Merry 146452c9ce25SScott Long static void 146552c9ce25SScott Long adasysctlinit(void *context, int pending) 146652c9ce25SScott Long { 146752c9ce25SScott Long struct cam_periph *periph; 146852c9ce25SScott Long struct ada_softc *softc; 14694d470952SAlexander Motin char tmpstr[32], tmpstr2[16]; 147052c9ce25SScott Long 147152c9ce25SScott Long periph = (struct cam_periph *)context; 1472e3a6d3a4SAlexander Motin 1473e3a6d3a4SAlexander Motin /* periph was held for us when this task was enqueued */ 14747338ef1aSAlexander Motin if ((periph->flags & CAM_PERIPH_INVALID) != 0) { 1475e3a6d3a4SAlexander Motin cam_periph_release(periph); 147652c9ce25SScott Long return; 1477e3a6d3a4SAlexander Motin } 147852c9ce25SScott Long 147952c9ce25SScott Long softc = (struct ada_softc *)periph->softc; 148052c9ce25SScott Long snprintf(tmpstr, sizeof(tmpstr), "CAM ADA unit %d",periph->unit_number); 148152c9ce25SScott Long snprintf(tmpstr2, sizeof(tmpstr2), "%d", periph->unit_number); 148252c9ce25SScott Long 148352c9ce25SScott Long sysctl_ctx_init(&softc->sysctl_ctx); 148452c9ce25SScott Long softc->flags |= ADA_FLAG_SCTX_INIT; 14854c484fd2SEd Schouten softc->sysctl_tree = SYSCTL_ADD_NODE_WITH_LABEL(&softc->sysctl_ctx, 148652c9ce25SScott Long SYSCTL_STATIC_CHILDREN(_kern_cam_ada), OID_AUTO, tmpstr2, 14877029da5cSPawel Biernacki CTLFLAG_RD | CTLFLAG_MPSAFE, 0, tmpstr, "device_index"); 148852c9ce25SScott Long if (softc->sysctl_tree == NULL) { 148952c9ce25SScott Long printf("adasysctlinit: unable to allocate sysctl tree\n"); 149052c9ce25SScott Long cam_periph_release(periph); 149152c9ce25SScott Long return; 149252c9ce25SScott Long } 149352c9ce25SScott Long 1494a6e0c5daSWarner Losh SYSCTL_ADD_PROC(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), 14957029da5cSPawel Biernacki OID_AUTO, "delete_method", 1496303477d3SAlexander Motin CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, 1497a6e0c5daSWarner Losh softc, 0, adadeletemethodsysctl, "A", 1498a6e0c5daSWarner Losh "BIO_DELETE execution method"); 1499ea657f2cSWarner Losh SYSCTL_ADD_UQUAD(&softc->sysctl_ctx, 1500ea657f2cSWarner Losh SYSCTL_CHILDREN(softc->sysctl_tree), OID_AUTO, 1501ea657f2cSWarner Losh "trim_count", CTLFLAG_RD, &softc->trim_count, 1502ea657f2cSWarner Losh "Total number of dsm commands sent"); 1503ea657f2cSWarner Losh SYSCTL_ADD_UQUAD(&softc->sysctl_ctx, 1504ea657f2cSWarner Losh SYSCTL_CHILDREN(softc->sysctl_tree), OID_AUTO, 1505ea657f2cSWarner Losh "trim_ranges", CTLFLAG_RD, &softc->trim_ranges, 1506ea657f2cSWarner Losh "Total number of ranges in dsm commands"); 1507ea657f2cSWarner Losh SYSCTL_ADD_UQUAD(&softc->sysctl_ctx, 1508ea657f2cSWarner Losh SYSCTL_CHILDREN(softc->sysctl_tree), OID_AUTO, 1509ea657f2cSWarner Losh "trim_lbas", CTLFLAG_RD, &softc->trim_lbas, 1510ea657f2cSWarner Losh "Total lbas in the dsm commands sent"); 1511e3a6d3a4SAlexander Motin SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), 15121ed6aaf9SAlexander Motin OID_AUTO, "read_ahead", CTLFLAG_RW | CTLFLAG_MPSAFE, 15131ed6aaf9SAlexander Motin &softc->read_ahead, 0, "Enable disk read ahead."); 15141ed6aaf9SAlexander Motin SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), 1515e3a6d3a4SAlexander Motin OID_AUTO, "write_cache", CTLFLAG_RW | CTLFLAG_MPSAFE, 1516e3a6d3a4SAlexander Motin &softc->write_cache, 0, "Enable disk write cache."); 15179a6844d5SKenneth D. Merry SYSCTL_ADD_PROC(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), 15187029da5cSPawel Biernacki OID_AUTO, "zone_mode", 1519303477d3SAlexander Motin CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, 15209a6844d5SKenneth D. Merry softc, 0, adazonemodesysctl, "A", 15219a6844d5SKenneth D. Merry "Zone Mode"); 15229a6844d5SKenneth D. Merry SYSCTL_ADD_PROC(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), 15237029da5cSPawel Biernacki OID_AUTO, "zone_support", 1524303477d3SAlexander Motin CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, 15259a6844d5SKenneth D. Merry softc, 0, adazonesupsysctl, "A", 15269a6844d5SKenneth D. Merry "Zone Support"); 15279a6844d5SKenneth D. Merry SYSCTL_ADD_UQUAD(&softc->sysctl_ctx, 15289a6844d5SKenneth D. Merry SYSCTL_CHILDREN(softc->sysctl_tree), OID_AUTO, 15299a6844d5SKenneth D. Merry "optimal_seq_zones", CTLFLAG_RD, &softc->optimal_seq_zones, 15309a6844d5SKenneth D. Merry "Optimal Number of Open Sequential Write Preferred Zones"); 15319a6844d5SKenneth D. Merry SYSCTL_ADD_UQUAD(&softc->sysctl_ctx, 15329a6844d5SKenneth D. Merry SYSCTL_CHILDREN(softc->sysctl_tree), OID_AUTO, 15339a6844d5SKenneth D. Merry "optimal_nonseq_zones", CTLFLAG_RD, 15349a6844d5SKenneth D. Merry &softc->optimal_nonseq_zones, 15359a6844d5SKenneth D. Merry "Optimal Number of Non-Sequentially Written Sequential Write " 15369a6844d5SKenneth D. Merry "Preferred Zones"); 15379a6844d5SKenneth D. Merry SYSCTL_ADD_UQUAD(&softc->sysctl_ctx, 15389a6844d5SKenneth D. Merry SYSCTL_CHILDREN(softc->sysctl_tree), OID_AUTO, 15399a6844d5SKenneth D. Merry "max_seq_zones", CTLFLAG_RD, &softc->max_seq_zones, 15409a6844d5SKenneth D. Merry "Maximum Number of Open Sequential Write Required Zones"); 1541aeab0812SWarner Losh SYSCTL_ADD_PROC(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), 1542aeab0812SWarner Losh OID_AUTO, "flags", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, 1543aeab0812SWarner Losh softc, 0, adaflagssysctl, "A", 1544aeab0812SWarner Losh "Flags for drive"); 1545cf3ff63eSWarner Losh SYSCTL_ADD_PROC(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), 1546cf3ff63eSWarner Losh OID_AUTO, "unmapped_io", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, 1547cf3ff63eSWarner Losh &softc->flags, (u_int)ADA_FLAG_UNMAPPEDIO, adabitsysctl, "I", 1548cf3ff63eSWarner Losh "Unmapped I/O support *DEPRECATED* gone in FreeBSD 14"); 154996eb32bfSWarner Losh SYSCTL_ADD_PROC(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), 155096eb32bfSWarner Losh OID_AUTO, "rotating", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, 155196eb32bfSWarner Losh &softc->flags, (u_int)ADA_FLAG_ROTATING, adabitsysctl, "I", 155296eb32bfSWarner Losh "Rotating media *DEPRECATED* gone in FreeBSD 14"); 15539a6844d5SKenneth D. Merry 1554d38677d2SWarner Losh #ifdef CAM_TEST_FAILURE 1555e3a6d3a4SAlexander Motin /* 1556e3a6d3a4SAlexander Motin * Add a 'door bell' sysctl which allows one to set it from userland 1557e3a6d3a4SAlexander Motin * and cause something bad to happen. For the moment, we only allow 1558e3a6d3a4SAlexander Motin * whacking the next read or write. 1559e3a6d3a4SAlexander Motin */ 1560e3a6d3a4SAlexander Motin SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), 1561e3a6d3a4SAlexander Motin OID_AUTO, "force_read_error", CTLFLAG_RW | CTLFLAG_MPSAFE, 1562e3a6d3a4SAlexander Motin &softc->force_read_error, 0, 1563e3a6d3a4SAlexander Motin "Force a read error for the next N reads."); 1564e3a6d3a4SAlexander Motin SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), 1565e3a6d3a4SAlexander Motin OID_AUTO, "force_write_error", CTLFLAG_RW | CTLFLAG_MPSAFE, 1566e3a6d3a4SAlexander Motin &softc->force_write_error, 0, 1567e3a6d3a4SAlexander Motin "Force a write error for the next N writes."); 1568e3a6d3a4SAlexander Motin SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), 1569e3a6d3a4SAlexander Motin OID_AUTO, "periodic_read_error", CTLFLAG_RW | CTLFLAG_MPSAFE, 1570e3a6d3a4SAlexander Motin &softc->periodic_read_error, 0, 1571e3a6d3a4SAlexander Motin "Force a read error every N reads (don't set too low)."); 1572d38677d2SWarner Losh SYSCTL_ADD_PROC(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), 1573d38677d2SWarner Losh OID_AUTO, "invalidate", CTLTYPE_U64 | CTLFLAG_RW | CTLFLAG_MPSAFE, 1574d38677d2SWarner Losh periph, 0, cam_periph_invalidate_sysctl, "I", 1575d38677d2SWarner Losh "Write 1 to invalidate the drive immediately"); 1576e3a6d3a4SAlexander Motin #endif 1577a6e0c5daSWarner Losh 1578a6e0c5daSWarner Losh #ifdef CAM_IO_STATS 1579a6e0c5daSWarner Losh softc->sysctl_stats_tree = SYSCTL_ADD_NODE(&softc->sysctl_stats_ctx, 1580a6e0c5daSWarner Losh SYSCTL_CHILDREN(softc->sysctl_tree), OID_AUTO, "stats", 15817029da5cSPawel Biernacki CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "Statistics"); 1582a6e0c5daSWarner Losh SYSCTL_ADD_INT(&softc->sysctl_stats_ctx, 1583a6e0c5daSWarner Losh SYSCTL_CHILDREN(softc->sysctl_stats_tree), 1584a6e0c5daSWarner Losh OID_AUTO, "timeouts", CTLFLAG_RD | CTLFLAG_MPSAFE, 1585a6e0c5daSWarner Losh &softc->timeouts, 0, 1586a6e0c5daSWarner Losh "Device timeouts reported by the SIM"); 1587a6e0c5daSWarner Losh SYSCTL_ADD_INT(&softc->sysctl_stats_ctx, 1588a6e0c5daSWarner Losh SYSCTL_CHILDREN(softc->sysctl_stats_tree), 1589a6e0c5daSWarner Losh OID_AUTO, "errors", CTLFLAG_RD | CTLFLAG_MPSAFE, 1590a6e0c5daSWarner Losh &softc->errors, 0, 1591a6e0c5daSWarner Losh "Transport errors reported by the SIM."); 1592a6e0c5daSWarner Losh SYSCTL_ADD_INT(&softc->sysctl_stats_ctx, 1593a6e0c5daSWarner Losh SYSCTL_CHILDREN(softc->sysctl_stats_tree), 1594a6e0c5daSWarner Losh OID_AUTO, "pack_invalidations", CTLFLAG_RD | CTLFLAG_MPSAFE, 1595a6e0c5daSWarner Losh &softc->invalidations, 0, 1596a6e0c5daSWarner Losh "Device pack invalidations."); 1597a6e0c5daSWarner Losh #endif 1598a6e0c5daSWarner Losh 1599a6e0c5daSWarner Losh cam_iosched_sysctl_init(softc->cam_iosched, &softc->sysctl_ctx, 1600a6e0c5daSWarner Losh softc->sysctl_tree); 1601a6e0c5daSWarner Losh 160252c9ce25SScott Long cam_periph_release(periph); 160352c9ce25SScott Long } 160452c9ce25SScott Long 1605416494d7SJustin T. Gibbs static int 1606416494d7SJustin T. Gibbs adagetattr(struct bio *bp) 1607416494d7SJustin T. Gibbs { 16086884b662SAlexander Motin int ret; 1609416494d7SJustin T. Gibbs struct cam_periph *periph; 1610416494d7SJustin T. Gibbs 161113532153SScott Long if (g_handleattr_int(bp, "GEOM::canspeedup", ada_enable_biospeedup)) 161213532153SScott Long return (EJUSTRETURN); 161313532153SScott Long 1614416494d7SJustin T. Gibbs periph = (struct cam_periph *)bp->bio_disk->d_drv1; 16156884b662SAlexander Motin cam_periph_lock(periph); 1616416494d7SJustin T. Gibbs ret = xpt_getattr(bp->bio_data, bp->bio_length, bp->bio_attribute, 1617416494d7SJustin T. Gibbs periph->path); 16186884b662SAlexander Motin cam_periph_unlock(periph); 1619416494d7SJustin T. Gibbs if (ret == 0) 1620416494d7SJustin T. Gibbs bp->bio_completed = bp->bio_length; 1621416494d7SJustin T. Gibbs return ret; 1622416494d7SJustin T. Gibbs } 1623416494d7SJustin T. Gibbs 1624a6e0c5daSWarner Losh static int 1625a6e0c5daSWarner Losh adadeletemethodsysctl(SYSCTL_HANDLER_ARGS) 1626a6e0c5daSWarner Losh { 1627a6e0c5daSWarner Losh char buf[16]; 1628a6e0c5daSWarner Losh const char *p; 1629a6e0c5daSWarner Losh struct ada_softc *softc; 1630a6e0c5daSWarner Losh int i, error, value, methods; 1631a6e0c5daSWarner Losh 1632a6e0c5daSWarner Losh softc = (struct ada_softc *)arg1; 1633a6e0c5daSWarner Losh 1634a6e0c5daSWarner Losh value = softc->delete_method; 1635a6e0c5daSWarner Losh if (value < 0 || value > ADA_DELETE_MAX) 1636a6e0c5daSWarner Losh p = "UNKNOWN"; 1637a6e0c5daSWarner Losh else 1638a6e0c5daSWarner Losh p = ada_delete_method_names[value]; 1639a6e0c5daSWarner Losh strncpy(buf, p, sizeof(buf)); 1640a6e0c5daSWarner Losh error = sysctl_handle_string(oidp, buf, sizeof(buf), req); 1641a6e0c5daSWarner Losh if (error != 0 || req->newptr == NULL) 1642a6e0c5daSWarner Losh return (error); 1643a6e0c5daSWarner Losh methods = 1 << ADA_DELETE_DISABLE; 1644a6e0c5daSWarner Losh if ((softc->flags & ADA_FLAG_CAN_CFA) && 1645a6e0c5daSWarner Losh !(softc->flags & ADA_FLAG_CAN_48BIT)) 1646a6e0c5daSWarner Losh methods |= 1 << ADA_DELETE_CFA_ERASE; 1647a6e0c5daSWarner Losh if (softc->flags & ADA_FLAG_CAN_TRIM) 1648a6e0c5daSWarner Losh methods |= 1 << ADA_DELETE_DSM_TRIM; 1649a6e0c5daSWarner Losh if (softc->flags & ADA_FLAG_CAN_NCQ_TRIM) 1650a6e0c5daSWarner Losh methods |= 1 << ADA_DELETE_NCQ_DSM_TRIM; 1651a6e0c5daSWarner Losh for (i = 0; i <= ADA_DELETE_MAX; i++) { 1652a6e0c5daSWarner Losh if (!(methods & (1 << i)) || 1653a6e0c5daSWarner Losh strcmp(buf, ada_delete_method_names[i]) != 0) 1654a6e0c5daSWarner Losh continue; 1655a6e0c5daSWarner Losh softc->delete_method = i; 1656a6e0c5daSWarner Losh return (0); 1657a6e0c5daSWarner Losh } 1658a6e0c5daSWarner Losh return (EINVAL); 1659a6e0c5daSWarner Losh } 1660a6e0c5daSWarner Losh 1661aeab0812SWarner Losh static int 1662cf3ff63eSWarner Losh adabitsysctl(SYSCTL_HANDLER_ARGS) 1663cf3ff63eSWarner Losh { 1664cf3ff63eSWarner Losh u_int *flags = arg1; 1665cf3ff63eSWarner Losh u_int test = arg2; 1666cf3ff63eSWarner Losh int tmpout, error; 1667cf3ff63eSWarner Losh 1668cf3ff63eSWarner Losh tmpout = !!(*flags & test); 1669cf3ff63eSWarner Losh error = SYSCTL_OUT(req, &tmpout, sizeof(tmpout)); 1670cf3ff63eSWarner Losh if (error || !req->newptr) 1671cf3ff63eSWarner Losh return (error); 1672cf3ff63eSWarner Losh 1673cf3ff63eSWarner Losh return (EPERM); 1674cf3ff63eSWarner Losh } 1675cf3ff63eSWarner Losh 1676cf3ff63eSWarner Losh static int 1677aeab0812SWarner Losh adaflagssysctl(SYSCTL_HANDLER_ARGS) 1678aeab0812SWarner Losh { 1679aeab0812SWarner Losh struct sbuf sbuf; 1680aeab0812SWarner Losh struct ada_softc *softc = arg1; 1681aeab0812SWarner Losh int error; 1682aeab0812SWarner Losh 1683aeab0812SWarner Losh sbuf_new_for_sysctl(&sbuf, NULL, 0, req); 1684aeab0812SWarner Losh if (softc->flags != 0) 1685aeab0812SWarner Losh sbuf_printf(&sbuf, "0x%b", (unsigned)softc->flags, ADA_FLAG_STRING); 1686aeab0812SWarner Losh else 1687aeab0812SWarner Losh sbuf_printf(&sbuf, "0"); 1688aeab0812SWarner Losh error = sbuf_finish(&sbuf); 1689aeab0812SWarner Losh sbuf_delete(&sbuf); 1690aeab0812SWarner Losh 1691aeab0812SWarner Losh return (error); 1692aeab0812SWarner Losh } 1693aeab0812SWarner Losh 16949a6844d5SKenneth D. Merry static void 16959a6844d5SKenneth D. Merry adasetflags(struct ada_softc *softc, struct ccb_getdev *cgd) 16969a6844d5SKenneth D. Merry { 16979a6844d5SKenneth D. Merry if ((cgd->ident_data.capabilities1 & ATA_SUPPORT_DMA) && 16989a6844d5SKenneth D. Merry (cgd->inq_flags & SID_DMA)) 16999a6844d5SKenneth D. Merry softc->flags |= ADA_FLAG_CAN_DMA; 17009a6844d5SKenneth D. Merry else 17019a6844d5SKenneth D. Merry softc->flags &= ~ADA_FLAG_CAN_DMA; 17029a6844d5SKenneth D. Merry 17039a6844d5SKenneth D. Merry if (cgd->ident_data.support.command2 & ATA_SUPPORT_ADDRESS48) { 17049a6844d5SKenneth D. Merry softc->flags |= ADA_FLAG_CAN_48BIT; 17059a6844d5SKenneth D. Merry if (cgd->inq_flags & SID_DMA48) 17069a6844d5SKenneth D. Merry softc->flags |= ADA_FLAG_CAN_DMA48; 17079a6844d5SKenneth D. Merry else 17089a6844d5SKenneth D. Merry softc->flags &= ~ADA_FLAG_CAN_DMA48; 17099a6844d5SKenneth D. Merry } else 17109a6844d5SKenneth D. Merry softc->flags &= ~(ADA_FLAG_CAN_48BIT | ADA_FLAG_CAN_DMA48); 17119a6844d5SKenneth D. Merry 17129a6844d5SKenneth D. Merry if (cgd->ident_data.support.command2 & ATA_SUPPORT_FLUSHCACHE) 17139a6844d5SKenneth D. Merry softc->flags |= ADA_FLAG_CAN_FLUSHCACHE; 17149a6844d5SKenneth D. Merry else 17159a6844d5SKenneth D. Merry softc->flags &= ~ADA_FLAG_CAN_FLUSHCACHE; 17169a6844d5SKenneth D. Merry 17179a6844d5SKenneth D. Merry if (cgd->ident_data.support.command1 & ATA_SUPPORT_POWERMGT) 17189a6844d5SKenneth D. Merry softc->flags |= ADA_FLAG_CAN_POWERMGT; 17199a6844d5SKenneth D. Merry else 17209a6844d5SKenneth D. Merry softc->flags &= ~ADA_FLAG_CAN_POWERMGT; 17219a6844d5SKenneth D. Merry 17229a6844d5SKenneth D. Merry if ((cgd->ident_data.satacapabilities & ATA_SUPPORT_NCQ) && 17239a6844d5SKenneth D. Merry (cgd->inq_flags & SID_DMA) && (cgd->inq_flags & SID_CmdQue)) 17249a6844d5SKenneth D. Merry softc->flags |= ADA_FLAG_CAN_NCQ; 17259a6844d5SKenneth D. Merry else 17269a6844d5SKenneth D. Merry softc->flags &= ~ADA_FLAG_CAN_NCQ; 17279a6844d5SKenneth D. Merry 17289a6844d5SKenneth D. Merry if ((cgd->ident_data.support_dsm & ATA_SUPPORT_DSM_TRIM) && 172976d843daSAlexander Motin (cgd->inq_flags & SID_DMA) && 173076d843daSAlexander Motin (softc->quirks & ADA_Q_NO_TRIM) == 0) { 17319a6844d5SKenneth D. Merry softc->flags |= ADA_FLAG_CAN_TRIM; 17329a6844d5SKenneth D. Merry softc->trim_max_ranges = TRIM_MAX_RANGES; 17339a6844d5SKenneth D. Merry if (cgd->ident_data.max_dsm_blocks != 0) { 17349a6844d5SKenneth D. Merry softc->trim_max_ranges = 17359a6844d5SKenneth D. Merry min(cgd->ident_data.max_dsm_blocks * 17369a6844d5SKenneth D. Merry ATA_DSM_BLK_RANGES, softc->trim_max_ranges); 17379a6844d5SKenneth D. Merry } 17389a6844d5SKenneth D. Merry /* 17399a6844d5SKenneth D. Merry * If we can do RCVSND_FPDMA_QUEUED commands, we may be able 17409a6844d5SKenneth D. Merry * to do NCQ trims, if we support trims at all. We also need 17419a6844d5SKenneth D. Merry * support from the SIM to do things properly. Perhaps we 17429a6844d5SKenneth D. Merry * should look at log 13 dword 0 bit 0 and dword 1 bit 0 are 17439a6844d5SKenneth D. Merry * set too... 17449a6844d5SKenneth D. Merry */ 17459a6844d5SKenneth D. Merry if ((softc->quirks & ADA_Q_NCQ_TRIM_BROKEN) == 0 && 17469a6844d5SKenneth D. Merry (softc->flags & ADA_FLAG_PIM_ATA_EXT) != 0 && 17479a6844d5SKenneth D. Merry (cgd->ident_data.satacapabilities2 & 17489a6844d5SKenneth D. Merry ATA_SUPPORT_RCVSND_FPDMA_QUEUED) != 0 && 17499a6844d5SKenneth D. Merry (softc->flags & ADA_FLAG_CAN_TRIM) != 0) 17509a6844d5SKenneth D. Merry softc->flags |= ADA_FLAG_CAN_NCQ_TRIM; 17519a6844d5SKenneth D. Merry else 17529a6844d5SKenneth D. Merry softc->flags &= ~ADA_FLAG_CAN_NCQ_TRIM; 17539a6844d5SKenneth D. Merry } else 17549a6844d5SKenneth D. Merry softc->flags &= ~(ADA_FLAG_CAN_TRIM | ADA_FLAG_CAN_NCQ_TRIM); 17559a6844d5SKenneth D. Merry 17569a6844d5SKenneth D. Merry if (cgd->ident_data.support.command2 & ATA_SUPPORT_CFA) 17579a6844d5SKenneth D. Merry softc->flags |= ADA_FLAG_CAN_CFA; 17589a6844d5SKenneth D. Merry else 17599a6844d5SKenneth D. Merry softc->flags &= ~ADA_FLAG_CAN_CFA; 17609a6844d5SKenneth D. Merry 17619a6844d5SKenneth D. Merry /* 17629a6844d5SKenneth D. Merry * Now that we've set the appropriate flags, setup the delete 17639a6844d5SKenneth D. Merry * method. 17649a6844d5SKenneth D. Merry */ 17659a6844d5SKenneth D. Merry adasetdeletemethod(softc); 17669a6844d5SKenneth D. Merry 1767600fd98fSKenneth D. Merry if ((cgd->ident_data.support.extension & ATA_SUPPORT_GENLOG) 1768600fd98fSKenneth D. Merry && ((softc->quirks & ADA_Q_LOG_BROKEN) == 0)) 17699a6844d5SKenneth D. Merry softc->flags |= ADA_FLAG_CAN_LOG; 17709a6844d5SKenneth D. Merry else 17719a6844d5SKenneth D. Merry softc->flags &= ~ADA_FLAG_CAN_LOG; 17729a6844d5SKenneth D. Merry 17739a6844d5SKenneth D. Merry if ((cgd->ident_data.support3 & ATA_SUPPORT_ZONE_MASK) == 17749a6844d5SKenneth D. Merry ATA_SUPPORT_ZONE_HOST_AWARE) 17759a6844d5SKenneth D. Merry softc->zone_mode = ADA_ZONE_HOST_AWARE; 1776600fd98fSKenneth D. Merry else if (((cgd->ident_data.support3 & ATA_SUPPORT_ZONE_MASK) == 17779a6844d5SKenneth D. Merry ATA_SUPPORT_ZONE_DEV_MANAGED) 1778600fd98fSKenneth D. Merry || (softc->quirks & ADA_Q_SMR_DM)) 17799a6844d5SKenneth D. Merry softc->zone_mode = ADA_ZONE_DRIVE_MANAGED; 17809a6844d5SKenneth D. Merry else 17819a6844d5SKenneth D. Merry softc->zone_mode = ADA_ZONE_NONE; 17829a6844d5SKenneth D. Merry 17839a6844d5SKenneth D. Merry if (cgd->ident_data.support.command1 & ATA_SUPPORT_LOOKAHEAD) 17849a6844d5SKenneth D. Merry softc->flags |= ADA_FLAG_CAN_RAHEAD; 17859a6844d5SKenneth D. Merry else 17869a6844d5SKenneth D. Merry softc->flags &= ~ADA_FLAG_CAN_RAHEAD; 17879a6844d5SKenneth D. Merry 17889a6844d5SKenneth D. Merry if (cgd->ident_data.support.command1 & ATA_SUPPORT_WRITECACHE) 17899a6844d5SKenneth D. Merry softc->flags |= ADA_FLAG_CAN_WCACHE; 17909a6844d5SKenneth D. Merry else 17919a6844d5SKenneth D. Merry softc->flags &= ~ADA_FLAG_CAN_WCACHE; 17929a6844d5SKenneth D. Merry } 17939a6844d5SKenneth D. Merry 179452c9ce25SScott Long static cam_status 179552c9ce25SScott Long adaregister(struct cam_periph *periph, void *arg) 179652c9ce25SScott Long { 179752c9ce25SScott Long struct ada_softc *softc; 179852c9ce25SScott Long struct ccb_getdev *cgd; 179952c9ce25SScott Long struct disk_params *dp; 18005d01277fSScott Long struct sbuf sb; 18015d01277fSScott Long char *announce_buf; 180252c9ce25SScott Long caddr_t match; 18034a3760baSAlexander Motin int quirks; 180452c9ce25SScott Long 180552c9ce25SScott Long cgd = (struct ccb_getdev *)arg; 180652c9ce25SScott Long if (cgd == NULL) { 180752c9ce25SScott Long printf("adaregister: no getdev CCB, can't register device\n"); 180852c9ce25SScott Long return(CAM_REQ_CMP_ERR); 180952c9ce25SScott Long } 181052c9ce25SScott Long 181152c9ce25SScott Long softc = (struct ada_softc *)malloc(sizeof(*softc), M_DEVBUF, 181252c9ce25SScott Long M_NOWAIT|M_ZERO); 181352c9ce25SScott Long 181452c9ce25SScott Long if (softc == NULL) { 181552c9ce25SScott Long printf("adaregister: Unable to probe new device. " 181652c9ce25SScott Long "Unable to allocate softc\n"); 181752c9ce25SScott Long return(CAM_REQ_CMP_ERR); 181852c9ce25SScott Long } 181952c9ce25SScott Long 18205d01277fSScott Long announce_buf = softc->announce_temp; 18215d01277fSScott Long bzero(announce_buf, ADA_ANNOUNCETMP_SZ); 18225d01277fSScott Long 18230028abe6SWarner Losh if (cam_iosched_init(&softc->cam_iosched, periph) != 0) { 1824a6e0c5daSWarner Losh printf("adaregister: Unable to probe new device. " 1825a6e0c5daSWarner Losh "Unable to allocate iosched memory\n"); 182675548271SConrad Meyer free(softc, M_DEVBUF); 1827a6e0c5daSWarner Losh return(CAM_REQ_CMP_ERR); 1828a6e0c5daSWarner Losh } 182952c9ce25SScott Long 183052c9ce25SScott Long periph->softc = softc; 183176d843daSAlexander Motin xpt_path_inq(&softc->cpi, periph->path); 183252c9ce25SScott Long 183352c9ce25SScott Long /* 183452c9ce25SScott Long * See if this device has any quirks. 183552c9ce25SScott Long */ 183630a4094fSAlexander Motin match = cam_quirkmatch((caddr_t)&cgd->ident_data, 183730a4094fSAlexander Motin (caddr_t)ada_quirk_table, 1838323b076eSPedro F. Giffuni nitems(ada_quirk_table), 183930a4094fSAlexander Motin sizeof(*ada_quirk_table), ata_identify_match); 184052c9ce25SScott Long if (match != NULL) 184152c9ce25SScott Long softc->quirks = ((struct ada_quirk_entry *)match)->quirks; 184252c9ce25SScott Long else 184352c9ce25SScott Long softc->quirks = ADA_Q_NONE; 184452c9ce25SScott Long 184552c9ce25SScott Long TASK_INIT(&softc->sysctl_task, 0, adasysctlinit, periph); 184652c9ce25SScott Long 184752c9ce25SScott Long /* 1848*90bcc81bSAlexander Motin * Take a reference on the periph while adastart is called to finish 1849*90bcc81bSAlexander Motin * the probe. The reference will be dropped in adaprobedone at the 1850*90bcc81bSAlexander Motin * end of probe. 185152c9ce25SScott Long */ 1852*90bcc81bSAlexander Motin (void)cam_periph_acquire(periph); 1853edec59d9SAlexander Motin cam_periph_unlock(periph); 18545d01277fSScott Long snprintf(announce_buf, ADA_ANNOUNCETMP_SZ, 1855d3a460d3SAlexander Motin "kern.cam.ada.%d.quirks", periph->unit_number); 1856d3a460d3SAlexander Motin quirks = softc->quirks; 1857d3a460d3SAlexander Motin TUNABLE_INT_FETCH(announce_buf, &quirks); 1858d3a460d3SAlexander Motin softc->quirks = quirks; 18591ed6aaf9SAlexander Motin softc->read_ahead = -1; 18605d01277fSScott Long snprintf(announce_buf, ADA_ANNOUNCETMP_SZ, 18611ed6aaf9SAlexander Motin "kern.cam.ada.%d.read_ahead", periph->unit_number); 18621ed6aaf9SAlexander Motin TUNABLE_INT_FETCH(announce_buf, &softc->read_ahead); 1863781338b6SAlexander Motin softc->write_cache = -1; 18645d01277fSScott Long snprintf(announce_buf, ADA_ANNOUNCETMP_SZ, 1865781338b6SAlexander Motin "kern.cam.ada.%d.write_cache", periph->unit_number); 1866781338b6SAlexander Motin TUNABLE_INT_FETCH(announce_buf, &softc->write_cache); 18673f54ec85SKenneth D. Merry 18683f54ec85SKenneth D. Merry /* 18693394d423SEdward Tomasz Napierala * Let XPT know we can use UMA-allocated CCBs. 18703394d423SEdward Tomasz Napierala */ 18713394d423SEdward Tomasz Napierala if (ada_enable_uma_ccbs) { 18723394d423SEdward Tomasz Napierala KASSERT(ada_ccb_zone != NULL, 18733394d423SEdward Tomasz Napierala ("%s: NULL ada_ccb_zone", __func__)); 18743394d423SEdward Tomasz Napierala periph->ccb_zone = ada_ccb_zone; 18753394d423SEdward Tomasz Napierala } 18763394d423SEdward Tomasz Napierala 18773394d423SEdward Tomasz Napierala /* 18783f54ec85SKenneth D. Merry * Set support flags based on the Identify data and quirks. 18793f54ec85SKenneth D. Merry */ 18803f54ec85SKenneth D. Merry adasetflags(softc, cgd); 188176d843daSAlexander Motin if (softc->cpi.hba_misc & PIM_ATA_EXT) 188276d843daSAlexander Motin softc->flags |= ADA_FLAG_PIM_ATA_EXT; 18833f54ec85SKenneth D. Merry 188462cc3a63SSteven Hartland /* Disable queue sorting for non-rotational media by default. */ 1885a6e0c5daSWarner Losh if (cgd->ident_data.media_rotation_rate == ATA_RATE_NON_ROTATING) { 188696eb32bfSWarner Losh softc->flags &= ~ADA_FLAG_ROTATING; 1887a6e0c5daSWarner Losh } else { 188896eb32bfSWarner Losh softc->flags |= ADA_FLAG_ROTATING; 1889a6e0c5daSWarner Losh } 189096eb32bfSWarner Losh cam_iosched_set_sort_queue(softc->cam_iosched, 189196eb32bfSWarner Losh (softc->flags & ADA_FLAG_ROTATING) ? -1 : 0); 189252c9ce25SScott Long softc->disk = disk_alloc(); 189376d843daSAlexander Motin adasetgeom(softc, cgd); 1894b8b6b5d3SAlexander Motin softc->disk->d_devstat = devstat_new_entry(periph->periph_name, 1895b8b6b5d3SAlexander Motin periph->unit_number, softc->params.secsize, 1896b8b6b5d3SAlexander Motin DEVSTAT_ALL_SUPPORTED, 1897b8b6b5d3SAlexander Motin DEVSTAT_TYPE_DIRECT | 189876d843daSAlexander Motin XPORT_DEVSTAT_TYPE(softc->cpi.transport), 1899b8b6b5d3SAlexander Motin DEVSTAT_PRIORITY_DISK); 190052c9ce25SScott Long softc->disk->d_open = adaopen; 190152c9ce25SScott Long softc->disk->d_close = adaclose; 190252c9ce25SScott Long softc->disk->d_strategy = adastrategy; 1903416494d7SJustin T. Gibbs softc->disk->d_getattr = adagetattr; 1904e07ac3f2SJohn Baldwin if (cam_sim_pollable(periph->sim)) 190552c9ce25SScott Long softc->disk->d_dump = adadump; 19060ba1e4d0SKenneth D. Merry softc->disk->d_gone = adadiskgonecb; 190752c9ce25SScott Long softc->disk->d_name = "ada"; 190852c9ce25SScott Long softc->disk->d_drv1 = periph; 190952c9ce25SScott Long softc->disk->d_unit = periph->unit_number; 1910edec59d9SAlexander Motin cam_periph_lock(periph); 191152c9ce25SScott Long 191252c9ce25SScott Long dp = &softc->params; 19135d01277fSScott Long snprintf(announce_buf, ADA_ANNOUNCETMP_SZ, 191468546995SAlexander Motin "%juMB (%ju %u byte sectors)", 191568546995SAlexander Motin ((uintmax_t)dp->secsize * dp->sectors) / (1024 * 1024), 191668546995SAlexander Motin (uintmax_t)dp->sectors, dp->secsize); 19175d01277fSScott Long 19185d01277fSScott Long sbuf_new(&sb, softc->announce_buffer, ADA_ANNOUNCE_SZ, SBUF_FIXEDLEN); 19195d01277fSScott Long xpt_announce_periph_sbuf(periph, &sb, announce_buf); 19205d01277fSScott Long xpt_announce_quirks_sbuf(periph, &sb, softc->quirks, ADA_Q_BIT_STRING); 19215d01277fSScott Long sbuf_finish(&sb); 19225d01277fSScott Long sbuf_putbuf(&sb); 1923e3a6d3a4SAlexander Motin 1924e3a6d3a4SAlexander Motin /* 1925e3a6d3a4SAlexander Motin * Create our sysctl variables, now that we know 1926e3a6d3a4SAlexander Motin * we have successfully attached. 1927e3a6d3a4SAlexander Motin */ 192899e7a4adSScott Long if (cam_periph_acquire(periph) == 0) 1929e3a6d3a4SAlexander Motin taskqueue_enqueue(taskqueue_thread, &softc->sysctl_task); 1930e3a6d3a4SAlexander Motin 193152c9ce25SScott Long /* 193252c9ce25SScott Long * Add async callbacks for bus reset and 193352c9ce25SScott Long * bus device reset calls. I don't bother 193452c9ce25SScott Long * checking if this fails as, in most cases, 193552c9ce25SScott Long * the system will function just fine without 193652c9ce25SScott Long * them and the only alternative would be to 193752c9ce25SScott Long * not attach the device on failure. 193852c9ce25SScott Long */ 19393089bb2eSAlexander Motin xpt_register_async(AC_SENT_BDR | AC_BUS_RESET | AC_LOST_DEVICE | 1940581b2e78SAlexander Motin AC_GETDEV_CHANGED | AC_ADVINFO_CHANGED, 1941581b2e78SAlexander Motin adaasync, periph, periph->path); 194252c9ce25SScott Long 194352c9ce25SScott Long /* 194452c9ce25SScott Long * Schedule a periodic event to occasionally send an 194552c9ce25SScott Long * ordered tag to a device. 194652c9ce25SScott Long */ 1947227d67aaSAlexander Motin callout_init_mtx(&softc->sendordered_c, cam_periph_mtx(periph), 0); 19480e5c50bfSAlexander Motin callout_reset_sbt(&softc->sendordered_c, 19490e5c50bfSAlexander Motin SBT_1S / ADA_ORDEREDTAG_INTERVAL * ada_default_timeout, 0, 19500e5c50bfSAlexander Motin adasendorderedtag, softc, C_PREL(1)); 195152c9ce25SScott Long 1952*90bcc81bSAlexander Motin /* Released after probe when disk_create() call pass it to GEOM. */ 1953*90bcc81bSAlexander Motin cam_periph_hold_boot(periph); 1954*90bcc81bSAlexander Motin 19559a6844d5SKenneth D. Merry if (ADA_RA >= 0 && softc->flags & ADA_FLAG_CAN_RAHEAD) { 19561ed6aaf9SAlexander Motin softc->state = ADA_STATE_RAHEAD; 19579a6844d5SKenneth D. Merry } else if (ADA_WC >= 0 && softc->flags & ADA_FLAG_CAN_WCACHE) { 1958f513d14cSAlexander Motin softc->state = ADA_STATE_WCACHE; 1959600fd98fSKenneth D. Merry } else if ((softc->flags & ADA_FLAG_CAN_LOG) 1960600fd98fSKenneth D. Merry && (softc->zone_mode != ADA_ZONE_NONE)) { 19619a6844d5SKenneth D. Merry softc->state = ADA_STATE_LOGDIR; 1962227d67aaSAlexander Motin } else { 19639a6844d5SKenneth D. Merry /* 19649a6844d5SKenneth D. Merry * Nothing to probe, so we can just transition to the 19659a6844d5SKenneth D. Merry * normal state. 19669a6844d5SKenneth D. Merry */ 19679a6844d5SKenneth D. Merry adaprobedone(periph, NULL); 1968227d67aaSAlexander Motin return(CAM_REQ_CMP); 1969227d67aaSAlexander Motin } 19709a6844d5SKenneth D. Merry 1971227d67aaSAlexander Motin xpt_schedule(periph, CAM_PRIORITY_DEV); 197252c9ce25SScott Long return(CAM_REQ_CMP); 197352c9ce25SScott Long } 197452c9ce25SScott Long 1975a6e0c5daSWarner Losh static int 1976a6e0c5daSWarner Losh ada_dsmtrim_req_create(struct ada_softc *softc, struct bio *bp, struct trim_request *req) 197752c9ce25SScott Long { 1978ea657f2cSWarner Losh uint64_t lastlba = (uint64_t)-1, lbas = 0; 19792030b294SAlexander Motin int c, lastcount = 0, off, ranges = 0; 198052c9ce25SScott Long 19811c80ec0aSAlexander Motin bzero(req, sizeof(*req)); 19822030b294SAlexander Motin TAILQ_INIT(&req->bps); 19831c80ec0aSAlexander Motin do { 19847ddad071SWarner Losh uint64_t lba = bp->bio_pblkno; 19857ddad071SWarner Losh int count = bp->bio_bcount / softc->params.secsize; 19861c80ec0aSAlexander Motin 198737ddbd16SAlexander Motin /* Try to extend the previous range. */ 198837ddbd16SAlexander Motin if (lba == lastlba) { 1989c213c551SSteven Hartland c = min(count, ATA_DSM_RANGE_MAX - lastcount); 199037ddbd16SAlexander Motin lastcount += c; 1991c213c551SSteven Hartland off = (ranges - 1) * ATA_DSM_RANGE_SIZE; 199237ddbd16SAlexander Motin req->data[off + 6] = lastcount & 0xff; 199337ddbd16SAlexander Motin req->data[off + 7] = 199437ddbd16SAlexander Motin (lastcount >> 8) & 0xff; 199537ddbd16SAlexander Motin count -= c; 199637ddbd16SAlexander Motin lba += c; 1997ea657f2cSWarner Losh lbas += c; 199837ddbd16SAlexander Motin } 199937ddbd16SAlexander Motin 200037ddbd16SAlexander Motin while (count > 0) { 2001c213c551SSteven Hartland c = min(count, ATA_DSM_RANGE_MAX); 2002c213c551SSteven Hartland off = ranges * ATA_DSM_RANGE_SIZE; 20031c80ec0aSAlexander Motin req->data[off + 0] = lba & 0xff; 20041c80ec0aSAlexander Motin req->data[off + 1] = (lba >> 8) & 0xff; 20051c80ec0aSAlexander Motin req->data[off + 2] = (lba >> 16) & 0xff; 20061c80ec0aSAlexander Motin req->data[off + 3] = (lba >> 24) & 0xff; 20071c80ec0aSAlexander Motin req->data[off + 4] = (lba >> 32) & 0xff; 20081c80ec0aSAlexander Motin req->data[off + 5] = (lba >> 40) & 0xff; 20091c80ec0aSAlexander Motin req->data[off + 6] = c & 0xff; 20101c80ec0aSAlexander Motin req->data[off + 7] = (c >> 8) & 0xff; 20111c80ec0aSAlexander Motin lba += c; 2012ea657f2cSWarner Losh lbas += c; 20131c80ec0aSAlexander Motin count -= c; 201437ddbd16SAlexander Motin lastcount = c; 20151c80ec0aSAlexander Motin ranges++; 2016c213c551SSteven Hartland /* 2017c213c551SSteven Hartland * Its the caller's responsibility to ensure the 2018c213c551SSteven Hartland * request will fit so we don't need to check for 2019c213c551SSteven Hartland * overrun here 2020c213c551SSteven Hartland */ 20211c80ec0aSAlexander Motin } 202237ddbd16SAlexander Motin lastlba = lba; 20237ddad071SWarner Losh TAILQ_INSERT_TAIL(&req->bps, bp, bio_queue); 2024a6e0c5daSWarner Losh 2025a6e0c5daSWarner Losh bp = cam_iosched_next_trim(softc->cam_iosched); 2026a6e0c5daSWarner Losh if (bp == NULL) 20271c80ec0aSAlexander Motin break; 2028a6e0c5daSWarner Losh if (bp->bio_bcount / softc->params.secsize > 2029a6e0c5daSWarner Losh (softc->trim_max_ranges - ranges) * ATA_DSM_RANGE_MAX) { 2030a6e0c5daSWarner Losh cam_iosched_put_back_trim(softc->cam_iosched, bp); 2031a6e0c5daSWarner Losh break; 2032a6e0c5daSWarner Losh } 20331c80ec0aSAlexander Motin } while (1); 2034ea657f2cSWarner Losh softc->trim_count++; 2035ea657f2cSWarner Losh softc->trim_ranges += ranges; 2036ea657f2cSWarner Losh softc->trim_lbas += lbas; 2037a6e0c5daSWarner Losh 2038a6e0c5daSWarner Losh return (ranges); 2039a6e0c5daSWarner Losh } 2040a6e0c5daSWarner Losh 2041a6e0c5daSWarner Losh static void 2042a6e0c5daSWarner Losh ada_dsmtrim(struct ada_softc *softc, struct bio *bp, struct ccb_ataio *ataio) 2043a6e0c5daSWarner Losh { 2044a6e0c5daSWarner Losh struct trim_request *req = &softc->trim_req; 2045a6e0c5daSWarner Losh int ranges; 2046a6e0c5daSWarner Losh 2047a6e0c5daSWarner Losh ranges = ada_dsmtrim_req_create(softc, bp, req); 20481c80ec0aSAlexander Motin cam_fill_ataio(ataio, 20491c80ec0aSAlexander Motin ada_retry_count, 20501c80ec0aSAlexander Motin adadone, 20511c80ec0aSAlexander Motin CAM_DIR_OUT, 20521c80ec0aSAlexander Motin 0, 20531c80ec0aSAlexander Motin req->data, 205455e0987aSPedro F. Giffuni howmany(ranges, ATA_DSM_BLK_RANGES) * ATA_DSM_BLK_SIZE, 20551c80ec0aSAlexander Motin ada_default_timeout * 1000); 20561c80ec0aSAlexander Motin ata_48bit_cmd(ataio, ATA_DATA_SET_MANAGEMENT, 205755e0987aSPedro F. Giffuni ATA_DSM_TRIM, 0, howmany(ranges, ATA_DSM_BLK_RANGES)); 20587ddad071SWarner Losh } 20597ddad071SWarner Losh 20607ddad071SWarner Losh static void 2061a6e0c5daSWarner Losh ada_ncq_dsmtrim(struct ada_softc *softc, struct bio *bp, struct ccb_ataio *ataio) 2062a6e0c5daSWarner Losh { 2063a6e0c5daSWarner Losh struct trim_request *req = &softc->trim_req; 2064a6e0c5daSWarner Losh int ranges; 2065a6e0c5daSWarner Losh 2066a6e0c5daSWarner Losh ranges = ada_dsmtrim_req_create(softc, bp, req); 2067a6e0c5daSWarner Losh cam_fill_ataio(ataio, 2068a6e0c5daSWarner Losh ada_retry_count, 2069a6e0c5daSWarner Losh adadone, 2070a6e0c5daSWarner Losh CAM_DIR_OUT, 2071a6e0c5daSWarner Losh 0, 2072a6e0c5daSWarner Losh req->data, 207355e0987aSPedro F. Giffuni howmany(ranges, ATA_DSM_BLK_RANGES) * ATA_DSM_BLK_SIZE, 2074a6e0c5daSWarner Losh ada_default_timeout * 1000); 2075a6e0c5daSWarner Losh ata_ncq_cmd(ataio, 2076a6e0c5daSWarner Losh ATA_SEND_FPDMA_QUEUED, 2077a6e0c5daSWarner Losh 0, 207855e0987aSPedro F. Giffuni howmany(ranges, ATA_DSM_BLK_RANGES)); 2079a6e0c5daSWarner Losh ataio->cmd.sector_count_exp = ATA_SFPDMA_DSM; 2080916d57dfSWarner Losh ataio->ata_flags |= ATA_FLAG_AUX; 2081916d57dfSWarner Losh ataio->aux = 1; 2082a6e0c5daSWarner Losh } 2083a6e0c5daSWarner Losh 2084a6e0c5daSWarner Losh static void 20857ddad071SWarner Losh ada_cfaerase(struct ada_softc *softc, struct bio *bp, struct ccb_ataio *ataio) 20867ddad071SWarner Losh { 2087467298f5SSteven Hartland struct trim_request *req = &softc->trim_req; 20887ddad071SWarner Losh uint64_t lba = bp->bio_pblkno; 20897ddad071SWarner Losh uint16_t count = bp->bio_bcount / softc->params.secsize; 20907ddad071SWarner Losh 2091467298f5SSteven Hartland bzero(req, sizeof(*req)); 2092467298f5SSteven Hartland TAILQ_INIT(&req->bps); 2093467298f5SSteven Hartland TAILQ_INSERT_TAIL(&req->bps, bp, bio_queue); 2094467298f5SSteven Hartland 20957ddad071SWarner Losh cam_fill_ataio(ataio, 20967ddad071SWarner Losh ada_retry_count, 20977ddad071SWarner Losh adadone, 20987ddad071SWarner Losh CAM_DIR_NONE, 20997ddad071SWarner Losh 0, 21007ddad071SWarner Losh NULL, 21017ddad071SWarner Losh 0, 21027ddad071SWarner Losh ada_default_timeout*1000); 21037ddad071SWarner Losh 21047ddad071SWarner Losh if (count >= 256) 21057ddad071SWarner Losh count = 0; 21067ddad071SWarner Losh ata_28bit_cmd(ataio, ATA_CFA_ERASE, 0, lba, count); 21077ddad071SWarner Losh } 21087ddad071SWarner Losh 21099a6844d5SKenneth D. Merry static int 21109a6844d5SKenneth D. Merry ada_zone_bio_to_ata(int disk_zone_cmd) 21119a6844d5SKenneth D. Merry { 21129a6844d5SKenneth D. Merry switch (disk_zone_cmd) { 21139a6844d5SKenneth D. Merry case DISK_ZONE_OPEN: 21149a6844d5SKenneth D. Merry return ATA_ZM_OPEN_ZONE; 21159a6844d5SKenneth D. Merry case DISK_ZONE_CLOSE: 21169a6844d5SKenneth D. Merry return ATA_ZM_CLOSE_ZONE; 21179a6844d5SKenneth D. Merry case DISK_ZONE_FINISH: 21189a6844d5SKenneth D. Merry return ATA_ZM_FINISH_ZONE; 21199a6844d5SKenneth D. Merry case DISK_ZONE_RWP: 21209a6844d5SKenneth D. Merry return ATA_ZM_RWP; 21219a6844d5SKenneth D. Merry } 21229a6844d5SKenneth D. Merry 21239a6844d5SKenneth D. Merry return -1; 21249a6844d5SKenneth D. Merry } 21259a6844d5SKenneth D. Merry 21269a6844d5SKenneth D. Merry static int 21279a6844d5SKenneth D. Merry ada_zone_cmd(struct cam_periph *periph, union ccb *ccb, struct bio *bp, 21289a6844d5SKenneth D. Merry int *queue_ccb) 21299a6844d5SKenneth D. Merry { 21309a6844d5SKenneth D. Merry struct ada_softc *softc; 21319a6844d5SKenneth D. Merry int error; 21329a6844d5SKenneth D. Merry 21339a6844d5SKenneth D. Merry error = 0; 21349a6844d5SKenneth D. Merry 21359a6844d5SKenneth D. Merry if (bp->bio_cmd != BIO_ZONE) { 21369a6844d5SKenneth D. Merry error = EINVAL; 21379a6844d5SKenneth D. Merry goto bailout; 21389a6844d5SKenneth D. Merry } 21399a6844d5SKenneth D. Merry 21409a6844d5SKenneth D. Merry softc = periph->softc; 21419a6844d5SKenneth D. Merry 21429a6844d5SKenneth D. Merry switch (bp->bio_zone.zone_cmd) { 21439a6844d5SKenneth D. Merry case DISK_ZONE_OPEN: 21449a6844d5SKenneth D. Merry case DISK_ZONE_CLOSE: 21459a6844d5SKenneth D. Merry case DISK_ZONE_FINISH: 21469a6844d5SKenneth D. Merry case DISK_ZONE_RWP: { 21479a6844d5SKenneth D. Merry int zone_flags; 21489a6844d5SKenneth D. Merry int zone_sa; 21499a6844d5SKenneth D. Merry uint64_t lba; 21509a6844d5SKenneth D. Merry 21519a6844d5SKenneth D. Merry zone_sa = ada_zone_bio_to_ata(bp->bio_zone.zone_cmd); 21529a6844d5SKenneth D. Merry if (zone_sa == -1) { 21539a6844d5SKenneth D. Merry xpt_print(periph->path, "Cannot translate zone " 21549a6844d5SKenneth D. Merry "cmd %#x to ATA\n", bp->bio_zone.zone_cmd); 21559a6844d5SKenneth D. Merry error = EINVAL; 21569a6844d5SKenneth D. Merry goto bailout; 21579a6844d5SKenneth D. Merry } 21589a6844d5SKenneth D. Merry 21599a6844d5SKenneth D. Merry zone_flags = 0; 21609a6844d5SKenneth D. Merry lba = bp->bio_zone.zone_params.rwp.id; 21619a6844d5SKenneth D. Merry 21629a6844d5SKenneth D. Merry if (bp->bio_zone.zone_params.rwp.flags & 21639a6844d5SKenneth D. Merry DISK_ZONE_RWP_FLAG_ALL) 21649a6844d5SKenneth D. Merry zone_flags |= ZBC_OUT_ALL; 21659a6844d5SKenneth D. Merry 21669a6844d5SKenneth D. Merry ata_zac_mgmt_out(&ccb->ataio, 21679a6844d5SKenneth D. Merry /*retries*/ ada_retry_count, 21689a6844d5SKenneth D. Merry /*cbfcnp*/ adadone, 21699a6844d5SKenneth D. Merry /*use_ncq*/ (softc->flags & 21709a6844d5SKenneth D. Merry ADA_FLAG_PIM_ATA_EXT) ? 1 : 0, 21719a6844d5SKenneth D. Merry /*zm_action*/ zone_sa, 21729a6844d5SKenneth D. Merry /*zone_id*/ lba, 21739a6844d5SKenneth D. Merry /*zone_flags*/ zone_flags, 21749a6844d5SKenneth D. Merry /*sector_count*/ 0, 21759a6844d5SKenneth D. Merry /*data_ptr*/ NULL, 21769a6844d5SKenneth D. Merry /*dxfer_len*/ 0, 21779a6844d5SKenneth D. Merry /*timeout*/ ada_default_timeout * 1000); 21789a6844d5SKenneth D. Merry *queue_ccb = 1; 21799a6844d5SKenneth D. Merry 21809a6844d5SKenneth D. Merry break; 21819a6844d5SKenneth D. Merry } 21829a6844d5SKenneth D. Merry case DISK_ZONE_REPORT_ZONES: { 21839a6844d5SKenneth D. Merry uint8_t *rz_ptr; 21849a6844d5SKenneth D. Merry uint32_t num_entries, alloc_size; 21859a6844d5SKenneth D. Merry struct disk_zone_report *rep; 21869a6844d5SKenneth D. Merry 21879a6844d5SKenneth D. Merry rep = &bp->bio_zone.zone_params.report; 21889a6844d5SKenneth D. Merry 21899a6844d5SKenneth D. Merry num_entries = rep->entries_allocated; 21909a6844d5SKenneth D. Merry if (num_entries == 0) { 21919a6844d5SKenneth D. Merry xpt_print(periph->path, "No entries allocated for " 21929a6844d5SKenneth D. Merry "Report Zones request\n"); 21939a6844d5SKenneth D. Merry error = EINVAL; 21949a6844d5SKenneth D. Merry goto bailout; 21959a6844d5SKenneth D. Merry } 21969a6844d5SKenneth D. Merry alloc_size = sizeof(struct scsi_report_zones_hdr) + 21979a6844d5SKenneth D. Merry (sizeof(struct scsi_report_zones_desc) * num_entries); 21989a6844d5SKenneth D. Merry alloc_size = min(alloc_size, softc->disk->d_maxsize); 21999a6844d5SKenneth D. Merry rz_ptr = malloc(alloc_size, M_ATADA, M_NOWAIT | M_ZERO); 22009a6844d5SKenneth D. Merry if (rz_ptr == NULL) { 22019a6844d5SKenneth D. Merry xpt_print(periph->path, "Unable to allocate memory " 22029a6844d5SKenneth D. Merry "for Report Zones request\n"); 22039a6844d5SKenneth D. Merry error = ENOMEM; 22049a6844d5SKenneth D. Merry goto bailout; 22059a6844d5SKenneth D. Merry } 22069a6844d5SKenneth D. Merry 22079a6844d5SKenneth D. Merry ata_zac_mgmt_in(&ccb->ataio, 22089a6844d5SKenneth D. Merry /*retries*/ ada_retry_count, 22099a6844d5SKenneth D. Merry /*cbcfnp*/ adadone, 22109a6844d5SKenneth D. Merry /*use_ncq*/ (softc->flags & 22119a6844d5SKenneth D. Merry ADA_FLAG_PIM_ATA_EXT) ? 1 : 0, 22129a6844d5SKenneth D. Merry /*zm_action*/ ATA_ZM_REPORT_ZONES, 22139a6844d5SKenneth D. Merry /*zone_id*/ rep->starting_id, 22149a6844d5SKenneth D. Merry /*zone_flags*/ rep->rep_options, 22159a6844d5SKenneth D. Merry /*data_ptr*/ rz_ptr, 22169a6844d5SKenneth D. Merry /*dxfer_len*/ alloc_size, 22179a6844d5SKenneth D. Merry /*timeout*/ ada_default_timeout * 1000); 22189a6844d5SKenneth D. Merry 22199a6844d5SKenneth D. Merry /* 22209a6844d5SKenneth D. Merry * For BIO_ZONE, this isn't normally needed. However, it 22219a6844d5SKenneth D. Merry * is used by devstat_end_transaction_bio() to determine 22229a6844d5SKenneth D. Merry * how much data was transferred. 22239a6844d5SKenneth D. Merry */ 22249a6844d5SKenneth D. Merry /* 22259a6844d5SKenneth D. Merry * XXX KDM we have a problem. But I'm not sure how to fix 22269a6844d5SKenneth D. Merry * it. devstat uses bio_bcount - bio_resid to calculate 22279a6844d5SKenneth D. Merry * the amount of data transferred. The GEOM disk code 22289a6844d5SKenneth D. Merry * uses bio_length - bio_resid to calculate the amount of 22299a6844d5SKenneth D. Merry * data in bio_completed. We have different structure 22309a6844d5SKenneth D. Merry * sizes above and below the ada(4) driver. So, if we 22319a6844d5SKenneth D. Merry * use the sizes above, the amount transferred won't be 22329a6844d5SKenneth D. Merry * quite accurate for devstat. If we use different sizes 22339a6844d5SKenneth D. Merry * for bio_bcount and bio_length (above and below 22349a6844d5SKenneth D. Merry * respectively), then the residual needs to match one or 22359a6844d5SKenneth D. Merry * the other. Everything is calculated after the bio 22369a6844d5SKenneth D. Merry * leaves the driver, so changing the values around isn't 22379a6844d5SKenneth D. Merry * really an option. For now, just set the count to the 22389a6844d5SKenneth D. Merry * passed in length. This means that the calculations 22399a6844d5SKenneth D. Merry * above (e.g. bio_completed) will be correct, but the 22409a6844d5SKenneth D. Merry * amount of data reported to devstat will be slightly 22419a6844d5SKenneth D. Merry * under or overstated. 22429a6844d5SKenneth D. Merry */ 22439a6844d5SKenneth D. Merry bp->bio_bcount = bp->bio_length; 22449a6844d5SKenneth D. Merry 22459a6844d5SKenneth D. Merry *queue_ccb = 1; 22469a6844d5SKenneth D. Merry 22479a6844d5SKenneth D. Merry break; 22489a6844d5SKenneth D. Merry } 22499a6844d5SKenneth D. Merry case DISK_ZONE_GET_PARAMS: { 22509a6844d5SKenneth D. Merry struct disk_zone_disk_params *params; 22519a6844d5SKenneth D. Merry 22529a6844d5SKenneth D. Merry params = &bp->bio_zone.zone_params.disk_params; 22539a6844d5SKenneth D. Merry bzero(params, sizeof(*params)); 22549a6844d5SKenneth D. Merry 22559a6844d5SKenneth D. Merry switch (softc->zone_mode) { 22569a6844d5SKenneth D. Merry case ADA_ZONE_DRIVE_MANAGED: 22579a6844d5SKenneth D. Merry params->zone_mode = DISK_ZONE_MODE_DRIVE_MANAGED; 22589a6844d5SKenneth D. Merry break; 22599a6844d5SKenneth D. Merry case ADA_ZONE_HOST_AWARE: 22609a6844d5SKenneth D. Merry params->zone_mode = DISK_ZONE_MODE_HOST_AWARE; 22619a6844d5SKenneth D. Merry break; 22629a6844d5SKenneth D. Merry case ADA_ZONE_HOST_MANAGED: 22639a6844d5SKenneth D. Merry params->zone_mode = DISK_ZONE_MODE_HOST_MANAGED; 22649a6844d5SKenneth D. Merry break; 22659a6844d5SKenneth D. Merry default: 22669a6844d5SKenneth D. Merry case ADA_ZONE_NONE: 22679a6844d5SKenneth D. Merry params->zone_mode = DISK_ZONE_MODE_NONE; 22689a6844d5SKenneth D. Merry break; 22699a6844d5SKenneth D. Merry } 22709a6844d5SKenneth D. Merry 22719a6844d5SKenneth D. Merry if (softc->zone_flags & ADA_ZONE_FLAG_URSWRZ) 22729a6844d5SKenneth D. Merry params->flags |= DISK_ZONE_DISK_URSWRZ; 22739a6844d5SKenneth D. Merry 22749a6844d5SKenneth D. Merry if (softc->zone_flags & ADA_ZONE_FLAG_OPT_SEQ_SET) { 22759a6844d5SKenneth D. Merry params->optimal_seq_zones = softc->optimal_seq_zones; 22769a6844d5SKenneth D. Merry params->flags |= DISK_ZONE_OPT_SEQ_SET; 22779a6844d5SKenneth D. Merry } 22789a6844d5SKenneth D. Merry 22799a6844d5SKenneth D. Merry if (softc->zone_flags & ADA_ZONE_FLAG_OPT_NONSEQ_SET) { 22809a6844d5SKenneth D. Merry params->optimal_nonseq_zones = 22819a6844d5SKenneth D. Merry softc->optimal_nonseq_zones; 22829a6844d5SKenneth D. Merry params->flags |= DISK_ZONE_OPT_NONSEQ_SET; 22839a6844d5SKenneth D. Merry } 22849a6844d5SKenneth D. Merry 22859a6844d5SKenneth D. Merry if (softc->zone_flags & ADA_ZONE_FLAG_MAX_SEQ_SET) { 22869a6844d5SKenneth D. Merry params->max_seq_zones = softc->max_seq_zones; 22879a6844d5SKenneth D. Merry params->flags |= DISK_ZONE_MAX_SEQ_SET; 22889a6844d5SKenneth D. Merry } 22899a6844d5SKenneth D. Merry if (softc->zone_flags & ADA_ZONE_FLAG_RZ_SUP) 22909a6844d5SKenneth D. Merry params->flags |= DISK_ZONE_RZ_SUP; 22919a6844d5SKenneth D. Merry 22929a6844d5SKenneth D. Merry if (softc->zone_flags & ADA_ZONE_FLAG_OPEN_SUP) 22939a6844d5SKenneth D. Merry params->flags |= DISK_ZONE_OPEN_SUP; 22949a6844d5SKenneth D. Merry 22959a6844d5SKenneth D. Merry if (softc->zone_flags & ADA_ZONE_FLAG_CLOSE_SUP) 22969a6844d5SKenneth D. Merry params->flags |= DISK_ZONE_CLOSE_SUP; 22979a6844d5SKenneth D. Merry 22989a6844d5SKenneth D. Merry if (softc->zone_flags & ADA_ZONE_FLAG_FINISH_SUP) 22999a6844d5SKenneth D. Merry params->flags |= DISK_ZONE_FINISH_SUP; 23009a6844d5SKenneth D. Merry 23019a6844d5SKenneth D. Merry if (softc->zone_flags & ADA_ZONE_FLAG_RWP_SUP) 23029a6844d5SKenneth D. Merry params->flags |= DISK_ZONE_RWP_SUP; 23039a6844d5SKenneth D. Merry break; 23049a6844d5SKenneth D. Merry } 23059a6844d5SKenneth D. Merry default: 23069a6844d5SKenneth D. Merry break; 23079a6844d5SKenneth D. Merry } 23089a6844d5SKenneth D. Merry bailout: 23099a6844d5SKenneth D. Merry return (error); 23109a6844d5SKenneth D. Merry } 23119a6844d5SKenneth D. Merry 23127ddad071SWarner Losh static void 23137ddad071SWarner Losh adastart(struct cam_periph *periph, union ccb *start_ccb) 23147ddad071SWarner Losh { 23157ddad071SWarner Losh struct ada_softc *softc = (struct ada_softc *)periph->softc; 23167ddad071SWarner Losh struct ccb_ataio *ataio = &start_ccb->ataio; 23177ddad071SWarner Losh 23187ddad071SWarner Losh CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("adastart\n")); 23197ddad071SWarner Losh 23207ddad071SWarner Losh switch (softc->state) { 23217ddad071SWarner Losh case ADA_STATE_NORMAL: 23227ddad071SWarner Losh { 23237ddad071SWarner Losh struct bio *bp; 23247ddad071SWarner Losh u_int8_t tag_code; 23257ddad071SWarner Losh 2326a6e0c5daSWarner Losh bp = cam_iosched_next_bio(softc->cam_iosched); 23271c80ec0aSAlexander Motin if (bp == NULL) { 23281c80ec0aSAlexander Motin xpt_release_ccb(start_ccb); 23291c80ec0aSAlexander Motin break; 23301c80ec0aSAlexander Motin } 233152c9ce25SScott Long 2332a6e0c5daSWarner Losh if ((bp->bio_flags & BIO_ORDERED) != 0 || 2333a6e0c5daSWarner Losh (bp->bio_cmd != BIO_DELETE && (softc->flags & ADA_FLAG_NEED_OTAG) != 0)) { 233452c9ce25SScott Long softc->flags &= ~ADA_FLAG_NEED_OTAG; 2335030844d1SAlexander Motin softc->flags |= ADA_FLAG_WAS_OTAG; 233646f118feSAlexander Motin tag_code = 0; 233752c9ce25SScott Long } else { 233846f118feSAlexander Motin tag_code = 1; 233952c9ce25SScott Long } 234052c9ce25SScott Long switch (bp->bio_cmd) { 234152c9ce25SScott Long case BIO_WRITE: 234269114bc0SAlexander Motin case BIO_READ: 234352c9ce25SScott Long { 234452c9ce25SScott Long uint64_t lba = bp->bio_pblkno; 234552c9ce25SScott Long uint16_t count = bp->bio_bcount / softc->params.secsize; 2346a9934668SKenneth D. Merry void *data_ptr; 2347a9934668SKenneth D. Merry int rw_op; 2348a9934668SKenneth D. Merry 2349a9934668SKenneth D. Merry if (bp->bio_cmd == BIO_WRITE) { 2350a9934668SKenneth D. Merry softc->flags |= ADA_FLAG_DIRTY; 2351a9934668SKenneth D. Merry rw_op = CAM_DIR_OUT; 2352a9934668SKenneth D. Merry } else { 2353a9934668SKenneth D. Merry rw_op = CAM_DIR_IN; 2354a9934668SKenneth D. Merry } 2355a9934668SKenneth D. Merry 2356a9934668SKenneth D. Merry data_ptr = bp->bio_data; 2357a9934668SKenneth D. Merry if ((bp->bio_flags & (BIO_UNMAPPED|BIO_VLIST)) != 0) { 2358a9934668SKenneth D. Merry rw_op |= CAM_DATA_BIO; 2359a9934668SKenneth D. Merry data_ptr = bp; 2360a9934668SKenneth D. Merry } 2361a9934668SKenneth D. Merry 2362d38677d2SWarner Losh #ifdef CAM_TEST_FAILURE 2363e3a6d3a4SAlexander Motin int fail = 0; 236452c9ce25SScott Long 2365e3a6d3a4SAlexander Motin /* 2366e3a6d3a4SAlexander Motin * Support the failure ioctls. If the command is a 2367e3a6d3a4SAlexander Motin * read, and there are pending forced read errors, or 2368e3a6d3a4SAlexander Motin * if a write and pending write errors, then fail this 2369e3a6d3a4SAlexander Motin * operation with EIO. This is useful for testing 2370e3a6d3a4SAlexander Motin * purposes. Also, support having every Nth read fail. 2371e3a6d3a4SAlexander Motin * 2372e3a6d3a4SAlexander Motin * This is a rather blunt tool. 2373e3a6d3a4SAlexander Motin */ 2374e3a6d3a4SAlexander Motin if (bp->bio_cmd == BIO_READ) { 2375e3a6d3a4SAlexander Motin if (softc->force_read_error) { 2376e3a6d3a4SAlexander Motin softc->force_read_error--; 2377e3a6d3a4SAlexander Motin fail = 1; 2378e3a6d3a4SAlexander Motin } 2379e3a6d3a4SAlexander Motin if (softc->periodic_read_error > 0) { 2380e3a6d3a4SAlexander Motin if (++softc->periodic_read_count >= 2381e3a6d3a4SAlexander Motin softc->periodic_read_error) { 2382e3a6d3a4SAlexander Motin softc->periodic_read_count = 0; 2383e3a6d3a4SAlexander Motin fail = 1; 2384e3a6d3a4SAlexander Motin } 2385e3a6d3a4SAlexander Motin } 2386e3a6d3a4SAlexander Motin } else { 2387e3a6d3a4SAlexander Motin if (softc->force_write_error) { 2388e3a6d3a4SAlexander Motin softc->force_write_error--; 2389e3a6d3a4SAlexander Motin fail = 1; 2390e3a6d3a4SAlexander Motin } 2391e3a6d3a4SAlexander Motin } 2392e3a6d3a4SAlexander Motin if (fail) { 23934beec135SAlexander Motin biofinish(bp, NULL, EIO); 2394e3a6d3a4SAlexander Motin xpt_release_ccb(start_ccb); 2395e3a6d3a4SAlexander Motin adaschedule(periph); 2396e3a6d3a4SAlexander Motin return; 2397e3a6d3a4SAlexander Motin } 2398e3a6d3a4SAlexander Motin #endif 2399abc1e60eSKonstantin Belousov KASSERT((bp->bio_flags & BIO_UNMAPPED) == 0 || 2400abc1e60eSKonstantin Belousov round_page(bp->bio_bcount + bp->bio_ma_offset) / 2401abc1e60eSKonstantin Belousov PAGE_SIZE == bp->bio_ma_n, 2402abc1e60eSKonstantin Belousov ("Short bio %p", bp)); 240352c9ce25SScott Long cam_fill_ataio(ataio, 240452c9ce25SScott Long ada_retry_count, 240552c9ce25SScott Long adadone, 2406a9934668SKenneth D. Merry rw_op, 2407e4cc6558SWarner Losh 0, 2408a9934668SKenneth D. Merry data_ptr, 240952c9ce25SScott Long bp->bio_bcount, 241052c9ce25SScott Long ada_default_timeout*1000); 241152c9ce25SScott Long 241246f118feSAlexander Motin if ((softc->flags & ADA_FLAG_CAN_NCQ) && tag_code) { 241352c9ce25SScott Long if (bp->bio_cmd == BIO_READ) { 241452c9ce25SScott Long ata_ncq_cmd(ataio, ATA_READ_FPDMA_QUEUED, 241552c9ce25SScott Long lba, count); 241652c9ce25SScott Long } else { 241752c9ce25SScott Long ata_ncq_cmd(ataio, ATA_WRITE_FPDMA_QUEUED, 241852c9ce25SScott Long lba, count); 241952c9ce25SScott Long } 242052c9ce25SScott Long } else if ((softc->flags & ADA_FLAG_CAN_48BIT) && 242152c9ce25SScott Long (lba + count >= ATA_MAX_28BIT_LBA || 242246f118feSAlexander Motin count > 256)) { 24232e1eb332SMarius Strobl if (softc->flags & ADA_FLAG_CAN_DMA48) { 242452c9ce25SScott Long if (bp->bio_cmd == BIO_READ) { 242552c9ce25SScott Long ata_48bit_cmd(ataio, ATA_READ_DMA48, 242652c9ce25SScott Long 0, lba, count); 242752c9ce25SScott Long } else { 242852c9ce25SScott Long ata_48bit_cmd(ataio, ATA_WRITE_DMA48, 242952c9ce25SScott Long 0, lba, count); 243052c9ce25SScott Long } 243152c9ce25SScott Long } else { 243252c9ce25SScott Long if (bp->bio_cmd == BIO_READ) { 243346f118feSAlexander Motin ata_48bit_cmd(ataio, ATA_READ_MUL48, 243446f118feSAlexander Motin 0, lba, count); 243546f118feSAlexander Motin } else { 243646f118feSAlexander Motin ata_48bit_cmd(ataio, ATA_WRITE_MUL48, 243746f118feSAlexander Motin 0, lba, count); 243846f118feSAlexander Motin } 243946f118feSAlexander Motin } 244046f118feSAlexander Motin } else { 244146f118feSAlexander Motin if (count == 256) 244246f118feSAlexander Motin count = 0; 244346f118feSAlexander Motin if (softc->flags & ADA_FLAG_CAN_DMA) { 244446f118feSAlexander Motin if (bp->bio_cmd == BIO_READ) { 24457606b445SAlexander Motin ata_28bit_cmd(ataio, ATA_READ_DMA, 244652c9ce25SScott Long 0, lba, count); 244752c9ce25SScott Long } else { 24487606b445SAlexander Motin ata_28bit_cmd(ataio, ATA_WRITE_DMA, 244952c9ce25SScott Long 0, lba, count); 245052c9ce25SScott Long } 245146f118feSAlexander Motin } else { 245246f118feSAlexander Motin if (bp->bio_cmd == BIO_READ) { 245346f118feSAlexander Motin ata_28bit_cmd(ataio, ATA_READ_MUL, 245446f118feSAlexander Motin 0, lba, count); 245546f118feSAlexander Motin } else { 245646f118feSAlexander Motin ata_28bit_cmd(ataio, ATA_WRITE_MUL, 245746f118feSAlexander Motin 0, lba, count); 245846f118feSAlexander Motin } 245946f118feSAlexander Motin } 246052c9ce25SScott Long } 246152c9ce25SScott Long break; 24621c80ec0aSAlexander Motin } 2463a6e0c5daSWarner Losh case BIO_DELETE: 2464a6e0c5daSWarner Losh switch (softc->delete_method) { 2465a6e0c5daSWarner Losh case ADA_DELETE_NCQ_DSM_TRIM: 2466a6e0c5daSWarner Losh ada_ncq_dsmtrim(softc, bp, ataio); 2467a6e0c5daSWarner Losh break; 2468a6e0c5daSWarner Losh case ADA_DELETE_DSM_TRIM: 2469a6e0c5daSWarner Losh ada_dsmtrim(softc, bp, ataio); 2470a6e0c5daSWarner Losh break; 2471a6e0c5daSWarner Losh case ADA_DELETE_CFA_ERASE: 2472a6e0c5daSWarner Losh ada_cfaerase(softc, bp, ataio); 2473a6e0c5daSWarner Losh break; 2474a6e0c5daSWarner Losh default: 2475a6e0c5daSWarner Losh biofinish(bp, NULL, EOPNOTSUPP); 2476a6e0c5daSWarner Losh xpt_release_ccb(start_ccb); 2477a6e0c5daSWarner Losh adaschedule(periph); 2478a6e0c5daSWarner Losh return; 2479a6e0c5daSWarner Losh } 2480a6e0c5daSWarner Losh start_ccb->ccb_h.ccb_state = ADA_CCB_TRIM; 2481a6e0c5daSWarner Losh start_ccb->ccb_h.flags |= CAM_UNLOCKED; 2482a6e0c5daSWarner Losh cam_iosched_submit_trim(softc->cam_iosched); 2483a6e0c5daSWarner Losh goto out; 248452c9ce25SScott Long case BIO_FLUSH: 248552c9ce25SScott Long cam_fill_ataio(ataio, 248652c9ce25SScott Long 1, 248752c9ce25SScott Long adadone, 248852c9ce25SScott Long CAM_DIR_NONE, 248946f118feSAlexander Motin 0, 249052c9ce25SScott Long NULL, 249152c9ce25SScott Long 0, 249252c9ce25SScott Long ada_default_timeout*1000); 249352c9ce25SScott Long 249452c9ce25SScott Long if (softc->flags & ADA_FLAG_CAN_48BIT) 249552c9ce25SScott Long ata_48bit_cmd(ataio, ATA_FLUSHCACHE48, 0, 0, 0); 249652c9ce25SScott Long else 24977606b445SAlexander Motin ata_28bit_cmd(ataio, ATA_FLUSHCACHE, 0, 0, 0); 249852c9ce25SScott Long break; 24999a6844d5SKenneth D. Merry case BIO_ZONE: { 25009a6844d5SKenneth D. Merry int error, queue_ccb; 25019a6844d5SKenneth D. Merry 25029a6844d5SKenneth D. Merry queue_ccb = 0; 25039a6844d5SKenneth D. Merry 25049a6844d5SKenneth D. Merry error = ada_zone_cmd(periph, start_ccb, bp, &queue_ccb); 25059a6844d5SKenneth D. Merry if ((error != 0) 25069a6844d5SKenneth D. Merry || (queue_ccb == 0)) { 25079a6844d5SKenneth D. Merry biofinish(bp, NULL, error); 25089a6844d5SKenneth D. Merry xpt_release_ccb(start_ccb); 25099a6844d5SKenneth D. Merry return; 25109a6844d5SKenneth D. Merry } 25119a6844d5SKenneth D. Merry break; 25129a6844d5SKenneth D. Merry } 2513d176b803SScott Long default: 2514d176b803SScott Long biofinish(bp, NULL, EOPNOTSUPP); 2515d176b803SScott Long xpt_release_ccb(start_ccb); 2516d176b803SScott Long return; 251752c9ce25SScott Long } 251852c9ce25SScott Long start_ccb->ccb_h.ccb_state = ADA_CCB_BUFFER_IO; 2519227d67aaSAlexander Motin start_ccb->ccb_h.flags |= CAM_UNLOCKED; 25201c80ec0aSAlexander Motin out: 252152c9ce25SScott Long start_ccb->ccb_h.ccb_bp = bp; 2522c1bd46c2SAlexander Motin softc->outstanding_cmds++; 2523227d67aaSAlexander Motin softc->refcount++; 2524227d67aaSAlexander Motin cam_periph_unlock(periph); 252552c9ce25SScott Long xpt_action(start_ccb); 2526227d67aaSAlexander Motin cam_periph_lock(periph); 252752c9ce25SScott Long 25281c80ec0aSAlexander Motin /* May have more work to do, so ensure we stay scheduled */ 25291c80ec0aSAlexander Motin adaschedule(periph); 253052c9ce25SScott Long break; 253152c9ce25SScott Long } 25321ed6aaf9SAlexander Motin case ADA_STATE_RAHEAD: 2533f513d14cSAlexander Motin case ADA_STATE_WCACHE: 2534f513d14cSAlexander Motin { 2535f513d14cSAlexander Motin cam_fill_ataio(ataio, 2536f513d14cSAlexander Motin 1, 2537f513d14cSAlexander Motin adadone, 2538f513d14cSAlexander Motin CAM_DIR_NONE, 2539f513d14cSAlexander Motin 0, 2540f513d14cSAlexander Motin NULL, 2541f513d14cSAlexander Motin 0, 2542f513d14cSAlexander Motin ada_default_timeout*1000); 2543f513d14cSAlexander Motin 25441ed6aaf9SAlexander Motin if (softc->state == ADA_STATE_RAHEAD) { 25451ed6aaf9SAlexander Motin ata_28bit_cmd(ataio, ATA_SETFEATURES, ADA_RA ? 25461ed6aaf9SAlexander Motin ATA_SF_ENAB_RCACHE : ATA_SF_DIS_RCACHE, 0, 0); 25471ed6aaf9SAlexander Motin start_ccb->ccb_h.ccb_state = ADA_CCB_RAHEAD; 25481ed6aaf9SAlexander Motin } else { 25491ed6aaf9SAlexander Motin ata_28bit_cmd(ataio, ATA_SETFEATURES, ADA_WC ? 2550f513d14cSAlexander Motin ATA_SF_ENAB_WCACHE : ATA_SF_DIS_WCACHE, 0, 0); 2551f513d14cSAlexander Motin start_ccb->ccb_h.ccb_state = ADA_CCB_WCACHE; 25521ed6aaf9SAlexander Motin } 2553cccf4220SAlexander Motin start_ccb->ccb_h.flags |= CAM_DEV_QFREEZE; 2554f513d14cSAlexander Motin xpt_action(start_ccb); 2555f513d14cSAlexander Motin break; 2556f513d14cSAlexander Motin } 25579a6844d5SKenneth D. Merry case ADA_STATE_LOGDIR: 25589a6844d5SKenneth D. Merry { 25599a6844d5SKenneth D. Merry struct ata_gp_log_dir *log_dir; 25609a6844d5SKenneth D. Merry 25619a6844d5SKenneth D. Merry if ((softc->flags & ADA_FLAG_CAN_LOG) == 0) { 25629a6844d5SKenneth D. Merry adaprobedone(periph, start_ccb); 25639a6844d5SKenneth D. Merry break; 25649a6844d5SKenneth D. Merry } 25659a6844d5SKenneth D. Merry 25669a6844d5SKenneth D. Merry log_dir = malloc(sizeof(*log_dir), M_ATADA, M_NOWAIT|M_ZERO); 25679a6844d5SKenneth D. Merry if (log_dir == NULL) { 25689a6844d5SKenneth D. Merry xpt_print(periph->path, "Couldn't malloc log_dir " 25699a6844d5SKenneth D. Merry "data\n"); 25709a6844d5SKenneth D. Merry softc->state = ADA_STATE_NORMAL; 25719a6844d5SKenneth D. Merry xpt_release_ccb(start_ccb); 25729a6844d5SKenneth D. Merry break; 25739a6844d5SKenneth D. Merry } 25749a6844d5SKenneth D. Merry 25759a6844d5SKenneth D. Merry ata_read_log(ataio, 25769a6844d5SKenneth D. Merry /*retries*/1, 25779a6844d5SKenneth D. Merry /*cbfcnp*/adadone, 25789a6844d5SKenneth D. Merry /*log_address*/ ATA_LOG_DIRECTORY, 25799a6844d5SKenneth D. Merry /*page_number*/ 0, 25809a6844d5SKenneth D. Merry /*block_count*/ 1, 25819a6844d5SKenneth D. Merry /*protocol*/ softc->flags & ADA_FLAG_CAN_DMA ? 25829a6844d5SKenneth D. Merry CAM_ATAIO_DMA : 0, 25839a6844d5SKenneth D. Merry /*data_ptr*/ (uint8_t *)log_dir, 25849a6844d5SKenneth D. Merry /*dxfer_len*/sizeof(*log_dir), 25859a6844d5SKenneth D. Merry /*timeout*/ada_default_timeout*1000); 25869a6844d5SKenneth D. Merry 25879a6844d5SKenneth D. Merry start_ccb->ccb_h.ccb_state = ADA_CCB_LOGDIR; 25889a6844d5SKenneth D. Merry xpt_action(start_ccb); 25899a6844d5SKenneth D. Merry break; 25909a6844d5SKenneth D. Merry } 25919a6844d5SKenneth D. Merry case ADA_STATE_IDDIR: 25929a6844d5SKenneth D. Merry { 25939a6844d5SKenneth D. Merry struct ata_identify_log_pages *id_dir; 25949a6844d5SKenneth D. Merry 25959a6844d5SKenneth D. Merry id_dir = malloc(sizeof(*id_dir), M_ATADA, M_NOWAIT | M_ZERO); 25969a6844d5SKenneth D. Merry if (id_dir == NULL) { 25979a6844d5SKenneth D. Merry xpt_print(periph->path, "Couldn't malloc id_dir " 25989a6844d5SKenneth D. Merry "data\n"); 25999a6844d5SKenneth D. Merry adaprobedone(periph, start_ccb); 26009a6844d5SKenneth D. Merry break; 26019a6844d5SKenneth D. Merry } 26029a6844d5SKenneth D. Merry 26039a6844d5SKenneth D. Merry ata_read_log(ataio, 26049a6844d5SKenneth D. Merry /*retries*/1, 26059a6844d5SKenneth D. Merry /*cbfcnp*/adadone, 26069a6844d5SKenneth D. Merry /*log_address*/ ATA_IDENTIFY_DATA_LOG, 26079a6844d5SKenneth D. Merry /*page_number*/ ATA_IDL_PAGE_LIST, 26089a6844d5SKenneth D. Merry /*block_count*/ 1, 26099a6844d5SKenneth D. Merry /*protocol*/ softc->flags & ADA_FLAG_CAN_DMA ? 26109a6844d5SKenneth D. Merry CAM_ATAIO_DMA : 0, 26119a6844d5SKenneth D. Merry /*data_ptr*/ (uint8_t *)id_dir, 26129a6844d5SKenneth D. Merry /*dxfer_len*/ sizeof(*id_dir), 26139a6844d5SKenneth D. Merry /*timeout*/ada_default_timeout*1000); 26149a6844d5SKenneth D. Merry 26159a6844d5SKenneth D. Merry start_ccb->ccb_h.ccb_state = ADA_CCB_IDDIR; 26169a6844d5SKenneth D. Merry xpt_action(start_ccb); 26179a6844d5SKenneth D. Merry break; 26189a6844d5SKenneth D. Merry } 26199a6844d5SKenneth D. Merry case ADA_STATE_SUP_CAP: 26209a6844d5SKenneth D. Merry { 26219a6844d5SKenneth D. Merry struct ata_identify_log_sup_cap *sup_cap; 26229a6844d5SKenneth D. Merry 26239a6844d5SKenneth D. Merry sup_cap = malloc(sizeof(*sup_cap), M_ATADA, M_NOWAIT|M_ZERO); 26249a6844d5SKenneth D. Merry if (sup_cap == NULL) { 26259a6844d5SKenneth D. Merry xpt_print(periph->path, "Couldn't malloc sup_cap " 26269a6844d5SKenneth D. Merry "data\n"); 26279a6844d5SKenneth D. Merry adaprobedone(periph, start_ccb); 26289a6844d5SKenneth D. Merry break; 26299a6844d5SKenneth D. Merry } 26309a6844d5SKenneth D. Merry 26319a6844d5SKenneth D. Merry ata_read_log(ataio, 26329a6844d5SKenneth D. Merry /*retries*/1, 26339a6844d5SKenneth D. Merry /*cbfcnp*/adadone, 26349a6844d5SKenneth D. Merry /*log_address*/ ATA_IDENTIFY_DATA_LOG, 26359a6844d5SKenneth D. Merry /*page_number*/ ATA_IDL_SUP_CAP, 26369a6844d5SKenneth D. Merry /*block_count*/ 1, 26379a6844d5SKenneth D. Merry /*protocol*/ softc->flags & ADA_FLAG_CAN_DMA ? 26389a6844d5SKenneth D. Merry CAM_ATAIO_DMA : 0, 26399a6844d5SKenneth D. Merry /*data_ptr*/ (uint8_t *)sup_cap, 26409a6844d5SKenneth D. Merry /*dxfer_len*/ sizeof(*sup_cap), 26419a6844d5SKenneth D. Merry /*timeout*/ada_default_timeout*1000); 26429a6844d5SKenneth D. Merry 26439a6844d5SKenneth D. Merry start_ccb->ccb_h.ccb_state = ADA_CCB_SUP_CAP; 26449a6844d5SKenneth D. Merry xpt_action(start_ccb); 26459a6844d5SKenneth D. Merry break; 26469a6844d5SKenneth D. Merry } 26479a6844d5SKenneth D. Merry case ADA_STATE_ZONE: 26489a6844d5SKenneth D. Merry { 26499a6844d5SKenneth D. Merry struct ata_zoned_info_log *ata_zone; 26509a6844d5SKenneth D. Merry 26519a6844d5SKenneth D. Merry ata_zone = malloc(sizeof(*ata_zone), M_ATADA, M_NOWAIT|M_ZERO); 26529a6844d5SKenneth D. Merry if (ata_zone == NULL) { 26539a6844d5SKenneth D. Merry xpt_print(periph->path, "Couldn't malloc ata_zone " 26549a6844d5SKenneth D. Merry "data\n"); 26559a6844d5SKenneth D. Merry adaprobedone(periph, start_ccb); 26569a6844d5SKenneth D. Merry break; 26579a6844d5SKenneth D. Merry } 26589a6844d5SKenneth D. Merry 26599a6844d5SKenneth D. Merry ata_read_log(ataio, 26609a6844d5SKenneth D. Merry /*retries*/1, 26619a6844d5SKenneth D. Merry /*cbfcnp*/adadone, 26629a6844d5SKenneth D. Merry /*log_address*/ ATA_IDENTIFY_DATA_LOG, 26639a6844d5SKenneth D. Merry /*page_number*/ ATA_IDL_ZDI, 26649a6844d5SKenneth D. Merry /*block_count*/ 1, 26659a6844d5SKenneth D. Merry /*protocol*/ softc->flags & ADA_FLAG_CAN_DMA ? 26669a6844d5SKenneth D. Merry CAM_ATAIO_DMA : 0, 26679a6844d5SKenneth D. Merry /*data_ptr*/ (uint8_t *)ata_zone, 26689a6844d5SKenneth D. Merry /*dxfer_len*/ sizeof(*ata_zone), 26699a6844d5SKenneth D. Merry /*timeout*/ada_default_timeout*1000); 26709a6844d5SKenneth D. Merry 26719a6844d5SKenneth D. Merry start_ccb->ccb_h.ccb_state = ADA_CCB_ZONE; 26729a6844d5SKenneth D. Merry xpt_action(start_ccb); 26739a6844d5SKenneth D. Merry break; 267452c9ce25SScott Long } 267552c9ce25SScott Long } 26769a6844d5SKenneth D. Merry } 26779a6844d5SKenneth D. Merry 26789a6844d5SKenneth D. Merry static void 26799a6844d5SKenneth D. Merry adaprobedone(struct cam_periph *periph, union ccb *ccb) 26809a6844d5SKenneth D. Merry { 26819a6844d5SKenneth D. Merry struct ada_softc *softc; 26829a6844d5SKenneth D. Merry 26839a6844d5SKenneth D. Merry softc = (struct ada_softc *)periph->softc; 26849a6844d5SKenneth D. Merry 26851907e1c0SWarner Losh /* 26861907e1c0SWarner Losh * Since our peripheral may be invalidated by an error we must release 26871907e1c0SWarner Losh * our CCB before releasing the reference on the peripheral. The 26881907e1c0SWarner Losh * peripheral will only go away once the last reference is removed, and 26891907e1c0SWarner Losh * we need it around for the CCB release operation. 26901907e1c0SWarner Losh */ 26919a6844d5SKenneth D. Merry if (ccb != NULL) 26929a6844d5SKenneth D. Merry xpt_release_ccb(ccb); 26939a6844d5SKenneth D. Merry 26949a6844d5SKenneth D. Merry softc->state = ADA_STATE_NORMAL; 26959a6844d5SKenneth D. Merry softc->flags |= ADA_FLAG_PROBED; 26969a6844d5SKenneth D. Merry adaschedule(periph); 26979a6844d5SKenneth D. Merry if ((softc->flags & ADA_FLAG_ANNOUNCED) == 0) { 26989a6844d5SKenneth D. Merry softc->flags |= ADA_FLAG_ANNOUNCED; 2699*90bcc81bSAlexander Motin 2700*90bcc81bSAlexander Motin /* 2701*90bcc81bSAlexander Motin * We'll release this reference once GEOM calls us back via 2702*90bcc81bSAlexander Motin * adadiskgonecb(), telling us that our provider has been freed. 2703*90bcc81bSAlexander Motin */ 2704*90bcc81bSAlexander Motin if (cam_periph_acquire(periph) == 0) 2705*90bcc81bSAlexander Motin disk_create(softc->disk, DISK_VERSION); 2706*90bcc81bSAlexander Motin 2707*90bcc81bSAlexander Motin cam_periph_release_boot(periph); 27089a6844d5SKenneth D. Merry } 2709*90bcc81bSAlexander Motin cam_periph_release_locked(periph); 27109a6844d5SKenneth D. Merry } 27119a6844d5SKenneth D. Merry 27129a6844d5SKenneth D. Merry static void 27139a6844d5SKenneth D. Merry adazonedone(struct cam_periph *periph, union ccb *ccb) 27149a6844d5SKenneth D. Merry { 27159a6844d5SKenneth D. Merry struct bio *bp; 27169a6844d5SKenneth D. Merry 27179a6844d5SKenneth D. Merry bp = (struct bio *)ccb->ccb_h.ccb_bp; 27189a6844d5SKenneth D. Merry 27199a6844d5SKenneth D. Merry switch (bp->bio_zone.zone_cmd) { 27209a6844d5SKenneth D. Merry case DISK_ZONE_OPEN: 27219a6844d5SKenneth D. Merry case DISK_ZONE_CLOSE: 27229a6844d5SKenneth D. Merry case DISK_ZONE_FINISH: 27239a6844d5SKenneth D. Merry case DISK_ZONE_RWP: 27249a6844d5SKenneth D. Merry break; 27259a6844d5SKenneth D. Merry case DISK_ZONE_REPORT_ZONES: { 27269a6844d5SKenneth D. Merry uint32_t avail_len; 27279a6844d5SKenneth D. Merry struct disk_zone_report *rep; 27289a6844d5SKenneth D. Merry struct scsi_report_zones_hdr *hdr; 27299a6844d5SKenneth D. Merry struct scsi_report_zones_desc *desc; 27309a6844d5SKenneth D. Merry struct disk_zone_rep_entry *entry; 2731151ba793SAlexander Kabaev uint32_t hdr_len, num_avail; 27329a6844d5SKenneth D. Merry uint32_t num_to_fill, i; 27339a6844d5SKenneth D. Merry 27349a6844d5SKenneth D. Merry rep = &bp->bio_zone.zone_params.report; 27359a6844d5SKenneth D. Merry avail_len = ccb->ataio.dxfer_len - ccb->ataio.resid; 27369a6844d5SKenneth D. Merry /* 27379a6844d5SKenneth D. Merry * Note that bio_resid isn't normally used for zone 27389a6844d5SKenneth D. Merry * commands, but it is used by devstat_end_transaction_bio() 27399a6844d5SKenneth D. Merry * to determine how much data was transferred. Because 27409a6844d5SKenneth D. Merry * the size of the SCSI/ATA data structures is different 27419a6844d5SKenneth D. Merry * than the size of the BIO interface structures, the 27429a6844d5SKenneth D. Merry * amount of data actually transferred from the drive will 27439a6844d5SKenneth D. Merry * be different than the amount of data transferred to 27449a6844d5SKenneth D. Merry * the user. 27459a6844d5SKenneth D. Merry */ 27469a6844d5SKenneth D. Merry hdr = (struct scsi_report_zones_hdr *)ccb->ataio.data_ptr; 27479a6844d5SKenneth D. Merry if (avail_len < sizeof(*hdr)) { 27489a6844d5SKenneth D. Merry /* 27499a6844d5SKenneth D. Merry * Is there a better error than EIO here? We asked 27509a6844d5SKenneth D. Merry * for at least the header, and we got less than 27519a6844d5SKenneth D. Merry * that. 27529a6844d5SKenneth D. Merry */ 27539a6844d5SKenneth D. Merry bp->bio_error = EIO; 27549a6844d5SKenneth D. Merry bp->bio_flags |= BIO_ERROR; 27559a6844d5SKenneth D. Merry bp->bio_resid = bp->bio_bcount; 27569a6844d5SKenneth D. Merry break; 27579a6844d5SKenneth D. Merry } 27589a6844d5SKenneth D. Merry 27599a6844d5SKenneth D. Merry hdr_len = le32dec(hdr->length); 27609a6844d5SKenneth D. Merry if (hdr_len > 0) 27619a6844d5SKenneth D. Merry rep->entries_available = hdr_len / sizeof(*desc); 27629a6844d5SKenneth D. Merry else 27639a6844d5SKenneth D. Merry rep->entries_available = 0; 27649a6844d5SKenneth D. Merry /* 27659a6844d5SKenneth D. Merry * NOTE: using the same values for the BIO version of the 27669a6844d5SKenneth D. Merry * same field as the SCSI/ATA values. This means we could 27679a6844d5SKenneth D. Merry * get some additional values that aren't defined in bio.h 27689a6844d5SKenneth D. Merry * if more values of the same field are defined later. 27699a6844d5SKenneth D. Merry */ 27709a6844d5SKenneth D. Merry rep->header.same = hdr->byte4 & SRZ_SAME_MASK; 27719a6844d5SKenneth D. Merry rep->header.maximum_lba = le64dec(hdr->maximum_lba); 27729a6844d5SKenneth D. Merry /* 27739a6844d5SKenneth D. Merry * If the drive reports no entries that match the query, 27749a6844d5SKenneth D. Merry * we're done. 27759a6844d5SKenneth D. Merry */ 27769a6844d5SKenneth D. Merry if (hdr_len == 0) { 27779a6844d5SKenneth D. Merry rep->entries_filled = 0; 27789a6844d5SKenneth D. Merry bp->bio_resid = bp->bio_bcount; 27799a6844d5SKenneth D. Merry break; 27809a6844d5SKenneth D. Merry } 27819a6844d5SKenneth D. Merry 27829a6844d5SKenneth D. Merry num_avail = min((avail_len - sizeof(*hdr)) / sizeof(*desc), 27839a6844d5SKenneth D. Merry hdr_len / sizeof(*desc)); 27849a6844d5SKenneth D. Merry /* 27859a6844d5SKenneth D. Merry * If the drive didn't return any data, then we're done. 27869a6844d5SKenneth D. Merry */ 27879a6844d5SKenneth D. Merry if (num_avail == 0) { 27889a6844d5SKenneth D. Merry rep->entries_filled = 0; 27899a6844d5SKenneth D. Merry bp->bio_resid = bp->bio_bcount; 27909a6844d5SKenneth D. Merry break; 27919a6844d5SKenneth D. Merry } 27929a6844d5SKenneth D. Merry 27939a6844d5SKenneth D. Merry num_to_fill = min(num_avail, rep->entries_allocated); 27949a6844d5SKenneth D. Merry /* 27959a6844d5SKenneth D. Merry * If the user didn't allocate any entries for us to fill, 27969a6844d5SKenneth D. Merry * we're done. 27979a6844d5SKenneth D. Merry */ 27989a6844d5SKenneth D. Merry if (num_to_fill == 0) { 27999a6844d5SKenneth D. Merry rep->entries_filled = 0; 28009a6844d5SKenneth D. Merry bp->bio_resid = bp->bio_bcount; 28019a6844d5SKenneth D. Merry break; 28029a6844d5SKenneth D. Merry } 28039a6844d5SKenneth D. Merry 28049a6844d5SKenneth D. Merry for (i = 0, desc = &hdr->desc_list[0], entry=&rep->entries[0]; 28059a6844d5SKenneth D. Merry i < num_to_fill; i++, desc++, entry++) { 28069a6844d5SKenneth D. Merry /* 28079a6844d5SKenneth D. Merry * NOTE: we're mapping the values here directly 28089a6844d5SKenneth D. Merry * from the SCSI/ATA bit definitions to the bio.h 28099a6844d5SKenneth D. Merry * definitions. There is also a warning in 28109a6844d5SKenneth D. Merry * disk_zone.h, but the impact is that if 28119a6844d5SKenneth D. Merry * additional values are added in the SCSI/ATA 28129a6844d5SKenneth D. Merry * specs these will be visible to consumers of 28139a6844d5SKenneth D. Merry * this interface. 28149a6844d5SKenneth D. Merry */ 28159a6844d5SKenneth D. Merry entry->zone_type = desc->zone_type & SRZ_TYPE_MASK; 28169a6844d5SKenneth D. Merry entry->zone_condition = 28179a6844d5SKenneth D. Merry (desc->zone_flags & SRZ_ZONE_COND_MASK) >> 28189a6844d5SKenneth D. Merry SRZ_ZONE_COND_SHIFT; 28199a6844d5SKenneth D. Merry entry->zone_flags |= desc->zone_flags & 28209a6844d5SKenneth D. Merry (SRZ_ZONE_NON_SEQ|SRZ_ZONE_RESET); 28219a6844d5SKenneth D. Merry entry->zone_length = le64dec(desc->zone_length); 28229a6844d5SKenneth D. Merry entry->zone_start_lba = le64dec(desc->zone_start_lba); 28239a6844d5SKenneth D. Merry entry->write_pointer_lba = 28249a6844d5SKenneth D. Merry le64dec(desc->write_pointer_lba); 28259a6844d5SKenneth D. Merry } 28269a6844d5SKenneth D. Merry rep->entries_filled = num_to_fill; 28279a6844d5SKenneth D. Merry /* 28289a6844d5SKenneth D. Merry * Note that this residual is accurate from the user's 28299a6844d5SKenneth D. Merry * standpoint, but the amount transferred isn't accurate 28309a6844d5SKenneth D. Merry * from the standpoint of what actually came back from the 28319a6844d5SKenneth D. Merry * drive. 28329a6844d5SKenneth D. Merry */ 28339a6844d5SKenneth D. Merry bp->bio_resid = bp->bio_bcount - (num_to_fill * sizeof(*entry)); 28349a6844d5SKenneth D. Merry break; 28359a6844d5SKenneth D. Merry } 28369a6844d5SKenneth D. Merry case DISK_ZONE_GET_PARAMS: 28379a6844d5SKenneth D. Merry default: 28389a6844d5SKenneth D. Merry /* 28399a6844d5SKenneth D. Merry * In theory we should not get a GET_PARAMS bio, since it 28409a6844d5SKenneth D. Merry * should be handled without queueing the command to the 28419a6844d5SKenneth D. Merry * drive. 28429a6844d5SKenneth D. Merry */ 28439a6844d5SKenneth D. Merry panic("%s: Invalid zone command %d", __func__, 28449a6844d5SKenneth D. Merry bp->bio_zone.zone_cmd); 28459a6844d5SKenneth D. Merry break; 28469a6844d5SKenneth D. Merry } 28479a6844d5SKenneth D. Merry 28489a6844d5SKenneth D. Merry if (bp->bio_zone.zone_cmd == DISK_ZONE_REPORT_ZONES) 28499a6844d5SKenneth D. Merry free(ccb->ataio.data_ptr, M_ATADA); 28509a6844d5SKenneth D. Merry } 28519a6844d5SKenneth D. Merry 285252c9ce25SScott Long static void 285352c9ce25SScott Long adadone(struct cam_periph *periph, union ccb *done_ccb) 285452c9ce25SScott Long { 285552c9ce25SScott Long struct ada_softc *softc; 285652c9ce25SScott Long struct ccb_ataio *ataio; 2857cccf4220SAlexander Motin struct cam_path *path; 28589a6844d5SKenneth D. Merry uint32_t priority; 28597651b989SAlexander Motin int state; 286052c9ce25SScott Long 286152c9ce25SScott Long softc = (struct ada_softc *)periph->softc; 286252c9ce25SScott Long ataio = &done_ccb->ataio; 2863cccf4220SAlexander Motin path = done_ccb->ccb_h.path; 28649a6844d5SKenneth D. Merry priority = done_ccb->ccb_h.pinfo.priority; 2865fddde2b8SAlexander Motin 2866cccf4220SAlexander Motin CAM_DEBUG(path, CAM_DEBUG_TRACE, ("adadone\n")); 2867fddde2b8SAlexander Motin 28687651b989SAlexander Motin state = ataio->ccb_h.ccb_state & ADA_CCB_TYPE_MASK; 28697651b989SAlexander Motin switch (state) { 287052c9ce25SScott Long case ADA_CCB_BUFFER_IO: 28711c80ec0aSAlexander Motin case ADA_CCB_TRIM: 287252c9ce25SScott Long { 287352c9ce25SScott Long struct bio *bp; 287452c9ce25SScott Long int error; 287552c9ce25SScott Long 2876227d67aaSAlexander Motin cam_periph_lock(periph); 2877a6e0c5daSWarner Losh bp = (struct bio *)done_ccb->ccb_h.ccb_bp; 28787651b989SAlexander Motin if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { 28796c8ab086SWarner Losh error = adaerror(done_ccb, CAM_RETRY_SELTO, 0); 288052c9ce25SScott Long if (error == ERESTART) { 288146f118feSAlexander Motin /* A retry was scheduled, so just return. */ 2882227d67aaSAlexander Motin cam_periph_unlock(periph); 288352c9ce25SScott Long return; 288452c9ce25SScott Long } 288552c9ce25SScott Long if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) 2886cccf4220SAlexander Motin cam_release_devq(path, 288752c9ce25SScott Long /*relsim_flags*/0, 288852c9ce25SScott Long /*reduction*/0, 288952c9ce25SScott Long /*timeout*/0, 289052c9ce25SScott Long /*getcount_only*/0); 2891a6e0c5daSWarner Losh /* 2892a6e0c5daSWarner Losh * If we get an error on an NCQ DSM TRIM, fall back 2893a6e0c5daSWarner Losh * to a non-NCQ DSM TRIM forever. Please note that if 2894a6e0c5daSWarner Losh * CAN_NCQ_TRIM is set, CAN_TRIM is necessarily set too. 2895a6e0c5daSWarner Losh * However, for this one trim, we treat it as advisory 2896a6e0c5daSWarner Losh * and return success up the stack. 2897a6e0c5daSWarner Losh */ 2898a6e0c5daSWarner Losh if (state == ADA_CCB_TRIM && 2899a6e0c5daSWarner Losh error != 0 && 2900a6e0c5daSWarner Losh (softc->flags & ADA_FLAG_CAN_NCQ_TRIM) != 0) { 2901a6e0c5daSWarner Losh softc->flags &= ~ADA_FLAG_CAN_NCQ_TRIM; 2902a6e0c5daSWarner Losh error = 0; 2903a6e0c5daSWarner Losh adasetdeletemethod(softc); 2904a6e0c5daSWarner Losh } 290552c9ce25SScott Long } else { 290652c9ce25SScott Long if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) 290752c9ce25SScott Long panic("REQ_CMP with QFRZN"); 29089a6844d5SKenneth D. Merry 29097651b989SAlexander Motin error = 0; 29107651b989SAlexander Motin } 29117651b989SAlexander Motin bp->bio_error = error; 29127651b989SAlexander Motin if (error != 0) { 29137651b989SAlexander Motin bp->bio_resid = bp->bio_bcount; 29147651b989SAlexander Motin bp->bio_flags |= BIO_ERROR; 29157651b989SAlexander Motin } else { 29169a6844d5SKenneth D. Merry if (bp->bio_cmd == BIO_ZONE) 29179a6844d5SKenneth D. Merry adazonedone(periph, done_ccb); 29189a6844d5SKenneth D. Merry else if (state == ADA_CCB_TRIM) 29197651b989SAlexander Motin bp->bio_resid = 0; 29207651b989SAlexander Motin else 292152c9ce25SScott Long bp->bio_resid = ataio->resid; 29229a6844d5SKenneth D. Merry 29239a6844d5SKenneth D. Merry if ((bp->bio_resid > 0) 29249a6844d5SKenneth D. Merry && (bp->bio_cmd != BIO_ZONE)) 292552c9ce25SScott Long bp->bio_flags |= BIO_ERROR; 292652c9ce25SScott Long } 292752c9ce25SScott Long softc->outstanding_cmds--; 292852c9ce25SScott Long if (softc->outstanding_cmds == 0) 2929030844d1SAlexander Motin softc->flags |= ADA_FLAG_WAS_OTAG; 2930a6e0c5daSWarner Losh 29319754579bSWarner Losh /* 29329754579bSWarner Losh * We need to call cam_iosched before we call biodone so that we 29339754579bSWarner Losh * don't measure any activity that happens in the completion 29349754579bSWarner Losh * routine, which in the case of sendfile can be quite 2935d9a7a61bSWarner Losh * extensive. Release the periph refcount taken in adastart() 2936d9a7a61bSWarner Losh * for each CCB. 29379754579bSWarner Losh */ 2938a6e0c5daSWarner Losh cam_iosched_bio_complete(softc->cam_iosched, bp, done_ccb); 2939227d67aaSAlexander Motin xpt_release_ccb(done_ccb); 2940d9a7a61bSWarner Losh KASSERT(softc->refcount >= 1, ("adadone softc %p refcount %d", softc, softc->refcount)); 2941d9a7a61bSWarner Losh softc->refcount--; 29427651b989SAlexander Motin if (state == ADA_CCB_TRIM) { 29432030b294SAlexander Motin TAILQ_HEAD(, bio) queue; 29442030b294SAlexander Motin struct bio *bp1; 294552c9ce25SScott Long 29462030b294SAlexander Motin TAILQ_INIT(&queue); 29472030b294SAlexander Motin TAILQ_CONCAT(&queue, &softc->trim_req.bps, bio_queue); 29480ac66574SWarner Losh /* 29490ac66574SWarner Losh * Normally, the xpt_release_ccb() above would make sure 29500ac66574SWarner Losh * that when we have more work to do, that work would 29510ac66574SWarner Losh * get kicked off. However, we specifically keep 29520ac66574SWarner Losh * trim_running set to 0 before the call above to allow 29530ac66574SWarner Losh * other I/O to progress when many BIO_DELETE requests 29540ac66574SWarner Losh * are pushed down. We set trim_running to 0 and call 29550ac66574SWarner Losh * daschedule again so that we don't stall if there are 29560ac66574SWarner Losh * no other I/Os pending apart from BIO_DELETEs. 29570ac66574SWarner Losh */ 2958a6e0c5daSWarner Losh cam_iosched_trim_done(softc->cam_iosched); 2959227d67aaSAlexander Motin adaschedule(periph); 2960227d67aaSAlexander Motin cam_periph_unlock(periph); 29612030b294SAlexander Motin while ((bp1 = TAILQ_FIRST(&queue)) != NULL) { 29622030b294SAlexander Motin TAILQ_REMOVE(&queue, bp1, bio_queue); 29632030b294SAlexander Motin bp1->bio_error = error; 29642030b294SAlexander Motin if (error != 0) { 29651c80ec0aSAlexander Motin bp1->bio_flags |= BIO_ERROR; 29667651b989SAlexander Motin bp1->bio_resid = bp1->bio_bcount; 29677651b989SAlexander Motin } else 29687651b989SAlexander Motin bp1->bio_resid = 0; 29691c80ec0aSAlexander Motin biodone(bp1); 29701c80ec0aSAlexander Motin } 2971227d67aaSAlexander Motin } else { 2972a6e0c5daSWarner Losh adaschedule(periph); 2973227d67aaSAlexander Motin cam_periph_unlock(periph); 297452c9ce25SScott Long biodone(bp); 2975227d67aaSAlexander Motin } 2976227d67aaSAlexander Motin return; 297752c9ce25SScott Long } 29781ed6aaf9SAlexander Motin case ADA_CCB_RAHEAD: 29791ed6aaf9SAlexander Motin { 29801ed6aaf9SAlexander Motin if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { 29811ed6aaf9SAlexander Motin if (adaerror(done_ccb, 0, 0) == ERESTART) { 2982cccf4220SAlexander Motin /* Drop freeze taken due to CAM_DEV_QFREEZE */ 2983cccf4220SAlexander Motin cam_release_devq(path, 0, 0, 0, FALSE); 29841ed6aaf9SAlexander Motin return; 29851ed6aaf9SAlexander Motin } else if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) { 2986cccf4220SAlexander Motin cam_release_devq(path, 29871ed6aaf9SAlexander Motin /*relsim_flags*/0, 29881ed6aaf9SAlexander Motin /*reduction*/0, 29891ed6aaf9SAlexander Motin /*timeout*/0, 29901ed6aaf9SAlexander Motin /*getcount_only*/0); 29911ed6aaf9SAlexander Motin } 29921ed6aaf9SAlexander Motin } 29931ed6aaf9SAlexander Motin 29949a6844d5SKenneth D. Merry xpt_release_ccb(done_ccb); 29951ed6aaf9SAlexander Motin softc->state = ADA_STATE_WCACHE; 29969a6844d5SKenneth D. Merry xpt_schedule(periph, priority); 2997cccf4220SAlexander Motin /* Drop freeze taken due to CAM_DEV_QFREEZE */ 2998cccf4220SAlexander Motin cam_release_devq(path, 0, 0, 0, FALSE); 29991ed6aaf9SAlexander Motin return; 30001ed6aaf9SAlexander Motin } 3001f513d14cSAlexander Motin case ADA_CCB_WCACHE: 3002f513d14cSAlexander Motin { 3003f513d14cSAlexander Motin if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { 3004f513d14cSAlexander Motin if (adaerror(done_ccb, 0, 0) == ERESTART) { 30059a6844d5SKenneth D. Merry /* Drop freeze taken due to CAM_DEV_QFREEZE */ 30069a6844d5SKenneth D. Merry cam_release_devq(path, 0, 0, 0, FALSE); 30079a6844d5SKenneth D. Merry return; 3008f513d14cSAlexander Motin } else if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) { 3009cccf4220SAlexander Motin cam_release_devq(path, 3010f513d14cSAlexander Motin /*relsim_flags*/0, 3011f513d14cSAlexander Motin /*reduction*/0, 3012f513d14cSAlexander Motin /*timeout*/0, 3013f513d14cSAlexander Motin /*getcount_only*/0); 3014f513d14cSAlexander Motin } 3015f513d14cSAlexander Motin } 3016f513d14cSAlexander Motin 3017cccf4220SAlexander Motin /* Drop freeze taken due to CAM_DEV_QFREEZE */ 3018cccf4220SAlexander Motin cam_release_devq(path, 0, 0, 0, FALSE); 30199a6844d5SKenneth D. Merry 3020600fd98fSKenneth D. Merry if ((softc->flags & ADA_FLAG_CAN_LOG) 3021600fd98fSKenneth D. Merry && (softc->zone_mode != ADA_ZONE_NONE)) { 30229a6844d5SKenneth D. Merry xpt_release_ccb(done_ccb); 30239a6844d5SKenneth D. Merry softc->state = ADA_STATE_LOGDIR; 30249a6844d5SKenneth D. Merry xpt_schedule(periph, priority); 30259a6844d5SKenneth D. Merry } else { 30269a6844d5SKenneth D. Merry adaprobedone(periph, done_ccb); 30279a6844d5SKenneth D. Merry } 30289a6844d5SKenneth D. Merry return; 30299a6844d5SKenneth D. Merry } 30309a6844d5SKenneth D. Merry case ADA_CCB_LOGDIR: 30319a6844d5SKenneth D. Merry { 30329a6844d5SKenneth D. Merry int error; 30339a6844d5SKenneth D. Merry 30349a6844d5SKenneth D. Merry if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) { 30359a6844d5SKenneth D. Merry error = 0; 30369a6844d5SKenneth D. Merry softc->valid_logdir_len = 0; 30379a6844d5SKenneth D. Merry bzero(&softc->ata_logdir, sizeof(softc->ata_logdir)); 30389a6844d5SKenneth D. Merry softc->valid_logdir_len = 30399a6844d5SKenneth D. Merry ataio->dxfer_len - ataio->resid; 30409a6844d5SKenneth D. Merry if (softc->valid_logdir_len > 0) 30419a6844d5SKenneth D. Merry bcopy(ataio->data_ptr, &softc->ata_logdir, 30429a6844d5SKenneth D. Merry min(softc->valid_logdir_len, 30439a6844d5SKenneth D. Merry sizeof(softc->ata_logdir))); 30449a6844d5SKenneth D. Merry /* 30459a6844d5SKenneth D. Merry * Figure out whether the Identify Device log is 30469a6844d5SKenneth D. Merry * supported. The General Purpose log directory 30479a6844d5SKenneth D. Merry * has a header, and lists the number of pages 30489a6844d5SKenneth D. Merry * available for each GP log identified by the 30499a6844d5SKenneth D. Merry * offset into the list. 30509a6844d5SKenneth D. Merry */ 30519a6844d5SKenneth D. Merry if ((softc->valid_logdir_len >= 30529a6844d5SKenneth D. Merry ((ATA_IDENTIFY_DATA_LOG + 1) * sizeof(uint16_t))) 30539a6844d5SKenneth D. Merry && (le16dec(softc->ata_logdir.header) == 30549a6844d5SKenneth D. Merry ATA_GP_LOG_DIR_VERSION) 30559a6844d5SKenneth D. Merry && (le16dec(&softc->ata_logdir.num_pages[ 30569a6844d5SKenneth D. Merry (ATA_IDENTIFY_DATA_LOG * 30579a6844d5SKenneth D. Merry sizeof(uint16_t)) - sizeof(uint16_t)]) > 0)){ 30589a6844d5SKenneth D. Merry softc->flags |= ADA_FLAG_CAN_IDLOG; 30599a6844d5SKenneth D. Merry } else { 30609a6844d5SKenneth D. Merry softc->flags &= ~ADA_FLAG_CAN_IDLOG; 30619a6844d5SKenneth D. Merry } 30629a6844d5SKenneth D. Merry } else { 30639a6844d5SKenneth D. Merry error = adaerror(done_ccb, CAM_RETRY_SELTO, 30649a6844d5SKenneth D. Merry SF_RETRY_UA|SF_NO_PRINT); 30659a6844d5SKenneth D. Merry if (error == ERESTART) 30669a6844d5SKenneth D. Merry return; 30679a6844d5SKenneth D. Merry else if (error != 0) { 30689a6844d5SKenneth D. Merry /* 30699a6844d5SKenneth D. Merry * If we can't get the ATA log directory, 30709a6844d5SKenneth D. Merry * then ATA logs are effectively not 30719a6844d5SKenneth D. Merry * supported even if the bit is set in the 30729a6844d5SKenneth D. Merry * identify data. 30739a6844d5SKenneth D. Merry */ 30749a6844d5SKenneth D. Merry softc->flags &= ~(ADA_FLAG_CAN_LOG | 30759a6844d5SKenneth D. Merry ADA_FLAG_CAN_IDLOG); 30769a6844d5SKenneth D. Merry if ((done_ccb->ccb_h.status & 30779a6844d5SKenneth D. Merry CAM_DEV_QFRZN) != 0) { 30789a6844d5SKenneth D. Merry /* Don't wedge this device's queue */ 30799a6844d5SKenneth D. Merry cam_release_devq(done_ccb->ccb_h.path, 30809a6844d5SKenneth D. Merry /*relsim_flags*/0, 30819a6844d5SKenneth D. Merry /*reduction*/0, 30829a6844d5SKenneth D. Merry /*timeout*/0, 30839a6844d5SKenneth D. Merry /*getcount_only*/0); 30849a6844d5SKenneth D. Merry } 30859a6844d5SKenneth D. Merry } 30869a6844d5SKenneth D. Merry } 30879a6844d5SKenneth D. Merry 30889a6844d5SKenneth D. Merry free(ataio->data_ptr, M_ATADA); 30899a6844d5SKenneth D. Merry 30909a6844d5SKenneth D. Merry if ((error == 0) 30919a6844d5SKenneth D. Merry && (softc->flags & ADA_FLAG_CAN_IDLOG)) { 30929a6844d5SKenneth D. Merry softc->state = ADA_STATE_IDDIR; 30939a6844d5SKenneth D. Merry xpt_release_ccb(done_ccb); 30949a6844d5SKenneth D. Merry xpt_schedule(periph, priority); 30959a6844d5SKenneth D. Merry } else 30969a6844d5SKenneth D. Merry adaprobedone(periph, done_ccb); 30979a6844d5SKenneth D. Merry 30989a6844d5SKenneth D. Merry return; 30999a6844d5SKenneth D. Merry } 31009a6844d5SKenneth D. Merry case ADA_CCB_IDDIR: { 31019a6844d5SKenneth D. Merry int error; 31029a6844d5SKenneth D. Merry 31039a6844d5SKenneth D. Merry if ((ataio->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) { 31049a6844d5SKenneth D. Merry off_t entries_offset, max_entries; 31059a6844d5SKenneth D. Merry error = 0; 31069a6844d5SKenneth D. Merry 31079a6844d5SKenneth D. Merry softc->valid_iddir_len = 0; 31089a6844d5SKenneth D. Merry bzero(&softc->ata_iddir, sizeof(softc->ata_iddir)); 31099a6844d5SKenneth D. Merry softc->flags &= ~(ADA_FLAG_CAN_SUPCAP | 31109a6844d5SKenneth D. Merry ADA_FLAG_CAN_ZONE); 31119a6844d5SKenneth D. Merry softc->valid_iddir_len = 31129a6844d5SKenneth D. Merry ataio->dxfer_len - ataio->resid; 31139a6844d5SKenneth D. Merry if (softc->valid_iddir_len > 0) 31149a6844d5SKenneth D. Merry bcopy(ataio->data_ptr, &softc->ata_iddir, 31159a6844d5SKenneth D. Merry min(softc->valid_iddir_len, 31169a6844d5SKenneth D. Merry sizeof(softc->ata_iddir))); 31179a6844d5SKenneth D. Merry 31189a6844d5SKenneth D. Merry entries_offset = 31199a6844d5SKenneth D. Merry __offsetof(struct ata_identify_log_pages,entries); 31209a6844d5SKenneth D. Merry max_entries = softc->valid_iddir_len - entries_offset; 31219a6844d5SKenneth D. Merry if ((softc->valid_iddir_len > (entries_offset + 1)) 31229a6844d5SKenneth D. Merry && (le64dec(softc->ata_iddir.header) == 31239a6844d5SKenneth D. Merry ATA_IDLOG_REVISION) 31249a6844d5SKenneth D. Merry && (softc->ata_iddir.entry_count > 0)) { 31259a6844d5SKenneth D. Merry int num_entries, i; 31269a6844d5SKenneth D. Merry 31279a6844d5SKenneth D. Merry num_entries = softc->ata_iddir.entry_count; 31289a6844d5SKenneth D. Merry num_entries = min(num_entries, 31299a6844d5SKenneth D. Merry softc->valid_iddir_len - entries_offset); 31309a6844d5SKenneth D. Merry for (i = 0; i < num_entries && 31319a6844d5SKenneth D. Merry i < max_entries; i++) { 31329a6844d5SKenneth D. Merry if (softc->ata_iddir.entries[i] == 31339a6844d5SKenneth D. Merry ATA_IDL_SUP_CAP) 31349a6844d5SKenneth D. Merry softc->flags |= 31359a6844d5SKenneth D. Merry ADA_FLAG_CAN_SUPCAP; 31369a6844d5SKenneth D. Merry else if (softc->ata_iddir.entries[i]== 31379a6844d5SKenneth D. Merry ATA_IDL_ZDI) 31389a6844d5SKenneth D. Merry softc->flags |= 31399a6844d5SKenneth D. Merry ADA_FLAG_CAN_ZONE; 31409a6844d5SKenneth D. Merry 31419a6844d5SKenneth D. Merry if ((softc->flags & 31429a6844d5SKenneth D. Merry ADA_FLAG_CAN_SUPCAP) 31439a6844d5SKenneth D. Merry && (softc->flags & 31449a6844d5SKenneth D. Merry ADA_FLAG_CAN_ZONE)) 31459a6844d5SKenneth D. Merry break; 31469a6844d5SKenneth D. Merry } 31479a6844d5SKenneth D. Merry } 31489a6844d5SKenneth D. Merry } else { 31499a6844d5SKenneth D. Merry error = adaerror(done_ccb, CAM_RETRY_SELTO, 31509a6844d5SKenneth D. Merry SF_RETRY_UA|SF_NO_PRINT); 31519a6844d5SKenneth D. Merry if (error == ERESTART) 31529a6844d5SKenneth D. Merry return; 31539a6844d5SKenneth D. Merry else if (error != 0) { 31549a6844d5SKenneth D. Merry /* 31559a6844d5SKenneth D. Merry * If we can't get the ATA Identify Data log 31569a6844d5SKenneth D. Merry * directory, then it effectively isn't 31579a6844d5SKenneth D. Merry * supported even if the ATA Log directory 31589a6844d5SKenneth D. Merry * a non-zero number of pages present for 31599a6844d5SKenneth D. Merry * this log. 31609a6844d5SKenneth D. Merry */ 31619a6844d5SKenneth D. Merry softc->flags &= ~ADA_FLAG_CAN_IDLOG; 31629a6844d5SKenneth D. Merry if ((done_ccb->ccb_h.status & 31639a6844d5SKenneth D. Merry CAM_DEV_QFRZN) != 0) { 31649a6844d5SKenneth D. Merry /* Don't wedge this device's queue */ 31659a6844d5SKenneth D. Merry cam_release_devq(done_ccb->ccb_h.path, 31669a6844d5SKenneth D. Merry /*relsim_flags*/0, 31679a6844d5SKenneth D. Merry /*reduction*/0, 31689a6844d5SKenneth D. Merry /*timeout*/0, 31699a6844d5SKenneth D. Merry /*getcount_only*/0); 31709a6844d5SKenneth D. Merry } 31719a6844d5SKenneth D. Merry } 31729a6844d5SKenneth D. Merry } 31739a6844d5SKenneth D. Merry 31749a6844d5SKenneth D. Merry free(ataio->data_ptr, M_ATADA); 31759a6844d5SKenneth D. Merry 31769a6844d5SKenneth D. Merry if ((error == 0) 31779a6844d5SKenneth D. Merry && (softc->flags & ADA_FLAG_CAN_SUPCAP)) { 31789a6844d5SKenneth D. Merry softc->state = ADA_STATE_SUP_CAP; 31799a6844d5SKenneth D. Merry xpt_release_ccb(done_ccb); 31809a6844d5SKenneth D. Merry xpt_schedule(periph, priority); 31819a6844d5SKenneth D. Merry } else 31829a6844d5SKenneth D. Merry adaprobedone(periph, done_ccb); 31839a6844d5SKenneth D. Merry return; 31849a6844d5SKenneth D. Merry } 31859a6844d5SKenneth D. Merry case ADA_CCB_SUP_CAP: { 31869a6844d5SKenneth D. Merry int error; 31879a6844d5SKenneth D. Merry 31889a6844d5SKenneth D. Merry if ((ataio->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) { 31899a6844d5SKenneth D. Merry uint32_t valid_len; 31909a6844d5SKenneth D. Merry size_t needed_size; 31919a6844d5SKenneth D. Merry struct ata_identify_log_sup_cap *sup_cap; 31929a6844d5SKenneth D. Merry error = 0; 31939a6844d5SKenneth D. Merry 31949a6844d5SKenneth D. Merry sup_cap = (struct ata_identify_log_sup_cap *) 31959a6844d5SKenneth D. Merry ataio->data_ptr; 31969a6844d5SKenneth D. Merry valid_len = ataio->dxfer_len - ataio->resid; 31979a6844d5SKenneth D. Merry needed_size = 31989a6844d5SKenneth D. Merry __offsetof(struct ata_identify_log_sup_cap, 31999a6844d5SKenneth D. Merry sup_zac_cap) + 1 + sizeof(sup_cap->sup_zac_cap); 32009a6844d5SKenneth D. Merry if (valid_len >= needed_size) { 32019a6844d5SKenneth D. Merry uint64_t zoned, zac_cap; 32029a6844d5SKenneth D. Merry 32039a6844d5SKenneth D. Merry zoned = le64dec(sup_cap->zoned_cap); 32049a6844d5SKenneth D. Merry if (zoned & ATA_ZONED_VALID) { 32059a6844d5SKenneth D. Merry /* 32069a6844d5SKenneth D. Merry * This should have already been 32079a6844d5SKenneth D. Merry * set, because this is also in the 32089a6844d5SKenneth D. Merry * ATA identify data. 32099a6844d5SKenneth D. Merry */ 32109a6844d5SKenneth D. Merry if ((zoned & ATA_ZONED_MASK) == 32119a6844d5SKenneth D. Merry ATA_SUPPORT_ZONE_HOST_AWARE) 32129a6844d5SKenneth D. Merry softc->zone_mode = 32139a6844d5SKenneth D. Merry ADA_ZONE_HOST_AWARE; 32149a6844d5SKenneth D. Merry else if ((zoned & ATA_ZONED_MASK) == 32159a6844d5SKenneth D. Merry ATA_SUPPORT_ZONE_DEV_MANAGED) 32169a6844d5SKenneth D. Merry softc->zone_mode = 32179a6844d5SKenneth D. Merry ADA_ZONE_DRIVE_MANAGED; 32189a6844d5SKenneth D. Merry } 32199a6844d5SKenneth D. Merry 32209a6844d5SKenneth D. Merry zac_cap = le64dec(sup_cap->sup_zac_cap); 32219a6844d5SKenneth D. Merry if (zac_cap & ATA_SUP_ZAC_CAP_VALID) { 32229a6844d5SKenneth D. Merry if (zac_cap & ATA_REPORT_ZONES_SUP) 32239a6844d5SKenneth D. Merry softc->zone_flags |= 32249a6844d5SKenneth D. Merry ADA_ZONE_FLAG_RZ_SUP; 32259a6844d5SKenneth D. Merry if (zac_cap & ATA_ND_OPEN_ZONE_SUP) 32269a6844d5SKenneth D. Merry softc->zone_flags |= 32279a6844d5SKenneth D. Merry ADA_ZONE_FLAG_OPEN_SUP; 32289a6844d5SKenneth D. Merry if (zac_cap & ATA_ND_CLOSE_ZONE_SUP) 32299a6844d5SKenneth D. Merry softc->zone_flags |= 32309a6844d5SKenneth D. Merry ADA_ZONE_FLAG_CLOSE_SUP; 32319a6844d5SKenneth D. Merry if (zac_cap & ATA_ND_FINISH_ZONE_SUP) 32329a6844d5SKenneth D. Merry softc->zone_flags |= 32339a6844d5SKenneth D. Merry ADA_ZONE_FLAG_FINISH_SUP; 32349a6844d5SKenneth D. Merry if (zac_cap & ATA_ND_RWP_SUP) 32359a6844d5SKenneth D. Merry softc->zone_flags |= 32369a6844d5SKenneth D. Merry ADA_ZONE_FLAG_RWP_SUP; 32379a6844d5SKenneth D. Merry } else { 32389a6844d5SKenneth D. Merry /* 32399a6844d5SKenneth D. Merry * This field was introduced in 32409a6844d5SKenneth D. Merry * ACS-4, r08 on April 28th, 2015. 32419a6844d5SKenneth D. Merry * If the drive firmware was written 32429a6844d5SKenneth D. Merry * to an earlier spec, it won't have 32439a6844d5SKenneth D. Merry * the field. So, assume all 32449a6844d5SKenneth D. Merry * commands are supported. 32459a6844d5SKenneth D. Merry */ 32469a6844d5SKenneth D. Merry softc->zone_flags |= 32479a6844d5SKenneth D. Merry ADA_ZONE_FLAG_SUP_MASK; 32489a6844d5SKenneth D. Merry } 32499a6844d5SKenneth D. Merry } 32509a6844d5SKenneth D. Merry } else { 32519a6844d5SKenneth D. Merry error = adaerror(done_ccb, CAM_RETRY_SELTO, 32529a6844d5SKenneth D. Merry SF_RETRY_UA|SF_NO_PRINT); 32539a6844d5SKenneth D. Merry if (error == ERESTART) 32549a6844d5SKenneth D. Merry return; 32559a6844d5SKenneth D. Merry else if (error != 0) { 32569a6844d5SKenneth D. Merry /* 32579a6844d5SKenneth D. Merry * If we can't get the ATA Identify Data 32589a6844d5SKenneth D. Merry * Supported Capabilities page, clear the 32599a6844d5SKenneth D. Merry * flag... 32609a6844d5SKenneth D. Merry */ 32619a6844d5SKenneth D. Merry softc->flags &= ~ADA_FLAG_CAN_SUPCAP; 32629a6844d5SKenneth D. Merry /* 32639a6844d5SKenneth D. Merry * And clear zone capabilities. 32649a6844d5SKenneth D. Merry */ 32659a6844d5SKenneth D. Merry softc->zone_flags &= ~ADA_ZONE_FLAG_SUP_MASK; 32669a6844d5SKenneth D. Merry if ((done_ccb->ccb_h.status & 32679a6844d5SKenneth D. Merry CAM_DEV_QFRZN) != 0) { 32689a6844d5SKenneth D. Merry /* Don't wedge this device's queue */ 32699a6844d5SKenneth D. Merry cam_release_devq(done_ccb->ccb_h.path, 32709a6844d5SKenneth D. Merry /*relsim_flags*/0, 32719a6844d5SKenneth D. Merry /*reduction*/0, 32729a6844d5SKenneth D. Merry /*timeout*/0, 32739a6844d5SKenneth D. Merry /*getcount_only*/0); 32749a6844d5SKenneth D. Merry } 32759a6844d5SKenneth D. Merry } 32769a6844d5SKenneth D. Merry } 32779a6844d5SKenneth D. Merry 32789a6844d5SKenneth D. Merry free(ataio->data_ptr, M_ATADA); 32799a6844d5SKenneth D. Merry 32809a6844d5SKenneth D. Merry if ((error == 0) 32819a6844d5SKenneth D. Merry && (softc->flags & ADA_FLAG_CAN_ZONE)) { 32829a6844d5SKenneth D. Merry softc->state = ADA_STATE_ZONE; 32839a6844d5SKenneth D. Merry xpt_release_ccb(done_ccb); 32849a6844d5SKenneth D. Merry xpt_schedule(periph, priority); 32859a6844d5SKenneth D. Merry } else 32869a6844d5SKenneth D. Merry adaprobedone(periph, done_ccb); 32879a6844d5SKenneth D. Merry return; 32889a6844d5SKenneth D. Merry } 32899a6844d5SKenneth D. Merry case ADA_CCB_ZONE: { 32909a6844d5SKenneth D. Merry int error; 32919a6844d5SKenneth D. Merry 32929a6844d5SKenneth D. Merry if ((ataio->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) { 32939a6844d5SKenneth D. Merry struct ata_zoned_info_log *zi_log; 32949a6844d5SKenneth D. Merry uint32_t valid_len; 32959a6844d5SKenneth D. Merry size_t needed_size; 32969a6844d5SKenneth D. Merry 32979a6844d5SKenneth D. Merry zi_log = (struct ata_zoned_info_log *)ataio->data_ptr; 32989a6844d5SKenneth D. Merry 32999a6844d5SKenneth D. Merry valid_len = ataio->dxfer_len - ataio->resid; 33009a6844d5SKenneth D. Merry needed_size = __offsetof(struct ata_zoned_info_log, 33019a6844d5SKenneth D. Merry version_info) + 1 + sizeof(zi_log->version_info); 33029a6844d5SKenneth D. Merry if (valid_len >= needed_size) { 33039a6844d5SKenneth D. Merry uint64_t tmpvar; 33049a6844d5SKenneth D. Merry 33059a6844d5SKenneth D. Merry tmpvar = le64dec(zi_log->zoned_cap); 33069a6844d5SKenneth D. Merry if (tmpvar & ATA_ZDI_CAP_VALID) { 33079a6844d5SKenneth D. Merry if (tmpvar & ATA_ZDI_CAP_URSWRZ) 33089a6844d5SKenneth D. Merry softc->zone_flags |= 33099a6844d5SKenneth D. Merry ADA_ZONE_FLAG_URSWRZ; 33109a6844d5SKenneth D. Merry else 33119a6844d5SKenneth D. Merry softc->zone_flags &= 33129a6844d5SKenneth D. Merry ~ADA_ZONE_FLAG_URSWRZ; 33139a6844d5SKenneth D. Merry } 33149a6844d5SKenneth D. Merry tmpvar = le64dec(zi_log->optimal_seq_zones); 33159a6844d5SKenneth D. Merry if (tmpvar & ATA_ZDI_OPT_SEQ_VALID) { 33169a6844d5SKenneth D. Merry softc->zone_flags |= 33179a6844d5SKenneth D. Merry ADA_ZONE_FLAG_OPT_SEQ_SET; 33189a6844d5SKenneth D. Merry softc->optimal_seq_zones = (tmpvar & 33199a6844d5SKenneth D. Merry ATA_ZDI_OPT_SEQ_MASK); 33209a6844d5SKenneth D. Merry } else { 33219a6844d5SKenneth D. Merry softc->zone_flags &= 33229a6844d5SKenneth D. Merry ~ADA_ZONE_FLAG_OPT_SEQ_SET; 33239a6844d5SKenneth D. Merry softc->optimal_seq_zones = 0; 33249a6844d5SKenneth D. Merry } 33259a6844d5SKenneth D. Merry 33269a6844d5SKenneth D. Merry tmpvar =le64dec(zi_log->optimal_nonseq_zones); 33279a6844d5SKenneth D. Merry if (tmpvar & ATA_ZDI_OPT_NS_VALID) { 33289a6844d5SKenneth D. Merry softc->zone_flags |= 33299a6844d5SKenneth D. Merry ADA_ZONE_FLAG_OPT_NONSEQ_SET; 33309a6844d5SKenneth D. Merry softc->optimal_nonseq_zones = 33319a6844d5SKenneth D. Merry (tmpvar & ATA_ZDI_OPT_NS_MASK); 33329a6844d5SKenneth D. Merry } else { 33339a6844d5SKenneth D. Merry softc->zone_flags &= 33349a6844d5SKenneth D. Merry ~ADA_ZONE_FLAG_OPT_NONSEQ_SET; 33359a6844d5SKenneth D. Merry softc->optimal_nonseq_zones = 0; 33369a6844d5SKenneth D. Merry } 33379a6844d5SKenneth D. Merry 33389a6844d5SKenneth D. Merry tmpvar = le64dec(zi_log->max_seq_req_zones); 33399a6844d5SKenneth D. Merry if (tmpvar & ATA_ZDI_MAX_SEQ_VALID) { 33409a6844d5SKenneth D. Merry softc->zone_flags |= 33419a6844d5SKenneth D. Merry ADA_ZONE_FLAG_MAX_SEQ_SET; 33429a6844d5SKenneth D. Merry softc->max_seq_zones = 33439a6844d5SKenneth D. Merry (tmpvar & ATA_ZDI_MAX_SEQ_MASK); 33449a6844d5SKenneth D. Merry } else { 33459a6844d5SKenneth D. Merry softc->zone_flags &= 33469a6844d5SKenneth D. Merry ~ADA_ZONE_FLAG_MAX_SEQ_SET; 33479a6844d5SKenneth D. Merry softc->max_seq_zones = 0; 33489a6844d5SKenneth D. Merry } 33499a6844d5SKenneth D. Merry } 33509a6844d5SKenneth D. Merry } else { 33519a6844d5SKenneth D. Merry error = adaerror(done_ccb, CAM_RETRY_SELTO, 33529a6844d5SKenneth D. Merry SF_RETRY_UA|SF_NO_PRINT); 33539a6844d5SKenneth D. Merry if (error == ERESTART) 33549a6844d5SKenneth D. Merry return; 33559a6844d5SKenneth D. Merry else if (error != 0) { 33569a6844d5SKenneth D. Merry softc->flags &= ~ADA_FLAG_CAN_ZONE; 33579a6844d5SKenneth D. Merry softc->flags &= ~ADA_ZONE_FLAG_SET_MASK; 33589a6844d5SKenneth D. Merry 33599a6844d5SKenneth D. Merry if ((done_ccb->ccb_h.status & 33609a6844d5SKenneth D. Merry CAM_DEV_QFRZN) != 0) { 33619a6844d5SKenneth D. Merry /* Don't wedge this device's queue */ 33629a6844d5SKenneth D. Merry cam_release_devq(done_ccb->ccb_h.path, 33639a6844d5SKenneth D. Merry /*relsim_flags*/0, 33649a6844d5SKenneth D. Merry /*reduction*/0, 33659a6844d5SKenneth D. Merry /*timeout*/0, 33669a6844d5SKenneth D. Merry /*getcount_only*/0); 33679a6844d5SKenneth D. Merry } 33689a6844d5SKenneth D. Merry } 33699a6844d5SKenneth D. Merry } 33709a6844d5SKenneth D. Merry free(ataio->data_ptr, M_ATADA); 33719a6844d5SKenneth D. Merry 33729a6844d5SKenneth D. Merry adaprobedone(periph, done_ccb); 3373f513d14cSAlexander Motin return; 3374f513d14cSAlexander Motin } 337552c9ce25SScott Long case ADA_CCB_DUMP: 337652c9ce25SScott Long /* No-op. We're polling */ 337752c9ce25SScott Long return; 337852c9ce25SScott Long default: 337952c9ce25SScott Long break; 338052c9ce25SScott Long } 338152c9ce25SScott Long xpt_release_ccb(done_ccb); 338252c9ce25SScott Long } 338352c9ce25SScott Long 338452c9ce25SScott Long static int 338552c9ce25SScott Long adaerror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags) 338652c9ce25SScott Long { 3387acfc9b68SWarner Losh #ifdef CAM_IO_STATS 3388a6e0c5daSWarner Losh struct ada_softc *softc; 3389a6e0c5daSWarner Losh struct cam_periph *periph; 3390a6e0c5daSWarner Losh 3391a6e0c5daSWarner Losh periph = xpt_path_periph(ccb->ccb_h.path); 3392a6e0c5daSWarner Losh softc = (struct ada_softc *)periph->softc; 3393a6e0c5daSWarner Losh 3394a6e0c5daSWarner Losh switch (ccb->ccb_h.status & CAM_STATUS_MASK) { 3395a6e0c5daSWarner Losh case CAM_CMD_TIMEOUT: 3396a6e0c5daSWarner Losh softc->timeouts++; 3397a6e0c5daSWarner Losh break; 3398a6e0c5daSWarner Losh case CAM_REQ_ABORTED: 3399a6e0c5daSWarner Losh case CAM_REQ_CMP_ERR: 3400a6e0c5daSWarner Losh case CAM_REQ_TERMIO: 3401a6e0c5daSWarner Losh case CAM_UNREC_HBA_ERROR: 3402a6e0c5daSWarner Losh case CAM_DATA_RUN_ERR: 3403a6e0c5daSWarner Losh case CAM_ATA_STATUS_ERROR: 3404a6e0c5daSWarner Losh softc->errors++; 3405a6e0c5daSWarner Losh break; 3406a6e0c5daSWarner Losh default: 3407a6e0c5daSWarner Losh break; 3408a6e0c5daSWarner Losh } 3409acfc9b68SWarner Losh #endif 341052c9ce25SScott Long 3411553484aeSWarner Losh return(cam_periph_error(ccb, cam_flags, sense_flags)); 341252c9ce25SScott Long } 341352c9ce25SScott Long 341452c9ce25SScott Long static void 341576d843daSAlexander Motin adasetgeom(struct ada_softc *softc, struct ccb_getdev *cgd) 341652c9ce25SScott Long { 341752c9ce25SScott Long struct disk_params *dp = &softc->params; 341852c9ce25SScott Long u_int64_t lbasize48; 341952c9ce25SScott Long u_int32_t lbasize; 342076d843daSAlexander Motin u_int maxio, d_flags; 3421f350bc1dSJessica Clarke size_t tmpsize; 342252c9ce25SScott Long 3423c1bd46c2SAlexander Motin dp->secsize = ata_logical_sector_size(&cgd->ident_data); 342452c9ce25SScott Long if ((cgd->ident_data.atavalid & ATA_FLAG_54_58) && 342576d843daSAlexander Motin cgd->ident_data.current_heads != 0 && 342676d843daSAlexander Motin cgd->ident_data.current_sectors != 0) { 342752c9ce25SScott Long dp->heads = cgd->ident_data.current_heads; 342852c9ce25SScott Long dp->secs_per_track = cgd->ident_data.current_sectors; 342952c9ce25SScott Long dp->cylinders = cgd->ident_data.cylinders; 343052c9ce25SScott Long dp->sectors = (u_int32_t)cgd->ident_data.current_size_1 | 343152c9ce25SScott Long ((u_int32_t)cgd->ident_data.current_size_2 << 16); 343252c9ce25SScott Long } else { 343352c9ce25SScott Long dp->heads = cgd->ident_data.heads; 343452c9ce25SScott Long dp->secs_per_track = cgd->ident_data.sectors; 343552c9ce25SScott Long dp->cylinders = cgd->ident_data.cylinders; 343699ae1d3bSAlan Somers dp->sectors = cgd->ident_data.cylinders * 343799ae1d3bSAlan Somers (u_int32_t)(dp->heads * dp->secs_per_track); 343852c9ce25SScott Long } 343952c9ce25SScott Long lbasize = (u_int32_t)cgd->ident_data.lba_size_1 | 344052c9ce25SScott Long ((u_int32_t)cgd->ident_data.lba_size_2 << 16); 344152c9ce25SScott Long 344252c9ce25SScott Long /* use the 28bit LBA size if valid or bigger than the CHS mapping */ 344352c9ce25SScott Long if (cgd->ident_data.cylinders == 16383 || dp->sectors < lbasize) 344452c9ce25SScott Long dp->sectors = lbasize; 344552c9ce25SScott Long 344652c9ce25SScott Long /* use the 48bit LBA size if valid */ 344752c9ce25SScott Long lbasize48 = ((u_int64_t)cgd->ident_data.lba_size48_1) | 344852c9ce25SScott Long ((u_int64_t)cgd->ident_data.lba_size48_2 << 16) | 344952c9ce25SScott Long ((u_int64_t)cgd->ident_data.lba_size48_3 << 32) | 345052c9ce25SScott Long ((u_int64_t)cgd->ident_data.lba_size48_4 << 48); 345152c9ce25SScott Long if ((cgd->ident_data.support.command2 & ATA_SUPPORT_ADDRESS48) && 345252c9ce25SScott Long lbasize48 > ATA_MAX_28BIT_LBA) 345352c9ce25SScott Long dp->sectors = lbasize48; 345476d843daSAlexander Motin 345576d843daSAlexander Motin maxio = softc->cpi.maxio; /* Honor max I/O size of SIM */ 345676d843daSAlexander Motin if (maxio == 0) 345776d843daSAlexander Motin maxio = DFLTPHYS; /* traditional default */ 3458cd853791SKonstantin Belousov else if (maxio > maxphys) 3459cd853791SKonstantin Belousov maxio = maxphys; /* for safety */ 346076d843daSAlexander Motin if (softc->flags & ADA_FLAG_CAN_48BIT) 346176d843daSAlexander Motin maxio = min(maxio, 65536 * softc->params.secsize); 346276d843daSAlexander Motin else /* 28bit ATA command limit */ 346376d843daSAlexander Motin maxio = min(maxio, 256 * softc->params.secsize); 346476d843daSAlexander Motin if (softc->quirks & ADA_Q_128KB) 346576d843daSAlexander Motin maxio = min(maxio, 128 * 1024); 346676d843daSAlexander Motin softc->disk->d_maxsize = maxio; 346776d843daSAlexander Motin d_flags = DISKFLAG_DIRECT_COMPLETION | DISKFLAG_CANZONE; 346876d843daSAlexander Motin if (softc->flags & ADA_FLAG_CAN_FLUSHCACHE) 346976d843daSAlexander Motin d_flags |= DISKFLAG_CANFLUSHCACHE; 347076d843daSAlexander Motin if (softc->flags & ADA_FLAG_CAN_TRIM) { 347176d843daSAlexander Motin d_flags |= DISKFLAG_CANDELETE; 347276d843daSAlexander Motin softc->disk->d_delmaxsize = softc->params.secsize * 347376d843daSAlexander Motin ATA_DSM_RANGE_MAX * softc->trim_max_ranges; 347476d843daSAlexander Motin } else if ((softc->flags & ADA_FLAG_CAN_CFA) && 347576d843daSAlexander Motin !(softc->flags & ADA_FLAG_CAN_48BIT)) { 347676d843daSAlexander Motin d_flags |= DISKFLAG_CANDELETE; 347776d843daSAlexander Motin softc->disk->d_delmaxsize = 256 * softc->params.secsize; 347876d843daSAlexander Motin } else 347976d843daSAlexander Motin softc->disk->d_delmaxsize = maxio; 348076d843daSAlexander Motin if ((softc->cpi.hba_misc & PIM_UNMAPPED) != 0) { 348176d843daSAlexander Motin d_flags |= DISKFLAG_UNMAPPED_BIO; 3482cf3ff63eSWarner Losh softc->flags |= ADA_FLAG_UNMAPPEDIO; 348376d843daSAlexander Motin } 348476d843daSAlexander Motin softc->disk->d_flags = d_flags; 3485f350bc1dSJessica Clarke 3486f350bc1dSJessica Clarke /* 3487f350bc1dSJessica Clarke * ata_param_fixup will strip trailing padding spaces and add a NUL, 3488f350bc1dSJessica Clarke * but if the field has no padding (as is common for serial numbers) 3489f350bc1dSJessica Clarke * there will still be no NUL terminator. We cannot use strlcpy, since 3490f350bc1dSJessica Clarke * it keeps reading src until it finds a NUL in order to compute the 3491f350bc1dSJessica Clarke * return value (and will truncate the final character due to having a 3492f350bc1dSJessica Clarke * single dsize rather than separate ssize and dsize), and strncpy does 3493f350bc1dSJessica Clarke * not add a NUL to the destination if it reaches the character limit. 3494f350bc1dSJessica Clarke */ 3495f350bc1dSJessica Clarke tmpsize = MIN(sizeof(softc->disk->d_descr) - 1, 3496f350bc1dSJessica Clarke sizeof(cgd->ident_data.model)); 3497f350bc1dSJessica Clarke memcpy(softc->disk->d_descr, cgd->ident_data.model, tmpsize); 3498f350bc1dSJessica Clarke softc->disk->d_descr[tmpsize] = '\0'; 3499f350bc1dSJessica Clarke 3500f350bc1dSJessica Clarke tmpsize = MIN(sizeof(softc->disk->d_ident) - 1, 3501f350bc1dSJessica Clarke sizeof(cgd->ident_data.serial)); 3502f350bc1dSJessica Clarke memcpy(softc->disk->d_ident, cgd->ident_data.serial, tmpsize); 3503f350bc1dSJessica Clarke softc->disk->d_ident[tmpsize] = '\0'; 350476d843daSAlexander Motin 350576d843daSAlexander Motin softc->disk->d_sectorsize = softc->params.secsize; 350676d843daSAlexander Motin softc->disk->d_mediasize = (off_t)softc->params.sectors * 350776d843daSAlexander Motin softc->params.secsize; 350876d843daSAlexander Motin if (ata_physical_sector_size(&cgd->ident_data) != 350976d843daSAlexander Motin softc->params.secsize) { 351076d843daSAlexander Motin softc->disk->d_stripesize = 351176d843daSAlexander Motin ata_physical_sector_size(&cgd->ident_data); 351276d843daSAlexander Motin softc->disk->d_stripeoffset = (softc->disk->d_stripesize - 351376d843daSAlexander Motin ata_logical_sector_offset(&cgd->ident_data)) % 351476d843daSAlexander Motin softc->disk->d_stripesize; 351576d843daSAlexander Motin } else if (softc->quirks & ADA_Q_4K) { 351676d843daSAlexander Motin softc->disk->d_stripesize = 4096; 351776d843daSAlexander Motin softc->disk->d_stripeoffset = 0; 351876d843daSAlexander Motin } 351976d843daSAlexander Motin softc->disk->d_fwsectors = softc->params.secs_per_track; 352076d843daSAlexander Motin softc->disk->d_fwheads = softc->params.heads; 352176d843daSAlexander Motin softc->disk->d_rotation_rate = cgd->ident_data.media_rotation_rate; 3522b5961be1SEdward Tomasz Napierala snprintf(softc->disk->d_attachment, sizeof(softc->disk->d_attachment), 3523b5961be1SEdward Tomasz Napierala "%s%d", softc->cpi.dev_name, softc->cpi.unit_number); 352452c9ce25SScott Long } 352552c9ce25SScott Long 352652c9ce25SScott Long static void 352752c9ce25SScott Long adasendorderedtag(void *arg) 352852c9ce25SScott Long { 352952c9ce25SScott Long struct ada_softc *softc = arg; 353052c9ce25SScott Long 353152c9ce25SScott Long if (ada_send_ordered) { 3532030844d1SAlexander Motin if (softc->outstanding_cmds > 0) { 3533030844d1SAlexander Motin if ((softc->flags & ADA_FLAG_WAS_OTAG) == 0) 353452c9ce25SScott Long softc->flags |= ADA_FLAG_NEED_OTAG; 3535030844d1SAlexander Motin softc->flags &= ~ADA_FLAG_WAS_OTAG; 353652c9ce25SScott Long } 353752c9ce25SScott Long } 35380e5c50bfSAlexander Motin 353952c9ce25SScott Long /* Queue us up again */ 35400e5c50bfSAlexander Motin callout_schedule_sbt(&softc->sendordered_c, 35410e5c50bfSAlexander Motin SBT_1S / ADA_ORDEREDTAG_INTERVAL * ada_default_timeout, 0, 35420e5c50bfSAlexander Motin C_PREL(1)); 354352c9ce25SScott Long } 354452c9ce25SScott Long 354552c9ce25SScott Long /* 354652c9ce25SScott Long * Step through all ADA peripheral drivers, and if the device is still open, 354752c9ce25SScott Long * sync the disk cache to physical media. 354852c9ce25SScott Long */ 354952c9ce25SScott Long static void 3550c3d0d168SAlexander Motin adaflush(void) 355152c9ce25SScott Long { 355252c9ce25SScott Long struct cam_periph *periph; 355352c9ce25SScott Long struct ada_softc *softc; 355409cfadbeSAlexander Motin union ccb *ccb; 35550191d9b3SAlexander Motin int error; 355652c9ce25SScott Long 3557f371c9e2SAlexander Motin CAM_PERIPH_FOREACH(periph, &adadriver) { 355852c9ce25SScott Long softc = (struct ada_softc *)periph->softc; 35592f87dfb0SAlexander Motin if (SCHEDULER_STOPPED()) { 356049dace1dSGordon Bergling /* If we panicked with the lock held, do not recurse. */ 35612f87dfb0SAlexander Motin if (!cam_periph_owned(periph) && 35622f87dfb0SAlexander Motin (softc->flags & ADA_FLAG_OPEN)) { 3563489ba222SMitchell Horne adadump(softc->disk, NULL, 0, 0); 35642f87dfb0SAlexander Motin } 35652f87dfb0SAlexander Motin continue; 35662f87dfb0SAlexander Motin } 35672f87dfb0SAlexander Motin cam_periph_lock(periph); 356852c9ce25SScott Long /* 356952c9ce25SScott Long * We only sync the cache if the drive is still open, and 357052c9ce25SScott Long * if the drive is capable of it.. 357152c9ce25SScott Long */ 357252c9ce25SScott Long if (((softc->flags & ADA_FLAG_OPEN) == 0) || 357352c9ce25SScott Long (softc->flags & ADA_FLAG_CAN_FLUSHCACHE) == 0) { 357452c9ce25SScott Long cam_periph_unlock(periph); 357552c9ce25SScott Long continue; 357652c9ce25SScott Long } 357752c9ce25SScott Long 357809cfadbeSAlexander Motin ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL); 357909cfadbeSAlexander Motin cam_fill_ataio(&ccb->ataio, 35800191d9b3SAlexander Motin 0, 3581eed99e75SScott Long NULL, 358252c9ce25SScott Long CAM_DIR_NONE, 358352c9ce25SScott Long 0, 358452c9ce25SScott Long NULL, 358552c9ce25SScott Long 0, 358652c9ce25SScott Long ada_default_timeout*1000); 358752c9ce25SScott Long if (softc->flags & ADA_FLAG_CAN_48BIT) 358809cfadbeSAlexander Motin ata_48bit_cmd(&ccb->ataio, ATA_FLUSHCACHE48, 0, 0, 0); 358952c9ce25SScott Long else 359009cfadbeSAlexander Motin ata_28bit_cmd(&ccb->ataio, ATA_FLUSHCACHE, 0, 0, 0); 359152c9ce25SScott Long 359209cfadbeSAlexander Motin error = cam_periph_runccb(ccb, adaerror, /*cam_flags*/0, 359309cfadbeSAlexander Motin /*sense_flags*/ SF_NO_RECOVERY | SF_NO_RETRY, 359409cfadbeSAlexander Motin softc->disk->d_devstat); 35950191d9b3SAlexander Motin if (error != 0) 35960191d9b3SAlexander Motin xpt_print(periph->path, "Synchronize cache failed\n"); 3597d6794b70SAlexander Motin xpt_release_ccb(ccb); 359852c9ce25SScott Long cam_periph_unlock(periph); 359952c9ce25SScott Long } 3600c3d0d168SAlexander Motin } 3601fd104c15SRebecca Cran 3602c3d0d168SAlexander Motin static void 3603c3d0d168SAlexander Motin adaspindown(uint8_t cmd, int flags) 3604c3d0d168SAlexander Motin { 3605c3d0d168SAlexander Motin struct cam_periph *periph; 3606c3d0d168SAlexander Motin struct ada_softc *softc; 3607f902366cSWarner Losh struct ccb_ataio local_ccb; 36080191d9b3SAlexander Motin int error; 360915910dc0SAndriy Gapon int mode; 3610fd104c15SRebecca Cran 3611f371c9e2SAlexander Motin CAM_PERIPH_FOREACH(periph, &adadriver) { 361249dace1dSGordon Bergling /* If we panicked with lock held - not recurse here. */ 3613fd104c15SRebecca Cran if (cam_periph_owned(periph)) 3614fd104c15SRebecca Cran continue; 3615fd104c15SRebecca Cran cam_periph_lock(periph); 3616fd104c15SRebecca Cran softc = (struct ada_softc *)periph->softc; 3617fd104c15SRebecca Cran /* 3618fd104c15SRebecca Cran * We only spin-down the drive if it is capable of it.. 3619fd104c15SRebecca Cran */ 3620fd104c15SRebecca Cran if ((softc->flags & ADA_FLAG_CAN_POWERMGT) == 0) { 3621fd104c15SRebecca Cran cam_periph_unlock(periph); 3622fd104c15SRebecca Cran continue; 3623fd104c15SRebecca Cran } 3624fd104c15SRebecca Cran 362515910dc0SAndriy Gapon /* 362615910dc0SAndriy Gapon * Additionally check if we would spin up the drive instead of 362715910dc0SAndriy Gapon * spinning it down. 362815910dc0SAndriy Gapon */ 362915910dc0SAndriy Gapon if (cmd == ATA_IDLE_IMMEDIATE) { 363015910dc0SAndriy Gapon memset(&local_ccb, 0, sizeof(local_ccb)); 363115910dc0SAndriy Gapon xpt_setup_ccb(&local_ccb.ccb_h, periph->path, 363215910dc0SAndriy Gapon CAM_PRIORITY_NORMAL); 363315910dc0SAndriy Gapon local_ccb.ccb_h.ccb_state = ADA_CCB_DUMP; 363415910dc0SAndriy Gapon 363515910dc0SAndriy Gapon cam_fill_ataio(&local_ccb, 0, NULL, CAM_DIR_NONE, 363615910dc0SAndriy Gapon 0, NULL, 0, ada_default_timeout * 1000); 363715910dc0SAndriy Gapon ata_28bit_cmd(&local_ccb, ATA_CHECK_POWER_MODE, 363815910dc0SAndriy Gapon 0, 0, 0); 363915910dc0SAndriy Gapon local_ccb.cmd.flags |= CAM_ATAIO_NEEDRESULT; 364015910dc0SAndriy Gapon 364115910dc0SAndriy Gapon error = cam_periph_runccb((union ccb *)&local_ccb, 364215910dc0SAndriy Gapon adaerror, /*cam_flags*/0, 364315910dc0SAndriy Gapon /*sense_flags*/ SF_NO_RECOVERY | SF_NO_RETRY, 364415910dc0SAndriy Gapon softc->disk->d_devstat); 364515910dc0SAndriy Gapon if (error != 0) { 364615910dc0SAndriy Gapon xpt_print(periph->path, 364715910dc0SAndriy Gapon "Failed to read current power mode\n"); 364815910dc0SAndriy Gapon } else { 364915910dc0SAndriy Gapon mode = local_ccb.res.sector_count; 365015910dc0SAndriy Gapon #ifdef DIAGNOSTIC 365115910dc0SAndriy Gapon if (bootverbose) { 365215910dc0SAndriy Gapon xpt_print(periph->path, 365315910dc0SAndriy Gapon "disk power mode 0x%02x\n", mode); 365415910dc0SAndriy Gapon } 365515910dc0SAndriy Gapon #endif 365615910dc0SAndriy Gapon switch (mode) { 365775bc7150SAndriy Gapon case ATA_PM_STANDBY: 365875bc7150SAndriy Gapon case ATA_PM_STANDBY_Y: 365915910dc0SAndriy Gapon if (bootverbose) { 366015910dc0SAndriy Gapon xpt_print(periph->path, 366115910dc0SAndriy Gapon "already spun down\n"); 366215910dc0SAndriy Gapon } 366315910dc0SAndriy Gapon cam_periph_unlock(periph); 366415910dc0SAndriy Gapon continue; 366515910dc0SAndriy Gapon default: 366615910dc0SAndriy Gapon break; 366715910dc0SAndriy Gapon } 366815910dc0SAndriy Gapon } 366915910dc0SAndriy Gapon } 367015910dc0SAndriy Gapon 3671fd104c15SRebecca Cran if (bootverbose) 3672fd104c15SRebecca Cran xpt_print(periph->path, "spin-down\n"); 3673fd104c15SRebecca Cran 3674f902366cSWarner Losh memset(&local_ccb, 0, sizeof(local_ccb)); 3675f902366cSWarner Losh xpt_setup_ccb(&local_ccb.ccb_h, periph->path, CAM_PRIORITY_NORMAL); 3676f902366cSWarner Losh local_ccb.ccb_h.ccb_state = ADA_CCB_DUMP; 3677f902366cSWarner Losh 3678f902366cSWarner Losh cam_fill_ataio(&local_ccb, 36790191d9b3SAlexander Motin 0, 3680eed99e75SScott Long NULL, 3681c3d0d168SAlexander Motin CAM_DIR_NONE | flags, 3682fd104c15SRebecca Cran 0, 3683fd104c15SRebecca Cran NULL, 3684fd104c15SRebecca Cran 0, 3685fd104c15SRebecca Cran ada_default_timeout*1000); 3686f902366cSWarner Losh ata_28bit_cmd(&local_ccb, cmd, 0, 0, 0); 3687f902366cSWarner Losh error = cam_periph_runccb((union ccb *)&local_ccb, adaerror, 3688f902366cSWarner Losh /*cam_flags*/0, /*sense_flags*/ SF_NO_RECOVERY | SF_NO_RETRY, 368909cfadbeSAlexander Motin softc->disk->d_devstat); 36900191d9b3SAlexander Motin if (error != 0) 36910191d9b3SAlexander Motin xpt_print(periph->path, "Spin-down disk failed\n"); 3692fd104c15SRebecca Cran cam_periph_unlock(periph); 3693fd104c15SRebecca Cran } 369452c9ce25SScott Long } 369552c9ce25SScott Long 3696c3d0d168SAlexander Motin static void 3697c3d0d168SAlexander Motin adashutdown(void *arg, int howto) 3698c3d0d168SAlexander Motin { 369950ee2e2aSWarner Losh int how; 3700c3d0d168SAlexander Motin 370138f8addaSAlexander Motin if ((howto & RB_NOSYNC) != 0) 370238f8addaSAlexander Motin return; 370338f8addaSAlexander Motin 3704c3d0d168SAlexander Motin adaflush(); 3705f902366cSWarner Losh 3706f902366cSWarner Losh /* 370750ee2e2aSWarner Losh * STANDBY IMMEDIATE saves any volatile data to the drive. It also spins 370850ee2e2aSWarner Losh * down hard drives. IDLE IMMEDIATE also saves the volatile data without 370950ee2e2aSWarner Losh * a spindown. We send the former when we expect to lose power soon. For 371050ee2e2aSWarner Losh * a warm boot, we send the latter to avoid a thundering herd of spinups 371150ee2e2aSWarner Losh * just after the kernel loads while probing. We have to do something to 371250ee2e2aSWarner Losh * flush the data because the BIOS in many systems resets the HBA 371350ee2e2aSWarner Losh * causing a COMINIT/COMRESET negotiation, which some drives interpret 371450ee2e2aSWarner Losh * as license to toss the volatile data, and others count as unclean 371550ee2e2aSWarner Losh * shutdown when in the Active PM state in SMART attributes. 371650ee2e2aSWarner Losh * 371750ee2e2aSWarner Losh * adaspindown will ensure that we don't send this to a drive that 371850ee2e2aSWarner Losh * doesn't support it. 3719f902366cSWarner Losh */ 372050ee2e2aSWarner Losh if (ada_spindown_shutdown != 0) { 372150ee2e2aSWarner Losh how = (howto & (RB_HALT | RB_POWEROFF | RB_POWERCYCLE)) ? 372250ee2e2aSWarner Losh ATA_STANDBY_IMMEDIATE : ATA_IDLE_IMMEDIATE; 372350ee2e2aSWarner Losh adaspindown(how, 0); 372450ee2e2aSWarner Losh } 3725c3d0d168SAlexander Motin } 3726c3d0d168SAlexander Motin 3727c3d0d168SAlexander Motin static void 3728c3d0d168SAlexander Motin adasuspend(void *arg) 3729c3d0d168SAlexander Motin { 3730c3d0d168SAlexander Motin 3731c3d0d168SAlexander Motin adaflush(); 3732f902366cSWarner Losh /* 3733f902366cSWarner Losh * SLEEP also fushes any volatile data, like STANDBY IMEDIATE, 3734f902366cSWarner Losh * so we don't need to send it as well. 3735f902366cSWarner Losh */ 3736c3d0d168SAlexander Motin if (ada_spindown_suspend != 0) 3737c3d0d168SAlexander Motin adaspindown(ATA_SLEEP, CAM_DEV_QFREEZE); 3738c3d0d168SAlexander Motin } 3739c3d0d168SAlexander Motin 3740c3d0d168SAlexander Motin static void 3741c3d0d168SAlexander Motin adaresume(void *arg) 3742c3d0d168SAlexander Motin { 3743c3d0d168SAlexander Motin struct cam_periph *periph; 3744c3d0d168SAlexander Motin struct ada_softc *softc; 3745c3d0d168SAlexander Motin 3746c3d0d168SAlexander Motin if (ada_spindown_suspend == 0) 3747c3d0d168SAlexander Motin return; 3748c3d0d168SAlexander Motin 3749f371c9e2SAlexander Motin CAM_PERIPH_FOREACH(periph, &adadriver) { 3750c3d0d168SAlexander Motin cam_periph_lock(periph); 3751c3d0d168SAlexander Motin softc = (struct ada_softc *)periph->softc; 3752c3d0d168SAlexander Motin /* 3753c3d0d168SAlexander Motin * We only spin-down the drive if it is capable of it.. 3754c3d0d168SAlexander Motin */ 3755c3d0d168SAlexander Motin if ((softc->flags & ADA_FLAG_CAN_POWERMGT) == 0) { 3756c3d0d168SAlexander Motin cam_periph_unlock(periph); 3757c3d0d168SAlexander Motin continue; 3758c3d0d168SAlexander Motin } 3759c3d0d168SAlexander Motin 3760c3d0d168SAlexander Motin if (bootverbose) 3761c3d0d168SAlexander Motin xpt_print(periph->path, "resume\n"); 3762c3d0d168SAlexander Motin 3763c3d0d168SAlexander Motin /* 3764c3d0d168SAlexander Motin * Drop freeze taken due to CAM_DEV_QFREEZE flag set on 3765c3d0d168SAlexander Motin * sleep request. 3766c3d0d168SAlexander Motin */ 3767c3d0d168SAlexander Motin cam_release_devq(periph->path, 3768c3d0d168SAlexander Motin /*relsim_flags*/0, 3769c3d0d168SAlexander Motin /*openings*/0, 3770c3d0d168SAlexander Motin /*timeout*/0, 3771c3d0d168SAlexander Motin /*getcount_only*/0); 3772c3d0d168SAlexander Motin 3773c3d0d168SAlexander Motin cam_periph_unlock(periph); 3774c3d0d168SAlexander Motin } 3775c3d0d168SAlexander Motin } 3776c3d0d168SAlexander Motin 377752c9ce25SScott Long #endif /* _KERNEL */ 3778