18b8a9b1dSJustin T. Gibbs /* 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 * 29c3aac50fSPeter Wemm * $FreeBSD$ 308b8a9b1dSJustin T. Gibbs */ 318b8a9b1dSJustin T. Gibbs 328b8a9b1dSJustin T. Gibbs #include <sys/param.h> 338b8a9b1dSJustin T. Gibbs #include <sys/systm.h> 348b8a9b1dSJustin T. Gibbs #include <sys/types.h> 358b8a9b1dSJustin T. Gibbs #include <sys/malloc.h> 360ec81012SJohn Polstra #include <sys/linker_set.h> 379626b608SPoul-Henning Kamp #include <sys/bio.h> 388b8a9b1dSJustin T. Gibbs #include <sys/buf.h> 398b8a9b1dSJustin T. Gibbs #include <sys/proc.h> 408b8a9b1dSJustin T. Gibbs #include <sys/devicestat.h> 4175f51904SPeter Wemm #include <sys/bus.h> 428b8a9b1dSJustin T. Gibbs #include <vm/vm.h> 438b8a9b1dSJustin T. Gibbs #include <vm/vm_extern.h> 448b8a9b1dSJustin T. Gibbs 458b8a9b1dSJustin T. Gibbs #include <cam/cam.h> 468b8a9b1dSJustin T. Gibbs #include <cam/cam_ccb.h> 478b8a9b1dSJustin T. Gibbs #include <cam/cam_xpt_periph.h> 488b8a9b1dSJustin T. Gibbs #include <cam/cam_periph.h> 498b8a9b1dSJustin T. Gibbs #include <cam/cam_debug.h> 508b8a9b1dSJustin T. Gibbs 518b8a9b1dSJustin T. Gibbs #include <cam/scsi/scsi_all.h> 528b8a9b1dSJustin T. Gibbs #include <cam/scsi/scsi_message.h> 538b8a9b1dSJustin T. Gibbs #include <cam/scsi/scsi_pass.h> 548b8a9b1dSJustin T. Gibbs 558b8a9b1dSJustin T. Gibbs static u_int camperiphnextunit(struct periph_driver *p_drv, 56501468a5SKenneth D. Merry u_int newunit, int wired, 57501468a5SKenneth D. Merry path_id_t pathid, target_id_t target, 58501468a5SKenneth D. Merry lun_id_t lun); 598b8a9b1dSJustin T. Gibbs static u_int camperiphunit(struct periph_driver *p_drv, 60501468a5SKenneth D. Merry path_id_t pathid, target_id_t target, 61501468a5SKenneth D. Merry lun_id_t lun); 628b8a9b1dSJustin T. Gibbs static void camperiphdone(struct cam_periph *periph, 638b8a9b1dSJustin T. Gibbs union ccb *done_ccb); 648b8a9b1dSJustin T. Gibbs static void camperiphfree(struct cam_periph *periph); 658b8a9b1dSJustin T. Gibbs 668b8a9b1dSJustin T. Gibbs cam_status 67ee9c90c7SKenneth D. Merry cam_periph_alloc(periph_ctor_t *periph_ctor, 68ee9c90c7SKenneth D. Merry periph_oninv_t *periph_oninvalidate, 69ee9c90c7SKenneth D. Merry periph_dtor_t *periph_dtor, periph_start_t *periph_start, 70ee9c90c7SKenneth D. Merry char *name, cam_periph_type type, struct cam_path *path, 71ee9c90c7SKenneth D. Merry ac_callback_t *ac_callback, ac_code code, void *arg) 728b8a9b1dSJustin T. Gibbs { 738b8a9b1dSJustin T. Gibbs struct periph_driver **p_drv; 748b8a9b1dSJustin T. Gibbs struct cam_periph *periph; 758b8a9b1dSJustin T. Gibbs struct cam_periph *cur_periph; 768b8a9b1dSJustin T. Gibbs path_id_t path_id; 778b8a9b1dSJustin T. Gibbs target_id_t target_id; 788b8a9b1dSJustin T. Gibbs lun_id_t lun_id; 798b8a9b1dSJustin T. Gibbs cam_status status; 808b8a9b1dSJustin T. Gibbs u_int init_level; 818b8a9b1dSJustin T. Gibbs int s; 828b8a9b1dSJustin T. Gibbs 838b8a9b1dSJustin T. Gibbs init_level = 0; 848b8a9b1dSJustin T. Gibbs /* 858b8a9b1dSJustin T. Gibbs * Handle Hot-Plug scenarios. If there is already a peripheral 868b8a9b1dSJustin T. Gibbs * of our type assigned to this path, we are likely waiting for 878b8a9b1dSJustin T. Gibbs * final close on an old, invalidated, peripheral. If this is 888b8a9b1dSJustin T. Gibbs * the case, queue up a deferred call to the peripheral's async 898b8a9b1dSJustin T. Gibbs * handler. If it looks like a mistaken re-alloation, complain. 908b8a9b1dSJustin T. Gibbs */ 918b8a9b1dSJustin T. Gibbs if ((periph = cam_periph_find(path, name)) != NULL) { 928b8a9b1dSJustin T. Gibbs 938b8a9b1dSJustin T. Gibbs if ((periph->flags & CAM_PERIPH_INVALID) != 0 948b8a9b1dSJustin T. Gibbs && (periph->flags & CAM_PERIPH_NEW_DEV_FOUND) == 0) { 958b8a9b1dSJustin T. Gibbs periph->flags |= CAM_PERIPH_NEW_DEV_FOUND; 968b8a9b1dSJustin T. Gibbs periph->deferred_callback = ac_callback; 978b8a9b1dSJustin T. Gibbs periph->deferred_ac = code; 988b8a9b1dSJustin T. Gibbs return (CAM_REQ_INPROG); 998b8a9b1dSJustin T. Gibbs } else { 1008b8a9b1dSJustin T. Gibbs printf("cam_periph_alloc: attempt to re-allocate " 1018b8a9b1dSJustin T. Gibbs "valid device %s%d rejected\n", 1028b8a9b1dSJustin T. Gibbs periph->periph_name, periph->unit_number); 1038b8a9b1dSJustin T. Gibbs } 1048b8a9b1dSJustin T. Gibbs return (CAM_REQ_INVALID); 1058b8a9b1dSJustin T. Gibbs } 1068b8a9b1dSJustin T. Gibbs 1078b8a9b1dSJustin T. Gibbs periph = (struct cam_periph *)malloc(sizeof(*periph), M_DEVBUF, 1088b8a9b1dSJustin T. Gibbs M_NOWAIT); 1098b8a9b1dSJustin T. Gibbs 1108b8a9b1dSJustin T. Gibbs if (periph == NULL) 1118b8a9b1dSJustin T. Gibbs return (CAM_RESRC_UNAVAIL); 1128b8a9b1dSJustin T. Gibbs 1138b8a9b1dSJustin T. Gibbs init_level++; 1148b8a9b1dSJustin T. Gibbs 1158b8a9b1dSJustin T. Gibbs for (p_drv = (struct periph_driver **)periphdriver_set.ls_items; 1168b8a9b1dSJustin T. Gibbs *p_drv != NULL; p_drv++) { 1178b8a9b1dSJustin T. Gibbs if (strcmp((*p_drv)->driver_name, name) == 0) 1188b8a9b1dSJustin T. Gibbs break; 1198b8a9b1dSJustin T. Gibbs } 1208b8a9b1dSJustin T. Gibbs 1218b8a9b1dSJustin T. Gibbs path_id = xpt_path_path_id(path); 1228b8a9b1dSJustin T. Gibbs target_id = xpt_path_target_id(path); 1238b8a9b1dSJustin T. Gibbs lun_id = xpt_path_lun_id(path); 1248b8a9b1dSJustin T. Gibbs bzero(periph, sizeof(*periph)); 1258b8a9b1dSJustin T. Gibbs cam_init_pinfo(&periph->pinfo); 1268b8a9b1dSJustin T. Gibbs periph->periph_start = periph_start; 1278b8a9b1dSJustin T. Gibbs periph->periph_dtor = periph_dtor; 128ee9c90c7SKenneth D. Merry periph->periph_oninval = periph_oninvalidate; 1298b8a9b1dSJustin T. Gibbs periph->type = type; 1308b8a9b1dSJustin T. Gibbs periph->periph_name = name; 1318b8a9b1dSJustin T. Gibbs periph->unit_number = camperiphunit(*p_drv, path_id, target_id, lun_id); 1328b8a9b1dSJustin T. Gibbs periph->immediate_priority = CAM_PRIORITY_NONE; 1338b8a9b1dSJustin T. Gibbs periph->refcount = 0; 1348b8a9b1dSJustin T. Gibbs SLIST_INIT(&periph->ccb_list); 1358b8a9b1dSJustin T. Gibbs status = xpt_create_path(&path, periph, path_id, target_id, lun_id); 1368b8a9b1dSJustin T. Gibbs if (status != CAM_REQ_CMP) 1378b8a9b1dSJustin T. Gibbs goto failure; 1388b8a9b1dSJustin T. Gibbs 1398b8a9b1dSJustin T. Gibbs periph->path = path; 1408b8a9b1dSJustin T. Gibbs init_level++; 1418b8a9b1dSJustin T. Gibbs 1428b8a9b1dSJustin T. Gibbs status = xpt_add_periph(periph); 1438b8a9b1dSJustin T. Gibbs 1448b8a9b1dSJustin T. Gibbs if (status != CAM_REQ_CMP) 1458b8a9b1dSJustin T. Gibbs goto failure; 1468b8a9b1dSJustin T. Gibbs 1478b8a9b1dSJustin T. Gibbs s = splsoftcam(); 1488b8a9b1dSJustin T. Gibbs cur_periph = TAILQ_FIRST(&(*p_drv)->units); 1498b8a9b1dSJustin T. Gibbs while (cur_periph != NULL 1508b8a9b1dSJustin T. Gibbs && cur_periph->unit_number < periph->unit_number) 1518b8a9b1dSJustin T. Gibbs cur_periph = TAILQ_NEXT(cur_periph, unit_links); 1528b8a9b1dSJustin T. Gibbs 1538b8a9b1dSJustin T. Gibbs if (cur_periph != NULL) 1548b8a9b1dSJustin T. Gibbs TAILQ_INSERT_BEFORE(cur_periph, periph, unit_links); 1558b8a9b1dSJustin T. Gibbs else { 1568b8a9b1dSJustin T. Gibbs TAILQ_INSERT_TAIL(&(*p_drv)->units, periph, unit_links); 1578b8a9b1dSJustin T. Gibbs (*p_drv)->generation++; 1588b8a9b1dSJustin T. Gibbs } 1598b8a9b1dSJustin T. Gibbs 1608b8a9b1dSJustin T. Gibbs splx(s); 1618b8a9b1dSJustin T. Gibbs 1628b8a9b1dSJustin T. Gibbs init_level++; 1638b8a9b1dSJustin T. Gibbs 1648b8a9b1dSJustin T. Gibbs status = periph_ctor(periph, arg); 1658b8a9b1dSJustin T. Gibbs 1668b8a9b1dSJustin T. Gibbs if (status == CAM_REQ_CMP) 1678b8a9b1dSJustin T. Gibbs init_level++; 1688b8a9b1dSJustin T. Gibbs 1698b8a9b1dSJustin T. Gibbs failure: 1708b8a9b1dSJustin T. Gibbs switch (init_level) { 1718b8a9b1dSJustin T. Gibbs case 4: 1728b8a9b1dSJustin T. Gibbs /* Initialized successfully */ 1738b8a9b1dSJustin T. Gibbs break; 1748b8a9b1dSJustin T. Gibbs case 3: 1758b8a9b1dSJustin T. Gibbs s = splsoftcam(); 1768b8a9b1dSJustin T. Gibbs TAILQ_REMOVE(&(*p_drv)->units, periph, unit_links); 1778b8a9b1dSJustin T. Gibbs splx(s); 1788b8a9b1dSJustin T. Gibbs xpt_remove_periph(periph); 1798b8a9b1dSJustin T. Gibbs case 2: 1808b8a9b1dSJustin T. Gibbs xpt_free_path(periph->path); 1818b8a9b1dSJustin T. Gibbs case 1: 1828b8a9b1dSJustin T. Gibbs free(periph, M_DEVBUF); 1838b8a9b1dSJustin T. Gibbs case 0: 1848b8a9b1dSJustin T. Gibbs /* No cleanup to perform. */ 1858b8a9b1dSJustin T. Gibbs break; 1868b8a9b1dSJustin T. Gibbs default: 1878b8a9b1dSJustin T. Gibbs panic("cam_periph_alloc: Unkown init level"); 1888b8a9b1dSJustin T. Gibbs } 1898b8a9b1dSJustin T. Gibbs return(status); 1908b8a9b1dSJustin T. Gibbs } 1918b8a9b1dSJustin T. Gibbs 1928b8a9b1dSJustin T. Gibbs /* 1938b8a9b1dSJustin T. Gibbs * Find a peripheral structure with the specified path, target, lun, 1948b8a9b1dSJustin T. Gibbs * and (optionally) type. If the name is NULL, this function will return 1958b8a9b1dSJustin T. Gibbs * the first peripheral driver that matches the specified path. 1968b8a9b1dSJustin T. Gibbs */ 1978b8a9b1dSJustin T. Gibbs struct cam_periph * 1988b8a9b1dSJustin T. Gibbs cam_periph_find(struct cam_path *path, char *name) 1998b8a9b1dSJustin T. Gibbs { 2008b8a9b1dSJustin T. Gibbs struct periph_driver **p_drv; 2018b8a9b1dSJustin T. Gibbs struct cam_periph *periph; 2028b8a9b1dSJustin T. Gibbs int s; 2038b8a9b1dSJustin T. Gibbs 2048b8a9b1dSJustin T. Gibbs for (p_drv = (struct periph_driver **)periphdriver_set.ls_items; 2058b8a9b1dSJustin T. Gibbs *p_drv != NULL; p_drv++) { 2068b8a9b1dSJustin T. Gibbs 2078b8a9b1dSJustin T. Gibbs if (name != NULL && (strcmp((*p_drv)->driver_name, name) != 0)) 2088b8a9b1dSJustin T. Gibbs continue; 2098b8a9b1dSJustin T. Gibbs 2108b8a9b1dSJustin T. Gibbs s = splsoftcam(); 21137d40066SPoul-Henning Kamp TAILQ_FOREACH(periph, &(*p_drv)->units, unit_links) { 2128b8a9b1dSJustin T. Gibbs if (xpt_path_comp(periph->path, path) == 0) { 2138b8a9b1dSJustin T. Gibbs splx(s); 2148b8a9b1dSJustin T. Gibbs return(periph); 2158b8a9b1dSJustin T. Gibbs } 2168b8a9b1dSJustin T. Gibbs } 2178b8a9b1dSJustin T. Gibbs splx(s); 2188b8a9b1dSJustin T. Gibbs if (name != NULL) 2198b8a9b1dSJustin T. Gibbs return(NULL); 2208b8a9b1dSJustin T. Gibbs } 2218b8a9b1dSJustin T. Gibbs return(NULL); 2228b8a9b1dSJustin T. Gibbs } 2238b8a9b1dSJustin T. Gibbs 2248b8a9b1dSJustin T. Gibbs cam_status 2258b8a9b1dSJustin T. Gibbs cam_periph_acquire(struct cam_periph *periph) 2268b8a9b1dSJustin T. Gibbs { 2278b8a9b1dSJustin T. Gibbs int s; 2288b8a9b1dSJustin T. Gibbs 2298b8a9b1dSJustin T. Gibbs if (periph == NULL) 2308b8a9b1dSJustin T. Gibbs return(CAM_REQ_CMP_ERR); 2318b8a9b1dSJustin T. Gibbs 2328b8a9b1dSJustin T. Gibbs s = splsoftcam(); 2338b8a9b1dSJustin T. Gibbs periph->refcount++; 2348b8a9b1dSJustin T. Gibbs splx(s); 2358b8a9b1dSJustin T. Gibbs 2368b8a9b1dSJustin T. Gibbs return(CAM_REQ_CMP); 2378b8a9b1dSJustin T. Gibbs } 2388b8a9b1dSJustin T. Gibbs 2398b8a9b1dSJustin T. Gibbs void 2408b8a9b1dSJustin T. Gibbs cam_periph_release(struct cam_periph *periph) 2418b8a9b1dSJustin T. Gibbs { 2428b8a9b1dSJustin T. Gibbs int s; 2438b8a9b1dSJustin T. Gibbs 2448b8a9b1dSJustin T. Gibbs if (periph == NULL) 2458b8a9b1dSJustin T. Gibbs return; 2468b8a9b1dSJustin T. Gibbs 2478b8a9b1dSJustin T. Gibbs s = splsoftcam(); 2488b8a9b1dSJustin T. Gibbs if ((--periph->refcount == 0) 2498b8a9b1dSJustin T. Gibbs && (periph->flags & CAM_PERIPH_INVALID)) { 2508b8a9b1dSJustin T. Gibbs camperiphfree(periph); 2518b8a9b1dSJustin T. Gibbs } 2528b8a9b1dSJustin T. Gibbs splx(s); 2538b8a9b1dSJustin T. Gibbs 2548b8a9b1dSJustin T. Gibbs } 2558b8a9b1dSJustin T. Gibbs 2568b8a9b1dSJustin T. Gibbs /* 2578b8a9b1dSJustin T. Gibbs * Look for the next unit number that is not currently in use for this 2588b8a9b1dSJustin T. Gibbs * peripheral type starting at "newunit". Also exclude unit numbers that 2598b8a9b1dSJustin T. Gibbs * are reserved by for future "hardwiring" unless we already know that this 2608b8a9b1dSJustin T. Gibbs * is a potential wired device. Only assume that the device is "wired" the 2618b8a9b1dSJustin T. Gibbs * first time through the loop since after that we'll be looking at unit 2628b8a9b1dSJustin T. Gibbs * numbers that did not match a wiring entry. 2638b8a9b1dSJustin T. Gibbs */ 2648b8a9b1dSJustin T. Gibbs static u_int 265501468a5SKenneth D. Merry camperiphnextunit(struct periph_driver *p_drv, u_int newunit, int wired, 266501468a5SKenneth D. Merry path_id_t pathid, target_id_t target, lun_id_t lun) 2678b8a9b1dSJustin T. Gibbs { 2688b8a9b1dSJustin T. Gibbs struct cam_periph *periph; 26975f51904SPeter Wemm char *periph_name, *strval; 2708b8a9b1dSJustin T. Gibbs int s; 27175f51904SPeter Wemm int i, val, dunit; 27275f51904SPeter Wemm const char *dname; 2738b8a9b1dSJustin T. Gibbs 2748b8a9b1dSJustin T. Gibbs s = splsoftcam(); 2758b8a9b1dSJustin T. Gibbs periph_name = p_drv->driver_name; 2768b8a9b1dSJustin T. Gibbs for (;;newunit++) { 2778b8a9b1dSJustin T. Gibbs 2788b8a9b1dSJustin T. Gibbs for (periph = TAILQ_FIRST(&p_drv->units); 2798b8a9b1dSJustin T. Gibbs periph != NULL && periph->unit_number != newunit; 2808b8a9b1dSJustin T. Gibbs periph = TAILQ_NEXT(periph, unit_links)) 2818b8a9b1dSJustin T. Gibbs ; 2828b8a9b1dSJustin T. Gibbs 2838b8a9b1dSJustin T. Gibbs if (periph != NULL && periph->unit_number == newunit) { 2848b8a9b1dSJustin T. Gibbs if (wired != 0) { 2858b8a9b1dSJustin T. Gibbs xpt_print_path(periph->path); 2868b8a9b1dSJustin T. Gibbs printf("Duplicate Wired Device entry!\n"); 2878b8a9b1dSJustin T. Gibbs xpt_print_path(periph->path); 288501468a5SKenneth D. Merry printf("Second device (%s device at scbus%d " 289501468a5SKenneth D. Merry "target %d lun %d) will not be wired\n", 290501468a5SKenneth D. Merry periph_name, pathid, target, lun); 2918b8a9b1dSJustin T. Gibbs wired = 0; 2928b8a9b1dSJustin T. Gibbs } 2938b8a9b1dSJustin T. Gibbs continue; 2948b8a9b1dSJustin T. Gibbs } 29575f51904SPeter Wemm if (wired) 29675f51904SPeter Wemm break; 2978b8a9b1dSJustin T. Gibbs 2988b8a9b1dSJustin T. Gibbs /* 2998b8a9b1dSJustin T. Gibbs * Don't match entries like "da 4" as a wired down 3008b8a9b1dSJustin T. Gibbs * device, but do match entries like "da 4 target 5" 3018b8a9b1dSJustin T. Gibbs * or even "da 4 scbus 1". 3028b8a9b1dSJustin T. Gibbs */ 30375f51904SPeter Wemm i = -1; 30475f51904SPeter Wemm while ((i = resource_locate(i, periph_name)) != -1) { 30575f51904SPeter Wemm dname = resource_query_name(i); 30675f51904SPeter Wemm dunit = resource_query_unit(i); 30775f51904SPeter Wemm /* if no "target" and no specific scbus, skip */ 30875f51904SPeter Wemm if (resource_int_value(dname, dunit, "target", &val) && 30975f51904SPeter Wemm (resource_string_value(dname, dunit, "at",&strval)|| 31075f51904SPeter Wemm strcmp(strval, "scbus") == 0)) 31175f51904SPeter Wemm continue; 31275f51904SPeter Wemm if (newunit == dunit) 3138b8a9b1dSJustin T. Gibbs break; 3148b8a9b1dSJustin T. Gibbs } 31575f51904SPeter Wemm if (i == -1) 3168b8a9b1dSJustin T. Gibbs break; 3178b8a9b1dSJustin T. Gibbs } 3188b8a9b1dSJustin T. Gibbs splx(s); 3198b8a9b1dSJustin T. Gibbs return (newunit); 3208b8a9b1dSJustin T. Gibbs } 3218b8a9b1dSJustin T. Gibbs 3228b8a9b1dSJustin T. Gibbs static u_int 3238b8a9b1dSJustin T. Gibbs camperiphunit(struct periph_driver *p_drv, path_id_t pathid, 3248b8a9b1dSJustin T. Gibbs target_id_t target, lun_id_t lun) 3258b8a9b1dSJustin T. Gibbs { 3268b8a9b1dSJustin T. Gibbs u_int unit; 32775f51904SPeter Wemm int hit, i, val, dunit; 32875f51904SPeter Wemm const char *dname; 32975f51904SPeter Wemm char pathbuf[32], *strval, *periph_name; 3308b8a9b1dSJustin T. Gibbs 3318b8a9b1dSJustin T. Gibbs unit = 0; 3328b8a9b1dSJustin T. Gibbs hit = 0; 3338b8a9b1dSJustin T. Gibbs 33475f51904SPeter Wemm periph_name = p_drv->driver_name; 33575f51904SPeter Wemm snprintf(pathbuf, sizeof(pathbuf), "scbus%d", pathid); 33675f51904SPeter Wemm i = -1; 33775f51904SPeter Wemm while ((i = resource_locate(i, periph_name)) != -1) { 33875f51904SPeter Wemm dname = resource_query_name(i); 33975f51904SPeter Wemm dunit = resource_query_unit(i); 34075f51904SPeter Wemm if (resource_string_value(dname, dunit, "at", &strval) == 0) { 34175f51904SPeter Wemm if (strcmp(strval, pathbuf) != 0) 3428b8a9b1dSJustin T. Gibbs continue; 3438b8a9b1dSJustin T. Gibbs hit++; 3448b8a9b1dSJustin T. Gibbs } 34575f51904SPeter Wemm if (resource_int_value(dname, dunit, "target", &val) == 0) { 34675f51904SPeter Wemm if (val != target) 3478b8a9b1dSJustin T. Gibbs continue; 3488b8a9b1dSJustin T. Gibbs hit++; 3498b8a9b1dSJustin T. Gibbs } 35075f51904SPeter Wemm if (resource_int_value(dname, dunit, "lun", &val) == 0) { 35175f51904SPeter Wemm if (val != lun) 3528b8a9b1dSJustin T. Gibbs continue; 3538b8a9b1dSJustin T. Gibbs hit++; 3548b8a9b1dSJustin T. Gibbs } 3558b8a9b1dSJustin T. Gibbs if (hit != 0) { 35675f51904SPeter Wemm unit = dunit; 3578b8a9b1dSJustin T. Gibbs break; 3588b8a9b1dSJustin T. Gibbs } 3598b8a9b1dSJustin T. Gibbs } 3608b8a9b1dSJustin T. Gibbs 3618b8a9b1dSJustin T. Gibbs /* 3628b8a9b1dSJustin T. Gibbs * Either start from 0 looking for the next unit or from 36375f51904SPeter Wemm * the unit number given in the resource config. This way, 3648b8a9b1dSJustin T. Gibbs * if we have wildcard matches, we don't return the same 3658b8a9b1dSJustin T. Gibbs * unit number twice. 3668b8a9b1dSJustin T. Gibbs */ 367501468a5SKenneth D. Merry unit = camperiphnextunit(p_drv, unit, /*wired*/hit, pathid, 368501468a5SKenneth D. Merry target, lun); 3698b8a9b1dSJustin T. Gibbs 3708b8a9b1dSJustin T. Gibbs return (unit); 3718b8a9b1dSJustin T. Gibbs } 3728b8a9b1dSJustin T. Gibbs 3738b8a9b1dSJustin T. Gibbs void 3748b8a9b1dSJustin T. Gibbs cam_periph_invalidate(struct cam_periph *periph) 3758b8a9b1dSJustin T. Gibbs { 3768b8a9b1dSJustin T. Gibbs int s; 3778b8a9b1dSJustin T. Gibbs 378ee9c90c7SKenneth D. Merry s = splsoftcam(); 379ee9c90c7SKenneth D. Merry /* 380ee9c90c7SKenneth D. Merry * We only call this routine the first time a peripheral is 381ee9c90c7SKenneth D. Merry * invalidated. The oninvalidate() routine is always called at 382ee9c90c7SKenneth D. Merry * splsoftcam(). 383ee9c90c7SKenneth D. Merry */ 384ee9c90c7SKenneth D. Merry if (((periph->flags & CAM_PERIPH_INVALID) == 0) 385ee9c90c7SKenneth D. Merry && (periph->periph_oninval != NULL)) 386ee9c90c7SKenneth D. Merry periph->periph_oninval(periph); 387ee9c90c7SKenneth D. Merry 3888b8a9b1dSJustin T. Gibbs periph->flags |= CAM_PERIPH_INVALID; 3898b8a9b1dSJustin T. Gibbs periph->flags &= ~CAM_PERIPH_NEW_DEV_FOUND; 3908b8a9b1dSJustin T. Gibbs 3918b8a9b1dSJustin T. Gibbs if (periph->refcount == 0) 3928b8a9b1dSJustin T. Gibbs camperiphfree(periph); 3938b8a9b1dSJustin T. Gibbs else if (periph->refcount < 0) 3948b8a9b1dSJustin T. Gibbs printf("cam_invalidate_periph: refcount < 0!!\n"); 3958b8a9b1dSJustin T. Gibbs splx(s); 3968b8a9b1dSJustin T. Gibbs } 3978b8a9b1dSJustin T. Gibbs 3988b8a9b1dSJustin T. Gibbs static void 3998b8a9b1dSJustin T. Gibbs camperiphfree(struct cam_periph *periph) 4008b8a9b1dSJustin T. Gibbs { 4018b8a9b1dSJustin T. Gibbs int s; 4028b8a9b1dSJustin T. Gibbs struct periph_driver **p_drv; 4038b8a9b1dSJustin T. Gibbs 4048b8a9b1dSJustin T. Gibbs for (p_drv = (struct periph_driver **)periphdriver_set.ls_items; 4058b8a9b1dSJustin T. Gibbs *p_drv != NULL; p_drv++) { 4068b8a9b1dSJustin T. Gibbs if (strcmp((*p_drv)->driver_name, periph->periph_name) == 0) 4078b8a9b1dSJustin T. Gibbs break; 4088b8a9b1dSJustin T. Gibbs } 4098b8a9b1dSJustin T. Gibbs 4108b8a9b1dSJustin T. Gibbs if (periph->periph_dtor != NULL) 4118b8a9b1dSJustin T. Gibbs periph->periph_dtor(periph); 4128b8a9b1dSJustin T. Gibbs 4138b8a9b1dSJustin T. Gibbs s = splsoftcam(); 4148b8a9b1dSJustin T. Gibbs TAILQ_REMOVE(&(*p_drv)->units, periph, unit_links); 4158b8a9b1dSJustin T. Gibbs (*p_drv)->generation++; 4168b8a9b1dSJustin T. Gibbs splx(s); 4178b8a9b1dSJustin T. Gibbs 4188b8a9b1dSJustin T. Gibbs xpt_remove_periph(periph); 4198b8a9b1dSJustin T. Gibbs 4208b8a9b1dSJustin T. Gibbs if (periph->flags & CAM_PERIPH_NEW_DEV_FOUND) { 4218b8a9b1dSJustin T. Gibbs union ccb ccb; 4228b8a9b1dSJustin T. Gibbs void *arg; 4238b8a9b1dSJustin T. Gibbs 4248b8a9b1dSJustin T. Gibbs switch (periph->deferred_ac) { 4258b8a9b1dSJustin T. Gibbs case AC_FOUND_DEVICE: 4268b8a9b1dSJustin T. Gibbs ccb.ccb_h.func_code = XPT_GDEV_TYPE; 4278b8a9b1dSJustin T. Gibbs xpt_setup_ccb(&ccb.ccb_h, periph->path, /*priority*/ 1); 4288b8a9b1dSJustin T. Gibbs xpt_action(&ccb); 4298b8a9b1dSJustin T. Gibbs arg = &ccb; 4308b8a9b1dSJustin T. Gibbs break; 4318b8a9b1dSJustin T. Gibbs case AC_PATH_REGISTERED: 4328b8a9b1dSJustin T. Gibbs ccb.ccb_h.func_code = XPT_PATH_INQ; 4338b8a9b1dSJustin T. Gibbs xpt_setup_ccb(&ccb.ccb_h, periph->path, /*priority*/ 1); 4348b8a9b1dSJustin T. Gibbs xpt_action(&ccb); 4358b8a9b1dSJustin T. Gibbs arg = &ccb; 4368b8a9b1dSJustin T. Gibbs break; 4378b8a9b1dSJustin T. Gibbs default: 4388b8a9b1dSJustin T. Gibbs arg = NULL; 4398b8a9b1dSJustin T. Gibbs break; 4408b8a9b1dSJustin T. Gibbs } 4418b8a9b1dSJustin T. Gibbs periph->deferred_callback(NULL, periph->deferred_ac, 4428b8a9b1dSJustin T. Gibbs periph->path, arg); 4438b8a9b1dSJustin T. Gibbs } 4448b8a9b1dSJustin T. Gibbs xpt_free_path(periph->path); 4458b8a9b1dSJustin T. Gibbs free(periph, M_DEVBUF); 4468b8a9b1dSJustin T. Gibbs } 4478b8a9b1dSJustin T. Gibbs 4488b8a9b1dSJustin T. Gibbs /* 4498b8a9b1dSJustin T. Gibbs * Wait interruptibly for an exclusive lock. 4508b8a9b1dSJustin T. Gibbs */ 4518b8a9b1dSJustin T. Gibbs int 4528b8a9b1dSJustin T. Gibbs cam_periph_lock(struct cam_periph *periph, int priority) 4538b8a9b1dSJustin T. Gibbs { 4548b8a9b1dSJustin T. Gibbs int error; 4558b8a9b1dSJustin T. Gibbs 4568b8a9b1dSJustin T. Gibbs while ((periph->flags & CAM_PERIPH_LOCKED) != 0) { 4578b8a9b1dSJustin T. Gibbs periph->flags |= CAM_PERIPH_LOCK_WANTED; 4588b8a9b1dSJustin T. Gibbs if ((error = tsleep(periph, priority, "caplck", 0)) != 0) 4598b8a9b1dSJustin T. Gibbs return error; 4608b8a9b1dSJustin T. Gibbs } 4618b8a9b1dSJustin T. Gibbs 4628b8a9b1dSJustin T. Gibbs if (cam_periph_acquire(periph) != CAM_REQ_CMP) 4638b8a9b1dSJustin T. Gibbs return(ENXIO); 4648b8a9b1dSJustin T. Gibbs 4658b8a9b1dSJustin T. Gibbs periph->flags |= CAM_PERIPH_LOCKED; 4668b8a9b1dSJustin T. Gibbs return 0; 4678b8a9b1dSJustin T. Gibbs } 4688b8a9b1dSJustin T. Gibbs 4698b8a9b1dSJustin T. Gibbs /* 4708b8a9b1dSJustin T. Gibbs * Unlock and wake up any waiters. 4718b8a9b1dSJustin T. Gibbs */ 4728b8a9b1dSJustin T. Gibbs void 4738b8a9b1dSJustin T. Gibbs cam_periph_unlock(struct cam_periph *periph) 4748b8a9b1dSJustin T. Gibbs { 4758b8a9b1dSJustin T. Gibbs periph->flags &= ~CAM_PERIPH_LOCKED; 4768b8a9b1dSJustin T. Gibbs if ((periph->flags & CAM_PERIPH_LOCK_WANTED) != 0) { 4778b8a9b1dSJustin T. Gibbs periph->flags &= ~CAM_PERIPH_LOCK_WANTED; 4788b8a9b1dSJustin T. Gibbs wakeup(periph); 4798b8a9b1dSJustin T. Gibbs } 4808b8a9b1dSJustin T. Gibbs 4818b8a9b1dSJustin T. Gibbs cam_periph_release(periph); 4828b8a9b1dSJustin T. Gibbs } 4838b8a9b1dSJustin T. Gibbs 4848b8a9b1dSJustin T. Gibbs /* 4858b8a9b1dSJustin T. Gibbs * Map user virtual pointers into kernel virtual address space, so we can 4868b8a9b1dSJustin T. Gibbs * access the memory. This won't work on physical pointers, for now it's 4878b8a9b1dSJustin T. Gibbs * up to the caller to check for that. (XXX KDM -- should we do that here 4888b8a9b1dSJustin T. Gibbs * instead?) This also only works for up to MAXPHYS memory. Since we use 4898b8a9b1dSJustin T. Gibbs * buffers to map stuff in and out, we're limited to the buffer size. 4908b8a9b1dSJustin T. Gibbs */ 4918b8a9b1dSJustin T. Gibbs int 4928b8a9b1dSJustin T. Gibbs cam_periph_mapmem(union ccb *ccb, struct cam_periph_map_info *mapinfo) 4938b8a9b1dSJustin T. Gibbs { 49479d49a06SKenneth D. Merry int numbufs, i; 49579d49a06SKenneth D. Merry int flags[CAM_PERIPH_MAXMAPS]; 4968b8a9b1dSJustin T. Gibbs u_int8_t **data_ptrs[CAM_PERIPH_MAXMAPS]; 4978b8a9b1dSJustin T. Gibbs u_int32_t lengths[CAM_PERIPH_MAXMAPS]; 4988b8a9b1dSJustin T. Gibbs u_int32_t dirs[CAM_PERIPH_MAXMAPS]; 4998b8a9b1dSJustin T. Gibbs 5008b8a9b1dSJustin T. Gibbs switch(ccb->ccb_h.func_code) { 5018b8a9b1dSJustin T. Gibbs case XPT_DEV_MATCH: 5028b8a9b1dSJustin T. Gibbs if (ccb->cdm.match_buf_len == 0) { 5038b8a9b1dSJustin T. Gibbs printf("cam_periph_mapmem: invalid match buffer " 5048b8a9b1dSJustin T. Gibbs "length 0\n"); 5058b8a9b1dSJustin T. Gibbs return(EINVAL); 5068b8a9b1dSJustin T. Gibbs } 5078b8a9b1dSJustin T. Gibbs if (ccb->cdm.pattern_buf_len > 0) { 5088b8a9b1dSJustin T. Gibbs data_ptrs[0] = (u_int8_t **)&ccb->cdm.patterns; 5098b8a9b1dSJustin T. Gibbs lengths[0] = ccb->cdm.pattern_buf_len; 5108b8a9b1dSJustin T. Gibbs dirs[0] = CAM_DIR_OUT; 5118b8a9b1dSJustin T. Gibbs data_ptrs[1] = (u_int8_t **)&ccb->cdm.matches; 5128b8a9b1dSJustin T. Gibbs lengths[1] = ccb->cdm.match_buf_len; 5138b8a9b1dSJustin T. Gibbs dirs[1] = CAM_DIR_IN; 5148b8a9b1dSJustin T. Gibbs numbufs = 2; 5158b8a9b1dSJustin T. Gibbs } else { 5168b8a9b1dSJustin T. Gibbs data_ptrs[0] = (u_int8_t **)&ccb->cdm.matches; 5178b8a9b1dSJustin T. Gibbs lengths[0] = ccb->cdm.match_buf_len; 5188b8a9b1dSJustin T. Gibbs dirs[0] = CAM_DIR_IN; 5198b8a9b1dSJustin T. Gibbs numbufs = 1; 5208b8a9b1dSJustin T. Gibbs } 5218b8a9b1dSJustin T. Gibbs break; 5228b8a9b1dSJustin T. Gibbs case XPT_SCSI_IO: 52387cfaf0eSJustin T. Gibbs case XPT_CONT_TARGET_IO: 5248b8a9b1dSJustin T. Gibbs if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_NONE) 5258b8a9b1dSJustin T. Gibbs return(0); 5268b8a9b1dSJustin T. Gibbs 5278b8a9b1dSJustin T. Gibbs data_ptrs[0] = &ccb->csio.data_ptr; 52879d49a06SKenneth D. Merry lengths[0] = ccb->csio.dxfer_len; 5298b8a9b1dSJustin T. Gibbs dirs[0] = ccb->ccb_h.flags & CAM_DIR_MASK; 5308b8a9b1dSJustin T. Gibbs numbufs = 1; 5318b8a9b1dSJustin T. Gibbs break; 5328b8a9b1dSJustin T. Gibbs default: 5338b8a9b1dSJustin T. Gibbs return(EINVAL); 5348b8a9b1dSJustin T. Gibbs break; /* NOTREACHED */ 5358b8a9b1dSJustin T. Gibbs } 5368b8a9b1dSJustin T. Gibbs 5378b8a9b1dSJustin T. Gibbs /* 53879d49a06SKenneth D. Merry * Check the transfer length and permissions first, so we don't 53979d49a06SKenneth D. Merry * have to unmap any previously mapped buffers. 5408b8a9b1dSJustin T. Gibbs */ 5418b8a9b1dSJustin T. Gibbs for (i = 0; i < numbufs; i++) { 54279d49a06SKenneth D. Merry 54379d49a06SKenneth D. Merry flags[i] = 0; 54479d49a06SKenneth D. Merry 54579d49a06SKenneth D. Merry /* 54679d49a06SKenneth D. Merry * The userland data pointer passed in may not be page 54779d49a06SKenneth D. Merry * aligned. vmapbuf() truncates the address to a page 54879d49a06SKenneth D. Merry * boundary, so if the address isn't page aligned, we'll 54979d49a06SKenneth D. Merry * need enough space for the given transfer length, plus 55079d49a06SKenneth D. Merry * whatever extra space is necessary to make it to the page 55179d49a06SKenneth D. Merry * boundary. 55279d49a06SKenneth D. Merry */ 55379d49a06SKenneth D. Merry if ((lengths[i] + 554ff1fe75fSKenneth D. Merry (((vm_offset_t)(*data_ptrs[i])) & PAGE_MASK)) > DFLTPHYS){ 5556d7b539aSPeter Wemm printf("cam_periph_mapmem: attempt to map %lu bytes, " 556ff1fe75fSKenneth D. Merry "which is greater than DFLTPHYS(%d)\n", 5576d7b539aSPeter Wemm (long)(lengths[i] + 5586d7b539aSPeter Wemm (((vm_offset_t)(*data_ptrs[i])) & PAGE_MASK)), 559ff1fe75fSKenneth D. Merry DFLTPHYS); 56079d49a06SKenneth D. Merry return(E2BIG); 56179d49a06SKenneth D. Merry } 5628b8a9b1dSJustin T. Gibbs 563edd24ab7SKenneth D. Merry if (dirs[i] & CAM_DIR_OUT) { 56421144e3bSPoul-Henning Kamp flags[i] = BIO_WRITE; 56502c58685SPoul-Henning Kamp if (!useracc(*data_ptrs[i], lengths[i], 56602c58685SPoul-Henning Kamp VM_PROT_READ)) { 5678b8a9b1dSJustin T. Gibbs printf("cam_periph_mapmem: error, " 5682e8bf209SBruce Evans "address %p, length %lu isn't " 5698b8a9b1dSJustin T. Gibbs "user accessible for READ\n", 5702e8bf209SBruce Evans (void *)*data_ptrs[i], 5712e8bf209SBruce Evans (u_long)lengths[i]); 5728b8a9b1dSJustin T. Gibbs return(EACCES); 5738b8a9b1dSJustin T. Gibbs } 5748b8a9b1dSJustin T. Gibbs } 5758b8a9b1dSJustin T. Gibbs 576edd24ab7SKenneth D. Merry if (dirs[i] & CAM_DIR_IN) { 57721144e3bSPoul-Henning Kamp flags[i] = BIO_READ; 57802c58685SPoul-Henning Kamp if (!useracc(*data_ptrs[i], lengths[i], 57902c58685SPoul-Henning Kamp VM_PROT_WRITE)) { 5808b8a9b1dSJustin T. Gibbs printf("cam_periph_mapmem: error, " 5812e8bf209SBruce Evans "address %p, length %lu isn't " 5828b8a9b1dSJustin T. Gibbs "user accessible for WRITE\n", 5832e8bf209SBruce Evans (void *)*data_ptrs[i], 5842e8bf209SBruce Evans (u_long)lengths[i]); 5858b8a9b1dSJustin T. Gibbs 5868b8a9b1dSJustin T. Gibbs return(EACCES); 5878b8a9b1dSJustin T. Gibbs } 5888b8a9b1dSJustin T. Gibbs } 5898b8a9b1dSJustin T. Gibbs 59079d49a06SKenneth D. Merry } 59179d49a06SKenneth D. Merry 59279d49a06SKenneth D. Merry /* this keeps the current process from getting swapped */ 59379d49a06SKenneth D. Merry /* 59479d49a06SKenneth D. Merry * XXX KDM should I use P_NOSWAP instead? 59579d49a06SKenneth D. Merry */ 5960cbbb7bfSPeter Wemm PHOLD(curproc); 59779d49a06SKenneth D. Merry 59879d49a06SKenneth D. Merry for (i = 0; i < numbufs; i++) { 5998b8a9b1dSJustin T. Gibbs /* 6008b8a9b1dSJustin T. Gibbs * Get the buffer. 6018b8a9b1dSJustin T. Gibbs */ 6021c7c3c6aSMatthew Dillon mapinfo->bp[i] = getpbuf(NULL); 6038b8a9b1dSJustin T. Gibbs 6048b8a9b1dSJustin T. Gibbs /* save the buffer's data address */ 6058b8a9b1dSJustin T. Gibbs mapinfo->bp[i]->b_saveaddr = mapinfo->bp[i]->b_data; 6068b8a9b1dSJustin T. Gibbs 6078b8a9b1dSJustin T. Gibbs /* put our pointer in the data slot */ 6088b8a9b1dSJustin T. Gibbs mapinfo->bp[i]->b_data = *data_ptrs[i]; 6098b8a9b1dSJustin T. Gibbs 610ff1fe75fSKenneth D. Merry /* set the transfer length, we know it's < DFLTPHYS */ 6118b8a9b1dSJustin T. Gibbs mapinfo->bp[i]->b_bufsize = lengths[i]; 6128b8a9b1dSJustin T. Gibbs 6138b8a9b1dSJustin T. Gibbs /* set the flags */ 61421144e3bSPoul-Henning Kamp mapinfo->bp[i]->b_flags = B_PHYS; 61521144e3bSPoul-Henning Kamp 61621144e3bSPoul-Henning Kamp /* set the direction */ 61721144e3bSPoul-Henning Kamp mapinfo->bp[i]->b_iocmd = flags[i]; 6188b8a9b1dSJustin T. Gibbs 6198b8a9b1dSJustin T. Gibbs /* map the buffer into kernel memory */ 6208b8a9b1dSJustin T. Gibbs vmapbuf(mapinfo->bp[i]); 6218b8a9b1dSJustin T. Gibbs 6228b8a9b1dSJustin T. Gibbs /* set our pointer to the new mapped area */ 6238b8a9b1dSJustin T. Gibbs *data_ptrs[i] = mapinfo->bp[i]->b_data; 6248b8a9b1dSJustin T. Gibbs 6258b8a9b1dSJustin T. Gibbs mapinfo->num_bufs_used++; 6268b8a9b1dSJustin T. Gibbs } 6278b8a9b1dSJustin T. Gibbs 6288b8a9b1dSJustin T. Gibbs return(0); 6298b8a9b1dSJustin T. Gibbs } 6308b8a9b1dSJustin T. Gibbs 6318b8a9b1dSJustin T. Gibbs /* 6328b8a9b1dSJustin T. Gibbs * Unmap memory segments mapped into kernel virtual address space by 6338b8a9b1dSJustin T. Gibbs * cam_periph_mapmem(). 6348b8a9b1dSJustin T. Gibbs */ 6358b8a9b1dSJustin T. Gibbs void 6368b8a9b1dSJustin T. Gibbs cam_periph_unmapmem(union ccb *ccb, struct cam_periph_map_info *mapinfo) 6378b8a9b1dSJustin T. Gibbs { 6388b8a9b1dSJustin T. Gibbs int numbufs, i; 6398b8a9b1dSJustin T. Gibbs u_int8_t **data_ptrs[CAM_PERIPH_MAXMAPS]; 6408b8a9b1dSJustin T. Gibbs 6418b8a9b1dSJustin T. Gibbs if (mapinfo->num_bufs_used <= 0) { 6428b8a9b1dSJustin T. Gibbs /* allow ourselves to be swapped once again */ 6430cbbb7bfSPeter Wemm PRELE(curproc); 6448b8a9b1dSJustin T. Gibbs return; 6458b8a9b1dSJustin T. Gibbs } 6468b8a9b1dSJustin T. Gibbs 6478b8a9b1dSJustin T. Gibbs switch (ccb->ccb_h.func_code) { 6488b8a9b1dSJustin T. Gibbs case XPT_DEV_MATCH: 6498b8a9b1dSJustin T. Gibbs numbufs = min(mapinfo->num_bufs_used, 2); 6508b8a9b1dSJustin T. Gibbs 6518b8a9b1dSJustin T. Gibbs if (numbufs == 1) { 6528b8a9b1dSJustin T. Gibbs data_ptrs[0] = (u_int8_t **)&ccb->cdm.matches; 6538b8a9b1dSJustin T. Gibbs } else { 6548b8a9b1dSJustin T. Gibbs data_ptrs[0] = (u_int8_t **)&ccb->cdm.patterns; 6558b8a9b1dSJustin T. Gibbs data_ptrs[1] = (u_int8_t **)&ccb->cdm.matches; 6568b8a9b1dSJustin T. Gibbs } 6578b8a9b1dSJustin T. Gibbs break; 6588b8a9b1dSJustin T. Gibbs case XPT_SCSI_IO: 6599911ecf9SJustin T. Gibbs case XPT_CONT_TARGET_IO: 6608b8a9b1dSJustin T. Gibbs data_ptrs[0] = &ccb->csio.data_ptr; 6618b8a9b1dSJustin T. Gibbs numbufs = min(mapinfo->num_bufs_used, 1); 6628b8a9b1dSJustin T. Gibbs break; 6638b8a9b1dSJustin T. Gibbs default: 6648b8a9b1dSJustin T. Gibbs /* allow ourselves to be swapped once again */ 6650cbbb7bfSPeter Wemm PRELE(curproc); 6668b8a9b1dSJustin T. Gibbs return; 6678b8a9b1dSJustin T. Gibbs break; /* NOTREACHED */ 6688b8a9b1dSJustin T. Gibbs } 6698b8a9b1dSJustin T. Gibbs 6708b8a9b1dSJustin T. Gibbs for (i = 0; i < numbufs; i++) { 6718b8a9b1dSJustin T. Gibbs /* Set the user's pointer back to the original value */ 6728b8a9b1dSJustin T. Gibbs *data_ptrs[i] = mapinfo->bp[i]->b_saveaddr; 6738b8a9b1dSJustin T. Gibbs 6748b8a9b1dSJustin T. Gibbs /* unmap the buffer */ 6758b8a9b1dSJustin T. Gibbs vunmapbuf(mapinfo->bp[i]); 6768b8a9b1dSJustin T. Gibbs 6778b8a9b1dSJustin T. Gibbs /* clear the flags we set above */ 67867812eacSKirk McKusick mapinfo->bp[i]->b_flags &= ~B_PHYS; 6798b8a9b1dSJustin T. Gibbs 6808b8a9b1dSJustin T. Gibbs /* release the buffer */ 6811c7c3c6aSMatthew Dillon relpbuf(mapinfo->bp[i], NULL); 6828b8a9b1dSJustin T. Gibbs } 6838b8a9b1dSJustin T. Gibbs 6848b8a9b1dSJustin T. Gibbs /* allow ourselves to be swapped once again */ 6850cbbb7bfSPeter Wemm PRELE(curproc); 6868b8a9b1dSJustin T. Gibbs } 6878b8a9b1dSJustin T. Gibbs 6888b8a9b1dSJustin T. Gibbs union ccb * 6898b8a9b1dSJustin T. Gibbs cam_periph_getccb(struct cam_periph *periph, u_int32_t priority) 6908b8a9b1dSJustin T. Gibbs { 6918b8a9b1dSJustin T. Gibbs struct ccb_hdr *ccb_h; 6928b8a9b1dSJustin T. Gibbs int s; 6938b8a9b1dSJustin T. Gibbs 6948b8a9b1dSJustin T. Gibbs CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering cdgetccb\n")); 6958b8a9b1dSJustin T. Gibbs 6968b8a9b1dSJustin T. Gibbs s = splsoftcam(); 6978b8a9b1dSJustin T. Gibbs 698fc2ffbe6SPoul-Henning Kamp while (SLIST_FIRST(&periph->ccb_list) == NULL) { 6998b8a9b1dSJustin T. Gibbs if (periph->immediate_priority > priority) 7008b8a9b1dSJustin T. Gibbs periph->immediate_priority = priority; 7018b8a9b1dSJustin T. Gibbs xpt_schedule(periph, priority); 702fc2ffbe6SPoul-Henning Kamp if ((SLIST_FIRST(&periph->ccb_list) != NULL) 703fc2ffbe6SPoul-Henning Kamp && (SLIST_FIRST(&periph->ccb_list)->pinfo.priority == priority)) 7048b8a9b1dSJustin T. Gibbs break; 7058b8a9b1dSJustin T. Gibbs tsleep(&periph->ccb_list, PRIBIO, "cgticb", 0); 7068b8a9b1dSJustin T. Gibbs } 7078b8a9b1dSJustin T. Gibbs 708fc2ffbe6SPoul-Henning Kamp ccb_h = SLIST_FIRST(&periph->ccb_list); 7098b8a9b1dSJustin T. Gibbs SLIST_REMOVE_HEAD(&periph->ccb_list, periph_links.sle); 7108b8a9b1dSJustin T. Gibbs splx(s); 7118b8a9b1dSJustin T. Gibbs return ((union ccb *)ccb_h); 7128b8a9b1dSJustin T. Gibbs } 7138b8a9b1dSJustin T. Gibbs 7148b8a9b1dSJustin T. Gibbs void 7158b8a9b1dSJustin T. Gibbs cam_periph_ccbwait(union ccb *ccb) 7168b8a9b1dSJustin T. Gibbs { 7178b8a9b1dSJustin T. Gibbs int s; 7188b8a9b1dSJustin T. Gibbs 7198b8a9b1dSJustin T. Gibbs s = splsoftcam(); 7208b8a9b1dSJustin T. Gibbs if ((ccb->ccb_h.pinfo.index != CAM_UNQUEUED_INDEX) 7218b8a9b1dSJustin T. Gibbs || ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_INPROG)) 7228b8a9b1dSJustin T. Gibbs tsleep(&ccb->ccb_h.cbfcnp, PRIBIO, "cbwait", 0); 7238b8a9b1dSJustin T. Gibbs 7248b8a9b1dSJustin T. Gibbs splx(s); 7258b8a9b1dSJustin T. Gibbs } 7268b8a9b1dSJustin T. Gibbs 7278b8a9b1dSJustin T. Gibbs int 7288b8a9b1dSJustin T. Gibbs cam_periph_ioctl(struct cam_periph *periph, int cmd, caddr_t addr, 7298b8a9b1dSJustin T. Gibbs int (*error_routine)(union ccb *ccb, 7308b8a9b1dSJustin T. Gibbs cam_flags camflags, 7318b8a9b1dSJustin T. Gibbs u_int32_t sense_flags)) 7328b8a9b1dSJustin T. Gibbs { 7338b8a9b1dSJustin T. Gibbs union ccb *ccb; 7348b8a9b1dSJustin T. Gibbs int error; 7358b8a9b1dSJustin T. Gibbs int found; 7368b8a9b1dSJustin T. Gibbs 7378b8a9b1dSJustin T. Gibbs error = found = 0; 7388b8a9b1dSJustin T. Gibbs 7398b8a9b1dSJustin T. Gibbs switch(cmd){ 7408b8a9b1dSJustin T. Gibbs case CAMGETPASSTHRU: 7418b8a9b1dSJustin T. Gibbs ccb = cam_periph_getccb(periph, /* priority */ 1); 7428b8a9b1dSJustin T. Gibbs xpt_setup_ccb(&ccb->ccb_h, 7438b8a9b1dSJustin T. Gibbs ccb->ccb_h.path, 7448b8a9b1dSJustin T. Gibbs /*priority*/1); 7458b8a9b1dSJustin T. Gibbs ccb->ccb_h.func_code = XPT_GDEVLIST; 7468b8a9b1dSJustin T. Gibbs 7478b8a9b1dSJustin T. Gibbs /* 7488b8a9b1dSJustin T. Gibbs * Basically, the point of this is that we go through 7498b8a9b1dSJustin T. Gibbs * getting the list of devices, until we find a passthrough 7508b8a9b1dSJustin T. Gibbs * device. In the current version of the CAM code, the 7518b8a9b1dSJustin T. Gibbs * only way to determine what type of device we're dealing 7528b8a9b1dSJustin T. Gibbs * with is by its name. 7538b8a9b1dSJustin T. Gibbs */ 7548b8a9b1dSJustin T. Gibbs while (found == 0) { 7558b8a9b1dSJustin T. Gibbs ccb->cgdl.index = 0; 7568b8a9b1dSJustin T. Gibbs ccb->cgdl.status = CAM_GDEVLIST_MORE_DEVS; 7578b8a9b1dSJustin T. Gibbs while (ccb->cgdl.status == CAM_GDEVLIST_MORE_DEVS) { 7588b8a9b1dSJustin T. Gibbs 7598b8a9b1dSJustin T. Gibbs /* we want the next device in the list */ 7608b8a9b1dSJustin T. Gibbs xpt_action(ccb); 7618b8a9b1dSJustin T. Gibbs if (strncmp(ccb->cgdl.periph_name, 7628b8a9b1dSJustin T. Gibbs "pass", 4) == 0){ 7638b8a9b1dSJustin T. Gibbs found = 1; 7648b8a9b1dSJustin T. Gibbs break; 7658b8a9b1dSJustin T. Gibbs } 7668b8a9b1dSJustin T. Gibbs } 7678b8a9b1dSJustin T. Gibbs if ((ccb->cgdl.status == CAM_GDEVLIST_LAST_DEVICE) && 7688b8a9b1dSJustin T. Gibbs (found == 0)) { 7698b8a9b1dSJustin T. Gibbs ccb->cgdl.periph_name[0] = '\0'; 7708b8a9b1dSJustin T. Gibbs ccb->cgdl.unit_number = 0; 7718b8a9b1dSJustin T. Gibbs break; 7728b8a9b1dSJustin T. Gibbs } 7738b8a9b1dSJustin T. Gibbs } 7748b8a9b1dSJustin T. Gibbs 7758b8a9b1dSJustin T. Gibbs /* copy the result back out */ 7768b8a9b1dSJustin T. Gibbs bcopy(ccb, addr, sizeof(union ccb)); 7778b8a9b1dSJustin T. Gibbs 7788b8a9b1dSJustin T. Gibbs /* and release the ccb */ 7798b8a9b1dSJustin T. Gibbs xpt_release_ccb(ccb); 7808b8a9b1dSJustin T. Gibbs 7818b8a9b1dSJustin T. Gibbs break; 7828b8a9b1dSJustin T. Gibbs default: 7838b8a9b1dSJustin T. Gibbs error = ENOTTY; 7848b8a9b1dSJustin T. Gibbs break; 7858b8a9b1dSJustin T. Gibbs } 7868b8a9b1dSJustin T. Gibbs return(error); 7878b8a9b1dSJustin T. Gibbs } 7888b8a9b1dSJustin T. Gibbs 7898b8a9b1dSJustin T. Gibbs int 7908b8a9b1dSJustin T. Gibbs cam_periph_runccb(union ccb *ccb, 7918b8a9b1dSJustin T. Gibbs int (*error_routine)(union ccb *ccb, 7928b8a9b1dSJustin T. Gibbs cam_flags camflags, 7938b8a9b1dSJustin T. Gibbs u_int32_t sense_flags), 7948b8a9b1dSJustin T. Gibbs cam_flags camflags, u_int32_t sense_flags, 7958b8a9b1dSJustin T. Gibbs struct devstat *ds) 7968b8a9b1dSJustin T. Gibbs { 7978b8a9b1dSJustin T. Gibbs int error; 7988b8a9b1dSJustin T. Gibbs 7998b8a9b1dSJustin T. Gibbs error = 0; 8008b8a9b1dSJustin T. Gibbs 8018b8a9b1dSJustin T. Gibbs /* 8028b8a9b1dSJustin T. Gibbs * If the user has supplied a stats structure, and if we understand 8038b8a9b1dSJustin T. Gibbs * this particular type of ccb, record the transaction start. 8048b8a9b1dSJustin T. Gibbs */ 8058b8a9b1dSJustin T. Gibbs if ((ds != NULL) && (ccb->ccb_h.func_code == XPT_SCSI_IO)) 8068b8a9b1dSJustin T. Gibbs devstat_start_transaction(ds); 8078b8a9b1dSJustin T. Gibbs 8088b8a9b1dSJustin T. Gibbs xpt_action(ccb); 8098b8a9b1dSJustin T. Gibbs 8108b8a9b1dSJustin T. Gibbs do { 8118b8a9b1dSJustin T. Gibbs cam_periph_ccbwait(ccb); 8128b8a9b1dSJustin T. Gibbs if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) 8138b8a9b1dSJustin T. Gibbs error = 0; 8148b8a9b1dSJustin T. Gibbs else if (error_routine != NULL) 8158b8a9b1dSJustin T. Gibbs error = (*error_routine)(ccb, camflags, sense_flags); 8168b8a9b1dSJustin T. Gibbs else 8178b8a9b1dSJustin T. Gibbs error = 0; 8188b8a9b1dSJustin T. Gibbs 8198b8a9b1dSJustin T. Gibbs } while (error == ERESTART); 8208b8a9b1dSJustin T. Gibbs 8218b8a9b1dSJustin T. Gibbs if ((ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) 8228b8a9b1dSJustin T. Gibbs cam_release_devq(ccb->ccb_h.path, 8238b8a9b1dSJustin T. Gibbs /* relsim_flags */0, 8248b8a9b1dSJustin T. Gibbs /* openings */0, 8258b8a9b1dSJustin T. Gibbs /* timeout */0, 8268b8a9b1dSJustin T. Gibbs /* getcount_only */ FALSE); 8278b8a9b1dSJustin T. Gibbs 8288b8a9b1dSJustin T. Gibbs if ((ds != NULL) && (ccb->ccb_h.func_code == XPT_SCSI_IO)) 8298b8a9b1dSJustin T. Gibbs devstat_end_transaction(ds, 8308b8a9b1dSJustin T. Gibbs ccb->csio.dxfer_len, 8318b8a9b1dSJustin T. Gibbs ccb->csio.tag_action & 0xf, 8328b8a9b1dSJustin T. Gibbs ((ccb->ccb_h.flags & CAM_DIR_MASK) == 8338b8a9b1dSJustin T. Gibbs CAM_DIR_NONE) ? DEVSTAT_NO_DATA : 8348b8a9b1dSJustin T. Gibbs (ccb->ccb_h.flags & CAM_DIR_OUT) ? 8358b8a9b1dSJustin T. Gibbs DEVSTAT_WRITE : 8368b8a9b1dSJustin T. Gibbs DEVSTAT_READ); 8378b8a9b1dSJustin T. Gibbs 8388b8a9b1dSJustin T. Gibbs return(error); 8398b8a9b1dSJustin T. Gibbs } 8408b8a9b1dSJustin T. Gibbs 84187cfaf0eSJustin T. Gibbs void 84287cfaf0eSJustin T. Gibbs cam_freeze_devq(struct cam_path *path) 84387cfaf0eSJustin T. Gibbs { 84487cfaf0eSJustin T. Gibbs struct ccb_hdr ccb_h; 84587cfaf0eSJustin T. Gibbs 84687cfaf0eSJustin T. Gibbs xpt_setup_ccb(&ccb_h, path, /*priority*/1); 84787cfaf0eSJustin T. Gibbs ccb_h.func_code = XPT_NOOP; 84887cfaf0eSJustin T. Gibbs ccb_h.flags = CAM_DEV_QFREEZE; 84987cfaf0eSJustin T. Gibbs xpt_action((union ccb *)&ccb_h); 85087cfaf0eSJustin T. Gibbs } 85187cfaf0eSJustin T. Gibbs 8528b8a9b1dSJustin T. Gibbs u_int32_t 8538b8a9b1dSJustin T. Gibbs cam_release_devq(struct cam_path *path, u_int32_t relsim_flags, 8548b8a9b1dSJustin T. Gibbs u_int32_t openings, u_int32_t timeout, 8558b8a9b1dSJustin T. Gibbs int getcount_only) 8568b8a9b1dSJustin T. Gibbs { 8578b8a9b1dSJustin T. Gibbs struct ccb_relsim crs; 8588b8a9b1dSJustin T. Gibbs 8598b8a9b1dSJustin T. Gibbs xpt_setup_ccb(&crs.ccb_h, path, 8608b8a9b1dSJustin T. Gibbs /*priority*/1); 8618b8a9b1dSJustin T. Gibbs crs.ccb_h.func_code = XPT_REL_SIMQ; 8628b8a9b1dSJustin T. Gibbs crs.ccb_h.flags = getcount_only ? CAM_DEV_QFREEZE : 0; 8638b8a9b1dSJustin T. Gibbs crs.release_flags = relsim_flags; 8648b8a9b1dSJustin T. Gibbs crs.openings = openings; 8658b8a9b1dSJustin T. Gibbs crs.release_timeout = timeout; 8668b8a9b1dSJustin T. Gibbs xpt_action((union ccb *)&crs); 8678b8a9b1dSJustin T. Gibbs return (crs.qfrozen_cnt); 8688b8a9b1dSJustin T. Gibbs } 8698b8a9b1dSJustin T. Gibbs 8708b8a9b1dSJustin T. Gibbs #define saved_ccb_ptr ppriv_ptr0 8718b8a9b1dSJustin T. Gibbs static void 8728b8a9b1dSJustin T. Gibbs camperiphdone(struct cam_periph *periph, union ccb *done_ccb) 8738b8a9b1dSJustin T. Gibbs { 8748b8a9b1dSJustin T. Gibbs cam_status status; 8758b8a9b1dSJustin T. Gibbs int frozen; 8768b8a9b1dSJustin T. Gibbs int sense; 8778b8a9b1dSJustin T. Gibbs struct scsi_start_stop_unit *scsi_cmd; 8788b8a9b1dSJustin T. Gibbs u_int32_t relsim_flags, timeout; 8798b8a9b1dSJustin T. Gibbs u_int32_t qfrozen_cnt; 8808b8a9b1dSJustin T. Gibbs 8818b8a9b1dSJustin T. Gibbs status = done_ccb->ccb_h.status; 8828b8a9b1dSJustin T. Gibbs frozen = (status & CAM_DEV_QFRZN) != 0; 8838b8a9b1dSJustin T. Gibbs sense = (status & CAM_AUTOSNS_VALID) != 0; 8848b8a9b1dSJustin T. Gibbs status &= CAM_STATUS_MASK; 8858b8a9b1dSJustin T. Gibbs 8868b8a9b1dSJustin T. Gibbs timeout = 0; 8878b8a9b1dSJustin T. Gibbs relsim_flags = 0; 8888b8a9b1dSJustin T. Gibbs 8898b8a9b1dSJustin T. Gibbs /* 8908b8a9b1dSJustin T. Gibbs * Unfreeze the queue once if it is already frozen.. 8918b8a9b1dSJustin T. Gibbs */ 8928b8a9b1dSJustin T. Gibbs if (frozen != 0) { 8938b8a9b1dSJustin T. Gibbs qfrozen_cnt = cam_release_devq(done_ccb->ccb_h.path, 8948b8a9b1dSJustin T. Gibbs /*relsim_flags*/0, 8958b8a9b1dSJustin T. Gibbs /*openings*/0, 8968b8a9b1dSJustin T. Gibbs /*timeout*/0, 8978b8a9b1dSJustin T. Gibbs /*getcount_only*/0); 8988b8a9b1dSJustin T. Gibbs } 8998b8a9b1dSJustin T. Gibbs 9008b8a9b1dSJustin T. Gibbs switch (status) { 9018b8a9b1dSJustin T. Gibbs 9028b8a9b1dSJustin T. Gibbs case CAM_REQ_CMP: 9038b8a9b1dSJustin T. Gibbs 9048b8a9b1dSJustin T. Gibbs /* 9058b8a9b1dSJustin T. Gibbs * If we have successfully taken a device from the not 9068b8a9b1dSJustin T. Gibbs * ready to ready state, re-scan the device and re-get the 9078b8a9b1dSJustin T. Gibbs * inquiry information. Many devices (mostly disks) don't 9088b8a9b1dSJustin T. Gibbs * properly report their inquiry information unless they 9098b8a9b1dSJustin T. Gibbs * are spun up. 9108b8a9b1dSJustin T. Gibbs */ 9118b8a9b1dSJustin T. Gibbs if (done_ccb->ccb_h.func_code == XPT_SCSI_IO) { 9128b8a9b1dSJustin T. Gibbs scsi_cmd = (struct scsi_start_stop_unit *) 9138b8a9b1dSJustin T. Gibbs &done_ccb->csio.cdb_io.cdb_bytes; 9148b8a9b1dSJustin T. Gibbs 9158b8a9b1dSJustin T. Gibbs if (scsi_cmd->opcode == START_STOP_UNIT) 9168b8a9b1dSJustin T. Gibbs xpt_async(AC_INQ_CHANGED, 9178b8a9b1dSJustin T. Gibbs done_ccb->ccb_h.path, NULL); 9188b8a9b1dSJustin T. Gibbs } 9198b8a9b1dSJustin T. Gibbs bcopy(done_ccb->ccb_h.saved_ccb_ptr, done_ccb, 9208b8a9b1dSJustin T. Gibbs sizeof(union ccb)); 9218b8a9b1dSJustin T. Gibbs 92260a899a0SKenneth D. Merry periph->flags &= ~CAM_PERIPH_RECOVERY_INPROG; 92360a899a0SKenneth D. Merry 9248b8a9b1dSJustin T. Gibbs xpt_action(done_ccb); 9258b8a9b1dSJustin T. Gibbs 9268b8a9b1dSJustin T. Gibbs break; 9278b8a9b1dSJustin T. Gibbs case CAM_SCSI_STATUS_ERROR: 9288b8a9b1dSJustin T. Gibbs scsi_cmd = (struct scsi_start_stop_unit *) 9298b8a9b1dSJustin T. Gibbs &done_ccb->csio.cdb_io.cdb_bytes; 9308b8a9b1dSJustin T. Gibbs if (sense != 0) { 9318b8a9b1dSJustin T. Gibbs struct scsi_sense_data *sense; 9328b8a9b1dSJustin T. Gibbs int error_code, sense_key, asc, ascq; 9338b8a9b1dSJustin T. Gibbs 9348b8a9b1dSJustin T. Gibbs sense = &done_ccb->csio.sense_data; 9358b8a9b1dSJustin T. Gibbs scsi_extract_sense(sense, &error_code, 9368b8a9b1dSJustin T. Gibbs &sense_key, &asc, &ascq); 9378b8a9b1dSJustin T. Gibbs 9388b8a9b1dSJustin T. Gibbs /* 9398b8a9b1dSJustin T. Gibbs * If the error is "invalid field in CDB", 9408b8a9b1dSJustin T. Gibbs * and the load/eject flag is set, turn the 9418b8a9b1dSJustin T. Gibbs * flag off and try again. This is just in 9428b8a9b1dSJustin T. Gibbs * case the drive in question barfs on the 9438b8a9b1dSJustin T. Gibbs * load eject flag. The CAM code should set 9448b8a9b1dSJustin T. Gibbs * the load/eject flag by default for 9458b8a9b1dSJustin T. Gibbs * removable media. 9468b8a9b1dSJustin T. Gibbs */ 9478b8a9b1dSJustin T. Gibbs 9488b8a9b1dSJustin T. Gibbs /* XXX KDM 9498b8a9b1dSJustin T. Gibbs * Should we check to see what the specific 9508b8a9b1dSJustin T. Gibbs * scsi status is?? Or does it not matter 9518b8a9b1dSJustin T. Gibbs * since we already know that there was an 9528b8a9b1dSJustin T. Gibbs * error, and we know what the specific 9538b8a9b1dSJustin T. Gibbs * error code was, and we know what the 9548b8a9b1dSJustin T. Gibbs * opcode is.. 9558b8a9b1dSJustin T. Gibbs */ 9568b8a9b1dSJustin T. Gibbs if ((scsi_cmd->opcode == START_STOP_UNIT) && 9578b8a9b1dSJustin T. Gibbs ((scsi_cmd->how & SSS_LOEJ) != 0) && 9588b8a9b1dSJustin T. Gibbs (asc == 0x24) && (ascq == 0x00) && 9598b8a9b1dSJustin T. Gibbs (done_ccb->ccb_h.retry_count > 0)) { 9608b8a9b1dSJustin T. Gibbs 9618b8a9b1dSJustin T. Gibbs scsi_cmd->how &= ~SSS_LOEJ; 9628b8a9b1dSJustin T. Gibbs 9638b8a9b1dSJustin T. Gibbs xpt_action(done_ccb); 9648b8a9b1dSJustin T. Gibbs 9658b8a9b1dSJustin T. Gibbs } else if (done_ccb->ccb_h.retry_count > 0) { 9668b8a9b1dSJustin T. Gibbs /* 9678b8a9b1dSJustin T. Gibbs * In this case, the error recovery 9688b8a9b1dSJustin T. Gibbs * command failed, but we've got 9698b8a9b1dSJustin T. Gibbs * some retries left on it. Give 9708b8a9b1dSJustin T. Gibbs * it another try. 9718b8a9b1dSJustin T. Gibbs */ 9728b8a9b1dSJustin T. Gibbs 9738b8a9b1dSJustin T. Gibbs /* set the timeout to .5 sec */ 9748b8a9b1dSJustin T. Gibbs relsim_flags = 9758b8a9b1dSJustin T. Gibbs RELSIM_RELEASE_AFTER_TIMEOUT; 9768b8a9b1dSJustin T. Gibbs timeout = 500; 9778b8a9b1dSJustin T. Gibbs 9788b8a9b1dSJustin T. Gibbs xpt_action(done_ccb); 9798b8a9b1dSJustin T. Gibbs 9808b8a9b1dSJustin T. Gibbs break; 9818b8a9b1dSJustin T. Gibbs 9828b8a9b1dSJustin T. Gibbs } else { 9838b8a9b1dSJustin T. Gibbs /* 9848b8a9b1dSJustin T. Gibbs * Copy the original CCB back and 9858b8a9b1dSJustin T. Gibbs * send it back to the caller. 9868b8a9b1dSJustin T. Gibbs */ 9878b8a9b1dSJustin T. Gibbs bcopy(done_ccb->ccb_h.saved_ccb_ptr, 9888b8a9b1dSJustin T. Gibbs done_ccb, sizeof(union ccb)); 9898b8a9b1dSJustin T. Gibbs 99060a899a0SKenneth D. Merry periph->flags &= ~CAM_PERIPH_RECOVERY_INPROG; 99160a899a0SKenneth D. Merry 9928b8a9b1dSJustin T. Gibbs xpt_action(done_ccb); 9938b8a9b1dSJustin T. Gibbs } 9948b8a9b1dSJustin T. Gibbs } else { 9958b8a9b1dSJustin T. Gibbs /* 9968b8a9b1dSJustin T. Gibbs * Eh?? The command failed, but we don't 9978b8a9b1dSJustin T. Gibbs * have any sense. What's up with that? 9988b8a9b1dSJustin T. Gibbs * Fire the CCB again to return it to the 9998b8a9b1dSJustin T. Gibbs * caller. 10008b8a9b1dSJustin T. Gibbs */ 10018b8a9b1dSJustin T. Gibbs bcopy(done_ccb->ccb_h.saved_ccb_ptr, 10028b8a9b1dSJustin T. Gibbs done_ccb, sizeof(union ccb)); 10038b8a9b1dSJustin T. Gibbs 100460a899a0SKenneth D. Merry periph->flags &= ~CAM_PERIPH_RECOVERY_INPROG; 100560a899a0SKenneth D. Merry 10068b8a9b1dSJustin T. Gibbs xpt_action(done_ccb); 10078b8a9b1dSJustin T. Gibbs 10088b8a9b1dSJustin T. Gibbs } 10098b8a9b1dSJustin T. Gibbs break; 10108b8a9b1dSJustin T. Gibbs default: 10118b8a9b1dSJustin T. Gibbs bcopy(done_ccb->ccb_h.saved_ccb_ptr, done_ccb, 10128b8a9b1dSJustin T. Gibbs sizeof(union ccb)); 10138b8a9b1dSJustin T. Gibbs 101460a899a0SKenneth D. Merry periph->flags &= ~CAM_PERIPH_RECOVERY_INPROG; 101560a899a0SKenneth D. Merry 10168b8a9b1dSJustin T. Gibbs xpt_action(done_ccb); 10178b8a9b1dSJustin T. Gibbs 10188b8a9b1dSJustin T. Gibbs break; 10198b8a9b1dSJustin T. Gibbs } 10208b8a9b1dSJustin T. Gibbs 10218b8a9b1dSJustin T. Gibbs /* decrement the retry count */ 10228b8a9b1dSJustin T. Gibbs if (done_ccb->ccb_h.retry_count > 0) 10238b8a9b1dSJustin T. Gibbs done_ccb->ccb_h.retry_count--; 10248b8a9b1dSJustin T. Gibbs 10258b8a9b1dSJustin T. Gibbs qfrozen_cnt = cam_release_devq(done_ccb->ccb_h.path, 10268b8a9b1dSJustin T. Gibbs /*relsim_flags*/relsim_flags, 10278b8a9b1dSJustin T. Gibbs /*openings*/0, 10288b8a9b1dSJustin T. Gibbs /*timeout*/timeout, 10298b8a9b1dSJustin T. Gibbs /*getcount_only*/0); 10308b8a9b1dSJustin T. Gibbs } 10318b8a9b1dSJustin T. Gibbs 10328b8a9b1dSJustin T. Gibbs /* 103387cfaf0eSJustin T. Gibbs * Generic Async Event handler. Peripheral drivers usually 103487cfaf0eSJustin T. Gibbs * filter out the events that require personal attention, 103587cfaf0eSJustin T. Gibbs * and leave the rest to this function. 103687cfaf0eSJustin T. Gibbs */ 103787cfaf0eSJustin T. Gibbs void 103887cfaf0eSJustin T. Gibbs cam_periph_async(struct cam_periph *periph, u_int32_t code, 103987cfaf0eSJustin T. Gibbs struct cam_path *path, void *arg) 104087cfaf0eSJustin T. Gibbs { 104187cfaf0eSJustin T. Gibbs switch (code) { 104287cfaf0eSJustin T. Gibbs case AC_LOST_DEVICE: 104387cfaf0eSJustin T. Gibbs cam_periph_invalidate(periph); 104487cfaf0eSJustin T. Gibbs break; 104587cfaf0eSJustin T. Gibbs case AC_SENT_BDR: 104687cfaf0eSJustin T. Gibbs case AC_BUS_RESET: 104787cfaf0eSJustin T. Gibbs { 104887cfaf0eSJustin T. Gibbs cam_periph_bus_settle(periph, SCSI_DELAY); 104987cfaf0eSJustin T. Gibbs break; 105087cfaf0eSJustin T. Gibbs } 105187cfaf0eSJustin T. Gibbs default: 105287cfaf0eSJustin T. Gibbs break; 105387cfaf0eSJustin T. Gibbs } 105487cfaf0eSJustin T. Gibbs } 105587cfaf0eSJustin T. Gibbs 105687cfaf0eSJustin T. Gibbs void 105787cfaf0eSJustin T. Gibbs cam_periph_bus_settle(struct cam_periph *periph, u_int bus_settle) 105887cfaf0eSJustin T. Gibbs { 105987cfaf0eSJustin T. Gibbs struct ccb_getdevstats cgds; 106087cfaf0eSJustin T. Gibbs 106187cfaf0eSJustin T. Gibbs xpt_setup_ccb(&cgds.ccb_h, periph->path, /*priority*/1); 106287cfaf0eSJustin T. Gibbs cgds.ccb_h.func_code = XPT_GDEV_STATS; 106387cfaf0eSJustin T. Gibbs xpt_action((union ccb *)&cgds); 106487cfaf0eSJustin T. Gibbs cam_periph_freeze_after_event(periph, &cgds.last_reset, bus_settle); 106587cfaf0eSJustin T. Gibbs } 106687cfaf0eSJustin T. Gibbs 106787cfaf0eSJustin T. Gibbs void 106887cfaf0eSJustin T. Gibbs cam_periph_freeze_after_event(struct cam_periph *periph, 106987cfaf0eSJustin T. Gibbs struct timeval* event_time, u_int duration_ms) 107087cfaf0eSJustin T. Gibbs { 107187cfaf0eSJustin T. Gibbs struct timeval delta; 107287cfaf0eSJustin T. Gibbs struct timeval duration_tv; 107387cfaf0eSJustin T. Gibbs int s; 107487cfaf0eSJustin T. Gibbs 107587cfaf0eSJustin T. Gibbs s = splclock(); 107687cfaf0eSJustin T. Gibbs microtime(&delta); 107787cfaf0eSJustin T. Gibbs splx(s); 107887cfaf0eSJustin T. Gibbs timevalsub(&delta, event_time); 107987cfaf0eSJustin T. Gibbs duration_tv.tv_sec = duration_ms / 1000; 108087cfaf0eSJustin T. Gibbs duration_tv.tv_usec = (duration_ms % 1000) * 1000; 108187cfaf0eSJustin T. Gibbs if (timevalcmp(&delta, &duration_tv, <)) { 108287cfaf0eSJustin T. Gibbs timevalsub(&duration_tv, &delta); 108387cfaf0eSJustin T. Gibbs 108487cfaf0eSJustin T. Gibbs duration_ms = duration_tv.tv_sec * 1000; 108587cfaf0eSJustin T. Gibbs duration_ms += duration_tv.tv_usec / 1000; 108687cfaf0eSJustin T. Gibbs cam_freeze_devq(periph->path); 108787cfaf0eSJustin T. Gibbs cam_release_devq(periph->path, 108887cfaf0eSJustin T. Gibbs RELSIM_RELEASE_AFTER_TIMEOUT, 108987cfaf0eSJustin T. Gibbs /*reduction*/0, 109087cfaf0eSJustin T. Gibbs /*timeout*/duration_ms, 109187cfaf0eSJustin T. Gibbs /*getcount_only*/0); 109287cfaf0eSJustin T. Gibbs } 109387cfaf0eSJustin T. Gibbs 109487cfaf0eSJustin T. Gibbs } 109587cfaf0eSJustin T. Gibbs 109687cfaf0eSJustin T. Gibbs /* 10978b8a9b1dSJustin T. Gibbs * Generic error handler. Peripheral drivers usually filter 10988b8a9b1dSJustin T. Gibbs * out the errors that they handle in a unique mannor, then 10998b8a9b1dSJustin T. Gibbs * call this function. 11008b8a9b1dSJustin T. Gibbs */ 11018b8a9b1dSJustin T. Gibbs int 11028b8a9b1dSJustin T. Gibbs cam_periph_error(union ccb *ccb, cam_flags camflags, 11038b8a9b1dSJustin T. Gibbs u_int32_t sense_flags, union ccb *save_ccb) 11048b8a9b1dSJustin T. Gibbs { 11058b8a9b1dSJustin T. Gibbs cam_status status; 11068b8a9b1dSJustin T. Gibbs int frozen; 11078b8a9b1dSJustin T. Gibbs int sense; 11088b8a9b1dSJustin T. Gibbs int error; 11098b8a9b1dSJustin T. Gibbs int openings; 11108b8a9b1dSJustin T. Gibbs int retry; 11118b8a9b1dSJustin T. Gibbs u_int32_t relsim_flags; 11128b8a9b1dSJustin T. Gibbs u_int32_t timeout; 11138b8a9b1dSJustin T. Gibbs 11148b8a9b1dSJustin T. Gibbs status = ccb->ccb_h.status; 11158b8a9b1dSJustin T. Gibbs frozen = (status & CAM_DEV_QFRZN) != 0; 11168b8a9b1dSJustin T. Gibbs sense = (status & CAM_AUTOSNS_VALID) != 0; 11178b8a9b1dSJustin T. Gibbs status &= CAM_STATUS_MASK; 11188b8a9b1dSJustin T. Gibbs relsim_flags = 0; 11198b8a9b1dSJustin T. Gibbs 11208b8a9b1dSJustin T. Gibbs switch (status) { 11218b8a9b1dSJustin T. Gibbs case CAM_REQ_CMP: 11228b8a9b1dSJustin T. Gibbs /* decrement the number of retries */ 11238b8a9b1dSJustin T. Gibbs retry = ccb->ccb_h.retry_count > 0; 11248b8a9b1dSJustin T. Gibbs if (retry) 11258b8a9b1dSJustin T. Gibbs ccb->ccb_h.retry_count--; 11268b8a9b1dSJustin T. Gibbs error = 0; 11278b8a9b1dSJustin T. Gibbs break; 11285a526431SJustin T. Gibbs case CAM_AUTOSENSE_FAIL: 11298b8a9b1dSJustin T. Gibbs case CAM_SCSI_STATUS_ERROR: 11308b8a9b1dSJustin T. Gibbs 11318b8a9b1dSJustin T. Gibbs switch (ccb->csio.scsi_status) { 11328b8a9b1dSJustin T. Gibbs case SCSI_STATUS_OK: 11338b8a9b1dSJustin T. Gibbs case SCSI_STATUS_COND_MET: 11348b8a9b1dSJustin T. Gibbs case SCSI_STATUS_INTERMED: 11358b8a9b1dSJustin T. Gibbs case SCSI_STATUS_INTERMED_COND_MET: 11368b8a9b1dSJustin T. Gibbs error = 0; 11378b8a9b1dSJustin T. Gibbs break; 11388b8a9b1dSJustin T. Gibbs case SCSI_STATUS_CMD_TERMINATED: 11398b8a9b1dSJustin T. Gibbs case SCSI_STATUS_CHECK_COND: 11408b8a9b1dSJustin T. Gibbs if (sense != 0) { 11418b8a9b1dSJustin T. Gibbs struct scsi_sense_data *sense; 11428b8a9b1dSJustin T. Gibbs int error_code, sense_key, asc, ascq; 11438b8a9b1dSJustin T. Gibbs struct cam_periph *periph; 11448b8a9b1dSJustin T. Gibbs scsi_sense_action err_action; 11458b8a9b1dSJustin T. Gibbs struct ccb_getdev cgd; 11468b8a9b1dSJustin T. Gibbs 11478b8a9b1dSJustin T. Gibbs sense = &ccb->csio.sense_data; 11488b8a9b1dSJustin T. Gibbs scsi_extract_sense(sense, &error_code, 11498b8a9b1dSJustin T. Gibbs &sense_key, &asc, &ascq); 11508b8a9b1dSJustin T. Gibbs periph = xpt_path_periph(ccb->ccb_h.path); 11518b8a9b1dSJustin T. Gibbs 11528b8a9b1dSJustin T. Gibbs /* 11538b8a9b1dSJustin T. Gibbs * Grab the inquiry data for this device. 11548b8a9b1dSJustin T. Gibbs */ 11558b8a9b1dSJustin T. Gibbs xpt_setup_ccb(&cgd.ccb_h, ccb->ccb_h.path, 11568b8a9b1dSJustin T. Gibbs /*priority*/ 1); 11578b8a9b1dSJustin T. Gibbs cgd.ccb_h.func_code = XPT_GDEV_TYPE; 11588b8a9b1dSJustin T. Gibbs xpt_action((union ccb *)&cgd); 11598b8a9b1dSJustin T. Gibbs 11608b8a9b1dSJustin T. Gibbs err_action = scsi_error_action(asc, ascq, 11618b8a9b1dSJustin T. Gibbs &cgd.inq_data); 11628b8a9b1dSJustin T. Gibbs 11638b8a9b1dSJustin T. Gibbs /* 11648b8a9b1dSJustin T. Gibbs * Send a Test Unit Ready to the device. 11658b8a9b1dSJustin T. Gibbs * If the 'many' flag is set, we send 120 11668b8a9b1dSJustin T. Gibbs * test unit ready commands, one every half 11678b8a9b1dSJustin T. Gibbs * second. Otherwise, we just send one TUR. 11688b8a9b1dSJustin T. Gibbs * We only want to do this if the retry 11698b8a9b1dSJustin T. Gibbs * count has not been exhausted. 11708b8a9b1dSJustin T. Gibbs */ 11718b8a9b1dSJustin T. Gibbs if (((err_action & SS_MASK) == SS_TUR) 11728b8a9b1dSJustin T. Gibbs && save_ccb != NULL 11738b8a9b1dSJustin T. Gibbs && ccb->ccb_h.retry_count > 0) { 11748b8a9b1dSJustin T. Gibbs 117560a899a0SKenneth D. Merry /* 117660a899a0SKenneth D. Merry * Since error recovery is already 117760a899a0SKenneth D. Merry * in progress, don't attempt to 117860a899a0SKenneth D. Merry * process this error. It is probably 117960a899a0SKenneth D. Merry * related to the error that caused 118060a899a0SKenneth D. Merry * the currently active error recovery 118160a899a0SKenneth D. Merry * action. Also, we only have 118260a899a0SKenneth D. Merry * space for one saved CCB, so if we 118360a899a0SKenneth D. Merry * had two concurrent error recovery 118460a899a0SKenneth D. Merry * actions, we would end up 118560a899a0SKenneth D. Merry * over-writing one error recovery 118660a899a0SKenneth D. Merry * CCB with another one. 118760a899a0SKenneth D. Merry */ 118860a899a0SKenneth D. Merry if (periph->flags & 118960a899a0SKenneth D. Merry CAM_PERIPH_RECOVERY_INPROG) { 119060a899a0SKenneth D. Merry error = ERESTART; 119160a899a0SKenneth D. Merry break; 119260a899a0SKenneth D. Merry } 119360a899a0SKenneth D. Merry 119460a899a0SKenneth D. Merry periph->flags |= 119560a899a0SKenneth D. Merry CAM_PERIPH_RECOVERY_INPROG; 119660a899a0SKenneth D. Merry 11978b8a9b1dSJustin T. Gibbs /* decrement the number of retries */ 11988b8a9b1dSJustin T. Gibbs if ((err_action & 11998b8a9b1dSJustin T. Gibbs SSQ_DECREMENT_COUNT) != 0) { 12008b8a9b1dSJustin T. Gibbs retry = 1; 12018b8a9b1dSJustin T. Gibbs ccb->ccb_h.retry_count--; 12028b8a9b1dSJustin T. Gibbs } 12038b8a9b1dSJustin T. Gibbs 12048b8a9b1dSJustin T. Gibbs bcopy(ccb, save_ccb, sizeof(*save_ccb)); 12058b8a9b1dSJustin T. Gibbs 12068b8a9b1dSJustin T. Gibbs /* 12078b8a9b1dSJustin T. Gibbs * We retry this one every half 12088b8a9b1dSJustin T. Gibbs * second for a minute. If the 12098b8a9b1dSJustin T. Gibbs * device hasn't become ready in a 12108b8a9b1dSJustin T. Gibbs * minute's time, it's unlikely to 12118b8a9b1dSJustin T. Gibbs * ever become ready. If the table 12128b8a9b1dSJustin T. Gibbs * doesn't specify SSQ_MANY, we can 12138b8a9b1dSJustin T. Gibbs * only try this once. Oh well. 12148b8a9b1dSJustin T. Gibbs */ 12158b8a9b1dSJustin T. Gibbs if ((err_action & SSQ_MANY) != 0) 12168b8a9b1dSJustin T. Gibbs scsi_test_unit_ready(&ccb->csio, 12178b8a9b1dSJustin T. Gibbs /*retries*/120, 12188b8a9b1dSJustin T. Gibbs camperiphdone, 12198b8a9b1dSJustin T. Gibbs MSG_SIMPLE_Q_TAG, 12208b8a9b1dSJustin T. Gibbs SSD_FULL_SIZE, 12218b8a9b1dSJustin T. Gibbs /*timeout*/5000); 12228b8a9b1dSJustin T. Gibbs else 12238b8a9b1dSJustin T. Gibbs scsi_test_unit_ready(&ccb->csio, 12248b8a9b1dSJustin T. Gibbs /*retries*/1, 12258b8a9b1dSJustin T. Gibbs camperiphdone, 12268b8a9b1dSJustin T. Gibbs MSG_SIMPLE_Q_TAG, 12278b8a9b1dSJustin T. Gibbs SSD_FULL_SIZE, 12288b8a9b1dSJustin T. Gibbs /*timeout*/5000); 12298b8a9b1dSJustin T. Gibbs 12308b8a9b1dSJustin T. Gibbs /* release the queue after .5 sec. */ 12318b8a9b1dSJustin T. Gibbs relsim_flags = 12328b8a9b1dSJustin T. Gibbs RELSIM_RELEASE_AFTER_TIMEOUT; 12338b8a9b1dSJustin T. Gibbs timeout = 500; 12348b8a9b1dSJustin T. Gibbs /* 12358b8a9b1dSJustin T. Gibbs * Drop the priority to 0 so that 12368b8a9b1dSJustin T. Gibbs * we are the first to execute. Also 12378b8a9b1dSJustin T. Gibbs * freeze the queue after this command 12388b8a9b1dSJustin T. Gibbs * is sent so that we can restore the 12398b8a9b1dSJustin T. Gibbs * old csio and have it queued in the 12408b8a9b1dSJustin T. Gibbs * proper order before we let normal 12418b8a9b1dSJustin T. Gibbs * transactions go to the drive. 12428b8a9b1dSJustin T. Gibbs */ 12438b8a9b1dSJustin T. Gibbs ccb->ccb_h.pinfo.priority = 0; 12448b8a9b1dSJustin T. Gibbs ccb->ccb_h.flags |= CAM_DEV_QFREEZE; 12458b8a9b1dSJustin T. Gibbs 12468b8a9b1dSJustin T. Gibbs /* 12478b8a9b1dSJustin T. Gibbs * Save a pointer to the original 12488b8a9b1dSJustin T. Gibbs * CCB in the new CCB. 12498b8a9b1dSJustin T. Gibbs */ 12508b8a9b1dSJustin T. Gibbs ccb->ccb_h.saved_ccb_ptr = save_ccb; 12518b8a9b1dSJustin T. Gibbs 12528b8a9b1dSJustin T. Gibbs error = ERESTART; 12538b8a9b1dSJustin T. Gibbs } 12548b8a9b1dSJustin T. Gibbs /* 12558b8a9b1dSJustin T. Gibbs * Send a start unit command to the device, 12568b8a9b1dSJustin T. Gibbs * and then retry the command. We only 12578b8a9b1dSJustin T. Gibbs * want to do this if the retry count has 12588b8a9b1dSJustin T. Gibbs * not been exhausted. If the user 12598b8a9b1dSJustin T. Gibbs * specified 0 retries, then we follow 12608b8a9b1dSJustin T. Gibbs * their request and do not retry. 12618b8a9b1dSJustin T. Gibbs */ 12628b8a9b1dSJustin T. Gibbs else if (((err_action & SS_MASK) == SS_START) 12638b8a9b1dSJustin T. Gibbs && save_ccb != NULL 12648b8a9b1dSJustin T. Gibbs && ccb->ccb_h.retry_count > 0) { 12658b8a9b1dSJustin T. Gibbs int le; 12668b8a9b1dSJustin T. Gibbs 126760a899a0SKenneth D. Merry /* 126860a899a0SKenneth D. Merry * Only one error recovery action 126960a899a0SKenneth D. Merry * at a time. See above. 127060a899a0SKenneth D. Merry */ 127160a899a0SKenneth D. Merry if (periph->flags & 127260a899a0SKenneth D. Merry CAM_PERIPH_RECOVERY_INPROG) { 127360a899a0SKenneth D. Merry error = ERESTART; 127460a899a0SKenneth D. Merry break; 127560a899a0SKenneth D. Merry } 127660a899a0SKenneth D. Merry 127760a899a0SKenneth D. Merry periph->flags |= 127860a899a0SKenneth D. Merry CAM_PERIPH_RECOVERY_INPROG; 127960a899a0SKenneth D. Merry 12808b8a9b1dSJustin T. Gibbs /* decrement the number of retries */ 12818b8a9b1dSJustin T. Gibbs retry = 1; 12828b8a9b1dSJustin T. Gibbs ccb->ccb_h.retry_count--; 12838b8a9b1dSJustin T. Gibbs 12848b8a9b1dSJustin T. Gibbs /* 12858b8a9b1dSJustin T. Gibbs * Check for removable media and 12868b8a9b1dSJustin T. Gibbs * set load/eject flag 12878b8a9b1dSJustin T. Gibbs * appropriately. 12888b8a9b1dSJustin T. Gibbs */ 12898b8a9b1dSJustin T. Gibbs if (SID_IS_REMOVABLE(&cgd.inq_data)) 12908b8a9b1dSJustin T. Gibbs le = TRUE; 12918b8a9b1dSJustin T. Gibbs else 12928b8a9b1dSJustin T. Gibbs le = FALSE; 12938b8a9b1dSJustin T. Gibbs 12948b8a9b1dSJustin T. Gibbs /* 12958b8a9b1dSJustin T. Gibbs * Attempt to start the drive up. 12968b8a9b1dSJustin T. Gibbs * 12978b8a9b1dSJustin T. Gibbs * Save the current ccb so it can 12988b8a9b1dSJustin T. Gibbs * be restored and retried once the 12998b8a9b1dSJustin T. Gibbs * drive is started up. 13008b8a9b1dSJustin T. Gibbs */ 13018b8a9b1dSJustin T. Gibbs bcopy(ccb, save_ccb, sizeof(*save_ccb)); 13028b8a9b1dSJustin T. Gibbs 13038b8a9b1dSJustin T. Gibbs scsi_start_stop(&ccb->csio, 13048b8a9b1dSJustin T. Gibbs /*retries*/1, 13058b8a9b1dSJustin T. Gibbs camperiphdone, 13068b8a9b1dSJustin T. Gibbs MSG_SIMPLE_Q_TAG, 13078b8a9b1dSJustin T. Gibbs /*start*/TRUE, 13088b8a9b1dSJustin T. Gibbs /*load/eject*/le, 13098b8a9b1dSJustin T. Gibbs /*immediate*/FALSE, 13108b8a9b1dSJustin T. Gibbs SSD_FULL_SIZE, 13118b8a9b1dSJustin T. Gibbs /*timeout*/50000); 13128b8a9b1dSJustin T. Gibbs /* 13138b8a9b1dSJustin T. Gibbs * Drop the priority to 0 so that 13148b8a9b1dSJustin T. Gibbs * we are the first to execute. Also 13158b8a9b1dSJustin T. Gibbs * freeze the queue after this command 13168b8a9b1dSJustin T. Gibbs * is sent so that we can restore the 13178b8a9b1dSJustin T. Gibbs * old csio and have it queued in the 13188b8a9b1dSJustin T. Gibbs * proper order before we let normal 13198b8a9b1dSJustin T. Gibbs * transactions go to the drive. 13208b8a9b1dSJustin T. Gibbs */ 13218b8a9b1dSJustin T. Gibbs ccb->ccb_h.pinfo.priority = 0; 13228b8a9b1dSJustin T. Gibbs ccb->ccb_h.flags |= CAM_DEV_QFREEZE; 13238b8a9b1dSJustin T. Gibbs 13248b8a9b1dSJustin T. Gibbs /* 13258b8a9b1dSJustin T. Gibbs * Save a pointer to the original 13268b8a9b1dSJustin T. Gibbs * CCB in the new CCB. 13278b8a9b1dSJustin T. Gibbs */ 13288b8a9b1dSJustin T. Gibbs ccb->ccb_h.saved_ccb_ptr = save_ccb; 13298b8a9b1dSJustin T. Gibbs 13308b8a9b1dSJustin T. Gibbs error = ERESTART; 13318b8a9b1dSJustin T. Gibbs } else if ((sense_flags & SF_RETRY_UA) != 0) { 13328b8a9b1dSJustin T. Gibbs /* 13338b8a9b1dSJustin T. Gibbs * XXX KDM this is a *horrible* 13348b8a9b1dSJustin T. Gibbs * hack. 13358b8a9b1dSJustin T. Gibbs */ 13368b8a9b1dSJustin T. Gibbs error = scsi_interpret_sense(ccb, 13378b8a9b1dSJustin T. Gibbs sense_flags, 13388b8a9b1dSJustin T. Gibbs &relsim_flags, 13398b8a9b1dSJustin T. Gibbs &openings, 13408b8a9b1dSJustin T. Gibbs &timeout, 13418b8a9b1dSJustin T. Gibbs err_action); 13428b8a9b1dSJustin T. Gibbs } 13438b8a9b1dSJustin T. Gibbs 13448b8a9b1dSJustin T. Gibbs /* 13458b8a9b1dSJustin T. Gibbs * Theoretically, this code should send a 13468b8a9b1dSJustin T. Gibbs * test unit ready to the given device, and 13478b8a9b1dSJustin T. Gibbs * if it returns and error, send a start 13488b8a9b1dSJustin T. Gibbs * unit command. Since we don't yet have 13498b8a9b1dSJustin T. Gibbs * the capability to do two-command error 13508b8a9b1dSJustin T. Gibbs * recovery, just send a start unit. 13518b8a9b1dSJustin T. Gibbs * XXX KDM fix this! 13528b8a9b1dSJustin T. Gibbs */ 13538b8a9b1dSJustin T. Gibbs else if (((err_action & SS_MASK) == SS_TURSTART) 13548b8a9b1dSJustin T. Gibbs && save_ccb != NULL 13558b8a9b1dSJustin T. Gibbs && ccb->ccb_h.retry_count > 0) { 13568b8a9b1dSJustin T. Gibbs int le; 13578b8a9b1dSJustin T. Gibbs 135860a899a0SKenneth D. Merry /* 135960a899a0SKenneth D. Merry * Only one error recovery action 136060a899a0SKenneth D. Merry * at a time. See above. 136160a899a0SKenneth D. Merry */ 136260a899a0SKenneth D. Merry if (periph->flags & 136360a899a0SKenneth D. Merry CAM_PERIPH_RECOVERY_INPROG) { 136460a899a0SKenneth D. Merry error = ERESTART; 136560a899a0SKenneth D. Merry break; 136660a899a0SKenneth D. Merry } 136760a899a0SKenneth D. Merry 136860a899a0SKenneth D. Merry periph->flags |= 136960a899a0SKenneth D. Merry CAM_PERIPH_RECOVERY_INPROG; 137060a899a0SKenneth D. Merry 13718b8a9b1dSJustin T. Gibbs /* decrement the number of retries */ 13728b8a9b1dSJustin T. Gibbs retry = 1; 13738b8a9b1dSJustin T. Gibbs ccb->ccb_h.retry_count--; 13748b8a9b1dSJustin T. Gibbs 13758b8a9b1dSJustin T. Gibbs /* 13768b8a9b1dSJustin T. Gibbs * Check for removable media and 13778b8a9b1dSJustin T. Gibbs * set load/eject flag 13788b8a9b1dSJustin T. Gibbs * appropriately. 13798b8a9b1dSJustin T. Gibbs */ 13808b8a9b1dSJustin T. Gibbs if (SID_IS_REMOVABLE(&cgd.inq_data)) 13818b8a9b1dSJustin T. Gibbs le = TRUE; 13828b8a9b1dSJustin T. Gibbs else 13838b8a9b1dSJustin T. Gibbs le = FALSE; 13848b8a9b1dSJustin T. Gibbs 13858b8a9b1dSJustin T. Gibbs /* 13868b8a9b1dSJustin T. Gibbs * Attempt to start the drive up. 13878b8a9b1dSJustin T. Gibbs * 13888b8a9b1dSJustin T. Gibbs * Save the current ccb so it can 13898b8a9b1dSJustin T. Gibbs * be restored and retried once the 13908b8a9b1dSJustin T. Gibbs * drive is started up. 13918b8a9b1dSJustin T. Gibbs */ 13928b8a9b1dSJustin T. Gibbs bcopy(ccb, save_ccb, sizeof(*save_ccb)); 13938b8a9b1dSJustin T. Gibbs 13948b8a9b1dSJustin T. Gibbs scsi_start_stop(&ccb->csio, 13958b8a9b1dSJustin T. Gibbs /*retries*/1, 13968b8a9b1dSJustin T. Gibbs camperiphdone, 13978b8a9b1dSJustin T. Gibbs MSG_SIMPLE_Q_TAG, 13988b8a9b1dSJustin T. Gibbs /*start*/TRUE, 13998b8a9b1dSJustin T. Gibbs /*load/eject*/le, 14008b8a9b1dSJustin T. Gibbs /*immediate*/FALSE, 14018b8a9b1dSJustin T. Gibbs SSD_FULL_SIZE, 14028b8a9b1dSJustin T. Gibbs /*timeout*/50000); 14038b8a9b1dSJustin T. Gibbs 14048b8a9b1dSJustin T. Gibbs /* release the queue after .5 sec. */ 14058b8a9b1dSJustin T. Gibbs relsim_flags = 14068b8a9b1dSJustin T. Gibbs RELSIM_RELEASE_AFTER_TIMEOUT; 14078b8a9b1dSJustin T. Gibbs timeout = 500; 14088b8a9b1dSJustin T. Gibbs /* 14098b8a9b1dSJustin T. Gibbs * Drop the priority to 0 so that 14108b8a9b1dSJustin T. Gibbs * we are the first to execute. Also 14118b8a9b1dSJustin T. Gibbs * freeze the queue after this command 14128b8a9b1dSJustin T. Gibbs * is sent so that we can restore the 14138b8a9b1dSJustin T. Gibbs * old csio and have it queued in the 14148b8a9b1dSJustin T. Gibbs * proper order before we let normal 14158b8a9b1dSJustin T. Gibbs * transactions go to the drive. 14168b8a9b1dSJustin T. Gibbs */ 14178b8a9b1dSJustin T. Gibbs ccb->ccb_h.pinfo.priority = 0; 14188b8a9b1dSJustin T. Gibbs ccb->ccb_h.flags |= CAM_DEV_QFREEZE; 14198b8a9b1dSJustin T. Gibbs 14208b8a9b1dSJustin T. Gibbs /* 14218b8a9b1dSJustin T. Gibbs * Save a pointer to the original 14228b8a9b1dSJustin T. Gibbs * CCB in the new CCB. 14238b8a9b1dSJustin T. Gibbs */ 14248b8a9b1dSJustin T. Gibbs ccb->ccb_h.saved_ccb_ptr = save_ccb; 14258b8a9b1dSJustin T. Gibbs 14268b8a9b1dSJustin T. Gibbs error = ERESTART; 14278b8a9b1dSJustin T. Gibbs } else { 14288b8a9b1dSJustin T. Gibbs error = scsi_interpret_sense(ccb, 14298b8a9b1dSJustin T. Gibbs sense_flags, 14308b8a9b1dSJustin T. Gibbs &relsim_flags, 14318b8a9b1dSJustin T. Gibbs &openings, 14328b8a9b1dSJustin T. Gibbs &timeout, 14338b8a9b1dSJustin T. Gibbs err_action); 14348b8a9b1dSJustin T. Gibbs } 14358b8a9b1dSJustin T. Gibbs } else if (ccb->csio.scsi_status == 14365a526431SJustin T. Gibbs SCSI_STATUS_CHECK_COND 14375a526431SJustin T. Gibbs && status != CAM_AUTOSENSE_FAIL) { 14388b8a9b1dSJustin T. Gibbs /* no point in decrementing the retry count */ 14398b8a9b1dSJustin T. Gibbs panic("cam_periph_error: scsi status of " 14408b8a9b1dSJustin T. Gibbs "CHECK COND returned but no sense " 14418b8a9b1dSJustin T. Gibbs "information is availible. " 14428b8a9b1dSJustin T. Gibbs "Controller should have returned " 14438b8a9b1dSJustin T. Gibbs "CAM_AUTOSENSE_FAILED"); 14448b8a9b1dSJustin T. Gibbs /* NOTREACHED */ 14458b8a9b1dSJustin T. Gibbs error = EIO; 144656e3e24bSKenneth D. Merry } else if (ccb->ccb_h.retry_count == 0) { 14478b8a9b1dSJustin T. Gibbs /* 14488b8a9b1dSJustin T. Gibbs * XXX KDM shouldn't there be a better 14498b8a9b1dSJustin T. Gibbs * argument to return?? 14508b8a9b1dSJustin T. Gibbs */ 14518b8a9b1dSJustin T. Gibbs error = EIO; 14528b8a9b1dSJustin T. Gibbs } else { 14538b8a9b1dSJustin T. Gibbs /* decrement the number of retries */ 14548b8a9b1dSJustin T. Gibbs retry = ccb->ccb_h.retry_count > 0; 14558b8a9b1dSJustin T. Gibbs if (retry) 14568b8a9b1dSJustin T. Gibbs ccb->ccb_h.retry_count--; 14578b8a9b1dSJustin T. Gibbs /* 14588b8a9b1dSJustin T. Gibbs * If it was aborted with no 14598b8a9b1dSJustin T. Gibbs * clue as to the reason, just 14608b8a9b1dSJustin T. Gibbs * retry it again. 14618b8a9b1dSJustin T. Gibbs */ 14628b8a9b1dSJustin T. Gibbs error = ERESTART; 14638b8a9b1dSJustin T. Gibbs } 14648b8a9b1dSJustin T. Gibbs break; 14658b8a9b1dSJustin T. Gibbs case SCSI_STATUS_QUEUE_FULL: 14668b8a9b1dSJustin T. Gibbs { 14678b8a9b1dSJustin T. Gibbs /* no decrement */ 146882815562SJustin T. Gibbs struct ccb_getdevstats cgds; 14698b8a9b1dSJustin T. Gibbs 14708b8a9b1dSJustin T. Gibbs /* 14718b8a9b1dSJustin T. Gibbs * First off, find out what the current 14728b8a9b1dSJustin T. Gibbs * transaction counts are. 14738b8a9b1dSJustin T. Gibbs */ 147482815562SJustin T. Gibbs xpt_setup_ccb(&cgds.ccb_h, 14758b8a9b1dSJustin T. Gibbs ccb->ccb_h.path, 14768b8a9b1dSJustin T. Gibbs /*priority*/1); 147782815562SJustin T. Gibbs cgds.ccb_h.func_code = XPT_GDEV_STATS; 147882815562SJustin T. Gibbs xpt_action((union ccb *)&cgds); 14798b8a9b1dSJustin T. Gibbs 14808b8a9b1dSJustin T. Gibbs /* 14818b8a9b1dSJustin T. Gibbs * If we were the only transaction active, treat 14828b8a9b1dSJustin T. Gibbs * the QUEUE FULL as if it were a BUSY condition. 14838b8a9b1dSJustin T. Gibbs */ 148482815562SJustin T. Gibbs if (cgds.dev_active != 0) { 148582815562SJustin T. Gibbs int total_openings; 148682815562SJustin T. Gibbs 14878b8a9b1dSJustin T. Gibbs /* 14888b8a9b1dSJustin T. Gibbs * Reduce the number of openings to 14898b8a9b1dSJustin T. Gibbs * be 1 less than the amount it took 14908b8a9b1dSJustin T. Gibbs * to get a queue full bounded by the 14918b8a9b1dSJustin T. Gibbs * minimum allowed tag count for this 14928b8a9b1dSJustin T. Gibbs * device. 14938b8a9b1dSJustin T. Gibbs */ 149482815562SJustin T. Gibbs total_openings = 149582815562SJustin T. Gibbs cgds.dev_active+cgds.dev_openings; 149682815562SJustin T. Gibbs openings = cgds.dev_active; 149782815562SJustin T. Gibbs if (openings < cgds.mintags) 149882815562SJustin T. Gibbs openings = cgds.mintags; 149982815562SJustin T. Gibbs if (openings < total_openings) 15008b8a9b1dSJustin T. Gibbs relsim_flags = RELSIM_ADJUST_OPENINGS; 15018b8a9b1dSJustin T. Gibbs else { 15028b8a9b1dSJustin T. Gibbs /* 15038b8a9b1dSJustin T. Gibbs * Some devices report queue full for 15048b8a9b1dSJustin T. Gibbs * temporary resource shortages. For 15058b8a9b1dSJustin T. Gibbs * this reason, we allow a minimum 15068b8a9b1dSJustin T. Gibbs * tag count to be entered via a 15078b8a9b1dSJustin T. Gibbs * quirk entry to prevent the queue 15088b8a9b1dSJustin T. Gibbs * count on these devices from falling 15098b8a9b1dSJustin T. Gibbs * to a pessimisticly low value. We 15108b8a9b1dSJustin T. Gibbs * still wait for the next successful 15118b8a9b1dSJustin T. Gibbs * completion, however, before queueing 15128b8a9b1dSJustin T. Gibbs * more transactions to the device. 15138b8a9b1dSJustin T. Gibbs */ 15148b8a9b1dSJustin T. Gibbs relsim_flags = 15158b8a9b1dSJustin T. Gibbs RELSIM_RELEASE_AFTER_CMDCMPLT; 15168b8a9b1dSJustin T. Gibbs } 15178b8a9b1dSJustin T. Gibbs timeout = 0; 15188b8a9b1dSJustin T. Gibbs error = ERESTART; 15198b8a9b1dSJustin T. Gibbs break; 15208b8a9b1dSJustin T. Gibbs } 15218b8a9b1dSJustin T. Gibbs /* FALLTHROUGH */ 15228b8a9b1dSJustin T. Gibbs } 15238b8a9b1dSJustin T. Gibbs case SCSI_STATUS_BUSY: 15248b8a9b1dSJustin T. Gibbs /* 15258b8a9b1dSJustin T. Gibbs * Restart the queue after either another 15268b8a9b1dSJustin T. Gibbs * command completes or a 1 second timeout. 1527af51b059SMatt Jacob * If we have any retries left, that is. 15288b8a9b1dSJustin T. Gibbs */ 1529af51b059SMatt Jacob retry = ccb->ccb_h.retry_count > 0; 1530af51b059SMatt Jacob if (retry) { 1531af51b059SMatt Jacob ccb->ccb_h.retry_count--; 15328b8a9b1dSJustin T. Gibbs error = ERESTART; 15338b8a9b1dSJustin T. Gibbs relsim_flags = RELSIM_RELEASE_AFTER_TIMEOUT 15348b8a9b1dSJustin T. Gibbs | RELSIM_RELEASE_AFTER_CMDCMPLT; 15358b8a9b1dSJustin T. Gibbs timeout = 1000; 1536af51b059SMatt Jacob } else { 1537af51b059SMatt Jacob error = EIO; 1538af51b059SMatt Jacob } 15398b8a9b1dSJustin T. Gibbs break; 15408b8a9b1dSJustin T. Gibbs case SCSI_STATUS_RESERV_CONFLICT: 15418b8a9b1dSJustin T. Gibbs error = EIO; 15428b8a9b1dSJustin T. Gibbs break; 15438b8a9b1dSJustin T. Gibbs default: 15448b8a9b1dSJustin T. Gibbs error = EIO; 15458b8a9b1dSJustin T. Gibbs break; 15468b8a9b1dSJustin T. Gibbs } 15478b8a9b1dSJustin T. Gibbs break; 15488b8a9b1dSJustin T. Gibbs case CAM_REQ_CMP_ERR: 15498b8a9b1dSJustin T. Gibbs case CAM_CMD_TIMEOUT: 15508b8a9b1dSJustin T. Gibbs case CAM_UNEXP_BUSFREE: 15518b8a9b1dSJustin T. Gibbs case CAM_UNCOR_PARITY: 15528b8a9b1dSJustin T. Gibbs case CAM_DATA_RUN_ERR: 15538b8a9b1dSJustin T. Gibbs /* decrement the number of retries */ 15548b8a9b1dSJustin T. Gibbs retry = ccb->ccb_h.retry_count > 0; 15558b8a9b1dSJustin T. Gibbs if (retry) { 15568b8a9b1dSJustin T. Gibbs ccb->ccb_h.retry_count--; 15578b8a9b1dSJustin T. Gibbs error = ERESTART; 15588b8a9b1dSJustin T. Gibbs } else { 15598b8a9b1dSJustin T. Gibbs error = EIO; 15608b8a9b1dSJustin T. Gibbs } 15618b8a9b1dSJustin T. Gibbs break; 15628b8a9b1dSJustin T. Gibbs case CAM_UA_ABORT: 15638b8a9b1dSJustin T. Gibbs case CAM_UA_TERMIO: 15648b8a9b1dSJustin T. Gibbs case CAM_MSG_REJECT_REC: 15658b8a9b1dSJustin T. Gibbs /* XXX Don't know that these are correct */ 15668b8a9b1dSJustin T. Gibbs error = EIO; 15678b8a9b1dSJustin T. Gibbs break; 15688b8a9b1dSJustin T. Gibbs case CAM_SEL_TIMEOUT: 15698b8a9b1dSJustin T. Gibbs { 1570e471e974SJustin T. Gibbs /* 1571e471e974SJustin T. Gibbs * XXX 1572e471e974SJustin T. Gibbs * A single selection timeout should not be enough 1573e471e974SJustin T. Gibbs * to invalidate a device. We should retry for multiple 1574e471e974SJustin T. Gibbs * seconds assuming this isn't a probe. We'll probably 1575e471e974SJustin T. Gibbs * need a special flag for that. 1576e471e974SJustin T. Gibbs */ 1577e471e974SJustin T. Gibbs #if 0 15788b8a9b1dSJustin T. Gibbs struct cam_path *newpath; 15798b8a9b1dSJustin T. Gibbs 15808b8a9b1dSJustin T. Gibbs /* Should we do more if we can't create the path?? */ 15818b8a9b1dSJustin T. Gibbs if (xpt_create_path(&newpath, xpt_path_periph(ccb->ccb_h.path), 15828b8a9b1dSJustin T. Gibbs xpt_path_path_id(ccb->ccb_h.path), 15838b8a9b1dSJustin T. Gibbs xpt_path_target_id(ccb->ccb_h.path), 15848b8a9b1dSJustin T. Gibbs CAM_LUN_WILDCARD) != CAM_REQ_CMP) 15858b8a9b1dSJustin T. Gibbs break; 15868b8a9b1dSJustin T. Gibbs /* 15878b8a9b1dSJustin T. Gibbs * Let peripheral drivers know that this device has gone 15888b8a9b1dSJustin T. Gibbs * away. 15898b8a9b1dSJustin T. Gibbs */ 15908b8a9b1dSJustin T. Gibbs xpt_async(AC_LOST_DEVICE, newpath, NULL); 15918b8a9b1dSJustin T. Gibbs xpt_free_path(newpath); 1592e471e974SJustin T. Gibbs #endif 159350711c71SKenneth D. Merry if ((sense_flags & SF_RETRY_SELTO) != 0) { 159450711c71SKenneth D. Merry retry = ccb->ccb_h.retry_count > 0; 159550711c71SKenneth D. Merry if (retry) { 159650711c71SKenneth D. Merry ccb->ccb_h.retry_count--; 159750711c71SKenneth D. Merry error = ERESTART; 159850711c71SKenneth D. Merry /* 159950711c71SKenneth D. Merry * Wait half a second to give the device 160050711c71SKenneth D. Merry * time to recover before we try again. 160150711c71SKenneth D. Merry */ 160250711c71SKenneth D. Merry relsim_flags = RELSIM_RELEASE_AFTER_TIMEOUT; 160350711c71SKenneth D. Merry timeout = 500; 160450711c71SKenneth D. Merry } else { 1605e471e974SJustin T. Gibbs error = ENXIO; 160650711c71SKenneth D. Merry } 160750711c71SKenneth D. Merry } else { 160850711c71SKenneth D. Merry error = ENXIO; 160950711c71SKenneth D. Merry } 16108b8a9b1dSJustin T. Gibbs break; 16118b8a9b1dSJustin T. Gibbs } 16128b8a9b1dSJustin T. Gibbs case CAM_REQ_INVALID: 16138b8a9b1dSJustin T. Gibbs case CAM_PATH_INVALID: 16148b8a9b1dSJustin T. Gibbs case CAM_DEV_NOT_THERE: 16158b8a9b1dSJustin T. Gibbs case CAM_NO_HBA: 16168b8a9b1dSJustin T. Gibbs case CAM_PROVIDE_FAIL: 16178b8a9b1dSJustin T. Gibbs case CAM_REQ_TOO_BIG: 16188b8a9b1dSJustin T. Gibbs error = EINVAL; 16198b8a9b1dSJustin T. Gibbs break; 16208b8a9b1dSJustin T. Gibbs case CAM_SCSI_BUS_RESET: 16218b8a9b1dSJustin T. Gibbs case CAM_BDR_SENT: 16228b8a9b1dSJustin T. Gibbs case CAM_REQUEUE_REQ: 16238b8a9b1dSJustin T. Gibbs /* Unconditional requeue, dammit */ 16248b8a9b1dSJustin T. Gibbs error = ERESTART; 16258b8a9b1dSJustin T. Gibbs break; 16268b8a9b1dSJustin T. Gibbs case CAM_RESRC_UNAVAIL: 16278b8a9b1dSJustin T. Gibbs case CAM_BUSY: 16288b8a9b1dSJustin T. Gibbs /* timeout??? */ 16298b8a9b1dSJustin T. Gibbs default: 16308b8a9b1dSJustin T. Gibbs /* decrement the number of retries */ 16318b8a9b1dSJustin T. Gibbs retry = ccb->ccb_h.retry_count > 0; 16328b8a9b1dSJustin T. Gibbs if (retry) { 16338b8a9b1dSJustin T. Gibbs ccb->ccb_h.retry_count--; 16348b8a9b1dSJustin T. Gibbs error = ERESTART; 16358b8a9b1dSJustin T. Gibbs } else { 16368b8a9b1dSJustin T. Gibbs /* Check the sense codes */ 16378b8a9b1dSJustin T. Gibbs error = EIO; 16388b8a9b1dSJustin T. Gibbs } 16398b8a9b1dSJustin T. Gibbs break; 16408b8a9b1dSJustin T. Gibbs } 16418b8a9b1dSJustin T. Gibbs 16428b8a9b1dSJustin T. Gibbs /* Attempt a retry */ 16438b8a9b1dSJustin T. Gibbs if (error == ERESTART || error == 0) { 16448b8a9b1dSJustin T. Gibbs if (frozen != 0) 16458b8a9b1dSJustin T. Gibbs ccb->ccb_h.status &= ~CAM_DEV_QFRZN; 16468b8a9b1dSJustin T. Gibbs 16478b8a9b1dSJustin T. Gibbs if (error == ERESTART) 16488b8a9b1dSJustin T. Gibbs xpt_action(ccb); 16498b8a9b1dSJustin T. Gibbs 16508b8a9b1dSJustin T. Gibbs if (frozen != 0) { 16518b8a9b1dSJustin T. Gibbs cam_release_devq(ccb->ccb_h.path, 16528b8a9b1dSJustin T. Gibbs relsim_flags, 16538b8a9b1dSJustin T. Gibbs openings, 16548b8a9b1dSJustin T. Gibbs timeout, 16558b8a9b1dSJustin T. Gibbs /*getcount_only*/0); 16568b8a9b1dSJustin T. Gibbs } 16578b8a9b1dSJustin T. Gibbs } 16588b8a9b1dSJustin T. Gibbs 16598b8a9b1dSJustin T. Gibbs 16608b8a9b1dSJustin T. Gibbs return (error); 16618b8a9b1dSJustin T. Gibbs } 1662