1898b0535SWarner Losh /*- 28b8a9b1dSJustin T. Gibbs * Common functions for CAM "type" (peripheral) drivers. 38b8a9b1dSJustin T. Gibbs * 48b8a9b1dSJustin T. Gibbs * Copyright (c) 1997, 1998 Justin T. Gibbs. 5501468a5SKenneth D. Merry * Copyright (c) 1997, 1998, 1999, 2000 Kenneth D. Merry. 68b8a9b1dSJustin T. Gibbs * All rights reserved. 78b8a9b1dSJustin T. Gibbs * 88b8a9b1dSJustin T. Gibbs * Redistribution and use in source and binary forms, with or without 98b8a9b1dSJustin T. Gibbs * modification, are permitted provided that the following conditions 108b8a9b1dSJustin T. Gibbs * are met: 118b8a9b1dSJustin T. Gibbs * 1. Redistributions of source code must retain the above copyright 128b8a9b1dSJustin T. Gibbs * notice, this list of conditions, and the following disclaimer, 138b8a9b1dSJustin T. Gibbs * without modification, immediately at the beginning of the file. 148b8a9b1dSJustin T. Gibbs * 2. The name of the author may not be used to endorse or promote products 158b8a9b1dSJustin T. Gibbs * derived from this software without specific prior written permission. 168b8a9b1dSJustin T. Gibbs * 178b8a9b1dSJustin T. Gibbs * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 188b8a9b1dSJustin T. Gibbs * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 198b8a9b1dSJustin T. Gibbs * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 208b8a9b1dSJustin T. Gibbs * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 218b8a9b1dSJustin T. Gibbs * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 228b8a9b1dSJustin T. Gibbs * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 238b8a9b1dSJustin T. Gibbs * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 248b8a9b1dSJustin T. Gibbs * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 258b8a9b1dSJustin T. Gibbs * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 268b8a9b1dSJustin T. Gibbs * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 278b8a9b1dSJustin T. Gibbs * SUCH DAMAGE. 288b8a9b1dSJustin T. Gibbs */ 298b8a9b1dSJustin T. Gibbs 309c963d87SDavid E. O'Brien #include <sys/cdefs.h> 319c963d87SDavid E. O'Brien __FBSDID("$FreeBSD$"); 329c963d87SDavid E. O'Brien 338b8a9b1dSJustin T. Gibbs #include <sys/param.h> 348b8a9b1dSJustin T. Gibbs #include <sys/systm.h> 358b8a9b1dSJustin T. Gibbs #include <sys/types.h> 368b8a9b1dSJustin T. Gibbs #include <sys/malloc.h> 37362abc44STai-hwa Liang #include <sys/kernel.h> 389626b608SPoul-Henning Kamp #include <sys/bio.h> 39f34fa851SJohn Baldwin #include <sys/lock.h> 40f34fa851SJohn Baldwin #include <sys/mutex.h> 418b8a9b1dSJustin T. Gibbs #include <sys/buf.h> 428b8a9b1dSJustin T. Gibbs #include <sys/proc.h> 438b8a9b1dSJustin T. Gibbs #include <sys/devicestat.h> 4475f51904SPeter Wemm #include <sys/bus.h> 453501942bSJustin T. Gibbs #include <sys/sbuf.h> 468b8a9b1dSJustin T. Gibbs #include <vm/vm.h> 478b8a9b1dSJustin T. Gibbs #include <vm/vm_extern.h> 488b8a9b1dSJustin T. Gibbs 498b8a9b1dSJustin T. Gibbs #include <cam/cam.h> 508b8a9b1dSJustin T. Gibbs #include <cam/cam_ccb.h> 5152c9ce25SScott Long #include <cam/cam_queue.h> 528b8a9b1dSJustin T. Gibbs #include <cam/cam_xpt_periph.h> 538b8a9b1dSJustin T. Gibbs #include <cam/cam_periph.h> 548b8a9b1dSJustin T. Gibbs #include <cam/cam_debug.h> 552b83592fSScott Long #include <cam/cam_sim.h> 568b8a9b1dSJustin T. Gibbs 578b8a9b1dSJustin T. Gibbs #include <cam/scsi/scsi_all.h> 588b8a9b1dSJustin T. Gibbs #include <cam/scsi/scsi_message.h> 598b8a9b1dSJustin T. Gibbs #include <cam/scsi/scsi_pass.h> 608b8a9b1dSJustin T. Gibbs 618b8a9b1dSJustin T. Gibbs static u_int camperiphnextunit(struct periph_driver *p_drv, 62501468a5SKenneth D. Merry u_int newunit, int wired, 63501468a5SKenneth D. Merry path_id_t pathid, target_id_t target, 64501468a5SKenneth D. Merry lun_id_t lun); 658b8a9b1dSJustin T. Gibbs static u_int camperiphunit(struct periph_driver *p_drv, 66501468a5SKenneth D. Merry path_id_t pathid, target_id_t target, 67501468a5SKenneth D. Merry lun_id_t lun); 688b8a9b1dSJustin T. Gibbs static void camperiphdone(struct cam_periph *periph, 698b8a9b1dSJustin T. Gibbs union ccb *done_ccb); 708b8a9b1dSJustin T. Gibbs static void camperiphfree(struct cam_periph *periph); 713393f8daSKenneth D. Merry static int camperiphscsistatuserror(union ccb *ccb, 720191d9b3SAlexander Motin union ccb **orig_ccb, 733393f8daSKenneth D. Merry cam_flags camflags, 743393f8daSKenneth D. Merry u_int32_t sense_flags, 753393f8daSKenneth D. Merry int *openings, 763393f8daSKenneth D. Merry u_int32_t *relsim_flags, 7783c5d981SAlexander Motin u_int32_t *timeout, 780181d54bSAlexander Motin u_int32_t *action, 7983c5d981SAlexander Motin const char **action_string); 803393f8daSKenneth D. Merry static int camperiphscsisenseerror(union ccb *ccb, 810191d9b3SAlexander Motin union ccb **orig_ccb, 823393f8daSKenneth D. Merry cam_flags camflags, 833393f8daSKenneth D. Merry u_int32_t sense_flags, 843393f8daSKenneth D. Merry int *openings, 853393f8daSKenneth D. Merry u_int32_t *relsim_flags, 8683c5d981SAlexander Motin u_int32_t *timeout, 870181d54bSAlexander Motin u_int32_t *action, 8883c5d981SAlexander Motin const char **action_string); 898b8a9b1dSJustin T. Gibbs 900b7c27b9SPeter Wemm static int nperiph_drivers; 9183c5d981SAlexander Motin static int initialized = 0; 920b7c27b9SPeter Wemm struct periph_driver **periph_drivers; 930b7c27b9SPeter Wemm 94d745c852SEd Schouten static MALLOC_DEFINE(M_CAMPERIPH, "CAM periph", "CAM peripheral buffers"); 95362abc44STai-hwa Liang 9673cf209fSMatt Jacob static int periph_selto_delay = 1000; 9773cf209fSMatt Jacob TUNABLE_INT("kern.cam.periph_selto_delay", &periph_selto_delay); 9873cf209fSMatt Jacob static int periph_noresrc_delay = 500; 9973cf209fSMatt Jacob TUNABLE_INT("kern.cam.periph_noresrc_delay", &periph_noresrc_delay); 10073cf209fSMatt Jacob static int periph_busy_delay = 500; 10173cf209fSMatt Jacob TUNABLE_INT("kern.cam.periph_busy_delay", &periph_busy_delay); 10273cf209fSMatt Jacob 10373cf209fSMatt Jacob 1040b7c27b9SPeter Wemm void 1050b7c27b9SPeter Wemm periphdriver_register(void *data) 1060b7c27b9SPeter Wemm { 10783c5d981SAlexander Motin struct periph_driver *drv = (struct periph_driver *)data; 1080b7c27b9SPeter Wemm struct periph_driver **newdrivers, **old; 1090b7c27b9SPeter Wemm int ndrivers; 1100b7c27b9SPeter Wemm 1110b7c27b9SPeter Wemm ndrivers = nperiph_drivers + 2; 1120dd50e9bSScott Long newdrivers = malloc(sizeof(*newdrivers) * ndrivers, M_CAMPERIPH, 1130dd50e9bSScott Long M_WAITOK); 1140b7c27b9SPeter Wemm if (periph_drivers) 1150b7c27b9SPeter Wemm bcopy(periph_drivers, newdrivers, 116623db360SKenneth D. Merry sizeof(*newdrivers) * nperiph_drivers); 11783c5d981SAlexander Motin newdrivers[nperiph_drivers] = drv; 1180b7c27b9SPeter Wemm newdrivers[nperiph_drivers + 1] = NULL; 1190b7c27b9SPeter Wemm old = periph_drivers; 1200b7c27b9SPeter Wemm periph_drivers = newdrivers; 1210b7c27b9SPeter Wemm if (old) 1220dd50e9bSScott Long free(old, M_CAMPERIPH); 1230b7c27b9SPeter Wemm nperiph_drivers++; 12483c5d981SAlexander Motin /* If driver marked as early or it is late now, initialize it. */ 12583c5d981SAlexander Motin if (((drv->flags & CAM_PERIPH_DRV_EARLY) != 0 && initialized > 0) || 12683c5d981SAlexander Motin initialized > 1) 12783c5d981SAlexander Motin (*drv->init)(); 12883c5d981SAlexander Motin } 12983c5d981SAlexander Motin 13083c5d981SAlexander Motin void 13183c5d981SAlexander Motin periphdriver_init(int level) 13283c5d981SAlexander Motin { 13383c5d981SAlexander Motin int i, early; 13483c5d981SAlexander Motin 13583c5d981SAlexander Motin initialized = max(initialized, level); 13683c5d981SAlexander Motin for (i = 0; periph_drivers[i] != NULL; i++) { 13783c5d981SAlexander Motin early = (periph_drivers[i]->flags & CAM_PERIPH_DRV_EARLY) ? 1 : 2; 13883c5d981SAlexander Motin if (early == initialized) 13983c5d981SAlexander Motin (*periph_drivers[i]->init)(); 14083c5d981SAlexander Motin } 1410b7c27b9SPeter Wemm } 1420b7c27b9SPeter Wemm 1438b8a9b1dSJustin T. Gibbs cam_status 144ee9c90c7SKenneth D. Merry cam_periph_alloc(periph_ctor_t *periph_ctor, 145ee9c90c7SKenneth D. Merry periph_oninv_t *periph_oninvalidate, 146ee9c90c7SKenneth D. Merry periph_dtor_t *periph_dtor, periph_start_t *periph_start, 147ee9c90c7SKenneth D. Merry char *name, cam_periph_type type, struct cam_path *path, 148ee9c90c7SKenneth D. Merry ac_callback_t *ac_callback, ac_code code, void *arg) 1498b8a9b1dSJustin T. Gibbs { 1508b8a9b1dSJustin T. Gibbs struct periph_driver **p_drv; 1512b83592fSScott Long struct cam_sim *sim; 1528b8a9b1dSJustin T. Gibbs struct cam_periph *periph; 1538b8a9b1dSJustin T. Gibbs struct cam_periph *cur_periph; 1548b8a9b1dSJustin T. Gibbs path_id_t path_id; 1558b8a9b1dSJustin T. Gibbs target_id_t target_id; 1568b8a9b1dSJustin T. Gibbs lun_id_t lun_id; 1578b8a9b1dSJustin T. Gibbs cam_status status; 1588b8a9b1dSJustin T. Gibbs u_int init_level; 1598b8a9b1dSJustin T. Gibbs 1608b8a9b1dSJustin T. Gibbs init_level = 0; 1618b8a9b1dSJustin T. Gibbs /* 1628b8a9b1dSJustin T. Gibbs * Handle Hot-Plug scenarios. If there is already a peripheral 1638b8a9b1dSJustin T. Gibbs * of our type assigned to this path, we are likely waiting for 1648b8a9b1dSJustin T. Gibbs * final close on an old, invalidated, peripheral. If this is 1658b8a9b1dSJustin T. Gibbs * the case, queue up a deferred call to the peripheral's async 16674c91ec5SJustin T. Gibbs * handler. If it looks like a mistaken re-allocation, complain. 1678b8a9b1dSJustin T. Gibbs */ 1688b8a9b1dSJustin T. Gibbs if ((periph = cam_periph_find(path, name)) != NULL) { 1698b8a9b1dSJustin T. Gibbs 1708b8a9b1dSJustin T. Gibbs if ((periph->flags & CAM_PERIPH_INVALID) != 0 1718b8a9b1dSJustin T. Gibbs && (periph->flags & CAM_PERIPH_NEW_DEV_FOUND) == 0) { 1728b8a9b1dSJustin T. Gibbs periph->flags |= CAM_PERIPH_NEW_DEV_FOUND; 1738b8a9b1dSJustin T. Gibbs periph->deferred_callback = ac_callback; 1748b8a9b1dSJustin T. Gibbs periph->deferred_ac = code; 1758b8a9b1dSJustin T. Gibbs return (CAM_REQ_INPROG); 1768b8a9b1dSJustin T. Gibbs } else { 1778b8a9b1dSJustin T. Gibbs printf("cam_periph_alloc: attempt to re-allocate " 1788900f4b8SKenneth D. Merry "valid device %s%d rejected flags %#x " 1798900f4b8SKenneth D. Merry "refcount %d\n", periph->periph_name, 1808900f4b8SKenneth D. Merry periph->unit_number, periph->flags, 1818900f4b8SKenneth D. Merry periph->refcount); 1828b8a9b1dSJustin T. Gibbs } 1838b8a9b1dSJustin T. Gibbs return (CAM_REQ_INVALID); 1848b8a9b1dSJustin T. Gibbs } 1858b8a9b1dSJustin T. Gibbs 186362abc44STai-hwa Liang periph = (struct cam_periph *)malloc(sizeof(*periph), M_CAMPERIPH, 1878900f4b8SKenneth D. Merry M_NOWAIT|M_ZERO); 1888b8a9b1dSJustin T. Gibbs 1898b8a9b1dSJustin T. Gibbs if (periph == NULL) 1908b8a9b1dSJustin T. Gibbs return (CAM_RESRC_UNAVAIL); 1918b8a9b1dSJustin T. Gibbs 1928b8a9b1dSJustin T. Gibbs init_level++; 1938b8a9b1dSJustin T. Gibbs 1948b8a9b1dSJustin T. Gibbs 1952b83592fSScott Long sim = xpt_path_sim(path); 1968b8a9b1dSJustin T. Gibbs path_id = xpt_path_path_id(path); 1978b8a9b1dSJustin T. Gibbs target_id = xpt_path_target_id(path); 1988b8a9b1dSJustin T. Gibbs lun_id = xpt_path_lun_id(path); 1998b8a9b1dSJustin T. Gibbs periph->periph_start = periph_start; 2008b8a9b1dSJustin T. Gibbs periph->periph_dtor = periph_dtor; 201ee9c90c7SKenneth D. Merry periph->periph_oninval = periph_oninvalidate; 2028b8a9b1dSJustin T. Gibbs periph->type = type; 2038b8a9b1dSJustin T. Gibbs periph->periph_name = name; 204227d67aaSAlexander Motin periph->scheduled_priority = CAM_PRIORITY_NONE; 2058b8a9b1dSJustin T. Gibbs periph->immediate_priority = CAM_PRIORITY_NONE; 206d38c0e53SAlexander Motin periph->refcount = 1; /* Dropped by invalidation. */ 2072b83592fSScott Long periph->sim = sim; 2088b8a9b1dSJustin T. Gibbs SLIST_INIT(&periph->ccb_list); 2098b8a9b1dSJustin T. Gibbs status = xpt_create_path(&path, periph, path_id, target_id, lun_id); 2108b8a9b1dSJustin T. Gibbs if (status != CAM_REQ_CMP) 2118b8a9b1dSJustin T. Gibbs goto failure; 2128b8a9b1dSJustin T. Gibbs periph->path = path; 2138b8a9b1dSJustin T. Gibbs 214f1e2546aSMatt Jacob xpt_lock_buses(); 215f1e2546aSMatt Jacob for (p_drv = periph_drivers; *p_drv != NULL; p_drv++) { 216f1e2546aSMatt Jacob if (strcmp((*p_drv)->driver_name, name) == 0) 217f1e2546aSMatt Jacob break; 218f1e2546aSMatt Jacob } 219f1e2546aSMatt Jacob if (*p_drv == NULL) { 220f1e2546aSMatt Jacob printf("cam_periph_alloc: invalid periph name '%s'\n", name); 2219e0d30e2SEdward Tomasz Napierala xpt_unlock_buses(); 222f1e2546aSMatt Jacob xpt_free_path(periph->path); 223f1e2546aSMatt Jacob free(periph, M_CAMPERIPH); 224f1e2546aSMatt Jacob return (CAM_REQ_INVALID); 225f1e2546aSMatt Jacob } 226f1e2546aSMatt Jacob periph->unit_number = camperiphunit(*p_drv, path_id, target_id, lun_id); 2278b8a9b1dSJustin T. Gibbs cur_periph = TAILQ_FIRST(&(*p_drv)->units); 2288b8a9b1dSJustin T. Gibbs while (cur_periph != NULL 2298b8a9b1dSJustin T. Gibbs && cur_periph->unit_number < periph->unit_number) 2308b8a9b1dSJustin T. Gibbs cur_periph = TAILQ_NEXT(cur_periph, unit_links); 231f1e2546aSMatt Jacob if (cur_periph != NULL) { 232f1e2546aSMatt Jacob KASSERT(cur_periph->unit_number != periph->unit_number, ("duplicate units on periph list")); 2338b8a9b1dSJustin T. Gibbs TAILQ_INSERT_BEFORE(cur_periph, periph, unit_links); 234f1e2546aSMatt Jacob } else { 2358b8a9b1dSJustin T. Gibbs TAILQ_INSERT_TAIL(&(*p_drv)->units, periph, unit_links); 2368b8a9b1dSJustin T. Gibbs (*p_drv)->generation++; 2378b8a9b1dSJustin T. Gibbs } 238f1e2546aSMatt Jacob xpt_unlock_buses(); 239f1e2546aSMatt Jacob 240f1e2546aSMatt Jacob init_level++; 241f1e2546aSMatt Jacob 242f1e2546aSMatt Jacob status = xpt_add_periph(periph); 243f1e2546aSMatt Jacob if (status != CAM_REQ_CMP) 244f1e2546aSMatt Jacob goto failure; 2458b8a9b1dSJustin T. Gibbs 2468b8a9b1dSJustin T. Gibbs init_level++; 24722c7d606SAlexander Motin CAM_DEBUG(periph->path, CAM_DEBUG_INFO, ("Periph created\n")); 2488b8a9b1dSJustin T. Gibbs 2498b8a9b1dSJustin T. Gibbs status = periph_ctor(periph, arg); 2508b8a9b1dSJustin T. Gibbs 2518b8a9b1dSJustin T. Gibbs if (status == CAM_REQ_CMP) 2528b8a9b1dSJustin T. Gibbs init_level++; 2538b8a9b1dSJustin T. Gibbs 2548b8a9b1dSJustin T. Gibbs failure: 2558b8a9b1dSJustin T. Gibbs switch (init_level) { 2568b8a9b1dSJustin T. Gibbs case 4: 2578b8a9b1dSJustin T. Gibbs /* Initialized successfully */ 2588b8a9b1dSJustin T. Gibbs break; 2598b8a9b1dSJustin T. Gibbs case 3: 26022c7d606SAlexander Motin CAM_DEBUG(periph->path, CAM_DEBUG_INFO, ("Periph destroyed\n")); 261a29779e8SAlexander Motin xpt_remove_periph(periph); 2627379c88fSPoul-Henning Kamp /* FALLTHROUGH */ 2638b8a9b1dSJustin T. Gibbs case 2: 264f1e2546aSMatt Jacob xpt_lock_buses(); 265f1e2546aSMatt Jacob TAILQ_REMOVE(&(*p_drv)->units, periph, unit_links); 266f1e2546aSMatt Jacob xpt_unlock_buses(); 2678b8a9b1dSJustin T. Gibbs xpt_free_path(periph->path); 2687379c88fSPoul-Henning Kamp /* FALLTHROUGH */ 2698b8a9b1dSJustin T. Gibbs case 1: 270362abc44STai-hwa Liang free(periph, M_CAMPERIPH); 2717379c88fSPoul-Henning Kamp /* FALLTHROUGH */ 2728b8a9b1dSJustin T. Gibbs case 0: 2738b8a9b1dSJustin T. Gibbs /* No cleanup to perform. */ 2748b8a9b1dSJustin T. Gibbs break; 2758b8a9b1dSJustin T. Gibbs default: 276905cb85dSKenneth D. Merry panic("%s: Unknown init level", __func__); 2778b8a9b1dSJustin T. Gibbs } 2788b8a9b1dSJustin T. Gibbs return(status); 2798b8a9b1dSJustin T. Gibbs } 2808b8a9b1dSJustin T. Gibbs 2818b8a9b1dSJustin T. Gibbs /* 2828b8a9b1dSJustin T. Gibbs * Find a peripheral structure with the specified path, target, lun, 2838b8a9b1dSJustin T. Gibbs * and (optionally) type. If the name is NULL, this function will return 2848b8a9b1dSJustin T. Gibbs * the first peripheral driver that matches the specified path. 2858b8a9b1dSJustin T. Gibbs */ 2868b8a9b1dSJustin T. Gibbs struct cam_periph * 2878b8a9b1dSJustin T. Gibbs cam_periph_find(struct cam_path *path, char *name) 2888b8a9b1dSJustin T. Gibbs { 2898b8a9b1dSJustin T. Gibbs struct periph_driver **p_drv; 2908b8a9b1dSJustin T. Gibbs struct cam_periph *periph; 2918b8a9b1dSJustin T. Gibbs 29277dc25ccSScott Long xpt_lock_buses(); 2930b7c27b9SPeter Wemm for (p_drv = periph_drivers; *p_drv != NULL; p_drv++) { 2948b8a9b1dSJustin T. Gibbs 2958b8a9b1dSJustin T. Gibbs if (name != NULL && (strcmp((*p_drv)->driver_name, name) != 0)) 2968b8a9b1dSJustin T. Gibbs continue; 2978b8a9b1dSJustin T. Gibbs 29837d40066SPoul-Henning Kamp TAILQ_FOREACH(periph, &(*p_drv)->units, unit_links) { 2998b8a9b1dSJustin T. Gibbs if (xpt_path_comp(periph->path, path) == 0) { 30077dc25ccSScott Long xpt_unlock_buses(); 301227d67aaSAlexander Motin cam_periph_assert(periph, MA_OWNED); 3028b8a9b1dSJustin T. Gibbs return(periph); 3038b8a9b1dSJustin T. Gibbs } 3048b8a9b1dSJustin T. Gibbs } 30577dc25ccSScott Long if (name != NULL) { 30677dc25ccSScott Long xpt_unlock_buses(); 3078b8a9b1dSJustin T. Gibbs return(NULL); 3088b8a9b1dSJustin T. Gibbs } 30977dc25ccSScott Long } 31077dc25ccSScott Long xpt_unlock_buses(); 3118b8a9b1dSJustin T. Gibbs return(NULL); 3128b8a9b1dSJustin T. Gibbs } 3138b8a9b1dSJustin T. Gibbs 3143501942bSJustin T. Gibbs /* 3158900f4b8SKenneth D. Merry * Find peripheral driver instances attached to the specified path. 3163501942bSJustin T. Gibbs */ 3173501942bSJustin T. Gibbs int 3183501942bSJustin T. Gibbs cam_periph_list(struct cam_path *path, struct sbuf *sb) 3193501942bSJustin T. Gibbs { 3208900f4b8SKenneth D. Merry struct sbuf local_sb; 3213501942bSJustin T. Gibbs struct periph_driver **p_drv; 3223501942bSJustin T. Gibbs struct cam_periph *periph; 3233501942bSJustin T. Gibbs int count; 3248900f4b8SKenneth D. Merry int sbuf_alloc_len; 3253501942bSJustin T. Gibbs 3268900f4b8SKenneth D. Merry sbuf_alloc_len = 16; 3278900f4b8SKenneth D. Merry retry: 3288900f4b8SKenneth D. Merry sbuf_new(&local_sb, NULL, sbuf_alloc_len, SBUF_FIXEDLEN); 3293501942bSJustin T. Gibbs count = 0; 3303501942bSJustin T. Gibbs xpt_lock_buses(); 3313501942bSJustin T. Gibbs for (p_drv = periph_drivers; *p_drv != NULL; p_drv++) { 3323501942bSJustin T. Gibbs 3333501942bSJustin T. Gibbs TAILQ_FOREACH(periph, &(*p_drv)->units, unit_links) { 3343501942bSJustin T. Gibbs if (xpt_path_comp(periph->path, path) != 0) 3353501942bSJustin T. Gibbs continue; 3363501942bSJustin T. Gibbs 3378900f4b8SKenneth D. Merry if (sbuf_len(&local_sb) != 0) 3388900f4b8SKenneth D. Merry sbuf_cat(&local_sb, ","); 3393501942bSJustin T. Gibbs 3408900f4b8SKenneth D. Merry sbuf_printf(&local_sb, "%s%d", periph->periph_name, 3413501942bSJustin T. Gibbs periph->unit_number); 3428900f4b8SKenneth D. Merry 3438900f4b8SKenneth D. Merry if (sbuf_error(&local_sb) == ENOMEM) { 3448900f4b8SKenneth D. Merry sbuf_alloc_len *= 2; 3458900f4b8SKenneth D. Merry xpt_unlock_buses(); 3468900f4b8SKenneth D. Merry sbuf_delete(&local_sb); 3478900f4b8SKenneth D. Merry goto retry; 3488900f4b8SKenneth D. Merry } 3493501942bSJustin T. Gibbs count++; 3503501942bSJustin T. Gibbs } 3513501942bSJustin T. Gibbs } 3523501942bSJustin T. Gibbs xpt_unlock_buses(); 3538900f4b8SKenneth D. Merry sbuf_finish(&local_sb); 3548900f4b8SKenneth D. Merry sbuf_cpy(sb, sbuf_data(&local_sb)); 3558900f4b8SKenneth D. Merry sbuf_delete(&local_sb); 3563501942bSJustin T. Gibbs return (count); 3573501942bSJustin T. Gibbs } 3583501942bSJustin T. Gibbs 3598b8a9b1dSJustin T. Gibbs cam_status 3608b8a9b1dSJustin T. Gibbs cam_periph_acquire(struct cam_periph *periph) 3618b8a9b1dSJustin T. Gibbs { 3628900f4b8SKenneth D. Merry cam_status status; 3638b8a9b1dSJustin T. Gibbs 3648900f4b8SKenneth D. Merry status = CAM_REQ_CMP_ERR; 3658b8a9b1dSJustin T. Gibbs if (periph == NULL) 3668900f4b8SKenneth D. Merry return (status); 3678b8a9b1dSJustin T. Gibbs 3682b83592fSScott Long xpt_lock_buses(); 3698900f4b8SKenneth D. Merry if ((periph->flags & CAM_PERIPH_INVALID) == 0) { 3708b8a9b1dSJustin T. Gibbs periph->refcount++; 3718900f4b8SKenneth D. Merry status = CAM_REQ_CMP; 3728900f4b8SKenneth D. Merry } 3732b83592fSScott Long xpt_unlock_buses(); 3748b8a9b1dSJustin T. Gibbs 3758900f4b8SKenneth D. Merry return (status); 3768900f4b8SKenneth D. Merry } 3778900f4b8SKenneth D. Merry 3788900f4b8SKenneth D. Merry void 3798900f4b8SKenneth D. Merry cam_periph_release_locked_buses(struct cam_periph *periph) 3808900f4b8SKenneth D. Merry { 381dcdf6e74SAlexander Motin 382227d67aaSAlexander Motin cam_periph_assert(periph, MA_OWNED); 383dcdf6e74SAlexander Motin KASSERT(periph->refcount >= 1, ("periph->refcount >= 1")); 384d38c0e53SAlexander Motin if (--periph->refcount == 0) 3858900f4b8SKenneth D. Merry camperiphfree(periph); 3868900f4b8SKenneth D. Merry } 3878b8a9b1dSJustin T. Gibbs 3888b8a9b1dSJustin T. Gibbs void 38924ebf566SEdward Tomasz Napierala cam_periph_release_locked(struct cam_periph *periph) 3908b8a9b1dSJustin T. Gibbs { 3918b8a9b1dSJustin T. Gibbs 3928b8a9b1dSJustin T. Gibbs if (periph == NULL) 3938b8a9b1dSJustin T. Gibbs return; 3948b8a9b1dSJustin T. Gibbs 3952b83592fSScott Long xpt_lock_buses(); 3968900f4b8SKenneth D. Merry cam_periph_release_locked_buses(periph); 3972b83592fSScott Long xpt_unlock_buses(); 39824ebf566SEdward Tomasz Napierala } 3998b8a9b1dSJustin T. Gibbs 40024ebf566SEdward Tomasz Napierala void 40124ebf566SEdward Tomasz Napierala cam_periph_release(struct cam_periph *periph) 40224ebf566SEdward Tomasz Napierala { 403227d67aaSAlexander Motin struct mtx *mtx; 40424ebf566SEdward Tomasz Napierala 40524ebf566SEdward Tomasz Napierala if (periph == NULL) 40624ebf566SEdward Tomasz Napierala return; 40724ebf566SEdward Tomasz Napierala 408227d67aaSAlexander Motin cam_periph_assert(periph, MA_NOTOWNED); 409227d67aaSAlexander Motin mtx = cam_periph_mtx(periph); 410227d67aaSAlexander Motin mtx_lock(mtx); 41124ebf566SEdward Tomasz Napierala cam_periph_release_locked(periph); 412227d67aaSAlexander Motin mtx_unlock(mtx); 4138b8a9b1dSJustin T. Gibbs } 4148b8a9b1dSJustin T. Gibbs 4152b83592fSScott Long int 4162b83592fSScott Long cam_periph_hold(struct cam_periph *periph, int priority) 4172b83592fSScott Long { 4182b83592fSScott Long int error; 4192b83592fSScott Long 4202b83592fSScott Long /* 4212b83592fSScott Long * Increment the reference count on the peripheral 4222b83592fSScott Long * while we wait for our lock attempt to succeed 4232b83592fSScott Long * to ensure the peripheral doesn't disappear out 4242b83592fSScott Long * from user us while we sleep. 4252b83592fSScott Long */ 4262b83592fSScott Long 4272b83592fSScott Long if (cam_periph_acquire(periph) != CAM_REQ_CMP) 4282b83592fSScott Long return (ENXIO); 4292b83592fSScott Long 430227d67aaSAlexander Motin cam_periph_assert(periph, MA_OWNED); 4312b83592fSScott Long while ((periph->flags & CAM_PERIPH_LOCKED) != 0) { 4322b83592fSScott Long periph->flags |= CAM_PERIPH_LOCK_WANTED; 433227d67aaSAlexander Motin if ((error = cam_periph_sleep(periph, periph, priority, 4341fa738c2SJohn Baldwin "caplck", 0)) != 0) { 43524ebf566SEdward Tomasz Napierala cam_periph_release_locked(periph); 4362b83592fSScott Long return (error); 4372b83592fSScott Long } 438aed9c88cSAlexander Motin if (periph->flags & CAM_PERIPH_INVALID) { 439aed9c88cSAlexander Motin cam_periph_release_locked(periph); 440aed9c88cSAlexander Motin return (ENXIO); 441aed9c88cSAlexander Motin } 4422b83592fSScott Long } 4432b83592fSScott Long 4442b83592fSScott Long periph->flags |= CAM_PERIPH_LOCKED; 4452b83592fSScott Long return (0); 4462b83592fSScott Long } 4472b83592fSScott Long 4482b83592fSScott Long void 4492b83592fSScott Long cam_periph_unhold(struct cam_periph *periph) 4502b83592fSScott Long { 4512b83592fSScott Long 452227d67aaSAlexander Motin cam_periph_assert(periph, MA_OWNED); 4532b83592fSScott Long 4542b83592fSScott Long periph->flags &= ~CAM_PERIPH_LOCKED; 4552b83592fSScott Long if ((periph->flags & CAM_PERIPH_LOCK_WANTED) != 0) { 4562b83592fSScott Long periph->flags &= ~CAM_PERIPH_LOCK_WANTED; 4572b83592fSScott Long wakeup(periph); 4582b83592fSScott Long } 4592b83592fSScott Long 46024ebf566SEdward Tomasz Napierala cam_periph_release_locked(periph); 4612b83592fSScott Long } 4622b83592fSScott Long 4638b8a9b1dSJustin T. Gibbs /* 4648b8a9b1dSJustin T. Gibbs * Look for the next unit number that is not currently in use for this 4658b8a9b1dSJustin T. Gibbs * peripheral type starting at "newunit". Also exclude unit numbers that 4668b8a9b1dSJustin T. Gibbs * are reserved by for future "hardwiring" unless we already know that this 4678b8a9b1dSJustin T. Gibbs * is a potential wired device. Only assume that the device is "wired" the 4688b8a9b1dSJustin T. Gibbs * first time through the loop since after that we'll be looking at unit 4698b8a9b1dSJustin T. Gibbs * numbers that did not match a wiring entry. 4708b8a9b1dSJustin T. Gibbs */ 4718b8a9b1dSJustin T. Gibbs static u_int 472501468a5SKenneth D. Merry camperiphnextunit(struct periph_driver *p_drv, u_int newunit, int wired, 473501468a5SKenneth D. Merry path_id_t pathid, target_id_t target, lun_id_t lun) 4748b8a9b1dSJustin T. Gibbs { 4758b8a9b1dSJustin T. Gibbs struct cam_periph *periph; 4762398f0cdSPeter Wemm char *periph_name; 4772398f0cdSPeter Wemm int i, val, dunit, r; 4782398f0cdSPeter Wemm const char *dname, *strval; 4798b8a9b1dSJustin T. Gibbs 4808b8a9b1dSJustin T. Gibbs periph_name = p_drv->driver_name; 4818b8a9b1dSJustin T. Gibbs for (;;newunit++) { 4828b8a9b1dSJustin T. Gibbs 4838b8a9b1dSJustin T. Gibbs for (periph = TAILQ_FIRST(&p_drv->units); 4848b8a9b1dSJustin T. Gibbs periph != NULL && periph->unit_number != newunit; 4858b8a9b1dSJustin T. Gibbs periph = TAILQ_NEXT(periph, unit_links)) 4868b8a9b1dSJustin T. Gibbs ; 4878b8a9b1dSJustin T. Gibbs 4888b8a9b1dSJustin T. Gibbs if (periph != NULL && periph->unit_number == newunit) { 4898b8a9b1dSJustin T. Gibbs if (wired != 0) { 490f0d9af51SMatt Jacob xpt_print(periph->path, "Duplicate Wired " 491f0d9af51SMatt Jacob "Device entry!\n"); 492f0d9af51SMatt Jacob xpt_print(periph->path, "Second device (%s " 493f0d9af51SMatt Jacob "device at scbus%d target %d lun %d) will " 494f0d9af51SMatt Jacob "not be wired\n", periph_name, pathid, 495f0d9af51SMatt Jacob target, lun); 4968b8a9b1dSJustin T. Gibbs wired = 0; 4978b8a9b1dSJustin T. Gibbs } 4988b8a9b1dSJustin T. Gibbs continue; 4998b8a9b1dSJustin T. Gibbs } 50075f51904SPeter Wemm if (wired) 50175f51904SPeter Wemm break; 5028b8a9b1dSJustin T. Gibbs 5038b8a9b1dSJustin T. Gibbs /* 5048b8a9b1dSJustin T. Gibbs * Don't match entries like "da 4" as a wired down 5058b8a9b1dSJustin T. Gibbs * device, but do match entries like "da 4 target 5" 5068b8a9b1dSJustin T. Gibbs * or even "da 4 scbus 1". 5078b8a9b1dSJustin T. Gibbs */ 5082398f0cdSPeter Wemm i = 0; 5092398f0cdSPeter Wemm dname = periph_name; 5102398f0cdSPeter Wemm for (;;) { 5112398f0cdSPeter Wemm r = resource_find_dev(&i, dname, &dunit, NULL, NULL); 5122398f0cdSPeter Wemm if (r != 0) 5132398f0cdSPeter Wemm break; 51475f51904SPeter Wemm /* if no "target" and no specific scbus, skip */ 51575f51904SPeter Wemm if (resource_int_value(dname, dunit, "target", &val) && 51675f51904SPeter Wemm (resource_string_value(dname, dunit, "at",&strval)|| 51775f51904SPeter Wemm strcmp(strval, "scbus") == 0)) 51875f51904SPeter Wemm continue; 51975f51904SPeter Wemm if (newunit == dunit) 5208b8a9b1dSJustin T. Gibbs break; 5218b8a9b1dSJustin T. Gibbs } 5222398f0cdSPeter Wemm if (r != 0) 5238b8a9b1dSJustin T. Gibbs break; 5248b8a9b1dSJustin T. Gibbs } 5258b8a9b1dSJustin T. Gibbs return (newunit); 5268b8a9b1dSJustin T. Gibbs } 5278b8a9b1dSJustin T. Gibbs 5288b8a9b1dSJustin T. Gibbs static u_int 5298b8a9b1dSJustin T. Gibbs camperiphunit(struct periph_driver *p_drv, path_id_t pathid, 5308b8a9b1dSJustin T. Gibbs target_id_t target, lun_id_t lun) 5318b8a9b1dSJustin T. Gibbs { 5328b8a9b1dSJustin T. Gibbs u_int unit; 533c1b81613SPeter Wemm int wired, i, val, dunit; 5342398f0cdSPeter Wemm const char *dname, *strval; 5352398f0cdSPeter Wemm char pathbuf[32], *periph_name; 5368b8a9b1dSJustin T. Gibbs 53775f51904SPeter Wemm periph_name = p_drv->driver_name; 53875f51904SPeter Wemm snprintf(pathbuf, sizeof(pathbuf), "scbus%d", pathid); 539c1b81613SPeter Wemm unit = 0; 5402398f0cdSPeter Wemm i = 0; 5412398f0cdSPeter Wemm dname = periph_name; 542c1b81613SPeter Wemm for (wired = 0; resource_find_dev(&i, dname, &dunit, NULL, NULL) == 0; 543c1b81613SPeter Wemm wired = 0) { 54475f51904SPeter Wemm if (resource_string_value(dname, dunit, "at", &strval) == 0) { 54575f51904SPeter Wemm if (strcmp(strval, pathbuf) != 0) 5468b8a9b1dSJustin T. Gibbs continue; 547c1b81613SPeter Wemm wired++; 5488b8a9b1dSJustin T. Gibbs } 54975f51904SPeter Wemm if (resource_int_value(dname, dunit, "target", &val) == 0) { 55075f51904SPeter Wemm if (val != target) 5518b8a9b1dSJustin T. Gibbs continue; 552c1b81613SPeter Wemm wired++; 5538b8a9b1dSJustin T. Gibbs } 55475f51904SPeter Wemm if (resource_int_value(dname, dunit, "lun", &val) == 0) { 55575f51904SPeter Wemm if (val != lun) 5568b8a9b1dSJustin T. Gibbs continue; 557c1b81613SPeter Wemm wired++; 5588b8a9b1dSJustin T. Gibbs } 559c1b81613SPeter Wemm if (wired != 0) { 56075f51904SPeter Wemm unit = dunit; 5618b8a9b1dSJustin T. Gibbs break; 5628b8a9b1dSJustin T. Gibbs } 5638b8a9b1dSJustin T. Gibbs } 5648b8a9b1dSJustin T. Gibbs 5658b8a9b1dSJustin T. Gibbs /* 5668b8a9b1dSJustin T. Gibbs * Either start from 0 looking for the next unit or from 56775f51904SPeter Wemm * the unit number given in the resource config. This way, 5688b8a9b1dSJustin T. Gibbs * if we have wildcard matches, we don't return the same 5698b8a9b1dSJustin T. Gibbs * unit number twice. 5708b8a9b1dSJustin T. Gibbs */ 571c1b81613SPeter Wemm unit = camperiphnextunit(p_drv, unit, wired, pathid, target, lun); 5728b8a9b1dSJustin T. Gibbs 5738b8a9b1dSJustin T. Gibbs return (unit); 5748b8a9b1dSJustin T. Gibbs } 5758b8a9b1dSJustin T. Gibbs 5768b8a9b1dSJustin T. Gibbs void 5778b8a9b1dSJustin T. Gibbs cam_periph_invalidate(struct cam_periph *periph) 5788b8a9b1dSJustin T. Gibbs { 5798b8a9b1dSJustin T. Gibbs 580227d67aaSAlexander Motin cam_periph_assert(periph, MA_OWNED); 581ee9c90c7SKenneth D. Merry /* 582ee9c90c7SKenneth D. Merry * We only call this routine the first time a peripheral is 58377dc25ccSScott Long * invalidated. 584ee9c90c7SKenneth D. Merry */ 585d38c0e53SAlexander Motin if ((periph->flags & CAM_PERIPH_INVALID) != 0) 586d38c0e53SAlexander Motin return; 587ee9c90c7SKenneth D. Merry 588d38c0e53SAlexander Motin CAM_DEBUG(periph->path, CAM_DEBUG_INFO, ("Periph invalidated\n")); 5898d36a71bSAlexander Motin if (periph->flags & CAM_PERIPH_ANNOUNCED) 5908d36a71bSAlexander Motin xpt_denounce_periph(periph); 5918b8a9b1dSJustin T. Gibbs periph->flags |= CAM_PERIPH_INVALID; 5928b8a9b1dSJustin T. Gibbs periph->flags &= ~CAM_PERIPH_NEW_DEV_FOUND; 593d38c0e53SAlexander Motin if (periph->periph_oninval != NULL) 594d38c0e53SAlexander Motin periph->periph_oninval(periph); 595d38c0e53SAlexander Motin cam_periph_release_locked(periph); 5968b8a9b1dSJustin T. Gibbs } 5978b8a9b1dSJustin T. Gibbs 5988b8a9b1dSJustin T. Gibbs static void 5998b8a9b1dSJustin T. Gibbs camperiphfree(struct cam_periph *periph) 6008b8a9b1dSJustin T. Gibbs { 6018b8a9b1dSJustin T. Gibbs struct periph_driver **p_drv; 6028b8a9b1dSJustin T. Gibbs 603227d67aaSAlexander Motin cam_periph_assert(periph, MA_OWNED); 604227d67aaSAlexander Motin KASSERT(periph->periph_allocating == 0, ("%s%d: freed while allocating", 605227d67aaSAlexander Motin periph->periph_name, periph->unit_number)); 6060b7c27b9SPeter Wemm for (p_drv = periph_drivers; *p_drv != NULL; p_drv++) { 6078b8a9b1dSJustin T. Gibbs if (strcmp((*p_drv)->driver_name, periph->periph_name) == 0) 6088b8a9b1dSJustin T. Gibbs break; 6098b8a9b1dSJustin T. Gibbs } 610661658a6SScott Long if (*p_drv == NULL) { 611661658a6SScott Long printf("camperiphfree: attempt to free non-existant periph\n"); 612661658a6SScott Long return; 613661658a6SScott Long } 6148b8a9b1dSJustin T. Gibbs 615ea37f519SKenneth D. Merry /* 61633a38f74SKenneth D. Merry * We need to set this flag before dropping the topology lock, to 61733a38f74SKenneth D. Merry * let anyone who is traversing the list that this peripheral is 61833a38f74SKenneth D. Merry * about to be freed, and there will be no more reference count 61933a38f74SKenneth D. Merry * checks. 62033a38f74SKenneth D. Merry */ 62133a38f74SKenneth D. Merry periph->flags |= CAM_PERIPH_FREE; 62233a38f74SKenneth D. Merry 62333a38f74SKenneth D. Merry /* 624ea37f519SKenneth D. Merry * The peripheral destructor semantics dictate calling with only the 625ea37f519SKenneth D. Merry * SIM mutex held. Since it might sleep, it should not be called 626ea37f519SKenneth D. Merry * with the topology lock held. 627ea37f519SKenneth D. Merry */ 628f3548746SScott Long xpt_unlock_buses(); 6298b8a9b1dSJustin T. Gibbs 630ea37f519SKenneth D. Merry /* 631ea37f519SKenneth D. Merry * We need to call the peripheral destructor prior to removing the 632ea37f519SKenneth D. Merry * peripheral from the list. Otherwise, we risk running into a 633ea37f519SKenneth D. Merry * scenario where the peripheral unit number may get reused 634ea37f519SKenneth D. Merry * (because it has been removed from the list), but some resources 635ea37f519SKenneth D. Merry * used by the peripheral are still hanging around. In particular, 636ea37f519SKenneth D. Merry * the devfs nodes used by some peripherals like the pass(4) driver 637ea37f519SKenneth D. Merry * aren't fully cleaned up until the destructor is run. If the 638ea37f519SKenneth D. Merry * unit number is reused before the devfs instance is fully gone, 639ea37f519SKenneth D. Merry * devfs will panic. 640ea37f519SKenneth D. Merry */ 641f3548746SScott Long if (periph->periph_dtor != NULL) 642f3548746SScott Long periph->periph_dtor(periph); 643ea37f519SKenneth D. Merry 644ea37f519SKenneth D. Merry /* 645ea37f519SKenneth D. Merry * The peripheral list is protected by the topology lock. 646ea37f519SKenneth D. Merry */ 647ea37f519SKenneth D. Merry xpt_lock_buses(); 648ea37f519SKenneth D. Merry 649ea37f519SKenneth D. Merry TAILQ_REMOVE(&(*p_drv)->units, periph, unit_links); 650ea37f519SKenneth D. Merry (*p_drv)->generation++; 651ea37f519SKenneth D. Merry 652a29779e8SAlexander Motin xpt_remove_periph(periph); 653ea37f519SKenneth D. Merry 654ea37f519SKenneth D. Merry xpt_unlock_buses(); 6558d36a71bSAlexander Motin if (periph->flags & CAM_PERIPH_ANNOUNCED) { 6568d36a71bSAlexander Motin xpt_print(periph->path, "Periph destroyed\n"); 6578d36a71bSAlexander Motin } else 65822c7d606SAlexander Motin CAM_DEBUG(periph->path, CAM_DEBUG_INFO, ("Periph destroyed\n")); 6598b8a9b1dSJustin T. Gibbs 6608b8a9b1dSJustin T. Gibbs if (periph->flags & CAM_PERIPH_NEW_DEV_FOUND) { 6618b8a9b1dSJustin T. Gibbs union ccb ccb; 6628b8a9b1dSJustin T. Gibbs void *arg; 6638b8a9b1dSJustin T. Gibbs 6648b8a9b1dSJustin T. Gibbs switch (periph->deferred_ac) { 6658b8a9b1dSJustin T. Gibbs case AC_FOUND_DEVICE: 6668b8a9b1dSJustin T. Gibbs ccb.ccb_h.func_code = XPT_GDEV_TYPE; 667bbfa4aa1SAlexander Motin xpt_setup_ccb(&ccb.ccb_h, periph->path, CAM_PRIORITY_NORMAL); 6688b8a9b1dSJustin T. Gibbs xpt_action(&ccb); 6698b8a9b1dSJustin T. Gibbs arg = &ccb; 6708b8a9b1dSJustin T. Gibbs break; 6718b8a9b1dSJustin T. Gibbs case AC_PATH_REGISTERED: 6728b8a9b1dSJustin T. Gibbs ccb.ccb_h.func_code = XPT_PATH_INQ; 673bbfa4aa1SAlexander Motin xpt_setup_ccb(&ccb.ccb_h, periph->path, CAM_PRIORITY_NORMAL); 6748b8a9b1dSJustin T. Gibbs xpt_action(&ccb); 6758b8a9b1dSJustin T. Gibbs arg = &ccb; 6768b8a9b1dSJustin T. Gibbs break; 6778b8a9b1dSJustin T. Gibbs default: 6788b8a9b1dSJustin T. Gibbs arg = NULL; 6798b8a9b1dSJustin T. Gibbs break; 6808b8a9b1dSJustin T. Gibbs } 6818b8a9b1dSJustin T. Gibbs periph->deferred_callback(NULL, periph->deferred_ac, 6828b8a9b1dSJustin T. Gibbs periph->path, arg); 6838b8a9b1dSJustin T. Gibbs } 6848b8a9b1dSJustin T. Gibbs xpt_free_path(periph->path); 685362abc44STai-hwa Liang free(periph, M_CAMPERIPH); 686f3548746SScott Long xpt_lock_buses(); 6878b8a9b1dSJustin T. Gibbs } 6888b8a9b1dSJustin T. Gibbs 6898b8a9b1dSJustin T. Gibbs /* 6908b8a9b1dSJustin T. Gibbs * Map user virtual pointers into kernel virtual address space, so we can 69195fbded6SScott Long * access the memory. This is now a generic function that centralizes most 69295fbded6SScott Long * of the sanity checks on the data flags, if any. 69395fbded6SScott Long * This also only works for up to MAXPHYS memory. Since we use 6948b8a9b1dSJustin T. Gibbs * buffers to map stuff in and out, we're limited to the buffer size. 6958b8a9b1dSJustin T. Gibbs */ 6968b8a9b1dSJustin T. Gibbs int 6978b8a9b1dSJustin T. Gibbs cam_periph_mapmem(union ccb *ccb, struct cam_periph_map_info *mapinfo) 6988b8a9b1dSJustin T. Gibbs { 6992d5c7e45SMatthew Dillon int numbufs, i, j; 70079d49a06SKenneth D. Merry int flags[CAM_PERIPH_MAXMAPS]; 7018b8a9b1dSJustin T. Gibbs u_int8_t **data_ptrs[CAM_PERIPH_MAXMAPS]; 7028b8a9b1dSJustin T. Gibbs u_int32_t lengths[CAM_PERIPH_MAXMAPS]; 7038b8a9b1dSJustin T. Gibbs u_int32_t dirs[CAM_PERIPH_MAXMAPS]; 70452c9ce25SScott Long /* Some controllers may not be able to handle more data. */ 70552c9ce25SScott Long size_t maxmap = DFLTPHYS; 7068b8a9b1dSJustin T. Gibbs 7078b8a9b1dSJustin T. Gibbs switch(ccb->ccb_h.func_code) { 7088b8a9b1dSJustin T. Gibbs case XPT_DEV_MATCH: 7098b8a9b1dSJustin T. Gibbs if (ccb->cdm.match_buf_len == 0) { 7108b8a9b1dSJustin T. Gibbs printf("cam_periph_mapmem: invalid match buffer " 7118b8a9b1dSJustin T. Gibbs "length 0\n"); 7128b8a9b1dSJustin T. Gibbs return(EINVAL); 7138b8a9b1dSJustin T. Gibbs } 7148b8a9b1dSJustin T. Gibbs if (ccb->cdm.pattern_buf_len > 0) { 7158b8a9b1dSJustin T. Gibbs data_ptrs[0] = (u_int8_t **)&ccb->cdm.patterns; 7168b8a9b1dSJustin T. Gibbs lengths[0] = ccb->cdm.pattern_buf_len; 7178b8a9b1dSJustin T. Gibbs dirs[0] = CAM_DIR_OUT; 7188b8a9b1dSJustin T. Gibbs data_ptrs[1] = (u_int8_t **)&ccb->cdm.matches; 7198b8a9b1dSJustin T. Gibbs lengths[1] = ccb->cdm.match_buf_len; 7208b8a9b1dSJustin T. Gibbs dirs[1] = CAM_DIR_IN; 7218b8a9b1dSJustin T. Gibbs numbufs = 2; 7228b8a9b1dSJustin T. Gibbs } else { 7238b8a9b1dSJustin T. Gibbs data_ptrs[0] = (u_int8_t **)&ccb->cdm.matches; 7248b8a9b1dSJustin T. Gibbs lengths[0] = ccb->cdm.match_buf_len; 7258b8a9b1dSJustin T. Gibbs dirs[0] = CAM_DIR_IN; 7268b8a9b1dSJustin T. Gibbs numbufs = 1; 7278b8a9b1dSJustin T. Gibbs } 72852c9ce25SScott Long /* 72952c9ce25SScott Long * This request will not go to the hardware, no reason 73052c9ce25SScott Long * to be so strict. vmapbuf() is able to map up to MAXPHYS. 73152c9ce25SScott Long */ 73252c9ce25SScott Long maxmap = MAXPHYS; 7338b8a9b1dSJustin T. Gibbs break; 7348b8a9b1dSJustin T. Gibbs case XPT_SCSI_IO: 73587cfaf0eSJustin T. Gibbs case XPT_CONT_TARGET_IO: 7368b8a9b1dSJustin T. Gibbs if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_NONE) 7378b8a9b1dSJustin T. Gibbs return(0); 73895fbded6SScott Long if ((ccb->ccb_h.flags & CAM_DATA_MASK) != CAM_DATA_VADDR) 73995fbded6SScott Long return (EINVAL); 7408b8a9b1dSJustin T. Gibbs data_ptrs[0] = &ccb->csio.data_ptr; 74179d49a06SKenneth D. Merry lengths[0] = ccb->csio.dxfer_len; 7428b8a9b1dSJustin T. Gibbs dirs[0] = ccb->ccb_h.flags & CAM_DIR_MASK; 7438b8a9b1dSJustin T. Gibbs numbufs = 1; 7448b8a9b1dSJustin T. Gibbs break; 74552c9ce25SScott Long case XPT_ATA_IO: 74652c9ce25SScott Long if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_NONE) 74752c9ce25SScott Long return(0); 74895fbded6SScott Long if ((ccb->ccb_h.flags & CAM_DATA_MASK) != CAM_DATA_VADDR) 74995fbded6SScott Long return (EINVAL); 75052c9ce25SScott Long data_ptrs[0] = &ccb->ataio.data_ptr; 75152c9ce25SScott Long lengths[0] = ccb->ataio.dxfer_len; 75252c9ce25SScott Long dirs[0] = ccb->ccb_h.flags & CAM_DIR_MASK; 75352c9ce25SScott Long numbufs = 1; 75452c9ce25SScott Long break; 75506e79492SKenneth D. Merry case XPT_SMP_IO: 75606e79492SKenneth D. Merry data_ptrs[0] = &ccb->smpio.smp_request; 75706e79492SKenneth D. Merry lengths[0] = ccb->smpio.smp_request_len; 75806e79492SKenneth D. Merry dirs[0] = CAM_DIR_OUT; 75906e79492SKenneth D. Merry data_ptrs[1] = &ccb->smpio.smp_response; 76006e79492SKenneth D. Merry lengths[1] = ccb->smpio.smp_response_len; 76106e79492SKenneth D. Merry dirs[1] = CAM_DIR_IN; 76206e79492SKenneth D. Merry numbufs = 2; 76306e79492SKenneth D. Merry break; 7643501942bSJustin T. Gibbs case XPT_DEV_ADVINFO: 7653501942bSJustin T. Gibbs if (ccb->cdai.bufsiz == 0) 7667c103ddeSKenneth D. Merry return (0); 7677c103ddeSKenneth D. Merry 7683501942bSJustin T. Gibbs data_ptrs[0] = (uint8_t **)&ccb->cdai.buf; 7693501942bSJustin T. Gibbs lengths[0] = ccb->cdai.bufsiz; 77006e79492SKenneth D. Merry dirs[0] = CAM_DIR_IN; 77106e79492SKenneth D. Merry numbufs = 1; 7727c103ddeSKenneth D. Merry 7737c103ddeSKenneth D. Merry /* 7747c103ddeSKenneth D. Merry * This request will not go to the hardware, no reason 7757c103ddeSKenneth D. Merry * to be so strict. vmapbuf() is able to map up to MAXPHYS. 7767c103ddeSKenneth D. Merry */ 7777c103ddeSKenneth D. Merry maxmap = MAXPHYS; 77806e79492SKenneth D. Merry break; 7798b8a9b1dSJustin T. Gibbs default: 7808b8a9b1dSJustin T. Gibbs return(EINVAL); 7818b8a9b1dSJustin T. Gibbs break; /* NOTREACHED */ 7828b8a9b1dSJustin T. Gibbs } 7838b8a9b1dSJustin T. Gibbs 7848b8a9b1dSJustin T. Gibbs /* 78579d49a06SKenneth D. Merry * Check the transfer length and permissions first, so we don't 78679d49a06SKenneth D. Merry * have to unmap any previously mapped buffers. 7878b8a9b1dSJustin T. Gibbs */ 7888b8a9b1dSJustin T. Gibbs for (i = 0; i < numbufs; i++) { 78979d49a06SKenneth D. Merry 79079d49a06SKenneth D. Merry flags[i] = 0; 79179d49a06SKenneth D. Merry 79279d49a06SKenneth D. Merry /* 79379d49a06SKenneth D. Merry * The userland data pointer passed in may not be page 79479d49a06SKenneth D. Merry * aligned. vmapbuf() truncates the address to a page 79579d49a06SKenneth D. Merry * boundary, so if the address isn't page aligned, we'll 79679d49a06SKenneth D. Merry * need enough space for the given transfer length, plus 79779d49a06SKenneth D. Merry * whatever extra space is necessary to make it to the page 79879d49a06SKenneth D. Merry * boundary. 79979d49a06SKenneth D. Merry */ 80079d49a06SKenneth D. Merry if ((lengths[i] + 80152c9ce25SScott Long (((vm_offset_t)(*data_ptrs[i])) & PAGE_MASK)) > maxmap){ 8026d7b539aSPeter Wemm printf("cam_periph_mapmem: attempt to map %lu bytes, " 80352c9ce25SScott Long "which is greater than %lu\n", 8046d7b539aSPeter Wemm (long)(lengths[i] + 8056d7b539aSPeter Wemm (((vm_offset_t)(*data_ptrs[i])) & PAGE_MASK)), 80652c9ce25SScott Long (u_long)maxmap); 80779d49a06SKenneth D. Merry return(E2BIG); 80879d49a06SKenneth D. Merry } 8098b8a9b1dSJustin T. Gibbs 810edd24ab7SKenneth D. Merry if (dirs[i] & CAM_DIR_OUT) { 81121144e3bSPoul-Henning Kamp flags[i] = BIO_WRITE; 8128b8a9b1dSJustin T. Gibbs } 8138b8a9b1dSJustin T. Gibbs 814edd24ab7SKenneth D. Merry if (dirs[i] & CAM_DIR_IN) { 81521144e3bSPoul-Henning Kamp flags[i] = BIO_READ; 8168b8a9b1dSJustin T. Gibbs } 8178b8a9b1dSJustin T. Gibbs 81879d49a06SKenneth D. Merry } 81979d49a06SKenneth D. Merry 82079d49a06SKenneth D. Merry /* 82195fbded6SScott Long * This keeps the the kernel stack of current thread from getting 82295fbded6SScott Long * swapped. In low-memory situations where the kernel stack might 82395fbded6SScott Long * otherwise get swapped out, this holds it and allows the thread 82495fbded6SScott Long * to make progress and release the kernel mapped pages sooner. 82595fbded6SScott Long * 82679d49a06SKenneth D. Merry * XXX KDM should I use P_NOSWAP instead? 82779d49a06SKenneth D. Merry */ 8280cbbb7bfSPeter Wemm PHOLD(curproc); 82979d49a06SKenneth D. Merry 83079d49a06SKenneth D. Merry for (i = 0; i < numbufs; i++) { 8318b8a9b1dSJustin T. Gibbs /* 8328b8a9b1dSJustin T. Gibbs * Get the buffer. 8338b8a9b1dSJustin T. Gibbs */ 8341c7c3c6aSMatthew Dillon mapinfo->bp[i] = getpbuf(NULL); 8358b8a9b1dSJustin T. Gibbs 8368b8a9b1dSJustin T. Gibbs /* save the buffer's data address */ 8378b8a9b1dSJustin T. Gibbs mapinfo->bp[i]->b_saveaddr = mapinfo->bp[i]->b_data; 8388b8a9b1dSJustin T. Gibbs 8398b8a9b1dSJustin T. Gibbs /* put our pointer in the data slot */ 8408b8a9b1dSJustin T. Gibbs mapinfo->bp[i]->b_data = *data_ptrs[i]; 8418b8a9b1dSJustin T. Gibbs 84252c9ce25SScott Long /* set the transfer length, we know it's < MAXPHYS */ 8438b8a9b1dSJustin T. Gibbs mapinfo->bp[i]->b_bufsize = lengths[i]; 8448b8a9b1dSJustin T. Gibbs 84521144e3bSPoul-Henning Kamp /* set the direction */ 84621144e3bSPoul-Henning Kamp mapinfo->bp[i]->b_iocmd = flags[i]; 8478b8a9b1dSJustin T. Gibbs 8482d5c7e45SMatthew Dillon /* 8492d5c7e45SMatthew Dillon * Map the buffer into kernel memory. 8502d5c7e45SMatthew Dillon * 8512d5c7e45SMatthew Dillon * Note that useracc() alone is not a sufficient test. 8522d5c7e45SMatthew Dillon * vmapbuf() can still fail due to a smaller file mapped 8532d5c7e45SMatthew Dillon * into a larger area of VM, or if userland races against 8542d5c7e45SMatthew Dillon * vmapbuf() after the useracc() check. 8552d5c7e45SMatthew Dillon */ 856e81ff91eSKonstantin Belousov if (vmapbuf(mapinfo->bp[i], 1) < 0) { 8572d5c7e45SMatthew Dillon for (j = 0; j < i; ++j) { 8582d5c7e45SMatthew Dillon *data_ptrs[j] = mapinfo->bp[j]->b_saveaddr; 8598373f36dSAlan Cox vunmapbuf(mapinfo->bp[j]); 8602d5c7e45SMatthew Dillon relpbuf(mapinfo->bp[j], NULL); 8612d5c7e45SMatthew Dillon } 8628373f36dSAlan Cox relpbuf(mapinfo->bp[i], NULL); 8632d5c7e45SMatthew Dillon PRELE(curproc); 8642d5c7e45SMatthew Dillon return(EACCES); 8652d5c7e45SMatthew Dillon } 8668b8a9b1dSJustin T. Gibbs 8678b8a9b1dSJustin T. Gibbs /* set our pointer to the new mapped area */ 8688b8a9b1dSJustin T. Gibbs *data_ptrs[i] = mapinfo->bp[i]->b_data; 8698b8a9b1dSJustin T. Gibbs 8708b8a9b1dSJustin T. Gibbs mapinfo->num_bufs_used++; 8718b8a9b1dSJustin T. Gibbs } 8728b8a9b1dSJustin T. Gibbs 873a62525f3SMatt Jacob /* 874a62525f3SMatt Jacob * Now that we've gotten this far, change ownership to the kernel 875a62525f3SMatt Jacob * of the buffers so that we don't run afoul of returning to user 876a62525f3SMatt Jacob * space with locks (on the buffer) held. 877a62525f3SMatt Jacob */ 878a62525f3SMatt Jacob for (i = 0; i < numbufs; i++) { 879a62525f3SMatt Jacob BUF_KERNPROC(mapinfo->bp[i]); 880a62525f3SMatt Jacob } 881a62525f3SMatt Jacob 882a62525f3SMatt Jacob 8838b8a9b1dSJustin T. Gibbs return(0); 8848b8a9b1dSJustin T. Gibbs } 8858b8a9b1dSJustin T. Gibbs 8868b8a9b1dSJustin T. Gibbs /* 8878b8a9b1dSJustin T. Gibbs * Unmap memory segments mapped into kernel virtual address space by 8888b8a9b1dSJustin T. Gibbs * cam_periph_mapmem(). 8898b8a9b1dSJustin T. Gibbs */ 8908b8a9b1dSJustin T. Gibbs void 8918b8a9b1dSJustin T. Gibbs cam_periph_unmapmem(union ccb *ccb, struct cam_periph_map_info *mapinfo) 8928b8a9b1dSJustin T. Gibbs { 8938b8a9b1dSJustin T. Gibbs int numbufs, i; 8948b8a9b1dSJustin T. Gibbs u_int8_t **data_ptrs[CAM_PERIPH_MAXMAPS]; 8958b8a9b1dSJustin T. Gibbs 8968b8a9b1dSJustin T. Gibbs if (mapinfo->num_bufs_used <= 0) { 89795fbded6SScott Long /* nothing to free and the process wasn't held. */ 8988b8a9b1dSJustin T. Gibbs return; 8998b8a9b1dSJustin T. Gibbs } 9008b8a9b1dSJustin T. Gibbs 9018b8a9b1dSJustin T. Gibbs switch (ccb->ccb_h.func_code) { 9028b8a9b1dSJustin T. Gibbs case XPT_DEV_MATCH: 9038b8a9b1dSJustin T. Gibbs numbufs = min(mapinfo->num_bufs_used, 2); 9048b8a9b1dSJustin T. Gibbs 9058b8a9b1dSJustin T. Gibbs if (numbufs == 1) { 9068b8a9b1dSJustin T. Gibbs data_ptrs[0] = (u_int8_t **)&ccb->cdm.matches; 9078b8a9b1dSJustin T. Gibbs } else { 9088b8a9b1dSJustin T. Gibbs data_ptrs[0] = (u_int8_t **)&ccb->cdm.patterns; 9098b8a9b1dSJustin T. Gibbs data_ptrs[1] = (u_int8_t **)&ccb->cdm.matches; 9108b8a9b1dSJustin T. Gibbs } 9118b8a9b1dSJustin T. Gibbs break; 9128b8a9b1dSJustin T. Gibbs case XPT_SCSI_IO: 9139911ecf9SJustin T. Gibbs case XPT_CONT_TARGET_IO: 9148b8a9b1dSJustin T. Gibbs data_ptrs[0] = &ccb->csio.data_ptr; 9158b8a9b1dSJustin T. Gibbs numbufs = min(mapinfo->num_bufs_used, 1); 9168b8a9b1dSJustin T. Gibbs break; 91752c9ce25SScott Long case XPT_ATA_IO: 91852c9ce25SScott Long data_ptrs[0] = &ccb->ataio.data_ptr; 91952c9ce25SScott Long numbufs = min(mapinfo->num_bufs_used, 1); 92052c9ce25SScott Long break; 92106e79492SKenneth D. Merry case XPT_SMP_IO: 92206e79492SKenneth D. Merry numbufs = min(mapinfo->num_bufs_used, 2); 92306e79492SKenneth D. Merry data_ptrs[0] = &ccb->smpio.smp_request; 92406e79492SKenneth D. Merry data_ptrs[1] = &ccb->smpio.smp_response; 92506e79492SKenneth D. Merry break; 9263501942bSJustin T. Gibbs case XPT_DEV_ADVINFO: 92706e79492SKenneth D. Merry numbufs = min(mapinfo->num_bufs_used, 1); 9283501942bSJustin T. Gibbs data_ptrs[0] = (uint8_t **)&ccb->cdai.buf; 92906e79492SKenneth D. Merry break; 9308b8a9b1dSJustin T. Gibbs default: 9318b8a9b1dSJustin T. Gibbs /* allow ourselves to be swapped once again */ 9320cbbb7bfSPeter Wemm PRELE(curproc); 9338b8a9b1dSJustin T. Gibbs return; 9348b8a9b1dSJustin T. Gibbs break; /* NOTREACHED */ 9358b8a9b1dSJustin T. Gibbs } 9368b8a9b1dSJustin T. Gibbs 9378b8a9b1dSJustin T. Gibbs for (i = 0; i < numbufs; i++) { 9388b8a9b1dSJustin T. Gibbs /* Set the user's pointer back to the original value */ 9398b8a9b1dSJustin T. Gibbs *data_ptrs[i] = mapinfo->bp[i]->b_saveaddr; 9408b8a9b1dSJustin T. Gibbs 9418b8a9b1dSJustin T. Gibbs /* unmap the buffer */ 9428b8a9b1dSJustin T. Gibbs vunmapbuf(mapinfo->bp[i]); 9438b8a9b1dSJustin T. Gibbs 9448b8a9b1dSJustin T. Gibbs /* release the buffer */ 9451c7c3c6aSMatthew Dillon relpbuf(mapinfo->bp[i], NULL); 9468b8a9b1dSJustin T. Gibbs } 9478b8a9b1dSJustin T. Gibbs 9488b8a9b1dSJustin T. Gibbs /* allow ourselves to be swapped once again */ 9490cbbb7bfSPeter Wemm PRELE(curproc); 9508b8a9b1dSJustin T. Gibbs } 9518b8a9b1dSJustin T. Gibbs 9528b8a9b1dSJustin T. Gibbs void 9538b8a9b1dSJustin T. Gibbs cam_periph_ccbwait(union ccb *ccb) 9548b8a9b1dSJustin T. Gibbs { 9558b8a9b1dSJustin T. Gibbs 9568b8a9b1dSJustin T. Gibbs if ((ccb->ccb_h.pinfo.index != CAM_UNQUEUED_INDEX) 9578b8a9b1dSJustin T. Gibbs || ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_INPROG)) 958227d67aaSAlexander Motin xpt_path_sleep(ccb->ccb_h.path, &ccb->ccb_h.cbfcnp, PRIBIO, 959227d67aaSAlexander Motin "cbwait", 0); 9608b8a9b1dSJustin T. Gibbs } 9618b8a9b1dSJustin T. Gibbs 9628b8a9b1dSJustin T. Gibbs int 963571e8e20SScott Long cam_periph_ioctl(struct cam_periph *periph, u_long cmd, caddr_t addr, 9648b8a9b1dSJustin T. Gibbs int (*error_routine)(union ccb *ccb, 9658b8a9b1dSJustin T. Gibbs cam_flags camflags, 9668b8a9b1dSJustin T. Gibbs u_int32_t sense_flags)) 9678b8a9b1dSJustin T. Gibbs { 9688b8a9b1dSJustin T. Gibbs union ccb *ccb; 9698b8a9b1dSJustin T. Gibbs int error; 9708b8a9b1dSJustin T. Gibbs int found; 9718b8a9b1dSJustin T. Gibbs 9728b8a9b1dSJustin T. Gibbs error = found = 0; 9738b8a9b1dSJustin T. Gibbs 9748b8a9b1dSJustin T. Gibbs switch(cmd){ 9758b8a9b1dSJustin T. Gibbs case CAMGETPASSTHRU: 976bbfa4aa1SAlexander Motin ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL); 9778b8a9b1dSJustin T. Gibbs xpt_setup_ccb(&ccb->ccb_h, 9788b8a9b1dSJustin T. Gibbs ccb->ccb_h.path, 979bbfa4aa1SAlexander Motin CAM_PRIORITY_NORMAL); 9808b8a9b1dSJustin T. Gibbs ccb->ccb_h.func_code = XPT_GDEVLIST; 9818b8a9b1dSJustin T. Gibbs 9828b8a9b1dSJustin T. Gibbs /* 9838b8a9b1dSJustin T. Gibbs * Basically, the point of this is that we go through 9848b8a9b1dSJustin T. Gibbs * getting the list of devices, until we find a passthrough 9858b8a9b1dSJustin T. Gibbs * device. In the current version of the CAM code, the 9868b8a9b1dSJustin T. Gibbs * only way to determine what type of device we're dealing 9878b8a9b1dSJustin T. Gibbs * with is by its name. 9888b8a9b1dSJustin T. Gibbs */ 9898b8a9b1dSJustin T. Gibbs while (found == 0) { 9908b8a9b1dSJustin T. Gibbs ccb->cgdl.index = 0; 9918b8a9b1dSJustin T. Gibbs ccb->cgdl.status = CAM_GDEVLIST_MORE_DEVS; 9928b8a9b1dSJustin T. Gibbs while (ccb->cgdl.status == CAM_GDEVLIST_MORE_DEVS) { 9938b8a9b1dSJustin T. Gibbs 9948b8a9b1dSJustin T. Gibbs /* we want the next device in the list */ 9958b8a9b1dSJustin T. Gibbs xpt_action(ccb); 9968b8a9b1dSJustin T. Gibbs if (strncmp(ccb->cgdl.periph_name, 9978b8a9b1dSJustin T. Gibbs "pass", 4) == 0){ 9988b8a9b1dSJustin T. Gibbs found = 1; 9998b8a9b1dSJustin T. Gibbs break; 10008b8a9b1dSJustin T. Gibbs } 10018b8a9b1dSJustin T. Gibbs } 10028b8a9b1dSJustin T. Gibbs if ((ccb->cgdl.status == CAM_GDEVLIST_LAST_DEVICE) && 10038b8a9b1dSJustin T. Gibbs (found == 0)) { 10048b8a9b1dSJustin T. Gibbs ccb->cgdl.periph_name[0] = '\0'; 10058b8a9b1dSJustin T. Gibbs ccb->cgdl.unit_number = 0; 10068b8a9b1dSJustin T. Gibbs break; 10078b8a9b1dSJustin T. Gibbs } 10088b8a9b1dSJustin T. Gibbs } 10098b8a9b1dSJustin T. Gibbs 10108b8a9b1dSJustin T. Gibbs /* copy the result back out */ 10118b8a9b1dSJustin T. Gibbs bcopy(ccb, addr, sizeof(union ccb)); 10128b8a9b1dSJustin T. Gibbs 10138b8a9b1dSJustin T. Gibbs /* and release the ccb */ 10148b8a9b1dSJustin T. Gibbs xpt_release_ccb(ccb); 10158b8a9b1dSJustin T. Gibbs 10168b8a9b1dSJustin T. Gibbs break; 10178b8a9b1dSJustin T. Gibbs default: 10188b8a9b1dSJustin T. Gibbs error = ENOTTY; 10198b8a9b1dSJustin T. Gibbs break; 10208b8a9b1dSJustin T. Gibbs } 10218b8a9b1dSJustin T. Gibbs return(error); 10228b8a9b1dSJustin T. Gibbs } 10238b8a9b1dSJustin T. Gibbs 1024227d67aaSAlexander Motin static void 1025227d67aaSAlexander Motin cam_periph_done(struct cam_periph *periph, union ccb *done_ccb) 1026227d67aaSAlexander Motin { 1027227d67aaSAlexander Motin 1028227d67aaSAlexander Motin /* Caller will release the CCB */ 1029227d67aaSAlexander Motin wakeup(&done_ccb->ccb_h.cbfcnp); 1030227d67aaSAlexander Motin } 1031227d67aaSAlexander Motin 10328b8a9b1dSJustin T. Gibbs int 10338b8a9b1dSJustin T. Gibbs cam_periph_runccb(union ccb *ccb, 10348b8a9b1dSJustin T. Gibbs int (*error_routine)(union ccb *ccb, 10358b8a9b1dSJustin T. Gibbs cam_flags camflags, 10368b8a9b1dSJustin T. Gibbs u_int32_t sense_flags), 10378b8a9b1dSJustin T. Gibbs cam_flags camflags, u_int32_t sense_flags, 10388b8a9b1dSJustin T. Gibbs struct devstat *ds) 10398b8a9b1dSJustin T. Gibbs { 10408b8a9b1dSJustin T. Gibbs int error; 10418b8a9b1dSJustin T. Gibbs 1042227d67aaSAlexander Motin xpt_path_assert(ccb->ccb_h.path, MA_OWNED); 10438b8a9b1dSJustin T. Gibbs 10448b8a9b1dSJustin T. Gibbs /* 10458b8a9b1dSJustin T. Gibbs * If the user has supplied a stats structure, and if we understand 10468b8a9b1dSJustin T. Gibbs * this particular type of ccb, record the transaction start. 10478b8a9b1dSJustin T. Gibbs */ 1048379ad358SAlexander Motin if ((ds != NULL) && (ccb->ccb_h.func_code == XPT_SCSI_IO || 1049379ad358SAlexander Motin ccb->ccb_h.func_code == XPT_ATA_IO)) 10507194d335SPoul-Henning Kamp devstat_start_transaction(ds, NULL); 10518b8a9b1dSJustin T. Gibbs 1052227d67aaSAlexander Motin ccb->ccb_h.cbfcnp = cam_periph_done; 10538b8a9b1dSJustin T. Gibbs xpt_action(ccb); 10548b8a9b1dSJustin T. Gibbs 10558b8a9b1dSJustin T. Gibbs do { 10568b8a9b1dSJustin T. Gibbs cam_periph_ccbwait(ccb); 10578b8a9b1dSJustin T. Gibbs if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) 10588b8a9b1dSJustin T. Gibbs error = 0; 10598b8a9b1dSJustin T. Gibbs else if (error_routine != NULL) 10608b8a9b1dSJustin T. Gibbs error = (*error_routine)(ccb, camflags, sense_flags); 10618b8a9b1dSJustin T. Gibbs else 10628b8a9b1dSJustin T. Gibbs error = 0; 10638b8a9b1dSJustin T. Gibbs 10648b8a9b1dSJustin T. Gibbs } while (error == ERESTART); 10658b8a9b1dSJustin T. Gibbs 106683c5d981SAlexander Motin if ((ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) { 10678b8a9b1dSJustin T. Gibbs cam_release_devq(ccb->ccb_h.path, 10688b8a9b1dSJustin T. Gibbs /* relsim_flags */0, 10698b8a9b1dSJustin T. Gibbs /* openings */0, 10708b8a9b1dSJustin T. Gibbs /* timeout */0, 10718b8a9b1dSJustin T. Gibbs /* getcount_only */ FALSE); 107283c5d981SAlexander Motin ccb->ccb_h.status &= ~CAM_DEV_QFRZN; 107383c5d981SAlexander Motin } 10748b8a9b1dSJustin T. Gibbs 1075379ad358SAlexander Motin if (ds != NULL) { 1076379ad358SAlexander Motin if (ccb->ccb_h.func_code == XPT_SCSI_IO) { 10778b8a9b1dSJustin T. Gibbs devstat_end_transaction(ds, 10788b8a9b1dSJustin T. Gibbs ccb->csio.dxfer_len, 1079379ad358SAlexander Motin ccb->csio.tag_action & 0x3, 10808b8a9b1dSJustin T. Gibbs ((ccb->ccb_h.flags & CAM_DIR_MASK) == 10818b8a9b1dSJustin T. Gibbs CAM_DIR_NONE) ? DEVSTAT_NO_DATA : 10828b8a9b1dSJustin T. Gibbs (ccb->ccb_h.flags & CAM_DIR_OUT) ? 10838b8a9b1dSJustin T. Gibbs DEVSTAT_WRITE : 10847194d335SPoul-Henning Kamp DEVSTAT_READ, NULL, NULL); 1085379ad358SAlexander Motin } else if (ccb->ccb_h.func_code == XPT_ATA_IO) { 1086379ad358SAlexander Motin devstat_end_transaction(ds, 1087379ad358SAlexander Motin ccb->ataio.dxfer_len, 1088379ad358SAlexander Motin ccb->ataio.tag_action & 0x3, 1089379ad358SAlexander Motin ((ccb->ccb_h.flags & CAM_DIR_MASK) == 1090379ad358SAlexander Motin CAM_DIR_NONE) ? DEVSTAT_NO_DATA : 1091379ad358SAlexander Motin (ccb->ccb_h.flags & CAM_DIR_OUT) ? 1092379ad358SAlexander Motin DEVSTAT_WRITE : 1093379ad358SAlexander Motin DEVSTAT_READ, NULL, NULL); 1094379ad358SAlexander Motin } 1095379ad358SAlexander Motin } 10968b8a9b1dSJustin T. Gibbs 10978b8a9b1dSJustin T. Gibbs return(error); 10988b8a9b1dSJustin T. Gibbs } 10998b8a9b1dSJustin T. Gibbs 110087cfaf0eSJustin T. Gibbs void 110187cfaf0eSJustin T. Gibbs cam_freeze_devq(struct cam_path *path) 110287cfaf0eSJustin T. Gibbs { 1103cccf4220SAlexander Motin struct ccb_hdr ccb_h; 110487cfaf0eSJustin T. Gibbs 11050d4f3c31SAlexander Motin CAM_DEBUG(path, CAM_DEBUG_TRACE, ("cam_freeze_devq\n")); 1106cccf4220SAlexander Motin xpt_setup_ccb(&ccb_h, path, /*priority*/1); 1107cccf4220SAlexander Motin ccb_h.func_code = XPT_NOOP; 1108cccf4220SAlexander Motin ccb_h.flags = CAM_DEV_QFREEZE; 1109cccf4220SAlexander Motin xpt_action((union ccb *)&ccb_h); 111087cfaf0eSJustin T. Gibbs } 111187cfaf0eSJustin T. Gibbs 11128b8a9b1dSJustin T. Gibbs u_int32_t 11138b8a9b1dSJustin T. Gibbs cam_release_devq(struct cam_path *path, u_int32_t relsim_flags, 111483c5d981SAlexander Motin u_int32_t openings, u_int32_t arg, 11158b8a9b1dSJustin T. Gibbs int getcount_only) 11168b8a9b1dSJustin T. Gibbs { 11178b8a9b1dSJustin T. Gibbs struct ccb_relsim crs; 11188b8a9b1dSJustin T. Gibbs 11190d4f3c31SAlexander Motin CAM_DEBUG(path, CAM_DEBUG_TRACE, ("cam_release_devq(%u, %u, %u, %d)\n", 11200d4f3c31SAlexander Motin relsim_flags, openings, arg, getcount_only)); 1121bbfa4aa1SAlexander Motin xpt_setup_ccb(&crs.ccb_h, path, CAM_PRIORITY_NORMAL); 11228b8a9b1dSJustin T. Gibbs crs.ccb_h.func_code = XPT_REL_SIMQ; 11238b8a9b1dSJustin T. Gibbs crs.ccb_h.flags = getcount_only ? CAM_DEV_QFREEZE : 0; 11248b8a9b1dSJustin T. Gibbs crs.release_flags = relsim_flags; 11258b8a9b1dSJustin T. Gibbs crs.openings = openings; 112683c5d981SAlexander Motin crs.release_timeout = arg; 11278b8a9b1dSJustin T. Gibbs xpt_action((union ccb *)&crs); 11288b8a9b1dSJustin T. Gibbs return (crs.qfrozen_cnt); 11298b8a9b1dSJustin T. Gibbs } 11308b8a9b1dSJustin T. Gibbs 11318b8a9b1dSJustin T. Gibbs #define saved_ccb_ptr ppriv_ptr0 113283c5d981SAlexander Motin static void 113383c5d981SAlexander Motin camperiphdone(struct cam_periph *periph, union ccb *done_ccb) 113483c5d981SAlexander Motin { 11350191d9b3SAlexander Motin union ccb *saved_ccb; 113683c5d981SAlexander Motin cam_status status; 113783c5d981SAlexander Motin struct scsi_start_stop_unit *scsi_cmd; 1138e7493b28SAlexander Motin int error_code, sense_key, asc, ascq; 113983c5d981SAlexander Motin 11400191d9b3SAlexander Motin scsi_cmd = (struct scsi_start_stop_unit *) 11410191d9b3SAlexander Motin &done_ccb->csio.cdb_io.cdb_bytes; 114283c5d981SAlexander Motin status = done_ccb->ccb_h.status; 11438b8a9b1dSJustin T. Gibbs 11440191d9b3SAlexander Motin if ((status & CAM_STATUS_MASK) != CAM_REQ_CMP) { 1145e7493b28SAlexander Motin if (scsi_extract_sense_ccb(done_ccb, 1146e7493b28SAlexander Motin &error_code, &sense_key, &asc, &ascq)) { 114765afe1f4SKenneth D. Merry /* 11488b8a9b1dSJustin T. Gibbs * If the error is "invalid field in CDB", 11498b8a9b1dSJustin T. Gibbs * and the load/eject flag is set, turn the 11508b8a9b1dSJustin T. Gibbs * flag off and try again. This is just in 11518b8a9b1dSJustin T. Gibbs * case the drive in question barfs on the 11528b8a9b1dSJustin T. Gibbs * load eject flag. The CAM code should set 11538b8a9b1dSJustin T. Gibbs * the load/eject flag by default for 11548b8a9b1dSJustin T. Gibbs * removable media. 11558b8a9b1dSJustin T. Gibbs */ 11568b8a9b1dSJustin T. Gibbs if ((scsi_cmd->opcode == START_STOP_UNIT) && 11578b8a9b1dSJustin T. Gibbs ((scsi_cmd->how & SSS_LOEJ) != 0) && 11580191d9b3SAlexander Motin (asc == 0x24) && (ascq == 0x00)) { 11598b8a9b1dSJustin T. Gibbs scsi_cmd->how &= ~SSS_LOEJ; 11600191d9b3SAlexander Motin if (status & CAM_DEV_QFRZN) { 11610191d9b3SAlexander Motin cam_release_devq(done_ccb->ccb_h.path, 11620191d9b3SAlexander Motin 0, 0, 0, 0); 11630191d9b3SAlexander Motin done_ccb->ccb_h.status &= 11640191d9b3SAlexander Motin ~CAM_DEV_QFRZN; 11650191d9b3SAlexander Motin } 11668b8a9b1dSJustin T. Gibbs xpt_action(done_ccb); 11670191d9b3SAlexander Motin goto out; 11680191d9b3SAlexander Motin } 11690191d9b3SAlexander Motin } 11700191d9b3SAlexander Motin if (cam_periph_error(done_ccb, 11710191d9b3SAlexander Motin 0, SF_RETRY_UA | SF_NO_PRINT, NULL) == ERESTART) 11720191d9b3SAlexander Motin goto out; 11730191d9b3SAlexander Motin if (done_ccb->ccb_h.status & CAM_DEV_QFRZN) { 11740191d9b3SAlexander Motin cam_release_devq(done_ccb->ccb_h.path, 0, 0, 0, 0); 11750191d9b3SAlexander Motin done_ccb->ccb_h.status &= ~CAM_DEV_QFRZN; 11760191d9b3SAlexander Motin } 11770191d9b3SAlexander Motin } else { 11780191d9b3SAlexander Motin /* 11790191d9b3SAlexander Motin * If we have successfully taken a device from the not 11800191d9b3SAlexander Motin * ready to ready state, re-scan the device and re-get 11810191d9b3SAlexander Motin * the inquiry information. Many devices (mostly disks) 11820191d9b3SAlexander Motin * don't properly report their inquiry information unless 11830191d9b3SAlexander Motin * they are spun up. 11840191d9b3SAlexander Motin */ 11850191d9b3SAlexander Motin if (scsi_cmd->opcode == START_STOP_UNIT) 11860191d9b3SAlexander Motin xpt_async(AC_INQ_CHANGED, done_ccb->ccb_h.path, NULL); 11870191d9b3SAlexander Motin } 118865afe1f4SKenneth D. Merry 11898b8a9b1dSJustin T. Gibbs /* 11900191d9b3SAlexander Motin * Perform the final retry with the original CCB so that final 11910191d9b3SAlexander Motin * error processing is performed by the owner of the CCB. 11928b8a9b1dSJustin T. Gibbs */ 11930191d9b3SAlexander Motin saved_ccb = (union ccb *)done_ccb->ccb_h.saved_ccb_ptr; 119483c5d981SAlexander Motin bcopy(saved_ccb, done_ccb, sizeof(*done_ccb)); 119583c5d981SAlexander Motin xpt_free_ccb(saved_ccb); 11960191d9b3SAlexander Motin if (done_ccb->ccb_h.cbfcnp != camperiphdone) 119760a899a0SKenneth D. Merry periph->flags &= ~CAM_PERIPH_RECOVERY_INPROG; 11988b8a9b1dSJustin T. Gibbs xpt_action(done_ccb); 11998b8a9b1dSJustin T. Gibbs 12000191d9b3SAlexander Motin out: 12010191d9b3SAlexander Motin /* Drop freeze taken due to CAM_DEV_QFREEZE flag set. */ 12020191d9b3SAlexander Motin cam_release_devq(done_ccb->ccb_h.path, 0, 0, 0, 0); 1203d84c90a6SAlexander Motin } 12048b8a9b1dSJustin T. Gibbs 12058b8a9b1dSJustin T. Gibbs /* 120687cfaf0eSJustin T. Gibbs * Generic Async Event handler. Peripheral drivers usually 120787cfaf0eSJustin T. Gibbs * filter out the events that require personal attention, 120887cfaf0eSJustin T. Gibbs * and leave the rest to this function. 120987cfaf0eSJustin T. Gibbs */ 121087cfaf0eSJustin T. Gibbs void 121187cfaf0eSJustin T. Gibbs cam_periph_async(struct cam_periph *periph, u_int32_t code, 121287cfaf0eSJustin T. Gibbs struct cam_path *path, void *arg) 121387cfaf0eSJustin T. Gibbs { 121487cfaf0eSJustin T. Gibbs switch (code) { 121587cfaf0eSJustin T. Gibbs case AC_LOST_DEVICE: 121687cfaf0eSJustin T. Gibbs cam_periph_invalidate(periph); 121787cfaf0eSJustin T. Gibbs break; 121887cfaf0eSJustin T. Gibbs default: 121987cfaf0eSJustin T. Gibbs break; 122087cfaf0eSJustin T. Gibbs } 122187cfaf0eSJustin T. Gibbs } 122287cfaf0eSJustin T. Gibbs 122387cfaf0eSJustin T. Gibbs void 122487cfaf0eSJustin T. Gibbs cam_periph_bus_settle(struct cam_periph *periph, u_int bus_settle) 122587cfaf0eSJustin T. Gibbs { 122687cfaf0eSJustin T. Gibbs struct ccb_getdevstats cgds; 122787cfaf0eSJustin T. Gibbs 1228bbfa4aa1SAlexander Motin xpt_setup_ccb(&cgds.ccb_h, periph->path, CAM_PRIORITY_NORMAL); 122987cfaf0eSJustin T. Gibbs cgds.ccb_h.func_code = XPT_GDEV_STATS; 123087cfaf0eSJustin T. Gibbs xpt_action((union ccb *)&cgds); 123187cfaf0eSJustin T. Gibbs cam_periph_freeze_after_event(periph, &cgds.last_reset, bus_settle); 123287cfaf0eSJustin T. Gibbs } 123387cfaf0eSJustin T. Gibbs 123487cfaf0eSJustin T. Gibbs void 123587cfaf0eSJustin T. Gibbs cam_periph_freeze_after_event(struct cam_periph *periph, 123687cfaf0eSJustin T. Gibbs struct timeval* event_time, u_int duration_ms) 123787cfaf0eSJustin T. Gibbs { 123887cfaf0eSJustin T. Gibbs struct timeval delta; 123987cfaf0eSJustin T. Gibbs struct timeval duration_tv; 124087cfaf0eSJustin T. Gibbs 1241f8ff57d2SAlexander Motin if (!timevalisset(event_time)) 1242f8ff57d2SAlexander Motin return; 1243f8ff57d2SAlexander Motin 124487cfaf0eSJustin T. Gibbs microtime(&delta); 124587cfaf0eSJustin T. Gibbs timevalsub(&delta, event_time); 124687cfaf0eSJustin T. Gibbs duration_tv.tv_sec = duration_ms / 1000; 124787cfaf0eSJustin T. Gibbs duration_tv.tv_usec = (duration_ms % 1000) * 1000; 124887cfaf0eSJustin T. Gibbs if (timevalcmp(&delta, &duration_tv, <)) { 124987cfaf0eSJustin T. Gibbs timevalsub(&duration_tv, &delta); 125087cfaf0eSJustin T. Gibbs 125187cfaf0eSJustin T. Gibbs duration_ms = duration_tv.tv_sec * 1000; 125287cfaf0eSJustin T. Gibbs duration_ms += duration_tv.tv_usec / 1000; 125387cfaf0eSJustin T. Gibbs cam_freeze_devq(periph->path); 125487cfaf0eSJustin T. Gibbs cam_release_devq(periph->path, 125587cfaf0eSJustin T. Gibbs RELSIM_RELEASE_AFTER_TIMEOUT, 125687cfaf0eSJustin T. Gibbs /*reduction*/0, 125787cfaf0eSJustin T. Gibbs /*timeout*/duration_ms, 125887cfaf0eSJustin T. Gibbs /*getcount_only*/0); 125987cfaf0eSJustin T. Gibbs } 126087cfaf0eSJustin T. Gibbs 126187cfaf0eSJustin T. Gibbs } 126287cfaf0eSJustin T. Gibbs 12633393f8daSKenneth D. Merry static int 12640191d9b3SAlexander Motin camperiphscsistatuserror(union ccb *ccb, union ccb **orig_ccb, 12650191d9b3SAlexander Motin cam_flags camflags, u_int32_t sense_flags, 12663393f8daSKenneth D. Merry int *openings, u_int32_t *relsim_flags, 12670181d54bSAlexander Motin u_int32_t *timeout, u_int32_t *action, const char **action_string) 12688b8a9b1dSJustin T. Gibbs { 12698b8a9b1dSJustin T. Gibbs int error; 12708b8a9b1dSJustin T. Gibbs 12718b8a9b1dSJustin T. Gibbs switch (ccb->csio.scsi_status) { 12728b8a9b1dSJustin T. Gibbs case SCSI_STATUS_OK: 12738b8a9b1dSJustin T. Gibbs case SCSI_STATUS_COND_MET: 12748b8a9b1dSJustin T. Gibbs case SCSI_STATUS_INTERMED: 12758b8a9b1dSJustin T. Gibbs case SCSI_STATUS_INTERMED_COND_MET: 12768b8a9b1dSJustin T. Gibbs error = 0; 12778b8a9b1dSJustin T. Gibbs break; 12788b8a9b1dSJustin T. Gibbs case SCSI_STATUS_CMD_TERMINATED: 12798b8a9b1dSJustin T. Gibbs case SCSI_STATUS_CHECK_COND: 12800191d9b3SAlexander Motin error = camperiphscsisenseerror(ccb, orig_ccb, 12813393f8daSKenneth D. Merry camflags, 12828b8a9b1dSJustin T. Gibbs sense_flags, 12833393f8daSKenneth D. Merry openings, 12843393f8daSKenneth D. Merry relsim_flags, 128583c5d981SAlexander Motin timeout, 12860181d54bSAlexander Motin action, 128783c5d981SAlexander Motin action_string); 12888b8a9b1dSJustin T. Gibbs break; 12898b8a9b1dSJustin T. Gibbs case SCSI_STATUS_QUEUE_FULL: 12908b8a9b1dSJustin T. Gibbs { 12918b8a9b1dSJustin T. Gibbs /* no decrement */ 129282815562SJustin T. Gibbs struct ccb_getdevstats cgds; 12938b8a9b1dSJustin T. Gibbs 12948b8a9b1dSJustin T. Gibbs /* 12958b8a9b1dSJustin T. Gibbs * First off, find out what the current 12968b8a9b1dSJustin T. Gibbs * transaction counts are. 12978b8a9b1dSJustin T. Gibbs */ 129882815562SJustin T. Gibbs xpt_setup_ccb(&cgds.ccb_h, 12998b8a9b1dSJustin T. Gibbs ccb->ccb_h.path, 1300bbfa4aa1SAlexander Motin CAM_PRIORITY_NORMAL); 130182815562SJustin T. Gibbs cgds.ccb_h.func_code = XPT_GDEV_STATS; 130282815562SJustin T. Gibbs xpt_action((union ccb *)&cgds); 13038b8a9b1dSJustin T. Gibbs 13048b8a9b1dSJustin T. Gibbs /* 13058b8a9b1dSJustin T. Gibbs * If we were the only transaction active, treat 13068b8a9b1dSJustin T. Gibbs * the QUEUE FULL as if it were a BUSY condition. 13078b8a9b1dSJustin T. Gibbs */ 130882815562SJustin T. Gibbs if (cgds.dev_active != 0) { 130982815562SJustin T. Gibbs int total_openings; 131082815562SJustin T. Gibbs 13118b8a9b1dSJustin T. Gibbs /* 13128b8a9b1dSJustin T. Gibbs * Reduce the number of openings to 13138b8a9b1dSJustin T. Gibbs * be 1 less than the amount it took 13148b8a9b1dSJustin T. Gibbs * to get a queue full bounded by the 13158b8a9b1dSJustin T. Gibbs * minimum allowed tag count for this 13168b8a9b1dSJustin T. Gibbs * device. 13178b8a9b1dSJustin T. Gibbs */ 13183393f8daSKenneth D. Merry total_openings = cgds.dev_active + cgds.dev_openings; 13193393f8daSKenneth D. Merry *openings = cgds.dev_active; 13203393f8daSKenneth D. Merry if (*openings < cgds.mintags) 13213393f8daSKenneth D. Merry *openings = cgds.mintags; 13223393f8daSKenneth D. Merry if (*openings < total_openings) 13233393f8daSKenneth D. Merry *relsim_flags = RELSIM_ADJUST_OPENINGS; 13248b8a9b1dSJustin T. Gibbs else { 13258b8a9b1dSJustin T. Gibbs /* 13268b8a9b1dSJustin T. Gibbs * Some devices report queue full for 13278b8a9b1dSJustin T. Gibbs * temporary resource shortages. For 13288b8a9b1dSJustin T. Gibbs * this reason, we allow a minimum 13298b8a9b1dSJustin T. Gibbs * tag count to be entered via a 13308b8a9b1dSJustin T. Gibbs * quirk entry to prevent the queue 13318b8a9b1dSJustin T. Gibbs * count on these devices from falling 13328b8a9b1dSJustin T. Gibbs * to a pessimisticly low value. We 13338b8a9b1dSJustin T. Gibbs * still wait for the next successful 13348b8a9b1dSJustin T. Gibbs * completion, however, before queueing 13358b8a9b1dSJustin T. Gibbs * more transactions to the device. 13368b8a9b1dSJustin T. Gibbs */ 13373393f8daSKenneth D. Merry *relsim_flags = RELSIM_RELEASE_AFTER_CMDCMPLT; 13388b8a9b1dSJustin T. Gibbs } 13393393f8daSKenneth D. Merry *timeout = 0; 13408b8a9b1dSJustin T. Gibbs error = ERESTART; 13410181d54bSAlexander Motin *action &= ~SSQ_PRINT_SENSE; 13428b8a9b1dSJustin T. Gibbs break; 13438b8a9b1dSJustin T. Gibbs } 13448b8a9b1dSJustin T. Gibbs /* FALLTHROUGH */ 13458b8a9b1dSJustin T. Gibbs } 13468b8a9b1dSJustin T. Gibbs case SCSI_STATUS_BUSY: 13478b8a9b1dSJustin T. Gibbs /* 13488b8a9b1dSJustin T. Gibbs * Restart the queue after either another 13498b8a9b1dSJustin T. Gibbs * command completes or a 1 second timeout. 13508b8a9b1dSJustin T. Gibbs */ 13513393f8daSKenneth D. Merry if (ccb->ccb_h.retry_count > 0) { 1352af51b059SMatt Jacob ccb->ccb_h.retry_count--; 13538b8a9b1dSJustin T. Gibbs error = ERESTART; 13543393f8daSKenneth D. Merry *relsim_flags = RELSIM_RELEASE_AFTER_TIMEOUT 13558b8a9b1dSJustin T. Gibbs | RELSIM_RELEASE_AFTER_CMDCMPLT; 13563393f8daSKenneth D. Merry *timeout = 1000; 1357af51b059SMatt Jacob } else { 1358af51b059SMatt Jacob error = EIO; 1359af51b059SMatt Jacob } 13608b8a9b1dSJustin T. Gibbs break; 13618b8a9b1dSJustin T. Gibbs case SCSI_STATUS_RESERV_CONFLICT: 13628b8a9b1dSJustin T. Gibbs default: 13638b8a9b1dSJustin T. Gibbs error = EIO; 13648b8a9b1dSJustin T. Gibbs break; 13658b8a9b1dSJustin T. Gibbs } 13663393f8daSKenneth D. Merry return (error); 13673393f8daSKenneth D. Merry } 13683393f8daSKenneth D. Merry 13693393f8daSKenneth D. Merry static int 13700191d9b3SAlexander Motin camperiphscsisenseerror(union ccb *ccb, union ccb **orig, 13710191d9b3SAlexander Motin cam_flags camflags, u_int32_t sense_flags, 13723393f8daSKenneth D. Merry int *openings, u_int32_t *relsim_flags, 13730181d54bSAlexander Motin u_int32_t *timeout, u_int32_t *action, const char **action_string) 13743393f8daSKenneth D. Merry { 13753393f8daSKenneth D. Merry struct cam_periph *periph; 137683c5d981SAlexander Motin union ccb *orig_ccb = ccb; 13770191d9b3SAlexander Motin int error, recoveryccb; 13783393f8daSKenneth D. Merry 13793393f8daSKenneth D. Merry periph = xpt_path_periph(ccb->ccb_h.path); 13800191d9b3SAlexander Motin recoveryccb = (ccb->ccb_h.cbfcnp == camperiphdone); 13810191d9b3SAlexander Motin if ((periph->flags & CAM_PERIPH_RECOVERY_INPROG) && !recoveryccb) { 13823393f8daSKenneth D. Merry /* 13833393f8daSKenneth D. Merry * If error recovery is already in progress, don't attempt 13843393f8daSKenneth D. Merry * to process this error, but requeue it unconditionally 13853393f8daSKenneth D. Merry * and attempt to process it once error recovery has 13863393f8daSKenneth D. Merry * completed. This failed command is probably related to 13873393f8daSKenneth D. Merry * the error that caused the currently active error recovery 13883393f8daSKenneth D. Merry * action so our current recovery efforts should also 13893393f8daSKenneth D. Merry * address this command. Be aware that the error recovery 13903393f8daSKenneth D. Merry * code assumes that only one recovery action is in progress 13913393f8daSKenneth D. Merry * on a particular peripheral instance at any given time 13923393f8daSKenneth D. Merry * (e.g. only one saved CCB for error recovery) so it is 13933393f8daSKenneth D. Merry * imperitive that we don't violate this assumption. 13943393f8daSKenneth D. Merry */ 13953393f8daSKenneth D. Merry error = ERESTART; 13960181d54bSAlexander Motin *action &= ~SSQ_PRINT_SENSE; 13973393f8daSKenneth D. Merry } else { 13983393f8daSKenneth D. Merry scsi_sense_action err_action; 13993393f8daSKenneth D. Merry struct ccb_getdev cgd; 14003393f8daSKenneth D. Merry 14013393f8daSKenneth D. Merry /* 14023393f8daSKenneth D. Merry * Grab the inquiry data for this device. 14033393f8daSKenneth D. Merry */ 1404bbfa4aa1SAlexander Motin xpt_setup_ccb(&cgd.ccb_h, ccb->ccb_h.path, CAM_PRIORITY_NORMAL); 14053393f8daSKenneth D. Merry cgd.ccb_h.func_code = XPT_GDEV_TYPE; 14063393f8daSKenneth D. Merry xpt_action((union ccb *)&cgd); 14073393f8daSKenneth D. Merry 1408e7493b28SAlexander Motin err_action = scsi_error_action(&ccb->csio, &cgd.inq_data, 14093393f8daSKenneth D. Merry sense_flags); 14103393f8daSKenneth D. Merry error = err_action & SS_ERRMASK; 14113393f8daSKenneth D. Merry 14123393f8daSKenneth D. Merry /* 14130191d9b3SAlexander Motin * Do not autostart sequential access devices 14140191d9b3SAlexander Motin * to avoid unexpected tape loading. 14150191d9b3SAlexander Motin */ 14160191d9b3SAlexander Motin if ((err_action & SS_MASK) == SS_START && 14170191d9b3SAlexander Motin SID_TYPE(&cgd.inq_data) == T_SEQUENTIAL) { 14180191d9b3SAlexander Motin *action_string = "Will not autostart a " 14190191d9b3SAlexander Motin "sequential access device"; 14200191d9b3SAlexander Motin goto sense_error_done; 14210191d9b3SAlexander Motin } 14220191d9b3SAlexander Motin 14230191d9b3SAlexander Motin /* 14240191d9b3SAlexander Motin * Avoid recovery recursion if recovery action is the same. 14250191d9b3SAlexander Motin */ 14260191d9b3SAlexander Motin if ((err_action & SS_MASK) >= SS_START && recoveryccb) { 14270191d9b3SAlexander Motin if (((err_action & SS_MASK) == SS_START && 14280191d9b3SAlexander Motin ccb->csio.cdb_io.cdb_bytes[0] == START_STOP_UNIT) || 14290191d9b3SAlexander Motin ((err_action & SS_MASK) == SS_TUR && 14300191d9b3SAlexander Motin (ccb->csio.cdb_io.cdb_bytes[0] == TEST_UNIT_READY))) { 14310191d9b3SAlexander Motin err_action = SS_RETRY|SSQ_DECREMENT_COUNT|EIO; 14320191d9b3SAlexander Motin *relsim_flags = RELSIM_RELEASE_AFTER_TIMEOUT; 14330191d9b3SAlexander Motin *timeout = 500; 14340191d9b3SAlexander Motin } 14350191d9b3SAlexander Motin } 14360191d9b3SAlexander Motin 14370191d9b3SAlexander Motin /* 14383393f8daSKenneth D. Merry * If the recovery action will consume a retry, 14393393f8daSKenneth D. Merry * make sure we actually have retries available. 14403393f8daSKenneth D. Merry */ 14413393f8daSKenneth D. Merry if ((err_action & SSQ_DECREMENT_COUNT) != 0) { 1442a07e846bSAlexander Motin if (ccb->ccb_h.retry_count > 0 && 1443a07e846bSAlexander Motin (periph->flags & CAM_PERIPH_INVALID) == 0) 14443393f8daSKenneth D. Merry ccb->ccb_h.retry_count--; 14453393f8daSKenneth D. Merry else { 144683c5d981SAlexander Motin *action_string = "Retries exhausted"; 14473393f8daSKenneth D. Merry goto sense_error_done; 14483393f8daSKenneth D. Merry } 14493393f8daSKenneth D. Merry } 14503393f8daSKenneth D. Merry 14513393f8daSKenneth D. Merry if ((err_action & SS_MASK) >= SS_START) { 14523393f8daSKenneth D. Merry /* 14533393f8daSKenneth D. Merry * Do common portions of commands that 14543393f8daSKenneth D. Merry * use recovery CCBs. 14553393f8daSKenneth D. Merry */ 145683c5d981SAlexander Motin orig_ccb = xpt_alloc_ccb_nowait(); 145783c5d981SAlexander Motin if (orig_ccb == NULL) { 145883c5d981SAlexander Motin *action_string = "Can't allocate recovery CCB"; 14593393f8daSKenneth D. Merry goto sense_error_done; 14603393f8daSKenneth D. Merry } 14611f1158b2SAlexander Motin /* 14621f1158b2SAlexander Motin * Clear freeze flag for original request here, as 14631f1158b2SAlexander Motin * this freeze will be dropped as part of ERESTART. 14641f1158b2SAlexander Motin */ 14651f1158b2SAlexander Motin ccb->ccb_h.status &= ~CAM_DEV_QFRZN; 146683c5d981SAlexander Motin bcopy(ccb, orig_ccb, sizeof(*orig_ccb)); 14673393f8daSKenneth D. Merry } 14683393f8daSKenneth D. Merry 14693393f8daSKenneth D. Merry switch (err_action & SS_MASK) { 14703393f8daSKenneth D. Merry case SS_NOP: 147183c5d981SAlexander Motin *action_string = "No recovery action needed"; 147200e54d14SKenneth D. Merry error = 0; 147300e54d14SKenneth D. Merry break; 14743393f8daSKenneth D. Merry case SS_RETRY: 147583c5d981SAlexander Motin *action_string = "Retrying command (per sense data)"; 14763393f8daSKenneth D. Merry error = ERESTART; 14778b8a9b1dSJustin T. Gibbs break; 14783393f8daSKenneth D. Merry case SS_FAIL: 147983c5d981SAlexander Motin *action_string = "Unretryable error"; 14803393f8daSKenneth D. Merry break; 14813393f8daSKenneth D. Merry case SS_START: 14823393f8daSKenneth D. Merry { 14833393f8daSKenneth D. Merry int le; 14843393f8daSKenneth D. Merry 14853393f8daSKenneth D. Merry /* 14863393f8daSKenneth D. Merry * Send a start unit command to the device, and 14873393f8daSKenneth D. Merry * then retry the command. 14883393f8daSKenneth D. Merry */ 148983c5d981SAlexander Motin *action_string = "Attempting to start unit"; 149083c5d981SAlexander Motin periph->flags |= CAM_PERIPH_RECOVERY_INPROG; 14913393f8daSKenneth D. Merry 14923393f8daSKenneth D. Merry /* 14933393f8daSKenneth D. Merry * Check for removable media and set 14943393f8daSKenneth D. Merry * load/eject flag appropriately. 14953393f8daSKenneth D. Merry */ 14963393f8daSKenneth D. Merry if (SID_IS_REMOVABLE(&cgd.inq_data)) 14973393f8daSKenneth D. Merry le = TRUE; 14983393f8daSKenneth D. Merry else 14993393f8daSKenneth D. Merry le = FALSE; 15003393f8daSKenneth D. Merry 15013393f8daSKenneth D. Merry scsi_start_stop(&ccb->csio, 15023393f8daSKenneth D. Merry /*retries*/1, 15033393f8daSKenneth D. Merry camperiphdone, 15043393f8daSKenneth D. Merry MSG_SIMPLE_Q_TAG, 15053393f8daSKenneth D. Merry /*start*/TRUE, 15063393f8daSKenneth D. Merry /*load/eject*/le, 15073393f8daSKenneth D. Merry /*immediate*/FALSE, 15083393f8daSKenneth D. Merry SSD_FULL_SIZE, 15093393f8daSKenneth D. Merry /*timeout*/50000); 15103393f8daSKenneth D. Merry break; 15113393f8daSKenneth D. Merry } 15123393f8daSKenneth D. Merry case SS_TUR: 15133393f8daSKenneth D. Merry { 15143393f8daSKenneth D. Merry /* 15153393f8daSKenneth D. Merry * Send a Test Unit Ready to the device. 15163393f8daSKenneth D. Merry * If the 'many' flag is set, we send 120 15173393f8daSKenneth D. Merry * test unit ready commands, one every half 15183393f8daSKenneth D. Merry * second. Otherwise, we just send one TUR. 15193393f8daSKenneth D. Merry * We only want to do this if the retry 15203393f8daSKenneth D. Merry * count has not been exhausted. 15213393f8daSKenneth D. Merry */ 15223393f8daSKenneth D. Merry int retries; 15233393f8daSKenneth D. Merry 15243393f8daSKenneth D. Merry if ((err_action & SSQ_MANY) != 0) { 152583c5d981SAlexander Motin *action_string = "Polling device for readiness"; 15263393f8daSKenneth D. Merry retries = 120; 15273393f8daSKenneth D. Merry } else { 152883c5d981SAlexander Motin *action_string = "Testing device for readiness"; 15293393f8daSKenneth D. Merry retries = 1; 15303393f8daSKenneth D. Merry } 153183c5d981SAlexander Motin periph->flags |= CAM_PERIPH_RECOVERY_INPROG; 15323393f8daSKenneth D. Merry scsi_test_unit_ready(&ccb->csio, 15333393f8daSKenneth D. Merry retries, 15343393f8daSKenneth D. Merry camperiphdone, 15353393f8daSKenneth D. Merry MSG_SIMPLE_Q_TAG, 15363393f8daSKenneth D. Merry SSD_FULL_SIZE, 15373393f8daSKenneth D. Merry /*timeout*/5000); 15383393f8daSKenneth D. Merry 15393393f8daSKenneth D. Merry /* 15403393f8daSKenneth D. Merry * Accomplish our 500ms delay by deferring 15413393f8daSKenneth D. Merry * the release of our device queue appropriately. 15423393f8daSKenneth D. Merry */ 15433393f8daSKenneth D. Merry *relsim_flags = RELSIM_RELEASE_AFTER_TIMEOUT; 15443393f8daSKenneth D. Merry *timeout = 500; 15453393f8daSKenneth D. Merry break; 15463393f8daSKenneth D. Merry } 15473393f8daSKenneth D. Merry default: 1548e3c29144SWarner Losh panic("Unhandled error action %x", err_action); 15493393f8daSKenneth D. Merry } 15503393f8daSKenneth D. Merry 15513393f8daSKenneth D. Merry if ((err_action & SS_MASK) >= SS_START) { 15523393f8daSKenneth D. Merry /* 1553bbfa4aa1SAlexander Motin * Drop the priority, so that the recovery 15543393f8daSKenneth D. Merry * CCB is the first to execute. Freeze the queue 15553393f8daSKenneth D. Merry * after this command is sent so that we can 15563393f8daSKenneth D. Merry * restore the old csio and have it queued in 15573393f8daSKenneth D. Merry * the proper order before we release normal 15583393f8daSKenneth D. Merry * transactions to the device. 15593393f8daSKenneth D. Merry */ 156083c5d981SAlexander Motin ccb->ccb_h.pinfo.priority--; 15613393f8daSKenneth D. Merry ccb->ccb_h.flags |= CAM_DEV_QFREEZE; 156283c5d981SAlexander Motin ccb->ccb_h.saved_ccb_ptr = orig_ccb; 15633393f8daSKenneth D. Merry error = ERESTART; 15640191d9b3SAlexander Motin *orig = orig_ccb; 15653393f8daSKenneth D. Merry } 15663393f8daSKenneth D. Merry 15673393f8daSKenneth D. Merry sense_error_done: 15680181d54bSAlexander Motin *action = err_action; 15693393f8daSKenneth D. Merry } 15703393f8daSKenneth D. Merry return (error); 15713393f8daSKenneth D. Merry } 15723393f8daSKenneth D. Merry 15733393f8daSKenneth D. Merry /* 15743393f8daSKenneth D. Merry * Generic error handler. Peripheral drivers usually filter 15753393f8daSKenneth D. Merry * out the errors that they handle in a unique mannor, then 15763393f8daSKenneth D. Merry * call this function. 15773393f8daSKenneth D. Merry */ 15783393f8daSKenneth D. Merry int 15793393f8daSKenneth D. Merry cam_periph_error(union ccb *ccb, cam_flags camflags, 15803393f8daSKenneth D. Merry u_int32_t sense_flags, union ccb *save_ccb) 15813393f8daSKenneth D. Merry { 15820181d54bSAlexander Motin struct cam_path *newpath; 15830181d54bSAlexander Motin union ccb *orig_ccb, *scan_ccb; 1584a07e846bSAlexander Motin struct cam_periph *periph; 15853393f8daSKenneth D. Merry const char *action_string; 15863393f8daSKenneth D. Merry cam_status status; 15870181d54bSAlexander Motin int frozen, error, openings; 15880181d54bSAlexander Motin u_int32_t action, relsim_flags, timeout; 15893393f8daSKenneth D. Merry 15900181d54bSAlexander Motin action = SSQ_PRINT_SENSE; 1591a07e846bSAlexander Motin periph = xpt_path_periph(ccb->ccb_h.path); 15923393f8daSKenneth D. Merry action_string = NULL; 15933393f8daSKenneth D. Merry status = ccb->ccb_h.status; 15943393f8daSKenneth D. Merry frozen = (status & CAM_DEV_QFRZN) != 0; 15953393f8daSKenneth D. Merry status &= CAM_STATUS_MASK; 15960181d54bSAlexander Motin openings = relsim_flags = timeout = 0; 15970191d9b3SAlexander Motin orig_ccb = ccb; 15983393f8daSKenneth D. Merry 15993393f8daSKenneth D. Merry switch (status) { 16003393f8daSKenneth D. Merry case CAM_REQ_CMP: 16013393f8daSKenneth D. Merry error = 0; 16020181d54bSAlexander Motin action &= ~SSQ_PRINT_SENSE; 16033393f8daSKenneth D. Merry break; 16043393f8daSKenneth D. Merry case CAM_SCSI_STATUS_ERROR: 16050191d9b3SAlexander Motin error = camperiphscsistatuserror(ccb, &orig_ccb, 16060191d9b3SAlexander Motin camflags, sense_flags, &openings, &relsim_flags, 16070181d54bSAlexander Motin &timeout, &action, &action_string); 16083393f8daSKenneth D. Merry break; 16093393f8daSKenneth D. Merry case CAM_AUTOSENSE_FAIL: 1610c7ec4390SMatt Jacob error = EIO; /* we have to kill the command */ 1611c7ec4390SMatt Jacob break; 16128b8a9b1dSJustin T. Gibbs case CAM_UA_ABORT: 16138b8a9b1dSJustin T. Gibbs case CAM_UA_TERMIO: 16148b8a9b1dSJustin T. Gibbs case CAM_MSG_REJECT_REC: 16158b8a9b1dSJustin T. Gibbs /* XXX Don't know that these are correct */ 16168b8a9b1dSJustin T. Gibbs error = EIO; 16178b8a9b1dSJustin T. Gibbs break; 16188b8a9b1dSJustin T. Gibbs case CAM_SEL_TIMEOUT: 16193393f8daSKenneth D. Merry if ((camflags & CAM_RETRY_SELTO) != 0) { 1620a07e846bSAlexander Motin if (ccb->ccb_h.retry_count > 0 && 1621a07e846bSAlexander Motin (periph->flags & CAM_PERIPH_INVALID) == 0) { 16223393f8daSKenneth D. Merry ccb->ccb_h.retry_count--; 16233393f8daSKenneth D. Merry error = ERESTART; 16243393f8daSKenneth D. Merry 16253393f8daSKenneth D. Merry /* 162673cf209fSMatt Jacob * Wait a bit to give the device 16273393f8daSKenneth D. Merry * time to recover before we try again. 16283393f8daSKenneth D. Merry */ 16293393f8daSKenneth D. Merry relsim_flags = RELSIM_RELEASE_AFTER_TIMEOUT; 163073cf209fSMatt Jacob timeout = periph_selto_delay; 16313393f8daSKenneth D. Merry break; 16323393f8daSKenneth D. Merry } 1633a07e846bSAlexander Motin action_string = "Retries exhausted"; 16343393f8daSKenneth D. Merry } 16358900f4b8SKenneth D. Merry /* FALLTHROUGH */ 16368900f4b8SKenneth D. Merry case CAM_DEV_NOT_THERE: 16373393f8daSKenneth D. Merry error = ENXIO; 16380181d54bSAlexander Motin action = SSQ_LOST; 16398b8a9b1dSJustin T. Gibbs break; 16408b8a9b1dSJustin T. Gibbs case CAM_REQ_INVALID: 16418b8a9b1dSJustin T. Gibbs case CAM_PATH_INVALID: 16428b8a9b1dSJustin T. Gibbs case CAM_NO_HBA: 16438b8a9b1dSJustin T. Gibbs case CAM_PROVIDE_FAIL: 16448b8a9b1dSJustin T. Gibbs case CAM_REQ_TOO_BIG: 16459a014e6fSIan Dowse case CAM_LUN_INVALID: 16469a014e6fSIan Dowse case CAM_TID_INVALID: 16478b8a9b1dSJustin T. Gibbs error = EINVAL; 16488b8a9b1dSJustin T. Gibbs break; 16498b8a9b1dSJustin T. Gibbs case CAM_SCSI_BUS_RESET: 16508b8a9b1dSJustin T. Gibbs case CAM_BDR_SENT: 16513393f8daSKenneth D. Merry /* 16523393f8daSKenneth D. Merry * Commands that repeatedly timeout and cause these 16533393f8daSKenneth D. Merry * kinds of error recovery actions, should return 16543393f8daSKenneth D. Merry * CAM_CMD_TIMEOUT, which allows us to safely assume 16553393f8daSKenneth D. Merry * that this command was an innocent bystander to 16563393f8daSKenneth D. Merry * these events and should be unconditionally 16573393f8daSKenneth D. Merry * retried. 16583393f8daSKenneth D. Merry */ 16598b8a9b1dSJustin T. Gibbs case CAM_REQUEUE_REQ: 16600191d9b3SAlexander Motin /* Unconditional requeue if device is still there */ 16610191d9b3SAlexander Motin if (periph->flags & CAM_PERIPH_INVALID) { 16620191d9b3SAlexander Motin action_string = "Periph was invalidated"; 1663a07e846bSAlexander Motin error = EIO; 16640191d9b3SAlexander Motin } else if (sense_flags & SF_NO_RETRY) { 16650191d9b3SAlexander Motin error = EIO; 16660191d9b3SAlexander Motin action_string = "Retry was blocked"; 166784c4de2dSAlexander Motin } else { 16680191d9b3SAlexander Motin error = ERESTART; 16690181d54bSAlexander Motin action &= ~SSQ_PRINT_SENSE; 167084c4de2dSAlexander Motin } 16718b8a9b1dSJustin T. Gibbs break; 16728b8a9b1dSJustin T. Gibbs case CAM_RESRC_UNAVAIL: 167373cf209fSMatt Jacob /* Wait a bit for the resource shortage to abate. */ 167473cf209fSMatt Jacob timeout = periph_noresrc_delay; 167573cf209fSMatt Jacob /* FALLTHROUGH */ 16768b8a9b1dSJustin T. Gibbs case CAM_BUSY: 167773cf209fSMatt Jacob if (timeout == 0) { 167873cf209fSMatt Jacob /* Wait a bit for the busy condition to abate. */ 167973cf209fSMatt Jacob timeout = periph_busy_delay; 168073cf209fSMatt Jacob } 168173cf209fSMatt Jacob relsim_flags = RELSIM_RELEASE_AFTER_TIMEOUT; 168273cf209fSMatt Jacob /* FALLTHROUGH */ 16830191d9b3SAlexander Motin case CAM_ATA_STATUS_ERROR: 16840191d9b3SAlexander Motin case CAM_REQ_CMP_ERR: 16850191d9b3SAlexander Motin case CAM_CMD_TIMEOUT: 16860191d9b3SAlexander Motin case CAM_UNEXP_BUSFREE: 16870191d9b3SAlexander Motin case CAM_UNCOR_PARITY: 16880191d9b3SAlexander Motin case CAM_DATA_RUN_ERR: 16898b8a9b1dSJustin T. Gibbs default: 16900191d9b3SAlexander Motin if (periph->flags & CAM_PERIPH_INVALID) { 16910191d9b3SAlexander Motin error = EIO; 16920191d9b3SAlexander Motin action_string = "Periph was invalidated"; 16930191d9b3SAlexander Motin } else if (ccb->ccb_h.retry_count == 0) { 16948b8a9b1dSJustin T. Gibbs error = EIO; 169583c5d981SAlexander Motin action_string = "Retries exhausted"; 16960191d9b3SAlexander Motin } else if (sense_flags & SF_NO_RETRY) { 16970191d9b3SAlexander Motin error = EIO; 16980191d9b3SAlexander Motin action_string = "Retry was blocked"; 16990191d9b3SAlexander Motin } else { 17000191d9b3SAlexander Motin ccb->ccb_h.retry_count--; 17010191d9b3SAlexander Motin error = ERESTART; 17028b8a9b1dSJustin T. Gibbs } 17038b8a9b1dSJustin T. Gibbs break; 17048b8a9b1dSJustin T. Gibbs } 17058b8a9b1dSJustin T. Gibbs 17060191d9b3SAlexander Motin if ((sense_flags & SF_PRINT_ALWAYS) || 17070191d9b3SAlexander Motin CAM_DEBUGGED(ccb->ccb_h.path, CAM_DEBUG_INFO)) 17080181d54bSAlexander Motin action |= SSQ_PRINT_SENSE; 17090191d9b3SAlexander Motin else if (sense_flags & SF_NO_PRINT) 17100181d54bSAlexander Motin action &= ~SSQ_PRINT_SENSE; 17110181d54bSAlexander Motin if ((action & SSQ_PRINT_SENSE) != 0) 17120191d9b3SAlexander Motin cam_error_print(orig_ccb, CAM_ESF_ALL, CAM_EPF_ALL); 17130181d54bSAlexander Motin if (error != 0 && (action & SSQ_PRINT_SENSE) != 0) { 17140f3e2159SAlexander Motin if (error != ERESTART) { 17153393f8daSKenneth D. Merry if (action_string == NULL) 171683c5d981SAlexander Motin action_string = "Unretryable error"; 171783c5d981SAlexander Motin xpt_print(ccb->ccb_h.path, "Error %d, %s\n", 171883c5d981SAlexander Motin error, action_string); 171983c5d981SAlexander Motin } else if (action_string != NULL) 1720f0d9af51SMatt Jacob xpt_print(ccb->ccb_h.path, "%s\n", action_string); 172183c5d981SAlexander Motin else 172283c5d981SAlexander Motin xpt_print(ccb->ccb_h.path, "Retrying command\n"); 17230f3e2159SAlexander Motin } 17240f3e2159SAlexander Motin 17250181d54bSAlexander Motin if ((action & SSQ_LOST) != 0) { 17260191d9b3SAlexander Motin lun_id_t lun_id; 17270191d9b3SAlexander Motin 17280191d9b3SAlexander Motin /* 17290191d9b3SAlexander Motin * For a selection timeout, we consider all of the LUNs on 17300191d9b3SAlexander Motin * the target to be gone. If the status is CAM_DEV_NOT_THERE, 17310191d9b3SAlexander Motin * then we only get rid of the device(s) specified by the 17320191d9b3SAlexander Motin * path in the original CCB. 17330191d9b3SAlexander Motin */ 17340181d54bSAlexander Motin if (status == CAM_SEL_TIMEOUT) 17350191d9b3SAlexander Motin lun_id = CAM_LUN_WILDCARD; 17360181d54bSAlexander Motin else 17370181d54bSAlexander Motin lun_id = xpt_path_lun_id(ccb->ccb_h.path); 17380191d9b3SAlexander Motin 17390191d9b3SAlexander Motin /* Should we do more if we can't create the path?? */ 17400191d9b3SAlexander Motin if (xpt_create_path(&newpath, periph, 17410191d9b3SAlexander Motin xpt_path_path_id(ccb->ccb_h.path), 17420191d9b3SAlexander Motin xpt_path_target_id(ccb->ccb_h.path), 17430191d9b3SAlexander Motin lun_id) == CAM_REQ_CMP) { 17440191d9b3SAlexander Motin 17450191d9b3SAlexander Motin /* 17460191d9b3SAlexander Motin * Let peripheral drivers know that this 17470191d9b3SAlexander Motin * device has gone away. 17480191d9b3SAlexander Motin */ 17490191d9b3SAlexander Motin xpt_async(AC_LOST_DEVICE, newpath, NULL); 17500191d9b3SAlexander Motin xpt_free_path(newpath); 17510191d9b3SAlexander Motin } 17520181d54bSAlexander Motin } 17533631c638SAlexander Motin 17543631c638SAlexander Motin /* Broadcast UNIT ATTENTIONs to all periphs. */ 17550181d54bSAlexander Motin if ((action & SSQ_UA) != 0) 17563631c638SAlexander Motin xpt_async(AC_UNIT_ATTENTION, orig_ccb->ccb_h.path, orig_ccb); 17570181d54bSAlexander Motin 17580181d54bSAlexander Motin /* Rescan target on "Reported LUNs data has changed" */ 17590181d54bSAlexander Motin if ((action & SSQ_RESCAN) != 0) { 17600181d54bSAlexander Motin if (xpt_create_path(&newpath, NULL, 17610181d54bSAlexander Motin xpt_path_path_id(ccb->ccb_h.path), 17620181d54bSAlexander Motin xpt_path_target_id(ccb->ccb_h.path), 1763e5736ac8SAlexander Motin CAM_LUN_WILDCARD) == CAM_REQ_CMP) { 17640181d54bSAlexander Motin 17650181d54bSAlexander Motin scan_ccb = xpt_alloc_ccb_nowait(); 1766e5736ac8SAlexander Motin if (scan_ccb != NULL) { 17670181d54bSAlexander Motin scan_ccb->ccb_h.path = newpath; 1768e5736ac8SAlexander Motin scan_ccb->ccb_h.func_code = XPT_SCAN_TGT; 17690181d54bSAlexander Motin scan_ccb->crcn.flags = 0; 17700181d54bSAlexander Motin xpt_rescan(scan_ccb); 1771*3231e8bdSAlexander Motin } else { 1772e5736ac8SAlexander Motin xpt_print(newpath, 1773e5736ac8SAlexander Motin "Can't allocate CCB to rescan target\n"); 1774*3231e8bdSAlexander Motin xpt_free_path(newpath); 1775*3231e8bdSAlexander Motin } 17760181d54bSAlexander Motin } 17770191d9b3SAlexander Motin } 17780191d9b3SAlexander Motin 17790f3e2159SAlexander Motin /* Attempt a retry */ 17800f3e2159SAlexander Motin if (error == ERESTART || error == 0) { 17810f3e2159SAlexander Motin if (frozen != 0) 17820f3e2159SAlexander Motin ccb->ccb_h.status &= ~CAM_DEV_QFRZN; 17830f3e2159SAlexander Motin if (error == ERESTART) 17840f3e2159SAlexander Motin xpt_action(ccb); 17850f3e2159SAlexander Motin if (frozen != 0) 17860f3e2159SAlexander Motin cam_release_devq(ccb->ccb_h.path, 17870f3e2159SAlexander Motin relsim_flags, 17880f3e2159SAlexander Motin openings, 17890f3e2159SAlexander Motin timeout, 17900f3e2159SAlexander Motin /*getcount_only*/0); 17913393f8daSKenneth D. Merry } 17928b8a9b1dSJustin T. Gibbs 17938b8a9b1dSJustin T. Gibbs return (error); 17948b8a9b1dSJustin T. Gibbs } 1795