1898b0535SWarner Losh /*- 28b8a9b1dSJustin T. Gibbs * Common functions for CAM "type" (peripheral) drivers. 38b8a9b1dSJustin T. Gibbs * 4bec9534dSPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 5bec9534dSPedro F. Giffuni * 68b8a9b1dSJustin T. Gibbs * Copyright (c) 1997, 1998 Justin T. Gibbs. 7501468a5SKenneth D. Merry * Copyright (c) 1997, 1998, 1999, 2000 Kenneth D. Merry. 88b8a9b1dSJustin T. Gibbs * All rights reserved. 98b8a9b1dSJustin T. Gibbs * 108b8a9b1dSJustin T. Gibbs * Redistribution and use in source and binary forms, with or without 118b8a9b1dSJustin T. Gibbs * modification, are permitted provided that the following conditions 128b8a9b1dSJustin T. Gibbs * are met: 138b8a9b1dSJustin T. Gibbs * 1. Redistributions of source code must retain the above copyright 148b8a9b1dSJustin T. Gibbs * notice, this list of conditions, and the following disclaimer, 158b8a9b1dSJustin T. Gibbs * without modification, immediately at the beginning of the file. 168b8a9b1dSJustin T. Gibbs * 2. The name of the author may not be used to endorse or promote products 178b8a9b1dSJustin T. Gibbs * derived from this software without specific prior written permission. 188b8a9b1dSJustin T. Gibbs * 198b8a9b1dSJustin T. Gibbs * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 208b8a9b1dSJustin T. Gibbs * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 218b8a9b1dSJustin T. Gibbs * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 228b8a9b1dSJustin T. Gibbs * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 238b8a9b1dSJustin T. Gibbs * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 248b8a9b1dSJustin T. Gibbs * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 258b8a9b1dSJustin T. Gibbs * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 268b8a9b1dSJustin T. Gibbs * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 278b8a9b1dSJustin T. Gibbs * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 288b8a9b1dSJustin T. Gibbs * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 298b8a9b1dSJustin T. Gibbs * SUCH DAMAGE. 308b8a9b1dSJustin T. Gibbs */ 318b8a9b1dSJustin T. Gibbs 329c963d87SDavid E. O'Brien #include <sys/cdefs.h> 339c963d87SDavid E. O'Brien __FBSDID("$FreeBSD$"); 349c963d87SDavid E. O'Brien 358b8a9b1dSJustin T. Gibbs #include <sys/param.h> 368b8a9b1dSJustin T. Gibbs #include <sys/systm.h> 378b8a9b1dSJustin T. Gibbs #include <sys/types.h> 388b8a9b1dSJustin T. Gibbs #include <sys/malloc.h> 39362abc44STai-hwa Liang #include <sys/kernel.h> 409626b608SPoul-Henning Kamp #include <sys/bio.h> 415cf3cd10SWarner Losh #include <sys/conf.h> 42f34fa851SJohn Baldwin #include <sys/lock.h> 43f34fa851SJohn Baldwin #include <sys/mutex.h> 448b8a9b1dSJustin T. Gibbs #include <sys/buf.h> 458b8a9b1dSJustin T. Gibbs #include <sys/proc.h> 468b8a9b1dSJustin T. Gibbs #include <sys/devicestat.h> 4775f51904SPeter Wemm #include <sys/bus.h> 483501942bSJustin T. Gibbs #include <sys/sbuf.h> 498b8a9b1dSJustin T. Gibbs #include <vm/vm.h> 508b8a9b1dSJustin T. Gibbs #include <vm/vm_extern.h> 518b8a9b1dSJustin T. Gibbs 528b8a9b1dSJustin T. Gibbs #include <cam/cam.h> 538b8a9b1dSJustin T. Gibbs #include <cam/cam_ccb.h> 5452c9ce25SScott Long #include <cam/cam_queue.h> 558b8a9b1dSJustin T. Gibbs #include <cam/cam_xpt_periph.h> 568b8a9b1dSJustin T. Gibbs #include <cam/cam_periph.h> 578b8a9b1dSJustin T. Gibbs #include <cam/cam_debug.h> 582b83592fSScott Long #include <cam/cam_sim.h> 598b8a9b1dSJustin T. Gibbs 608b8a9b1dSJustin T. Gibbs #include <cam/scsi/scsi_all.h> 618b8a9b1dSJustin T. Gibbs #include <cam/scsi/scsi_message.h> 628b8a9b1dSJustin T. Gibbs #include <cam/scsi/scsi_pass.h> 638b8a9b1dSJustin T. Gibbs 648b8a9b1dSJustin T. Gibbs static u_int camperiphnextunit(struct periph_driver *p_drv, 65501468a5SKenneth D. Merry u_int newunit, int wired, 66501468a5SKenneth D. Merry path_id_t pathid, target_id_t target, 67501468a5SKenneth D. Merry lun_id_t lun); 688b8a9b1dSJustin T. Gibbs static u_int camperiphunit(struct periph_driver *p_drv, 69501468a5SKenneth D. Merry path_id_t pathid, target_id_t target, 70501468a5SKenneth D. Merry lun_id_t lun); 718b8a9b1dSJustin T. Gibbs static void camperiphdone(struct cam_periph *periph, 728b8a9b1dSJustin T. Gibbs union ccb *done_ccb); 738b8a9b1dSJustin T. Gibbs static void camperiphfree(struct cam_periph *periph); 743393f8daSKenneth D. Merry static int camperiphscsistatuserror(union ccb *ccb, 750191d9b3SAlexander Motin union ccb **orig_ccb, 763393f8daSKenneth D. Merry cam_flags camflags, 773393f8daSKenneth D. Merry u_int32_t sense_flags, 783393f8daSKenneth D. Merry int *openings, 793393f8daSKenneth D. Merry u_int32_t *relsim_flags, 8083c5d981SAlexander Motin u_int32_t *timeout, 810181d54bSAlexander Motin u_int32_t *action, 8283c5d981SAlexander Motin const char **action_string); 833393f8daSKenneth D. Merry static int camperiphscsisenseerror(union ccb *ccb, 840191d9b3SAlexander Motin union ccb **orig_ccb, 853393f8daSKenneth D. Merry cam_flags camflags, 863393f8daSKenneth D. Merry u_int32_t sense_flags, 873393f8daSKenneth D. Merry int *openings, 883393f8daSKenneth D. Merry u_int32_t *relsim_flags, 8983c5d981SAlexander Motin u_int32_t *timeout, 900181d54bSAlexander Motin u_int32_t *action, 9183c5d981SAlexander Motin const char **action_string); 92a30ecd42SScott Long static void cam_periph_devctl_notify(union ccb *ccb); 938b8a9b1dSJustin T. Gibbs 940b7c27b9SPeter Wemm static int nperiph_drivers; 9583c5d981SAlexander Motin static int initialized = 0; 960b7c27b9SPeter Wemm struct periph_driver **periph_drivers; 970b7c27b9SPeter Wemm 98d745c852SEd Schouten static MALLOC_DEFINE(M_CAMPERIPH, "CAM periph", "CAM peripheral buffers"); 99362abc44STai-hwa Liang 10073cf209fSMatt Jacob static int periph_selto_delay = 1000; 10173cf209fSMatt Jacob TUNABLE_INT("kern.cam.periph_selto_delay", &periph_selto_delay); 10273cf209fSMatt Jacob static int periph_noresrc_delay = 500; 10373cf209fSMatt Jacob TUNABLE_INT("kern.cam.periph_noresrc_delay", &periph_noresrc_delay); 10473cf209fSMatt Jacob static int periph_busy_delay = 500; 10573cf209fSMatt Jacob TUNABLE_INT("kern.cam.periph_busy_delay", &periph_busy_delay); 10673cf209fSMatt Jacob 10773cf209fSMatt Jacob 1080b7c27b9SPeter Wemm void 1090b7c27b9SPeter Wemm periphdriver_register(void *data) 1100b7c27b9SPeter Wemm { 11183c5d981SAlexander Motin struct periph_driver *drv = (struct periph_driver *)data; 1120b7c27b9SPeter Wemm struct periph_driver **newdrivers, **old; 1130b7c27b9SPeter Wemm int ndrivers; 1140b7c27b9SPeter Wemm 1155719711fSEdward Tomasz Napierala again: 1160b7c27b9SPeter Wemm ndrivers = nperiph_drivers + 2; 1170dd50e9bSScott Long newdrivers = malloc(sizeof(*newdrivers) * ndrivers, M_CAMPERIPH, 1180dd50e9bSScott Long M_WAITOK); 1195719711fSEdward Tomasz Napierala xpt_lock_buses(); 1205719711fSEdward Tomasz Napierala if (ndrivers != nperiph_drivers + 2) { 1215719711fSEdward Tomasz Napierala /* 1225719711fSEdward Tomasz Napierala * Lost race against itself; go around. 1235719711fSEdward Tomasz Napierala */ 1245719711fSEdward Tomasz Napierala xpt_unlock_buses(); 1255719711fSEdward Tomasz Napierala free(newdrivers, M_CAMPERIPH); 1265719711fSEdward Tomasz Napierala goto again; 1275719711fSEdward Tomasz Napierala } 1280b7c27b9SPeter Wemm if (periph_drivers) 1290b7c27b9SPeter Wemm bcopy(periph_drivers, newdrivers, 130623db360SKenneth D. Merry sizeof(*newdrivers) * nperiph_drivers); 13183c5d981SAlexander Motin newdrivers[nperiph_drivers] = drv; 1320b7c27b9SPeter Wemm newdrivers[nperiph_drivers + 1] = NULL; 1330b7c27b9SPeter Wemm old = periph_drivers; 1340b7c27b9SPeter Wemm periph_drivers = newdrivers; 1355719711fSEdward Tomasz Napierala nperiph_drivers++; 1365719711fSEdward Tomasz Napierala xpt_unlock_buses(); 1370b7c27b9SPeter Wemm if (old) 1380dd50e9bSScott Long free(old, M_CAMPERIPH); 13983c5d981SAlexander Motin /* If driver marked as early or it is late now, initialize it. */ 14083c5d981SAlexander Motin if (((drv->flags & CAM_PERIPH_DRV_EARLY) != 0 && initialized > 0) || 14183c5d981SAlexander Motin initialized > 1) 14283c5d981SAlexander Motin (*drv->init)(); 14383c5d981SAlexander Motin } 14483c5d981SAlexander Motin 14594173c3cSAlexander Motin int 14694173c3cSAlexander Motin periphdriver_unregister(void *data) 14794173c3cSAlexander Motin { 14894173c3cSAlexander Motin struct periph_driver *drv = (struct periph_driver *)data; 14994173c3cSAlexander Motin int error, n; 15094173c3cSAlexander Motin 15194173c3cSAlexander Motin /* If driver marked as early or it is late now, deinitialize it. */ 15294173c3cSAlexander Motin if (((drv->flags & CAM_PERIPH_DRV_EARLY) != 0 && initialized > 0) || 15394173c3cSAlexander Motin initialized > 1) { 15494173c3cSAlexander Motin if (drv->deinit == NULL) { 15594173c3cSAlexander Motin printf("CAM periph driver '%s' doesn't have deinit.\n", 15694173c3cSAlexander Motin drv->driver_name); 15794173c3cSAlexander Motin return (EOPNOTSUPP); 15894173c3cSAlexander Motin } 15994173c3cSAlexander Motin error = drv->deinit(); 16094173c3cSAlexander Motin if (error != 0) 16194173c3cSAlexander Motin return (error); 16294173c3cSAlexander Motin } 16394173c3cSAlexander Motin 16494173c3cSAlexander Motin xpt_lock_buses(); 16594173c3cSAlexander Motin for (n = 0; n < nperiph_drivers && periph_drivers[n] != drv; n++) 16694173c3cSAlexander Motin ; 16794173c3cSAlexander Motin KASSERT(n < nperiph_drivers, 16894173c3cSAlexander Motin ("Periph driver '%s' was not registered", drv->driver_name)); 16994173c3cSAlexander Motin for (; n + 1 < nperiph_drivers; n++) 17094173c3cSAlexander Motin periph_drivers[n] = periph_drivers[n + 1]; 17194173c3cSAlexander Motin periph_drivers[n + 1] = NULL; 17294173c3cSAlexander Motin nperiph_drivers--; 17394173c3cSAlexander Motin xpt_unlock_buses(); 17494173c3cSAlexander Motin return (0); 17594173c3cSAlexander Motin } 17694173c3cSAlexander Motin 17783c5d981SAlexander Motin void 17883c5d981SAlexander Motin periphdriver_init(int level) 17983c5d981SAlexander Motin { 18083c5d981SAlexander Motin int i, early; 18183c5d981SAlexander Motin 18283c5d981SAlexander Motin initialized = max(initialized, level); 18383c5d981SAlexander Motin for (i = 0; periph_drivers[i] != NULL; i++) { 18483c5d981SAlexander Motin early = (periph_drivers[i]->flags & CAM_PERIPH_DRV_EARLY) ? 1 : 2; 18583c5d981SAlexander Motin if (early == initialized) 18683c5d981SAlexander Motin (*periph_drivers[i]->init)(); 18783c5d981SAlexander Motin } 1880b7c27b9SPeter Wemm } 1890b7c27b9SPeter Wemm 1908b8a9b1dSJustin T. Gibbs cam_status 191ee9c90c7SKenneth D. Merry cam_periph_alloc(periph_ctor_t *periph_ctor, 192ee9c90c7SKenneth D. Merry periph_oninv_t *periph_oninvalidate, 193ee9c90c7SKenneth D. Merry periph_dtor_t *periph_dtor, periph_start_t *periph_start, 194ee9c90c7SKenneth D. Merry char *name, cam_periph_type type, struct cam_path *path, 195ee9c90c7SKenneth D. Merry ac_callback_t *ac_callback, ac_code code, void *arg) 1968b8a9b1dSJustin T. Gibbs { 1978b8a9b1dSJustin T. Gibbs struct periph_driver **p_drv; 1982b83592fSScott Long struct cam_sim *sim; 1998b8a9b1dSJustin T. Gibbs struct cam_periph *periph; 2008b8a9b1dSJustin T. Gibbs struct cam_periph *cur_periph; 2018b8a9b1dSJustin T. Gibbs path_id_t path_id; 2028b8a9b1dSJustin T. Gibbs target_id_t target_id; 2038b8a9b1dSJustin T. Gibbs lun_id_t lun_id; 2048b8a9b1dSJustin T. Gibbs cam_status status; 2058b8a9b1dSJustin T. Gibbs u_int init_level; 2068b8a9b1dSJustin T. Gibbs 2078b8a9b1dSJustin T. Gibbs init_level = 0; 2088b8a9b1dSJustin T. Gibbs /* 2098b8a9b1dSJustin T. Gibbs * Handle Hot-Plug scenarios. If there is already a peripheral 2108b8a9b1dSJustin T. Gibbs * of our type assigned to this path, we are likely waiting for 2118b8a9b1dSJustin T. Gibbs * final close on an old, invalidated, peripheral. If this is 2128b8a9b1dSJustin T. Gibbs * the case, queue up a deferred call to the peripheral's async 21374c91ec5SJustin T. Gibbs * handler. If it looks like a mistaken re-allocation, complain. 2148b8a9b1dSJustin T. Gibbs */ 2158b8a9b1dSJustin T. Gibbs if ((periph = cam_periph_find(path, name)) != NULL) { 2168b8a9b1dSJustin T. Gibbs 2178b8a9b1dSJustin T. Gibbs if ((periph->flags & CAM_PERIPH_INVALID) != 0 2188b8a9b1dSJustin T. Gibbs && (periph->flags & CAM_PERIPH_NEW_DEV_FOUND) == 0) { 2198b8a9b1dSJustin T. Gibbs periph->flags |= CAM_PERIPH_NEW_DEV_FOUND; 2208b8a9b1dSJustin T. Gibbs periph->deferred_callback = ac_callback; 2218b8a9b1dSJustin T. Gibbs periph->deferred_ac = code; 2228b8a9b1dSJustin T. Gibbs return (CAM_REQ_INPROG); 2238b8a9b1dSJustin T. Gibbs } else { 2248b8a9b1dSJustin T. Gibbs printf("cam_periph_alloc: attempt to re-allocate " 2258900f4b8SKenneth D. Merry "valid device %s%d rejected flags %#x " 2268900f4b8SKenneth D. Merry "refcount %d\n", periph->periph_name, 2278900f4b8SKenneth D. Merry periph->unit_number, periph->flags, 2288900f4b8SKenneth D. Merry periph->refcount); 2298b8a9b1dSJustin T. Gibbs } 2308b8a9b1dSJustin T. Gibbs return (CAM_REQ_INVALID); 2318b8a9b1dSJustin T. Gibbs } 2328b8a9b1dSJustin T. Gibbs 233362abc44STai-hwa Liang periph = (struct cam_periph *)malloc(sizeof(*periph), M_CAMPERIPH, 2348900f4b8SKenneth D. Merry M_NOWAIT|M_ZERO); 2358b8a9b1dSJustin T. Gibbs 2368b8a9b1dSJustin T. Gibbs if (periph == NULL) 2378b8a9b1dSJustin T. Gibbs return (CAM_RESRC_UNAVAIL); 2388b8a9b1dSJustin T. Gibbs 2398b8a9b1dSJustin T. Gibbs init_level++; 2408b8a9b1dSJustin T. Gibbs 2418b8a9b1dSJustin T. Gibbs 2422b83592fSScott Long sim = xpt_path_sim(path); 2438b8a9b1dSJustin T. Gibbs path_id = xpt_path_path_id(path); 2448b8a9b1dSJustin T. Gibbs target_id = xpt_path_target_id(path); 2458b8a9b1dSJustin T. Gibbs lun_id = xpt_path_lun_id(path); 2468b8a9b1dSJustin T. Gibbs periph->periph_start = periph_start; 2478b8a9b1dSJustin T. Gibbs periph->periph_dtor = periph_dtor; 248ee9c90c7SKenneth D. Merry periph->periph_oninval = periph_oninvalidate; 2498b8a9b1dSJustin T. Gibbs periph->type = type; 2508b8a9b1dSJustin T. Gibbs periph->periph_name = name; 251227d67aaSAlexander Motin periph->scheduled_priority = CAM_PRIORITY_NONE; 2528b8a9b1dSJustin T. Gibbs periph->immediate_priority = CAM_PRIORITY_NONE; 253d38c0e53SAlexander Motin periph->refcount = 1; /* Dropped by invalidation. */ 2542b83592fSScott Long periph->sim = sim; 2558b8a9b1dSJustin T. Gibbs SLIST_INIT(&periph->ccb_list); 2568b8a9b1dSJustin T. Gibbs status = xpt_create_path(&path, periph, path_id, target_id, lun_id); 2578b8a9b1dSJustin T. Gibbs if (status != CAM_REQ_CMP) 2588b8a9b1dSJustin T. Gibbs goto failure; 2598b8a9b1dSJustin T. Gibbs periph->path = path; 2608b8a9b1dSJustin T. Gibbs 261f1e2546aSMatt Jacob xpt_lock_buses(); 262f1e2546aSMatt Jacob for (p_drv = periph_drivers; *p_drv != NULL; p_drv++) { 263f1e2546aSMatt Jacob if (strcmp((*p_drv)->driver_name, name) == 0) 264f1e2546aSMatt Jacob break; 265f1e2546aSMatt Jacob } 266f1e2546aSMatt Jacob if (*p_drv == NULL) { 267f1e2546aSMatt Jacob printf("cam_periph_alloc: invalid periph name '%s'\n", name); 2689e0d30e2SEdward Tomasz Napierala xpt_unlock_buses(); 269f1e2546aSMatt Jacob xpt_free_path(periph->path); 270f1e2546aSMatt Jacob free(periph, M_CAMPERIPH); 271f1e2546aSMatt Jacob return (CAM_REQ_INVALID); 272f1e2546aSMatt Jacob } 273f1e2546aSMatt Jacob periph->unit_number = camperiphunit(*p_drv, path_id, target_id, lun_id); 2748b8a9b1dSJustin T. Gibbs cur_periph = TAILQ_FIRST(&(*p_drv)->units); 2758b8a9b1dSJustin T. Gibbs while (cur_periph != NULL 2768b8a9b1dSJustin T. Gibbs && cur_periph->unit_number < periph->unit_number) 2778b8a9b1dSJustin T. Gibbs cur_periph = TAILQ_NEXT(cur_periph, unit_links); 278f1e2546aSMatt Jacob if (cur_periph != NULL) { 279f1e2546aSMatt Jacob KASSERT(cur_periph->unit_number != periph->unit_number, ("duplicate units on periph list")); 2808b8a9b1dSJustin T. Gibbs TAILQ_INSERT_BEFORE(cur_periph, periph, unit_links); 281f1e2546aSMatt Jacob } else { 2828b8a9b1dSJustin T. Gibbs TAILQ_INSERT_TAIL(&(*p_drv)->units, periph, unit_links); 2838b8a9b1dSJustin T. Gibbs (*p_drv)->generation++; 2848b8a9b1dSJustin T. Gibbs } 285f1e2546aSMatt Jacob xpt_unlock_buses(); 286f1e2546aSMatt Jacob 287f1e2546aSMatt Jacob init_level++; 288f1e2546aSMatt Jacob 289f1e2546aSMatt Jacob status = xpt_add_periph(periph); 290f1e2546aSMatt Jacob if (status != CAM_REQ_CMP) 291f1e2546aSMatt Jacob goto failure; 2928b8a9b1dSJustin T. Gibbs 2938b8a9b1dSJustin T. Gibbs init_level++; 29422c7d606SAlexander Motin CAM_DEBUG(periph->path, CAM_DEBUG_INFO, ("Periph created\n")); 2958b8a9b1dSJustin T. Gibbs 2968b8a9b1dSJustin T. Gibbs status = periph_ctor(periph, arg); 2978b8a9b1dSJustin T. Gibbs 2988b8a9b1dSJustin T. Gibbs if (status == CAM_REQ_CMP) 2998b8a9b1dSJustin T. Gibbs init_level++; 3008b8a9b1dSJustin T. Gibbs 3018b8a9b1dSJustin T. Gibbs failure: 3028b8a9b1dSJustin T. Gibbs switch (init_level) { 3038b8a9b1dSJustin T. Gibbs case 4: 3048b8a9b1dSJustin T. Gibbs /* Initialized successfully */ 3058b8a9b1dSJustin T. Gibbs break; 3068b8a9b1dSJustin T. Gibbs case 3: 30722c7d606SAlexander Motin CAM_DEBUG(periph->path, CAM_DEBUG_INFO, ("Periph destroyed\n")); 308a29779e8SAlexander Motin xpt_remove_periph(periph); 3097379c88fSPoul-Henning Kamp /* FALLTHROUGH */ 3108b8a9b1dSJustin T. Gibbs case 2: 311f1e2546aSMatt Jacob xpt_lock_buses(); 312f1e2546aSMatt Jacob TAILQ_REMOVE(&(*p_drv)->units, periph, unit_links); 313f1e2546aSMatt Jacob xpt_unlock_buses(); 3148b8a9b1dSJustin T. Gibbs xpt_free_path(periph->path); 3157379c88fSPoul-Henning Kamp /* FALLTHROUGH */ 3168b8a9b1dSJustin T. Gibbs case 1: 317362abc44STai-hwa Liang free(periph, M_CAMPERIPH); 3187379c88fSPoul-Henning Kamp /* FALLTHROUGH */ 3198b8a9b1dSJustin T. Gibbs case 0: 3208b8a9b1dSJustin T. Gibbs /* No cleanup to perform. */ 3218b8a9b1dSJustin T. Gibbs break; 3228b8a9b1dSJustin T. Gibbs default: 323905cb85dSKenneth D. Merry panic("%s: Unknown init level", __func__); 3248b8a9b1dSJustin T. Gibbs } 3258b8a9b1dSJustin T. Gibbs return(status); 3268b8a9b1dSJustin T. Gibbs } 3278b8a9b1dSJustin T. Gibbs 3288b8a9b1dSJustin T. Gibbs /* 3298b8a9b1dSJustin T. Gibbs * Find a peripheral structure with the specified path, target, lun, 3308b8a9b1dSJustin T. Gibbs * and (optionally) type. If the name is NULL, this function will return 3318b8a9b1dSJustin T. Gibbs * the first peripheral driver that matches the specified path. 3328b8a9b1dSJustin T. Gibbs */ 3338b8a9b1dSJustin T. Gibbs struct cam_periph * 3348b8a9b1dSJustin T. Gibbs cam_periph_find(struct cam_path *path, char *name) 3358b8a9b1dSJustin T. Gibbs { 3368b8a9b1dSJustin T. Gibbs struct periph_driver **p_drv; 3378b8a9b1dSJustin T. Gibbs struct cam_periph *periph; 3388b8a9b1dSJustin T. Gibbs 33977dc25ccSScott Long xpt_lock_buses(); 3400b7c27b9SPeter Wemm for (p_drv = periph_drivers; *p_drv != NULL; p_drv++) { 3418b8a9b1dSJustin T. Gibbs 3428b8a9b1dSJustin T. Gibbs if (name != NULL && (strcmp((*p_drv)->driver_name, name) != 0)) 3438b8a9b1dSJustin T. Gibbs continue; 3448b8a9b1dSJustin T. Gibbs 34537d40066SPoul-Henning Kamp TAILQ_FOREACH(periph, &(*p_drv)->units, unit_links) { 3468b8a9b1dSJustin T. Gibbs if (xpt_path_comp(periph->path, path) == 0) { 34777dc25ccSScott Long xpt_unlock_buses(); 348227d67aaSAlexander Motin cam_periph_assert(periph, MA_OWNED); 3498b8a9b1dSJustin T. Gibbs return(periph); 3508b8a9b1dSJustin T. Gibbs } 3518b8a9b1dSJustin T. Gibbs } 35277dc25ccSScott Long if (name != NULL) { 35377dc25ccSScott Long xpt_unlock_buses(); 3548b8a9b1dSJustin T. Gibbs return(NULL); 3558b8a9b1dSJustin T. Gibbs } 35677dc25ccSScott Long } 35777dc25ccSScott Long xpt_unlock_buses(); 3588b8a9b1dSJustin T. Gibbs return(NULL); 3598b8a9b1dSJustin T. Gibbs } 3608b8a9b1dSJustin T. Gibbs 3613501942bSJustin T. Gibbs /* 3628900f4b8SKenneth D. Merry * Find peripheral driver instances attached to the specified path. 3633501942bSJustin T. Gibbs */ 3643501942bSJustin T. Gibbs int 3653501942bSJustin T. Gibbs cam_periph_list(struct cam_path *path, struct sbuf *sb) 3663501942bSJustin T. Gibbs { 3678900f4b8SKenneth D. Merry struct sbuf local_sb; 3683501942bSJustin T. Gibbs struct periph_driver **p_drv; 3693501942bSJustin T. Gibbs struct cam_periph *periph; 3703501942bSJustin T. Gibbs int count; 3718900f4b8SKenneth D. Merry int sbuf_alloc_len; 3723501942bSJustin T. Gibbs 3738900f4b8SKenneth D. Merry sbuf_alloc_len = 16; 3748900f4b8SKenneth D. Merry retry: 3758900f4b8SKenneth D. Merry sbuf_new(&local_sb, NULL, sbuf_alloc_len, SBUF_FIXEDLEN); 3763501942bSJustin T. Gibbs count = 0; 3773501942bSJustin T. Gibbs xpt_lock_buses(); 3783501942bSJustin T. Gibbs for (p_drv = periph_drivers; *p_drv != NULL; p_drv++) { 3793501942bSJustin T. Gibbs 3803501942bSJustin T. Gibbs TAILQ_FOREACH(periph, &(*p_drv)->units, unit_links) { 3813501942bSJustin T. Gibbs if (xpt_path_comp(periph->path, path) != 0) 3823501942bSJustin T. Gibbs continue; 3833501942bSJustin T. Gibbs 3848900f4b8SKenneth D. Merry if (sbuf_len(&local_sb) != 0) 3858900f4b8SKenneth D. Merry sbuf_cat(&local_sb, ","); 3863501942bSJustin T. Gibbs 3878900f4b8SKenneth D. Merry sbuf_printf(&local_sb, "%s%d", periph->periph_name, 3883501942bSJustin T. Gibbs periph->unit_number); 3898900f4b8SKenneth D. Merry 3908900f4b8SKenneth D. Merry if (sbuf_error(&local_sb) == ENOMEM) { 3918900f4b8SKenneth D. Merry sbuf_alloc_len *= 2; 3928900f4b8SKenneth D. Merry xpt_unlock_buses(); 3938900f4b8SKenneth D. Merry sbuf_delete(&local_sb); 3948900f4b8SKenneth D. Merry goto retry; 3958900f4b8SKenneth D. Merry } 3963501942bSJustin T. Gibbs count++; 3973501942bSJustin T. Gibbs } 3983501942bSJustin T. Gibbs } 3993501942bSJustin T. Gibbs xpt_unlock_buses(); 4008900f4b8SKenneth D. Merry sbuf_finish(&local_sb); 4018900f4b8SKenneth D. Merry sbuf_cpy(sb, sbuf_data(&local_sb)); 4028900f4b8SKenneth D. Merry sbuf_delete(&local_sb); 4033501942bSJustin T. Gibbs return (count); 4043501942bSJustin T. Gibbs } 4053501942bSJustin T. Gibbs 406*99e7a4adSScott Long int 4078b8a9b1dSJustin T. Gibbs cam_periph_acquire(struct cam_periph *periph) 4088b8a9b1dSJustin T. Gibbs { 409*99e7a4adSScott Long int status; 4108b8a9b1dSJustin T. Gibbs 4118b8a9b1dSJustin T. Gibbs if (periph == NULL) 412*99e7a4adSScott Long return (EINVAL); 4138b8a9b1dSJustin T. Gibbs 414*99e7a4adSScott Long status = ENOENT; 4152b83592fSScott Long xpt_lock_buses(); 4168900f4b8SKenneth D. Merry if ((periph->flags & CAM_PERIPH_INVALID) == 0) { 4178b8a9b1dSJustin T. Gibbs periph->refcount++; 418*99e7a4adSScott Long status = 0; 4198900f4b8SKenneth D. Merry } 4202b83592fSScott Long xpt_unlock_buses(); 4218b8a9b1dSJustin T. Gibbs 4228900f4b8SKenneth D. Merry return (status); 4238900f4b8SKenneth D. Merry } 4248900f4b8SKenneth D. Merry 4258900f4b8SKenneth D. Merry void 426c33e4029SAlexander Motin cam_periph_doacquire(struct cam_periph *periph) 427c33e4029SAlexander Motin { 428c33e4029SAlexander Motin 429c33e4029SAlexander Motin xpt_lock_buses(); 430c33e4029SAlexander Motin KASSERT(periph->refcount >= 1, 431c33e4029SAlexander Motin ("cam_periph_doacquire() with refcount == %d", periph->refcount)); 432c33e4029SAlexander Motin periph->refcount++; 433c33e4029SAlexander Motin xpt_unlock_buses(); 434c33e4029SAlexander Motin } 435c33e4029SAlexander Motin 436c33e4029SAlexander Motin void 4378900f4b8SKenneth D. Merry cam_periph_release_locked_buses(struct cam_periph *periph) 4388900f4b8SKenneth D. Merry { 439dcdf6e74SAlexander Motin 440227d67aaSAlexander Motin cam_periph_assert(periph, MA_OWNED); 441dcdf6e74SAlexander Motin KASSERT(periph->refcount >= 1, ("periph->refcount >= 1")); 442d38c0e53SAlexander Motin if (--periph->refcount == 0) 4438900f4b8SKenneth D. Merry camperiphfree(periph); 4448900f4b8SKenneth D. Merry } 4458b8a9b1dSJustin T. Gibbs 4468b8a9b1dSJustin T. Gibbs void 44724ebf566SEdward Tomasz Napierala cam_periph_release_locked(struct cam_periph *periph) 4488b8a9b1dSJustin T. Gibbs { 4498b8a9b1dSJustin T. Gibbs 4508b8a9b1dSJustin T. Gibbs if (periph == NULL) 4518b8a9b1dSJustin T. Gibbs return; 4528b8a9b1dSJustin T. Gibbs 4532b83592fSScott Long xpt_lock_buses(); 4548900f4b8SKenneth D. Merry cam_periph_release_locked_buses(periph); 4552b83592fSScott Long xpt_unlock_buses(); 45624ebf566SEdward Tomasz Napierala } 4578b8a9b1dSJustin T. Gibbs 45824ebf566SEdward Tomasz Napierala void 45924ebf566SEdward Tomasz Napierala cam_periph_release(struct cam_periph *periph) 46024ebf566SEdward Tomasz Napierala { 461227d67aaSAlexander Motin struct mtx *mtx; 46224ebf566SEdward Tomasz Napierala 46324ebf566SEdward Tomasz Napierala if (periph == NULL) 46424ebf566SEdward Tomasz Napierala return; 46524ebf566SEdward Tomasz Napierala 466227d67aaSAlexander Motin cam_periph_assert(periph, MA_NOTOWNED); 467227d67aaSAlexander Motin mtx = cam_periph_mtx(periph); 468227d67aaSAlexander Motin mtx_lock(mtx); 46924ebf566SEdward Tomasz Napierala cam_periph_release_locked(periph); 470227d67aaSAlexander Motin mtx_unlock(mtx); 4718b8a9b1dSJustin T. Gibbs } 4728b8a9b1dSJustin T. Gibbs 4732b83592fSScott Long int 4742b83592fSScott Long cam_periph_hold(struct cam_periph *periph, int priority) 4752b83592fSScott Long { 4762b83592fSScott Long int error; 4772b83592fSScott Long 4782b83592fSScott Long /* 4792b83592fSScott Long * Increment the reference count on the peripheral 4802b83592fSScott Long * while we wait for our lock attempt to succeed 4812b83592fSScott Long * to ensure the peripheral doesn't disappear out 4822b83592fSScott Long * from user us while we sleep. 4832b83592fSScott Long */ 4842b83592fSScott Long 485*99e7a4adSScott Long if (cam_periph_acquire(periph) != 0) 4862b83592fSScott Long return (ENXIO); 4872b83592fSScott Long 488227d67aaSAlexander Motin cam_periph_assert(periph, MA_OWNED); 4892b83592fSScott Long while ((periph->flags & CAM_PERIPH_LOCKED) != 0) { 4902b83592fSScott Long periph->flags |= CAM_PERIPH_LOCK_WANTED; 491227d67aaSAlexander Motin if ((error = cam_periph_sleep(periph, periph, priority, 4921fa738c2SJohn Baldwin "caplck", 0)) != 0) { 49324ebf566SEdward Tomasz Napierala cam_periph_release_locked(periph); 4942b83592fSScott Long return (error); 4952b83592fSScott Long } 496aed9c88cSAlexander Motin if (periph->flags & CAM_PERIPH_INVALID) { 497aed9c88cSAlexander Motin cam_periph_release_locked(periph); 498aed9c88cSAlexander Motin return (ENXIO); 499aed9c88cSAlexander Motin } 5002b83592fSScott Long } 5012b83592fSScott Long 5022b83592fSScott Long periph->flags |= CAM_PERIPH_LOCKED; 5032b83592fSScott Long return (0); 5042b83592fSScott Long } 5052b83592fSScott Long 5062b83592fSScott Long void 5072b83592fSScott Long cam_periph_unhold(struct cam_periph *periph) 5082b83592fSScott Long { 5092b83592fSScott Long 510227d67aaSAlexander Motin cam_periph_assert(periph, MA_OWNED); 5112b83592fSScott Long 5122b83592fSScott Long periph->flags &= ~CAM_PERIPH_LOCKED; 5132b83592fSScott Long if ((periph->flags & CAM_PERIPH_LOCK_WANTED) != 0) { 5142b83592fSScott Long periph->flags &= ~CAM_PERIPH_LOCK_WANTED; 5152b83592fSScott Long wakeup(periph); 5162b83592fSScott Long } 5172b83592fSScott Long 51824ebf566SEdward Tomasz Napierala cam_periph_release_locked(periph); 5192b83592fSScott Long } 5202b83592fSScott Long 5218b8a9b1dSJustin T. Gibbs /* 5228b8a9b1dSJustin T. Gibbs * Look for the next unit number that is not currently in use for this 5238b8a9b1dSJustin T. Gibbs * peripheral type starting at "newunit". Also exclude unit numbers that 5248b8a9b1dSJustin T. Gibbs * are reserved by for future "hardwiring" unless we already know that this 5258b8a9b1dSJustin T. Gibbs * is a potential wired device. Only assume that the device is "wired" the 5268b8a9b1dSJustin T. Gibbs * first time through the loop since after that we'll be looking at unit 5278b8a9b1dSJustin T. Gibbs * numbers that did not match a wiring entry. 5288b8a9b1dSJustin T. Gibbs */ 5298b8a9b1dSJustin T. Gibbs static u_int 530501468a5SKenneth D. Merry camperiphnextunit(struct periph_driver *p_drv, u_int newunit, int wired, 531501468a5SKenneth D. Merry path_id_t pathid, target_id_t target, lun_id_t lun) 5328b8a9b1dSJustin T. Gibbs { 5338b8a9b1dSJustin T. Gibbs struct cam_periph *periph; 5342398f0cdSPeter Wemm char *periph_name; 5352398f0cdSPeter Wemm int i, val, dunit, r; 5362398f0cdSPeter Wemm const char *dname, *strval; 5378b8a9b1dSJustin T. Gibbs 5388b8a9b1dSJustin T. Gibbs periph_name = p_drv->driver_name; 5398b8a9b1dSJustin T. Gibbs for (;;newunit++) { 5408b8a9b1dSJustin T. Gibbs 5418b8a9b1dSJustin T. Gibbs for (periph = TAILQ_FIRST(&p_drv->units); 5428b8a9b1dSJustin T. Gibbs periph != NULL && periph->unit_number != newunit; 5438b8a9b1dSJustin T. Gibbs periph = TAILQ_NEXT(periph, unit_links)) 5448b8a9b1dSJustin T. Gibbs ; 5458b8a9b1dSJustin T. Gibbs 5468b8a9b1dSJustin T. Gibbs if (periph != NULL && periph->unit_number == newunit) { 5478b8a9b1dSJustin T. Gibbs if (wired != 0) { 548f0d9af51SMatt Jacob xpt_print(periph->path, "Duplicate Wired " 549f0d9af51SMatt Jacob "Device entry!\n"); 550f0d9af51SMatt Jacob xpt_print(periph->path, "Second device (%s " 551f0d9af51SMatt Jacob "device at scbus%d target %d lun %d) will " 552f0d9af51SMatt Jacob "not be wired\n", periph_name, pathid, 553f0d9af51SMatt Jacob target, lun); 5548b8a9b1dSJustin T. Gibbs wired = 0; 5558b8a9b1dSJustin T. Gibbs } 5568b8a9b1dSJustin T. Gibbs continue; 5578b8a9b1dSJustin T. Gibbs } 55875f51904SPeter Wemm if (wired) 55975f51904SPeter Wemm break; 5608b8a9b1dSJustin T. Gibbs 5618b8a9b1dSJustin T. Gibbs /* 5628b8a9b1dSJustin T. Gibbs * Don't match entries like "da 4" as a wired down 5638b8a9b1dSJustin T. Gibbs * device, but do match entries like "da 4 target 5" 5648b8a9b1dSJustin T. Gibbs * or even "da 4 scbus 1". 5658b8a9b1dSJustin T. Gibbs */ 5662398f0cdSPeter Wemm i = 0; 5672398f0cdSPeter Wemm dname = periph_name; 5682398f0cdSPeter Wemm for (;;) { 5692398f0cdSPeter Wemm r = resource_find_dev(&i, dname, &dunit, NULL, NULL); 5702398f0cdSPeter Wemm if (r != 0) 5712398f0cdSPeter Wemm break; 57275f51904SPeter Wemm /* if no "target" and no specific scbus, skip */ 57375f51904SPeter Wemm if (resource_int_value(dname, dunit, "target", &val) && 57475f51904SPeter Wemm (resource_string_value(dname, dunit, "at",&strval)|| 57575f51904SPeter Wemm strcmp(strval, "scbus") == 0)) 57675f51904SPeter Wemm continue; 57775f51904SPeter Wemm if (newunit == dunit) 5788b8a9b1dSJustin T. Gibbs break; 5798b8a9b1dSJustin T. Gibbs } 5802398f0cdSPeter Wemm if (r != 0) 5818b8a9b1dSJustin T. Gibbs break; 5828b8a9b1dSJustin T. Gibbs } 5838b8a9b1dSJustin T. Gibbs return (newunit); 5848b8a9b1dSJustin T. Gibbs } 5858b8a9b1dSJustin T. Gibbs 5868b8a9b1dSJustin T. Gibbs static u_int 5878b8a9b1dSJustin T. Gibbs camperiphunit(struct periph_driver *p_drv, path_id_t pathid, 5888b8a9b1dSJustin T. Gibbs target_id_t target, lun_id_t lun) 5898b8a9b1dSJustin T. Gibbs { 5908b8a9b1dSJustin T. Gibbs u_int unit; 591c1b81613SPeter Wemm int wired, i, val, dunit; 5922398f0cdSPeter Wemm const char *dname, *strval; 5932398f0cdSPeter Wemm char pathbuf[32], *periph_name; 5948b8a9b1dSJustin T. Gibbs 59575f51904SPeter Wemm periph_name = p_drv->driver_name; 59675f51904SPeter Wemm snprintf(pathbuf, sizeof(pathbuf), "scbus%d", pathid); 597c1b81613SPeter Wemm unit = 0; 5982398f0cdSPeter Wemm i = 0; 5992398f0cdSPeter Wemm dname = periph_name; 600c1b81613SPeter Wemm for (wired = 0; resource_find_dev(&i, dname, &dunit, NULL, NULL) == 0; 601c1b81613SPeter Wemm wired = 0) { 60275f51904SPeter Wemm if (resource_string_value(dname, dunit, "at", &strval) == 0) { 60375f51904SPeter Wemm if (strcmp(strval, pathbuf) != 0) 6048b8a9b1dSJustin T. Gibbs continue; 605c1b81613SPeter Wemm wired++; 6068b8a9b1dSJustin T. Gibbs } 60775f51904SPeter Wemm if (resource_int_value(dname, dunit, "target", &val) == 0) { 60875f51904SPeter Wemm if (val != target) 6098b8a9b1dSJustin T. Gibbs continue; 610c1b81613SPeter Wemm wired++; 6118b8a9b1dSJustin T. Gibbs } 61275f51904SPeter Wemm if (resource_int_value(dname, dunit, "lun", &val) == 0) { 61375f51904SPeter Wemm if (val != lun) 6148b8a9b1dSJustin T. Gibbs continue; 615c1b81613SPeter Wemm wired++; 6168b8a9b1dSJustin T. Gibbs } 617c1b81613SPeter Wemm if (wired != 0) { 61875f51904SPeter Wemm unit = dunit; 6198b8a9b1dSJustin T. Gibbs break; 6208b8a9b1dSJustin T. Gibbs } 6218b8a9b1dSJustin T. Gibbs } 6228b8a9b1dSJustin T. Gibbs 6238b8a9b1dSJustin T. Gibbs /* 6248b8a9b1dSJustin T. Gibbs * Either start from 0 looking for the next unit or from 62575f51904SPeter Wemm * the unit number given in the resource config. This way, 6268b8a9b1dSJustin T. Gibbs * if we have wildcard matches, we don't return the same 6278b8a9b1dSJustin T. Gibbs * unit number twice. 6288b8a9b1dSJustin T. Gibbs */ 629c1b81613SPeter Wemm unit = camperiphnextunit(p_drv, unit, wired, pathid, target, lun); 6308b8a9b1dSJustin T. Gibbs 6318b8a9b1dSJustin T. Gibbs return (unit); 6328b8a9b1dSJustin T. Gibbs } 6338b8a9b1dSJustin T. Gibbs 6348b8a9b1dSJustin T. Gibbs void 6358b8a9b1dSJustin T. Gibbs cam_periph_invalidate(struct cam_periph *periph) 6368b8a9b1dSJustin T. Gibbs { 6378b8a9b1dSJustin T. Gibbs 638227d67aaSAlexander Motin cam_periph_assert(periph, MA_OWNED); 639ee9c90c7SKenneth D. Merry /* 640ee9c90c7SKenneth D. Merry * We only call this routine the first time a peripheral is 64177dc25ccSScott Long * invalidated. 642ee9c90c7SKenneth D. Merry */ 643d38c0e53SAlexander Motin if ((periph->flags & CAM_PERIPH_INVALID) != 0) 644d38c0e53SAlexander Motin return; 645ee9c90c7SKenneth D. Merry 646d38c0e53SAlexander Motin CAM_DEBUG(periph->path, CAM_DEBUG_INFO, ("Periph invalidated\n")); 6475d01277fSScott Long if ((periph->flags & CAM_PERIPH_ANNOUNCED) && !rebooting) { 6485d01277fSScott Long struct sbuf sb; 649da0d7209SScott Long char buffer[160]; 6505d01277fSScott Long 651da0d7209SScott Long sbuf_new(&sb, buffer, 160, SBUF_FIXEDLEN); 6525d01277fSScott Long xpt_denounce_periph_sbuf(periph, &sb); 6535d01277fSScott Long sbuf_finish(&sb); 6545d01277fSScott Long sbuf_putbuf(&sb); 6555d01277fSScott Long } 6568b8a9b1dSJustin T. Gibbs periph->flags |= CAM_PERIPH_INVALID; 6578b8a9b1dSJustin T. Gibbs periph->flags &= ~CAM_PERIPH_NEW_DEV_FOUND; 658d38c0e53SAlexander Motin if (periph->periph_oninval != NULL) 659d38c0e53SAlexander Motin periph->periph_oninval(periph); 660d38c0e53SAlexander Motin cam_periph_release_locked(periph); 6618b8a9b1dSJustin T. Gibbs } 6628b8a9b1dSJustin T. Gibbs 6638b8a9b1dSJustin T. Gibbs static void 6648b8a9b1dSJustin T. Gibbs camperiphfree(struct cam_periph *periph) 6658b8a9b1dSJustin T. Gibbs { 6668b8a9b1dSJustin T. Gibbs struct periph_driver **p_drv; 66759fe7664SKenneth D. Merry struct periph_driver *drv; 6688b8a9b1dSJustin T. Gibbs 669227d67aaSAlexander Motin cam_periph_assert(periph, MA_OWNED); 670227d67aaSAlexander Motin KASSERT(periph->periph_allocating == 0, ("%s%d: freed while allocating", 671227d67aaSAlexander Motin periph->periph_name, periph->unit_number)); 6720b7c27b9SPeter Wemm for (p_drv = periph_drivers; *p_drv != NULL; p_drv++) { 6738b8a9b1dSJustin T. Gibbs if (strcmp((*p_drv)->driver_name, periph->periph_name) == 0) 6748b8a9b1dSJustin T. Gibbs break; 6758b8a9b1dSJustin T. Gibbs } 676661658a6SScott Long if (*p_drv == NULL) { 677661658a6SScott Long printf("camperiphfree: attempt to free non-existant periph\n"); 678661658a6SScott Long return; 679661658a6SScott Long } 68059fe7664SKenneth D. Merry /* 68159fe7664SKenneth D. Merry * Cache a pointer to the periph_driver structure. If a 68259fe7664SKenneth D. Merry * periph_driver is added or removed from the array (see 68359fe7664SKenneth D. Merry * periphdriver_register()) while we drop the toplogy lock 68459fe7664SKenneth D. Merry * below, p_drv may change. This doesn't protect against this 68559fe7664SKenneth D. Merry * particular periph_driver going away. That will require full 68659fe7664SKenneth D. Merry * reference counting in the periph_driver infrastructure. 68759fe7664SKenneth D. Merry */ 68859fe7664SKenneth D. Merry drv = *p_drv; 6898b8a9b1dSJustin T. Gibbs 690ea37f519SKenneth D. Merry /* 69133a38f74SKenneth D. Merry * We need to set this flag before dropping the topology lock, to 69233a38f74SKenneth D. Merry * let anyone who is traversing the list that this peripheral is 69333a38f74SKenneth D. Merry * about to be freed, and there will be no more reference count 69433a38f74SKenneth D. Merry * checks. 69533a38f74SKenneth D. Merry */ 69633a38f74SKenneth D. Merry periph->flags |= CAM_PERIPH_FREE; 69733a38f74SKenneth D. Merry 69833a38f74SKenneth D. Merry /* 699ea37f519SKenneth D. Merry * The peripheral destructor semantics dictate calling with only the 700ea37f519SKenneth D. Merry * SIM mutex held. Since it might sleep, it should not be called 701ea37f519SKenneth D. Merry * with the topology lock held. 702ea37f519SKenneth D. Merry */ 703f3548746SScott Long xpt_unlock_buses(); 7048b8a9b1dSJustin T. Gibbs 705ea37f519SKenneth D. Merry /* 706ea37f519SKenneth D. Merry * We need to call the peripheral destructor prior to removing the 707ea37f519SKenneth D. Merry * peripheral from the list. Otherwise, we risk running into a 708ea37f519SKenneth D. Merry * scenario where the peripheral unit number may get reused 709ea37f519SKenneth D. Merry * (because it has been removed from the list), but some resources 710ea37f519SKenneth D. Merry * used by the peripheral are still hanging around. In particular, 711ea37f519SKenneth D. Merry * the devfs nodes used by some peripherals like the pass(4) driver 712ea37f519SKenneth D. Merry * aren't fully cleaned up until the destructor is run. If the 713ea37f519SKenneth D. Merry * unit number is reused before the devfs instance is fully gone, 714ea37f519SKenneth D. Merry * devfs will panic. 715ea37f519SKenneth D. Merry */ 716f3548746SScott Long if (periph->periph_dtor != NULL) 717f3548746SScott Long periph->periph_dtor(periph); 718ea37f519SKenneth D. Merry 719ea37f519SKenneth D. Merry /* 720ea37f519SKenneth D. Merry * The peripheral list is protected by the topology lock. 721ea37f519SKenneth D. Merry */ 722ea37f519SKenneth D. Merry xpt_lock_buses(); 723ea37f519SKenneth D. Merry 72459fe7664SKenneth D. Merry TAILQ_REMOVE(&drv->units, periph, unit_links); 72559fe7664SKenneth D. Merry drv->generation++; 726ea37f519SKenneth D. Merry 727a29779e8SAlexander Motin xpt_remove_periph(periph); 728ea37f519SKenneth D. Merry 729ea37f519SKenneth D. Merry xpt_unlock_buses(); 730de6a705eSMarius Strobl if ((periph->flags & CAM_PERIPH_ANNOUNCED) && !rebooting) 7318d36a71bSAlexander Motin xpt_print(periph->path, "Periph destroyed\n"); 732de6a705eSMarius Strobl else 73322c7d606SAlexander Motin CAM_DEBUG(periph->path, CAM_DEBUG_INFO, ("Periph destroyed\n")); 7348b8a9b1dSJustin T. Gibbs 7358b8a9b1dSJustin T. Gibbs if (periph->flags & CAM_PERIPH_NEW_DEV_FOUND) { 7368b8a9b1dSJustin T. Gibbs union ccb ccb; 7378b8a9b1dSJustin T. Gibbs void *arg; 7388b8a9b1dSJustin T. Gibbs 7398b8a9b1dSJustin T. Gibbs switch (periph->deferred_ac) { 7408b8a9b1dSJustin T. Gibbs case AC_FOUND_DEVICE: 7418b8a9b1dSJustin T. Gibbs ccb.ccb_h.func_code = XPT_GDEV_TYPE; 742bbfa4aa1SAlexander Motin xpt_setup_ccb(&ccb.ccb_h, periph->path, CAM_PRIORITY_NORMAL); 7438b8a9b1dSJustin T. Gibbs xpt_action(&ccb); 7448b8a9b1dSJustin T. Gibbs arg = &ccb; 7458b8a9b1dSJustin T. Gibbs break; 7468b8a9b1dSJustin T. Gibbs case AC_PATH_REGISTERED: 747762a7f4fSWarner Losh xpt_path_inq(&ccb.cpi, periph->path); 7488b8a9b1dSJustin T. Gibbs arg = &ccb; 7498b8a9b1dSJustin T. Gibbs break; 7508b8a9b1dSJustin T. Gibbs default: 7518b8a9b1dSJustin T. Gibbs arg = NULL; 7528b8a9b1dSJustin T. Gibbs break; 7538b8a9b1dSJustin T. Gibbs } 7548b8a9b1dSJustin T. Gibbs periph->deferred_callback(NULL, periph->deferred_ac, 7558b8a9b1dSJustin T. Gibbs periph->path, arg); 7568b8a9b1dSJustin T. Gibbs } 7578b8a9b1dSJustin T. Gibbs xpt_free_path(periph->path); 758362abc44STai-hwa Liang free(periph, M_CAMPERIPH); 759f3548746SScott Long xpt_lock_buses(); 7608b8a9b1dSJustin T. Gibbs } 7618b8a9b1dSJustin T. Gibbs 7628b8a9b1dSJustin T. Gibbs /* 7638b8a9b1dSJustin T. Gibbs * Map user virtual pointers into kernel virtual address space, so we can 76495fbded6SScott Long * access the memory. This is now a generic function that centralizes most 76595fbded6SScott Long * of the sanity checks on the data flags, if any. 76695fbded6SScott Long * This also only works for up to MAXPHYS memory. Since we use 7678b8a9b1dSJustin T. Gibbs * buffers to map stuff in and out, we're limited to the buffer size. 7688b8a9b1dSJustin T. Gibbs */ 7698b8a9b1dSJustin T. Gibbs int 770de239312SAlexander Motin cam_periph_mapmem(union ccb *ccb, struct cam_periph_map_info *mapinfo, 771de239312SAlexander Motin u_int maxmap) 7728b8a9b1dSJustin T. Gibbs { 7732d5c7e45SMatthew Dillon int numbufs, i, j; 77479d49a06SKenneth D. Merry int flags[CAM_PERIPH_MAXMAPS]; 7758b8a9b1dSJustin T. Gibbs u_int8_t **data_ptrs[CAM_PERIPH_MAXMAPS]; 7768b8a9b1dSJustin T. Gibbs u_int32_t lengths[CAM_PERIPH_MAXMAPS]; 7778b8a9b1dSJustin T. Gibbs u_int32_t dirs[CAM_PERIPH_MAXMAPS]; 7788b8a9b1dSJustin T. Gibbs 779de239312SAlexander Motin if (maxmap == 0) 780de239312SAlexander Motin maxmap = DFLTPHYS; /* traditional default */ 781de239312SAlexander Motin else if (maxmap > MAXPHYS) 782de239312SAlexander Motin maxmap = MAXPHYS; /* for safety */ 7838b8a9b1dSJustin T. Gibbs switch(ccb->ccb_h.func_code) { 7848b8a9b1dSJustin T. Gibbs case XPT_DEV_MATCH: 7858b8a9b1dSJustin T. Gibbs if (ccb->cdm.match_buf_len == 0) { 7868b8a9b1dSJustin T. Gibbs printf("cam_periph_mapmem: invalid match buffer " 7878b8a9b1dSJustin T. Gibbs "length 0\n"); 7888b8a9b1dSJustin T. Gibbs return(EINVAL); 7898b8a9b1dSJustin T. Gibbs } 7908b8a9b1dSJustin T. Gibbs if (ccb->cdm.pattern_buf_len > 0) { 7918b8a9b1dSJustin T. Gibbs data_ptrs[0] = (u_int8_t **)&ccb->cdm.patterns; 7928b8a9b1dSJustin T. Gibbs lengths[0] = ccb->cdm.pattern_buf_len; 7938b8a9b1dSJustin T. Gibbs dirs[0] = CAM_DIR_OUT; 7948b8a9b1dSJustin T. Gibbs data_ptrs[1] = (u_int8_t **)&ccb->cdm.matches; 7958b8a9b1dSJustin T. Gibbs lengths[1] = ccb->cdm.match_buf_len; 7968b8a9b1dSJustin T. Gibbs dirs[1] = CAM_DIR_IN; 7978b8a9b1dSJustin T. Gibbs numbufs = 2; 7988b8a9b1dSJustin T. Gibbs } else { 7998b8a9b1dSJustin T. Gibbs data_ptrs[0] = (u_int8_t **)&ccb->cdm.matches; 8008b8a9b1dSJustin T. Gibbs lengths[0] = ccb->cdm.match_buf_len; 8018b8a9b1dSJustin T. Gibbs dirs[0] = CAM_DIR_IN; 8028b8a9b1dSJustin T. Gibbs numbufs = 1; 8038b8a9b1dSJustin T. Gibbs } 80452c9ce25SScott Long /* 80552c9ce25SScott Long * This request will not go to the hardware, no reason 80652c9ce25SScott Long * to be so strict. vmapbuf() is able to map up to MAXPHYS. 80752c9ce25SScott Long */ 80852c9ce25SScott Long maxmap = MAXPHYS; 8098b8a9b1dSJustin T. Gibbs break; 8108b8a9b1dSJustin T. Gibbs case XPT_SCSI_IO: 81187cfaf0eSJustin T. Gibbs case XPT_CONT_TARGET_IO: 8128b8a9b1dSJustin T. Gibbs if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_NONE) 8138b8a9b1dSJustin T. Gibbs return(0); 81495fbded6SScott Long if ((ccb->ccb_h.flags & CAM_DATA_MASK) != CAM_DATA_VADDR) 81595fbded6SScott Long return (EINVAL); 8168b8a9b1dSJustin T. Gibbs data_ptrs[0] = &ccb->csio.data_ptr; 81779d49a06SKenneth D. Merry lengths[0] = ccb->csio.dxfer_len; 8188b8a9b1dSJustin T. Gibbs dirs[0] = ccb->ccb_h.flags & CAM_DIR_MASK; 8198b8a9b1dSJustin T. Gibbs numbufs = 1; 8208b8a9b1dSJustin T. Gibbs break; 82152c9ce25SScott Long case XPT_ATA_IO: 82252c9ce25SScott Long if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_NONE) 82352c9ce25SScott Long return(0); 82495fbded6SScott Long if ((ccb->ccb_h.flags & CAM_DATA_MASK) != CAM_DATA_VADDR) 82595fbded6SScott Long return (EINVAL); 82652c9ce25SScott Long data_ptrs[0] = &ccb->ataio.data_ptr; 82752c9ce25SScott Long lengths[0] = ccb->ataio.dxfer_len; 82852c9ce25SScott Long dirs[0] = ccb->ccb_h.flags & CAM_DIR_MASK; 82952c9ce25SScott Long numbufs = 1; 83052c9ce25SScott Long break; 831a94a63f0SWarner Losh case XPT_MMC_IO: 832a94a63f0SWarner Losh if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_NONE) 833a94a63f0SWarner Losh return(0); 834a94a63f0SWarner Losh /* Two mappings: one for cmd->data and one for cmd->data->data */ 835a94a63f0SWarner Losh data_ptrs[0] = (unsigned char **)&ccb->mmcio.cmd.data; 836a94a63f0SWarner Losh lengths[0] = sizeof(struct mmc_data *); 837a94a63f0SWarner Losh dirs[0] = ccb->ccb_h.flags & CAM_DIR_MASK; 838a94a63f0SWarner Losh data_ptrs[1] = (unsigned char **)&ccb->mmcio.cmd.data->data; 839a94a63f0SWarner Losh lengths[1] = ccb->mmcio.cmd.data->len; 840a94a63f0SWarner Losh dirs[1] = ccb->ccb_h.flags & CAM_DIR_MASK; 841a94a63f0SWarner Losh numbufs = 2; 842a94a63f0SWarner Losh break; 84306e79492SKenneth D. Merry case XPT_SMP_IO: 84406e79492SKenneth D. Merry data_ptrs[0] = &ccb->smpio.smp_request; 84506e79492SKenneth D. Merry lengths[0] = ccb->smpio.smp_request_len; 84606e79492SKenneth D. Merry dirs[0] = CAM_DIR_OUT; 84706e79492SKenneth D. Merry data_ptrs[1] = &ccb->smpio.smp_response; 84806e79492SKenneth D. Merry lengths[1] = ccb->smpio.smp_response_len; 84906e79492SKenneth D. Merry dirs[1] = CAM_DIR_IN; 85006e79492SKenneth D. Merry numbufs = 2; 85106e79492SKenneth D. Merry break; 852df424515SWarner Losh case XPT_NVME_IO: 853df424515SWarner Losh case XPT_NVME_ADMIN: 854df424515SWarner Losh if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_NONE) 855df424515SWarner Losh return (0); 856df424515SWarner Losh if ((ccb->ccb_h.flags & CAM_DATA_MASK) != CAM_DATA_VADDR) 857df424515SWarner Losh return (EINVAL); 858df424515SWarner Losh data_ptrs[0] = &ccb->nvmeio.data_ptr; 859df424515SWarner Losh lengths[0] = ccb->nvmeio.dxfer_len; 860df424515SWarner Losh dirs[0] = ccb->ccb_h.flags & CAM_DIR_MASK; 861df424515SWarner Losh numbufs = 1; 862df424515SWarner Losh break; 8633501942bSJustin T. Gibbs case XPT_DEV_ADVINFO: 8643501942bSJustin T. Gibbs if (ccb->cdai.bufsiz == 0) 8657c103ddeSKenneth D. Merry return (0); 8667c103ddeSKenneth D. Merry 8673501942bSJustin T. Gibbs data_ptrs[0] = (uint8_t **)&ccb->cdai.buf; 8683501942bSJustin T. Gibbs lengths[0] = ccb->cdai.bufsiz; 86906e79492SKenneth D. Merry dirs[0] = CAM_DIR_IN; 87006e79492SKenneth D. Merry numbufs = 1; 8717c103ddeSKenneth D. Merry 8727c103ddeSKenneth D. Merry /* 8737c103ddeSKenneth D. Merry * This request will not go to the hardware, no reason 8747c103ddeSKenneth D. Merry * to be so strict. vmapbuf() is able to map up to MAXPHYS. 8757c103ddeSKenneth D. Merry */ 8767c103ddeSKenneth D. Merry maxmap = MAXPHYS; 87706e79492SKenneth D. Merry break; 8788b8a9b1dSJustin T. Gibbs default: 8798b8a9b1dSJustin T. Gibbs return(EINVAL); 8808b8a9b1dSJustin T. Gibbs break; /* NOTREACHED */ 8818b8a9b1dSJustin T. Gibbs } 8828b8a9b1dSJustin T. Gibbs 8838b8a9b1dSJustin T. Gibbs /* 88479d49a06SKenneth D. Merry * Check the transfer length and permissions first, so we don't 88579d49a06SKenneth D. Merry * have to unmap any previously mapped buffers. 8868b8a9b1dSJustin T. Gibbs */ 8878b8a9b1dSJustin T. Gibbs for (i = 0; i < numbufs; i++) { 88879d49a06SKenneth D. Merry 88979d49a06SKenneth D. Merry flags[i] = 0; 89079d49a06SKenneth D. Merry 89179d49a06SKenneth D. Merry /* 89279d49a06SKenneth D. Merry * The userland data pointer passed in may not be page 89379d49a06SKenneth D. Merry * aligned. vmapbuf() truncates the address to a page 89479d49a06SKenneth D. Merry * boundary, so if the address isn't page aligned, we'll 89579d49a06SKenneth D. Merry * need enough space for the given transfer length, plus 89679d49a06SKenneth D. Merry * whatever extra space is necessary to make it to the page 89779d49a06SKenneth D. Merry * boundary. 89879d49a06SKenneth D. Merry */ 89979d49a06SKenneth D. Merry if ((lengths[i] + 90052c9ce25SScott Long (((vm_offset_t)(*data_ptrs[i])) & PAGE_MASK)) > maxmap){ 9016d7b539aSPeter Wemm printf("cam_periph_mapmem: attempt to map %lu bytes, " 90252c9ce25SScott Long "which is greater than %lu\n", 9036d7b539aSPeter Wemm (long)(lengths[i] + 9046d7b539aSPeter Wemm (((vm_offset_t)(*data_ptrs[i])) & PAGE_MASK)), 90552c9ce25SScott Long (u_long)maxmap); 90679d49a06SKenneth D. Merry return(E2BIG); 90779d49a06SKenneth D. Merry } 9088b8a9b1dSJustin T. Gibbs 909edd24ab7SKenneth D. Merry if (dirs[i] & CAM_DIR_OUT) { 91021144e3bSPoul-Henning Kamp flags[i] = BIO_WRITE; 9118b8a9b1dSJustin T. Gibbs } 9128b8a9b1dSJustin T. Gibbs 913edd24ab7SKenneth D. Merry if (dirs[i] & CAM_DIR_IN) { 91421144e3bSPoul-Henning Kamp flags[i] = BIO_READ; 9158b8a9b1dSJustin T. Gibbs } 9168b8a9b1dSJustin T. Gibbs 91779d49a06SKenneth D. Merry } 91879d49a06SKenneth D. Merry 91979d49a06SKenneth D. Merry /* 920cef367e6SEitan Adler * This keeps the kernel stack of current thread from getting 92195fbded6SScott Long * swapped. In low-memory situations where the kernel stack might 92295fbded6SScott Long * otherwise get swapped out, this holds it and allows the thread 92395fbded6SScott Long * to make progress and release the kernel mapped pages sooner. 92495fbded6SScott Long * 92579d49a06SKenneth D. Merry * XXX KDM should I use P_NOSWAP instead? 92679d49a06SKenneth D. Merry */ 9270cbbb7bfSPeter Wemm PHOLD(curproc); 92879d49a06SKenneth D. Merry 92979d49a06SKenneth D. Merry for (i = 0; i < numbufs; i++) { 9308b8a9b1dSJustin T. Gibbs /* 9318b8a9b1dSJustin T. Gibbs * Get the buffer. 9328b8a9b1dSJustin T. Gibbs */ 9331c7c3c6aSMatthew Dillon mapinfo->bp[i] = getpbuf(NULL); 9348b8a9b1dSJustin T. Gibbs 9358b8a9b1dSJustin T. Gibbs /* put our pointer in the data slot */ 9368b8a9b1dSJustin T. Gibbs mapinfo->bp[i]->b_data = *data_ptrs[i]; 9378b8a9b1dSJustin T. Gibbs 938fade8dd7SJeff Roberson /* save the user's data address */ 939fade8dd7SJeff Roberson mapinfo->bp[i]->b_caller1 = *data_ptrs[i]; 940fade8dd7SJeff Roberson 94152c9ce25SScott Long /* set the transfer length, we know it's < MAXPHYS */ 9428b8a9b1dSJustin T. Gibbs mapinfo->bp[i]->b_bufsize = lengths[i]; 9438b8a9b1dSJustin T. Gibbs 94421144e3bSPoul-Henning Kamp /* set the direction */ 94521144e3bSPoul-Henning Kamp mapinfo->bp[i]->b_iocmd = flags[i]; 9468b8a9b1dSJustin T. Gibbs 9472d5c7e45SMatthew Dillon /* 9482d5c7e45SMatthew Dillon * Map the buffer into kernel memory. 9492d5c7e45SMatthew Dillon * 9502d5c7e45SMatthew Dillon * Note that useracc() alone is not a sufficient test. 9512d5c7e45SMatthew Dillon * vmapbuf() can still fail due to a smaller file mapped 9522d5c7e45SMatthew Dillon * into a larger area of VM, or if userland races against 9532d5c7e45SMatthew Dillon * vmapbuf() after the useracc() check. 9542d5c7e45SMatthew Dillon */ 955e81ff91eSKonstantin Belousov if (vmapbuf(mapinfo->bp[i], 1) < 0) { 9562d5c7e45SMatthew Dillon for (j = 0; j < i; ++j) { 957fade8dd7SJeff Roberson *data_ptrs[j] = mapinfo->bp[j]->b_caller1; 9588373f36dSAlan Cox vunmapbuf(mapinfo->bp[j]); 9592d5c7e45SMatthew Dillon relpbuf(mapinfo->bp[j], NULL); 9602d5c7e45SMatthew Dillon } 9618373f36dSAlan Cox relpbuf(mapinfo->bp[i], NULL); 9622d5c7e45SMatthew Dillon PRELE(curproc); 9632d5c7e45SMatthew Dillon return(EACCES); 9642d5c7e45SMatthew Dillon } 9658b8a9b1dSJustin T. Gibbs 9668b8a9b1dSJustin T. Gibbs /* set our pointer to the new mapped area */ 9678b8a9b1dSJustin T. Gibbs *data_ptrs[i] = mapinfo->bp[i]->b_data; 9688b8a9b1dSJustin T. Gibbs 9698b8a9b1dSJustin T. Gibbs mapinfo->num_bufs_used++; 9708b8a9b1dSJustin T. Gibbs } 9718b8a9b1dSJustin T. Gibbs 972a62525f3SMatt Jacob /* 973a62525f3SMatt Jacob * Now that we've gotten this far, change ownership to the kernel 974a62525f3SMatt Jacob * of the buffers so that we don't run afoul of returning to user 975a62525f3SMatt Jacob * space with locks (on the buffer) held. 976a62525f3SMatt Jacob */ 977a62525f3SMatt Jacob for (i = 0; i < numbufs; i++) { 978a62525f3SMatt Jacob BUF_KERNPROC(mapinfo->bp[i]); 979a62525f3SMatt Jacob } 980a62525f3SMatt Jacob 981a62525f3SMatt Jacob 9828b8a9b1dSJustin T. Gibbs return(0); 9838b8a9b1dSJustin T. Gibbs } 9848b8a9b1dSJustin T. Gibbs 9858b8a9b1dSJustin T. Gibbs /* 9868b8a9b1dSJustin T. Gibbs * Unmap memory segments mapped into kernel virtual address space by 9878b8a9b1dSJustin T. Gibbs * cam_periph_mapmem(). 9888b8a9b1dSJustin T. Gibbs */ 9898b8a9b1dSJustin T. Gibbs void 9908b8a9b1dSJustin T. Gibbs cam_periph_unmapmem(union ccb *ccb, struct cam_periph_map_info *mapinfo) 9918b8a9b1dSJustin T. Gibbs { 9928b8a9b1dSJustin T. Gibbs int numbufs, i; 9938b8a9b1dSJustin T. Gibbs u_int8_t **data_ptrs[CAM_PERIPH_MAXMAPS]; 9948b8a9b1dSJustin T. Gibbs 9958b8a9b1dSJustin T. Gibbs if (mapinfo->num_bufs_used <= 0) { 99695fbded6SScott Long /* nothing to free and the process wasn't held. */ 9978b8a9b1dSJustin T. Gibbs return; 9988b8a9b1dSJustin T. Gibbs } 9998b8a9b1dSJustin T. Gibbs 10008b8a9b1dSJustin T. Gibbs switch (ccb->ccb_h.func_code) { 10018b8a9b1dSJustin T. Gibbs case XPT_DEV_MATCH: 10028b8a9b1dSJustin T. Gibbs numbufs = min(mapinfo->num_bufs_used, 2); 10038b8a9b1dSJustin T. Gibbs 10048b8a9b1dSJustin T. Gibbs if (numbufs == 1) { 10058b8a9b1dSJustin T. Gibbs data_ptrs[0] = (u_int8_t **)&ccb->cdm.matches; 10068b8a9b1dSJustin T. Gibbs } else { 10078b8a9b1dSJustin T. Gibbs data_ptrs[0] = (u_int8_t **)&ccb->cdm.patterns; 10088b8a9b1dSJustin T. Gibbs data_ptrs[1] = (u_int8_t **)&ccb->cdm.matches; 10098b8a9b1dSJustin T. Gibbs } 10108b8a9b1dSJustin T. Gibbs break; 10118b8a9b1dSJustin T. Gibbs case XPT_SCSI_IO: 10129911ecf9SJustin T. Gibbs case XPT_CONT_TARGET_IO: 10138b8a9b1dSJustin T. Gibbs data_ptrs[0] = &ccb->csio.data_ptr; 10148b8a9b1dSJustin T. Gibbs numbufs = min(mapinfo->num_bufs_used, 1); 10158b8a9b1dSJustin T. Gibbs break; 101652c9ce25SScott Long case XPT_ATA_IO: 101752c9ce25SScott Long data_ptrs[0] = &ccb->ataio.data_ptr; 101852c9ce25SScott Long numbufs = min(mapinfo->num_bufs_used, 1); 101952c9ce25SScott Long break; 102006e79492SKenneth D. Merry case XPT_SMP_IO: 102106e79492SKenneth D. Merry numbufs = min(mapinfo->num_bufs_used, 2); 102206e79492SKenneth D. Merry data_ptrs[0] = &ccb->smpio.smp_request; 102306e79492SKenneth D. Merry data_ptrs[1] = &ccb->smpio.smp_response; 102406e79492SKenneth D. Merry break; 10253501942bSJustin T. Gibbs case XPT_DEV_ADVINFO: 102606e79492SKenneth D. Merry numbufs = min(mapinfo->num_bufs_used, 1); 10273501942bSJustin T. Gibbs data_ptrs[0] = (uint8_t **)&ccb->cdai.buf; 102806e79492SKenneth D. Merry break; 1029df424515SWarner Losh case XPT_NVME_IO: 1030df424515SWarner Losh case XPT_NVME_ADMIN: 1031df424515SWarner Losh data_ptrs[0] = &ccb->nvmeio.data_ptr; 1032df424515SWarner Losh numbufs = min(mapinfo->num_bufs_used, 1); 1033df424515SWarner Losh break; 10348b8a9b1dSJustin T. Gibbs default: 10358b8a9b1dSJustin T. Gibbs /* allow ourselves to be swapped once again */ 10360cbbb7bfSPeter Wemm PRELE(curproc); 10378b8a9b1dSJustin T. Gibbs return; 10388b8a9b1dSJustin T. Gibbs break; /* NOTREACHED */ 10398b8a9b1dSJustin T. Gibbs } 10408b8a9b1dSJustin T. Gibbs 10418b8a9b1dSJustin T. Gibbs for (i = 0; i < numbufs; i++) { 10428b8a9b1dSJustin T. Gibbs /* Set the user's pointer back to the original value */ 1043fade8dd7SJeff Roberson *data_ptrs[i] = mapinfo->bp[i]->b_caller1; 10448b8a9b1dSJustin T. Gibbs 10458b8a9b1dSJustin T. Gibbs /* unmap the buffer */ 10468b8a9b1dSJustin T. Gibbs vunmapbuf(mapinfo->bp[i]); 10478b8a9b1dSJustin T. Gibbs 10488b8a9b1dSJustin T. Gibbs /* release the buffer */ 10491c7c3c6aSMatthew Dillon relpbuf(mapinfo->bp[i], NULL); 10508b8a9b1dSJustin T. Gibbs } 10518b8a9b1dSJustin T. Gibbs 10528b8a9b1dSJustin T. Gibbs /* allow ourselves to be swapped once again */ 10530cbbb7bfSPeter Wemm PRELE(curproc); 10548b8a9b1dSJustin T. Gibbs } 10558b8a9b1dSJustin T. Gibbs 10568b8a9b1dSJustin T. Gibbs int 1057571e8e20SScott Long cam_periph_ioctl(struct cam_periph *periph, u_long cmd, caddr_t addr, 10588b8a9b1dSJustin T. Gibbs int (*error_routine)(union ccb *ccb, 10598b8a9b1dSJustin T. Gibbs cam_flags camflags, 10608b8a9b1dSJustin T. Gibbs u_int32_t sense_flags)) 10618b8a9b1dSJustin T. Gibbs { 10628b8a9b1dSJustin T. Gibbs union ccb *ccb; 10638b8a9b1dSJustin T. Gibbs int error; 10648b8a9b1dSJustin T. Gibbs int found; 10658b8a9b1dSJustin T. Gibbs 10668b8a9b1dSJustin T. Gibbs error = found = 0; 10678b8a9b1dSJustin T. Gibbs 10688b8a9b1dSJustin T. Gibbs switch(cmd){ 10698b8a9b1dSJustin T. Gibbs case CAMGETPASSTHRU: 1070bbfa4aa1SAlexander Motin ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL); 10718b8a9b1dSJustin T. Gibbs xpt_setup_ccb(&ccb->ccb_h, 10728b8a9b1dSJustin T. Gibbs ccb->ccb_h.path, 1073bbfa4aa1SAlexander Motin CAM_PRIORITY_NORMAL); 10748b8a9b1dSJustin T. Gibbs ccb->ccb_h.func_code = XPT_GDEVLIST; 10758b8a9b1dSJustin T. Gibbs 10768b8a9b1dSJustin T. Gibbs /* 10778b8a9b1dSJustin T. Gibbs * Basically, the point of this is that we go through 10788b8a9b1dSJustin T. Gibbs * getting the list of devices, until we find a passthrough 10798b8a9b1dSJustin T. Gibbs * device. In the current version of the CAM code, the 10808b8a9b1dSJustin T. Gibbs * only way to determine what type of device we're dealing 10818b8a9b1dSJustin T. Gibbs * with is by its name. 10828b8a9b1dSJustin T. Gibbs */ 10838b8a9b1dSJustin T. Gibbs while (found == 0) { 10848b8a9b1dSJustin T. Gibbs ccb->cgdl.index = 0; 10858b8a9b1dSJustin T. Gibbs ccb->cgdl.status = CAM_GDEVLIST_MORE_DEVS; 10868b8a9b1dSJustin T. Gibbs while (ccb->cgdl.status == CAM_GDEVLIST_MORE_DEVS) { 10878b8a9b1dSJustin T. Gibbs 10888b8a9b1dSJustin T. Gibbs /* we want the next device in the list */ 10898b8a9b1dSJustin T. Gibbs xpt_action(ccb); 10908b8a9b1dSJustin T. Gibbs if (strncmp(ccb->cgdl.periph_name, 10918b8a9b1dSJustin T. Gibbs "pass", 4) == 0){ 10928b8a9b1dSJustin T. Gibbs found = 1; 10938b8a9b1dSJustin T. Gibbs break; 10948b8a9b1dSJustin T. Gibbs } 10958b8a9b1dSJustin T. Gibbs } 10968b8a9b1dSJustin T. Gibbs if ((ccb->cgdl.status == CAM_GDEVLIST_LAST_DEVICE) && 10978b8a9b1dSJustin T. Gibbs (found == 0)) { 10988b8a9b1dSJustin T. Gibbs ccb->cgdl.periph_name[0] = '\0'; 10998b8a9b1dSJustin T. Gibbs ccb->cgdl.unit_number = 0; 11008b8a9b1dSJustin T. Gibbs break; 11018b8a9b1dSJustin T. Gibbs } 11028b8a9b1dSJustin T. Gibbs } 11038b8a9b1dSJustin T. Gibbs 11048b8a9b1dSJustin T. Gibbs /* copy the result back out */ 11058b8a9b1dSJustin T. Gibbs bcopy(ccb, addr, sizeof(union ccb)); 11068b8a9b1dSJustin T. Gibbs 11078b8a9b1dSJustin T. Gibbs /* and release the ccb */ 11088b8a9b1dSJustin T. Gibbs xpt_release_ccb(ccb); 11098b8a9b1dSJustin T. Gibbs 11108b8a9b1dSJustin T. Gibbs break; 11118b8a9b1dSJustin T. Gibbs default: 11128b8a9b1dSJustin T. Gibbs error = ENOTTY; 11138b8a9b1dSJustin T. Gibbs break; 11148b8a9b1dSJustin T. Gibbs } 11158b8a9b1dSJustin T. Gibbs return(error); 11168b8a9b1dSJustin T. Gibbs } 11178b8a9b1dSJustin T. Gibbs 1118227d67aaSAlexander Motin static void 1119991b5d26SMark Johnston cam_periph_done_panic(struct cam_periph *periph, union ccb *done_ccb) 1120991b5d26SMark Johnston { 1121991b5d26SMark Johnston 1122991b5d26SMark Johnston panic("%s: already done with ccb %p", __func__, done_ccb); 1123991b5d26SMark Johnston } 1124991b5d26SMark Johnston 1125991b5d26SMark Johnston static void 1126227d67aaSAlexander Motin cam_periph_done(struct cam_periph *periph, union ccb *done_ccb) 1127227d67aaSAlexander Motin { 1128227d67aaSAlexander Motin 1129227d67aaSAlexander Motin /* Caller will release the CCB */ 1130991b5d26SMark Johnston xpt_path_assert(done_ccb->ccb_h.path, MA_OWNED); 1131991b5d26SMark Johnston done_ccb->ccb_h.cbfcnp = cam_periph_done_panic; 1132227d67aaSAlexander Motin wakeup(&done_ccb->ccb_h.cbfcnp); 1133227d67aaSAlexander Motin } 1134227d67aaSAlexander Motin 1135991b5d26SMark Johnston static void 1136991b5d26SMark Johnston cam_periph_ccbwait(union ccb *ccb) 1137991b5d26SMark Johnston { 1138991b5d26SMark Johnston 1139991b5d26SMark Johnston if ((ccb->ccb_h.func_code & XPT_FC_QUEUED) != 0) { 1140991b5d26SMark Johnston while (ccb->ccb_h.cbfcnp != cam_periph_done_panic) 1141991b5d26SMark Johnston xpt_path_sleep(ccb->ccb_h.path, &ccb->ccb_h.cbfcnp, 1142991b5d26SMark Johnston PRIBIO, "cbwait", 0); 1143991b5d26SMark Johnston } 1144991b5d26SMark Johnston KASSERT(ccb->ccb_h.pinfo.index == CAM_UNQUEUED_INDEX && 1145991b5d26SMark Johnston (ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_INPROG, 1146991b5d26SMark Johnston ("%s: proceeding with incomplete ccb: ccb=%p, func_code=%#x, " 1147991b5d26SMark Johnston "status=%#x, index=%d", __func__, ccb, ccb->ccb_h.func_code, 1148991b5d26SMark Johnston ccb->ccb_h.status, ccb->ccb_h.pinfo.index)); 1149991b5d26SMark Johnston } 1150991b5d26SMark Johnston 11518b8a9b1dSJustin T. Gibbs int 11528b8a9b1dSJustin T. Gibbs cam_periph_runccb(union ccb *ccb, 11538b8a9b1dSJustin T. Gibbs int (*error_routine)(union ccb *ccb, 11548b8a9b1dSJustin T. Gibbs cam_flags camflags, 11558b8a9b1dSJustin T. Gibbs u_int32_t sense_flags), 11568b8a9b1dSJustin T. Gibbs cam_flags camflags, u_int32_t sense_flags, 11578b8a9b1dSJustin T. Gibbs struct devstat *ds) 11588b8a9b1dSJustin T. Gibbs { 1159fd8be18fSKenneth D. Merry struct bintime *starttime; 1160fd8be18fSKenneth D. Merry struct bintime ltime; 11618b8a9b1dSJustin T. Gibbs int error; 11625cf3cd10SWarner Losh bool must_poll; 1163f93a843cSWarner Losh uint32_t timeout = 1; 11648b8a9b1dSJustin T. Gibbs 1165fd8be18fSKenneth D. Merry starttime = NULL; 1166227d67aaSAlexander Motin xpt_path_assert(ccb->ccb_h.path, MA_OWNED); 1167991b5d26SMark Johnston KASSERT((ccb->ccb_h.flags & CAM_UNLOCKED) == 0, 1168991b5d26SMark Johnston ("%s: ccb=%p, func_code=%#x, flags=%#x", __func__, ccb, 1169991b5d26SMark Johnston ccb->ccb_h.func_code, ccb->ccb_h.flags)); 11708b8a9b1dSJustin T. Gibbs 11718b8a9b1dSJustin T. Gibbs /* 11728b8a9b1dSJustin T. Gibbs * If the user has supplied a stats structure, and if we understand 11738b8a9b1dSJustin T. Gibbs * this particular type of ccb, record the transaction start. 11748b8a9b1dSJustin T. Gibbs */ 1175d2f3208dSWarner Losh if (ds != NULL && 1176d2f3208dSWarner Losh (ccb->ccb_h.func_code == XPT_SCSI_IO || 1177d2f3208dSWarner Losh ccb->ccb_h.func_code == XPT_ATA_IO || 1178d2f3208dSWarner Losh ccb->ccb_h.func_code == XPT_NVME_IO)) { 1179fd8be18fSKenneth D. Merry starttime = <ime; 1180fd8be18fSKenneth D. Merry binuptime(starttime); 1181fd8be18fSKenneth D. Merry devstat_start_transaction(ds, starttime); 1182fd8be18fSKenneth D. Merry } 11838b8a9b1dSJustin T. Gibbs 11845cf3cd10SWarner Losh /* 11855cf3cd10SWarner Losh * We must poll the I/O while we're dumping. The scheduler is normally 11865cf3cd10SWarner Losh * stopped for dumping, except when we call doadump from ddb. While the 11875cf3cd10SWarner Losh * scheduler is running in this case, we still need to poll the I/O to 11885cf3cd10SWarner Losh * avoid sleeping waiting for the ccb to complete. 118904e814aeSScott Long * 1190045f8bc8SWarner Losh * A panic triggered dump stops the scheduler, any callback from the 1191045f8bc8SWarner Losh * shutdown_post_sync event will run with the scheduler stopped, but 1192045f8bc8SWarner Losh * before we're officially dumping. To avoid hanging in adashutdown 1193045f8bc8SWarner Losh * initiated commands (or other similar situations), we have to test for 1194045f8bc8SWarner Losh * either SCHEDULER_STOPPED() here as well. 1195045f8bc8SWarner Losh * 1196045f8bc8SWarner Losh * To avoid locking problems, dumping/polling callers must call 119704e814aeSScott Long * without a periph lock held. 11985cf3cd10SWarner Losh */ 1199045f8bc8SWarner Losh must_poll = dumping || SCHEDULER_STOPPED(); 1200227d67aaSAlexander Motin ccb->ccb_h.cbfcnp = cam_periph_done; 12018b8a9b1dSJustin T. Gibbs 1202f93a843cSWarner Losh /* 1203f93a843cSWarner Losh * If we're polling, then we need to ensure that we have ample resources 120404e814aeSScott Long * in the periph. 1205f93a843cSWarner Losh * cam_periph_error can reschedule the ccb by calling xpt_action and returning 1206f93a843cSWarner Losh * ERESTART, so we have to effect the polling in the do loop below. 1207f93a843cSWarner Losh */ 12085cf3cd10SWarner Losh if (must_poll) { 1209f93a843cSWarner Losh timeout = xpt_poll_setup(ccb); 1210f93a843cSWarner Losh } 1211f93a843cSWarner Losh 1212f93a843cSWarner Losh if (timeout == 0) { 1213f93a843cSWarner Losh ccb->ccb_h.status = CAM_RESRC_UNAVAIL; 1214f93a843cSWarner Losh error = EBUSY; 1215f93a843cSWarner Losh } else { 1216f93a843cSWarner Losh xpt_action(ccb); 12178b8a9b1dSJustin T. Gibbs do { 12185cf3cd10SWarner Losh if (must_poll) { 1219f93a843cSWarner Losh xpt_pollwait(ccb, timeout); 1220f93a843cSWarner Losh timeout = ccb->ccb_h.timeout * 10; 12215cf3cd10SWarner Losh } else { 12225cf3cd10SWarner Losh cam_periph_ccbwait(ccb); 1223f93a843cSWarner Losh } 12248b8a9b1dSJustin T. Gibbs if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) 12258b8a9b1dSJustin T. Gibbs error = 0; 1226991b5d26SMark Johnston else if (error_routine != NULL) { 1227991b5d26SMark Johnston ccb->ccb_h.cbfcnp = cam_periph_done; 12288b8a9b1dSJustin T. Gibbs error = (*error_routine)(ccb, camflags, sense_flags); 1229991b5d26SMark Johnston } else 12308b8a9b1dSJustin T. Gibbs error = 0; 12318b8a9b1dSJustin T. Gibbs } while (error == ERESTART); 1232f93a843cSWarner Losh } 1233f93a843cSWarner Losh 123483c5d981SAlexander Motin if ((ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) { 12358b8a9b1dSJustin T. Gibbs cam_release_devq(ccb->ccb_h.path, 12368b8a9b1dSJustin T. Gibbs /* relsim_flags */0, 12378b8a9b1dSJustin T. Gibbs /* openings */0, 12388b8a9b1dSJustin T. Gibbs /* timeout */0, 12398b8a9b1dSJustin T. Gibbs /* getcount_only */ FALSE); 124083c5d981SAlexander Motin ccb->ccb_h.status &= ~CAM_DEV_QFRZN; 124183c5d981SAlexander Motin } 12428b8a9b1dSJustin T. Gibbs 1243379ad358SAlexander Motin if (ds != NULL) { 1244d2f3208dSWarner Losh uint32_t bytes; 1245d2f3208dSWarner Losh devstat_tag_type tag; 1246d2f3208dSWarner Losh bool valid = true; 1247d2f3208dSWarner Losh 1248379ad358SAlexander Motin if (ccb->ccb_h.func_code == XPT_SCSI_IO) { 1249d2f3208dSWarner Losh bytes = ccb->csio.dxfer_len - ccb->csio.resid; 1250d2f3208dSWarner Losh tag = (devstat_tag_type)(ccb->csio.tag_action & 0x3); 1251379ad358SAlexander Motin } else if (ccb->ccb_h.func_code == XPT_ATA_IO) { 1252d2f3208dSWarner Losh bytes = ccb->ataio.dxfer_len - ccb->ataio.resid; 1253d2f3208dSWarner Losh tag = (devstat_tag_type)0; 1254d2f3208dSWarner Losh } else if (ccb->ccb_h.func_code == XPT_NVME_IO) { 1255d2f3208dSWarner Losh bytes = ccb->nvmeio.dxfer_len; /* NB: resid no possible */ 1256d2f3208dSWarner Losh tag = (devstat_tag_type)0; 1257d2f3208dSWarner Losh } else { 1258d2f3208dSWarner Losh valid = false; 1259379ad358SAlexander Motin } 1260d2f3208dSWarner Losh if (valid) 1261d2f3208dSWarner Losh devstat_end_transaction(ds, bytes, tag, 1262d2f3208dSWarner Losh ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_NONE) ? 1263d2f3208dSWarner Losh DEVSTAT_NO_DATA : (ccb->ccb_h.flags & CAM_DIR_OUT) ? 1264d2f3208dSWarner Losh DEVSTAT_WRITE : DEVSTAT_READ, NULL, starttime); 1265379ad358SAlexander Motin } 12668b8a9b1dSJustin T. Gibbs 12678b8a9b1dSJustin T. Gibbs return(error); 12688b8a9b1dSJustin T. Gibbs } 12698b8a9b1dSJustin T. Gibbs 127087cfaf0eSJustin T. Gibbs void 127187cfaf0eSJustin T. Gibbs cam_freeze_devq(struct cam_path *path) 127287cfaf0eSJustin T. Gibbs { 1273cccf4220SAlexander Motin struct ccb_hdr ccb_h; 127487cfaf0eSJustin T. Gibbs 12750d4f3c31SAlexander Motin CAM_DEBUG(path, CAM_DEBUG_TRACE, ("cam_freeze_devq\n")); 1276cccf4220SAlexander Motin xpt_setup_ccb(&ccb_h, path, /*priority*/1); 1277cccf4220SAlexander Motin ccb_h.func_code = XPT_NOOP; 1278cccf4220SAlexander Motin ccb_h.flags = CAM_DEV_QFREEZE; 1279cccf4220SAlexander Motin xpt_action((union ccb *)&ccb_h); 128087cfaf0eSJustin T. Gibbs } 128187cfaf0eSJustin T. Gibbs 12828b8a9b1dSJustin T. Gibbs u_int32_t 12838b8a9b1dSJustin T. Gibbs cam_release_devq(struct cam_path *path, u_int32_t relsim_flags, 128483c5d981SAlexander Motin u_int32_t openings, u_int32_t arg, 12858b8a9b1dSJustin T. Gibbs int getcount_only) 12868b8a9b1dSJustin T. Gibbs { 12878b8a9b1dSJustin T. Gibbs struct ccb_relsim crs; 12888b8a9b1dSJustin T. Gibbs 12890d4f3c31SAlexander Motin CAM_DEBUG(path, CAM_DEBUG_TRACE, ("cam_release_devq(%u, %u, %u, %d)\n", 12900d4f3c31SAlexander Motin relsim_flags, openings, arg, getcount_only)); 1291bbfa4aa1SAlexander Motin xpt_setup_ccb(&crs.ccb_h, path, CAM_PRIORITY_NORMAL); 12928b8a9b1dSJustin T. Gibbs crs.ccb_h.func_code = XPT_REL_SIMQ; 12938b8a9b1dSJustin T. Gibbs crs.ccb_h.flags = getcount_only ? CAM_DEV_QFREEZE : 0; 12948b8a9b1dSJustin T. Gibbs crs.release_flags = relsim_flags; 12958b8a9b1dSJustin T. Gibbs crs.openings = openings; 129683c5d981SAlexander Motin crs.release_timeout = arg; 12978b8a9b1dSJustin T. Gibbs xpt_action((union ccb *)&crs); 12988b8a9b1dSJustin T. Gibbs return (crs.qfrozen_cnt); 12998b8a9b1dSJustin T. Gibbs } 13008b8a9b1dSJustin T. Gibbs 13018b8a9b1dSJustin T. Gibbs #define saved_ccb_ptr ppriv_ptr0 130283c5d981SAlexander Motin static void 130383c5d981SAlexander Motin camperiphdone(struct cam_periph *periph, union ccb *done_ccb) 130483c5d981SAlexander Motin { 13050191d9b3SAlexander Motin union ccb *saved_ccb; 130683c5d981SAlexander Motin cam_status status; 130783c5d981SAlexander Motin struct scsi_start_stop_unit *scsi_cmd; 1308e7493b28SAlexander Motin int error_code, sense_key, asc, ascq; 130983c5d981SAlexander Motin 13100191d9b3SAlexander Motin scsi_cmd = (struct scsi_start_stop_unit *) 13110191d9b3SAlexander Motin &done_ccb->csio.cdb_io.cdb_bytes; 131283c5d981SAlexander Motin status = done_ccb->ccb_h.status; 13138b8a9b1dSJustin T. Gibbs 13140191d9b3SAlexander Motin if ((status & CAM_STATUS_MASK) != CAM_REQ_CMP) { 1315e7493b28SAlexander Motin if (scsi_extract_sense_ccb(done_ccb, 1316e7493b28SAlexander Motin &error_code, &sense_key, &asc, &ascq)) { 131765afe1f4SKenneth D. Merry /* 13188b8a9b1dSJustin T. Gibbs * If the error is "invalid field in CDB", 13198b8a9b1dSJustin T. Gibbs * and the load/eject flag is set, turn the 13208b8a9b1dSJustin T. Gibbs * flag off and try again. This is just in 13218b8a9b1dSJustin T. Gibbs * case the drive in question barfs on the 13228b8a9b1dSJustin T. Gibbs * load eject flag. The CAM code should set 13238b8a9b1dSJustin T. Gibbs * the load/eject flag by default for 13248b8a9b1dSJustin T. Gibbs * removable media. 13258b8a9b1dSJustin T. Gibbs */ 13268b8a9b1dSJustin T. Gibbs if ((scsi_cmd->opcode == START_STOP_UNIT) && 13278b8a9b1dSJustin T. Gibbs ((scsi_cmd->how & SSS_LOEJ) != 0) && 13280191d9b3SAlexander Motin (asc == 0x24) && (ascq == 0x00)) { 13298b8a9b1dSJustin T. Gibbs scsi_cmd->how &= ~SSS_LOEJ; 13300191d9b3SAlexander Motin if (status & CAM_DEV_QFRZN) { 13310191d9b3SAlexander Motin cam_release_devq(done_ccb->ccb_h.path, 13320191d9b3SAlexander Motin 0, 0, 0, 0); 13330191d9b3SAlexander Motin done_ccb->ccb_h.status &= 13340191d9b3SAlexander Motin ~CAM_DEV_QFRZN; 13350191d9b3SAlexander Motin } 13368b8a9b1dSJustin T. Gibbs xpt_action(done_ccb); 13370191d9b3SAlexander Motin goto out; 13380191d9b3SAlexander Motin } 13390191d9b3SAlexander Motin } 13400191d9b3SAlexander Motin if (cam_periph_error(done_ccb, 1341553484aeSWarner Losh 0, SF_RETRY_UA | SF_NO_PRINT) == ERESTART) 13420191d9b3SAlexander Motin goto out; 13430191d9b3SAlexander Motin if (done_ccb->ccb_h.status & CAM_DEV_QFRZN) { 13440191d9b3SAlexander Motin cam_release_devq(done_ccb->ccb_h.path, 0, 0, 0, 0); 13450191d9b3SAlexander Motin done_ccb->ccb_h.status &= ~CAM_DEV_QFRZN; 13460191d9b3SAlexander Motin } 13470191d9b3SAlexander Motin } else { 13480191d9b3SAlexander Motin /* 13490191d9b3SAlexander Motin * If we have successfully taken a device from the not 13500191d9b3SAlexander Motin * ready to ready state, re-scan the device and re-get 13510191d9b3SAlexander Motin * the inquiry information. Many devices (mostly disks) 13520191d9b3SAlexander Motin * don't properly report their inquiry information unless 13530191d9b3SAlexander Motin * they are spun up. 13540191d9b3SAlexander Motin */ 13550191d9b3SAlexander Motin if (scsi_cmd->opcode == START_STOP_UNIT) 13560191d9b3SAlexander Motin xpt_async(AC_INQ_CHANGED, done_ccb->ccb_h.path, NULL); 13570191d9b3SAlexander Motin } 135865afe1f4SKenneth D. Merry 13598b8a9b1dSJustin T. Gibbs /* 13600191d9b3SAlexander Motin * Perform the final retry with the original CCB so that final 13610191d9b3SAlexander Motin * error processing is performed by the owner of the CCB. 13628b8a9b1dSJustin T. Gibbs */ 13630191d9b3SAlexander Motin saved_ccb = (union ccb *)done_ccb->ccb_h.saved_ccb_ptr; 136483c5d981SAlexander Motin bcopy(saved_ccb, done_ccb, sizeof(*done_ccb)); 136583c5d981SAlexander Motin xpt_free_ccb(saved_ccb); 13660191d9b3SAlexander Motin if (done_ccb->ccb_h.cbfcnp != camperiphdone) 136760a899a0SKenneth D. Merry periph->flags &= ~CAM_PERIPH_RECOVERY_INPROG; 13688b8a9b1dSJustin T. Gibbs xpt_action(done_ccb); 13698b8a9b1dSJustin T. Gibbs 13700191d9b3SAlexander Motin out: 13710191d9b3SAlexander Motin /* Drop freeze taken due to CAM_DEV_QFREEZE flag set. */ 13720191d9b3SAlexander Motin cam_release_devq(done_ccb->ccb_h.path, 0, 0, 0, 0); 1373d84c90a6SAlexander Motin } 13748b8a9b1dSJustin T. Gibbs 13758b8a9b1dSJustin T. Gibbs /* 137687cfaf0eSJustin T. Gibbs * Generic Async Event handler. Peripheral drivers usually 137787cfaf0eSJustin T. Gibbs * filter out the events that require personal attention, 137887cfaf0eSJustin T. Gibbs * and leave the rest to this function. 137987cfaf0eSJustin T. Gibbs */ 138087cfaf0eSJustin T. Gibbs void 138187cfaf0eSJustin T. Gibbs cam_periph_async(struct cam_periph *periph, u_int32_t code, 138287cfaf0eSJustin T. Gibbs struct cam_path *path, void *arg) 138387cfaf0eSJustin T. Gibbs { 138487cfaf0eSJustin T. Gibbs switch (code) { 138587cfaf0eSJustin T. Gibbs case AC_LOST_DEVICE: 138687cfaf0eSJustin T. Gibbs cam_periph_invalidate(periph); 138787cfaf0eSJustin T. Gibbs break; 138887cfaf0eSJustin T. Gibbs default: 138987cfaf0eSJustin T. Gibbs break; 139087cfaf0eSJustin T. Gibbs } 139187cfaf0eSJustin T. Gibbs } 139287cfaf0eSJustin T. Gibbs 139387cfaf0eSJustin T. Gibbs void 139487cfaf0eSJustin T. Gibbs cam_periph_bus_settle(struct cam_periph *periph, u_int bus_settle) 139587cfaf0eSJustin T. Gibbs { 139687cfaf0eSJustin T. Gibbs struct ccb_getdevstats cgds; 139787cfaf0eSJustin T. Gibbs 1398bbfa4aa1SAlexander Motin xpt_setup_ccb(&cgds.ccb_h, periph->path, CAM_PRIORITY_NORMAL); 139987cfaf0eSJustin T. Gibbs cgds.ccb_h.func_code = XPT_GDEV_STATS; 140087cfaf0eSJustin T. Gibbs xpt_action((union ccb *)&cgds); 140187cfaf0eSJustin T. Gibbs cam_periph_freeze_after_event(periph, &cgds.last_reset, bus_settle); 140287cfaf0eSJustin T. Gibbs } 140387cfaf0eSJustin T. Gibbs 140487cfaf0eSJustin T. Gibbs void 140587cfaf0eSJustin T. Gibbs cam_periph_freeze_after_event(struct cam_periph *periph, 140687cfaf0eSJustin T. Gibbs struct timeval* event_time, u_int duration_ms) 140787cfaf0eSJustin T. Gibbs { 140887cfaf0eSJustin T. Gibbs struct timeval delta; 140987cfaf0eSJustin T. Gibbs struct timeval duration_tv; 141087cfaf0eSJustin T. Gibbs 1411f8ff57d2SAlexander Motin if (!timevalisset(event_time)) 1412f8ff57d2SAlexander Motin return; 1413f8ff57d2SAlexander Motin 141487cfaf0eSJustin T. Gibbs microtime(&delta); 141587cfaf0eSJustin T. Gibbs timevalsub(&delta, event_time); 141687cfaf0eSJustin T. Gibbs duration_tv.tv_sec = duration_ms / 1000; 141787cfaf0eSJustin T. Gibbs duration_tv.tv_usec = (duration_ms % 1000) * 1000; 141887cfaf0eSJustin T. Gibbs if (timevalcmp(&delta, &duration_tv, <)) { 141987cfaf0eSJustin T. Gibbs timevalsub(&duration_tv, &delta); 142087cfaf0eSJustin T. Gibbs 142187cfaf0eSJustin T. Gibbs duration_ms = duration_tv.tv_sec * 1000; 142287cfaf0eSJustin T. Gibbs duration_ms += duration_tv.tv_usec / 1000; 142387cfaf0eSJustin T. Gibbs cam_freeze_devq(periph->path); 142487cfaf0eSJustin T. Gibbs cam_release_devq(periph->path, 142587cfaf0eSJustin T. Gibbs RELSIM_RELEASE_AFTER_TIMEOUT, 142687cfaf0eSJustin T. Gibbs /*reduction*/0, 142787cfaf0eSJustin T. Gibbs /*timeout*/duration_ms, 142887cfaf0eSJustin T. Gibbs /*getcount_only*/0); 142987cfaf0eSJustin T. Gibbs } 143087cfaf0eSJustin T. Gibbs 143187cfaf0eSJustin T. Gibbs } 143287cfaf0eSJustin T. Gibbs 14333393f8daSKenneth D. Merry static int 14340191d9b3SAlexander Motin camperiphscsistatuserror(union ccb *ccb, union ccb **orig_ccb, 14350191d9b3SAlexander Motin cam_flags camflags, u_int32_t sense_flags, 14363393f8daSKenneth D. Merry int *openings, u_int32_t *relsim_flags, 14370181d54bSAlexander Motin u_int32_t *timeout, u_int32_t *action, const char **action_string) 14388b8a9b1dSJustin T. Gibbs { 14398b8a9b1dSJustin T. Gibbs int error; 14408b8a9b1dSJustin T. Gibbs 14418b8a9b1dSJustin T. Gibbs switch (ccb->csio.scsi_status) { 14428b8a9b1dSJustin T. Gibbs case SCSI_STATUS_OK: 14438b8a9b1dSJustin T. Gibbs case SCSI_STATUS_COND_MET: 14448b8a9b1dSJustin T. Gibbs case SCSI_STATUS_INTERMED: 14458b8a9b1dSJustin T. Gibbs case SCSI_STATUS_INTERMED_COND_MET: 14468b8a9b1dSJustin T. Gibbs error = 0; 14478b8a9b1dSJustin T. Gibbs break; 14488b8a9b1dSJustin T. Gibbs case SCSI_STATUS_CMD_TERMINATED: 14498b8a9b1dSJustin T. Gibbs case SCSI_STATUS_CHECK_COND: 14500191d9b3SAlexander Motin error = camperiphscsisenseerror(ccb, orig_ccb, 14513393f8daSKenneth D. Merry camflags, 14528b8a9b1dSJustin T. Gibbs sense_flags, 14533393f8daSKenneth D. Merry openings, 14543393f8daSKenneth D. Merry relsim_flags, 145583c5d981SAlexander Motin timeout, 14560181d54bSAlexander Motin action, 145783c5d981SAlexander Motin action_string); 14588b8a9b1dSJustin T. Gibbs break; 14598b8a9b1dSJustin T. Gibbs case SCSI_STATUS_QUEUE_FULL: 14608b8a9b1dSJustin T. Gibbs { 14618b8a9b1dSJustin T. Gibbs /* no decrement */ 146282815562SJustin T. Gibbs struct ccb_getdevstats cgds; 14638b8a9b1dSJustin T. Gibbs 14648b8a9b1dSJustin T. Gibbs /* 14658b8a9b1dSJustin T. Gibbs * First off, find out what the current 14668b8a9b1dSJustin T. Gibbs * transaction counts are. 14678b8a9b1dSJustin T. Gibbs */ 146882815562SJustin T. Gibbs xpt_setup_ccb(&cgds.ccb_h, 14698b8a9b1dSJustin T. Gibbs ccb->ccb_h.path, 1470bbfa4aa1SAlexander Motin CAM_PRIORITY_NORMAL); 147182815562SJustin T. Gibbs cgds.ccb_h.func_code = XPT_GDEV_STATS; 147282815562SJustin T. Gibbs xpt_action((union ccb *)&cgds); 14738b8a9b1dSJustin T. Gibbs 14748b8a9b1dSJustin T. Gibbs /* 14758b8a9b1dSJustin T. Gibbs * If we were the only transaction active, treat 14768b8a9b1dSJustin T. Gibbs * the QUEUE FULL as if it were a BUSY condition. 14778b8a9b1dSJustin T. Gibbs */ 147882815562SJustin T. Gibbs if (cgds.dev_active != 0) { 147982815562SJustin T. Gibbs int total_openings; 148082815562SJustin T. Gibbs 14818b8a9b1dSJustin T. Gibbs /* 14828b8a9b1dSJustin T. Gibbs * Reduce the number of openings to 14838b8a9b1dSJustin T. Gibbs * be 1 less than the amount it took 14848b8a9b1dSJustin T. Gibbs * to get a queue full bounded by the 14858b8a9b1dSJustin T. Gibbs * minimum allowed tag count for this 14868b8a9b1dSJustin T. Gibbs * device. 14878b8a9b1dSJustin T. Gibbs */ 14883393f8daSKenneth D. Merry total_openings = cgds.dev_active + cgds.dev_openings; 14893393f8daSKenneth D. Merry *openings = cgds.dev_active; 14903393f8daSKenneth D. Merry if (*openings < cgds.mintags) 14913393f8daSKenneth D. Merry *openings = cgds.mintags; 14923393f8daSKenneth D. Merry if (*openings < total_openings) 14933393f8daSKenneth D. Merry *relsim_flags = RELSIM_ADJUST_OPENINGS; 14948b8a9b1dSJustin T. Gibbs else { 14958b8a9b1dSJustin T. Gibbs /* 14968b8a9b1dSJustin T. Gibbs * Some devices report queue full for 14978b8a9b1dSJustin T. Gibbs * temporary resource shortages. For 14988b8a9b1dSJustin T. Gibbs * this reason, we allow a minimum 14998b8a9b1dSJustin T. Gibbs * tag count to be entered via a 15008b8a9b1dSJustin T. Gibbs * quirk entry to prevent the queue 15018b8a9b1dSJustin T. Gibbs * count on these devices from falling 15028b8a9b1dSJustin T. Gibbs * to a pessimisticly low value. We 15038b8a9b1dSJustin T. Gibbs * still wait for the next successful 15048b8a9b1dSJustin T. Gibbs * completion, however, before queueing 15058b8a9b1dSJustin T. Gibbs * more transactions to the device. 15068b8a9b1dSJustin T. Gibbs */ 15073393f8daSKenneth D. Merry *relsim_flags = RELSIM_RELEASE_AFTER_CMDCMPLT; 15088b8a9b1dSJustin T. Gibbs } 15093393f8daSKenneth D. Merry *timeout = 0; 15108b8a9b1dSJustin T. Gibbs error = ERESTART; 15110181d54bSAlexander Motin *action &= ~SSQ_PRINT_SENSE; 15128b8a9b1dSJustin T. Gibbs break; 15138b8a9b1dSJustin T. Gibbs } 15148b8a9b1dSJustin T. Gibbs /* FALLTHROUGH */ 15158b8a9b1dSJustin T. Gibbs } 15168b8a9b1dSJustin T. Gibbs case SCSI_STATUS_BUSY: 15178b8a9b1dSJustin T. Gibbs /* 15188b8a9b1dSJustin T. Gibbs * Restart the queue after either another 15198b8a9b1dSJustin T. Gibbs * command completes or a 1 second timeout. 15208b8a9b1dSJustin T. Gibbs */ 1521174b32ceSAlexander Motin if ((sense_flags & SF_RETRY_BUSY) != 0 || 1522174b32ceSAlexander Motin (ccb->ccb_h.retry_count--) > 0) { 15238b8a9b1dSJustin T. Gibbs error = ERESTART; 15243393f8daSKenneth D. Merry *relsim_flags = RELSIM_RELEASE_AFTER_TIMEOUT 15258b8a9b1dSJustin T. Gibbs | RELSIM_RELEASE_AFTER_CMDCMPLT; 15263393f8daSKenneth D. Merry *timeout = 1000; 1527af51b059SMatt Jacob } else { 1528af51b059SMatt Jacob error = EIO; 1529af51b059SMatt Jacob } 15308b8a9b1dSJustin T. Gibbs break; 15318b8a9b1dSJustin T. Gibbs case SCSI_STATUS_RESERV_CONFLICT: 15328b8a9b1dSJustin T. Gibbs default: 15338b8a9b1dSJustin T. Gibbs error = EIO; 15348b8a9b1dSJustin T. Gibbs break; 15358b8a9b1dSJustin T. Gibbs } 15363393f8daSKenneth D. Merry return (error); 15373393f8daSKenneth D. Merry } 15383393f8daSKenneth D. Merry 15393393f8daSKenneth D. Merry static int 15400191d9b3SAlexander Motin camperiphscsisenseerror(union ccb *ccb, union ccb **orig, 15410191d9b3SAlexander Motin cam_flags camflags, u_int32_t sense_flags, 15423393f8daSKenneth D. Merry int *openings, u_int32_t *relsim_flags, 15430181d54bSAlexander Motin u_int32_t *timeout, u_int32_t *action, const char **action_string) 15443393f8daSKenneth D. Merry { 15453393f8daSKenneth D. Merry struct cam_periph *periph; 154683c5d981SAlexander Motin union ccb *orig_ccb = ccb; 15470191d9b3SAlexander Motin int error, recoveryccb; 15483393f8daSKenneth D. Merry 15498532d381SConrad Meyer #if defined(BUF_TRACKING) || defined(FULL_BUF_TRACKING) 15508532d381SConrad Meyer if (ccb->ccb_h.func_code == XPT_SCSI_IO && ccb->csio.bio != NULL) 15518532d381SConrad Meyer biotrack(ccb->csio.bio, __func__); 15528532d381SConrad Meyer #endif 15538532d381SConrad Meyer 15543393f8daSKenneth D. Merry periph = xpt_path_periph(ccb->ccb_h.path); 15550191d9b3SAlexander Motin recoveryccb = (ccb->ccb_h.cbfcnp == camperiphdone); 15560191d9b3SAlexander Motin if ((periph->flags & CAM_PERIPH_RECOVERY_INPROG) && !recoveryccb) { 15573393f8daSKenneth D. Merry /* 15583393f8daSKenneth D. Merry * If error recovery is already in progress, don't attempt 15593393f8daSKenneth D. Merry * to process this error, but requeue it unconditionally 15603393f8daSKenneth D. Merry * and attempt to process it once error recovery has 15613393f8daSKenneth D. Merry * completed. This failed command is probably related to 15623393f8daSKenneth D. Merry * the error that caused the currently active error recovery 15633393f8daSKenneth D. Merry * action so our current recovery efforts should also 15643393f8daSKenneth D. Merry * address this command. Be aware that the error recovery 15653393f8daSKenneth D. Merry * code assumes that only one recovery action is in progress 15663393f8daSKenneth D. Merry * on a particular peripheral instance at any given time 15673393f8daSKenneth D. Merry * (e.g. only one saved CCB for error recovery) so it is 15683393f8daSKenneth D. Merry * imperitive that we don't violate this assumption. 15693393f8daSKenneth D. Merry */ 15703393f8daSKenneth D. Merry error = ERESTART; 15710181d54bSAlexander Motin *action &= ~SSQ_PRINT_SENSE; 15723393f8daSKenneth D. Merry } else { 15733393f8daSKenneth D. Merry scsi_sense_action err_action; 15743393f8daSKenneth D. Merry struct ccb_getdev cgd; 15753393f8daSKenneth D. Merry 15763393f8daSKenneth D. Merry /* 15773393f8daSKenneth D. Merry * Grab the inquiry data for this device. 15783393f8daSKenneth D. Merry */ 1579bbfa4aa1SAlexander Motin xpt_setup_ccb(&cgd.ccb_h, ccb->ccb_h.path, CAM_PRIORITY_NORMAL); 15803393f8daSKenneth D. Merry cgd.ccb_h.func_code = XPT_GDEV_TYPE; 15813393f8daSKenneth D. Merry xpt_action((union ccb *)&cgd); 15823393f8daSKenneth D. Merry 1583e7493b28SAlexander Motin err_action = scsi_error_action(&ccb->csio, &cgd.inq_data, 15843393f8daSKenneth D. Merry sense_flags); 15853393f8daSKenneth D. Merry error = err_action & SS_ERRMASK; 15863393f8daSKenneth D. Merry 15873393f8daSKenneth D. Merry /* 15880191d9b3SAlexander Motin * Do not autostart sequential access devices 15890191d9b3SAlexander Motin * to avoid unexpected tape loading. 15900191d9b3SAlexander Motin */ 15910191d9b3SAlexander Motin if ((err_action & SS_MASK) == SS_START && 15920191d9b3SAlexander Motin SID_TYPE(&cgd.inq_data) == T_SEQUENTIAL) { 15930191d9b3SAlexander Motin *action_string = "Will not autostart a " 15940191d9b3SAlexander Motin "sequential access device"; 15950191d9b3SAlexander Motin goto sense_error_done; 15960191d9b3SAlexander Motin } 15970191d9b3SAlexander Motin 15980191d9b3SAlexander Motin /* 15990191d9b3SAlexander Motin * Avoid recovery recursion if recovery action is the same. 16000191d9b3SAlexander Motin */ 16010191d9b3SAlexander Motin if ((err_action & SS_MASK) >= SS_START && recoveryccb) { 16020191d9b3SAlexander Motin if (((err_action & SS_MASK) == SS_START && 16030191d9b3SAlexander Motin ccb->csio.cdb_io.cdb_bytes[0] == START_STOP_UNIT) || 16040191d9b3SAlexander Motin ((err_action & SS_MASK) == SS_TUR && 16050191d9b3SAlexander Motin (ccb->csio.cdb_io.cdb_bytes[0] == TEST_UNIT_READY))) { 16060191d9b3SAlexander Motin err_action = SS_RETRY|SSQ_DECREMENT_COUNT|EIO; 16070191d9b3SAlexander Motin *relsim_flags = RELSIM_RELEASE_AFTER_TIMEOUT; 16080191d9b3SAlexander Motin *timeout = 500; 16090191d9b3SAlexander Motin } 16100191d9b3SAlexander Motin } 16110191d9b3SAlexander Motin 16120191d9b3SAlexander Motin /* 16133393f8daSKenneth D. Merry * If the recovery action will consume a retry, 16143393f8daSKenneth D. Merry * make sure we actually have retries available. 16153393f8daSKenneth D. Merry */ 16163393f8daSKenneth D. Merry if ((err_action & SSQ_DECREMENT_COUNT) != 0) { 1617a07e846bSAlexander Motin if (ccb->ccb_h.retry_count > 0 && 1618a07e846bSAlexander Motin (periph->flags & CAM_PERIPH_INVALID) == 0) 16193393f8daSKenneth D. Merry ccb->ccb_h.retry_count--; 16203393f8daSKenneth D. Merry else { 162183c5d981SAlexander Motin *action_string = "Retries exhausted"; 16223393f8daSKenneth D. Merry goto sense_error_done; 16233393f8daSKenneth D. Merry } 16243393f8daSKenneth D. Merry } 16253393f8daSKenneth D. Merry 16263393f8daSKenneth D. Merry if ((err_action & SS_MASK) >= SS_START) { 16273393f8daSKenneth D. Merry /* 16283393f8daSKenneth D. Merry * Do common portions of commands that 16293393f8daSKenneth D. Merry * use recovery CCBs. 16303393f8daSKenneth D. Merry */ 163183c5d981SAlexander Motin orig_ccb = xpt_alloc_ccb_nowait(); 163283c5d981SAlexander Motin if (orig_ccb == NULL) { 163383c5d981SAlexander Motin *action_string = "Can't allocate recovery CCB"; 16343393f8daSKenneth D. Merry goto sense_error_done; 16353393f8daSKenneth D. Merry } 16361f1158b2SAlexander Motin /* 16371f1158b2SAlexander Motin * Clear freeze flag for original request here, as 16381f1158b2SAlexander Motin * this freeze will be dropped as part of ERESTART. 16391f1158b2SAlexander Motin */ 16401f1158b2SAlexander Motin ccb->ccb_h.status &= ~CAM_DEV_QFRZN; 164183c5d981SAlexander Motin bcopy(ccb, orig_ccb, sizeof(*orig_ccb)); 16423393f8daSKenneth D. Merry } 16433393f8daSKenneth D. Merry 16443393f8daSKenneth D. Merry switch (err_action & SS_MASK) { 16453393f8daSKenneth D. Merry case SS_NOP: 164683c5d981SAlexander Motin *action_string = "No recovery action needed"; 164700e54d14SKenneth D. Merry error = 0; 164800e54d14SKenneth D. Merry break; 16493393f8daSKenneth D. Merry case SS_RETRY: 165083c5d981SAlexander Motin *action_string = "Retrying command (per sense data)"; 16513393f8daSKenneth D. Merry error = ERESTART; 16528b8a9b1dSJustin T. Gibbs break; 16533393f8daSKenneth D. Merry case SS_FAIL: 165483c5d981SAlexander Motin *action_string = "Unretryable error"; 16553393f8daSKenneth D. Merry break; 16563393f8daSKenneth D. Merry case SS_START: 16573393f8daSKenneth D. Merry { 16583393f8daSKenneth D. Merry int le; 16593393f8daSKenneth D. Merry 16603393f8daSKenneth D. Merry /* 16613393f8daSKenneth D. Merry * Send a start unit command to the device, and 16623393f8daSKenneth D. Merry * then retry the command. 16633393f8daSKenneth D. Merry */ 166483c5d981SAlexander Motin *action_string = "Attempting to start unit"; 166583c5d981SAlexander Motin periph->flags |= CAM_PERIPH_RECOVERY_INPROG; 16663393f8daSKenneth D. Merry 16673393f8daSKenneth D. Merry /* 16683393f8daSKenneth D. Merry * Check for removable media and set 16693393f8daSKenneth D. Merry * load/eject flag appropriately. 16703393f8daSKenneth D. Merry */ 16713393f8daSKenneth D. Merry if (SID_IS_REMOVABLE(&cgd.inq_data)) 16723393f8daSKenneth D. Merry le = TRUE; 16733393f8daSKenneth D. Merry else 16743393f8daSKenneth D. Merry le = FALSE; 16753393f8daSKenneth D. Merry 16763393f8daSKenneth D. Merry scsi_start_stop(&ccb->csio, 16773393f8daSKenneth D. Merry /*retries*/1, 16783393f8daSKenneth D. Merry camperiphdone, 16793393f8daSKenneth D. Merry MSG_SIMPLE_Q_TAG, 16803393f8daSKenneth D. Merry /*start*/TRUE, 16813393f8daSKenneth D. Merry /*load/eject*/le, 16823393f8daSKenneth D. Merry /*immediate*/FALSE, 16833393f8daSKenneth D. Merry SSD_FULL_SIZE, 16843393f8daSKenneth D. Merry /*timeout*/50000); 16853393f8daSKenneth D. Merry break; 16863393f8daSKenneth D. Merry } 16873393f8daSKenneth D. Merry case SS_TUR: 16883393f8daSKenneth D. Merry { 16893393f8daSKenneth D. Merry /* 16903393f8daSKenneth D. Merry * Send a Test Unit Ready to the device. 16913393f8daSKenneth D. Merry * If the 'many' flag is set, we send 120 16923393f8daSKenneth D. Merry * test unit ready commands, one every half 16933393f8daSKenneth D. Merry * second. Otherwise, we just send one TUR. 16943393f8daSKenneth D. Merry * We only want to do this if the retry 16953393f8daSKenneth D. Merry * count has not been exhausted. 16963393f8daSKenneth D. Merry */ 16973393f8daSKenneth D. Merry int retries; 16983393f8daSKenneth D. Merry 16993393f8daSKenneth D. Merry if ((err_action & SSQ_MANY) != 0) { 170083c5d981SAlexander Motin *action_string = "Polling device for readiness"; 17013393f8daSKenneth D. Merry retries = 120; 17023393f8daSKenneth D. Merry } else { 170383c5d981SAlexander Motin *action_string = "Testing device for readiness"; 17043393f8daSKenneth D. Merry retries = 1; 17053393f8daSKenneth D. Merry } 170683c5d981SAlexander Motin periph->flags |= CAM_PERIPH_RECOVERY_INPROG; 17073393f8daSKenneth D. Merry scsi_test_unit_ready(&ccb->csio, 17083393f8daSKenneth D. Merry retries, 17093393f8daSKenneth D. Merry camperiphdone, 17103393f8daSKenneth D. Merry MSG_SIMPLE_Q_TAG, 17113393f8daSKenneth D. Merry SSD_FULL_SIZE, 17123393f8daSKenneth D. Merry /*timeout*/5000); 17133393f8daSKenneth D. Merry 17143393f8daSKenneth D. Merry /* 17153393f8daSKenneth D. Merry * Accomplish our 500ms delay by deferring 17163393f8daSKenneth D. Merry * the release of our device queue appropriately. 17173393f8daSKenneth D. Merry */ 17183393f8daSKenneth D. Merry *relsim_flags = RELSIM_RELEASE_AFTER_TIMEOUT; 17193393f8daSKenneth D. Merry *timeout = 500; 17203393f8daSKenneth D. Merry break; 17213393f8daSKenneth D. Merry } 17223393f8daSKenneth D. Merry default: 1723e3c29144SWarner Losh panic("Unhandled error action %x", err_action); 17243393f8daSKenneth D. Merry } 17253393f8daSKenneth D. Merry 17263393f8daSKenneth D. Merry if ((err_action & SS_MASK) >= SS_START) { 17273393f8daSKenneth D. Merry /* 1728bbfa4aa1SAlexander Motin * Drop the priority, so that the recovery 17293393f8daSKenneth D. Merry * CCB is the first to execute. Freeze the queue 17303393f8daSKenneth D. Merry * after this command is sent so that we can 17313393f8daSKenneth D. Merry * restore the old csio and have it queued in 17323393f8daSKenneth D. Merry * the proper order before we release normal 17333393f8daSKenneth D. Merry * transactions to the device. 17343393f8daSKenneth D. Merry */ 173583c5d981SAlexander Motin ccb->ccb_h.pinfo.priority--; 17363393f8daSKenneth D. Merry ccb->ccb_h.flags |= CAM_DEV_QFREEZE; 173783c5d981SAlexander Motin ccb->ccb_h.saved_ccb_ptr = orig_ccb; 17383393f8daSKenneth D. Merry error = ERESTART; 17390191d9b3SAlexander Motin *orig = orig_ccb; 17403393f8daSKenneth D. Merry } 17413393f8daSKenneth D. Merry 17423393f8daSKenneth D. Merry sense_error_done: 17430181d54bSAlexander Motin *action = err_action; 17443393f8daSKenneth D. Merry } 17453393f8daSKenneth D. Merry return (error); 17463393f8daSKenneth D. Merry } 17473393f8daSKenneth D. Merry 17483393f8daSKenneth D. Merry /* 17493393f8daSKenneth D. Merry * Generic error handler. Peripheral drivers usually filter 17501ffe5851SPedro F. Giffuni * out the errors that they handle in a unique manner, then 17513393f8daSKenneth D. Merry * call this function. 17523393f8daSKenneth D. Merry */ 17533393f8daSKenneth D. Merry int 17543393f8daSKenneth D. Merry cam_periph_error(union ccb *ccb, cam_flags camflags, 1755553484aeSWarner Losh u_int32_t sense_flags) 17563393f8daSKenneth D. Merry { 17570181d54bSAlexander Motin struct cam_path *newpath; 17580181d54bSAlexander Motin union ccb *orig_ccb, *scan_ccb; 1759a07e846bSAlexander Motin struct cam_periph *periph; 17603393f8daSKenneth D. Merry const char *action_string; 17613393f8daSKenneth D. Merry cam_status status; 1762a30ecd42SScott Long int frozen, error, openings, devctl_err; 17630181d54bSAlexander Motin u_int32_t action, relsim_flags, timeout; 17643393f8daSKenneth D. Merry 17650181d54bSAlexander Motin action = SSQ_PRINT_SENSE; 1766a07e846bSAlexander Motin periph = xpt_path_periph(ccb->ccb_h.path); 17673393f8daSKenneth D. Merry action_string = NULL; 17683393f8daSKenneth D. Merry status = ccb->ccb_h.status; 17693393f8daSKenneth D. Merry frozen = (status & CAM_DEV_QFRZN) != 0; 17703393f8daSKenneth D. Merry status &= CAM_STATUS_MASK; 1771a30ecd42SScott Long devctl_err = openings = relsim_flags = timeout = 0; 17720191d9b3SAlexander Motin orig_ccb = ccb; 17733393f8daSKenneth D. Merry 1774a30ecd42SScott Long /* Filter the errors that should be reported via devctl */ 1775a30ecd42SScott Long switch (ccb->ccb_h.status & CAM_STATUS_MASK) { 1776a30ecd42SScott Long case CAM_CMD_TIMEOUT: 1777a30ecd42SScott Long case CAM_REQ_ABORTED: 1778a30ecd42SScott Long case CAM_REQ_CMP_ERR: 1779a30ecd42SScott Long case CAM_REQ_TERMIO: 1780a30ecd42SScott Long case CAM_UNREC_HBA_ERROR: 1781a30ecd42SScott Long case CAM_DATA_RUN_ERR: 1782a30ecd42SScott Long case CAM_SCSI_STATUS_ERROR: 1783a30ecd42SScott Long case CAM_ATA_STATUS_ERROR: 1784a30ecd42SScott Long case CAM_SMP_STATUS_ERROR: 1785a30ecd42SScott Long devctl_err++; 1786a30ecd42SScott Long break; 1787a30ecd42SScott Long default: 1788a30ecd42SScott Long break; 1789a30ecd42SScott Long } 1790a30ecd42SScott Long 17913393f8daSKenneth D. Merry switch (status) { 17923393f8daSKenneth D. Merry case CAM_REQ_CMP: 17933393f8daSKenneth D. Merry error = 0; 17940181d54bSAlexander Motin action &= ~SSQ_PRINT_SENSE; 17953393f8daSKenneth D. Merry break; 17963393f8daSKenneth D. Merry case CAM_SCSI_STATUS_ERROR: 17970191d9b3SAlexander Motin error = camperiphscsistatuserror(ccb, &orig_ccb, 17980191d9b3SAlexander Motin camflags, sense_flags, &openings, &relsim_flags, 17990181d54bSAlexander Motin &timeout, &action, &action_string); 18003393f8daSKenneth D. Merry break; 18013393f8daSKenneth D. Merry case CAM_AUTOSENSE_FAIL: 1802c7ec4390SMatt Jacob error = EIO; /* we have to kill the command */ 1803c7ec4390SMatt Jacob break; 18048b8a9b1dSJustin T. Gibbs case CAM_UA_ABORT: 18058b8a9b1dSJustin T. Gibbs case CAM_UA_TERMIO: 18068b8a9b1dSJustin T. Gibbs case CAM_MSG_REJECT_REC: 18078b8a9b1dSJustin T. Gibbs /* XXX Don't know that these are correct */ 18088b8a9b1dSJustin T. Gibbs error = EIO; 18098b8a9b1dSJustin T. Gibbs break; 18108b8a9b1dSJustin T. Gibbs case CAM_SEL_TIMEOUT: 18113393f8daSKenneth D. Merry if ((camflags & CAM_RETRY_SELTO) != 0) { 1812a07e846bSAlexander Motin if (ccb->ccb_h.retry_count > 0 && 1813a07e846bSAlexander Motin (periph->flags & CAM_PERIPH_INVALID) == 0) { 18143393f8daSKenneth D. Merry ccb->ccb_h.retry_count--; 18153393f8daSKenneth D. Merry error = ERESTART; 18163393f8daSKenneth D. Merry 18173393f8daSKenneth D. Merry /* 181873cf209fSMatt Jacob * Wait a bit to give the device 18193393f8daSKenneth D. Merry * time to recover before we try again. 18203393f8daSKenneth D. Merry */ 18213393f8daSKenneth D. Merry relsim_flags = RELSIM_RELEASE_AFTER_TIMEOUT; 182273cf209fSMatt Jacob timeout = periph_selto_delay; 18233393f8daSKenneth D. Merry break; 18243393f8daSKenneth D. Merry } 1825a07e846bSAlexander Motin action_string = "Retries exhausted"; 18263393f8daSKenneth D. Merry } 18278900f4b8SKenneth D. Merry /* FALLTHROUGH */ 18288900f4b8SKenneth D. Merry case CAM_DEV_NOT_THERE: 18293393f8daSKenneth D. Merry error = ENXIO; 18300181d54bSAlexander Motin action = SSQ_LOST; 18318b8a9b1dSJustin T. Gibbs break; 18328b8a9b1dSJustin T. Gibbs case CAM_REQ_INVALID: 18338b8a9b1dSJustin T. Gibbs case CAM_PATH_INVALID: 18348b8a9b1dSJustin T. Gibbs case CAM_NO_HBA: 18358b8a9b1dSJustin T. Gibbs case CAM_PROVIDE_FAIL: 18368b8a9b1dSJustin T. Gibbs case CAM_REQ_TOO_BIG: 18379a014e6fSIan Dowse case CAM_LUN_INVALID: 18389a014e6fSIan Dowse case CAM_TID_INVALID: 1839357478a5SAlexander Motin case CAM_FUNC_NOTAVAIL: 18408b8a9b1dSJustin T. Gibbs error = EINVAL; 18418b8a9b1dSJustin T. Gibbs break; 18428b8a9b1dSJustin T. Gibbs case CAM_SCSI_BUS_RESET: 18438b8a9b1dSJustin T. Gibbs case CAM_BDR_SENT: 18443393f8daSKenneth D. Merry /* 18453393f8daSKenneth D. Merry * Commands that repeatedly timeout and cause these 18463393f8daSKenneth D. Merry * kinds of error recovery actions, should return 18473393f8daSKenneth D. Merry * CAM_CMD_TIMEOUT, which allows us to safely assume 18483393f8daSKenneth D. Merry * that this command was an innocent bystander to 18493393f8daSKenneth D. Merry * these events and should be unconditionally 18503393f8daSKenneth D. Merry * retried. 18513393f8daSKenneth D. Merry */ 18528b8a9b1dSJustin T. Gibbs case CAM_REQUEUE_REQ: 18530191d9b3SAlexander Motin /* Unconditional requeue if device is still there */ 18540191d9b3SAlexander Motin if (periph->flags & CAM_PERIPH_INVALID) { 18550191d9b3SAlexander Motin action_string = "Periph was invalidated"; 1856a07e846bSAlexander Motin error = EIO; 18570191d9b3SAlexander Motin } else if (sense_flags & SF_NO_RETRY) { 18580191d9b3SAlexander Motin error = EIO; 18590191d9b3SAlexander Motin action_string = "Retry was blocked"; 186084c4de2dSAlexander Motin } else { 18610191d9b3SAlexander Motin error = ERESTART; 18620181d54bSAlexander Motin action &= ~SSQ_PRINT_SENSE; 186384c4de2dSAlexander Motin } 18648b8a9b1dSJustin T. Gibbs break; 18658b8a9b1dSJustin T. Gibbs case CAM_RESRC_UNAVAIL: 186673cf209fSMatt Jacob /* Wait a bit for the resource shortage to abate. */ 186773cf209fSMatt Jacob timeout = periph_noresrc_delay; 186873cf209fSMatt Jacob /* FALLTHROUGH */ 18698b8a9b1dSJustin T. Gibbs case CAM_BUSY: 187073cf209fSMatt Jacob if (timeout == 0) { 187173cf209fSMatt Jacob /* Wait a bit for the busy condition to abate. */ 187273cf209fSMatt Jacob timeout = periph_busy_delay; 187373cf209fSMatt Jacob } 187473cf209fSMatt Jacob relsim_flags = RELSIM_RELEASE_AFTER_TIMEOUT; 187573cf209fSMatt Jacob /* FALLTHROUGH */ 18760191d9b3SAlexander Motin case CAM_ATA_STATUS_ERROR: 18770191d9b3SAlexander Motin case CAM_REQ_CMP_ERR: 18780191d9b3SAlexander Motin case CAM_CMD_TIMEOUT: 18790191d9b3SAlexander Motin case CAM_UNEXP_BUSFREE: 18800191d9b3SAlexander Motin case CAM_UNCOR_PARITY: 18810191d9b3SAlexander Motin case CAM_DATA_RUN_ERR: 18828b8a9b1dSJustin T. Gibbs default: 18830191d9b3SAlexander Motin if (periph->flags & CAM_PERIPH_INVALID) { 18840191d9b3SAlexander Motin error = EIO; 18850191d9b3SAlexander Motin action_string = "Periph was invalidated"; 18860191d9b3SAlexander Motin } else if (ccb->ccb_h.retry_count == 0) { 18878b8a9b1dSJustin T. Gibbs error = EIO; 188883c5d981SAlexander Motin action_string = "Retries exhausted"; 18890191d9b3SAlexander Motin } else if (sense_flags & SF_NO_RETRY) { 18900191d9b3SAlexander Motin error = EIO; 18910191d9b3SAlexander Motin action_string = "Retry was blocked"; 18920191d9b3SAlexander Motin } else { 18930191d9b3SAlexander Motin ccb->ccb_h.retry_count--; 18940191d9b3SAlexander Motin error = ERESTART; 18958b8a9b1dSJustin T. Gibbs } 18968b8a9b1dSJustin T. Gibbs break; 18978b8a9b1dSJustin T. Gibbs } 18988b8a9b1dSJustin T. Gibbs 18990191d9b3SAlexander Motin if ((sense_flags & SF_PRINT_ALWAYS) || 19000191d9b3SAlexander Motin CAM_DEBUGGED(ccb->ccb_h.path, CAM_DEBUG_INFO)) 19010181d54bSAlexander Motin action |= SSQ_PRINT_SENSE; 19020191d9b3SAlexander Motin else if (sense_flags & SF_NO_PRINT) 19030181d54bSAlexander Motin action &= ~SSQ_PRINT_SENSE; 19040181d54bSAlexander Motin if ((action & SSQ_PRINT_SENSE) != 0) 19050191d9b3SAlexander Motin cam_error_print(orig_ccb, CAM_ESF_ALL, CAM_EPF_ALL); 19060181d54bSAlexander Motin if (error != 0 && (action & SSQ_PRINT_SENSE) != 0) { 19070f3e2159SAlexander Motin if (error != ERESTART) { 19083393f8daSKenneth D. Merry if (action_string == NULL) 190983c5d981SAlexander Motin action_string = "Unretryable error"; 191083c5d981SAlexander Motin xpt_print(ccb->ccb_h.path, "Error %d, %s\n", 191183c5d981SAlexander Motin error, action_string); 191283c5d981SAlexander Motin } else if (action_string != NULL) 1913f0d9af51SMatt Jacob xpt_print(ccb->ccb_h.path, "%s\n", action_string); 191483c5d981SAlexander Motin else 191583c5d981SAlexander Motin xpt_print(ccb->ccb_h.path, "Retrying command\n"); 19160f3e2159SAlexander Motin } 19170f3e2159SAlexander Motin 1918cd375264SAlexander Motin if (devctl_err && (error != 0 || (action & SSQ_PRINT_SENSE) != 0)) 1919a30ecd42SScott Long cam_periph_devctl_notify(orig_ccb); 1920a30ecd42SScott Long 19210181d54bSAlexander Motin if ((action & SSQ_LOST) != 0) { 19220191d9b3SAlexander Motin lun_id_t lun_id; 19230191d9b3SAlexander Motin 19240191d9b3SAlexander Motin /* 19250191d9b3SAlexander Motin * For a selection timeout, we consider all of the LUNs on 19260191d9b3SAlexander Motin * the target to be gone. If the status is CAM_DEV_NOT_THERE, 19270191d9b3SAlexander Motin * then we only get rid of the device(s) specified by the 19280191d9b3SAlexander Motin * path in the original CCB. 19290191d9b3SAlexander Motin */ 19300181d54bSAlexander Motin if (status == CAM_SEL_TIMEOUT) 19310191d9b3SAlexander Motin lun_id = CAM_LUN_WILDCARD; 19320181d54bSAlexander Motin else 19330181d54bSAlexander Motin lun_id = xpt_path_lun_id(ccb->ccb_h.path); 19340191d9b3SAlexander Motin 19350191d9b3SAlexander Motin /* Should we do more if we can't create the path?? */ 19360191d9b3SAlexander Motin if (xpt_create_path(&newpath, periph, 19370191d9b3SAlexander Motin xpt_path_path_id(ccb->ccb_h.path), 19380191d9b3SAlexander Motin xpt_path_target_id(ccb->ccb_h.path), 19390191d9b3SAlexander Motin lun_id) == CAM_REQ_CMP) { 19400191d9b3SAlexander Motin 19410191d9b3SAlexander Motin /* 19420191d9b3SAlexander Motin * Let peripheral drivers know that this 19430191d9b3SAlexander Motin * device has gone away. 19440191d9b3SAlexander Motin */ 19450191d9b3SAlexander Motin xpt_async(AC_LOST_DEVICE, newpath, NULL); 19460191d9b3SAlexander Motin xpt_free_path(newpath); 19470191d9b3SAlexander Motin } 19480181d54bSAlexander Motin } 19493631c638SAlexander Motin 19503631c638SAlexander Motin /* Broadcast UNIT ATTENTIONs to all periphs. */ 19510181d54bSAlexander Motin if ((action & SSQ_UA) != 0) 19523631c638SAlexander Motin xpt_async(AC_UNIT_ATTENTION, orig_ccb->ccb_h.path, orig_ccb); 19530181d54bSAlexander Motin 19540181d54bSAlexander Motin /* Rescan target on "Reported LUNs data has changed" */ 19550181d54bSAlexander Motin if ((action & SSQ_RESCAN) != 0) { 19560181d54bSAlexander Motin if (xpt_create_path(&newpath, NULL, 19570181d54bSAlexander Motin xpt_path_path_id(ccb->ccb_h.path), 19580181d54bSAlexander Motin xpt_path_target_id(ccb->ccb_h.path), 1959e5736ac8SAlexander Motin CAM_LUN_WILDCARD) == CAM_REQ_CMP) { 19600181d54bSAlexander Motin 19610181d54bSAlexander Motin scan_ccb = xpt_alloc_ccb_nowait(); 1962e5736ac8SAlexander Motin if (scan_ccb != NULL) { 19630181d54bSAlexander Motin scan_ccb->ccb_h.path = newpath; 1964e5736ac8SAlexander Motin scan_ccb->ccb_h.func_code = XPT_SCAN_TGT; 19650181d54bSAlexander Motin scan_ccb->crcn.flags = 0; 19660181d54bSAlexander Motin xpt_rescan(scan_ccb); 19673231e8bdSAlexander Motin } else { 1968e5736ac8SAlexander Motin xpt_print(newpath, 1969e5736ac8SAlexander Motin "Can't allocate CCB to rescan target\n"); 19703231e8bdSAlexander Motin xpt_free_path(newpath); 19713231e8bdSAlexander Motin } 19720181d54bSAlexander Motin } 19730191d9b3SAlexander Motin } 19740191d9b3SAlexander Motin 19750f3e2159SAlexander Motin /* Attempt a retry */ 19760f3e2159SAlexander Motin if (error == ERESTART || error == 0) { 19770f3e2159SAlexander Motin if (frozen != 0) 19780f3e2159SAlexander Motin ccb->ccb_h.status &= ~CAM_DEV_QFRZN; 19790f3e2159SAlexander Motin if (error == ERESTART) 19800f3e2159SAlexander Motin xpt_action(ccb); 19810f3e2159SAlexander Motin if (frozen != 0) 19820f3e2159SAlexander Motin cam_release_devq(ccb->ccb_h.path, 19830f3e2159SAlexander Motin relsim_flags, 19840f3e2159SAlexander Motin openings, 19850f3e2159SAlexander Motin timeout, 19860f3e2159SAlexander Motin /*getcount_only*/0); 19873393f8daSKenneth D. Merry } 19888b8a9b1dSJustin T. Gibbs 19898b8a9b1dSJustin T. Gibbs return (error); 19908b8a9b1dSJustin T. Gibbs } 1991a30ecd42SScott Long 1992a30ecd42SScott Long #define CAM_PERIPH_DEVD_MSG_SIZE 256 1993a30ecd42SScott Long 1994a30ecd42SScott Long static void 1995a30ecd42SScott Long cam_periph_devctl_notify(union ccb *ccb) 1996a30ecd42SScott Long { 1997a30ecd42SScott Long struct cam_periph *periph; 1998a30ecd42SScott Long struct ccb_getdev *cgd; 1999a30ecd42SScott Long struct sbuf sb; 2000a30ecd42SScott Long int serr, sk, asc, ascq; 2001a30ecd42SScott Long char *sbmsg, *type; 2002a30ecd42SScott Long 2003a30ecd42SScott Long sbmsg = malloc(CAM_PERIPH_DEVD_MSG_SIZE, M_CAMPERIPH, M_NOWAIT); 2004a30ecd42SScott Long if (sbmsg == NULL) 2005a30ecd42SScott Long return; 2006a30ecd42SScott Long 2007a30ecd42SScott Long sbuf_new(&sb, sbmsg, CAM_PERIPH_DEVD_MSG_SIZE, SBUF_FIXEDLEN); 2008a30ecd42SScott Long 2009a30ecd42SScott Long periph = xpt_path_periph(ccb->ccb_h.path); 2010a30ecd42SScott Long sbuf_printf(&sb, "device=%s%d ", periph->periph_name, 2011a30ecd42SScott Long periph->unit_number); 2012a30ecd42SScott Long 2013a30ecd42SScott Long sbuf_printf(&sb, "serial=\""); 2014a30ecd42SScott Long if ((cgd = (struct ccb_getdev *)xpt_alloc_ccb_nowait()) != NULL) { 2015a30ecd42SScott Long xpt_setup_ccb(&cgd->ccb_h, ccb->ccb_h.path, 2016a30ecd42SScott Long CAM_PRIORITY_NORMAL); 2017a30ecd42SScott Long cgd->ccb_h.func_code = XPT_GDEV_TYPE; 2018a30ecd42SScott Long xpt_action((union ccb *)cgd); 2019a30ecd42SScott Long 2020a30ecd42SScott Long if (cgd->ccb_h.status == CAM_REQ_CMP) 2021a30ecd42SScott Long sbuf_bcat(&sb, cgd->serial_num, cgd->serial_num_len); 2022de482e7bSScott Long xpt_free_ccb((union ccb *)cgd); 2023a30ecd42SScott Long } 2024a30ecd42SScott Long sbuf_printf(&sb, "\" "); 2025a30ecd42SScott Long sbuf_printf(&sb, "cam_status=\"0x%x\" ", ccb->ccb_h.status); 2026a30ecd42SScott Long 2027a30ecd42SScott Long switch (ccb->ccb_h.status & CAM_STATUS_MASK) { 2028a30ecd42SScott Long case CAM_CMD_TIMEOUT: 2029a30ecd42SScott Long sbuf_printf(&sb, "timeout=%d ", ccb->ccb_h.timeout); 2030a30ecd42SScott Long type = "timeout"; 2031a30ecd42SScott Long break; 2032a30ecd42SScott Long case CAM_SCSI_STATUS_ERROR: 2033a30ecd42SScott Long sbuf_printf(&sb, "scsi_status=%d ", ccb->csio.scsi_status); 2034a30ecd42SScott Long if (scsi_extract_sense_ccb(ccb, &serr, &sk, &asc, &ascq)) 2035a30ecd42SScott Long sbuf_printf(&sb, "scsi_sense=\"%02x %02x %02x %02x\" ", 2036a30ecd42SScott Long serr, sk, asc, ascq); 2037a30ecd42SScott Long type = "error"; 2038a30ecd42SScott Long break; 2039a30ecd42SScott Long case CAM_ATA_STATUS_ERROR: 2040a30ecd42SScott Long sbuf_printf(&sb, "RES=\""); 2041a30ecd42SScott Long ata_res_sbuf(&ccb->ataio.res, &sb); 2042a30ecd42SScott Long sbuf_printf(&sb, "\" "); 2043a30ecd42SScott Long type = "error"; 2044a30ecd42SScott Long break; 2045a30ecd42SScott Long default: 2046a30ecd42SScott Long type = "error"; 2047a30ecd42SScott Long break; 2048a30ecd42SScott Long } 2049a30ecd42SScott Long 2050a30ecd42SScott Long if (ccb->ccb_h.func_code == XPT_SCSI_IO) { 2051a30ecd42SScott Long sbuf_printf(&sb, "CDB=\""); 20524902e14dSAlexander Motin scsi_cdb_sbuf(scsiio_cdb_ptr(&ccb->csio), &sb); 2053a30ecd42SScott Long sbuf_printf(&sb, "\" "); 2054a30ecd42SScott Long } else if (ccb->ccb_h.func_code == XPT_ATA_IO) { 2055a30ecd42SScott Long sbuf_printf(&sb, "ACB=\""); 2056a30ecd42SScott Long ata_cmd_sbuf(&ccb->ataio.cmd, &sb); 2057a30ecd42SScott Long sbuf_printf(&sb, "\" "); 2058a30ecd42SScott Long } 2059a30ecd42SScott Long 2060a30ecd42SScott Long if (sbuf_finish(&sb) == 0) 2061a30ecd42SScott Long devctl_notify("CAM", "periph", type, sbuf_data(&sb)); 2062a30ecd42SScott Long sbuf_delete(&sb); 2063a30ecd42SScott Long free(sbmsg, M_CAMPERIPH); 2064a30ecd42SScott Long } 2065a30ecd42SScott Long 2066