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