1898b0535SWarner Losh /*- 28b8a9b1dSJustin T. Gibbs * Common functions for CAM "type" (peripheral) drivers. 38b8a9b1dSJustin T. Gibbs * 48b8a9b1dSJustin T. Gibbs * Copyright (c) 1997, 1998 Justin T. Gibbs. 5501468a5SKenneth D. Merry * Copyright (c) 1997, 1998, 1999, 2000 Kenneth D. Merry. 68b8a9b1dSJustin T. Gibbs * All rights reserved. 78b8a9b1dSJustin T. Gibbs * 88b8a9b1dSJustin T. Gibbs * Redistribution and use in source and binary forms, with or without 98b8a9b1dSJustin T. Gibbs * modification, are permitted provided that the following conditions 108b8a9b1dSJustin T. Gibbs * are met: 118b8a9b1dSJustin T. Gibbs * 1. Redistributions of source code must retain the above copyright 128b8a9b1dSJustin T. Gibbs * notice, this list of conditions, and the following disclaimer, 138b8a9b1dSJustin T. Gibbs * without modification, immediately at the beginning of the file. 148b8a9b1dSJustin T. Gibbs * 2. The name of the author may not be used to endorse or promote products 158b8a9b1dSJustin T. Gibbs * derived from this software without specific prior written permission. 168b8a9b1dSJustin T. Gibbs * 178b8a9b1dSJustin T. Gibbs * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 188b8a9b1dSJustin T. Gibbs * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 198b8a9b1dSJustin T. Gibbs * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 208b8a9b1dSJustin T. Gibbs * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 218b8a9b1dSJustin T. Gibbs * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 228b8a9b1dSJustin T. Gibbs * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 238b8a9b1dSJustin T. Gibbs * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 248b8a9b1dSJustin T. Gibbs * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 258b8a9b1dSJustin T. Gibbs * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 268b8a9b1dSJustin T. Gibbs * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 278b8a9b1dSJustin T. Gibbs * SUCH DAMAGE. 288b8a9b1dSJustin T. Gibbs */ 298b8a9b1dSJustin T. Gibbs 309c963d87SDavid E. O'Brien #include <sys/cdefs.h> 319c963d87SDavid E. O'Brien __FBSDID("$FreeBSD$"); 329c963d87SDavid E. O'Brien 338b8a9b1dSJustin T. Gibbs #include <sys/param.h> 348b8a9b1dSJustin T. Gibbs #include <sys/systm.h> 358b8a9b1dSJustin T. Gibbs #include <sys/types.h> 368b8a9b1dSJustin T. Gibbs #include <sys/malloc.h> 37362abc44STai-hwa Liang #include <sys/kernel.h> 389626b608SPoul-Henning Kamp #include <sys/bio.h> 39f34fa851SJohn Baldwin #include <sys/lock.h> 40f34fa851SJohn Baldwin #include <sys/mutex.h> 418b8a9b1dSJustin T. Gibbs #include <sys/buf.h> 428b8a9b1dSJustin T. Gibbs #include <sys/proc.h> 438b8a9b1dSJustin T. Gibbs #include <sys/devicestat.h> 4475f51904SPeter Wemm #include <sys/bus.h> 453501942bSJustin T. Gibbs #include <sys/sbuf.h> 468b8a9b1dSJustin T. Gibbs #include <vm/vm.h> 478b8a9b1dSJustin T. Gibbs #include <vm/vm_extern.h> 488b8a9b1dSJustin T. Gibbs 498b8a9b1dSJustin T. Gibbs #include <cam/cam.h> 508b8a9b1dSJustin T. Gibbs #include <cam/cam_ccb.h> 5152c9ce25SScott Long #include <cam/cam_queue.h> 528b8a9b1dSJustin T. Gibbs #include <cam/cam_xpt_periph.h> 538b8a9b1dSJustin T. Gibbs #include <cam/cam_periph.h> 548b8a9b1dSJustin T. Gibbs #include <cam/cam_debug.h> 552b83592fSScott Long #include <cam/cam_sim.h> 568b8a9b1dSJustin T. Gibbs 578b8a9b1dSJustin T. Gibbs #include <cam/scsi/scsi_all.h> 588b8a9b1dSJustin T. Gibbs #include <cam/scsi/scsi_message.h> 598b8a9b1dSJustin T. Gibbs #include <cam/scsi/scsi_pass.h> 608b8a9b1dSJustin T. Gibbs 618b8a9b1dSJustin T. Gibbs static u_int camperiphnextunit(struct periph_driver *p_drv, 62501468a5SKenneth D. Merry u_int newunit, int wired, 63501468a5SKenneth D. Merry path_id_t pathid, target_id_t target, 64501468a5SKenneth D. Merry lun_id_t lun); 658b8a9b1dSJustin T. Gibbs static u_int camperiphunit(struct periph_driver *p_drv, 66501468a5SKenneth D. Merry path_id_t pathid, target_id_t target, 67501468a5SKenneth D. Merry lun_id_t lun); 688b8a9b1dSJustin T. Gibbs static void camperiphdone(struct cam_periph *periph, 698b8a9b1dSJustin T. Gibbs union ccb *done_ccb); 708b8a9b1dSJustin T. Gibbs static void camperiphfree(struct cam_periph *periph); 713393f8daSKenneth D. Merry static int camperiphscsistatuserror(union ccb *ccb, 723393f8daSKenneth D. Merry cam_flags camflags, 733393f8daSKenneth D. Merry u_int32_t sense_flags, 743393f8daSKenneth D. Merry int *openings, 753393f8daSKenneth D. Merry u_int32_t *relsim_flags, 7683c5d981SAlexander Motin u_int32_t *timeout, 7783c5d981SAlexander Motin const char **action_string); 783393f8daSKenneth D. Merry static int camperiphscsisenseerror(union ccb *ccb, 793393f8daSKenneth D. Merry cam_flags camflags, 803393f8daSKenneth D. Merry u_int32_t sense_flags, 813393f8daSKenneth D. Merry int *openings, 823393f8daSKenneth D. Merry u_int32_t *relsim_flags, 8383c5d981SAlexander Motin u_int32_t *timeout, 8483c5d981SAlexander Motin const char **action_string); 858b8a9b1dSJustin T. Gibbs 860b7c27b9SPeter Wemm static int nperiph_drivers; 8783c5d981SAlexander Motin static int initialized = 0; 880b7c27b9SPeter Wemm struct periph_driver **periph_drivers; 890b7c27b9SPeter Wemm 90d745c852SEd Schouten static MALLOC_DEFINE(M_CAMPERIPH, "CAM periph", "CAM peripheral buffers"); 91362abc44STai-hwa Liang 9273cf209fSMatt Jacob static int periph_selto_delay = 1000; 9373cf209fSMatt Jacob TUNABLE_INT("kern.cam.periph_selto_delay", &periph_selto_delay); 9473cf209fSMatt Jacob static int periph_noresrc_delay = 500; 9573cf209fSMatt Jacob TUNABLE_INT("kern.cam.periph_noresrc_delay", &periph_noresrc_delay); 9673cf209fSMatt Jacob static int periph_busy_delay = 500; 9773cf209fSMatt Jacob TUNABLE_INT("kern.cam.periph_busy_delay", &periph_busy_delay); 9873cf209fSMatt Jacob 9973cf209fSMatt Jacob 1000b7c27b9SPeter Wemm void 1010b7c27b9SPeter Wemm periphdriver_register(void *data) 1020b7c27b9SPeter Wemm { 10383c5d981SAlexander Motin struct periph_driver *drv = (struct periph_driver *)data; 1040b7c27b9SPeter Wemm struct periph_driver **newdrivers, **old; 1050b7c27b9SPeter Wemm int ndrivers; 1060b7c27b9SPeter Wemm 1070b7c27b9SPeter Wemm ndrivers = nperiph_drivers + 2; 1080dd50e9bSScott Long newdrivers = malloc(sizeof(*newdrivers) * ndrivers, M_CAMPERIPH, 1090dd50e9bSScott Long M_WAITOK); 1100b7c27b9SPeter Wemm if (periph_drivers) 1110b7c27b9SPeter Wemm bcopy(periph_drivers, newdrivers, 112623db360SKenneth D. Merry sizeof(*newdrivers) * nperiph_drivers); 11383c5d981SAlexander Motin newdrivers[nperiph_drivers] = drv; 1140b7c27b9SPeter Wemm newdrivers[nperiph_drivers + 1] = NULL; 1150b7c27b9SPeter Wemm old = periph_drivers; 1160b7c27b9SPeter Wemm periph_drivers = newdrivers; 1170b7c27b9SPeter Wemm if (old) 1180dd50e9bSScott Long free(old, M_CAMPERIPH); 1190b7c27b9SPeter Wemm nperiph_drivers++; 12083c5d981SAlexander Motin /* If driver marked as early or it is late now, initialize it. */ 12183c5d981SAlexander Motin if (((drv->flags & CAM_PERIPH_DRV_EARLY) != 0 && initialized > 0) || 12283c5d981SAlexander Motin initialized > 1) 12383c5d981SAlexander Motin (*drv->init)(); 12483c5d981SAlexander Motin } 12583c5d981SAlexander Motin 12683c5d981SAlexander Motin void 12783c5d981SAlexander Motin periphdriver_init(int level) 12883c5d981SAlexander Motin { 12983c5d981SAlexander Motin int i, early; 13083c5d981SAlexander Motin 13183c5d981SAlexander Motin initialized = max(initialized, level); 13283c5d981SAlexander Motin for (i = 0; periph_drivers[i] != NULL; i++) { 13383c5d981SAlexander Motin early = (periph_drivers[i]->flags & CAM_PERIPH_DRV_EARLY) ? 1 : 2; 13483c5d981SAlexander Motin if (early == initialized) 13583c5d981SAlexander Motin (*periph_drivers[i]->init)(); 13683c5d981SAlexander Motin } 1370b7c27b9SPeter Wemm } 1380b7c27b9SPeter Wemm 1398b8a9b1dSJustin T. Gibbs cam_status 140ee9c90c7SKenneth D. Merry cam_periph_alloc(periph_ctor_t *periph_ctor, 141ee9c90c7SKenneth D. Merry periph_oninv_t *periph_oninvalidate, 142ee9c90c7SKenneth D. Merry periph_dtor_t *periph_dtor, periph_start_t *periph_start, 143ee9c90c7SKenneth D. Merry char *name, cam_periph_type type, struct cam_path *path, 144ee9c90c7SKenneth D. Merry ac_callback_t *ac_callback, ac_code code, void *arg) 1458b8a9b1dSJustin T. Gibbs { 1468b8a9b1dSJustin T. Gibbs struct periph_driver **p_drv; 1472b83592fSScott Long struct cam_sim *sim; 1488b8a9b1dSJustin T. Gibbs struct cam_periph *periph; 1498b8a9b1dSJustin T. Gibbs struct cam_periph *cur_periph; 1508b8a9b1dSJustin T. Gibbs path_id_t path_id; 1518b8a9b1dSJustin T. Gibbs target_id_t target_id; 1528b8a9b1dSJustin T. Gibbs lun_id_t lun_id; 1538b8a9b1dSJustin T. Gibbs cam_status status; 1548b8a9b1dSJustin T. Gibbs u_int init_level; 1558b8a9b1dSJustin T. Gibbs 1568b8a9b1dSJustin T. Gibbs init_level = 0; 1578b8a9b1dSJustin T. Gibbs /* 1588b8a9b1dSJustin T. Gibbs * Handle Hot-Plug scenarios. If there is already a peripheral 1598b8a9b1dSJustin T. Gibbs * of our type assigned to this path, we are likely waiting for 1608b8a9b1dSJustin T. Gibbs * final close on an old, invalidated, peripheral. If this is 1618b8a9b1dSJustin T. Gibbs * the case, queue up a deferred call to the peripheral's async 16274c91ec5SJustin T. Gibbs * handler. If it looks like a mistaken re-allocation, complain. 1638b8a9b1dSJustin T. Gibbs */ 1648b8a9b1dSJustin T. Gibbs if ((periph = cam_periph_find(path, name)) != NULL) { 1658b8a9b1dSJustin T. Gibbs 1668b8a9b1dSJustin T. Gibbs if ((periph->flags & CAM_PERIPH_INVALID) != 0 1678b8a9b1dSJustin T. Gibbs && (periph->flags & CAM_PERIPH_NEW_DEV_FOUND) == 0) { 1688b8a9b1dSJustin T. Gibbs periph->flags |= CAM_PERIPH_NEW_DEV_FOUND; 1698b8a9b1dSJustin T. Gibbs periph->deferred_callback = ac_callback; 1708b8a9b1dSJustin T. Gibbs periph->deferred_ac = code; 1718b8a9b1dSJustin T. Gibbs return (CAM_REQ_INPROG); 1728b8a9b1dSJustin T. Gibbs } else { 1738b8a9b1dSJustin T. Gibbs printf("cam_periph_alloc: attempt to re-allocate " 1748900f4b8SKenneth D. Merry "valid device %s%d rejected flags %#x " 1758900f4b8SKenneth D. Merry "refcount %d\n", periph->periph_name, 1768900f4b8SKenneth D. Merry periph->unit_number, periph->flags, 1778900f4b8SKenneth D. Merry periph->refcount); 1788b8a9b1dSJustin T. Gibbs } 1798b8a9b1dSJustin T. Gibbs return (CAM_REQ_INVALID); 1808b8a9b1dSJustin T. Gibbs } 1818b8a9b1dSJustin T. Gibbs 182362abc44STai-hwa Liang periph = (struct cam_periph *)malloc(sizeof(*periph), M_CAMPERIPH, 1838900f4b8SKenneth D. Merry M_NOWAIT|M_ZERO); 1848b8a9b1dSJustin T. Gibbs 1858b8a9b1dSJustin T. Gibbs if (periph == NULL) 1868b8a9b1dSJustin T. Gibbs return (CAM_RESRC_UNAVAIL); 1878b8a9b1dSJustin T. Gibbs 1888b8a9b1dSJustin T. Gibbs init_level++; 1898b8a9b1dSJustin T. Gibbs 1908b8a9b1dSJustin T. Gibbs 1912b83592fSScott Long sim = xpt_path_sim(path); 1928b8a9b1dSJustin T. Gibbs path_id = xpt_path_path_id(path); 1938b8a9b1dSJustin T. Gibbs target_id = xpt_path_target_id(path); 1948b8a9b1dSJustin T. Gibbs lun_id = xpt_path_lun_id(path); 1958b8a9b1dSJustin T. Gibbs cam_init_pinfo(&periph->pinfo); 1968b8a9b1dSJustin T. Gibbs periph->periph_start = periph_start; 1978b8a9b1dSJustin T. Gibbs periph->periph_dtor = periph_dtor; 198ee9c90c7SKenneth D. Merry periph->periph_oninval = periph_oninvalidate; 1998b8a9b1dSJustin T. Gibbs periph->type = type; 2008b8a9b1dSJustin T. Gibbs periph->periph_name = name; 2018b8a9b1dSJustin T. Gibbs periph->immediate_priority = CAM_PRIORITY_NONE; 2028b8a9b1dSJustin T. Gibbs periph->refcount = 0; 2032b83592fSScott Long periph->sim = sim; 2048b8a9b1dSJustin T. Gibbs SLIST_INIT(&periph->ccb_list); 2058b8a9b1dSJustin T. Gibbs status = xpt_create_path(&path, periph, path_id, target_id, lun_id); 2068b8a9b1dSJustin T. Gibbs if (status != CAM_REQ_CMP) 2078b8a9b1dSJustin T. Gibbs goto failure; 2088b8a9b1dSJustin T. Gibbs periph->path = path; 2098b8a9b1dSJustin T. Gibbs 210f1e2546aSMatt Jacob xpt_lock_buses(); 211f1e2546aSMatt Jacob for (p_drv = periph_drivers; *p_drv != NULL; p_drv++) { 212f1e2546aSMatt Jacob if (strcmp((*p_drv)->driver_name, name) == 0) 213f1e2546aSMatt Jacob break; 214f1e2546aSMatt Jacob } 215f1e2546aSMatt Jacob if (*p_drv == NULL) { 216f1e2546aSMatt Jacob printf("cam_periph_alloc: invalid periph name '%s'\n", name); 217f1e2546aSMatt Jacob xpt_free_path(periph->path); 218f1e2546aSMatt Jacob free(periph, M_CAMPERIPH); 219f1e2546aSMatt Jacob xpt_unlock_buses(); 220f1e2546aSMatt Jacob return (CAM_REQ_INVALID); 221f1e2546aSMatt Jacob } 222f1e2546aSMatt Jacob periph->unit_number = camperiphunit(*p_drv, path_id, target_id, lun_id); 2238b8a9b1dSJustin T. Gibbs cur_periph = TAILQ_FIRST(&(*p_drv)->units); 2248b8a9b1dSJustin T. Gibbs while (cur_periph != NULL 2258b8a9b1dSJustin T. Gibbs && cur_periph->unit_number < periph->unit_number) 2268b8a9b1dSJustin T. Gibbs cur_periph = TAILQ_NEXT(cur_periph, unit_links); 227f1e2546aSMatt Jacob if (cur_periph != NULL) { 228f1e2546aSMatt Jacob KASSERT(cur_periph->unit_number != periph->unit_number, ("duplicate units on periph list")); 2298b8a9b1dSJustin T. Gibbs TAILQ_INSERT_BEFORE(cur_periph, periph, unit_links); 230f1e2546aSMatt Jacob } else { 2318b8a9b1dSJustin T. Gibbs TAILQ_INSERT_TAIL(&(*p_drv)->units, periph, unit_links); 2328b8a9b1dSJustin T. Gibbs (*p_drv)->generation++; 2338b8a9b1dSJustin T. Gibbs } 234f1e2546aSMatt Jacob xpt_unlock_buses(); 235f1e2546aSMatt Jacob 236f1e2546aSMatt Jacob init_level++; 237f1e2546aSMatt Jacob 238f1e2546aSMatt Jacob status = xpt_add_periph(periph); 239f1e2546aSMatt Jacob if (status != CAM_REQ_CMP) 240f1e2546aSMatt Jacob goto failure; 2418b8a9b1dSJustin T. Gibbs 2428b8a9b1dSJustin T. Gibbs init_level++; 243*22c7d606SAlexander Motin CAM_DEBUG(periph->path, CAM_DEBUG_INFO, ("Periph created\n")); 2448b8a9b1dSJustin T. Gibbs 2458b8a9b1dSJustin T. Gibbs status = periph_ctor(periph, arg); 2468b8a9b1dSJustin T. Gibbs 2478b8a9b1dSJustin T. Gibbs if (status == CAM_REQ_CMP) 2488b8a9b1dSJustin T. Gibbs init_level++; 2498b8a9b1dSJustin T. Gibbs 2508b8a9b1dSJustin T. Gibbs failure: 2518b8a9b1dSJustin T. Gibbs switch (init_level) { 2528b8a9b1dSJustin T. Gibbs case 4: 2538b8a9b1dSJustin T. Gibbs /* Initialized successfully */ 2548b8a9b1dSJustin T. Gibbs break; 2558b8a9b1dSJustin T. Gibbs case 3: 256*22c7d606SAlexander Motin CAM_DEBUG(periph->path, CAM_DEBUG_INFO, ("Periph destroyed\n")); 2578b8a9b1dSJustin T. Gibbs xpt_remove_periph(periph); 2587379c88fSPoul-Henning Kamp /* FALLTHROUGH */ 2598b8a9b1dSJustin T. Gibbs case 2: 260f1e2546aSMatt Jacob xpt_lock_buses(); 261f1e2546aSMatt Jacob TAILQ_REMOVE(&(*p_drv)->units, periph, unit_links); 262f1e2546aSMatt Jacob xpt_unlock_buses(); 2638b8a9b1dSJustin T. Gibbs xpt_free_path(periph->path); 2647379c88fSPoul-Henning Kamp /* FALLTHROUGH */ 2658b8a9b1dSJustin T. Gibbs case 1: 266362abc44STai-hwa Liang free(periph, M_CAMPERIPH); 2677379c88fSPoul-Henning Kamp /* FALLTHROUGH */ 2688b8a9b1dSJustin T. Gibbs case 0: 2698b8a9b1dSJustin T. Gibbs /* No cleanup to perform. */ 2708b8a9b1dSJustin T. Gibbs break; 2718b8a9b1dSJustin T. Gibbs default: 2728b8a9b1dSJustin T. Gibbs panic("cam_periph_alloc: Unkown init level"); 2738b8a9b1dSJustin T. Gibbs } 2748b8a9b1dSJustin T. Gibbs return(status); 2758b8a9b1dSJustin T. Gibbs } 2768b8a9b1dSJustin T. Gibbs 2778b8a9b1dSJustin T. Gibbs /* 2788b8a9b1dSJustin T. Gibbs * Find a peripheral structure with the specified path, target, lun, 2798b8a9b1dSJustin T. Gibbs * and (optionally) type. If the name is NULL, this function will return 2808b8a9b1dSJustin T. Gibbs * the first peripheral driver that matches the specified path. 2818b8a9b1dSJustin T. Gibbs */ 2828b8a9b1dSJustin T. Gibbs struct cam_periph * 2838b8a9b1dSJustin T. Gibbs cam_periph_find(struct cam_path *path, char *name) 2848b8a9b1dSJustin T. Gibbs { 2858b8a9b1dSJustin T. Gibbs struct periph_driver **p_drv; 2868b8a9b1dSJustin T. Gibbs struct cam_periph *periph; 2878b8a9b1dSJustin T. Gibbs 28877dc25ccSScott Long xpt_lock_buses(); 2890b7c27b9SPeter Wemm for (p_drv = periph_drivers; *p_drv != NULL; p_drv++) { 2908b8a9b1dSJustin T. Gibbs 2918b8a9b1dSJustin T. Gibbs if (name != NULL && (strcmp((*p_drv)->driver_name, name) != 0)) 2928b8a9b1dSJustin T. Gibbs continue; 2938b8a9b1dSJustin T. Gibbs 29437d40066SPoul-Henning Kamp TAILQ_FOREACH(periph, &(*p_drv)->units, unit_links) { 2958b8a9b1dSJustin T. Gibbs if (xpt_path_comp(periph->path, path) == 0) { 29677dc25ccSScott Long xpt_unlock_buses(); 297f1e2546aSMatt Jacob mtx_assert(periph->sim->mtx, MA_OWNED); 2988b8a9b1dSJustin T. Gibbs return(periph); 2998b8a9b1dSJustin T. Gibbs } 3008b8a9b1dSJustin T. Gibbs } 30177dc25ccSScott Long if (name != NULL) { 30277dc25ccSScott Long xpt_unlock_buses(); 3038b8a9b1dSJustin T. Gibbs return(NULL); 3048b8a9b1dSJustin T. Gibbs } 30577dc25ccSScott Long } 30677dc25ccSScott Long xpt_unlock_buses(); 3078b8a9b1dSJustin T. Gibbs return(NULL); 3088b8a9b1dSJustin T. Gibbs } 3098b8a9b1dSJustin T. Gibbs 3103501942bSJustin T. Gibbs /* 3118900f4b8SKenneth D. Merry * Find peripheral driver instances attached to the specified path. 3123501942bSJustin T. Gibbs */ 3133501942bSJustin T. Gibbs int 3143501942bSJustin T. Gibbs cam_periph_list(struct cam_path *path, struct sbuf *sb) 3153501942bSJustin T. Gibbs { 3168900f4b8SKenneth D. Merry struct sbuf local_sb; 3173501942bSJustin T. Gibbs struct periph_driver **p_drv; 3183501942bSJustin T. Gibbs struct cam_periph *periph; 3193501942bSJustin T. Gibbs int count; 3208900f4b8SKenneth D. Merry int sbuf_alloc_len; 3213501942bSJustin T. Gibbs 3228900f4b8SKenneth D. Merry sbuf_alloc_len = 16; 3238900f4b8SKenneth D. Merry retry: 3248900f4b8SKenneth D. Merry sbuf_new(&local_sb, NULL, sbuf_alloc_len, SBUF_FIXEDLEN); 3253501942bSJustin T. Gibbs count = 0; 3263501942bSJustin T. Gibbs xpt_lock_buses(); 3273501942bSJustin T. Gibbs for (p_drv = periph_drivers; *p_drv != NULL; p_drv++) { 3283501942bSJustin T. Gibbs 3293501942bSJustin T. Gibbs TAILQ_FOREACH(periph, &(*p_drv)->units, unit_links) { 3303501942bSJustin T. Gibbs if (xpt_path_comp(periph->path, path) != 0) 3313501942bSJustin T. Gibbs continue; 3323501942bSJustin T. Gibbs 3338900f4b8SKenneth D. Merry if (sbuf_len(&local_sb) != 0) 3348900f4b8SKenneth D. Merry sbuf_cat(&local_sb, ","); 3353501942bSJustin T. Gibbs 3368900f4b8SKenneth D. Merry sbuf_printf(&local_sb, "%s%d", periph->periph_name, 3373501942bSJustin T. Gibbs periph->unit_number); 3388900f4b8SKenneth D. Merry 3398900f4b8SKenneth D. Merry if (sbuf_error(&local_sb) == ENOMEM) { 3408900f4b8SKenneth D. Merry sbuf_alloc_len *= 2; 3418900f4b8SKenneth D. Merry xpt_unlock_buses(); 3428900f4b8SKenneth D. Merry sbuf_delete(&local_sb); 3438900f4b8SKenneth D. Merry goto retry; 3448900f4b8SKenneth D. Merry } 3453501942bSJustin T. Gibbs count++; 3463501942bSJustin T. Gibbs } 3473501942bSJustin T. Gibbs } 3483501942bSJustin T. Gibbs xpt_unlock_buses(); 3498900f4b8SKenneth D. Merry sbuf_finish(&local_sb); 3508900f4b8SKenneth D. Merry sbuf_cpy(sb, sbuf_data(&local_sb)); 3518900f4b8SKenneth D. Merry sbuf_delete(&local_sb); 3523501942bSJustin T. Gibbs return (count); 3533501942bSJustin T. Gibbs } 3543501942bSJustin T. Gibbs 3558b8a9b1dSJustin T. Gibbs cam_status 3568b8a9b1dSJustin T. Gibbs cam_periph_acquire(struct cam_periph *periph) 3578b8a9b1dSJustin T. Gibbs { 3588900f4b8SKenneth D. Merry cam_status status; 3598b8a9b1dSJustin T. Gibbs 3608900f4b8SKenneth D. Merry status = CAM_REQ_CMP_ERR; 3618b8a9b1dSJustin T. Gibbs if (periph == NULL) 3628900f4b8SKenneth D. Merry return (status); 3638b8a9b1dSJustin T. Gibbs 3642b83592fSScott Long xpt_lock_buses(); 3658900f4b8SKenneth D. Merry if ((periph->flags & CAM_PERIPH_INVALID) == 0) { 3668b8a9b1dSJustin T. Gibbs periph->refcount++; 3678900f4b8SKenneth D. Merry status = CAM_REQ_CMP; 3688900f4b8SKenneth D. Merry } 3692b83592fSScott Long xpt_unlock_buses(); 3708b8a9b1dSJustin T. Gibbs 3718900f4b8SKenneth D. Merry return (status); 3728900f4b8SKenneth D. Merry } 3738900f4b8SKenneth D. Merry 3748900f4b8SKenneth D. Merry void 3758900f4b8SKenneth D. Merry cam_periph_release_locked_buses(struct cam_periph *periph) 3768900f4b8SKenneth D. Merry { 3778900f4b8SKenneth D. Merry if (periph->refcount != 0) { 3788900f4b8SKenneth D. Merry periph->refcount--; 3798900f4b8SKenneth D. Merry } else { 3808900f4b8SKenneth D. Merry panic("%s: release of %p when refcount is zero\n ", __func__, 3818900f4b8SKenneth D. Merry periph); 3828900f4b8SKenneth D. Merry } 3838900f4b8SKenneth D. Merry if (periph->refcount == 0 3848900f4b8SKenneth D. Merry && (periph->flags & CAM_PERIPH_INVALID)) { 3858900f4b8SKenneth D. Merry camperiphfree(periph); 3868900f4b8SKenneth D. Merry } 3878b8a9b1dSJustin T. Gibbs } 3888b8a9b1dSJustin T. Gibbs 3898b8a9b1dSJustin T. Gibbs void 39024ebf566SEdward Tomasz Napierala cam_periph_release_locked(struct cam_periph *periph) 3918b8a9b1dSJustin T. Gibbs { 3928b8a9b1dSJustin T. Gibbs 3938b8a9b1dSJustin T. Gibbs if (periph == NULL) 3948b8a9b1dSJustin T. Gibbs return; 3958b8a9b1dSJustin T. Gibbs 3962b83592fSScott Long xpt_lock_buses(); 3978900f4b8SKenneth D. Merry cam_periph_release_locked_buses(periph); 3982b83592fSScott Long xpt_unlock_buses(); 39924ebf566SEdward Tomasz Napierala } 4008b8a9b1dSJustin T. Gibbs 40124ebf566SEdward Tomasz Napierala void 40224ebf566SEdward Tomasz Napierala cam_periph_release(struct cam_periph *periph) 40324ebf566SEdward Tomasz Napierala { 40424ebf566SEdward Tomasz Napierala struct cam_sim *sim; 40524ebf566SEdward Tomasz Napierala 40624ebf566SEdward Tomasz Napierala if (periph == NULL) 40724ebf566SEdward Tomasz Napierala return; 40824ebf566SEdward Tomasz Napierala 40924ebf566SEdward Tomasz Napierala sim = periph->sim; 41024ebf566SEdward Tomasz Napierala mtx_assert(sim->mtx, MA_NOTOWNED); 41124ebf566SEdward Tomasz Napierala mtx_lock(sim->mtx); 41224ebf566SEdward Tomasz Napierala cam_periph_release_locked(periph); 41324ebf566SEdward Tomasz Napierala mtx_unlock(sim->mtx); 4148b8a9b1dSJustin T. Gibbs } 4158b8a9b1dSJustin T. Gibbs 4162b83592fSScott Long int 4172b83592fSScott Long cam_periph_hold(struct cam_periph *periph, int priority) 4182b83592fSScott Long { 4192b83592fSScott Long int error; 4202b83592fSScott Long 4212b83592fSScott Long /* 4222b83592fSScott Long * Increment the reference count on the peripheral 4232b83592fSScott Long * while we wait for our lock attempt to succeed 4242b83592fSScott Long * to ensure the peripheral doesn't disappear out 4252b83592fSScott Long * from user us while we sleep. 4262b83592fSScott Long */ 4272b83592fSScott Long 4282b83592fSScott Long if (cam_periph_acquire(periph) != CAM_REQ_CMP) 4292b83592fSScott Long return (ENXIO); 4302b83592fSScott Long 4311fa738c2SJohn Baldwin mtx_assert(periph->sim->mtx, MA_OWNED); 4322b83592fSScott Long while ((periph->flags & CAM_PERIPH_LOCKED) != 0) { 4332b83592fSScott Long periph->flags |= CAM_PERIPH_LOCK_WANTED; 4341fa738c2SJohn Baldwin if ((error = mtx_sleep(periph, periph->sim->mtx, priority, 4351fa738c2SJohn Baldwin "caplck", 0)) != 0) { 43624ebf566SEdward Tomasz Napierala cam_periph_release_locked(periph); 4372b83592fSScott Long return (error); 4382b83592fSScott Long } 4392b83592fSScott Long } 4402b83592fSScott Long 4412b83592fSScott Long periph->flags |= CAM_PERIPH_LOCKED; 4422b83592fSScott Long return (0); 4432b83592fSScott Long } 4442b83592fSScott Long 4452b83592fSScott Long void 4462b83592fSScott Long cam_periph_unhold(struct cam_periph *periph) 4472b83592fSScott Long { 4482b83592fSScott Long 4492b83592fSScott Long mtx_assert(periph->sim->mtx, MA_OWNED); 4502b83592fSScott Long 4512b83592fSScott Long periph->flags &= ~CAM_PERIPH_LOCKED; 4522b83592fSScott Long if ((periph->flags & CAM_PERIPH_LOCK_WANTED) != 0) { 4532b83592fSScott Long periph->flags &= ~CAM_PERIPH_LOCK_WANTED; 4542b83592fSScott Long wakeup(periph); 4552b83592fSScott Long } 4562b83592fSScott Long 45724ebf566SEdward Tomasz Napierala cam_periph_release_locked(periph); 4582b83592fSScott Long } 4592b83592fSScott Long 4608b8a9b1dSJustin T. Gibbs /* 4618b8a9b1dSJustin T. Gibbs * Look for the next unit number that is not currently in use for this 4628b8a9b1dSJustin T. Gibbs * peripheral type starting at "newunit". Also exclude unit numbers that 4638b8a9b1dSJustin T. Gibbs * are reserved by for future "hardwiring" unless we already know that this 4648b8a9b1dSJustin T. Gibbs * is a potential wired device. Only assume that the device is "wired" the 4658b8a9b1dSJustin T. Gibbs * first time through the loop since after that we'll be looking at unit 4668b8a9b1dSJustin T. Gibbs * numbers that did not match a wiring entry. 4678b8a9b1dSJustin T. Gibbs */ 4688b8a9b1dSJustin T. Gibbs static u_int 469501468a5SKenneth D. Merry camperiphnextunit(struct periph_driver *p_drv, u_int newunit, int wired, 470501468a5SKenneth D. Merry path_id_t pathid, target_id_t target, lun_id_t lun) 4718b8a9b1dSJustin T. Gibbs { 4728b8a9b1dSJustin T. Gibbs struct cam_periph *periph; 4732398f0cdSPeter Wemm char *periph_name; 4742398f0cdSPeter Wemm int i, val, dunit, r; 4752398f0cdSPeter Wemm const char *dname, *strval; 4768b8a9b1dSJustin T. Gibbs 4778b8a9b1dSJustin T. Gibbs periph_name = p_drv->driver_name; 4788b8a9b1dSJustin T. Gibbs for (;;newunit++) { 4798b8a9b1dSJustin T. Gibbs 4808b8a9b1dSJustin T. Gibbs for (periph = TAILQ_FIRST(&p_drv->units); 4818b8a9b1dSJustin T. Gibbs periph != NULL && periph->unit_number != newunit; 4828b8a9b1dSJustin T. Gibbs periph = TAILQ_NEXT(periph, unit_links)) 4838b8a9b1dSJustin T. Gibbs ; 4848b8a9b1dSJustin T. Gibbs 4858b8a9b1dSJustin T. Gibbs if (periph != NULL && periph->unit_number == newunit) { 4868b8a9b1dSJustin T. Gibbs if (wired != 0) { 487f0d9af51SMatt Jacob xpt_print(periph->path, "Duplicate Wired " 488f0d9af51SMatt Jacob "Device entry!\n"); 489f0d9af51SMatt Jacob xpt_print(periph->path, "Second device (%s " 490f0d9af51SMatt Jacob "device at scbus%d target %d lun %d) will " 491f0d9af51SMatt Jacob "not be wired\n", periph_name, pathid, 492f0d9af51SMatt Jacob target, lun); 4938b8a9b1dSJustin T. Gibbs wired = 0; 4948b8a9b1dSJustin T. Gibbs } 4958b8a9b1dSJustin T. Gibbs continue; 4968b8a9b1dSJustin T. Gibbs } 49775f51904SPeter Wemm if (wired) 49875f51904SPeter Wemm break; 4998b8a9b1dSJustin T. Gibbs 5008b8a9b1dSJustin T. Gibbs /* 5018b8a9b1dSJustin T. Gibbs * Don't match entries like "da 4" as a wired down 5028b8a9b1dSJustin T. Gibbs * device, but do match entries like "da 4 target 5" 5038b8a9b1dSJustin T. Gibbs * or even "da 4 scbus 1". 5048b8a9b1dSJustin T. Gibbs */ 5052398f0cdSPeter Wemm i = 0; 5062398f0cdSPeter Wemm dname = periph_name; 5072398f0cdSPeter Wemm for (;;) { 5082398f0cdSPeter Wemm r = resource_find_dev(&i, dname, &dunit, NULL, NULL); 5092398f0cdSPeter Wemm if (r != 0) 5102398f0cdSPeter Wemm break; 51175f51904SPeter Wemm /* if no "target" and no specific scbus, skip */ 51275f51904SPeter Wemm if (resource_int_value(dname, dunit, "target", &val) && 51375f51904SPeter Wemm (resource_string_value(dname, dunit, "at",&strval)|| 51475f51904SPeter Wemm strcmp(strval, "scbus") == 0)) 51575f51904SPeter Wemm continue; 51675f51904SPeter Wemm if (newunit == dunit) 5178b8a9b1dSJustin T. Gibbs break; 5188b8a9b1dSJustin T. Gibbs } 5192398f0cdSPeter Wemm if (r != 0) 5208b8a9b1dSJustin T. Gibbs break; 5218b8a9b1dSJustin T. Gibbs } 5228b8a9b1dSJustin T. Gibbs return (newunit); 5238b8a9b1dSJustin T. Gibbs } 5248b8a9b1dSJustin T. Gibbs 5258b8a9b1dSJustin T. Gibbs static u_int 5268b8a9b1dSJustin T. Gibbs camperiphunit(struct periph_driver *p_drv, path_id_t pathid, 5278b8a9b1dSJustin T. Gibbs target_id_t target, lun_id_t lun) 5288b8a9b1dSJustin T. Gibbs { 5298b8a9b1dSJustin T. Gibbs u_int unit; 530c1b81613SPeter Wemm int wired, i, val, dunit; 5312398f0cdSPeter Wemm const char *dname, *strval; 5322398f0cdSPeter Wemm char pathbuf[32], *periph_name; 5338b8a9b1dSJustin T. Gibbs 53475f51904SPeter Wemm periph_name = p_drv->driver_name; 53575f51904SPeter Wemm snprintf(pathbuf, sizeof(pathbuf), "scbus%d", pathid); 536c1b81613SPeter Wemm unit = 0; 5372398f0cdSPeter Wemm i = 0; 5382398f0cdSPeter Wemm dname = periph_name; 539c1b81613SPeter Wemm for (wired = 0; resource_find_dev(&i, dname, &dunit, NULL, NULL) == 0; 540c1b81613SPeter Wemm wired = 0) { 54175f51904SPeter Wemm if (resource_string_value(dname, dunit, "at", &strval) == 0) { 54275f51904SPeter Wemm if (strcmp(strval, pathbuf) != 0) 5438b8a9b1dSJustin T. Gibbs continue; 544c1b81613SPeter Wemm wired++; 5458b8a9b1dSJustin T. Gibbs } 54675f51904SPeter Wemm if (resource_int_value(dname, dunit, "target", &val) == 0) { 54775f51904SPeter Wemm if (val != target) 5488b8a9b1dSJustin T. Gibbs continue; 549c1b81613SPeter Wemm wired++; 5508b8a9b1dSJustin T. Gibbs } 55175f51904SPeter Wemm if (resource_int_value(dname, dunit, "lun", &val) == 0) { 55275f51904SPeter Wemm if (val != lun) 5538b8a9b1dSJustin T. Gibbs continue; 554c1b81613SPeter Wemm wired++; 5558b8a9b1dSJustin T. Gibbs } 556c1b81613SPeter Wemm if (wired != 0) { 55775f51904SPeter Wemm unit = dunit; 5588b8a9b1dSJustin T. Gibbs break; 5598b8a9b1dSJustin T. Gibbs } 5608b8a9b1dSJustin T. Gibbs } 5618b8a9b1dSJustin T. Gibbs 5628b8a9b1dSJustin T. Gibbs /* 5638b8a9b1dSJustin T. Gibbs * Either start from 0 looking for the next unit or from 56475f51904SPeter Wemm * the unit number given in the resource config. This way, 5658b8a9b1dSJustin T. Gibbs * if we have wildcard matches, we don't return the same 5668b8a9b1dSJustin T. Gibbs * unit number twice. 5678b8a9b1dSJustin T. Gibbs */ 568c1b81613SPeter Wemm unit = camperiphnextunit(p_drv, unit, wired, pathid, target, lun); 5698b8a9b1dSJustin T. Gibbs 5708b8a9b1dSJustin T. Gibbs return (unit); 5718b8a9b1dSJustin T. Gibbs } 5728b8a9b1dSJustin T. Gibbs 5738b8a9b1dSJustin T. Gibbs void 5748b8a9b1dSJustin T. Gibbs cam_periph_invalidate(struct cam_periph *periph) 5758b8a9b1dSJustin T. Gibbs { 5768b8a9b1dSJustin T. Gibbs 577*22c7d606SAlexander Motin CAM_DEBUG(periph->path, CAM_DEBUG_INFO, ("Periph invalidated\n")); 578ee9c90c7SKenneth D. Merry /* 579ee9c90c7SKenneth D. Merry * We only call this routine the first time a peripheral is 58077dc25ccSScott Long * invalidated. 581ee9c90c7SKenneth D. Merry */ 582ee9c90c7SKenneth D. Merry if (((periph->flags & CAM_PERIPH_INVALID) == 0) 583ee9c90c7SKenneth D. Merry && (periph->periph_oninval != NULL)) 584ee9c90c7SKenneth D. Merry periph->periph_oninval(periph); 585ee9c90c7SKenneth D. Merry 5868b8a9b1dSJustin T. Gibbs periph->flags |= CAM_PERIPH_INVALID; 5878b8a9b1dSJustin T. Gibbs periph->flags &= ~CAM_PERIPH_NEW_DEV_FOUND; 5888b8a9b1dSJustin T. Gibbs 5892b83592fSScott Long xpt_lock_buses(); 5908b8a9b1dSJustin T. Gibbs if (periph->refcount == 0) 5918b8a9b1dSJustin T. Gibbs camperiphfree(periph); 5922b83592fSScott Long xpt_unlock_buses(); 5938b8a9b1dSJustin T. Gibbs } 5948b8a9b1dSJustin T. Gibbs 5958b8a9b1dSJustin T. Gibbs static void 5968b8a9b1dSJustin T. Gibbs camperiphfree(struct cam_periph *periph) 5978b8a9b1dSJustin T. Gibbs { 5988b8a9b1dSJustin T. Gibbs struct periph_driver **p_drv; 5998b8a9b1dSJustin T. Gibbs 6000b7c27b9SPeter Wemm for (p_drv = periph_drivers; *p_drv != NULL; p_drv++) { 6018b8a9b1dSJustin T. Gibbs if (strcmp((*p_drv)->driver_name, periph->periph_name) == 0) 6028b8a9b1dSJustin T. Gibbs break; 6038b8a9b1dSJustin T. Gibbs } 604661658a6SScott Long if (*p_drv == NULL) { 605661658a6SScott Long printf("camperiphfree: attempt to free non-existant periph\n"); 606661658a6SScott Long return; 607661658a6SScott Long } 6088b8a9b1dSJustin T. Gibbs 6098b8a9b1dSJustin T. Gibbs TAILQ_REMOVE(&(*p_drv)->units, periph, unit_links); 6108b8a9b1dSJustin T. Gibbs (*p_drv)->generation++; 611f3548746SScott Long xpt_unlock_buses(); 6128b8a9b1dSJustin T. Gibbs 613f3548746SScott Long if (periph->periph_dtor != NULL) 614f3548746SScott Long periph->periph_dtor(periph); 6158b8a9b1dSJustin T. Gibbs xpt_remove_periph(periph); 616*22c7d606SAlexander Motin CAM_DEBUG(periph->path, CAM_DEBUG_INFO, ("Periph destroyed\n")); 6178b8a9b1dSJustin T. Gibbs 6188b8a9b1dSJustin T. Gibbs if (periph->flags & CAM_PERIPH_NEW_DEV_FOUND) { 6198b8a9b1dSJustin T. Gibbs union ccb ccb; 6208b8a9b1dSJustin T. Gibbs void *arg; 6218b8a9b1dSJustin T. Gibbs 6228b8a9b1dSJustin T. Gibbs switch (periph->deferred_ac) { 6238b8a9b1dSJustin T. Gibbs case AC_FOUND_DEVICE: 6248b8a9b1dSJustin T. Gibbs ccb.ccb_h.func_code = XPT_GDEV_TYPE; 625bbfa4aa1SAlexander Motin xpt_setup_ccb(&ccb.ccb_h, periph->path, CAM_PRIORITY_NORMAL); 6268b8a9b1dSJustin T. Gibbs xpt_action(&ccb); 6278b8a9b1dSJustin T. Gibbs arg = &ccb; 6288b8a9b1dSJustin T. Gibbs break; 6298b8a9b1dSJustin T. Gibbs case AC_PATH_REGISTERED: 6308b8a9b1dSJustin T. Gibbs ccb.ccb_h.func_code = XPT_PATH_INQ; 631bbfa4aa1SAlexander Motin xpt_setup_ccb(&ccb.ccb_h, periph->path, CAM_PRIORITY_NORMAL); 6328b8a9b1dSJustin T. Gibbs xpt_action(&ccb); 6338b8a9b1dSJustin T. Gibbs arg = &ccb; 6348b8a9b1dSJustin T. Gibbs break; 6358b8a9b1dSJustin T. Gibbs default: 6368b8a9b1dSJustin T. Gibbs arg = NULL; 6378b8a9b1dSJustin T. Gibbs break; 6388b8a9b1dSJustin T. Gibbs } 6398b8a9b1dSJustin T. Gibbs periph->deferred_callback(NULL, periph->deferred_ac, 6408b8a9b1dSJustin T. Gibbs periph->path, arg); 6418b8a9b1dSJustin T. Gibbs } 6428b8a9b1dSJustin T. Gibbs xpt_free_path(periph->path); 643362abc44STai-hwa Liang free(periph, M_CAMPERIPH); 644f3548746SScott Long xpt_lock_buses(); 6458b8a9b1dSJustin T. Gibbs } 6468b8a9b1dSJustin T. Gibbs 6478b8a9b1dSJustin T. Gibbs /* 6488b8a9b1dSJustin T. Gibbs * Map user virtual pointers into kernel virtual address space, so we can 6498b8a9b1dSJustin T. Gibbs * access the memory. This won't work on physical pointers, for now it's 6508b8a9b1dSJustin T. Gibbs * up to the caller to check for that. (XXX KDM -- should we do that here 6518b8a9b1dSJustin T. Gibbs * instead?) This also only works for up to MAXPHYS memory. Since we use 6528b8a9b1dSJustin T. Gibbs * buffers to map stuff in and out, we're limited to the buffer size. 6538b8a9b1dSJustin T. Gibbs */ 6548b8a9b1dSJustin T. Gibbs int 6558b8a9b1dSJustin T. Gibbs cam_periph_mapmem(union ccb *ccb, struct cam_periph_map_info *mapinfo) 6568b8a9b1dSJustin T. Gibbs { 6572d5c7e45SMatthew Dillon int numbufs, i, j; 65879d49a06SKenneth D. Merry int flags[CAM_PERIPH_MAXMAPS]; 6598b8a9b1dSJustin T. Gibbs u_int8_t **data_ptrs[CAM_PERIPH_MAXMAPS]; 6608b8a9b1dSJustin T. Gibbs u_int32_t lengths[CAM_PERIPH_MAXMAPS]; 6618b8a9b1dSJustin T. Gibbs u_int32_t dirs[CAM_PERIPH_MAXMAPS]; 66252c9ce25SScott Long /* Some controllers may not be able to handle more data. */ 66352c9ce25SScott Long size_t maxmap = DFLTPHYS; 6648b8a9b1dSJustin T. Gibbs 6658b8a9b1dSJustin T. Gibbs switch(ccb->ccb_h.func_code) { 6668b8a9b1dSJustin T. Gibbs case XPT_DEV_MATCH: 6678b8a9b1dSJustin T. Gibbs if (ccb->cdm.match_buf_len == 0) { 6688b8a9b1dSJustin T. Gibbs printf("cam_periph_mapmem: invalid match buffer " 6698b8a9b1dSJustin T. Gibbs "length 0\n"); 6708b8a9b1dSJustin T. Gibbs return(EINVAL); 6718b8a9b1dSJustin T. Gibbs } 6728b8a9b1dSJustin T. Gibbs if (ccb->cdm.pattern_buf_len > 0) { 6738b8a9b1dSJustin T. Gibbs data_ptrs[0] = (u_int8_t **)&ccb->cdm.patterns; 6748b8a9b1dSJustin T. Gibbs lengths[0] = ccb->cdm.pattern_buf_len; 6758b8a9b1dSJustin T. Gibbs dirs[0] = CAM_DIR_OUT; 6768b8a9b1dSJustin T. Gibbs data_ptrs[1] = (u_int8_t **)&ccb->cdm.matches; 6778b8a9b1dSJustin T. Gibbs lengths[1] = ccb->cdm.match_buf_len; 6788b8a9b1dSJustin T. Gibbs dirs[1] = CAM_DIR_IN; 6798b8a9b1dSJustin T. Gibbs numbufs = 2; 6808b8a9b1dSJustin T. Gibbs } else { 6818b8a9b1dSJustin T. Gibbs data_ptrs[0] = (u_int8_t **)&ccb->cdm.matches; 6828b8a9b1dSJustin T. Gibbs lengths[0] = ccb->cdm.match_buf_len; 6838b8a9b1dSJustin T. Gibbs dirs[0] = CAM_DIR_IN; 6848b8a9b1dSJustin T. Gibbs numbufs = 1; 6858b8a9b1dSJustin T. Gibbs } 68652c9ce25SScott Long /* 68752c9ce25SScott Long * This request will not go to the hardware, no reason 68852c9ce25SScott Long * to be so strict. vmapbuf() is able to map up to MAXPHYS. 68952c9ce25SScott Long */ 69052c9ce25SScott Long maxmap = MAXPHYS; 6918b8a9b1dSJustin T. Gibbs break; 6928b8a9b1dSJustin T. Gibbs case XPT_SCSI_IO: 69387cfaf0eSJustin T. Gibbs case XPT_CONT_TARGET_IO: 6948b8a9b1dSJustin T. Gibbs if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_NONE) 6958b8a9b1dSJustin T. Gibbs return(0); 6968b8a9b1dSJustin T. Gibbs 6978b8a9b1dSJustin T. Gibbs data_ptrs[0] = &ccb->csio.data_ptr; 69879d49a06SKenneth D. Merry lengths[0] = ccb->csio.dxfer_len; 6998b8a9b1dSJustin T. Gibbs dirs[0] = ccb->ccb_h.flags & CAM_DIR_MASK; 7008b8a9b1dSJustin T. Gibbs numbufs = 1; 7018b8a9b1dSJustin T. Gibbs break; 70252c9ce25SScott Long case XPT_ATA_IO: 70352c9ce25SScott Long if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_NONE) 70452c9ce25SScott Long return(0); 70552c9ce25SScott Long 70652c9ce25SScott Long data_ptrs[0] = &ccb->ataio.data_ptr; 70752c9ce25SScott Long lengths[0] = ccb->ataio.dxfer_len; 70852c9ce25SScott Long dirs[0] = ccb->ccb_h.flags & CAM_DIR_MASK; 70952c9ce25SScott Long numbufs = 1; 71052c9ce25SScott Long break; 71106e79492SKenneth D. Merry case XPT_SMP_IO: 71206e79492SKenneth D. Merry data_ptrs[0] = &ccb->smpio.smp_request; 71306e79492SKenneth D. Merry lengths[0] = ccb->smpio.smp_request_len; 71406e79492SKenneth D. Merry dirs[0] = CAM_DIR_OUT; 71506e79492SKenneth D. Merry data_ptrs[1] = &ccb->smpio.smp_response; 71606e79492SKenneth D. Merry lengths[1] = ccb->smpio.smp_response_len; 71706e79492SKenneth D. Merry dirs[1] = CAM_DIR_IN; 71806e79492SKenneth D. Merry numbufs = 2; 71906e79492SKenneth D. Merry break; 7203501942bSJustin T. Gibbs case XPT_DEV_ADVINFO: 7213501942bSJustin T. Gibbs if (ccb->cdai.bufsiz == 0) 7227c103ddeSKenneth D. Merry return (0); 7237c103ddeSKenneth D. Merry 7243501942bSJustin T. Gibbs data_ptrs[0] = (uint8_t **)&ccb->cdai.buf; 7253501942bSJustin T. Gibbs lengths[0] = ccb->cdai.bufsiz; 72606e79492SKenneth D. Merry dirs[0] = CAM_DIR_IN; 72706e79492SKenneth D. Merry numbufs = 1; 7287c103ddeSKenneth D. Merry 7297c103ddeSKenneth D. Merry /* 7307c103ddeSKenneth D. Merry * This request will not go to the hardware, no reason 7317c103ddeSKenneth D. Merry * to be so strict. vmapbuf() is able to map up to MAXPHYS. 7327c103ddeSKenneth D. Merry */ 7337c103ddeSKenneth D. Merry maxmap = MAXPHYS; 73406e79492SKenneth D. Merry break; 7358b8a9b1dSJustin T. Gibbs default: 7368b8a9b1dSJustin T. Gibbs return(EINVAL); 7378b8a9b1dSJustin T. Gibbs break; /* NOTREACHED */ 7388b8a9b1dSJustin T. Gibbs } 7398b8a9b1dSJustin T. Gibbs 7408b8a9b1dSJustin T. Gibbs /* 74179d49a06SKenneth D. Merry * Check the transfer length and permissions first, so we don't 74279d49a06SKenneth D. Merry * have to unmap any previously mapped buffers. 7438b8a9b1dSJustin T. Gibbs */ 7448b8a9b1dSJustin T. Gibbs for (i = 0; i < numbufs; i++) { 74579d49a06SKenneth D. Merry 74679d49a06SKenneth D. Merry flags[i] = 0; 74779d49a06SKenneth D. Merry 74879d49a06SKenneth D. Merry /* 74979d49a06SKenneth D. Merry * The userland data pointer passed in may not be page 75079d49a06SKenneth D. Merry * aligned. vmapbuf() truncates the address to a page 75179d49a06SKenneth D. Merry * boundary, so if the address isn't page aligned, we'll 75279d49a06SKenneth D. Merry * need enough space for the given transfer length, plus 75379d49a06SKenneth D. Merry * whatever extra space is necessary to make it to the page 75479d49a06SKenneth D. Merry * boundary. 75579d49a06SKenneth D. Merry */ 75679d49a06SKenneth D. Merry if ((lengths[i] + 75752c9ce25SScott Long (((vm_offset_t)(*data_ptrs[i])) & PAGE_MASK)) > maxmap){ 7586d7b539aSPeter Wemm printf("cam_periph_mapmem: attempt to map %lu bytes, " 75952c9ce25SScott Long "which is greater than %lu\n", 7606d7b539aSPeter Wemm (long)(lengths[i] + 7616d7b539aSPeter Wemm (((vm_offset_t)(*data_ptrs[i])) & PAGE_MASK)), 76252c9ce25SScott Long (u_long)maxmap); 76379d49a06SKenneth D. Merry return(E2BIG); 76479d49a06SKenneth D. Merry } 7658b8a9b1dSJustin T. Gibbs 766edd24ab7SKenneth D. Merry if (dirs[i] & CAM_DIR_OUT) { 76721144e3bSPoul-Henning Kamp flags[i] = BIO_WRITE; 7688b8a9b1dSJustin T. Gibbs } 7698b8a9b1dSJustin T. Gibbs 770edd24ab7SKenneth D. Merry if (dirs[i] & CAM_DIR_IN) { 77121144e3bSPoul-Henning Kamp flags[i] = BIO_READ; 7728b8a9b1dSJustin T. Gibbs } 7738b8a9b1dSJustin T. Gibbs 77479d49a06SKenneth D. Merry } 77579d49a06SKenneth D. Merry 77679d49a06SKenneth D. Merry /* this keeps the current process from getting swapped */ 77779d49a06SKenneth D. Merry /* 77879d49a06SKenneth D. Merry * XXX KDM should I use P_NOSWAP instead? 77979d49a06SKenneth D. Merry */ 7800cbbb7bfSPeter Wemm PHOLD(curproc); 78179d49a06SKenneth D. Merry 78279d49a06SKenneth D. Merry for (i = 0; i < numbufs; i++) { 7838b8a9b1dSJustin T. Gibbs /* 7848b8a9b1dSJustin T. Gibbs * Get the buffer. 7858b8a9b1dSJustin T. Gibbs */ 7861c7c3c6aSMatthew Dillon mapinfo->bp[i] = getpbuf(NULL); 7878b8a9b1dSJustin T. Gibbs 7888b8a9b1dSJustin T. Gibbs /* save the buffer's data address */ 7898b8a9b1dSJustin T. Gibbs mapinfo->bp[i]->b_saveaddr = mapinfo->bp[i]->b_data; 7908b8a9b1dSJustin T. Gibbs 7918b8a9b1dSJustin T. Gibbs /* put our pointer in the data slot */ 7928b8a9b1dSJustin T. Gibbs mapinfo->bp[i]->b_data = *data_ptrs[i]; 7938b8a9b1dSJustin T. Gibbs 79452c9ce25SScott Long /* set the transfer length, we know it's < MAXPHYS */ 7958b8a9b1dSJustin T. Gibbs mapinfo->bp[i]->b_bufsize = lengths[i]; 7968b8a9b1dSJustin T. Gibbs 79721144e3bSPoul-Henning Kamp /* set the direction */ 79821144e3bSPoul-Henning Kamp mapinfo->bp[i]->b_iocmd = flags[i]; 7998b8a9b1dSJustin T. Gibbs 8002d5c7e45SMatthew Dillon /* 8012d5c7e45SMatthew Dillon * Map the buffer into kernel memory. 8022d5c7e45SMatthew Dillon * 8032d5c7e45SMatthew Dillon * Note that useracc() alone is not a sufficient test. 8042d5c7e45SMatthew Dillon * vmapbuf() can still fail due to a smaller file mapped 8052d5c7e45SMatthew Dillon * into a larger area of VM, or if userland races against 8062d5c7e45SMatthew Dillon * vmapbuf() after the useracc() check. 8072d5c7e45SMatthew Dillon */ 8082d5c7e45SMatthew Dillon if (vmapbuf(mapinfo->bp[i]) < 0) { 8092d5c7e45SMatthew Dillon for (j = 0; j < i; ++j) { 8102d5c7e45SMatthew Dillon *data_ptrs[j] = mapinfo->bp[j]->b_saveaddr; 8118373f36dSAlan Cox vunmapbuf(mapinfo->bp[j]); 8122d5c7e45SMatthew Dillon relpbuf(mapinfo->bp[j], NULL); 8132d5c7e45SMatthew Dillon } 8148373f36dSAlan Cox relpbuf(mapinfo->bp[i], NULL); 8152d5c7e45SMatthew Dillon PRELE(curproc); 8162d5c7e45SMatthew Dillon return(EACCES); 8172d5c7e45SMatthew Dillon } 8188b8a9b1dSJustin T. Gibbs 8198b8a9b1dSJustin T. Gibbs /* set our pointer to the new mapped area */ 8208b8a9b1dSJustin T. Gibbs *data_ptrs[i] = mapinfo->bp[i]->b_data; 8218b8a9b1dSJustin T. Gibbs 8228b8a9b1dSJustin T. Gibbs mapinfo->num_bufs_used++; 8238b8a9b1dSJustin T. Gibbs } 8248b8a9b1dSJustin T. Gibbs 825a62525f3SMatt Jacob /* 826a62525f3SMatt Jacob * Now that we've gotten this far, change ownership to the kernel 827a62525f3SMatt Jacob * of the buffers so that we don't run afoul of returning to user 828a62525f3SMatt Jacob * space with locks (on the buffer) held. 829a62525f3SMatt Jacob */ 830a62525f3SMatt Jacob for (i = 0; i < numbufs; i++) { 831a62525f3SMatt Jacob BUF_KERNPROC(mapinfo->bp[i]); 832a62525f3SMatt Jacob } 833a62525f3SMatt Jacob 834a62525f3SMatt Jacob 8358b8a9b1dSJustin T. Gibbs return(0); 8368b8a9b1dSJustin T. Gibbs } 8378b8a9b1dSJustin T. Gibbs 8388b8a9b1dSJustin T. Gibbs /* 8398b8a9b1dSJustin T. Gibbs * Unmap memory segments mapped into kernel virtual address space by 8408b8a9b1dSJustin T. Gibbs * cam_periph_mapmem(). 8418b8a9b1dSJustin T. Gibbs */ 8428b8a9b1dSJustin T. Gibbs void 8438b8a9b1dSJustin T. Gibbs cam_periph_unmapmem(union ccb *ccb, struct cam_periph_map_info *mapinfo) 8448b8a9b1dSJustin T. Gibbs { 8458b8a9b1dSJustin T. Gibbs int numbufs, i; 8468b8a9b1dSJustin T. Gibbs u_int8_t **data_ptrs[CAM_PERIPH_MAXMAPS]; 8478b8a9b1dSJustin T. Gibbs 8488b8a9b1dSJustin T. Gibbs if (mapinfo->num_bufs_used <= 0) { 8498b8a9b1dSJustin T. Gibbs /* allow ourselves to be swapped once again */ 8500cbbb7bfSPeter Wemm PRELE(curproc); 8518b8a9b1dSJustin T. Gibbs return; 8528b8a9b1dSJustin T. Gibbs } 8538b8a9b1dSJustin T. Gibbs 8548b8a9b1dSJustin T. Gibbs switch (ccb->ccb_h.func_code) { 8558b8a9b1dSJustin T. Gibbs case XPT_DEV_MATCH: 8568b8a9b1dSJustin T. Gibbs numbufs = min(mapinfo->num_bufs_used, 2); 8578b8a9b1dSJustin T. Gibbs 8588b8a9b1dSJustin T. Gibbs if (numbufs == 1) { 8598b8a9b1dSJustin T. Gibbs data_ptrs[0] = (u_int8_t **)&ccb->cdm.matches; 8608b8a9b1dSJustin T. Gibbs } else { 8618b8a9b1dSJustin T. Gibbs data_ptrs[0] = (u_int8_t **)&ccb->cdm.patterns; 8628b8a9b1dSJustin T. Gibbs data_ptrs[1] = (u_int8_t **)&ccb->cdm.matches; 8638b8a9b1dSJustin T. Gibbs } 8648b8a9b1dSJustin T. Gibbs break; 8658b8a9b1dSJustin T. Gibbs case XPT_SCSI_IO: 8669911ecf9SJustin T. Gibbs case XPT_CONT_TARGET_IO: 8678b8a9b1dSJustin T. Gibbs data_ptrs[0] = &ccb->csio.data_ptr; 8688b8a9b1dSJustin T. Gibbs numbufs = min(mapinfo->num_bufs_used, 1); 8698b8a9b1dSJustin T. Gibbs break; 87052c9ce25SScott Long case XPT_ATA_IO: 87152c9ce25SScott Long data_ptrs[0] = &ccb->ataio.data_ptr; 87252c9ce25SScott Long numbufs = min(mapinfo->num_bufs_used, 1); 87352c9ce25SScott Long break; 87406e79492SKenneth D. Merry case XPT_SMP_IO: 87506e79492SKenneth D. Merry numbufs = min(mapinfo->num_bufs_used, 2); 87606e79492SKenneth D. Merry data_ptrs[0] = &ccb->smpio.smp_request; 87706e79492SKenneth D. Merry data_ptrs[1] = &ccb->smpio.smp_response; 87806e79492SKenneth D. Merry break; 8793501942bSJustin T. Gibbs case XPT_DEV_ADVINFO: 88006e79492SKenneth D. Merry numbufs = min(mapinfo->num_bufs_used, 1); 8813501942bSJustin T. Gibbs data_ptrs[0] = (uint8_t **)&ccb->cdai.buf; 88206e79492SKenneth D. Merry break; 8838b8a9b1dSJustin T. Gibbs default: 8848b8a9b1dSJustin T. Gibbs /* allow ourselves to be swapped once again */ 8850cbbb7bfSPeter Wemm PRELE(curproc); 8868b8a9b1dSJustin T. Gibbs return; 8878b8a9b1dSJustin T. Gibbs break; /* NOTREACHED */ 8888b8a9b1dSJustin T. Gibbs } 8898b8a9b1dSJustin T. Gibbs 8908b8a9b1dSJustin T. Gibbs for (i = 0; i < numbufs; i++) { 8918b8a9b1dSJustin T. Gibbs /* Set the user's pointer back to the original value */ 8928b8a9b1dSJustin T. Gibbs *data_ptrs[i] = mapinfo->bp[i]->b_saveaddr; 8938b8a9b1dSJustin T. Gibbs 8948b8a9b1dSJustin T. Gibbs /* unmap the buffer */ 8958b8a9b1dSJustin T. Gibbs vunmapbuf(mapinfo->bp[i]); 8968b8a9b1dSJustin T. Gibbs 8978b8a9b1dSJustin T. Gibbs /* release the buffer */ 8981c7c3c6aSMatthew Dillon relpbuf(mapinfo->bp[i], NULL); 8998b8a9b1dSJustin T. Gibbs } 9008b8a9b1dSJustin T. Gibbs 9018b8a9b1dSJustin T. Gibbs /* allow ourselves to be swapped once again */ 9020cbbb7bfSPeter Wemm PRELE(curproc); 9038b8a9b1dSJustin T. Gibbs } 9048b8a9b1dSJustin T. Gibbs 9058b8a9b1dSJustin T. Gibbs union ccb * 9068b8a9b1dSJustin T. Gibbs cam_periph_getccb(struct cam_periph *periph, u_int32_t priority) 9078b8a9b1dSJustin T. Gibbs { 9088b8a9b1dSJustin T. Gibbs struct ccb_hdr *ccb_h; 9098b8a9b1dSJustin T. Gibbs 9102b83592fSScott Long mtx_assert(periph->sim->mtx, MA_OWNED); 9118b8a9b1dSJustin T. Gibbs CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering cdgetccb\n")); 9128b8a9b1dSJustin T. Gibbs 913fc2ffbe6SPoul-Henning Kamp while (SLIST_FIRST(&periph->ccb_list) == NULL) { 9148b8a9b1dSJustin T. Gibbs if (periph->immediate_priority > priority) 9158b8a9b1dSJustin T. Gibbs periph->immediate_priority = priority; 9168b8a9b1dSJustin T. Gibbs xpt_schedule(periph, priority); 917fc2ffbe6SPoul-Henning Kamp if ((SLIST_FIRST(&periph->ccb_list) != NULL) 918fc2ffbe6SPoul-Henning Kamp && (SLIST_FIRST(&periph->ccb_list)->pinfo.priority == priority)) 9198b8a9b1dSJustin T. Gibbs break; 9202b83592fSScott Long mtx_assert(periph->sim->mtx, MA_OWNED); 9211fa738c2SJohn Baldwin mtx_sleep(&periph->ccb_list, periph->sim->mtx, PRIBIO, "cgticb", 9221fa738c2SJohn Baldwin 0); 9238b8a9b1dSJustin T. Gibbs } 9248b8a9b1dSJustin T. Gibbs 925fc2ffbe6SPoul-Henning Kamp ccb_h = SLIST_FIRST(&periph->ccb_list); 9268b8a9b1dSJustin T. Gibbs SLIST_REMOVE_HEAD(&periph->ccb_list, periph_links.sle); 9278b8a9b1dSJustin T. Gibbs return ((union ccb *)ccb_h); 9288b8a9b1dSJustin T. Gibbs } 9298b8a9b1dSJustin T. Gibbs 9308b8a9b1dSJustin T. Gibbs void 9318b8a9b1dSJustin T. Gibbs cam_periph_ccbwait(union ccb *ccb) 9328b8a9b1dSJustin T. Gibbs { 9332b83592fSScott Long struct cam_sim *sim; 9348b8a9b1dSJustin T. Gibbs 9352b83592fSScott Long sim = xpt_path_sim(ccb->ccb_h.path); 9368b8a9b1dSJustin T. Gibbs if ((ccb->ccb_h.pinfo.index != CAM_UNQUEUED_INDEX) 9378b8a9b1dSJustin T. Gibbs || ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_INPROG)) 9381fa738c2SJohn Baldwin mtx_sleep(&ccb->ccb_h.cbfcnp, sim->mtx, PRIBIO, "cbwait", 0); 9398b8a9b1dSJustin T. Gibbs } 9408b8a9b1dSJustin T. Gibbs 9418b8a9b1dSJustin T. Gibbs int 942571e8e20SScott Long cam_periph_ioctl(struct cam_periph *periph, u_long cmd, caddr_t addr, 9438b8a9b1dSJustin T. Gibbs int (*error_routine)(union ccb *ccb, 9448b8a9b1dSJustin T. Gibbs cam_flags camflags, 9458b8a9b1dSJustin T. Gibbs u_int32_t sense_flags)) 9468b8a9b1dSJustin T. Gibbs { 9478b8a9b1dSJustin T. Gibbs union ccb *ccb; 9488b8a9b1dSJustin T. Gibbs int error; 9498b8a9b1dSJustin T. Gibbs int found; 9508b8a9b1dSJustin T. Gibbs 9518b8a9b1dSJustin T. Gibbs error = found = 0; 9528b8a9b1dSJustin T. Gibbs 9538b8a9b1dSJustin T. Gibbs switch(cmd){ 9548b8a9b1dSJustin T. Gibbs case CAMGETPASSTHRU: 955bbfa4aa1SAlexander Motin ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL); 9568b8a9b1dSJustin T. Gibbs xpt_setup_ccb(&ccb->ccb_h, 9578b8a9b1dSJustin T. Gibbs ccb->ccb_h.path, 958bbfa4aa1SAlexander Motin CAM_PRIORITY_NORMAL); 9598b8a9b1dSJustin T. Gibbs ccb->ccb_h.func_code = XPT_GDEVLIST; 9608b8a9b1dSJustin T. Gibbs 9618b8a9b1dSJustin T. Gibbs /* 9628b8a9b1dSJustin T. Gibbs * Basically, the point of this is that we go through 9638b8a9b1dSJustin T. Gibbs * getting the list of devices, until we find a passthrough 9648b8a9b1dSJustin T. Gibbs * device. In the current version of the CAM code, the 9658b8a9b1dSJustin T. Gibbs * only way to determine what type of device we're dealing 9668b8a9b1dSJustin T. Gibbs * with is by its name. 9678b8a9b1dSJustin T. Gibbs */ 9688b8a9b1dSJustin T. Gibbs while (found == 0) { 9698b8a9b1dSJustin T. Gibbs ccb->cgdl.index = 0; 9708b8a9b1dSJustin T. Gibbs ccb->cgdl.status = CAM_GDEVLIST_MORE_DEVS; 9718b8a9b1dSJustin T. Gibbs while (ccb->cgdl.status == CAM_GDEVLIST_MORE_DEVS) { 9728b8a9b1dSJustin T. Gibbs 9738b8a9b1dSJustin T. Gibbs /* we want the next device in the list */ 9748b8a9b1dSJustin T. Gibbs xpt_action(ccb); 9758b8a9b1dSJustin T. Gibbs if (strncmp(ccb->cgdl.periph_name, 9768b8a9b1dSJustin T. Gibbs "pass", 4) == 0){ 9778b8a9b1dSJustin T. Gibbs found = 1; 9788b8a9b1dSJustin T. Gibbs break; 9798b8a9b1dSJustin T. Gibbs } 9808b8a9b1dSJustin T. Gibbs } 9818b8a9b1dSJustin T. Gibbs if ((ccb->cgdl.status == CAM_GDEVLIST_LAST_DEVICE) && 9828b8a9b1dSJustin T. Gibbs (found == 0)) { 9838b8a9b1dSJustin T. Gibbs ccb->cgdl.periph_name[0] = '\0'; 9848b8a9b1dSJustin T. Gibbs ccb->cgdl.unit_number = 0; 9858b8a9b1dSJustin T. Gibbs break; 9868b8a9b1dSJustin T. Gibbs } 9878b8a9b1dSJustin T. Gibbs } 9888b8a9b1dSJustin T. Gibbs 9898b8a9b1dSJustin T. Gibbs /* copy the result back out */ 9908b8a9b1dSJustin T. Gibbs bcopy(ccb, addr, sizeof(union ccb)); 9918b8a9b1dSJustin T. Gibbs 9928b8a9b1dSJustin T. Gibbs /* and release the ccb */ 9938b8a9b1dSJustin T. Gibbs xpt_release_ccb(ccb); 9948b8a9b1dSJustin T. Gibbs 9958b8a9b1dSJustin T. Gibbs break; 9968b8a9b1dSJustin T. Gibbs default: 9978b8a9b1dSJustin T. Gibbs error = ENOTTY; 9988b8a9b1dSJustin T. Gibbs break; 9998b8a9b1dSJustin T. Gibbs } 10008b8a9b1dSJustin T. Gibbs return(error); 10018b8a9b1dSJustin T. Gibbs } 10028b8a9b1dSJustin T. Gibbs 10038b8a9b1dSJustin T. Gibbs int 10048b8a9b1dSJustin T. Gibbs cam_periph_runccb(union ccb *ccb, 10058b8a9b1dSJustin T. Gibbs int (*error_routine)(union ccb *ccb, 10068b8a9b1dSJustin T. Gibbs cam_flags camflags, 10078b8a9b1dSJustin T. Gibbs u_int32_t sense_flags), 10088b8a9b1dSJustin T. Gibbs cam_flags camflags, u_int32_t sense_flags, 10098b8a9b1dSJustin T. Gibbs struct devstat *ds) 10108b8a9b1dSJustin T. Gibbs { 10112b83592fSScott Long struct cam_sim *sim; 10128b8a9b1dSJustin T. Gibbs int error; 10138b8a9b1dSJustin T. Gibbs 10148b8a9b1dSJustin T. Gibbs error = 0; 10152b83592fSScott Long sim = xpt_path_sim(ccb->ccb_h.path); 10162b83592fSScott Long mtx_assert(sim->mtx, MA_OWNED); 10178b8a9b1dSJustin T. Gibbs 10188b8a9b1dSJustin T. Gibbs /* 10198b8a9b1dSJustin T. Gibbs * If the user has supplied a stats structure, and if we understand 10208b8a9b1dSJustin T. Gibbs * this particular type of ccb, record the transaction start. 10218b8a9b1dSJustin T. Gibbs */ 1022379ad358SAlexander Motin if ((ds != NULL) && (ccb->ccb_h.func_code == XPT_SCSI_IO || 1023379ad358SAlexander Motin ccb->ccb_h.func_code == XPT_ATA_IO)) 10247194d335SPoul-Henning Kamp devstat_start_transaction(ds, NULL); 10258b8a9b1dSJustin T. Gibbs 10268b8a9b1dSJustin T. Gibbs xpt_action(ccb); 10278b8a9b1dSJustin T. Gibbs 10288b8a9b1dSJustin T. Gibbs do { 10298b8a9b1dSJustin T. Gibbs cam_periph_ccbwait(ccb); 10308b8a9b1dSJustin T. Gibbs if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) 10318b8a9b1dSJustin T. Gibbs error = 0; 10328b8a9b1dSJustin T. Gibbs else if (error_routine != NULL) 10338b8a9b1dSJustin T. Gibbs error = (*error_routine)(ccb, camflags, sense_flags); 10348b8a9b1dSJustin T. Gibbs else 10358b8a9b1dSJustin T. Gibbs error = 0; 10368b8a9b1dSJustin T. Gibbs 10378b8a9b1dSJustin T. Gibbs } while (error == ERESTART); 10388b8a9b1dSJustin T. Gibbs 103983c5d981SAlexander Motin if ((ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) { 10408b8a9b1dSJustin T. Gibbs cam_release_devq(ccb->ccb_h.path, 10418b8a9b1dSJustin T. Gibbs /* relsim_flags */0, 10428b8a9b1dSJustin T. Gibbs /* openings */0, 10438b8a9b1dSJustin T. Gibbs /* timeout */0, 10448b8a9b1dSJustin T. Gibbs /* getcount_only */ FALSE); 104583c5d981SAlexander Motin ccb->ccb_h.status &= ~CAM_DEV_QFRZN; 104683c5d981SAlexander Motin } 10478b8a9b1dSJustin T. Gibbs 1048379ad358SAlexander Motin if (ds != NULL) { 1049379ad358SAlexander Motin if (ccb->ccb_h.func_code == XPT_SCSI_IO) { 10508b8a9b1dSJustin T. Gibbs devstat_end_transaction(ds, 10518b8a9b1dSJustin T. Gibbs ccb->csio.dxfer_len, 1052379ad358SAlexander Motin ccb->csio.tag_action & 0x3, 10538b8a9b1dSJustin T. Gibbs ((ccb->ccb_h.flags & CAM_DIR_MASK) == 10548b8a9b1dSJustin T. Gibbs CAM_DIR_NONE) ? DEVSTAT_NO_DATA : 10558b8a9b1dSJustin T. Gibbs (ccb->ccb_h.flags & CAM_DIR_OUT) ? 10568b8a9b1dSJustin T. Gibbs DEVSTAT_WRITE : 10577194d335SPoul-Henning Kamp DEVSTAT_READ, NULL, NULL); 1058379ad358SAlexander Motin } else if (ccb->ccb_h.func_code == XPT_ATA_IO) { 1059379ad358SAlexander Motin devstat_end_transaction(ds, 1060379ad358SAlexander Motin ccb->ataio.dxfer_len, 1061379ad358SAlexander Motin ccb->ataio.tag_action & 0x3, 1062379ad358SAlexander Motin ((ccb->ccb_h.flags & CAM_DIR_MASK) == 1063379ad358SAlexander Motin CAM_DIR_NONE) ? DEVSTAT_NO_DATA : 1064379ad358SAlexander Motin (ccb->ccb_h.flags & CAM_DIR_OUT) ? 1065379ad358SAlexander Motin DEVSTAT_WRITE : 1066379ad358SAlexander Motin DEVSTAT_READ, NULL, NULL); 1067379ad358SAlexander Motin } 1068379ad358SAlexander Motin } 10698b8a9b1dSJustin T. Gibbs 10708b8a9b1dSJustin T. Gibbs return(error); 10718b8a9b1dSJustin T. Gibbs } 10728b8a9b1dSJustin T. Gibbs 107387cfaf0eSJustin T. Gibbs void 107487cfaf0eSJustin T. Gibbs cam_freeze_devq(struct cam_path *path) 107587cfaf0eSJustin T. Gibbs { 107687cfaf0eSJustin T. Gibbs 107783c5d981SAlexander Motin cam_freeze_devq_arg(path, 0, 0); 107883c5d981SAlexander Motin } 107983c5d981SAlexander Motin 108083c5d981SAlexander Motin void 108183c5d981SAlexander Motin cam_freeze_devq_arg(struct cam_path *path, uint32_t flags, uint32_t arg) 108283c5d981SAlexander Motin { 108383c5d981SAlexander Motin struct ccb_relsim crs; 108483c5d981SAlexander Motin 108583c5d981SAlexander Motin xpt_setup_ccb(&crs.ccb_h, path, CAM_PRIORITY_NONE); 108683c5d981SAlexander Motin crs.ccb_h.func_code = XPT_FREEZE_QUEUE; 108783c5d981SAlexander Motin crs.release_flags = flags; 108883c5d981SAlexander Motin crs.openings = arg; 108983c5d981SAlexander Motin crs.release_timeout = arg; 109083c5d981SAlexander Motin xpt_action((union ccb *)&crs); 109187cfaf0eSJustin T. Gibbs } 109287cfaf0eSJustin T. Gibbs 10938b8a9b1dSJustin T. Gibbs u_int32_t 10948b8a9b1dSJustin T. Gibbs cam_release_devq(struct cam_path *path, u_int32_t relsim_flags, 109583c5d981SAlexander Motin u_int32_t openings, u_int32_t arg, 10968b8a9b1dSJustin T. Gibbs int getcount_only) 10978b8a9b1dSJustin T. Gibbs { 10988b8a9b1dSJustin T. Gibbs struct ccb_relsim crs; 10998b8a9b1dSJustin T. Gibbs 1100bbfa4aa1SAlexander Motin xpt_setup_ccb(&crs.ccb_h, path, CAM_PRIORITY_NORMAL); 11018b8a9b1dSJustin T. Gibbs crs.ccb_h.func_code = XPT_REL_SIMQ; 11028b8a9b1dSJustin T. Gibbs crs.ccb_h.flags = getcount_only ? CAM_DEV_QFREEZE : 0; 11038b8a9b1dSJustin T. Gibbs crs.release_flags = relsim_flags; 11048b8a9b1dSJustin T. Gibbs crs.openings = openings; 110583c5d981SAlexander Motin crs.release_timeout = arg; 11068b8a9b1dSJustin T. Gibbs xpt_action((union ccb *)&crs); 11078b8a9b1dSJustin T. Gibbs return (crs.qfrozen_cnt); 11088b8a9b1dSJustin T. Gibbs } 11098b8a9b1dSJustin T. Gibbs 11108b8a9b1dSJustin T. Gibbs #define saved_ccb_ptr ppriv_ptr0 111183c5d981SAlexander Motin #define recovery_depth ppriv_field1 11128b8a9b1dSJustin T. Gibbs static void 111383c5d981SAlexander Motin camperiphsensedone(struct cam_periph *periph, union ccb *done_ccb) 11148b8a9b1dSJustin T. Gibbs { 111583c5d981SAlexander Motin union ccb *saved_ccb = (union ccb *)done_ccb->ccb_h.saved_ccb_ptr; 11168b8a9b1dSJustin T. Gibbs cam_status status; 1117d84c90a6SAlexander Motin int frozen = 0; 111883c5d981SAlexander Motin int depth = done_ccb->ccb_h.recovery_depth; 11198b8a9b1dSJustin T. Gibbs 11208b8a9b1dSJustin T. Gibbs status = done_ccb->ccb_h.status; 1121d84c90a6SAlexander Motin if (status & CAM_DEV_QFRZN) { 1122d84c90a6SAlexander Motin frozen = 1; 1123d84c90a6SAlexander Motin /* 1124d84c90a6SAlexander Motin * Clear freeze flag now for case of retry, 1125d84c90a6SAlexander Motin * freeze will be dropped later. 1126d84c90a6SAlexander Motin */ 1127d84c90a6SAlexander Motin done_ccb->ccb_h.status &= ~CAM_DEV_QFRZN; 1128d84c90a6SAlexander Motin } 11298b8a9b1dSJustin T. Gibbs status &= CAM_STATUS_MASK; 113083c5d981SAlexander Motin switch (status) { 113183c5d981SAlexander Motin case CAM_REQ_CMP: 113283c5d981SAlexander Motin { 11331cc052e8SKenneth D. Merry int error_code, sense_key, asc, ascq; 11341cc052e8SKenneth D. Merry 11351cc052e8SKenneth D. Merry scsi_extract_sense_len(&saved_ccb->csio.sense_data, 11361cc052e8SKenneth D. Merry saved_ccb->csio.sense_len - 11371cc052e8SKenneth D. Merry saved_ccb->csio.sense_resid, 11381cc052e8SKenneth D. Merry &error_code, &sense_key, &asc, &ascq, 11391cc052e8SKenneth D. Merry /*show_errors*/ 1); 114083c5d981SAlexander Motin /* 114183c5d981SAlexander Motin * If we manually retrieved sense into a CCB and got 114283c5d981SAlexander Motin * something other than "NO SENSE" send the updated CCB 114383c5d981SAlexander Motin * back to the client via xpt_done() to be processed via 114483c5d981SAlexander Motin * the error recovery code again. 114583c5d981SAlexander Motin */ 11461cc052e8SKenneth D. Merry if ((sense_key != -1) 11471cc052e8SKenneth D. Merry && (sense_key != SSD_KEY_NO_SENSE)) { 11481cc052e8SKenneth D. Merry saved_ccb->ccb_h.status |= CAM_AUTOSNS_VALID; 114983c5d981SAlexander Motin } else { 11501cc052e8SKenneth D. Merry saved_ccb->ccb_h.status &= ~CAM_STATUS_MASK; 11511cc052e8SKenneth D. Merry saved_ccb->ccb_h.status |= CAM_AUTOSENSE_FAIL; 115283c5d981SAlexander Motin } 11530bb3f1d3SAlexander Motin saved_ccb->csio.sense_resid = done_ccb->csio.resid; 115483c5d981SAlexander Motin bcopy(saved_ccb, done_ccb, sizeof(union ccb)); 115583c5d981SAlexander Motin xpt_free_ccb(saved_ccb); 115683c5d981SAlexander Motin break; 115783c5d981SAlexander Motin } 115883c5d981SAlexander Motin default: 115983c5d981SAlexander Motin bcopy(saved_ccb, done_ccb, sizeof(union ccb)); 116083c5d981SAlexander Motin xpt_free_ccb(saved_ccb); 116183c5d981SAlexander Motin done_ccb->ccb_h.status &= ~CAM_STATUS_MASK; 116283c5d981SAlexander Motin done_ccb->ccb_h.status |= CAM_AUTOSENSE_FAIL; 116383c5d981SAlexander Motin break; 116483c5d981SAlexander Motin } 116583c5d981SAlexander Motin periph->flags &= ~CAM_PERIPH_SENSE_INPROG; 116683c5d981SAlexander Motin /* 116783c5d981SAlexander Motin * If it is the end of recovery, drop freeze, taken due to 116883c5d981SAlexander Motin * CAM_DEV_QFREEZE flag, set on recovery request. 116983c5d981SAlexander Motin */ 117083c5d981SAlexander Motin if (depth == 0) { 117183c5d981SAlexander Motin cam_release_devq(done_ccb->ccb_h.path, 117283c5d981SAlexander Motin /*relsim_flags*/0, 117383c5d981SAlexander Motin /*openings*/0, 117483c5d981SAlexander Motin /*timeout*/0, 117583c5d981SAlexander Motin /*getcount_only*/0); 117683c5d981SAlexander Motin } 117783c5d981SAlexander Motin /* 117883c5d981SAlexander Motin * Copy frozen flag from recovery request if it is set there 117983c5d981SAlexander Motin * for some reason. 118083c5d981SAlexander Motin */ 118183c5d981SAlexander Motin if (frozen != 0) 118283c5d981SAlexander Motin done_ccb->ccb_h.status |= CAM_DEV_QFRZN; 118383c5d981SAlexander Motin (*done_ccb->ccb_h.cbfcnp)(periph, done_ccb); 118483c5d981SAlexander Motin } 118583c5d981SAlexander Motin 118683c5d981SAlexander Motin static void 118783c5d981SAlexander Motin camperiphdone(struct cam_periph *periph, union ccb *done_ccb) 118883c5d981SAlexander Motin { 118983c5d981SAlexander Motin union ccb *saved_ccb, *save_ccb; 119083c5d981SAlexander Motin cam_status status; 119183c5d981SAlexander Motin int frozen = 0; 119283c5d981SAlexander Motin struct scsi_start_stop_unit *scsi_cmd; 119383c5d981SAlexander Motin u_int32_t relsim_flags, timeout; 119483c5d981SAlexander Motin 119583c5d981SAlexander Motin status = done_ccb->ccb_h.status; 119683c5d981SAlexander Motin if (status & CAM_DEV_QFRZN) { 119783c5d981SAlexander Motin frozen = 1; 119883c5d981SAlexander Motin /* 119983c5d981SAlexander Motin * Clear freeze flag now for case of retry, 120083c5d981SAlexander Motin * freeze will be dropped later. 120183c5d981SAlexander Motin */ 120283c5d981SAlexander Motin done_ccb->ccb_h.status &= ~CAM_DEV_QFRZN; 120383c5d981SAlexander Motin } 12048b8a9b1dSJustin T. Gibbs 12058b8a9b1dSJustin T. Gibbs timeout = 0; 12068b8a9b1dSJustin T. Gibbs relsim_flags = 0; 12073393f8daSKenneth D. Merry saved_ccb = (union ccb *)done_ccb->ccb_h.saved_ccb_ptr; 12088b8a9b1dSJustin T. Gibbs 120983c5d981SAlexander Motin switch (status & CAM_STATUS_MASK) { 12108b8a9b1dSJustin T. Gibbs case CAM_REQ_CMP: 12113393f8daSKenneth D. Merry { 12128b8a9b1dSJustin T. Gibbs /* 12138b8a9b1dSJustin T. Gibbs * If we have successfully taken a device from the not 12143393f8daSKenneth D. Merry * ready to ready state, re-scan the device and re-get 12153393f8daSKenneth D. Merry * the inquiry information. Many devices (mostly disks) 12163393f8daSKenneth D. Merry * don't properly report their inquiry information unless 12173393f8daSKenneth D. Merry * they are spun up. 12188b8a9b1dSJustin T. Gibbs */ 12198b8a9b1dSJustin T. Gibbs scsi_cmd = (struct scsi_start_stop_unit *) 12208b8a9b1dSJustin T. Gibbs &done_ccb->csio.cdb_io.cdb_bytes; 12218b8a9b1dSJustin T. Gibbs 12228b8a9b1dSJustin T. Gibbs if (scsi_cmd->opcode == START_STOP_UNIT) 12238b8a9b1dSJustin T. Gibbs xpt_async(AC_INQ_CHANGED, 12248b8a9b1dSJustin T. Gibbs done_ccb->ccb_h.path, NULL); 122583c5d981SAlexander Motin goto final; 12263393f8daSKenneth D. Merry } 12278b8a9b1dSJustin T. Gibbs case CAM_SCSI_STATUS_ERROR: 12288b8a9b1dSJustin T. Gibbs scsi_cmd = (struct scsi_start_stop_unit *) 12298b8a9b1dSJustin T. Gibbs &done_ccb->csio.cdb_io.cdb_bytes; 123083c5d981SAlexander Motin if (status & CAM_AUTOSNS_VALID) { 123165afe1f4SKenneth D. Merry struct ccb_getdev cgd; 12328b8a9b1dSJustin T. Gibbs struct scsi_sense_data *sense; 12331cc052e8SKenneth D. Merry int error_code, sense_key, asc, ascq, sense_len; 123465afe1f4SKenneth D. Merry scsi_sense_action err_action; 12358b8a9b1dSJustin T. Gibbs 12368b8a9b1dSJustin T. Gibbs sense = &done_ccb->csio.sense_data; 12371cc052e8SKenneth D. Merry sense_len = done_ccb->csio.sense_len - 12381cc052e8SKenneth D. Merry done_ccb->csio.sense_resid; 12391cc052e8SKenneth D. Merry scsi_extract_sense_len(sense, sense_len, &error_code, 12401cc052e8SKenneth D. Merry &sense_key, &asc, &ascq, 12411cc052e8SKenneth D. Merry /*show_errors*/ 1); 12428b8a9b1dSJustin T. Gibbs /* 124365afe1f4SKenneth D. Merry * Grab the inquiry data for this device. 124465afe1f4SKenneth D. Merry */ 124565afe1f4SKenneth D. Merry xpt_setup_ccb(&cgd.ccb_h, done_ccb->ccb_h.path, 1246bbfa4aa1SAlexander Motin CAM_PRIORITY_NORMAL); 124765afe1f4SKenneth D. Merry cgd.ccb_h.func_code = XPT_GDEV_TYPE; 124865afe1f4SKenneth D. Merry xpt_action((union ccb *)&cgd); 124965afe1f4SKenneth D. Merry err_action = scsi_error_action(&done_ccb->csio, 125065afe1f4SKenneth D. Merry &cgd.inq_data, 0); 125165afe1f4SKenneth D. Merry /* 12528b8a9b1dSJustin T. Gibbs * If the error is "invalid field in CDB", 12538b8a9b1dSJustin T. Gibbs * and the load/eject flag is set, turn the 12548b8a9b1dSJustin T. Gibbs * flag off and try again. This is just in 12558b8a9b1dSJustin T. Gibbs * case the drive in question barfs on the 12568b8a9b1dSJustin T. Gibbs * load eject flag. The CAM code should set 12578b8a9b1dSJustin T. Gibbs * the load/eject flag by default for 12588b8a9b1dSJustin T. Gibbs * removable media. 12598b8a9b1dSJustin T. Gibbs */ 12608b8a9b1dSJustin T. Gibbs /* XXX KDM 12618b8a9b1dSJustin T. Gibbs * Should we check to see what the specific 12628b8a9b1dSJustin T. Gibbs * scsi status is?? Or does it not matter 12638b8a9b1dSJustin T. Gibbs * since we already know that there was an 12648b8a9b1dSJustin T. Gibbs * error, and we know what the specific 12658b8a9b1dSJustin T. Gibbs * error code was, and we know what the 12668b8a9b1dSJustin T. Gibbs * opcode is.. 12678b8a9b1dSJustin T. Gibbs */ 12688b8a9b1dSJustin T. Gibbs if ((scsi_cmd->opcode == START_STOP_UNIT) && 12698b8a9b1dSJustin T. Gibbs ((scsi_cmd->how & SSS_LOEJ) != 0) && 12708b8a9b1dSJustin T. Gibbs (asc == 0x24) && (ascq == 0x00) && 12718b8a9b1dSJustin T. Gibbs (done_ccb->ccb_h.retry_count > 0)) { 12728b8a9b1dSJustin T. Gibbs 12738b8a9b1dSJustin T. Gibbs scsi_cmd->how &= ~SSS_LOEJ; 12748b8a9b1dSJustin T. Gibbs xpt_action(done_ccb); 127565afe1f4SKenneth D. Merry } else if ((done_ccb->ccb_h.retry_count > 1) 127665afe1f4SKenneth D. Merry && ((err_action & SS_MASK) != SS_FAIL)) { 127765afe1f4SKenneth D. Merry 12788b8a9b1dSJustin T. Gibbs /* 12798b8a9b1dSJustin T. Gibbs * In this case, the error recovery 12808b8a9b1dSJustin T. Gibbs * command failed, but we've got 12818b8a9b1dSJustin T. Gibbs * some retries left on it. Give 128265afe1f4SKenneth D. Merry * it another try unless this is an 128365afe1f4SKenneth D. Merry * unretryable error. 12848b8a9b1dSJustin T. Gibbs */ 12858b8a9b1dSJustin T. Gibbs /* set the timeout to .5 sec */ 12868b8a9b1dSJustin T. Gibbs relsim_flags = 12878b8a9b1dSJustin T. Gibbs RELSIM_RELEASE_AFTER_TIMEOUT; 12888b8a9b1dSJustin T. Gibbs timeout = 500; 12898b8a9b1dSJustin T. Gibbs xpt_action(done_ccb); 12908b8a9b1dSJustin T. Gibbs break; 12918b8a9b1dSJustin T. Gibbs } else { 12928b8a9b1dSJustin T. Gibbs /* 12933393f8daSKenneth D. Merry * Perform the final retry with the original 12943393f8daSKenneth D. Merry * CCB so that final error processing is 12953393f8daSKenneth D. Merry * performed by the owner of the CCB. 12968b8a9b1dSJustin T. Gibbs */ 129783c5d981SAlexander Motin goto final; 12988b8a9b1dSJustin T. Gibbs } 12998b8a9b1dSJustin T. Gibbs } else { 130083c5d981SAlexander Motin save_ccb = xpt_alloc_ccb_nowait(); 130183c5d981SAlexander Motin if (save_ccb == NULL) 130283c5d981SAlexander Motin goto final; 130383c5d981SAlexander Motin bcopy(done_ccb, save_ccb, sizeof(*save_ccb)); 130483c5d981SAlexander Motin periph->flags |= CAM_PERIPH_SENSE_INPROG; 13058b8a9b1dSJustin T. Gibbs /* 130683c5d981SAlexander Motin * Send a Request Sense to the device. We 130783c5d981SAlexander Motin * assume that we are in a contingent allegiance 130883c5d981SAlexander Motin * condition so we do not tag this request. 13098b8a9b1dSJustin T. Gibbs */ 131083c5d981SAlexander Motin scsi_request_sense(&done_ccb->csio, /*retries*/1, 131183c5d981SAlexander Motin camperiphsensedone, 131283c5d981SAlexander Motin &save_ccb->csio.sense_data, 13130bb3f1d3SAlexander Motin save_ccb->csio.sense_len, 131483c5d981SAlexander Motin CAM_TAG_ACTION_NONE, 131583c5d981SAlexander Motin /*sense_len*/SSD_FULL_SIZE, 131683c5d981SAlexander Motin /*timeout*/5000); 131783c5d981SAlexander Motin done_ccb->ccb_h.pinfo.priority--; 131883c5d981SAlexander Motin done_ccb->ccb_h.flags |= CAM_DEV_QFREEZE; 131983c5d981SAlexander Motin done_ccb->ccb_h.saved_ccb_ptr = save_ccb; 132083c5d981SAlexander Motin done_ccb->ccb_h.recovery_depth++; 13218b8a9b1dSJustin T. Gibbs xpt_action(done_ccb); 13228b8a9b1dSJustin T. Gibbs } 13238b8a9b1dSJustin T. Gibbs break; 13248b8a9b1dSJustin T. Gibbs default: 132583c5d981SAlexander Motin final: 132683c5d981SAlexander Motin bcopy(saved_ccb, done_ccb, sizeof(*done_ccb)); 132783c5d981SAlexander Motin xpt_free_ccb(saved_ccb); 132860a899a0SKenneth D. Merry periph->flags &= ~CAM_PERIPH_RECOVERY_INPROG; 13298b8a9b1dSJustin T. Gibbs xpt_action(done_ccb); 13308b8a9b1dSJustin T. Gibbs break; 13318b8a9b1dSJustin T. Gibbs } 13328b8a9b1dSJustin T. Gibbs 13338b8a9b1dSJustin T. Gibbs /* decrement the retry count */ 13343393f8daSKenneth D. Merry /* 13353393f8daSKenneth D. Merry * XXX This isn't appropriate in all cases. Restructure, 13363393f8daSKenneth D. Merry * so that the retry count is only decremented on an 13373393f8daSKenneth D. Merry * actual retry. Remeber that the orignal ccb had its 13383393f8daSKenneth D. Merry * retry count dropped before entering recovery, so 13393393f8daSKenneth D. Merry * doing it again is a bug. 13403393f8daSKenneth D. Merry */ 13418b8a9b1dSJustin T. Gibbs if (done_ccb->ccb_h.retry_count > 0) 13428b8a9b1dSJustin T. Gibbs done_ccb->ccb_h.retry_count--; 1343d84c90a6SAlexander Motin /* 1344d84c90a6SAlexander Motin * Drop freeze taken due to CAM_DEV_QFREEZE flag set on recovery 1345d84c90a6SAlexander Motin * request. 1346d84c90a6SAlexander Motin */ 1347d84c90a6SAlexander Motin cam_release_devq(done_ccb->ccb_h.path, 13488b8a9b1dSJustin T. Gibbs /*relsim_flags*/relsim_flags, 13498b8a9b1dSJustin T. Gibbs /*openings*/0, 13508b8a9b1dSJustin T. Gibbs /*timeout*/timeout, 13518b8a9b1dSJustin T. Gibbs /*getcount_only*/0); 1352d84c90a6SAlexander Motin /* Drop freeze taken, if this recovery request got error. */ 1353d84c90a6SAlexander Motin if (frozen != 0) { 1354d84c90a6SAlexander Motin cam_release_devq(done_ccb->ccb_h.path, 1355d84c90a6SAlexander Motin /*relsim_flags*/0, 1356d84c90a6SAlexander Motin /*openings*/0, 1357d84c90a6SAlexander Motin /*timeout*/0, 1358d84c90a6SAlexander Motin /*getcount_only*/0); 1359d84c90a6SAlexander Motin } 1360d84c90a6SAlexander Motin } 13618b8a9b1dSJustin T. Gibbs 13628b8a9b1dSJustin T. Gibbs /* 136387cfaf0eSJustin T. Gibbs * Generic Async Event handler. Peripheral drivers usually 136487cfaf0eSJustin T. Gibbs * filter out the events that require personal attention, 136587cfaf0eSJustin T. Gibbs * and leave the rest to this function. 136687cfaf0eSJustin T. Gibbs */ 136787cfaf0eSJustin T. Gibbs void 136887cfaf0eSJustin T. Gibbs cam_periph_async(struct cam_periph *periph, u_int32_t code, 136987cfaf0eSJustin T. Gibbs struct cam_path *path, void *arg) 137087cfaf0eSJustin T. Gibbs { 137187cfaf0eSJustin T. Gibbs switch (code) { 137287cfaf0eSJustin T. Gibbs case AC_LOST_DEVICE: 137387cfaf0eSJustin T. Gibbs cam_periph_invalidate(periph); 137487cfaf0eSJustin T. Gibbs break; 137587cfaf0eSJustin T. Gibbs default: 137687cfaf0eSJustin T. Gibbs break; 137787cfaf0eSJustin T. Gibbs } 137887cfaf0eSJustin T. Gibbs } 137987cfaf0eSJustin T. Gibbs 138087cfaf0eSJustin T. Gibbs void 138187cfaf0eSJustin T. Gibbs cam_periph_bus_settle(struct cam_periph *periph, u_int bus_settle) 138287cfaf0eSJustin T. Gibbs { 138387cfaf0eSJustin T. Gibbs struct ccb_getdevstats cgds; 138487cfaf0eSJustin T. Gibbs 1385bbfa4aa1SAlexander Motin xpt_setup_ccb(&cgds.ccb_h, periph->path, CAM_PRIORITY_NORMAL); 138687cfaf0eSJustin T. Gibbs cgds.ccb_h.func_code = XPT_GDEV_STATS; 138787cfaf0eSJustin T. Gibbs xpt_action((union ccb *)&cgds); 138887cfaf0eSJustin T. Gibbs cam_periph_freeze_after_event(periph, &cgds.last_reset, bus_settle); 138987cfaf0eSJustin T. Gibbs } 139087cfaf0eSJustin T. Gibbs 139187cfaf0eSJustin T. Gibbs void 139287cfaf0eSJustin T. Gibbs cam_periph_freeze_after_event(struct cam_periph *periph, 139387cfaf0eSJustin T. Gibbs struct timeval* event_time, u_int duration_ms) 139487cfaf0eSJustin T. Gibbs { 139587cfaf0eSJustin T. Gibbs struct timeval delta; 139687cfaf0eSJustin T. Gibbs struct timeval duration_tv; 139787cfaf0eSJustin T. Gibbs 139887cfaf0eSJustin T. Gibbs microtime(&delta); 139987cfaf0eSJustin T. Gibbs timevalsub(&delta, event_time); 140087cfaf0eSJustin T. Gibbs duration_tv.tv_sec = duration_ms / 1000; 140187cfaf0eSJustin T. Gibbs duration_tv.tv_usec = (duration_ms % 1000) * 1000; 140287cfaf0eSJustin T. Gibbs if (timevalcmp(&delta, &duration_tv, <)) { 140387cfaf0eSJustin T. Gibbs timevalsub(&duration_tv, &delta); 140487cfaf0eSJustin T. Gibbs 140587cfaf0eSJustin T. Gibbs duration_ms = duration_tv.tv_sec * 1000; 140687cfaf0eSJustin T. Gibbs duration_ms += duration_tv.tv_usec / 1000; 140787cfaf0eSJustin T. Gibbs cam_freeze_devq(periph->path); 140887cfaf0eSJustin T. Gibbs cam_release_devq(periph->path, 140987cfaf0eSJustin T. Gibbs RELSIM_RELEASE_AFTER_TIMEOUT, 141087cfaf0eSJustin T. Gibbs /*reduction*/0, 141187cfaf0eSJustin T. Gibbs /*timeout*/duration_ms, 141287cfaf0eSJustin T. Gibbs /*getcount_only*/0); 141387cfaf0eSJustin T. Gibbs } 141487cfaf0eSJustin T. Gibbs 141587cfaf0eSJustin T. Gibbs } 141687cfaf0eSJustin T. Gibbs 14173393f8daSKenneth D. Merry static int 14183393f8daSKenneth D. Merry camperiphscsistatuserror(union ccb *ccb, cam_flags camflags, 141983c5d981SAlexander Motin u_int32_t sense_flags, 14203393f8daSKenneth D. Merry int *openings, u_int32_t *relsim_flags, 142183c5d981SAlexander Motin u_int32_t *timeout, const char **action_string) 14228b8a9b1dSJustin T. Gibbs { 14238b8a9b1dSJustin T. Gibbs int error; 14248b8a9b1dSJustin T. Gibbs 14258b8a9b1dSJustin T. Gibbs switch (ccb->csio.scsi_status) { 14268b8a9b1dSJustin T. Gibbs case SCSI_STATUS_OK: 14278b8a9b1dSJustin T. Gibbs case SCSI_STATUS_COND_MET: 14288b8a9b1dSJustin T. Gibbs case SCSI_STATUS_INTERMED: 14298b8a9b1dSJustin T. Gibbs case SCSI_STATUS_INTERMED_COND_MET: 14308b8a9b1dSJustin T. Gibbs error = 0; 14318b8a9b1dSJustin T. Gibbs break; 14328b8a9b1dSJustin T. Gibbs case SCSI_STATUS_CMD_TERMINATED: 14338b8a9b1dSJustin T. Gibbs case SCSI_STATUS_CHECK_COND: 143483c5d981SAlexander Motin if (bootverbose) 143583c5d981SAlexander Motin xpt_print(ccb->ccb_h.path, "SCSI status error\n"); 14363393f8daSKenneth D. Merry error = camperiphscsisenseerror(ccb, 14373393f8daSKenneth D. Merry camflags, 14388b8a9b1dSJustin T. Gibbs sense_flags, 14393393f8daSKenneth D. Merry openings, 14403393f8daSKenneth D. Merry relsim_flags, 144183c5d981SAlexander Motin timeout, 144283c5d981SAlexander Motin action_string); 14438b8a9b1dSJustin T. Gibbs break; 14448b8a9b1dSJustin T. Gibbs case SCSI_STATUS_QUEUE_FULL: 14458b8a9b1dSJustin T. Gibbs { 14468b8a9b1dSJustin T. Gibbs /* no decrement */ 144782815562SJustin T. Gibbs struct ccb_getdevstats cgds; 14488b8a9b1dSJustin T. Gibbs 14498b8a9b1dSJustin T. Gibbs /* 14508b8a9b1dSJustin T. Gibbs * First off, find out what the current 14518b8a9b1dSJustin T. Gibbs * transaction counts are. 14528b8a9b1dSJustin T. Gibbs */ 145382815562SJustin T. Gibbs xpt_setup_ccb(&cgds.ccb_h, 14548b8a9b1dSJustin T. Gibbs ccb->ccb_h.path, 1455bbfa4aa1SAlexander Motin CAM_PRIORITY_NORMAL); 145682815562SJustin T. Gibbs cgds.ccb_h.func_code = XPT_GDEV_STATS; 145782815562SJustin T. Gibbs xpt_action((union ccb *)&cgds); 14588b8a9b1dSJustin T. Gibbs 14598b8a9b1dSJustin T. Gibbs /* 14608b8a9b1dSJustin T. Gibbs * If we were the only transaction active, treat 14618b8a9b1dSJustin T. Gibbs * the QUEUE FULL as if it were a BUSY condition. 14628b8a9b1dSJustin T. Gibbs */ 146382815562SJustin T. Gibbs if (cgds.dev_active != 0) { 146482815562SJustin T. Gibbs int total_openings; 146582815562SJustin T. Gibbs 14668b8a9b1dSJustin T. Gibbs /* 14678b8a9b1dSJustin T. Gibbs * Reduce the number of openings to 14688b8a9b1dSJustin T. Gibbs * be 1 less than the amount it took 14698b8a9b1dSJustin T. Gibbs * to get a queue full bounded by the 14708b8a9b1dSJustin T. Gibbs * minimum allowed tag count for this 14718b8a9b1dSJustin T. Gibbs * device. 14728b8a9b1dSJustin T. Gibbs */ 14733393f8daSKenneth D. Merry total_openings = cgds.dev_active + cgds.dev_openings; 14743393f8daSKenneth D. Merry *openings = cgds.dev_active; 14753393f8daSKenneth D. Merry if (*openings < cgds.mintags) 14763393f8daSKenneth D. Merry *openings = cgds.mintags; 14773393f8daSKenneth D. Merry if (*openings < total_openings) 14783393f8daSKenneth D. Merry *relsim_flags = RELSIM_ADJUST_OPENINGS; 14798b8a9b1dSJustin T. Gibbs else { 14808b8a9b1dSJustin T. Gibbs /* 14818b8a9b1dSJustin T. Gibbs * Some devices report queue full for 14828b8a9b1dSJustin T. Gibbs * temporary resource shortages. For 14838b8a9b1dSJustin T. Gibbs * this reason, we allow a minimum 14848b8a9b1dSJustin T. Gibbs * tag count to be entered via a 14858b8a9b1dSJustin T. Gibbs * quirk entry to prevent the queue 14868b8a9b1dSJustin T. Gibbs * count on these devices from falling 14878b8a9b1dSJustin T. Gibbs * to a pessimisticly low value. We 14888b8a9b1dSJustin T. Gibbs * still wait for the next successful 14898b8a9b1dSJustin T. Gibbs * completion, however, before queueing 14908b8a9b1dSJustin T. Gibbs * more transactions to the device. 14918b8a9b1dSJustin T. Gibbs */ 14923393f8daSKenneth D. Merry *relsim_flags = RELSIM_RELEASE_AFTER_CMDCMPLT; 14938b8a9b1dSJustin T. Gibbs } 14943393f8daSKenneth D. Merry *timeout = 0; 14958b8a9b1dSJustin T. Gibbs error = ERESTART; 149696d333b7SMatt Jacob if (bootverbose) { 149783c5d981SAlexander Motin xpt_print(ccb->ccb_h.path, "Queue full\n"); 149896d333b7SMatt Jacob } 14998b8a9b1dSJustin T. Gibbs break; 15008b8a9b1dSJustin T. Gibbs } 15018b8a9b1dSJustin T. Gibbs /* FALLTHROUGH */ 15028b8a9b1dSJustin T. Gibbs } 15038b8a9b1dSJustin T. Gibbs case SCSI_STATUS_BUSY: 15048b8a9b1dSJustin T. Gibbs /* 15058b8a9b1dSJustin T. Gibbs * Restart the queue after either another 15068b8a9b1dSJustin T. Gibbs * command completes or a 1 second timeout. 15078b8a9b1dSJustin T. Gibbs */ 150896d333b7SMatt Jacob if (bootverbose) { 150983c5d981SAlexander Motin xpt_print(ccb->ccb_h.path, "Device busy\n"); 151096d333b7SMatt Jacob } 15113393f8daSKenneth D. Merry if (ccb->ccb_h.retry_count > 0) { 1512af51b059SMatt Jacob ccb->ccb_h.retry_count--; 15138b8a9b1dSJustin T. Gibbs error = ERESTART; 15143393f8daSKenneth D. Merry *relsim_flags = RELSIM_RELEASE_AFTER_TIMEOUT 15158b8a9b1dSJustin T. Gibbs | RELSIM_RELEASE_AFTER_CMDCMPLT; 15163393f8daSKenneth D. Merry *timeout = 1000; 1517af51b059SMatt Jacob } else { 1518af51b059SMatt Jacob error = EIO; 1519af51b059SMatt Jacob } 15208b8a9b1dSJustin T. Gibbs break; 15218b8a9b1dSJustin T. Gibbs case SCSI_STATUS_RESERV_CONFLICT: 152283c5d981SAlexander Motin xpt_print(ccb->ccb_h.path, "Reservation conflict\n"); 15238b8a9b1dSJustin T. Gibbs error = EIO; 15248b8a9b1dSJustin T. Gibbs break; 15258b8a9b1dSJustin T. Gibbs default: 152683c5d981SAlexander Motin xpt_print(ccb->ccb_h.path, "SCSI status 0x%x\n", 1527f0d9af51SMatt Jacob ccb->csio.scsi_status); 15288b8a9b1dSJustin T. Gibbs error = EIO; 15298b8a9b1dSJustin T. Gibbs break; 15308b8a9b1dSJustin T. Gibbs } 15313393f8daSKenneth D. Merry return (error); 15323393f8daSKenneth D. Merry } 15333393f8daSKenneth D. Merry 15343393f8daSKenneth D. Merry static int 15353393f8daSKenneth D. Merry camperiphscsisenseerror(union ccb *ccb, cam_flags camflags, 153683c5d981SAlexander Motin u_int32_t sense_flags, 15373393f8daSKenneth D. Merry int *openings, u_int32_t *relsim_flags, 153883c5d981SAlexander Motin u_int32_t *timeout, const char **action_string) 15393393f8daSKenneth D. Merry { 15403393f8daSKenneth D. Merry struct cam_periph *periph; 154183c5d981SAlexander Motin union ccb *orig_ccb = ccb; 15423393f8daSKenneth D. Merry int error; 15433393f8daSKenneth D. Merry 15443393f8daSKenneth D. Merry periph = xpt_path_periph(ccb->ccb_h.path); 154583c5d981SAlexander Motin if (periph->flags & 154683c5d981SAlexander Motin (CAM_PERIPH_RECOVERY_INPROG | CAM_PERIPH_SENSE_INPROG)) { 15473393f8daSKenneth D. Merry /* 15483393f8daSKenneth D. Merry * If error recovery is already in progress, don't attempt 15493393f8daSKenneth D. Merry * to process this error, but requeue it unconditionally 15503393f8daSKenneth D. Merry * and attempt to process it once error recovery has 15513393f8daSKenneth D. Merry * completed. This failed command is probably related to 15523393f8daSKenneth D. Merry * the error that caused the currently active error recovery 15533393f8daSKenneth D. Merry * action so our current recovery efforts should also 15543393f8daSKenneth D. Merry * address this command. Be aware that the error recovery 15553393f8daSKenneth D. Merry * code assumes that only one recovery action is in progress 15563393f8daSKenneth D. Merry * on a particular peripheral instance at any given time 15573393f8daSKenneth D. Merry * (e.g. only one saved CCB for error recovery) so it is 15583393f8daSKenneth D. Merry * imperitive that we don't violate this assumption. 15593393f8daSKenneth D. Merry */ 15603393f8daSKenneth D. Merry error = ERESTART; 15613393f8daSKenneth D. Merry } else { 15623393f8daSKenneth D. Merry scsi_sense_action err_action; 15633393f8daSKenneth D. Merry struct ccb_getdev cgd; 15643393f8daSKenneth D. Merry 15653393f8daSKenneth D. Merry /* 15663393f8daSKenneth D. Merry * Grab the inquiry data for this device. 15673393f8daSKenneth D. Merry */ 1568bbfa4aa1SAlexander Motin xpt_setup_ccb(&cgd.ccb_h, ccb->ccb_h.path, CAM_PRIORITY_NORMAL); 15693393f8daSKenneth D. Merry cgd.ccb_h.func_code = XPT_GDEV_TYPE; 15703393f8daSKenneth D. Merry xpt_action((union ccb *)&cgd); 15713393f8daSKenneth D. Merry 15723393f8daSKenneth D. Merry if ((ccb->ccb_h.status & CAM_AUTOSNS_VALID) != 0) 15733393f8daSKenneth D. Merry err_action = scsi_error_action(&ccb->csio, 15743393f8daSKenneth D. Merry &cgd.inq_data, 15753393f8daSKenneth D. Merry sense_flags); 15763393f8daSKenneth D. Merry else if ((ccb->ccb_h.flags & CAM_DIS_AUTOSENSE) == 0) 15773393f8daSKenneth D. Merry err_action = SS_REQSENSE; 15783393f8daSKenneth D. Merry else 15793393f8daSKenneth D. Merry err_action = SS_RETRY|SSQ_DECREMENT_COUNT|EIO; 15803393f8daSKenneth D. Merry 15813393f8daSKenneth D. Merry error = err_action & SS_ERRMASK; 15823393f8daSKenneth D. Merry 15833393f8daSKenneth D. Merry /* 15843393f8daSKenneth D. Merry * If the recovery action will consume a retry, 15853393f8daSKenneth D. Merry * make sure we actually have retries available. 15863393f8daSKenneth D. Merry */ 15873393f8daSKenneth D. Merry if ((err_action & SSQ_DECREMENT_COUNT) != 0) { 1588a07e846bSAlexander Motin if (ccb->ccb_h.retry_count > 0 && 1589a07e846bSAlexander Motin (periph->flags & CAM_PERIPH_INVALID) == 0) 15903393f8daSKenneth D. Merry ccb->ccb_h.retry_count--; 15913393f8daSKenneth D. Merry else { 159283c5d981SAlexander Motin *action_string = "Retries exhausted"; 15933393f8daSKenneth D. Merry goto sense_error_done; 15943393f8daSKenneth D. Merry } 15953393f8daSKenneth D. Merry } 15963393f8daSKenneth D. Merry 15973393f8daSKenneth D. Merry if ((err_action & SS_MASK) >= SS_START) { 15983393f8daSKenneth D. Merry /* 15993393f8daSKenneth D. Merry * Do common portions of commands that 16003393f8daSKenneth D. Merry * use recovery CCBs. 16013393f8daSKenneth D. Merry */ 160283c5d981SAlexander Motin orig_ccb = xpt_alloc_ccb_nowait(); 160383c5d981SAlexander Motin if (orig_ccb == NULL) { 160483c5d981SAlexander Motin *action_string = "Can't allocate recovery CCB"; 16053393f8daSKenneth D. Merry goto sense_error_done; 16063393f8daSKenneth D. Merry } 16071f1158b2SAlexander Motin /* 16081f1158b2SAlexander Motin * Clear freeze flag for original request here, as 16091f1158b2SAlexander Motin * this freeze will be dropped as part of ERESTART. 16101f1158b2SAlexander Motin */ 16111f1158b2SAlexander Motin ccb->ccb_h.status &= ~CAM_DEV_QFRZN; 161283c5d981SAlexander Motin bcopy(ccb, orig_ccb, sizeof(*orig_ccb)); 16133393f8daSKenneth D. Merry } 16143393f8daSKenneth D. Merry 16153393f8daSKenneth D. Merry switch (err_action & SS_MASK) { 16163393f8daSKenneth D. Merry case SS_NOP: 161783c5d981SAlexander Motin *action_string = "No recovery action needed"; 161800e54d14SKenneth D. Merry error = 0; 161900e54d14SKenneth D. Merry break; 16203393f8daSKenneth D. Merry case SS_RETRY: 162183c5d981SAlexander Motin *action_string = "Retrying command (per sense data)"; 16223393f8daSKenneth D. Merry error = ERESTART; 16238b8a9b1dSJustin T. Gibbs break; 16243393f8daSKenneth D. Merry case SS_FAIL: 162583c5d981SAlexander Motin *action_string = "Unretryable error"; 16263393f8daSKenneth D. Merry break; 16273393f8daSKenneth D. Merry case SS_START: 16283393f8daSKenneth D. Merry { 16293393f8daSKenneth D. Merry int le; 1630753e7ed0SMatt Jacob if (SID_TYPE(&cgd.inq_data) == T_SEQUENTIAL) { 1631753e7ed0SMatt Jacob xpt_free_ccb(orig_ccb); 1632753e7ed0SMatt Jacob ccb->ccb_h.status |= CAM_DEV_QFRZN; 1633753e7ed0SMatt Jacob *action_string = "Will not autostart a " 1634753e7ed0SMatt Jacob "sequential access device"; 1635753e7ed0SMatt Jacob err_action = SS_FAIL; 1636753e7ed0SMatt Jacob error = EIO; 1637753e7ed0SMatt Jacob break; 1638753e7ed0SMatt Jacob } 16393393f8daSKenneth D. Merry 16403393f8daSKenneth D. Merry /* 16413393f8daSKenneth D. Merry * Send a start unit command to the device, and 16423393f8daSKenneth D. Merry * then retry the command. 16433393f8daSKenneth D. Merry */ 164483c5d981SAlexander Motin *action_string = "Attempting to start unit"; 164583c5d981SAlexander Motin periph->flags |= CAM_PERIPH_RECOVERY_INPROG; 16463393f8daSKenneth D. Merry 16473393f8daSKenneth D. Merry /* 16483393f8daSKenneth D. Merry * Check for removable media and set 16493393f8daSKenneth D. Merry * load/eject flag appropriately. 16503393f8daSKenneth D. Merry */ 16513393f8daSKenneth D. Merry if (SID_IS_REMOVABLE(&cgd.inq_data)) 16523393f8daSKenneth D. Merry le = TRUE; 16533393f8daSKenneth D. Merry else 16543393f8daSKenneth D. Merry le = FALSE; 16553393f8daSKenneth D. Merry 16563393f8daSKenneth D. Merry scsi_start_stop(&ccb->csio, 16573393f8daSKenneth D. Merry /*retries*/1, 16583393f8daSKenneth D. Merry camperiphdone, 16593393f8daSKenneth D. Merry MSG_SIMPLE_Q_TAG, 16603393f8daSKenneth D. Merry /*start*/TRUE, 16613393f8daSKenneth D. Merry /*load/eject*/le, 16623393f8daSKenneth D. Merry /*immediate*/FALSE, 16633393f8daSKenneth D. Merry SSD_FULL_SIZE, 16643393f8daSKenneth D. Merry /*timeout*/50000); 16653393f8daSKenneth D. Merry break; 16663393f8daSKenneth D. Merry } 16673393f8daSKenneth D. Merry case SS_TUR: 16683393f8daSKenneth D. Merry { 16693393f8daSKenneth D. Merry /* 16703393f8daSKenneth D. Merry * Send a Test Unit Ready to the device. 16713393f8daSKenneth D. Merry * If the 'many' flag is set, we send 120 16723393f8daSKenneth D. Merry * test unit ready commands, one every half 16733393f8daSKenneth D. Merry * second. Otherwise, we just send one TUR. 16743393f8daSKenneth D. Merry * We only want to do this if the retry 16753393f8daSKenneth D. Merry * count has not been exhausted. 16763393f8daSKenneth D. Merry */ 16773393f8daSKenneth D. Merry int retries; 16783393f8daSKenneth D. Merry 16793393f8daSKenneth D. Merry if ((err_action & SSQ_MANY) != 0) { 168083c5d981SAlexander Motin *action_string = "Polling device for readiness"; 16813393f8daSKenneth D. Merry retries = 120; 16823393f8daSKenneth D. Merry } else { 168383c5d981SAlexander Motin *action_string = "Testing device for readiness"; 16843393f8daSKenneth D. Merry retries = 1; 16853393f8daSKenneth D. Merry } 168683c5d981SAlexander Motin periph->flags |= CAM_PERIPH_RECOVERY_INPROG; 16873393f8daSKenneth D. Merry scsi_test_unit_ready(&ccb->csio, 16883393f8daSKenneth D. Merry retries, 16893393f8daSKenneth D. Merry camperiphdone, 16903393f8daSKenneth D. Merry MSG_SIMPLE_Q_TAG, 16913393f8daSKenneth D. Merry SSD_FULL_SIZE, 16923393f8daSKenneth D. Merry /*timeout*/5000); 16933393f8daSKenneth D. Merry 16943393f8daSKenneth D. Merry /* 16953393f8daSKenneth D. Merry * Accomplish our 500ms delay by deferring 16963393f8daSKenneth D. Merry * the release of our device queue appropriately. 16973393f8daSKenneth D. Merry */ 16983393f8daSKenneth D. Merry *relsim_flags = RELSIM_RELEASE_AFTER_TIMEOUT; 16993393f8daSKenneth D. Merry *timeout = 500; 17003393f8daSKenneth D. Merry break; 17013393f8daSKenneth D. Merry } 17023393f8daSKenneth D. Merry case SS_REQSENSE: 17033393f8daSKenneth D. Merry { 170483c5d981SAlexander Motin *action_string = "Requesting SCSI sense data"; 170583c5d981SAlexander Motin periph->flags |= CAM_PERIPH_SENSE_INPROG; 17063393f8daSKenneth D. Merry /* 17073393f8daSKenneth D. Merry * Send a Request Sense to the device. We 17083393f8daSKenneth D. Merry * assume that we are in a contingent allegiance 17093393f8daSKenneth D. Merry * condition so we do not tag this request. 17103393f8daSKenneth D. Merry */ 17113393f8daSKenneth D. Merry scsi_request_sense(&ccb->csio, /*retries*/1, 171283c5d981SAlexander Motin camperiphsensedone, 171383c5d981SAlexander Motin &orig_ccb->csio.sense_data, 17140bb3f1d3SAlexander Motin orig_ccb->csio.sense_len, 17153393f8daSKenneth D. Merry CAM_TAG_ACTION_NONE, 17163393f8daSKenneth D. Merry /*sense_len*/SSD_FULL_SIZE, 17173393f8daSKenneth D. Merry /*timeout*/5000); 17183393f8daSKenneth D. Merry break; 17193393f8daSKenneth D. Merry } 17203393f8daSKenneth D. Merry default: 1721e3c29144SWarner Losh panic("Unhandled error action %x", err_action); 17223393f8daSKenneth D. Merry } 17233393f8daSKenneth D. Merry 17243393f8daSKenneth D. Merry if ((err_action & SS_MASK) >= SS_START) { 17253393f8daSKenneth D. Merry /* 1726bbfa4aa1SAlexander Motin * Drop the priority, so that the recovery 17273393f8daSKenneth D. Merry * CCB is the first to execute. Freeze the queue 17283393f8daSKenneth D. Merry * after this command is sent so that we can 17293393f8daSKenneth D. Merry * restore the old csio and have it queued in 17303393f8daSKenneth D. Merry * the proper order before we release normal 17313393f8daSKenneth D. Merry * transactions to the device. 17323393f8daSKenneth D. Merry */ 173383c5d981SAlexander Motin ccb->ccb_h.pinfo.priority--; 17343393f8daSKenneth D. Merry ccb->ccb_h.flags |= CAM_DEV_QFREEZE; 173583c5d981SAlexander Motin ccb->ccb_h.saved_ccb_ptr = orig_ccb; 173683c5d981SAlexander Motin ccb->ccb_h.recovery_depth = 0; 17373393f8daSKenneth D. Merry error = ERESTART; 17383393f8daSKenneth D. Merry } 17393393f8daSKenneth D. Merry 17403393f8daSKenneth D. Merry sense_error_done: 17413393f8daSKenneth D. Merry if ((err_action & SSQ_PRINT_SENSE) != 0 174283c5d981SAlexander Motin && (ccb->ccb_h.status & CAM_AUTOSNS_VALID) != 0) 174383c5d981SAlexander Motin cam_error_print(orig_ccb, CAM_ESF_ALL, CAM_EPF_ALL); 17443393f8daSKenneth D. Merry } 17453393f8daSKenneth D. Merry return (error); 17463393f8daSKenneth D. Merry } 17473393f8daSKenneth D. Merry 17483393f8daSKenneth D. Merry /* 17493393f8daSKenneth D. Merry * Generic error handler. Peripheral drivers usually filter 17503393f8daSKenneth D. Merry * out the errors that they handle in a unique mannor, then 17513393f8daSKenneth D. Merry * call this function. 17523393f8daSKenneth D. Merry */ 17533393f8daSKenneth D. Merry int 17543393f8daSKenneth D. Merry cam_periph_error(union ccb *ccb, cam_flags camflags, 17553393f8daSKenneth D. Merry u_int32_t sense_flags, union ccb *save_ccb) 17563393f8daSKenneth D. Merry { 1757a07e846bSAlexander Motin struct cam_periph *periph; 17583393f8daSKenneth D. Merry const char *action_string; 17593393f8daSKenneth D. Merry cam_status status; 17603393f8daSKenneth D. Merry int frozen; 176196d333b7SMatt Jacob int error, printed = 0; 17623393f8daSKenneth D. Merry int openings; 17633393f8daSKenneth D. Merry u_int32_t relsim_flags; 176473cf209fSMatt Jacob u_int32_t timeout = 0; 17653393f8daSKenneth D. Merry 1766a07e846bSAlexander Motin periph = xpt_path_periph(ccb->ccb_h.path); 17673393f8daSKenneth D. Merry action_string = NULL; 17683393f8daSKenneth D. Merry status = ccb->ccb_h.status; 17693393f8daSKenneth D. Merry frozen = (status & CAM_DEV_QFRZN) != 0; 17703393f8daSKenneth D. Merry status &= CAM_STATUS_MASK; 1771c7ec4390SMatt Jacob openings = relsim_flags = 0; 17723393f8daSKenneth D. Merry 17733393f8daSKenneth D. Merry switch (status) { 17743393f8daSKenneth D. Merry case CAM_REQ_CMP: 17753393f8daSKenneth D. Merry error = 0; 17763393f8daSKenneth D. Merry break; 17773393f8daSKenneth D. Merry case CAM_SCSI_STATUS_ERROR: 17783393f8daSKenneth D. Merry error = camperiphscsistatuserror(ccb, 17793393f8daSKenneth D. Merry camflags, 17803393f8daSKenneth D. Merry sense_flags, 17813393f8daSKenneth D. Merry &openings, 17823393f8daSKenneth D. Merry &relsim_flags, 178383c5d981SAlexander Motin &timeout, 178483c5d981SAlexander Motin &action_string); 17853393f8daSKenneth D. Merry break; 17863393f8daSKenneth D. Merry case CAM_AUTOSENSE_FAIL: 178783c5d981SAlexander Motin xpt_print(ccb->ccb_h.path, "AutoSense failed\n"); 1788c7ec4390SMatt Jacob error = EIO; /* we have to kill the command */ 1789c7ec4390SMatt Jacob break; 179052c9ce25SScott Long case CAM_ATA_STATUS_ERROR: 179152c9ce25SScott Long if (bootverbose && printed == 0) { 179283c5d981SAlexander Motin xpt_print(ccb->ccb_h.path, "ATA status error\n"); 17938691755dSAlexander Motin cam_error_print(ccb, CAM_ESF_ALL, CAM_EPF_ALL); 179452c9ce25SScott Long printed++; 179552c9ce25SScott Long } 179652c9ce25SScott Long /* FALLTHROUGH */ 17978b8a9b1dSJustin T. Gibbs case CAM_REQ_CMP_ERR: 179896d333b7SMatt Jacob if (bootverbose && printed == 0) { 1799f0d9af51SMatt Jacob xpt_print(ccb->ccb_h.path, 1800f0d9af51SMatt Jacob "Request completed with CAM_REQ_CMP_ERR\n"); 180196d333b7SMatt Jacob printed++; 180296d333b7SMatt Jacob } 180305867503SPoul-Henning Kamp /* FALLTHROUGH */ 18048b8a9b1dSJustin T. Gibbs case CAM_CMD_TIMEOUT: 180596d333b7SMatt Jacob if (bootverbose && printed == 0) { 1806f0d9af51SMatt Jacob xpt_print(ccb->ccb_h.path, "Command timed out\n"); 180796d333b7SMatt Jacob printed++; 180896d333b7SMatt Jacob } 180905867503SPoul-Henning Kamp /* FALLTHROUGH */ 18108b8a9b1dSJustin T. Gibbs case CAM_UNEXP_BUSFREE: 181196d333b7SMatt Jacob if (bootverbose && printed == 0) { 1812f0d9af51SMatt Jacob xpt_print(ccb->ccb_h.path, "Unexpected Bus Free\n"); 181396d333b7SMatt Jacob printed++; 181496d333b7SMatt Jacob } 181505867503SPoul-Henning Kamp /* FALLTHROUGH */ 18168b8a9b1dSJustin T. Gibbs case CAM_UNCOR_PARITY: 181796d333b7SMatt Jacob if (bootverbose && printed == 0) { 1818f0d9af51SMatt Jacob xpt_print(ccb->ccb_h.path, 181983c5d981SAlexander Motin "Uncorrected parity error\n"); 182096d333b7SMatt Jacob printed++; 182196d333b7SMatt Jacob } 182205867503SPoul-Henning Kamp /* FALLTHROUGH */ 18238b8a9b1dSJustin T. Gibbs case CAM_DATA_RUN_ERR: 182496d333b7SMatt Jacob if (bootverbose && printed == 0) { 182583c5d981SAlexander Motin xpt_print(ccb->ccb_h.path, "Data overrun\n"); 182696d333b7SMatt Jacob printed++; 182796d333b7SMatt Jacob } 18288b8a9b1dSJustin T. Gibbs /* decrement the number of retries */ 1829a07e846bSAlexander Motin if (ccb->ccb_h.retry_count > 0 && 1830a07e846bSAlexander Motin (periph->flags & CAM_PERIPH_INVALID) == 0) { 18318b8a9b1dSJustin T. Gibbs ccb->ccb_h.retry_count--; 18328b8a9b1dSJustin T. Gibbs error = ERESTART; 18338b8a9b1dSJustin T. Gibbs } else { 183483c5d981SAlexander Motin action_string = "Retries exhausted"; 18358b8a9b1dSJustin T. Gibbs error = EIO; 18368b8a9b1dSJustin T. Gibbs } 18378b8a9b1dSJustin T. Gibbs break; 18388b8a9b1dSJustin T. Gibbs case CAM_UA_ABORT: 18398b8a9b1dSJustin T. Gibbs case CAM_UA_TERMIO: 18408b8a9b1dSJustin T. Gibbs case CAM_MSG_REJECT_REC: 18418b8a9b1dSJustin T. Gibbs /* XXX Don't know that these are correct */ 18428b8a9b1dSJustin T. Gibbs error = EIO; 18438b8a9b1dSJustin T. Gibbs break; 18448b8a9b1dSJustin T. Gibbs case CAM_SEL_TIMEOUT: 18453393f8daSKenneth D. Merry if ((camflags & CAM_RETRY_SELTO) != 0) { 1846a07e846bSAlexander Motin if (ccb->ccb_h.retry_count > 0 && 1847a07e846bSAlexander Motin (periph->flags & CAM_PERIPH_INVALID) == 0) { 18483393f8daSKenneth D. Merry 18493393f8daSKenneth D. Merry ccb->ccb_h.retry_count--; 18503393f8daSKenneth D. Merry error = ERESTART; 185196d333b7SMatt Jacob if (bootverbose && printed == 0) { 1852f0d9af51SMatt Jacob xpt_print(ccb->ccb_h.path, 185383c5d981SAlexander Motin "Selection timeout\n"); 185496d333b7SMatt Jacob printed++; 185596d333b7SMatt Jacob } 18563393f8daSKenneth D. Merry 18573393f8daSKenneth D. Merry /* 185873cf209fSMatt Jacob * Wait a bit to give the device 18593393f8daSKenneth D. Merry * time to recover before we try again. 18603393f8daSKenneth D. Merry */ 18613393f8daSKenneth D. Merry relsim_flags = RELSIM_RELEASE_AFTER_TIMEOUT; 186273cf209fSMatt Jacob timeout = periph_selto_delay; 18633393f8daSKenneth D. Merry break; 18643393f8daSKenneth D. Merry } 1865a07e846bSAlexander Motin action_string = "Retries exhausted"; 18663393f8daSKenneth D. Merry } 18678900f4b8SKenneth D. Merry /* FALLTHROUGH */ 18688900f4b8SKenneth D. Merry case CAM_DEV_NOT_THERE: 18698900f4b8SKenneth D. Merry { 18708900f4b8SKenneth D. Merry struct cam_path *newpath; 18716e4e0e26SKenneth D. Merry lun_id_t lun_id; 18728900f4b8SKenneth D. Merry 18733393f8daSKenneth D. Merry error = ENXIO; 18746e4e0e26SKenneth D. Merry 18756e4e0e26SKenneth D. Merry /* 18766e4e0e26SKenneth D. Merry * For a selection timeout, we consider all of the LUNs on 18776e4e0e26SKenneth D. Merry * the target to be gone. If the status is CAM_DEV_NOT_THERE, 18786e4e0e26SKenneth D. Merry * then we only get rid of the device(s) specified by the 18796e4e0e26SKenneth D. Merry * path in the original CCB. 18806e4e0e26SKenneth D. Merry */ 18816e4e0e26SKenneth D. Merry if (status == CAM_DEV_NOT_THERE) 18826e4e0e26SKenneth D. Merry lun_id = xpt_path_lun_id(ccb->ccb_h.path); 18836e4e0e26SKenneth D. Merry else 18846e4e0e26SKenneth D. Merry lun_id = CAM_LUN_WILDCARD; 18856e4e0e26SKenneth D. Merry 18868b8a9b1dSJustin T. Gibbs /* Should we do more if we can't create the path?? */ 1887a07e846bSAlexander Motin if (xpt_create_path(&newpath, periph, 18888b8a9b1dSJustin T. Gibbs xpt_path_path_id(ccb->ccb_h.path), 18898b8a9b1dSJustin T. Gibbs xpt_path_target_id(ccb->ccb_h.path), 18906e4e0e26SKenneth D. Merry lun_id) != CAM_REQ_CMP) 18918b8a9b1dSJustin T. Gibbs break; 18923393f8daSKenneth D. Merry 18938b8a9b1dSJustin T. Gibbs /* 18948b8a9b1dSJustin T. Gibbs * Let peripheral drivers know that this device has gone 18958b8a9b1dSJustin T. Gibbs * away. 18968b8a9b1dSJustin T. Gibbs */ 18978b8a9b1dSJustin T. Gibbs xpt_async(AC_LOST_DEVICE, newpath, NULL); 18988b8a9b1dSJustin T. Gibbs xpt_free_path(newpath); 18998b8a9b1dSJustin T. Gibbs break; 19008b8a9b1dSJustin T. Gibbs } 19018b8a9b1dSJustin T. Gibbs case CAM_REQ_INVALID: 19028b8a9b1dSJustin T. Gibbs case CAM_PATH_INVALID: 19038b8a9b1dSJustin T. Gibbs case CAM_NO_HBA: 19048b8a9b1dSJustin T. Gibbs case CAM_PROVIDE_FAIL: 19058b8a9b1dSJustin T. Gibbs case CAM_REQ_TOO_BIG: 19069a014e6fSIan Dowse case CAM_LUN_INVALID: 19079a014e6fSIan Dowse case CAM_TID_INVALID: 19088b8a9b1dSJustin T. Gibbs error = EINVAL; 19098b8a9b1dSJustin T. Gibbs break; 19108b8a9b1dSJustin T. Gibbs case CAM_SCSI_BUS_RESET: 19118b8a9b1dSJustin T. Gibbs case CAM_BDR_SENT: 19123393f8daSKenneth D. Merry /* 19133393f8daSKenneth D. Merry * Commands that repeatedly timeout and cause these 19143393f8daSKenneth D. Merry * kinds of error recovery actions, should return 19153393f8daSKenneth D. Merry * CAM_CMD_TIMEOUT, which allows us to safely assume 19163393f8daSKenneth D. Merry * that this command was an innocent bystander to 19173393f8daSKenneth D. Merry * these events and should be unconditionally 19183393f8daSKenneth D. Merry * retried. 19193393f8daSKenneth D. Merry */ 192096d333b7SMatt Jacob if (bootverbose && printed == 0) { 192196d333b7SMatt Jacob xpt_print_path(ccb->ccb_h.path); 192296d333b7SMatt Jacob if (status == CAM_BDR_SENT) 192396d333b7SMatt Jacob printf("Bus Device Reset sent\n"); 192496d333b7SMatt Jacob else 192596d333b7SMatt Jacob printf("Bus Reset issued\n"); 192696d333b7SMatt Jacob printed++; 192796d333b7SMatt Jacob } 19283393f8daSKenneth D. Merry /* FALLTHROUGH */ 19298b8a9b1dSJustin T. Gibbs case CAM_REQUEUE_REQ: 19303393f8daSKenneth D. Merry /* Unconditional requeue */ 193196d333b7SMatt Jacob if (bootverbose && printed == 0) { 193283c5d981SAlexander Motin xpt_print(ccb->ccb_h.path, "Request requeued\n"); 193396d333b7SMatt Jacob printed++; 193496d333b7SMatt Jacob } 1935a07e846bSAlexander Motin if ((periph->flags & CAM_PERIPH_INVALID) == 0) 1936a07e846bSAlexander Motin error = ERESTART; 1937a07e846bSAlexander Motin else { 1938a07e846bSAlexander Motin action_string = "Retries exhausted"; 1939a07e846bSAlexander Motin error = EIO; 1940a07e846bSAlexander Motin } 19418b8a9b1dSJustin T. Gibbs break; 19428b8a9b1dSJustin T. Gibbs case CAM_RESRC_UNAVAIL: 194373cf209fSMatt Jacob /* Wait a bit for the resource shortage to abate. */ 194473cf209fSMatt Jacob timeout = periph_noresrc_delay; 194573cf209fSMatt Jacob /* FALLTHROUGH */ 19468b8a9b1dSJustin T. Gibbs case CAM_BUSY: 194773cf209fSMatt Jacob if (timeout == 0) { 194873cf209fSMatt Jacob /* Wait a bit for the busy condition to abate. */ 194973cf209fSMatt Jacob timeout = periph_busy_delay; 195073cf209fSMatt Jacob } 195173cf209fSMatt Jacob relsim_flags = RELSIM_RELEASE_AFTER_TIMEOUT; 195273cf209fSMatt Jacob /* FALLTHROUGH */ 19538b8a9b1dSJustin T. Gibbs default: 19548b8a9b1dSJustin T. Gibbs /* decrement the number of retries */ 1955a07e846bSAlexander Motin if (ccb->ccb_h.retry_count > 0 && 1956a07e846bSAlexander Motin (periph->flags & CAM_PERIPH_INVALID) == 0) { 19578b8a9b1dSJustin T. Gibbs ccb->ccb_h.retry_count--; 19588b8a9b1dSJustin T. Gibbs error = ERESTART; 195996d333b7SMatt Jacob if (bootverbose && printed == 0) { 196083c5d981SAlexander Motin xpt_print(ccb->ccb_h.path, "CAM status 0x%x\n", 1961f0d9af51SMatt Jacob status); 196296d333b7SMatt Jacob printed++; 196396d333b7SMatt Jacob } 19648b8a9b1dSJustin T. Gibbs } else { 19658b8a9b1dSJustin T. Gibbs error = EIO; 196683c5d981SAlexander Motin action_string = "Retries exhausted"; 19678b8a9b1dSJustin T. Gibbs } 19688b8a9b1dSJustin T. Gibbs break; 19698b8a9b1dSJustin T. Gibbs } 19708b8a9b1dSJustin T. Gibbs 197134707c9fSMatt Jacob /* 197234707c9fSMatt Jacob * If we have and error and are booting verbosely, whine 197334707c9fSMatt Jacob * *unless* this was a non-retryable selection timeout. 197434707c9fSMatt Jacob */ 197534707c9fSMatt Jacob if (error != 0 && bootverbose && 197634707c9fSMatt Jacob !(status == CAM_SEL_TIMEOUT && (camflags & CAM_RETRY_SELTO) == 0)) { 19770f3e2159SAlexander Motin if (error != ERESTART) { 19783393f8daSKenneth D. Merry if (action_string == NULL) 197983c5d981SAlexander Motin action_string = "Unretryable error"; 198083c5d981SAlexander Motin xpt_print(ccb->ccb_h.path, "Error %d, %s\n", 198183c5d981SAlexander Motin error, action_string); 198283c5d981SAlexander Motin } else if (action_string != NULL) 1983f0d9af51SMatt Jacob xpt_print(ccb->ccb_h.path, "%s\n", action_string); 198483c5d981SAlexander Motin else 198583c5d981SAlexander Motin xpt_print(ccb->ccb_h.path, "Retrying command\n"); 19860f3e2159SAlexander Motin } 19870f3e2159SAlexander Motin 19880f3e2159SAlexander Motin /* Attempt a retry */ 19890f3e2159SAlexander Motin if (error == ERESTART || error == 0) { 19900f3e2159SAlexander Motin if (frozen != 0) 19910f3e2159SAlexander Motin ccb->ccb_h.status &= ~CAM_DEV_QFRZN; 19920f3e2159SAlexander Motin if (error == ERESTART) 19930f3e2159SAlexander Motin xpt_action(ccb); 19940f3e2159SAlexander Motin if (frozen != 0) 19950f3e2159SAlexander Motin cam_release_devq(ccb->ccb_h.path, 19960f3e2159SAlexander Motin relsim_flags, 19970f3e2159SAlexander Motin openings, 19980f3e2159SAlexander Motin timeout, 19990f3e2159SAlexander Motin /*getcount_only*/0); 20003393f8daSKenneth D. Merry } 20018b8a9b1dSJustin T. Gibbs 20028b8a9b1dSJustin T. Gibbs return (error); 20038b8a9b1dSJustin T. Gibbs } 2004