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); 653393f8daSKenneth D. Merry static int camperiphscsistatuserror(union ccb *ccb, 663393f8daSKenneth D. Merry cam_flags camflags, 673393f8daSKenneth D. Merry u_int32_t sense_flags, 683393f8daSKenneth D. Merry union ccb *save_ccb, 693393f8daSKenneth D. Merry int *openings, 703393f8daSKenneth D. Merry u_int32_t *relsim_flags, 713393f8daSKenneth D. Merry u_int32_t *timeout); 723393f8daSKenneth D. Merry static int camperiphscsisenseerror(union ccb *ccb, 733393f8daSKenneth D. Merry cam_flags camflags, 743393f8daSKenneth D. Merry u_int32_t sense_flags, 753393f8daSKenneth D. Merry union ccb *save_ccb, 763393f8daSKenneth D. Merry int *openings, 773393f8daSKenneth D. Merry u_int32_t *relsim_flags, 783393f8daSKenneth D. Merry u_int32_t *timeout); 798b8a9b1dSJustin T. Gibbs 800b7c27b9SPeter Wemm static int nperiph_drivers; 810b7c27b9SPeter Wemm struct periph_driver **periph_drivers; 820b7c27b9SPeter Wemm 830b7c27b9SPeter Wemm void 840b7c27b9SPeter Wemm periphdriver_register(void *data) 850b7c27b9SPeter Wemm { 860b7c27b9SPeter Wemm struct periph_driver **newdrivers, **old; 870b7c27b9SPeter Wemm int ndrivers; 880b7c27b9SPeter Wemm 890b7c27b9SPeter Wemm ndrivers = nperiph_drivers + 2; 900b7c27b9SPeter Wemm newdrivers = malloc(sizeof(*newdrivers) * ndrivers, M_TEMP, M_WAITOK); 910b7c27b9SPeter Wemm if (periph_drivers) 920b7c27b9SPeter Wemm bcopy(periph_drivers, newdrivers, 930b7c27b9SPeter Wemm sizeof(*newdrivers) * ndrivers); 940b7c27b9SPeter Wemm newdrivers[nperiph_drivers] = (struct periph_driver *)data; 950b7c27b9SPeter Wemm newdrivers[nperiph_drivers + 1] = NULL; 960b7c27b9SPeter Wemm old = periph_drivers; 970b7c27b9SPeter Wemm periph_drivers = newdrivers; 980b7c27b9SPeter Wemm if (old) 990b7c27b9SPeter Wemm free(old, M_TEMP); 1000b7c27b9SPeter Wemm nperiph_drivers++; 1010b7c27b9SPeter Wemm } 1020b7c27b9SPeter Wemm 1038b8a9b1dSJustin T. Gibbs cam_status 104ee9c90c7SKenneth D. Merry cam_periph_alloc(periph_ctor_t *periph_ctor, 105ee9c90c7SKenneth D. Merry periph_oninv_t *periph_oninvalidate, 106ee9c90c7SKenneth D. Merry periph_dtor_t *periph_dtor, periph_start_t *periph_start, 107ee9c90c7SKenneth D. Merry char *name, cam_periph_type type, struct cam_path *path, 108ee9c90c7SKenneth D. Merry ac_callback_t *ac_callback, ac_code code, void *arg) 1098b8a9b1dSJustin T. Gibbs { 1108b8a9b1dSJustin T. Gibbs struct periph_driver **p_drv; 1118b8a9b1dSJustin T. Gibbs struct cam_periph *periph; 1128b8a9b1dSJustin T. Gibbs struct cam_periph *cur_periph; 1138b8a9b1dSJustin T. Gibbs path_id_t path_id; 1148b8a9b1dSJustin T. Gibbs target_id_t target_id; 1158b8a9b1dSJustin T. Gibbs lun_id_t lun_id; 1168b8a9b1dSJustin T. Gibbs cam_status status; 1178b8a9b1dSJustin T. Gibbs u_int init_level; 1188b8a9b1dSJustin T. Gibbs int s; 1198b8a9b1dSJustin T. Gibbs 1208b8a9b1dSJustin T. Gibbs init_level = 0; 1218b8a9b1dSJustin T. Gibbs /* 1228b8a9b1dSJustin T. Gibbs * Handle Hot-Plug scenarios. If there is already a peripheral 1238b8a9b1dSJustin T. Gibbs * of our type assigned to this path, we are likely waiting for 1248b8a9b1dSJustin T. Gibbs * final close on an old, invalidated, peripheral. If this is 1258b8a9b1dSJustin T. Gibbs * the case, queue up a deferred call to the peripheral's async 1268b8a9b1dSJustin T. Gibbs * handler. If it looks like a mistaken re-alloation, complain. 1278b8a9b1dSJustin T. Gibbs */ 1288b8a9b1dSJustin T. Gibbs if ((periph = cam_periph_find(path, name)) != NULL) { 1298b8a9b1dSJustin T. Gibbs 1308b8a9b1dSJustin T. Gibbs if ((periph->flags & CAM_PERIPH_INVALID) != 0 1318b8a9b1dSJustin T. Gibbs && (periph->flags & CAM_PERIPH_NEW_DEV_FOUND) == 0) { 1328b8a9b1dSJustin T. Gibbs periph->flags |= CAM_PERIPH_NEW_DEV_FOUND; 1338b8a9b1dSJustin T. Gibbs periph->deferred_callback = ac_callback; 1348b8a9b1dSJustin T. Gibbs periph->deferred_ac = code; 1358b8a9b1dSJustin T. Gibbs return (CAM_REQ_INPROG); 1368b8a9b1dSJustin T. Gibbs } else { 1378b8a9b1dSJustin T. Gibbs printf("cam_periph_alloc: attempt to re-allocate " 1388b8a9b1dSJustin T. Gibbs "valid device %s%d rejected\n", 1398b8a9b1dSJustin T. Gibbs periph->periph_name, periph->unit_number); 1408b8a9b1dSJustin T. Gibbs } 1418b8a9b1dSJustin T. Gibbs return (CAM_REQ_INVALID); 1428b8a9b1dSJustin T. Gibbs } 1438b8a9b1dSJustin T. Gibbs 1448b8a9b1dSJustin T. Gibbs periph = (struct cam_periph *)malloc(sizeof(*periph), M_DEVBUF, 1458b8a9b1dSJustin T. Gibbs M_NOWAIT); 1468b8a9b1dSJustin T. Gibbs 1478b8a9b1dSJustin T. Gibbs if (periph == NULL) 1488b8a9b1dSJustin T. Gibbs return (CAM_RESRC_UNAVAIL); 1498b8a9b1dSJustin T. Gibbs 1508b8a9b1dSJustin T. Gibbs init_level++; 1518b8a9b1dSJustin T. Gibbs 1520b7c27b9SPeter Wemm for (p_drv = periph_drivers; *p_drv != NULL; p_drv++) { 1538b8a9b1dSJustin T. Gibbs if (strcmp((*p_drv)->driver_name, name) == 0) 1548b8a9b1dSJustin T. Gibbs break; 1558b8a9b1dSJustin T. Gibbs } 1568b8a9b1dSJustin T. Gibbs 1578b8a9b1dSJustin T. Gibbs path_id = xpt_path_path_id(path); 1588b8a9b1dSJustin T. Gibbs target_id = xpt_path_target_id(path); 1598b8a9b1dSJustin T. Gibbs lun_id = xpt_path_lun_id(path); 1608b8a9b1dSJustin T. Gibbs bzero(periph, sizeof(*periph)); 1618b8a9b1dSJustin T. Gibbs cam_init_pinfo(&periph->pinfo); 1628b8a9b1dSJustin T. Gibbs periph->periph_start = periph_start; 1638b8a9b1dSJustin T. Gibbs periph->periph_dtor = periph_dtor; 164ee9c90c7SKenneth D. Merry periph->periph_oninval = periph_oninvalidate; 1658b8a9b1dSJustin T. Gibbs periph->type = type; 1668b8a9b1dSJustin T. Gibbs periph->periph_name = name; 1678b8a9b1dSJustin T. Gibbs periph->unit_number = camperiphunit(*p_drv, path_id, target_id, lun_id); 1688b8a9b1dSJustin T. Gibbs periph->immediate_priority = CAM_PRIORITY_NONE; 1698b8a9b1dSJustin T. Gibbs periph->refcount = 0; 1708b8a9b1dSJustin T. Gibbs SLIST_INIT(&periph->ccb_list); 1718b8a9b1dSJustin T. Gibbs status = xpt_create_path(&path, periph, path_id, target_id, lun_id); 1728b8a9b1dSJustin T. Gibbs if (status != CAM_REQ_CMP) 1738b8a9b1dSJustin T. Gibbs goto failure; 1748b8a9b1dSJustin T. Gibbs 1758b8a9b1dSJustin T. Gibbs periph->path = path; 1768b8a9b1dSJustin T. Gibbs init_level++; 1778b8a9b1dSJustin T. Gibbs 1788b8a9b1dSJustin T. Gibbs status = xpt_add_periph(periph); 1798b8a9b1dSJustin T. Gibbs 1808b8a9b1dSJustin T. Gibbs if (status != CAM_REQ_CMP) 1818b8a9b1dSJustin T. Gibbs goto failure; 1828b8a9b1dSJustin T. Gibbs 1838b8a9b1dSJustin T. Gibbs s = splsoftcam(); 1848b8a9b1dSJustin T. Gibbs cur_periph = TAILQ_FIRST(&(*p_drv)->units); 1858b8a9b1dSJustin T. Gibbs while (cur_periph != NULL 1868b8a9b1dSJustin T. Gibbs && cur_periph->unit_number < periph->unit_number) 1878b8a9b1dSJustin T. Gibbs cur_periph = TAILQ_NEXT(cur_periph, unit_links); 1888b8a9b1dSJustin T. Gibbs 1898b8a9b1dSJustin T. Gibbs if (cur_periph != NULL) 1908b8a9b1dSJustin T. Gibbs TAILQ_INSERT_BEFORE(cur_periph, periph, unit_links); 1918b8a9b1dSJustin T. Gibbs else { 1928b8a9b1dSJustin T. Gibbs TAILQ_INSERT_TAIL(&(*p_drv)->units, periph, unit_links); 1938b8a9b1dSJustin T. Gibbs (*p_drv)->generation++; 1948b8a9b1dSJustin T. Gibbs } 1958b8a9b1dSJustin T. Gibbs 1968b8a9b1dSJustin T. Gibbs splx(s); 1978b8a9b1dSJustin T. Gibbs 1988b8a9b1dSJustin T. Gibbs init_level++; 1998b8a9b1dSJustin T. Gibbs 2008b8a9b1dSJustin T. Gibbs status = periph_ctor(periph, arg); 2018b8a9b1dSJustin T. Gibbs 2028b8a9b1dSJustin T. Gibbs if (status == CAM_REQ_CMP) 2038b8a9b1dSJustin T. Gibbs init_level++; 2048b8a9b1dSJustin T. Gibbs 2058b8a9b1dSJustin T. Gibbs failure: 2068b8a9b1dSJustin T. Gibbs switch (init_level) { 2078b8a9b1dSJustin T. Gibbs case 4: 2088b8a9b1dSJustin T. Gibbs /* Initialized successfully */ 2098b8a9b1dSJustin T. Gibbs break; 2108b8a9b1dSJustin T. Gibbs case 3: 2118b8a9b1dSJustin T. Gibbs s = splsoftcam(); 2128b8a9b1dSJustin T. Gibbs TAILQ_REMOVE(&(*p_drv)->units, periph, unit_links); 2138b8a9b1dSJustin T. Gibbs splx(s); 2148b8a9b1dSJustin T. Gibbs xpt_remove_periph(periph); 2158b8a9b1dSJustin T. Gibbs case 2: 2168b8a9b1dSJustin T. Gibbs xpt_free_path(periph->path); 2178b8a9b1dSJustin T. Gibbs case 1: 2188b8a9b1dSJustin T. Gibbs free(periph, M_DEVBUF); 2198b8a9b1dSJustin T. Gibbs case 0: 2208b8a9b1dSJustin T. Gibbs /* No cleanup to perform. */ 2218b8a9b1dSJustin T. Gibbs break; 2228b8a9b1dSJustin T. Gibbs default: 2238b8a9b1dSJustin T. Gibbs panic("cam_periph_alloc: Unkown init level"); 2248b8a9b1dSJustin T. Gibbs } 2258b8a9b1dSJustin T. Gibbs return(status); 2268b8a9b1dSJustin T. Gibbs } 2278b8a9b1dSJustin T. Gibbs 2288b8a9b1dSJustin T. Gibbs /* 2298b8a9b1dSJustin T. Gibbs * Find a peripheral structure with the specified path, target, lun, 2308b8a9b1dSJustin T. Gibbs * and (optionally) type. If the name is NULL, this function will return 2318b8a9b1dSJustin T. Gibbs * the first peripheral driver that matches the specified path. 2328b8a9b1dSJustin T. Gibbs */ 2338b8a9b1dSJustin T. Gibbs struct cam_periph * 2348b8a9b1dSJustin T. Gibbs cam_periph_find(struct cam_path *path, char *name) 2358b8a9b1dSJustin T. Gibbs { 2368b8a9b1dSJustin T. Gibbs struct periph_driver **p_drv; 2378b8a9b1dSJustin T. Gibbs struct cam_periph *periph; 2388b8a9b1dSJustin T. Gibbs int s; 2398b8a9b1dSJustin T. Gibbs 2400b7c27b9SPeter Wemm for (p_drv = periph_drivers; *p_drv != NULL; p_drv++) { 2418b8a9b1dSJustin T. Gibbs 2428b8a9b1dSJustin T. Gibbs if (name != NULL && (strcmp((*p_drv)->driver_name, name) != 0)) 2438b8a9b1dSJustin T. Gibbs continue; 2448b8a9b1dSJustin T. Gibbs 2458b8a9b1dSJustin T. Gibbs s = splsoftcam(); 24637d40066SPoul-Henning Kamp TAILQ_FOREACH(periph, &(*p_drv)->units, unit_links) { 2478b8a9b1dSJustin T. Gibbs if (xpt_path_comp(periph->path, path) == 0) { 2488b8a9b1dSJustin T. Gibbs splx(s); 2498b8a9b1dSJustin T. Gibbs return(periph); 2508b8a9b1dSJustin T. Gibbs } 2518b8a9b1dSJustin T. Gibbs } 2528b8a9b1dSJustin T. Gibbs splx(s); 2538b8a9b1dSJustin T. Gibbs if (name != NULL) 2548b8a9b1dSJustin T. Gibbs return(NULL); 2558b8a9b1dSJustin T. Gibbs } 2568b8a9b1dSJustin T. Gibbs return(NULL); 2578b8a9b1dSJustin T. Gibbs } 2588b8a9b1dSJustin T. Gibbs 2598b8a9b1dSJustin T. Gibbs cam_status 2608b8a9b1dSJustin T. Gibbs cam_periph_acquire(struct cam_periph *periph) 2618b8a9b1dSJustin T. Gibbs { 2628b8a9b1dSJustin T. Gibbs int s; 2638b8a9b1dSJustin T. Gibbs 2648b8a9b1dSJustin T. Gibbs if (periph == NULL) 2658b8a9b1dSJustin T. Gibbs return(CAM_REQ_CMP_ERR); 2668b8a9b1dSJustin T. Gibbs 2678b8a9b1dSJustin T. Gibbs s = splsoftcam(); 2688b8a9b1dSJustin T. Gibbs periph->refcount++; 2698b8a9b1dSJustin T. Gibbs splx(s); 2708b8a9b1dSJustin T. Gibbs 2718b8a9b1dSJustin T. Gibbs return(CAM_REQ_CMP); 2728b8a9b1dSJustin T. Gibbs } 2738b8a9b1dSJustin T. Gibbs 2748b8a9b1dSJustin T. Gibbs void 2758b8a9b1dSJustin T. Gibbs cam_periph_release(struct cam_periph *periph) 2768b8a9b1dSJustin T. Gibbs { 2778b8a9b1dSJustin T. Gibbs int s; 2788b8a9b1dSJustin T. Gibbs 2798b8a9b1dSJustin T. Gibbs if (periph == NULL) 2808b8a9b1dSJustin T. Gibbs return; 2818b8a9b1dSJustin T. Gibbs 2828b8a9b1dSJustin T. Gibbs s = splsoftcam(); 2838b8a9b1dSJustin T. Gibbs if ((--periph->refcount == 0) 2848b8a9b1dSJustin T. Gibbs && (periph->flags & CAM_PERIPH_INVALID)) { 2858b8a9b1dSJustin T. Gibbs camperiphfree(periph); 2868b8a9b1dSJustin T. Gibbs } 2878b8a9b1dSJustin T. Gibbs splx(s); 2888b8a9b1dSJustin T. Gibbs 2898b8a9b1dSJustin T. Gibbs } 2908b8a9b1dSJustin T. Gibbs 2918b8a9b1dSJustin T. Gibbs /* 2928b8a9b1dSJustin T. Gibbs * Look for the next unit number that is not currently in use for this 2938b8a9b1dSJustin T. Gibbs * peripheral type starting at "newunit". Also exclude unit numbers that 2948b8a9b1dSJustin T. Gibbs * are reserved by for future "hardwiring" unless we already know that this 2958b8a9b1dSJustin T. Gibbs * is a potential wired device. Only assume that the device is "wired" the 2968b8a9b1dSJustin T. Gibbs * first time through the loop since after that we'll be looking at unit 2978b8a9b1dSJustin T. Gibbs * numbers that did not match a wiring entry. 2988b8a9b1dSJustin T. Gibbs */ 2998b8a9b1dSJustin T. Gibbs static u_int 300501468a5SKenneth D. Merry camperiphnextunit(struct periph_driver *p_drv, u_int newunit, int wired, 301501468a5SKenneth D. Merry path_id_t pathid, target_id_t target, lun_id_t lun) 3028b8a9b1dSJustin T. Gibbs { 3038b8a9b1dSJustin T. Gibbs struct cam_periph *periph; 30475f51904SPeter Wemm char *periph_name, *strval; 3058b8a9b1dSJustin T. Gibbs int s; 30675f51904SPeter Wemm int i, val, dunit; 30775f51904SPeter Wemm const char *dname; 3088b8a9b1dSJustin T. Gibbs 3098b8a9b1dSJustin T. Gibbs s = splsoftcam(); 3108b8a9b1dSJustin T. Gibbs periph_name = p_drv->driver_name; 3118b8a9b1dSJustin T. Gibbs for (;;newunit++) { 3128b8a9b1dSJustin T. Gibbs 3138b8a9b1dSJustin T. Gibbs for (periph = TAILQ_FIRST(&p_drv->units); 3148b8a9b1dSJustin T. Gibbs periph != NULL && periph->unit_number != newunit; 3158b8a9b1dSJustin T. Gibbs periph = TAILQ_NEXT(periph, unit_links)) 3168b8a9b1dSJustin T. Gibbs ; 3178b8a9b1dSJustin T. Gibbs 3188b8a9b1dSJustin T. Gibbs if (periph != NULL && periph->unit_number == newunit) { 3198b8a9b1dSJustin T. Gibbs if (wired != 0) { 3208b8a9b1dSJustin T. Gibbs xpt_print_path(periph->path); 3218b8a9b1dSJustin T. Gibbs printf("Duplicate Wired Device entry!\n"); 3228b8a9b1dSJustin T. Gibbs xpt_print_path(periph->path); 323501468a5SKenneth D. Merry printf("Second device (%s device at scbus%d " 324501468a5SKenneth D. Merry "target %d lun %d) will not be wired\n", 325501468a5SKenneth D. Merry periph_name, pathid, target, lun); 3268b8a9b1dSJustin T. Gibbs wired = 0; 3278b8a9b1dSJustin T. Gibbs } 3288b8a9b1dSJustin T. Gibbs continue; 3298b8a9b1dSJustin T. Gibbs } 33075f51904SPeter Wemm if (wired) 33175f51904SPeter Wemm break; 3328b8a9b1dSJustin T. Gibbs 3338b8a9b1dSJustin T. Gibbs /* 3348b8a9b1dSJustin T. Gibbs * Don't match entries like "da 4" as a wired down 3358b8a9b1dSJustin T. Gibbs * device, but do match entries like "da 4 target 5" 3368b8a9b1dSJustin T. Gibbs * or even "da 4 scbus 1". 3378b8a9b1dSJustin T. Gibbs */ 33875f51904SPeter Wemm i = -1; 33975f51904SPeter Wemm while ((i = resource_locate(i, periph_name)) != -1) { 34075f51904SPeter Wemm dname = resource_query_name(i); 34175f51904SPeter Wemm dunit = resource_query_unit(i); 34275f51904SPeter Wemm /* if no "target" and no specific scbus, skip */ 34375f51904SPeter Wemm if (resource_int_value(dname, dunit, "target", &val) && 34475f51904SPeter Wemm (resource_string_value(dname, dunit, "at",&strval)|| 34575f51904SPeter Wemm strcmp(strval, "scbus") == 0)) 34675f51904SPeter Wemm continue; 34775f51904SPeter Wemm if (newunit == dunit) 3488b8a9b1dSJustin T. Gibbs break; 3498b8a9b1dSJustin T. Gibbs } 35075f51904SPeter Wemm if (i == -1) 3518b8a9b1dSJustin T. Gibbs break; 3528b8a9b1dSJustin T. Gibbs } 3538b8a9b1dSJustin T. Gibbs splx(s); 3548b8a9b1dSJustin T. Gibbs return (newunit); 3558b8a9b1dSJustin T. Gibbs } 3568b8a9b1dSJustin T. Gibbs 3578b8a9b1dSJustin T. Gibbs static u_int 3588b8a9b1dSJustin T. Gibbs camperiphunit(struct periph_driver *p_drv, path_id_t pathid, 3598b8a9b1dSJustin T. Gibbs target_id_t target, lun_id_t lun) 3608b8a9b1dSJustin T. Gibbs { 3618b8a9b1dSJustin T. Gibbs u_int unit; 36275f51904SPeter Wemm int hit, i, val, dunit; 36375f51904SPeter Wemm const char *dname; 36475f51904SPeter Wemm char pathbuf[32], *strval, *periph_name; 3658b8a9b1dSJustin T. Gibbs 3668b8a9b1dSJustin T. Gibbs unit = 0; 3678b8a9b1dSJustin T. Gibbs hit = 0; 3688b8a9b1dSJustin T. Gibbs 36975f51904SPeter Wemm periph_name = p_drv->driver_name; 37075f51904SPeter Wemm snprintf(pathbuf, sizeof(pathbuf), "scbus%d", pathid); 37175f51904SPeter Wemm i = -1; 37275f51904SPeter Wemm while ((i = resource_locate(i, periph_name)) != -1) { 37375f51904SPeter Wemm dname = resource_query_name(i); 37475f51904SPeter Wemm dunit = resource_query_unit(i); 37575f51904SPeter Wemm if (resource_string_value(dname, dunit, "at", &strval) == 0) { 37675f51904SPeter Wemm if (strcmp(strval, pathbuf) != 0) 3778b8a9b1dSJustin T. Gibbs continue; 3788b8a9b1dSJustin T. Gibbs hit++; 3798b8a9b1dSJustin T. Gibbs } 38075f51904SPeter Wemm if (resource_int_value(dname, dunit, "target", &val) == 0) { 38175f51904SPeter Wemm if (val != target) 3828b8a9b1dSJustin T. Gibbs continue; 3838b8a9b1dSJustin T. Gibbs hit++; 3848b8a9b1dSJustin T. Gibbs } 38575f51904SPeter Wemm if (resource_int_value(dname, dunit, "lun", &val) == 0) { 38675f51904SPeter Wemm if (val != lun) 3878b8a9b1dSJustin T. Gibbs continue; 3888b8a9b1dSJustin T. Gibbs hit++; 3898b8a9b1dSJustin T. Gibbs } 3908b8a9b1dSJustin T. Gibbs if (hit != 0) { 39175f51904SPeter Wemm unit = dunit; 3928b8a9b1dSJustin T. Gibbs break; 3938b8a9b1dSJustin T. Gibbs } 3948b8a9b1dSJustin T. Gibbs } 3958b8a9b1dSJustin T. Gibbs 3968b8a9b1dSJustin T. Gibbs /* 3978b8a9b1dSJustin T. Gibbs * Either start from 0 looking for the next unit or from 39875f51904SPeter Wemm * the unit number given in the resource config. This way, 3998b8a9b1dSJustin T. Gibbs * if we have wildcard matches, we don't return the same 4008b8a9b1dSJustin T. Gibbs * unit number twice. 4018b8a9b1dSJustin T. Gibbs */ 402501468a5SKenneth D. Merry unit = camperiphnextunit(p_drv, unit, /*wired*/hit, pathid, 403501468a5SKenneth D. Merry target, lun); 4048b8a9b1dSJustin T. Gibbs 4058b8a9b1dSJustin T. Gibbs return (unit); 4068b8a9b1dSJustin T. Gibbs } 4078b8a9b1dSJustin T. Gibbs 4088b8a9b1dSJustin T. Gibbs void 4098b8a9b1dSJustin T. Gibbs cam_periph_invalidate(struct cam_periph *periph) 4108b8a9b1dSJustin T. Gibbs { 4118b8a9b1dSJustin T. Gibbs int s; 4128b8a9b1dSJustin T. Gibbs 413ee9c90c7SKenneth D. Merry s = splsoftcam(); 414ee9c90c7SKenneth D. Merry /* 415ee9c90c7SKenneth D. Merry * We only call this routine the first time a peripheral is 416ee9c90c7SKenneth D. Merry * invalidated. The oninvalidate() routine is always called at 417ee9c90c7SKenneth D. Merry * splsoftcam(). 418ee9c90c7SKenneth D. Merry */ 419ee9c90c7SKenneth D. Merry if (((periph->flags & CAM_PERIPH_INVALID) == 0) 420ee9c90c7SKenneth D. Merry && (periph->periph_oninval != NULL)) 421ee9c90c7SKenneth D. Merry periph->periph_oninval(periph); 422ee9c90c7SKenneth D. Merry 4238b8a9b1dSJustin T. Gibbs periph->flags |= CAM_PERIPH_INVALID; 4248b8a9b1dSJustin T. Gibbs periph->flags &= ~CAM_PERIPH_NEW_DEV_FOUND; 4258b8a9b1dSJustin T. Gibbs 4268b8a9b1dSJustin T. Gibbs if (periph->refcount == 0) 4278b8a9b1dSJustin T. Gibbs camperiphfree(periph); 4288b8a9b1dSJustin T. Gibbs else if (periph->refcount < 0) 4298b8a9b1dSJustin T. Gibbs printf("cam_invalidate_periph: refcount < 0!!\n"); 4308b8a9b1dSJustin T. Gibbs splx(s); 4318b8a9b1dSJustin T. Gibbs } 4328b8a9b1dSJustin T. Gibbs 4338b8a9b1dSJustin T. Gibbs static void 4348b8a9b1dSJustin T. Gibbs camperiphfree(struct cam_periph *periph) 4358b8a9b1dSJustin T. Gibbs { 4368b8a9b1dSJustin T. Gibbs int s; 4378b8a9b1dSJustin T. Gibbs struct periph_driver **p_drv; 4388b8a9b1dSJustin T. Gibbs 4390b7c27b9SPeter Wemm for (p_drv = periph_drivers; *p_drv != NULL; p_drv++) { 4408b8a9b1dSJustin T. Gibbs if (strcmp((*p_drv)->driver_name, periph->periph_name) == 0) 4418b8a9b1dSJustin T. Gibbs break; 4428b8a9b1dSJustin T. Gibbs } 4438b8a9b1dSJustin T. Gibbs 4448b8a9b1dSJustin T. Gibbs if (periph->periph_dtor != NULL) 4458b8a9b1dSJustin T. Gibbs periph->periph_dtor(periph); 4468b8a9b1dSJustin T. Gibbs 4478b8a9b1dSJustin T. Gibbs s = splsoftcam(); 4488b8a9b1dSJustin T. Gibbs TAILQ_REMOVE(&(*p_drv)->units, periph, unit_links); 4498b8a9b1dSJustin T. Gibbs (*p_drv)->generation++; 4508b8a9b1dSJustin T. Gibbs splx(s); 4518b8a9b1dSJustin T. Gibbs 4528b8a9b1dSJustin T. Gibbs xpt_remove_periph(periph); 4538b8a9b1dSJustin T. Gibbs 4548b8a9b1dSJustin T. Gibbs if (periph->flags & CAM_PERIPH_NEW_DEV_FOUND) { 4558b8a9b1dSJustin T. Gibbs union ccb ccb; 4568b8a9b1dSJustin T. Gibbs void *arg; 4578b8a9b1dSJustin T. Gibbs 4588b8a9b1dSJustin T. Gibbs switch (periph->deferred_ac) { 4598b8a9b1dSJustin T. Gibbs case AC_FOUND_DEVICE: 4608b8a9b1dSJustin T. Gibbs ccb.ccb_h.func_code = XPT_GDEV_TYPE; 4618b8a9b1dSJustin T. Gibbs xpt_setup_ccb(&ccb.ccb_h, periph->path, /*priority*/ 1); 4628b8a9b1dSJustin T. Gibbs xpt_action(&ccb); 4638b8a9b1dSJustin T. Gibbs arg = &ccb; 4648b8a9b1dSJustin T. Gibbs break; 4658b8a9b1dSJustin T. Gibbs case AC_PATH_REGISTERED: 4668b8a9b1dSJustin T. Gibbs ccb.ccb_h.func_code = XPT_PATH_INQ; 4678b8a9b1dSJustin T. Gibbs xpt_setup_ccb(&ccb.ccb_h, periph->path, /*priority*/ 1); 4688b8a9b1dSJustin T. Gibbs xpt_action(&ccb); 4698b8a9b1dSJustin T. Gibbs arg = &ccb; 4708b8a9b1dSJustin T. Gibbs break; 4718b8a9b1dSJustin T. Gibbs default: 4728b8a9b1dSJustin T. Gibbs arg = NULL; 4738b8a9b1dSJustin T. Gibbs break; 4748b8a9b1dSJustin T. Gibbs } 4758b8a9b1dSJustin T. Gibbs periph->deferred_callback(NULL, periph->deferred_ac, 4768b8a9b1dSJustin T. Gibbs periph->path, arg); 4778b8a9b1dSJustin T. Gibbs } 4788b8a9b1dSJustin T. Gibbs xpt_free_path(periph->path); 4798b8a9b1dSJustin T. Gibbs free(periph, M_DEVBUF); 4808b8a9b1dSJustin T. Gibbs } 4818b8a9b1dSJustin T. Gibbs 4828b8a9b1dSJustin T. Gibbs /* 4838b8a9b1dSJustin T. Gibbs * Wait interruptibly for an exclusive lock. 4848b8a9b1dSJustin T. Gibbs */ 4858b8a9b1dSJustin T. Gibbs int 4868b8a9b1dSJustin T. Gibbs cam_periph_lock(struct cam_periph *periph, int priority) 4878b8a9b1dSJustin T. Gibbs { 4888b8a9b1dSJustin T. Gibbs int error; 4898b8a9b1dSJustin T. Gibbs 4903393f8daSKenneth D. Merry /* 4913393f8daSKenneth D. Merry * Increment the reference count on the peripheral 4923393f8daSKenneth D. Merry * while we wait for our lock attempt to succeed 4933393f8daSKenneth D. Merry * to ensure the peripheral doesn't dissappear 4943393f8daSKenneth D. Merry * out from under us while we sleep. 4953393f8daSKenneth D. Merry */ 4968b8a9b1dSJustin T. Gibbs if (cam_periph_acquire(periph) != CAM_REQ_CMP) 4978b8a9b1dSJustin T. Gibbs return(ENXIO); 4988b8a9b1dSJustin T. Gibbs 4993393f8daSKenneth D. Merry while ((periph->flags & CAM_PERIPH_LOCKED) != 0) { 5003393f8daSKenneth D. Merry periph->flags |= CAM_PERIPH_LOCK_WANTED; 5013393f8daSKenneth D. Merry if ((error = tsleep(periph, priority, "caplck", 0)) != 0) { 5023393f8daSKenneth D. Merry cam_periph_release(periph); 5033393f8daSKenneth D. Merry return error; 5043393f8daSKenneth D. Merry } 5053393f8daSKenneth D. Merry } 5063393f8daSKenneth D. Merry 5078b8a9b1dSJustin T. Gibbs periph->flags |= CAM_PERIPH_LOCKED; 5088b8a9b1dSJustin T. Gibbs return 0; 5098b8a9b1dSJustin T. Gibbs } 5108b8a9b1dSJustin T. Gibbs 5118b8a9b1dSJustin T. Gibbs /* 5128b8a9b1dSJustin T. Gibbs * Unlock and wake up any waiters. 5138b8a9b1dSJustin T. Gibbs */ 5148b8a9b1dSJustin T. Gibbs void 5158b8a9b1dSJustin T. Gibbs cam_periph_unlock(struct cam_periph *periph) 5168b8a9b1dSJustin T. Gibbs { 5178b8a9b1dSJustin T. Gibbs periph->flags &= ~CAM_PERIPH_LOCKED; 5188b8a9b1dSJustin T. Gibbs if ((periph->flags & CAM_PERIPH_LOCK_WANTED) != 0) { 5198b8a9b1dSJustin T. Gibbs periph->flags &= ~CAM_PERIPH_LOCK_WANTED; 5208b8a9b1dSJustin T. Gibbs wakeup(periph); 5218b8a9b1dSJustin T. Gibbs } 5228b8a9b1dSJustin T. Gibbs 5238b8a9b1dSJustin T. Gibbs cam_periph_release(periph); 5248b8a9b1dSJustin T. Gibbs } 5258b8a9b1dSJustin T. Gibbs 5268b8a9b1dSJustin T. Gibbs /* 5278b8a9b1dSJustin T. Gibbs * Map user virtual pointers into kernel virtual address space, so we can 5288b8a9b1dSJustin T. Gibbs * access the memory. This won't work on physical pointers, for now it's 5298b8a9b1dSJustin T. Gibbs * up to the caller to check for that. (XXX KDM -- should we do that here 5308b8a9b1dSJustin T. Gibbs * instead?) This also only works for up to MAXPHYS memory. Since we use 5318b8a9b1dSJustin T. Gibbs * buffers to map stuff in and out, we're limited to the buffer size. 5328b8a9b1dSJustin T. Gibbs */ 5338b8a9b1dSJustin T. Gibbs int 5348b8a9b1dSJustin T. Gibbs cam_periph_mapmem(union ccb *ccb, struct cam_periph_map_info *mapinfo) 5358b8a9b1dSJustin T. Gibbs { 53679d49a06SKenneth D. Merry int numbufs, i; 53779d49a06SKenneth D. Merry int flags[CAM_PERIPH_MAXMAPS]; 5388b8a9b1dSJustin T. Gibbs u_int8_t **data_ptrs[CAM_PERIPH_MAXMAPS]; 5398b8a9b1dSJustin T. Gibbs u_int32_t lengths[CAM_PERIPH_MAXMAPS]; 5408b8a9b1dSJustin T. Gibbs u_int32_t dirs[CAM_PERIPH_MAXMAPS]; 5418b8a9b1dSJustin T. Gibbs 5428b8a9b1dSJustin T. Gibbs switch(ccb->ccb_h.func_code) { 5438b8a9b1dSJustin T. Gibbs case XPT_DEV_MATCH: 5448b8a9b1dSJustin T. Gibbs if (ccb->cdm.match_buf_len == 0) { 5458b8a9b1dSJustin T. Gibbs printf("cam_periph_mapmem: invalid match buffer " 5468b8a9b1dSJustin T. Gibbs "length 0\n"); 5478b8a9b1dSJustin T. Gibbs return(EINVAL); 5488b8a9b1dSJustin T. Gibbs } 5498b8a9b1dSJustin T. Gibbs if (ccb->cdm.pattern_buf_len > 0) { 5508b8a9b1dSJustin T. Gibbs data_ptrs[0] = (u_int8_t **)&ccb->cdm.patterns; 5518b8a9b1dSJustin T. Gibbs lengths[0] = ccb->cdm.pattern_buf_len; 5528b8a9b1dSJustin T. Gibbs dirs[0] = CAM_DIR_OUT; 5538b8a9b1dSJustin T. Gibbs data_ptrs[1] = (u_int8_t **)&ccb->cdm.matches; 5548b8a9b1dSJustin T. Gibbs lengths[1] = ccb->cdm.match_buf_len; 5558b8a9b1dSJustin T. Gibbs dirs[1] = CAM_DIR_IN; 5568b8a9b1dSJustin T. Gibbs numbufs = 2; 5578b8a9b1dSJustin T. Gibbs } else { 5588b8a9b1dSJustin T. Gibbs data_ptrs[0] = (u_int8_t **)&ccb->cdm.matches; 5598b8a9b1dSJustin T. Gibbs lengths[0] = ccb->cdm.match_buf_len; 5608b8a9b1dSJustin T. Gibbs dirs[0] = CAM_DIR_IN; 5618b8a9b1dSJustin T. Gibbs numbufs = 1; 5628b8a9b1dSJustin T. Gibbs } 5638b8a9b1dSJustin T. Gibbs break; 5648b8a9b1dSJustin T. Gibbs case XPT_SCSI_IO: 56587cfaf0eSJustin T. Gibbs case XPT_CONT_TARGET_IO: 5668b8a9b1dSJustin T. Gibbs if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_NONE) 5678b8a9b1dSJustin T. Gibbs return(0); 5688b8a9b1dSJustin T. Gibbs 5698b8a9b1dSJustin T. Gibbs data_ptrs[0] = &ccb->csio.data_ptr; 57079d49a06SKenneth D. Merry lengths[0] = ccb->csio.dxfer_len; 5718b8a9b1dSJustin T. Gibbs dirs[0] = ccb->ccb_h.flags & CAM_DIR_MASK; 5728b8a9b1dSJustin T. Gibbs numbufs = 1; 5738b8a9b1dSJustin T. Gibbs break; 5748b8a9b1dSJustin T. Gibbs default: 5758b8a9b1dSJustin T. Gibbs return(EINVAL); 5768b8a9b1dSJustin T. Gibbs break; /* NOTREACHED */ 5778b8a9b1dSJustin T. Gibbs } 5788b8a9b1dSJustin T. Gibbs 5798b8a9b1dSJustin T. Gibbs /* 58079d49a06SKenneth D. Merry * Check the transfer length and permissions first, so we don't 58179d49a06SKenneth D. Merry * have to unmap any previously mapped buffers. 5828b8a9b1dSJustin T. Gibbs */ 5838b8a9b1dSJustin T. Gibbs for (i = 0; i < numbufs; i++) { 58479d49a06SKenneth D. Merry 58579d49a06SKenneth D. Merry flags[i] = 0; 58679d49a06SKenneth D. Merry 58779d49a06SKenneth D. Merry /* 58879d49a06SKenneth D. Merry * The userland data pointer passed in may not be page 58979d49a06SKenneth D. Merry * aligned. vmapbuf() truncates the address to a page 59079d49a06SKenneth D. Merry * boundary, so if the address isn't page aligned, we'll 59179d49a06SKenneth D. Merry * need enough space for the given transfer length, plus 59279d49a06SKenneth D. Merry * whatever extra space is necessary to make it to the page 59379d49a06SKenneth D. Merry * boundary. 59479d49a06SKenneth D. Merry */ 59579d49a06SKenneth D. Merry if ((lengths[i] + 596ff1fe75fSKenneth D. Merry (((vm_offset_t)(*data_ptrs[i])) & PAGE_MASK)) > DFLTPHYS){ 5976d7b539aSPeter Wemm printf("cam_periph_mapmem: attempt to map %lu bytes, " 598ff1fe75fSKenneth D. Merry "which is greater than DFLTPHYS(%d)\n", 5996d7b539aSPeter Wemm (long)(lengths[i] + 6006d7b539aSPeter Wemm (((vm_offset_t)(*data_ptrs[i])) & PAGE_MASK)), 601ff1fe75fSKenneth D. Merry DFLTPHYS); 60279d49a06SKenneth D. Merry return(E2BIG); 60379d49a06SKenneth D. Merry } 6048b8a9b1dSJustin T. Gibbs 605edd24ab7SKenneth D. Merry if (dirs[i] & CAM_DIR_OUT) { 60621144e3bSPoul-Henning Kamp flags[i] = BIO_WRITE; 60702c58685SPoul-Henning Kamp if (!useracc(*data_ptrs[i], lengths[i], 60802c58685SPoul-Henning Kamp VM_PROT_READ)) { 6098b8a9b1dSJustin T. Gibbs printf("cam_periph_mapmem: error, " 6102e8bf209SBruce Evans "address %p, length %lu isn't " 6118b8a9b1dSJustin T. Gibbs "user accessible for READ\n", 6122e8bf209SBruce Evans (void *)*data_ptrs[i], 6132e8bf209SBruce Evans (u_long)lengths[i]); 6148b8a9b1dSJustin T. Gibbs return(EACCES); 6158b8a9b1dSJustin T. Gibbs } 6168b8a9b1dSJustin T. Gibbs } 6178b8a9b1dSJustin T. Gibbs 618edd24ab7SKenneth D. Merry if (dirs[i] & CAM_DIR_IN) { 61921144e3bSPoul-Henning Kamp flags[i] = BIO_READ; 62002c58685SPoul-Henning Kamp if (!useracc(*data_ptrs[i], lengths[i], 62102c58685SPoul-Henning Kamp VM_PROT_WRITE)) { 6228b8a9b1dSJustin T. Gibbs printf("cam_periph_mapmem: error, " 6232e8bf209SBruce Evans "address %p, length %lu isn't " 6248b8a9b1dSJustin T. Gibbs "user accessible for WRITE\n", 6252e8bf209SBruce Evans (void *)*data_ptrs[i], 6262e8bf209SBruce Evans (u_long)lengths[i]); 6278b8a9b1dSJustin T. Gibbs 6288b8a9b1dSJustin T. Gibbs return(EACCES); 6298b8a9b1dSJustin T. Gibbs } 6308b8a9b1dSJustin T. Gibbs } 6318b8a9b1dSJustin T. Gibbs 63279d49a06SKenneth D. Merry } 63379d49a06SKenneth D. Merry 63479d49a06SKenneth D. Merry /* this keeps the current process from getting swapped */ 63579d49a06SKenneth D. Merry /* 63679d49a06SKenneth D. Merry * XXX KDM should I use P_NOSWAP instead? 63779d49a06SKenneth D. Merry */ 6380cbbb7bfSPeter Wemm PHOLD(curproc); 63979d49a06SKenneth D. Merry 64079d49a06SKenneth D. Merry for (i = 0; i < numbufs; i++) { 6418b8a9b1dSJustin T. Gibbs /* 6428b8a9b1dSJustin T. Gibbs * Get the buffer. 6438b8a9b1dSJustin T. Gibbs */ 6441c7c3c6aSMatthew Dillon mapinfo->bp[i] = getpbuf(NULL); 6458b8a9b1dSJustin T. Gibbs 6468b8a9b1dSJustin T. Gibbs /* save the buffer's data address */ 6478b8a9b1dSJustin T. Gibbs mapinfo->bp[i]->b_saveaddr = mapinfo->bp[i]->b_data; 6488b8a9b1dSJustin T. Gibbs 6498b8a9b1dSJustin T. Gibbs /* put our pointer in the data slot */ 6508b8a9b1dSJustin T. Gibbs mapinfo->bp[i]->b_data = *data_ptrs[i]; 6518b8a9b1dSJustin T. Gibbs 652ff1fe75fSKenneth D. Merry /* set the transfer length, we know it's < DFLTPHYS */ 6538b8a9b1dSJustin T. Gibbs mapinfo->bp[i]->b_bufsize = lengths[i]; 6548b8a9b1dSJustin T. Gibbs 6558b8a9b1dSJustin T. Gibbs /* set the flags */ 65621144e3bSPoul-Henning Kamp mapinfo->bp[i]->b_flags = B_PHYS; 65721144e3bSPoul-Henning Kamp 65821144e3bSPoul-Henning Kamp /* set the direction */ 65921144e3bSPoul-Henning Kamp mapinfo->bp[i]->b_iocmd = flags[i]; 6608b8a9b1dSJustin T. Gibbs 6618b8a9b1dSJustin T. Gibbs /* map the buffer into kernel memory */ 6628b8a9b1dSJustin T. Gibbs vmapbuf(mapinfo->bp[i]); 6638b8a9b1dSJustin T. Gibbs 6648b8a9b1dSJustin T. Gibbs /* set our pointer to the new mapped area */ 6658b8a9b1dSJustin T. Gibbs *data_ptrs[i] = mapinfo->bp[i]->b_data; 6668b8a9b1dSJustin T. Gibbs 6678b8a9b1dSJustin T. Gibbs mapinfo->num_bufs_used++; 6688b8a9b1dSJustin T. Gibbs } 6698b8a9b1dSJustin T. Gibbs 6708b8a9b1dSJustin T. Gibbs return(0); 6718b8a9b1dSJustin T. Gibbs } 6728b8a9b1dSJustin T. Gibbs 6738b8a9b1dSJustin T. Gibbs /* 6748b8a9b1dSJustin T. Gibbs * Unmap memory segments mapped into kernel virtual address space by 6758b8a9b1dSJustin T. Gibbs * cam_periph_mapmem(). 6768b8a9b1dSJustin T. Gibbs */ 6778b8a9b1dSJustin T. Gibbs void 6788b8a9b1dSJustin T. Gibbs cam_periph_unmapmem(union ccb *ccb, struct cam_periph_map_info *mapinfo) 6798b8a9b1dSJustin T. Gibbs { 6808b8a9b1dSJustin T. Gibbs int numbufs, i; 6818b8a9b1dSJustin T. Gibbs u_int8_t **data_ptrs[CAM_PERIPH_MAXMAPS]; 6828b8a9b1dSJustin T. Gibbs 6838b8a9b1dSJustin T. Gibbs if (mapinfo->num_bufs_used <= 0) { 6848b8a9b1dSJustin T. Gibbs /* allow ourselves to be swapped once again */ 6850cbbb7bfSPeter Wemm PRELE(curproc); 6868b8a9b1dSJustin T. Gibbs return; 6878b8a9b1dSJustin T. Gibbs } 6888b8a9b1dSJustin T. Gibbs 6898b8a9b1dSJustin T. Gibbs switch (ccb->ccb_h.func_code) { 6908b8a9b1dSJustin T. Gibbs case XPT_DEV_MATCH: 6918b8a9b1dSJustin T. Gibbs numbufs = min(mapinfo->num_bufs_used, 2); 6928b8a9b1dSJustin T. Gibbs 6938b8a9b1dSJustin T. Gibbs if (numbufs == 1) { 6948b8a9b1dSJustin T. Gibbs data_ptrs[0] = (u_int8_t **)&ccb->cdm.matches; 6958b8a9b1dSJustin T. Gibbs } else { 6968b8a9b1dSJustin T. Gibbs data_ptrs[0] = (u_int8_t **)&ccb->cdm.patterns; 6978b8a9b1dSJustin T. Gibbs data_ptrs[1] = (u_int8_t **)&ccb->cdm.matches; 6988b8a9b1dSJustin T. Gibbs } 6998b8a9b1dSJustin T. Gibbs break; 7008b8a9b1dSJustin T. Gibbs case XPT_SCSI_IO: 7019911ecf9SJustin T. Gibbs case XPT_CONT_TARGET_IO: 7028b8a9b1dSJustin T. Gibbs data_ptrs[0] = &ccb->csio.data_ptr; 7038b8a9b1dSJustin T. Gibbs numbufs = min(mapinfo->num_bufs_used, 1); 7048b8a9b1dSJustin T. Gibbs break; 7058b8a9b1dSJustin T. Gibbs default: 7068b8a9b1dSJustin T. Gibbs /* allow ourselves to be swapped once again */ 7070cbbb7bfSPeter Wemm PRELE(curproc); 7088b8a9b1dSJustin T. Gibbs return; 7098b8a9b1dSJustin T. Gibbs break; /* NOTREACHED */ 7108b8a9b1dSJustin T. Gibbs } 7118b8a9b1dSJustin T. Gibbs 7128b8a9b1dSJustin T. Gibbs for (i = 0; i < numbufs; i++) { 7138b8a9b1dSJustin T. Gibbs /* Set the user's pointer back to the original value */ 7148b8a9b1dSJustin T. Gibbs *data_ptrs[i] = mapinfo->bp[i]->b_saveaddr; 7158b8a9b1dSJustin T. Gibbs 7168b8a9b1dSJustin T. Gibbs /* unmap the buffer */ 7178b8a9b1dSJustin T. Gibbs vunmapbuf(mapinfo->bp[i]); 7188b8a9b1dSJustin T. Gibbs 7198b8a9b1dSJustin T. Gibbs /* clear the flags we set above */ 72067812eacSKirk McKusick mapinfo->bp[i]->b_flags &= ~B_PHYS; 7218b8a9b1dSJustin T. Gibbs 7228b8a9b1dSJustin T. Gibbs /* release the buffer */ 7231c7c3c6aSMatthew Dillon relpbuf(mapinfo->bp[i], NULL); 7248b8a9b1dSJustin T. Gibbs } 7258b8a9b1dSJustin T. Gibbs 7268b8a9b1dSJustin T. Gibbs /* allow ourselves to be swapped once again */ 7270cbbb7bfSPeter Wemm PRELE(curproc); 7288b8a9b1dSJustin T. Gibbs } 7298b8a9b1dSJustin T. Gibbs 7308b8a9b1dSJustin T. Gibbs union ccb * 7318b8a9b1dSJustin T. Gibbs cam_periph_getccb(struct cam_periph *periph, u_int32_t priority) 7328b8a9b1dSJustin T. Gibbs { 7338b8a9b1dSJustin T. Gibbs struct ccb_hdr *ccb_h; 7348b8a9b1dSJustin T. Gibbs int s; 7358b8a9b1dSJustin T. Gibbs 7368b8a9b1dSJustin T. Gibbs CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering cdgetccb\n")); 7378b8a9b1dSJustin T. Gibbs 7388b8a9b1dSJustin T. Gibbs s = splsoftcam(); 7398b8a9b1dSJustin T. Gibbs 740fc2ffbe6SPoul-Henning Kamp while (SLIST_FIRST(&periph->ccb_list) == NULL) { 7418b8a9b1dSJustin T. Gibbs if (periph->immediate_priority > priority) 7428b8a9b1dSJustin T. Gibbs periph->immediate_priority = priority; 7438b8a9b1dSJustin T. Gibbs xpt_schedule(periph, priority); 744fc2ffbe6SPoul-Henning Kamp if ((SLIST_FIRST(&periph->ccb_list) != NULL) 745fc2ffbe6SPoul-Henning Kamp && (SLIST_FIRST(&periph->ccb_list)->pinfo.priority == priority)) 7468b8a9b1dSJustin T. Gibbs break; 7478b8a9b1dSJustin T. Gibbs tsleep(&periph->ccb_list, PRIBIO, "cgticb", 0); 7488b8a9b1dSJustin T. Gibbs } 7498b8a9b1dSJustin T. Gibbs 750fc2ffbe6SPoul-Henning Kamp ccb_h = SLIST_FIRST(&periph->ccb_list); 7518b8a9b1dSJustin T. Gibbs SLIST_REMOVE_HEAD(&periph->ccb_list, periph_links.sle); 7528b8a9b1dSJustin T. Gibbs splx(s); 7538b8a9b1dSJustin T. Gibbs return ((union ccb *)ccb_h); 7548b8a9b1dSJustin T. Gibbs } 7558b8a9b1dSJustin T. Gibbs 7568b8a9b1dSJustin T. Gibbs void 7578b8a9b1dSJustin T. Gibbs cam_periph_ccbwait(union ccb *ccb) 7588b8a9b1dSJustin T. Gibbs { 7598b8a9b1dSJustin T. Gibbs int s; 7608b8a9b1dSJustin T. Gibbs 7618b8a9b1dSJustin T. Gibbs s = splsoftcam(); 7628b8a9b1dSJustin T. Gibbs if ((ccb->ccb_h.pinfo.index != CAM_UNQUEUED_INDEX) 7638b8a9b1dSJustin T. Gibbs || ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_INPROG)) 7648b8a9b1dSJustin T. Gibbs tsleep(&ccb->ccb_h.cbfcnp, PRIBIO, "cbwait", 0); 7658b8a9b1dSJustin T. Gibbs 7668b8a9b1dSJustin T. Gibbs splx(s); 7678b8a9b1dSJustin T. Gibbs } 7688b8a9b1dSJustin T. Gibbs 7698b8a9b1dSJustin T. Gibbs int 7708b8a9b1dSJustin T. Gibbs cam_periph_ioctl(struct cam_periph *periph, int cmd, caddr_t addr, 7718b8a9b1dSJustin T. Gibbs int (*error_routine)(union ccb *ccb, 7728b8a9b1dSJustin T. Gibbs cam_flags camflags, 7738b8a9b1dSJustin T. Gibbs u_int32_t sense_flags)) 7748b8a9b1dSJustin T. Gibbs { 7758b8a9b1dSJustin T. Gibbs union ccb *ccb; 7768b8a9b1dSJustin T. Gibbs int error; 7778b8a9b1dSJustin T. Gibbs int found; 7788b8a9b1dSJustin T. Gibbs 7798b8a9b1dSJustin T. Gibbs error = found = 0; 7808b8a9b1dSJustin T. Gibbs 7818b8a9b1dSJustin T. Gibbs switch(cmd){ 7828b8a9b1dSJustin T. Gibbs case CAMGETPASSTHRU: 7838b8a9b1dSJustin T. Gibbs ccb = cam_periph_getccb(periph, /* priority */ 1); 7848b8a9b1dSJustin T. Gibbs xpt_setup_ccb(&ccb->ccb_h, 7858b8a9b1dSJustin T. Gibbs ccb->ccb_h.path, 7868b8a9b1dSJustin T. Gibbs /*priority*/1); 7878b8a9b1dSJustin T. Gibbs ccb->ccb_h.func_code = XPT_GDEVLIST; 7888b8a9b1dSJustin T. Gibbs 7898b8a9b1dSJustin T. Gibbs /* 7908b8a9b1dSJustin T. Gibbs * Basically, the point of this is that we go through 7918b8a9b1dSJustin T. Gibbs * getting the list of devices, until we find a passthrough 7928b8a9b1dSJustin T. Gibbs * device. In the current version of the CAM code, the 7938b8a9b1dSJustin T. Gibbs * only way to determine what type of device we're dealing 7948b8a9b1dSJustin T. Gibbs * with is by its name. 7958b8a9b1dSJustin T. Gibbs */ 7968b8a9b1dSJustin T. Gibbs while (found == 0) { 7978b8a9b1dSJustin T. Gibbs ccb->cgdl.index = 0; 7988b8a9b1dSJustin T. Gibbs ccb->cgdl.status = CAM_GDEVLIST_MORE_DEVS; 7998b8a9b1dSJustin T. Gibbs while (ccb->cgdl.status == CAM_GDEVLIST_MORE_DEVS) { 8008b8a9b1dSJustin T. Gibbs 8018b8a9b1dSJustin T. Gibbs /* we want the next device in the list */ 8028b8a9b1dSJustin T. Gibbs xpt_action(ccb); 8038b8a9b1dSJustin T. Gibbs if (strncmp(ccb->cgdl.periph_name, 8048b8a9b1dSJustin T. Gibbs "pass", 4) == 0){ 8058b8a9b1dSJustin T. Gibbs found = 1; 8068b8a9b1dSJustin T. Gibbs break; 8078b8a9b1dSJustin T. Gibbs } 8088b8a9b1dSJustin T. Gibbs } 8098b8a9b1dSJustin T. Gibbs if ((ccb->cgdl.status == CAM_GDEVLIST_LAST_DEVICE) && 8108b8a9b1dSJustin T. Gibbs (found == 0)) { 8118b8a9b1dSJustin T. Gibbs ccb->cgdl.periph_name[0] = '\0'; 8128b8a9b1dSJustin T. Gibbs ccb->cgdl.unit_number = 0; 8138b8a9b1dSJustin T. Gibbs break; 8148b8a9b1dSJustin T. Gibbs } 8158b8a9b1dSJustin T. Gibbs } 8168b8a9b1dSJustin T. Gibbs 8178b8a9b1dSJustin T. Gibbs /* copy the result back out */ 8188b8a9b1dSJustin T. Gibbs bcopy(ccb, addr, sizeof(union ccb)); 8198b8a9b1dSJustin T. Gibbs 8208b8a9b1dSJustin T. Gibbs /* and release the ccb */ 8218b8a9b1dSJustin T. Gibbs xpt_release_ccb(ccb); 8228b8a9b1dSJustin T. Gibbs 8238b8a9b1dSJustin T. Gibbs break; 8248b8a9b1dSJustin T. Gibbs default: 8258b8a9b1dSJustin T. Gibbs error = ENOTTY; 8268b8a9b1dSJustin T. Gibbs break; 8278b8a9b1dSJustin T. Gibbs } 8288b8a9b1dSJustin T. Gibbs return(error); 8298b8a9b1dSJustin T. Gibbs } 8308b8a9b1dSJustin T. Gibbs 8318b8a9b1dSJustin T. Gibbs int 8328b8a9b1dSJustin T. Gibbs cam_periph_runccb(union ccb *ccb, 8338b8a9b1dSJustin T. Gibbs int (*error_routine)(union ccb *ccb, 8348b8a9b1dSJustin T. Gibbs cam_flags camflags, 8358b8a9b1dSJustin T. Gibbs u_int32_t sense_flags), 8368b8a9b1dSJustin T. Gibbs cam_flags camflags, u_int32_t sense_flags, 8378b8a9b1dSJustin T. Gibbs struct devstat *ds) 8388b8a9b1dSJustin T. Gibbs { 8398b8a9b1dSJustin T. Gibbs int error; 8408b8a9b1dSJustin T. Gibbs 8418b8a9b1dSJustin T. Gibbs error = 0; 8428b8a9b1dSJustin T. Gibbs 8438b8a9b1dSJustin T. Gibbs /* 8448b8a9b1dSJustin T. Gibbs * If the user has supplied a stats structure, and if we understand 8458b8a9b1dSJustin T. Gibbs * this particular type of ccb, record the transaction start. 8468b8a9b1dSJustin T. Gibbs */ 8478b8a9b1dSJustin T. Gibbs if ((ds != NULL) && (ccb->ccb_h.func_code == XPT_SCSI_IO)) 8488b8a9b1dSJustin T. Gibbs devstat_start_transaction(ds); 8498b8a9b1dSJustin T. Gibbs 8508b8a9b1dSJustin T. Gibbs xpt_action(ccb); 8518b8a9b1dSJustin T. Gibbs 8528b8a9b1dSJustin T. Gibbs do { 8538b8a9b1dSJustin T. Gibbs cam_periph_ccbwait(ccb); 8548b8a9b1dSJustin T. Gibbs if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) 8558b8a9b1dSJustin T. Gibbs error = 0; 8568b8a9b1dSJustin T. Gibbs else if (error_routine != NULL) 8578b8a9b1dSJustin T. Gibbs error = (*error_routine)(ccb, camflags, sense_flags); 8588b8a9b1dSJustin T. Gibbs else 8598b8a9b1dSJustin T. Gibbs error = 0; 8608b8a9b1dSJustin T. Gibbs 8618b8a9b1dSJustin T. Gibbs } while (error == ERESTART); 8628b8a9b1dSJustin T. Gibbs 8638b8a9b1dSJustin T. Gibbs if ((ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) 8648b8a9b1dSJustin T. Gibbs cam_release_devq(ccb->ccb_h.path, 8658b8a9b1dSJustin T. Gibbs /* relsim_flags */0, 8668b8a9b1dSJustin T. Gibbs /* openings */0, 8678b8a9b1dSJustin T. Gibbs /* timeout */0, 8688b8a9b1dSJustin T. Gibbs /* getcount_only */ FALSE); 8698b8a9b1dSJustin T. Gibbs 8708b8a9b1dSJustin T. Gibbs if ((ds != NULL) && (ccb->ccb_h.func_code == XPT_SCSI_IO)) 8718b8a9b1dSJustin T. Gibbs devstat_end_transaction(ds, 8728b8a9b1dSJustin T. Gibbs ccb->csio.dxfer_len, 8738b8a9b1dSJustin T. Gibbs ccb->csio.tag_action & 0xf, 8748b8a9b1dSJustin T. Gibbs ((ccb->ccb_h.flags & CAM_DIR_MASK) == 8758b8a9b1dSJustin T. Gibbs CAM_DIR_NONE) ? DEVSTAT_NO_DATA : 8768b8a9b1dSJustin T. Gibbs (ccb->ccb_h.flags & CAM_DIR_OUT) ? 8778b8a9b1dSJustin T. Gibbs DEVSTAT_WRITE : 8788b8a9b1dSJustin T. Gibbs DEVSTAT_READ); 8798b8a9b1dSJustin T. Gibbs 8808b8a9b1dSJustin T. Gibbs return(error); 8818b8a9b1dSJustin T. Gibbs } 8828b8a9b1dSJustin T. Gibbs 88387cfaf0eSJustin T. Gibbs void 88487cfaf0eSJustin T. Gibbs cam_freeze_devq(struct cam_path *path) 88587cfaf0eSJustin T. Gibbs { 88687cfaf0eSJustin T. Gibbs struct ccb_hdr ccb_h; 88787cfaf0eSJustin T. Gibbs 88887cfaf0eSJustin T. Gibbs xpt_setup_ccb(&ccb_h, path, /*priority*/1); 88987cfaf0eSJustin T. Gibbs ccb_h.func_code = XPT_NOOP; 89087cfaf0eSJustin T. Gibbs ccb_h.flags = CAM_DEV_QFREEZE; 89187cfaf0eSJustin T. Gibbs xpt_action((union ccb *)&ccb_h); 89287cfaf0eSJustin T. Gibbs } 89387cfaf0eSJustin T. Gibbs 8948b8a9b1dSJustin T. Gibbs u_int32_t 8958b8a9b1dSJustin T. Gibbs cam_release_devq(struct cam_path *path, u_int32_t relsim_flags, 8968b8a9b1dSJustin T. Gibbs u_int32_t openings, u_int32_t timeout, 8978b8a9b1dSJustin T. Gibbs int getcount_only) 8988b8a9b1dSJustin T. Gibbs { 8998b8a9b1dSJustin T. Gibbs struct ccb_relsim crs; 9008b8a9b1dSJustin T. Gibbs 9018b8a9b1dSJustin T. Gibbs xpt_setup_ccb(&crs.ccb_h, path, 9028b8a9b1dSJustin T. Gibbs /*priority*/1); 9038b8a9b1dSJustin T. Gibbs crs.ccb_h.func_code = XPT_REL_SIMQ; 9048b8a9b1dSJustin T. Gibbs crs.ccb_h.flags = getcount_only ? CAM_DEV_QFREEZE : 0; 9058b8a9b1dSJustin T. Gibbs crs.release_flags = relsim_flags; 9068b8a9b1dSJustin T. Gibbs crs.openings = openings; 9078b8a9b1dSJustin T. Gibbs crs.release_timeout = timeout; 9088b8a9b1dSJustin T. Gibbs xpt_action((union ccb *)&crs); 9098b8a9b1dSJustin T. Gibbs return (crs.qfrozen_cnt); 9108b8a9b1dSJustin T. Gibbs } 9118b8a9b1dSJustin T. Gibbs 9128b8a9b1dSJustin T. Gibbs #define saved_ccb_ptr ppriv_ptr0 9138b8a9b1dSJustin T. Gibbs static void 9148b8a9b1dSJustin T. Gibbs camperiphdone(struct cam_periph *periph, union ccb *done_ccb) 9158b8a9b1dSJustin T. Gibbs { 9163393f8daSKenneth D. Merry union ccb *saved_ccb; 9178b8a9b1dSJustin T. Gibbs cam_status status; 9188b8a9b1dSJustin T. Gibbs int frozen; 9198b8a9b1dSJustin T. Gibbs int sense; 9208b8a9b1dSJustin T. Gibbs struct scsi_start_stop_unit *scsi_cmd; 9218b8a9b1dSJustin T. Gibbs u_int32_t relsim_flags, timeout; 9228b8a9b1dSJustin T. Gibbs u_int32_t qfrozen_cnt; 9233393f8daSKenneth D. Merry int xpt_done_ccb; 9248b8a9b1dSJustin T. Gibbs 9253393f8daSKenneth D. Merry xpt_done_ccb = FALSE; 9268b8a9b1dSJustin T. Gibbs status = done_ccb->ccb_h.status; 9278b8a9b1dSJustin T. Gibbs frozen = (status & CAM_DEV_QFRZN) != 0; 9288b8a9b1dSJustin T. Gibbs sense = (status & CAM_AUTOSNS_VALID) != 0; 9298b8a9b1dSJustin T. Gibbs status &= CAM_STATUS_MASK; 9308b8a9b1dSJustin T. Gibbs 9318b8a9b1dSJustin T. Gibbs timeout = 0; 9328b8a9b1dSJustin T. Gibbs relsim_flags = 0; 9333393f8daSKenneth D. Merry saved_ccb = (union ccb *)done_ccb->ccb_h.saved_ccb_ptr; 9348b8a9b1dSJustin T. Gibbs 9358b8a9b1dSJustin T. Gibbs /* 9368b8a9b1dSJustin T. Gibbs * Unfreeze the queue once if it is already frozen.. 9378b8a9b1dSJustin T. Gibbs */ 9388b8a9b1dSJustin T. Gibbs if (frozen != 0) { 9398b8a9b1dSJustin T. Gibbs qfrozen_cnt = cam_release_devq(done_ccb->ccb_h.path, 9408b8a9b1dSJustin T. Gibbs /*relsim_flags*/0, 9418b8a9b1dSJustin T. Gibbs /*openings*/0, 9428b8a9b1dSJustin T. Gibbs /*timeout*/0, 9438b8a9b1dSJustin T. Gibbs /*getcount_only*/0); 9448b8a9b1dSJustin T. Gibbs } 9458b8a9b1dSJustin T. Gibbs 9468b8a9b1dSJustin T. Gibbs switch (status) { 9478b8a9b1dSJustin T. Gibbs case CAM_REQ_CMP: 9483393f8daSKenneth D. Merry { 9498b8a9b1dSJustin T. Gibbs /* 9508b8a9b1dSJustin T. Gibbs * If we have successfully taken a device from the not 9513393f8daSKenneth D. Merry * ready to ready state, re-scan the device and re-get 9523393f8daSKenneth D. Merry * the inquiry information. Many devices (mostly disks) 9533393f8daSKenneth D. Merry * don't properly report their inquiry information unless 9543393f8daSKenneth D. Merry * they are spun up. 9553393f8daSKenneth D. Merry * 9563393f8daSKenneth D. Merry * If we manually retrieved sense into a CCB and got 9573393f8daSKenneth D. Merry * something other than "NO SENSE" send the updated CCB 9583393f8daSKenneth D. Merry * back to the client via xpt_done() to be processed via 9593393f8daSKenneth D. Merry * the error recovery code again. 9608b8a9b1dSJustin T. Gibbs */ 9618b8a9b1dSJustin T. Gibbs if (done_ccb->ccb_h.func_code == XPT_SCSI_IO) { 9628b8a9b1dSJustin T. Gibbs scsi_cmd = (struct scsi_start_stop_unit *) 9638b8a9b1dSJustin T. Gibbs &done_ccb->csio.cdb_io.cdb_bytes; 9648b8a9b1dSJustin T. Gibbs 9658b8a9b1dSJustin T. Gibbs if (scsi_cmd->opcode == START_STOP_UNIT) 9668b8a9b1dSJustin T. Gibbs xpt_async(AC_INQ_CHANGED, 9678b8a9b1dSJustin T. Gibbs done_ccb->ccb_h.path, NULL); 9683393f8daSKenneth D. Merry if (scsi_cmd->opcode == REQUEST_SENSE) { 9693393f8daSKenneth D. Merry u_int sense_key; 9703393f8daSKenneth D. Merry 9713393f8daSKenneth D. Merry sense_key = saved_ccb->csio.sense_data.flags; 9723393f8daSKenneth D. Merry sense_key &= SSD_KEY; 9733393f8daSKenneth D. Merry if (sense_key != SSD_KEY_NO_SENSE) { 9743393f8daSKenneth D. Merry saved_ccb->ccb_h.flags |= 9753393f8daSKenneth D. Merry CAM_AUTOSNS_VALID; 9763393f8daSKenneth D. Merry xpt_print_path(saved_ccb->ccb_h.path); 9773393f8daSKenneth D. Merry printf("Recovered Sense\n"); 9783393f8daSKenneth D. Merry #if 0 9793393f8daSKenneth D. Merry scsi_sense_print(&saved_ccb->csio); 9803393f8daSKenneth D. Merry #endif 9813393f8daSKenneth D. Merry cam_error_print(saved_ccb, CAM_ESF_ALL, 9823393f8daSKenneth D. Merry CAM_EPF_ALL); 9833393f8daSKenneth D. Merry xpt_done_ccb = TRUE; 9843393f8daSKenneth D. Merry } 9853393f8daSKenneth D. Merry } 9868b8a9b1dSJustin T. Gibbs } 9878b8a9b1dSJustin T. Gibbs bcopy(done_ccb->ccb_h.saved_ccb_ptr, done_ccb, 9888b8a9b1dSJustin T. Gibbs sizeof(union ccb)); 9898b8a9b1dSJustin T. Gibbs 99060a899a0SKenneth D. Merry periph->flags &= ~CAM_PERIPH_RECOVERY_INPROG; 99160a899a0SKenneth D. Merry 9923393f8daSKenneth D. Merry if (xpt_done_ccb == FALSE) 9938b8a9b1dSJustin T. Gibbs xpt_action(done_ccb); 9948b8a9b1dSJustin T. Gibbs 9958b8a9b1dSJustin T. Gibbs break; 9963393f8daSKenneth D. Merry } 9978b8a9b1dSJustin T. Gibbs case CAM_SCSI_STATUS_ERROR: 9988b8a9b1dSJustin T. Gibbs scsi_cmd = (struct scsi_start_stop_unit *) 9998b8a9b1dSJustin T. Gibbs &done_ccb->csio.cdb_io.cdb_bytes; 10008b8a9b1dSJustin T. Gibbs if (sense != 0) { 10018b8a9b1dSJustin T. Gibbs struct scsi_sense_data *sense; 10028b8a9b1dSJustin T. Gibbs int error_code, sense_key, asc, ascq; 10038b8a9b1dSJustin T. Gibbs 10048b8a9b1dSJustin T. Gibbs sense = &done_ccb->csio.sense_data; 10058b8a9b1dSJustin T. Gibbs scsi_extract_sense(sense, &error_code, 10068b8a9b1dSJustin T. Gibbs &sense_key, &asc, &ascq); 10078b8a9b1dSJustin T. Gibbs 10088b8a9b1dSJustin T. Gibbs /* 10098b8a9b1dSJustin T. Gibbs * If the error is "invalid field in CDB", 10108b8a9b1dSJustin T. Gibbs * and the load/eject flag is set, turn the 10118b8a9b1dSJustin T. Gibbs * flag off and try again. This is just in 10128b8a9b1dSJustin T. Gibbs * case the drive in question barfs on the 10138b8a9b1dSJustin T. Gibbs * load eject flag. The CAM code should set 10148b8a9b1dSJustin T. Gibbs * the load/eject flag by default for 10158b8a9b1dSJustin T. Gibbs * removable media. 10168b8a9b1dSJustin T. Gibbs */ 10178b8a9b1dSJustin T. Gibbs 10188b8a9b1dSJustin T. Gibbs /* XXX KDM 10198b8a9b1dSJustin T. Gibbs * Should we check to see what the specific 10208b8a9b1dSJustin T. Gibbs * scsi status is?? Or does it not matter 10218b8a9b1dSJustin T. Gibbs * since we already know that there was an 10228b8a9b1dSJustin T. Gibbs * error, and we know what the specific 10238b8a9b1dSJustin T. Gibbs * error code was, and we know what the 10248b8a9b1dSJustin T. Gibbs * opcode is.. 10258b8a9b1dSJustin T. Gibbs */ 10268b8a9b1dSJustin T. Gibbs if ((scsi_cmd->opcode == START_STOP_UNIT) && 10278b8a9b1dSJustin T. Gibbs ((scsi_cmd->how & SSS_LOEJ) != 0) && 10288b8a9b1dSJustin T. Gibbs (asc == 0x24) && (ascq == 0x00) && 10298b8a9b1dSJustin T. Gibbs (done_ccb->ccb_h.retry_count > 0)) { 10308b8a9b1dSJustin T. Gibbs 10318b8a9b1dSJustin T. Gibbs scsi_cmd->how &= ~SSS_LOEJ; 10328b8a9b1dSJustin T. Gibbs 10338b8a9b1dSJustin T. Gibbs xpt_action(done_ccb); 10348b8a9b1dSJustin T. Gibbs 10353393f8daSKenneth D. Merry } else if (done_ccb->ccb_h.retry_count > 1) { 10368b8a9b1dSJustin T. Gibbs /* 10378b8a9b1dSJustin T. Gibbs * In this case, the error recovery 10388b8a9b1dSJustin T. Gibbs * command failed, but we've got 10398b8a9b1dSJustin T. Gibbs * some retries left on it. Give 10408b8a9b1dSJustin T. Gibbs * it another try. 10418b8a9b1dSJustin T. Gibbs */ 10428b8a9b1dSJustin T. Gibbs 10438b8a9b1dSJustin T. Gibbs /* set the timeout to .5 sec */ 10448b8a9b1dSJustin T. Gibbs relsim_flags = 10458b8a9b1dSJustin T. Gibbs RELSIM_RELEASE_AFTER_TIMEOUT; 10468b8a9b1dSJustin T. Gibbs timeout = 500; 10478b8a9b1dSJustin T. Gibbs 10488b8a9b1dSJustin T. Gibbs xpt_action(done_ccb); 10498b8a9b1dSJustin T. Gibbs 10508b8a9b1dSJustin T. Gibbs break; 10518b8a9b1dSJustin T. Gibbs 10528b8a9b1dSJustin T. Gibbs } else { 10538b8a9b1dSJustin T. Gibbs /* 10543393f8daSKenneth D. Merry * Perform the final retry with the original 10553393f8daSKenneth D. Merry * CCB so that final error processing is 10563393f8daSKenneth D. Merry * performed by the owner of the CCB. 10578b8a9b1dSJustin T. Gibbs */ 10588b8a9b1dSJustin T. Gibbs bcopy(done_ccb->ccb_h.saved_ccb_ptr, 10598b8a9b1dSJustin T. Gibbs done_ccb, sizeof(union ccb)); 10608b8a9b1dSJustin T. Gibbs 106160a899a0SKenneth D. Merry periph->flags &= ~CAM_PERIPH_RECOVERY_INPROG; 106260a899a0SKenneth D. Merry 10638b8a9b1dSJustin T. Gibbs xpt_action(done_ccb); 10648b8a9b1dSJustin T. Gibbs } 10658b8a9b1dSJustin T. Gibbs } else { 10668b8a9b1dSJustin T. Gibbs /* 10678b8a9b1dSJustin T. Gibbs * Eh?? The command failed, but we don't 10688b8a9b1dSJustin T. Gibbs * have any sense. What's up with that? 10698b8a9b1dSJustin T. Gibbs * Fire the CCB again to return it to the 10708b8a9b1dSJustin T. Gibbs * caller. 10718b8a9b1dSJustin T. Gibbs */ 10728b8a9b1dSJustin T. Gibbs bcopy(done_ccb->ccb_h.saved_ccb_ptr, 10738b8a9b1dSJustin T. Gibbs done_ccb, sizeof(union ccb)); 10748b8a9b1dSJustin T. Gibbs 107560a899a0SKenneth D. Merry periph->flags &= ~CAM_PERIPH_RECOVERY_INPROG; 107660a899a0SKenneth D. Merry 10778b8a9b1dSJustin T. Gibbs xpt_action(done_ccb); 10788b8a9b1dSJustin T. Gibbs 10798b8a9b1dSJustin T. Gibbs } 10808b8a9b1dSJustin T. Gibbs break; 10818b8a9b1dSJustin T. Gibbs default: 10828b8a9b1dSJustin T. Gibbs bcopy(done_ccb->ccb_h.saved_ccb_ptr, done_ccb, 10838b8a9b1dSJustin T. Gibbs sizeof(union ccb)); 10848b8a9b1dSJustin T. Gibbs 108560a899a0SKenneth D. Merry periph->flags &= ~CAM_PERIPH_RECOVERY_INPROG; 108660a899a0SKenneth D. Merry 10878b8a9b1dSJustin T. Gibbs xpt_action(done_ccb); 10888b8a9b1dSJustin T. Gibbs 10898b8a9b1dSJustin T. Gibbs break; 10908b8a9b1dSJustin T. Gibbs } 10918b8a9b1dSJustin T. Gibbs 10928b8a9b1dSJustin T. Gibbs /* decrement the retry count */ 10933393f8daSKenneth D. Merry /* 10943393f8daSKenneth D. Merry * XXX This isn't appropriate in all cases. Restructure, 10953393f8daSKenneth D. Merry * so that the retry count is only decremented on an 10963393f8daSKenneth D. Merry * actual retry. Remeber that the orignal ccb had its 10973393f8daSKenneth D. Merry * retry count dropped before entering recovery, so 10983393f8daSKenneth D. Merry * doing it again is a bug. 10993393f8daSKenneth D. Merry */ 11008b8a9b1dSJustin T. Gibbs if (done_ccb->ccb_h.retry_count > 0) 11018b8a9b1dSJustin T. Gibbs done_ccb->ccb_h.retry_count--; 11028b8a9b1dSJustin T. Gibbs 11038b8a9b1dSJustin T. Gibbs qfrozen_cnt = cam_release_devq(done_ccb->ccb_h.path, 11048b8a9b1dSJustin T. Gibbs /*relsim_flags*/relsim_flags, 11058b8a9b1dSJustin T. Gibbs /*openings*/0, 11068b8a9b1dSJustin T. Gibbs /*timeout*/timeout, 11078b8a9b1dSJustin T. Gibbs /*getcount_only*/0); 11083393f8daSKenneth D. Merry if (xpt_done_ccb == TRUE) 11093393f8daSKenneth D. Merry (*done_ccb->ccb_h.cbfcnp)(periph, done_ccb); 11108b8a9b1dSJustin T. Gibbs } 11118b8a9b1dSJustin T. Gibbs 11128b8a9b1dSJustin T. Gibbs /* 111387cfaf0eSJustin T. Gibbs * Generic Async Event handler. Peripheral drivers usually 111487cfaf0eSJustin T. Gibbs * filter out the events that require personal attention, 111587cfaf0eSJustin T. Gibbs * and leave the rest to this function. 111687cfaf0eSJustin T. Gibbs */ 111787cfaf0eSJustin T. Gibbs void 111887cfaf0eSJustin T. Gibbs cam_periph_async(struct cam_periph *periph, u_int32_t code, 111987cfaf0eSJustin T. Gibbs struct cam_path *path, void *arg) 112087cfaf0eSJustin T. Gibbs { 112187cfaf0eSJustin T. Gibbs switch (code) { 112287cfaf0eSJustin T. Gibbs case AC_LOST_DEVICE: 112387cfaf0eSJustin T. Gibbs cam_periph_invalidate(periph); 112487cfaf0eSJustin T. Gibbs break; 112587cfaf0eSJustin T. Gibbs case AC_SENT_BDR: 112687cfaf0eSJustin T. Gibbs case AC_BUS_RESET: 112787cfaf0eSJustin T. Gibbs { 112887cfaf0eSJustin T. Gibbs cam_periph_bus_settle(periph, SCSI_DELAY); 112987cfaf0eSJustin T. Gibbs break; 113087cfaf0eSJustin T. Gibbs } 113187cfaf0eSJustin T. Gibbs default: 113287cfaf0eSJustin T. Gibbs break; 113387cfaf0eSJustin T. Gibbs } 113487cfaf0eSJustin T. Gibbs } 113587cfaf0eSJustin T. Gibbs 113687cfaf0eSJustin T. Gibbs void 113787cfaf0eSJustin T. Gibbs cam_periph_bus_settle(struct cam_periph *periph, u_int bus_settle) 113887cfaf0eSJustin T. Gibbs { 113987cfaf0eSJustin T. Gibbs struct ccb_getdevstats cgds; 114087cfaf0eSJustin T. Gibbs 114187cfaf0eSJustin T. Gibbs xpt_setup_ccb(&cgds.ccb_h, periph->path, /*priority*/1); 114287cfaf0eSJustin T. Gibbs cgds.ccb_h.func_code = XPT_GDEV_STATS; 114387cfaf0eSJustin T. Gibbs xpt_action((union ccb *)&cgds); 114487cfaf0eSJustin T. Gibbs cam_periph_freeze_after_event(periph, &cgds.last_reset, bus_settle); 114587cfaf0eSJustin T. Gibbs } 114687cfaf0eSJustin T. Gibbs 114787cfaf0eSJustin T. Gibbs void 114887cfaf0eSJustin T. Gibbs cam_periph_freeze_after_event(struct cam_periph *periph, 114987cfaf0eSJustin T. Gibbs struct timeval* event_time, u_int duration_ms) 115087cfaf0eSJustin T. Gibbs { 115187cfaf0eSJustin T. Gibbs struct timeval delta; 115287cfaf0eSJustin T. Gibbs struct timeval duration_tv; 115387cfaf0eSJustin T. Gibbs int s; 115487cfaf0eSJustin T. Gibbs 115587cfaf0eSJustin T. Gibbs s = splclock(); 115687cfaf0eSJustin T. Gibbs microtime(&delta); 115787cfaf0eSJustin T. Gibbs splx(s); 115887cfaf0eSJustin T. Gibbs timevalsub(&delta, event_time); 115987cfaf0eSJustin T. Gibbs duration_tv.tv_sec = duration_ms / 1000; 116087cfaf0eSJustin T. Gibbs duration_tv.tv_usec = (duration_ms % 1000) * 1000; 116187cfaf0eSJustin T. Gibbs if (timevalcmp(&delta, &duration_tv, <)) { 116287cfaf0eSJustin T. Gibbs timevalsub(&duration_tv, &delta); 116387cfaf0eSJustin T. Gibbs 116487cfaf0eSJustin T. Gibbs duration_ms = duration_tv.tv_sec * 1000; 116587cfaf0eSJustin T. Gibbs duration_ms += duration_tv.tv_usec / 1000; 116687cfaf0eSJustin T. Gibbs cam_freeze_devq(periph->path); 116787cfaf0eSJustin T. Gibbs cam_release_devq(periph->path, 116887cfaf0eSJustin T. Gibbs RELSIM_RELEASE_AFTER_TIMEOUT, 116987cfaf0eSJustin T. Gibbs /*reduction*/0, 117087cfaf0eSJustin T. Gibbs /*timeout*/duration_ms, 117187cfaf0eSJustin T. Gibbs /*getcount_only*/0); 117287cfaf0eSJustin T. Gibbs } 117387cfaf0eSJustin T. Gibbs 117487cfaf0eSJustin T. Gibbs } 117587cfaf0eSJustin T. Gibbs 11763393f8daSKenneth D. Merry static int 11773393f8daSKenneth D. Merry camperiphscsistatuserror(union ccb *ccb, cam_flags camflags, 11783393f8daSKenneth D. Merry u_int32_t sense_flags, union ccb *save_ccb, 11793393f8daSKenneth D. Merry int *openings, u_int32_t *relsim_flags, 11803393f8daSKenneth D. Merry u_int32_t *timeout) 11818b8a9b1dSJustin T. Gibbs { 11828b8a9b1dSJustin T. Gibbs int error; 11838b8a9b1dSJustin T. Gibbs 11848b8a9b1dSJustin T. Gibbs switch (ccb->csio.scsi_status) { 11858b8a9b1dSJustin T. Gibbs case SCSI_STATUS_OK: 11868b8a9b1dSJustin T. Gibbs case SCSI_STATUS_COND_MET: 11878b8a9b1dSJustin T. Gibbs case SCSI_STATUS_INTERMED: 11888b8a9b1dSJustin T. Gibbs case SCSI_STATUS_INTERMED_COND_MET: 11898b8a9b1dSJustin T. Gibbs error = 0; 11908b8a9b1dSJustin T. Gibbs break; 11918b8a9b1dSJustin T. Gibbs case SCSI_STATUS_CMD_TERMINATED: 11928b8a9b1dSJustin T. Gibbs case SCSI_STATUS_CHECK_COND: 11933393f8daSKenneth D. Merry error = camperiphscsisenseerror(ccb, 11943393f8daSKenneth D. Merry camflags, 11958b8a9b1dSJustin T. Gibbs sense_flags, 11963393f8daSKenneth D. Merry save_ccb, 11973393f8daSKenneth D. Merry openings, 11983393f8daSKenneth D. Merry relsim_flags, 11993393f8daSKenneth D. Merry timeout); 12008b8a9b1dSJustin T. Gibbs break; 12018b8a9b1dSJustin T. Gibbs case SCSI_STATUS_QUEUE_FULL: 12028b8a9b1dSJustin T. Gibbs { 12038b8a9b1dSJustin T. Gibbs /* no decrement */ 120482815562SJustin T. Gibbs struct ccb_getdevstats cgds; 12058b8a9b1dSJustin T. Gibbs 12068b8a9b1dSJustin T. Gibbs /* 12078b8a9b1dSJustin T. Gibbs * First off, find out what the current 12088b8a9b1dSJustin T. Gibbs * transaction counts are. 12098b8a9b1dSJustin T. Gibbs */ 121082815562SJustin T. Gibbs xpt_setup_ccb(&cgds.ccb_h, 12118b8a9b1dSJustin T. Gibbs ccb->ccb_h.path, 12128b8a9b1dSJustin T. Gibbs /*priority*/1); 121382815562SJustin T. Gibbs cgds.ccb_h.func_code = XPT_GDEV_STATS; 121482815562SJustin T. Gibbs xpt_action((union ccb *)&cgds); 12158b8a9b1dSJustin T. Gibbs 12168b8a9b1dSJustin T. Gibbs /* 12178b8a9b1dSJustin T. Gibbs * If we were the only transaction active, treat 12188b8a9b1dSJustin T. Gibbs * the QUEUE FULL as if it were a BUSY condition. 12198b8a9b1dSJustin T. Gibbs */ 122082815562SJustin T. Gibbs if (cgds.dev_active != 0) { 122182815562SJustin T. Gibbs int total_openings; 122282815562SJustin T. Gibbs 12238b8a9b1dSJustin T. Gibbs /* 12248b8a9b1dSJustin T. Gibbs * Reduce the number of openings to 12258b8a9b1dSJustin T. Gibbs * be 1 less than the amount it took 12268b8a9b1dSJustin T. Gibbs * to get a queue full bounded by the 12278b8a9b1dSJustin T. Gibbs * minimum allowed tag count for this 12288b8a9b1dSJustin T. Gibbs * device. 12298b8a9b1dSJustin T. Gibbs */ 12303393f8daSKenneth D. Merry total_openings = cgds.dev_active + cgds.dev_openings; 12313393f8daSKenneth D. Merry *openings = cgds.dev_active; 12323393f8daSKenneth D. Merry if (*openings < cgds.mintags) 12333393f8daSKenneth D. Merry *openings = cgds.mintags; 12343393f8daSKenneth D. Merry if (*openings < total_openings) 12353393f8daSKenneth D. Merry *relsim_flags = RELSIM_ADJUST_OPENINGS; 12368b8a9b1dSJustin T. Gibbs else { 12378b8a9b1dSJustin T. Gibbs /* 12388b8a9b1dSJustin T. Gibbs * Some devices report queue full for 12398b8a9b1dSJustin T. Gibbs * temporary resource shortages. For 12408b8a9b1dSJustin T. Gibbs * this reason, we allow a minimum 12418b8a9b1dSJustin T. Gibbs * tag count to be entered via a 12428b8a9b1dSJustin T. Gibbs * quirk entry to prevent the queue 12438b8a9b1dSJustin T. Gibbs * count on these devices from falling 12448b8a9b1dSJustin T. Gibbs * to a pessimisticly low value. We 12458b8a9b1dSJustin T. Gibbs * still wait for the next successful 12468b8a9b1dSJustin T. Gibbs * completion, however, before queueing 12478b8a9b1dSJustin T. Gibbs * more transactions to the device. 12488b8a9b1dSJustin T. Gibbs */ 12493393f8daSKenneth D. Merry *relsim_flags = RELSIM_RELEASE_AFTER_CMDCMPLT; 12508b8a9b1dSJustin T. Gibbs } 12513393f8daSKenneth D. Merry *timeout = 0; 12528b8a9b1dSJustin T. Gibbs error = ERESTART; 12538b8a9b1dSJustin T. Gibbs break; 12548b8a9b1dSJustin T. Gibbs } 12558b8a9b1dSJustin T. Gibbs /* FALLTHROUGH */ 12568b8a9b1dSJustin T. Gibbs } 12578b8a9b1dSJustin T. Gibbs case SCSI_STATUS_BUSY: 12588b8a9b1dSJustin T. Gibbs /* 12598b8a9b1dSJustin T. Gibbs * Restart the queue after either another 12608b8a9b1dSJustin T. Gibbs * command completes or a 1 second timeout. 12618b8a9b1dSJustin T. Gibbs */ 12623393f8daSKenneth D. Merry if (ccb->ccb_h.retry_count > 0) { 1263af51b059SMatt Jacob ccb->ccb_h.retry_count--; 12648b8a9b1dSJustin T. Gibbs error = ERESTART; 12653393f8daSKenneth D. Merry *relsim_flags = RELSIM_RELEASE_AFTER_TIMEOUT 12668b8a9b1dSJustin T. Gibbs | RELSIM_RELEASE_AFTER_CMDCMPLT; 12673393f8daSKenneth D. Merry *timeout = 1000; 1268af51b059SMatt Jacob } else { 1269af51b059SMatt Jacob error = EIO; 1270af51b059SMatt Jacob } 12718b8a9b1dSJustin T. Gibbs break; 12728b8a9b1dSJustin T. Gibbs case SCSI_STATUS_RESERV_CONFLICT: 12738b8a9b1dSJustin T. Gibbs error = EIO; 12748b8a9b1dSJustin T. Gibbs break; 12758b8a9b1dSJustin T. Gibbs default: 12768b8a9b1dSJustin T. Gibbs error = EIO; 12778b8a9b1dSJustin T. Gibbs break; 12788b8a9b1dSJustin T. Gibbs } 12793393f8daSKenneth D. Merry return (error); 12803393f8daSKenneth D. Merry } 12813393f8daSKenneth D. Merry 12823393f8daSKenneth D. Merry static int 12833393f8daSKenneth D. Merry camperiphscsisenseerror(union ccb *ccb, cam_flags camflags, 12843393f8daSKenneth D. Merry u_int32_t sense_flags, union ccb *save_ccb, 12853393f8daSKenneth D. Merry int *openings, u_int32_t *relsim_flags, 12863393f8daSKenneth D. Merry u_int32_t *timeout) 12873393f8daSKenneth D. Merry { 12883393f8daSKenneth D. Merry struct cam_periph *periph; 12893393f8daSKenneth D. Merry int error; 12903393f8daSKenneth D. Merry 12913393f8daSKenneth D. Merry periph = xpt_path_periph(ccb->ccb_h.path); 12923393f8daSKenneth D. Merry if (periph->flags & CAM_PERIPH_RECOVERY_INPROG) { 12933393f8daSKenneth D. Merry 12943393f8daSKenneth D. Merry /* 12953393f8daSKenneth D. Merry * If error recovery is already in progress, don't attempt 12963393f8daSKenneth D. Merry * to process this error, but requeue it unconditionally 12973393f8daSKenneth D. Merry * and attempt to process it once error recovery has 12983393f8daSKenneth D. Merry * completed. This failed command is probably related to 12993393f8daSKenneth D. Merry * the error that caused the currently active error recovery 13003393f8daSKenneth D. Merry * action so our current recovery efforts should also 13013393f8daSKenneth D. Merry * address this command. Be aware that the error recovery 13023393f8daSKenneth D. Merry * code assumes that only one recovery action is in progress 13033393f8daSKenneth D. Merry * on a particular peripheral instance at any given time 13043393f8daSKenneth D. Merry * (e.g. only one saved CCB for error recovery) so it is 13053393f8daSKenneth D. Merry * imperitive that we don't violate this assumption. 13063393f8daSKenneth D. Merry */ 13073393f8daSKenneth D. Merry error = ERESTART; 13083393f8daSKenneth D. Merry } else { 13093393f8daSKenneth D. Merry scsi_sense_action err_action; 13103393f8daSKenneth D. Merry struct ccb_getdev cgd; 13113393f8daSKenneth D. Merry const char *action_string; 13123393f8daSKenneth D. Merry union ccb* print_ccb; 13133393f8daSKenneth D. Merry 13143393f8daSKenneth D. Merry /* A description of the error recovery action performed */ 13153393f8daSKenneth D. Merry action_string = NULL; 13163393f8daSKenneth D. Merry 13173393f8daSKenneth D. Merry /* 13183393f8daSKenneth D. Merry * The location of the orignal ccb 13193393f8daSKenneth D. Merry * for sense printing purposes. 13203393f8daSKenneth D. Merry */ 13213393f8daSKenneth D. Merry print_ccb = ccb; 13223393f8daSKenneth D. Merry 13233393f8daSKenneth D. Merry /* 13243393f8daSKenneth D. Merry * Grab the inquiry data for this device. 13253393f8daSKenneth D. Merry */ 13263393f8daSKenneth D. Merry xpt_setup_ccb(&cgd.ccb_h, ccb->ccb_h.path, /*priority*/ 1); 13273393f8daSKenneth D. Merry cgd.ccb_h.func_code = XPT_GDEV_TYPE; 13283393f8daSKenneth D. Merry xpt_action((union ccb *)&cgd); 13293393f8daSKenneth D. Merry 13303393f8daSKenneth D. Merry if ((ccb->ccb_h.status & CAM_AUTOSNS_VALID) != 0) 13313393f8daSKenneth D. Merry err_action = scsi_error_action(&ccb->csio, 13323393f8daSKenneth D. Merry &cgd.inq_data, 13333393f8daSKenneth D. Merry sense_flags); 13343393f8daSKenneth D. Merry else if ((ccb->ccb_h.flags & CAM_DIS_AUTOSENSE) == 0) 13353393f8daSKenneth D. Merry err_action = SS_REQSENSE; 13363393f8daSKenneth D. Merry else 13373393f8daSKenneth D. Merry err_action = SS_RETRY|SSQ_DECREMENT_COUNT|EIO; 13383393f8daSKenneth D. Merry 13393393f8daSKenneth D. Merry error = err_action & SS_ERRMASK; 13403393f8daSKenneth D. Merry 13413393f8daSKenneth D. Merry /* 13423393f8daSKenneth D. Merry * If the recovery action will consume a retry, 13433393f8daSKenneth D. Merry * make sure we actually have retries available. 13443393f8daSKenneth D. Merry */ 13453393f8daSKenneth D. Merry if ((err_action & SSQ_DECREMENT_COUNT) != 0) { 13463393f8daSKenneth D. Merry if (ccb->ccb_h.retry_count > 0) 13473393f8daSKenneth D. Merry ccb->ccb_h.retry_count--; 13483393f8daSKenneth D. Merry else { 13493393f8daSKenneth D. Merry action_string = "Retries Exhausted"; 13503393f8daSKenneth D. Merry goto sense_error_done; 13513393f8daSKenneth D. Merry } 13523393f8daSKenneth D. Merry } 13533393f8daSKenneth D. Merry 13543393f8daSKenneth D. Merry if ((err_action & SS_MASK) >= SS_START) { 13553393f8daSKenneth D. Merry /* 13563393f8daSKenneth D. Merry * Do common portions of commands that 13573393f8daSKenneth D. Merry * use recovery CCBs. 13583393f8daSKenneth D. Merry */ 13593393f8daSKenneth D. Merry if (save_ccb == NULL) { 13603393f8daSKenneth D. Merry action_string = "No recovery CCB supplied"; 13613393f8daSKenneth D. Merry goto sense_error_done; 13623393f8daSKenneth D. Merry } 13633393f8daSKenneth D. Merry bcopy(ccb, save_ccb, sizeof(*save_ccb)); 13643393f8daSKenneth D. Merry print_ccb = save_ccb; 13653393f8daSKenneth D. Merry periph->flags |= CAM_PERIPH_RECOVERY_INPROG; 13663393f8daSKenneth D. Merry } 13673393f8daSKenneth D. Merry 13683393f8daSKenneth D. Merry switch (err_action & SS_MASK) { 13693393f8daSKenneth D. Merry case SS_NOP: 13703393f8daSKenneth D. Merry case SS_RETRY: 13713393f8daSKenneth D. Merry action_string = "Retrying Command"; 13723393f8daSKenneth D. Merry error = ERESTART; 13738b8a9b1dSJustin T. Gibbs break; 13743393f8daSKenneth D. Merry case SS_FAIL: 13753393f8daSKenneth D. Merry action_string = "Unretryable error"; 13763393f8daSKenneth D. Merry break; 13773393f8daSKenneth D. Merry case SS_START: 13783393f8daSKenneth D. Merry { 13793393f8daSKenneth D. Merry int le; 13803393f8daSKenneth D. Merry 13813393f8daSKenneth D. Merry /* 13823393f8daSKenneth D. Merry * Send a start unit command to the device, and 13833393f8daSKenneth D. Merry * then retry the command. 13843393f8daSKenneth D. Merry */ 13853393f8daSKenneth D. Merry action_string = "Attempting to Start Unit"; 13863393f8daSKenneth D. Merry 13873393f8daSKenneth D. Merry /* 13883393f8daSKenneth D. Merry * Check for removable media and set 13893393f8daSKenneth D. Merry * load/eject flag appropriately. 13903393f8daSKenneth D. Merry */ 13913393f8daSKenneth D. Merry if (SID_IS_REMOVABLE(&cgd.inq_data)) 13923393f8daSKenneth D. Merry le = TRUE; 13933393f8daSKenneth D. Merry else 13943393f8daSKenneth D. Merry le = FALSE; 13953393f8daSKenneth D. Merry 13963393f8daSKenneth D. Merry scsi_start_stop(&ccb->csio, 13973393f8daSKenneth D. Merry /*retries*/1, 13983393f8daSKenneth D. Merry camperiphdone, 13993393f8daSKenneth D. Merry MSG_SIMPLE_Q_TAG, 14003393f8daSKenneth D. Merry /*start*/TRUE, 14013393f8daSKenneth D. Merry /*load/eject*/le, 14023393f8daSKenneth D. Merry /*immediate*/FALSE, 14033393f8daSKenneth D. Merry SSD_FULL_SIZE, 14043393f8daSKenneth D. Merry /*timeout*/50000); 14053393f8daSKenneth D. Merry break; 14063393f8daSKenneth D. Merry } 14073393f8daSKenneth D. Merry case SS_TUR: 14083393f8daSKenneth D. Merry { 14093393f8daSKenneth D. Merry /* 14103393f8daSKenneth D. Merry * Send a Test Unit Ready to the device. 14113393f8daSKenneth D. Merry * If the 'many' flag is set, we send 120 14123393f8daSKenneth D. Merry * test unit ready commands, one every half 14133393f8daSKenneth D. Merry * second. Otherwise, we just send one TUR. 14143393f8daSKenneth D. Merry * We only want to do this if the retry 14153393f8daSKenneth D. Merry * count has not been exhausted. 14163393f8daSKenneth D. Merry */ 14173393f8daSKenneth D. Merry int retries; 14183393f8daSKenneth D. Merry 14193393f8daSKenneth D. Merry if ((err_action & SSQ_MANY) != 0) { 14203393f8daSKenneth D. Merry action_string = "Polling device for readiness"; 14213393f8daSKenneth D. Merry retries = 120; 14223393f8daSKenneth D. Merry } else { 14233393f8daSKenneth D. Merry action_string = "Testing device for readiness"; 14243393f8daSKenneth D. Merry retries = 1; 14253393f8daSKenneth D. Merry } 14263393f8daSKenneth D. Merry scsi_test_unit_ready(&ccb->csio, 14273393f8daSKenneth D. Merry retries, 14283393f8daSKenneth D. Merry camperiphdone, 14293393f8daSKenneth D. Merry MSG_SIMPLE_Q_TAG, 14303393f8daSKenneth D. Merry SSD_FULL_SIZE, 14313393f8daSKenneth D. Merry /*timeout*/5000); 14323393f8daSKenneth D. Merry 14333393f8daSKenneth D. Merry /* 14343393f8daSKenneth D. Merry * Accomplish our 500ms delay by deferring 14353393f8daSKenneth D. Merry * the release of our device queue appropriately. 14363393f8daSKenneth D. Merry */ 14373393f8daSKenneth D. Merry *relsim_flags = RELSIM_RELEASE_AFTER_TIMEOUT; 14383393f8daSKenneth D. Merry *timeout = 500; 14393393f8daSKenneth D. Merry break; 14403393f8daSKenneth D. Merry } 14413393f8daSKenneth D. Merry case SS_REQSENSE: 14423393f8daSKenneth D. Merry { 14433393f8daSKenneth D. Merry /* 14443393f8daSKenneth D. Merry * Send a Request Sense to the device. We 14453393f8daSKenneth D. Merry * assume that we are in a contingent allegiance 14463393f8daSKenneth D. Merry * condition so we do not tag this request. 14473393f8daSKenneth D. Merry */ 14483393f8daSKenneth D. Merry scsi_request_sense(&ccb->csio, /*retries*/1, 14493393f8daSKenneth D. Merry camperiphdone, 14503393f8daSKenneth D. Merry &save_ccb->csio.sense_data, 14513393f8daSKenneth D. Merry sizeof(save_ccb->csio.sense_data), 14523393f8daSKenneth D. Merry CAM_TAG_ACTION_NONE, 14533393f8daSKenneth D. Merry /*sense_len*/SSD_FULL_SIZE, 14543393f8daSKenneth D. Merry /*timeout*/5000); 14553393f8daSKenneth D. Merry break; 14563393f8daSKenneth D. Merry } 14573393f8daSKenneth D. Merry default: 14583393f8daSKenneth D. Merry panic("Unhandled error action %x\n", err_action); 14593393f8daSKenneth D. Merry } 14603393f8daSKenneth D. Merry 14613393f8daSKenneth D. Merry if ((err_action & SS_MASK) >= SS_START) { 14623393f8daSKenneth D. Merry /* 14633393f8daSKenneth D. Merry * Drop the priority to 0 so that the recovery 14643393f8daSKenneth D. Merry * CCB is the first to execute. Freeze the queue 14653393f8daSKenneth D. Merry * after this command is sent so that we can 14663393f8daSKenneth D. Merry * restore the old csio and have it queued in 14673393f8daSKenneth D. Merry * the proper order before we release normal 14683393f8daSKenneth D. Merry * transactions to the device. 14693393f8daSKenneth D. Merry */ 14703393f8daSKenneth D. Merry ccb->ccb_h.pinfo.priority = 0; 14713393f8daSKenneth D. Merry ccb->ccb_h.flags |= CAM_DEV_QFREEZE; 14723393f8daSKenneth D. Merry ccb->ccb_h.saved_ccb_ptr = save_ccb; 14733393f8daSKenneth D. Merry error = ERESTART; 14743393f8daSKenneth D. Merry } 14753393f8daSKenneth D. Merry 14763393f8daSKenneth D. Merry sense_error_done: 14773393f8daSKenneth D. Merry if ((err_action & SSQ_PRINT_SENSE) != 0 14783393f8daSKenneth D. Merry && (ccb->ccb_h.status & CAM_AUTOSNS_VALID) != 0) { 14793393f8daSKenneth D. Merry #if 0 14803393f8daSKenneth D. Merry scsi_sense_print(&print_ccb->csio); 14813393f8daSKenneth D. Merry #endif 14823393f8daSKenneth D. Merry cam_error_print(print_ccb, CAM_ESF_ALL, CAM_EPF_ALL); 14833393f8daSKenneth D. Merry xpt_print_path(ccb->ccb_h.path); 14843393f8daSKenneth D. Merry printf("%s\n", action_string); 14853393f8daSKenneth D. Merry } 14863393f8daSKenneth D. Merry } 14873393f8daSKenneth D. Merry return (error); 14883393f8daSKenneth D. Merry } 14893393f8daSKenneth D. Merry 14903393f8daSKenneth D. Merry /* 14913393f8daSKenneth D. Merry * Generic error handler. Peripheral drivers usually filter 14923393f8daSKenneth D. Merry * out the errors that they handle in a unique mannor, then 14933393f8daSKenneth D. Merry * call this function. 14943393f8daSKenneth D. Merry */ 14953393f8daSKenneth D. Merry int 14963393f8daSKenneth D. Merry cam_periph_error(union ccb *ccb, cam_flags camflags, 14973393f8daSKenneth D. Merry u_int32_t sense_flags, union ccb *save_ccb) 14983393f8daSKenneth D. Merry { 14993393f8daSKenneth D. Merry const char *action_string; 15003393f8daSKenneth D. Merry cam_status status; 15013393f8daSKenneth D. Merry int frozen; 15023393f8daSKenneth D. Merry int error; 15033393f8daSKenneth D. Merry int openings; 15043393f8daSKenneth D. Merry u_int32_t relsim_flags; 15053393f8daSKenneth D. Merry u_int32_t timeout; 15063393f8daSKenneth D. Merry 15073393f8daSKenneth D. Merry action_string = NULL; 15083393f8daSKenneth D. Merry status = ccb->ccb_h.status; 15093393f8daSKenneth D. Merry frozen = (status & CAM_DEV_QFRZN) != 0; 15103393f8daSKenneth D. Merry status &= CAM_STATUS_MASK; 15113393f8daSKenneth D. Merry relsim_flags = 0; 15123393f8daSKenneth D. Merry 15133393f8daSKenneth D. Merry switch (status) { 15143393f8daSKenneth D. Merry case CAM_REQ_CMP: 15153393f8daSKenneth D. Merry error = 0; 15163393f8daSKenneth D. Merry break; 15173393f8daSKenneth D. Merry case CAM_SCSI_STATUS_ERROR: 15183393f8daSKenneth D. Merry error = camperiphscsistatuserror(ccb, 15193393f8daSKenneth D. Merry camflags, 15203393f8daSKenneth D. Merry sense_flags, 15213393f8daSKenneth D. Merry save_ccb, 15223393f8daSKenneth D. Merry &openings, 15233393f8daSKenneth D. Merry &relsim_flags, 15243393f8daSKenneth D. Merry &timeout); 15253393f8daSKenneth D. Merry break; 15263393f8daSKenneth D. Merry case CAM_AUTOSENSE_FAIL: 15273393f8daSKenneth D. Merry xpt_print_path(ccb->ccb_h.path); 15283393f8daSKenneth D. Merry printf("AutoSense Failed\n"); 15298b8a9b1dSJustin T. Gibbs case CAM_REQ_CMP_ERR: 15308b8a9b1dSJustin T. Gibbs case CAM_CMD_TIMEOUT: 15318b8a9b1dSJustin T. Gibbs case CAM_UNEXP_BUSFREE: 15328b8a9b1dSJustin T. Gibbs case CAM_UNCOR_PARITY: 15338b8a9b1dSJustin T. Gibbs case CAM_DATA_RUN_ERR: 15348b8a9b1dSJustin T. Gibbs /* decrement the number of retries */ 15353393f8daSKenneth D. Merry if (ccb->ccb_h.retry_count > 0) { 15368b8a9b1dSJustin T. Gibbs ccb->ccb_h.retry_count--; 15378b8a9b1dSJustin T. Gibbs error = ERESTART; 15388b8a9b1dSJustin T. Gibbs } else { 15393393f8daSKenneth D. Merry action_string = "Retries Exausted"; 15408b8a9b1dSJustin T. Gibbs error = EIO; 15418b8a9b1dSJustin T. Gibbs } 15428b8a9b1dSJustin T. Gibbs break; 15438b8a9b1dSJustin T. Gibbs case CAM_UA_ABORT: 15448b8a9b1dSJustin T. Gibbs case CAM_UA_TERMIO: 15458b8a9b1dSJustin T. Gibbs case CAM_MSG_REJECT_REC: 15468b8a9b1dSJustin T. Gibbs /* XXX Don't know that these are correct */ 15478b8a9b1dSJustin T. Gibbs error = EIO; 15488b8a9b1dSJustin T. Gibbs break; 15498b8a9b1dSJustin T. Gibbs case CAM_SEL_TIMEOUT: 15508b8a9b1dSJustin T. Gibbs { 15518b8a9b1dSJustin T. Gibbs struct cam_path *newpath; 15528b8a9b1dSJustin T. Gibbs 15533393f8daSKenneth D. Merry if ((camflags & CAM_RETRY_SELTO) != 0) { 15543393f8daSKenneth D. Merry if (ccb->ccb_h.retry_count > 0) { 15553393f8daSKenneth D. Merry 15563393f8daSKenneth D. Merry ccb->ccb_h.retry_count--; 15573393f8daSKenneth D. Merry error = ERESTART; 15583393f8daSKenneth D. Merry 15593393f8daSKenneth D. Merry /* 15603393f8daSKenneth D. Merry * Wait a second to give the device 15613393f8daSKenneth D. Merry * time to recover before we try again. 15623393f8daSKenneth D. Merry */ 15633393f8daSKenneth D. Merry relsim_flags = RELSIM_RELEASE_AFTER_TIMEOUT; 15643393f8daSKenneth D. Merry timeout = 1000; 15653393f8daSKenneth D. Merry break; 15663393f8daSKenneth D. Merry } 15673393f8daSKenneth D. Merry } 15683393f8daSKenneth D. Merry error = ENXIO; 15698b8a9b1dSJustin T. Gibbs /* Should we do more if we can't create the path?? */ 15708b8a9b1dSJustin T. Gibbs if (xpt_create_path(&newpath, xpt_path_periph(ccb->ccb_h.path), 15718b8a9b1dSJustin T. Gibbs xpt_path_path_id(ccb->ccb_h.path), 15728b8a9b1dSJustin T. Gibbs xpt_path_target_id(ccb->ccb_h.path), 15738b8a9b1dSJustin T. Gibbs CAM_LUN_WILDCARD) != CAM_REQ_CMP) 15748b8a9b1dSJustin T. Gibbs break; 15753393f8daSKenneth D. Merry 15768b8a9b1dSJustin T. Gibbs /* 15778b8a9b1dSJustin T. Gibbs * Let peripheral drivers know that this device has gone 15788b8a9b1dSJustin T. Gibbs * away. 15798b8a9b1dSJustin T. Gibbs */ 15808b8a9b1dSJustin T. Gibbs xpt_async(AC_LOST_DEVICE, newpath, NULL); 15818b8a9b1dSJustin T. Gibbs xpt_free_path(newpath); 15828b8a9b1dSJustin T. Gibbs break; 15838b8a9b1dSJustin T. Gibbs } 15848b8a9b1dSJustin T. Gibbs case CAM_REQ_INVALID: 15858b8a9b1dSJustin T. Gibbs case CAM_PATH_INVALID: 15868b8a9b1dSJustin T. Gibbs case CAM_DEV_NOT_THERE: 15878b8a9b1dSJustin T. Gibbs case CAM_NO_HBA: 15888b8a9b1dSJustin T. Gibbs case CAM_PROVIDE_FAIL: 15898b8a9b1dSJustin T. Gibbs case CAM_REQ_TOO_BIG: 15908b8a9b1dSJustin T. Gibbs error = EINVAL; 15918b8a9b1dSJustin T. Gibbs break; 15928b8a9b1dSJustin T. Gibbs case CAM_SCSI_BUS_RESET: 15938b8a9b1dSJustin T. Gibbs case CAM_BDR_SENT: 15943393f8daSKenneth D. Merry /* 15953393f8daSKenneth D. Merry * Commands that repeatedly timeout and cause these 15963393f8daSKenneth D. Merry * kinds of error recovery actions, should return 15973393f8daSKenneth D. Merry * CAM_CMD_TIMEOUT, which allows us to safely assume 15983393f8daSKenneth D. Merry * that this command was an innocent bystander to 15993393f8daSKenneth D. Merry * these events and should be unconditionally 16003393f8daSKenneth D. Merry * retried. 16013393f8daSKenneth D. Merry */ 16023393f8daSKenneth D. Merry /* FALLTHROUGH */ 16038b8a9b1dSJustin T. Gibbs case CAM_REQUEUE_REQ: 16043393f8daSKenneth D. Merry /* Unconditional requeue */ 16058b8a9b1dSJustin T. Gibbs error = ERESTART; 16068b8a9b1dSJustin T. Gibbs break; 16078b8a9b1dSJustin T. Gibbs case CAM_RESRC_UNAVAIL: 16088b8a9b1dSJustin T. Gibbs case CAM_BUSY: 16098b8a9b1dSJustin T. Gibbs /* timeout??? */ 16108b8a9b1dSJustin T. Gibbs default: 16118b8a9b1dSJustin T. Gibbs /* decrement the number of retries */ 16123393f8daSKenneth D. Merry if (ccb->ccb_h.retry_count > 0) { 16138b8a9b1dSJustin T. Gibbs ccb->ccb_h.retry_count--; 16148b8a9b1dSJustin T. Gibbs error = ERESTART; 16158b8a9b1dSJustin T. Gibbs } else { 16168b8a9b1dSJustin T. Gibbs error = EIO; 16173393f8daSKenneth D. Merry action_string = "Retries Exhausted"; 16188b8a9b1dSJustin T. Gibbs } 16198b8a9b1dSJustin T. Gibbs break; 16208b8a9b1dSJustin T. Gibbs } 16218b8a9b1dSJustin T. Gibbs 16228b8a9b1dSJustin T. Gibbs /* Attempt a retry */ 16238b8a9b1dSJustin T. Gibbs if (error == ERESTART || error == 0) { 16248b8a9b1dSJustin T. Gibbs if (frozen != 0) 16258b8a9b1dSJustin T. Gibbs ccb->ccb_h.status &= ~CAM_DEV_QFRZN; 16268b8a9b1dSJustin T. Gibbs 16273393f8daSKenneth D. Merry if (error == ERESTART) { 16283393f8daSKenneth D. Merry action_string = "Retrying Command"; 16298b8a9b1dSJustin T. Gibbs xpt_action(ccb); 16303393f8daSKenneth D. Merry } 16318b8a9b1dSJustin T. Gibbs 16323393f8daSKenneth D. Merry if (frozen != 0) 16338b8a9b1dSJustin T. Gibbs cam_release_devq(ccb->ccb_h.path, 16348b8a9b1dSJustin T. Gibbs relsim_flags, 16358b8a9b1dSJustin T. Gibbs openings, 16368b8a9b1dSJustin T. Gibbs timeout, 16378b8a9b1dSJustin T. Gibbs /*getcount_only*/0); 16388b8a9b1dSJustin T. Gibbs } 16398b8a9b1dSJustin T. Gibbs 16403393f8daSKenneth D. Merry if (error != 0 && bootverbose) { 16413393f8daSKenneth D. Merry 16423393f8daSKenneth D. Merry if (action_string == NULL) 16433393f8daSKenneth D. Merry action_string = "Unretryable Error"; 16443393f8daSKenneth D. Merry if (error != ERESTART) { 16453393f8daSKenneth D. Merry xpt_print_path(ccb->ccb_h.path); 16463393f8daSKenneth D. Merry printf("error %d\n", error); 16473393f8daSKenneth D. Merry } 16483393f8daSKenneth D. Merry xpt_print_path(ccb->ccb_h.path); 16493393f8daSKenneth D. Merry printf("%s\n", action_string); 16503393f8daSKenneth D. Merry } 16518b8a9b1dSJustin T. Gibbs 16528b8a9b1dSJustin T. Gibbs return (error); 16538b8a9b1dSJustin T. Gibbs } 1654