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 110252c9ce25SScott Long adadump(void *arg, void *virtual, vm_offset_t physical, 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 /* 124352c9ce25SScott Long * Return all queued I/O with ENXIO. 124452c9ce25SScott Long * XXX Handle any transactions queued to the card 124552c9ce25SScott Long * with XPT_ABORT_CCB. 124652c9ce25SScott Long */ 1247a6e0c5daSWarner Losh cam_iosched_flush(softc->cam_iosched, NULL, ENXIO); 124852c9ce25SScott Long 124952c9ce25SScott Long disk_gone(softc->disk); 125052c9ce25SScott Long } 125152c9ce25SScott Long 125252c9ce25SScott Long static void 125352c9ce25SScott Long adacleanup(struct cam_periph *periph) 125452c9ce25SScott Long { 125552c9ce25SScott Long struct ada_softc *softc; 125652c9ce25SScott Long 125752c9ce25SScott Long softc = (struct ada_softc *)periph->softc; 125852c9ce25SScott Long 125952c9ce25SScott Long cam_periph_unlock(periph); 126052c9ce25SScott Long 1261a6e0c5daSWarner Losh cam_iosched_fini(softc->cam_iosched); 1262a6e0c5daSWarner Losh 126352c9ce25SScott Long /* 126452c9ce25SScott Long * If we can't free the sysctl tree, oh well... 126552c9ce25SScott Long */ 1266a6e0c5daSWarner Losh if ((softc->flags & ADA_FLAG_SCTX_INIT) != 0) { 1267a6e0c5daSWarner Losh #ifdef CAM_IO_STATS 1268a6e0c5daSWarner Losh if (sysctl_ctx_free(&softc->sysctl_stats_ctx) != 0) 1269a6e0c5daSWarner Losh xpt_print(periph->path, 1270a6e0c5daSWarner Losh "can't remove sysctl stats context\n"); 1271a6e0c5daSWarner Losh #endif 1272a6e0c5daSWarner Losh if (sysctl_ctx_free(&softc->sysctl_ctx) != 0) 1273a6e0c5daSWarner Losh xpt_print(periph->path, 1274a6e0c5daSWarner Losh "can't remove sysctl context\n"); 127552c9ce25SScott Long } 127652c9ce25SScott Long 127752c9ce25SScott Long disk_destroy(softc->disk); 127852c9ce25SScott Long callout_drain(&softc->sendordered_c); 127952c9ce25SScott Long free(softc, M_DEVBUF); 128052c9ce25SScott Long cam_periph_lock(periph); 128152c9ce25SScott Long } 128252c9ce25SScott Long 128352c9ce25SScott Long static void 1284a6e0c5daSWarner Losh adasetdeletemethod(struct ada_softc *softc) 1285a6e0c5daSWarner Losh { 1286a6e0c5daSWarner Losh 1287a6e0c5daSWarner Losh if (softc->flags & ADA_FLAG_CAN_NCQ_TRIM) 1288a6e0c5daSWarner Losh softc->delete_method = ADA_DELETE_NCQ_DSM_TRIM; 1289a6e0c5daSWarner Losh else if (softc->flags & ADA_FLAG_CAN_TRIM) 1290a6e0c5daSWarner Losh softc->delete_method = ADA_DELETE_DSM_TRIM; 1291a6e0c5daSWarner Losh else if ((softc->flags & ADA_FLAG_CAN_CFA) && !(softc->flags & ADA_FLAG_CAN_48BIT)) 1292a6e0c5daSWarner Losh softc->delete_method = ADA_DELETE_CFA_ERASE; 1293a6e0c5daSWarner Losh else 1294a6e0c5daSWarner Losh softc->delete_method = ADA_DELETE_NONE; 1295a6e0c5daSWarner Losh } 1296a6e0c5daSWarner Losh 1297a6e0c5daSWarner Losh static void 129852c9ce25SScott Long adaasync(void *callback_arg, u_int32_t code, 129952c9ce25SScott Long struct cam_path *path, void *arg) 130052c9ce25SScott Long { 1301581b2e78SAlexander Motin struct ccb_getdev cgd; 130252c9ce25SScott Long struct cam_periph *periph; 1303f513d14cSAlexander Motin struct ada_softc *softc; 130452c9ce25SScott Long 130552c9ce25SScott Long periph = (struct cam_periph *)callback_arg; 130652c9ce25SScott Long switch (code) { 130752c9ce25SScott Long case AC_FOUND_DEVICE: 130852c9ce25SScott Long { 130952c9ce25SScott Long struct ccb_getdev *cgd; 131052c9ce25SScott Long cam_status status; 131152c9ce25SScott Long 131252c9ce25SScott Long cgd = (struct ccb_getdev *)arg; 131352c9ce25SScott Long if (cgd == NULL) 131452c9ce25SScott Long break; 131552c9ce25SScott Long 131652c9ce25SScott Long if (cgd->protocol != PROTO_ATA) 131752c9ce25SScott Long break; 131852c9ce25SScott Long 131952c9ce25SScott Long /* 132052c9ce25SScott Long * Allocate a peripheral instance for 132152c9ce25SScott Long * this device and start the probe 132252c9ce25SScott Long * process. 132352c9ce25SScott Long */ 132452c9ce25SScott Long status = cam_periph_alloc(adaregister, adaoninvalidate, 132552c9ce25SScott Long adacleanup, adastart, 132652c9ce25SScott Long "ada", CAM_PERIPH_BIO, 1327227d67aaSAlexander Motin path, adaasync, 132852c9ce25SScott Long AC_FOUND_DEVICE, cgd); 132952c9ce25SScott Long 133052c9ce25SScott Long if (status != CAM_REQ_CMP 133152c9ce25SScott Long && status != CAM_REQ_INPROG) 133252c9ce25SScott Long printf("adaasync: Unable to attach to new device " 133352c9ce25SScott Long "due to status 0x%x\n", status); 133452c9ce25SScott Long break; 133552c9ce25SScott Long } 1336581b2e78SAlexander Motin case AC_GETDEV_CHANGED: 1337581b2e78SAlexander Motin { 1338581b2e78SAlexander Motin softc = (struct ada_softc *)periph->softc; 1339ec5325dbSEdward Tomasz Napierala memset(&cgd, 0, sizeof(cgd)); 1340581b2e78SAlexander Motin xpt_setup_ccb(&cgd.ccb_h, periph->path, CAM_PRIORITY_NORMAL); 1341581b2e78SAlexander Motin cgd.ccb_h.func_code = XPT_GDEV_TYPE; 1342581b2e78SAlexander Motin xpt_action((union ccb *)&cgd); 1343581b2e78SAlexander Motin 1344a6e0c5daSWarner Losh /* 134576d843daSAlexander Motin * Update our information based on the new Identify data. 1346a6e0c5daSWarner Losh */ 13479a6844d5SKenneth D. Merry adasetflags(softc, &cgd); 134876d843daSAlexander Motin adasetgeom(softc, &cgd); 134976d843daSAlexander Motin disk_resize(softc->disk, M_NOWAIT); 1350581b2e78SAlexander Motin 1351581b2e78SAlexander Motin cam_periph_async(periph, code, path, arg); 1352581b2e78SAlexander Motin break; 1353581b2e78SAlexander Motin } 13543089bb2eSAlexander Motin case AC_ADVINFO_CHANGED: 13553089bb2eSAlexander Motin { 13563089bb2eSAlexander Motin uintptr_t buftype; 13573089bb2eSAlexander Motin 13583089bb2eSAlexander Motin buftype = (uintptr_t)arg; 13593089bb2eSAlexander Motin if (buftype == CDAI_TYPE_PHYS_PATH) { 13603089bb2eSAlexander Motin struct ada_softc *softc; 13613089bb2eSAlexander Motin 13623089bb2eSAlexander Motin softc = periph->softc; 13633089bb2eSAlexander Motin disk_attr_changed(softc->disk, "GEOM::physpath", 13643089bb2eSAlexander Motin M_NOWAIT); 13653089bb2eSAlexander Motin } 13663089bb2eSAlexander Motin break; 13673089bb2eSAlexander Motin } 1368f513d14cSAlexander Motin case AC_SENT_BDR: 1369f513d14cSAlexander Motin case AC_BUS_RESET: 1370f513d14cSAlexander Motin { 1371f513d14cSAlexander Motin softc = (struct ada_softc *)periph->softc; 1372f513d14cSAlexander Motin cam_periph_async(periph, code, path, arg); 1373f513d14cSAlexander Motin if (softc->state != ADA_STATE_NORMAL) 1374f513d14cSAlexander Motin break; 1375ec5325dbSEdward Tomasz Napierala memset(&cgd, 0, sizeof(cgd)); 1376e3a6d3a4SAlexander Motin xpt_setup_ccb(&cgd.ccb_h, periph->path, CAM_PRIORITY_NORMAL); 1377f513d14cSAlexander Motin cgd.ccb_h.func_code = XPT_GDEV_TYPE; 1378f513d14cSAlexander Motin xpt_action((union ccb *)&cgd); 13799a6844d5SKenneth D. Merry if (ADA_RA >= 0 && softc->flags & ADA_FLAG_CAN_RAHEAD) 13801ed6aaf9SAlexander Motin softc->state = ADA_STATE_RAHEAD; 13811d9aaa86SAlexander Motin else if (ADA_WC >= 0 && softc->flags & ADA_FLAG_CAN_WCACHE) 1382f513d14cSAlexander Motin softc->state = ADA_STATE_WCACHE; 1383600fd98fSKenneth D. Merry else if ((softc->flags & ADA_FLAG_CAN_LOG) 1384600fd98fSKenneth D. Merry && (softc->zone_mode != ADA_ZONE_NONE)) 13859a6844d5SKenneth D. Merry softc->state = ADA_STATE_LOGDIR; 13861ed6aaf9SAlexander Motin else 13871ed6aaf9SAlexander Motin break; 138899e7a4adSScott Long if (cam_periph_acquire(periph) != 0) 1389227d67aaSAlexander Motin softc->state = ADA_STATE_NORMAL; 1390227d67aaSAlexander Motin else 1391f513d14cSAlexander Motin xpt_schedule(periph, CAM_PRIORITY_DEV); 1392f513d14cSAlexander Motin } 139352c9ce25SScott Long default: 139452c9ce25SScott Long cam_periph_async(periph, code, path, arg); 139552c9ce25SScott Long break; 139652c9ce25SScott Long } 139752c9ce25SScott Long } 139852c9ce25SScott Long 13999a6844d5SKenneth D. Merry static int 14009a6844d5SKenneth D. Merry adazonemodesysctl(SYSCTL_HANDLER_ARGS) 14019a6844d5SKenneth D. Merry { 14029a6844d5SKenneth D. Merry char tmpbuf[40]; 14039a6844d5SKenneth D. Merry struct ada_softc *softc; 14049a6844d5SKenneth D. Merry int error; 14059a6844d5SKenneth D. Merry 14069a6844d5SKenneth D. Merry softc = (struct ada_softc *)arg1; 14079a6844d5SKenneth D. Merry 14089a6844d5SKenneth D. Merry switch (softc->zone_mode) { 14099a6844d5SKenneth D. Merry case ADA_ZONE_DRIVE_MANAGED: 14109a6844d5SKenneth D. Merry snprintf(tmpbuf, sizeof(tmpbuf), "Drive Managed"); 14119a6844d5SKenneth D. Merry break; 14129a6844d5SKenneth D. Merry case ADA_ZONE_HOST_AWARE: 14139a6844d5SKenneth D. Merry snprintf(tmpbuf, sizeof(tmpbuf), "Host Aware"); 14149a6844d5SKenneth D. Merry break; 14159a6844d5SKenneth D. Merry case ADA_ZONE_HOST_MANAGED: 14169a6844d5SKenneth D. Merry snprintf(tmpbuf, sizeof(tmpbuf), "Host Managed"); 14179a6844d5SKenneth D. Merry break; 14189a6844d5SKenneth D. Merry case ADA_ZONE_NONE: 14199a6844d5SKenneth D. Merry default: 14209a6844d5SKenneth D. Merry snprintf(tmpbuf, sizeof(tmpbuf), "Not Zoned"); 14219a6844d5SKenneth D. Merry break; 14229a6844d5SKenneth D. Merry } 14239a6844d5SKenneth D. Merry 14249a6844d5SKenneth D. Merry error = sysctl_handle_string(oidp, tmpbuf, sizeof(tmpbuf), req); 14259a6844d5SKenneth D. Merry 14269a6844d5SKenneth D. Merry return (error); 14279a6844d5SKenneth D. Merry } 14289a6844d5SKenneth D. Merry 14299a6844d5SKenneth D. Merry static int 14309a6844d5SKenneth D. Merry adazonesupsysctl(SYSCTL_HANDLER_ARGS) 14319a6844d5SKenneth D. Merry { 14329a6844d5SKenneth D. Merry char tmpbuf[180]; 14339a6844d5SKenneth D. Merry struct ada_softc *softc; 14349a6844d5SKenneth D. Merry struct sbuf sb; 14359a6844d5SKenneth D. Merry int error, first; 14369a6844d5SKenneth D. Merry unsigned int i; 14379a6844d5SKenneth D. Merry 14389a6844d5SKenneth D. Merry softc = (struct ada_softc *)arg1; 14399a6844d5SKenneth D. Merry 14409a6844d5SKenneth D. Merry error = 0; 14419a6844d5SKenneth D. Merry first = 1; 14429a6844d5SKenneth D. Merry sbuf_new(&sb, tmpbuf, sizeof(tmpbuf), 0); 14439a6844d5SKenneth D. Merry 14449a6844d5SKenneth D. Merry for (i = 0; i < sizeof(ada_zone_desc_table) / 14459a6844d5SKenneth D. Merry sizeof(ada_zone_desc_table[0]); i++) { 14469a6844d5SKenneth D. Merry if (softc->zone_flags & ada_zone_desc_table[i].value) { 14479a6844d5SKenneth D. Merry if (first == 0) 14489a6844d5SKenneth D. Merry sbuf_printf(&sb, ", "); 14499a6844d5SKenneth D. Merry else 14509a6844d5SKenneth D. Merry first = 0; 14519a6844d5SKenneth D. Merry sbuf_cat(&sb, ada_zone_desc_table[i].desc); 14529a6844d5SKenneth D. Merry } 14539a6844d5SKenneth D. Merry } 14549a6844d5SKenneth D. Merry 14559a6844d5SKenneth D. Merry if (first == 1) 14569a6844d5SKenneth D. Merry sbuf_printf(&sb, "None"); 14579a6844d5SKenneth D. Merry 14589a6844d5SKenneth D. Merry sbuf_finish(&sb); 14599a6844d5SKenneth D. Merry 14609a6844d5SKenneth D. Merry error = sysctl_handle_string(oidp, sbuf_data(&sb), sbuf_len(&sb), req); 14619a6844d5SKenneth D. Merry 14629a6844d5SKenneth D. Merry return (error); 14639a6844d5SKenneth D. Merry } 14649a6844d5SKenneth D. Merry 146552c9ce25SScott Long static void 146652c9ce25SScott Long adasysctlinit(void *context, int pending) 146752c9ce25SScott Long { 146852c9ce25SScott Long struct cam_periph *periph; 146952c9ce25SScott Long struct ada_softc *softc; 14704d470952SAlexander Motin char tmpstr[32], tmpstr2[16]; 147152c9ce25SScott Long 147252c9ce25SScott Long periph = (struct cam_periph *)context; 1473e3a6d3a4SAlexander Motin 1474e3a6d3a4SAlexander Motin /* periph was held for us when this task was enqueued */ 14757338ef1aSAlexander Motin if ((periph->flags & CAM_PERIPH_INVALID) != 0) { 1476e3a6d3a4SAlexander Motin cam_periph_release(periph); 147752c9ce25SScott Long return; 1478e3a6d3a4SAlexander Motin } 147952c9ce25SScott Long 148052c9ce25SScott Long softc = (struct ada_softc *)periph->softc; 148152c9ce25SScott Long snprintf(tmpstr, sizeof(tmpstr), "CAM ADA unit %d",periph->unit_number); 148252c9ce25SScott Long snprintf(tmpstr2, sizeof(tmpstr2), "%d", periph->unit_number); 148352c9ce25SScott Long 148452c9ce25SScott Long sysctl_ctx_init(&softc->sysctl_ctx); 148552c9ce25SScott Long softc->flags |= ADA_FLAG_SCTX_INIT; 14864c484fd2SEd Schouten softc->sysctl_tree = SYSCTL_ADD_NODE_WITH_LABEL(&softc->sysctl_ctx, 148752c9ce25SScott Long SYSCTL_STATIC_CHILDREN(_kern_cam_ada), OID_AUTO, tmpstr2, 14887029da5cSPawel Biernacki CTLFLAG_RD | CTLFLAG_MPSAFE, 0, tmpstr, "device_index"); 148952c9ce25SScott Long if (softc->sysctl_tree == NULL) { 149052c9ce25SScott Long printf("adasysctlinit: unable to allocate sysctl tree\n"); 149152c9ce25SScott Long cam_periph_release(periph); 149252c9ce25SScott Long return; 149352c9ce25SScott Long } 149452c9ce25SScott Long 1495a6e0c5daSWarner Losh SYSCTL_ADD_PROC(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), 14967029da5cSPawel Biernacki OID_AUTO, "delete_method", 1497303477d3SAlexander Motin CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, 1498a6e0c5daSWarner Losh softc, 0, adadeletemethodsysctl, "A", 1499a6e0c5daSWarner Losh "BIO_DELETE execution method"); 1500ea657f2cSWarner Losh SYSCTL_ADD_UQUAD(&softc->sysctl_ctx, 1501ea657f2cSWarner Losh SYSCTL_CHILDREN(softc->sysctl_tree), OID_AUTO, 1502ea657f2cSWarner Losh "trim_count", CTLFLAG_RD, &softc->trim_count, 1503ea657f2cSWarner Losh "Total number of dsm commands sent"); 1504ea657f2cSWarner Losh SYSCTL_ADD_UQUAD(&softc->sysctl_ctx, 1505ea657f2cSWarner Losh SYSCTL_CHILDREN(softc->sysctl_tree), OID_AUTO, 1506ea657f2cSWarner Losh "trim_ranges", CTLFLAG_RD, &softc->trim_ranges, 1507ea657f2cSWarner Losh "Total number of ranges in dsm commands"); 1508ea657f2cSWarner Losh SYSCTL_ADD_UQUAD(&softc->sysctl_ctx, 1509ea657f2cSWarner Losh SYSCTL_CHILDREN(softc->sysctl_tree), OID_AUTO, 1510ea657f2cSWarner Losh "trim_lbas", CTLFLAG_RD, &softc->trim_lbas, 1511ea657f2cSWarner Losh "Total lbas in the dsm commands sent"); 1512e3a6d3a4SAlexander Motin SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), 15131ed6aaf9SAlexander Motin OID_AUTO, "read_ahead", CTLFLAG_RW | CTLFLAG_MPSAFE, 15141ed6aaf9SAlexander Motin &softc->read_ahead, 0, "Enable disk read ahead."); 15151ed6aaf9SAlexander Motin SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), 1516e3a6d3a4SAlexander Motin OID_AUTO, "write_cache", CTLFLAG_RW | CTLFLAG_MPSAFE, 1517e3a6d3a4SAlexander Motin &softc->write_cache, 0, "Enable disk write cache."); 15189a6844d5SKenneth D. Merry SYSCTL_ADD_PROC(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), 15197029da5cSPawel Biernacki OID_AUTO, "zone_mode", 1520303477d3SAlexander Motin CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, 15219a6844d5SKenneth D. Merry softc, 0, adazonemodesysctl, "A", 15229a6844d5SKenneth D. Merry "Zone Mode"); 15239a6844d5SKenneth D. Merry SYSCTL_ADD_PROC(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), 15247029da5cSPawel Biernacki OID_AUTO, "zone_support", 1525303477d3SAlexander Motin CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, 15269a6844d5SKenneth D. Merry softc, 0, adazonesupsysctl, "A", 15279a6844d5SKenneth D. Merry "Zone Support"); 15289a6844d5SKenneth D. Merry SYSCTL_ADD_UQUAD(&softc->sysctl_ctx, 15299a6844d5SKenneth D. Merry SYSCTL_CHILDREN(softc->sysctl_tree), OID_AUTO, 15309a6844d5SKenneth D. Merry "optimal_seq_zones", CTLFLAG_RD, &softc->optimal_seq_zones, 15319a6844d5SKenneth D. Merry "Optimal Number of Open Sequential Write Preferred Zones"); 15329a6844d5SKenneth D. Merry SYSCTL_ADD_UQUAD(&softc->sysctl_ctx, 15339a6844d5SKenneth D. Merry SYSCTL_CHILDREN(softc->sysctl_tree), OID_AUTO, 15349a6844d5SKenneth D. Merry "optimal_nonseq_zones", CTLFLAG_RD, 15359a6844d5SKenneth D. Merry &softc->optimal_nonseq_zones, 15369a6844d5SKenneth D. Merry "Optimal Number of Non-Sequentially Written Sequential Write " 15379a6844d5SKenneth D. Merry "Preferred Zones"); 15389a6844d5SKenneth D. Merry SYSCTL_ADD_UQUAD(&softc->sysctl_ctx, 15399a6844d5SKenneth D. Merry SYSCTL_CHILDREN(softc->sysctl_tree), OID_AUTO, 15409a6844d5SKenneth D. Merry "max_seq_zones", CTLFLAG_RD, &softc->max_seq_zones, 15419a6844d5SKenneth D. Merry "Maximum Number of Open Sequential Write Required Zones"); 1542aeab0812SWarner Losh SYSCTL_ADD_PROC(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), 1543aeab0812SWarner Losh OID_AUTO, "flags", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, 1544aeab0812SWarner Losh softc, 0, adaflagssysctl, "A", 1545aeab0812SWarner Losh "Flags for drive"); 1546cf3ff63eSWarner Losh SYSCTL_ADD_PROC(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), 1547cf3ff63eSWarner Losh OID_AUTO, "unmapped_io", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, 1548cf3ff63eSWarner Losh &softc->flags, (u_int)ADA_FLAG_UNMAPPEDIO, adabitsysctl, "I", 1549cf3ff63eSWarner Losh "Unmapped I/O support *DEPRECATED* gone in FreeBSD 14"); 155096eb32bfSWarner Losh SYSCTL_ADD_PROC(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), 155196eb32bfSWarner Losh OID_AUTO, "rotating", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, 155296eb32bfSWarner Losh &softc->flags, (u_int)ADA_FLAG_ROTATING, adabitsysctl, "I", 155396eb32bfSWarner Losh "Rotating media *DEPRECATED* gone in FreeBSD 14"); 15549a6844d5SKenneth D. Merry 1555d38677d2SWarner Losh #ifdef CAM_TEST_FAILURE 1556e3a6d3a4SAlexander Motin /* 1557e3a6d3a4SAlexander Motin * Add a 'door bell' sysctl which allows one to set it from userland 1558e3a6d3a4SAlexander Motin * and cause something bad to happen. For the moment, we only allow 1559e3a6d3a4SAlexander Motin * whacking the next read or write. 1560e3a6d3a4SAlexander Motin */ 1561e3a6d3a4SAlexander Motin SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), 1562e3a6d3a4SAlexander Motin OID_AUTO, "force_read_error", CTLFLAG_RW | CTLFLAG_MPSAFE, 1563e3a6d3a4SAlexander Motin &softc->force_read_error, 0, 1564e3a6d3a4SAlexander Motin "Force a read error for the next N reads."); 1565e3a6d3a4SAlexander Motin SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), 1566e3a6d3a4SAlexander Motin OID_AUTO, "force_write_error", CTLFLAG_RW | CTLFLAG_MPSAFE, 1567e3a6d3a4SAlexander Motin &softc->force_write_error, 0, 1568e3a6d3a4SAlexander Motin "Force a write error for the next N writes."); 1569e3a6d3a4SAlexander Motin SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), 1570e3a6d3a4SAlexander Motin OID_AUTO, "periodic_read_error", CTLFLAG_RW | CTLFLAG_MPSAFE, 1571e3a6d3a4SAlexander Motin &softc->periodic_read_error, 0, 1572e3a6d3a4SAlexander Motin "Force a read error every N reads (don't set too low)."); 1573d38677d2SWarner Losh SYSCTL_ADD_PROC(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), 1574d38677d2SWarner Losh OID_AUTO, "invalidate", CTLTYPE_U64 | CTLFLAG_RW | CTLFLAG_MPSAFE, 1575d38677d2SWarner Losh periph, 0, cam_periph_invalidate_sysctl, "I", 1576d38677d2SWarner Losh "Write 1 to invalidate the drive immediately"); 1577e3a6d3a4SAlexander Motin #endif 1578a6e0c5daSWarner Losh 1579a6e0c5daSWarner Losh #ifdef CAM_IO_STATS 1580a6e0c5daSWarner Losh softc->sysctl_stats_tree = SYSCTL_ADD_NODE(&softc->sysctl_stats_ctx, 1581a6e0c5daSWarner Losh SYSCTL_CHILDREN(softc->sysctl_tree), OID_AUTO, "stats", 15827029da5cSPawel Biernacki CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "Statistics"); 1583a6e0c5daSWarner Losh SYSCTL_ADD_INT(&softc->sysctl_stats_ctx, 1584a6e0c5daSWarner Losh SYSCTL_CHILDREN(softc->sysctl_stats_tree), 1585a6e0c5daSWarner Losh OID_AUTO, "timeouts", CTLFLAG_RD | CTLFLAG_MPSAFE, 1586a6e0c5daSWarner Losh &softc->timeouts, 0, 1587a6e0c5daSWarner Losh "Device timeouts reported by the SIM"); 1588a6e0c5daSWarner Losh SYSCTL_ADD_INT(&softc->sysctl_stats_ctx, 1589a6e0c5daSWarner Losh SYSCTL_CHILDREN(softc->sysctl_stats_tree), 1590a6e0c5daSWarner Losh OID_AUTO, "errors", CTLFLAG_RD | CTLFLAG_MPSAFE, 1591a6e0c5daSWarner Losh &softc->errors, 0, 1592a6e0c5daSWarner Losh "Transport errors reported by the SIM."); 1593a6e0c5daSWarner Losh SYSCTL_ADD_INT(&softc->sysctl_stats_ctx, 1594a6e0c5daSWarner Losh SYSCTL_CHILDREN(softc->sysctl_stats_tree), 1595a6e0c5daSWarner Losh OID_AUTO, "pack_invalidations", CTLFLAG_RD | CTLFLAG_MPSAFE, 1596a6e0c5daSWarner Losh &softc->invalidations, 0, 1597a6e0c5daSWarner Losh "Device pack invalidations."); 1598a6e0c5daSWarner Losh #endif 1599a6e0c5daSWarner Losh 1600a6e0c5daSWarner Losh cam_iosched_sysctl_init(softc->cam_iosched, &softc->sysctl_ctx, 1601a6e0c5daSWarner Losh softc->sysctl_tree); 1602a6e0c5daSWarner Losh 160352c9ce25SScott Long cam_periph_release(periph); 160452c9ce25SScott Long } 160552c9ce25SScott Long 1606416494d7SJustin T. Gibbs static int 1607416494d7SJustin T. Gibbs adagetattr(struct bio *bp) 1608416494d7SJustin T. Gibbs { 16096884b662SAlexander Motin int ret; 1610416494d7SJustin T. Gibbs struct cam_periph *periph; 1611416494d7SJustin T. Gibbs 161213532153SScott Long if (g_handleattr_int(bp, "GEOM::canspeedup", ada_enable_biospeedup)) 161313532153SScott Long return (EJUSTRETURN); 161413532153SScott Long 1615416494d7SJustin T. Gibbs periph = (struct cam_periph *)bp->bio_disk->d_drv1; 16166884b662SAlexander Motin cam_periph_lock(periph); 1617416494d7SJustin T. Gibbs ret = xpt_getattr(bp->bio_data, bp->bio_length, bp->bio_attribute, 1618416494d7SJustin T. Gibbs periph->path); 16196884b662SAlexander Motin cam_periph_unlock(periph); 1620416494d7SJustin T. Gibbs if (ret == 0) 1621416494d7SJustin T. Gibbs bp->bio_completed = bp->bio_length; 1622416494d7SJustin T. Gibbs return ret; 1623416494d7SJustin T. Gibbs } 1624416494d7SJustin T. Gibbs 1625a6e0c5daSWarner Losh static int 1626a6e0c5daSWarner Losh adadeletemethodsysctl(SYSCTL_HANDLER_ARGS) 1627a6e0c5daSWarner Losh { 1628a6e0c5daSWarner Losh char buf[16]; 1629a6e0c5daSWarner Losh const char *p; 1630a6e0c5daSWarner Losh struct ada_softc *softc; 1631a6e0c5daSWarner Losh int i, error, value, methods; 1632a6e0c5daSWarner Losh 1633a6e0c5daSWarner Losh softc = (struct ada_softc *)arg1; 1634a6e0c5daSWarner Losh 1635a6e0c5daSWarner Losh value = softc->delete_method; 1636a6e0c5daSWarner Losh if (value < 0 || value > ADA_DELETE_MAX) 1637a6e0c5daSWarner Losh p = "UNKNOWN"; 1638a6e0c5daSWarner Losh else 1639a6e0c5daSWarner Losh p = ada_delete_method_names[value]; 1640a6e0c5daSWarner Losh strncpy(buf, p, sizeof(buf)); 1641a6e0c5daSWarner Losh error = sysctl_handle_string(oidp, buf, sizeof(buf), req); 1642a6e0c5daSWarner Losh if (error != 0 || req->newptr == NULL) 1643a6e0c5daSWarner Losh return (error); 1644a6e0c5daSWarner Losh methods = 1 << ADA_DELETE_DISABLE; 1645a6e0c5daSWarner Losh if ((softc->flags & ADA_FLAG_CAN_CFA) && 1646a6e0c5daSWarner Losh !(softc->flags & ADA_FLAG_CAN_48BIT)) 1647a6e0c5daSWarner Losh methods |= 1 << ADA_DELETE_CFA_ERASE; 1648a6e0c5daSWarner Losh if (softc->flags & ADA_FLAG_CAN_TRIM) 1649a6e0c5daSWarner Losh methods |= 1 << ADA_DELETE_DSM_TRIM; 1650a6e0c5daSWarner Losh if (softc->flags & ADA_FLAG_CAN_NCQ_TRIM) 1651a6e0c5daSWarner Losh methods |= 1 << ADA_DELETE_NCQ_DSM_TRIM; 1652a6e0c5daSWarner Losh for (i = 0; i <= ADA_DELETE_MAX; i++) { 1653a6e0c5daSWarner Losh if (!(methods & (1 << i)) || 1654a6e0c5daSWarner Losh strcmp(buf, ada_delete_method_names[i]) != 0) 1655a6e0c5daSWarner Losh continue; 1656a6e0c5daSWarner Losh softc->delete_method = i; 1657a6e0c5daSWarner Losh return (0); 1658a6e0c5daSWarner Losh } 1659a6e0c5daSWarner Losh return (EINVAL); 1660a6e0c5daSWarner Losh } 1661a6e0c5daSWarner Losh 1662aeab0812SWarner Losh static int 1663cf3ff63eSWarner Losh adabitsysctl(SYSCTL_HANDLER_ARGS) 1664cf3ff63eSWarner Losh { 1665cf3ff63eSWarner Losh u_int *flags = arg1; 1666cf3ff63eSWarner Losh u_int test = arg2; 1667cf3ff63eSWarner Losh int tmpout, error; 1668cf3ff63eSWarner Losh 1669cf3ff63eSWarner Losh tmpout = !!(*flags & test); 1670cf3ff63eSWarner Losh error = SYSCTL_OUT(req, &tmpout, sizeof(tmpout)); 1671cf3ff63eSWarner Losh if (error || !req->newptr) 1672cf3ff63eSWarner Losh return (error); 1673cf3ff63eSWarner Losh 1674cf3ff63eSWarner Losh return (EPERM); 1675cf3ff63eSWarner Losh } 1676cf3ff63eSWarner Losh 1677cf3ff63eSWarner Losh static int 1678aeab0812SWarner Losh adaflagssysctl(SYSCTL_HANDLER_ARGS) 1679aeab0812SWarner Losh { 1680aeab0812SWarner Losh struct sbuf sbuf; 1681aeab0812SWarner Losh struct ada_softc *softc = arg1; 1682aeab0812SWarner Losh int error; 1683aeab0812SWarner Losh 1684aeab0812SWarner Losh sbuf_new_for_sysctl(&sbuf, NULL, 0, req); 1685aeab0812SWarner Losh if (softc->flags != 0) 1686aeab0812SWarner Losh sbuf_printf(&sbuf, "0x%b", (unsigned)softc->flags, ADA_FLAG_STRING); 1687aeab0812SWarner Losh else 1688aeab0812SWarner Losh sbuf_printf(&sbuf, "0"); 1689aeab0812SWarner Losh error = sbuf_finish(&sbuf); 1690aeab0812SWarner Losh sbuf_delete(&sbuf); 1691aeab0812SWarner Losh 1692aeab0812SWarner Losh return (error); 1693aeab0812SWarner Losh } 1694aeab0812SWarner Losh 16959a6844d5SKenneth D. Merry static void 16969a6844d5SKenneth D. Merry adasetflags(struct ada_softc *softc, struct ccb_getdev *cgd) 16979a6844d5SKenneth D. Merry { 16989a6844d5SKenneth D. Merry if ((cgd->ident_data.capabilities1 & ATA_SUPPORT_DMA) && 16999a6844d5SKenneth D. Merry (cgd->inq_flags & SID_DMA)) 17009a6844d5SKenneth D. Merry softc->flags |= ADA_FLAG_CAN_DMA; 17019a6844d5SKenneth D. Merry else 17029a6844d5SKenneth D. Merry softc->flags &= ~ADA_FLAG_CAN_DMA; 17039a6844d5SKenneth D. Merry 17049a6844d5SKenneth D. Merry if (cgd->ident_data.support.command2 & ATA_SUPPORT_ADDRESS48) { 17059a6844d5SKenneth D. Merry softc->flags |= ADA_FLAG_CAN_48BIT; 17069a6844d5SKenneth D. Merry if (cgd->inq_flags & SID_DMA48) 17079a6844d5SKenneth D. Merry softc->flags |= ADA_FLAG_CAN_DMA48; 17089a6844d5SKenneth D. Merry else 17099a6844d5SKenneth D. Merry softc->flags &= ~ADA_FLAG_CAN_DMA48; 17109a6844d5SKenneth D. Merry } else 17119a6844d5SKenneth D. Merry softc->flags &= ~(ADA_FLAG_CAN_48BIT | ADA_FLAG_CAN_DMA48); 17129a6844d5SKenneth D. Merry 17139a6844d5SKenneth D. Merry if (cgd->ident_data.support.command2 & ATA_SUPPORT_FLUSHCACHE) 17149a6844d5SKenneth D. Merry softc->flags |= ADA_FLAG_CAN_FLUSHCACHE; 17159a6844d5SKenneth D. Merry else 17169a6844d5SKenneth D. Merry softc->flags &= ~ADA_FLAG_CAN_FLUSHCACHE; 17179a6844d5SKenneth D. Merry 17189a6844d5SKenneth D. Merry if (cgd->ident_data.support.command1 & ATA_SUPPORT_POWERMGT) 17199a6844d5SKenneth D. Merry softc->flags |= ADA_FLAG_CAN_POWERMGT; 17209a6844d5SKenneth D. Merry else 17219a6844d5SKenneth D. Merry softc->flags &= ~ADA_FLAG_CAN_POWERMGT; 17229a6844d5SKenneth D. Merry 17239a6844d5SKenneth D. Merry if ((cgd->ident_data.satacapabilities & ATA_SUPPORT_NCQ) && 17249a6844d5SKenneth D. Merry (cgd->inq_flags & SID_DMA) && (cgd->inq_flags & SID_CmdQue)) 17259a6844d5SKenneth D. Merry softc->flags |= ADA_FLAG_CAN_NCQ; 17269a6844d5SKenneth D. Merry else 17279a6844d5SKenneth D. Merry softc->flags &= ~ADA_FLAG_CAN_NCQ; 17289a6844d5SKenneth D. Merry 17299a6844d5SKenneth D. Merry if ((cgd->ident_data.support_dsm & ATA_SUPPORT_DSM_TRIM) && 173076d843daSAlexander Motin (cgd->inq_flags & SID_DMA) && 173176d843daSAlexander Motin (softc->quirks & ADA_Q_NO_TRIM) == 0) { 17329a6844d5SKenneth D. Merry softc->flags |= ADA_FLAG_CAN_TRIM; 17339a6844d5SKenneth D. Merry softc->trim_max_ranges = TRIM_MAX_RANGES; 17349a6844d5SKenneth D. Merry if (cgd->ident_data.max_dsm_blocks != 0) { 17359a6844d5SKenneth D. Merry softc->trim_max_ranges = 17369a6844d5SKenneth D. Merry min(cgd->ident_data.max_dsm_blocks * 17379a6844d5SKenneth D. Merry ATA_DSM_BLK_RANGES, softc->trim_max_ranges); 17389a6844d5SKenneth D. Merry } 17399a6844d5SKenneth D. Merry /* 17409a6844d5SKenneth D. Merry * If we can do RCVSND_FPDMA_QUEUED commands, we may be able 17419a6844d5SKenneth D. Merry * to do NCQ trims, if we support trims at all. We also need 17429a6844d5SKenneth D. Merry * support from the SIM to do things properly. Perhaps we 17439a6844d5SKenneth D. Merry * should look at log 13 dword 0 bit 0 and dword 1 bit 0 are 17449a6844d5SKenneth D. Merry * set too... 17459a6844d5SKenneth D. Merry */ 17469a6844d5SKenneth D. Merry if ((softc->quirks & ADA_Q_NCQ_TRIM_BROKEN) == 0 && 17479a6844d5SKenneth D. Merry (softc->flags & ADA_FLAG_PIM_ATA_EXT) != 0 && 17489a6844d5SKenneth D. Merry (cgd->ident_data.satacapabilities2 & 17499a6844d5SKenneth D. Merry ATA_SUPPORT_RCVSND_FPDMA_QUEUED) != 0 && 17509a6844d5SKenneth D. Merry (softc->flags & ADA_FLAG_CAN_TRIM) != 0) 17519a6844d5SKenneth D. Merry softc->flags |= ADA_FLAG_CAN_NCQ_TRIM; 17529a6844d5SKenneth D. Merry else 17539a6844d5SKenneth D. Merry softc->flags &= ~ADA_FLAG_CAN_NCQ_TRIM; 17549a6844d5SKenneth D. Merry } else 17559a6844d5SKenneth D. Merry softc->flags &= ~(ADA_FLAG_CAN_TRIM | ADA_FLAG_CAN_NCQ_TRIM); 17569a6844d5SKenneth D. Merry 17579a6844d5SKenneth D. Merry if (cgd->ident_data.support.command2 & ATA_SUPPORT_CFA) 17589a6844d5SKenneth D. Merry softc->flags |= ADA_FLAG_CAN_CFA; 17599a6844d5SKenneth D. Merry else 17609a6844d5SKenneth D. Merry softc->flags &= ~ADA_FLAG_CAN_CFA; 17619a6844d5SKenneth D. Merry 17629a6844d5SKenneth D. Merry /* 17639a6844d5SKenneth D. Merry * Now that we've set the appropriate flags, setup the delete 17649a6844d5SKenneth D. Merry * method. 17659a6844d5SKenneth D. Merry */ 17669a6844d5SKenneth D. Merry adasetdeletemethod(softc); 17679a6844d5SKenneth D. Merry 1768600fd98fSKenneth D. Merry if ((cgd->ident_data.support.extension & ATA_SUPPORT_GENLOG) 1769600fd98fSKenneth D. Merry && ((softc->quirks & ADA_Q_LOG_BROKEN) == 0)) 17709a6844d5SKenneth D. Merry softc->flags |= ADA_FLAG_CAN_LOG; 17719a6844d5SKenneth D. Merry else 17729a6844d5SKenneth D. Merry softc->flags &= ~ADA_FLAG_CAN_LOG; 17739a6844d5SKenneth D. Merry 17749a6844d5SKenneth D. Merry if ((cgd->ident_data.support3 & ATA_SUPPORT_ZONE_MASK) == 17759a6844d5SKenneth D. Merry ATA_SUPPORT_ZONE_HOST_AWARE) 17769a6844d5SKenneth D. Merry softc->zone_mode = ADA_ZONE_HOST_AWARE; 1777600fd98fSKenneth D. Merry else if (((cgd->ident_data.support3 & ATA_SUPPORT_ZONE_MASK) == 17789a6844d5SKenneth D. Merry ATA_SUPPORT_ZONE_DEV_MANAGED) 1779600fd98fSKenneth D. Merry || (softc->quirks & ADA_Q_SMR_DM)) 17809a6844d5SKenneth D. Merry softc->zone_mode = ADA_ZONE_DRIVE_MANAGED; 17819a6844d5SKenneth D. Merry else 17829a6844d5SKenneth D. Merry softc->zone_mode = ADA_ZONE_NONE; 17839a6844d5SKenneth D. Merry 17849a6844d5SKenneth D. Merry if (cgd->ident_data.support.command1 & ATA_SUPPORT_LOOKAHEAD) 17859a6844d5SKenneth D. Merry softc->flags |= ADA_FLAG_CAN_RAHEAD; 17869a6844d5SKenneth D. Merry else 17879a6844d5SKenneth D. Merry softc->flags &= ~ADA_FLAG_CAN_RAHEAD; 17889a6844d5SKenneth D. Merry 17899a6844d5SKenneth D. Merry if (cgd->ident_data.support.command1 & ATA_SUPPORT_WRITECACHE) 17909a6844d5SKenneth D. Merry softc->flags |= ADA_FLAG_CAN_WCACHE; 17919a6844d5SKenneth D. Merry else 17929a6844d5SKenneth D. Merry softc->flags &= ~ADA_FLAG_CAN_WCACHE; 17939a6844d5SKenneth D. Merry } 17949a6844d5SKenneth D. Merry 179552c9ce25SScott Long static cam_status 179652c9ce25SScott Long adaregister(struct cam_periph *periph, void *arg) 179752c9ce25SScott Long { 179852c9ce25SScott Long struct ada_softc *softc; 179952c9ce25SScott Long struct ccb_getdev *cgd; 180052c9ce25SScott Long struct disk_params *dp; 18015d01277fSScott Long struct sbuf sb; 18025d01277fSScott Long char *announce_buf; 180352c9ce25SScott Long caddr_t match; 18044a3760baSAlexander Motin int quirks; 180552c9ce25SScott Long 180652c9ce25SScott Long cgd = (struct ccb_getdev *)arg; 180752c9ce25SScott Long if (cgd == NULL) { 180852c9ce25SScott Long printf("adaregister: no getdev CCB, can't register device\n"); 180952c9ce25SScott Long return(CAM_REQ_CMP_ERR); 181052c9ce25SScott Long } 181152c9ce25SScott Long 181252c9ce25SScott Long softc = (struct ada_softc *)malloc(sizeof(*softc), M_DEVBUF, 181352c9ce25SScott Long M_NOWAIT|M_ZERO); 181452c9ce25SScott Long 181552c9ce25SScott Long if (softc == NULL) { 181652c9ce25SScott Long printf("adaregister: Unable to probe new device. " 181752c9ce25SScott Long "Unable to allocate softc\n"); 181852c9ce25SScott Long return(CAM_REQ_CMP_ERR); 181952c9ce25SScott Long } 182052c9ce25SScott Long 18215d01277fSScott Long announce_buf = softc->announce_temp; 18225d01277fSScott Long bzero(announce_buf, ADA_ANNOUNCETMP_SZ); 18235d01277fSScott Long 18240028abe6SWarner Losh if (cam_iosched_init(&softc->cam_iosched, periph) != 0) { 1825a6e0c5daSWarner Losh printf("adaregister: Unable to probe new device. " 1826a6e0c5daSWarner Losh "Unable to allocate iosched memory\n"); 182775548271SConrad Meyer free(softc, M_DEVBUF); 1828a6e0c5daSWarner Losh return(CAM_REQ_CMP_ERR); 1829a6e0c5daSWarner Losh } 183052c9ce25SScott Long 183152c9ce25SScott Long periph->softc = softc; 183276d843daSAlexander Motin xpt_path_inq(&softc->cpi, periph->path); 183352c9ce25SScott Long 183452c9ce25SScott Long /* 183552c9ce25SScott Long * See if this device has any quirks. 183652c9ce25SScott Long */ 183730a4094fSAlexander Motin match = cam_quirkmatch((caddr_t)&cgd->ident_data, 183830a4094fSAlexander Motin (caddr_t)ada_quirk_table, 1839323b076eSPedro F. Giffuni nitems(ada_quirk_table), 184030a4094fSAlexander Motin sizeof(*ada_quirk_table), ata_identify_match); 184152c9ce25SScott Long if (match != NULL) 184252c9ce25SScott Long softc->quirks = ((struct ada_quirk_entry *)match)->quirks; 184352c9ce25SScott Long else 184452c9ce25SScott Long softc->quirks = ADA_Q_NONE; 184552c9ce25SScott Long 184652c9ce25SScott Long TASK_INIT(&softc->sysctl_task, 0, adasysctlinit, periph); 184752c9ce25SScott Long 184852c9ce25SScott Long /* 184952c9ce25SScott Long * Register this media as a disk 185052c9ce25SScott Long */ 1851781338b6SAlexander Motin (void)cam_periph_hold(periph, PRIBIO); 1852edec59d9SAlexander Motin cam_periph_unlock(periph); 18535d01277fSScott Long snprintf(announce_buf, ADA_ANNOUNCETMP_SZ, 1854d3a460d3SAlexander Motin "kern.cam.ada.%d.quirks", periph->unit_number); 1855d3a460d3SAlexander Motin quirks = softc->quirks; 1856d3a460d3SAlexander Motin TUNABLE_INT_FETCH(announce_buf, &quirks); 1857d3a460d3SAlexander Motin softc->quirks = quirks; 18581ed6aaf9SAlexander Motin softc->read_ahead = -1; 18595d01277fSScott Long snprintf(announce_buf, ADA_ANNOUNCETMP_SZ, 18601ed6aaf9SAlexander Motin "kern.cam.ada.%d.read_ahead", periph->unit_number); 18611ed6aaf9SAlexander Motin TUNABLE_INT_FETCH(announce_buf, &softc->read_ahead); 1862781338b6SAlexander Motin softc->write_cache = -1; 18635d01277fSScott Long snprintf(announce_buf, ADA_ANNOUNCETMP_SZ, 1864781338b6SAlexander Motin "kern.cam.ada.%d.write_cache", periph->unit_number); 1865781338b6SAlexander Motin TUNABLE_INT_FETCH(announce_buf, &softc->write_cache); 18663f54ec85SKenneth D. Merry 18673f54ec85SKenneth D. Merry /* 18683394d423SEdward Tomasz Napierala * Let XPT know we can use UMA-allocated CCBs. 18693394d423SEdward Tomasz Napierala */ 18703394d423SEdward Tomasz Napierala if (ada_enable_uma_ccbs) { 18713394d423SEdward Tomasz Napierala KASSERT(ada_ccb_zone != NULL, 18723394d423SEdward Tomasz Napierala ("%s: NULL ada_ccb_zone", __func__)); 18733394d423SEdward Tomasz Napierala periph->ccb_zone = ada_ccb_zone; 18743394d423SEdward Tomasz Napierala } 18753394d423SEdward Tomasz Napierala 18763394d423SEdward Tomasz Napierala /* 18773f54ec85SKenneth D. Merry * Set support flags based on the Identify data and quirks. 18783f54ec85SKenneth D. Merry */ 18793f54ec85SKenneth D. Merry adasetflags(softc, cgd); 188076d843daSAlexander Motin if (softc->cpi.hba_misc & PIM_ATA_EXT) 188176d843daSAlexander Motin softc->flags |= ADA_FLAG_PIM_ATA_EXT; 18823f54ec85SKenneth D. Merry 188362cc3a63SSteven Hartland /* Disable queue sorting for non-rotational media by default. */ 1884a6e0c5daSWarner Losh if (cgd->ident_data.media_rotation_rate == ATA_RATE_NON_ROTATING) { 188596eb32bfSWarner Losh softc->flags &= ~ADA_FLAG_ROTATING; 1886a6e0c5daSWarner Losh } else { 188796eb32bfSWarner Losh softc->flags |= ADA_FLAG_ROTATING; 1888a6e0c5daSWarner Losh } 188996eb32bfSWarner Losh cam_iosched_set_sort_queue(softc->cam_iosched, 189096eb32bfSWarner Losh (softc->flags & ADA_FLAG_ROTATING) ? -1 : 0); 189152c9ce25SScott Long softc->disk = disk_alloc(); 189276d843daSAlexander Motin adasetgeom(softc, cgd); 1893b8b6b5d3SAlexander Motin softc->disk->d_devstat = devstat_new_entry(periph->periph_name, 1894b8b6b5d3SAlexander Motin periph->unit_number, softc->params.secsize, 1895b8b6b5d3SAlexander Motin DEVSTAT_ALL_SUPPORTED, 1896b8b6b5d3SAlexander Motin DEVSTAT_TYPE_DIRECT | 189776d843daSAlexander Motin XPORT_DEVSTAT_TYPE(softc->cpi.transport), 1898b8b6b5d3SAlexander Motin DEVSTAT_PRIORITY_DISK); 189952c9ce25SScott Long softc->disk->d_open = adaopen; 190052c9ce25SScott Long softc->disk->d_close = adaclose; 190152c9ce25SScott Long softc->disk->d_strategy = adastrategy; 1902416494d7SJustin T. Gibbs softc->disk->d_getattr = adagetattr; 1903e07ac3f2SJohn Baldwin if (cam_sim_pollable(periph->sim)) 190452c9ce25SScott Long softc->disk->d_dump = adadump; 19050ba1e4d0SKenneth D. Merry softc->disk->d_gone = adadiskgonecb; 190652c9ce25SScott Long softc->disk->d_name = "ada"; 190752c9ce25SScott Long softc->disk->d_drv1 = periph; 190852c9ce25SScott Long softc->disk->d_unit = periph->unit_number; 190952c9ce25SScott Long 19100ba1e4d0SKenneth D. Merry /* 19110ba1e4d0SKenneth D. Merry * Acquire a reference to the periph before we register with GEOM. 19120ba1e4d0SKenneth D. Merry * We'll release this reference once GEOM calls us back (via 19130ba1e4d0SKenneth D. Merry * adadiskgonecb()) telling us that our provider has been freed. 19140ba1e4d0SKenneth D. Merry */ 191599e7a4adSScott Long if (cam_periph_acquire(periph) != 0) { 19160ba1e4d0SKenneth D. Merry xpt_print(periph->path, "%s: lost periph during " 19170ba1e4d0SKenneth D. Merry "registration!\n", __func__); 19180ba1e4d0SKenneth D. Merry cam_periph_lock(periph); 19190ba1e4d0SKenneth D. Merry return (CAM_REQ_CMP_ERR); 19200ba1e4d0SKenneth D. Merry } 192152c9ce25SScott Long disk_create(softc->disk, DISK_VERSION); 1922edec59d9SAlexander Motin cam_periph_lock(periph); 192352c9ce25SScott Long 192452c9ce25SScott Long dp = &softc->params; 19255d01277fSScott Long snprintf(announce_buf, ADA_ANNOUNCETMP_SZ, 192668546995SAlexander Motin "%juMB (%ju %u byte sectors)", 192768546995SAlexander Motin ((uintmax_t)dp->secsize * dp->sectors) / (1024 * 1024), 192868546995SAlexander Motin (uintmax_t)dp->sectors, dp->secsize); 19295d01277fSScott Long 19305d01277fSScott Long sbuf_new(&sb, softc->announce_buffer, ADA_ANNOUNCE_SZ, SBUF_FIXEDLEN); 19315d01277fSScott Long xpt_announce_periph_sbuf(periph, &sb, announce_buf); 19325d01277fSScott Long xpt_announce_quirks_sbuf(periph, &sb, softc->quirks, ADA_Q_BIT_STRING); 19335d01277fSScott Long sbuf_finish(&sb); 19345d01277fSScott Long sbuf_putbuf(&sb); 1935e3a6d3a4SAlexander Motin 1936e3a6d3a4SAlexander Motin /* 1937e3a6d3a4SAlexander Motin * Create our sysctl variables, now that we know 1938e3a6d3a4SAlexander Motin * we have successfully attached. 1939e3a6d3a4SAlexander Motin */ 194099e7a4adSScott Long if (cam_periph_acquire(periph) == 0) 1941e3a6d3a4SAlexander Motin taskqueue_enqueue(taskqueue_thread, &softc->sysctl_task); 1942e3a6d3a4SAlexander Motin 194352c9ce25SScott Long /* 194452c9ce25SScott Long * Add async callbacks for bus reset and 194552c9ce25SScott Long * bus device reset calls. I don't bother 194652c9ce25SScott Long * checking if this fails as, in most cases, 194752c9ce25SScott Long * the system will function just fine without 194852c9ce25SScott Long * them and the only alternative would be to 194952c9ce25SScott Long * not attach the device on failure. 195052c9ce25SScott Long */ 19513089bb2eSAlexander Motin xpt_register_async(AC_SENT_BDR | AC_BUS_RESET | AC_LOST_DEVICE | 1952581b2e78SAlexander Motin AC_GETDEV_CHANGED | AC_ADVINFO_CHANGED, 1953581b2e78SAlexander Motin adaasync, periph, periph->path); 195452c9ce25SScott Long 195552c9ce25SScott Long /* 195652c9ce25SScott Long * Schedule a periodic event to occasionally send an 195752c9ce25SScott Long * ordered tag to a device. 195852c9ce25SScott Long */ 1959227d67aaSAlexander Motin callout_init_mtx(&softc->sendordered_c, cam_periph_mtx(periph), 0); 19600e5c50bfSAlexander Motin callout_reset_sbt(&softc->sendordered_c, 19610e5c50bfSAlexander Motin SBT_1S / ADA_ORDEREDTAG_INTERVAL * ada_default_timeout, 0, 19620e5c50bfSAlexander Motin adasendorderedtag, softc, C_PREL(1)); 196352c9ce25SScott Long 19649a6844d5SKenneth D. Merry if (ADA_RA >= 0 && softc->flags & ADA_FLAG_CAN_RAHEAD) { 19651ed6aaf9SAlexander Motin softc->state = ADA_STATE_RAHEAD; 19669a6844d5SKenneth D. Merry } else if (ADA_WC >= 0 && softc->flags & ADA_FLAG_CAN_WCACHE) { 1967f513d14cSAlexander Motin softc->state = ADA_STATE_WCACHE; 1968600fd98fSKenneth D. Merry } else if ((softc->flags & ADA_FLAG_CAN_LOG) 1969600fd98fSKenneth D. Merry && (softc->zone_mode != ADA_ZONE_NONE)) { 19709a6844d5SKenneth D. Merry softc->state = ADA_STATE_LOGDIR; 1971227d67aaSAlexander Motin } else { 19729a6844d5SKenneth D. Merry /* 19739a6844d5SKenneth D. Merry * Nothing to probe, so we can just transition to the 19749a6844d5SKenneth D. Merry * normal state. 19759a6844d5SKenneth D. Merry */ 19769a6844d5SKenneth D. Merry adaprobedone(periph, NULL); 1977227d67aaSAlexander Motin return(CAM_REQ_CMP); 1978227d67aaSAlexander Motin } 19799a6844d5SKenneth D. Merry 1980227d67aaSAlexander Motin xpt_schedule(periph, CAM_PRIORITY_DEV); 19819a6844d5SKenneth D. Merry 198252c9ce25SScott Long return(CAM_REQ_CMP); 198352c9ce25SScott Long } 198452c9ce25SScott Long 1985a6e0c5daSWarner Losh static int 1986a6e0c5daSWarner Losh ada_dsmtrim_req_create(struct ada_softc *softc, struct bio *bp, struct trim_request *req) 198752c9ce25SScott Long { 1988ea657f2cSWarner Losh uint64_t lastlba = (uint64_t)-1, lbas = 0; 19892030b294SAlexander Motin int c, lastcount = 0, off, ranges = 0; 199052c9ce25SScott Long 19911c80ec0aSAlexander Motin bzero(req, sizeof(*req)); 19922030b294SAlexander Motin TAILQ_INIT(&req->bps); 19931c80ec0aSAlexander Motin do { 19947ddad071SWarner Losh uint64_t lba = bp->bio_pblkno; 19957ddad071SWarner Losh int count = bp->bio_bcount / softc->params.secsize; 19961c80ec0aSAlexander Motin 199737ddbd16SAlexander Motin /* Try to extend the previous range. */ 199837ddbd16SAlexander Motin if (lba == lastlba) { 1999c213c551SSteven Hartland c = min(count, ATA_DSM_RANGE_MAX - lastcount); 200037ddbd16SAlexander Motin lastcount += c; 2001c213c551SSteven Hartland off = (ranges - 1) * ATA_DSM_RANGE_SIZE; 200237ddbd16SAlexander Motin req->data[off + 6] = lastcount & 0xff; 200337ddbd16SAlexander Motin req->data[off + 7] = 200437ddbd16SAlexander Motin (lastcount >> 8) & 0xff; 200537ddbd16SAlexander Motin count -= c; 200637ddbd16SAlexander Motin lba += c; 2007ea657f2cSWarner Losh lbas += c; 200837ddbd16SAlexander Motin } 200937ddbd16SAlexander Motin 201037ddbd16SAlexander Motin while (count > 0) { 2011c213c551SSteven Hartland c = min(count, ATA_DSM_RANGE_MAX); 2012c213c551SSteven Hartland off = ranges * ATA_DSM_RANGE_SIZE; 20131c80ec0aSAlexander Motin req->data[off + 0] = lba & 0xff; 20141c80ec0aSAlexander Motin req->data[off + 1] = (lba >> 8) & 0xff; 20151c80ec0aSAlexander Motin req->data[off + 2] = (lba >> 16) & 0xff; 20161c80ec0aSAlexander Motin req->data[off + 3] = (lba >> 24) & 0xff; 20171c80ec0aSAlexander Motin req->data[off + 4] = (lba >> 32) & 0xff; 20181c80ec0aSAlexander Motin req->data[off + 5] = (lba >> 40) & 0xff; 20191c80ec0aSAlexander Motin req->data[off + 6] = c & 0xff; 20201c80ec0aSAlexander Motin req->data[off + 7] = (c >> 8) & 0xff; 20211c80ec0aSAlexander Motin lba += c; 2022ea657f2cSWarner Losh lbas += c; 20231c80ec0aSAlexander Motin count -= c; 202437ddbd16SAlexander Motin lastcount = c; 20251c80ec0aSAlexander Motin ranges++; 2026c213c551SSteven Hartland /* 2027c213c551SSteven Hartland * Its the caller's responsibility to ensure the 2028c213c551SSteven Hartland * request will fit so we don't need to check for 2029c213c551SSteven Hartland * overrun here 2030c213c551SSteven Hartland */ 20311c80ec0aSAlexander Motin } 203237ddbd16SAlexander Motin lastlba = lba; 20337ddad071SWarner Losh TAILQ_INSERT_TAIL(&req->bps, bp, bio_queue); 2034a6e0c5daSWarner Losh 2035a6e0c5daSWarner Losh bp = cam_iosched_next_trim(softc->cam_iosched); 2036a6e0c5daSWarner Losh if (bp == NULL) 20371c80ec0aSAlexander Motin break; 2038a6e0c5daSWarner Losh if (bp->bio_bcount / softc->params.secsize > 2039a6e0c5daSWarner Losh (softc->trim_max_ranges - ranges) * ATA_DSM_RANGE_MAX) { 2040a6e0c5daSWarner Losh cam_iosched_put_back_trim(softc->cam_iosched, bp); 2041a6e0c5daSWarner Losh break; 2042a6e0c5daSWarner Losh } 20431c80ec0aSAlexander Motin } while (1); 2044ea657f2cSWarner Losh softc->trim_count++; 2045ea657f2cSWarner Losh softc->trim_ranges += ranges; 2046ea657f2cSWarner Losh softc->trim_lbas += lbas; 2047a6e0c5daSWarner Losh 2048a6e0c5daSWarner Losh return (ranges); 2049a6e0c5daSWarner Losh } 2050a6e0c5daSWarner Losh 2051a6e0c5daSWarner Losh static void 2052a6e0c5daSWarner Losh ada_dsmtrim(struct ada_softc *softc, struct bio *bp, struct ccb_ataio *ataio) 2053a6e0c5daSWarner Losh { 2054a6e0c5daSWarner Losh struct trim_request *req = &softc->trim_req; 2055a6e0c5daSWarner Losh int ranges; 2056a6e0c5daSWarner Losh 2057a6e0c5daSWarner Losh ranges = ada_dsmtrim_req_create(softc, bp, req); 20581c80ec0aSAlexander Motin cam_fill_ataio(ataio, 20591c80ec0aSAlexander Motin ada_retry_count, 20601c80ec0aSAlexander Motin adadone, 20611c80ec0aSAlexander Motin CAM_DIR_OUT, 20621c80ec0aSAlexander Motin 0, 20631c80ec0aSAlexander Motin req->data, 206455e0987aSPedro F. Giffuni howmany(ranges, ATA_DSM_BLK_RANGES) * ATA_DSM_BLK_SIZE, 20651c80ec0aSAlexander Motin ada_default_timeout * 1000); 20661c80ec0aSAlexander Motin ata_48bit_cmd(ataio, ATA_DATA_SET_MANAGEMENT, 206755e0987aSPedro F. Giffuni ATA_DSM_TRIM, 0, howmany(ranges, ATA_DSM_BLK_RANGES)); 20687ddad071SWarner Losh } 20697ddad071SWarner Losh 20707ddad071SWarner Losh static void 2071a6e0c5daSWarner Losh ada_ncq_dsmtrim(struct ada_softc *softc, struct bio *bp, struct ccb_ataio *ataio) 2072a6e0c5daSWarner Losh { 2073a6e0c5daSWarner Losh struct trim_request *req = &softc->trim_req; 2074a6e0c5daSWarner Losh int ranges; 2075a6e0c5daSWarner Losh 2076a6e0c5daSWarner Losh ranges = ada_dsmtrim_req_create(softc, bp, req); 2077a6e0c5daSWarner Losh cam_fill_ataio(ataio, 2078a6e0c5daSWarner Losh ada_retry_count, 2079a6e0c5daSWarner Losh adadone, 2080a6e0c5daSWarner Losh CAM_DIR_OUT, 2081a6e0c5daSWarner Losh 0, 2082a6e0c5daSWarner Losh req->data, 208355e0987aSPedro F. Giffuni howmany(ranges, ATA_DSM_BLK_RANGES) * ATA_DSM_BLK_SIZE, 2084a6e0c5daSWarner Losh ada_default_timeout * 1000); 2085a6e0c5daSWarner Losh ata_ncq_cmd(ataio, 2086a6e0c5daSWarner Losh ATA_SEND_FPDMA_QUEUED, 2087a6e0c5daSWarner Losh 0, 208855e0987aSPedro F. Giffuni howmany(ranges, ATA_DSM_BLK_RANGES)); 2089a6e0c5daSWarner Losh ataio->cmd.sector_count_exp = ATA_SFPDMA_DSM; 2090916d57dfSWarner Losh ataio->ata_flags |= ATA_FLAG_AUX; 2091916d57dfSWarner Losh ataio->aux = 1; 2092a6e0c5daSWarner Losh } 2093a6e0c5daSWarner Losh 2094a6e0c5daSWarner Losh static void 20957ddad071SWarner Losh ada_cfaerase(struct ada_softc *softc, struct bio *bp, struct ccb_ataio *ataio) 20967ddad071SWarner Losh { 2097467298f5SSteven Hartland struct trim_request *req = &softc->trim_req; 20987ddad071SWarner Losh uint64_t lba = bp->bio_pblkno; 20997ddad071SWarner Losh uint16_t count = bp->bio_bcount / softc->params.secsize; 21007ddad071SWarner Losh 2101467298f5SSteven Hartland bzero(req, sizeof(*req)); 2102467298f5SSteven Hartland TAILQ_INIT(&req->bps); 2103467298f5SSteven Hartland TAILQ_INSERT_TAIL(&req->bps, bp, bio_queue); 2104467298f5SSteven Hartland 21057ddad071SWarner Losh cam_fill_ataio(ataio, 21067ddad071SWarner Losh ada_retry_count, 21077ddad071SWarner Losh adadone, 21087ddad071SWarner Losh CAM_DIR_NONE, 21097ddad071SWarner Losh 0, 21107ddad071SWarner Losh NULL, 21117ddad071SWarner Losh 0, 21127ddad071SWarner Losh ada_default_timeout*1000); 21137ddad071SWarner Losh 21147ddad071SWarner Losh if (count >= 256) 21157ddad071SWarner Losh count = 0; 21167ddad071SWarner Losh ata_28bit_cmd(ataio, ATA_CFA_ERASE, 0, lba, count); 21177ddad071SWarner Losh } 21187ddad071SWarner Losh 21199a6844d5SKenneth D. Merry static int 21209a6844d5SKenneth D. Merry ada_zone_bio_to_ata(int disk_zone_cmd) 21219a6844d5SKenneth D. Merry { 21229a6844d5SKenneth D. Merry switch (disk_zone_cmd) { 21239a6844d5SKenneth D. Merry case DISK_ZONE_OPEN: 21249a6844d5SKenneth D. Merry return ATA_ZM_OPEN_ZONE; 21259a6844d5SKenneth D. Merry case DISK_ZONE_CLOSE: 21269a6844d5SKenneth D. Merry return ATA_ZM_CLOSE_ZONE; 21279a6844d5SKenneth D. Merry case DISK_ZONE_FINISH: 21289a6844d5SKenneth D. Merry return ATA_ZM_FINISH_ZONE; 21299a6844d5SKenneth D. Merry case DISK_ZONE_RWP: 21309a6844d5SKenneth D. Merry return ATA_ZM_RWP; 21319a6844d5SKenneth D. Merry } 21329a6844d5SKenneth D. Merry 21339a6844d5SKenneth D. Merry return -1; 21349a6844d5SKenneth D. Merry } 21359a6844d5SKenneth D. Merry 21369a6844d5SKenneth D. Merry static int 21379a6844d5SKenneth D. Merry ada_zone_cmd(struct cam_periph *periph, union ccb *ccb, struct bio *bp, 21389a6844d5SKenneth D. Merry int *queue_ccb) 21399a6844d5SKenneth D. Merry { 21409a6844d5SKenneth D. Merry struct ada_softc *softc; 21419a6844d5SKenneth D. Merry int error; 21429a6844d5SKenneth D. Merry 21439a6844d5SKenneth D. Merry error = 0; 21449a6844d5SKenneth D. Merry 21459a6844d5SKenneth D. Merry if (bp->bio_cmd != BIO_ZONE) { 21469a6844d5SKenneth D. Merry error = EINVAL; 21479a6844d5SKenneth D. Merry goto bailout; 21489a6844d5SKenneth D. Merry } 21499a6844d5SKenneth D. Merry 21509a6844d5SKenneth D. Merry softc = periph->softc; 21519a6844d5SKenneth D. Merry 21529a6844d5SKenneth D. Merry switch (bp->bio_zone.zone_cmd) { 21539a6844d5SKenneth D. Merry case DISK_ZONE_OPEN: 21549a6844d5SKenneth D. Merry case DISK_ZONE_CLOSE: 21559a6844d5SKenneth D. Merry case DISK_ZONE_FINISH: 21569a6844d5SKenneth D. Merry case DISK_ZONE_RWP: { 21579a6844d5SKenneth D. Merry int zone_flags; 21589a6844d5SKenneth D. Merry int zone_sa; 21599a6844d5SKenneth D. Merry uint64_t lba; 21609a6844d5SKenneth D. Merry 21619a6844d5SKenneth D. Merry zone_sa = ada_zone_bio_to_ata(bp->bio_zone.zone_cmd); 21629a6844d5SKenneth D. Merry if (zone_sa == -1) { 21639a6844d5SKenneth D. Merry xpt_print(periph->path, "Cannot translate zone " 21649a6844d5SKenneth D. Merry "cmd %#x to ATA\n", bp->bio_zone.zone_cmd); 21659a6844d5SKenneth D. Merry error = EINVAL; 21669a6844d5SKenneth D. Merry goto bailout; 21679a6844d5SKenneth D. Merry } 21689a6844d5SKenneth D. Merry 21699a6844d5SKenneth D. Merry zone_flags = 0; 21709a6844d5SKenneth D. Merry lba = bp->bio_zone.zone_params.rwp.id; 21719a6844d5SKenneth D. Merry 21729a6844d5SKenneth D. Merry if (bp->bio_zone.zone_params.rwp.flags & 21739a6844d5SKenneth D. Merry DISK_ZONE_RWP_FLAG_ALL) 21749a6844d5SKenneth D. Merry zone_flags |= ZBC_OUT_ALL; 21759a6844d5SKenneth D. Merry 21769a6844d5SKenneth D. Merry ata_zac_mgmt_out(&ccb->ataio, 21779a6844d5SKenneth D. Merry /*retries*/ ada_retry_count, 21789a6844d5SKenneth D. Merry /*cbfcnp*/ adadone, 21799a6844d5SKenneth D. Merry /*use_ncq*/ (softc->flags & 21809a6844d5SKenneth D. Merry ADA_FLAG_PIM_ATA_EXT) ? 1 : 0, 21819a6844d5SKenneth D. Merry /*zm_action*/ zone_sa, 21829a6844d5SKenneth D. Merry /*zone_id*/ lba, 21839a6844d5SKenneth D. Merry /*zone_flags*/ zone_flags, 21849a6844d5SKenneth D. Merry /*sector_count*/ 0, 21859a6844d5SKenneth D. Merry /*data_ptr*/ NULL, 21869a6844d5SKenneth D. Merry /*dxfer_len*/ 0, 21879a6844d5SKenneth D. Merry /*timeout*/ ada_default_timeout * 1000); 21889a6844d5SKenneth D. Merry *queue_ccb = 1; 21899a6844d5SKenneth D. Merry 21909a6844d5SKenneth D. Merry break; 21919a6844d5SKenneth D. Merry } 21929a6844d5SKenneth D. Merry case DISK_ZONE_REPORT_ZONES: { 21939a6844d5SKenneth D. Merry uint8_t *rz_ptr; 21949a6844d5SKenneth D. Merry uint32_t num_entries, alloc_size; 21959a6844d5SKenneth D. Merry struct disk_zone_report *rep; 21969a6844d5SKenneth D. Merry 21979a6844d5SKenneth D. Merry rep = &bp->bio_zone.zone_params.report; 21989a6844d5SKenneth D. Merry 21999a6844d5SKenneth D. Merry num_entries = rep->entries_allocated; 22009a6844d5SKenneth D. Merry if (num_entries == 0) { 22019a6844d5SKenneth D. Merry xpt_print(periph->path, "No entries allocated for " 22029a6844d5SKenneth D. Merry "Report Zones request\n"); 22039a6844d5SKenneth D. Merry error = EINVAL; 22049a6844d5SKenneth D. Merry goto bailout; 22059a6844d5SKenneth D. Merry } 22069a6844d5SKenneth D. Merry alloc_size = sizeof(struct scsi_report_zones_hdr) + 22079a6844d5SKenneth D. Merry (sizeof(struct scsi_report_zones_desc) * num_entries); 22089a6844d5SKenneth D. Merry alloc_size = min(alloc_size, softc->disk->d_maxsize); 22099a6844d5SKenneth D. Merry rz_ptr = malloc(alloc_size, M_ATADA, M_NOWAIT | M_ZERO); 22109a6844d5SKenneth D. Merry if (rz_ptr == NULL) { 22119a6844d5SKenneth D. Merry xpt_print(periph->path, "Unable to allocate memory " 22129a6844d5SKenneth D. Merry "for Report Zones request\n"); 22139a6844d5SKenneth D. Merry error = ENOMEM; 22149a6844d5SKenneth D. Merry goto bailout; 22159a6844d5SKenneth D. Merry } 22169a6844d5SKenneth D. Merry 22179a6844d5SKenneth D. Merry ata_zac_mgmt_in(&ccb->ataio, 22189a6844d5SKenneth D. Merry /*retries*/ ada_retry_count, 22199a6844d5SKenneth D. Merry /*cbcfnp*/ adadone, 22209a6844d5SKenneth D. Merry /*use_ncq*/ (softc->flags & 22219a6844d5SKenneth D. Merry ADA_FLAG_PIM_ATA_EXT) ? 1 : 0, 22229a6844d5SKenneth D. Merry /*zm_action*/ ATA_ZM_REPORT_ZONES, 22239a6844d5SKenneth D. Merry /*zone_id*/ rep->starting_id, 22249a6844d5SKenneth D. Merry /*zone_flags*/ rep->rep_options, 22259a6844d5SKenneth D. Merry /*data_ptr*/ rz_ptr, 22269a6844d5SKenneth D. Merry /*dxfer_len*/ alloc_size, 22279a6844d5SKenneth D. Merry /*timeout*/ ada_default_timeout * 1000); 22289a6844d5SKenneth D. Merry 22299a6844d5SKenneth D. Merry /* 22309a6844d5SKenneth D. Merry * For BIO_ZONE, this isn't normally needed. However, it 22319a6844d5SKenneth D. Merry * is used by devstat_end_transaction_bio() to determine 22329a6844d5SKenneth D. Merry * how much data was transferred. 22339a6844d5SKenneth D. Merry */ 22349a6844d5SKenneth D. Merry /* 22359a6844d5SKenneth D. Merry * XXX KDM we have a problem. But I'm not sure how to fix 22369a6844d5SKenneth D. Merry * it. devstat uses bio_bcount - bio_resid to calculate 22379a6844d5SKenneth D. Merry * the amount of data transferred. The GEOM disk code 22389a6844d5SKenneth D. Merry * uses bio_length - bio_resid to calculate the amount of 22399a6844d5SKenneth D. Merry * data in bio_completed. We have different structure 22409a6844d5SKenneth D. Merry * sizes above and below the ada(4) driver. So, if we 22419a6844d5SKenneth D. Merry * use the sizes above, the amount transferred won't be 22429a6844d5SKenneth D. Merry * quite accurate for devstat. If we use different sizes 22439a6844d5SKenneth D. Merry * for bio_bcount and bio_length (above and below 22449a6844d5SKenneth D. Merry * respectively), then the residual needs to match one or 22459a6844d5SKenneth D. Merry * the other. Everything is calculated after the bio 22469a6844d5SKenneth D. Merry * leaves the driver, so changing the values around isn't 22479a6844d5SKenneth D. Merry * really an option. For now, just set the count to the 22489a6844d5SKenneth D. Merry * passed in length. This means that the calculations 22499a6844d5SKenneth D. Merry * above (e.g. bio_completed) will be correct, but the 22509a6844d5SKenneth D. Merry * amount of data reported to devstat will be slightly 22519a6844d5SKenneth D. Merry * under or overstated. 22529a6844d5SKenneth D. Merry */ 22539a6844d5SKenneth D. Merry bp->bio_bcount = bp->bio_length; 22549a6844d5SKenneth D. Merry 22559a6844d5SKenneth D. Merry *queue_ccb = 1; 22569a6844d5SKenneth D. Merry 22579a6844d5SKenneth D. Merry break; 22589a6844d5SKenneth D. Merry } 22599a6844d5SKenneth D. Merry case DISK_ZONE_GET_PARAMS: { 22609a6844d5SKenneth D. Merry struct disk_zone_disk_params *params; 22619a6844d5SKenneth D. Merry 22629a6844d5SKenneth D. Merry params = &bp->bio_zone.zone_params.disk_params; 22639a6844d5SKenneth D. Merry bzero(params, sizeof(*params)); 22649a6844d5SKenneth D. Merry 22659a6844d5SKenneth D. Merry switch (softc->zone_mode) { 22669a6844d5SKenneth D. Merry case ADA_ZONE_DRIVE_MANAGED: 22679a6844d5SKenneth D. Merry params->zone_mode = DISK_ZONE_MODE_DRIVE_MANAGED; 22689a6844d5SKenneth D. Merry break; 22699a6844d5SKenneth D. Merry case ADA_ZONE_HOST_AWARE: 22709a6844d5SKenneth D. Merry params->zone_mode = DISK_ZONE_MODE_HOST_AWARE; 22719a6844d5SKenneth D. Merry break; 22729a6844d5SKenneth D. Merry case ADA_ZONE_HOST_MANAGED: 22739a6844d5SKenneth D. Merry params->zone_mode = DISK_ZONE_MODE_HOST_MANAGED; 22749a6844d5SKenneth D. Merry break; 22759a6844d5SKenneth D. Merry default: 22769a6844d5SKenneth D. Merry case ADA_ZONE_NONE: 22779a6844d5SKenneth D. Merry params->zone_mode = DISK_ZONE_MODE_NONE; 22789a6844d5SKenneth D. Merry break; 22799a6844d5SKenneth D. Merry } 22809a6844d5SKenneth D. Merry 22819a6844d5SKenneth D. Merry if (softc->zone_flags & ADA_ZONE_FLAG_URSWRZ) 22829a6844d5SKenneth D. Merry params->flags |= DISK_ZONE_DISK_URSWRZ; 22839a6844d5SKenneth D. Merry 22849a6844d5SKenneth D. Merry if (softc->zone_flags & ADA_ZONE_FLAG_OPT_SEQ_SET) { 22859a6844d5SKenneth D. Merry params->optimal_seq_zones = softc->optimal_seq_zones; 22869a6844d5SKenneth D. Merry params->flags |= DISK_ZONE_OPT_SEQ_SET; 22879a6844d5SKenneth D. Merry } 22889a6844d5SKenneth D. Merry 22899a6844d5SKenneth D. Merry if (softc->zone_flags & ADA_ZONE_FLAG_OPT_NONSEQ_SET) { 22909a6844d5SKenneth D. Merry params->optimal_nonseq_zones = 22919a6844d5SKenneth D. Merry softc->optimal_nonseq_zones; 22929a6844d5SKenneth D. Merry params->flags |= DISK_ZONE_OPT_NONSEQ_SET; 22939a6844d5SKenneth D. Merry } 22949a6844d5SKenneth D. Merry 22959a6844d5SKenneth D. Merry if (softc->zone_flags & ADA_ZONE_FLAG_MAX_SEQ_SET) { 22969a6844d5SKenneth D. Merry params->max_seq_zones = softc->max_seq_zones; 22979a6844d5SKenneth D. Merry params->flags |= DISK_ZONE_MAX_SEQ_SET; 22989a6844d5SKenneth D. Merry } 22999a6844d5SKenneth D. Merry if (softc->zone_flags & ADA_ZONE_FLAG_RZ_SUP) 23009a6844d5SKenneth D. Merry params->flags |= DISK_ZONE_RZ_SUP; 23019a6844d5SKenneth D. Merry 23029a6844d5SKenneth D. Merry if (softc->zone_flags & ADA_ZONE_FLAG_OPEN_SUP) 23039a6844d5SKenneth D. Merry params->flags |= DISK_ZONE_OPEN_SUP; 23049a6844d5SKenneth D. Merry 23059a6844d5SKenneth D. Merry if (softc->zone_flags & ADA_ZONE_FLAG_CLOSE_SUP) 23069a6844d5SKenneth D. Merry params->flags |= DISK_ZONE_CLOSE_SUP; 23079a6844d5SKenneth D. Merry 23089a6844d5SKenneth D. Merry if (softc->zone_flags & ADA_ZONE_FLAG_FINISH_SUP) 23099a6844d5SKenneth D. Merry params->flags |= DISK_ZONE_FINISH_SUP; 23109a6844d5SKenneth D. Merry 23119a6844d5SKenneth D. Merry if (softc->zone_flags & ADA_ZONE_FLAG_RWP_SUP) 23129a6844d5SKenneth D. Merry params->flags |= DISK_ZONE_RWP_SUP; 23139a6844d5SKenneth D. Merry break; 23149a6844d5SKenneth D. Merry } 23159a6844d5SKenneth D. Merry default: 23169a6844d5SKenneth D. Merry break; 23179a6844d5SKenneth D. Merry } 23189a6844d5SKenneth D. Merry bailout: 23199a6844d5SKenneth D. Merry return (error); 23209a6844d5SKenneth D. Merry } 23219a6844d5SKenneth D. Merry 23227ddad071SWarner Losh static void 23237ddad071SWarner Losh adastart(struct cam_periph *periph, union ccb *start_ccb) 23247ddad071SWarner Losh { 23257ddad071SWarner Losh struct ada_softc *softc = (struct ada_softc *)periph->softc; 23267ddad071SWarner Losh struct ccb_ataio *ataio = &start_ccb->ataio; 23277ddad071SWarner Losh 23287ddad071SWarner Losh CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("adastart\n")); 23297ddad071SWarner Losh 23307ddad071SWarner Losh switch (softc->state) { 23317ddad071SWarner Losh case ADA_STATE_NORMAL: 23327ddad071SWarner Losh { 23337ddad071SWarner Losh struct bio *bp; 23347ddad071SWarner Losh u_int8_t tag_code; 23357ddad071SWarner Losh 2336a6e0c5daSWarner Losh bp = cam_iosched_next_bio(softc->cam_iosched); 23371c80ec0aSAlexander Motin if (bp == NULL) { 23381c80ec0aSAlexander Motin xpt_release_ccb(start_ccb); 23391c80ec0aSAlexander Motin break; 23401c80ec0aSAlexander Motin } 234152c9ce25SScott Long 2342a6e0c5daSWarner Losh if ((bp->bio_flags & BIO_ORDERED) != 0 || 2343a6e0c5daSWarner Losh (bp->bio_cmd != BIO_DELETE && (softc->flags & ADA_FLAG_NEED_OTAG) != 0)) { 234452c9ce25SScott Long softc->flags &= ~ADA_FLAG_NEED_OTAG; 2345030844d1SAlexander Motin softc->flags |= ADA_FLAG_WAS_OTAG; 234646f118feSAlexander Motin tag_code = 0; 234752c9ce25SScott Long } else { 234846f118feSAlexander Motin tag_code = 1; 234952c9ce25SScott Long } 235052c9ce25SScott Long switch (bp->bio_cmd) { 235152c9ce25SScott Long case BIO_WRITE: 235269114bc0SAlexander Motin case BIO_READ: 235352c9ce25SScott Long { 235452c9ce25SScott Long uint64_t lba = bp->bio_pblkno; 235552c9ce25SScott Long uint16_t count = bp->bio_bcount / softc->params.secsize; 2356a9934668SKenneth D. Merry void *data_ptr; 2357a9934668SKenneth D. Merry int rw_op; 2358a9934668SKenneth D. Merry 2359a9934668SKenneth D. Merry if (bp->bio_cmd == BIO_WRITE) { 2360a9934668SKenneth D. Merry softc->flags |= ADA_FLAG_DIRTY; 2361a9934668SKenneth D. Merry rw_op = CAM_DIR_OUT; 2362a9934668SKenneth D. Merry } else { 2363a9934668SKenneth D. Merry rw_op = CAM_DIR_IN; 2364a9934668SKenneth D. Merry } 2365a9934668SKenneth D. Merry 2366a9934668SKenneth D. Merry data_ptr = bp->bio_data; 2367a9934668SKenneth D. Merry if ((bp->bio_flags & (BIO_UNMAPPED|BIO_VLIST)) != 0) { 2368a9934668SKenneth D. Merry rw_op |= CAM_DATA_BIO; 2369a9934668SKenneth D. Merry data_ptr = bp; 2370a9934668SKenneth D. Merry } 2371a9934668SKenneth D. Merry 2372d38677d2SWarner Losh #ifdef CAM_TEST_FAILURE 2373e3a6d3a4SAlexander Motin int fail = 0; 237452c9ce25SScott Long 2375e3a6d3a4SAlexander Motin /* 2376e3a6d3a4SAlexander Motin * Support the failure ioctls. If the command is a 2377e3a6d3a4SAlexander Motin * read, and there are pending forced read errors, or 2378e3a6d3a4SAlexander Motin * if a write and pending write errors, then fail this 2379e3a6d3a4SAlexander Motin * operation with EIO. This is useful for testing 2380e3a6d3a4SAlexander Motin * purposes. Also, support having every Nth read fail. 2381e3a6d3a4SAlexander Motin * 2382e3a6d3a4SAlexander Motin * This is a rather blunt tool. 2383e3a6d3a4SAlexander Motin */ 2384e3a6d3a4SAlexander Motin if (bp->bio_cmd == BIO_READ) { 2385e3a6d3a4SAlexander Motin if (softc->force_read_error) { 2386e3a6d3a4SAlexander Motin softc->force_read_error--; 2387e3a6d3a4SAlexander Motin fail = 1; 2388e3a6d3a4SAlexander Motin } 2389e3a6d3a4SAlexander Motin if (softc->periodic_read_error > 0) { 2390e3a6d3a4SAlexander Motin if (++softc->periodic_read_count >= 2391e3a6d3a4SAlexander Motin softc->periodic_read_error) { 2392e3a6d3a4SAlexander Motin softc->periodic_read_count = 0; 2393e3a6d3a4SAlexander Motin fail = 1; 2394e3a6d3a4SAlexander Motin } 2395e3a6d3a4SAlexander Motin } 2396e3a6d3a4SAlexander Motin } else { 2397e3a6d3a4SAlexander Motin if (softc->force_write_error) { 2398e3a6d3a4SAlexander Motin softc->force_write_error--; 2399e3a6d3a4SAlexander Motin fail = 1; 2400e3a6d3a4SAlexander Motin } 2401e3a6d3a4SAlexander Motin } 2402e3a6d3a4SAlexander Motin if (fail) { 24034beec135SAlexander Motin biofinish(bp, NULL, EIO); 2404e3a6d3a4SAlexander Motin xpt_release_ccb(start_ccb); 2405e3a6d3a4SAlexander Motin adaschedule(periph); 2406e3a6d3a4SAlexander Motin return; 2407e3a6d3a4SAlexander Motin } 2408e3a6d3a4SAlexander Motin #endif 2409abc1e60eSKonstantin Belousov KASSERT((bp->bio_flags & BIO_UNMAPPED) == 0 || 2410abc1e60eSKonstantin Belousov round_page(bp->bio_bcount + bp->bio_ma_offset) / 2411abc1e60eSKonstantin Belousov PAGE_SIZE == bp->bio_ma_n, 2412abc1e60eSKonstantin Belousov ("Short bio %p", bp)); 241352c9ce25SScott Long cam_fill_ataio(ataio, 241452c9ce25SScott Long ada_retry_count, 241552c9ce25SScott Long adadone, 2416a9934668SKenneth D. Merry rw_op, 2417e4cc6558SWarner Losh 0, 2418a9934668SKenneth D. Merry data_ptr, 241952c9ce25SScott Long bp->bio_bcount, 242052c9ce25SScott Long ada_default_timeout*1000); 242152c9ce25SScott Long 242246f118feSAlexander Motin if ((softc->flags & ADA_FLAG_CAN_NCQ) && tag_code) { 242352c9ce25SScott Long if (bp->bio_cmd == BIO_READ) { 242452c9ce25SScott Long ata_ncq_cmd(ataio, ATA_READ_FPDMA_QUEUED, 242552c9ce25SScott Long lba, count); 242652c9ce25SScott Long } else { 242752c9ce25SScott Long ata_ncq_cmd(ataio, ATA_WRITE_FPDMA_QUEUED, 242852c9ce25SScott Long lba, count); 242952c9ce25SScott Long } 243052c9ce25SScott Long } else if ((softc->flags & ADA_FLAG_CAN_48BIT) && 243152c9ce25SScott Long (lba + count >= ATA_MAX_28BIT_LBA || 243246f118feSAlexander Motin count > 256)) { 24332e1eb332SMarius Strobl if (softc->flags & ADA_FLAG_CAN_DMA48) { 243452c9ce25SScott Long if (bp->bio_cmd == BIO_READ) { 243552c9ce25SScott Long ata_48bit_cmd(ataio, ATA_READ_DMA48, 243652c9ce25SScott Long 0, lba, count); 243752c9ce25SScott Long } else { 243852c9ce25SScott Long ata_48bit_cmd(ataio, ATA_WRITE_DMA48, 243952c9ce25SScott Long 0, lba, count); 244052c9ce25SScott Long } 244152c9ce25SScott Long } else { 244252c9ce25SScott Long if (bp->bio_cmd == BIO_READ) { 244346f118feSAlexander Motin ata_48bit_cmd(ataio, ATA_READ_MUL48, 244446f118feSAlexander Motin 0, lba, count); 244546f118feSAlexander Motin } else { 244646f118feSAlexander Motin ata_48bit_cmd(ataio, ATA_WRITE_MUL48, 244746f118feSAlexander Motin 0, lba, count); 244846f118feSAlexander Motin } 244946f118feSAlexander Motin } 245046f118feSAlexander Motin } else { 245146f118feSAlexander Motin if (count == 256) 245246f118feSAlexander Motin count = 0; 245346f118feSAlexander Motin if (softc->flags & ADA_FLAG_CAN_DMA) { 245446f118feSAlexander Motin if (bp->bio_cmd == BIO_READ) { 24557606b445SAlexander Motin ata_28bit_cmd(ataio, ATA_READ_DMA, 245652c9ce25SScott Long 0, lba, count); 245752c9ce25SScott Long } else { 24587606b445SAlexander Motin ata_28bit_cmd(ataio, ATA_WRITE_DMA, 245952c9ce25SScott Long 0, lba, count); 246052c9ce25SScott Long } 246146f118feSAlexander Motin } else { 246246f118feSAlexander Motin if (bp->bio_cmd == BIO_READ) { 246346f118feSAlexander Motin ata_28bit_cmd(ataio, ATA_READ_MUL, 246446f118feSAlexander Motin 0, lba, count); 246546f118feSAlexander Motin } else { 246646f118feSAlexander Motin ata_28bit_cmd(ataio, ATA_WRITE_MUL, 246746f118feSAlexander Motin 0, lba, count); 246846f118feSAlexander Motin } 246946f118feSAlexander Motin } 247052c9ce25SScott Long } 247152c9ce25SScott Long break; 24721c80ec0aSAlexander Motin } 2473a6e0c5daSWarner Losh case BIO_DELETE: 2474a6e0c5daSWarner Losh switch (softc->delete_method) { 2475a6e0c5daSWarner Losh case ADA_DELETE_NCQ_DSM_TRIM: 2476a6e0c5daSWarner Losh ada_ncq_dsmtrim(softc, bp, ataio); 2477a6e0c5daSWarner Losh break; 2478a6e0c5daSWarner Losh case ADA_DELETE_DSM_TRIM: 2479a6e0c5daSWarner Losh ada_dsmtrim(softc, bp, ataio); 2480a6e0c5daSWarner Losh break; 2481a6e0c5daSWarner Losh case ADA_DELETE_CFA_ERASE: 2482a6e0c5daSWarner Losh ada_cfaerase(softc, bp, ataio); 2483a6e0c5daSWarner Losh break; 2484a6e0c5daSWarner Losh default: 2485a6e0c5daSWarner Losh biofinish(bp, NULL, EOPNOTSUPP); 2486a6e0c5daSWarner Losh xpt_release_ccb(start_ccb); 2487a6e0c5daSWarner Losh adaschedule(periph); 2488a6e0c5daSWarner Losh return; 2489a6e0c5daSWarner Losh } 2490a6e0c5daSWarner Losh start_ccb->ccb_h.ccb_state = ADA_CCB_TRIM; 2491a6e0c5daSWarner Losh start_ccb->ccb_h.flags |= CAM_UNLOCKED; 2492a6e0c5daSWarner Losh cam_iosched_submit_trim(softc->cam_iosched); 2493a6e0c5daSWarner Losh goto out; 249452c9ce25SScott Long case BIO_FLUSH: 249552c9ce25SScott Long cam_fill_ataio(ataio, 249652c9ce25SScott Long 1, 249752c9ce25SScott Long adadone, 249852c9ce25SScott Long CAM_DIR_NONE, 249946f118feSAlexander Motin 0, 250052c9ce25SScott Long NULL, 250152c9ce25SScott Long 0, 250252c9ce25SScott Long ada_default_timeout*1000); 250352c9ce25SScott Long 250452c9ce25SScott Long if (softc->flags & ADA_FLAG_CAN_48BIT) 250552c9ce25SScott Long ata_48bit_cmd(ataio, ATA_FLUSHCACHE48, 0, 0, 0); 250652c9ce25SScott Long else 25077606b445SAlexander Motin ata_28bit_cmd(ataio, ATA_FLUSHCACHE, 0, 0, 0); 250852c9ce25SScott Long break; 25099a6844d5SKenneth D. Merry case BIO_ZONE: { 25109a6844d5SKenneth D. Merry int error, queue_ccb; 25119a6844d5SKenneth D. Merry 25129a6844d5SKenneth D. Merry queue_ccb = 0; 25139a6844d5SKenneth D. Merry 25149a6844d5SKenneth D. Merry error = ada_zone_cmd(periph, start_ccb, bp, &queue_ccb); 25159a6844d5SKenneth D. Merry if ((error != 0) 25169a6844d5SKenneth D. Merry || (queue_ccb == 0)) { 25179a6844d5SKenneth D. Merry biofinish(bp, NULL, error); 25189a6844d5SKenneth D. Merry xpt_release_ccb(start_ccb); 25199a6844d5SKenneth D. Merry return; 25209a6844d5SKenneth D. Merry } 25219a6844d5SKenneth D. Merry break; 25229a6844d5SKenneth D. Merry } 2523d176b803SScott Long default: 2524d176b803SScott Long biofinish(bp, NULL, EOPNOTSUPP); 2525d176b803SScott Long xpt_release_ccb(start_ccb); 2526d176b803SScott Long return; 252752c9ce25SScott Long } 252852c9ce25SScott Long start_ccb->ccb_h.ccb_state = ADA_CCB_BUFFER_IO; 2529227d67aaSAlexander Motin start_ccb->ccb_h.flags |= CAM_UNLOCKED; 25301c80ec0aSAlexander Motin out: 253152c9ce25SScott Long start_ccb->ccb_h.ccb_bp = bp; 2532c1bd46c2SAlexander Motin softc->outstanding_cmds++; 2533227d67aaSAlexander Motin softc->refcount++; 2534227d67aaSAlexander Motin cam_periph_unlock(periph); 253552c9ce25SScott Long xpt_action(start_ccb); 2536227d67aaSAlexander Motin cam_periph_lock(periph); 253752c9ce25SScott Long 25381c80ec0aSAlexander Motin /* May have more work to do, so ensure we stay scheduled */ 25391c80ec0aSAlexander Motin adaschedule(periph); 254052c9ce25SScott Long break; 254152c9ce25SScott Long } 25421ed6aaf9SAlexander Motin case ADA_STATE_RAHEAD: 2543f513d14cSAlexander Motin case ADA_STATE_WCACHE: 2544f513d14cSAlexander Motin { 2545f513d14cSAlexander Motin cam_fill_ataio(ataio, 2546f513d14cSAlexander Motin 1, 2547f513d14cSAlexander Motin adadone, 2548f513d14cSAlexander Motin CAM_DIR_NONE, 2549f513d14cSAlexander Motin 0, 2550f513d14cSAlexander Motin NULL, 2551f513d14cSAlexander Motin 0, 2552f513d14cSAlexander Motin ada_default_timeout*1000); 2553f513d14cSAlexander Motin 25541ed6aaf9SAlexander Motin if (softc->state == ADA_STATE_RAHEAD) { 25551ed6aaf9SAlexander Motin ata_28bit_cmd(ataio, ATA_SETFEATURES, ADA_RA ? 25561ed6aaf9SAlexander Motin ATA_SF_ENAB_RCACHE : ATA_SF_DIS_RCACHE, 0, 0); 25571ed6aaf9SAlexander Motin start_ccb->ccb_h.ccb_state = ADA_CCB_RAHEAD; 25581ed6aaf9SAlexander Motin } else { 25591ed6aaf9SAlexander Motin ata_28bit_cmd(ataio, ATA_SETFEATURES, ADA_WC ? 2560f513d14cSAlexander Motin ATA_SF_ENAB_WCACHE : ATA_SF_DIS_WCACHE, 0, 0); 2561f513d14cSAlexander Motin start_ccb->ccb_h.ccb_state = ADA_CCB_WCACHE; 25621ed6aaf9SAlexander Motin } 2563cccf4220SAlexander Motin start_ccb->ccb_h.flags |= CAM_DEV_QFREEZE; 2564f513d14cSAlexander Motin xpt_action(start_ccb); 2565f513d14cSAlexander Motin break; 2566f513d14cSAlexander Motin } 25679a6844d5SKenneth D. Merry case ADA_STATE_LOGDIR: 25689a6844d5SKenneth D. Merry { 25699a6844d5SKenneth D. Merry struct ata_gp_log_dir *log_dir; 25709a6844d5SKenneth D. Merry 25719a6844d5SKenneth D. Merry if ((softc->flags & ADA_FLAG_CAN_LOG) == 0) { 25729a6844d5SKenneth D. Merry adaprobedone(periph, start_ccb); 25739a6844d5SKenneth D. Merry break; 25749a6844d5SKenneth D. Merry } 25759a6844d5SKenneth D. Merry 25769a6844d5SKenneth D. Merry log_dir = malloc(sizeof(*log_dir), M_ATADA, M_NOWAIT|M_ZERO); 25779a6844d5SKenneth D. Merry if (log_dir == NULL) { 25789a6844d5SKenneth D. Merry xpt_print(periph->path, "Couldn't malloc log_dir " 25799a6844d5SKenneth D. Merry "data\n"); 25809a6844d5SKenneth D. Merry softc->state = ADA_STATE_NORMAL; 25819a6844d5SKenneth D. Merry xpt_release_ccb(start_ccb); 25829a6844d5SKenneth D. Merry break; 25839a6844d5SKenneth D. Merry } 25849a6844d5SKenneth D. Merry 25859a6844d5SKenneth D. Merry ata_read_log(ataio, 25869a6844d5SKenneth D. Merry /*retries*/1, 25879a6844d5SKenneth D. Merry /*cbfcnp*/adadone, 25889a6844d5SKenneth D. Merry /*log_address*/ ATA_LOG_DIRECTORY, 25899a6844d5SKenneth D. Merry /*page_number*/ 0, 25909a6844d5SKenneth D. Merry /*block_count*/ 1, 25919a6844d5SKenneth D. Merry /*protocol*/ softc->flags & ADA_FLAG_CAN_DMA ? 25929a6844d5SKenneth D. Merry CAM_ATAIO_DMA : 0, 25939a6844d5SKenneth D. Merry /*data_ptr*/ (uint8_t *)log_dir, 25949a6844d5SKenneth D. Merry /*dxfer_len*/sizeof(*log_dir), 25959a6844d5SKenneth D. Merry /*timeout*/ada_default_timeout*1000); 25969a6844d5SKenneth D. Merry 25979a6844d5SKenneth D. Merry start_ccb->ccb_h.ccb_state = ADA_CCB_LOGDIR; 25989a6844d5SKenneth D. Merry xpt_action(start_ccb); 25999a6844d5SKenneth D. Merry break; 26009a6844d5SKenneth D. Merry } 26019a6844d5SKenneth D. Merry case ADA_STATE_IDDIR: 26029a6844d5SKenneth D. Merry { 26039a6844d5SKenneth D. Merry struct ata_identify_log_pages *id_dir; 26049a6844d5SKenneth D. Merry 26059a6844d5SKenneth D. Merry id_dir = malloc(sizeof(*id_dir), M_ATADA, M_NOWAIT | M_ZERO); 26069a6844d5SKenneth D. Merry if (id_dir == NULL) { 26079a6844d5SKenneth D. Merry xpt_print(periph->path, "Couldn't malloc id_dir " 26089a6844d5SKenneth D. Merry "data\n"); 26099a6844d5SKenneth D. Merry adaprobedone(periph, start_ccb); 26109a6844d5SKenneth D. Merry break; 26119a6844d5SKenneth D. Merry } 26129a6844d5SKenneth D. Merry 26139a6844d5SKenneth D. Merry ata_read_log(ataio, 26149a6844d5SKenneth D. Merry /*retries*/1, 26159a6844d5SKenneth D. Merry /*cbfcnp*/adadone, 26169a6844d5SKenneth D. Merry /*log_address*/ ATA_IDENTIFY_DATA_LOG, 26179a6844d5SKenneth D. Merry /*page_number*/ ATA_IDL_PAGE_LIST, 26189a6844d5SKenneth D. Merry /*block_count*/ 1, 26199a6844d5SKenneth D. Merry /*protocol*/ softc->flags & ADA_FLAG_CAN_DMA ? 26209a6844d5SKenneth D. Merry CAM_ATAIO_DMA : 0, 26219a6844d5SKenneth D. Merry /*data_ptr*/ (uint8_t *)id_dir, 26229a6844d5SKenneth D. Merry /*dxfer_len*/ sizeof(*id_dir), 26239a6844d5SKenneth D. Merry /*timeout*/ada_default_timeout*1000); 26249a6844d5SKenneth D. Merry 26259a6844d5SKenneth D. Merry start_ccb->ccb_h.ccb_state = ADA_CCB_IDDIR; 26269a6844d5SKenneth D. Merry xpt_action(start_ccb); 26279a6844d5SKenneth D. Merry break; 26289a6844d5SKenneth D. Merry } 26299a6844d5SKenneth D. Merry case ADA_STATE_SUP_CAP: 26309a6844d5SKenneth D. Merry { 26319a6844d5SKenneth D. Merry struct ata_identify_log_sup_cap *sup_cap; 26329a6844d5SKenneth D. Merry 26339a6844d5SKenneth D. Merry sup_cap = malloc(sizeof(*sup_cap), M_ATADA, M_NOWAIT|M_ZERO); 26349a6844d5SKenneth D. Merry if (sup_cap == NULL) { 26359a6844d5SKenneth D. Merry xpt_print(periph->path, "Couldn't malloc sup_cap " 26369a6844d5SKenneth D. Merry "data\n"); 26379a6844d5SKenneth D. Merry adaprobedone(periph, start_ccb); 26389a6844d5SKenneth D. Merry break; 26399a6844d5SKenneth D. Merry } 26409a6844d5SKenneth D. Merry 26419a6844d5SKenneth D. Merry ata_read_log(ataio, 26429a6844d5SKenneth D. Merry /*retries*/1, 26439a6844d5SKenneth D. Merry /*cbfcnp*/adadone, 26449a6844d5SKenneth D. Merry /*log_address*/ ATA_IDENTIFY_DATA_LOG, 26459a6844d5SKenneth D. Merry /*page_number*/ ATA_IDL_SUP_CAP, 26469a6844d5SKenneth D. Merry /*block_count*/ 1, 26479a6844d5SKenneth D. Merry /*protocol*/ softc->flags & ADA_FLAG_CAN_DMA ? 26489a6844d5SKenneth D. Merry CAM_ATAIO_DMA : 0, 26499a6844d5SKenneth D. Merry /*data_ptr*/ (uint8_t *)sup_cap, 26509a6844d5SKenneth D. Merry /*dxfer_len*/ sizeof(*sup_cap), 26519a6844d5SKenneth D. Merry /*timeout*/ada_default_timeout*1000); 26529a6844d5SKenneth D. Merry 26539a6844d5SKenneth D. Merry start_ccb->ccb_h.ccb_state = ADA_CCB_SUP_CAP; 26549a6844d5SKenneth D. Merry xpt_action(start_ccb); 26559a6844d5SKenneth D. Merry break; 26569a6844d5SKenneth D. Merry } 26579a6844d5SKenneth D. Merry case ADA_STATE_ZONE: 26589a6844d5SKenneth D. Merry { 26599a6844d5SKenneth D. Merry struct ata_zoned_info_log *ata_zone; 26609a6844d5SKenneth D. Merry 26619a6844d5SKenneth D. Merry ata_zone = malloc(sizeof(*ata_zone), M_ATADA, M_NOWAIT|M_ZERO); 26629a6844d5SKenneth D. Merry if (ata_zone == NULL) { 26639a6844d5SKenneth D. Merry xpt_print(periph->path, "Couldn't malloc ata_zone " 26649a6844d5SKenneth D. Merry "data\n"); 26659a6844d5SKenneth D. Merry adaprobedone(periph, start_ccb); 26669a6844d5SKenneth D. Merry break; 26679a6844d5SKenneth D. Merry } 26689a6844d5SKenneth D. Merry 26699a6844d5SKenneth D. Merry ata_read_log(ataio, 26709a6844d5SKenneth D. Merry /*retries*/1, 26719a6844d5SKenneth D. Merry /*cbfcnp*/adadone, 26729a6844d5SKenneth D. Merry /*log_address*/ ATA_IDENTIFY_DATA_LOG, 26739a6844d5SKenneth D. Merry /*page_number*/ ATA_IDL_ZDI, 26749a6844d5SKenneth D. Merry /*block_count*/ 1, 26759a6844d5SKenneth D. Merry /*protocol*/ softc->flags & ADA_FLAG_CAN_DMA ? 26769a6844d5SKenneth D. Merry CAM_ATAIO_DMA : 0, 26779a6844d5SKenneth D. Merry /*data_ptr*/ (uint8_t *)ata_zone, 26789a6844d5SKenneth D. Merry /*dxfer_len*/ sizeof(*ata_zone), 26799a6844d5SKenneth D. Merry /*timeout*/ada_default_timeout*1000); 26809a6844d5SKenneth D. Merry 26819a6844d5SKenneth D. Merry start_ccb->ccb_h.ccb_state = ADA_CCB_ZONE; 26829a6844d5SKenneth D. Merry xpt_action(start_ccb); 26839a6844d5SKenneth D. Merry break; 268452c9ce25SScott Long } 268552c9ce25SScott Long } 26869a6844d5SKenneth D. Merry } 26879a6844d5SKenneth D. Merry 26889a6844d5SKenneth D. Merry static void 26899a6844d5SKenneth D. Merry adaprobedone(struct cam_periph *periph, union ccb *ccb) 26909a6844d5SKenneth D. Merry { 26919a6844d5SKenneth D. Merry struct ada_softc *softc; 26929a6844d5SKenneth D. Merry 26939a6844d5SKenneth D. Merry softc = (struct ada_softc *)periph->softc; 26949a6844d5SKenneth D. Merry 26959a6844d5SKenneth D. Merry if (ccb != NULL) 26969a6844d5SKenneth D. Merry xpt_release_ccb(ccb); 26979a6844d5SKenneth D. Merry 26989a6844d5SKenneth D. Merry softc->state = ADA_STATE_NORMAL; 26999a6844d5SKenneth D. Merry softc->flags |= ADA_FLAG_PROBED; 27009a6844d5SKenneth D. Merry adaschedule(periph); 27019a6844d5SKenneth D. Merry if ((softc->flags & ADA_FLAG_ANNOUNCED) == 0) { 27029a6844d5SKenneth D. Merry softc->flags |= ADA_FLAG_ANNOUNCED; 27039a6844d5SKenneth D. Merry cam_periph_unhold(periph); 27049a6844d5SKenneth D. Merry } else { 27059a6844d5SKenneth D. Merry cam_periph_release_locked(periph); 27069a6844d5SKenneth D. Merry } 27079a6844d5SKenneth D. Merry } 27089a6844d5SKenneth D. Merry 27099a6844d5SKenneth D. Merry static void 27109a6844d5SKenneth D. Merry adazonedone(struct cam_periph *periph, union ccb *ccb) 27119a6844d5SKenneth D. Merry { 27129a6844d5SKenneth D. Merry struct bio *bp; 27139a6844d5SKenneth D. Merry 27149a6844d5SKenneth D. Merry bp = (struct bio *)ccb->ccb_h.ccb_bp; 27159a6844d5SKenneth D. Merry 27169a6844d5SKenneth D. Merry switch (bp->bio_zone.zone_cmd) { 27179a6844d5SKenneth D. Merry case DISK_ZONE_OPEN: 27189a6844d5SKenneth D. Merry case DISK_ZONE_CLOSE: 27199a6844d5SKenneth D. Merry case DISK_ZONE_FINISH: 27209a6844d5SKenneth D. Merry case DISK_ZONE_RWP: 27219a6844d5SKenneth D. Merry break; 27229a6844d5SKenneth D. Merry case DISK_ZONE_REPORT_ZONES: { 27239a6844d5SKenneth D. Merry uint32_t avail_len; 27249a6844d5SKenneth D. Merry struct disk_zone_report *rep; 27259a6844d5SKenneth D. Merry struct scsi_report_zones_hdr *hdr; 27269a6844d5SKenneth D. Merry struct scsi_report_zones_desc *desc; 27279a6844d5SKenneth D. Merry struct disk_zone_rep_entry *entry; 2728151ba793SAlexander Kabaev uint32_t hdr_len, num_avail; 27299a6844d5SKenneth D. Merry uint32_t num_to_fill, i; 27309a6844d5SKenneth D. Merry 27319a6844d5SKenneth D. Merry rep = &bp->bio_zone.zone_params.report; 27329a6844d5SKenneth D. Merry avail_len = ccb->ataio.dxfer_len - ccb->ataio.resid; 27339a6844d5SKenneth D. Merry /* 27349a6844d5SKenneth D. Merry * Note that bio_resid isn't normally used for zone 27359a6844d5SKenneth D. Merry * commands, but it is used by devstat_end_transaction_bio() 27369a6844d5SKenneth D. Merry * to determine how much data was transferred. Because 27379a6844d5SKenneth D. Merry * the size of the SCSI/ATA data structures is different 27389a6844d5SKenneth D. Merry * than the size of the BIO interface structures, the 27399a6844d5SKenneth D. Merry * amount of data actually transferred from the drive will 27409a6844d5SKenneth D. Merry * be different than the amount of data transferred to 27419a6844d5SKenneth D. Merry * the user. 27429a6844d5SKenneth D. Merry */ 27439a6844d5SKenneth D. Merry hdr = (struct scsi_report_zones_hdr *)ccb->ataio.data_ptr; 27449a6844d5SKenneth D. Merry if (avail_len < sizeof(*hdr)) { 27459a6844d5SKenneth D. Merry /* 27469a6844d5SKenneth D. Merry * Is there a better error than EIO here? We asked 27479a6844d5SKenneth D. Merry * for at least the header, and we got less than 27489a6844d5SKenneth D. Merry * that. 27499a6844d5SKenneth D. Merry */ 27509a6844d5SKenneth D. Merry bp->bio_error = EIO; 27519a6844d5SKenneth D. Merry bp->bio_flags |= BIO_ERROR; 27529a6844d5SKenneth D. Merry bp->bio_resid = bp->bio_bcount; 27539a6844d5SKenneth D. Merry break; 27549a6844d5SKenneth D. Merry } 27559a6844d5SKenneth D. Merry 27569a6844d5SKenneth D. Merry hdr_len = le32dec(hdr->length); 27579a6844d5SKenneth D. Merry if (hdr_len > 0) 27589a6844d5SKenneth D. Merry rep->entries_available = hdr_len / sizeof(*desc); 27599a6844d5SKenneth D. Merry else 27609a6844d5SKenneth D. Merry rep->entries_available = 0; 27619a6844d5SKenneth D. Merry /* 27629a6844d5SKenneth D. Merry * NOTE: using the same values for the BIO version of the 27639a6844d5SKenneth D. Merry * same field as the SCSI/ATA values. This means we could 27649a6844d5SKenneth D. Merry * get some additional values that aren't defined in bio.h 27659a6844d5SKenneth D. Merry * if more values of the same field are defined later. 27669a6844d5SKenneth D. Merry */ 27679a6844d5SKenneth D. Merry rep->header.same = hdr->byte4 & SRZ_SAME_MASK; 27689a6844d5SKenneth D. Merry rep->header.maximum_lba = le64dec(hdr->maximum_lba); 27699a6844d5SKenneth D. Merry /* 27709a6844d5SKenneth D. Merry * If the drive reports no entries that match the query, 27719a6844d5SKenneth D. Merry * we're done. 27729a6844d5SKenneth D. Merry */ 27739a6844d5SKenneth D. Merry if (hdr_len == 0) { 27749a6844d5SKenneth D. Merry rep->entries_filled = 0; 27759a6844d5SKenneth D. Merry bp->bio_resid = bp->bio_bcount; 27769a6844d5SKenneth D. Merry break; 27779a6844d5SKenneth D. Merry } 27789a6844d5SKenneth D. Merry 27799a6844d5SKenneth D. Merry num_avail = min((avail_len - sizeof(*hdr)) / sizeof(*desc), 27809a6844d5SKenneth D. Merry hdr_len / sizeof(*desc)); 27819a6844d5SKenneth D. Merry /* 27829a6844d5SKenneth D. Merry * If the drive didn't return any data, then we're done. 27839a6844d5SKenneth D. Merry */ 27849a6844d5SKenneth D. Merry if (num_avail == 0) { 27859a6844d5SKenneth D. Merry rep->entries_filled = 0; 27869a6844d5SKenneth D. Merry bp->bio_resid = bp->bio_bcount; 27879a6844d5SKenneth D. Merry break; 27889a6844d5SKenneth D. Merry } 27899a6844d5SKenneth D. Merry 27909a6844d5SKenneth D. Merry num_to_fill = min(num_avail, rep->entries_allocated); 27919a6844d5SKenneth D. Merry /* 27929a6844d5SKenneth D. Merry * If the user didn't allocate any entries for us to fill, 27939a6844d5SKenneth D. Merry * we're done. 27949a6844d5SKenneth D. Merry */ 27959a6844d5SKenneth D. Merry if (num_to_fill == 0) { 27969a6844d5SKenneth D. Merry rep->entries_filled = 0; 27979a6844d5SKenneth D. Merry bp->bio_resid = bp->bio_bcount; 27989a6844d5SKenneth D. Merry break; 27999a6844d5SKenneth D. Merry } 28009a6844d5SKenneth D. Merry 28019a6844d5SKenneth D. Merry for (i = 0, desc = &hdr->desc_list[0], entry=&rep->entries[0]; 28029a6844d5SKenneth D. Merry i < num_to_fill; i++, desc++, entry++) { 28039a6844d5SKenneth D. Merry /* 28049a6844d5SKenneth D. Merry * NOTE: we're mapping the values here directly 28059a6844d5SKenneth D. Merry * from the SCSI/ATA bit definitions to the bio.h 28069a6844d5SKenneth D. Merry * definitions. There is also a warning in 28079a6844d5SKenneth D. Merry * disk_zone.h, but the impact is that if 28089a6844d5SKenneth D. Merry * additional values are added in the SCSI/ATA 28099a6844d5SKenneth D. Merry * specs these will be visible to consumers of 28109a6844d5SKenneth D. Merry * this interface. 28119a6844d5SKenneth D. Merry */ 28129a6844d5SKenneth D. Merry entry->zone_type = desc->zone_type & SRZ_TYPE_MASK; 28139a6844d5SKenneth D. Merry entry->zone_condition = 28149a6844d5SKenneth D. Merry (desc->zone_flags & SRZ_ZONE_COND_MASK) >> 28159a6844d5SKenneth D. Merry SRZ_ZONE_COND_SHIFT; 28169a6844d5SKenneth D. Merry entry->zone_flags |= desc->zone_flags & 28179a6844d5SKenneth D. Merry (SRZ_ZONE_NON_SEQ|SRZ_ZONE_RESET); 28189a6844d5SKenneth D. Merry entry->zone_length = le64dec(desc->zone_length); 28199a6844d5SKenneth D. Merry entry->zone_start_lba = le64dec(desc->zone_start_lba); 28209a6844d5SKenneth D. Merry entry->write_pointer_lba = 28219a6844d5SKenneth D. Merry le64dec(desc->write_pointer_lba); 28229a6844d5SKenneth D. Merry } 28239a6844d5SKenneth D. Merry rep->entries_filled = num_to_fill; 28249a6844d5SKenneth D. Merry /* 28259a6844d5SKenneth D. Merry * Note that this residual is accurate from the user's 28269a6844d5SKenneth D. Merry * standpoint, but the amount transferred isn't accurate 28279a6844d5SKenneth D. Merry * from the standpoint of what actually came back from the 28289a6844d5SKenneth D. Merry * drive. 28299a6844d5SKenneth D. Merry */ 28309a6844d5SKenneth D. Merry bp->bio_resid = bp->bio_bcount - (num_to_fill * sizeof(*entry)); 28319a6844d5SKenneth D. Merry break; 28329a6844d5SKenneth D. Merry } 28339a6844d5SKenneth D. Merry case DISK_ZONE_GET_PARAMS: 28349a6844d5SKenneth D. Merry default: 28359a6844d5SKenneth D. Merry /* 28369a6844d5SKenneth D. Merry * In theory we should not get a GET_PARAMS bio, since it 28379a6844d5SKenneth D. Merry * should be handled without queueing the command to the 28389a6844d5SKenneth D. Merry * drive. 28399a6844d5SKenneth D. Merry */ 28409a6844d5SKenneth D. Merry panic("%s: Invalid zone command %d", __func__, 28419a6844d5SKenneth D. Merry bp->bio_zone.zone_cmd); 28429a6844d5SKenneth D. Merry break; 28439a6844d5SKenneth D. Merry } 28449a6844d5SKenneth D. Merry 28459a6844d5SKenneth D. Merry if (bp->bio_zone.zone_cmd == DISK_ZONE_REPORT_ZONES) 28469a6844d5SKenneth D. Merry free(ccb->ataio.data_ptr, M_ATADA); 28479a6844d5SKenneth D. Merry } 28489a6844d5SKenneth D. Merry 284952c9ce25SScott Long static void 285052c9ce25SScott Long adadone(struct cam_periph *periph, union ccb *done_ccb) 285152c9ce25SScott Long { 285252c9ce25SScott Long struct ada_softc *softc; 285352c9ce25SScott Long struct ccb_ataio *ataio; 2854cccf4220SAlexander Motin struct cam_path *path; 28559a6844d5SKenneth D. Merry uint32_t priority; 28567651b989SAlexander Motin int state; 285752c9ce25SScott Long 285852c9ce25SScott Long softc = (struct ada_softc *)periph->softc; 285952c9ce25SScott Long ataio = &done_ccb->ataio; 2860cccf4220SAlexander Motin path = done_ccb->ccb_h.path; 28619a6844d5SKenneth D. Merry priority = done_ccb->ccb_h.pinfo.priority; 2862fddde2b8SAlexander Motin 2863cccf4220SAlexander Motin CAM_DEBUG(path, CAM_DEBUG_TRACE, ("adadone\n")); 2864fddde2b8SAlexander Motin 28657651b989SAlexander Motin state = ataio->ccb_h.ccb_state & ADA_CCB_TYPE_MASK; 28667651b989SAlexander Motin switch (state) { 286752c9ce25SScott Long case ADA_CCB_BUFFER_IO: 28681c80ec0aSAlexander Motin case ADA_CCB_TRIM: 286952c9ce25SScott Long { 287052c9ce25SScott Long struct bio *bp; 287152c9ce25SScott Long int error; 287252c9ce25SScott Long 2873227d67aaSAlexander Motin cam_periph_lock(periph); 2874a6e0c5daSWarner Losh bp = (struct bio *)done_ccb->ccb_h.ccb_bp; 28757651b989SAlexander Motin if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { 287646f118feSAlexander Motin error = adaerror(done_ccb, 0, 0); 287752c9ce25SScott Long if (error == ERESTART) { 287846f118feSAlexander Motin /* A retry was scheduled, so just return. */ 2879227d67aaSAlexander Motin cam_periph_unlock(periph); 288052c9ce25SScott Long return; 288152c9ce25SScott Long } 288252c9ce25SScott Long if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) 2883cccf4220SAlexander Motin cam_release_devq(path, 288452c9ce25SScott Long /*relsim_flags*/0, 288552c9ce25SScott Long /*reduction*/0, 288652c9ce25SScott Long /*timeout*/0, 288752c9ce25SScott Long /*getcount_only*/0); 2888a6e0c5daSWarner Losh /* 2889a6e0c5daSWarner Losh * If we get an error on an NCQ DSM TRIM, fall back 2890a6e0c5daSWarner Losh * to a non-NCQ DSM TRIM forever. Please note that if 2891a6e0c5daSWarner Losh * CAN_NCQ_TRIM is set, CAN_TRIM is necessarily set too. 2892a6e0c5daSWarner Losh * However, for this one trim, we treat it as advisory 2893a6e0c5daSWarner Losh * and return success up the stack. 2894a6e0c5daSWarner Losh */ 2895a6e0c5daSWarner Losh if (state == ADA_CCB_TRIM && 2896a6e0c5daSWarner Losh error != 0 && 2897a6e0c5daSWarner Losh (softc->flags & ADA_FLAG_CAN_NCQ_TRIM) != 0) { 2898a6e0c5daSWarner Losh softc->flags &= ~ADA_FLAG_CAN_NCQ_TRIM; 2899a6e0c5daSWarner Losh error = 0; 2900a6e0c5daSWarner Losh adasetdeletemethod(softc); 2901a6e0c5daSWarner Losh } 290252c9ce25SScott Long } else { 290352c9ce25SScott Long if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) 290452c9ce25SScott Long panic("REQ_CMP with QFRZN"); 29059a6844d5SKenneth D. Merry 29067651b989SAlexander Motin error = 0; 29077651b989SAlexander Motin } 29087651b989SAlexander Motin bp->bio_error = error; 29097651b989SAlexander Motin if (error != 0) { 29107651b989SAlexander Motin bp->bio_resid = bp->bio_bcount; 29117651b989SAlexander Motin bp->bio_flags |= BIO_ERROR; 29127651b989SAlexander Motin } else { 29139a6844d5SKenneth D. Merry if (bp->bio_cmd == BIO_ZONE) 29149a6844d5SKenneth D. Merry adazonedone(periph, done_ccb); 29159a6844d5SKenneth D. Merry else if (state == ADA_CCB_TRIM) 29167651b989SAlexander Motin bp->bio_resid = 0; 29177651b989SAlexander Motin else 291852c9ce25SScott Long bp->bio_resid = ataio->resid; 29199a6844d5SKenneth D. Merry 29209a6844d5SKenneth D. Merry if ((bp->bio_resid > 0) 29219a6844d5SKenneth D. Merry && (bp->bio_cmd != BIO_ZONE)) 292252c9ce25SScott Long bp->bio_flags |= BIO_ERROR; 292352c9ce25SScott Long } 292452c9ce25SScott Long softc->outstanding_cmds--; 292552c9ce25SScott Long if (softc->outstanding_cmds == 0) 2926030844d1SAlexander Motin softc->flags |= ADA_FLAG_WAS_OTAG; 2927a6e0c5daSWarner Losh 29289754579bSWarner Losh /* 29299754579bSWarner Losh * We need to call cam_iosched before we call biodone so that we 29309754579bSWarner Losh * don't measure any activity that happens in the completion 29319754579bSWarner Losh * routine, which in the case of sendfile can be quite 2932d9a7a61bSWarner Losh * extensive. Release the periph refcount taken in adastart() 2933d9a7a61bSWarner Losh * for each CCB. 29349754579bSWarner Losh */ 2935a6e0c5daSWarner Losh cam_iosched_bio_complete(softc->cam_iosched, bp, done_ccb); 2936227d67aaSAlexander Motin xpt_release_ccb(done_ccb); 2937d9a7a61bSWarner Losh KASSERT(softc->refcount >= 1, ("adadone softc %p refcount %d", softc, softc->refcount)); 2938d9a7a61bSWarner Losh softc->refcount--; 29397651b989SAlexander Motin if (state == ADA_CCB_TRIM) { 29402030b294SAlexander Motin TAILQ_HEAD(, bio) queue; 29412030b294SAlexander Motin struct bio *bp1; 294252c9ce25SScott Long 29432030b294SAlexander Motin TAILQ_INIT(&queue); 29442030b294SAlexander Motin TAILQ_CONCAT(&queue, &softc->trim_req.bps, bio_queue); 29450ac66574SWarner Losh /* 29460ac66574SWarner Losh * Normally, the xpt_release_ccb() above would make sure 29470ac66574SWarner Losh * that when we have more work to do, that work would 29480ac66574SWarner Losh * get kicked off. However, we specifically keep 29490ac66574SWarner Losh * trim_running set to 0 before the call above to allow 29500ac66574SWarner Losh * other I/O to progress when many BIO_DELETE requests 29510ac66574SWarner Losh * are pushed down. We set trim_running to 0 and call 29520ac66574SWarner Losh * daschedule again so that we don't stall if there are 29530ac66574SWarner Losh * no other I/Os pending apart from BIO_DELETEs. 29540ac66574SWarner Losh */ 2955a6e0c5daSWarner Losh cam_iosched_trim_done(softc->cam_iosched); 2956227d67aaSAlexander Motin adaschedule(periph); 2957227d67aaSAlexander Motin cam_periph_unlock(periph); 29582030b294SAlexander Motin while ((bp1 = TAILQ_FIRST(&queue)) != NULL) { 29592030b294SAlexander Motin TAILQ_REMOVE(&queue, bp1, bio_queue); 29602030b294SAlexander Motin bp1->bio_error = error; 29612030b294SAlexander Motin if (error != 0) { 29621c80ec0aSAlexander Motin bp1->bio_flags |= BIO_ERROR; 29637651b989SAlexander Motin bp1->bio_resid = bp1->bio_bcount; 29647651b989SAlexander Motin } else 29657651b989SAlexander Motin bp1->bio_resid = 0; 29661c80ec0aSAlexander Motin biodone(bp1); 29671c80ec0aSAlexander Motin } 2968227d67aaSAlexander Motin } else { 2969a6e0c5daSWarner Losh adaschedule(periph); 2970227d67aaSAlexander Motin cam_periph_unlock(periph); 297152c9ce25SScott Long biodone(bp); 2972227d67aaSAlexander Motin } 2973227d67aaSAlexander Motin return; 297452c9ce25SScott Long } 29751ed6aaf9SAlexander Motin case ADA_CCB_RAHEAD: 29761ed6aaf9SAlexander Motin { 29771ed6aaf9SAlexander Motin if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { 29781ed6aaf9SAlexander Motin if (adaerror(done_ccb, 0, 0) == ERESTART) { 2979cccf4220SAlexander Motin /* Drop freeze taken due to CAM_DEV_QFREEZE */ 2980cccf4220SAlexander Motin cam_release_devq(path, 0, 0, 0, FALSE); 29811ed6aaf9SAlexander Motin return; 29821ed6aaf9SAlexander Motin } else if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) { 2983cccf4220SAlexander Motin cam_release_devq(path, 29841ed6aaf9SAlexander Motin /*relsim_flags*/0, 29851ed6aaf9SAlexander Motin /*reduction*/0, 29861ed6aaf9SAlexander Motin /*timeout*/0, 29871ed6aaf9SAlexander Motin /*getcount_only*/0); 29881ed6aaf9SAlexander Motin } 29891ed6aaf9SAlexander Motin } 29901ed6aaf9SAlexander Motin 29911ed6aaf9SAlexander Motin /* 29921ed6aaf9SAlexander Motin * Since our peripheral may be invalidated by an error 29931ed6aaf9SAlexander Motin * above or an external event, we must release our CCB 29941ed6aaf9SAlexander Motin * before releasing the reference on the peripheral. 29951ed6aaf9SAlexander Motin * The peripheral will only go away once the last reference 29961ed6aaf9SAlexander Motin * is removed, and we need it around for the CCB release 29971ed6aaf9SAlexander Motin * operation. 29981ed6aaf9SAlexander Motin */ 29999a6844d5SKenneth D. Merry 30009a6844d5SKenneth D. Merry xpt_release_ccb(done_ccb); 30011ed6aaf9SAlexander Motin softc->state = ADA_STATE_WCACHE; 30029a6844d5SKenneth D. Merry xpt_schedule(periph, priority); 3003cccf4220SAlexander Motin /* Drop freeze taken due to CAM_DEV_QFREEZE */ 3004cccf4220SAlexander Motin cam_release_devq(path, 0, 0, 0, FALSE); 30051ed6aaf9SAlexander Motin return; 30061ed6aaf9SAlexander Motin } 3007f513d14cSAlexander Motin case ADA_CCB_WCACHE: 3008f513d14cSAlexander Motin { 3009f513d14cSAlexander Motin if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { 3010f513d14cSAlexander Motin if (adaerror(done_ccb, 0, 0) == ERESTART) { 30119a6844d5SKenneth D. Merry /* Drop freeze taken due to CAM_DEV_QFREEZE */ 30129a6844d5SKenneth D. Merry cam_release_devq(path, 0, 0, 0, FALSE); 30139a6844d5SKenneth D. Merry return; 3014f513d14cSAlexander Motin } else if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) { 3015cccf4220SAlexander Motin cam_release_devq(path, 3016f513d14cSAlexander Motin /*relsim_flags*/0, 3017f513d14cSAlexander Motin /*reduction*/0, 3018f513d14cSAlexander Motin /*timeout*/0, 3019f513d14cSAlexander Motin /*getcount_only*/0); 3020f513d14cSAlexander Motin } 3021f513d14cSAlexander Motin } 3022f513d14cSAlexander Motin 3023cccf4220SAlexander Motin /* Drop freeze taken due to CAM_DEV_QFREEZE */ 3024cccf4220SAlexander Motin cam_release_devq(path, 0, 0, 0, FALSE); 30259a6844d5SKenneth D. Merry 3026600fd98fSKenneth D. Merry if ((softc->flags & ADA_FLAG_CAN_LOG) 3027600fd98fSKenneth D. Merry && (softc->zone_mode != ADA_ZONE_NONE)) { 30289a6844d5SKenneth D. Merry xpt_release_ccb(done_ccb); 30299a6844d5SKenneth D. Merry softc->state = ADA_STATE_LOGDIR; 30309a6844d5SKenneth D. Merry xpt_schedule(periph, priority); 30319a6844d5SKenneth D. Merry } else { 30329a6844d5SKenneth D. Merry adaprobedone(periph, done_ccb); 30339a6844d5SKenneth D. Merry } 30349a6844d5SKenneth D. Merry return; 30359a6844d5SKenneth D. Merry } 30369a6844d5SKenneth D. Merry case ADA_CCB_LOGDIR: 30379a6844d5SKenneth D. Merry { 30389a6844d5SKenneth D. Merry int error; 30399a6844d5SKenneth D. Merry 30409a6844d5SKenneth D. Merry if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) { 30419a6844d5SKenneth D. Merry error = 0; 30429a6844d5SKenneth D. Merry softc->valid_logdir_len = 0; 30439a6844d5SKenneth D. Merry bzero(&softc->ata_logdir, sizeof(softc->ata_logdir)); 30449a6844d5SKenneth D. Merry softc->valid_logdir_len = 30459a6844d5SKenneth D. Merry ataio->dxfer_len - ataio->resid; 30469a6844d5SKenneth D. Merry if (softc->valid_logdir_len > 0) 30479a6844d5SKenneth D. Merry bcopy(ataio->data_ptr, &softc->ata_logdir, 30489a6844d5SKenneth D. Merry min(softc->valid_logdir_len, 30499a6844d5SKenneth D. Merry sizeof(softc->ata_logdir))); 30509a6844d5SKenneth D. Merry /* 30519a6844d5SKenneth D. Merry * Figure out whether the Identify Device log is 30529a6844d5SKenneth D. Merry * supported. The General Purpose log directory 30539a6844d5SKenneth D. Merry * has a header, and lists the number of pages 30549a6844d5SKenneth D. Merry * available for each GP log identified by the 30559a6844d5SKenneth D. Merry * offset into the list. 30569a6844d5SKenneth D. Merry */ 30579a6844d5SKenneth D. Merry if ((softc->valid_logdir_len >= 30589a6844d5SKenneth D. Merry ((ATA_IDENTIFY_DATA_LOG + 1) * sizeof(uint16_t))) 30599a6844d5SKenneth D. Merry && (le16dec(softc->ata_logdir.header) == 30609a6844d5SKenneth D. Merry ATA_GP_LOG_DIR_VERSION) 30619a6844d5SKenneth D. Merry && (le16dec(&softc->ata_logdir.num_pages[ 30629a6844d5SKenneth D. Merry (ATA_IDENTIFY_DATA_LOG * 30639a6844d5SKenneth D. Merry sizeof(uint16_t)) - sizeof(uint16_t)]) > 0)){ 30649a6844d5SKenneth D. Merry softc->flags |= ADA_FLAG_CAN_IDLOG; 30659a6844d5SKenneth D. Merry } else { 30669a6844d5SKenneth D. Merry softc->flags &= ~ADA_FLAG_CAN_IDLOG; 30679a6844d5SKenneth D. Merry } 30689a6844d5SKenneth D. Merry } else { 30699a6844d5SKenneth D. Merry error = adaerror(done_ccb, CAM_RETRY_SELTO, 30709a6844d5SKenneth D. Merry SF_RETRY_UA|SF_NO_PRINT); 30719a6844d5SKenneth D. Merry if (error == ERESTART) 30729a6844d5SKenneth D. Merry return; 30739a6844d5SKenneth D. Merry else if (error != 0) { 30749a6844d5SKenneth D. Merry /* 30759a6844d5SKenneth D. Merry * If we can't get the ATA log directory, 30769a6844d5SKenneth D. Merry * then ATA logs are effectively not 30779a6844d5SKenneth D. Merry * supported even if the bit is set in the 30789a6844d5SKenneth D. Merry * identify data. 30799a6844d5SKenneth D. Merry */ 30809a6844d5SKenneth D. Merry softc->flags &= ~(ADA_FLAG_CAN_LOG | 30819a6844d5SKenneth D. Merry ADA_FLAG_CAN_IDLOG); 30829a6844d5SKenneth D. Merry if ((done_ccb->ccb_h.status & 30839a6844d5SKenneth D. Merry CAM_DEV_QFRZN) != 0) { 30849a6844d5SKenneth D. Merry /* Don't wedge this device's queue */ 30859a6844d5SKenneth D. Merry cam_release_devq(done_ccb->ccb_h.path, 30869a6844d5SKenneth D. Merry /*relsim_flags*/0, 30879a6844d5SKenneth D. Merry /*reduction*/0, 30889a6844d5SKenneth D. Merry /*timeout*/0, 30899a6844d5SKenneth D. Merry /*getcount_only*/0); 30909a6844d5SKenneth D. Merry } 30919a6844d5SKenneth D. Merry } 30929a6844d5SKenneth D. Merry } 30939a6844d5SKenneth D. Merry 30949a6844d5SKenneth D. Merry free(ataio->data_ptr, M_ATADA); 30959a6844d5SKenneth D. Merry 30969a6844d5SKenneth D. Merry if ((error == 0) 30979a6844d5SKenneth D. Merry && (softc->flags & ADA_FLAG_CAN_IDLOG)) { 30989a6844d5SKenneth D. Merry softc->state = ADA_STATE_IDDIR; 30999a6844d5SKenneth D. Merry xpt_release_ccb(done_ccb); 31009a6844d5SKenneth D. Merry xpt_schedule(periph, priority); 31019a6844d5SKenneth D. Merry } else 31029a6844d5SKenneth D. Merry adaprobedone(periph, done_ccb); 31039a6844d5SKenneth D. Merry 31049a6844d5SKenneth D. Merry return; 31059a6844d5SKenneth D. Merry } 31069a6844d5SKenneth D. Merry case ADA_CCB_IDDIR: { 31079a6844d5SKenneth D. Merry int error; 31089a6844d5SKenneth D. Merry 31099a6844d5SKenneth D. Merry if ((ataio->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) { 31109a6844d5SKenneth D. Merry off_t entries_offset, max_entries; 31119a6844d5SKenneth D. Merry error = 0; 31129a6844d5SKenneth D. Merry 31139a6844d5SKenneth D. Merry softc->valid_iddir_len = 0; 31149a6844d5SKenneth D. Merry bzero(&softc->ata_iddir, sizeof(softc->ata_iddir)); 31159a6844d5SKenneth D. Merry softc->flags &= ~(ADA_FLAG_CAN_SUPCAP | 31169a6844d5SKenneth D. Merry ADA_FLAG_CAN_ZONE); 31179a6844d5SKenneth D. Merry softc->valid_iddir_len = 31189a6844d5SKenneth D. Merry ataio->dxfer_len - ataio->resid; 31199a6844d5SKenneth D. Merry if (softc->valid_iddir_len > 0) 31209a6844d5SKenneth D. Merry bcopy(ataio->data_ptr, &softc->ata_iddir, 31219a6844d5SKenneth D. Merry min(softc->valid_iddir_len, 31229a6844d5SKenneth D. Merry sizeof(softc->ata_iddir))); 31239a6844d5SKenneth D. Merry 31249a6844d5SKenneth D. Merry entries_offset = 31259a6844d5SKenneth D. Merry __offsetof(struct ata_identify_log_pages,entries); 31269a6844d5SKenneth D. Merry max_entries = softc->valid_iddir_len - entries_offset; 31279a6844d5SKenneth D. Merry if ((softc->valid_iddir_len > (entries_offset + 1)) 31289a6844d5SKenneth D. Merry && (le64dec(softc->ata_iddir.header) == 31299a6844d5SKenneth D. Merry ATA_IDLOG_REVISION) 31309a6844d5SKenneth D. Merry && (softc->ata_iddir.entry_count > 0)) { 31319a6844d5SKenneth D. Merry int num_entries, i; 31329a6844d5SKenneth D. Merry 31339a6844d5SKenneth D. Merry num_entries = softc->ata_iddir.entry_count; 31349a6844d5SKenneth D. Merry num_entries = min(num_entries, 31359a6844d5SKenneth D. Merry softc->valid_iddir_len - entries_offset); 31369a6844d5SKenneth D. Merry for (i = 0; i < num_entries && 31379a6844d5SKenneth D. Merry i < max_entries; i++) { 31389a6844d5SKenneth D. Merry if (softc->ata_iddir.entries[i] == 31399a6844d5SKenneth D. Merry ATA_IDL_SUP_CAP) 31409a6844d5SKenneth D. Merry softc->flags |= 31419a6844d5SKenneth D. Merry ADA_FLAG_CAN_SUPCAP; 31429a6844d5SKenneth D. Merry else if (softc->ata_iddir.entries[i]== 31439a6844d5SKenneth D. Merry ATA_IDL_ZDI) 31449a6844d5SKenneth D. Merry softc->flags |= 31459a6844d5SKenneth D. Merry ADA_FLAG_CAN_ZONE; 31469a6844d5SKenneth D. Merry 31479a6844d5SKenneth D. Merry if ((softc->flags & 31489a6844d5SKenneth D. Merry ADA_FLAG_CAN_SUPCAP) 31499a6844d5SKenneth D. Merry && (softc->flags & 31509a6844d5SKenneth D. Merry ADA_FLAG_CAN_ZONE)) 31519a6844d5SKenneth D. Merry break; 31529a6844d5SKenneth D. Merry } 31539a6844d5SKenneth D. Merry } 31549a6844d5SKenneth D. Merry } else { 31559a6844d5SKenneth D. Merry error = adaerror(done_ccb, CAM_RETRY_SELTO, 31569a6844d5SKenneth D. Merry SF_RETRY_UA|SF_NO_PRINT); 31579a6844d5SKenneth D. Merry if (error == ERESTART) 31589a6844d5SKenneth D. Merry return; 31599a6844d5SKenneth D. Merry else if (error != 0) { 31609a6844d5SKenneth D. Merry /* 31619a6844d5SKenneth D. Merry * If we can't get the ATA Identify Data log 31629a6844d5SKenneth D. Merry * directory, then it effectively isn't 31639a6844d5SKenneth D. Merry * supported even if the ATA Log directory 31649a6844d5SKenneth D. Merry * a non-zero number of pages present for 31659a6844d5SKenneth D. Merry * this log. 31669a6844d5SKenneth D. Merry */ 31679a6844d5SKenneth D. Merry softc->flags &= ~ADA_FLAG_CAN_IDLOG; 31689a6844d5SKenneth D. Merry if ((done_ccb->ccb_h.status & 31699a6844d5SKenneth D. Merry CAM_DEV_QFRZN) != 0) { 31709a6844d5SKenneth D. Merry /* Don't wedge this device's queue */ 31719a6844d5SKenneth D. Merry cam_release_devq(done_ccb->ccb_h.path, 31729a6844d5SKenneth D. Merry /*relsim_flags*/0, 31739a6844d5SKenneth D. Merry /*reduction*/0, 31749a6844d5SKenneth D. Merry /*timeout*/0, 31759a6844d5SKenneth D. Merry /*getcount_only*/0); 31769a6844d5SKenneth D. Merry } 31779a6844d5SKenneth D. Merry } 31789a6844d5SKenneth D. Merry } 31799a6844d5SKenneth D. Merry 31809a6844d5SKenneth D. Merry free(ataio->data_ptr, M_ATADA); 31819a6844d5SKenneth D. Merry 31829a6844d5SKenneth D. Merry if ((error == 0) 31839a6844d5SKenneth D. Merry && (softc->flags & ADA_FLAG_CAN_SUPCAP)) { 31849a6844d5SKenneth D. Merry softc->state = ADA_STATE_SUP_CAP; 31859a6844d5SKenneth D. Merry xpt_release_ccb(done_ccb); 31869a6844d5SKenneth D. Merry xpt_schedule(periph, priority); 31879a6844d5SKenneth D. Merry } else 31889a6844d5SKenneth D. Merry adaprobedone(periph, done_ccb); 31899a6844d5SKenneth D. Merry return; 31909a6844d5SKenneth D. Merry } 31919a6844d5SKenneth D. Merry case ADA_CCB_SUP_CAP: { 31929a6844d5SKenneth D. Merry int error; 31939a6844d5SKenneth D. Merry 31949a6844d5SKenneth D. Merry if ((ataio->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) { 31959a6844d5SKenneth D. Merry uint32_t valid_len; 31969a6844d5SKenneth D. Merry size_t needed_size; 31979a6844d5SKenneth D. Merry struct ata_identify_log_sup_cap *sup_cap; 31989a6844d5SKenneth D. Merry error = 0; 31999a6844d5SKenneth D. Merry 32009a6844d5SKenneth D. Merry sup_cap = (struct ata_identify_log_sup_cap *) 32019a6844d5SKenneth D. Merry ataio->data_ptr; 32029a6844d5SKenneth D. Merry valid_len = ataio->dxfer_len - ataio->resid; 32039a6844d5SKenneth D. Merry needed_size = 32049a6844d5SKenneth D. Merry __offsetof(struct ata_identify_log_sup_cap, 32059a6844d5SKenneth D. Merry sup_zac_cap) + 1 + sizeof(sup_cap->sup_zac_cap); 32069a6844d5SKenneth D. Merry if (valid_len >= needed_size) { 32079a6844d5SKenneth D. Merry uint64_t zoned, zac_cap; 32089a6844d5SKenneth D. Merry 32099a6844d5SKenneth D. Merry zoned = le64dec(sup_cap->zoned_cap); 32109a6844d5SKenneth D. Merry if (zoned & ATA_ZONED_VALID) { 32119a6844d5SKenneth D. Merry /* 32129a6844d5SKenneth D. Merry * This should have already been 32139a6844d5SKenneth D. Merry * set, because this is also in the 32149a6844d5SKenneth D. Merry * ATA identify data. 32159a6844d5SKenneth D. Merry */ 32169a6844d5SKenneth D. Merry if ((zoned & ATA_ZONED_MASK) == 32179a6844d5SKenneth D. Merry ATA_SUPPORT_ZONE_HOST_AWARE) 32189a6844d5SKenneth D. Merry softc->zone_mode = 32199a6844d5SKenneth D. Merry ADA_ZONE_HOST_AWARE; 32209a6844d5SKenneth D. Merry else if ((zoned & ATA_ZONED_MASK) == 32219a6844d5SKenneth D. Merry ATA_SUPPORT_ZONE_DEV_MANAGED) 32229a6844d5SKenneth D. Merry softc->zone_mode = 32239a6844d5SKenneth D. Merry ADA_ZONE_DRIVE_MANAGED; 32249a6844d5SKenneth D. Merry } 32259a6844d5SKenneth D. Merry 32269a6844d5SKenneth D. Merry zac_cap = le64dec(sup_cap->sup_zac_cap); 32279a6844d5SKenneth D. Merry if (zac_cap & ATA_SUP_ZAC_CAP_VALID) { 32289a6844d5SKenneth D. Merry if (zac_cap & ATA_REPORT_ZONES_SUP) 32299a6844d5SKenneth D. Merry softc->zone_flags |= 32309a6844d5SKenneth D. Merry ADA_ZONE_FLAG_RZ_SUP; 32319a6844d5SKenneth D. Merry if (zac_cap & ATA_ND_OPEN_ZONE_SUP) 32329a6844d5SKenneth D. Merry softc->zone_flags |= 32339a6844d5SKenneth D. Merry ADA_ZONE_FLAG_OPEN_SUP; 32349a6844d5SKenneth D. Merry if (zac_cap & ATA_ND_CLOSE_ZONE_SUP) 32359a6844d5SKenneth D. Merry softc->zone_flags |= 32369a6844d5SKenneth D. Merry ADA_ZONE_FLAG_CLOSE_SUP; 32379a6844d5SKenneth D. Merry if (zac_cap & ATA_ND_FINISH_ZONE_SUP) 32389a6844d5SKenneth D. Merry softc->zone_flags |= 32399a6844d5SKenneth D. Merry ADA_ZONE_FLAG_FINISH_SUP; 32409a6844d5SKenneth D. Merry if (zac_cap & ATA_ND_RWP_SUP) 32419a6844d5SKenneth D. Merry softc->zone_flags |= 32429a6844d5SKenneth D. Merry ADA_ZONE_FLAG_RWP_SUP; 32439a6844d5SKenneth D. Merry } else { 32449a6844d5SKenneth D. Merry /* 32459a6844d5SKenneth D. Merry * This field was introduced in 32469a6844d5SKenneth D. Merry * ACS-4, r08 on April 28th, 2015. 32479a6844d5SKenneth D. Merry * If the drive firmware was written 32489a6844d5SKenneth D. Merry * to an earlier spec, it won't have 32499a6844d5SKenneth D. Merry * the field. So, assume all 32509a6844d5SKenneth D. Merry * commands are supported. 32519a6844d5SKenneth D. Merry */ 32529a6844d5SKenneth D. Merry softc->zone_flags |= 32539a6844d5SKenneth D. Merry ADA_ZONE_FLAG_SUP_MASK; 32549a6844d5SKenneth D. Merry } 32559a6844d5SKenneth D. Merry } 32569a6844d5SKenneth D. Merry } else { 32579a6844d5SKenneth D. Merry error = adaerror(done_ccb, CAM_RETRY_SELTO, 32589a6844d5SKenneth D. Merry SF_RETRY_UA|SF_NO_PRINT); 32599a6844d5SKenneth D. Merry if (error == ERESTART) 32609a6844d5SKenneth D. Merry return; 32619a6844d5SKenneth D. Merry else if (error != 0) { 32629a6844d5SKenneth D. Merry /* 32639a6844d5SKenneth D. Merry * If we can't get the ATA Identify Data 32649a6844d5SKenneth D. Merry * Supported Capabilities page, clear the 32659a6844d5SKenneth D. Merry * flag... 32669a6844d5SKenneth D. Merry */ 32679a6844d5SKenneth D. Merry softc->flags &= ~ADA_FLAG_CAN_SUPCAP; 32689a6844d5SKenneth D. Merry /* 32699a6844d5SKenneth D. Merry * And clear zone capabilities. 32709a6844d5SKenneth D. Merry */ 32719a6844d5SKenneth D. Merry softc->zone_flags &= ~ADA_ZONE_FLAG_SUP_MASK; 32729a6844d5SKenneth D. Merry if ((done_ccb->ccb_h.status & 32739a6844d5SKenneth D. Merry CAM_DEV_QFRZN) != 0) { 32749a6844d5SKenneth D. Merry /* Don't wedge this device's queue */ 32759a6844d5SKenneth D. Merry cam_release_devq(done_ccb->ccb_h.path, 32769a6844d5SKenneth D. Merry /*relsim_flags*/0, 32779a6844d5SKenneth D. Merry /*reduction*/0, 32789a6844d5SKenneth D. Merry /*timeout*/0, 32799a6844d5SKenneth D. Merry /*getcount_only*/0); 32809a6844d5SKenneth D. Merry } 32819a6844d5SKenneth D. Merry } 32829a6844d5SKenneth D. Merry } 32839a6844d5SKenneth D. Merry 32849a6844d5SKenneth D. Merry free(ataio->data_ptr, M_ATADA); 32859a6844d5SKenneth D. Merry 32869a6844d5SKenneth D. Merry if ((error == 0) 32879a6844d5SKenneth D. Merry && (softc->flags & ADA_FLAG_CAN_ZONE)) { 32889a6844d5SKenneth D. Merry softc->state = ADA_STATE_ZONE; 32899a6844d5SKenneth D. Merry xpt_release_ccb(done_ccb); 32909a6844d5SKenneth D. Merry xpt_schedule(periph, priority); 32919a6844d5SKenneth D. Merry } else 32929a6844d5SKenneth D. Merry adaprobedone(periph, done_ccb); 32939a6844d5SKenneth D. Merry return; 32949a6844d5SKenneth D. Merry } 32959a6844d5SKenneth D. Merry case ADA_CCB_ZONE: { 32969a6844d5SKenneth D. Merry int error; 32979a6844d5SKenneth D. Merry 32989a6844d5SKenneth D. Merry if ((ataio->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) { 32999a6844d5SKenneth D. Merry struct ata_zoned_info_log *zi_log; 33009a6844d5SKenneth D. Merry uint32_t valid_len; 33019a6844d5SKenneth D. Merry size_t needed_size; 33029a6844d5SKenneth D. Merry 33039a6844d5SKenneth D. Merry zi_log = (struct ata_zoned_info_log *)ataio->data_ptr; 33049a6844d5SKenneth D. Merry 33059a6844d5SKenneth D. Merry valid_len = ataio->dxfer_len - ataio->resid; 33069a6844d5SKenneth D. Merry needed_size = __offsetof(struct ata_zoned_info_log, 33079a6844d5SKenneth D. Merry version_info) + 1 + sizeof(zi_log->version_info); 33089a6844d5SKenneth D. Merry if (valid_len >= needed_size) { 33099a6844d5SKenneth D. Merry uint64_t tmpvar; 33109a6844d5SKenneth D. Merry 33119a6844d5SKenneth D. Merry tmpvar = le64dec(zi_log->zoned_cap); 33129a6844d5SKenneth D. Merry if (tmpvar & ATA_ZDI_CAP_VALID) { 33139a6844d5SKenneth D. Merry if (tmpvar & ATA_ZDI_CAP_URSWRZ) 33149a6844d5SKenneth D. Merry softc->zone_flags |= 33159a6844d5SKenneth D. Merry ADA_ZONE_FLAG_URSWRZ; 33169a6844d5SKenneth D. Merry else 33179a6844d5SKenneth D. Merry softc->zone_flags &= 33189a6844d5SKenneth D. Merry ~ADA_ZONE_FLAG_URSWRZ; 33199a6844d5SKenneth D. Merry } 33209a6844d5SKenneth D. Merry tmpvar = le64dec(zi_log->optimal_seq_zones); 33219a6844d5SKenneth D. Merry if (tmpvar & ATA_ZDI_OPT_SEQ_VALID) { 33229a6844d5SKenneth D. Merry softc->zone_flags |= 33239a6844d5SKenneth D. Merry ADA_ZONE_FLAG_OPT_SEQ_SET; 33249a6844d5SKenneth D. Merry softc->optimal_seq_zones = (tmpvar & 33259a6844d5SKenneth D. Merry ATA_ZDI_OPT_SEQ_MASK); 33269a6844d5SKenneth D. Merry } else { 33279a6844d5SKenneth D. Merry softc->zone_flags &= 33289a6844d5SKenneth D. Merry ~ADA_ZONE_FLAG_OPT_SEQ_SET; 33299a6844d5SKenneth D. Merry softc->optimal_seq_zones = 0; 33309a6844d5SKenneth D. Merry } 33319a6844d5SKenneth D. Merry 33329a6844d5SKenneth D. Merry tmpvar =le64dec(zi_log->optimal_nonseq_zones); 33339a6844d5SKenneth D. Merry if (tmpvar & ATA_ZDI_OPT_NS_VALID) { 33349a6844d5SKenneth D. Merry softc->zone_flags |= 33359a6844d5SKenneth D. Merry ADA_ZONE_FLAG_OPT_NONSEQ_SET; 33369a6844d5SKenneth D. Merry softc->optimal_nonseq_zones = 33379a6844d5SKenneth D. Merry (tmpvar & ATA_ZDI_OPT_NS_MASK); 33389a6844d5SKenneth D. Merry } else { 33399a6844d5SKenneth D. Merry softc->zone_flags &= 33409a6844d5SKenneth D. Merry ~ADA_ZONE_FLAG_OPT_NONSEQ_SET; 33419a6844d5SKenneth D. Merry softc->optimal_nonseq_zones = 0; 33429a6844d5SKenneth D. Merry } 33439a6844d5SKenneth D. Merry 33449a6844d5SKenneth D. Merry tmpvar = le64dec(zi_log->max_seq_req_zones); 33459a6844d5SKenneth D. Merry if (tmpvar & ATA_ZDI_MAX_SEQ_VALID) { 33469a6844d5SKenneth D. Merry softc->zone_flags |= 33479a6844d5SKenneth D. Merry ADA_ZONE_FLAG_MAX_SEQ_SET; 33489a6844d5SKenneth D. Merry softc->max_seq_zones = 33499a6844d5SKenneth D. Merry (tmpvar & ATA_ZDI_MAX_SEQ_MASK); 33509a6844d5SKenneth D. Merry } else { 33519a6844d5SKenneth D. Merry softc->zone_flags &= 33529a6844d5SKenneth D. Merry ~ADA_ZONE_FLAG_MAX_SEQ_SET; 33539a6844d5SKenneth D. Merry softc->max_seq_zones = 0; 33549a6844d5SKenneth D. Merry } 33559a6844d5SKenneth D. Merry } 33569a6844d5SKenneth D. Merry } else { 33579a6844d5SKenneth D. Merry error = adaerror(done_ccb, CAM_RETRY_SELTO, 33589a6844d5SKenneth D. Merry SF_RETRY_UA|SF_NO_PRINT); 33599a6844d5SKenneth D. Merry if (error == ERESTART) 33609a6844d5SKenneth D. Merry return; 33619a6844d5SKenneth D. Merry else if (error != 0) { 33629a6844d5SKenneth D. Merry softc->flags &= ~ADA_FLAG_CAN_ZONE; 33639a6844d5SKenneth D. Merry softc->flags &= ~ADA_ZONE_FLAG_SET_MASK; 33649a6844d5SKenneth D. Merry 33659a6844d5SKenneth D. Merry if ((done_ccb->ccb_h.status & 33669a6844d5SKenneth D. Merry CAM_DEV_QFRZN) != 0) { 33679a6844d5SKenneth D. Merry /* Don't wedge this device's queue */ 33689a6844d5SKenneth D. Merry cam_release_devq(done_ccb->ccb_h.path, 33699a6844d5SKenneth D. Merry /*relsim_flags*/0, 33709a6844d5SKenneth D. Merry /*reduction*/0, 33719a6844d5SKenneth D. Merry /*timeout*/0, 33729a6844d5SKenneth D. Merry /*getcount_only*/0); 33739a6844d5SKenneth D. Merry } 33749a6844d5SKenneth D. Merry } 33759a6844d5SKenneth D. Merry } 33769a6844d5SKenneth D. Merry free(ataio->data_ptr, M_ATADA); 33779a6844d5SKenneth D. Merry 33789a6844d5SKenneth D. Merry adaprobedone(periph, done_ccb); 3379f513d14cSAlexander Motin return; 3380f513d14cSAlexander Motin } 338152c9ce25SScott Long case ADA_CCB_DUMP: 338252c9ce25SScott Long /* No-op. We're polling */ 338352c9ce25SScott Long return; 338452c9ce25SScott Long default: 338552c9ce25SScott Long break; 338652c9ce25SScott Long } 338752c9ce25SScott Long xpt_release_ccb(done_ccb); 338852c9ce25SScott Long } 338952c9ce25SScott Long 339052c9ce25SScott Long static int 339152c9ce25SScott Long adaerror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags) 339252c9ce25SScott Long { 3393acfc9b68SWarner Losh #ifdef CAM_IO_STATS 3394a6e0c5daSWarner Losh struct ada_softc *softc; 3395a6e0c5daSWarner Losh struct cam_periph *periph; 3396a6e0c5daSWarner Losh 3397a6e0c5daSWarner Losh periph = xpt_path_periph(ccb->ccb_h.path); 3398a6e0c5daSWarner Losh softc = (struct ada_softc *)periph->softc; 3399a6e0c5daSWarner Losh 3400a6e0c5daSWarner Losh switch (ccb->ccb_h.status & CAM_STATUS_MASK) { 3401a6e0c5daSWarner Losh case CAM_CMD_TIMEOUT: 3402a6e0c5daSWarner Losh softc->timeouts++; 3403a6e0c5daSWarner Losh break; 3404a6e0c5daSWarner Losh case CAM_REQ_ABORTED: 3405a6e0c5daSWarner Losh case CAM_REQ_CMP_ERR: 3406a6e0c5daSWarner Losh case CAM_REQ_TERMIO: 3407a6e0c5daSWarner Losh case CAM_UNREC_HBA_ERROR: 3408a6e0c5daSWarner Losh case CAM_DATA_RUN_ERR: 3409a6e0c5daSWarner Losh case CAM_ATA_STATUS_ERROR: 3410a6e0c5daSWarner Losh softc->errors++; 3411a6e0c5daSWarner Losh break; 3412a6e0c5daSWarner Losh default: 3413a6e0c5daSWarner Losh break; 3414a6e0c5daSWarner Losh } 3415acfc9b68SWarner Losh #endif 341652c9ce25SScott Long 3417553484aeSWarner Losh return(cam_periph_error(ccb, cam_flags, sense_flags)); 341852c9ce25SScott Long } 341952c9ce25SScott Long 342052c9ce25SScott Long static void 342176d843daSAlexander Motin adasetgeom(struct ada_softc *softc, struct ccb_getdev *cgd) 342252c9ce25SScott Long { 342352c9ce25SScott Long struct disk_params *dp = &softc->params; 342452c9ce25SScott Long u_int64_t lbasize48; 342552c9ce25SScott Long u_int32_t lbasize; 342676d843daSAlexander Motin u_int maxio, d_flags; 3427f350bc1dSJessica Clarke size_t tmpsize; 342852c9ce25SScott Long 3429c1bd46c2SAlexander Motin dp->secsize = ata_logical_sector_size(&cgd->ident_data); 343052c9ce25SScott Long if ((cgd->ident_data.atavalid & ATA_FLAG_54_58) && 343176d843daSAlexander Motin cgd->ident_data.current_heads != 0 && 343276d843daSAlexander Motin cgd->ident_data.current_sectors != 0) { 343352c9ce25SScott Long dp->heads = cgd->ident_data.current_heads; 343452c9ce25SScott Long dp->secs_per_track = cgd->ident_data.current_sectors; 343552c9ce25SScott Long dp->cylinders = cgd->ident_data.cylinders; 343652c9ce25SScott Long dp->sectors = (u_int32_t)cgd->ident_data.current_size_1 | 343752c9ce25SScott Long ((u_int32_t)cgd->ident_data.current_size_2 << 16); 343852c9ce25SScott Long } else { 343952c9ce25SScott Long dp->heads = cgd->ident_data.heads; 344052c9ce25SScott Long dp->secs_per_track = cgd->ident_data.sectors; 344152c9ce25SScott Long dp->cylinders = cgd->ident_data.cylinders; 344299ae1d3bSAlan Somers dp->sectors = cgd->ident_data.cylinders * 344399ae1d3bSAlan Somers (u_int32_t)(dp->heads * dp->secs_per_track); 344452c9ce25SScott Long } 344552c9ce25SScott Long lbasize = (u_int32_t)cgd->ident_data.lba_size_1 | 344652c9ce25SScott Long ((u_int32_t)cgd->ident_data.lba_size_2 << 16); 344752c9ce25SScott Long 344852c9ce25SScott Long /* use the 28bit LBA size if valid or bigger than the CHS mapping */ 344952c9ce25SScott Long if (cgd->ident_data.cylinders == 16383 || dp->sectors < lbasize) 345052c9ce25SScott Long dp->sectors = lbasize; 345152c9ce25SScott Long 345252c9ce25SScott Long /* use the 48bit LBA size if valid */ 345352c9ce25SScott Long lbasize48 = ((u_int64_t)cgd->ident_data.lba_size48_1) | 345452c9ce25SScott Long ((u_int64_t)cgd->ident_data.lba_size48_2 << 16) | 345552c9ce25SScott Long ((u_int64_t)cgd->ident_data.lba_size48_3 << 32) | 345652c9ce25SScott Long ((u_int64_t)cgd->ident_data.lba_size48_4 << 48); 345752c9ce25SScott Long if ((cgd->ident_data.support.command2 & ATA_SUPPORT_ADDRESS48) && 345852c9ce25SScott Long lbasize48 > ATA_MAX_28BIT_LBA) 345952c9ce25SScott Long dp->sectors = lbasize48; 346076d843daSAlexander Motin 346176d843daSAlexander Motin maxio = softc->cpi.maxio; /* Honor max I/O size of SIM */ 346276d843daSAlexander Motin if (maxio == 0) 346376d843daSAlexander Motin maxio = DFLTPHYS; /* traditional default */ 3464cd853791SKonstantin Belousov else if (maxio > maxphys) 3465cd853791SKonstantin Belousov maxio = maxphys; /* for safety */ 346676d843daSAlexander Motin if (softc->flags & ADA_FLAG_CAN_48BIT) 346776d843daSAlexander Motin maxio = min(maxio, 65536 * softc->params.secsize); 346876d843daSAlexander Motin else /* 28bit ATA command limit */ 346976d843daSAlexander Motin maxio = min(maxio, 256 * softc->params.secsize); 347076d843daSAlexander Motin if (softc->quirks & ADA_Q_128KB) 347176d843daSAlexander Motin maxio = min(maxio, 128 * 1024); 347276d843daSAlexander Motin softc->disk->d_maxsize = maxio; 347376d843daSAlexander Motin d_flags = DISKFLAG_DIRECT_COMPLETION | DISKFLAG_CANZONE; 347476d843daSAlexander Motin if (softc->flags & ADA_FLAG_CAN_FLUSHCACHE) 347576d843daSAlexander Motin d_flags |= DISKFLAG_CANFLUSHCACHE; 347676d843daSAlexander Motin if (softc->flags & ADA_FLAG_CAN_TRIM) { 347776d843daSAlexander Motin d_flags |= DISKFLAG_CANDELETE; 347876d843daSAlexander Motin softc->disk->d_delmaxsize = softc->params.secsize * 347976d843daSAlexander Motin ATA_DSM_RANGE_MAX * softc->trim_max_ranges; 348076d843daSAlexander Motin } else if ((softc->flags & ADA_FLAG_CAN_CFA) && 348176d843daSAlexander Motin !(softc->flags & ADA_FLAG_CAN_48BIT)) { 348276d843daSAlexander Motin d_flags |= DISKFLAG_CANDELETE; 348376d843daSAlexander Motin softc->disk->d_delmaxsize = 256 * softc->params.secsize; 348476d843daSAlexander Motin } else 348576d843daSAlexander Motin softc->disk->d_delmaxsize = maxio; 348676d843daSAlexander Motin if ((softc->cpi.hba_misc & PIM_UNMAPPED) != 0) { 348776d843daSAlexander Motin d_flags |= DISKFLAG_UNMAPPED_BIO; 3488cf3ff63eSWarner Losh softc->flags |= ADA_FLAG_UNMAPPEDIO; 348976d843daSAlexander Motin } 349076d843daSAlexander Motin softc->disk->d_flags = d_flags; 3491f350bc1dSJessica Clarke 3492f350bc1dSJessica Clarke /* 3493f350bc1dSJessica Clarke * ata_param_fixup will strip trailing padding spaces and add a NUL, 3494f350bc1dSJessica Clarke * but if the field has no padding (as is common for serial numbers) 3495f350bc1dSJessica Clarke * there will still be no NUL terminator. We cannot use strlcpy, since 3496f350bc1dSJessica Clarke * it keeps reading src until it finds a NUL in order to compute the 3497f350bc1dSJessica Clarke * return value (and will truncate the final character due to having a 3498f350bc1dSJessica Clarke * single dsize rather than separate ssize and dsize), and strncpy does 3499f350bc1dSJessica Clarke * not add a NUL to the destination if it reaches the character limit. 3500f350bc1dSJessica Clarke */ 3501f350bc1dSJessica Clarke tmpsize = MIN(sizeof(softc->disk->d_descr) - 1, 3502f350bc1dSJessica Clarke sizeof(cgd->ident_data.model)); 3503f350bc1dSJessica Clarke memcpy(softc->disk->d_descr, cgd->ident_data.model, tmpsize); 3504f350bc1dSJessica Clarke softc->disk->d_descr[tmpsize] = '\0'; 3505f350bc1dSJessica Clarke 3506f350bc1dSJessica Clarke tmpsize = MIN(sizeof(softc->disk->d_ident) - 1, 3507f350bc1dSJessica Clarke sizeof(cgd->ident_data.serial)); 3508f350bc1dSJessica Clarke memcpy(softc->disk->d_ident, cgd->ident_data.serial, tmpsize); 3509f350bc1dSJessica Clarke softc->disk->d_ident[tmpsize] = '\0'; 351076d843daSAlexander Motin 351176d843daSAlexander Motin softc->disk->d_sectorsize = softc->params.secsize; 351276d843daSAlexander Motin softc->disk->d_mediasize = (off_t)softc->params.sectors * 351376d843daSAlexander Motin softc->params.secsize; 351476d843daSAlexander Motin if (ata_physical_sector_size(&cgd->ident_data) != 351576d843daSAlexander Motin softc->params.secsize) { 351676d843daSAlexander Motin softc->disk->d_stripesize = 351776d843daSAlexander Motin ata_physical_sector_size(&cgd->ident_data); 351876d843daSAlexander Motin softc->disk->d_stripeoffset = (softc->disk->d_stripesize - 351976d843daSAlexander Motin ata_logical_sector_offset(&cgd->ident_data)) % 352076d843daSAlexander Motin softc->disk->d_stripesize; 352176d843daSAlexander Motin } else if (softc->quirks & ADA_Q_4K) { 352276d843daSAlexander Motin softc->disk->d_stripesize = 4096; 352376d843daSAlexander Motin softc->disk->d_stripeoffset = 0; 352476d843daSAlexander Motin } 352576d843daSAlexander Motin softc->disk->d_fwsectors = softc->params.secs_per_track; 352676d843daSAlexander Motin softc->disk->d_fwheads = softc->params.heads; 352776d843daSAlexander Motin softc->disk->d_rotation_rate = cgd->ident_data.media_rotation_rate; 3528b5961be1SEdward Tomasz Napierala snprintf(softc->disk->d_attachment, sizeof(softc->disk->d_attachment), 3529b5961be1SEdward Tomasz Napierala "%s%d", softc->cpi.dev_name, softc->cpi.unit_number); 353052c9ce25SScott Long } 353152c9ce25SScott Long 353252c9ce25SScott Long static void 353352c9ce25SScott Long adasendorderedtag(void *arg) 353452c9ce25SScott Long { 353552c9ce25SScott Long struct ada_softc *softc = arg; 353652c9ce25SScott Long 353752c9ce25SScott Long if (ada_send_ordered) { 3538030844d1SAlexander Motin if (softc->outstanding_cmds > 0) { 3539030844d1SAlexander Motin if ((softc->flags & ADA_FLAG_WAS_OTAG) == 0) 354052c9ce25SScott Long softc->flags |= ADA_FLAG_NEED_OTAG; 3541030844d1SAlexander Motin softc->flags &= ~ADA_FLAG_WAS_OTAG; 354252c9ce25SScott Long } 354352c9ce25SScott Long } 35440e5c50bfSAlexander Motin 354552c9ce25SScott Long /* Queue us up again */ 35460e5c50bfSAlexander Motin callout_schedule_sbt(&softc->sendordered_c, 35470e5c50bfSAlexander Motin SBT_1S / ADA_ORDEREDTAG_INTERVAL * ada_default_timeout, 0, 35480e5c50bfSAlexander Motin C_PREL(1)); 354952c9ce25SScott Long } 355052c9ce25SScott Long 355152c9ce25SScott Long /* 355252c9ce25SScott Long * Step through all ADA peripheral drivers, and if the device is still open, 355352c9ce25SScott Long * sync the disk cache to physical media. 355452c9ce25SScott Long */ 355552c9ce25SScott Long static void 3556c3d0d168SAlexander Motin adaflush(void) 355752c9ce25SScott Long { 355852c9ce25SScott Long struct cam_periph *periph; 355952c9ce25SScott Long struct ada_softc *softc; 356009cfadbeSAlexander Motin union ccb *ccb; 35610191d9b3SAlexander Motin int error; 356252c9ce25SScott Long 3563f371c9e2SAlexander Motin CAM_PERIPH_FOREACH(periph, &adadriver) { 356452c9ce25SScott Long softc = (struct ada_softc *)periph->softc; 35652f87dfb0SAlexander Motin if (SCHEDULER_STOPPED()) { 35662f87dfb0SAlexander Motin /* If we paniced with the lock held, do not recurse. */ 35672f87dfb0SAlexander Motin if (!cam_periph_owned(periph) && 35682f87dfb0SAlexander Motin (softc->flags & ADA_FLAG_OPEN)) { 35692f87dfb0SAlexander Motin adadump(softc->disk, NULL, 0, 0, 0); 35702f87dfb0SAlexander Motin } 35712f87dfb0SAlexander Motin continue; 35722f87dfb0SAlexander Motin } 35732f87dfb0SAlexander Motin cam_periph_lock(periph); 357452c9ce25SScott Long /* 357552c9ce25SScott Long * We only sync the cache if the drive is still open, and 357652c9ce25SScott Long * if the drive is capable of it.. 357752c9ce25SScott Long */ 357852c9ce25SScott Long if (((softc->flags & ADA_FLAG_OPEN) == 0) || 357952c9ce25SScott Long (softc->flags & ADA_FLAG_CAN_FLUSHCACHE) == 0) { 358052c9ce25SScott Long cam_periph_unlock(periph); 358152c9ce25SScott Long continue; 358252c9ce25SScott Long } 358352c9ce25SScott Long 358409cfadbeSAlexander Motin ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL); 358509cfadbeSAlexander Motin cam_fill_ataio(&ccb->ataio, 35860191d9b3SAlexander Motin 0, 3587eed99e75SScott Long NULL, 358852c9ce25SScott Long CAM_DIR_NONE, 358952c9ce25SScott Long 0, 359052c9ce25SScott Long NULL, 359152c9ce25SScott Long 0, 359252c9ce25SScott Long ada_default_timeout*1000); 359352c9ce25SScott Long if (softc->flags & ADA_FLAG_CAN_48BIT) 359409cfadbeSAlexander Motin ata_48bit_cmd(&ccb->ataio, ATA_FLUSHCACHE48, 0, 0, 0); 359552c9ce25SScott Long else 359609cfadbeSAlexander Motin ata_28bit_cmd(&ccb->ataio, ATA_FLUSHCACHE, 0, 0, 0); 359752c9ce25SScott Long 359809cfadbeSAlexander Motin error = cam_periph_runccb(ccb, adaerror, /*cam_flags*/0, 359909cfadbeSAlexander Motin /*sense_flags*/ SF_NO_RECOVERY | SF_NO_RETRY, 360009cfadbeSAlexander Motin softc->disk->d_devstat); 36010191d9b3SAlexander Motin if (error != 0) 36020191d9b3SAlexander Motin xpt_print(periph->path, "Synchronize cache failed\n"); 3603d6794b70SAlexander Motin xpt_release_ccb(ccb); 360452c9ce25SScott Long cam_periph_unlock(periph); 360552c9ce25SScott Long } 3606c3d0d168SAlexander Motin } 3607fd104c15SRebecca Cran 3608c3d0d168SAlexander Motin static void 3609c3d0d168SAlexander Motin adaspindown(uint8_t cmd, int flags) 3610c3d0d168SAlexander Motin { 3611c3d0d168SAlexander Motin struct cam_periph *periph; 3612c3d0d168SAlexander Motin struct ada_softc *softc; 3613f902366cSWarner Losh struct ccb_ataio local_ccb; 36140191d9b3SAlexander Motin int error; 361515910dc0SAndriy Gapon int mode; 3616fd104c15SRebecca Cran 3617f371c9e2SAlexander Motin CAM_PERIPH_FOREACH(periph, &adadriver) { 3618fd104c15SRebecca Cran /* If we paniced with lock held - not recurse here. */ 3619fd104c15SRebecca Cran if (cam_periph_owned(periph)) 3620fd104c15SRebecca Cran continue; 3621fd104c15SRebecca Cran cam_periph_lock(periph); 3622fd104c15SRebecca Cran softc = (struct ada_softc *)periph->softc; 3623fd104c15SRebecca Cran /* 3624fd104c15SRebecca Cran * We only spin-down the drive if it is capable of it.. 3625fd104c15SRebecca Cran */ 3626fd104c15SRebecca Cran if ((softc->flags & ADA_FLAG_CAN_POWERMGT) == 0) { 3627fd104c15SRebecca Cran cam_periph_unlock(periph); 3628fd104c15SRebecca Cran continue; 3629fd104c15SRebecca Cran } 3630fd104c15SRebecca Cran 363115910dc0SAndriy Gapon /* 363215910dc0SAndriy Gapon * Additionally check if we would spin up the drive instead of 363315910dc0SAndriy Gapon * spinning it down. 363415910dc0SAndriy Gapon */ 363515910dc0SAndriy Gapon if (cmd == ATA_IDLE_IMMEDIATE) { 363615910dc0SAndriy Gapon memset(&local_ccb, 0, sizeof(local_ccb)); 363715910dc0SAndriy Gapon xpt_setup_ccb(&local_ccb.ccb_h, periph->path, 363815910dc0SAndriy Gapon CAM_PRIORITY_NORMAL); 363915910dc0SAndriy Gapon local_ccb.ccb_h.ccb_state = ADA_CCB_DUMP; 364015910dc0SAndriy Gapon 364115910dc0SAndriy Gapon cam_fill_ataio(&local_ccb, 0, NULL, CAM_DIR_NONE, 364215910dc0SAndriy Gapon 0, NULL, 0, ada_default_timeout * 1000); 364315910dc0SAndriy Gapon ata_28bit_cmd(&local_ccb, ATA_CHECK_POWER_MODE, 364415910dc0SAndriy Gapon 0, 0, 0); 364515910dc0SAndriy Gapon local_ccb.cmd.flags |= CAM_ATAIO_NEEDRESULT; 364615910dc0SAndriy Gapon 364715910dc0SAndriy Gapon error = cam_periph_runccb((union ccb *)&local_ccb, 364815910dc0SAndriy Gapon adaerror, /*cam_flags*/0, 364915910dc0SAndriy Gapon /*sense_flags*/ SF_NO_RECOVERY | SF_NO_RETRY, 365015910dc0SAndriy Gapon softc->disk->d_devstat); 365115910dc0SAndriy Gapon if (error != 0) { 365215910dc0SAndriy Gapon xpt_print(periph->path, 365315910dc0SAndriy Gapon "Failed to read current power mode\n"); 365415910dc0SAndriy Gapon } else { 365515910dc0SAndriy Gapon mode = local_ccb.res.sector_count; 365615910dc0SAndriy Gapon #ifdef DIAGNOSTIC 365715910dc0SAndriy Gapon if (bootverbose) { 365815910dc0SAndriy Gapon xpt_print(periph->path, 365915910dc0SAndriy Gapon "disk power mode 0x%02x\n", mode); 366015910dc0SAndriy Gapon } 366115910dc0SAndriy Gapon #endif 366215910dc0SAndriy Gapon switch (mode) { 3663*75bc7150SAndriy Gapon case ATA_PM_STANDBY: 3664*75bc7150SAndriy Gapon case ATA_PM_STANDBY_Y: 366515910dc0SAndriy Gapon if (bootverbose) { 366615910dc0SAndriy Gapon xpt_print(periph->path, 366715910dc0SAndriy Gapon "already spun down\n"); 366815910dc0SAndriy Gapon } 366915910dc0SAndriy Gapon cam_periph_unlock(periph); 367015910dc0SAndriy Gapon continue; 367115910dc0SAndriy Gapon default: 367215910dc0SAndriy Gapon break; 367315910dc0SAndriy Gapon } 367415910dc0SAndriy Gapon } 367515910dc0SAndriy Gapon } 367615910dc0SAndriy Gapon 3677fd104c15SRebecca Cran if (bootverbose) 3678fd104c15SRebecca Cran xpt_print(periph->path, "spin-down\n"); 3679fd104c15SRebecca Cran 3680f902366cSWarner Losh memset(&local_ccb, 0, sizeof(local_ccb)); 3681f902366cSWarner Losh xpt_setup_ccb(&local_ccb.ccb_h, periph->path, CAM_PRIORITY_NORMAL); 3682f902366cSWarner Losh local_ccb.ccb_h.ccb_state = ADA_CCB_DUMP; 3683f902366cSWarner Losh 3684f902366cSWarner Losh cam_fill_ataio(&local_ccb, 36850191d9b3SAlexander Motin 0, 3686eed99e75SScott Long NULL, 3687c3d0d168SAlexander Motin CAM_DIR_NONE | flags, 3688fd104c15SRebecca Cran 0, 3689fd104c15SRebecca Cran NULL, 3690fd104c15SRebecca Cran 0, 3691fd104c15SRebecca Cran ada_default_timeout*1000); 3692f902366cSWarner Losh ata_28bit_cmd(&local_ccb, cmd, 0, 0, 0); 3693f902366cSWarner Losh error = cam_periph_runccb((union ccb *)&local_ccb, adaerror, 3694f902366cSWarner Losh /*cam_flags*/0, /*sense_flags*/ SF_NO_RECOVERY | SF_NO_RETRY, 369509cfadbeSAlexander Motin softc->disk->d_devstat); 36960191d9b3SAlexander Motin if (error != 0) 36970191d9b3SAlexander Motin xpt_print(periph->path, "Spin-down disk failed\n"); 3698fd104c15SRebecca Cran cam_periph_unlock(periph); 3699fd104c15SRebecca Cran } 370052c9ce25SScott Long } 370152c9ce25SScott Long 3702c3d0d168SAlexander Motin static void 3703c3d0d168SAlexander Motin adashutdown(void *arg, int howto) 3704c3d0d168SAlexander Motin { 370550ee2e2aSWarner Losh int how; 3706c3d0d168SAlexander Motin 3707c3d0d168SAlexander Motin adaflush(); 3708f902366cSWarner Losh 3709f902366cSWarner Losh /* 371050ee2e2aSWarner Losh * STANDBY IMMEDIATE saves any volatile data to the drive. It also spins 371150ee2e2aSWarner Losh * down hard drives. IDLE IMMEDIATE also saves the volatile data without 371250ee2e2aSWarner Losh * a spindown. We send the former when we expect to lose power soon. For 371350ee2e2aSWarner Losh * a warm boot, we send the latter to avoid a thundering herd of spinups 371450ee2e2aSWarner Losh * just after the kernel loads while probing. We have to do something to 371550ee2e2aSWarner Losh * flush the data because the BIOS in many systems resets the HBA 371650ee2e2aSWarner Losh * causing a COMINIT/COMRESET negotiation, which some drives interpret 371750ee2e2aSWarner Losh * as license to toss the volatile data, and others count as unclean 371850ee2e2aSWarner Losh * shutdown when in the Active PM state in SMART attributes. 371950ee2e2aSWarner Losh * 372050ee2e2aSWarner Losh * adaspindown will ensure that we don't send this to a drive that 372150ee2e2aSWarner Losh * doesn't support it. 3722f902366cSWarner Losh */ 372350ee2e2aSWarner Losh if (ada_spindown_shutdown != 0) { 372450ee2e2aSWarner Losh how = (howto & (RB_HALT | RB_POWEROFF | RB_POWERCYCLE)) ? 372550ee2e2aSWarner Losh ATA_STANDBY_IMMEDIATE : ATA_IDLE_IMMEDIATE; 372650ee2e2aSWarner Losh adaspindown(how, 0); 372750ee2e2aSWarner Losh } 3728c3d0d168SAlexander Motin } 3729c3d0d168SAlexander Motin 3730c3d0d168SAlexander Motin static void 3731c3d0d168SAlexander Motin adasuspend(void *arg) 3732c3d0d168SAlexander Motin { 3733c3d0d168SAlexander Motin 3734c3d0d168SAlexander Motin adaflush(); 3735f902366cSWarner Losh /* 3736f902366cSWarner Losh * SLEEP also fushes any volatile data, like STANDBY IMEDIATE, 3737f902366cSWarner Losh * so we don't need to send it as well. 3738f902366cSWarner Losh */ 3739c3d0d168SAlexander Motin if (ada_spindown_suspend != 0) 3740c3d0d168SAlexander Motin adaspindown(ATA_SLEEP, CAM_DEV_QFREEZE); 3741c3d0d168SAlexander Motin } 3742c3d0d168SAlexander Motin 3743c3d0d168SAlexander Motin static void 3744c3d0d168SAlexander Motin adaresume(void *arg) 3745c3d0d168SAlexander Motin { 3746c3d0d168SAlexander Motin struct cam_periph *periph; 3747c3d0d168SAlexander Motin struct ada_softc *softc; 3748c3d0d168SAlexander Motin 3749c3d0d168SAlexander Motin if (ada_spindown_suspend == 0) 3750c3d0d168SAlexander Motin return; 3751c3d0d168SAlexander Motin 3752f371c9e2SAlexander Motin CAM_PERIPH_FOREACH(periph, &adadriver) { 3753c3d0d168SAlexander Motin cam_periph_lock(periph); 3754c3d0d168SAlexander Motin softc = (struct ada_softc *)periph->softc; 3755c3d0d168SAlexander Motin /* 3756c3d0d168SAlexander Motin * We only spin-down the drive if it is capable of it.. 3757c3d0d168SAlexander Motin */ 3758c3d0d168SAlexander Motin if ((softc->flags & ADA_FLAG_CAN_POWERMGT) == 0) { 3759c3d0d168SAlexander Motin cam_periph_unlock(periph); 3760c3d0d168SAlexander Motin continue; 3761c3d0d168SAlexander Motin } 3762c3d0d168SAlexander Motin 3763c3d0d168SAlexander Motin if (bootverbose) 3764c3d0d168SAlexander Motin xpt_print(periph->path, "resume\n"); 3765c3d0d168SAlexander Motin 3766c3d0d168SAlexander Motin /* 3767c3d0d168SAlexander Motin * Drop freeze taken due to CAM_DEV_QFREEZE flag set on 3768c3d0d168SAlexander Motin * sleep request. 3769c3d0d168SAlexander Motin */ 3770c3d0d168SAlexander Motin cam_release_devq(periph->path, 3771c3d0d168SAlexander Motin /*relsim_flags*/0, 3772c3d0d168SAlexander Motin /*openings*/0, 3773c3d0d168SAlexander Motin /*timeout*/0, 3774c3d0d168SAlexander Motin /*getcount_only*/0); 3775c3d0d168SAlexander Motin 3776c3d0d168SAlexander Motin cam_periph_unlock(periph); 3777c3d0d168SAlexander Motin } 3778c3d0d168SAlexander Motin } 3779c3d0d168SAlexander Motin 378052c9ce25SScott Long #endif /* _KERNEL */ 3781