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 660b7c27b9SPeter Wemm static int nperiph_drivers; 670b7c27b9SPeter Wemm struct periph_driver **periph_drivers; 680b7c27b9SPeter Wemm 690b7c27b9SPeter Wemm void 700b7c27b9SPeter Wemm periphdriver_register(void *data) 710b7c27b9SPeter Wemm { 720b7c27b9SPeter Wemm struct periph_driver **newdrivers, **old; 730b7c27b9SPeter Wemm int ndrivers; 740b7c27b9SPeter Wemm 750b7c27b9SPeter Wemm ndrivers = nperiph_drivers + 2; 760b7c27b9SPeter Wemm newdrivers = malloc(sizeof(*newdrivers) * ndrivers, M_TEMP, M_WAITOK); 770b7c27b9SPeter Wemm if (periph_drivers) 780b7c27b9SPeter Wemm bcopy(periph_drivers, newdrivers, 790b7c27b9SPeter Wemm sizeof(*newdrivers) * ndrivers); 800b7c27b9SPeter Wemm newdrivers[nperiph_drivers] = (struct periph_driver *)data; 810b7c27b9SPeter Wemm newdrivers[nperiph_drivers + 1] = NULL; 820b7c27b9SPeter Wemm old = periph_drivers; 830b7c27b9SPeter Wemm periph_drivers = newdrivers; 840b7c27b9SPeter Wemm if (old) 850b7c27b9SPeter Wemm free(old, M_TEMP); 860b7c27b9SPeter Wemm nperiph_drivers++; 870b7c27b9SPeter Wemm } 880b7c27b9SPeter Wemm 898b8a9b1dSJustin T. Gibbs cam_status 90ee9c90c7SKenneth D. Merry cam_periph_alloc(periph_ctor_t *periph_ctor, 91ee9c90c7SKenneth D. Merry periph_oninv_t *periph_oninvalidate, 92ee9c90c7SKenneth D. Merry periph_dtor_t *periph_dtor, periph_start_t *periph_start, 93ee9c90c7SKenneth D. Merry char *name, cam_periph_type type, struct cam_path *path, 94ee9c90c7SKenneth D. Merry ac_callback_t *ac_callback, ac_code code, void *arg) 958b8a9b1dSJustin T. Gibbs { 968b8a9b1dSJustin T. Gibbs struct periph_driver **p_drv; 978b8a9b1dSJustin T. Gibbs struct cam_periph *periph; 988b8a9b1dSJustin T. Gibbs struct cam_periph *cur_periph; 998b8a9b1dSJustin T. Gibbs path_id_t path_id; 1008b8a9b1dSJustin T. Gibbs target_id_t target_id; 1018b8a9b1dSJustin T. Gibbs lun_id_t lun_id; 1028b8a9b1dSJustin T. Gibbs cam_status status; 1038b8a9b1dSJustin T. Gibbs u_int init_level; 1048b8a9b1dSJustin T. Gibbs int s; 1058b8a9b1dSJustin T. Gibbs 1068b8a9b1dSJustin T. Gibbs init_level = 0; 1078b8a9b1dSJustin T. Gibbs /* 1088b8a9b1dSJustin T. Gibbs * Handle Hot-Plug scenarios. If there is already a peripheral 1098b8a9b1dSJustin T. Gibbs * of our type assigned to this path, we are likely waiting for 1108b8a9b1dSJustin T. Gibbs * final close on an old, invalidated, peripheral. If this is 1118b8a9b1dSJustin T. Gibbs * the case, queue up a deferred call to the peripheral's async 1128b8a9b1dSJustin T. Gibbs * handler. If it looks like a mistaken re-alloation, complain. 1138b8a9b1dSJustin T. Gibbs */ 1148b8a9b1dSJustin T. Gibbs if ((periph = cam_periph_find(path, name)) != NULL) { 1158b8a9b1dSJustin T. Gibbs 1168b8a9b1dSJustin T. Gibbs if ((periph->flags & CAM_PERIPH_INVALID) != 0 1178b8a9b1dSJustin T. Gibbs && (periph->flags & CAM_PERIPH_NEW_DEV_FOUND) == 0) { 1188b8a9b1dSJustin T. Gibbs periph->flags |= CAM_PERIPH_NEW_DEV_FOUND; 1198b8a9b1dSJustin T. Gibbs periph->deferred_callback = ac_callback; 1208b8a9b1dSJustin T. Gibbs periph->deferred_ac = code; 1218b8a9b1dSJustin T. Gibbs return (CAM_REQ_INPROG); 1228b8a9b1dSJustin T. Gibbs } else { 1238b8a9b1dSJustin T. Gibbs printf("cam_periph_alloc: attempt to re-allocate " 1248b8a9b1dSJustin T. Gibbs "valid device %s%d rejected\n", 1258b8a9b1dSJustin T. Gibbs periph->periph_name, periph->unit_number); 1268b8a9b1dSJustin T. Gibbs } 1278b8a9b1dSJustin T. Gibbs return (CAM_REQ_INVALID); 1288b8a9b1dSJustin T. Gibbs } 1298b8a9b1dSJustin T. Gibbs 1308b8a9b1dSJustin T. Gibbs periph = (struct cam_periph *)malloc(sizeof(*periph), M_DEVBUF, 1318b8a9b1dSJustin T. Gibbs M_NOWAIT); 1328b8a9b1dSJustin T. Gibbs 1338b8a9b1dSJustin T. Gibbs if (periph == NULL) 1348b8a9b1dSJustin T. Gibbs return (CAM_RESRC_UNAVAIL); 1358b8a9b1dSJustin T. Gibbs 1368b8a9b1dSJustin T. Gibbs init_level++; 1378b8a9b1dSJustin T. Gibbs 1380b7c27b9SPeter Wemm for (p_drv = periph_drivers; *p_drv != NULL; p_drv++) { 1398b8a9b1dSJustin T. Gibbs if (strcmp((*p_drv)->driver_name, name) == 0) 1408b8a9b1dSJustin T. Gibbs break; 1418b8a9b1dSJustin T. Gibbs } 1428b8a9b1dSJustin T. Gibbs 1438b8a9b1dSJustin T. Gibbs path_id = xpt_path_path_id(path); 1448b8a9b1dSJustin T. Gibbs target_id = xpt_path_target_id(path); 1458b8a9b1dSJustin T. Gibbs lun_id = xpt_path_lun_id(path); 1468b8a9b1dSJustin T. Gibbs bzero(periph, sizeof(*periph)); 1478b8a9b1dSJustin T. Gibbs cam_init_pinfo(&periph->pinfo); 1488b8a9b1dSJustin T. Gibbs periph->periph_start = periph_start; 1498b8a9b1dSJustin T. Gibbs periph->periph_dtor = periph_dtor; 150ee9c90c7SKenneth D. Merry periph->periph_oninval = periph_oninvalidate; 1518b8a9b1dSJustin T. Gibbs periph->type = type; 1528b8a9b1dSJustin T. Gibbs periph->periph_name = name; 1538b8a9b1dSJustin T. Gibbs periph->unit_number = camperiphunit(*p_drv, path_id, target_id, lun_id); 1548b8a9b1dSJustin T. Gibbs periph->immediate_priority = CAM_PRIORITY_NONE; 1558b8a9b1dSJustin T. Gibbs periph->refcount = 0; 1568b8a9b1dSJustin T. Gibbs SLIST_INIT(&periph->ccb_list); 1578b8a9b1dSJustin T. Gibbs status = xpt_create_path(&path, periph, path_id, target_id, lun_id); 1588b8a9b1dSJustin T. Gibbs if (status != CAM_REQ_CMP) 1598b8a9b1dSJustin T. Gibbs goto failure; 1608b8a9b1dSJustin T. Gibbs 1618b8a9b1dSJustin T. Gibbs periph->path = path; 1628b8a9b1dSJustin T. Gibbs init_level++; 1638b8a9b1dSJustin T. Gibbs 1648b8a9b1dSJustin T. Gibbs status = xpt_add_periph(periph); 1658b8a9b1dSJustin T. Gibbs 1668b8a9b1dSJustin T. Gibbs if (status != CAM_REQ_CMP) 1678b8a9b1dSJustin T. Gibbs goto failure; 1688b8a9b1dSJustin T. Gibbs 1698b8a9b1dSJustin T. Gibbs s = splsoftcam(); 1708b8a9b1dSJustin T. Gibbs cur_periph = TAILQ_FIRST(&(*p_drv)->units); 1718b8a9b1dSJustin T. Gibbs while (cur_periph != NULL 1728b8a9b1dSJustin T. Gibbs && cur_periph->unit_number < periph->unit_number) 1738b8a9b1dSJustin T. Gibbs cur_periph = TAILQ_NEXT(cur_periph, unit_links); 1748b8a9b1dSJustin T. Gibbs 1758b8a9b1dSJustin T. Gibbs if (cur_periph != NULL) 1768b8a9b1dSJustin T. Gibbs TAILQ_INSERT_BEFORE(cur_periph, periph, unit_links); 1778b8a9b1dSJustin T. Gibbs else { 1788b8a9b1dSJustin T. Gibbs TAILQ_INSERT_TAIL(&(*p_drv)->units, periph, unit_links); 1798b8a9b1dSJustin T. Gibbs (*p_drv)->generation++; 1808b8a9b1dSJustin T. Gibbs } 1818b8a9b1dSJustin T. Gibbs 1828b8a9b1dSJustin T. Gibbs splx(s); 1838b8a9b1dSJustin T. Gibbs 1848b8a9b1dSJustin T. Gibbs init_level++; 1858b8a9b1dSJustin T. Gibbs 1868b8a9b1dSJustin T. Gibbs status = periph_ctor(periph, arg); 1878b8a9b1dSJustin T. Gibbs 1888b8a9b1dSJustin T. Gibbs if (status == CAM_REQ_CMP) 1898b8a9b1dSJustin T. Gibbs init_level++; 1908b8a9b1dSJustin T. Gibbs 1918b8a9b1dSJustin T. Gibbs failure: 1928b8a9b1dSJustin T. Gibbs switch (init_level) { 1938b8a9b1dSJustin T. Gibbs case 4: 1948b8a9b1dSJustin T. Gibbs /* Initialized successfully */ 1958b8a9b1dSJustin T. Gibbs break; 1968b8a9b1dSJustin T. Gibbs case 3: 1978b8a9b1dSJustin T. Gibbs s = splsoftcam(); 1988b8a9b1dSJustin T. Gibbs TAILQ_REMOVE(&(*p_drv)->units, periph, unit_links); 1998b8a9b1dSJustin T. Gibbs splx(s); 2008b8a9b1dSJustin T. Gibbs xpt_remove_periph(periph); 2018b8a9b1dSJustin T. Gibbs case 2: 2028b8a9b1dSJustin T. Gibbs xpt_free_path(periph->path); 2038b8a9b1dSJustin T. Gibbs case 1: 2048b8a9b1dSJustin T. Gibbs free(periph, M_DEVBUF); 2058b8a9b1dSJustin T. Gibbs case 0: 2068b8a9b1dSJustin T. Gibbs /* No cleanup to perform. */ 2078b8a9b1dSJustin T. Gibbs break; 2088b8a9b1dSJustin T. Gibbs default: 2098b8a9b1dSJustin T. Gibbs panic("cam_periph_alloc: Unkown init level"); 2108b8a9b1dSJustin T. Gibbs } 2118b8a9b1dSJustin T. Gibbs return(status); 2128b8a9b1dSJustin T. Gibbs } 2138b8a9b1dSJustin T. Gibbs 2148b8a9b1dSJustin T. Gibbs /* 2158b8a9b1dSJustin T. Gibbs * Find a peripheral structure with the specified path, target, lun, 2168b8a9b1dSJustin T. Gibbs * and (optionally) type. If the name is NULL, this function will return 2178b8a9b1dSJustin T. Gibbs * the first peripheral driver that matches the specified path. 2188b8a9b1dSJustin T. Gibbs */ 2198b8a9b1dSJustin T. Gibbs struct cam_periph * 2208b8a9b1dSJustin T. Gibbs cam_periph_find(struct cam_path *path, char *name) 2218b8a9b1dSJustin T. Gibbs { 2228b8a9b1dSJustin T. Gibbs struct periph_driver **p_drv; 2238b8a9b1dSJustin T. Gibbs struct cam_periph *periph; 2248b8a9b1dSJustin T. Gibbs int s; 2258b8a9b1dSJustin T. Gibbs 2260b7c27b9SPeter Wemm for (p_drv = periph_drivers; *p_drv != NULL; p_drv++) { 2278b8a9b1dSJustin T. Gibbs 2288b8a9b1dSJustin T. Gibbs if (name != NULL && (strcmp((*p_drv)->driver_name, name) != 0)) 2298b8a9b1dSJustin T. Gibbs continue; 2308b8a9b1dSJustin T. Gibbs 2318b8a9b1dSJustin T. Gibbs s = splsoftcam(); 23237d40066SPoul-Henning Kamp TAILQ_FOREACH(periph, &(*p_drv)->units, unit_links) { 2338b8a9b1dSJustin T. Gibbs if (xpt_path_comp(periph->path, path) == 0) { 2348b8a9b1dSJustin T. Gibbs splx(s); 2358b8a9b1dSJustin T. Gibbs return(periph); 2368b8a9b1dSJustin T. Gibbs } 2378b8a9b1dSJustin T. Gibbs } 2388b8a9b1dSJustin T. Gibbs splx(s); 2398b8a9b1dSJustin T. Gibbs if (name != NULL) 2408b8a9b1dSJustin T. Gibbs return(NULL); 2418b8a9b1dSJustin T. Gibbs } 2428b8a9b1dSJustin T. Gibbs return(NULL); 2438b8a9b1dSJustin T. Gibbs } 2448b8a9b1dSJustin T. Gibbs 2458b8a9b1dSJustin T. Gibbs cam_status 2468b8a9b1dSJustin T. Gibbs cam_periph_acquire(struct cam_periph *periph) 2478b8a9b1dSJustin T. Gibbs { 2488b8a9b1dSJustin T. Gibbs int s; 2498b8a9b1dSJustin T. Gibbs 2508b8a9b1dSJustin T. Gibbs if (periph == NULL) 2518b8a9b1dSJustin T. Gibbs return(CAM_REQ_CMP_ERR); 2528b8a9b1dSJustin T. Gibbs 2538b8a9b1dSJustin T. Gibbs s = splsoftcam(); 2548b8a9b1dSJustin T. Gibbs periph->refcount++; 2558b8a9b1dSJustin T. Gibbs splx(s); 2568b8a9b1dSJustin T. Gibbs 2578b8a9b1dSJustin T. Gibbs return(CAM_REQ_CMP); 2588b8a9b1dSJustin T. Gibbs } 2598b8a9b1dSJustin T. Gibbs 2608b8a9b1dSJustin T. Gibbs void 2618b8a9b1dSJustin T. Gibbs cam_periph_release(struct cam_periph *periph) 2628b8a9b1dSJustin T. Gibbs { 2638b8a9b1dSJustin T. Gibbs int s; 2648b8a9b1dSJustin T. Gibbs 2658b8a9b1dSJustin T. Gibbs if (periph == NULL) 2668b8a9b1dSJustin T. Gibbs return; 2678b8a9b1dSJustin T. Gibbs 2688b8a9b1dSJustin T. Gibbs s = splsoftcam(); 2698b8a9b1dSJustin T. Gibbs if ((--periph->refcount == 0) 2708b8a9b1dSJustin T. Gibbs && (periph->flags & CAM_PERIPH_INVALID)) { 2718b8a9b1dSJustin T. Gibbs camperiphfree(periph); 2728b8a9b1dSJustin T. Gibbs } 2738b8a9b1dSJustin T. Gibbs splx(s); 2748b8a9b1dSJustin T. Gibbs 2758b8a9b1dSJustin T. Gibbs } 2768b8a9b1dSJustin T. Gibbs 2778b8a9b1dSJustin T. Gibbs /* 2788b8a9b1dSJustin T. Gibbs * Look for the next unit number that is not currently in use for this 2798b8a9b1dSJustin T. Gibbs * peripheral type starting at "newunit". Also exclude unit numbers that 2808b8a9b1dSJustin T. Gibbs * are reserved by for future "hardwiring" unless we already know that this 2818b8a9b1dSJustin T. Gibbs * is a potential wired device. Only assume that the device is "wired" the 2828b8a9b1dSJustin T. Gibbs * first time through the loop since after that we'll be looking at unit 2838b8a9b1dSJustin T. Gibbs * numbers that did not match a wiring entry. 2848b8a9b1dSJustin T. Gibbs */ 2858b8a9b1dSJustin T. Gibbs static u_int 286501468a5SKenneth D. Merry camperiphnextunit(struct periph_driver *p_drv, u_int newunit, int wired, 287501468a5SKenneth D. Merry path_id_t pathid, target_id_t target, lun_id_t lun) 2888b8a9b1dSJustin T. Gibbs { 2898b8a9b1dSJustin T. Gibbs struct cam_periph *periph; 29075f51904SPeter Wemm char *periph_name, *strval; 2918b8a9b1dSJustin T. Gibbs int s; 29275f51904SPeter Wemm int i, val, dunit; 29375f51904SPeter Wemm const char *dname; 2948b8a9b1dSJustin T. Gibbs 2958b8a9b1dSJustin T. Gibbs s = splsoftcam(); 2968b8a9b1dSJustin T. Gibbs periph_name = p_drv->driver_name; 2978b8a9b1dSJustin T. Gibbs for (;;newunit++) { 2988b8a9b1dSJustin T. Gibbs 2998b8a9b1dSJustin T. Gibbs for (periph = TAILQ_FIRST(&p_drv->units); 3008b8a9b1dSJustin T. Gibbs periph != NULL && periph->unit_number != newunit; 3018b8a9b1dSJustin T. Gibbs periph = TAILQ_NEXT(periph, unit_links)) 3028b8a9b1dSJustin T. Gibbs ; 3038b8a9b1dSJustin T. Gibbs 3048b8a9b1dSJustin T. Gibbs if (periph != NULL && periph->unit_number == newunit) { 3058b8a9b1dSJustin T. Gibbs if (wired != 0) { 3068b8a9b1dSJustin T. Gibbs xpt_print_path(periph->path); 3078b8a9b1dSJustin T. Gibbs printf("Duplicate Wired Device entry!\n"); 3088b8a9b1dSJustin T. Gibbs xpt_print_path(periph->path); 309501468a5SKenneth D. Merry printf("Second device (%s device at scbus%d " 310501468a5SKenneth D. Merry "target %d lun %d) will not be wired\n", 311501468a5SKenneth D. Merry periph_name, pathid, target, lun); 3128b8a9b1dSJustin T. Gibbs wired = 0; 3138b8a9b1dSJustin T. Gibbs } 3148b8a9b1dSJustin T. Gibbs continue; 3158b8a9b1dSJustin T. Gibbs } 31675f51904SPeter Wemm if (wired) 31775f51904SPeter Wemm break; 3188b8a9b1dSJustin T. Gibbs 3198b8a9b1dSJustin T. Gibbs /* 3208b8a9b1dSJustin T. Gibbs * Don't match entries like "da 4" as a wired down 3218b8a9b1dSJustin T. Gibbs * device, but do match entries like "da 4 target 5" 3228b8a9b1dSJustin T. Gibbs * or even "da 4 scbus 1". 3238b8a9b1dSJustin T. Gibbs */ 32475f51904SPeter Wemm i = -1; 32575f51904SPeter Wemm while ((i = resource_locate(i, periph_name)) != -1) { 32675f51904SPeter Wemm dname = resource_query_name(i); 32775f51904SPeter Wemm dunit = resource_query_unit(i); 32875f51904SPeter Wemm /* if no "target" and no specific scbus, skip */ 32975f51904SPeter Wemm if (resource_int_value(dname, dunit, "target", &val) && 33075f51904SPeter Wemm (resource_string_value(dname, dunit, "at",&strval)|| 33175f51904SPeter Wemm strcmp(strval, "scbus") == 0)) 33275f51904SPeter Wemm continue; 33375f51904SPeter Wemm if (newunit == dunit) 3348b8a9b1dSJustin T. Gibbs break; 3358b8a9b1dSJustin T. Gibbs } 33675f51904SPeter Wemm if (i == -1) 3378b8a9b1dSJustin T. Gibbs break; 3388b8a9b1dSJustin T. Gibbs } 3398b8a9b1dSJustin T. Gibbs splx(s); 3408b8a9b1dSJustin T. Gibbs return (newunit); 3418b8a9b1dSJustin T. Gibbs } 3428b8a9b1dSJustin T. Gibbs 3438b8a9b1dSJustin T. Gibbs static u_int 3448b8a9b1dSJustin T. Gibbs camperiphunit(struct periph_driver *p_drv, path_id_t pathid, 3458b8a9b1dSJustin T. Gibbs target_id_t target, lun_id_t lun) 3468b8a9b1dSJustin T. Gibbs { 3478b8a9b1dSJustin T. Gibbs u_int unit; 34875f51904SPeter Wemm int hit, i, val, dunit; 34975f51904SPeter Wemm const char *dname; 35075f51904SPeter Wemm char pathbuf[32], *strval, *periph_name; 3518b8a9b1dSJustin T. Gibbs 3528b8a9b1dSJustin T. Gibbs unit = 0; 3538b8a9b1dSJustin T. Gibbs hit = 0; 3548b8a9b1dSJustin T. Gibbs 35575f51904SPeter Wemm periph_name = p_drv->driver_name; 35675f51904SPeter Wemm snprintf(pathbuf, sizeof(pathbuf), "scbus%d", pathid); 35775f51904SPeter Wemm i = -1; 35875f51904SPeter Wemm while ((i = resource_locate(i, periph_name)) != -1) { 35975f51904SPeter Wemm dname = resource_query_name(i); 36075f51904SPeter Wemm dunit = resource_query_unit(i); 36175f51904SPeter Wemm if (resource_string_value(dname, dunit, "at", &strval) == 0) { 36275f51904SPeter Wemm if (strcmp(strval, pathbuf) != 0) 3638b8a9b1dSJustin T. Gibbs continue; 3648b8a9b1dSJustin T. Gibbs hit++; 3658b8a9b1dSJustin T. Gibbs } 36675f51904SPeter Wemm if (resource_int_value(dname, dunit, "target", &val) == 0) { 36775f51904SPeter Wemm if (val != target) 3688b8a9b1dSJustin T. Gibbs continue; 3698b8a9b1dSJustin T. Gibbs hit++; 3708b8a9b1dSJustin T. Gibbs } 37175f51904SPeter Wemm if (resource_int_value(dname, dunit, "lun", &val) == 0) { 37275f51904SPeter Wemm if (val != lun) 3738b8a9b1dSJustin T. Gibbs continue; 3748b8a9b1dSJustin T. Gibbs hit++; 3758b8a9b1dSJustin T. Gibbs } 3768b8a9b1dSJustin T. Gibbs if (hit != 0) { 37775f51904SPeter Wemm unit = dunit; 3788b8a9b1dSJustin T. Gibbs break; 3798b8a9b1dSJustin T. Gibbs } 3808b8a9b1dSJustin T. Gibbs } 3818b8a9b1dSJustin T. Gibbs 3828b8a9b1dSJustin T. Gibbs /* 3838b8a9b1dSJustin T. Gibbs * Either start from 0 looking for the next unit or from 38475f51904SPeter Wemm * the unit number given in the resource config. This way, 3858b8a9b1dSJustin T. Gibbs * if we have wildcard matches, we don't return the same 3868b8a9b1dSJustin T. Gibbs * unit number twice. 3878b8a9b1dSJustin T. Gibbs */ 388501468a5SKenneth D. Merry unit = camperiphnextunit(p_drv, unit, /*wired*/hit, pathid, 389501468a5SKenneth D. Merry target, lun); 3908b8a9b1dSJustin T. Gibbs 3918b8a9b1dSJustin T. Gibbs return (unit); 3928b8a9b1dSJustin T. Gibbs } 3938b8a9b1dSJustin T. Gibbs 3948b8a9b1dSJustin T. Gibbs void 3958b8a9b1dSJustin T. Gibbs cam_periph_invalidate(struct cam_periph *periph) 3968b8a9b1dSJustin T. Gibbs { 3978b8a9b1dSJustin T. Gibbs int s; 3988b8a9b1dSJustin T. Gibbs 399ee9c90c7SKenneth D. Merry s = splsoftcam(); 400ee9c90c7SKenneth D. Merry /* 401ee9c90c7SKenneth D. Merry * We only call this routine the first time a peripheral is 402ee9c90c7SKenneth D. Merry * invalidated. The oninvalidate() routine is always called at 403ee9c90c7SKenneth D. Merry * splsoftcam(). 404ee9c90c7SKenneth D. Merry */ 405ee9c90c7SKenneth D. Merry if (((periph->flags & CAM_PERIPH_INVALID) == 0) 406ee9c90c7SKenneth D. Merry && (periph->periph_oninval != NULL)) 407ee9c90c7SKenneth D. Merry periph->periph_oninval(periph); 408ee9c90c7SKenneth D. Merry 4098b8a9b1dSJustin T. Gibbs periph->flags |= CAM_PERIPH_INVALID; 4108b8a9b1dSJustin T. Gibbs periph->flags &= ~CAM_PERIPH_NEW_DEV_FOUND; 4118b8a9b1dSJustin T. Gibbs 4128b8a9b1dSJustin T. Gibbs if (periph->refcount == 0) 4138b8a9b1dSJustin T. Gibbs camperiphfree(periph); 4148b8a9b1dSJustin T. Gibbs else if (periph->refcount < 0) 4158b8a9b1dSJustin T. Gibbs printf("cam_invalidate_periph: refcount < 0!!\n"); 4168b8a9b1dSJustin T. Gibbs splx(s); 4178b8a9b1dSJustin T. Gibbs } 4188b8a9b1dSJustin T. Gibbs 4198b8a9b1dSJustin T. Gibbs static void 4208b8a9b1dSJustin T. Gibbs camperiphfree(struct cam_periph *periph) 4218b8a9b1dSJustin T. Gibbs { 4228b8a9b1dSJustin T. Gibbs int s; 4238b8a9b1dSJustin T. Gibbs struct periph_driver **p_drv; 4248b8a9b1dSJustin T. Gibbs 4250b7c27b9SPeter Wemm for (p_drv = periph_drivers; *p_drv != NULL; p_drv++) { 4268b8a9b1dSJustin T. Gibbs if (strcmp((*p_drv)->driver_name, periph->periph_name) == 0) 4278b8a9b1dSJustin T. Gibbs break; 4288b8a9b1dSJustin T. Gibbs } 4298b8a9b1dSJustin T. Gibbs 4308b8a9b1dSJustin T. Gibbs if (periph->periph_dtor != NULL) 4318b8a9b1dSJustin T. Gibbs periph->periph_dtor(periph); 4328b8a9b1dSJustin T. Gibbs 4338b8a9b1dSJustin T. Gibbs s = splsoftcam(); 4348b8a9b1dSJustin T. Gibbs TAILQ_REMOVE(&(*p_drv)->units, periph, unit_links); 4358b8a9b1dSJustin T. Gibbs (*p_drv)->generation++; 4368b8a9b1dSJustin T. Gibbs splx(s); 4378b8a9b1dSJustin T. Gibbs 4388b8a9b1dSJustin T. Gibbs xpt_remove_periph(periph); 4398b8a9b1dSJustin T. Gibbs 4408b8a9b1dSJustin T. Gibbs if (periph->flags & CAM_PERIPH_NEW_DEV_FOUND) { 4418b8a9b1dSJustin T. Gibbs union ccb ccb; 4428b8a9b1dSJustin T. Gibbs void *arg; 4438b8a9b1dSJustin T. Gibbs 4448b8a9b1dSJustin T. Gibbs switch (periph->deferred_ac) { 4458b8a9b1dSJustin T. Gibbs case AC_FOUND_DEVICE: 4468b8a9b1dSJustin T. Gibbs ccb.ccb_h.func_code = XPT_GDEV_TYPE; 4478b8a9b1dSJustin T. Gibbs xpt_setup_ccb(&ccb.ccb_h, periph->path, /*priority*/ 1); 4488b8a9b1dSJustin T. Gibbs xpt_action(&ccb); 4498b8a9b1dSJustin T. Gibbs arg = &ccb; 4508b8a9b1dSJustin T. Gibbs break; 4518b8a9b1dSJustin T. Gibbs case AC_PATH_REGISTERED: 4528b8a9b1dSJustin T. Gibbs ccb.ccb_h.func_code = XPT_PATH_INQ; 4538b8a9b1dSJustin T. Gibbs xpt_setup_ccb(&ccb.ccb_h, periph->path, /*priority*/ 1); 4548b8a9b1dSJustin T. Gibbs xpt_action(&ccb); 4558b8a9b1dSJustin T. Gibbs arg = &ccb; 4568b8a9b1dSJustin T. Gibbs break; 4578b8a9b1dSJustin T. Gibbs default: 4588b8a9b1dSJustin T. Gibbs arg = NULL; 4598b8a9b1dSJustin T. Gibbs break; 4608b8a9b1dSJustin T. Gibbs } 4618b8a9b1dSJustin T. Gibbs periph->deferred_callback(NULL, periph->deferred_ac, 4628b8a9b1dSJustin T. Gibbs periph->path, arg); 4638b8a9b1dSJustin T. Gibbs } 4648b8a9b1dSJustin T. Gibbs xpt_free_path(periph->path); 4658b8a9b1dSJustin T. Gibbs free(periph, M_DEVBUF); 4668b8a9b1dSJustin T. Gibbs } 4678b8a9b1dSJustin T. Gibbs 4688b8a9b1dSJustin T. Gibbs /* 4698b8a9b1dSJustin T. Gibbs * Wait interruptibly for an exclusive lock. 4708b8a9b1dSJustin T. Gibbs */ 4718b8a9b1dSJustin T. Gibbs int 4728b8a9b1dSJustin T. Gibbs cam_periph_lock(struct cam_periph *periph, int priority) 4738b8a9b1dSJustin T. Gibbs { 4748b8a9b1dSJustin T. Gibbs int error; 4758b8a9b1dSJustin T. Gibbs 4768b8a9b1dSJustin T. Gibbs while ((periph->flags & CAM_PERIPH_LOCKED) != 0) { 4778b8a9b1dSJustin T. Gibbs periph->flags |= CAM_PERIPH_LOCK_WANTED; 4788b8a9b1dSJustin T. Gibbs if ((error = tsleep(periph, priority, "caplck", 0)) != 0) 4798b8a9b1dSJustin T. Gibbs return error; 4808b8a9b1dSJustin T. Gibbs } 4818b8a9b1dSJustin T. Gibbs 4828b8a9b1dSJustin T. Gibbs if (cam_periph_acquire(periph) != CAM_REQ_CMP) 4838b8a9b1dSJustin T. Gibbs return(ENXIO); 4848b8a9b1dSJustin T. Gibbs 4858b8a9b1dSJustin T. Gibbs periph->flags |= CAM_PERIPH_LOCKED; 4868b8a9b1dSJustin T. Gibbs return 0; 4878b8a9b1dSJustin T. Gibbs } 4888b8a9b1dSJustin T. Gibbs 4898b8a9b1dSJustin T. Gibbs /* 4908b8a9b1dSJustin T. Gibbs * Unlock and wake up any waiters. 4918b8a9b1dSJustin T. Gibbs */ 4928b8a9b1dSJustin T. Gibbs void 4938b8a9b1dSJustin T. Gibbs cam_periph_unlock(struct cam_periph *periph) 4948b8a9b1dSJustin T. Gibbs { 4958b8a9b1dSJustin T. Gibbs periph->flags &= ~CAM_PERIPH_LOCKED; 4968b8a9b1dSJustin T. Gibbs if ((periph->flags & CAM_PERIPH_LOCK_WANTED) != 0) { 4978b8a9b1dSJustin T. Gibbs periph->flags &= ~CAM_PERIPH_LOCK_WANTED; 4988b8a9b1dSJustin T. Gibbs wakeup(periph); 4998b8a9b1dSJustin T. Gibbs } 5008b8a9b1dSJustin T. Gibbs 5018b8a9b1dSJustin T. Gibbs cam_periph_release(periph); 5028b8a9b1dSJustin T. Gibbs } 5038b8a9b1dSJustin T. Gibbs 5048b8a9b1dSJustin T. Gibbs /* 5058b8a9b1dSJustin T. Gibbs * Map user virtual pointers into kernel virtual address space, so we can 5068b8a9b1dSJustin T. Gibbs * access the memory. This won't work on physical pointers, for now it's 5078b8a9b1dSJustin T. Gibbs * up to the caller to check for that. (XXX KDM -- should we do that here 5088b8a9b1dSJustin T. Gibbs * instead?) This also only works for up to MAXPHYS memory. Since we use 5098b8a9b1dSJustin T. Gibbs * buffers to map stuff in and out, we're limited to the buffer size. 5108b8a9b1dSJustin T. Gibbs */ 5118b8a9b1dSJustin T. Gibbs int 5128b8a9b1dSJustin T. Gibbs cam_periph_mapmem(union ccb *ccb, struct cam_periph_map_info *mapinfo) 5138b8a9b1dSJustin T. Gibbs { 51479d49a06SKenneth D. Merry int numbufs, i; 51579d49a06SKenneth D. Merry int flags[CAM_PERIPH_MAXMAPS]; 5168b8a9b1dSJustin T. Gibbs u_int8_t **data_ptrs[CAM_PERIPH_MAXMAPS]; 5178b8a9b1dSJustin T. Gibbs u_int32_t lengths[CAM_PERIPH_MAXMAPS]; 5188b8a9b1dSJustin T. Gibbs u_int32_t dirs[CAM_PERIPH_MAXMAPS]; 5198b8a9b1dSJustin T. Gibbs 5208b8a9b1dSJustin T. Gibbs switch(ccb->ccb_h.func_code) { 5218b8a9b1dSJustin T. Gibbs case XPT_DEV_MATCH: 5228b8a9b1dSJustin T. Gibbs if (ccb->cdm.match_buf_len == 0) { 5238b8a9b1dSJustin T. Gibbs printf("cam_periph_mapmem: invalid match buffer " 5248b8a9b1dSJustin T. Gibbs "length 0\n"); 5258b8a9b1dSJustin T. Gibbs return(EINVAL); 5268b8a9b1dSJustin T. Gibbs } 5278b8a9b1dSJustin T. Gibbs if (ccb->cdm.pattern_buf_len > 0) { 5288b8a9b1dSJustin T. Gibbs data_ptrs[0] = (u_int8_t **)&ccb->cdm.patterns; 5298b8a9b1dSJustin T. Gibbs lengths[0] = ccb->cdm.pattern_buf_len; 5308b8a9b1dSJustin T. Gibbs dirs[0] = CAM_DIR_OUT; 5318b8a9b1dSJustin T. Gibbs data_ptrs[1] = (u_int8_t **)&ccb->cdm.matches; 5328b8a9b1dSJustin T. Gibbs lengths[1] = ccb->cdm.match_buf_len; 5338b8a9b1dSJustin T. Gibbs dirs[1] = CAM_DIR_IN; 5348b8a9b1dSJustin T. Gibbs numbufs = 2; 5358b8a9b1dSJustin T. Gibbs } else { 5368b8a9b1dSJustin T. Gibbs data_ptrs[0] = (u_int8_t **)&ccb->cdm.matches; 5378b8a9b1dSJustin T. Gibbs lengths[0] = ccb->cdm.match_buf_len; 5388b8a9b1dSJustin T. Gibbs dirs[0] = CAM_DIR_IN; 5398b8a9b1dSJustin T. Gibbs numbufs = 1; 5408b8a9b1dSJustin T. Gibbs } 5418b8a9b1dSJustin T. Gibbs break; 5428b8a9b1dSJustin T. Gibbs case XPT_SCSI_IO: 54387cfaf0eSJustin T. Gibbs case XPT_CONT_TARGET_IO: 5448b8a9b1dSJustin T. Gibbs if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_NONE) 5458b8a9b1dSJustin T. Gibbs return(0); 5468b8a9b1dSJustin T. Gibbs 5478b8a9b1dSJustin T. Gibbs data_ptrs[0] = &ccb->csio.data_ptr; 54879d49a06SKenneth D. Merry lengths[0] = ccb->csio.dxfer_len; 5498b8a9b1dSJustin T. Gibbs dirs[0] = ccb->ccb_h.flags & CAM_DIR_MASK; 5508b8a9b1dSJustin T. Gibbs numbufs = 1; 5518b8a9b1dSJustin T. Gibbs break; 5528b8a9b1dSJustin T. Gibbs default: 5538b8a9b1dSJustin T. Gibbs return(EINVAL); 5548b8a9b1dSJustin T. Gibbs break; /* NOTREACHED */ 5558b8a9b1dSJustin T. Gibbs } 5568b8a9b1dSJustin T. Gibbs 5578b8a9b1dSJustin T. Gibbs /* 55879d49a06SKenneth D. Merry * Check the transfer length and permissions first, so we don't 55979d49a06SKenneth D. Merry * have to unmap any previously mapped buffers. 5608b8a9b1dSJustin T. Gibbs */ 5618b8a9b1dSJustin T. Gibbs for (i = 0; i < numbufs; i++) { 56279d49a06SKenneth D. Merry 56379d49a06SKenneth D. Merry flags[i] = 0; 56479d49a06SKenneth D. Merry 56579d49a06SKenneth D. Merry /* 56679d49a06SKenneth D. Merry * The userland data pointer passed in may not be page 56779d49a06SKenneth D. Merry * aligned. vmapbuf() truncates the address to a page 56879d49a06SKenneth D. Merry * boundary, so if the address isn't page aligned, we'll 56979d49a06SKenneth D. Merry * need enough space for the given transfer length, plus 57079d49a06SKenneth D. Merry * whatever extra space is necessary to make it to the page 57179d49a06SKenneth D. Merry * boundary. 57279d49a06SKenneth D. Merry */ 57379d49a06SKenneth D. Merry if ((lengths[i] + 574ff1fe75fSKenneth D. Merry (((vm_offset_t)(*data_ptrs[i])) & PAGE_MASK)) > DFLTPHYS){ 5756d7b539aSPeter Wemm printf("cam_periph_mapmem: attempt to map %lu bytes, " 576ff1fe75fSKenneth D. Merry "which is greater than DFLTPHYS(%d)\n", 5776d7b539aSPeter Wemm (long)(lengths[i] + 5786d7b539aSPeter Wemm (((vm_offset_t)(*data_ptrs[i])) & PAGE_MASK)), 579ff1fe75fSKenneth D. Merry DFLTPHYS); 58079d49a06SKenneth D. Merry return(E2BIG); 58179d49a06SKenneth D. Merry } 5828b8a9b1dSJustin T. Gibbs 583edd24ab7SKenneth D. Merry if (dirs[i] & CAM_DIR_OUT) { 58421144e3bSPoul-Henning Kamp flags[i] = BIO_WRITE; 58502c58685SPoul-Henning Kamp if (!useracc(*data_ptrs[i], lengths[i], 58602c58685SPoul-Henning Kamp VM_PROT_READ)) { 5878b8a9b1dSJustin T. Gibbs printf("cam_periph_mapmem: error, " 5882e8bf209SBruce Evans "address %p, length %lu isn't " 5898b8a9b1dSJustin T. Gibbs "user accessible for READ\n", 5902e8bf209SBruce Evans (void *)*data_ptrs[i], 5912e8bf209SBruce Evans (u_long)lengths[i]); 5928b8a9b1dSJustin T. Gibbs return(EACCES); 5938b8a9b1dSJustin T. Gibbs } 5948b8a9b1dSJustin T. Gibbs } 5958b8a9b1dSJustin T. Gibbs 596edd24ab7SKenneth D. Merry if (dirs[i] & CAM_DIR_IN) { 59721144e3bSPoul-Henning Kamp flags[i] = BIO_READ; 59802c58685SPoul-Henning Kamp if (!useracc(*data_ptrs[i], lengths[i], 59902c58685SPoul-Henning Kamp VM_PROT_WRITE)) { 6008b8a9b1dSJustin T. Gibbs printf("cam_periph_mapmem: error, " 6012e8bf209SBruce Evans "address %p, length %lu isn't " 6028b8a9b1dSJustin T. Gibbs "user accessible for WRITE\n", 6032e8bf209SBruce Evans (void *)*data_ptrs[i], 6042e8bf209SBruce Evans (u_long)lengths[i]); 6058b8a9b1dSJustin T. Gibbs 6068b8a9b1dSJustin T. Gibbs return(EACCES); 6078b8a9b1dSJustin T. Gibbs } 6088b8a9b1dSJustin T. Gibbs } 6098b8a9b1dSJustin T. Gibbs 61079d49a06SKenneth D. Merry } 61179d49a06SKenneth D. Merry 61279d49a06SKenneth D. Merry /* this keeps the current process from getting swapped */ 61379d49a06SKenneth D. Merry /* 61479d49a06SKenneth D. Merry * XXX KDM should I use P_NOSWAP instead? 61579d49a06SKenneth D. Merry */ 6160cbbb7bfSPeter Wemm PHOLD(curproc); 61779d49a06SKenneth D. Merry 61879d49a06SKenneth D. Merry for (i = 0; i < numbufs; i++) { 6198b8a9b1dSJustin T. Gibbs /* 6208b8a9b1dSJustin T. Gibbs * Get the buffer. 6218b8a9b1dSJustin T. Gibbs */ 6221c7c3c6aSMatthew Dillon mapinfo->bp[i] = getpbuf(NULL); 6238b8a9b1dSJustin T. Gibbs 6248b8a9b1dSJustin T. Gibbs /* save the buffer's data address */ 6258b8a9b1dSJustin T. Gibbs mapinfo->bp[i]->b_saveaddr = mapinfo->bp[i]->b_data; 6268b8a9b1dSJustin T. Gibbs 6278b8a9b1dSJustin T. Gibbs /* put our pointer in the data slot */ 6288b8a9b1dSJustin T. Gibbs mapinfo->bp[i]->b_data = *data_ptrs[i]; 6298b8a9b1dSJustin T. Gibbs 630ff1fe75fSKenneth D. Merry /* set the transfer length, we know it's < DFLTPHYS */ 6318b8a9b1dSJustin T. Gibbs mapinfo->bp[i]->b_bufsize = lengths[i]; 6328b8a9b1dSJustin T. Gibbs 6338b8a9b1dSJustin T. Gibbs /* set the flags */ 63421144e3bSPoul-Henning Kamp mapinfo->bp[i]->b_flags = B_PHYS; 63521144e3bSPoul-Henning Kamp 63621144e3bSPoul-Henning Kamp /* set the direction */ 63721144e3bSPoul-Henning Kamp mapinfo->bp[i]->b_iocmd = flags[i]; 6388b8a9b1dSJustin T. Gibbs 6398b8a9b1dSJustin T. Gibbs /* map the buffer into kernel memory */ 6408b8a9b1dSJustin T. Gibbs vmapbuf(mapinfo->bp[i]); 6418b8a9b1dSJustin T. Gibbs 6428b8a9b1dSJustin T. Gibbs /* set our pointer to the new mapped area */ 6438b8a9b1dSJustin T. Gibbs *data_ptrs[i] = mapinfo->bp[i]->b_data; 6448b8a9b1dSJustin T. Gibbs 6458b8a9b1dSJustin T. Gibbs mapinfo->num_bufs_used++; 6468b8a9b1dSJustin T. Gibbs } 6478b8a9b1dSJustin T. Gibbs 6488b8a9b1dSJustin T. Gibbs return(0); 6498b8a9b1dSJustin T. Gibbs } 6508b8a9b1dSJustin T. Gibbs 6518b8a9b1dSJustin T. Gibbs /* 6528b8a9b1dSJustin T. Gibbs * Unmap memory segments mapped into kernel virtual address space by 6538b8a9b1dSJustin T. Gibbs * cam_periph_mapmem(). 6548b8a9b1dSJustin T. Gibbs */ 6558b8a9b1dSJustin T. Gibbs void 6568b8a9b1dSJustin T. Gibbs cam_periph_unmapmem(union ccb *ccb, struct cam_periph_map_info *mapinfo) 6578b8a9b1dSJustin T. Gibbs { 6588b8a9b1dSJustin T. Gibbs int numbufs, i; 6598b8a9b1dSJustin T. Gibbs u_int8_t **data_ptrs[CAM_PERIPH_MAXMAPS]; 6608b8a9b1dSJustin T. Gibbs 6618b8a9b1dSJustin T. Gibbs if (mapinfo->num_bufs_used <= 0) { 6628b8a9b1dSJustin T. Gibbs /* allow ourselves to be swapped once again */ 6630cbbb7bfSPeter Wemm PRELE(curproc); 6648b8a9b1dSJustin T. Gibbs return; 6658b8a9b1dSJustin T. Gibbs } 6668b8a9b1dSJustin T. Gibbs 6678b8a9b1dSJustin T. Gibbs switch (ccb->ccb_h.func_code) { 6688b8a9b1dSJustin T. Gibbs case XPT_DEV_MATCH: 6698b8a9b1dSJustin T. Gibbs numbufs = min(mapinfo->num_bufs_used, 2); 6708b8a9b1dSJustin T. Gibbs 6718b8a9b1dSJustin T. Gibbs if (numbufs == 1) { 6728b8a9b1dSJustin T. Gibbs data_ptrs[0] = (u_int8_t **)&ccb->cdm.matches; 6738b8a9b1dSJustin T. Gibbs } else { 6748b8a9b1dSJustin T. Gibbs data_ptrs[0] = (u_int8_t **)&ccb->cdm.patterns; 6758b8a9b1dSJustin T. Gibbs data_ptrs[1] = (u_int8_t **)&ccb->cdm.matches; 6768b8a9b1dSJustin T. Gibbs } 6778b8a9b1dSJustin T. Gibbs break; 6788b8a9b1dSJustin T. Gibbs case XPT_SCSI_IO: 6799911ecf9SJustin T. Gibbs case XPT_CONT_TARGET_IO: 6808b8a9b1dSJustin T. Gibbs data_ptrs[0] = &ccb->csio.data_ptr; 6818b8a9b1dSJustin T. Gibbs numbufs = min(mapinfo->num_bufs_used, 1); 6828b8a9b1dSJustin T. Gibbs break; 6838b8a9b1dSJustin T. Gibbs default: 6848b8a9b1dSJustin T. Gibbs /* allow ourselves to be swapped once again */ 6850cbbb7bfSPeter Wemm PRELE(curproc); 6868b8a9b1dSJustin T. Gibbs return; 6878b8a9b1dSJustin T. Gibbs break; /* NOTREACHED */ 6888b8a9b1dSJustin T. Gibbs } 6898b8a9b1dSJustin T. Gibbs 6908b8a9b1dSJustin T. Gibbs for (i = 0; i < numbufs; i++) { 6918b8a9b1dSJustin T. Gibbs /* Set the user's pointer back to the original value */ 6928b8a9b1dSJustin T. Gibbs *data_ptrs[i] = mapinfo->bp[i]->b_saveaddr; 6938b8a9b1dSJustin T. Gibbs 6948b8a9b1dSJustin T. Gibbs /* unmap the buffer */ 6958b8a9b1dSJustin T. Gibbs vunmapbuf(mapinfo->bp[i]); 6968b8a9b1dSJustin T. Gibbs 6978b8a9b1dSJustin T. Gibbs /* clear the flags we set above */ 69867812eacSKirk McKusick mapinfo->bp[i]->b_flags &= ~B_PHYS; 6998b8a9b1dSJustin T. Gibbs 7008b8a9b1dSJustin T. Gibbs /* release the buffer */ 7011c7c3c6aSMatthew Dillon relpbuf(mapinfo->bp[i], NULL); 7028b8a9b1dSJustin T. Gibbs } 7038b8a9b1dSJustin T. Gibbs 7048b8a9b1dSJustin T. Gibbs /* allow ourselves to be swapped once again */ 7050cbbb7bfSPeter Wemm PRELE(curproc); 7068b8a9b1dSJustin T. Gibbs } 7078b8a9b1dSJustin T. Gibbs 7088b8a9b1dSJustin T. Gibbs union ccb * 7098b8a9b1dSJustin T. Gibbs cam_periph_getccb(struct cam_periph *periph, u_int32_t priority) 7108b8a9b1dSJustin T. Gibbs { 7118b8a9b1dSJustin T. Gibbs struct ccb_hdr *ccb_h; 7128b8a9b1dSJustin T. Gibbs int s; 7138b8a9b1dSJustin T. Gibbs 7148b8a9b1dSJustin T. Gibbs CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering cdgetccb\n")); 7158b8a9b1dSJustin T. Gibbs 7168b8a9b1dSJustin T. Gibbs s = splsoftcam(); 7178b8a9b1dSJustin T. Gibbs 718fc2ffbe6SPoul-Henning Kamp while (SLIST_FIRST(&periph->ccb_list) == NULL) { 7198b8a9b1dSJustin T. Gibbs if (periph->immediate_priority > priority) 7208b8a9b1dSJustin T. Gibbs periph->immediate_priority = priority; 7218b8a9b1dSJustin T. Gibbs xpt_schedule(periph, priority); 722fc2ffbe6SPoul-Henning Kamp if ((SLIST_FIRST(&periph->ccb_list) != NULL) 723fc2ffbe6SPoul-Henning Kamp && (SLIST_FIRST(&periph->ccb_list)->pinfo.priority == priority)) 7248b8a9b1dSJustin T. Gibbs break; 7258b8a9b1dSJustin T. Gibbs tsleep(&periph->ccb_list, PRIBIO, "cgticb", 0); 7268b8a9b1dSJustin T. Gibbs } 7278b8a9b1dSJustin T. Gibbs 728fc2ffbe6SPoul-Henning Kamp ccb_h = SLIST_FIRST(&periph->ccb_list); 7298b8a9b1dSJustin T. Gibbs SLIST_REMOVE_HEAD(&periph->ccb_list, periph_links.sle); 7308b8a9b1dSJustin T. Gibbs splx(s); 7318b8a9b1dSJustin T. Gibbs return ((union ccb *)ccb_h); 7328b8a9b1dSJustin T. Gibbs } 7338b8a9b1dSJustin T. Gibbs 7348b8a9b1dSJustin T. Gibbs void 7358b8a9b1dSJustin T. Gibbs cam_periph_ccbwait(union ccb *ccb) 7368b8a9b1dSJustin T. Gibbs { 7378b8a9b1dSJustin T. Gibbs int s; 7388b8a9b1dSJustin T. Gibbs 7398b8a9b1dSJustin T. Gibbs s = splsoftcam(); 7408b8a9b1dSJustin T. Gibbs if ((ccb->ccb_h.pinfo.index != CAM_UNQUEUED_INDEX) 7418b8a9b1dSJustin T. Gibbs || ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_INPROG)) 7428b8a9b1dSJustin T. Gibbs tsleep(&ccb->ccb_h.cbfcnp, PRIBIO, "cbwait", 0); 7438b8a9b1dSJustin T. Gibbs 7448b8a9b1dSJustin T. Gibbs splx(s); 7458b8a9b1dSJustin T. Gibbs } 7468b8a9b1dSJustin T. Gibbs 7478b8a9b1dSJustin T. Gibbs int 7488b8a9b1dSJustin T. Gibbs cam_periph_ioctl(struct cam_periph *periph, int cmd, caddr_t addr, 7498b8a9b1dSJustin T. Gibbs int (*error_routine)(union ccb *ccb, 7508b8a9b1dSJustin T. Gibbs cam_flags camflags, 7518b8a9b1dSJustin T. Gibbs u_int32_t sense_flags)) 7528b8a9b1dSJustin T. Gibbs { 7538b8a9b1dSJustin T. Gibbs union ccb *ccb; 7548b8a9b1dSJustin T. Gibbs int error; 7558b8a9b1dSJustin T. Gibbs int found; 7568b8a9b1dSJustin T. Gibbs 7578b8a9b1dSJustin T. Gibbs error = found = 0; 7588b8a9b1dSJustin T. Gibbs 7598b8a9b1dSJustin T. Gibbs switch(cmd){ 7608b8a9b1dSJustin T. Gibbs case CAMGETPASSTHRU: 7618b8a9b1dSJustin T. Gibbs ccb = cam_periph_getccb(periph, /* priority */ 1); 7628b8a9b1dSJustin T. Gibbs xpt_setup_ccb(&ccb->ccb_h, 7638b8a9b1dSJustin T. Gibbs ccb->ccb_h.path, 7648b8a9b1dSJustin T. Gibbs /*priority*/1); 7658b8a9b1dSJustin T. Gibbs ccb->ccb_h.func_code = XPT_GDEVLIST; 7668b8a9b1dSJustin T. Gibbs 7678b8a9b1dSJustin T. Gibbs /* 7688b8a9b1dSJustin T. Gibbs * Basically, the point of this is that we go through 7698b8a9b1dSJustin T. Gibbs * getting the list of devices, until we find a passthrough 7708b8a9b1dSJustin T. Gibbs * device. In the current version of the CAM code, the 7718b8a9b1dSJustin T. Gibbs * only way to determine what type of device we're dealing 7728b8a9b1dSJustin T. Gibbs * with is by its name. 7738b8a9b1dSJustin T. Gibbs */ 7748b8a9b1dSJustin T. Gibbs while (found == 0) { 7758b8a9b1dSJustin T. Gibbs ccb->cgdl.index = 0; 7768b8a9b1dSJustin T. Gibbs ccb->cgdl.status = CAM_GDEVLIST_MORE_DEVS; 7778b8a9b1dSJustin T. Gibbs while (ccb->cgdl.status == CAM_GDEVLIST_MORE_DEVS) { 7788b8a9b1dSJustin T. Gibbs 7798b8a9b1dSJustin T. Gibbs /* we want the next device in the list */ 7808b8a9b1dSJustin T. Gibbs xpt_action(ccb); 7818b8a9b1dSJustin T. Gibbs if (strncmp(ccb->cgdl.periph_name, 7828b8a9b1dSJustin T. Gibbs "pass", 4) == 0){ 7838b8a9b1dSJustin T. Gibbs found = 1; 7848b8a9b1dSJustin T. Gibbs break; 7858b8a9b1dSJustin T. Gibbs } 7868b8a9b1dSJustin T. Gibbs } 7878b8a9b1dSJustin T. Gibbs if ((ccb->cgdl.status == CAM_GDEVLIST_LAST_DEVICE) && 7888b8a9b1dSJustin T. Gibbs (found == 0)) { 7898b8a9b1dSJustin T. Gibbs ccb->cgdl.periph_name[0] = '\0'; 7908b8a9b1dSJustin T. Gibbs ccb->cgdl.unit_number = 0; 7918b8a9b1dSJustin T. Gibbs break; 7928b8a9b1dSJustin T. Gibbs } 7938b8a9b1dSJustin T. Gibbs } 7948b8a9b1dSJustin T. Gibbs 7958b8a9b1dSJustin T. Gibbs /* copy the result back out */ 7968b8a9b1dSJustin T. Gibbs bcopy(ccb, addr, sizeof(union ccb)); 7978b8a9b1dSJustin T. Gibbs 7988b8a9b1dSJustin T. Gibbs /* and release the ccb */ 7998b8a9b1dSJustin T. Gibbs xpt_release_ccb(ccb); 8008b8a9b1dSJustin T. Gibbs 8018b8a9b1dSJustin T. Gibbs break; 8028b8a9b1dSJustin T. Gibbs default: 8038b8a9b1dSJustin T. Gibbs error = ENOTTY; 8048b8a9b1dSJustin T. Gibbs break; 8058b8a9b1dSJustin T. Gibbs } 8068b8a9b1dSJustin T. Gibbs return(error); 8078b8a9b1dSJustin T. Gibbs } 8088b8a9b1dSJustin T. Gibbs 8098b8a9b1dSJustin T. Gibbs int 8108b8a9b1dSJustin T. Gibbs cam_periph_runccb(union ccb *ccb, 8118b8a9b1dSJustin T. Gibbs int (*error_routine)(union ccb *ccb, 8128b8a9b1dSJustin T. Gibbs cam_flags camflags, 8138b8a9b1dSJustin T. Gibbs u_int32_t sense_flags), 8148b8a9b1dSJustin T. Gibbs cam_flags camflags, u_int32_t sense_flags, 8158b8a9b1dSJustin T. Gibbs struct devstat *ds) 8168b8a9b1dSJustin T. Gibbs { 8178b8a9b1dSJustin T. Gibbs int error; 8188b8a9b1dSJustin T. Gibbs 8198b8a9b1dSJustin T. Gibbs error = 0; 8208b8a9b1dSJustin T. Gibbs 8218b8a9b1dSJustin T. Gibbs /* 8228b8a9b1dSJustin T. Gibbs * If the user has supplied a stats structure, and if we understand 8238b8a9b1dSJustin T. Gibbs * this particular type of ccb, record the transaction start. 8248b8a9b1dSJustin T. Gibbs */ 8258b8a9b1dSJustin T. Gibbs if ((ds != NULL) && (ccb->ccb_h.func_code == XPT_SCSI_IO)) 8268b8a9b1dSJustin T. Gibbs devstat_start_transaction(ds); 8278b8a9b1dSJustin T. Gibbs 8288b8a9b1dSJustin T. Gibbs xpt_action(ccb); 8298b8a9b1dSJustin T. Gibbs 8308b8a9b1dSJustin T. Gibbs do { 8318b8a9b1dSJustin T. Gibbs cam_periph_ccbwait(ccb); 8328b8a9b1dSJustin T. Gibbs if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) 8338b8a9b1dSJustin T. Gibbs error = 0; 8348b8a9b1dSJustin T. Gibbs else if (error_routine != NULL) 8358b8a9b1dSJustin T. Gibbs error = (*error_routine)(ccb, camflags, sense_flags); 8368b8a9b1dSJustin T. Gibbs else 8378b8a9b1dSJustin T. Gibbs error = 0; 8388b8a9b1dSJustin T. Gibbs 8398b8a9b1dSJustin T. Gibbs } while (error == ERESTART); 8408b8a9b1dSJustin T. Gibbs 8418b8a9b1dSJustin T. Gibbs if ((ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) 8428b8a9b1dSJustin T. Gibbs cam_release_devq(ccb->ccb_h.path, 8438b8a9b1dSJustin T. Gibbs /* relsim_flags */0, 8448b8a9b1dSJustin T. Gibbs /* openings */0, 8458b8a9b1dSJustin T. Gibbs /* timeout */0, 8468b8a9b1dSJustin T. Gibbs /* getcount_only */ FALSE); 8478b8a9b1dSJustin T. Gibbs 8488b8a9b1dSJustin T. Gibbs if ((ds != NULL) && (ccb->ccb_h.func_code == XPT_SCSI_IO)) 8498b8a9b1dSJustin T. Gibbs devstat_end_transaction(ds, 8508b8a9b1dSJustin T. Gibbs ccb->csio.dxfer_len, 8518b8a9b1dSJustin T. Gibbs ccb->csio.tag_action & 0xf, 8528b8a9b1dSJustin T. Gibbs ((ccb->ccb_h.flags & CAM_DIR_MASK) == 8538b8a9b1dSJustin T. Gibbs CAM_DIR_NONE) ? DEVSTAT_NO_DATA : 8548b8a9b1dSJustin T. Gibbs (ccb->ccb_h.flags & CAM_DIR_OUT) ? 8558b8a9b1dSJustin T. Gibbs DEVSTAT_WRITE : 8568b8a9b1dSJustin T. Gibbs DEVSTAT_READ); 8578b8a9b1dSJustin T. Gibbs 8588b8a9b1dSJustin T. Gibbs return(error); 8598b8a9b1dSJustin T. Gibbs } 8608b8a9b1dSJustin T. Gibbs 86187cfaf0eSJustin T. Gibbs void 86287cfaf0eSJustin T. Gibbs cam_freeze_devq(struct cam_path *path) 86387cfaf0eSJustin T. Gibbs { 86487cfaf0eSJustin T. Gibbs struct ccb_hdr ccb_h; 86587cfaf0eSJustin T. Gibbs 86687cfaf0eSJustin T. Gibbs xpt_setup_ccb(&ccb_h, path, /*priority*/1); 86787cfaf0eSJustin T. Gibbs ccb_h.func_code = XPT_NOOP; 86887cfaf0eSJustin T. Gibbs ccb_h.flags = CAM_DEV_QFREEZE; 86987cfaf0eSJustin T. Gibbs xpt_action((union ccb *)&ccb_h); 87087cfaf0eSJustin T. Gibbs } 87187cfaf0eSJustin T. Gibbs 8728b8a9b1dSJustin T. Gibbs u_int32_t 8738b8a9b1dSJustin T. Gibbs cam_release_devq(struct cam_path *path, u_int32_t relsim_flags, 8748b8a9b1dSJustin T. Gibbs u_int32_t openings, u_int32_t timeout, 8758b8a9b1dSJustin T. Gibbs int getcount_only) 8768b8a9b1dSJustin T. Gibbs { 8778b8a9b1dSJustin T. Gibbs struct ccb_relsim crs; 8788b8a9b1dSJustin T. Gibbs 8798b8a9b1dSJustin T. Gibbs xpt_setup_ccb(&crs.ccb_h, path, 8808b8a9b1dSJustin T. Gibbs /*priority*/1); 8818b8a9b1dSJustin T. Gibbs crs.ccb_h.func_code = XPT_REL_SIMQ; 8828b8a9b1dSJustin T. Gibbs crs.ccb_h.flags = getcount_only ? CAM_DEV_QFREEZE : 0; 8838b8a9b1dSJustin T. Gibbs crs.release_flags = relsim_flags; 8848b8a9b1dSJustin T. Gibbs crs.openings = openings; 8858b8a9b1dSJustin T. Gibbs crs.release_timeout = timeout; 8868b8a9b1dSJustin T. Gibbs xpt_action((union ccb *)&crs); 8878b8a9b1dSJustin T. Gibbs return (crs.qfrozen_cnt); 8888b8a9b1dSJustin T. Gibbs } 8898b8a9b1dSJustin T. Gibbs 8908b8a9b1dSJustin T. Gibbs #define saved_ccb_ptr ppriv_ptr0 8918b8a9b1dSJustin T. Gibbs static void 8928b8a9b1dSJustin T. Gibbs camperiphdone(struct cam_periph *periph, union ccb *done_ccb) 8938b8a9b1dSJustin T. Gibbs { 8948b8a9b1dSJustin T. Gibbs cam_status status; 8958b8a9b1dSJustin T. Gibbs int frozen; 8968b8a9b1dSJustin T. Gibbs int sense; 8978b8a9b1dSJustin T. Gibbs struct scsi_start_stop_unit *scsi_cmd; 8988b8a9b1dSJustin T. Gibbs u_int32_t relsim_flags, timeout; 8998b8a9b1dSJustin T. Gibbs u_int32_t qfrozen_cnt; 9008b8a9b1dSJustin T. Gibbs 9018b8a9b1dSJustin T. Gibbs status = done_ccb->ccb_h.status; 9028b8a9b1dSJustin T. Gibbs frozen = (status & CAM_DEV_QFRZN) != 0; 9038b8a9b1dSJustin T. Gibbs sense = (status & CAM_AUTOSNS_VALID) != 0; 9048b8a9b1dSJustin T. Gibbs status &= CAM_STATUS_MASK; 9058b8a9b1dSJustin T. Gibbs 9068b8a9b1dSJustin T. Gibbs timeout = 0; 9078b8a9b1dSJustin T. Gibbs relsim_flags = 0; 9088b8a9b1dSJustin T. Gibbs 9098b8a9b1dSJustin T. Gibbs /* 9108b8a9b1dSJustin T. Gibbs * Unfreeze the queue once if it is already frozen.. 9118b8a9b1dSJustin T. Gibbs */ 9128b8a9b1dSJustin T. Gibbs if (frozen != 0) { 9138b8a9b1dSJustin T. Gibbs qfrozen_cnt = cam_release_devq(done_ccb->ccb_h.path, 9148b8a9b1dSJustin T. Gibbs /*relsim_flags*/0, 9158b8a9b1dSJustin T. Gibbs /*openings*/0, 9168b8a9b1dSJustin T. Gibbs /*timeout*/0, 9178b8a9b1dSJustin T. Gibbs /*getcount_only*/0); 9188b8a9b1dSJustin T. Gibbs } 9198b8a9b1dSJustin T. Gibbs 9208b8a9b1dSJustin T. Gibbs switch (status) { 9218b8a9b1dSJustin T. Gibbs 9228b8a9b1dSJustin T. Gibbs case CAM_REQ_CMP: 9238b8a9b1dSJustin T. Gibbs 9248b8a9b1dSJustin T. Gibbs /* 9258b8a9b1dSJustin T. Gibbs * If we have successfully taken a device from the not 9268b8a9b1dSJustin T. Gibbs * ready to ready state, re-scan the device and re-get the 9278b8a9b1dSJustin T. Gibbs * inquiry information. Many devices (mostly disks) don't 9288b8a9b1dSJustin T. Gibbs * properly report their inquiry information unless they 9298b8a9b1dSJustin T. Gibbs * are spun up. 9308b8a9b1dSJustin T. Gibbs */ 9318b8a9b1dSJustin T. Gibbs if (done_ccb->ccb_h.func_code == XPT_SCSI_IO) { 9328b8a9b1dSJustin T. Gibbs scsi_cmd = (struct scsi_start_stop_unit *) 9338b8a9b1dSJustin T. Gibbs &done_ccb->csio.cdb_io.cdb_bytes; 9348b8a9b1dSJustin T. Gibbs 9358b8a9b1dSJustin T. Gibbs if (scsi_cmd->opcode == START_STOP_UNIT) 9368b8a9b1dSJustin T. Gibbs xpt_async(AC_INQ_CHANGED, 9378b8a9b1dSJustin T. Gibbs done_ccb->ccb_h.path, NULL); 9388b8a9b1dSJustin T. Gibbs } 9398b8a9b1dSJustin T. Gibbs bcopy(done_ccb->ccb_h.saved_ccb_ptr, done_ccb, 9408b8a9b1dSJustin T. Gibbs sizeof(union ccb)); 9418b8a9b1dSJustin T. Gibbs 94260a899a0SKenneth D. Merry periph->flags &= ~CAM_PERIPH_RECOVERY_INPROG; 94360a899a0SKenneth D. Merry 9448b8a9b1dSJustin T. Gibbs xpt_action(done_ccb); 9458b8a9b1dSJustin T. Gibbs 9468b8a9b1dSJustin T. Gibbs break; 9478b8a9b1dSJustin T. Gibbs case CAM_SCSI_STATUS_ERROR: 9488b8a9b1dSJustin T. Gibbs scsi_cmd = (struct scsi_start_stop_unit *) 9498b8a9b1dSJustin T. Gibbs &done_ccb->csio.cdb_io.cdb_bytes; 9508b8a9b1dSJustin T. Gibbs if (sense != 0) { 9518b8a9b1dSJustin T. Gibbs struct scsi_sense_data *sense; 9528b8a9b1dSJustin T. Gibbs int error_code, sense_key, asc, ascq; 9538b8a9b1dSJustin T. Gibbs 9548b8a9b1dSJustin T. Gibbs sense = &done_ccb->csio.sense_data; 9558b8a9b1dSJustin T. Gibbs scsi_extract_sense(sense, &error_code, 9568b8a9b1dSJustin T. Gibbs &sense_key, &asc, &ascq); 9578b8a9b1dSJustin T. Gibbs 9588b8a9b1dSJustin T. Gibbs /* 9598b8a9b1dSJustin T. Gibbs * If the error is "invalid field in CDB", 9608b8a9b1dSJustin T. Gibbs * and the load/eject flag is set, turn the 9618b8a9b1dSJustin T. Gibbs * flag off and try again. This is just in 9628b8a9b1dSJustin T. Gibbs * case the drive in question barfs on the 9638b8a9b1dSJustin T. Gibbs * load eject flag. The CAM code should set 9648b8a9b1dSJustin T. Gibbs * the load/eject flag by default for 9658b8a9b1dSJustin T. Gibbs * removable media. 9668b8a9b1dSJustin T. Gibbs */ 9678b8a9b1dSJustin T. Gibbs 9688b8a9b1dSJustin T. Gibbs /* XXX KDM 9698b8a9b1dSJustin T. Gibbs * Should we check to see what the specific 9708b8a9b1dSJustin T. Gibbs * scsi status is?? Or does it not matter 9718b8a9b1dSJustin T. Gibbs * since we already know that there was an 9728b8a9b1dSJustin T. Gibbs * error, and we know what the specific 9738b8a9b1dSJustin T. Gibbs * error code was, and we know what the 9748b8a9b1dSJustin T. Gibbs * opcode is.. 9758b8a9b1dSJustin T. Gibbs */ 9768b8a9b1dSJustin T. Gibbs if ((scsi_cmd->opcode == START_STOP_UNIT) && 9778b8a9b1dSJustin T. Gibbs ((scsi_cmd->how & SSS_LOEJ) != 0) && 9788b8a9b1dSJustin T. Gibbs (asc == 0x24) && (ascq == 0x00) && 9798b8a9b1dSJustin T. Gibbs (done_ccb->ccb_h.retry_count > 0)) { 9808b8a9b1dSJustin T. Gibbs 9818b8a9b1dSJustin T. Gibbs scsi_cmd->how &= ~SSS_LOEJ; 9828b8a9b1dSJustin T. Gibbs 9838b8a9b1dSJustin T. Gibbs xpt_action(done_ccb); 9848b8a9b1dSJustin T. Gibbs 9858b8a9b1dSJustin T. Gibbs } else if (done_ccb->ccb_h.retry_count > 0) { 9868b8a9b1dSJustin T. Gibbs /* 9878b8a9b1dSJustin T. Gibbs * In this case, the error recovery 9888b8a9b1dSJustin T. Gibbs * command failed, but we've got 9898b8a9b1dSJustin T. Gibbs * some retries left on it. Give 9908b8a9b1dSJustin T. Gibbs * it another try. 9918b8a9b1dSJustin T. Gibbs */ 9928b8a9b1dSJustin T. Gibbs 9938b8a9b1dSJustin T. Gibbs /* set the timeout to .5 sec */ 9948b8a9b1dSJustin T. Gibbs relsim_flags = 9958b8a9b1dSJustin T. Gibbs RELSIM_RELEASE_AFTER_TIMEOUT; 9968b8a9b1dSJustin T. Gibbs timeout = 500; 9978b8a9b1dSJustin T. Gibbs 9988b8a9b1dSJustin T. Gibbs xpt_action(done_ccb); 9998b8a9b1dSJustin T. Gibbs 10008b8a9b1dSJustin T. Gibbs break; 10018b8a9b1dSJustin T. Gibbs 10028b8a9b1dSJustin T. Gibbs } else { 10038b8a9b1dSJustin T. Gibbs /* 10048b8a9b1dSJustin T. Gibbs * Copy the original CCB back and 10058b8a9b1dSJustin T. Gibbs * send it back to the caller. 10068b8a9b1dSJustin T. Gibbs */ 10078b8a9b1dSJustin T. Gibbs bcopy(done_ccb->ccb_h.saved_ccb_ptr, 10088b8a9b1dSJustin T. Gibbs done_ccb, sizeof(union ccb)); 10098b8a9b1dSJustin T. Gibbs 101060a899a0SKenneth D. Merry periph->flags &= ~CAM_PERIPH_RECOVERY_INPROG; 101160a899a0SKenneth D. Merry 10128b8a9b1dSJustin T. Gibbs xpt_action(done_ccb); 10138b8a9b1dSJustin T. Gibbs } 10148b8a9b1dSJustin T. Gibbs } else { 10158b8a9b1dSJustin T. Gibbs /* 10168b8a9b1dSJustin T. Gibbs * Eh?? The command failed, but we don't 10178b8a9b1dSJustin T. Gibbs * have any sense. What's up with that? 10188b8a9b1dSJustin T. Gibbs * Fire the CCB again to return it to the 10198b8a9b1dSJustin T. Gibbs * caller. 10208b8a9b1dSJustin T. Gibbs */ 10218b8a9b1dSJustin T. Gibbs bcopy(done_ccb->ccb_h.saved_ccb_ptr, 10228b8a9b1dSJustin T. Gibbs done_ccb, sizeof(union ccb)); 10238b8a9b1dSJustin T. Gibbs 102460a899a0SKenneth D. Merry periph->flags &= ~CAM_PERIPH_RECOVERY_INPROG; 102560a899a0SKenneth D. Merry 10268b8a9b1dSJustin T. Gibbs xpt_action(done_ccb); 10278b8a9b1dSJustin T. Gibbs 10288b8a9b1dSJustin T. Gibbs } 10298b8a9b1dSJustin T. Gibbs break; 10308b8a9b1dSJustin T. Gibbs default: 10318b8a9b1dSJustin T. Gibbs bcopy(done_ccb->ccb_h.saved_ccb_ptr, done_ccb, 10328b8a9b1dSJustin T. Gibbs sizeof(union ccb)); 10338b8a9b1dSJustin T. Gibbs 103460a899a0SKenneth D. Merry periph->flags &= ~CAM_PERIPH_RECOVERY_INPROG; 103560a899a0SKenneth D. Merry 10368b8a9b1dSJustin T. Gibbs xpt_action(done_ccb); 10378b8a9b1dSJustin T. Gibbs 10388b8a9b1dSJustin T. Gibbs break; 10398b8a9b1dSJustin T. Gibbs } 10408b8a9b1dSJustin T. Gibbs 10418b8a9b1dSJustin T. Gibbs /* decrement the retry count */ 10428b8a9b1dSJustin T. Gibbs if (done_ccb->ccb_h.retry_count > 0) 10438b8a9b1dSJustin T. Gibbs done_ccb->ccb_h.retry_count--; 10448b8a9b1dSJustin T. Gibbs 10458b8a9b1dSJustin T. Gibbs qfrozen_cnt = cam_release_devq(done_ccb->ccb_h.path, 10468b8a9b1dSJustin T. Gibbs /*relsim_flags*/relsim_flags, 10478b8a9b1dSJustin T. Gibbs /*openings*/0, 10488b8a9b1dSJustin T. Gibbs /*timeout*/timeout, 10498b8a9b1dSJustin T. Gibbs /*getcount_only*/0); 10508b8a9b1dSJustin T. Gibbs } 10518b8a9b1dSJustin T. Gibbs 10528b8a9b1dSJustin T. Gibbs /* 105387cfaf0eSJustin T. Gibbs * Generic Async Event handler. Peripheral drivers usually 105487cfaf0eSJustin T. Gibbs * filter out the events that require personal attention, 105587cfaf0eSJustin T. Gibbs * and leave the rest to this function. 105687cfaf0eSJustin T. Gibbs */ 105787cfaf0eSJustin T. Gibbs void 105887cfaf0eSJustin T. Gibbs cam_periph_async(struct cam_periph *periph, u_int32_t code, 105987cfaf0eSJustin T. Gibbs struct cam_path *path, void *arg) 106087cfaf0eSJustin T. Gibbs { 106187cfaf0eSJustin T. Gibbs switch (code) { 106287cfaf0eSJustin T. Gibbs case AC_LOST_DEVICE: 106387cfaf0eSJustin T. Gibbs cam_periph_invalidate(periph); 106487cfaf0eSJustin T. Gibbs break; 106587cfaf0eSJustin T. Gibbs case AC_SENT_BDR: 106687cfaf0eSJustin T. Gibbs case AC_BUS_RESET: 106787cfaf0eSJustin T. Gibbs { 106887cfaf0eSJustin T. Gibbs cam_periph_bus_settle(periph, SCSI_DELAY); 106987cfaf0eSJustin T. Gibbs break; 107087cfaf0eSJustin T. Gibbs } 107187cfaf0eSJustin T. Gibbs default: 107287cfaf0eSJustin T. Gibbs break; 107387cfaf0eSJustin T. Gibbs } 107487cfaf0eSJustin T. Gibbs } 107587cfaf0eSJustin T. Gibbs 107687cfaf0eSJustin T. Gibbs void 107787cfaf0eSJustin T. Gibbs cam_periph_bus_settle(struct cam_periph *periph, u_int bus_settle) 107887cfaf0eSJustin T. Gibbs { 107987cfaf0eSJustin T. Gibbs struct ccb_getdevstats cgds; 108087cfaf0eSJustin T. Gibbs 108187cfaf0eSJustin T. Gibbs xpt_setup_ccb(&cgds.ccb_h, periph->path, /*priority*/1); 108287cfaf0eSJustin T. Gibbs cgds.ccb_h.func_code = XPT_GDEV_STATS; 108387cfaf0eSJustin T. Gibbs xpt_action((union ccb *)&cgds); 108487cfaf0eSJustin T. Gibbs cam_periph_freeze_after_event(periph, &cgds.last_reset, bus_settle); 108587cfaf0eSJustin T. Gibbs } 108687cfaf0eSJustin T. Gibbs 108787cfaf0eSJustin T. Gibbs void 108887cfaf0eSJustin T. Gibbs cam_periph_freeze_after_event(struct cam_periph *periph, 108987cfaf0eSJustin T. Gibbs struct timeval* event_time, u_int duration_ms) 109087cfaf0eSJustin T. Gibbs { 109187cfaf0eSJustin T. Gibbs struct timeval delta; 109287cfaf0eSJustin T. Gibbs struct timeval duration_tv; 109387cfaf0eSJustin T. Gibbs int s; 109487cfaf0eSJustin T. Gibbs 109587cfaf0eSJustin T. Gibbs s = splclock(); 109687cfaf0eSJustin T. Gibbs microtime(&delta); 109787cfaf0eSJustin T. Gibbs splx(s); 109887cfaf0eSJustin T. Gibbs timevalsub(&delta, event_time); 109987cfaf0eSJustin T. Gibbs duration_tv.tv_sec = duration_ms / 1000; 110087cfaf0eSJustin T. Gibbs duration_tv.tv_usec = (duration_ms % 1000) * 1000; 110187cfaf0eSJustin T. Gibbs if (timevalcmp(&delta, &duration_tv, <)) { 110287cfaf0eSJustin T. Gibbs timevalsub(&duration_tv, &delta); 110387cfaf0eSJustin T. Gibbs 110487cfaf0eSJustin T. Gibbs duration_ms = duration_tv.tv_sec * 1000; 110587cfaf0eSJustin T. Gibbs duration_ms += duration_tv.tv_usec / 1000; 110687cfaf0eSJustin T. Gibbs cam_freeze_devq(periph->path); 110787cfaf0eSJustin T. Gibbs cam_release_devq(periph->path, 110887cfaf0eSJustin T. Gibbs RELSIM_RELEASE_AFTER_TIMEOUT, 110987cfaf0eSJustin T. Gibbs /*reduction*/0, 111087cfaf0eSJustin T. Gibbs /*timeout*/duration_ms, 111187cfaf0eSJustin T. Gibbs /*getcount_only*/0); 111287cfaf0eSJustin T. Gibbs } 111387cfaf0eSJustin T. Gibbs 111487cfaf0eSJustin T. Gibbs } 111587cfaf0eSJustin T. Gibbs 111687cfaf0eSJustin T. Gibbs /* 11178b8a9b1dSJustin T. Gibbs * Generic error handler. Peripheral drivers usually filter 11188b8a9b1dSJustin T. Gibbs * out the errors that they handle in a unique mannor, then 11198b8a9b1dSJustin T. Gibbs * call this function. 11208b8a9b1dSJustin T. Gibbs */ 11218b8a9b1dSJustin T. Gibbs int 11228b8a9b1dSJustin T. Gibbs cam_periph_error(union ccb *ccb, cam_flags camflags, 11238b8a9b1dSJustin T. Gibbs u_int32_t sense_flags, union ccb *save_ccb) 11248b8a9b1dSJustin T. Gibbs { 11258b8a9b1dSJustin T. Gibbs cam_status status; 11268b8a9b1dSJustin T. Gibbs int frozen; 11278b8a9b1dSJustin T. Gibbs int sense; 11288b8a9b1dSJustin T. Gibbs int error; 11298b8a9b1dSJustin T. Gibbs int openings; 11308b8a9b1dSJustin T. Gibbs int retry; 11318b8a9b1dSJustin T. Gibbs u_int32_t relsim_flags; 11328b8a9b1dSJustin T. Gibbs u_int32_t timeout; 11338b8a9b1dSJustin T. Gibbs 11348b8a9b1dSJustin T. Gibbs status = ccb->ccb_h.status; 11358b8a9b1dSJustin T. Gibbs frozen = (status & CAM_DEV_QFRZN) != 0; 11368b8a9b1dSJustin T. Gibbs sense = (status & CAM_AUTOSNS_VALID) != 0; 11378b8a9b1dSJustin T. Gibbs status &= CAM_STATUS_MASK; 11388b8a9b1dSJustin T. Gibbs relsim_flags = 0; 11398b8a9b1dSJustin T. Gibbs 11408b8a9b1dSJustin T. Gibbs switch (status) { 11418b8a9b1dSJustin T. Gibbs case CAM_REQ_CMP: 11428b8a9b1dSJustin T. Gibbs /* decrement the number of retries */ 11438b8a9b1dSJustin T. Gibbs retry = ccb->ccb_h.retry_count > 0; 11448b8a9b1dSJustin T. Gibbs if (retry) 11458b8a9b1dSJustin T. Gibbs ccb->ccb_h.retry_count--; 11468b8a9b1dSJustin T. Gibbs error = 0; 11478b8a9b1dSJustin T. Gibbs break; 11485a526431SJustin T. Gibbs case CAM_AUTOSENSE_FAIL: 11498b8a9b1dSJustin T. Gibbs case CAM_SCSI_STATUS_ERROR: 11508b8a9b1dSJustin T. Gibbs 11518b8a9b1dSJustin T. Gibbs switch (ccb->csio.scsi_status) { 11528b8a9b1dSJustin T. Gibbs case SCSI_STATUS_OK: 11538b8a9b1dSJustin T. Gibbs case SCSI_STATUS_COND_MET: 11548b8a9b1dSJustin T. Gibbs case SCSI_STATUS_INTERMED: 11558b8a9b1dSJustin T. Gibbs case SCSI_STATUS_INTERMED_COND_MET: 11568b8a9b1dSJustin T. Gibbs error = 0; 11578b8a9b1dSJustin T. Gibbs break; 11588b8a9b1dSJustin T. Gibbs case SCSI_STATUS_CMD_TERMINATED: 11598b8a9b1dSJustin T. Gibbs case SCSI_STATUS_CHECK_COND: 11608b8a9b1dSJustin T. Gibbs if (sense != 0) { 11618b8a9b1dSJustin T. Gibbs struct scsi_sense_data *sense; 11628b8a9b1dSJustin T. Gibbs int error_code, sense_key, asc, ascq; 11638b8a9b1dSJustin T. Gibbs struct cam_periph *periph; 11648b8a9b1dSJustin T. Gibbs scsi_sense_action err_action; 11658b8a9b1dSJustin T. Gibbs struct ccb_getdev cgd; 11668b8a9b1dSJustin T. Gibbs 11678b8a9b1dSJustin T. Gibbs sense = &ccb->csio.sense_data; 11688b8a9b1dSJustin T. Gibbs scsi_extract_sense(sense, &error_code, 11698b8a9b1dSJustin T. Gibbs &sense_key, &asc, &ascq); 11708b8a9b1dSJustin T. Gibbs periph = xpt_path_periph(ccb->ccb_h.path); 11718b8a9b1dSJustin T. Gibbs 11728b8a9b1dSJustin T. Gibbs /* 11738b8a9b1dSJustin T. Gibbs * Grab the inquiry data for this device. 11748b8a9b1dSJustin T. Gibbs */ 11758b8a9b1dSJustin T. Gibbs xpt_setup_ccb(&cgd.ccb_h, ccb->ccb_h.path, 11768b8a9b1dSJustin T. Gibbs /*priority*/ 1); 11778b8a9b1dSJustin T. Gibbs cgd.ccb_h.func_code = XPT_GDEV_TYPE; 11788b8a9b1dSJustin T. Gibbs xpt_action((union ccb *)&cgd); 11798b8a9b1dSJustin T. Gibbs 11808b8a9b1dSJustin T. Gibbs err_action = scsi_error_action(asc, ascq, 11818b8a9b1dSJustin T. Gibbs &cgd.inq_data); 11828b8a9b1dSJustin T. Gibbs 11838b8a9b1dSJustin T. Gibbs /* 11848b8a9b1dSJustin T. Gibbs * Send a Test Unit Ready to the device. 11858b8a9b1dSJustin T. Gibbs * If the 'many' flag is set, we send 120 11868b8a9b1dSJustin T. Gibbs * test unit ready commands, one every half 11878b8a9b1dSJustin T. Gibbs * second. Otherwise, we just send one TUR. 11888b8a9b1dSJustin T. Gibbs * We only want to do this if the retry 11898b8a9b1dSJustin T. Gibbs * count has not been exhausted. 11908b8a9b1dSJustin T. Gibbs */ 11918b8a9b1dSJustin T. Gibbs if (((err_action & SS_MASK) == SS_TUR) 11928b8a9b1dSJustin T. Gibbs && save_ccb != NULL 11938b8a9b1dSJustin T. Gibbs && ccb->ccb_h.retry_count > 0) { 11948b8a9b1dSJustin T. Gibbs 119560a899a0SKenneth D. Merry /* 119660a899a0SKenneth D. Merry * Since error recovery is already 119760a899a0SKenneth D. Merry * in progress, don't attempt to 119860a899a0SKenneth D. Merry * process this error. It is probably 119960a899a0SKenneth D. Merry * related to the error that caused 120060a899a0SKenneth D. Merry * the currently active error recovery 120160a899a0SKenneth D. Merry * action. Also, we only have 120260a899a0SKenneth D. Merry * space for one saved CCB, so if we 120360a899a0SKenneth D. Merry * had two concurrent error recovery 120460a899a0SKenneth D. Merry * actions, we would end up 120560a899a0SKenneth D. Merry * over-writing one error recovery 120660a899a0SKenneth D. Merry * CCB with another one. 120760a899a0SKenneth D. Merry */ 120860a899a0SKenneth D. Merry if (periph->flags & 120960a899a0SKenneth D. Merry CAM_PERIPH_RECOVERY_INPROG) { 121060a899a0SKenneth D. Merry error = ERESTART; 121160a899a0SKenneth D. Merry break; 121260a899a0SKenneth D. Merry } 121360a899a0SKenneth D. Merry 121460a899a0SKenneth D. Merry periph->flags |= 121560a899a0SKenneth D. Merry CAM_PERIPH_RECOVERY_INPROG; 121660a899a0SKenneth D. Merry 12178b8a9b1dSJustin T. Gibbs /* decrement the number of retries */ 12188b8a9b1dSJustin T. Gibbs if ((err_action & 12198b8a9b1dSJustin T. Gibbs SSQ_DECREMENT_COUNT) != 0) { 12208b8a9b1dSJustin T. Gibbs retry = 1; 12218b8a9b1dSJustin T. Gibbs ccb->ccb_h.retry_count--; 12228b8a9b1dSJustin T. Gibbs } 12238b8a9b1dSJustin T. Gibbs 12248b8a9b1dSJustin T. Gibbs bcopy(ccb, save_ccb, sizeof(*save_ccb)); 12258b8a9b1dSJustin T. Gibbs 12268b8a9b1dSJustin T. Gibbs /* 12278b8a9b1dSJustin T. Gibbs * We retry this one every half 12288b8a9b1dSJustin T. Gibbs * second for a minute. If the 12298b8a9b1dSJustin T. Gibbs * device hasn't become ready in a 12308b8a9b1dSJustin T. Gibbs * minute's time, it's unlikely to 12318b8a9b1dSJustin T. Gibbs * ever become ready. If the table 12328b8a9b1dSJustin T. Gibbs * doesn't specify SSQ_MANY, we can 12338b8a9b1dSJustin T. Gibbs * only try this once. Oh well. 12348b8a9b1dSJustin T. Gibbs */ 12358b8a9b1dSJustin T. Gibbs if ((err_action & SSQ_MANY) != 0) 12368b8a9b1dSJustin T. Gibbs scsi_test_unit_ready(&ccb->csio, 12378b8a9b1dSJustin T. Gibbs /*retries*/120, 12388b8a9b1dSJustin T. Gibbs camperiphdone, 12398b8a9b1dSJustin T. Gibbs MSG_SIMPLE_Q_TAG, 12408b8a9b1dSJustin T. Gibbs SSD_FULL_SIZE, 12418b8a9b1dSJustin T. Gibbs /*timeout*/5000); 12428b8a9b1dSJustin T. Gibbs else 12438b8a9b1dSJustin T. Gibbs scsi_test_unit_ready(&ccb->csio, 12448b8a9b1dSJustin T. Gibbs /*retries*/1, 12458b8a9b1dSJustin T. Gibbs camperiphdone, 12468b8a9b1dSJustin T. Gibbs MSG_SIMPLE_Q_TAG, 12478b8a9b1dSJustin T. Gibbs SSD_FULL_SIZE, 12488b8a9b1dSJustin T. Gibbs /*timeout*/5000); 12498b8a9b1dSJustin T. Gibbs 12508b8a9b1dSJustin T. Gibbs /* release the queue after .5 sec. */ 12518b8a9b1dSJustin T. Gibbs relsim_flags = 12528b8a9b1dSJustin T. Gibbs RELSIM_RELEASE_AFTER_TIMEOUT; 12538b8a9b1dSJustin T. Gibbs timeout = 500; 12548b8a9b1dSJustin T. Gibbs /* 12558b8a9b1dSJustin T. Gibbs * Drop the priority to 0 so that 12568b8a9b1dSJustin T. Gibbs * we are the first to execute. Also 12578b8a9b1dSJustin T. Gibbs * freeze the queue after this command 12588b8a9b1dSJustin T. Gibbs * is sent so that we can restore the 12598b8a9b1dSJustin T. Gibbs * old csio and have it queued in the 12608b8a9b1dSJustin T. Gibbs * proper order before we let normal 12618b8a9b1dSJustin T. Gibbs * transactions go to the drive. 12628b8a9b1dSJustin T. Gibbs */ 12638b8a9b1dSJustin T. Gibbs ccb->ccb_h.pinfo.priority = 0; 12648b8a9b1dSJustin T. Gibbs ccb->ccb_h.flags |= CAM_DEV_QFREEZE; 12658b8a9b1dSJustin T. Gibbs 12668b8a9b1dSJustin T. Gibbs /* 12678b8a9b1dSJustin T. Gibbs * Save a pointer to the original 12688b8a9b1dSJustin T. Gibbs * CCB in the new CCB. 12698b8a9b1dSJustin T. Gibbs */ 12708b8a9b1dSJustin T. Gibbs ccb->ccb_h.saved_ccb_ptr = save_ccb; 12718b8a9b1dSJustin T. Gibbs 12728b8a9b1dSJustin T. Gibbs error = ERESTART; 12738b8a9b1dSJustin T. Gibbs } 12748b8a9b1dSJustin T. Gibbs /* 12758b8a9b1dSJustin T. Gibbs * Send a start unit command to the device, 12768b8a9b1dSJustin T. Gibbs * and then retry the command. We only 12778b8a9b1dSJustin T. Gibbs * want to do this if the retry count has 12788b8a9b1dSJustin T. Gibbs * not been exhausted. If the user 12798b8a9b1dSJustin T. Gibbs * specified 0 retries, then we follow 12808b8a9b1dSJustin T. Gibbs * their request and do not retry. 12818b8a9b1dSJustin T. Gibbs */ 12828b8a9b1dSJustin T. Gibbs else if (((err_action & SS_MASK) == SS_START) 12838b8a9b1dSJustin T. Gibbs && save_ccb != NULL 12848b8a9b1dSJustin T. Gibbs && ccb->ccb_h.retry_count > 0) { 12858b8a9b1dSJustin T. Gibbs int le; 12868b8a9b1dSJustin T. Gibbs 128760a899a0SKenneth D. Merry /* 128860a899a0SKenneth D. Merry * Only one error recovery action 128960a899a0SKenneth D. Merry * at a time. See above. 129060a899a0SKenneth D. Merry */ 129160a899a0SKenneth D. Merry if (periph->flags & 129260a899a0SKenneth D. Merry CAM_PERIPH_RECOVERY_INPROG) { 129360a899a0SKenneth D. Merry error = ERESTART; 129460a899a0SKenneth D. Merry break; 129560a899a0SKenneth D. Merry } 129660a899a0SKenneth D. Merry 129760a899a0SKenneth D. Merry periph->flags |= 129860a899a0SKenneth D. Merry CAM_PERIPH_RECOVERY_INPROG; 129960a899a0SKenneth D. Merry 13008b8a9b1dSJustin T. Gibbs /* decrement the number of retries */ 13018b8a9b1dSJustin T. Gibbs retry = 1; 13028b8a9b1dSJustin T. Gibbs ccb->ccb_h.retry_count--; 13038b8a9b1dSJustin T. Gibbs 13048b8a9b1dSJustin T. Gibbs /* 13058b8a9b1dSJustin T. Gibbs * Check for removable media and 13068b8a9b1dSJustin T. Gibbs * set load/eject flag 13078b8a9b1dSJustin T. Gibbs * appropriately. 13088b8a9b1dSJustin T. Gibbs */ 13098b8a9b1dSJustin T. Gibbs if (SID_IS_REMOVABLE(&cgd.inq_data)) 13108b8a9b1dSJustin T. Gibbs le = TRUE; 13118b8a9b1dSJustin T. Gibbs else 13128b8a9b1dSJustin T. Gibbs le = FALSE; 13138b8a9b1dSJustin T. Gibbs 13148b8a9b1dSJustin T. Gibbs /* 13158b8a9b1dSJustin T. Gibbs * Attempt to start the drive up. 13168b8a9b1dSJustin T. Gibbs * 13178b8a9b1dSJustin T. Gibbs * Save the current ccb so it can 13188b8a9b1dSJustin T. Gibbs * be restored and retried once the 13198b8a9b1dSJustin T. Gibbs * drive is started up. 13208b8a9b1dSJustin T. Gibbs */ 13218b8a9b1dSJustin T. Gibbs bcopy(ccb, save_ccb, sizeof(*save_ccb)); 13228b8a9b1dSJustin T. Gibbs 13238b8a9b1dSJustin T. Gibbs scsi_start_stop(&ccb->csio, 13248b8a9b1dSJustin T. Gibbs /*retries*/1, 13258b8a9b1dSJustin T. Gibbs camperiphdone, 13268b8a9b1dSJustin T. Gibbs MSG_SIMPLE_Q_TAG, 13278b8a9b1dSJustin T. Gibbs /*start*/TRUE, 13288b8a9b1dSJustin T. Gibbs /*load/eject*/le, 13298b8a9b1dSJustin T. Gibbs /*immediate*/FALSE, 13308b8a9b1dSJustin T. Gibbs SSD_FULL_SIZE, 13318b8a9b1dSJustin T. Gibbs /*timeout*/50000); 13328b8a9b1dSJustin T. Gibbs /* 13338b8a9b1dSJustin T. Gibbs * Drop the priority to 0 so that 13348b8a9b1dSJustin T. Gibbs * we are the first to execute. Also 13358b8a9b1dSJustin T. Gibbs * freeze the queue after this command 13368b8a9b1dSJustin T. Gibbs * is sent so that we can restore the 13378b8a9b1dSJustin T. Gibbs * old csio and have it queued in the 13388b8a9b1dSJustin T. Gibbs * proper order before we let normal 13398b8a9b1dSJustin T. Gibbs * transactions go to the drive. 13408b8a9b1dSJustin T. Gibbs */ 13418b8a9b1dSJustin T. Gibbs ccb->ccb_h.pinfo.priority = 0; 13428b8a9b1dSJustin T. Gibbs ccb->ccb_h.flags |= CAM_DEV_QFREEZE; 13438b8a9b1dSJustin T. Gibbs 13448b8a9b1dSJustin T. Gibbs /* 13458b8a9b1dSJustin T. Gibbs * Save a pointer to the original 13468b8a9b1dSJustin T. Gibbs * CCB in the new CCB. 13478b8a9b1dSJustin T. Gibbs */ 13488b8a9b1dSJustin T. Gibbs ccb->ccb_h.saved_ccb_ptr = save_ccb; 13498b8a9b1dSJustin T. Gibbs 13508b8a9b1dSJustin T. Gibbs error = ERESTART; 13518b8a9b1dSJustin T. Gibbs } else if ((sense_flags & SF_RETRY_UA) != 0) { 13528b8a9b1dSJustin T. Gibbs /* 13538b8a9b1dSJustin T. Gibbs * XXX KDM this is a *horrible* 13548b8a9b1dSJustin T. Gibbs * hack. 13558b8a9b1dSJustin T. Gibbs */ 13568b8a9b1dSJustin T. Gibbs error = scsi_interpret_sense(ccb, 13578b8a9b1dSJustin T. Gibbs sense_flags, 13588b8a9b1dSJustin T. Gibbs &relsim_flags, 13598b8a9b1dSJustin T. Gibbs &openings, 13608b8a9b1dSJustin T. Gibbs &timeout, 13618b8a9b1dSJustin T. Gibbs err_action); 13628b8a9b1dSJustin T. Gibbs } 13638b8a9b1dSJustin T. Gibbs 13648b8a9b1dSJustin T. Gibbs /* 13658b8a9b1dSJustin T. Gibbs * Theoretically, this code should send a 13668b8a9b1dSJustin T. Gibbs * test unit ready to the given device, and 13678b8a9b1dSJustin T. Gibbs * if it returns and error, send a start 13688b8a9b1dSJustin T. Gibbs * unit command. Since we don't yet have 13698b8a9b1dSJustin T. Gibbs * the capability to do two-command error 13708b8a9b1dSJustin T. Gibbs * recovery, just send a start unit. 13718b8a9b1dSJustin T. Gibbs * XXX KDM fix this! 13728b8a9b1dSJustin T. Gibbs */ 13738b8a9b1dSJustin T. Gibbs else if (((err_action & SS_MASK) == SS_TURSTART) 13748b8a9b1dSJustin T. Gibbs && save_ccb != NULL 13758b8a9b1dSJustin T. Gibbs && ccb->ccb_h.retry_count > 0) { 13768b8a9b1dSJustin T. Gibbs int le; 13778b8a9b1dSJustin T. Gibbs 137860a899a0SKenneth D. Merry /* 137960a899a0SKenneth D. Merry * Only one error recovery action 138060a899a0SKenneth D. Merry * at a time. See above. 138160a899a0SKenneth D. Merry */ 138260a899a0SKenneth D. Merry if (periph->flags & 138360a899a0SKenneth D. Merry CAM_PERIPH_RECOVERY_INPROG) { 138460a899a0SKenneth D. Merry error = ERESTART; 138560a899a0SKenneth D. Merry break; 138660a899a0SKenneth D. Merry } 138760a899a0SKenneth D. Merry 138860a899a0SKenneth D. Merry periph->flags |= 138960a899a0SKenneth D. Merry CAM_PERIPH_RECOVERY_INPROG; 139060a899a0SKenneth D. Merry 13918b8a9b1dSJustin T. Gibbs /* decrement the number of retries */ 13928b8a9b1dSJustin T. Gibbs retry = 1; 13938b8a9b1dSJustin T. Gibbs ccb->ccb_h.retry_count--; 13948b8a9b1dSJustin T. Gibbs 13958b8a9b1dSJustin T. Gibbs /* 13968b8a9b1dSJustin T. Gibbs * Check for removable media and 13978b8a9b1dSJustin T. Gibbs * set load/eject flag 13988b8a9b1dSJustin T. Gibbs * appropriately. 13998b8a9b1dSJustin T. Gibbs */ 14008b8a9b1dSJustin T. Gibbs if (SID_IS_REMOVABLE(&cgd.inq_data)) 14018b8a9b1dSJustin T. Gibbs le = TRUE; 14028b8a9b1dSJustin T. Gibbs else 14038b8a9b1dSJustin T. Gibbs le = FALSE; 14048b8a9b1dSJustin T. Gibbs 14058b8a9b1dSJustin T. Gibbs /* 14068b8a9b1dSJustin T. Gibbs * Attempt to start the drive up. 14078b8a9b1dSJustin T. Gibbs * 14088b8a9b1dSJustin T. Gibbs * Save the current ccb so it can 14098b8a9b1dSJustin T. Gibbs * be restored and retried once the 14108b8a9b1dSJustin T. Gibbs * drive is started up. 14118b8a9b1dSJustin T. Gibbs */ 14128b8a9b1dSJustin T. Gibbs bcopy(ccb, save_ccb, sizeof(*save_ccb)); 14138b8a9b1dSJustin T. Gibbs 14148b8a9b1dSJustin T. Gibbs scsi_start_stop(&ccb->csio, 14158b8a9b1dSJustin T. Gibbs /*retries*/1, 14168b8a9b1dSJustin T. Gibbs camperiphdone, 14178b8a9b1dSJustin T. Gibbs MSG_SIMPLE_Q_TAG, 14188b8a9b1dSJustin T. Gibbs /*start*/TRUE, 14198b8a9b1dSJustin T. Gibbs /*load/eject*/le, 14208b8a9b1dSJustin T. Gibbs /*immediate*/FALSE, 14218b8a9b1dSJustin T. Gibbs SSD_FULL_SIZE, 14228b8a9b1dSJustin T. Gibbs /*timeout*/50000); 14238b8a9b1dSJustin T. Gibbs 14248b8a9b1dSJustin T. Gibbs /* release the queue after .5 sec. */ 14258b8a9b1dSJustin T. Gibbs relsim_flags = 14268b8a9b1dSJustin T. Gibbs RELSIM_RELEASE_AFTER_TIMEOUT; 14278b8a9b1dSJustin T. Gibbs timeout = 500; 14288b8a9b1dSJustin T. Gibbs /* 14298b8a9b1dSJustin T. Gibbs * Drop the priority to 0 so that 14308b8a9b1dSJustin T. Gibbs * we are the first to execute. Also 14318b8a9b1dSJustin T. Gibbs * freeze the queue after this command 14328b8a9b1dSJustin T. Gibbs * is sent so that we can restore the 14338b8a9b1dSJustin T. Gibbs * old csio and have it queued in the 14348b8a9b1dSJustin T. Gibbs * proper order before we let normal 14358b8a9b1dSJustin T. Gibbs * transactions go to the drive. 14368b8a9b1dSJustin T. Gibbs */ 14378b8a9b1dSJustin T. Gibbs ccb->ccb_h.pinfo.priority = 0; 14388b8a9b1dSJustin T. Gibbs ccb->ccb_h.flags |= CAM_DEV_QFREEZE; 14398b8a9b1dSJustin T. Gibbs 14408b8a9b1dSJustin T. Gibbs /* 14418b8a9b1dSJustin T. Gibbs * Save a pointer to the original 14428b8a9b1dSJustin T. Gibbs * CCB in the new CCB. 14438b8a9b1dSJustin T. Gibbs */ 14448b8a9b1dSJustin T. Gibbs ccb->ccb_h.saved_ccb_ptr = save_ccb; 14458b8a9b1dSJustin T. Gibbs 14468b8a9b1dSJustin T. Gibbs error = ERESTART; 14478b8a9b1dSJustin T. Gibbs } else { 14488b8a9b1dSJustin T. Gibbs error = scsi_interpret_sense(ccb, 14498b8a9b1dSJustin T. Gibbs sense_flags, 14508b8a9b1dSJustin T. Gibbs &relsim_flags, 14518b8a9b1dSJustin T. Gibbs &openings, 14528b8a9b1dSJustin T. Gibbs &timeout, 14538b8a9b1dSJustin T. Gibbs err_action); 14548b8a9b1dSJustin T. Gibbs } 14558b8a9b1dSJustin T. Gibbs } else if (ccb->csio.scsi_status == 14565a526431SJustin T. Gibbs SCSI_STATUS_CHECK_COND 14575a526431SJustin T. Gibbs && status != CAM_AUTOSENSE_FAIL) { 14588b8a9b1dSJustin T. Gibbs /* no point in decrementing the retry count */ 14598b8a9b1dSJustin T. Gibbs panic("cam_periph_error: scsi status of " 14608b8a9b1dSJustin T. Gibbs "CHECK COND returned but no sense " 14618b8a9b1dSJustin T. Gibbs "information is availible. " 14628b8a9b1dSJustin T. Gibbs "Controller should have returned " 14638b8a9b1dSJustin T. Gibbs "CAM_AUTOSENSE_FAILED"); 14648b8a9b1dSJustin T. Gibbs /* NOTREACHED */ 14658b8a9b1dSJustin T. Gibbs error = EIO; 146656e3e24bSKenneth D. Merry } else if (ccb->ccb_h.retry_count == 0) { 14678b8a9b1dSJustin T. Gibbs /* 14688b8a9b1dSJustin T. Gibbs * XXX KDM shouldn't there be a better 14698b8a9b1dSJustin T. Gibbs * argument to return?? 14708b8a9b1dSJustin T. Gibbs */ 14718b8a9b1dSJustin T. Gibbs error = EIO; 14728b8a9b1dSJustin T. Gibbs } else { 14738b8a9b1dSJustin T. Gibbs /* decrement the number of retries */ 14748b8a9b1dSJustin T. Gibbs retry = ccb->ccb_h.retry_count > 0; 14758b8a9b1dSJustin T. Gibbs if (retry) 14768b8a9b1dSJustin T. Gibbs ccb->ccb_h.retry_count--; 14778b8a9b1dSJustin T. Gibbs /* 14788b8a9b1dSJustin T. Gibbs * If it was aborted with no 14798b8a9b1dSJustin T. Gibbs * clue as to the reason, just 14808b8a9b1dSJustin T. Gibbs * retry it again. 14818b8a9b1dSJustin T. Gibbs */ 14828b8a9b1dSJustin T. Gibbs error = ERESTART; 14838b8a9b1dSJustin T. Gibbs } 14848b8a9b1dSJustin T. Gibbs break; 14858b8a9b1dSJustin T. Gibbs case SCSI_STATUS_QUEUE_FULL: 14868b8a9b1dSJustin T. Gibbs { 14878b8a9b1dSJustin T. Gibbs /* no decrement */ 148882815562SJustin T. Gibbs struct ccb_getdevstats cgds; 14898b8a9b1dSJustin T. Gibbs 14908b8a9b1dSJustin T. Gibbs /* 14918b8a9b1dSJustin T. Gibbs * First off, find out what the current 14928b8a9b1dSJustin T. Gibbs * transaction counts are. 14938b8a9b1dSJustin T. Gibbs */ 149482815562SJustin T. Gibbs xpt_setup_ccb(&cgds.ccb_h, 14958b8a9b1dSJustin T. Gibbs ccb->ccb_h.path, 14968b8a9b1dSJustin T. Gibbs /*priority*/1); 149782815562SJustin T. Gibbs cgds.ccb_h.func_code = XPT_GDEV_STATS; 149882815562SJustin T. Gibbs xpt_action((union ccb *)&cgds); 14998b8a9b1dSJustin T. Gibbs 15008b8a9b1dSJustin T. Gibbs /* 15018b8a9b1dSJustin T. Gibbs * If we were the only transaction active, treat 15028b8a9b1dSJustin T. Gibbs * the QUEUE FULL as if it were a BUSY condition. 15038b8a9b1dSJustin T. Gibbs */ 150482815562SJustin T. Gibbs if (cgds.dev_active != 0) { 150582815562SJustin T. Gibbs int total_openings; 150682815562SJustin T. Gibbs 15078b8a9b1dSJustin T. Gibbs /* 15088b8a9b1dSJustin T. Gibbs * Reduce the number of openings to 15098b8a9b1dSJustin T. Gibbs * be 1 less than the amount it took 15108b8a9b1dSJustin T. Gibbs * to get a queue full bounded by the 15118b8a9b1dSJustin T. Gibbs * minimum allowed tag count for this 15128b8a9b1dSJustin T. Gibbs * device. 15138b8a9b1dSJustin T. Gibbs */ 151482815562SJustin T. Gibbs total_openings = 151582815562SJustin T. Gibbs cgds.dev_active+cgds.dev_openings; 151682815562SJustin T. Gibbs openings = cgds.dev_active; 151782815562SJustin T. Gibbs if (openings < cgds.mintags) 151882815562SJustin T. Gibbs openings = cgds.mintags; 151982815562SJustin T. Gibbs if (openings < total_openings) 15208b8a9b1dSJustin T. Gibbs relsim_flags = RELSIM_ADJUST_OPENINGS; 15218b8a9b1dSJustin T. Gibbs else { 15228b8a9b1dSJustin T. Gibbs /* 15238b8a9b1dSJustin T. Gibbs * Some devices report queue full for 15248b8a9b1dSJustin T. Gibbs * temporary resource shortages. For 15258b8a9b1dSJustin T. Gibbs * this reason, we allow a minimum 15268b8a9b1dSJustin T. Gibbs * tag count to be entered via a 15278b8a9b1dSJustin T. Gibbs * quirk entry to prevent the queue 15288b8a9b1dSJustin T. Gibbs * count on these devices from falling 15298b8a9b1dSJustin T. Gibbs * to a pessimisticly low value. We 15308b8a9b1dSJustin T. Gibbs * still wait for the next successful 15318b8a9b1dSJustin T. Gibbs * completion, however, before queueing 15328b8a9b1dSJustin T. Gibbs * more transactions to the device. 15338b8a9b1dSJustin T. Gibbs */ 15348b8a9b1dSJustin T. Gibbs relsim_flags = 15358b8a9b1dSJustin T. Gibbs RELSIM_RELEASE_AFTER_CMDCMPLT; 15368b8a9b1dSJustin T. Gibbs } 15378b8a9b1dSJustin T. Gibbs timeout = 0; 15388b8a9b1dSJustin T. Gibbs error = ERESTART; 15398b8a9b1dSJustin T. Gibbs break; 15408b8a9b1dSJustin T. Gibbs } 15418b8a9b1dSJustin T. Gibbs /* FALLTHROUGH */ 15428b8a9b1dSJustin T. Gibbs } 15438b8a9b1dSJustin T. Gibbs case SCSI_STATUS_BUSY: 15448b8a9b1dSJustin T. Gibbs /* 15458b8a9b1dSJustin T. Gibbs * Restart the queue after either another 15468b8a9b1dSJustin T. Gibbs * command completes or a 1 second timeout. 1547af51b059SMatt Jacob * If we have any retries left, that is. 15488b8a9b1dSJustin T. Gibbs */ 1549af51b059SMatt Jacob retry = ccb->ccb_h.retry_count > 0; 1550af51b059SMatt Jacob if (retry) { 1551af51b059SMatt Jacob ccb->ccb_h.retry_count--; 15528b8a9b1dSJustin T. Gibbs error = ERESTART; 15538b8a9b1dSJustin T. Gibbs relsim_flags = RELSIM_RELEASE_AFTER_TIMEOUT 15548b8a9b1dSJustin T. Gibbs | RELSIM_RELEASE_AFTER_CMDCMPLT; 15558b8a9b1dSJustin T. Gibbs timeout = 1000; 1556af51b059SMatt Jacob } else { 1557af51b059SMatt Jacob error = EIO; 1558af51b059SMatt Jacob } 15598b8a9b1dSJustin T. Gibbs break; 15608b8a9b1dSJustin T. Gibbs case SCSI_STATUS_RESERV_CONFLICT: 15618b8a9b1dSJustin T. Gibbs error = EIO; 15628b8a9b1dSJustin T. Gibbs break; 15638b8a9b1dSJustin T. Gibbs default: 15648b8a9b1dSJustin T. Gibbs error = EIO; 15658b8a9b1dSJustin T. Gibbs break; 15668b8a9b1dSJustin T. Gibbs } 15678b8a9b1dSJustin T. Gibbs break; 15688b8a9b1dSJustin T. Gibbs case CAM_REQ_CMP_ERR: 15698b8a9b1dSJustin T. Gibbs case CAM_CMD_TIMEOUT: 15708b8a9b1dSJustin T. Gibbs case CAM_UNEXP_BUSFREE: 15718b8a9b1dSJustin T. Gibbs case CAM_UNCOR_PARITY: 15728b8a9b1dSJustin T. Gibbs case CAM_DATA_RUN_ERR: 15738b8a9b1dSJustin T. Gibbs /* decrement the number of retries */ 15748b8a9b1dSJustin T. Gibbs retry = ccb->ccb_h.retry_count > 0; 15758b8a9b1dSJustin T. Gibbs if (retry) { 15768b8a9b1dSJustin T. Gibbs ccb->ccb_h.retry_count--; 15778b8a9b1dSJustin T. Gibbs error = ERESTART; 15788b8a9b1dSJustin T. Gibbs } else { 15798b8a9b1dSJustin T. Gibbs error = EIO; 15808b8a9b1dSJustin T. Gibbs } 15818b8a9b1dSJustin T. Gibbs break; 15828b8a9b1dSJustin T. Gibbs case CAM_UA_ABORT: 15838b8a9b1dSJustin T. Gibbs case CAM_UA_TERMIO: 15848b8a9b1dSJustin T. Gibbs case CAM_MSG_REJECT_REC: 15858b8a9b1dSJustin T. Gibbs /* XXX Don't know that these are correct */ 15868b8a9b1dSJustin T. Gibbs error = EIO; 15878b8a9b1dSJustin T. Gibbs break; 15888b8a9b1dSJustin T. Gibbs case CAM_SEL_TIMEOUT: 15898b8a9b1dSJustin T. Gibbs { 1590e471e974SJustin T. Gibbs /* 1591e471e974SJustin T. Gibbs * XXX 1592e471e974SJustin T. Gibbs * A single selection timeout should not be enough 1593e471e974SJustin T. Gibbs * to invalidate a device. We should retry for multiple 1594e471e974SJustin T. Gibbs * seconds assuming this isn't a probe. We'll probably 1595e471e974SJustin T. Gibbs * need a special flag for that. 1596e471e974SJustin T. Gibbs */ 1597e471e974SJustin T. Gibbs #if 0 15988b8a9b1dSJustin T. Gibbs struct cam_path *newpath; 15998b8a9b1dSJustin T. Gibbs 16008b8a9b1dSJustin T. Gibbs /* Should we do more if we can't create the path?? */ 16018b8a9b1dSJustin T. Gibbs if (xpt_create_path(&newpath, xpt_path_periph(ccb->ccb_h.path), 16028b8a9b1dSJustin T. Gibbs xpt_path_path_id(ccb->ccb_h.path), 16038b8a9b1dSJustin T. Gibbs xpt_path_target_id(ccb->ccb_h.path), 16048b8a9b1dSJustin T. Gibbs CAM_LUN_WILDCARD) != CAM_REQ_CMP) 16058b8a9b1dSJustin T. Gibbs break; 16068b8a9b1dSJustin T. Gibbs /* 16078b8a9b1dSJustin T. Gibbs * Let peripheral drivers know that this device has gone 16088b8a9b1dSJustin T. Gibbs * away. 16098b8a9b1dSJustin T. Gibbs */ 16108b8a9b1dSJustin T. Gibbs xpt_async(AC_LOST_DEVICE, newpath, NULL); 16118b8a9b1dSJustin T. Gibbs xpt_free_path(newpath); 1612e471e974SJustin T. Gibbs #endif 161350711c71SKenneth D. Merry if ((sense_flags & SF_RETRY_SELTO) != 0) { 161450711c71SKenneth D. Merry retry = ccb->ccb_h.retry_count > 0; 161550711c71SKenneth D. Merry if (retry) { 161650711c71SKenneth D. Merry ccb->ccb_h.retry_count--; 161750711c71SKenneth D. Merry error = ERESTART; 161850711c71SKenneth D. Merry /* 161950711c71SKenneth D. Merry * Wait half a second to give the device 162050711c71SKenneth D. Merry * time to recover before we try again. 162150711c71SKenneth D. Merry */ 162250711c71SKenneth D. Merry relsim_flags = RELSIM_RELEASE_AFTER_TIMEOUT; 162350711c71SKenneth D. Merry timeout = 500; 162450711c71SKenneth D. Merry } else { 1625e471e974SJustin T. Gibbs error = ENXIO; 162650711c71SKenneth D. Merry } 162750711c71SKenneth D. Merry } else { 162850711c71SKenneth D. Merry error = ENXIO; 162950711c71SKenneth D. Merry } 16308b8a9b1dSJustin T. Gibbs break; 16318b8a9b1dSJustin T. Gibbs } 16328b8a9b1dSJustin T. Gibbs case CAM_REQ_INVALID: 16338b8a9b1dSJustin T. Gibbs case CAM_PATH_INVALID: 16348b8a9b1dSJustin T. Gibbs case CAM_DEV_NOT_THERE: 16358b8a9b1dSJustin T. Gibbs case CAM_NO_HBA: 16368b8a9b1dSJustin T. Gibbs case CAM_PROVIDE_FAIL: 16378b8a9b1dSJustin T. Gibbs case CAM_REQ_TOO_BIG: 16388b8a9b1dSJustin T. Gibbs error = EINVAL; 16398b8a9b1dSJustin T. Gibbs break; 16408b8a9b1dSJustin T. Gibbs case CAM_SCSI_BUS_RESET: 16418b8a9b1dSJustin T. Gibbs case CAM_BDR_SENT: 16428b8a9b1dSJustin T. Gibbs case CAM_REQUEUE_REQ: 16438b8a9b1dSJustin T. Gibbs /* Unconditional requeue, dammit */ 16448b8a9b1dSJustin T. Gibbs error = ERESTART; 16458b8a9b1dSJustin T. Gibbs break; 16468b8a9b1dSJustin T. Gibbs case CAM_RESRC_UNAVAIL: 16478b8a9b1dSJustin T. Gibbs case CAM_BUSY: 16488b8a9b1dSJustin T. Gibbs /* timeout??? */ 16498b8a9b1dSJustin T. Gibbs default: 16508b8a9b1dSJustin T. Gibbs /* decrement the number of retries */ 16518b8a9b1dSJustin T. Gibbs retry = ccb->ccb_h.retry_count > 0; 16528b8a9b1dSJustin T. Gibbs if (retry) { 16538b8a9b1dSJustin T. Gibbs ccb->ccb_h.retry_count--; 16548b8a9b1dSJustin T. Gibbs error = ERESTART; 16558b8a9b1dSJustin T. Gibbs } else { 16568b8a9b1dSJustin T. Gibbs /* Check the sense codes */ 16578b8a9b1dSJustin T. Gibbs error = EIO; 16588b8a9b1dSJustin T. Gibbs } 16598b8a9b1dSJustin T. Gibbs break; 16608b8a9b1dSJustin T. Gibbs } 16618b8a9b1dSJustin T. Gibbs 16628b8a9b1dSJustin T. Gibbs /* Attempt a retry */ 16638b8a9b1dSJustin T. Gibbs if (error == ERESTART || error == 0) { 16648b8a9b1dSJustin T. Gibbs if (frozen != 0) 16658b8a9b1dSJustin T. Gibbs ccb->ccb_h.status &= ~CAM_DEV_QFRZN; 16668b8a9b1dSJustin T. Gibbs 16678b8a9b1dSJustin T. Gibbs if (error == ERESTART) 16688b8a9b1dSJustin T. Gibbs xpt_action(ccb); 16698b8a9b1dSJustin T. Gibbs 16708b8a9b1dSJustin T. Gibbs if (frozen != 0) { 16718b8a9b1dSJustin T. Gibbs cam_release_devq(ccb->ccb_h.path, 16728b8a9b1dSJustin T. Gibbs relsim_flags, 16738b8a9b1dSJustin T. Gibbs openings, 16748b8a9b1dSJustin T. Gibbs timeout, 16758b8a9b1dSJustin T. Gibbs /*getcount_only*/0); 16768b8a9b1dSJustin T. Gibbs } 16778b8a9b1dSJustin T. Gibbs } 16788b8a9b1dSJustin T. Gibbs 16798b8a9b1dSJustin T. Gibbs 16808b8a9b1dSJustin T. Gibbs return (error); 16818b8a9b1dSJustin T. Gibbs } 1682