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> 380ec81012SJohn Polstra #include <sys/linker_set.h> 399626b608SPoul-Henning Kamp #include <sys/bio.h> 40f34fa851SJohn Baldwin #include <sys/lock.h> 41f34fa851SJohn Baldwin #include <sys/mutex.h> 428b8a9b1dSJustin T. Gibbs #include <sys/buf.h> 438b8a9b1dSJustin T. Gibbs #include <sys/proc.h> 448b8a9b1dSJustin T. Gibbs #include <sys/devicestat.h> 4575f51904SPeter Wemm #include <sys/bus.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 union ccb *save_ccb, 753393f8daSKenneth D. Merry int *openings, 763393f8daSKenneth D. Merry u_int32_t *relsim_flags, 773393f8daSKenneth D. Merry u_int32_t *timeout); 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 union ccb *save_ccb, 823393f8daSKenneth D. Merry int *openings, 833393f8daSKenneth D. Merry u_int32_t *relsim_flags, 843393f8daSKenneth D. Merry u_int32_t *timeout); 858b8a9b1dSJustin T. Gibbs 860b7c27b9SPeter Wemm static int nperiph_drivers; 870b7c27b9SPeter Wemm struct periph_driver **periph_drivers; 880b7c27b9SPeter Wemm 89362abc44STai-hwa Liang MALLOC_DEFINE(M_CAMPERIPH, "CAM periph", "CAM peripheral buffers"); 90362abc44STai-hwa Liang 9173cf209fSMatt Jacob static int periph_selto_delay = 1000; 9273cf209fSMatt Jacob TUNABLE_INT("kern.cam.periph_selto_delay", &periph_selto_delay); 9373cf209fSMatt Jacob static int periph_noresrc_delay = 500; 9473cf209fSMatt Jacob TUNABLE_INT("kern.cam.periph_noresrc_delay", &periph_noresrc_delay); 9573cf209fSMatt Jacob static int periph_busy_delay = 500; 9673cf209fSMatt Jacob TUNABLE_INT("kern.cam.periph_busy_delay", &periph_busy_delay); 9773cf209fSMatt Jacob 9873cf209fSMatt Jacob 990b7c27b9SPeter Wemm void 1000b7c27b9SPeter Wemm periphdriver_register(void *data) 1010b7c27b9SPeter Wemm { 1020b7c27b9SPeter Wemm struct periph_driver **newdrivers, **old; 1030b7c27b9SPeter Wemm int ndrivers; 1040b7c27b9SPeter Wemm 1050b7c27b9SPeter Wemm ndrivers = nperiph_drivers + 2; 1060dd50e9bSScott Long newdrivers = malloc(sizeof(*newdrivers) * ndrivers, M_CAMPERIPH, 1070dd50e9bSScott Long M_WAITOK); 1080b7c27b9SPeter Wemm if (periph_drivers) 1090b7c27b9SPeter Wemm bcopy(periph_drivers, newdrivers, 110623db360SKenneth D. Merry sizeof(*newdrivers) * nperiph_drivers); 1110b7c27b9SPeter Wemm newdrivers[nperiph_drivers] = (struct periph_driver *)data; 1120b7c27b9SPeter Wemm newdrivers[nperiph_drivers + 1] = NULL; 1130b7c27b9SPeter Wemm old = periph_drivers; 1140b7c27b9SPeter Wemm periph_drivers = newdrivers; 1150b7c27b9SPeter Wemm if (old) 1160dd50e9bSScott Long free(old, M_CAMPERIPH); 1170b7c27b9SPeter Wemm nperiph_drivers++; 1180b7c27b9SPeter Wemm } 1190b7c27b9SPeter Wemm 1208b8a9b1dSJustin T. Gibbs cam_status 121ee9c90c7SKenneth D. Merry cam_periph_alloc(periph_ctor_t *periph_ctor, 122ee9c90c7SKenneth D. Merry periph_oninv_t *periph_oninvalidate, 123ee9c90c7SKenneth D. Merry periph_dtor_t *periph_dtor, periph_start_t *periph_start, 124ee9c90c7SKenneth D. Merry char *name, cam_periph_type type, struct cam_path *path, 125ee9c90c7SKenneth D. Merry ac_callback_t *ac_callback, ac_code code, void *arg) 1268b8a9b1dSJustin T. Gibbs { 1278b8a9b1dSJustin T. Gibbs struct periph_driver **p_drv; 1282b83592fSScott Long struct cam_sim *sim; 1298b8a9b1dSJustin T. Gibbs struct cam_periph *periph; 1308b8a9b1dSJustin T. Gibbs struct cam_periph *cur_periph; 1318b8a9b1dSJustin T. Gibbs path_id_t path_id; 1328b8a9b1dSJustin T. Gibbs target_id_t target_id; 1338b8a9b1dSJustin T. Gibbs lun_id_t lun_id; 1348b8a9b1dSJustin T. Gibbs cam_status status; 1358b8a9b1dSJustin T. Gibbs u_int init_level; 1368b8a9b1dSJustin T. Gibbs 1378b8a9b1dSJustin T. Gibbs init_level = 0; 1388b8a9b1dSJustin T. Gibbs /* 1398b8a9b1dSJustin T. Gibbs * Handle Hot-Plug scenarios. If there is already a peripheral 1408b8a9b1dSJustin T. Gibbs * of our type assigned to this path, we are likely waiting for 1418b8a9b1dSJustin T. Gibbs * final close on an old, invalidated, peripheral. If this is 1428b8a9b1dSJustin T. Gibbs * the case, queue up a deferred call to the peripheral's async 14374c91ec5SJustin T. Gibbs * handler. If it looks like a mistaken re-allocation, complain. 1448b8a9b1dSJustin T. Gibbs */ 1458b8a9b1dSJustin T. Gibbs if ((periph = cam_periph_find(path, name)) != NULL) { 1468b8a9b1dSJustin T. Gibbs 1478b8a9b1dSJustin T. Gibbs if ((periph->flags & CAM_PERIPH_INVALID) != 0 1488b8a9b1dSJustin T. Gibbs && (periph->flags & CAM_PERIPH_NEW_DEV_FOUND) == 0) { 1498b8a9b1dSJustin T. Gibbs periph->flags |= CAM_PERIPH_NEW_DEV_FOUND; 1508b8a9b1dSJustin T. Gibbs periph->deferred_callback = ac_callback; 1518b8a9b1dSJustin T. Gibbs periph->deferred_ac = code; 1528b8a9b1dSJustin T. Gibbs return (CAM_REQ_INPROG); 1538b8a9b1dSJustin T. Gibbs } else { 1548b8a9b1dSJustin T. Gibbs printf("cam_periph_alloc: attempt to re-allocate " 1558b8a9b1dSJustin T. Gibbs "valid device %s%d rejected\n", 1568b8a9b1dSJustin T. Gibbs periph->periph_name, periph->unit_number); 1578b8a9b1dSJustin T. Gibbs } 1588b8a9b1dSJustin T. Gibbs return (CAM_REQ_INVALID); 1598b8a9b1dSJustin T. Gibbs } 1608b8a9b1dSJustin T. Gibbs 161362abc44STai-hwa Liang periph = (struct cam_periph *)malloc(sizeof(*periph), M_CAMPERIPH, 1628b8a9b1dSJustin T. Gibbs M_NOWAIT); 1638b8a9b1dSJustin T. Gibbs 1648b8a9b1dSJustin T. Gibbs if (periph == NULL) 1658b8a9b1dSJustin T. Gibbs return (CAM_RESRC_UNAVAIL); 1668b8a9b1dSJustin T. Gibbs 1678b8a9b1dSJustin T. Gibbs init_level++; 1688b8a9b1dSJustin T. Gibbs 1692b83592fSScott Long xpt_lock_buses(); 1700b7c27b9SPeter Wemm for (p_drv = periph_drivers; *p_drv != NULL; p_drv++) { 1718b8a9b1dSJustin T. Gibbs if (strcmp((*p_drv)->driver_name, name) == 0) 1728b8a9b1dSJustin T. Gibbs break; 1738b8a9b1dSJustin T. Gibbs } 1742b83592fSScott Long xpt_unlock_buses(); 175aa812d9eSMax Khon if (*p_drv == NULL) { 17653f8b22bSEdward Tomasz Napierala printf("cam_periph_alloc: invalid periph name '%s'\n", name); 1779b61a5b9SEdward Tomasz Napierala free(periph, M_CAMPERIPH); 17853f8b22bSEdward Tomasz Napierala return (CAM_REQ_INVALID); 17953f8b22bSEdward Tomasz Napierala } 1808b8a9b1dSJustin T. Gibbs 1812b83592fSScott Long sim = xpt_path_sim(path); 1828b8a9b1dSJustin T. Gibbs path_id = xpt_path_path_id(path); 1838b8a9b1dSJustin T. Gibbs target_id = xpt_path_target_id(path); 1848b8a9b1dSJustin T. Gibbs lun_id = xpt_path_lun_id(path); 1858b8a9b1dSJustin T. Gibbs bzero(periph, sizeof(*periph)); 1868b8a9b1dSJustin T. Gibbs cam_init_pinfo(&periph->pinfo); 1878b8a9b1dSJustin T. Gibbs periph->periph_start = periph_start; 1888b8a9b1dSJustin T. Gibbs periph->periph_dtor = periph_dtor; 189ee9c90c7SKenneth D. Merry periph->periph_oninval = periph_oninvalidate; 1908b8a9b1dSJustin T. Gibbs periph->type = type; 1918b8a9b1dSJustin T. Gibbs periph->periph_name = name; 1928b8a9b1dSJustin T. Gibbs periph->unit_number = camperiphunit(*p_drv, path_id, target_id, lun_id); 1938b8a9b1dSJustin T. Gibbs periph->immediate_priority = CAM_PRIORITY_NONE; 1948b8a9b1dSJustin T. Gibbs periph->refcount = 0; 1952b83592fSScott Long periph->sim = sim; 1968b8a9b1dSJustin T. Gibbs SLIST_INIT(&periph->ccb_list); 1978b8a9b1dSJustin T. Gibbs status = xpt_create_path(&path, periph, path_id, target_id, lun_id); 1988b8a9b1dSJustin T. Gibbs if (status != CAM_REQ_CMP) 1998b8a9b1dSJustin T. Gibbs goto failure; 2008b8a9b1dSJustin T. Gibbs 2018b8a9b1dSJustin T. Gibbs periph->path = path; 2028b8a9b1dSJustin T. Gibbs init_level++; 2038b8a9b1dSJustin T. Gibbs 2048b8a9b1dSJustin T. Gibbs status = xpt_add_periph(periph); 2058b8a9b1dSJustin T. Gibbs 2068b8a9b1dSJustin T. Gibbs if (status != CAM_REQ_CMP) 2078b8a9b1dSJustin T. Gibbs goto failure; 2088b8a9b1dSJustin T. Gibbs 2098b8a9b1dSJustin T. Gibbs cur_periph = TAILQ_FIRST(&(*p_drv)->units); 2108b8a9b1dSJustin T. Gibbs while (cur_periph != NULL 2118b8a9b1dSJustin T. Gibbs && cur_periph->unit_number < periph->unit_number) 2128b8a9b1dSJustin T. Gibbs cur_periph = TAILQ_NEXT(cur_periph, unit_links); 2138b8a9b1dSJustin T. Gibbs 2148b8a9b1dSJustin T. Gibbs if (cur_periph != NULL) 2158b8a9b1dSJustin T. Gibbs TAILQ_INSERT_BEFORE(cur_periph, periph, unit_links); 2168b8a9b1dSJustin T. Gibbs else { 2178b8a9b1dSJustin T. Gibbs TAILQ_INSERT_TAIL(&(*p_drv)->units, periph, unit_links); 2188b8a9b1dSJustin T. Gibbs (*p_drv)->generation++; 2198b8a9b1dSJustin T. Gibbs } 2208b8a9b1dSJustin T. Gibbs 2218b8a9b1dSJustin T. Gibbs init_level++; 2228b8a9b1dSJustin T. Gibbs 2238b8a9b1dSJustin T. Gibbs status = periph_ctor(periph, arg); 2248b8a9b1dSJustin T. Gibbs 2258b8a9b1dSJustin T. Gibbs if (status == CAM_REQ_CMP) 2268b8a9b1dSJustin T. Gibbs init_level++; 2278b8a9b1dSJustin T. Gibbs 2288b8a9b1dSJustin T. Gibbs failure: 2298b8a9b1dSJustin T. Gibbs switch (init_level) { 2308b8a9b1dSJustin T. Gibbs case 4: 2318b8a9b1dSJustin T. Gibbs /* Initialized successfully */ 2328b8a9b1dSJustin T. Gibbs break; 2338b8a9b1dSJustin T. Gibbs case 3: 2348b8a9b1dSJustin T. Gibbs TAILQ_REMOVE(&(*p_drv)->units, periph, unit_links); 2358b8a9b1dSJustin T. Gibbs xpt_remove_periph(periph); 2367379c88fSPoul-Henning Kamp /* FALLTHROUGH */ 2378b8a9b1dSJustin T. Gibbs case 2: 2388b8a9b1dSJustin T. Gibbs xpt_free_path(periph->path); 2397379c88fSPoul-Henning Kamp /* FALLTHROUGH */ 2408b8a9b1dSJustin T. Gibbs case 1: 241362abc44STai-hwa Liang free(periph, M_CAMPERIPH); 2427379c88fSPoul-Henning Kamp /* FALLTHROUGH */ 2438b8a9b1dSJustin T. Gibbs case 0: 2448b8a9b1dSJustin T. Gibbs /* No cleanup to perform. */ 2458b8a9b1dSJustin T. Gibbs break; 2468b8a9b1dSJustin T. Gibbs default: 2478b8a9b1dSJustin T. Gibbs panic("cam_periph_alloc: Unkown init level"); 2488b8a9b1dSJustin T. Gibbs } 2498b8a9b1dSJustin T. Gibbs return(status); 2508b8a9b1dSJustin T. Gibbs } 2518b8a9b1dSJustin T. Gibbs 2528b8a9b1dSJustin T. Gibbs /* 2538b8a9b1dSJustin T. Gibbs * Find a peripheral structure with the specified path, target, lun, 2548b8a9b1dSJustin T. Gibbs * and (optionally) type. If the name is NULL, this function will return 2558b8a9b1dSJustin T. Gibbs * the first peripheral driver that matches the specified path. 2568b8a9b1dSJustin T. Gibbs */ 2578b8a9b1dSJustin T. Gibbs struct cam_periph * 2588b8a9b1dSJustin T. Gibbs cam_periph_find(struct cam_path *path, char *name) 2598b8a9b1dSJustin T. Gibbs { 2608b8a9b1dSJustin T. Gibbs struct periph_driver **p_drv; 2618b8a9b1dSJustin T. Gibbs struct cam_periph *periph; 2628b8a9b1dSJustin T. Gibbs 26377dc25ccSScott Long xpt_lock_buses(); 2640b7c27b9SPeter Wemm for (p_drv = periph_drivers; *p_drv != NULL; p_drv++) { 2658b8a9b1dSJustin T. Gibbs 2668b8a9b1dSJustin T. Gibbs if (name != NULL && (strcmp((*p_drv)->driver_name, name) != 0)) 2678b8a9b1dSJustin T. Gibbs continue; 2688b8a9b1dSJustin T. Gibbs 26937d40066SPoul-Henning Kamp TAILQ_FOREACH(periph, &(*p_drv)->units, unit_links) { 2708b8a9b1dSJustin T. Gibbs if (xpt_path_comp(periph->path, path) == 0) { 27177dc25ccSScott Long xpt_unlock_buses(); 2728b8a9b1dSJustin T. Gibbs return(periph); 2738b8a9b1dSJustin T. Gibbs } 2748b8a9b1dSJustin T. Gibbs } 27577dc25ccSScott Long if (name != NULL) { 27677dc25ccSScott Long xpt_unlock_buses(); 2778b8a9b1dSJustin T. Gibbs return(NULL); 2788b8a9b1dSJustin T. Gibbs } 27977dc25ccSScott Long } 28077dc25ccSScott Long xpt_unlock_buses(); 2818b8a9b1dSJustin T. Gibbs return(NULL); 2828b8a9b1dSJustin T. Gibbs } 2838b8a9b1dSJustin T. Gibbs 2848b8a9b1dSJustin T. Gibbs cam_status 2858b8a9b1dSJustin T. Gibbs cam_periph_acquire(struct cam_periph *periph) 2868b8a9b1dSJustin T. Gibbs { 2878b8a9b1dSJustin T. Gibbs 2888b8a9b1dSJustin T. Gibbs if (periph == NULL) 2898b8a9b1dSJustin T. Gibbs return(CAM_REQ_CMP_ERR); 2908b8a9b1dSJustin T. Gibbs 2912b83592fSScott Long xpt_lock_buses(); 2928b8a9b1dSJustin T. Gibbs periph->refcount++; 2932b83592fSScott Long xpt_unlock_buses(); 2948b8a9b1dSJustin T. Gibbs 2958b8a9b1dSJustin T. Gibbs return(CAM_REQ_CMP); 2968b8a9b1dSJustin T. Gibbs } 2978b8a9b1dSJustin T. Gibbs 2988b8a9b1dSJustin T. Gibbs void 29924ebf566SEdward Tomasz Napierala cam_periph_release_locked(struct cam_periph *periph) 3008b8a9b1dSJustin T. Gibbs { 3018b8a9b1dSJustin T. Gibbs 3028b8a9b1dSJustin T. Gibbs if (periph == NULL) 3038b8a9b1dSJustin T. Gibbs return; 3048b8a9b1dSJustin T. Gibbs 3052b83592fSScott Long xpt_lock_buses(); 3068b8a9b1dSJustin T. Gibbs if ((--periph->refcount == 0) 3078b8a9b1dSJustin T. Gibbs && (periph->flags & CAM_PERIPH_INVALID)) { 3088b8a9b1dSJustin T. Gibbs camperiphfree(periph); 3098b8a9b1dSJustin T. Gibbs } 3102b83592fSScott Long xpt_unlock_buses(); 31124ebf566SEdward Tomasz Napierala } 3128b8a9b1dSJustin T. Gibbs 31324ebf566SEdward Tomasz Napierala void 31424ebf566SEdward Tomasz Napierala cam_periph_release(struct cam_periph *periph) 31524ebf566SEdward Tomasz Napierala { 31624ebf566SEdward Tomasz Napierala struct cam_sim *sim; 31724ebf566SEdward Tomasz Napierala 31824ebf566SEdward Tomasz Napierala if (periph == NULL) 31924ebf566SEdward Tomasz Napierala return; 32024ebf566SEdward Tomasz Napierala 32124ebf566SEdward Tomasz Napierala sim = periph->sim; 32224ebf566SEdward Tomasz Napierala mtx_assert(sim->mtx, MA_NOTOWNED); 32324ebf566SEdward Tomasz Napierala mtx_lock(sim->mtx); 32424ebf566SEdward Tomasz Napierala cam_periph_release_locked(periph); 32524ebf566SEdward Tomasz Napierala mtx_unlock(sim->mtx); 3268b8a9b1dSJustin T. Gibbs } 3278b8a9b1dSJustin T. Gibbs 3282b83592fSScott Long int 3292b83592fSScott Long cam_periph_hold(struct cam_periph *periph, int priority) 3302b83592fSScott Long { 3312b83592fSScott Long int error; 3322b83592fSScott Long 3332b83592fSScott Long /* 3342b83592fSScott Long * Increment the reference count on the peripheral 3352b83592fSScott Long * while we wait for our lock attempt to succeed 3362b83592fSScott Long * to ensure the peripheral doesn't disappear out 3372b83592fSScott Long * from user us while we sleep. 3382b83592fSScott Long */ 3392b83592fSScott Long 3402b83592fSScott Long if (cam_periph_acquire(periph) != CAM_REQ_CMP) 3412b83592fSScott Long return (ENXIO); 3422b83592fSScott Long 3431fa738c2SJohn Baldwin mtx_assert(periph->sim->mtx, MA_OWNED); 3442b83592fSScott Long while ((periph->flags & CAM_PERIPH_LOCKED) != 0) { 3452b83592fSScott Long periph->flags |= CAM_PERIPH_LOCK_WANTED; 3461fa738c2SJohn Baldwin if ((error = mtx_sleep(periph, periph->sim->mtx, priority, 3471fa738c2SJohn Baldwin "caplck", 0)) != 0) { 34824ebf566SEdward Tomasz Napierala cam_periph_release_locked(periph); 3492b83592fSScott Long return (error); 3502b83592fSScott Long } 3512b83592fSScott Long } 3522b83592fSScott Long 3532b83592fSScott Long periph->flags |= CAM_PERIPH_LOCKED; 3542b83592fSScott Long return (0); 3552b83592fSScott Long } 3562b83592fSScott Long 3572b83592fSScott Long void 3582b83592fSScott Long cam_periph_unhold(struct cam_periph *periph) 3592b83592fSScott Long { 3602b83592fSScott Long 3612b83592fSScott Long mtx_assert(periph->sim->mtx, MA_OWNED); 3622b83592fSScott Long 3632b83592fSScott Long periph->flags &= ~CAM_PERIPH_LOCKED; 3642b83592fSScott Long if ((periph->flags & CAM_PERIPH_LOCK_WANTED) != 0) { 3652b83592fSScott Long periph->flags &= ~CAM_PERIPH_LOCK_WANTED; 3662b83592fSScott Long wakeup(periph); 3672b83592fSScott Long } 3682b83592fSScott Long 36924ebf566SEdward Tomasz Napierala cam_periph_release_locked(periph); 3702b83592fSScott Long } 3712b83592fSScott Long 3728b8a9b1dSJustin T. Gibbs /* 3738b8a9b1dSJustin T. Gibbs * Look for the next unit number that is not currently in use for this 3748b8a9b1dSJustin T. Gibbs * peripheral type starting at "newunit". Also exclude unit numbers that 3758b8a9b1dSJustin T. Gibbs * are reserved by for future "hardwiring" unless we already know that this 3768b8a9b1dSJustin T. Gibbs * is a potential wired device. Only assume that the device is "wired" the 3778b8a9b1dSJustin T. Gibbs * first time through the loop since after that we'll be looking at unit 3788b8a9b1dSJustin T. Gibbs * numbers that did not match a wiring entry. 3798b8a9b1dSJustin T. Gibbs */ 3808b8a9b1dSJustin T. Gibbs static u_int 381501468a5SKenneth D. Merry camperiphnextunit(struct periph_driver *p_drv, u_int newunit, int wired, 382501468a5SKenneth D. Merry path_id_t pathid, target_id_t target, lun_id_t lun) 3838b8a9b1dSJustin T. Gibbs { 3848b8a9b1dSJustin T. Gibbs struct cam_periph *periph; 3852398f0cdSPeter Wemm char *periph_name; 3862398f0cdSPeter Wemm int i, val, dunit, r; 3872398f0cdSPeter Wemm const char *dname, *strval; 3888b8a9b1dSJustin T. Gibbs 3898b8a9b1dSJustin T. Gibbs periph_name = p_drv->driver_name; 3908b8a9b1dSJustin T. Gibbs for (;;newunit++) { 3918b8a9b1dSJustin T. Gibbs 3928b8a9b1dSJustin T. Gibbs for (periph = TAILQ_FIRST(&p_drv->units); 3938b8a9b1dSJustin T. Gibbs periph != NULL && periph->unit_number != newunit; 3948b8a9b1dSJustin T. Gibbs periph = TAILQ_NEXT(periph, unit_links)) 3958b8a9b1dSJustin T. Gibbs ; 3968b8a9b1dSJustin T. Gibbs 3978b8a9b1dSJustin T. Gibbs if (periph != NULL && periph->unit_number == newunit) { 3988b8a9b1dSJustin T. Gibbs if (wired != 0) { 399f0d9af51SMatt Jacob xpt_print(periph->path, "Duplicate Wired " 400f0d9af51SMatt Jacob "Device entry!\n"); 401f0d9af51SMatt Jacob xpt_print(periph->path, "Second device (%s " 402f0d9af51SMatt Jacob "device at scbus%d target %d lun %d) will " 403f0d9af51SMatt Jacob "not be wired\n", periph_name, pathid, 404f0d9af51SMatt Jacob target, lun); 4058b8a9b1dSJustin T. Gibbs wired = 0; 4068b8a9b1dSJustin T. Gibbs } 4078b8a9b1dSJustin T. Gibbs continue; 4088b8a9b1dSJustin T. Gibbs } 40975f51904SPeter Wemm if (wired) 41075f51904SPeter Wemm break; 4118b8a9b1dSJustin T. Gibbs 4128b8a9b1dSJustin T. Gibbs /* 4138b8a9b1dSJustin T. Gibbs * Don't match entries like "da 4" as a wired down 4148b8a9b1dSJustin T. Gibbs * device, but do match entries like "da 4 target 5" 4158b8a9b1dSJustin T. Gibbs * or even "da 4 scbus 1". 4168b8a9b1dSJustin T. Gibbs */ 4172398f0cdSPeter Wemm i = 0; 4182398f0cdSPeter Wemm dname = periph_name; 4192398f0cdSPeter Wemm for (;;) { 4202398f0cdSPeter Wemm r = resource_find_dev(&i, dname, &dunit, NULL, NULL); 4212398f0cdSPeter Wemm if (r != 0) 4222398f0cdSPeter Wemm break; 42375f51904SPeter Wemm /* if no "target" and no specific scbus, skip */ 42475f51904SPeter Wemm if (resource_int_value(dname, dunit, "target", &val) && 42575f51904SPeter Wemm (resource_string_value(dname, dunit, "at",&strval)|| 42675f51904SPeter Wemm strcmp(strval, "scbus") == 0)) 42775f51904SPeter Wemm continue; 42875f51904SPeter Wemm if (newunit == dunit) 4298b8a9b1dSJustin T. Gibbs break; 4308b8a9b1dSJustin T. Gibbs } 4312398f0cdSPeter Wemm if (r != 0) 4328b8a9b1dSJustin T. Gibbs break; 4338b8a9b1dSJustin T. Gibbs } 4348b8a9b1dSJustin T. Gibbs return (newunit); 4358b8a9b1dSJustin T. Gibbs } 4368b8a9b1dSJustin T. Gibbs 4378b8a9b1dSJustin T. Gibbs static u_int 4388b8a9b1dSJustin T. Gibbs camperiphunit(struct periph_driver *p_drv, path_id_t pathid, 4398b8a9b1dSJustin T. Gibbs target_id_t target, lun_id_t lun) 4408b8a9b1dSJustin T. Gibbs { 4418b8a9b1dSJustin T. Gibbs u_int unit; 442c1b81613SPeter Wemm int wired, i, val, dunit; 4432398f0cdSPeter Wemm const char *dname, *strval; 4442398f0cdSPeter Wemm char pathbuf[32], *periph_name; 4458b8a9b1dSJustin T. Gibbs 44675f51904SPeter Wemm periph_name = p_drv->driver_name; 44775f51904SPeter Wemm snprintf(pathbuf, sizeof(pathbuf), "scbus%d", pathid); 448c1b81613SPeter Wemm unit = 0; 4492398f0cdSPeter Wemm i = 0; 4502398f0cdSPeter Wemm dname = periph_name; 451c1b81613SPeter Wemm for (wired = 0; resource_find_dev(&i, dname, &dunit, NULL, NULL) == 0; 452c1b81613SPeter Wemm wired = 0) { 45375f51904SPeter Wemm if (resource_string_value(dname, dunit, "at", &strval) == 0) { 45475f51904SPeter Wemm if (strcmp(strval, pathbuf) != 0) 4558b8a9b1dSJustin T. Gibbs continue; 456c1b81613SPeter Wemm wired++; 4578b8a9b1dSJustin T. Gibbs } 45875f51904SPeter Wemm if (resource_int_value(dname, dunit, "target", &val) == 0) { 45975f51904SPeter Wemm if (val != target) 4608b8a9b1dSJustin T. Gibbs continue; 461c1b81613SPeter Wemm wired++; 4628b8a9b1dSJustin T. Gibbs } 46375f51904SPeter Wemm if (resource_int_value(dname, dunit, "lun", &val) == 0) { 46475f51904SPeter Wemm if (val != lun) 4658b8a9b1dSJustin T. Gibbs continue; 466c1b81613SPeter Wemm wired++; 4678b8a9b1dSJustin T. Gibbs } 468c1b81613SPeter Wemm if (wired != 0) { 46975f51904SPeter Wemm unit = dunit; 4708b8a9b1dSJustin T. Gibbs break; 4718b8a9b1dSJustin T. Gibbs } 4728b8a9b1dSJustin T. Gibbs } 4738b8a9b1dSJustin T. Gibbs 4748b8a9b1dSJustin T. Gibbs /* 4758b8a9b1dSJustin T. Gibbs * Either start from 0 looking for the next unit or from 47675f51904SPeter Wemm * the unit number given in the resource config. This way, 4778b8a9b1dSJustin T. Gibbs * if we have wildcard matches, we don't return the same 4788b8a9b1dSJustin T. Gibbs * unit number twice. 4798b8a9b1dSJustin T. Gibbs */ 480c1b81613SPeter Wemm unit = camperiphnextunit(p_drv, unit, wired, pathid, target, lun); 4818b8a9b1dSJustin T. Gibbs 4828b8a9b1dSJustin T. Gibbs return (unit); 4838b8a9b1dSJustin T. Gibbs } 4848b8a9b1dSJustin T. Gibbs 4858b8a9b1dSJustin T. Gibbs void 4868b8a9b1dSJustin T. Gibbs cam_periph_invalidate(struct cam_periph *periph) 4878b8a9b1dSJustin T. Gibbs { 4888b8a9b1dSJustin T. Gibbs 489ee9c90c7SKenneth D. Merry /* 490ee9c90c7SKenneth D. Merry * We only call this routine the first time a peripheral is 49177dc25ccSScott Long * invalidated. 492ee9c90c7SKenneth D. Merry */ 493ee9c90c7SKenneth D. Merry if (((periph->flags & CAM_PERIPH_INVALID) == 0) 494ee9c90c7SKenneth D. Merry && (periph->periph_oninval != NULL)) 495ee9c90c7SKenneth D. Merry periph->periph_oninval(periph); 496ee9c90c7SKenneth D. Merry 4978b8a9b1dSJustin T. Gibbs periph->flags |= CAM_PERIPH_INVALID; 4988b8a9b1dSJustin T. Gibbs periph->flags &= ~CAM_PERIPH_NEW_DEV_FOUND; 4998b8a9b1dSJustin T. Gibbs 5002b83592fSScott Long xpt_lock_buses(); 5018b8a9b1dSJustin T. Gibbs if (periph->refcount == 0) 5028b8a9b1dSJustin T. Gibbs camperiphfree(periph); 5038b8a9b1dSJustin T. Gibbs else if (periph->refcount < 0) 5048b8a9b1dSJustin T. Gibbs printf("cam_invalidate_periph: refcount < 0!!\n"); 5052b83592fSScott Long xpt_unlock_buses(); 5068b8a9b1dSJustin T. Gibbs } 5078b8a9b1dSJustin T. Gibbs 5088b8a9b1dSJustin T. Gibbs static void 5098b8a9b1dSJustin T. Gibbs camperiphfree(struct cam_periph *periph) 5108b8a9b1dSJustin T. Gibbs { 5118b8a9b1dSJustin T. Gibbs struct periph_driver **p_drv; 5128b8a9b1dSJustin T. Gibbs 5130b7c27b9SPeter Wemm for (p_drv = periph_drivers; *p_drv != NULL; p_drv++) { 5148b8a9b1dSJustin T. Gibbs if (strcmp((*p_drv)->driver_name, periph->periph_name) == 0) 5158b8a9b1dSJustin T. Gibbs break; 5168b8a9b1dSJustin T. Gibbs } 517661658a6SScott Long if (*p_drv == NULL) { 518661658a6SScott Long printf("camperiphfree: attempt to free non-existant periph\n"); 519661658a6SScott Long return; 520661658a6SScott Long } 5218b8a9b1dSJustin T. Gibbs 5228b8a9b1dSJustin T. Gibbs TAILQ_REMOVE(&(*p_drv)->units, periph, unit_links); 5238b8a9b1dSJustin T. Gibbs (*p_drv)->generation++; 524f3548746SScott Long xpt_unlock_buses(); 5258b8a9b1dSJustin T. Gibbs 526f3548746SScott Long if (periph->periph_dtor != NULL) 527f3548746SScott Long periph->periph_dtor(periph); 5288b8a9b1dSJustin T. Gibbs xpt_remove_periph(periph); 5298b8a9b1dSJustin T. Gibbs 5308b8a9b1dSJustin T. Gibbs if (periph->flags & CAM_PERIPH_NEW_DEV_FOUND) { 5318b8a9b1dSJustin T. Gibbs union ccb ccb; 5328b8a9b1dSJustin T. Gibbs void *arg; 5338b8a9b1dSJustin T. Gibbs 5348b8a9b1dSJustin T. Gibbs switch (periph->deferred_ac) { 5358b8a9b1dSJustin T. Gibbs case AC_FOUND_DEVICE: 5368b8a9b1dSJustin T. Gibbs ccb.ccb_h.func_code = XPT_GDEV_TYPE; 5378b8a9b1dSJustin T. Gibbs xpt_setup_ccb(&ccb.ccb_h, periph->path, /*priority*/ 1); 5388b8a9b1dSJustin T. Gibbs xpt_action(&ccb); 5398b8a9b1dSJustin T. Gibbs arg = &ccb; 5408b8a9b1dSJustin T. Gibbs break; 5418b8a9b1dSJustin T. Gibbs case AC_PATH_REGISTERED: 5428b8a9b1dSJustin T. Gibbs ccb.ccb_h.func_code = XPT_PATH_INQ; 5438b8a9b1dSJustin T. Gibbs xpt_setup_ccb(&ccb.ccb_h, periph->path, /*priority*/ 1); 5448b8a9b1dSJustin T. Gibbs xpt_action(&ccb); 5458b8a9b1dSJustin T. Gibbs arg = &ccb; 5468b8a9b1dSJustin T. Gibbs break; 5478b8a9b1dSJustin T. Gibbs default: 5488b8a9b1dSJustin T. Gibbs arg = NULL; 5498b8a9b1dSJustin T. Gibbs break; 5508b8a9b1dSJustin T. Gibbs } 5518b8a9b1dSJustin T. Gibbs periph->deferred_callback(NULL, periph->deferred_ac, 5528b8a9b1dSJustin T. Gibbs periph->path, arg); 5538b8a9b1dSJustin T. Gibbs } 5548b8a9b1dSJustin T. Gibbs xpt_free_path(periph->path); 555362abc44STai-hwa Liang free(periph, M_CAMPERIPH); 556f3548746SScott Long xpt_lock_buses(); 5578b8a9b1dSJustin T. Gibbs } 5588b8a9b1dSJustin T. Gibbs 5598b8a9b1dSJustin T. Gibbs /* 5608b8a9b1dSJustin T. Gibbs * Map user virtual pointers into kernel virtual address space, so we can 5618b8a9b1dSJustin T. Gibbs * access the memory. This won't work on physical pointers, for now it's 5628b8a9b1dSJustin T. Gibbs * up to the caller to check for that. (XXX KDM -- should we do that here 5638b8a9b1dSJustin T. Gibbs * instead?) This also only works for up to MAXPHYS memory. Since we use 5648b8a9b1dSJustin T. Gibbs * buffers to map stuff in and out, we're limited to the buffer size. 5658b8a9b1dSJustin T. Gibbs */ 5668b8a9b1dSJustin T. Gibbs int 5678b8a9b1dSJustin T. Gibbs cam_periph_mapmem(union ccb *ccb, struct cam_periph_map_info *mapinfo) 5688b8a9b1dSJustin T. Gibbs { 5692d5c7e45SMatthew Dillon int numbufs, i, j; 57079d49a06SKenneth D. Merry int flags[CAM_PERIPH_MAXMAPS]; 5718b8a9b1dSJustin T. Gibbs u_int8_t **data_ptrs[CAM_PERIPH_MAXMAPS]; 5728b8a9b1dSJustin T. Gibbs u_int32_t lengths[CAM_PERIPH_MAXMAPS]; 5738b8a9b1dSJustin T. Gibbs u_int32_t dirs[CAM_PERIPH_MAXMAPS]; 57452c9ce25SScott Long /* Some controllers may not be able to handle more data. */ 57552c9ce25SScott Long size_t maxmap = DFLTPHYS; 5768b8a9b1dSJustin T. Gibbs 5778b8a9b1dSJustin T. Gibbs switch(ccb->ccb_h.func_code) { 5788b8a9b1dSJustin T. Gibbs case XPT_DEV_MATCH: 5798b8a9b1dSJustin T. Gibbs if (ccb->cdm.match_buf_len == 0) { 5808b8a9b1dSJustin T. Gibbs printf("cam_periph_mapmem: invalid match buffer " 5818b8a9b1dSJustin T. Gibbs "length 0\n"); 5828b8a9b1dSJustin T. Gibbs return(EINVAL); 5838b8a9b1dSJustin T. Gibbs } 5848b8a9b1dSJustin T. Gibbs if (ccb->cdm.pattern_buf_len > 0) { 5858b8a9b1dSJustin T. Gibbs data_ptrs[0] = (u_int8_t **)&ccb->cdm.patterns; 5868b8a9b1dSJustin T. Gibbs lengths[0] = ccb->cdm.pattern_buf_len; 5878b8a9b1dSJustin T. Gibbs dirs[0] = CAM_DIR_OUT; 5888b8a9b1dSJustin T. Gibbs data_ptrs[1] = (u_int8_t **)&ccb->cdm.matches; 5898b8a9b1dSJustin T. Gibbs lengths[1] = ccb->cdm.match_buf_len; 5908b8a9b1dSJustin T. Gibbs dirs[1] = CAM_DIR_IN; 5918b8a9b1dSJustin T. Gibbs numbufs = 2; 5928b8a9b1dSJustin T. Gibbs } else { 5938b8a9b1dSJustin T. Gibbs data_ptrs[0] = (u_int8_t **)&ccb->cdm.matches; 5948b8a9b1dSJustin T. Gibbs lengths[0] = ccb->cdm.match_buf_len; 5958b8a9b1dSJustin T. Gibbs dirs[0] = CAM_DIR_IN; 5968b8a9b1dSJustin T. Gibbs numbufs = 1; 5978b8a9b1dSJustin T. Gibbs } 59852c9ce25SScott Long /* 59952c9ce25SScott Long * This request will not go to the hardware, no reason 60052c9ce25SScott Long * to be so strict. vmapbuf() is able to map up to MAXPHYS. 60152c9ce25SScott Long */ 60252c9ce25SScott Long maxmap = MAXPHYS; 6038b8a9b1dSJustin T. Gibbs break; 6048b8a9b1dSJustin T. Gibbs case XPT_SCSI_IO: 60587cfaf0eSJustin T. Gibbs case XPT_CONT_TARGET_IO: 6068b8a9b1dSJustin T. Gibbs if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_NONE) 6078b8a9b1dSJustin T. Gibbs return(0); 6088b8a9b1dSJustin T. Gibbs 6098b8a9b1dSJustin T. Gibbs data_ptrs[0] = &ccb->csio.data_ptr; 61079d49a06SKenneth D. Merry lengths[0] = ccb->csio.dxfer_len; 6118b8a9b1dSJustin T. Gibbs dirs[0] = ccb->ccb_h.flags & CAM_DIR_MASK; 6128b8a9b1dSJustin T. Gibbs numbufs = 1; 6138b8a9b1dSJustin T. Gibbs break; 61452c9ce25SScott Long case XPT_ATA_IO: 61552c9ce25SScott Long if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_NONE) 61652c9ce25SScott Long return(0); 61752c9ce25SScott Long 61852c9ce25SScott Long data_ptrs[0] = &ccb->ataio.data_ptr; 61952c9ce25SScott Long lengths[0] = ccb->ataio.dxfer_len; 62052c9ce25SScott Long dirs[0] = ccb->ccb_h.flags & CAM_DIR_MASK; 62152c9ce25SScott Long numbufs = 1; 62252c9ce25SScott Long break; 6238b8a9b1dSJustin T. Gibbs default: 6248b8a9b1dSJustin T. Gibbs return(EINVAL); 6258b8a9b1dSJustin T. Gibbs break; /* NOTREACHED */ 6268b8a9b1dSJustin T. Gibbs } 6278b8a9b1dSJustin T. Gibbs 6288b8a9b1dSJustin T. Gibbs /* 62979d49a06SKenneth D. Merry * Check the transfer length and permissions first, so we don't 63079d49a06SKenneth D. Merry * have to unmap any previously mapped buffers. 6318b8a9b1dSJustin T. Gibbs */ 6328b8a9b1dSJustin T. Gibbs for (i = 0; i < numbufs; i++) { 63379d49a06SKenneth D. Merry 63479d49a06SKenneth D. Merry flags[i] = 0; 63579d49a06SKenneth D. Merry 63679d49a06SKenneth D. Merry /* 63779d49a06SKenneth D. Merry * The userland data pointer passed in may not be page 63879d49a06SKenneth D. Merry * aligned. vmapbuf() truncates the address to a page 63979d49a06SKenneth D. Merry * boundary, so if the address isn't page aligned, we'll 64079d49a06SKenneth D. Merry * need enough space for the given transfer length, plus 64179d49a06SKenneth D. Merry * whatever extra space is necessary to make it to the page 64279d49a06SKenneth D. Merry * boundary. 64379d49a06SKenneth D. Merry */ 64479d49a06SKenneth D. Merry if ((lengths[i] + 64552c9ce25SScott Long (((vm_offset_t)(*data_ptrs[i])) & PAGE_MASK)) > maxmap){ 6466d7b539aSPeter Wemm printf("cam_periph_mapmem: attempt to map %lu bytes, " 64752c9ce25SScott Long "which is greater than %lu\n", 6486d7b539aSPeter Wemm (long)(lengths[i] + 6496d7b539aSPeter Wemm (((vm_offset_t)(*data_ptrs[i])) & PAGE_MASK)), 65052c9ce25SScott Long (u_long)maxmap); 65179d49a06SKenneth D. Merry return(E2BIG); 65279d49a06SKenneth D. Merry } 6538b8a9b1dSJustin T. Gibbs 654edd24ab7SKenneth D. Merry if (dirs[i] & CAM_DIR_OUT) { 65521144e3bSPoul-Henning Kamp flags[i] = BIO_WRITE; 6568b8a9b1dSJustin T. Gibbs } 6578b8a9b1dSJustin T. Gibbs 658edd24ab7SKenneth D. Merry if (dirs[i] & CAM_DIR_IN) { 65921144e3bSPoul-Henning Kamp flags[i] = BIO_READ; 6608b8a9b1dSJustin T. Gibbs } 6618b8a9b1dSJustin T. Gibbs 66279d49a06SKenneth D. Merry } 66379d49a06SKenneth D. Merry 66479d49a06SKenneth D. Merry /* this keeps the current process from getting swapped */ 66579d49a06SKenneth D. Merry /* 66679d49a06SKenneth D. Merry * XXX KDM should I use P_NOSWAP instead? 66779d49a06SKenneth D. Merry */ 6680cbbb7bfSPeter Wemm PHOLD(curproc); 66979d49a06SKenneth D. Merry 67079d49a06SKenneth D. Merry for (i = 0; i < numbufs; i++) { 6718b8a9b1dSJustin T. Gibbs /* 6728b8a9b1dSJustin T. Gibbs * Get the buffer. 6738b8a9b1dSJustin T. Gibbs */ 6741c7c3c6aSMatthew Dillon mapinfo->bp[i] = getpbuf(NULL); 6758b8a9b1dSJustin T. Gibbs 6768b8a9b1dSJustin T. Gibbs /* save the buffer's data address */ 6778b8a9b1dSJustin T. Gibbs mapinfo->bp[i]->b_saveaddr = mapinfo->bp[i]->b_data; 6788b8a9b1dSJustin T. Gibbs 6798b8a9b1dSJustin T. Gibbs /* put our pointer in the data slot */ 6808b8a9b1dSJustin T. Gibbs mapinfo->bp[i]->b_data = *data_ptrs[i]; 6818b8a9b1dSJustin T. Gibbs 68252c9ce25SScott Long /* set the transfer length, we know it's < MAXPHYS */ 6838b8a9b1dSJustin T. Gibbs mapinfo->bp[i]->b_bufsize = lengths[i]; 6848b8a9b1dSJustin T. Gibbs 68521144e3bSPoul-Henning Kamp /* set the direction */ 68621144e3bSPoul-Henning Kamp mapinfo->bp[i]->b_iocmd = flags[i]; 6878b8a9b1dSJustin T. Gibbs 6882d5c7e45SMatthew Dillon /* 6892d5c7e45SMatthew Dillon * Map the buffer into kernel memory. 6902d5c7e45SMatthew Dillon * 6912d5c7e45SMatthew Dillon * Note that useracc() alone is not a sufficient test. 6922d5c7e45SMatthew Dillon * vmapbuf() can still fail due to a smaller file mapped 6932d5c7e45SMatthew Dillon * into a larger area of VM, or if userland races against 6942d5c7e45SMatthew Dillon * vmapbuf() after the useracc() check. 6952d5c7e45SMatthew Dillon */ 6962d5c7e45SMatthew Dillon if (vmapbuf(mapinfo->bp[i]) < 0) { 6972d5c7e45SMatthew Dillon for (j = 0; j < i; ++j) { 6982d5c7e45SMatthew Dillon *data_ptrs[j] = mapinfo->bp[j]->b_saveaddr; 6998373f36dSAlan Cox vunmapbuf(mapinfo->bp[j]); 7002d5c7e45SMatthew Dillon relpbuf(mapinfo->bp[j], NULL); 7012d5c7e45SMatthew Dillon } 7028373f36dSAlan Cox relpbuf(mapinfo->bp[i], NULL); 7032d5c7e45SMatthew Dillon PRELE(curproc); 7042d5c7e45SMatthew Dillon return(EACCES); 7052d5c7e45SMatthew Dillon } 7068b8a9b1dSJustin T. Gibbs 7078b8a9b1dSJustin T. Gibbs /* set our pointer to the new mapped area */ 7088b8a9b1dSJustin T. Gibbs *data_ptrs[i] = mapinfo->bp[i]->b_data; 7098b8a9b1dSJustin T. Gibbs 7108b8a9b1dSJustin T. Gibbs mapinfo->num_bufs_used++; 7118b8a9b1dSJustin T. Gibbs } 7128b8a9b1dSJustin T. Gibbs 713a62525f3SMatt Jacob /* 714a62525f3SMatt Jacob * Now that we've gotten this far, change ownership to the kernel 715a62525f3SMatt Jacob * of the buffers so that we don't run afoul of returning to user 716a62525f3SMatt Jacob * space with locks (on the buffer) held. 717a62525f3SMatt Jacob */ 718a62525f3SMatt Jacob for (i = 0; i < numbufs; i++) { 719a62525f3SMatt Jacob BUF_KERNPROC(mapinfo->bp[i]); 720a62525f3SMatt Jacob } 721a62525f3SMatt Jacob 722a62525f3SMatt Jacob 7238b8a9b1dSJustin T. Gibbs return(0); 7248b8a9b1dSJustin T. Gibbs } 7258b8a9b1dSJustin T. Gibbs 7268b8a9b1dSJustin T. Gibbs /* 7278b8a9b1dSJustin T. Gibbs * Unmap memory segments mapped into kernel virtual address space by 7288b8a9b1dSJustin T. Gibbs * cam_periph_mapmem(). 7298b8a9b1dSJustin T. Gibbs */ 7308b8a9b1dSJustin T. Gibbs void 7318b8a9b1dSJustin T. Gibbs cam_periph_unmapmem(union ccb *ccb, struct cam_periph_map_info *mapinfo) 7328b8a9b1dSJustin T. Gibbs { 7338b8a9b1dSJustin T. Gibbs int numbufs, i; 7348b8a9b1dSJustin T. Gibbs u_int8_t **data_ptrs[CAM_PERIPH_MAXMAPS]; 7358b8a9b1dSJustin T. Gibbs 7368b8a9b1dSJustin T. Gibbs if (mapinfo->num_bufs_used <= 0) { 7378b8a9b1dSJustin T. Gibbs /* allow ourselves to be swapped once again */ 7380cbbb7bfSPeter Wemm PRELE(curproc); 7398b8a9b1dSJustin T. Gibbs return; 7408b8a9b1dSJustin T. Gibbs } 7418b8a9b1dSJustin T. Gibbs 7428b8a9b1dSJustin T. Gibbs switch (ccb->ccb_h.func_code) { 7438b8a9b1dSJustin T. Gibbs case XPT_DEV_MATCH: 7448b8a9b1dSJustin T. Gibbs numbufs = min(mapinfo->num_bufs_used, 2); 7458b8a9b1dSJustin T. Gibbs 7468b8a9b1dSJustin T. Gibbs if (numbufs == 1) { 7478b8a9b1dSJustin T. Gibbs data_ptrs[0] = (u_int8_t **)&ccb->cdm.matches; 7488b8a9b1dSJustin T. Gibbs } else { 7498b8a9b1dSJustin T. Gibbs data_ptrs[0] = (u_int8_t **)&ccb->cdm.patterns; 7508b8a9b1dSJustin T. Gibbs data_ptrs[1] = (u_int8_t **)&ccb->cdm.matches; 7518b8a9b1dSJustin T. Gibbs } 7528b8a9b1dSJustin T. Gibbs break; 7538b8a9b1dSJustin T. Gibbs case XPT_SCSI_IO: 7549911ecf9SJustin T. Gibbs case XPT_CONT_TARGET_IO: 7558b8a9b1dSJustin T. Gibbs data_ptrs[0] = &ccb->csio.data_ptr; 7568b8a9b1dSJustin T. Gibbs numbufs = min(mapinfo->num_bufs_used, 1); 7578b8a9b1dSJustin T. Gibbs break; 75852c9ce25SScott Long case XPT_ATA_IO: 75952c9ce25SScott Long data_ptrs[0] = &ccb->ataio.data_ptr; 76052c9ce25SScott Long numbufs = min(mapinfo->num_bufs_used, 1); 76152c9ce25SScott Long break; 7628b8a9b1dSJustin T. Gibbs default: 7638b8a9b1dSJustin T. Gibbs /* allow ourselves to be swapped once again */ 7640cbbb7bfSPeter Wemm PRELE(curproc); 7658b8a9b1dSJustin T. Gibbs return; 7668b8a9b1dSJustin T. Gibbs break; /* NOTREACHED */ 7678b8a9b1dSJustin T. Gibbs } 7688b8a9b1dSJustin T. Gibbs 7698b8a9b1dSJustin T. Gibbs for (i = 0; i < numbufs; i++) { 7708b8a9b1dSJustin T. Gibbs /* Set the user's pointer back to the original value */ 7718b8a9b1dSJustin T. Gibbs *data_ptrs[i] = mapinfo->bp[i]->b_saveaddr; 7728b8a9b1dSJustin T. Gibbs 7738b8a9b1dSJustin T. Gibbs /* unmap the buffer */ 7748b8a9b1dSJustin T. Gibbs vunmapbuf(mapinfo->bp[i]); 7758b8a9b1dSJustin T. Gibbs 7768b8a9b1dSJustin T. Gibbs /* release the buffer */ 7771c7c3c6aSMatthew Dillon relpbuf(mapinfo->bp[i], NULL); 7788b8a9b1dSJustin T. Gibbs } 7798b8a9b1dSJustin T. Gibbs 7808b8a9b1dSJustin T. Gibbs /* allow ourselves to be swapped once again */ 7810cbbb7bfSPeter Wemm PRELE(curproc); 7828b8a9b1dSJustin T. Gibbs } 7838b8a9b1dSJustin T. Gibbs 7848b8a9b1dSJustin T. Gibbs union ccb * 7858b8a9b1dSJustin T. Gibbs cam_periph_getccb(struct cam_periph *periph, u_int32_t priority) 7868b8a9b1dSJustin T. Gibbs { 7878b8a9b1dSJustin T. Gibbs struct ccb_hdr *ccb_h; 7888b8a9b1dSJustin T. Gibbs 7892b83592fSScott Long mtx_assert(periph->sim->mtx, MA_OWNED); 7908b8a9b1dSJustin T. Gibbs CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering cdgetccb\n")); 7918b8a9b1dSJustin T. Gibbs 792fc2ffbe6SPoul-Henning Kamp while (SLIST_FIRST(&periph->ccb_list) == NULL) { 7938b8a9b1dSJustin T. Gibbs if (periph->immediate_priority > priority) 7948b8a9b1dSJustin T. Gibbs periph->immediate_priority = priority; 7958b8a9b1dSJustin T. Gibbs xpt_schedule(periph, priority); 796fc2ffbe6SPoul-Henning Kamp if ((SLIST_FIRST(&periph->ccb_list) != NULL) 797fc2ffbe6SPoul-Henning Kamp && (SLIST_FIRST(&periph->ccb_list)->pinfo.priority == priority)) 7988b8a9b1dSJustin T. Gibbs break; 7992b83592fSScott Long mtx_assert(periph->sim->mtx, MA_OWNED); 8001fa738c2SJohn Baldwin mtx_sleep(&periph->ccb_list, periph->sim->mtx, PRIBIO, "cgticb", 8011fa738c2SJohn Baldwin 0); 8028b8a9b1dSJustin T. Gibbs } 8038b8a9b1dSJustin T. Gibbs 804fc2ffbe6SPoul-Henning Kamp ccb_h = SLIST_FIRST(&periph->ccb_list); 8058b8a9b1dSJustin T. Gibbs SLIST_REMOVE_HEAD(&periph->ccb_list, periph_links.sle); 8068b8a9b1dSJustin T. Gibbs return ((union ccb *)ccb_h); 8078b8a9b1dSJustin T. Gibbs } 8088b8a9b1dSJustin T. Gibbs 8098b8a9b1dSJustin T. Gibbs void 8108b8a9b1dSJustin T. Gibbs cam_periph_ccbwait(union ccb *ccb) 8118b8a9b1dSJustin T. Gibbs { 8122b83592fSScott Long struct cam_sim *sim; 8138b8a9b1dSJustin T. Gibbs 8142b83592fSScott Long sim = xpt_path_sim(ccb->ccb_h.path); 8158b8a9b1dSJustin T. Gibbs if ((ccb->ccb_h.pinfo.index != CAM_UNQUEUED_INDEX) 8168b8a9b1dSJustin T. Gibbs || ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_INPROG)) 8171fa738c2SJohn Baldwin mtx_sleep(&ccb->ccb_h.cbfcnp, sim->mtx, PRIBIO, "cbwait", 0); 8188b8a9b1dSJustin T. Gibbs } 8198b8a9b1dSJustin T. Gibbs 8208b8a9b1dSJustin T. Gibbs int 821571e8e20SScott Long cam_periph_ioctl(struct cam_periph *periph, u_long cmd, caddr_t addr, 8228b8a9b1dSJustin T. Gibbs int (*error_routine)(union ccb *ccb, 8238b8a9b1dSJustin T. Gibbs cam_flags camflags, 8248b8a9b1dSJustin T. Gibbs u_int32_t sense_flags)) 8258b8a9b1dSJustin T. Gibbs { 8268b8a9b1dSJustin T. Gibbs union ccb *ccb; 8278b8a9b1dSJustin T. Gibbs int error; 8288b8a9b1dSJustin T. Gibbs int found; 8298b8a9b1dSJustin T. Gibbs 8308b8a9b1dSJustin T. Gibbs error = found = 0; 8318b8a9b1dSJustin T. Gibbs 8328b8a9b1dSJustin T. Gibbs switch(cmd){ 8338b8a9b1dSJustin T. Gibbs case CAMGETPASSTHRU: 8348b8a9b1dSJustin T. Gibbs ccb = cam_periph_getccb(periph, /* priority */ 1); 8358b8a9b1dSJustin T. Gibbs xpt_setup_ccb(&ccb->ccb_h, 8368b8a9b1dSJustin T. Gibbs ccb->ccb_h.path, 8378b8a9b1dSJustin T. Gibbs /*priority*/1); 8388b8a9b1dSJustin T. Gibbs ccb->ccb_h.func_code = XPT_GDEVLIST; 8398b8a9b1dSJustin T. Gibbs 8408b8a9b1dSJustin T. Gibbs /* 8418b8a9b1dSJustin T. Gibbs * Basically, the point of this is that we go through 8428b8a9b1dSJustin T. Gibbs * getting the list of devices, until we find a passthrough 8438b8a9b1dSJustin T. Gibbs * device. In the current version of the CAM code, the 8448b8a9b1dSJustin T. Gibbs * only way to determine what type of device we're dealing 8458b8a9b1dSJustin T. Gibbs * with is by its name. 8468b8a9b1dSJustin T. Gibbs */ 8478b8a9b1dSJustin T. Gibbs while (found == 0) { 8488b8a9b1dSJustin T. Gibbs ccb->cgdl.index = 0; 8498b8a9b1dSJustin T. Gibbs ccb->cgdl.status = CAM_GDEVLIST_MORE_DEVS; 8508b8a9b1dSJustin T. Gibbs while (ccb->cgdl.status == CAM_GDEVLIST_MORE_DEVS) { 8518b8a9b1dSJustin T. Gibbs 8528b8a9b1dSJustin T. Gibbs /* we want the next device in the list */ 8538b8a9b1dSJustin T. Gibbs xpt_action(ccb); 8548b8a9b1dSJustin T. Gibbs if (strncmp(ccb->cgdl.periph_name, 8558b8a9b1dSJustin T. Gibbs "pass", 4) == 0){ 8568b8a9b1dSJustin T. Gibbs found = 1; 8578b8a9b1dSJustin T. Gibbs break; 8588b8a9b1dSJustin T. Gibbs } 8598b8a9b1dSJustin T. Gibbs } 8608b8a9b1dSJustin T. Gibbs if ((ccb->cgdl.status == CAM_GDEVLIST_LAST_DEVICE) && 8618b8a9b1dSJustin T. Gibbs (found == 0)) { 8628b8a9b1dSJustin T. Gibbs ccb->cgdl.periph_name[0] = '\0'; 8638b8a9b1dSJustin T. Gibbs ccb->cgdl.unit_number = 0; 8648b8a9b1dSJustin T. Gibbs break; 8658b8a9b1dSJustin T. Gibbs } 8668b8a9b1dSJustin T. Gibbs } 8678b8a9b1dSJustin T. Gibbs 8688b8a9b1dSJustin T. Gibbs /* copy the result back out */ 8698b8a9b1dSJustin T. Gibbs bcopy(ccb, addr, sizeof(union ccb)); 8708b8a9b1dSJustin T. Gibbs 8718b8a9b1dSJustin T. Gibbs /* and release the ccb */ 8728b8a9b1dSJustin T. Gibbs xpt_release_ccb(ccb); 8738b8a9b1dSJustin T. Gibbs 8748b8a9b1dSJustin T. Gibbs break; 8758b8a9b1dSJustin T. Gibbs default: 8768b8a9b1dSJustin T. Gibbs error = ENOTTY; 8778b8a9b1dSJustin T. Gibbs break; 8788b8a9b1dSJustin T. Gibbs } 8798b8a9b1dSJustin T. Gibbs return(error); 8808b8a9b1dSJustin T. Gibbs } 8818b8a9b1dSJustin T. Gibbs 8828b8a9b1dSJustin T. Gibbs int 8838b8a9b1dSJustin T. Gibbs cam_periph_runccb(union ccb *ccb, 8848b8a9b1dSJustin T. Gibbs int (*error_routine)(union ccb *ccb, 8858b8a9b1dSJustin T. Gibbs cam_flags camflags, 8868b8a9b1dSJustin T. Gibbs u_int32_t sense_flags), 8878b8a9b1dSJustin T. Gibbs cam_flags camflags, u_int32_t sense_flags, 8888b8a9b1dSJustin T. Gibbs struct devstat *ds) 8898b8a9b1dSJustin T. Gibbs { 8902b83592fSScott Long struct cam_sim *sim; 8918b8a9b1dSJustin T. Gibbs int error; 8928b8a9b1dSJustin T. Gibbs 8938b8a9b1dSJustin T. Gibbs error = 0; 8942b83592fSScott Long sim = xpt_path_sim(ccb->ccb_h.path); 8952b83592fSScott Long mtx_assert(sim->mtx, MA_OWNED); 8968b8a9b1dSJustin T. Gibbs 8978b8a9b1dSJustin T. Gibbs /* 8988b8a9b1dSJustin T. Gibbs * If the user has supplied a stats structure, and if we understand 8998b8a9b1dSJustin T. Gibbs * this particular type of ccb, record the transaction start. 9008b8a9b1dSJustin T. Gibbs */ 9018b8a9b1dSJustin T. Gibbs if ((ds != NULL) && (ccb->ccb_h.func_code == XPT_SCSI_IO)) 9027194d335SPoul-Henning Kamp devstat_start_transaction(ds, NULL); 9038b8a9b1dSJustin T. Gibbs 9048b8a9b1dSJustin T. Gibbs xpt_action(ccb); 9058b8a9b1dSJustin T. Gibbs 9068b8a9b1dSJustin T. Gibbs do { 9078b8a9b1dSJustin T. Gibbs cam_periph_ccbwait(ccb); 9088b8a9b1dSJustin T. Gibbs if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) 9098b8a9b1dSJustin T. Gibbs error = 0; 9108b8a9b1dSJustin T. Gibbs else if (error_routine != NULL) 9118b8a9b1dSJustin T. Gibbs error = (*error_routine)(ccb, camflags, sense_flags); 9128b8a9b1dSJustin T. Gibbs else 9138b8a9b1dSJustin T. Gibbs error = 0; 9148b8a9b1dSJustin T. Gibbs 9158b8a9b1dSJustin T. Gibbs } while (error == ERESTART); 9168b8a9b1dSJustin T. Gibbs 9178b8a9b1dSJustin T. Gibbs if ((ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) 9188b8a9b1dSJustin T. Gibbs cam_release_devq(ccb->ccb_h.path, 9198b8a9b1dSJustin T. Gibbs /* relsim_flags */0, 9208b8a9b1dSJustin T. Gibbs /* openings */0, 9218b8a9b1dSJustin T. Gibbs /* timeout */0, 9228b8a9b1dSJustin T. Gibbs /* getcount_only */ FALSE); 9238b8a9b1dSJustin T. Gibbs 9248b8a9b1dSJustin T. Gibbs if ((ds != NULL) && (ccb->ccb_h.func_code == XPT_SCSI_IO)) 9258b8a9b1dSJustin T. Gibbs devstat_end_transaction(ds, 9268b8a9b1dSJustin T. Gibbs ccb->csio.dxfer_len, 9278b8a9b1dSJustin T. Gibbs ccb->csio.tag_action & 0xf, 9288b8a9b1dSJustin T. Gibbs ((ccb->ccb_h.flags & CAM_DIR_MASK) == 9298b8a9b1dSJustin T. Gibbs CAM_DIR_NONE) ? DEVSTAT_NO_DATA : 9308b8a9b1dSJustin T. Gibbs (ccb->ccb_h.flags & CAM_DIR_OUT) ? 9318b8a9b1dSJustin T. Gibbs DEVSTAT_WRITE : 9327194d335SPoul-Henning Kamp DEVSTAT_READ, NULL, NULL); 9338b8a9b1dSJustin T. Gibbs 9348b8a9b1dSJustin T. Gibbs return(error); 9358b8a9b1dSJustin T. Gibbs } 9368b8a9b1dSJustin T. Gibbs 93787cfaf0eSJustin T. Gibbs void 93887cfaf0eSJustin T. Gibbs cam_freeze_devq(struct cam_path *path) 93987cfaf0eSJustin T. Gibbs { 94087cfaf0eSJustin T. Gibbs struct ccb_hdr ccb_h; 94187cfaf0eSJustin T. Gibbs 94287cfaf0eSJustin T. Gibbs xpt_setup_ccb(&ccb_h, path, /*priority*/1); 94387cfaf0eSJustin T. Gibbs ccb_h.func_code = XPT_NOOP; 94487cfaf0eSJustin T. Gibbs ccb_h.flags = CAM_DEV_QFREEZE; 94587cfaf0eSJustin T. Gibbs xpt_action((union ccb *)&ccb_h); 94687cfaf0eSJustin T. Gibbs } 94787cfaf0eSJustin T. Gibbs 9488b8a9b1dSJustin T. Gibbs u_int32_t 9498b8a9b1dSJustin T. Gibbs cam_release_devq(struct cam_path *path, u_int32_t relsim_flags, 9508b8a9b1dSJustin T. Gibbs u_int32_t openings, u_int32_t timeout, 9518b8a9b1dSJustin T. Gibbs int getcount_only) 9528b8a9b1dSJustin T. Gibbs { 9538b8a9b1dSJustin T. Gibbs struct ccb_relsim crs; 9548b8a9b1dSJustin T. Gibbs 9558b8a9b1dSJustin T. Gibbs xpt_setup_ccb(&crs.ccb_h, path, 9568b8a9b1dSJustin T. Gibbs /*priority*/1); 9578b8a9b1dSJustin T. Gibbs crs.ccb_h.func_code = XPT_REL_SIMQ; 9588b8a9b1dSJustin T. Gibbs crs.ccb_h.flags = getcount_only ? CAM_DEV_QFREEZE : 0; 9598b8a9b1dSJustin T. Gibbs crs.release_flags = relsim_flags; 9608b8a9b1dSJustin T. Gibbs crs.openings = openings; 9618b8a9b1dSJustin T. Gibbs crs.release_timeout = timeout; 9628b8a9b1dSJustin T. Gibbs xpt_action((union ccb *)&crs); 9638b8a9b1dSJustin T. Gibbs return (crs.qfrozen_cnt); 9648b8a9b1dSJustin T. Gibbs } 9658b8a9b1dSJustin T. Gibbs 9668b8a9b1dSJustin T. Gibbs #define saved_ccb_ptr ppriv_ptr0 9678b8a9b1dSJustin T. Gibbs static void 9688b8a9b1dSJustin T. Gibbs camperiphdone(struct cam_periph *periph, union ccb *done_ccb) 9698b8a9b1dSJustin T. Gibbs { 9703393f8daSKenneth D. Merry union ccb *saved_ccb; 9718b8a9b1dSJustin T. Gibbs cam_status status; 9728b8a9b1dSJustin T. Gibbs int frozen; 9738b8a9b1dSJustin T. Gibbs int sense; 9748b8a9b1dSJustin T. Gibbs struct scsi_start_stop_unit *scsi_cmd; 9758b8a9b1dSJustin T. Gibbs u_int32_t relsim_flags, timeout; 9768b8a9b1dSJustin T. Gibbs u_int32_t qfrozen_cnt; 9773393f8daSKenneth D. Merry int xpt_done_ccb; 9788b8a9b1dSJustin T. Gibbs 9793393f8daSKenneth D. Merry xpt_done_ccb = FALSE; 9808b8a9b1dSJustin T. Gibbs status = done_ccb->ccb_h.status; 9818b8a9b1dSJustin T. Gibbs frozen = (status & CAM_DEV_QFRZN) != 0; 9828b8a9b1dSJustin T. Gibbs sense = (status & CAM_AUTOSNS_VALID) != 0; 9838b8a9b1dSJustin T. Gibbs status &= CAM_STATUS_MASK; 9848b8a9b1dSJustin T. Gibbs 9858b8a9b1dSJustin T. Gibbs timeout = 0; 9868b8a9b1dSJustin T. Gibbs relsim_flags = 0; 9873393f8daSKenneth D. Merry saved_ccb = (union ccb *)done_ccb->ccb_h.saved_ccb_ptr; 9888b8a9b1dSJustin T. Gibbs 9898b8a9b1dSJustin T. Gibbs /* 9908b8a9b1dSJustin T. Gibbs * Unfreeze the queue once if it is already frozen.. 9918b8a9b1dSJustin T. Gibbs */ 9928b8a9b1dSJustin T. Gibbs if (frozen != 0) { 9938b8a9b1dSJustin T. Gibbs qfrozen_cnt = cam_release_devq(done_ccb->ccb_h.path, 9948b8a9b1dSJustin T. Gibbs /*relsim_flags*/0, 9958b8a9b1dSJustin T. Gibbs /*openings*/0, 9968b8a9b1dSJustin T. Gibbs /*timeout*/0, 9978b8a9b1dSJustin T. Gibbs /*getcount_only*/0); 9988b8a9b1dSJustin T. Gibbs } 9998b8a9b1dSJustin T. Gibbs 10008b8a9b1dSJustin T. Gibbs switch (status) { 10018b8a9b1dSJustin T. Gibbs case CAM_REQ_CMP: 10023393f8daSKenneth D. Merry { 10038b8a9b1dSJustin T. Gibbs /* 10048b8a9b1dSJustin T. Gibbs * If we have successfully taken a device from the not 10053393f8daSKenneth D. Merry * ready to ready state, re-scan the device and re-get 10063393f8daSKenneth D. Merry * the inquiry information. Many devices (mostly disks) 10073393f8daSKenneth D. Merry * don't properly report their inquiry information unless 10083393f8daSKenneth D. Merry * they are spun up. 10093393f8daSKenneth D. Merry * 10103393f8daSKenneth D. Merry * If we manually retrieved sense into a CCB and got 10113393f8daSKenneth D. Merry * something other than "NO SENSE" send the updated CCB 10123393f8daSKenneth D. Merry * back to the client via xpt_done() to be processed via 10133393f8daSKenneth D. Merry * the error recovery code again. 10148b8a9b1dSJustin T. Gibbs */ 10158b8a9b1dSJustin T. Gibbs if (done_ccb->ccb_h.func_code == XPT_SCSI_IO) { 10168b8a9b1dSJustin T. Gibbs scsi_cmd = (struct scsi_start_stop_unit *) 10178b8a9b1dSJustin T. Gibbs &done_ccb->csio.cdb_io.cdb_bytes; 10188b8a9b1dSJustin T. Gibbs 10198b8a9b1dSJustin T. Gibbs if (scsi_cmd->opcode == START_STOP_UNIT) 10208b8a9b1dSJustin T. Gibbs xpt_async(AC_INQ_CHANGED, 10218b8a9b1dSJustin T. Gibbs done_ccb->ccb_h.path, NULL); 10223393f8daSKenneth D. Merry if (scsi_cmd->opcode == REQUEST_SENSE) { 10233393f8daSKenneth D. Merry u_int sense_key; 10243393f8daSKenneth D. Merry 10253393f8daSKenneth D. Merry sense_key = saved_ccb->csio.sense_data.flags; 10263393f8daSKenneth D. Merry sense_key &= SSD_KEY; 10273393f8daSKenneth D. Merry if (sense_key != SSD_KEY_NO_SENSE) { 102886addc52SThomas Quinot saved_ccb->ccb_h.status |= 10293393f8daSKenneth D. Merry CAM_AUTOSNS_VALID; 10308226fdbdSScott Long #if 0 1031f0d9af51SMatt Jacob xpt_print(saved_ccb->ccb_h.path, 1032f0d9af51SMatt Jacob "Recovered Sense\n"); 10333393f8daSKenneth D. Merry scsi_sense_print(&saved_ccb->csio); 10343393f8daSKenneth D. Merry cam_error_print(saved_ccb, CAM_ESF_ALL, 10353393f8daSKenneth D. Merry CAM_EPF_ALL); 10368226fdbdSScott Long #endif 10373393f8daSKenneth D. Merry xpt_done_ccb = TRUE; 10383393f8daSKenneth D. Merry } 10393393f8daSKenneth D. Merry } 10408b8a9b1dSJustin T. Gibbs } 10418b8a9b1dSJustin T. Gibbs bcopy(done_ccb->ccb_h.saved_ccb_ptr, done_ccb, 10428b8a9b1dSJustin T. Gibbs sizeof(union ccb)); 10438b8a9b1dSJustin T. Gibbs 104460a899a0SKenneth D. Merry periph->flags &= ~CAM_PERIPH_RECOVERY_INPROG; 104560a899a0SKenneth D. Merry 10463393f8daSKenneth D. Merry if (xpt_done_ccb == FALSE) 10478b8a9b1dSJustin T. Gibbs xpt_action(done_ccb); 10488b8a9b1dSJustin T. Gibbs 10498b8a9b1dSJustin T. Gibbs break; 10503393f8daSKenneth D. Merry } 10518b8a9b1dSJustin T. Gibbs case CAM_SCSI_STATUS_ERROR: 10528b8a9b1dSJustin T. Gibbs scsi_cmd = (struct scsi_start_stop_unit *) 10538b8a9b1dSJustin T. Gibbs &done_ccb->csio.cdb_io.cdb_bytes; 10548b8a9b1dSJustin T. Gibbs if (sense != 0) { 105565afe1f4SKenneth D. Merry struct ccb_getdev cgd; 10568b8a9b1dSJustin T. Gibbs struct scsi_sense_data *sense; 10578b8a9b1dSJustin T. Gibbs int error_code, sense_key, asc, ascq; 105865afe1f4SKenneth D. Merry scsi_sense_action err_action; 10598b8a9b1dSJustin T. Gibbs 10608b8a9b1dSJustin T. Gibbs sense = &done_ccb->csio.sense_data; 10618b8a9b1dSJustin T. Gibbs scsi_extract_sense(sense, &error_code, 10628b8a9b1dSJustin T. Gibbs &sense_key, &asc, &ascq); 10638b8a9b1dSJustin T. Gibbs 10648b8a9b1dSJustin T. Gibbs /* 106565afe1f4SKenneth D. Merry * Grab the inquiry data for this device. 106665afe1f4SKenneth D. Merry */ 106765afe1f4SKenneth D. Merry xpt_setup_ccb(&cgd.ccb_h, done_ccb->ccb_h.path, 106865afe1f4SKenneth D. Merry /*priority*/ 1); 106965afe1f4SKenneth D. Merry cgd.ccb_h.func_code = XPT_GDEV_TYPE; 107065afe1f4SKenneth D. Merry xpt_action((union ccb *)&cgd); 107165afe1f4SKenneth D. Merry err_action = scsi_error_action(&done_ccb->csio, 107265afe1f4SKenneth D. Merry &cgd.inq_data, 0); 107365afe1f4SKenneth D. Merry 107465afe1f4SKenneth D. Merry /* 10758b8a9b1dSJustin T. Gibbs * If the error is "invalid field in CDB", 10768b8a9b1dSJustin T. Gibbs * and the load/eject flag is set, turn the 10778b8a9b1dSJustin T. Gibbs * flag off and try again. This is just in 10788b8a9b1dSJustin T. Gibbs * case the drive in question barfs on the 10798b8a9b1dSJustin T. Gibbs * load eject flag. The CAM code should set 10808b8a9b1dSJustin T. Gibbs * the load/eject flag by default for 10818b8a9b1dSJustin T. Gibbs * removable media. 10828b8a9b1dSJustin T. Gibbs */ 10838b8a9b1dSJustin T. Gibbs 10848b8a9b1dSJustin T. Gibbs /* XXX KDM 10858b8a9b1dSJustin T. Gibbs * Should we check to see what the specific 10868b8a9b1dSJustin T. Gibbs * scsi status is?? Or does it not matter 10878b8a9b1dSJustin T. Gibbs * since we already know that there was an 10888b8a9b1dSJustin T. Gibbs * error, and we know what the specific 10898b8a9b1dSJustin T. Gibbs * error code was, and we know what the 10908b8a9b1dSJustin T. Gibbs * opcode is.. 10918b8a9b1dSJustin T. Gibbs */ 10928b8a9b1dSJustin T. Gibbs if ((scsi_cmd->opcode == START_STOP_UNIT) && 10938b8a9b1dSJustin T. Gibbs ((scsi_cmd->how & SSS_LOEJ) != 0) && 10948b8a9b1dSJustin T. Gibbs (asc == 0x24) && (ascq == 0x00) && 10958b8a9b1dSJustin T. Gibbs (done_ccb->ccb_h.retry_count > 0)) { 10968b8a9b1dSJustin T. Gibbs 10978b8a9b1dSJustin T. Gibbs scsi_cmd->how &= ~SSS_LOEJ; 10988b8a9b1dSJustin T. Gibbs 10998b8a9b1dSJustin T. Gibbs xpt_action(done_ccb); 11008b8a9b1dSJustin T. Gibbs 110165afe1f4SKenneth D. Merry } else if ((done_ccb->ccb_h.retry_count > 1) 110265afe1f4SKenneth D. Merry && ((err_action & SS_MASK) != SS_FAIL)) { 110365afe1f4SKenneth D. Merry 11048b8a9b1dSJustin T. Gibbs /* 11058b8a9b1dSJustin T. Gibbs * In this case, the error recovery 11068b8a9b1dSJustin T. Gibbs * command failed, but we've got 11078b8a9b1dSJustin T. Gibbs * some retries left on it. Give 110865afe1f4SKenneth D. Merry * it another try unless this is an 110965afe1f4SKenneth D. Merry * unretryable error. 11108b8a9b1dSJustin T. Gibbs */ 11118b8a9b1dSJustin T. Gibbs 11128b8a9b1dSJustin T. Gibbs /* set the timeout to .5 sec */ 11138b8a9b1dSJustin T. Gibbs relsim_flags = 11148b8a9b1dSJustin T. Gibbs RELSIM_RELEASE_AFTER_TIMEOUT; 11158b8a9b1dSJustin T. Gibbs timeout = 500; 11168b8a9b1dSJustin T. Gibbs 11178b8a9b1dSJustin T. Gibbs xpt_action(done_ccb); 11188b8a9b1dSJustin T. Gibbs 11198b8a9b1dSJustin T. Gibbs break; 11208b8a9b1dSJustin T. Gibbs 11218b8a9b1dSJustin T. Gibbs } else { 11228b8a9b1dSJustin T. Gibbs /* 11233393f8daSKenneth D. Merry * Perform the final retry with the original 11243393f8daSKenneth D. Merry * CCB so that final error processing is 11253393f8daSKenneth D. Merry * performed by the owner of the CCB. 11268b8a9b1dSJustin T. Gibbs */ 11278b8a9b1dSJustin T. Gibbs bcopy(done_ccb->ccb_h.saved_ccb_ptr, 11288b8a9b1dSJustin T. Gibbs done_ccb, sizeof(union ccb)); 11298b8a9b1dSJustin T. Gibbs 113060a899a0SKenneth D. Merry periph->flags &= ~CAM_PERIPH_RECOVERY_INPROG; 113160a899a0SKenneth D. Merry 11328b8a9b1dSJustin T. Gibbs xpt_action(done_ccb); 11338b8a9b1dSJustin T. Gibbs } 11348b8a9b1dSJustin T. Gibbs } else { 11358b8a9b1dSJustin T. Gibbs /* 11368b8a9b1dSJustin T. Gibbs * Eh?? The command failed, but we don't 11378b8a9b1dSJustin T. Gibbs * have any sense. What's up with that? 11388b8a9b1dSJustin T. Gibbs * Fire the CCB again to return it to the 11398b8a9b1dSJustin T. Gibbs * caller. 11408b8a9b1dSJustin T. Gibbs */ 11418b8a9b1dSJustin T. Gibbs bcopy(done_ccb->ccb_h.saved_ccb_ptr, 11428b8a9b1dSJustin T. Gibbs done_ccb, sizeof(union ccb)); 11438b8a9b1dSJustin T. Gibbs 114460a899a0SKenneth D. Merry periph->flags &= ~CAM_PERIPH_RECOVERY_INPROG; 114560a899a0SKenneth D. Merry 11468b8a9b1dSJustin T. Gibbs xpt_action(done_ccb); 11478b8a9b1dSJustin T. Gibbs 11488b8a9b1dSJustin T. Gibbs } 11498b8a9b1dSJustin T. Gibbs break; 11508b8a9b1dSJustin T. Gibbs default: 11518b8a9b1dSJustin T. Gibbs bcopy(done_ccb->ccb_h.saved_ccb_ptr, done_ccb, 11528b8a9b1dSJustin T. Gibbs sizeof(union ccb)); 11538b8a9b1dSJustin T. Gibbs 115460a899a0SKenneth D. Merry periph->flags &= ~CAM_PERIPH_RECOVERY_INPROG; 115560a899a0SKenneth D. Merry 11568b8a9b1dSJustin T. Gibbs xpt_action(done_ccb); 11578b8a9b1dSJustin T. Gibbs 11588b8a9b1dSJustin T. Gibbs break; 11598b8a9b1dSJustin T. Gibbs } 11608b8a9b1dSJustin T. Gibbs 11618b8a9b1dSJustin T. Gibbs /* decrement the retry count */ 11623393f8daSKenneth D. Merry /* 11633393f8daSKenneth D. Merry * XXX This isn't appropriate in all cases. Restructure, 11643393f8daSKenneth D. Merry * so that the retry count is only decremented on an 11653393f8daSKenneth D. Merry * actual retry. Remeber that the orignal ccb had its 11663393f8daSKenneth D. Merry * retry count dropped before entering recovery, so 11673393f8daSKenneth D. Merry * doing it again is a bug. 11683393f8daSKenneth D. Merry */ 11698b8a9b1dSJustin T. Gibbs if (done_ccb->ccb_h.retry_count > 0) 11708b8a9b1dSJustin T. Gibbs done_ccb->ccb_h.retry_count--; 11718b8a9b1dSJustin T. Gibbs 11728b8a9b1dSJustin T. Gibbs qfrozen_cnt = cam_release_devq(done_ccb->ccb_h.path, 11738b8a9b1dSJustin T. Gibbs /*relsim_flags*/relsim_flags, 11748b8a9b1dSJustin T. Gibbs /*openings*/0, 11758b8a9b1dSJustin T. Gibbs /*timeout*/timeout, 11768b8a9b1dSJustin T. Gibbs /*getcount_only*/0); 11773393f8daSKenneth D. Merry if (xpt_done_ccb == TRUE) 11783393f8daSKenneth D. Merry (*done_ccb->ccb_h.cbfcnp)(periph, done_ccb); 11798b8a9b1dSJustin T. Gibbs } 11808b8a9b1dSJustin T. Gibbs 11818b8a9b1dSJustin T. Gibbs /* 118287cfaf0eSJustin T. Gibbs * Generic Async Event handler. Peripheral drivers usually 118387cfaf0eSJustin T. Gibbs * filter out the events that require personal attention, 118487cfaf0eSJustin T. Gibbs * and leave the rest to this function. 118587cfaf0eSJustin T. Gibbs */ 118687cfaf0eSJustin T. Gibbs void 118787cfaf0eSJustin T. Gibbs cam_periph_async(struct cam_periph *periph, u_int32_t code, 118887cfaf0eSJustin T. Gibbs struct cam_path *path, void *arg) 118987cfaf0eSJustin T. Gibbs { 119087cfaf0eSJustin T. Gibbs switch (code) { 119187cfaf0eSJustin T. Gibbs case AC_LOST_DEVICE: 119287cfaf0eSJustin T. Gibbs cam_periph_invalidate(periph); 119387cfaf0eSJustin T. Gibbs break; 119487cfaf0eSJustin T. Gibbs case AC_SENT_BDR: 119587cfaf0eSJustin T. Gibbs case AC_BUS_RESET: 119687cfaf0eSJustin T. Gibbs { 11973a937198SBrooks Davis cam_periph_bus_settle(periph, scsi_delay); 119887cfaf0eSJustin T. Gibbs break; 119987cfaf0eSJustin T. Gibbs } 120087cfaf0eSJustin T. Gibbs default: 120187cfaf0eSJustin T. Gibbs break; 120287cfaf0eSJustin T. Gibbs } 120387cfaf0eSJustin T. Gibbs } 120487cfaf0eSJustin T. Gibbs 120587cfaf0eSJustin T. Gibbs void 120687cfaf0eSJustin T. Gibbs cam_periph_bus_settle(struct cam_periph *periph, u_int bus_settle) 120787cfaf0eSJustin T. Gibbs { 120887cfaf0eSJustin T. Gibbs struct ccb_getdevstats cgds; 120987cfaf0eSJustin T. Gibbs 121087cfaf0eSJustin T. Gibbs xpt_setup_ccb(&cgds.ccb_h, periph->path, /*priority*/1); 121187cfaf0eSJustin T. Gibbs cgds.ccb_h.func_code = XPT_GDEV_STATS; 121287cfaf0eSJustin T. Gibbs xpt_action((union ccb *)&cgds); 121387cfaf0eSJustin T. Gibbs cam_periph_freeze_after_event(periph, &cgds.last_reset, bus_settle); 121487cfaf0eSJustin T. Gibbs } 121587cfaf0eSJustin T. Gibbs 121687cfaf0eSJustin T. Gibbs void 121787cfaf0eSJustin T. Gibbs cam_periph_freeze_after_event(struct cam_periph *periph, 121887cfaf0eSJustin T. Gibbs struct timeval* event_time, u_int duration_ms) 121987cfaf0eSJustin T. Gibbs { 122087cfaf0eSJustin T. Gibbs struct timeval delta; 122187cfaf0eSJustin T. Gibbs struct timeval duration_tv; 122287cfaf0eSJustin T. Gibbs 122387cfaf0eSJustin T. Gibbs microtime(&delta); 122487cfaf0eSJustin T. Gibbs timevalsub(&delta, event_time); 122587cfaf0eSJustin T. Gibbs duration_tv.tv_sec = duration_ms / 1000; 122687cfaf0eSJustin T. Gibbs duration_tv.tv_usec = (duration_ms % 1000) * 1000; 122787cfaf0eSJustin T. Gibbs if (timevalcmp(&delta, &duration_tv, <)) { 122887cfaf0eSJustin T. Gibbs timevalsub(&duration_tv, &delta); 122987cfaf0eSJustin T. Gibbs 123087cfaf0eSJustin T. Gibbs duration_ms = duration_tv.tv_sec * 1000; 123187cfaf0eSJustin T. Gibbs duration_ms += duration_tv.tv_usec / 1000; 123287cfaf0eSJustin T. Gibbs cam_freeze_devq(periph->path); 123387cfaf0eSJustin T. Gibbs cam_release_devq(periph->path, 123487cfaf0eSJustin T. Gibbs RELSIM_RELEASE_AFTER_TIMEOUT, 123587cfaf0eSJustin T. Gibbs /*reduction*/0, 123687cfaf0eSJustin T. Gibbs /*timeout*/duration_ms, 123787cfaf0eSJustin T. Gibbs /*getcount_only*/0); 123887cfaf0eSJustin T. Gibbs } 123987cfaf0eSJustin T. Gibbs 124087cfaf0eSJustin T. Gibbs } 124187cfaf0eSJustin T. Gibbs 12423393f8daSKenneth D. Merry static int 12433393f8daSKenneth D. Merry camperiphscsistatuserror(union ccb *ccb, cam_flags camflags, 12443393f8daSKenneth D. Merry u_int32_t sense_flags, union ccb *save_ccb, 12453393f8daSKenneth D. Merry int *openings, u_int32_t *relsim_flags, 12463393f8daSKenneth D. Merry u_int32_t *timeout) 12478b8a9b1dSJustin T. Gibbs { 12488b8a9b1dSJustin T. Gibbs int error; 12498b8a9b1dSJustin T. Gibbs 12508b8a9b1dSJustin T. Gibbs switch (ccb->csio.scsi_status) { 12518b8a9b1dSJustin T. Gibbs case SCSI_STATUS_OK: 12528b8a9b1dSJustin T. Gibbs case SCSI_STATUS_COND_MET: 12538b8a9b1dSJustin T. Gibbs case SCSI_STATUS_INTERMED: 12548b8a9b1dSJustin T. Gibbs case SCSI_STATUS_INTERMED_COND_MET: 12558b8a9b1dSJustin T. Gibbs error = 0; 12568b8a9b1dSJustin T. Gibbs break; 12578b8a9b1dSJustin T. Gibbs case SCSI_STATUS_CMD_TERMINATED: 12588b8a9b1dSJustin T. Gibbs case SCSI_STATUS_CHECK_COND: 12593393f8daSKenneth D. Merry error = camperiphscsisenseerror(ccb, 12603393f8daSKenneth D. Merry camflags, 12618b8a9b1dSJustin T. Gibbs sense_flags, 12623393f8daSKenneth D. Merry save_ccb, 12633393f8daSKenneth D. Merry openings, 12643393f8daSKenneth D. Merry relsim_flags, 12653393f8daSKenneth D. Merry timeout); 12668b8a9b1dSJustin T. Gibbs break; 12678b8a9b1dSJustin T. Gibbs case SCSI_STATUS_QUEUE_FULL: 12688b8a9b1dSJustin T. Gibbs { 12698b8a9b1dSJustin T. Gibbs /* no decrement */ 127082815562SJustin T. Gibbs struct ccb_getdevstats cgds; 12718b8a9b1dSJustin T. Gibbs 12728b8a9b1dSJustin T. Gibbs /* 12738b8a9b1dSJustin T. Gibbs * First off, find out what the current 12748b8a9b1dSJustin T. Gibbs * transaction counts are. 12758b8a9b1dSJustin T. Gibbs */ 127682815562SJustin T. Gibbs xpt_setup_ccb(&cgds.ccb_h, 12778b8a9b1dSJustin T. Gibbs ccb->ccb_h.path, 12788b8a9b1dSJustin T. Gibbs /*priority*/1); 127982815562SJustin T. Gibbs cgds.ccb_h.func_code = XPT_GDEV_STATS; 128082815562SJustin T. Gibbs xpt_action((union ccb *)&cgds); 12818b8a9b1dSJustin T. Gibbs 12828b8a9b1dSJustin T. Gibbs /* 12838b8a9b1dSJustin T. Gibbs * If we were the only transaction active, treat 12848b8a9b1dSJustin T. Gibbs * the QUEUE FULL as if it were a BUSY condition. 12858b8a9b1dSJustin T. Gibbs */ 128682815562SJustin T. Gibbs if (cgds.dev_active != 0) { 128782815562SJustin T. Gibbs int total_openings; 128882815562SJustin T. Gibbs 12898b8a9b1dSJustin T. Gibbs /* 12908b8a9b1dSJustin T. Gibbs * Reduce the number of openings to 12918b8a9b1dSJustin T. Gibbs * be 1 less than the amount it took 12928b8a9b1dSJustin T. Gibbs * to get a queue full bounded by the 12938b8a9b1dSJustin T. Gibbs * minimum allowed tag count for this 12948b8a9b1dSJustin T. Gibbs * device. 12958b8a9b1dSJustin T. Gibbs */ 12963393f8daSKenneth D. Merry total_openings = cgds.dev_active + cgds.dev_openings; 12973393f8daSKenneth D. Merry *openings = cgds.dev_active; 12983393f8daSKenneth D. Merry if (*openings < cgds.mintags) 12993393f8daSKenneth D. Merry *openings = cgds.mintags; 13003393f8daSKenneth D. Merry if (*openings < total_openings) 13013393f8daSKenneth D. Merry *relsim_flags = RELSIM_ADJUST_OPENINGS; 13028b8a9b1dSJustin T. Gibbs else { 13038b8a9b1dSJustin T. Gibbs /* 13048b8a9b1dSJustin T. Gibbs * Some devices report queue full for 13058b8a9b1dSJustin T. Gibbs * temporary resource shortages. For 13068b8a9b1dSJustin T. Gibbs * this reason, we allow a minimum 13078b8a9b1dSJustin T. Gibbs * tag count to be entered via a 13088b8a9b1dSJustin T. Gibbs * quirk entry to prevent the queue 13098b8a9b1dSJustin T. Gibbs * count on these devices from falling 13108b8a9b1dSJustin T. Gibbs * to a pessimisticly low value. We 13118b8a9b1dSJustin T. Gibbs * still wait for the next successful 13128b8a9b1dSJustin T. Gibbs * completion, however, before queueing 13138b8a9b1dSJustin T. Gibbs * more transactions to the device. 13148b8a9b1dSJustin T. Gibbs */ 13153393f8daSKenneth D. Merry *relsim_flags = RELSIM_RELEASE_AFTER_CMDCMPLT; 13168b8a9b1dSJustin T. Gibbs } 13173393f8daSKenneth D. Merry *timeout = 0; 13188b8a9b1dSJustin T. Gibbs error = ERESTART; 131996d333b7SMatt Jacob if (bootverbose) { 1320f0d9af51SMatt Jacob xpt_print(ccb->ccb_h.path, "Queue Full\n"); 132196d333b7SMatt Jacob } 13228b8a9b1dSJustin T. Gibbs break; 13238b8a9b1dSJustin T. Gibbs } 13248b8a9b1dSJustin T. Gibbs /* FALLTHROUGH */ 13258b8a9b1dSJustin T. Gibbs } 13268b8a9b1dSJustin T. Gibbs case SCSI_STATUS_BUSY: 13278b8a9b1dSJustin T. Gibbs /* 13288b8a9b1dSJustin T. Gibbs * Restart the queue after either another 13298b8a9b1dSJustin T. Gibbs * command completes or a 1 second timeout. 13308b8a9b1dSJustin T. Gibbs */ 133196d333b7SMatt Jacob if (bootverbose) { 1332f0d9af51SMatt Jacob xpt_print(ccb->ccb_h.path, "Device Busy\n"); 133396d333b7SMatt Jacob } 13343393f8daSKenneth D. Merry if (ccb->ccb_h.retry_count > 0) { 1335af51b059SMatt Jacob ccb->ccb_h.retry_count--; 13368b8a9b1dSJustin T. Gibbs error = ERESTART; 13373393f8daSKenneth D. Merry *relsim_flags = RELSIM_RELEASE_AFTER_TIMEOUT 13388b8a9b1dSJustin T. Gibbs | RELSIM_RELEASE_AFTER_CMDCMPLT; 13393393f8daSKenneth D. Merry *timeout = 1000; 1340af51b059SMatt Jacob } else { 1341af51b059SMatt Jacob error = EIO; 1342af51b059SMatt Jacob } 13438b8a9b1dSJustin T. Gibbs break; 13448b8a9b1dSJustin T. Gibbs case SCSI_STATUS_RESERV_CONFLICT: 1345f0d9af51SMatt Jacob xpt_print(ccb->ccb_h.path, "Reservation Conflict\n"); 13468b8a9b1dSJustin T. Gibbs error = EIO; 13478b8a9b1dSJustin T. Gibbs break; 13488b8a9b1dSJustin T. Gibbs default: 1349f0d9af51SMatt Jacob xpt_print(ccb->ccb_h.path, "SCSI Status 0x%x\n", 1350f0d9af51SMatt Jacob ccb->csio.scsi_status); 13518b8a9b1dSJustin T. Gibbs error = EIO; 13528b8a9b1dSJustin T. Gibbs break; 13538b8a9b1dSJustin T. Gibbs } 13543393f8daSKenneth D. Merry return (error); 13553393f8daSKenneth D. Merry } 13563393f8daSKenneth D. Merry 13573393f8daSKenneth D. Merry static int 13583393f8daSKenneth D. Merry camperiphscsisenseerror(union ccb *ccb, cam_flags camflags, 13593393f8daSKenneth D. Merry u_int32_t sense_flags, union ccb *save_ccb, 13603393f8daSKenneth D. Merry int *openings, u_int32_t *relsim_flags, 13613393f8daSKenneth D. Merry u_int32_t *timeout) 13623393f8daSKenneth D. Merry { 13633393f8daSKenneth D. Merry struct cam_periph *periph; 13643393f8daSKenneth D. Merry int error; 13653393f8daSKenneth D. Merry 13663393f8daSKenneth D. Merry periph = xpt_path_periph(ccb->ccb_h.path); 13673393f8daSKenneth D. Merry if (periph->flags & CAM_PERIPH_RECOVERY_INPROG) { 13683393f8daSKenneth D. Merry 13693393f8daSKenneth D. Merry /* 13703393f8daSKenneth D. Merry * If error recovery is already in progress, don't attempt 13713393f8daSKenneth D. Merry * to process this error, but requeue it unconditionally 13723393f8daSKenneth D. Merry * and attempt to process it once error recovery has 13733393f8daSKenneth D. Merry * completed. This failed command is probably related to 13743393f8daSKenneth D. Merry * the error that caused the currently active error recovery 13753393f8daSKenneth D. Merry * action so our current recovery efforts should also 13763393f8daSKenneth D. Merry * address this command. Be aware that the error recovery 13773393f8daSKenneth D. Merry * code assumes that only one recovery action is in progress 13783393f8daSKenneth D. Merry * on a particular peripheral instance at any given time 13793393f8daSKenneth D. Merry * (e.g. only one saved CCB for error recovery) so it is 13803393f8daSKenneth D. Merry * imperitive that we don't violate this assumption. 13813393f8daSKenneth D. Merry */ 13823393f8daSKenneth D. Merry error = ERESTART; 13833393f8daSKenneth D. Merry } else { 13843393f8daSKenneth D. Merry scsi_sense_action err_action; 13853393f8daSKenneth D. Merry struct ccb_getdev cgd; 13863393f8daSKenneth D. Merry const char *action_string; 13873393f8daSKenneth D. Merry union ccb* print_ccb; 13883393f8daSKenneth D. Merry 13893393f8daSKenneth D. Merry /* A description of the error recovery action performed */ 13903393f8daSKenneth D. Merry action_string = NULL; 13913393f8daSKenneth D. Merry 13923393f8daSKenneth D. Merry /* 13933393f8daSKenneth D. Merry * The location of the orignal ccb 13943393f8daSKenneth D. Merry * for sense printing purposes. 13953393f8daSKenneth D. Merry */ 13963393f8daSKenneth D. Merry print_ccb = ccb; 13973393f8daSKenneth D. Merry 13983393f8daSKenneth D. Merry /* 13993393f8daSKenneth D. Merry * Grab the inquiry data for this device. 14003393f8daSKenneth D. Merry */ 14013393f8daSKenneth D. Merry xpt_setup_ccb(&cgd.ccb_h, ccb->ccb_h.path, /*priority*/ 1); 14023393f8daSKenneth D. Merry cgd.ccb_h.func_code = XPT_GDEV_TYPE; 14033393f8daSKenneth D. Merry xpt_action((union ccb *)&cgd); 14043393f8daSKenneth D. Merry 14053393f8daSKenneth D. Merry if ((ccb->ccb_h.status & CAM_AUTOSNS_VALID) != 0) 14063393f8daSKenneth D. Merry err_action = scsi_error_action(&ccb->csio, 14073393f8daSKenneth D. Merry &cgd.inq_data, 14083393f8daSKenneth D. Merry sense_flags); 14093393f8daSKenneth D. Merry else if ((ccb->ccb_h.flags & CAM_DIS_AUTOSENSE) == 0) 14103393f8daSKenneth D. Merry err_action = SS_REQSENSE; 14113393f8daSKenneth D. Merry else 14123393f8daSKenneth D. Merry err_action = SS_RETRY|SSQ_DECREMENT_COUNT|EIO; 14133393f8daSKenneth D. Merry 14143393f8daSKenneth D. Merry error = err_action & SS_ERRMASK; 14153393f8daSKenneth D. Merry 14163393f8daSKenneth D. Merry /* 14173393f8daSKenneth D. Merry * If the recovery action will consume a retry, 14183393f8daSKenneth D. Merry * make sure we actually have retries available. 14193393f8daSKenneth D. Merry */ 14203393f8daSKenneth D. Merry if ((err_action & SSQ_DECREMENT_COUNT) != 0) { 14213393f8daSKenneth D. Merry if (ccb->ccb_h.retry_count > 0) 14223393f8daSKenneth D. Merry ccb->ccb_h.retry_count--; 14233393f8daSKenneth D. Merry else { 14243393f8daSKenneth D. Merry action_string = "Retries Exhausted"; 14253393f8daSKenneth D. Merry goto sense_error_done; 14263393f8daSKenneth D. Merry } 14273393f8daSKenneth D. Merry } 14283393f8daSKenneth D. Merry 14293393f8daSKenneth D. Merry if ((err_action & SS_MASK) >= SS_START) { 14303393f8daSKenneth D. Merry /* 14313393f8daSKenneth D. Merry * Do common portions of commands that 14323393f8daSKenneth D. Merry * use recovery CCBs. 14333393f8daSKenneth D. Merry */ 14343393f8daSKenneth D. Merry if (save_ccb == NULL) { 14353393f8daSKenneth D. Merry action_string = "No recovery CCB supplied"; 14363393f8daSKenneth D. Merry goto sense_error_done; 14373393f8daSKenneth D. Merry } 14383393f8daSKenneth D. Merry bcopy(ccb, save_ccb, sizeof(*save_ccb)); 14393393f8daSKenneth D. Merry print_ccb = save_ccb; 14403393f8daSKenneth D. Merry periph->flags |= CAM_PERIPH_RECOVERY_INPROG; 14413393f8daSKenneth D. Merry } 14423393f8daSKenneth D. Merry 14433393f8daSKenneth D. Merry switch (err_action & SS_MASK) { 14443393f8daSKenneth D. Merry case SS_NOP: 144500e54d14SKenneth D. Merry action_string = "No Recovery Action Needed"; 144600e54d14SKenneth D. Merry error = 0; 144700e54d14SKenneth D. Merry break; 14483393f8daSKenneth D. Merry case SS_RETRY: 144996d333b7SMatt Jacob action_string = "Retrying Command (per Sense Data)"; 14503393f8daSKenneth D. Merry error = ERESTART; 14518b8a9b1dSJustin T. Gibbs break; 14523393f8daSKenneth D. Merry case SS_FAIL: 14533393f8daSKenneth D. Merry action_string = "Unretryable error"; 14543393f8daSKenneth D. Merry break; 14553393f8daSKenneth D. Merry case SS_START: 14563393f8daSKenneth D. Merry { 14573393f8daSKenneth D. Merry int le; 14583393f8daSKenneth D. Merry 14593393f8daSKenneth D. Merry /* 14603393f8daSKenneth D. Merry * Send a start unit command to the device, and 14613393f8daSKenneth D. Merry * then retry the command. 14623393f8daSKenneth D. Merry */ 14633393f8daSKenneth D. Merry action_string = "Attempting to Start Unit"; 14643393f8daSKenneth D. Merry 14653393f8daSKenneth D. Merry /* 14663393f8daSKenneth D. Merry * Check for removable media and set 14673393f8daSKenneth D. Merry * load/eject flag appropriately. 14683393f8daSKenneth D. Merry */ 14693393f8daSKenneth D. Merry if (SID_IS_REMOVABLE(&cgd.inq_data)) 14703393f8daSKenneth D. Merry le = TRUE; 14713393f8daSKenneth D. Merry else 14723393f8daSKenneth D. Merry le = FALSE; 14733393f8daSKenneth D. Merry 14743393f8daSKenneth D. Merry scsi_start_stop(&ccb->csio, 14753393f8daSKenneth D. Merry /*retries*/1, 14763393f8daSKenneth D. Merry camperiphdone, 14773393f8daSKenneth D. Merry MSG_SIMPLE_Q_TAG, 14783393f8daSKenneth D. Merry /*start*/TRUE, 14793393f8daSKenneth D. Merry /*load/eject*/le, 14803393f8daSKenneth D. Merry /*immediate*/FALSE, 14813393f8daSKenneth D. Merry SSD_FULL_SIZE, 14823393f8daSKenneth D. Merry /*timeout*/50000); 14833393f8daSKenneth D. Merry break; 14843393f8daSKenneth D. Merry } 14853393f8daSKenneth D. Merry case SS_TUR: 14863393f8daSKenneth D. Merry { 14873393f8daSKenneth D. Merry /* 14883393f8daSKenneth D. Merry * Send a Test Unit Ready to the device. 14893393f8daSKenneth D. Merry * If the 'many' flag is set, we send 120 14903393f8daSKenneth D. Merry * test unit ready commands, one every half 14913393f8daSKenneth D. Merry * second. Otherwise, we just send one TUR. 14923393f8daSKenneth D. Merry * We only want to do this if the retry 14933393f8daSKenneth D. Merry * count has not been exhausted. 14943393f8daSKenneth D. Merry */ 14953393f8daSKenneth D. Merry int retries; 14963393f8daSKenneth D. Merry 14973393f8daSKenneth D. Merry if ((err_action & SSQ_MANY) != 0) { 14983393f8daSKenneth D. Merry action_string = "Polling device for readiness"; 14993393f8daSKenneth D. Merry retries = 120; 15003393f8daSKenneth D. Merry } else { 15013393f8daSKenneth D. Merry action_string = "Testing device for readiness"; 15023393f8daSKenneth D. Merry retries = 1; 15033393f8daSKenneth D. Merry } 15043393f8daSKenneth D. Merry scsi_test_unit_ready(&ccb->csio, 15053393f8daSKenneth D. Merry retries, 15063393f8daSKenneth D. Merry camperiphdone, 15073393f8daSKenneth D. Merry MSG_SIMPLE_Q_TAG, 15083393f8daSKenneth D. Merry SSD_FULL_SIZE, 15093393f8daSKenneth D. Merry /*timeout*/5000); 15103393f8daSKenneth D. Merry 15113393f8daSKenneth D. Merry /* 15123393f8daSKenneth D. Merry * Accomplish our 500ms delay by deferring 15133393f8daSKenneth D. Merry * the release of our device queue appropriately. 15143393f8daSKenneth D. Merry */ 15153393f8daSKenneth D. Merry *relsim_flags = RELSIM_RELEASE_AFTER_TIMEOUT; 15163393f8daSKenneth D. Merry *timeout = 500; 15173393f8daSKenneth D. Merry break; 15183393f8daSKenneth D. Merry } 15193393f8daSKenneth D. Merry case SS_REQSENSE: 15203393f8daSKenneth D. Merry { 15213393f8daSKenneth D. Merry /* 15223393f8daSKenneth D. Merry * Send a Request Sense to the device. We 15233393f8daSKenneth D. Merry * assume that we are in a contingent allegiance 15243393f8daSKenneth D. Merry * condition so we do not tag this request. 15253393f8daSKenneth D. Merry */ 15263393f8daSKenneth D. Merry scsi_request_sense(&ccb->csio, /*retries*/1, 15273393f8daSKenneth D. Merry camperiphdone, 15283393f8daSKenneth D. Merry &save_ccb->csio.sense_data, 15293393f8daSKenneth D. Merry sizeof(save_ccb->csio.sense_data), 15303393f8daSKenneth D. Merry CAM_TAG_ACTION_NONE, 15313393f8daSKenneth D. Merry /*sense_len*/SSD_FULL_SIZE, 15323393f8daSKenneth D. Merry /*timeout*/5000); 15333393f8daSKenneth D. Merry break; 15343393f8daSKenneth D. Merry } 15353393f8daSKenneth D. Merry default: 1536e3c29144SWarner Losh panic("Unhandled error action %x", err_action); 15373393f8daSKenneth D. Merry } 15383393f8daSKenneth D. Merry 15393393f8daSKenneth D. Merry if ((err_action & SS_MASK) >= SS_START) { 15403393f8daSKenneth D. Merry /* 15413393f8daSKenneth D. Merry * Drop the priority to 0 so that the recovery 15423393f8daSKenneth D. Merry * CCB is the first to execute. Freeze the queue 15433393f8daSKenneth D. Merry * after this command is sent so that we can 15443393f8daSKenneth D. Merry * restore the old csio and have it queued in 15453393f8daSKenneth D. Merry * the proper order before we release normal 15463393f8daSKenneth D. Merry * transactions to the device. 15473393f8daSKenneth D. Merry */ 15483393f8daSKenneth D. Merry ccb->ccb_h.pinfo.priority = 0; 15493393f8daSKenneth D. Merry ccb->ccb_h.flags |= CAM_DEV_QFREEZE; 15503393f8daSKenneth D. Merry ccb->ccb_h.saved_ccb_ptr = save_ccb; 15513393f8daSKenneth D. Merry error = ERESTART; 15523393f8daSKenneth D. Merry } 15533393f8daSKenneth D. Merry 15543393f8daSKenneth D. Merry sense_error_done: 15553393f8daSKenneth D. Merry if ((err_action & SSQ_PRINT_SENSE) != 0 15563393f8daSKenneth D. Merry && (ccb->ccb_h.status & CAM_AUTOSNS_VALID) != 0) { 15573393f8daSKenneth D. Merry cam_error_print(print_ccb, CAM_ESF_ALL, CAM_EPF_ALL); 15583393f8daSKenneth D. Merry xpt_print_path(ccb->ccb_h.path); 155996d333b7SMatt Jacob if (bootverbose) 156096d333b7SMatt Jacob scsi_sense_print(&print_ccb->csio); 15613393f8daSKenneth D. Merry printf("%s\n", action_string); 15623393f8daSKenneth D. Merry } 15633393f8daSKenneth D. Merry } 15643393f8daSKenneth D. Merry return (error); 15653393f8daSKenneth D. Merry } 15663393f8daSKenneth D. Merry 15673393f8daSKenneth D. Merry /* 15683393f8daSKenneth D. Merry * Generic error handler. Peripheral drivers usually filter 15693393f8daSKenneth D. Merry * out the errors that they handle in a unique mannor, then 15703393f8daSKenneth D. Merry * call this function. 15713393f8daSKenneth D. Merry */ 15723393f8daSKenneth D. Merry int 15733393f8daSKenneth D. Merry cam_periph_error(union ccb *ccb, cam_flags camflags, 15743393f8daSKenneth D. Merry u_int32_t sense_flags, union ccb *save_ccb) 15753393f8daSKenneth D. Merry { 15763393f8daSKenneth D. Merry const char *action_string; 15773393f8daSKenneth D. Merry cam_status status; 15783393f8daSKenneth D. Merry int frozen; 157996d333b7SMatt Jacob int error, printed = 0; 15803393f8daSKenneth D. Merry int openings; 15813393f8daSKenneth D. Merry u_int32_t relsim_flags; 158273cf209fSMatt Jacob u_int32_t timeout = 0; 15833393f8daSKenneth D. Merry 15843393f8daSKenneth D. Merry action_string = NULL; 15853393f8daSKenneth D. Merry status = ccb->ccb_h.status; 15863393f8daSKenneth D. Merry frozen = (status & CAM_DEV_QFRZN) != 0; 15873393f8daSKenneth D. Merry status &= CAM_STATUS_MASK; 1588c7ec4390SMatt Jacob openings = relsim_flags = 0; 15893393f8daSKenneth D. Merry 15903393f8daSKenneth D. Merry switch (status) { 15913393f8daSKenneth D. Merry case CAM_REQ_CMP: 15923393f8daSKenneth D. Merry error = 0; 15933393f8daSKenneth D. Merry break; 15943393f8daSKenneth D. Merry case CAM_SCSI_STATUS_ERROR: 15953393f8daSKenneth D. Merry error = camperiphscsistatuserror(ccb, 15963393f8daSKenneth D. Merry camflags, 15973393f8daSKenneth D. Merry sense_flags, 15983393f8daSKenneth D. Merry save_ccb, 15993393f8daSKenneth D. Merry &openings, 16003393f8daSKenneth D. Merry &relsim_flags, 16013393f8daSKenneth D. Merry &timeout); 16023393f8daSKenneth D. Merry break; 16033393f8daSKenneth D. Merry case CAM_AUTOSENSE_FAIL: 1604f0d9af51SMatt Jacob xpt_print(ccb->ccb_h.path, "AutoSense Failed\n"); 1605c7ec4390SMatt Jacob error = EIO; /* we have to kill the command */ 1606c7ec4390SMatt Jacob break; 160752c9ce25SScott Long case CAM_ATA_STATUS_ERROR: 160852c9ce25SScott Long if (bootverbose && printed == 0) { 160952c9ce25SScott Long xpt_print(ccb->ccb_h.path, 161052c9ce25SScott Long "Request completed with CAM_ATA_STATUS_ERROR\n"); 161152c9ce25SScott Long printed++; 161252c9ce25SScott Long } 161352c9ce25SScott Long /* FALLTHROUGH */ 16148b8a9b1dSJustin T. Gibbs case CAM_REQ_CMP_ERR: 161596d333b7SMatt Jacob if (bootverbose && printed == 0) { 1616f0d9af51SMatt Jacob xpt_print(ccb->ccb_h.path, 1617f0d9af51SMatt Jacob "Request completed with CAM_REQ_CMP_ERR\n"); 161896d333b7SMatt Jacob printed++; 161996d333b7SMatt Jacob } 162005867503SPoul-Henning Kamp /* FALLTHROUGH */ 16218b8a9b1dSJustin T. Gibbs case CAM_CMD_TIMEOUT: 162296d333b7SMatt Jacob if (bootverbose && printed == 0) { 1623f0d9af51SMatt Jacob xpt_print(ccb->ccb_h.path, "Command timed out\n"); 162496d333b7SMatt Jacob printed++; 162596d333b7SMatt Jacob } 162605867503SPoul-Henning Kamp /* FALLTHROUGH */ 16278b8a9b1dSJustin T. Gibbs case CAM_UNEXP_BUSFREE: 162896d333b7SMatt Jacob if (bootverbose && printed == 0) { 1629f0d9af51SMatt Jacob xpt_print(ccb->ccb_h.path, "Unexpected Bus Free\n"); 163096d333b7SMatt Jacob printed++; 163196d333b7SMatt Jacob } 163205867503SPoul-Henning Kamp /* FALLTHROUGH */ 16338b8a9b1dSJustin T. Gibbs case CAM_UNCOR_PARITY: 163496d333b7SMatt Jacob if (bootverbose && printed == 0) { 1635f0d9af51SMatt Jacob xpt_print(ccb->ccb_h.path, 1636f0d9af51SMatt Jacob "Uncorrected Parity Error\n"); 163796d333b7SMatt Jacob printed++; 163896d333b7SMatt Jacob } 163905867503SPoul-Henning Kamp /* FALLTHROUGH */ 16408b8a9b1dSJustin T. Gibbs case CAM_DATA_RUN_ERR: 164196d333b7SMatt Jacob if (bootverbose && printed == 0) { 1642f0d9af51SMatt Jacob xpt_print(ccb->ccb_h.path, "Data Overrun\n"); 164396d333b7SMatt Jacob printed++; 164496d333b7SMatt Jacob } 164596d333b7SMatt Jacob error = EIO; /* we have to kill the command */ 16468b8a9b1dSJustin T. Gibbs /* decrement the number of retries */ 16473393f8daSKenneth D. Merry if (ccb->ccb_h.retry_count > 0) { 16488b8a9b1dSJustin T. Gibbs ccb->ccb_h.retry_count--; 16498b8a9b1dSJustin T. Gibbs error = ERESTART; 16508b8a9b1dSJustin T. Gibbs } else { 1651af3c383aSRafal Jaworowski action_string = "Retries Exhausted"; 16528b8a9b1dSJustin T. Gibbs error = EIO; 16538b8a9b1dSJustin T. Gibbs } 16548b8a9b1dSJustin T. Gibbs break; 16558b8a9b1dSJustin T. Gibbs case CAM_UA_ABORT: 16568b8a9b1dSJustin T. Gibbs case CAM_UA_TERMIO: 16578b8a9b1dSJustin T. Gibbs case CAM_MSG_REJECT_REC: 16588b8a9b1dSJustin T. Gibbs /* XXX Don't know that these are correct */ 16598b8a9b1dSJustin T. Gibbs error = EIO; 16608b8a9b1dSJustin T. Gibbs break; 16618b8a9b1dSJustin T. Gibbs case CAM_SEL_TIMEOUT: 16628b8a9b1dSJustin T. Gibbs { 16638b8a9b1dSJustin T. Gibbs struct cam_path *newpath; 16648b8a9b1dSJustin T. Gibbs 16653393f8daSKenneth D. Merry if ((camflags & CAM_RETRY_SELTO) != 0) { 16663393f8daSKenneth D. Merry if (ccb->ccb_h.retry_count > 0) { 16673393f8daSKenneth D. Merry 16683393f8daSKenneth D. Merry ccb->ccb_h.retry_count--; 16693393f8daSKenneth D. Merry error = ERESTART; 167096d333b7SMatt Jacob if (bootverbose && printed == 0) { 1671f0d9af51SMatt Jacob xpt_print(ccb->ccb_h.path, 1672f0d9af51SMatt Jacob "Selection Timeout\n"); 167396d333b7SMatt Jacob printed++; 167496d333b7SMatt Jacob } 16753393f8daSKenneth D. Merry 16763393f8daSKenneth D. Merry /* 167773cf209fSMatt Jacob * Wait a bit to give the device 16783393f8daSKenneth D. Merry * time to recover before we try again. 16793393f8daSKenneth D. Merry */ 16803393f8daSKenneth D. Merry relsim_flags = RELSIM_RELEASE_AFTER_TIMEOUT; 168173cf209fSMatt Jacob timeout = periph_selto_delay; 16823393f8daSKenneth D. Merry break; 16833393f8daSKenneth D. Merry } 16843393f8daSKenneth D. Merry } 16853393f8daSKenneth D. Merry error = ENXIO; 16868b8a9b1dSJustin T. Gibbs /* Should we do more if we can't create the path?? */ 16878b8a9b1dSJustin T. Gibbs if (xpt_create_path(&newpath, xpt_path_periph(ccb->ccb_h.path), 16888b8a9b1dSJustin T. Gibbs xpt_path_path_id(ccb->ccb_h.path), 16898b8a9b1dSJustin T. Gibbs xpt_path_target_id(ccb->ccb_h.path), 16908b8a9b1dSJustin T. Gibbs CAM_LUN_WILDCARD) != CAM_REQ_CMP) 16918b8a9b1dSJustin T. Gibbs break; 16923393f8daSKenneth D. Merry 16938b8a9b1dSJustin T. Gibbs /* 16948b8a9b1dSJustin T. Gibbs * Let peripheral drivers know that this device has gone 16958b8a9b1dSJustin T. Gibbs * away. 16968b8a9b1dSJustin T. Gibbs */ 16978b8a9b1dSJustin T. Gibbs xpt_async(AC_LOST_DEVICE, newpath, NULL); 16988b8a9b1dSJustin T. Gibbs xpt_free_path(newpath); 16998b8a9b1dSJustin T. Gibbs break; 17008b8a9b1dSJustin T. Gibbs } 17018b8a9b1dSJustin T. Gibbs case CAM_REQ_INVALID: 17028b8a9b1dSJustin T. Gibbs case CAM_PATH_INVALID: 17038b8a9b1dSJustin T. Gibbs case CAM_DEV_NOT_THERE: 17048b8a9b1dSJustin T. Gibbs case CAM_NO_HBA: 17058b8a9b1dSJustin T. Gibbs case CAM_PROVIDE_FAIL: 17068b8a9b1dSJustin T. Gibbs case CAM_REQ_TOO_BIG: 17079a014e6fSIan Dowse case CAM_LUN_INVALID: 17089a014e6fSIan Dowse case CAM_TID_INVALID: 17098b8a9b1dSJustin T. Gibbs error = EINVAL; 17108b8a9b1dSJustin T. Gibbs break; 17118b8a9b1dSJustin T. Gibbs case CAM_SCSI_BUS_RESET: 17128b8a9b1dSJustin T. Gibbs case CAM_BDR_SENT: 17133393f8daSKenneth D. Merry /* 17143393f8daSKenneth D. Merry * Commands that repeatedly timeout and cause these 17153393f8daSKenneth D. Merry * kinds of error recovery actions, should return 17163393f8daSKenneth D. Merry * CAM_CMD_TIMEOUT, which allows us to safely assume 17173393f8daSKenneth D. Merry * that this command was an innocent bystander to 17183393f8daSKenneth D. Merry * these events and should be unconditionally 17193393f8daSKenneth D. Merry * retried. 17203393f8daSKenneth D. Merry */ 172196d333b7SMatt Jacob if (bootverbose && printed == 0) { 172296d333b7SMatt Jacob xpt_print_path(ccb->ccb_h.path); 172396d333b7SMatt Jacob if (status == CAM_BDR_SENT) 172496d333b7SMatt Jacob printf("Bus Device Reset sent\n"); 172596d333b7SMatt Jacob else 172696d333b7SMatt Jacob printf("Bus Reset issued\n"); 172796d333b7SMatt Jacob printed++; 172896d333b7SMatt Jacob } 17293393f8daSKenneth D. Merry /* FALLTHROUGH */ 17308b8a9b1dSJustin T. Gibbs case CAM_REQUEUE_REQ: 17313393f8daSKenneth D. Merry /* Unconditional requeue */ 17328b8a9b1dSJustin T. Gibbs error = ERESTART; 173396d333b7SMatt Jacob if (bootverbose && printed == 0) { 1734f0d9af51SMatt Jacob xpt_print(ccb->ccb_h.path, "Request Requeued\n"); 173596d333b7SMatt Jacob printed++; 173696d333b7SMatt Jacob } 17378b8a9b1dSJustin T. Gibbs break; 17388b8a9b1dSJustin T. Gibbs case CAM_RESRC_UNAVAIL: 173973cf209fSMatt Jacob /* Wait a bit for the resource shortage to abate. */ 174073cf209fSMatt Jacob timeout = periph_noresrc_delay; 174173cf209fSMatt Jacob /* FALLTHROUGH */ 17428b8a9b1dSJustin T. Gibbs case CAM_BUSY: 174373cf209fSMatt Jacob if (timeout == 0) { 174473cf209fSMatt Jacob /* Wait a bit for the busy condition to abate. */ 174573cf209fSMatt Jacob timeout = periph_busy_delay; 174673cf209fSMatt Jacob } 174773cf209fSMatt Jacob relsim_flags = RELSIM_RELEASE_AFTER_TIMEOUT; 174873cf209fSMatt Jacob /* FALLTHROUGH */ 17498b8a9b1dSJustin T. Gibbs default: 17508b8a9b1dSJustin T. Gibbs /* decrement the number of retries */ 17513393f8daSKenneth D. Merry if (ccb->ccb_h.retry_count > 0) { 17528b8a9b1dSJustin T. Gibbs ccb->ccb_h.retry_count--; 17538b8a9b1dSJustin T. Gibbs error = ERESTART; 175496d333b7SMatt Jacob if (bootverbose && printed == 0) { 1755f0d9af51SMatt Jacob xpt_print(ccb->ccb_h.path, "CAM Status 0x%x\n", 1756f0d9af51SMatt Jacob status); 175796d333b7SMatt Jacob printed++; 175896d333b7SMatt Jacob } 17598b8a9b1dSJustin T. Gibbs } else { 17608b8a9b1dSJustin T. Gibbs error = EIO; 17613393f8daSKenneth D. Merry action_string = "Retries Exhausted"; 17628b8a9b1dSJustin T. Gibbs } 17638b8a9b1dSJustin T. Gibbs break; 17648b8a9b1dSJustin T. Gibbs } 17658b8a9b1dSJustin T. Gibbs 17668b8a9b1dSJustin T. Gibbs /* Attempt a retry */ 17678b8a9b1dSJustin T. Gibbs if (error == ERESTART || error == 0) { 17688b8a9b1dSJustin T. Gibbs if (frozen != 0) 17698b8a9b1dSJustin T. Gibbs ccb->ccb_h.status &= ~CAM_DEV_QFRZN; 17708b8a9b1dSJustin T. Gibbs 17713393f8daSKenneth D. Merry if (error == ERESTART) { 17723393f8daSKenneth D. Merry action_string = "Retrying Command"; 17738b8a9b1dSJustin T. Gibbs xpt_action(ccb); 17743393f8daSKenneth D. Merry } 17758b8a9b1dSJustin T. Gibbs 17763393f8daSKenneth D. Merry if (frozen != 0) 17778b8a9b1dSJustin T. Gibbs cam_release_devq(ccb->ccb_h.path, 17788b8a9b1dSJustin T. Gibbs relsim_flags, 17798b8a9b1dSJustin T. Gibbs openings, 17808b8a9b1dSJustin T. Gibbs timeout, 17818b8a9b1dSJustin T. Gibbs /*getcount_only*/0); 17828b8a9b1dSJustin T. Gibbs } 17838b8a9b1dSJustin T. Gibbs 178434707c9fSMatt Jacob /* 178534707c9fSMatt Jacob * If we have and error and are booting verbosely, whine 178634707c9fSMatt Jacob * *unless* this was a non-retryable selection timeout. 178734707c9fSMatt Jacob */ 178834707c9fSMatt Jacob if (error != 0 && bootverbose && 178934707c9fSMatt Jacob !(status == CAM_SEL_TIMEOUT && (camflags & CAM_RETRY_SELTO) == 0)) { 179034707c9fSMatt Jacob 17913393f8daSKenneth D. Merry 17923393f8daSKenneth D. Merry if (action_string == NULL) 17933393f8daSKenneth D. Merry action_string = "Unretryable Error"; 17943393f8daSKenneth D. Merry if (error != ERESTART) { 1795f0d9af51SMatt Jacob xpt_print(ccb->ccb_h.path, "error %d\n", error); 17963393f8daSKenneth D. Merry } 1797f0d9af51SMatt Jacob xpt_print(ccb->ccb_h.path, "%s\n", action_string); 17983393f8daSKenneth D. Merry } 17998b8a9b1dSJustin T. Gibbs 18008b8a9b1dSJustin T. Gibbs return (error); 18018b8a9b1dSJustin T. Gibbs } 1802